| /* | |
| FreeRTOS V8.0.1 - Copyright (C) 2014 Real Time Engineers Ltd. | |
| All rights reserved | |
| VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. | |
| *************************************************************************** | |
| * * | |
| * FreeRTOS provides completely free yet professionally developed, * | |
| * robust, strictly quality controlled, supported, and cross * | |
| * platform software that has become a de facto standard. * | |
| * * | |
| * Help yourself get started quickly and support the FreeRTOS * | |
| * project by purchasing a FreeRTOS tutorial book, reference * | |
| * manual, or both from: http://www.FreeRTOS.org/Documentation * | |
| * * | |
| * Thank you! * | |
| * * | |
| *************************************************************************** | |
| This file is part of the FreeRTOS distribution. | |
| FreeRTOS is free software; you can redistribute it and/or modify it under | |
| the terms of the GNU General Public License (version 2) as published by the | |
| Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. | |
| >>! NOTE: The modification to the GPL is included to allow you to !<< | |
| >>! distribute a combined work that includes FreeRTOS without being !<< | |
| >>! obliged to provide the source code for proprietary components !<< | |
| >>! outside of the FreeRTOS kernel. !<< | |
| FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY | |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
| FOR A PARTICULAR PURPOSE. Full license text is available from the following | |
| link: http://www.freertos.org/a00114.html | |
| 1 tab == 4 spaces! | |
| *************************************************************************** | |
| * * | |
| * Having a problem? Start by reading the FAQ "My application does * | |
| * not run, what could be wrong?" * | |
| * * | |
| * http://www.FreeRTOS.org/FAQHelp.html * | |
| * * | |
| *************************************************************************** | |
| http://www.FreeRTOS.org - Documentation, books, training, latest versions, | |
| license and Real Time Engineers Ltd. contact details. | |
| http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, | |
| including FreeRTOS+Trace - an indispensable productivity tool, a DOS | |
| compatible FAT file system, and our tiny thread aware UDP/IP stack. | |
| http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High | |
| Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS | |
| licenses offer ticketed support, indemnification and middleware. | |
| http://www.SafeRTOS.com - High Integrity Systems also provide a safety | |
| engineered and independently SIL3 certified version for use in safety and | |
| mission critical applications that require provable dependability. | |
| 1 tab == 4 spaces! | |
| */ | |
| /* Freescale includes. */ | |
| #include "common.h" | |
| #include "eth_phy.h" | |
| #include "enet.h" | |
| #include "mii.h" | |
| /* FreeRTOS includes. */ | |
| #include "FreeRTOS.h" | |
| #include "task.h" | |
| #include "queue.h" | |
| /* uIP includes. */ | |
| #include "net/uip.h" | |
| /* The time to wait between attempts to obtain a free buffer. */ | |
| #define emacBUFFER_WAIT_DELAY_ms ( 3 / portTICK_PERIOD_MS ) | |
| /* The number of times emacBUFFER_WAIT_DELAY_ms should be waited before giving | |
| up on attempting to obtain a free buffer all together. */ | |
| #define emacBUFFER_WAIT_ATTEMPTS ( 30 ) | |
| /* The number of Rx descriptors. */ | |
| #define emacNUM_RX_DESCRIPTORS 8 | |
| /* The number of Tx descriptors. When using uIP there is not point in having | |
| more than two. */ | |
| #define emacNUM_TX_BUFFERS 2 | |
| /* The total number of EMAC buffers to allocate. */ | |
| #define emacNUM_BUFFERS ( emacNUM_RX_DESCRIPTORS + emacNUM_TX_BUFFERS ) | |
| /* The time to wait for the Tx descriptor to become free. */ | |
| #define emacTX_WAIT_DELAY_ms ( 10 / portTICK_PERIOD_MS ) | |
| /* The total number of times to wait emacTX_WAIT_DELAY_ms for the Tx descriptor to | |
| become free. */ | |
| #define emacTX_WAIT_ATTEMPTS ( 50 ) | |
| /* Constants used for set up and initialisation. */ | |
| #define emacTX_INTERRUPT_NO ( 76 ) | |
| #define emacRX_INTERRUPT_NO ( 77 ) | |
| #define emacERROR_INTERRUPT_NO ( 78 ) | |
| #define emacLINK_DELAY ( 500 / portTICK_PERIOD_MS ) | |
| #define emacPHY_STATUS ( 0x1F ) | |
| #define emacPHY_DUPLEX_STATUS ( 4 << 2 ) | |
| #define emacPHY_SPEED_STATUS ( 1 << 2 ) | |
| /*-----------------------------------------------------------*/ | |
| /* | |
| * Initialise both the Rx and Tx descriptors. | |
| */ | |
| static void prvInitialiseDescriptors( void ); | |
| /* | |
| * Return a pointer to a free buffer within xEthernetBuffers. | |
| */ | |
| static unsigned char *prvGetNextBuffer( void ); | |
| /* | |
| * Return a buffer to the list of free buffers. | |
| */ | |
| static void prvReturnBuffer( unsigned char *pucBuffer ); | |
| /* | |
| * Examine the status of the next Rx descriptor to see if it contains new data. | |
| */ | |
| static unsigned short prvCheckRxStatus( void ); | |
| /* | |
| * Something has gone wrong with the descriptor usage. Reset all the buffers | |
| * and descriptors. | |
| */ | |
| static void prvResetEverything( void ); | |
| /*-----------------------------------------------------------*/ | |
| /* The buffers and descriptors themselves. */ | |
| #pragma data_alignment=16 | |
| volatile NBUF xRxDescriptors[ emacNUM_RX_DESCRIPTORS ]; | |
| #pragma data_alignment=16 | |
| volatile NBUF xTxDescriptors[ emacNUM_TX_BUFFERS ]; | |
| #pragma data_alignment=16 | |
| char xEthernetBuffers[ emacNUM_BUFFERS ][ UIP_BUFSIZE ]; | |
| /* Used to indicate which buffers are free and which are in use. If an index | |
| contains 0 then the corresponding buffer in xEthernetBuffers is free, otherwise | |
| the buffer is in use or about to be used. */ | |
| static unsigned char ucBufferInUse[ emacNUM_BUFFERS ]; | |
| /* Points to the Rx descriptor currently in use. */ | |
| static volatile NBUF *pxCurrentRxDesc = NULL; | |
| /* pxCurrentRxDesc points to descriptor within the xRxDescriptors array that | |
| has an index defined by ulRxDescriptorIndex. */ | |
| static unsigned long ulRxDescriptorIndex = 0UL; | |
| /* The buffer used by the uIP stack to both receive and send. This points to | |
| one of the Ethernet buffers when its actually in use. */ | |
| unsigned char *uip_buf = NULL; | |
| /*-----------------------------------------------------------*/ | |
| void vEMACInit( void ) | |
| { | |
| int iData; | |
| extern int periph_clk_khz; | |
| const unsigned char ucMACAddress[] = | |
| { | |
| configMAC_ADDR0, configMAC_ADDR1, configMAC_ADDR2, configMAC_ADDR3, configMAC_ADDR4, configMAC_ADDR5 | |
| }; | |
| /* Enable the ENET clock. */ | |
| SIM_SCGC2 |= SIM_SCGC2_ENET_MASK; | |
| /* Allow concurrent access to MPU controller to avoid bus errors. */ | |
| MPU_CESR = 0; | |
| prvInitialiseDescriptors(); | |
| /* Reset and enable. */ | |
| ENET_ECR = ENET_ECR_RESET_MASK; | |
| /* Wait at least 8 clock cycles */ | |
| vTaskDelay( 2 ); | |
| /* Start the MII interface*/ | |
| mii_init( 0, periph_clk_khz / 1000L ); | |
| /* Configure the transmit interrupt. */ | |
| set_irq_priority( emacTX_INTERRUPT_NO, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY ); | |
| enable_irq( emacTX_INTERRUPT_NO ); | |
| /* Configure the receive interrupt. */ | |
| set_irq_priority( emacRX_INTERRUPT_NO, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY ); | |
| enable_irq( emacRX_INTERRUPT_NO ); | |
| /* Configure the error interrupt. */ | |
| set_irq_priority( emacERROR_INTERRUPT_NO, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY ); | |
| enable_irq( emacERROR_INTERRUPT_NO ); | |
| /* Configure the pins to the PHY - RMII mode used. */ | |
| PORTB_PCR0 = PORT_PCR_MUX( 4 ); /* RMII0_MDIO / MII0_MDIO. */ | |
| PORTB_PCR1 = PORT_PCR_MUX( 4 ); /* RMII0_MDC / MII0_MDC */ | |
| PORTA_PCR14 = PORT_PCR_MUX( 4 ); /* RMII0_CRS_DV / MII0_RXDV */ | |
| PORTA_PCR12 = PORT_PCR_MUX( 4 ); /* RMII0_RXD1 / MII0_RXD1 */ | |
| PORTA_PCR13 = PORT_PCR_MUX( 4 ); /* RMII0_RXD0/MII0_RXD0 */ | |
| PORTA_PCR15 = PORT_PCR_MUX( 4 ); /* RMII0_TXEN/MII0_TXEN */ | |
| PORTA_PCR16 = PORT_PCR_MUX( 4 ); /* RMII0_TXD0/MII0_TXD0 */ | |
| PORTA_PCR17 = PORT_PCR_MUX( 4 ); /* RMII0_TXD1/MII0_TXD1 */ | |
| /* Is there communication with the PHY? */ | |
| do | |
| { | |
| vTaskDelay( emacLINK_DELAY ); | |
| iData = 0xFFFF; | |
| mii_read( 0, configPHY_ADDRESS, PHY_PHYIDR1, &iData ); | |
| } while( iData == 0xFFFF ); | |
| /* Start to auto negotiate. */ | |
| mii_write( 0, configPHY_ADDRESS, PHY_BMCR, ( PHY_BMCR_AN_RESTART | PHY_BMCR_AN_ENABLE ) ); | |
| /* Wait for auto negotiate to complete. */ | |
| do | |
| { | |
| vTaskDelay( emacLINK_DELAY ); | |
| mii_read( 0, configPHY_ADDRESS, PHY_BMSR, &iData ); | |
| } while( !( iData & PHY_BMSR_AN_COMPLETE ) ); | |
| /* A link has been established. What was negotiated? */ | |
| iData = 0; | |
| mii_read( 0, configPHY_ADDRESS, emacPHY_STATUS, &iData ); | |
| /* Clear the Individual and Group Address Hash registers */ | |
| ENET_IALR = 0; | |
| ENET_IAUR = 0; | |
| ENET_GALR = 0; | |
| ENET_GAUR = 0; | |
| /* Set the Physical Address for the selected ENET */ | |
| enet_set_address( 0, ucMACAddress ); | |
| ENET_RCR = ENET_RCR_MAX_FL( UIP_BUFSIZE ) | ENET_RCR_MII_MODE_MASK | ENET_RCR_CRCFWD_MASK | ENET_RCR_RMII_MODE_MASK; | |
| /* Clear the control registers. */ | |
| ENET_TCR = 0; | |
| if( iData & emacPHY_DUPLEX_STATUS ) | |
| { | |
| /* Full duplex */ | |
| ENET_RCR &= ( unsigned long )~ENET_RCR_DRT_MASK; | |
| ENET_TCR |= ENET_TCR_FDEN_MASK; | |
| } | |
| else | |
| { | |
| /* Half duplex */ | |
| ENET_RCR |= ENET_RCR_DRT_MASK; | |
| ENET_TCR &= (unsigned long)~ENET_TCR_FDEN_MASK; | |
| } | |
| if( iData & emacPHY_SPEED_STATUS ) | |
| { | |
| /* 10Mbps */ | |
| ENET_RCR |= ENET_RCR_RMII_10T_MASK; | |
| } | |
| ENET_ECR = ENET_ECR_EN1588_MASK; | |
| /* Store and forward checksum. */ | |
| ENET_TFWR = ENET_TFWR_STRFWD_MASK; | |
| /* Set Rx Buffer Size */ | |
| ENET_MRBR = ( unsigned short ) UIP_BUFSIZE; | |
| /* Point to the start of the circular Rx buffer descriptor queue */ | |
| ENET_RDSR = ( unsigned long ) &( xRxDescriptors[ 0 ] ); | |
| /* Point to the start of the circular Tx buffer descriptor queue */ | |
| ENET_TDSR = ( unsigned long ) &( xTxDescriptors[ 0 ] ); | |
| /* Clear all ENET interrupt events */ | |
| ENET_EIR = ( unsigned long ) -1; | |
| /* Enable interrupts. */ | |
| ENET_EIMR = 0 | |
| /*rx irqs*/ | |
| | ENET_EIMR_RXF_MASK/* only for complete frame, not partial buffer descriptor | ENET_EIMR_RXB_MASK*/ | |
| /*xmit irqs*/ | |
| | ENET_EIMR_TXF_MASK/* only for complete frame, not partial buffer descriptor | ENET_EIMR_TXB_MASK*/ | |
| /*enet irqs*/ | |
| | ENET_EIMR_UN_MASK | ENET_EIMR_RL_MASK | ENET_EIMR_LC_MASK | ENET_EIMR_BABT_MASK | ENET_EIMR_BABR_MASK | ENET_EIMR_EBERR_MASK | |
| ; | |
| /* Enable the MAC itself. */ | |
| ENET_ECR |= ENET_ECR_ETHEREN_MASK; | |
| /* Indicate that there have been empty receive buffers produced */ | |
| ENET_RDAR = ENET_RDAR_RDAR_MASK; | |
| } | |
| /*-----------------------------------------------------------*/ | |
| static void prvInitialiseDescriptors( void ) | |
| { | |
| volatile NBUF *pxDescriptor; | |
| long x; | |
| for( x = 0; x < emacNUM_BUFFERS; x++ ) | |
| { | |
| /* Ensure none of the buffers are shown as in use at the start. */ | |
| ucBufferInUse[ x ] = pdFALSE; | |
| } | |
| /* Initialise the Rx descriptors. */ | |
| for( x = 0; x < emacNUM_RX_DESCRIPTORS; x++ ) | |
| { | |
| pxDescriptor = &( xRxDescriptors[ x ] ); | |
| pxDescriptor->data = ( uint8_t* ) &( xEthernetBuffers[ x ][ 0 ] ); | |
| pxDescriptor->data = ( uint8_t* ) __REV( ( unsigned long ) pxDescriptor->data ); | |
| pxDescriptor->length = 0; | |
| pxDescriptor->status = RX_BD_E; | |
| pxDescriptor->bdu = 0; | |
| pxDescriptor->ebd_status = RX_BD_INT; | |
| /* Mark this buffer as in use. */ | |
| ucBufferInUse[ x ] = pdTRUE; | |
| } | |
| /* The last descriptor points back to the start. */ | |
| pxDescriptor->status |= RX_BD_W; | |
| /* Initialise the Tx descriptors. */ | |
| for( x = 0; x < emacNUM_TX_BUFFERS; x++ ) | |
| { | |
| pxDescriptor = &( xTxDescriptors[ x ] ); | |
| /* A buffer is not allocated to the Tx descriptor until a send is | |
| actually required. */ | |
| pxDescriptor->data = NULL; | |
| pxDescriptor->length = 0; | |
| pxDescriptor->status = TX_BD_TC; | |
| pxDescriptor->ebd_status = TX_BD_INT; | |
| } | |
| /* The last descriptor points back to the start. */ | |
| pxDescriptor->status |= TX_BD_W; | |
| /* Use the first Rx descriptor to start with. */ | |
| ulRxDescriptorIndex = 0UL; | |
| pxCurrentRxDesc = &( xRxDescriptors[ 0 ] ); | |
| } | |
| /*-----------------------------------------------------------*/ | |
| void vEMACWrite( void ) | |
| { | |
| long x; | |
| /* Wait until the second transmission of the last packet has completed. */ | |
| for( x = 0; x < emacTX_WAIT_ATTEMPTS; x++ ) | |
| { | |
| if( ( xTxDescriptors[ 1 ].status & TX_BD_R ) != 0 ) | |
| { | |
| /* Descriptor is still active. */ | |
| vTaskDelay( emacTX_WAIT_DELAY_ms ); | |
| } | |
| else | |
| { | |
| break; | |
| } | |
| } | |
| /* Is the descriptor free after waiting for it? */ | |
| if( ( xTxDescriptors[ 1 ].status & TX_BD_R ) != 0 ) | |
| { | |
| /* Something has gone wrong. */ | |
| prvResetEverything(); | |
| } | |
| /* Setup both descriptors to transmit the frame. */ | |
| xTxDescriptors[ 0 ].data = ( uint8_t * ) __REV( ( unsigned long ) uip_buf ); | |
| xTxDescriptors[ 0 ].length = __REVSH( uip_len ); | |
| xTxDescriptors[ 1 ].data = ( uint8_t * ) __REV( ( unsigned long ) uip_buf ); | |
| xTxDescriptors[ 1 ].length = __REVSH( uip_len ); | |
| /* uip_buf is being sent by the Tx descriptor. Allocate a new buffer | |
| for use by the stack. */ | |
| uip_buf = prvGetNextBuffer(); | |
| /* Clear previous settings and go. */ | |
| xTxDescriptors[ 0 ].status |= ( TX_BD_R | TX_BD_L ); | |
| xTxDescriptors[ 1 ].status |= ( TX_BD_R | TX_BD_L ); | |
| /* Start the Tx. */ | |
| ENET_TDAR = ENET_TDAR_TDAR_MASK; | |
| } | |
| /*-----------------------------------------------------------*/ | |
| static unsigned char *prvGetNextBuffer( void ) | |
| { | |
| long x; | |
| unsigned char *pucReturn = NULL; | |
| unsigned long ulAttempts = 0; | |
| while( pucReturn == NULL ) | |
| { | |
| /* Look through the buffers to find one that is not in use by | |
| anything else. */ | |
| for( x = 0; x < emacNUM_BUFFERS; x++ ) | |
| { | |
| if( ucBufferInUse[ x ] == pdFALSE ) | |
| { | |
| ucBufferInUse[ x ] = pdTRUE; | |
| pucReturn = ( unsigned char * ) &( xEthernetBuffers[ x ][ 0 ] ); | |
| break; | |
| } | |
| } | |
| /* Was a buffer found? */ | |
| if( pucReturn == NULL ) | |
| { | |
| ulAttempts++; | |
| if( ulAttempts >= emacBUFFER_WAIT_ATTEMPTS ) | |
| { | |
| break; | |
| } | |
| /* Wait then look again. */ | |
| vTaskDelay( emacBUFFER_WAIT_DELAY_ms ); | |
| } | |
| } | |
| return pucReturn; | |
| } | |
| /*-----------------------------------------------------------*/ | |
| static void prvResetEverything( void ) | |
| { | |
| /* Temporary code just to see if this gets called. This function has not | |
| been implemented. */ | |
| portDISABLE_INTERRUPTS(); | |
| for( ;; ); | |
| } | |
| /*-----------------------------------------------------------*/ | |
| unsigned short usEMACRead( void ) | |
| { | |
| unsigned short usBytesReceived; | |
| usBytesReceived = prvCheckRxStatus(); | |
| usBytesReceived = __REVSH( usBytesReceived ); | |
| if( usBytesReceived > 0 ) | |
| { | |
| /* Mark the pxDescriptor buffer as free as uip_buf is going to be set to | |
| the buffer that contains the received data. */ | |
| prvReturnBuffer( uip_buf ); | |
| /* Point uip_buf to the data about to be processed. */ | |
| uip_buf = ( void * ) pxCurrentRxDesc->data; | |
| uip_buf = ( void * ) __REV( ( unsigned long ) uip_buf ); | |
| /* Allocate a new buffer to the descriptor, as uip_buf is now using it's | |
| old descriptor. */ | |
| pxCurrentRxDesc->data = ( uint8_t * ) prvGetNextBuffer(); | |
| pxCurrentRxDesc->data = ( uint8_t* ) __REV( ( unsigned long ) pxCurrentRxDesc->data ); | |
| /* Prepare the descriptor to go again. */ | |
| pxCurrentRxDesc->status |= RX_BD_E; | |
| /* Move onto the next buffer in the ring. */ | |
| ulRxDescriptorIndex++; | |
| if( ulRxDescriptorIndex >= emacNUM_RX_DESCRIPTORS ) | |
| { | |
| ulRxDescriptorIndex = 0UL; | |
| } | |
| pxCurrentRxDesc = &( xRxDescriptors[ ulRxDescriptorIndex ] ); | |
| /* Restart Ethernet if it has stopped */ | |
| ENET_RDAR = ENET_RDAR_RDAR_MASK; | |
| } | |
| return usBytesReceived; | |
| } | |
| /*-----------------------------------------------------------*/ | |
| static void prvReturnBuffer( unsigned char *pucBuffer ) | |
| { | |
| unsigned long ul; | |
| /* Return a buffer to the pool of free buffers. */ | |
| for( ul = 0; ul < emacNUM_BUFFERS; ul++ ) | |
| { | |
| if( &( xEthernetBuffers[ ul ][ 0 ] ) == ( void * ) pucBuffer ) | |
| { | |
| ucBufferInUse[ ul ] = pdFALSE; | |
| break; | |
| } | |
| } | |
| } | |
| /*-----------------------------------------------------------*/ | |
| static unsigned short prvCheckRxStatus( void ) | |
| { | |
| unsigned long usReturn = 0; | |
| if( ( pxCurrentRxDesc->status & RX_BD_E ) != 0 ) | |
| { | |
| /* Current descriptor is still active. */ | |
| } | |
| else | |
| { | |
| /* The descriptor contains a frame. Because of the size of the buffers | |
| the frame should always be complete. */ | |
| usReturn = pxCurrentRxDesc->length; | |
| } | |
| return usReturn; | |
| } | |
| /*-----------------------------------------------------------*/ | |
| void vEMAC_TxISRHandler( void ) | |
| { | |
| /* Clear the interrupt. */ | |
| ENET_EIR = ENET_EIR_TXF_MASK; | |
| /* Check the buffers have not already been freed in the first of the | |
| two Tx interrupts - which could potentially happen if the second Tx completed | |
| during the interrupt for the first Tx. */ | |
| if( xTxDescriptors[ 0 ].data != NULL ) | |
| { | |
| if( ( ( xTxDescriptors[ 0 ].status & TX_BD_R ) == 0 ) && ( ( xTxDescriptors[ 0 ].status & TX_BD_R ) == 0 ) ) | |
| { | |
| configASSERT( xTxDescriptors[ 0 ].data == xTxDescriptors[ 1 ].data ); | |
| xTxDescriptors[ 0 ].data = ( uint8_t* ) __REV( ( unsigned long ) xTxDescriptors[ 0 ].data ); | |
| prvReturnBuffer( xTxDescriptors[ 0 ].data ); | |
| /* Just to mark the fact that the buffer has already been released. */ | |
| xTxDescriptors[ 0 ].data = NULL; | |
| } | |
| } | |
| } | |
| /*-----------------------------------------------------------*/ | |
| void vEMAC_RxISRHandler( void ) | |
| { | |
| const unsigned long ulRxEvent = uipETHERNET_RX_EVENT; | |
| long lHigherPriorityTaskWoken = pdFALSE; | |
| extern QueueHandle_t xEMACEventQueue; | |
| /* Clear the interrupt. */ | |
| ENET_EIR = ENET_EIR_RXF_MASK; | |
| /* An Ethernet Rx event has occurred. */ | |
| xQueueSendFromISR( xEMACEventQueue, &ulRxEvent, &lHigherPriorityTaskWoken ); | |
| portEND_SWITCHING_ISR( lHigherPriorityTaskWoken ); | |
| } | |
| /*-----------------------------------------------------------*/ | |
| void vEMAC_ErrorISRHandler( void ) | |
| { | |
| /* Clear the interrupt. */ | |
| ENET_EIR = ENET_EIR & ENET_EIMR; | |
| /* Attempt recovery. Not very sophisticated. */ | |
| prvInitialiseDescriptors(); | |
| ENET_RDAR = ENET_RDAR_RDAR_MASK; | |
| } | |
| /*-----------------------------------------------------------*/ | |