diff options
author | Yuan-Hsin Chen <yhchen@faraday-tech.com> | 2011-01-18 14:49:28 +0800 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-01-22 19:45:36 -0800 |
commit | 0fe6f1d1f612035d78d8d195bbc3485a341386d5 (patch) | |
tree | c62236f95906cd576788897674f85bf43c5392da /drivers/usb/gadget/fusb300_udc.c | |
parent | bc0f23dccad16c7834cb09d943981475be81ddb1 (diff) |
usb: udc: add Faraday fusb300 driver
USB2.0 device controller driver for Faraday fubs300
Signed-off-by: Yuan-Hsin Chen <yhchen@faraday-tech.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/gadget/fusb300_udc.c')
-rw-r--r-- | drivers/usb/gadget/fusb300_udc.c | 1743 |
1 files changed, 1743 insertions, 0 deletions
diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c new file mode 100644 index 00000000000..fd4fd69f5ad --- /dev/null +++ b/drivers/usb/gadget/fusb300_udc.c @@ -0,0 +1,1743 @@ +/* + * Fusb300 UDC (USB gadget) + * + * Copyright (C) 2010 Faraday Technology Corp. + * + * Author : Yuan-hsin Chen <yhchen@faraday-tech.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include "fusb300_udc.h" + +MODULE_DESCRIPTION("FUSB300 USB gadget driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yuan Hsin Chen <yhchen@faraday-tech.com>"); +MODULE_ALIAS("platform:fusb300_udc"); + +#define DRIVER_VERSION "20 October 2010" + +static const char udc_name[] = "fusb300_udc"; +static const char * const fusb300_ep_name[] = { + "ep0", "ep1", "ep2", "ep3", "ep4", "ep5", "ep6", "ep7" +}; + +static void done(struct fusb300_ep *ep, struct fusb300_request *req, + int status); + +static void fusb300_enable_bit(struct fusb300 *fusb300, u32 offset, + u32 value) +{ + u32 reg = ioread32(fusb300->reg + offset); + + reg |= value; + iowrite32(reg, fusb300->reg + offset); +} + +static void fusb300_disable_bit(struct fusb300 *fusb300, u32 offset, + u32 value) +{ + u32 reg = ioread32(fusb300->reg + offset); + + reg &= ~value; + iowrite32(reg, fusb300->reg + offset); +} + + +static void fusb300_ep_setting(struct fusb300_ep *ep, + struct fusb300_ep_info info) +{ + ep->epnum = info.epnum; + ep->type = info.type; +} + +static int fusb300_ep_release(struct fusb300_ep *ep) +{ + if (!ep->epnum) + return 0; + ep->epnum = 0; + ep->stall = 0; + ep->wedged = 0; + return 0; +} + +static void fusb300_set_fifo_entry(struct fusb300 *fusb300, + u32 ep) +{ + u32 val = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(ep)); + + val &= ~FUSB300_EPSET1_FIFOENTRY_MSK; + val |= FUSB300_EPSET1_FIFOENTRY(FUSB300_FIFO_ENTRY_NUM); + iowrite32(val, fusb300->reg + FUSB300_OFFSET_EPSET1(ep)); +} + +static void fusb300_set_start_entry(struct fusb300 *fusb300, + u8 ep) +{ + u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(ep)); + u32 start_entry = fusb300->fifo_entry_num * FUSB300_FIFO_ENTRY_NUM; + + reg &= ~FUSB300_EPSET1_START_ENTRY_MSK ; + reg |= FUSB300_EPSET1_START_ENTRY(start_entry); + iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(ep)); + if (fusb300->fifo_entry_num == FUSB300_MAX_FIFO_ENTRY) { + fusb300->fifo_entry_num = 0; + fusb300->addrofs = 0; + pr_err("fifo entry is over the maximum number!\n"); + } else + fusb300->fifo_entry_num++; +} + +/* set fusb300_set_start_entry first before fusb300_set_epaddrofs */ +static void fusb300_set_epaddrofs(struct fusb300 *fusb300, + struct fusb300_ep_info info) +{ + u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET2(info.epnum)); + + reg &= ~FUSB300_EPSET2_ADDROFS_MSK; + reg |= FUSB300_EPSET2_ADDROFS(fusb300->addrofs); + iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET2(info.epnum)); + fusb300->addrofs += (info.maxpacket + 7) / 8 * FUSB300_FIFO_ENTRY_NUM; +} + +static void ep_fifo_setting(struct fusb300 *fusb300, + struct fusb300_ep_info info) +{ + fusb300_set_fifo_entry(fusb300, info.epnum); + fusb300_set_start_entry(fusb300, info.epnum); + fusb300_set_epaddrofs(fusb300, info); +} + +static void fusb300_set_eptype(struct fusb300 *fusb300, + struct fusb300_ep_info info) +{ + u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum)); + + reg &= ~FUSB300_EPSET1_TYPE_MSK; + reg |= FUSB300_EPSET1_TYPE(info.type); + iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum)); +} + +static void fusb300_set_epdir(struct fusb300 *fusb300, + struct fusb300_ep_info info) +{ + u32 reg; + + if (!info.dir_in) + return; + reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum)); + reg &= ~FUSB300_EPSET1_DIR_MSK; + reg |= FUSB300_EPSET1_DIRIN; + iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum)); +} + +static void fusb300_set_ep_active(struct fusb300 *fusb300, + u8 ep) +{ + u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(ep)); + + reg |= FUSB300_EPSET1_ACTEN; + iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(ep)); +} + +static void fusb300_set_epmps(struct fusb300 *fusb300, + struct fusb300_ep_info info) +{ + u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET2(info.epnum)); + + reg &= ~FUSB300_EPSET2_MPS_MSK; + reg |= FUSB300_EPSET2_MPS(info.maxpacket); + iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET2(info.epnum)); +} + +static void fusb300_set_interval(struct fusb300 *fusb300, + struct fusb300_ep_info info) +{ + u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum)); + + reg &= ~FUSB300_EPSET1_INTERVAL(0x7); + reg |= FUSB300_EPSET1_INTERVAL(info.interval); + iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum)); +} + +static void fusb300_set_bwnum(struct fusb300 *fusb300, + struct fusb300_ep_info info) +{ + u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum)); + + reg &= ~FUSB300_EPSET1_BWNUM(0x3); + reg |= FUSB300_EPSET1_BWNUM(info.bw_num); + iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum)); +} + +static void set_ep_reg(struct fusb300 *fusb300, + struct fusb300_ep_info info) +{ + fusb300_set_eptype(fusb300, info); + fusb300_set_epdir(fusb300, info); + fusb300_set_epmps(fusb300, info); + + if (info.interval) + fusb300_set_interval(fusb300, info); + + if (info.bw_num) + fusb300_set_bwnum(fusb300, info); + + fusb300_set_ep_active(fusb300, info.epnum); +} + +static int config_ep(struct fusb300_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct fusb300 *fusb300 = ep->fusb300; + struct fusb300_ep_info info; + + ep->desc = desc; + + info.interval = 0; + info.addrofs = 0; + info.bw_num = 0; + + info.type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + info.dir_in = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? 1 : 0; + info.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + + if ((info.type == USB_ENDPOINT_XFER_INT) || + (info.type == USB_ENDPOINT_XFER_ISOC)) { + info.interval = desc->bInterval; + if (info.type == USB_ENDPOINT_XFER_ISOC) + info.bw_num = ((desc->wMaxPacketSize & 0x1800) >> 11); + } + + ep_fifo_setting(fusb300, info); + + set_ep_reg(fusb300, info); + + fusb300_ep_setting(ep, info); + + fusb300->ep[info.epnum] = ep; + + return 0; +} + +static int fusb300_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct fusb300_ep *ep; + + ep = container_of(_ep, struct fusb300_ep, ep); + + if (ep->fusb300->reenum) { + ep->fusb300->fifo_entry_num = 0; + ep->fusb300->addrofs = 0; + ep->fusb300->reenum = 0; + } + + return config_ep(ep, desc); +} + +static int fusb300_disable(struct usb_ep *_ep) +{ + struct fusb300_ep *ep; + struct fusb300_request *req; + unsigned long flags; + + ep = container_of(_ep, struct fusb300_ep, ep); + + BUG_ON(!ep); + + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct fusb300_request, queue); + spin_lock_irqsave(&ep->fusb300->lock, flags); + done(ep, req, -ECONNRESET); + spin_unlock_irqrestore(&ep->fusb300->lock, flags); + } + + return fusb300_ep_release(ep); +} + +static struct usb_request *fusb300_alloc_request(struct usb_ep *_ep, + gfp_t gfp_flags) +{ + struct fusb300_request *req; + + req = kzalloc(sizeof(struct fusb300_request), gfp_flags); + if (!req) + return NULL; + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void fusb300_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct fusb300_request *req; + + req = container_of(_req, struct fusb300_request, req); + kfree(req); +} + +static int enable_fifo_int(struct fusb300_ep *ep) +{ + struct fusb300 *fusb300 = ep->fusb300; + + if (ep->epnum) { + fusb300_enable_bit(fusb300, FUSB300_OFFSET_IGER0, + FUSB300_IGER0_EEPn_FIFO_INT(ep->epnum)); + } else { + pr_err("can't enable_fifo_int ep0\n"); + return -EINVAL; + } + + return 0; +} + +static int disable_fifo_int(struct fusb300_ep *ep) +{ + struct fusb300 *fusb300 = ep->fusb300; + + if (ep->epnum) { + fusb300_disable_bit(fusb300, FUSB300_OFFSET_IGER0, + FUSB300_IGER0_EEPn_FIFO_INT(ep->epnum)); + } else { + pr_err("can't disable_fifo_int ep0\n"); + return -EINVAL; + } + + return 0; +} + +static void fusb300_set_cxlen(struct fusb300 *fusb300, u32 length) +{ + u32 reg; + + reg = ioread32(fusb300->reg + FUSB300_OFFSET_CSR); + reg &= ~FUSB300_CSR_LEN_MSK; + reg |= FUSB300_CSR_LEN(length); + iowrite32(reg, fusb300->reg + FUSB300_OFFSET_CSR); +} + +/* write data to cx fifo */ +static void fusb300_wrcxf(struct fusb300_ep *ep, + struct fusb300_request *req) +{ + int i = 0; + u8 *tmp; + u32 data; + struct fusb300 *fusb300 = ep->fusb300; + u32 length = req->req.length - req->req.actual; + + tmp = req->req.buf + req->req.actual; + + if (length > SS_CTL_MAX_PACKET_SIZE) { + fusb300_set_cxlen(fusb300, SS_CTL_MAX_PACKET_SIZE); + for (i = (SS_CTL_MAX_PACKET_SIZE >> 2); i > 0; i--) { + data = *tmp | *(tmp + 1) << 8 | *(tmp + 2) << 16 | + *(tmp + 3) << 24; + iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT); + tmp += 4; + } + req->req.actual += SS_CTL_MAX_PACKET_SIZE; + } else { /* length is less than max packet size */ + fusb300_set_cxlen(fusb300, length); + for (i = length >> 2; i > 0; i--) { + data = *tmp | *(tmp + 1) << 8 | *(tmp + 2) << 16 | + *(tmp + 3) << 24; + printk(KERN_DEBUG " 0x%x\n", data); + iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT); + tmp = tmp + 4; + } + switch (length % 4) { + case 1: + data = *tmp; + printk(KERN_DEBUG " 0x%x\n", data); + iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT); + break; + case 2: + data = *tmp | *(tmp + 1) << 8; + printk(KERN_DEBUG " 0x%x\n", data); + iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT); + break; + case 3: + data = *tmp | *(tmp + 1) << 8 | *(tmp + 2) << 16; + printk(KERN_DEBUG " 0x%x\n", data); + iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT); + break; + default: + break; + } + req->req.actual += length; + } +} + +static void fusb300_set_epnstall(struct fusb300 *fusb300, u8 ep) +{ + fusb300_enable_bit(fusb300, FUSB300_OFFSET_EPSET0(ep), + FUSB300_EPSET0_STL); +} + +static void fusb300_clear_epnstall(struct fusb300 *fusb300, u8 ep) +{ + u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET0(ep)); + + if (reg & FUSB300_EPSET0_STL) { + printk(KERN_DEBUG "EP%d stall... Clear!!\n", ep); + reg &= ~FUSB300_EPSET0_STL; + iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET0(ep)); + } +} + +static void ep0_queue(struct fusb300_ep *ep, struct fusb300_request *req) +{ + if (ep->fusb300->ep0_dir) { /* if IN */ + if (req->req.length) { + fusb300_wrcxf(ep, req); + } else + printk(KERN_DEBUG "%s : req->req.length = 0x%x\n", + __func__, req->req.length); + if ((req->req.length == req->req.actual) || + (req->req.actual < ep->ep.maxpacket)) + done(ep, req, 0); + } else { /* OUT */ + if (!req->req.length) + done(ep, req, 0); + else + fusb300_enable_bit(ep->fusb300, FUSB300_OFFSET_IGER1, + FUSB300_IGER1_CX_OUT_INT); + } +} + +static int fusb300_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct fusb300_ep *ep; + struct fusb300_request *req; + unsigned long flags; + int request = 0; + + ep = container_of(_ep, struct fusb300_ep, ep); + req = container_of(_req, struct fusb300_request, req); + + if (ep->fusb300->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + spin_lock_irqsave(&ep->fusb300->lock, flags); + + if (list_empty(&ep->queue)) + request = 1; + + list_add_tail(&req->queue, &ep->queue); + + req->req.actual = 0; + req->req.status = -EINPROGRESS; + + if (ep->desc == NULL) /* ep0 */ + ep0_queue(ep, req); + else if (request && !ep->stall) + enable_fifo_int(ep); + + spin_unlock_irqrestore(&ep->fusb300->lock, flags); + + return 0; +} + +static int fusb300_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct fusb300_ep *ep; + struct fusb300_request *req; + unsigned long flags; + + ep = container_of(_ep, struct fusb300_ep, ep); + req = container_of(_req, struct fusb300_request, req); + + spin_lock_irqsave(&ep->fusb300->lock, flags); + if (!list_empty(&ep->queue)) + done(ep, req, -ECONNRESET); + spin_unlock_irqrestore(&ep->fusb300->lock, flags); + + return 0; +} + +static int fusb300_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedge) +{ + struct fusb300_ep *ep; + struct fusb300 *fusb300; + unsigned long flags; + int ret = 0; + + ep = container_of(_ep, struct fusb300_ep, ep); + + fusb300 = ep->fusb300; + + spin_lock_irqsave(&ep->fusb300->lock, flags); + + if (!list_empty(&ep->queue)) { + ret = -EAGAIN; + goto out; + } + + if (value) { + fusb300_set_epnstall(fusb300, ep->epnum); + ep->stall = 1; + if (wedge) + ep->wedged = 1; + } else { + fusb300_clear_epnstall(fusb300, ep->epnum); + ep->stall = 0; + ep->wedged = 0; + } + +out: + spin_unlock_irqrestore(&ep->fusb300->lock, flags); + return ret; +} + +static int fusb300_set_halt(struct usb_ep *_ep, int value) +{ + return fusb300_set_halt_and_wedge(_ep, value, 0); +} + +static int fusb300_set_wedge(struct usb_ep *_ep) +{ + return fusb300_set_halt_and_wedge(_ep, 1, 1); +} + +static void fusb300_fifo_flush(struct usb_ep *_ep) +{ +} + +static struct usb_ep_ops fusb300_ep_ops = { + .enable = fusb300_enable, + .disable = fusb300_disable, + + .alloc_request = fusb300_alloc_request, + .free_request = fusb300_free_request, + + .queue = fusb300_queue, + .dequeue = fusb300_dequeue, + + .set_halt = fusb300_set_halt, + .fifo_flush = fusb300_fifo_flush, + .set_wedge = fusb300_set_wedge, +}; + +/*****************************************************************************/ +static void fusb300_clear_int(struct fusb300 *fusb300, u32 offset, + u32 value) +{ + iowrite32(value, fusb300->reg + offset); +} + +static void fusb300_reset(void) +{ +} + +static void fusb300_set_cxstall(struct fusb300 *fusb300) +{ + fusb300_enable_bit(fusb300, FUSB300_OFFSET_CSR, + FUSB300_CSR_STL); +} + +static void fusb300_set_cxdone(struct fusb300 *fusb300) +{ + fusb300_enable_bit(fusb300, FUSB300_OFFSET_CSR, + FUSB300_CSR_DONE); +} + +/* read data from cx fifo */ +void fusb300_rdcxf(struct fusb300 *fusb300, + u8 *buffer, u32 length) +{ + int i = 0; + u8 *tmp; + u32 data; + + tmp = buffer; + + for (i = (length >> 2); i > 0; i--) { + data = ioread32(fusb300->reg + FUSB300_OFFSET_CXPORT); + printk(KERN_DEBUG " 0x%x\n", data); + *tmp = data & 0xFF; + *(tmp + 1) = (data >> 8) & 0xFF; + *(tmp + 2) = (data >> 16) & 0xFF; + *(tmp + 3) = (data >> 24) & 0xFF; + tmp = tmp + 4; + } + + switch (length % 4) { + case 1: + data = ioread32(fusb300->reg + FUSB300_OFFSET_CXPORT); + printk(KERN_DEBUG " 0x%x\n", data); + *tmp = data & 0xFF; + break; + case 2: + data = ioread32(fusb300->reg + FUSB300_OFFSET_CXPORT); + printk(KERN_DEBUG " 0x%x\n", data); + *tmp = data & 0xFF; + *(tmp + 1) = (data >> 8) & 0xFF; + break; + case 3: + data = ioread32(fusb300->reg + FUSB300_OFFSET_CXPORT); + printk(KERN_DEBUG " 0x%x\n", data); + *tmp = data & 0xFF; + *(tmp + 1) = (data >> 8) & 0xFF; + *(tmp + 2) = (data >> 16) & 0xFF; + break; + default: + break; + } +} + +#if 0 +static void fusb300_dbg_fifo(struct fusb300_ep *ep, + u8 entry, u16 length) +{ + u32 reg; + u32 i = 0; + u32 j = 0; + + reg = ioread32(ep->fusb300->reg + FUSB300_OFFSET_GTM); + reg &= ~(FUSB300_GTM_TST_EP_ENTRY(0xF) | + FUSB300_GTM_TST_EP_NUM(0xF) | FUSB300_GTM_TST_FIFO_DEG); + reg |= (FUSB300_GTM_TST_EP_ENTRY(entry) | + FUSB300_GTM_TST_EP_NUM(ep->epnum) | FUSB300_GTM_TST_FIFO_DEG); + iowrite32(reg, ep->fusb300->reg + FUSB300_OFFSET_GTM); + + for (i = 0; i < (length >> 2); i++) { + if (i * 4 == 1024) + break; + reg = ioread32(ep->fusb300->reg + + FUSB300_OFFSET_BUFDBG_START + i * 4); + printk(KERN_DEBUG" 0x%-8x", reg); + j++; + if ((j % 4) == 0) + printk(KERN_DEBUG "\n"); + } + + if (length % 4) { + reg = ioread32(ep->fusb300->reg + + FUSB300_OFFSET_BUFDBG_START + i * 4); + printk(KERN_DEBUG " 0x%x\n", reg); + } + + if ((j % 4) != 0) + printk(KERN_DEBUG "\n"); + + fusb300_disable_bit(ep->fusb300, FUSB300_OFFSET_GTM, + FUSB300_GTM_TST_FIFO_DEG); +} + +static void fusb300_cmp_dbg_fifo(struct fusb300_ep *ep, + u8 entry, u16 length, u8 *golden) +{ + u32 reg; + u32 i = 0; + u32 golden_value; + u8 *tmp; + + tmp = golden; + + printk(KERN_DEBUG "fusb300_cmp_dbg_fifo (entry %d) : start\n", entry); + + reg = ioread32(ep->fusb300->reg + FUSB300_OFFSET_GTM); + reg &= ~(FUSB300_GTM_TST_EP_ENTRY(0xF) | + FUSB300_GTM_TST_EP_NUM(0xF) | FUSB300_GTM_TST_FIFO_DEG); + reg |= (FUSB300_GTM_TST_EP_ENTRY(entry) | + FUSB300_GTM_TST_EP_NUM(ep->epnum) | FUSB300_GTM_TST_FIFO_DEG); + iowrite32(reg, ep->fusb300->reg + FUSB300_OFFSET_GTM); + + for (i = 0; i < (length >> 2); i++) { + if (i * 4 == 1024) + break; + golden_value = *tmp | *(tmp + 1) << 8 | + *(tmp + 2) << 16 | *(tmp + 3) << 24; + + reg = ioread32(ep->fusb300->reg + + FUSB300_OFFSET_BUFDBG_START + i*4); + + if (reg != golden_value) { + printk(KERN_DEBUG "0x%x : ", (u32)(ep->fusb300->reg + + FUSB300_OFFSET_BUFDBG_START + i*4)); + printk(KERN_DEBUG " golden = 0x%x, reg = 0x%x\n", + golden_value, reg); + } + tmp += 4; + } + + switch (length % 4) { + case 1: + golden_value = *tmp; + case 2: + golden_value = *tmp | *(tmp + 1) << 8; + case 3: + golden_value = *tmp | *(tmp + 1) << 8 | *(tmp + 2) << 16; + default: + break; + + reg = ioread32(ep->fusb300->reg + FUSB300_OFFSET_BUFDBG_START + i*4); + if (reg != golden_value) { + printk(KERN_DEBUG "0x%x:", (u32)(ep->fusb300->reg + + FUSB300_OFFSET_BUFDBG_START + i*4)); + printk(KERN_DEBUG " golden = 0x%x, reg = 0x%x\n", + golden_value, reg); + } + } + + printk(KERN_DEBUG "fusb300_cmp_dbg_fifo : end\n"); + fusb300_disable_bit(ep->fusb300, FUSB300_OFFSET_GTM, + FUSB300_GTM_TST_FIFO_DEG); +} +#endif + +static void fusb300_rdfifo(struct fusb300_ep *ep, + struct fusb300_request *req, + u32 length) +{ + int i = 0; + u8 *tmp; + u32 data, reg; + struct fusb300 *fusb300 = ep->fusb300; + + tmp = req->req.buf + req->req.actual; + req->req.actual += length; + + if (req->req.actual > req->req.length) + printk(KERN_DEBUG "req->req.actual > req->req.length\n"); + + for (i = (length >> 2); i > 0; i--) { + data = ioread32(fusb300->reg + + FUSB300_OFFSET_EPPORT(ep->epnum)); + *tmp = data & 0xFF; + *(tmp + 1) = (data >> 8) & 0xFF; + *(tmp + 2) = (data >> 16) & 0xFF; + *(tmp + 3) = (data >> 24) & 0xFF; + tmp = tmp + 4; + } + + switch (length % 4) { + case 1: + data = ioread32(fusb300->reg + + FUSB300_OFFSET_EPPORT(ep->epnum)); + *tmp = data & 0xFF; + break; + case 2: + data = ioread32(fusb300->reg + + FUSB300_OFFSET_EPPORT(ep->epnum)); + *tmp = data & 0xFF; + *(tmp + 1) = (data >> 8) & 0xFF; + break; + case 3: + data = ioread32(fusb300->reg + + FUSB300_OFFSET_EPPORT(ep->epnum)); + *tmp = data & 0xFF; + *(tmp + 1) = (data >> 8) & 0xFF; + *(tmp + 2) = (data >> 16) & 0xFF; + break; + default: + break; + } + + do { + reg = ioread32(fusb300->reg + FUSB300_OFFSET_IGR1); + reg &= FUSB300_IGR1_SYNF0_EMPTY_INT; + if (i) + printk(KERN_INFO "sync fifo is not empty!\n"); + i++; + } while (!reg); +} + +/* write data to fifo */ +static void fusb300_wrfifo(struct fusb300_ep *ep, + struct fusb300_request *req) +{ + int i = 0; + u8 *tmp; + u32 data, reg; + struct fusb300 *fusb300 = ep->fusb300; + + tmp = req->req.buf; + req->req.actual = req->req.length; + + for (i = (req->req.length >> 2); i > 0; i--) { + data = *tmp | *(tmp + 1) << 8 | + *(tmp + 2) << 16 | *(tmp + 3) << 24; + + iowrite32(data, fusb300->reg + + FUSB300_OFFSET_EPPORT(ep->epnum)); + tmp += 4; + } + + switch (req->req.length % 4) { + case 1: + data = *tmp; + iowrite32(data, fusb300->reg + + FUSB300_OFFSET_EPPORT(ep->epnum)); + break; + case 2: + data = *tmp | *(tmp + 1) << 8; + iowrite32(data, fusb300->reg + + FUSB300_OFFSET_EPPORT(ep->epnum)); + break; + case 3: + data = *tmp | *(tmp + 1) << 8 | *(tmp + 2) << 16; + iowrite32(data, fusb300->reg + + FUSB300_OFFSET_EPPORT(ep->epnum)); + break; + default: + break; + } + + do { + reg = ioread32(fusb300->reg + FUSB300_OFFSET_IGR1); + reg &= FUSB300_IGR1_SYNF0_EMPTY_INT; + if (i) + printk(KERN_INFO"sync fifo is not empty!\n"); + i++; + } while (!reg); +} + +static u8 fusb300_get_epnstall(struct fusb300 *fusb300, u8 ep) +{ + u8 value; + u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET0(ep)); + + value = reg & FUSB300_EPSET0_STL; + + return value; +} + +static u8 fusb300_get_cxstall(struct fusb300 *fusb300) +{ + u8 value; + u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_CSR); + + value = (reg & FUSB300_CSR_STL) >> 1; + + return value; +} + +static void request_error(struct fusb300 *fusb300) +{ + fusb300_set_cxstall(fusb300); + printk(KERN_DEBUG "request error!!\n"); +} + +static void get_status(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl) +__releases(fusb300->lock) +__acquires(fusb300->lock) +{ + u8 ep; + u16 status = 0; + u16 w_index = ctrl->wIndex; + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + status = 1 << USB_DEVICE_SELF_POWERED; + break; + case USB_RECIP_INTERFACE: + status = 0; + break; + case USB_RECIP_ENDPOINT: + ep = w_index & USB_ENDPOINT_NUMBER_MASK; + if (ep) { + if (fusb300_get_epnstall(fusb300, ep)) + status = 1 << USB_ENDPOINT_HALT; + } else { + if (fusb300_get_cxstall(fusb300)) + status = 0; + } + break; + + default: + request_error(fusb300); + return; /* exit */ + } + + fusb300->ep0_data = cpu_to_le16(status); + fusb300->ep0_req->buf = &fusb300->ep0_data; + fusb300->ep0_req->length = 2; + + spin_unlock(&fusb300->lock); + fusb300_queue(fusb300->gadget.ep0, fusb300->ep0_req, GFP_KERNEL); + spin_lock(&fusb300->lock); +} + +static void set_feature(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl) +{ + u8 ep; + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + fusb300_set_cxdone(fusb300); + break; + case USB_RECIP_INTERFACE: + fusb300_set_cxdone(fusb300); + break; + case USB_RECIP_ENDPOINT: { + u16 w_index = le16_to_cpu(ctrl->wIndex); + + ep = w_index & USB_ENDPOINT_NUMBER_MASK; + if (ep) + fusb300_set_epnstall(fusb300, ep); + else + fusb300_set_cxstall(fusb300); + fusb300_set_cxdone(fusb300); + } + break; + default: + request_error(fusb300); + break; + } +} + +static void fusb300_clear_seqnum(struct fusb300 *fusb300, u8 ep) +{ + fusb300_enable_bit(fusb300, FUSB300_OFFSET_EPSET0(ep), + FUSB300_EPSET0_CLRSEQNUM); +} + +static void clear_feature(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl) +{ + struct fusb300_ep *ep = + fusb300->ep[ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK]; + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + fusb300_set_cxdone(fusb300); + break; + case USB_RECIP_INTERFACE: + fusb300_set_cxdone(fusb300); + break; + case USB_RECIP_ENDPOINT: + if (ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK) { + if (ep->wedged) { + fusb300_set_cxdone(fusb300); + break; + } + if (ep->stall) { + ep->stall = 0; + fusb300_clear_seqnum(fusb300, ep->epnum); + fusb300_clear_epnstall(fusb300, ep->epnum); + if (!list_empty(&ep->queue)) + enable_fifo_int(ep); + } + } + fusb300_set_cxdone(fusb300); + break; + default: + request_error(fusb300); + break; + } +} + +static void fusb300_set_dev_addr(struct fusb300 *fusb300, u16 addr) +{ + u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_DAR); + + reg &= ~FUSB300_DAR_DRVADDR_MSK; + reg |= FUSB300_DAR_DRVADDR(addr); + + iowrite32(reg, fusb300->reg + FUSB300_OFFSET_DAR); +} + +static void set_address(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl) +{ + if (ctrl->wValue >= 0x0100) + request_error(fusb300); + else { + fusb300_set_dev_addr(fusb300, ctrl->wValue); + fusb300_set_cxdone(fusb300); + } +} + +#define UVC_COPY_DESCRIPTORS(mem, src) \ + do { \ + const struct usb_descriptor_header * const *__src; \ + for (__src = src; *__src; ++__src) { \ + memcpy(mem, *__src, (*__src)->bLength); \ + mem += (*__src)->bLength; \ + } \ + } while (0) + +static void fusb300_ep0_complete(struct usb_ep *ep, + struct usb_request *req) +{ +} + +static int setup_packet(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl) +{ + u8 *p = (u8 *)ctrl; + u8 ret = 0; + u8 i = 0; + + fusb300_rdcxf(fusb300, p, 8); + fusb300->ep0_dir = ctrl->bRequestType & USB_DIR_IN; + fusb300->ep0_length = ctrl->wLength; + + /* check request */ + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + switch (ctrl->bRequest) { + case USB_REQ_GET_STATUS: + get_status(fusb300, ctrl); + break; + case USB_REQ_CLEAR_FEATURE: + clear_feature(fusb300, ctrl); + break; + case USB_REQ_SET_FEATURE: + set_feature(fusb300, ctrl); + break; + case USB_REQ_SET_ADDRESS: + set_address(fusb300, ctrl); + break; + case USB_REQ_SET_CONFIGURATION: + fusb300_enable_bit(fusb300, FUSB300_OFFSET_DAR, + FUSB300_DAR_SETCONFG); + /* clear sequence number */ + for (i = 1; i <= FUSB300_MAX_NUM_EP; i++) + fusb300_clear_seqnum(fusb300, i); + fusb300->reenum = 1; + ret = 1; + break; + default: + ret = 1; + break; + } + } else + ret = 1; + + return ret; +} + +static void fusb300_set_ep_bycnt(struct fusb300_ep *ep, u32 bycnt) +{ + struct fusb300 *fusb300 = ep->fusb300; + u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPFFR(ep->epnum)); + + reg &= ~FUSB300_FFR_BYCNT; + reg |= bycnt & FUSB300_FFR_BYCNT; + + iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPFFR(ep->epnum)); +} + +static void done(struct fusb300_ep *ep, struct fusb300_request *req, + int status) +{ + list_del_init(&req->queue); + + /* don't modify queue heads during completion callback */ + if (ep->fusb300->gadget.speed == USB_SPEED_UNKNOWN) + req->req.status = -ESHUTDOWN; + else + req->req.status = status; + + spin_unlock(&ep->fusb300->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&ep->fusb300->lock); + + if (ep->epnum) { + disable_fifo_int(ep); + if (!list_empty(&ep->queue)) + enable_fifo_int(ep); + } else + fusb300_set_cxdone(ep->fusb300); +} + +void fusb300_fill_idma_prdtbl(struct fusb300_ep *ep, + struct fusb300_request *req) +{ + u32 value; + u32 reg; + + /* wait SW owner */ + do { + reg = ioread32(ep->fusb300->reg + + FUSB300_OFFSET_EPPRD_W0(ep->epnum)); + reg &= FUSB300_EPPRD0_H; + } while (reg); + + iowrite32((u32) req->req.buf, ep->fusb300->reg + + FUSB300_OFFSET_EPPRD_W1(ep->epnum)); + + value = FUSB300_EPPRD0_BTC(req->req.length) | FUSB300_EPPRD0_H | + FUSB300_EPPRD0_F | FUSB300_EPPRD0_L | FUSB300_EPPRD0_I; + iowrite32(value, ep->fusb300->reg + FUSB300_OFFSET_EPPRD_W0(ep->epnum)); + + iowrite32(0x0, ep->fusb300->reg + FUSB300_OFFSET_EPPRD_W2(ep->epnum)); + + fusb300_enable_bit(ep->fusb300, FUSB300_OFFSET_EPPRDRDY, + FUSB300_EPPRDR_EP_PRD_RDY(ep->epnum)); +} + +static void fusb300_wait_idma_finished(struct fusb300_ep *ep) +{ + u32 reg; + + do { + reg = ioread32(ep->fusb300->reg + FUSB300_OFFSET_IGR1); + if ((reg & FUSB300_IGR1_VBUS_CHG_INT) || + (reg & FUSB300_IGR1_WARM_RST_INT) || + (reg & FUSB300_IGR1_HOT_RST_INT) || + (reg & FUSB300_IGR1_USBRST_INT) + ) + goto IDMA_RESET; + reg = ioread32(ep->fusb300->reg + FUSB300_OFFSET_IGR0); + reg &= FUSB300_IGR0_EPn_PRD_INT(ep->epnum); + } while (!reg); + + fusb300_clear_int(ep->fusb300, FUSB300_OFFSET_IGR0, + FUSB300_IGR0_EPn_PRD_INT(ep->epnum)); +IDMA_RESET: + fusb300_clear_int(ep->fusb300, FUSB300_OFFSET_IGER0, + FUSB300_IGER0_EEPn_PRD_INT(ep->epnum)); +} + +static void fusb300_set_idma(struct fusb300_ep *ep, + struct fusb300_request *req) +{ + dma_addr_t d; + u8 *tmp = NULL; + + d = dma_map_single(NULL, req->req.buf, req->req.length, DMA_TO_DEVICE); + + if (dma_mapping_error(NULL, d)) { + kfree(req->req.buf); + printk(KERN_DEBUG "dma_mapping_error\n"); + } + + dma_sync_single_for_device(NULL, d, req->req.length, DMA_TO_DEVICE); + + fusb300_enable_bit(ep->fusb300, FUSB300_OFFSET_IGER0, + FUSB300_IGER0_EEPn_PRD_INT(ep->epnum)); + + tmp = req->req.buf; + req->req.buf = (u8 *)d; + + fusb300_fill_idma_prdtbl(ep, req); + /* check idma is done */ + fusb300_wait_idma_finished(ep); + + req->req.buf = tmp; + + if (d) + dma_unmap_single(NULL, d, req->req.length, DMA_TO_DEVICE); +} + +static void in_ep_fifo_handler(struct fusb300_ep *ep) +{ + struct fusb300_request *req = list_entry(ep->queue.next, + struct fusb300_request, queue); + + if (req->req.length) { +#if 0 + fusb300_set_ep_bycnt(ep, req->req.length); + fusb300_wrfifo(ep, req); +#else + fusb300_set_idma(ep, req); +#endif + } + done(ep, req, 0); +} + +static void out_ep_fifo_handler(struct fusb300_ep *ep) +{ + struct fusb300 *fusb300 = ep->fusb300; + struct fusb300_request *req = list_entry(ep->queue.next, + struct fusb300_request, queue); + u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPFFR(ep->epnum)); + u32 length = reg & FUSB300_FFR_BYCNT; + + fusb300_rdfifo(ep, req, length); + + /* finish out transfer */ + if ((req->req.length == req->req.actual) || (length < ep->ep.maxpacket)) + done(ep, req, 0); +} + +static void check_device_mode(struct fusb300 *fusb300) +{ + u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_GCR); + + switch (reg & FUSB300_GCR_DEVEN_MSK) { + case FUSB300_GCR_DEVEN_SS: + fusb300->gadget.speed = USB_SPEED_SUPER; + break; + case FUSB300_GCR_DEVEN_HS: + fusb300->gadget.speed = USB_SPEED_HIGH; + break; + case FUSB300_GCR_DEVEN_FS: + fusb300->gadget.speed = USB_SPEED_FULL; + break; + default: + fusb300->gadget.speed = USB_SPEED_UNKNOWN; |