1 /* Kernel includes. */
\r
2 #include "FreeRTOS.h"
\r
10 #include "eth_phy.h"
\r
15 #include "uip_arp.h"
\r
17 /*-----------------------------------------------------------*/
\r
19 /* FEC hardware specifics. */
\r
20 #define MCF_FEC_MSCR_MII_SPEED(x) (((x)&0x3F)<<0x1)
\r
21 #define MCF_FEC_RDAR_R_DES_ACTIVE ( 0x1000000 )
\r
22 #define MCF_FEC_TDAR_X_DES_ACTIVE ( 0x1000000 )
\r
24 /* PHY hardware specifics. */
\r
25 #define PHY_STATUS ( 16 )
\r
26 #define PHY_DUPLEX_STATUS ( 4 )
\r
28 /* Delay between polling the PHY to see if a link has been established. */
\r
29 #define fecLINK_DELAY ( 500 / portTICK_PERIOD_MS )
\r
31 /* Very short delay to use when waiting for the Tx to finish with a buffer if
\r
32 we run out of Rx buffers. */
\r
33 #define fecMINIMAL_DELAY ( 3 / portTICK_PERIOD_MS )
\r
35 /* Don't block to wait for a buffer more than this many times. */
\r
36 #define uipBUFFER_WAIT_ATTEMPTS ( 30 )
\r
38 /* The Tx re-uses the Rx buffers and only has one descriptor. */
\r
39 #define fecNUM_TX_DESCRIPTORS ( 1 )
\r
41 /* The total number of buffers available, which should be greater than the
\r
42 number of Rx descriptors. */
\r
43 #define fecNUM_BUFFERS ( configNUM_FEC_RX_DESCRIPTORS + 2 )
\r
45 /*-----------------------------------------------------------*/
\r
48 * Return an unused buffer to the pool of free buffers.
\r
50 static void prvReturnBuffer( unsigned char *pucBuffer );
\r
53 * Find and return the next buffer that is not in use by anything else.
\r
55 static unsigned char *prvGetFreeBuffer( void );
\r
56 /*-----------------------------------------------------------*/
\r
58 /* The semaphore used to wake the uIP task when data arrives. */
\r
59 SemaphoreHandle_t xFECSemaphore = NULL;
\r
61 /* The buffer used by the uIP stack. In this case the pointer is used to
\r
62 point to one of the Rx buffers to avoid having to copy the Rx buffer into
\r
64 unsigned char *uip_buf;
\r
66 /* The DMA descriptors. These are char arrays to allow us to align them
\r
68 static unsigned char xFECTxDescriptors_unaligned[ ( fecNUM_TX_DESCRIPTORS * sizeof( FECBD ) ) + 16 ];
\r
69 static unsigned char xFECRxDescriptors_unaligned[ ( configNUM_FEC_RX_DESCRIPTORS * sizeof( FECBD ) ) + 16 ];
\r
70 static FECBD *pxFECTxDescriptor;
\r
71 static FECBD *xFECRxDescriptors;
\r
73 /* The DMA buffer. This is a char arrays to allow it to be aligned correctly. */
\r
74 static unsigned char ucFECRxBuffers[ ( fecNUM_BUFFERS * configFEC_BUFFER_SIZE ) + 16 ];
\r
76 /* Index to the next descriptor to be inspected for received data. */
\r
77 static unsigned long ulNextRxDescriptor = 0;
\r
79 /* Contains the start address of each Rx buffer, after it has been correctly
\r
81 static unsigned char *pucAlignedBufferStartAddresses[ fecNUM_BUFFERS ] = { 0 };
\r
83 /* Each ucBufferInUse index corresponds to a position in the same index in the
\r
84 pucAlignedBufferStartAddresses array. If the index contains a 1 then the
\r
85 buffer within pucAlignedBufferStartAddresses is in use, if it contains a 0 then
\r
86 the buffer is free. */
\r
87 static unsigned char ucBufferInUse[ fecNUM_BUFFERS ] = { 0 };
\r
89 /*-----------------------------------------------------------*/
\r
91 /********************************************************************/
\r
93 * Write a value to a PHY's MII register.
\r
97 * phy_addr Address of the PHY.
\r
98 * reg_addr Address of the register in the PHY.
\r
99 * data Data to be written to the PHY register.
\r
105 * Please refer to your PHY manual for registers and their meanings.
\r
106 * mii_write() polls for the FEC's MII interrupt event and clears it.
\r
107 * If after a suitable amount of time the event isn't triggered, a
\r
108 * value of 0 is returned.
\r
110 static int fec_mii_write( int phy_addr, int reg_addr, int data )
\r
113 unsigned long eimr;
\r
115 /* Clear the MII interrupt bit */
\r
118 /* Mask the MII interrupt */
\r
122 /* Write to the MII Management Frame Register to kick-off the MII write */
\r
123 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
125 /* Poll for the MII interrupt (interrupt should be masked) */
\r
126 for (timeout = 0; timeout < FEC_MII_TIMEOUT; timeout++)
\r
134 if( timeout == FEC_MII_TIMEOUT )
\r
139 /* Clear the MII interrupt bit */
\r
142 /* Restore the EIMR */
\r
148 /********************************************************************/
\r
150 * Read a value from a PHY's MII register.
\r
154 * phy_addr Address of the PHY.
\r
155 * reg_addr Address of the register in the PHY.
\r
156 * data Pointer to storage for the Data to be read
\r
157 * from the PHY register (passed by reference)
\r
163 * Please refer to your PHY manual for registers and their meanings.
\r
164 * mii_read() polls for the FEC's MII interrupt event and clears it.
\r
165 * If after a suitable amount of time the event isn't triggered, a
\r
166 * value of 0 is returned.
\r
168 static int fec_mii_read( int phy_addr, int reg_addr, unsigned short* data )
\r
171 unsigned long eimr;
\r
173 /* Clear the MII interrupt bit */
\r
176 /* Mask the MII interrupt */
\r
180 /* Write to the MII Management Frame Register to kick-off the MII read */
\r
181 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
183 /* Poll for the MII interrupt (interrupt should be masked) */
\r
184 for (timeout = 0; timeout < FEC_MII_TIMEOUT; timeout++)
\r
192 if(timeout == FEC_MII_TIMEOUT)
\r
197 /* Clear the MII interrupt bit */
\r
200 /* Restore the EIMR */
\r
203 *data = (unsigned short)(MMFR & 0x0000FFFF);
\r
209 /********************************************************************/
\r
211 * Generate the hash table settings for the given address
\r
214 * addr 48-bit (6 byte) Address to generate the hash for
\r
217 * The 6 most significant bits of the 32-bit CRC result
\r
219 static unsigned char fec_hash_address( const unsigned char* addr )
\r
222 unsigned char byte;
\r
231 if((byte & 0x01)^(crc & 0x01))
\r
234 crc = crc ^ 0xEDB88320;
\r
245 return (unsigned char)(crc >> 26);
\r
248 /********************************************************************/
\r
250 * Set the Physical (Hardware) Address and the Individual Address
\r
251 * Hash in the selected FEC
\r
255 * pa Physical (Hardware) Address for the selected FEC
\r
257 static void fec_set_address( const unsigned char *pa )
\r
262 * Set the Physical Address
\r
264 PALR = (unsigned long)((pa[0]<<24) | (pa[1]<<16) | (pa[2]<<8) | pa[3]);
\r
265 PAUR = (unsigned long)((pa[4]<<24) | (pa[5]<<16));
\r
268 * Calculate and set the hash for given Physical Address
\r
269 * in the Individual Address Hash registers
\r
271 crc = fec_hash_address(pa);
\r
274 IAUR |= (unsigned long)(1 << (crc - 32));
\r
278 IALR |= (unsigned long)(1 << crc);
\r
281 /*-----------------------------------------------------------*/
\r
283 static void prvInitialiseFECBuffers( void )
\r
285 unsigned portBASE_TYPE ux;
\r
286 unsigned char *pcBufPointer;
\r
288 /* Set the pointer to a correctly aligned address. */
\r
289 pcBufPointer = &( xFECTxDescriptors_unaligned[ 0 ] );
\r
290 while( ( ( unsigned long ) pcBufPointer & 0x0fUL ) != 0 )
\r
295 pxFECTxDescriptor = ( FECBD * ) pcBufPointer;
\r
297 /* Likewise the pointer to the Rx descriptor. */
\r
298 pcBufPointer = &( xFECRxDescriptors_unaligned[ 0 ] );
\r
299 while( ( ( unsigned long ) pcBufPointer & 0x0fUL ) != 0 )
\r
304 xFECRxDescriptors = ( FECBD * ) pcBufPointer;
\r
306 /* There is no Tx buffer as the Rx buffer is reused. */
\r
307 pxFECTxDescriptor->length = 0;
\r
308 pxFECTxDescriptor->status = 0;
\r
310 /* Align the Rx buffers. */
\r
311 pcBufPointer = &( ucFECRxBuffers[ 0 ] );
\r
312 while( ( ( unsigned long ) pcBufPointer & 0x0fUL ) != 0 )
\r
317 /* Then fill in the Rx descriptors. */
\r
318 for( ux = 0; ux < configNUM_FEC_RX_DESCRIPTORS; ux++ )
\r
320 xFECRxDescriptors[ ux ].status = RX_BD_E;
\r
321 xFECRxDescriptors[ ux ].length = configFEC_BUFFER_SIZE;
\r
322 xFECRxDescriptors[ ux ].data = pcBufPointer;
\r
324 /* Note the start address of the buffer now that it is correctly
\r
326 pucAlignedBufferStartAddresses[ ux ] = pcBufPointer;
\r
328 /* The buffer is in use by the descriptor. */
\r
329 ucBufferInUse[ ux ] = pdTRUE;
\r
331 pcBufPointer += configFEC_BUFFER_SIZE;
\r
334 /* Note the start address of the last buffer as one more buffer is
\r
335 allocated than there are Rx descriptors. */
\r
336 pucAlignedBufferStartAddresses[ ux ] = pcBufPointer;
\r
338 /* Set uip_buf to point to the last buffer. */
\r
339 uip_buf = pcBufPointer;
\r
340 ucBufferInUse[ ux ] = pdTRUE;
\r
342 /* Set the wrap bit in the last descriptors to form a ring. */
\r
343 xFECRxDescriptors[ configNUM_FEC_RX_DESCRIPTORS - 1 ].status |= RX_BD_W;
\r
345 /* We start with descriptor 0. */
\r
346 ulNextRxDescriptor = 0;
\r
348 /*-----------------------------------------------------------*/
\r
350 void vInitFEC( void )
\r
352 unsigned short usData;
\r
353 struct uip_eth_addr xAddr;
\r
354 const unsigned char ucMACAddress[6] =
\r
356 configMAC_0, configMAC_1,configMAC_2,configMAC_3,configMAC_4,configMAC_5
\r
359 prvInitialiseFECBuffers();
\r
361 /* Create the semaphore used to wake the uIP task when data arrives. */
\r
362 vSemaphoreCreateBinary( xFECSemaphore );
\r
364 /* Set the MAC address within the stack. */
\r
365 for( usData = 0; usData < 6; usData++ )
\r
367 xAddr.addr[ usData ] = ucMACAddress[ usData ];
\r
369 uip_setethaddr( xAddr );
\r
371 /* Set the Reset bit and clear the Enable bit */
\r
374 /* Enable the clock. */
\r
375 SCGC4 |= SCGC4_FEC_MASK;
\r
377 /* Wait at least 8 clock cycles */
\r
378 for( usData = 0; usData < 10; usData++ )
\r
383 /* Set MII speed to 2.5MHz. */
\r
384 MSCR = MCF_FEC_MSCR_MII_SPEED( ( ( configCPU_CLOCK_HZ / 1000000 ) / 5 ) + 1 );
\r
387 * Make sure the external interface signals are enabled
\r
397 /* Set all pins to full drive with no filter. */
\r
406 /* Can we talk to the PHY? */
\r
409 vTaskDelay( fecLINK_DELAY );
\r
411 fec_mii_read( configPHY_ADDRESS, PHY_PHYIDR1, &usData );
\r
413 } while( usData == 0xffff );
\r
415 /* Start auto negotiate. */
\r
416 fec_mii_write( configPHY_ADDRESS, PHY_BMCR, ( PHY_BMCR_AN_RESTART | PHY_BMCR_AN_ENABLE ) );
\r
418 /* Wait for auto negotiate to complete. */
\r
421 vTaskDelay( fecLINK_DELAY );
\r
422 fec_mii_read( configPHY_ADDRESS, PHY_BMSR, &usData );
\r
424 } while( !( usData & PHY_BMSR_AN_COMPLETE ) );
\r
426 /* When we get here we have a link - find out what has been negotiated. */
\r
428 fec_mii_read( configPHY_ADDRESS, PHY_STATUS, &usData );
\r
430 /* Setup half or full duplex. */
\r
431 if( usData & PHY_DUPLEX_STATUS )
\r
433 RCR &= (unsigned long)~RCR_DRT;
\r
439 TCR &= (unsigned long)~TCR_FDEN;
\r
442 /* Clear the Individual and Group Address Hash registers */
\r
448 /* Set the Physical Address for the selected FEC */
\r
449 fec_set_address( ucMACAddress );
\r
451 /* Set Rx Buffer Size */
\r
452 EMRBR = (unsigned short) configFEC_BUFFER_SIZE;
\r
454 /* Point to the start of the circular Rx buffer descriptor queue */
\r
455 ERDSR = ( volatile unsigned long ) &( xFECRxDescriptors[ 0 ] );
\r
457 /* Point to the start of the circular Tx buffer descriptor queue */
\r
458 ETSDR = ( volatile unsigned long ) pxFECTxDescriptor;
\r
460 /* Clear all FEC interrupt events */
\r
461 EIR = ( unsigned long ) -1;
\r
463 /* Various mode/status setup. */
\r
465 RCR_MAX_FL = configFEC_BUFFER_SIZE;
\r
468 #if( configUSE_PROMISCUOUS_MODE == 1 )
\r
474 /* Enable interrupts. */
\r
475 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
477 /* Enable the MAC itself. */
\r
478 ECR = ECR_ETHER_EN_MASK;
\r
480 /* Indicate that there have been empty receive buffers produced */
\r
481 RDAR = MCF_FEC_RDAR_R_DES_ACTIVE;
\r
483 /*-----------------------------------------------------------*/
\r
485 unsigned long ulFECRx( void )
\r
487 unsigned long ulLen = 0UL;
\r
489 /* Is a buffer ready? */
\r
490 if( ( xFECRxDescriptors[ ulNextRxDescriptor ].status & RX_BD_E ) == 0 )
\r
492 /* uip_buf is about to be set to a new buffer, so return the buffer it
\r
493 is already pointing to. */
\r
494 prvReturnBuffer( uip_buf );
\r
496 /* Obtain the size of the packet and put it into the "len" variable. */
\r
497 ulLen = xFECRxDescriptors[ ulNextRxDescriptor ].length;
\r
498 uip_buf = xFECRxDescriptors[ ulNextRxDescriptor ].data;
\r
500 /* The buffer that this descriptor was using is now in use by the
\r
501 TCP/IP stack, so allocate it a new buffer. */
\r
502 xFECRxDescriptors[ ulNextRxDescriptor ].data = prvGetFreeBuffer();
\r
504 /* Doing this here could cause corruption! */
\r
505 xFECRxDescriptors[ ulNextRxDescriptor ].status |= RX_BD_E;
\r
507 portENTER_CRITICAL();
\r
509 ulNextRxDescriptor++;
\r
510 if( ulNextRxDescriptor >= configNUM_FEC_RX_DESCRIPTORS )
\r
512 ulNextRxDescriptor = 0;
\r
515 portEXIT_CRITICAL();
\r
517 /* Tell the DMA a new buffer is available. */
\r
518 RDAR = MCF_FEC_RDAR_R_DES_ACTIVE;
\r
523 /*-----------------------------------------------------------*/
\r
525 void vFECTx( void )
\r
527 /* When we get here the Tx descriptor should show as having completed. */
\r
528 while( pxFECTxDescriptor->status & TX_BD_R )
\r
530 vTaskDelay( fecMINIMAL_DELAY );
\r
533 portENTER_CRITICAL();
\r
535 /* To maintain the zero copy implementation, point the Tx descriptor
\r
536 to the data from the Rx buffer. */
\r
537 pxFECTxDescriptor->data = uip_buf;
\r
539 /* Setup the buffer descriptor for transmission */
\r
540 pxFECTxDescriptor->length = uip_len;
\r
542 /* NB this assumes only one Tx descriptor! */
\r
543 pxFECTxDescriptor->status = ( TX_BD_R | TX_BD_L | TX_BD_TC | TX_BD_W );
\r
545 portEXIT_CRITICAL();
\r
547 /* Continue the Tx DMA task (in case it was waiting for a new TxBD) */
\r
548 TDAR = MCF_FEC_TDAR_X_DES_ACTIVE;
\r
550 /* uip_buf is being used by the Tx descriptor. Allocate a new buffer to
\r
552 uip_buf = prvGetFreeBuffer();
\r
554 /*-----------------------------------------------------------*/
\r
556 static void prvReturnBuffer( unsigned char *pucBuffer )
\r
560 /* Mark a buffer as free for use. */
\r
561 for( ul = 0; ul < fecNUM_BUFFERS; ul++ )
\r
563 if( pucAlignedBufferStartAddresses[ ul ] == pucBuffer )
\r
565 ucBufferInUse[ ul ] = pdFALSE;
\r
570 /*-----------------------------------------------------------*/
\r
572 static unsigned char *prvGetFreeBuffer( void )
\r
575 unsigned char *pucReturn = NULL;
\r
576 unsigned long ulAttempts = 0;
\r
578 while( pucReturn == NULL )
\r
580 /* Look through the buffers to find one that is not in use by
\r
582 for( x = 0; x < fecNUM_BUFFERS; x++ )
\r
584 if( ucBufferInUse[ x ] == pdFALSE )
\r
586 ucBufferInUse[ x ] = pdTRUE;
\r
587 pucReturn = pucAlignedBufferStartAddresses[ x ];
\r
592 /* Was a buffer found? */
\r
593 if( pucReturn == NULL )
\r
597 if( ulAttempts >= uipBUFFER_WAIT_ATTEMPTS )
\r
602 /* Wait then look again. */
\r
603 vTaskDelay( fecMINIMAL_DELAY );
\r
609 /*-----------------------------------------------------------*/
\r
611 void interrupt 86 vFECISRHandler( void )
\r
613 unsigned long ulEvent;
\r
614 portBASE_TYPE xHighPriorityTaskWoken = pdFALSE;
\r
616 /* Determine the cause of the interrupt. */
\r
617 ulEvent = EIR & EIMR;
\r
620 if( ulEvent & EIR_RXF_MASK )
\r
622 /* A packet has been received. Wake the handler task in case it is
\r
624 xSemaphoreGiveFromISR( xFECSemaphore, &xHighPriorityTaskWoken );
\r
627 if( ulEvent & EIR_TXF_MASK )
\r
629 /* The Tx has completed. Mark the buffer it was using as free again. */
\r
630 prvReturnBuffer( pxFECTxDescriptor->data );
\r
631 pxFECTxDescriptor->data = NULL;
\r
634 if (ulEvent & ( EIR_UN_MASK | EIR_RL_MASK | EIR_LC_MASK | EIR_EBERR_MASK | EIR_BABT_MASK | EIR_BABR_MASK | EIR_HBERR_MASK ) )
\r
636 /* Sledge hammer error handling. */
\r
637 prvInitialiseFECBuffers();
\r
638 RDAR = MCF_FEC_RDAR_R_DES_ACTIVE;
\r
641 portEND_SWITCHING_ISR( xHighPriorityTaskWoken );
\r