📄 dwc_otg_cil.c
字号:
hcchar.b.lspddev = (_hc->speed == DWC_OTG_EP_SPEED_LOW); hcchar.b.eptype = _hc->ep_type; hcchar.b.mps = _hc->max_packet; dwc_write_reg32(&host_if->hc_regs[hc_num]->hcchar, hcchar.d32); DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, _hc->hc_num); DWC_DEBUGPL(DBG_HCDV, " Dev Addr: %d\n", hcchar.b.devaddr); DWC_DEBUGPL(DBG_HCDV, " Ep Num: %d\n", hcchar.b.epnum); DWC_DEBUGPL(DBG_HCDV, " Is In: %d\n", hcchar.b.epdir); DWC_DEBUGPL(DBG_HCDV, " Is Low Speed: %d\n", hcchar.b.lspddev); DWC_DEBUGPL(DBG_HCDV, " Ep Type: %d\n", hcchar.b.eptype); DWC_DEBUGPL(DBG_HCDV, " Max Pkt: %d\n", hcchar.b.mps); DWC_DEBUGPL(DBG_HCDV, " Multi Cnt: %d\n", hcchar.b.multicnt); /* * Program the HCSPLIT register for SPLITs */ hcsplt.d32 = 0; if (_hc->do_split) { DWC_DEBUGPL(DBG_HCDV, "Programming HC %d with split --> %s\n", _hc->hc_num, _hc->complete_split ? "CSPLIT" : "SSPLIT"); hcsplt.b.compsplt = _hc->complete_split; hcsplt.b.xactpos = _hc->xact_pos; hcsplt.b.hubaddr = _hc->hub_addr; hcsplt.b.prtaddr = _hc->port_addr; DWC_DEBUGPL(DBG_HCDV, " comp split %d\n", _hc->complete_split); DWC_DEBUGPL(DBG_HCDV, " xact pos %d\n", _hc->xact_pos); DWC_DEBUGPL(DBG_HCDV, " hub addr %d\n", _hc->hub_addr); DWC_DEBUGPL(DBG_HCDV, " port addr %d\n", _hc->port_addr); DWC_DEBUGPL(DBG_HCDV, " is_in %d\n", _hc->ep_is_in); DWC_DEBUGPL(DBG_HCDV, " Max Pkt: %d\n", hcchar.b.mps); DWC_DEBUGPL(DBG_HCDV, " xferlen: %d\n", _hc->xfer_len); } dwc_write_reg32(&host_if->hc_regs[hc_num]->hcsplt, hcsplt.d32);}/** * Attempts to halt a host channel. This function should only be called in * Slave mode or to abort a transfer in either Slave mode or DMA mode. Under * normal circumstances in DMA mode, the controller halts the channel when the * transfer is complete or a condition occurs that requires application * intervention. * * In slave mode, checks for a free request queue entry, then sets the Channel * Enable and Channel Disable bits of the Host Channel Characteristics * register of the specified channel to intiate the halt. If there is no free * request queue entry, sets only the Channel Disable bit of the HCCHARn * register to flush requests for this channel. In the latter case, sets a * flag to indicate that the host channel needs to be halted when a request * queue slot is open. * * In DMA mode, always sets the Channel Enable and Channel Disable bits of the * HCCHARn register. The controller ensures there is space in the request * queue before submitting the halt request. * * Some time may elapse before the core flushes any posted requests for this * host channel and halts. The Channel Halted interrupt handler completes the * deactivation of the host channel. * * @param _core_if Controller register interface. * @param _hc Host channel to halt. * @param _halt_status Reason for halting the channel. */void dwc_otg_hc_halt(dwc_otg_core_if_t * _core_if, dwc_hc_t * _hc, dwc_otg_halt_status_e _halt_status){ gnptxsts_data_t nptxsts; hptxsts_data_t hptxsts; hcchar_data_t hcchar; dwc_otg_hc_regs_t *hc_regs; dwc_otg_core_global_regs_t *global_regs; dwc_otg_host_global_regs_t *host_global_regs; hc_regs = _core_if->host_if->hc_regs[_hc->hc_num]; global_regs = _core_if->core_global_regs; host_global_regs = _core_if->host_if->host_global_regs; WARN_ON(_halt_status == DWC_OTG_HC_XFER_NO_HALT_STATUS); if (_halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE || _halt_status == DWC_OTG_HC_XFER_AHB_ERR) { /* * Disable all channel interrupts except Ch Halted. The QTD * and QH state associated with this transfer has been cleared * (in the case of URB_DEQUEUE), so the channel needs to be * shut down carefully to prevent crashes. */ hcintmsk_data_t hcintmsk; hcintmsk.d32 = 0; hcintmsk.b.chhltd = 1; dwc_write_reg32(&hc_regs->hcintmsk, hcintmsk.d32); /* * Make sure no other interrupts besides halt are currently * pending. Handling another interrupt could cause a crash due * to the QTD and QH state. */ dwc_write_reg32(&hc_regs->hcint, ~hcintmsk.d32); /* * Make sure the halt status is set to URB_DEQUEUE or AHB_ERR * even if the channel was already halted for some other * reason. */ _hc->halt_status = _halt_status; hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); if (hcchar.b.chen == 0) { /* * The channel is either already halted or it hasn't * started yet. In DMA mode, the transfer may halt if * it finishes normally or a condition occurs that * requires driver intervention. Don't want to halt * the channel again. In either Slave or DMA mode, * it's possible that the transfer has been assigned * to a channel, but not started yet when an URB is * dequeued. Don't want to halt a channel that hasn't * started yet. */ return; } } if (_hc->halt_pending) { /* * A halt has already been issued for this channel. This might * happen when a transfer is aborted by a higher level in * the stack. */#ifdef DEBUG DWC_PRINT("*** %s: Channel %d, _hc->halt_pending already set ***\n", __func__, _hc->hc_num);/* dwc_otg_dump_global_registers(_core_if); *//* dwc_otg_dump_host_registers(_core_if); */#endif return; } hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); hcchar.b.chen = 1; hcchar.b.chdis = 1; if (!_core_if->dma_enable) { /* Check for space in the request queue to issue the halt. */ if (_hc->ep_type == DWC_OTG_EP_TYPE_CONTROL || _hc->ep_type == DWC_OTG_EP_TYPE_BULK) { nptxsts.d32 = dwc_read_reg32(&global_regs->gnptxsts); if (nptxsts.b.nptxqspcavail == 0) { hcchar.b.chen = 0; } } else { hptxsts.d32 = dwc_read_reg32(&host_global_regs->hptxsts); if ((hptxsts.b.ptxqspcavail == 0) || (_core_if->queuing_high_bandwidth)) { hcchar.b.chen = 0; } } } dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); _hc->halt_status = _halt_status; if (hcchar.b.chen) { _hc->halt_pending = 1; _hc->halt_on_queue = 0; } else { _hc->halt_on_queue = 1; } DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, _hc->hc_num); DWC_DEBUGPL(DBG_HCDV, " hcchar: 0x%08x\n", hcchar.d32); DWC_DEBUGPL(DBG_HCDV, " halt_pending: %d\n", _hc->halt_pending); DWC_DEBUGPL(DBG_HCDV, " halt_on_queue: %d\n", _hc->halt_on_queue); DWC_DEBUGPL(DBG_HCDV, " halt_status: %d\n", _hc->halt_status); return;}/** * Clears the transfer state for a host channel. This function is normally * called after a transfer is done and the host channel is being released. * * @param _core_if Programming view of DWC_otg controller. * @param _hc Identifies the host channel to clean up. */void dwc_otg_hc_cleanup(dwc_otg_core_if_t * _core_if, dwc_hc_t * _hc){ dwc_otg_hc_regs_t *hc_regs; _hc->xfer_started = 0; /* * Clear channel interrupt enables and any unhandled channel interrupt * conditions. */ hc_regs = _core_if->host_if->hc_regs[_hc->hc_num]; dwc_write_reg32(&hc_regs->hcintmsk, 0); dwc_write_reg32(&hc_regs->hcint, 0xFFFFFFFF);#ifdef DEBUG del_timer(&_core_if->hc_xfer_timer[_hc->hc_num]); { hcchar_data_t hcchar; hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); if (hcchar.b.chdis) { DWC_WARN("%s: chdis set, channel %d, hcchar 0x%08x\n", __func__, _hc->hc_num, hcchar.d32); } }#endif}/** * Sets the channel property that indicates in which frame a periodic transfer * should occur. This is always set to the _next_ frame. This function has no * effect on non-periodic transfers. * * @param _core_if Programming view of DWC_otg controller. * @param _hc Identifies the host channel to set up and its properties. * @param _hcchar Current value of the HCCHAR register for the specified host * channel. */static inline void hc_set_even_odd_frame(dwc_otg_core_if_t * _core_if, dwc_hc_t * _hc, hcchar_data_t * _hcchar){ if (_hc->ep_type == DWC_OTG_EP_TYPE_INTR || _hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { hfnum_data_t hfnum; hfnum.d32 = dwc_read_reg32(&_core_if->host_if->host_global_regs->hfnum); /* 1 if _next_ frame is odd, 0 if it's even */ _hcchar->b.oddfrm = (hfnum.b.frnum & 0x1) ? 0 : 1;#ifdef DEBUG if (_hc->ep_type == DWC_OTG_EP_TYPE_INTR && _hc->do_split && !_hc->complete_split) { switch (hfnum.b.frnum & 0x7) { case 7: _core_if->hfnum_7_samples++; _core_if->hfnum_7_frrem_accum += hfnum.b.frrem; break; case 0: _core_if->hfnum_0_samples++; _core_if->hfnum_0_frrem_accum += hfnum.b.frrem; break; default: _core_if->hfnum_other_samples++; _core_if->hfnum_other_frrem_accum += hfnum.b.frrem; break; } }#endif }}#ifdef DEBUGstatic void hc_xfer_timeout(unsigned long _ptr){ hc_xfer_info_t *xfer_info = (hc_xfer_info_t *) _ptr; int hc_num = xfer_info->hc->hc_num; DWC_WARN("%s: timeout on channel %d\n", __func__, hc_num); DWC_WARN(" start_hcchar_val 0x%08x\n", xfer_info->core_if->start_hcchar_val[hc_num]);}#endif/* * This function does the setup for a data transfer for a host channel and * starts the transfer. May be called in either Slave mode or DMA mode. In * Slave mode, the caller must ensure that there is sufficient space in the * request queue and Tx Data FIFO. * * For an OUT transfer in Slave mode, it loads a data packet into the * appropriate FIFO. If necessary, additional data packets will be loaded in * the Host ISR. * * For an IN transfer in Slave mode, a data packet is requested. The data * packets are unloaded from the Rx FIFO in the Host ISR. If necessary, * additional data packets are requested in the Host ISR. * * For a PING transfer in Slave mode, the Do Ping bit is set in the HCTSIZ * register along with a packet count of 1 and the channel is enabled. This * causes a single PING transaction to occur. Other fields in HCTSIZ are * simply set to 0 since no data transfer occurs in this case. * * For a PING transfer in DMA mode, the HCTSIZ register is initialized with * all the information required to perform the subsequent data transfer. In * addition, the Do Ping bit is set in the HCTSIZ register. In this case, the * controller performs the entire PING protocol, then starts the data * transfer. * * @param _core_if Programming view of DWC_otg controller. * @param _hc Information needed to initialize the host channel. The xfer_len * value may be reduced to accommodate the max widths of the XferSize and * PktCnt fields in the HCTSIZn register. The multi_count value may be changed * to reflect the final xfer_len value. */void dwc_otg_hc_start_transfer(dwc_otg_core_if_t * _core_if, dwc_hc_t * _hc){ hcchar_data_t hcchar; hctsiz_data_t hctsiz; uint16_t num_packets; uint32_t max_hc_xfer_size = _core_if->core_params->max_transfer_size; uint16_t max_hc_pkt_count = _core_if->core_params->max_packet_count; dwc_otg_hc_regs_t *hc_regs = _core_if->host_if->hc_regs[_hc->hc_num]; hctsiz.d32 = 0;#if 0 // orig if (_hc->do_ping) { if (!_core_if->dma_enable) { dwc_otg_hc_do_ping(_core_if, _hc); _hc->xfer_started = 1; return; } else { hctsiz.b.dopng = 1; } }#else if (_hc->do_ping) { if (likely(_core_if->dma_enable)) { hctsiz.b.dopng = 1; } else { dwc_otg_hc_do_ping(_core_if, _hc); _hc->xfer_started = 1; return; } }#endif /* * split attribute is generally used in USB 1.1 spec * When you want to use USB 2.0 HIGH speed this is unnecessary. * by scsuh */ if (unlikely(_hc->do_split)) { num_packets = 1; if (_hc->complete_split && !_hc->ep_is_in) { /* For CSPLIT OUT Transfer, set the size to 0 so the * core doesn't expect any data written to the FIFO */ _hc->xfer_len = 0; } else if (_hc->ep_is_in || (_hc->xfer_len > _hc->max_packet)) { _hc->xfer_len = _hc->max_packet; } else if (!_hc->ep_is_in && (_hc->xfer_len > 188)) { _hc->xfer_len = 188; } hctsiz.b.xfersize = _hc->xfer_len; } else { /* * Ensure that the transfer length and packet count will fit * in the widths allocated for them in the HCTSIZn register. */ if (_hc->ep_type == DWC_OTG_EP_TYPE_INTR || _hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { /* * Make sure the transfer size is no larger than one * (micro)frame's worth of data. (A check was done * when the periodic transfer was accepted to ensure * that a (micro)frame's worth of data can be * programmed into a channel.) */ uint32_t max_periodic_len = _hc->multi_count * _hc->max_packet; if (_hc->xfer_len > max_periodic_len) { _hc->xfer_len = max_periodic_len; } else { } } else if (_hc->xfer_len > max_hc_xfer_size) { /* Make sure that xfer_len is a multiple of max packet size. */ _hc->xfer_len = max_hc_xfer_size - _hc->max_packet + 1; } if (_hc->xfer_len > 0) { num_packets = (_hc->xfer_len + _hc->max_packet - 1) / _hc->max_packet; if (num_packets > max_hc_pkt_count) { num_packets = max_hc_pkt_count; _hc->xfer_len = num_packets * _hc->max_packet; } } else { /* Need 1 packet for transfer length of 0. */ num_packets = 1; } if (_hc->ep_is_in) { /* Always program an integral # of max packets for IN transfers. */ _hc->xfer_len = num_packets * _hc->max_packet; } if (_hc->ep_type == DWC_OTG_EP_TYPE_INTR || _hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { /* * Make sure that the multi_count field matches the * actual transfer length. */ _hc->multi_count = num_packets; } if (_hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { /* Set up the initial PID for the transfer. */ if (_hc->speed == DWC_OTG_EP_SPEED_HIGH) { if (_hc->ep_is_in) { if (_hc->multi_count == 1) { _hc->data_pid_start = DWC_OTG_HC_PID_DATA0; } else if (_hc->multi_count == 2) { _hc->data_pid_start = DWC_OTG_HC_PID_DATA1; } else { _hc->data_pid_start = DWC_OTG_HC_PID_DATA2; } } else { if (_hc->multi_count == 1) { _hc->data_pid_start = DWC_OTG_HC_PID_DATA0; } else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -