/** * 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); }
/** * Initialize necessary allocated buffer lists for GMAC Driver to transfer data. * Must be invoked after GMACD_Init() but before RX/TX start. * \param pGmacd Pointer to GMAC Driver instance. * \param pRxBuffer Pointer to allocated buffer for RX. The address should * be 8-byte aligned and the size should be * GMAC_RX_UNITSIZE * wRxSize. * \param pRxD Pointer to allocated RX descriptor list. * \param wRxSize RX size, in number of registered units (RX descriptors). * \param pTxBuffer Pointer to allocated buffer for TX. The address should * be 8-byte aligned and the size should be * GMAC_TX_UNITSIZE * wTxSize. * \param pTxD Pointer to allocated TX descriptor list. * \param pTxCb Pointer to allocated TX callback list. * \param wTxSize TX size, in number of registered units (TX descriptors). * \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, uint8_t *pRxBuffer, sGmacRxDescriptor *pRxD, uint16_t wRxSize, uint8_t *pTxBuffer, sGmacTxDescriptor *pTxD, fGmacdTransferCallback *pTxCb, uint16_t wTxSize) { Gmac *pHw = pGmacd->pHw; if (wRxSize <= 1 || wTxSize <= 1 || pTxCb == NULL) return GMACD_PARAM; /* Assign RX buffers */ if ( ((uint32_t)pRxBuffer & 0x7) || ((uint32_t)pRxD & 0x7) ) { wRxSize --; TRACE_DEBUG("RX list address adjusted\n\r"); } pGmacd->pRxBuffer = (uint8_t*)((uint32_t)pRxBuffer & 0xFFFFFFF8); pGmacd->pRxD = (sGmacRxDescriptor*)((uint32_t)pRxD & 0xFFFFFFF8); pGmacd->wRxListSize = wRxSize; /* Assign TX buffers */ if ( ((uint32_t)pTxBuffer & 0x7) || ((uint32_t)pTxD & 0x7) ) { wTxSize --; TRACE_DEBUG("TX list address adjusted\n\r"); } pGmacd->pTxBuffer = (uint8_t*)((uint32_t)pTxBuffer & 0xFFFFFFF8); pGmacd->pTxD = (sGmacTxDescriptor*)((uint32_t)pTxD & 0xFFFFFFF8); pGmacd->wTxListSize = wTxSize; pGmacd->fTxCbList = pTxCb; /* Reset TX & RX */ GMACD_ResetRx(pGmacd); GMACD_ResetTx(pGmacd); /* Enable Rx and Tx, plus the stats register. */ GMAC_TransmitEnable(pHw, 1); GMAC_ReceiveEnable(pHw, 1); GMAC_StatisticsWriteEnable(pHw, 1); /* Setup the interrupts for RX/TX completion (and errors) */ GMAC_EnableIt(pHw, GMAC_INT_RX_BITS | GMAC_INT_TX_BITS | GMAC_IER_HRESP); 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(); }
/** * Initialize necessary allocated buffer lists for GMAC Driver to transfer data. * Must be invoked after GMACD_Init() but before RX/TX start. * \param pGmacd Pointer to GMAC Driver instance. * \param pRxBuffer Pointer to allocated buffer for RX. The address should * be 8-byte aligned and the size should be * GMAC_RX_UNITSIZE * wRxSize. * \param pRxD Pointer to allocated RX descriptor list. * \param wRxSize RX size, in number of registered units (RX descriptors). * \param pTxBuffer Pointer to allocated buffer for TX. The address should * be 8-byte aligned and the size should be * GMAC_TX_UNITSIZE * wTxSize. * \param pTxD Pointer to allocated TX descriptor list. * \param pTxCb Pointer to allocated TX callback list. * \param wTxSize TX size, in number of registered units (TX descriptors). * \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, uint8_t *pRxBuffer, sGmacRxDescriptor *pRxD, uint16_t wRxSize, uint8_t *pTxBuffer, sGmacTxDescriptor *pTxD, fGmacdTransferCallback *pTxCb, uint16_t wTxSize, gmacQueList_t queIdx) { Gmac *pHw = pGmacd->pHw; if (wRxSize <= 1 || wTxSize <= 1 || pTxCb == NULL) return GMACD_PARAM; /* 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 stats 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; }
/** * Initialize necessary allocated buffer lists for GMAC Driver to transfer data. * Must be invoked after GMACD_Init() but before RX/TX start. * \param pGmacd Pointer to GMAC Driver instance. * \param pRxBuffer Pointer to allocated buffer for RX. The address should * be 8-byte aligned and the size should be * GMAC_RX_UNITSIZE * wRxSize. * \param pRxD Pointer to allocated RX descriptor list. * \param wRxSize RX size, in number of registered units (RX descriptors). * \param pTxBuffer Pointer to allocated buffer for TX. The address should * be 8-byte aligned and the size should be * GMAC_TX_UNITSIZE * wTxSize. * \param pTxD Pointer to allocated TX descriptor list. * \param pTxCb Pointer to allocated TX callback list. * \param wTxSize TX size, in number of registered units (TX descriptors). * \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, uint8_t *pRxBuffer, sGmacRxDescriptor *pRxD, uint16_t wRxSize, uint8_t *pTxBuffer, sGmacTxDescriptor *pTxD, fGmacdTransferCallback *pTxCb, uint16_t wTxSize) { Gmac *pHw = pGmacd->pHw; if (wRxSize <= 1 || wTxSize <= 1 || pTxCb == NULL) return GMACD_PARAM; /* Assign RX buffers */ if ( ((uint32_t)pRxBuffer & 0x7) || ((uint32_t)pRxD & 0x7) ) { wRxSize --; TRACE_DEBUG("RX list address adjusted\n\r"); } pGmacd->pRxBuffer = (uint8_t*)((uint32_t)pRxBuffer & 0xFFFFFFF8); pGmacd->pRxD = (sGmacRxDescriptor*)((uint32_t)pRxD & 0xFFFFFFF8); pGmacd->wRxListSize = wRxSize; /* Assign TX buffers */ if ( ((uint32_t)pTxBuffer & 0x7) || ((uint32_t)pTxD & 0x7) ) { wTxSize --; TRACE_DEBUG("TX list address adjusted\n\r"); } pGmacd->pTxBuffer = (uint8_t*)((uint32_t)pTxBuffer & 0xFFFFFFF8); pGmacd->pTxD = (sGmacTxDescriptor*)((uint32_t)pTxD & 0xFFFFFFF8); pGmacd->wTxListSize = wTxSize; pGmacd->fTxCbList = pTxCb; /* Reset TX & RX */ GMACD_ResetRx(pGmacd); GMACD_ResetTx(pGmacd); /* Enable Rx and Tx, plus the stats register. */ GMAC_TransmitEnable(pHw, 1); GMAC_ReceiveEnable(pHw, 1); GMAC_StatisticsWriteEnable(pHw, 1); /* Setup the interrupts for TX (and errors) */ GMAC_EnableIt(pHw, GMAC_IER_MFS |GMAC_IER_RCOMP |GMAC_IER_RXUBR |GMAC_IER_TXUBR |GMAC_IER_TUR |GMAC_IER_RLEX |GMAC_IER_TFC |GMAC_IER_TCOMP |GMAC_IER_ROVR |GMAC_IER_HRESP |GMAC_IER_PFNZ |GMAC_IER_PTZ |GMAC_IER_PFTR |GMAC_IER_EXINT |GMAC_IER_DRQFR |GMAC_IER_SFR |GMAC_IER_DRQFT |GMAC_IER_SFT |GMAC_IER_PDRQFR |GMAC_IER_PDRSFR |GMAC_IER_PDRQFT |GMAC_IER_PDRSFT); //0x03FCFCFF return GMACD_OK; }
/** * \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"); }