/* ---------------------------------------------------------------------------- */ /* Atmel Microcontroller Software Support */ /* SAM Software Package License */ /* ---------------------------------------------------------------------------- */ /* Copyright (c) 2015, Atmel Corporation */ /* */ /* All rights reserved. */ /* */ /* Redistribution and use in source and binary forms, with or without */ /* modification, are permitted provided that the following condition is met: */ /* */ /* - Redistributions of source code must retain the above copyright notice, */ /* this list of conditions and the disclaimer below. */ /* */ /* Atmel's name may not be used to endorse or promote products derived from */ /* this software without specific prior written permission. */ /* */ /* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR */ /* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE */ /* DISCLAIMED. IN NO EVENT SHALL ATMEL 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 */ /*--------------------------------------------------------------------------- * Headers *---------------------------------------------------------------------------*/ #include "chip.h" #include /** \addtogroup gmacd_defines @{*/ /** ISO/IEC 14882:2003(E) - 5.6 Multiplicative operators: * The binary / operator yields the quotient, and the binary % operator yields * the remainder from the division of the first expression by the second. * If the second operand of / or % is zero the behaviour is undefined; otherwise * (a/b)*b + a%b is equal to a. * If both operands are non-negative then the remainder is non-negative; * if not, the sign of the remainder is implementation-defined 74). */ __STATIC_INLINE int fixed_mod(int a, int b) { int rem = a % b; while (rem < 0) rem += b; return rem; } /** Return count in buffer */ #define GCIRC_CNT(head,tail,size) fixed_mod((head) - (tail), (size)) /** Return space available, 0..size-1. always leave one free char as a completely full buffer has head == tail, which is the same as empty */ #define GCIRC_SPACE(head,tail,size) GCIRC_CNT((tail),((head)+1),(size)) /** Return count up to the end of the buffer. Carefully avoid accessing head and tail more than once, so they can change underneath us without returning inconsistent results */ #define GCIRC_CNT_TO_END(head,tail,size) \ ({int end = (size) - (tail); \ int n = fixed_mod((head) + end, (size)); \ n < end ? n : end;}) /** Return space available up to the end of the buffer */ #define GCIRC_SPACE_TO_END(head,tail,size) \ ({int end = (size) - 1 - (head); \ int n = fixed_mod(end + (tail), (size)); \ n <= end ? n : end+1;}) /** Increment head or tail */ #define GCIRC_INC(headortail,size) \ headortail++; \ if (headortail >= size) { \ headortail = 0; \ } /** Circular buffer is empty ? */ #define GCIRC_EMPTY(head, tail) (head == tail) /** Clear circular buffer */ #define GCIRC_CLEAR(head, tail) (head = tail = 0) /* This variable holds the write index into gPtpMsgTxQue */ uint8_t ptpTxQueWriteIdx = 0u; uint8_t ptpTxQueReadIdx = 0u; /* This queue holds the transmit event messages */ ptpMsgType gPtpMsgTxQue[EFRS_BUFFER_LEN]; uint16_t gPtpMsgTxSeqId[EFRS_BUFFER_LEN]; const uint32_t isrMasks[] = { GMAC_IMR_SFT, GMAC_IMR_DRQFT, GMAC_IMR_PDRQFT , GMAC_IMR_PDRSFT }; /*--------------------------------------------------------------------------- * Local functions *---------------------------------------------------------------------------*/ /** * \brief Disable TX & reset registers and descriptor list * \param pDrv Pointer to GMAC Driver instance. */ static void GMACD_ResetTx(sGmacd *pDrv, gmacQueList_t queIdx) { Gmac *pHw = pDrv->pHw; uint8_t *pTxBuffer = pDrv->queueList[queIdx].pTxBuffer; sGmacTxDescriptor *pTd = pDrv->queueList[queIdx].pTxD; uint32_t Index; uint32_t Address; /* Disable TX */ GMAC_TransmitEnable(pHw, 0); /* Setup the TX descriptors. */ GCIRC_CLEAR(pDrv->queueList[queIdx].wTxHead, pDrv->queueList[queIdx].wTxTail); for (Index = 0; Index < pDrv->queueList[queIdx].wTxListSize; Index++) { Address = (uint32_t)(&(pTxBuffer[Index * pDrv->queueList[queIdx].wTxBufferSize])); pTd[Index].addr = Address; pTd[Index].status.val = (uint32_t)GMAC_TX_USED_BIT; } pTd[pDrv->queueList[queIdx].wTxListSize - 1].status.val = GMAC_TX_USED_BIT | GMAC_TX_WRAP_BIT; /* Transmit Buffer Queue Pointer Register */ GMAC_SetTxQueue(pHw, (uint32_t)pTd, queIdx); } /** * \brief Disable RX & reset registers and descriptor list * \param pDrv Pointer to GMAC Driver instance. */ static void GMACD_ResetRx(sGmacd *pDrv, gmacQueList_t queIdx) { Gmac *pHw = pDrv->pHw; uint8_t *pRxBuffer = pDrv->queueList[queIdx].pRxBuffer; sGmacRxDescriptor *pRd = pDrv->queueList[queIdx].pRxD; uint32_t Index; uint32_t Address; /* Disable RX */ GMAC_ReceiveEnable(pHw, 0); /* Setup the RX descriptors. */ pDrv->queueList[queIdx].wRxI = 0; for (Index = 0; Index < pDrv->queueList[queIdx].wRxListSize; Index++) { Address = (uint32_t)(&(pRxBuffer[Index * pDrv->queueList[queIdx].wRxBufferSize])); /* Remove GMAC_RXD_bmOWNERSHIP and GMAC_RXD_bmWRAP */ pRd[Index].addr.val = Address & GMAC_ADDRESS_MASK; pRd[Index].status.val = 0; } pRd[pDrv->queueList[queIdx].wRxListSize - 1].addr.val |= GMAC_RX_WRAP_BIT; /* Receive Buffer Queue Pointer Register */ GMAC_SetRxQueue(pHw, (uint32_t)pRd, queIdx); } /** * \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]; /* 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 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]; /* 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]; } /* 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(); } /*--------------------------------------------------------------------------- * Exported functions *---------------------------------------------------------------------------*/ #ifndef PTP_1588_TX_DISABLE void GMACD_TxPtpEvtMsgCBRegister (sGmacd *pGmacd, fGmacdTxPtpEvtCallBack pTxPtpEvtCb, gmacQueList_t queIdx) { pGmacd->queueList[queIdx].fTxPtpEvtCb = pTxPtpEvtCb; } #endif /* #ifdef PTP_1588_TX_DISABLE */ /** * \brief GMAC Interrupt handler * \param pGmacd Pointer to GMAC Driver instance. */ void GMACD_Handler(sGmacd *pGmacd, gmacQueList_t queIdx) { Gmac *pHw = pGmacd->pHw; uint32_t isr; uint32_t rsr; /* Interrupt Status Register is cleared on read */ while ((isr = GMAC_GetItStatus(pHw, queIdx)) != 0) { /* Sync Frame Received - PTP */ if (0u != (isr & GMAC_ISR_SFR)) { rsr = GMAC_ISR_SFR; memory_barrier(); /* Invoke callbacks */ if (pGmacd->queueList[queIdx].fRxCb) pGmacd->queueList[queIdx].fRxCb(rsr); else { } } else { } /* Peer Delay Request Frame Received - PTP */ if (0u != (isr & GMAC_ISR_PDRQFR)) { rsr = GMAC_ISR_PDRQFR; memory_barrier(); /* Invoke callbacks */ if (pGmacd->queueList[queIdx].fRxCb) pGmacd->queueList[queIdx].fRxCb(rsr); else { } } else { } /* Peer Delay Response Frame Received - PTP */ if (0u != (isr & GMAC_ISR_PDRSFR)) { rsr = GMAC_ISR_PDRSFR; memory_barrier(); /* Invoke callbacks */ if (pGmacd->queueList[queIdx].fRxCb) pGmacd->queueList[queIdx].fRxCb(rsr); else { } } else { } if (0u != (isr & GMAC_ISR_TSU)) { /* Invoke call back with flag set to TSU comparison interrupt */ rsr = GMAC_ISR_TSU; memory_barrier(); /* Invoke callbacks */ if (pGmacd->queueList[queIdx].fRxCb) pGmacd->queueList[queIdx].fRxCb(rsr); else { } } else { } /* RX packet */ if (isr & GMAC_INT_RX_STATUS_BITS) { /* Clear status */ rsr = GMAC_GetRxStatus(pHw); GMAC_ClearRxStatus(pHw, rsr); /* Invoke callback */ if (pGmacd->queueList[queIdx].fRxCb) pGmacd->queueList[queIdx].fRxCb(rsr); } /* TX error */ if (isr & GMAC_INT_TX_STATUS_ERR_BITS) { GMACD_TxErrorHandler(pGmacd, queIdx); break; } #ifndef PTP_1588_TX_DISABLE /* Transmit of SYNC / PDELAY_REQ / PDELAY_RSP */ if (0u != (isr & isrMasks[gPtpMsgTxQue[ptpTxQueReadIdx]])) { /* Invoke callback */ /* Check if it is possible for multiple messages to be triggered within a single isr. If so, a loop may be needed to validate the top of the queue with the actual interrupt that has been triggered */ /* while (0u != (isr & (GMAC_IMR_SFT | GMAC_IMR_PDRQFT | GMAC_IMR_PDRSFT))) { */ if (pGmacd->queueList[queIdx].fTxPtpEvtCb) { switch (gPtpMsgTxQue[ptpTxQueReadIdx]) { case SYNC_MSG_TYPE: pGmacd->queueList[queIdx].fTxPtpEvtCb (gPtpMsgTxQue[ptpTxQueReadIdx], GMAC_GetTxEvtFrameSec(pHw), GMAC_GetTxEvtFrameNsec(pHw), gPtpMsgTxSeqId[ptpTxQueReadIdx]); isr &= GMAC_IMR_SFT; break; case PDELAY_REQ_TYPE: pGmacd->queueList[queIdx].fTxPtpEvtCb (gPtpMsgTxQue[ptpTxQueReadIdx], GMAC_GetTxPeerEvtFrameSec(pHw), GMAC_GetTxPeerEvtFrameNsec(pHw), gPtpMsgTxSeqId[ptpTxQueReadIdx]); isr &= GMAC_IMR_PDRQFT; break; case PDELAY_RESP_TYPE: pGmacd->queueList[queIdx].fTxPtpEvtCb (gPtpMsgTxQue[ptpTxQueReadIdx], GMAC_GetTxPeerEvtFrameSec(pHw), GMAC_GetTxPeerEvtFrameNsec(pHw), gPtpMsgTxSeqId[ptpTxQueReadIdx]); isr &= GMAC_IMR_PDRSFT; break; default: /* Only for Peer messages & sync messages */ break; }; } else { } ptpTxQueReadIdx++; ptpTxQueReadIdx &= (EFRS_BUFFER_LEN - 1); } else { /* if (0u != (isr & isrMasks[gPtpMsgTxQue[ptpTxQueReadIdx]])) */ } #endif /* #ifndef PTP_1588_TX_DISABLE */ /* TX packet */ if (isr & GMAC_IER_TCOMP) GMACD_TxCompleteHandler(pGmacd, queIdx); if (isr & GMAC_IER_HRESP) TRACE_ERROR("HRESP\n\r"); } } /** * \brief Initialize the GMAC with the Gmac controller address * \param pGmacd Pointer to GMAC Driver instance. * \param pHw Pointer to HW address for registers. * \param bID HW ID for power management * \param enableCAF Enable/Disable CopyAllFrame. * \param enableNBC Enable/Disable NoBroadCast. */ void GMACD_Init(sGmacd *pGmacd, Gmac *pHw, uint8_t bID, uint8_t enableCAF, uint8_t enableNBC) { uint32_t dwNcfgr; /* Check parameters */ // assert(GRX_BUFFERS * GMAC_RX_UNITSIZE > GMAC_FRAME_LENTGH_MAX); TRACE_DEBUG("GMAC_Init\n\r"); /* Initialize struct */ pGmacd->pHw = pHw; pGmacd->bId = bID; /* Power ON */ PMC_EnablePeripheral(bID); /* Disable TX & RX and more */ GMAC_NetworkControl(pHw, 0); GMAC_DisableAllQueueIt(pHw, ~0u); GMAC_ClearStatistics(pHw); /* Clear all status bits in the receive status register. */ GMAC_ClearRxStatus(pHw, GMAC_RSR_RXOVR | GMAC_RSR_REC | GMAC_RSR_BNA | GMAC_RSR_HNO); /* Clear all status bits in the transmit status register */ GMAC_ClearTxStatus(pHw, GMAC_TSR_UBR | GMAC_TSR_COL | GMAC_TSR_RLE | GMAC_TSR_TXGO | GMAC_TSR_TFC | GMAC_TSR_TXCOMP | GMAC_TSR_HRESP); /* Clear All interrupts */ GMAC_GetItStatus(pHw, GMAC_QUE_0); GMAC_GetItStatus(pHw, GMAC_QUE_1); GMAC_GetItStatus(pHw, GMAC_QUE_2); /* Enable the copy of data into the buffers ignore broadcasts, and don't copy FCS. */ dwNcfgr = GMAC_NCFGR_FD | GMAC_NCFGR_DBW(0) | GMAC_NCFGR_CLK_MCK_64 | GMAC_NCFGR_MAXFS | GMAC_NCFGR_PEN | GMAC_NCFGR_RFCS; if (enableCAF) dwNcfgr |= GMAC_NCFGR_CAF; if (enableNBC) dwNcfgr |= GMAC_NCFGR_NBC; GMAC_Configure(pHw, dwNcfgr); } /** * Initialize necessary allocated buffer lists for GMAC Driver to transfer data. * Must be invoked after GMACD_Init() but before RX/TX start. * Replace the deprecated GMACD_InitTransfer(). * \param pGmacd Pointer to GMAC Driver instance. * \param pInit Pointer to sGmacInit. * \param pInit Pointer to gmacQueList_t for different queue. * \return GMACD_OK or GMACD_PARAM. * \note If input address is not 8-byte aligned the address is automatically * adjusted and the list size is reduced by one. */ uint8_t GMACD_InitTransfer(sGmacd *pGmacd, const sGmacInit *pInit, gmacQueList_t queIdx) { Gmac *pHw = pGmacd->pHw; uint8_t *pRxBuffer = pInit->pRxBuffer; sGmacRxDescriptor *pRxD = pInit->pRxD; uint16_t wRxBufferSize = pInit->wRxBufferSize; uint16_t wRxSize = pInit->wRxSize; uint8_t *pTxBuffer = pInit->pTxBuffer; sGmacTxDescriptor *pTxD = pInit->pTxD; uint16_t wTxBufferSize = pInit->wTxBufferSize; uint16_t wTxSize = pInit->wTxSize; fGmacdTransferCallback *pTxCb = pInit->pTxCb; uint32_t dwDmaCfg; if (wRxSize <= 1 || wTxSize <= 1 || pTxCb == NULL) return GMACD_PARAM; if (!wRxBufferSize || wRxBufferSize > 16 * 1024 || wRxBufferSize & 0x3f) return GMACD_PARAM; if (!wTxBufferSize) return GMACD_PARAM; if (pInit->bIsGem) { if (!queIdx) { dwDmaCfg = (GMAC_DCFGR_DRBS(wRxBufferSize >> 6)) | GMAC_DCFGR_RXBMS(3) | GMAC_DCFGR_TXPBMS; switch (pInit->bDmaBurstLength) { case 16: dwDmaCfg |= GMAC_DCFGR_FBLDO_INCR16; break; case 8: dwDmaCfg |= GMAC_DCFGR_FBLDO_INCR8; break; case 4: dwDmaCfg |= GMAC_DCFGR_FBLDO_INCR4; break; case 1: dwDmaCfg |= GMAC_DCFGR_FBLDO_SINGLE; break; default: return GMACD_PARAM; } } else dwDmaCfg = (GMAC_RBSRPQ_RBS(wRxBufferSize >> 6)); GMAC_SetDMAConfig(pHw, dwDmaCfg, queIdx); } pGmacd->queueList[queIdx].wRxBufferSize = wRxBufferSize; pGmacd->queueList[queIdx].wTxBufferSize = wTxBufferSize; /* Assign RX buffers */ if (((uint32_t)pRxBuffer & 0x7) || ((uint32_t)pRxD & 0x7)) { wRxSize --; TRACE_DEBUG("RX list address adjusted\n\r"); } pGmacd->queueList[queIdx].pRxBuffer = (uint8_t *)((uint32_t)pRxBuffer & 0xFFFFFFF8); pGmacd->queueList[queIdx].pRxD = (sGmacRxDescriptor *)(( uint32_t)pRxD & 0xFFFFFFF8); pGmacd->queueList[queIdx].wRxListSize = wRxSize; /* Assign TX buffers */ if (((uint32_t)pTxBuffer & 0x7) || ((uint32_t)pTxD & 0x7)) { wTxSize --; TRACE_DEBUG("TX list address adjusted\n\r"); } pGmacd->queueList[queIdx].pTxBuffer = (uint8_t *)((uint32_t)pTxBuffer & 0xFFFFFFF8); pGmacd->queueList[queIdx].pTxD = (sGmacTxDescriptor *)(( uint32_t)pTxD & 0xFFFFFFF8); pGmacd->queueList[queIdx].wTxListSize = wTxSize; pGmacd->queueList[queIdx].fTxCbList = pTxCb; /* Reset TX & RX */ GMACD_ResetRx(pGmacd, queIdx); GMACD_ResetTx(pGmacd, queIdx); /* Setup the interrupts for RX/TX completion (and errors) */ switch (queIdx) { case GMAC_QUE_0: /* YBP: Que 0 should be configured last so as to enable transmit and Receive in the NCR register */ /* Enable Rx and Tx, plus the status register. */ GMAC_TransmitEnable(pHw, 1); GMAC_ReceiveEnable(pHw, 1); GMAC_StatisticsWriteEnable(pHw, 1); GMAC_EnableIt(pHw, GMAC_INT_RX_BITS | GMAC_INT_TX_BITS | GMAC_INT_TX_ERR_BITS, GMAC_QUE_0); break; case GMAC_QUE_1: GMAC_EnableIt(pHw, GMAC_INT_RX_BITS | GMAC_INT_TX_BITS | GMAC_INT_TX_ERR_BITS, GMAC_QUE_1); break; case GMAC_QUE_2: GMAC_EnableIt(pHw, GMAC_INT_RX_BITS | GMAC_INT_TX_BITS | GMAC_INT_TX_ERR_BITS, GMAC_QUE_2); break; }; return GMACD_OK; } /** * Reset TX & RX queue & statistics * \param pGmacd Pointer to GMAC Driver instance. */ void GMACD_Reset(sGmacd *pGmacd) { Gmac *pHw = pGmacd->pHw; GMACD_ResetRx(pGmacd, GMAC_QUE_0); GMACD_ResetRx(pGmacd, GMAC_QUE_1); GMACD_ResetRx(pGmacd, GMAC_QUE_2); GMACD_ResetTx(pGmacd, GMAC_QUE_0); GMACD_ResetTx(pGmacd, GMAC_QUE_1); GMACD_ResetTx(pGmacd, GMAC_QUE_2); //memset((void*)&GmacStatistics, 0x00, sizeof(GmacStats)); GMAC_NetworkControl(pHw, GMAC_NCR_TXEN | GMAC_NCR_RXEN | GMAC_NCR_WESTAT | GMAC_NCR_CLRSTAT); } /** * \brief Send a frame split into buffers. If the frame size is larger than * transfer buffer size error returned. If frame transfer status is monitored, * specify callback for each frame. * \param pGmacd Pointer to GMAC Driver instance. * \param sgl Pointer to a scatter-gather list describing the buffers of the * ethernet frame. * \param fTxCb Pointer to callback function. */ uint8_t GMACD_SendSG(sGmacd *pGmacd, const sGmacSGList *sgl, fGmacdTransferCallback fTxCb, gmacQueList_t queIdx) { Gmac *pHw = pGmacd->pHw; sGmacTxDescriptor *pTd = pGmacd->queueList[queIdx].pTxD; sGmacTxDescriptor *pTxTd; uint16_t wTxPos, wTxHead; int i; TRACE_DEBUG("%s\n\r", __FUNCTION__); /* Check parameter */ if (!sgl->len) { TRACE_ERROR("%s:: ethernet frame is empty.\r\n", __FUNCTION__); return GMACD_PARAM; } if (sgl->len >= pGmacd->queueList[queIdx].wTxListSize) { TRACE_ERROR("%s: ethernet frame has too many buffers.\r\n", __FUNCTION__); return GMACD_PARAM; } /* Check available space */ if (GCIRC_SPACE(pGmacd->queueList[queIdx].wTxHead, pGmacd->queueList[queIdx].wTxTail, pGmacd->queueList[queIdx].wTxListSize) < (int)sgl->len) return GMACD_TX_BUSY; /* Tag end of TX queue */ wTxHead = fixed_mod(pGmacd->queueList[queIdx].wTxHead + sgl->len, pGmacd->queueList[queIdx].wTxListSize); wTxPos = wTxHead; pGmacd->queueList[queIdx].fTxCbList[wTxPos] = NULL; pTxTd = &pTd[wTxPos]; pTxTd->status.val = GMAC_TX_USED_BIT; /* Update buffer descriptors in reverse order to avoid a race * condition with hardware. */ for (i = (int)(sgl->len - 1); i >= 0; --i) { const sGmacSG *sg = &sgl->sg[i]; uint32_t status; if (sg->size > pGmacd->queueList[queIdx].wTxBufferSize) { TRACE_ERROR("%s: buffer size is too big.\r\n", __FUNCTION__); return GMACD_PARAM; } if (wTxPos == 0) wTxPos = pGmacd->queueList[queIdx].wTxListSize - 1; else wTxPos--; /* Reset TX callback */ pGmacd->queueList[queIdx].fTxCbList[wTxPos] = NULL; pTxTd = &pTd[wTxPos]; #ifdef GMAC_ZERO_COPY /** Update buffer descriptor address word: * MUST be done before status word to avoid a race condition. */ pTxTd->addr = (uint32_t)sg->pBuffer; #else /* Copy data into transmission buffer */ if (sg->pBuffer && sg->size) { memcpy((void *)pTxTd->addr, sg->pBuffer, sg->size); SCB_CleanDCache_by_Addr((void *)pTxTd->addr, sg->size); } #endif /* Compute buffer descriptor status word */ status = sg->size & GMAC_LENGTH_FRAME; if (i == (int)(sgl->len - 1)) { status |= GMAC_TX_LAST_BUFFER_BIT; pGmacd->queueList[queIdx].fTxCbList[wTxPos] = fTxCb; } if (wTxPos == pGmacd->queueList[queIdx].wTxListSize - 1) status |= GMAC_TX_WRAP_BIT; /* Update buffer descriptor status word: clear USED bit */ pTxTd->status.val = status; memory_sync(); } /* Update TX ring buffer pointers */ pGmacd->queueList[queIdx].wTxHead = wTxHead; /* Now start to transmit if it is not already done */ GMAC_TransmissionStart(pHw); return GMACD_OK; } /** * \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 pBuffer The buffer to be send * \param size The size of buffer to be send * \param fTxCb Threshold Wakeup callback * \return OK, Busy or invalid packet */ uint8_t GMACD_Send(sGmacd *pGmacd, void *pBuffer, uint32_t size, fGmacdTransferCallback fTxCb, gmacQueList_t queIdx) { sGmacSGList sgl; sGmacSG sg; uint8_t *msgPtr; ptpMsgType ptpMsg; /* Init single entry scatter-gather list */ sg.size = size; sg.pBuffer = pBuffer; sgl.len = 1; sgl.sg = &sg; #ifndef PTP_1588_TX_DISABLE msgPtr = (uint8_t *)pBuffer; if (0x88u == msgPtr[12] && 0xf7u == msgPtr[13]) { /* Extract Tx PTP message type */ ptpMsg = (ptpMsgType)(msgPtr[14] & 0x0F); if (ptpMsg == SYNC_MSG_TYPE || ptpMsg == PDELAY_REQ_TYPE || ptpMsg == PDELAY_RESP_TYPE) { /* Only add message to Tx queue of msg types that have tx event ISRs enabled. */ gPtpMsgTxQue[ptpTxQueWriteIdx] = ptpMsg; /* Copy the Sequence Id */ gPtpMsgTxSeqId[ptpTxQueWriteIdx] = (uint16_t)(((uint16_t)msgPtr[44] << 8) | msgPtr[45]); ptpTxQueWriteIdx++; ptpTxQueWriteIdx &= (EFRS_BUFFER_LEN - 1u); } else { /* if (ptpMsg == SYNC_MSG_TYPE || ptpMsg == PDELAY_REQ_TYPE\ || ptpMsg == PDELAY_RESP_TYPE) { */ } } else { /* if (0x88 == msgPtr[12] && 0xf7 == msgPtr[13]) { */ } #endif /* #ifndef PTP_1588_TX_DISABLE */ return GMACD_SendSG(pGmacd, &sgl, fTxCb, queIdx); } /** * Return current load of TX. * \param pGmacd Pointer to GMAC Driver instance. */ uint32_t GMACD_TxLoad(sGmacd *pGmacd, gmacQueList_t queIdx) { uint16_t head = pGmacd->queueList[queIdx].wTxHead; uint16_t tail = pGmacd->queueList[queIdx].wTxTail; return GCIRC_CNT(head, tail, pGmacd->queueList[queIdx].wTxListSize); } /** * \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 = pGmacd->queueList[queIdx].wRxBufferSize; if ((tmpFrameSize + bufferLength) > frameSize) bufferLength = frameSize - tmpFrameSize; SCB_InvalidateDCache_by_Addr((void *)(pRxTd->addr.val & GMAC_ADDRESS_MASK) , bufferLength); 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; } } else { /* SOF has not been detected, skip the fragment */ pRxTd->addr.val &= ~(GMAC_RX_OWNERSHIP_BIT); pGmacd->queueList[queIdx].wRxI = tmpIdx; } /* Process the next buffer */ pRxTd = &pGmacd->queueList[queIdx].pRxD[tmpIdx]; } return GMACD_RX_NULL; } /** * \brief Registers pRxCb callback. Callback will be invoked after the next * received frame. When GMAC_Poll() returns GMAC_RX_NO_DATA the application task * call GMAC_Set_RxCb() to register pRxCb() callback and enters suspend state. * The callback is in charge to resume the task once a new frame has been * received. The next time GMAC_Poll() is called, it will be successful. * \param pGmacd Pointer to GMAC Driver instance. * \param fRxCb Pointer to callback function * \return OK, no data, or frame too small */ void GMACD_SetRxCallback(sGmacd *pGmacd, fGmacdTransferCallback fRxCb, gmacQueList_t queIdx) { Gmac *pHw = pGmacd->pHw; if (fRxCb == NULL) { GMAC_DisableIt(pHw, GMAC_IDR_RCOMP, queIdx); pGmacd->queueList[queIdx].fRxCb = NULL; } else { pGmacd->queueList[queIdx].fRxCb = fRxCb; GMAC_EnableIt(pHw, GMAC_IER_RCOMP, queIdx); } } /** * Register/Clear TX wakeup callback. * * When GMACD_Send() returns GMACD_TX_BUSY (all TD busy) the application * task calls GMACD_SetTxWakeupCallback() to register fWakeup() callback and * enters suspend state. The callback is in charge to resume the task once * several TD have been released. The next time GMACD_Send() will be called, * it shall be successful. * * This function is usually invoked with NULL callback from the TX wakeup * callback itself, to unregister. Once the callback has resumed the * application task, there is no need to invoke the callback again. * * \param pGmacd Pointer to GMAC Driver instance. * \param fWakeup Wakeup callback. * \param bThreshold Number of free TD before wakeup callback invoked. * \return GMACD_OK, GMACD_PARAM on parameter error. */ uint8_t GMACD_SetTxWakeupCallback(sGmacd *pGmacd, fGmacdWakeupCallback fWakeup, uint8_t bThreshold, gmacQueList_t queIdx) { if (fWakeup == NULL) pGmacd->queueList[queIdx].fWakupCb = NULL; else { if (bThreshold <= pGmacd->queueList[queIdx].wTxListSize) { pGmacd->queueList[queIdx].fWakupCb = fWakeup; pGmacd->queueList[queIdx].bWakeupThreshold = bThreshold; } else return GMACD_PARAM; } return GMACD_OK; }