From 044ad7c3987460ede48ff27afd6bdb0ca05a0432 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Mon, 4 Jul 2011 20:52:54 +0200 Subject: import at91lib from at91lib_20100901_softpack_1_9_v_1_0_svn_v15011 it's sad to see that atmel doesn't publish their svn repo or has a centralized location or even puts proper version/release info into the library itself --- peripherals/emac/emac.c | 914 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 914 insertions(+) create mode 100644 peripherals/emac/emac.c (limited to 'peripherals/emac/emac.c') diff --git a/peripherals/emac/emac.c b/peripherals/emac/emac.c new file mode 100644 index 0000000..09380f8 --- /dev/null +++ b/peripherals/emac/emac.c @@ -0,0 +1,914 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are 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. + * ---------------------------------------------------------------------------- + */ + +//----------------------------------------------------------------------------- +// Headers +//----------------------------------------------------------------------------- +#include +#include "emac.h" +#include +#include +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ +/// The buffer addresses written into the descriptors must be aligned so the +/// last few bits are zero. These bits have special meaning for the EMAC +/// peripheral and cannot be used as part of the address. +#define EMAC_ADDRESS_MASK ((unsigned int)0xFFFFFFFC) +#define EMAC_LENGTH_FRAME ((unsigned int)0x0FFF) /// Length of frame mask + +// receive buffer descriptor bits +#define EMAC_RX_OWNERSHIP_BIT (1 << 0) +#define EMAC_RX_WRAP_BIT (1 << 1) +#define EMAC_RX_SOF_BIT (1 << 14) +#define EMAC_RX_EOF_BIT (1 << 15) + +// Transmit buffer descriptor bits +#define EMAC_TX_LAST_BUFFER_BIT (1 << 15) +#define EMAC_TX_WRAP_BIT (1 << 30) +#define EMAC_TX_USED_BIT (1 << 31) +#define EMAC_TX_RLE_BIT (1 << 29) /// Retry Limit Exceeded +#define EMAC_TX_UND_BIT (1 << 28) /// Tx Buffer Underrun +#define EMAC_TX_ERR_BIT (1 << 27) /// Exhausted in mid-frame +#define EMAC_TX_ERR_BITS \ + (EMAC_TX_RLE_BIT | EMAC_TX_UND_BIT | EMAC_TX_ERR_BIT) + +//----------------------------------------------------------------------------- +// Circular buffer management +//----------------------------------------------------------------------------- +// Return count in buffer +#define CIRC_CNT(head,tail,size) (((head) - (tail)) & ((size)-1)) + +// Return space available, 0..size-1 +// We always leave one free char as a completely full buffer +// has head == tail, which is the same as empty +#define CIRC_SPACE(head,tail,size) CIRC_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 CIRC_CNT_TO_END(head,tail,size) \ + ({int end = (size) - (tail); \ + int n = ((head) + end) & ((size)-1); \ + n < end ? n : end;}) + +// Return space available up to the end of the buffer +#define CIRC_SPACE_TO_END(head,tail,size) \ + ({int end = (size) - 1 - (head); \ + int n = (end + (tail)) & ((size)-1); \ + n <= end ? n : end+1;}) + +// Increment head or tail +#define CIRC_INC(headortail,size) \ + headortail++; \ + if(headortail >= size) { \ + headortail = 0; \ + } + +#define CIRC_EMPTY(circ) ((circ)->head == (circ)->tail) +#define CIRC_CLEAR(circ) ((circ)->head = (circ)->tail = 0) + + +//------------------------------------------------------------------------------ +// Structures +//------------------------------------------------------------------------------ +#ifdef __ICCARM__ // IAR +#pragma pack(4) // IAR +#define __attribute__(...) // IAR +#endif // IAR +/// Describes the type and attribute of Receive Transfer descriptor. +typedef struct _EmacRxTDescriptor { + unsigned int addr; + unsigned int status; +} __attribute__((packed, aligned(8))) EmacRxTDescriptor, *PEmacRxTDescriptor; + +/// Describes the type and attribute of Transmit Transfer descriptor. +typedef struct _EmacTxTDescriptor { + unsigned int addr; + unsigned int status; +} __attribute__((packed, aligned(8))) EmacTxTDescriptor, *PEmacTxTDescriptor; +#ifdef __ICCARM__ // IAR +#pragma pack() // IAR +#endif // IAR + +#ifdef __ICCARM__ // IAR +#pragma data_alignment=8 // IAR +#endif // IAR +/// Descriptors for RX (required aligned by 8) +typedef struct { + volatile EmacRxTDescriptor td[RX_BUFFERS]; + EMAC_RxCallback rxCb; /// Callback function to be invoked once a frame has been received + unsigned short idx; +} RxTd; + +#ifdef __ICCARM__ // IAR +#pragma data_alignment=8 // IAR +#endif // IAR +/// Descriptors for TX (required aligned by 8) +typedef struct { + volatile EmacTxTDescriptor td[TX_BUFFERS]; + EMAC_TxCallback txCb[TX_BUFFERS]; /// Callback function to be invoked once TD has been processed + EMAC_WakeupCallback wakeupCb; /// Callback function to be invoked once several TD have been released + unsigned short wakeupThreshold; /// Number of free TD before wakeupCb is invoked + unsigned short head; /// Circular buffer head pointer incremented by the upper layer (buffer to be sent) + unsigned short tail; /// Circular buffer head pointer incremented by the IT handler (buffer sent) +} TxTd; + +//------------------------------------------------------------------------------ +// Internal variables +//------------------------------------------------------------------------------ +// Receive Transfer Descriptor buffer +static volatile RxTd rxTd; +// Transmit Transfer Descriptor buffer +static volatile TxTd txTd; +/// Send Buffer +// Section 3.6 of AMBA 2.0 spec states that burst should not cross 1K Boundaries. +// Receive buffer manager writes are burst of 2 words => 3 lsb bits of the address shall be set to 0 +#ifdef __ICCARM__ // IAR +#pragma data_alignment=8 // IAR +#endif // IAR +static volatile unsigned char pTxBuffer[TX_BUFFERS * EMAC_TX_UNITSIZE] __attribute__((aligned(8))); + +#ifdef __ICCARM__ // IAR +#pragma data_alignment=8 // IAR +#endif // IAR +/// Receive Buffer +static volatile unsigned char pRxBuffer[RX_BUFFERS * EMAC_RX_UNITSIZE] __attribute__((aligned(8))); +/// Statistics +static volatile EmacStats EmacStatistics; + +//----------------------------------------------------------------------------- +// Internal functions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// Wait PHY operation complete. +/// Return 1 if the operation completed successfully. +/// May be need to re-implemented to reduce CPU load. +/// \param retry: the retry times, 0 to wait forever until complete. +//----------------------------------------------------------------------------- +static unsigned char EMAC_WaitPhy( unsigned int retry ) +{ + unsigned int retry_count = 0; + + while((AT91C_BASE_EMAC->EMAC_NSR & AT91C_EMAC_IDLE) == 0) { + + // Dead LOOP! + if (retry == 0) { + + continue; + } + + // Timeout check + retry_count++; + if(retry_count >= retry) { + + TRACE_ERROR("E: Wait PHY time out\n\r"); + return 0; + } + } + + return 1; +} + +//----------------------------------------------------------------------------- +/// Disable TX & reset registers and descriptor list +//----------------------------------------------------------------------------- +static void EMAC_ResetTx(void) +{ + unsigned int Index; + unsigned int Address; + + // Disable TX + AT91C_BASE_EMAC->EMAC_NCR &= ~AT91C_EMAC_TE; + // Setup the TX descriptors. + CIRC_CLEAR(&txTd); + for(Index = 0; Index < TX_BUFFERS; Index++) { + + Address = (unsigned int)(&(pTxBuffer[Index * EMAC_TX_UNITSIZE])); + txTd.td[Index].addr = Address; + txTd.td[Index].status = EMAC_TX_USED_BIT; + } + txTd.td[TX_BUFFERS - 1].status = EMAC_TX_USED_BIT | EMAC_TX_WRAP_BIT; + // Transmit Buffer Queue Pointer Register + AT91C_BASE_EMAC->EMAC_TBQP = (unsigned int) (txTd.td); +} + +//----------------------------------------------------------------------------- +/// Disable RX & reset registers and descriptor list +//----------------------------------------------------------------------------- +static void EMAC_ResetRx(void) +{ + unsigned int Index; + unsigned int Address; + + // Disable RX + AT91C_BASE_EMAC->EMAC_NCR &= ~AT91C_EMAC_RE; + // Setup the RX descriptors. + rxTd.idx = 0; + for(Index = 0; Index < RX_BUFFERS; Index++) { + + Address = (unsigned int)(&(pRxBuffer[Index * EMAC_RX_UNITSIZE])); + // Remove EMAC_RX_OWNERSHIP_BIT and EMAC_RX_WRAP_BIT + rxTd.td[Index].addr = Address & EMAC_ADDRESS_MASK; + rxTd.td[Index].status = 0; + } + rxTd.td[RX_BUFFERS - 1].addr |= EMAC_RX_WRAP_BIT; + // Receive Buffer Queue Pointer Register + AT91C_BASE_EMAC->EMAC_RBQP = (unsigned int) (rxTd.td); +} + +//----------------------------------------------------------------------------- +// Exported functions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// PHY management functions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// Set MDC clock according to current board clock. Per 802.3, MDC should be +/// less then 2.5MHz. +/// Return 1 if successfully, 0 if MDC clock not found. +//----------------------------------------------------------------------------- +unsigned char EMAC_SetMdcClock( unsigned int mck ) +{ + int clock_dividor; + + if (mck <= 20000000) { + clock_dividor = AT91C_EMAC_CLK_HCLK_8; /// MDC clock = MCK/8 + } + else if (mck <= 40000000) { + clock_dividor = AT91C_EMAC_CLK_HCLK_16; /// MDC clock = MCK/16 + } + else if (mck <= 80000000) { + clock_dividor = AT91C_EMAC_CLK_HCLK_32; /// MDC clock = MCK/32 + } + else if (mck <= 160000000) { + clock_dividor = AT91C_EMAC_CLK_HCLK_64; /// MDC clock = MCK/64 + } + else { + TRACE_ERROR("E: No valid MDC clock.\n\r"); + return 0; + } + AT91C_BASE_EMAC->EMAC_NCFGR = (AT91C_BASE_EMAC->EMAC_NCFGR & (~AT91C_EMAC_CLK)) + | clock_dividor; + return 1; +} + +//----------------------------------------------------------------------------- +/// Enable MDI with PHY +//----------------------------------------------------------------------------- +void EMAC_EnableMdio( void ) +{ + AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_MPE; +} + +//----------------------------------------------------------------------------- +/// Enable MDI with PHY +//----------------------------------------------------------------------------- +void EMAC_DisableMdio( void ) +{ + AT91C_BASE_EMAC->EMAC_NCR &= ~AT91C_EMAC_MPE; +} + +//----------------------------------------------------------------------------- +/// Enable MII mode for EMAC, called once after autonegotiate +//----------------------------------------------------------------------------- +void EMAC_EnableMII( void ) +{ + AT91C_BASE_EMAC->EMAC_USRIO = AT91C_EMAC_CLKEN; +} + +//----------------------------------------------------------------------------- +/// Enable RMII mode for EMAC, called once after autonegotiate +//----------------------------------------------------------------------------- +void EMAC_EnableRMII( void ) +{ + AT91C_BASE_EMAC->EMAC_USRIO = AT91C_EMAC_CLKEN | AT91C_EMAC_RMII; +} + +//----------------------------------------------------------------------------- +/// Read PHY register. +/// Return 1 if successfully, 0 if timeout. +/// \param PhyAddress PHY Address +/// \param Address Register Address +/// \param pValue Pointer to a 32 bit location to store read data +/// \param retry The retry times, 0 to wait forever until complete. +//----------------------------------------------------------------------------- +unsigned char EMAC_ReadPhy(unsigned char PhyAddress, + unsigned char Address, + unsigned int *pValue, + unsigned int retry) +{ + AT91C_BASE_EMAC->EMAC_MAN = (AT91C_EMAC_SOF & (0x01 << 30)) + | (AT91C_EMAC_CODE & (2 << 16)) + | (AT91C_EMAC_RW & (2 << 28)) + | (AT91C_EMAC_PHYA & ((PhyAddress & 0x1f) << 23)) + | (AT91C_EMAC_REGA & (Address << 18)); + + if ( EMAC_WaitPhy(retry) == 0 ) { + + TRACE_ERROR("TimeOut EMAC_ReadPhy\n\r"); + return 0; + } + *pValue = ( AT91C_BASE_EMAC->EMAC_MAN & 0x0000ffff ); + return 1; +} + +//----------------------------------------------------------------------------- +/// Write PHY register +/// Return 1 if successfully, 0 if timeout. +/// \param PhyAddress PHY Address +/// \param Address Register Address +/// \param Value Data to write ( Actually 16 bit data ) +/// \param retry The retry times, 0 to wait forever until complete. +//----------------------------------------------------------------------------- +unsigned char EMAC_WritePhy(unsigned char PhyAddress, + unsigned char Address, + unsigned int Value, + unsigned int retry) +{ + AT91C_BASE_EMAC->EMAC_MAN = (AT91C_EMAC_SOF & (0x01 << 30)) + | (AT91C_EMAC_CODE & (2 << 16)) + | (AT91C_EMAC_RW & (1 << 28)) + | (AT91C_EMAC_PHYA & ((PhyAddress & 0x1f) << 23)) + | (AT91C_EMAC_REGA & (Address << 18)) + | (AT91C_EMAC_DATA & Value) ; + if ( EMAC_WaitPhy(retry) == 0 ) { + + TRACE_ERROR("TimeOut EMAC_WritePhy\n\r"); + return 0; + } + return 1; +} + +//----------------------------------------------------------------------------- +/// Setup the EMAC for the link : speed 100M/10M and Full/Half duplex +/// \param speed Link speed, 0 for 10M, 1 for 100M +/// \param fullduplex 1 for Full Duplex mode +//----------------------------------------------------------------------------- +void EMAC_SetLinkSpeed(unsigned char speed, unsigned char fullduplex) +{ + unsigned int ncfgr; + + ncfgr = AT91C_BASE_EMAC->EMAC_NCFGR; + ncfgr &= ~(AT91C_EMAC_SPD | AT91C_EMAC_FD); + if (speed) { + + ncfgr |= AT91C_EMAC_SPD; + } + if (fullduplex) { + + ncfgr |= AT91C_EMAC_FD; + } + AT91C_BASE_EMAC->EMAC_NCFGR = ncfgr; +} + + + +//----------------------------------------------------------------------------- +// EMAC functions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// EMAC Interrupt handler +//----------------------------------------------------------------------------- +void EMAC_Handler(void) +{ + volatile EmacTxTDescriptor *pTxTd; + volatile EMAC_TxCallback *pTxCb; + volatile unsigned int isr; + volatile unsigned int rsr; + volatile unsigned int tsr; + unsigned int rxStatusFlag; + unsigned int txStatusFlag; + + //TRACE_DEBUG("EMAC_Handler\n\r"); + isr = AT91C_BASE_EMAC->EMAC_ISR; + rsr = AT91C_BASE_EMAC->EMAC_RSR; + tsr = AT91C_BASE_EMAC->EMAC_TSR; + isr &= ~(AT91C_BASE_EMAC->EMAC_IMR | 0xFFC300); + + // RX packet + if ((isr & AT91C_EMAC_RCOMP) || (rsr & AT91C_EMAC_REC)) { + rxStatusFlag = AT91C_EMAC_REC; + + // Frame received + EmacStatistics.rx_packets++; + + // Check OVR + if (rsr & AT91C_EMAC_OVR) { + rxStatusFlag |= AT91C_EMAC_OVR; + EmacStatistics.rx_ovrs++; + } + // Check BNA + if (rsr & AT91C_EMAC_BNA) { + rxStatusFlag |= AT91C_EMAC_BNA; + EmacStatistics.rx_bnas++; + } + // Clear status + AT91C_BASE_EMAC->EMAC_RSR |= rxStatusFlag; + + // Invoke callbacks + if (rxTd.rxCb) { + rxTd.rxCb(rxStatusFlag); + } + } + + // TX packet + if ((isr & AT91C_EMAC_TCOMP) || (tsr & AT91C_EMAC_COMP)) { + + txStatusFlag = AT91C_EMAC_COMP; + EmacStatistics.tx_comp ++; + + // A frame transmitted + // Check RLE + if (tsr & AT91C_EMAC_RLES) { + // Status RLE & Number of discarded buffers + txStatusFlag = AT91C_EMAC_RLES + | CIRC_CNT(txTd.head, txTd.tail, TX_BUFFERS); + pTxCb = txTd.txCb + txTd.tail; + EMAC_ResetTx(); + TRACE_INFO("Tx RLE!!\n\r"); + AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_TE; + EmacStatistics.tx_errors++; + } + // Check COL + if (tsr & AT91C_EMAC_COL) { + txStatusFlag |= AT91C_EMAC_COL; + EmacStatistics.collisions++; + } + // Check BEX + if (tsr & AT91C_EMAC_BEX) { + txStatusFlag |= AT91C_EMAC_BEX; + EmacStatistics.tx_exausts++; + } + // Check UND + if (tsr & AT91C_EMAC_UND) { + txStatusFlag |= AT91C_EMAC_UND; + EmacStatistics.tx_underruns++; + } + // Clear status + AT91C_BASE_EMAC->EMAC_TSR |= txStatusFlag; + + if (!CIRC_EMPTY(&txTd)) + { + // Check the buffers + do { + pTxTd = txTd.td + txTd.tail; + pTxCb = txTd.txCb + txTd.tail; + // Any error? + // Exit if buffer has not been sent yet + if ((pTxTd->status & EMAC_TX_USED_BIT) == 0) { + break; + } + + // Notify upper layer that a packet has been sent + if (*pTxCb) { + (*pTxCb)(txStatusFlag); + } + + CIRC_INC( txTd.tail, TX_BUFFERS ); + } while (CIRC_CNT(txTd.head, txTd.tail, TX_BUFFERS)); + } + + if (tsr & AT91C_EMAC_RLES) { + // 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( (CIRC_SPACE(txTd.head, txTd.tail, TX_BUFFERS) >= + txTd.wakeupThreshold) + && txTd.wakeupCb) { + txTd.wakeupCb(); + } + } + + // PAUSE Frame + if (isr & AT91C_EMAC_PFRE) { + TRACE_INFO("Pause!\n\r"); + } + if (isr & AT91C_EMAC_PTZ) { + TRACE_INFO("Pause TO!\n\r"); + } +} + +//----------------------------------------------------------------------------- +/// Initialize the EMAC with the emac controller address +/// \param id HW ID for power management +/// \param pTxWakeUpfct Thresold TX Wakeup Callback +/// \param pRxfct RX Wakeup Callback +/// \param pMacAddress Mac Address +/// \param enableCAF enable AT91C_EMAC_CAF if needed by application +/// \param enableNBC AT91C_EMAC_NBC if needed by application +//----------------------------------------------------------------------------- +void EMAC_Init( unsigned char id, const unsigned char *pMacAddress, + unsigned char enableCAF, unsigned char enableNBC ) +{ + + // Check parameters + ASSERT(RX_BUFFERS * EMAC_RX_UNITSIZE > EMAC_FRAME_LENTGH_MAX, + "E: RX buffers too small\n\r"); + + TRACE_DEBUG("EMAC_Init\n\r"); + + // Power ON + AT91C_BASE_PMC->PMC_PCER = 1 << id; + + // Disable TX & RX and more + AT91C_BASE_EMAC->EMAC_NCR = 0; + + // disable + AT91C_BASE_EMAC->EMAC_IDR = ~0; + + EMAC_ResetRx(); + EMAC_ResetTx(); + + // Set the MAC address + if( pMacAddress != (unsigned char *)0 ) { + AT91C_BASE_EMAC->EMAC_SA1L = ( ((unsigned int)pMacAddress[3] << 24) + | ((unsigned int)pMacAddress[2] << 16) + | ((unsigned int)pMacAddress[1] << 8 ) + | pMacAddress[0] ); + + AT91C_BASE_EMAC->EMAC_SA1H = ( ((unsigned int)pMacAddress[5] << 8 ) + | pMacAddress[4] ); + } + + AT91C_BASE_EMAC->EMAC_NCR = AT91C_EMAC_CLRSTAT; + + // Clear all status bits in the receive status register. + AT91C_BASE_EMAC->EMAC_RSR = (AT91C_EMAC_OVR | AT91C_EMAC_REC | AT91C_EMAC_BNA); + + // Clear all status bits in the transmit status register + AT91C_BASE_EMAC->EMAC_TSR = ( AT91C_EMAC_UBR | AT91C_EMAC_COL | AT91C_EMAC_RLES + | AT91C_EMAC_BEX | AT91C_EMAC_COMP + | AT91C_EMAC_UND ); + + // Clear interrupts + AT91C_BASE_EMAC->EMAC_ISR; + + // Enable the copy of data into the buffers + // ignore broadcasts, and don't copy FCS. + AT91C_BASE_EMAC->EMAC_NCFGR |= (AT91C_EMAC_DRFCS | AT91C_EMAC_PAE); + + if( enableCAF == EMAC_CAF_ENABLE ) { + AT91C_BASE_EMAC->EMAC_NCFGR |= AT91C_EMAC_CAF; + } + if( enableNBC == EMAC_NBC_ENABLE ) { + AT91C_BASE_EMAC->EMAC_NCFGR |= AT91C_EMAC_NBC; + } + + // Enable Rx and Tx, plus the stats register. + AT91C_BASE_EMAC->EMAC_NCR |= (AT91C_EMAC_TE | AT91C_EMAC_RE | AT91C_EMAC_WESTAT); + + // Setup the interrupts for TX (and errors) + AT91C_BASE_EMAC->EMAC_IER = AT91C_EMAC_RXUBR + | AT91C_EMAC_TUNDR + | AT91C_EMAC_RLEX + | AT91C_EMAC_TXERR + | AT91C_EMAC_TCOMP + | AT91C_EMAC_ROVR + | AT91C_EMAC_HRESP + | AT91C_EMAC_PFRE + | AT91C_EMAC_PTZ + ; + +} + +//----------------------------------------------------------------------------- +/// Reset TX & RX queue & statistics +//----------------------------------------------------------------------------- +void EMAC_Reset(void) +{ + EMAC_ResetRx(); + EMAC_ResetTx(); + memset((void*)&EmacStatistics, 0x00, sizeof(EmacStats)); + AT91C_BASE_EMAC->EMAC_NCR |= (AT91C_EMAC_TE + | AT91C_EMAC_RE + | AT91C_EMAC_WESTAT + | AT91C_EMAC_CLRSTAT); + +} + +//----------------------------------------------------------------------------- +/// Get the statstic information & reset it +/// \param pStats Pointer to EmacStats structure to copy the informations +/// \param reset Reset the statistics after copy it +//----------------------------------------------------------------------------- +void EMAC_GetStatistics(EmacStats *pStats, unsigned char reset) +{ + unsigned int ncrBackup = 0; + + TRACE_DEBUG("EMAC_GetStatistics\n\r"); + + // Sanity check + if (pStats == (EmacStats *) 0) { + return; + } + + ncrBackup = AT91C_BASE_EMAC->EMAC_NCR & (AT91C_EMAC_TE | AT91C_EMAC_RE); + + + // Copy the informations + memcpy(pStats, (void*)&EmacStatistics, sizeof(EmacStats)); + + // Reset the statistics + if (reset) { + memset((void*)&EmacStatistics, 0x00, sizeof(EmacStats)); + AT91C_BASE_EMAC->EMAC_NCR = ncrBackup | AT91C_EMAC_CLRSTAT; + } +} + +//----------------------------------------------------------------------------- +/// Send a packet with EMAC. +/// If the packet size is larger than transfer buffer size error returned. +/// If packet transfer status is monitored, specify callback for each packet. +/// \param buffer The buffer to be send +/// \param size The size of buffer to be send +/// \param fEMAC_TxCallback Threshold Wakeup callback +/// \param fWakeUpCb TX Wakeup +/// \return OK, Busy or invalid packet +//----------------------------------------------------------------------------- +unsigned char EMAC_Send(void *pBuffer, + unsigned int size, + EMAC_TxCallback fEMAC_TxCallback) +{ + volatile EmacTxTDescriptor *pTxTd; + volatile EMAC_TxCallback *pTxCb; + + //TRACE_DEBUG("EMAC_Send\n\r"); + + // Check parameter + if (size > EMAC_TX_UNITSIZE) { + + TRACE_ERROR("EMAC driver does not split send packets."); + TRACE_ERROR("%d bytes max in one packet (%d bytes requested)\n\r", + EMAC_TX_UNITSIZE, size); + return EMAC_TX_INVALID_PACKET; + } + + // Pointers to the current TxTd + pTxTd = txTd.td + txTd.head; + + // If no free TxTd, buffer can't be sent, schedule the wakeup callback + if( CIRC_SPACE(txTd.head, txTd.tail, TX_BUFFERS) == 0) { + if ((pTxTd->status & EMAC_TX_USED_BIT) != 0) { + //EMAC_ResetTx(); + //TRACE_WARNING("Circ Full but FREE TD found\n\r"); + //AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_TE; + } + //else { + return EMAC_TX_BUFFER_BUSY; + //} + } + + // Pointers to the current TxTd + pTxCb = txTd.txCb + txTd.head; + + // Sanity check + + // Setup/Copy data to transmition buffer + if (pBuffer && size) { + // Driver manage the ring buffer + memcpy((void *)pTxTd->addr, pBuffer, size); + } + + // Tx Callback + *pTxCb = fEMAC_TxCallback; + + // Update TD status + // The buffer size defined is length of ethernet frame + // so it's always the last buffer of the frame. + if (txTd.head == TX_BUFFERS-1) { + pTxTd->status = + (size & EMAC_LENGTH_FRAME) | EMAC_TX_LAST_BUFFER_BIT | EMAC_TX_WRAP_BIT; + } + else { + pTxTd->status = (size & EMAC_LENGTH_FRAME) | EMAC_TX_LAST_BUFFER_BIT; + } + + CIRC_INC(txTd.head, TX_BUFFERS) + + // Tx packets count + EmacStatistics.tx_packets++; + + // Now start to transmit if it is not already done + AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_TSTART; + + return EMAC_TX_OK; +} + +//----------------------------------------------------------------------------- +/// Return current load of TX. +//----------------------------------------------------------------------------- +unsigned int EMAC_TxLoad(void) +{ + unsigned short head = txTd.head; + unsigned short tail = txTd.tail; + #if 1 + return CIRC_CNT(head, tail, TX_BUFFERS); + #else + return (TX_BUFFERS - CIRC_SPACE(head, tail, TX_BUFFERS)); + #endif +} + +//----------------------------------------------------------------------------- +/// Receive a packet with EMAC +/// If not enough buffer for the packet, the remaining data is lost but right +/// frame length is returned. +/// \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 +//----------------------------------------------------------------------------- +unsigned char EMAC_Poll(unsigned char *pFrame, + unsigned int frameSize, + unsigned int *pRcvSize) +{ + unsigned short bufferLength; + unsigned int tmpFrameSize=0; + unsigned char *pTmpFrame=0; + unsigned int tmpIdx = rxTd.idx; + volatile EmacRxTDescriptor *pRxTd = rxTd.td + rxTd.idx; + + ASSERT(pFrame, "F: EMAC_Poll\n\r"); + + char isFrame = 0; + // Set the default return value + *pRcvSize = 0; + + // Process received RxTd + while ((pRxTd->addr & EMAC_RX_OWNERSHIP_BIT) == EMAC_RX_OWNERSHIP_BIT) { + + // A start of frame has been received, discard previous fragments + if ((pRxTd->status & EMAC_RX_SOF_BIT) == EMAC_RX_SOF_BIT) { + // Skip previous fragment + while (tmpIdx != rxTd.idx) { + pRxTd = rxTd.td + rxTd.idx; + pRxTd->addr &= ~(EMAC_RX_OWNERSHIP_BIT); + CIRC_INC(rxTd.idx, RX_BUFFERS); + } + // Reset the temporary frame pointer + pTmpFrame = pFrame; + tmpFrameSize = 0; + // Start to gather buffers in a frame + isFrame = 1; + } + + // Increment the pointer + CIRC_INC(tmpIdx, RX_BUFFERS); + + // Copy data in the frame buffer + if (isFrame) { + if (tmpIdx == rxTd.idx) { + TRACE_INFO("no EOF (Invalid of buffers too small)\n\r"); + + do { + + pRxTd = rxTd.td + rxTd.idx; + pRxTd->addr &= ~(EMAC_RX_OWNERSHIP_BIT); + CIRC_INC(rxTd.idx, RX_BUFFERS); + } while(tmpIdx != rxTd.idx); + return EMAC_RX_NO_DATA; + } + // Copy the buffer into the application frame + bufferLength = EMAC_RX_UNITSIZE; + if ((tmpFrameSize + bufferLength) > frameSize) { + bufferLength = frameSize - tmpFrameSize; + } + + memcpy(pTmpFrame, (void*)(pRxTd->addr & EMAC_ADDRESS_MASK), bufferLength); + pTmpFrame += bufferLength; + tmpFrameSize += bufferLength; + + // An end of frame has been received, return the data + if ((pRxTd->status & EMAC_RX_EOF_BIT) == EMAC_RX_EOF_BIT) { + // Frame size from the EMAC + *pRcvSize = (pRxTd->status & EMAC_LENGTH_FRAME); + + // Application frame buffer is too small all data have not been copied + if (tmpFrameSize < *pRcvSize) { + printf("size req %d size allocated %d\n\r", *pRcvSize, frameSize); + + return EMAC_RX_FRAME_SIZE_TOO_SMALL; + } + + TRACE_DEBUG("packet %d-%d (%d)\n\r", rxTd.idx, tmpIdx, *pRcvSize); + // All data have been copied in the application frame buffer => release TD + while (rxTd.idx != tmpIdx) { + pRxTd = rxTd.td + rxTd.idx; + pRxTd->addr &= ~(EMAC_RX_OWNERSHIP_BIT); + CIRC_INC(rxTd.idx, RX_BUFFERS); + } + EmacStatistics.rx_packets++; + return EMAC_RX_OK; + } + } + + // SOF has not been detected, skip the fragment + else { + pRxTd->addr &= ~(EMAC_RX_OWNERSHIP_BIT); + rxTd.idx = tmpIdx; + } + + // Process the next buffer + pRxTd = rxTd.td + tmpIdx; + } + + //TRACE_DEBUG("E"); + return EMAC_RX_NO_DATA; +} + +//----------------------------------------------------------------------------- +/// Registers pRxCb callback. Callback will be invoked after the next received +/// frame. +/// When EMAC_Poll() returns EMAC_RX_NO_DATA the application task call EMAC_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 EMAC_Poll() +/// is called, it will be successfull. +/// \param pRxCb Pointer to callback function +//----------------------------------------------------------------------------- +void EMAC_Set_RxCb(EMAC_RxCallback pRxCb) +{ + rxTd.rxCb = pRxCb; + AT91C_BASE_EMAC->EMAC_IER = AT91C_EMAC_RCOMP; +} + +//----------------------------------------------------------------------------- +/// Remove the RX callback function. +/// This function is usually invoked from the RX callback itself. Once the callback +/// has resumed the application task, there is no need to invoke the callback again. +//----------------------------------------------------------------------------- +void EMAC_Clear_RxCb(void) +{ + AT91C_BASE_EMAC->EMAC_IDR = AT91C_EMAC_RCOMP; + rxTd.rxCb = (EMAC_RxCallback) 0; +} + +//----------------------------------------------------------------------------- +/// Registers TX wakeup callback callback. Callback will be invoked once several +/// transfer descriptors are available. +/// When EMAC_Send() returns EMAC_TX_BUFFER_BUSY (all TD busy) the application +/// task calls EMAC_Set_TxWakeUpCb() to register pTxWakeUpCb() callback and +/// enters suspend state. The callback is in charge to resume the task once +/// several TD have been released. The next time EMAC_Send() will be called, it +/// shall be successfull. +/// \param pTxWakeUpCb Pointer to callback function +/// \param threshold Minimum number of available transfer descriptors before pTxWakeUpCb() is invoked +/// \return 0= success, 1 = threshold exceeds nuber of transfer descriptors +//----------------------------------------------------------------------------- +char EMAC_Set_TxWakeUpCb(EMAC_WakeupCallback pTxWakeUpCb, unsigned short threshold) +{ + if (threshold <= TX_BUFFERS) { + txTd.wakeupCb = pTxWakeUpCb; + txTd.wakeupThreshold = threshold; + return 0; + } + return 1; +} + +//----------------------------------------------------------------------------- +/// Remove the TX wakeup callback function. +/// This function is usually invoked from the TX wakeup callback itself. Once the callback +/// has resumed the application task, there is no need to invoke the callback again. +//----------------------------------------------------------------------------- +void EMAC_Clear_TxWakeUpCb(void) +{ + txTd.wakeupCb = (EMAC_WakeupCallback) 0; +} + + -- cgit v1.2.3