X-Git-Url: https://git.sur5r.net/?p=freertos;a=blobdiff_plain;f=FreeRTOS-Plus%2FSource%2FFreeRTOS-Plus-TCP%2Fportable%2FNetworkInterface%2FM487%2Fm480_eth.c;fp=FreeRTOS-Plus%2FSource%2FFreeRTOS-Plus-TCP%2Fportable%2FNetworkInterface%2FM487%2Fm480_eth.c;h=2d45661d003606d220e0bfba536b36a7ebd73a29;hp=cf862da3ea7c9c110ca546ac52c565682435332b;hb=4fd3a0b144b6aa91eabd79a1eaf9bc0c8397f433;hpb=b15dfacb6026af3b0ba697e5753844923b468d2b diff --git a/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/M487/m480_eth.c b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/M487/m480_eth.c index cf862da3e..2d45661d0 100644 --- a/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/M487/m480_eth.c +++ b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/M487/m480_eth.c @@ -1,448 +1,448 @@ -/**************************************************************************//** - * @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of Nuvoton Technology Corp. nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*****************************************************************************/ -#include "FreeRTOS.h" -#include "list.h" -#include "FreeRTOS_IP.h" - -#include "m480_eth.h" - -#define ETH_TRIGGER_RX() do{EMAC->RXST = 0;}while(0) -#define ETH_TRIGGER_TX() do{EMAC->TXST = 0;}while(0) -#define ETH_ENABLE_TX() do{EMAC->CTL |= EMAC_CTL_TXON;}while(0) -#define ETH_ENABLE_RX() do{EMAC->CTL |= EMAC_CTL_RXON;}while(0) -#define ETH_DISABLE_TX() do{EMAC->CTL &= ~EMAC_CTL_TXON;}while(0) -#define ETH_DISABLE_RX() do{EMAC->CTL &= ~EMAC_CTL_RXON;}while(0) - - -struct eth_descriptor rx_desc[RX_DESCRIPTOR_NUM] __attribute__ ((aligned(4))); -struct eth_descriptor tx_desc[TX_DESCRIPTOR_NUM] __attribute__ ((aligned(4))); -#ifdef __ICCARM__ -#pragma data_alignment=4 -struct eth_descriptor rx_desc[RX_DESCRIPTOR_NUM]; -struct eth_descriptor tx_desc[TX_DESCRIPTOR_NUM]; -uint8_t rx_buf[RX_DESCRIPTOR_NUM][PACKET_BUFFER_SIZE]; -uint8_t tx_buf[TX_DESCRIPTOR_NUM][PACKET_BUFFER_SIZE]; -#else -struct eth_descriptor rx_desc[RX_DESCRIPTOR_NUM] __attribute__ ((aligned(4))); -struct eth_descriptor tx_desc[TX_DESCRIPTOR_NUM] __attribute__ ((aligned(4))); -uint8_t rx_buf[RX_DESCRIPTOR_NUM][PACKET_BUFFER_SIZE] __attribute__ ((aligned(4))); -uint8_t tx_buf[TX_DESCRIPTOR_NUM][PACKET_BUFFER_SIZE] __attribute__ ((aligned(4))); -#endif -struct eth_descriptor volatile *cur_tx_desc_ptr, *cur_rx_desc_ptr, *fin_tx_desc_ptr; - - -// PTP source clock is 84MHz (Real chip using PLL). Each tick is 11.90ns -// Assume we want to set each tick to 100ns. -// Increase register = (100 * 2^31) / (10^9) = 214.71 =~ 215 = 0xD7 -// Addend register = 2^32 * tick_freq / (84MHz), where tick_freq = (2^31 / 215) MHz -// From above equation, addend register = 2^63 / (84M * 215) ~= 510707200 = 0x1E70C600 - - - -static void mdio_write(uint8_t addr, uint8_t reg, uint16_t val) -{ - - EMAC->MIIMDAT = val; - EMAC->MIIMCTL = (addr << EMAC_MIIMCTL_PHYADDR_Pos) | reg | EMAC_MIIMCTL_BUSY_Msk | EMAC_MIIMCTL_WRITE_Msk | EMAC_MIIMCTL_MDCON_Msk; - - while (EMAC->MIIMCTL & EMAC_MIIMCTL_BUSY_Msk); - -} - - -static uint16_t mdio_read(uint8_t addr, uint8_t reg) -{ - EMAC->MIIMCTL = (addr << EMAC_MIIMCTL_PHYADDR_Pos) | reg | EMAC_MIIMCTL_BUSY_Msk | EMAC_MIIMCTL_MDCON_Msk; - while (EMAC->MIIMCTL & EMAC_MIIMCTL_BUSY_Msk); - - return(EMAC->MIIMDAT); -} - -static int reset_phy(void) -{ - - uint16_t reg; - uint32_t delayCnt; - - - mdio_write(CONFIG_PHY_ADDR, MII_BMCR, BMCR_RESET); - - delayCnt = 2000; - while(delayCnt-- > 0) { - if((mdio_read(CONFIG_PHY_ADDR, MII_BMCR) & BMCR_RESET) == 0) - break; - - } - - if(delayCnt == 0) { - NU_DEBUGF(("Reset phy failed\n")); - return(-1); - } - - mdio_write(CONFIG_PHY_ADDR, MII_ADVERTISE, ADVERTISE_CSMA | - ADVERTISE_10HALF | - ADVERTISE_10FULL | - ADVERTISE_100HALF | - ADVERTISE_100FULL); - - reg = mdio_read(CONFIG_PHY_ADDR, MII_BMCR); - mdio_write(CONFIG_PHY_ADDR, MII_BMCR, reg | BMCR_ANRESTART); - - delayCnt = 200000; - while(delayCnt-- > 0) { - if((mdio_read(CONFIG_PHY_ADDR, MII_BMSR) & (BMSR_ANEGCOMPLETE | BMSR_LSTATUS)) - == (BMSR_ANEGCOMPLETE | BMSR_LSTATUS)) - break; - } - - if(delayCnt == 0) { - NU_DEBUGF(("AN failed. Set to 100 FULL\n")); - EMAC->CTL |= (EMAC_CTL_OPMODE_Msk | EMAC_CTL_FUDUP_Msk); - return(-1); - } else { - reg = mdio_read(CONFIG_PHY_ADDR, MII_LPA); - - if(reg & ADVERTISE_100FULL) { - NU_DEBUGF(("100 full\n")); - EMAC->CTL |= (EMAC_CTL_OPMODE_Msk | EMAC_CTL_FUDUP_Msk); - } else if(reg & ADVERTISE_100HALF) { - NU_DEBUGF(("100 half\n")); - EMAC->CTL = (EMAC->CTL & ~EMAC_CTL_FUDUP_Msk) | EMAC_CTL_OPMODE_Msk; - } else if(reg & ADVERTISE_10FULL) { - NU_DEBUGF(("10 full\n")); - EMAC->CTL = (EMAC->CTL & ~EMAC_CTL_OPMODE_Msk) | EMAC_CTL_FUDUP_Msk; - } else { - NU_DEBUGF(("10 half\n")); - EMAC->CTL &= ~(EMAC_CTL_OPMODE_Msk | EMAC_CTL_FUDUP_Msk); - } - } - FreeRTOS_printf(("PHY ID 1:0x%x\r\n", mdio_read(CONFIG_PHY_ADDR, MII_PHYSID1))); - FreeRTOS_printf(("PHY ID 2:0x%x\r\n", mdio_read(CONFIG_PHY_ADDR, MII_PHYSID2))); - - return(0); -} - - -static void init_tx_desc(void) -{ - uint32_t i; - - - cur_tx_desc_ptr = fin_tx_desc_ptr = &tx_desc[0]; - - for(i = 0; i < TX_DESCRIPTOR_NUM; i++) { - tx_desc[i].status1 = TXFD_PADEN | TXFD_CRCAPP | TXFD_INTEN; - tx_desc[i].buf = &tx_buf[i][0]; - tx_desc[i].status2 = 0; - tx_desc[i].next = &tx_desc[(i + 1) % TX_DESCRIPTOR_NUM]; - - } - EMAC->TXDSA = (unsigned int)&tx_desc[0]; - return; -} - -static void init_rx_desc(void) -{ - uint32_t i; - - - cur_rx_desc_ptr = &rx_desc[0]; - - for(i = 0; i < RX_DESCRIPTOR_NUM; i++) { - rx_desc[i].status1 = OWNERSHIP_EMAC; - rx_desc[i].buf = &rx_buf[i][0]; - rx_desc[i].status2 = 0; - rx_desc[i].next = &rx_desc[(i + 1) % TX_DESCRIPTOR_NUM]; - } - EMAC->RXDSA = (unsigned int)&rx_desc[0]; - return; -} - -void numaker_set_mac_addr(uint8_t *addr) -{ - - EMAC->CAM0M = (addr[0] << 24) | - (addr[1] << 16) | - (addr[2] << 8) | - addr[3]; - - EMAC->CAM0L = (addr[4] << 24) | - (addr[5] << 16); - - -} - -static void __eth_clk_pin_init() -{ - /* Unlock protected registers */ - SYS_UnlockReg(); - - /* Enable IP clock */ - CLK_EnableModuleClock(EMAC_MODULE); - - // Configure MDC clock rate to HCLK / (127 + 1) = 1.25 MHz if system is running at 160 MH - CLK_SetModuleClock(EMAC_MODULE, 0, CLK_CLKDIV3_EMAC(127)); - - /* Update System Core Clock */ - SystemCoreClockUpdate(); - - /*---------------------------------------------------------------------------------------------------------*/ - /* Init I/O Multi-function */ - /*---------------------------------------------------------------------------------------------------------*/ - // Configure RMII pins - SYS->GPA_MFPL &= ~(SYS_GPA_MFPL_PA6MFP_Msk | SYS_GPA_MFPL_PA7MFP_Msk); - SYS->GPA_MFPL |= SYS_GPA_MFPL_PA6MFP_EMAC_RMII_RXERR | SYS_GPA_MFPL_PA7MFP_EMAC_RMII_CRSDV; - SYS->GPC_MFPL &= ~(SYS_GPC_MFPL_PC6MFP_Msk | SYS_GPC_MFPL_PC7MFP_Msk); - SYS->GPC_MFPL |= SYS_GPC_MFPL_PC6MFP_EMAC_RMII_RXD1 | SYS_GPC_MFPL_PC7MFP_EMAC_RMII_RXD0; - SYS->GPC_MFPH &= ~SYS_GPC_MFPH_PC8MFP_Msk; - SYS->GPC_MFPH |= SYS_GPC_MFPH_PC8MFP_EMAC_RMII_REFCLK; - SYS->GPE_MFPH &= ~(SYS_GPE_MFPH_PE8MFP_Msk | SYS_GPE_MFPH_PE9MFP_Msk | SYS_GPE_MFPH_PE10MFP_Msk | - SYS_GPE_MFPH_PE11MFP_Msk | SYS_GPE_MFPH_PE12MFP_Msk); - SYS->GPE_MFPH |= SYS_GPE_MFPH_PE8MFP_EMAC_RMII_MDC | - SYS_GPE_MFPH_PE9MFP_EMAC_RMII_MDIO | - SYS_GPE_MFPH_PE10MFP_EMAC_RMII_TXD0 | - SYS_GPE_MFPH_PE11MFP_EMAC_RMII_TXD1 | - SYS_GPE_MFPH_PE12MFP_EMAC_RMII_TXEN; - - // Enable high slew rate on all RMII TX output pins - PE->SLEWCTL = (GPIO_SLEWCTL_HIGH << GPIO_SLEWCTL_HSREN10_Pos) | - (GPIO_SLEWCTL_HIGH << GPIO_SLEWCTL_HSREN11_Pos) | - (GPIO_SLEWCTL_HIGH << GPIO_SLEWCTL_HSREN12_Pos); - - - /* Lock protected registers */ - SYS_LockReg(); - - -} - -int numaker_eth_init(uint8_t *mac_addr) -{ - int ret = 0; - // init CLK & pins - __eth_clk_pin_init(); - - // Reset MAC - EMAC->CTL = EMAC_CTL_RST_Msk; - while(EMAC->CTL & EMAC_CTL_RST_Msk) {} - - init_tx_desc(); - init_rx_desc(); - - numaker_set_mac_addr(mac_addr); // need to reconfigure hardware address 'cos we just RESET emc... - - - /* Configure the MAC interrupt enable register. */ - EMAC->INTEN = EMAC_INTEN_RXIEN_Msk | - EMAC_INTEN_TXIEN_Msk | - EMAC_INTEN_RXGDIEN_Msk | - EMAC_INTEN_TXCPIEN_Msk | - EMAC_INTEN_RXBEIEN_Msk | - EMAC_INTEN_TXBEIEN_Msk | - EMAC_INTEN_RDUIEN_Msk | - EMAC_INTEN_TSALMIEN_Msk | - EMAC_INTEN_WOLIEN_Msk; - - /* Configure the MAC control register. */ - EMAC->CTL = EMAC_CTL_STRIPCRC_Msk | EMAC_CTL_RMIIEN_Msk; - - /* Accept packets for us and all broadcast and multicast packets */ - EMAC->CAMCTL = EMAC_CAMCTL_CMPEN_Msk | - EMAC_CAMCTL_AMP_Msk | - EMAC_CAMCTL_ABP_Msk; - EMAC->CAMEN = 1; // Enable CAM entry 0 - - ret= reset_phy(); - - EMAC_ENABLE_RX(); - EMAC_ENABLE_TX(); - return ret; -} - - - -void ETH_halt(void) -{ - - EMAC->CTL &= ~(EMAC_CTL_RXON_Msk | EMAC_CTL_TXON_Msk); -} - -unsigned int m_status; - -void EMAC_RX_IRQHandler(void) -{ -// NU_DEBUGF(("%s ... \r\n", __FUNCTION__)); - m_status = EMAC->INTSTS & 0xFFFF; - EMAC->INTSTS = m_status; - if (m_status & EMAC_INTSTS_RXBEIF_Msk) { - // Shouldn't goes here, unless descriptor corrupted - NU_DEBUGF(("RX descriptor corrupted \r\n")); - //return; - } - // FIX ME: for rx-event, to ack rx_isr into event queue - xNetworkCallback('R'); -} - - -void numaker_eth_trigger_rx(void) -{ - ETH_TRIGGER_RX(); -} - -int numaker_eth_get_rx_buf(uint16_t *len, uint8_t **buf) -{ - unsigned int cur_entry, status; - - cur_entry = EMAC->CRXDSA; - 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 - return -1; - status = cur_rx_desc_ptr->status1; - - if(status & OWNERSHIP_EMAC) - return -1; - - if (status & RXFD_RXGD) { - *buf = cur_rx_desc_ptr->buf; - *len = status & 0xFFFF; - } - return 0; -} - -void numaker_eth_rx_next(void) -{ - cur_rx_desc_ptr->status1 = OWNERSHIP_EMAC; - cur_rx_desc_ptr = cur_rx_desc_ptr->next; -} - -void EMAC_TX_IRQHandler(void) -{ - unsigned int cur_entry, status; - - status = EMAC->INTSTS & 0xFFFF0000; - EMAC->INTSTS = status; - if(status & EMAC_INTSTS_TXBEIF_Msk) { - // Shouldn't goes here, unless descriptor corrupted - return; - } - - cur_entry = EMAC->CTXDSA; - - while (cur_entry != (uint32_t)fin_tx_desc_ptr) { - - fin_tx_desc_ptr = fin_tx_desc_ptr->next; - } - // FIX ME: for tx-event, no-op at this stage - xNetworkCallback('T'); -} - -uint8_t *numaker_eth_get_tx_buf(void) -{ - if(cur_tx_desc_ptr->status1 & OWNERSHIP_EMAC) - return(NULL); - else - return(cur_tx_desc_ptr->buf); -} - -void numaker_eth_trigger_tx(uint16_t length, void *p) -{ - struct eth_descriptor volatile *desc; - cur_tx_desc_ptr->status2 = (unsigned int)length; - desc = cur_tx_desc_ptr->next; // in case TX is transmitting and overwrite next pointer before we can update cur_tx_desc_ptr - cur_tx_desc_ptr->status1 |= OWNERSHIP_EMAC; - cur_tx_desc_ptr = desc; - - ETH_TRIGGER_TX(); - -} - -int numaker_eth_link_ok(void) -{ - /* first, a dummy read to latch */ - mdio_read(CONFIG_PHY_ADDR, MII_BMSR); - if(mdio_read(CONFIG_PHY_ADDR, MII_BMSR) & BMSR_LSTATUS) - return 1; - return 0; -} - -//void numaker_eth_set_cb(eth_callback_t eth_cb, void *userData) -//{ -// nu_eth_txrx_cb = eth_cb; -// nu_userData = userData; -//} - -// Provide ethernet devices with a semi-unique MAC address -void numaker_mac_address(uint8_t *mac) -{ - uint32_t uID1; - // Fetch word 0 - uint32_t word0 = *(uint32_t *)0x7F804; // 2KB Data Flash at 0x7F800 - // Fetch word 1 - // we only want bottom 16 bits of word1 (MAC bits 32-47) - // and bit 9 forced to 1, bit 8 forced to 0 - // Locally administered MAC, reduced conflicts - // http://en.wikipedia.org/wiki/MAC_address - uint32_t word1 = *(uint32_t *)0x7F800; // 2KB Data Flash at 0x7F800 - - if( word0 == 0xFFFFFFFF ) // Not burn any mac address at 1st 2 words of Data Flash - { - // with a semi-unique MAC address from the UUID - /* Enable FMC ISP function */ - SYS_UnlockReg(); - FMC_Open(); - // = FMC_ReadUID(0); - uID1 = FMC_ReadUID(1); - word1 = (uID1 & 0x003FFFFF) | ((uID1 & 0x030000) << 6) >> 8; - word0 = ((FMC_ReadUID(0) >> 4) << 20) | ((uID1 & 0xFF)<<12) | (FMC_ReadUID(2) & 0xFFF); - /* Disable FMC ISP function */ - FMC_Close(); - /* Lock protected registers */ - SYS_LockReg(); - } - - word1 |= 0x00000200; - word1 &= 0x0000FEFF; - - mac[0] = (word1 & 0x0000ff00) >> 8; - mac[1] = (word1 & 0x000000ff); - mac[2] = (word0 & 0xff000000) >> 24; - mac[3] = (word0 & 0x00ff0000) >> 16; - mac[4] = (word0 & 0x0000ff00) >> 8; - mac[5] = (word0 & 0x000000ff); - - NU_DEBUGF(("mac address %02x-%02x-%02x-%02x-%02x-%02x \r\n", mac[0], mac[1],mac[2],mac[3],mac[4],mac[5])); -} - -void numaker_eth_enable_interrupts(void) { - EMAC->INTEN |= EMAC_INTEN_RXIEN_Msk | - EMAC_INTEN_TXIEN_Msk ; - NVIC_EnableIRQ(EMAC_RX_IRQn); - NVIC_EnableIRQ(EMAC_TX_IRQn); -} - -void numaker_eth_disable_interrupts(void) { - NVIC_DisableIRQ(EMAC_RX_IRQn); - NVIC_DisableIRQ(EMAC_TX_IRQn); -} +/**************************************************************************//** + * @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Nuvoton Technology Corp. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ +#include "FreeRTOS.h" +#include "list.h" +#include "FreeRTOS_IP.h" + +#include "m480_eth.h" + +#define ETH_TRIGGER_RX() do{EMAC->RXST = 0;}while(0) +#define ETH_TRIGGER_TX() do{EMAC->TXST = 0;}while(0) +#define ETH_ENABLE_TX() do{EMAC->CTL |= EMAC_CTL_TXON;}while(0) +#define ETH_ENABLE_RX() do{EMAC->CTL |= EMAC_CTL_RXON;}while(0) +#define ETH_DISABLE_TX() do{EMAC->CTL &= ~EMAC_CTL_TXON;}while(0) +#define ETH_DISABLE_RX() do{EMAC->CTL &= ~EMAC_CTL_RXON;}while(0) + + +struct eth_descriptor rx_desc[RX_DESCRIPTOR_NUM] __attribute__ ((aligned(4))); +struct eth_descriptor tx_desc[TX_DESCRIPTOR_NUM] __attribute__ ((aligned(4))); +#ifdef __ICCARM__ +#pragma data_alignment=4 +struct eth_descriptor rx_desc[RX_DESCRIPTOR_NUM]; +struct eth_descriptor tx_desc[TX_DESCRIPTOR_NUM]; +uint8_t rx_buf[RX_DESCRIPTOR_NUM][PACKET_BUFFER_SIZE]; +uint8_t tx_buf[TX_DESCRIPTOR_NUM][PACKET_BUFFER_SIZE]; +#else +struct eth_descriptor rx_desc[RX_DESCRIPTOR_NUM] __attribute__ ((aligned(4))); +struct eth_descriptor tx_desc[TX_DESCRIPTOR_NUM] __attribute__ ((aligned(4))); +uint8_t rx_buf[RX_DESCRIPTOR_NUM][PACKET_BUFFER_SIZE] __attribute__ ((aligned(4))); +uint8_t tx_buf[TX_DESCRIPTOR_NUM][PACKET_BUFFER_SIZE] __attribute__ ((aligned(4))); +#endif +struct eth_descriptor volatile *cur_tx_desc_ptr, *cur_rx_desc_ptr, *fin_tx_desc_ptr; + + +// PTP source clock is 84MHz (Real chip using PLL). Each tick is 11.90ns +// Assume we want to set each tick to 100ns. +// Increase register = (100 * 2^31) / (10^9) = 214.71 =~ 215 = 0xD7 +// Addend register = 2^32 * tick_freq / (84MHz), where tick_freq = (2^31 / 215) MHz +// From above equation, addend register = 2^63 / (84M * 215) ~= 510707200 = 0x1E70C600 + + + +static void mdio_write(uint8_t addr, uint8_t reg, uint16_t val) +{ + + EMAC->MIIMDAT = val; + EMAC->MIIMCTL = (addr << EMAC_MIIMCTL_PHYADDR_Pos) | reg | EMAC_MIIMCTL_BUSY_Msk | EMAC_MIIMCTL_WRITE_Msk | EMAC_MIIMCTL_MDCON_Msk; + + while (EMAC->MIIMCTL & EMAC_MIIMCTL_BUSY_Msk); + +} + + +static uint16_t mdio_read(uint8_t addr, uint8_t reg) +{ + EMAC->MIIMCTL = (addr << EMAC_MIIMCTL_PHYADDR_Pos) | reg | EMAC_MIIMCTL_BUSY_Msk | EMAC_MIIMCTL_MDCON_Msk; + while (EMAC->MIIMCTL & EMAC_MIIMCTL_BUSY_Msk); + + return(EMAC->MIIMDAT); +} + +static int reset_phy(void) +{ + + uint16_t reg; + uint32_t delayCnt; + + + mdio_write(CONFIG_PHY_ADDR, MII_BMCR, BMCR_RESET); + + delayCnt = 2000; + while(delayCnt-- > 0) { + if((mdio_read(CONFIG_PHY_ADDR, MII_BMCR) & BMCR_RESET) == 0) + break; + + } + + if(delayCnt == 0) { + NU_DEBUGF(("Reset phy failed\n")); + return(-1); + } + + mdio_write(CONFIG_PHY_ADDR, MII_ADVERTISE, ADVERTISE_CSMA | + ADVERTISE_10HALF | + ADVERTISE_10FULL | + ADVERTISE_100HALF | + ADVERTISE_100FULL); + + reg = mdio_read(CONFIG_PHY_ADDR, MII_BMCR); + mdio_write(CONFIG_PHY_ADDR, MII_BMCR, reg | BMCR_ANRESTART); + + delayCnt = 200000; + while(delayCnt-- > 0) { + if((mdio_read(CONFIG_PHY_ADDR, MII_BMSR) & (BMSR_ANEGCOMPLETE | BMSR_LSTATUS)) + == (BMSR_ANEGCOMPLETE | BMSR_LSTATUS)) + break; + } + + if(delayCnt == 0) { + NU_DEBUGF(("AN failed. Set to 100 FULL\n")); + EMAC->CTL |= (EMAC_CTL_OPMODE_Msk | EMAC_CTL_FUDUP_Msk); + return(-1); + } else { + reg = mdio_read(CONFIG_PHY_ADDR, MII_LPA); + + if(reg & ADVERTISE_100FULL) { + NU_DEBUGF(("100 full\n")); + EMAC->CTL |= (EMAC_CTL_OPMODE_Msk | EMAC_CTL_FUDUP_Msk); + } else if(reg & ADVERTISE_100HALF) { + NU_DEBUGF(("100 half\n")); + EMAC->CTL = (EMAC->CTL & ~EMAC_CTL_FUDUP_Msk) | EMAC_CTL_OPMODE_Msk; + } else if(reg & ADVERTISE_10FULL) { + NU_DEBUGF(("10 full\n")); + EMAC->CTL = (EMAC->CTL & ~EMAC_CTL_OPMODE_Msk) | EMAC_CTL_FUDUP_Msk; + } else { + NU_DEBUGF(("10 half\n")); + EMAC->CTL &= ~(EMAC_CTL_OPMODE_Msk | EMAC_CTL_FUDUP_Msk); + } + } + FreeRTOS_printf(("PHY ID 1:0x%x\r\n", mdio_read(CONFIG_PHY_ADDR, MII_PHYSID1))); + FreeRTOS_printf(("PHY ID 2:0x%x\r\n", mdio_read(CONFIG_PHY_ADDR, MII_PHYSID2))); + + return(0); +} + + +static void init_tx_desc(void) +{ + uint32_t i; + + + cur_tx_desc_ptr = fin_tx_desc_ptr = &tx_desc[0]; + + for(i = 0; i < TX_DESCRIPTOR_NUM; i++) { + tx_desc[i].status1 = TXFD_PADEN | TXFD_CRCAPP | TXFD_INTEN; + tx_desc[i].buf = &tx_buf[i][0]; + tx_desc[i].status2 = 0; + tx_desc[i].next = &tx_desc[(i + 1) % TX_DESCRIPTOR_NUM]; + + } + EMAC->TXDSA = (unsigned int)&tx_desc[0]; + return; +} + +static void init_rx_desc(void) +{ + uint32_t i; + + + cur_rx_desc_ptr = &rx_desc[0]; + + for(i = 0; i < RX_DESCRIPTOR_NUM; i++) { + rx_desc[i].status1 = OWNERSHIP_EMAC; + rx_desc[i].buf = &rx_buf[i][0]; + rx_desc[i].status2 = 0; + rx_desc[i].next = &rx_desc[(i + 1) % TX_DESCRIPTOR_NUM]; + } + EMAC->RXDSA = (unsigned int)&rx_desc[0]; + return; +} + +void numaker_set_mac_addr(uint8_t *addr) +{ + + EMAC->CAM0M = (addr[0] << 24) | + (addr[1] << 16) | + (addr[2] << 8) | + addr[3]; + + EMAC->CAM0L = (addr[4] << 24) | + (addr[5] << 16); + + +} + +static void __eth_clk_pin_init() +{ + /* Unlock protected registers */ + SYS_UnlockReg(); + + /* Enable IP clock */ + CLK_EnableModuleClock(EMAC_MODULE); + + // Configure MDC clock rate to HCLK / (127 + 1) = 1.25 MHz if system is running at 160 MH + CLK_SetModuleClock(EMAC_MODULE, 0, CLK_CLKDIV3_EMAC(127)); + + /* Update System Core Clock */ + SystemCoreClockUpdate(); + + /*---------------------------------------------------------------------------------------------------------*/ + /* Init I/O Multi-function */ + /*---------------------------------------------------------------------------------------------------------*/ + // Configure RMII pins + SYS->GPA_MFPL &= ~(SYS_GPA_MFPL_PA6MFP_Msk | SYS_GPA_MFPL_PA7MFP_Msk); + SYS->GPA_MFPL |= SYS_GPA_MFPL_PA6MFP_EMAC_RMII_RXERR | SYS_GPA_MFPL_PA7MFP_EMAC_RMII_CRSDV; + SYS->GPC_MFPL &= ~(SYS_GPC_MFPL_PC6MFP_Msk | SYS_GPC_MFPL_PC7MFP_Msk); + SYS->GPC_MFPL |= SYS_GPC_MFPL_PC6MFP_EMAC_RMII_RXD1 | SYS_GPC_MFPL_PC7MFP_EMAC_RMII_RXD0; + SYS->GPC_MFPH &= ~SYS_GPC_MFPH_PC8MFP_Msk; + SYS->GPC_MFPH |= SYS_GPC_MFPH_PC8MFP_EMAC_RMII_REFCLK; + SYS->GPE_MFPH &= ~(SYS_GPE_MFPH_PE8MFP_Msk | SYS_GPE_MFPH_PE9MFP_Msk | SYS_GPE_MFPH_PE10MFP_Msk | + SYS_GPE_MFPH_PE11MFP_Msk | SYS_GPE_MFPH_PE12MFP_Msk); + SYS->GPE_MFPH |= SYS_GPE_MFPH_PE8MFP_EMAC_RMII_MDC | + SYS_GPE_MFPH_PE9MFP_EMAC_RMII_MDIO | + SYS_GPE_MFPH_PE10MFP_EMAC_RMII_TXD0 | + SYS_GPE_MFPH_PE11MFP_EMAC_RMII_TXD1 | + SYS_GPE_MFPH_PE12MFP_EMAC_RMII_TXEN; + + // Enable high slew rate on all RMII TX output pins + PE->SLEWCTL = (GPIO_SLEWCTL_HIGH << GPIO_SLEWCTL_HSREN10_Pos) | + (GPIO_SLEWCTL_HIGH << GPIO_SLEWCTL_HSREN11_Pos) | + (GPIO_SLEWCTL_HIGH << GPIO_SLEWCTL_HSREN12_Pos); + + + /* Lock protected registers */ + SYS_LockReg(); + + +} + +int numaker_eth_init(uint8_t *mac_addr) +{ + int ret = 0; + // init CLK & pins + __eth_clk_pin_init(); + + // Reset MAC + EMAC->CTL = EMAC_CTL_RST_Msk; + while(EMAC->CTL & EMAC_CTL_RST_Msk) {} + + init_tx_desc(); + init_rx_desc(); + + numaker_set_mac_addr(mac_addr); // need to reconfigure hardware address 'cos we just RESET emc... + + + /* Configure the MAC interrupt enable register. */ + EMAC->INTEN = EMAC_INTEN_RXIEN_Msk | + EMAC_INTEN_TXIEN_Msk | + EMAC_INTEN_RXGDIEN_Msk | + EMAC_INTEN_TXCPIEN_Msk | + EMAC_INTEN_RXBEIEN_Msk | + EMAC_INTEN_TXBEIEN_Msk | + EMAC_INTEN_RDUIEN_Msk | + EMAC_INTEN_TSALMIEN_Msk | + EMAC_INTEN_WOLIEN_Msk; + + /* Configure the MAC control register. */ + EMAC->CTL = EMAC_CTL_STRIPCRC_Msk | EMAC_CTL_RMIIEN_Msk; + + /* Accept packets for us and all broadcast and multicast packets */ + EMAC->CAMCTL = EMAC_CAMCTL_CMPEN_Msk | + EMAC_CAMCTL_AMP_Msk | + EMAC_CAMCTL_ABP_Msk; + EMAC->CAMEN = 1; // Enable CAM entry 0 + + ret= reset_phy(); + + EMAC_ENABLE_RX(); + EMAC_ENABLE_TX(); + return ret; +} + + + +void ETH_halt(void) +{ + + EMAC->CTL &= ~(EMAC_CTL_RXON_Msk | EMAC_CTL_TXON_Msk); +} + +unsigned int m_status; + +void EMAC_RX_IRQHandler(void) +{ +// NU_DEBUGF(("%s ... \r\n", __FUNCTION__)); + m_status = EMAC->INTSTS & 0xFFFF; + EMAC->INTSTS = m_status; + if (m_status & EMAC_INTSTS_RXBEIF_Msk) { + // Shouldn't goes here, unless descriptor corrupted + NU_DEBUGF(("RX descriptor corrupted \r\n")); + //return; + } + // FIX ME: for rx-event, to ack rx_isr into event queue + xNetworkCallback('R'); +} + + +void numaker_eth_trigger_rx(void) +{ + ETH_TRIGGER_RX(); +} + +int numaker_eth_get_rx_buf(uint16_t *len, uint8_t **buf) +{ + unsigned int cur_entry, status; + + cur_entry = EMAC->CRXDSA; + 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 + return -1; + status = cur_rx_desc_ptr->status1; + + if(status & OWNERSHIP_EMAC) + return -1; + + if (status & RXFD_RXGD) { + *buf = cur_rx_desc_ptr->buf; + *len = status & 0xFFFF; + } + return 0; +} + +void numaker_eth_rx_next(void) +{ + cur_rx_desc_ptr->status1 = OWNERSHIP_EMAC; + cur_rx_desc_ptr = cur_rx_desc_ptr->next; +} + +void EMAC_TX_IRQHandler(void) +{ + unsigned int cur_entry, status; + + status = EMAC->INTSTS & 0xFFFF0000; + EMAC->INTSTS = status; + if(status & EMAC_INTSTS_TXBEIF_Msk) { + // Shouldn't goes here, unless descriptor corrupted + return; + } + + cur_entry = EMAC->CTXDSA; + + while (cur_entry != (uint32_t)fin_tx_desc_ptr) { + + fin_tx_desc_ptr = fin_tx_desc_ptr->next; + } + // FIX ME: for tx-event, no-op at this stage + xNetworkCallback('T'); +} + +uint8_t *numaker_eth_get_tx_buf(void) +{ + if(cur_tx_desc_ptr->status1 & OWNERSHIP_EMAC) + return(NULL); + else + return(cur_tx_desc_ptr->buf); +} + +void numaker_eth_trigger_tx(uint16_t length, void *p) +{ + struct eth_descriptor volatile *desc; + cur_tx_desc_ptr->status2 = (unsigned int)length; + desc = cur_tx_desc_ptr->next; // in case TX is transmitting and overwrite next pointer before we can update cur_tx_desc_ptr + cur_tx_desc_ptr->status1 |= OWNERSHIP_EMAC; + cur_tx_desc_ptr = desc; + + ETH_TRIGGER_TX(); + +} + +int numaker_eth_link_ok(void) +{ + /* first, a dummy read to latch */ + mdio_read(CONFIG_PHY_ADDR, MII_BMSR); + if(mdio_read(CONFIG_PHY_ADDR, MII_BMSR) & BMSR_LSTATUS) + return 1; + return 0; +} + +//void numaker_eth_set_cb(eth_callback_t eth_cb, void *userData) +//{ +// nu_eth_txrx_cb = eth_cb; +// nu_userData = userData; +//} + +// Provide ethernet devices with a semi-unique MAC address +void numaker_mac_address(uint8_t *mac) +{ + uint32_t uID1; + // Fetch word 0 + uint32_t word0 = *(uint32_t *)0x7F804; // 2KB Data Flash at 0x7F800 + // Fetch word 1 + // we only want bottom 16 bits of word1 (MAC bits 32-47) + // and bit 9 forced to 1, bit 8 forced to 0 + // Locally administered MAC, reduced conflicts + // http://en.wikipedia.org/wiki/MAC_address + uint32_t word1 = *(uint32_t *)0x7F800; // 2KB Data Flash at 0x7F800 + + if( word0 == 0xFFFFFFFF ) // Not burn any mac address at 1st 2 words of Data Flash + { + // with a semi-unique MAC address from the UUID + /* Enable FMC ISP function */ + SYS_UnlockReg(); + FMC_Open(); + // = FMC_ReadUID(0); + uID1 = FMC_ReadUID(1); + word1 = (uID1 & 0x003FFFFF) | ((uID1 & 0x030000) << 6) >> 8; + word0 = ((FMC_ReadUID(0) >> 4) << 20) | ((uID1 & 0xFF)<<12) | (FMC_ReadUID(2) & 0xFFF); + /* Disable FMC ISP function */ + FMC_Close(); + /* Lock protected registers */ + SYS_LockReg(); + } + + word1 |= 0x00000200; + word1 &= 0x0000FEFF; + + mac[0] = (word1 & 0x0000ff00) >> 8; + mac[1] = (word1 & 0x000000ff); + mac[2] = (word0 & 0xff000000) >> 24; + mac[3] = (word0 & 0x00ff0000) >> 16; + mac[4] = (word0 & 0x0000ff00) >> 8; + mac[5] = (word0 & 0x000000ff); + + NU_DEBUGF(("mac address %02x-%02x-%02x-%02x-%02x-%02x \r\n", mac[0], mac[1],mac[2],mac[3],mac[4],mac[5])); +} + +void numaker_eth_enable_interrupts(void) { + EMAC->INTEN |= EMAC_INTEN_RXIEN_Msk | + EMAC_INTEN_TXIEN_Msk ; + NVIC_EnableIRQ(EMAC_RX_IRQn); + NVIC_EnableIRQ(EMAC_TX_IRQn); +} + +void numaker_eth_disable_interrupts(void) { + NVIC_DisableIRQ(EMAC_RX_IRQn); + NVIC_DisableIRQ(EMAC_TX_IRQn); +}