📄 dwc_otg_hcd_intr.c
字号:
hcchar.d32 = dwc_read_reg32(&_hc_regs->hcchar); hcsplt.d32 = dwc_read_reg32(&_hc_regs->hcsplt); hctsiz.d32 = dwc_read_reg32(&_hc_regs->hctsiz); hcdma = dwc_read_reg32(&_hc_regs->hcdma); DWC_ERROR("AHB ERROR, Channel %d\n", _hc->hc_num); DWC_ERROR(" hcchar 0x%08x, hcsplt 0x%08x\n", hcchar.d32, hcsplt.d32); DWC_ERROR(" hctsiz 0x%08x, hcdma 0x%08x\n", hctsiz.d32, hcdma); DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Enqueue\n"); DWC_ERROR(" Device address: %d\n", usb_pipedevice(urb->pipe)); DWC_ERROR(" Endpoint: %d, %s\n", usb_pipeendpoint(urb->pipe), (usb_pipein(urb->pipe) ? "IN" : "OUT")); DWC_ERROR(" Endpoint type: %s\n", ( { char *pipetype; switch (usb_pipetype(urb->pipe)) {case PIPE_CONTROL:pipetype = "CONTROL"; break; case PIPE_BULK:pipetype = "BULK"; break; case PIPE_INTERRUPT:pipetype = "INTERRUPT"; break; case PIPE_ISOCHRONOUS:pipetype = "ISOCHRONOUS"; break; default: pipetype = "UNKNOWN"; break;}; pipetype;} )); DWC_ERROR(" Speed: %s\n", ( { char *speed; switch (urb->dev->speed) {case USB_SPEED_HIGH:speed = "HIGH"; break; case USB_SPEED_FULL:speed = "FULL"; break; case USB_SPEED_LOW:speed = "LOW"; break; default: speed = "UNKNOWN"; break;}; speed;} )); DWC_ERROR(" Max packet size: %d\n", usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))); DWC_ERROR(" Data buffer length: %d\n", urb->transfer_buffer_length); DWC_ERROR(" Transfer buffer: %p, Transfer DMA: %p\n", urb->transfer_buffer, (void *) urb->transfer_dma); DWC_ERROR(" Setup buffer: %p, Setup DMA: %p\n", urb->setup_packet, (void *) urb->setup_dma); DWC_ERROR(" Interval: %d\n", urb->interval); dwc_otg_hcd_complete_urb(_hcd, urb, -EIO); /* * Force a channel halt. Don't call halt_channel because that won't * write to the HCCHARn register in DMA mode to force the halt. */ dwc_otg_hc_halt(_hcd->core_if, _hc, DWC_OTG_HC_XFER_AHB_ERR); disable_hc_int(_hc_regs, ahberr); return 1;}/** * Handles a host channel transaction error interrupt. This handler may be * called in either DMA mode or Slave mode. */static int32_t handle_hc_xacterr_intr(dwc_otg_hcd_t * _hcd, dwc_hc_t * _hc, dwc_otg_hc_regs_t * _hc_regs, dwc_otg_qtd_t * _qtd){ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " "Transaction Error--\n", _hc->hc_num); switch (usb_pipetype(_qtd->urb->pipe)) { case PIPE_CONTROL: case PIPE_BULK: _qtd->error_count++; if (!_hc->qh->ping_state) { update_urb_state_xfer_intr(_hc, _hc_regs, _qtd->urb, _qtd, DWC_OTG_HC_XFER_XACT_ERR); save_data_toggle(_hc, _hc_regs, _qtd); if (!_hc->ep_is_in && _qtd->urb->dev->speed == USB_SPEED_HIGH) { _hc->qh->ping_state = 1; } } /* * Halt the channel so the transfer can be re-started from * the appropriate point or the PING protocol will start. */ halt_channel(_hcd, _hc, _qtd, DWC_OTG_HC_XFER_XACT_ERR); break; case PIPE_INTERRUPT: _qtd->error_count++; if ((_hc->do_split) && (_hc->complete_split)) { _qtd->complete_split = 0; } halt_channel(_hcd, _hc, _qtd, DWC_OTG_HC_XFER_XACT_ERR); break; case PIPE_ISOCHRONOUS: { dwc_otg_halt_status_e halt_status; halt_status = update_isoc_urb_state(_hcd, _hc, _hc_regs, _qtd, DWC_OTG_HC_XFER_XACT_ERR); halt_channel(_hcd, _hc, _qtd, halt_status); } break; } disable_hc_int(_hc_regs, xacterr); return 1;}/** * Handles a host channel frame overrun interrupt. This handler may be called * in either DMA mode or Slave mode. */static int32_t handle_hc_frmovrun_intr(dwc_otg_hcd_t * _hcd, dwc_hc_t * _hc, dwc_otg_hc_regs_t * _hc_regs, dwc_otg_qtd_t * _qtd){ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " "Frame Overrun--\n", _hc->hc_num); switch (usb_pipetype(_qtd->urb->pipe)) { case PIPE_CONTROL: case PIPE_BULK: break; case PIPE_INTERRUPT: halt_channel(_hcd, _hc, _qtd, DWC_OTG_HC_XFER_FRAME_OVERRUN); break; case PIPE_ISOCHRONOUS: { dwc_otg_halt_status_e halt_status; halt_status = update_isoc_urb_state(_hcd, _hc, _hc_regs, _qtd, DWC_OTG_HC_XFER_FRAME_OVERRUN); halt_channel(_hcd, _hc, _qtd, halt_status); } break; } disable_hc_int(_hc_regs, frmovrun); return 1;}/** * Handles a host channel data toggle error interrupt. This handler may be * called in either DMA mode or Slave mode. */static int32_t handle_hc_datatglerr_intr(dwc_otg_hcd_t * _hcd, dwc_hc_t * _hc, dwc_otg_hc_regs_t * _hc_regs, dwc_otg_qtd_t * _qtd){ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " "Data Toggle Error--\n", _hc->hc_num); if (_hc->ep_is_in) { _qtd->error_count = 0; } else { DWC_ERROR("Data Toggle Error on OUT transfer," "channel %d\n", _hc->hc_num); } disable_hc_int(_hc_regs, datatglerr); return 1;}#ifdef DEBUG/** * This function is for debug only. It checks that a valid halt status is set * and that HCCHARn.chdis is clear. If there's a problem, corrective action is * taken and a warning is issued. * @return 1 if halt status is ok, 0 otherwise. */static inline int halt_status_ok(dwc_otg_hcd_t * _hcd, dwc_hc_t * _hc, dwc_otg_hc_regs_t * _hc_regs, dwc_otg_qtd_t * _qtd){ hcchar_data_t hcchar; hctsiz_data_t hctsiz; hcint_data_t hcint; hcintmsk_data_t hcintmsk; hcsplt_data_t hcsplt; if (_hc->halt_status == DWC_OTG_HC_XFER_NO_HALT_STATUS) { /* * This code is here only as a check. This condition should * never happen. Ignore the halt if it does occur. */ hcchar.d32 = dwc_read_reg32(&_hc_regs->hcchar); hctsiz.d32 = dwc_read_reg32(&_hc_regs->hctsiz); hcint.d32 = dwc_read_reg32(&_hc_regs->hcint); hcintmsk.d32 = dwc_read_reg32(&_hc_regs->hcintmsk); hcsplt.d32 = dwc_read_reg32(&_hc_regs->hcsplt); DWC_WARN("%s: _hc->halt_status == DWC_OTG_HC_XFER_NO_HALT_STATUS, " "channel %d, hcchar 0x%08x, hctsiz 0x%08x, " "hcint 0x%08x, hcintmsk 0x%08x, " "hcsplt 0x%08x, qtd->complete_split %d\n", __func__, _hc->hc_num, hcchar.d32, hctsiz.d32, hcint.d32, hcintmsk.d32, hcsplt.d32, _qtd->complete_split); DWC_WARN("%s: no halt status, channel %d, ignoring interrupt\n", __func__, _hc->hc_num); DWC_WARN("\n"); clear_hc_int(_hc_regs, chhltd); return 0; } /* * This code is here only as a check. hcchar.chdis should * never be set when the halt interrupt occurs. Halt the * channel again if it does occur. */ hcchar.d32 = dwc_read_reg32(&_hc_regs->hcchar); if (hcchar.b.chdis) { DWC_WARN("%s: hcchar.chdis set unexpectedly, " "hcchar 0x%08x, trying to halt again\n", __func__, hcchar.d32); clear_hc_int(_hc_regs, chhltd); _hc->halt_pending = 0; halt_channel(_hcd, _hc, _qtd, _hc->halt_status); return 0; } return 1;}#endif/** * Handles a host Channel Halted interrupt in DMA mode. This handler * determines the reason the channel halted and proceeds accordingly. */static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * _hcd, dwc_hc_t * _hc, dwc_otg_hc_regs_t * _hc_regs, dwc_otg_qtd_t * _qtd){ hcint_data_t hcint; hcintmsk_data_t hcintmsk; if (_hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE || _hc->halt_status == DWC_OTG_HC_XFER_AHB_ERR) { /* * Just release the channel. A dequeue can happen on a * transfer timeout. In the case of an AHB Error, the channel * was forced to halt because there's no way to gracefully * recover. */ release_channel(_hcd, _hc, _qtd, _hc->halt_status); return; } /* Read the HCINTn register to determine the cause for the halt. */ hcint.d32 = dwc_read_reg32(&_hc_regs->hcint); hcintmsk.d32 = dwc_read_reg32(&_hc_regs->hcintmsk); if (hcint.b.xfercomp) { /** @todo This is here because of a possible hardware bug. Spec * says that on SPLIT-ISOC OUT transfers in DMA mode that a HALT * interrupt w/ACK bit set should occur, but I only see the * XFERCOMP bit, even with it masked out. This is a workaround * for that behavior. Should fix this when hardware is fixed. */ if ((_hc->ep_type == DWC_OTG_EP_TYPE_ISOC) && (!_hc->ep_is_in)) { handle_hc_ack_intr(_hcd, _hc, _hc_regs, _qtd); } handle_hc_xfercomp_intr(_hcd, _hc, _hc_regs, _qtd); _hcd->core_if->irq_hlt[0]++; } else if (hcint.b.stall) { handle_hc_stall_intr(_hcd, _hc, _hc_regs, _qtd); _hcd->core_if->irq_hlt[1]++; } else if (hcint.b.xacterr) { /* * Must handle xacterr before nak or ack. Could get a xacterr * at the same time as either of these on a BULK/CONTROL OUT * that started with a PING. The xacterr takes precedence. */ handle_hc_xacterr_intr(_hcd, _hc, _hc_regs, _qtd); _hcd->core_if->irq_hlt[2]++; } else if (hcint.b.nyet) { /* * Must handle nyet before nak or ack. Could get a nyet at the * same time as either of those on a BULK/CONTROL OUT that * started with a PING. The nyet takes precedence. */ handle_hc_nyet_intr(_hcd, _hc, _hc_regs, _qtd); _hcd->core_if->irq_hlt[3]++; } else if (hcint.b.bblerr) { handle_hc_babble_intr(_hcd, _hc, _hc_regs, _qtd); _hcd->core_if->irq_hlt[4]++; } else if (hcint.b.frmovrun) { handle_hc_frmovrun_intr(_hcd, _hc, _hc_regs, _qtd); _hcd->core_if->irq_hlt[5]++; } else if (hcint.b.nak && !hcintmsk.b.nak) { /* * If nak is not masked, it's because a non-split IN transfer * is in an error state. In that case, the nak is handled by * the nak interrupt handler, not here. Handle nak here for * BULK/CONTROL OUT transfers, which halt on a NAK to allow * rewinding the buffer pointer. */ handle_hc_nak_intr(_hcd, _hc, _hc_regs, _qtd); _hcd->core_if->irq_hlt[6]++; } else if (hcint.b.ack && !hcintmsk.b.ack) { /* * If ack is not masked, it's because a non-split IN transfer * is in an error state. In that case, the ack is handled by * the ack interrupt handler, not here. Handle ack here for * split transfers. Start splits halt on ACK. */ handle_hc_ack_intr(_hcd, _hc, _hc_regs, _qtd); _hcd->core_if->irq_hlt[7]++; } else { if (_hc->ep_type == DWC_OTG_EP_TYPE_INTR || _hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { /* * A periodic transfer halted with no other channel * interrupts set. Assume it was halted by the core * because it could not be completed in its scheduled * (micro)frame. */#ifdef DEBUG DWC_PRINT("%s: Halt channel %d (assume incomplete periodic transfer)\n", __func__, _hc->hc_num);#endif halt_channel(_hcd, _hc, _qtd, DWC_OTG_HC_XFER_PERIODIC_INCOMPLETE); } else { DWC_ERROR("%s: Channel %d, DMA Mode -- ChHltd set, but reason " "for halting is unknown, hcint 0x%08x, intsts 0x%08x\n", __func__, _hc->hc_num, hcint.d32, dwc_read_reg32(&_hcd->core_if->core_global_regs->gintsts)); } _hcd->core_if->irq_hlt[8]++; }}/** * Handles a host channel Channel Halted interrupt. * * In slave mode, this handler is called only when the driver specifically * requests a halt. This occurs during handling other host channel interrupts * (e.g. nak, xacterr, stall, nyet, etc.). * * In DMA mode, this is the interrupt that occurs when the core has finished * processing a transfer on a channel. Other host channel interrupts (except * ahberr) are disabled in DMA mode. */static inline int32_t handle_hc_chhltd_intr(dwc_otg_hcd_t * _hcd, dwc_hc_t * _hc, dwc_otg_hc_regs_t * _hc_regs, dwc_otg_qtd_t * _qtd){ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " "Channel Halted--\n", _hc->hc_num); if (_hcd->core_if->dma_enable) { handle_hc_chhltd_intr_dma(_hcd, _hc, _hc_regs, _qtd); } else {#ifdef DEBUG if (!halt_status_ok(_hcd, _hc, _hc_regs, _qtd)) { return 1; }#endif release_channel(_hcd, _hc, _qtd, _hc->halt_status); } return 1;}/** Handles interrupt for a specific Host Channel */int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * _dwc_otg_hcd, uint32_t _num){ int retval = 0; hcint_data_t hcint; hcintmsk_data_t hcintmsk; dwc_hc_t *hc; dwc_otg_hc_regs_t *hc_regs; dwc_otg_qtd_t *qtd; DWC_DEBUGPL(DBG_HCDV, "--Host Channel Interrupt--, Channel %d\n", _num); hc = _dwc_otg_hcd->hc_ptr_array[_num]; hc_regs = _dwc_otg_hcd->core_if->host_if->hc_regs[_num]; qtd = list_entry(hc->qh->qtd_list.next, dwc_otg_qtd_t, qtd_list_entry); hcint.d32 = dwc_read_reg32(&hc_regs->hcint); hcintmsk.d32 = dwc_read_reg32(&hc_regs->hcintmsk); DWC_DEBUGPL(DBG_HCDV, " hcint 0x%08x, hcintmsk 0x%08x, hcint&hcintmsk 0x%08x\n", hcint.d32, hcintmsk.d32, (hcint.d32 & hcintmsk.d32)); hcint.d32 = hcint.d32 & hcintmsk.d32; if (!_dwc_otg_hcd->core_if->dma_enable) { printk("DO NOT OCCUR.... ERROR in IRQ in dma enabled\n"); if ((hcint.b.chhltd) && (hcint.d32 != 0x2)) { hcint.b.chhltd = 0; } } if (hcint.b.xfercomp) { _dwc_otg_hcd->core_if->irq_hcd[0]++; retval |= handle_hc_xfercomp_intr(_dwc_otg_hcd, hc, hc_regs, qtd); /* * If NYET occurred at same time as Xfer Complete, the NYET is * handled by the Xfer Complete interrupt handler. Don't want * to call the NYET interrupt handler in this case. */ hcint.b.nyet = 0; } if (hcint.b.chhltd) { _dwc_otg_hcd->core_if->irq_hcd[1]++; retval |= handle_hc_chhltd_intr(_dwc_otg_hcd, hc, hc_regs, qtd); } if (hcint.b.ahberr) { _dwc_otg_hcd->core_if->irq_hcd[2]++; retval |= handle_hc_ahberr_intr(_dwc_otg_hcd, hc, hc_regs, qtd); } if (hcint.b.stall) { _dwc_otg_hcd->core_if->irq_hcd[3]++; retval |= handle_hc_stall_intr(_dwc_otg_hcd, hc, hc_regs, qtd); } if (hcint.b.nak) { _dwc_otg_hcd->core_if->irq_hcd[4]++; retval |= handle_hc_nak_intr(_dwc_otg_hcd, hc, hc_regs, qtd); } if (hcint.b.ack) { _dwc_otg_hcd->core_if->irq_hcd[5]++; retval |= handle_hc_ack_intr(_dwc_otg_hcd, hc, hc_regs, qtd); } if (hcint.b.nyet) { _dwc_otg_hcd->core_if->irq_hcd[6]++; retval |= handle_hc_nyet_intr(_dwc_otg_hcd, hc, hc_regs, qtd); } if (hcint.b.xacterr) { _dwc_otg_hcd->core_if->irq_hcd[7]++; retval |= handle_hc_xacterr_intr(_dwc_otg_hcd, hc, hc_regs, qtd); } if (hcint.b.bblerr) { _dwc_otg_hcd->core_if->irq_hcd[8]++; retval |= handle_hc_babble_intr(_dwc_otg_hcd, hc, hc_regs, qtd); } if (hcint.b.frmovrun) { _dwc_otg_hcd->core_if->irq_hcd[9]++; retval |= handle_hc_frmovrun_intr(_dwc_otg_hcd, hc, hc_regs, qtd); } if (hcint.b.datatglerr) { _dwc_otg_hcd->core_if->irq_hcd[10]++; retval |= handle_hc_datatglerr_intr(_dwc_otg_hcd, hc, hc_regs, qtd); } return retval;}#endif /* DWC_DEVICE_ONLY */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -