blob: a54ae5d47f4457f42044e363c25f73e8b52facab [file] [log] [blame]
/*
* linux/drivers/usb/gadget/ambarella_udc.c
* driver for High/Full speed USB device controller on Ambarella processors
*
* History:
* 2008/06/12 - [Cao Rongrong] created file
* 2009/03/16 - [Cao Rongrong] Change DMA descriptor allocate mode
*
* Copyright (C) 2008 by Ambarella, Inc.
* http://www.ambarella.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; either version 2 of the License, or
* (at your option) any later version.
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
#include <linux/usb.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
#include <linux/proc_fs.h>
#include <linux/of.h>
#include <mach/hardware.h>
#include <plat/rct.h>
#include <plat/udc.h>
#include "ambarella_udc.h"
#define DRIVER_DESC "Ambarella USB Device Controller Gadget"
#define DRIVER_VERSION "12 August 2015"
#define DRIVER_AUTHOR "Cao Rongrong <rrcao@ambarella.com>"
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
static const char gadget_name[] = "ambarella_udc";
#if 0
static const char driver_desc[] = DRIVER_DESC;
static const char ep0name [] = "ep0";
#endif
static const char *amb_ep_string[] = {
"ep0in", "ep1in", "ep2in", "ep3in",
"ep4in", "ep5in", "ep6in", "ep7in",
"ep8in", "ep9in", "ep10in", "ep11in",
"ep12in", "ep13in", "ep14in", "ep15in",
"ep0out", "ep1out", "ep2out", "ep3out",
"ep4out", "ep5out", "ep6out", "ep7out",
"ep8out", "ep9out", "ep10out", "ep11out",
"ep12out", "ep13out", "ep14out", "ep15out"
};
/*************************** DEBUG FUNCTION ***************************/
#define DEBUG_NORMAL 0
#define DEBUG_VERBOSE 1
#define AMBARELLA_USB_DEBUG 0
#if AMBARELLA_USB_DEBUG
#define dprintk(level,format,args...) \
do { \
if(level < 1) \
printk(KERN_DEBUG "%s: " format, __FUNCTION__,## args); \
} while(0)
#else
#define dprintk(args...) do { } while(0)
#endif
static inline struct ambarella_ep *to_ambarella_ep(struct usb_ep *ep)
{
return container_of(ep, struct ambarella_ep, ep);
}
static inline struct ambarella_udc *to_ambarella_udc(struct usb_gadget *gadget)
{
return container_of(gadget, struct ambarella_udc, gadget);
}
static inline struct ambarella_request *to_ambarella_req(struct usb_request *req)
{
return container_of(req, struct ambarella_request, req);
}
/************** PROC FILESYSTEM BEGIN *****************/
static void ambarella_uevent_work(struct work_struct *data)
{
struct ambarella_udc *udc;
char buf_status[64];
char buf_vbus[64];
char buf_driver[64];
char *envp[4];
udc = container_of(data, struct ambarella_udc, uevent_work);
buf_status[0] = '\0';
buf_vbus[0] = '\0';
buf_driver[0] = '\0';
spin_lock_irq(&udc->lock);
if (udc->pre_state == udc->gadget.state) {
spin_unlock_irq(&udc->lock);
return;
}
udc->pre_state = udc->gadget.state;
snprintf(buf_status, sizeof(buf_status),
"AMBUDC_STATUS=%s", usb_state_string(udc->gadget.state));
snprintf(buf_vbus, sizeof(buf_vbus), "AMBUDC_VBUS=%s",
udc->vbus_status ? "Connected" : "Disconnected");
snprintf(buf_driver, sizeof(buf_driver), "AMBUDC_DRIVER=%s",
udc->driver ? udc->driver->driver.name : "NULL");
spin_unlock_irq(&udc->lock);
envp[0] = buf_status;
envp[1] = buf_vbus;
envp[2] = buf_driver;
envp[3] = NULL;
kobject_uevent_env(&udc->dev->kobj, KOBJ_CHANGE, envp);
}
/* ========================================================================== */
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
#include <linux/seq_file.h>
static const char proc_node_name[] = "driver/udc";
static int ambarella_udc_proc_show(struct seq_file *m, void *v)
{
struct ambarella_udc *udc;
struct usb_ctrlrequest *crq;
unsigned long flags;
udc = (struct ambarella_udc *)m->private;
crq = (struct usb_ctrlrequest *)&udc->setup[0];
spin_lock_irqsave(&udc->lock, flags);
/* basic device status */
seq_printf(m,
DRIVER_DESC "\n"
"Name: %s\n"
"Version: %s\n"
"Author: %s\n\n"
"Gadget driver: %s\n"
"Host: %s\n\n",
gadget_name,
DRIVER_VERSION,
DRIVER_AUTHOR,
udc->driver ? udc->driver->driver.name : "(none)",
udc->vbus_status ? (udc->gadget.speed == USB_SPEED_HIGH ?
"high speed" : "full speed") : "disconnected");
seq_printf(m, "AMBUDC_STATUS=%s",
usb_state_string(udc->gadget.state));
seq_printf(m,
"the last setup packet is: \n"
"bRequestType = 0x%02x, bRequest = 0x%02x,\n"
"wValue = 0x%04x, wIndex = 0x%04x, wLength = 0x%04x\n\n",
crq->bRequestType, crq->bRequest, crq->wValue, crq->wIndex,
crq->wLength);
spin_unlock_irqrestore(&udc->lock, flags);
return 0;
}
static int ambarella_udc_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, ambarella_udc_proc_show, PDE_DATA(inode));
}
static const struct file_operations ambarella_udc_fops = {
.open = ambarella_udc_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
#define remove_debugfs_files() remove_proc_entry(proc_node_name, NULL)
#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
/************** PROC FILESYSTEM END*****************/
static void ambarella_regaddr_map(struct ambarella_udc *udc)
{
u32 i;
for(i = 0; i < EP_NUM_MAX; i++) {
struct ambarella_ep *ep;
ep = &udc->ep[i];
if(ep->dir == USB_DIR_IN){
ep->ep_reg.ctrl_reg = USB_EP_IN_CTRL_REG(ep->id);
ep->ep_reg.sts_reg = USB_EP_IN_STS_REG(ep->id);
ep->ep_reg.buf_sz_reg = USB_EP_IN_BUF_SZ_REG(ep->id);
ep->ep_reg.max_pkt_sz_reg =
USB_EP_IN_MAX_PKT_SZ_REG(ep->id);
ep->ep_reg.dat_desc_ptr_reg =
USB_EP_IN_DAT_DESC_PTR_REG(ep->id);
} else {
ep->ep_reg.ctrl_reg = USB_EP_OUT_CTRL_REG(ep->id - 16);
ep->ep_reg.sts_reg = USB_EP_OUT_STS_REG(ep->id - 16);
ep->ep_reg.buf_sz_reg =
USB_EP_OUT_PKT_FRM_NUM_REG(ep->id - 16);
ep->ep_reg.max_pkt_sz_reg =
USB_EP_OUT_MAX_PKT_SZ_REG(ep->id - 16);
ep->ep_reg.dat_desc_ptr_reg =
USB_EP_OUT_DAT_DESC_PTR_REG(ep->id - 16);
}
ep->ep_reg.setup_buf_ptr_reg = (i == CTRL_OUT) ?
USB_EP_OUT_SETUP_BUF_PTR_REG(ep->id - 16) : (u32)NULL;
}
}
static void ambarella_disable_all_intr(void)
{
/* device interrupt mask register */
amba_writel(USB_DEV_INTR_MSK_REG, 0x0000007f);
amba_writel(USB_DEV_EP_INTR_MSK_REG, 0xffffffff);
amba_writel(USB_DEV_INTR_REG, 0x0000007f);
amba_writel(USB_DEV_EP_INTR_REG, 0xffffffff);
}
static void ambarella_ep_fifo_flush(struct ambarella_ep *ep)
{
if(ep->dir == USB_DIR_IN) /* Empty Tx FIFO */
amba_setbitsl(ep->ep_reg.ctrl_reg, USB_EP_FLUSH);
else { /* Empty RX FIFO */
if (!(amba_readl(USB_DEV_STS_REG) & USB_DEV_RXFIFO_EMPTY_STS)) {
int retry_count = 1000;
amba_setbitsl(USB_DEV_CTRL_REG, USB_DEV_NAK);
amba_setbitsl(USB_DEV_CTRL_REG, USB_DEV_FLUSH_RXFIFO);
while(!(amba_readl(USB_DEV_STS_REG) & USB_DEV_RXFIFO_EMPTY_STS)) {
if (retry_count-- < 0) {
printk (KERN_ERR "%s: failed", __func__);
break;
}
udelay(5);
}
amba_clrbitsl(USB_DEV_CTRL_REG, USB_DEV_NAK);
}
}
}
/*
* Name: ambarella_flush_fifo
* Description:
* Empty Tx/Rx FIFO of DMA
*/
static void ambarella_udc_fifo_flush(struct ambarella_udc *udc)
{
struct ambarella_ep *ep;
u32 ep_id;
for (ep_id = 0; ep_id < EP_NUM_MAX; ep_id++) {
ep = &udc->ep[ep_id];
if(ep->ep.desc == NULL && !IS_EP0(ep))
continue;
ambarella_ep_fifo_flush(ep);
}
}
static void ambarella_udc_reset(void __iomem *reset_reg, struct device_node *np)
{
amba_rct_setbitsl(reset_reg, UDC_SOFT_RESET_MASK);
msleep(1);
amba_rct_clrbitsl(reset_reg, UDC_SOFT_RESET_MASK);
};
static void ambarella_init_usb(struct ambarella_udc *udc)
{
u32 value;
/* disconnect to host */
amba_setbitsl(USB_DEV_CTRL_REG, USB_DEV_SOFT_DISCON);
/* disable all interrupts */
ambarella_disable_all_intr();
/* disable Tx and Rx DMA */
amba_clrbitsl(USB_DEV_CTRL_REG, USB_DEV_RCV_DMA_EN | USB_DEV_TRN_DMA_EN);
/* flush dma fifo, may used in AMboot */
ambarella_udc_fifo_flush(udc);
/* device config register */
value = USB_DEV_SPD_HI |
USB_DEV_SELF_POWER |
USB_DEV_PHY_8BIT |
USB_DEV_UTMI_DIR_BI |
USB_DEV_HALT_ACK |
USB_DEV_SET_DESC_STALL |
USB_DEV_DDR |
USB_DEV_CSR_PRG_EN |
USB_DEV_REMOTE_WAKEUP_EN;
amba_setbitsl(USB_DEV_CFG_REG, value);
/* device control register */
value = USB_DEV_DESC_UPD_PYL |
USB_DEV_LITTLE_ENDN |
USB_DEV_DMA_MD;
amba_setbitsl(USB_DEV_CTRL_REG, value);
// udelay(200); // FIXME: how long to wait is the best?
}
/*
* Name: init_setup_descriptor
* Description:
* Config the setup packet to specific endpoint register
*/
static void init_setup_descriptor(struct ambarella_udc *udc)
{
struct ambarella_ep *ep = &udc->ep[CTRL_OUT];
udc->setup_buf->status = USB_DMA_BUF_HOST_RDY;
udc->setup_buf->reserved = 0xffffffff;
udc->setup_buf->data0 = 0xffffffff;
udc->setup_buf->data1 = 0xffffffff;
amba_writel(ep->ep_reg.setup_buf_ptr_reg, udc->setup_addr | udc->dma_fix);
}
static int init_null_pkt_desc(struct ambarella_udc *udc)
{
udc->dummy_desc = dma_pool_alloc(udc->desc_dma_pool, GFP_KERNEL,
&udc->dummy_desc_addr);
if(udc->dummy_desc == NULL){
printk(KERN_ERR "No memory to DMA\n");
return -ENOMEM;
}
udc->dummy_desc->data_ptr = udc->dummy_desc_addr | udc->dma_fix;
udc->dummy_desc->reserved = 0xffffffff;
udc->dummy_desc->next_desc_ptr = udc->dummy_desc_addr | udc->dma_fix;
udc->dummy_desc->status = USB_DMA_BUF_HOST_RDY | USB_DMA_LAST;
return 0;
}
static void init_ep0(struct ambarella_udc *udc)
{
struct ambarella_ep_reg *ep_reg;
ep_reg = &udc->ep[CTRL_IN].ep_reg;
amba_writel(ep_reg->ctrl_reg, USB_EP_TYPE_CTRL);
amba_writel(ep_reg->buf_sz_reg, USB_TXFIFO_DEPTH_CTRLIN);
amba_writel(ep_reg->max_pkt_sz_reg, USB_EP_CTRLIN_MAX_PKT_SZ);
udc->ep[CTRL_IN].ctrl_sts_phase = 0;
ep_reg = &udc->ep[CTRL_OUT].ep_reg;
amba_writel(ep_reg->ctrl_reg, USB_EP_TYPE_CTRL);
amba_writel(ep_reg->max_pkt_sz_reg, USB_EP_CTRL_MAX_PKT_SZ);
init_setup_descriptor(udc);
udc->ep[CTRL_OUT].ctrl_sts_phase = 0;
/* This should be set after gadget->bind */
udc->ep[CTRL_OUT].ep.driver_data = udc->ep[CTRL_IN].ep.driver_data;
/* FIXME: For A5S, this bit must be set,
* or USB_UDC_REG can't be read or write */
amba_setbitsl(USB_DEV_CTRL_REG, USB_DEV_REMOTE_WAKEUP);
/* setup ep0 CSR. Note: ep0-in and ep0-out share the same CSR reg */
amba_clrbitsl(USB_UDC_REG(CTRL_IN), 0x7ff << 19);
amba_setbitsl(USB_UDC_REG(CTRL_IN), USB_EP_CTRL_MAX_PKT_SZ << 19);
/* the direction bit should not take effect for ep0 */
amba_setbitsl(USB_UDC_REG(CTRL_IN), 0x1 << 4);
}
static int ambarella_map_dma_buffer(struct ambarella_ep *ep,
struct ambarella_request *req)
{
struct ambarella_udc *udc = ep->udc;
enum dma_data_direction dmadir;
dmadir = (ep->dir & USB_DIR_IN) ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
if (likely(!req->use_aux_buf)) {
/* map req.buf, and get req's dma address */
if (req->req.dma == DMA_ADDR_INVALID) {
req->req.dma = dma_map_single(udc->dev,
req->req.buf, req->req.length, dmadir);
/* dma address isn't 8-byte align */
if(req->req.dma & 0x7){
printk("dma address isn't 8-byte align\n");
BUG();
}
req->mapped = 1;
} else {
dma_sync_single_for_device(udc->dev,
req->req.dma, req->req.length, dmadir);
req->mapped = 0;
}
} else {
if (req->dma_aux == DMA_ADDR_INVALID) {
req->dma_aux = dma_map_single(udc->dev,
req->buf_aux, req->req.length, dmadir);
/* dma address isn't 8-byte align */
if(req->dma_aux & 0x7){
printk("dma address isn't 8-byte align\n");
BUG();
}
req->mapped = 1;
} else {
dma_sync_single_for_device(udc->dev,
req->dma_aux, req->req.length, dmadir);
req->mapped = 0;
}
}
return 0;
}
static int ambarella_unmap_dma_buffer(struct ambarella_ep *ep,
struct ambarella_request *req)
{
struct ambarella_udc *udc = ep->udc;
enum dma_data_direction dmadir;
dmadir = (ep->dir & USB_DIR_IN) ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
if (likely(!req->use_aux_buf)) {
if (req->mapped) {
dma_unmap_single(udc->dev,
req->req.dma, req->req.length, dmadir);
req->req.dma = DMA_ADDR_INVALID;
req->mapped = 0;
} else {
dma_sync_single_for_cpu(udc->dev,
req->req.dma, req->req.length, dmadir);
}
} else {
if (req->mapped) {
dma_unmap_single(udc->dev,
req->dma_aux, req->req.length, dmadir);
req->dma_aux = DMA_ADDR_INVALID;
req->mapped = 0;
} else {
dma_sync_single_for_cpu(udc->dev,
req->dma_aux, req->req.length, dmadir);
}
}
return 0;
}
static struct ambarella_data_desc *ambarella_get_last_desc(struct ambarella_ep *ep)
{
struct ambarella_data_desc *desc = ep->data_desc;
int retry_count = 1000;
while(desc && (desc->status & USB_DMA_LAST) == 0) {
if (retry_count-- < 0) {
printk(KERN_ERR "Can't find the last descriptor\n");
break;
}
if (desc->last_aux == 1)
break;
desc = desc->next_desc_virt;
};
return desc;
}
static u32 ambarella_check_bna_error (struct ambarella_ep *ep, u32 ep_status)
{
u32 retval = 0;
/* Error: Buffer Not Available */
if (ep_status & USB_EP_BUF_NOT_AVAIL) {
//printk(KERN_ERR "[USB]:BNA error in %s\n", ep->ep.name);
amba_writel(ep->ep_reg.sts_reg, USB_EP_BUF_NOT_AVAIL);
retval = 1;
}
return retval;
}
static u32 ambarella_check_he_error (struct ambarella_ep *ep, u32 ep_status)
{
u32 retval = 0;
/* Error: Host Error */
if (ep_status & USB_EP_HOST_ERR) {
printk(KERN_ERR "[USB]:HE error in %s\n", ep->ep.name);
amba_writel(ep->ep_reg.sts_reg, USB_EP_HOST_ERR);
retval = 1;
}
return retval;
}
static u32 ambarella_check_dma_error (struct ambarella_ep *ep)
{
u32 retval = 0, sts_tmp1, sts_tmp2;
if(ep->last_data_desc){
sts_tmp1 = ep->last_data_desc->status & USB_DMA_BUF_STS;
sts_tmp2 = ep->last_data_desc->status & USB_DMA_RXTX_STS;
if ((sts_tmp1 != USB_DMA_BUF_DMA_DONE) || (sts_tmp2 != USB_DMA_RXTX_SUCC)){
//printk(KERN_ERR "%s: DMA failed\n", ep->ep.name);
retval = 1;
}
}
return retval;
}
static void ambarella_free_descriptor(struct ambarella_ep *ep,
struct ambarella_request *req)
{
struct ambarella_data_desc *data_desc, *next_data_desc;
struct dma_pool *desc_dma_pool = ep->udc->desc_dma_pool;
int i;
next_data_desc = req->data_desc;
for(i = 0; i < req->desc_count; i++){
data_desc = next_data_desc;
data_desc->status = USB_DMA_BUF_HOST_BUSY | USB_DMA_LAST;
data_desc->data_ptr = 0xffffffff;
data_desc->next_desc_ptr = 0;
data_desc->last_aux = 1;
next_data_desc = data_desc->next_desc_virt;
dma_pool_free(desc_dma_pool, data_desc, data_desc->cur_desc_addr);
}
req->desc_count = 0;
req->data_desc = NULL;
req->data_desc_addr = 0;
}
static int ambarella_reuse_descriptor(struct ambarella_ep *ep,
struct ambarella_request *req, u32 desc_count, dma_addr_t start_address)
{
struct ambarella_udc *udc = ep->udc;
struct ambarella_data_desc *data_desc, *next_data_desc;
u32 data_transmit, rest_bytes, i;
dma_addr_t buf_dma_address;
next_data_desc = req->data_desc;
for(i = 0; i < desc_count; i++){
rest_bytes = req->req.length - i * ep->ep.maxpacket;
if(ep->dir == USB_DIR_IN)
data_transmit = rest_bytes < ep->ep.maxpacket ?
rest_bytes : ep->ep.maxpacket;
else
data_transmit = 0;
data_desc = next_data_desc;
data_desc->status = USB_DMA_BUF_HOST_RDY | data_transmit;
data_desc->reserved = 0xffffffff;
buf_dma_address = start_address + i * ep->ep.maxpacket;
data_desc->data_ptr = buf_dma_address | udc->dma_fix;
data_desc->last_aux = 0;
next_data_desc = data_desc->next_desc_virt;
}
/* Patch last one. */
data_desc->status |= USB_DMA_LAST;
data_desc->last_aux = 1;
return 0;
}
static int ambarella_prepare_descriptor(struct ambarella_ep *ep,
struct ambarella_request *req, gfp_t gfp)
{
struct ambarella_udc *udc = ep->udc;
struct ambarella_data_desc *data_desc = NULL;
struct ambarella_data_desc *prev_data_desc = NULL;
dma_addr_t desc_phys, start_address, buf_dma_address;
u32 desc_count, data_transmit, rest_bytes, i;
if (likely(!req->use_aux_buf))
start_address = req->req.dma;
else
start_address = req->dma_aux;
desc_count = (req->req.length + ep->ep.maxpacket - 1) / ep->ep.maxpacket;
if(req->req.zero && (req->req.length % ep->ep.maxpacket == 0))
desc_count++;
if(desc_count == 0)
desc_count = 1;
req->active_desc_count = desc_count;
if (desc_count <= req->desc_count) {
ambarella_reuse_descriptor(ep, req, desc_count, start_address);
return 0;
}
if(req->desc_count > 0)
ambarella_free_descriptor(ep, req);
req->desc_count = desc_count;
for(i = 0; i < desc_count; i++){
rest_bytes = req->req.length - i * ep->ep.maxpacket;
if(ep->dir == USB_DIR_IN)
data_transmit = rest_bytes < ep->ep.maxpacket ?
rest_bytes : ep->ep.maxpacket;
else
data_transmit = 0;
data_desc = dma_pool_alloc(udc->desc_dma_pool, gfp, &desc_phys);
if (!data_desc) {
req->desc_count = i;
if(req->desc_count > 0)
ambarella_free_descriptor(ep, req);
return -ENOMEM;
}
data_desc->status = USB_DMA_BUF_HOST_RDY | data_transmit;
data_desc->reserved = 0xffffffff;
buf_dma_address = start_address + i * ep->ep.maxpacket;
data_desc->data_ptr = buf_dma_address | udc->dma_fix;
data_desc->next_desc_ptr = 0;
data_desc->rsvd1 = 0xffffffff;
data_desc->last_aux = 0;
data_desc->cur_desc_addr = desc_phys;
if(prev_data_desc){
prev_data_desc->next_desc_ptr = desc_phys | udc->dma_fix;
prev_data_desc->next_desc_virt = data_desc;
}
prev_data_desc = data_desc;
if(i == 0){
req->data_desc = data_desc;
req->data_desc_addr = desc_phys;
}
}
/* Patch last one */
data_desc->status |= USB_DMA_LAST;
data_desc->next_desc_ptr = 0;
data_desc->next_desc_virt = NULL;
data_desc->last_aux = 1;
return 0;
}
static void ambarella_clr_ep_nak(struct ambarella_ep *ep)
{
struct ambarella_ep_reg *ep_reg = &ep->ep_reg;
amba_setbitsl(ep_reg->ctrl_reg, USB_EP_CLR_NAK);
if (amba_readl(ep_reg->ctrl_reg) & USB_EP_NAK_STS) {
/* can't clear NAK, let somebody clear it after Rx DMA is done. */
ep->need_cnak = 1;
}else{
ep->need_cnak = 0;
}
}
static void ambarella_set_ep_nak(struct ambarella_ep *ep)
{
amba_setbitsl(ep->ep_reg.ctrl_reg, USB_EP_SET_NAK);
}
static void ambarella_enable_rx_dma(struct ambarella_ep *ep)
{
ep->dma_going = 1;
amba_setbitsl(USB_DEV_CTRL_REG, USB_DEV_RCV_DMA_EN);
}
static void ambarella_patch_iso_desc(struct ambarella_udc *udc,
struct ambarella_request * req, struct ambarella_ep *ep, int frame_fix)
{
struct ambarella_data_desc *data_desc;
u32 current_frame, max_packet_num, i, j;
current_frame = ambarella_udc_get_frame(&udc->gadget);
data_desc = req->data_desc;
/* according to USB2.0 spec, each microframe can send 3 packets at most */
for (i = 0; i < req->active_desc_count; i += j) {
if ((req->active_desc_count - i) >= ISO_MAX_PACKET)
max_packet_num = ISO_MAX_PACKET;
else
max_packet_num = req->active_desc_count - i;
for (j = 0; j < max_packet_num; j++) {
data_desc->status |= ((current_frame + ep->frame_offset + frame_fix) << 16);
data_desc->status |= (max_packet_num << 14);
data_desc = data_desc->next_desc_virt;
}
}
}
static void ambarella_set_tx_dma(struct ambarella_ep *ep,
struct ambarella_request * req, int frame_fix)
{
struct ambarella_udc *udc = ep->udc;
struct ambarella_ep_reg *ep_reg = &ep->ep_reg;
if (IS_ISO_IN_EP(ep))
ambarella_patch_iso_desc(udc, req, ep, frame_fix);
ep->data_desc = req->data_desc;
amba_writel(ep_reg->dat_desc_ptr_reg, req->data_desc_addr | udc->dma_fix);
/* set Poll command to transfer data to Tx FIFO */
amba_setbitsl(ep_reg->ctrl_reg, USB_EP_POLL_DEMAND);
ep->dma_going = 1;
}
static void ambarella_set_rx_dma(struct ambarella_ep *ep,
struct ambarella_request * req)
{
struct ambarella_ep_reg *ep_reg = &ep->ep_reg;
struct ambarella_udc *udc = ep->udc;
u32 i;
if(req){
ep->data_desc = req->data_desc;
amba_writel(ep_reg->dat_desc_ptr_reg,
req->data_desc_addr | udc->dma_fix);
} else {
/* receive zero-length-packet */
udc->dummy_desc->status = USB_DMA_BUF_HOST_RDY | USB_DMA_LAST;
amba_writel(ep_reg->dat_desc_ptr_reg,
udc->dummy_desc_addr | udc->dma_fix);
}
/* enable dma completion interrupt for next RX data */
amba_clrbitsl(USB_DEV_EP_INTR_MSK_REG, 1 << ep->id);
/* re-enable DMA read */
ambarella_enable_rx_dma(ep);
if (amba_readl(USB_DEV_STS_REG) & USB_DEV_RXFIFO_EMPTY_STS) {
/* clear NAK for TX dma */
for (i = 0; i < EP_NUM_MAX; i++) {
struct ambarella_ep *_ep = &udc->ep[i];
if (_ep->need_cnak == 1)
ambarella_clr_ep_nak(_ep);
}
}
/* clear NAK */
ambarella_clr_ep_nak(ep);
}
static int ambarella_handle_ep_stall(struct ambarella_ep *ep, u32 ep_status)
{
int ret = 0;
if (ep_status & USB_EP_RCV_CLR_STALL) {
amba_setbitsl(ep->ep_reg.ctrl_reg, USB_EP_CLR_NAK);
amba_clrbitsl(ep->ep_reg.ctrl_reg, USB_EP_STALL);
amba_setbitsl(ep->ep_reg.sts_reg, USB_EP_RCV_CLR_STALL);
ret = 1;
}
if (ep_status & USB_EP_RCV_SET_STALL) {
amba_setbitsl(ep->ep_reg.ctrl_reg, USB_EP_STALL);
if (ep->dir == USB_DIR_IN)
amba_setbitsl(ep->ep_reg.ctrl_reg, USB_EP_FLUSH);
amba_setbitsl(ep->ep_reg.sts_reg, USB_EP_RCV_SET_STALL);
ret = 1;
}
return ret;
}
static void ambarella_handle_request_packet(struct ambarella_udc *udc)
{
struct usb_ctrlrequest *crq;
int ret;
ambarella_ep_nuke(&udc->ep[CTRL_OUT], -EPROTO); // Todo
/* read out setup packet */
udc->setup[0] = udc->setup_buf->data0;
udc->setup[1] = udc->setup_buf->data1;
/* reinitialize setup packet */
init_setup_descriptor(udc);
crq = (struct usb_ctrlrequest *) &udc->setup[0];
dprintk(DEBUG_NORMAL, "bRequestType = 0x%02x, bRequest = 0x%02x, "
"wValue = 0x%04x, wIndex = 0x%04x, wLength = 0x%04x\n",
crq->bRequestType, crq->bRequest, crq->wValue, crq->wIndex,
crq->wLength);
if((crq->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD){
switch(crq->bRequest)
{
case USB_REQ_GET_STATUS:
case USB_REQ_SET_ADDRESS:
case USB_REQ_CLEAR_FEATURE:
case USB_REQ_SET_FEATURE:
pr_err("%s: This bRequest is not implemented!\n"
"\tbRequestType = 0x%02x, bRequest = 0x%02x,\n"
"\twValue = 0x%04x, wIndex = 0x%04x, wLength = 0x%04x\n",
__func__,
crq->bRequestType, crq->bRequest, crq->wValue, crq->wIndex,
crq->wLength);
}
}
if (crq->bRequestType & USB_DIR_IN)
udc->gadget.ep0 = &udc->ep[CTRL_IN].ep;
else
udc->gadget.ep0 = &udc->ep[CTRL_OUT].ep;
spin_unlock(&udc->lock);
ret = udc->driver->setup(&udc->gadget, crq);
spin_lock(&udc->lock);
if (ret < 0) {
pr_err("%s: SETUP request failed (%d)\n", __func__, ret);
amba_setbitsl(udc->ep[CTRL_IN].ep_reg.ctrl_reg, USB_EP_STALL | USB_EP_FLUSH);
/* Re-enable Rx DMA to receive next setup packet */
ambarella_enable_rx_dma(&udc->ep[CTRL_OUT]);
ambarella_clr_ep_nak(&udc->ep[CTRL_OUT]);
}
return;
}
static void ambarella_handle_data_in(struct ambarella_ep *ep)
{
struct ambarella_request *req = NULL;
struct ambarella_udc *udc = ep->udc;
/* get request */
if (list_empty(&ep->queue)) {
printk(KERN_DEBUG "%s: req NULL\n", __func__);
return ;
}
req = list_first_entry(&ep->queue, struct ambarella_request,queue);
/* If error happened, issue the request again */
if (ambarella_check_dma_error(ep))
req->req.status = -EPROTO;
else {
/* No error happened, so all the data has been sent to host */
req->req.actual = req->req.length;
}
ambarella_udc_done(ep, req, 0);
if(ep->id == CTRL_IN){
/* For STATUS-OUT stage */
udc->ep[CTRL_OUT].ctrl_sts_phase = 1;
ambarella_set_rx_dma(&udc->ep[CTRL_OUT], NULL);
}
}
static int ambarella_handle_data_out(struct ambarella_ep *ep)
{
struct ambarella_request *req = NULL;
struct ambarella_udc *udc = ep->udc;
u32 recv_size = 0, req_size;
/* get request */
if (list_empty(&ep->queue)) {
printk(KERN_DEBUG "%s: req NULL\n", __func__);
return -EINVAL;
}
req = list_first_entry(&ep->queue, struct ambarella_request,queue);
/* If error happened, issue the request again */
if (ambarella_check_dma_error(ep))
req->req.status = -EPROTO;
recv_size = ep->last_data_desc->status & USB_DMA_RXTX_BYTES;
if (!recv_size && req->req.length == UDC_DMA_MAXPACKET) {
/* on 64k packets the RXBYTES field is zero */
recv_size = UDC_DMA_MAXPACKET;
}
req_size = req->req.length - req->req.actual;
if (recv_size > req_size) {
if ((req_size % ep->ep.maxpacket) != 0)
req->req.status = -EOVERFLOW;
recv_size = req_size;
}
req->req.actual += recv_size;
ambarella_udc_done(ep, req, 0);
if(ep->id == CTRL_OUT) {
/* For STATUS-IN stage */
ambarella_clr_ep_nak(&udc->ep[CTRL_IN]);
/* Re-enable Rx DMA to receive next setup packet */
ambarella_enable_rx_dma(ep);
ep->dma_going = 0;
}
return 0;
}
static void ambarella_udc_done(struct ambarella_ep *ep,
struct ambarella_request *req, int status)
{
unsigned halted_tmp, need_queue = 0;
struct ambarella_request *next_req;
halted_tmp = ep->halted;
list_del_init(&req->queue);
if(!list_empty(&ep->queue) && !ep->halted && !ep->cancel_transfer){
need_queue = 1;
} else if (!IS_EP0(ep) && (ep->dir == USB_DIR_IN) && !ep->cancel_transfer) {
/* ep->ep.desc = NULL when ep disabled */
if (!ep->ep.desc || IS_ISO_IN_EP(ep))
ambarella_set_ep_nak(ep);
amba_setbitsl(USB_DEV_EP_INTR_MSK_REG, 1 << ep->id);
}
if (likely (req->req.status == -EINPROGRESS))
req->req.status = status;
else
status = req->req.status;
ambarella_unmap_dma_buffer(ep, req);
if (req->use_aux_buf && ep->dir != USB_DIR_IN)
memcpy(req->req.buf, req->buf_aux, req->req.actual);
ep->data_desc = NULL;
ep->last_data_desc = NULL;
ep->halted = 1;
spin_unlock(&ep->udc->lock);
req->req.complete(&ep->ep, &req->req);
spin_lock(&ep->udc->lock);
ep->halted = halted_tmp;
if(need_queue){
next_req = list_first_entry (&ep->queue,
struct ambarella_request, queue);
switch(ep->dir) {
case USB_DIR_IN:
/* no need to wait for IN-token for ISO transfer */
if (IS_ISO_IN_EP(ep)) {
amba_writel(ep->ep_reg.sts_reg, USB_EP_IN_PKT);
ambarella_set_tx_dma(ep, next_req, 1);
}
ambarella_clr_ep_nak(ep);
break;
case USB_DIR_OUT:
ambarella_set_rx_dma(ep, next_req);
break;
default:
return;
}
}
}
static void ambarella_ep_nuke(struct ambarella_ep *ep, int status)
{
while (!list_empty (&ep->queue)) {
struct ambarella_request *req;
req = list_first_entry(&ep->queue,
struct ambarella_request, queue);
ambarella_udc_done(ep, req, status);
}
}
/*
* Name: device_interrupt
* Description:
* Process related device interrupt
*/
static void udc_device_interrupt(struct ambarella_udc *udc, u32 int_value)
{
/* case 1. Get set_config or set_interface request from host */
if (int_value & (USB_DEV_SET_CFG | USB_DEV_SET_INTF)) {
struct usb_ctrlrequest crq;
u32 i, ret, csr_config;
if(int_value & USB_DEV_SET_CFG) {
/* Ack the SC interrupt */
amba_writel(USB_DEV_INTR_REG, USB_DEV_SET_CFG);
udc->cur_config = (u16)(amba_readl(USB_DEV_STS_REG) & USB_DEV_CFG_NUM);
crq.bRequestType = 0x00;
crq.bRequest = USB_REQ_SET_CONFIGURATION;
crq.wValue = cpu_to_le16(udc->cur_config);
crq.wIndex = 0x0000;
crq.wLength = 0x0000;
} else if(int_value & USB_DEV_SET_INTF){
/* Ack the SI interrupt */
amba_writel(USB_DEV_INTR_REG, USB_DEV_SET_INTF);
udc->cur_intf = (amba_readl(USB_DEV_STS_REG) & USB_DEV_INTF_NUM) >> 4;
udc->cur_alt = (amba_readl(USB_DEV_STS_REG) & USB_DEV_ALT_SET) >> 8;
crq.bRequestType = 0x01;
crq.bRequest = USB_REQ_SET_INTERFACE;
crq.wValue = cpu_to_le16(udc->cur_alt);
crq.wIndex = cpu_to_le16(udc->cur_intf);
crq.wLength = 0x0000;
}
for (i = 0; i < EP_NUM_MAX; i++){
udc->ep[i].halted = 0;
amba_clrbitsl(udc->ep[i].ep_reg.ctrl_reg, USB_EP_STALL);
}
/* setup ep0 CSR. Note: ep0-in and ep0-out share the same CSR reg */
csr_config = (udc->cur_config << 7) | (udc->cur_intf << 11) |
(udc->cur_alt << 15);
amba_clrbitsl(USB_UDC_REG(CTRL_IN), 0xfff << 7);
amba_setbitsl(USB_UDC_REG(CTRL_IN), csr_config);
udc->auto_ack_0_pkt = 1;
ambarella_ep_nuke(&udc->ep[CTRL_OUT], -EPROTO);
spin_unlock(&udc->lock);
ret = udc->driver->setup(&udc->gadget, &crq);
spin_lock(&udc->lock);
if(ret < 0)
printk(KERN_ERR "set config failed. (%d)\n", ret);
/* told UDC the configuration is done, and to ack HOST
* UDC has to ack the host quickly, or Host will think failed,
* do don't add much debug message when receive SC/SI irq.*/
amba_setbitsl(USB_DEV_CTRL_REG, USB_DEV_CSR_DONE);
udelay(150);
usb_gadget_set_state(&udc->gadget, USB_STATE_CONFIGURED);
schedule_work(&udc->uevent_work);
}
/* case 2. Get reset Interrupt */
else if (int_value & USB_DEV_RESET) {
dprintk(DEBUG_NORMAL, "USB reset IRQ\n");
amba_writel(USB_DEV_INTR_REG, USB_DEV_RESET);
ambarella_disable_all_intr();
if (udc->host_suspended && udc->driver && udc->driver->resume){
spin_unlock(&udc->lock);
udc->driver->resume(&udc->gadget);
spin_lock(&udc->lock);
udc->host_suspended = 0;
}
ambarella_stop_activity(udc);
udc->gadget.speed = USB_SPEED_UNKNOWN;
udc->auto_ack_0_pkt = 0;
udc->remote_wakeup_en = 0;
udc->udc_is_enabled = 0;
ambarella_udc_enable(udc);
usb_gadget_set_state(&udc->gadget, USB_STATE_NOTATTACHED);
schedule_work(&udc->uevent_work);
#if 0
/* enable suspend interrupt */
amba_clrbitsl(USB_DEV_INTR_MSK_REG, UDC_INTR_MSK_US);
#endif
}
/* case 3. Get suspend Interrupt */
else if (int_value & USB_DEV_SUSP) {
pr_err("%s: USB suspend IRQ\n", __func__);
amba_writel(USB_DEV_INTR_REG, USB_DEV_SUSP);
if (udc->driver->suspend) {
udc->host_suspended = 1;
spin_unlock(&udc->lock);
udc->driver->suspend(&udc->gadget);
spin_lock(&udc->lock);
}
}
/* case 4. enumeration complete */
else if(int_value & USB_DEV_ENUM_CMPL) {
u32 value = 0;
/* Ack the CMPL interrupt */
amba_writel(USB_DEV_INTR_REG, USB_DEV_ENUM_CMPL);
value = amba_readl(USB_DEV_STS_REG) & USB_DEV_ENUM_SPD;
if(value == USB_DEV_ENUM_SPD_HI) { /* high speed */
dprintk(DEBUG_NORMAL,"enumeration IRQ - "
"High-speed bus detected\n");
udc->gadget.speed = USB_SPEED_HIGH;
} else if (value == USB_DEV_ENUM_SPD_FU) { /* full speed */
dprintk(DEBUG_NORMAL,"enumeration IRQ - "
"Full-speed bus detected\n");
udc->gadget.speed = USB_SPEED_FULL;
} else {
printk(KERN_ERR "Not supported speed!"
"USB_DEV_STS_REG = 0x%x\n", value);
udc->gadget.speed = USB_SPEED_UNKNOWN;
}
} /* ENUM COMPLETE */
else {
printk(KERN_ERR "Unknown Interrupt:0x%08x\n", int_value);
/* Ack the Unknown interrupt */
amba_writel(USB_DEV_INTR_REG, int_value);
}
}
/*
* Name: udc_epin_interrupt
* Description:
* Process IN(CTRL or BULK) endpoint interrupt
*/
static void udc_epin_interrupt(struct ambarella_udc *udc, u32 ep_id)
{
u32 ep_status = 0;
struct ambarella_ep *ep = &udc->ep[ep_id];
struct ambarella_request *req;
ep_status = amba_readl(ep->ep_reg.sts_reg);
/* TxFIFO is empty, but we've not used this bit, so just ignored simply. */
if (ep_status == USB_EP_TXFIFO_EMPTY) {
amba_writel(ep->ep_reg.sts_reg, ep_status);
return;
}
dprintk(DEBUG_NORMAL, "%s: ep_status = 0x%08x\n", ep->ep.name, ep_status);
if (ambarella_handle_ep_stall(ep, ep_status))
return;
if (ambarella_check_bna_error(ep, ep_status)
|| ambarella_check_he_error(ep, ep_status)) {
struct ambarella_request *req = NULL;
ep->dma_going = 0;
ep->cancel_transfer = 0;
ep->need_cnak = 0;
if (!list_empty(&ep->queue)) {
req = list_first_entry(&ep->queue, struct ambarella_request,queue);
req->req.status = -EPROTO;
ambarella_udc_done(ep, req, 0);
}
return;
}
if (ep_status & USB_EP_TRN_DMA_CMPL) {
/* write dummy desc to try to avoid BNA error */
ep->udc->dummy_desc->status =
USB_DMA_BUF_HOST_RDY | USB_DMA_LAST;
amba_writel(ep->ep_reg.dat_desc_ptr_reg,
udc->dummy_desc_addr | udc->dma_fix);
if(ep->halted || ep->dma_going == 0 || ep->cancel_transfer == 1
|| list_empty(&ep->queue)) {
ep_status &= (USB_EP_IN_PKT | USB_EP_TRN_DMA_CMPL);
amba_writel(ep->ep_reg.sts_reg, ep_status);
ep->dma_going = 0;
ep->cancel_transfer = 0;
return;
}
ep->dma_going = 0;
ep->cancel_transfer = 0;
ep->need_cnak = 0;
ep->last_data_desc = ambarella_get_last_desc(ep);
if(ep->last_data_desc == NULL){
printk(KERN_ERR "%s: last_data_desc is NULL\n", ep->ep.name);
BUG();
return;
}
ambarella_handle_data_in(&udc->ep[ep_id]);
} else if(ep_status & USB_EP_IN_PKT) {
#if 0
if (IS_ISO_IN_EP(ep))
goto finish;
#endif
if(!ep->halted && !ep->cancel_transfer && !list_empty(&ep->queue)){
req = list_first_entry(&ep->queue,
struct ambarella_request, queue);
ambarella_set_tx_dma(ep, req, 0);
} else if (ep->dma_going == 0 || ep->halted || ep->cancel_transfer) {
ambarella_set_ep_nak(ep);
}
ep->cancel_transfer = 0;
} else if (ep_status != 0){
pr_err("%s: %s, Unknown int source(0x%08x)\n", __func__,
ep->ep.name, ep_status);
amba_writel(ep->ep_reg.sts_reg, ep_status);
return;
}
//finish:
if (ep_status != 0) {
ep_status &= (USB_EP_IN_PKT | USB_EP_TRN_DMA_CMPL | USB_EP_TXFIFO_EMPTY);
// ep_status &= (USB_EP_IN_PKT | USB_EP_TRN_DMA_CMPL | USB_EP_RCV_CLR_STALL);
amba_writel(ep->ep_reg.sts_reg, ep_status);
}
}
/*
* Name: udc_epout_interrupt
* Description:
* Process OUT endpoint interrupt
*/
static void udc_epout_interrupt(struct ambarella_udc *udc, u32 ep_id)
{
struct ambarella_ep *ep = &udc->ep[ep_id];
u32 desc_status, ep_status, i;
/* check the status bits for what kind of packets in */
ep_status = amba_readl(ep->ep_reg.sts_reg);
if (ambarella_handle_ep_stall(ep, ep_status))
return;
if(ep_id == CTRL_OUT) {
/* Cope with setup-data packet */
if((ep_status & USB_EP_OUT_PKT_MSK) == USB_EP_SETUP_PKT){
amba_writel(ep->ep_reg.sts_reg, USB_EP_SETUP_PKT);
ep->ctrl_sts_phase = 0;
ep->dma_going = 0;
ambarella_handle_request_packet(udc);
}
}
/* Cope with normal data packet */
if((ep_status & USB_EP_OUT_PKT_MSK) == USB_EP_OUT_PKT) {
amba_writel(ep->ep_reg.sts_reg, USB_EP_OUT_PKT);
ep->dma_going = 0;
/* Just cope with the zero-length packet */
if(ep->ctrl_sts_phase == 1) {
ep->ctrl_sts_phase = 0;
ambarella_enable_rx_dma(ep);
ep->dma_going = 0;
return;
}
if(ep->halted || ep->cancel_transfer || list_empty(&ep->queue)) {
amba_writel(ep->ep_reg.sts_reg, ep_status);
return;
}
if (ambarella_check_bna_error(ep, ep_status))
return;
if(ambarella_check_he_error(ep, ep_status) && !list_empty(&ep->queue)) {
struct ambarella_request *req = NULL;
req = list_first_entry(&ep->queue,
struct ambarella_request, queue);
req->req.status = -EPROTO;
ambarella_udc_done(ep, req, 0);
return;
}
ep->last_data_desc = ambarella_get_last_desc(ep);
if(ep->last_data_desc == NULL){
pr_err("%s: %s, last_data_desc is NULL\n", __func__, ep->ep.name);
BUG();
return;
}
if(ep_id != CTRL_OUT){
desc_status = ep->last_data_desc->status;
/* received data */
if((desc_status & USB_DMA_BUF_STS) == USB_DMA_BUF_DMA_DONE) {
amba_setbitsl(USB_DEV_EP_INTR_MSK_REG, 1 << ep_id);
ambarella_set_ep_nak(ep);
}
}
ambarella_handle_data_out(ep);
/* clear NAK for TX dma */
if (amba_readl(USB_DEV_STS_REG) & USB_DEV_RXFIFO_EMPTY_STS) {
for (i = 0; i < EP_NUM_MAX; i++) {
struct ambarella_ep *_ep = &udc->ep[i];
if (_ep->need_cnak == 1)
ambarella_clr_ep_nak(_ep);
}
}
}
return;
}
static irqreturn_t ambarella_udc_irq(int irq, void *_dev)
{
struct ambarella_udc *udc = _dev;
u32 value, handled = 0, i, ep_irq;
spin_lock(&udc->lock);
/* If gadget driver is not connected, do not handle the interrupt */
if (!udc->driver) {
amba_writel(USB_DEV_INTR_REG, amba_readl(USB_DEV_INTR_REG));
amba_writel(USB_DEV_EP_INTR_REG, amba_readl(USB_DEV_EP_INTR_REG));
ambarella_udc_set_pullup(udc, 0);
}
/* 1. check if device interrupt */
value = amba_readl(USB_DEV_INTR_REG);
if(value) {
dprintk(DEBUG_NORMAL, "device int value = 0x%x\n", value);
handled = 1;
udc_device_interrupt(udc, value);
}
/* 2. check if endpoint interrupt */
value = amba_readl(USB_DEV_EP_INTR_REG);
if(value) {
handled = 1;
for(i = 0; i < EP_NUM_MAX; i++){
ep_irq = 1 << i;
if (!(value & ep_irq))
continue;
/* ack the endpoint interrupt */
amba_writel(USB_DEV_EP_INTR_REG, ep_irq);
/* irq for out ep ? */
if (i >= EP_IN_NUM)
udc_epout_interrupt(udc, i);
else
udc_epin_interrupt(udc, i);
value &= ~ep_irq;
if(value == 0)
break;
}
}
spin_unlock(&udc->lock);
return IRQ_RETVAL(handled);
}
static void ambarella_vbus_timer(unsigned long data)
{
struct ambarella_udc *udc = (struct ambarella_udc *)data;
enum usb_device_state state;
u32 raw_status, connected, suspended;
suspended = (amba_readl(USB_DEV_STS_REG) & USB_DEV_SUSP_STS);
raw_status = amba_readl(AHB_SCRATCHPAD_REG(0x04));
connected = !!(raw_status & (1 << 26));
if (suspended)
usb_gadget_set_state(&udc->gadget, USB_STATE_SUSPENDED);
if (udc->vbus_status != connected) {
state = connected ? USB_STATE_ATTACHED : USB_STATE_NOTATTACHED;
usb_gadget_set_state(&udc->gadget, state);
udc->vbus_status = connected;
schedule_work(&udc->uevent_work);
if (udc->driver != NULL)
ambarella_udc_vbus_session(&udc->gadget, udc->vbus_status);
}
mod_timer(&udc->vbus_timer, jiffies + VBUS_POLL_TIMEOUT);
}
static void ambarella_stop_activity(struct ambarella_udc *udc)
{
struct usb_gadget_driver *driver = udc->driver;
struct ambarella_ep *ep;
u32 i;
/* Disable Tx and Rx DMA */
amba_clrbitsl(USB_DEV_CTRL_REG, USB_DEV_RCV_DMA_EN | USB_DEV_TRN_DMA_EN);
if (udc->gadget.speed == USB_SPEED_UNKNOWN)
driver = NULL;
udc->gadget.speed = USB_SPEED_UNKNOWN;
for (i = 0; i < EP_NUM_MAX; i++) {
ep = &udc->ep[i];
if(ep->ep.desc == NULL && !IS_EP0(ep))
continue;
ambarella_set_ep_nak(ep);
ep->halted = 1;
ambarella_ep_nuke(ep, -ESHUTDOWN);
}
if (driver) {
spin_unlock(&udc->lock);
driver->disconnect(&udc->gadget);
spin_lock(&udc->lock);
}
ambarella_udc_reinit(udc);
}
static int ambarella_udc_ep_enable(struct usb_ep *_ep,
const struct usb_endpoint_descriptor *desc)
{
struct ambarella_udc *udc;
struct ambarella_ep *ep = to_ambarella_ep(_ep);
u32 max_packet, tmp, type, idx = 0;
unsigned long flags;
/* Sanity check */
if (!_ep || !desc || IS_EP0(ep)
|| desc->bDescriptorType != USB_DT_ENDPOINT) {
printk("%s ep %d is inval \n",__func__,ep->id);
return -EINVAL;
}
udc = ep->udc;
if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
return -ESHUTDOWN;
max_packet = usb_endpoint_maxp(desc) & 0x7ff;
spin_lock_irqsave(&udc->lock, flags);
ep->ep.maxpacket = max_packet;
ep->ep.desc = desc;
ep->halted = 0;
ep->data_desc = NULL;
ep->last_data_desc = NULL;
ep->ctrl_sts_phase = 0;
ep->dma_going = 0;
ep->cancel_transfer = 0;
ep->frame_offset = (1 << (desc->bInterval - 1));
if(ep->dir == USB_DIR_IN){
idx = ep->id;
} else {
idx = ep->id - CTRL_OUT_UDC_IDX;
}
/* setup CSR */
amba_clrbitsl(USB_UDC_REG(idx), 0x3fffffff);
tmp = (desc->bEndpointAddress & 0xf) << 0;
tmp |= (desc->bEndpointAddress >> 7) << 4;
tmp |= (desc->bmAttributes & 0x3) << 5;
tmp |= udc->cur_config << 7;
tmp |= udc->cur_intf << 11;
tmp |= udc->cur_alt << 15;
tmp |= max_packet << 19;
amba_setbitsl(USB_UDC_REG(idx), tmp);
type = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) << 4;
amba_writel(ep->ep_reg.ctrl_reg, type | USB_EP_SET_NAK);
if(ep->dir == USB_DIR_IN) {
/* NOTE: total IN fifo size must be less than 576 * 4B */
tmp = max_packet / 4;
#if 0
if (IS_ISO_IN_EP(ep))
tmp *= max_packet > 1024 ? 1 : max_packet > 512 ? 2 : 3;
else
#endif
tmp *= 2;
amba_writel(ep->ep_reg.buf_sz_reg, tmp);
}
amba_writel(ep->ep_reg.max_pkt_sz_reg, max_packet);
spin_unlock_irqrestore(&udc->lock, flags);
return 0;
}
static int ambarella_udc_ep_disable(struct usb_ep *_ep)
{
struct ambarella_ep *ep = to_ambarella_ep(_ep);
unsigned long flags;
if (!_ep || !ep->ep.desc) {
dprintk(DEBUG_NORMAL, "%s not enabled\n",
_ep ? ep->ep.name : NULL);
return -EINVAL;
}
spin_lock_irqsave(&ep->udc->lock, flags);
ep->ep.desc = NULL;
ep->halted = 1;
ambarella_ep_nuke(ep, -ESHUTDOWN);
ambarella_set_ep_nak(ep);
if(ep->dir == USB_DIR_IN){
/* clear DMA poll demand bit */
amba_clrbitsl(ep->ep_reg.ctrl_reg, USB_EP_POLL_DEMAND);
/* clear status register */
amba_setbitsl(ep->ep_reg.sts_reg, USB_EP_IN_PKT);
/* flush the fifo */
amba_setbitsl(ep->ep_reg.ctrl_reg, USB_EP_FLUSH);
}
/* disable irqs */
amba_setbitsl(USB_DEV_EP_INTR_MSK_REG, 1 << ep->id);
spin_unlock_irqrestore(&ep->udc->lock, flags);
return 0;
}
static struct usb_request *
ambarella_udc_alloc_request(struct usb_ep *_ep, gfp_t mem_flags)
{
struct ambarella_request *req;
if (!_ep)
return NULL;
req = kzalloc (sizeof(struct ambarella_request), mem_flags);
if (!req)
return NULL;
req->req.dma = DMA_ADDR_INVALID;
INIT_LIST_HEAD (&req->queue);
req->desc_count = 0;
req->buf_aux = NULL;
req->dma_aux = DMA_ADDR_INVALID;
req->use_aux_buf = 0;
return &req->req;
}
static void
ambarella_udc_free_request(struct usb_ep *_ep, struct usb_request *_req)
{
struct ambarella_ep *ep = to_ambarella_ep(_ep);
struct ambarella_request *req = to_ambarella_req(_req);
if (!ep || !_req || (!ep->ep.desc && !IS_EP0(ep)))
return;
if(req->desc_count > 0)
ambarella_free_descriptor(ep, req);
if (req->buf_aux) {
kfree(req->buf_aux);
req->buf_aux = NULL;
}
WARN_ON (!list_empty (&req->queue));
kfree(req);
}
static int ambarella_udc_queue(struct usb_ep *_ep, struct usb_request *_req,
gfp_t gfp_flags)
{
struct ambarella_request *req = NULL;
struct ambarella_ep *ep = NULL;
struct ambarella_udc *udc;
unsigned long flags;
int i;
if (unlikely (!_ep)) {
pr_err("%s: _ep is NULL\n", __func__);
return -EINVAL;
}
ep = to_ambarella_ep(_ep);
udc = ep->udc;
for(i = 0; i < EP_NUM_MAX; i++) {
struct ambarella_ep *endp;
endp = &udc->ep[i];
if (endp != NULL && endp->dma_going) //Check for any ongoing DMA
break;
//If no other onging DMA and the current req is for ISO, enable the DMA
if((i == EP_NUM_MAX - 1) && IS_ISO_IN_EP(ep)) {
amba_setbitsl(USB_DEV_CTRL_REG,
USB_DEV_RCV_DMA_EN | USB_DEV_TRN_DMA_EN);
}
}
if (unlikely (!ep->ep.desc && !IS_EP0(ep))) {
pr_err("%s: %s, invalid args\n", __func__, _ep->name);
return -EINVAL;
}
if( unlikely( !udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)){
dprintk(DEBUG_NORMAL, "%s: %01d %01d\n", _ep->name,
!udc->driver, udc->gadget.speed==USB_SPEED_UNKNOWN);
return -ESHUTDOWN;
}
if (unlikely(!_req )) {
pr_err("%s: %s, _req is NULL\n", __func__, _ep->name);
return -EINVAL;
}
req = to_ambarella_req(_req);
if (unlikely(!req->req.complete || !req->req.buf
|| !list_empty(&req->queue))) {
pr_err("%s: %s, %01d %01d %01d\n", __func__, _ep->name,
!_req->complete, !_req->buf, !list_empty(&req->queue));
return -EINVAL;
}
if(IS_EP0(ep) && (udc->auto_ack_0_pkt == 1)){
/* It's status stage in setup packet. And A2/A3 will
* automatically send the zero-length packet to Host */
udc->auto_ack_0_pkt = 0;
req->req.actual = 0;
req->req.complete(&ep->ep, &req->req);
return 0;
}
/* check whether USB is suspended */
if(amba_readl(USB_DEV_STS_REG) & USB_DEV_SUSP_STS){
pr_err("%s: UDC is suspended!\n", __func__);
return -ESHUTDOWN;
}
if (unlikely((unsigned long)req->req.buf & 0x7)) {
req->use_aux_buf = 1;
if (req->buf_aux == NULL) {
req->buf_aux = kmalloc(UDC_DMA_MAXPACKET, GFP_ATOMIC);
if (req->buf_aux == NULL)
return -ENOMEM;
}
if (ep->dir == USB_DIR_IN)
memcpy(req->buf_aux, req->req.buf, req->req.length);
} else {
req->use_aux_buf = 0;
}
ambarella_map_dma_buffer(ep, req);
/* disable IRQ handler's bottom-half */
spin_lock_irqsave(&udc->lock, flags);
_req->status = -EINPROGRESS;
_req->actual = 0;
ambarella_prepare_descriptor(ep, req, gfp_flags);
/* kickstart this i/o queue? */
//if (list_empty(&ep->queue) && !ep->halted) {
if (list_empty(&ep->queue)) {
/* when the data length in ctrl-out transfer is zero, we just
* need to implement the STATUS-IN stage. But we don't
* implement the case that the data length in ctrl-in transfer
* is zero. */
if(req->req.length == 0) {
if(ep->id == CTRL_OUT) {
ambarella_udc_done(ep, req, 0);
/* told UDC the configuration is done, and to ack HOST */
//amba_setbitsl(USB_DEV_CTRL_REG, USB_DEV_CSR_DONE);
//udelay(150);
/* For STATUS-IN stage */
ambarella_clr_ep_nak(&udc->ep[CTRL_IN]);
/* Re-enable Rx DMA to receive next setup packet */
ambarella_set_rx_dma(ep, NULL);
ep->dma_going = 0;
goto finish;
} else if (ep->id == CTRL_IN) {
//printk("the data length of ctrl-in is zero\n");
//BUG();
}
}
if (ep->dir == USB_DIR_IN) {
/* no need to wait for IN-token for ISO transfer */
#if 0
if (IS_ISO_IN_EP(ep)) {
amba_writel(ep->ep_reg.sts_reg, USB_EP_IN_PKT);
ambarella_set_tx_dma(ep, req);
}
#endif
/* enable dma completion interrupt for current TX data */
amba_clrbitsl(USB_DEV_EP_INTR_MSK_REG, 1 << ep->id);
ambarella_clr_ep_nak(ep);
} else {
ambarella_set_rx_dma(ep, req);
}
}
list_add_tail(&req->queue, &ep->queue);
finish:
/* enable IRQ handler's bottom-half */
spin_unlock_irqrestore(&udc->lock, flags);
return 0;
}
static int ambarella_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
struct ambarella_ep *ep = to_ambarella_ep(_ep);
struct ambarella_request *req;
unsigned long flags;
unsigned halted;
if (!ep->udc->driver)
return -ESHUTDOWN;
if (!_ep || !_req)
return -EINVAL;
spin_lock_irqsave(&ep->udc->lock, flags);
/* make sure the request is 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;
}
halted = ep->halted;
ep->halted = 1;
/* request in processing */
if((ep->data_desc == req->data_desc) && (ep->dma_going == 1)) {
if (ep->dir == USB_DIR_IN)
ep->cancel_transfer = 1;
else {
u32 tmp, desc_status;
/* stop potential receive DMA */
tmp = amba_readl(USB_DEV_CTRL_REG);
amba_clrbitsl(USB_DEV_CTRL_REG, USB_DEV_RCV_DMA_EN);
/* cancel transfer later in ISR if descriptor was touched. */
desc_status = req->data_desc->status;
if (desc_status != USB_DMA_BUF_HOST_RDY)
ep->cancel_transfer = 1;
amba_writel(USB_DEV_CTRL_REG, tmp);
}
}
ambarella_udc_done(ep, req, -ECONNRESET);
ep->halted = halted;
spin_unlock_irqrestore(&ep->udc->lock, flags);
return 0;
}
/*
* ambarella_udc_set_halt
*/
static int ambarella_udc_set_halt(struct usb_ep *_ep, int value)
{
struct ambarella_ep *ep = to_ambarella_ep(_ep);
unsigned long flags;
if (unlikely (!_ep || (!ep->ep.desc && !IS_EP0(ep)))) {
pr_err("%s: %s, -EINVAL 1\n", __func__,_ep->name);
return -EINVAL;
}
if (!ep->udc->driver || ep->udc->gadget.speed == USB_SPEED_UNKNOWN){
pr_err("%s: %s, -ESHUTDOWN\n", __func__, _ep->name);
return -ESHUTDOWN;
}
/* isochronous transfer never halts because there is no handshake
* to report a halt condition */
if (ep->ep.desc /* not ep0 */ && IS_ISO_IN_EP(ep)) {
pr_err("%s: %s, -EINVAL 2\n", __func__, _ep->name);
return -EINVAL;
}
spin_lock_irqsave(&ep->udc->lock, flags);
/* set/clear, then synch memory views with the device */
if (value) { /* stall endpoint */
if (ep->dir == USB_DIR_IN) {
amba_setbitsl(ep->ep_reg.ctrl_reg, USB_EP_STALL | USB_EP_FLUSH);
} else {
int retry_count = 10000;
/* Wait Rx-FIFO to be empty */
while(!(amba_readl(USB_DEV_STS_REG) & USB_DEV_RXFIFO_EMPTY_STS)){
if (retry_count-- < 0) {
printk(KERN_ERR"[USB] stall_endpoint:failed");
break;
}
}
amba_setbitsl(ep->ep_reg.ctrl_reg, USB_EP_STALL);
}
} else { /* clear stall endpoint */
amba_clrbitsl(ep->ep_reg.ctrl_reg, USB_EP_STALL);
}
ep->halted = !!value;
spin_unlock_irqrestore(&ep->udc->lock, flags);
return 0;
}
static const struct usb_ep_ops ambarella_ep_ops = {
.enable = ambarella_udc_ep_enable,
.disable = ambarella_udc_ep_disable,
.alloc_request = ambarella_udc_alloc_request,
.free_request = ambarella_udc_free_request,
.queue = ambarella_udc_queue,
.dequeue = ambarella_udc_dequeue,
.set_halt = ambarella_udc_set_halt,
/* fifo ops not implemented */
};
/*------------------------- usb_gadget_ops ----------------------------------*/
static int ambarella_udc_get_frame(struct usb_gadget *_gadget)
{
return (amba_readl(USB_DEV_STS_REG) >> 18) & 0x7ff;
}
static int ambarella_udc_wakeup(struct usb_gadget *_gadget)
{
struct ambarella_udc *udc = to_ambarella_udc(_gadget);
u32 tmp;
tmp = amba_readl(USB_DEV_CFG_REG);
/* Remote wakeup feature not enabled by host */
if ((!udc->remote_wakeup_en) || (!(tmp & USB_DEV_REMOTE_WAKEUP_EN)))
return -ENOTSUPP;
tmp = amba_readl(USB_DEV_STS_REG);
/* not suspended? */
if (!(tmp & USB_DEV_SUSP_STS))
return 0;
/* trigger force resume */
amba_setbitsl(USB_DEV_CTRL_REG, USB_DEV_REMOTE_WAKEUP);
return 0;
}
static int ambarella_udc_set_pullup(struct ambarella_udc *udc, int is_on)
{
if (is_on) {
ambarella_udc_enable(udc);
/* reconnect to host */
amba_clrbitsl(USB_DEV_CTRL_REG, USB_DEV_SOFT_DISCON);
} else {
if (udc->gadget.speed != USB_SPEED_UNKNOWN)
ambarella_stop_activity(udc);
/* disconnect to host */
amba_setbitsl(USB_DEV_CTRL_REG, USB_DEV_SOFT_DISCON);
ambarella_udc_disable(udc);
}
return 0;
}
static int ambarella_udc_vbus_session(struct usb_gadget *gadget, int is_active)
{
unsigned long flags;
struct ambarella_udc *udc = to_ambarella_udc(gadget);
spin_lock_irqsave(&udc->lock, flags);
if (udc->driver)
ambarella_udc_set_pullup(udc, is_active);
else
ambarella_udc_set_pullup(udc, 0);
spin_unlock_irqrestore(&udc->lock, flags);
return 0;
}
static int ambarella_udc_pullup(struct usb_gadget *gadget, int is_on)
{
unsigned long flags;
struct ambarella_udc *udc = to_ambarella_udc(gadget);
spin_lock_irqsave(&udc->lock, flags);
ambarella_udc_set_pullup(udc, is_on);
spin_unlock_irqrestore(&udc->lock, flags);
return 0;
}
static int ambarella_udc_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
struct ambarella_udc *udc = to_ambarella_udc(gadget);
unsigned long flags;
spin_lock_irqsave(&udc->lock, flags);
/* Hook the driver */
driver->driver.bus = NULL;
udc->driver = driver;
/* Enable udc */
ambarella_udc_enable(udc);
spin_unlock_irqrestore(&udc->lock, flags);
return 0;
}
static int ambarella_udc_stop(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
struct ambarella_udc *udc = to_ambarella_udc(gadget);
unsigned long flags;
spin_lock_irqsave(&udc->lock, flags);
ambarella_stop_activity(udc);
ambarella_udc_disable(udc);
udc->driver = NULL;
spin_unlock_irqrestore(&udc->lock, flags);
return 0;
}
static const struct usb_gadget_ops ambarella_ops = {
.get_frame = ambarella_udc_get_frame,
.wakeup = ambarella_udc_wakeup,
.pullup = ambarella_udc_pullup,
.vbus_session = ambarella_udc_vbus_session,
/*.set_selfpowered: Always selfpowered */
.udc_start = ambarella_udc_start,
.udc_stop = ambarella_udc_stop,
};
/*------------------------- gadget driver handling---------------------------*/
/* Tears down device */
static void ambarella_gadget_release(struct device *dev)
{
struct ambarella_udc *udc = dev_get_drvdata(dev);
kfree(udc);
}
static void ambarella_init_gadget(struct ambarella_udc *udc,
struct platform_device *pdev)
{
struct ambarella_ep *ep;
u32 i;
spin_lock_init (&udc->lock);
udc->gadget.ops = &ambarella_ops;
udc->gadget.name = gadget_name;
udc->gadget.max_speed = USB_SPEED_HIGH;
udc->gadget.ep0 = &udc->ep[CTRL_IN].ep;
pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
/* set basic ep parameters */
for (i = 0; i < EP_NUM_MAX; i++) {
ep = &udc->ep[i];
ep->ep.name = amb_ep_string[i];
ep->id = i;
ep->ep.ops = &ambarella_ep_ops;
ep->ep.maxpacket = (unsigned short) ~0;
if (i < EP_IN_NUM) {
ep->dir = USB_DIR_IN;
} else {
ep->dir = USB_DIR_OUT;
}
}
udc->ep[CTRL_IN].ep.maxpacket = USB_EP_CTRL_MAX_PKT_SZ;
udc->ep[CTRL_OUT].ep.maxpacket = USB_EP_CTRL_MAX_PKT_SZ;
return;
}
static void ambarella_udc_enable(struct ambarella_udc *udc)
{
if (udc->udc_is_enabled)
return;
udc->udc_is_enabled = 1;
/* Disable Tx and Rx DMA */
amba_clrbitsl(USB_DEV_CTRL_REG,
USB_DEV_RCV_DMA_EN | USB_DEV_TRN_DMA_EN);
/* flush all of dma fifo */
ambarella_udc_fifo_flush(udc);
/* initialize ep0 register */
init_ep0(udc);
/* enable ep0 interrupt. */
amba_clrbitsl(USB_DEV_EP_INTR_MSK_REG,
USB_DEV_MSK_EP0_IN | USB_DEV_MSK_EP0_OUT);
/* enable Tx and Rx DMA */
amba_setbitsl(USB_DEV_CTRL_REG,
USB_DEV_RCV_DMA_EN | USB_DEV_TRN_DMA_EN);
/* enable device interrupt:
* Set_Configure, Set_Interface, Speed Enumerate Complete, Reset */
amba_clrbitsl(USB_DEV_INTR_MSK_REG,
USB_DEV_MSK_SET_CFG |
USB_DEV_MSK_SET_INTF |
USB_DEV_MSK_SPD_ENUM_CMPL |
USB_DEV_MSK_RESET);
}
static void ambarella_udc_disable(struct ambarella_udc *udc)
{
/* Disable all interrupts and Clear the interrupt registers */
ambarella_disable_all_intr();
/* Disable Tx and Rx DMA */
amba_clrbitsl(USB_DEV_CTRL_REG, USB_DEV_RCV_DMA_EN | USB_DEV_TRN_DMA_EN);
udc->gadget.speed = USB_SPEED_UNKNOWN;
udc->udc_is_enabled = 0;
}
/*
* ambarella_udc_reinit
*/
static void ambarella_udc_reinit(struct ambarella_udc *udc)
{
u32 i;
/* device/ep0 records init */
INIT_LIST_HEAD (&udc->gadget.ep_list);
INIT_LIST_HEAD (&udc->gadget.ep0->ep_list);
udc->auto_ack_0_pkt = 0;
udc->remote_wakeup_en = 0;
for (i = 0; i < EP_NUM_MAX; i++) {
struct ambarella_ep *ep = &udc->ep[i];
if (!IS_EP0(ep))
list_add_tail (&ep->ep.ep_list, &udc->gadget.ep_list);
ep->udc = udc;
ep->halted = 0;
ep->data_desc = NULL;
ep->last_data_desc = NULL;
INIT_LIST_HEAD (&ep->queue);
}
}
static int ambarella_udc_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct ambarella_udc *udc;
struct resource *res;
struct usb_phy *phy;
int retval;
dev_info(&pdev->dev, "%s: version %s\n", gadget_name, DRIVER_VERSION);
udc = devm_kzalloc(&pdev->dev, sizeof(struct ambarella_udc), GFP_KERNEL);
if (!udc) {
dev_err(&pdev->dev, "failed to allocate memory\n");
return -ENOMEM;
}
udc->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "no mem resource for base_reg!\n");
return -ENXIO;
}
udc->base_reg = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (udc->base_reg == NULL) {
dev_err(&pdev->dev, "devm_ioremap() failed\n");
return -ENOMEM;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (res == NULL) {
dev_err(&pdev->dev, "no mem resource for reset_reg!\n");
return -ENXIO;
}
udc->reset_reg = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (udc->reset_reg == NULL) {
dev_err(&pdev->dev, "devm_ioremap() failed\n");
return -ENOMEM;
}
udc->irq = platform_get_irq(pdev, 0);
if (udc->irq < 0) {
dev_err(&pdev->dev, "no irq!\n");
return -ENODEV;
}
if (of_find_property(np, "amb,dma-addr-fix", NULL))
udc->dma_fix = 0xc0000000;
else
udc->dma_fix = 0;
/* get the PHY device */
phy = devm_usb_get_phy_by_phandle(&pdev->dev, "amb,usbphy", 0);
if (IS_ERR(phy)) {
dev_err(&pdev->dev, "Can't get USB PHY %ld\n", PTR_ERR(phy));
return -ENXIO;
}
usb_phy_init(phy);
udc->phy = phy;
ambarella_udc_reset(udc->reset_reg, np);
udc->udc_is_enabled = 0;
udc->vbus_status = 0;
ambarella_init_gadget(udc, pdev);
ambarella_udc_reinit(udc);
ambarella_regaddr_map(udc);
/*initial usb hardware, and set soft disconnect */
ambarella_init_usb(udc);
/* DMA pool create */
udc->desc_dma_pool = dma_pool_create("desc_dma_pool", NULL,
sizeof(struct ambarella_data_desc), 16, 0);
if (!udc->desc_dma_pool) {
pr_err("%s: can't get descriptor dma pool\n", __func__);
return -ENOMEM;
}
udc->setup_buf = dma_pool_alloc(udc->desc_dma_pool, GFP_KERNEL,
&udc->setup_addr);
if(udc->setup_buf == NULL) {
printk(KERN_ERR "No memory to DMA\n");
retval = -ENOMEM;
goto err_out1;
}
retval = init_null_pkt_desc(udc);
if(retval){
goto err_out2;
}
/* irq setup after old hardware state is cleaned up */
retval = devm_request_irq(&pdev->dev, udc->irq, ambarella_udc_irq,
IRQF_TRIGGER_HIGH, dev_name(&pdev->dev), udc);
if (retval != 0) {
pr_err("%s: cannot get irq %d\n", __func__, udc->irq);
goto err_out3;
}
setup_timer(&udc->vbus_timer,
ambarella_vbus_timer, (unsigned long)udc);
mod_timer(&udc->vbus_timer, jiffies + VBUS_POLL_TIMEOUT);
udc->pre_state = USB_STATE_NOTATTACHED;
INIT_WORK(&udc->uevent_work, ambarella_uevent_work);
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
udc->proc_file = proc_create_data(proc_node_name, 0,
NULL, &ambarella_udc_fops, udc);
if (udc->proc_file == NULL) {
retval = -ENOMEM;
goto err_out3;
}
#endif
/* Register gadget driver */
retval = usb_add_gadget_udc_release(&pdev->dev, &udc->gadget,
ambarella_gadget_release);
if (retval) {
goto err_out4;
}
platform_set_drvdata(pdev, udc);
return 0;
err_out4:
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
remove_debugfs_files();
#endif
err_out3:
dma_pool_free(udc->desc_dma_pool, udc->dummy_desc, udc->dummy_desc_addr);
err_out2:
dma_pool_free(udc->desc_dma_pool, udc->setup_buf, udc->setup_addr);
err_out1:
dma_pool_destroy(udc->desc_dma_pool);
return retval;
}
/*
* Name: ambarella_udc_remove
* Description:
* Remove udc driver.
*/
static int ambarella_udc_remove(struct platform_device *pdev)
{
struct ambarella_udc *udc = platform_get_drvdata(pdev);
usb_del_gadget_udc(&udc->gadget);
if (udc->driver)
return -EBUSY;
del_timer_sync(&udc->vbus_timer);
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
remove_debugfs_files();
#endif
dma_pool_free(udc->desc_dma_pool, udc->dummy_desc, udc->dummy_desc_addr);
dma_pool_free(udc->desc_dma_pool, udc->setup_buf, udc->setup_addr);
dma_pool_destroy(udc->desc_dma_pool);
return 0;
}
#ifdef CONFIG_PM
static int ambarella_udc_suspend(struct platform_device *pdev, pm_message_t message)
{
unsigned long flags;
int retval = 0;
struct ambarella_udc *udc;
udc = platform_get_drvdata(pdev);
udc->sys_suspended = 1;
disable_irq(udc->irq);
del_timer_sync(&udc->vbus_timer);
spin_lock_irqsave(&udc->lock, flags);
ambarella_udc_set_pullup(udc, 0);
spin_unlock_irqrestore(&udc->lock, flags);
udc->udc_is_enabled = 0;
udc->vbus_status = 0;
dev_dbg(&pdev->dev, "%s exit with %d @ %d\n",
__func__, retval, message.event);
return retval;
}
static int ambarella_udc_resume(struct platform_device *pdev)
{
unsigned long flags;
int retval = 0;
struct ambarella_udc *udc;
udc = platform_get_drvdata(pdev);
udc->sys_suspended = 0;
/* Initial USB PLL */
usb_phy_init(udc->phy);
/* Reset USB */
ambarella_udc_reset(udc->reset_reg, pdev->dev.of_node);
/*initial usb hardware */
ambarella_init_usb(udc);
enable_irq(udc->irq);
spin_lock_irqsave(&udc->lock, flags);
ambarella_udc_set_pullup(udc, 1);
spin_unlock_irqrestore(&udc->lock, flags);
setup_timer(&udc->vbus_timer,
ambarella_vbus_timer, (unsigned long)udc);
mod_timer(&udc->vbus_timer, jiffies + VBUS_POLL_TIMEOUT);
dev_dbg(&pdev->dev, "%s exit with %d\n", __func__, retval);
return retval;
}
#endif
static const struct of_device_id ambarella_udc_dt_ids[] = {
{ .compatible = "ambarella,udc" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ambarella_udc_dt_ids);
static struct platform_driver ambarella_udc_driver = {
.driver = {
.name = "ambarella-udc",
.owner = THIS_MODULE,
.of_match_table = ambarella_udc_dt_ids,
},
.remove = ambarella_udc_remove,
#ifdef CONFIG_PM
.suspend = ambarella_udc_suspend,
.resume = ambarella_udc_resume,
#endif
};
module_platform_driver_probe(ambarella_udc_driver, ambarella_udc_probe);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:ambarella-usbgadget");