2 * FreeRTOS Kernel V10.0.0
\r
3 * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
\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
12 * The above copyright notice and this permission notice shall be included in all
\r
13 * copies or substantial portions of the Software. If you wish to use our Amazon
\r
14 * FreeRTOS name, please do so in a fair use way that does not cause confusion.
\r
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
\r
18 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
\r
19 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
\r
20 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
\r
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
23 * http://www.FreeRTOS.org
\r
24 * http://aws.amazon.com/freertos
\r
26 * 1 tab == 4 spaces!
\r
29 /* Hardware specific includes. */
\r
30 #include "iodefine.h"
\r
31 #include "typedefine.h"
\r
32 #include "hwEthernet.h"
\r
33 #include "hwEthernetPhy.h"
\r
35 /* FreeRTOS includes. */
\r
36 #include "FreeRTOS.h"
\r
41 #include "net/uip.h"
\r
43 /* The time to wait between attempts to obtain a free buffer. */
\r
44 #define emacBUFFER_WAIT_DELAY_ms ( 3 / portTICK_PERIOD_MS )
\r
46 /* The number of times emacBUFFER_WAIT_DELAY_ms should be waited before giving
\r
47 up on attempting to obtain a free buffer all together. */
\r
48 #define emacBUFFER_WAIT_ATTEMPTS ( 30 )
\r
50 /* The number of Rx descriptors. */
\r
51 #define emacNUM_RX_DESCRIPTORS 3
\r
53 /* The number of Tx descriptors. When using uIP there is not point in having
\r
55 #define emacNUM_TX_BUFFERS 2
\r
57 /* The total number of EMAC buffers to allocate. */
\r
58 #define emacNUM_BUFFERS ( emacNUM_RX_DESCRIPTORS + emacNUM_TX_BUFFERS )
\r
60 /* The time to wait for the Tx descriptor to become free. */
\r
61 #define emacTX_WAIT_DELAY_ms ( 10 / portTICK_PERIOD_MS )
\r
63 /* The total number of times to wait emacTX_WAIT_DELAY_ms for the Tx descriptor to
\r
65 #define emacTX_WAIT_ATTEMPTS ( 5 )
\r
67 /* Only Rx end and Tx end interrupts are used by this driver. */
\r
68 #define emacTX_END_INTERRUPT ( 1UL << 21UL )
\r
69 #define emacRX_END_INTERRUPT ( 1UL << 18UL )
\r
71 /*-----------------------------------------------------------*/
\r
73 /* The buffers and descriptors themselves. */
\r
74 #pragma section RX_DESCR
\r
75 ethfifo xRxDescriptors[ emacNUM_RX_DESCRIPTORS ];
\r
76 #pragma section TX_DESCR
\r
77 ethfifo xTxDescriptors[ emacNUM_TX_BUFFERS ];
\r
78 #pragma section _ETHERNET_BUFFERS
\r
79 char xEthernetBuffers[ emacNUM_BUFFERS ][ UIP_BUFSIZE ];
\r
82 /* Used to indicate which buffers are free and which are in use. If an index
\r
83 contains 0 then the corresponding buffer in xEthernetBuffers is free, otherwise
\r
84 the buffer is in use or about to be used. */
\r
85 static unsigned char ucBufferInUse[ emacNUM_BUFFERS ];
\r
87 /*-----------------------------------------------------------*/
\r
90 * Initialise both the Rx and Tx descriptors.
\r
92 static void prvInitialiseDescriptors( void );
\r
95 * Return a pointer to a free buffer within xEthernetBuffers.
\r
97 static unsigned char *prvGetNextBuffer( void );
\r
100 * Return a buffer to the list of free buffers.
\r
102 static void prvReturnBuffer( unsigned char *pucBuffer );
\r
105 * Examine the status of the next Rx FIFO to see if it contains new data.
\r
107 static unsigned long prvCheckRxFifoStatus( void );
\r
110 * Setup the microcontroller for communication with the PHY.
\r
112 static void prvSetupPortPinsAndReset( void );
\r
115 * Configure the Ethernet interface peripherals.
\r
117 static void prvConfigureEtherCAndEDMAC( void );
\r
120 * Something has gone wrong with the descriptor usage. Reset all the buffers
\r
123 static void prvResetEverything( void );
\r
125 /*-----------------------------------------------------------*/
\r
127 /* Points to the Rx descriptor currently in use. */
\r
128 static ethfifo *xCurrentRxDesc = NULL;
\r
130 /* The buffer used by the uIP stack to both receive and send. This points to
\r
131 one of the Ethernet buffers when its actually in use. */
\r
132 unsigned char *uip_buf = NULL;
\r
134 /*-----------------------------------------------------------*/
\r
136 void vInitEmac( void )
\r
138 /* Setup the SH hardware for MII communications. */
\r
139 prvSetupPortPinsAndReset();
\r
141 /* Set the Rx and Tx descriptors into their initial state. */
\r
142 prvInitialiseDescriptors();
\r
144 /* Set the MAC address into the ETHERC */
\r
145 EtherC.MAHR = ( ( unsigned long ) configMAC_ADDR0 << 24UL ) |
\r
146 ( ( unsigned long ) configMAC_ADDR1 << 16UL ) |
\r
147 ( ( unsigned long ) configMAC_ADDR2 << 8UL ) |
\r
148 ( unsigned long ) configMAC_ADDR3;
\r
150 EtherC.MALR.BIT.MA = ( ( unsigned long ) configMAC_ADDR4 << 8UL ) |
\r
151 ( unsigned long ) configMAC_ADDR5;
\r
153 /* Perform rest of interface hardware configuration. */
\r
154 prvConfigureEtherCAndEDMAC();
\r
156 /* Nothing received yet, so uip_buf points nowhere. */
\r
159 /* Initialize the PHY */
\r
162 /*-----------------------------------------------------------*/
\r
164 void vEMACWrite( void )
\r
168 /* Wait until the second transmission of the last packet has completed. */
\r
169 for( x = 0; x < emacTX_WAIT_ATTEMPTS; x++ )
\r
171 if( ( xTxDescriptors[ 1 ].status & ACT ) != 0 )
\r
173 /* Descriptor is still active. */
\r
174 vTaskDelay( emacTX_WAIT_DELAY_ms );
\r
182 /* Is the descriptor free after waiting for it? */
\r
183 if( ( xTxDescriptors[ 1 ].status & ACT ) != 0 )
\r
185 /* Something has gone wrong. */
\r
186 prvResetEverything();
\r
189 /* Setup both descriptors to transmit the frame. */
\r
190 xTxDescriptors[ 0 ].buf_p = ( char * ) uip_buf;
\r
191 xTxDescriptors[ 0 ].bufsize = uip_len;
\r
192 xTxDescriptors[ 1 ].buf_p = ( char * ) uip_buf;
\r
193 xTxDescriptors[ 1 ].bufsize = uip_len;
\r
195 /* uip_buf is being sent by the Tx descriptor. Allocate a new buffer
\r
196 for use by the stack. */
\r
197 uip_buf = prvGetNextBuffer();
\r
199 /* Clear previous settings and go. */
\r
200 xTxDescriptors[0].status &= ~( FP1 | FP0 );
\r
201 xTxDescriptors[0].status |= ( FP1 | FP0 | ACT );
\r
202 xTxDescriptors[1].status &= ~( FP1 | FP0 );
\r
203 xTxDescriptors[1].status |= ( FP1 | FP0 | ACT );
\r
205 EDMAC.EDTRR.LONG = 0x00000001;
\r
207 /*-----------------------------------------------------------*/
\r
209 unsigned long ulEMACRead( void )
\r
211 unsigned long ulBytesReceived;
\r
213 ulBytesReceived = prvCheckRxFifoStatus();
\r
215 if( ulBytesReceived > 0 )
\r
217 xCurrentRxDesc->status &= ~( FP1 | FP0 );
\r
218 xCurrentRxDesc->status |= ACT;
\r
220 if( EDMAC.EDRRR.LONG == 0x00000000L )
\r
222 /* Restart Ethernet if it has stopped */
\r
223 EDMAC.EDRRR.LONG = 0x00000001L;
\r
226 /* Mark the pxDescriptor buffer as free as uip_buf is going to be set to
\r
227 the buffer that contains the received data. */
\r
228 prvReturnBuffer( uip_buf );
\r
230 uip_buf = ( void * ) xCurrentRxDesc->buf_p;
\r
232 /* Move onto the next buffer in the ring. */
\r
233 xCurrentRxDesc = xCurrentRxDesc->next;
\r
236 return ulBytesReceived;
\r
238 /*-----------------------------------------------------------*/
\r
240 long lEMACWaitForLink( void )
\r
244 /* Set the link status. */
\r
245 switch( phyStatus() )
\r
247 /* Half duplex link */
\r
248 case PHY_LINK_100H:
\r
250 EtherC.ECMR.BIT.DM = 0;
\r
254 /* Full duplex link */
\r
255 case PHY_LINK_100F:
\r
257 EtherC.ECMR.BIT.DM = 1;
\r
266 if( lReturn == pdPASS )
\r
268 /* Enable receive and transmit. */
\r
269 EtherC.ECMR.BIT.RE = 1;
\r
270 EtherC.ECMR.BIT.TE = 1;
\r
272 /* Enable EDMAC receive */
\r
273 EDMAC.EDRRR.LONG = 0x1;
\r
278 /*-----------------------------------------------------------*/
\r
280 static void prvInitialiseDescriptors( void )
\r
282 ethfifo *pxDescriptor;
\r
285 for( x = 0; x < emacNUM_BUFFERS; x++ )
\r
287 /* Ensure none of the buffers are shown as in use at the start. */
\r
288 ucBufferInUse[ x ] = pdFALSE;
\r
291 /* Initialise the Rx descriptors. */
\r
292 for( x = 0; x < emacNUM_RX_DESCRIPTORS; x++ )
\r
294 pxDescriptor = &( xRxDescriptors[ x ] );
\r
295 pxDescriptor->buf_p = &( xEthernetBuffers[ x ][ 0 ] );
\r
297 pxDescriptor->bufsize = UIP_BUFSIZE;
\r
298 pxDescriptor->size = 0;
\r
299 pxDescriptor->status = ACT;
\r
300 pxDescriptor->next = &xRxDescriptors[ x + 1 ];
\r
302 /* Mark this buffer as in use. */
\r
303 ucBufferInUse[ x ] = pdTRUE;
\r
306 /* The last descriptor points back to the start. */
\r
307 pxDescriptor->status |= DL;
\r
308 pxDescriptor->next = &xRxDescriptors[ 0 ];
\r
310 /* Initialise the Tx descriptors. */
\r
311 for( x = 0; x < emacNUM_TX_BUFFERS; x++ )
\r
313 pxDescriptor = &( xTxDescriptors[ x ] );
\r
315 /* A buffer is not allocated to the Tx descriptor until a send is
\r
316 actually required. */
\r
317 pxDescriptor->buf_p = NULL;
\r
319 pxDescriptor->bufsize = UIP_BUFSIZE;
\r
320 pxDescriptor->size = 0;
\r
321 pxDescriptor->status = 0;
\r
322 pxDescriptor->next = &xTxDescriptors[ x + 1 ];
\r
325 /* The last descriptor points back to the start. */
\r
326 pxDescriptor->status |= DL;
\r
327 pxDescriptor->next = &( xTxDescriptors[ 0 ] );
\r
329 /* Use the first Rx descriptor to start with. */
\r
330 xCurrentRxDesc = &( xRxDescriptors[ 0 ] );
\r
332 /*-----------------------------------------------------------*/
\r
334 static unsigned char *prvGetNextBuffer( void )
\r
337 unsigned char *pucReturn = NULL;
\r
338 unsigned long ulAttempts = 0;
\r
340 while( pucReturn == NULL )
\r
342 /* Look through the buffers to find one that is not in use by
\r
344 for( x = 0; x < emacNUM_BUFFERS; x++ )
\r
346 if( ucBufferInUse[ x ] == pdFALSE )
\r
348 ucBufferInUse[ x ] = pdTRUE;
\r
349 pucReturn = ( unsigned char * ) &( xEthernetBuffers[ x ][ 0 ] );
\r
354 /* Was a buffer found? */
\r
355 if( pucReturn == NULL )
\r
359 if( ulAttempts >= emacBUFFER_WAIT_ATTEMPTS )
\r
364 /* Wait then look again. */
\r
365 vTaskDelay( emacBUFFER_WAIT_DELAY_ms );
\r
371 /*-----------------------------------------------------------*/
\r
373 static void prvReturnBuffer( unsigned char *pucBuffer )
\r
377 /* Return a buffer to the pool of free buffers. */
\r
378 for( ul = 0; ul < emacNUM_BUFFERS; ul++ )
\r
380 if( &( xEthernetBuffers[ ul ][ 0 ] ) == ( void * ) pucBuffer )
\r
382 ucBufferInUse[ ul ] = pdFALSE;
\r
387 /*-----------------------------------------------------------*/
\r
389 static void prvResetEverything( void )
\r
391 /* Temporary code just to see if this gets called. This function has not
\r
392 been implemented. */
\r
393 portDISABLE_INTERRUPTS();
\r
396 /*-----------------------------------------------------------*/
\r
398 static unsigned long prvCheckRxFifoStatus( void )
\r
400 unsigned long ulReturn = 0;
\r
402 if( ( xCurrentRxDesc->status & ACT ) != 0 )
\r
404 /* Current descriptor is still active. */
\r
406 else if( ( xCurrentRxDesc->status & FE ) != 0 )
\r
408 /* Frame error. Clear the error. */
\r
409 xCurrentRxDesc->status &= ~( FP1 | FP0 | FE );
\r
410 xCurrentRxDesc->status &= ~( RMAF | RRF | RTLF | RTSF | PRE | CERF );
\r
411 xCurrentRxDesc->status |= ACT;
\r
412 xCurrentRxDesc = xCurrentRxDesc->next;
\r
414 if( EDMAC.EDRRR.LONG == 0x00000000UL )
\r
416 /* Restart Ethernet if it has stopped. */
\r
417 EDMAC.EDRRR.LONG = 0x00000001UL;
\r
422 /* The descriptor contains a frame. Because of the size of the buffers
\r
423 the frame should always be complete. */
\r
424 if( (xCurrentRxDesc->status & FP0) == FP0 )
\r
426 ulReturn = xCurrentRxDesc->size;
\r
430 /* Do not expect to get here. */
\r
431 prvResetEverything();
\r
437 /*-----------------------------------------------------------*/
\r
439 static void prvSetupPortPinsAndReset( void )
\r
441 /* Initialisation code taken from Renesas example project. */
\r
443 PFC.PACRL4.BIT.PA12MD = 0x7; /* Set TX_CLK input (EtherC) */
\r
444 PFC.PACRL3.BIT.PA11MD = 0x7; /* Set TX_EN output (EtherC) */
\r
445 PFC.PACRL3.BIT.PA10MD = 0x7; /* Set MII_TXD0 output (EtherC) */
\r
446 PFC.PACRL3.BIT.PA9MD = 0x7; /* Set MII_TXD1 output (EtherC) */
\r
447 PFC.PACRL3.BIT.PA8MD = 0x7; /* Set MII_TXD2 output (EtherC) */
\r
448 PFC.PACRL2.BIT.PA7MD = 0x7; /* Set MII_TXD3 output (EtherC) */
\r
449 PFC.PACRL2.BIT.PA6MD = 0x7; /* Set TX_ER output (EtherC) */
\r
450 PFC.PDCRH4.BIT.PD31MD = 0x7; /* Set RX_DV input (EtherC) */
\r
451 PFC.PDCRH4.BIT.PD30MD = 0x7; /* Set RX_ER input (EtherC) */
\r
452 PFC.PDCRH4.BIT.PD29MD = 0x7; /* Set MII_RXD3 input (EtherC) */
\r
453 PFC.PDCRH4.BIT.PD28MD = 0x7; /* Set MII_RXD2 input (EtherC) */
\r
454 PFC.PDCRH3.BIT.PD27MD = 0x7; /* Set MII_RXD1 input (EtherC) */
\r
455 PFC.PDCRH3.BIT.PD26MD = 0x7; /* Set MII_RXD0 input (EtherC) */
\r
456 PFC.PDCRH3.BIT.PD25MD = 0x7; /* Set RX_CLK input (EtherC) */
\r
457 PFC.PDCRH3.BIT.PD24MD = 0x7; /* Set CRS input (EtherC) */
\r
458 PFC.PDCRH2.BIT.PD23MD = 0x7; /* Set COL input (EtherC) */
\r
459 PFC.PDCRH2.BIT.PD22MD = 0x7; /* Set WOL output (EtherC) */
\r
460 PFC.PDCRH2.BIT.PD21MD = 0x7; /* Set EXOUT output (EtherC) */
\r
461 PFC.PDCRH2.BIT.PD20MD = 0x7; /* Set MDC output (EtherC) */
\r
462 PFC.PDCRH1.BIT.PD19MD = 0x7; /* Set LINKSTA input (EtherC) */
\r
463 PFC.PDCRH1.BIT.PD18MD = 0x7; /* Set MDIO input/output (EtherC) */
\r
465 STB.CR4.BIT._ETHER = 0x0;
\r
466 EDMAC.EDMR.BIT.SWR = 1;
\r
468 /* Crude wait for reset to complete. */
\r
469 vTaskDelay( 500 / portTICK_PERIOD_MS );
\r
471 /*-----------------------------------------------------------*/
\r
473 static void prvConfigureEtherCAndEDMAC( void )
\r
475 /* Initialisation code taken from Renesas example project. */
\r
477 /* TODO: Check bit 5 */
\r
478 EtherC.ECSR.LONG = 0x00000037; /* Clear all EtherC statuS BFR, PSRTO, LCHNG, MPD, ICD */
\r
480 /* TODO: Check bit 5 */
\r
481 EtherC.ECSIPR.LONG = 0x00000020; /* Disable EtherC status change interrupt */
\r
482 EtherC.RFLR.LONG = 1518; /* Ether payload is 1500+ CRC */
\r
483 EtherC.IPGR.LONG = 0x00000014; /* Intergap is 96-bit time */
\r
486 EDMAC.EESR.LONG = 0x47FF0F9F; /* Clear all EtherC and EDMAC status bits */
\r
487 EDMAC.RDLAR = ( void * ) xCurrentRxDesc; /* Initialaize Rx Descriptor List Address */
\r
488 EDMAC.TDLAR = &( xTxDescriptors[ 0 ] ); /* Initialaize Tx Descriptor List Address */
\r
489 EDMAC.TRSCER.LONG = 0x00000000; /* Copy-back status is RFE & TFE only */
\r
490 EDMAC.TFTR.LONG = 0x00000000; /* Threshold of Tx_FIFO */
\r
491 EDMAC.FDR.LONG = 0x00000000; /* Transmit fifo & receive fifo is 256 bytes */
\r
492 EDMAC.RMCR.LONG = 0x00000003; /* Receive function is normal mode(continued) */
\r
494 /* Set the EDMAC interrupt priority - the interrupt priority must be
\r
495 configKERNEL_INTERRUPT_PRIORITY no matter which peripheral is used to
\r
496 generate the tick interrupt. */
\r
497 INTC.IPR19.BIT._EDMAC = portKERNEL_INTERRUPT_PRIORITY;
\r
498 EDMAC.EESIPR.LONG = emacTX_END_INTERRUPT | emacRX_END_INTERRUPT; /* Enable Rx and Tx end interrupts. */
\r
500 /* Clear the interrupt flag. */
\r
501 CMT0.CMCSR.BIT.CMF = 0;
\r
503 /*-----------------------------------------------------------*/
\r
505 void vEMAC_ISR_Handler( void )
\r
507 unsigned long ul = EDMAC.EESR.LONG;
\r
508 long lHigherPriorityTaskWoken = pdFALSE;
\r
509 extern SemaphoreHandle_t xEMACSemaphore;
\r
510 static long ulTxEndInts = 0;
\r
512 /* Has a Tx end occurred? */
\r
513 if( ul & emacTX_END_INTERRUPT )
\r
516 if( ulTxEndInts >= 2 )
\r
518 /* Only return the buffer to the pool once both Txes have completed. */
\r
519 prvReturnBuffer( ( void * ) xTxDescriptors[ 0 ].buf_p );
\r
522 EDMAC.EESR.LONG = emacTX_END_INTERRUPT;
\r
525 /* Has an Rx end occurred? */
\r
526 if( ul & emacRX_END_INTERRUPT )
\r
528 /* Make sure the Ethernet task is not blocked waiting for a packet. */
\r
529 xSemaphoreGiveFromISR( xEMACSemaphore, &lHigherPriorityTaskWoken );
\r
530 portYIELD_FROM_ISR( lHigherPriorityTaskWoken );
\r
531 EDMAC.EESR.LONG = emacRX_END_INTERRUPT;
\r