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 /* The semaphore used to wake the uIP task when data arives. */
\r
34 xSemaphoreHandle xEMACSemaphore = NULL;
\r
36 static unsigned short *rptr;
\r
37 static unsigned short *tptr;
\r
39 static void prvInitDescriptors( void );
\r
40 static void prvSetupEMACHardware( void );
\r
41 static void prvConfigurePHY( void );
\r
42 static long prvSetupLinkStatus( void );
\r
44 /*-----------------------------------------------------------*/
\r
46 int write_PHY( long lPhyReg, long lValue )
\r
48 const long lMaxTime = 10;
\r
51 MAC_MADR = DP83848C_DEF_ADR | lPhyReg;
\r
55 for( x = 0; x < lMaxTime; x++ )
\r
57 if( ( MAC_MIND & MIND_BUSY ) == 0 )
\r
59 /* Operation has finished. */
\r
63 vTaskDelay( emacSHORT_DELAY );
\r
75 /*-----------------------------------------------------------*/
\r
77 unsigned short read_PHY( unsigned char ucPhyReg, portBASE_TYPE *pxStatus )
\r
80 const long lMaxTime = 10;
\r
82 MAC_MADR = DP83848C_DEF_ADR | ucPhyReg;
\r
83 MAC_MCMD = MCMD_READ;
\r
85 for( x = 0; x < lMaxTime; x++ )
\r
87 /* Operation has finished. */
\r
88 if( ( MAC_MIND & MIND_BUSY ) == 0 )
\r
93 vTaskDelay( emacSHORT_DELAY );
\r
100 *pxStatus = pdFAIL;
\r
103 return( MAC_MRDD );
\r
105 /*-----------------------------------------------------------*/
\r
107 static void prvInitDescriptors( void )
\r
111 for( x = 0; x < NUM_RX_FRAG; x++ )
\r
113 RX_DESC_PACKET( x ) = RX_BUF( x );
\r
114 RX_DESC_CTRL( x ) = RCTRL_INT | ( ETH_FRAG_SIZE - 1 );
\r
115 RX_STAT_INFO( x ) = 0;
\r
116 RX_STAT_HASHCRC( x ) = 0;
\r
119 /* Set EMAC Receive Descriptor Registers. */
\r
120 MAC_RXDESCRIPTOR = RX_DESC_BASE;
\r
121 MAC_RXSTATUS = RX_STAT_BASE;
\r
122 MAC_RXDESCRIPTORNUM = NUM_RX_FRAG - 1;
\r
124 /* Rx Descriptors Point to 0 */
\r
125 MAC_RXCONSUMEINDEX = 0;
\r
127 for( x = 0; x < NUM_TX_FRAG; x++ )
\r
129 TX_DESC_PACKET( x ) = TX_BUF( x );
\r
130 TX_DESC_CTRL( x ) = 0;
\r
131 TX_STAT_INFO( x ) = 0;
\r
134 /* Set EMAC Transmit Descriptor Registers. */
\r
135 MAC_TXDESCRIPTOR = TX_DESC_BASE;
\r
136 MAC_TXSTATUS = TX_STAT_BASE;
\r
137 MAC_TXDESCRIPTORNUM = NUM_TX_FRAG - 1;
\r
139 /* Tx Descriptors Point to 0 */
\r
140 MAC_TXPRODUCEINDEX = 0;
\r
142 /*-----------------------------------------------------------*/
\r
144 static void prvSetupEMACHardware( void )
\r
149 /* Enable P1 Ethernet Pins. */
\r
150 PINSEL2 = emacPINSEL2_VALUE;
\r
151 PINSEL3 = ( PINSEL3 & ~0x0000000F ) | 0x00000005;
\r
153 /* Power Up the EMAC controller. */
\r
154 PCONP |= PCONP_PCENET;
\r
155 vTaskDelay( emacSHORT_DELAY );
\r
157 /* Reset all EMAC internal modules. */
\r
158 MAC_MAC1 = MAC1_RES_TX | MAC1_RES_MCS_TX | MAC1_RES_RX | MAC1_RES_MCS_RX | MAC1_SIM_RES | MAC1_SOFT_RES;
\r
159 MAC_COMMAND = CR_REG_RES | CR_TX_RES | CR_RX_RES | CR_PASS_RUNT_FRM;
\r
161 /* A short delay after reset. */
\r
162 vTaskDelay( emacSHORT_DELAY );
\r
164 /* Initialize MAC control registers. */
\r
165 MAC_MAC1 = MAC1_PASS_ALL;
\r
166 MAC_MAC2 = MAC2_CRC_EN | MAC2_PAD_EN;
\r
167 MAC_MAXF = ETH_MAX_FLEN;
\r
168 MAC_CLRT = CLRT_DEF;
\r
169 MAC_IPGR = IPGR_DEF;
\r
171 /* Enable Reduced MII interface. */
\r
172 MAC_COMMAND = CR_RMII | CR_PASS_RUNT_FRM;
\r
174 /* Reset Reduced MII Logic. */
\r
175 MAC_SUPP = SUPP_RES_RMII;
\r
176 vTaskDelay( emacSHORT_DELAY );
\r
179 /* Put the PHY in reset mode */
\r
180 write_PHY( PHY_REG_BMCR, MCFG_RES_MII );
\r
181 write_PHY( PHY_REG_BMCR, MCFG_RES_MII );
\r
183 /* Wait for hardware reset to end. */
\r
184 for( x = 0; x < 100; x++ )
\r
186 vTaskDelay( emacSHORT_DELAY * 5 );
\r
187 us = read_PHY( PHY_REG_BMCR, &us );
\r
188 if( !( us & MCFG_RES_MII ) )
\r
190 /* Reset complete */
\r
195 /*-----------------------------------------------------------*/
\r
197 static void prvConfigurePHY( void )
\r
202 /* Auto negotiate the configuration. */
\r
203 if( write_PHY( PHY_REG_BMCR, PHY_AUTO_NEG ) )
\r
205 vTaskDelay( emacSHORT_DELAY * 5 );
\r
207 for( x = 0; x < 10; x++ )
\r
209 us = read_PHY( PHY_REG_BMSR, &us );
\r
211 if( us & PHY_AUTO_NEG_COMPLETE )
\r
216 vTaskDelay( emacWAIT_FOR_LINK_TO_ESTABLISH );
\r
220 /*-----------------------------------------------------------*/
\r
222 static long prvSetupLinkStatus( void )
\r
224 long lReturn = pdFAIL, x;
\r
225 unsigned short usLinkStatus;
\r
227 for( x = 0; x < 10; x++ )
\r
229 usLinkStatus = read_PHY( PHY_REG_STS, &lReturn );
\r
230 if( usLinkStatus & emacLINK_ESTABLISHED )
\r
232 /* Link is established. */
\r
237 vTaskDelay( emacWAIT_FOR_LINK_TO_ESTABLISH );
\r
240 if( lReturn == pdPASS )
\r
242 /* Configure Full/Half Duplex mode. */
\r
243 if( usLinkStatus & emacFULL_DUPLEX_ENABLED )
\r
245 /* Full duplex is enabled. */
\r
246 MAC_MAC2 |= MAC2_FULL_DUP;
\r
247 MAC_COMMAND |= CR_FULL_DUP;
\r
248 MAC_IPGT = IPGT_FULL_DUP;
\r
252 /* Half duplex mode. */
\r
253 MAC_IPGT = IPGT_HALF_DUP;
\r
256 /* Configure 100MBit/10MBit mode. */
\r
257 if( usLinkStatus & emac10BASE_T_MODE )
\r
264 /* 100MBit mode. */
\r
265 MAC_SUPP = SUPP_SPEED;
\r
271 /*-----------------------------------------------------------*/
\r
273 portBASE_TYPE Init_EMAC( void )
\r
275 portBASE_TYPE xReturn = pdPASS;
\r
276 volatile unsigned long regv, tout;
\r
277 unsigned long ulID1, ulID2;
\r
279 /* Reset peripherals, configure port pins and registers. */
\r
280 prvSetupEMACHardware();
\r
282 /* Check if connected to a DP83848C PHY. */
\r
283 ulID1 = read_PHY( PHY_REG_IDR1, &xReturn );
\r
284 ulID2 = read_PHY( PHY_REG_IDR2, &xReturn );
\r
285 if( ( (ulID1 << 16UL ) | ( ulID2 & 0xFFF0UL ) ) == DP83848C_ID )
\r
287 /* Set the Ethernet MAC Address registers */
\r
288 MAC_SA0 = ( configMAC_ADDR0 << 8 ) | configMAC_ADDR1;
\r
289 MAC_SA1 = ( configMAC_ADDR2 << 8 ) | configMAC_ADDR3;
\r
290 MAC_SA2 = ( configMAC_ADDR4 << 8 ) | configMAC_ADDR5;
\r
292 /* Initialize Tx and Rx DMA Descriptors */
\r
293 prvInitDescriptors();
\r
295 /* Receive Broadcast and Perfect Match Packets */
\r
296 MAC_RXFILTERCTRL = RFC_UCAST_EN | RFC_BCAST_EN | RFC_PERFECT_EN;
\r
298 /* Create the semaphore used to wake the uIP task. */
\r
299 vSemaphoreCreateBinary( xEMACSemaphore );
\r
301 /* Setup the PHY. */
\r
309 /* Check the link status. */
\r
310 if( xReturn == pdPASS )
\r
312 xReturn = prvSetupLinkStatus();
\r
315 if( xReturn == pdPASS )
\r
317 /* Reset all interrupts */
\r
318 MAC_INTCLEAR = 0xFFFF;
\r
320 /* Enable receive and transmit mode of MAC Ethernet core */
\r
321 MAC_COMMAND |= ( CR_RX_EN | CR_TX_EN );
\r
322 MAC_MAC1 |= MAC1_REC_EN;
\r
327 /*-----------------------------------------------------------*/
\r
329 // reads a word in little-endian byte order from RX_BUFFER
\r
330 unsigned short ReadFrame_EMAC( void )
\r
335 // copies bytes from frame port to MCU-memory
\r
336 // NOTES: * an odd number of byte may only be transfered
\r
337 // if the frame is read to the end!
\r
338 // * MCU-memory MUST start at word-boundary
\r
339 void CopyFromFrame_EMAC( void *Dest, unsigned short Size )
\r
341 unsigned short *piDest; // Keil: Pointer added to correct expression
\r
342 piDest = Dest; // Keil: Line added
\r
345 *piDest++ = ReadFrame_EMAC();
\r
350 { // check for leftover byte...
\r
351 *( unsigned char * ) piDest = ( char ) ReadFrame_EMAC(); // the LAN-Controller will return 0
\r
352 } // for the highbyte
\r
356 // Reads the length of the received ethernet frame and checks if the
\r
357 // destination address is a broadcast message or not
\r
358 // returns the frame length
\r
359 unsigned short StartReadFrame( void )
\r
361 unsigned short RxLen;
\r
364 idx = MAC_RXCONSUMEINDEX;
\r
365 RxLen = ( RX_STAT_INFO(idx) & RINFO_SIZE ) - 3;
\r
366 rptr = ( unsigned short * ) RX_DESC_PACKET( idx );
\r
370 void EndReadFrame( void )
\r
374 /* DMA free packet. */
\r
375 idx = MAC_RXCONSUMEINDEX;
\r
377 if( ++idx == NUM_RX_FRAG )
\r
382 MAC_RXCONSUMEINDEX = idx;
\r
385 unsigned int uiGetEMACRxData( unsigned char *ucBuffer )
\r
387 unsigned int uiLen = 0;
\r
389 if( MAC_RXPRODUCEINDEX != MAC_RXCONSUMEINDEX )
\r
391 uiLen = StartReadFrame();
\r
392 CopyFromFrame_EMAC( ucBuffer, uiLen );
\r
399 // requests space in EMAC memory for storing an outgoing frame
\r
400 void RequestSend( void )
\r
404 idx = MAC_TXPRODUCEINDEX;
\r
405 tptr = ( unsigned short * ) TX_DESC_PACKET( idx );
\r
408 // writes a word in little-endian byte order to TX_BUFFER
\r
409 void WriteFrame_EMAC( unsigned short Data )
\r
414 // copies bytes from MCU-memory to frame port
\r
415 // NOTES: * an odd number of byte may only be transfered
\r
416 // if the frame is written to the end!
\r
417 // * MCU-memory MUST start at word-boundary
\r
418 void CopyToFrame_EMAC( void *Source, unsigned int Size )
\r
420 unsigned short *piSource;
\r
423 Size = ( Size + 1 ) & 0xFFFE; // round Size up to next even number
\r
426 WriteFrame_EMAC( *piSource++ );
\r
431 void DoSend_EMAC( unsigned short FrameSize )
\r
435 idx = MAC_TXPRODUCEINDEX;
\r
436 TX_DESC_CTRL( idx ) = FrameSize | TCTRL_LAST;
\r
437 if( ++idx == NUM_TX_FRAG )
\r
442 MAC_TXPRODUCEINDEX = idx;
\r
445 void vEMAC_ISR( void )
\r
447 portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
\r
449 /* Clear the interrupt. */
\r
450 MAC_INTCLEAR = 0xffff;
\r
452 /* Ensure the uIP task is not blocked as data has arrived. */
\r
453 xSemaphoreGiveFromISR( xEMACSemaphore, &xHigherPriorityTaskWoken );
\r
455 portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
\r