From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from buildserver.ru.mvista.com (unknown [85.21.88.6]) by ozlabs.org (Postfix) with ESMTP id 8AE5CDE00D for ; Thu, 7 Aug 2008 01:15:40 +1000 (EST) Date: Wed, 6 Aug 2008 19:15:38 +0400 From: Anton Vorontsov To: Li Yang Subject: Re: [PATCH] usb: add Freescale QE/CPM USB peripheral controller driver Message-ID: <20080806151538.GA1276@polina.dev.rtsoft.ru> References: <1218007000-27320-1-git-send-email-leoli@freescale.com> MIME-Version: 1.0 Content-Type: text/plain; charset=windows-1251 In-Reply-To: <1218007000-27320-1-git-send-email-leoli@freescale.com> Cc: david-b@pacbell.net, linuxppc-dev@ozlabs.org, gregkh@suse.de, linux-usb@vger.kernel.org, Xie Xiaobo Reply-To: avorontsov@ru.mvista.com List-Id: Linux on PowerPC Developers Mail List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , 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 > Signed-off-by: Li Yang 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 > + * Li Yang > + * 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 > +#include > +#include > +#include > +#include > +#include > +#include Don't see any calls to sleep or delay. > +#include > +#include > +#include > +#include > +#include Do you really need irq.h? This is somewhat internal header. > +#include > +#include Seems spurious also. > +#include > +#include Not sure about this one. > +#include > + > +#include > + > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > +#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 > + * > + * 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