| /*This file has been prepared for Doxygen automatic documentation generation.*/ | |
| /*! \file ********************************************************************* | |
| * | |
| * \brief MACB driver for EVK1100 board. | |
| * | |
| * This file defines a useful set of functions for the MACB interface on | |
| * AVR32 devices. | |
| * | |
| * - Compiler: IAR EWAVR32 and GNU GCC for AVR32 | |
| * - Supported devices: All AVR32 devices with a MACB module can be used. | |
| * - AppNote: | |
| * | |
| * \author Atmel Corporation: http://www.atmel.com \n | |
| * Support and FAQ: http://support.atmel.no/ | |
| * | |
| *****************************************************************************/ | |
| /* Copyright (c) 2007, 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: | |
| * | |
| * 1. Redistributions of source code must retain the above copyright notice, | |
| * this list of conditions and the following disclaimer. | |
| * | |
| * 2. Redistributions in binary form must reproduce the above copyright notice, | |
| * this list of conditions and the following disclaimer in the documentation | |
| * and/or other materials provided with the distribution. | |
| * | |
| * 3. The name of ATMEL may not be used to endorse or promote products derived | |
| * from this software without specific prior written permission. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY ATMEL ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY AND | |
| * SPECIFICALLY 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. | |
| */ | |
| #include <stdio.h> | |
| #include <string.h> | |
| #include <avr32/io.h> | |
| #ifdef FREERTOS_USED | |
| #include "FreeRTOS.h" | |
| #include "task.h" | |
| #include "semphr.h" | |
| #endif | |
| #include "macb.h" | |
| #include "gpio.h" | |
| #include "conf_eth.h" | |
| #include "intc.h" | |
| /* Size of each receive buffer - DO NOT CHANGE. */ | |
| #define RX_BUFFER_SIZE 128 | |
| /* The buffer addresses written into the descriptors must be aligned so the | |
| last few bits are zero. These bits have special meaning for the MACB | |
| peripheral and cannot be used as part of the address. */ | |
| #define ADDRESS_MASK ( ( unsigned long ) 0xFFFFFFFC ) | |
| /* Bit used within the address stored in the descriptor to mark the last | |
| descriptor in the array. */ | |
| #define RX_WRAP_BIT ( ( unsigned long ) 0x02 ) | |
| /* A short delay is used to wait for a buffer to become available, should | |
| one not be immediately available when trying to transmit a frame. */ | |
| #define BUFFER_WAIT_DELAY ( 2 ) | |
| #ifndef FREERTOS_USED | |
| #define portENTER_CRITICAL Disable_global_interrupt | |
| #define portEXIT_CRITICAL Enable_global_interrupt | |
| #define portENTER_SWITCHING_ISR() | |
| #define portEXIT_SWITCHING_ISR() | |
| #endif | |
| /* Buffer written to by the MACB DMA. Must be aligned as described by the | |
| comment above the ADDRESS_MASK definition. */ | |
| #if __GNUC__ | |
| static volatile char pcRxBuffer[ ETHERNET_CONF_NB_RX_BUFFERS * RX_BUFFER_SIZE ] __attribute__ ((aligned (8))); | |
| #elif __ICCAVR32__ | |
| #pragma data_alignment=8 | |
| static volatile char pcRxBuffer[ ETHERNET_CONF_NB_RX_BUFFERS * RX_BUFFER_SIZE ]; | |
| #endif | |
| /* Buffer read by the MACB DMA. Must be aligned as described by the comment | |
| above the ADDRESS_MASK definition. */ | |
| #if __GNUC__ | |
| static volatile char pcTxBuffer[ ETHERNET_CONF_NB_TX_BUFFERS * ETHERNET_CONF_TX_BUFFER_SIZE ] __attribute__ ((aligned (8))); | |
| #elif __ICCAVR32__ | |
| #pragma data_alignment=8 | |
| static volatile char pcTxBuffer[ ETHERNET_CONF_NB_TX_BUFFERS * ETHERNET_CONF_TX_BUFFER_SIZE ]; | |
| #endif | |
| /* Descriptors used to communicate between the program and the MACB peripheral. | |
| These descriptors hold the locations and state of the Rx and Tx buffers. */ | |
| static volatile AVR32_TxTdDescriptor xTxDescriptors[ ETHERNET_CONF_NB_TX_BUFFERS ]; | |
| static volatile AVR32_RxTdDescriptor xRxDescriptors[ ETHERNET_CONF_NB_RX_BUFFERS ]; | |
| /* The IP and Ethernet addresses are read from the header files. */ | |
| char cMACAddress[ 6 ] = { ETHERNET_CONF_ETHADDR0,ETHERNET_CONF_ETHADDR1,ETHERNET_CONF_ETHADDR2,ETHERNET_CONF_ETHADDR3,ETHERNET_CONF_ETHADDR4,ETHERNET_CONF_ETHADDR5 }; | |
| /*-----------------------------------------------------------*/ | |
| /* See the header file for descriptions of public functions. */ | |
| /* | |
| * Prototype for the MACB interrupt function - called by the asm wrapper. | |
| */ | |
| #ifdef FREERTOS_USED | |
| #if __GNUC__ | |
| __attribute__((naked)) | |
| #elif __ICCAVR32__ | |
| #pragma shadow_registers = full // Naked. | |
| #endif | |
| #else | |
| #if __GNUC__ | |
| __attribute__((__interrupt__)) | |
| #elif __ICCAVR32__ | |
| __interrupt | |
| #endif | |
| #endif | |
| void vMACB_ISR( void ); | |
| static long prvMACB_ISR_NonNakedBehaviour( void ); | |
| #if ETHERNET_CONF_USE_PHY_IT | |
| #ifdef FREERTOS_USED | |
| #if __GNUC__ | |
| __attribute__((naked)) | |
| #elif __ICCAVR32__ | |
| #pragma shadow_registers = full // Naked. | |
| #endif | |
| #else | |
| #if __GNUC__ | |
| __attribute__((__interrupt__)) | |
| #elif __ICCAVR32__ | |
| __interrupt | |
| #endif | |
| #endif | |
| void vPHY_ISR( void ); | |
| static long prvPHY_ISR_NonNakedBehaviour( void ); | |
| #endif | |
| /* | |
| * Initialise both the Tx and Rx descriptors used by the MACB. | |
| */ | |
| static void prvSetupDescriptors(volatile avr32_macb_t * macb); | |
| /* | |
| * Write our MAC address into the MACB. | |
| */ | |
| static void prvSetupMACAddress( volatile avr32_macb_t * macb ); | |
| /* | |
| * Configure the MACB for interrupts. | |
| */ | |
| static void prvSetupMACBInterrupt( volatile avr32_macb_t * macb ); | |
| /* | |
| * Some initialisation functions. | |
| */ | |
| static Bool prvProbePHY( volatile avr32_macb_t * macb ); | |
| static unsigned long ulReadMDIO(volatile avr32_macb_t * macb, unsigned short usAddress); | |
| static void vWriteMDIO(volatile avr32_macb_t * macb, unsigned short usAddress, unsigned short usValue); | |
| #ifdef FREERTOS_USED | |
| /* The semaphore used by the MACB ISR to wake the MACB task. */ | |
| static SemaphoreHandle_t xSemaphore = NULL; | |
| #else | |
| static volatile Bool DataToRead = FALSE; | |
| #endif | |
| /* Holds the index to the next buffer from which data will be read. */ | |
| volatile unsigned long ulNextRxBuffer = 0; | |
| long lMACBSend(volatile avr32_macb_t * macb, char *pcFrom, unsigned long ulLength, long lEndOfFrame ) | |
| { | |
| static unsigned long uxTxBufferIndex = 0; | |
| char *pcBuffer; | |
| unsigned long ulLastBuffer, ulDataBuffered = 0, ulDataRemainingToSend, ulLengthToSend; | |
| /* If the length of data to be transmitted is greater than each individual | |
| transmit buffer then the data will be split into more than one buffer. | |
| Loop until the entire length has been buffered. */ | |
| while( ulDataBuffered < ulLength ) | |
| { | |
| // Is a buffer available ? | |
| while( !( xTxDescriptors[ uxTxBufferIndex ].U_Status.status & AVR32_TRANSMIT_OK ) ) | |
| { | |
| // There is no room to write the Tx data to the Tx buffer. | |
| // Wait a short while, then try again. | |
| #ifdef FREERTOS_USED | |
| vTaskDelay( BUFFER_WAIT_DELAY ); | |
| #else | |
| __asm__ __volatile__ ("nop"); | |
| #endif | |
| } | |
| portENTER_CRITICAL(); | |
| { | |
| // Get the address of the buffer from the descriptor, | |
| // then copy the data into the buffer. | |
| pcBuffer = ( char * ) xTxDescriptors[ uxTxBufferIndex ].addr; | |
| // How much can we write to the buffer ? | |
| ulDataRemainingToSend = ulLength - ulDataBuffered; | |
| if( ulDataRemainingToSend <= ETHERNET_CONF_TX_BUFFER_SIZE ) | |
| { | |
| // We can write all the remaining bytes. | |
| ulLengthToSend = ulDataRemainingToSend; | |
| } | |
| else | |
| { | |
| // We can't write more than ETH_TX_BUFFER_SIZE in one go. | |
| ulLengthToSend = ETHERNET_CONF_TX_BUFFER_SIZE; | |
| } | |
| // Copy the data into the buffer. | |
| memcpy( ( void * ) pcBuffer, ( void * ) &( pcFrom[ ulDataBuffered ] ), ulLengthToSend ); | |
| ulDataBuffered += ulLengthToSend; | |
| // Is this the last data for the frame ? | |
| if( lEndOfFrame && ( ulDataBuffered >= ulLength ) ) | |
| { | |
| // No more data remains for this frame so we can start the transmission. | |
| ulLastBuffer = AVR32_LAST_BUFFER; | |
| } | |
| else | |
| { | |
| // More data to come for this frame. | |
| ulLastBuffer = 0; | |
| } | |
| // Fill out the necessary in the descriptor to get the data sent, | |
| // then move to the next descriptor, wrapping if necessary. | |
| if( uxTxBufferIndex >= ( ETHERNET_CONF_NB_TX_BUFFERS - 1 ) ) | |
| { | |
| xTxDescriptors[ uxTxBufferIndex ].U_Status.status = ( ulLengthToSend & ( unsigned long ) AVR32_LENGTH_FRAME ) | |
| | ulLastBuffer | |
| | AVR32_TRANSMIT_WRAP; | |
| uxTxBufferIndex = 0; | |
| } | |
| else | |
| { | |
| xTxDescriptors[ uxTxBufferIndex ].U_Status.status = ( ulLengthToSend & ( unsigned long ) AVR32_LENGTH_FRAME ) | |
| | ulLastBuffer; | |
| uxTxBufferIndex++; | |
| } | |
| /* If this is the last buffer to be sent for this frame we can | |
| start the transmission. */ | |
| if( ulLastBuffer ) | |
| { | |
| macb->ncr |= AVR32_MACB_TSTART_MASK; | |
| } | |
| } | |
| portEXIT_CRITICAL(); | |
| } | |
| return PASS; | |
| } | |
| unsigned long ulMACBInputLength( void ) | |
| { | |
| register unsigned long ulIndex , ulLength = 0; | |
| unsigned int uiTemp; | |
| // Skip any fragments. We are looking for the first buffer that contains | |
| // data and has the SOF (start of frame) bit set. | |
| while( ( xRxDescriptors[ ulNextRxBuffer ].addr & AVR32_OWNERSHIP_BIT ) && !( xRxDescriptors[ ulNextRxBuffer ].U_Status.status & AVR32_SOF ) ) | |
| { | |
| // Ignoring this buffer. Mark it as free again. | |
| uiTemp = xRxDescriptors[ ulNextRxBuffer ].addr; | |
| xRxDescriptors[ ulNextRxBuffer ].addr = uiTemp & ~( AVR32_OWNERSHIP_BIT ); | |
| ulNextRxBuffer++; | |
| if( ulNextRxBuffer >= ETHERNET_CONF_NB_RX_BUFFERS ) | |
| { | |
| ulNextRxBuffer = 0; | |
| } | |
| } | |
| // We are going to walk through the descriptors that make up this frame, | |
| // but don't want to alter ulNextRxBuffer as this would prevent vMACBRead() | |
| // from finding the data. Therefore use a copy of ulNextRxBuffer instead. | |
| ulIndex = ulNextRxBuffer; | |
| // Walk through the descriptors until we find the last buffer for this frame. | |
| // The last buffer will give us the length of the entire frame. | |
| while( ( xRxDescriptors[ ulIndex ].addr & AVR32_OWNERSHIP_BIT ) && !ulLength ) | |
| { | |
| ulLength = xRxDescriptors[ ulIndex ].U_Status.status & AVR32_LENGTH_FRAME; | |
| // Increment to the next buffer, wrapping if necessary. | |
| ulIndex++; | |
| if( ulIndex >= ETHERNET_CONF_NB_RX_BUFFERS ) | |
| { | |
| ulIndex = 0; | |
| } | |
| } | |
| return ulLength; | |
| } | |
| /*-----------------------------------------------------------*/ | |
| void vMACBRead( char *pcTo, unsigned long ulSectionLength, unsigned long ulTotalFrameLength ) | |
| { | |
| static unsigned long ulSectionBytesReadSoFar = 0, ulBufferPosition = 0, ulFameBytesReadSoFar = 0; | |
| static char *pcSource; | |
| register unsigned long ulBytesRemainingInBuffer, ulRemainingSectionBytes; | |
| unsigned int uiTemp; | |
| // Read ulSectionLength bytes from the Rx buffers. | |
| // This is not necessarily any correspondence between the length of our Rx buffers, | |
| // and the length of the data we are returning or the length of the data being requested. | |
| // Therefore, between calls we have to remember not only which buffer we are currently | |
| // processing, but our position within that buffer. | |
| // This would be greatly simplified if PBUF_POOL_BUFSIZE could be guaranteed to be greater | |
| // than the size of each Rx buffer, and that memory fragmentation did not occur. | |
| // This function should only be called after a call to ulMACBInputLength(). | |
| // This will ensure ulNextRxBuffer is set to the correct buffer. */ | |
| // vMACBRead is called with pcTo set to NULL to indicate that we are about | |
| // to read a new frame. Any fragments remaining in the frame we were | |
| // processing during the last call should be dropped. | |
| if( pcTo == NULL ) | |
| { | |
| // How many bytes are indicated as being in this buffer? | |
| // If none then the buffer is completely full and the frame is contained within more | |
| // than one buffer. | |
| // Reset our state variables ready for the next read from this buffer. | |
| pcSource = ( char * )( xRxDescriptors[ ulNextRxBuffer ].addr & ADDRESS_MASK ); | |
| ulFameBytesReadSoFar = ( unsigned long ) 0; | |
| ulBufferPosition = ( unsigned long ) 0; | |
| } | |
| else | |
| { | |
| // Loop until we have obtained the required amount of data. | |
| ulSectionBytesReadSoFar = 0; | |
| while( ulSectionBytesReadSoFar < ulSectionLength ) | |
| { | |
| // We may have already read some data from this buffer. | |
| // How much data remains in the buffer? | |
| ulBytesRemainingInBuffer = ( RX_BUFFER_SIZE - ulBufferPosition ); | |
| // How many more bytes do we need to read before we have the | |
| // required amount of data? | |
| ulRemainingSectionBytes = ulSectionLength - ulSectionBytesReadSoFar; | |
| // Do we want more data than remains in the buffer? | |
| if( ulRemainingSectionBytes > ulBytesRemainingInBuffer ) | |
| { | |
| // We want more data than remains in the buffer so we can | |
| // write the remains of the buffer to the destination, then move | |
| // onto the next buffer to get the rest. | |
| memcpy( &( pcTo[ ulSectionBytesReadSoFar ] ), &( pcSource[ ulBufferPosition ] ), ulBytesRemainingInBuffer ); | |
| ulSectionBytesReadSoFar += ulBytesRemainingInBuffer; | |
| ulFameBytesReadSoFar += ulBytesRemainingInBuffer; | |
| // Mark the buffer as free again. | |
| uiTemp = xRxDescriptors[ ulNextRxBuffer ].addr; | |
| xRxDescriptors[ ulNextRxBuffer ].addr = uiTemp & ~( AVR32_OWNERSHIP_BIT ); | |
| // Move onto the next buffer. | |
| ulNextRxBuffer++; | |
| if( ulNextRxBuffer >= ETHERNET_CONF_NB_RX_BUFFERS ) | |
| { | |
| ulNextRxBuffer = ( unsigned long ) 0; | |
| } | |
| // Reset the variables for the new buffer. | |
| pcSource = ( char * )( xRxDescriptors[ ulNextRxBuffer ].addr & ADDRESS_MASK ); | |
| ulBufferPosition = ( unsigned long ) 0; | |
| } | |
| else | |
| { | |
| // We have enough data in this buffer to send back. | |
| // Read out enough data and remember how far we read up to. | |
| memcpy( &( pcTo[ ulSectionBytesReadSoFar ] ), &( pcSource[ ulBufferPosition ] ), ulRemainingSectionBytes ); | |
| // There may be more data in this buffer yet. | |
| // Increment our position in this buffer past the data we have just read. | |
| ulBufferPosition += ulRemainingSectionBytes; | |
| ulSectionBytesReadSoFar += ulRemainingSectionBytes; | |
| ulFameBytesReadSoFar += ulRemainingSectionBytes; | |
| // Have we now finished with this buffer? | |
| if( ( ulBufferPosition >= RX_BUFFER_SIZE ) || ( ulFameBytesReadSoFar >= ulTotalFrameLength ) ) | |
| { | |
| // Mark the buffer as free again. | |
| uiTemp = xRxDescriptors[ ulNextRxBuffer ].addr; | |
| xRxDescriptors[ ulNextRxBuffer ].addr = uiTemp & ~( AVR32_OWNERSHIP_BIT ); | |
| // Move onto the next buffer. | |
| ulNextRxBuffer++; | |
| if( ulNextRxBuffer >= ETHERNET_CONF_NB_RX_BUFFERS ) | |
| { | |
| ulNextRxBuffer = 0; | |
| } | |
| pcSource = ( char * )( xRxDescriptors[ ulNextRxBuffer ].addr & ADDRESS_MASK ); | |
| ulBufferPosition = 0; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| /*-----------------------------------------------------------*/ | |
| void vMACBSetMACAddress(const char * MACAddress) | |
| { | |
| memcpy(cMACAddress, MACAddress, sizeof(cMACAddress)); | |
| } | |
| Bool xMACBInit( volatile avr32_macb_t * macb ) | |
| { | |
| volatile unsigned long status; | |
| // set up registers | |
| macb->ncr = 0; | |
| macb->tsr = ~0UL; | |
| macb->rsr = ~0UL; | |
| macb->idr = ~0UL; | |
| status = macb->isr; | |
| #if ETHERNET_CONF_USE_RMII_INTERFACE | |
| // RMII used, set 0 to the USRIO Register | |
| macb->usrio &= ~AVR32_MACB_RMII_MASK; | |
| #else | |
| // RMII not used, set 1 to the USRIO Register | |
| macb->usrio |= AVR32_MACB_RMII_MASK; | |
| #endif | |
| // Load our MAC address into the MACB. | |
| prvSetupMACAddress(macb); | |
| // Setup the buffers and descriptors. | |
| prvSetupDescriptors(macb); | |
| #if ETHERNET_CONF_SYSTEM_CLOCK <= 20000000 | |
| macb->ncfgr |= (AVR32_MACB_NCFGR_CLK_DIV8 << AVR32_MACB_NCFGR_CLK_OFFSET); | |
| #elif ETHERNET_CONF_SYSTEM_CLOCK <= 40000000 | |
| macb->ncfgr |= (AVR32_MACB_NCFGR_CLK_DIV16 << AVR32_MACB_NCFGR_CLK_OFFSET); | |
| #elif ETHERNET_CONF_SYSTEM_CLOCK <= 80000000 | |
| macb->ncfgr |= AVR32_MACB_NCFGR_CLK_DIV32 << AVR32_MACB_NCFGR_CLK_OFFSET; | |
| #elif ETHERNET_CONF_SYSTEM_CLOCK <= 160000000 | |
| macb->ncfgr |= AVR32_MACB_NCFGR_CLK_DIV64 << AVR32_MACB_NCFGR_CLK_OFFSET; | |
| #else | |
| # error System clock too fast | |
| #endif | |
| // Are we connected? | |
| if( prvProbePHY(macb) == TRUE ) | |
| { | |
| // Enable the interrupt! | |
| portENTER_CRITICAL(); | |
| { | |
| prvSetupMACBInterrupt(macb); | |
| } | |
| portEXIT_CRITICAL(); | |
| // Enable Rx and Tx, plus the stats register. | |
| macb->ncr = AVR32_MACB_NCR_TE_MASK | AVR32_MACB_NCR_RE_MASK; | |
| return (TRUE); | |
| } | |
| return (FALSE); | |
| } | |
| void vDisableMACBOperations(volatile avr32_macb_t * macb) | |
| { | |
| #if ETHERNET_CONF_USE_PHY_IT | |
| volatile avr32_gpio_t *gpio = &AVR32_GPIO; | |
| volatile avr32_gpio_port_t *gpio_port = &gpio->port[MACB_INTERRUPT_PIN/32]; | |
| gpio_port->ierc = 1 << (MACB_INTERRUPT_PIN%32); | |
| #endif | |
| // write the MACB control register : disable Tx & Rx | |
| macb->ncr &= ~((1 << AVR32_MACB_RE_OFFSET) | (1 << AVR32_MACB_TE_OFFSET)); | |
| // We no more want to interrupt on Rx and Tx events. | |
| macb->idr = AVR32_MACB_IER_RCOMP_MASK | AVR32_MACB_IER_TCOMP_MASK; | |
| } | |
| void vClearMACBTxBuffer( void ) | |
| { | |
| static unsigned long uxNextBufferToClear = 0; | |
| // Called on Tx interrupt events to set the AVR32_TRANSMIT_OK bit in each | |
| // Tx buffer within the frame just transmitted. This marks all the buffers | |
| // as available again. | |
| // The first buffer in the frame should have the bit set automatically. */ | |
| if( xTxDescriptors[ uxNextBufferToClear ].U_Status.status & AVR32_TRANSMIT_OK ) | |
| { | |
| // Loop through the other buffers in the frame. | |
| while( !( xTxDescriptors[ uxNextBufferToClear ].U_Status.status & AVR32_LAST_BUFFER ) ) | |
| { | |
| uxNextBufferToClear++; | |
| if( uxNextBufferToClear >= ETHERNET_CONF_NB_TX_BUFFERS ) | |
| { | |
| uxNextBufferToClear = 0; | |
| } | |
| xTxDescriptors[ uxNextBufferToClear ].U_Status.status |= AVR32_TRANSMIT_OK; | |
| } | |
| // Start with the next buffer the next time a Tx interrupt is called. | |
| uxNextBufferToClear++; | |
| // Do we need to wrap back to the first buffer? | |
| if( uxNextBufferToClear >= ETHERNET_CONF_NB_TX_BUFFERS ) | |
| { | |
| uxNextBufferToClear = 0; | |
| } | |
| } | |
| } | |
| static void prvSetupDescriptors(volatile avr32_macb_t * macb) | |
| { | |
| unsigned long xIndex; | |
| unsigned long ulAddress; | |
| // Initialise xRxDescriptors descriptor. | |
| for( xIndex = 0; xIndex < ETHERNET_CONF_NB_RX_BUFFERS; ++xIndex ) | |
| { | |
| // Calculate the address of the nth buffer within the array. | |
| ulAddress = ( unsigned long )( pcRxBuffer + ( xIndex * RX_BUFFER_SIZE ) ); | |
| // Write the buffer address into the descriptor. | |
| // The DMA will place the data at this address when this descriptor is being used. | |
| // Mask off the bottom bits of the address as these have special meaning. | |
| xRxDescriptors[ xIndex ].addr = ulAddress & ADDRESS_MASK; | |
| } | |
| // The last buffer has the wrap bit set so the MACB knows to wrap back | |
| // to the first buffer. | |
| xRxDescriptors[ ETHERNET_CONF_NB_RX_BUFFERS - 1 ].addr |= RX_WRAP_BIT; | |
| // Initialise xTxDescriptors. | |
| for( xIndex = 0; xIndex < ETHERNET_CONF_NB_TX_BUFFERS; ++xIndex ) | |
| { | |
| // Calculate the address of the nth buffer within the array. | |
| ulAddress = ( unsigned long )( pcTxBuffer + ( xIndex * ETHERNET_CONF_TX_BUFFER_SIZE ) ); | |
| // Write the buffer address into the descriptor. | |
| // The DMA will read data from here when the descriptor is being used. | |
| xTxDescriptors[ xIndex ].addr = ulAddress & ADDRESS_MASK; | |
| xTxDescriptors[ xIndex ].U_Status.status = AVR32_TRANSMIT_OK; | |
| } | |
| // The last buffer has the wrap bit set so the MACB knows to wrap back | |
| // to the first buffer. | |
| xTxDescriptors[ ETHERNET_CONF_NB_TX_BUFFERS - 1 ].U_Status.status = AVR32_TRANSMIT_WRAP | AVR32_TRANSMIT_OK; | |
| // Tell the MACB where to find the descriptors. | |
| macb->rbqp = ( unsigned long )xRxDescriptors; | |
| macb->tbqp = ( unsigned long )xTxDescriptors; | |
| // Enable the copy of data into the buffers, ignore broadcasts, | |
| // and don't copy FCS. | |
| macb->ncfgr |= (AVR32_MACB_CAF_MASK | AVR32_MACB_NBC_MASK | AVR32_MACB_NCFGR_DRFCS_MASK); | |
| } | |
| static void prvSetupMACAddress( volatile avr32_macb_t * macb ) | |
| { | |
| // Must be written SA1L then SA1H. | |
| macb->sa1b = ( ( unsigned long ) cMACAddress[ 3 ] << 24 ) | | |
| ( ( unsigned long ) cMACAddress[ 2 ] << 16 ) | | |
| ( ( unsigned long ) cMACAddress[ 1 ] << 8 ) | | |
| cMACAddress[ 0 ]; | |
| macb->sa1t = ( ( unsigned long ) cMACAddress[ 5 ] << 8 ) | | |
| cMACAddress[ 4 ]; | |
| } | |
| static void prvSetupMACBInterrupt( volatile avr32_macb_t * macb ) | |
| { | |
| #ifdef FREERTOS_USED | |
| // Create the semaphore used to trigger the MACB task. | |
| if (xSemaphore == NULL) | |
| { | |
| vSemaphoreCreateBinary( xSemaphore ); | |
| } | |
| #else | |
| // Create the flag used to trigger the MACB polling task. | |
| DataToRead = FALSE; | |
| #endif | |
| #ifdef FREERTOS_USED | |
| if( xSemaphore != NULL) | |
| { | |
| // We start by 'taking' the semaphore so the ISR can 'give' it when the | |
| // first interrupt occurs. | |
| xSemaphoreTake( xSemaphore, 0 ); | |
| #endif | |
| // Setup the interrupt for MACB. | |
| // Register the interrupt handler to the interrupt controller at interrupt level 2 | |
| INTC_register_interrupt((__int_handler)&vMACB_ISR, AVR32_MACB_IRQ, INT2); | |
| #if ETHERNET_CONF_USE_PHY_IT | |
| /* GPIO enable interrupt upon rising edge */ | |
| gpio_enable_pin_interrupt(MACB_INTERRUPT_PIN, GPIO_FALLING_EDGE); | |
| // Setup the interrupt for PHY. | |
| // Register the interrupt handler to the interrupt controller at interrupt level 2 | |
| INTC_register_interrupt((__int_handler)&vPHY_ISR, (AVR32_GPIO_IRQ_0 + (MACB_INTERRUPT_PIN/8)), INT2); | |
| /* enable interrupts on INT pin */ | |
| vWriteMDIO( macb, PHY_MICR , ( MICR_INTEN | MICR_INTOE )); | |
| /* enable "link change" interrupt for Phy */ | |
| vWriteMDIO( macb, PHY_MISR , MISR_LINK_INT_EN ); | |
| #endif | |
| // We want to interrupt on Rx and Tx events | |
| macb->ier = AVR32_MACB_IER_RCOMP_MASK | AVR32_MACB_IER_TCOMP_MASK; | |
| #ifdef FREERTOS_USED | |
| } | |
| #endif | |
| } | |
| /*! Read a register on MDIO bus (access to the PHY) | |
| * This function is looping until PHY gets ready | |
| * | |
| * \param macb Input. instance of the MACB to use | |
| * \param usAddress Input. register to set. | |
| * | |
| * \return unsigned long data that has been read | |
| */ | |
| static unsigned long ulReadMDIO(volatile avr32_macb_t * macb, unsigned short usAddress) | |
| { | |
| unsigned long value, status; | |
| // initiate transaction : enable management port | |
| macb->ncr |= AVR32_MACB_NCR_MPE_MASK; | |
| // Write the PHY configuration frame to the MAN register | |
| macb->man = (AVR32_MACB_SOF_MASK & (0x01<<AVR32_MACB_SOF_OFFSET)) // SOF | |
| | (2 << AVR32_MACB_CODE_OFFSET) // Code | |
| | (2 << AVR32_MACB_RW_OFFSET) // Read operation | |
| | ((ETHERNET_CONF_PHY_ADDR & 0x1f) << AVR32_MACB_PHYA_OFFSET) // Phy Add | |
| | (usAddress << AVR32_MACB_REGA_OFFSET); // Reg Add | |
| // wait for PHY to be ready | |
| do { | |
| status = macb->nsr; | |
| } while (!(status & AVR32_MACB_NSR_IDLE_MASK)); | |
| // read the register value in maintenance register | |
| value = macb->man & 0x0000ffff; | |
| // disable management port | |
| macb->ncr &= ~AVR32_MACB_NCR_MPE_MASK; | |
| // return the read value | |
| return (value); | |
| } | |
| /*! Write a given value to a register on MDIO bus (access to the PHY) | |
| * This function is looping until PHY gets ready | |
| * | |
| * \param *macb Input. instance of the MACB to use | |
| * \param usAddress Input. register to set. | |
| * \param usValue Input. value to write. | |
| * | |
| */ | |
| static void vWriteMDIO(volatile avr32_macb_t * macb, unsigned short usAddress, unsigned short usValue) | |
| { | |
| unsigned long status; | |
| // initiate transaction : enable management port | |
| macb->ncr |= AVR32_MACB_NCR_MPE_MASK; | |
| // Write the PHY configuration frame to the MAN register | |
| macb->man = (( AVR32_MACB_SOF_MASK & (0x01<<AVR32_MACB_SOF_OFFSET)) // SOF | |
| | (2 << AVR32_MACB_CODE_OFFSET) // Code | |
| | (1 << AVR32_MACB_RW_OFFSET) // Write operation | |
| | ((ETHERNET_CONF_PHY_ADDR & 0x1f) << AVR32_MACB_PHYA_OFFSET) // Phy Add | |
| | (usAddress << AVR32_MACB_REGA_OFFSET)) // Reg Add | |
| | (usValue & 0xffff); // Data | |
| // wait for PHY to be ready | |
| do { | |
| status = macb->nsr; | |
| } while (!(status & AVR32_MACB_NSR_IDLE_MASK)); | |
| // disable management port | |
| macb->ncr &= ~AVR32_MACB_NCR_MPE_MASK; | |
| } | |
| static Bool prvProbePHY( volatile avr32_macb_t * macb ) | |
| { | |
| volatile unsigned long mii_status, phy_ctrl; | |
| volatile unsigned long config; | |
| unsigned long upper, lower, mode, advertise, lpa; | |
| volatile unsigned long physID; | |
| // Read Phy Identifier register 1 & 2 | |
| lower = ulReadMDIO(macb, PHY_PHYSID2); | |
| upper = ulReadMDIO(macb, PHY_PHYSID1); | |
| // get Phy ID, ignore Revision | |
| physID = ((upper << 16) & 0xFFFF0000) | (lower & 0xFFF0); | |
| // check if it match config | |
| if (physID == ETHERNET_CONF_PHY_ID) | |
| { | |
| // read RBR | |
| mode = ulReadMDIO(macb, PHY_RBR); | |
| // set RMII mode if not done | |
| if ((mode & RBR_RMII) != RBR_RMII) | |
| { | |
| // force RMII flag if strap options are wrong | |
| mode |= RBR_RMII; | |
| vWriteMDIO(macb, PHY_RBR, mode); | |
| } | |
| // set advertise register | |
| #if ETHERNET_CONF_AN_ENABLE == 1 | |
| advertise = ADVERTISE_CSMA | ADVERTISE_ALL; | |
| #else | |
| advertise = ADVERTISE_CSMA; | |
| #if ETHERNET_CONF_USE_100MB | |
| #if ETHERNET_CONF_USE_FULL_DUPLEX | |
| advertise |= ADVERTISE_100FULL; | |
| #else | |
| advertise |= ADVERTISE_100HALF; | |
| #endif | |
| #else | |
| #if ETHERNET_CONF_USE_FULL_DUPLEX | |
| advertise |= ADVERTISE_10FULL; | |
| #else | |
| advertise |= ADVERTISE_10HALF; | |
| #endif | |
| #endif | |
| #endif | |
| // write advertise register | |
| vWriteMDIO(macb, PHY_ADVERTISE, advertise); | |
| // read Control register | |
| config = ulReadMDIO(macb, PHY_BMCR); | |
| // read Phy Control register | |
| phy_ctrl = ulReadMDIO(macb, PHY_PHYCR); | |
| #if ETHERNET_CONF_AN_ENABLE | |
| #if ETHERNET_CONF_AUTO_CROSS_ENABLE | |
| // enable Auto MDIX | |
| phy_ctrl |= PHYCR_MDIX_EN; | |
| #else | |
| // disable Auto MDIX | |
| phy_ctrl &= ~PHYCR_MDIX_EN; | |
| #if ETHERNET_CONF_CROSSED_LINK | |
| // force direct link = Use crossed RJ45 cable | |
| phy_ctrl &= ~PHYCR_MDIX_FORCE; | |
| #else | |
| // force crossed link = Use direct RJ45 cable | |
| phy_ctrl |= PHYCR_MDIX_FORCE; | |
| #endif | |
| #endif | |
| // reset auto-negociation capability | |
| config |= (BMCR_ANRESTART | BMCR_ANENABLE); | |
| #else | |
| // disable Auto MDIX | |
| phy_ctrl &= ~PHYCR_MDIX_EN; | |
| #if ETHERNET_CONF_CROSSED_LINK | |
| // force direct link = Use crossed RJ45 cable | |
| phy_ctrl &= ~PHYCR_MDIX_FORCE; | |
| #else | |
| // force crossed link = Use direct RJ45 cable | |
| phy_ctrl |= PHYCR_MDIX_FORCE; | |
| #endif | |
| // clear AN bit | |
| config &= ~BMCR_ANENABLE; | |
| #if ETHERNET_CONF_USE_100MB | |
| config |= BMCR_SPEED100; | |
| #else | |
| config &= ~BMCR_SPEED100; | |
| #endif | |
| #if ETHERNET_CONF_USE_FULL_DUPLEX | |
| config |= BMCR_FULLDPLX; | |
| #else | |
| config &= ~BMCR_FULLDPLX; | |
| #endif | |
| #endif | |
| // update Phy ctrl register | |
| vWriteMDIO(macb, PHY_PHYCR, phy_ctrl); | |
| // update ctrl register | |
| vWriteMDIO(macb, PHY_BMCR, config); | |
| // loop while link status isn't OK | |
| do { | |
| mii_status = ulReadMDIO(macb, PHY_BMSR); | |
| } while (!(mii_status & BMSR_LSTATUS)); | |
| // read the LPA configuration of the PHY | |
| lpa = ulReadMDIO(macb, PHY_LPA); | |
| // read the MACB config register | |
| config = AVR32_MACB.ncfgr; | |
| // if 100MB needed | |
| if ((lpa & advertise) & (LPA_100HALF | LPA_100FULL)) | |
| { | |
| config |= AVR32_MACB_SPD_MASK; | |
| } | |
| else | |
| { | |
| config &= ~(AVR32_MACB_SPD_MASK); | |
| } | |
| // if FULL DUPLEX needed | |
| if ((lpa & advertise) & (LPA_10FULL | LPA_100FULL)) | |
| { | |
| config |= AVR32_MACB_FD_MASK; | |
| } | |
| else | |
| { | |
| config &= ~(AVR32_MACB_FD_MASK); | |
| } | |
| // write the MACB config register | |
| macb->ncfgr = config; | |
| return TRUE; | |
| } | |
| return FALSE; | |
| } | |
| void vMACBWaitForInput( unsigned long ulTimeOut ) | |
| { | |
| #ifdef FREERTOS_USED | |
| // Just wait until we are signled from an ISR that data is available, or | |
| // we simply time out. | |
| xSemaphoreTake( xSemaphore, ulTimeOut ); | |
| #else | |
| unsigned long i; | |
| gpio_clr_gpio_pin(LED0_GPIO); | |
| i = ulTimeOut * 1000; | |
| // wait for an interrupt to occurs | |
| do | |
| { | |
| if ( DataToRead == TRUE ) | |
| { | |
| // IT occurs, reset interrupt flag | |
| portENTER_CRITICAL(); | |
| DataToRead = FALSE; | |
| portEXIT_CRITICAL(); | |
| break; | |
| } | |
| i--; | |
| } | |
| while(i != 0); | |
| gpio_set_gpio_pin(LED0_GPIO); | |
| #endif | |
| } | |
| /* | |
| * The MACB ISR. Handles both Tx and Rx complete interrupts. | |
| */ | |
| #ifdef FREERTOS_USED | |
| #if __GNUC__ | |
| __attribute__((naked)) | |
| #elif __ICCAVR32__ | |
| #pragma shadow_registers = full // Naked. | |
| #endif | |
| #else | |
| #if __GNUC__ | |
| __attribute__((__interrupt__)) | |
| #elif __ICCAVR32__ | |
| __interrupt | |
| #endif | |
| #endif | |
| void vMACB_ISR( void ) | |
| { | |
| // This ISR can cause a context switch, so the first statement must be a | |
| // call to the portENTER_SWITCHING_ISR() macro. This must be BEFORE any | |
| // variable declarations. | |
| portENTER_SWITCHING_ISR(); | |
| // the return value is used by FreeRTOS to change the context if needed after rete instruction | |
| // in standalone use, this value should be ignored | |
| prvMACB_ISR_NonNakedBehaviour(); | |
| // Exit the ISR. If a task was woken by either a character being received | |
| // or transmitted then a context switch will occur. | |
| portEXIT_SWITCHING_ISR(); | |
| } | |
| /*-----------------------------------------------------------*/ | |
| #if __GNUC__ | |
| __attribute__((__noinline__)) | |
| #elif __ICCAVR32__ | |
| #pragma optimize = no_inline | |
| #endif | |
| static long prvMACB_ISR_NonNakedBehaviour( void ) | |
| { | |
| // Variable definitions can be made now. | |
| volatile unsigned long ulIntStatus, ulEventStatus; | |
| long xHigherPriorityTaskWoken = FALSE; | |
| // Find the cause of the interrupt. | |
| ulIntStatus = AVR32_MACB.isr; | |
| ulEventStatus = AVR32_MACB.rsr; | |
| if( ( ulIntStatus & AVR32_MACB_IDR_RCOMP_MASK ) || ( ulEventStatus & AVR32_MACB_REC_MASK ) ) | |
| { | |
| // A frame has been received, signal the IP task so it can process | |
| // the Rx descriptors. | |
| portENTER_CRITICAL(); | |
| #ifdef FREERTOS_USED | |
| xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken ); | |
| #else | |
| DataToRead = TRUE; | |
| #endif | |
| portEXIT_CRITICAL(); | |
| AVR32_MACB.rsr = AVR32_MACB_REC_MASK; | |
| AVR32_MACB.rsr; | |
| } | |
| if( ulIntStatus & AVR32_MACB_TCOMP_MASK ) | |
| { | |
| // A frame has been transmitted. Mark all the buffers used by the | |
| // frame just transmitted as free again. | |
| vClearMACBTxBuffer(); | |
| AVR32_MACB.tsr = AVR32_MACB_TSR_COMP_MASK; | |
| AVR32_MACB.tsr; | |
| } | |
| return ( xHigherPriorityTaskWoken ); | |
| } | |
| #if ETHERNET_CONF_USE_PHY_IT | |
| /* | |
| * The PHY ISR. Handles Phy interrupts. | |
| */ | |
| #ifdef FREERTOS_USED | |
| #if __GNUC__ | |
| __attribute__((naked)) | |
| #elif __ICCAVR32__ | |
| #pragma shadow_registers = full // Naked. | |
| #endif | |
| #else | |
| #if __GNUC__ | |
| __attribute__((__interrupt__)) | |
| #elif __ICCAVR32__ | |
| __interrupt | |
| #endif | |
| #endif | |
| void vPHY_ISR( void ) | |
| { | |
| // This ISR can cause a context switch, so the first statement must be a | |
| // call to the portENTER_SWITCHING_ISR() macro. This must be BEFORE any | |
| // variable declarations. | |
| portENTER_SWITCHING_ISR(); | |
| // the return value is used by FreeRTOS to change the context if needed after rete instruction | |
| // in standalone use, this value should be ignored | |
| prvPHY_ISR_NonNakedBehaviour(); | |
| // Exit the ISR. If a task was woken by either a character being received | |
| // or transmitted then a context switch will occur. | |
| portEXIT_SWITCHING_ISR(); | |
| } | |
| /*-----------------------------------------------------------*/ | |
| #if __GNUC__ | |
| __attribute__((__noinline__)) | |
| #elif __ICCAVR32__ | |
| #pragma optimize = no_inline | |
| #endif | |
| static long prvPHY_ISR_NonNakedBehaviour( void ) | |
| { | |
| // Variable definitions can be made now. | |
| volatile unsigned long ulIntStatus, ulEventStatus; | |
| long xSwitchRequired = FALSE; | |
| volatile avr32_gpio_t *gpio = &AVR32_GPIO; | |
| volatile avr32_gpio_port_t *gpio_port = &gpio->port[MACB_INTERRUPT_PIN/32]; | |
| // read Phy Interrupt register Status | |
| ulIntStatus = ulReadMDIO(&AVR32_MACB, PHY_MISR); | |
| // read Phy status register | |
| ulEventStatus = ulReadMDIO(&AVR32_MACB, PHY_BMSR); | |
| // dummy read | |
| ulEventStatus = ulReadMDIO(&AVR32_MACB, PHY_BMSR); | |
| // clear interrupt flag on GPIO | |
| gpio_port->ifrc = 1 << (MACB_INTERRUPT_PIN%32); | |
| return ( xSwitchRequired ); | |
| } | |
| #endif |