]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/RX600_RX62N-RSK_Renesas/RTOSDemo/webserver/EMAC.c
126d59c180ca6a7ecc2df650e1f254f0d355e0db
[freertos] / FreeRTOS / Demo / RX600_RX62N-RSK_Renesas / RTOSDemo / webserver / EMAC.c
1 /*\r
2  * FreeRTOS Kernel V10.3.0\r
3  * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4  *\r
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
6  * this software and associated documentation files (the "Software"), to deal in\r
7  * the Software without restriction, including without limitation the rights to\r
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
9  * the Software, and to permit persons to whom the Software is furnished to do so,\r
10  * subject to the following conditions:\r
11  *\r
12  * The above copyright notice and this permission notice shall be included in all\r
13  * copies or substantial portions of the Software.\r
14  *\r
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
17  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
18  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
19  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
21  *\r
22  * http://www.FreeRTOS.org\r
23  * http://aws.amazon.com/freertos\r
24  *\r
25  * 1 tab == 4 spaces!\r
26  */\r
27 \r
28 /* Hardware specific includes. */\r
29 #include "iodefine.h"\r
30 #include "typedefine.h"\r
31 #include "r_ether.h"\r
32 #include "phy.h"\r
33 \r
34 /* FreeRTOS includes. */\r
35 #include "FreeRTOS.h"\r
36 #include "task.h"\r
37 #include "semphr.h"\r
38 \r
39 /* uIP includes. */\r
40 #include "net/uip.h"\r
41 \r
42 /* The time to wait between attempts to obtain a free buffer. */\r
43 #define emacBUFFER_WAIT_DELAY_ms                ( 3 / portTICK_PERIOD_MS )\r
44 \r
45 /* The number of times emacBUFFER_WAIT_DELAY_ms should be waited before giving\r
46 up on attempting to obtain a free buffer all together. */\r
47 #define emacBUFFER_WAIT_ATTEMPTS        ( 30 )\r
48 \r
49 /* The number of Rx descriptors. */\r
50 #define emacNUM_RX_DESCRIPTORS  8\r
51 \r
52 /* The number of Tx descriptors.  When using uIP there is not point in having\r
53 more than two. */\r
54 #define emacNUM_TX_BUFFERS      2\r
55 \r
56 /* The total number of EMAC buffers to allocate. */\r
57 #define emacNUM_BUFFERS         ( emacNUM_RX_DESCRIPTORS + emacNUM_TX_BUFFERS )\r
58 \r
59 /* The time to wait for the Tx descriptor to become free. */\r
60 #define emacTX_WAIT_DELAY_ms ( 10 / portTICK_PERIOD_MS )\r
61 \r
62 /* The total number of times to wait emacTX_WAIT_DELAY_ms for the Tx descriptor to\r
63 become free. */\r
64 #define emacTX_WAIT_ATTEMPTS ( 50 )\r
65 \r
66 /* Only Rx end and Tx end interrupts are used by this driver. */\r
67 #define emacTX_END_INTERRUPT    ( 1UL << 21UL )\r
68 #define emacRX_END_INTERRUPT    ( 1UL << 18UL )\r
69 \r
70 /*-----------------------------------------------------------*/\r
71 \r
72 /* The buffers and descriptors themselves.  */\r
73 #pragma section _RX_DESC\r
74         volatile ethfifo xRxDescriptors[ emacNUM_RX_DESCRIPTORS ];\r
75 #pragma section _TX_DESC\r
76         volatile ethfifo xTxDescriptors[ emacNUM_TX_BUFFERS ];\r
77 #pragma section _ETHERNET_BUFFERS\r
78         struct\r
79         {\r
80                 unsigned long ulAlignmentVariable;\r
81                 char cBuffer[ emacNUM_BUFFERS ][ UIP_BUFSIZE ];\r
82         } xEthernetBuffers;\r
83 #pragma section\r
84 \r
85 \r
86 \r
87 \r
88 /* Used to indicate which buffers are free and which are in use.  If an index\r
89 contains 0 then the corresponding buffer in xEthernetBuffers is free, otherwise \r
90 the buffer is in use or about to be used. */\r
91 static unsigned char ucBufferInUse[ emacNUM_BUFFERS ];\r
92 \r
93 /*-----------------------------------------------------------*/\r
94 \r
95 /*\r
96  * Initialise both the Rx and Tx descriptors.\r
97  */\r
98 static void prvInitialiseDescriptors( void );\r
99 \r
100 /*\r
101  * Return a pointer to a free buffer within xEthernetBuffers.\r
102  */\r
103 static unsigned char *prvGetNextBuffer( void );\r
104 \r
105 /*\r
106  * Return a buffer to the list of free buffers.\r
107  */\r
108 static void prvReturnBuffer( unsigned char *pucBuffer );\r
109 \r
110 /*\r
111  * Examine the status of the next Rx FIFO to see if it contains new data.\r
112  */\r
113 static unsigned long prvCheckRxFifoStatus( void );\r
114 \r
115 /*\r
116  * Setup the microcontroller for communication with the PHY.\r
117  */\r
118 static void prvResetMAC( void );\r
119 \r
120 /*\r
121  * Configure the Ethernet interface peripherals.\r
122  */\r
123 static void prvConfigureEtherCAndEDMAC( void );\r
124 \r
125 /*\r
126  * Something has gone wrong with the descriptor usage.  Reset all the buffers\r
127  * and descriptors.\r
128  */\r
129 static void prvResetEverything( void );\r
130 \r
131 /*-----------------------------------------------------------*/\r
132 \r
133 /* Points to the Rx descriptor currently in use. */\r
134 static ethfifo *pxCurrentRxDesc = NULL;\r
135 \r
136 /* The buffer used by the uIP stack to both receive and send.  This points to\r
137 one of the Ethernet buffers when its actually in use. */\r
138 unsigned char *uip_buf = NULL;\r
139 \r
140 /*-----------------------------------------------------------*/\r
141 \r
142 void vInitEmac( void )\r
143 {\r
144         /* Software reset. */\r
145         prvResetMAC();\r
146         \r
147         /* Set the Rx and Tx descriptors into their initial state. */\r
148         prvInitialiseDescriptors();\r
149 \r
150         /* Set the MAC address into the ETHERC */\r
151         ETHERC.MAHR =   ( ( unsigned long ) configMAC_ADDR0 << 24UL ) | \r
152                                         ( ( unsigned long ) configMAC_ADDR1 << 16UL ) | \r
153                                         ( ( unsigned long ) configMAC_ADDR2 << 8UL ) | \r
154                                         ( unsigned long ) configMAC_ADDR3;\r
155                                         \r
156         ETHERC.MALR.BIT.MA = ( ( unsigned long ) configMAC_ADDR4 << 8UL ) |\r
157                                                  ( unsigned long ) configMAC_ADDR5;\r
158 \r
159         /* Perform rest of interface hardware configuration. */\r
160         prvConfigureEtherCAndEDMAC();\r
161         \r
162         /* Nothing received yet, so uip_buf points nowhere. */\r
163         uip_buf = NULL;\r
164 \r
165         /* Initialize the PHY */\r
166         phy_init();\r
167 }\r
168 /*-----------------------------------------------------------*/\r
169 \r
170 void vEMACWrite( void )\r
171 {\r
172 long x;\r
173 \r
174         /* Wait until the second transmission of the last packet has completed. */\r
175         for( x = 0; x < emacTX_WAIT_ATTEMPTS; x++ )\r
176         {\r
177                 if( ( xTxDescriptors[ 1 ].status & ACT ) != 0 )\r
178                 {\r
179                         /* Descriptor is still active. */\r
180                         vTaskDelay( emacTX_WAIT_DELAY_ms );\r
181                 }\r
182                 else\r
183                 {\r
184                         break;\r
185                 }\r
186         }\r
187         \r
188         /* Is the descriptor free after waiting for it? */\r
189         if( ( xTxDescriptors[ 1 ].status & ACT ) != 0 )\r
190         {\r
191                 /* Something has gone wrong. */\r
192                 prvResetEverything();\r
193         }\r
194         \r
195         /* Setup both descriptors to transmit the frame. */\r
196         xTxDescriptors[ 0 ].buf_p = ( char * ) uip_buf;\r
197         xTxDescriptors[ 0 ].bufsize = uip_len;  \r
198         xTxDescriptors[ 1 ].buf_p = ( char * ) uip_buf;\r
199         xTxDescriptors[ 1 ].bufsize = uip_len;\r
200 \r
201         /* uip_buf is being sent by the Tx descriptor.  Allocate a new buffer\r
202         for use by the stack. */\r
203         uip_buf = prvGetNextBuffer();\r
204 \r
205         /* Clear previous settings and go. */\r
206         xTxDescriptors[0].status &= ~( FP1 | FP0 );\r
207         xTxDescriptors[0].status |= ( FP1 | FP0 | ACT );\r
208         xTxDescriptors[1].status &= ~( FP1 | FP0 );\r
209         xTxDescriptors[1].status |= ( FP1 | FP0 | ACT );\r
210 \r
211         EDMAC.EDTRR.LONG = 0x00000001;\r
212 }\r
213 /*-----------------------------------------------------------*/\r
214 \r
215 unsigned long ulEMACRead( void )\r
216 {\r
217 unsigned long ulBytesReceived;\r
218 \r
219         ulBytesReceived = prvCheckRxFifoStatus();\r
220 \r
221         if( ulBytesReceived > 0 )\r
222         {\r
223                 /* Mark the pxDescriptor buffer as free as uip_buf is going to be set to\r
224                 the buffer that contains the received data. */\r
225                 prvReturnBuffer( uip_buf );\r
226 \r
227                 /* Point uip_buf to the data about ot be processed. */\r
228                 uip_buf = ( void * ) pxCurrentRxDesc->buf_p;\r
229                 \r
230                 /* Allocate a new buffer to the descriptor, as uip_buf is now using it's\r
231                 old descriptor. */\r
232                 pxCurrentRxDesc->buf_p = prvGetNextBuffer();\r
233 \r
234                 /* Prepare the descriptor to go again. */\r
235                 pxCurrentRxDesc->status &= ~( FP1 | FP0 );\r
236                 pxCurrentRxDesc->status |= ACT;\r
237 \r
238                 /* Move onto the next buffer in the ring. */\r
239                 pxCurrentRxDesc = pxCurrentRxDesc->next;\r
240                 \r
241                 if( EDMAC.EDRRR.LONG == 0x00000000L )\r
242                 {\r
243                         /* Restart Ethernet if it has stopped */\r
244                         EDMAC.EDRRR.LONG = 0x00000001L;\r
245                 }\r
246         }\r
247 \r
248         return ulBytesReceived;\r
249 }\r
250 /*-----------------------------------------------------------*/\r
251 \r
252 long lEMACWaitForLink( void )\r
253 {\r
254 long lReturn;\r
255 \r
256         /* Set the link status. */\r
257         switch( phy_set_autonegotiate() )\r
258         {\r
259                 /* Half duplex link */\r
260                 case PHY_LINK_100H:\r
261                                                                 ETHERC.ECMR.BIT.DM = 0;\r
262                                                                 ETHERC.ECMR.BIT.RTM = 1;\r
263                                                                 lReturn = pdPASS;\r
264                                                                 break;\r
265 \r
266                 case PHY_LINK_10H:\r
267                                                                 ETHERC.ECMR.BIT.DM = 0;\r
268                                                                 ETHERC.ECMR.BIT.RTM = 0;\r
269                                                                 lReturn = pdPASS;\r
270                                                                 break;\r
271 \r
272 \r
273                 /* Full duplex link */\r
274                 case PHY_LINK_100F:\r
275                                                                 ETHERC.ECMR.BIT.DM = 1;\r
276                                                                 ETHERC.ECMR.BIT.RTM = 1;\r
277                                                                 lReturn = pdPASS;\r
278                                                                 break;\r
279                 \r
280                 case PHY_LINK_10F:\r
281                                                                 ETHERC.ECMR.BIT.DM = 1;\r
282                                                                 ETHERC.ECMR.BIT.RTM = 0;\r
283                                                                 lReturn = pdPASS;\r
284                                                                 break;\r
285 \r
286                 default:\r
287                                                                 lReturn = pdFAIL;\r
288                                                                 break;\r
289         }\r
290 \r
291         if( lReturn == pdPASS )\r
292         {\r
293                 /* Enable receive and transmit. */\r
294                 ETHERC.ECMR.BIT.RE = 1;\r
295                 ETHERC.ECMR.BIT.TE = 1;\r
296 \r
297                 /* Enable EDMAC receive */\r
298                 EDMAC.EDRRR.LONG = 0x1;\r
299         }\r
300         \r
301         return lReturn;\r
302 }\r
303 /*-----------------------------------------------------------*/\r
304 \r
305 static void prvInitialiseDescriptors( void )\r
306 {\r
307 ethfifo *pxDescriptor;\r
308 long x;\r
309 \r
310         for( x = 0; x < emacNUM_BUFFERS; x++ )\r
311         {\r
312                 /* Ensure none of the buffers are shown as in use at the start. */\r
313                 ucBufferInUse[ x ] = pdFALSE;\r
314         }\r
315 \r
316         /* Initialise the Rx descriptors. */\r
317         for( x = 0; x < emacNUM_RX_DESCRIPTORS; x++ )\r
318         {\r
319                 pxDescriptor = &( xRxDescriptors[ x ] );\r
320                 pxDescriptor->buf_p = &( xEthernetBuffers.cBuffer[ x ][ 0 ] );\r
321 \r
322                 pxDescriptor->bufsize = UIP_BUFSIZE;\r
323                 pxDescriptor->size = 0;\r
324                 pxDescriptor->status = ACT;\r
325                 pxDescriptor->next = &xRxDescriptors[ x + 1 ];  \r
326                 \r
327                 /* Mark this buffer as in use. */\r
328                 ucBufferInUse[ x ] = pdTRUE;\r
329         }\r
330 \r
331         /* The last descriptor points back to the start. */\r
332         pxDescriptor->status |= DL;\r
333         pxDescriptor->next = &xRxDescriptors[ 0 ];\r
334         \r
335         /* Initialise the Tx descriptors. */\r
336         for( x = 0; x < emacNUM_TX_BUFFERS; x++ )\r
337         {\r
338                 pxDescriptor = &( xTxDescriptors[ x ] );\r
339                 \r
340                 /* A buffer is not allocated to the Tx descriptor until a send is\r
341                 actually required. */\r
342                 pxDescriptor->buf_p = NULL;\r
343 \r
344                 pxDescriptor->bufsize = UIP_BUFSIZE;\r
345                 pxDescriptor->size = 0;\r
346                 pxDescriptor->status = 0;\r
347                 pxDescriptor->next = &xTxDescriptors[ x + 1 ];  \r
348         }\r
349 \r
350         /* The last descriptor points back to the start. */\r
351         pxDescriptor->status |= DL;\r
352         pxDescriptor->next = &( xTxDescriptors[ 0 ] );\r
353         \r
354         /* Use the first Rx descriptor to start with. */\r
355         pxCurrentRxDesc = &( xRxDescriptors[ 0 ] );\r
356 }\r
357 /*-----------------------------------------------------------*/\r
358 \r
359 static unsigned char *prvGetNextBuffer( void )\r
360 {\r
361 long x;\r
362 unsigned char *pucReturn = NULL;\r
363 unsigned long ulAttempts = 0;\r
364 \r
365         while( pucReturn == NULL )\r
366         {\r
367                 /* Look through the buffers to find one that is not in use by\r
368                 anything else. */\r
369                 for( x = 0; x < emacNUM_BUFFERS; x++ )\r
370                 {\r
371                         if( ucBufferInUse[ x ] == pdFALSE )\r
372                         {\r
373                                 ucBufferInUse[ x ] = pdTRUE;\r
374                                 pucReturn = ( unsigned char * ) &( xEthernetBuffers.cBuffer[ x ][ 0 ] );\r
375                                 break;\r
376                         }\r
377                 }\r
378 \r
379                 /* Was a buffer found? */\r
380                 if( pucReturn == NULL )\r
381                 {\r
382                         ulAttempts++;\r
383 \r
384                         if( ulAttempts >= emacBUFFER_WAIT_ATTEMPTS )\r
385                         {\r
386                                 break;\r
387                         }\r
388 \r
389                         /* Wait then look again. */\r
390                         vTaskDelay( emacBUFFER_WAIT_DELAY_ms );\r
391                 }\r
392         }\r
393 \r
394         return pucReturn;\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         /* Return a buffer to the pool of free buffers. */\r
403         for( ul = 0; ul < emacNUM_BUFFERS; ul++ )\r
404         {\r
405                 if( &( xEthernetBuffers.cBuffer[ ul ][ 0 ] ) == ( void * ) pucBuffer )\r
406                 {\r
407                         ucBufferInUse[ ul ] = pdFALSE;\r
408                         break;\r
409                 }\r
410         }\r
411 }\r
412 /*-----------------------------------------------------------*/\r
413 \r
414 static void prvResetEverything( void )\r
415 {\r
416         /* Temporary code just to see if this gets called.  This function has not\r
417         been implemented. */\r
418         portDISABLE_INTERRUPTS();\r
419         for( ;; );\r
420 }\r
421 /*-----------------------------------------------------------*/\r
422 \r
423 static unsigned long prvCheckRxFifoStatus( void )\r
424 {\r
425 unsigned long ulReturn = 0;\r
426 \r
427         if( ( pxCurrentRxDesc->status & ACT ) != 0 )\r
428         {\r
429                 /* Current descriptor is still active. */\r
430         }\r
431         else if( ( pxCurrentRxDesc->status & FE ) != 0 )\r
432         {\r
433                 /* Frame error.  Clear the error. */\r
434                 pxCurrentRxDesc->status &= ~( FP1 | FP0 | FE );\r
435                 pxCurrentRxDesc->status &= ~( RMAF | RRF | RTLF | RTSF | PRE | CERF );\r
436                 pxCurrentRxDesc->status |= ACT;\r
437                 pxCurrentRxDesc = pxCurrentRxDesc->next;\r
438 \r
439                 if( EDMAC.EDRRR.LONG == 0x00000000UL )\r
440                 {\r
441                         /* Restart Ethernet if it has stopped. */\r
442                         EDMAC.EDRRR.LONG = 0x00000001UL;\r
443                 }       \r
444         }\r
445         else\r
446         {\r
447                 /* The descriptor contains a frame.  Because of the size of the buffers\r
448                 the frame should always be complete. */\r
449                 if( ( pxCurrentRxDesc->status & FP0 ) == FP0 )\r
450                 {\r
451                         ulReturn = pxCurrentRxDesc->size;\r
452                 }\r
453                 else\r
454                 {\r
455                         /* Do not expect to get here. */\r
456                         prvResetEverything();\r
457                 }\r
458         }\r
459         \r
460         return ulReturn;\r
461 }\r
462 /*-----------------------------------------------------------*/\r
463 \r
464 static void prvResetMAC( void )\r
465 {\r
466         /* Ensure the EtherC and EDMAC are enabled. */\r
467         SYSTEM.MSTPCRB.BIT.MSTPB15 = 0;\r
468         vTaskDelay( 100 / portTICK_PERIOD_MS );\r
469         \r
470         EDMAC.EDMR.BIT.SWR = 1; \r
471         \r
472         /* Crude wait for reset to complete. */\r
473         vTaskDelay( 500 / portTICK_PERIOD_MS ); \r
474 }\r
475 /*-----------------------------------------------------------*/\r
476 \r
477 static void prvConfigureEtherCAndEDMAC( void )\r
478 {\r
479         /* Initialisation code taken from Renesas example project. */\r
480         \r
481         /* TODO:    Check   bit 5   */\r
482         ETHERC.ECSR.LONG = 0x00000037;                          /* Clear all ETHERC statuS BFR, PSRTO, LCHNG, MPD, ICD */\r
483 \r
484         /* Set the EDMAC interrupt priority. */\r
485         _IPR( _ETHER_EINT ) = configKERNEL_INTERRUPT_PRIORITY;\r
486 \r
487         /* TODO:    Check   bit 5   */\r
488         /* Enable interrupts of interest only. */\r
489         EDMAC.EESIPR.LONG = emacTX_END_INTERRUPT | emacRX_END_INTERRUPT;\r
490         ETHERC.RFLR.LONG = 1518;                                        /* Ether payload is 1500+ CRC */\r
491         ETHERC.IPGR.LONG = 0x00000014;                          /* Intergap is 96-bit time */\r
492 \r
493         /* EDMAC */\r
494         EDMAC.EESR.LONG = 0x47FF0F9F;                           /* Clear all ETHERC and EDMAC status bits */\r
495         #ifdef __LIT\r
496                 EDMAC.EDMR.BIT.DE = 1;\r
497         #endif\r
498         EDMAC.RDLAR = ( void * ) pxCurrentRxDesc;       /* Initialaize Rx Descriptor List Address */\r
499         EDMAC.TDLAR = &( xTxDescriptors[ 0 ] );         /* Initialaize Tx Descriptor List Address */\r
500         EDMAC.TRSCER.LONG = 0x00000000;                         /* Copy-back status is RFE & TFE only   */\r
501         EDMAC.TFTR.LONG = 0x00000000;                           /* Threshold of Tx_FIFO */\r
502         EDMAC.FDR.LONG = 0x00000000;                            /* Transmit fifo & receive fifo is 256 bytes */\r
503         EDMAC.RMCR.LONG = 0x00000003;                           /* Receive function is normal mode(continued) */\r
504         ETHERC.ECMR.BIT.PRM = 0;                                        /* Ensure promiscuous mode is off. */\r
505                 \r
506         /* Enable the interrupt... */\r
507         _IEN( _ETHER_EINT ) = 1;        \r
508 }\r
509 /*-----------------------------------------------------------*/\r
510 \r
511 #pragma interrupt ( vEMAC_ISR_Handler( vect = VECT_ETHER_EINT, enable ) )\r
512 void vEMAC_ISR_Handler( void )\r
513 {\r
514 unsigned long ul = EDMAC.EESR.LONG;\r
515 long lHigherPriorityTaskWoken = pdFALSE;\r
516 extern QueueHandle_t xEMACEventQueue;\r
517 const unsigned long ulRxEvent = uipETHERNET_RX_EVENT;\r
518 \r
519         /* Has a Tx end occurred? */\r
520         if( ul & emacTX_END_INTERRUPT )\r
521         {\r
522                 /* Only return the buffer to the pool once both Txes have completed. */\r
523                 prvReturnBuffer( ( void * ) xTxDescriptors[ 0 ].buf_p );\r
524                 EDMAC.EESR.LONG = emacTX_END_INTERRUPT;\r
525         }\r
526 \r
527         /* Has an Rx end occurred? */\r
528         if( ul & emacRX_END_INTERRUPT )\r
529         {\r
530                 /* Make sure the Ethernet task is not blocked waiting for a packet. */\r
531                 xQueueSendFromISR( xEMACEventQueue, &ulRxEvent, &lHigherPriorityTaskWoken );\r
532                 portYIELD_FROM_ISR( lHigherPriorityTaskWoken );\r
533                 EDMAC.EESR.LONG = emacRX_END_INTERRUPT;\r
534         }\r
535 }\r
536 \r