📄 dwc_otg_cil.c
字号:
_hc->data_pid_start = DWC_OTG_HC_PID_MDATA; } } } else { _hc->data_pid_start = DWC_OTG_HC_PID_DATA0; } } hctsiz.b.xfersize = _hc->xfer_len; } _hc->start_pkt_count = num_packets; hctsiz.b.pktcnt = num_packets; hctsiz.b.pid = _hc->data_pid_start; dwc_write_reg32(&hc_regs->hctsiz, hctsiz.d32); dbg_otg("%s: Channel %d\n", __func__, _hc->hc_num); dbg_otg(" Xfer Size: %d\n", hctsiz.b.xfersize); dbg_otg(" Num Pkts: %d\n", hctsiz.b.pktcnt); dbg_otg(" Start PID: %d\n", hctsiz.b.pid); if (_core_if->dma_enable) { dwc_write_reg32(&hc_regs->hcdma, (uint32_t) _hc->xfer_buff); } /* Start the split */ if (unlikely(_hc->do_split)) { hcsplt_data_t hcsplt; hcsplt.d32 = dwc_read_reg32(&hc_regs->hcsplt); hcsplt.b.spltena = 1; dwc_write_reg32(&hc_regs->hcsplt, hcsplt.d32); } hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); hcchar.b.multicnt = _hc->multi_count; hc_set_even_odd_frame(_core_if, _hc, &hcchar);#ifdef DEBUG _core_if->start_hcchar_val[_hc->hc_num] = hcchar.d32; if (hcchar.b.chdis) { DWC_WARN("%s: chdis set, channel %d, hcchar 0x%08x\n", __func__, _hc->hc_num, hcchar.d32); }#endif /* Set host channel enable after all other setup is complete. */ hcchar.b.chen = 1; hcchar.b.chdis = 0; printk("%s() - %08x\n", __FUNCTION__, hcchar.d32); dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); _hc->xfer_started = 1; _hc->requests++; if (unlikely(!_core_if->dma_enable && !_hc->ep_is_in && _hc->xfer_len > 0)) { /* Load OUT packet into the appropriate Tx FIFO. */ dwc_otg_hc_write_packet(_core_if, _hc); }#ifdef DEBUG /* Start a timer for this transfer. */ _core_if->hc_xfer_timer[_hc->hc_num].function = hc_xfer_timeout; _core_if->hc_xfer_info[_hc->hc_num].core_if = _core_if; _core_if->hc_xfer_info[_hc->hc_num].hc = _hc; _core_if->hc_xfer_timer[_hc->hc_num].data = (unsigned long) (&_core_if->hc_xfer_info[_hc->hc_num]); _core_if->hc_xfer_timer[_hc->hc_num].expires = jiffies + (HZ * 10); add_timer(&_core_if->hc_xfer_timer[_hc->hc_num]);#endif}/** * This function continues a data transfer that was started by previous call * to <code>dwc_otg_hc_start_transfer</code>. The caller must ensure there is * sufficient space in the request queue and Tx Data FIFO. This function * should only be called in Slave mode. In DMA mode, the controller acts * autonomously to complete transfers programmed to a host channel. * * For an OUT transfer, a new data packet is loaded into the appropriate FIFO * if there is any data remaining to be queued. For an IN transfer, another * data packet is always requested. For the SETUP phase of a control transfer, * this function does nothing. * * @return 1 if a new request is queued, 0 if no more requests are required * for this transfer. */int dwc_otg_hc_continue_transfer(dwc_otg_core_if_t * _core_if, dwc_hc_t * _hc){ dbg_otg("%s: Channel %d\n", __func__, _hc->hc_num); if (_hc->do_split) { /* SPLITs always queue just once per channel */ return 0; } else if (_hc->data_pid_start == DWC_OTG_HC_PID_SETUP) { /* SETUPs are queued only once since they can't be NAKed. */ return 0; } else if (_hc->ep_is_in) { /* * Always queue another request for other IN transfers. If * back-to-back INs are issued and NAKs are received for both, * the driver may still be processing the first NAK when the * second NAK is received. When the interrupt handler clears * the NAK interrupt for the first NAK, the second NAK will * not be seen. So we can't depend on the NAK interrupt * handler to requeue a NAKed request. Instead, IN requests * are issued each time this function is called. When the * transfer completes, the extra requests for the channel will * be flushed. */ hcchar_data_t hcchar; dwc_otg_hc_regs_t *hc_regs = _core_if->host_if->hc_regs[_hc->hc_num]; hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); hc_set_even_odd_frame(_core_if, _hc, &hcchar); hcchar.b.chen = 1; hcchar.b.chdis = 0; DWC_DEBUGPL(DBG_HCDV, " IN xfer: hcchar = 0x%08x\n", hcchar.d32); dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); _hc->requests++; return 1; } else { /* OUT transfers. */ if (_hc->xfer_count < _hc->xfer_len) { if (_hc->ep_type == DWC_OTG_EP_TYPE_INTR || _hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { hcchar_data_t hcchar; dwc_otg_hc_regs_t *hc_regs; hc_regs = _core_if->host_if->hc_regs[_hc->hc_num]; hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); hc_set_even_odd_frame(_core_if, _hc, &hcchar); } /* Load OUT packet into the appropriate Tx FIFO. */ dwc_otg_hc_write_packet(_core_if, _hc); _hc->requests++; return 1; } else { return 0; } }}/** * Starts a PING transfer. This function should only be called in Slave mode. * The Do Ping bit is set in the HCTSIZ register, then the channel is enabled. */void dwc_otg_hc_do_ping(dwc_otg_core_if_t * _core_if, dwc_hc_t * _hc){ hcchar_data_t hcchar; hctsiz_data_t hctsiz; dwc_otg_hc_regs_t *hc_regs = _core_if->host_if->hc_regs[_hc->hc_num]; DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, _hc->hc_num); hctsiz.d32 = 0; hctsiz.b.dopng = 1; hctsiz.b.pktcnt = 1; dwc_write_reg32(&hc_regs->hctsiz, hctsiz.d32); hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); hcchar.b.chen = 1; hcchar.b.chdis = 0; dwc_write_reg32(&hc_regs->hcchar, hcchar.d32);}/* * This function writes a packet into the Tx FIFO associated with the Host * Channel. For a channel associated with a non-periodic EP, the non-periodic * Tx FIFO is written. For a channel associated with a periodic EP, the * periodic Tx FIFO is written. This function should only be called in Slave * mode. * * Upon return the xfer_buff and xfer_count fields in _hc are incremented by * then number of bytes written to the Tx FIFO. */void dwc_otg_hc_write_packet(dwc_otg_core_if_t * _core_if, dwc_hc_t * _hc){ uint32_t i; uint32_t remaining_count; uint32_t byte_count; uint32_t dword_count; uint32_t *data_buff = (uint32_t *) (_hc->xfer_buff); uint32_t *data_fifo = _core_if->data_fifo[_hc->hc_num]; remaining_count = _hc->xfer_len - _hc->xfer_count; if (remaining_count > _hc->max_packet) { byte_count = _hc->max_packet; } else { byte_count = remaining_count; } dword_count = (byte_count + 3) / 4; if ((((unsigned long) data_buff) & 0x3) == 0) { /* xfer_buff is DWORD aligned. */ for (i = 0; i < dword_count; i++, data_buff++) { dwc_write_reg32(data_fifo, *data_buff); } } else { /* xfer_buff is not DWORD aligned. */ for (i = 0; i < dword_count; i++, data_buff++) { dwc_write_reg32(data_fifo, get_unaligned(data_buff)); } } _hc->xfer_count += byte_count; _hc->xfer_buff += byte_count;}/** * Gets the current USB frame number. This is the frame number from the last * SOF packet. */uint32_t dwc_otg_get_frame_number(dwc_otg_core_if_t * _core_if){ dsts_data_t dsts; dsts.d32 = dwc_read_reg32(&_core_if->dev_if->dev_global_regs->dsts); /* read current frame/microframe number from DSTS register */ return dsts.b.soffn;}/** * This function reads a setup packet from the Rx FIFO into the destination * buffer. This function is called from the Rx Status Queue Level (RxStsQLvl) * Interrupt routine when a SETUP packet has been received in Slave mode. * * @param _core_if Programming view of DWC_otg controller. * @param _dest Destination buffer for packet data. */void dwc_otg_read_setup_packet(dwc_otg_core_if_t * _core_if, uint32_t * _dest){ /* Get the 8 bytes of a setup transaction data */ /* Pop 2 DWORDS off the receive data FIFO into memory */ _dest[0] = dwc_read_reg32(_core_if->data_fifo[0]); _dest[1] = dwc_read_reg32(_core_if->data_fifo[0]);}/** * This function enables EP0 OUT to receive SETUP packets and configures EP0 * IN for transmitting packets. It is normally called when the * "Enumeration Done" interrupt occurs. * * @param _core_if Programming view of DWC_otg controller. * @param _ep The EP0 data. */void dwc_otg_ep0_activate(dwc_otg_core_if_t * _core_if, dwc_ep_t * _ep){ dwc_otg_dev_if_t *dev_if = _core_if->dev_if; dsts_data_t dsts; depctl_data_t diepctl; depctl_data_t doepctl; dctl_data_t dctl = {.d32 = 0 }; /* Read the Device Status and Endpoint 0 Control registers */ dsts.d32 = dwc_read_reg32(&dev_if->dev_global_regs->dsts); diepctl.d32 = dwc_read_reg32(&dev_if->in_ep_regs[0]->diepctl); doepctl.d32 = dwc_read_reg32(&dev_if->out_ep_regs[0]->doepctl); /* Set the MPS of the IN EP based on the enumeration speed */ switch (dsts.b.enumspd) { case DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ: case DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ: case DWC_DSTS_ENUMSPD_FS_PHY_48MHZ: diepctl.b.mps = DWC_DEP0CTL_MPS_64; break; case DWC_DSTS_ENUMSPD_LS_PHY_6MHZ: diepctl.b.mps = DWC_DEP0CTL_MPS_8; break; } dwc_write_reg32(&dev_if->in_ep_regs[0]->diepctl, diepctl.d32); /* Enable OUT EP for receive */ doepctl.b.epena = 1; dwc_write_reg32(&dev_if->out_ep_regs[0]->doepctl, doepctl.d32);#ifdef VERBOSE DWC_DEBUGPL(DBG_PCDV, "doepctl0=%0x\n", dwc_read_reg32(&dev_if->out_ep_regs[0]->doepctl)); DWC_DEBUGPL(DBG_PCDV, "diepctl0=%0x\n", dwc_read_reg32(&dev_if->in_ep_regs[0]->diepctl));#endif dctl.b.cgnpinnak = 1; dwc_modify_reg32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32); DWC_DEBUGPL(DBG_PCDV, "dctl=%0x\n", dwc_read_reg32(&dev_if->dev_global_regs->dctl));}/** * This function activates an EP. The Device EP control register for * the EP is configured as defined in the ep structure. Note: This * function is not used for EP0. * * @param _core_if Programming view of DWC_otg controller. * @param _ep The EP to activate. */void dwc_otg_ep_activate(dwc_otg_core_if_t * _core_if, dwc_ep_t * _ep){ dwc_otg_dev_if_t *dev_if = _core_if->dev_if; depctl_data_t depctl; volatile uint32_t *addr; daint_data_t daintmsk = {.d32 = 0 }; DWC_DEBUGPL(DBG_PCDV, "%s() EP%d-%s\n", __func__, _ep->num, (_ep->is_in ? "IN" : "OUT")); /* Read DEPCTLn register */ if (_ep->is_in == 1) { addr = &dev_if->in_ep_regs[_ep->num]->diepctl; daintmsk.ep.in = 1 << _ep->num; } else { addr = &dev_if->out_ep_regs[_ep->num]->doepctl; daintmsk.ep.out = 1 << _ep->num; } /* If the EP is already active don't change the EP Control * register. */ depctl.d32 = dwc_read_reg32(addr); if (!depctl.b.usbactep) { depctl.b.mps = _ep->maxpacket; depctl.b.eptype = _ep->type; depctl.b.txfnum = _ep->tx_fifo_num; if (_ep->type == DWC_OTG_EP_TYPE_ISOC) { depctl.b.setd0pid = 1; // ??? } else { depctl.b.setd0pid = 1; } depctl.b.usbactep = 1; dwc_write_reg32(addr, depctl.d32); DWC_DEBUGPL(DBG_PCDV, "DEPCTL=%08x\n", dwc_read_reg32(addr)); } /* Enable the Interrupt for this EP */ dwc_modify_reg32(&dev_if->dev_global_regs->daintmsk, 0, daintmsk.d32); DWC_DEBUGPL(DBG_PCDV, "DAINTMSK=%0x\n", dwc_read_reg32(&dev_if->dev_global_regs->daintmsk)); _ep->stall_clear_flag = 0; return;}/** * This function deactivates an EP. This is done by clearing the USB Active * EP bit in the Device EP control register. Note: This function is not used * for EP0. EP0 cannot be deactivated. * * @param _core_if Programming view of DWC_otg controller. * @param _ep The EP to deactivate. */void dwc_otg_ep_deactivate(dwc_otg_core_if_t * _core_if, dwc_ep_t * _ep){ depctl_data_t depctl = {.d32 = 0 }; volatile uint32_t *addr; daint_data_t daintmsk = {.d32 = 0 }; /* Read DEPCTLn register */ if (_ep->is_in == 1) { addr = &_core_if->dev_if->in_ep_regs[_ep->num]->diepctl; daintmsk.ep.in = 1 << _ep->num; } else { addr = &_core_if->dev_if->out_ep_regs[_ep->num]->doepctl; daintmsk.ep.out = 1 << _ep->num; } depctl.b.usbactep = 0; if (_core_if->dma_desc_enable) depctl.b.epdis = 1; dwc_write_reg32(addr, depctl.d32); /* Disable the Interrupt for this EP */ dwc_modify_reg32(&_core_if->dev_if->dev_global_regs->daintmsk, daintmsk.d32, 0); return;}/** * This function does the setup for a data transfer for an EP and * starts the transfer. For an IN transfer, the packets will be * loaded into the appropriate Tx FIFO in the ISR. For OUT transfers, * the packets are unloaded from the Rx FIFO in the ISR. the ISR. * * @param _core_if Programming view of DWC_otg controller. * @param _ep The EP to start the transfer on. * FOR PCD only. by scsuh */void dwc_otg_ep_start_transfer(dwc_otg_core_if_t * _core_if, dwc_ep_t * _ep){ /** @todo Refactor this funciton to check the transfer size * count value does not execed the number bits in the Transfer * count register. */ depctl_data_t depctl; deptsiz_data_t deptsiz; gintmsk_data_t intr_mask = {.d32 = 0 }; dwc_otg_dma_desc_t *dma_desc; dbg_otg("%s()\n", __func__); dbg_otg("ep%d-%s xfer_len=%d xfer_cnt=%d " "xfer_buff=%p start_xfer_buff=%p\n", _ep->num, (_ep->is_in ? "IN" : "OUT"), _ep->xfer_len, _ep->xfer_count, _ep->xf
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -