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