| /******************************************************************************* |
| 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); |
| |