[PATCH] usb: add Freescale QE/CPM USB peripheral controller driver
Anton Vorontsov
avorontsov at ru.mvista.com
Thu Aug 7 01:15:38 EST 2008
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 at freescale.com>
> Signed-off-by: Li Yang <leoli at 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 at freescale.com>
> + * Li Yang <leoli at 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 at 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 at gmail.com
irc://irc.freenode.net/bd2
More information about the Linuxppc-dev
mailing list