blob: ba1031efe416c1a7faf85d084773587ce3b15ba4 [file] [log] [blame]
/*******************************************************************************
Copyright (C) Marvell International Ltd. and its affiliates
This software file (the "File") is owned and distributed by Marvell
International Ltd. and/or its affiliates ("Marvell") under the following
alternative licensing terms. Once you have made an election to distribute the
File under one of the following license alternatives, please (i) delete this
introductory statement regarding license alternatives, (ii) delete the two
license alternatives that you have not elected to use and (iii) preserve the
Marvell copyright notice above.
********************************************************************************
Marvell Commercial License Option
If you received this File from Marvell and you have entered into a commercial
license agreement (a "Commercial License") with Marvell, the File is licensed
to you under the terms of the applicable Commercial License.
********************************************************************************
Marvell GPL License Option
If you received this File from Marvell, you may opt to use, redistribute and/or
modify this File in accordance with the terms and conditions of the General
Public License Version 2, June 1991 (the "GPL License"), a copy of which is
available along with the File in the license.txt file or by writing to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or
on the worldwide web at http://www.gnu.org/licenses/gpl.txt.
THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED
WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY
DISCLAIMED. The GPL License provides additional details about this warranty
disclaimer.
********************************************************************************
Marvell BSD License Option
If you received this File from Marvell, you may opt to use, redistribute and/or
modify this File under the following licensing terms.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Marvell nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/
#include "mvOs.h"
#include "config/mvSysHwConfig.h"
#include "boardEnv/mvBoardEnvLib.h"
#include "ctrlEnv/mvCtrlEnvLib.h"
#include "usb/api/mvUsbDevApi.h"
#include "usb/api/mvUsbCh9.h"
#include "usb/mvUsb.h"
#include "usb/mvUsbRegs.h"
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/wait.h>
#include <linux/usb/ch9.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
#include <linux/usb_gadget.h>
#else
#include <linux/usb/gadget.h>
#endif
#include <linux/proc_fs.h>
#include <linux/platform_device.h>
#include <asm/irq.h>
#include <linux/device.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#ifdef MV_USB_VOLTAGE_FIX
#define MV_USB_CONNECTED 1
#define MV_USB_DISCONNECTED 2
static int mv_usb_initialize_kobject (void);
#endif /* MV_USB_VOLTAGE_FIX */
#if defined(CONFIG_MV645xx) || defined(CONFIG_MV646xx)
# include "marvell_pic.h"
#endif /* CONFIG_MV645xx */
#ifdef CONFIG_SMP
#define MV_SPIN_LOCK_IRQSAVE(spinlock,flags) \
if(!in_interrupt()) \
spin_lock_irqsave(spinlock, flags)
#define MV_SPIN_UNLOCK_IRQRESTORE(spinlock,flags) \
if(!in_interrupt()) \
spin_unlock_irqrestore(spinlock, flags)
#else /* CONFIG_SMP */
#define MV_SPIN_LOCK_IRQSAVE(spinlock,flags) spin_lock_irqsave(spinlock, flags)
#define MV_SPIN_UNLOCK_IRQRESTORE(spinlock,flags) spin_unlock_irqrestore(spinlock, flags)
#endif /* CONFIG_SMP */
/*
* Enable/Disable Streaming mode for USB Core
*/
/*#if (defined(CONFIG_MV88F6082) || defined(CONFIG_MV645xx) || defined(USB_UNDERRUN_WA))*/
#if (MV_USB_VERSION >= 1) || defined(USB_UNDERRUN_WA)
static int streaming = 1;
#else
static int streaming = 0;
#endif
module_param_named(streaming, streaming, int, S_IRUGO);
MODULE_PARM_DESC(streaming, "0 - Streaming Disable, 1 - Streaming Enable");
static int epin_first = 1;
module_param_named(epin_first, epin_first, int, S_IRUGO);
MODULE_PARM_DESC(epin_first, "First choose of IN endpoint number");
static int epout_first = 1;
module_param_named(epout_first, epout_first, int, S_IRUGO);
MODULE_PARM_DESC(epout_first, "First choose of OUT endpoint number");
#if defined(USB_UNDERRUN_WA)
#include "mvIdma.h"
extern int mv_idma_usage_get(int* free_map);
extern unsigned char* mv_sram_usage_get(int* sram_size_ptr);
#define USB_IDMA_CTRL_LOW_VALUE ICCLR_DST_BURST_LIM_128BYTE \
| ICCLR_SRC_BURST_LIM_128BYTE \
| ICCLR_BLOCK_MODE \
| ICCLR_DESC_MODE_16M
/*
* 0..3 - use specifed IDMA engine. If the engine is busy - find free one.
* Other - don't use IDMA (use memcpy instead)
*/
static int idma = 1;
module_param_named(idma, idma, int, S_IRUGO);
MODULE_PARM_DESC(idma, "IDMA engine used for copy from DRAM to SRAM [0..3]");
/*
*/
static int wa_sram_parts = 2;
module_param_named(wa_sram_parts, wa_sram_parts, int, S_IRUGO);
MODULE_PARM_DESC(wa_sram_parts, "");
static int wa_sram_descr = 1;
module_param_named(wa_sram_descr, wa_sram_descr, int, S_IRUGO);
MODULE_PARM_DESC(wa_sram_descr, "");
/*
*/
static int wa_threshold = 64;
module_param_named(wa_threshold, wa_threshold, int, S_IRUGO);
MODULE_PARM_DESC(wa_threshold, "");
static char* sramBase = (char*)NULL;
static int sramSize = 0;
u32 mvUsbSramGet(u32* pSize)
{
char* pBuf;
/* Align address to 64 bytes */
pBuf = (char*)MV_ALIGN_UP((u32)sramBase, 64);
if(pSize != NULL)
{
*pSize = sramSize - (pBuf - sramBase);
}
/*
mvOsPrintf("mvUsbSramGet: Base=%p (%p), Size=%d (%d)\n",
sramBase, pBuf, sramSize, *pSize);
*/
return (u32)pBuf;
}
void mvUsbIdmaToSramCopy(void* sram_buf, void* src_buf, unsigned int size)
{
unsigned long phys_addr;
/*
mvOsPrintf("IdmaToSramCopy: idma=%d, sram=%p, src=%p (0x%x)\n",
usbWaIdma, sram_buf, src_buf, phys_addr);
*/
if( (idma >= 0) && (idma < MV_IDMA_MAX_CHAN) )
{
phys_addr = pci_map_single(NULL, src_buf, size, PCI_DMA_TODEVICE );
/* !!!! SRAM Uncached */
/*mvOsCacheInvalidate(NULL, sram_buf, size);*/
mvDmaTransfer(idma, (MV_U32)phys_addr,
(MV_U32)sram_buf, size, 0);
/* Wait until copy is finished */
while( mvDmaStateGet(idma) != MV_IDLE );
}
else
{
/* mvOsPrintf("usbWA: copy to SRAM %d bytes: \n", size); */
memcpy(sram_buf, src_buf, size);
/* !!!! SRAM Uncached */
/*mvOsCacheFlush(NULL, sram_buf, size);*/
}
}
USB_WA_FUNCS usbWaFuncs =
{
mvUsbSramGet,
mvUsbIdmaToSramCopy
};
int mv_usb_find_idma_engine(int idma_no)
{
int idma, free = 0;
int free_map[MV_IDMA_MAX_CHAN];
if( (idma_no < 0) || (idma_no >= MV_IDMA_MAX_CHAN) )
{
mvOsPrintf("Wrong IDMA number (%d): Valid range [0..%d]\n",
idma_no, (MV_IDMA_MAX_CHAN-1) );
return -1;
}
free = mv_idma_usage_get(free_map);
if(free == 0)
{
mvOsPrintf("No free IDMAs for USB Underrun WA: use memcpy\n");
return -1;
}
/* First of all check user idma_no */
if(free_map[idma_no] != 0)
return idma_no;
/* User idma_no is Busy. Look for free IDMA engine */
for(idma=0; idma<MV_IDMA_MAX_CHAN; idma++)
{
if(free_map[idma] != 0)
break;
}
mvOsPrintf("IDMA engine #%d is Busy. Use IDMA engine #%d instead\n", idma_no, idma);
return idma;
}
#endif /* USB_UNDERRUN_WA */
#undef DEBUG
#ifdef DEBUG
#define DBGMSG(fmt,args...) \
mvOsPrintf(fmt , ## args)
#else
# define DBGMSG(fmt,args...)
#endif /* DEBUG */
#define DRIVER_VERSION "05-July-2006"
#define DRIVER_DESC "Marvell Gadget USB Peripheral Controller"
struct mv_usb_dev;
struct mv_usb_ep
{
struct usb_ep ep;
struct mv_usb_dev* usb_dev;
struct list_head req_list;
unsigned num : 8,
is_enabled : 1,
is_in : 1;
};
struct mv_usb_dev
{
/* each pci device provides one gadget, several endpoints */
struct usb_gadget gadget;
spinlock_t lock;
struct usb_gadget_driver *driver;
struct mv_usb_ep ep[2*ARC_USB_MAX_ENDPOINTS];
unsigned enabled : 1,
protocol_stall : 1,
got_irq : 1;
u16 chiprev;
struct device *dev;
void* mv_usb_handle;
int dev_no;
MV_U8 vbus_gpp_no;
};
void mv_usb_show(struct mv_usb_dev* mv_dev, unsigned int mode);
extern void _usb_dci_vusb20_isr(pointer);
static void* mvUsbMalloc(unsigned int size)
{
return kmalloc(size,GFP_ATOMIC);
}
static void mvUsbFree(void* buf)
{
return kfree(buf);
}
static void* mvUsbIoUncachedMalloc( void* pDev, MV_U32 size, MV_U32 alignment,
MV_ULONG* pPhyAddr )
{
#if defined(USB_UNDERRUN_WA)
if(wa_sram_descr != 0)
{
char* pBuf;
pBuf = (char*)MV_ALIGN_UP((MV_U32)sramBase, alignment);
size += (pBuf - sramBase);
if( (sramSize < size) )
{
mvOsPrintf("SRAM malloc failed: Required %d bytes - Free %d bytes\n",
size, sramSize);
return NULL;
}
if(pPhyAddr != NULL)
*pPhyAddr = (MV_ULONG)sramBase;
/*
mvOsPrintf("usbUncachedMalloc: , pBuf=%p (%p), size=%d, align=%d\n",
sramBase, pBuf, size, alignment);
*/
pBuf = sramBase;
sramSize -= size;
sramBase += size;
return pBuf;
}
#endif
return pci_alloc_consistent( pDev, size+alignment, (dma_addr_t *)pPhyAddr );
}
static void mvUsbIoUncachedFree( void* pDev, MV_U32 size, MV_ULONG phyAddr, void* pVirtAddr )
{
#if defined(USB_UNDERRUN_WA)
if(wa_sram_descr != 0)
{
return;
}
#endif
return pci_free_consistent( pDev, size, pVirtAddr, (dma_addr_t)phyAddr );
}
static MV_ULONG mvUsbCacheInvalidate( void* pDev, void* p, int size )
{
#if defined(USB_UNDERRUN_WA)
if( ((char*)p >= sramBase) &&
((char*)p < (sramBase + sramSize)) )
return (unsigned long)p;
#endif
return pci_map_single( pDev, p, size, PCI_DMA_FROMDEVICE );
}
static MV_ULONG mvUsbCacheFlush( void* pDev, void* p, int size )
{
#if defined(USB_UNDERRUN_WA)
if( ((char*)p >= sramBase) &&
((char*)p < (sramBase + sramSize)) )
return (unsigned long)p;
#endif
return pci_map_single( pDev, p, size, PCI_DMA_TODEVICE );
}
static unsigned long mvUsbVirtToPhys(void* pDev, void* pVirtAddr)
{
#if defined(USB_UNDERRUN_WA)
if( ((char*)pVirtAddr >= sramBase) &&
((char*)pVirtAddr < (sramBase + sramSize)) )
return (unsigned long)pVirtAddr;
#endif
return virt_to_phys(pVirtAddr);
}
static void usbDevResetComplete(int devNo)
{
MV_U32 regVal;
regVal = MV_USB_CORE_MODE_DEVICE | MV_USB_CORE_SETUP_LOCK_DISABLE_MASK;
if(streaming == 0)
regVal |= MV_USB_CORE_STREAM_DISABLE_MASK;
/* Set USB_MODE register */
MV_REG_WRITE(MV_USB_CORE_MODE_REG(devNo), regVal);
}
extern MV_U32 mvUsbGetCapRegAddr(int devNo);
USB_IMPORT_FUNCS usbImportFuncs =
{
mvOsPrintf,
mvOsSPrintf,
mvUsbIoUncachedMalloc,
mvUsbIoUncachedFree,
mvUsbMalloc,
mvUsbFree,
memset,
memcpy,
mvUsbCacheFlush,
mvUsbCacheInvalidate,
mvUsbVirtToPhys,
NULL,
NULL,
mvUsbGetCapRegAddr,
usbDevResetComplete
};
static struct mv_usb_dev* the_controllers[MV_USB_MAX_PORTS] = {NULL};
static const char driver_name [] = "mv_udc";
static const char driver_desc [] = DRIVER_DESC;
static char ep_names [2*ARC_USB_MAX_ENDPOINTS][10] =
{
"ep0out", "ep0in",
};
static struct usb_ep_ops mv_usb_ep_ops;
static void mv_usb_ep_cancel_all_req(struct mv_usb_ep *mv_ep)
{
struct mv_usb_dev* mv_dev = mv_ep->usb_dev;
struct usb_request* usb_req;
int req_cntr, tr_cntr;
req_cntr = tr_cntr = 0;
/* Cancel all transfers */
while(_usb_device_get_transfer_status(mv_dev->mv_usb_handle, mv_ep->num,
mv_ep->is_in ? ARC_USB_SEND : ARC_USB_RECV) != ARC_USB_STATUS_IDLE)
{
tr_cntr++;
_usb_device_cancel_transfer(mv_dev->mv_usb_handle, mv_ep->num,
mv_ep->is_in ? ARC_USB_SEND : ARC_USB_RECV);
}
/*
if(tr_cntr > 0)
{
mvOsPrintf("Cancel ALL transfers: ep=%d-%s, %d transfers\n",
mv_ep->num, mv_ep->is_in ? "in" : "out", tr_cntr);
}
*/
while (!list_empty (&mv_ep->req_list))
{
usb_req = list_entry (mv_ep->req_list.next, struct usb_request, list);
/* Dequeue request and call complete function */
list_del_init (&usb_req->list);
if (usb_req->status == -EINPROGRESS)
usb_req->status = -ESHUTDOWN;
usb_req->complete (&mv_ep->ep, usb_req);
req_cntr++;
if(req_cntr >= MAX_XDS_FOR_TR_CALLS)
break;
}
/*
if(req_cntr > 0)
{
mvOsPrintf("Cancel ALL Requests: ep=%d-%s, %d requests\n",
mv_ep->num, mv_ep->is_in ? "in" : "out", req_cntr);
_usb_stats(mv_dev->mv_usb_handle);
}
*/
}
static uint_8 mv_usb_start_ep0(struct mv_usb_dev *mv_dev)
{
DBGMSG("%s: mv_dev=%p, mv_usb_handle=%p, mv_ep=%p, usb_ep=%p\n",
__FUNCTION__, mv_dev, mv_dev->mv_usb_handle, &mv_dev->ep[0], &mv_dev->ep[0].ep);
/* Init ep0 IN and OUT */
mv_dev->ep[0].is_enabled = 1;
_usb_device_init_endpoint(mv_dev->mv_usb_handle, 0, mv_dev->ep[0].ep.maxpacket,
ARC_USB_SEND, ARC_USB_CONTROL_ENDPOINT, 0);
_usb_device_init_endpoint(mv_dev->mv_usb_handle, 0, mv_dev->ep[0].ep.maxpacket,
ARC_USB_RECV, ARC_USB_CONTROL_ENDPOINT, 0);
return USB_OK;
}
static void mv_usb_ep_init(struct mv_usb_ep *ep, int num, int is_in)
{
sprintf(&ep_names[num*2+is_in][0], "ep%d%s", num, is_in ? "in" : "out");
ep->ep.name = &ep_names[num*2+is_in][0];
ep->num = num;
ep->is_in = is_in;
ep->is_enabled = 0;
INIT_LIST_HEAD (&ep->req_list);
ep->ep.maxpacket = ~0;
ep->ep.ops = &mv_usb_ep_ops;
}
static uint_8 mv_usb_reinit (struct mv_usb_dev *usb_dev)
{
int i, ep_num;
struct mv_usb_ep *ep;
DBGMSG("%s: mv_dev=%p, mv_usb_handle=%p\n",
__FUNCTION__, usb_dev, usb_dev->mv_usb_handle);
INIT_LIST_HEAD (&usb_dev->gadget.ep_list);
/* Enumerate IN endpoints */
ep_num = epin_first;
for(i=0; i<_usb_device_get_max_endpoint(usb_dev->mv_usb_handle); i++)
{
ep = &usb_dev->ep[ep_num*2+1];
if (ep_num != 0)
{
INIT_LIST_HEAD(&ep->ep.ep_list);
list_add_tail (&ep->ep.ep_list, &usb_dev->gadget.ep_list);
}
mv_usb_ep_init(ep, ep_num, 1);
ep->usb_dev = usb_dev;
ep_num++;
if(ep_num == _usb_device_get_max_endpoint(usb_dev->mv_usb_handle))
ep_num = 0;
}
/* Enumerate OUT endpoints */
ep_num = epout_first;
for(i=0; i<_usb_device_get_max_endpoint(usb_dev->mv_usb_handle); i++)
{
ep = &usb_dev->ep[ep_num*2];
if (ep_num != 0)
{
INIT_LIST_HEAD(&ep->ep.ep_list);
list_add_tail (&ep->ep.ep_list, &usb_dev->gadget.ep_list);
}
mv_usb_ep_init(ep, ep_num, 0);
ep->usb_dev = usb_dev;
ep_num++;
if(ep_num == _usb_device_get_max_endpoint(usb_dev->mv_usb_handle))
ep_num = 0;
}
usb_dev->ep[0].ep.maxpacket = 64;
usb_dev->gadget.ep0 = &usb_dev->ep[0].ep;
INIT_LIST_HEAD (&usb_dev->gadget.ep0->ep_list);
return USB_OK;
}
void mv_usb_bus_reset_service(void* handle,
uint_8 type,
boolean setup,
uint_8 direction,
uint_8_ptr buffer,
uint_32 length,
uint_8 error)
{
int i, dev_no = _usb_device_get_dev_num(handle);
struct mv_usb_dev *mv_dev = the_controllers[dev_no];
struct mv_usb_ep *mv_ep;
if(setup == 0)
{
/* mv_usb_show(mv_dev, 0x3ff); */
/* Stop Hardware and cancel all pending requests */
for (i=0; i<2*_usb_device_get_max_endpoint(handle); i++)
{
mv_ep = &mv_dev->ep[i];
if(mv_ep->is_enabled == 0)
continue;
mv_usb_ep_cancel_all_req(mv_ep);
}
/* If connected call Function disconnect callback */
if( (mv_dev->gadget.speed != USB_SPEED_UNKNOWN) &&
(mv_dev->driver != NULL) &&
(mv_dev->driver->disconnect != NULL) )
{
/*
USB_printf("USB gadget device disconnect or port reset: frindex=0x%x\n",
MV_REG_READ(MV_USB_CORE_FRAME_INDEX_REG(dev_no)) );
*/
mv_dev->driver->disconnect (&mv_dev->gadget);
}
mv_dev->gadget.speed = USB_SPEED_UNKNOWN;
/* Reinit all endpoints */
mv_usb_reinit(mv_dev);
}
else
{
_usb_device_start(mv_dev->mv_usb_handle);
/* Restart Control Endpoint #0 */
mv_usb_start_ep0(mv_dev);
}
}
void mv_usb_speed_service(void* handle,
uint_8 type,
boolean setup,
uint_8 direction,
uint_8_ptr buffer,
uint_32 length,
uint_8 error)
{
int dev_no = _usb_device_get_dev_num(handle);
struct mv_usb_dev *mv_dev = the_controllers[dev_no];
DBGMSG("Speed = %s\n", (length == ARC_USB_SPEED_HIGH) ? "High" : "Full");
if(length == ARC_USB_SPEED_HIGH)
mv_dev->gadget.speed = USB_SPEED_HIGH;
else
mv_dev->gadget.speed = USB_SPEED_FULL;
return;
}
void mv_usb_suspend_service(void* handle,
uint_8 type,
boolean setup,
uint_8 direction,
uint_8_ptr buffer,
uint_32 length,
uint_8 error)
{
int dev_no = _usb_device_get_dev_num(handle);
struct mv_usb_dev *mv_dev = the_controllers[dev_no];
DBGMSG("%s\n", __FUNCTION__);
if( (mv_dev->driver != NULL) &&
(mv_dev->driver->suspend != NULL) )
mv_dev->driver->suspend (&mv_dev->gadget);
}
void mv_usb_resume_service(void* handle,
uint_8 type,
boolean setup,
uint_8 direction,
uint_8_ptr buffer,
uint_32 length,
uint_8 error)
{
int dev_no = _usb_device_get_dev_num(handle);
struct mv_usb_dev *mv_dev = the_controllers[dev_no];
DBGMSG("%s\n", __FUNCTION__);
if( (mv_dev->driver != NULL) &&
(mv_dev->driver->resume != NULL) )
mv_dev->driver->resume (&mv_dev->gadget);
}
void mv_usb_tr_complete_service(void* handle,
uint_8 type,
boolean setup,
uint_8 direction,
uint_8_ptr buffer,
uint_32 length,
uint_8 error)
{
int dev_no = _usb_device_get_dev_num(handle);
struct mv_usb_dev *mv_dev = the_controllers[dev_no];
struct mv_usb_ep *mv_ep;
struct usb_request *usb_req;
int ep_num = (type*2) + direction;
DBGMSG("%s: ep_num=%d, setup=%s, direction=%s, pBuf=0x%x, length=%d, error=0x%x\n",
__FUNCTION__, type, setup ? "YES" : "NO",
(direction == ARC_USB_RECV) ? "RECV" : "SEND",
(unsigned)buffer, (int)length, error);
mv_ep = &mv_dev->ep[ep_num];
if( !list_empty(&mv_ep->req_list) )
{
usb_req = list_entry (mv_ep->req_list.next, struct usb_request, list);
if(usb_req->buf != buffer)
{
mvOsPrintf("ep=%d-%s: req=%p, Unexpected buffer pointer: %p, len=%d, expected=%p\n",
ep_num, (direction == ARC_USB_RECV) ? "out" : "in",
usb_req, buffer, length, usb_req->buf);
return;
}
/* Dequeue request and call complete function */
list_del_init (&usb_req->list);
usb_req->actual += length;
usb_req->status = error;
usb_req->complete (&mv_ep->ep, usb_req);
if(error != 0)
{
_usb_device_stall_endpoint(mv_ep->usb_dev->mv_usb_handle, mv_ep->num,
mv_ep->is_in ? ARC_USB_SEND : ARC_USB_RECV);
}
}
else
mvOsPrintf("ep=%p, epName=%s, epNum=%d - reqList EMPTY\n",
mv_ep, mv_ep->ep.name, mv_ep->num);
}
void mv_usb_ep0_complete_service(void* handle,
uint_8 type,
boolean setup,
uint_8 direction,
uint_8_ptr buffer,
uint_32 length,
uint_8 error)
{ /* Body */
int dev_no = _usb_device_get_dev_num(handle);
struct mv_usb_dev *mv_dev = the_controllers[dev_no];
struct mv_usb_ep *mv_ep;
struct usb_request* usb_req;
int rc;
boolean is_delegate = FALSE;
SETUP_STRUCT ctrl_req_org;
static SETUP_STRUCT mv_ctrl_req;
DBGMSG("%s: EP0(%d), setup=%s, direction=%s, pBuf=0x%x, length=%d, error=0x%x\n",
__FUNCTION__, type, setup ? "YES" : "NO",
(direction == ARC_USB_RECV) ? "RECV" : "SEND",
(unsigned)buffer, (int)length, error);
mv_ep = &mv_dev->ep[type];
if (setup)
{
_usb_device_read_setup_data(handle, type, (u8 *)&ctrl_req_org);
mv_ctrl_req.REQUESTTYPE = ctrl_req_org.REQUESTTYPE;
mv_ctrl_req.REQUEST = ctrl_req_org.REQUEST;
mv_ctrl_req.VALUE = le16_to_cpu (ctrl_req_org.VALUE);
mv_ctrl_req.INDEX = le16_to_cpu (ctrl_req_org.INDEX);
mv_ctrl_req.LENGTH = le16_to_cpu (ctrl_req_org.LENGTH);
while(_usb_device_get_transfer_status(handle, mv_ep->num,
ARC_USB_SEND) != ARC_USB_STATUS_IDLE)
{
_usb_device_cancel_transfer(mv_dev->mv_usb_handle, mv_ep->num,
ARC_USB_SEND);
}
while(_usb_device_get_transfer_status(handle, mv_ep->num,
ARC_USB_RECV) != ARC_USB_STATUS_IDLE)
{
_usb_device_cancel_transfer(mv_dev->mv_usb_handle, mv_ep->num,
ARC_USB_RECV);
}
/* make sure any leftover request state is cleared */
while (!list_empty (&mv_ep->req_list))
{
usb_req = list_entry (mv_ep->req_list.next, struct usb_request, list);
/* Dequeue request and call complete function */
list_del_init (&usb_req->list);
if (usb_req->status == -EINPROGRESS)
usb_req->status = -EPROTO;
usb_req->complete (&mv_ep->ep, usb_req);
}
}
/* Setup request direction */
mv_ep->is_in = (mv_ctrl_req.REQUESTTYPE & REQ_DIR_IN) != 0;
if(setup)
DBGMSG("Setup: dir=%s, reqType=0x%x, req=0x%x, value=0x%02x, index=0x%02x, length=0x%02x\n",
(direction == ARC_USB_SEND) ? "In" : "Out",
mv_ctrl_req.REQUESTTYPE, mv_ctrl_req.REQUEST, mv_ctrl_req.VALUE,
mv_ctrl_req.INDEX, mv_ctrl_req.LENGTH);
/* Handle most lowlevel requests;
* everything else goes uplevel to the gadget code.
*/
if( (mv_ctrl_req.REQUESTTYPE & REQ_TYPE_MASK) == REQ_TYPE_STANDARD)
{
switch (mv_ctrl_req.REQUEST)
{
case REQ_GET_STATUS:
mvUsbCh9GetStatus(handle, setup, &mv_ctrl_req);
break;
case REQ_CLEAR_FEATURE:
mvUsbCh9ClearFeature(handle, setup, &mv_ctrl_req);
break;
case REQ_SET_FEATURE:
mvUsbCh9SetFeature(handle, setup, &mv_ctrl_req);
break;
case REQ_SET_ADDRESS:
mvUsbCh9SetAddress(handle, setup, &mv_ctrl_req);
break;
default:
/* All others delegate call up-layer gadget code */
is_delegate = TRUE;
}
}
else
is_delegate = TRUE;
/* delegate call up-layer gadget code */
if(is_delegate)
{
if(setup)
{
rc = mv_dev->driver->setup (&mv_dev->gadget, (struct usb_ctrlrequest*)&ctrl_req_org);
if(rc < 0)
{
mvOsPrintf("Setup is failed: rc=%d, req=0x%02x, reqType=0x%x, value=0x%04x, index=0x%04x\n",
rc, ctrl_req_org.REQUEST, ctrl_req_org.REQUESTTYPE,
ctrl_req_org.VALUE, ctrl_req_org.INDEX);
_usb_device_stall_endpoint(handle, 0, ARC_USB_RECV);
return;
}
/* Acknowledge */
if( mv_ep->is_in ) {
_usb_device_recv_data(handle, 0, NULL, 0);
}
else if( mv_ctrl_req.LENGTH ) {
_usb_device_send_data(handle, 0, NULL, 0);
}
}
}
if(!setup)
{
if( !list_empty(&mv_ep->req_list) )
{
usb_req = list_entry (mv_ep->req_list.next, struct usb_request, list);
/* Dequeue request and call complete function */
list_del_init (&usb_req->list);
usb_req->actual = length;
usb_req->status = error;
usb_req->complete (&mv_ep->ep, usb_req);
}
DBGMSG("Setup complete: dir=%s, is_in=%d, length=%d\n",
(direction == ARC_USB_SEND) ? "In" : "Out",
mv_ep->is_in, length);
}
}
#ifdef MV_USB_VOLTAGE_FIX
/* usb_state - state of usb:connected(1)/disconnected(2) */
static int usb_state = MV_USB_DISCONNECTED;
/* usb_device-device structure for kobject functionality */
static struct device mv_usb_device;
/* mv_usb_work_struct - workqueue structure using for creating and run work tasks */
static struct work_struct mv_usb_work_struct;
/* mv_bustype for inizialization usb_device structure */
static struct bus_type mv_usb_bustype = {
.name = "mv_udc",
};
/*******************************************************************************
* mv_usb_work_struct_routine
* DESCRIPTION: notify userspace by ending an uevent
* INPUTS: *ignored - structure work_struct - N/A
* OUTPUTS: N/A
* RETURNS: N/A
*******************************************************************************/
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
static int mv_usb_work_struct_routine(struct work_struct *ignored)
#else
static void mv_usb_work_struct_routine(struct work_struct *ignored)
#endif
{
int retval=1;
/* Usb device connected */
if(usb_state == MV_USB_CONNECTED)
{
retval = kobject_uevent(&mv_usb_device.kobj, KOBJ_ADD);
mvOsPrintf("Usb device connected\n");
}
/* Usb device disconnected */
else
{
retval = kobject_uevent(&mv_usb_device.kobj, KOBJ_REMOVE);
mvOsPrintf("Usb device disconnected\n");
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
/* on error */
if(retval != 0)
{
mvOsPrintf("ERROR: %d, kobject_uevent() failed\n",retval);
}
return retval;
#endif
}
/* initialize a work-struct with work task -mv_usb_work_struct_routine */
static DECLARE_WORK(mv_usb_work_struct, mv_usb_work_struct_routine);
static irqreturn_t mv_usb_vbus_irq (int irq, void *_dev)
{
struct mv_usb_dev *mv_dev = _dev;
int vbus_change, retval=0;
vbus_change = mvUsbBackVoltageUpdate(mv_dev->dev_no, (int)mv_dev->vbus_gpp_no);
if(vbus_change == 2)
{
if( (mv_dev->gadget.speed != USB_SPEED_UNKNOWN) &&
(mv_dev->driver != NULL) &&
(mv_dev->driver->disconnect != NULL) )
mv_dev->driver->disconnect (&mv_dev->gadget);
}
/*
printk("%s: vbus_change = %d, usb_state=%d\n",
__FUNCTION__, vbus_change, usb_state);
*/
/*
set state of usb device (1-connected;2-disconnected) and
schedule mv_usb_work_struct_routine in workqueue
if mv_usb_work_struct entry is empty
*/
usb_state=(vbus_change==2) ? MV_USB_DISCONNECTED : MV_USB_CONNECTED;
if(list_empty(&mv_usb_work_struct.entry))
{
retval = schedule_work(&mv_usb_work_struct);
if(retval == 0)
{
mvOsPrintf("ERROR: %d, schedule_work() failed\n",retval);
}
}
return IRQ_HANDLED;
}
#endif /* MV_USB_VOLTAGE_FIX */
static irqreturn_t mv_usb_dev_irq (int irq, void *_dev)
{
struct mv_usb_dev *mv_dev = _dev;
spin_lock (&mv_dev->lock);
/* handle ARC USB Device interrupts */
_usb_dci_vusb20_isr(mv_dev->mv_usb_handle);
spin_unlock (&mv_dev->lock);
return IRQ_HANDLED;
}
#ifdef MV_USB_VOLTAGE_FIX
/*******************************************************************************
* mv_usb_initialize_kobject
* DESCRIPTION: init device,set bus type, create and add kobject
* INPUTS: *dev - pointer for device structure
* OUTPUTS: N/A
* RETURNS: status USB_OK on success
*******************************************************************************/
static int mv_usb_initialize_kobject (void)
{
int retval = 1;
/* init device */
device_initialize(&mv_usb_device);
/* set bus_type for device */
mv_usb_device.bus = &mv_usb_bustype;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
/* init kobject */
retval = kobject_set_name(&mv_usb_device.kobj, "mv_udc_d");
if(retval != USB_OK)
{
mvOsPrintf("ERROR: %d, kobject_set_name() failed\n",retval);
return retval;
}
retval = kobject_add(&mv_usb_device.kobj);
#else
retval = kobject_add(&mv_usb_device.kobj, NULL, "mv_udc_d");
#endif
/* add kobject */
if(retval != USB_OK)
{
mvOsPrintf("ERROR: %d, kobject_add() failed\n",retval);
return retval;
}
return USB_OK;
}
#endif /* MV_USB_VOLTAGE_FIX */
/* when a driver is successfully registered, it will receive
* control requests including set_configuration(), which enables
* non-control requests. then usb traffic follows until a
* disconnect is reported. then a host may connect again, or
* the driver might get unbound.
*/
int usb_gadget_register_driver (struct usb_gadget_driver *driver)
{
int retval, dev_no;
struct mv_usb_dev *mv_dev = NULL;
uint_8 error;
/* mvOsPrintf("ENTER usb_gadget_register_driver: \n");*/
/* Find USB Gadget device controller */
for(dev_no=0; dev_no<mvCtrlUsbMaxGet(); dev_no++)
{
mv_dev = the_controllers[dev_no];
if(mv_dev != NULL)
break;
}
if ( (driver == NULL)
|| (driver->speed != USB_SPEED_HIGH)
|| !driver->bind
|| !driver->unbind
|| !driver->setup)
{
mvOsPrintf("ERROR: speed=%d, bind=%p, unbind=%p, setup=%p\n",
driver->speed, driver->bind, driver->unbind, driver->setup);
return -EINVAL;
}
if (!mv_dev)
{
mvOsPrintf("ERROR: max_dev=%d, mv_dev=%p\n", mvCtrlUsbMaxGet(), mv_dev);
return -ENODEV;
}
if (mv_dev->driver)
{
mvOsPrintf("ERROR: driver=%p is busy\n", mv_dev->driver);
return -EBUSY;
}
/*
mvOsPrintf("usb_gadget_register_driver: dev=%d, mv_dev=%p, pDriver=%p\n",
dev_no, mv_dev, driver);
*/
/* first hook up the driver ... */
mv_dev->driver = driver;
mv_dev->gadget.dev.driver = &driver->driver;
retval = driver->bind (&mv_dev->gadget);
if (retval) {
mvOsPrintf("bind to driver %s --> %d\n",
driver->driver.name, retval);
mv_dev->driver = 0;
mv_dev->gadget.dev.driver = 0;
return retval;
}
/* request_irq */
if (request_irq (IRQ_USB_CTRL(dev_no), mv_usb_dev_irq, IRQF_DISABLED,
driver_name, mv_dev) != 0)
{
mvOsPrintf("%s register: request interrupt %d failed\n",
driver->driver.name, IRQ_USB_CTRL(dev_no));
return -EBUSY;
}
_usb_device_start(mv_dev->mv_usb_handle);
error = mv_usb_start_ep0(mv_dev);
mvOsPrintf("registered Marvell USB-%d gadget driver %s\n", dev_no, driver->driver.name);
return error;
}
EXPORT_SYMBOL (usb_gadget_register_driver);
int usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
{
int i, dev_no;
struct mv_usb_ep *mv_ep;
struct mv_usb_dev *mv_dev = NULL;
unsigned long flags = 0;
/* Find USB Gadget device controller */
for(dev_no=0; dev_no<mvCtrlUsbMaxGet(); dev_no++)
{
if( (the_controllers[dev_no] != NULL) && (the_controllers[dev_no]->driver == driver) )
{
mv_dev = the_controllers[dev_no];
break;
}
}
if (!mv_dev)
{
mvOsPrintf("usb_gadget_unregister_driver FAILED: no such device\n");
return -ENODEV;
}
if (!driver || (driver != mv_dev->driver) )
{
mvOsPrintf("usb_gadget_unregister_driver FAILED: no such driver, dev_no=%d\n", dev_no);
return -EINVAL;
}
/* Stop and Disable ARC USB device */
MV_SPIN_LOCK_IRQSAVE(&mv_dev->lock, flags);
/* Stop Endpoints */
for (i=0; i<2*_usb_device_get_max_endpoint(mv_dev->mv_usb_handle); i++)
{
mv_ep = &mv_dev->ep[i];
if(mv_ep->is_enabled == 0)
continue;
mv_ep->is_enabled = 0;
mv_usb_ep_cancel_all_req(mv_ep);
_usb_device_deinit_endpoint(mv_dev->mv_usb_handle, mv_ep->num,
mv_ep->is_in ? ARC_USB_SEND : ARC_USB_RECV);
}
_usb_device_stop(mv_dev->mv_usb_handle);
MV_SPIN_UNLOCK_IRQRESTORE(&mv_dev->lock, flags);
if (mv_dev->gadget.speed != USB_SPEED_UNKNOWN)
mv_dev->driver->disconnect (&mv_dev->gadget);
driver->unbind (&mv_dev->gadget);
/* free_irq */
free_irq (IRQ_USB_CTRL(dev_no), mv_dev);
mv_dev->gadget.dev.driver = 0;
mv_dev->driver = 0;
mv_dev->gadget.speed = USB_SPEED_UNKNOWN;
/* Reinit all endpoints */
mv_usb_reinit(mv_dev);
/*device_remove_file(dev->dev, &dev_attr_function); ?????*/
mvOsPrintf("unregistered Marvell USB %d gadget driver %s\n", dev_no, driver->driver.name);
return 0;
}
EXPORT_SYMBOL (usb_gadget_unregister_driver);
void mv_usb_show(struct mv_usb_dev* mv_dev, unsigned int mode)
{
int i;
mvOsPrintf("\n-------------------------------------------------------------\n");
if( MV_BIT_CHECK(mode, 0) )
_usb_regs(mv_dev->mv_usb_handle);
if( MV_BIT_CHECK(mode, 1) )
_usb_status(mv_dev->mv_usb_handle);
if( MV_BIT_CHECK(mode, 2) )
_usb_stats(mv_dev->mv_usb_handle);
if( MV_BIT_CHECK(mode, 3) )
_usb_debug_print_trace_log();
for(i=0; i<_usb_device_get_max_endpoint(mv_dev->mv_usb_handle); i++)
{
if( MV_BIT_CHECK(mode, (8+i)) )
{
_usb_ep_status(mv_dev->mv_usb_handle, i, ARC_USB_RECV);
_usb_ep_status(mv_dev->mv_usb_handle, i, ARC_USB_SEND);
}
}
mvOsPrintf("-------------------------------------------------------------\n\n");
}
EXPORT_SYMBOL (mv_usb_show);
static int mv_usb_ep_enable(struct usb_ep *_ep,
const struct usb_endpoint_descriptor *desc)
{
struct mv_usb_dev* usb_dev;
struct mv_usb_ep* usb_ep;
uint_16 maxSize;
uint_8 epType;
unsigned long flags = 0;
usb_ep = container_of (_ep, struct mv_usb_ep, ep);
if( (_ep == NULL) || (desc == NULL) )
return -EINVAL;
usb_dev = usb_ep->usb_dev;
if(usb_ep->is_enabled)
{
mvOsPrintf("mv_usb: %d%s Endpoint (%s) is already in use\n",
usb_ep->num, usb_ep->is_in ? "In" : "Out", usb_ep->ep.name);
return -EINVAL;
}
/*
mvOsPrintf("USB Enable %s: type=%d, maxPktSize=%d\n",
_ep->name, desc->bmAttributes & 0x3, desc->wMaxPacketSize);
*/
if(usb_ep->num == 0)
{
mvOsPrintf("mv_usb: ep0 is reserved\n");
return -EINVAL;
}
if(desc->bDescriptorType != USB_DT_ENDPOINT)
{
mvOsPrintf("mv_usb: wrong descriptor type %d\n", desc->bDescriptorType);
return -EINVAL;
}
MV_SPIN_LOCK_IRQSAVE(&usb_dev->lock, flags);
usb_dev = usb_ep->usb_dev;
if( (usb_dev->driver == NULL) ||
(usb_dev->gadget.speed == USB_SPEED_UNKNOWN) )
{
MV_SPIN_UNLOCK_IRQRESTORE(&usb_dev->lock, flags);
return -ESHUTDOWN;
}
/* Max packet size */
maxSize = le16_to_cpu (desc->wMaxPacketSize);
/* Endpoint type */
if( (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_CONTROL)
epType = ARC_USB_CONTROL_ENDPOINT;
else if( (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_ISOC)
epType = ARC_USB_ISOCHRONOUS_ENDPOINT;
else if( (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)
epType = ARC_USB_BULK_ENDPOINT;
else
epType = ARC_USB_INTERRUPT_ENDPOINT;
_ep->maxpacket = maxSize & 0x7ff;
usb_ep->is_enabled = 1;
_usb_device_init_endpoint(usb_dev->mv_usb_handle, usb_ep->num, maxSize,
usb_ep->is_in ? ARC_USB_SEND : ARC_USB_RECV, epType,
(epType == ARC_USB_BULK_ENDPOINT) ? ARC_USB_DEVICE_DONT_ZERO_TERMINATE : 0);
MV_SPIN_UNLOCK_IRQRESTORE(&usb_dev->lock, flags);
return 0;
}
static int mv_usb_ep_disable (struct usb_ep *_ep)
{
struct mv_usb_dev* mv_dev;
struct mv_usb_ep* mv_ep;
unsigned long flags = 0;
uint_8 direction;
mv_ep = container_of (_ep, struct mv_usb_ep, ep);
if( (_ep == NULL) || (mv_ep->is_enabled == 0) || (mv_ep->num == 0))
return -EINVAL;
mv_dev = mv_ep->usb_dev;
/*
mvOsPrintf("mv_usb_ep_disable: mv_dev=%p, ep=0x%x (%d-%s), name=%s\n",
mv_dev, (unsigned)_ep, mv_ep->num, mv_ep->is_in ? "in" : "out",
_ep->name);
*/
MV_SPIN_LOCK_IRQSAVE(&mv_dev->lock, flags);
direction = mv_ep->is_in ? ARC_USB_SEND : ARC_USB_RECV;
mv_ep->is_enabled = 0;
/* Cancell all requests */
mv_usb_ep_cancel_all_req(mv_ep);
/* Disable endpoint */
_usb_device_deinit_endpoint(mv_dev->mv_usb_handle, mv_ep->num, direction);
MV_SPIN_UNLOCK_IRQRESTORE(&mv_dev->lock, flags);
return 0;
}
static struct usb_request* mv_usb_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
{
struct usb_request* req;
if (!_ep)
return NULL;
req = kmalloc (sizeof *req, gfp_flags);
if (!req)
return NULL;
memset (req, 0, sizeof *req);
INIT_LIST_HEAD (&req->list);
return req;
}
static void mv_usb_ep_free_request(struct usb_ep *_ep, struct usb_request *_req)
{
if (!_ep || !_req)
{
mvOsPrintf("ep_free_request Error: _ep=%p, _req=%p\n", _ep, _req);
return;
}
kfree (_req);
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
static void *mv_usb_ep_alloc_buffer(struct usb_ep *_ep, unsigned bytes,
dma_addr_t *dma, gfp_t gfp_flags)
{
struct mv_usb_ep *ep;
if (!_ep)
return NULL;
ep = container_of(_ep, struct mv_usb_ep, ep);
return dma_alloc_coherent(ep->usb_dev->gadget.dev.parent,
bytes, dma, gfp_flags);
}
/*------------------------------------------------------------------
* frees an i/o buffer
*---------------------------------------------------------------------*/
static void mv_usb_ep_free_buffer(struct usb_ep *_ep, void *buf,
dma_addr_t dma, unsigned bytes)
{
struct mv_usb_ep *ep;
if (!_ep)
return;
ep = container_of(_ep, struct mv_usb_ep, ep);
dma_free_coherent(ep->usb_dev->gadget.dev.parent, bytes, buf, dma);
}
#endif
static int mv_usb_ep_queue (struct usb_ep *_ep, struct usb_request *_req,
gfp_t gfp_flags)
{
struct mv_usb_dev* usb_dev;
struct mv_usb_ep* usb_ep;
unsigned long flags = 0;
uint_8 error;
usb_ep = container_of (_ep, struct mv_usb_ep, ep);
/* check parameters */
if( (_ep == NULL) || (_req == NULL) )
{
mvOsPrintf("ep_queue Failed: _ep=%p, _req=%p\n", _ep, _req);
return -EINVAL;
}
usb_dev = usb_ep->usb_dev;
if ( (usb_dev->driver == NULL) || (usb_dev->gadget.speed == USB_SPEED_UNKNOWN) )
return -ESHUTDOWN;
if(usb_ep->is_enabled == 0)
{
mvOsPrintf("ep_queue Failed - %s is disabled: usb_ep=%p\n", _ep->name, usb_ep);
return -EINVAL;
}
DBGMSG("%s: num=%d-%s, name=%s, _req=%p, buf=%p, length=%d\n",
__FUNCTION__, usb_ep->num, usb_ep->is_in ? "in" : "out",
_ep->name, _req, _req->buf, _req->length);
MV_SPIN_LOCK_IRQSAVE(&usb_dev->lock, flags);
_req->status = -EINPROGRESS;
_req->actual = 0;
/* Add request to list */
if( ((usb_ep->num == 0) && (_req->length == 0)) || (usb_ep->is_in) )
{
int send_size, size;
uint_8 *send_ptr, *buf_ptr;
send_ptr = buf_ptr = _req->buf;
send_size = size = _req->length;
list_add_tail(&_req->list, &usb_ep->req_list);
error = _usb_device_send_data(usb_dev->mv_usb_handle, usb_ep->num, send_ptr, send_size);
if(error != USB_OK)
{
mvOsPrintf("ep_queue: Can't SEND data (err=%d): ep_num=%d, pBuf=0x%x, send_size=%d\n",
error, usb_ep->num, (unsigned)_req->buf, _req->length);
list_del_init (&_req->list);
}
size -= send_size;
buf_ptr += send_size;
}
else
{
error = _usb_device_recv_data(usb_dev->mv_usb_handle, usb_ep->num, _req->buf, _req->length);
if(error != USB_OK)
{
mvOsPrintf("mv_usb_ep_queue: Can't RCV data (err=%d): ep_num=%d, pBuf=0x%x, size=%d\n",
error, usb_ep->num, (unsigned)_req->buf, _req->length);
}
else
list_add_tail(&_req->list, &usb_ep->req_list);
}
MV_SPIN_UNLOCK_IRQRESTORE(&usb_dev->lock, flags);
return (int)error;
}
/* Cancell request */
static int mv_usb_ep_dequeue (struct usb_ep *_ep, struct usb_request *_req)
{
struct mv_usb_dev* usb_dev;
struct usb_request *usb_req;
struct mv_usb_ep* usb_ep;
unsigned long flags = 0;
int status = 0;
usb_ep = container_of (_ep, struct mv_usb_ep, ep);
/* check parameters */
if( (_ep == NULL) || (_req == NULL) || (usb_ep->is_enabled == 0) )
return -EINVAL;
usb_dev = usb_ep->usb_dev;
if ( (usb_dev->driver == NULL) || (usb_dev->gadget.speed == USB_SPEED_UNKNOWN) )
{
mvOsPrintf("mv_usb_ep_dequeue: ep=0x%x, num=%d-%s, name=%s, driver=0x%x, speed=%d\n",
(unsigned)_ep, usb_ep->num, usb_ep->is_in ? "in" : "out",
_ep->name, (unsigned)usb_dev->driver, usb_dev->gadget.speed);
return -ESHUTDOWN;
}
MV_SPIN_LOCK_IRQSAVE(&usb_dev->lock, flags);
/* ????? Currently supported only dequeue request from the HEAD of List */
if( !list_empty(&usb_ep->req_list) )
{
usb_req = list_entry (usb_ep->req_list.next, struct usb_request, list);
if(usb_req == _req)
{
/* Cancel transfer */
_usb_device_cancel_transfer(usb_dev->mv_usb_handle, usb_ep->num,
usb_ep->is_in ? ARC_USB_SEND : ARC_USB_RECV);
/* Dequeue request and call complete function */
list_del_init (&_req->list);
if (_req->status == -EINPROGRESS)
_req->status = -ECONNRESET;
/* ????? what about enable interrupts */
_req->complete (&usb_ep->ep, _req);
}
else
{
mvOsPrintf("Cancel request failed: ep=%p, usb_req=%p, req=%p\n",
_ep, usb_req, _req);
status = EINVAL;
}
}
/*
else
mvOsPrintf("%s: ep=%p, epName=%s, epNum=%d - reqList EMPTY\n",
__FUNCTION__, usb_ep, usb_ep->ep.name, usb_ep->num);
*/
MV_SPIN_UNLOCK_IRQRESTORE(&usb_dev->lock, flags);
return status;
}
static int mv_usb_ep_set_halt (struct usb_ep *_ep, int value)
{
struct mv_usb_ep* mv_ep;
unsigned long flags = 0;
int retval = 0;
mv_ep = container_of (_ep, struct mv_usb_ep, ep);
if (_ep == NULL)
return -EINVAL;
if( (mv_ep->usb_dev->driver == NULL) ||
(mv_ep->usb_dev->gadget.speed == USB_SPEED_UNKNOWN) )
return -ESHUTDOWN;
/*
mvOsPrintf("%s - %s \n",
_ep->name, value ? "Stalled" : "Unstalled");
*/
MV_SPIN_LOCK_IRQSAVE(&mv_ep->usb_dev->lock, flags);
if (!list_empty (&mv_ep->req_list))
retval = -EAGAIN;
else
{
/* set/clear, then synch memory views with the device */
if (value)
{
if (mv_ep->num == 0)
mv_ep->usb_dev->protocol_stall = 1;
else
_usb_device_stall_endpoint(mv_ep->usb_dev->mv_usb_handle, mv_ep->num,
mv_ep->is_in ? ARC_USB_SEND : ARC_USB_RECV);
}
else
{
_usb_device_unstall_endpoint(mv_ep->usb_dev->mv_usb_handle, mv_ep->num,
mv_ep->is_in ? ARC_USB_SEND : ARC_USB_RECV);
}
}
MV_SPIN_UNLOCK_IRQRESTORE(&mv_ep->usb_dev->lock, flags);
return retval;
}
static void mv_usb_ep_fifo_flush (struct usb_ep *_ep)
{
DBGMSG("%s: ep=%p, ep_name=%s - NOT supported\n", __FUNCTION__, _ep, _ep->name);
}
static struct usb_ep_ops mv_usb_ep_ops =
{
.enable = mv_usb_ep_enable,
.disable = mv_usb_ep_disable,
.alloc_request = mv_usb_ep_alloc_request,
.free_request = mv_usb_ep_free_request,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
.alloc_buffer = mv_usb_ep_alloc_buffer,
.free_buffer = mv_usb_ep_free_buffer,
#endif
.queue = mv_usb_ep_queue,
.dequeue = mv_usb_ep_dequeue,
.set_halt = mv_usb_ep_set_halt,
.fifo_flush = mv_usb_ep_fifo_flush,
/*.fifo_status = Not supported */
};
static int mv_usb_get_frame (struct usb_gadget *_gadget)
{
DBGMSG("Call mv_usb_get_frame - NOT supported\n");
return 0;
}
static int mv_usb_wakeup(struct usb_gadget *_gadget)
{
DBGMSG("Call mv_usb_wakeup - NOT supported\n");
return 0;
}
static int mv_usb_set_selfpowered (struct usb_gadget *_gadget, int value)
{
DBGMSG("Call mv_usb_set_selfpowered - NOT supported\n");
return 0;
}
static const struct usb_gadget_ops mv_usb_ops =
{
.get_frame = mv_usb_get_frame,
.wakeup = mv_usb_wakeup,
.set_selfpowered = mv_usb_set_selfpowered,
.ioctl = NULL,
};
static void mv_usb_gadget_release (struct device *_dev)
{
struct mv_usb_dev *usb_dev = dev_get_drvdata (_dev);
/*mvOsPrintf("Call mv_usb_gadget_release \n");*/
mvOsFree(usb_dev);
}
static int __init mv_usb_gadget_probe(struct platform_device *pDev)
{
struct device *_dev = &pDev->dev;
struct mv_usb_dev *mv_dev;
int dev_no, retval, i;
uint_8 error;
/*mvOsPrintf("Call mv_usb_gadget_probe: _dev=%p, pDev=%p\n", _dev, pDev);*/
for(dev_no=0; dev_no<mvCtrlUsbMaxGet(); dev_no++)
{
if( (pDev->resource[1].flags == IORESOURCE_IRQ) &&
(pDev->resource[1].start == IRQ_USB_CTRL(dev_no)) )
{
break;
}
}
if(dev_no >= mvCtrlUsbMaxGet())
{
mvOsPrintf("mv_udc_probe: device is not found\n");
return -EINVAL;
}
mvOsPrintf("USB-%d Gadget driver probed\n", dev_no);
if (the_controllers[dev_no])
{
mvOsPrintf("mv_dev_load: USB-%d controller is BUSY\n", dev_no);
return -EBUSY;
}
/* alloc, and start init */
mv_dev = mvOsMalloc (sizeof(struct mv_usb_dev));
if (mv_dev == NULL)
{
mvOsPrintf("mv_dev_load: malloc failed\n");
return -ENOMEM;
}
memset (mv_dev, 0, sizeof *mv_dev);
spin_lock_init (&mv_dev->lock);
mv_dev->dev = _dev;
mv_dev->gadget.ops = &mv_usb_ops;
mv_dev->gadget.is_dualspeed = 1;
/* the "gadget" abstracts/virtualizes the controller */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
strcpy (mv_dev->gadget.dev.bus_id, "gadget");
#else
dev_set_name(&mv_dev->gadget.dev, "gadget");
#endif
mv_dev->gadget.dev.parent = _dev;
mv_dev->gadget.dev.dma_mask = _dev->dma_mask; /* ?????? */
mv_dev->gadget.dev.release = mv_usb_gadget_release;
mv_dev->gadget.name = driver_name;
mv_dev->mv_usb_handle = NULL;
mv_dev->dev_no = dev_no;
dev_set_drvdata(_dev, mv_dev);
the_controllers[dev_no] = mv_dev;
#ifdef MV_USB_VOLTAGE_FIX
mv_dev->vbus_gpp_no = mvUsbGppInit(dev_no);
mvOsPrintf("USB gadget device #%d: vbus_gpp_no = %d\n",
dev_no, mv_dev->vbus_gpp_no);
if(mv_dev->vbus_gpp_no != (MV_U8)N_A)
{
if (request_irq (IRQ_GPP_START + mv_dev->vbus_gpp_no, mv_usb_vbus_irq, IRQF_DISABLED,
driver_name, mv_dev) != 0)
{
mvOsPrintf("%s probe: request interrupt %d failed\n",
driver_name, IRQ_GPP_START + mv_dev->vbus_gpp_no);
return -EBUSY;
}
}
#endif /* MV_USB_VOLTAGE_FIX */
/* Reset ARC USB device ????? */
/* Reinit ARC USB device ????? */
/* First of all. */
_usb_device_set_bsp_funcs(&usbImportFuncs);
#if defined(USB_UNDERRUN_WA)
if(wa_sram_parts > USB_SRAM_MAX_PARTS)
{
mvOsPrintf("Wrong <wa_sram_parts> param (%d): Valid range [1 .. %d]\n",
wa_sram_parts, USB_SRAM_MAX_PARTS);
return -EINVAL;
}
global_wa_funcs = &usbWaFuncs;
global_wa_threshold = wa_threshold;
global_wa_sram_parts = wa_sram_parts;
sramBase = mv_sram_usage_get(&sramSize);
if(sramBase == NULL)
{
mvOsPrintf("USB Underrun WA: No free SRAM space\n");
return -ENOMEM;
}
memset(sramBase, 0, sramSize);
idma = mv_usb_find_idma_engine(idma);
if(idma != -1)
{
MV_REG_WRITE(IDMA_BYTE_COUNT_REG(idma), 0);
MV_REG_WRITE(IDMA_CURR_DESC_PTR_REG(idma), 0);
MV_REG_WRITE(IDMA_CTRL_HIGH_REG(idma), ICCHR_ENDIAN_LITTLE
#ifdef MV_CPU_LE
| ICCHR_DESC_BYTE_SWAP_EN
#endif
);
MV_REG_WRITE(IDMA_CTRL_LOW_REG(idma), USB_IDMA_CTRL_LOW_VALUE);
}
mvOsPrintf("USB underrun WA: idma=%d, streaming=%d, threshold=%d, SRAM: base=%p, size=%d, parts=%d\n",
idma, streaming, global_wa_threshold, sramBase, sramSize, global_wa_sram_parts);
#endif /* USB_UNDERRUN_WA */
/*_usb_debug_set_flags(MV_USB_GADGET_DEBUG_FLAGS);*/
/* Enable ARC USB device */
retval = (int)_usb_device_init(dev_no, &mv_dev->mv_usb_handle);
if (retval != USB_OK)
{
mvOsPrintf("\nUSB Initialization failed. Error: %x", retval);
return -EINVAL;
} /* Endif */
if( (epin_first < 1) || (epin_first >= _usb_device_get_max_endpoint(mv_dev->mv_usb_handle)) )
{
mvOsPrintf("\nUSB_%d: epin_first=%d is out of range 1..%d. Use default epin_first=1\n",
dev_no, epin_first, _usb_device_get_max_endpoint(mv_dev->mv_usb_handle) );
epin_first = 1;
}
if( (epout_first < 1) || (epout_first >= _usb_device_get_max_endpoint(mv_dev->mv_usb_handle)) )
{
mvOsPrintf("\nUSB_%d: epout_first=%d is out of range 1..%d. Use default epout_first=1\n",
dev_no, epout_first, _usb_device_get_max_endpoint(mv_dev->mv_usb_handle) );
epout_first = 1;
}
/* Self Power, Remote wakeup disable */
_usb_device_set_status(mv_dev->mv_usb_handle, ARC_USB_STATUS_DEVICE, (1 << DEVICE_SELF_POWERED));
/* Register all ARC Services */
error = _usb_device_register_service(mv_dev->mv_usb_handle,
ARC_USB_SERVICE_BUS_RESET, mv_usb_bus_reset_service);
if (error != USB_OK)
{
mvOsPrintf("\nUSB BUS_RESET Service Registration failed. Error: 0x%x", error);
return -EINVAL;
} /* Endif */
error = _usb_device_register_service(mv_dev->mv_usb_handle,
ARC_USB_SERVICE_SPEED_DETECTION, mv_usb_speed_service);
if (error != USB_OK)
{
mvOsPrintf("\nUSB SPEED_DETECTION Service Registration failed. Error: 0x%x",
error);
return -EINVAL;
} /* Endif */
error = _usb_device_register_service(mv_dev->mv_usb_handle,
ARC_USB_SERVICE_SUSPEND, mv_usb_suspend_service);
if (error != USB_OK)
{
mvOsPrintf("\nUSB SUSPEND Service Registration failed. Error: 0x%x", error);
return -EINVAL;
} /* Endif */
error = _usb_device_register_service(mv_dev->mv_usb_handle,
ARC_USB_SERVICE_SLEEP, mv_usb_suspend_service);
if (error != USB_OK)
{
mvOsPrintf("\nUSB SUSPEND Service Registration failed. Error: 0x%x", error);
return -EINVAL;
} /* Endif */
error = _usb_device_register_service(mv_dev->mv_usb_handle,
ARC_USB_SERVICE_RESUME, mv_usb_resume_service);
if (error != USB_OK)
{
mvOsPrintf("\nUSB RESUME Service Registration failed. Error: 0x%x", error);
return -EINVAL;
} /* Endif */
error = _usb_device_register_service(mv_dev->mv_usb_handle, 0,
mv_usb_ep0_complete_service);
if (error != USB_OK)
{
mvOsPrintf("\nUSB ep0 TR_COMPLETE Service Registration failed. Error: 0x%x", error);
return error;
} /* Endif */
for (i=1; i<_usb_device_get_max_endpoint(mv_dev->mv_usb_handle); i++)
{
error = _usb_device_register_service(mv_dev->mv_usb_handle, i,
mv_usb_tr_complete_service);
if (error != USB_OK)
{
mvOsPrintf("\nUSB ep0 TR_COMPLETE Service Registration failed. Error: 0x%x", error);
return error;
} /* Endif */
}
mv_dev->gadget.speed = USB_SPEED_UNKNOWN;
if( mv_usb_reinit (mv_dev) != USB_OK)
return -EINVAL;
retval = device_register (&mv_dev->gadget.dev);
#ifdef MV_USB_VOLTAGE_FIX
if(retval!= USB_OK)
{
mvOsPrintf("ERROR: %d, device_register() failed\n", retval);
return retval;
}
/* create and init kobject */
retval = mv_usb_initialize_kobject();
if(retval != USB_OK)
{
mvOsPrintf("ERROR: %d, kobject_init() failed\n", retval);
return retval;
}
#endif /* MV_USB_VOLTAGE_FIX */
return retval;
}
static int __exit mv_usb_gadget_remove(struct platform_device *pDev)
{
int i;
struct device *_dev = &pDev->dev;
struct mv_usb_dev *mv_dev = dev_get_drvdata(_dev);
mvOsPrintf("mv_usb_gadget_remove: mv_dev=%p, driver=%p\n",
mv_dev, mv_dev->driver);
/* start with the driver above us */
if (mv_dev->driver)
{
/* should have been done already by driver model core */
mvOsPrintf("pci remove, driver '%s' is still registered\n",
mv_dev->driver->driver.name);
usb_gadget_unregister_driver (mv_dev->driver);
}
spin_lock (&mv_dev->lock);
for (i=0; i<_usb_device_get_max_endpoint(mv_dev->mv_usb_handle); i++)
_usb_device_unregister_service(mv_dev->mv_usb_handle, i);
/* Deregister all other services */
_usb_device_unregister_service(mv_dev->mv_usb_handle, ARC_USB_SERVICE_BUS_RESET);
_usb_device_unregister_service(mv_dev->mv_usb_handle, ARC_USB_SERVICE_SPEED_DETECTION);
_usb_device_unregister_service(mv_dev->mv_usb_handle, ARC_USB_SERVICE_SUSPEND);
_usb_device_unregister_service(mv_dev->mv_usb_handle, ARC_USB_SERVICE_RESUME);
_usb_device_shutdown(mv_dev->mv_usb_handle);
spin_unlock (&mv_dev->lock);
#ifdef MV_USB_VOLTAGE_FIX
if(mv_dev->vbus_gpp_no != (MV_U8)N_A)
{
free_irq (IRQ_GPP_START + mv_dev->vbus_gpp_no, mv_dev);
}
/* delete kobject */
kobject_del(&mv_usb_device.kobj);
#endif /* MV_USB_VOLTAGE_FIX */
the_controllers[mv_dev->dev_no] = 0;
device_unregister (&mv_dev->gadget.dev);
kfree(mv_dev);
dev_set_drvdata(_dev, 0);
return 0;
}
/* global variables from 'regdump' */
static struct proc_dir_entry *usb_resource_dump;
static u32 usb_resource_dump_result;
int usb_resource_dump_write (struct file *file, const char *buffer,
unsigned long count, void *data)
{
return 0;
}
int usb_resource_dump_read (char *buffer, char **buffer_location, off_t offset,
int buffer_length, int *zero, void *ptr)
{
int i, dev;
static int count = 0;
struct mv_usb_dev* mv_dev;
printk("usb_resource_dump_read_%-3d\n", count);
if(offset > 0)
return 0;
count++;
usb_resource_dump_result = count;
for(dev=0; dev<mvCtrlUsbMaxGet(); dev++)
{
mv_dev = the_controllers[dev];
if(mv_dev != NULL)
{
mv_usb_show(mv_dev, 0xff);
_usb_ep_status(mv_dev->mv_usb_handle, 0, ARC_USB_RECV);
_usb_ep_status(mv_dev->mv_usb_handle, 0, ARC_USB_SEND);
for(i=1; i<_usb_device_get_max_endpoint(mv_dev->mv_usb_handle); i++)
{
struct mv_usb_ep *ep;
/* OUT endpoint (RECV) */
ep = &mv_dev->ep[i*2];
if(ep->is_enabled)
_usb_ep_status(mv_dev->mv_usb_handle, i, ARC_USB_RECV);
/* IN endpoint (SEND) */
ep = &mv_dev->ep[i*2+1];
if(ep->is_enabled)
_usb_ep_status(mv_dev->mv_usb_handle, i, ARC_USB_SEND);
}
}
}
return sprintf(buffer, "%x\n", usb_resource_dump_result);
}
int usb_start_resource_dump(void)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
usb_resource_dump = create_proc_entry ("usb_dump" , 0666 , &proc_root);
#else
usb_resource_dump = create_proc_entry ("usb_dump" , 0666, 0);
#endif
usb_resource_dump->read_proc = usb_resource_dump_read;
usb_resource_dump->write_proc = usb_resource_dump_write;
usb_resource_dump->nlink = 1;
return 0;
}
/*
static struct device_driver mv_usb_gadget_driver =
{
.name = (char *) driver_name,
.id_table = NULL,
.bus = &platform_bus_type,
.probe = mv_usb_gadget_probe,
.remove = __exit_p(mv_usb_gadget_remove),
};
*/
static struct platform_driver mv_usb_gadget_driver = {
.probe = mv_usb_gadget_probe,
.remove = __exit_p(mv_usb_gadget_remove),
.id_table = NULL,
.driver = {
.name = driver_name,
.bus = &platform_bus_type
}
};
MODULE_VERSION (DRIVER_VERSION);
MODULE_DESCRIPTION (DRIVER_DESC);
MODULE_AUTHOR ("Dima Epshtein");
MODULE_LICENSE ("GPL");
static int __init init (void)
{
int dev_no;
mvOsPrintf("%s: version %s loaded\n", driver_name, DRIVER_VERSION);
for(dev_no=0; dev_no<mvCtrlUsbMaxGet(); dev_no++)
{
the_controllers[dev_no] = NULL;
}
usb_start_resource_dump();
return platform_driver_register(&mv_usb_gadget_driver);
}
module_init (init);
static void __exit cleanup (void)
{
mvOsPrintf("%s: version %s unloaded\n", driver_name, DRIVER_VERSION);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
remove_proc_entry("usb_dump" , &proc_root);
#else
remove_proc_entry("usb_dump", NULL);
#endif
platform_driver_unregister(&mv_usb_gadget_driver);
}
module_exit (cleanup);