diff options
author | Magnus Damm <damm+renesas@opensource.se> | 2014-06-06 19:44:17 +0900 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-06-19 14:42:17 -0700 |
commit | 33aa8d45a4fea3dc5d90338aad0867a66e0c38d5 (patch) | |
tree | 08c2a0298dbc894fe11342168b8db08407e883d3 /drivers/staging/emxx_udc | |
parent | 22b5371412f91ea0e1bf5e1c1dd527eb3eed6dc2 (diff) |
staging: emxx_udc: Add Emma Mobile USB Gadget driver
Add the emxx_udc driver to staging based on an old linux-2.6.35.7
android tree. The driver has been brushed up slightly to complile
but it is still in great need of cleanup.
At this point DT bindings are clearly lacking and I doubt that the
driver even can run with multiple instances (global variables, hurray!).
Signed-off-by: Magnus Damm <damm+renesas@opensource.se>
Acked-by: Simon Horman <horms+renesas@verge.net.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/staging/emxx_udc')
-rw-r--r-- | drivers/staging/emxx_udc/Kconfig | 10 | ||||
-rw-r--r-- | drivers/staging/emxx_udc/Makefile | 1 | ||||
-rw-r--r-- | drivers/staging/emxx_udc/emxx_udc.c | 3559 | ||||
-rw-r--r-- | drivers/staging/emxx_udc/emxx_udc.h | 662 |
4 files changed, 4232 insertions, 0 deletions
diff --git a/drivers/staging/emxx_udc/Kconfig b/drivers/staging/emxx_udc/Kconfig new file mode 100644 index 000000000000..9bc6d3db86d9 --- /dev/null +++ b/drivers/staging/emxx_udc/Kconfig @@ -0,0 +1,10 @@ +config USB_EMXX + boolean "EMXX USB Function Device Controller" + depends on USB_GADGET && (ARCH_SHMOBILE || (ARM && COMPILE_TEST)) + help + The Emma Mobile series of SoCs from Renesas Electronics and + former NEC Electronics include USB Function hardware. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "emxx_udc" and force all + gadget drivers to also be dynamically linked. diff --git a/drivers/staging/emxx_udc/Makefile b/drivers/staging/emxx_udc/Makefile new file mode 100644 index 000000000000..6352724c0b57 --- /dev/null +++ b/drivers/staging/emxx_udc/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_USB_EMXX) := emxx_udc.o diff --git a/drivers/staging/emxx_udc/emxx_udc.c b/drivers/staging/emxx_udc/emxx_udc.c new file mode 100644 index 000000000000..b89c4115e570 --- /dev/null +++ b/drivers/staging/emxx_udc/emxx_udc.c @@ -0,0 +1,3559 @@ +/* + * drivers/usb/gadget/emxx_udc.c + * EMXX FCD (Function Controller Driver) for USB. + * + * Copyright (C) 2010 Renesas Electronics Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/interrupt.h> +#include <linux/proc_fs.h> +#include <linux/clk.h> +#include <linux/ctype.h> +#include <linux/string.h> +#include <linux/dma-mapping.h> +#include <linux/workqueue.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include <linux/irq.h> +#include <linux/gpio.h> + +#include "emxx_udc.h" + +#define DRIVER_DESC "EMXX UDC driver" +#define DMA_ADDR_INVALID (~(dma_addr_t)0) + +static const char driver_name[] = "emxx_udc"; +static const char driver_desc[] = DRIVER_DESC; + +/*===========================================================================*/ +/* Prototype */ +static void _nbu2ss_ep_dma_abort(struct nbu2ss_udc *, struct nbu2ss_ep *); +static void _nbu2ss_ep0_enable(struct nbu2ss_udc *); +/*static void _nbu2ss_ep0_disable(struct nbu2ss_udc *);*/ +static void _nbu2ss_ep_done(struct nbu2ss_ep *, struct nbu2ss_req *, int); +static void _nbu2ss_set_test_mode(struct nbu2ss_udc *, u32 mode); +static void _nbu2ss_endpoint_toggle_reset(struct nbu2ss_udc *udc, u8 ep_adrs); + +static int _nbu2ss_pullup(struct nbu2ss_udc *, int); +static void _nbu2ss_fifo_flush(struct nbu2ss_udc *, struct nbu2ss_ep *); + +/*===========================================================================*/ +/* Macro */ +#define _nbu2ss_zero_len_pkt(udc, epnum) \ + _nbu2ss_ep_in_end(udc, epnum, 0, 0) + + +/*===========================================================================*/ +/* Global */ +struct nbu2ss_udc udc_controller; + + +/*-------------------------------------------------------------------------*/ +/* Read */ +static inline u32 _nbu2ss_readl(void *address) +{ + return __raw_readl(address) ; +} + +/*-------------------------------------------------------------------------*/ +/* Write */ +static inline void _nbu2ss_writel(void *address, u32 udata) +{ + __raw_writel(udata, address) ; +} + +/*-------------------------------------------------------------------------*/ +/* Set Bit */ +static inline void _nbu2ss_bitset(void *address, u32 udata) +{ + u32 reg_dt = __raw_readl(address) | (udata); + __raw_writel(reg_dt, address) ; +} + +/*-------------------------------------------------------------------------*/ +/* Clear Bit */ +static inline void _nbu2ss_bitclr(void *address, u32 udata) +{ + u32 reg_dt = __raw_readl(address) & ~(udata); + __raw_writel(reg_dt, address) ; +} + +#ifdef UDC_DEBUG_DUMP +/*-------------------------------------------------------------------------*/ +static void _nbu2ss_dump_register(struct nbu2ss_udc *udc) +{ + int i; + u32 reg_data; + + pr_info("=== %s()\n", __func__); + + if (udc == NULL) { + ERR("%s udc == NULL\n", __func__); + return; + } + + spin_unlock(&udc->lock); + + printk(KERN_DEBUG "\n-USB REG-\n"); + for (i = 0x0 ; i < USB_BASE_SIZE ; i += 16) { + reg_data = _nbu2ss_readl( + (u32 *)IO_ADDRESS(USB_BASE_ADDRESS + i)); + printk(KERN_DEBUG "USB%04x =%08x", i, (int)reg_data); + + reg_data = _nbu2ss_readl( + (u32 *)IO_ADDRESS(USB_BASE_ADDRESS + i + 4)); + printk(KERN_DEBUG " %08x", (int)reg_data); + + reg_data = _nbu2ss_readl( + (u32 *)IO_ADDRESS(USB_BASE_ADDRESS + i + 8)); + printk(KERN_DEBUG " %08x", (int)reg_data); + + reg_data = _nbu2ss_readl( + (u32 *)IO_ADDRESS(USB_BASE_ADDRESS + i + 12)); + printk(KERN_DEBUG " %08x\n", (int)reg_data); + + } + + spin_lock(&udc->lock); +} +#endif /* UDC_DEBUG_DUMP */ + +/*-------------------------------------------------------------------------*/ +/* Endpoint 0 Callback (Complete) */ +static void _nbu2ss_ep0_complete(struct usb_ep *_ep, struct usb_request *_req) +{ + u8 recipient; + u16 selector; + u32 test_mode; + struct usb_ctrlrequest *p_ctrl; + struct nbu2ss_udc *udc; + + if ((_ep == NULL) || (_req == NULL)) + return; + + udc = (struct nbu2ss_udc *)_req->context; + p_ctrl = &udc->ctrl; + if ((p_ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + + if (p_ctrl->bRequest == USB_REQ_SET_FEATURE) { + /*-------------------------------------------------*/ + /* SET_FEATURE */ + recipient = (u8)(p_ctrl->bRequestType & USB_RECIP_MASK); + selector = p_ctrl->wValue; + if ((recipient == USB_RECIP_DEVICE) && + (selector == USB_DEVICE_TEST_MODE)) { + test_mode = (u32)(p_ctrl->wIndex >> 8); + _nbu2ss_set_test_mode(udc, test_mode); + } + } + } +} + +/*-------------------------------------------------------------------------*/ +/* Initialization usb_request */ +static void _nbu2ss_create_ep0_packet( + struct nbu2ss_udc *udc, + void *p_buf, + unsigned length +) +{ + udc->ep0_req.req.buf = p_buf; + udc->ep0_req.req.length = length; + udc->ep0_req.req.dma = 0; + udc->ep0_req.req.zero = TRUE; + udc->ep0_req.req.complete = _nbu2ss_ep0_complete; + udc->ep0_req.req.status = -EINPROGRESS; + udc->ep0_req.req.context = udc; + udc->ep0_req.req.actual = 0; +} + +/*-------------------------------------------------------------------------*/ +/* Acquisition of the first address of RAM(FIFO) */ +static u32 _nbu2ss_get_begin_ram_address(struct nbu2ss_udc *udc) +{ + u32 num, buf_type; + u32 data, last_ram_adr, use_ram_size; + + PT_EP_REGS p_ep_regs; + + last_ram_adr = (D_RAM_SIZE_CTRL / sizeof(u32)) * 2; + use_ram_size = 0; + + for (num = 0; num < NUM_ENDPOINTS - 1; num++) { + p_ep_regs = &udc->p_regs->EP_REGS[num]; + data = _nbu2ss_readl(&p_ep_regs->EP_PCKT_ADRS); + buf_type = _nbu2ss_readl(&p_ep_regs->EP_CONTROL) & EPn_BUF_TYPE; + if (buf_type == 0) { + /* Single Buffer */ + use_ram_size += (data & EPn_MPKT) / sizeof(u32); + } else { + /* Double Buffer */ + use_ram_size += ((data & EPn_MPKT) / sizeof(u32)) * 2; + } + + if ((data >> 16) > last_ram_adr) + last_ram_adr = data>>16; + } + + return last_ram_adr + use_ram_size; +} + +/*-------------------------------------------------------------------------*/ +/* Construction of Endpoint */ +static int _nbu2ss_ep_init(struct nbu2ss_udc *udc, struct nbu2ss_ep *ep) +{ + u32 num; + u32 data; + u32 begin_adrs; + + if (ep->epnum == 0) + return -EINVAL; + + num = ep->epnum - 1; + + /*-------------------------------------------------------------*/ + /* RAM Transfer Address */ + begin_adrs = _nbu2ss_get_begin_ram_address(udc); + data = (begin_adrs << 16) | ep->ep.maxpacket; + _nbu2ss_writel(&udc->p_regs->EP_REGS[num].EP_PCKT_ADRS, data); + + /*-------------------------------------------------------------*/ + /* Interrupt Enable */ + data = 1 << (ep->epnum + 8); + _nbu2ss_bitset(&udc->p_regs->USB_INT_ENA, data); + + /*-------------------------------------------------------------*/ + /* Endpoint Type(Mode) */ + /* Bulk, Interrupt, ISO */ + switch (ep->ep_type) { + case USB_ENDPOINT_XFER_BULK: + data = EPn_BULK; + break; + + case USB_ENDPOINT_XFER_INT: + data = EPn_BUF_SINGLE | EPn_INTERRUPT; + break; + + case USB_ENDPOINT_XFER_ISOC: + data = EPn_ISO; + break; + + default: + data = 0; + break; + } + + _nbu2ss_bitset(&udc->p_regs->EP_REGS[num].EP_CONTROL, data); + _nbu2ss_endpoint_toggle_reset(udc, (ep->epnum|ep->direct)); + + if (ep->direct == USB_DIR_OUT) { + /*---------------------------------------------------------*/ + /* OUT */ + data = EPn_EN | EPn_BCLR | EPn_DIR0; + _nbu2ss_bitset(&udc->p_regs->EP_REGS[num].EP_CONTROL, data); + + data = (EPn_ONAK | EPn_OSTL_EN | EPn_OSTL); + _nbu2ss_bitclr(&udc->p_regs->EP_REGS[num].EP_CONTROL, data); + + data = (EPn_OUT_EN | EPn_OUT_END_EN); + _nbu2ss_bitset(&udc->p_regs->EP_REGS[num].EP_INT_ENA, data); + } else { + /*---------------------------------------------------------*/ + /* IN */ + data = (EPn_EN | EPn_BCLR | EPn_AUTO); + _nbu2ss_bitset(&udc->p_regs->EP_REGS[num].EP_CONTROL, data); + + data = (EPn_ISTL); + _nbu2ss_bitclr(&udc->p_regs->EP_REGS[num].EP_CONTROL, data); + + data = (EPn_IN_EN | EPn_IN_END_EN); + _nbu2ss_bitset(&udc->p_regs->EP_REGS[num].EP_INT_ENA, data); + } + + return 0; +} + +/*-------------------------------------------------------------------------*/ +/* Release of Endpoint */ +static int _nbu2ss_epn_exit(struct nbu2ss_udc *udc, struct nbu2ss_ep *ep) +{ + u32 num; + u32 data; + + if ((ep->epnum == 0) || (udc->vbus_active == 0)) + return -EINVAL; + + num = ep->epnum - 1; + + /*-------------------------------------------------------------*/ + /* RAM Transfer Address */ + _nbu2ss_writel(&udc->p_regs->EP_REGS[num].EP_PCKT_ADRS, 0); + + /*-------------------------------------------------------------*/ + /* Interrupt Disable */ + data = 1 << (ep->epnum + 8); + _nbu2ss_bitclr(&udc->p_regs->USB_INT_ENA, data); + + if (ep->direct == USB_DIR_OUT) { + /*---------------------------------------------------------*/ + /* OUT */ + data = EPn_ONAK | EPn_BCLR; + _nbu2ss_bitset(&udc->p_regs->EP_REGS[num].EP_CONTROL, data); + + data = EPn_EN | EPn_DIR0; + _nbu2ss_bitclr(&udc->p_regs->EP_REGS[num].EP_CONTROL, data); + + data = EPn_OUT_EN | EPn_OUT_END_EN; + _nbu2ss_bitclr(&udc->p_regs->EP_REGS[num].EP_INT_ENA, data); + } else { + /*---------------------------------------------------------*/ + /* IN */ + data = EPn_BCLR; + _nbu2ss_bitset(&udc->p_regs->EP_REGS[num].EP_CONTROL, data); + + data = EPn_EN | EPn_AUTO; + _nbu2ss_bitclr(&udc->p_regs->EP_REGS[num].EP_CONTROL, data); + + data = EPn_IN_EN | EPn_IN_END_EN; + _nbu2ss_bitclr(&udc->p_regs->EP_REGS[num].EP_INT_ENA, data); + } + + return 0; +} + +/*-------------------------------------------------------------------------*/ +/* DMA setting (without Endpoint 0) */ +static void _nbu2ss_ep_dma_init(struct nbu2ss_udc *udc, struct nbu2ss_ep *ep) +{ + u32 num; + u32 data; + + data = _nbu2ss_readl(&udc->p_regs->USBSSCONF); + if (((ep->epnum == 0) || (data & (1 << ep->epnum)) == 0)) + return; /* Not Support DMA */ + + num = ep->epnum - 1; + + if (ep->direct == USB_DIR_OUT) { + /*---------------------------------------------------------*/ + /* OUT */ + data = ep->ep.maxpacket; + _nbu2ss_writel(&udc->p_regs->EP_DCR[num].EP_DCR2, data); + + /*---------------------------------------------------------*/ + /* Transfer Direct */ + data = DCR1_EPn_DIR0; + _nbu2ss_bitset(&udc->p_regs->EP_DCR[num].EP_DCR1, data); + + /*---------------------------------------------------------*/ + /* DMA Mode etc. */ + data = EPn_STOP_MODE | EPn_STOP_SET | EPn_DMAMODE0; + _nbu2ss_writel(&udc->p_regs->EP_REGS[num].EP_DMA_CTRL, data); + } else { + /*---------------------------------------------------------*/ + /* IN */ + _nbu2ss_bitset(&udc->p_regs->EP_REGS[num].EP_CONTROL, EPn_AUTO); + + /*---------------------------------------------------------*/ + /* DMA Mode etc. */ + data = EPn_BURST_SET | EPn_DMAMODE0; + _nbu2ss_writel(&udc->p_regs->EP_REGS[num].EP_DMA_CTRL, data); + } +} + +/*-------------------------------------------------------------------------*/ +/* DMA setting release */ +static void _nbu2ss_ep_dma_exit(struct nbu2ss_udc *udc, struct nbu2ss_ep *ep) +{ + u32 num; + u32 data; + PT_FC_REGS preg = udc->p_regs; + + if (udc->vbus_active == 0) + return; /* VBUS OFF */ + + data = _nbu2ss_readl(&preg->USBSSCONF); + if ((ep->epnum == 0) || ((data & (1 << ep->epnum)) == 0)) + return; /* Not Support DMA */ + + num = ep->epnum - 1; + + _nbu2ss_ep_dma_abort(udc, ep); + + if (ep->direct == USB_DIR_OUT) { + /*---------------------------------------------------------*/ + /* OUT */ + _nbu2ss_writel(&preg->EP_DCR[num].EP_DCR2, 0); + _nbu2ss_bitclr(&preg->EP_DCR[num].EP_DCR1, DCR1_EPn_DIR0); + _nbu2ss_writel(&preg->EP_REGS[num].EP_DMA_CTRL, 0); + } else { + /*---------------------------------------------------------*/ + /* IN */ + _nbu2ss_bitclr(&preg->EP_REGS[num].EP_CONTROL, EPn_AUTO); + _nbu2ss_writel(&preg->EP_REGS[num].EP_DMA_CTRL, 0); + } +} + +/*-------------------------------------------------------------------------*/ +/* Abort DMA */ +static void _nbu2ss_ep_dma_abort(struct nbu2ss_udc *udc, struct nbu2ss_ep *ep) +{ + PT_FC_REGS preg = udc->p_regs; + + _nbu2ss_bitclr(&preg->EP_DCR[ep->epnum-1].EP_DCR1, DCR1_EPn_REQEN); + mdelay(DMA_DISABLE_TIME); /* DCR1_EPn_REQEN Clear */ + _nbu2ss_bitclr(&preg->EP_REGS[ep->epnum-1].EP_DMA_CTRL, EPn_DMA_EN); +} + +/*-------------------------------------------------------------------------*/ +/* Start IN Transfer */ +static void _nbu2ss_ep_in_end( + struct nbu2ss_udc *udc, + u32 epnum, + u32 data32, + u32 length +) +{ + u32 data; + u32 num; + PT_FC_REGS preg = udc->p_regs; + + if (length >= sizeof(u32)) + return; + + if (epnum == 0) { + _nbu2ss_bitclr(&preg->EP0_CONTROL, EP0_AUTO); + + /* Writing of 1-4 bytes */ + if (length) + _nbu2ss_writel(&preg->EP0_WRITE, data32); + + data = ((length << 5) & EP0_DW) | EP0_DEND; + _nbu2ss_writel(&preg->EP0_CONTROL, data); + + _nbu2ss_bitset(&preg->EP0_CONTROL, EP0_AUTO); + } else { + num = epnum - 1; + + _nbu2ss_bitclr(&preg->EP_REGS[num].EP_CONTROL, EPn_AUTO); + + /* Writing of 1-4 bytes */ + if (length) + _nbu2ss_writel(&preg->EP_REGS[num].EP_WRITE, data32); + + data = (((((u32)length) << 5) & EPn_DW) | EPn_DEND); + _nbu2ss_bitset(&preg->EP_REGS[num].EP_CONTROL, data); + + _nbu2ss_bitset(&preg->EP_REGS[num].EP_CONTROL, EPn_AUTO); + } + + return; +} + +#ifdef USE_DMA +/*-------------------------------------------------------------------------*/ +static void _nbu2ss_dma_map_single( + struct nbu2ss_udc *udc, + struct nbu2ss_ep *ep, + struct nbu2ss_req *req, + u8 direct +) +{ + if (req->req.dma == DMA_ADDR_INVALID) { + if (req->unaligned) + req->req.dma = ep->phys_buf; + else { + req->req.dma = dma_map_single( + udc->gadget.dev.parent, + req->req.buf, + req->req.length, + (direct == USB_DIR_IN) + ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + } + req->mapped = 1; + } else { + if (!req->unaligned) + dma_sync_single_for_device( + udc->gadget.dev.parent, + req->req.dma, + req->req.length, + (direct == USB_DIR_IN) + ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + req->mapped = 0; + } +} + +/*-------------------------------------------------------------------------*/ +static void _nbu2ss_dma_unmap_single( + struct nbu2ss_udc *udc, + struct nbu2ss_ep *ep, + struct nbu2ss_req *req, + u8 direct +) +{ + u8 data[4]; + u8 *p; + u32 count = 0; + + if (direct == USB_DIR_OUT) { + count = req->req.actual % 4; + if (count) { + p = req->req.buf; + p += (req->req.actual - count); + memcpy(data, p, count); + } + } + + if (req->mapped) { + if (req->unaligned) { + if (direct == USB_DIR_OUT) + memcpy(req->req.buf, ep->virt_buf, + req->req.actual & 0xfffffffc); + } else + dma_unmap_single(udc->gadget.dev.parent, + req->req.dma, req->req.length, + (direct == USB_DIR_IN) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + req->req.dma = DMA_ADDR_INVALID; + req->mapped = 0; + } else { + if (!req->unaligned) + dma_sync_single_for_cpu(udc->gadget.dev.parent, + req->req.dma, req->req.length, + (direct == USB_DIR_IN) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + } + + if (count) { + p = req->req.buf; + p += (req->req.actual - count); + memcpy(p, data, count); + } +} +#endif + +/*-------------------------------------------------------------------------*/ +/* Endpoint 0 OUT Transfer (PIO) */ +static int EP0_out_PIO(struct nbu2ss_udc *udc, u8* pBuf, u32 length) +{ + u32 i; + int nret = 0; + u32 iWordLength = 0; + USB_REG_ACCESS* pBuf32 = (USB_REG_ACCESS *)pBuf; + + /*------------------------------------------------------------*/ + /* Read Length */ + iWordLength = length / sizeof(u32); + + /*------------------------------------------------------------*/ + /* PIO Read */ + if (iWordLength) { + for (i = 0; i < iWordLength; i++) { + pBuf32->dw = _nbu2ss_readl(&udc->p_regs->EP0_READ); + pBuf32++; + } + nret = iWordLength * sizeof(u32); + } + + return nret; +} + +/*-------------------------------------------------------------------------*/ +/* Endpoint 0 OUT Transfer (PIO, OverBytes) */ +static int EP0_out_OverBytes(struct nbu2ss_udc *udc, u8* pBuf, u32 length) +{ + u32 i; + u32 iReadSize = 0; + USB_REG_ACCESS Temp32; + USB_REG_ACCESS* pBuf32 = (USB_REG_ACCESS *)pBuf; + + if ((0 < length) && (length < sizeof(u32))) { + Temp32.dw = _nbu2ss_readl(&udc->p_regs->EP0_READ); + for (i = 0 ; i < length ; i++) + pBuf32->byte.DATA[i] = Temp32.byte.DATA[i]; + iReadSize += length; + } + + return iReadSize; +} + +/*-------------------------------------------------------------------------*/ +/* Endpoint 0 IN Transfer (PIO) */ +static int EP0_in_PIO(struct nbu2ss_udc *udc, u8 *pBuf, u32 length) +{ + u32 i; + u32 iMaxLength = EP0_PACKETSIZE; + u32 iWordLength = 0; + u32 iWriteLength = 0; + USB_REG_ACCESS* pBuf32 = (USB_REG_ACCESS *)pBuf; + + /*------------------------------------------------------------*/ + /* Transfer Length */ + if (iMaxLength < length) + iWordLength = iMaxLength / sizeof(u32); + else + iWordLength = length / sizeof(u32); + + /*------------------------------------------------------------*/ + /* PIO */ + for (i = 0; i < iWordLength; i++) { + _nbu2ss_writel(&udc->p_regs->EP0_WRITE, pBuf32->dw); + pBuf32++; + iWriteLength += sizeof(u32); + } + + return iWriteLength; +} + +/*-------------------------------------------------------------------------*/ +/* Endpoint 0 IN Transfer (PIO, OverBytes) */ +static int EP0_in_OverBytes(struct nbu2ss_udc *udc, u8 *pBuf, u32 iRemainSize) +{ + u32 i; + USB_REG_ACCESS Temp32; + USB_REG_ACCESS* pBuf32 = (USB_REG_ACCESS *)pBuf; + + if ((0 < iRemainSize) && (iRemainSize < sizeof(u32))) { + for (i = 0 ; i < iRemainSize ; i++) + Temp32.byte.DATA[i] = pBuf32->byte.DATA[i]; + _nbu2ss_ep_in_end(udc, 0, Temp32.dw, iRemainSize); + + return iRemainSize; + } + + return 0; +} + +/*-------------------------------------------------------------------------*/ +/* Transfer NULL Packet (Epndoint 0) */ +static int EP0_send_NULL(struct nbu2ss_udc *udc, bool pid_flag) +{ + u32 data; + + data = _nbu2ss_readl(&udc->p_regs->EP0_CONTROL); + data &= ~(u32)EP0_INAK; + + if (pid_flag) + data |= (EP0_INAK_EN | EP0_PIDCLR | EP0_DEND); + else + data |= (EP0_INAK_EN | EP0_DEND); + + _nbu2ss_writel(&udc->p_regs->EP0_CONTROL, data); + + return 0; +} + +/*-------------------------------------------------------------------------*/ +/* Receive NULL Packet (Endpoint 0) */ +static int EP0_receive_NULL(struct nbu2ss_udc *udc, bool pid_flag) +{ + u32 data; + + data = _nbu2ss_readl(&udc->p_regs->EP0_CONTROL); + data &= ~(u32)EP0_ONAK; + + if (pid_flag) + data |= EP0_PIDCLR; + + _nbu2ss_writel(&udc->p_regs->EP0_CONTROL, data); + + return 0; +} + +/*-------------------------------------------------------------------------*/ +static int _nbu2ss_ep0_in_transfer( + struct nbu2ss_udc *udc, + struct nbu2ss_ep *ep, + struct nbu2ss_req *req +) +{ + u8 *pBuffer; /* IN Data Buffer */ + u32 data; + u32 iRemainSize = 0; + int result = 0; + + /*-------------------------------------------------------------*/ + /* End confirmation */ + if (req->req.actual == req->req.length) { + if ((req->req.actual % EP0_PACKETSIZE) == 0) { + if (req->zero) { + req->zero = 0; + EP0_send_NULL(udc, FALSE); + return 1; + } + } + + return 0; /* Transfer End */ + } + + /*-------------------------------------------------------------*/ + /* NAK release */ + data = _nbu2ss_readl(&udc->p_regs->EP0_CONTROL); + data |= EP0_INAK_EN; + data &= ~(u32)EP0_INAK; + _nbu2ss_writel(&udc->p_regs->EP0_CONTROL, data); + + iRemainSize = req->req.length - req->req.actual; + pBuffer = (u8 *)req->req.buf; + pBuffer += req->req.actual; + + /*-------------------------------------------------------------*/ + /* Data transfer */ + result = EP0_in_PIO(udc, pBuffer, iRemainSize); + + req->div_len = result; + iRemainSize -= result; + + if (iRemainSize == 0) { + EP0_send_NULL(udc, FALSE); + return result; + } + + if ((iRemainSize < sizeof(u32)) && (result != EP0_PACKETSIZE)) { + pBuffer += result; + result += EP0_in_OverBytes(udc, pBuffer, iRemainSize); + req->div_len = result; + } + + return result; +} + +/*-------------------------------------------------------------------------*/ +static int _nbu2ss_ep0_out_transfer( + struct nbu2ss_udc *udc, + struct nbu2ss_ep *ep, + struct nbu2ss_req *req +) +{ + u8 *pBuffer; + u32 iRemainSize; + u32 iRecvLength; + int result = 0; + int fRcvZero; + + /*-------------------------------------------------------------*/ + /* Receive data confirmation */ + iRecvLength = _nbu2ss_readl(&udc->p_regs->EP0_LENGTH) & EP0_LDATA; + if (iRecvLength != 0) { + + fRcvZero = 0; + + iRemainSize = req->req.length - req->req.actual; + pBuffer = (u8 *)req->req.buf; + pBuffer += req->req.actual; + + result = EP0_out_PIO(udc, pBuffer + , min(iRemainSize, iRecvLength)); + if (result < 0) + return result; + + req->req.actual += result; + iRecvLength -= result; + + if ((0 < iRecvLength) && (iRecvLength < sizeof(u32))) { + pBuffer += result; + iRemainSize -= result; + + result = EP0_out_OverBytes(udc, pBuffer + , min(iRemainSize, iRecvLength)); + req->req.actual += result; + } + } else { + fRcvZero = 1; + } + + /*-------------------------------------------------------------*/ + /* End confirmation */ + if (req->req.actual == req->req.length) { + if ((req->req.actual % EP0_PACKETSIZE) == 0) { + if (req->zero) { + req->zero = 0; + EP0_receive_NULL(udc, FALSE); + return 1; + } + } + + return 0; /* Transfer End */ + } + + if ((req->req.actual % EP0_PACKETSIZE) != 0) + return 0; /* Short Packet Transfer End */ + + if (req->req.actual > req->req.length) { + ERR(" *** Overrun Error\n"); + return -EOVERFLOW; + } + + if (fRcvZero != 0) { + iRemainSize = _nbu2ss_readl(&udc->p_regs->EP0_CONTROL); + if (iRemainSize & EP0_ONAK) { + /*---------------------------------------------------*/ + /* NACK release */ + _nbu2ss_bitclr(&udc->p_regs->EP0_CONTROL, EP0_ONAK); + } + result = 1; + } + + return result; +} + +/*-------------------------------------------------------------------------*/ +static int _nbu2ss_out_dma( + struct nbu2ss_udc *udc, + struct nbu2ss_req *req, + u32 num, + u32 length +) +{ + u8 *pBuffer; + u32 mpkt; + u32 lmpkt; + u32 dmacnt; + u32 burst = 1; + u32 data; + int result = -EINVAL; + PT_FC_REGS preg = udc->p_regs; + + if (req->dma_flag) + return 1; /* DMA is forwarded */ + + req->dma_flag = TRUE; + pBuffer = (u8 *)req->req.dma; + pBuffer += req->req.actual; + + /* DMA Address */ + _nbu2ss_writel(&preg->EP_DCR[num].EP_TADR, (u32)pBuffer); + + /* Number of transfer packets */ + mpkt = _nbu2ss_readl(&preg->EP_REGS[num].EP_PCKT_ADRS) & EPn_MPKT; + dmacnt = (length / mpkt); + lmpkt = (length % mpkt) & ~(u32)0x03; + + if (DMA_MAX_COUNT < dmacnt) { + dmacnt = DMA_MAX_COUNT; + lmpkt = 0; + } else if (0 != lmpkt) { + if (0 == dmacnt) + burst = 0; /* Burst OFF */ + dmacnt++; + } + + data = mpkt | (lmpkt << 16); + _nbu2ss_writel(&preg->EP_DCR[num].EP_DCR2, data); + + data = ((dmacnt & 0xff) << 16) | DCR1_EPn_DIR0 | DCR1_EPn_REQEN; + _nbu2ss_writel(&preg->EP_DCR[num].EP_DCR1, data); + + if (0 == burst) { + _nbu2ss_writel(&preg->EP_REGS[num].EP_LEN_DCNT, 0); + _nbu2ss_bitclr(&preg->EP_REGS[num].EP_DMA_CTRL, EPn_BURST_SET); + } else { + _nbu2ss_writel(&preg->EP_REGS[num].EP_LEN_DCNT + , (dmacnt << 16)); + _nbu2ss_bitset(&preg->EP_REGS[num].EP_DMA_CTRL, EPn_BURST_SET); + } + _nbu2ss_bitset(&preg->EP_REGS[num].EP_DMA_CTRL, EPn_DMA_EN); + + result = length & ~(u32)0x03; + req->div_len = result; + + return result; +} + +/*-------------------------------------------------------------------------*/ +static int _nbu2ss_epn_out_pio( + struct nbu2ss_udc *udc, + struct nbu2ss_ep *ep, + struct nbu2ss_req *req, + u32 length +) +{ + u8 *pBuffer; + u32 i; + u32 data; + u32 iWordLength; + USB_REG_ACCESS Temp32; + USB_REG_ACCESS *pBuf32; + int result = 0; + PT_FC_REGS preg = udc->p_regs; + + if (req->dma_flag) + return 1; /* DMA is forwarded */ + + if (length == 0) + return 0; + + pBuffer = (u8 *)req->req.buf; + pBuf32 = (USB_REG_ACCESS *)(pBuffer + req->req.actual); + + iWordLength = length / sizeof(u32); + if (iWordLength > 0) { + /*---------------------------------------------------------*/ + /* Copy of every four bytes */ + for (i = 0; i < iWordLength; i++) { + pBuf32->dw = + _nbu2ss_readl(&preg->EP_REGS[ep->epnum-1].EP_READ); + pBuf32++; + } + result = iWordLength * sizeof(u32); + } + + data = length - result; + if (data > 0) { + /*---------------------------------------------------------*/ + /* Copy of fraction byte */ + Temp32.dw = _nbu2ss_readl(&preg->EP_REGS[ep->epnum-1].EP_READ); + for (i = 0 ; i < data ; i++) + pBuf32->byte.DATA[i] = Temp32.byte.DATA[i]; + result += data; + } + + req->req.actual += result; + + if ((req->req.actual == req->req.length) + || ((req->req.actual % ep->ep.maxpacket) != 0)) { + + result = 0; + } + + return result; +} + +/*-------------------------------------------------------------------------*/ +static int _nbu2ss_epn_out_data( + struct nbu2ss_udc *udc, + struct nbu2ss_ep *ep, + struct nbu2ss_req *req, + u32 data_size +) +{ + u32 num; + u32 iBufSize; + int nret = 1; + + if (ep->epnum == 0) + return -EINVAL; + + num = ep->epnum - 1; + + iBufSize = min((req->req.length - req->req.actual), data_size); + + if ((ep->ep_type != USB_ENDPOINT_XFER_INT) + && (req->req.dma != 0) + && (iBufSize >= sizeof(u32))) { + nret = _nbu2ss_out_dma(udc, req, num, iBufSize); + } else { + iBufSize = min(iBufSize, (u32)ep->ep.maxpacket); + nret = _nbu2ss_epn_out_pio(udc, ep, req, iBufSize); + } + + return nret; +} + +/*-------------------------------------------------------------------------*/ +static int _nbu2ss_epn_out_transfer( + struct nbu2ss_udc *udc, + struct nbu2ss_ep *ep, + struct nbu2ss_req *req +) +{ + u32 num; + u32 iRecvLength; + int result = 1; + PT_FC_REGS preg = udc->p_regs; + + if (ep->epnum == 0) + return -EINVAL; + + num = ep->epnum - 1; + + /*-------------------------------------------------------------*/ + /* Receive Length */ + iRecvLength + = _nbu2ss_readl(&preg->EP_REGS[num].EP_LEN_DCNT) & EPn_LDATA; + + if (iRecvLength != 0) { + result = _nbu2ss_epn_out_data(udc, ep, req, iRecvLength); + if (iRecvLength < ep->ep.maxpacket) { + if (iRecvLength == result) { + req->req.actual += result; + result = 0; + } + } + } else { + if ((req->req.actual == req->req.length) + || ((req->req.actual % ep->ep.maxpacket) != 0)) { + + result = 0; + } + } + + if (result == 0) { + if ((req->req.actual % ep->ep.maxpacket) == 0) { + if (req->zero) { + req->zero = 0; + return 1; + } + } + } + + if (req->req.actual > req->req.length) { + ERR(" *** Overrun Error\n"); + ERR(" *** actual = %d, length = %d\n", + req->req.actual, req->req.length); + result = -EOVERFLOW; + } + + return result; +} + +/*-------------------------------------------------------------------------*/ +static int _nbu2ss_in_dma( + struct nbu2ss_udc *udc, + struct nbu2ss_ep *ep, + struct nbu2ss_req *req, + u32 num, + u32 length +) +{ + u8 *pBuffer; + u32 mpkt; /* MaxPacketSize */ + u32 lmpkt; /* Last Packet Data Size */ + u32 dmacnt; /* IN Data Size */ + u32 iWriteLength; + u32 data; + int result = -EINVAL; + PT_FC_REGS preg = udc->p_regs; + + if (req->dma_flag) + return 1; /* DMA is forwarded */ + +#ifdef USE_DMA + if (req->req.actual == 0) + _nbu2ss_dma_map_single(udc, ep, req, USB_DIR_IN); +#endif + req->dma_flag = TRUE; + + /* MAX Packet Size */ + mpkt = _nbu2ss_readl(&preg->EP_REGS[num].EP_PCKT_ADRS) & EPn_MPKT; + + if ((DMA_MAX_COUNT * mpkt) < length) + iWriteLength = DMA_MAX_COUNT * mpkt; + else + iWriteLength = length; + + /*------------------------------------------------------------*/ + /* Number of transmission packets */ + if (mpkt < iWriteLength) { + dmacnt = iWriteLength / mpkt; + lmpkt = (iWriteLength % mpkt) & ~(u32)0x3; + if (lmpkt != 0) + dmacnt++; + else + lmpkt = mpkt & ~(u32)0x3; + + } else { + dmacnt = 1; + lmpkt = iWriteLength & ~(u32)0x3; + } + + /* Packet setting */ + data = mpkt | (lmpkt << 16); + _nbu2ss_writel(&preg->EP_DCR[num].EP_DCR2, data); + + /* Address setting */ + pBuffer = (u8 *)req->req.dma; + pBuffer += req->req.actual; + _nbu2ss_writel(&preg->EP_DCR[num].EP_TADR, (u32)pBuffer); + + /* Packet and DMA setting */ + data = ((dmacnt & 0xff) << 16) | DCR1_EPn_REQEN; + _nbu2ss_writel(&preg->EP_DCR[num].EP_DCR1, data); + + /* Packet setting of EPC */ + data = dmacnt << 16; + _nbu2ss_writel(&preg->EP_REGS[num].EP_LEN_DCNT, data); + + /*DMA setting of EPC */ |