]> git.sur5r.net Git - freertos/blob - Demo/CORTEX_LPC1768_GCC_Rowley/webserver/emac.c
Continue work on 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 /* The semaphore used to wake the uIP task when data arives. */\r
34 xSemaphoreHandle                xEMACSemaphore = NULL;\r
35 \r
36 static unsigned short   *rptr;\r
37 static unsigned short   *tptr;\r
38 \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
43 \r
44 /*-----------------------------------------------------------*/\r
45 \r
46 int write_PHY( long lPhyReg, long lValue )\r
47 {\r
48 const long lMaxTime = 10;\r
49 long x;\r
50 \r
51         MAC_MADR = DP83848C_DEF_ADR | lPhyReg;\r
52         MAC_MWTD = lValue;\r
53 \r
54         x = 0;\r
55         for( x = 0; x < lMaxTime; x++ )\r
56         {\r
57                 if( ( MAC_MIND & MIND_BUSY ) == 0 )\r
58                 {\r
59                         /* Operation has finished. */\r
60                         break;\r
61                 }\r
62 \r
63                 vTaskDelay( emacSHORT_DELAY );\r
64         }\r
65 \r
66         if( x < lMaxTime )\r
67         {\r
68                 return pdPASS;\r
69         }\r
70         else\r
71         {\r
72                 return pdFAIL;\r
73         }\r
74 }\r
75 /*-----------------------------------------------------------*/\r
76 \r
77 unsigned short read_PHY( unsigned char ucPhyReg, portBASE_TYPE *pxStatus )\r
78 {\r
79 long x;\r
80 const long lMaxTime = 10;\r
81 \r
82         MAC_MADR = DP83848C_DEF_ADR | ucPhyReg;\r
83         MAC_MCMD = MCMD_READ;\r
84 \r
85         for( x = 0; x < lMaxTime; x++ )\r
86         {\r
87                 /* Operation has finished. */\r
88                 if( ( MAC_MIND & MIND_BUSY ) == 0 )\r
89                 {\r
90                         break;\r
91                 }\r
92 \r
93                 vTaskDelay( emacSHORT_DELAY );\r
94         }\r
95 \r
96         MAC_MCMD = 0;\r
97 \r
98         if( x >= lMaxTime )\r
99         {\r
100                 *pxStatus = pdFAIL;\r
101         }\r
102 \r
103         return( MAC_MRDD );\r
104 }\r
105 /*-----------------------------------------------------------*/\r
106 \r
107 static void prvInitDescriptors( void )\r
108 {\r
109 long x;\r
110 \r
111         for( x = 0; x < NUM_RX_FRAG; x++ )\r
112         {\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
117         }\r
118 \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
123 \r
124         /* Rx Descriptors Point to 0 */\r
125         MAC_RXCONSUMEINDEX = 0;\r
126 \r
127         for( x = 0; x < NUM_TX_FRAG; x++ )\r
128         {\r
129                 TX_DESC_PACKET( x ) = TX_BUF( x );\r
130                 TX_DESC_CTRL( x ) = 0;\r
131                 TX_STAT_INFO( x ) = 0;\r
132         }\r
133 \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
138 \r
139         /* Tx Descriptors Point to 0 */\r
140         MAC_TXPRODUCEINDEX = 0;\r
141 }\r
142 /*-----------------------------------------------------------*/\r
143 \r
144 static void prvSetupEMACHardware( void )\r
145 {\r
146 unsigned short us;\r
147 long x;\r
148 \r
149         /* Enable P1 Ethernet Pins. */\r
150         PINSEL2 = emacPINSEL2_VALUE;\r
151         PINSEL3 = ( PINSEL3 & ~0x0000000F ) | 0x00000005;\r
152 \r
153         /* Power Up the EMAC controller. */\r
154         PCONP |= PCONP_PCENET;\r
155         vTaskDelay( emacSHORT_DELAY );\r
156 \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
160 \r
161         /* A short delay after reset. */\r
162         vTaskDelay( emacSHORT_DELAY );\r
163 \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
170 \r
171         /* Enable Reduced MII interface. */\r
172         MAC_COMMAND = CR_RMII | CR_PASS_RUNT_FRM;\r
173 \r
174         /* Reset Reduced MII Logic. */\r
175         MAC_SUPP = SUPP_RES_RMII;\r
176         vTaskDelay( emacSHORT_DELAY );\r
177         MAC_SUPP = 0;\r
178 \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
182 \r
183         /* Wait for hardware reset to end. */\r
184         for( x = 0; x < 100; x++ )\r
185         {\r
186                 vTaskDelay( emacSHORT_DELAY * 5 );\r
187                 us = read_PHY( PHY_REG_BMCR, &us );\r
188                 if( !( us & MCFG_RES_MII ) )\r
189                 {\r
190                         /* Reset complete */\r
191                         break;\r
192                 }\r
193         }\r
194 }\r
195 /*-----------------------------------------------------------*/\r
196 \r
197 static void prvConfigurePHY( void )\r
198 {\r
199 unsigned short us;\r
200 long x;\r
201 \r
202         /* Auto negotiate the configuration. */\r
203         if( write_PHY( PHY_REG_BMCR, PHY_AUTO_NEG ) )\r
204         {\r
205                 vTaskDelay( emacSHORT_DELAY * 5 );\r
206 \r
207                 for( x = 0; x < 10; x++ )\r
208                 {\r
209                         us = read_PHY( PHY_REG_BMSR, &us );\r
210 \r
211                         if( us & PHY_AUTO_NEG_COMPLETE )\r
212                         {\r
213                                 break;\r
214                         }\r
215 \r
216                         vTaskDelay( emacWAIT_FOR_LINK_TO_ESTABLISH );\r
217                 }\r
218         }\r
219 }\r
220 /*-----------------------------------------------------------*/\r
221 \r
222 static long prvSetupLinkStatus( void )\r
223 {\r
224 long lReturn = pdFAIL, x;\r
225 unsigned short usLinkStatus;\r
226 \r
227         for( x = 0; x < 10; x++ )\r
228         {\r
229                 usLinkStatus = read_PHY( PHY_REG_STS, &lReturn );\r
230                 if( usLinkStatus & emacLINK_ESTABLISHED )\r
231                 {\r
232                         /* Link is established. */\r
233                         lReturn = pdPASS;\r
234                         break;\r
235                 }\r
236 \r
237         vTaskDelay( emacWAIT_FOR_LINK_TO_ESTABLISH );\r
238         }\r
239 \r
240         if( lReturn == pdPASS )\r
241         {\r
242                 /* Configure Full/Half Duplex mode. */\r
243                 if( usLinkStatus & emacFULL_DUPLEX_ENABLED )\r
244                 {\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
249                 }\r
250                 else\r
251                 {\r
252                         /* Half duplex mode. */\r
253                         MAC_IPGT = IPGT_HALF_DUP;\r
254                 }\r
255 \r
256                 /* Configure 100MBit/10MBit mode. */\r
257                 if( usLinkStatus & emac10BASE_T_MODE )\r
258                 {\r
259                         /* 10MBit mode. */\r
260                         MAC_SUPP = 0;\r
261                 }\r
262                 else\r
263                 {\r
264                         /* 100MBit mode. */\r
265                         MAC_SUPP = SUPP_SPEED;\r
266                 }\r
267         }\r
268 \r
269         return lReturn;\r
270 }\r
271 /*-----------------------------------------------------------*/\r
272 \r
273 portBASE_TYPE Init_EMAC( void )\r
274 {\r
275 portBASE_TYPE xReturn = pdPASS;\r
276 volatile unsigned long regv, tout;\r
277 unsigned long ulID1, ulID2;\r
278 \r
279         /* Reset peripherals, configure port pins and registers. */\r
280         prvSetupEMACHardware();\r
281 \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
286         {\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
291 \r
292                 /* Initialize Tx and Rx DMA Descriptors */\r
293                 prvInitDescriptors();\r
294 \r
295                 /* Receive Broadcast and Perfect Match Packets */\r
296                 MAC_RXFILTERCTRL = RFC_UCAST_EN | RFC_BCAST_EN | RFC_PERFECT_EN;\r
297 \r
298                 /* Create the semaphore used to wake the uIP task. */\r
299                 vSemaphoreCreateBinary( xEMACSemaphore );\r
300 \r
301                 /* Setup the PHY. */\r
302                 prvConfigurePHY();\r
303         }\r
304         else\r
305         {\r
306                 xReturn = pdFAIL;\r
307         }\r
308 \r
309         /* Check the link status. */\r
310         if( xReturn == pdPASS )\r
311         {\r
312                 xReturn = prvSetupLinkStatus();\r
313         }\r
314 \r
315         if( xReturn == pdPASS )\r
316         {\r
317                 /* Reset all interrupts */\r
318                 MAC_INTCLEAR = 0xFFFF;\r
319 \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
323         }\r
324 \r
325         return xReturn;\r
326 }\r
327 /*-----------------------------------------------------------*/\r
328 \r
329 // reads a word in little-endian byte order from RX_BUFFER\r
330 unsigned short ReadFrame_EMAC( void )\r
331 {\r
332         return( *rptr++ );\r
333 }\r
334 \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
340 {\r
341         unsigned short  *piDest;        // Keil: Pointer added to correct expression\r
342         piDest = Dest;                          // Keil: Line added\r
343         while( Size > 1 )\r
344         {\r
345                 *piDest++ = ReadFrame_EMAC();\r
346                 Size -= 2;\r
347         }\r
348 \r
349         if( Size )\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
353 }\r
354 \r
355 \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
360 {\r
361         unsigned short  RxLen;\r
362         unsigned int    idx;\r
363 \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
367         return( RxLen );\r
368 }\r
369 \r
370 void EndReadFrame( void )\r
371 {\r
372         unsigned int    idx;\r
373 \r
374         /* DMA free packet. */\r
375         idx = MAC_RXCONSUMEINDEX;\r
376 \r
377         if( ++idx == NUM_RX_FRAG )\r
378         {\r
379                 idx = 0;\r
380         }\r
381 \r
382         MAC_RXCONSUMEINDEX = idx;\r
383 }\r
384 \r
385 unsigned int uiGetEMACRxData( unsigned char *ucBuffer )\r
386 {\r
387         unsigned int    uiLen = 0;\r
388 \r
389         if( MAC_RXPRODUCEINDEX != MAC_RXCONSUMEINDEX )\r
390         {\r
391                 uiLen = StartReadFrame();\r
392                 CopyFromFrame_EMAC( ucBuffer, uiLen );\r
393                 EndReadFrame();\r
394         }\r
395 \r
396         return uiLen;\r
397 }\r
398 \r
399 // requests space in EMAC memory for storing an outgoing frame\r
400 void RequestSend( void )\r
401 {\r
402         unsigned int    idx;\r
403 \r
404         idx = MAC_TXPRODUCEINDEX;\r
405         tptr = ( unsigned short * ) TX_DESC_PACKET( idx );\r
406 }\r
407 \r
408 // writes a word in little-endian byte order to TX_BUFFER\r
409 void WriteFrame_EMAC( unsigned short Data )\r
410 {\r
411         *tptr++ = Data;\r
412 }\r
413 \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
419 {\r
420         unsigned short  *piSource;\r
421 \r
422         piSource = Source;\r
423         Size = ( Size + 1 ) & 0xFFFE;   // round Size up to next even number\r
424         while( Size > 0 )\r
425         {\r
426                 WriteFrame_EMAC( *piSource++ );\r
427                 Size -= 2;\r
428         }\r
429 }\r
430 \r
431 void DoSend_EMAC( unsigned short FrameSize )\r
432 {\r
433         unsigned int    idx;\r
434 \r
435         idx = MAC_TXPRODUCEINDEX;\r
436         TX_DESC_CTRL( idx ) = FrameSize | TCTRL_LAST;\r
437         if( ++idx == NUM_TX_FRAG )\r
438         {\r
439                 idx = 0;\r
440         }\r
441 \r
442         MAC_TXPRODUCEINDEX = idx;\r
443 }\r
444 \r
445 void vEMAC_ISR( void )\r
446 {\r
447         portBASE_TYPE   xHigherPriorityTaskWoken = pdFALSE;\r
448 \r
449         /* Clear the interrupt. */\r
450         MAC_INTCLEAR = 0xffff;\r
451 \r
452         /* Ensure the uIP task is not blocked as data has arrived. */\r
453         xSemaphoreGiveFromISR( xEMACSemaphore, &xHigherPriorityTaskWoken );\r
454 \r
455         portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );\r
456 }\r