/** * \brief Process successfully sent packets * \param pGmacd Pointer to GMAC Driver instance. */ static void GMACD_TxCompleteHandler(sGmacd *pGmacd, gmacQueList_t qId) { Gmac *pHw = pGmacd->pHw; sGmacTxDescriptor *pTxTd; fGmacdTransferCallback fTxCb; uint32_t tsr; /* Clear status */ tsr = GMAC_GetTxStatus(pHw); GMAC_ClearTxStatus(pHw, tsr); while (!GCIRC_EMPTY( pGmacd->queueList[qId].wTxHead, pGmacd->queueList[qId].wTxTail)) { pTxTd = &pGmacd->queueList[qId].pTxD[pGmacd->queueList[qId].wTxTail]; /* Make hw descriptor updates visible to CPU */ GMAC_CACHE_COHERENCE /* Exit if frame has not been sent yet: * On TX completion, the GMAC set the USED bit only into the * very first buffer descriptor of the sent frame. * Otherwise it updates this descriptor with status error bits. * This is the descriptor write back. */ if ((pTxTd->status.val & GMAC_TX_USED_BIT) == 0) break; /* Process all buffers of the current transmitted frame */ while ((pTxTd->status.val & GMAC_TX_LAST_BUFFER_BIT) == 0) { GCIRC_INC(pGmacd->queueList[qId].wTxTail, pGmacd->queueList[qId].wTxListSize); pTxTd = &pGmacd->queueList[qId].pTxD[pGmacd->queueList[qId].wTxTail]; memory_sync(); } /* Notify upper layer that a frame has been sent */ fTxCb = pGmacd->queueList[qId].fTxCbList[pGmacd->queueList[qId].wTxTail]; if (fTxCb) fTxCb(tsr); /* Go to next frame */ GCIRC_INC(pGmacd->queueList[qId].wTxTail, pGmacd->queueList[qId].wTxListSize); } /* If a wakeup has been scheduled, notify upper layer that it can send other packets, send will be successful. */ if (pGmacd->queueList[qId].fWakupCb && GCIRC_SPACE(pGmacd->queueList[qId].wTxHead, pGmacd->queueList[qId].wTxTail, pGmacd->queueList[qId].wTxListSize) >= pGmacd->queueList[qId].bWakeupThreshold) pGmacd->queueList[qId].fWakupCb(); }
/** * \brief Send a packet with GMAC. If the packet size is larger than transfer buffer size * error returned. If packet transfer status is monitored, specify callback for each packet. * \param pGmacd Pointer to GMAC Driver instance. * \param buffer The buffer to be send * \param size The size of buffer to be send * \param fGMAC_TxCallback Threshold Wakeup callback * \param fWakeUpCb TX Wakeup * \return OK, Busy or invalid packet */ uint8_t GMACD_Send(sGmacd *pGmacd, void *pBuffer, uint32_t size, fGmacdTransferCallback fTxCb ) { Gmac *pHw = pGmacd->pHw; sGmacTxDescriptor *pTxTd; volatile fGmacdTransferCallback *pfTxCb; TRACE_DEBUG("GMAC_Send\n\r"); /* Check parameter */ if (size > GMAC_TX_UNITSIZE) { TRACE_ERROR("GMAC driver does not split send packets."); return GMACD_PARAM; } /* Pointers to the current TxTd */ pTxTd = &pGmacd->pTxD[pGmacd->wTxHead]; /* If no free TxTd, buffer can't be sent */ if( GCIRC_SPACE(pGmacd->wTxHead, pGmacd->wTxTail, pGmacd->wTxListSize) == 0) return GMACD_TX_BUSY; /* Pointers to the current Tx Callback */ pfTxCb = &pGmacd->fTxCbList[pGmacd->wTxHead]; /* Sanity check */ /* Setup/Copy data to transmition buffer */ if (pBuffer && size) { // Driver manage the ring buffer memcpy((void *)pTxTd->addr, pBuffer, size); } /* Tx Callback */ *pfTxCb = fTxCb; /* Update TD status. The buffer size defined is length of ethernet frame so it's always the last buffer of the frame. */ if (pGmacd->wTxHead == pGmacd->wTxListSize-1) { pTxTd->status.val = (size & GMAC_LENGTH_FRAME) | GMAC_TX_LAST_BUFFER_BIT | GMAC_TX_WRAP_BIT; } else { pTxTd->status.val = (size & GMAC_LENGTH_FRAME) | GMAC_TX_LAST_BUFFER_BIT; } GCIRC_INC(pGmacd->wTxHead, pGmacd->wTxListSize); //CP15_flush_dcache_for_dma ((uint32_t)(pTxTd), ((uint32_t)(pTxTd) + sizeof(pTxTd))); /* Tx packets count */ /* Now start to transmit if it is not already done */ GMAC_TransmissionStart(pHw); return GMACD_OK; }
/** * \brief Reset TX queue when errors are detected * \param pGmacd Pointer to GMAC Driver instance. */ static void GMACD_TxErrorHandler(sGmacd *pGmacd, gmacQueList_t qId) { Gmac *pHw = pGmacd->pHw; sGmacTxDescriptor *pTxTd; fGmacdTransferCallback fTxCb; uint32_t tsr; /* Clear TXEN bit into the Network Configuration Register: * this is a workaround to recover from TX lockups that * occur on sama5d3 gmac (r1p24f2) when using scatter-gather. * This issue has never been seen on sama5d4 gmac (r1p31). */ GMAC_TransmitEnable(pHw, 0); /* The following step should be optional since this function is called * directly by the IRQ handler. Indeed, according to Cadence * documentation, the transmission is halted on errors such as * too many retries or transmit under run. * However it would become mandatory if the call of this function * were scheduled as a task by the IRQ handler (this is how Linux * driver works). Then this function might compete with GMACD_Send(). * * Setting bit 10, tx_halt, of the Network Control Register is not enough: * We should wait for bit 3, tx_go, of the Transmit Status Register to * be cleared at transmit completion if a frame is being transmitted. */ GMAC_TransmissionHalt(pHw); while (GMAC_GetTxStatus(pHw) & GMAC_TSR_TXGO); /* Treat frames in TX queue including the ones that caused the error. */ while (!GCIRC_EMPTY(pGmacd->queueList[qId].wTxHead, pGmacd->queueList[qId].wTxTail)) { int tx_completed = 0; pTxTd = &pGmacd->queueList[qId].pTxD[pGmacd->queueList[qId].wTxTail]; /* Make hw descriptor updates visible to CPU */ GMAC_CACHE_COHERENCE /* Check USED bit on the very first buffer descriptor to validate * TX completion. */ if (pTxTd->status.val & GMAC_TX_USED_BIT) tx_completed = 1; /* Go to the last buffer descriptor of the frame */ while ((pTxTd->status.val & GMAC_TX_LAST_BUFFER_BIT) == 0) { GCIRC_INC(pGmacd->queueList[qId].wTxTail, pGmacd->queueList[qId].wTxListSize); pTxTd = &pGmacd->queueList[qId].pTxD[pGmacd->queueList[qId].wTxTail]; GMAC_CACHE_COHERENCE } /* Notify upper layer that a frame status */ fTxCb = pGmacd->queueList[qId].fTxCbList[pGmacd->queueList[qId].wTxTail]; if (fTxCb) fTxCb(tx_completed ? GMAC_TSR_TXCOMP : 0); // TODO: which error to notify? /* Go to next frame */ GCIRC_INC(pGmacd->queueList[qId].wTxTail, pGmacd->queueList[qId].wTxListSize); } /* Reset TX queue */ GMACD_ResetTx(pGmacd, qId); /* Clear status */ tsr = GMAC_GetTxStatus(pHw); GMAC_ClearTxStatus(pHw, tsr); /* Now we are ready to start transmission again */ GMAC_TransmitEnable(pHw, 1); if (pGmacd->queueList[qId].fWakupCb) pGmacd->queueList[qId].fWakupCb(); }
/** * \brief Receive a packet with GMAC. * If not enough buffer for the packet, the remaining data is lost but right * frame length is returned. * \param pGmacd Pointer to GMAC Driver instance. * \param pFrame Buffer to store the frame * \param frameSize Size of the frame * \param pRcvSize Received size * \return OK, no data, or frame too small */ uint8_t GMACD_Poll(sGmacd * pGmacd, uint8_t *pFrame, uint32_t frameSize, uint32_t *pRcvSize, gmacQueList_t queIdx) { uint16_t bufferLength; uint32_t tmpFrameSize = 0; uint8_t *pTmpFrame = 0; uint32_t tmpIdx = pGmacd->queueList[queIdx].wRxI; volatile sGmacRxDescriptor *pRxTd = &pGmacd->queueList[queIdx].pRxD[pGmacd->queueList[queIdx].wRxI]; uint8_t isFrame = 0; if (pFrame == NULL) return GMACD_PARAM; /* Set the default return value */ *pRcvSize = 0; /* Process received RxTd */ while ((pRxTd->addr.val & GMAC_RX_OWNERSHIP_BIT) == GMAC_RX_OWNERSHIP_BIT) { /* A start of frame has been received, discard previous fragments */ if ((pRxTd->status.val & GMAC_RX_SOF_BIT) == GMAC_RX_SOF_BIT) { /* Skip previous fragment */ while (tmpIdx != pGmacd->queueList[queIdx].wRxI) { pRxTd = &pGmacd->queueList[queIdx].pRxD[pGmacd->queueList[queIdx].wRxI]; pRxTd->addr.val &= ~(GMAC_RX_OWNERSHIP_BIT); GCIRC_INC(pGmacd->queueList[queIdx].wRxI, pGmacd->queueList[queIdx].wRxListSize); } pTmpFrame = pFrame; tmpFrameSize = 0; /* Start to gather buffers in a frame */ isFrame = 1; } /* Increment the pointer */ GCIRC_INC(tmpIdx, pGmacd->queueList[queIdx].wRxListSize); /* Copy data in the frame buffer */ if (isFrame) { if (tmpIdx == pGmacd->queueList[queIdx].wRxI) { TRACE_INFO("no EOF (Invalid of buffers too small)\n\r"); do { pRxTd = &pGmacd->queueList[queIdx].pRxD[pGmacd->queueList[queIdx].wRxI]; pRxTd->addr.val &= ~(GMAC_RX_OWNERSHIP_BIT); GCIRC_INC(pGmacd->queueList[queIdx].wRxI, pGmacd->queueList[queIdx].wRxListSize); } while(tmpIdx != pGmacd->queueList[queIdx].wRxI); return GMACD_RX_NULL; } /* Copy the buffer into the application frame */ bufferLength = GMAC_RX_UNITSIZE; if ((tmpFrameSize + bufferLength) > frameSize) { bufferLength = frameSize - tmpFrameSize; } memcpy(pTmpFrame, (void*)(pRxTd->addr.val & GMAC_ADDRESS_MASK), bufferLength); pTmpFrame += bufferLength; tmpFrameSize += bufferLength; /* An end of frame has been received, return the data */ if ((pRxTd->status.val & GMAC_RX_EOF_BIT) == GMAC_RX_EOF_BIT) { /* Frame size from the GMAC */ *pRcvSize = (pRxTd->status.val & GMAC_LENGTH_FRAME); /* Application frame buffer is too small all data have not been copied */ if (tmpFrameSize < *pRcvSize) { return GMACD_SIZE_TOO_SMALL; } TRACE_DEBUG("packet %d-%d (%d)\n\r", pGmacd->queueList[queIdx].wRxI, tmpIdx, *pRcvSize); /* All data have been copied in the application frame buffer => release TD */ while (pGmacd->queueList[queIdx].wRxI != tmpIdx) { pRxTd = &pGmacd->queueList[queIdx].pRxD[pGmacd->queueList[queIdx].wRxI]; pRxTd->addr.val &= ~(GMAC_RX_OWNERSHIP_BIT); GCIRC_INC(pGmacd->queueList[queIdx].wRxI, pGmacd->queueList[queIdx].wRxListSize); } return GMACD_OK; } } /* SOF has not been detected, skip the fragment */ else { pRxTd->addr.val &= ~(GMAC_RX_OWNERSHIP_BIT); pGmacd->queueList[queIdx].wRxI = tmpIdx; } /* Process the next buffer */ pRxTd = &pGmacd->queueList[queIdx].pRxD[tmpIdx]; memory_barrier(); } return GMACD_RX_NULL; }
/** * \brief GMAC Interrupt handler * \param pGmacd Pointer to GMAC Driver instance. */ void GMACD_Handler(sGmacd *pGmacd ) { Gmac *pHw = pGmacd->pHw; sGmacTxDescriptor *pTxTd; fGmacdTransferCallback *pTxCb = NULL; uint32_t isr; uint32_t rsr; uint32_t tsr; uint32_t rxStatusFlag; uint32_t txStatusFlag; isr = GMAC_GetItStatus(pHw); rsr = GMAC_GetRxStatus(pHw); tsr = GMAC_GetTxStatus(pHw); isr &= ~(GMAC_GetItMask(pHw)| 0xF8030300); /* RX packet */ if ((isr & GMAC_ISR_RCOMP) || (rsr & GMAC_RSR_REC)) { asm("nop"); rxStatusFlag = GMAC_RSR_REC; /* Frame received */ /* Check OVR */ if (rsr & GMAC_RSR_RXOVR) { rxStatusFlag |= GMAC_RSR_RXOVR; } /* Check BNA */ if (rsr & GMAC_RSR_BNA) { rxStatusFlag |= GMAC_RSR_BNA; } /* Check HNO */ if (rsr & GMAC_RSR_HNO) { rxStatusFlag |= GMAC_RSR_HNO; } /* Clear status */ GMAC_ClearRxStatus(pHw, rxStatusFlag); /* Invoke callbacks */ if (pGmacd->fRxCb) { pGmacd->fRxCb(rxStatusFlag); } } /* TX packet */ if ((isr & GMAC_ISR_TCOMP) || (tsr & GMAC_TSR_TXCOMP)) { asm("nop"); txStatusFlag = GMAC_TSR_TXCOMP; /* A frame transmitted Check RLE */ if (tsr & GMAC_TSR_RLE) { /* Status RLE & Number of discarded buffers */ txStatusFlag = GMAC_TSR_RLE | GCIRC_CNT(pGmacd->wTxHead, pGmacd->wTxTail, pGmacd->wTxListSize); pTxCb = &pGmacd->fTxCbList[pGmacd->wTxTail]; GMACD_ResetTx(pGmacd); TRACE_INFO("Tx RLE!!\n\r"); GMAC_TransmitEnable(pHw, 1); } /* Check COL */ if (tsr & GMAC_TSR_COL) { txStatusFlag |= GMAC_TSR_COL; } /* Check TFC */ if (tsr & GMAC_TSR_TFC) { txStatusFlag |= GMAC_TSR_TFC; } /* Check UND */ if (tsr & GMAC_TSR_UND) { txStatusFlag |= GMAC_TSR_UND; } /* Check HRESP */ if (tsr & GMAC_TSR_HRESP) { txStatusFlag |= GMAC_TSR_HRESP; } /* Check LCO */ if (tsr & GMAC_TSR_LCO) { txStatusFlag |= GMAC_TSR_LCO; } /* Clear status */ GMAC_ClearTxStatus(pHw, txStatusFlag); if (!GCIRC_EMPTY(pGmacd->wTxHead, pGmacd->wTxTail)) { /* Check the buffers */ do { pTxTd = &pGmacd->pTxD[pGmacd->wTxTail]; pTxCb = &pGmacd->fTxCbList[pGmacd->wTxTail]; /* Exit if buffer has not been sent yet */ if ((pTxTd->status.val & (uint32_t)GMAC_TX_USED_BIT) == 0) { break; } /* Notify upper layer that a packet has been sent */ if (*pTxCb) { (*pTxCb)(txStatusFlag); } GCIRC_INC( pGmacd->wTxTail, pGmacd->wTxListSize ); } while (GCIRC_CNT(pGmacd->wTxHead, pGmacd->wTxTail, pGmacd->wTxListSize)); } if (tsr & GMAC_TSR_RLE) { /* Notify upper layer RLE */ if (*pTxCb) { (*pTxCb)(txStatusFlag); } } /* If a wakeup has been scheduled, notify upper layer that it can send other packets, send will be successfull. */ if((GCIRC_SPACE(pGmacd->wTxHead, pGmacd->wTxTail, pGmacd->wTxListSize) >= pGmacd->bWakeupThreshold) && pGmacd->fWakupCb) { pGmacd->fWakupCb(); } } /* PAUSE Frame */ if (isr & GMAC_ISR_PFNZ) TRACE_INFO("Pause!\n\r"); if (isr & GMAC_ISR_PTZ) TRACE_INFO("Pause TO!\n\r"); }