musb_host.c

来自「omap3 linux 2.6 用nocc去除了冗余代码」· C语言 代码 · 共 1,976 行 · 第 1/4 页

C
1,976
字号
		wCsrVal, qh, wCount, pUrb, pThis->bEnd0Stage);	/* if we just did status stage, we are done */	if (MGC_END0_STATUS == pThis->bEnd0Stage) {		retval = IRQ_HANDLED;		bComplete = TRUE;	}	/* prepare status */	if (wCsrVal & MGC_M_CSR0_H_RXSTALL) {		DBG(6, "STALLING ENDPOINT\n");		status = -EPIPE;	} else if (wCsrVal & MGC_M_CSR0_H_ERROR) {		DBG(2, "no response, csr0 %04x\n", wCsrVal);		status = -EPROTO;	} else if (wCsrVal & MGC_M_CSR0_H_NAKTIMEOUT) {		DBG(2, "control NAK timeout\n");		/* NOTE:  this code path would be a good place to PAUSE a		 * control transfer, if another one is queued, so that		 * ep0 is more likely to stay busy.		 *		 * if (qh->ring.next != &musb->control), then		 * we have a candidate... NAKing is *NOT* an error		 */		musb_writew(epio, MGC_O_HDRC_CSR0, 0);		retval = IRQ_HANDLED;	}	if (status) {		DBG(6, "aborting\n");		retval = IRQ_HANDLED;		if (pUrb)			pUrb->status = status;		bComplete = TRUE;		/* use the proper sequence to abort the transfer */		if (wCsrVal & MGC_M_CSR0_H_REQPKT) {			wCsrVal &= ~MGC_M_CSR0_H_REQPKT;			musb_writew(epio, MGC_O_HDRC_CSR0, wCsrVal);			wCsrVal &= ~MGC_M_CSR0_H_NAKTIMEOUT;			musb_writew(epio, MGC_O_HDRC_CSR0, wCsrVal);		} else {			wCsrVal |= MGC_M_CSR0_FLUSHFIFO;			musb_writew(epio, MGC_O_HDRC_CSR0, wCsrVal);			musb_writew(epio, MGC_O_HDRC_CSR0, wCsrVal);			wCsrVal &= ~MGC_M_CSR0_H_NAKTIMEOUT;			musb_writew(epio, MGC_O_HDRC_CSR0, wCsrVal);		}		musb_writeb(epio, MGC_O_HDRC_NAKLIMIT0, 0);		/* clear it */		musb_writew(epio, MGC_O_HDRC_CSR0, 0);	}	if (unlikely(!pUrb)) {		/* stop endpoint since we have no place for its data, this		 * SHOULD NEVER HAPPEN! */		ERR("no URB for end 0\n");		musb_writew(epio, MGC_O_HDRC_CSR0, MGC_M_CSR0_FLUSHFIFO);		musb_writew(epio, MGC_O_HDRC_CSR0, MGC_M_CSR0_FLUSHFIFO);		musb_writew(epio, MGC_O_HDRC_CSR0, 0);		goto done;	}	if (!bComplete) {		/* call common logic and prepare response */		if (musb_h_ep0_continue(pThis, wCount, pUrb)) {			/* more packets required */			wCsrVal = (MGC_END0_IN == pThis->bEnd0Stage)				?  MGC_M_CSR0_H_REQPKT : MGC_M_CSR0_TXPKTRDY;		} else {			/* data transfer complete; perform status phase */			if (usb_pipeout(pUrb->pipe)					|| !pUrb->transfer_buffer_length)				wCsrVal = MGC_M_CSR0_H_STATUSPKT					| MGC_M_CSR0_H_REQPKT;			else				wCsrVal = MGC_M_CSR0_H_STATUSPKT					| MGC_M_CSR0_TXPKTRDY;			/* flag status stage */			pThis->bEnd0Stage = MGC_END0_STATUS;			DBG(5, "ep0 STATUS, csr %04x\n", wCsrVal);		}		musb_writew(epio, MGC_O_HDRC_CSR0, wCsrVal);		retval = IRQ_HANDLED;	} else		pThis->bEnd0Stage = MGC_END0_IDLE;	/* call completion handler if done */	if (bComplete)		musb_advance_schedule(pThis, pUrb, pEnd, 1);done:	return retval;}/* Service a Tx-Available or dma completion irq for the endpoint */void musb_host_tx(struct musb *pThis, u8 bEnd){	int			nPipe;	u8			bDone = FALSE;	u16			wTxCsrVal;	size_t			wLength = 0;	u8			*pBuffer = NULL;	struct urb		*pUrb;	struct musb_hw_ep	*pEnd = pThis->aLocalEnd + bEnd;	void __iomem		*epio = pEnd->regs;	struct musb_qh		*qh = pEnd->out_qh;	u32			status = 0;	void __iomem		*pBase = pThis->pRegs;	struct dma_channel	*dma;	pUrb = next_urb(qh);	MGC_SelectEnd(pBase, bEnd);	wTxCsrVal = musb_readw(epio, MGC_O_HDRC_TXCSR);	/* with CPPI, DMA sometimes triggers "extra" irqs */	if (!pUrb) {		DBG(4, "extra TX%d ready, csr %04x\n", bEnd, wTxCsrVal);		goto finish;	}	nPipe = pUrb->pipe;	dma = is_dma_capable() ? pEnd->tx_channel : NULL;	DBG(4, "OUT/TX%d end, urb %p, csr %04x%s\n", bEnd, pUrb, wTxCsrVal,			dma ? ", dma" : "");	/* check for errors */	if (wTxCsrVal & MGC_M_TXCSR_H_RXSTALL) {		/* dma was disabled, fifo flushed */		DBG(3, "TX end %d stall\n", bEnd);		/* stall; record URB status */		status = -EPIPE;	} else if (wTxCsrVal & MGC_M_TXCSR_H_ERROR) {		/* (NON-ISO) dma was disabled, fifo flushed */		DBG(3, "TX 3strikes on ep=%d\n", bEnd);		status = -ETIMEDOUT;	} else if (wTxCsrVal & MGC_M_TXCSR_H_NAKTIMEOUT) {		DBG(6, "TX end=%d device not responding\n", bEnd);		/* NOTE:  this code path would be a good place to PAUSE a		 * transfer, if there's some other (nonperiodic) tx urb		 * that could use this fifo.  (dma complicates it...)		 *		 * if (bulk && qh->ring.next != &musb->out_bulk), then		 * we have a candidate... NAKing is *NOT* an error		 */		MGC_SelectEnd(pBase, bEnd);		musb_writew(epio, MGC_O_HDRC_CSR0,				MGC_M_TXCSR_H_WZC_BITS				| MGC_M_TXCSR_TXPKTRDY);		goto finish;	}	if (status) {		if (dma_channel_status(dma) == MGC_DMA_STATUS_BUSY) {			dma->bStatus = MGC_DMA_STATUS_CORE_ABORT;			(void) pThis->pDmaController->channel_abort(dma);		}		/* do the proper sequence to abort the transfer in the		 * usb core; the dma engine should already be stopped.		 */		musb_h_tx_flush_fifo(pEnd);		wTxCsrVal &= ~(MGC_M_TXCSR_AUTOSET				| MGC_M_TXCSR_FIFONOTEMPTY				| MGC_M_TXCSR_DMAENAB				| MGC_M_TXCSR_H_ERROR				| MGC_M_TXCSR_H_RXSTALL				| MGC_M_TXCSR_H_NAKTIMEOUT				);		MGC_SelectEnd(pBase, bEnd);		musb_writew(epio, MGC_O_HDRC_TXCSR, wTxCsrVal);		/* REVISIT may need to clear FLUSHFIFO ... */		musb_writew(epio, MGC_O_HDRC_TXCSR, wTxCsrVal);		musb_writeb(epio, MGC_O_HDRC_TXINTERVAL, 0);		bDone = TRUE;	}	/* second cppi case */	if (dma_channel_status(dma) == MGC_DMA_STATUS_BUSY) {		DBG(4, "extra TX%d ready, csr %04x\n", bEnd, wTxCsrVal);		goto finish;	}	/* REVISIT this looks wrong... */	if (!status || dma || usb_pipeisoc(nPipe)) {		if (dma)			wLength = dma->dwActualLength;		else			wLength = qh->segsize;		qh->offset += wLength;		if (usb_pipeisoc(nPipe)) {			struct usb_iso_packet_descriptor	*d;			d = pUrb->iso_frame_desc + qh->iso_idx;			d->actual_length = qh->segsize;			if (++qh->iso_idx >= pUrb->number_of_packets) {				bDone = TRUE;			} else {				bDone = FALSE;				d++;			}			if (!bDone && dma) {			}			/* pio or DMA failed case */			if (!bDone && !dma) {				pBuffer = pUrb->transfer_buffer + d->offset;				wLength = d->length;			}		} else if (dma) {			bDone = TRUE;		} else {			/* see if we need to send more data, or ZLP */			if (qh->segsize < qh->maxpacket)				bDone = TRUE;			else if (qh->offset == pUrb->transfer_buffer_length					&& !(pUrb-> transfer_flags							& URB_ZERO_PACKET))				bDone = TRUE;			if (!bDone) {				pBuffer = pUrb->transfer_buffer						+ qh->offset;				wLength = pUrb->transfer_buffer_length						- qh->offset;			}		}	}	/* urb->status != -EINPROGRESS means request has been faulted,	 * so we must abort this transfer after cleanup	 */	if (pUrb->status != -EINPROGRESS) {		bDone = TRUE;		if (status == 0)			status = pUrb->status;	}	if (bDone) {		/* set status */		pUrb->status = status;		pUrb->actual_length = qh->offset;		musb_advance_schedule(pThis, pUrb, pEnd, USB_DIR_OUT);	} else if (!dma) {		// WARN_ON(!pBuffer);		/* REVISIT:  some docs say that when pEnd->tx_double_buffered,		 * (and presumably, fifo is not half-full) we should write TWO		 * packets before updating TXCSR ... other docs disagree ...		 */		/* PIO:  start next packet in this URB */		wLength = min(qh->maxpacket, (u16) wLength);		musb_write_fifo(pEnd, wLength, pBuffer);		qh->segsize = wLength;		MGC_SelectEnd(pBase, bEnd);		musb_writew(epio, MGC_O_HDRC_TXCSR,				MGC_M_TXCSR_H_WZC_BITS | MGC_M_TXCSR_TXPKTRDY);	} else		DBG(1, "not complete, but dma enabled?\n");finish:	return;}/* * Service an RX interrupt for the given IN endpoint; docs cover bulk, iso, * and high-bandwidth IN transfer cases. */void musb_host_rx(struct musb *pThis, u8 bEnd){	struct urb		*pUrb;	struct musb_hw_ep	*pEnd = pThis->aLocalEnd + bEnd;	void __iomem		*epio = pEnd->regs;	struct musb_qh		*qh = pEnd->in_qh;	size_t			xfer_len;	void __iomem		*pBase = pThis->pRegs;	int			nPipe;	u16			wRxCsrVal, wVal;	u8			bIsochError = FALSE;	u8			bDone = FALSE;	u32			status;	struct dma_channel	*dma;	MGC_SelectEnd(pBase, bEnd);	pUrb = next_urb(qh);	dma = is_dma_capable() ? pEnd->rx_channel : NULL;	status = 0;	xfer_len = 0;	wVal = wRxCsrVal = musb_readw(epio, MGC_O_HDRC_RXCSR);	if (unlikely(!pUrb)) {		/* REVISIT -- THIS SHOULD NEVER HAPPEN ... but, at least		 * usbtest #11 (unlinks) triggers it regularly, sometimes		 * with fifo full.  (Only with DMA??)		 */		DBG(3, "BOGUS RX%d ready, csr %04x, count %d\n", bEnd, wVal,			musb_readw(epio, MGC_O_HDRC_RXCOUNT));		musb_h_flush_rxfifo(pEnd, MGC_M_RXCSR_CLRDATATOG);		return;	}	nPipe = pUrb->pipe;	DBG(5, "<== hw %d rxcsr %04x, urb actual %d (+dma %zd)\n",		bEnd, wRxCsrVal, pUrb->actual_length,		dma ? dma->dwActualLength : 0);	/* check for errors, concurrent stall & unlink is not really	 * handled yet! */	if (wRxCsrVal & MGC_M_RXCSR_H_RXSTALL) {		DBG(3, "RX end %d STALL\n", bEnd);		/* stall; record URB status */		status = -EPIPE;	} else if (wRxCsrVal & MGC_M_RXCSR_H_ERROR) {		DBG(3, "end %d RX proto error\n", bEnd);		status = -EPROTO;		musb_writeb(epio, MGC_O_HDRC_RXINTERVAL, 0);	} else if (wRxCsrVal & MGC_M_RXCSR_DATAERROR) {		if (USB_ENDPOINT_XFER_ISOC != qh->type) {			/* NOTE this code path would be a good place to PAUSE a			 * transfer, if there's some other (nonperiodic) rx urb			 * that could use this fifo.  (dma complicates it...)			 *			 * if (bulk && qh->ring.next != &musb->in_bulk), then			 * we have a candidate... NAKing is *NOT* an error			 */			DBG(6, "RX end %d NAK timeout\n", bEnd);			MGC_SelectEnd(pBase, bEnd);			musb_writew(epio, MGC_O_HDRC_RXCSR,					MGC_M_RXCSR_H_WZC_BITS					| MGC_M_RXCSR_H_REQPKT);			goto finish;		} else {			DBG(4, "RX end %d ISO data error\n", bEnd);			/* packet error reported later */			bIsochError = TRUE;		}	}	/* faults abort the transfer */	if (status) {		/* clean up dma and collect transfer count */		if (dma_channel_status(dma) == MGC_DMA_STATUS_BUSY) {			dma->bStatus = MGC_DMA_STATUS_CORE_ABORT;			(void) pThis->pDmaController->channel_abort(dma);			xfer_len = dma->dwActualLength;		}		musb_h_flush_rxfifo(pEnd, 0);		musb_writeb(epio, MGC_O_HDRC_RXINTERVAL, 0);		bDone = TRUE;		goto finish;	}	if (unlikely(dma_channel_status(dma) == MGC_DMA_STATUS_BUSY)) {		/* SHOULD NEVER HAPPEN ... but at least DaVinci has done it */		ERR("RX%d dma busy, csr %04x\n", bEnd, wRxCsrVal);		goto finish;	}	/* thorough shutdown for now ... given more precise fault handling	 * and better queueing support, we might keep a DMA pipeline going	 * while processing this irq for earlier completions.	 */	/* FIXME this is _way_ too much in-line logic for Mentor DMA */	if (wRxCsrVal & MGC_M_RXCSR_H_REQPKT)  {		/* REVISIT this happened for a while on some short reads...		 * the cleanup still needs investigation... looks bad...		 * and also duplicates dma cleanup code above ... plus,		 * shouldn't this be the "half full" double buffer case?		 */		if (dma_channel_status(dma) == MGC_DMA_STATUS_BUSY) {			dma->bStatus = MGC_DMA_STATUS_CORE_ABORT;			(void) pThis->pDmaController->channel_abort(dma);			xfer_len = dma->dwActualLength;			bDone = TRUE;		}		DBG(2, "RXCSR%d %04x, reqpkt, len %zd%s\n", bEnd, wRxCsrVal,				xfer_len, dma ? ", dma" : "");		wRxCsrVal &= ~MGC_M_RXCSR_H_REQPKT;		MGC_SelectEnd(pBase, bEnd);		musb_writew(epio, MGC_O_HDRC_RXCSR,				MGC_M_RXCSR_H_WZC_BITS | wRxCsrVal);	}	if (dma && (wRxCsrVal & MGC_M_RXCSR_DMAENAB)) {		xfer_len = dma->dwActualLength;		wVal &= ~(MGC_M_RXCSR_DMAENAB			| MGC_M_RXCSR_H_AUTOREQ			| MGC_M_RXCSR_AUTOCLEAR			| MGC_M_RXCSR_RXPKTRDY);		musb_writew(pEnd->regs, MGC_O_HDRC_RXCSR, wVal);		bDone = TRUE;	} else if (pUrb->status == -EINPROGRESS) {		/* if no errors, be sure a packet is ready for unloading */		if (unlikely(!(wRxCsrVal & MGC_M_RXCSR_RXPKTRDY))) {			status = -EPROTO;			ERR("Rx interrupt with no errors or packet!\n");			// FIXME this is another "SHOULD NEVER HAPPEN"// SCRUB (RX)			/* do the proper sequence to abort the transfer */			MGC_SelectEnd(pBase, bEnd);			wVal &= ~MGC_M_RXCSR_H_REQPKT;			musb_writew(epio, MGC_O_HDRC_RXCSR, wVal);			goto finish;		}		/* we are expecting IN packets */		if (!dma) {			bDone = musb_host_packet_rx(pThis, pUrb,					bEnd, bIsochError);			DBG(6, "read %spacket\n", bDone ? "last " : "");		}	}finish:	pUrb->actual_length += xfer_len;	qh->offset += xfer_len;	if (bDone) {		if (pUrb->status == -EINPROGRESS)			pUrb->status = status;		musb_advance_schedule(pThis, pUrb, pEnd, USB_DIR_IN);	}}/* schedule nodes correspond to peripheral endpoints, like an OHCI QH. * the software schedule associates multiple such nodes with a given * host side hardware endpoint + direction; scheduling may activate * that hardware endpoint. */static int musb_schedule(	struct musb		*musb,	struct musb_qh		*qh,	int			is_in){	int			idle;	int			wBestDiff;	int			nBestEnd, nEnd;	struct musb_hw_ep	*hw_ep = NULL;	struct list_head	*head = NULL;	/* use fixed hardware for control and bulk */	switch (qh->type) {	case USB_ENDPOINT_XFER_CONTROL:		head = &musb->control;		hw_ep = musb->control_ep;		break;	case USB_ENDPOINT_XFER_BULK:		hw_ep = musb->bulk_ep;		if (is_in)			head = &musb->in_bulk;		else			head = &musb->out_bulk;		break;	}

⌨️ 快捷键说明

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