xref: /FreeRTOS-Plus-TCP-v4.0.0/source/portable/NetworkInterface/M487/m480_eth.c (revision a4124602cc584fa0658448c229f48a459a84fbb1)
1 /**************************************************************************//**
2  * @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without modification,
5  * are permitted provided that the following conditions are met:
6  *   1. Redistributions of source code must retain the above copyright notice,
7  *      this list of conditions and the following disclaimer.
8  *   2. Redistributions in binary form must reproduce the above copyright notice,
9  *      this list of conditions and the following disclaimer in the documentation
10  *      and/or other materials provided with the distribution.
11  *   3. Neither the name of Nuvoton Technology Corp. nor the names of its contributors
12  *      may be used to endorse or promote products derived from this software
13  *      without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *****************************************************************************/
26 #include "FreeRTOS.h"
27 #include "list.h"
28 #include "FreeRTOS_IP.h"
29 
30 #include "m480_eth.h"
31 
32 #define ETH_TRIGGER_RX()    do { EMAC->RXST = 0; } while( 0 )
33 #define ETH_TRIGGER_TX()    do { EMAC->TXST = 0; } while( 0 )
34 #define ETH_ENABLE_TX()     do { EMAC->CTL |= EMAC_CTL_TXON; } while( 0 )
35 #define ETH_ENABLE_RX()     do { EMAC->CTL |= EMAC_CTL_RXON; } while( 0 )
36 #define ETH_DISABLE_TX()    do { EMAC->CTL &= ~EMAC_CTL_TXON; } while( 0 )
37 #define ETH_DISABLE_RX()    do { EMAC->CTL &= ~EMAC_CTL_RXON; } while( 0 )
38 
39 
40 struct eth_descriptor rx_desc[ RX_DESCRIPTOR_NUM ] __attribute__( ( aligned( 4 ) ) );
41 struct eth_descriptor tx_desc[ TX_DESCRIPTOR_NUM ] __attribute__( ( aligned( 4 ) ) );
42 #ifdef __ICCARM__
43     #pragma data_alignment=4
44     struct eth_descriptor rx_desc[ RX_DESCRIPTOR_NUM ];
45     struct eth_descriptor tx_desc[ TX_DESCRIPTOR_NUM ];
46     uint8_t rx_buf[ RX_DESCRIPTOR_NUM ][ PACKET_BUFFER_SIZE ];
47     uint8_t tx_buf[ TX_DESCRIPTOR_NUM ][ PACKET_BUFFER_SIZE ];
48 #else
49     struct eth_descriptor rx_desc[ RX_DESCRIPTOR_NUM ] __attribute__( ( aligned( 4 ) ) );
50     struct eth_descriptor tx_desc[ TX_DESCRIPTOR_NUM ] __attribute__( ( aligned( 4 ) ) );
51     uint8_t rx_buf[ RX_DESCRIPTOR_NUM ][ PACKET_BUFFER_SIZE ]  __attribute__( ( aligned( 4 ) ) );
52     uint8_t tx_buf[ TX_DESCRIPTOR_NUM ][ PACKET_BUFFER_SIZE ]  __attribute__( ( aligned( 4 ) ) );
53 #endif
54 struct eth_descriptor volatile * cur_tx_desc_ptr, * cur_rx_desc_ptr, * fin_tx_desc_ptr;
55 
56 
57 /* PTP source clock is 84MHz (Real chip using PLL). Each tick is 11.90ns */
58 /* Assume we want to set each tick to 100ns. */
59 /* Increase register = (100 * 2^31) / (10^9) = 214.71 =~ 215 = 0xD7 */
60 /* Addend register = 2^32 * tick_freq / (84MHz), where tick_freq = (2^31 / 215) MHz */
61 /* From above equation, addend register = 2^63 / (84M * 215) ~= 510707200 = 0x1E70C600 */
62 
63 
64 
mdio_write(uint8_t addr,uint8_t reg,uint16_t val)65 static void mdio_write( uint8_t addr,
66                         uint8_t reg,
67                         uint16_t val )
68 {
69     EMAC->MIIMDAT = val;
70     EMAC->MIIMCTL = ( addr << EMAC_MIIMCTL_PHYADDR_Pos ) | reg | EMAC_MIIMCTL_BUSY_Msk | EMAC_MIIMCTL_WRITE_Msk | EMAC_MIIMCTL_MDCON_Msk;
71 
72     while( EMAC->MIIMCTL & EMAC_MIIMCTL_BUSY_Msk )
73     {
74     }
75 }
76 
77 
mdio_read(uint8_t addr,uint8_t reg)78 static uint16_t mdio_read( uint8_t addr,
79                            uint8_t reg )
80 {
81     EMAC->MIIMCTL = ( addr << EMAC_MIIMCTL_PHYADDR_Pos ) | reg | EMAC_MIIMCTL_BUSY_Msk | EMAC_MIIMCTL_MDCON_Msk;
82 
83     while( EMAC->MIIMCTL & EMAC_MIIMCTL_BUSY_Msk )
84     {
85     }
86 
87     return( EMAC->MIIMDAT );
88 }
89 
reset_phy(void)90 static int reset_phy( void )
91 {
92     uint16_t reg;
93     uint32_t delayCnt;
94 
95 
96     mdio_write( CONFIG_PHY_ADDR, MII_BMCR, BMCR_RESET );
97 
98     delayCnt = 2000;
99 
100     while( delayCnt-- > 0 )
101     {
102         if( ( mdio_read( CONFIG_PHY_ADDR, MII_BMCR ) & BMCR_RESET ) == 0 )
103         {
104             break;
105         }
106     }
107 
108     if( delayCnt == 0 )
109     {
110         NU_DEBUGF( ( "Reset phy failed\n" ) );
111         return( -1 );
112     }
113 
114     mdio_write( CONFIG_PHY_ADDR, MII_ADVERTISE, ADVERTISE_CSMA |
115                 ADVERTISE_10HALF |
116                 ADVERTISE_10FULL |
117                 ADVERTISE_100HALF |
118                 ADVERTISE_100FULL );
119 
120     reg = mdio_read( CONFIG_PHY_ADDR, MII_BMCR );
121     mdio_write( CONFIG_PHY_ADDR, MII_BMCR, reg | BMCR_ANRESTART );
122 
123     delayCnt = 200000;
124 
125     while( delayCnt-- > 0 )
126     {
127         if( ( mdio_read( CONFIG_PHY_ADDR, MII_BMSR ) & ( BMSR_ANEGCOMPLETE | BMSR_LSTATUS ) )
128             == ( BMSR_ANEGCOMPLETE | BMSR_LSTATUS ) )
129         {
130             break;
131         }
132     }
133 
134     if( delayCnt == 0 )
135     {
136         NU_DEBUGF( ( "AN failed. Set to 100 FULL\n" ) );
137         EMAC->CTL |= ( EMAC_CTL_OPMODE_Msk | EMAC_CTL_FUDUP_Msk );
138         return( -1 );
139     }
140     else
141     {
142         reg = mdio_read( CONFIG_PHY_ADDR, MII_LPA );
143 
144         if( reg & ADVERTISE_100FULL )
145         {
146             NU_DEBUGF( ( "100 full\n" ) );
147             EMAC->CTL |= ( EMAC_CTL_OPMODE_Msk | EMAC_CTL_FUDUP_Msk );
148         }
149         else if( reg & ADVERTISE_100HALF )
150         {
151             NU_DEBUGF( ( "100 half\n" ) );
152             EMAC->CTL = ( EMAC->CTL & ~EMAC_CTL_FUDUP_Msk ) | EMAC_CTL_OPMODE_Msk;
153         }
154         else if( reg & ADVERTISE_10FULL )
155         {
156             NU_DEBUGF( ( "10 full\n" ) );
157             EMAC->CTL = ( EMAC->CTL & ~EMAC_CTL_OPMODE_Msk ) | EMAC_CTL_FUDUP_Msk;
158         }
159         else
160         {
161             NU_DEBUGF( ( "10 half\n" ) );
162             EMAC->CTL &= ~( EMAC_CTL_OPMODE_Msk | EMAC_CTL_FUDUP_Msk );
163         }
164     }
165 
166     FreeRTOS_printf( ( "PHY ID 1:0x%x\r\n", mdio_read( CONFIG_PHY_ADDR, MII_PHYSID1 ) ) );
167     FreeRTOS_printf( ( "PHY ID 2:0x%x\r\n", mdio_read( CONFIG_PHY_ADDR, MII_PHYSID2 ) ) );
168 
169     return( 0 );
170 }
171 
172 
init_tx_desc(void)173 static void init_tx_desc( void )
174 {
175     uint32_t i;
176 
177 
178     cur_tx_desc_ptr = fin_tx_desc_ptr = &tx_desc[ 0 ];
179 
180     for( i = 0; i < TX_DESCRIPTOR_NUM; i++ )
181     {
182         tx_desc[ i ].status1 = TXFD_PADEN | TXFD_CRCAPP | TXFD_INTEN;
183         tx_desc[ i ].buf = &tx_buf[ i ][ 0 ];
184         tx_desc[ i ].status2 = 0;
185         tx_desc[ i ].next = &tx_desc[ ( i + 1 ) % TX_DESCRIPTOR_NUM ];
186     }
187 
188     EMAC->TXDSA = ( unsigned int ) &tx_desc[ 0 ];
189 }
190 
init_rx_desc(void)191 static void init_rx_desc( void )
192 {
193     uint32_t i;
194 
195 
196     cur_rx_desc_ptr = &rx_desc[ 0 ];
197 
198     for( i = 0; i < RX_DESCRIPTOR_NUM; i++ )
199     {
200         rx_desc[ i ].status1 = OWNERSHIP_EMAC;
201         rx_desc[ i ].buf = &rx_buf[ i ][ 0 ];
202         rx_desc[ i ].status2 = 0;
203         rx_desc[ i ].next = &rx_desc[ ( i + 1 ) % TX_DESCRIPTOR_NUM ];
204     }
205 
206     EMAC->RXDSA = ( unsigned int ) &rx_desc[ 0 ];
207 }
208 
numaker_set_mac_addr(uint8_t * addr)209 void numaker_set_mac_addr( uint8_t * addr )
210 {
211     EMAC->CAM0M = ( addr[ 0 ] << 24 ) |
212                   ( addr[ 1 ] << 16 ) |
213                   ( addr[ 2 ] << 8 ) |
214                   addr[ 3 ];
215 
216     EMAC->CAM0L = ( addr[ 4 ] << 24 ) |
217                   ( addr[ 5 ] << 16 );
218 }
219 
__eth_clk_pin_init()220 static void __eth_clk_pin_init()
221 {
222     /* Unlock protected registers */
223     SYS_UnlockReg();
224 
225     /* Enable IP clock */
226     CLK_EnableModuleClock( EMAC_MODULE );
227 
228     /* Configure MDC clock rate to HCLK / (127 + 1) = 1.25 MHz if system is running at 160 MH */
229     CLK_SetModuleClock( EMAC_MODULE, 0, CLK_CLKDIV3_EMAC( 127 ) );
230 
231     /* Update System Core Clock */
232     SystemCoreClockUpdate();
233 
234     /*---------------------------------------------------------------------------------------------------------*/
235     /* Init I/O Multi-function                                                                                 */
236     /*---------------------------------------------------------------------------------------------------------*/
237     /* Configure RMII pins */
238     SYS->GPA_MFPL &= ~( SYS_GPA_MFPL_PA6MFP_Msk | SYS_GPA_MFPL_PA7MFP_Msk );
239     SYS->GPA_MFPL |= SYS_GPA_MFPL_PA6MFP_EMAC_RMII_RXERR | SYS_GPA_MFPL_PA7MFP_EMAC_RMII_CRSDV;
240     SYS->GPC_MFPL &= ~( SYS_GPC_MFPL_PC6MFP_Msk | SYS_GPC_MFPL_PC7MFP_Msk );
241     SYS->GPC_MFPL |= SYS_GPC_MFPL_PC6MFP_EMAC_RMII_RXD1 | SYS_GPC_MFPL_PC7MFP_EMAC_RMII_RXD0;
242     SYS->GPC_MFPH &= ~SYS_GPC_MFPH_PC8MFP_Msk;
243     SYS->GPC_MFPH |= SYS_GPC_MFPH_PC8MFP_EMAC_RMII_REFCLK;
244     SYS->GPE_MFPH &= ~( SYS_GPE_MFPH_PE8MFP_Msk | SYS_GPE_MFPH_PE9MFP_Msk | SYS_GPE_MFPH_PE10MFP_Msk |
245                         SYS_GPE_MFPH_PE11MFP_Msk | SYS_GPE_MFPH_PE12MFP_Msk );
246     SYS->GPE_MFPH |= SYS_GPE_MFPH_PE8MFP_EMAC_RMII_MDC |
247                      SYS_GPE_MFPH_PE9MFP_EMAC_RMII_MDIO |
248                      SYS_GPE_MFPH_PE10MFP_EMAC_RMII_TXD0 |
249                      SYS_GPE_MFPH_PE11MFP_EMAC_RMII_TXD1 |
250                      SYS_GPE_MFPH_PE12MFP_EMAC_RMII_TXEN;
251 
252     /* Enable high slew rate on all RMII TX output pins */
253     PE->SLEWCTL = ( GPIO_SLEWCTL_HIGH << GPIO_SLEWCTL_HSREN10_Pos ) |
254                   ( GPIO_SLEWCTL_HIGH << GPIO_SLEWCTL_HSREN11_Pos ) |
255                   ( GPIO_SLEWCTL_HIGH << GPIO_SLEWCTL_HSREN12_Pos );
256 
257 
258     /* Lock protected registers */
259     SYS_LockReg();
260 }
261 
numaker_eth_init(uint8_t * mac_addr)262 int numaker_eth_init( uint8_t * mac_addr )
263 {
264     int ret = 0;
265 
266     /* init CLK & pins */
267     __eth_clk_pin_init();
268 
269     /* Reset MAC */
270     EMAC->CTL = EMAC_CTL_RST_Msk;
271 
272     while( EMAC->CTL & EMAC_CTL_RST_Msk )
273     {
274     }
275 
276     init_tx_desc();
277     init_rx_desc();
278 
279     numaker_set_mac_addr( mac_addr ); /* need to reconfigure hardware address because we just RESET EMAC... */
280 
281 
282     /* Configure the MAC interrupt enable register. */
283     EMAC->INTEN = EMAC_INTEN_RXIEN_Msk |
284                   EMAC_INTEN_TXIEN_Msk |
285                   EMAC_INTEN_RXGDIEN_Msk |
286                   EMAC_INTEN_TXCPIEN_Msk |
287                   EMAC_INTEN_RXBEIEN_Msk |
288                   EMAC_INTEN_TXBEIEN_Msk |
289                   EMAC_INTEN_RDUIEN_Msk |
290                   EMAC_INTEN_TSALMIEN_Msk |
291                   EMAC_INTEN_WOLIEN_Msk;
292 
293     /* Configure the MAC control register. */
294     EMAC->CTL = EMAC_CTL_STRIPCRC_Msk | EMAC_CTL_RMIIEN_Msk;
295 
296     /* Accept packets for us and all broadcast and multicast packets */
297     EMAC->CAMCTL = EMAC_CAMCTL_CMPEN_Msk |
298                    EMAC_CAMCTL_AMP_Msk |
299                    EMAC_CAMCTL_ABP_Msk;
300     EMAC->CAMEN = 1; /* Enable CAM entry 0 */
301 
302     ret = reset_phy();
303 
304     EMAC_ENABLE_RX();
305     EMAC_ENABLE_TX();
306     return ret;
307 }
308 
309 
310 
ETH_halt(void)311 void ETH_halt( void )
312 {
313     EMAC->CTL &= ~( EMAC_CTL_RXON_Msk | EMAC_CTL_TXON_Msk );
314 }
315 
316 unsigned int m_status;
317 
EMAC_RX_IRQHandler(void)318 void EMAC_RX_IRQHandler( void )
319 {
320 /*    NU_DEBUGF(("%s ... \r\n", __FUNCTION__)); */
321     m_status = EMAC->INTSTS & 0xFFFF;
322     EMAC->INTSTS = m_status;
323 
324     if( m_status & EMAC_INTSTS_RXBEIF_Msk )
325     {
326         /* Shouldn't goes here, unless descriptor corrupted */
327         NU_DEBUGF( ( "RX descriptor corrupted \r\n" ) );
328         /*return; */
329     }
330 
331     /* FIX ME: for rx-event, to ack rx_isr into event queue */
332     xNetworkCallback( 'R' );
333 }
334 
335 
numaker_eth_trigger_rx(void)336 void numaker_eth_trigger_rx( void )
337 {
338     ETH_TRIGGER_RX();
339 }
340 
numaker_eth_get_rx_buf(uint16_t * len,uint8_t ** buf)341 int numaker_eth_get_rx_buf( uint16_t * len,
342                             uint8_t ** buf )
343 {
344     unsigned int cur_entry, status;
345 
346     cur_entry = EMAC->CRXDSA;
347 
348     if( ( cur_entry == ( uint32_t ) cur_rx_desc_ptr ) && ( !( m_status & EMAC_INTSTS_RDUIF_Msk ) ) ) /* cur_entry may equal to cur_rx_desc_ptr if RDU occurred */
349     {
350         return -1;
351     }
352 
353     status = cur_rx_desc_ptr->status1;
354 
355     if( status & OWNERSHIP_EMAC )
356     {
357         return -1;
358     }
359 
360     if( status & RXFD_RXGD )
361     {
362         *buf = cur_rx_desc_ptr->buf;
363         *len = status & 0xFFFF;
364     }
365 
366     return 0;
367 }
368 
numaker_eth_rx_next(void)369 void numaker_eth_rx_next( void )
370 {
371     cur_rx_desc_ptr->status1 = OWNERSHIP_EMAC;
372     cur_rx_desc_ptr = cur_rx_desc_ptr->next;
373 }
374 
EMAC_TX_IRQHandler(void)375 void EMAC_TX_IRQHandler( void )
376 {
377     unsigned int cur_entry, status;
378 
379     status = EMAC->INTSTS & 0xFFFF0000;
380     EMAC->INTSTS = status;
381 
382     if( status & EMAC_INTSTS_TXBEIF_Msk )
383     {
384         /* Shouldn't goes here, unless descriptor corrupted */
385         return;
386     }
387 
388     cur_entry = EMAC->CTXDSA;
389 
390     while( cur_entry != ( uint32_t ) fin_tx_desc_ptr )
391     {
392         fin_tx_desc_ptr = fin_tx_desc_ptr->next;
393     }
394 
395     /* FIX ME: for tx-event, no-op at this stage */
396     xNetworkCallback( 'T' );
397 }
398 
numaker_eth_get_tx_buf(void)399 uint8_t * numaker_eth_get_tx_buf( void )
400 {
401     if( cur_tx_desc_ptr->status1 & OWNERSHIP_EMAC )
402     {
403         return( NULL );
404     }
405     else
406     {
407         return( cur_tx_desc_ptr->buf );
408     }
409 }
410 
numaker_eth_trigger_tx(uint16_t length,void * p)411 void numaker_eth_trigger_tx( uint16_t length,
412                              void * p )
413 {
414     struct eth_descriptor volatile * desc;
415 
416     cur_tx_desc_ptr->status2 = ( unsigned int ) length;
417     desc = cur_tx_desc_ptr->next; /* in case TX is transmitting and overwrite next pointer before we can update cur_tx_desc_ptr */
418     cur_tx_desc_ptr->status1 |= OWNERSHIP_EMAC;
419     cur_tx_desc_ptr = desc;
420 
421     ETH_TRIGGER_TX();
422 }
423 
numaker_eth_link_ok(void)424 int numaker_eth_link_ok( void )
425 {
426     /* first, a dummy read to latch */
427     mdio_read( CONFIG_PHY_ADDR, MII_BMSR );
428 
429     if( mdio_read( CONFIG_PHY_ADDR, MII_BMSR ) & BMSR_LSTATUS )
430     {
431         return 1;
432     }
433 
434     return 0;
435 }
436 
437 /*void numaker_eth_set_cb(eth_callback_t eth_cb, void *userData) */
438 /*{ */
439 /*    nu_eth_txrx_cb =  eth_cb; */
440 /*    nu_userData = userData; */
441 /*} */
442 
443 /* Provide ethernet devices with a semi-unique MAC address */
numaker_mac_address(uint8_t * mac)444 void numaker_mac_address( uint8_t * mac )
445 {
446     uint32_t uID1;
447     /* Fetch word 0 */
448     uint32_t word0 = *( uint32_t * ) 0x7F804; /* 2KB Data Flash at 0x7F800 */
449     /* Fetch word 1 */
450     /* we only want bottom 16 bits of word1 (MAC bits 32-47) */
451     /* and bit 9 forced to 1, bit 8 forced to 0 */
452     /* Locally administered MAC, reduced conflicts */
453     /* http://en.wikipedia.org/wiki/MAC_address */
454     uint32_t word1 = *( uint32_t * ) 0x7F800; /* 2KB Data Flash at 0x7F800 */
455 
456     if( word0 == 0xFFFFFFFF )                 /* Not burn any mac address at 1st 2 words of Data Flash */
457     {
458         /* with a semi-unique MAC address from the UUID */
459         /* Enable FMC ISP function */
460         SYS_UnlockReg();
461         FMC_Open();
462         /* = FMC_ReadUID(0); */
463         uID1 = FMC_ReadUID( 1 );
464         word1 = ( uID1 & 0x003FFFFF ) | ( ( uID1 & 0x030000 ) << 6 ) >> 8;
465         word0 = ( ( FMC_ReadUID( 0 ) >> 4 ) << 20 ) | ( ( uID1 & 0xFF ) << 12 ) | ( FMC_ReadUID( 2 ) & 0xFFF );
466         /* Disable FMC ISP function */
467         FMC_Close();
468         /* Lock protected registers */
469         SYS_LockReg();
470     }
471 
472     word1 |= 0x00000200;
473     word1 &= 0x0000FEFF;
474 
475     mac[ 0 ] = ( word1 & 0x0000ff00 ) >> 8;
476     mac[ 1 ] = ( word1 & 0x000000ff );
477     mac[ 2 ] = ( word0 & 0xff000000 ) >> 24;
478     mac[ 3 ] = ( word0 & 0x00ff0000 ) >> 16;
479     mac[ 4 ] = ( word0 & 0x0000ff00 ) >> 8;
480     mac[ 5 ] = ( word0 & 0x000000ff );
481 
482     NU_DEBUGF( ( "mac address %02x-%02x-%02x-%02x-%02x-%02x \r\n", mac[ 0 ], mac[ 1 ], mac[ 2 ], mac[ 3 ], mac[ 4 ], mac[ 5 ] ) );
483 }
484 
numaker_eth_enable_interrupts(void)485 void numaker_eth_enable_interrupts( void )
486 {
487     EMAC->INTEN |= EMAC_INTEN_RXIEN_Msk |
488                    EMAC_INTEN_TXIEN_Msk;
489     NVIC_EnableIRQ( EMAC_RX_IRQn );
490     NVIC_EnableIRQ( EMAC_TX_IRQn );
491 }
492 
numaker_eth_disable_interrupts(void)493 void numaker_eth_disable_interrupts( void )
494 {
495     NVIC_DisableIRQ( EMAC_RX_IRQn );
496     NVIC_DisableIRQ( EMAC_TX_IRQn );
497 }
498