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 + -
显示快捷键?