/* ---------------------------------------------------------------------------- */
/*                  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 <string.h>

/** \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;
}
