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