1 /**********************************************************************
\r
2 * Copyright(C) 2011, NXP Semiconductor
\r
3 * All rights reserved.
\r
4 * Heavily modified by Real Time Engineers ltd.
\r
5 ***********************************************************************
\r
6 * Software that is described herein is for illustrative purposes only
\r
7 * which provides customers with programming information regarding the
\r
8 * products. This software is supplied "AS IS" without any warranties.
\r
9 * NXP Semiconductors assumes no responsibility or liability for the
\r
10 * use of the software, conveys no license or title under any patent,
\r
11 * copyright, or mask work right to the product. NXP Semiconductors
\r
12 * reserves the right to make changes in the software without
\r
13 * notification. NXP Semiconductors also make no representation or
\r
14 * warranty that such application will be suitable for the specified
\r
15 * use without further testing or modification.
\r
16 **********************************************************************/
\r
18 /* FreeRTOS includes. */
\r
19 #include "FreeRTOS.h"
\r
22 /* FreeRTOS+UDP includes. */
\r
23 #include "FreeRTOS_UDP_IP.h"
\r
24 #include "FreeRTOS_IP_Private.h"
\r
25 #include "NetworkBufferManagement.h"
\r
27 /* Library includes. */
\r
28 #include "lpc18xx_emac.h"
\r
29 #include "lpc18xx_rgu.h"
\r
30 #include "lpc18xx_scu.h"
\r
31 #include "lpc18xx_gpio.h"
\r
34 #define emacTIMEOUT_DELAY ( 2 )
\r
35 #define emacNEGOTIATE_DELAY ( 10 / portTICK_RATE_MS )
\r
37 #define emacEXPECTED_RX_STATUS_MASK ( RX_FIRST_SEGM | RX_LAST_SEGM )
\r
39 /* Rx descriptors and data array. */
\r
40 static volatile RX_Desc Rx_Desc[ configNUM_RX_ETHERNET_DMA_DESCRIPTORS ];
\r
41 static unsigned int RxDescIndex = 0;
\r
43 /** Rx Status data array - Must be 8-Byte aligned */
\r
44 #if defined ( __CC_ARM )
\r
45 static __align(8) RX_Stat Rx_Stat[ configNUM_RX_ETHERNET_DMA_DESCRIPTORS ];
\r
46 #elif defined ( __ICCARM__ )
\r
47 #pragma data_alignment=8
\r
48 static RX_Stat Rx_Stat[ configNUM_RX_ETHERNET_DMA_DESCRIPTORS ];
\r
49 #elif defined ( __GNUC__ )
\r
50 static volatile __attribute__ ((aligned (8))) RX_Stat Rx_Stat[ configNUM_RX_ETHERNET_DMA_DESCRIPTORS ];
\r
53 /* Tx descriptors and status array. */
\r
54 static volatile TX_Desc Tx_Desc[ configNUM_TX_ETHERNET_DMA_DESCRIPTORS ];
\r
55 static volatile TX_Stat Tx_Stat[ configNUM_TX_ETHERNET_DMA_DESCRIPTORS ];
\r
56 static unsigned int TxDescIndex = 0;
\r
58 /* Private Functions ---------------------------------------------------------- */
\r
59 static void rx_descr_init( void );
\r
60 static void tx_descr_init( void );
\r
61 static int32_t write_PHY( uint32_t PhyReg, uint16_t Value );
\r
62 static int32_t read_PHY( uint32_t PhyReg );
\r
63 static void setEmacAddr( uint8_t abStationAddr[] );
\r
65 /*********************************************************************//**
\r
66 * @brief Initializes the EMAC peripheral according to the specified
\r
67 * parameters in the EMAC_ConfigStruct.
\r
68 * @param[in] EMAC_ConfigStruct Pointer to a EMAC_CFG_Type structure
\r
69 * that contains the configuration information for the
\r
70 * specified EMAC peripheral.
\r
73 * Note: This function will initialize EMAC module according to procedure below:
\r
74 * - Remove the soft reset condition from the MAC
\r
75 * - Configure the PHY via the MIIM interface of the MAC
\r
76 * - Select RMII mode
\r
77 * - Configure the transmit and receive DMA engines, including the descriptor arrays
\r
78 * - Configure the host registers (MAC1,MAC2 etc.) in the MAC
\r
79 * - Enable the receive and transmit data paths
\r
80 * In default state after initializing, only Rx Done and Tx Done interrupt are enabled,
\r
81 * all remain interrupts are disabled
\r
82 * (Ref. from LPC17xx UM)
\r
83 **********************************************************************/
\r
84 portBASE_TYPE EMAC_Init(EMAC_CFG_Type *EMAC_ConfigStruct)
\r
86 int32_t id1, id2, regv, phy = 0;
\r
87 int32_t phy_linkstatus_reg, phy_linkstatus_mask;
\r
89 const uint32_t ulMaxAttempts = 250UL;
\r
90 portBASE_TYPE xReturn = pdPASS;
\r
92 /* Enable Ethernet Pins (NGX LPC1830 Xplorer. */
\r
93 scu_pinmux(0x2 ,0 , (MD_EHS | MD_PLN | MD_EZI | MD_ZI), FUNC7);
\r
94 scu_pinmux(0x1 ,17 , (MD_EHS | MD_PLN | MD_EZI | MD_ZI), FUNC3);
\r
95 scu_pinmux(0x1 ,18 , (MD_EHS | MD_PLN | MD_EZI | MD_ZI), FUNC3);
\r
96 scu_pinmux(0x1 ,20 , (MD_EHS | MD_PLN | MD_EZI | MD_ZI), FUNC3);
\r
97 scu_pinmux(0x1 ,19 , (MD_EHS | MD_PLN | MD_EZI | MD_ZI), FUNC0);
\r
98 scu_pinmux(0x0 ,1 , (MD_EHS | MD_PLN | MD_EZI | MD_ZI), FUNC6);
\r
99 scu_pinmux(0x1 ,15 , (MD_EHS | MD_PLN | MD_EZI | MD_ZI), FUNC3);
\r
100 scu_pinmux(0x0 ,0 , (MD_EHS | MD_PLN | MD_EZI | MD_ZI), FUNC2);
\r
101 scu_pinmux(0x1 ,16 , (MD_EHS | MD_PLN | MD_EZI | MD_ZI), FUNC3);
\r
102 scu_pinmux(0xC ,9 , (MD_EHS | MD_PLN | MD_EZI | MD_ZI), FUNC3);
\r
103 scu_pinmux(0x1 ,16 , (MD_EHS | MD_PLN | MD_EZI | MD_ZI), FUNC7);
\r
105 /* Ethernet RESET Pins */
\r
106 scu_pinmux(0x1 ,0 , MD_PUP, FUNC0);
\r
107 GPIO_SetDir(0,(1<<4), 1);
\r
108 GPIO_SetValue(0,(1<<4));
\r
111 #if MII /* Select MII interface */ // check MUXING for new Eagle...
\r
112 scu_pinmux(0xC ,6 , (MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_RXD2: PC_6 -> FUNC3
\r
113 scu_pinmux(0xC ,7 , (MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_RXD3: PC_7 -> FUNC3
\r
114 scu_pinmux(0xC ,0 , (MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_RXLK: PC_0 -> FUNC3
\r
115 scu_pinmux(0xC ,2 , (MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_TXD2: PC_2 -> FUNC3
\r
116 scu_pinmux(0xC ,3 , (MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_TXD3: PC_3 -> FUNC3
\r
117 scu_pinmux(0xC ,5 , (MD_PLN | MD_EZI | MD_ZI), FUNC3); // ENET_TX_ER: PC_5 -> FUNC3
\r
118 scu_pinmux(0x0 ,1 , (MD_PLN | MD_EZI | MD_ZI), FUNC2); // ENET_COL: P0_1 -> FUNC2
\r
119 #else /* Select RMII interface */
\r
120 LPC_CREG->CREG6 |= RMII_SELECT;
\r
124 RGU_SoftReset( RGU_SIG_ETHERNET );
\r
126 /* Wait for reset. */
\r
127 while( !( LPC_RGU->RESET_ACTIVE_STATUS0 & ( 1 << ETHERNET_RST ) ) )
\r
129 vTaskDelay( emacTIMEOUT_DELAY );
\r
132 /* Reset all GMAC Subsystem internal registers and logic. */
\r
133 LPC_ETHERNET->DMA_BUS_MODE |= DMA_SOFT_RESET;
\r
135 /* Wait for software reset completion. */
\r
136 while( LPC_ETHERNET->DMA_BUS_MODE & DMA_SOFT_RESET )
\r
138 vTaskDelay( emacTIMEOUT_DELAY );
\r
141 /* Put the PHY in reset mode */
\r
142 write_PHY( PHY_REG_BMCR, PHY_BMCR_RESET );
\r
144 /* Wait for hardware reset to end. */
\r
145 for( x = 0; x < ulMaxAttempts; x++ )
\r
147 regv = read_PHY (PHY_REG_BMCR);
\r
148 if( !( regv & PHY_BMCR_RESET ) )
\r
150 /* Reset complete */
\r
155 vTaskDelay( emacTIMEOUT_DELAY );
\r
159 if( x == ulMaxAttempts )
\r
164 /* Check if this is a DP83848C PHY. */
\r
165 id1 = read_PHY( PHY_REG_IDR1 );
\r
166 id2 = read_PHY( PHY_REG_IDR2 );
\r
167 if( ( ( id1 << 16 ) | ( id2 & 0xFFF0 ) ) == DP83848C_ID )
\r
171 else if( ( ( id1 << 16 ) | id2 ) == LAN8720_ID )
\r
178 /* Use autonegotiation about the link speed. */
\r
179 write_PHY( PHY_REG_BMCR, PHY_AUTO_NEG );
\r
181 /* Wait to complete Auto_Negotiation. */
\r
182 for( x = 0; x < ulMaxAttempts; x++ )
\r
184 regv = read_PHY( PHY_REG_BMSR );
\r
186 if( ( regv & PHY_AUTO_NEG_DONE ) != 0 )
\r
188 /* Auto negotiation Complete. */
\r
193 vTaskDelay( emacNEGOTIATE_DELAY );
\r
197 if( x == ulMaxAttempts )
\r
208 if( xReturn == pdPASS )
\r
210 /* Default to DP83848C. */
\r
211 phy_linkstatus_reg = PHY_REG_STS;
\r
212 phy_linkstatus_mask = 0x0001;
\r
214 if( phy == LAN8720_ID )
\r
216 phy_linkstatus_reg = PHY_REG_BMSR;
\r
217 phy_linkstatus_mask = 0x0004;
\r
220 /* Check the link status. */
\r
221 for( x = 0; x < ulMaxAttempts; x++ )
\r
223 regv = read_PHY( phy_linkstatus_reg );
\r
225 if( ( regv & phy_linkstatus_mask ) != 0 )
\r
232 vTaskDelay( emacNEGOTIATE_DELAY );
\r
236 if( x == ulMaxAttempts )
\r
241 regv = read_PHY( PHY_REG_SPCON );
\r
242 regv &= PHY_REG_HCDSPEED_MASK;
\r
244 /* Configure 100MBit/10MBit mode and Full/Half Duplex mode. */
\r
247 case PHY_REG_HCDSPEED_10MB_FULLD:
\r
248 LPC_ETHERNET->MAC_CONFIG |= MAC_DUPMODE;
\r
251 case PHY_REG_HCDSPEED_100MB_HALFD:
\r
252 LPC_ETHERNET->MAC_CONFIG |= MAC_100MPS;
\r
255 case PHY_REG_HCDSPEED_100MB_FULLD:
\r
256 LPC_ETHERNET->MAC_CONFIG |= MAC_DUPMODE;
\r
257 LPC_ETHERNET->MAC_CONFIG |= MAC_100MPS;
\r
264 /* Set the Ethernet MAC Address registers */
\r
265 setEmacAddr( EMAC_ConfigStruct->pbEMAC_Addr );
\r
267 /* Initialize Descriptor Lists */
\r
271 /* Configure Filter
\r
272 LPC_ETHERNET->MAC_FRAME_FILTER is left at its default value.
\r
273 MAC_PROMISCUOUS and MAC_RECEIVEALL can be set if required. */
\r
275 /* Enable Receiver and Transmitter */
\r
276 LPC_ETHERNET->MAC_CONFIG |= (MAC_TX_ENABLE | MAC_RX_ENABLE);
\r
278 /* Enable interrupts */
\r
279 LPC_ETHERNET->DMA_INT_EN = DMA_INT_NOR_SUM | DMA_INT_RECEIVE ;
\r
281 /* Start Transmission & Receive processes */
\r
282 LPC_ETHERNET->DMA_OP_MODE |= (DMA_SS_TRANSMIT | DMA_SS_RECEIVE );
\r
288 /*********************************************************************//**
\r
289 **********************************************************************/
\r
290 portBASE_TYPE EMAC_CheckTransmitIndex( void )
\r
292 portBASE_TYPE xReturn;
\r
294 if( ( Tx_Desc[ TxDescIndex ].Status & OWN_BIT ) == 0 )
\r
306 /*********************************************************************//**
\r
307 * @brief EMAC_SetNextPacketToSend
\r
308 * @param[in] pucBuffer
\r
310 ***********************************************************************/
\r
311 void EMAC_SetNextPacketToSend( uint8_t * pucBuffer )
\r
313 /* The old packet is now finished with and can be freed. */
\r
314 vEthernetBufferRelease( ( void * ) Tx_Desc[ TxDescIndex ].Packet );
\r
316 /* Assign the new packet to the descriptor. */
\r
317 Tx_Desc[ TxDescIndex ].Packet = ( uint32_t ) pucBuffer;
\r
320 void EMAC_StartTransmitNextBuffer( uint32_t ulLength )
\r
322 Tx_Desc[ TxDescIndex ].Ctrl = ulLength;
\r
323 Tx_Desc[ TxDescIndex ].Status |= OWN_BIT;
\r
325 /* Wake Up the DMA if it's in Suspended Mode. */
\r
326 LPC_ETHERNET->DMA_TRANS_POLL_DEMAND = 1;
\r
329 if( TxDescIndex == configNUM_TX_ETHERNET_DMA_DESCRIPTORS )
\r
335 /*********************************************************************//**
\r
336 * @brief Get size of current Received data in received buffer (due to
\r
339 * @return Size of received data
\r
340 **********************************************************************/
\r
341 uint32_t EMAC_GetReceiveDataSize(void)
\r
343 unsigned short RxLen = 0;
\r
345 RxLen = ( Rx_Desc[ RxDescIndex ].Status >> 16 ) & 0x03FFF;
\r
349 /*********************************************************************//**
\r
350 * @brief Increase the RxConsumeIndex (after reading the Receive buffer
\r
351 * to release the Receive buffer) and wrap-around the index if
\r
352 * it reaches the maximum Receive Number
\r
355 **********************************************************************/
\r
356 void EMAC_UpdateRxConsumeIndex( void )
\r
358 Rx_Desc[ RxDescIndex ].Status = OWN_BIT;
\r
361 if( RxDescIndex == configNUM_RX_ETHERNET_DMA_DESCRIPTORS )
\r
367 /*********************************************************************//**
\r
368 * @brief Check whether if the current RxConsumeIndex is not equal to the
\r
369 * current RxProduceIndex.
\r
371 * @return TRUE if they're not equal, otherwise return FALSE
\r
373 * Note: In case the RxConsumeIndex is not equal to the RxProduceIndex,
\r
374 * it means there're available data has been received. They should be read
\r
375 * out and released the Receive Data Buffer by updating the RxConsumeIndex value.
\r
376 **********************************************************************/
\r
377 portBASE_TYPE EMAC_CheckReceiveIndex(void)
\r
379 portBASE_TYPE xReturn;
\r
381 if( ( Rx_Desc[ RxDescIndex ].Status & OWN_BIT ) == 0 )
\r
393 void EMAC_NextPacketToRead( xNetworkBufferDescriptor_t *pxNetworkBuffer )
\r
397 /* Swap the buffer in the network buffer with the buffer used by the DMA.
\r
398 This allows the data to be passed out without having to perform any copies. */
\r
399 pucTemp = ( uint8_t * ) Rx_Desc[ RxDescIndex ].Packet;
\r
400 Rx_Desc[ RxDescIndex ].Packet = ( uint32_t ) pxNetworkBuffer->pucEthernetBuffer;
\r
401 pxNetworkBuffer->pucEthernetBuffer = pucTemp;
\r
403 /* Only supports frames coming in single buffers. If this frame is split
\r
404 across multiple buffers then reject it (and if the frame is needed increase
\r
405 the ipconfigNETWORK_MTU setting). */
\r
406 if( ( Rx_Desc[ RxDescIndex ].Status & emacEXPECTED_RX_STATUS_MASK ) != emacEXPECTED_RX_STATUS_MASK )
\r
408 pxNetworkBuffer->xDataLength = 0;
\r
412 pxNetworkBuffer->xDataLength = ( size_t ) EMAC_GetReceiveDataSize() - ( ipETHERNET_CRC_BYTES - 1U );;
\r
416 /*********************************************************************//**
\r
417 * @brief Initializes RX Descriptor
\r
420 ***********************************************************************/
\r
421 static void rx_descr_init( void )
\r
424 size_t xBufferSize = ipTOTAL_ETHERNET_FRAME_SIZE;
\r
426 for( x = 0; x < configNUM_RX_ETHERNET_DMA_DESCRIPTORS; x++ )
\r
428 /* Obtain the buffer first, as the size of the buffer might be changed
\r
429 within the pucEthernetBufferGet() call. */
\r
430 Rx_Desc[ x ].Packet = ( uint32_t ) pucEthernetBufferGet( &xBufferSize );
\r
431 Rx_Desc[ x ].Status = OWN_BIT;
\r
432 Rx_Desc[ x ].Ctrl = xBufferSize;
\r
433 Rx_Desc[ x ].NextDescripter = ( uint32_t ) &Rx_Desc[ x + 1 ];
\r
435 configASSERT( ( ( ( uint32_t ) Rx_Desc[x].Packet ) & 0x07 ) == 0 );
\r
438 /* Last Descriptor */
\r
439 Rx_Desc[ configNUM_RX_ETHERNET_DMA_DESCRIPTORS - 1 ].Ctrl |= RX_END_RING;
\r
443 /* Set Starting address of RX Descriptor list */
\r
444 LPC_ETHERNET->DMA_REC_DES_ADDR = ( uint32_t ) Rx_Desc;
\r
447 /*********************************************************************//**
\r
448 * @brief Initializes TX Descriptor
\r
451 ***********************************************************************/
\r
452 static void tx_descr_init( void )
\r
454 /* Initialize Transmit Descriptor and Status array. */
\r
457 for( x = 0; x < configNUM_TX_ETHERNET_DMA_DESCRIPTORS; x++ )
\r
459 Tx_Desc[ x ].Status = TX_LAST_SEGM | TX_FIRST_SEGM;
\r
460 Tx_Desc[ x ].Ctrl = 0;
\r
461 Tx_Desc[ x ].NextDescripter = ( uint32_t ) &Tx_Desc[ x + 1 ];
\r
463 /* Packet is assigned when a Tx is initiated. */
\r
464 Tx_Desc[ x ].Packet = ( uint32_t )NULL;
\r
467 /* Last Descriptor? */
\r
468 Tx_Desc[ configNUM_TX_ETHERNET_DMA_DESCRIPTORS-1 ].Status |= TX_END_RING;
\r
470 /* Set Starting address of TX Descriptor list */
\r
471 LPC_ETHERNET->DMA_TRANS_DES_ADDR = ( uint32_t ) Tx_Desc;
\r
475 /*********************************************************************//**
\r
476 * @brief Write value to PHY device
\r
477 * @param[in] PhyReg: PHY Register address
\r
478 * @param[in] Value: Value to write
\r
479 * @return 0 - if success
\r
481 ***********************************************************************/
\r
482 static int32_t write_PHY (uint32_t PhyReg, uint16_t Value)
\r
485 const uint32_t ulMaxAttempts = 250UL;
\r
486 int32_t lReturn = pdPASS;
\r
488 /* Write a data 'Value' to PHY register 'PhyReg'. */
\r
490 while( LPC_ETHERNET->MAC_MII_ADDR & GMII_BUSY )
\r
494 if( x >= ulMaxAttempts )
\r
502 /* GMII is busy. */
\r
503 vTaskDelay( emacTIMEOUT_DELAY );
\r
507 if( lReturn == pdPASS )
\r
509 LPC_ETHERNET->MAC_MII_ADDR = ( DP83848C_DEF_ADR << 11 ) | ( PhyReg << 6 ) | GMII_WRITE;
\r
510 LPC_ETHERNET->MAC_MII_DATA = Value;
\r
512 /* Start PHY Write Cycle. */
\r
513 LPC_ETHERNET->MAC_MII_ADDR |= GMII_BUSY;
\r
515 /* Wait untl operation completed. */
\r
516 for( x = 0; x < ulMaxAttempts; x++ )
\r
518 if( ( LPC_ETHERNET->MAC_MII_ADDR & GMII_BUSY ) == 0 )
\r
524 vTaskDelay( emacTIMEOUT_DELAY );
\r
528 if( x == ulMaxAttempts )
\r
538 /*********************************************************************//**
\r
539 * @brief Read value from PHY device
\r
540 * @param[in] PhyReg: PHY Register address
\r
541 * @return 0 - if success
\r
543 ***********************************************************************/
\r
544 static int32_t read_PHY( uint32_t PhyReg )
\r
546 int32_t lValue = 0;
\r
548 const uint32_t ulMaxAttempts = 250UL;
\r
550 /* Write a data 'Value' to PHY register 'PhyReg'. */
\r
552 while( LPC_ETHERNET->MAC_MII_ADDR & GMII_BUSY )
\r
556 if( x >= ulMaxAttempts )
\r
563 /* GMII is busy. */
\r
564 vTaskDelay( emacTIMEOUT_DELAY );
\r
568 if( x < ulMaxAttempts )
\r
570 /* Read a PHY register 'PhyReg'. */
\r
571 LPC_ETHERNET->MAC_MII_ADDR = ( DP83848C_DEF_ADR << 11 ) | ( PhyReg << 6 ) | GMII_READ;
\r
573 /* Start PHY Read Cycle. */
\r
574 LPC_ETHERNET->MAC_MII_ADDR |= GMII_BUSY;
\r
576 /* Wait until operation completed */
\r
577 for( x = 0; x < ulMaxAttempts; x++ )
\r
579 if( ( LPC_ETHERNET->MAC_MII_ADDR & GMII_BUSY ) == 0 )
\r
585 vTaskDelay( emacTIMEOUT_DELAY );
\r
589 configASSERT( x != ulMaxAttempts );
\r
590 lValue = LPC_ETHERNET->MAC_MII_DATA;
\r
596 /*********************************************************************//**
\r
597 * @brief Set Station MAC address for EMAC module
\r
598 * @param[in] abStationAddr Pointer to Station address that contains 6-bytes
\r
599 * of MAC address (should be in order from MAC Address 1 to MAC Address 6)
\r
601 **********************************************************************/
\r
602 static void setEmacAddr( uint8_t abStationAddr[] )
\r
604 /* Set the Ethernet MAC Address registers */
\r
605 LPC_ETHERNET->MAC_ADDR0_HIGH = (( uint32_t ) abStationAddr[ 5 ] << 8 ) | ( uint32_t )abStationAddr[ 4 ];
\r
606 LPC_ETHERNET->MAC_ADDR0_LOW = (( uint32_t )abStationAddr[ 3 ] << 24) | (( uint32_t )abStationAddr[ 2 ] << 16) | (( uint32_t )abStationAddr[ 1 ] << 8 ) | ( uint32_t )abStationAddr[ 0 ];
\r