Example #1
0
/**
 *  \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();
}
Example #2
0
/**
 * \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;
}
Example #3
0
/**
 *  \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();
}
Example #4
0
/**
 * \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;
}
Example #5
0
/**
 *  \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");
}