2 This file is part of the FreeRTOS.org distribution.
\r
4 FreeRTOS.org is free software; you can redistribute it and/or modify it
\r
5 under the terms of the GNU General Public License (version 2) as published
\r
6 by the Free Software Foundation and modified by the FreeRTOS exception.
\r
8 FreeRTOS.org is distributed in the hope that it will be useful, but WITHOUT
\r
9 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
\r
10 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
\r
13 You should have received a copy of the GNU General Public License along
\r
14 with FreeRTOS.org; if not, write to the Free Software Foundation, Inc., 59
\r
15 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
\r
17 A special exception to the GPL is included to allow you to distribute a
\r
18 combined work that includes FreeRTOS.org without being obliged to provide
\r
19 the source code for any proprietary components. See the licensing section
\r
20 of http://www.FreeRTOS.org for full details.
\r
23 ***************************************************************************
\r
25 * Get the FreeRTOS eBook! See http://www.FreeRTOS.org/Documentation *
\r
27 * This is a concise, step by step, 'hands on' guide that describes both *
\r
28 * general multitasking concepts and FreeRTOS specifics. It presents and *
\r
29 * explains numerous examples that are written using the FreeRTOS API. *
\r
30 * Full source code for all the examples is provided in an accompanying *
\r
33 ***************************************************************************
\r
37 Please ensure to read the configuration and relevant port sections of the
\r
38 online documentation.
\r
40 ***************************************************************************
\r
42 * Having a problem? Start by reading the FAQ "My application does *
\r
43 * not run, what could be wrong? *
\r
45 * http://www.FreeRTOS.org/FAQHelp.html *
\r
47 ***************************************************************************
\r
50 http://www.FreeRTOS.org - Documentation, training, latest information,
\r
51 license and contact details.
\r
53 http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,
\r
54 including FreeRTOS+Trace - an indispensable productivity tool.
\r
56 Real Time Engineers ltd license FreeRTOS to High Integrity Systems, who sell
\r
57 the code with commercial support, indemnification, and middleware, under
\r
58 the OpenRTOS brand: http://www.OpenRTOS.com. High Integrity Systems also
\r
59 provide a safety engineered and independently SIL3 certified version under
\r
60 the SafeRTOS brand: http://www.SafeRTOS.com.
\r
64 /* Kernel includes. */
\r
65 #include "FreeRTOS.h"
\r
69 /* Demo includes. */
\r
73 #include "eth_phy.h"
\r
78 #include "uip_arp.h"
\r
80 /*-----------------------------------------------------------*/
\r
82 /* FEC hardware specifics. */
\r
83 #define MCF_FEC_MSCR_MII_SPEED(x) (((x)&0x3F)<<0x1)
\r
84 #define MCF_FEC_RDAR_R_DES_ACTIVE ( 0x1000000 )
\r
85 #define MCF_FEC_TDAR_X_DES_ACTIVE ( 0x1000000 )
\r
87 /* PHY hardware specifics. */
\r
88 #define PHY_STATUS ( 16 )
\r
89 #define PHY_DUPLEX_STATUS ( 4 )
\r
91 /* Delay between polling the PHY to see if a link has been established. */
\r
92 #define fecLINK_DELAY ( 500 / portTICK_PERIOD_MS )
\r
94 /* Very short delay to use when waiting for the Tx to finish with a buffer if
\r
95 we run out of Rx buffers. */
\r
96 #define fecMINIMAL_DELAY ( 3 / portTICK_PERIOD_MS )
\r
98 /* Don't block to wait for a buffer more than this many times. */
\r
99 #define uipBUFFER_WAIT_ATTEMPTS ( 30 )
\r
101 /* The Tx re-uses the Rx buffers and only has one descriptor. */
\r
102 #define fecNUM_TX_DESCRIPTORS ( 1 )
\r
104 /* The total number of buffers available, which should be greater than the
\r
105 number of Rx descriptors. */
\r
106 #define fecNUM_BUFFERS ( configNUM_FEC_RX_DESCRIPTORS + 2 )
\r
108 /*-----------------------------------------------------------*/
\r
111 * Return an unused buffer to the pool of free buffers.
\r
113 static void prvReturnBuffer( unsigned char *pucBuffer );
\r
116 * Find and return the next buffer that is not in use by anything else.
\r
118 static unsigned char *prvGetFreeBuffer( void );
\r
119 /*-----------------------------------------------------------*/
\r
121 /* The semaphore used to wake the uIP task when data arrives. */
\r
122 SemaphoreHandle_t xFECSemaphore = NULL;
\r
124 /* The buffer used by the uIP stack. In this case the pointer is used to
\r
125 point to one of the Rx buffers to avoid having to copy the Rx buffer into
\r
127 unsigned char *uip_buf;
\r
129 /* The DMA descriptors. These are char arrays to allow us to align them
\r
131 static unsigned char xFECTxDescriptors_unaligned[ ( fecNUM_TX_DESCRIPTORS * sizeof( FECBD ) ) + 16 ];
\r
132 static unsigned char xFECRxDescriptors_unaligned[ ( configNUM_FEC_RX_DESCRIPTORS * sizeof( FECBD ) ) + 16 ];
\r
133 static FECBD *pxFECTxDescriptor;
\r
134 static FECBD *xFECRxDescriptors;
\r
136 /* The DMA buffer. This is a char arrays to allow it to be aligned correctly. */
\r
137 static unsigned char ucFECRxBuffers[ ( fecNUM_BUFFERS * configFEC_BUFFER_SIZE ) + 16 ];
\r
139 /* Index to the next descriptor to be inspected for received data. */
\r
140 static unsigned long ulNextRxDescriptor = 0;
\r
142 /* Contains the start address of each Rx buffer, after it has been correctly
\r
144 static unsigned char *pucAlignedBufferStartAddresses[ fecNUM_BUFFERS ] = { 0 };
\r
146 /* Each ucBufferInUse index corresponds to a position in the same index in the
\r
147 pucAlignedBufferStartAddresses array. If the index contains a 1 then the
\r
148 buffer within pucAlignedBufferStartAddresses is in use, if it contains a 0 then
\r
149 the buffer is free. */
\r
150 static unsigned char ucBufferInUse[ fecNUM_BUFFERS ] = { 0 };
\r
152 /*-----------------------------------------------------------*/
\r
154 /********************************************************************/
\r
156 * Write a value to a PHY's MII register.
\r
160 * phy_addr Address of the PHY.
\r
161 * reg_addr Address of the register in the PHY.
\r
162 * data Data to be written to the PHY register.
\r
168 * Please refer to your PHY manual for registers and their meanings.
\r
169 * mii_write() polls for the FEC's MII interrupt event and clears it.
\r
170 * If after a suitable amount of time the event isn't triggered, a
\r
171 * value of 0 is returned.
\r
173 static int fec_mii_write( int phy_addr, int reg_addr, int data )
\r
176 unsigned long eimr;
\r
178 /* Clear the MII interrupt bit */
\r
181 /* Mask the MII interrupt */
\r
185 /* Write to the MII Management Frame Register to kick-off the MII write */
\r
186 MMFR = ( unsigned long ) ( FEC_MMFR_ST_01 | FEC_MMFR_OP_WRITE | FEC_MMFR_PA(phy_addr) | FEC_MMFR_RA(reg_addr) | FEC_MMFR_TA_10 | FEC_MMFR_DATA( data ) );
\r
188 /* Poll for the MII interrupt (interrupt should be masked) */
\r
189 for (timeout = 0; timeout < FEC_MII_TIMEOUT; timeout++)
\r
197 if( timeout == FEC_MII_TIMEOUT )
\r
202 /* Clear the MII interrupt bit */
\r
205 /* Restore the EIMR */
\r
211 /********************************************************************/
\r
213 * Read a value from a PHY's MII register.
\r
217 * phy_addr Address of the PHY.
\r
218 * reg_addr Address of the register in the PHY.
\r
219 * data Pointer to storage for the Data to be read
\r
220 * from the PHY register (passed by reference)
\r
226 * Please refer to your PHY manual for registers and their meanings.
\r
227 * mii_read() polls for the FEC's MII interrupt event and clears it.
\r
228 * If after a suitable amount of time the event isn't triggered, a
\r
229 * value of 0 is returned.
\r
231 static int fec_mii_read( int phy_addr, int reg_addr, unsigned short* data )
\r
234 unsigned long eimr;
\r
236 /* Clear the MII interrupt bit */
\r
239 /* Mask the MII interrupt */
\r
243 /* Write to the MII Management Frame Register to kick-off the MII read */
\r
244 MMFR = ( unsigned long ) ( FEC_MMFR_ST_01 | FEC_MMFR_OP_READ | FEC_MMFR_PA(phy_addr) | FEC_MMFR_RA(reg_addr) | FEC_MMFR_TA_10 );
\r
246 /* Poll for the MII interrupt (interrupt should be masked) */
\r
247 for (timeout = 0; timeout < FEC_MII_TIMEOUT; timeout++)
\r
255 if(timeout == FEC_MII_TIMEOUT)
\r
260 /* Clear the MII interrupt bit */
\r
263 /* Restore the EIMR */
\r
266 *data = (unsigned short)(MMFR & 0x0000FFFF);
\r
272 /********************************************************************/
\r
274 * Generate the hash table settings for the given address
\r
277 * addr 48-bit (6 byte) Address to generate the hash for
\r
280 * The 6 most significant bits of the 32-bit CRC result
\r
282 static unsigned char fec_hash_address( const unsigned char* addr )
\r
285 unsigned char byte;
\r
294 if((byte & 0x01)^(crc & 0x01))
\r
297 crc = crc ^ 0xEDB88320;
\r
308 return (unsigned char)(crc >> 26);
\r
311 /********************************************************************/
\r
313 * Set the Physical (Hardware) Address and the Individual Address
\r
314 * Hash in the selected FEC
\r
318 * pa Physical (Hardware) Address for the selected FEC
\r
320 static void fec_set_address( const unsigned char *pa )
\r
325 * Set the Physical Address
\r
327 PALR = (unsigned long)((pa[0]<<24) | (pa[1]<<16) | (pa[2]<<8) | pa[3]);
\r
328 PAUR = (unsigned long)((pa[4]<<24) | (pa[5]<<16));
\r
331 * Calculate and set the hash for given Physical Address
\r
332 * in the Individual Address Hash registers
\r
334 crc = fec_hash_address(pa);
\r
337 IAUR |= (unsigned long)(1 << (crc - 32));
\r
341 IALR |= (unsigned long)(1 << crc);
\r
344 /*-----------------------------------------------------------*/
\r
346 static void prvInitialiseFECBuffers( void )
\r
348 unsigned portBASE_TYPE ux;
\r
349 unsigned char *pcBufPointer;
\r
351 /* Set the pointer to a correctly aligned address. */
\r
352 pcBufPointer = &( xFECTxDescriptors_unaligned[ 0 ] );
\r
353 while( ( ( unsigned long ) pcBufPointer & 0x0fUL ) != 0 )
\r
358 pxFECTxDescriptor = ( FECBD * ) pcBufPointer;
\r
360 /* Likewise the pointer to the Rx descriptor. */
\r
361 pcBufPointer = &( xFECRxDescriptors_unaligned[ 0 ] );
\r
362 while( ( ( unsigned long ) pcBufPointer & 0x0fUL ) != 0 )
\r
367 xFECRxDescriptors = ( FECBD * ) pcBufPointer;
\r
369 /* There is no Tx buffer as the Rx buffer is reused. */
\r
370 pxFECTxDescriptor->length = 0;
\r
371 pxFECTxDescriptor->status = 0;
\r
373 /* Align the Rx buffers. */
\r
374 pcBufPointer = &( ucFECRxBuffers[ 0 ] );
\r
375 while( ( ( unsigned long ) pcBufPointer & 0x0fUL ) != 0 )
\r
380 /* Then fill in the Rx descriptors. */
\r
381 for( ux = 0; ux < configNUM_FEC_RX_DESCRIPTORS; ux++ )
\r
383 xFECRxDescriptors[ ux ].status = RX_BD_E;
\r
384 xFECRxDescriptors[ ux ].length = configFEC_BUFFER_SIZE;
\r
385 xFECRxDescriptors[ ux ].data = pcBufPointer;
\r
387 /* Note the start address of the buffer now that it is correctly
\r
389 pucAlignedBufferStartAddresses[ ux ] = pcBufPointer;
\r
391 /* The buffer is in use by the descriptor. */
\r
392 ucBufferInUse[ ux ] = pdTRUE;
\r
394 pcBufPointer += configFEC_BUFFER_SIZE;
\r
397 /* Note the start address of the last buffer as one more buffer is
\r
398 allocated than there are Rx descriptors. */
\r
399 pucAlignedBufferStartAddresses[ ux ] = pcBufPointer;
\r
401 /* Set uip_buf to point to the last buffer. */
\r
402 uip_buf = pcBufPointer;
\r
403 ucBufferInUse[ ux ] = pdTRUE;
\r
405 /* Set the wrap bit in the last descriptors to form a ring. */
\r
406 xFECRxDescriptors[ configNUM_FEC_RX_DESCRIPTORS - 1 ].status |= RX_BD_W;
\r
408 /* We start with descriptor 0. */
\r
409 ulNextRxDescriptor = 0;
\r
411 /*-----------------------------------------------------------*/
\r
413 void vInitFEC( void )
\r
415 unsigned short usData;
\r
416 struct uip_eth_addr xAddr;
\r
417 const unsigned char ucMACAddress[6] =
\r
419 configMAC_0, configMAC_1,configMAC_2,configMAC_3,configMAC_4,configMAC_5
\r
422 prvInitialiseFECBuffers();
\r
424 /* Create the semaphore used to wake the uIP task when data arrives. */
\r
425 vSemaphoreCreateBinary( xFECSemaphore );
\r
427 /* Set the MAC address within the stack. */
\r
428 for( usData = 0; usData < 6; usData++ )
\r
430 xAddr.addr[ usData ] = ucMACAddress[ usData ];
\r
432 uip_setethaddr( xAddr );
\r
434 /* Set the Reset bit and clear the Enable bit */
\r
437 /* Enable the clock. */
\r
438 SCGC4 |= SCGC4_FEC_MASK;
\r
440 /* Wait at least 8 clock cycles */
\r
441 for( usData = 0; usData < 10; usData++ )
\r
446 /* Set MII speed to 2.5MHz. */
\r
447 MSCR = MCF_FEC_MSCR_MII_SPEED( ( ( configCPU_CLOCK_HZ / 1000000 ) / 5 ) + 1 );
\r
450 * Make sure the external interface signals are enabled
\r
460 /* Set all pins to full drive with no filter. */
\r
469 /* Can we talk to the PHY? */
\r
472 vTaskDelay( fecLINK_DELAY );
\r
474 fec_mii_read( configPHY_ADDRESS, PHY_PHYIDR1, &usData );
\r
476 } while( usData == 0xffff );
\r
478 /* Start auto negotiate. */
\r
479 fec_mii_write( configPHY_ADDRESS, PHY_BMCR, ( PHY_BMCR_AN_RESTART | PHY_BMCR_AN_ENABLE ) );
\r
481 /* Wait for auto negotiate to complete. */
\r
484 vTaskDelay( fecLINK_DELAY );
\r
485 fec_mii_read( configPHY_ADDRESS, PHY_BMSR, &usData );
\r
487 } while( !( usData & PHY_BMSR_AN_COMPLETE ) );
\r
489 /* When we get here we have a link - find out what has been negotiated. */
\r
491 fec_mii_read( configPHY_ADDRESS, PHY_STATUS, &usData );
\r
493 /* Setup half or full duplex. */
\r
494 if( usData & PHY_DUPLEX_STATUS )
\r
496 RCR &= (unsigned long)~RCR_DRT;
\r
502 TCR &= (unsigned long)~TCR_FDEN;
\r
505 /* Clear the Individual and Group Address Hash registers */
\r
511 /* Set the Physical Address for the selected FEC */
\r
512 fec_set_address( ucMACAddress );
\r
514 /* Set Rx Buffer Size */
\r
515 EMRBR = (unsigned short) configFEC_BUFFER_SIZE;
\r
517 /* Point to the start of the circular Rx buffer descriptor queue */
\r
518 ERDSR = ( volatile unsigned long ) &( xFECRxDescriptors[ 0 ] );
\r
520 /* Point to the start of the circular Tx buffer descriptor queue */
\r
521 ETSDR = ( volatile unsigned long ) pxFECTxDescriptor;
\r
523 /* Clear all FEC interrupt events */
\r
524 EIR = ( unsigned long ) -1;
\r
526 /* Various mode/status setup. */
\r
528 RCR_MAX_FL = configFEC_BUFFER_SIZE;
\r
531 #if( configUSE_PROMISCUOUS_MODE == 1 )
\r
537 /* Enable interrupts. */
\r
538 EIMR = EIR_TXF_MASK | EIMR_RXF_MASK | EIMR_RXB_MASK | EIMR_UN_MASK | EIMR_RL_MASK | EIMR_LC_MASK | EIMR_BABT_MASK | EIMR_BABR_MASK | EIMR_HBERR_MASK;
\r
540 /* Enable the MAC itself. */
\r
541 ECR = ECR_ETHER_EN_MASK;
\r
543 /* Indicate that there have been empty receive buffers produced */
\r
544 RDAR = MCF_FEC_RDAR_R_DES_ACTIVE;
\r
546 /*-----------------------------------------------------------*/
\r
548 unsigned long ulFECRx( void )
\r
550 unsigned long ulLen = 0UL;
\r
552 /* Is a buffer ready? */
\r
553 if( ( xFECRxDescriptors[ ulNextRxDescriptor ].status & RX_BD_E ) == 0 )
\r
555 /* uip_buf is about to be set to a new buffer, so return the buffer it
\r
556 is already pointing to. */
\r
557 prvReturnBuffer( uip_buf );
\r
559 /* Obtain the size of the packet and put it into the "len" variable. */
\r
560 ulLen = xFECRxDescriptors[ ulNextRxDescriptor ].length;
\r
561 uip_buf = xFECRxDescriptors[ ulNextRxDescriptor ].data;
\r
563 /* The buffer that this descriptor was using is now in use by the
\r
564 TCP/IP stack, so allocate it a new buffer. */
\r
565 xFECRxDescriptors[ ulNextRxDescriptor ].data = prvGetFreeBuffer();
\r
567 /* Doing this here could cause corruption! */
\r
568 xFECRxDescriptors[ ulNextRxDescriptor ].status |= RX_BD_E;
\r
570 portENTER_CRITICAL();
\r
572 ulNextRxDescriptor++;
\r
573 if( ulNextRxDescriptor >= configNUM_FEC_RX_DESCRIPTORS )
\r
575 ulNextRxDescriptor = 0;
\r
578 portEXIT_CRITICAL();
\r
580 /* Tell the DMA a new buffer is available. */
\r
581 RDAR = MCF_FEC_RDAR_R_DES_ACTIVE;
\r
586 /*-----------------------------------------------------------*/
\r
588 void vFECTx( void )
\r
590 /* When we get here the Tx descriptor should show as having completed. */
\r
591 while( pxFECTxDescriptor->status & TX_BD_R )
\r
593 vTaskDelay( fecMINIMAL_DELAY );
\r
596 portENTER_CRITICAL();
\r
598 /* To maintain the zero copy implementation, point the Tx descriptor
\r
599 to the data from the Rx buffer. */
\r
600 pxFECTxDescriptor->data = uip_buf;
\r
602 /* Setup the buffer descriptor for transmission */
\r
603 pxFECTxDescriptor->length = uip_len;
\r
605 /* NB this assumes only one Tx descriptor! */
\r
606 pxFECTxDescriptor->status = ( TX_BD_R | TX_BD_L | TX_BD_TC | TX_BD_W );
\r
608 portEXIT_CRITICAL();
\r
610 /* Continue the Tx DMA task (in case it was waiting for a new TxBD) */
\r
611 TDAR = MCF_FEC_TDAR_X_DES_ACTIVE;
\r
613 /* uip_buf is being used by the Tx descriptor. Allocate a new buffer to
\r
615 uip_buf = prvGetFreeBuffer();
\r
617 /*-----------------------------------------------------------*/
\r
619 static void prvReturnBuffer( unsigned char *pucBuffer )
\r
623 /* Mark a buffer as free for use. */
\r
624 for( ul = 0; ul < fecNUM_BUFFERS; ul++ )
\r
626 if( pucAlignedBufferStartAddresses[ ul ] == pucBuffer )
\r
628 ucBufferInUse[ ul ] = pdFALSE;
\r
633 /*-----------------------------------------------------------*/
\r
635 static unsigned char *prvGetFreeBuffer( void )
\r
638 unsigned char *pucReturn = NULL;
\r
639 unsigned long ulAttempts = 0;
\r
641 while( pucReturn == NULL )
\r
643 /* Look through the buffers to find one that is not in use by
\r
645 for( x = 0; x < fecNUM_BUFFERS; x++ )
\r
647 if( ucBufferInUse[ x ] == pdFALSE )
\r
649 ucBufferInUse[ x ] = pdTRUE;
\r
650 pucReturn = pucAlignedBufferStartAddresses[ x ];
\r
655 /* Was a buffer found? */
\r
656 if( pucReturn == NULL )
\r
660 if( ulAttempts >= uipBUFFER_WAIT_ATTEMPTS )
\r
665 /* Wait then look again. */
\r
666 vTaskDelay( fecMINIMAL_DELAY );
\r
672 /*-----------------------------------------------------------*/
\r
674 void interrupt 86 vFECISRHandler( void )
\r
676 unsigned long ulEvent;
\r
677 portBASE_TYPE xHighPriorityTaskWoken = pdFALSE;
\r
679 /* Determine the cause of the interrupt. */
\r
680 ulEvent = EIR & EIMR;
\r
683 if( ulEvent & EIR_RXF_MASK )
\r
685 /* A packet has been received. Wake the handler task in case it is
\r
687 xSemaphoreGiveFromISR( xFECSemaphore, &xHighPriorityTaskWoken );
\r
690 if( ulEvent & EIR_TXF_MASK )
\r
692 /* The Tx has completed. Mark the buffer it was using as free again. */
\r
693 prvReturnBuffer( pxFECTxDescriptor->data );
\r
694 pxFECTxDescriptor->data = NULL;
\r
697 if (ulEvent & ( EIR_UN_MASK | EIR_RL_MASK | EIR_LC_MASK | EIR_EBERR_MASK | EIR_BABT_MASK | EIR_BABR_MASK | EIR_HBERR_MASK ) )
\r
699 /* Sledge hammer error handling. */
\r
700 prvInitialiseFECBuffers();
\r
701 RDAR = MCF_FEC_RDAR_R_DES_ACTIVE;
\r
704 portEND_SWITCHING_ISR( xHighPriorityTaskWoken );
\r