]> git.sur5r.net Git - freertos/blob - Demo/CORTEX_LPC1768_GCC_Rowley/webserver/emac.c
Update EMAC driver.
[freertos] / Demo / CORTEX_LPC1768_GCC_Rowley / webserver / emac.c
1 /******************************************************************\r
2  *****                                                        *****\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
8  *****        Germany                                         *****\r
9  *****  Func: ethernet packet-driver for use with LAN-        *****\r
10  *****        controller CS8900 from Crystal/Cirrus Logic     *****\r
11  *****                                                        *****\r
12  *****  Keil: Module modified for use with Philips            *****\r
13  *****        LPC2378 EMAC Ethernet controller                *****\r
14  *****                                                        *****\r
15  ******************************************************************/\r
16 \r
17 /* Adapted from file originally written by Andreas Dannenberg.  Supplied with permission. */\r
18 #include "FreeRTOS.h"\r
19 #include "semphr.h"\r
20 #include "task.h"\r
21 #include "LPC17xx_defs.h"\r
22 #include "EthDev_LPC17xx.h"\r
23 \r
24 #define emacPINSEL2_VALUE 0x50150105\r
25 \r
26 #define emacWAIT_FOR_LINK_TO_ESTABLISH ( 500 / portTICK_RATE_MS )\r
27 #define emacSHORT_DELAY                            ( 2 )\r
28 \r
29 #define emacLINK_ESTABLISHED            ( 0x0001 )\r
30 #define emacFULL_DUPLEX_ENABLED         ( 0x0004 )\r
31 #define emac10BASE_T_MODE                       ( 0x0002 )\r
32 \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
35 \r
36 /* ...and don't look more than this many times. */\r
37 #define emacBUFFER_WAIT_ATTEMPTS        ( 30 )\r
38 \r
39 #define emacTX_DESC_INDEX                       ( 0 )\r
40 \r
41 /* The semaphore used to wake the uIP task when data arives. */\r
42 extern xSemaphoreHandle xEMACSemaphore;\r
43 \r
44 static unsigned short   *rptr;\r
45 static unsigned short   *tptr;\r
46 \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
53 \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
58 \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
62 \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
66 \r
67 /*-----------------------------------------------------------*/\r
68 \r
69 int write_PHY( long lPhyReg, long lValue )\r
70 {\r
71 const long lMaxTime = 10;\r
72 long x;\r
73 \r
74         MAC_MADR = DP83848C_DEF_ADR | lPhyReg;\r
75         MAC_MWTD = lValue;\r
76 \r
77         x = 0;\r
78         for( x = 0; x < lMaxTime; x++ )\r
79         {\r
80                 if( ( MAC_MIND & MIND_BUSY ) == 0 )\r
81                 {\r
82                         /* Operation has finished. */\r
83                         break;\r
84                 }\r
85 \r
86                 vTaskDelay( emacSHORT_DELAY );\r
87         }\r
88 \r
89         if( x < lMaxTime )\r
90         {\r
91                 return pdPASS;\r
92         }\r
93         else\r
94         {\r
95                 return pdFAIL;\r
96         }\r
97 }\r
98 /*-----------------------------------------------------------*/\r
99 \r
100 unsigned short read_PHY( unsigned char ucPhyReg, long *plStatus )\r
101 {\r
102 long x;\r
103 const long lMaxTime = 10;\r
104 \r
105         MAC_MADR = DP83848C_DEF_ADR | ucPhyReg;\r
106         MAC_MCMD = MCMD_READ;\r
107 \r
108         for( x = 0; x < lMaxTime; x++ )\r
109         {\r
110                 /* Operation has finished. */\r
111                 if( ( MAC_MIND & MIND_BUSY ) == 0 )\r
112                 {\r
113                         break;\r
114                 }\r
115 \r
116                 vTaskDelay( emacSHORT_DELAY );\r
117         }\r
118 \r
119         MAC_MCMD = 0;\r
120 \r
121         if( x >= lMaxTime )\r
122         {\r
123                 *plStatus = pdFAIL;\r
124         }\r
125 \r
126         return( MAC_MRDD );\r
127 }\r
128 /*-----------------------------------------------------------*/\r
129 \r
130 static unsigned char *prvGetNextBuffer( void )\r
131 {\r
132 long x;\r
133 unsigned char *pucReturn = NULL;\r
134 unsigned long ulAttempts = 0;\r
135 \r
136         while( pucReturn == NULL )\r
137         {\r
138                 /* Look through the buffers to find one that is not in use by\r
139                 anything else. */\r
140                 for( x = 0; x < ETH_NUM_BUFFERS; x++ )\r
141                 {\r
142                         if( ucBufferInUse[ x ] == pdFALSE )\r
143                         {\r
144                                 ucBufferInUse[ x ] = pdTRUE;\r
145                                 pucReturn = ( unsigned char * ) ETH_BUF( x );\r
146                                 break;\r
147                         }\r
148                 }\r
149 \r
150                 /* Was a buffer found? */\r
151                 if( pucReturn == NULL )\r
152                 {\r
153                         ulAttempts++;\r
154 \r
155                         if( ulAttempts >= emacBUFFER_WAIT_ATTEMPTS )\r
156                         {\r
157                                 break;\r
158                         }\r
159 \r
160                         /* Wait then look again. */\r
161                         vTaskDelay( emacBUFFER_WAIT_DELAY );\r
162                 }\r
163         }\r
164 \r
165         return pucReturn;\r
166 }\r
167 /*-----------------------------------------------------------*/\r
168 \r
169 static void prvInitDescriptors( void )\r
170 {\r
171 long x, lNextBuffer = 0;\r
172 \r
173         for( x = 0; x < NUM_RX_FRAG; x++ )\r
174         {\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
180 \r
181                 /* The Ethernet buffer is now in use. */\r
182                 ucBufferInUse[ lNextBuffer ] = pdTRUE;\r
183                 lNextBuffer++;\r
184         }\r
185 \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
190 \r
191         /* Rx Descriptors Point to 0 */\r
192         MAC_RXCONSUMEINDEX = 0;\r
193 \r
194         /* A buffer is not allocated to the Tx descriptors until they are actually\r
195         used. */\r
196         for( x = 0; x < NUM_TX_FRAG; x++ )\r
197         {\r
198                 TX_DESC_PACKET( x ) = NULL;\r
199                 TX_DESC_CTRL( x ) = 0;\r
200                 TX_STAT_INFO( x ) = 0;\r
201         }\r
202 \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
207 \r
208         /* Tx Descriptors Point to 0 */\r
209         MAC_TXPRODUCEINDEX = 0;\r
210 }\r
211 /*-----------------------------------------------------------*/\r
212 \r
213 static void prvSetupEMACHardware( void )\r
214 {\r
215 unsigned short us;\r
216 long x, lDummy;\r
217 \r
218         /* Enable P1 Ethernet Pins. */\r
219         PINSEL2 = emacPINSEL2_VALUE;\r
220         PINSEL3 = ( PINSEL3 & ~0x0000000F ) | 0x00000005;\r
221 \r
222         /* Power Up the EMAC controller. */\r
223         PCONP |= PCONP_PCENET;\r
224         vTaskDelay( emacSHORT_DELAY );\r
225 \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
229 \r
230         /* A short delay after reset. */\r
231         vTaskDelay( emacSHORT_DELAY );\r
232 \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
239 \r
240         /* Enable Reduced MII interface. */\r
241         MAC_COMMAND = CR_RMII | CR_PASS_RUNT_FRM;\r
242 \r
243         /* Reset Reduced MII Logic. */\r
244         MAC_SUPP = SUPP_RES_RMII;\r
245         vTaskDelay( emacSHORT_DELAY );\r
246         MAC_SUPP = 0;\r
247 \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
251 \r
252         /* Wait for hardware reset to end. */\r
253         for( x = 0; x < 100; x++ )\r
254         {\r
255                 vTaskDelay( emacSHORT_DELAY * 5 );\r
256                 us = read_PHY( PHY_REG_BMCR, &lDummy );\r
257                 if( !( us & MCFG_RES_MII ) )\r
258                 {\r
259                         /* Reset complete */\r
260                         break;\r
261                 }\r
262         }\r
263 }\r
264 /*-----------------------------------------------------------*/\r
265 \r
266 static void prvConfigurePHY( void )\r
267 {\r
268 unsigned short us;\r
269 long x, lDummy;\r
270 \r
271         /* Auto negotiate the configuration. */\r
272         if( write_PHY( PHY_REG_BMCR, PHY_AUTO_NEG ) )\r
273         {\r
274                 vTaskDelay( emacSHORT_DELAY * 5 );\r
275 \r
276                 for( x = 0; x < 10; x++ )\r
277                 {\r
278                         us = read_PHY( PHY_REG_BMSR, &lDummy );\r
279 \r
280                         if( us & PHY_AUTO_NEG_COMPLETE )\r
281                         {\r
282                                 break;\r
283                         }\r
284 \r
285                         vTaskDelay( emacWAIT_FOR_LINK_TO_ESTABLISH );\r
286                 }\r
287         }\r
288 }\r
289 /*-----------------------------------------------------------*/\r
290 \r
291 static long prvSetupLinkStatus( void )\r
292 {\r
293 long lReturn = pdFAIL, x;\r
294 unsigned short usLinkStatus;\r
295 \r
296         for( x = 0; x < 10; x++ )\r
297         {\r
298                 usLinkStatus = read_PHY( PHY_REG_STS, &lReturn );\r
299                 if( usLinkStatus & emacLINK_ESTABLISHED )\r
300                 {\r
301                         /* Link is established. */\r
302                         lReturn = pdPASS;\r
303                         break;\r
304                 }\r
305 \r
306         vTaskDelay( emacWAIT_FOR_LINK_TO_ESTABLISH );\r
307         }\r
308 \r
309         if( lReturn == pdPASS )\r
310         {\r
311                 /* Configure Full/Half Duplex mode. */\r
312                 if( usLinkStatus & emacFULL_DUPLEX_ENABLED )\r
313                 {\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
318                 }\r
319                 else\r
320                 {\r
321                         /* Half duplex mode. */\r
322                         MAC_IPGT = IPGT_HALF_DUP;\r
323                 }\r
324 \r
325                 /* Configure 100MBit/10MBit mode. */\r
326                 if( usLinkStatus & emac10BASE_T_MODE )\r
327                 {\r
328                         /* 10MBit mode. */\r
329                         MAC_SUPP = 0;\r
330                 }\r
331                 else\r
332                 {\r
333                         /* 100MBit mode. */\r
334                         MAC_SUPP = SUPP_SPEED;\r
335                 }\r
336         }\r
337 \r
338         return lReturn;\r
339 }\r
340 /*-----------------------------------------------------------*/\r
341 \r
342 long Init_EMAC( void )\r
343 {\r
344 long lReturn = pdPASS;\r
345 volatile unsigned long regv, tout;\r
346 unsigned long ulID1, ulID2;\r
347 \r
348         /* Reset peripherals, configure port pins and registers. */\r
349         prvSetupEMACHardware();\r
350 \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
355         {\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
360 \r
361                 /* Initialize Tx and Rx DMA Descriptors */\r
362                 prvInitDescriptors();\r
363 \r
364                 /* Receive Broadcast and Perfect Match Packets */\r
365                 MAC_RXFILTERCTRL = RFC_UCAST_EN | RFC_BCAST_EN | RFC_PERFECT_EN;\r
366 \r
367                 /* Setup the PHY. */\r
368                 prvConfigurePHY();\r
369         }\r
370         else\r
371         {\r
372                 lReturn = pdFAIL;\r
373         }\r
374 \r
375         /* Check the link status. */\r
376         if( lReturn == pdPASS )\r
377         {\r
378                 lReturn = prvSetupLinkStatus();\r
379         }\r
380 \r
381         if( lReturn == pdPASS )\r
382         {\r
383                 /* Initialise uip_buf to ensure it points somewhere valid. */\r
384                 uip_buf = prvGetNextBuffer();\r
385 \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
388 \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
392         }\r
393 \r
394         return lReturn;\r
395 }\r
396 /*-----------------------------------------------------------*/\r
397 \r
398 static void prvReturnBuffer( unsigned char *pucBuffer )\r
399 {\r
400 unsigned long ul;\r
401 \r
402         /* Mark a buffer as free for use. */\r
403         for( ul = 0; ul < ETH_NUM_BUFFERS; ul++ )\r
404         {\r
405                 if( ETH_BUF( ul ) == ( unsigned long ) pucBuffer )\r
406                 {\r
407                         ucBufferInUse[ ul ] = pdFALSE;\r
408                         break;\r
409                 }\r
410         }\r
411 }\r
412 /*-----------------------------------------------------------*/\r
413 \r
414 unsigned long ulGetEMACRxData( void )\r
415 {\r
416 unsigned long ulLen = 0;\r
417 long lIndex;\r
418 \r
419         if( MAC_RXPRODUCEINDEX != MAC_RXCONSUMEINDEX )\r
420         {\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
424 \r
425                 ulLen = ( RX_STAT_INFO( MAC_RXCONSUMEINDEX ) & RINFO_SIZE ) - 3;\r
426                 uip_buf = ( unsigned char * ) RX_DESC_PACKET( MAC_RXCONSUMEINDEX );\r
427 \r
428                 /* Allocate a new buffer to the descriptor. */\r
429         RX_DESC_PACKET( MAC_RXCONSUMEINDEX ) = ( unsigned long ) prvGetNextBuffer();\r
430 \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
434 \r
435                 lIndex++;\r
436                 if( lIndex >= NUM_RX_FRAG )\r
437                 {\r
438                         lIndex = 0;\r
439                 }\r
440 \r
441                 MAC_RXCONSUMEINDEX = lIndex;\r
442         }\r
443 \r
444         return ulLen;\r
445 }\r
446 /*-----------------------------------------------------------*/\r
447 \r
448 void vSendEMACTxData( unsigned short usTxDataLen )\r
449 {\r
450 unsigned long ulAttempts = 0UL;\r
451 \r
452         /* Check to see if the Tx descriptor is free, indicated by its buffer being\r
453         NULL. */\r
454         while( TX_DESC_PACKET( emacTX_DESC_INDEX ) != NULL )\r
455         {\r
456                 /* Wait for the Tx descriptor to become available. */\r
457                 vTaskDelay( emacBUFFER_WAIT_DELAY );\r
458 \r
459                 ulAttempts++;\r
460                 if( ulAttempts > emacBUFFER_WAIT_ATTEMPTS )\r
461                 {\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
464                         lost. */\r
465                         prvReturnBuffer( ( unsigned char * ) TX_DESC_PACKET( emacTX_DESC_INDEX ) );\r
466                         break;\r
467                 }\r
468         }\r
469 \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
472         within the ISR. */\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
477 \r
478         /* uip_buf is being sent by the Tx descriptor.  Allocate a new buffer. */\r
479         uip_buf = prvGetNextBuffer();\r
480 }\r
481 /*-----------------------------------------------------------*/\r
482 \r
483 \r
484 static char c[ 256 ] = { 0 };\r
485 static int i = 0;\r
486 \r
487 \r
488 void vEMAC_ISR( void )\r
489 {\r
490 unsigned long ulStatus;\r
491 portBASE_TYPE   xHigherPriorityTaskWoken = pdFALSE;\r
492 \r
493         ulStatus = MAC_INTSTATUS;\r
494 \r
495         /* Clear the interrupt. */\r
496         MAC_INTCLEAR = ulStatus;\r
497 \r
498         if( ulStatus & INT_RX_DONE )\r
499         {\r
500                 /* Ensure the uIP task is not blocked as data has arrived. */\r
501                 xSemaphoreGiveFromISR( xEMACSemaphore, &xHigherPriorityTaskWoken );\r
502         }\r
503 \r
504         if( ulStatus & INT_TX_DONE )\r
505         {\r
506                 if( usSendLen > 0 )\r
507                 {\r
508 if( i < 255 )\r
509         c[ i++ ] = 1;\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
515 \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
518                         usSendLen = 0UL;\r
519                 }\r
520                 else\r
521                 {\r
522 if( i < 255 )\r
523         c[ i++ ] = 1;\r
524 \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
528                 }\r
529         }\r
530 \r
531         portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );\r
532 }\r