1 /* ----------------------------------------------------------------------------
\r
2 * SAM Software Package License
\r
3 * ----------------------------------------------------------------------------
\r
4 * Copyright (c) 2012, Atmel Corporation
\r
6 * All rights reserved.
\r
8 * Redistribution and use in source and binary forms, with or without
\r
9 * modification, are permitted provided that the following conditions are met:
\r
11 * - Redistributions of source code must retain the above copyright notice,
\r
12 * this list of conditions and the disclaimer below.
\r
14 * Atmel's name may not be used to endorse or promote products derived from
\r
15 * this software without specific prior written permission.
\r
17 * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
\r
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
\r
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
\r
20 * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
\r
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
\r
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
\r
23 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
\r
24 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
\r
25 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
\r
26 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\r
27 * ----------------------------------------------------------------------------
\r
31 /*---------------------------------------------------------------------------
\r
33 *---------------------------------------------------------------------------*/
\r
38 /** \addtogroup gmacd_defines
\r
41 /*----------------------------------------------------------------------------
\r
43 *----------------------------------------------------------------------------*/
\r
46 /** ISO/IEC 14882:2003(E) - 5.6 Multiplicative operators:
\r
47 * The binary / operator yields the quotient, and the binary % operator yields the remainder
\r
48 * from the division of the first expression by the second.
\r
49 * If the second operand of / or % is zero the behavior is undefined; otherwise (a/b)*b + a%b is equal to a.
\r
50 * If both operands are nonnegative then the remainder is nonnegative;
\r
51 * if not, the sign of the remainder is implementation-defined 74).
\r
53 static inline int fixed_mod(int a, int b)
\r
62 /** Return count in buffer */
\r
63 #define GCIRC_CNT(head,tail,size) fixed_mod((head) - (tail), (size))
\r
65 /** Return space available, 0..size-1. always leave one free char as a completely full buffer
\r
66 has head == tail, which is the same as empty */
\r
67 #define GCIRC_SPACE(head,tail,size) GCIRC_CNT((tail),((head)+1),(size))
\r
69 /** Return count up to the end of the buffer. Carefully avoid accessing head and tail more than once,
\r
70 so they can change underneath us without returning inconsistent results */
\r
71 #define GCIRC_CNT_TO_END(head,tail,size) \
\r
72 ({int end = (size) - (tail); \
\r
73 int n = fixed_mod((head) + end, (size)); \
\r
74 n < end ? n : end;})
\r
76 /** Return space available up to the end of the buffer */
\r
77 #define GCIRC_SPACE_TO_END(head,tail,size) \
\r
78 ({int end = (size) - 1 - (head); \
\r
79 int n = fixed_mod(end + (tail), (size)); \
\r
80 n <= end ? n : end+1;})
\r
82 /** Increment head or tail */
\r
83 #define GCIRC_INC(headortail,size) \
\r
85 if(headortail >= size) { \
\r
89 /** Circular buffer is empty ? */
\r
90 #define GCIRC_EMPTY(head, tail) (head == tail)
\r
92 /** Clear circular buffer */
\r
93 #define GCIRC_CLEAR(head, tail) (head = tail = 0)
\r
97 /*---------------------------------------------------------------------------
\r
99 *---------------------------------------------------------------------------*/
\r
102 * \brief Disable TX & reset registers and descriptor list
\r
103 * \param pDrv Pointer to GMAC Driver instance.
\r
105 static void GMACD_ResetTx(sGmacd *pDrv, gmacQueList_t queIdx)
\r
107 Gmac *pHw = pDrv->pHw;
\r
108 uint8_t *pTxBuffer = pDrv->queueList[queIdx].pTxBuffer;
\r
109 sGmacTxDescriptor *pTd = pDrv->queueList[queIdx].pTxD;
\r
114 GMAC_TransmitEnable(pHw, 0);
\r
116 /* Setup the TX descriptors. */
\r
117 GCIRC_CLEAR(pDrv->queueList[queIdx].wTxHead, pDrv->queueList[queIdx].wTxTail);
\r
118 for(Index = 0; Index < pDrv->queueList[queIdx].wTxListSize; Index++) {
\r
119 Address = (uint32_t)(&(pTxBuffer[Index * GMAC_TX_UNITSIZE]));
\r
120 pTd[Index].addr = Address;
\r
121 pTd[Index].status.val = (uint32_t)GMAC_TX_USED_BIT;
\r
124 pTd[pDrv->queueList[queIdx].wTxListSize - 1].status.val = GMAC_TX_USED_BIT | GMAC_TX_WRAP_BIT;
\r
126 /* Transmit Buffer Queue Pointer Register */
\r
128 GMAC_SetTxQueue(pHw, (uint32_t)pTd, queIdx);
\r
132 * \brief Disable RX & reset registers and descriptor list
\r
133 * \param pDrv Pointer to GMAC Driver instance.
\r
135 static void GMACD_ResetRx(sGmacd *pDrv, gmacQueList_t queIdx )
\r
137 Gmac *pHw = pDrv->pHw;
\r
138 uint8_t *pRxBuffer = pDrv->queueList[queIdx].pRxBuffer;
\r
139 sGmacRxDescriptor *pRd = pDrv->queueList[queIdx].pRxD;
\r
145 GMAC_ReceiveEnable(pHw, 0);
\r
147 /* Setup the RX descriptors. */
\r
148 pDrv->queueList[queIdx].wRxI = 0;
\r
149 for(Index = 0; Index < pDrv->queueList[queIdx].wRxListSize; Index++)
\r
151 Address = (uint32_t)(&(pRxBuffer[Index * GMAC_RX_UNITSIZE]));
\r
152 /* Remove GMAC_RXD_bmOWNERSHIP and GMAC_RXD_bmWRAP */
\r
153 pRd[Index].addr.val = Address & GMAC_ADDRESS_MASK;
\r
154 pRd[Index].status.val = 0;
\r
157 pRd[pDrv->queueList[queIdx].wRxListSize - 1].addr.val |= GMAC_RX_WRAP_BIT;
\r
159 /* Receive Buffer Queue Pointer Register */
\r
160 GMAC_SetRxQueue(pHw, (uint32_t) pRd, queIdx);
\r
165 * \brief Process successfully sent packets
\r
166 * \param pGmacd Pointer to GMAC Driver instance.
\r
168 static void GMACD_TxCompleteHandler(sGmacd *pGmacd, gmacQueList_t qId)
\r
170 Gmac *pHw = pGmacd->pHw;
\r
171 sGmacTxDescriptor *pTxTd;
\r
172 fGmacdTransferCallback fTxCb;
\r
176 tsr = GMAC_GetTxStatus(pHw);
\r
177 GMAC_ClearTxStatus(pHw, tsr);
\r
179 while (!GCIRC_EMPTY(pGmacd->queueList[qId].wTxHead, pGmacd->queueList[qId].wTxTail)) {
\r
180 pTxTd = &pGmacd->queueList[qId].pTxD[pGmacd->queueList[qId].wTxTail];
\r
182 /* Make hw descriptor updates visible to CPU */
\r
185 /* Exit if frame has not been sent yet:
\r
186 * On TX completion, the GMAC set the USED bit only into the
\r
187 * very first buffer descriptor of the sent frame.
\r
188 * Otherwise it updates this descriptor with status error bits.
\r
189 * This is the descriptor writeback.
\r
191 if ((pTxTd->status.val & GMAC_TX_USED_BIT) == 0)
\r
194 /* Process all buffers of the current transmitted frame */
\r
195 while ((pTxTd->status.val & GMAC_TX_LAST_BUFFER_BIT) == 0) {
\r
196 GCIRC_INC(pGmacd->queueList[qId].wTxTail, pGmacd->queueList[qId].wTxListSize);
\r
197 pTxTd = &pGmacd->queueList[qId].pTxD[pGmacd->queueList[qId].wTxTail];
\r
200 /* Notify upper layer that a frame has been sent */
\r
201 fTxCb = pGmacd->queueList[qId].fTxCbList[pGmacd->queueList[qId].wTxTail];
\r
205 /* Go to next frame */
\r
206 GCIRC_INC(pGmacd->queueList[qId].wTxTail, pGmacd->queueList[qId].wTxListSize);
\r
209 /* If a wakeup has been scheduled, notify upper layer that it can
\r
210 send other packets, send will be successfull. */
\r
211 if (pGmacd->queueList[qId].fWakupCb &&
\r
212 GCIRC_SPACE(pGmacd->queueList[qId].wTxHead,
\r
213 pGmacd->queueList[qId].wTxTail,
\r
214 pGmacd->queueList[qId].wTxListSize) >= pGmacd->queueList[qId].bWakeupThreshold)
\r
215 pGmacd->queueList[qId].fWakupCb();
\r
220 * \brief Reset TX queue when errors are detected
\r
221 * \param pGmacd Pointer to GMAC Driver instance.
\r
223 static void GMACD_TxErrorHandler(sGmacd *pGmacd, gmacQueList_t qId)
\r
225 Gmac *pHw = pGmacd->pHw;
\r
226 sGmacTxDescriptor *pTxTd;
\r
227 fGmacdTransferCallback fTxCb;
\r
230 /* Clear TXEN bit into the Network Configuration Register:
\r
231 * this is a workaround to recover from TX lockups that
\r
232 * occur on sama5d3 gmac (r1p24f2) when using scatter-gather.
\r
233 * This issue has never been seen on sama5d4 gmac (r1p31).
\r
235 GMAC_TransmitEnable(pHw, 0);
\r
237 /* The following step should be optional since this function is called
\r
238 * directly by the IRQ handler. Indeed, according to Cadence
\r
239 * documentation, the transmission is halted on errors such as
\r
240 * too many retries or transmit under run.
\r
241 * However it would become mandatory if the call of this function
\r
242 * were scheduled as a task by the IRQ handler (this is how Linux
\r
243 * driver works). Then this function might compete with GMACD_Send().
\r
245 * Setting bit 10, tx_halt, of the Network Control Register is not enough:
\r
246 * We should wait for bit 3, tx_go, of the Transmit Status Register to
\r
247 * be cleared at transmit completion if a frame is being transmitted.
\r
249 GMAC_TransmissionHalt(pHw);
\r
250 while (GMAC_GetTxStatus(pHw) & GMAC_TSR_TXGO);
\r
252 /* Treat frames in TX queue including the ones that caused the error. */
\r
253 while (!GCIRC_EMPTY(pGmacd->queueList[qId].wTxHead, pGmacd->queueList[qId].wTxTail)) {
\r
254 int tx_completed = 0;
\r
255 pTxTd = &pGmacd->queueList[qId].pTxD[pGmacd->queueList[qId].wTxTail];
\r
257 /* Make hw descriptor updates visible to CPU */
\r
260 /* Check USED bit on the very first buffer descriptor to validate
\r
263 if (pTxTd->status.val & GMAC_TX_USED_BIT)
\r
266 /* Go to the last buffer descriptor of the frame */
\r
267 while ((pTxTd->status.val & GMAC_TX_LAST_BUFFER_BIT) == 0) {
\r
268 GCIRC_INC(pGmacd->queueList[qId].wTxTail, pGmacd->queueList[qId].wTxListSize);
\r
269 pTxTd = &pGmacd->queueList[qId].pTxD[pGmacd->queueList[qId].wTxTail];
\r
272 /* Notify upper layer that a frame status */
\r
273 fTxCb = pGmacd->queueList[qId].fTxCbList[pGmacd->queueList[qId].wTxTail];
\r
275 fTxCb(tx_completed ? GMAC_TSR_TXCOMP : 0); // TODO: which error to notify?
\r
277 /* Go to next frame */
\r
278 GCIRC_INC(pGmacd->queueList[qId].wTxTail, pGmacd->queueList[qId].wTxListSize);
\r
281 /* Reset TX queue */
\r
282 GMACD_ResetTx(pGmacd, qId);
\r
285 tsr = GMAC_GetTxStatus(pHw);
\r
286 GMAC_ClearTxStatus(pHw, tsr);
\r
288 /* Now we are ready to start transmission again */
\r
289 GMAC_TransmitEnable(pHw, 1);
\r
290 if (pGmacd->queueList[qId].fWakupCb)
\r
291 pGmacd->queueList[qId].fWakupCb();
\r
295 /*---------------------------------------------------------------------------
\r
296 * Exported functions
\r
297 *---------------------------------------------------------------------------*/
\r
301 * \brief GMAC Interrupt handler
\r
302 * \param pGmacd Pointer to GMAC Driver instance.
\r
304 void GMACD_Handler(sGmacd *pGmacd, gmacQueList_t queIdx)
\r
306 Gmac *pHw = pGmacd->pHw;
\r
312 /* Interrupt Status Register is cleared on read */
\r
313 while ( (isr = GMAC_GetItStatus(pHw, queIdx)) !=0) {
\r
315 if (isr & GMAC_INT_RX_BITS) {
\r
317 rsr = GMAC_GetRxStatus(pHw);
\r
318 GMAC_ClearRxStatus(pHw, rsr);
\r
320 /* Invoke callback */
\r
321 if (pGmacd->queueList[queIdx].fRxCb)
\r
322 pGmacd->queueList[queIdx].fRxCb(rsr);
\r
326 if (isr & GMAC_INT_TX_ERR_BITS) {
\r
327 GMACD_TxErrorHandler(pGmacd, queIdx);
\r
332 if (isr & GMAC_IER_TCOMP)
\r
333 GMACD_TxCompleteHandler(pGmacd, queIdx);
\r
335 if (isr & GMAC_IER_HRESP) {
\r
336 TRACE_ERROR("HRESP\n\r");
\r
344 * \brief Initialize the GMAC with the Gmac controller address
\r
345 * \param pGmacd Pointer to GMAC Driver instance.
\r
346 * \param pHw Pointer to HW address for registers.
\r
347 * \param bID HW ID for power management
\r
348 * \param enableCAF Enable/Disable CopyAllFrame.
\r
349 * \param enableNBC Enable/Disable NoBroadCast.
\r
351 void GMACD_Init(sGmacd *pGmacd,
\r
354 uint8_t enableCAF,
\r
355 uint8_t enableNBC )
\r
357 uint32_t dwNcfgr, dwDcfgr;
\r
359 /* Check parameters */
\r
360 // assert(GRX_BUFFERS * GMAC_RX_UNITSIZE > GMAC_FRAME_LENTGH_MAX);
\r
362 TRACE_DEBUG("GMAC_Init\n\r");
\r
364 /* Initialize struct */
\r
369 PMC_EnablePeripheral(bID);
\r
371 /* Disable TX & RX and more */
\r
372 GMAC_NetworkControl(pHw, 0);
\r
373 GMAC_DisableAllQueueIt(pHw, ~0u);
\r
375 GMAC_ClearStatistics(pHw);
\r
376 /* Clear all status bits in the receive status register. */
\r
377 GMAC_ClearRxStatus(pHw, GMAC_RSR_RXOVR | GMAC_RSR_REC | GMAC_RSR_BNA |GMAC_RSR_HNO);
\r
379 /* Clear all status bits in the transmit status register */
\r
380 GMAC_ClearTxStatus(pHw, GMAC_TSR_UBR | GMAC_TSR_COL | GMAC_TSR_RLE
\r
381 | GMAC_TSR_TXGO | GMAC_TSR_TFC | GMAC_TSR_TXCOMP
\r
382 | GMAC_TSR_UND | GMAC_TSR_HRESP );
\r
384 /* Clear All interrupts */
\r
385 GMAC_GetItStatus(pHw, GMAC_QUE_0);
\r
386 GMAC_GetItStatus(pHw, GMAC_QUE_1);
\r
387 GMAC_GetItStatus(pHw, GMAC_QUE_2);
\r
389 /* Enable the copy of data into the buffers
\r
390 ignore broadcasts, and don't copy FCS. */
\r
391 dwNcfgr = GMAC_NCFGR_FD | GMAC_NCFGR_DBW(0) | GMAC_NCFGR_CLK_MCK_64;
\r
392 /* enable 1536 buffer */
\r
393 // dwNcfgr |= GMAC_NCFGR_MAXFS;
\r
395 dwNcfgr |= GMAC_NCFGR_CAF;
\r
398 dwNcfgr |= GMAC_NCFGR_NBC;
\r
401 dwDcfgr = (GMAC_DCFGR_DRBS(8) | (0<<8) | (0<<10) );
\r
402 GMAC_Configure(pHw, dwNcfgr);
\r
403 GMAC_DmaConfigure(pHw, dwDcfgr);
\r
408 * Initialize necessary allocated buffer lists for GMAC Driver to transfer data.
\r
409 * Must be invoked after GMACD_Init() but before RX/TX start.
\r
410 * \param pGmacd Pointer to GMAC Driver instance.
\r
411 * \param pRxBuffer Pointer to allocated buffer for RX. The address should
\r
412 * be 8-byte aligned and the size should be
\r
413 * GMAC_RX_UNITSIZE * wRxSize.
\r
414 * \param pRxD Pointer to allocated RX descriptor list.
\r
415 * \param wRxSize RX size, in number of registered units (RX descriptors).
\r
416 * \param pTxBuffer Pointer to allocated buffer for TX. The address should
\r
417 * be 8-byte aligned and the size should be
\r
418 * GMAC_TX_UNITSIZE * wTxSize.
\r
419 * \param pTxD Pointer to allocated TX descriptor list.
\r
420 * \param pTxCb Pointer to allocated TX callback list.
\r
421 * \param wTxSize TX size, in number of registered units (TX descriptors).
\r
422 * \return GMACD_OK or GMACD_PARAM.
\r
423 * \note If input address is not 8-byte aligned the address is automatically
\r
424 * adjusted and the list size is reduced by one.
\r
426 uint8_t GMACD_InitTransfer( sGmacd *pGmacd,
\r
427 uint8_t *pRxBuffer, sGmacRxDescriptor *pRxD,
\r
429 uint8_t *pTxBuffer, sGmacTxDescriptor *pTxD, fGmacdTransferCallback *pTxCb,
\r
431 gmacQueList_t queIdx)
\r
433 Gmac *pHw = pGmacd->pHw;
\r
435 if (wRxSize <= 1 || wTxSize <= 1 || pTxCb == NULL) return GMACD_PARAM;
\r
437 /* Assign RX buffers */
\r
438 if ( ((uint32_t)pRxBuffer & 0x7)
\r
439 || ((uint32_t)pRxD & 0x7) )
\r
442 TRACE_DEBUG("RX list address adjusted\n\r");
\r
444 pGmacd->queueList[queIdx].pRxBuffer = (uint8_t*)((uint32_t)pRxBuffer & 0xFFFFFFF8);
\r
445 pGmacd->queueList[queIdx].pRxD = (sGmacRxDescriptor*)((uint32_t)pRxD & 0xFFFFFFF8);
\r
446 pGmacd->queueList[queIdx].wRxListSize = wRxSize;
\r
447 /* Assign TX buffers */
\r
448 if ( ((uint32_t)pTxBuffer & 0x7)
\r
449 || ((uint32_t)pTxD & 0x7) )
\r
452 TRACE_DEBUG("TX list address adjusted\n\r");
\r
454 pGmacd->queueList[queIdx].pTxBuffer = (uint8_t*)((uint32_t)pTxBuffer & 0xFFFFFFF8);
\r
455 pGmacd->queueList[queIdx].pTxD = (sGmacTxDescriptor*)((uint32_t)pTxD & 0xFFFFFFF8);
\r
456 pGmacd->queueList[queIdx].wTxListSize = wTxSize;
\r
457 pGmacd->queueList[queIdx].fTxCbList = pTxCb;
\r
459 /* Reset TX & RX */
\r
460 GMACD_ResetRx(pGmacd, queIdx);
\r
461 GMACD_ResetTx(pGmacd, queIdx);
\r
464 /* Setup the interrupts for RX/TX completion (and errors) */
\r
468 /* YBP: Que 0 should be configured last so as to enable transmit and Receive in the NCR register */
\r
470 /* Enable Rx and Tx, plus the stats register. */
\r
471 GMAC_TransmitEnable(pHw, 1);
\r
472 GMAC_ReceiveEnable(pHw, 1);
\r
473 GMAC_StatisticsWriteEnable(pHw, 1);
\r
478 GMAC_INT_TX_ERR_BITS, GMAC_QUE_0);
\r
487 GMAC_INT_TX_ERR_BITS, GMAC_QUE_1);
\r
494 GMAC_INT_TX_ERR_BITS, GMAC_QUE_2);
\r
503 * Reset TX & RX queue & statistics
\r
504 * \param pGmacd Pointer to GMAC Driver instance.
\r
506 void GMACD_Reset(sGmacd *pGmacd)
\r
508 Gmac *pHw = pGmacd->pHw;
\r
510 GMACD_ResetRx(pGmacd, GMAC_QUE_0);
\r
511 GMACD_ResetRx(pGmacd, GMAC_QUE_1);
\r
512 GMACD_ResetRx(pGmacd, GMAC_QUE_2);
\r
514 GMACD_ResetTx(pGmacd, GMAC_QUE_0);
\r
515 GMACD_ResetTx(pGmacd, GMAC_QUE_1);
\r
516 GMACD_ResetTx(pGmacd, GMAC_QUE_2);
\r
518 //memset((void*)&GmacStatistics, 0x00, sizeof(GmacStats));
\r
519 GMAC_NetworkControl(pHw, GMAC_NCR_TXEN | GMAC_NCR_RXEN
\r
520 | GMAC_NCR_WESTAT | GMAC_NCR_CLRSTAT);
\r
524 * \brief Send a frame splitted into buffers. If the frame size is larger than transfer buffer size
\r
525 * error returned. If frame transfer status is monitored, specify callback for each frame.
\r
526 * \param pGmacd Pointer to GMAC Driver instance.
\r
527 * \param sgl Pointer to a scatter-gather list describing the buffers of the ethernet frame.
\r
529 uint8_t GMACD_SendSG(sGmacd *pGmacd,
\r
530 const sGmacSGList *sgl,
\r
531 fGmacdTransferCallback fTxCb,
\r
532 gmacQueList_t queIdx)
\r
534 Gmac *pHw = pGmacd->pHw;
\r
535 sGmacTxDescriptor *pTd = pGmacd->queueList[queIdx].pTxD;
\r
536 sGmacTxDescriptor *pTxTd;
\r
537 uint16_t wTxPos, wTxHead;
\r
540 TRACE_DEBUG("%s\n\r", __FUNCTION__);
\r
542 /* Check parameter */
\r
544 TRACE_ERROR("%s:: ethernet frame is empty.\r\n", __FUNCTION__);
\r
545 return GMACD_PARAM;
\r
547 if (sgl->len >= pGmacd->queueList[queIdx].wTxListSize) {
\r
548 TRACE_ERROR("%s: ethernet frame has too many buffers.\r\n", __FUNCTION__);
\r
549 return GMACD_PARAM;
\r
552 /* Check available space */
\r
553 if (GCIRC_SPACE(pGmacd->queueList[queIdx].wTxHead, pGmacd->queueList[queIdx].wTxTail, pGmacd->queueList[queIdx].wTxListSize) < (int)sgl->len)
\r
554 return GMACD_TX_BUSY;
\r
556 /* Tag end of TX queue */
\r
557 wTxHead = fixed_mod(pGmacd->queueList[queIdx].wTxHead + sgl->len, pGmacd->queueList[queIdx].wTxListSize);
\r
559 pGmacd->queueList[queIdx].fTxCbList[wTxPos] = NULL;
\r
560 pTxTd = &pTd[wTxPos];
\r
561 pTxTd->status.val = GMAC_TX_USED_BIT;
\r
563 /* Update buffer descriptors in reverse order to avoid a race
\r
564 * condition with hardware.
\r
566 for (i = (int)(sgl->len-1); i >= 0; --i) {
\r
567 const sGmacSG *sg = &sgl->sg[i];
\r
570 if (sg->size > GMAC_TX_UNITSIZE) {
\r
571 TRACE_ERROR("%s: buffer size is too big.\r\n", __FUNCTION__);
\r
572 return GMACD_PARAM;
\r
576 wTxPos = pGmacd->queueList[queIdx].wTxListSize-1;
\r
580 /* Reset TX callback */
\r
581 pGmacd->queueList[queIdx].fTxCbList[wTxPos] = NULL;
\r
583 pTxTd = &pTd[wTxPos];
\r
584 #ifdef GMAC_ZERO_COPY
\r
585 /** Update buffer descriptor address word:
\r
586 * MUST be done before status word to avoid a race condition.
\r
588 pTxTd->addr = (uint32_t)sg->pBuffer;
\r
590 /* Copy data into transmittion buffer */
\r
591 if (sg->pBuffer && sg->size)
\r
592 memcpy((void *)pTxTd->addr, sg->pBuffer, sg->size);
\r
595 /* Compute buffer descriptor status word */
\r
596 status = sg->size & GMAC_LENGTH_FRAME;
\r
597 if (i == (int)(sgl->len-1)) {
\r
598 status |= GMAC_TX_LAST_BUFFER_BIT;
\r
599 pGmacd->queueList[queIdx].fTxCbList[wTxPos] = fTxCb;
\r
601 if (wTxPos == pGmacd->queueList[queIdx].wTxListSize-1)
\r
602 status |= GMAC_TX_WRAP_BIT;
\r
604 /* Update buffer descriptor status word: clear USED bit */
\r
605 pTxTd->status.val = status;
\r
607 /* Make newly initialized descriptor visible to hardware */
\r
611 /* Update TX ring buffer pointers */
\r
612 pGmacd->queueList[queIdx].wTxHead = wTxHead;
\r
615 /* Now start to transmit if it is not already done */
\r
616 GMAC_TransmissionStart(pHw);
\r
622 * \brief Send a packet with GMAC. If the packet size is larger than transfer buffer size
\r
623 * error returned. If packet transfer status is monitored, specify callback for each packet.
\r
624 * \param pGmacd Pointer to GMAC Driver instance.
\r
625 * \param buffer The buffer to be send
\r
626 * \param size The size of buffer to be send
\r
627 * \param fGMAC_TxCallback Threshold Wakeup callback
\r
628 * \param fWakeUpCb TX Wakeup
\r
629 * \return OK, Busy or invalid packet
\r
631 uint8_t GMACD_Send(sGmacd *pGmacd,
\r
634 fGmacdTransferCallback fTxCb,
\r
635 gmacQueList_t queIdx)
\r
640 /* Init single entry scatter-gather list */
\r
642 sg.pBuffer = pBuffer;
\r
646 return GMACD_SendSG(pGmacd, &sgl, fTxCb, queIdx);
\r
651 * Return current load of TX.
\r
652 * \param pGmacd Pointer to GMAC Driver instance.
\r
654 uint32_t GMACD_TxLoad(sGmacd *pGmacd, gmacQueList_t queIdx)
\r
656 uint16_t head = pGmacd->queueList[queIdx].wTxHead;
\r
657 uint16_t tail = pGmacd->queueList[queIdx].wTxTail;
\r
658 return GCIRC_CNT(head, tail, pGmacd->queueList[queIdx].wTxListSize);
\r
662 * \brief Receive a packet with GMAC.
\r
663 * If not enough buffer for the packet, the remaining data is lost but right
\r
664 * frame length is returned.
\r
665 * \param pGmacd Pointer to GMAC Driver instance.
\r
666 * \param pFrame Buffer to store the frame
\r
667 * \param frameSize Size of the frame
\r
668 * \param pRcvSize Received size
\r
669 * \return OK, no data, or frame too small
\r
671 uint8_t GMACD_Poll(sGmacd * pGmacd,
\r
673 uint32_t frameSize,
\r
674 uint32_t *pRcvSize,
\r
675 gmacQueList_t queIdx)
\r
678 uint16_t bufferLength;
\r
679 uint32_t tmpFrameSize = 0;
\r
680 uint8_t *pTmpFrame = 0;
\r
681 uint32_t tmpIdx = pGmacd->queueList[queIdx].wRxI;
\r
682 volatile sGmacRxDescriptor *pRxTd = &pGmacd->queueList[queIdx].pRxD[pGmacd->queueList[queIdx].wRxI];
\r
684 uint8_t isFrame = 0;
\r
686 if (pFrame == NULL) return GMACD_PARAM;
\r
688 /* Set the default return value */
\r
691 /* Process received RxTd */
\r
692 while ((pRxTd->addr.val & GMAC_RX_OWNERSHIP_BIT) == GMAC_RX_OWNERSHIP_BIT)
\r
694 /* A start of frame has been received, discard previous fragments */
\r
695 if ((pRxTd->status.val & GMAC_RX_SOF_BIT) == GMAC_RX_SOF_BIT)
\r
697 /* Skip previous fragment */
\r
698 while (tmpIdx != pGmacd->queueList[queIdx].wRxI)
\r
700 pRxTd = &pGmacd->queueList[queIdx].pRxD[pGmacd->queueList[queIdx].wRxI];
\r
701 pRxTd->addr.val &= ~(GMAC_RX_OWNERSHIP_BIT);
\r
702 GCIRC_INC(pGmacd->queueList[queIdx].wRxI, pGmacd->queueList[queIdx].wRxListSize);
\r
704 pTmpFrame = pFrame;
\r
706 /* Start to gather buffers in a frame */
\r
709 /* Increment the pointer */
\r
710 GCIRC_INC(tmpIdx, pGmacd->queueList[queIdx].wRxListSize);
\r
711 /* Copy data in the frame buffer */
\r
713 if (tmpIdx == pGmacd->queueList[queIdx].wRxI)
\r
715 TRACE_INFO("no EOF (Invalid of buffers too small)\n\r");
\r
718 pRxTd = &pGmacd->queueList[queIdx].pRxD[pGmacd->queueList[queIdx].wRxI];
\r
719 pRxTd->addr.val &= ~(GMAC_RX_OWNERSHIP_BIT);
\r
720 GCIRC_INC(pGmacd->queueList[queIdx].wRxI, pGmacd->queueList[queIdx].wRxListSize);
\r
721 } while(tmpIdx != pGmacd->queueList[queIdx].wRxI);
\r
722 return GMACD_RX_NULL;
\r
725 /* Copy the buffer into the application frame */
\r
726 bufferLength = GMAC_RX_UNITSIZE;
\r
727 if ((tmpFrameSize + bufferLength) > frameSize)
\r
729 bufferLength = frameSize - tmpFrameSize;
\r
732 memcpy(pTmpFrame, (void*)(pRxTd->addr.val & GMAC_ADDRESS_MASK), bufferLength);
\r
733 pTmpFrame += bufferLength;
\r
734 tmpFrameSize += bufferLength;
\r
736 /* An end of frame has been received, return the data */
\r
737 if ((pRxTd->status.val & GMAC_RX_EOF_BIT) == GMAC_RX_EOF_BIT)
\r
739 /* Frame size from the GMAC */
\r
740 *pRcvSize = (pRxTd->status.val & GMAC_LENGTH_FRAME);
\r
742 /* Application frame buffer is too small all data have not been copied */
\r
743 if (tmpFrameSize < *pRcvSize) {
\r
744 return GMACD_SIZE_TOO_SMALL;
\r
746 TRACE_DEBUG("packet %d-%d (%d)\n\r", pGmacd->queueList[queIdx].wRxI, tmpIdx, *pRcvSize);
\r
747 /* All data have been copied in the application frame buffer => release TD */
\r
748 while (pGmacd->queueList[queIdx].wRxI != tmpIdx)
\r
750 pRxTd = &pGmacd->queueList[queIdx].pRxD[pGmacd->queueList[queIdx].wRxI];
\r
751 pRxTd->addr.val &= ~(GMAC_RX_OWNERSHIP_BIT);
\r
752 GCIRC_INC(pGmacd->queueList[queIdx].wRxI, pGmacd->queueList[queIdx].wRxListSize);
\r
758 /* SOF has not been detected, skip the fragment */
\r
760 pRxTd->addr.val &= ~(GMAC_RX_OWNERSHIP_BIT);
\r
761 pGmacd->queueList[queIdx].wRxI = tmpIdx;
\r
764 /* Process the next buffer */
\r
765 pRxTd = &pGmacd->queueList[queIdx].pRxD[tmpIdx];
\r
768 return GMACD_RX_NULL;
\r
772 * \brief Registers pRxCb callback. Callback will be invoked after the next received
\r
773 * frame. When GMAC_Poll() returns GMAC_RX_NO_DATA the application task call GMAC_Set_RxCb()
\r
774 * to register pRxCb() callback and enters suspend state. The callback is in charge
\r
775 * to resume the task once a new frame has been received. The next time GMAC_Poll()
\r
776 * is called, it will be successfull.
\r
777 * \param pGmacd Pointer to GMAC Driver instance.
\r
778 * \param pRxCb Pointer to callback function
\r
779 * \return OK, no data, or frame too small
\r
782 void GMACD_SetRxCallback(sGmacd * pGmacd, fGmacdTransferCallback fRxCb, gmacQueList_t queIdx)
\r
784 Gmac *pHw = pGmacd->pHw;
\r
788 GMAC_DisableIt(pHw, GMAC_IDR_RCOMP, queIdx);
\r
789 pGmacd->queueList[queIdx].fRxCb = NULL;
\r
793 pGmacd->queueList[queIdx].fRxCb = fRxCb;
\r
794 GMAC_EnableIt(pHw, GMAC_IER_RCOMP, queIdx);
\r
800 * Register/Clear TX wakeup callback.
\r
802 * When GMACD_Send() returns GMACD_TX_BUSY (all TD busy) the application
\r
803 * task calls GMACD_SetTxWakeupCallback() to register fWakeup() callback and
\r
804 * enters suspend state. The callback is in charge to resume the task once
\r
805 * several TD have been released. The next time GMACD_Send() will be called,
\r
806 * it shall be successfull.
\r
808 * This function is usually invoked with NULL callback from the TX wakeup
\r
809 * callback itself, to unregister. Once the callback has resumed the
\r
810 * application task, there is no need to invoke the callback again.
\r
812 * \param pGmacd Pointer to GMAC Driver instance.
\r
813 * \param fWakeup Wakeup callback.
\r
814 * \param bThreshould Number of free TD before wakeup callback invoked.
\r
815 * \return GMACD_OK, GMACD_PARAM on parameter error.
\r
817 uint8_t GMACD_SetTxWakeupCallback(sGmacd * pGmacd,
\r
818 fGmacdWakeupCallback fWakeup,
\r
819 uint8_t bThreshold,
\r
820 gmacQueList_t queIdx)
\r
822 if (fWakeup == NULL)
\r
824 pGmacd->queueList[queIdx].fWakupCb = NULL;
\r
828 if (bThreshold <= pGmacd->queueList[queIdx].wTxListSize)
\r
830 pGmacd->queueList[queIdx].fWakupCb = fWakeup;
\r
831 pGmacd->queueList[queIdx].bWakeupThreshold = bThreshold;
\r
835 return GMACD_PARAM;
\r
842 //uint32_t GMACD_GetQ0BuffBaseAddr (uint8_t ** baseAddrPtr)
\r
844 // *baseAddrPtr = &gs_uc_rx_buffer[0u];
\r
845 // return sizeof(gs_uc_rx_buffer);
\r
848 //uint32_t GMACD_GetQ1BuffBaseAddr (uint8_t ** baseAddrPtr)
\r
850 // *baseAddrPtr = &gs_uc_q1_rx_buffer[0u];
\r
851 // return sizeof(gs_uc_q1_rx_buffer);
\r
854 //uint32_t GMACD_GetQ2BuffBaseAddr (uint8_t ** baseAddrPtr)
\r
856 // *baseAddrPtr = &gs_uc_q2_rx_buffer[0u];
\r
857 // return sizeof(gs_uc_q2_rx_buffer);
\r