2376 lines
		
	
	
		
			61 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			2376 lines
		
	
	
		
			61 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /*
 | |
|  * NVT680 UDC Driver supports Bulk transfer so far
 | |
|  *
 | |
|  * Copyright (C) 2017 Novatek Technology Corporation
 | |
|  *
 | |
|  * Author : Klins Chen <klins_chen@novatek.com.tw>
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU General Public License as published by
 | |
|  * the Free Software Foundation; version 2 of the License.
 | |
|  */
 | |
| 
 | |
| #include <linux/dma-mapping.h>
 | |
| #include <linux/err.h>
 | |
| #include <linux/interrupt.h>
 | |
| #include <linux/io.h>
 | |
| #include <linux/module.h>
 | |
| #include <linux/platform_device.h>
 | |
| #include <linux/usb/ch9.h>
 | |
| #include <linux/usb/gadget.h>
 | |
| #include <linux/delay.h>
 | |
| #include <linux/of.h>
 | |
| #include <linux/clk.h>
 | |
| #include <plat/efuse_protected.h>
 | |
| 
 | |
| #include "nvt520.h"
 | |
| 
 | |
| #define	DRIVER_DESC	"NOVATEK iVot USB Device NVT520 Controller Driver"
 | |
| #define	DRIVER_VERSION	"19-May-2020"
 | |
| 
 | |
| static const char udc_name[] = "nvt,nvt520_udc";
 | |
| 
 | |
| //static const char * const nvt680_ep_name[] = {
 | |
| //	"ep0", "ep1", "ep2", "ep3", "ep4", "ep5", "ep6", "ep7", "ep8"};
 | |
| 
 | |
| static const struct {
 | |
| 	const char *name;
 | |
| 	const struct usb_ep_caps caps;
 | |
| } ep_info[] = {
 | |
| #define EP_INFO(_name, _caps) \
 | |
| 	{ \
 | |
| 		.name = _name, \
 | |
| 		.caps = _caps, \
 | |
| 	}
 | |
| 
 | |
| 	EP_INFO("ep0",
 | |
| 		USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL, USB_EP_CAPS_DIR_ALL)),
 | |
| 	EP_INFO("ep1",
 | |
| 		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)),
 | |
| 	EP_INFO("ep2",
 | |
| 		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)),
 | |
| 	EP_INFO("ep3",
 | |
| 		USB_EP_CAPS(USB_EP_CAPS_TYPE_INT, USB_EP_CAPS_DIR_ALL)),
 | |
| 	EP_INFO("ep4",
 | |
| 		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)),
 | |
| 	EP_INFO("ep5",
 | |
| 		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)),
 | |
| 
 | |
| #undef EP_INFO
 | |
| };
 | |
| 
 | |
| 
 | |
| 
 | |
| #define	NVTUSB_DEBUG	0
 | |
| 
 | |
| #if NVTUSB_DEBUG
 | |
| #define numsg		printk
 | |
| #define itfnumsg	printk
 | |
| #define devnumsg	printk
 | |
| #define ep0numsg	printk
 | |
| #define intrmsg		printk
 | |
| #else
 | |
| #define numsg		pr_debug
 | |
| #define itfnumsg	pr_debug
 | |
| #define devnumsg	pr_debug
 | |
| #define ep0numsg	pr_debug
 | |
| #define intrmsg		pr_debug
 | |
| #endif
 | |
| 
 | |
| #if 1
 | |
| 
 | |
| #define USB_GETREG(x)    ioread32(nvt680->reg + (x))
 | |
| #define USB_SETREG(x, y) iowrite32((y), nvt680->reg + (x));
 | |
| 
 | |
| static int nvt680_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags);
 | |
| 
 | |
| void usbnvt_set_ep0_done(struct nvt680_udc *nvt680);
 | |
| BOOL usbnvt_ep_stall_status(struct nvt680_udc *nvt680, UINT32 EPn);
 | |
| void usbnvt_set_ep_stall(struct nvt680_udc *nvt680, UINT32 ep_number);
 | |
| void usbnvt_clr_ep_stall(struct nvt680_udc *nvt680, UINT32 EPn);
 | |
| void usbnvt_mask_ep_interrupt(struct nvt680_udc *nvt680, UINT32 EPn);
 | |
| void usbnvt_unmask_ep_interrupt(struct nvt680_udc *nvt680, UINT32 EPn);
 | |
| void nvt680_stop_ep_transfer(struct nvt680_ep *ep);
 | |
| void nvt680_enable_ep_transfer(struct nvt680_ep *ep);
 | |
| void usbnvt_set_ep_write(struct nvt680_udc *nvt680, UINT32 EPn, dma_addr_t d_adr, UINT32 length);
 | |
| void usbnvt_set_ep_read(struct nvt680_udc *nvt680, UINT32 EPn, dma_addr_t d_adr, UINT32 length);
 | |
| 
 | |
| #if 1
 | |
| static void nvt680_done(struct nvt680_ep *ep, struct nvt680_request *req, int status)
 | |
| {
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	if(ep->epnum) {
 | |
| 		itfnumsg("nvt680_done %d\n", ep->epnum);
 | |
| 	} else {
 | |
| 		ep0numsg("nvt680_done %d\n", ep->epnum);
 | |
| 	}
 | |
| 	spin_lock_irqsave(&ep->nvt680->lock, flags);
 | |
| 	list_del_init(&req->queue);
 | |
| 	spin_unlock_irqrestore(&ep->nvt680->lock, flags);
 | |
| 
 | |
| 	/* don't modify queue heads during completion callback */
 | |
| 	if (ep->nvt680->gadget.speed == USB_SPEED_UNKNOWN) {
 | |
| 		req->req.status = -ESHUTDOWN;
 | |
| 	} else {
 | |
| 		req->req.status = status;
 | |
| 	}
 | |
| 
 | |
| 	if(req->req.complete != NULL) {
 | |
| 		req->req.complete(&ep->ep, &req->req);
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	if ((ep->epnum)&&(status == 0)) {
 | |
| 		if (list_empty(&ep->queue)) {
 | |
| 			nvt680_stop_ep_transfer(ep);
 | |
| 		} else {
 | |
| 			if (ep->type != USB_ENDPOINT_XFER_ISOC) {
 | |
| 						nvt680_enable_ep_transfer(ep);
 | |
| 			} else {
 | |
| 				dma_addr_t d_adr;
 | |
| 				UINT32 d_len, epnum = ep->epnum;
 | |
| 				struct nvt680_udc *nvt680;
 | |
| 
 | |
| 				nvt680 = ep->nvt680;
 | |
| 
 | |
| 				if(ep->dir_in) {
 | |
| 					ep = nvt680->ep[epnum];
 | |
| 					req = list_entry(ep->queue.next, struct nvt680_request, queue);
 | |
| 
 | |
| 					if ((req->req.length == 0) && (req->req.actual == 0)) {
 | |
| 
 | |
| 						T_USB_DEVMAXPS_EP_REG DevEPMaxPS;
 | |
| 
 | |
| 						spin_lock_irqsave(&nvt680->lock, flags);
 | |
| 						DevEPMaxPS.reg = USB_GETREG(USB_DEVMAXPS_EP1_REG_OFS + ((epnum - 1) << 2));
 | |
| 						DevEPMaxPS.bit.TX0BYTE_IEP = 1;
 | |
| 						USB_SETREG((USB_DEVMAXPS_EP1_REG_OFS + ((epnum - 1) << 2)), DevEPMaxPS.reg);
 | |
| 						spin_unlock_irqrestore(&nvt680->lock, flags);
 | |
| 
 | |
| 						do {
 | |
| 							DevEPMaxPS.reg = USB_GETREG(USB_DEVMAXPS_EP1_REG_OFS + ((epnum - 1) << 2));
 | |
| 							if(!DevEPMaxPS.bit.TX0BYTE_IEP) {
 | |
| 								break;
 | |
| 							}
 | |
| 							msleep(1);
 | |
| 						}while(DevEPMaxPS.bit.TX0BYTE_IEP);
 | |
| 
 | |
| 						nvt680_done(ep, req, 0);
 | |
| 					} else {
 | |
| 						d_len = req->req.length;
 | |
| 						d_adr = dma_map_single(NULL, (u8 *)(req->req.buf), d_len, ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
 | |
| 
 | |
| 						if (dma_mapping_error(NULL, d_adr)) {
 | |
| 							pr_err("dma_mapping_error\n");
 | |
| 						}
 | |
| 
 | |
| 						dma_sync_single_for_device(NULL, d_adr, d_len, ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
 | |
| 
 | |
| 						req->req.actual = d_len;
 | |
| 						ep->d_adr = d_adr;
 | |
| 						ep->d_len = d_len;
 | |
| 
 | |
| 						usbnvt_set_ep_write(nvt680, epnum, d_adr, d_len);
 | |
| 
 | |
| 						nvt680_stop_ep_transfer(ep);
 | |
| 					}
 | |
| 				} else {
 | |
| 					dma_addr_t d_adr;
 | |
| 					UINT32 d_len, epnum = ep->epnum;
 | |
| 					T_USB_DEVFIFOBYTECNT0_REG	reg_devfifo_bc;
 | |
| 
 | |
| 					ep = nvt680->ep[epnum];
 | |
| 					req = list_entry(ep->queue.next, struct nvt680_request, queue);
 | |
| 
 | |
| 					//reg_devfifo_bc.reg = USB_GETREG(USB_DEVFIFOBYTECNT0_REG_OFS+((epnum-1)<<2));
 | |
| 					//d_len = reg_devfifo_bc.bit.BC_F0;
 | |
| 					reg_devfifo_bc.reg = USB_GETREG(USB_DEVFIFOBYTECNT0_REG_OFS+((nvt680->ep_2_fifo[epnum]>>1)<<2));
 | |
| 					d_len = (reg_devfifo_bc.reg >> ((nvt680->ep_2_fifo[epnum]&0x1)<<4)) & 0x7FF;
 | |
| 
 | |
| 					if(!(d_len & 511)) {
 | |
| 						d_len = req->req.length;
 | |
| 					}
 | |
| 
 | |
| 					d_adr = dma_map_single(NULL, (u8 *)(req->req.buf), d_len, ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
 | |
| 
 | |
| 					if (dma_mapping_error(NULL, d_adr)) {
 | |
| 						pr_err("dma_mapping_error\n");
 | |
| 					}
 | |
| 
 | |
| 					dma_sync_single_for_device(NULL, d_adr, d_len, ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
 | |
| 
 | |
| 					req->req.actual = d_len;
 | |
| 					ep->d_adr = d_adr;
 | |
| 					ep->d_len = d_len;
 | |
| 
 | |
| 					usbnvt_set_ep_read(nvt680, epnum, d_adr, d_len);
 | |
| 
 | |
| 					nvt680_stop_ep_transfer(ep);
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| static int nvt680_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedge)
 | |
| {
 | |
| 	struct nvt680_ep *ep;
 | |
| 	struct nvt680_udc *nvt680;
 | |
| 	unsigned long flags;
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	ep = container_of(_ep, struct nvt680_ep, ep);
 | |
| 
 | |
| 	nvt680 = ep->nvt680;
 | |
| 
 | |
| 
 | |
| 	if (value) {
 | |
| 		usbnvt_set_ep_stall(nvt680, ep->epnum);
 | |
| 
 | |
| 		ep->stall = 1;
 | |
| 
 | |
| 		if (wedge)
 | |
| 			ep->wedged = 1;
 | |
| 
 | |
| 	} else {
 | |
| 		T_USB_DEVMAXPS_EP_REG       devEPMaxPS;
 | |
| 
 | |
| 		spin_lock_irqsave(&ep->nvt680->lock, flags);
 | |
| 		devEPMaxPS.reg = USB_GETREG(USB_DEVMAXPS_EP1_REG_OFS + ((ep->epnum - 1) << 2));
 | |
| 		devEPMaxPS.bit.RSTG_EP = 1;
 | |
| 		USB_SETREG((USB_DEVMAXPS_EP1_REG_OFS + ((ep->epnum - 1) << 2)), devEPMaxPS.reg);
 | |
| 		devEPMaxPS.bit.RSTG_EP = 0;
 | |
| 		USB_SETREG((USB_DEVMAXPS_EP1_REG_OFS + ((ep->epnum - 1) << 2)), devEPMaxPS.reg);
 | |
| 		spin_unlock_irqrestore(&ep->nvt680->lock, flags);
 | |
| 
 | |
| 		usbnvt_clr_ep_stall(nvt680, ep->epnum);
 | |
| 
 | |
| 		ep->stall = 0;
 | |
| 		ep->wedged = 0;
 | |
| 
 | |
| 		if (!list_empty(&ep->queue)) {
 | |
| 			if (list_empty(&ep->queue)) {
 | |
| 				nvt680_stop_ep_transfer(ep);
 | |
| 			} else {
 | |
| 				nvt680_enable_ep_transfer(ep);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void nvt680_start_ep0_data(struct nvt680_ep *ep,
 | |
| 			struct nvt680_request *req)
 | |
| {
 | |
| 	u32 *buffer;
 | |
| 	u32 value,length,i=0;
 | |
| 	s32	opsize;
 | |
| 	T_USB_DEVINTMASKGROUP0_REG reg_mask_g0;
 | |
| 	T_USB_DEVCXCFGFIFOSTATUS_REG reg_cxcfg;
 | |
| 	unsigned long flags;
 | |
| 	struct nvt680_udc *nvt680 = ep->nvt680;
 | |
| 
 | |
| 
 | |
| 	buffer = (u32 *)(req->req.buf + req->req.actual);
 | |
| 
 | |
| 	if (req->req.length - req->req.actual > 64)  {
 | |
| 		length = 64;
 | |
| 
 | |
| 		if (ep->dir_in) {
 | |
| 			spin_lock_irqsave(&ep->nvt680->lock, flags);
 | |
| 			reg_mask_g0.reg = USB_GETREG(USB_DEVINTMASKGROUP0_REG_OFS);
 | |
| 			reg_mask_g0.bit.MCX_IN_INT = 0;
 | |
| 			USB_SETREG(USB_DEVINTMASKGROUP0_REG_OFS, reg_mask_g0.reg);
 | |
| 			spin_unlock_irqrestore(&ep->nvt680->lock, flags);
 | |
| 		}
 | |
| 	} else {
 | |
| 		length = req->req.length - req->req.actual;
 | |
| 
 | |
| 		if (ep->dir_in) {
 | |
| 			spin_lock_irqsave(&ep->nvt680->lock, flags);
 | |
| 			reg_mask_g0.reg = USB_GETREG(USB_DEVINTMASKGROUP0_REG_OFS);
 | |
| 			reg_mask_g0.bit.MCX_IN_INT = 1;
 | |
| 			USB_SETREG(USB_DEVINTMASKGROUP0_REG_OFS, reg_mask_g0.reg);
 | |
| 			spin_unlock_irqrestore(&ep->nvt680->lock, flags);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	reg_cxcfg.reg = USB_GETREG(USB_DEVCXCFGFIFOSTATUS_REG_OFS);
 | |
| 	if(reg_cxcfg.bit.CX_DATAPORT_EN)
 | |
| 		pr_err("DATAPORT EN ERROR!!!\n");
 | |
| 
 | |
| 
 | |
| 	if (ep->dir_in) {
 | |
| 		ep0numsg("fotg200_start_ep0_data IN 0x%X  act=0x%X len=%d\n",req->req.length,req->req.actual,length);
 | |
| 
 | |
| 		spin_lock_irqsave(&ep->nvt680->lock, flags);
 | |
| 
 | |
| 		reg_cxcfg.reg = USB_GETREG(USB_DEVCXCFGFIFOSTATUS_REG_OFS);
 | |
| 		reg_cxcfg.bit.CX_FNT_IN      = length;
 | |
| 		reg_cxcfg.bit.CX_DATAPORT_EN = 1;
 | |
| 		USB_SETREG(USB_DEVCXCFGFIFOSTATUS_REG_OFS, reg_cxcfg.reg);
 | |
| 
 | |
| 		opsize = length;
 | |
| 		while(opsize>0)
 | |
| 		{
 | |
| 			USB_SETREG(USB_DEVCXDATAPORT_REG_OFS, buffer[i++]);
 | |
| 			opsize-=4;
 | |
| 		}
 | |
| 
 | |
| 		reg_cxcfg.reg = USB_GETREG(USB_DEVCXCFGFIFOSTATUS_REG_OFS);
 | |
| 		reg_cxcfg.bit.CX_DATAPORT_EN = 0;
 | |
| 		USB_SETREG(USB_DEVCXCFGFIFOSTATUS_REG_OFS, reg_cxcfg.reg);
 | |
| 
 | |
| 		spin_unlock_irqrestore(&ep->nvt680->lock, flags);
 | |
| 
 | |
| 	} else {
 | |
| 		ep0numsg("fotg200_start_ep0_data OUT 0x%X  act=0x%X\n",req->req.length,req->req.actual);
 | |
| 
 | |
| 		reg_cxcfg.reg = USB_GETREG(USB_DEVCXCFGFIFOSTATUS_REG_OFS);
 | |
| 		opsize = reg_cxcfg.bit.CX_FNT_OUT;
 | |
| 
 | |
| 		while(!opsize) {
 | |
| 			msleep(1);
 | |
| 			reg_cxcfg.reg = USB_GETREG(USB_DEVCXCFGFIFOSTATUS_REG_OFS);
 | |
| 			opsize = reg_cxcfg.bit.CX_FNT_OUT;
 | |
| 		}
 | |
| 
 | |
| 		spin_lock_irqsave(&ep->nvt680->lock, flags);
 | |
| 
 | |
| 		reg_cxcfg.reg = USB_GETREG(USB_DEVCXCFGFIFOSTATUS_REG_OFS);
 | |
| 		reg_cxcfg.bit.CX_DATAPORT_EN = 1;
 | |
| 		USB_SETREG(USB_DEVCXCFGFIFOSTATUS_REG_OFS, reg_cxcfg.reg);
 | |
| 
 | |
| 		while(opsize>0)
 | |
| 		{
 | |
| 			value = USB_GETREG(USB_DEVCXDATAPORT_REG_OFS);
 | |
| 
 | |
| 			if(opsize>=4) {
 | |
| 				buffer[i++] = value;
 | |
| 			} else if (opsize==3) {
 | |
| 				buffer[i] &= ~0xFFFFFF;
 | |
| 				value &= 0xFFFFFF;
 | |
| 				buffer[i] += value;
 | |
| 			} else if (opsize==2) {
 | |
| 				buffer[i] &= ~0xFFFF;
 | |
| 				value &= 0xFFFF;
 | |
| 				buffer[i] += value;
 | |
| 			} else if (opsize==1) {
 | |
| 				buffer[i] &= ~0xFF;
 | |
| 				value &= 0xFF;
 | |
| 				buffer[i] += value;
 | |
| 			}
 | |
| 			opsize-=4;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		reg_cxcfg.reg = USB_GETREG(USB_DEVCXCFGFIFOSTATUS_REG_OFS);
 | |
| 		reg_cxcfg.bit.CX_DATAPORT_EN = 0;
 | |
| 		USB_SETREG(USB_DEVCXCFGFIFOSTATUS_REG_OFS, reg_cxcfg.reg);
 | |
| 
 | |
| 		spin_unlock_irqrestore(&ep->nvt680->lock, flags);
 | |
| 
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	/* update actual transfer length */
 | |
| 	req->req.actual += length;
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| static void nvt680_ep0_queue(struct nvt680_ep *ep, struct nvt680_request *req)
 | |
| {
 | |
| 	unsigned long flags;
 | |
| 	struct nvt680_udc *nvt680 = ep->nvt680;
 | |
| 
 | |
| 	ep0numsg("nvt680_ep0_queue <%d>\n", req->req.length);
 | |
| 
 | |
| 	if (!req->req.length) {
 | |
| 
 | |
| 		usbnvt_set_ep0_done(nvt680);
 | |
| 		nvt680_done(ep, req, 0);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (ep->dir_in) {
 | |
| 		/* if IN */
 | |
| 
 | |
| 		if (req->req.length) {
 | |
| 			nvt680_start_ep0_data(ep, req);
 | |
| 		} else {
 | |
| 			pr_err("%s : req->req.length = 0x%x\n",
 | |
| 			       __func__, req->req.length);
 | |
| 		}
 | |
| 
 | |
| 		if ((req->req.length == req->req.actual) || (req->req.actual < ep->ep.maxpacket)) {
 | |
| 			nvt680_done(ep, req, 0);
 | |
| 
 | |
| 			usbnvt_set_ep0_done(nvt680);
 | |
| 		}
 | |
| 
 | |
| 	} else {	/* OUT */
 | |
| 		T_USB_DEVINTMASKGROUP0_REG reg_mask_g0;
 | |
| 
 | |
| 		spin_lock_irqsave(&ep->nvt680->lock, flags);
 | |
| 		reg_mask_g0.reg = USB_GETREG(USB_DEVINTMASKGROUP0_REG_OFS);
 | |
| 		reg_mask_g0.bit.MCX_OUT_INT = 0;
 | |
| 		USB_SETREG(USB_DEVINTMASKGROUP0_REG_OFS, reg_mask_g0.reg);
 | |
| 		spin_unlock_irqrestore(&ep->nvt680->lock, flags);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| static void nvt680_get_status(struct nvt680_udc *nvt680, struct usb_ctrlrequest *ctrl)
 | |
| {
 | |
| 	u8 epnum;
 | |
| 
 | |
| 	switch (ctrl->bRequestType & USB_RECIP_MASK) {
 | |
| 	case USB_RECIP_DEVICE:
 | |
| 		nvt680->ep0_data = 1 << USB_DEVICE_SELF_POWERED;
 | |
| 		break;
 | |
| 	case USB_RECIP_INTERFACE:
 | |
| 		nvt680->ep0_data = 0;
 | |
| 		break;
 | |
| 	case USB_RECIP_ENDPOINT:
 | |
| 		epnum = ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK;
 | |
| 
 | |
| 		if (epnum)
 | |
| 			nvt680->ep0_data = usbnvt_ep_stall_status(nvt680, epnum)<< USB_ENDPOINT_HALT;
 | |
| 		else
 | |
| 			usbnvt_set_ep_stall(nvt680, 0);
 | |
| 
 | |
| 		break;
 | |
| 
 | |
| 	default:
 | |
| 		usbnvt_set_ep_stall(nvt680, 0);
 | |
| 		return;		/* exit */
 | |
| 	}
 | |
| 
 | |
| 	nvt680->ep0_req->buf = &nvt680->ep0_data;
 | |
| 	nvt680->ep0_req->length = 2;
 | |
| 	nvt680_ep_queue(nvt680->gadget.ep0, nvt680->ep0_req, GFP_KERNEL);
 | |
| 
 | |
| }
 | |
| 
 | |
| static void nvt680_clear_feature(struct nvt680_udc *nvt680, struct usb_ctrlrequest *ctrl)
 | |
| {
 | |
| 	struct nvt680_ep *ep = nvt680->ep[ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK];
 | |
| 
 | |
| 	switch (ctrl->bRequestType & USB_RECIP_MASK) {
 | |
| 	case USB_RECIP_DEVICE:
 | |
| 		usbnvt_set_ep0_done(nvt680);
 | |
| 		break;
 | |
| 	case USB_RECIP_INTERFACE:
 | |
| 		usbnvt_set_ep0_done(nvt680);
 | |
| 		break;
 | |
| 	case USB_RECIP_ENDPOINT:
 | |
| 
 | |
| 		if (ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK) {
 | |
| 
 | |
| 			if (ep->wedged) {
 | |
| 				usbnvt_set_ep0_done(nvt680);
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			usbnvt_clr_ep_stall(nvt680, ep->epnum);
 | |
| 
 | |
| 			if (ep->stall) {
 | |
| 				printk("nvt680_clear_feature nvt680_set_halt_and_wedge\r\n");
 | |
| 				nvt680_set_halt_and_wedge(&ep->ep, 0, 0);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		usbnvt_set_ep0_done(nvt680);
 | |
| 
 | |
| 		break;
 | |
| 	default:
 | |
| 		usbnvt_set_ep_stall(nvt680, 0);
 | |
| 		break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void nvt680_set_feature(struct nvt680_udc *nvt680, struct usb_ctrlrequest *ctrl)
 | |
| {
 | |
| 	switch (ctrl->bRequestType & USB_RECIP_MASK) {
 | |
| 	case USB_RECIP_DEVICE:
 | |
| 		usbnvt_set_ep0_done(nvt680);
 | |
| 		break;
 | |
| 
 | |
| 	case USB_RECIP_INTERFACE:
 | |
| 		usbnvt_set_ep0_done(nvt680);
 | |
| 		break;
 | |
| 
 | |
| 	case USB_RECIP_ENDPOINT: {
 | |
| 		u8 epnum;
 | |
| 
 | |
| 		epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK;
 | |
| 
 | |
| 		usbnvt_set_ep_stall(nvt680, epnum);
 | |
| 
 | |
| 		usbnvt_set_ep0_done(nvt680);
 | |
| 	} break;
 | |
| 
 | |
| 	default:
 | |
| 		usbnvt_set_ep_stall(nvt680, 0);
 | |
| 		break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void nvt680_set_fifo_cfg(struct nvt680_ep *ep, u32 epnum, u32 dir_in,u32 type, u32 mps)
 | |
| {
 | |
| 	struct nvt680_udc *nvt680 = ep->nvt680;
 | |
| 	T_USB_DEVBUFCFG0_REG	reg_devbufcfg0;
 | |
| 	T_USB_DEVMAXPS_EP_REG 	DevEPMaxPS;
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	spin_lock_irqsave(&nvt680->lock, flags);
 | |
| 
 | |
| 	DevEPMaxPS.reg = USB_GETREG(USB_DEVMAXPS_EP1_REG_OFS + ((epnum - 1) << 2));
 | |
| 	DevEPMaxPS.bit.MAXPS_EP = mps;
 | |
| 
 | |
| 	if (ep->type == USB_ENDPOINT_XFER_ISOC) {
 | |
| 		if ((ep->ep.maxpacket == 0x400)&&(ep->ep.mult == 1))
 | |
| 			DevEPMaxPS.bit.TX_NUM_HBW_EP = 2;
 | |
| 		else
 | |
| 			DevEPMaxPS.bit.TX_NUM_HBW_EP = 1;
 | |
| 
 | |
| 	} else {
 | |
| 		DevEPMaxPS.bit.TX_NUM_HBW_EP = 0;
 | |
| 	}
 | |
| 
 | |
| 	if (dir_in > 0) {
 | |
| 		DevEPMaxPS.bit.DIR_EP = 0;
 | |
| 		DevEPMaxPS.bit.FNO_IEP = nvt680->ep_2_fifo[epnum];
 | |
| 	} else {
 | |
| 		DevEPMaxPS.bit.DIR_EP = 1;
 | |
| 		DevEPMaxPS.bit.FNO_OEP = nvt680->ep_2_fifo[epnum];
 | |
| 	}
 | |
| 	USB_SETREG((USB_DEVMAXPS_EP1_REG_OFS + ((epnum - 1) << 2)), DevEPMaxPS.reg);
 | |
| 
 | |
| 	{
 | |
| 		u8 startfifo,regofs;
 | |
| 
 | |
| 		startfifo = nvt680->ep_2_fifo[epnum];
 | |
| 		if(startfifo < 4) {
 | |
| 			regofs = 0;
 | |
| 		} else {
 | |
| 			regofs = 4;
 | |
| 			startfifo -= 4;
 | |
| 		}
 | |
| 
 | |
| 		reg_devbufcfg0.reg = USB_GETREG(USB_DEVBUFCFG0_REG_OFS+regofs);
 | |
| 
 | |
| 		if(mps <= 512) {
 | |
| 			reg_devbufcfg0.reg &= ~(0xFF << (startfifo<<3));
 | |
| 			reg_devbufcfg0.reg |= (type << (startfifo<<3));//buf_type
 | |
| 			reg_devbufcfg0.reg |= (0x0    << (startfifo<<3));//buf_blkno
 | |
| 			reg_devbufcfg0.reg |= (0    << (startfifo<<3));//buf_blksz
 | |
| 			if(dir_in > 0)
 | |
| 				reg_devbufcfg0.reg |= (0x20<< (startfifo<<3));//dir_fifo
 | |
| 
 | |
| 			reg_devbufcfg0.reg |= (0x80 << (startfifo<<3));//buf_en
 | |
| 
 | |
| 		} else if ((ep->ep.maxpacket == 0x400)&&(ep->ep.mult == 1)&&(ep->type == USB_ENDPOINT_XFER_ISOC)&&(regofs==4)) {
 | |
| 			reg_devbufcfg0.reg = 0x353535B5;
 | |
| 		} else {
 | |
| 
 | |
| 			reg_devbufcfg0.reg &= ~(0xFFFF << (startfifo<<3));
 | |
| 			reg_devbufcfg0.reg |= (type << (startfifo<<3));//buf_type
 | |
| 			reg_devbufcfg0.reg |= (0    << (startfifo<<3));//buf_blkno
 | |
| 			reg_devbufcfg0.reg |= (0x10 << (startfifo<<3));//buf_blksz
 | |
| 			if(dir_in > 0)
 | |
| 				reg_devbufcfg0.reg |= (0x20<< (startfifo<<3));//dir_fifo
 | |
| 
 | |
| 			reg_devbufcfg0.reg |= (0x80 << (startfifo<<3));//buf_en
 | |
| 
 | |
| 			reg_devbufcfg0.reg |= (type << ((startfifo<<3)+8));//buf_type
 | |
| 			reg_devbufcfg0.reg |= (0    << ((startfifo<<3)+8));//buf_blkno
 | |
| 			reg_devbufcfg0.reg |= (0x10 << ((startfifo<<3)+8));//buf_blksz
 | |
| 			if(dir_in > 0)
 | |
| 				reg_devbufcfg0.reg |= (0x20<< ((startfifo<<3)+8));//dir_fifo
 | |
| 		}
 | |
| 
 | |
| 		USB_SETREG(USB_DEVBUFCFG0_REG_OFS+regofs, reg_devbufcfg0.reg);
 | |
| 	}
 | |
| 
 | |
| 	spin_unlock_irqrestore(&nvt680->lock, flags);
 | |
| 
 | |
| }
 | |
| 
 | |
| static int nvt680_config_ep(struct nvt680_ep *ep, const struct usb_endpoint_descriptor *desc)
 | |
| {
 | |
| 	struct nvt680_udc *nvt680 = ep->nvt680;
 | |
| 
 | |
| 	if ((ep->ep.mult == 1) && (ep->type == USB_ENDPOINT_XFER_ISOC) && (ep->ep.maxpacket == 0x400)) {
 | |
| 		if ((nvt680->fifo_vld_msk & 0x0F) == 0x0F) {
 | |
| 
 | |
| 			nvt680->ep_2_fifo[ep->epnum]		= 0;
 | |
| 			nvt680->fifo_2_ep[nvt680->ep_2_fifo[ep->epnum]] = ep->epnum;
 | |
| 			nvt680->fifo_vld_msk &= 0xF0;
 | |
| 		} else {
 | |
| 			pr_err("FIFO 4-7 for isoc-in 2048 not available\n");
 | |
| 			return -ENOPKG;
 | |
| 		}
 | |
| #if 1
 | |
| 	} else if (ep->type == USB_ENDPOINT_XFER_ISOC) {
 | |
| 		u8 i,chkmsk;
 | |
| 
 | |
| 		if (ep->ep.mult) {
 | |
| 			pr_err("mult is supported!\n");
 | |
| 		}
 | |
| 
 | |
| 		if (ep->ep.maxpacket > 512)
 | |
| 			chkmsk = 0x3;
 | |
| 		else
 | |
| 			chkmsk = 0x1;
 | |
| 
 | |
| 		for (i=4; i < 8; i+=2) {
 | |
| 			if (((nvt680->fifo_vld_msk>>i) & chkmsk) == chkmsk) {
 | |
| 				nvt680->ep_2_fifo[ep->epnum]		= i;
 | |
| 				nvt680->fifo_2_ep[nvt680->ep_2_fifo[ep->epnum]] = ep->epnum;
 | |
| 				nvt680->fifo_vld_msk = nvt680->fifo_vld_msk & ~(chkmsk << i);
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			if (i==6) {
 | |
| 				pr_err("FIFO search not available\n");
 | |
| 				return -ENOPKG;
 | |
| 			}
 | |
| 		}
 | |
| #endif
 | |
| 	} else {
 | |
| 		u8 i,chkmsk;
 | |
| 
 | |
| 		if (ep->ep.mult) {
 | |
| 			pr_err("mult is supported!\n");
 | |
| 		}
 | |
| 
 | |
| 		if (ep->ep.maxpacket > 512)
 | |
| 			chkmsk = 0x3;
 | |
| 		else
 | |
| 			chkmsk = 0x1;
 | |
| 
 | |
| 		for (i=4; i < 8; i++) {
 | |
| 			if (((nvt680->fifo_vld_msk>>i) & chkmsk) == chkmsk) {
 | |
| 				nvt680->ep_2_fifo[ep->epnum]		= i;
 | |
| 				nvt680->fifo_2_ep[nvt680->ep_2_fifo[ep->epnum]] = ep->epnum;
 | |
| 				nvt680->fifo_vld_msk = nvt680->fifo_vld_msk & ~(chkmsk << i);
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			//if (i==7) {
 | |
| 			//	pr_err("FIFO search not available\n");
 | |
| 			//	return -ENOPKG;
 | |
| 			//}
 | |
| 		}
 | |
| 
 | |
| 		if (i==8) {
 | |
| 			for (i=0; i < 4; i++) {
 | |
| 				if (((nvt680->fifo_vld_msk>>i) & chkmsk) == chkmsk) {
 | |
| 					nvt680->ep_2_fifo[ep->epnum]		= i;
 | |
| 					nvt680->fifo_2_ep[nvt680->ep_2_fifo[ep->epnum]] = ep->epnum;
 | |
| 					nvt680->fifo_vld_msk = nvt680->fifo_vld_msk & ~(chkmsk << i);
 | |
| 					break;
 | |
| 				}
 | |
| 
 | |
| 				if (i==3) {
 | |
| 					pr_err("FIFO search not available\n");
 | |
| 					return -ENOPKG;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	nvt680_set_fifo_cfg(ep, ep->epnum, ep->dir_in, ep->type, ep->ep.maxpacket);
 | |
| 	nvt680->ep_en &= ~(0x1<<ep->epnum);
 | |
| 	nvt680->ep[ep->epnum] = ep;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| void nvt680_enable_ep_transfer(struct nvt680_ep *ep)
 | |
| {
 | |
| 	struct nvt680_udc *nvt680 = ep->nvt680;
 | |
| 
 | |
| 	devnumsg("nvt680_enable_ep_transfer %d\n", ep->epnum);
 | |
| 
 | |
| 	usbnvt_unmask_ep_interrupt(nvt680, ep->epnum);
 | |
| }
 | |
| 
 | |
| void nvt680_stop_ep_transfer(struct nvt680_ep *ep)
 | |
| {
 | |
| 	struct nvt680_udc *nvt680 = ep->nvt680;
 | |
| 
 | |
| 	devnumsg("nvt680_stop_ep_transfer %d\n", ep->epnum);
 | |
| 
 | |
| 	usbnvt_mask_ep_interrupt(nvt680, ep->epnum);
 | |
| }
 | |
| 
 | |
| static int nvt680_ep_release(struct nvt680_ep *ep)
 | |
| {
 | |
| 	struct nvt680_udc *nvt680 = ep->nvt680;
 | |
| 	T_USB_DEVMAXPS_EP_REG       devEPMaxPS;
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	if (!ep->epnum)
 | |
| 		return 0;
 | |
| 
 | |
| 	spin_lock_irqsave(&ep->nvt680->lock, flags);
 | |
| 	devEPMaxPS.reg = USB_GETREG(USB_DEVMAXPS_EP1_REG_OFS + ((ep->epnum - 1) << 2));
 | |
| 	devEPMaxPS.bit.RSTG_EP = 1;
 | |
| 	USB_SETREG((USB_DEVMAXPS_EP1_REG_OFS + ((ep->epnum - 1) << 2)), devEPMaxPS.reg);
 | |
| 	devEPMaxPS.bit.RSTG_EP = 0;
 | |
| 	USB_SETREG((USB_DEVMAXPS_EP1_REG_OFS + ((ep->epnum - 1) << 2)), devEPMaxPS.reg);
 | |
| 	spin_unlock_irqrestore(&ep->nvt680->lock, flags);
 | |
| 
 | |
| 	//ep->epnum = 0;
 | |
| 	ep->stall = 0;
 | |
| 	ep->wedged = 0;
 | |
| 
 | |
| #if 1
 | |
| 	if ((ep->ep.mult == 1) && (ep->type == USB_ENDPOINT_XFER_ISOC) && (ep->ep.maxpacket == 0x400)) {
 | |
| 		if ((nvt680->fifo_vld_msk & 0xF0) == 0) {
 | |
| 
 | |
| 			//nvt680->ep_2_fifo[ep->epnum]		= 0xF;
 | |
| 			//nvt680->fifo_2_ep[nvt680->ep_2_fifo[ep->epnum]] = ep->epnum;
 | |
| 			nvt680->fifo_vld_msk |= 0xF;
 | |
| 		} else {
 | |
| 			pr_err("FIFO 0-3 for isoc-in 2048 unmap error\n");
 | |
| 		}
 | |
| 	} else {
 | |
| 		u8 chkmsk;
 | |
| 
 | |
| 		if (ep->ep.mult) {
 | |
| 			pr_err("mult is supported!\n");
 | |
| 		}
 | |
| 
 | |
| 		if (ep->ep.maxpacket > 512)
 | |
| 			chkmsk = 0x3;
 | |
| 		else
 | |
| 			chkmsk = 0x1;
 | |
| 
 | |
| 		if (((nvt680->fifo_vld_msk >> nvt680->ep_2_fifo[ep->epnum]) & chkmsk) == 0) {
 | |
| 			//nvt680->ep_2_fifo[ep->epnum]		= 0xF;
 | |
| 			//nvt680->fifo_2_ep[nvt680->ep_2_fifo[ep->epnum]] = ep->epnum;
 | |
| 			nvt680->fifo_vld_msk = nvt680->fifo_vld_msk | (chkmsk << nvt680->ep_2_fifo[ep->epnum]);
 | |
| 		} else {
 | |
| 			pr_err("FIFO unmap error\n");
 | |
| 		}
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	nvt680->ep_en |= (0x1<<ep->epnum);
 | |
| 
 | |
| #endif
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| #endif
 | |
| 
 | |
| void usbnvt_set_ep_read(struct nvt680_udc *nvt680, UINT32 EPn, dma_addr_t d_adr, UINT32 length)
 | |
| {
 | |
| #if 1
 | |
| 	UINT32                        fifonum;
 | |
| 	UINT32                        uiRegOfs;
 | |
| 	unsigned long				  flags;
 | |
| 
 | |
| 	fifonum = nvt680->ep_2_fifo[EPn];
 | |
| 
 | |
| 	if(fifonum >= 4) {
 | |
| 		T_USB_DEVDMA2CTRL0_REG        devDMAxCtl;
 | |
| 		T_USB_DEVDMA2CTRL1_REG        devDMAxCtl2;
 | |
| 
 | |
| 		uiRegOfs = (fifonum-4) << 3;
 | |
| 
 | |
| 		itfnumsg("usbnvt_set_ep_read: EP%d Addr=0x%08X Size=0x%08X\n", EPn, d_adr, length);
 | |
| 
 | |
| 		devDMAxCtl.reg = USB_GETREG(USB_DEVDMA2CTRL0_REG_OFS + uiRegOfs);
 | |
| 		if (devDMAxCtl.bit.DMA_START == 1) {
 | |
| 			printk("EP%d: DMA-%d not available\r\n", EPn, fifonum);
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		spin_lock_irqsave(&nvt680->lock, flags);
 | |
| 
 | |
| 		devDMAxCtl.bit.DMA_TYPE = 0;// FIFO to memory
 | |
| 		devDMAxCtl.bit.DMA_LEN  = length;
 | |
| 		USB_SETREG(USB_DEVDMA2CTRL0_REG_OFS + uiRegOfs, devDMAxCtl.reg);
 | |
| 
 | |
| 		// set Dma Addr
 | |
| 		devDMAxCtl2.reg = d_adr;
 | |
| 		USB_SETREG(USB_DEVDMA2CTRL1_REG_OFS + uiRegOfs, devDMAxCtl2.reg);
 | |
| 
 | |
| 		// start Dma
 | |
| 		devDMAxCtl.bit.DMA_START = 1;
 | |
| 		USB_SETREG(USB_DEVDMA2CTRL0_REG_OFS + uiRegOfs, devDMAxCtl.reg);
 | |
| 
 | |
| 		spin_unlock_irqrestore(&nvt680->lock, flags);
 | |
| 	}else {
 | |
| 		T_USB_DEVDMACTRL1_REG         devDMACtl1;
 | |
| 		T_USB_DEVDMACTRL2_REG         devDMACtl2;
 | |
| 		T_USB_DEVACCFIFO_REG    	  devAccFifo;
 | |
| 
 | |
| 		do {
 | |
| 			devDMACtl1.reg = USB_GETREG(USB_DEVDMACTRL1_REG_OFS);
 | |
| 
 | |
| 			if(devDMACtl1.bit.DMA_START) {
 | |
| 				cpu_relax();
 | |
| 				udelay(100);
 | |
| 			}
 | |
| 		}while(devDMACtl1.bit.DMA_START);
 | |
| 
 | |
| 		// Config DMA direction & length
 | |
| 		devDMACtl1.reg = USB_GETREG(USB_DEVDMACTRL1_REG_OFS);
 | |
| 		devDMACtl1.bit.DMA_TYPE = 0; // FIFO to memory
 | |
| 		devDMACtl1.bit.DMA_LEN  = length;
 | |
| 		USB_SETREG(USB_DEVDMACTRL1_REG_OFS, devDMACtl1.reg);
 | |
| 
 | |
| 		devAccFifo.reg = 0;
 | |
| 		devAccFifo.bit.ACC_F0_3 = fifonum;
 | |
| 		USB_SETREG(USB_DEVACCFIFO_REG_OFS, devAccFifo.reg);
 | |
| 
 | |
| 		// set Dma Addr
 | |
| 		devDMACtl2.reg = d_adr;
 | |
| 		USB_SETREG(USB_DEVDMACTRL2_REG_OFS, devDMACtl2.reg);
 | |
| 
 | |
| 		// start Dma
 | |
| 		devDMACtl1.reg = USB_GETREG(USB_DEVDMACTRL1_REG_OFS);
 | |
| 		devDMACtl1.bit.DMA_START = 1;
 | |
| 		USB_SETREG(USB_DEVDMACTRL1_REG_OFS, devDMACtl1.reg);
 | |
| 
 | |
| 	}
 | |
| #else
 | |
| 	printk("usbnvt_set_ep_read: EP%d Addr=0x%08X Size=0x%08X\n", EPn, d_adr, length);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| void usbnvt_set_ep_write(struct nvt680_udc *nvt680, UINT32 EPn, dma_addr_t d_adr, UINT32 length)
 | |
| {
 | |
| #if 1
 | |
| 	UINT32                  	  fifonum;
 | |
| 	UINT32                        uiRegOfs;
 | |
| 	unsigned long				  flags;
 | |
| 
 | |
| 	fifonum = nvt680->ep_2_fifo[EPn];
 | |
| 
 | |
| 	if(fifonum >= 4) {
 | |
| 		T_USB_DEVDMA2CTRL0_REG        devDMAxCtl;
 | |
| 		T_USB_DEVDMA2CTRL1_REG        devDMAxCtl2;
 | |
| 
 | |
| 		uiRegOfs = (fifonum-4) << 3;
 | |
| 
 | |
| 		itfnumsg("usbnvt_set_ep_write: EP%d Addr=0x%08X Size=0x%08X\n", EPn, d_adr, length);
 | |
| 
 | |
| 		//do {
 | |
| 		//	devDMAxCtl.reg = USB_GETREG(USB_DEVDMA2CTRL0_REG_OFS + uiRegOfs);
 | |
| 		//} while(devDMAxCtl.bit.DMA_START == 1);
 | |
| 		if (devDMAxCtl.bit.DMA_START == 1) {
 | |
| 			printk("EP%d: DMA-%d not available\r\n", EPn, fifonum);
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		spin_lock_irqsave(&nvt680->lock, flags);
 | |
| 
 | |
| 		devDMAxCtl.bit.DMA_TYPE = 1;// memory to FIFO
 | |
| 		devDMAxCtl.bit.DMA_LEN  = length;
 | |
| 		USB_SETREG(USB_DEVDMA2CTRL0_REG_OFS + uiRegOfs, devDMAxCtl.reg);
 | |
| 
 | |
| 		// set Dma Addr
 | |
| 		devDMAxCtl2.reg = d_adr;
 | |
| 		USB_SETREG(USB_DEVDMA2CTRL1_REG_OFS + uiRegOfs, devDMAxCtl2.reg);
 | |
| 
 | |
| 		// start Dma
 | |
| 		devDMAxCtl.bit.DMA_START = 1;
 | |
| 		USB_SETREG(USB_DEVDMA2CTRL0_REG_OFS + uiRegOfs, devDMAxCtl.reg);
 | |
| 
 | |
| 		spin_unlock_irqrestore(&nvt680->lock, flags);
 | |
| 	}else {
 | |
| 		T_USB_DEVDMACTRL1_REG         devDMACtl1;
 | |
| 		T_USB_DEVDMACTRL2_REG         devDMACtl2;
 | |
| 		T_USB_DEVACCFIFO_REG    	  devAccFifo;
 | |
| 
 | |
| 		do {
 | |
| 			devDMACtl1.reg = USB_GETREG(USB_DEVDMACTRL1_REG_OFS);
 | |
| 
 | |
| 			if(devDMACtl1.bit.DMA_START) {
 | |
| 				cpu_relax();
 | |
| 				udelay(100);
 | |
| 			}
 | |
| 		}while(devDMACtl1.bit.DMA_START);
 | |
| 
 | |
| 		// Config DMA direction & length
 | |
| 		devDMACtl1.reg = USB_GETREG(USB_DEVDMACTRL1_REG_OFS);
 | |
| 		devDMACtl1.bit.DMA_TYPE = 1; // memory to FIFO
 | |
| 		devDMACtl1.bit.DMA_LEN  = length;
 | |
| 		USB_SETREG(USB_DEVDMACTRL1_REG_OFS, devDMACtl1.reg);
 | |
| 
 | |
| 		devAccFifo.reg = 0;
 | |
| 		devAccFifo.bit.ACC_F0_3 = fifonum;
 | |
| 		USB_SETREG(USB_DEVACCFIFO_REG_OFS, devAccFifo.reg);
 | |
| 
 | |
| 		// set Dma Addr
 | |
| 		devDMACtl2.reg = d_adr;
 | |
| 		USB_SETREG(USB_DEVDMACTRL2_REG_OFS, devDMACtl2.reg);
 | |
| 
 | |
| 		// start Dma
 | |
| 		devDMACtl1.reg = USB_GETREG(USB_DEVDMACTRL1_REG_OFS);
 | |
| 		devDMACtl1.bit.DMA_START = 1;
 | |
| 		USB_SETREG(USB_DEVDMACTRL1_REG_OFS, devDMACtl1.reg);
 | |
| 
 | |
| 	}
 | |
| #else
 | |
| 	printk("usbnvt_set_ep_write: EP%d Addr=0x%08X Size=0x%08X\n", EPn, d_adr, length);
 | |
| #endif
 | |
| 	return;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| BOOL usbnvt_get_ep_direction_in(struct nvt680_udc *nvt680, UINT32 EPn)
 | |
| {
 | |
| 	T_USB_DEVMAXPS_EP_REG   devEPMaxPS;
 | |
| 
 | |
| 	devEPMaxPS.reg = USB_GETREG(USB_DEVMAXPS_EP1_REG_OFS + ((EPn - 1) << 2));
 | |
| 	return !devEPMaxPS.bit.DIR_EP;
 | |
| }
 | |
| 
 | |
| void usbnvt_mask_ep_interrupt(struct nvt680_udc *nvt680, UINT32 EPn)
 | |
| {
 | |
| 	T_USB_DEVINTMASKGROUP0_REG devIntMskGrp0;
 | |
| 	T_USB_DEVINTMASKGROUP1_REG intGroup1Mask;
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	if (EPn == 0) {
 | |
| 		devIntMskGrp0.reg = USB_GETREG(USB_DEVINTMASKGROUP0_REG_OFS);
 | |
| 		devIntMskGrp0.bit.MCX_IN_INT  = 1;
 | |
| 		devIntMskGrp0.bit.MCX_OUT_INT = 1;
 | |
| 		USB_SETREG(USB_DEVINTMASKGROUP0_REG_OFS, devIntMskGrp0.reg);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if(nvt680->ep_2_fifo[EPn] == 0xF) {
 | |
| 		printk("usbnvt_unmask_ep_interrupt ERROR!!!!!!!!!!!!!!!!!!!!!!\r\n");
 | |
| 	}
 | |
| 
 | |
| 	devnumsg("usbnvt_mask_ep_interrupt %d\n", EPn);
 | |
| 
 | |
| 	spin_lock_irqsave(&nvt680->lock, flags);
 | |
| 
 | |
| 	if (usbnvt_get_ep_direction_in(nvt680, EPn)) {
 | |
| 		intGroup1Mask.reg  = USB_GETREG(USB_DEVINTMASKGROUP1_REG_OFS);
 | |
| 		intGroup1Mask.reg |= (0x1 << (16 + nvt680->ep_2_fifo[EPn]));
 | |
| 		USB_SETREG(USB_DEVINTMASKGROUP1_REG_OFS, intGroup1Mask.reg);
 | |
| 	} else {
 | |
| 		intGroup1Mask.reg  = USB_GETREG(USB_DEVINTMASKGROUP1_REG_OFS);
 | |
| 		intGroup1Mask.reg |= (0x3 << ( nvt680->ep_2_fifo[EPn] << 1));
 | |
| 		USB_SETREG(USB_DEVINTMASKGROUP1_REG_OFS, intGroup1Mask.reg);
 | |
| 	}
 | |
| 
 | |
| 	spin_unlock_irqrestore(&nvt680->lock, flags);
 | |
| 
 | |
| }
 | |
| 
 | |
| void usbnvt_unmask_ep_interrupt(struct nvt680_udc *nvt680, UINT32 EPn)
 | |
| {
 | |
| 	T_USB_DEVINTMASKGROUP0_REG devIntMskGrp0;
 | |
| 	T_USB_DEVINTMASKGROUP1_REG intGroup1Mask;
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	if (EPn == 0) {
 | |
| 		devIntMskGrp0.reg = USB_GETREG(USB_DEVINTMASKGROUP0_REG_OFS);
 | |
| 		devIntMskGrp0.bit.MCX_IN_INT  = 0;
 | |
| 		devIntMskGrp0.bit.MCX_OUT_INT = 0;
 | |
| 		USB_SETREG(USB_DEVINTMASKGROUP0_REG_OFS, devIntMskGrp0.reg);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if(nvt680->ep_2_fifo[EPn] == 0xF) {
 | |
| 		printk("usbnvt_unmask_ep_interrupt ERROR!!!!!!!!!!!!!!!!!!!!!!\r\n");
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	devnumsg("usbnvt_unmask_ep_interrupt %d\n", EPn);
 | |
| 
 | |
| 	spin_lock_irqsave(&nvt680->lock, flags);
 | |
| 
 | |
| 	if (usbnvt_get_ep_direction_in(nvt680, EPn)) {
 | |
| 		intGroup1Mask.reg  = USB_GETREG(USB_DEVINTMASKGROUP1_REG_OFS);
 | |
| 		intGroup1Mask.reg &= ~(0x1 << (16 +  nvt680->ep_2_fifo[EPn]));
 | |
| 		USB_SETREG(USB_DEVINTMASKGROUP1_REG_OFS, intGroup1Mask.reg);
 | |
| 	} else {
 | |
| 		intGroup1Mask.reg  = USB_GETREG(USB_DEVINTMASKGROUP1_REG_OFS);
 | |
| 		intGroup1Mask.reg &= ~(0x3 << ( nvt680->ep_2_fifo[EPn] << 1));
 | |
| 		USB_SETREG(USB_DEVINTMASKGROUP1_REG_OFS, intGroup1Mask.reg);
 | |
| 	}
 | |
| 
 | |
| 	spin_unlock_irqrestore(&nvt680->lock, flags);
 | |
| 
 | |
| }
 | |
| 
 | |
| BOOL usbnvt_ep_stall_status(struct nvt680_udc *nvt680, UINT32 EPn)
 | |
| {
 | |
| 	T_USB_DEVMAXPS_EP_REG   devEPMaxPS;
 | |
| 
 | |
| 	devEPMaxPS.reg = USB_GETREG(USB_DEVMAXPS_EP1_REG_OFS + ((EPn - 1) << 2));
 | |
| 
 | |
| 	return devEPMaxPS.bit.STL_EP;
 | |
| }
 | |
| 
 | |
| BOOL usbnvt_is_highspeed_enabled(struct nvt680_udc *nvt680)
 | |
| {
 | |
| 	T_USB_DEVMAINCTRL_REG devMainCtl;
 | |
| 
 | |
| 	devMainCtl.reg = USB_GETREG(USB_DEVMAINCTRL_REG_OFS);
 | |
| 	return (BOOL)(devMainCtl.bit.HS_EN);
 | |
| }
 | |
| 
 | |
| void usbnvt_set_ep0_done(struct nvt680_udc *nvt680)
 | |
| {
 | |
| 	T_USB_DEVCXCFGFIFOSTATUS_REG    reg_cxcfg;
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	ep0numsg("usbnvt_set_ep0_done\n");
 | |
| 
 | |
| 	spin_lock_irqsave(&nvt680->lock, flags);
 | |
| 
 | |
| 	reg_cxcfg.reg = USB_GETREG(USB_DEVCXCFGFIFOSTATUS_REG_OFS);
 | |
| 	reg_cxcfg.bit.CX_DONE = 1;
 | |
| 	USB_SETREG(USB_DEVCXCFGFIFOSTATUS_REG_OFS, reg_cxcfg.reg);
 | |
| 
 | |
| 	spin_unlock_irqrestore(&nvt680->lock, flags);
 | |
| }
 | |
| 
 | |
| void usbnvt_set_ep_stall(struct nvt680_udc *nvt680, UINT32 ep_number)
 | |
| {
 | |
| 	T_USB_DEVMAXPS_EP_REG           devEPMaxPS;
 | |
| 	T_USB_DEVCXCFGFIFOSTATUS_REG    devCXCFG;
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	//printk("usbnvt_set_ep_stall %d\r\n", ep_number);
 | |
| 
 | |
| 	spin_lock_irqsave(&nvt680->lock, flags);
 | |
| 
 | |
| 	if (ep_number == 0) {
 | |
| 		devCXCFG.reg = USB_GETREG(USB_DEVCXCFGFIFOSTATUS_REG_OFS);
 | |
| 		devCXCFG.bit.CX_STL = 1;
 | |
| 		USB_SETREG(USB_DEVCXCFGFIFOSTATUS_REG_OFS, devCXCFG.reg);
 | |
| 	} else if (ep_number <= 15) {
 | |
| 		devEPMaxPS.reg = USB_GETREG(USB_DEVMAXPS_EP1_REG_OFS + ((ep_number - 1) << 2));
 | |
| 		devEPMaxPS.bit.STL_EP = 1;
 | |
| 		USB_SETREG((USB_DEVMAXPS_EP1_REG_OFS + ((ep_number - 1) << 2)), devEPMaxPS.reg);
 | |
| 	}
 | |
| 
 | |
| 	spin_unlock_irqrestore(&nvt680->lock, flags);
 | |
| }
 | |
| 
 | |
| void usbnvt_clr_ep_stall(struct nvt680_udc *nvt680, UINT32 EPn)
 | |
| {
 | |
| 	T_USB_DEVMAXPS_EP_REG   devEPMaxPS;
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	spin_lock_irqsave(&nvt680->lock, flags);
 | |
| 	devEPMaxPS.reg = USB_GETREG(USB_DEVMAXPS_EP1_REG_OFS + ((EPn - 1) << 2));
 | |
| 	devEPMaxPS.bit.STL_EP = 0;
 | |
| 	USB_SETREG((USB_DEVMAXPS_EP1_REG_OFS + ((EPn - 1) << 2)), devEPMaxPS.reg);
 | |
| 	spin_unlock_irqrestore(&nvt680->lock, flags);
 | |
| }
 | |
| 
 | |
| void usbnvt_set_id(struct nvt680_udc *nvt680, BOOL device)
 | |
| {
 | |
| 	T_USB_TOP_REG               RegTop;
 | |
| 
 | |
| 	RegTop.reg = USB_GETREG(USB_TOP_REG_OFS);
 | |
| 	RegTop.bit.ID    = device;
 | |
| 	USB_SETREG(USB_TOP_REG_OFS, RegTop.reg);
 | |
| }
 | |
| 
 | |
| static void usbnvt_enable_highspeed(struct nvt680_udc *nvt680, BOOL highspeed)
 | |
| {
 | |
| 	T_USB_DEVMAINCTRL_REG devMainCtl;
 | |
| 
 | |
| 	devMainCtl.reg = USB_GETREG(USB_DEVMAINCTRL_REG_OFS);
 | |
| 
 | |
| 	if (highspeed) {
 | |
| 		devMainCtl.bit.FORCE_FS = 0; // bit[9]
 | |
| 	} else {
 | |
| 		devMainCtl.bit.FORCE_FS = 1; // bit[9]
 | |
| 	}
 | |
| 
 | |
| 	USB_SETREG(USB_DEVMAINCTRL_REG_OFS, devMainCtl.reg);
 | |
| }
 | |
| 
 | |
| void usbnvt_clr_fifo(struct nvt680_udc *nvt680)
 | |
| {
 | |
| 	T_USB_DEVTEST_REG devTest;
 | |
| 
 | |
| 	devTest.reg = USB_GETREG(USB_DEVTEST_REG_OFS);
 | |
| 	devTest.bit.TST_CLRFF = 1;
 | |
| 	USB_SETREG(USB_DEVTEST_REG_OFS, devTest.reg);
 | |
| }
 | |
| 
 | |
| void usbnvt_power_on_init(struct nvt680_udc *nvt680)
 | |
| {
 | |
| 	T_USB_DEVDMACTRL0_REG       devDMACtl0;
 | |
| 	T_USB_DEVDMACTRL1_REG       devDMACtl1;
 | |
| 	T_USB_HCMISC_REG            hcMiscReg;
 | |
| 
 | |
| 	itfnumsg("%s: enter\r\n", __func__);
 | |
| 
 | |
| 	//default set to device mode
 | |
| 	usbnvt_set_id(nvt680, TRUE);
 | |
| 
 | |
| 	//It is possible that loader has turn off the USB physical layer.
 | |
| 	//So we should turn on the usb physical layer.
 | |
| 	devDMACtl1.reg = USB_GETREG(USB_DEVDMACTRL1_REG_OFS);
 | |
| 	devDMACtl1.bit.DEVPHY_SUSPEND = 0;
 | |
| 	USB_SETREG(USB_DEVDMACTRL1_REG_OFS, devDMACtl1.reg);
 | |
| 
 | |
| 	usbnvt_enable_highspeed(nvt680, TRUE);
 | |
| 
 | |
| 	// Bug fix for host resume pulse
 | |
| 	hcMiscReg.reg = USB_GETREG(USB_HCMISC_REG_OFS);
 | |
| 	hcMiscReg.bit.RESM_SE0_CNT = 0x22;
 | |
| 	USB_SETREG(USB_HCMISC_REG_OFS, hcMiscReg.reg);
 | |
| 
 | |
| 	// set DMA access SDRAM
 | |
| 	devDMACtl0.reg = USB_GETREG(USB_DEVDMACTRL0_REG_OFS);
 | |
| 	devDMACtl0.bit.USB_ACCESS_SELECT = 0;
 | |
| 	USB_SETREG(USB_DEVDMACTRL0_REG_OFS, devDMACtl0.reg);
 | |
| 
 | |
| 	// Clear FIFOs
 | |
| 	usbnvt_clr_fifo(nvt680);
 | |
| 
 | |
| 	//USB Device is unattached.
 | |
| 	//Disable USB physcial layer. IT will auto re-function when USB plug-in
 | |
| 	devDMACtl1.reg = USB_GETREG(USB_DEVDMACTRL1_REG_OFS);
 | |
| 	devDMACtl1.bit.DEVPHY_SUSPEND   = 1;
 | |
| 	USB_SETREG(USB_DEVDMACTRL1_REG_OFS, devDMACtl1.reg);
 | |
| 
 | |
| 	{
 | |
| 		T_USB_DCD_COUNTER_REG   RegDcdCnt;
 | |
| 
 | |
| 		RegDcdCnt.reg   = USB_GETREG(USB_DCD_COUNTER_REG_OFS);
 | |
| 		RegDcdCnt.bit.DCD_COUNT = 2;
 | |
| 		USB_SETREG(USB_DCD_COUNTER_REG_OFS, RegDcdCnt.reg);
 | |
| 	}
 | |
| 
 | |
| 	{
 | |
| 		UINT16 data=0;
 | |
| 		INT32 result=0;
 | |
| 		UINT32 temp;
 | |
| 		UINT8 u2_trim_swctrl=6, u2_trim_sqsel=4, u2_trim_resint=8;
 | |
| 
 | |
| 
 | |
| 		#define U2PHY_SETREG(ofs,value)	writel((value), (volatile void __iomem *)(0xFD601000+((ofs)<<2)))
 | |
| 		#define U2PHY_GETREG(ofs)	readl((volatile void __iomem *)(0xFD601000+((ofs)<<2)))
 | |
| 
 | |
| 		result= efuse_readParamOps(EFUSE_USBC_TRIM_DATA, &data);
 | |
| 		if(result == 0) {
 | |
| 			u2_trim_swctrl = data&0x7;
 | |
| 			u2_trim_sqsel  = (data>>3)&0x7;
 | |
| 			u2_trim_resint = (data>>6)&0x1F;
 | |
| 		}
 | |
| 
 | |
| 		U2PHY_SETREG(0x51, 0x20);
 | |
| 		U2PHY_SETREG(0x50, 0x30);
 | |
| 
 | |
| 		temp = U2PHY_GETREG(0x06);
 | |
| 		temp &= ~(0x7<<1);
 | |
| 		temp |= (u2_trim_swctrl<<1);
 | |
| 		U2PHY_SETREG(0x06, temp);
 | |
| 
 | |
| 		temp = U2PHY_GETREG(0x05);
 | |
| 		temp &= ~(0x7<<2);
 | |
| 		temp |= (u2_trim_sqsel<<2);
 | |
| 		U2PHY_SETREG(0x05, temp);
 | |
| 
 | |
| 		U2PHY_SETREG(0x52, 0x60+u2_trim_resint);
 | |
| 		U2PHY_SETREG(0x51, 0x00);
 | |
| 
 | |
| 		writel(0x100+u2_trim_resint, (volatile unsigned long *)(0xFD600000+0x30C));
 | |
| 	}
 | |
| 
 | |
| 	itfnumsg("%s: exit\r\n", __func__);
 | |
| 
 | |
| }
 | |
| 
 | |
| void usbnvt_init_controller(struct nvt680_udc *nvt680)
 | |
| {
 | |
| 	T_USB_DEVADDR_REG           devAddr;
 | |
| 	T_USB_PHYTSTSELECT_REG      devPhyTest;
 | |
| 	T_USB_DEVIDLECNT_REG        devIdleCnt;
 | |
| 	T_USB_DEVMAINCTRL_REG       devMainCtl;
 | |
| 	T_USB_DEVTEST_REG           devTest;
 | |
| 	T_USB_GLOBALINTMASK_REG     glbIntMsk;
 | |
| 	T_USB_DEVINTMASKGROUP0_REG  devIntMskGrp0;
 | |
| 	T_USB_DEVINTMASKGROUP2_REG  devIntMskGrp2;
 | |
| 	T_USB_OTGCTRLSTATUS_REG     otgCtrlSts;
 | |
| 	T_USB_DEVDMACTRL1_REG       devDMACtl1;
 | |
| 	T_USB_DEVDMACTRL1_REG       devDMACtl1_bak;
 | |
| 	T_USB_TOP_REG               RegTop;
 | |
| 
 | |
| 	RegTop.reg = USB_GETREG(USB_TOP_REG_OFS);
 | |
| 	RegTop.bit.VBUSI = 1;
 | |
| 	USB_SETREG(USB_TOP_REG_OFS, RegTop.reg);
 | |
| 
 | |
| 	// decrease VBUS debounce time from 540us to 200us
 | |
| 	otgCtrlSts.reg = USB_GETREG(USB_OTGCTRLSTATUS_REG_OFS);
 | |
| 	otgCtrlSts.bit.Vbus_FLT_SEL = 1;
 | |
| 	USB_SETREG(USB_OTGCTRLSTATUS_REG_OFS, otgCtrlSts.reg);
 | |
| 
 | |
| 	//turn on the usb physical layer.
 | |
| 	devDMACtl1.reg = USB_GETREG(USB_DEVDMACTRL1_REG_OFS);
 | |
| 	devDMACtl1_bak.reg = devDMACtl1.reg;
 | |
| 	devDMACtl1.bit.DEVPHY_SUSPEND = 0;
 | |
| 	USB_SETREG(USB_DEVDMACTRL1_REG_OFS, devDMACtl1.reg);
 | |
| 	if (devDMACtl1_bak.bit.DEVPHY_SUSPEND == 1) {
 | |
| 		// wait VBUS debounce time passed
 | |
| 		udelay(200);
 | |
| 	}
 | |
| 
 | |
| 	//set USB Device in unconfigure state
 | |
| 	devAddr.reg          = USB_GETREG(USB_DEVADDR_REG_OFS);
 | |
| 	devAddr.bit.AFT_CONF = 0;
 | |
| 	USB_SETREG(USB_DEVADDR_REG_OFS, devAddr.reg);
 | |
| 
 | |
| 	// chip enable
 | |
| 	devMainCtl.reg = USB_GETREG(USB_DEVMAINCTRL_REG_OFS);
 | |
| 	devMainCtl.bit.CHIP_EN    = 1;
 | |
| 	devMainCtl.bit.HALF_SPEED = 0;
 | |
| 
 | |
| 	// Fix printer connect fail if test mode is enabled
 | |
| 	devMainCtl.bit.RESET_DEBOUNCE_INTERVAL = 5; // set debounce time to 5ms
 | |
| 	USB_SETREG(USB_DEVMAINCTRL_REG_OFS, devMainCtl.reg);
 | |
| 
 | |
| 	// suspend counter
 | |
| 	devIdleCnt.reg = USB_GETREG(USB_DEVIDLECNT_REG_OFS);
 | |
| 	devIdleCnt.bit.IDLE_CNT = 7;
 | |
| 	USB_SETREG(USB_DEVIDLECNT_REG_OFS, devIdleCnt.reg);
 | |
| 
 | |
| 	// clear interrupt
 | |
| 	USB_SETREG(USB_DEVINTGROUP2_REG_OFS, 0xFFFFFFFF);
 | |
| 
 | |
| 	// disable all fifo interrupt
 | |
| 	USB_SETREG(USB_DEVINTMASKGROUP1_REG_OFS, 0xFFFFFF);
 | |
| 
 | |
| 	// soft reset
 | |
| 	devMainCtl.reg = USB_GETREG(USB_DEVMAINCTRL_REG_OFS);
 | |
| 	devMainCtl.bit.SFRST = 1;
 | |
| 	USB_SETREG(USB_DEVMAINCTRL_REG_OFS, devMainCtl.reg);
 | |
| 	// soft reset clear
 | |
| 	devMainCtl.bit.SFRST = 0;
 | |
| 	USB_SETREG(USB_DEVMAINCTRL_REG_OFS, devMainCtl.reg);
 | |
| 
 | |
| 	// clear all fifo
 | |
| 	devTest.reg = USB_GETREG(USB_DEVTEST_REG_OFS);
 | |
| 	devTest.bit.TST_CLRFF = 1;
 | |
| 	USB_SETREG(USB_DEVTEST_REG_OFS, devTest.reg);
 | |
| 
 | |
| 	// disable Host & OTG interrupt
 | |
| 	glbIntMsk.reg = USB_GETREG(USB_GLOBALINTMASK_REG_OFS);
 | |
| 	glbIntMsk.bit.MOTG_INT = 1;
 | |
| 	glbIntMsk.bit.MHC_INT  = 1;
 | |
| 	USB_SETREG(USB_GLOBALINTMASK_REG_OFS, glbIntMsk.reg);
 | |
| 
 | |
| 	// maks CX_IN/CX_OUT/CX_COMEND interrupt
 | |
| 	devIntMskGrp0.reg = USB_GETREG(USB_DEVINTMASKGROUP0_REG_OFS);
 | |
| 	devIntMskGrp0.bit.MCX_IN_INT  = 1;
 | |
| 	devIntMskGrp0.bit.MCX_OUT_INT = 1;
 | |
| 	devIntMskGrp0.bit.MCX_COMEND  = 1;
 | |
| 	USB_SETREG(USB_DEVINTMASKGROUP0_REG_OFS, devIntMskGrp0.reg);
 | |
| 
 | |
| 	// mask DEV_IDLE interrupt
 | |
| 	devIntMskGrp2.reg = USB_GETREG(USB_DEVINTMASKGROUP2_REG_OFS);
 | |
| 	devIntMskGrp2.bit.MDEV_IDLE             = 1;
 | |
| 	devIntMskGrp2.bit.MDEV_WAKEUP_BYVBUS    = 1;
 | |
| 	USB_SETREG(USB_DEVINTMASKGROUP2_REG_OFS, devIntMskGrp2.reg);
 | |
| 
 | |
| 	// enable global interrupt
 | |
| 	devMainCtl.reg = USB_GETREG(USB_DEVMAINCTRL_REG_OFS);
 | |
| 	devMainCtl.bit.GLINT_EN = 1;
 | |
| 	USB_SETREG(USB_DEVMAINCTRL_REG_OFS, devMainCtl.reg);
 | |
| 
 | |
| 	devPhyTest.reg = USB_GETREG(USB_PHYTSTSELECT_REG_OFS);
 | |
| 	devPhyTest.bit.UNPLUG = 1;
 | |
| 	USB_SETREG(USB_PHYTSTSELECT_REG_OFS, devPhyTest.reg);
 | |
| }
 | |
| 
 | |
| static void usbnvt_global_interrupt_enable(struct nvt680_udc *nvt680, BOOL enable)
 | |
| {
 | |
| 	T_USB_DEVMAINCTRL_REG	reg_devmainctrl;
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	spin_lock_irqsave(&nvt680->lock, flags);
 | |
| 	reg_devmainctrl.reg = USB_GETREG(USB_DEVMAINCTRL_REG_OFS);
 | |
| 	reg_devmainctrl.bit.GLINT_EN = enable;
 | |
| 	USB_SETREG(USB_DEVMAINCTRL_REG_OFS, reg_devmainctrl.reg);
 | |
| 	spin_unlock_irqrestore(&nvt680->lock, flags);
 | |
| }
 | |
| 
 | |
| 
 | |
| static void usbnvt_intr_g2_busreset(struct nvt680_udc *nvt680)
 | |
| {
 | |
| 	T_USB_DEVINTMASKGROUP1_REG      devIntMskGrp1;
 | |
| 	T_USB_DEVTEST_REG               devTest;
 | |
| 	T_USB_DEVCXCFGFIFOSTATUS_REG    devCXCFG;
 | |
| 	T_USB_DEVMAINCTRL_REG           devMainCtl;
 | |
| 	T_USB_DEVFIFOBYTECNT0_REG       devFIFOByteCnt;
 | |
| 	UINT32                          i;
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	ep0numsg("usbintr G2: bus-reset\n");
 | |
| 
 | |
| 	spin_lock_irqsave(&nvt680->lock, flags);
 | |
| 
 | |
| 	//Set USB device Addr=0 & not in configured state
 | |
| 	USB_SETREG(USB_DEVADDR_REG_OFS, 0);
 | |
| 
 | |
| 	// disable FIFO0 IN int // TODO Review
 | |
| 	devIntMskGrp1.reg = USB_GETREG(USB_DEVINTMASKGROUP1_REG_OFS);
 | |
| 	devIntMskGrp1.bit.MF0_IN_INT = 1;  // bit[16]
 | |
| 	USB_SETREG(USB_DEVINTMASKGROUP1_REG_OFS, devIntMskGrp1.reg);
 | |
| 
 | |
| 	// Clear All FIFO
 | |
| 	devTest.reg = USB_GETREG(USB_DEVTEST_REG_OFS);
 | |
| 	devTest.bit.TST_CLRFF = 1; // bit[0]
 | |
| 	USB_SETREG(USB_DEVTEST_REG_OFS, devTest.reg);
 | |
| 
 | |
| 	for (i = 0; i < 4; i++) {
 | |
| 		devFIFOByteCnt.reg = USB_GETREG(USB_DEVFIFOBYTECNT0_REG_OFS + (i << 2));
 | |
| 		devFIFOByteCnt.bit.FFRST0 = 1;
 | |
| 		devFIFOByteCnt.bit.FFRST1 = 1;
 | |
| 		USB_SETREG(USB_DEVFIFOBYTECNT0_REG_OFS, devFIFOByteCnt.reg + (i << 2));
 | |
| 	}
 | |
| 
 | |
| 	// clear EP0 Stall
 | |
| 	devCXCFG.reg = USB_GETREG(USB_DEVCXCFGFIFOSTATUS_REG_OFS);
 | |
| 	devCXCFG.bit.CX_STL = 0; // bit[2]
 | |
| 	USB_SETREG(USB_DEVCXCFGFIFOSTATUS_REG_OFS, devCXCFG.reg);
 | |
| 
 | |
| 	// In 2.0 spec page255, the remote wakeup should be disabled after bus reset.
 | |
| 	devMainCtl.reg = USB_GETREG(USB_DEVMAINCTRL_REG_OFS);
 | |
| 	devMainCtl.bit.CAP_RMWAKUP = 0;
 | |
| 	USB_SETREG(USB_DEVMAINCTRL_REG_OFS, devMainCtl.reg);
 | |
| 
 | |
| 	spin_unlock_irqrestore(&nvt680->lock, flags);
 | |
| 
 | |
| }
 | |
| 
 | |
| static void usbnvt_intr_g2_suspend(struct nvt680_udc *nvt680)
 | |
| {
 | |
| 	T_USB_DEVFIFOBYTECNT0_REG   devFIFOByteCnt;
 | |
| 	//T_USB_DEVMAINCTRL_REG       devMainCtl;
 | |
| 	UINT32                      i;
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	ep0numsg("usbintr G2: suspend\n");
 | |
| 
 | |
| 	spin_lock_irqsave(&nvt680->lock, flags);
 | |
| 
 | |
| 	//Clear all USB FIFOs
 | |
| 	for (i = 0; i < 4; i++) {
 | |
| 		devFIFOByteCnt.reg = USB_GETREG(USB_DEVFIFOBYTECNT0_REG_OFS + (i << 2));
 | |
| 		devFIFOByteCnt.bit.FFRST0 = 1;
 | |
| 		devFIFOByteCnt.bit.FFRST1 = 1;
 | |
| 		USB_SETREG(USB_DEVFIFOBYTECNT0_REG_OFS, devFIFOByteCnt.reg + (i << 2));
 | |
| 	}
 | |
| 
 | |
| 	//*****************************************
 | |
| 	//Defualt mode to enter suspend.
 | |
| 	//devMainCtl.reg = USB_GETREG(USB_DEVMAINCTRL_REG_OFS);
 | |
| 	//devMainCtl.bit.GOSUSP = 1;
 | |
| 	//USB_SETREG(USB_DEVMAINCTRL_REG_OFS, devMainCtl.reg);
 | |
| 	//*****************************************
 | |
| 
 | |
| 	spin_unlock_irqrestore(&nvt680->lock, flags);
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| static void usbnvt_intr_g0_setup(struct nvt680_udc *nvt680)
 | |
| {
 | |
| 	struct usb_ctrlrequest ctrl;
 | |
| 	UINT32 *pControlData = (UINT32 *)(&ctrl);
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	*pControlData++ = USB_GETREG(USB_DEVDMACTRL3_REG_OFS);
 | |
| 	*pControlData   = USB_GETREG(USB_DEVDMACTRL3_REG_OFS);
 | |
| 
 | |
| 	ep0numsg("usbintr G0: CX_SETUP_INT 0x%02X 0x%02X 0x%04X 0x%04X 0x%04X\n"
 | |
| 		, ctrl.bRequestType, ctrl.bRequest, ctrl.wValue, ctrl.wIndex, ctrl.wLength);
 | |
| 
 | |
| 
 | |
| 	nvt680->ep[0]->dir_in = ctrl.bRequestType & USB_DIR_IN;
 | |
| 	nvt680->gadget.speed = USB_GETREG(USB_DEVMAINCTRL_REG_OFS) & (0x1<<6) ? USB_SPEED_HIGH : USB_SPEED_FULL;
 | |
| 
 | |
| 	/*
 | |
| 		Handle SETUP
 | |
| 	*/
 | |
| 
 | |
| 	if ((ctrl.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
 | |
| 		switch (ctrl.bRequest) {
 | |
| 		case USB_REQ_GET_STATUS:
 | |
| 			nvt680_get_status(nvt680, &ctrl);
 | |
| 			ep0numsg("USB_REQ_GET_STATUS\n");
 | |
| 			break;
 | |
| 		case USB_REQ_CLEAR_FEATURE:
 | |
| 			nvt680_clear_feature(nvt680, &ctrl);
 | |
| 			ep0numsg("USB_REQ_CLEAR_FEATURE\n");
 | |
| 			break;
 | |
| 		case USB_REQ_SET_FEATURE:
 | |
| 			nvt680_set_feature(nvt680, &ctrl);
 | |
| 			ep0numsg("USB_REQ_SET_FEATURE\n");
 | |
| 			break;
 | |
| 		case USB_REQ_SET_ADDRESS: {
 | |
| 			T_USB_DEVADDR_REG   reg_dev_addr;
 | |
| 
 | |
| 			ep0numsg("USB_REQ_SET_ADDRESS\n");
 | |
| 
 | |
| 			spin_lock_irqsave(&nvt680->lock, flags);
 | |
| 
 | |
| 			reg_dev_addr.reg = USB_GETREG(USB_DEVADDR_REG_OFS);
 | |
| 			reg_dev_addr.bit.DEVADR = ctrl.wValue & 0x7F;
 | |
| 			USB_SETREG(USB_DEVADDR_REG_OFS, reg_dev_addr.reg);
 | |
| 
 | |
| 			spin_unlock_irqrestore(&nvt680->lock, flags);
 | |
| 
 | |
| 			usbnvt_set_ep0_done(nvt680);
 | |
| 		} break;
 | |
| 
 | |
| 		case USB_REQ_SET_CONFIGURATION: {
 | |
| 			T_USB_DEVADDR_REG   devAddr;
 | |
| 
 | |
| 			spin_lock_irqsave(&nvt680->lock, flags);
 | |
| 
 | |
| 			if (ctrl.wValue == 0) {
 | |
| 				devAddr.reg = USB_GETREG(USB_DEVADDR_REG_OFS);
 | |
| 				devAddr.bit.AFT_CONF = 0;
 | |
| 				USB_SETREG(USB_DEVADDR_REG_OFS, devAddr.reg);
 | |
| 			} else {
 | |
| 				UINT32                      EPn;
 | |
| 				T_USB_DEVMAXPS_EP_REG       devEPMaxPS;
 | |
| 
 | |
| 				// Clear All EPx & FIFOx map register
 | |
| 				USB_SETREG(USB_DEVMAXPS_EP1_REG_OFS,    0x88000200);
 | |
| 				USB_SETREG(USB_DEVMAXPS_EP2_REG_OFS,    0x88000200);
 | |
| 				USB_SETREG(USB_DEVMAXPS_EP3_REG_OFS,    0x88000200);
 | |
| 				USB_SETREG(USB_DEVMAXPS_EP4_REG_OFS,    0x88000200);
 | |
| 				USB_SETREG(USB_DEVMAXPS_EP5_REG_OFS,    0x88000200);
 | |
| 				USB_SETREG(USB_DEVMAXPS_EP6_REG_OFS,    0x88000200);
 | |
| 				USB_SETREG(USB_DEVMAXPS_EP7_REG_OFS,    0x88000200);
 | |
| 				USB_SETREG(USB_DEVMAXPS_EP8_REG_OFS,    0x88000200);
 | |
| 				USB_SETREG(USB_DEVMAXPS_EP9_REG_OFS,    0x88000200);
 | |
| 				USB_SETREG(USB_DEVMAXPS_EP10_REG_OFS,   0x88000200);
 | |
| 				USB_SETREG(USB_DEVMAXPS_EP11_REG_OFS,   0x88000200);
 | |
| 				USB_SETREG(USB_DEVMAXPS_EP12_REG_OFS,   0x88000200);
 | |
| 				USB_SETREG(USB_DEVMAXPS_EP13_REG_OFS,   0x88000200);
 | |
| 				USB_SETREG(USB_DEVMAXPS_EP14_REG_OFS,   0x88000200);
 | |
| 				USB_SETREG(USB_DEVMAXPS_EP15_REG_OFS,   0x88000200);
 | |
| 
 | |
| 				USB_SETREG(USB_DEVBUFCFG0_REG_OFS,      0);
 | |
| 				USB_SETREG(USB_DEVBUFCFG1_REG_OFS,      0);
 | |
| 
 | |
| 				usbnvt_clr_fifo(nvt680);
 | |
| 
 | |
| 				for (EPn = 1; EPn <= 15; EPn++) {
 | |
| 					// Reset Toggle sequence for IN EP
 | |
| 					devEPMaxPS.reg = USB_GETREG(USB_DEVMAXPS_EP1_REG_OFS + ((EPn - 1) << 2));
 | |
| 					devEPMaxPS.bit.RSTG_EP = 1;
 | |
| 					USB_SETREG((USB_DEVMAXPS_EP1_REG_OFS + ((EPn - 1) << 2)), devEPMaxPS.reg);
 | |
| 					devEPMaxPS.bit.RSTG_EP = 0;
 | |
| 					USB_SETREG((USB_DEVMAXPS_EP1_REG_OFS + ((EPn - 1) << 2)), devEPMaxPS.reg);
 | |
| 				}
 | |
| 
 | |
| 				devAddr.reg = USB_GETREG(USB_DEVADDR_REG_OFS);
 | |
| 				devAddr.bit.AFT_CONF = 1;
 | |
| 				USB_SETREG(USB_DEVADDR_REG_OFS, devAddr.reg);
 | |
| 
 | |
| 				if (usbnvt_is_highspeed_enabled(nvt680)) {
 | |
| 					USB_SETREG(USB_DEVSOFTIMERMASK_REG_OFS, 0x44c);
 | |
| 				} else {
 | |
| 					USB_SETREG(USB_DEVSOFTIMERMASK_REG_OFS, 0x2710);
 | |
| 				}
 | |
| 
 | |
| 			}
 | |
| 
 | |
| 			spin_unlock_irqrestore(&nvt680->lock, flags);
 | |
| 
 | |
| 			// The ep enable would be invoked here
 | |
| 			if (nvt680->driver->setup(&nvt680->gadget, &ctrl) < 0) {
 | |
| 				usbnvt_set_ep_stall(nvt680, 0);
 | |
| 				ep0numsg("SET STALL\n");
 | |
| 			}
 | |
| 
 | |
| 			//else {
 | |
| 			//	Must not set done here. because gadget would send 0 byte ep0.
 | |
| 			//	The done is set induced by that 0byte request.
 | |
| 			//	usbnvt_set_ep0_done(nvt680);
 | |
| 			//}
 | |
| 
 | |
| 			ep0numsg("USB_REQ_SET_CONFIGURATION\n");
 | |
| 		} break;
 | |
| 
 | |
| 
 | |
| 		default: {
 | |
| 				if (nvt680->driver->setup(&nvt680->gadget, &ctrl) < 0) {
 | |
| 					usbnvt_set_ep_stall(nvt680, 0);
 | |
| 					ep0numsg("SET STALL\n");
 | |
| 				}
 | |
| 		}
 | |
| 		break;
 | |
| 
 | |
| 		}
 | |
| 	} else {
 | |
| 		if (nvt680->driver->setup(&nvt680->gadget, &ctrl) < 0) {
 | |
| 			usbnvt_set_ep_stall(nvt680, 0);
 | |
| 			ep0numsg("SET STALL\n");
 | |
| 		}
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 
 | |
| }
 | |
| 
 | |
| static void usbnvt_intr_g0_cx_in(struct nvt680_udc *nvt680)
 | |
| {
 | |
| 	struct nvt680_ep *ep = nvt680->ep[0];
 | |
| 
 | |
| 	ep0numsg("nvt680_ep0in\n");
 | |
| 
 | |
| 	if ((!list_empty(&ep->queue)) && (ep->dir_in)) {
 | |
| 		struct nvt680_request *req;
 | |
| 
 | |
| 		req = list_entry(ep->queue.next, struct nvt680_request, queue);
 | |
| 
 | |
| 		if (req->req.length) {
 | |
| 			nvt680_start_ep0_data(ep, req);
 | |
| 		}
 | |
| 
 | |
| 		if ((req->req.length == req->req.actual) || (req->req.actual < ep->ep.maxpacket)) {
 | |
| 
 | |
| 			nvt680_done(ep, req, 0);
 | |
| 			usbnvt_set_ep0_done(nvt680);
 | |
| 		}
 | |
| 
 | |
| 	} else {
 | |
| 		usbnvt_set_ep0_done(nvt680);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void usbnvt_intr_g0_cx_out(struct nvt680_udc *nvt680)
 | |
| {
 | |
| 	struct nvt680_ep *ep = nvt680->ep[0];
 | |
| 
 | |
| 	ep0numsg("nvt680_ep0out\n");
 | |
| 
 | |
| 	if (!list_empty(&ep->queue) && !ep->dir_in) {
 | |
| 		struct nvt680_request *req;
 | |
| 
 | |
| 		req = list_first_entry(&ep->queue, struct nvt680_request, queue);
 | |
| 
 | |
| 		if (req->req.length) {
 | |
| 			nvt680_start_ep0_data(ep, req);
 | |
| 		}
 | |
| 
 | |
| 		if ((req->req.length == req->req.actual) || (req->req.actual < ep->ep.maxpacket)) {
 | |
| 			nvt680_done(ep, req, 0);
 | |
| 			usbnvt_set_ep0_done(nvt680);
 | |
| 		}
 | |
| 
 | |
| 	} else {
 | |
| 		numsg("%s : empty queue\n", __func__);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| static void usbnvt_intr_g0_cx_abort(struct nvt680_udc *nvt680)
 | |
| {
 | |
| 	T_USB_DEVINTGROUP0_REG          devIntGrp0;
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	ep0numsg("nvt680_cx_abort\n");
 | |
| 
 | |
| 	spin_lock_irqsave(&nvt680->lock, flags);
 | |
| 
 | |
| 	devIntGrp0.reg = USB_GETREG(USB_DEVINTGROUP0_REG_OFS);
 | |
| 	devIntGrp0.bit.CX_COMABORT_INT = 0;
 | |
| 	USB_SETREG(USB_DEVINTGROUP0_REG_OFS, devIntGrp0.reg);
 | |
| 
 | |
| 	spin_unlock_irqrestore(&nvt680->lock, flags);
 | |
| }
 | |
| 
 | |
| 
 | |
| #endif
 | |
| ////////////////////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
| 
 | |
| #if 1
 | |
| static irqreturn_t nvt680_interrupt(int irq, void *_nvt680)
 | |
| {
 | |
| 	struct nvt680_udc *nvt680 = _nvt680;
 | |
| 
 | |
| 	//intrmsg("nvt680_interrupt\n");
 | |
| 
 | |
| 	usbnvt_global_interrupt_enable(nvt680, FALSE);
 | |
| 	return IRQ_WAKE_THREAD;
 | |
| }
 | |
| 
 | |
| static irqreturn_t nvt680_thread_interrupt(int irq, void *_nvt680)
 | |
| {
 | |
| 	struct nvt680_udc *nvt680 = _nvt680;
 | |
| 	T_USB_DEVINTGROUP_REG 		devIntGrp;
 | |
| 	T_USB_DEVINTGROUP0_REG		devIntGrp0;
 | |
| 	T_USB_DEVINTGROUP1_REG		devIntGrp1;
 | |
| 	T_USB_DEVINTGROUP2_REG		devIntGrp2;
 | |
| 	unsigned long flags, fifono;
 | |
| 
 | |
| 	devIntGrp.reg = USB_GETREG(USB_DEVINTGROUP_REG_OFS) & (~USB_GETREG(USB_DEVINTGROUPMASK_REG_OFS));
 | |
| 
 | |
| 	if (devIntGrp.bit.INT_G2) {
 | |
| 
 | |
| 		//USB_SETREG(USB_DEVINTMASKGROUP2_REG_OFS, (((0xF<<(4+7))+0x30000)));
 | |
| 		devIntGrp2.reg = USB_GETREG(USB_DEVINTGROUP2_REG_OFS) & ( ~USB_GETREG(USB_DEVINTMASKGROUP2_REG_OFS));
 | |
| 		USB_SETREG(USB_DEVINTGROUP2_REG_OFS, devIntGrp2.reg);
 | |
| 
 | |
| 		//intrmsg("usbintr G2: 0x%08X\n", devIntGrp2.reg);
 | |
| 
 | |
| 		if(devIntGrp2.bit.USBRST_INT) {
 | |
| 			usbnvt_intr_g2_busreset(nvt680);
 | |
| 		}
 | |
| 
 | |
| 		if(devIntGrp2.bit.SUSP_INT) {
 | |
| 			usbnvt_intr_g2_suspend(nvt680);
 | |
| 		}
 | |
| 
 | |
| 		if(devIntGrp2.bit.RESM_INT) {
 | |
| 			ep0numsg("usbintr G2: resume\n");
 | |
| 		}
 | |
| 
 | |
| 		// DMA-1
 | |
| 		for(fifono=0; fifono < 1; fifono++) {
 | |
| 			if(devIntGrp2.reg & (0x1 << (fifono+7))) {
 | |
| 				struct nvt680_ep *ep;
 | |
| 				struct nvt680_request *req;
 | |
| 				int epnum;
 | |
| 
 | |
| 				fifono = USB_GETREG(USB_DEVACCFIFO_REG_OFS);
 | |
| 				epnum = nvt680->fifo_2_ep[fifono];
 | |
| 
 | |
| 				if(epnum == 0xF) {
 | |
| 					pr_err("*************fifo_2_ep cmplt mapping_error %d 0x%08X****************\n", (int)fifono, devIntGrp2.reg);
 | |
| 				}
 | |
| 
 | |
| 				ep = nvt680->ep[epnum];
 | |
| 
 | |
| 				intrmsg("usbintr G2: DMA%d_CMPLT\n", (int)fifono);
 | |
| 
 | |
| 				spin_lock_irqsave(&nvt680->lock, flags);
 | |
| 				if(ep->d_adr && ep->d_len) {
 | |
| 					dma_unmap_single(NULL, ep->d_adr, ep->d_len, ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
 | |
| 					ep->d_adr = 0;
 | |
| 					ep->d_len = 0;
 | |
| 				}
 | |
| 				spin_unlock_irqrestore(&nvt680->lock, flags);
 | |
| 
 | |
| 				//printk("&&&& %d %d\n\n",fifono,list_empty(&ep->queue));
 | |
| 				if(!list_empty(&ep->queue)) {
 | |
| 					req = list_entry(ep->queue.next, struct nvt680_request, queue);
 | |
| 					nvt680_done(ep, req, 0);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// DMA-2~5
 | |
| 		for(fifono=4; fifono < 8; fifono++) {
 | |
| 			if(devIntGrp2.reg & (0x1 << (fifono+5))) {
 | |
| 				struct nvt680_ep *ep;
 | |
| 				struct nvt680_request *req;
 | |
| 				int epnum = nvt680->fifo_2_ep[fifono];
 | |
| 
 | |
| 				if(epnum == 0xF) {
 | |
| 					pr_err("*************fifo_2_ep cmplt mapping_error %d 0x%08X****************\n", (int)fifono, devIntGrp2.reg);
 | |
| 				}
 | |
| 
 | |
| 				ep = nvt680->ep[epnum];
 | |
| 
 | |
| 				intrmsg("usbintr G2: DMA%d_CMPLT\n", (int)fifono-2);
 | |
| 
 | |
| 				spin_lock_irqsave(&nvt680->lock, flags);
 | |
| 				if(ep->d_adr && ep->d_len) {
 | |
| 					dma_unmap_single(NULL, ep->d_adr, ep->d_len, ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
 | |
| 					ep->d_adr = 0;
 | |
| 					ep->d_len = 0;
 | |
| 				}
 | |
| 				spin_unlock_irqrestore(&nvt680->lock, flags);
 | |
| 
 | |
| 				//printk("&&&& %d %d\n\n",fifono,list_empty(&ep->queue));
 | |
| 				if(!list_empty(&ep->queue)) {
 | |
| 					req = list_entry(ep->queue.next, struct nvt680_request, queue);
 | |
| 					nvt680_done(ep, req, 0);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		if(devIntGrp2.bit.SEQ_ERR_INT) {
 | |
| 			intrmsg("usbintr G2: SEQ_ERR_INT\n");
 | |
| 		}
 | |
| 
 | |
| 		if(devIntGrp2.bit.SEQ_ABORT_INT) {
 | |
| 			intrmsg("usbintr G2: SEQ_ABORT_INT\n");
 | |
| 		}
 | |
| 
 | |
| 		if(devIntGrp2.bit.TX0BYTE_INT) {
 | |
| 			intrmsg("usbintr G2: TX0BYTE_INT\n");
 | |
| 			USB_SETREG(USB_DEVTX0BYTE_REG_OFS, 0);
 | |
| 		}
 | |
| 
 | |
| 		if(devIntGrp2.bit.RX0BYTE_INT) {
 | |
| 			intrmsg("usbintr G2: RX0BYTE_INT\n");
 | |
| 			USB_SETREG(USB_DEVRX0BYTE_REG_OFS, 0);
 | |
| 		}
 | |
| 
 | |
| 		if(devIntGrp2.bit.DEV_IDLE) {
 | |
| 			intrmsg("usbintr G2: DEV_IDLE\n");
 | |
| 		}
 | |
| 		if(devIntGrp2.bit.DEV_WAKEUP_BYVBUS) {
 | |
| 			intrmsg("usbintr G2: DEV_WAKEUP_BYVBUS\n");
 | |
| 		}
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	if (devIntGrp.bit.INT_G0) {
 | |
| 		devIntGrp0.reg = USB_GETREG(USB_DEVINTGROUP0_REG_OFS) & ( ~ USB_GETREG(USB_DEVINTMASKGROUP0_REG_OFS));
 | |
| 		ep0numsg("usbintr G0: 0x%08X\n", devIntGrp0.reg);
 | |
| 
 | |
| 		if(devIntGrp0.bit.CX_SETUP_INT) {
 | |
| 			usbnvt_intr_g0_setup(nvt680);
 | |
| 		}
 | |
| 
 | |
| 		if(devIntGrp0.bit.CX_IN_INT) {
 | |
| 			usbnvt_intr_g0_cx_in(nvt680);
 | |
| 		}
 | |
| 
 | |
| 		if(devIntGrp0.bit.CX_OUT_INT) {
 | |
| 			usbnvt_intr_g0_cx_out(nvt680);
 | |
| 		}
 | |
| 
 | |
| 		if(devIntGrp0.bit.CX_COMEND) {
 | |
| 			printk("CX_COMEND ??\n");
 | |
| 		}
 | |
| 
 | |
| 		if(devIntGrp0.bit.CX_COMFAIL_INT) {
 | |
| 
 | |
| 			spin_lock_irqsave(&nvt680->lock, flags);
 | |
| 			devIntGrp0.reg = USB_GETREG(USB_DEVINTGROUP0_REG_OFS);
 | |
| 			devIntGrp0.bit.CX_COMFAIL_INT = 0;
 | |
| 			USB_SETREG(USB_DEVINTGROUP0_REG_OFS, devIntGrp0.reg);
 | |
| 			spin_unlock_irqrestore(&nvt680->lock, flags);
 | |
| 
 | |
| 			usbnvt_set_ep_stall(nvt680, 0);
 | |
| 		}
 | |
| 
 | |
| 		if(devIntGrp0.bit.CX_COMABORT_INT) {
 | |
| 			usbnvt_intr_g0_cx_abort(nvt680);
 | |
| 		}
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	if (devIntGrp.bit.INT_G1) {
 | |
| 		devIntGrp1.reg = USB_GETREG(USB_DEVINTGROUP1_REG_OFS) & ( ~ USB_GETREG(USB_DEVINTMASKGROUP1_REG_OFS));
 | |
| 		intrmsg("usbintr G1: 0x%08X\n", devIntGrp1.reg);
 | |
| 
 | |
| 
 | |
| 		/* fifo out */
 | |
| 		for(fifono = 0; fifono < 8; fifono++) {
 | |
| 
 | |
| 			// (devIntGrp1.bit.F0_OUT_INT || devIntGrp1.bit.F0_SPK_INT)
 | |
| 			if(devIntGrp1.reg & (0x3 << (fifono<<1))) {
 | |
| 				struct nvt680_ep *ep;
 | |
| 				struct nvt680_request *req;
 | |
| 				dma_addr_t d_adr;
 | |
| 				UINT32 d_len, epnum = nvt680->fifo_2_ep[fifono];
 | |
| 				T_USB_DEVFIFOBYTECNT0_REG	reg_devfifo_bc;
 | |
| 
 | |
| 				if(epnum == 0xF) {
 | |
| 					pr_err("fifo_2_ep out mapping_error %d\n", (int)fifono);
 | |
| 				}
 | |
| 
 | |
| 				ep = nvt680->ep[epnum];
 | |
| 				req = list_entry(ep->queue.next, struct nvt680_request, queue);
 | |
| 
 | |
| 				//reg_devfifo_bc.reg = USB_GETREG(USB_DEVFIFOBYTECNT0_REG_OFS+((epnum-1)<<2));
 | |
| 				//d_len = reg_devfifo_bc.bit.BC_F0;
 | |
| 				reg_devfifo_bc.reg = USB_GETREG(USB_DEVFIFOBYTECNT0_REG_OFS+((nvt680->ep_2_fifo[epnum]>>1)<<2));
 | |
| 				d_len = (reg_devfifo_bc.reg >> ((nvt680->ep_2_fifo[epnum]&0x1)<<4)) & 0x7FF;
 | |
| 
 | |
| 				if(!(d_len & 511)) {
 | |
| 					d_len = req->req.length;
 | |
| 				}
 | |
| 
 | |
| 				d_adr = dma_map_single(NULL, (u8 *)(req->req.buf), d_len, ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
 | |
| 
 | |
| 				if (dma_mapping_error(NULL, d_adr)) {
 | |
| 					pr_err("dma_mapping_error\n");
 | |
| 				}
 | |
| 
 | |
| 				dma_sync_single_for_device(NULL, d_adr, d_len, ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
 | |
| 
 | |
| 				req->req.actual = d_len;
 | |
| 				ep->d_adr = d_adr;
 | |
| 				ep->d_len = d_len;
 | |
| 
 | |
| 				usbnvt_set_ep_read(nvt680, epnum, d_adr, d_len);
 | |
| 
 | |
| 				//nvt680_stop_ep_transfer(ep);
 | |
| 				usbnvt_mask_ep_interrupt(nvt680, epnum);
 | |
| 
 | |
| 			}
 | |
| 
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		/* fifo in */
 | |
| 		for(fifono = 0; fifono < 8; fifono++) {
 | |
| 
 | |
| 			if(devIntGrp1.reg & (0x1 << (fifono+16))) {
 | |
| 				struct nvt680_ep *ep;
 | |
| 				struct nvt680_request *req;
 | |
| 				dma_addr_t d_adr;
 | |
| 				UINT32 d_len, epnum = nvt680->fifo_2_ep[fifono];
 | |
| 
 | |
| 				if(epnum == 0xF) {
 | |
| 					pr_err("fifo_2_ep in mapping_error %d\n", (int)fifono);
 | |
| 				}
 | |
| 
 | |
| 				ep = nvt680->ep[epnum];
 | |
| 				req = list_entry(ep->queue.next, struct nvt680_request, queue);
 | |
| 
 | |
| 				if ((req->req.length == 0) && (req->req.actual == 0)) {
 | |
| 
 | |
| 					T_USB_DEVMAXPS_EP_REG DevEPMaxPS;
 | |
| 
 | |
| 					spin_lock_irqsave(&nvt680->lock, flags);
 | |
| 					DevEPMaxPS.reg = USB_GETREG(USB_DEVMAXPS_EP1_REG_OFS + ((epnum - 1) << 2));
 | |
| 					DevEPMaxPS.bit.TX0BYTE_IEP = 1;
 | |
| 					USB_SETREG((USB_DEVMAXPS_EP1_REG_OFS + ((epnum - 1) << 2)), DevEPMaxPS.reg);
 | |
| 					spin_unlock_irqrestore(&nvt680->lock, flags);
 | |
| 
 | |
| 					do {
 | |
| 						DevEPMaxPS.reg = USB_GETREG(USB_DEVMAXPS_EP1_REG_OFS + ((epnum - 1) << 2));
 | |
| 						if(!DevEPMaxPS.bit.TX0BYTE_IEP) {
 | |
| 							break;
 | |
| 						}
 | |
| 						msleep(1);
 | |
| 					}while(DevEPMaxPS.bit.TX0BYTE_IEP);
 | |
| 
 | |
| 					nvt680_done(ep, req, 0);
 | |
| 				} else {
 | |
| 					d_len = req->req.length;
 | |
| 					d_adr = dma_map_single(NULL, (u8 *)(req->req.buf), d_len, ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
 | |
| 
 | |
| 					if (dma_mapping_error(NULL, d_adr)) {
 | |
| 						pr_err("dma_mapping_error\n");
 | |
| 					}
 | |
| 
 | |
| 					dma_sync_single_for_device(NULL, d_adr, d_len, ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
 | |
| 
 | |
| 					req->req.actual = d_len;
 | |
| 					ep->d_adr = d_adr;
 | |
| 					ep->d_len = d_len;
 | |
| 
 | |
| 					usbnvt_set_ep_write(nvt680, epnum, d_adr, d_len);
 | |
| 
 | |
| 					//nvt680_stop_ep_transfer(ep);
 | |
| 					usbnvt_mask_ep_interrupt(nvt680, epnum);
 | |
| 				}
 | |
| 
 | |
| 			}
 | |
| 
 | |
| 		}
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	usbnvt_global_interrupt_enable(nvt680, TRUE);
 | |
| 	return IRQ_HANDLED;
 | |
| }
 | |
| 
 | |
| #endif
 | |
| 
 | |
| static int nvt680_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
 | |
| {
 | |
| 	struct nvt680_ep *ep;
 | |
| 	#define QH_ISO_MULT(x)		((x >> 11) & 0x03)
 | |
| 
 | |
| 	ep = container_of(_ep, struct nvt680_ep, ep);
 | |
| 
 | |
| 	ep->desc = desc;
 | |
| 	ep->epnum = usb_endpoint_num(desc);
 | |
| 	ep->type = usb_endpoint_type(desc);
 | |
| 	ep->dir_in = usb_endpoint_dir_in(desc);
 | |
| 	ep->ep.maxpacket = usb_endpoint_maxp(desc)& 0x07ff;
 | |
| 	ep->ep.mult = QH_ISO_MULT(usb_endpoint_maxp(desc));
 | |
| 
 | |
| 	itfnumsg("fotg200_ep_enable: ep-desc len=0x%X type=0x%X epaddr=0x%X attr=0x%X MaxPkt=0x%X intval=0x%X mult=%d\n"
 | |
| 		,desc->bLength,desc->bDescriptorType ,desc->bEndpointAddress,desc->bmAttributes,desc->wMaxPacketSize,desc->bInterval, ep->ep.mult);
 | |
| 	//printk("fotg200_ep_enable: ep-desc len=0x%X type=0x%X epaddr=0x%X attr=0x%X MaxPkt=0x%X intval=0x%X mult=%d\n"
 | |
| 	//	,desc->bLength,desc->bDescriptorType ,desc->bEndpointAddress,desc->bmAttributes,desc->wMaxPacketSize,desc->bInterval, ep->ep.mult);
 | |
| 
 | |
| 	return nvt680_config_ep(ep, desc);
 | |
| }
 | |
| 
 | |
| static int nvt680_ep_disable(struct usb_ep *_ep)
 | |
| {
 | |
| 	struct nvt680_ep *ep;
 | |
| 	struct nvt680_request *req;
 | |
| 
 | |
| 	BUG_ON(!_ep);
 | |
| 
 | |
| 	itfnumsg("nvt680_ep_disable\n");
 | |
| 
 | |
| 	ep = container_of(_ep, struct nvt680_ep, ep);
 | |
| 	//printk("nvt680_ep_disable %d\n",ep->epnum);
 | |
| 
 | |
| 	while (!list_empty(&ep->queue)) {
 | |
| 		req = list_entry(ep->queue.next, struct nvt680_request, queue);
 | |
| 		nvt680_done(ep, req, -ESHUTDOWN);
 | |
| 	}
 | |
| 
 | |
| 	return nvt680_ep_release(ep);
 | |
| }
 | |
| 
 | |
| static struct usb_request *nvt680_ep_alloc_request(struct usb_ep *_ep,
 | |
| 						gfp_t gfp_flags)
 | |
| {
 | |
| 	struct nvt680_request *req;
 | |
| 
 | |
| 	itfnumsg("nvt680_ep_alloc_request\n");
 | |
| 
 | |
| 	req = kzalloc(sizeof(struct nvt680_request), gfp_flags);
 | |
| 	if (!req)
 | |
| 		return NULL;
 | |
| 
 | |
| 	INIT_LIST_HEAD(&req->queue);
 | |
| 
 | |
| 	return &req->req;
 | |
| }
 | |
| 
 | |
| static void nvt680_ep_free_request(struct usb_ep *_ep,
 | |
| 					struct usb_request *_req)
 | |
| {
 | |
| 	struct nvt680_request *req;
 | |
| 
 | |
| 	itfnumsg("nvt680_ep_free_request\n");
 | |
| 
 | |
| 	req = container_of(_req, struct nvt680_request, req);
 | |
| 	kfree(req);
 | |
| 
 | |
| }
 | |
| 
 | |
| static int nvt680_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
 | |
| {
 | |
| 	struct nvt680_ep *ep;
 | |
| 	struct nvt680_request *req;
 | |
| 	unsigned long flags;
 | |
| 	int empty_list = 0;
 | |
| 
 | |
| 	ep = container_of(_ep, struct nvt680_ep, ep);
 | |
| 	req = container_of(_req, struct nvt680_request, req);
 | |
| 
 | |
| 	if(ep->epnum) {
 | |
| 		itfnumsg("nvt680_ep_queue %d\n", ep->epnum);
 | |
| 	} else {
 | |
| 		ep0numsg("nvt680_ep0_queue %d\n", ep->epnum);
 | |
| 	}
 | |
| 
 | |
| 	if (ep->nvt680->gadget.speed == USB_SPEED_UNKNOWN) {
 | |
| 		printk("unknown speed\n");
 | |
| 		return -ESHUTDOWN;
 | |
| 	}
 | |
| 
 | |
| 	spin_lock_irqsave(&ep->nvt680->lock, flags);
 | |
| 
 | |
| 	empty_list = list_empty(&ep->queue);
 | |
| 
 | |
| 	list_add_tail(&req->queue, &ep->queue);
 | |
| 
 | |
| 	req->req.actual = 0;
 | |
| 	req->req.status = -EINPROGRESS;
 | |
| 
 | |
| 	spin_unlock_irqrestore(&ep->nvt680->lock, flags);
 | |
| 
 | |
| 	if (!ep->epnum) {
 | |
| 		/* ep0 */
 | |
| 		nvt680_ep0_queue(ep, req);
 | |
| 	} else if (empty_list) {
 | |
| 		nvt680_enable_ep_transfer(ep);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int nvt680_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
 | |
| {
 | |
| 	struct nvt680_ep *ep;
 | |
| 	struct nvt680_request *req;
 | |
| 
 | |
| 	itfnumsg("nvt680_ep_dequeue\n");
 | |
| 
 | |
| 	ep = container_of(_ep, struct nvt680_ep, ep);
 | |
| 	req = container_of(_req, struct nvt680_request, req);
 | |
| 
 | |
| 	if (!list_empty(&ep->queue)) {
 | |
| 		nvt680_done(ep, req, -ECONNRESET);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int nvt680_ep_set_halt(struct usb_ep *_ep, int value)
 | |
| {
 | |
| 	itfnumsg("nvt680_ep_set_halt\n");
 | |
| 	return nvt680_set_halt_and_wedge(_ep, value, 0);
 | |
| }
 | |
| 
 | |
| static int nvt680_ep_set_wedge(struct usb_ep *_ep)
 | |
| {
 | |
| 	itfnumsg("nvt680_ep_set_wedge\n");
 | |
| 	return nvt680_set_halt_and_wedge(_ep, 1, 1);
 | |
| }
 | |
| 
 | |
| static void nvt680_ep_fifo_flush(struct usb_ep *_ep)
 | |
| {
 | |
| 	itfnumsg("nvt680_ep_fifo_flush\n");
 | |
| }
 | |
| 
 | |
| static int nvt680_fifo_status(struct usb_ep *ep)
 | |
| {
 | |
| 	itfnumsg("nvt680_fifo_status\n");
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static struct usb_ep_ops nvt680_ep_ops = {
 | |
| 	.enable		= nvt680_ep_enable,
 | |
| 	.disable	= nvt680_ep_disable,
 | |
| 
 | |
| 	.alloc_request	= nvt680_ep_alloc_request,
 | |
| 	.free_request	= nvt680_ep_free_request,
 | |
| 
 | |
| 	.queue		= nvt680_ep_queue,
 | |
| 	.dequeue	= nvt680_ep_dequeue,
 | |
| 
 | |
| 	.set_halt	= nvt680_ep_set_halt,
 | |
| 	.fifo_flush	= nvt680_ep_fifo_flush,
 | |
| 	.set_wedge	= nvt680_ep_set_wedge,
 | |
| 	.fifo_status= nvt680_fifo_status,
 | |
| };
 | |
| 
 | |
| static int nvt680_udc_start(struct usb_gadget *g,
 | |
| 		struct usb_gadget_driver *driver)
 | |
| {
 | |
| 	struct nvt680_udc *nvt680 = gadget_to_nvt680(g);
 | |
| 	int i, ret = 0;
 | |
| 
 | |
| 	itfnumsg("nvt680_udc_start %d\n", nvt680->irq_no);
 | |
| 
 | |
| 	usbnvt_power_on_init(nvt680);
 | |
| 	usbnvt_init_controller(nvt680);
 | |
| 
 | |
| 	for (i = 0; i < NVT680_MAX_NUM_EP; i++) {
 | |
| 		nvt680->ep_2_fifo[i]=0xF;
 | |
| 	}
 | |
| 
 | |
| 	for (i = 0; i < 8; i++) {
 | |
| 		nvt680->fifo_2_ep[i]=0xF;
 | |
| 	}
 | |
| 
 | |
| 	nvt680->fifo_vld_msk = 0xff;
 | |
| 	nvt680->fifo_vld_idx = 0;
 | |
| 
 | |
| #if 1
 | |
| 	ret = request_threaded_irq(nvt680->irq_no, nvt680_interrupt, nvt680_thread_interrupt,
 | |
| 			IRQF_SHARED, udc_name, nvt680);
 | |
| 
 | |
| 	if (ret < 0) {
 | |
| 		pr_err("request_irq error (%d)\n", ret);
 | |
| 		goto err_irq;
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| 	/* hook up the driver */
 | |
| 	driver->driver.bus = NULL;
 | |
| 	nvt680->driver = driver;
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
| err_irq:
 | |
| 	free_irq(nvt680->irq_no, nvt680);
 | |
| 
 | |
| 	return ret;
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| static int nvt680_udc_stop(struct usb_gadget *g)
 | |
| {
 | |
| 	struct nvt680_udc *nvt680 = gadget_to_nvt680(g);
 | |
| 	unsigned long	flags;
 | |
| 	u32 value;
 | |
| 
 | |
| 	itfnumsg("nvt680_udc_stop\n");
 | |
| 
 | |
| 	spin_lock_irqsave(&nvt680->lock, flags);
 | |
| 
 | |
| 	//fotg210_init(fotg210);
 | |
| 	nvt680->driver = NULL;
 | |
| 	//nvt680_enable_unplugsuspend(nvt680);
 | |
| 
 | |
| 	/* enable device global interrupt */
 | |
| 	value = ioread32((void __iomem *)0xFD0200A4);
 | |
| 	value &= ~(0x1<<19);
 | |
| 	iowrite32(value, (void __iomem *)0xFD0200A4);
 | |
| 	value |=  (0x1<<19);
 | |
| 	iowrite32(value, (void __iomem *)0xFD0200A4);
 | |
| 	spin_unlock_irqrestore(&nvt680->lock, flags);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * nvt680_udc_pullup - Enable/disable pullup on D+ line.
 | |
|  * @gadget: USB slave device.
 | |
|  * @is_on: 0 to disable pullup, 1 to enable.
 | |
|  *
 | |
|  * See notes in bcm63xx_select_pullup().
 | |
|  */
 | |
| static int nvt680_udc_pullup(struct usb_gadget *gadget, int is_on)
 | |
| {
 | |
| 	T_USB_PHYTSTSELECT_REG      devPhyTest;
 | |
| 	struct nvt680_udc *nvt680 = gadget_to_nvt680(gadget);
 | |
| 
 | |
| 	itfnumsg("nvt680_udc_pullup %d\n", is_on);
 | |
| 
 | |
| 	devPhyTest.reg = USB_GETREG(USB_PHYTSTSELECT_REG_OFS);
 | |
| 	devPhyTest.bit.UNPLUG = !is_on;
 | |
| 	USB_SETREG(USB_PHYTSTSELECT_REG_OFS, devPhyTest.reg);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int nvt680_udc_get_frame(struct usb_gadget *g)
 | |
| {
 | |
| 	itfnumsg("nvt680_udc_get_frame\n");
 | |
| 	return 0;// return 11 bits SOF number
 | |
| }
 | |
| 
 | |
| static struct usb_gadget_ops nvt680_gadget_ops = {
 | |
| 	.get_frame		= nvt680_udc_get_frame,
 | |
| 	.udc_start		= nvt680_udc_start,
 | |
| 	.udc_stop		= nvt680_udc_stop,
 | |
| 	.pullup			= nvt680_udc_pullup,
 | |
| };
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| static int nvt680_udc_remove(struct platform_device *pdev)
 | |
| {
 | |
| 	struct nvt680_udc *nvt680 = platform_get_drvdata(pdev);
 | |
| 
 | |
| 	itfnumsg("nvt680_udc_remove\n");
 | |
| 
 | |
| 	usb_del_gadget_udc(&nvt680->gadget);
 | |
| 	free_irq(platform_get_irq(pdev, 0), nvt680);
 | |
| 
 | |
| 	nvt680_ep_free_request(&nvt680->ep[0]->ep, nvt680->ep0_req);
 | |
| 	iounmap(nvt680->reg);
 | |
| 	kfree(nvt680);
 | |
| 
 | |
| 	{
 | |
| 		struct resource *res;
 | |
| 		struct clk *source_clk;
 | |
| 
 | |
| 		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 | |
| 		release_mem_region(res->start, resource_size(res));
 | |
| 
 | |
| 		source_clk = clk_get(&pdev->dev, "f0600000.usb20");
 | |
| 		if (IS_ERR(source_clk)) {
 | |
| 			printk("faile to get clock f0600000.usb20\n");
 | |
| 		} else {
 | |
| 			/* toggle reset */
 | |
| 			clk_prepare(source_clk);
 | |
| 			clk_unprepare(source_clk);
 | |
| 			clk_put(source_clk);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int nvt680_udc_probe(struct platform_device *pdev)
 | |
| {
 | |
| 	struct resource *res, *ires;
 | |
| 	struct nvt680_udc *nvt680 = NULL;
 | |
| 	struct nvt680_ep *_ep[NVT680_MAX_NUM_EP];
 | |
| 	int ret = 0;
 | |
| 	int i;
 | |
| 
 | |
| 	itfnumsg("nvt680_udc_probe\n");
 | |
| 
 | |
| 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 | |
| 	if (!res) {
 | |
| 		pr_err("platform_get_resource error.\n");
 | |
| 		return -ENODEV;
 | |
| 	}
 | |
| 
 | |
| 	ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
 | |
| 	if (!ires) {
 | |
| 		pr_err("platform_get_resource IORESOURCE_IRQ error.\n");
 | |
| 		return -ENODEV;
 | |
| 	}
 | |
| 
 | |
| 	ret = -ENOMEM;
 | |
| 
 | |
| 	/* initialize nvt680_udc data struct */
 | |
| 	nvt680 = kzalloc(sizeof(struct nvt680_udc), GFP_KERNEL);
 | |
| 	if (nvt680 == NULL) {
 | |
| 		pr_err("kzalloc error\n");
 | |
| 		goto err_alloc;
 | |
| 	}
 | |
| 
 | |
| 	for (i = 0; i < NVT680_MAX_NUM_EP; i++) {
 | |
| 		_ep[i] = kzalloc(sizeof(struct nvt680_ep), GFP_KERNEL);
 | |
| 		if (_ep[i] == NULL) {
 | |
| 			pr_err("_ep kzalloc error\n");
 | |
| 			goto err_alloc;
 | |
| 		}
 | |
| 		nvt680->ep[i] = _ep[i];
 | |
| 
 | |
| 		nvt680->ep_2_fifo[i]=0xF;
 | |
| 	}
 | |
| 
 | |
| 	for (i = 0; i < 8; i++) {
 | |
| 		nvt680->fifo_2_ep[i]=0xF;
 | |
| 	}
 | |
| 
 | |
| 	if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
 | |
| 		pr_err("failed to request memory resource %s\n", pdev->name);
 | |
| 		goto err_alloc;
 | |
| 	}
 | |
| 
 | |
| 	nvt680->reg = ioremap_nocache(res->start, resource_size(res));
 | |
| 	if (nvt680->reg == NULL) {
 | |
| 		pr_err("ioremap error.\n");
 | |
| 		goto err_map;
 | |
| 	}
 | |
| 
 | |
| 	spin_lock_init(&nvt680->lock);
 | |
| 
 | |
| 	platform_set_drvdata(pdev, nvt680);
 | |
| 
 | |
| 	nvt680->gadget.ops = &nvt680_gadget_ops;
 | |
| 
 | |
| 	nvt680->gadget.max_speed = USB_SPEED_HIGH;
 | |
| 	nvt680->gadget.dev.parent = &pdev->dev;
 | |
| 	nvt680->gadget.dev.dma_mask = pdev->dev.dma_mask;
 | |
| 	nvt680->gadget.name = udc_name;
 | |
| 
 | |
| 	INIT_LIST_HEAD(&nvt680->gadget.ep_list);
 | |
| 
 | |
| 	for (i = 0; i < NVT680_MAX_NUM_EP; i++) {
 | |
| 		struct nvt680_ep *ep = nvt680->ep[i];
 | |
| 
 | |
| 		if (i) {
 | |
| 			INIT_LIST_HEAD(&nvt680->ep[i]->ep.ep_list);
 | |
| 			list_add_tail(&nvt680->ep[i]->ep.ep_list,
 | |
| 				      &nvt680->gadget.ep_list);
 | |
| 		}
 | |
| 		ep->nvt680 = nvt680;
 | |
| 		INIT_LIST_HEAD(&ep->queue);
 | |
| 		//ep->ep.name = nvt680_ep_name[i];
 | |
| 		ep->ep.name = ep_info[i].name;
 | |
| 		ep->ep.caps = ep_info[i].caps;
 | |
| 		ep->ep.ops = &nvt680_ep_ops;
 | |
| 		usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0);
 | |
| 	}
 | |
| 	usb_ep_set_maxpacket_limit(&nvt680->ep[0]->ep, 0x40);
 | |
| 	nvt680->gadget.ep0 = &nvt680->ep[0]->ep;
 | |
| 	INIT_LIST_HEAD(&nvt680->gadget.ep0->ep_list);
 | |
| 
 | |
| 	nvt680->ep0_req = nvt680_ep_alloc_request(&nvt680->ep[0]->ep,
 | |
| 				GFP_KERNEL);
 | |
| 
 | |
| 	if (nvt680->ep0_req == NULL)
 | |
| 		goto err_req;
 | |
| 
 | |
| 	usbnvt_power_on_init(nvt680);
 | |
| 
 | |
| 	nvt680->irq_no = ires->start;
 | |
| 
 | |
| 	ret = usb_add_gadget_udc(&pdev->dev, &nvt680->gadget);
 | |
| 	if (ret)
 | |
| 		goto err_add_udc;
 | |
| 
 | |
| 	dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION);
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
| err_add_udc:
 | |
| err_req:
 | |
| 	nvt680_ep_free_request(&nvt680->ep[0]->ep, nvt680->ep0_req);
 | |
| 
 | |
| err_map:
 | |
| 	if (nvt680->reg)
 | |
| 		iounmap(nvt680->reg);
 | |
| 
 | |
| err_alloc:
 | |
| 	kfree(nvt680);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| 
 | |
| #ifdef CONFIG_OF
 | |
| static const struct of_device_id of_nvt680_match[] = {
 | |
| 	{
 | |
| 		.compatible = "nvt,fotg200_udc"
 | |
| 	},
 | |
| 
 | |
| 	{ },
 | |
| };
 | |
| MODULE_DEVICE_TABLE(of, of_nvt680_match);
 | |
| #endif
 | |
| 
 | |
| 
 | |
| static struct platform_driver nvt680_driver = {
 | |
| 	.driver		= {
 | |
| 		.name =	(char *)udc_name,
 | |
| #ifdef CONFIG_OF
 | |
| 		.of_match_table = of_match_ptr(of_nvt680_match),
 | |
| #endif
 | |
| 	},
 | |
| 	.probe		= nvt680_udc_probe,
 | |
| 	.remove		= nvt680_udc_remove,
 | |
| 
 | |
| };
 | |
| 
 | |
| module_platform_driver(nvt680_driver);
 | |
| 
 | |
| MODULE_AUTHOR("Klins Chen <klins_chen@novatek.com.tw>");
 | |
| MODULE_LICENSE("GPL");
 | |
| MODULE_VERSION("1.01.001");
 | |
| MODULE_DESCRIPTION(DRIVER_DESC);
 | 
