/***************************************************************************** ** FILE NAME : ifxhcd_intr.c ** PROJECT : IFX USB sub-system V3 ** MODULES : IFX USB sub-system Host and Device driver ** SRC VERSION : 3.2 ** DATE : 1/Jan/2011 ** AUTHOR : Chen, Howard ** DESCRIPTION : This file contains the implementation of the HCD Interrupt handlers. ** FUNCTIONS : ** COMPILER : gcc ** REFERENCE : Synopsys DWC-OTG Driver 2.7 ** COPYRIGHT : Copyright (c) 2010 ** LANTIQ DEUTSCHLAND GMBH, ** Am Campeon 3, 85579 Neubiberg, Germany ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** Version Control Section ** ** $Author$ ** $Date$ ** $Revisions$ ** $Log$ Revision history *****************************************************************************/ /* * This file contains code fragments from Synopsys HS OTG Linux Software Driver. * For this code the following notice is applicable: * * ========================================================================== * * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless * otherwise expressly agreed to in writing between Synopsys and you. * * The Software IS NOT an item of Licensed Software or Licensed Product under * any End User Software License Agreement or Agreement for Licensed Product * with Synopsys or any supplement thereto. You are permitted to use and * redistribute this Software in source and binary forms, with or without * modification, provided that redistributions of source code must retain this * notice. You may not view, use, disclose, copy or distribute this file or * any information contained herein except pursuant to this license grant from * Synopsys. If you do not agree with this notice, including the disclaimer * below, then you are not authorized to use the Software. * * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * ========================================================================== */ /*! \file ifxhcd_intr.c \ingroup IFXUSB_DRIVER_V3 \brief This file contains the implementation of the HCD Interrupt handlers. */ #include #include "ifxusb_version.h" #include "ifxusb_plat.h" #include "ifxusb_regs.h" #include "ifxusb_cif.h" #include "ifxhcd.h" /* Macro used to clear one channel interrupt */ #define clear_hc_int(_hc_regs_,_intr_) \ do { \ hcint_data_t hcint_clear = {.d32 = 0}; \ hcint_clear.b._intr_ = 1; \ ifxusb_wreg(&((_hc_regs_)->hcint), hcint_clear.d32); \ } while (0) /* * Macro used to disable one channel interrupt. Channel interrupts are * disabled when the channel is halted or released by the interrupt handler. * There is no need to handle further interrupts of that type until the * channel is re-assigned. In fact, subsequent handling may cause crashes * because the channel structures are cleaned up when the channel is released. */ #define disable_hc_int(_hc_regs_,_intr_) \ do { \ hcint_data_t hcintmsk = {.d32 = 0}; \ hcintmsk.b._intr_ = 1; \ ifxusb_mreg(&((_hc_regs_)->hcintmsk), hcintmsk.d32, 0); \ } while (0) #define enable_hc_int(_hc_regs_,_intr_) \ do { \ hcint_data_t hcintmsk = {.d32 = 0}; \ hcintmsk.b._intr_ = 1; \ ifxusb_mreg(&((_hc_regs_)->hcintmsk),0, hcintmsk.d32); \ } while (0) /* * Save the starting data toggle for the next transfer. The data toggle is * saved in the QH for non-control transfers and it's saved in the QTD for * control transfers. */ uint8_t read_data_toggle(ifxusb_hc_regs_t *_hc_regs) { hctsiz_data_t hctsiz; hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); return(hctsiz.b.pid); } static void release_channel_dump(ifxhcd_hc_t *ifxhc, struct urb *urb, ifxhcd_epqh_t *epqh, ifxhcd_urbd_t *urbd, ifxhcd_halt_status_e halt_status) { #ifdef __DEBUG__ printk(KERN_INFO); switch (halt_status) { case HC_XFER_NO_HALT_STATUS: printk("HC_XFER_NO_HALT_STATUS");break; case HC_XFER_URB_COMPLETE: printk("HC_XFER_URB_COMPLETE");break; case HC_XFER_AHB_ERR: printk("HC_XFER_AHB_ERR");break; case HC_XFER_STALL: printk("HC_XFER_STALL");break; case HC_XFER_BABBLE_ERR: printk("HC_XFER_BABBLE_ERR");break; case HC_XFER_XACT_ERR: printk("HC_XFER_XACT_ERR");break; case HC_XFER_URB_DEQUEUE: printk("HC_XFER_URB_DEQUEUE");break; case HC_XFER_FRAME_OVERRUN: printk("HC_XFER_FRAME_OVERRUN");break; case HC_XFER_DATA_TOGGLE_ERR: printk("HC_XFER_DATA_TOGGLE_ERR");break; #ifdef __NAKSTOP__ case HC_XFER_NAK: printk("HC_XFER_NAK");break; #endif case HC_XFER_COMPLETE: printk("HC_XFER_COMPLETE");break; default: printk("KNOWN");break; } if(ifxhc) printk("Ch %d %s%s S%d " , ifxhc->hc_num ,(ifxhc->ep_type == IFXUSB_EP_TYPE_CTRL)?"CTRL-": ((ifxhc->ep_type == IFXUSB_EP_TYPE_BULK)?"BULK-": ((ifxhc->ep_type == IFXUSB_EP_TYPE_INTR)?"INTR-": ((ifxhc->ep_type == IFXUSB_EP_TYPE_ISOC)?"ISOC-":"????" ) ) ) ,(ifxhc->is_in)?"IN":"OUT" ,(ifxhc->split) ); else printk(" [NULL HC] "); printk("urb=%p epqh=%p urbd=%p\n",urb,epqh,urbd); if(urb) { printk(KERN_INFO " Device address: %d\n", usb_pipedevice(urb->pipe)); printk(KERN_INFO " Endpoint: %d, %s\n", usb_pipeendpoint(urb->pipe), (usb_pipein(urb->pipe) ? "IN" : "OUT")); printk(KERN_INFO " Endpoint type: %s\n", ({char *pipetype; switch (usb_pipetype(urb->pipe)) { case PIPE_CONTROL: pipetype = "CTRL"; break; case PIPE_BULK: pipetype = "BULK"; break; case PIPE_INTERRUPT: pipetype = "INTR"; break; case PIPE_ISOCHRONOUS: pipetype = "ISOC"; break; default: pipetype = "????"; break; }; pipetype;})); printk(KERN_INFO " Speed: %s\n", ({char *speed; switch (urb->dev->speed) { case USB_SPEED_HIGH: speed = "HS"; break; case USB_SPEED_FULL: speed = "FS"; break; case USB_SPEED_LOW: speed = "LS"; break; default: speed = "????"; break; }; speed;})); printk(KERN_INFO " Max packet size: %d\n", usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))); printk(KERN_INFO " Data buffer length: %d/%d\n",urb->actual_length, urb->transfer_buffer_length); printk(KERN_INFO " Transfer buffer: %p, Transfer DMA: %p\n", urb->transfer_buffer, (void *)urb->transfer_dma); printk(KERN_INFO " Setup buffer: %p, Setup DMA: %p\n", urb->setup_packet, (void *)urb->setup_dma); printk(KERN_INFO " Interval: %d\n", urb->interval); } if(urbd) { switch (urbd->status) { case HC_XFER_NO_HALT_STATUS: printk(KERN_INFO " STATUS:HC_XFER_NO_HALT_STATUS\n");break; case HC_XFER_URB_COMPLETE: printk(KERN_INFO " STATUS:HC_XFER_URB_COMPLETE\n");break; case HC_XFER_AHB_ERR: printk(KERN_INFO " STATUS:HC_XFER_AHB_ERR\n");break; case HC_XFER_STALL: printk(KERN_INFO " STATUS:HC_XFER_STALL\n");break; case HC_XFER_BABBLE_ERR: printk(KERN_INFO " STATUS:HC_XFER_BABBLE_ERR\n");break; case HC_XFER_XACT_ERR: printk(KERN_INFO " STATUS:HC_XFER_XACT_ERR\n");break; case HC_XFER_URB_DEQUEUE: printk(KERN_INFO " STATUS:HC_XFER_URB_DEQUEUE\n");break; case HC_XFER_FRAME_OVERRUN: printk(KERN_INFO " STATUS:HC_XFER_FRAME_OVERRUN\n");break; case HC_XFER_DATA_TOGGLE_ERR: printk(KERN_INFO " STATUS:HC_XFER_DATA_TOGGLE_ERR\n");break; case HC_XFER_COMPLETE: printk(KERN_INFO " STATUS:HC_XFER_COMPLETE\n");break; default: printk(KERN_INFO " STATUS:UNKKNOWN %d\n",urbd->status);break; } } #endif } /*! \fn static void release_channel(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxhcd_halt_status_e _halt_status) \brief Release the halted channel. \param _ifxhcd Pointer to the sate of HCD structure \param _ifxhc Pointer to host channel descriptor \param _halt_status Halt satus \return None \ingroup IFXUSB_HCD */ static void release_channel(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxhcd_halt_status_e _halt_status) { ifxusb_hc_regs_t *hc_regs = _ifxhcd->core_if.hc_regs[_ifxhc->hc_num]; struct urb *urb = NULL; ifxhcd_epqh_t *epqh = NULL; ifxhcd_urbd_t *urbd = NULL; IFX_DEBUGPL(DBG_HCDV, " %s: channel %d, halt_status %d\n", __func__, _ifxhc->hc_num, _halt_status); epqh=_ifxhc->epqh; if(!epqh) { if(_halt_status!=HC_XFER_NO_EPQH) IFX_ERROR("%s epqh=null\n",__func__); } else { urbd=epqh->urbd; if(!urbd) IFX_ERROR("%s urbd=null\n",__func__); else { urb=urbd->urb; if(!urb) { if(_halt_status!=HC_XFER_NO_URB) IFX_ERROR("%s urb =null\n",__func__); } else { if (read_data_toggle(hc_regs) == IFXUSB_HCTSIZ_DATA0) usb_settoggle (urb->dev,usb_pipeendpoint (urb->pipe), (_ifxhc->is_in)?0:1,0); else if (read_data_toggle(hc_regs) == IFXUSB_HCTSIZ_DATA1) usb_settoggle (urb->dev,usb_pipeendpoint (urb->pipe), (_ifxhc->is_in)?0:1,1); } } } switch (_halt_status) { case HC_XFER_NO_HALT_STATUS: IFX_ERROR("%s: No halt_status, channel %d\n", __func__, _ifxhc->hc_num); // return; break; case HC_XFER_COMPLETE: IFX_ERROR("%s: Inavalid halt_status HC_XFER_COMPLETE, channel %d\n", __func__, _ifxhc->hc_num); // return; break; case HC_XFER_NO_URB: break; case HC_XFER_NO_EPQH: break; case HC_XFER_URB_DEQUEUE: case HC_XFER_AHB_ERR: case HC_XFER_XACT_ERR: case HC_XFER_FRAME_OVERRUN: if(urbd && urb) { urbd->phase=URBD_DEQUEUEING; ifxhcd_complete_urb(_ifxhcd, urbd, urbd->status); } else { IFX_WARN("WARNING %s():%d urbd=%p urb=%p\n",__func__,__LINE__,urbd,urb); release_channel_dump(_ifxhc,urb,epqh,urbd,_halt_status); } break; case HC_XFER_URB_COMPLETE: if(urbd && urb) { urbd->phase=URBD_COMPLETING; ifxhcd_complete_urb(_ifxhcd, urbd, urbd->status); } else { IFX_WARN("WARNING %s():%d urbd=%p urb=%p\n",__func__,__LINE__,urbd,urb); release_channel_dump(_ifxhc,urb,epqh,urbd,_halt_status); } break; case HC_XFER_STALL: if(urbd) { urbd->phase=URBD_DEQUEUEING; ifxhcd_complete_urb(_ifxhcd, urbd, -EPIPE); } else { IFX_WARN("WARNING %s():%d urbd=%p urb=%p\n",__func__,__LINE__,urbd,urb); release_channel_dump(_ifxhc,urb,epqh,urbd,_halt_status); } if(epqh && urb && urb->dev && urb->pipe) usb_settoggle(urb->dev, usb_pipeendpoint (urb->pipe), !usb_pipein(urb->pipe), IFXUSB_HC_PID_DATA0); break; case HC_XFER_BABBLE_ERR: case HC_XFER_DATA_TOGGLE_ERR: if(urbd) { urbd->phase=URBD_DEQUEUEING; ifxhcd_complete_urb(_ifxhcd, urbd, -EOVERFLOW); } else { IFX_WARN("WARNING %s():%d urbd=%p urb=%p\n",__func__,__LINE__,urbd,urb); release_channel_dump(_ifxhc,urb,epqh,urbd,_halt_status); } break; #ifdef __NAKSTOP__ case HC_XFER_NAK: if (_ifxhc->is_in) { if(urbd && urb) { urbd->phase=URBD_COMPLETING; ifxhcd_complete_urb(_ifxhcd, urbd, 0); } else { IFX_WARN("WARNING %s():%d urbd=%p urb=%p\n",__func__,__LINE__,urbd,urb); release_channel_dump(_ifxhc,urb,epqh,urbd,_halt_status); } } else { IFX_WARN("WARNING %s():%d urbd=%p urb=%p\n",__func__,__LINE__,urbd,urb); release_channel_dump(_ifxhc,urb,epqh,urbd,_halt_status); } break; #endif #if defined(__INTRNAKRETRY__) || defined(__INTRINCRETRY__) case HC_XFER_INTR_NAK_RETRY: epqh->phase=EPQH_READY; urbd->phase=URBD_IDLE; ifxhcd_hc_cleanup(&_ifxhcd->core_if, _ifxhc); select_eps(_ifxhcd); return; break; #endif } if(epqh) { ifxhcd_epqh_idle(epqh); } else if(_halt_status!=HC_XFER_NO_EPQH) { IFX_WARN("WARNING %s():%d epqh=%p\n",__func__,__LINE__,epqh); release_channel_dump(_ifxhc,urb,epqh,urbd,_halt_status); } ifxhcd_hc_cleanup(&_ifxhcd->core_if, _ifxhc); select_eps(_ifxhcd); } /* * Updates the state of the URB after a Transfer Complete interrupt on the * host channel. Updates the actual_length field of the URB based on the * number of bytes transferred via the host channel. Sets the URB status * if the data transfer is finished. * * @return 1 if the data transfer specified by the URB is completely finished, * 0 otherwise. */ static int update_urb_state_xfer_comp(ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, struct urb *_urb, ifxhcd_urbd_t *_urbd) { int xfer_done = 0; #ifdef __EN_ISOC__ if(_urbd->epqh->ep_type==IFXUSB_EP_TYPE_ISOC) { struct usb_iso_packet_descriptor *frame_desc; frame_desc = &_urb->iso_frame_desc[_urbd->isoc_frame_index]; if (_ifxhc->is_in) { hctsiz_data_t hctsiz; hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); frame_desc->actual_length += (_ifxhc->xfer_len - hctsiz.b.xfersize); if ((hctsiz.b.xfersize != 0) || (frame_desc->actual_length >= _urbd->xfer_len)) { xfer_done = 1; frame_desc->status = 0; #if 0 if (frame_desc->actual_length < frame_desc->length && _urb->transfer_flags & URB_SHORT_NOT_OK) frame_desc->status = -EREMOTEIO; #endif } } else { if (_ifxhc->split) frame_desc->actual_length += _ifxhc->ssplit_out_xfer_count; else frame_desc->actual_length += _ifxhc->xfer_len; if (frame_desc->actual_length >= _urbd->xfer_len) { xfer_done = 1; frame_desc->status = 0; } } } else #endif if (_ifxhc->is_in) { hctsiz_data_t hctsiz; hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); _urb->actual_length += (_ifxhc->xfer_len - hctsiz.b.xfersize); #ifdef __INTRINCRETRY__ if(_urbd->epqh->ep_type==IFXUSB_EP_TYPE_INTR) { if(_ifxhc->xfer_len != hctsiz.b.xfersize) { xfer_done = 1; _urbd->status = 0; } } else #endif if ((hctsiz.b.xfersize != 0) || (_urb->actual_length >= _urb->transfer_buffer_length)) { xfer_done = 1; _urbd->status = 0; if(_urb->transfer_flags & URB_SHORT_NOT_OK) { if (_urb->actual_length < _urb->transfer_buffer_length) _urbd->status = -EREMOTEIO; } } } else if(_urb->transfer_buffer_length%_ifxhc->mps) // OUT without ZLP { if (_ifxhc->split) _urb->actual_length += _ifxhc->ssplit_out_xfer_count; else _urb->actual_length += _ifxhc->xfer_len; if (_urb->actual_length >= _urb->transfer_buffer_length) { xfer_done = 1; _urbd->status = 0; } } else if (_urb->actual_length >= _urb->transfer_buffer_length) //OUT with ZLP { xfer_done = 1; _urbd->status = 0; } else //OUT without ZLP, unfinished { if (_ifxhc->split) _urb->actual_length += _ifxhc->ssplit_out_xfer_count; else _urb->actual_length += _ifxhc->xfer_len; if (!_ifxhc->short_rw && _urb->actual_length >= _urb->transfer_buffer_length) { xfer_done = 1; _urbd->status = 0; } } #ifdef __DEBUG__ { hctsiz_data_t hctsiz; hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); IFX_DEBUGPL(DBG_HCDV, "IFXUSB: %s: %s, channel %d\n", __func__, (_ifxhc->is_in ? "IN" : "OUT"), _ifxhc->hc_num); IFX_DEBUGPL(DBG_HCDV, " hc->xfer_len %d\n", _ifxhc->xfer_len); IFX_DEBUGPL(DBG_HCDV, " hctsiz.xfersize %d\n", hctsiz.b.xfersize); #ifdef __EN_ISOC__ if(_urbd->epqh->ep_type==IFXUSB_EP_TYPE_ISOC) { IFX_DEBUGPL(DBG_HCDV, " descritor # %d\n", _urbd->isoc_frame_index); IFX_DEBUGPL(DBG_HCDV, " buffer_length %d\n", _urb->iso_frame_desc[_urbd->isoc_frame_index].length); IFX_DEBUGPL(DBG_HCDV, " actual_length %d\n", _urb->iso_frame_desc[_urbd->isoc_frame_index].actual_length); } else #endif { IFX_DEBUGPL(DBG_HCDV, " urb->transfer_buffer_length %d\n", _urb->transfer_buffer_length); IFX_DEBUGPL(DBG_HCDV, " urb->actual_length %d\n", _urb->actual_length); } } #endif return xfer_done; } #ifdef __EN_ISOC__ static void next_isoc_sub(unsigned long data) { ifxhcd_urbd_t *urbd; ifxhcd_hcd_t *ifxhcd; urbd=((ifxhcd_urbd_t *)data); ifxhcd=urbd->epqh->ifxhcd; if (!urbd->epqh) IFX_ERROR("%s: invalid epqd\n",__func__); #if defined(__UNALIGNED_BUF_ADJ__) else { if( urbd->aligned_checked && // urbd->using_aligned_buf && urbd->xfer_buff && urbd->is_in) { uint8_t *buf; buf=urbd->xfer_buff; buf+=urbd->urb->iso_frame_desc[urbd->isoc_frame_index].offset; memcpy(buf,urbd->aligned_buf,urbd->urb->iso_frame_desc[urbd->isoc_frame_index].length); } // urbd->using_aligned_buf=0; // urbd->using_aligned_setup=0; } #endif urbd->isoc_frame_index++; if(urbd->isoc_frame_index>=urbd->urb->number_of_packets) release_channel(ifxhcd,urbd->epqh->hc,HC_XFER_URB_COMPLETE); else init_hc(urbd->epqh); } #endif /*! \fn static void complete_channel(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxhcd_urbd_t *_urbd) \brief Complete the transaction on the channel. \param _ifxhcd Pointer to the sate of HCD structure \param _ifxhc Pointer to host channel descriptor \param _urbd Pointer to URB descriptor \return None \ingroup IFXUSB_HCD */ static void complete_channel(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxhcd_urbd_t *_urbd) { ifxusb_hc_regs_t *hc_regs = _ifxhcd->core_if.hc_regs[_ifxhc->hc_num]; struct urb *urb = NULL; ifxhcd_epqh_t *epqh = NULL; int urb_xfer_done; IFX_DEBUGPL(DBG_HCD, "--Complete Channel %d : \n", _ifxhc->hc_num); if(!_urbd) { IFX_ERROR("ERROR %s():%d urbd=%p\n",__func__,__LINE__,_urbd); return; } urb = _urbd->urb; epqh = _urbd->epqh; if(!epqh) { release_channel(_ifxhcd,_ifxhc,HC_XFER_NO_EPQH); return; } if(!urb || (unsigned long)urb->hcpriv!=(unsigned long)_urbd) { release_channel(_ifxhcd,_ifxhc,HC_XFER_NO_URB); return; } if (_ifxhc->split) _ifxhc->split = 1; switch (epqh->ep_type) { case IFXUSB_EP_TYPE_CTRL: switch (_ifxhc->control_phase) { case IFXHCD_CONTROL_SETUP: if (_urbd->xfer_len > 0) { _ifxhc->control_phase = IFXHCD_CONTROL_DATA; IFX_DEBUGPL(DBG_HCDV, " Control setup transaction done Data Stage now\n"); _ifxhc->is_in = _urbd->is_in; _ifxhc->xfer_len = _urbd->xfer_len; #if defined(__UNALIGNED_BUF_ADJ__) if(_urbd->aligned_buf) _ifxhc->xfer_buff = _urbd->aligned_buf; else #endif _ifxhc->xfer_buff = _urbd->xfer_buff; #ifdef __NAKSTOP__ if(!_ifxhc->split) { #ifdef __INNAKSTOP_CTRL__ if(_ifxhc->is_in) _ifxhc->stop_on=1; #endif #ifdef __PINGSTOP_CTRL__ if(!_ifxhc->is_in) _ifxhc->stop_on=1; #endif } #endif } else { IFX_DEBUGPL(DBG_HCDV, " Control setup transaction done Status Stage now\n"); _ifxhc->control_phase = IFXHCD_CONTROL_STATUS; _ifxhc->is_in = 1; _ifxhc->xfer_len = 0; _ifxhc->xfer_buff = _ifxhcd->status_buf; #ifdef __NAKSTOP__ _ifxhc->stop_on=0; #endif } if(_ifxhc->is_in) _ifxhc->short_rw =0; else _ifxhc->short_rw =(urb->transfer_flags & URB_ZERO_PACKET)?1:0; _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA1; _ifxhc->xfer_count = 0; _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); break; case IFXHCD_CONTROL_DATA: urb_xfer_done = update_urb_state_xfer_comp(_ifxhc, hc_regs, urb, _urbd); if (urb_xfer_done) { _ifxhc->control_phase = IFXHCD_CONTROL_STATUS; IFX_DEBUGPL(DBG_HCDV, " Control data transaction done Status Stage now\n"); _ifxhc->is_in = (_urbd->is_in)?0:1; _ifxhc->xfer_len = 0; _ifxhc->xfer_count = 0; _ifxhc->xfer_buff = _ifxhcd->status_buf; _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA1; if(_ifxhc->is_in) _ifxhc->short_rw =0; else _ifxhc->short_rw =1; #ifdef __NAKSTOP__ _ifxhc->stop_on=0; #endif } else // continue { IFX_DEBUGPL(DBG_HCDV, " Control data transaction continue\n"); _ifxhc->xfer_len = _urbd->xfer_len - urb->actual_length; _ifxhc->xfer_count = urb->actual_length; _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; _ifxhc->data_pid_start = read_data_toggle(hc_regs); } _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); break; case IFXHCD_CONTROL_STATUS: IFX_DEBUGPL(DBG_HCDV, " Control status transaction done\n"); if (_urbd->status == -EINPROGRESS) _urbd->status = 0; release_channel(_ifxhcd,_ifxhc,HC_XFER_URB_COMPLETE); break; } break; case IFXUSB_EP_TYPE_BULK: IFX_DEBUGPL(DBG_HCDV, " Bulk transfer complete\n"); urb_xfer_done = update_urb_state_xfer_comp(_ifxhc, hc_regs, urb, _urbd); if (urb_xfer_done) release_channel(_ifxhcd,_ifxhc,HC_XFER_URB_COMPLETE); else { _ifxhc->xfer_len = _urbd->xfer_len - urb->actual_length; _ifxhc->xfer_count = urb->actual_length; _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; _ifxhc->data_pid_start = read_data_toggle(hc_regs); _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } break; case IFXUSB_EP_TYPE_INTR: urb_xfer_done = update_urb_state_xfer_comp(_ifxhc, hc_regs, urb, _urbd); #ifdef __INTRINCRETRY__ if(!urb_xfer_done) release_channel(_ifxhcd,_ifxhc,HC_XFER_INTR_NAK_RETRY); else #endif release_channel(_ifxhcd,_ifxhc,HC_XFER_URB_COMPLETE); break; case IFXUSB_EP_TYPE_ISOC: #ifdef __EN_ISOC__ urb_xfer_done = update_urb_state_xfer_comp(_ifxhc, hc_regs, urb, _urbd); if (urb_xfer_done) { #if defined(__UNALIGNED_BUF_ADJ__) if(in_irq()) { if(!epqh->tasklet_next_isoc.func) { epqh->tasklet_next_isoc.next = NULL; epqh->tasklet_next_isoc.state = 0; atomic_set( &epqh->tasklet_next_isoc.count, 0); epqh->tasklet_next_isoc.func = next_isoc_sub; epqh->tasklet_next_isoc.data = (unsigned long)_urbd; } tasklet_schedule(&epqh->tasklet_next_isoc); } else #endif { next_isoc_sub((unsigned long)_urbd); } } else { struct usb_iso_packet_descriptor *frame_desc; frame_desc = &urb->iso_frame_desc[_urbd->isoc_frame_index]; _ifxhc->xfer_len = _urbd->xfer_len - frame_desc->actual_length; _ifxhc->xfer_count = frame_desc->actual_length; _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; _ifxhc->data_pid_start = read_data_toggle(hc_regs); _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } #endif break; } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static int32_t chhltd_ctrl_rx_nonsplit(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { hcint_data_t hcint; hcint_data_t hcintmsk; hctsiz_data_t hctsiz; hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nak); disable_hc_int(_hc_regs,nyet); #ifdef __INNAKSTOP_CTRL__ if (_ifxhc->halt_status == HC_XFER_NAK) { if(_ifxhc->control_phase == IFXHCD_CONTROL_DATA) { u32 actual_length; actual_length = _urbd->urb->actual_length + (_ifxhc->xfer_len - hctsiz.b.xfersize); if(_urbd->xfer_len && actual_length >= _urbd->xfer_len) { _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); } else { _ifxhc->xfer_count = _urbd->urb->actual_length = actual_length; _ifxhc->xfer_len = _urbd->xfer_len - actual_length; _ifxhc->data_pid_start = read_data_toggle(_hc_regs); _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } } else { printk(KERN_INFO "Warning: %s() %d Invalid CTRL Phase:%d\n",__func__,__LINE__,_ifxhc->control_phase); release_channel(_ifxhcd, _ifxhc, _ifxhc->halt_status); } return 1; } #endif if (hcint.b.xfercomp || hcint.d32 == 0x02) { _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); return 1; } else if (hcint.b.stall) { _urbd->error_count =0; // ZLP shortcut #if 0 if(hctsiz.b.pktcnt==0) complete_channel(_ifxhcd, _ifxhc, _urbd); else #endif #if 0 if(_ifxhc->control_phase == IFXHCD_CONTROL_STATUS) complete_channel(_ifxhcd, _ifxhc, _urbd); else #endif { if(_ifxhc->control_phase == IFXHCD_CONTROL_DATA) _urbd->urb->actual_length += (_ifxhc->xfer_len - hctsiz.b.xfersize); // if( _urbd->urb->actual_length > _ifxhc->xfer_len) _urbd->urb->actual_length = _urbd->xfer_len; release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); } return 1; } else if (hcint.b.bblerr) { _urbd->error_count =0; // ZLP shortcut #if 0 if(hctsiz.b.pktcnt==0) complete_channel(_ifxhcd, _ifxhc, _urbd); else #endif #if 0 if(_ifxhc->control_phase == IFXHCD_CONTROL_STATUS) complete_channel(_ifxhcd, _ifxhc, _urbd); else #endif { if(_ifxhc->control_phase == IFXHCD_CONTROL_DATA) _urbd->urb->actual_length += (_ifxhc->xfer_len - hctsiz.b.xfersize); // if( _urbd->urb->actual_length > _ifxhc->xfer_len) _urbd->urb->actual_length = _urbd->xfer_len; release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); } return 1; } else if (hcint.b.xacterr) { // ZLP shortcut #if 1 if(hctsiz.b.pktcnt==0) { _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); } else #endif #if 1 if(_ifxhc->control_phase == IFXHCD_CONTROL_STATUS) { _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); } else #endif if(_ifxhc->control_phase == IFXHCD_CONTROL_DATA) { #if 1 _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); #else u32 actual_length; actual_length = _urbd->urb->actual_length + (_ifxhc->xfer_len - hctsiz.b.xfersize); if(actual_length >= _urbd->xfer_len) { _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); } else { _urbd->error_count++; _ifxhc->xfer_count = _urbd->urb->actual_length = actual_length; _ifxhc->xfer_len = _urbd->xfer_len - actual_length; _ifxhc->data_pid_start = read_data_toggle(_hc_regs); if (_urbd->error_count >= 3) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); } else { _ifxhc->erron=1; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } } #endif } else { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); } return 1; } else if(hcint.b.datatglerr ) { #if 0 #if 1 _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); #else u32 actual_length; actual_length = _urbd->urb->actual_length + (_ifxhc->xfer_len - hctsiz.b.xfersize); if(actual_length>=_urbd->xfer_len) { _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); } else { _urbd->urb->actual_length = actual_length; _ifxhc->data_pid_start = read_data_toggle(_hc_regs); _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_DATA_TOGGLE_ERR); } #endif #else if(_ifxhc->control_phase == IFXHCD_CONTROL_DATA) _urbd->urb->actual_length += (_ifxhc->xfer_len - hctsiz.b.xfersize); // if( _urbd->urb->actual_length > _ifxhc->xfer_len) _urbd->urb->actual_length = _urbd->xfer_len; release_channel(_ifxhcd, _ifxhc, HC_XFER_DATA_TOGGLE_ERR); #endif return 1; } else if(hcint.b.frmovrun ) { if(_ifxhc->control_phase == IFXHCD_CONTROL_DATA) _urbd->urb->actual_length += (_ifxhc->xfer_len - hctsiz.b.xfersize); // if( _urbd->urb->actual_length > _ifxhc->xfer_len) _urbd->urb->actual_length = _urbd->xfer_len; release_channel(_ifxhcd, _ifxhc, HC_XFER_FRAME_OVERRUN); return 1; } else { _urbd->error_count =0; IFX_ERROR("ERROR %s():%d invalid chhlt condition %08X/%08X %d\n",__func__,__LINE__,hcint.d32,hcintmsk.d32,_ifxhc->halt_status); release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); return 1; } return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static int32_t chhltd_ctrl_tx_nonsplit(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { hcint_data_t hcint; hcint_data_t hcintmsk; hctsiz_data_t hctsiz; hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nak); disable_hc_int(_hc_regs,nyet); #ifdef __PINGSTOP_CTRL__ if (_ifxhc->halt_status == HC_XFER_NAK) { if(_ifxhc->control_phase == IFXHCD_CONTROL_DATA) { u32 actual_length; actual_length = _urbd->urb->actual_length + ((_ifxhc->start_pkt_count - hctsiz.b.pktcnt ) * _ifxhc->mps); if(_urbd->xfer_len && actual_length >= _urbd->xfer_len) { _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); } else { _ifxhc->xfer_count = _urbd->urb->actual_length = actual_length; _ifxhc->xfer_len = _urbd->xfer_len - actual_length; _ifxhc->data_pid_start = read_data_toggle(_hc_regs); _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } } else { printk(KERN_INFO "Warning: %s() %d Invalid CTRL Phase:%d\n",__func__,__LINE__,_ifxhc->control_phase); release_channel(_ifxhcd, _ifxhc, _ifxhc->halt_status); } return 1; } #endif if (hcint.b.xfercomp || hcint.d32 == 0x02) { _urbd->error_count =0; if(_ifxhc->xfer_len==0 && !hcint.b.ack && hcint.b.nak) { // Walkaround: When sending ZLP and receive NAK but also issue CMPT intr // Solution: NoSplit: Resend at next SOF // Split : Resend at next SOF with SSPLIT if(hcint.b.nyet) _ifxhc->epqh->do_ping=1; _ifxhc->xfer_len = 0; _ifxhc->xfer_count = 0; _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } else { if(hcint.b.nyet) _ifxhc->epqh->do_ping=1; complete_channel(_ifxhcd, _ifxhc, _urbd); } return 1; } else if (hcint.b.stall) { _urbd->error_count =0; // ZLP shortcut #if 1 if(hctsiz.b.pktcnt==0) complete_channel(_ifxhcd, _ifxhc, _urbd); else #endif { if(_ifxhc->control_phase == IFXHCD_CONTROL_DATA) { _urbd->urb->actual_length += ((_ifxhc->start_pkt_count - hctsiz.b.pktcnt ) * _ifxhc->mps); // if( _urbd->urb->actual_length > _ifxhc->xfer_len) _urbd->urb->actual_length = _urbd->xfer_len; } release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); } return 1; } else if (hcint.b.xacterr) { // ZLP shortcut #if 1 if(hctsiz.b.pktcnt==0) { _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); } else #endif if(_ifxhc->control_phase == IFXHCD_CONTROL_STATUS) { _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); } else if(_ifxhc->control_phase == IFXHCD_CONTROL_SETUP) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); } else if(_ifxhc->control_phase == IFXHCD_CONTROL_DATA) { #if 0 _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); #else u32 actual_length; actual_length = _urbd->urb->actual_length + ((_ifxhc->start_pkt_count - hctsiz.b.pktcnt ) * _ifxhc->mps); if(actual_length>=_urbd->xfer_len) { _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); } else { _urbd->error_count++; _ifxhc->xfer_count = _urbd->urb->actual_length = actual_length; _ifxhc->xfer_len = _urbd->xfer_len - actual_length; _ifxhc->data_pid_start = read_data_toggle(_hc_regs); if (_urbd->error_count >= 3) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); } else { _ifxhc->erron=1; _ifxhc->phase=HC_WAITING; _ifxhc->epqh->do_ping=1; ifxhcd_hc_start(_ifxhcd, _ifxhc); } } #endif } else { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); } return 1; } else if(hcint.b.bblerr ) { if(_ifxhc->control_phase == IFXHCD_CONTROL_DATA) { _urbd->urb->actual_length += ((_ifxhc->start_pkt_count - hctsiz.b.pktcnt ) * _ifxhc->mps); // if( _urbd->urb->actual_length > _ifxhc->xfer_len) _urbd->urb->actual_length = _urbd->xfer_len; } release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); return 1; } else if(hcint.b.nak || hcint.b.nyet) { #ifdef __PINGSTOP_CTRL__ _urbd->error_count =0; IFX_ERROR("ERROR %s():%d invalid chhlt condition\n",__func__,__LINE__); release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); #else // ZLP shortcut #if 1 if(hctsiz.b.pktcnt==0) { _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); } else #endif if(_ifxhc->control_phase == IFXHCD_CONTROL_STATUS) { _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); } else if(_ifxhc->control_phase == IFXHCD_CONTROL_SETUP) { _urbd->error_count =0; IFX_ERROR("ERROR %s():%d invalid chhlt condition\n",__func__,__LINE__); release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); } else if(_ifxhc->control_phase == IFXHCD_CONTROL_DATA) { #if 0 _ifxhc->epqh->do_ping=1; _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); #else u32 actual_length; _ifxhc->epqh->do_ping=1; actual_length = _urbd->urb->actual_length + ((_ifxhc->start_pkt_count - hctsiz.b.pktcnt ) * _ifxhc->mps); if(actual_length>=_urbd->xfer_len) { _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); } else { _ifxhc->xfer_count = _urbd->urb->actual_length = actual_length; _ifxhc->xfer_len = _urbd->xfer_len - actual_length; _ifxhc->data_pid_start = read_data_toggle(_hc_regs); _ifxhc->erron=1; _ifxhc->epqh->do_ping=1; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } #endif } #endif return 1; } else if(hcint.b.datatglerr ) { if(_ifxhc->control_phase == IFXHCD_CONTROL_DATA) { _urbd->urb->actual_length += ((_ifxhc->start_pkt_count - hctsiz.b.pktcnt ) * _ifxhc->mps); // if( _urbd->urb->actual_length > _ifxhc->xfer_len) _urbd->urb->actual_length = _urbd->xfer_len; } _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_DATA_TOGGLE_ERR); return 1; } else if(hcint.b.frmovrun ) { if(_ifxhc->control_phase == IFXHCD_CONTROL_DATA) { _urbd->urb->actual_length += ((_ifxhc->start_pkt_count - hctsiz.b.pktcnt ) * _ifxhc->mps); // if( _urbd->urb->actual_length > _ifxhc->xfer_len) _urbd->urb->actual_length = _urbd->xfer_len; } _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_FRAME_OVERRUN); return 1; } else { _urbd->error_count =0; IFX_ERROR("ERROR %s():%d invalid chhlt condition %08X/%08X %d\n",__func__,__LINE__,hcint.d32,hcintmsk.d32,_ifxhc->halt_status); release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); return 1; } return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static int32_t chhltd_bulk_rx_nonsplit(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { hcint_data_t hcint; hcint_data_t hcintmsk; hctsiz_data_t hctsiz; hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nak); disable_hc_int(_hc_regs,nyet); #ifdef __INNAKSTOP_BULK__ if(_ifxhc->halt_status == HC_XFER_NAK) { u32 actual_length; actual_length = _urbd->urb->actual_length + (_ifxhc->xfer_len - hctsiz.b.xfersize); if( (_urbd->xfer_len && actual_length>=_urbd->xfer_len) || hctsiz.b.pktcnt==0 || (hctsiz.b.xfersize % _ifxhc->mps)>0 ) { _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); } else { _urbd->urb->actual_length = actual_length; _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; _ifxhc->xfer_count = _urbd->urb->actual_length; _ifxhc->data_pid_start = read_data_toggle(_hc_regs); _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } return 1; } #endif if (hcint.b.xfercomp || hcint.d32 == 0x02) { _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); return 1; } else if (hcint.b.stall) { _urbd->error_count =0; // ZLP shortcut #if 0 if(hctsiz.b.pktcnt==0) complete_channel(_ifxhcd, _ifxhc, _urbd); else #endif { _urbd->urb->actual_length += (_ifxhc->xfer_len - hctsiz.b.xfersize); release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); } return 1; } else if (hcint.b.bblerr) { _urbd->error_count =0; // ZLP shortcut #if 0 if(hctsiz.b.pktcnt==0) complete_channel(_ifxhcd, _ifxhc, _urbd); else #endif { _urbd->urb->actual_length += (_ifxhc->xfer_len - hctsiz.b.xfersize); release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); } return 1; } else if (hcint.b.xacterr) { // ZLP shortcut #if 1 if(hctsiz.b.pktcnt==0) { _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); } else #endif { #if 0 _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); #else u32 actual_length; actual_length = _urbd->urb->actual_length + (_ifxhc->xfer_len - hctsiz.b.xfersize); if(actual_length >= _urbd->xfer_len) { _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); } else { _urbd->error_count++; _ifxhc->xfer_count = _urbd->urb->actual_length = actual_length; _ifxhc->xfer_len = _urbd->xfer_len - actual_length; _ifxhc->data_pid_start = read_data_toggle(_hc_regs); if (_urbd->error_count >= 3) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); } else { _ifxhc->erron=1; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } } #endif } return 1; } else if(hcint.b.datatglerr ) { #if 0 #if 1 _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); #else u32 actual_length; actual_length = _urbd->urb->actual_length + (_ifxhc->xfer_len - hctsiz.b.xfersize); if(actual_length >= _urbd->xfer_len) { _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); } else { _urbd->urb->actual_length = actual_length; _ifxhc->data_pid_start = read_data_toggle(_hc_regs); _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_DATA_TOGGLE_ERR); } #endif #else _urbd->urb->actual_length += (_ifxhc->xfer_len - hctsiz.b.xfersize); // if( _urbd->urb->actual_length > _ifxhc->xfer_len) _urbd->urb->actual_length = _urbd->xfer_len; release_channel(_ifxhcd, _ifxhc, HC_XFER_DATA_TOGGLE_ERR); #endif return 1; } else if(hcint.b.frmovrun ) { // if( _urbd->urb->actual_length > _ifxhc->xfer_len) _urbd->urb->actual_length = _urbd->xfer_len; release_channel(_ifxhcd, _ifxhc, HC_XFER_FRAME_OVERRUN); return 1; } else { _urbd->error_count =0; IFX_ERROR("ERROR %s():%d invalid chhlt condition %08X/%08X %d sz:%d/%d/%d/%d\n",__func__,__LINE__,hcint.d32,hcintmsk.d32,_ifxhc->halt_status , hctsiz.b.xfersize, _ifxhc->xfer_len-_ifxhc->xfer_len,_ifxhc->xfer_len,_urbd->xfer_len); release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); return 1; } return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static int32_t chhltd_bulk_tx_nonsplit(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { hcint_data_t hcint; hcint_data_t hcintmsk; hctsiz_data_t hctsiz; int out_nak_enh = 0; if (_ifxhcd->core_if.snpsid >= 0x4f54271a && _ifxhc->speed == IFXUSB_EP_SPEED_HIGH) out_nak_enh = 1; hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nak); disable_hc_int(_hc_regs,nyet); #ifdef __PINGSTOP_BULK__ if (_ifxhc->halt_status == HC_XFER_NAK) { u32 actual_length; actual_length = _urbd->urb->actual_length + ((_ifxhc->start_pkt_count - hctsiz.b.pktcnt ) * _ifxhc->mps); if(_urbd->xfer_len && actual_length >= _urbd->xfer_len) { _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); } else { _ifxhc->xfer_count = _urbd->urb->actual_length = actual_length; _ifxhc->xfer_len = _urbd->xfer_len - actual_length; _ifxhc->data_pid_start = read_data_toggle(_hc_regs); _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } return 1; } #endif if (hcint.b.xfercomp || hcint.d32 == 0x02) { _urbd->error_count =0; if(_ifxhc->xfer_len==0 && !hcint.b.ack && hcint.b.nak) { // Walkaround: When sending ZLP and receive NAK but also issue CMPT intr // Solution: NoSplit: Resend at next SOF // Split : Resend at next SOF with SSPLIT if(hcint.b.nyet) _ifxhc->epqh->do_ping=1; _ifxhc->xfer_len = 0; _ifxhc->xfer_count = 0; _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } else { if(hcint.b.nyet) _ifxhc->epqh->do_ping=1; complete_channel(_ifxhcd, _ifxhc, _urbd); } return 1; } else if (hcint.b.stall) { _urbd->error_count =0; // ZLP shortcut #if 1 if(hctsiz.b.pktcnt==0) complete_channel(_ifxhcd, _ifxhc, _urbd); else #endif { _urbd->urb->actual_length += ((_ifxhc->start_pkt_count - hctsiz.b.pktcnt ) * _ifxhc->mps); if(_urbd->urb->actual_length>_urbd->xfer_len) _urbd->urb->actual_length=_urbd->xfer_len; release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); } return 1; } else if (hcint.b.xacterr) { // ZLP shortcut #if 1 if(hctsiz.b.pktcnt==0) { _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); } else #endif { #if 0 _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); #else u32 actual_length; actual_length = _urbd->urb->actual_length + ((_ifxhc->start_pkt_count - hctsiz.b.pktcnt ) * _ifxhc->mps); if(actual_length >= _urbd->xfer_len) { _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); } else { _urbd->error_count++; _ifxhc->xfer_count = _urbd->urb->actual_length = actual_length; _ifxhc->xfer_len = _urbd->xfer_len - actual_length; _ifxhc->data_pid_start = read_data_toggle(_hc_regs); if (_urbd->error_count >= 3) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); } else { _ifxhc->erron=1; _ifxhc->phase=HC_WAITING; _ifxhc->epqh->do_ping=1; ifxhcd_hc_start(_ifxhcd, _ifxhc); } } #endif } return 1; } else if(hcint.b.bblerr ) { _urbd->urb->actual_length += ((_ifxhc->start_pkt_count - hctsiz.b.pktcnt ) * _ifxhc->mps); if(_urbd->urb->actual_length>_urbd->xfer_len) _urbd->urb->actual_length=_urbd->xfer_len; IFX_ERROR("ERROR %s():%d invalid packet babble\n",__func__,__LINE__); release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); return 1; } else if(hcint.b.nak || hcint.b.nyet) { #ifdef __PINGSTOP_BULK__ _urbd->error_count =0; IFX_ERROR("ERROR %s():%d invalid chhlt condition\n",__func__,__LINE__); release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); #else // ZLP shortcut #if 1 if(hctsiz.b.pktcnt==0) { _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); } else #endif { #if 0 _ifxhc->epqh->do_ping=1; _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); #else u32 actual_length; _ifxhc->epqh->do_ping=1; actual_length = _urbd->urb->actual_length + ((_ifxhc->start_pkt_count - hctsiz.b.pktcnt ) * _ifxhc->mps); if(actual_length>=_urbd->xfer_len) { _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); } else { _ifxhc->xfer_count = _urbd->urb->actual_length = actual_length; _ifxhc->xfer_len = _urbd->xfer_len - actual_length; _ifxhc->data_pid_start = read_data_toggle(_hc_regs); _ifxhc->erron=1; _ifxhc->epqh->do_ping=1; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } #endif } #endif return 1; } else if(hcint.b.datatglerr ) { _urbd->urb->actual_length += ((_ifxhc->start_pkt_count - hctsiz.b.pktcnt ) * _ifxhc->mps); // if( _urbd->urb->actual_length > _ifxhc->xfer_len) _urbd->urb->actual_length = _urbd->xfer_len; _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_DATA_TOGGLE_ERR); return 1; } else if(hcint.b.frmovrun ) { _urbd->urb->actual_length += ((_ifxhc->start_pkt_count - hctsiz.b.pktcnt ) * _ifxhc->mps); // if( _urbd->urb->actual_length > _ifxhc->xfer_len) _urbd->urb->actual_length = _urbd->xfer_len; _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_FRAME_OVERRUN); return 1; } else { _urbd->error_count =0; IFX_ERROR("ERROR %s():%d invalid chhlt condition %08X/%08X %d\n",__func__,__LINE__,hcint.d32,hcintmsk.d32,_ifxhc->halt_status); release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); return 1; } return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static int32_t chhltd_intr_rx_nonsplit(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { hcint_data_t hcint; hcint_data_t hcintmsk; hctsiz_data_t hctsiz; hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nak); disable_hc_int(_hc_regs,nyet); if (hcint.b.xfercomp || hcint.d32 == 0x02) { _urbd->error_count =0; //restart INTR immediately complete_channel(_ifxhcd, _ifxhc, _urbd); return 1; } else if (hcint.b.stall) { _urbd->error_count =0; // Don't care shortcut #if 0 if(hctsiz.b.pktcnt==0) complete_channel(_ifxhcd, _ifxhc, _urbd); else #endif { _urbd->urb->actual_length += (_ifxhc->xfer_len - hctsiz.b.xfersize); release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); } return 1; } else if (hcint.b.bblerr) { _urbd->error_count =0; // Don't care shortcut #if 0 if(hctsiz.b.pktcnt==0) complete_channel(_ifxhcd, _ifxhc, _urbd); else #endif { _urbd->urb->actual_length += (_ifxhc->xfer_len - hctsiz.b.xfersize); release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); } return 1; } else if (hcint.b.datatglerr || hcint.b.frmovrun) { _urbd->error_count =0; //restart INTR immediately complete_channel(_ifxhcd, _ifxhc, _urbd); return 1; } else if (hcint.b.xacterr) { // ZLP shortcut #if 1 if(hctsiz.b.pktcnt==0) { _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); } else #endif { _urbd->error_count++; if(_urbd->error_count>=3) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); } else { _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } } return 1; } else if(hcint.b.nyet ) { return 1; } else if (hcint.b.nak) { #ifdef __INTRNAKRETRY__ if(hctsiz.b.pktcnt) { release_channel(_ifxhcd, _ifxhc, HC_XFER_INTR_NAK_RETRY); return 1; } #endif _urbd->error_count =0; //restart INTR immediately complete_channel(_ifxhcd, _ifxhc, _urbd); return 1; } else { _urbd->error_count =0; //restart INTR immediately #if 0 if(hctsiz.b.pktcnt>0) { // TODO Re-initialize Channel (in next b_interval - 1 uF/F) _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } else #endif { complete_channel(_ifxhcd, _ifxhc, _urbd); } return 1; } return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static int32_t chhltd_intr_tx_nonsplit(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { hcint_data_t hcint; hcint_data_t hcintmsk; hctsiz_data_t hctsiz; int out_nak_enh = 0; if (_ifxhcd->core_if.snpsid >= 0x4f54271a && _ifxhc->speed == IFXUSB_EP_SPEED_HIGH) out_nak_enh = 1; hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); if (hcint.b.xfercomp || hcint.d32 == 0x02) { disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nak); disable_hc_int(_hc_regs,nyet); _urbd->error_count =0; //restart INTR immediately complete_channel(_ifxhcd, _ifxhc, _urbd); return 1; } else if (hcint.b.stall) { disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nyet); disable_hc_int(_hc_regs,nak); _urbd->error_count =0; // Don't care shortcut #if 0 if(hctsiz.b.pktcnt==0) complete_channel(_ifxhcd, _ifxhc, _urbd); else #endif { if(_ifxhc->xfer_len!=0)// !_ifxhc->is_in _urbd->urb->actual_length += ((_ifxhc->start_pkt_count - hctsiz.b.pktcnt ) * _ifxhc->mps); release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); } return 1; } else if(hcint.b.nak || hcint.b.frmovrun ) { disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nyet); disable_hc_int(_hc_regs,nak); _urbd->error_count =0; //restart INTR immediately complete_channel(_ifxhcd, _ifxhc, _urbd); return 1; } else if(hcint.b.xacterr ) { // ZLP shortcut #if 1 if(hctsiz.b.pktcnt==0) { _urbd->error_count =0; complete_channel(_ifxhcd, _ifxhc, _urbd); } else #endif { _urbd->error_count++; if(_urbd->error_count>=3) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); } else { _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } } return 1; } else if(hcint.b.bblerr ) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); return 1; } else if(hcint.b.datatglerr ) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_DATA_TOGGLE_ERR); return 1; } return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static int32_t chhltd_isoc_rx_nonsplit(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { #ifdef __EN_ISOC__ hcint_data_t hcint; hcint_data_t hcintmsk; hctsiz_data_t hctsiz; hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); if (hcint.b.xfercomp || hcint.b.frmovrun || hcint.d32 == 0x02) { _urbd->error_count=0; disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nak); disable_hc_int(_hc_regs,nyet); if (hcint.b.xfercomp) complete_channel(_ifxhcd, _ifxhc, _urbd); else release_channel(_ifxhcd, _ifxhc, HC_XFER_FRAME_OVERRUN); } else if (hcint.b.xacterr || hcint.b.bblerr) { #ifndef VR9Skip if(hctsiz.b.pktcnt==0) { complete_channel(_ifxhcd, _ifxhc, _urbd); } else { _urbd->urb->actual_length += (_ifxhc->xfer_len - hctsiz.b.xfersize); _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; _ifxhc->xfer_count = _urbd->urb->actual_length; _ifxhc->data_pid_start = read_data_toggle(_hc_regs); _urbd->error_count++; if(_urbd->error_count>=3) { _urbd->error_count=0; if (hcint.b.bblerr) release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); else if (hcint.b.xacterr) release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); } else { enable_hc_int(_hc_regs,ack); enable_hc_int(_hc_regs,nak); enable_hc_int(_hc_regs,nyet); _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } } #endif } else if(hcint.b.datatglerr ) { return 1; } else if(hcint.b.stall ) { return 1; } #endif return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static int32_t chhltd_isoc_tx_nonsplit(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { #ifdef __EN_ISOC__ hcint_data_t hcint; hcint_data_t hcintmsk; hctsiz_data_t hctsiz; int out_nak_enh = 0; if (_ifxhcd->core_if.snpsid >= 0x4f54271a && _ifxhc->speed == IFXUSB_EP_SPEED_HIGH) out_nak_enh = 1; hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); if (hcint.b.xfercomp || hcint.d32 == 0x02) { _urbd->error_count=0; disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nak); disable_hc_int(_hc_regs,nyet); complete_channel(_ifxhcd, _ifxhc, _urbd); return 1; } else if (hcint.b.frmovrun) { #ifndef VR9Skip _urbd->error_count=0; disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nak); disable_hc_int(_hc_regs,nyet); release_channel(_ifxhcd, _ifxhc, HC_XFER_FRAME_OVERRUN); #endif } else if(hcint.b.datatglerr ) { return 1; } else if(hcint.b.bblerr ) { #ifndef VR9Skip if(hctsiz.b.pktcnt==0) { complete_channel(_ifxhcd, _ifxhc, _urbd); } else { _urbd->urb->actual_length += (_ifxhc->xfer_len - hctsiz.b.xfersize); _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; _ifxhc->xfer_count = _urbd->urb->actual_length; _ifxhc->data_pid_start = read_data_toggle(_hc_regs); _urbd->error_count++; if(_urbd->error_count>=3) { _urbd->error_count=0; release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); } else { enable_hc_int(_hc_regs,ack); enable_hc_int(_hc_regs,nak); enable_hc_int(_hc_regs,nyet); _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } } #endif } else if(hcint.b.xacterr ) { if(hctsiz.b.pktcnt==0) { complete_channel(_ifxhcd, _ifxhc, _urbd); return 1; } _urbd->error_count++; if(_urbd->error_count>=3) { _urbd->error_count=0; release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); } else { _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } return 1; } else if(hcint.b.stall ) { return 1; } #endif return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static int32_t chhltd_ctrl_rx_ssplit(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { hcint_data_t hcint; hcint_data_t hcintmsk; hctsiz_data_t hctsiz; hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nak); disable_hc_int(_hc_regs,nyet); if (hcint.b.ack) { _urbd->error_count=0; _ifxhc->split=2; _ifxhc->data_pid_start = read_data_toggle(_hc_regs); _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if (hcint.b.nak) { _urbd->error_count = 0; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if (hcint.b.xacterr) { _urbd->error_count++; if(_urbd->error_count>=3) { _urbd->error_count=0; release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); } else { _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } return 1; } else if(hcint.b.bblerr ) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); return 1; } else if(hcint.b.stall ) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); return 1; } else if(hcint.b.datatglerr ) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_DATA_TOGGLE_ERR); return 1; } else if(hcint.b.frmovrun ) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_FRAME_OVERRUN); return 1; } else if(hcint.b.nyet ) { } else if(hcint.b.xfercomp ) { } return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static int32_t chhltd_ctrl_tx_ssplit(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { hcint_data_t hcint; hcint_data_t hcintmsk; hctsiz_data_t hctsiz; int out_nak_enh = 0; if (_ifxhcd->core_if.snpsid >= 0x4f54271a && _ifxhc->speed == IFXUSB_EP_SPEED_HIGH) out_nak_enh = 1; hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nak); disable_hc_int(_hc_regs,nyet); if (hcint.b.ack ) { _urbd->error_count=0; if (_ifxhc->control_phase != IFXHCD_CONTROL_SETUP) _ifxhc->ssplit_out_xfer_count = _ifxhc->xfer_len; _ifxhc->split=2; _ifxhc->data_pid_start =read_data_toggle(_hc_regs); _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.nyet) { _urbd->error_count=0; if (_ifxhc->control_phase != IFXHCD_CONTROL_SETUP) _ifxhc->ssplit_out_xfer_count = _ifxhc->xfer_len; _ifxhc->split=2; _ifxhc->data_pid_start =read_data_toggle(_hc_regs); _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.nak ) { _urbd->error_count =0; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.xacterr ) { _urbd->error_count++; if(_urbd->error_count>=3) { _urbd->error_count=0; release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); } else { _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } return 1; } else if(hcint.b.datatglerr ) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_DATA_TOGGLE_ERR); return 1; } else if(hcint.b.bblerr ) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); return 1; } else if(hcint.b.stall ) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); return 1; } else if(hcint.b.frmovrun ) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_FRAME_OVERRUN); return 1; } else if(hcint.b.xfercomp ) { printk(KERN_INFO "Warning: %s() %d CTRL OUT SPLIT1 COMPLETE\n",__func__,__LINE__); } return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static int32_t chhltd_bulk_rx_ssplit(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { hcint_data_t hcint; hcint_data_t hcintmsk; hctsiz_data_t hctsiz; hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nak); disable_hc_int(_hc_regs,nyet); if (hcint.b.ack) { _urbd->error_count=0; _ifxhc->split=2; _ifxhc->data_pid_start = read_data_toggle(_hc_regs); _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if (hcint.b.nak) { _urbd->error_count = 0; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if (hcint.b.xacterr) { _urbd->error_count++; if(_urbd->error_count>=3) { _urbd->error_count=0; release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); } else { _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } return 1; } else if(hcint.b.bblerr ) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); return 1; } else if(hcint.b.stall ) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); return 1; } else if(hcint.b.datatglerr ) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_DATA_TOGGLE_ERR); return 1; } else if(hcint.b.frmovrun ) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_FRAME_OVERRUN); return 1; } else if(hcint.b.nyet ) { } else if(hcint.b.xfercomp ) { } return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static int32_t chhltd_bulk_tx_ssplit(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { hcint_data_t hcint; hcint_data_t hcintmsk; hctsiz_data_t hctsiz; int out_nak_enh = 0; if (_ifxhcd->core_if.snpsid >= 0x4f54271a && _ifxhc->speed == IFXUSB_EP_SPEED_HIGH) out_nak_enh = 1; hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nak); disable_hc_int(_hc_regs,nyet); if (hcint.b.ack ) { _urbd->error_count=0; _ifxhc->ssplit_out_xfer_count = _ifxhc->xfer_len; _ifxhc->split=2; _ifxhc->data_pid_start =read_data_toggle(_hc_regs); _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.nyet) { _urbd->error_count=0; _ifxhc->ssplit_out_xfer_count = _ifxhc->xfer_len; _ifxhc->split=2; _ifxhc->data_pid_start =read_data_toggle(_hc_regs); _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.nak ) { _urbd->error_count =0; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.xacterr ) { _urbd->error_count++; if(_urbd->error_count>=3) { _urbd->error_count=0; release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); } else { _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } return 1; } else if(hcint.b.datatglerr ) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_DATA_TOGGLE_ERR); return 1; } else if(hcint.b.bblerr ) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); return 1; } else if(hcint.b.stall ) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); return 1; } else if(hcint.b.frmovrun ) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_FRAME_OVERRUN); return 1; } else if(hcint.b.xfercomp ) { printk(KERN_INFO "Warning: %s() %d BULK OUT SPLIT1 COMPLETE\n",__func__,__LINE__); } return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static int32_t chhltd_intr_rx_ssplit(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { hcint_data_t hcint; hcint_data_t hcintmsk; hctsiz_data_t hctsiz; hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nak); disable_hc_int(_hc_regs,nyet); if (hcint.b.ack) { _urbd->error_count=0; _ifxhc->split=2; _ifxhc->data_pid_start = read_data_toggle(_hc_regs); _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.nak) { _urbd->error_count=0; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.xacterr) { hcchar_data_t hcchar; hcchar.d32 = ifxusb_rreg(&_hc_regs->hcchar); _urbd->error_count=hcchar.b.multicnt; if(_urbd->error_count>=3) { _urbd->error_count=0; release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); } else { _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } return 1; } else if(hcint.b.stall ) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); return 1; } else if(hcint.b.bblerr ) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); return 1; } else if(hcint.b.frmovrun ) { _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.datatglerr ) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_DATA_TOGGLE_ERR); return 1; } else if(hcint.b.xfercomp ) { } return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static int32_t chhltd_intr_tx_ssplit(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { hcint_data_t hcint; hcint_data_t hcintmsk; hctsiz_data_t hctsiz; int out_nak_enh = 0; if (_ifxhcd->core_if.snpsid >= 0x4f54271a && _ifxhc->speed == IFXUSB_EP_SPEED_HIGH) out_nak_enh = 1; hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nak); disable_hc_int(_hc_regs,nyet); if (hcint.b.ack ) { _urbd->error_count=0; _ifxhc->ssplit_out_xfer_count = _ifxhc->xfer_len; _ifxhc->split=2; _ifxhc->data_pid_start = read_data_toggle(_hc_regs); _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.nyet) { _urbd->error_count=0; _ifxhc->ssplit_out_xfer_count = _ifxhc->xfer_len; _ifxhc->split=2; _ifxhc->data_pid_start = read_data_toggle(_hc_regs); _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.nak ) { _urbd->error_count =0; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.frmovrun ) { _urbd->error_count =0; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.xacterr ) { hcchar_data_t hcchar; hcchar.d32 = ifxusb_rreg(&_hc_regs->hcchar); _urbd->error_count=hcchar.b.multicnt; if(_urbd->error_count>=3) { _urbd->error_count=0; release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); } else { enable_hc_int(_hc_regs,ack); enable_hc_int(_hc_regs,nak); enable_hc_int(_hc_regs,nyet); _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } return 1; } else if(hcint.b.datatglerr ) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_DATA_TOGGLE_ERR); return 1; } else if(hcint.b.bblerr ) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); return 1; } else if(hcint.b.stall ) { _urbd->error_count =0; release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); return 1; } else if(hcint.b.xfercomp ) { } return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static int32_t chhltd_isoc_rx_ssplit(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { #if defined(__EN_ISOC__) && defined(__EN_ISOC_SPLIT__) hcint_data_t hcint; hcint_data_t hcintmsk; hctsiz_data_t hctsiz; hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); if (hcint.b.ack ) { Do Complete Split } else if(hcint.b.frmovrun ) { Rewind Buffer Pointers Retry Start Split (in next b_interval ¡V 1 uF) } else if(hcint.b.datatglerr ) { //warning } else if(hcint.b.bblerr ) { //warning } else if(hcint.b.xacterr ) { //warning } else if(hcint.b.stall ) { //warning } else if(hcint.b.nak ) { //warning } else if(hcint.b.xfercomp ) { //warning } else if(hcint.b.nyet) { //warning } #endif return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static int32_t chhltd_isoc_tx_ssplit(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { #if defined(__EN_ISOC__) && defined(__EN_ISOC_SPLIT__) hcint_data_t hcint; hcint_data_t hcintmsk; hctsiz_data_t hctsiz; int out_nak_enh = 0; if (_ifxhcd->core_if.snpsid >= 0x4f54271a && _ifxhc->speed == IFXUSB_EP_SPEED_HIGH) out_nak_enh = 1; hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); if (hcint.b.ack ) { //Do Next Start Split (in next b_interval ¡V 1 uF) } else if(hcint.b.frmovrun ) { //Do Next Transaction in next frame. } else if(hcint.b.datatglerr ) { //warning } else if(hcint.b.bblerr ) { //warning } else if(hcint.b.xacterr ) { //warning } else if(hcint.b.stall ) { //warning } else if(hcint.b.nak ) { //warning } else if(hcint.b.xfercomp ) { //warning } else if(hcint.b.nyet) { //warning } #endif return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static int32_t chhltd_ctrl_rx_csplit(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { hcint_data_t hcint; hcint_data_t hcintmsk; hctsiz_data_t hctsiz; hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nak); disable_hc_int(_hc_regs,nyet); if (hcint.b.xfercomp) { _urbd->error_count =0; _ifxhc->split=1; complete_channel(_ifxhcd, _ifxhc, _urbd); return 1; } else if (hcint.b.nak) { _ifxhc->split = 1; if(_ifxhc->control_phase == IFXHCD_CONTROL_DATA) { _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; _ifxhc->xfer_count = _urbd->urb->actual_length; } _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.nyet) { _urbd->error_count=0; _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.stall || hcint.b.bblerr ) { _urbd->error_count=0; if (hcint.b.stall) release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); else if(hcint.b.bblerr ) release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); return 1; } else if(hcint.b.xacterr ) { _urbd->error_count++; if(_urbd->error_count>=3) { _urbd->error_count=0; release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); } else { _ifxhc->split=1; if(_ifxhc->control_phase == IFXHCD_CONTROL_DATA) { _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; _ifxhc->xfer_count = _urbd->urb->actual_length; } _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } return 1; } else if(hcint.b.datatglerr ) { if(_ifxhc->data_pid_start == IFXUSB_HC_PID_DATA0) _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA1; else _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA0; _ifxhc->split=1; if(_ifxhc->control_phase == IFXHCD_CONTROL_DATA) { _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; _ifxhc->xfer_count = _urbd->urb->actual_length; } _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.frmovrun ) { _urbd->error_count=0; release_channel(_ifxhcd, _ifxhc, HC_XFER_FRAME_OVERRUN); return 1; } return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static int32_t chhltd_ctrl_tx_csplit(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { hcint_data_t hcint; hcint_data_t hcintmsk; hctsiz_data_t hctsiz; int out_nak_enh = 0; if (_ifxhcd->core_if.snpsid >= 0x4f54271a && _ifxhc->speed == IFXUSB_EP_SPEED_HIGH) out_nak_enh = 1; hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nak); disable_hc_int(_hc_regs,nyet); if(hcint.b.xfercomp ) { _urbd->error_count=0; _ifxhc->split=1; #if 0 if(_ifxhc->xfer_len==0 && !hcint.b.ack && (hcint.b.nak || hcint.b.nyet)) { // Walkaround: When sending ZLP and receive NYEY or NAK but also issue CMPT intr // Solution: NoSplit: Resend at next SOF // Split : Resend at next SOF with SSPLIT _ifxhc->xfer_len = 0; _ifxhc->xfer_count = 0; _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } else #endif { complete_channel(_ifxhcd, _ifxhc, _urbd); } return 1; } else if(hcint.b.nak ) { _ifxhc->split = 1; if(_ifxhc->control_phase == IFXHCD_CONTROL_DATA) { _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; _ifxhc->xfer_count = _urbd->urb->actual_length; } _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.nyet) { //Retry Complete Split // Issue Retry instantly on next SOF, without gothrough process_channels _urbd->error_count=0; _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.stall ) { _urbd->error_count=0; release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); return 1; } else if(hcint.b.xacterr ) { _urbd->error_count++; if(_urbd->error_count>=3) { _urbd->error_count=0; release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); } else { _ifxhc->split=1; if(_ifxhc->control_phase == IFXHCD_CONTROL_DATA) { _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; _ifxhc->xfer_count = _urbd->urb->actual_length; } _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } return 1; } else if(hcint.b.datatglerr ) { if(_ifxhc->data_pid_start == IFXUSB_HC_PID_DATA0) _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA1; else _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA0; _ifxhc->split=1; if(_ifxhc->control_phase == IFXHCD_CONTROL_DATA) { _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; _ifxhc->xfer_count = _urbd->urb->actual_length; } _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.frmovrun ) { _urbd->error_count=0; release_channel(_ifxhcd, _ifxhc, HC_XFER_FRAME_OVERRUN); return 1; } else if(hcint.b.bblerr ) { _urbd->error_count=0; release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); return 1; } return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static int32_t chhltd_bulk_rx_csplit(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { hcint_data_t hcint; hcint_data_t hcintmsk; hctsiz_data_t hctsiz; hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nak); disable_hc_int(_hc_regs,nyet); if (hcint.b.xfercomp) { _urbd->error_count =0; _ifxhc->split=1; complete_channel(_ifxhcd, _ifxhc, _urbd); return 1; } else if (hcint.b.nak) { _ifxhc->split = 1; _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; _ifxhc->xfer_count = _urbd->urb->actual_length; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.nyet) { _urbd->error_count=0; _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.stall || hcint.b.bblerr ) { _urbd->error_count=0; if (hcint.b.stall) release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); else if(hcint.b.bblerr ) release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); return 1; } else if(hcint.b.xacterr ) { _urbd->error_count++; if(_urbd->error_count>=3) { _urbd->error_count=0; release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); } else { _ifxhc->split=1; _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; _ifxhc->xfer_count = _urbd->urb->actual_length; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } return 1; } else if(hcint.b.datatglerr ) { if(_ifxhc->data_pid_start == IFXUSB_HC_PID_DATA0) _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA1; else _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA0; _ifxhc->split=1; _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; _ifxhc->xfer_count = _urbd->urb->actual_length; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.frmovrun ) { _urbd->error_count=0; release_channel(_ifxhcd, _ifxhc, HC_XFER_FRAME_OVERRUN); return 1; } return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static int32_t chhltd_bulk_tx_csplit(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { hcint_data_t hcint; hcint_data_t hcintmsk; hctsiz_data_t hctsiz; int out_nak_enh = 0; if (_ifxhcd->core_if.snpsid >= 0x4f54271a && _ifxhc->speed == IFXUSB_EP_SPEED_HIGH) out_nak_enh = 1; hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nak); disable_hc_int(_hc_regs,nyet); if(hcint.b.xfercomp ) { _urbd->error_count=0; _ifxhc->split=1; #if 0 if(_ifxhc->xfer_len==0 && !hcint.b.ack && (hcint.b.nak || hcint.b.nyet)) { // Walkaround: When sending ZLP and receive NYEY or NAK but also issue CMPT intr // Solution: NoSplit: Resend at next SOF // Split : Resend at next SOF with SSPLIT _ifxhc->xfer_len = 0; _ifxhc->xfer_count = 0; _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } else #endif { complete_channel(_ifxhcd, _ifxhc, _urbd); } return 1; } else if(hcint.b.nak ) { _ifxhc->split = 1; _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; _ifxhc->xfer_count = _urbd->urb->actual_length; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.nyet) { //Retry Complete Split // Issue Retry instantly on next SOF, without gothrough process_channels _urbd->error_count=0; _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.stall ) { _urbd->error_count=0; release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); return 1; } else if(hcint.b.xacterr ) { _urbd->error_count++; if(_urbd->error_count>=3) { _urbd->error_count=0; release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); } else { _ifxhc->split=1; _ifxhc->epqh->do_ping=1; _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; _ifxhc->xfer_count = _urbd->urb->actual_length; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } return 1; } else if(hcint.b.datatglerr ) { if(_ifxhc->data_pid_start == IFXUSB_HC_PID_DATA0) _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA1; else _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA0; _ifxhc->split=1; _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; _ifxhc->xfer_count = _urbd->urb->actual_length; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.frmovrun ) { _urbd->error_count=0; release_channel(_ifxhcd, _ifxhc, HC_XFER_FRAME_OVERRUN); return 1; } else if(hcint.b.bblerr ) { _urbd->error_count=0; release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); return 1; } return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static int32_t chhltd_intr_rx_csplit(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { hcint_data_t hcint; hcint_data_t hcintmsk; hctsiz_data_t hctsiz; hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nak); disable_hc_int(_hc_regs,nyet); if (hcint.b.xfercomp ) { _urbd->error_count=0; _ifxhc->split=1; complete_channel(_ifxhcd, _ifxhc, _urbd); return 1; } else if(hcint.b.nak ) { _ifxhc->split = 1; _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; _ifxhc->xfer_count = _urbd->urb->actual_length; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.nyet) { _urbd->error_count=0; _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.frmovrun || hcint.b.bblerr || hcint.b.stall ) { _urbd->error_count=0; if (hcint.b.stall) release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); else if(hcint.b.bblerr ) release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); else if(hcint.b.frmovrun ) release_channel(_ifxhcd, _ifxhc, HC_XFER_FRAME_OVERRUN); return 1; } else if(hcint.b.xacterr ) { hcchar_data_t hcchar; hcchar.d32 = ifxusb_rreg(&_hc_regs->hcchar); _urbd->error_count=hcchar.b.multicnt; if(_urbd->error_count>=3) { _urbd->error_count=0; release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); } else { _ifxhc->split=1; _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; _ifxhc->xfer_count = _urbd->urb->actual_length; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } return 1; } else if(hcint.b.datatglerr ) { if(_ifxhc->data_pid_start == IFXUSB_HC_PID_DATA0) _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA1; else _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA0; _ifxhc->split=1; _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; _ifxhc->xfer_count = _urbd->urb->actual_length; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static int32_t chhltd_intr_tx_csplit(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { hcint_data_t hcint; hcint_data_t hcintmsk; hctsiz_data_t hctsiz; int out_nak_enh = 0; if (_ifxhcd->core_if.snpsid >= 0x4f54271a && _ifxhc->speed == IFXUSB_EP_SPEED_HIGH) out_nak_enh = 1; hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nak); disable_hc_int(_hc_regs,nyet); if(hcint.b.xfercomp ) { _urbd->error_count=0; _ifxhc->split=1; complete_channel(_ifxhcd, _ifxhc, _urbd); return 1; } else if(hcint.b.nak ) { _ifxhc->split = 1; _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; _ifxhc->xfer_count = _urbd->urb->actual_length; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.nyet) { _urbd->error_count=0; _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.stall || hcint.b.frmovrun) { _urbd->error_count=0; if (hcint.b.stall) release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); else if(hcint.b.frmovrun ) release_channel(_ifxhcd, _ifxhc, HC_XFER_FRAME_OVERRUN); return 1; } else if(hcint.b.xacterr ) { hcchar_data_t hcchar; hcchar.d32 = ifxusb_rreg(&_hc_regs->hcchar); _urbd->error_count=hcchar.b.multicnt; if(_urbd->error_count>=3) { _urbd->error_count=0; release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); } else { _ifxhc->split=1; _ifxhc->epqh->do_ping=1; _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; _ifxhc->xfer_count = _urbd->urb->actual_length; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); } return 1; } else if(hcint.b.datatglerr ) { if(_ifxhc->data_pid_start == IFXUSB_HC_PID_DATA0) _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA1; else _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA0; _ifxhc->split=1; _ifxhc->epqh->do_ping=1; _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; _ifxhc->xfer_count = _urbd->urb->actual_length; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.bblerr ) { _urbd->error_count=0; release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); return 1; } return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static int32_t chhltd_isoc_rx_csplit(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { #if defined(__EN_ISOC__) && defined(__EN_ISOC_SPLIT__) hcint_data_t hcint; hcint_data_t hcintmsk; hctsiz_data_t hctsiz; hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); if(hcint.b.xfercomp ) { disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nak); disable_hc_int(_hc_regs,nyet); _urbd->error_count=0; _ifxhc->split=1; complete_channel(_ifxhcd, _ifxhc, _urbd); return 1; } else if(hcint.b.nak ) { Retry Start Split (in next b_interval ¡V 1 uF) } else if(hcint.b.nyet) { //Do Next Complete Split // Issue Retry instantly on next SOF, without gothrough process_channels _urbd->error_count=0; //disable_hc_int(_hc_regs,ack); //disable_hc_int(_hc_regs,nak); //disable_hc_int(_hc_regs,datatglerr); _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; _ifxhc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, _ifxhc); return 1; } else if(hcint.b.frmovrun || hcint.b.stall || hcint.b.bblerr) { _urbd->error_count=0; disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nyet); disable_hc_int(_hc_regs,nak); _ifxhc->wait_for_sof = 0; //if(hctsiz.b.pktcnt==0) //{ // complete_channel(_ifxhcd, _ifxhc, _urbd); // return 1; //} //else // _urbd->urb->actual_length += (_ifxhc->xfer_len - hctsiz.b.xfersize); if (hcint.b.stall) release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); else if(hcint.b.frmovrun ) else if(hcint.b.bblerr ) return 1; } else if(hcint.b.xacterr ) { Rewind Buffer Pointers if (HCCHARn.EC = = 3) // ERR response received { Record ERR error Do Next Start Split (in next frame) } else { De-allocate Channel } } else if(hcint.b.datatglerr ) { warning } else if(hcint.b.ack ) { warning } #endif return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static int32_t chhltd_isoc_tx_csplit(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { #if defined(__EN_ISOC__) && defined(__EN_ISOC_SPLIT__) hcint_data_t hcint; hcint_data_t hcintmsk; hctsiz_data_t hctsiz; int out_nak_enh = 0; if (_ifxhcd->core_if.snpsid >= 0x4f54271a && _ifxhc->speed == IFXUSB_EP_SPEED_HIGH) out_nak_enh = 1; hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); warning #endif return 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /*! \fn static int32_t handle_hc_chhltd_intr(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) \brief This function handles halted interrupts of host channels. \param _ifxhcd Pointer to the sate of HCD structure \param _ifxhc Pointer to host channel descriptor \param _hc_regs Pointer to host channel registers \param _urbd Pointer to URB descriptor \return 0 OK \ingroup IFXUSB_HCD */ static int32_t handle_hc_chhltd_intr(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { IFX_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: Channel Halted--\n", _ifxhc->hc_num); _ifxhc->phase = HC_STOPPED; if(_ifxhc->epqh) if(_ifxhc->epqh->urbd) _ifxhc->epqh->urbd->phase=URBD_ACTIVE; if (_ifxhc->halt_status == HC_XFER_URB_DEQUEUE || _ifxhc->halt_status == 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. */ if(_ifxhc->epqh) if(_ifxhc->epqh->urbd) _ifxhc->epqh->urbd->phase=URBD_DEQUEUEING; release_channel(_ifxhcd, _ifxhc, _ifxhc->halt_status); return 1; } if (_ifxhc->ep_type == IFXUSB_EP_TYPE_CTRL) { if (_ifxhc->split==0) { if(_ifxhc->is_in) return (chhltd_ctrl_rx_nonsplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); else return (chhltd_ctrl_tx_nonsplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); } else if(_ifxhc->split==1) { if(_ifxhc->is_in) return (chhltd_ctrl_rx_ssplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); else return (chhltd_ctrl_tx_ssplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); } else if(_ifxhc->split==2) { if(_ifxhc->is_in) return (chhltd_ctrl_rx_csplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); else return (chhltd_ctrl_tx_csplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); } } else if(_ifxhc->ep_type == IFXUSB_EP_TYPE_BULK) { if (_ifxhc->split==0) { if(_ifxhc->is_in) return (chhltd_bulk_rx_nonsplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); else return (chhltd_bulk_tx_nonsplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); } else if(_ifxhc->split==1) { if(_ifxhc->is_in) return (chhltd_bulk_rx_ssplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); else return (chhltd_bulk_tx_ssplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); } else if(_ifxhc->split==2) { if(_ifxhc->is_in) return (chhltd_bulk_rx_csplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); else return (chhltd_bulk_tx_csplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); } } else if(_ifxhc->ep_type == IFXUSB_EP_TYPE_INTR) { if (_ifxhc->split==0) { if(_ifxhc->is_in) return (chhltd_intr_rx_nonsplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); else return (chhltd_intr_tx_nonsplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); } else if(_ifxhc->split==1) { if(_ifxhc->is_in) return (chhltd_intr_rx_ssplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); else return (chhltd_intr_tx_ssplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); } else if(_ifxhc->split==2) { if(_ifxhc->is_in) return (chhltd_intr_rx_csplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); else return (chhltd_intr_tx_csplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); } } else if(_ifxhc->ep_type == IFXUSB_EP_TYPE_ISOC) { if (_ifxhc->split==0) { if(_ifxhc->is_in) return (chhltd_isoc_rx_nonsplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); else return (chhltd_isoc_tx_nonsplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); } else if(_ifxhc->split==1) { if(_ifxhc->is_in) return (chhltd_isoc_rx_ssplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); else return (chhltd_isoc_tx_ssplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); } else if(_ifxhc->split==2) { if(_ifxhc->is_in) return (chhltd_isoc_rx_csplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); else return (chhltd_isoc_tx_csplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); } } return 0; } /* * Handles a host channel AHB error interrupt. This handler is only called in * DMA mode. */ static void hc_other_intr_dump(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { #ifdef __DEBUG__ hcchar_data_t hcchar; hcsplt_data_t hcsplt; hctsiz_data_t hctsiz; uint32_t hcdma; struct urb *urb = _urbd->urb; hcchar.d32 = ifxusb_rreg(&_hc_regs->hcchar); hcsplt.d32 = ifxusb_rreg(&_hc_regs->hcsplt); hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); hcdma = ifxusb_rreg(&_hc_regs->hcdma); IFX_ERROR("Channel %d\n", _ifxhc->hc_num); IFX_ERROR(" hcchar 0x%08x, hcsplt 0x%08x\n", hcchar.d32, hcsplt.d32); IFX_ERROR(" hctsiz 0x%08x, hcdma 0x%08x\n", hctsiz.d32, hcdma); IFX_ERROR(" Device address: %d\n", usb_pipedevice(urb->pipe)); IFX_ERROR(" Endpoint: %d, %s\n", usb_pipeendpoint(urb->pipe), (usb_pipein(urb->pipe) ? "IN" : "OUT")); IFX_ERROR(" Endpoint type: %s\n", ({char *pipetype; switch (usb_pipetype(urb->pipe)) { case PIPE_CONTROL: pipetype = "CTRL"; break; case PIPE_BULK: pipetype = "BULK"; break; case PIPE_INTERRUPT: pipetype = "INTR"; break; case PIPE_ISOCHRONOUS: pipetype = "ISOC"; break; default: pipetype = "????"; break; }; pipetype;})); IFX_ERROR(" Speed: %s\n", ({char *speed; switch (urb->dev->speed) { case USB_SPEED_HIGH: speed = "HS"; break; case USB_SPEED_FULL: speed = "FS"; break; case USB_SPEED_LOW: speed = "LS"; break; default: speed = "????"; break; }; speed;})); IFX_ERROR(" Max packet size: %d\n", usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))); IFX_ERROR(" Data buffer length: %d\n", urb->transfer_buffer_length); IFX_ERROR(" Transfer buffer: %p, Transfer DMA: %p\n", urb->transfer_buffer, (void *)urb->transfer_dma); IFX_ERROR(" Setup buffer: %p, Setup DMA: %p\n", urb->setup_packet, (void *)urb->setup_dma); IFX_ERROR(" Interval: %d\n", urb->interval); #endif //__DEBUG__ } /* * Handles a host channel ACK interrupt. This interrupt is enabled when * errors occur, and during Start Split transactions. */ static int32_t handle_hc_ack_intr(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { _urbd->error_count=0; _ifxhc->erron = 0; disable_hc_int(_hc_regs,nyet); #ifdef __NAKSTOP__ if(!_ifxhc->stop_on) { disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nak); } #else disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nak); #endif return 1; } /* * Handles a host channel ACK interrupt. This interrupt is enabled when * errors occur, and during Start Split transactions. */ static int32_t handle_hc_nak_intr(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { _urbd->error_count=0; _ifxhc->erron=0; disable_hc_int(_hc_regs,nyet); disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nak); #ifdef __NAKSTOP__ if(_ifxhc->stop_on) { hcchar_data_t hcchar; hcchar.d32 = ifxusb_rreg(&_hc_regs->hcchar); if(hcchar.b.chen) { hcchar.b.chdis = 1; _ifxhc->halt_status = HC_XFER_NAK; ifxusb_wreg(&_hc_regs->hcchar, hcchar.d32); } } #endif return 1; } static int32_t handle_hc_nyet_intr(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { _urbd->error_count=0; _ifxhc->erron = 0; disable_hc_int(_hc_regs,nyet); #ifdef __NAKSTOP__ if(!_ifxhc->stop_on) { disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nak); } #else disable_hc_int(_hc_regs,ack); disable_hc_int(_hc_regs,nak); #endif return 1; } /* * Handles a host channel AHB error interrupt. This handler is only called in * DMA mode. */ static int32_t handle_hc_ahberr_intr(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { IFX_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " "AHB Error--\n", _ifxhc->hc_num); hc_other_intr_dump(_ifxhcd,_ifxhc,_hc_regs,_urbd); ifxhcd_hc_halt(&_ifxhcd->core_if, _ifxhc, HC_XFER_AHB_ERR); return 1; } /* * Datatoggle */ static int32_t handle_hc_datatglerr_intr(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { IFX_ERROR( "--Host Channel %d Interrupt: " "DATATOGGLE Error--\n", _ifxhc->hc_num); hc_other_intr_dump(_ifxhcd,_ifxhc,_hc_regs,_urbd); disable_hc_int(_hc_regs,datatglerr); return 1; } /* * Interrupts which should not been triggered */ static int32_t handle_hc_frmovrun_intr(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { IFX_ERROR( "--Host Channel %d Interrupt: " "FrameOverRun Error--\n", _ifxhc->hc_num); hc_other_intr_dump(_ifxhcd,_ifxhc,_hc_regs,_urbd); disable_hc_int(_hc_regs,frmovrun); return 1; } static int32_t handle_hc_bblerr_intr(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { IFX_ERROR( "--Host Channel %d Interrupt: " "BBL Error--\n", _ifxhc->hc_num); hc_other_intr_dump(_ifxhcd,_ifxhc,_hc_regs,_urbd); disable_hc_int(_hc_regs,bblerr); return 1; } static int32_t handle_hc_xacterr_intr(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { IFX_ERROR( "--Host Channel %d Interrupt: " "XACT Error--\n", _ifxhc->hc_num); hc_other_intr_dump(_ifxhcd,_ifxhc,_hc_regs,_urbd); disable_hc_int(_hc_regs,xacterr); return 1; } static int32_t handle_hc_stall_intr(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { IFX_ERROR( "--Host Channel %d Interrupt: " "STALL--\n", _ifxhc->hc_num); hc_other_intr_dump(_ifxhcd,_ifxhc,_hc_regs,_urbd); disable_hc_int(_hc_regs,stall); return 1; } static int32_t handle_hc_xfercomp_intr(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxusb_hc_regs_t *_hc_regs, ifxhcd_urbd_t *_urbd) { IFX_ERROR( "--Host Channel %d Interrupt: " "XFERCOMP--\n", _ifxhc->hc_num); hc_other_intr_dump(_ifxhcd,_ifxhc,_hc_regs,_urbd); disable_hc_int(_hc_regs,xfercomp); return 1; } /* This interrupt indicates that the specified host channels has a pending * interrupt. There are multiple conditions that can cause each host channel * interrupt. This function determines which conditions have occurred for this * host channel interrupt and handles them appropriately. */ static int32_t handle_hc_n_intr (ifxhcd_hcd_t *_ifxhcd, uint32_t _num) { uint32_t hcintval,hcintmsk; hcint_data_t hcint; ifxhcd_hc_t *ifxhc; ifxusb_hc_regs_t *hc_regs; ifxhcd_urbd_t *urbd; int retval = 0; IFX_DEBUGPL(DBG_HCDV, "--Host Channel Interrupt--, Channel %d\n", _num); ifxhc = &_ifxhcd->ifxhc[_num]; hc_regs = _ifxhcd->core_if.hc_regs[_num]; hcintval = ifxusb_rreg(&hc_regs->hcint); hcintmsk = ifxusb_rreg(&hc_regs->hcintmsk); hcint.d32 = hcintval & hcintmsk; IFX_DEBUGPL(DBG_HCDV, " 0x%08x & 0x%08x = 0x%08x\n", hcintval, hcintmsk, hcint.d32); urbd = ifxhc->epqh->urbd; if (hcint.b.ahberr) retval |= handle_hc_ahberr_intr(_ifxhcd, ifxhc, hc_regs, urbd); else if (hcint.b.chhltd) retval |= handle_hc_chhltd_intr(_ifxhcd, ifxhc, hc_regs, urbd); else { if (hcint.b.datatglerr) retval |= handle_hc_datatglerr_intr(_ifxhcd, ifxhc, hc_regs, urbd); if (hcint.b.frmovrun) retval |= handle_hc_frmovrun_intr(_ifxhcd, ifxhc, hc_regs, urbd); if (hcint.b.bblerr) retval |= handle_hc_bblerr_intr(_ifxhcd, ifxhc, hc_regs, urbd); if (hcint.b.xacterr) retval |= handle_hc_xacterr_intr(_ifxhcd, ifxhc, hc_regs, urbd); if (hcint.b.nyet) retval |= handle_hc_nyet_intr(_ifxhcd, ifxhc, hc_regs, urbd); if (hcint.b.ack) retval |= handle_hc_ack_intr(_ifxhcd, ifxhc, hc_regs, urbd); if (hcint.b.nak) retval |= handle_hc_nak_intr(_ifxhcd, ifxhc, hc_regs, urbd); if (hcint.b.stall) retval |= handle_hc_stall_intr(_ifxhcd, ifxhc, hc_regs, urbd); if (hcint.b.xfercomp) retval |= handle_hc_xfercomp_intr(_ifxhcd, ifxhc, hc_regs, urbd); } ifxusb_wreg(&hc_regs->hcint,hcintval); return retval; } static uint8_t update_interval_counter(ifxhcd_epqh_t *_epqh,uint32_t _diff) { if(_diff>=_epqh->period_counter) { _epqh->period_do=1; if(_diff>_epqh->interval) _epqh->period_counter=1; else _epqh->period_counter=_epqh->period_counter+_epqh->interval-_diff; return 1; } _epqh->period_counter=_epqh->period_counter-_diff; return 0; } static void process_unaligned( ifxhcd_epqh_t *_epqh, ifxusb_core_if_t *_core_if) { ifxhcd_urbd_t *urbd; urbd =_epqh->urbd; #if defined(__UNALIGNED_BUF_ADJ__) || defined(__UNALIGNED_BUF_CHK__) if(!urbd->aligned_checked) { #if defined(__UNALIGNED_BUF_ADJ__) uint32_t xfer_len; xfer_len=urbd->xfer_len; if(urbd->is_in && xfer_len<_epqh->mps) xfer_len = _epqh->mps; // urbd->using_aligned_buf=0; if(xfer_len > 0 && ((unsigned long)urbd->xfer_buff) & _core_if->unaligned_mask) { if( urbd->aligned_buf && urbd->aligned_buf_len > 0 && urbd->aligned_buf_len < xfer_len ) { ifxusb_free_buf_h(urbd->aligned_buf); urbd->aligned_buf=NULL; urbd->aligned_buf_len=0; } if(! urbd->aligned_buf || ! urbd->aligned_buf_len) { urbd->aligned_buf = ifxusb_alloc_buf_h(xfer_len, urbd->is_in); if(urbd->aligned_buf) urbd->aligned_buf_len = xfer_len; } if(urbd->aligned_buf) { if(!urbd->is_in) memcpy(urbd->aligned_buf, urbd->xfer_buff, xfer_len); // urbd->using_aligned_buf=1; _epqh->hc->xfer_buff = urbd->aligned_buf; } else IFX_WARN("%s():%d\n",__func__,__LINE__); } if(_epqh->ep_type==IFXUSB_EP_TYPE_CTRL) { // urbd->using_aligned_setup=0; if(((unsigned long)urbd->setup_buff) & _core_if->unaligned_mask) { if(! urbd->aligned_setup) urbd->aligned_setup = ifxusb_alloc_buf_h(8,0); if(urbd->aligned_setup) { memcpy(urbd->aligned_setup, urbd->setup_buff, 8); // urbd->using_aligned_setup=1; } else IFX_WARN("%s():%d\n",__func__,__LINE__); _epqh->hc->xfer_buff = urbd->aligned_setup; } } #elif defined(__UNALIGNED_BUF_CHK__) if(_epqh->urbd->is_in) { if(_epqh->urbd->xfer_len==0) IFX_WARN("%s():%d IN xfer while length is zero \n",__func__,__LINE__); else{ if(_epqh->urbd->xfer_len < _epqh->mps) IFX_WARN("%s():%d IN xfer while length < mps \n",__func__,__LINE__); if(((unsigned long)_epqh->urbd->xfer_buff) & _core_if->unaligned_mask) IFX_WARN("%s():%d IN xfer Buffer UNALIGNED\n",__func__,__LINE__); } } else { if(_epqh->urbd->xfer_len > 0 && (((unsigned long)_epqh->urbd->xfer_buff) & _core_if->unaligned_mask)) IFX_WARN("%s():%d OUT xfer Buffer UNALIGNED\n",__func__,__LINE__); } if(_epqh->ep_type==IFXUSB_EP_TYPE_CTRL) { if(((unsigned long)_epqh->urbd->setup_buff) & _core_if->unaligned_mask) IFX_WARN("%s():%d SETUP xfer Buffer UNALIGNED\n",__func__,__LINE__); } #endif } urbd->aligned_checked=1; #endif } /*! \brief Assigns transactions from a URBD to a free host channel and initializes the host channel to perform the transactions. The host channel is removed from the free list. \param _ifxhcd The HCD state structure. \param _epqh Transactions from the first URBD for this EPQH are selected and assigned to a free host channel. */ static int assign_hc(ifxhcd_hcd_t *_ifxhcd, ifxhcd_epqh_t *_epqh,ifxhcd_urbd_t *_urbd) { ifxhcd_hc_t *ifxhc; struct urb *urb; IFX_DEBUGPL(DBG_HCDV, "%s(%p,%p)\n", __func__, _ifxhcd, _epqh); if(_ifxhcd->disconnecting) { printk(KERN_INFO "Warning: %s() Port is in discoonection\n",__func__); return 0; } if(!_epqh) return 0; if(!_urbd) return 0; if(!_urbd->urb) return 0; { int i; int num_channels = _ifxhcd->core_if.params.host_channels; for(i=0;icore_if.hc_regs[i]; if(_ifxhcd->ifxhc[i].phase!=HC_IDLE) { continue; } hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar); if(hcchar.b.chen || hcchar.b.chdis) { continue; } break; } if(iifxhc[i]; ifxhc->phase=HC_ASSIGNED; } else return 0; } urb = _urbd->urb; _epqh->hc = ifxhc; _epqh->urbd = _urbd; ifxhc->epqh = _epqh; /* * Use usb_pipedevice to determine device address. This address is * 0 before the SET_ADDRESS command and the correct address afterward. */ ifxhc->dev_addr = usb_pipedevice(urb->pipe); ifxhc->ep_num = usb_pipeendpoint(urb->pipe); if (urb->dev->speed == USB_SPEED_LOW) ifxhc->speed = IFXUSB_EP_SPEED_LOW; else if (urb->dev->speed == USB_SPEED_FULL) ifxhc->speed = IFXUSB_EP_SPEED_FULL; else ifxhc->speed = IFXUSB_EP_SPEED_HIGH; ifxhc->mps = _epqh->mps; ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; ifxhc->ep_type = _epqh->ep_type; ifxhc->split = 0; if (_epqh->need_split) { ifxhc->split = 1; ifxhc->hub_addr = urb->dev->tt->hub->devnum; ifxhc->port_addr = urb->dev->ttport; } return 1; } /*! \brief Assigns transactions from a URBD to a free host channel and initializes the host channel to perform the transactions. The host channel is removed from the free list. \param _ifxhcd The HCD state structure. \param _epqh Transactions from the first URBD for this EPQH are selected and assigned to a free host channel. */ static void init_hc(ifxhcd_epqh_t *_epqh) { ifxhcd_hc_t *ifxhc; ifxhcd_urbd_t *urbd; struct urb *urb; ifxhcd_hcd_t *ifxhcd; IFX_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, _epqh); ifxhc =_epqh->hc; urbd =_epqh->urbd; ifxhcd=_epqh->ifxhcd; urb = urbd->urb; #if defined(__UNALIGNED_BUF_ADJ__) || defined(__UNALIGNED_BUF_CHK__) urbd->aligned_checked=0; #endif ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; if(_epqh->ep_type==IFXUSB_EP_TYPE_CTRL) { ifxhc->control_phase =IFXHCD_CONTROL_SETUP; ifxhc->is_in = 0; ifxhc->data_pid_start = IFXUSB_HC_PID_SETUP; ifxhc->xfer_buff = urbd->setup_buff; ifxhc->xfer_len = 8; ifxhc->xfer_count = 0; ifxhc->short_rw =(urb->transfer_flags & URB_ZERO_PACKET)?1:0; ifxhc->sof_delay = 0; _epqh->do_ping=0; if(!ifxhc->is_in && ifxhc->split==0) _epqh->do_ping=1; } else if(_epqh->ep_type==IFXUSB_EP_TYPE_ISOC) { #ifdef __EN_ISOC__ struct usb_iso_packet_descriptor *frame_desc; ifxhc->is_in = urbd->is_in; frame_desc = &urb->iso_frame_desc[urbd->isoc_frame_index]; urbd->xfer_len = ifxhc->xfer_len = frame_desc->length; ifxhc->xfer_buff = urbd->xfer_buff; ifxhc->xfer_buff += frame_desc->offset; ifxhc->xfer_count = 0; ifxhc->sof_delay = 0; if(usb_gettoggle (urb->dev,usb_pipeendpoint (urb->pipe), (ifxhc->is_in)?0:1)) ifxhc->data_pid_start = IFXUSB_HCTSIZ_DATA1; else ifxhc->data_pid_start = IFXUSB_HCTSIZ_DATA0; if(ifxhc->is_in) ifxhc->short_rw =0; else ifxhc->short_rw =(urb->transfer_flags & URB_ZERO_PACKET)?1:0; #ifdef __EN_ISOC_SPLIT__ ifxhc->isoc_xact_pos = IFXUSB_HCSPLIT_XACTPOS_ALL; #endif _epqh->isoc_frame_index=0; _epqh->isoc_now=0; _epqh->isoc_start_frame=0; if(_urb->transfer_flags && URB_ISO_ASAP) _epqh->isoc_now=1; else _epqh->isoc_start_frame=_urb->start_frame; #ifdef __EN_ISOC_SPLIT__ _epqh->isoc_split_pos =0; _epqh->isoc_split_offset=0; #endif _epqh->do_ping=0; #endif } else { ifxhc->is_in = urbd->is_in; ifxhc->xfer_buff = urbd->xfer_buff; ifxhc->xfer_len = urbd->xfer_len; ifxhc->xfer_count = 0; ifxhc->sof_delay = 0; // if(ifxhc->xfer_len==13 && ifxhc->is_in && _epqh->ep_type==IFXUSB_EP_TYPE_BULK && ifxhc->split==0) // ifxhc->sof_delay = 8; if(usb_gettoggle (urb->dev,usb_pipeendpoint (urb->pipe), (ifxhc->is_in)?0:1)) ifxhc->data_pid_start = IFXUSB_HCTSIZ_DATA1; else ifxhc->data_pid_start = IFXUSB_HCTSIZ_DATA0; if(ifxhc->is_in) ifxhc->short_rw =0; else ifxhc->short_rw =(urb->transfer_flags & URB_ZERO_PACKET)?1:0; _epqh->do_ping=0; if(!ifxhc->is_in && ifxhc->split==0) { if(_epqh->ep_type==IFXUSB_EP_TYPE_BULK) _epqh->do_ping=1; } } { hcint_data_t hc_intr_mask; uint8_t hc_num = ifxhc->hc_num; ifxusb_hc_regs_t *hc_regs = ifxhcd->core_if.hc_regs[hc_num]; /* Clear old interrupt conditions for this host channel. */ hc_intr_mask.d32 = 0xFFFFFFFF; hc_intr_mask.b.reserved = 0; ifxusb_wreg(&hc_regs->hcint, hc_intr_mask.d32); /* Enable channel interrupts required for this transfer. */ hc_intr_mask.d32 = 0; hc_intr_mask.b.chhltd = 1; hc_intr_mask.b.ahberr = 1; ifxusb_wreg(&hc_regs->hcintmsk, hc_intr_mask.d32); /* Enable the top level host channel interrupt. */ { uint32_t intr_enable; intr_enable = (1 << hc_num); ifxusb_mreg(&ifxhcd->core_if.host_global_regs->haintmsk, 0, intr_enable); } /* Make sure host channel interrupts are enabled. */ { gint_data_t gintmsk ={.d32 = 0}; gintmsk.b.hcintr = 1; ifxusb_mreg(&ifxhcd->core_if.core_global_regs->gintmsk, 0, gintmsk.d32); } /* * Program the HCCHARn register with the endpoint characteristics for * the current transfer. */ { hcchar_data_t hcchar; hcchar.d32 = 0; hcchar.b.devaddr = ifxhc->dev_addr; hcchar.b.epnum = ifxhc->ep_num; hcchar.b.lspddev = (ifxhc->speed == IFXUSB_EP_SPEED_LOW); hcchar.b.eptype = ifxhc->ep_type; hcchar.b.mps = ifxhc->mps; ifxusb_wreg(&hc_regs->hcchar, hcchar.d32); IFX_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, ifxhc->hc_num); IFX_DEBUGPL(DBG_HCDV, " Dev Addr: %d\n" , hcchar.b.devaddr); IFX_DEBUGPL(DBG_HCDV, " Ep Num: %d\n" , hcchar.b.epnum); IFX_DEBUGPL(DBG_HCDV, " Is Low Speed: %d\n", hcchar.b.lspddev); IFX_DEBUGPL(DBG_HCDV, " Ep Type: %d\n" , hcchar.b.eptype); IFX_DEBUGPL(DBG_HCDV, " Max Pkt: %d\n" , hcchar.b.mps); IFX_DEBUGPL(DBG_HCDV, " Multi Cnt: %d\n" , hcchar.b.multicnt); } /* Program the HCSPLIT register for SPLITs */ { hcsplt_data_t hcsplt; hcsplt.d32 = 0; if (ifxhc->split) { IFX_DEBUGPL(DBG_HCDV, "Programming HC %d with split --> %s\n", ifxhc->hc_num, (ifxhc->split==2) ? "CSPLIT" : "SSPLIT"); hcsplt.b.spltena = 1; hcsplt.b.compsplt = (ifxhc->split==2); #if defined(__EN_ISOC__) && defined(__EN_ISOC_SPLIT__) if(_epqh->ep_type==IFXUSB_EP_TYPE_ISOC) hcsplt.b.xactpos = ifxhc->isoc_xact_pos; else #endif hcsplt.b.xactpos = IFXUSB_HCSPLIT_XACTPOS_ALL; hcsplt.b.hubaddr = ifxhc->hub_addr; hcsplt.b.prtaddr = ifxhc->port_addr; IFX_DEBUGPL(DBG_HCDV, " comp split %d\n" , hcsplt.b.compsplt); IFX_DEBUGPL(DBG_HCDV, " xact pos %d\n" , hcsplt.b.xactpos); IFX_DEBUGPL(DBG_HCDV, " hub addr %d\n" , hcsplt.b.hubaddr); IFX_DEBUGPL(DBG_HCDV, " port addr %d\n" , hcsplt.b.prtaddr); IFX_DEBUGPL(DBG_HCDV, " is_in %d\n" , ifxhc->is_in); IFX_DEBUGPL(DBG_HCDV, " Max Pkt: %d\n" , ifxhc->mps); IFX_DEBUGPL(DBG_HCDV, " xferlen: %d\n" , ifxhc->xfer_len); } ifxusb_wreg(&hc_regs->hcsplt, hcsplt.d32); } } process_unaligned(_epqh,&ifxhcd->core_if); #ifdef __NAKSTOP__ ifxhc->stop_on=0; if (!ifxhc->split && ifxhc->ep_type == IFXUSB_EP_TYPE_BULK) { #ifdef __INNAKSTOP_BULK__ if(ifxhc->is_in) ifxhc->stop_on=1; #endif #ifdef __PINGSTOP_BULK__ if(!ifxhc->is_in) ifxhc->stop_on=1; #endif } #endif } static void select_eps_sub(ifxhcd_hcd_t *_ifxhcd) { struct list_head *epqh_ptr; ifxhcd_epqh_t *epqh; struct list_head *urbd_ptr; unsigned long flags; ifxhcd_urbd_t *urbd; hfnum_data_t hfnum; uint32_t fndiff; if(_ifxhcd->disconnecting) { // printk(KERN_INFO "Warning: %s() Port is in discoonection\n",__func__); return ; } local_irq_save(flags); LOCK_EPQH_LIST(_ifxhcd); hfnum.d32 = ifxusb_rreg(&_ifxhcd->core_if.host_global_regs->hfnum); fndiff = hfnum.b.frnum; fndiff+= 0x00004000; fndiff-= _ifxhcd->lastframe ; fndiff&= 0x00003FFF; if(!fndiff) fndiff =1; #ifdef __EN_ISOC__ epqh_ptr = _ifxhcd->epqh_list_isoc.next; while (epqh_ptr != &_ifxhcd->epqh_list_isoc) { epqh = list_entry(epqh_ptr, ifxhcd_epqh_t, ql); epqh_ptr = epqh_ptr->next; #ifdef __DYN_SOF_INTR__ if (!list_empty(&epqh->urbd_list)) _ifxhcd->dyn_sof_count = DYN_SOF_COUNT_DEF; #endif if(epqh->pause) continue; if(epqh->phase==EPQH_READY) { if(update_interval_counter(epqh,fndiff) || epqh->isoc_now) { LOCK_URBD_LIST(epqh); urbd_ptr = epqh->urbd_list.next; while (urbd_ptr != &epqh->urbd_list) { urbd = list_entry(urbd_ptr, ifxhcd_urbd_t, ql); urbd_ptr=urbd_ptr->next; if(urbd->phase==URBD_IDLE) { if(assign_hc(_ifxhcd, epqh,urbd)) { IFX_DEBUGPL(DBG_HCD, " select_eps ISOC\n"); #ifdef __EPQD_DESTROY_TIMEOUT__ del_timer(&epqh->destroy_timer); #endif epqh->isoc_now=0; list_del_init (&epqh->ql); list_add_tail(&epqh->ql, &_ifxhcd->epqh_list_isoc); init_hc(epqh); epqh->phase=EPQH_ACTIVE; urbd->phase==URBD_ACTIVE; epqh->hc.phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, epqh->hc); } break; } } UNLOCK_URBD_LIST(epqh); } } } #endif //__EN_ISOC__ epqh_ptr = _ifxhcd->epqh_list_intr.next; while (epqh_ptr != &_ifxhcd->epqh_list_intr) { epqh = list_entry(epqh_ptr, ifxhcd_epqh_t, ql); epqh_ptr = epqh_ptr->next; #ifdef __DYN_SOF_INTR__ if (!list_empty(&epqh->urbd_list)) _ifxhcd->dyn_sof_count = DYN_SOF_COUNT_DEF; #endif if(epqh->pause) continue; if(epqh->phase==EPQH_READY) { if(update_interval_counter(epqh,fndiff)) { LOCK_URBD_LIST(epqh); urbd_ptr = epqh->urbd_list.next; while (urbd_ptr != &epqh->urbd_list) { urbd = list_entry(urbd_ptr, ifxhcd_urbd_t, ql); urbd_ptr=urbd_ptr->next; if(urbd->phase==URBD_IDLE) { if(assign_hc(_ifxhcd, epqh,urbd)) { IFX_DEBUGPL(DBG_HCD, " select_eps INTR\n"); #ifdef __EPQD_DESTROY_TIMEOUT__ del_timer(&epqh->destroy_timer); #endif list_del_init (&epqh->ql); list_add_tail(&epqh->ql, &_ifxhcd->epqh_list_intr); init_hc(epqh); epqh->phase=EPQH_ACTIVE; urbd->phase=URBD_ACTIVE; epqh->hc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, epqh->hc); } break; } } UNLOCK_URBD_LIST(epqh); } } else if(epqh->phase==EPQH_STDBY) { if(epqh->period_counter > 0 ) epqh->period_counter --; if(epqh->period_counter == 0) ifxhcd_epqh_idle_periodic(epqh); update_interval_counter(epqh,fndiff); } else update_interval_counter(epqh,fndiff); } epqh_ptr = _ifxhcd->epqh_list_np.next; while (epqh_ptr != &_ifxhcd->epqh_list_np) // may need to preserve at lease one for period { epqh = list_entry(epqh_ptr, ifxhcd_epqh_t, ql); epqh_ptr = epqh_ptr->next; #ifdef __DYN_SOF_INTR__ if (!list_empty(&epqh->urbd_list)) _ifxhcd->dyn_sof_count = DYN_SOF_COUNT_DEF; #endif if(epqh->pause) continue; if(epqh->phase==EPQH_READY) { LOCK_URBD_LIST(epqh); urbd_ptr = epqh->urbd_list.next; while (urbd_ptr != &epqh->urbd_list) { urbd = list_entry(urbd_ptr, ifxhcd_urbd_t, ql); urbd_ptr=urbd_ptr->next; if(urbd->phase==URBD_IDLE) { if(assign_hc(_ifxhcd, epqh,urbd)) { IFX_DEBUGPL(DBG_HCD, " select_eps Non-Period\n"); #ifdef __EPQD_DESTROY_TIMEOUT__ del_timer(&epqh->destroy_timer); #endif list_del_init (&epqh->ql); list_add_tail(&epqh->ql, &_ifxhcd->epqh_list_np); init_hc(epqh); epqh->phase=EPQH_ACTIVE; urbd->phase=URBD_ACTIVE; epqh->hc->phase=HC_WAITING; ifxhcd_hc_start(_ifxhcd, epqh->hc); } break; } } UNLOCK_URBD_LIST(epqh); } } _ifxhcd->lastframe=hfnum.b.frnum; UNLOCK_EPQH_LIST(_ifxhcd); local_irq_restore(flags); } static void select_eps_func(unsigned long data) { ifxhcd_hcd_t *ifxhcd; ifxhcd=((ifxhcd_hcd_t *)data); select_eps_sub(ifxhcd); } /*! \fn void select_eps(ifxhcd_hcd_t *_ifxhcd) \brief This function selects transactions from the HCD transfer schedule and assigns them to available host channels. \param _ifxhcd Pointer to the sate of HCD structure \ingroup IFXUSB_HCD */ void select_eps(ifxhcd_hcd_t *_ifxhcd) { if(in_irq()) { if(!_ifxhcd->tasklet_select_eps.func) { _ifxhcd->tasklet_select_eps.next = NULL; _ifxhcd->tasklet_select_eps.state = 0; atomic_set( &_ifxhcd->tasklet_select_eps.count, 0); _ifxhcd->tasklet_select_eps.func = select_eps_func; _ifxhcd->tasklet_select_eps.data = (unsigned long)_ifxhcd; } tasklet_schedule(&_ifxhcd->tasklet_select_eps); } else { select_eps_sub(_ifxhcd); } } static void ifxhcd_hc_kickstart(ifxhcd_hcd_t *_ifxhcd) { int num_channels; ifxusb_hc_regs_t *hc_regs; int i; ifxhcd_hc_t *ifxhc; num_channels = _ifxhcd->core_if.params.host_channels; for (i = 0; i < num_channels; i++) { ifxhc=&_ifxhcd->ifxhc[i]; if(ifxhc->phase==HC_STARTING) { if(ifxhc->sof_delay) ifxhc->sof_delay--; if(!ifxhc->sof_delay) { hcint_data_t hcint; // ifxhc->erron=0; hc_regs = _ifxhcd->core_if.hc_regs[i]; hcint.d32 =0xFFFFFFFF; ifxusb_wreg(&hc_regs->hcint, hcint.d32); hcint.d32 =ifxusb_rreg(&hc_regs->hcintmsk); hcint.b.nak =0; hcint.b.ack =0; hcint.b.nyet=0; if(ifxhc->erron) { hcint.b.ack =1; hcint.b.nak =1; hcint.b.nyet =1; } #ifdef __NAKSTOP__ if(ifxhc->stop_on) { hcint.b.ack =1; hcint.b.nak =1; } #endif ifxusb_wreg(&hc_regs->hcintmsk, hcint.d32); ifxusb_wreg(&hc_regs->hcchar, ifxhc->hcchar); ifxhc->phase=HC_STARTED; } } } for (i = 0; i < num_channels; i++) { ifxhc=&_ifxhcd->ifxhc[i]; if(ifxhc->phase==HC_WAITING && (ifxhc->ep_type == IFXUSB_EP_TYPE_INTR || ifxhc->ep_type == IFXUSB_EP_TYPE_ISOC) ) { ifxhcd_hc_start(_ifxhcd, ifxhc); } } for (i = 0; i < num_channels; i++) { ifxhc=&_ifxhcd->ifxhc[i]; if(ifxhc->phase==HC_WAITING) { ifxhcd_hc_start(_ifxhcd, ifxhc); } } } /* * Handles the start-of-frame interrupt in host mode. Non-periodic * transactions may be queued to the DWC_otg controller for the current * (micro)frame. Periodic transactions may be queued to the controller for the * next (micro)frame. */ static int32_t handle_sof_intr (ifxhcd_hcd_t *_ifxhcd) { _ifxhcd->pkt_remaining=_ifxhcd->pkt_remaining_reload; ifxhcd_hc_kickstart(_ifxhcd); select_eps(_ifxhcd); /* Clear interrupt */ { gint_data_t gintsts; gintsts.d32=0; gintsts.b.sofintr = 1; ifxusb_wreg(&_ifxhcd->core_if.core_global_regs->gintsts, gintsts.d32); #ifdef __DYN_SOF_INTR__ if(_ifxhcd->dyn_sof_count) _ifxhcd->dyn_sof_count--; if(!_ifxhcd->dyn_sof_count) ifxusb_mreg(&_ifxhcd->core_if.core_global_regs->gintmsk, gintsts.d32,0); #endif } return 1; } /* There are multiple conditions that can cause a port interrupt. This function * determines which interrupt conditions have occurred and handles them * appropriately. */ static int32_t handle_port_intr (ifxhcd_hcd_t *_ifxhcd) { int retval = 0; hprt0_data_t hprt0; hprt0_data_t hprt0_modify; hprt0.d32 = hprt0_modify.d32 = ifxusb_rreg(_ifxhcd->core_if.hprt0); /* Clear appropriate bits in HPRT0 to clear the interrupt bit in * GINTSTS */ hprt0_modify.b.prtena = 0; hprt0_modify.b.prtconndet = 0; hprt0_modify.b.prtenchng = 0; hprt0_modify.b.prtovrcurrchng = 0; /* Port Connect Detected * Set flag and clear if detected */ if (hprt0.b.prtconndet) { IFX_DEBUGPL(DBG_HCD, "--Port Interrupt HPRT0=0x%08x " "Port Connect Detected--\n", hprt0.d32); _ifxhcd->flags.b.port_connect_status_change = 1; _ifxhcd->flags.b.port_connect_status = 1; hprt0_modify.b.prtconndet = 1; /* The Hub driver asserts a reset when it sees port connect * status change flag */ retval |= 1; } /* Port Enable Changed * Clear if detected - Set internal flag if disabled */ if (hprt0.b.prtenchng) { IFX_DEBUGPL(DBG_HCD, " --Port Interrupt HPRT0=0x%08x " "Port Enable Changed--\n", hprt0.d32); hprt0_modify.b.prtenchng = 1; if (hprt0.b.prtena == 1) { /* Port has been enabled set the reset change flag */ _ifxhcd->flags.b.port_reset_change = 1; if (hprt0.b.prtspd == IFXUSB_HPRT0_PRTSPD_HIGH_SPEED) _ifxhcd->pkt_remaining_reload=_ifxhcd->pkt_remaining_reload_hs; else if (hprt0.b.prtspd == IFXUSB_HPRT0_PRTSPD_LOW_SPEED) _ifxhcd->pkt_remaining_reload=_ifxhcd->pkt_remaining_reload_ls; else _ifxhcd->pkt_remaining_reload=_ifxhcd->pkt_remaining_reload_fs; } else _ifxhcd->flags.b.port_enable_change = 1; retval |= 1; } /* Overcurrent Change Interrupt */ if (hprt0.b.prtovrcurrchng) { IFX_DEBUGPL(DBG_HCD, " --Port Interrupt HPRT0=0x%08x " "Port Overcurrent Changed--\n", hprt0.d32); _ifxhcd->flags.b.port_over_current_change = 1; hprt0_modify.b.prtovrcurrchng = 1; retval |= 1; } /* Clear Port Interrupts */ ifxusb_wreg(_ifxhcd->core_if.hprt0, hprt0_modify.d32); return retval; } /* * This interrupt indicates that SUSPEND state has been detected on * the USB. * No Functioning in Host Mode */ static int32_t handle_usb_suspend_intr(ifxhcd_hcd_t *_ifxhcd) { gint_data_t gintsts; IFX_DEBUGP("USB SUSPEND RECEIVED!\n"); /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.usbsuspend = 1; ifxusb_wreg(&_ifxhcd->core_if.core_global_regs->gintsts, gintsts.d32); return 1; } /* * This interrupt indicates that the IFXUSB controller has detected a * resume or remote wakeup sequence. If the IFXUSB controller is in * low power mode, the handler must brings the controller out of low * power mode. The controller automatically begins resume * signaling. The handler schedules a time to stop resume signaling. */ static int32_t handle_wakeup_detected_intr(ifxhcd_hcd_t *_ifxhcd) { gint_data_t gintsts; hprt0_data_t hprt0 = {.d32=0}; pcgcctl_data_t pcgcctl = {.d32=0}; ifxusb_core_if_t *core_if = &_ifxhcd->core_if; IFX_DEBUGPL(DBG_ANY, "++Resume and Remote Wakeup Detected Interrupt++\n"); /* * Clear the Resume after 70ms. (Need 20 ms minimum. Use 70 ms * so that OPT tests pass with all PHYs). */ /* Restart the Phy Clock */ pcgcctl.b.stoppclk = 1; ifxusb_mreg(core_if->pcgcctl, pcgcctl.d32, 0); UDELAY(10); /* Now wait for 70 ms. */ hprt0.d32 = ifxusb_read_hprt0( core_if ); IFX_DEBUGPL(DBG_ANY,"Resume: HPRT0=%0x\n", hprt0.d32); MDELAY(70); hprt0.b.prtres = 0; /* Resume */ ifxusb_wreg(core_if->hprt0, hprt0.d32); IFX_DEBUGPL(DBG_ANY,"Clear Resume: HPRT0=%0x\n", ifxusb_rreg(core_if->hprt0)); /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.wkupintr = 1; ifxusb_wreg(&core_if->core_global_regs->gintsts, gintsts.d32); return 1; } /* * This interrupt indicates that a device is initiating the Session * Request Protocol to request the host to turn on bus power so a new * session can begin. The handler responds by turning on bus power. If * the DWC_otg controller is in low power mode, the handler brings the * controller out of low power mode before turning on bus power. */ static int32_t handle_session_req_intr(ifxhcd_hcd_t *_ifxhcd) { /* Clear interrupt */ gint_data_t gintsts = { .d32 = 0 }; gintsts.b.sessreqintr = 1; ifxusb_wreg(&_ifxhcd->core_if.core_global_regs->gintsts, gintsts.d32); return 1; } /* * This interrupt indicates that a device has been disconnected from * the root port. */ static int32_t handle_disconnect_intr(ifxhcd_hcd_t *_ifxhcd) { gint_data_t gintsts; ifxhcd_disconnect(_ifxhcd); gintsts.d32 = 0; gintsts.b.disconnect = 1; ifxusb_wreg(&_ifxhcd->core_if.core_global_regs->gintsts, gintsts.d32); return 1; } /* * This function handles the Connector ID Status Change Interrupt. It * reads the OTG Interrupt Register (GOTCTL) to determine whether this * is a Device to Host Mode transition or a Host Mode to Device * Transition. * This only occurs when the cable is connected/removed from the PHY * connector. */ static int32_t handle_conn_id_status_change_intr(ifxhcd_hcd_t *_ifxhcd) { gint_data_t gintsts; IFX_WARN("ID Status Change Interrupt: currently in %s mode\n", ifxusb_mode(&_ifxhcd->core_if) ? "Host" : "Device"); gintsts.d32 = 0; gintsts.b.conidstschng = 1; ifxusb_wreg(&_ifxhcd->core_if.core_global_regs->gintsts, gintsts.d32); return 1; } static int32_t handle_otg_intr(ifxhcd_hcd_t *_ifxhcd) { ifxusb_core_global_regs_t *global_regs = _ifxhcd->core_if.core_global_regs; gotgint_data_t gotgint; gotgint.d32 = ifxusb_rreg( &global_regs->gotgint); /* Clear GOTGINT */ ifxusb_wreg (&global_regs->gotgint, gotgint.d32); return 1; } /** This function will log a debug message */ static int32_t handle_mode_mismatch_intr(ifxhcd_hcd_t *_ifxhcd) { gint_data_t gintsts; IFX_WARN("Mode Mismatch Interrupt: currently in %s mode\n", ifxusb_mode(&_ifxhcd->core_if) ? "Host" : "Device"); gintsts.d32 = 0; gintsts.b.modemismatch = 1; ifxusb_wreg(&_ifxhcd->core_if.core_global_regs->gintsts, gintsts.d32); return 1; } /** This function handles interrupts for the HCD. */ int32_t ifxhcd_handle_intr (ifxhcd_hcd_t *_ifxhcd) { int retval = 0; ifxusb_core_if_t *core_if = &_ifxhcd->core_if; gint_data_t gintsts,gintsts2; /* Check if HOST Mode */ if (ifxusb_is_device_mode(core_if)) { IFX_ERROR("%s() CRITICAL! IN DEVICE MODE\n", __func__); return 0; } gintsts.d32 = ifxusb_read_core_intr(core_if); gintsts2.d32 = 0; if (!gintsts.d32) return 0; //Common INT if (gintsts.b.modemismatch) { retval |= handle_mode_mismatch_intr(_ifxhcd); gintsts.b.modemismatch=0; gintsts2.b.modemismatch=1; } if (gintsts.b.otgintr) { retval |= handle_otg_intr(_ifxhcd); gintsts.b.otgintr=0; gintsts2.b.otgintr=1; } if (gintsts.b.conidstschng) { retval |= handle_conn_id_status_change_intr(_ifxhcd); gintsts.b.conidstschng=0; gintsts2.b.conidstschng=1; } if (gintsts.b.disconnect) { retval |= handle_disconnect_intr(_ifxhcd); gintsts.b.disconnect=0; gintsts2.b.disconnect=1; } if (gintsts.b.sessreqintr) { retval |= handle_session_req_intr(_ifxhcd); gintsts.b.sessreqintr=0; gintsts2.b.sessreqintr=1; } if (gintsts.b.wkupintr) { retval |= handle_wakeup_detected_intr(_ifxhcd); gintsts.b.wkupintr=0; gintsts2.b.wkupintr=1; } if (gintsts.b.usbsuspend) { retval |= handle_usb_suspend_intr(_ifxhcd); gintsts.b.usbsuspend=0; gintsts2.b.usbsuspend=1; } //Host Int if (gintsts.b.sofintr) { retval |= handle_sof_intr (_ifxhcd); gintsts.b.sofintr=0; gintsts2.b.sofintr=1; } if (gintsts.b.portintr) { retval |= handle_port_intr (_ifxhcd); gintsts.b.portintr=0; gintsts2.b.portintr=1; } if (gintsts.b.hcintr) { int i; haint_data_t haint; haint.d32 = ifxusb_read_host_all_channels_intr(core_if); for (i=0; iparams.host_channels; i++) if (haint.b2.chint & (1 << i)) retval |= handle_hc_n_intr (_ifxhcd, i); gintsts.b.hcintr=0; gintsts2.b.hcintr=1; } return retval; }