linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
@ 2008-08-28  9:43 Li Yang
  2008-08-28 15:04 ` Arnd Bergmann
                   ` (4 more replies)
  0 siblings, 5 replies; 33+ messages in thread
From: Li Yang @ 2008-08-28  9:43 UTC (permalink / raw)
  To: greg, linux-usb, dbrownell; +Cc: Li Yang, linuxppc-dev, linux-kernel

Some of Freescale SoC chips have a QE or CPM co-processor which
supports full speed USB.  The driver adds device mode support
of both QE and CPM USB controller to Linux USB gadget.  The
driver is tested with MPC8360 and MPC8272, and should work with
other models having QE/CPM given minor tweaks.

Signed-off-by: Xie Xiaobo <X.Xie@freescale.com>
Signed-off-by: Li Yang <leoli@freescale.com>
---
This is the second submission of the driver.  This version addressed:
Comments from Anton Vorontsov.
A lot of cosmetic problem.
Sparse and various kernel DEBUG warnings.

 drivers/usb/gadget/Kconfig        |   20 +
 drivers/usb/gadget/Makefile       |    1 +
 drivers/usb/gadget/fsl_qe_udc.c   | 2731 +++++++++++++++++++++++++++++++++++++
 drivers/usb/gadget/fsl_qe_udc.h   |  422 ++++++
 drivers/usb/gadget/gadget_chips.h |    9 +
 5 files changed, 3183 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/gadget/fsl_qe_udc.c
 create mode 100644 drivers/usb/gadget/fsl_qe_udc.h

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index acc95b2..4dbf622 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -150,6 +150,26 @@ config USB_FSL_USB2
 	default USB_GADGET
 	select USB_GADGET_SELECTED
 
+config USB_GADGET_FSL_QE
+	boolean "Freescale QE/CPM USB Device Controller"
+	depends on FSL_SOC && (QUICC_ENGINE || CPM)
+	help
+	   Some of Freescale PowerPC processors have a Full Speed
+	   QE/CPM2 USB controller, which support device mode with 4
+	   programmable endpoints. This driver supports the
+	   controller in the MPC8360 and MPC8272, and should work with
+	   controllers having QE or CPM2, given minor tweaks.
+
+	   Say "y" to link the driver statically, or "m" to build a
+	   dynamically linked module called "fsl_qe_udc" and force all
+	   gadget drivers to also be dynamically linked.
+
+config USB_FSL_QE
+	tristate
+	depends on USB_GADGET_FSL_QE
+	default USB_GADGET
+	select USB_GADGET_SELECTED
+
 config USB_GADGET_NET2280
 	boolean "NetChip 228x"
 	depends on PCI
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index fcb5cb9..4871554 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_USB_AT91)		+= at91_udc.o
 obj-$(CONFIG_USB_ATMEL_USBA)	+= atmel_usba_udc.o
 obj-$(CONFIG_USB_FSL_USB2)	+= fsl_usb2_udc.o
 obj-$(CONFIG_USB_M66592)	+= m66592-udc.o
+obj-$(CONFIG_USB_FSL_QE)	+= fsl_qe_udc.o
 
 #
 # USB gadget drivers
diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c
new file mode 100644
index 0000000..f17233c
--- /dev/null
+++ b/drivers/usb/gadget/fsl_qe_udc.c
@@ -0,0 +1,2731 @@
+/*
+ * driver/usb/gadget/fsl_qe_udc.c
+ *
+ * Copyright (c) 2006-2008 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * 	Xie Xiaobo <X.Xie@freescale.com>
+ * 	Li Yang <leoli@freescale.com>
+ * 	Based on bareboard code from Shlomi Gridish.
+ *
+ * Description:
+ * Freescle QE/CPM USB Pheripheral Controller Driver
+ * The controller can be found on MPC8360, MPC8272, and etc.
+ * MPC8360 Rev 1.1 may need QE mircocode update
+ *
+ * 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.
+ */
+
+#undef USB_TRACE
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/moduleparam.h>
+#include <linux/of_platform.h>
+#include <linux/dma-mapping.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <asm/qe.h>
+#include <asm/dma.h>
+#include <asm/reg.h>
+#include "fsl_qe_udc.h"
+
+#ifdef CONFIG_CPM2
+#include <asm/cpm.h>
+
+#define qe_muram_addr cpm_muram_addr
+#define qe_muram_offset cpm_muram_offset
+#define qe_muram_alloc cpm_muram_alloc
+#define qe_muram_free cpm_muram_free
+#endif
+
+#define DRIVER_DESC     "Freescale QE/CPM USB Device Controller driver"
+#define DRIVER_AUTHOR   "Xie XiaoBo"
+#define DRIVER_VERSION  "1.0"
+
+#define DMA_ADDR_INVALID        (~(dma_addr_t)0)
+
+static const char driver_name[] = "fsl_qe_udc";
+static const char driver_desc[] = DRIVER_DESC;
+
+/*ep name is important in gadget, it should obey the convention of ep_match()*/
+static const char *const ep_name[] = {
+	"ep0-control", /* everyone has ep0 */
+	/* 3 configurable endpoints */
+	"ep1",
+	"ep2",
+	"ep3",
+};
+
+static struct usb_endpoint_descriptor qe_ep0_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	0,
+	.bmAttributes =		USB_ENDPOINT_XFER_CONTROL,
+	.wMaxPacketSize =	USB_MAX_CTRL_PAYLOAD,
+};
+
+/* it is initialized in probe()  */
+static struct qe_udc *udc_controller;
+
+/********************************************************************
+ *      Internal Used Function Start
+********************************************************************/
+/*-----------------------------------------------------------------
+ * done() - retire a request; caller blocked irqs
+ *--------------------------------------------------------------*/
+static void done(struct qe_ep *ep, struct qe_req *req, int status)
+{
+	struct qe_udc *udc = ep->udc;
+	unsigned char stopped = ep->stopped;
+
+	/* the req->queue pointer is used by ep_queue() func, in which
+	 * the request will be added into a udc_ep->queue 'd tail
+	 * so here the req will be dropped from the ep->queue
+	 */
+	list_del_init(&req->queue);
+
+	/* req.status should be set as -EINPROGRESS in ep_queue() */
+	if (req->req.status == -EINPROGRESS)
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	if (req->mapped) {
+		dma_unmap_single(udc->gadget.dev.parent,
+			req->req.dma, req->req.length,
+			ep_is_in(ep)
+				? DMA_TO_DEVICE
+				: DMA_FROM_DEVICE);
+		req->req.dma = DMA_ADDR_INVALID;
+		req->mapped = 0;
+	} else
+		dma_sync_single_for_cpu(udc->gadget.dev.parent,
+			req->req.dma, req->req.length,
+			ep_is_in(ep)
+				? DMA_TO_DEVICE
+				: DMA_FROM_DEVICE);
+
+	if (status && (status != -ESHUTDOWN))
+		VDBG("complete %s req %p stat %d len %u/%u",
+			ep->ep.name, &req->req, status,
+			req->req.actual, req->req.length);
+
+	/* don't modify queue heads during completion callback */
+	ep->stopped = 1;
+	spin_unlock(&udc->lock);
+
+	/* this complete() should a func implemented by gadget layer,
+	 * eg fsg->bulk_in_complete() */
+	if (req->req.complete)
+		req->req.complete(&ep->ep, &req->req);
+
+	spin_lock(&udc->lock);
+
+	ep->stopped = stopped;
+}
+
+/*-----------------------------------------------------------------
+ * nuke(): delete all requests related to this ep
+ *--------------------------------------------------------------*/
+static void nuke(struct qe_ep *ep, int status)
+{
+	/* Whether this eq has request linked */
+	while (!list_empty(&ep->queue)) {
+		struct qe_req *req = NULL;
+		req = list_entry(ep->queue.next, struct qe_req, queue);
+
+		done(ep, req, status);
+	}
+}
+
+/*---------------------------------------------------------------------------*
+ * USB and Endpoint manipulate process, include parameter and register       *
+ *---------------------------------------------------------------------------*/
+/* @value: 1--set stall 0--clean stall */
+static int qe_eprx_stall_change(struct qe_ep *ep, int value)
+{
+	u16 tem_usep;
+	u8 epnum = ep->epnum;
+	struct qe_udc *udc = ep->udc;
+
+	tem_usep = in_be16(&udc->usb_regs->usb_usep[epnum]);
+	tem_usep = tem_usep & ~USB_RHS_MASK;
+	if (value == 1)
+		tem_usep |= USB_RHS_STALL;
+	else if (ep->dir == USB_DIR_IN)
+		tem_usep |= USB_RHS_IGNORE_OUT;
+
+	out_be16(&udc->usb_regs->usb_usep[epnum], tem_usep);
+	return 0;
+}
+
+static int qe_eptx_stall_change(struct qe_ep *ep, int value)
+{
+	u16 tem_usep;
+	u8 epnum = ep->epnum;
+	struct qe_udc *udc = ep->udc;
+
+	tem_usep = in_be16(&udc->usb_regs->usb_usep[epnum]);
+	tem_usep = tem_usep & ~USB_THS_MASK;
+	if (value == 1)
+		tem_usep |= USB_THS_STALL;
+	else if (ep->dir == USB_DIR_OUT)
+		tem_usep |= USB_THS_IGNORE_IN;
+
+	out_be16(&udc->usb_regs->usb_usep[epnum], tem_usep);
+
+	return 0;
+}
+
+static int qe_ep0_stall(struct qe_udc *udc)
+{
+	qe_eptx_stall_change(&udc->eps[0], 1);
+	qe_eprx_stall_change(&udc->eps[0], 1);
+	udc_controller->ep0_state = WAIT_FOR_SETUP;
+	udc_controller->ep0_dir = 0;
+	return 0;
+}
+
+static int qe_eprx_nack(struct qe_ep *ep)
+{
+	u8 epnum = ep->epnum;
+	struct qe_udc *udc = ep->udc;
+
+	if (ep->state == EP_STATE_IDLE) {
+		/* Set the ep's nack */
+		clrsetbits_be16(&udc->usb_regs->usb_usep[epnum],
+				USB_RHS_MASK, USB_RHS_NACK);
+
+		/* Mask Rx and Busy interrupts */
+		clrbits16(&udc->usb_regs->usb_usbmr,
+				(USB_E_RXB_MASK | USB_E_BSY_MASK));
+
+		ep->state = EP_STATE_NACK;
+	}
+	return 0;
+}
+
+static int qe_eprx_normal(struct qe_ep *ep)
+{
+	struct qe_udc *udc = ep->udc;
+
+	if (ep->state == EP_STATE_NACK) {
+		clrsetbits_be16(&udc->usb_regs->usb_usep[ep->epnum],
+				USB_RTHS_MASK, USB_THS_IGNORE_IN);
+
+		/* Unmask RX interrupts */
+		out_be16(&udc->usb_regs->usb_usber,
+				USB_E_BSY_MASK | USB_E_RXB_MASK);
+		setbits16(&udc->usb_regs->usb_usbmr,
+				(USB_E_RXB_MASK | USB_E_BSY_MASK));
+
+		ep->state = EP_STATE_IDLE;
+		ep->has_data = 0;
+	}
+
+	return 0;
+}
+
+static int qe_ep_cmd_stoptx(struct qe_ep *ep)
+{
+	u8 ep_num;
+#ifdef CONFIG_CPM2
+	u32 command;
+	u8 opcode;
+
+	ep_num = ep->epnum << CPM_USB_EP_SHIFT;
+	command = CPM_USB_STOP_TX | (u32)ep_num;
+	opcode = CPM_USB_STOP_TX_OPCODE;
+	cpm_command(command, opcode);
+#else
+	ep_num = ep->epnum;
+	qe_issue_cmd(QE_USB_STOP_TX, QE_CR_SUBBLOCK_USB, ep_num, 0);
+#endif
+	return 0;
+}
+
+static int qe_ep_cmd_restarttx(struct qe_ep *ep)
+{
+	u8 ep_num;
+#ifdef CONFIG_CPM2
+	u32 command;
+	u8 opcode;
+
+	ep_num = ep->epnum << CPM_USB_EP_SHIFT;
+	command = CPM_USB_RESTART_TX | (u32)ep_num;
+	opcode = CPM_USB_RESTART_TX_OPCODE;
+	cpm_command(command, opcode);
+#else
+	ep_num = ep->epnum;
+	qe_issue_cmd(QE_USB_RESTART_TX, QE_CR_SUBBLOCK_USB, ep_num, 0);
+#endif
+	return 0;
+}
+
+static int qe_ep_flushtxfifo(struct qe_ep *ep)
+{
+	struct qe_udc *udc = ep->udc;
+	int i;
+
+	i = (int)ep->epnum;
+
+	qe_ep_cmd_stoptx(ep);
+	out_8(&udc->usb_regs->usb_uscom,
+		USB_CMD_FLUSH_FIFO | (USB_CMD_EP_MASK & (ep->epnum)));
+	out_be16(&udc->ep_param[i]->tbptr, in_be16(&udc->ep_param[i]->tbase));
+	out_be32(&udc->ep_param[i]->tstate, 0);
+	out_be16(&udc->ep_param[i]->tbcnt, 0);
+
+	ep->c_txbd = ep->txbase;
+	ep->n_txbd = ep->txbase;
+	qe_ep_cmd_restarttx(ep);
+	return 0;
+}
+
+static int qe_ep_filltxfifo(struct qe_ep *ep)
+{
+	struct qe_udc *udc = ep->udc;
+
+	out_8(&udc->usb_regs->usb_uscom,
+			USB_CMD_STR_FIFO | (USB_CMD_EP_MASK & (ep->epnum)));
+	return 0;
+}
+
+static int qe_epbds_reset(struct qe_udc *udc, int pipe_num)
+{
+	struct qe_ep *ep;
+	u32 bdring_len;
+	struct qe_bd __iomem *bd;
+	int i;
+
+	ep = &udc->eps[pipe_num];
+
+	if (ep->dir == USB_DIR_OUT)
+		bdring_len = USB_BDRING_LEN_RX;
+	else
+		bdring_len = USB_BDRING_LEN;
+
+	bd = ep->rxbase;
+	for (i = 0; i < (bdring_len - 1); i++) {
+		out_be32((u32 __iomem *)bd, R_E | R_I);
+		bd++;
+	}
+	out_be32((u32 __iomem *)bd, R_E | R_I | R_W);
+
+	bd = ep->txbase;
+	for (i = 0; i < USB_BDRING_LEN_TX - 1; i++) {
+		out_be32(&bd->buf, 0);
+		out_be32((u32 __iomem *)bd, 0);
+		bd++;
+	}
+	out_be32((u32 __iomem *)bd, T_W);
+
+	return 0;
+}
+
+static int qe_ep_reset(struct qe_udc *udc, int pipe_num)
+{
+	struct qe_ep *ep;
+	u16 tmpusep;
+
+	ep = &udc->eps[pipe_num];
+	tmpusep = in_be16(&udc->usb_regs->usb_usep[pipe_num]);
+	tmpusep &= ~USB_RTHS_MASK;
+
+	switch (ep->dir) {
+	case USB_DIR_BOTH:
+		qe_ep_flushtxfifo(ep);
+		break;
+	case USB_DIR_OUT:
+		tmpusep |= USB_THS_IGNORE_IN;
+		break;
+	case USB_DIR_IN:
+		qe_ep_flushtxfifo(ep);
+		tmpusep |= USB_RHS_IGNORE_OUT;
+		break;
+	default:
+		break;
+	}
+	out_be16(&udc->usb_regs->usb_usep[pipe_num], tmpusep);
+
+	qe_epbds_reset(udc, pipe_num);
+
+	return 0;
+}
+
+static int qe_ep_toggledata01(struct qe_ep *ep)
+{
+	ep->data01 ^= 0x1;
+	return 0;
+}
+
+static int qe_ep_bd_init(struct qe_udc *udc, unsigned char pipe_num)
+{
+	struct qe_ep *ep = &udc->eps[pipe_num];
+	unsigned long tmp_addr = 0;
+	struct usb_ep_para __iomem *epparam;
+	int i;
+	struct qe_bd __iomem *bd;
+	int bdring_len;
+
+	if (ep->dir == USB_DIR_OUT)
+		bdring_len = USB_BDRING_LEN_RX;
+	else
+		bdring_len = USB_BDRING_LEN;
+
+	epparam = udc->ep_param[pipe_num];
+	/* alloc multi-ram for BD rings and set the ep parameters */
+	tmp_addr = qe_muram_alloc(sizeof(struct qe_bd) * (bdring_len +
+				USB_BDRING_LEN_TX), QE_ALIGNMENT_OF_BD);
+	out_be16(&epparam->rbase, (u16)tmp_addr);
+	out_be16(&epparam->tbase, (u16)(tmp_addr +
+				(sizeof(struct qe_bd) * bdring_len)));
+
+	out_be16(&epparam->rbptr, in_be16(&epparam->rbase));
+	out_be16(&epparam->tbptr, in_be16(&epparam->tbase));
+
+	ep->rxbase = qe_muram_addr(tmp_addr);
+	ep->txbase = qe_muram_addr(tmp_addr + (sizeof(struct qe_bd)
+				* bdring_len));
+	ep->n_rxbd = ep->rxbase;
+	ep->e_rxbd = ep->rxbase;
+	ep->n_txbd = ep->txbase;
+	ep->c_txbd = ep->txbase;
+	ep->data01 = 0; /* data0 */
+
+	/* Init TX and RX bds */
+	bd = ep->rxbase;
+	for (i = 0; i < bdring_len - 1; i++) {
+		out_be32(&bd->buf, 0);
+		out_be32((u32 __iomem *)bd, 0);
+		bd++;
+	}
+	out_be32(&bd->buf, 0);
+	out_be32((u32 __iomem *)bd, R_W);
+
+	bd = ep->txbase;
+	for (i = 0; i < USB_BDRING_LEN_TX - 1; i++) {
+		out_be32(&bd->buf, 0);
+		out_be32((u32 __iomem *)bd, 0);
+		bd++;
+	}
+	out_be32(&bd->buf, 0);
+	out_be32((u32 __iomem *)bd, T_W);
+
+	return 0;
+}
+
+static int qe_ep_rxbd_update(struct qe_ep *ep)
+{
+	unsigned int size;
+	int i;
+	unsigned int tmp;
+	struct qe_bd __iomem *bd;
+	unsigned int bdring_len;
+
+	if (ep->rxbase == NULL)
+		return -EINVAL;
+
+	bd = ep->rxbase;
+
+	ep->rxframe = kmalloc(sizeof(*ep->rxframe), GFP_KERNEL);
+	if (ep->rxframe == NULL) {
+		dev_err(ep->udc->dev, "malloc rxframe failed\n");
+		return -ENOMEM;
+	}
+
+	qe_frame_init(ep->rxframe);
+
+	if (ep->dir == USB_DIR_OUT)
+		bdring_len = USB_BDRING_LEN_RX;
+	else
+		bdring_len = USB_BDRING_LEN;
+
+	size = (ep->ep.maxpacket + USB_CRC_SIZE + 2) * (bdring_len + 1);
+	ep->rxbuffer = kzalloc(size, GFP_KERNEL);
+	if (ep->rxbuffer == NULL) {
+		dev_err(ep->udc->dev, "malloc rxbuffer failed,size=%d\n",
+				size);
+		return -ENOMEM;
+	}
+
+	ep->rxbuf_d = virt_to_phys((void *)ep->rxbuffer);
+	if (ep->rxbuf_d == DMA_ADDR_INVALID) {
+		ep->rxbuf_d = dma_map_single(udc_controller->gadget.dev.parent,
+					ep->rxbuffer,
+					size,
+					DMA_FROM_DEVICE);
+		ep->rxbufmap = 1;
+	} else {
+		dma_sync_single_for_device(udc_controller->gadget.dev.parent,
+					ep->rxbuf_d, size,
+					DMA_FROM_DEVICE);
+		ep->rxbufmap = 0;
+	}
+
+	size = ep->ep.maxpacket + USB_CRC_SIZE + 2;
+	tmp = ep->rxbuf_d;
+	tmp = (u32)(((tmp >> 2) << 2) + 4);
+
+	for (i = 0; i < bdring_len - 1; i++) {
+		out_be32(&bd->buf, tmp);
+		out_be32((u32 __iomem *)bd, (R_E | R_I));
+		tmp = tmp + size;
+		bd++;
+	}
+	out_be32(&bd->buf, tmp);
+	out_be32((u32 __iomem *)bd, (R_E | R_I | R_W));
+
+	return 0;
+}
+
+static int qe_ep_register_init(struct qe_udc *udc, unsigned char pipe_num)
+{
+	struct qe_ep *ep = &udc->eps[pipe_num];
+	struct usb_ep_para __iomem *epparam;
+	u16 usep, logepnum;
+	u16 tmp;
+	u8 rtfcr = 0;
+
+	epparam = udc->ep_param[pipe_num];
+
+	usep = 0;
+	logepnum = (ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+	usep |= (logepnum << USB_EPNUM_SHIFT);
+
+	switch (ep->desc->bmAttributes & 0x03) {
+	case USB_ENDPOINT_XFER_BULK:
+		usep |= USB_TRANS_BULK;
+		break;
+	case USB_ENDPOINT_XFER_ISOC:
+		usep |=  USB_TRANS_ISO;
+		break;
+	case USB_ENDPOINT_XFER_INT:
+		usep |= USB_TRANS_INT;
+		break;
+	default:
+		usep |= USB_TRANS_CTR;
+		break;
+	}
+
+	switch (ep->dir) {
+	case USB_DIR_OUT:
+		usep |= USB_THS_IGNORE_IN;
+		break;
+	case USB_DIR_IN:
+		usep |= USB_RHS_IGNORE_OUT;
+		break;
+	default:
+		break;
+	}
+	out_be16(&udc->usb_regs->usb_usep[pipe_num], usep);
+
+	rtfcr = 0x30;
+	out_8(&epparam->rbmr, rtfcr);
+	out_8(&epparam->tbmr, rtfcr);
+
+	tmp = (u16)(ep->ep.maxpacket + USB_CRC_SIZE);
+	/* MRBLR must be divisble by 4 */
+	tmp = (u16)(((tmp >> 2) << 2) + 4);
+	out_be16(&epparam->mrblr, tmp);
+
+	return 0;
+}
+
+static int qe_ep_init(struct qe_udc *udc,
+		      unsigned char pipe_num,
+		      const struct usb_endpoint_descriptor *desc)
+{
+	struct qe_ep *ep = &udc->eps[pipe_num];
+	unsigned long flags = 0;
+	int reval = 0;
+	u16 max = 0;
+
+	max = le16_to_cpu(desc->wMaxPacketSize);
+
+	/* check the max package size validate for this endpoint */
+	/* Refer to USB2.0 spec table 9-13,
+	*/
+	if (pipe_num != 0) {
+		switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+		case USB_ENDPOINT_XFER_BULK:
+			if (strstr(ep->ep.name, "-iso")
+					|| strstr(ep->ep.name, "-int"))
+				goto en_done;
+			switch (udc->gadget.speed) {
+			case USB_SPEED_HIGH:
+			if ((max == 128) || (max == 256) || (max == 512))
+				break;
+			default:
+				switch (max) {
+				case 4:
+				case 8:
+				case 16:
+				case 32:
+				case 64:
+					break;
+				default:
+				case USB_SPEED_LOW:
+					goto en_done;
+				}
+			}
+			break;
+		case USB_ENDPOINT_XFER_INT:
+			if (strstr(ep->ep.name, "-iso"))	/* bulk is ok */
+				goto en_done;
+			switch (udc->gadget.speed) {
+			case USB_SPEED_HIGH:
+				if (max <= 1024)
+					break;
+			case USB_SPEED_FULL:
+				if (max <= 64)
+					break;
+			default:
+				if (max <= 8)
+					break;
+				goto en_done;
+			}
+			break;
+		case USB_ENDPOINT_XFER_ISOC:
+			if (strstr(ep->ep.name, "-bulk")
+				|| strstr(ep->ep.name, "-int"))
+				goto en_done;
+			switch (udc->gadget.speed) {
+			case USB_SPEED_HIGH:
+				if (max <= 1024)
+					break;
+			case USB_SPEED_FULL:
+				if (max <= 1023)
+					break;
+			default:
+				goto en_done;
+			}
+			break;
+		case USB_ENDPOINT_XFER_CONTROL:
+			if (strstr(ep->ep.name, "-iso")
+				|| strstr(ep->ep.name, "-int"))
+				goto en_done;
+			switch (udc->gadget.speed) {
+			case USB_SPEED_HIGH:
+			case USB_SPEED_FULL:
+				switch (max) {
+				case 1:
+				case 2:
+				case 4:
+				case 8:
+				case 16:
+				case 32:
+				case 64:
+					break;
+				default:
+					goto en_done;
+				}
+			case USB_SPEED_LOW:
+				switch (max) {
+				case 1:
+				case 2:
+				case 4:
+				case 8:
+					break;
+				default:
+					goto en_done;
+				}
+			default:
+				goto en_done;
+			}
+			break;
+
+		default:
+			goto en_done;
+		}
+	} /* if ep0*/
+
+	/* we do not hold the lock as ep_enable is always called before
+	 * the ep is used */
+
+	/* initialize ep structure */
+	ep->ep.maxpacket = max;
+	ep->tm = (u8)(desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
+	ep->desc = desc;
+	ep->stopped = 0;
+	ep->init = 1;
+
+	if (pipe_num == 0) {
+		ep->dir = USB_DIR_BOTH;
+		udc->ep0_dir = USB_DIR_OUT;
+		udc->ep0_state = WAIT_FOR_SETUP;
+	} else	{
+		switch (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
+		case USB_DIR_OUT:
+			ep->dir = USB_DIR_OUT;
+			break;
+		case USB_DIR_IN:
+			ep->dir = USB_DIR_IN;
+		default:
+			break;
+		}
+	}
+
+	/* hardware special operation */
+	qe_ep_bd_init(udc, pipe_num);
+	if ((ep->tm == USBP_TM_CTL) || (ep->dir == USB_DIR_OUT)) {
+		reval = qe_ep_rxbd_update(ep);
+		if (reval)
+			goto en_done1;
+	}
+
+	if ((ep->tm == USBP_TM_CTL) || (ep->dir == USB_DIR_IN)) {
+		ep->txframe = kmalloc(sizeof(*ep->txframe), GFP_KERNEL);
+		if (ep->txframe == NULL) {
+			dev_err(udc->dev, "malloc txframe failed\n");
+			goto en_done;
+		}
+		qe_frame_init(ep->txframe);
+	}
+
+	qe_ep_register_init(udc, pipe_num);
+
+	/* Now HW will be NAKing transfers to that EP,
+	 * until a buffer is queued to it. */
+
+	return 0;
+
+en_done1:
+	spin_unlock_irqrestore(&udc->lock, flags);
+en_done:
+	VDBG("init %s failed!", ep->ep.name);
+	return -ENODEV;
+}
+
+static inline void qe_usb_enable(void)
+{
+	setbits8(&udc_controller->usb_regs->usb_usmod, USB_MODE_EN);
+}
+
+static inline void qe_usb_disable(void)
+{
+	clrbits8(&udc_controller->usb_regs->usb_usmod, USB_MODE_EN);
+}
+
+/*----------------------------------------------------------------------------*
+ *		USB and EP basic manipulate function end		      *
+ *----------------------------------------------------------------------------*/
+
+
+/******************************************************************************
+		UDC transmit and receive process
+ ******************************************************************************/
+static void recycle_one_rxbd(struct qe_ep *ep)
+{
+	u32 bdstatus;
+
+	bdstatus = in_be32((u32 __iomem *)ep->e_rxbd);
+	bdstatus = R_I | R_E | (bdstatus & R_W);
+	out_be32((u32 __iomem *)ep->e_rxbd, bdstatus);
+
+	if (bdstatus & R_W)
+		ep->e_rxbd = ep->rxbase;
+	else
+		ep->e_rxbd++;
+}
+
+static void recycle_rxbds(struct qe_ep *ep, unsigned char stopatnext)
+{
+	u32 bdstatus;
+	struct qe_bd __iomem *bd, *nextbd;
+	unsigned char stop = 0;
+
+	nextbd = ep->n_rxbd;
+	bd = ep->e_rxbd;
+	bdstatus = in_be32((u32 __iomem *)bd);
+
+	while (!(bdstatus & R_E) && !(bdstatus & BD_LENGTH_MASK) && !stop) {
+		bdstatus = R_E | R_I | (bdstatus & R_W);
+		out_be32((u32 __iomem *)bd, bdstatus);
+
+		if (bdstatus & R_W)
+			bd = ep->rxbase;
+		else
+			bd++;
+
+		bdstatus = in_be32((u32 __iomem *)bd);
+		if (stopatnext && (bd == nextbd))
+			stop = 1;
+	}
+
+	ep->e_rxbd = bd;
+}
+
+static void ep_recycle_rxbds(struct qe_ep *ep)
+{
+	struct qe_bd __iomem *bd = ep->n_rxbd;
+	u32 bdstatus;
+	u8 epnum = ep->epnum;
+	struct qe_udc *udc = ep->udc;
+
+	bdstatus = in_be32((u32 __iomem *)bd);
+	if (!(bdstatus & R_E) && !(bdstatus & BD_LENGTH_MASK)) {
+		bd = ep->rxbase +
+				((in_be16(&udc->ep_param[epnum]->rbptr) -
+				  in_be16(&udc->ep_param[epnum]->rbase))
+				 >> 3);
+		bdstatus = in_be32((u32 __iomem *)bd);
+
+		if (bdstatus & R_W)
+			bd = ep->rxbase;
+		else
+			bd++;
+
+		ep->e_rxbd = bd;
+		VDBG("The QE completed full bd cycle");
+		recycle_rxbds(ep, 0);
+		ep->e_rxbd = ep->n_rxbd;
+	} else
+		recycle_rxbds(ep, 1);
+
+	if (in_be16(&udc->usb_regs->usb_usber) & USB_E_BSY_MASK)
+		out_be16(&udc->usb_regs->usb_usber, USB_E_BSY_MASK);
+
+	if (ep->has_data <= 0 && (!list_empty(&ep->queue)))
+		qe_eprx_normal(ep);
+
+	ep->localnack = 0;
+}
+
+static void setup_received_handle(struct qe_udc *udc,
+					struct usb_ctrlrequest *setup);
+static int qe_ep_rxframe_handle(struct qe_ep *ep);
+static void ep0_req_complete(struct qe_udc *udc, struct qe_req *req);
+/* when BD PID is setup, handle the packet */
+static int ep0_setup_handle(struct qe_udc *udc)
+{
+	struct qe_ep *ep = &udc->eps[0];
+	struct qe_frame *pframe;
+	unsigned int fsize;
+	u8 *cp;
+
+	pframe = ep->rxframe;
+	if ((frame_get_info(pframe) & PID_SETUP)
+			&& (udc->ep0_state == WAIT_FOR_SETUP)) {
+		fsize = frame_get_length(pframe);
+		if (unlikely(fsize != 8))
+			return -EINVAL;
+		cp = (u8 *)&udc->local_setup_buff;
+		memcpy(cp, pframe->data, fsize);
+		ep->data01 = 1;
+
+		/* handle the usb command base on the usb_ctrlrequest */
+		setup_received_handle(udc, &udc->local_setup_buff);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int qe_ep0_rx(struct qe_udc *udc)
+{
+	struct qe_ep *ep = &udc->eps[0];
+	struct qe_frame *pframe;
+	struct qe_bd __iomem *bd;
+	u32 bdstatus, length;
+	u32 vaddr;
+
+	pframe = ep->rxframe;
+
+	if (ep->dir == USB_DIR_IN) {
+		VDBG("error:This is a transmit ep!");
+		return -EINVAL;
+	}
+
+	bd = ep->n_rxbd;
+	bdstatus = in_be32((u32 __iomem *)bd);
+	length = bdstatus & BD_LENGTH_MASK;
+
+	while (!(bdstatus & R_E) && length) {
+		if ((bdstatus & R_F) && (bdstatus & R_L)
+			&& !(bdstatus & R_ERROR)) {
+			if (length == USB_CRC_SIZE) {
+				udc->ep0_state = WAIT_FOR_SETUP;
+				VDBG("receive a ZLP in status phase");
+			} else {
+				qe_frame_clean(pframe);
+				vaddr = (u32)phys_to_virt(in_be32(&bd->buf));
+				frame_set_data(pframe, (u8 *)vaddr);
+				frame_set_length(pframe,
+						(length - USB_CRC_SIZE));
+				frame_set_status(pframe, FRAME_OK);
+				switch (bdstatus & R_PID) {
+				case R_PID_SETUP:
+					frame_set_info(pframe, PID_SETUP);
+					break;
+				case R_PID_DATA1:
+					frame_set_info(pframe, PID_DATA1);
+					break;
+				default:
+					frame_set_info(pframe, PID_DATA0);
+					break;
+				}
+
+				if ((bdstatus & R_PID) == R_PID_SETUP)
+					ep0_setup_handle(udc);
+				else
+					qe_ep_rxframe_handle(ep);
+			}
+		} else {
+			dev_err(udc->dev, "The receive frame with error!\n");
+		}
+
+		/* note: don't clear the rxbd's buffer address */
+		recycle_one_rxbd(ep);
+
+		/* Get next BD */
+		if (bdstatus & R_W)
+			bd = ep->rxbase;
+		else
+			bd++;
+
+		bdstatus = in_be32((u32 __iomem *)bd);
+		length = bdstatus & BD_LENGTH_MASK;
+
+	}
+
+	ep->n_rxbd = bd;
+
+	return 0;
+}
+
+static int qe_ep_rxframe_handle(struct qe_ep *ep)
+{
+	struct qe_frame *pframe;
+	u8 framepid = 0;
+	unsigned int fsize;
+	u8 *cp;
+	struct qe_req *req;
+
+	pframe = ep->rxframe;
+
+	if (frame_get_info(pframe) & PID_DATA1)
+		framepid = 0x1;
+
+	if (framepid != ep->data01) {
+		dev_err(ep->udc->dev, "the data01 error!\n");
+		return -EIO;
+	}
+
+	fsize = frame_get_length(pframe);
+	if (list_empty(&ep->queue)) {
+		dev_err(ep->udc->dev, "the %s have no requeue!\n", ep->name);
+	} else {
+		req = list_entry(ep->queue.next, struct qe_req, queue);
+
+		cp = (u8 *)(req->req.buf) + req->req.actual;
+		if (cp) {
+			memcpy(cp, pframe->data, fsize);
+			req->req.actual += fsize;
+			if ((fsize < ep->ep.maxpacket) ||
+					(req->req.actual >= req->req.length)) {
+				if (ep->epnum == 0)
+					ep0_req_complete(ep->udc, req);
+				else
+					done(ep, req, 0);
+				if (list_empty(&ep->queue) && ep->epnum != 0)
+					qe_eprx_nack(ep);
+			}
+		}
+	}
+
+	qe_ep_toggledata01(ep);
+
+	return 0;
+}
+
+static void ep_rx_tasklet(unsigned long data)
+{
+	struct qe_udc *udc = (struct qe_udc *)data;
+	struct qe_ep *ep;
+	struct qe_frame *pframe;
+	struct qe_bd __iomem *bd;
+	unsigned long flags;
+	u32 bdstatus, length;
+	u32 vaddr, i;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	for (i = 1; i < USB_MAX_ENDPOINTS; i++) {
+		ep = &udc->eps[i];
+
+		if (ep->dir == USB_DIR_IN || ep->enable_tasklet == 0) {
+			VDBG("This is a transmit ep or disable tasklet!");
+			continue;
+		}
+
+		pframe = ep->rxframe;
+		bd = ep->n_rxbd;
+		bdstatus = in_be32((u32 __iomem *)bd);
+		length = bdstatus & BD_LENGTH_MASK;
+
+		while (!(bdstatus & R_E) && length) {
+			if (list_empty(&ep->queue)) {
+				qe_eprx_nack(ep);
+				VDBG("The rxep have noreq %d", ep->has_data);
+				break;
+			}
+
+			if ((bdstatus & R_F) && (bdstatus & R_L)
+				&& !(bdstatus & R_ERROR)) {
+				qe_frame_clean(pframe);
+				vaddr = (u32)phys_to_virt(in_be32(&bd->buf));
+				frame_set_data(pframe, (u8 *)vaddr);
+				frame_set_length(pframe,
+						(length - USB_CRC_SIZE));
+				frame_set_status(pframe, FRAME_OK);
+				switch (bdstatus & R_PID) {
+				case R_PID_DATA1:
+					frame_set_info(pframe, PID_DATA1);
+					break;
+				case R_PID_SETUP:
+					frame_set_info(pframe, PID_SETUP);
+					break;
+				default:
+					frame_set_info(pframe, PID_DATA0);
+					break;
+				}
+				/* handle the rx frame */
+				qe_ep_rxframe_handle(ep);
+			} else {
+				dev_err(udc->dev,
+					"error in received frame\n");
+			}
+			/* note: don't clear the rxbd's buffer address */
+			/*clear the length */
+			out_be32((u32 __iomem *)bd, bdstatus & BD_STATUS_MASK);
+			ep->has_data--;
+			if (!(ep->localnack))
+				recycle_one_rxbd(ep);
+
+			/* Get next BD */
+			if (bdstatus & R_W)
+				bd = ep->rxbase;
+			else
+				bd++;
+
+			bdstatus = in_be32((u32 __iomem *)bd);
+			length = bdstatus & BD_LENGTH_MASK;
+		}
+
+		ep->n_rxbd = bd;
+
+		if (ep->localnack)
+			ep_recycle_rxbds(ep);
+
+		ep->enable_tasklet = 0;
+	} /* for i=1 */
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+}
+
+static int qe_ep_rx(struct qe_ep *ep)
+{
+	struct qe_udc *udc;
+	struct qe_frame *pframe;
+	struct qe_bd __iomem *bd;
+	u16 swoffs, ucoffs, emptybds;
+
+	udc = ep->udc;
+	pframe = ep->rxframe;
+
+	if (ep->dir == USB_DIR_IN) {
+		VDBG("error:This is a transmit ep!");
+		return -EINVAL;
+	}
+
+	bd = ep->n_rxbd;
+
+	swoffs = (u16)(bd - ep->rxbase);
+	ucoffs = (u16)((in_be16(&udc->ep_param[ep->epnum]->rbptr) -
+			in_be16(&udc->ep_param[ep->epnum]->rbase)) >> 3);
+	if (swoffs < ucoffs)
+		emptybds = USB_BDRING_LEN_RX - ucoffs + swoffs;
+	else
+		emptybds = swoffs - ucoffs;
+
+	if (emptybds < MIN_EMPTY_BDS) {
+		qe_eprx_nack(ep);
+		ep->localnack = 1;
+		VDBG("The rx ep have a nack!%d", emptybds);
+	}
+	ep->has_data = USB_BDRING_LEN_RX - emptybds;
+
+	if (list_empty(&ep->queue)) {
+		qe_eprx_nack(ep);
+		VDBG("The rxep have noreq %d", ep->has_data);
+		return 0;
+	}
+
+	tasklet_schedule(&udc->rx_tasklet);
+	ep->enable_tasklet = 1;
+
+	return 0;
+}
+
+/* send data from a frame, no matter what tx_req */
+static int qe_ep_tx(struct qe_ep *ep, struct qe_frame *frame)
+{
+	struct qe_udc *udc = ep->udc;
+	struct qe_bd __iomem *bd;
+	u16 saveusbmr;
+	u32 bdstatus, pidmask;
+	u32 paddr;
+
+	if (ep->dir == USB_DIR_OUT) {
+		VDBG("error:this is a receive ep!");
+		return -EINVAL;
+	}
+
+	/* Disable the Tx interrupt */
+	saveusbmr = in_be16(&udc->usb_regs->usb_usbmr);
+	out_be16(&udc->usb_regs->usb_usbmr,
+			saveusbmr & ~(USB_E_TXB_MASK | USB_E_TXE_MASK));
+
+	bd = ep->n_txbd;
+	bdstatus = in_be32((u32 __iomem *)bd);
+
+	if (!(bdstatus & (T_R | BD_LENGTH_MASK))) {
+		if (frame_get_length(frame) == 0) {
+			frame_set_data(frame, udc->nullbuf);
+			frame_set_length(frame, 2);
+			frame->info |= (ZLP | NO_CRC);
+			VDBG("the frame size = 0");
+		}
+		paddr = virt_to_phys((void *)frame->data);
+		out_be32(&bd->buf, paddr);
+		bdstatus = (bdstatus&T_W);
+		if (!(frame_get_info(frame) & NO_CRC))
+			bdstatus |= T_R | T_I | T_L | T_TC
+					| frame_get_length(frame);
+		else
+			bdstatus |= T_R | T_I | T_L | frame_get_length(frame);
+
+		/* if the packet is a ZLP in status phase */
+		if ((ep->epnum == 0) && (udc->ep0_state == DATA_STATE_NEED_ZLP))
+			ep->data01 = 0x1;
+
+		if (ep->data01) {
+			pidmask = T_PID_DATA1;
+			frame->info |= PID_DATA1;
+		} else {
+			pidmask = T_PID_DATA0;
+			frame->info |= PID_DATA0;
+		}
+		bdstatus |= T_CNF;
+		bdstatus |= pidmask;
+		out_be32((u32 __iomem *)bd, bdstatus);
+		qe_ep_filltxfifo(ep);
+
+		/* enable the TX interrupt */
+		out_be16(&udc->usb_regs->usb_usbmr, saveusbmr);
+
+		qe_ep_toggledata01(ep);
+		if (bdstatus & T_W)
+			ep->n_txbd = ep->txbase;
+		else
+			ep->n_txbd++;
+
+		return 0;
+	} else {
+		out_be16(&udc->usb_regs->usb_usbmr, saveusbmr);
+		VDBG("The tx bd is not ready!");
+		return -EBUSY;
+	}
+}
+
+/* when an bd was transmitted, the function can *
+ * handle the tx_req, not include ep0           */
+static int txcomplete(struct qe_ep *ep, unsigned char restart)
+{
+	if (ep->tx_req != NULL) {
+		if (!restart) {
+			int asent = ep->last;
+			ep->sent += asent;
+			ep->last -= asent;
+		} else {
+			ep->last = 0;
+		}
+
+		/* a request already were transmitted completely */
+		if ((ep->tx_req->req.length - ep->sent) <= 0) {
+			ep->tx_req->req.actual = (unsigned int)ep->sent;
+			done(ep, ep->tx_req, 0);
+			ep->tx_req = NULL;
+			ep->last = 0;
+			ep->sent = 0;
+		}
+	}
+
+	/* we should gain a new tx_req fot this endpoint */
+	if (ep->tx_req == NULL) {
+		if (!list_empty(&ep->queue)) {
+			ep->tx_req = list_entry(ep->queue.next,	struct qe_req,
+							queue);
+			ep->last = 0;
+			ep->sent = 0;
+		}
+	}
+
+	return 0;
+}
+
+/* give a frame and a tx_req,send some data */
+static int qe_usb_senddata(struct qe_ep *ep, struct qe_frame *frame)
+{
+	unsigned int size;
+	u8 *buf;
+
+	qe_frame_clean(frame);
+	size = min_t(u32, (ep->tx_req->req.length - ep->sent),
+				ep->ep.maxpacket);
+	buf = (u8 *)ep->tx_req->req.buf + ep->sent;
+	if (buf && size) {
+		ep->last = size;
+		frame_set_data(frame, buf);
+		frame_set_length(frame, size);
+		frame_set_status(frame, FRAME_OK);
+		frame_set_info(frame, 0);
+		return qe_ep_tx(ep, frame);
+	}
+	return -EIO;
+}
+
+/* give a frame struct,send a ZLP */
+static int sendnulldata(struct qe_ep *ep, struct qe_frame *frame, uint infor)
+{
+	struct qe_udc *udc = ep->udc;
+
+	if (frame == NULL)
+		return -ENODEV;
+
+	qe_frame_clean(frame);
+	frame_set_data(frame, (u8 *)udc->nullbuf);
+	frame_set_length(frame, 2);
+	frame_set_status(frame, FRAME_OK);
+	frame_set_info(frame, (ZLP | NO_CRC | infor));
+
+	return qe_ep_tx(ep, frame);
+}
+
+static int frame_create_tx(struct qe_ep *ep, struct qe_frame *frame)
+{
+	struct qe_req *req = ep->tx_req;
+	int reval;
+
+	if (req == NULL)
+		return -ENODEV;
+
+	if ((req->req.length - ep->sent) > 0)
+		reval = qe_usb_senddata(ep, frame);
+	else
+		reval = sendnulldata(ep, frame, 0);
+
+	return reval;
+}
+
+/* if direction is DIR_IN, the status is Device->Host
+ * if direction is DIR_OUT, the status transaction is Device<-Host
+ * in status phase, udc create a request and gain status */
+static int ep0_prime_status(struct qe_udc *udc, int direction)
+{
+
+	struct qe_ep *ep = &udc->eps[0];
+
+	if (direction == USB_DIR_IN) {
+		udc->ep0_state = DATA_STATE_NEED_ZLP;
+		udc->ep0_dir = USB_DIR_IN;
+		sendnulldata(ep, ep->txframe, SETUP_STATUS | NO_REQ);
+	} else {
+		udc->ep0_dir = USB_DIR_OUT;
+		udc->ep0_state = WAIT_FOR_OUT_STATUS;
+	}
+
+	return 0;
+}
+
+/* a request complete in ep0, whether gadget request or udc request */
+static void ep0_req_complete(struct qe_udc *udc, struct qe_req *req)
+{
+	struct qe_ep *ep = &udc->eps[0];
+	/* because usb and ep's status already been set in ch9setaddress() */
+
+	switch (udc->ep0_state) {
+	case DATA_STATE_XMIT:
+		done(ep, req, 0);
+		/* receive status phase */
+		if (ep0_prime_status(udc, USB_DIR_OUT))
+			qe_ep0_stall(udc);
+		break;
+
+	case DATA_STATE_NEED_ZLP:
+		done(ep, req, 0);
+		udc->ep0_state = WAIT_FOR_SETUP;
+		break;
+
+	case DATA_STATE_RECV:
+		done(ep, req, 0);
+		/* send status phase */
+		if (ep0_prime_status(udc, USB_DIR_IN))
+			qe_ep0_stall(udc);
+		break;
+
+	case WAIT_FOR_OUT_STATUS:
+		done(ep, req, 0);
+		udc->ep0_state = WAIT_FOR_SETUP;
+		break;
+
+	case WAIT_FOR_SETUP:
+		VDBG("Unexpected interrupt");
+		break;
+
+	default:
+		qe_ep0_stall(udc);
+		break;
+	}
+}
+
+static int ep0_txcomplete(struct qe_ep *ep, unsigned char restart)
+{
+	struct qe_req *tx_req = NULL;
+	struct qe_frame *frame = ep->txframe;
+
+	if ((frame_get_info(frame) & (ZLP | NO_REQ)) == (ZLP | NO_REQ)) {
+		if (!restart)
+			ep->udc->ep0_state = WAIT_FOR_SETUP;
+		else
+			sendnulldata(ep, ep->txframe, SETUP_STATUS | NO_REQ);
+		return 0;
+	}
+
+	tx_req = ep->tx_req;
+	if (tx_req != NULL) {
+		if (!restart) {
+			int asent = ep->last;
+			ep->sent += asent;
+			ep->last -= asent;
+		} else {
+			ep->last = 0;
+		}
+
+		/* a request already were transmitted completely */
+		if ((ep->tx_req->req.length - ep->sent) <= 0) {
+			ep->tx_req->req.actual = (unsigned int)ep->sent;
+			ep0_req_complete(ep->udc, ep->tx_req);
+			ep->tx_req = NULL;
+			ep->last = 0;
+			ep->sent = 0;
+		}
+	} else {
+		VDBG("the ep0_controller have no req");
+	}
+
+	return 0;
+}
+
+static int ep0_txframe_handle(struct qe_ep *ep)
+{
+	/* if have error, transmit again */
+	if (frame_get_status(ep->txframe) & FRAME_ERROR) {
+		qe_ep_flushtxfifo(ep);
+		VDBG("The EP0 transmit data have error!");
+		if (frame_get_info(ep->txframe) & PID_DATA0)
+			ep->data01 = 0;
+		else
+			ep->data01 = 1;
+
+		ep0_txcomplete(ep, 1);
+	} else
+		ep0_txcomplete(ep, 0);
+
+	frame_create_tx(ep, ep->txframe);
+	return 0;
+}
+
+static int qe_ep0_txconf(struct qe_ep *ep)
+{
+	struct qe_bd __iomem *bd;
+	struct qe_frame *pframe;
+	u32 bdstatus;
+
+	bd = ep->c_txbd;
+	bdstatus = in_be32((u32 __iomem *)bd);
+	while (!(bdstatus & T_R) && (bdstatus & ~T_W)) {
+		pframe = ep->txframe;
+
+		/* clear and recycle the BD */
+		out_be32((u32 __iomem *)bd, bdstatus & T_W);
+		out_be32(&bd->buf, 0);
+		if (bdstatus & T_W)
+			ep->c_txbd = ep->txbase;
+		else
+			ep->c_txbd++;
+
+		if (ep->c_txbd == ep->n_txbd) {
+			if (bdstatus & DEVICE_T_ERROR) {
+				frame_set_status(pframe, FRAME_ERROR);
+				if (bdstatus & T_TO)
+					pframe->status |= TX_ER_TIMEOUT;
+				if (bdstatus & T_UN)
+					pframe->status |= TX_ER_UNDERUN;
+			}
+			ep0_txframe_handle(ep);
+		}
+
+		bd = ep->c_txbd;
+		bdstatus = in_be32((u32 __iomem *)bd);
+	}
+
+	return 0;
+}
+
+static int ep_txframe_handle(struct qe_ep *ep)
+{
+	if (frame_get_status(ep->txframe) & FRAME_ERROR) {
+		qe_ep_flushtxfifo(ep);
+		VDBG("The EP0 transmit data have error!");
+		if (frame_get_info(ep->txframe) & PID_DATA0)
+			ep->data01 = 0;
+		else
+			ep->data01 = 1;
+
+		txcomplete(ep, 1);
+	} else
+		txcomplete(ep, 0);
+
+	frame_create_tx(ep, ep->txframe); /* send the data */
+	return 0;
+}
+
+/* confirm the already trainsmited bd */
+static int qe_ep_txconf(struct qe_ep *ep)
+{
+	struct qe_bd __iomem *bd;
+	struct qe_frame *pframe = NULL;
+	u32 bdstatus;
+	unsigned char breakonrxinterrupt = 0;
+
+	bd = ep->c_txbd;
+	bdstatus = in_be32((u32 __iomem *)bd);
+	while (!(bdstatus & T_R) && (bdstatus & ~T_W)) {
+		pframe = ep->txframe;
+		if (bdstatus & DEVICE_T_ERROR) {
+			frame_set_status(pframe, FRAME_ERROR);
+			if (bdstatus & T_TO)
+				pframe->status |= TX_ER_TIMEOUT;
+			if (bdstatus & T_UN)
+				pframe->status |= TX_ER_UNDERUN;
+		}
+
+		/* clear and recycle the BD */
+		out_be32((u32 __iomem *)bd, bdstatus & T_W);
+		out_be32(&bd->buf, 0);
+		if (bdstatus & T_W)
+			ep->c_txbd = ep->txbase;
+		else
+			ep->c_txbd++;
+
+		/* handle the tx frame */
+		ep_txframe_handle(ep);
+		bd = ep->c_txbd;
+		bdstatus = in_be32((u32 __iomem *)bd);
+	}
+	if (breakonrxinterrupt)
+		return -EIO;
+	else
+		return 0;
+}
+
+/* Add a request in queue, and try to transmit a packet */
+static int ep_req_send(struct qe_ep *ep, struct qe_req *req)
+{
+	int reval = 0;
+
+	if (ep->tx_req == NULL) {
+		ep->sent = 0;
+		ep->last = 0;
+		txcomplete(ep, 0); /* can gain a new tx_req */
+		reval = frame_create_tx(ep, ep->txframe);
+	}
+	return reval;
+}
+
+/* Maybe this is a good ideal */
+static int ep_req_rx(struct qe_ep *ep, struct qe_req *req)
+{
+	struct qe_udc *udc = ep->udc;
+	struct qe_frame *pframe = NULL;
+	struct qe_bd __iomem *bd;
+	u32 bdstatus, length;
+	u32 vaddr, fsize;
+	u8 *cp;
+	u8 finish_req = 0;
+	u8 framepid;
+
+	if (list_empty(&ep->queue)) {
+		VDBG("the req already finish!");
+		return 0;
+	}
+	pframe = ep->rxframe;
+
+	bd = ep->n_rxbd;
+	bdstatus = in_be32((u32 __iomem *)bd);
+	length = bdstatus & BD_LENGTH_MASK;
+
+	while (!(bdstatus & R_E) && length) {
+		if (finish_req)
+			break;
+		if ((bdstatus & R_F) && (bdstatus & R_L)
+					&& !(bdstatus & R_ERROR)) {
+			qe_frame_clean(pframe);
+			vaddr = (u32)phys_to_virt(in_be32(&bd->buf));
+			frame_set_data(pframe, (u8 *)vaddr);
+			frame_set_length(pframe, (length - USB_CRC_SIZE));
+			frame_set_status(pframe, FRAME_OK);
+			switch (bdstatus & R_PID) {
+			case R_PID_DATA1:
+				frame_set_info(pframe, PID_DATA1); break;
+			default:
+				frame_set_info(pframe, PID_DATA0); break;
+			}
+			/* handle the rx frame */
+
+			if (frame_get_info(pframe) & PID_DATA1)
+				framepid = 0x1;
+			else
+				framepid = 0;
+
+			if (framepid != ep->data01) {
+				VDBG("the data01 error!");
+			} else {
+				fsize = frame_get_length(pframe);
+
+				cp = (u8 *)(req->req.buf) + req->req.actual;
+				if (cp) {
+					memcpy(cp, pframe->data, fsize);
+					req->req.actual += fsize;
+					if ((fsize < ep->ep.maxpacket)
+						|| (req->req.actual >=
+							req->req.length)) {
+						finish_req = 1;
+						done(ep, req, 0);
+						if (list_empty(&ep->queue))
+							qe_eprx_nack(ep);
+					}
+				}
+				qe_ep_toggledata01(ep);
+			}
+		} else {
+			dev_err(udc->dev, "The receive frame with error!\n");
+		}
+
+		/* note: don't clear the rxbd's buffer address *
+		 * only Clear the length */
+		out_be32((u32 __iomem *)bd, (bdstatus & BD_STATUS_MASK));
+		ep->has_data--;
+
+		/* Get next BD */
+		if (bdstatus & R_W)
+			bd = ep->rxbase;
+		else
+			bd++;
+
+		bdstatus = in_be32((u32 __iomem *)bd);
+		length = bdstatus & BD_LENGTH_MASK;
+	}
+
+	ep->n_rxbd = bd;
+	ep_recycle_rxbds(ep);
+
+	return 0;
+}
+
+/* only add the request in queue */
+static int ep_req_receive(struct qe_ep *ep, struct qe_req *req)
+{
+	if (ep->state == EP_STATE_NACK) {
+		if (ep->has_data <= 0) {
+			/* Enable rx and unmask rx interrupt */
+			qe_eprx_normal(ep);
+		} else {
+			/* Copy the exist BD data */
+			ep_req_rx(ep, req);
+		}
+	}
+
+	return 0;
+}
+
+/********************************************************************
+	Internal Used Function End
+********************************************************************/
+
+/*-----------------------------------------------------------------------
+	Endpoint Management Functions For Gadget
+ -----------------------------------------------------------------------*/
+static int qe_ep_enable(struct usb_ep *_ep,
+			 const struct usb_endpoint_descriptor *desc)
+{
+	struct qe_udc *udc;
+	struct qe_ep *ep;
+	int retval = 0;
+	unsigned char epnum;
+
+	ep = container_of(_ep, struct qe_ep, ep);
+
+	/* catch various bogus parameters */
+	if (!_ep || !desc || ep->desc || _ep->name == ep_name[0] ||
+			(desc->bDescriptorType != USB_DT_ENDPOINT))
+		return -EINVAL;
+
+	udc = ep->udc;
+	if (!udc->driver || (udc->gadget.speed == USB_SPEED_UNKNOWN))
+		return -ESHUTDOWN;
+
+	epnum = (u8)desc->bEndpointAddress & 0xF;
+
+	retval = qe_ep_init(udc, epnum, desc);
+	if (retval != 0) {
+		qe_muram_free(qe_muram_offset(ep->rxbase));
+		VDBG("enable ep%d fail", ep->epnum);
+		return -EINVAL;
+	}
+	VDBG("enable ep%d ok", ep->epnum);
+	return 0;
+}
+
+static int qe_ep_disable(struct usb_ep *_ep)
+{
+	struct qe_udc *udc;
+	struct qe_ep *ep;
+	unsigned long flags;
+	unsigned int size;
+
+	ep = container_of(_ep, struct qe_ep, ep);
+
+	if (!_ep || !ep->desc) {
+		VDBG("%s not enabled", _ep ? ep->ep.name : NULL);
+		return -EINVAL;
+	}
+
+	udc = ep->udc;
+
+	spin_lock_irqsave(&udc->lock, flags);
+	/* Nuke all pending requests (does flush) */
+	nuke(ep, -ESHUTDOWN);
+	ep->desc = NULL;
+	ep->stopped = 1;
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	qe_muram_free(qe_muram_offset(ep->rxbase));
+
+	if (ep->dir == USB_DIR_OUT)
+		size = (ep->ep.maxpacket + USB_CRC_SIZE + 2) *
+				(USB_BDRING_LEN_RX + 1);
+	else
+		size = (ep->ep.maxpacket + USB_CRC_SIZE + 2) *
+				(USB_BDRING_LEN + 1);
+
+	if (ep->dir != USB_DIR_IN) {
+		kfree(ep->rxframe);
+		if (ep->rxbufmap) {
+			dma_unmap_single(udc_controller->gadget.dev.parent,
+					ep->rxbuf_d, size,
+					DMA_FROM_DEVICE);
+			ep->rxbuf_d = DMA_ADDR_INVALID;
+		} else {
+			dma_sync_single_for_cpu(
+					udc_controller->gadget.dev.parent,
+					ep->rxbuf_d, size,
+					DMA_FROM_DEVICE);
+		}
+		kfree(ep->rxbuffer);
+	}
+
+	if (ep->dir != USB_DIR_OUT)
+		kfree(ep->txframe);
+
+	VDBG("disabled %s OK", _ep->name);
+	return 0;
+}
+
+static struct usb_request *qe_alloc_request(struct usb_ep *_ep,	gfp_t gfp_flags)
+{
+	struct qe_req *req;
+
+	req = kzalloc(sizeof(*req), gfp_flags);
+	if (!req)
+		return NULL;
+
+	req->req.dma = DMA_ADDR_INVALID;
+
+	INIT_LIST_HEAD(&req->queue);
+
+	return &req->req;
+}
+
+static void qe_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct qe_req *req;
+
+	req = container_of(_req, struct qe_req, req);
+
+	if (_req)
+		kfree(req);
+}
+
+/* queues (submits) an I/O request to an endpoint */
+static int qe_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
+				gfp_t gfp_flags)
+{
+	struct qe_ep *ep = container_of(_ep, struct qe_ep, ep);
+	struct qe_req *req = container_of(_req, struct qe_req, req);
+	struct qe_udc *udc;
+	unsigned long flags;
+	int reval;
+
+	/* catch various bogus parameters */
+	if (!_req || !req->req.complete || !req->req.buf
+	|| !list_empty(&req->queue)) {
+		VDBG("bad params");
+		return -EINVAL;
+	}
+	if (!_ep || (!ep->desc && ep_index(ep))) {
+		VDBG("bad ep");
+		return -EINVAL;
+	}
+
+	udc = ep->udc;
+	if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	req->ep = ep;
+
+	/* map virtual address to hardware */
+	if (req->req.dma == DMA_ADDR_INVALID) {
+		req->req.dma = dma_map_single(ep->udc->gadget.dev.parent,
+					req->req.buf,
+					req->req.length,
+					ep_is_in(ep)
+					? DMA_TO_DEVICE :
+					DMA_FROM_DEVICE);
+		req->mapped = 1;
+	} else {
+		dma_sync_single_for_device(ep->udc->gadget.dev.parent,
+					req->req.dma, req->req.length,
+					ep_is_in(ep)
+					? DMA_TO_DEVICE :
+					DMA_FROM_DEVICE);
+		req->mapped = 0;
+	}
+
+	req->req.status = -EINPROGRESS;
+	req->req.actual = 0;
+
+	list_add_tail(&req->queue, &ep->queue);
+	VDBG("gadget have request in %s! %d", ep->name, req->req.length);
+	spin_lock_irqsave(&udc->lock, flags);
+	/* push the request to device */
+	if (ep_is_in(ep))
+		reval = ep_req_send(ep, req);
+
+	/* EP0 */
+	if (ep_index(ep) == 0 && req->req.length > 0) {
+		if (ep_is_in(ep))
+			udc->ep0_state = DATA_STATE_XMIT;
+		else
+			udc->ep0_state = DATA_STATE_RECV;
+	}
+
+	if (ep->dir == USB_DIR_OUT)
+		reval = ep_req_receive(ep, req);
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return 0;
+}
+
+/* dequeues (cancels, unlinks) an I/O request from an endpoint */
+static int qe_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct qe_ep *ep = container_of(_ep, struct qe_ep, ep);
+	struct qe_req *req;
+	unsigned long flags;
+
+	if (!_ep || !_req)
+		return -EINVAL;
+
+	spin_lock_irqsave(&ep->udc->lock, flags);
+
+	/* make sure it's actually queued on this endpoint */
+	list_for_each_entry(req, &ep->queue, queue) {
+		if (&req->req == _req)
+			break;
+	}
+
+	if (&req->req != _req) {
+		spin_unlock_irqrestore(&ep->udc->lock, flags);
+		return -EINVAL;
+	}
+
+	done(ep, req, -ECONNRESET);
+
+	spin_unlock_irqrestore(&ep->udc->lock, flags);
+	return 0;
+}
+
+/*-----------------------------------------------------------------
+ * modify the endpoint halt feature
+ * @ep: the non-isochronous endpoint being stalled
+ * @value: 1--set halt  0--clear halt
+ * Returns zero, or a negative error code.
+*----------------------------------------------------------------*/
+static int qe_ep_set_halt(struct usb_ep *_ep, int value)
+{
+	struct qe_ep *ep;
+	unsigned long flags;
+	int status = -EOPNOTSUPP;
+	struct qe_udc *udc;
+
+	ep = container_of(_ep, struct qe_ep, ep);
+	if (!_ep || !ep->desc) {
+		status = -EINVAL;
+		goto out;
+	}
+
+	if (ep->epnum != 0) {
+		status = 0;
+		goto out;
+	}
+
+	udc = ep->udc;
+	/* Attempt to halt IN ep will fail if any transfer requests
+	 * are still queue */
+	if (value && ep_is_in(ep) && !list_empty(&ep->queue)) {
+		status = -EAGAIN;
+		goto out;
+	}
+
+	status = 0;
+	spin_lock_irqsave(&ep->udc->lock, flags);
+	qe_eptx_stall_change(ep, value);
+	qe_eprx_stall_change(ep, value);
+	spin_unlock_irqrestore(&ep->udc->lock, flags);
+
+	if (ep->epnum == 0) {
+		udc->ep0_state = WAIT_FOR_SETUP;
+		udc->ep0_dir = 0;
+	}
+out:
+	VDBG(" %s %s halt stat %d", ep->ep.name,
+			value ?  "set" : "clear", status);
+
+	return status;
+}
+
+static struct usb_ep_ops qe_ep_ops = {
+	.enable = qe_ep_enable,
+	.disable = qe_ep_disable,
+
+	.alloc_request = qe_alloc_request,
+	.free_request = qe_free_request,
+
+	.queue = qe_ep_queue,
+	.dequeue = qe_ep_dequeue,
+
+	.set_halt = qe_ep_set_halt,
+};
+
+/*------------------------------------------------------------------------
+	Gadget Driver Layer Operations
+ ------------------------------------------------------------------------*/
+
+/* Get the current frame number */
+static int qe_get_frame(struct usb_gadget *gadget)
+{
+	u16 tmp;
+
+	tmp = in_be16(&udc_controller->usb_param->frame_n);
+	if (tmp & 0x8000)
+		tmp = tmp & 0x07ff;
+	else
+		tmp = -EINVAL;
+
+	return (int)tmp;
+}
+
+/* Tries to wake up the host connected to this gadget
+ *
+ * Return : 0-success
+ * Negative-this feature not enabled by host or not supported by device hw
+ */
+static int qe_wakeup(struct usb_gadget *gadget)
+{
+	return -ENOTSUPP;
+}
+
+/* Notify controller that VBUS is powered, Called by whatever
+   detects VBUS sessions */
+static int qe_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+	return -ENOTSUPP;
+}
+
+/* constrain controller's VBUS power usage
+ * This call is used by gadget drivers during SET_CONFIGURATION calls,
+ * reporting how much power the device may consume.  For example, this
+ * could affect how quickly batteries are recharged.
+ *
+ * Returns zero on success, else negative errno.
+ */
+static int qe_vbus_draw(struct usb_gadget *gadget, unsigned mA)
+{
+	return -ENOTSUPP;
+}
+
+/* Change Data+ pullup status
+ * this func is used by usb_gadget_connect/disconnet
+ */
+static int qe_pullup(struct usb_gadget *gadget, int is_on)
+{
+	return -ENOTSUPP;
+}
+
+/* defined in usb_gadget.h */
+static struct usb_gadget_ops qe_gadget_ops = {
+	.get_frame = qe_get_frame,
+	.wakeup = qe_wakeup,
+/*	.set_selfpowered = qe_set_selfpowered,*/ /* always selfpowered */
+	.vbus_session = qe_vbus_session,
+	.vbus_draw = qe_vbus_draw,
+	.pullup = qe_pullup,
+};
+
+/*-------------------------------------------------------------------------
+	USB ep0 Setup process in BUS Enumeration
+ -------------------------------------------------------------------------*/
+static int udc_reset_ep_queue(struct qe_udc *udc, u8 pipe)
+{
+	struct qe_ep *ep = &udc->eps[pipe];
+
+	nuke(ep, -ECONNRESET);
+	ep->tx_req = NULL;
+	return 0;
+}
+
+static int reset_queues(struct qe_udc *udc)
+{
+	u8 pipe;
+
+	for (pipe = 0; pipe < USB_MAX_ENDPOINTS; pipe++)
+		udc_reset_ep_queue(udc, pipe);
+
+	/* report disconnect; the driver is already quiesced */
+	udc->driver->disconnect(&udc->gadget);
+
+	return 0;
+}
+
+static void ch9setaddress(struct qe_udc *udc, u16 value, u16 index,
+			u16 length)
+{
+	/* Save the new address to device struct */
+	udc->device_address = (u8) value;
+	/* Update usb state */
+	udc->usb_state = USB_STATE_ADDRESS;
+
+	/* Status phase , send a ZLP */
+	if (ep0_prime_status(udc, USB_DIR_IN))
+		qe_ep0_stall(udc);
+}
+
+static void ownercomplete(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct qe_req *req = container_of(_req, struct qe_req, req);
+
+	req->req.buf = NULL;
+	kfree(req);
+}
+
+static void ch9getstatus(struct qe_udc *udc, u16 value, u16 index,
+			 u16 length)
+{
+	u16 usb_status = 0;	/* fix me to give correct status */
+
+	struct qe_req *req;
+	struct qe_ep *ep;
+	int status = 0;
+
+	ep = &udc->eps[0];
+
+	req = container_of(qe_alloc_request(&ep->ep, GFP_KERNEL),
+					struct qe_req, req);
+	req->req.length = 2;
+	req->req.buf = udc->nullbuf;
+	memcpy(req->req.buf, (u8 *)&usb_status, 2);
+	req->req.status = -EINPROGRESS;
+	req->req.actual = 0;
+	req->req.complete = ownercomplete;
+
+	udc->ep0_dir = USB_DIR_IN;
+
+	/* data phase */
+	status = qe_ep_queue(&ep->ep, &req->req, GFP_ATOMIC);
+
+	if (status) {
+		dev_err(udc->dev, "Can't respond to getstatus request \n");
+		qe_ep0_stall(udc);
+	}
+}
+
+/* only handle the setup request, suppose the device in normal status */
+static void setup_received_handle(struct qe_udc *udc,
+				struct usb_ctrlrequest *setup)
+{
+	/* Fix Endian (udc->local_setup_buff is cpu Endian now)*/
+	u16 wValue = le16_to_cpu(setup->wValue);
+	u16 wIndex = le16_to_cpu(setup->wIndex);
+	u16 wLength = le16_to_cpu(setup->wLength);
+
+	/* clear the previous request in the ep0 */
+	udc_reset_ep_queue(udc, 0);
+
+	if (setup->bRequestType & USB_DIR_IN)
+		udc->ep0_dir = USB_DIR_IN;
+	else
+		udc->ep0_dir = USB_DIR_OUT;
+
+	switch (setup->bRequest) {
+	case USB_REQ_GET_STATUS:
+		/* Data+Status phase form udc */
+		if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_MASK))
+					!= (USB_DIR_IN | USB_TYPE_STANDARD))
+			break;
+		ch9getstatus(udc, wValue, wIndex, wLength);
+		return;
+
+	case USB_REQ_SET_ADDRESS:
+		/* Status phase from udc */
+		if (setup->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD |
+						USB_RECIP_DEVICE))
+			break;
+		ch9setaddress(udc, wValue, wIndex, wLength);
+		return;
+
+	case USB_REQ_CLEAR_FEATURE:
+	case USB_REQ_SET_FEATURE:
+		/* Requests with no data phase, status phase from udc */
+		if ((setup->bRequestType &  USB_TYPE_MASK)
+					!= USB_TYPE_STANDARD)
+			break;
+
+		if ((setup->bRequestType & USB_RECIP_MASK)
+				== USB_RECIP_ENDPOINT) {
+			int pipe = wIndex & USB_ENDPOINT_NUMBER_MASK;
+			struct qe_ep *ep;
+
+			if (wValue != 0 || wLength != 0
+				|| pipe > USB_MAX_ENDPOINTS)
+				break;
+			ep = &udc->eps[pipe];
+
+			spin_unlock(&udc->lock);
+			qe_ep_set_halt(&ep->ep,
+					(setup->bRequest == USB_REQ_SET_FEATURE)
+						? 1 : 0);
+			spin_lock(&udc->lock);
+		}
+
+		ep0_prime_status(udc, USB_DIR_IN);
+
+		return;
+
+	default:
+		break;
+	}
+
+	if (wLength) {
+		/* Data phase from gadget, status phase from udc */
+		if (setup->bRequestType & USB_DIR_IN) {
+			udc->ep0_state = DATA_STATE_XMIT;
+			udc->ep0_dir = USB_DIR_IN;
+		} else{
+			udc->ep0_state = DATA_STATE_RECV;
+			udc->ep0_dir = USB_DIR_OUT;
+		}
+		spin_unlock(&udc->lock);
+		if (udc->driver->setup(&udc->gadget,
+					&udc->local_setup_buff) < 0)
+			qe_ep0_stall(udc);
+		spin_lock(&udc->lock);
+	} else {
+		/* No data phase, IN status from gadget */
+		udc->ep0_dir = USB_DIR_IN;
+		spin_unlock(&udc->lock);
+		if (udc->driver->setup(&udc->gadget,
+					&udc->local_setup_buff) < 0)
+			qe_ep0_stall(udc);
+		spin_lock(&udc->lock);
+		udc->ep0_state = DATA_STATE_NEED_ZLP;
+	}
+}
+
+/*-------------------------------------------------------------------------
+	USB Interrupt handlers
+ -------------------------------------------------------------------------*/
+static void suspend_irq(struct qe_udc *udc)
+{
+	udc->resume_state = udc->usb_state;
+	udc->usb_state = USB_STATE_SUSPENDED;
+
+	/* report suspend to the driver ,serial.c not support this*/
+	if (udc->driver->suspend)
+		udc->driver->suspend(&udc->gadget);
+}
+
+static void resume_irq(struct qe_udc *udc)
+{
+	udc->usb_state = udc->resume_state;
+	udc->resume_state = 0;
+
+	/* report resume to the driver , serial.c not support this*/
+	if (udc->driver->resume)
+		udc->driver->resume(&udc->gadget);
+}
+
+static void idle_irq(struct qe_udc *udc)
+{
+	u8 usbs;
+
+	usbs = in_8(&udc->usb_regs->usb_usbs);
+	if (usbs & USB_IDLE_STATUS_MASK) {
+		if ((udc->usb_state) != USB_STATE_SUSPENDED)
+			suspend_irq(udc);
+	} else {
+		if (udc->usb_state == USB_STATE_SUSPENDED)
+			resume_irq(udc);
+	}
+}
+
+static int reset_irq(struct qe_udc *udc)
+{
+	unsigned char i;
+
+	qe_usb_disable();
+	out_8(&udc->usb_regs->usb_usadr, 0);
+
+	for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
+		if (udc->eps[i].init)
+			qe_ep_reset(udc, i);
+	}
+
+	reset_queues(udc);
+	udc->usb_state = USB_STATE_DEFAULT;
+	udc->ep0_state = WAIT_FOR_SETUP;
+	udc->ep0_dir = USB_DIR_OUT;
+	qe_usb_enable();
+	return 0;
+}
+
+static int bsy_irq(struct qe_udc *udc)
+{
+	return 0;
+}
+
+static int txe_irq(struct qe_udc *udc)
+{
+	return 0;
+}
+
+/* ep0 tx interrupt also in here */
+static int tx_irq(struct qe_udc *udc)
+{
+	struct qe_ep *ep;
+	struct qe_bd __iomem *bd;
+	int i, res = 0;
+
+	if ((udc->usb_state == USB_STATE_ADDRESS)
+		&& (in_8(&udc->usb_regs->usb_usadr) == 0))
+		out_8(&udc->usb_regs->usb_usadr, udc->device_address);
+
+	for (i = (USB_MAX_ENDPOINTS-1); ((i >= 0) && (res == 0)); i--) {
+		ep = &udc->eps[i];
+		if (ep && ep->init && (ep->dir != USB_DIR_OUT)) {
+			bd = ep->c_txbd;
+			if (!(in_be32((u32 __iomem *)bd) & T_R)
+						&& (in_be32(&bd->buf))) {
+				/* Disable the TX Interrupt */
+				/*confirm the transmitted bd*/
+				if (ep->epnum == 0)
+					res = qe_ep0_txconf(ep);
+				else
+					res = qe_ep_txconf(ep);
+				/* Enable the TX Interrupt */
+			}
+		}
+	}
+	return res;
+}
+
+
+/* setup packect's rx is handle in the function too */
+static void rx_irq(struct qe_udc *udc)
+{
+	struct qe_ep *ep;
+	struct qe_bd __iomem *bd;
+	int i;
+
+	for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
+		ep = &udc->eps[i];
+		if (ep && ep->init && (ep->dir != USB_DIR_IN)) {
+			bd = ep->n_rxbd;
+			if (!(in_be32((u32 __iomem *)bd) & R_E)
+						&& (in_be32(&bd->buf))) {
+				if (ep->epnum == 0) {
+					qe_ep0_rx(udc);
+				} else {
+					/*non-setup package receive*/
+					qe_ep_rx(ep);
+				}
+			}
+		}
+	}
+}
+
+static irqreturn_t qe_udc_irq(int irq, void *_udc)
+{
+	struct qe_udc *udc = (struct qe_udc *)_udc;
+	u16 irq_src;
+	irqreturn_t status = IRQ_NONE;
+	unsigned long flags;
+
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	irq_src = in_be16(&udc->usb_regs->usb_usber) &
+		in_be16(&udc->usb_regs->usb_usbmr);
+	/* Clear notification bits */
+	out_be16(&udc->usb_regs->usb_usber, irq_src);
+	/* USB Interrupt */
+	if (irq_src & USB_E_IDLE_MASK) {
+		idle_irq(udc);
+		irq_src &= ~USB_E_IDLE_MASK;
+		status = IRQ_HANDLED;
+	}
+
+	if (irq_src & USB_E_TXB_MASK) {
+		tx_irq(udc);
+		irq_src &= ~USB_E_TXB_MASK;
+		status = IRQ_HANDLED;
+	}
+
+	if (irq_src & USB_E_RXB_MASK) {
+		rx_irq(udc);
+		irq_src &= ~USB_E_RXB_MASK;
+		status = IRQ_HANDLED;
+	}
+
+	if (irq_src & USB_E_RESET_MASK) {
+		reset_irq(udc);
+		irq_src &= ~USB_E_RESET_MASK;
+		status = IRQ_HANDLED;
+	}
+
+	if (irq_src & USB_E_BSY_MASK) {
+		bsy_irq(udc);
+		irq_src &= ~USB_E_BSY_MASK;
+		status = IRQ_HANDLED;
+	}
+
+	if (irq_src & USB_E_TXE_MASK) {
+		txe_irq(udc);
+		irq_src &= ~USB_E_TXE_MASK;
+		status = IRQ_HANDLED;
+	}
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return status;
+}
+
+/*-------------------------------------------------------------------------
+	Gadget driver register and unregister.
+ --------------------------------------------------------------------------*/
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+	int retval;
+	unsigned long flags = 0;
+
+	/* standard operations */
+	if (!udc_controller)
+		return -ENODEV;
+
+	if (!driver || (driver->speed != USB_SPEED_FULL
+			&& driver->speed != USB_SPEED_HIGH)
+			|| !driver->bind || !driver->disconnect
+			|| !driver->setup)
+		return -EINVAL;
+
+	if (udc_controller->driver)
+		return -EBUSY;
+
+	/* lock is needed but whether should use this lock or another */
+	spin_lock_irqsave(&udc_controller->lock, flags);
+
+	driver->driver.bus = NULL;
+	/* hook up the driver */
+	udc_controller->driver = driver;
+	udc_controller->gadget.dev.driver = &driver->driver;
+	udc_controller->gadget.speed = (enum usb_device_speed)(driver->speed);
+	spin_unlock_irqrestore(&udc_controller->lock, flags);
+
+	retval = driver->bind(&udc_controller->gadget);
+	if (retval) {
+		dev_err(udc_controller->dev, "bind to %s --> %d",
+				driver->driver.name, retval);
+		udc_controller->gadget.dev.driver = NULL;
+		udc_controller->driver = NULL;
+		return retval;
+	}
+
+	/* Enable IRQ reg and Set usbcmd reg EN bit */
+	qe_usb_enable();
+
+	out_be16(&udc_controller->usb_regs->usb_usber, 0xffff);
+	out_be16(&udc_controller->usb_regs->usb_usbmr, USB_E_DEFAULT_DEVICE);
+	udc_controller->usb_state = USB_STATE_ATTACHED;
+	udc_controller->ep0_state = WAIT_FOR_SETUP;
+	udc_controller->ep0_dir = USB_DIR_OUT;
+	dev_info(udc_controller->dev, "%s bind to driver %s \n",
+		udc_controller->gadget.name, driver->driver.name);
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+	struct qe_ep *loop_ep;
+	unsigned long flags;
+
+	if (!udc_controller)
+		return -ENODEV;
+
+	if (!driver || driver != udc_controller->driver)
+		return -EINVAL;
+
+	/* stop usb controller, disable intr */
+	qe_usb_disable();
+
+	/* in fact, no needed */
+	udc_controller->usb_state = USB_STATE_ATTACHED;
+	udc_controller->ep0_state = WAIT_FOR_SETUP;
+	udc_controller->ep0_dir = 0;
+
+	/* stand operation */
+	spin_lock_irqsave(&udc_controller->lock, flags);
+	udc_controller->gadget.speed = USB_SPEED_UNKNOWN;
+	nuke(&udc_controller->eps[0], -ESHUTDOWN);
+	list_for_each_entry(loop_ep, &udc_controller->gadget.ep_list,
+				ep.ep_list)
+		nuke(loop_ep, -ESHUTDOWN);
+	spin_unlock_irqrestore(&udc_controller->lock, flags);
+
+	/* unbind gadget and unhook driver. */
+	driver->unbind(&udc_controller->gadget);
+	udc_controller->gadget.dev.driver = NULL;
+	udc_controller->driver = NULL;
+
+	dev_info(udc_controller->dev, "unregistered gadget driver '%s'\r\n",
+			driver->driver.name);
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+/* udc structure's alloc and setup, include ep-param alloc */
+static struct qe_udc __devinit *qe_udc_config(struct of_device *ofdev)
+{
+	struct qe_udc *udc;
+	struct device_node *np = ofdev->node;
+	unsigned int tmp_addr = 0;
+	struct usb_device_para __iomem *usbpram;
+	unsigned int i;
+	u64 size;
+	u32 offset;
+	int nsize;
+	const unsigned int *prop;
+
+	udc = kzalloc(sizeof(*udc), GFP_KERNEL);
+	if (udc == NULL) {
+		dev_err(&ofdev->dev, "malloc udc failed\n");
+		goto cleanup;
+	}
+
+	udc->dev = &ofdev->dev;
+
+	/* use the default address for the usb parameter */
+	prop = of_get_property(np, "reg", &nsize);
+	offset = of_read_number(prop + 2, 1);
+	size = of_read_number(prop + 3, 1);
+
+	udc->usb_param = qe_muram_addr(offset);
+
+	memset_io(udc->usb_param, 0, size);
+
+	usbpram = udc->usb_param;
+
+	out_be16(&usbpram->frame_n, 0);
+	out_be32(&usbpram->rstate, 0);
+
+	tmp_addr = qe_muram_alloc((USB_MAX_ENDPOINTS *
+					sizeof(struct usb_ep_para)),
+					   USB_EP_PARA_ALIGNMENT);
+
+	for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
+		out_be16(&usbpram->epptr[i], (u16)tmp_addr);
+		udc->ep_param[i] = qe_muram_addr(tmp_addr);
+		tmp_addr += 32;
+	}
+
+	memset_io(udc->ep_param[0], 0,
+			USB_MAX_ENDPOINTS * sizeof(struct usb_ep_para));
+
+	udc->resume_state = USB_STATE_NOTATTACHED;
+	udc->usb_state = USB_STATE_POWERED;
+	udc->ep0_dir = 0;
+
+	/* initliaze the qe_udc lock */
+	spin_lock_init(&udc->lock);
+	return udc;
+
+cleanup:
+	kfree(udc);
+	return NULL;
+}
+
+/* USB Controller register init */
+static int __devinit qe_udc_reg_init(struct qe_udc *udc)
+{
+	struct usb_ctlr __iomem *qe_usbregs;
+	qe_usbregs = udc->usb_regs;
+
+	/* Init the usb register */
+	out_8(&qe_usbregs->usb_usmod, 0x01);
+	out_be16(&qe_usbregs->usb_usbmr, 0);
+	out_8(&qe_usbregs->usb_uscom, 0);
+	out_be16(&qe_usbregs->usb_usber, USBER_ALL_CLEAR);
+
+	return 0;
+}
+
+static int __devinit qe_ep_config(struct qe_udc *udc, unsigned char pipe_num)
+{
+	struct qe_ep *ep = &udc->eps[pipe_num];
+
+	ep->udc = udc;
+	strcpy(ep->name, ep_name[pipe_num]);
+	ep->ep.name = ep_name[pipe_num];
+
+	ep->ep.ops = &qe_ep_ops;
+	ep->stopped = 1;
+	ep->ep.maxpacket = (unsigned short) ~0;
+	ep->desc = NULL;
+	ep->dir = 0xff;
+	ep->epnum = (u8)pipe_num;
+	ep->sent = 0;
+	ep->last = 0;
+	ep->init = 0;
+	ep->rxframe = NULL;
+	ep->txframe = NULL;
+	ep->tx_req = NULL;
+	ep->state = EP_STATE_IDLE;
+	ep->has_data = 0;
+
+	/* the queue lists any req for this ep */
+	INIT_LIST_HEAD(&ep->queue);
+
+	/* gagdet.ep_list used for ep_autoconfig so no ep0*/
+	if (pipe_num != 0)
+		list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
+
+	ep->gadget = &udc->gadget;
+
+	return 0;
+}
+
+/*-----------------------------------------------------------------------
+ *	UDC device Driver operation functions				*
+ *----------------------------------------------------------------------*/
+static void qe_udc_release(struct device *dev)
+{
+	int i = 0;
+
+	complete(udc_controller->done);
+	qe_muram_free(qe_muram_offset(udc_controller->ep_param[0]));
+	for (i = 0; i < USB_MAX_ENDPOINTS; i++)
+		udc_controller->ep_param[i] = NULL;
+
+	kfree(udc_controller);
+	udc_controller = NULL;
+}
+
+/* Driver probe functions */
+static int __devinit qe_udc_probe(struct of_device *ofdev,
+			const struct of_device_id *match)
+{
+	struct device_node *np = ofdev->node;
+	struct qe_ep *ep;
+	unsigned int ret = 0;
+	unsigned int i;
+	const void *prop;
+
+	prop = of_get_property(np, "mode", NULL);
+	if (!prop || strcmp(prop, "peripheral"))
+		return -ENODEV;
+
+	/* Initialize the udc structure including QH member and other member */
+	udc_controller = qe_udc_config(ofdev);
+	if (!udc_controller) {
+		VDBG("udc_controll is NULL");
+		return -ENOMEM;
+	}
+
+	udc_controller->usb_regs = of_iomap(np, 0);
+	if (!udc_controller->usb_regs) {
+		ret = -ENOMEM;
+		goto err1;
+	}
+
+	/* initialize usb hw reg except for regs for EP,
+	 * leave usbintr reg untouched*/
+	qe_udc_reg_init(udc_controller);
+
+	/* here comes the stand operations for probe
+	 * set the qe_udc->gadget.xxx */
+	udc_controller->gadget.ops = &qe_gadget_ops;
+
+	/* gadget.ep0 is a pointer */
+	udc_controller->gadget.ep0 = &udc_controller->eps[0].ep;
+
+	INIT_LIST_HEAD(&udc_controller->gadget.ep_list);
+
+	/* modify in register gadget process */
+	udc_controller->gadget.speed = USB_SPEED_UNKNOWN;
+
+	/* name: Identifies the controller hardware type. */
+	udc_controller->gadget.name = driver_name;
+
+	device_initialize(&udc_controller->gadget.dev);
+
+	strcpy(udc_controller->gadget.dev.bus_id, "gadget");
+
+	udc_controller->gadget.dev.release = qe_udc_release;
+	udc_controller->gadget.dev.parent = &ofdev->dev;
+
+
+	/* EP:intialization qe_ep struct */
+	for (i = 0; i < USB_MAX_ENDPOINTS ; i++) {
+		/*because the ep type isn't decide here so
+		 * qe_ep_init() should be called in ep_enable() */
+
+		/* setup the qe_ep struct and link ep.ep.list
+		 * into gadget.ep_list */
+		qe_ep_config(udc_controller, (unsigned char)i);
+	}
+
+	/* ep0 initialization in here */
+	ret = qe_ep_init(udc_controller, 0, &qe_ep0_desc);
+	if (ret)
+		goto err2;
+
+	/* create a buf for ZLP send */
+	udc_controller->nullbuf = kzalloc(256, GFP_KERNEL);
+	if (udc_controller->nullbuf == NULL) {
+		VDBG("cannot alloc nullbuf");
+		ret = -ENOMEM;
+		goto err3;
+	}
+
+	udc_controller->nullp = virt_to_phys((void *)udc_controller->nullbuf);
+	if (udc_controller->nullp == DMA_ADDR_INVALID) {
+		udc_controller->nullp = dma_map_single(
+					udc_controller->gadget.dev.parent,
+					udc_controller->nullbuf,
+					256,
+					DMA_TO_DEVICE);
+		udc_controller->nullmap = 1;
+	} else {
+		dma_sync_single_for_device(udc_controller->gadget.dev.parent,
+					udc_controller->nullp, 256,
+					DMA_TO_DEVICE);
+		udc_controller->nullmap = 1;
+	}
+
+	tasklet_init(&udc_controller->rx_tasklet, ep_rx_tasklet,
+			(unsigned long)udc_controller);
+	/* request irq and disable DR  */
+	udc_controller->usb_irq = irq_of_parse_and_map(np, 0);
+
+	ret = request_irq(udc_controller->usb_irq, qe_udc_irq, 0,
+				driver_name, udc_controller);
+	if (ret) {
+		dev_err(udc_controller->dev, "cannot request irq %d err %d \n",
+			udc_controller->usb_irq, ret);
+		goto err4;
+	}
+
+	ret = device_add(&udc_controller->gadget.dev);
+	if (ret)
+		goto err5;
+
+	dev_info(udc_controller->dev,
+			"QE/CPM USB controller initialized as device\n");
+	return 0;
+
+err5:
+	free_irq(udc_controller->usb_irq, udc_controller);
+err4:
+	kfree(udc_controller->nullbuf);
+err3:
+	ep = &udc_controller->eps[0];
+	qe_muram_free(qe_muram_offset(ep->rxbase));
+	kfree(ep->rxframe);
+	kfree(ep->rxbuffer);
+	kfree(ep->txframe);
+err2:
+	iounmap(udc_controller->usb_regs);
+err1:
+	kfree(udc_controller);
+
+	return ret;
+}
+
+#ifdef CONFIG_PM
+static int qe_udc_suspend(struct of_device *dev, pm_message_t state)
+{
+	return -ENOTSUPP;
+}
+
+static int qe_udc_resume(struct of_device *dev)
+{
+	return -ENOTSUPP;
+}
+#endif
+
+static int __devexit qe_udc_remove(struct of_device *ofdev)
+{
+	struct qe_ep *ep;
+	unsigned int size;
+
+	DECLARE_COMPLETION(done);
+
+	if (!udc_controller)
+		return -ENODEV;
+
+	udc_controller->done = &done;
+	tasklet_disable(&udc_controller->rx_tasklet);
+
+	if (udc_controller->nullmap) {
+		dma_unmap_single(udc_controller->gadget.dev.parent,
+			udc_controller->nullp, 256,
+				DMA_TO_DEVICE);
+			udc_controller->nullp = DMA_ADDR_INVALID;
+	} else {
+		dma_sync_single_for_cpu(udc_controller->gadget.dev.parent,
+			udc_controller->nullp, 256,
+				DMA_TO_DEVICE);
+	}
+	kfree(udc_controller->nullbuf);
+
+	ep = &udc_controller->eps[0];
+	qe_muram_free(qe_muram_offset(ep->rxbase));
+	size = (ep->ep.maxpacket + USB_CRC_SIZE + 2) * (USB_BDRING_LEN + 1);
+
+	kfree(ep->rxframe);
+	if (ep->rxbufmap) {
+		dma_unmap_single(udc_controller->gadget.dev.parent,
+				ep->rxbuf_d, size,
+				DMA_FROM_DEVICE);
+		ep->rxbuf_d = DMA_ADDR_INVALID;
+	} else {
+		dma_sync_single_for_cpu(udc_controller->gadget.dev.parent,
+				ep->rxbuf_d, size,
+				DMA_FROM_DEVICE);
+	}
+
+	kfree(ep->rxbuffer);
+	kfree(ep->txframe);
+
+	free_irq(udc_controller->usb_irq, udc_controller);
+
+	tasklet_kill(&udc_controller->rx_tasklet);
+
+	device_unregister(&udc_controller->gadget.dev);
+
+	iounmap(udc_controller->usb_regs);
+
+	/* wait for release() to free udc */
+	wait_for_completion(&done);
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static struct of_device_id qe_udc_match[] = {
+	{
+		.compatible = "fsl,mpc8360-qe-usb",
+	},
+	{
+		.compatible = "fsl,mpc8272-cpm-usb",
+	},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, qe_udc_match);
+
+static struct of_platform_driver udc_driver = {
+	.name           = (char *)driver_name,
+	.match_table    = qe_udc_match,
+	.probe          = qe_udc_probe,
+	.remove         = __devexit_p(qe_udc_remove),
+#ifdef CONFIG_PM
+	.suspend        = qe_udc_suspend,
+	.resume         = qe_udc_resume,
+#endif
+};
+
+static int __init qe_udc_init(void)
+{
+	printk(KERN_INFO "%s: %s, %s\n", driver_name, driver_desc,
+			DRIVER_VERSION);
+	return of_register_platform_driver(&udc_driver);
+}
+
+static void __exit qe_udc_exit(void)
+{
+	of_unregister_platform_driver(&udc_driver);
+}
+
+module_init(qe_udc_init);
+module_exit(qe_udc_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/usb/gadget/fsl_qe_udc.h b/drivers/usb/gadget/fsl_qe_udc.h
new file mode 100644
index 0000000..e2e9639
--- /dev/null
+++ b/drivers/usb/gadget/fsl_qe_udc.h
@@ -0,0 +1,422 @@
+/*
+ * drivers/usb/gadget/qe_udc.h
+ *
+ * Copyright (C) 2006-2008 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Author: Xiaobo Xie <X.Xie@freescale.com>
+ *
+ * Description:
+ * Freescale USB device/endpoint management registers
+ *
+ * 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.
+ */
+
+#ifndef __FSL_QE_UDC_H
+#define __FSL_QE_UDC_H
+
+#define USB_MAX_ENDPOINTS               4
+#define USB_MAX_PIPES                   USB_MAX_ENDPOINTS
+#define USB_EP0_MAX_SIZE		64
+#define USB_MAX_CTRL_PAYLOAD            0x4000
+#define USB_BDRING_LEN			16
+#define USB_BDRING_LEN_RX		256
+#define USB_BDRING_LEN_TX		16
+#define MIN_EMPTY_BDS			128
+#define MAX_DATA_BDS			8
+#define USB_CRC_SIZE			2
+#define USB_DIR_BOTH			0x88
+#define R_BUF_MAXSIZE			0x800
+#define USB_EP_PARA_ALIGNMENT		32
+
+/* USB Mode Register bit define */
+#define USB_MODE_EN		0x01
+#define USB_MODE_HOST		0x02
+#define USB_MODE_TEST		0x04
+#define USB_MODE_SFTE		0x08
+#define USB_MODE_RESUME		0x40
+#define USB_MODE_LSS		0x80
+
+/* USB Slave Address Register Mask */
+#define USB_SLVADDR_MASK	0x7F
+
+/* USB Endpoint register define */
+#define USB_EPNUM_MASK		0xF000
+#define USB_EPNUM_SHIFT		12
+
+#define USB_TRANS_MODE_SHIFT	8
+#define USB_TRANS_CTR		0x0000
+#define USB_TRANS_INT		0x0100
+#define USB_TRANS_BULK		0x0200
+#define USB_TRANS_ISO		0x0300
+
+#define USB_EP_MF		0x0020
+#define USB_EP_RTE		0x0010
+
+#define USB_THS_SHIFT		2
+#define USB_THS_MASK		0x000c
+#define USB_THS_NORMAL		0x0
+#define USB_THS_IGNORE_IN	0x0004
+#define USB_THS_NACK		0x0008
+#define USB_THS_STALL		0x000c
+
+#define USB_RHS_SHIFT   	0
+#define USB_RHS_MASK		0x0003
+#define USB_RHS_NORMAL  	0x0
+#define USB_RHS_IGNORE_OUT	0x0001
+#define USB_RHS_NACK		0x0002
+#define USB_RHS_STALL		0x0003
+
+#define USB_RTHS_MASK		0x000f
+
+/* USB Command Register define */
+#define USB_CMD_STR_FIFO	0x80
+#define USB_CMD_FLUSH_FIFO	0x40
+#define USB_CMD_ISFT		0x20
+#define USB_CMD_DSFT		0x10
+#define USB_CMD_EP_MASK		0x03
+
+/* USB Event and Mask Register define */
+#define USB_E_MSF_MASK		0x0800
+#define USB_E_SFT_MASK		0x0400
+#define USB_E_RESET_MASK	0x0200
+#define USB_E_IDLE_MASK		0x0100
+#define USB_E_TXE4_MASK		0x0080
+#define USB_E_TXE3_MASK		0x0040
+#define USB_E_TXE2_MASK		0x0020
+#define USB_E_TXE1_MASK		0x0010
+#define USB_E_SOF_MASK		0x0008
+#define USB_E_BSY_MASK		0x0004
+#define USB_E_TXB_MASK		0x0002
+#define USB_E_RXB_MASK		0x0001
+#define USBER_ALL_CLEAR 	0x0fff
+
+#define USB_E_DEFAULT_DEVICE   (USB_E_RESET_MASK | USB_E_TXE4_MASK | \
+				USB_E_TXE3_MASK | USB_E_TXE2_MASK | \
+				USB_E_TXE1_MASK | USB_E_BSY_MASK | \
+				USB_E_TXB_MASK | USB_E_RXB_MASK)
+
+#define USB_E_TXE_MASK         (USB_E_TXE4_MASK | USB_E_TXE3_MASK|\
+				 USB_E_TXE2_MASK | USB_E_TXE1_MASK)
+/* USB Status Register define */
+#define USB_IDLE_STATUS_MASK	0x01
+
+/* USB Start of Frame Timer */
+#define USB_USSFT_MASK		0x3FFF
+
+/* USB Frame Number Register */
+#define USB_USFRN_MASK		0xFFFF
+
+struct usb_device_para{
+	u16	epptr[4];
+	u32	rstate;
+	u32	rptr;
+	u16	frame_n;
+	u16	rbcnt;
+	u32	rtemp;
+	u32	rxusb_data;
+	u16	rxuptr;
+	u8	reso[2];
+	u32	softbl;
+	u8	sofucrctemp;
+};
+
+struct usb_ep_para{
+	u16	rbase;
+	u16	tbase;
+	u8	rbmr;
+	u8	tbmr;
+	u16	mrblr;
+	u16	rbptr;
+	u16	tbptr;
+	u32	tstate;
+	u32	tptr;
+	u16	tcrc;
+	u16	tbcnt;
+	u32	ttemp;
+	u16	txusbu_ptr;
+	u8	reserve[2];
+};
+
+#define USB_BUSMODE_GBL		0x20
+#define USB_BUSMODE_BO_MASK	0x18
+#define USB_BUSMODE_BO_SHIFT	0x3
+#define USB_BUSMODE_BE		0x2
+#define USB_BUSMODE_CETM	0x04
+#define USB_BUSMODE_DTB		0x02
+
+/* Endpoint basic handle */
+#define ep_index(EP)		((EP)->desc->bEndpointAddress & 0xF)
+#define ep_maxpacket(EP)	((EP)->ep.maxpacket)
+#define ep_is_in(EP)	((ep_index(EP) == 0) ? (EP->udc->ep0_dir == \
+			USB_DIR_IN) : ((EP)->desc->bEndpointAddress \
+			& USB_DIR_IN) == USB_DIR_IN)
+
+/* ep0 transfer state */
+#define WAIT_FOR_SETUP          0
+#define DATA_STATE_XMIT         1
+#define DATA_STATE_NEED_ZLP     2
+#define WAIT_FOR_OUT_STATUS     3
+#define DATA_STATE_RECV         4
+
+/* ep tramsfer mode */
+#define USBP_TM_CTL	0
+#define USBP_TM_ISO	1
+#define USBP_TM_BULK	2
+#define USBP_TM_INT	3
+
+/*-----------------------------------------------------------------------------
+	USB RX And TX DATA Frame
+ -----------------------------------------------------------------------------*/
+struct qe_frame{
+	u8 *data;
+	u32 len;
+	u32 status;
+	u32 info;
+
+	void *privdata;
+	struct list_head node;
+};
+
+/* Frame structure, info field. */
+#define PID_DATA0              0x80000000 /* Data toggle zero */
+#define PID_DATA1              0x40000000 /* Data toggle one  */
+#define PID_SETUP              0x20000000 /* setup bit */
+#define SETUP_STATUS           0x10000000 /* setup status bit */
+#define SETADDR_STATUS         0x08000000 /* setupup address status bit */
+#define NO_REQ                 0x04000000 /* Frame without request */
+#define HOST_DATA              0x02000000 /* Host data frame */
+#define FIRST_PACKET_IN_FRAME  0x01000000 /* first packet in the frame */
+#define TOKEN_FRAME            0x00800000 /* Host token frame */
+#define ZLP                    0x00400000 /* Zero length packet */
+#define IN_TOKEN_FRAME         0x00200000 /* In token package */
+#define OUT_TOKEN_FRAME        0x00100000 /* Out token package */
+#define SETUP_TOKEN_FRAME      0x00080000 /* Setup token package */
+#define STALL_FRAME            0x00040000 /* Stall handshake */
+#define NACK_FRAME             0x00020000 /* Nack handshake */
+#define NO_PID                 0x00010000 /* No send PID */
+#define NO_CRC                 0x00008000 /* No send CRC */
+#define HOST_COMMAND           0x00004000 /* Host command frame   */
+
+/* Frame status field */
+/* Receive side */
+#define FRAME_OK               0x00000000 /* Frame tranmitted or received OK */
+#define FRAME_ERROR            0x80000000 /* Error occured on frame */
+#define START_FRAME_LOST       0x40000000 /* START_FRAME_LOST */
+#define END_FRAME_LOST         0x20000000 /* END_FRAME_LOST */
+#define RX_ER_NONOCT           0x10000000 /* Rx Non Octet Aligned Packet */
+#define RX_ER_BITSTUFF         0x08000000 /* Frame Aborted --Received packet
+						with bit stuff error */
+#define RX_ER_CRC              0x04000000 /* Received packet with CRC error */
+#define RX_ER_OVERUN           0x02000000 /* Over-run occured on reception */
+#define RX_ER_PID              0x01000000 /* Wrong PID received */
+/* Tranmit side */
+#define TX_ER_NAK              0x00800000 /* Received NAK handshake */
+#define TX_ER_STALL            0x00400000 /* Received STALL handshake */
+#define TX_ER_TIMEOUT          0x00200000 /* Transmit time out */
+#define TX_ER_UNDERUN          0x00100000 /* Transmit underrun */
+#define FRAME_INPROGRESS       0x00080000 /* Frame is being transmitted */
+#define ER_DATA_UNDERUN        0x00040000 /* Frame is shorter then expected */
+#define ER_DATA_OVERUN         0x00020000 /* Frame is longer then expected */
+
+/* QE USB frame operation functions */
+#define frame_get_length(frm) (frm->len)
+#define frame_set_length(frm, leng) (frm->len = leng)
+#define frame_get_data(frm) (frm->data)
+#define frame_set_data(frm, dat) (frm->data = dat)
+#define frame_get_info(frm) (frm->info)
+#define frame_set_info(frm, inf) (frm->info = inf)
+#define frame_get_status(frm) (frm->status)
+#define frame_set_status(frm, stat) (frm->status = stat)
+#define frame_get_privdata(frm) (frm->privdata)
+#define frame_set_privdata(frm, dat) (frm->privdata = dat)
+
+static inline void qe_frame_clean(struct qe_frame *frm)
+{
+	frame_set_data(frm, NULL);
+	frame_set_length(frm, 0);
+	frame_set_status(frm, FRAME_OK);
+	frame_set_info(frm, 0);
+	frame_set_privdata(frm, NULL);
+}
+
+static inline void qe_frame_init(struct qe_frame *frm)
+{
+	qe_frame_clean(frm);
+	INIT_LIST_HEAD(&(frm->node));
+}
+
+struct qe_req {
+	struct usb_request req;
+	struct list_head queue;
+	/* ep_queue() func will add
+	 a request->queue into a udc_ep->queue 'd tail */
+	struct qe_ep *ep;
+	unsigned mapped:1;
+};
+
+struct qe_ep {
+	struct usb_ep ep;
+	struct list_head queue;
+	struct qe_udc *udc;
+	const struct usb_endpoint_descriptor *desc;
+	struct usb_gadget *gadget;
+
+	u8 state;
+
+	struct qe_bd __iomem *rxbase;
+	struct qe_bd __iomem *n_rxbd;
+	struct qe_bd __iomem *e_rxbd;
+
+	struct qe_bd __iomem *txbase;
+	struct qe_bd __iomem *n_txbd;
+	struct qe_bd __iomem *c_txbd;
+
+	struct qe_frame *rxframe;
+	u8 *rxbuffer;
+	dma_addr_t rxbuf_d;
+	u8 rxbufmap;
+	unsigned char localnack;
+	int has_data;
+
+	struct qe_frame *txframe;
+	struct qe_req *tx_req;
+	int sent;  /*data already sent */
+	int last;  /*data sent in the last time*/
+
+	u8 dir;
+	u8 epnum;
+	u8 tm; /* transfer mode */
+	u8 data01;
+	u8 init;
+
+	u8 already_seen;
+	u8 enable_tasklet;
+	u8 setup_stage;
+	u32 last_io;            /* timestamp */
+
+	char name[14];
+
+	unsigned double_buf:1;
+	unsigned stopped:1;
+	unsigned fnf:1;
+	unsigned has_dma:1;
+
+	u8 ackwait;
+	u8 dma_channel;
+	u16 dma_counter;
+	int lch;
+
+	struct timer_list timer;
+};
+
+struct qe_udc {
+	struct usb_gadget gadget;
+	struct usb_gadget_driver *driver;
+	struct device *dev;
+	struct qe_ep eps[USB_MAX_ENDPOINTS];
+	struct usb_ctrlrequest local_setup_buff;
+	spinlock_t lock;	/* lock for set/config qe_udc */
+
+	struct qe_req *status_req;     /* ep0 status request */
+
+	/* USB and EP Parameter Block pointer */
+	struct usb_device_para __iomem *usb_param;
+	struct usb_ep_para __iomem *ep_param[4];
+
+	u32 max_pipes;          /* Device max pipes */
+	u32 max_use_endpts;     /* Max endpointes to be used */
+	u32 bus_reset;          /* Device is bus reseting */
+	u32 resume_state;       /* USB state to resume*/
+	u32 usb_state;          /* USB current state */
+	u32 usb_next_state;     /* USB next state */
+	u32 ep0_state;          /* Enpoint zero state */
+	u32 ep0_dir;            /* Enpoint zero direction: can be
+				USB_DIR_IN or USB_DIR_OUT*/
+	u32 usb_sof_count;      /* SOF count */
+	u32 errors;             /* USB ERRORs count */
+
+	u8 *tmpbuf;
+	u32 c_start;
+	u32 c_end;
+
+	u8 *nullbuf;
+	dma_addr_t nullp;
+	u8 nullmap;
+	u8 device_address;	/* Device USB address */
+
+	unsigned int usb_clock;
+	unsigned int usb_irq;
+	struct usb_ctlr __iomem *usb_regs;
+
+	struct tasklet_struct rx_tasklet;
+
+	struct completion *done;	/* to make sure release() is done */
+};
+
+#define EP_STATE_IDLE	0
+#define EP_STATE_NACK	1
+#define EP_STATE_STALL	2
+
+/*
+ * transmit BD's status
+ */
+#define T_R           0x80000000         /* ready bit */
+#define T_W           0x20000000         /* wrap bit */
+#define T_I           0x10000000         /* interrupt on completion */
+#define T_L           0x08000000         /* last */
+#define T_TC          0x04000000         /* transmit CRC */
+#define T_CNF         0x02000000         /* wait for  transmit confirm */
+#define T_LSP         0x01000000         /* Low-speed transaction */
+#define T_PID         0x00c00000         /* packet id */
+#define T_NAK         0x00100000         /* No ack. */
+#define T_STAL        0x00080000         /* Stall recieved */
+#define T_TO          0x00040000         /* time out */
+#define T_UN          0x00020000         /* underrun */
+
+#define DEVICE_T_ERROR    (T_UN | T_TO)
+#define HOST_T_ERROR      (T_UN | T_TO | T_NAK | T_STAL)
+#define DEVICE_T_BD_MASK  DEVICE_T_ERROR
+#define HOST_T_BD_MASK    HOST_T_ERROR
+
+#define T_PID_SHIFT   6
+#define T_PID_DATA0   0x00800000         /* Data 0 toggle */
+#define T_PID_DATA1   0x00c00000         /* Data 1 toggle */
+
+/*
+ * receive BD's status
+ */
+#define R_E           0x80000000         /* buffer empty */
+#define R_W           0x20000000         /* wrap bit */
+#define R_I           0x10000000         /* interrupt on reception */
+#define R_L           0x08000000         /* last */
+#define R_F           0x04000000         /* first */
+#define R_PID         0x00c00000         /* packet id */
+#define R_NO          0x00100000         /* Rx Non Octet Aligned Packet */
+#define R_AB          0x00080000         /* Frame Aborted */
+#define R_CR          0x00040000         /* CRC Error */
+#define R_OV          0x00020000         /* Overrun */
+
+#define R_ERROR       (R_NO | R_AB | R_CR | R_OV)
+#define R_BD_MASK     R_ERROR
+
+#define R_PID_DATA0   0x00000000
+#define R_PID_DATA1   0x00400000
+#define R_PID_SETUP   0x00800000
+
+#ifdef DEBUG
+#define VDBG(fmt, args...)	printk(KERN_DEBUG "[%s]  " fmt "\n", \
+				__func__, ## args)
+#else
+#define VDBG(fmt, args...)       do {} while (0)
+#endif
+
+#define CPM_USB_STOP_TX 0x2e600000
+#define CPM_USB_RESTART_TX 0x2e600000
+#define CPM_USB_STOP_TX_OPCODE 0x0a
+#define CPM_USB_RESTART_TX_OPCODE 0x0b
+#define CPM_USB_EP_SHIFT 5
+
+#endif
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index 17d9905..4e3107d 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -151,6 +151,13 @@
 #define	gadget_is_m66592(g)	0
 #endif
 
+/* Freescale CPM/QE UDC SUPPORT */
+#ifdef CONFIG_USB_GADGET_FSL_QE
+#define gadget_is_fsl_qe(g)	!strcmp("fsl_qe_udc", (g)->name)
+#else
+#define gadget_is_fsl_qe(g)	0
+#endif
+
 
 // CONFIG_USB_GADGET_SX2
 // CONFIG_USB_GADGET_AU1X00
@@ -216,6 +223,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
 		return 0x20;
 	else if (gadget_is_m66592(gadget))
 		return 0x21;
+	else if (gadget_is_fsl_qe(gadget))
+		return 0x22;
 	return -ENOENT;
 }
 
-- 
1.5.5.1.248.g4b17

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
  2008-08-28  9:43 [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver Li Yang
@ 2008-08-28 15:04 ` Arnd Bergmann
  2008-08-28 17:22   ` Alan Stern
                     ` (2 more replies)
  2008-08-28 16:39 ` Scott Wood
                   ` (3 subsequent siblings)
  4 siblings, 3 replies; 33+ messages in thread
From: Arnd Bergmann @ 2008-08-28 15:04 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: greg, Li Yang, linux-usb, dbrownell, linux-kernel

On Thursday 28 August 2008, Li Yang wrote:
> Some of Freescale SoC chips have a QE or CPM co-processor which
> supports full speed USB.  The driver adds device mode support
> of both QE and CPM USB controller to Linux USB gadget.  The
> driver is tested with MPC8360 and MPC8272, and should work with
> other models having QE/CPM given minor tweaks.

Looks pretty good, just a few comments on the driver:
  
> +config USB_GADGET_FSL_QE
> +	boolean "Freescale QE/CPM USB Device Controller"
> +	depends on FSL_SOC && (QUICC_ENGINE || CPM)
> +	help
> +	   Some of Freescale PowerPC processors have a Full Speed
> +	   QE/CPM2 USB controller, which support device mode with 4
> +	   programmable endpoints. This driver supports the
> +	   controller in the MPC8360 and MPC8272, and should work with
> +	   controllers having QE or CPM2, given minor tweaks.
> +
> +	   Say "y" to link the driver statically, or "m" to build a
> +	   dynamically linked module called "fsl_qe_udc" and force all
> +	   gadget drivers to also be dynamically linked.
> +
> +config USB_FSL_QE
> +	tristate
> +	depends on USB_GADGET_FSL_QE
> +	default USB_GADGET
> +	select USB_GADGET_SELECTED


Why do you need the two config options, not just one?

> +#ifdef CONFIG_CPM2
> +#include <asm/cpm.h>
> +
> +#define qe_muram_addr cpm_muram_addr
> +#define qe_muram_offset cpm_muram_offset
> +#define qe_muram_alloc cpm_muram_alloc
> +#define qe_muram_free cpm_muram_free
> +#endif
...
> +static int qe_ep_cmd_restarttx(struct qe_ep *ep)
> +{
> +	u8 ep_num;
> +#ifdef CONFIG_CPM2
> +	u32 command;
> +	u8 opcode;
> +
> +	ep_num = ep->epnum << CPM_USB_EP_SHIFT;
> +	command = CPM_USB_RESTART_TX | (u32)ep_num;
> +	opcode = CPM_USB_RESTART_TX_OPCODE;
> +	cpm_command(command, opcode);
> +#else
> +	ep_num = ep->epnum;
> +	qe_issue_cmd(QE_USB_RESTART_TX, QE_CR_SUBBLOCK_USB, ep_num, 0);
> +#endif
> +	return 0;
> +}

This part doesn't look good, you should try to avoid hardcoding
the specific type of chip (QE or CPM2) here. AFAICT, you can build
a multiplatform kernel that supports both QE and CPM2, but your code
here would be broken in that case if you try to run it on QE.

> +static void setup_received_handle(struct qe_udc *udc,
> +					struct usb_ctrlrequest *setup);
> +static int qe_ep_rxframe_handle(struct qe_ep *ep);
> +static void ep0_req_complete(struct qe_udc *udc, struct qe_req *req);

Better try to avoid static forward declarations by reordering your
functions in call order. That is the common coding style and makes
drivers easier to read when you're used to it.

> +
> +	tasklet_schedule(&udc->rx_tasklet);

Not a problem, but an observation: Most new code uses work queues instead
of tasklets these days, which gives you more predictable real time
latencies.
If you don't have a specific reason to prefer a tasklet, just use
a workqueue here.

> +/*-------------------------------------------------------------------------
> +	Gadget driver register and unregister.
> + --------------------------------------------------------------------------*/
> +int usb_gadget_register_driver(struct usb_gadget_driver *driver)
> +EXPORT_SYMBOL(usb_gadget_register_driver);
> +
> +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
> +EXPORT_SYMBOL(usb_gadget_unregister_driver);

Not addressing this driver in particular, but the USB gadget layer in
general: This is a horrible interface, since every gadget driver exports
the same symbols, you can never build a kernel that includes more than
one gadget driver. Even if the drivers are all built as modules, simply
loading one of them prevents loading another one.

> +	udc->dev = &ofdev->dev;
> +
> +	/* use the default address for the usb parameter */
> +	prop = of_get_property(np, "reg", &nsize);
> +	offset = of_read_number(prop + 2, 1);
> +	size = of_read_number(prop + 3, 1);

You should not assume a specific layout of the reg property but
rather use the simpler of_iomap function to get the registers,
independent of the bus mapping.

> +/*-------------------------------------------------------------------------*/
> +static struct of_device_id qe_udc_match[] = {
> +	{
> +		.compatible = "fsl,mpc8360-qe-usb",
> +	},
> +	{
> +		.compatible = "fsl,mpc8272-cpm-usb",
> +	},
> +	{},
> +};

Since they are evidently different implementations, you should
fill out the data field in the device id as the easiest way
to identify the two (and possible future) versions of the hardware.

> +#ifdef DEBUG
> +#define VDBG(fmt, args...)	printk(KERN_DEBUG "[%s]  " fmt "\n", \
> +				__func__, ## args)
> +#else
> +#define VDBG(fmt, args...)       do {} while (0)
> +#endif

please use the standard dev_debug and pr_debug macros instead of defining
your own.

	Arnd <><

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
  2008-08-28  9:43 [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver Li Yang
  2008-08-28 15:04 ` Arnd Bergmann
@ 2008-08-28 16:39 ` Scott Wood
  2008-08-29  9:35   ` Li Yang
  2008-09-01 13:52 ` Anton Vorontsov
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 33+ messages in thread
From: Scott Wood @ 2008-08-28 16:39 UTC (permalink / raw)
  To: Li Yang; +Cc: greg, linux-usb, linux-kernel, dbrownell, linuxppc-dev

On Thu, Aug 28, 2008 at 05:43:33PM +0800, Li Yang wrote:
> +config USB_GADGET_FSL_QE
> +	boolean "Freescale QE/CPM USB Device Controller"
> +	depends on FSL_SOC && (QUICC_ENGINE || CPM)
> +	help
> +	   Some of Freescale PowerPC processors have a Full Speed
> +	   QE/CPM2 USB controller, which support device mode with 4
> +	   programmable endpoints. This driver supports the
> +	   controller in the MPC8360 and MPC8272, and should work with
> +	   controllers having QE or CPM2, given minor tweaks.
> +
> +	   Say "y" to link the driver statically, or "m" to build a
> +	   dynamically linked module called "fsl_qe_udc" and force all
> +	   gadget drivers to also be dynamically linked.

How can you say "m" to something that is not a tristate?

-Scott

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
  2008-08-28 15:04 ` Arnd Bergmann
@ 2008-08-28 17:22   ` Alan Stern
  2008-08-28 17:53     ` Scott Wood
  2008-08-29  8:57   ` Li Yang
  2008-09-24 20:10   ` David Brownell
  2 siblings, 1 reply; 33+ messages in thread
From: Alan Stern @ 2008-08-28 17:22 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: dbrownell, greg, linux-usb, linux-kernel, linuxppc-dev, Li Yang

On Thu, 28 Aug 2008, Arnd Bergmann wrote:

> Not addressing this driver in particular, but the USB gadget layer in
> general: This is a horrible interface, since every gadget driver exports
> the same symbols, you can never build a kernel that includes more than
> one gadget driver. Even if the drivers are all built as modules, simply
> loading one of them prevents loading another one.

This was done deliberately.  The relevant standards state that a USB
device can have no more than one peripheral interface.

Now, I don't claim to fully support this decision.  But there is a 
reason behind it; it's not just a case of bad design.

Alan Stern

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
  2008-08-28 17:22   ` Alan Stern
@ 2008-08-28 17:53     ` Scott Wood
  2008-08-28 20:16       ` Alan Stern
  0 siblings, 1 reply; 33+ messages in thread
From: Scott Wood @ 2008-08-28 17:53 UTC (permalink / raw)
  To: Alan Stern
  Cc: dbrownell, Arnd Bergmann, greg, linux-usb, linux-kernel,
	linuxppc-dev, Li Yang

Alan Stern wrote:
> This was done deliberately.  The relevant standards state that a USB
> device can have no more than one peripheral interface.

Does building a kernel image that can run on different hardware without 
rebuilding also violate the "relevant standards"?

And who's to say that there aren't multiple USB devices on a single 
board, that just happen to share a CPU and memory? :-)

-Scott

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
  2008-08-28 17:53     ` Scott Wood
@ 2008-08-28 20:16       ` Alan Stern
  2008-08-28 22:27         ` Arnd Bergmann
  0 siblings, 1 reply; 33+ messages in thread
From: Alan Stern @ 2008-08-28 20:16 UTC (permalink / raw)
  To: Scott Wood
  Cc: dbrownell, Arnd Bergmann, greg, linux-usb, linux-kernel,
	linuxppc-dev, Li Yang

On Thu, 28 Aug 2008, Scott Wood wrote:

> Alan Stern wrote:
> > This was done deliberately.  The relevant standards state that a USB
> > device can have no more than one peripheral interface.
> 
> Does building a kernel image that can run on different hardware without 
> rebuilding also violate the "relevant standards"?

No.  That isn't what Arnd was concerned about.  He noted that even if 
you did build multiple modules, only one of them could be loaded at any 
time.

> And who's to say that there aren't multiple USB devices on a single 
> board, that just happen to share a CPU and memory? :-)

That's why I don't fully support this decision.  But I wanted to point 
out that there _was_ a conscious decision, as opposed to bad 
programming through sheer carelessness.

Alan Stern

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
  2008-08-28 20:16       ` Alan Stern
@ 2008-08-28 22:27         ` Arnd Bergmann
  2008-08-29 16:05           ` Alan Stern
  2008-09-24 20:15           ` David Brownell
  0 siblings, 2 replies; 33+ messages in thread
From: Arnd Bergmann @ 2008-08-28 22:27 UTC (permalink / raw)
  To: Alan Stern
  Cc: dbrownell, greg, linux-usb, linux-kernel, linuxppc-dev,
	Scott Wood, Li Yang

On Thursday 28 August 2008, Alan Stern wrote:
> On Thu, 28 Aug 2008, Scott Wood wrote:
>=20
> > Alan Stern wrote:
> > > This was done deliberately. =A0The relevant standards state that a USB
> > > device can have no more than one peripheral interface.
> >=20
> > Does building a kernel image that can run on different hardware without=
=20
> > rebuilding also violate the "relevant standards"?
>=20
> No. =A0That isn't what Arnd was concerned about. =A0He noted that even if=
=20
> you did build multiple modules, only one of them could be loaded at any=20
> time.

Well, actually it was exactly what I was concerned about ;-)

The way I understand the code, it is layered into the hardware specific
part and the protocol specific part, which are connected through
the interfaces I pointed out.

The standard requires that there can only be one protocol handler
per physical interface, which is a reasonable limitation.
However, what the Linux implementation actually enforces is
that there can only be one hardware specific driver built or loaded
into the kernel, which just looks like an arbitrary restriction
that does not actually help.

If the gadget hardware drivers were registering the device with a
gadget_bus_type, you could still enforce the "only one protocol"
rule by binding every protocol to every device in that bus type.

	Arnd <><

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
  2008-08-29  8:57   ` Li Yang
@ 2008-08-29  8:57     ` Arnd Bergmann
  0 siblings, 0 replies; 33+ messages in thread
From: Arnd Bergmann @ 2008-08-29  8:57 UTC (permalink / raw)
  To: leoli; +Cc: linuxppc-dev, linux-usb, linux-kernel, dbrownell, greg

On Friday 29 August 2008, Li Yang wrote:
>=20
> > Not a problem, but an observation: Most new code uses work queues inste=
ad
> > of tasklets these days, which gives you more predictable real time
> > latencies.
> > If you don't have a specific reason to prefer a tasklet, just use
> > a workqueue here.
>=20
> Is this truly a trend? =A0Work queue is more flexible but it has higher
> latency. =A0Why are work queues preferred?

Most drivers don't need the low irq to bottom half latency. As I said,
not a problem at all, just my observation. In 2.6.27, we have around
three times more new workqueue usages than new tasklet usages. My
feeling from doing reviews was that it would be closer to a factor
of ten, but I guess I was wrong in that.

	Arnd <><

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
  2008-08-28 15:04 ` Arnd Bergmann
  2008-08-28 17:22   ` Alan Stern
@ 2008-08-29  8:57   ` Li Yang
  2008-08-29  8:57     ` Arnd Bergmann
  2008-09-24 20:10   ` David Brownell
  2 siblings, 1 reply; 33+ messages in thread
From: Li Yang @ 2008-08-29  8:57 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linuxppc-dev, linux-usb, linux-kernel, dbrownell, greg

On Thu, 2008-08-28 at 17:04 +0200, Arnd Bergmann wrote:
> On Thursday 28 August 2008, Li Yang wrote:
> > Some of Freescale SoC chips have a QE or CPM co-processor which
> > supports full speed USB.  The driver adds device mode support
> > of both QE and CPM USB controller to Linux USB gadget.  The
> > driver is tested with MPC8360 and MPC8272, and should work with
> > other models having QE/CPM given minor tweaks.
> 
> Looks pretty good, just a few comments on the driver:
>   
> > +config USB_GADGET_FSL_QE
> > +	boolean "Freescale QE/CPM USB Device Controller"
> > +	depends on FSL_SOC && (QUICC_ENGINE || CPM)
> > +	help
> > +	   Some of Freescale PowerPC processors have a Full Speed
> > +	   QE/CPM2 USB controller, which support device mode with 4
> > +	   programmable endpoints. This driver supports the
> > +	   controller in the MPC8360 and MPC8272, and should work with
> > +	   controllers having QE or CPM2, given minor tweaks.
> > +
> > +	   Say "y" to link the driver statically, or "m" to build a
> > +	   dynamically linked module called "fsl_qe_udc" and force all
> > +	   gadget drivers to also be dynamically linked.
> > +
> > +config USB_FSL_QE
> > +	tristate
> > +	depends on USB_GADGET_FSL_QE
> > +	default USB_GADGET
> > +	select USB_GADGET_SELECTED
> 
> 
> Why do you need the two config options, not just one?

This is common for udc drivers.  I guess this measure is used to make
the selection of udc drivers a choice list while still make it possible
to compiled as module.

> 
> > +#ifdef CONFIG_CPM2
> > +#include <asm/cpm.h>
> > +
> > +#define qe_muram_addr cpm_muram_addr
> > +#define qe_muram_offset cpm_muram_offset
> > +#define qe_muram_alloc cpm_muram_alloc
> > +#define qe_muram_free cpm_muram_free
> > +#endif
> ...
> > +static int qe_ep_cmd_restarttx(struct qe_ep *ep)
> > +{
> > +	u8 ep_num;
> > +#ifdef CONFIG_CPM2
> > +	u32 command;
> > +	u8 opcode;
> > +
> > +	ep_num = ep->epnum << CPM_USB_EP_SHIFT;
> > +	command = CPM_USB_RESTART_TX | (u32)ep_num;
> > +	opcode = CPM_USB_RESTART_TX_OPCODE;
> > +	cpm_command(command, opcode);
> > +#else
> > +	ep_num = ep->epnum;
> > +	qe_issue_cmd(QE_USB_RESTART_TX, QE_CR_SUBBLOCK_USB, ep_num, 0);
> > +#endif
> > +	return 0;
> > +}
> 
> This part doesn't look good, you should try to avoid hardcoding
> the specific type of chip (QE or CPM2) here. AFAICT, you can build
> a multiplatform kernel that supports both QE and CPM2, but your code
> here would be broken in that case if you try to run it on QE.

Ok.

> 
> > +static void setup_received_handle(struct qe_udc *udc,
> > +					struct usb_ctrlrequest *setup);
> > +static int qe_ep_rxframe_handle(struct qe_ep *ep);
> > +static void ep0_req_complete(struct qe_udc *udc, struct qe_req *req);
> 
> Better try to avoid static forward declarations by reordering your
> functions in call order. That is the common coding style and makes
> drivers easier to read when you're used to it.
> 
> > +
> > +	tasklet_schedule(&udc->rx_tasklet);
> 
> Not a problem, but an observation: Most new code uses work queues instead
> of tasklets these days, which gives you more predictable real time
> latencies.
> If you don't have a specific reason to prefer a tasklet, just use
> a workqueue here.

Is this truly a trend?  Work queue is more flexible but it has higher
latency.  Why are work queues preferred?

- Leo

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
  2008-08-28 16:39 ` Scott Wood
@ 2008-08-29  9:35   ` Li Yang
  0 siblings, 0 replies; 33+ messages in thread
From: Li Yang @ 2008-08-29  9:35 UTC (permalink / raw)
  To: Scott Wood; +Cc: greg, linux-usb, linux-kernel, dbrownell, linuxppc-dev

On Thu, 2008-08-28 at 11:39 -0500, Scott Wood wrote:
> On Thu, Aug 28, 2008 at 05:43:33PM +0800, Li Yang wrote:
> > +config USB_GADGET_FSL_QE
> > +	boolean "Freescale QE/CPM USB Device Controller"
> > +	depends on FSL_SOC && (QUICC_ENGINE || CPM)
> > +	help
> > +	   Some of Freescale PowerPC processors have a Full Speed
> > +	   QE/CPM2 USB controller, which support device mode with 4
> > +	   programmable endpoints. This driver supports the
> > +	   controller in the MPC8360 and MPC8272, and should work with
> > +	   controllers having QE or CPM2, given minor tweaks.
> > +
> > +	   Say "y" to link the driver statically, or "m" to build a
> > +	   dynamically linked module called "fsl_qe_udc" and force all
> > +	   gadget drivers to also be dynamically linked.
> 
> How can you say "m" to something that is not a tristate?

Well, I copied these from other udc drivers.  Udc drivers can be
compiled as module, through the CONFIG_USB_GADGET option.  Though, the
description here is not accurate.

- Leo

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
  2008-08-28 22:27         ` Arnd Bergmann
@ 2008-08-29 16:05           ` Alan Stern
  2008-08-29 16:29             ` Arnd Bergmann
  2008-09-24 20:15           ` David Brownell
  1 sibling, 1 reply; 33+ messages in thread
From: Alan Stern @ 2008-08-29 16:05 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: dbrownell, greg, linux-usb, linux-kernel, linuxppc-dev,
	Scott Wood, Li Yang

On Fri, 29 Aug 2008, Arnd Bergmann wrote:

> > > Does building a kernel image that can run on different hardware without 
> > > rebuilding also violate the "relevant standards"?
> > 
> > No.  That isn't what Arnd was concerned about.  He noted that even if 
> > you did build multiple modules, only one of them could be loaded at any 
> > time.
> 
> Well, actually it was exactly what I was concerned about ;-)
> 
> The way I understand the code, it is layered into the hardware specific
> part and the protocol specific part, which are connected through
> the interfaces I pointed out.

That's right.

> The standard requires that there can only be one protocol handler
> per physical interface, which is a reasonable limitation.

No, you've got it exactly backward.  There can be multiple protocol 
handlers per physical interface, but there must be only one physical 
interface per device.

> However, what the Linux implementation actually enforces is
> that there can only be one hardware specific driver built or loaded
> into the kernel, which just looks like an arbitrary restriction
> that does not actually help.

Not at all -- it is an implementation of the constraint that there be 
only one physical interface.

> If the gadget hardware drivers were registering the device with a
> gadget_bus_type, you could still enforce the "only one protocol"
> rule by binding every protocol to every device in that bus type.

There is no such rule.

Alan Stern

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
  2008-08-29 16:05           ` Alan Stern
@ 2008-08-29 16:29             ` Arnd Bergmann
  2008-08-29 17:19               ` Alan Stern
  0 siblings, 1 reply; 33+ messages in thread
From: Arnd Bergmann @ 2008-08-29 16:29 UTC (permalink / raw)
  To: Alan Stern
  Cc: dbrownell, greg, linux-usb, linux-kernel, linuxppc-dev,
	Scott Wood, Li Yang

On Friday 29 August 2008, Alan Stern wrote:
> > The standard requires that there can only be one protocol handler
> > per physical interface, which is a reasonable limitation.
>=20
> No, you've got it exactly backward. =A0There can be multiple protocol=20
> handlers per physical interface, but there must be only one physical=20
> interface per device.

Maybe I am using wrong terminology, but I still don't see how that
fits together. Let me try to explain what I have understood so far:

The physical device is identified by a struct usb_gadget is defined
statically in the driver that exports the
usb_gadget_{un,}register_driver() functions. Obviously there can only
be one physical interface per physical device, I was not arguing
against that.

The protocol handler is identified by a usb_gadget_driver and defined
in a driver that calls usb_gadget_register_driver().
You say that there can be multiple protocol handlers, which I don't
understand because the protocol handlers all call set_gadget_data,
which overwrites the previous driver_data field in struct usb_gadget,
making it impossible to load more than one simultaneously.
=20
> > However, what the Linux implementation actually enforces is
> > that there can only be one hardware specific driver built or loaded
> > into the kernel, which just looks like an arbitrary restriction
> > that does not actually help.
>=20
> Not at all -- it is an implementation of the constraint that there be=20
> only one physical interface.

How that? Taking drivers/usb/gadget/fsl_usb2_udc.c as an example, it will
create a new fsl_udc structure for each matching platform_device it finds
(though it will leak every one except the last one), so the interface
does not limit the number of physical interfaces at all to this point,
the implementation of the every gadget hardware driver does this.

The real problem is that you cannot build a kernel that has both
fsl_usb2_udc.c and fsl_qe_udc.c built in. Having both drivers loaded
would not violate the one-interface rule, because only one of them
would find hardware to bind to on a given system, just like you can
load both the uhci and ohci drivers without them interfering.

	Arnd <><

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
  2008-08-29 16:29             ` Arnd Bergmann
@ 2008-08-29 17:19               ` Alan Stern
  2008-08-29 17:38                 ` Arnd Bergmann
  0 siblings, 1 reply; 33+ messages in thread
From: Alan Stern @ 2008-08-29 17:19 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: dbrownell, greg, linux-usb, linux-kernel, linuxppc-dev,
	Scott Wood, Li Yang

On Fri, 29 Aug 2008, Arnd Bergmann wrote:

> On Friday 29 August 2008, Alan Stern wrote:
> > > The standard requires that there can only be one protocol handler
> > > per physical interface, which is a reasonable limitation.
> > 
> > No, you've got it exactly backward.  There can be multiple protocol 
> > handlers per physical interface, but there must be only one physical 
> > interface per device.
> 
> Maybe I am using wrong terminology, but I still don't see how that
> fits together. Let me try to explain what I have understood so far:
> 
> The physical device is identified by a struct usb_gadget is defined
> statically in the driver that exports the
> usb_gadget_{un,}register_driver() functions. Obviously there can only
> be one physical interface per physical device, I was not arguing
> against that.

I thought you _were_ arguing against that.  Unless I misunderstood,
your original complaint was that since each peripheral controller
driver defines usb_gadget_{un}register_driver, there can be only one
controller driver loaded at a time.

However see below...

> The protocol handler is identified by a usb_gadget_driver and defined
> in a driver that calls usb_gadget_register_driver().
> You say that there can be multiple protocol handlers, which I don't
> understand because the protocol handlers all call set_gadget_data,
> which overwrites the previous driver_data field in struct usb_gadget,
> making it impossible to load more than one simultaneously.

It's a new feature, only recently added to the kernel and not fully 
implemented yet.  The protocol handlers are encapsulated as "function" 
drivers, where multiple functions can be combined at link time into a 
single gadget.

> > > However, what the Linux implementation actually enforces is
> > > that there can only be one hardware specific driver built or loaded
> > > into the kernel, which just looks like an arbitrary restriction
> > > that does not actually help.
> > 
> > Not at all -- it is an implementation of the constraint that there be 
> > only one physical interface.
> 
> How that? Taking drivers/usb/gadget/fsl_usb2_udc.c as an example, it will
> create a new fsl_udc structure for each matching platform_device it finds
> (though it will leak every one except the last one), so the interface
> does not limit the number of physical interfaces at all to this point,
> the implementation of the every gadget hardware driver does this.

fsl_usb2_udc isn't a good example; its behavior is not typical.  Other
peripheral hardware drivers behave differently -- they define a single
static structure (or a dynamic singleton structure) and expect the
platform to contain no more than one matching device.  Look at
lh7a40x_udc.c for a more typical example.

> The real problem is that you cannot build a kernel that has both
> fsl_usb2_udc.c and fsl_qe_udc.c built in. Having both drivers loaded
> would not violate the one-interface rule, because only one of them
> would find hardware to bind to on a given system, just like you can
> load both the uhci and ohci drivers without them interfering.

I see your point.  Even worse, you can't build both of them as modules.

Personally I wouldn't mind relaxing the "one interface per device" 
rule.  But I'm not the Gadget maintainer...

(There is another problem.  If you have multiple peripheral interfaces,
how would you specify which one a particular gadget driver should bind
to?)

Alan Stern

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
  2008-08-29 17:19               ` Alan Stern
@ 2008-08-29 17:38                 ` Arnd Bergmann
  2008-08-29 21:22                   ` Alan Stern
  0 siblings, 1 reply; 33+ messages in thread
From: Arnd Bergmann @ 2008-08-29 17:38 UTC (permalink / raw)
  To: linuxppc-dev
  Cc: dbrownell, greg, linux-usb, linux-kernel, Alan Stern, Scott Wood,
	Li Yang

On Friday 29 August 2008, Alan Stern wrote:
> I thought you _were_ arguing against that. =A0Unless I misunderstood,
> your original complaint was that since each peripheral controller
> driver defines usb_gadget_{un}register_driver, there can be only one
> controller driver loaded at a time.

That's probably where the misunderstanding came from: I did not expect
more than one driver to actually be useful on a given system, but that
should IMHO not prevent you from loading the drivers using modprobe, or
building them all into the kernel.

	Arnd <><

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
  2008-08-29 17:38                 ` Arnd Bergmann
@ 2008-08-29 21:22                   ` Alan Stern
  0 siblings, 0 replies; 33+ messages in thread
From: Alan Stern @ 2008-08-29 21:22 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: dbrownell, greg, linux-usb, linux-kernel, linuxppc-dev,
	Scott Wood, Li Yang

On Fri, 29 Aug 2008, Arnd Bergmann wrote:

> On Friday 29 August 2008, Alan Stern wrote:
> > I thought you _were_ arguing against that.  Unless I misunderstood,
> > your original complaint was that since each peripheral controller
> > driver defines usb_gadget_{un}register_driver, there can be only one
> > controller driver loaded at a time.
> 
> That's probably where the misunderstanding came from: I did not expect
> more than one driver to actually be useful on a given system, but that
> should IMHO not prevent you from loading the drivers using modprobe, or
> building them all into the kernel.

All right, then we're in agreement.  :-)

Alan Stern

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
  2008-08-28  9:43 [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver Li Yang
  2008-08-28 15:04 ` Arnd Bergmann
  2008-08-28 16:39 ` Scott Wood
@ 2008-09-01 13:52 ` Anton Vorontsov
  2008-09-02  7:08   ` Li Yang
  2008-09-01 16:34 ` Anton Vorontsov
  2008-09-01 17:11 ` Anton Vorontsov
  4 siblings, 1 reply; 33+ messages in thread
From: Anton Vorontsov @ 2008-09-01 13:52 UTC (permalink / raw)
  To: Li Yang; +Cc: greg, linux-usb, linux-kernel, dbrownell, linuxppc-dev

On Thu, Aug 28, 2008 at 05:43:33PM +0800, Li Yang wrote:
> Some of Freescale SoC chips have a QE or CPM co-processor which
> supports full speed USB.  The driver adds device mode support
> of both QE and CPM USB controller to Linux USB gadget.  The
> driver is tested with MPC8360 and MPC8272, and should work with
> other models having QE/CPM given minor tweaks.
> 
> Signed-off-by: Xie Xiaobo <X.Xie@freescale.com>
> Signed-off-by: Li Yang <leoli@freescale.com>
> ---
> This is the second submission of the driver.  This version addressed:
> Comments from Anton Vorontsov.
> A lot of cosmetic problem.
> Sparse and various kernel DEBUG warnings.

You might want to consider some of the following changes (they're
tested with
http://ozlabs.org/pipermail/linuxppc-dev/2008-September/062360.html).

- remove qe_muram -> cpm_muram defines
- configure clocks per device tree via qe_usb_clock_set(). To use this
  function we should select QE_USB.

  NOTE: currently this means that CPM build will break. :-( We should
  probably implement generic cpm_clock_source() and cpm_usb_clock_set().

  Or.. we can move this to the board file (Do we ever need to change the
  clocks in this driver? For the Host driver we need this, since it
  can switch between low and full speed).

  What do you think?

- change 8360 compatible to 8323 (8323 is the first chip with QE USB,
  device trees for 8360 boards should specify fsl,mpc8323-qe-usb
  as a compatible).

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 4dbf622..e57c298 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -153,6 +153,7 @@ config USB_FSL_USB2
 config USB_GADGET_FSL_QE
 	boolean "Freescale QE/CPM USB Device Controller"
 	depends on FSL_SOC && (QUICC_ENGINE || CPM)
+	select QE_USB
 	help
 	   Some of Freescale PowerPC processors have a Full Speed
 	   QE/CPM2 USB controller, which support device mode with 4
diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c
index e448610..c1a0beb 100644
--- a/drivers/usb/gadget/fsl_qe_udc.c
+++ b/drivers/usb/gadget/fsl_qe_udc.c
@@ -36,20 +36,12 @@
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
 #include <linux/usb/otg.h>
+#include <asm/cpm.h>
 #include <asm/qe.h>
 #include <asm/dma.h>
 #include <asm/reg.h>
 #include "fsl_qe_udc.h"
 
-#ifdef CONFIG_CPM2
-#include <asm/cpm.h>
-
-#define qe_muram_addr cpm_muram_addr
-#define qe_muram_offset cpm_muram_offset
-#define qe_muram_alloc cpm_muram_alloc
-#define qe_muram_free cpm_muram_free
-#endif
-
 #define DRIVER_DESC     "Freescale QE/CPM USB Device Controller driver"
 #define DRIVER_AUTHOR   "Xie XiaoBo"
 #define DRIVER_VERSION  "1.0"
@@ -2490,6 +2482,7 @@ static int __devinit qe_udc_probe(struct of_device *ofdev,
 			const struct of_device_id *match)
 {
 	struct device_node *np = ofdev->node;
+	enum qe_clock clk;
 	struct qe_ep *ep;
 	unsigned int ret = 0;
 	unsigned int i;
@@ -2499,6 +2492,16 @@ static int __devinit qe_udc_probe(struct of_device *ofdev,
 	if (!prop || strcmp(prop, "peripheral"))
 		return -ENODEV;
 
+	prop = of_get_property(np, "fsl,fullspeed-clock", NULL);
+	if (!prop)
+		return -EINVAL;
+
+	clk = qe_clock_source(prop);
+	if (clk == QE_CLK_NONE || clk == QE_CLK_DUMMY)
+		return -EINVAL;
+
+	qe_usb_clock_set(clk, USB_CLOCK);
+
 	/* Initialize the udc structure including QH member and other member */
 	udc_controller = qe_udc_config(ofdev);
 	if (!udc_controller) {
@@ -2689,7 +2692,7 @@ static int __devexit qe_udc_remove(struct of_device *ofdev)
 /*-------------------------------------------------------------------------*/
 static struct of_device_id qe_udc_match[] = {
 	{
-		.compatible = "fsl,mpc8360-qe-usb",
+		.compatible = "fsl,mpc8323-qe-usb",
 	},
 	{
 		.compatible = "fsl,mpc8272-cpm-usb",
diff --git a/drivers/usb/gadget/fsl_qe_udc.h b/drivers/usb/gadget/fsl_qe_udc.h
index e2e9639..0ff9c00 100644
--- a/drivers/usb/gadget/fsl_qe_udc.h
+++ b/drivers/usb/gadget/fsl_qe_udc.h
@@ -17,6 +17,8 @@
 #ifndef __FSL_QE_UDC_H
 #define __FSL_QE_UDC_H
 
+#define USB_CLOCK			48000000
+
 #define USB_MAX_ENDPOINTS               4
 #define USB_MAX_PIPES                   USB_MAX_ENDPOINTS
 #define USB_EP0_MAX_SIZE		64

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
  2008-08-28  9:43 [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver Li Yang
                   ` (2 preceding siblings ...)
  2008-09-01 13:52 ` Anton Vorontsov
@ 2008-09-01 16:34 ` Anton Vorontsov
  2008-09-01 17:11 ` Anton Vorontsov
  4 siblings, 0 replies; 33+ messages in thread
From: Anton Vorontsov @ 2008-09-01 16:34 UTC (permalink / raw)
  To: Li Yang
  Cc: dbrownell, greg, linux-usb, linux-kernel, linuxppc-dev, Robert Jarzmik

On Thu, Aug 28, 2008 at 05:43:33PM +0800, Li Yang wrote:
> Some of Freescale SoC chips have a QE or CPM co-processor which
> supports full speed USB.  The driver adds device mode support
> of both QE and CPM USB controller to Linux USB gadget.  The
> driver is tested with MPC8360 and MPC8272, and should work with
> other models having QE/CPM given minor tweaks.
> 
> Signed-off-by: Xie Xiaobo <X.Xie@freescale.com>
> Signed-off-by: Li Yang <leoli@freescale.com>
> ---

Just found a recursive locking bug:

[...]
> +static int reset_queues(struct qe_udc *udc)
> +{

Note: this function is called from the IRQ, the IRQ handler
grabs udc->lock spinlock..

> +	u8 pipe;
> +
> +	for (pipe = 0; pipe < USB_MAX_ENDPOINTS; pipe++)
> +		udc_reset_ep_queue(udc, pipe);
> +
> +	/* report disconnect; the driver is already quiesced */
> +	udc->driver->disconnect(&udc->gadget);

In the disconnect(), g_ether driver will immediately call
qe_ep_disable() function which will try to grab &udc->lock
spinlock once again..

Not sure how to fix this properly... :-/

p.s. the same bug exists in omap_udc.c, pxa27x_udc.c and probably
other drivers as well... The only reason why it does not exploit
in most cases is that the spin_lock_irqsave for !SMP case turns
into simple local_irq_save().

-- 
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
  2008-08-28  9:43 [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver Li Yang
                   ` (3 preceding siblings ...)
  2008-09-01 16:34 ` Anton Vorontsov
@ 2008-09-01 17:11 ` Anton Vorontsov
  2008-09-02  7:35   ` Li Yang
  4 siblings, 1 reply; 33+ messages in thread
From: Anton Vorontsov @ 2008-09-01 17:11 UTC (permalink / raw)
  To: Li Yang; +Cc: greg, linux-usb, linux-kernel, dbrownell, linuxppc-dev

On Thu, Aug 28, 2008 at 05:43:33PM +0800, Li Yang wrote:
> Some of Freescale SoC chips have a QE or CPM co-processor which
> supports full speed USB.  The driver adds device mode support
> of both QE and CPM USB controller to Linux USB gadget.  The
> driver is tested with MPC8360 and MPC8272, and should work with
> other models having QE/CPM given minor tweaks.
> 
> Signed-off-by: Xie Xiaobo <X.Xie@freescale.com>
> Signed-off-by: Li Yang <leoli@freescale.com>
> ---
> This is the second submission of the driver.  This version addressed:
> Comments from Anton Vorontsov.
> A lot of cosmetic problem.
> Sparse and various kernel DEBUG warnings.

Just caught this:

g_ether gadget: high speed config #1: CDC Ethernet (ECM)
BUG: sleeping function called from invalid context at mm/page_alloc.c:1450
in_atomic():1, irqs_disabled():1
Call Trace:
[c03a9c30] [c0008a90] show_stack+0x4c/0x16c (unreliable)
[c03a9c70] [c002cd28] __might_sleep+0xe4/0x114
[c03a9c80] [c006e550] __alloc_pages_internal+0x328/0x42c
[c03a9d00] [c006e67c] __get_free_pages+0x28/0x68
[c03a9d10] [c0090efc] __kmalloc+0xc0/0xe4
[c03a9d30] [c01d8efc] qe_ep_rxbd_update+0x98/0x1e0
[c03a9d50] [c01d90f0] qe_ep_init+0xac/0x37c
[c03a9d70] [c01d9458] qe_ep_enable+0x98/0xb8
[c03a9d80] [c01db268] gether_connect+0xa0/0x178
[c03a9da0] [c01dbba8] ecm_set_alt+0x108/0x12c
[c03a9db0] [c01dc988] composite_setup+0x2ec/0x3a8
[c03a9de0] [c01d8bec] setup_received_handle+0x1e4/0x26c
[c03a9e00] [c01d8ce0] ep0_setup_handle+0x6c/0x74
[c03a9e10] [c01d8e54] qe_ep0_rx+0x16c/0x17c
[c03a9e30] [c01d9db8] rx_irq+0x88/0xc0
[c03a9e40] [c01da32c] qe_udc_irq+0x10c/0x134
[c03a9e60] [c00631f4] handle_IRQ_event+0x5c/0xb0
[c03a9e80] [c006526c] handle_level_irq+0xa8/0x144
[c03a9ea0] [c002a5d0] qe_ic_cascade_low_ipic+0x58/0x70
[c03a9eb0] [c0006820] do_IRQ+0xa4/0xc8
[c03a9ec0] [c0013e14] ret_from_except+0x0/0x14
--- Exception: 501 at cpu_idle+0xa0/0x104
    LR = cpu_idle+0xa0/0x104
[c03a9f80] [c0009d88] cpu_idle+0x50/0x104 (unreliable)
[c03a9fa0] [c029307c] _etext+0x7c/0x90
[c03a9fc0] [c035491c] start_kernel+0x1ac/0x230
[c03a9ff0] [00003438] 0x3438

The workaround is obvious (below), but should we really allocate
things in interrupts? Can we just allocate everything in probe()
and free in remove()?


diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c
index c1a0beb..c54863b 100644
--- a/drivers/usb/gadget/fsl_qe_udc.c
+++ b/drivers/usb/gadget/fsl_qe_udc.c
@@ -433,7 +433,7 @@ static int qe_ep_rxbd_update(struct qe_ep *ep)
 
 	bd = ep->rxbase;
 
-	ep->rxframe = kmalloc(sizeof(*ep->rxframe), GFP_KERNEL);
+	ep->rxframe = kmalloc(sizeof(*ep->rxframe), GFP_ATOMIC);
 	if (ep->rxframe == NULL) {
 		dev_err(ep->udc->dev, "malloc rxframe failed\n");
 		return -ENOMEM;
@@ -447,7 +447,7 @@ static int qe_ep_rxbd_update(struct qe_ep *ep)
 		bdring_len = USB_BDRING_LEN;
 
 	size = (ep->ep.maxpacket + USB_CRC_SIZE + 2) * (bdring_len + 1);
-	ep->rxbuffer = kzalloc(size, GFP_KERNEL);
+	ep->rxbuffer = kzalloc(size, GFP_ATOMIC);
 	if (ep->rxbuffer == NULL) {
 		dev_err(ep->udc->dev, "malloc rxbuffer failed,size=%d\n",
 				size);
@@ -680,7 +680,7 @@ static int qe_ep_init(struct qe_udc *udc,
 	}
 
 	if ((ep->tm == USBP_TM_CTL) || (ep->dir == USB_DIR_IN)) {
-		ep->txframe = kmalloc(sizeof(*ep->txframe), GFP_KERNEL);
+		ep->txframe = kmalloc(sizeof(*ep->txframe), GFP_ATOMIC);
 		if (ep->txframe == NULL) {
 			dev_err(udc->dev, "malloc txframe failed\n");
 			goto en_done;
@@ -1930,7 +1930,9 @@ static int reset_queues(struct qe_udc *udc)
 		udc_reset_ep_queue(udc, pipe);
 
 	/* report disconnect; the driver is already quiesced */
+	spin_unlock(&udc->lock);
 	udc->driver->disconnect(&udc->gadget);
+	spin_lock(&udc->lock);
 
 	return 0;
 }

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
  2008-09-01 13:52 ` Anton Vorontsov
@ 2008-09-02  7:08   ` Li Yang
  0 siblings, 0 replies; 33+ messages in thread
From: Li Yang @ 2008-09-02  7:08 UTC (permalink / raw)
  To: avorontsov; +Cc: greg, linux-usb, linux-kernel, dbrownell, linuxppc-dev

On Mon, Sep 1, 2008 at 9:52 PM, Anton Vorontsov
<avorontsov@ru.mvista.com> wrote:
> On Thu, Aug 28, 2008 at 05:43:33PM +0800, Li Yang wrote:
>> Some of Freescale SoC chips have a QE or CPM co-processor which
>> supports full speed USB.  The driver adds device mode support
>> of both QE and CPM USB controller to Linux USB gadget.  The
>> driver is tested with MPC8360 and MPC8272, and should work with
>> other models having QE/CPM given minor tweaks.
>>
>> Signed-off-by: Xie Xiaobo <X.Xie@freescale.com>
>> Signed-off-by: Li Yang <leoli@freescale.com>
>> ---
>> This is the second submission of the driver.  This version addressed:
>> Comments from Anton Vorontsov.
>> A lot of cosmetic problem.
>> Sparse and various kernel DEBUG warnings.
>
> You might want to consider some of the following changes (they're
> tested with
> http://ozlabs.org/pipermail/linuxppc-dev/2008-September/062360.html).
>
> - remove qe_muram -> cpm_muram defines

Already done to address comment from Arnd.

> - configure clocks per device tree via qe_usb_clock_set(). To use this
>  function we should select QE_USB.
>
>  NOTE: currently this means that CPM build will break. :-( We should
>  probably implement generic cpm_clock_source() and cpm_usb_clock_set().
>
>  Or.. we can move this to the board file (Do we ever need to change the
>  clocks in this driver? For the Host driver we need this, since it
>  can switch between low and full speed).
>
>  What do you think?

I'd prefer to leave the clock setting for USB gadget mode in platform
code.  There is no need to change the clock in the udc driver.  There
is no point to make the speed of a USB device changable.

>
> - change 8360 compatible to 8323 (8323 is the first chip with QE USB,
>  device trees for 8360 boards should specify fsl,mpc8323-qe-usb
>  as a compatible).

Well. IIRC, 8360 came out earlier than 8323.  Whatsoever, 8360 is a
better representative for the PowerQUICC 2 pro family with a QE as it
has the full functionality.

>
> diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
> index 4dbf622..e57c298 100644
> --- a/drivers/usb/gadget/Kconfig
> +++ b/drivers/usb/gadget/Kconfig
> @@ -153,6 +153,7 @@ config USB_FSL_USB2
>  config USB_GADGET_FSL_QE
>        boolean "Freescale QE/CPM USB Device Controller"
>        depends on FSL_SOC && (QUICC_ENGINE || CPM)
> +       select QE_USB
>        help
>           Some of Freescale PowerPC processors have a Full Speed
>           QE/CPM2 USB controller, which support device mode with 4
> diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c
> index e448610..c1a0beb 100644
> --- a/drivers/usb/gadget/fsl_qe_udc.c
> +++ b/drivers/usb/gadget/fsl_qe_udc.c
> @@ -36,20 +36,12 @@
>  #include <linux/usb/ch9.h>
>  #include <linux/usb/gadget.h>
>  #include <linux/usb/otg.h>
> +#include <asm/cpm.h>
>  #include <asm/qe.h>
>  #include <asm/dma.h>
>  #include <asm/reg.h>
>  #include "fsl_qe_udc.h"
>
> -#ifdef CONFIG_CPM2
> -#include <asm/cpm.h>
> -
> -#define qe_muram_addr cpm_muram_addr
> -#define qe_muram_offset cpm_muram_offset
> -#define qe_muram_alloc cpm_muram_alloc
> -#define qe_muram_free cpm_muram_free
> -#endif
> -
>  #define DRIVER_DESC     "Freescale QE/CPM USB Device Controller driver"
>  #define DRIVER_AUTHOR   "Xie XiaoBo"
>  #define DRIVER_VERSION  "1.0"
> @@ -2490,6 +2482,7 @@ static int __devinit qe_udc_probe(struct of_device *ofdev,
>                        const struct of_device_id *match)
>  {
>        struct device_node *np = ofdev->node;
> +       enum qe_clock clk;
>        struct qe_ep *ep;
>        unsigned int ret = 0;
>        unsigned int i;
> @@ -2499,6 +2492,16 @@ static int __devinit qe_udc_probe(struct of_device *ofdev,
>        if (!prop || strcmp(prop, "peripheral"))
>                return -ENODEV;
>
> +       prop = of_get_property(np, "fsl,fullspeed-clock", NULL);
> +       if (!prop)
> +               return -EINVAL;
> +
> +       clk = qe_clock_source(prop);
> +       if (clk == QE_CLK_NONE || clk == QE_CLK_DUMMY)
> +               return -EINVAL;
> +
> +       qe_usb_clock_set(clk, USB_CLOCK);
> +
>        /* Initialize the udc structure including QH member and other member */
>        udc_controller = qe_udc_config(ofdev);
>        if (!udc_controller) {
> @@ -2689,7 +2692,7 @@ static int __devexit qe_udc_remove(struct of_device *ofdev)
>  /*-------------------------------------------------------------------------*/
>  static struct of_device_id qe_udc_match[] = {
>        {
> -               .compatible = "fsl,mpc8360-qe-usb",
> +               .compatible = "fsl,mpc8323-qe-usb",
>        },
>        {
>                .compatible = "fsl,mpc8272-cpm-usb",
> diff --git a/drivers/usb/gadget/fsl_qe_udc.h b/drivers/usb/gadget/fsl_qe_udc.h
> index e2e9639..0ff9c00 100644
> --- a/drivers/usb/gadget/fsl_qe_udc.h
> +++ b/drivers/usb/gadget/fsl_qe_udc.h
> @@ -17,6 +17,8 @@
>  #ifndef __FSL_QE_UDC_H
>  #define __FSL_QE_UDC_H
>
> +#define USB_CLOCK                      48000000
> +
>  #define USB_MAX_ENDPOINTS               4
>  #define USB_MAX_PIPES                   USB_MAX_ENDPOINTS
>  #define USB_EP0_MAX_SIZE               64
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
  2008-09-01 17:11 ` Anton Vorontsov
@ 2008-09-02  7:35   ` Li Yang
  2008-09-02  7:57     ` Joakim Tjernlund
  0 siblings, 1 reply; 33+ messages in thread
From: Li Yang @ 2008-09-02  7:35 UTC (permalink / raw)
  To: avorontsov; +Cc: greg, linux-usb, linux-kernel, dbrownell, linuxppc-dev

On Tue, Sep 2, 2008 at 1:11 AM, Anton Vorontsov
<avorontsov@ru.mvista.com> wrote:
> On Thu, Aug 28, 2008 at 05:43:33PM +0800, Li Yang wrote:
>> Some of Freescale SoC chips have a QE or CPM co-processor which
>> supports full speed USB.  The driver adds device mode support
>> of both QE and CPM USB controller to Linux USB gadget.  The
>> driver is tested with MPC8360 and MPC8272, and should work with
>> other models having QE/CPM given minor tweaks.
>>
>> Signed-off-by: Xie Xiaobo <X.Xie@freescale.com>
>> Signed-off-by: Li Yang <leoli@freescale.com>
>> ---
>> This is the second submission of the driver.  This version addressed:
>> Comments from Anton Vorontsov.
>> A lot of cosmetic problem.
>> Sparse and various kernel DEBUG warnings.
>
> Just caught this:
>
> g_ether gadget: high speed config #1: CDC Ethernet (ECM)
> BUG: sleeping function called from invalid context at mm/page_alloc.c:1450
> in_atomic():1, irqs_disabled():1
> Call Trace:
> [c03a9c30] [c0008a90] show_stack+0x4c/0x16c (unreliable)
> [c03a9c70] [c002cd28] __might_sleep+0xe4/0x114
> [c03a9c80] [c006e550] __alloc_pages_internal+0x328/0x42c
> [c03a9d00] [c006e67c] __get_free_pages+0x28/0x68
> [c03a9d10] [c0090efc] __kmalloc+0xc0/0xe4
> [c03a9d30] [c01d8efc] qe_ep_rxbd_update+0x98/0x1e0
> [c03a9d50] [c01d90f0] qe_ep_init+0xac/0x37c
> [c03a9d70] [c01d9458] qe_ep_enable+0x98/0xb8
> [c03a9d80] [c01db268] gether_connect+0xa0/0x178
> [c03a9da0] [c01dbba8] ecm_set_alt+0x108/0x12c
> [c03a9db0] [c01dc988] composite_setup+0x2ec/0x3a8
> [c03a9de0] [c01d8bec] setup_received_handle+0x1e4/0x26c
> [c03a9e00] [c01d8ce0] ep0_setup_handle+0x6c/0x74
> [c03a9e10] [c01d8e54] qe_ep0_rx+0x16c/0x17c
> [c03a9e30] [c01d9db8] rx_irq+0x88/0xc0
> [c03a9e40] [c01da32c] qe_udc_irq+0x10c/0x134
> [c03a9e60] [c00631f4] handle_IRQ_event+0x5c/0xb0
> [c03a9e80] [c006526c] handle_level_irq+0xa8/0x144
> [c03a9ea0] [c002a5d0] qe_ic_cascade_low_ipic+0x58/0x70
> [c03a9eb0] [c0006820] do_IRQ+0xa4/0xc8
> [c03a9ec0] [c0013e14] ret_from_except+0x0/0x14
> --- Exception: 501 at cpu_idle+0xa0/0x104
>    LR = cpu_idle+0xa0/0x104
> [c03a9f80] [c0009d88] cpu_idle+0x50/0x104 (unreliable)
> [c03a9fa0] [c029307c] _etext+0x7c/0x90
> [c03a9fc0] [c035491c] start_kernel+0x1ac/0x230
> [c03a9ff0] [00003438] 0x3438
>
> The workaround is obvious (below), but should we really allocate
> things in interrupts? Can we just allocate everything in probe()
> and free in remove()?

It's possible but wasteful.  We don't know which ep will be used and
what's the buffer size needed at probe time.

>
>
> diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c
> index c1a0beb..c54863b 100644
> --- a/drivers/usb/gadget/fsl_qe_udc.c
> +++ b/drivers/usb/gadget/fsl_qe_udc.c
> @@ -433,7 +433,7 @@ static int qe_ep_rxbd_update(struct qe_ep *ep)
>
>        bd = ep->rxbase;
>
> -       ep->rxframe = kmalloc(sizeof(*ep->rxframe), GFP_KERNEL);
> +       ep->rxframe = kmalloc(sizeof(*ep->rxframe), GFP_ATOMIC);
>        if (ep->rxframe == NULL) {
>                dev_err(ep->udc->dev, "malloc rxframe failed\n");
>                return -ENOMEM;
> @@ -447,7 +447,7 @@ static int qe_ep_rxbd_update(struct qe_ep *ep)
>                bdring_len = USB_BDRING_LEN;
>
>        size = (ep->ep.maxpacket + USB_CRC_SIZE + 2) * (bdring_len + 1);
> -       ep->rxbuffer = kzalloc(size, GFP_KERNEL);
> +       ep->rxbuffer = kzalloc(size, GFP_ATOMIC);
>        if (ep->rxbuffer == NULL) {
>                dev_err(ep->udc->dev, "malloc rxbuffer failed,size=%d\n",
>                                size);
> @@ -680,7 +680,7 @@ static int qe_ep_init(struct qe_udc *udc,
>        }
>
>        if ((ep->tm == USBP_TM_CTL) || (ep->dir == USB_DIR_IN)) {
> -               ep->txframe = kmalloc(sizeof(*ep->txframe), GFP_KERNEL);
> +               ep->txframe = kmalloc(sizeof(*ep->txframe), GFP_ATOMIC);
>                if (ep->txframe == NULL) {
>                        dev_err(udc->dev, "malloc txframe failed\n");
>                        goto en_done;
> @@ -1930,7 +1930,9 @@ static int reset_queues(struct qe_udc *udc)
>                udc_reset_ep_queue(udc, pipe);
>
>        /* report disconnect; the driver is already quiesced */
> +       spin_unlock(&udc->lock);
>        udc->driver->disconnect(&udc->gadget);
> +       spin_lock(&udc->lock);

Good catch.

>
>        return 0;
>  }
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
  2008-09-02  7:35   ` Li Yang
@ 2008-09-02  7:57     ` Joakim Tjernlund
  2008-09-02  8:12       ` [PATCH] usb: add Freescale QE/CPM USB peripheral controllerdriver Li Yang-R58472
  0 siblings, 1 reply; 33+ messages in thread
From: Joakim Tjernlund @ 2008-09-02  7:57 UTC (permalink / raw)
  To: Li Yang; +Cc: dbrownell, greg, linux-usb, linux-kernel, linuxppc-dev

On Tue, 2008-09-02 at 15:35 +0800, Li Yang wrote:
> On Tue, Sep 2, 2008 at 1:11 AM, Anton Vorontsov
> <avorontsov@ru.mvista.com> wrote:
> > On Thu, Aug 28, 2008 at 05:43:33PM +0800, Li Yang wrote:
> >> Some of Freescale SoC chips have a QE or CPM co-processor which
> >> supports full speed USB.  The driver adds device mode support
> >> of both QE and CPM USB controller to Linux USB gadget.  The
> >> driver is tested with MPC8360 and MPC8272, and should work with
> >> other models having QE/CPM given minor tweaks.
> >>
> >> Signed-off-by: Xie Xiaobo <X.Xie@freescale.com>
> >> Signed-off-by: Li Yang <leoli@freescale.com>
> >> ---
> >> This is the second submission of the driver.  This version addressed:
> >> Comments from Anton Vorontsov.
> >> A lot of cosmetic problem.
> >> Sparse and various kernel DEBUG warnings.
> >
> > Just caught this:
> >
> > g_ether gadget: high speed config #1: CDC Ethernet (ECM)

Does RNDIS work too? If not, is it possible to add or doesn't the HW
support it?

 Jocke

^ permalink raw reply	[flat|nested] 33+ messages in thread

* RE: [PATCH] usb: add Freescale QE/CPM USB peripheral controllerdriver
  2008-09-02  7:57     ` Joakim Tjernlund
@ 2008-09-02  8:12       ` Li Yang-R58472
  2008-09-02  8:15         ` Joakim Tjernlund
  2008-09-24 20:26         ` David Brownell
  0 siblings, 2 replies; 33+ messages in thread
From: Li Yang-R58472 @ 2008-09-02  8:12 UTC (permalink / raw)
  To: joakim.tjernlund; +Cc: dbrownell, greg, linux-usb, linux-kernel, linuxppc-dev

> -----Original Message-----
> From: Joakim Tjernlund [mailto:joakim.tjernlund@transmode.se]=20
> Sent: Tuesday, September 02, 2008 3:57 PM
> To: Li Yang-R58472
> Cc: avorontsov@ru.mvista.com; greg@kroah.com;=20
> linux-usb@vger.kernel.org; linux-kernel@vger.kernel.org;=20
> dbrownell@users.sourceforge.net; linuxppc-dev@ozlabs.org
> Subject: Re: [PATCH] usb: add Freescale QE/CPM USB peripheral=20
> controllerdriver
>=20
> On Tue, 2008-09-02 at 15:35 +0800, Li Yang wrote:
> > On Tue, Sep 2, 2008 at 1:11 AM, Anton Vorontsov=20
> > <avorontsov@ru.mvista.com> wrote:
> > > On Thu, Aug 28, 2008 at 05:43:33PM +0800, Li Yang wrote:
> > >> Some of Freescale SoC chips have a QE or CPM co-processor which=20
> > >> supports full speed USB.  The driver adds device mode support of=20
> > >> both QE and CPM USB controller to Linux USB gadget.  The=20
> driver is=20
> > >> tested with MPC8360 and MPC8272, and should work with=20
> other models=20
> > >> having QE/CPM given minor tweaks.
> > >>
> > >> Signed-off-by: Xie Xiaobo <X.Xie@freescale.com>
> > >> Signed-off-by: Li Yang <leoli@freescale.com>
> > >> ---
> > >> This is the second submission of the driver.  This=20
> version addressed:
> > >> Comments from Anton Vorontsov.
> > >> A lot of cosmetic problem.
> > >> Sparse and various kernel DEBUG warnings.
> > >
> > > Just caught this:
> > >
> > > g_ether gadget: high speed config #1: CDC Ethernet (ECM)
>=20
> Does RNDIS work too? If not, is it possible to add or doesn't=20
> the HW support it?

RNDIS is a gadget(protocol) level thing.  I believe it can work with
this driver although not tested myself.

Noted:  AFAIK, RNDIS gadget in Linux doesn't interoperate with windows
well enough to be production level.  Use at your own risk.

- Leo

^ permalink raw reply	[flat|nested] 33+ messages in thread

* RE: [PATCH] usb: add Freescale QE/CPM USB peripheral controllerdriver
  2008-09-02  8:12       ` [PATCH] usb: add Freescale QE/CPM USB peripheral controllerdriver Li Yang-R58472
@ 2008-09-02  8:15         ` Joakim Tjernlund
  2008-09-24 20:28           ` David Brownell
  2008-09-24 20:26         ` David Brownell
  1 sibling, 1 reply; 33+ messages in thread
From: Joakim Tjernlund @ 2008-09-02  8:15 UTC (permalink / raw)
  To: Li Yang-R58472; +Cc: dbrownell, greg, linux-usb, linux-kernel, linuxppc-dev

On Tue, 2008-09-02 at 16:12 +0800, Li Yang-R58472 wrote:
> > -----Original Message-----
> > From: Joakim Tjernlund [mailto:joakim.tjernlund@transmode.se] 
> > Sent: Tuesday, September 02, 2008 3:57 PM
> > To: Li Yang-R58472
> > Cc: avorontsov@ru.mvista.com; greg@kroah.com; 
> > linux-usb@vger.kernel.org; linux-kernel@vger.kernel.org; 
> > dbrownell@users.sourceforge.net; linuxppc-dev@ozlabs.org
> > Subject: Re: [PATCH] usb: add Freescale QE/CPM USB peripheral 
> > controllerdriver
> > 
> > On Tue, 2008-09-02 at 15:35 +0800, Li Yang wrote:
> > > On Tue, Sep 2, 2008 at 1:11 AM, Anton Vorontsov 
> > > <avorontsov@ru.mvista.com> wrote:
> > > > On Thu, Aug 28, 2008 at 05:43:33PM +0800, Li Yang wrote:
> > > >> Some of Freescale SoC chips have a QE or CPM co-processor which 
> > > >> supports full speed USB.  The driver adds device mode support of 
> > > >> both QE and CPM USB controller to Linux USB gadget.  The 
> > driver is 
> > > >> tested with MPC8360 and MPC8272, and should work with 
> > other models 
> > > >> having QE/CPM given minor tweaks.
> > > >>
> > > >> Signed-off-by: Xie Xiaobo <X.Xie@freescale.com>
> > > >> Signed-off-by: Li Yang <leoli@freescale.com>
> > > >> ---
> > > >> This is the second submission of the driver.  This 
> > version addressed:
> > > >> Comments from Anton Vorontsov.
> > > >> A lot of cosmetic problem.
> > > >> Sparse and various kernel DEBUG warnings.
> > > >
> > > > Just caught this:
> > > >
> > > > g_ether gadget: high speed config #1: CDC Ethernet (ECM)
> > 
> > Does RNDIS work too? If not, is it possible to add or doesn't 
> > the HW support it?
> 
> RNDIS is a gadget(protocol) level thing.  I believe it can work with
> this driver although not tested myself.
> 
> Noted:  AFAIK, RNDIS gadget in Linux doesn't interoperate with windows
> well enough to be production level.  Use at your own risk.

I see. If one wants to connect with CDC to Windows, what drivers are
there for Windows that works well with Linux?

 Jocke 

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
  2008-08-28 15:04 ` Arnd Bergmann
  2008-08-28 17:22   ` Alan Stern
  2008-08-29  8:57   ` Li Yang
@ 2008-09-24 20:10   ` David Brownell
  2 siblings, 0 replies; 33+ messages in thread
From: David Brownell @ 2008-09-24 20:10 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: linuxppc-dev, Li Yang, linux-usb, linux-kernel, greg

On Thursday 28 August 2008, Arnd Bergmann wrote:
> > +/*-------------------------------------------------------------------------
> > +     Gadget driver register and unregister.
> > + --------------------------------------------------------------------------*/
> > +int usb_gadget_register_driver(struct usb_gadget_driver *driver)
> > +EXPORT_SYMBOL(usb_gadget_register_driver);
> > +
> > +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
> > +EXPORT_SYMBOL(usb_gadget_unregister_driver);
> 
> Not addressing this driver in particular, but the USB gadget layer in
> general: This is a horrible interface, since every gadget driver exports
> the same symbols, 

Bad terminology.  Gadget drivers are what sit on TOP of peripheral
controller drivers ... only peripheral controller drivers touch the
actual hardware registers.  They export an abstract "gadget" interface.

Gadget drivers are what talk *to* that abstract interface.


> you can never build a kernel that includes more than 
> one gadget driver. Even if the drivers are all built as modules, simply
> loading one of them prevents loading another one.

That's never been a particular requirement.  Systems won't get
USB branding if they have more than one USB peripheral (upstream)
port.  Supporting more than one type of controller hardware is
at best a pretty esoteric configuration.

If you really want to see such stuff ... -ENOPATCH.  :)

- Dave

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
  2008-08-28 22:27         ` Arnd Bergmann
  2008-08-29 16:05           ` Alan Stern
@ 2008-09-24 20:15           ` David Brownell
  1 sibling, 0 replies; 33+ messages in thread
From: David Brownell @ 2008-09-24 20:15 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: greg, linux-usb, linux-kernel, linuxppc-dev, Alan Stern,
	Scott Wood, Li Yang

On Thursday 28 August 2008, Arnd Bergmann wrote:
> If the gadget hardware drivers were registering the device with a
> gadget_bus_type, you could still enforce the "only one protocol"
> rule by binding every protocol to every device in that bus type.

And you'd have to rewrite all the gadget drivers ("protocol")
to work with multiple upstream ports.

That gets messy with e.g. the Ethernet links ... each would
need to be configured with unique ethernet address pairs.
Likewise with serial numbers.

I've learned to just accept complaints in this area as sort
of a price for existing.  It's all complaints, no patches.
So obviously the complaints don't have any requirements
backing them.  ;)

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controllerdriver
  2008-09-02  8:12       ` [PATCH] usb: add Freescale QE/CPM USB peripheral controllerdriver Li Yang-R58472
  2008-09-02  8:15         ` Joakim Tjernlund
@ 2008-09-24 20:26         ` David Brownell
  1 sibling, 0 replies; 33+ messages in thread
From: David Brownell @ 2008-09-24 20:26 UTC (permalink / raw)
  To: Li Yang-R58472; +Cc: greg, linux-usb, linux-kernel, linuxppc-dev

On Tuesday 02 September 2008, Li Yang-R58472 wrote:
> 
> > Does RNDIS work too? If not, is it possible to add or doesn't 
> > the HW support it?
> 
> RNDIS is a gadget(protocol) level thing.  I believe it can work with
> this driver although not tested myself.

It should, so long as the QE hardware doesn't try to manage
too many USB protocol details.  Examples would be caring
at all about "config change events" -- SET_CONFIGURATION
picking between one of N configurations, and SET_INTERFACE
picking between altsettings of a given interface.


> Noted:  AFAIK, RNDIS gadget in Linux doesn't interoperate with windows
> well enough to be production level.  Use at your own risk.

Something in the past few releases broke so it may not be
operating much at all, for that matter...

However, it's a fair point that the interoperation requirements
for RNDIS are so badly defined that non-Microsoft code has a
very hard time being robust.

Somewhere in the linux-usb archives is someone's fairly careful
summary of a dozen extremely rude ways the MSFT RNDIS stack
misbehaves.  It'll do things like deciding to stop forwarding
packets to certain applications for a while ... then continuing
after a few minutes as if nothing was wrong.  Blue screens are
a possibility.  Best to wait a few minutes between plugging
and unplugging, while the races between the USB and NET stacks
sort themselves out.

- Dave

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controllerdriver
  2008-09-02  8:15         ` Joakim Tjernlund
@ 2008-09-24 20:28           ` David Brownell
  2008-09-24 21:30             ` Joakim Tjernlund
  0 siblings, 1 reply; 33+ messages in thread
From: David Brownell @ 2008-09-24 20:28 UTC (permalink / raw)
  To: joakim.tjernlund; +Cc: greg, linux-usb, linux-kernel, linuxppc-dev, dbrownell

On Tuesday 02 September 2008, Joakim Tjernlund wrote:
> 
> > Noted:  AFAIK, RNDIS gadget in Linux doesn't interoperate with windows
> > well enough to be production level.  Use at your own risk.
> 
> I see. If one wants to connect with CDC to Windows, what drivers are
> there for Windows that works well with Linux?

I believe MCCI has some.  It also has drivers for a CDC subset,
pretty much the same one Linux has used forever except wrapped
with a few extra descriptors to make it practical to identify
that "SAFE" (!) subset without needing vendor and product IDs.

- Dave

^ permalink raw reply	[flat|nested] 33+ messages in thread

* RE: [PATCH] usb: add Freescale QE/CPM USB peripheral controllerdriver
  2008-09-24 20:28           ` David Brownell
@ 2008-09-24 21:30             ` Joakim Tjernlund
  2008-09-24 21:42               ` David Brownell
  0 siblings, 1 reply; 33+ messages in thread
From: Joakim Tjernlund @ 2008-09-24 21:30 UTC (permalink / raw)
  To: 'David Brownell'
  Cc: greg, linux-usb, linux-kernel, linuxppc-dev, dbrownell

> -----Original Message-----
> From: David Brownell [mailto:david-b@pacbell.net]
> Sent: den 24 september 2008 22:29
> To: joakim.tjernlund@transmode.se
> Cc: Li Yang-R58472; avorontsov@ru.mvista.com; greg@kroah.com; =
linux-usb@vger.kernel.org; linux-
> kernel@vger.kernel.org; dbrownell@users.sourceforge.net; =
linuxppc-dev@ozlabs.org
> Subject: Re: [PATCH] usb: add Freescale QE/CPM USB peripheral =
controllerdriver
>=20
> On Tuesday 02 September 2008, Joakim Tjernlund wrote:
> >
> > > Noted: =A0AFAIK, RNDIS gadget in Linux doesn't interoperate with =
windows
> > > well enough to be production level. =A0Use at your own risk.
> >
> > I see. If one wants to connect with CDC to Windows, what drivers are
> > there for Windows that works well with Linux?
>=20
> I believe MCCI has some.  It also has drivers for a CDC subset,
> pretty much the same one Linux has used forever except wrapped
> with a few extra descriptors to make it practical to identify
> that "SAFE" (!) subset without needing vendor and product IDs.

Thanks, had a look and it seems like I have to buy these drives. I was
hoping to find some free ones, do you know of such free drivers?

 Jocke=20

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controllerdriver
  2008-09-24 21:30             ` Joakim Tjernlund
@ 2008-09-24 21:42               ` David Brownell
  0 siblings, 0 replies; 33+ messages in thread
From: David Brownell @ 2008-09-24 21:42 UTC (permalink / raw)
  To: Joakim Tjernlund; +Cc: greg, linux-usb, linux-kernel, linuxppc-dev

On Wednesday 24 September 2008, Joakim Tjernlund wrote:
> > > I see. If one wants to connect with CDC to Windows, what drivers are
> > > there for Windows that works well with Linux?
> > 
> > I believe MCCI has some.  It also has drivers for a CDC subset,
> > pretty much the same one Linux has used forever except wrapped
> > with a few extra descriptors to make it practical to identify
> > that "SAFE" (!) subset without needing vendor and product IDs.
> 
> Thanks, had a look and it seems like I have to buy these drives. I was
> hoping to find some free ones, do you know of such free drivers?

No, sorry.  Remember MSFT is somewhat rabidly anti-Free...

However, we'd be quite happy to have someone more attuned
to MS-Windows sort out the remaining issues in the Linux
support for RNDIS.  ;)

- Dave

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
  2008-08-06  7:16 [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver Li Yang
  2008-08-06 15:15 ` Anton Vorontsov
  2008-08-06 15:47 ` Timur Tabi
@ 2008-08-15 14:16 ` Anton Vorontsov
  2 siblings, 0 replies; 33+ messages in thread
From: Anton Vorontsov @ 2008-08-15 14:16 UTC (permalink / raw)
  To: Li Yang; +Cc: david-b, linuxppc-dev, gregkh, linux-usb, Xie Xiaobo

On Wed, Aug 06, 2008 at 03:16:40PM +0800, Li Yang wrote:
> Some of Freescale SoC chips have a QE or CPM co-processor which
> supports full speed USB.  The driver adds device mode support
> of both QE and CPM USB controller to Linux USB gadget.  The
> driver is tested with MPC8360 and MPC8272, and should work with
> other models having QE/CPM given minor tweaks.
> 
> Signed-off-by: Xie Xiaobo <X.Xie@freescale.com>
> Signed-off-by: Li Yang <leoli@freescale.com>

Hi,

Just want to report that the driver works here on MPC8360E-MDS,
with few issues though.

> +/*-------------------------------------------------------------------------
> +	Gadget driver register and unregister.
> + --------------------------------------------------------------------------*/
> +int usb_gadget_register_driver(struct usb_gadget_driver *driver)
> +{
> +	int retval;
> +	unsigned long flags = 0;
> +
> +	/* standard operations */
> +	if (!udc_controller)
> +		return -ENODEV;
> +
> +	if (!driver || (driver->speed != USB_SPEED_FULL
> +			&& driver->speed != USB_SPEED_HIGH)
> +			|| !driver->bind || !driver->unbind ||
> +			!driver->disconnect || !driver->setup)

Usually unbind is assigned via __exit_p() macro, thus the driver
will not able to work with g_ether when it is built in.


Plus I got this with various debugging enabled:

g_ether gadget: high speed config #1: CDC Ethernet (ECM)
BUG: spinlock recursion on CPU#0, swapper/0
 lock: cf846c50, .magic: dead4ead, .owner: swapper/0, .owner_cpu: 0
Call Trace:
[c0361cb0] [c0008a90] show_stack+0x4c/0x16c (unreliable)
[c0361cf0] [c0175b84] spin_bug+0x7c/0xc4
[c0361d10] [c0175ea8] _raw_spin_lock+0xb4/0xb8
[c0361d20] [c027e5dc] _spin_lock_irqsave+0x30/0x48
[c0361d40] [c01c8450] qe_ep_init+0x80/0x3a4
[c0361d60] [c01c8828] qe_ep_enable+0xb4/0xe4
[c0361d80] [c01cb210] ecm_set_alt+0x88/0x12c
[c0361d90] [c01cbb10] set_config+0xc8/0x1bc
[c0361db0] [c01cbf90] composite_setup+0x20c/0x3a8
[c0361de0] [c01c7f88] setup_received_handle+0x1e4/0x26c
[c0361e00] [c01c807c] ep0_setup_handle+0x6c/0x74
[c0361e10] [c01c81f0] qe_ep0_rx+0x16c/0x17c
[c0361e30] [c01c9140] rx_irq+0x88/0xc0
[c0361e40] [c01c98c8] qe_udc_irq+0x10c/0x134
[c0361e60] [c006083c] handle_IRQ_event+0x5c/0xb0
[c0361e80] [c0062964] handle_level_irq+0xa8/0x144
[c0361ea0] [c0029a9c] qe_ic_cascade_low_ipic+0x58/0x90
[c0361eb0] [c0006820] do_IRQ+0xa4/0xc8
[c0361ec0] [c0013e88] ret_from_except+0x0/0x14
--- Exception: 501 at cpu_idle+0xa0/0x104
    LR = cpu_idle+0xa0/0x104
[c0361f80] [c0009d88] cpu_idle+0x50/0x104 (unreliable)
[c0361fa0] [c027f07c] 0xc027f07c
[c0361fc0] [c030d91c] start_kernel+0x1ac/0x230
[c0361ff0] [00003438] 0x3438
BUG: spinlock lockup on CPU#0, swapper/0, cf846c50
Call Trace:
[c0361ca0] [c0008a90] show_stack+0x4c/0x16c (unreliable)
[c0361ce0] [c0175df0] __spin_lock_debug+0xf4/0xf8
[c0361d10] [c0175e80] _raw_spin_lock+0x8c/0xb8
[c0361d20] [c027e5dc] _spin_lock_irqsave+0x30/0x48
[c0361d40] [c01c8450] qe_ep_init+0x80/0x3a4
[c0361d60] [c01c8828] qe_ep_enable+0xb4/0xe4
[c0361d80] [c01cb210] ecm_set_alt+0x88/0x12c
[c0361d90] [c01cbb10] set_config+0xc8/0x1bc
[c0361db0] [c01cbf90] composite_setup+0x20c/0x3a8
[c0361de0] [c01c7f88] setup_received_handle+0x1e4/0x26c
[c0361e00] [c01c807c] ep0_setup_handle+0x6c/0x74
[c0361e10] [c01c81f0] qe_ep0_rx+0x16c/0x17c
[c0361e30] [c01c9140] rx_irq+0x88/0xc0
[c0361e40] [c01c98c8] qe_udc_irq+0x10c/0x134
[c0361e60] [c006083c] handle_IRQ_event+0x5c/0xb0
[c0361e80] [c0062964] handle_level_irq+0xa8/0x144
[c0361ea0] [c0029a9c] qe_ic_cascade_low_ipic+0x58/0x90
[c0361eb0] [c0006820] do_IRQ+0xa4/0xc8
[c0361ec0] [c0013e88] ret_from_except+0x0/0x14
--- Exception: 501 at cpu_idle+0xa0/0x104
    LR = cpu_idle+0xa0/0x104
[c0361f80] [c0009d88] cpu_idle+0x50/0x104 (unreliable)
[c0361fa0] [c027f07c] 0xc027f07c
[c0361fc0] [c030d91c] start_kernel+0x1ac/0x230
[c0361ff0] [00003438] 0x3438


Thanks,

-- 
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
  2008-08-06  7:16 [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver Li Yang
  2008-08-06 15:15 ` Anton Vorontsov
@ 2008-08-06 15:47 ` Timur Tabi
  2008-08-15 14:16 ` Anton Vorontsov
  2 siblings, 0 replies; 33+ messages in thread
From: Timur Tabi @ 2008-08-06 15:47 UTC (permalink / raw)
  To: Li Yang; +Cc: david-b, linuxppc-dev, gregkh, linux-usb, Xie Xiaobo

On Wed, Aug 6, 2008 at 2:16 AM, Li Yang <leoli@freescale.com> wrote:

> +/*---------------------------------------------------------------------
> + *             Mask definitions for usb BD                           *
> + *--------------------------------------------------------------------*/
> +#define QE_SIZEOF_BD       sizeof(struct qe_bd)
> +
> +#define BD_BUFFER_ARG(bd)                   (((struct qe_bd *)bd)->buf)
> +#define BD_BUFFER_CLEAR(bd)                 out_be32(&(BD_BUFFER_ARG(bd)), 0);
> +#define BD_BUFFER(bd)                       in_be32(&(BD_BUFFER_ARG(bd)))
> +#define BD_STATUS_AND_LENGTH_SET(bd, val)   out_be32((u32 *)bd, val)
> +#define BD_STATUS_AND_LENGTH(bd)            in_be32((u32 *)bd)
> +#define BD_BUFFER_SET(bd, buffer)           out_be32(&(BD_BUFFER_ARG(bd)), \
> +                                                       (u32)(buffer))

Delete all of these.  Don't use these silly macros at all.  Reference
the structure fields directly, and use the in_ and out_ functions
directly.

Using macros like these encourages unnecessary typecasts.  "struct
qe_bd" has been defined, so you should use it.

-- 
Timur Tabi
Linux kernel developer at Freescale

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
  2008-08-06  7:16 [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver Li Yang
@ 2008-08-06 15:15 ` Anton Vorontsov
  2008-08-06 15:47 ` Timur Tabi
  2008-08-15 14:16 ` Anton Vorontsov
  2 siblings, 0 replies; 33+ messages in thread
From: Anton Vorontsov @ 2008-08-06 15:15 UTC (permalink / raw)
  To: Li Yang; +Cc: david-b, linuxppc-dev, gregkh, linux-usb, Xie Xiaobo

On Wed, Aug 06, 2008 at 03:16:40PM +0800, Li Yang wrote:
> Some of Freescale SoC chips have a QE or CPM co-processor which
> supports full speed USB.  The driver adds device mode support
> of both QE and CPM USB controller to Linux USB gadget.  The
> driver is tested with MPC8360 and MPC8272, and should work with
> other models having QE/CPM given minor tweaks.
> 
> Signed-off-by: Xie Xiaobo <X.Xie@freescale.com>
> Signed-off-by: Li Yang <leoli@freescale.com>

Hi,

Few (mostly cosmetic) comments below. I didn't look into locking and
other stuff, just few general questions: did you try this driver
with CONFIG_SMP (even on UP machine), CONFIG_PREEMPT,
CONFIG_DEBUG_SPINLOCK and CONFIG_DEBUG_SPINLOCK_SLEEP?
These options will trigger most obvious bugs.

Also, I think sparse tool will be very unhappy about this driver,
there are lots of unnecessary and dubious casts...

Also surprisingly, checkpatch reports just few warnings though there
are a lot of cosmetic issues...

> ---
>  drivers/usb/gadget/Kconfig        |   19 +
>  drivers/usb/gadget/Makefile       |    1 +
>  drivers/usb/gadget/fsl_qe_udc.c   | 2729 +++++++++++++++++++++++++++++++++++++
>  drivers/usb/gadget/fsl_qe_udc.h   |  458 +++++++
>  drivers/usb/gadget/gadget_chips.h |    9 +
>  5 files changed, 3216 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/usb/gadget/fsl_qe_udc.c
>  create mode 100644 drivers/usb/gadget/fsl_qe_udc.h
> 
> diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
> index c6a8c6b..fba8305 100644
> --- a/drivers/usb/gadget/Kconfig
> +++ b/drivers/usb/gadget/Kconfig
> @@ -150,6 +150,25 @@ config USB_FSL_USB2
>  	default USB_GADGET
>  	select USB_GADGET_SELECTED
>  
> +config USB_GADGET_FSL_QE
> +    boolean "Freescale QE/CPM USB Device Controller"

no depends on? Without depends, this driver will break build for arches
other than powerpc && (qe || cpm)

> +    help
> +       Some of Freescale PowerPC processors have a Full Speed
> +       QE/CPM2 USB controller, which support device mode with 4
> +       programmable endpoints. This driver supports the
> +       controller in the MPC8360 and MPC8272, and should work with
> +       controllers having QE or CPM2, given minor tweaks.
> +
> +       Say "y" to link the driver statically, or "m" to build a
> +       dynamically linked module called "fsl_qe_udc" and force all
> +       gadget drivers to also be dynamically linked.
> +
> +config USB_FSL_QE
> +    tristate
> +    depends on USB_GADGET_FSL_QE
> +    default USB_GADGET
> +    select USB_GADGET_SELECTED
> +
>  config USB_GADGET_NET2280
>  	boolean "NetChip 228x"
>  	depends on PCI
> diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
> index fcb5cb9..4871554 100644
> --- a/drivers/usb/gadget/Makefile
> +++ b/drivers/usb/gadget/Makefile
> @@ -18,6 +18,7 @@ obj-$(CONFIG_USB_AT91)		+= at91_udc.o
>  obj-$(CONFIG_USB_ATMEL_USBA)	+= atmel_usba_udc.o
>  obj-$(CONFIG_USB_FSL_USB2)	+= fsl_usb2_udc.o
>  obj-$(CONFIG_USB_M66592)	+= m66592-udc.o
> +obj-$(CONFIG_USB_FSL_QE)	+= fsl_qe_udc.o
>  
>  #
>  # USB gadget drivers
> diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c
> new file mode 100644
> index 0000000..52790f7
> --- /dev/null
> +++ b/drivers/usb/gadget/fsl_qe_udc.c
> @@ -0,0 +1,2729 @@
> +/*
> + * driver/usb/gadget/fsl_qe_udc.c
> + *
> + * Copyright (c) 2006-2008 Freescale Semiconductor, Inc. All rights reserved.
> + *
> + * Author: Xie Xiaobo <X.Xie@freescale.com>
> + * Li Yang <leoli@freescale.com>
> + * Based on bareboard code from Shlomi Gridish.
> + *
> + * Description:
> + * Freescle QE/CPM USB Pheripheral Controller Driver
> + * The controller can be found on MPC8360, MPC8272, and etc.
> + * MPC8360 Rev 1.1 may need QE mircocode update
> + *
> + * 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.
> + */
> +
> +#undef USB_TRACE

Nobody seem to use this.

> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/ioport.h>
> +#include <linux/types.h>
> +#include <linux/errno.h>
> +#include <linux/delay.h>

Don't see any calls to sleep or delay.

> +#include <linux/slab.h>
> +#include <linux/timer.h>
> +#include <linux/list.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>

Do you really need irq.h? This is somewhat internal header.

> +#include <linux/io.h>
> +#include <linux/proc_fs.h>

Seems spurious also.

> +#include <linux/mm.h>
> +#include <linux/uaccess.h>

Not sure about this one.

> +#include <linux/moduleparam.h>
> +
> +#include <linux/of_platform.h>
> +
> +#include <asm/qe.h>
> +#include <asm/dma.h>
> +#include <asm/reg.h>
> +
> +#include <linux/usb/ch9.h>
> +#include <linux/usb/gadget.h>
> +#include <linux/usb/otg.h>
> +#include <linux/dma-mapping.h>
> +#include "fsl_qe_udc.h"
> +
> +#ifdef CONFIG_CPM2
> +extern int cpm_command(u32 command, u8 opcode);
> +extern unsigned long cpm_muram_alloc(unsigned long size, unsigned long align);
> +extern int cpm_muram_free(unsigned long offset);

These are externed in the include/asm-powerpc/cpm.h.

> +#define qe_muram_alloc cpm_muram_alloc
> +#define qe_muram_free cpm_muram_free

qe_muarm_* calls are cpm_muram_* calls, see
include/asm-powerpc/qe.h in the recent kernels.
So, you can just switch to cpm_*.

> +#endif
> +
> +#define DRIVER_DESC     "Freescale QE/CPM USB Device Controller driver"
> +#define DRIVER_AUTHOR   "Xie XiaoBo"
> +#define DRIVER_VERSION  "1.0"
> +
> +#define DMA_ADDR_INVALID        (~(dma_addr_t)0)
> +
> +static const char driver_name[] = "fsl_qe_udc";
> +static const char driver_desc[] = DRIVER_DESC;
> +
> +/*ep name is important in gadget, it should obey the convention of ep_match()*/
> +static const char *const ep_name[] = {
> +	"ep0-control", /* everyone has ep0 */
> +	/* 3 configurable endpoints */
> +	"ep1",
> +	"ep2",
> +	"ep3",
> +};
> +
> +static struct usb_endpoint_descriptor
> +qe_ep0_desc = {

This seem to fit into one line perfectly well.

> +	.bLength =		USB_DT_ENDPOINT_SIZE,
> +	.bDescriptorType =	USB_DT_ENDPOINT,
> +
> +	.bEndpointAddress =	0,
> +	.bmAttributes =		USB_ENDPOINT_XFER_CONTROL,
> +	.wMaxPacketSize =	USB_MAX_CTRL_PAYLOAD,
> +};
> +
> +/* it is initialized in probe()  */
> +static struct qe_udc *udc_controller;
> +
> +/********************************************************************
> + *      Internal Used Function Start
> +********************************************************************/
> +/*-----------------------------------------------------------------
> + * done() - retire a request; caller blocked irqs
> + *--------------------------------------------------------------*/
> +static void done(struct qe_ep *ep, struct qe_req *req, int status)
> +{
> +	struct qe_udc *udc = NULL;
> +	unsigned char stopped = ep->stopped;
> +	udc = (struct qe_udc *) ep->udc;
> +
> +	/* the req->queue pointer is used by ep_queue() func, in which
> +	* the request will be added into a udc_ep->queue 'd tail

The comments are broken this way all over the place.
Can we stick with

/*
 * Multiline
 * comment.
 */

? This is orthodox style.

> +	* so here the req will be dropped from the ep->queue
> +	*/
> +	list_del_init(&req->queue);
> +
> +	/* req.status should be set as -EINPROGRESS in ep_queue() */
> +	if (req->req.status == -EINPROGRESS)
> +		req->req.status = status;
> +	else
> +		status = req->req.status;
> +
> +	if (req->mapped) {
> +		dma_unmap_single(ep->udc->gadget.dev.parent,
> +			req->req.dma, req->req.length,
> +			ep_is_in(ep)
> +				? DMA_TO_DEVICE
> +				: DMA_FROM_DEVICE);
> +		req->req.dma = DMA_ADDR_INVALID;
> +		req->mapped = 0;
> +	} else

if () {
	multi;
	statement;
} else { <- brace
	single statement;
}

> +		dma_sync_single_for_cpu(ep->udc->gadget.dev.parent,
> +			req->req.dma, req->req.length,
> +			ep_is_in(ep)
> +				? DMA_TO_DEVICE
> +				: DMA_FROM_DEVICE);
> +
> +	if (status && (status != -ESHUTDOWN))
> +		VDBG("complete %s req %p stat %d len %u/%u",
> +			ep->ep.name, &req->req, status,
> +			req->req.actual, req->req.length);

please use dev_vdbg().

> +
> +	/* don't modify queue heads during completion callback */
> +	ep->stopped = 1;
> +	spin_unlock(&ep->udc->lock);
> +
> +	/* this complete() should a func implemented by gadget layer,
> +	* eg fsg->bulk_in_complete() */
> +	if (req->req.complete)
> +		req->req.complete(&ep->ep, &req->req);
> +
> +	spin_lock(&ep->udc->lock);
> +
> +	ep->stopped = stopped;
> +}
> +
> +/*-----------------------------------------------------------------
> + * nuke(): delete all requests related to this ep
> + *--------------------------------------------------------------*/
> +static void nuke(struct qe_ep *ep, int status)
> +{
> +	/* Whether this eq has request linked */
> +	while (!list_empty(&ep->queue)) {
> +		struct qe_req *req = NULL;

No need to = NULL.

> +		req = list_entry(ep->queue.next, struct qe_req, queue);
> +
> +		done(ep, req, status);
> +	}
> +}
> +
> +/*---------------------------------------------------------------------------*
> + * USB and Endpoint manipulate process, include parameter and register       *
> + *---------------------------------------------------------------------------*/
> +/* @value: 1--set stall 0--clean stall */
> +static int qe_eprx_stall_change(struct qe_ep *ep, int value)
> +{
> +	u16 tem_usep;
> +	u8 epnum = ep->epnum;
> +	struct qe_udc *udc = ep->udc;
> +
> +	tem_usep = in_be16(&udc->usb_regs->usb_usep[epnum]);
> +	tem_usep = tem_usep & ~USB_RHS_MASK;
> +	if (value == 1)
> +		tem_usep |= USB_RHS_STALL;
> +	else if (ep->dir == USB_DIR_IN)
> +		tem_usep |= USB_RHS_IGNORE_OUT;
> +
> +	out_be16(&udc->usb_regs->usb_usep[epnum], tem_usep);
> +	return 0;
> +}
> +
> +static int qe_eptx_stall_change(struct qe_ep *ep, int value)
> +{
> +	u16 tem_usep;
> +	u8 epnum = ep->epnum;
> +	struct qe_udc *udc = ep->udc;
> +
> +	tem_usep = in_be16(&udc->usb_regs->usb_usep[epnum]);
> +	tem_usep = tem_usep & ~USB_THS_MASK;
> +	if (value == 1)
> +		tem_usep |= USB_THS_STALL;
> +	else if (ep->dir == USB_DIR_OUT)
> +		tem_usep |= USB_THS_IGNORE_IN;
> +
> +	out_be16(&udc->usb_regs->usb_usep[epnum], tem_usep);
> +
> +	return 0;
> +}
> +
> +static int qe_ep0_stall(struct qe_udc *udc)
> +{
> +	qe_eptx_stall_change(&udc->eps[0], 1);
> +	qe_eprx_stall_change(&udc->eps[0], 1);
> +	udc_controller->ep0_state = WAIT_FOR_SETUP;
> +	udc_controller->ep0_dir = 0;
> +	return 0;
> +}
> +
> +static int qe_eprx_nack(struct qe_ep *ep)
> +{
> +	u8 epnum = ep->epnum;
> +	struct qe_udc *udc = ep->udc;
> +
> +	if (ep->state == EP_STATE_IDLE) {
> +		/* Set the ep's nack */
> +		clrsetbits_be16(&udc->usb_regs->usb_usep[epnum],
> +				USB_RHS_MASK, USB_RHS_NACK);
> +
> +		/* Mask Rx and Busy interrupts */
> +		clrbits16(&udc->usb_regs->usb_usbmr,
> +				(USB_E_RXB_MASK | USB_E_BSY_MASK));
> +
> +		ep->state = EP_STATE_NACK;
> +	}
> +	return 0;
> +}
> +
> +static int qe_eprx_normal(struct qe_ep *ep)
> +{
> +	struct qe_udc *udc = ep->udc;
> +
> +	if (ep->state == EP_STATE_NACK) {
> +		clrsetbits_be16(&udc->usb_regs->usb_usep[ep->epnum],
> +				USB_RTHS_MASK, USB_THS_IGNORE_IN);
> +
> +		/* Unmask RX interrupts */
> +		out_be16(&udc->usb_regs->usb_usber,
> +				USB_E_BSY_MASK|USB_E_RXB_MASK);

Not sure why checkpatch does not complain about missing spaces
around "|"...

> +		setbits16(&udc->usb_regs->usb_usbmr,
> +				(USB_E_RXB_MASK | USB_E_BSY_MASK));
> +
> +		ep->state = EP_STATE_IDLE;
> +		ep->has_data = 0;
> +	}
> +
> +	return 0;
> +}
> +
> +static int qe_ep_cmd_stoptx(struct qe_ep *ep)
> +{
> +	u8 ep_num;
> +#ifdef CONFIG_CPM2
> +	u32 command;
> +	u8 opcode;
> +
> +	ep_num = ep->epnum << CPM_USB_EP_SHIFT;
> +	command = CPM_USB_STOP_TX | (u32)ep_num;
> +	opcode = CPM_USB_STOP_TX_OPCODE;
> +	cpm_command(command, opcode);
> +#else
> +	ep_num = ep->epnum;
> +	qe_issue_cmd(QE_USB_STOP_TX, QE_CR_SUBBLOCK_USB, ep_num, 0);
> +#endif
> +	return 0;
> +}
> +
> +static int qe_ep_cmd_restarttx(struct qe_ep *ep)
> +{
> +	u8 ep_num;
> +#ifdef CONFIG_CPM2
> +	u32 command;
> +	u8 opcode;
> +
> +	ep_num = ep->epnum << CPM_USB_EP_SHIFT;
> +	command = CPM_USB_RESTART_TX | (u32)ep_num;
> +	opcode = CPM_USB_RESTART_TX_OPCODE;
> +	cpm_command(command, opcode);
> +#else
> +	ep_num = ep->epnum;
> +	qe_issue_cmd(QE_USB_RESTART_TX, QE_CR_SUBBLOCK_USB, ep_num, 0);
> +#endif
> +	return 0;
> +}
> +
> +static int qe_ep_flushtxfifo(struct qe_ep *ep)
> +{
> +	struct qe_ep *tmp_ep;
> +	struct qe_udc *udc = ep->udc;
> +	int i;
> +
> +	tmp_ep = ep;
> +	i = (int)tmp_ep->epnum;
> +
> +	qe_ep_cmd_stoptx(tmp_ep);
> +	out_8(&udc->usb_regs->usb_uscom,
> +		USB_CMD_FLUSH_FIFO | (USB_CMD_EP_MASK & (tmp_ep->epnum)));
> +	udc->ep_param[i]->tbptr = udc->ep_param[i]->tbase;
> +	udc->ep_param[i]->tstate = 0;
> +	udc->ep_param[i]->tbcnt = 0;
> +
> +	tmp_ep->c_txbd = tmp_ep->txbase;
> +	tmp_ep->n_txbd = tmp_ep->txbase;
> +	qe_ep_cmd_restarttx(tmp_ep);
> +	return 0;
> +}
> +
> +static int qe_ep_filltxfifo(struct qe_ep *ep)
> +{
> +	struct qe_udc *udc = ep->udc;
> +
> +	out_8(&udc->usb_regs->usb_uscom,
> +			USB_CMD_STR_FIFO | (USB_CMD_EP_MASK & (ep->epnum)));
> +	return 0;
> +}
> +
> +static int qe_epbds_reset(struct qe_udc *udc, int pipe_num)
> +{
> +	struct qe_ep *ep;
> +	u32 bdring_len;
> +	u8 *bd;
> +	int i;
> +
> +	ep = &(udc->eps[pipe_num]);

No need for parentheses.

> +
> +	if (ep->dir == USB_DIR_OUT)
> +		bdring_len = USB_BDRING_LEN_RX;
> +	else
> +		bdring_len = USB_BDRING_LEN;
> +
> +	bd = (u8 *)ep->rxbase;
> +	for (i = 0; i < bdring_len; i++) {
> +		BD_STATUS_AND_LENGTH_SET(bd, (R_E|R_I));

I would put spaces around "|".

> +		bd += QE_SIZEOF_BD;
> +	}
> +	bd -= QE_SIZEOF_BD;
> +	BD_STATUS_AND_LENGTH_SET(bd, (R_E|R_I|R_W));

ditto.

> +
> +	bd = (u8 *)(ep->txbase);
> +	for (i = 0; i < USB_BDRING_LEN_TX; i++) {
> +		BD_BUFFER_CLEAR(bd);
> +		BD_STATUS_AND_LENGTH_SET(bd, 0);
> +		bd += QE_SIZEOF_BD;
> +	}
> +	bd -= QE_SIZEOF_BD;
> +	BD_STATUS_AND_LENGTH_SET(bd, T_W);
> +
> +	return 0;
> +}
> +
> +static int qe_ep_reset(struct qe_udc *udc, int pipe_num)
> +{
> +	struct qe_ep *ep;
> +	u16 tmpusep;
> +
> +	ep = &(udc->eps[pipe_num]);

Parentheses.

> +	tmpusep = in_be16(&udc->usb_regs->usb_usep[pipe_num]);
> +	tmpusep &= ~USB_RTHS_MASK;
> +
> +	switch (ep->dir) {
> +	case USB_DIR_BOTH:
> +		qe_ep_flushtxfifo(ep);
> +		break;
> +	case USB_DIR_OUT:
> +		tmpusep |= USB_THS_IGNORE_IN;
> +		break;
> +	case USB_DIR_IN:
> +		qe_ep_flushtxfifo(ep);
> +		tmpusep |= USB_RHS_IGNORE_OUT;
> +		break;
> +	default:
> +		break;
> +	}
> +	out_be16(&udc->usb_regs->usb_usep[pipe_num], tmpusep);
> +
> +	qe_epbds_reset(udc, pipe_num);
> +
> +	return 0;
> +}
> +
> +static int qe_ep_toggledata01(struct qe_ep *ep)
> +{
> +	struct qe_ep *tmpep = ep;
> +
> +	tmpep->data01 ^= 0x1;
> +	return 0;
> +}
> +
> +static int qe_ep_bd_init(struct qe_udc *udc, unsigned char pipe_num)
> +{
> +	struct qe_ep *ep = &(udc->eps[pipe_num]);

Ditto.

> +	unsigned long tmp_addr = 0;
> +	struct usb_ep_para __iomem *epparam;
> +	int i;
> +	u8 *bd;
> +	int bdring_len;
> +
> +	if (ep->dir == USB_DIR_OUT)
> +		bdring_len = USB_BDRING_LEN_RX;
> +	else
> +		bdring_len = USB_BDRING_LEN;
> +
> +	epparam = udc->ep_param[pipe_num];
> +	/* alloc multi-ram for BD rings and set the ep parameters */
> +	tmp_addr = qe_muram_alloc(QE_SIZEOF_BD * (bdring_len +
> +				USB_BDRING_LEN_TX), QE_ALIGNMENT_OF_BD);
> +	out_be16(&epparam->rbase, (u16)tmp_addr);
> +	out_be16(&epparam->tbase, (u16)(tmp_addr +
> +				(QE_SIZEOF_BD * bdring_len)));
> +
> +	out_be16(&epparam->rbptr, in_be16(&epparam->rbase));
> +	out_be16(&epparam->tbptr, in_be16(&epparam->tbase));
> +
> +	ep->rxbase = (struct qe_bd *)muram_addr(tmp_addr);
> +	ep->txbase = (struct qe_bd *)muram_addr(tmp_addr +
> +						(QE_SIZEOF_BD * bdring_len));
> +	ep->n_rxbd = ep->rxbase;
> +	ep->e_rxbd = ep->rxbase;
> +	ep->n_txbd = ep->txbase;
> +	ep->c_txbd = ep->txbase;
> +	ep->data01 = 0; /* data0 */
> +
> +	/* Init TX and RX bds */
> +	bd = (u8 *)(ep->rxbase);
> +	for (i = 0; i < bdring_len; i++) {
> +		BD_BUFFER_CLEAR(bd);
> +		BD_STATUS_AND_LENGTH_SET(bd, 0);
> +		bd += QE_SIZEOF_BD;
> +	}
> +	bd -= QE_SIZEOF_BD;
> +	BD_STATUS_AND_LENGTH_SET(bd, R_W);
> +
> +	bd = (u8 *)(ep->txbase);
> +	for (i = 0; i < USB_BDRING_LEN_TX; i++) {
> +		BD_BUFFER_CLEAR(bd);
> +		BD_STATUS_AND_LENGTH_SET(bd, 0);
> +		bd += QE_SIZEOF_BD;
> +	}
> +	bd -= QE_SIZEOF_BD;
> +	BD_STATUS_AND_LENGTH_SET(bd, T_W);
> +
> +	return 0;
> +}
> +
> +static int qe_ep_rxbd_update(struct qe_ep *ep)
> +{
> +	unsigned int size;
> +	int i;
> +	unsigned int tmp;
> +	struct qe_bd *bd;
> +	unsigned int bdring_len;
> +
> +	if (ep->rxbase == NULL)
> +		return -EINVAL;
> +
> +	bd = ep->rxbase;
> +
> +	ep->rxframe = kmalloc(sizeof(struct qe_frame), GFP_KERNEL);
> +	if (ep->rxframe == NULL) {
> +		dev_err(ep->udc->dev, "malloc rxframe failed\n");
> +		return -ENOMEM;
> +	}
> +
> +	qe_frame_init(ep->rxframe);
> +
> +	if (ep->dir == USB_DIR_OUT)
> +		bdring_len = USB_BDRING_LEN_RX;
> +	else
> +		bdring_len = USB_BDRING_LEN;
> +
> +	size = (ep->ep.maxpacket + USB_CRC_SIZE + 2) * (bdring_len + 1);
> +	ep->rxbuffer = kzalloc(size, GFP_KERNEL);
> +	if (ep->rxbuffer == NULL) {
> +		dev_err(ep->udc->dev, "malloc rxbuffer failed,size=%d\n",
> +				size);
> +		return -ENOMEM;
> +	}
> +
> +	ep->rxbuf_d = virt_to_phys((void *)ep->rxbuffer);
> +	if (ep->rxbuf_d == DMA_ADDR_INVALID) {
> +		ep->rxbuf_d = dma_map_single(udc_controller->gadget.dev.parent,
> +					ep->rxbuffer,
> +					size,
> +					DMA_FROM_DEVICE);
> +		ep->rxbufmap = 1;
> +	} else {
> +		dma_sync_single_for_device(udc_controller->gadget.dev.parent,
> +					ep->rxbuf_d, size,
> +					DMA_FROM_DEVICE);
> +		ep->rxbufmap = 0;
> +	}
> +
> +	size = ep->ep.maxpacket + USB_CRC_SIZE + 2;
> +	tmp = ep->rxbuf_d;
> +	tmp = (u32)(((tmp>>2)<<2) + 4);
> +
> +	for (i = 0; i < bdring_len; i++) {
> +		BD_BUFFER_SET(bd, tmp);
> +		BD_STATUS_AND_LENGTH_SET(bd, (R_E|R_I));
> +		tmp = tmp + size;
> +		bd++;
> +	}
> +	bd--;
> +	BD_STATUS_AND_LENGTH_SET(bd, (R_E|R_I|R_W));
> +
> +	return 0;
> +}
> +
> +static int qe_ep_register_init(struct qe_udc *udc, unsigned char pipe_num)
> +{
> +	struct qe_ep *ep = &(udc->eps[pipe_num]);
> +	struct usb_ep_para __iomem *epparam;
> +	u16 usep, logepnum;
> +	u16 tmp;
> +	u8 rtfcr = 0;
> +
> +	epparam = udc->ep_param[pipe_num];
> +
> +	usep = 0;
> +	logepnum = (ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
> +	usep |= (logepnum << USB_EPNUM_SHIFT);
> +
> +	switch (ep->desc->bmAttributes & 0x03) {
> +	case USB_ENDPOINT_XFER_BULK:
> +		usep |= USB_TRANS_BULK;
> +		break;
> +	case USB_ENDPOINT_XFER_ISOC:
> +		usep |=  USB_TRANS_ISO;
> +		break;
> +	case USB_ENDPOINT_XFER_INT:
> +		usep |= USB_TRANS_INT;
> +		break;
> +	default:
> +		usep |= USB_TRANS_CTR;
> +		break;
> +	}
> +
> +	switch (ep->dir) {
> +	case USB_DIR_OUT:
> +		usep |= USB_THS_IGNORE_IN;
> +		break;
> +	case USB_DIR_IN:
> +		usep |= USB_RHS_IGNORE_OUT;
> +		break;
> +	default:
> +		break;
> +	}
> +	out_be16(&udc->usb_regs->usb_usep[pipe_num], usep);
> +
> +	rtfcr = 0x30;
> +	out_8(&epparam->rbmr, rtfcr);
> +	out_8(&epparam->tbmr, rtfcr);
> +
> +	tmp = (u16)(ep->ep.maxpacket + USB_CRC_SIZE);
> +	/* MRBLR must be divisble by 4 */
> +	tmp = (u16)(((tmp >> 2) << 2) + 4);
> +	out_be16(&epparam->mrblr, tmp);
> +
> +	return 0;
> +}
> +
> +static int qe_ep_init(struct qe_udc *udc,
> +		      unsigned char pipe_num,
> +		      const struct usb_endpoint_descriptor *desc)
> +{
> +	struct qe_ep *ep = &(udc->eps[pipe_num]);
> +	unsigned long flags = 0;
> +	int reval = 0;
> +	u16 max = 0;
> +
> +	max = le16_to_cpu(desc->wMaxPacketSize);
> +
> +	/* check the max package size validate for this endpoint */
> +	/* Refer to USB2.0 spec table 9-13,
> +	*/
> +	if (pipe_num != 0) {
> +		switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
> +		case USB_ENDPOINT_XFER_BULK:
> +			if (strstr(ep->ep.name, "-iso")
> +				|| strstr(ep->ep.name, "-int"))
> +			goto en_done;
> +			switch (udc->gadget.speed) {
> +			case USB_SPEED_HIGH:
> +			if ((max == 128) || (max == 256) || (max == 512))
> +				break;
> +			default:
> +				switch (max) {
> +				case 4:
> +				case 8:
> +				case 16:
> +				case 32:
> +				case 64:
> +					break;
> +				default:
> +				case USB_SPEED_LOW:
> +					goto en_done;
> +				}
> +			}
> +			break;
> +		case USB_ENDPOINT_XFER_INT:
> +			if (strstr(ep->ep.name, "-iso"))	/* bulk is ok */
> +				goto en_done;
> +			switch (udc->gadget.speed) {
> +			case USB_SPEED_HIGH:
> +				if (max <= 1024)
> +					break;
> +			case USB_SPEED_FULL:
> +				if (max <= 64)
> +					break;
> +			default:
> +				if (max <= 8)
> +					break;
> +				goto en_done;
> +			}
> +			break;
> +		case USB_ENDPOINT_XFER_ISOC:
> +			if (strstr(ep->ep.name, "-bulk")
> +				|| strstr(ep->ep.name, "-int"))
> +				goto en_done;
> +			switch (udc->gadget.speed) {
> +			case USB_SPEED_HIGH:
> +				if (max <= 1024)
> +					break;
> +			case USB_SPEED_FULL:
> +				if (max <= 1023)
> +					break;
> +			default:
> +				goto en_done;
> +			}
> +			break;
> +		case USB_ENDPOINT_XFER_CONTROL:
> +			if (strstr(ep->ep.name, "-iso")
> +				|| strstr(ep->ep.name, "-int"))
> +				goto en_done;
> +			switch (udc->gadget.speed) {
> +			case USB_SPEED_HIGH:
> +			case USB_SPEED_FULL:
> +				switch (max) {
> +				case 1:
> +				case 2:
> +				case 4:
> +				case 8:
> +				case 16:
> +				case 32:
> +				case 64:
> +					break;
> +				default:
> +					goto en_done;
> +				}
> +			case USB_SPEED_LOW:
> +				switch (max) {
> +				case 1:
> +				case 2:
> +				case 4:
> +				case 8:
> +					break;
> +				default:
> +					goto en_done;
> +				}
> +			default:
> +				goto en_done;
> +			}
> +			break;
> +
> +		default:
> +			goto en_done;
> +		}
> +	} /* if ep0*/
> +
> +	/* here initialize variable of ep */
> +	ep->ep.maxpacket = max;
> +	ep->tm = (u8)(desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
> +	ep->desc = desc;
> +	ep->stopped = 0;
> +	ep->init = 1;
> +
> +	if (pipe_num == 0) {
> +		ep->dir = USB_DIR_BOTH;
> +		udc->ep0_dir = USB_DIR_OUT;
> +		udc->ep0_state = WAIT_FOR_SETUP;
> +	} else	{
> +		switch ((desc->bEndpointAddress)&USB_ENDPOINT_DIR_MASK) {

This *looks* like a cast. But actually it is "a & b". It took me
quite some time to parse this. Just remove parentheses to avoid
the confusion.

> +		case USB_DIR_OUT:
> +			ep->dir = USB_DIR_OUT;
> +			break;
> +		case USB_DIR_IN:
> +			ep->dir = USB_DIR_IN;
> +		default:
> +			break;
> +		}
> +	}
> +
> +	spin_lock_irqsave(&udc->lock, flags);
> +
> +	/* hardware special operation */
> +	qe_ep_bd_init((void *)udc, pipe_num);

Unneeded cast.

> +	if ((ep->tm == USBP_TM_CTL) || (ep->dir == USB_DIR_OUT)) {
> +		reval = qe_ep_rxbd_update(ep);
> +		if (reval)
> +			goto en_done;
> +	}
> +
> +	if ((ep->tm == USBP_TM_CTL) || (ep->dir == USB_DIR_IN)) {
> +		ep->txframe = kmalloc(sizeof(struct qe_frame), GFP_KERNEL);
> +		if (ep->txframe == NULL) {
> +			dev_err(udc->dev, "malloc txframe failed\n");
> +			goto en_done;
> +		}
> +		qe_frame_init(ep->txframe);
> +	}
> +	qe_ep_register_init((void *)udc, pipe_num);

Unneeded cast.

> +
> +	/* Now HW will be NAKing transfers to that EP,
> +	 * until a buffer is queued to it. */
> +
> +	/* should have stop the lock */
> +	spin_unlock_irqrestore(&udc->lock, flags);
> +	return 0;
> +
> +en_done:
> +	VDBG("init %s failed!", ep->ep.name);
> +	return -ENODEV;
> +}
> +
> +static int qe_usb_enable(void)
> +{
> +	setbits8(&udc_controller->usb_regs->usb_usmod, USB_MODE_EN);
> +
> +	return 0;

You don't need, neither check any return value for this function.
Return type of void will work here.

> +}
> +
> +static int qe_usb_disable(void)
> +{
> +	clrbits8(&udc_controller->usb_regs->usb_usmod, USB_MODE_EN);
> +
> +	return 0;

Ditto.

> +}
> +
> +/*----------------------------------------------------------------------------*
> + *		USB and EP basic manipulate function end		      *
> + *----------------------------------------------------------------------------*/
> +
> +
> +/******************************************************************************
> +		UDC transmit and receive process
> + ******************************************************************************/
> +static void recycle_one_rxbd(struct qe_ep *ep)
> +{
> +	u32 bdstatus;
> +
> +	bdstatus = (u32)BD_STATUS_AND_LENGTH(ep->e_rxbd);
> +	bdstatus = R_I | R_E | (bdstatus & R_W);
> +	BD_STATUS_AND_LENGTH_SET(ep->e_rxbd, bdstatus);
> +
> +	if (bdstatus & R_W)
> +		ep->e_rxbd = ep->rxbase;
> +	else
> +		ep->e_rxbd++;
> +}
> +
> +static void recycle_rxbds(struct qe_ep *ep, unsigned char stopatnext)
> +{
> +	u32 bdstatus;
> +	struct qe_bd *bd, *nextbd;

struct qe_bd *bd;
struct qe_bd *nextbd;

> +	unsigned char stop = 0;
> +
> +	nextbd = ep->n_rxbd;
> +	bd = ep->e_rxbd;
> +	bdstatus = (u32)BD_STATUS_AND_LENGTH(bd);
> +
> +	while (!(bdstatus & R_E) && !(bdstatus&BD_LENGTH_MASK) && !stop) {
> +		bdstatus = R_E | R_I | (bdstatus & R_W);
> +		BD_STATUS_AND_LENGTH_SET(bd, bdstatus);
> +
> +		if (bdstatus & R_W)
> +			bd = ep->rxbase;
> +		else
> +			bd++;
> +
> +		bdstatus = (u32)BD_STATUS_AND_LENGTH(bd);
> +		if (stopatnext && (bd == nextbd))
> +			stop = 1;
> +	}
> +
> +	ep->e_rxbd = (struct qe_bd *)bd;
> +}
> +
> +static void ep_recycle_rxbds(struct qe_ep *ep)
> +{
> +	struct qe_bd *bd = NULL;
> +	u32 bdstatus;
> +	u8 epnum;
> +	struct qe_udc *udc = NULL;

No need to = NULL.

> +
> +	udc = ep->udc;
> +	epnum = ep->epnum;
> +
> +	bd = ep->n_rxbd;
> +	bdstatus = (u32)BD_STATUS_AND_LENGTH(bd);
> +	if (!(bdstatus & R_E) && !(bdstatus & BD_LENGTH_MASK)) {
> +		bd = (struct qe_bd *)(ep->rxbase +
> +				((in_be16(&udc->ep_param[epnum]->rbptr) -
> +				  in_be16(&udc->ep_param[epnum]->rbase))
> +				 >> 3));
> +		bdstatus = (u32)BD_STATUS_AND_LENGTH(bd);
> +
> +		if (bdstatus & R_W)
> +			bd = ep->rxbase;
> +		else
> +			bd++;
> +
> +		ep->e_rxbd = (struct qe_bd *)bd;
> +		VDBG("The QE completed full bd cycle");
> +		recycle_rxbds(ep, 0);
> +		ep->e_rxbd = ep->n_rxbd;
> +	} else
> +		recycle_rxbds(ep, 1);
> +
> +	if (in_be16(&udc->usb_regs->usb_usber) & USB_E_BSY_MASK)
> +		out_be16(&udc->usb_regs->usb_usber, USB_E_BSY_MASK);
> +
> +	if (ep->has_data <= 0 && (!list_empty(&ep->queue)))
> +		qe_eprx_normal(ep);
> +
> +	ep->localnack = 0;
> +}
> +
> +static void setup_received_handle(struct qe_udc *udc,
> +					struct usb_ctrlrequest *setup);
> +static int qe_ep_rxframe_handle(struct qe_ep *ep);
> +static void ep0_req_complete(struct qe_udc *udc, struct qe_req *req);
> +/* when BD PID is setup, handle the packet */
> +static int ep0_setup_handle(struct qe_udc *udc)
> +{
> +	struct qe_ep *ep = &(udc->eps[0]);
> +	struct qe_frame *pframe = NULL;
> +	unsigned int fsize;
> +	u8 *cp;
> +
> +	pframe = (struct qe_frame *)ep->rxframe;

No need to case here.

> +	if ((frame_get_info(pframe) & PID_SETUP)
> +		&& (udc->ep0_state == WAIT_FOR_SETUP)) {
> +		fsize = (int)frame_get_length(pframe);
> +		if (unlikely(fsize != 8))
> +			return -EINVAL;
> +		cp = (u8 *)&(udc->local_setup_buff);
> +		memcpy(cp, pframe->data, fsize);
> +		ep->data01 = 1;
> +
> +		/* handle the usb command base on the usb_ctrlrequest */
> +		setup_received_handle(udc, &udc->local_setup_buff);
> +		return 0;
> +	}
> +	return -EINVAL;
> +}
> +
> +static int qe_ep0_rx(struct qe_udc *udc)
> +{
> +	struct qe_ep *ep = &(udc->eps[0]);
> +	struct qe_frame *pframe = NULL;
> +	struct qe_bd *bd;
> +	u32 bdstatus, length;
> +	u32 vaddr;
> +
> +	pframe = (struct qe_frame *)ep->rxframe;
> +
> +	if (ep->dir == USB_DIR_IN) {
> +		VDBG("error:This is a transmit ep!");
> +		return -EINVAL;
> +	}
> +
> +	bd = (struct qe_bd *)(ep->n_rxbd);
> +	bdstatus = BD_STATUS_AND_LENGTH(bd);
> +	length = bdstatus & BD_LENGTH_MASK;
> +
> +	while (!(bdstatus&R_E) && length) {

Spaces around &.

> +		if ((bdstatus & R_F) && (bdstatus & R_L)
> +			&& !(bdstatus & R_ERROR)) {

I would add one more tab here.

> +			if (length == USB_CRC_SIZE) {
> +				udc->ep0_state = WAIT_FOR_SETUP;
> +				VDBG("receive a ZLP in status phase");
> +			} else {
> +				qe_frame_clean(pframe);
> +				vaddr = (u32)phys_to_virt(BD_BUFFER(bd));
> +				frame_set_data(pframe, (u8 *)vaddr);
> +				frame_set_length(pframe,
> +						(length - USB_CRC_SIZE));
> +				frame_set_status(pframe, FRAME_OK);
> +				switch (bdstatus & R_PID) {
> +				case R_PID_SETUP:
> +					frame_set_info(pframe, PID_SETUP);
> +					break;
> +				case R_PID_DATA1:
> +					frame_set_info(pframe, PID_DATA1);
> +					break;
> +				default:
> +					frame_set_info(pframe, PID_DATA0);
> +					break;
> +				}
> +
> +				if ((bdstatus & R_PID) == R_PID_SETUP)
> +					ep0_setup_handle(udc);
> +				else
> +					qe_ep_rxframe_handle(ep);
> +			}
> +		} else {
> +			dev_err(udc->dev, "The receive frame with error!\n");
> +		}
> +
> +		/* note: don't clear the rxbd's buffer address */
> +		recycle_one_rxbd(ep);
> +
> +		/* Get next BD */
> +		if (bdstatus & R_W)
> +			bd = ep->rxbase;
> +		else
> +			bd++;
> +
> +		bdstatus = BD_STATUS_AND_LENGTH(bd);
> +		length = bdstatus & BD_LENGTH_MASK;
> +
> +	}
> +
> +	ep->n_rxbd = (struct qe_bd *)bd;
> +
> +	return 0;
> +}
> +
> +static int qe_ep_rxframe_handle(struct qe_ep *ep)
> +{
> +	struct qe_frame *pframe = NULL;

No need for = NULL.

> +	u8 framepid = 0;
> +	unsigned int fsize;
> +	u8 *cp = NULL;
> +	struct qe_req *req = NULL;

You can move this variable to } else { branch down in this
function. And no need for = NULL.

> +
> +	pframe = (struct qe_frame *)ep->rxframe;
> +
> +	if (frame_get_info(pframe) & PID_DATA1)
> +		framepid = 0x1;
> +
> +	if (framepid != ep->data01) {
> +		dev_err(ep->udc->dev, "the data01 error!\n");
> +		return -EIO;
> +	}
> +
> +	fsize = frame_get_length(pframe);
> +	if (list_empty(&ep->queue)) {
> +		dev_err(ep->udc->dev, "the %s have no requeue!\n", ep->name);
> +	} else {
> +		req = list_entry(ep->queue.next, struct qe_req, queue);
> +
> +		cp = (u8 *)(req->req.buf) + req->req.actual;
> +		if (cp) {
> +			memcpy(cp, pframe->data, fsize);
> +			req->req.actual += fsize;
> +			if ((fsize < ep->ep.maxpacket) ||
> +					(req->req.actual >= req->req.length)) {
> +				if (ep->epnum == 0)
> +					ep0_req_complete(ep->udc, req);
> +				else
> +					done(ep, req, 0);
> +				if (list_empty(&ep->queue) && ep->epnum != 0)
> +					qe_eprx_nack(ep);
> +			}
> +		}
> +	}
> +
> +	qe_ep_toggledata01(ep);
> +
> +	return 0;
> +}
> +
> +static void ep_rx_tasklet(unsigned long data)
> +{
> +	struct qe_udc *udc = (struct qe_udc *)data;
> +	struct qe_ep *ep = NULL;
> +	struct qe_frame *pframe = NULL;

These NULLs are not needed.

> +	struct qe_bd *bd;
> +	u32 bdstatus, length;
> +	u32 vaddr, i;
> +
> +	for (i = 1; i < USB_MAX_ENDPOINTS; i++) {
> +		ep = (struct qe_ep *)&(udc->eps[i]);

Cast unneeded, I think.

> +
> +		if (ep->dir == USB_DIR_IN || ep->enable_tasklet == 0) {
> +			VDBG("This is a transmit ep or disable tasklet!");
> +			continue;
> +		}
> +
> +		pframe = (struct qe_frame *)ep->rxframe;
> +		bd = (struct qe_bd *)(ep->n_rxbd);
> +		bdstatus = BD_STATUS_AND_LENGTH(bd);
> +		length = bdstatus & BD_LENGTH_MASK;
> +
> +		while (!(bdstatus&R_E) && length) {

Spaces around &.

> +			if (list_empty(&ep->queue)) {
> +				qe_eprx_nack(ep);
> +				VDBG("The rxep have noreq %d", ep->has_data);
> +				break;
> +			}
> +
> +			if ((bdstatus & R_F) && (bdstatus & R_L)
> +				&& !(bdstatus & R_ERROR)) {
> +				qe_frame_clean(pframe);
> +				vaddr = (u32)phys_to_virt(BD_BUFFER(bd));
> +				frame_set_data(pframe, (u8 *)vaddr);
> +				frame_set_length(pframe,
> +						(length - USB_CRC_SIZE));
> +				frame_set_status(pframe, FRAME_OK);
> +				switch (bdstatus & R_PID) {
> +				case R_PID_DATA1:
> +					frame_set_info(pframe, PID_DATA1);
> +					break;
> +				case R_PID_SETUP:
> +					frame_set_info(pframe, PID_SETUP);
> +					break;
> +				default:
> +					frame_set_info(pframe, PID_DATA0);
> +					break;
> +				}
> +				/* handle the rx frame */
> +				qe_ep_rxframe_handle(ep);
> +			} else {
> +				dev_err(udc->dev,
> +					"error in received frame\n");
> +			}
> +			/* note: don't clear the rxbd's buffer address */
> +			/*clear the length */
> +			BD_STATUS_AND_LENGTH_SET(bd, (bdstatus&BD_STATUS_MASK));
> +			ep->has_data--;
> +			if (!(ep->localnack))
> +				recycle_one_rxbd(ep);
> +
> +			/* Get next BD */
> +			if (bdstatus & R_W)
> +				bd = ep->rxbase;
> +			else
> +				bd++;
> +
> +			bdstatus = BD_STATUS_AND_LENGTH(bd);
> +			length = bdstatus & BD_LENGTH_MASK;
> +		}
> +
> +		ep->n_rxbd = (struct qe_bd *)bd;
> +
> +		if (ep->localnack)
> +			ep_recycle_rxbds(ep);
> +
> +		ep->enable_tasklet = 0;
> +	} /* for i=1 */
> +}
> +
> +static int qe_ep_rx(struct qe_ep *ep)
> +{
> +	struct qe_udc *udc = NULL;
> +	struct qe_frame *pframe = NULL;
> +	struct qe_bd *bd;
> +	u16 swoffs, ucoffs, emptybds;
> +	u32 bdstatus, length;
> +
> +	udc = (struct qe_udc *)ep->udc;
> +	pframe = (struct qe_frame *)ep->rxframe;
> +
> +	if (ep->dir == USB_DIR_IN) {
> +		VDBG("error:This is a transmit ep!");
> +		return -EINVAL;
> +	}
> +
> +	bd = (struct qe_bd *)(ep->n_rxbd);
> +	bdstatus = BD_STATUS_AND_LENGTH(bd);
> +	length = bdstatus & BD_LENGTH_MASK;
> +
> +	swoffs = (u16)(((u8 *)bd - (u8 *)ep->rxbase) >> 3);
> +	ucoffs = (u16)((in_be16(&udc->ep_param[ep->epnum]->rbptr) -
> +			in_be16(&udc->ep_param[ep->epnum]->rbase)) >> 3);
> +	if (swoffs < ucoffs)
> +		emptybds = USB_BDRING_LEN_RX - ucoffs + swoffs;
> +	else
> +		emptybds = swoffs - ucoffs;
> +
> +	if (emptybds < MIN_EMPTY_BDS) {
> +		qe_eprx_nack(ep);
> +		ep->localnack = 1;
> +		VDBG("The rx ep have a nack!%d", emptybds);
> +	}
> +	ep->has_data = USB_BDRING_LEN_RX - emptybds;
> +
> +	if (list_empty(&ep->queue)) {
> +		qe_eprx_nack(ep);
> +		VDBG("The rxep have noreq %d", ep->has_data);
> +		return 0;
> +	}
> +
> +	tasklet_schedule(&udc->rx_tasklet);
> +	ep->enable_tasklet = 1;
> +
> +	return 0;
> +}
> +
> +/* send data from a frame, no matter what tx_req */
> +static int qe_ep_tx(struct qe_ep *ep, struct qe_frame *frame)
> +{
> +	struct qe_udc *udc = NULL;
> +
> +	struct qe_bd *bd = NULL;

Nulls, spurious empty line.

> +	u16 saveusbmr;
> +	u32 bdstatus, pidmask;
> +	u32 paddr;
> +
> +	udc = (struct qe_udc *)ep->udc;
> +
> +	if (ep->dir == USB_DIR_OUT) {
> +		VDBG("error:this is a receive ep!");
> +		return -EINVAL;
> +	}
> +
> +	/* Disable the Tx interrupt */
> +	saveusbmr = in_be16(&udc->usb_regs->usb_usbmr);
> +	out_be16(&udc->usb_regs->usb_usbmr,
> +			saveusbmr & ~(USB_E_TXB_MASK | USB_E_TXE_MASK));
> +
> +	bd = ep->n_txbd;
> +	bdstatus = BD_STATUS_AND_LENGTH(bd);
> +
> +	if (!(bdstatus & (T_R|BD_LENGTH_MASK))) {
> +		if (frame_get_length(frame) == 0) {
> +			frame_set_data(frame, udc->nullbuf);
> +			frame_set_length(frame, 2);
> +			frame->info |= (ZLP|NO_CRC);
> +			VDBG("the frame size = 0");
> +		}
> +		paddr = virt_to_phys((void *)frame->data);
> +		BD_BUFFER_SET(bd, paddr);
> +		bdstatus = (bdstatus&T_W);
> +		if (!(frame_get_info(frame) & NO_CRC))
> +			bdstatus |= T_R | T_I | T_L | T_TC
> +					| frame_get_length(frame);
> +		else
> +			bdstatus |= T_R | T_I | T_L | frame_get_length(frame);
> +
> +		/* if the packet is a ZLP in status phase */
> +		if ((ep->epnum == 0) && (udc->ep0_state == DATA_STATE_NEED_ZLP))
> +			ep->data01 = 0x1;
> +
> +		if (ep->data01) {
> +			pidmask = T_PID_DATA1;
> +			frame->info |= PID_DATA1;
> +		} else {
> +			pidmask = T_PID_DATA0;
> +			frame->info |= PID_DATA0;
> +		}
> +		bdstatus |= T_CNF;
> +		bdstatus |= pidmask;
> +		BD_STATUS_AND_LENGTH_SET(bd, bdstatus);
> +		qe_ep_filltxfifo(ep);
> +
> +		/* enable the TX interrupt */
> +		out_be16(&udc->usb_regs->usb_usbmr, saveusbmr);
> +
> +		qe_ep_toggledata01(ep);
> +		if (bdstatus & T_W)
> +			ep->n_txbd = ep->txbase;
> +		else
> +			ep->n_txbd++;
> +
> +		return 0;
> +	} else {
> +		out_be16(&udc->usb_regs->usb_usbmr, saveusbmr);
> +		VDBG("The tx bd is not ready!");
> +		return -EBUSY;
> +	}
> +}
> +
> +/* when an bd was transmitted, the function can *
> + * handle the tx_req, not include ep0           */
> +static int txcomplete(struct qe_ep *ep, unsigned char restart)
> +{
> +	struct qe_req *tx_req = NULL;
> +
> +	tx_req = ep->tx_req;
> +	if (tx_req != NULL) {
> +		if (!restart) {
> +			int asent = ep->last;
> +			ep->sent += asent;
> +			ep->last -= asent;
> +		} else {
> +			ep->last = 0;
> +		}
> +
> +		/* a request already were transmitted completely */
> +		if ((ep->tx_req->req.length - ep->sent) <= 0) {
> +			ep->tx_req->req.actual = (unsigned int)ep->sent;
> +			done(ep, ep->tx_req, 0);
> +			ep->tx_req = NULL;
> +			ep->last = 0;
> +			ep->sent = 0;
> +		}
> +	}
> +
> +	/* we should gain a new tx_req fot this endpoint */
> +	if (ep->tx_req == NULL) {
> +		if (!list_empty(&ep->queue)) {
> +			ep->tx_req = list_entry(ep->queue.next,	struct qe_req,
> +							queue);
> +			ep->last = 0;
> +			ep->sent = 0;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/* give a frame and a tx_req,send some data */
> +static int qe_usb_senddata(struct qe_ep *ep, struct qe_frame *frame)
> +{
> +	unsigned int size;
> +	u8 *buf;
> +
> +	qe_frame_clean(frame);
> +	size = min_t(u32, (ep->tx_req->req.length - ep->sent),
> +				ep->ep.maxpacket);
> +	buf = (u8 *)ep->tx_req->req.buf + ep->sent;
> +	if (buf && size) {
> +		ep->last = size;
> +		frame_set_data(frame, buf);
> +		frame_set_length(frame, size);
> +		frame_set_status(frame, FRAME_OK);
> +		frame_set_info(frame, 0);
> +		return qe_ep_tx(ep, frame);
> +	}
> +	return -EIO;
> +}
> +
> +/* give a frame struct,send a ZLP */
> +static int sendnulldata(struct qe_ep *ep, struct qe_frame *frame, uint infor)
> +{
> +	struct qe_udc *udc = ep->udc;
> +
> +	if (frame == NULL)
> +		return -ENODEV;
> +
> +	qe_frame_clean(frame);
> +	frame_set_data(frame, (u8 *)udc->nullbuf);
> +	frame_set_length(frame, 2);
> +	frame_set_status(frame, FRAME_OK);
> +	frame_set_info(frame, (ZLP|NO_CRC|infor));
> +
> +	return qe_ep_tx(ep, frame);
> +}
> +
> +static int frame_create_tx(struct qe_ep *ep, struct qe_frame *frame)
> +{
> +	struct qe_req *req;
> +	int reval;
> +	if (ep->tx_req == NULL)
> +		return -ENODEV;
> +
> +	req = ep->tx_req;
> +
> +	if ((req->req.length - ep->sent) > 0)
> +		reval = qe_usb_senddata(ep, frame);
> +	else
> +		reval = sendnulldata(ep, frame, 0);
> +
> +	return reval;
> +}
> +
> +/* if direction is DIR_IN, the status is Device->Host
> + * if direction is DIR_OUT, the status transaction is Device<-Host
> + * in status phase, udc create a request and gain status */
> +static int ep0_prime_status(struct qe_udc *udc, int direction)
> +{
> +
> +	struct qe_ep *ep = &udc->eps[0];
> +
> +	if (direction == USB_DIR_IN) {
> +		udc->ep0_state = DATA_STATE_NEED_ZLP;
> +		udc->ep0_dir = USB_DIR_IN;
> +		sendnulldata(ep, ep->txframe, SETUP_STATUS|NO_REQ);
> +	} else {
> +		udc->ep0_dir = USB_DIR_OUT;
> +		udc->ep0_state = WAIT_FOR_OUT_STATUS;
> +	}
> +
> +	return 0;
> +}
> +
> +/* a request complete in ep0, whether gadget request or udc request */
> +static void ep0_req_complete(struct qe_udc *udc, struct qe_req *req)
> +{
> +	struct qe_ep *ep = &(udc->eps[0]);
> +	/* because usb and ep's status already been set in ch9setaddress() */
> +
> +	switch (udc->ep0_state) {
> +	case DATA_STATE_XMIT:
> +		done(ep, req, 0);
> +		/* receive status phase */
> +		if (ep0_prime_status(udc, USB_DIR_OUT))
> +			qe_ep0_stall(udc);
> +		break;
> +
> +	case DATA_STATE_NEED_ZLP:
> +		done(ep, req, 0);
> +		udc->ep0_state = WAIT_FOR_SETUP;
> +		break;
> +
> +	case DATA_STATE_RECV:
> +		done(ep, req, 0);
> +		/* send status phase */
> +		if (ep0_prime_status(udc, USB_DIR_IN))
> +			qe_ep0_stall(udc);
> +		break;
> +
> +	case WAIT_FOR_OUT_STATUS:
> +		done(ep, req, 0);
> +		udc->ep0_state = WAIT_FOR_SETUP;
> +		break;
> +
> +	case WAIT_FOR_SETUP:
> +		VDBG("Unexpected interrupt");
> +		break;
> +
> +	default:
> +		qe_ep0_stall(udc);
> +		break;
> +	}
> +}
> +
> +static int ep0_txcomplete(struct qe_ep *ep, unsigned char restart)
> +{
> +	struct qe_req *tx_req = NULL;
> +	struct qe_frame *frame = ep->txframe;
> +
> +	if ((frame_get_info(frame)&(ZLP|NO_REQ)) == (ZLP|NO_REQ)) {
> +		if (!restart)
> +			ep->udc->ep0_state = WAIT_FOR_SETUP;
> +		else
> +			sendnulldata(ep, ep->txframe, SETUP_STATUS|NO_REQ);
> +		return 0;
> +	}
> +
> +	tx_req = ep->tx_req;
> +	if (tx_req != NULL) {
> +		if (!restart) {
> +			int asent = ep->last;
> +			ep->sent += asent;
> +			ep->last -= asent;
> +		} else {
> +			ep->last = 0;
> +		}
> +
> +		/* a request already were transmitted completely */
> +		if ((ep->tx_req->req.length - ep->sent) <= 0) {
> +			ep->tx_req->req.actual = (unsigned int)ep->sent;
> +			ep0_req_complete(ep->udc, ep->tx_req);
> +			ep->tx_req = NULL;
> +			ep->last = 0;
> +			ep->sent = 0;
> +		}
> +	} else {
> +		VDBG("the ep0_controller have no req");
> +	}
> +
> +	return 0;
> +}
> +
> +static int ep0_txframe_handle(struct qe_ep *ep)
> +{
> +	/* if have error, transmit again */
> +	if (frame_get_status(ep->txframe)&FRAME_ERROR) {
> +		qe_ep_flushtxfifo(ep);
> +		VDBG("The EP0 transmit data have error!");
> +		if (frame_get_info(ep->txframe)&PID_DATA0)
> +			ep->data01 = 0;
> +		else
> +			ep->data01 = 1;
> +
> +		ep0_txcomplete(ep, 1);
> +	} else
> +		ep0_txcomplete(ep, 0);
> +
> +	frame_create_tx(ep, ep->txframe);
> +	return 0;
> +}
> +
> +static int qe_ep0_txconf(struct qe_ep *ep)
> +{
> +	struct qe_bd *bd = NULL;
> +	struct qe_frame *pframe = NULL;
> +	u32 bdstatus;
> +
> +	bd = ep->c_txbd;
> +	bdstatus = BD_STATUS_AND_LENGTH(bd);
> +	while (!(bdstatus & T_R) && (bdstatus & ~T_W)) {
> +		pframe = ep->txframe;
> +
> +		/* clear and recycle the BD */
> +		BD_STATUS_AND_LENGTH_SET(bd, (bdstatus&T_W));
> +		BD_BUFFER_CLEAR(bd);
> +		if (bdstatus&T_W)
> +			ep->c_txbd = ep->txbase;
> +		else
> +			ep->c_txbd++;
> +
> +		if (ep->c_txbd == ep->n_txbd) {
> +			if (bdstatus & DEVICE_T_ERROR) {
> +				frame_set_status(pframe, FRAME_ERROR);
> +				if (bdstatus & T_TO)
> +					pframe->status |= TX_ER_TIMEOUT;
> +				if (bdstatus & T_UN)
> +					pframe->status |= TX_ER_UNDERUN;
> +			}
> +			ep0_txframe_handle(ep);
> +		}
> +
> +		bd = ep->c_txbd;
> +		bdstatus = BD_STATUS_AND_LENGTH(bd);
> +	}
> +
> +	return 0;
> +}
> +
> +static int ep_txframe_handle(struct qe_ep *ep)
> +{
> +	if (frame_get_status(ep->txframe)&FRAME_ERROR) {
> +		qe_ep_flushtxfifo(ep);
> +		VDBG("The EP0 transmit data have error!");
> +		if (frame_get_info(ep->txframe)&PID_DATA0)
> +			ep->data01 = 0;
> +		else
> +			ep->data01 = 1;
> +
> +		txcomplete(ep, 1);
> +	} else
> +		txcomplete(ep, 0);
> +
> +	frame_create_tx(ep, ep->txframe); /* send the data */
> +	return 0;
> +}
> +
> +/* confirm the already trainsmited bd */
> +static int qe_ep_txconf(struct qe_ep *ep)
> +{
> +	struct qe_bd *bd = NULL;
> +	struct qe_frame *pframe = NULL;
> +	u32 bdstatus;
> +	unsigned char breakonrxinterrupt = 0;
> +
> +	bd = ep->c_txbd;
> +	bdstatus = BD_STATUS_AND_LENGTH(bd);
> +	while (!(bdstatus & T_R) && (bdstatus & ~T_W)) {
> +		pframe = ep->txframe;
> +		if (bdstatus & DEVICE_T_ERROR) {
> +			frame_set_status(pframe, FRAME_ERROR);
> +			if (bdstatus & T_TO)
> +				pframe->status |= TX_ER_TIMEOUT;
> +			if (bdstatus & T_UN)
> +				pframe->status |= TX_ER_UNDERUN;
> +		}
> +
> +		/* clear and recycle the BD */
> +		BD_STATUS_AND_LENGTH_SET(bd, (bdstatus&T_W));
> +		BD_BUFFER_CLEAR(bd);
> +		if (bdstatus&T_W)
> +			ep->c_txbd = ep->txbase;
> +		else
> +			ep->c_txbd++;
> +
> +		/* handle the tx frame */
> +		ep_txframe_handle(ep);
> +		bd = ep->c_txbd;
> +		bdstatus = BD_STATUS_AND_LENGTH(bd);
> +	}
> +	if (breakonrxinterrupt)
> +		return -EIO;
> +	else
> +		return 0;
> +}
> +
> +/* Add a request in queue, and try to transmit a packet */
> +static int ep_req_send(struct qe_ep *ep, struct qe_req *req)
> +{
> +	int reval = 0;
> +
> +	if (ep->tx_req == NULL) {
> +		ep->sent = 0;
> +		ep->last = 0;
> +		txcomplete(ep, 0); /* can gain a new tx_req */
> +		reval = frame_create_tx(ep, ep->txframe);
> +	}
> +	return reval;
> +}
> +
> +/* Maybe this is a good ideal */
> +static int ep_req_rx(struct qe_ep *ep, struct qe_req *req)
> +{
> +	struct qe_udc *udc = NULL;
> +	struct qe_frame *pframe = NULL;
> +	struct qe_bd *bd;
> +	u32 bdstatus, length;
> +	u32 vaddr, fsize;
> +	u8 *cp;
> +	u8 finish_req = 0;
> +	u8 framepid;
> +
> +	if (list_empty(&ep->queue)) {
> +		VDBG("the req already finish!");
> +		return 0;
> +	}
> +	udc = (struct qe_udc *)ep->udc;
> +	pframe = (struct qe_frame *)ep->rxframe;
> +
> +	bd = (struct qe_bd *)(ep->n_rxbd);
> +	bdstatus = BD_STATUS_AND_LENGTH(bd);
> +	length = bdstatus & BD_LENGTH_MASK;
> +
> +	while (!(bdstatus&R_E) && length) {
> +		if (finish_req)
> +			break;
> +		if ((bdstatus & R_F) && (bdstatus & R_L)
> +					&& !(bdstatus & R_ERROR)) {
> +			qe_frame_clean(pframe);
> +			vaddr = (u32)phys_to_virt(BD_BUFFER(bd));
> +			frame_set_data(pframe, (u8 *)vaddr);
> +			frame_set_length(pframe, (length - USB_CRC_SIZE));
> +			frame_set_status(pframe, FRAME_OK);
> +			switch (bdstatus & R_PID) {
> +			case R_PID_DATA1:
> +				frame_set_info(pframe, PID_DATA1); break;
> +			default:
> +				frame_set_info(pframe, PID_DATA0); break;
> +			}
> +			/* handle the rx frame */
> +
> +			if (frame_get_info(pframe)&PID_DATA1)
> +				framepid = 0x1;
> +			else
> +				framepid = 0;
> +
> +			if (framepid != ep->data01) {
> +				VDBG("the data01 error!");
> +			} else {
> +				fsize = frame_get_length(pframe);
> +
> +				cp = (u8 *)(req->req.buf) + req->req.actual;
> +				if (cp) {
> +					memcpy(cp, pframe->data, fsize);
> +					req->req.actual += fsize;
> +					if ((fsize < ep->ep.maxpacket)
> +						|| (req->req.actual >=
> +							req->req.length)) {
> +						finish_req = 1;
> +						done(ep, req, 0);
> +						if (list_empty(&ep->queue))
> +							qe_eprx_nack(ep);
> +					}
> +				}
> +				qe_ep_toggledata01(ep);
> +			}
> +		} else {
> +			dev_err(udc->dev, "The receive frame with error!\n");
> +		}
> +
> +		/* note: don't clear the rxbd's buffer address *
> +		 * only Clear the length */
> +		BD_STATUS_AND_LENGTH_SET(bd, (bdstatus & BD_STATUS_MASK));
> +		ep->has_data--;
> +
> +		/* Get next BD */
> +		if (bdstatus & R_W)
> +			bd = ep->rxbase;
> +		else
> +			bd++;
> +
> +		bdstatus = BD_STATUS_AND_LENGTH(bd);
> +		length = bdstatus & BD_LENGTH_MASK;
> +	}
> +
> +	ep->n_rxbd = (struct qe_bd *)bd;
> +	ep_recycle_rxbds(ep);
> +
> +	return 0;
> +}
> +
> +/* only add the request in queue */
> +static int ep_req_receive(struct qe_ep *ep, struct qe_req *req)
> +{
> +	if (ep->state == EP_STATE_NACK) {
> +		if (ep->has_data <= 0) {
> +			/* Enable rx and unmask rx interrupt */
> +			qe_eprx_normal(ep);
> +		} else {
> +			/* Copy the exist BD data */
> +			ep_req_rx(ep, req);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/********************************************************************
> +	Internal Used Function End
> +********************************************************************/
> +
> +/*-----------------------------------------------------------------------
> +	Endpoint Management Functions For Gadget
> + -----------------------------------------------------------------------*/
> +static int qe_ep_enable(struct usb_ep *_ep,
> +			 const struct usb_endpoint_descriptor *desc)
> +{
> +	struct qe_udc *udc = NULL;
> +	struct qe_ep *ep = NULL;
> +	int retval = 0;
> +	unsigned char epnum;
> +	unsigned long flags = 0;
> +
> +	ep = container_of(_ep, struct qe_ep, ep);
> +
> +	/* catch various bogus parameters */
> +	if (!_ep || !desc || ep->desc || _ep->name == ep_name[0] ||
> +		(desc->bDescriptorType != USB_DT_ENDPOINT))
> +		/* FIXME: add judge for ep->bEndpointAddress */
> +		return -EINVAL;
> +
> +	udc = ep->udc;
> +	if (!udc->driver || (udc->gadget.speed == USB_SPEED_UNKNOWN))
> +		return -ESHUTDOWN;
> +
> +	epnum = (u8)desc->bEndpointAddress&0xF;
> +
> +	spin_lock_irqsave(&udc->lock, flags);
> +	retval = qe_ep_init((void *)udc, epnum, desc);
> +	spin_unlock_irqrestore(&udc->lock, flags);
> +	if (retval != 0) {
> +		qe_muram_free(muram_offset(ep->rxbase));
> +		VDBG("enable ep%d fail", ep->epnum);
> +		return -EINVAL;
> +	}
> +	VDBG("enable ep%d ok", ep->epnum);
> +	return 0;
> +}
> +
> +static int qe_ep_disable(struct usb_ep *_ep)
> +{
> +	struct qe_udc *udc = NULL;
> +	struct qe_ep *ep = NULL;
> +	unsigned long flags = 0;
> +	unsigned int size;
> +
> +	ep = container_of(_ep, struct qe_ep, ep);
> +
> +	if (!_ep || !ep->desc) {
> +		VDBG("%s not enabled", _ep ? ep->ep.name : NULL);
> +		return -EINVAL;
> +	}
> +
> +	udc = (struct qe_udc *) ep->udc;
> +
> +	spin_lock_irqsave(&udc->lock, flags);
> +	/* Nuke all pending requests (does flush) */
> +	nuke(ep, -ESHUTDOWN);
> +	ep->desc = NULL;
> +	ep->stopped = 1;
> +	spin_unlock_irqrestore(&udc->lock, flags);
> +
> +	qe_muram_free(muram_offset(ep->rxbase));
> +
> +	if (ep->dir == USB_DIR_OUT)
> +		size = (ep->ep.maxpacket + USB_CRC_SIZE + 2) *
> +				(USB_BDRING_LEN_RX + 1);
> +	else
> +		size = (ep->ep.maxpacket + USB_CRC_SIZE + 2) *
> +				(USB_BDRING_LEN + 1);
> +
> +	if (ep->dir != USB_DIR_IN) {
> +		kfree(ep->rxframe);
> +		if (ep->rxbufmap) {
> +			dma_unmap_single(udc_controller->gadget.dev.parent,
> +					ep->rxbuf_d, size,
> +					DMA_FROM_DEVICE);
> +				ep->rxbuf_d = DMA_ADDR_INVALID;
> +		} else
> +			dma_sync_single_for_cpu(
> +					udc_controller->gadget.dev.parent,
> +					ep->rxbuf_d, size,
> +					DMA_FROM_DEVICE);
> +		kfree(ep->rxbuffer);
> +	}
> +
> +	if (ep->dir != USB_DIR_OUT)
> +		kfree(ep->txframe);
> +
> +	VDBG("disabled %s OK", _ep->name);
> +	return 0;
> +}
> +
> +static struct usb_request *qe_alloc_request(struct usb_ep *_ep,	gfp_t gfp_flags)
> +{
> +	struct qe_req *req = NULL;
> +
> +	req = kzalloc(sizeof *req, gfp_flags);

sizeof(*reg)

> +	if (!req)
> +		return NULL;
> +
> +	req->req.dma = DMA_ADDR_INVALID;
> +
> +	INIT_LIST_HEAD(&req->queue);
> +
> +	return &req->req;
> +}
> +
> +static void qe_free_request(struct usb_ep *_ep, struct usb_request *_req)
> +{
> +	struct qe_req *req = NULL;
> +
> +	req = container_of(_req, struct qe_req, req);
> +
> +	if (_req)
> +		kfree(req);
> +}
> +
> +/* queues (submits) an I/O request to an endpoint */
> +static int qe_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
> +				gfp_t gfp_flags)
> +{
> +	struct qe_ep *ep = container_of(_ep, struct qe_ep, ep);
> +	struct qe_req *req = container_of(_req, struct qe_req, req);
> +	struct qe_udc *udc;
> +	unsigned long flags;
> +	int reval;
> +
> +	/* catch various bogus parameters */
> +	if (!_req || !req->req.complete || !req->req.buf
> +	|| !list_empty(&req->queue)) {
> +		VDBG("bad params");
> +		return -EINVAL;
> +	}
> +	if (!_ep || (!ep->desc && ep_index(ep))) {
> +		VDBG("bad ep");
> +		return -EINVAL;
> +	}
> +
> +	udc = ep->udc;
> +	if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
> +		return -ESHUTDOWN;
> +
> +	req->ep = ep;
> +
> +	/* map virtual address to hardware */
> +	if (req->req.dma == DMA_ADDR_INVALID) {
> +		req->req.dma = dma_map_single(ep->udc->gadget.dev.parent,
> +					req->req.buf,
> +					req->req.length,
> +					ep_is_in(ep)
> +					? DMA_TO_DEVICE :
> +					DMA_FROM_DEVICE);
> +		req->mapped = 1;
> +	} else {
> +		dma_sync_single_for_device(ep->udc->gadget.dev.parent,
> +					req->req.dma, req->req.length,
> +					ep_is_in(ep)
> +					? DMA_TO_DEVICE :
> +					DMA_FROM_DEVICE);
> +		req->mapped = 0;
> +	}
> +
> +	req->req.status = -EINPROGRESS;
> +	req->req.actual = 0;
> +
> +	list_add_tail(&req->queue, &ep->queue);
> +	VDBG("gadget have request in %s! %d", ep->name, req->req.length);
> +	spin_lock_irqsave(&udc->lock, flags);
> +	/* push the request to device */
> +	if (ep_is_in(ep))
> +		reval = ep_req_send(ep, req);
> +
> +	/* EP0 */
> +	if (ep_index(ep) == 0 && req->req.length > 0) {
> +		if (ep_is_in(ep))
> +			udc->ep0_state = DATA_STATE_XMIT;
> +		else
> +			udc->ep0_state = DATA_STATE_RECV;
> +	}
> +
> +	if (ep->dir == USB_DIR_OUT)
> +		reval = ep_req_receive(ep, req);
> +
> +	spin_unlock_irqrestore(&udc->lock, flags);
> +
> +	return 0;
> +}
> +
> +/* dequeues (cancels, unlinks) an I/O request from an endpoint */
> +static int qe_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
> +{
> +	struct qe_ep *ep = container_of(_ep, struct qe_ep, ep);
> +	struct qe_req *req;
> +	unsigned long flags;
> +
> +	if (!_ep || !_req)
> +		return -EINVAL;
> +
> +	spin_lock_irqsave(&ep->udc->lock, flags);
> +
> +	/* make sure it's actually queued on this endpoint */
> +	list_for_each_entry(req, &ep->queue, queue) {
> +		if (&req->req == _req)
> +			break;
> +	}
> +
> +	if (&req->req != _req) {
> +		spin_unlock_irqrestore(&ep->udc->lock, flags);
> +		return -EINVAL;
> +	}
> +
> +	done(ep, req, -ECONNRESET);
> +
> +	spin_unlock_irqrestore(&ep->udc->lock, flags);
> +	return 0;
> +}
> +
> +/*-----------------------------------------------------------------
> + * modify the endpoint halt feature
> + * @ep: the non-isochronous endpoint being stalled
> + * @value: 1--set halt  0--clear halt
> + * Returns zero, or a negative error code.
> +*----------------------------------------------------------------*/
> +static int qe_ep_set_halt(struct usb_ep *_ep, int value)
> +{
> +	struct qe_ep *ep = NULL;
> +	unsigned long flags = 0;
> +	int status = -EOPNOTSUPP;
> +	struct qe_udc *udc = NULL;
> +
> +	ep = container_of(_ep, struct qe_ep, ep);
> +	udc = ep->udc;
> +	if (!_ep || !ep->desc) {
> +		status = -EINVAL;
> +		goto out;
> +	}
> +
> +	if (ep->epnum != 0) {
> +		status = 0;
> +		goto out;
> +	}
> +
> +	/* Attempt to halt IN ep will fail if any transfer requests
> +	 * are still queue */
> +	if (value && ep_is_in(ep) && !list_empty(&ep->queue)) {
> +		status = -EAGAIN;
> +		goto out;
> +	}
> +
> +	status = 0;
> +	spin_lock_irqsave(&ep->udc->lock, flags);
> +	qe_eptx_stall_change(ep, value);
> +	qe_eprx_stall_change(ep, value);
> +	spin_unlock_irqrestore(&ep->udc->lock, flags);
> +
> +	if (ep->epnum == 0) {
> +		udc->ep0_state = WAIT_FOR_SETUP;
> +		udc->ep0_dir = 0;
> +	}
> +out:
> +	VDBG(" %s %s halt stat %d", ep->ep.name,
> +			value ?  "set" : "clear", status);
> +
> +	return status;
> +}
> +
> +static struct usb_ep_ops qe_ep_ops = {
> +	.enable = qe_ep_enable,
> +	.disable = qe_ep_disable,
> +
> +	.alloc_request = qe_alloc_request,
> +	.free_request = qe_free_request,
> +
> +	.queue = qe_ep_queue,
> +	.dequeue = qe_ep_dequeue,
> +
> +	.set_halt = qe_ep_set_halt,
> +};
> +
> +/*------------------------------------------------------------------------
> +	Gadget Driver Layer Operations
> + ------------------------------------------------------------------------*/
> +
> +/* Get the current frame number */
> +static int qe_get_frame(struct usb_gadget *gadget)
> +{
> +	u16 tmp;
> +
> +	tmp = in_be16(&udc_controller->usb_param->frame_n);
> +	if (tmp & 0x8000)
> +		tmp = tmp & 0x07ff;
> +	else
> +		tmp = -EINVAL;
> +
> +	return (int)tmp;
> +}
> +
> +/* Tries to wake up the host connected to this gadget
> + *
> + * Return : 0-success
> + * Negative-this feature not enabled by host or not supported by device hw
> + */
> +static int qe_wakeup(struct usb_gadget *gadget)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +/* Notify controller that VBUS is powered, Called by whatever
> +   detects VBUS sessions */
> +static int qe_vbus_session(struct usb_gadget *gadget, int is_active)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +/* constrain controller's VBUS power usage
> + * This call is used by gadget drivers during SET_CONFIGURATION calls,
> + * reporting how much power the device may consume.  For example, this
> + * could affect how quickly batteries are recharged.
> + *
> + * Returns zero on success, else negative errno.
> + */
> +static int qe_vbus_draw(struct usb_gadget *gadget, unsigned mA)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +/* Change Data+ pullup status
> + * this func is used by usb_gadget_connect/disconnet
> + */
> +static int qe_pullup(struct usb_gadget *gadget, int is_on)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +/* defined in usb_gadget.h */
> +static struct usb_gadget_ops qe_gadget_ops = {
> +	.get_frame = qe_get_frame,
> +	.wakeup = qe_wakeup,
> +/*	.set_selfpowered = qe_set_selfpowered,*/ /* always selfpowered */
> +	.vbus_session = qe_vbus_session,
> +	.vbus_draw = qe_vbus_draw,
> +	.pullup = qe_pullup,
> +};
> +
> +/*-------------------------------------------------------------------------
> +	USB ep0 Setup process in BUS Enumeration
> + -------------------------------------------------------------------------*/
> +static int udc_reset_ep_queue(struct qe_udc *udc, u8 pipe)
> +{
> +	struct qe_ep *ep = &udc->eps[pipe];
> +
> +	nuke(ep, -ECONNRESET);
> +	ep->tx_req = NULL;
> +	return 0;
> +}
> +
> +static int reset_queues(struct qe_udc *udc)
> +{
> +	u8 pipe;
> +
> +	for (pipe = 0; pipe < USB_MAX_ENDPOINTS; pipe++)
> +		udc_reset_ep_queue(udc, pipe);
> +
> +	/* report disconnect; the driver is already quiesced */
> +	udc->driver->disconnect(&udc->gadget);
> +
> +	return 0;
> +}
> +
> +static void ch9setaddress(struct qe_udc *udc, u16 value, u16 index,
> +			u16 length)
> +{
> +	/* Save the new address to device struct */
> +	udc->device_address = (u8) value;
> +	/* Update usb state */
> +	udc->usb_state = USB_STATE_ADDRESS;
> +
> +	/* Status phase , send a ZLP */
> +	if (ep0_prime_status(udc, USB_DIR_IN))
> +		qe_ep0_stall(udc);
> +}
> +
> +static void ownercomplete(struct usb_ep *_ep, struct usb_request *_req)
> +{
> +	struct qe_req *req = container_of(_req, struct qe_req, req);
> +
> +	req->req.buf = 0;
> +	kfree(req);
> +}
> +
> +static void ch9getstatus(struct qe_udc *udc, u16 value, u16 index,
> +			 u16 length)
> +{
> +	u16 usb_status = 0;	/* fix me to give correct status */
> +
> +	struct qe_req *req;
> +	struct qe_ep *ep;
> +	int status = 0;
> +
> +	ep = &udc->eps[0];
> +
> +	req = container_of(qe_alloc_request(&ep->ep, GFP_KERNEL),
> +					struct qe_req, req);
> +	req->req.length = 2;
> +	req->req.buf = udc->nullbuf;
> +	memcpy(req->req.buf, (u8 *)&usb_status, 2);
> +	req->req.status = -EINPROGRESS;
> +	req->req.actual = 0;
> +	req->req.complete = ownercomplete;
> +
> +	udc->ep0_dir = USB_DIR_IN;
> +
> +	/* data phase */
> +	status = qe_ep_queue(&(ep->ep), &(req->req), GFP_ATOMIC);
> +
> +	if (status) {
> +		dev_err(udc->dev, "Can't respond to getstatus request \n");
> +		qe_ep0_stall(udc);
> +	}
> +}
> +
> +/* only handle the setup request, suppose the device in normal status */
> +static void setup_received_handle(struct qe_udc *udc,
> +				struct usb_ctrlrequest *setup)
> +{
> +	/* Fix Endian (udc->local_setup_buff is cpu Endian now)*/
> +	u16 wValue = le16_to_cpu(setup->wValue);
> +	u16 wIndex = le16_to_cpu(setup->wIndex);
> +	u16 wLength = le16_to_cpu(setup->wLength);
> +
> +	/* clear the previous request in the ep0 */
> +	udc_reset_ep_queue(udc, 0);
> +
> +	if (setup->bRequestType & USB_DIR_IN)
> +		udc->ep0_dir = USB_DIR_IN;
> +	else
> +		udc->ep0_dir = USB_DIR_OUT;
> +
> +	switch (setup->bRequest) {
> +	case USB_REQ_GET_STATUS:
> +		/* Data+Status phase form udc */
> +		if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_MASK))
> +					!= (USB_DIR_IN | USB_TYPE_STANDARD))
> +			break;
> +		ch9getstatus(udc, wValue, wIndex, wLength);
> +		return;
> +
> +	case USB_REQ_SET_ADDRESS:
> +		/* Status phase from udc */
> +		if (setup->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD |
> +						USB_RECIP_DEVICE))
> +			break;
> +		ch9setaddress(udc, wValue, wIndex, wLength);
> +		return;
> +
> +	case USB_REQ_CLEAR_FEATURE:
> +	case USB_REQ_SET_FEATURE:
> +		/* Requests with no data phase, status phase from udc */
> +		if ((setup->bRequestType &  USB_TYPE_MASK)
> +					!= USB_TYPE_STANDARD)
> +			break;
> +
> +		if ((setup->bRequestType & USB_RECIP_MASK)
> +				== USB_RECIP_ENDPOINT) {
> +			int pipe = wIndex & USB_ENDPOINT_NUMBER_MASK;
> +			struct qe_ep *ep;
> +
> +			if (wValue != 0 || wLength != 0
> +				|| pipe > USB_MAX_ENDPOINTS)
> +				break;
> +			ep = &udc->eps[pipe];
> +
> +			spin_unlock(&udc->lock);
> +			qe_ep_set_halt(&ep->ep,
> +					(setup->bRequest == USB_REQ_SET_FEATURE)
> +						? 1 : 0);
> +			spin_lock(&udc->lock);
> +		}
> +
> +		ep0_prime_status(udc, USB_DIR_IN);
> +
> +		return;
> +
> +	default:
> +		break;
> +	}
> +
> +	if (wLength) {
> +		/* Data phase from gadget, status phase from udc */
> +		if (setup->bRequestType & USB_DIR_IN) {
> +			udc->ep0_state = DATA_STATE_XMIT;
> +			udc->ep0_dir = USB_DIR_IN;
> +		} else{
> +			udc->ep0_state = DATA_STATE_RECV;
> +			udc->ep0_dir = USB_DIR_OUT;
> +		}
> +		spin_unlock(&udc->lock);
> +		if (udc->driver->setup(&udc->gadget,
> +					&udc->local_setup_buff) < 0)
> +			qe_ep0_stall(udc);
> +		spin_lock(&udc->lock);
> +	} else {
> +		/* No data phase, IN status from gadget */
> +		udc->ep0_dir = USB_DIR_IN;
> +		spin_unlock(&udc->lock);
> +		if (udc->driver->setup(&udc->gadget,
> +					&udc->local_setup_buff) < 0)
> +			qe_ep0_stall(udc);
> +		spin_lock(&udc->lock);
> +		udc->ep0_state = DATA_STATE_NEED_ZLP;
> +	}
> +}
> +
> +/*-------------------------------------------------------------------------
> +	USB Interrupt handlers
> + -------------------------------------------------------------------------*/
> +static void suspend_irq(struct qe_udc *udc)
> +{
> +	udc->resume_state = udc->usb_state;
> +	udc->usb_state = USB_STATE_SUSPENDED;
> +
> +	/* report suspend to the driver ,serial.c not support this*/
> +	if (udc->driver->suspend)
> +		udc->driver->suspend(&udc->gadget);
> +}
> +
> +static void resume_irq(struct qe_udc *udc)
> +{
> +	udc->usb_state = udc->resume_state;
> +	udc->resume_state = 0;
> +
> +	/* report resume to the driver , serial.c not support this*/
> +	if (udc->driver->resume)
> +		udc->driver->resume(&udc->gadget);
> +}
> +
> +static void idle_irq(struct qe_udc *udc)
> +{
> +	u8 usbs;
> +
> +	usbs = in_8(&udc->usb_regs->usb_usbs);
> +	if (usbs & USB_IDLE_STATUS_MASK) {
> +		if ((udc->usb_state) != USB_STATE_SUSPENDED)
> +			suspend_irq(udc);
> +	} else {
> +		if (udc->usb_state == USB_STATE_SUSPENDED)
> +			resume_irq(udc);
> +	}
> +}
> +
> +static int reset_irq(struct qe_udc *udc)
> +{
> +	unsigned char i;
> +
> +	qe_usb_disable();
> +	out_8(&udc->usb_regs->usb_usadr, 0);
> +
> +	for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
> +		if (udc->eps[i].init)
> +			qe_ep_reset(udc, i);
> +	}
> +
> +	reset_queues(udc);
> +	udc->usb_state = USB_STATE_DEFAULT;
> +	udc->ep0_state = WAIT_FOR_SETUP;
> +	udc->ep0_dir = USB_DIR_OUT;
> +	qe_usb_enable();
> +	return 0;
> +}
> +
> +static int bsy_irq(struct qe_udc *udc)
> +{
> +	return 0;
> +}
> +
> +static int txe_irq(struct qe_udc *udc)
> +{
> +	return 0;
> +}
> +
> +/* ep0 tx interrupt also in here */
> +static int tx_irq(struct qe_udc *udc)
> +{
> +	struct qe_ep *ep;
> +	int i, res = 0;
> +	struct qe_bd *bd;
> +
> +	if ((udc->usb_state == USB_STATE_ADDRESS)
> +		&& (in_8(&udc->usb_regs->usb_usadr) == 0))
> +		out_8(&udc->usb_regs->usb_usadr, udc->device_address);
> +
> +	for (i = (USB_MAX_ENDPOINTS-1); ((i >= 0) && (res == 0)); i--) {
> +		ep = &(udc->eps[i]);
> +		if (ep && ep->init && (ep->dir != USB_DIR_OUT)) {
> +			bd = ep->c_txbd;
> +			if (!(BD_STATUS_AND_LENGTH(bd)&T_R)
> +						&& (BD_BUFFER(bd))) {
> +				/* Disable the TX Interrupt */
> +				/*confirm the transmitted bd*/
> +				if (ep->epnum == 0)
> +					res = qe_ep0_txconf(ep);
> +				else
> +					res = qe_ep_txconf(ep);
> +				/* Enable the TX Interrupt */
> +			}
> +		}
> +	}
> +	return res;
> +}
> +
> +
> +/* setup packect's rx is handle in the function too */
> +static void rx_irq(struct qe_udc *udc)
> +{
> +	struct qe_ep *ep;
> +	int i;
> +	struct qe_bd *bd;
> +
> +	for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
> +		ep = &(udc->eps[i]);
> +		if (ep && ep->init && (ep->dir != USB_DIR_IN)) {
> +			bd = ep->n_rxbd;
> +			if (!(BD_STATUS_AND_LENGTH(bd)&R_E)
> +						&& (BD_BUFFER(bd))) {
> +				if (ep->epnum == 0) {
> +					qe_ep0_rx(udc);
> +				} else {
> +					/*non-setup package receive*/
> +					qe_ep_rx(ep);
> +				}
> +			}
> +		}
> +	}
> +}
> +
> +static irqreturn_t qe_udc_irq(int irq, void *_udc)
> +{
> +	struct qe_udc *udc = (struct qe_udc *)_udc;
> +	u16 irq_src;
> +	irqreturn_t status = IRQ_NONE;
> +	unsigned long flags;
> +
> +
> +	spin_lock_irqsave(&udc->lock, flags);
> +
> +	irq_src = in_be16(&udc->usb_regs->usb_usber) &
> +		in_be16(&udc->usb_regs->usb_usbmr);
> +	/* Clear notification bits */
> +	out_be16(&udc->usb_regs->usb_usber, irq_src);
> +	/* USB Interrupt */
> +	if (irq_src & USB_E_IDLE_MASK) {
> +		idle_irq(udc);
> +		irq_src &= ~USB_E_IDLE_MASK;
> +		status = IRQ_HANDLED;
> +	}
> +
> +	if (irq_src & USB_E_TXB_MASK) {
> +		tx_irq(udc);
> +		irq_src &= ~USB_E_TXB_MASK;
> +		status = IRQ_HANDLED;
> +	}
> +
> +	if (irq_src & USB_E_RXB_MASK) {
> +		rx_irq(udc);
> +		irq_src &= ~USB_E_RXB_MASK;
> +		status = IRQ_HANDLED;
> +	}
> +
> +	if (irq_src & USB_E_RESET_MASK) {
> +		reset_irq(udc);
> +		irq_src &= ~USB_E_RESET_MASK;
> +		status = IRQ_HANDLED;
> +	}
> +
> +	if (irq_src & USB_E_BSY_MASK) {
> +		bsy_irq(udc);
> +		irq_src &= ~USB_E_BSY_MASK;
> +		status = IRQ_HANDLED;
> +	}
> +
> +	if (irq_src & USB_E_TXE_MASK) {
> +		txe_irq(udc);
> +		irq_src &= ~USB_E_TXE_MASK;
> +		status = IRQ_HANDLED;
> +	}
> +
> +	spin_unlock_irqrestore(&udc->lock, flags);
> +
> +	return status;
> +}
> +
> +/*-------------------------------------------------------------------------
> +	Gadget driver register and unregister.
> + --------------------------------------------------------------------------*/
> +int usb_gadget_register_driver(struct usb_gadget_driver *driver)
> +{
> +	int retval;
> +	unsigned long flags = 0;
> +
> +	/* standard operations */
> +	if (!udc_controller)
> +		return -ENODEV;
> +
> +	if (!driver || (driver->speed != USB_SPEED_FULL
> +			&& driver->speed != USB_SPEED_HIGH)
> +			|| !driver->bind || !driver->unbind ||
> +			!driver->disconnect || !driver->setup)
> +		return -EINVAL;
> +
> +	if (udc_controller->driver)
> +		return -EBUSY;
> +
> +	/* lock is needed but whether should use this lock or another */
> +	spin_lock_irqsave(&udc_controller->lock, flags);
> +
> +	driver->driver.bus = 0;
> +	/* hook up the driver */
> +	udc_controller->driver = driver;
> +	udc_controller->gadget.dev.driver = &driver->driver;
> +	udc_controller->gadget.speed = (enum usb_device_speed)(driver->speed);
> +	spin_unlock_irqrestore(&udc_controller->lock, flags);
> +
> +	retval = driver->bind(&udc_controller->gadget);
> +	if (retval) {
> +		dev_err(udc_controller->dev, "bind to %s --> %d",
> +				driver->driver.name, retval);
> +		udc_controller->gadget.dev.driver = 0;
> +		udc_controller->driver = 0;
> +		return retval;
> +	}
> +
> +	/* Enable IRQ reg and Set usbcmd reg EN bit */
> +	qe_usb_enable();
> +
> +	out_be16(&udc_controller->usb_regs->usb_usber, 0xffff);
> +	out_be16(&udc_controller->usb_regs->usb_usbmr, USB_E_DEFAULT_DEVICE);
> +	udc_controller->usb_state = USB_STATE_ATTACHED;
> +	udc_controller->ep0_state = WAIT_FOR_SETUP;
> +	udc_controller->ep0_dir = USB_DIR_OUT;
> +	dev_info(udc_controller->dev, "%s bind to driver %s \n",
> +		udc_controller->gadget.name, driver->driver.name);
> +	return 0;
> +}
> +EXPORT_SYMBOL(usb_gadget_register_driver);
> +
> +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
> +{
> +	struct qe_ep *loop_ep;
> +	unsigned long flags;
> +
> +	if (!udc_controller)
> +		return -ENODEV;
> +
> +	if (!driver || driver != udc_controller->driver)
> +		return -EINVAL;
> +
> +	/* stop usb controller, disable intr */
> +	qe_usb_disable();
> +
> +	/* in fact, no needed */
> +	udc_controller->usb_state = USB_STATE_ATTACHED;
> +	udc_controller->ep0_state = WAIT_FOR_SETUP;
> +	udc_controller->ep0_dir = 0;
> +
> +	/* stand operation */
> +	spin_lock_irqsave(&udc_controller->lock, flags);
> +	udc_controller->gadget.speed = USB_SPEED_UNKNOWN;
> +	nuke(&udc_controller->eps[0], -ESHUTDOWN);
> +	list_for_each_entry(loop_ep, &udc_controller->gadget.ep_list,
> +				ep.ep_list)
> +		nuke(loop_ep, -ESHUTDOWN);
> +	spin_unlock_irqrestore(&udc_controller->lock, flags);
> +
> +	/* unbind gadget and unhook driver. */
> +	driver->unbind(&udc_controller->gadget);
> +	udc_controller->gadget.dev.driver = NULL;
> +	udc_controller->driver = NULL;
> +
> +	dev_info(udc_controller->dev, "unregistered gadget driver '%s'\r\n",
> +			driver->driver.name);
> +	return 0;
> +}
> +EXPORT_SYMBOL(usb_gadget_unregister_driver);
> +
> +/* udc structure's alloc and setup, include ep-param alloc */
> +static void *qe_udc_config(struct of_device *ofdev)
> +{
> +	struct qe_udc *udc = NULL;
> +	struct device_node *np = ofdev->node;
> +	unsigned int tmp_addr = 0;
> +	struct usb_device_para __iomem *usbpram = NULL;
> +	unsigned int i;
> +	u64 size;
> +	u32 offset;
> +	unsigned int nsize;
> +	const unsigned int *prop;
> +
> +	udc = kzalloc(sizeof(struct qe_udc), GFP_KERNEL);

sizeof(*udc)

> +	if (udc == NULL) {
> +		dev_err(&ofdev->dev, "malloc udc failed\n");
> +		goto cleanup;
> +	}
> +
> +	udc->dev = &ofdev->dev;
> +
> +	/* use the default address for the usb parameter */
> +	prop = of_get_property(np, "reg", &nsize);
> +	offset = of_read_number(prop + 2, 1);
> +	size = of_read_number(prop + 3, 1);
> +
> +	udc->usb_param = muram_addr(offset);
> +
> +	memset_io(udc->usb_param, 0, size);
> +
> +	usbpram = udc->usb_param;
> +
> +	out_be16(&usbpram->frame_n, 0);
> +	out_be32(&usbpram->rstate, 0);
> +
> +	tmp_addr = qe_muram_alloc((USB_MAX_ENDPOINTS *
> +					sizeof(struct usb_ep_para)),
> +					   USB_EP_PARA_ALIGNMENT);
> +
> +	for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
> +		usbpram->epptr[i] = (u16)tmp_addr;
> +		udc->ep_param[i] = (struct usb_ep_para *)muram_addr(tmp_addr);
> +		tmp_addr += 32;
> +	}
> +
> +	memset_io(udc->ep_param[0], 0,
> +			USB_MAX_ENDPOINTS * sizeof(struct usb_ep_para));
> +
> +	udc->resume_state = USB_STATE_NOTATTACHED;
> +	udc->usb_state = USB_STATE_POWERED;
> +	udc->ep0_dir = 0;
> +
> +	/* initliaze the qe_udc lock */
> +	spin_lock_init(&udc->lock);
> +	return udc;
> +
> +cleanup:
> +	kfree(udc);
> +	return NULL;
> +}
> +
> +/* USB Controller register init */
> +static int qe_udc_reg_init(struct qe_udc *udc)
> +{
> +	struct usb_ctlr __iomem *qe_usbregs;
> +	qe_usbregs = udc->usb_regs;
> +
> +	/* Init the usb register */
> +	out_8(&qe_usbregs->usb_usmod, 0x01); /* FIXME if need enable in here */
> +	out_be16(&qe_usbregs->usb_usbmr, 0);
> +	out_8(&qe_usbregs->usb_uscom, 0);
> +	out_be16(&qe_usbregs->usb_usber, USBER_ALL_CLEAR);
> +
> +	return 0;
> +}
> +
> +static int qe_ep_config(struct qe_udc *udc, unsigned char pipe_num)
> +{
> +	struct qe_ep *ep = &(udc->eps[pipe_num]);
> +
> +	ep->udc = udc;
> +	strcpy(ep->name, ep_name[pipe_num]);
> +	ep->ep.name = ep_name[pipe_num];
> +
> +	ep->ep.ops = &qe_ep_ops;
> +	ep->stopped = 1;
> +	ep->ep.maxpacket = (unsigned short) ~0;
> +	ep->desc = NULL;
> +	ep->dir = 0xff;
> +	ep->epnum = (u8)pipe_num;
> +	ep->sent = 0;
> +	ep->last = 0;
> +	ep->init = 0;
> +	ep->rxframe = NULL;
> +	ep->txframe = NULL;
> +	ep->tx_req = NULL;
> +	ep->state = EP_STATE_IDLE;
> +	ep->has_data = 0;
> +
> +	/* the queue lists any req for this ep */
> +	INIT_LIST_HEAD(&ep->queue);
> +
> +	/* gagdet.ep_list used for ep_autoconfig so no ep0*/
> +	if (pipe_num != 0)
> +		list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
> +
> +	ep->gadget = &udc->gadget;
> +
> +	return 0;
> +}
> +
> +/*-----------------------------------------------------------------------
> + *	UDC device Driver operation functions				*
> + *----------------------------------------------------------------------*/
> +static void qe_udc_release(struct device *dev)
> +{
> +	int i = 0;
> +
> +	complete(udc_controller->done);
> +	qe_muram_free(muram_offset(&udc_controller->ep_param[0]));
> +	for (i = 0; i < USB_MAX_ENDPOINTS; i++)
> +		udc_controller->ep_param[i] = NULL;
> +
> +	kfree(udc_controller);
> +	udc_controller = NULL;
> +}
> +
> +/* Driver probe functions */
> +static int qe_udc_probe(struct of_device *ofdev,
> +			const struct of_device_id *match)
> +{
> +	struct device_node *np = ofdev->node;
> +	unsigned int tmp_status = -ENODEV;
> +	unsigned int i;
> +
> +	/* Initialize the udc structure including QH member and other member */
> +	udc_controller = (struct qe_udc *)qe_udc_config(ofdev);

You don't need the cast here. But also it is good idea to change
return type of qe_udc_config() from void * to struct qe_udc *.

> +	if (!udc_controller) {
> +		VDBG("udc_controll is NULL");
> +		return -ENOMEM;
> +	}
> +
> +	udc_controller->usb_regs = of_iomap(np, 0);

Can't fail?

> +
> +	/* initialize usb hw reg except for regs for EP,
> +	 * leave usbintr reg untouched*/
> +	qe_udc_reg_init(udc_controller);
> +
> +	/* here comes the stand operations for probe
> +	 * set the qe_udc->gadget.xxx */
> +	udc_controller->gadget.ops = &qe_gadget_ops;
> +
> +	/* gadget.ep0 is a pointer */
> +	udc_controller->gadget.ep0 = &(udc_controller->eps[0].ep);

Parentheses.

> +
> +	INIT_LIST_HEAD(&udc_controller->gadget.ep_list);
> +
> +	/* modify in register gadget process */
> +	udc_controller->gadget.speed = USB_SPEED_UNKNOWN;
> +
> +	/* name: Identifies the controller hardware type. */
> +	udc_controller->gadget.name = driver_name;
> +
> +	device_initialize(&udc_controller->gadget.dev);
> +
> +	strcpy(udc_controller->gadget.dev.bus_id, "gadget");
> +
> +	udc_controller->gadget.dev.release = qe_udc_release;
> +	udc_controller->gadget.dev.parent = &ofdev->dev;
> +
> +
> +	/* EP:intialization qe_ep struct */
> +	for (i = 0; i < USB_MAX_ENDPOINTS ; i++) {
> +		/*because the ep type isn't decide here so
> +		 * qe_ep_init() should be called in ep_enable() */
> +
> +		/* setup the qe_ep struct and link ep.ep.list
> +		 * into gadget.ep_list */
> +		qe_ep_config(udc_controller, (unsigned char)i);
> +	}
> +
> +	/* ep0 initialization in here */
> +	qe_ep_init(udc_controller, 0, &qe_ep0_desc);
> +
> +	/* create a buf for ZLP send */
> +	udc_controller->nullbuf = kzalloc(256, GFP_KERNEL);
> +	if (udc_controller->nullbuf == NULL) {
> +		VDBG("cannot alloc nullbuf");
> +		return -ENOMEM;
> +	}
> +
> +	udc_controller->nullp = virt_to_phys((void *)udc_controller->nullbuf);
> +	if (udc_controller->nullp == DMA_ADDR_INVALID) {
> +		udc_controller->nullp = dma_map_single(
> +					udc_controller->gadget.dev.parent,
> +					udc_controller->nullbuf,
> +					256,
> +					DMA_TO_DEVICE);
> +		udc_controller->nullmap = 1;
> +	} else {
> +		dma_sync_single_for_device(udc_controller->gadget.dev.parent,
> +					udc_controller->nullp, 256,
> +					DMA_TO_DEVICE);
> +		udc_controller->nullmap = 1;
> +	}
> +
> +	tasklet_init(&udc_controller->rx_tasklet, ep_rx_tasklet,
> +			(unsigned long)udc_controller);
> +	/* request irq and disable DR  */
> +	udc_controller->usb_irq = irq_of_parse_and_map(np, 0);
> +
> +	tmp_status = request_irq(udc_controller->usb_irq, qe_udc_irq, 0,
> +				driver_name, udc_controller);
> +	if (tmp_status != 0) {
> +		dev_err(udc_controller->dev, "cannot request irq %d err %d \n",
> +			udc_controller->usb_irq, tmp_status);
> +		return tmp_status;
> +	}
> +
> +	tmp_status = device_add(&udc_controller->gadget.dev);
> +	if (tmp_status != 0)
> +		return tmp_status;
> +
> +	dev_info(udc_controller->dev,
> +			"QE/CPM USB controller initialized as device\n");
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int qe_udc_suspend(struct of_device *dev, pm_message_t state)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static int qe_udc_resume(struct of_device *dev)
> +{
> +	return -ENOTSUPP;
> +}
> +#endif
> +
> +static int qe_udc_remove(struct of_device *ofdev)
> +{
> +	struct qe_ep *ep = NULL;
> +	unsigned int size;
> +
> +	DECLARE_COMPLETION(done);
> +
> +	if (!udc_controller)
> +		return -ENODEV;
> +
> +	udc_controller->done = &done;
> +	tasklet_disable(&udc_controller->rx_tasklet);
> +
> +	if (udc_controller->nullmap) {
> +		dma_unmap_single(udc_controller->gadget.dev.parent,
> +			udc_controller->nullp, 256,
> +				DMA_TO_DEVICE);
> +			udc_controller->nullp = DMA_ADDR_INVALID;
> +	} else
> +		dma_sync_single_for_cpu(udc_controller->gadget.dev.parent,
> +			udc_controller->nullp, 256,
> +				DMA_TO_DEVICE);
> +	kfree(udc_controller->nullbuf);
> +
> +	ep = &(udc_controller->eps[0]);
> +	qe_muram_free(muram_offset(ep->rxbase));
> +	size = (ep->ep.maxpacket + USB_CRC_SIZE + 2)*(USB_BDRING_LEN + 1);
> +
> +	kfree(ep->rxframe);
> +	if (ep->rxbufmap) {
> +		dma_unmap_single(udc_controller->gadget.dev.parent,
> +				ep->rxbuf_d, size,
> +				DMA_FROM_DEVICE);
> +		ep->rxbuf_d = DMA_ADDR_INVALID;
> +	} else
> +		dma_sync_single_for_cpu(udc_controller->gadget.dev.parent,
> +				ep->rxbuf_d, size,
> +				DMA_FROM_DEVICE);
> +
> +	kfree(ep->rxbuffer);
> +	kfree(ep->txframe);
> +
> +	/* free irq */
> +	free_irq(udc_controller->usb_irq, udc_controller);
> +
> +	tasklet_kill(&udc_controller->rx_tasklet);
> +
> +	device_unregister(&udc_controller->gadget.dev);
> +	if (udc_controller != NULL)
> +		kfree(udc_controller);
> +
> +	/* free udc --wait for the release() finished */
> +	wait_for_completion(&done);
> +
> +	return 0;
> +}
> +
> +/*-------------------------------------------------------------------------*/
> +static struct of_device_id qe_udc_match[] = {
> +	{
> +		.compatible = "fsl,qe_udc",
> +	},
> +	{},
> +};
> +
> +MODULE_DEVICE_TABLE(of, qe_udc_match);
> +
> +static struct of_platform_driver udc_driver = {
> +	.name           = (char *) driver_name,
> +	.match_table    = qe_udc_match,
> +	.probe          = qe_udc_probe,
> +	.remove         = qe_udc_remove,
> +#ifdef CONFIG_PM
> +	.suspend        = qe_udc_suspend,
> +	.resume         = qe_udc_resume,
> +#endif
> +};
> +
> +static int __init qe_udc_init(void)
> +{
> +	printk(KERN_INFO "%s: %s, %s\n", driver_name, driver_desc,
> +			DRIVER_VERSION);
> +	return of_register_platform_driver(&udc_driver);
> +}
> +
> +static void __exit qe_udc_exit(void)
> +{
> +	of_unregister_platform_driver(&udc_driver);
> +}
> +
> +module_init(qe_udc_init);
> +module_exit(qe_udc_exit);
> +
> +MODULE_DESCRIPTION(DRIVER_DESC);
> +MODULE_AUTHOR(DRIVER_AUTHOR);
> +MODULE_LICENSE("GPL");
> +
> diff --git a/drivers/usb/gadget/fsl_qe_udc.h b/drivers/usb/gadget/fsl_qe_udc.h
> new file mode 100644
> index 0000000..8b333f9
> --- /dev/null
> +++ b/drivers/usb/gadget/fsl_qe_udc.h
> @@ -0,0 +1,458 @@
> +/*
> + * drivers/usb/gadget/qe_udc.h
> + *
> + * Copyright (C) 2006-2008 Freescale Semiconductor, Inc. All rights reserved.
> + *
> + * Author: Xiaobo Xie <X.Xie@freescale.com>
> + *
> + * Description:
> + * Freescale USB device/endpoint management registers
> + *
> + * 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.
> + */
> +
> +#ifndef __FSL_QE_UDC_H
> +#define __FSL_QE_UDC_H
> +
> +#ifdef CONFIG_CPM2
> +extern unsigned long cpm2_immr;
> +
> +static inline void __iomem *muram_addr(unsigned long offset)
> +{
> +	return (void __iomem *)cpm2_immr + offset;
> +}
> +
> +static inline unsigned long muram_offset(void __iomem *addr)
> +{
> +	return addr - (void __iomem *)cpm2_immr;
> +}
> +#else
> +#define muram_addr	qe_muram_addr
> +#define muram_offset	qe_muram_offset
> +#endif

In the recent kernels we have both cpm_muram_addr and cpm_muram_offset.
No need to duplicate them.

> +
> +#define USB_MAX_ENDPOINTS               4
> +#define USB_MAX_PIPES                   USB_MAX_ENDPOINTS
> +#define USB_EP0_MAX_SIZE		64
> +#define USB_MAX_CTRL_PAYLOAD            0x4000
> +#define USB_BDRING_LEN			16
> +#define USB_BDRING_LEN_RX		256
> +#define USB_BDRING_LEN_TX		16
> +#define MIN_EMPTY_BDS			128
> +#define MAX_DATA_BDS			8
> +#define USB_CRC_SIZE			2
> +#define USB_DIR_BOTH			0x88
> +#define R_BUF_MAXSIZE			0x800
> +#define USB_EP_PARA_ALIGNMENT		32
> +
> +/* USB Mode Register bit define */
> +#define USB_MODE_EN		0x01
> +#define USB_MODE_HOST		0x02
> +#define USB_MODE_TEST		0x04
> +#define USB_MODE_SFTE		0x08
> +#define USB_MODE_RESUME		0x40
> +#define USB_MODE_LSS		0x80
> +
> +/* USB Slave Address Register Mask */
> +#define USB_SLVADDR_MASK	0x7F
> +
> +/* USB Endpoint register define */
> +#define USB_EPNUM_MASK		0xF000
> +#define USB_EPNUM_SHIFT		12
> +
> +#define USB_TRANS_MODE_SHIFT	8
> +#define USB_TRANS_CTR		0x0000
> +#define USB_TRANS_INT		0x0100
> +#define USB_TRANS_BULK		0x0200
> +#define USB_TRANS_ISO		0x0300
> +
> +#define USB_EP_MF		0x0020
> +#define USB_EP_RTE		0x0010
> +
> +#define USB_THS_SHIFT		2
> +#define USB_THS_MASK		0x000c
> +#define USB_THS_NORMAL		0x0
> +#define USB_THS_IGNORE_IN	0x0004
> +#define USB_THS_NACK		0x0008
> +#define USB_THS_STALL		0x000c
> +
> +#define USB_RHS_SHIFT   	0
> +#define USB_RHS_MASK		0x0003
> +#define USB_RHS_NORMAL  	0x0
> +#define USB_RHS_IGNORE_OUT	0x0001
> +#define USB_RHS_NACK		0x0002
> +#define USB_RHS_STALL		0x0003
> +
> +#define USB_RTHS_MASK		0x000f
> +
> +/* USB Command Register define */
> +#define USB_CMD_STR_FIFO	0x80
> +#define USB_CMD_FLUSH_FIFO	0x40
> +#define USB_CMD_ISFT		0x20
> +#define USB_CMD_DSFT		0x10
> +#define USB_CMD_EP_MASK		0x03
> +
> +/* USB Event and Mask Register define */
> +#define USB_E_MSF_MASK		0x0800
> +#define USB_E_SFT_MASK		0x0400
> +#define USB_E_RESET_MASK	0x0200
> +#define USB_E_IDLE_MASK		0x0100
> +#define USB_E_TXE4_MASK		0x0080
> +#define USB_E_TXE3_MASK		0x0040
> +#define USB_E_TXE2_MASK		0x0020
> +#define USB_E_TXE1_MASK		0x0010
> +#define USB_E_SOF_MASK		0x0008
> +#define USB_E_BSY_MASK		0x0004
> +#define USB_E_TXB_MASK		0x0002
> +#define USB_E_RXB_MASK		0x0001
> +#define USBER_ALL_CLEAR 	0x0fff
> +
> +#define USB_E_DEFAULT_DEVICE   (USB_E_RESET_MASK | USB_E_TXE4_MASK | \
> +				USB_E_TXE3_MASK | USB_E_TXE2_MASK | \
> +				USB_E_TXE1_MASK | USB_E_BSY_MASK | \
> +				USB_E_TXB_MASK | USB_E_RXB_MASK)
> +
> +#define USB_E_TXE_MASK         (USB_E_TXE4_MASK | USB_E_TXE3_MASK|\
> +				 USB_E_TXE2_MASK | USB_E_TXE1_MASK)
> +/* USB Status Register define */
> +#define USB_IDLE_STATUS_MASK	0x01
> +
> +/* USB Start of Frame Timer */
> +#define USB_USSFT_MASK		0x3FFF
> +
> +/* USB Frame Number Register */
> +#define USB_USFRN_MASK		0xFFFF
> +
> +struct usb_device_para{
> +	u16	epptr[4];
> +	u32	rstate;
> +	u32	rptr;
> +	u16	frame_n;
> +	u16	rbcnt;
> +	u32	rtemp;
> +	u32	rxusb_data;
> +	u16	rxuptr;
> +	u8	reso[2];
> +	u32	softbl;
> +	u8	sofucrctemp;
> +};
> +
> +struct usb_ep_para{
> +	u16	rbase;
> +	u16	tbase;
> +	u8	rbmr;
> +	u8	tbmr;
> +	u16	mrblr;
> +	u16	rbptr;
> +	u16	tbptr;
> +	u32	tstate;
> +	u32	tptr;
> +	u16	tcrc;
> +	u16	tbcnt;
> +	u32	ttemp;
> +	u16	txusbu_ptr;
> +	u8	reserve[2];
> +};
> +
> +#define USB_BUSMODE_GBL		0x20
> +#define USB_BUSMODE_BO_MASK	0x18
> +#define USB_BUSMODE_BO_SHIFT	0x3
> +#define USB_BUSMODE_BE		0x2
> +#define USB_BUSMODE_CETM	0x04
> +#define USB_BUSMODE_DTB		0x02
> +
> +/* Endpoint basic handle */
> +#define ep_index(EP)		((EP)->desc->bEndpointAddress&0xF)

Missing spaces around &, also..

> +#define ep_maxpacket(EP)	((EP)->ep.maxpacket)
> +#define ep_is_in(EP)	((ep_index(EP) == 0) ? (EP->udc->ep0_dir == \
> +			USB_DIR_IN):((EP)->desc->bEndpointAddress \
> +			& USB_DIR_IN) == USB_DIR_IN)

I'd turn this into static inlines. But since there are no casts,
this seems to be fine...

> +
> +/* ep0 transfer state */
> +#define WAIT_FOR_SETUP          0
> +#define DATA_STATE_XMIT         1
> +#define DATA_STATE_NEED_ZLP     2
> +#define WAIT_FOR_OUT_STATUS     3
> +#define DATA_STATE_RECV         4
> +
> +/* ep tramsfer mode */
> +#define USBP_TM_CTL	0
> +#define USBP_TM_ISO	1
> +#define USBP_TM_BULK	2
> +#define USBP_TM_INT	3
> +
> +/*-----------------------------------------------------------------------------
> +	USB RX And TX DATA Frame
> + -----------------------------------------------------------------------------*/
> +struct qe_frame{
> +	u8 *data;
> +	u32 len;
> +	u32 status;
> +	u32 info;
> +
> +	void *privdata;
> +	struct list_head node;
> +};
> +
> +/* Frame structure, info field. */
> +#define PID_DATA0              0x80000000 /* Data toggle zero */
> +#define PID_DATA1              0x40000000 /* Data toggle one  */
> +#define PID_SETUP              0x20000000 /* setup bit */
> +#define SETUP_STATUS           0x10000000 /* setup status bit */
> +#define SETADDR_STATUS         0x08000000 /* setupup address status bit */
> +#define NO_REQ                 0x04000000 /* Frame without request */
> +#define HOST_DATA              0x02000000 /* Host data frame */
> +#define FIRST_PACKET_IN_FRAME  0x01000000 /* first packet in the frame */
> +#define TOKEN_FRAME            0x00800000 /* Host token frame */
> +#define ZLP                    0x00400000 /* Zero length packet */
> +#define IN_TOKEN_FRAME         0x00200000 /* In token package */
> +#define OUT_TOKEN_FRAME        0x00100000 /* Out token package */
> +#define SETUP_TOKEN_FRAME      0x00080000 /* Setup token package */
> +#define STALL_FRAME            0x00040000 /* Stall handshake */
> +#define NACK_FRAME             0x00020000 /* Nack handshake */
> +#define NO_PID                 0x00010000 /* No send PID */
> +#define NO_CRC                 0x00008000 /* No send CRC */
> +#define HOST_COMMAND           0x00004000 /* Host command frame   */
> +
> +/* Frame status field */
> +/* Receive side */
> +#define FRAME_OK               0x00000000 /* Frame tranmitted or received OK */
> +#define FRAME_ERROR            0x80000000 /* Error occured on frame */
> +#define START_FRAME_LOST       0x40000000 /* START_FRAME_LOST */
> +#define END_FRAME_LOST         0x20000000 /* END_FRAME_LOST */
> +#define RX_ER_NONOCT           0x10000000 /* Rx Non Octet Aligned Packet */
> +#define RX_ER_BITSTUFF         0x08000000 /* Frame Aborted --Received packet
> +						with bit stuff error */
> +#define RX_ER_CRC              0x04000000 /* Received packet with CRC error */
> +#define RX_ER_OVERUN           0x02000000 /* Over-run occured on reception */
> +#define RX_ER_PID              0x01000000 /* Wrong PID received */
> +/* Tranmit side */
> +#define TX_ER_NAK              0x00800000 /* Received NAK handshake */
> +#define TX_ER_STALL            0x00400000 /* Received STALL handshake */
> +#define TX_ER_TIMEOUT          0x00200000 /* Transmit time out */
> +#define TX_ER_UNDERUN          0x00100000 /* Transmit underrun */
> +#define FRAME_INPROGRESS       0x00080000 /* Frame is being transmitted */
> +#define ER_DATA_UNDERUN        0x00040000 /* Frame is shorter then expected */
> +#define ER_DATA_OVERUN         0x00020000 /* Frame is longer then expected */
> +
> +/* QE USB frame operation functions */
> +#define frame_get_length(frm) (((struct qe_frame *)frm)->len)
> +#define frame_set_length(frm, leng) (((struct qe_frame *)frm)->len = leng)
> +#define frame_get_data(frm) (((struct qe_frame *)frm)->data)
> +#define frame_set_data(frm, dat) (((struct qe_frame *)frm)->data = dat)
> +#define frame_get_info(frm) (((struct qe_frame *)frm)->info)
> +#define frame_set_info(frm, inf) (((struct qe_frame *)frm)->info = inf)
> +#define frame_get_status(frm) (((struct qe_frame *)frm)->status)
> +#define frame_set_status(frm, stat) (((struct qe_frame *)frm)->status = stat)
> +#define frame_get_privdata(frm) (((struct qe_frame *)frm)->privdata)
> +#define frame_set_privdata(frm, dat) (((struct qe_frame *)frm)->privdata = dat)

Please turn these macros into `static inline' functions. The benefit of
using functions is type checking.

> +
> +#define qe_frame_clean(frm)                     \
> +	do {                                  \
> +		frame_set_data(frm, NULL);         \
> +		frame_set_length(frm, 0);          \
> +		frame_set_status(frm, FRAME_OK);   \
> +		frame_set_info(frm, 0);            \
> +		frame_set_privdata(frm, 0);        \
> +	} while (0)
> +
> +#define qe_frame_init(frm)                                   \
> +	do {                                               \
> +		qe_frame_clean(frm);                         \
> +		INIT_LIST_HEAD(&(((struct qe_frame *)frm)->node)); \
> +	} while (0)

Ditto.

> +struct qe_req {
> +	struct usb_request req;
> +	struct list_head queue;
> +	/* ep_queue() func will add
> +	 a request->queue into a udc_ep->queue 'd tail */
> +	struct qe_ep *ep;
> +	unsigned mapped:1;
> +};
> +
> +struct qe_ep {
> +	struct usb_ep ep;
> +	struct list_head queue;
> +	struct qe_udc *udc;
> +	const struct usb_endpoint_descriptor *desc;
> +	struct usb_gadget *gadget;
> +
> +	u8 state;
> +
> +	struct qe_bd *rxbase;
> +	struct qe_bd *n_rxbd;
> +	struct qe_bd *e_rxbd;
> +
> +	struct qe_bd *txbase;
> +	struct qe_bd *n_txbd;
> +	struct qe_bd *c_txbd;
> +
> +	struct qe_frame *rxframe;
> +	u8 *rxbuffer;
> +	dma_addr_t rxbuf_d;
> +	u8 rxbufmap;
> +	unsigned char localnack;
> +	int has_data;
> +
> +	struct qe_frame *txframe;
> +	struct qe_req *tx_req;
> +	int sent;  /*data already sent */
> +	int last;  /*data sent in the last time*/
> +
> +	u8 dir;
> +	u8 epnum;
> +	u8 tm; /* transfer mode */
> +	u8 data01;
> +	u8 init;
> +
> +	u8 already_seen;
> +	u8 enable_tasklet;
> +	u8 setup_stage;
> +	u32 last_io;            /* timestamp */
> +
> +	char name[14];
> +
> +	unsigned double_buf:1;
> +	unsigned stopped:1;
> +	unsigned fnf:1;
> +	unsigned has_dma:1;
> +
> +	u8 ackwait;
> +	u8 dma_channel;
> +	u16 dma_counter;
> +	int lch;
> +
> +	struct timer_list timer;
> +};
> +
> +struct qe_udc {
> +	struct usb_gadget gadget;
> +	struct usb_gadget_driver *driver;
> +	struct device *dev;
> +	struct qe_ep eps[USB_MAX_ENDPOINTS];
> +	struct usb_ctrlrequest local_setup_buff;
> +	spinlock_t lock;	/* lock for set/config qe_udc */
> +
> +	struct qe_req *status_req;     /* ep0 status request */
> +
> +	/* USB and EP Parameter Block pointer */
> +	struct usb_device_para __iomem *usb_param;
> +	struct usb_ep_para __iomem *ep_param[4];
> +
> +	u32 max_pipes;          /* Device max pipes */
> +	u32 max_use_endpts;     /* Max endpointes to be used */
> +	u32 bus_reset;          /* Device is bus reseting */
> +	u32 resume_state;       /* USB state to resume*/
> +	u32 usb_state;          /* USB current state */
> +	u32 usb_next_state;     /* USB next state */
> +	u32 ep0_state;          /* Enpoint zero state */
> +	u32 ep0_dir;            /* Enpoint zero direction: can be
> +				USB_DIR_IN or USB_DIR_OUT*/
> +	u32 usb_sof_count;      /* SOF count */
> +	u32 errors;             /* USB ERRORs count */
> +
> +	u8 *tmpbuf;
> +	u32 c_start;
> +	u32 c_end;
> +
> +	u8 *nullbuf;
> +	dma_addr_t nullp;
> +	u8 nullmap;
> +	u8 device_address;	/* Device USB address */
> +
> +	unsigned int usb_clock;
> +	unsigned int usb_irq;
> +	struct usb_ctlr __iomem *usb_regs;
> +
> +	struct tasklet_struct rx_tasklet;
> +
> +	struct completion *done;	/* to make sure release() is done */
> +};
> +
> +#define EP_STATE_IDLE	0
> +#define EP_STATE_NACK	1
> +#define EP_STATE_STALL	2
> +
> +/*---------------------------------------------------------------------
> + * 		Mask definitions for usb BD                           *
> + *--------------------------------------------------------------------*/
> +#define QE_SIZEOF_BD       sizeof(struct qe_bd)
> +
> +#define BD_BUFFER_ARG(bd)                   (((struct qe_bd *)bd)->buf)
> +#define BD_BUFFER_CLEAR(bd)                 out_be32(&(BD_BUFFER_ARG(bd)), 0);
> +#define BD_BUFFER(bd)                       in_be32(&(BD_BUFFER_ARG(bd)))
> +#define BD_STATUS_AND_LENGTH_SET(bd, val)   out_be32((u32 *)bd, val)
> +#define BD_STATUS_AND_LENGTH(bd)            in_be32((u32 *)bd)
> +#define BD_BUFFER_SET(bd, buffer)           out_be32(&(BD_BUFFER_ARG(bd)), \
> +							(u32)(buffer))
> +
> +/*
> + * transmit BD's status
> + */
> +#define T_R           0x80000000         /* ready bit */
> +#define T_W           0x20000000         /* wrap bit */
> +#define T_I           0x10000000         /* interrupt on completion */
> +#define T_L           0x08000000         /* last */
> +#define T_TC          0x04000000         /* transmit CRC */
> +#define T_CNF         0x02000000         /* wait for  transmit confirm */
> +#define T_LSP         0x01000000         /* Low-speed transaction */
> +#define T_PID         0x00c00000         /* packet id */
> +#define T_NAK         0x00100000         /* No ack. */
> +#define T_STAL        0x00080000         /* Stall recieved */
> +#define T_TO          0x00040000         /* time out */
> +#define T_UN          0x00020000         /* underrun */
> +
> +#define DEVICE_T_ERROR    (T_UN | T_TO)
> +#define HOST_T_ERROR      (T_UN | T_TO | T_NAK | T_STAL)
> +#define DEVICE_T_BD_MASK  DEVICE_T_ERROR
> +#define HOST_T_BD_MASK    HOST_T_ERROR
> +
> +#define T_PID_SHIFT   6
> +#define T_PID_DATA0   0x00800000         /* Data 0 toggle */
> +#define T_PID_DATA1   0x00c00000         /* Data 1 toggle */
> +
> +/*
> + * receive BD's status
> + */
> +#define R_E           0x80000000         /* buffer empty */
> +#define R_W           0x20000000         /* wrap bit */
> +#define R_I           0x10000000         /* interrupt on reception */
> +#define R_L           0x08000000         /* last */
> +#define R_F           0x04000000         /* first */
> +#define R_PID         0x00c00000         /* packet id */
> +#define R_NO          0x00100000         /* Rx Non Octet Aligned Packet */
> +#define R_AB          0x00080000         /* Frame Aborted */
> +#define R_CR          0x00040000         /* CRC Error */
> +#define R_OV          0x00020000         /* Overrun */
> +
> +#define R_ERROR       (R_NO | R_AB | R_CR | R_OV)
> +#define R_BD_MASK     R_ERROR
> +
> +#define R_PID_DATA0   0x00000000
> +#define R_PID_DATA1   0x00400000
> +#define R_PID_SETUP   0x00800000
> +
> +/* Bulk only class request */
> +#define USB_BULK_RESET_REQUEST          0xff
> +
> +extern int par_io_config_pin(u8  port, u8  pin, int dir, int open_drain,
> +				int assignment, int has_irq);
> +
> +#ifdef DEBUG
> +#define VDBG(fmt, args...)	printk(KERN_DEBUG "[%s]  " fmt "\n", \
> +				__func__, ## args)
> +#else
> +#define VDBG(fmt, args...)       do {} while (0)
> +#endif
> +
> +#define CPM_USB_STOP_TX 0x2e600000
> +#define CPM_USB_RESTART_TX 0x2e600000
> +#define CPM_USB_STOP_TX_OPCODE 0x0a
> +#define CPM_USB_RESTART_TX_OPCODE 0x0b
> +#define CPM_USB_EP_SHIFT 5
> +
> +#endif
> diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
> index 5246e8f..7e5be52 100644
> --- a/drivers/usb/gadget/gadget_chips.h
> +++ b/drivers/usb/gadget/gadget_chips.h
> @@ -147,6 +147,13 @@
>  #define	gadget_is_m66592(g)	0
>  #endif
>  
> +/* Freescale CPM/QE UDC SUPPORT */
> +#ifdef CONFIG_USB_GADGET_FSL_QE
> +#define gadget_is_fsl_qe(g)	!strcmp("fsl_qe_udc", (g)->name)
> +#else
> +#define gadget_is_fsl_qe(g)	0
> +#endif
> +
>  
>  // CONFIG_USB_GADGET_SX2
>  // CONFIG_USB_GADGET_AU1X00
> @@ -212,6 +219,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
>  		return 0x20;
>  	else if (gadget_is_m66592(gadget))
>  		return 0x21;
> +	else if (gadget_is_fsl_qe(gadget))
> +		return 0x22;
>  	return -ENOENT;
>  }
>  
> -- 
> 1.5.5.1.248.g4b17

Thanks,

-- 
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
@ 2008-08-06  7:16 Li Yang
  2008-08-06 15:15 ` Anton Vorontsov
                   ` (2 more replies)
  0 siblings, 3 replies; 33+ messages in thread
From: Li Yang @ 2008-08-06  7:16 UTC (permalink / raw)
  To: gregkh, david-b, linux-usb; +Cc: linuxppc-dev, Li Yang, Xie Xiaobo

Some of Freescale SoC chips have a QE or CPM co-processor which
supports full speed USB.  The driver adds device mode support
of both QE and CPM USB controller to Linux USB gadget.  The
driver is tested with MPC8360 and MPC8272, and should work with
other models having QE/CPM given minor tweaks.

Signed-off-by: Xie Xiaobo <X.Xie@freescale.com>
Signed-off-by: Li Yang <leoli@freescale.com>
---
 drivers/usb/gadget/Kconfig        |   19 +
 drivers/usb/gadget/Makefile       |    1 +
 drivers/usb/gadget/fsl_qe_udc.c   | 2729 +++++++++++++++++++++++++++++++++++++
 drivers/usb/gadget/fsl_qe_udc.h   |  458 +++++++
 drivers/usb/gadget/gadget_chips.h |    9 +
 5 files changed, 3216 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/gadget/fsl_qe_udc.c
 create mode 100644 drivers/usb/gadget/fsl_qe_udc.h

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index c6a8c6b..fba8305 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -150,6 +150,25 @@ config USB_FSL_USB2
 	default USB_GADGET
 	select USB_GADGET_SELECTED
 
+config USB_GADGET_FSL_QE
+    boolean "Freescale QE/CPM USB Device Controller"
+    help
+       Some of Freescale PowerPC processors have a Full Speed
+       QE/CPM2 USB controller, which support device mode with 4
+       programmable endpoints. This driver supports the
+       controller in the MPC8360 and MPC8272, and should work with
+       controllers having QE or CPM2, given minor tweaks.
+
+       Say "y" to link the driver statically, or "m" to build a
+       dynamically linked module called "fsl_qe_udc" and force all
+       gadget drivers to also be dynamically linked.
+
+config USB_FSL_QE
+    tristate
+    depends on USB_GADGET_FSL_QE
+    default USB_GADGET
+    select USB_GADGET_SELECTED
+
 config USB_GADGET_NET2280
 	boolean "NetChip 228x"
 	depends on PCI
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index fcb5cb9..4871554 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_USB_AT91)		+= at91_udc.o
 obj-$(CONFIG_USB_ATMEL_USBA)	+= atmel_usba_udc.o
 obj-$(CONFIG_USB_FSL_USB2)	+= fsl_usb2_udc.o
 obj-$(CONFIG_USB_M66592)	+= m66592-udc.o
+obj-$(CONFIG_USB_FSL_QE)	+= fsl_qe_udc.o
 
 #
 # USB gadget drivers
diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c
new file mode 100644
index 0000000..52790f7
--- /dev/null
+++ b/drivers/usb/gadget/fsl_qe_udc.c
@@ -0,0 +1,2729 @@
+/*
+ * driver/usb/gadget/fsl_qe_udc.c
+ *
+ * Copyright (c) 2006-2008 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Author: Xie Xiaobo <X.Xie@freescale.com>
+ * Li Yang <leoli@freescale.com>
+ * Based on bareboard code from Shlomi Gridish.
+ *
+ * Description:
+ * Freescle QE/CPM USB Pheripheral Controller Driver
+ * The controller can be found on MPC8360, MPC8272, and etc.
+ * MPC8360 Rev 1.1 may need QE mircocode update
+ *
+ * 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.
+ */
+
+#undef USB_TRACE
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/proc_fs.h>
+#include <linux/mm.h>
+#include <linux/uaccess.h>
+#include <linux/moduleparam.h>
+
+#include <linux/of_platform.h>
+
+#include <asm/qe.h>
+#include <asm/dma.h>
+#include <asm/reg.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/dma-mapping.h>
+#include "fsl_qe_udc.h"
+
+#ifdef CONFIG_CPM2
+extern int cpm_command(u32 command, u8 opcode);
+extern unsigned long cpm_muram_alloc(unsigned long size, unsigned long align);
+extern int cpm_muram_free(unsigned long offset);
+
+#define qe_muram_alloc cpm_muram_alloc
+#define qe_muram_free cpm_muram_free
+#endif
+
+#define DRIVER_DESC     "Freescale QE/CPM USB Device Controller driver"
+#define DRIVER_AUTHOR   "Xie XiaoBo"
+#define DRIVER_VERSION  "1.0"
+
+#define DMA_ADDR_INVALID        (~(dma_addr_t)0)
+
+static const char driver_name[] = "fsl_qe_udc";
+static const char driver_desc[] = DRIVER_DESC;
+
+/*ep name is important in gadget, it should obey the convention of ep_match()*/
+static const char *const ep_name[] = {
+	"ep0-control", /* everyone has ep0 */
+	/* 3 configurable endpoints */
+	"ep1",
+	"ep2",
+	"ep3",
+};
+
+static struct usb_endpoint_descriptor
+qe_ep0_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	0,
+	.bmAttributes =		USB_ENDPOINT_XFER_CONTROL,
+	.wMaxPacketSize =	USB_MAX_CTRL_PAYLOAD,
+};
+
+/* it is initialized in probe()  */
+static struct qe_udc *udc_controller;
+
+/********************************************************************
+ *      Internal Used Function Start
+********************************************************************/
+/*-----------------------------------------------------------------
+ * done() - retire a request; caller blocked irqs
+ *--------------------------------------------------------------*/
+static void done(struct qe_ep *ep, struct qe_req *req, int status)
+{
+	struct qe_udc *udc = NULL;
+	unsigned char stopped = ep->stopped;
+	udc = (struct qe_udc *) ep->udc;
+
+	/* the req->queue pointer is used by ep_queue() func, in which
+	* the request will be added into a udc_ep->queue 'd tail
+	* so here the req will be dropped from the ep->queue
+	*/
+	list_del_init(&req->queue);
+
+	/* req.status should be set as -EINPROGRESS in ep_queue() */
+	if (req->req.status == -EINPROGRESS)
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	if (req->mapped) {
+		dma_unmap_single(ep->udc->gadget.dev.parent,
+			req->req.dma, req->req.length,
+			ep_is_in(ep)
+				? DMA_TO_DEVICE
+				: DMA_FROM_DEVICE);
+		req->req.dma = DMA_ADDR_INVALID;
+		req->mapped = 0;
+	} else
+		dma_sync_single_for_cpu(ep->udc->gadget.dev.parent,
+			req->req.dma, req->req.length,
+			ep_is_in(ep)
+				? DMA_TO_DEVICE
+				: DMA_FROM_DEVICE);
+
+	if (status && (status != -ESHUTDOWN))
+		VDBG("complete %s req %p stat %d len %u/%u",
+			ep->ep.name, &req->req, status,
+			req->req.actual, req->req.length);
+
+	/* don't modify queue heads during completion callback */
+	ep->stopped = 1;
+	spin_unlock(&ep->udc->lock);
+
+	/* this complete() should a func implemented by gadget layer,
+	* eg fsg->bulk_in_complete() */
+	if (req->req.complete)
+		req->req.complete(&ep->ep, &req->req);
+
+	spin_lock(&ep->udc->lock);
+
+	ep->stopped = stopped;
+}
+
+/*-----------------------------------------------------------------
+ * nuke(): delete all requests related to this ep
+ *--------------------------------------------------------------*/
+static void nuke(struct qe_ep *ep, int status)
+{
+	/* Whether this eq has request linked */
+	while (!list_empty(&ep->queue)) {
+		struct qe_req *req = NULL;
+		req = list_entry(ep->queue.next, struct qe_req, queue);
+
+		done(ep, req, status);
+	}
+}
+
+/*---------------------------------------------------------------------------*
+ * USB and Endpoint manipulate process, include parameter and register       *
+ *---------------------------------------------------------------------------*/
+/* @value: 1--set stall 0--clean stall */
+static int qe_eprx_stall_change(struct qe_ep *ep, int value)
+{
+	u16 tem_usep;
+	u8 epnum = ep->epnum;
+	struct qe_udc *udc = ep->udc;
+
+	tem_usep = in_be16(&udc->usb_regs->usb_usep[epnum]);
+	tem_usep = tem_usep & ~USB_RHS_MASK;
+	if (value == 1)
+		tem_usep |= USB_RHS_STALL;
+	else if (ep->dir == USB_DIR_IN)
+		tem_usep |= USB_RHS_IGNORE_OUT;
+
+	out_be16(&udc->usb_regs->usb_usep[epnum], tem_usep);
+	return 0;
+}
+
+static int qe_eptx_stall_change(struct qe_ep *ep, int value)
+{
+	u16 tem_usep;
+	u8 epnum = ep->epnum;
+	struct qe_udc *udc = ep->udc;
+
+	tem_usep = in_be16(&udc->usb_regs->usb_usep[epnum]);
+	tem_usep = tem_usep & ~USB_THS_MASK;
+	if (value == 1)
+		tem_usep |= USB_THS_STALL;
+	else if (ep->dir == USB_DIR_OUT)
+		tem_usep |= USB_THS_IGNORE_IN;
+
+	out_be16(&udc->usb_regs->usb_usep[epnum], tem_usep);
+
+	return 0;
+}
+
+static int qe_ep0_stall(struct qe_udc *udc)
+{
+	qe_eptx_stall_change(&udc->eps[0], 1);
+	qe_eprx_stall_change(&udc->eps[0], 1);
+	udc_controller->ep0_state = WAIT_FOR_SETUP;
+	udc_controller->ep0_dir = 0;
+	return 0;
+}
+
+static int qe_eprx_nack(struct qe_ep *ep)
+{
+	u8 epnum = ep->epnum;
+	struct qe_udc *udc = ep->udc;
+
+	if (ep->state == EP_STATE_IDLE) {
+		/* Set the ep's nack */
+		clrsetbits_be16(&udc->usb_regs->usb_usep[epnum],
+				USB_RHS_MASK, USB_RHS_NACK);
+
+		/* Mask Rx and Busy interrupts */
+		clrbits16(&udc->usb_regs->usb_usbmr,
+				(USB_E_RXB_MASK | USB_E_BSY_MASK));
+
+		ep->state = EP_STATE_NACK;
+	}
+	return 0;
+}
+
+static int qe_eprx_normal(struct qe_ep *ep)
+{
+	struct qe_udc *udc = ep->udc;
+
+	if (ep->state == EP_STATE_NACK) {
+		clrsetbits_be16(&udc->usb_regs->usb_usep[ep->epnum],
+				USB_RTHS_MASK, USB_THS_IGNORE_IN);
+
+		/* Unmask RX interrupts */
+		out_be16(&udc->usb_regs->usb_usber,
+				USB_E_BSY_MASK|USB_E_RXB_MASK);
+		setbits16(&udc->usb_regs->usb_usbmr,
+				(USB_E_RXB_MASK | USB_E_BSY_MASK));
+
+		ep->state = EP_STATE_IDLE;
+		ep->has_data = 0;
+	}
+
+	return 0;
+}
+
+static int qe_ep_cmd_stoptx(struct qe_ep *ep)
+{
+	u8 ep_num;
+#ifdef CONFIG_CPM2
+	u32 command;
+	u8 opcode;
+
+	ep_num = ep->epnum << CPM_USB_EP_SHIFT;
+	command = CPM_USB_STOP_TX | (u32)ep_num;
+	opcode = CPM_USB_STOP_TX_OPCODE;
+	cpm_command(command, opcode);
+#else
+	ep_num = ep->epnum;
+	qe_issue_cmd(QE_USB_STOP_TX, QE_CR_SUBBLOCK_USB, ep_num, 0);
+#endif
+	return 0;
+}
+
+static int qe_ep_cmd_restarttx(struct qe_ep *ep)
+{
+	u8 ep_num;
+#ifdef CONFIG_CPM2
+	u32 command;
+	u8 opcode;
+
+	ep_num = ep->epnum << CPM_USB_EP_SHIFT;
+	command = CPM_USB_RESTART_TX | (u32)ep_num;
+	opcode = CPM_USB_RESTART_TX_OPCODE;
+	cpm_command(command, opcode);
+#else
+	ep_num = ep->epnum;
+	qe_issue_cmd(QE_USB_RESTART_TX, QE_CR_SUBBLOCK_USB, ep_num, 0);
+#endif
+	return 0;
+}
+
+static int qe_ep_flushtxfifo(struct qe_ep *ep)
+{
+	struct qe_ep *tmp_ep;
+	struct qe_udc *udc = ep->udc;
+	int i;
+
+	tmp_ep = ep;
+	i = (int)tmp_ep->epnum;
+
+	qe_ep_cmd_stoptx(tmp_ep);
+	out_8(&udc->usb_regs->usb_uscom,
+		USB_CMD_FLUSH_FIFO | (USB_CMD_EP_MASK & (tmp_ep->epnum)));
+	udc->ep_param[i]->tbptr = udc->ep_param[i]->tbase;
+	udc->ep_param[i]->tstate = 0;
+	udc->ep_param[i]->tbcnt = 0;
+
+	tmp_ep->c_txbd = tmp_ep->txbase;
+	tmp_ep->n_txbd = tmp_ep->txbase;
+	qe_ep_cmd_restarttx(tmp_ep);
+	return 0;
+}
+
+static int qe_ep_filltxfifo(struct qe_ep *ep)
+{
+	struct qe_udc *udc = ep->udc;
+
+	out_8(&udc->usb_regs->usb_uscom,
+			USB_CMD_STR_FIFO | (USB_CMD_EP_MASK & (ep->epnum)));
+	return 0;
+}
+
+static int qe_epbds_reset(struct qe_udc *udc, int pipe_num)
+{
+	struct qe_ep *ep;
+	u32 bdring_len;
+	u8 *bd;
+	int i;
+
+	ep = &(udc->eps[pipe_num]);
+
+	if (ep->dir == USB_DIR_OUT)
+		bdring_len = USB_BDRING_LEN_RX;
+	else
+		bdring_len = USB_BDRING_LEN;
+
+	bd = (u8 *)ep->rxbase;
+	for (i = 0; i < bdring_len; i++) {
+		BD_STATUS_AND_LENGTH_SET(bd, (R_E|R_I));
+		bd += QE_SIZEOF_BD;
+	}
+	bd -= QE_SIZEOF_BD;
+	BD_STATUS_AND_LENGTH_SET(bd, (R_E|R_I|R_W));
+
+	bd = (u8 *)(ep->txbase);
+	for (i = 0; i < USB_BDRING_LEN_TX; i++) {
+		BD_BUFFER_CLEAR(bd);
+		BD_STATUS_AND_LENGTH_SET(bd, 0);
+		bd += QE_SIZEOF_BD;
+	}
+	bd -= QE_SIZEOF_BD;
+	BD_STATUS_AND_LENGTH_SET(bd, T_W);
+
+	return 0;
+}
+
+static int qe_ep_reset(struct qe_udc *udc, int pipe_num)
+{
+	struct qe_ep *ep;
+	u16 tmpusep;
+
+	ep = &(udc->eps[pipe_num]);
+	tmpusep = in_be16(&udc->usb_regs->usb_usep[pipe_num]);
+	tmpusep &= ~USB_RTHS_MASK;
+
+	switch (ep->dir) {
+	case USB_DIR_BOTH:
+		qe_ep_flushtxfifo(ep);
+		break;
+	case USB_DIR_OUT:
+		tmpusep |= USB_THS_IGNORE_IN;
+		break;
+	case USB_DIR_IN:
+		qe_ep_flushtxfifo(ep);
+		tmpusep |= USB_RHS_IGNORE_OUT;
+		break;
+	default:
+		break;
+	}
+	out_be16(&udc->usb_regs->usb_usep[pipe_num], tmpusep);
+
+	qe_epbds_reset(udc, pipe_num);
+
+	return 0;
+}
+
+static int qe_ep_toggledata01(struct qe_ep *ep)
+{
+	struct qe_ep *tmpep = ep;
+
+	tmpep->data01 ^= 0x1;
+	return 0;
+}
+
+static int qe_ep_bd_init(struct qe_udc *udc, unsigned char pipe_num)
+{
+	struct qe_ep *ep = &(udc->eps[pipe_num]);
+	unsigned long tmp_addr = 0;
+	struct usb_ep_para __iomem *epparam;
+	int i;
+	u8 *bd;
+	int bdring_len;
+
+	if (ep->dir == USB_DIR_OUT)
+		bdring_len = USB_BDRING_LEN_RX;
+	else
+		bdring_len = USB_BDRING_LEN;
+
+	epparam = udc->ep_param[pipe_num];
+	/* alloc multi-ram for BD rings and set the ep parameters */
+	tmp_addr = qe_muram_alloc(QE_SIZEOF_BD * (bdring_len +
+				USB_BDRING_LEN_TX), QE_ALIGNMENT_OF_BD);
+	out_be16(&epparam->rbase, (u16)tmp_addr);
+	out_be16(&epparam->tbase, (u16)(tmp_addr +
+				(QE_SIZEOF_BD * bdring_len)));
+
+	out_be16(&epparam->rbptr, in_be16(&epparam->rbase));
+	out_be16(&epparam->tbptr, in_be16(&epparam->tbase));
+
+	ep->rxbase = (struct qe_bd *)muram_addr(tmp_addr);
+	ep->txbase = (struct qe_bd *)muram_addr(tmp_addr +
+						(QE_SIZEOF_BD * bdring_len));
+	ep->n_rxbd = ep->rxbase;
+	ep->e_rxbd = ep->rxbase;
+	ep->n_txbd = ep->txbase;
+	ep->c_txbd = ep->txbase;
+	ep->data01 = 0; /* data0 */
+
+	/* Init TX and RX bds */
+	bd = (u8 *)(ep->rxbase);
+	for (i = 0; i < bdring_len; i++) {
+		BD_BUFFER_CLEAR(bd);
+		BD_STATUS_AND_LENGTH_SET(bd, 0);
+		bd += QE_SIZEOF_BD;
+	}
+	bd -= QE_SIZEOF_BD;
+	BD_STATUS_AND_LENGTH_SET(bd, R_W);
+
+	bd = (u8 *)(ep->txbase);
+	for (i = 0; i < USB_BDRING_LEN_TX; i++) {
+		BD_BUFFER_CLEAR(bd);
+		BD_STATUS_AND_LENGTH_SET(bd, 0);
+		bd += QE_SIZEOF_BD;
+	}
+	bd -= QE_SIZEOF_BD;
+	BD_STATUS_AND_LENGTH_SET(bd, T_W);
+
+	return 0;
+}
+
+static int qe_ep_rxbd_update(struct qe_ep *ep)
+{
+	unsigned int size;
+	int i;
+	unsigned int tmp;
+	struct qe_bd *bd;
+	unsigned int bdring_len;
+
+	if (ep->rxbase == NULL)
+		return -EINVAL;
+
+	bd = ep->rxbase;
+
+	ep->rxframe = kmalloc(sizeof(struct qe_frame), GFP_KERNEL);
+	if (ep->rxframe == NULL) {
+		dev_err(ep->udc->dev, "malloc rxframe failed\n");
+		return -ENOMEM;
+	}
+
+	qe_frame_init(ep->rxframe);
+
+	if (ep->dir == USB_DIR_OUT)
+		bdring_len = USB_BDRING_LEN_RX;
+	else
+		bdring_len = USB_BDRING_LEN;
+
+	size = (ep->ep.maxpacket + USB_CRC_SIZE + 2) * (bdring_len + 1);
+	ep->rxbuffer = kzalloc(size, GFP_KERNEL);
+	if (ep->rxbuffer == NULL) {
+		dev_err(ep->udc->dev, "malloc rxbuffer failed,size=%d\n",
+				size);
+		return -ENOMEM;
+	}
+
+	ep->rxbuf_d = virt_to_phys((void *)ep->rxbuffer);
+	if (ep->rxbuf_d == DMA_ADDR_INVALID) {
+		ep->rxbuf_d = dma_map_single(udc_controller->gadget.dev.parent,
+					ep->rxbuffer,
+					size,
+					DMA_FROM_DEVICE);
+		ep->rxbufmap = 1;
+	} else {
+		dma_sync_single_for_device(udc_controller->gadget.dev.parent,
+					ep->rxbuf_d, size,
+					DMA_FROM_DEVICE);
+		ep->rxbufmap = 0;
+	}
+
+	size = ep->ep.maxpacket + USB_CRC_SIZE + 2;
+	tmp = ep->rxbuf_d;
+	tmp = (u32)(((tmp>>2)<<2) + 4);
+
+	for (i = 0; i < bdring_len; i++) {
+		BD_BUFFER_SET(bd, tmp);
+		BD_STATUS_AND_LENGTH_SET(bd, (R_E|R_I));
+		tmp = tmp + size;
+		bd++;
+	}
+	bd--;
+	BD_STATUS_AND_LENGTH_SET(bd, (R_E|R_I|R_W));
+
+	return 0;
+}
+
+static int qe_ep_register_init(struct qe_udc *udc, unsigned char pipe_num)
+{
+	struct qe_ep *ep = &(udc->eps[pipe_num]);
+	struct usb_ep_para __iomem *epparam;
+	u16 usep, logepnum;
+	u16 tmp;
+	u8 rtfcr = 0;
+
+	epparam = udc->ep_param[pipe_num];
+
+	usep = 0;
+	logepnum = (ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+	usep |= (logepnum << USB_EPNUM_SHIFT);
+
+	switch (ep->desc->bmAttributes & 0x03) {
+	case USB_ENDPOINT_XFER_BULK:
+		usep |= USB_TRANS_BULK;
+		break;
+	case USB_ENDPOINT_XFER_ISOC:
+		usep |=  USB_TRANS_ISO;
+		break;
+	case USB_ENDPOINT_XFER_INT:
+		usep |= USB_TRANS_INT;
+		break;
+	default:
+		usep |= USB_TRANS_CTR;
+		break;
+	}
+
+	switch (ep->dir) {
+	case USB_DIR_OUT:
+		usep |= USB_THS_IGNORE_IN;
+		break;
+	case USB_DIR_IN:
+		usep |= USB_RHS_IGNORE_OUT;
+		break;
+	default:
+		break;
+	}
+	out_be16(&udc->usb_regs->usb_usep[pipe_num], usep);
+
+	rtfcr = 0x30;
+	out_8(&epparam->rbmr, rtfcr);
+	out_8(&epparam->tbmr, rtfcr);
+
+	tmp = (u16)(ep->ep.maxpacket + USB_CRC_SIZE);
+	/* MRBLR must be divisble by 4 */
+	tmp = (u16)(((tmp >> 2) << 2) + 4);
+	out_be16(&epparam->mrblr, tmp);
+
+	return 0;
+}
+
+static int qe_ep_init(struct qe_udc *udc,
+		      unsigned char pipe_num,
+		      const struct usb_endpoint_descriptor *desc)
+{
+	struct qe_ep *ep = &(udc->eps[pipe_num]);
+	unsigned long flags = 0;
+	int reval = 0;
+	u16 max = 0;
+
+	max = le16_to_cpu(desc->wMaxPacketSize);
+
+	/* check the max package size validate for this endpoint */
+	/* Refer to USB2.0 spec table 9-13,
+	*/
+	if (pipe_num != 0) {
+		switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+		case USB_ENDPOINT_XFER_BULK:
+			if (strstr(ep->ep.name, "-iso")
+				|| strstr(ep->ep.name, "-int"))
+			goto en_done;
+			switch (udc->gadget.speed) {
+			case USB_SPEED_HIGH:
+			if ((max == 128) || (max == 256) || (max == 512))
+				break;
+			default:
+				switch (max) {
+				case 4:
+				case 8:
+				case 16:
+				case 32:
+				case 64:
+					break;
+				default:
+				case USB_SPEED_LOW:
+					goto en_done;
+				}
+			}
+			break;
+		case USB_ENDPOINT_XFER_INT:
+			if (strstr(ep->ep.name, "-iso"))	/* bulk is ok */
+				goto en_done;
+			switch (udc->gadget.speed) {
+			case USB_SPEED_HIGH:
+				if (max <= 1024)
+					break;
+			case USB_SPEED_FULL:
+				if (max <= 64)
+					break;
+			default:
+				if (max <= 8)
+					break;
+				goto en_done;
+			}
+			break;
+		case USB_ENDPOINT_XFER_ISOC:
+			if (strstr(ep->ep.name, "-bulk")
+				|| strstr(ep->ep.name, "-int"))
+				goto en_done;
+			switch (udc->gadget.speed) {
+			case USB_SPEED_HIGH:
+				if (max <= 1024)
+					break;
+			case USB_SPEED_FULL:
+				if (max <= 1023)
+					break;
+			default:
+				goto en_done;
+			}
+			break;
+		case USB_ENDPOINT_XFER_CONTROL:
+			if (strstr(ep->ep.name, "-iso")
+				|| strstr(ep->ep.name, "-int"))
+				goto en_done;
+			switch (udc->gadget.speed) {
+			case USB_SPEED_HIGH:
+			case USB_SPEED_FULL:
+				switch (max) {
+				case 1:
+				case 2:
+				case 4:
+				case 8:
+				case 16:
+				case 32:
+				case 64:
+					break;
+				default:
+					goto en_done;
+				}
+			case USB_SPEED_LOW:
+				switch (max) {
+				case 1:
+				case 2:
+				case 4:
+				case 8:
+					break;
+				default:
+					goto en_done;
+				}
+			default:
+				goto en_done;
+			}
+			break;
+
+		default:
+			goto en_done;
+		}
+	} /* if ep0*/
+
+	/* here initialize variable of ep */
+	ep->ep.maxpacket = max;
+	ep->tm = (u8)(desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
+	ep->desc = desc;
+	ep->stopped = 0;
+	ep->init = 1;
+
+	if (pipe_num == 0) {
+		ep->dir = USB_DIR_BOTH;
+		udc->ep0_dir = USB_DIR_OUT;
+		udc->ep0_state = WAIT_FOR_SETUP;
+	} else	{
+		switch ((desc->bEndpointAddress)&USB_ENDPOINT_DIR_MASK) {
+		case USB_DIR_OUT:
+			ep->dir = USB_DIR_OUT;
+			break;
+		case USB_DIR_IN:
+			ep->dir = USB_DIR_IN;
+		default:
+			break;
+		}
+	}
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	/* hardware special operation */
+	qe_ep_bd_init((void *)udc, pipe_num);
+	if ((ep->tm == USBP_TM_CTL) || (ep->dir == USB_DIR_OUT)) {
+		reval = qe_ep_rxbd_update(ep);
+		if (reval)
+			goto en_done;
+	}
+
+	if ((ep->tm == USBP_TM_CTL) || (ep->dir == USB_DIR_IN)) {
+		ep->txframe = kmalloc(sizeof(struct qe_frame), GFP_KERNEL);
+		if (ep->txframe == NULL) {
+			dev_err(udc->dev, "malloc txframe failed\n");
+			goto en_done;
+		}
+		qe_frame_init(ep->txframe);
+	}
+	qe_ep_register_init((void *)udc, pipe_num);
+
+	/* Now HW will be NAKing transfers to that EP,
+	 * until a buffer is queued to it. */
+
+	/* should have stop the lock */
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return 0;
+
+en_done:
+	VDBG("init %s failed!", ep->ep.name);
+	return -ENODEV;
+}
+
+static int qe_usb_enable(void)
+{
+	setbits8(&udc_controller->usb_regs->usb_usmod, USB_MODE_EN);
+
+	return 0;
+}
+
+static int qe_usb_disable(void)
+{
+	clrbits8(&udc_controller->usb_regs->usb_usmod, USB_MODE_EN);
+
+	return 0;
+}
+
+/*----------------------------------------------------------------------------*
+ *		USB and EP basic manipulate function end		      *
+ *----------------------------------------------------------------------------*/
+
+
+/******************************************************************************
+		UDC transmit and receive process
+ ******************************************************************************/
+static void recycle_one_rxbd(struct qe_ep *ep)
+{
+	u32 bdstatus;
+
+	bdstatus = (u32)BD_STATUS_AND_LENGTH(ep->e_rxbd);
+	bdstatus = R_I | R_E | (bdstatus & R_W);
+	BD_STATUS_AND_LENGTH_SET(ep->e_rxbd, bdstatus);
+
+	if (bdstatus & R_W)
+		ep->e_rxbd = ep->rxbase;
+	else
+		ep->e_rxbd++;
+}
+
+static void recycle_rxbds(struct qe_ep *ep, unsigned char stopatnext)
+{
+	u32 bdstatus;
+	struct qe_bd *bd, *nextbd;
+	unsigned char stop = 0;
+
+	nextbd = ep->n_rxbd;
+	bd = ep->e_rxbd;
+	bdstatus = (u32)BD_STATUS_AND_LENGTH(bd);
+
+	while (!(bdstatus & R_E) && !(bdstatus&BD_LENGTH_MASK) && !stop) {
+		bdstatus = R_E | R_I | (bdstatus & R_W);
+		BD_STATUS_AND_LENGTH_SET(bd, bdstatus);
+
+		if (bdstatus & R_W)
+			bd = ep->rxbase;
+		else
+			bd++;
+
+		bdstatus = (u32)BD_STATUS_AND_LENGTH(bd);
+		if (stopatnext && (bd == nextbd))
+			stop = 1;
+	}
+
+	ep->e_rxbd = (struct qe_bd *)bd;
+}
+
+static void ep_recycle_rxbds(struct qe_ep *ep)
+{
+	struct qe_bd *bd = NULL;
+	u32 bdstatus;
+	u8 epnum;
+	struct qe_udc *udc = NULL;
+
+	udc = ep->udc;
+	epnum = ep->epnum;
+
+	bd = ep->n_rxbd;
+	bdstatus = (u32)BD_STATUS_AND_LENGTH(bd);
+	if (!(bdstatus & R_E) && !(bdstatus & BD_LENGTH_MASK)) {
+		bd = (struct qe_bd *)(ep->rxbase +
+				((in_be16(&udc->ep_param[epnum]->rbptr) -
+				  in_be16(&udc->ep_param[epnum]->rbase))
+				 >> 3));
+		bdstatus = (u32)BD_STATUS_AND_LENGTH(bd);
+
+		if (bdstatus & R_W)
+			bd = ep->rxbase;
+		else
+			bd++;
+
+		ep->e_rxbd = (struct qe_bd *)bd;
+		VDBG("The QE completed full bd cycle");
+		recycle_rxbds(ep, 0);
+		ep->e_rxbd = ep->n_rxbd;
+	} else
+		recycle_rxbds(ep, 1);
+
+	if (in_be16(&udc->usb_regs->usb_usber) & USB_E_BSY_MASK)
+		out_be16(&udc->usb_regs->usb_usber, USB_E_BSY_MASK);
+
+	if (ep->has_data <= 0 && (!list_empty(&ep->queue)))
+		qe_eprx_normal(ep);
+
+	ep->localnack = 0;
+}
+
+static void setup_received_handle(struct qe_udc *udc,
+					struct usb_ctrlrequest *setup);
+static int qe_ep_rxframe_handle(struct qe_ep *ep);
+static void ep0_req_complete(struct qe_udc *udc, struct qe_req *req);
+/* when BD PID is setup, handle the packet */
+static int ep0_setup_handle(struct qe_udc *udc)
+{
+	struct qe_ep *ep = &(udc->eps[0]);
+	struct qe_frame *pframe = NULL;
+	unsigned int fsize;
+	u8 *cp;
+
+	pframe = (struct qe_frame *)ep->rxframe;
+	if ((frame_get_info(pframe) & PID_SETUP)
+		&& (udc->ep0_state == WAIT_FOR_SETUP)) {
+		fsize = (int)frame_get_length(pframe);
+		if (unlikely(fsize != 8))
+			return -EINVAL;
+		cp = (u8 *)&(udc->local_setup_buff);
+		memcpy(cp, pframe->data, fsize);
+		ep->data01 = 1;
+
+		/* handle the usb command base on the usb_ctrlrequest */
+		setup_received_handle(udc, &udc->local_setup_buff);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int qe_ep0_rx(struct qe_udc *udc)
+{
+	struct qe_ep *ep = &(udc->eps[0]);
+	struct qe_frame *pframe = NULL;
+	struct qe_bd *bd;
+	u32 bdstatus, length;
+	u32 vaddr;
+
+	pframe = (struct qe_frame *)ep->rxframe;
+
+	if (ep->dir == USB_DIR_IN) {
+		VDBG("error:This is a transmit ep!");
+		return -EINVAL;
+	}
+
+	bd = (struct qe_bd *)(ep->n_rxbd);
+	bdstatus = BD_STATUS_AND_LENGTH(bd);
+	length = bdstatus & BD_LENGTH_MASK;
+
+	while (!(bdstatus&R_E) && length) {
+		if ((bdstatus & R_F) && (bdstatus & R_L)
+			&& !(bdstatus & R_ERROR)) {
+			if (length == USB_CRC_SIZE) {
+				udc->ep0_state = WAIT_FOR_SETUP;
+				VDBG("receive a ZLP in status phase");
+			} else {
+				qe_frame_clean(pframe);
+				vaddr = (u32)phys_to_virt(BD_BUFFER(bd));
+				frame_set_data(pframe, (u8 *)vaddr);
+				frame_set_length(pframe,
+						(length - USB_CRC_SIZE));
+				frame_set_status(pframe, FRAME_OK);
+				switch (bdstatus & R_PID) {
+				case R_PID_SETUP:
+					frame_set_info(pframe, PID_SETUP);
+					break;
+				case R_PID_DATA1:
+					frame_set_info(pframe, PID_DATA1);
+					break;
+				default:
+					frame_set_info(pframe, PID_DATA0);
+					break;
+				}
+
+				if ((bdstatus & R_PID) == R_PID_SETUP)
+					ep0_setup_handle(udc);
+				else
+					qe_ep_rxframe_handle(ep);
+			}
+		} else {
+			dev_err(udc->dev, "The receive frame with error!\n");
+		}
+
+		/* note: don't clear the rxbd's buffer address */
+		recycle_one_rxbd(ep);
+
+		/* Get next BD */
+		if (bdstatus & R_W)
+			bd = ep->rxbase;
+		else
+			bd++;
+
+		bdstatus = BD_STATUS_AND_LENGTH(bd);
+		length = bdstatus & BD_LENGTH_MASK;
+
+	}
+
+	ep->n_rxbd = (struct qe_bd *)bd;
+
+	return 0;
+}
+
+static int qe_ep_rxframe_handle(struct qe_ep *ep)
+{
+	struct qe_frame *pframe = NULL;
+	u8 framepid = 0;
+	unsigned int fsize;
+	u8 *cp = NULL;
+	struct qe_req *req = NULL;
+
+	pframe = (struct qe_frame *)ep->rxframe;
+
+	if (frame_get_info(pframe) & PID_DATA1)
+		framepid = 0x1;
+
+	if (framepid != ep->data01) {
+		dev_err(ep->udc->dev, "the data01 error!\n");
+		return -EIO;
+	}
+
+	fsize = frame_get_length(pframe);
+	if (list_empty(&ep->queue)) {
+		dev_err(ep->udc->dev, "the %s have no requeue!\n", ep->name);
+	} else {
+		req = list_entry(ep->queue.next, struct qe_req, queue);
+
+		cp = (u8 *)(req->req.buf) + req->req.actual;
+		if (cp) {
+			memcpy(cp, pframe->data, fsize);
+			req->req.actual += fsize;
+			if ((fsize < ep->ep.maxpacket) ||
+					(req->req.actual >= req->req.length)) {
+				if (ep->epnum == 0)
+					ep0_req_complete(ep->udc, req);
+				else
+					done(ep, req, 0);
+				if (list_empty(&ep->queue) && ep->epnum != 0)
+					qe_eprx_nack(ep);
+			}
+		}
+	}
+
+	qe_ep_toggledata01(ep);
+
+	return 0;
+}
+
+static void ep_rx_tasklet(unsigned long data)
+{
+	struct qe_udc *udc = (struct qe_udc *)data;
+	struct qe_ep *ep = NULL;
+	struct qe_frame *pframe = NULL;
+	struct qe_bd *bd;
+	u32 bdstatus, length;
+	u32 vaddr, i;
+
+	for (i = 1; i < USB_MAX_ENDPOINTS; i++) {
+		ep = (struct qe_ep *)&(udc->eps[i]);
+
+		if (ep->dir == USB_DIR_IN || ep->enable_tasklet == 0) {
+			VDBG("This is a transmit ep or disable tasklet!");
+			continue;
+		}
+
+		pframe = (struct qe_frame *)ep->rxframe;
+		bd = (struct qe_bd *)(ep->n_rxbd);
+		bdstatus = BD_STATUS_AND_LENGTH(bd);
+		length = bdstatus & BD_LENGTH_MASK;
+
+		while (!(bdstatus&R_E) && length) {
+			if (list_empty(&ep->queue)) {
+				qe_eprx_nack(ep);
+				VDBG("The rxep have noreq %d", ep->has_data);
+				break;
+			}
+
+			if ((bdstatus & R_F) && (bdstatus & R_L)
+				&& !(bdstatus & R_ERROR)) {
+				qe_frame_clean(pframe);
+				vaddr = (u32)phys_to_virt(BD_BUFFER(bd));
+				frame_set_data(pframe, (u8 *)vaddr);
+				frame_set_length(pframe,
+						(length - USB_CRC_SIZE));
+				frame_set_status(pframe, FRAME_OK);
+				switch (bdstatus & R_PID) {
+				case R_PID_DATA1:
+					frame_set_info(pframe, PID_DATA1);
+					break;
+				case R_PID_SETUP:
+					frame_set_info(pframe, PID_SETUP);
+					break;
+				default:
+					frame_set_info(pframe, PID_DATA0);
+					break;
+				}
+				/* handle the rx frame */
+				qe_ep_rxframe_handle(ep);
+			} else {
+				dev_err(udc->dev,
+					"error in received frame\n");
+			}
+			/* note: don't clear the rxbd's buffer address */
+			/*clear the length */
+			BD_STATUS_AND_LENGTH_SET(bd, (bdstatus&BD_STATUS_MASK));
+			ep->has_data--;
+			if (!(ep->localnack))
+				recycle_one_rxbd(ep);
+
+			/* Get next BD */
+			if (bdstatus & R_W)
+				bd = ep->rxbase;
+			else
+				bd++;
+
+			bdstatus = BD_STATUS_AND_LENGTH(bd);
+			length = bdstatus & BD_LENGTH_MASK;
+		}
+
+		ep->n_rxbd = (struct qe_bd *)bd;
+
+		if (ep->localnack)
+			ep_recycle_rxbds(ep);
+
+		ep->enable_tasklet = 0;
+	} /* for i=1 */
+}
+
+static int qe_ep_rx(struct qe_ep *ep)
+{
+	struct qe_udc *udc = NULL;
+	struct qe_frame *pframe = NULL;
+	struct qe_bd *bd;
+	u16 swoffs, ucoffs, emptybds;
+	u32 bdstatus, length;
+
+	udc = (struct qe_udc *)ep->udc;
+	pframe = (struct qe_frame *)ep->rxframe;
+
+	if (ep->dir == USB_DIR_IN) {
+		VDBG("error:This is a transmit ep!");
+		return -EINVAL;
+	}
+
+	bd = (struct qe_bd *)(ep->n_rxbd);
+	bdstatus = BD_STATUS_AND_LENGTH(bd);
+	length = bdstatus & BD_LENGTH_MASK;
+
+	swoffs = (u16)(((u8 *)bd - (u8 *)ep->rxbase) >> 3);
+	ucoffs = (u16)((in_be16(&udc->ep_param[ep->epnum]->rbptr) -
+			in_be16(&udc->ep_param[ep->epnum]->rbase)) >> 3);
+	if (swoffs < ucoffs)
+		emptybds = USB_BDRING_LEN_RX - ucoffs + swoffs;
+	else
+		emptybds = swoffs - ucoffs;
+
+	if (emptybds < MIN_EMPTY_BDS) {
+		qe_eprx_nack(ep);
+		ep->localnack = 1;
+		VDBG("The rx ep have a nack!%d", emptybds);
+	}
+	ep->has_data = USB_BDRING_LEN_RX - emptybds;
+
+	if (list_empty(&ep->queue)) {
+		qe_eprx_nack(ep);
+		VDBG("The rxep have noreq %d", ep->has_data);
+		return 0;
+	}
+
+	tasklet_schedule(&udc->rx_tasklet);
+	ep->enable_tasklet = 1;
+
+	return 0;
+}
+
+/* send data from a frame, no matter what tx_req */
+static int qe_ep_tx(struct qe_ep *ep, struct qe_frame *frame)
+{
+	struct qe_udc *udc = NULL;
+
+	struct qe_bd *bd = NULL;
+	u16 saveusbmr;
+	u32 bdstatus, pidmask;
+	u32 paddr;
+
+	udc = (struct qe_udc *)ep->udc;
+
+	if (ep->dir == USB_DIR_OUT) {
+		VDBG("error:this is a receive ep!");
+		return -EINVAL;
+	}
+
+	/* Disable the Tx interrupt */
+	saveusbmr = in_be16(&udc->usb_regs->usb_usbmr);
+	out_be16(&udc->usb_regs->usb_usbmr,
+			saveusbmr & ~(USB_E_TXB_MASK | USB_E_TXE_MASK));
+
+	bd = ep->n_txbd;
+	bdstatus = BD_STATUS_AND_LENGTH(bd);
+
+	if (!(bdstatus & (T_R|BD_LENGTH_MASK))) {
+		if (frame_get_length(frame) == 0) {
+			frame_set_data(frame, udc->nullbuf);
+			frame_set_length(frame, 2);
+			frame->info |= (ZLP|NO_CRC);
+			VDBG("the frame size = 0");
+		}
+		paddr = virt_to_phys((void *)frame->data);
+		BD_BUFFER_SET(bd, paddr);
+		bdstatus = (bdstatus&T_W);
+		if (!(frame_get_info(frame) & NO_CRC))
+			bdstatus |= T_R | T_I | T_L | T_TC
+					| frame_get_length(frame);
+		else
+			bdstatus |= T_R | T_I | T_L | frame_get_length(frame);
+
+		/* if the packet is a ZLP in status phase */
+		if ((ep->epnum == 0) && (udc->ep0_state == DATA_STATE_NEED_ZLP))
+			ep->data01 = 0x1;
+
+		if (ep->data01) {
+			pidmask = T_PID_DATA1;
+			frame->info |= PID_DATA1;
+		} else {
+			pidmask = T_PID_DATA0;
+			frame->info |= PID_DATA0;
+		}
+		bdstatus |= T_CNF;
+		bdstatus |= pidmask;
+		BD_STATUS_AND_LENGTH_SET(bd, bdstatus);
+		qe_ep_filltxfifo(ep);
+
+		/* enable the TX interrupt */
+		out_be16(&udc->usb_regs->usb_usbmr, saveusbmr);
+
+		qe_ep_toggledata01(ep);
+		if (bdstatus & T_W)
+			ep->n_txbd = ep->txbase;
+		else
+			ep->n_txbd++;
+
+		return 0;
+	} else {
+		out_be16(&udc->usb_regs->usb_usbmr, saveusbmr);
+		VDBG("The tx bd is not ready!");
+		return -EBUSY;
+	}
+}
+
+/* when an bd was transmitted, the function can *
+ * handle the tx_req, not include ep0           */
+static int txcomplete(struct qe_ep *ep, unsigned char restart)
+{
+	struct qe_req *tx_req = NULL;
+
+	tx_req = ep->tx_req;
+	if (tx_req != NULL) {
+		if (!restart) {
+			int asent = ep->last;
+			ep->sent += asent;
+			ep->last -= asent;
+		} else {
+			ep->last = 0;
+		}
+
+		/* a request already were transmitted completely */
+		if ((ep->tx_req->req.length - ep->sent) <= 0) {
+			ep->tx_req->req.actual = (unsigned int)ep->sent;
+			done(ep, ep->tx_req, 0);
+			ep->tx_req = NULL;
+			ep->last = 0;
+			ep->sent = 0;
+		}
+	}
+
+	/* we should gain a new tx_req fot this endpoint */
+	if (ep->tx_req == NULL) {
+		if (!list_empty(&ep->queue)) {
+			ep->tx_req = list_entry(ep->queue.next,	struct qe_req,
+							queue);
+			ep->last = 0;
+			ep->sent = 0;
+		}
+	}
+
+	return 0;
+}
+
+/* give a frame and a tx_req,send some data */
+static int qe_usb_senddata(struct qe_ep *ep, struct qe_frame *frame)
+{
+	unsigned int size;
+	u8 *buf;
+
+	qe_frame_clean(frame);
+	size = min_t(u32, (ep->tx_req->req.length - ep->sent),
+				ep->ep.maxpacket);
+	buf = (u8 *)ep->tx_req->req.buf + ep->sent;
+	if (buf && size) {
+		ep->last = size;
+		frame_set_data(frame, buf);
+		frame_set_length(frame, size);
+		frame_set_status(frame, FRAME_OK);
+		frame_set_info(frame, 0);
+		return qe_ep_tx(ep, frame);
+	}
+	return -EIO;
+}
+
+/* give a frame struct,send a ZLP */
+static int sendnulldata(struct qe_ep *ep, struct qe_frame *frame, uint infor)
+{
+	struct qe_udc *udc = ep->udc;
+
+	if (frame == NULL)
+		return -ENODEV;
+
+	qe_frame_clean(frame);
+	frame_set_data(frame, (u8 *)udc->nullbuf);
+	frame_set_length(frame, 2);
+	frame_set_status(frame, FRAME_OK);
+	frame_set_info(frame, (ZLP|NO_CRC|infor));
+
+	return qe_ep_tx(ep, frame);
+}
+
+static int frame_create_tx(struct qe_ep *ep, struct qe_frame *frame)
+{
+	struct qe_req *req;
+	int reval;
+	if (ep->tx_req == NULL)
+		return -ENODEV;
+
+	req = ep->tx_req;
+
+	if ((req->req.length - ep->sent) > 0)
+		reval = qe_usb_senddata(ep, frame);
+	else
+		reval = sendnulldata(ep, frame, 0);
+
+	return reval;
+}
+
+/* if direction is DIR_IN, the status is Device->Host
+ * if direction is DIR_OUT, the status transaction is Device<-Host
+ * in status phase, udc create a request and gain status */
+static int ep0_prime_status(struct qe_udc *udc, int direction)
+{
+
+	struct qe_ep *ep = &udc->eps[0];
+
+	if (direction == USB_DIR_IN) {
+		udc->ep0_state = DATA_STATE_NEED_ZLP;
+		udc->ep0_dir = USB_DIR_IN;
+		sendnulldata(ep, ep->txframe, SETUP_STATUS|NO_REQ);
+	} else {
+		udc->ep0_dir = USB_DIR_OUT;
+		udc->ep0_state = WAIT_FOR_OUT_STATUS;
+	}
+
+	return 0;
+}
+
+/* a request complete in ep0, whether gadget request or udc request */
+static void ep0_req_complete(struct qe_udc *udc, struct qe_req *req)
+{
+	struct qe_ep *ep = &(udc->eps[0]);
+	/* because usb and ep's status already been set in ch9setaddress() */
+
+	switch (udc->ep0_state) {
+	case DATA_STATE_XMIT:
+		done(ep, req, 0);
+		/* receive status phase */
+		if (ep0_prime_status(udc, USB_DIR_OUT))
+			qe_ep0_stall(udc);
+		break;
+
+	case DATA_STATE_NEED_ZLP:
+		done(ep, req, 0);
+		udc->ep0_state = WAIT_FOR_SETUP;
+		break;
+
+	case DATA_STATE_RECV:
+		done(ep, req, 0);
+		/* send status phase */
+		if (ep0_prime_status(udc, USB_DIR_IN))
+			qe_ep0_stall(udc);
+		break;
+
+	case WAIT_FOR_OUT_STATUS:
+		done(ep, req, 0);
+		udc->ep0_state = WAIT_FOR_SETUP;
+		break;
+
+	case WAIT_FOR_SETUP:
+		VDBG("Unexpected interrupt");
+		break;
+
+	default:
+		qe_ep0_stall(udc);
+		break;
+	}
+}
+
+static int ep0_txcomplete(struct qe_ep *ep, unsigned char restart)
+{
+	struct qe_req *tx_req = NULL;
+	struct qe_frame *frame = ep->txframe;
+
+	if ((frame_get_info(frame)&(ZLP|NO_REQ)) == (ZLP|NO_REQ)) {
+		if (!restart)
+			ep->udc->ep0_state = WAIT_FOR_SETUP;
+		else
+			sendnulldata(ep, ep->txframe, SETUP_STATUS|NO_REQ);
+		return 0;
+	}
+
+	tx_req = ep->tx_req;
+	if (tx_req != NULL) {
+		if (!restart) {
+			int asent = ep->last;
+			ep->sent += asent;
+			ep->last -= asent;
+		} else {
+			ep->last = 0;
+		}
+
+		/* a request already were transmitted completely */
+		if ((ep->tx_req->req.length - ep->sent) <= 0) {
+			ep->tx_req->req.actual = (unsigned int)ep->sent;
+			ep0_req_complete(ep->udc, ep->tx_req);
+			ep->tx_req = NULL;
+			ep->last = 0;
+			ep->sent = 0;
+		}
+	} else {
+		VDBG("the ep0_controller have no req");
+	}
+
+	return 0;
+}
+
+static int ep0_txframe_handle(struct qe_ep *ep)
+{
+	/* if have error, transmit again */
+	if (frame_get_status(ep->txframe)&FRAME_ERROR) {
+		qe_ep_flushtxfifo(ep);
+		VDBG("The EP0 transmit data have error!");
+		if (frame_get_info(ep->txframe)&PID_DATA0)
+			ep->data01 = 0;
+		else
+			ep->data01 = 1;
+
+		ep0_txcomplete(ep, 1);
+	} else
+		ep0_txcomplete(ep, 0);
+
+	frame_create_tx(ep, ep->txframe);
+	return 0;
+}
+
+static int qe_ep0_txconf(struct qe_ep *ep)
+{
+	struct qe_bd *bd = NULL;
+	struct qe_frame *pframe = NULL;
+	u32 bdstatus;
+
+	bd = ep->c_txbd;
+	bdstatus = BD_STATUS_AND_LENGTH(bd);
+	while (!(bdstatus & T_R) && (bdstatus & ~T_W)) {
+		pframe = ep->txframe;
+
+		/* clear and recycle the BD */
+		BD_STATUS_AND_LENGTH_SET(bd, (bdstatus&T_W));
+		BD_BUFFER_CLEAR(bd);
+		if (bdstatus&T_W)
+			ep->c_txbd = ep->txbase;
+		else
+			ep->c_txbd++;
+
+		if (ep->c_txbd == ep->n_txbd) {
+			if (bdstatus & DEVICE_T_ERROR) {
+				frame_set_status(pframe, FRAME_ERROR);
+				if (bdstatus & T_TO)
+					pframe->status |= TX_ER_TIMEOUT;
+				if (bdstatus & T_UN)
+					pframe->status |= TX_ER_UNDERUN;
+			}
+			ep0_txframe_handle(ep);
+		}
+
+		bd = ep->c_txbd;
+		bdstatus = BD_STATUS_AND_LENGTH(bd);
+	}
+
+	return 0;
+}
+
+static int ep_txframe_handle(struct qe_ep *ep)
+{
+	if (frame_get_status(ep->txframe)&FRAME_ERROR) {
+		qe_ep_flushtxfifo(ep);
+		VDBG("The EP0 transmit data have error!");
+		if (frame_get_info(ep->txframe)&PID_DATA0)
+			ep->data01 = 0;
+		else
+			ep->data01 = 1;
+
+		txcomplete(ep, 1);
+	} else
+		txcomplete(ep, 0);
+
+	frame_create_tx(ep, ep->txframe); /* send the data */
+	return 0;
+}
+
+/* confirm the already trainsmited bd */
+static int qe_ep_txconf(struct qe_ep *ep)
+{
+	struct qe_bd *bd = NULL;
+	struct qe_frame *pframe = NULL;
+	u32 bdstatus;
+	unsigned char breakonrxinterrupt = 0;
+
+	bd = ep->c_txbd;
+	bdstatus = BD_STATUS_AND_LENGTH(bd);
+	while (!(bdstatus & T_R) && (bdstatus & ~T_W)) {
+		pframe = ep->txframe;
+		if (bdstatus & DEVICE_T_ERROR) {
+			frame_set_status(pframe, FRAME_ERROR);
+			if (bdstatus & T_TO)
+				pframe->status |= TX_ER_TIMEOUT;
+			if (bdstatus & T_UN)
+				pframe->status |= TX_ER_UNDERUN;
+		}
+
+		/* clear and recycle the BD */
+		BD_STATUS_AND_LENGTH_SET(bd, (bdstatus&T_W));
+		BD_BUFFER_CLEAR(bd);
+		if (bdstatus&T_W)
+			ep->c_txbd = ep->txbase;
+		else
+			ep->c_txbd++;
+
+		/* handle the tx frame */
+		ep_txframe_handle(ep);
+		bd = ep->c_txbd;
+		bdstatus = BD_STATUS_AND_LENGTH(bd);
+	}
+	if (breakonrxinterrupt)
+		return -EIO;
+	else
+		return 0;
+}
+
+/* Add a request in queue, and try to transmit a packet */
+static int ep_req_send(struct qe_ep *ep, struct qe_req *req)
+{
+	int reval = 0;
+
+	if (ep->tx_req == NULL) {
+		ep->sent = 0;
+		ep->last = 0;
+		txcomplete(ep, 0); /* can gain a new tx_req */
+		reval = frame_create_tx(ep, ep->txframe);
+	}
+	return reval;
+}
+
+/* Maybe this is a good ideal */
+static int ep_req_rx(struct qe_ep *ep, struct qe_req *req)
+{
+	struct qe_udc *udc = NULL;
+	struct qe_frame *pframe = NULL;
+	struct qe_bd *bd;
+	u32 bdstatus, length;
+	u32 vaddr, fsize;
+	u8 *cp;
+	u8 finish_req = 0;
+	u8 framepid;
+
+	if (list_empty(&ep->queue)) {
+		VDBG("the req already finish!");
+		return 0;
+	}
+	udc = (struct qe_udc *)ep->udc;
+	pframe = (struct qe_frame *)ep->rxframe;
+
+	bd = (struct qe_bd *)(ep->n_rxbd);
+	bdstatus = BD_STATUS_AND_LENGTH(bd);
+	length = bdstatus & BD_LENGTH_MASK;
+
+	while (!(bdstatus&R_E) && length) {
+		if (finish_req)
+			break;
+		if ((bdstatus & R_F) && (bdstatus & R_L)
+					&& !(bdstatus & R_ERROR)) {
+			qe_frame_clean(pframe);
+			vaddr = (u32)phys_to_virt(BD_BUFFER(bd));
+			frame_set_data(pframe, (u8 *)vaddr);
+			frame_set_length(pframe, (length - USB_CRC_SIZE));
+			frame_set_status(pframe, FRAME_OK);
+			switch (bdstatus & R_PID) {
+			case R_PID_DATA1:
+				frame_set_info(pframe, PID_DATA1); break;
+			default:
+				frame_set_info(pframe, PID_DATA0); break;
+			}
+			/* handle the rx frame */
+
+			if (frame_get_info(pframe)&PID_DATA1)
+				framepid = 0x1;
+			else
+				framepid = 0;
+
+			if (framepid != ep->data01) {
+				VDBG("the data01 error!");
+			} else {
+				fsize = frame_get_length(pframe);
+
+				cp = (u8 *)(req->req.buf) + req->req.actual;
+				if (cp) {
+					memcpy(cp, pframe->data, fsize);
+					req->req.actual += fsize;
+					if ((fsize < ep->ep.maxpacket)
+						|| (req->req.actual >=
+							req->req.length)) {
+						finish_req = 1;
+						done(ep, req, 0);
+						if (list_empty(&ep->queue))
+							qe_eprx_nack(ep);
+					}
+				}
+				qe_ep_toggledata01(ep);
+			}
+		} else {
+			dev_err(udc->dev, "The receive frame with error!\n");
+		}
+
+		/* note: don't clear the rxbd's buffer address *
+		 * only Clear the length */
+		BD_STATUS_AND_LENGTH_SET(bd, (bdstatus & BD_STATUS_MASK));
+		ep->has_data--;
+
+		/* Get next BD */
+		if (bdstatus & R_W)
+			bd = ep->rxbase;
+		else
+			bd++;
+
+		bdstatus = BD_STATUS_AND_LENGTH(bd);
+		length = bdstatus & BD_LENGTH_MASK;
+	}
+
+	ep->n_rxbd = (struct qe_bd *)bd;
+	ep_recycle_rxbds(ep);
+
+	return 0;
+}
+
+/* only add the request in queue */
+static int ep_req_receive(struct qe_ep *ep, struct qe_req *req)
+{
+	if (ep->state == EP_STATE_NACK) {
+		if (ep->has_data <= 0) {
+			/* Enable rx and unmask rx interrupt */
+			qe_eprx_normal(ep);
+		} else {
+			/* Copy the exist BD data */
+			ep_req_rx(ep, req);
+		}
+	}
+
+	return 0;
+}
+
+/********************************************************************
+	Internal Used Function End
+********************************************************************/
+
+/*-----------------------------------------------------------------------
+	Endpoint Management Functions For Gadget
+ -----------------------------------------------------------------------*/
+static int qe_ep_enable(struct usb_ep *_ep,
+			 const struct usb_endpoint_descriptor *desc)
+{
+	struct qe_udc *udc = NULL;
+	struct qe_ep *ep = NULL;
+	int retval = 0;
+	unsigned char epnum;
+	unsigned long flags = 0;
+
+	ep = container_of(_ep, struct qe_ep, ep);
+
+	/* catch various bogus parameters */
+	if (!_ep || !desc || ep->desc || _ep->name == ep_name[0] ||
+		(desc->bDescriptorType != USB_DT_ENDPOINT))
+		/* FIXME: add judge for ep->bEndpointAddress */
+		return -EINVAL;
+
+	udc = ep->udc;
+	if (!udc->driver || (udc->gadget.speed == USB_SPEED_UNKNOWN))
+		return -ESHUTDOWN;
+
+	epnum = (u8)desc->bEndpointAddress&0xF;
+
+	spin_lock_irqsave(&udc->lock, flags);
+	retval = qe_ep_init((void *)udc, epnum, desc);
+	spin_unlock_irqrestore(&udc->lock, flags);
+	if (retval != 0) {
+		qe_muram_free(muram_offset(ep->rxbase));
+		VDBG("enable ep%d fail", ep->epnum);
+		return -EINVAL;
+	}
+	VDBG("enable ep%d ok", ep->epnum);
+	return 0;
+}
+
+static int qe_ep_disable(struct usb_ep *_ep)
+{
+	struct qe_udc *udc = NULL;
+	struct qe_ep *ep = NULL;
+	unsigned long flags = 0;
+	unsigned int size;
+
+	ep = container_of(_ep, struct qe_ep, ep);
+
+	if (!_ep || !ep->desc) {
+		VDBG("%s not enabled", _ep ? ep->ep.name : NULL);
+		return -EINVAL;
+	}
+
+	udc = (struct qe_udc *) ep->udc;
+
+	spin_lock_irqsave(&udc->lock, flags);
+	/* Nuke all pending requests (does flush) */
+	nuke(ep, -ESHUTDOWN);
+	ep->desc = NULL;
+	ep->stopped = 1;
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	qe_muram_free(muram_offset(ep->rxbase));
+
+	if (ep->dir == USB_DIR_OUT)
+		size = (ep->ep.maxpacket + USB_CRC_SIZE + 2) *
+				(USB_BDRING_LEN_RX + 1);
+	else
+		size = (ep->ep.maxpacket + USB_CRC_SIZE + 2) *
+				(USB_BDRING_LEN + 1);
+
+	if (ep->dir != USB_DIR_IN) {
+		kfree(ep->rxframe);
+		if (ep->rxbufmap) {
+			dma_unmap_single(udc_controller->gadget.dev.parent,
+					ep->rxbuf_d, size,
+					DMA_FROM_DEVICE);
+				ep->rxbuf_d = DMA_ADDR_INVALID;
+		} else
+			dma_sync_single_for_cpu(
+					udc_controller->gadget.dev.parent,
+					ep->rxbuf_d, size,
+					DMA_FROM_DEVICE);
+		kfree(ep->rxbuffer);
+	}
+
+	if (ep->dir != USB_DIR_OUT)
+		kfree(ep->txframe);
+
+	VDBG("disabled %s OK", _ep->name);
+	return 0;
+}
+
+static struct usb_request *qe_alloc_request(struct usb_ep *_ep,	gfp_t gfp_flags)
+{
+	struct qe_req *req = NULL;
+
+	req = kzalloc(sizeof *req, gfp_flags);
+	if (!req)
+		return NULL;
+
+	req->req.dma = DMA_ADDR_INVALID;
+
+	INIT_LIST_HEAD(&req->queue);
+
+	return &req->req;
+}
+
+static void qe_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct qe_req *req = NULL;
+
+	req = container_of(_req, struct qe_req, req);
+
+	if (_req)
+		kfree(req);
+}
+
+/* queues (submits) an I/O request to an endpoint */
+static int qe_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
+				gfp_t gfp_flags)
+{
+	struct qe_ep *ep = container_of(_ep, struct qe_ep, ep);
+	struct qe_req *req = container_of(_req, struct qe_req, req);
+	struct qe_udc *udc;
+	unsigned long flags;
+	int reval;
+
+	/* catch various bogus parameters */
+	if (!_req || !req->req.complete || !req->req.buf
+	|| !list_empty(&req->queue)) {
+		VDBG("bad params");
+		return -EINVAL;
+	}
+	if (!_ep || (!ep->desc && ep_index(ep))) {
+		VDBG("bad ep");
+		return -EINVAL;
+	}
+
+	udc = ep->udc;
+	if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	req->ep = ep;
+
+	/* map virtual address to hardware */
+	if (req->req.dma == DMA_ADDR_INVALID) {
+		req->req.dma = dma_map_single(ep->udc->gadget.dev.parent,
+					req->req.buf,
+					req->req.length,
+					ep_is_in(ep)
+					? DMA_TO_DEVICE :
+					DMA_FROM_DEVICE);
+		req->mapped = 1;
+	} else {
+		dma_sync_single_for_device(ep->udc->gadget.dev.parent,
+					req->req.dma, req->req.length,
+					ep_is_in(ep)
+					? DMA_TO_DEVICE :
+					DMA_FROM_DEVICE);
+		req->mapped = 0;
+	}
+
+	req->req.status = -EINPROGRESS;
+	req->req.actual = 0;
+
+	list_add_tail(&req->queue, &ep->queue);
+	VDBG("gadget have request in %s! %d", ep->name, req->req.length);
+	spin_lock_irqsave(&udc->lock, flags);
+	/* push the request to device */
+	if (ep_is_in(ep))
+		reval = ep_req_send(ep, req);
+
+	/* EP0 */
+	if (ep_index(ep) == 0 && req->req.length > 0) {
+		if (ep_is_in(ep))
+			udc->ep0_state = DATA_STATE_XMIT;
+		else
+			udc->ep0_state = DATA_STATE_RECV;
+	}
+
+	if (ep->dir == USB_DIR_OUT)
+		reval = ep_req_receive(ep, req);
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return 0;
+}
+
+/* dequeues (cancels, unlinks) an I/O request from an endpoint */
+static int qe_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct qe_ep *ep = container_of(_ep, struct qe_ep, ep);
+	struct qe_req *req;
+	unsigned long flags;
+
+	if (!_ep || !_req)
+		return -EINVAL;
+
+	spin_lock_irqsave(&ep->udc->lock, flags);
+
+	/* make sure it's actually queued on this endpoint */
+	list_for_each_entry(req, &ep->queue, queue) {
+		if (&req->req == _req)
+			break;
+	}
+
+	if (&req->req != _req) {
+		spin_unlock_irqrestore(&ep->udc->lock, flags);
+		return -EINVAL;
+	}
+
+	done(ep, req, -ECONNRESET);
+
+	spin_unlock_irqrestore(&ep->udc->lock, flags);
+	return 0;
+}
+
+/*-----------------------------------------------------------------
+ * modify the endpoint halt feature
+ * @ep: the non-isochronous endpoint being stalled
+ * @value: 1--set halt  0--clear halt
+ * Returns zero, or a negative error code.
+*----------------------------------------------------------------*/
+static int qe_ep_set_halt(struct usb_ep *_ep, int value)
+{
+	struct qe_ep *ep = NULL;
+	unsigned long flags = 0;
+	int status = -EOPNOTSUPP;
+	struct qe_udc *udc = NULL;
+
+	ep = container_of(_ep, struct qe_ep, ep);
+	udc = ep->udc;
+	if (!_ep || !ep->desc) {
+		status = -EINVAL;
+		goto out;
+	}
+
+	if (ep->epnum != 0) {
+		status = 0;
+		goto out;
+	}
+
+	/* Attempt to halt IN ep will fail if any transfer requests
+	 * are still queue */
+	if (value && ep_is_in(ep) && !list_empty(&ep->queue)) {
+		status = -EAGAIN;
+		goto out;
+	}
+
+	status = 0;
+	spin_lock_irqsave(&ep->udc->lock, flags);
+	qe_eptx_stall_change(ep, value);
+	qe_eprx_stall_change(ep, value);
+	spin_unlock_irqrestore(&ep->udc->lock, flags);
+
+	if (ep->epnum == 0) {
+		udc->ep0_state = WAIT_FOR_SETUP;
+		udc->ep0_dir = 0;
+	}
+out:
+	VDBG(" %s %s halt stat %d", ep->ep.name,
+			value ?  "set" : "clear", status);
+
+	return status;
+}
+
+static struct usb_ep_ops qe_ep_ops = {
+	.enable = qe_ep_enable,
+	.disable = qe_ep_disable,
+
+	.alloc_request = qe_alloc_request,
+	.free_request = qe_free_request,
+
+	.queue = qe_ep_queue,
+	.dequeue = qe_ep_dequeue,
+
+	.set_halt = qe_ep_set_halt,
+};
+
+/*------------------------------------------------------------------------
+	Gadget Driver Layer Operations
+ ------------------------------------------------------------------------*/
+
+/* Get the current frame number */
+static int qe_get_frame(struct usb_gadget *gadget)
+{
+	u16 tmp;
+
+	tmp = in_be16(&udc_controller->usb_param->frame_n);
+	if (tmp & 0x8000)
+		tmp = tmp & 0x07ff;
+	else
+		tmp = -EINVAL;
+
+	return (int)tmp;
+}
+
+/* Tries to wake up the host connected to this gadget
+ *
+ * Return : 0-success
+ * Negative-this feature not enabled by host or not supported by device hw
+ */
+static int qe_wakeup(struct usb_gadget *gadget)
+{
+	return -ENOTSUPP;
+}
+
+/* Notify controller that VBUS is powered, Called by whatever
+   detects VBUS sessions */
+static int qe_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+	return -ENOTSUPP;
+}
+
+/* constrain controller's VBUS power usage
+ * This call is used by gadget drivers during SET_CONFIGURATION calls,
+ * reporting how much power the device may consume.  For example, this
+ * could affect how quickly batteries are recharged.
+ *
+ * Returns zero on success, else negative errno.
+ */
+static int qe_vbus_draw(struct usb_gadget *gadget, unsigned mA)
+{
+	return -ENOTSUPP;
+}
+
+/* Change Data+ pullup status
+ * this func is used by usb_gadget_connect/disconnet
+ */
+static int qe_pullup(struct usb_gadget *gadget, int is_on)
+{
+	return -ENOTSUPP;
+}
+
+/* defined in usb_gadget.h */
+static struct usb_gadget_ops qe_gadget_ops = {
+	.get_frame = qe_get_frame,
+	.wakeup = qe_wakeup,
+/*	.set_selfpowered = qe_set_selfpowered,*/ /* always selfpowered */
+	.vbus_session = qe_vbus_session,
+	.vbus_draw = qe_vbus_draw,
+	.pullup = qe_pullup,
+};
+
+/*-------------------------------------------------------------------------
+	USB ep0 Setup process in BUS Enumeration
+ -------------------------------------------------------------------------*/
+static int udc_reset_ep_queue(struct qe_udc *udc, u8 pipe)
+{
+	struct qe_ep *ep = &udc->eps[pipe];
+
+	nuke(ep, -ECONNRESET);
+	ep->tx_req = NULL;
+	return 0;
+}
+
+static int reset_queues(struct qe_udc *udc)
+{
+	u8 pipe;
+
+	for (pipe = 0; pipe < USB_MAX_ENDPOINTS; pipe++)
+		udc_reset_ep_queue(udc, pipe);
+
+	/* report disconnect; the driver is already quiesced */
+	udc->driver->disconnect(&udc->gadget);
+
+	return 0;
+}
+
+static void ch9setaddress(struct qe_udc *udc, u16 value, u16 index,
+			u16 length)
+{
+	/* Save the new address to device struct */
+	udc->device_address = (u8) value;
+	/* Update usb state */
+	udc->usb_state = USB_STATE_ADDRESS;
+
+	/* Status phase , send a ZLP */
+	if (ep0_prime_status(udc, USB_DIR_IN))
+		qe_ep0_stall(udc);
+}
+
+static void ownercomplete(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct qe_req *req = container_of(_req, struct qe_req, req);
+
+	req->req.buf = 0;
+	kfree(req);
+}
+
+static void ch9getstatus(struct qe_udc *udc, u16 value, u16 index,
+			 u16 length)
+{
+	u16 usb_status = 0;	/* fix me to give correct status */
+
+	struct qe_req *req;
+	struct qe_ep *ep;
+	int status = 0;
+
+	ep = &udc->eps[0];
+
+	req = container_of(qe_alloc_request(&ep->ep, GFP_KERNEL),
+					struct qe_req, req);
+	req->req.length = 2;
+	req->req.buf = udc->nullbuf;
+	memcpy(req->req.buf, (u8 *)&usb_status, 2);
+	req->req.status = -EINPROGRESS;
+	req->req.actual = 0;
+	req->req.complete = ownercomplete;
+
+	udc->ep0_dir = USB_DIR_IN;
+
+	/* data phase */
+	status = qe_ep_queue(&(ep->ep), &(req->req), GFP_ATOMIC);
+
+	if (status) {
+		dev_err(udc->dev, "Can't respond to getstatus request \n");
+		qe_ep0_stall(udc);
+	}
+}
+
+/* only handle the setup request, suppose the device in normal status */
+static void setup_received_handle(struct qe_udc *udc,
+				struct usb_ctrlrequest *setup)
+{
+	/* Fix Endian (udc->local_setup_buff is cpu Endian now)*/
+	u16 wValue = le16_to_cpu(setup->wValue);
+	u16 wIndex = le16_to_cpu(setup->wIndex);
+	u16 wLength = le16_to_cpu(setup->wLength);
+
+	/* clear the previous request in the ep0 */
+	udc_reset_ep_queue(udc, 0);
+
+	if (setup->bRequestType & USB_DIR_IN)
+		udc->ep0_dir = USB_DIR_IN;
+	else
+		udc->ep0_dir = USB_DIR_OUT;
+
+	switch (setup->bRequest) {
+	case USB_REQ_GET_STATUS:
+		/* Data+Status phase form udc */
+		if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_MASK))
+					!= (USB_DIR_IN | USB_TYPE_STANDARD))
+			break;
+		ch9getstatus(udc, wValue, wIndex, wLength);
+		return;
+
+	case USB_REQ_SET_ADDRESS:
+		/* Status phase from udc */
+		if (setup->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD |
+						USB_RECIP_DEVICE))
+			break;
+		ch9setaddress(udc, wValue, wIndex, wLength);
+		return;
+
+	case USB_REQ_CLEAR_FEATURE:
+	case USB_REQ_SET_FEATURE:
+		/* Requests with no data phase, status phase from udc */
+		if ((setup->bRequestType &  USB_TYPE_MASK)
+					!= USB_TYPE_STANDARD)
+			break;
+
+		if ((setup->bRequestType & USB_RECIP_MASK)
+				== USB_RECIP_ENDPOINT) {
+			int pipe = wIndex & USB_ENDPOINT_NUMBER_MASK;
+			struct qe_ep *ep;
+
+			if (wValue != 0 || wLength != 0
+				|| pipe > USB_MAX_ENDPOINTS)
+				break;
+			ep = &udc->eps[pipe];
+
+			spin_unlock(&udc->lock);
+			qe_ep_set_halt(&ep->ep,
+					(setup->bRequest == USB_REQ_SET_FEATURE)
+						? 1 : 0);
+			spin_lock(&udc->lock);
+		}
+
+		ep0_prime_status(udc, USB_DIR_IN);
+
+		return;
+
+	default:
+		break;
+	}
+
+	if (wLength) {
+		/* Data phase from gadget, status phase from udc */
+		if (setup->bRequestType & USB_DIR_IN) {
+			udc->ep0_state = DATA_STATE_XMIT;
+			udc->ep0_dir = USB_DIR_IN;
+		} else{
+			udc->ep0_state = DATA_STATE_RECV;
+			udc->ep0_dir = USB_DIR_OUT;
+		}
+		spin_unlock(&udc->lock);
+		if (udc->driver->setup(&udc->gadget,
+					&udc->local_setup_buff) < 0)
+			qe_ep0_stall(udc);
+		spin_lock(&udc->lock);
+	} else {
+		/* No data phase, IN status from gadget */
+		udc->ep0_dir = USB_DIR_IN;
+		spin_unlock(&udc->lock);
+		if (udc->driver->setup(&udc->gadget,
+					&udc->local_setup_buff) < 0)
+			qe_ep0_stall(udc);
+		spin_lock(&udc->lock);
+		udc->ep0_state = DATA_STATE_NEED_ZLP;
+	}
+}
+
+/*-------------------------------------------------------------------------
+	USB Interrupt handlers
+ -------------------------------------------------------------------------*/
+static void suspend_irq(struct qe_udc *udc)
+{
+	udc->resume_state = udc->usb_state;
+	udc->usb_state = USB_STATE_SUSPENDED;
+
+	/* report suspend to the driver ,serial.c not support this*/
+	if (udc->driver->suspend)
+		udc->driver->suspend(&udc->gadget);
+}
+
+static void resume_irq(struct qe_udc *udc)
+{
+	udc->usb_state = udc->resume_state;
+	udc->resume_state = 0;
+
+	/* report resume to the driver , serial.c not support this*/
+	if (udc->driver->resume)
+		udc->driver->resume(&udc->gadget);
+}
+
+static void idle_irq(struct qe_udc *udc)
+{
+	u8 usbs;
+
+	usbs = in_8(&udc->usb_regs->usb_usbs);
+	if (usbs & USB_IDLE_STATUS_MASK) {
+		if ((udc->usb_state) != USB_STATE_SUSPENDED)
+			suspend_irq(udc);
+	} else {
+		if (udc->usb_state == USB_STATE_SUSPENDED)
+			resume_irq(udc);
+	}
+}
+
+static int reset_irq(struct qe_udc *udc)
+{
+	unsigned char i;
+
+	qe_usb_disable();
+	out_8(&udc->usb_regs->usb_usadr, 0);
+
+	for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
+		if (udc->eps[i].init)
+			qe_ep_reset(udc, i);
+	}
+
+	reset_queues(udc);
+	udc->usb_state = USB_STATE_DEFAULT;
+	udc->ep0_state = WAIT_FOR_SETUP;
+	udc->ep0_dir = USB_DIR_OUT;
+	qe_usb_enable();
+	return 0;
+}
+
+static int bsy_irq(struct qe_udc *udc)
+{
+	return 0;
+}
+
+static int txe_irq(struct qe_udc *udc)
+{
+	return 0;
+}
+
+/* ep0 tx interrupt also in here */
+static int tx_irq(struct qe_udc *udc)
+{
+	struct qe_ep *ep;
+	int i, res = 0;
+	struct qe_bd *bd;
+
+	if ((udc->usb_state == USB_STATE_ADDRESS)
+		&& (in_8(&udc->usb_regs->usb_usadr) == 0))
+		out_8(&udc->usb_regs->usb_usadr, udc->device_address);
+
+	for (i = (USB_MAX_ENDPOINTS-1); ((i >= 0) && (res == 0)); i--) {
+		ep = &(udc->eps[i]);
+		if (ep && ep->init && (ep->dir != USB_DIR_OUT)) {
+			bd = ep->c_txbd;
+			if (!(BD_STATUS_AND_LENGTH(bd)&T_R)
+						&& (BD_BUFFER(bd))) {
+				/* Disable the TX Interrupt */
+				/*confirm the transmitted bd*/
+				if (ep->epnum == 0)
+					res = qe_ep0_txconf(ep);
+				else
+					res = qe_ep_txconf(ep);
+				/* Enable the TX Interrupt */
+			}
+		}
+	}
+	return res;
+}
+
+
+/* setup packect's rx is handle in the function too */
+static void rx_irq(struct qe_udc *udc)
+{
+	struct qe_ep *ep;
+	int i;
+	struct qe_bd *bd;
+
+	for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
+		ep = &(udc->eps[i]);
+		if (ep && ep->init && (ep->dir != USB_DIR_IN)) {
+			bd = ep->n_rxbd;
+			if (!(BD_STATUS_AND_LENGTH(bd)&R_E)
+						&& (BD_BUFFER(bd))) {
+				if (ep->epnum == 0) {
+					qe_ep0_rx(udc);
+				} else {
+					/*non-setup package receive*/
+					qe_ep_rx(ep);
+				}
+			}
+		}
+	}
+}
+
+static irqreturn_t qe_udc_irq(int irq, void *_udc)
+{
+	struct qe_udc *udc = (struct qe_udc *)_udc;
+	u16 irq_src;
+	irqreturn_t status = IRQ_NONE;
+	unsigned long flags;
+
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	irq_src = in_be16(&udc->usb_regs->usb_usber) &
+		in_be16(&udc->usb_regs->usb_usbmr);
+	/* Clear notification bits */
+	out_be16(&udc->usb_regs->usb_usber, irq_src);
+	/* USB Interrupt */
+	if (irq_src & USB_E_IDLE_MASK) {
+		idle_irq(udc);
+		irq_src &= ~USB_E_IDLE_MASK;
+		status = IRQ_HANDLED;
+	}
+
+	if (irq_src & USB_E_TXB_MASK) {
+		tx_irq(udc);
+		irq_src &= ~USB_E_TXB_MASK;
+		status = IRQ_HANDLED;
+	}
+
+	if (irq_src & USB_E_RXB_MASK) {
+		rx_irq(udc);
+		irq_src &= ~USB_E_RXB_MASK;
+		status = IRQ_HANDLED;
+	}
+
+	if (irq_src & USB_E_RESET_MASK) {
+		reset_irq(udc);
+		irq_src &= ~USB_E_RESET_MASK;
+		status = IRQ_HANDLED;
+	}
+
+	if (irq_src & USB_E_BSY_MASK) {
+		bsy_irq(udc);
+		irq_src &= ~USB_E_BSY_MASK;
+		status = IRQ_HANDLED;
+	}
+
+	if (irq_src & USB_E_TXE_MASK) {
+		txe_irq(udc);
+		irq_src &= ~USB_E_TXE_MASK;
+		status = IRQ_HANDLED;
+	}
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return status;
+}
+
+/*-------------------------------------------------------------------------
+	Gadget driver register and unregister.
+ --------------------------------------------------------------------------*/
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+	int retval;
+	unsigned long flags = 0;
+
+	/* standard operations */
+	if (!udc_controller)
+		return -ENODEV;
+
+	if (!driver || (driver->speed != USB_SPEED_FULL
+			&& driver->speed != USB_SPEED_HIGH)
+			|| !driver->bind || !driver->unbind ||
+			!driver->disconnect || !driver->setup)
+		return -EINVAL;
+
+	if (udc_controller->driver)
+		return -EBUSY;
+
+	/* lock is needed but whether should use this lock or another */
+	spin_lock_irqsave(&udc_controller->lock, flags);
+
+	driver->driver.bus = 0;
+	/* hook up the driver */
+	udc_controller->driver = driver;
+	udc_controller->gadget.dev.driver = &driver->driver;
+	udc_controller->gadget.speed = (enum usb_device_speed)(driver->speed);
+	spin_unlock_irqrestore(&udc_controller->lock, flags);
+
+	retval = driver->bind(&udc_controller->gadget);
+	if (retval) {
+		dev_err(udc_controller->dev, "bind to %s --> %d",
+				driver->driver.name, retval);
+		udc_controller->gadget.dev.driver = 0;
+		udc_controller->driver = 0;
+		return retval;
+	}
+
+	/* Enable IRQ reg and Set usbcmd reg EN bit */
+	qe_usb_enable();
+
+	out_be16(&udc_controller->usb_regs->usb_usber, 0xffff);
+	out_be16(&udc_controller->usb_regs->usb_usbmr, USB_E_DEFAULT_DEVICE);
+	udc_controller->usb_state = USB_STATE_ATTACHED;
+	udc_controller->ep0_state = WAIT_FOR_SETUP;
+	udc_controller->ep0_dir = USB_DIR_OUT;
+	dev_info(udc_controller->dev, "%s bind to driver %s \n",
+		udc_controller->gadget.name, driver->driver.name);
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+	struct qe_ep *loop_ep;
+	unsigned long flags;
+
+	if (!udc_controller)
+		return -ENODEV;
+
+	if (!driver || driver != udc_controller->driver)
+		return -EINVAL;
+
+	/* stop usb controller, disable intr */
+	qe_usb_disable();
+
+	/* in fact, no needed */
+	udc_controller->usb_state = USB_STATE_ATTACHED;
+	udc_controller->ep0_state = WAIT_FOR_SETUP;
+	udc_controller->ep0_dir = 0;
+
+	/* stand operation */
+	spin_lock_irqsave(&udc_controller->lock, flags);
+	udc_controller->gadget.speed = USB_SPEED_UNKNOWN;
+	nuke(&udc_controller->eps[0], -ESHUTDOWN);
+	list_for_each_entry(loop_ep, &udc_controller->gadget.ep_list,
+				ep.ep_list)
+		nuke(loop_ep, -ESHUTDOWN);
+	spin_unlock_irqrestore(&udc_controller->lock, flags);
+
+	/* unbind gadget and unhook driver. */
+	driver->unbind(&udc_controller->gadget);
+	udc_controller->gadget.dev.driver = NULL;
+	udc_controller->driver = NULL;
+
+	dev_info(udc_controller->dev, "unregistered gadget driver '%s'\r\n",
+			driver->driver.name);
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+/* udc structure's alloc and setup, include ep-param alloc */
+static void *qe_udc_config(struct of_device *ofdev)
+{
+	struct qe_udc *udc = NULL;
+	struct device_node *np = ofdev->node;
+	unsigned int tmp_addr = 0;
+	struct usb_device_para __iomem *usbpram = NULL;
+	unsigned int i;
+	u64 size;
+	u32 offset;
+	unsigned int nsize;
+	const unsigned int *prop;
+
+	udc = kzalloc(sizeof(struct qe_udc), GFP_KERNEL);
+	if (udc == NULL) {
+		dev_err(&ofdev->dev, "malloc udc failed\n");
+		goto cleanup;
+	}
+
+	udc->dev = &ofdev->dev;
+
+	/* use the default address for the usb parameter */
+	prop = of_get_property(np, "reg", &nsize);
+	offset = of_read_number(prop + 2, 1);
+	size = of_read_number(prop + 3, 1);
+
+	udc->usb_param = muram_addr(offset);
+
+	memset_io(udc->usb_param, 0, size);
+
+	usbpram = udc->usb_param;
+
+	out_be16(&usbpram->frame_n, 0);
+	out_be32(&usbpram->rstate, 0);
+
+	tmp_addr = qe_muram_alloc((USB_MAX_ENDPOINTS *
+					sizeof(struct usb_ep_para)),
+					   USB_EP_PARA_ALIGNMENT);
+
+	for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
+		usbpram->epptr[i] = (u16)tmp_addr;
+		udc->ep_param[i] = (struct usb_ep_para *)muram_addr(tmp_addr);
+		tmp_addr += 32;
+	}
+
+	memset_io(udc->ep_param[0], 0,
+			USB_MAX_ENDPOINTS * sizeof(struct usb_ep_para));
+
+	udc->resume_state = USB_STATE_NOTATTACHED;
+	udc->usb_state = USB_STATE_POWERED;
+	udc->ep0_dir = 0;
+
+	/* initliaze the qe_udc lock */
+	spin_lock_init(&udc->lock);
+	return udc;
+
+cleanup:
+	kfree(udc);
+	return NULL;
+}
+
+/* USB Controller register init */
+static int qe_udc_reg_init(struct qe_udc *udc)
+{
+	struct usb_ctlr __iomem *qe_usbregs;
+	qe_usbregs = udc->usb_regs;
+
+	/* Init the usb register */
+	out_8(&qe_usbregs->usb_usmod, 0x01); /* FIXME if need enable in here */
+	out_be16(&qe_usbregs->usb_usbmr, 0);
+	out_8(&qe_usbregs->usb_uscom, 0);
+	out_be16(&qe_usbregs->usb_usber, USBER_ALL_CLEAR);
+
+	return 0;
+}
+
+static int qe_ep_config(struct qe_udc *udc, unsigned char pipe_num)
+{
+	struct qe_ep *ep = &(udc->eps[pipe_num]);
+
+	ep->udc = udc;
+	strcpy(ep->name, ep_name[pipe_num]);
+	ep->ep.name = ep_name[pipe_num];
+
+	ep->ep.ops = &qe_ep_ops;
+	ep->stopped = 1;
+	ep->ep.maxpacket = (unsigned short) ~0;
+	ep->desc = NULL;
+	ep->dir = 0xff;
+	ep->epnum = (u8)pipe_num;
+	ep->sent = 0;
+	ep->last = 0;
+	ep->init = 0;
+	ep->rxframe = NULL;
+	ep->txframe = NULL;
+	ep->tx_req = NULL;
+	ep->state = EP_STATE_IDLE;
+	ep->has_data = 0;
+
+	/* the queue lists any req for this ep */
+	INIT_LIST_HEAD(&ep->queue);
+
+	/* gagdet.ep_list used for ep_autoconfig so no ep0*/
+	if (pipe_num != 0)
+		list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
+
+	ep->gadget = &udc->gadget;
+
+	return 0;
+}
+
+/*-----------------------------------------------------------------------
+ *	UDC device Driver operation functions				*
+ *----------------------------------------------------------------------*/
+static void qe_udc_release(struct device *dev)
+{
+	int i = 0;
+
+	complete(udc_controller->done);
+	qe_muram_free(muram_offset(&udc_controller->ep_param[0]));
+	for (i = 0; i < USB_MAX_ENDPOINTS; i++)
+		udc_controller->ep_param[i] = NULL;
+
+	kfree(udc_controller);
+	udc_controller = NULL;
+}
+
+/* Driver probe functions */
+static int qe_udc_probe(struct of_device *ofdev,
+			const struct of_device_id *match)
+{
+	struct device_node *np = ofdev->node;
+	unsigned int tmp_status = -ENODEV;
+	unsigned int i;
+
+	/* Initialize the udc structure including QH member and other member */
+	udc_controller = (struct qe_udc *)qe_udc_config(ofdev);
+	if (!udc_controller) {
+		VDBG("udc_controll is NULL");
+		return -ENOMEM;
+	}
+
+	udc_controller->usb_regs = of_iomap(np, 0);
+
+	/* initialize usb hw reg except for regs for EP,
+	 * leave usbintr reg untouched*/
+	qe_udc_reg_init(udc_controller);
+
+	/* here comes the stand operations for probe
+	 * set the qe_udc->gadget.xxx */
+	udc_controller->gadget.ops = &qe_gadget_ops;
+
+	/* gadget.ep0 is a pointer */
+	udc_controller->gadget.ep0 = &(udc_controller->eps[0].ep);
+
+	INIT_LIST_HEAD(&udc_controller->gadget.ep_list);
+
+	/* modify in register gadget process */
+	udc_controller->gadget.speed = USB_SPEED_UNKNOWN;
+
+	/* name: Identifies the controller hardware type. */
+	udc_controller->gadget.name = driver_name;
+
+	device_initialize(&udc_controller->gadget.dev);
+
+	strcpy(udc_controller->gadget.dev.bus_id, "gadget");
+
+	udc_controller->gadget.dev.release = qe_udc_release;
+	udc_controller->gadget.dev.parent = &ofdev->dev;
+
+
+	/* EP:intialization qe_ep struct */
+	for (i = 0; i < USB_MAX_ENDPOINTS ; i++) {
+		/*because the ep type isn't decide here so
+		 * qe_ep_init() should be called in ep_enable() */
+
+		/* setup the qe_ep struct and link ep.ep.list
+		 * into gadget.ep_list */
+		qe_ep_config(udc_controller, (unsigned char)i);
+	}
+
+	/* ep0 initialization in here */
+	qe_ep_init(udc_controller, 0, &qe_ep0_desc);
+
+	/* create a buf for ZLP send */
+	udc_controller->nullbuf = kzalloc(256, GFP_KERNEL);
+	if (udc_controller->nullbuf == NULL) {
+		VDBG("cannot alloc nullbuf");
+		return -ENOMEM;
+	}
+
+	udc_controller->nullp = virt_to_phys((void *)udc_controller->nullbuf);
+	if (udc_controller->nullp == DMA_ADDR_INVALID) {
+		udc_controller->nullp = dma_map_single(
+					udc_controller->gadget.dev.parent,
+					udc_controller->nullbuf,
+					256,
+					DMA_TO_DEVICE);
+		udc_controller->nullmap = 1;
+	} else {
+		dma_sync_single_for_device(udc_controller->gadget.dev.parent,
+					udc_controller->nullp, 256,
+					DMA_TO_DEVICE);
+		udc_controller->nullmap = 1;
+	}
+
+	tasklet_init(&udc_controller->rx_tasklet, ep_rx_tasklet,
+			(unsigned long)udc_controller);
+	/* request irq and disable DR  */
+	udc_controller->usb_irq = irq_of_parse_and_map(np, 0);
+
+	tmp_status = request_irq(udc_controller->usb_irq, qe_udc_irq, 0,
+				driver_name, udc_controller);
+	if (tmp_status != 0) {
+		dev_err(udc_controller->dev, "cannot request irq %d err %d \n",
+			udc_controller->usb_irq, tmp_status);
+		return tmp_status;
+	}
+
+	tmp_status = device_add(&udc_controller->gadget.dev);
+	if (tmp_status != 0)
+		return tmp_status;
+
+	dev_info(udc_controller->dev,
+			"QE/CPM USB controller initialized as device\n");
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int qe_udc_suspend(struct of_device *dev, pm_message_t state)
+{
+	return -ENOTSUPP;
+}
+
+static int qe_udc_resume(struct of_device *dev)
+{
+	return -ENOTSUPP;
+}
+#endif
+
+static int qe_udc_remove(struct of_device *ofdev)
+{
+	struct qe_ep *ep = NULL;
+	unsigned int size;
+
+	DECLARE_COMPLETION(done);
+
+	if (!udc_controller)
+		return -ENODEV;
+
+	udc_controller->done = &done;
+	tasklet_disable(&udc_controller->rx_tasklet);
+
+	if (udc_controller->nullmap) {
+		dma_unmap_single(udc_controller->gadget.dev.parent,
+			udc_controller->nullp, 256,
+				DMA_TO_DEVICE);
+			udc_controller->nullp = DMA_ADDR_INVALID;
+	} else
+		dma_sync_single_for_cpu(udc_controller->gadget.dev.parent,
+			udc_controller->nullp, 256,
+				DMA_TO_DEVICE);
+	kfree(udc_controller->nullbuf);
+
+	ep = &(udc_controller->eps[0]);
+	qe_muram_free(muram_offset(ep->rxbase));
+	size = (ep->ep.maxpacket + USB_CRC_SIZE + 2)*(USB_BDRING_LEN + 1);
+
+	kfree(ep->rxframe);
+	if (ep->rxbufmap) {
+		dma_unmap_single(udc_controller->gadget.dev.parent,
+				ep->rxbuf_d, size,
+				DMA_FROM_DEVICE);
+		ep->rxbuf_d = DMA_ADDR_INVALID;
+	} else
+		dma_sync_single_for_cpu(udc_controller->gadget.dev.parent,
+				ep->rxbuf_d, size,
+				DMA_FROM_DEVICE);
+
+	kfree(ep->rxbuffer);
+	kfree(ep->txframe);
+
+	/* free irq */
+	free_irq(udc_controller->usb_irq, udc_controller);
+
+	tasklet_kill(&udc_controller->rx_tasklet);
+
+	device_unregister(&udc_controller->gadget.dev);
+	if (udc_controller != NULL)
+		kfree(udc_controller);
+
+	/* free udc --wait for the release() finished */
+	wait_for_completion(&done);
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static struct of_device_id qe_udc_match[] = {
+	{
+		.compatible = "fsl,qe_udc",
+	},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, qe_udc_match);
+
+static struct of_platform_driver udc_driver = {
+	.name           = (char *) driver_name,
+	.match_table    = qe_udc_match,
+	.probe          = qe_udc_probe,
+	.remove         = qe_udc_remove,
+#ifdef CONFIG_PM
+	.suspend        = qe_udc_suspend,
+	.resume         = qe_udc_resume,
+#endif
+};
+
+static int __init qe_udc_init(void)
+{
+	printk(KERN_INFO "%s: %s, %s\n", driver_name, driver_desc,
+			DRIVER_VERSION);
+	return of_register_platform_driver(&udc_driver);
+}
+
+static void __exit qe_udc_exit(void)
+{
+	of_unregister_platform_driver(&udc_driver);
+}
+
+module_init(qe_udc_init);
+module_exit(qe_udc_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/usb/gadget/fsl_qe_udc.h b/drivers/usb/gadget/fsl_qe_udc.h
new file mode 100644
index 0000000..8b333f9
--- /dev/null
+++ b/drivers/usb/gadget/fsl_qe_udc.h
@@ -0,0 +1,458 @@
+/*
+ * drivers/usb/gadget/qe_udc.h
+ *
+ * Copyright (C) 2006-2008 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Author: Xiaobo Xie <X.Xie@freescale.com>
+ *
+ * Description:
+ * Freescale USB device/endpoint management registers
+ *
+ * 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.
+ */
+
+#ifndef __FSL_QE_UDC_H
+#define __FSL_QE_UDC_H
+
+#ifdef CONFIG_CPM2
+extern unsigned long cpm2_immr;
+
+static inline void __iomem *muram_addr(unsigned long offset)
+{
+	return (void __iomem *)cpm2_immr + offset;
+}
+
+static inline unsigned long muram_offset(void __iomem *addr)
+{
+	return addr - (void __iomem *)cpm2_immr;
+}
+#else
+#define muram_addr	qe_muram_addr
+#define muram_offset	qe_muram_offset
+#endif
+
+#define USB_MAX_ENDPOINTS               4
+#define USB_MAX_PIPES                   USB_MAX_ENDPOINTS
+#define USB_EP0_MAX_SIZE		64
+#define USB_MAX_CTRL_PAYLOAD            0x4000
+#define USB_BDRING_LEN			16
+#define USB_BDRING_LEN_RX		256
+#define USB_BDRING_LEN_TX		16
+#define MIN_EMPTY_BDS			128
+#define MAX_DATA_BDS			8
+#define USB_CRC_SIZE			2
+#define USB_DIR_BOTH			0x88
+#define R_BUF_MAXSIZE			0x800
+#define USB_EP_PARA_ALIGNMENT		32
+
+/* USB Mode Register bit define */
+#define USB_MODE_EN		0x01
+#define USB_MODE_HOST		0x02
+#define USB_MODE_TEST		0x04
+#define USB_MODE_SFTE		0x08
+#define USB_MODE_RESUME		0x40
+#define USB_MODE_LSS		0x80
+
+/* USB Slave Address Register Mask */
+#define USB_SLVADDR_MASK	0x7F
+
+/* USB Endpoint register define */
+#define USB_EPNUM_MASK		0xF000
+#define USB_EPNUM_SHIFT		12
+
+#define USB_TRANS_MODE_SHIFT	8
+#define USB_TRANS_CTR		0x0000
+#define USB_TRANS_INT		0x0100
+#define USB_TRANS_BULK		0x0200
+#define USB_TRANS_ISO		0x0300
+
+#define USB_EP_MF		0x0020
+#define USB_EP_RTE		0x0010
+
+#define USB_THS_SHIFT		2
+#define USB_THS_MASK		0x000c
+#define USB_THS_NORMAL		0x0
+#define USB_THS_IGNORE_IN	0x0004
+#define USB_THS_NACK		0x0008
+#define USB_THS_STALL		0x000c
+
+#define USB_RHS_SHIFT   	0
+#define USB_RHS_MASK		0x0003
+#define USB_RHS_NORMAL  	0x0
+#define USB_RHS_IGNORE_OUT	0x0001
+#define USB_RHS_NACK		0x0002
+#define USB_RHS_STALL		0x0003
+
+#define USB_RTHS_MASK		0x000f
+
+/* USB Command Register define */
+#define USB_CMD_STR_FIFO	0x80
+#define USB_CMD_FLUSH_FIFO	0x40
+#define USB_CMD_ISFT		0x20
+#define USB_CMD_DSFT		0x10
+#define USB_CMD_EP_MASK		0x03
+
+/* USB Event and Mask Register define */
+#define USB_E_MSF_MASK		0x0800
+#define USB_E_SFT_MASK		0x0400
+#define USB_E_RESET_MASK	0x0200
+#define USB_E_IDLE_MASK		0x0100
+#define USB_E_TXE4_MASK		0x0080
+#define USB_E_TXE3_MASK		0x0040
+#define USB_E_TXE2_MASK		0x0020
+#define USB_E_TXE1_MASK		0x0010
+#define USB_E_SOF_MASK		0x0008
+#define USB_E_BSY_MASK		0x0004
+#define USB_E_TXB_MASK		0x0002
+#define USB_E_RXB_MASK		0x0001
+#define USBER_ALL_CLEAR 	0x0fff
+
+#define USB_E_DEFAULT_DEVICE   (USB_E_RESET_MASK | USB_E_TXE4_MASK | \
+				USB_E_TXE3_MASK | USB_E_TXE2_MASK | \
+				USB_E_TXE1_MASK | USB_E_BSY_MASK | \
+				USB_E_TXB_MASK | USB_E_RXB_MASK)
+
+#define USB_E_TXE_MASK         (USB_E_TXE4_MASK | USB_E_TXE3_MASK|\
+				 USB_E_TXE2_MASK | USB_E_TXE1_MASK)
+/* USB Status Register define */
+#define USB_IDLE_STATUS_MASK	0x01
+
+/* USB Start of Frame Timer */
+#define USB_USSFT_MASK		0x3FFF
+
+/* USB Frame Number Register */
+#define USB_USFRN_MASK		0xFFFF
+
+struct usb_device_para{
+	u16	epptr[4];
+	u32	rstate;
+	u32	rptr;
+	u16	frame_n;
+	u16	rbcnt;
+	u32	rtemp;
+	u32	rxusb_data;
+	u16	rxuptr;
+	u8	reso[2];
+	u32	softbl;
+	u8	sofucrctemp;
+};
+
+struct usb_ep_para{
+	u16	rbase;
+	u16	tbase;
+	u8	rbmr;
+	u8	tbmr;
+	u16	mrblr;
+	u16	rbptr;
+	u16	tbptr;
+	u32	tstate;
+	u32	tptr;
+	u16	tcrc;
+	u16	tbcnt;
+	u32	ttemp;
+	u16	txusbu_ptr;
+	u8	reserve[2];
+};
+
+#define USB_BUSMODE_GBL		0x20
+#define USB_BUSMODE_BO_MASK	0x18
+#define USB_BUSMODE_BO_SHIFT	0x3
+#define USB_BUSMODE_BE		0x2
+#define USB_BUSMODE_CETM	0x04
+#define USB_BUSMODE_DTB		0x02
+
+/* Endpoint basic handle */
+#define ep_index(EP)		((EP)->desc->bEndpointAddress&0xF)
+#define ep_maxpacket(EP)	((EP)->ep.maxpacket)
+#define ep_is_in(EP)	((ep_index(EP) == 0) ? (EP->udc->ep0_dir == \
+			USB_DIR_IN):((EP)->desc->bEndpointAddress \
+			& USB_DIR_IN) == USB_DIR_IN)
+
+/* ep0 transfer state */
+#define WAIT_FOR_SETUP          0
+#define DATA_STATE_XMIT         1
+#define DATA_STATE_NEED_ZLP     2
+#define WAIT_FOR_OUT_STATUS     3
+#define DATA_STATE_RECV         4
+
+/* ep tramsfer mode */
+#define USBP_TM_CTL	0
+#define USBP_TM_ISO	1
+#define USBP_TM_BULK	2
+#define USBP_TM_INT	3
+
+/*-----------------------------------------------------------------------------
+	USB RX And TX DATA Frame
+ -----------------------------------------------------------------------------*/
+struct qe_frame{
+	u8 *data;
+	u32 len;
+	u32 status;
+	u32 info;
+
+	void *privdata;
+	struct list_head node;
+};
+
+/* Frame structure, info field. */
+#define PID_DATA0              0x80000000 /* Data toggle zero */
+#define PID_DATA1              0x40000000 /* Data toggle one  */
+#define PID_SETUP              0x20000000 /* setup bit */
+#define SETUP_STATUS           0x10000000 /* setup status bit */
+#define SETADDR_STATUS         0x08000000 /* setupup address status bit */
+#define NO_REQ                 0x04000000 /* Frame without request */
+#define HOST_DATA              0x02000000 /* Host data frame */
+#define FIRST_PACKET_IN_FRAME  0x01000000 /* first packet in the frame */
+#define TOKEN_FRAME            0x00800000 /* Host token frame */
+#define ZLP                    0x00400000 /* Zero length packet */
+#define IN_TOKEN_FRAME         0x00200000 /* In token package */
+#define OUT_TOKEN_FRAME        0x00100000 /* Out token package */
+#define SETUP_TOKEN_FRAME      0x00080000 /* Setup token package */
+#define STALL_FRAME            0x00040000 /* Stall handshake */
+#define NACK_FRAME             0x00020000 /* Nack handshake */
+#define NO_PID                 0x00010000 /* No send PID */
+#define NO_CRC                 0x00008000 /* No send CRC */
+#define HOST_COMMAND           0x00004000 /* Host command frame   */
+
+/* Frame status field */
+/* Receive side */
+#define FRAME_OK               0x00000000 /* Frame tranmitted or received OK */
+#define FRAME_ERROR            0x80000000 /* Error occured on frame */
+#define START_FRAME_LOST       0x40000000 /* START_FRAME_LOST */
+#define END_FRAME_LOST         0x20000000 /* END_FRAME_LOST */
+#define RX_ER_NONOCT           0x10000000 /* Rx Non Octet Aligned Packet */
+#define RX_ER_BITSTUFF         0x08000000 /* Frame Aborted --Received packet
+						with bit stuff error */
+#define RX_ER_CRC              0x04000000 /* Received packet with CRC error */
+#define RX_ER_OVERUN           0x02000000 /* Over-run occured on reception */
+#define RX_ER_PID              0x01000000 /* Wrong PID received */
+/* Tranmit side */
+#define TX_ER_NAK              0x00800000 /* Received NAK handshake */
+#define TX_ER_STALL            0x00400000 /* Received STALL handshake */
+#define TX_ER_TIMEOUT          0x00200000 /* Transmit time out */
+#define TX_ER_UNDERUN          0x00100000 /* Transmit underrun */
+#define FRAME_INPROGRESS       0x00080000 /* Frame is being transmitted */
+#define ER_DATA_UNDERUN        0x00040000 /* Frame is shorter then expected */
+#define ER_DATA_OVERUN         0x00020000 /* Frame is longer then expected */
+
+/* QE USB frame operation functions */
+#define frame_get_length(frm) (((struct qe_frame *)frm)->len)
+#define frame_set_length(frm, leng) (((struct qe_frame *)frm)->len = leng)
+#define frame_get_data(frm) (((struct qe_frame *)frm)->data)
+#define frame_set_data(frm, dat) (((struct qe_frame *)frm)->data = dat)
+#define frame_get_info(frm) (((struct qe_frame *)frm)->info)
+#define frame_set_info(frm, inf) (((struct qe_frame *)frm)->info = inf)
+#define frame_get_status(frm) (((struct qe_frame *)frm)->status)
+#define frame_set_status(frm, stat) (((struct qe_frame *)frm)->status = stat)
+#define frame_get_privdata(frm) (((struct qe_frame *)frm)->privdata)
+#define frame_set_privdata(frm, dat) (((struct qe_frame *)frm)->privdata = dat)
+
+#define qe_frame_clean(frm)                     \
+	do {                                  \
+		frame_set_data(frm, NULL);         \
+		frame_set_length(frm, 0);          \
+		frame_set_status(frm, FRAME_OK);   \
+		frame_set_info(frm, 0);            \
+		frame_set_privdata(frm, 0);        \
+	} while (0)
+
+#define qe_frame_init(frm)                                   \
+	do {                                               \
+		qe_frame_clean(frm);                         \
+		INIT_LIST_HEAD(&(((struct qe_frame *)frm)->node)); \
+	} while (0)
+
+struct qe_req {
+	struct usb_request req;
+	struct list_head queue;
+	/* ep_queue() func will add
+	 a request->queue into a udc_ep->queue 'd tail */
+	struct qe_ep *ep;
+	unsigned mapped:1;
+};
+
+struct qe_ep {
+	struct usb_ep ep;
+	struct list_head queue;
+	struct qe_udc *udc;
+	const struct usb_endpoint_descriptor *desc;
+	struct usb_gadget *gadget;
+
+	u8 state;
+
+	struct qe_bd *rxbase;
+	struct qe_bd *n_rxbd;
+	struct qe_bd *e_rxbd;
+
+	struct qe_bd *txbase;
+	struct qe_bd *n_txbd;
+	struct qe_bd *c_txbd;
+
+	struct qe_frame *rxframe;
+	u8 *rxbuffer;
+	dma_addr_t rxbuf_d;
+	u8 rxbufmap;
+	unsigned char localnack;
+	int has_data;
+
+	struct qe_frame *txframe;
+	struct qe_req *tx_req;
+	int sent;  /*data already sent */
+	int last;  /*data sent in the last time*/
+
+	u8 dir;
+	u8 epnum;
+	u8 tm; /* transfer mode */
+	u8 data01;
+	u8 init;
+
+	u8 already_seen;
+	u8 enable_tasklet;
+	u8 setup_stage;
+	u32 last_io;            /* timestamp */
+
+	char name[14];
+
+	unsigned double_buf:1;
+	unsigned stopped:1;
+	unsigned fnf:1;
+	unsigned has_dma:1;
+
+	u8 ackwait;
+	u8 dma_channel;
+	u16 dma_counter;
+	int lch;
+
+	struct timer_list timer;
+};
+
+struct qe_udc {
+	struct usb_gadget gadget;
+	struct usb_gadget_driver *driver;
+	struct device *dev;
+	struct qe_ep eps[USB_MAX_ENDPOINTS];
+	struct usb_ctrlrequest local_setup_buff;
+	spinlock_t lock;	/* lock for set/config qe_udc */
+
+	struct qe_req *status_req;     /* ep0 status request */
+
+	/* USB and EP Parameter Block pointer */
+	struct usb_device_para __iomem *usb_param;
+	struct usb_ep_para __iomem *ep_param[4];
+
+	u32 max_pipes;          /* Device max pipes */
+	u32 max_use_endpts;     /* Max endpointes to be used */
+	u32 bus_reset;          /* Device is bus reseting */
+	u32 resume_state;       /* USB state to resume*/
+	u32 usb_state;          /* USB current state */
+	u32 usb_next_state;     /* USB next state */
+	u32 ep0_state;          /* Enpoint zero state */
+	u32 ep0_dir;            /* Enpoint zero direction: can be
+				USB_DIR_IN or USB_DIR_OUT*/
+	u32 usb_sof_count;      /* SOF count */
+	u32 errors;             /* USB ERRORs count */
+
+	u8 *tmpbuf;
+	u32 c_start;
+	u32 c_end;
+
+	u8 *nullbuf;
+	dma_addr_t nullp;
+	u8 nullmap;
+	u8 device_address;	/* Device USB address */
+
+	unsigned int usb_clock;
+	unsigned int usb_irq;
+	struct usb_ctlr __iomem *usb_regs;
+
+	struct tasklet_struct rx_tasklet;
+
+	struct completion *done;	/* to make sure release() is done */
+};
+
+#define EP_STATE_IDLE	0
+#define EP_STATE_NACK	1
+#define EP_STATE_STALL	2
+
+/*---------------------------------------------------------------------
+ * 		Mask definitions for usb BD                           *
+ *--------------------------------------------------------------------*/
+#define QE_SIZEOF_BD       sizeof(struct qe_bd)
+
+#define BD_BUFFER_ARG(bd)                   (((struct qe_bd *)bd)->buf)
+#define BD_BUFFER_CLEAR(bd)                 out_be32(&(BD_BUFFER_ARG(bd)), 0);
+#define BD_BUFFER(bd)                       in_be32(&(BD_BUFFER_ARG(bd)))
+#define BD_STATUS_AND_LENGTH_SET(bd, val)   out_be32((u32 *)bd, val)
+#define BD_STATUS_AND_LENGTH(bd)            in_be32((u32 *)bd)
+#define BD_BUFFER_SET(bd, buffer)           out_be32(&(BD_BUFFER_ARG(bd)), \
+							(u32)(buffer))
+
+/*
+ * transmit BD's status
+ */
+#define T_R           0x80000000         /* ready bit */
+#define T_W           0x20000000         /* wrap bit */
+#define T_I           0x10000000         /* interrupt on completion */
+#define T_L           0x08000000         /* last */
+#define T_TC          0x04000000         /* transmit CRC */
+#define T_CNF         0x02000000         /* wait for  transmit confirm */
+#define T_LSP         0x01000000         /* Low-speed transaction */
+#define T_PID         0x00c00000         /* packet id */
+#define T_NAK         0x00100000         /* No ack. */
+#define T_STAL        0x00080000         /* Stall recieved */
+#define T_TO          0x00040000         /* time out */
+#define T_UN          0x00020000         /* underrun */
+
+#define DEVICE_T_ERROR    (T_UN | T_TO)
+#define HOST_T_ERROR      (T_UN | T_TO | T_NAK | T_STAL)
+#define DEVICE_T_BD_MASK  DEVICE_T_ERROR
+#define HOST_T_BD_MASK    HOST_T_ERROR
+
+#define T_PID_SHIFT   6
+#define T_PID_DATA0   0x00800000         /* Data 0 toggle */
+#define T_PID_DATA1   0x00c00000         /* Data 1 toggle */
+
+/*
+ * receive BD's status
+ */
+#define R_E           0x80000000         /* buffer empty */
+#define R_W           0x20000000         /* wrap bit */
+#define R_I           0x10000000         /* interrupt on reception */
+#define R_L           0x08000000         /* last */
+#define R_F           0x04000000         /* first */
+#define R_PID         0x00c00000         /* packet id */
+#define R_NO          0x00100000         /* Rx Non Octet Aligned Packet */
+#define R_AB          0x00080000         /* Frame Aborted */
+#define R_CR          0x00040000         /* CRC Error */
+#define R_OV          0x00020000         /* Overrun */
+
+#define R_ERROR       (R_NO | R_AB | R_CR | R_OV)
+#define R_BD_MASK     R_ERROR
+
+#define R_PID_DATA0   0x00000000
+#define R_PID_DATA1   0x00400000
+#define R_PID_SETUP   0x00800000
+
+/* Bulk only class request */
+#define USB_BULK_RESET_REQUEST          0xff
+
+extern int par_io_config_pin(u8  port, u8  pin, int dir, int open_drain,
+				int assignment, int has_irq);
+
+#ifdef DEBUG
+#define VDBG(fmt, args...)	printk(KERN_DEBUG "[%s]  " fmt "\n", \
+				__func__, ## args)
+#else
+#define VDBG(fmt, args...)       do {} while (0)
+#endif
+
+#define CPM_USB_STOP_TX 0x2e600000
+#define CPM_USB_RESTART_TX 0x2e600000
+#define CPM_USB_STOP_TX_OPCODE 0x0a
+#define CPM_USB_RESTART_TX_OPCODE 0x0b
+#define CPM_USB_EP_SHIFT 5
+
+#endif
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index 5246e8f..7e5be52 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -147,6 +147,13 @@
 #define	gadget_is_m66592(g)	0
 #endif
 
+/* Freescale CPM/QE UDC SUPPORT */
+#ifdef CONFIG_USB_GADGET_FSL_QE
+#define gadget_is_fsl_qe(g)	!strcmp("fsl_qe_udc", (g)->name)
+#else
+#define gadget_is_fsl_qe(g)	0
+#endif
+
 
 // CONFIG_USB_GADGET_SX2
 // CONFIG_USB_GADGET_AU1X00
@@ -212,6 +219,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
 		return 0x20;
 	else if (gadget_is_m66592(gadget))
 		return 0x21;
+	else if (gadget_is_fsl_qe(gadget))
+		return 0x22;
 	return -ENOENT;
 }
 
-- 
1.5.5.1.248.g4b17

^ permalink raw reply related	[flat|nested] 33+ messages in thread

end of thread, other threads:[~2008-09-24 22:19 UTC | newest]

Thread overview: 33+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-08-28  9:43 [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver Li Yang
2008-08-28 15:04 ` Arnd Bergmann
2008-08-28 17:22   ` Alan Stern
2008-08-28 17:53     ` Scott Wood
2008-08-28 20:16       ` Alan Stern
2008-08-28 22:27         ` Arnd Bergmann
2008-08-29 16:05           ` Alan Stern
2008-08-29 16:29             ` Arnd Bergmann
2008-08-29 17:19               ` Alan Stern
2008-08-29 17:38                 ` Arnd Bergmann
2008-08-29 21:22                   ` Alan Stern
2008-09-24 20:15           ` David Brownell
2008-08-29  8:57   ` Li Yang
2008-08-29  8:57     ` Arnd Bergmann
2008-09-24 20:10   ` David Brownell
2008-08-28 16:39 ` Scott Wood
2008-08-29  9:35   ` Li Yang
2008-09-01 13:52 ` Anton Vorontsov
2008-09-02  7:08   ` Li Yang
2008-09-01 16:34 ` Anton Vorontsov
2008-09-01 17:11 ` Anton Vorontsov
2008-09-02  7:35   ` Li Yang
2008-09-02  7:57     ` Joakim Tjernlund
2008-09-02  8:12       ` [PATCH] usb: add Freescale QE/CPM USB peripheral controllerdriver Li Yang-R58472
2008-09-02  8:15         ` Joakim Tjernlund
2008-09-24 20:28           ` David Brownell
2008-09-24 21:30             ` Joakim Tjernlund
2008-09-24 21:42               ` David Brownell
2008-09-24 20:26         ` David Brownell
  -- strict thread matches above, loose matches on Subject: below --
2008-08-06  7:16 [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver Li Yang
2008-08-06 15:15 ` Anton Vorontsov
2008-08-06 15:47 ` Timur Tabi
2008-08-15 14:16 ` Anton Vorontsov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).