1 /* ----------------------------------------------------------------------------
\r
2 * ATMEL Microcontroller Software Support
\r
3 * ----------------------------------------------------------------------------
\r
4 * Copyright (c) 2008, 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
30 //-----------------------------------------------------------------------------
\r
32 //-----------------------------------------------------------------------------
\r
35 #include <utility/trace.h>
\r
36 #include <utility/assert.h>
\r
39 //------------------------------------------------------------------------------
\r
41 //------------------------------------------------------------------------------
\r
42 /// The buffer addresses written into the descriptors must be aligned so the
\r
43 /// last few bits are zero. These bits have special meaning for the EMAC
\r
44 /// peripheral and cannot be used as part of the address.
\r
45 #define EMAC_ADDRESS_MASK ((unsigned int)0xFFFFFFFC)
\r
46 #define EMAC_LENGTH_FRAME ((unsigned int)0x0FFF) /// Length of frame mask
\r
48 // receive buffer descriptor bits
\r
49 #define EMAC_RX_OWNERSHIP_BIT (1UL << 0)
\r
50 #define EMAC_RX_WRAP_BIT (1UL << 1)
\r
51 #define EMAC_RX_SOF_BIT (1UL << 14)
\r
52 #define EMAC_RX_EOF_BIT (1UL << 15)
\r
54 // Transmit buffer descriptor bits
\r
55 #define EMAC_TX_LAST_BUFFER_BIT (1UL << 15)
\r
56 #define EMAC_TX_WRAP_BIT (1UL << 30)
\r
57 #define EMAC_TX_USED_BIT (1UL << 31)
\r
59 //-----------------------------------------------------------------------------
\r
60 // Circular buffer management
\r
61 //-----------------------------------------------------------------------------
\r
62 // Return count in buffer
\r
63 #define CIRC_CNT(head,tail,size) (((head) - (tail)) & ((size)-1))
\r
65 // Return space available, 0..size-1
\r
66 // We always leave one free char as a completely full buffer
\r
67 // has head == tail, which is the same as empty
\r
68 #define CIRC_SPACE(head,tail,size) CIRC_CNT((tail),((head)+1),(size))
\r
70 // Return count up to the end of the buffer.
\r
71 // Carefully avoid accessing head and tail more than once,
\r
72 // so they can change underneath us without returning inconsistent results
\r
73 #define CIRC_CNT_TO_END(head,tail,size) \
\r
74 ({int end = (size) - (tail); \
\r
75 int n = ((head) + end) & ((size)-1); \
\r
76 n < end ? n : end;})
\r
78 // Return space available up to the end of the buffer
\r
79 #define CIRC_SPACE_TO_END(head,tail,size) \
\r
80 ({int end = (size) - 1 - (head); \
\r
81 int n = (end + (tail)) & ((size)-1); \
\r
82 n <= end ? n : end+1;})
\r
84 // Increment head or tail
\r
85 #define CIRC_INC(headortail,size) \
\r
87 if(headortail >= size) { \
\r
91 #define CIRC_EMPTY(circ) ((circ)->head == (circ)->tail)
\r
92 #define CIRC_CLEAR(circ) ((circ)->head = (circ)->tail = 0)
\r
95 //------------------------------------------------------------------------------
\r
97 //------------------------------------------------------------------------------
\r
98 #ifdef __ICCARM__ // IAR
\r
99 #pragma pack(4) // IAR
\r
100 #define __attribute__(...) // IAR
\r
102 /// Describes the type and attribute of Receive Transfer descriptor.
\r
103 typedef struct _EmacRxTDescriptor {
\r
105 unsigned int status;
\r
106 } __attribute__((packed, aligned(8))) EmacRxTDescriptor, *PEmacRxTDescriptor;
\r
108 /// Describes the type and attribute of Transmit Transfer descriptor.
\r
109 typedef struct _EmacTxTDescriptor {
\r
111 unsigned int status;
\r
112 } __attribute__((packed, aligned(8))) EmacTxTDescriptor, *PEmacTxTDescriptor;
\r
113 #ifdef __ICCARM__ // IAR
\r
114 #pragma pack() // IAR
\r
117 /// Descriptors for RX (required aligned by 8)
\r
119 volatile EmacRxTDescriptor td[RX_BUFFERS];
\r
120 EMAC_RxCallback rxCb; /// Callback function to be invoked once a frame has been received
\r
121 unsigned short idx;
\r
124 /// Descriptors for TX (required aligned by 8)
\r
126 volatile EmacTxTDescriptor td[TX_BUFFERS];
\r
127 EMAC_TxCallback txCb[TX_BUFFERS]; /// Callback function to be invoked once TD has been processed
\r
128 EMAC_WakeupCallback wakeupCb; /// Callback function to be invoked once several TD have been released
\r
129 unsigned short wakeupThreshold; /// Number of free TD before wakeupCb is invoked
\r
130 unsigned short head; /// Circular buffer head pointer incremented by the upper layer (buffer to be sent)
\r
131 unsigned short tail; /// Circular buffer head pointer incremented by the IT handler (buffer sent)
\r
134 //------------------------------------------------------------------------------
\r
135 // Internal variables
\r
136 //------------------------------------------------------------------------------
\r
137 // Receive Transfer Descriptor buffer
\r
138 #ifdef __ICCARM__ // IAR
\r
139 #pragma data_alignment=8 // IAR
\r
141 static volatile RxTd rxTd;
\r
142 // Transmit Transfer Descriptor buffer
\r
143 #ifdef __ICCARM__ // IAR
\r
144 #pragma data_alignment=8 // IAR
\r
146 static volatile TxTd txTd;
\r
148 // Section 3.6 of AMBA 2.0 spec states that burst should not cross 1K Boundaries.
\r
149 // Receive buffer manager writes are burst of 2 words => 3 lsb bits of the address shall be set to 0
\r
150 #ifdef __ICCARM__ // IAR
\r
151 #pragma data_alignment=8 // IAR
\r
153 static volatile unsigned char pTxBuffer[TX_BUFFERS * EMAC_TX_UNITSIZE] __attribute__((aligned(8)));
\r
155 #ifdef __ICCARM__ // IAR
\r
156 #pragma data_alignment=8 // IAR
\r
159 static volatile unsigned char pRxBuffer[RX_BUFFERS * EMAC_RX_UNITSIZE] __attribute__((aligned(8)));
\r
161 static volatile EmacStats EmacStatistics;
\r
163 //-----------------------------------------------------------------------------
\r
164 // Internal functions
\r
165 //-----------------------------------------------------------------------------
\r
167 //-----------------------------------------------------------------------------
\r
168 /// Wait PHY operation complete.
\r
169 /// Return 1 if the operation completed successfully.
\r
170 /// May be need to re-implemented to reduce CPU load.
\r
171 /// \param retry: the retry times, 0 to wait forever until complete.
\r
172 //-----------------------------------------------------------------------------
\r
173 static unsigned char EMAC_WaitPhy( unsigned int retry )
\r
175 unsigned int retry_count = 0;
\r
177 while((AT91C_BASE_EMAC->EMAC_NSR & AT91C_EMAC_IDLE) == 0) {
\r
187 if(retry_count >= retry) {
\r
189 trace_LOG(trace_ERROR, "E: Wait PHY time out\n\r");
\r
197 //-----------------------------------------------------------------------------
\r
198 // Exported functions
\r
199 //-----------------------------------------------------------------------------
\r
201 //-----------------------------------------------------------------------------
\r
202 // PHY management functions
\r
203 //-----------------------------------------------------------------------------
\r
205 //-----------------------------------------------------------------------------
\r
206 /// Set MDC clock according to current board clock. Per 802.3, MDC should be
\r
207 /// less then 2.5MHz.
\r
208 /// Return 1 if successfully, 0 if MDC clock not found.
\r
209 //-----------------------------------------------------------------------------
\r
210 unsigned char EMAC_SetMdcClock( unsigned int mck )
\r
214 if (mck <= 20000000) {
\r
215 clock_dividor = AT91C_EMAC_CLK_HCLK_8; /// MDC clock = MCK/8
\r
217 else if (mck <= 40000000) {
\r
218 clock_dividor = AT91C_EMAC_CLK_HCLK_16; /// MDC clock = MCK/16
\r
220 else if (mck <= 80000000) {
\r
221 clock_dividor = AT91C_EMAC_CLK_HCLK_32; /// MDC clock = MCK/32
\r
223 else if (mck <= 160000000) {
\r
224 clock_dividor = AT91C_EMAC_CLK_HCLK_64; /// MDC clock = MCK/64
\r
227 trace_LOG(trace_ERROR, "E: No valid MDC clock.\n\r");
\r
230 AT91C_BASE_EMAC->EMAC_NCFGR = (AT91C_BASE_EMAC->EMAC_NCFGR & (~AT91C_EMAC_CLK))
\r
235 //-----------------------------------------------------------------------------
\r
236 /// Enable MDI with PHY
\r
237 //-----------------------------------------------------------------------------
\r
238 void EMAC_EnableMdio( void )
\r
240 AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_MPE;
\r
243 //-----------------------------------------------------------------------------
\r
244 /// Enable MDI with PHY
\r
245 //-----------------------------------------------------------------------------
\r
246 void EMAC_DisableMdio( void )
\r
248 AT91C_BASE_EMAC->EMAC_NCR &= ~AT91C_EMAC_MPE;
\r
251 //-----------------------------------------------------------------------------
\r
252 /// Enable MII mode for EMAC, called once after autonegotiate
\r
253 //-----------------------------------------------------------------------------
\r
254 void EMAC_EnableMII( void )
\r
256 AT91C_BASE_EMAC->EMAC_USRIO = AT91C_EMAC_CLKEN;
\r
259 //-----------------------------------------------------------------------------
\r
260 /// Enable RMII mode for EMAC, called once after autonegotiate
\r
261 //-----------------------------------------------------------------------------
\r
262 void EMAC_EnableRMII( void )
\r
264 AT91C_BASE_EMAC->EMAC_USRIO = AT91C_EMAC_CLKEN | AT91C_EMAC_RMII;
\r
267 //-----------------------------------------------------------------------------
\r
268 /// Read PHY register.
\r
269 /// Return 1 if successfully, 0 if timeout.
\r
270 /// \param PhyAddress PHY Address
\r
271 /// \param Address Register Address
\r
272 /// \param pValue Pointer to a 32 bit location to store read data
\r
273 /// \param retry The retry times, 0 to wait forever until complete.
\r
274 //-----------------------------------------------------------------------------
\r
275 unsigned char EMAC_ReadPhy(unsigned char PhyAddress,
\r
276 unsigned char Address,
\r
277 unsigned int *pValue,
\r
278 unsigned int retry)
\r
280 AT91C_BASE_EMAC->EMAC_MAN = (AT91C_EMAC_SOF & (0x01 << 30))
\r
281 | (AT91C_EMAC_CODE & (2 << 16))
\r
282 | (AT91C_EMAC_RW & (2 << 28))
\r
283 | (AT91C_EMAC_PHYA & ((PhyAddress & 0x1f) << 23))
\r
284 | (AT91C_EMAC_REGA & (Address << 18));
\r
286 if ( EMAC_WaitPhy(retry) == 0 ) {
\r
288 trace_LOG(trace_ERROR, "TimeOut EMAC_ReadPhy\n\r");
\r
291 *pValue = ( AT91C_BASE_EMAC->EMAC_MAN & 0x0000ffff );
\r
295 //-----------------------------------------------------------------------------
\r
296 /// Write PHY register
\r
297 /// Return 1 if successfully, 0 if timeout.
\r
298 /// \param PhyAddress PHY Address
\r
299 /// \param Address Register Address
\r
300 /// \param Value Data to write ( Actually 16 bit data )
\r
301 /// \param retry The retry times, 0 to wait forever until complete.
\r
302 //-----------------------------------------------------------------------------
\r
303 unsigned char EMAC_WritePhy(unsigned char PhyAddress,
\r
304 unsigned char Address,
\r
305 unsigned int Value,
\r
306 unsigned int retry)
\r
308 AT91C_BASE_EMAC->EMAC_MAN = (AT91C_EMAC_SOF & (0x01 << 30))
\r
309 | (AT91C_EMAC_CODE & (2 << 16))
\r
310 | (AT91C_EMAC_RW & (1 << 28))
\r
311 | (AT91C_EMAC_PHYA & ((PhyAddress & 0x1f) << 23))
\r
312 | (AT91C_EMAC_REGA & (Address << 18))
\r
313 | (AT91C_EMAC_DATA & Value) ;
\r
314 if ( EMAC_WaitPhy(retry) == 0 ) {
\r
316 trace_LOG(trace_ERROR, "TimeOut EMAC_WritePhy\n\r");
\r
322 //-----------------------------------------------------------------------------
\r
323 /// Setup the EMAC for the link : speed 100M/10M and Full/Half duplex
\r
324 /// \param speed Link speed, 0 for 10M, 1 for 100M
\r
325 /// \param fullduplex 1 for Full Duplex mode
\r
326 //-----------------------------------------------------------------------------
\r
327 void EMAC_SetLinkSpeed(unsigned char speed, unsigned char fullduplex)
\r
329 unsigned int ncfgr;
\r
331 ncfgr = AT91C_BASE_EMAC->EMAC_NCFGR;
\r
332 ncfgr &= ~(AT91C_EMAC_SPD | AT91C_EMAC_FD);
\r
335 ncfgr |= AT91C_EMAC_SPD;
\r
339 ncfgr |= AT91C_EMAC_FD;
\r
341 AT91C_BASE_EMAC->EMAC_NCFGR = ncfgr;
\r
346 //-----------------------------------------------------------------------------
\r
348 //-----------------------------------------------------------------------------
\r
350 //-----------------------------------------------------------------------------
\r
351 /// EMAC Interrupt handler
\r
352 //-----------------------------------------------------------------------------
\r
353 void EMAC_Handler(void)
\r
355 volatile EmacTxTDescriptor *pTxTd;
\r
356 volatile EMAC_TxCallback *pTxCb;
\r
360 unsigned int rxStatusFlag;
\r
361 unsigned int txStatusFlag;
\r
363 //trace_LOG(trace_DEBUG, "EMAC_Handler\n\r");
\r
364 isr = AT91C_BASE_EMAC->EMAC_ISR & AT91C_BASE_EMAC->EMAC_IMR;
\r
365 rsr = AT91C_BASE_EMAC->EMAC_RSR;
\r
366 tsr = AT91C_BASE_EMAC->EMAC_TSR;
\r
369 if ((isr & AT91C_EMAC_RCOMP) || (rsr & AT91C_EMAC_REC)) {
\r
370 rxStatusFlag = AT91C_EMAC_REC;
\r
373 EmacStatistics.rx_packets++;
\r
376 if (rsr & AT91C_EMAC_OVR) {
\r
377 rxStatusFlag |= AT91C_EMAC_OVR;
\r
378 EmacStatistics.rx_ovrs++;
\r
381 if (rsr & AT91C_EMAC_BNA) {
\r
382 rxStatusFlag |= AT91C_EMAC_BNA;
\r
383 EmacStatistics.rx_bnas++;
\r
386 AT91C_BASE_EMAC->EMAC_RSR |= rxStatusFlag;
\r
388 // Invoke callbacks
\r
390 rxTd.rxCb(rxStatusFlag);
\r
395 if ((isr & AT91C_EMAC_TCOMP) || (tsr & AT91C_EMAC_COMP)) {
\r
397 txStatusFlag = AT91C_EMAC_COMP;
\r
398 EmacStatistics.tx_comp ++;
\r
400 // A frame transmitted
\r
402 if (tsr & AT91C_EMAC_RLES) {
\r
403 txStatusFlag |= AT91C_EMAC_RLES;
\r
404 EmacStatistics.tx_errors++;
\r
407 if (tsr & AT91C_EMAC_COL) {
\r
408 txStatusFlag |= AT91C_EMAC_COL;
\r
409 EmacStatistics.collisions++;
\r
412 if (tsr & AT91C_EMAC_BEX) {
\r
413 txStatusFlag |= AT91C_EMAC_BEX;
\r
414 EmacStatistics.tx_exausts++;
\r
417 if (tsr & AT91C_EMAC_UND) {
\r
418 txStatusFlag |= AT91C_EMAC_UND;
\r
419 EmacStatistics.tx_underruns++;
\r
422 AT91C_BASE_EMAC->EMAC_TSR |= txStatusFlag;
\r
424 // Sanity check: Tx buffers have to be scheduled
\r
425 ASSERT(!CIRC_EMPTY(&txTd),
\r
426 "-F- EMAC Tx interrupt received meanwhile no TX buffers has been scheduled\n\r");
\r
428 // Check the buffers
\r
429 while (CIRC_CNT(txTd.head, txTd.tail, TX_BUFFERS)) {
\r
430 pTxTd = txTd.td + txTd.tail;
\r
431 pTxCb = txTd.txCb + txTd.tail;
\r
433 // Exit if buffer has not been sent yet
\r
434 if ((pTxTd->status & EMAC_TX_USED_BIT) == 0) {
\r
438 // Notify upper layer that packet has been sent
\r
440 (*pTxCb)(txStatusFlag);
\r
443 CIRC_INC( txTd.tail, TX_BUFFERS );
\r
446 // If a wakeup has been scheduled, notify upper layer that it can send
\r
447 // other packets, send will be successfull.
\r
448 if( (CIRC_SPACE(txTd.head, txTd.tail, TX_BUFFERS) >= txTd.wakeupThreshold)
\r
449 && txTd.wakeupCb) {
\r
455 //-----------------------------------------------------------------------------
\r
456 /// Initialize the EMAC with the emac controller address
\r
457 /// \param id HW ID for power management
\r
458 /// \param pTxWakeUpfct Thresold TX Wakeup Callback
\r
459 /// \param pRxfct RX Wakeup Callback
\r
460 /// \param pMacAddress Mac Address
\r
461 /// \param enableCAF enable AT91C_EMAC_CAF if needed by application
\r
462 /// \param enableNBC AT91C_EMAC_NBC if needed by application
\r
463 //-----------------------------------------------------------------------------
\r
464 void EMAC_Init( unsigned char id, const unsigned char *pMacAddress,
\r
465 unsigned char enableCAF, unsigned char enableNBC )
\r
468 unsigned int Address;
\r
470 // Check parameters
\r
471 ASSERT(RX_BUFFERS * EMAC_RX_UNITSIZE > EMAC_FRAME_LENTGH_MAX,
\r
472 "E: RX buffers too small\n\r");
\r
474 trace_LOG(trace_DEBUG, "EMAC_Init\n\r");
\r
477 AT91C_BASE_PMC->PMC_PCER = 1 << id;
\r
479 // Disable TX & RX and more
\r
480 AT91C_BASE_EMAC->EMAC_NCR = 0;
\r
483 AT91C_BASE_EMAC->EMAC_IDR = ~0;
\r
488 // Setup the RX descriptors.
\r
489 for(Index = 0; Index < RX_BUFFERS; Index++) {
\r
491 Address = (unsigned int)(&(pRxBuffer[Index * EMAC_RX_UNITSIZE]));
\r
492 // Remove EMAC_RX_OWNERSHIP_BIT and EMAC_RX_WRAP_BIT
\r
493 rxTd.td[Index].addr = Address & EMAC_ADDRESS_MASK;
\r
494 rxTd.td[Index].status = 0;
\r
496 rxTd.td[RX_BUFFERS - 1].addr |= EMAC_RX_WRAP_BIT;
\r
498 // Setup the TX descriptors.
\r
499 for(Index = 0; Index < TX_BUFFERS; Index++) {
\r
501 Address = (unsigned int)(&(pTxBuffer[Index * EMAC_TX_UNITSIZE]));
\r
502 txTd.td[Index].addr = Address;
\r
503 txTd.td[Index].status = EMAC_TX_USED_BIT;
\r
505 txTd.td[TX_BUFFERS - 1].status = EMAC_TX_USED_BIT | EMAC_TX_WRAP_BIT;
\r
507 // Set the MAC address
\r
508 if( pMacAddress != (unsigned char *)0 ) {
\r
509 AT91C_BASE_EMAC->EMAC_SA1L = ( ((unsigned int)pMacAddress[3] << 24)
\r
510 | ((unsigned int)pMacAddress[2] << 16)
\r
511 | ((unsigned int)pMacAddress[1] << 8 )
\r
512 | pMacAddress[0] );
\r
514 AT91C_BASE_EMAC->EMAC_SA1H = ( ((unsigned int)pMacAddress[5] << 8 )
\r
515 | pMacAddress[4] );
\r
517 // Now setup the descriptors
\r
518 // Receive Buffer Queue Pointer Register
\r
519 AT91C_BASE_EMAC->EMAC_RBQP = (unsigned int) (rxTd.td);
\r
520 // Transmit Buffer Queue Pointer Register
\r
521 AT91C_BASE_EMAC->EMAC_TBQP = (unsigned int) (txTd.td);
\r
523 AT91C_BASE_EMAC->EMAC_NCR = AT91C_EMAC_CLRSTAT;
\r
525 // Clear all status bits in the receive status register.
\r
526 AT91C_BASE_EMAC->EMAC_RSR = (AT91C_EMAC_OVR | AT91C_EMAC_REC | AT91C_EMAC_BNA);
\r
528 // Clear all status bits in the transmit status register
\r
529 AT91C_BASE_EMAC->EMAC_TSR = ( AT91C_EMAC_UBR | AT91C_EMAC_COL | AT91C_EMAC_RLES
\r
530 | AT91C_EMAC_BEX | AT91C_EMAC_COMP
\r
531 | AT91C_EMAC_UND );
\r
533 // Clear interrupts
\r
534 AT91C_BASE_EMAC->EMAC_ISR;
\r
536 // Enable the copy of data into the buffers
\r
537 // ignore broadcasts, and don't copy FCS.
\r
538 AT91C_BASE_EMAC->EMAC_NCFGR |= (AT91C_EMAC_DRFCS | AT91C_EMAC_PAE);
\r
540 if( enableCAF == EMAC_CAF_ENABLE ) {
\r
541 AT91C_BASE_EMAC->EMAC_NCFGR |= AT91C_EMAC_CAF;
\r
543 if( enableNBC == EMAC_NBC_ENABLE ) {
\r
544 AT91C_BASE_EMAC->EMAC_NCFGR |= AT91C_EMAC_NBC;
\r
547 // Enable Rx and Tx, plus the stats register.
\r
548 AT91C_BASE_EMAC->EMAC_NCR |= (AT91C_EMAC_TE | AT91C_EMAC_RE | AT91C_EMAC_WESTAT);
\r
550 // Setup the interrupts for TX (and errors)
\r
551 AT91C_BASE_EMAC->EMAC_IER = AT91C_EMAC_RXUBR
\r
557 | AT91C_EMAC_HRESP;
\r
561 //-----------------------------------------------------------------------------
\r
562 /// Get the statstic information & reset it
\r
563 /// \param pStats Pointer to EmacStats structure to copy the informations
\r
564 /// \param reset Reset the statistics after copy it
\r
565 //-----------------------------------------------------------------------------
\r
566 void EMAC_GetStatistics(EmacStats *pStats, unsigned char reset)
\r
568 unsigned int ncrBackup = 0;
\r
570 trace_LOG(trace_DEBUG, "EMAC_GetStatistics\n\r");
\r
573 if (pStats == (EmacStats *) 0) {
\r
577 ncrBackup = AT91C_BASE_EMAC->EMAC_NCR & (AT91C_EMAC_TE | AT91C_EMAC_RE);
\r
580 AT91C_BASE_EMAC->EMAC_NCR = ncrBackup & ~(AT91C_EMAC_TE | AT91C_EMAC_RE);
\r
582 // Copy the informations
\r
583 memcpy(pStats, (void*)&EmacStatistics, sizeof(EmacStats));
\r
585 // Reset the statistics
\r
587 memset((void*)&EmacStatistics, 0x00, sizeof(EmacStats));
\r
588 AT91C_BASE_EMAC->EMAC_NCR = ncrBackup | AT91C_EMAC_CLRSTAT;
\r
592 AT91C_BASE_EMAC->EMAC_NCR = ncrBackup;
\r
595 //-----------------------------------------------------------------------------
\r
596 /// Send a packet with EMAC.
\r
597 /// If the packet size is larger than transfer buffer size error returned.
\r
598 /// \param buffer The buffer to be send
\r
599 /// \param size The size of buffer to be send
\r
600 /// \param fEMAC_TxCallback Threshold Wakeup callback
\r
601 /// \param fWakeUpCb TX Wakeup
\r
602 /// \return OK, Busy or invalid packet
\r
603 //-----------------------------------------------------------------------------
\r
604 unsigned char EMAC_Send(void *pBuffer,
\r
606 EMAC_TxCallback fEMAC_TxCallback)
\r
608 volatile EmacTxTDescriptor *pTxTd;
\r
609 volatile EMAC_TxCallback *pTxCb;
\r
611 //trace_LOG(trace_DEBUG, "EMAC_Send\n\r");
\r
614 if (size > EMAC_TX_UNITSIZE) {
\r
616 trace_LOG(trace_ERROR, "-E- EMAC driver does not split send packets.");
\r
617 trace_LOG(trace_ERROR, " It can send %d bytes max in one packet (%u bytes requested)\n\r",
\r
618 EMAC_TX_UNITSIZE, size);
\r
619 return EMAC_TX_INVALID_PACKET;
\r
622 // If no free TxTd, buffer can't be sent, schedule the wakeup callback
\r
623 if( CIRC_SPACE(txTd.head, txTd.tail, TX_BUFFERS) == 0) {
\r
624 return EMAC_TX_BUFFER_BUSY;
\r
628 // Pointers to the current TxTd
\r
629 pTxTd = txTd.td + txTd.head;
\r
630 pTxCb = txTd.txCb + txTd.head;
\r
633 ASSERT((pTxTd->status & EMAC_TX_USED_BIT) != 0,
\r
634 "-F- Buffer is still under EMAC control\n\r");
\r
636 // Setup/Copy data to transmition buffer
\r
637 if (pBuffer && size) {
\r
638 // Driver manage the ring buffer
\r
639 memcpy((void *)pTxTd->addr, pBuffer, size);
\r
643 *pTxCb = fEMAC_TxCallback;
\r
645 // Update TD status
\r
646 // The buffer size defined is length of ethernet frame
\r
647 // so it's always the last buffer of the frame.
\r
648 if (txTd.head == TX_BUFFERS-1) {
\r
650 (size & EMAC_LENGTH_FRAME) | EMAC_TX_LAST_BUFFER_BIT | EMAC_TX_WRAP_BIT;
\r
653 pTxTd->status = (size & EMAC_LENGTH_FRAME) | EMAC_TX_LAST_BUFFER_BIT;
\r
656 CIRC_INC(txTd.head, TX_BUFFERS)
\r
658 // Tx packets count
\r
659 EmacStatistics.tx_packets++;
\r
661 // Now start to transmit if it is not already done
\r
662 AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_TSTART;
\r
667 //-----------------------------------------------------------------------------
\r
668 /// Receive a packet with EMAC
\r
669 /// If not enough buffer for the packet, the remaining data is lost but right
\r
670 /// frame length is returned.
\r
671 /// \param pFrame Buffer to store the frame
\r
672 /// \param frameSize Size of the frame
\r
673 /// \param pRcvSize Received size
\r
674 /// \return OK, no data, or frame too small
\r
675 //-----------------------------------------------------------------------------
\r
676 unsigned char EMAC_Poll(unsigned char *pFrame,
\r
677 unsigned int frameSize,
\r
678 unsigned int *pRcvSize)
\r
680 unsigned short bufferLength;
\r
681 unsigned int tmpFrameSize=0;
\r
682 unsigned char *pTmpFrame=0;
\r
683 unsigned int tmpIdx = rxTd.idx;
\r
684 volatile EmacRxTDescriptor *pRxTd = rxTd.td + rxTd.idx;
\r
686 ASSERT(pFrame, "F: EMAC_Poll\n\r");
\r
689 // Set the default return value
\r
692 // Process received RxTd
\r
693 while ((pRxTd->addr & EMAC_RX_OWNERSHIP_BIT) == EMAC_RX_OWNERSHIP_BIT) {
\r
695 // A start of frame has been received, discard previous fragments
\r
696 if ((pRxTd->status & EMAC_RX_SOF_BIT) == EMAC_RX_SOF_BIT) {
\r
697 // Skip previous fragment
\r
698 while (tmpIdx != rxTd.idx) {
\r
699 pRxTd = rxTd.td + rxTd.idx;
\r
700 pRxTd->addr &= ~(EMAC_RX_OWNERSHIP_BIT);
\r
701 CIRC_INC(rxTd.idx, RX_BUFFERS);
\r
703 // Reset the temporary frame pointer
\r
704 pTmpFrame = pFrame;
\r
706 // Start to gather buffers in a frame
\r
710 // Increment the pointer
\r
711 CIRC_INC(tmpIdx, RX_BUFFERS);
\r
713 // Copy data in the frame buffer
\r
715 if (tmpIdx == rxTd.idx) {
\r
716 trace_LOG(trace_INFO,
\r
717 "I: no EOF (Invalid of buffers too small)\n\r");
\r
721 pRxTd = rxTd.td + rxTd.idx;
\r
722 pRxTd->addr &= ~(EMAC_RX_OWNERSHIP_BIT);
\r
723 CIRC_INC(rxTd.idx, RX_BUFFERS);
\r
724 } while(tmpIdx != rxTd.idx);
\r
725 return EMAC_RX_NO_DATA;
\r
727 // Copy the buffer into the application frame
\r
728 bufferLength = EMAC_RX_UNITSIZE;
\r
729 if ((tmpFrameSize + bufferLength) > frameSize) {
\r
730 bufferLength = frameSize - tmpFrameSize;
\r
733 memcpy(pTmpFrame, (void*)(pRxTd->addr & EMAC_ADDRESS_MASK), bufferLength);
\r
734 pTmpFrame += bufferLength;
\r
735 tmpFrameSize += bufferLength;
\r
737 // An end of frame has been received, return the data
\r
738 if ((pRxTd->status & EMAC_RX_EOF_BIT) == EMAC_RX_EOF_BIT) {
\r
739 // Frame size from the EMAC
\r
740 *pRcvSize = (pRxTd->status & EMAC_LENGTH_FRAME);
\r
742 // Application frame buffer is too small all data have not been copied
\r
743 if (tmpFrameSize < *pRcvSize) {
\r
744 printf("size req %u size allocated %u\n\r", *pRcvSize, frameSize);
\r
746 return EMAC_RX_FRAME_SIZE_TOO_SMALL;
\r
749 trace_LOG(trace_INFO, "packet %d-%u (%u)\n\r", rxTd.idx, tmpIdx, *pRcvSize);
\r
750 // All data have been copied in the application frame buffer => release TD
\r
751 while (rxTd.idx != tmpIdx) {
\r
752 pRxTd = rxTd.td + rxTd.idx;
\r
753 pRxTd->addr &= ~(EMAC_RX_OWNERSHIP_BIT);
\r
754 CIRC_INC(rxTd.idx, RX_BUFFERS);
\r
756 EmacStatistics.rx_packets++;
\r
761 // SOF has not been detected, skip the fragment
\r
763 pRxTd->addr &= ~(EMAC_RX_OWNERSHIP_BIT);
\r
767 // Process the next buffer
\r
768 pRxTd = rxTd.td + tmpIdx;
\r
771 //trace_LOG(trace_DEBUG, "E");
\r
772 return EMAC_RX_NO_DATA;
\r
775 //-----------------------------------------------------------------------------
\r
776 /// Registers pRxCb callback. Callback will be invoked after the next received
\r
778 /// When EMAC_Poll() returns EMAC_RX_NO_DATA the application task call EMAC_Set_RxCb()
\r
779 /// to register pRxCb() callback and enters suspend state. The callback is in charge
\r
780 /// to resume the task once a new frame has been received. The next time EMAC_Poll()
\r
781 /// is called, it will be successfull.
\r
782 /// \param pRxCb Pointer to callback function
\r
783 //-----------------------------------------------------------------------------
\r
784 void EMAC_Set_RxCb(EMAC_RxCallback pRxCb)
\r
787 AT91C_BASE_EMAC->EMAC_IER = AT91C_EMAC_RCOMP;
\r
790 //-----------------------------------------------------------------------------
\r
791 /// Remove the RX callback function.
\r
792 /// This function is usually invoked from the RX callback itself. Once the callback
\r
793 /// has resumed the application task, there is no need to invoke the callback again.
\r
794 //-----------------------------------------------------------------------------
\r
795 void EMAC_Clear_RxCb(void)
\r
797 AT91C_BASE_EMAC->EMAC_IDR = AT91C_EMAC_RCOMP;
\r
798 rxTd.rxCb = (EMAC_RxCallback) 0;
\r
801 //-----------------------------------------------------------------------------
\r
802 /// Registers TX wakeup callback callback. Callback will be invoked once several
\r
803 /// transfer descriptors are available.
\r
804 /// When EMAC_Send() returns EMAC_TX_BUFFER_BUSY (all TD busy) the application
\r
805 /// task calls EMAC_Set_TxWakeUpCb() to register pTxWakeUpCb() callback and
\r
806 /// enters suspend state. The callback is in charge to resume the task once
\r
807 /// several TD have been released. The next time EMAC_Send() will be called, it
\r
808 /// shall be successfull.
\r
809 /// \param pTxWakeUpCb Pointer to callback function
\r
810 /// \param threshold Minimum number of available transfer descriptors before pTxWakeUpCb() is invoked
\r
811 /// \return 0= success, 1 = threshold exceeds nuber of transfer descriptors
\r
812 //-----------------------------------------------------------------------------
\r
813 char EMAC_Set_TxWakeUpCb(EMAC_WakeupCallback pTxWakeUpCb, unsigned short threshold)
\r
815 if (threshold <= TX_BUFFERS) {
\r
816 txTd.wakeupCb = pTxWakeUpCb;
\r
817 txTd.wakeupThreshold = threshold;
\r
823 //-----------------------------------------------------------------------------
\r
824 /// Remove the TX wakeup callback function.
\r
825 /// This function is usually invoked from the TX wakeup callback itself. Once the callback
\r
826 /// has resumed the application task, there is no need to invoke the callback again.
\r
827 //-----------------------------------------------------------------------------
\r
828 void EMAC_Clear_TxWakeUpCb(void)
\r
830 txTd.wakeupCb = (EMAC_WakeupCallback) 0;
\r