]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/M487/m480_eth.c
Sync FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP with the version in GitHub at (23665258ca...
[freertos] / FreeRTOS-Plus / Source / FreeRTOS-Plus-TCP / portable / NetworkInterface / M487 / m480_eth.c
1 /**************************************************************************//**\r
2  * @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved.\r
3  * \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
14  * \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
27 #include "list.h"\r
28 #include "FreeRTOS_IP.h"\r
29 \r
30 #include "m480_eth.h"\r
31 \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
38     \r
39 \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
42 #ifdef __ICCARM__\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
48 #else\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
53 #endif\r
54 struct eth_descriptor volatile *cur_tx_desc_ptr, *cur_rx_desc_ptr, *fin_tx_desc_ptr;\r
55 \r
56 \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
62 \r
63 \r
64 \r
65 static void mdio_write(uint8_t addr, uint8_t reg, uint16_t val)\r
66 {\r
67 \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
70 \r
71     while (EMAC->MIIMCTL & EMAC_MIIMCTL_BUSY_Msk);\r
72 \r
73 }\r
74 \r
75 \r
76 static uint16_t mdio_read(uint8_t addr, uint8_t reg)\r
77 {\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
80 \r
81     return(EMAC->MIIMDAT);\r
82 }\r
83 \r
84 static int reset_phy(void)\r
85 {\r
86 \r
87     uint16_t reg;\r
88     uint32_t delayCnt;\r
89 \r
90 \r
91     mdio_write(CONFIG_PHY_ADDR, MII_BMCR, BMCR_RESET);\r
92 \r
93     delayCnt = 2000;\r
94     while(delayCnt-- > 0) {\r
95         if((mdio_read(CONFIG_PHY_ADDR, MII_BMCR) & BMCR_RESET) == 0)\r
96             break;\r
97 \r
98     }\r
99 \r
100     if(delayCnt == 0) {\r
101         NU_DEBUGF(("Reset phy failed\n"));\r
102         return(-1);\r
103     }\r
104 \r
105     mdio_write(CONFIG_PHY_ADDR, MII_ADVERTISE, ADVERTISE_CSMA |\r
106                ADVERTISE_10HALF |\r
107                ADVERTISE_10FULL |\r
108                ADVERTISE_100HALF |\r
109                ADVERTISE_100FULL);\r
110 \r
111     reg = mdio_read(CONFIG_PHY_ADDR, MII_BMCR);\r
112     mdio_write(CONFIG_PHY_ADDR, MII_BMCR, reg | BMCR_ANRESTART);\r
113 \r
114     delayCnt = 200000;\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
118             break;\r
119     }\r
120 \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
124         return(-1);\r
125     } else {\r
126         reg = mdio_read(CONFIG_PHY_ADDR, MII_LPA);\r
127 \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
137         } else {\r
138             NU_DEBUGF(("10 half\n"));\r
139             EMAC->CTL &= ~(EMAC_CTL_OPMODE_Msk | EMAC_CTL_FUDUP_Msk);\r
140         }\r
141     }\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
144 \r
145     return(0);\r
146 }\r
147 \r
148 \r
149 static void init_tx_desc(void)\r
150 {\r
151     uint32_t i;\r
152 \r
153 \r
154     cur_tx_desc_ptr = fin_tx_desc_ptr = &tx_desc[0];\r
155 \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
161 \r
162     }\r
163     EMAC->TXDSA = (unsigned int)&tx_desc[0];\r
164     return;\r
165 }\r
166 \r
167 static void init_rx_desc(void)\r
168 {\r
169     uint32_t i;\r
170 \r
171 \r
172     cur_rx_desc_ptr = &rx_desc[0];\r
173 \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
179     }\r
180     EMAC->RXDSA = (unsigned int)&rx_desc[0];\r
181     return;\r
182 }\r
183 \r
184 void numaker_set_mac_addr(uint8_t *addr)\r
185 {\r
186 \r
187     EMAC->CAM0M = (addr[0] << 24) |\r
188                   (addr[1] << 16) |\r
189                   (addr[2] << 8) |\r
190                   addr[3];\r
191 \r
192     EMAC->CAM0L = (addr[4] << 24) |\r
193                   (addr[5] << 16);\r
194 \r
195 \r
196 }\r
197 \r
198 static void __eth_clk_pin_init()\r
199 {\r
200     /* Unlock protected registers */\r
201     SYS_UnlockReg();\r
202 \r
203     /* Enable IP clock */\r
204     CLK_EnableModuleClock(EMAC_MODULE);\r
205     \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
208     \r
209     /* Update System Core Clock */\r
210     SystemCoreClockUpdate();\r
211     \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
229 \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
234 \r
235 \r
236     /* Lock protected registers */\r
237     SYS_LockReg();\r
238 \r
239 \r
240 }\r
241 \r
242 int numaker_eth_init(uint8_t *mac_addr)\r
243 {\r
244     int ret = 0;\r
245     // init CLK & pins\r
246     __eth_clk_pin_init();\r
247   \r
248     // Reset MAC\r
249     EMAC->CTL = EMAC_CTL_RST_Msk;\r
250     while(EMAC->CTL & EMAC_CTL_RST_Msk) {}\r
251 \r
252     init_tx_desc();\r
253     init_rx_desc();\r
254 \r
255     numaker_set_mac_addr(mac_addr);  // need to reconfigure hardware address 'cos we just RESET emc...\r
256 \r
257 \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
268 \r
269     /* Configure the MAC control register. */\r
270     EMAC->CTL = EMAC_CTL_STRIPCRC_Msk | EMAC_CTL_RMIIEN_Msk;\r
271 \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
277 \r
278     ret= reset_phy();                    \r
279                     \r
280     EMAC_ENABLE_RX();\r
281     EMAC_ENABLE_TX();\r
282     return ret;\r
283 }\r
284 \r
285 \r
286 \r
287 void  ETH_halt(void)\r
288 {\r
289 \r
290     EMAC->CTL &= ~(EMAC_CTL_RXON_Msk | EMAC_CTL_TXON_Msk);\r
291 }\r
292 \r
293 unsigned int m_status;\r
294 \r
295 void EMAC_RX_IRQHandler(void)\r
296 {\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
303                 //return;\r
304     }\r
305     // FIX ME: for rx-event, to ack rx_isr into event queue\r
306         xNetworkCallback('R');\r
307 }\r
308 \r
309 \r
310 void numaker_eth_trigger_rx(void)\r
311 {\r
312     ETH_TRIGGER_RX();\r
313 }\r
314 \r
315 int numaker_eth_get_rx_buf(uint16_t *len, uint8_t **buf)\r
316 {\r
317     unsigned int cur_entry, status;\r
318 \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
321             return -1;\r
322     status = cur_rx_desc_ptr->status1;\r
323 \r
324     if(status & OWNERSHIP_EMAC)\r
325             return -1;\r
326 \r
327     if (status & RXFD_RXGD) {\r
328         *buf = cur_rx_desc_ptr->buf;\r
329         *len = status & 0xFFFF;\r
330     }\r
331     return 0;\r
332 }    \r
333 \r
334 void numaker_eth_rx_next(void)\r
335 {\r
336     cur_rx_desc_ptr->status1 = OWNERSHIP_EMAC;\r
337     cur_rx_desc_ptr = cur_rx_desc_ptr->next;    \r
338 }    \r
339 \r
340 void EMAC_TX_IRQHandler(void)\r
341 {\r
342     unsigned int cur_entry, status;\r
343 \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
348         return;\r
349     }\r
350 \r
351     cur_entry = EMAC->CTXDSA;\r
352 \r
353     while (cur_entry != (uint32_t)fin_tx_desc_ptr) {\r
354 \r
355         fin_tx_desc_ptr = fin_tx_desc_ptr->next;\r
356     }\r
357     // FIX ME: for tx-event, no-op at this stage\r
358     xNetworkCallback('T');\r
359 }\r
360 \r
361 uint8_t *numaker_eth_get_tx_buf(void)\r
362 {\r
363     if(cur_tx_desc_ptr->status1 & OWNERSHIP_EMAC)\r
364         return(NULL);\r
365     else\r
366         return(cur_tx_desc_ptr->buf);\r
367 }\r
368 \r
369 void numaker_eth_trigger_tx(uint16_t length, void *p)\r
370 {\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
376 \r
377     ETH_TRIGGER_TX();\r
378 \r
379 }\r
380 \r
381 int numaker_eth_link_ok(void)\r
382 {\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
386       return 1;\r
387     return 0;   \r
388 }\r
389 \r
390 //void numaker_eth_set_cb(eth_callback_t eth_cb, void *userData)\r
391 //{\r
392 //    nu_eth_txrx_cb =  eth_cb;\r
393 //    nu_userData = userData;\r
394 //}\r
395 \r
396 // Provide ethernet devices with a semi-unique MAC address\r
397 void numaker_mac_address(uint8_t *mac)\r
398 {\r
399     uint32_t uID1;\r
400     // Fetch word 0\r
401     uint32_t word0 = *(uint32_t *)0x7F804; // 2KB Data Flash at 0x7F800\r
402     // Fetch word 1\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
408 \r
409     if( word0 == 0xFFFFFFFF )           // Not burn any mac address at 1st 2 words of Data Flash\r
410     {\r
411         // with a semi-unique MAC address from the UUID\r
412         /* Enable FMC ISP function */\r
413         SYS_UnlockReg();\r
414         FMC_Open();\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
420         FMC_Close();\r
421         /* Lock protected registers */\r
422         SYS_LockReg();\r
423     }\r
424 \r
425     word1 |= 0x00000200;\r
426     word1 &= 0x0000FEFF;\r
427 \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
434     \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
436 }\r
437 \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
443 }\r
444 \r
445 void numaker_eth_disable_interrupts(void) {\r
446   NVIC_DisableIRQ(EMAC_RX_IRQn);\r
447   NVIC_DisableIRQ(EMAC_TX_IRQn);\r
448 }\r