1 /******************************************************************
\r
3 ***** Ver.: 1.0 *****
\r
4 ***** Date: 07/05/2001 *****
\r
5 ***** Auth: Andreas Dannenberg *****
\r
6 ***** HTWK Leipzig *****
\r
7 ***** university of applied sciences *****
\r
9 ***** Func: ethernet packet-driver for use with LAN- *****
\r
10 ***** controller CS8900 from Crystal/Cirrus Logic *****
\r
12 ***** Keil: Module modified for use with Philips *****
\r
13 ***** LPC2378 EMAC Ethernet controller *****
\r
15 ******************************************************************/
\r
17 /* Adapted from file originally written by Andreas Dannenberg. Supplied with permission. */
\r
18 #include "FreeRTOS.h"
\r
21 #include "LPC17xx_defs.h"
\r
22 #include "EthDev_LPC17xx.h"
\r
24 #define emacPINSEL2_VALUE 0x50150105
\r
26 #define emacWAIT_FOR_LINK_TO_ESTABLISH ( 500 / portTICK_RATE_MS )
\r
27 #define emacSHORT_DELAY ( 2 )
\r
29 #define emacLINK_ESTABLISHED ( 0x0001 )
\r
30 #define emacFULL_DUPLEX_ENABLED ( 0x0004 )
\r
31 #define emac10BASE_T_MODE ( 0x0002 )
\r
33 /* If no buffers are available, then wait this long before looking again.... */
\r
34 #define emacBUFFER_WAIT_DELAY ( 3 / portTICK_RATE_MS )
\r
36 /* ...and don't look more than this many times. */
\r
37 #define emacBUFFER_WAIT_ATTEMPTS ( 30 )
\r
39 #define emacTX_DESC_INDEX ( 0 )
\r
41 /* The semaphore used to wake the uIP task when data arives. */
\r
42 extern xSemaphoreHandle xEMACSemaphore;
\r
44 static unsigned short *rptr;
\r
45 static unsigned short *tptr;
\r
47 static void prvInitDescriptors( void );
\r
48 static void prvSetupEMACHardware( void );
\r
49 static void prvConfigurePHY( void );
\r
50 static long prvSetupLinkStatus( void );
\r
51 static unsigned char *prvGetNextBuffer( void );
\r
52 static void prvReturnBuffer( unsigned char *pucBuffer );
\r
54 /* Each ucBufferInUse index corresponds to a position in the same index in the
\r
55 ucMACBuffers array. If the index contains a 1 then the buffer within
\r
56 ucMACBuffers is in use, if it contains a 0 then the buffer is free. */
\r
57 static unsigned char ucBufferInUse[ ETH_NUM_BUFFERS ] = { pdFALSE };
\r
59 /* The uip_buffer is not a fixed array, but instead gets pointed to the buffers
\r
60 allocated within this file. */
\r
61 unsigned char * uip_buf;
\r
63 /* Store the length of the data being sent so the data can be sent twice. The
\r
64 value will be set back to 0 once the data has been sent twice. */
\r
65 static unsigned short usSendLen = 0;
\r
67 /*-----------------------------------------------------------*/
\r
69 int write_PHY( long lPhyReg, long lValue )
\r
71 const long lMaxTime = 10;
\r
74 MAC_MADR = DP83848C_DEF_ADR | lPhyReg;
\r
78 for( x = 0; x < lMaxTime; x++ )
\r
80 if( ( MAC_MIND & MIND_BUSY ) == 0 )
\r
82 /* Operation has finished. */
\r
86 vTaskDelay( emacSHORT_DELAY );
\r
98 /*-----------------------------------------------------------*/
\r
100 unsigned short read_PHY( unsigned char ucPhyReg, long *plStatus )
\r
103 const long lMaxTime = 10;
\r
105 MAC_MADR = DP83848C_DEF_ADR | ucPhyReg;
\r
106 MAC_MCMD = MCMD_READ;
\r
108 for( x = 0; x < lMaxTime; x++ )
\r
110 /* Operation has finished. */
\r
111 if( ( MAC_MIND & MIND_BUSY ) == 0 )
\r
116 vTaskDelay( emacSHORT_DELAY );
\r
121 if( x >= lMaxTime )
\r
123 *plStatus = pdFAIL;
\r
126 return( MAC_MRDD );
\r
128 /*-----------------------------------------------------------*/
\r
130 static unsigned char *prvGetNextBuffer( void )
\r
133 unsigned char *pucReturn = NULL;
\r
134 unsigned long ulAttempts = 0;
\r
136 while( pucReturn == NULL )
\r
138 /* Look through the buffers to find one that is not in use by
\r
140 for( x = 0; x < ETH_NUM_BUFFERS; x++ )
\r
142 if( ucBufferInUse[ x ] == pdFALSE )
\r
144 ucBufferInUse[ x ] = pdTRUE;
\r
145 pucReturn = ( unsigned char * ) ETH_BUF( x );
\r
150 /* Was a buffer found? */
\r
151 if( pucReturn == NULL )
\r
155 if( ulAttempts >= emacBUFFER_WAIT_ATTEMPTS )
\r
160 /* Wait then look again. */
\r
161 vTaskDelay( emacBUFFER_WAIT_DELAY );
\r
167 /*-----------------------------------------------------------*/
\r
169 static void prvInitDescriptors( void )
\r
171 long x, lNextBuffer = 0;
\r
173 for( x = 0; x < NUM_RX_FRAG; x++ )
\r
175 /* Allocate the next Ethernet buffer to this descriptor. */
\r
176 RX_DESC_PACKET( x ) = ETH_BUF( lNextBuffer );
\r
177 RX_DESC_CTRL( x ) = RCTRL_INT | ( ETH_FRAG_SIZE - 1 );
\r
178 RX_STAT_INFO( x ) = 0;
\r
179 RX_STAT_HASHCRC( x ) = 0;
\r
181 /* The Ethernet buffer is now in use. */
\r
182 ucBufferInUse[ lNextBuffer ] = pdTRUE;
\r
186 /* Set EMAC Receive Descriptor Registers. */
\r
187 MAC_RXDESCRIPTOR = RX_DESC_BASE;
\r
188 MAC_RXSTATUS = RX_STAT_BASE;
\r
189 MAC_RXDESCRIPTORNUM = NUM_RX_FRAG - 1;
\r
191 /* Rx Descriptors Point to 0 */
\r
192 MAC_RXCONSUMEINDEX = 0;
\r
194 /* A buffer is not allocated to the Tx descriptors until they are actually
\r
196 for( x = 0; x < NUM_TX_FRAG; x++ )
\r
198 TX_DESC_PACKET( x ) = NULL;
\r
199 TX_DESC_CTRL( x ) = 0;
\r
200 TX_STAT_INFO( x ) = 0;
\r
203 /* Set EMAC Transmit Descriptor Registers. */
\r
204 MAC_TXDESCRIPTOR = TX_DESC_BASE;
\r
205 MAC_TXSTATUS = TX_STAT_BASE;
\r
206 MAC_TXDESCRIPTORNUM = NUM_TX_FRAG - 1;
\r
208 /* Tx Descriptors Point to 0 */
\r
209 MAC_TXPRODUCEINDEX = 0;
\r
211 /*-----------------------------------------------------------*/
\r
213 static void prvSetupEMACHardware( void )
\r
218 /* Enable P1 Ethernet Pins. */
\r
219 PINSEL2 = emacPINSEL2_VALUE;
\r
220 PINSEL3 = ( PINSEL3 & ~0x0000000F ) | 0x00000005;
\r
222 /* Power Up the EMAC controller. */
\r
223 PCONP |= PCONP_PCENET;
\r
224 vTaskDelay( emacSHORT_DELAY );
\r
226 /* Reset all EMAC internal modules. */
\r
227 MAC_MAC1 = MAC1_RES_TX | MAC1_RES_MCS_TX | MAC1_RES_RX | MAC1_RES_MCS_RX | MAC1_SIM_RES | MAC1_SOFT_RES;
\r
228 MAC_COMMAND = CR_REG_RES | CR_TX_RES | CR_RX_RES | CR_PASS_RUNT_FRM;
\r
230 /* A short delay after reset. */
\r
231 vTaskDelay( emacSHORT_DELAY );
\r
233 /* Initialize MAC control registers. */
\r
234 MAC_MAC1 = MAC1_PASS_ALL;
\r
235 MAC_MAC2 = MAC2_CRC_EN | MAC2_PAD_EN;
\r
236 MAC_MAXF = ETH_MAX_FLEN;
\r
237 MAC_CLRT = CLRT_DEF;
\r
238 MAC_IPGR = IPGR_DEF;
\r
240 /* Enable Reduced MII interface. */
\r
241 MAC_COMMAND = CR_RMII | CR_PASS_RUNT_FRM;
\r
243 /* Reset Reduced MII Logic. */
\r
244 MAC_SUPP = SUPP_RES_RMII;
\r
245 vTaskDelay( emacSHORT_DELAY );
\r
248 /* Put the PHY in reset mode */
\r
249 write_PHY( PHY_REG_BMCR, MCFG_RES_MII );
\r
250 write_PHY( PHY_REG_BMCR, MCFG_RES_MII );
\r
252 /* Wait for hardware reset to end. */
\r
253 for( x = 0; x < 100; x++ )
\r
255 vTaskDelay( emacSHORT_DELAY * 5 );
\r
256 us = read_PHY( PHY_REG_BMCR, &lDummy );
\r
257 if( !( us & MCFG_RES_MII ) )
\r
259 /* Reset complete */
\r
264 /*-----------------------------------------------------------*/
\r
266 static void prvConfigurePHY( void )
\r
271 /* Auto negotiate the configuration. */
\r
272 if( write_PHY( PHY_REG_BMCR, PHY_AUTO_NEG ) )
\r
274 vTaskDelay( emacSHORT_DELAY * 5 );
\r
276 for( x = 0; x < 10; x++ )
\r
278 us = read_PHY( PHY_REG_BMSR, &lDummy );
\r
280 if( us & PHY_AUTO_NEG_COMPLETE )
\r
285 vTaskDelay( emacWAIT_FOR_LINK_TO_ESTABLISH );
\r
289 /*-----------------------------------------------------------*/
\r
291 static long prvSetupLinkStatus( void )
\r
293 long lReturn = pdFAIL, x;
\r
294 unsigned short usLinkStatus;
\r
296 for( x = 0; x < 10; x++ )
\r
298 usLinkStatus = read_PHY( PHY_REG_STS, &lReturn );
\r
299 if( usLinkStatus & emacLINK_ESTABLISHED )
\r
301 /* Link is established. */
\r
306 vTaskDelay( emacWAIT_FOR_LINK_TO_ESTABLISH );
\r
309 if( lReturn == pdPASS )
\r
311 /* Configure Full/Half Duplex mode. */
\r
312 if( usLinkStatus & emacFULL_DUPLEX_ENABLED )
\r
314 /* Full duplex is enabled. */
\r
315 MAC_MAC2 |= MAC2_FULL_DUP;
\r
316 MAC_COMMAND |= CR_FULL_DUP;
\r
317 MAC_IPGT = IPGT_FULL_DUP;
\r
321 /* Half duplex mode. */
\r
322 MAC_IPGT = IPGT_HALF_DUP;
\r
325 /* Configure 100MBit/10MBit mode. */
\r
326 if( usLinkStatus & emac10BASE_T_MODE )
\r
333 /* 100MBit mode. */
\r
334 MAC_SUPP = SUPP_SPEED;
\r
340 /*-----------------------------------------------------------*/
\r
342 long Init_EMAC( void )
\r
344 long lReturn = pdPASS;
\r
345 volatile unsigned long regv, tout;
\r
346 unsigned long ulID1, ulID2;
\r
348 /* Reset peripherals, configure port pins and registers. */
\r
349 prvSetupEMACHardware();
\r
351 /* Check if connected to a DP83848C PHY. */
\r
352 ulID1 = read_PHY( PHY_REG_IDR1, &lReturn );
\r
353 ulID2 = read_PHY( PHY_REG_IDR2, &lReturn );
\r
354 if( ( (ulID1 << 16UL ) | ( ulID2 & 0xFFF0UL ) ) == DP83848C_ID )
\r
356 /* Set the Ethernet MAC Address registers */
\r
357 MAC_SA0 = ( configMAC_ADDR0 << 8 ) | configMAC_ADDR1;
\r
358 MAC_SA1 = ( configMAC_ADDR2 << 8 ) | configMAC_ADDR3;
\r
359 MAC_SA2 = ( configMAC_ADDR4 << 8 ) | configMAC_ADDR5;
\r
361 /* Initialize Tx and Rx DMA Descriptors */
\r
362 prvInitDescriptors();
\r
364 /* Receive Broadcast and Perfect Match Packets */
\r
365 MAC_RXFILTERCTRL = RFC_UCAST_EN | RFC_BCAST_EN | RFC_PERFECT_EN;
\r
367 /* Setup the PHY. */
\r
375 /* Check the link status. */
\r
376 if( lReturn == pdPASS )
\r
378 lReturn = prvSetupLinkStatus();
\r
381 if( lReturn == pdPASS )
\r
383 /* Initialise uip_buf to ensure it points somewhere valid. */
\r
384 uip_buf = prvGetNextBuffer();
\r
386 /* Reset all interrupts */
\r
387 MAC_INTCLEAR = ( INT_RX_OVERRUN | INT_RX_ERR | INT_RX_FIN | INT_RX_DONE | INT_TX_UNDERRUN | INT_TX_ERR | INT_TX_FIN | INT_TX_DONE | INT_SOFT_INT | INT_WAKEUP );
\r
389 /* Enable receive and transmit mode of MAC Ethernet core */
\r
390 MAC_COMMAND |= ( CR_RX_EN | CR_TX_EN );
\r
391 MAC_MAC1 |= MAC1_REC_EN;
\r
396 /*-----------------------------------------------------------*/
\r
398 static void prvReturnBuffer( unsigned char *pucBuffer )
\r
402 /* Mark a buffer as free for use. */
\r
403 for( ul = 0; ul < ETH_NUM_BUFFERS; ul++ )
\r
405 if( ETH_BUF( ul ) == ( unsigned long ) pucBuffer )
\r
407 ucBufferInUse[ ul ] = pdFALSE;
\r
412 /*-----------------------------------------------------------*/
\r
414 unsigned long ulGetEMACRxData( void )
\r
416 unsigned long ulLen = 0;
\r
419 if( MAC_RXPRODUCEINDEX != MAC_RXCONSUMEINDEX )
\r
421 /* Mark the current buffer as free as uip_buf is going to be set to
\r
422 the buffer that contains the received data. */
\r
423 prvReturnBuffer( uip_buf );
\r
425 ulLen = ( RX_STAT_INFO( MAC_RXCONSUMEINDEX ) & RINFO_SIZE ) - 3;
\r
426 uip_buf = ( unsigned char * ) RX_DESC_PACKET( MAC_RXCONSUMEINDEX );
\r
428 /* Allocate a new buffer to the descriptor. */
\r
429 RX_DESC_PACKET( MAC_RXCONSUMEINDEX ) = ( unsigned long ) prvGetNextBuffer();
\r
431 /* Move the consume index onto the next position, ensuring it wraps to
\r
432 the beginning at the appropriate place. */
\r
433 lIndex = MAC_RXCONSUMEINDEX;
\r
436 if( lIndex >= NUM_RX_FRAG )
\r
441 MAC_RXCONSUMEINDEX = lIndex;
\r
446 /*-----------------------------------------------------------*/
\r
448 void vSendEMACTxData( unsigned short usTxDataLen )
\r
450 unsigned long ulAttempts = 0UL;
\r
452 /* Check to see if the Tx descriptor is free, indicated by its buffer being
\r
454 while( TX_DESC_PACKET( emacTX_DESC_INDEX ) != NULL )
\r
456 /* Wait for the Tx descriptor to become available. */
\r
457 vTaskDelay( emacBUFFER_WAIT_DELAY );
\r
460 if( ulAttempts > emacBUFFER_WAIT_ATTEMPTS )
\r
462 /* Something has gone wrong as the Tx descriptor is still in use.
\r
463 Clear it down manually, the data it was sending will probably be
\r
465 prvReturnBuffer( ( unsigned char * ) TX_DESC_PACKET( emacTX_DESC_INDEX ) );
\r
470 /* Setup the Tx descriptor for transmission. Remember the length of the
\r
471 data being sent so the second descriptor can be used to send it again from
\r
473 usSendLen = usTxDataLen;
\r
474 TX_DESC_PACKET( emacTX_DESC_INDEX ) = ( unsigned long ) uip_buf;
\r
475 TX_DESC_CTRL( emacTX_DESC_INDEX ) = ( usTxDataLen | TCTRL_LAST | TCTRL_INT );
\r
476 MAC_TXPRODUCEINDEX = ( emacTX_DESC_INDEX + 1 );
\r
478 /* uip_buf is being sent by the Tx descriptor. Allocate a new buffer. */
\r
479 uip_buf = prvGetNextBuffer();
\r
481 /*-----------------------------------------------------------*/
\r
484 static char c[ 256 ] = { 0 };
\r
488 void vEMAC_ISR( void )
\r
490 unsigned long ulStatus;
\r
491 portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
\r
493 ulStatus = MAC_INTSTATUS;
\r
495 /* Clear the interrupt. */
\r
496 MAC_INTCLEAR = ulStatus;
\r
498 if( ulStatus & INT_RX_DONE )
\r
500 /* Ensure the uIP task is not blocked as data has arrived. */
\r
501 xSemaphoreGiveFromISR( xEMACSemaphore, &xHigherPriorityTaskWoken );
\r
504 if( ulStatus & INT_TX_DONE )
\r
506 if( usSendLen > 0 )
\r
510 /* Send the data again, using the second descriptor. As there are
\r
511 only two descriptors the index is set back to 0. */
\r
512 TX_DESC_PACKET( ( emacTX_DESC_INDEX + 1 ) ) = TX_DESC_PACKET( emacTX_DESC_INDEX );
\r
513 TX_DESC_CTRL( ( emacTX_DESC_INDEX + 1 ) ) = ( usSendLen | TCTRL_LAST | TCTRL_INT );
\r
514 MAC_TXPRODUCEINDEX = ( emacTX_DESC_INDEX );
\r
516 /* This is the second Tx so set usSendLen to 0 to indicate that the
\r
517 Tx descriptors will be free again. */
\r
525 /* The Tx buffer is no longer required. */
\r
526 prvReturnBuffer( ( unsigned char * ) TX_DESC_PACKET( emacTX_DESC_INDEX ) );
\r
527 TX_DESC_PACKET( emacTX_DESC_INDEX ) = NULL;
\r
531 portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
\r