musb_host.c

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

C
1,976
字号
			pUrb->error_count++;		}		d = pUrb->iso_frame_desc + qh->iso_idx;		pBuffer = buffer + d->offset;		length = d->length;		if (wRxCount > length) {			if (status == 0) {				status = -EOVERFLOW;				pUrb->error_count++;			}			DBG(2, "** OVERFLOW %d into %d\n", wRxCount, length);			do_flush = 1;		} else			length = wRxCount;		pUrb->actual_length += length;		d->actual_length = length;		d->status = status;		/* see if we are done */		bDone = (++qh->iso_idx >= pUrb->number_of_packets);	} else {		/* non-isoch */		pBuffer = buffer + qh->offset;		length = pUrb->transfer_buffer_length - qh->offset;		if (wRxCount > length) {			if (pUrb->status == -EINPROGRESS)				pUrb->status = -EOVERFLOW;			DBG(2, "** OVERFLOW %d into %d\n", wRxCount, length);			do_flush = 1;		} else			length = wRxCount;		pUrb->actual_length += length;		qh->offset += length;		/* see if we are done */		bDone = (pUrb->actual_length == pUrb->transfer_buffer_length)			|| (wRxCount < qh->maxpacket)			|| (pUrb->status != -EINPROGRESS);		if (bDone				&& (pUrb->status == -EINPROGRESS)				&& (pUrb->transfer_flags & URB_SHORT_NOT_OK)				&& (pUrb->actual_length					< pUrb->transfer_buffer_length))			pUrb->status = -EREMOTEIO;	}	musb_read_fifo(pEnd, length, pBuffer);	wCsr = musb_readw(epio, MGC_O_HDRC_RXCSR);	wCsr |= MGC_M_RXCSR_H_WZC_BITS;	if (unlikely(do_flush))		musb_h_flush_rxfifo(pEnd, wCsr);	else {		/* REVISIT this assumes AUTOCLEAR is never set */		wCsr &= ~(MGC_M_RXCSR_RXPKTRDY | MGC_M_RXCSR_H_REQPKT);		if (!bDone)			wCsr |= MGC_M_RXCSR_H_REQPKT;		musb_writew(epio, MGC_O_HDRC_RXCSR, wCsr);	}	return bDone;}/* we don't always need to reinit a given side of an endpoint... * when we do, use tx/rx reinit routine and then construct a new CSR * to address data toggle, NYET, and DMA or PIO. * * it's possible that driver bugs (especially for DMA) or aborting a * transfer might have left the endpoint busier than it should be. * the busy/not-empty tests are basically paranoia. */static voidmusb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep){	u16	csr;	/* NOTE:  we know the "rx" fifo reinit never triggers for ep0.	 * That always uses tx_reinit since ep0 repurposes TX register	 * offsets; the initial SETUP packet is also a kind of OUT.	 */	/* if programmed for Tx, put it in RX mode */	if (ep->bIsSharedFifo) {		csr = musb_readw(ep->regs, MGC_O_HDRC_TXCSR);		if (csr & MGC_M_TXCSR_MODE) {			musb_h_tx_flush_fifo(ep);			musb_writew(ep->regs, MGC_O_HDRC_TXCSR,					MGC_M_TXCSR_FRCDATATOG);		}		/* clear mode (and everything else) to enable Rx */		musb_writew(ep->regs, MGC_O_HDRC_TXCSR, 0);	/* scrub all previous state, clearing toggle */	} else {		csr = musb_readw(ep->regs, MGC_O_HDRC_RXCSR);		if (csr & MGC_M_RXCSR_RXPKTRDY)			WARN("rx%d, packet/%d ready?\n", ep->bLocalEnd,				musb_readw(ep->regs, MGC_O_HDRC_RXCOUNT));		musb_h_flush_rxfifo(ep, MGC_M_RXCSR_CLRDATATOG);	}	/* target addr and (for multipoint) hub addr/port */	if (musb->bIsMultipoint) {		musb_writeb(ep->target_regs, MGC_O_HDRC_RXFUNCADDR,			qh->addr_reg);		musb_writeb(ep->target_regs, MGC_O_HDRC_RXHUBADDR,			qh->h_addr_reg);		musb_writeb(ep->target_regs, MGC_O_HDRC_RXHUBPORT,			qh->h_port_reg);	} else		musb_writeb(musb->pRegs, MGC_O_HDRC_FADDR, qh->addr_reg);	/* protocol/endpoint, interval/NAKlimit, i/o size */	musb_writeb(ep->regs, MGC_O_HDRC_RXTYPE, qh->type_reg);	musb_writeb(ep->regs, MGC_O_HDRC_RXINTERVAL, qh->intv_reg);	/* NOTE: bulk combining rewrites high bits of maxpacket */	musb_writew(ep->regs, MGC_O_HDRC_RXMAXP, qh->maxpacket);	ep->rx_reinit = 0;}/* * Program an HDRC endpoint as per the given URB * Context: irqs blocked, controller lock held */static void musb_ep_program(struct musb *pThis, u8 bEnd,			struct urb *pUrb, unsigned int is_out,			u8 * pBuffer, u32 dwLength){	struct dma_controller	*pDmaController;	struct dma_channel	*pDmaChannel;	u8			bDmaOk;	void __iomem		*pBase = pThis->pRegs;	struct musb_hw_ep	*pEnd = pThis->aLocalEnd + bEnd;	void __iomem		*epio = pEnd->regs;	struct musb_qh		*qh;	if (!is_out || pEnd->bIsSharedFifo)		qh = pEnd->in_qh;	else		qh = pEnd->out_qh;	DBG(3, "%s hw%d urb %p spd%d dev%d ep%d%s "				"h_addr%02x h_port%02x bytes %d\n",			is_out ? "-->" : "<--",			bEnd, pUrb, pUrb->dev->speed,			qh->addr_reg, qh->epnum, is_out ? "out" : "in",			qh->h_addr_reg, qh->h_port_reg,			dwLength);	MGC_SelectEnd(pBase, bEnd);	/* candidate for DMA? */	pDmaController = pThis->pDmaController;	if (is_dma_capable() && bEnd && pDmaController) {		pDmaChannel = is_out ? pEnd->tx_channel : pEnd->rx_channel;		if (!pDmaChannel) {			pDmaChannel = pDmaController->channel_alloc(					pDmaController, pEnd, is_out);			if (is_out)				pEnd->tx_channel = pDmaChannel;			else				pEnd->rx_channel = pDmaChannel;		}	} else		pDmaChannel = NULL;	/* make sure we clear DMAEnab, autoSet bits from previous run */	/* OUT/transmit/EP0 or IN/receive? */	if (is_out) {		u16	wCsr;		u16	wIntrTxE;		u16	wLoadCount;		wCsr = musb_readw(epio, MGC_O_HDRC_TXCSR);		/* disable interrupt in case we flush */		wIntrTxE = musb_readw(pBase, MGC_O_HDRC_INTRTXE);		musb_writew(pBase, MGC_O_HDRC_INTRTXE, wIntrTxE & ~(1 << bEnd));		/* general endpoint setup */		if (bEnd) {			u16	csr = wCsr;			/* ASSERT:  TXCSR_DMAENAB was already cleared */			/* flush all old state, set default */			musb_h_tx_flush_fifo(pEnd);			csr &= ~(MGC_M_TXCSR_H_NAKTIMEOUT					| MGC_M_TXCSR_DMAMODE					| MGC_M_TXCSR_FRCDATATOG					| MGC_M_TXCSR_H_RXSTALL					| MGC_M_TXCSR_H_ERROR					| MGC_M_TXCSR_TXPKTRDY					);			csr |= MGC_M_TXCSR_MODE;			if (usb_gettoggle(pUrb->dev,					qh->epnum, 1))				csr |= MGC_M_TXCSR_H_WR_DATATOGGLE					| MGC_M_TXCSR_H_DATATOGGLE;			else				csr |= MGC_M_TXCSR_CLRDATATOG;			/* twice in case of double packet buffering */			musb_writew(epio, MGC_O_HDRC_TXCSR, csr);			/* REVISIT may need to clear FLUSHFIFO ... */			musb_writew(epio, MGC_O_HDRC_TXCSR, csr);			wCsr = musb_readw(epio, MGC_O_HDRC_TXCSR);		} else {			/* endpoint 0: just flush */			musb_writew(epio, MGC_O_HDRC_CSR0,				wCsr | MGC_M_CSR0_FLUSHFIFO);			musb_writew(epio, MGC_O_HDRC_CSR0,				wCsr | MGC_M_CSR0_FLUSHFIFO);		}		/* target addr and (for multipoint) hub addr/port */		if (pThis->bIsMultipoint) {			musb_writeb(pBase,				MGC_BUSCTL_OFFSET(bEnd, MGC_O_HDRC_TXFUNCADDR),				qh->addr_reg);			musb_writeb(pBase,				MGC_BUSCTL_OFFSET(bEnd, MGC_O_HDRC_TXHUBADDR),				qh->h_addr_reg);			musb_writeb(pBase,				MGC_BUSCTL_OFFSET(bEnd, MGC_O_HDRC_TXHUBPORT),				qh->h_port_reg);/* FIXME if !bEnd, do the same for RX ... */		} else			musb_writeb(pBase, MGC_O_HDRC_FADDR, qh->addr_reg);		/* protocol/endpoint/interval/NAKlimit */		if (bEnd) {			musb_writeb(epio, MGC_O_HDRC_TXTYPE, qh->type_reg);			if (can_bulk_split(pThis, qh->type))				musb_writew(epio, MGC_O_HDRC_TXMAXP,					qh->maxpacket					| ((pEnd->wMaxPacketSizeTx /						qh->maxpacket) - 1) << 11);			else				musb_writew(epio, MGC_O_HDRC_TXMAXP,					qh->maxpacket);			musb_writeb(epio, MGC_O_HDRC_TXINTERVAL, qh->intv_reg);		} else {			musb_writeb(epio, MGC_O_HDRC_NAKLIMIT0, qh->intv_reg);			if (pThis->bIsMultipoint)				musb_writeb(epio, MGC_O_HDRC_TYPE0,						qh->type_reg);		}		if (can_bulk_split(pThis, qh->type))			wLoadCount = min((u32) pEnd->wMaxPacketSizeTx,						dwLength);		else			wLoadCount = min((u32) qh->maxpacket, dwLength);		/* candidate for DMA */		if ((is_cppi_enabled() || tusb_dma_omap()) && pDmaChannel) {			/* program endpoint CSRs first, then setup DMA.			 * assume CPPI setup succeeds.			 * defer enabling dma.			 */			wCsr = musb_readw(epio, MGC_O_HDRC_TXCSR);			wCsr &= ~(MGC_M_TXCSR_AUTOSET					| MGC_M_TXCSR_DMAMODE					| MGC_M_TXCSR_DMAENAB);			wCsr |= MGC_M_TXCSR_MODE;			musb_writew(epio, MGC_O_HDRC_TXCSR,				wCsr | MGC_M_TXCSR_MODE);			pDmaChannel->dwActualLength = 0L;			qh->segsize = dwLength;			/* TX uses "rndis" mode automatically, but needs help			 * to identify the zero-length-final-packet case.			 */			bDmaOk = pDmaController->channel_program(					pDmaChannel, qh->maxpacket,					(pUrb->transfer_flags							& URB_ZERO_PACKET)						== URB_ZERO_PACKET,					pUrb->transfer_dma,					qh->segsize);			if (bDmaOk) {				wLoadCount = 0;			} else {				pDmaController->channel_release(pDmaChannel);				pDmaChannel = pEnd->tx_channel = NULL;				/* REVISIT there's an error path here that				 * needs handling:  can't do dma, but				 * there's no pio buffer address...				 */			}		}		if (wLoadCount) {			/* ASSERT:  TXCSR_DMAENAB was already cleared */			/* PIO to load FIFO */			qh->segsize = wLoadCount;			musb_write_fifo(pEnd, wLoadCount, pBuffer);			wCsr = musb_readw(epio, MGC_O_HDRC_TXCSR);			wCsr &= ~(MGC_M_TXCSR_DMAENAB				| MGC_M_TXCSR_DMAMODE				| MGC_M_TXCSR_AUTOSET);			/* write CSR */			wCsr |= MGC_M_TXCSR_MODE;			if (bEnd)				musb_writew(epio, MGC_O_HDRC_TXCSR, wCsr);		}		/* re-enable interrupt */		musb_writew(pBase, MGC_O_HDRC_INTRTXE, wIntrTxE);	/* IN/receive */	} else {		u16	csr;		if (pEnd->rx_reinit) {			musb_rx_reinit(pThis, qh, pEnd);			/* init new state: toggle and NYET, maybe DMA later */			if (usb_gettoggle(pUrb->dev, qh->epnum, 0))				csr = MGC_M_RXCSR_H_WR_DATATOGGLE					| MGC_M_RXCSR_H_DATATOGGLE;			else				csr = 0;			if (qh->type == USB_ENDPOINT_XFER_INT)				csr |= MGC_M_RXCSR_DISNYET;		} else {			csr = musb_readw(pEnd->regs, MGC_O_HDRC_RXCSR);			if (csr & (MGC_M_RXCSR_RXPKTRDY					| MGC_M_RXCSR_DMAENAB					| MGC_M_RXCSR_H_REQPKT))				ERR("broken !rx_reinit, ep%d csr %04x\n",						pEnd->bLocalEnd, csr);			/* scrub any stale state, leaving toggle alone */			csr &= MGC_M_RXCSR_DISNYET;		}		/* kick things off */		if ((is_cppi_enabled() || tusb_dma_omap()) && pDmaChannel) {			/* candidate for DMA */			if (pDmaChannel) {				pDmaChannel->dwActualLength = 0L;				qh->segsize = dwLength;				/* AUTOREQ is in a DMA register */				musb_writew(pEnd->regs, MGC_O_HDRC_RXCSR, csr);				csr = musb_readw(pEnd->regs,						MGC_O_HDRC_RXCSR);				/* unless caller treats short rx transfers as				 * errors, we dare not queue multiple transfers.				 */				bDmaOk = pDmaController->channel_program(						pDmaChannel, qh->maxpacket,						!(pUrb->transfer_flags							& URB_SHORT_NOT_OK),						pUrb->transfer_dma,						qh->segsize);				if (!bDmaOk) {					pDmaController->channel_release(							pDmaChannel);					pDmaChannel = pEnd->rx_channel = NULL;				} else					csr |= MGC_M_RXCSR_DMAENAB;			}		}		csr |= MGC_M_RXCSR_H_REQPKT;		DBG(7, "RXCSR%d := %04x\n", bEnd, csr);		musb_writew(pEnd->regs, MGC_O_HDRC_RXCSR, csr);		csr = musb_readw(pEnd->regs, MGC_O_HDRC_RXCSR);	}}/* * Service the default endpoint (ep0) as host. * Return TRUE until it's time to start the status stage. */static int musb_h_ep0_continue(struct musb *pThis,				u16 wCount, struct urb *pUrb){	int			 bMore = FALSE;	u8 *pFifoDest = NULL;	u16 wFifoCount = 0;	struct musb_hw_ep	*pEnd = pThis->control_ep;	struct musb_qh		*qh = pEnd->in_qh;	struct usb_ctrlrequest	*pRequest;	switch (pThis->bEnd0Stage) {	case MGC_END0_IN:		pFifoDest = pUrb->transfer_buffer + pUrb->actual_length;		wFifoCount = min(wCount, ((u16) (pUrb->transfer_buffer_length					- pUrb->actual_length)));		if (wFifoCount < wCount)			pUrb->status = -EOVERFLOW;		musb_read_fifo(pEnd, wFifoCount, pFifoDest);		pUrb->actual_length += wFifoCount;		if (wCount < qh->maxpacket) {			/* always terminate on short read; it's			 * rarely reported as an error.			 */		} else if (pUrb->actual_length <				pUrb->transfer_buffer_length)			bMore = TRUE;		break;	case MGC_END0_START:		pRequest = (struct usb_ctrlrequest *) pUrb->setup_packet;		if (!pRequest->wLength) {			DBG(4, "start no-DATA\n");			break;		} else if (pRequest->bRequestType & USB_DIR_IN) {			DBG(4, "start IN-DATA\n");			pThis->bEnd0Stage = MGC_END0_IN;			bMore = TRUE;			break;		} else {			DBG(4, "start OUT-DATA\n");			pThis->bEnd0Stage = MGC_END0_OUT;			bMore = TRUE;		}		/* FALLTHROUGH */	case MGC_END0_OUT:		wFifoCount = min(qh->maxpacket, ((u16)				(pUrb->transfer_buffer_length				- pUrb->actual_length)));		if (wFifoCount) {			pFifoDest = (u8 *) (pUrb->transfer_buffer					+ pUrb->actual_length);			DBG(3, "Sending %d bytes to %p\n",					wFifoCount, pFifoDest);			musb_write_fifo(pEnd, wFifoCount, pFifoDest);			pUrb->actual_length += wFifoCount;			bMore = TRUE;		}		break;	default:		ERR("bogus ep0 stage %d\n", pThis->bEnd0Stage);		break;	}	return bMore;}/* * Handle default endpoint interrupt as host. Only called in IRQ time * from the LinuxIsr() interrupt service routine. * * called with controller irqlocked */irqreturn_t musb_h_ep0_irq(struct musb *pThis){	struct urb		*pUrb;	u16			wCsrVal, wCount;	int			status = 0;	void __iomem		*pBase = pThis->pRegs;	struct musb_hw_ep	*pEnd = pThis->control_ep;	void __iomem		*epio = pEnd->regs;	struct musb_qh		*qh = pEnd->in_qh;	u8			bComplete = FALSE;	irqreturn_t		retval = IRQ_NONE;	/* ep0 only has one queue, "in" */	pUrb = next_urb(qh);	MGC_SelectEnd(pBase, 0);	wCsrVal = musb_readw(epio, MGC_O_HDRC_CSR0);	wCount = (wCsrVal & MGC_M_CSR0_RXPKTRDY)			? musb_readb(epio, MGC_O_HDRC_COUNT0)			: 0;	DBG(4, "<== csr0 %04x, qh %p, count %d, urb %p, stage %d\n",

⌨️ 快捷键说明

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