⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dwc_otg_pcd_intr.c

📁 host usb 主设备程序 支持sd卡 mouse keyboard 的最单单的驱动程序 gcc编译
💻 C
📖 第 1 页 / 共 5 页
字号:
/* ========================================================================== * * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless * otherwise expressly agreed to in writing between Synopsys and you. * * The Software IS NOT an item of Licensed Software or Licensed Product under * any End User Software License Agreement or Agreement for Licensed Product * with Synopsys or any supplement thereto. You are permitted to use and * redistribute this Software in source and binary forms, with or without * modification, provided that redistributions of source code must retain this * notice. You may not view, use, disclose, copy or distribute this file or * any information contained herein except pursuant to this license grant from * Synopsys. If you do not agree with this notice, including the disclaimer * below, then you are not authorized to use the Software. * * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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. * ========================================================================== *///#ifndef DWC_HOST_ONLY#if 0#include <linux/interrupt.h>#include <linux/dma-mapping.h>#include <linux/version.h>#include "dwc_otg_driver.h"#include "dwc_otg_pcd.h"#define DEBUG_EP0/* request functions defined in "dwc_otg_pcd.c" */extern void request_done( dwc_otg_pcd_ep_t *_ep, dwc_otg_pcd_request_t *_req,								int _status);extern void request_nuke( dwc_otg_pcd_ep_t *_ep );extern void dwc_otg_pcd_update_otg( dwc_otg_pcd_t *_pcd,								const unsigned _reset );/** @file * This file contains the implementation of the PCD Interrupt handlers. * * The PCD handles the device interrupts.  Many conditions can cause a * device interrupt. When an interrupt occurs, the device interrupt * service routine determines the cause of the interrupt and * dispatches handling to the appropriate function. These interrupt * handling functions are described below. * All interrupt registers are processed from LSB to MSB. *//** * This function prints the ep0 state for debug purposes. */static inline void print_ep0_state( dwc_otg_pcd_t *_pcd ){#ifdef DEBUG	char str[40];	switch (_pcd->ep0state)	{	case EP0_DISCONNECT:		strcpy(str, "EP0_DISCONNECT");		break;	case EP0_IDLE:		strcpy(str, "EP0_IDLE");		break;	case EP0_IN_DATA_PHASE:		strcpy(str, "EP0_IN_DATA_PHASE");		break;	case EP0_OUT_DATA_PHASE:		strcpy(str, "EP0_OUT_DATA_PHASE");		break;	case EP0_IN_STATUS_PHASE:		strcpy(str,"EP0_IN_STATUS_PHASE");		break;	case EP0_OUT_STATUS_PHASE:		strcpy(str,"EP0_OUT_STATUS_PHASE");		break;	case EP0_STALL:		strcpy(str,"EP0_STALL");		break;	default:		strcpy(str,"EP0_INVALID");	}	DWC_DEBUGPL(DBG_ANY, "%s(%d)\n", str, _pcd->ep0state);#endif}/** * This function returns pointer to in ep struct with number ep_num */static inline dwc_otg_pcd_ep_t* get_in_ep( dwc_otg_pcd_t *_pcd, uint32_t ep_num){	int i;	int num_in_eps = GET_CORE_IF(_pcd)->dev_if->num_in_eps;	if(ep_num == 0)	{		return &_pcd->ep0;	}	else	{		for(i = 0; i < num_in_eps; ++i)		{			if(_pcd->in_ep[i].dwc_ep.num == ep_num)				return &_pcd->in_ep[i];		}		return 0;	}}/** * This function returns pointer to out ep struct with number ep_num */static inline dwc_otg_pcd_ep_t* get_out_ep( dwc_otg_pcd_t *_pcd, uint32_t ep_num){	int i;	int num_out_eps = GET_CORE_IF(_pcd)->dev_if->num_out_eps;	if(ep_num == 0)	{		return &_pcd->ep0;	}	else	{		for(i = 0; i < num_out_eps; ++i)		{			if(_pcd->out_ep[i].dwc_ep.num == ep_num)				return &_pcd->out_ep[i];		}		return 0;	}}/** * This functions gets a pointer to an EP from the wIndex address * value of the control request. */static dwc_otg_pcd_ep_t *get_ep_by_addr (dwc_otg_pcd_t *_pcd, u16 _wIndex){	dwc_otg_pcd_ep_t	*ep;	if ((_wIndex & USB_ENDPOINT_NUMBER_MASK) == 0)		return &_pcd->ep0;	list_for_each_entry( ep, &_pcd->gadget.ep_list, ep.ep_list)	{		u8	bEndpointAddress;		if (!ep->desc)			continue;		bEndpointAddress = ep->desc->bEndpointAddress;		if ((_wIndex ^ bEndpointAddress) & USB_DIR_IN)			continue;		if ((_wIndex & 0x0f) == (bEndpointAddress & 0x0f))			return ep;	}	return NULL;}/** * This function checks the EP request queue, if the queue is not * empty the next request is started. */void start_next_request( dwc_otg_pcd_ep_t *_ep ){	dwc_otg_pcd_request_t *req = 0;	if (!list_empty(&_ep->queue))	{		req = list_entry(_ep->queue.next,			   dwc_otg_pcd_request_t, queue);		/* Setup and start the Transfer */		_ep->dwc_ep.start_xfer_buff = req->req.buf;		_ep->dwc_ep.xfer_buff = req->req.buf;		_ep->dwc_ep.xfer_len = req->req.length;		_ep->dwc_ep.xfer_count = 0;		_ep->dwc_ep.dma_addr = req->req.dma;		_ep->dwc_ep.sent_zlp = 0;		_ep->dwc_ep.total_len = _ep->dwc_ep.xfer_len;		if(req->req.zero)		{			if((_ep->dwc_ep.xfer_len % _ep->dwc_ep.maxpacket == 0) && (_ep->dwc_ep.xfer_len != 0))			{				_ep->dwc_ep.sent_zlp = 1;			}		}		//DWC_ERROR(" -> starting transfer (start_next_req) %s %s\n",		//_ep->ep.name, _ep->dwc_ep.is_in?"IN":"OUT");		dwc_otg_ep_start_transfer( GET_CORE_IF(_ep->pcd), &_ep->dwc_ep );	}}/** * This function handles the SOF Interrupts. At this time the SOF * Interrupt is disabled. */int32_t dwc_otg_pcd_handle_sof_intr(dwc_otg_pcd_t *_pcd){	dwc_otg_core_if_t *core_if = GET_CORE_IF(_pcd);	gintsts_data_t gintsts;	//DWC_DEBUGPL(DBG_PCD, "SOF\n");	/* Clear interrupt */	gintsts.d32 = 0;	gintsts.b.sofintr = 1;	dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32);	return 1;}/** * This function handles the Rx Status Queue Level Interrupt, which * indicates that there is a least one packet in the Rx FIFO.  The * packets are moved from the FIFO to memory, where they will be * processed when the Endpoint Interrupt Register indicates Transfer * Complete or SETUP Phase Done. * * Repeat the following until the Rx Status Queue is empty: *	 -# Read the Receive Status Pop Register (GRXSTSP) to get Packet *		info *	 -# If Receive FIFO is empty then skip to step Clear the interrupt *		and exit *	 -# If SETUP Packet call dwc_otg_read_setup_packet to copy the *		SETUP data to the buffer *	 -# If OUT Data Packet call dwc_otg_read_packet to copy the data *		to the destination buffer */int32_t dwc_otg_pcd_handle_rx_status_q_level_intr(dwc_otg_pcd_t *_pcd){	dwc_otg_core_if_t *core_if = GET_CORE_IF(_pcd);	dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;	gintmsk_data_t gintmask = {.d32=0};	device_grxsts_data_t status;	dwc_otg_pcd_ep_t *ep;	gintsts_data_t gintsts;#ifdef DEBUG	static char *dpid_str[] ={ "D0", "D2", "D1", "MDATA" };#endif	//DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _pcd);	/* Disable the Rx Status Queue Level interrupt */	gintmask.b.rxstsqlvl= 1;	dwc_modify_reg32( &global_regs->gintmsk, gintmask.d32, 0);	/* Get the Status from the top of the FIFO */	status.d32 = dwc_read_reg32( &global_regs->grxstsp );	DWC_DEBUGPL(DBG_PCD, "EP:%d BCnt:%d DPID:%s "					"pktsts:%x Frame:%d(0x%0x)\n",					status.b.epnum, status.b.bcnt,					dpid_str[status.b.dpid],					status.b.pktsts, status.b.fn, status.b.fn);	/* Get pointer to EP structure */	ep = get_out_ep(_pcd, status.b.epnum);	switch (status.b.pktsts)	{	case DWC_DSTS_GOUT_NAK:		DWC_DEBUGPL(DBG_PCDV, "Global OUT NAK\n");		break;	case DWC_STS_DATA_UPDT:		DWC_DEBUGPL(DBG_PCDV, "OUT Data Packet\n");		if (status.b.bcnt && ep->dwc_ep.xfer_buff)		{			/** @todo NGS Check for buffer overflow? */			dwc_otg_read_packet( core_if,								 ep->dwc_ep.xfer_buff,								 status.b.bcnt);			ep->dwc_ep.xfer_count += status.b.bcnt;			ep->dwc_ep.xfer_buff += status.b.bcnt;		}		break;	case DWC_STS_XFER_COMP:		DWC_DEBUGPL(DBG_PCDV, "OUT Complete\n");		break;	case DWC_DSTS_SETUP_COMP:#ifdef DEBUG_EP0		DWC_DEBUGPL(DBG_PCDV, "Setup Complete\n");#endif		break;case DWC_DSTS_SETUP_UPDT:		dwc_otg_read_setup_packet( core_if, _pcd->setup_pkt->d32);#ifdef DEBUG_EP0		DWC_DEBUGPL(DBG_PCD,					"SETUP PKT: %02x.%02x v%04x i%04x l%04x\n",					_pcd->setup_pkt->req.bRequestType,					_pcd->setup_pkt->req.bRequest,					_pcd->setup_pkt->req.wValue,					_pcd->setup_pkt->req.wIndex,					_pcd->setup_pkt->req.wLength);#endif		ep->dwc_ep.xfer_count += status.b.bcnt;		break;	default:		DWC_DEBUGPL(DBG_PCDV, "Invalid Packet Status (0x%0x)\n",						status.b.pktsts);		break;	}	/* Enable the Rx Status Queue Level interrupt */	dwc_modify_reg32( &global_regs->gintmsk, 0, gintmask.d32);	/* Clear interrupt */	gintsts.d32 = 0;	gintsts.b.rxstsqlvl = 1;	dwc_write_reg32 (&global_regs->gintsts, gintsts.d32);	//DWC_DEBUGPL(DBG_PCDV, "EXIT: %s\n", __func__);	return 1;}/** * This function examines the Device IN Token Learning Queue to * determine the EP number of the last IN token received.  This * implementation is for the Mass Storage device where there are only * 2 IN EPs (Control-IN and BULK-IN). * * The EP numbers for the first six IN Tokens are in DTKNQR1 and there * are 8 EP Numbers in each of the other possible DTKNQ Registers. * * @param _core_if Programming view of DWC_otg controller. * */static inline int get_ep_of_last_in_token(dwc_otg_core_if_t *_core_if){	dwc_otg_device_global_regs_t *dev_global_regs =			_core_if->dev_if->dev_global_regs;	const uint32_t TOKEN_Q_DEPTH = _core_if->hwcfg2.b.dev_token_q_depth;	/* Number of Token Queue Registers */	const int DTKNQ_REG_CNT = (TOKEN_Q_DEPTH + 7) / 8;	dtknq1_data_t dtknqr1;	uint32_t in_tkn_epnums[4];	int ndx = 0;	int i = 0;	volatile uint32_t *addr = &dev_global_regs->dtknqr1;	int epnum = 0;	//DWC_DEBUGPL(DBG_PCD,"dev_token_q_depth=%d\n",TOKEN_Q_DEPTH);	/* Read the DTKNQ Registers */	for (i = 0; i < DTKNQ_REG_CNT; i++)	{		in_tkn_epnums[ i ] = dwc_read_reg32(addr);		DWC_DEBUGPL(DBG_PCDV, "DTKNQR%d=0x%08x\n", i+1,					in_tkn_epnums[i]);		if (addr == &dev_global_regs->dvbusdis)		{			addr = &dev_global_regs->dtknqr3_dthrctl;		}		else		{			++addr;		}		}		/* Copy the DTKNQR1 data to the bit field. */		dtknqr1.d32 = in_tkn_epnums[0];		/* Get the EP numbers */		in_tkn_epnums[0] = dtknqr1.b.epnums0_5;		ndx = dtknqr1.b.intknwptr - 1;		//DWC_DEBUGPL(DBG_PCDV,"ndx=%d\n",ndx);		if (ndx == -1)		{			/** @todo Find a simpler way to calculate the max			 * queue position.*/			int cnt = TOKEN_Q_DEPTH;			if (TOKEN_Q_DEPTH <= 6)			{				cnt = TOKEN_Q_DEPTH - 1;			}			else if (TOKEN_Q_DEPTH <= 14)			{				cnt = TOKEN_Q_DEPTH - 7;			}			else if (TOKEN_Q_DEPTH <= 22)			{				cnt = TOKEN_Q_DEPTH - 15;			}			else			{				cnt = TOKEN_Q_DEPTH - 23;			}			epnum = (in_tkn_epnums[ DTKNQ_REG_CNT - 1 ] >> (cnt * 4)) & 0xF;		}		else		{			if (ndx <= 5)			{				epnum = (in_tkn_epnums[0] >> (ndx * 4)) & 0xF;			}			else if (ndx <= 13 )			{				ndx -= 6;				epnum = (in_tkn_epnums[1] >> (ndx * 4)) & 0xF;			}			else if (ndx <= 21 )			{				ndx -= 14;				epnum = (in_tkn_epnums[2] >> (ndx * 4)) & 0xF;			}			else if (ndx <= 29 )			{				ndx -= 22;				epnum = (in_tkn_epnums[3] >> (ndx * 4)) & 0xF;			}		}		//DWC_DEBUGPL(DBG_PCD,"epnum=%d\n",epnum);		return epnum;}/** * This interrupt occurs when the non-periodic Tx FIFO is half-empty. * The active request is checked for the next packet to be loaded into * the non-periodic Tx FIFO. */int32_t dwc_otg_pcd_handle_np_tx_fifo_empty_intr(dwc_otg_pcd_t *_pcd){	dwc_otg_core_if_t *core_if = GET_CORE_IF(_pcd);	dwc_otg_core_global_regs_t *global_regs =			core_if->core_global_regs;	dwc_otg_dev_in_ep_regs_t *ep_regs;	gnptxsts_data_t txstatus = {.d32 = 0};	gintsts_data_t gintsts;	int epnum = 0;	dwc_otg_pcd_ep_t *ep = 0;	uint32_t len = 0;	int dwords;	/* Get the epnum from the IN Token Learning Queue. */	epnum = get_ep_of_last_in_token(core_if);	ep = get_in_ep(_pcd, epnum);	DWC_DEBUGPL(DBG_PCD, "NP TxFifo Empty: %s(%d) \n", ep->ep.name, epnum );	ep_regs = core_if->dev_if->in_ep_regs[epnum];	len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;	if (len > ep->dwc_ep.maxpacket)	{		len = ep->dwc_ep.maxpacket;	}	dwords = (len + 3)/4;	/* While there is space in the queue and space in the FIFO and	* More data to tranfer, Write packets to the Tx FIFO */	txstatus.d32 = dwc_read_reg32( &global_regs->gnptxsts );	DWC_DEBUGPL(DBG_PCDV, "b4 GNPTXSTS=0x%08x\n",txstatus.d32);	while  (txstatus.b.nptxqspcavail > 0 &&				txstatus.b.nptxfspcavail > dwords &&				ep->dwc_ep.xfer_count < ep->dwc_ep.xfer_len)	{		/* Write the FIFO */		dwc_otg_ep_write_packet( core_if, &ep->dwc_ep, 0 );		len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count;		if (len > ep->dwc_ep.maxpacket)		{			len = ep->dwc_ep.maxpacket;		}		dwords = (len + 3)/4;		txstatus.d32 = dwc_read_reg32(&global_regs->gnptxsts);		DWC_DEBUGPL(DBG_PCDV,"GNPTXSTS=0x%08x\n",txstatus.d32);	}	DWC_DEBUGPL(DBG_PCDV, "GNPTXSTS=0x%08x\n",					dwc_read_reg32( &global_regs->gnptxsts));	/* Clear interrupt */	gintsts.d32 = 0;	gintsts.b.nptxfempty = 1;	dwc_write_reg32 (&global_regs->gintsts, gintsts.d32);	return 1;}/** * This function is called when dedicated Tx FIFO Empty interrupt occurs. * The active request is checked for the next packet to be loaded into * apropriate Tx FIFO. */static int32_t write_empty_tx_fifo(dwc_otg_pcd_t *_pcd, uint32_t epnum){	dwc_otg_core_if_t *core_if = GET_CORE_IF(_pcd);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -