1 /**************************************************************************//**
\r
2 * @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved.
\r
4 * Redistribution and use in source and binary forms, with or without modification,
\r
5 * are permitted provided that the following conditions are met:
\r
6 * 1. Redistributions of source code must retain the above copyright notice,
\r
7 * this list of conditions and the following disclaimer.
\r
8 * 2. Redistributions in binary form must reproduce the above copyright notice,
\r
9 * this list of conditions and the following disclaimer in the documentation
\r
10 * and/or other materials provided with the distribution.
\r
11 * 3. Neither the name of Nuvoton Technology Corp. nor the names of its contributors
\r
12 * may be used to endorse or promote products derived from this software
\r
13 * without specific prior written permission.
\r
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
\r
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
\r
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
\r
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
\r
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
\r
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
\r
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
\r
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
\r
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
\r
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\r
25 *****************************************************************************/
\r
26 #include "FreeRTOS.h"
\r
28 #include "FreeRTOS_IP.h"
\r
30 #include "m480_eth.h"
\r
32 #define ETH_TRIGGER_RX() do{EMAC->RXST = 0;}while(0)
\r
33 #define ETH_TRIGGER_TX() do{EMAC->TXST = 0;}while(0)
\r
34 #define ETH_ENABLE_TX() do{EMAC->CTL |= EMAC_CTL_TXON;}while(0)
\r
35 #define ETH_ENABLE_RX() do{EMAC->CTL |= EMAC_CTL_RXON;}while(0)
\r
36 #define ETH_DISABLE_TX() do{EMAC->CTL &= ~EMAC_CTL_TXON;}while(0)
\r
37 #define ETH_DISABLE_RX() do{EMAC->CTL &= ~EMAC_CTL_RXON;}while(0)
\r
40 struct eth_descriptor rx_desc[RX_DESCRIPTOR_NUM] __attribute__ ((aligned(4)));
\r
41 struct eth_descriptor tx_desc[TX_DESCRIPTOR_NUM] __attribute__ ((aligned(4)));
\r
43 #pragma data_alignment=4
\r
44 struct eth_descriptor rx_desc[RX_DESCRIPTOR_NUM];
\r
45 struct eth_descriptor tx_desc[TX_DESCRIPTOR_NUM];
\r
46 uint8_t rx_buf[RX_DESCRIPTOR_NUM][PACKET_BUFFER_SIZE];
\r
47 uint8_t tx_buf[TX_DESCRIPTOR_NUM][PACKET_BUFFER_SIZE];
\r
49 struct eth_descriptor rx_desc[RX_DESCRIPTOR_NUM] __attribute__ ((aligned(4)));
\r
50 struct eth_descriptor tx_desc[TX_DESCRIPTOR_NUM] __attribute__ ((aligned(4)));
\r
51 uint8_t rx_buf[RX_DESCRIPTOR_NUM][PACKET_BUFFER_SIZE] __attribute__ ((aligned(4)));
\r
52 uint8_t tx_buf[TX_DESCRIPTOR_NUM][PACKET_BUFFER_SIZE] __attribute__ ((aligned(4)));
\r
54 struct eth_descriptor volatile *cur_tx_desc_ptr, *cur_rx_desc_ptr, *fin_tx_desc_ptr;
\r
57 // PTP source clock is 84MHz (Real chip using PLL). Each tick is 11.90ns
\r
58 // Assume we want to set each tick to 100ns.
\r
59 // Increase register = (100 * 2^31) / (10^9) = 214.71 =~ 215 = 0xD7
\r
60 // Addend register = 2^32 * tick_freq / (84MHz), where tick_freq = (2^31 / 215) MHz
\r
61 // From above equation, addend register = 2^63 / (84M * 215) ~= 510707200 = 0x1E70C600
\r
65 static void mdio_write(uint8_t addr, uint8_t reg, uint16_t val)
\r
68 EMAC->MIIMDAT = val;
\r
69 EMAC->MIIMCTL = (addr << EMAC_MIIMCTL_PHYADDR_Pos) | reg | EMAC_MIIMCTL_BUSY_Msk | EMAC_MIIMCTL_WRITE_Msk | EMAC_MIIMCTL_MDCON_Msk;
\r
71 while (EMAC->MIIMCTL & EMAC_MIIMCTL_BUSY_Msk);
\r
76 static uint16_t mdio_read(uint8_t addr, uint8_t reg)
\r
78 EMAC->MIIMCTL = (addr << EMAC_MIIMCTL_PHYADDR_Pos) | reg | EMAC_MIIMCTL_BUSY_Msk | EMAC_MIIMCTL_MDCON_Msk;
\r
79 while (EMAC->MIIMCTL & EMAC_MIIMCTL_BUSY_Msk);
\r
81 return(EMAC->MIIMDAT);
\r
84 static int reset_phy(void)
\r
91 mdio_write(CONFIG_PHY_ADDR, MII_BMCR, BMCR_RESET);
\r
94 while(delayCnt-- > 0) {
\r
95 if((mdio_read(CONFIG_PHY_ADDR, MII_BMCR) & BMCR_RESET) == 0)
\r
100 if(delayCnt == 0) {
\r
101 NU_DEBUGF(("Reset phy failed\n"));
\r
105 mdio_write(CONFIG_PHY_ADDR, MII_ADVERTISE, ADVERTISE_CSMA |
\r
108 ADVERTISE_100HALF |
\r
109 ADVERTISE_100FULL);
\r
111 reg = mdio_read(CONFIG_PHY_ADDR, MII_BMCR);
\r
112 mdio_write(CONFIG_PHY_ADDR, MII_BMCR, reg | BMCR_ANRESTART);
\r
115 while(delayCnt-- > 0) {
\r
116 if((mdio_read(CONFIG_PHY_ADDR, MII_BMSR) & (BMSR_ANEGCOMPLETE | BMSR_LSTATUS))
\r
117 == (BMSR_ANEGCOMPLETE | BMSR_LSTATUS))
\r
121 if(delayCnt == 0) {
\r
122 NU_DEBUGF(("AN failed. Set to 100 FULL\n"));
\r
123 EMAC->CTL |= (EMAC_CTL_OPMODE_Msk | EMAC_CTL_FUDUP_Msk);
\r
126 reg = mdio_read(CONFIG_PHY_ADDR, MII_LPA);
\r
128 if(reg & ADVERTISE_100FULL) {
\r
129 NU_DEBUGF(("100 full\n"));
\r
130 EMAC->CTL |= (EMAC_CTL_OPMODE_Msk | EMAC_CTL_FUDUP_Msk);
\r
131 } else if(reg & ADVERTISE_100HALF) {
\r
132 NU_DEBUGF(("100 half\n"));
\r
133 EMAC->CTL = (EMAC->CTL & ~EMAC_CTL_FUDUP_Msk) | EMAC_CTL_OPMODE_Msk;
\r
134 } else if(reg & ADVERTISE_10FULL) {
\r
135 NU_DEBUGF(("10 full\n"));
\r
136 EMAC->CTL = (EMAC->CTL & ~EMAC_CTL_OPMODE_Msk) | EMAC_CTL_FUDUP_Msk;
\r
138 NU_DEBUGF(("10 half\n"));
\r
139 EMAC->CTL &= ~(EMAC_CTL_OPMODE_Msk | EMAC_CTL_FUDUP_Msk);
\r
142 FreeRTOS_printf(("PHY ID 1:0x%x\r\n", mdio_read(CONFIG_PHY_ADDR, MII_PHYSID1)));
\r
143 FreeRTOS_printf(("PHY ID 2:0x%x\r\n", mdio_read(CONFIG_PHY_ADDR, MII_PHYSID2)));
\r
149 static void init_tx_desc(void)
\r
154 cur_tx_desc_ptr = fin_tx_desc_ptr = &tx_desc[0];
\r
156 for(i = 0; i < TX_DESCRIPTOR_NUM; i++) {
\r
157 tx_desc[i].status1 = TXFD_PADEN | TXFD_CRCAPP | TXFD_INTEN;
\r
158 tx_desc[i].buf = &tx_buf[i][0];
\r
159 tx_desc[i].status2 = 0;
\r
160 tx_desc[i].next = &tx_desc[(i + 1) % TX_DESCRIPTOR_NUM];
\r
163 EMAC->TXDSA = (unsigned int)&tx_desc[0];
\r
167 static void init_rx_desc(void)
\r
172 cur_rx_desc_ptr = &rx_desc[0];
\r
174 for(i = 0; i < RX_DESCRIPTOR_NUM; i++) {
\r
175 rx_desc[i].status1 = OWNERSHIP_EMAC;
\r
176 rx_desc[i].buf = &rx_buf[i][0];
\r
177 rx_desc[i].status2 = 0;
\r
178 rx_desc[i].next = &rx_desc[(i + 1) % TX_DESCRIPTOR_NUM];
\r
180 EMAC->RXDSA = (unsigned int)&rx_desc[0];
\r
184 void numaker_set_mac_addr(uint8_t *addr)
\r
187 EMAC->CAM0M = (addr[0] << 24) |
\r
192 EMAC->CAM0L = (addr[4] << 24) |
\r
198 static void __eth_clk_pin_init()
\r
200 /* Unlock protected registers */
\r
203 /* Enable IP clock */
\r
204 CLK_EnableModuleClock(EMAC_MODULE);
\r
206 // Configure MDC clock rate to HCLK / (127 + 1) = 1.25 MHz if system is running at 160 MH
\r
207 CLK_SetModuleClock(EMAC_MODULE, 0, CLK_CLKDIV3_EMAC(127));
\r
209 /* Update System Core Clock */
\r
210 SystemCoreClockUpdate();
\r
212 /*---------------------------------------------------------------------------------------------------------*/
\r
213 /* Init I/O Multi-function */
\r
214 /*---------------------------------------------------------------------------------------------------------*/
\r
215 // Configure RMII pins
\r
216 SYS->GPA_MFPL &= ~(SYS_GPA_MFPL_PA6MFP_Msk | SYS_GPA_MFPL_PA7MFP_Msk);
\r
217 SYS->GPA_MFPL |= SYS_GPA_MFPL_PA6MFP_EMAC_RMII_RXERR | SYS_GPA_MFPL_PA7MFP_EMAC_RMII_CRSDV;
\r
218 SYS->GPC_MFPL &= ~(SYS_GPC_MFPL_PC6MFP_Msk | SYS_GPC_MFPL_PC7MFP_Msk);
\r
219 SYS->GPC_MFPL |= SYS_GPC_MFPL_PC6MFP_EMAC_RMII_RXD1 | SYS_GPC_MFPL_PC7MFP_EMAC_RMII_RXD0;
\r
220 SYS->GPC_MFPH &= ~SYS_GPC_MFPH_PC8MFP_Msk;
\r
221 SYS->GPC_MFPH |= SYS_GPC_MFPH_PC8MFP_EMAC_RMII_REFCLK;
\r
222 SYS->GPE_MFPH &= ~(SYS_GPE_MFPH_PE8MFP_Msk | SYS_GPE_MFPH_PE9MFP_Msk | SYS_GPE_MFPH_PE10MFP_Msk |
\r
223 SYS_GPE_MFPH_PE11MFP_Msk | SYS_GPE_MFPH_PE12MFP_Msk);
\r
224 SYS->GPE_MFPH |= SYS_GPE_MFPH_PE8MFP_EMAC_RMII_MDC |
\r
225 SYS_GPE_MFPH_PE9MFP_EMAC_RMII_MDIO |
\r
226 SYS_GPE_MFPH_PE10MFP_EMAC_RMII_TXD0 |
\r
227 SYS_GPE_MFPH_PE11MFP_EMAC_RMII_TXD1 |
\r
228 SYS_GPE_MFPH_PE12MFP_EMAC_RMII_TXEN;
\r
230 // Enable high slew rate on all RMII TX output pins
\r
231 PE->SLEWCTL = (GPIO_SLEWCTL_HIGH << GPIO_SLEWCTL_HSREN10_Pos) |
\r
232 (GPIO_SLEWCTL_HIGH << GPIO_SLEWCTL_HSREN11_Pos) |
\r
233 (GPIO_SLEWCTL_HIGH << GPIO_SLEWCTL_HSREN12_Pos);
\r
236 /* Lock protected registers */
\r
242 int numaker_eth_init(uint8_t *mac_addr)
\r
246 __eth_clk_pin_init();
\r
249 EMAC->CTL = EMAC_CTL_RST_Msk;
\r
250 while(EMAC->CTL & EMAC_CTL_RST_Msk) {}
\r
255 numaker_set_mac_addr(mac_addr); // need to reconfigure hardware address 'cos we just RESET emc...
\r
258 /* Configure the MAC interrupt enable register. */
\r
259 EMAC->INTEN = EMAC_INTEN_RXIEN_Msk |
\r
260 EMAC_INTEN_TXIEN_Msk |
\r
261 EMAC_INTEN_RXGDIEN_Msk |
\r
262 EMAC_INTEN_TXCPIEN_Msk |
\r
263 EMAC_INTEN_RXBEIEN_Msk |
\r
264 EMAC_INTEN_TXBEIEN_Msk |
\r
265 EMAC_INTEN_RDUIEN_Msk |
\r
266 EMAC_INTEN_TSALMIEN_Msk |
\r
267 EMAC_INTEN_WOLIEN_Msk;
\r
269 /* Configure the MAC control register. */
\r
270 EMAC->CTL = EMAC_CTL_STRIPCRC_Msk | EMAC_CTL_RMIIEN_Msk;
\r
272 /* Accept packets for us and all broadcast and multicast packets */
\r
273 EMAC->CAMCTL = EMAC_CAMCTL_CMPEN_Msk |
\r
274 EMAC_CAMCTL_AMP_Msk |
\r
275 EMAC_CAMCTL_ABP_Msk;
\r
276 EMAC->CAMEN = 1; // Enable CAM entry 0
\r
287 void ETH_halt(void)
\r
290 EMAC->CTL &= ~(EMAC_CTL_RXON_Msk | EMAC_CTL_TXON_Msk);
\r
293 unsigned int m_status;
\r
295 void EMAC_RX_IRQHandler(void)
\r
297 // NU_DEBUGF(("%s ... \r\n", __FUNCTION__));
\r
298 m_status = EMAC->INTSTS & 0xFFFF;
\r
299 EMAC->INTSTS = m_status;
\r
300 if (m_status & EMAC_INTSTS_RXBEIF_Msk) {
\r
301 // Shouldn't goes here, unless descriptor corrupted
\r
302 NU_DEBUGF(("RX descriptor corrupted \r\n"));
\r
305 // FIX ME: for rx-event, to ack rx_isr into event queue
\r
306 xNetworkCallback('R');
\r
310 void numaker_eth_trigger_rx(void)
\r
315 int numaker_eth_get_rx_buf(uint16_t *len, uint8_t **buf)
\r
317 unsigned int cur_entry, status;
\r
319 cur_entry = EMAC->CRXDSA;
\r
320 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 occures
\r
322 status = cur_rx_desc_ptr->status1;
\r
324 if(status & OWNERSHIP_EMAC)
\r
327 if (status & RXFD_RXGD) {
\r
328 *buf = cur_rx_desc_ptr->buf;
\r
329 *len = status & 0xFFFF;
\r
334 void numaker_eth_rx_next(void)
\r
336 cur_rx_desc_ptr->status1 = OWNERSHIP_EMAC;
\r
337 cur_rx_desc_ptr = cur_rx_desc_ptr->next;
\r
340 void EMAC_TX_IRQHandler(void)
\r
342 unsigned int cur_entry, status;
\r
344 status = EMAC->INTSTS & 0xFFFF0000;
\r
345 EMAC->INTSTS = status;
\r
346 if(status & EMAC_INTSTS_TXBEIF_Msk) {
\r
347 // Shouldn't goes here, unless descriptor corrupted
\r
351 cur_entry = EMAC->CTXDSA;
\r
353 while (cur_entry != (uint32_t)fin_tx_desc_ptr) {
\r
355 fin_tx_desc_ptr = fin_tx_desc_ptr->next;
\r
357 // FIX ME: for tx-event, no-op at this stage
\r
358 xNetworkCallback('T');
\r
361 uint8_t *numaker_eth_get_tx_buf(void)
\r
363 if(cur_tx_desc_ptr->status1 & OWNERSHIP_EMAC)
\r
366 return(cur_tx_desc_ptr->buf);
\r
369 void numaker_eth_trigger_tx(uint16_t length, void *p)
\r
371 struct eth_descriptor volatile *desc;
\r
372 cur_tx_desc_ptr->status2 = (unsigned int)length;
\r
373 desc = cur_tx_desc_ptr->next; // in case TX is transmitting and overwrite next pointer before we can update cur_tx_desc_ptr
\r
374 cur_tx_desc_ptr->status1 |= OWNERSHIP_EMAC;
\r
375 cur_tx_desc_ptr = desc;
\r
381 int numaker_eth_link_ok(void)
\r
383 /* first, a dummy read to latch */
\r
384 mdio_read(CONFIG_PHY_ADDR, MII_BMSR);
\r
385 if(mdio_read(CONFIG_PHY_ADDR, MII_BMSR) & BMSR_LSTATUS)
\r
390 //void numaker_eth_set_cb(eth_callback_t eth_cb, void *userData)
\r
392 // nu_eth_txrx_cb = eth_cb;
\r
393 // nu_userData = userData;
\r
396 // Provide ethernet devices with a semi-unique MAC address
\r
397 void numaker_mac_address(uint8_t *mac)
\r
401 uint32_t word0 = *(uint32_t *)0x7F804; // 2KB Data Flash at 0x7F800
\r
403 // we only want bottom 16 bits of word1 (MAC bits 32-47)
\r
404 // and bit 9 forced to 1, bit 8 forced to 0
\r
405 // Locally administered MAC, reduced conflicts
\r
406 // http://en.wikipedia.org/wiki/MAC_address
\r
407 uint32_t word1 = *(uint32_t *)0x7F800; // 2KB Data Flash at 0x7F800
\r
409 if( word0 == 0xFFFFFFFF ) // Not burn any mac address at 1st 2 words of Data Flash
\r
411 // with a semi-unique MAC address from the UUID
\r
412 /* Enable FMC ISP function */
\r
415 // = FMC_ReadUID(0);
\r
416 uID1 = FMC_ReadUID(1);
\r
417 word1 = (uID1 & 0x003FFFFF) | ((uID1 & 0x030000) << 6) >> 8;
\r
418 word0 = ((FMC_ReadUID(0) >> 4) << 20) | ((uID1 & 0xFF)<<12) | (FMC_ReadUID(2) & 0xFFF);
\r
419 /* Disable FMC ISP function */
\r
421 /* Lock protected registers */
\r
425 word1 |= 0x00000200;
\r
426 word1 &= 0x0000FEFF;
\r
428 mac[0] = (word1 & 0x0000ff00) >> 8;
\r
429 mac[1] = (word1 & 0x000000ff);
\r
430 mac[2] = (word0 & 0xff000000) >> 24;
\r
431 mac[3] = (word0 & 0x00ff0000) >> 16;
\r
432 mac[4] = (word0 & 0x0000ff00) >> 8;
\r
433 mac[5] = (word0 & 0x000000ff);
\r
435 NU_DEBUGF(("mac address %02x-%02x-%02x-%02x-%02x-%02x \r\n", mac[0], mac[1],mac[2],mac[3],mac[4],mac[5]));
\r
438 void numaker_eth_enable_interrupts(void) {
\r
439 EMAC->INTEN |= EMAC_INTEN_RXIEN_Msk |
\r
440 EMAC_INTEN_TXIEN_Msk ;
\r
441 NVIC_EnableIRQ(EMAC_RX_IRQn);
\r
442 NVIC_EnableIRQ(EMAC_TX_IRQn);
\r
445 void numaker_eth_disable_interrupts(void) {
\r
446 NVIC_DisableIRQ(EMAC_RX_IRQn);
\r
447 NVIC_DisableIRQ(EMAC_TX_IRQn);
\r