diff --git a/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/CMakeLists.txt b/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/CMakeLists.txt index 30e39671bd6..d38d9f1f38d 100644 --- a/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/CMakeLists.txt +++ b/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/CMakeLists.txt @@ -9,12 +9,14 @@ elseif("M460" IN_LIST MBED_TARGET_LABELS) add_subdirectory(TARGET_M460) endif() -target_include_directories(mbed-emac - PUBLIC - . -) +if(NOT "M480" IN_LIST MBED_TARGET_LABELS) + target_include_directories(mbed-emac + PUBLIC + . + ) -target_sources(mbed-emac - PRIVATE - numaker_emac.cpp -) + target_sources(mbed-emac + PRIVATE + numaker_emac.cpp + ) +endif() \ No newline at end of file diff --git a/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/TARGET_M480/CMakeLists.txt b/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/TARGET_M480/CMakeLists.txt index cce4f2b40a7..8529c68faa7 100644 --- a/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/TARGET_M480/CMakeLists.txt +++ b/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/TARGET_M480/CMakeLists.txt @@ -8,5 +8,6 @@ target_include_directories(mbed-emac target_sources(mbed-emac PRIVATE - m480_eth.c + NuvotonM480EthMAC.cpp + m480_eth_pins.c ) diff --git a/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/TARGET_M480/NuvotonM480EthMAC.cpp b/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/TARGET_M480/NuvotonM480EthMAC.cpp new file mode 100644 index 00000000000..5215ca34d59 --- /dev/null +++ b/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/TARGET_M480/NuvotonM480EthMAC.cpp @@ -0,0 +1,339 @@ +/* Copyright (c) 2025 Jamie Smith +* SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NuvotonM480EthMAC.h" + +#include +#include + +#include "m480_eth_pins.h" + +namespace mbed { + +void NuvotonM480EthMAC::MACDriver::writeMACAddress(size_t index, MACAddress macAddress) { + // Find the registers to write the MAC into. Sadly they didn't use an array... + volatile uint32_t * highReg = (&base->CAM0M) + 2 * index; + volatile uint32_t * lowReg = (&base->CAM0L) + 2 * index; + + // Write the MAC into the registers. + *highReg = (static_cast(macAddress[0]) << 24) | (static_cast(macAddress[1]) << 16) | (static_cast(macAddress[2]) << 8) | macAddress[3]; + *lowReg = (static_cast(macAddress[4]) << 24) | (static_cast(macAddress[5]) << 16); + + // Mark the address as valid + base->CAMEN |= (1 << index); +} + +CompositeEMAC::ErrCode NuvotonM480EthMAC::MACDriver::init() { + sleep_manager_lock_deep_sleep(); + nu_eth_clk_and_pin_init(); + + // Reset MAC + base->CTL = EMAC_CTL_RST_Msk; + while (base->CTL & EMAC_CTL_RST_Msk) {} + + // Reset class vars + numMulticastSubscriptions = 0; + passAllMcastEnabled = false; + promiscuousEnabled = false; + + /* Configure the MAC interrupt enable register. Note that we need to enable interrupts for all types + * of Rx errors, so that we know when any Rx descriptor has been freed up by the DMA. */ + base->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_CRCEIEN_Msk | + EMAC_INTEN_RXOVIEN_Msk | + EMAC_INTEN_ALIEIEN_Msk | + EMAC_INTEN_RPIEN_Msk | + EMAC_INTEN_MFLEIEN_Msk; + + /* Enable interrupts. */ + NVIC_SetVector(EMAC_RX_IRQn, reinterpret_cast(&NuvotonM480EthMAC::rxIrqHandler)); + NVIC_EnableIRQ(EMAC_RX_IRQn); + NVIC_SetVector(EMAC_TX_IRQn, reinterpret_cast(&NuvotonM480EthMAC::txIrqHandler)); + NVIC_EnableIRQ(EMAC_TX_IRQn); + + /* Configure the MAC control register. */ + base->CTL = EMAC_CTL_STRIPCRC_Msk | EMAC_CTL_RMIIEN_Msk; + + /* Accept broadcast packets without using the address filter */ + base->CAMCTL = EMAC_CAMCTL_CMPEN_Msk | + EMAC_CAMCTL_ABP_Msk; + + // Maximum frame length. + // This apparently includes the CRC, so we need to set this 4 bytes higher than the MTU of 1514 bytes + // or 1514 byte packets get rejected + base->MRFL = 1518; + + /* Set RX FIFO threshold as 8 words */ + base->FIFOCTL = 0x00200100; + + return ErrCode::SUCCESS; +} + +CompositeEMAC::ErrCode NuvotonM480EthMAC::MACDriver::deinit() { + NVIC_DisableIRQ(EMAC_RX_IRQn); + NVIC_DisableIRQ(EMAC_TX_IRQn); + + nu_eth_clk_and_pin_deinit(); + + sleep_manager_unlock_deep_sleep(); + + return ErrCode::SUCCESS; +} + +CompositeEMAC::ErrCode NuvotonM480EthMAC::MACDriver::enable(LinkSpeed speed, Duplex duplex) { + if(speed == LinkSpeed::LINK_100MBIT) { + base->CTL |= EMAC_CTL_OPMODE_Msk; + } + else { + base->CTL &= ~EMAC_CTL_OPMODE_Msk; + } + + if(duplex == Duplex::FULL) { + base->CTL |= EMAC_CTL_FUDUP_Msk; + } + else { + base->CTL &= ~EMAC_CTL_FUDUP_Msk; + } + + base->CTL |= EMAC_CTL_RXON_Msk | EMAC_CTL_TXON_Msk; + + return ErrCode::SUCCESS; +} + +CompositeEMAC::ErrCode NuvotonM480EthMAC::MACDriver::disable() { + base->CTL &= ~(EMAC_CTL_RXON_Msk | EMAC_CTL_TXON_Msk); + + return ErrCode::SUCCESS; +} + +void NuvotonM480EthMAC::MACDriver::setOwnMACAddr(const MACAddress &ownAddress) { + writeMACAddress(0, ownAddress); +} + +CompositeEMAC::ErrCode NuvotonM480EthMAC::MACDriver::mdioRead(uint8_t devAddr, uint8_t regAddr, uint16_t &result) { + base->MIIMCTL = (devAddr << EMAC_MIIMCTL_PHYADDR_Pos) | regAddr | EMAC_MIIMCTL_BUSY_Msk | EMAC_MIIMCTL_MDCON_Msk; + while (base->MIIMCTL & EMAC_MIIMCTL_BUSY_Msk); + result = base->MIIMDAT; + + return ErrCode::SUCCESS; +} + +CompositeEMAC::ErrCode NuvotonM480EthMAC::MACDriver::mdioWrite(uint8_t devAddr, uint8_t regAddr, uint16_t data) { + base->MIIMDAT = data; + base->MIIMCTL = (devAddr << EMAC_MIIMCTL_PHYADDR_Pos) | regAddr | EMAC_MIIMCTL_BUSY_Msk | EMAC_MIIMCTL_WRITE_Msk | EMAC_MIIMCTL_MDCON_Msk; + + while (base->MIIMCTL & EMAC_MIIMCTL_BUSY_Msk); + return ErrCode::SUCCESS; +} + +PinName NuvotonM480EthMAC::MACDriver::getPhyResetPin() { + return nu_eth_get_phy_reset_pin(); +} + +CompositeEMAC::ErrCode NuvotonM480EthMAC::MACDriver::addMcastMAC(MACAddress mac) { + if(numMulticastSubscriptions >= 14) { + // 14 is the max we can handle in hardware + return ErrCode::OUT_OF_MEMORY; + } + // We use MAC slots 1 through 14 for the multicast subscriptions + ++numMulticastSubscriptions; + writeMACAddress(numMulticastSubscriptions, mac); + + return ErrCode::SUCCESS; +} + +CompositeEMAC::ErrCode NuvotonM480EthMAC::MACDriver::clearMcastFilter() { + // Disable all MAC addresses except CAM0, which is our own unicast MAC + base->CAMEN = 1; + + return ErrCode::SUCCESS; +} + +void NuvotonM480EthMAC::MACDriver::setPassAllMcast(bool pass) { + passAllMcastEnabled = pass; + if(pass) { + base->CAMCTL |= EMAC_CAMCTL_AMP_Msk; + } + else if(!promiscuousEnabled){ + base->CAMCTL &= ~EMAC_CAMCTL_AMP_Msk; + } +} + +void NuvotonM480EthMAC::MACDriver::setPromiscuous(bool enable) { + promiscuousEnabled = enable; + + // To enable promiscuous mode on this MAC, we need to enable pass all multicast and pass all unicast. + if(enable) { + base->CAMCTL |= EMAC_CAMCTL_AMP_Msk | EMAC_CAMCTL_AUP_Msk; + } + else { + base->CAMCTL &= ~EMAC_CAMCTL_AUP_Msk; + + // Only disable the AMP bit if we aren't in pass-all-mcast mode + if(!passAllMcastEnabled) { + base->CAMCTL &= ~EMAC_CAMCTL_AMP_Msk; + } + } +} + +void NuvotonM480EthMAC::TxDMA::startDMA() { + // Set linked list base address + base->TXDSA = reinterpret_cast(&txDescs[0]); +} + +void NuvotonM480EthMAC::TxDMA::stopDMA() { + // No specific disable for DMA. DMA will get disabled when the MAC is disabled. +} + +bool NuvotonM480EthMAC::TxDMA::descOwnedByDMA(size_t descIdx) { + return txDescs[descIdx].EMAC_OWN; +} + +bool NuvotonM480EthMAC::TxDMA::isDMAReadableBuffer(uint8_t const *start, size_t size) const { + // No restrictions on what DMA can read + return true; +} + +void NuvotonM480EthMAC::TxDMA::giveToDMA(size_t descIdx, uint8_t const *buffer, size_t len, bool firstDesc, + bool lastDesc) { + + // Populate Tx descriptor fields + txDescs[descIdx].PADEN = true; + txDescs[descIdx].CRCAPP = true; + txDescs[descIdx].INTEN = true; + txDescs[descIdx].TXBSA = buffer; + txDescs[descIdx].TBC = len; + txDescs[descIdx].NTXDSA = &txDescs[(descIdx + 1) % TX_NUM_DESCS]; + + // Give to DMA + txDescs[descIdx].EMAC_OWN = true; + + // Tell DMA to start writing if stopped + base->TXST = 1; +} + +void NuvotonM480EthMAC::RxDMA::startDMA() { + // Set linked list base address + base->RXDSA = reinterpret_cast(&rxDescs[0]); +} + +void NuvotonM480EthMAC::RxDMA::stopDMA() { + // No specific disable for DMA. DMA will get disabled when the MAC is disabled. +} + +bool NuvotonM480EthMAC::RxDMA::descOwnedByDMA(size_t descIdx) { + return rxDescs[descIdx].EMAC_OWN; +} + +// The M480 EMAC enforces a 1:1 descriptor to packet relationship, so every desc is always a first and last desc. +bool NuvotonM480EthMAC::RxDMA::isFirstDesc(size_t descIdx) { + return true; +} +bool NuvotonM480EthMAC::RxDMA::isLastDesc(size_t descIdx) { + return true; +} + +bool NuvotonM480EthMAC::RxDMA::isErrorDesc(size_t descIdx) { + // If it's not a good frame, then it's an error. + return !(rxDescs[descIdx].RXGDIF); +} + +void NuvotonM480EthMAC::RxDMA::returnDescriptor(size_t descIdx, uint8_t *buffer) { + // Populate descriptor + rxDescs[descIdx].RXBSA = buffer; + rxDescs[descIdx].NRXDSA = &rxDescs[(descIdx + 1) % RX_NUM_DESCS]; + + // Give to DMA + rxDescs[descIdx].EMAC_OWN = true; + + // Tell DMA to start receiving if stopped + base->RXST = 1; +} + +size_t NuvotonM480EthMAC::RxDMA::getTotalLen(size_t firstDescIdx, size_t lastDescIdx) { + return rxDescs[firstDescIdx].RBC; +} + +NuvotonM480EthMAC * NuvotonM480EthMAC::instance = nullptr; + +NuvotonM480EthMAC::NuvotonM480EthMAC(): +CompositeEMAC(txDMA, rxDMA, macDriver), +// Note: we can't use the "EMAC" symbol directly because it conflicts with the EMAC class. So we have to +// use the integer address and cast it instead. +base(reinterpret_cast(EMAC_BASE)), +txDMA(base), +rxDMA(base), +macDriver(base) +{ + instance = this; +} + +void NuvotonM480EthMAC::txIrqHandler() { + const auto base = instance->base; + if(base->INTSTS & EMAC_INTSTS_TXBEIF_Msk) { + MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_ETHERNET, EIO), \ + "M480 EMAC: Hardware reports fatal DMA Tx bus error\n"); + } + + if(base->INTSTS & EMAC_INTSTS_TXCPIF_Msk) { + // Transmission complete + instance->txISR(); + + // Clear flag + base->INTSTS = EMAC_INTSTS_TXCPIF_Msk; + } + + // Clear general Tx interrupt flag + base->INTSTS = EMAC_INTSTS_TXIF_Msk; +} + +void NuvotonM480EthMAC::rxIrqHandler() { + const auto base = instance->base; + if(base->INTSTS & EMAC_INTSTS_RXBEIF_Msk) { + MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_ETHERNET, EIO), \ + "M480 EMAC: Hardware reports fatal DMA Rx bus error\n"); + } + + if(base->INTSTS & EMAC_INTSTS_RXIF_Msk) { + // Frames(s) received (good or otherwise) + instance->rxISR(); + + // Clear flags + base->INTSTS = EMAC_INTSTS_RXIF_Msk | + EMAC_INTSTS_CRCEIF_Msk | + EMAC_INTSTS_RXOVIF_Msk | + EMAC_INTSTS_LPIF_Msk | + EMAC_INTSTS_RXGDIF_Msk | + EMAC_INTSTS_RPIF_Msk | + EMAC_INTSTS_MFLEIF_Msk; + } + + // Clear general Tx interrupt flag + base->INTSTS = EMAC_INTSTS_TXIF_Msk; +} +} + +// Provide default EMAC driver +MBED_WEAK EMAC &EMAC::get_default_instance() +{ + static mbed::NuvotonM480EthMAC emac; + return emac; +} \ No newline at end of file diff --git a/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/TARGET_M480/NuvotonM480EthMAC.h b/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/TARGET_M480/NuvotonM480EthMAC.h new file mode 100644 index 00000000000..bfcade3003e --- /dev/null +++ b/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/TARGET_M480/NuvotonM480EthMAC.h @@ -0,0 +1,142 @@ +/* Copyright (c) 2025 Jamie Smith + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NUVOTONM480ETHMAC_H +#define NUVOTONM480ETHMAC_H + +#include "CompositeEMAC.h" +#include "GenericEthDMA.h" +#include "m480_eth_descriptors.h" + +namespace mbed +{ + +class NuvotonM480EthMAC : public CompositeEMAC +{ + class MACDriver : public CompositeEMAC::MACDriver + { + EMAC_T * const base; + + /// Number of multicast MACs we are currently subscribed to + size_t numMulticastSubscriptions = 0; + + bool passAllMcastEnabled = false; + bool promiscuousEnabled = false; + + /// Write a MAC address into the CAM (mac filter) registers + void writeMACAddress(size_t index, MACAddress macAddress); + + public: + explicit MACDriver(EMAC_T * const base): + base(base) + {} + + ErrCode init() override; + + ErrCode deinit() override; + + ErrCode enable(LinkSpeed speed, Duplex duplex) override; + + ErrCode disable() override; + + void setOwnMACAddr(const MACAddress &ownAddress) override; + + ErrCode mdioRead(uint8_t devAddr, uint8_t regAddr, uint16_t &result) override; + + ErrCode mdioWrite(uint8_t devAddr, uint8_t regAddr, uint16_t data) override; + + PinName getPhyResetPin() override; + + ErrCode addMcastMAC(MACAddress mac) override; + + ErrCode clearMcastFilter() override; + + void setPassAllMcast(bool pass) override; + + void setPromiscuous(bool enable) override; + }; + + class TxDMA : public GenericTxDMARing + { + protected: + EMAC_T * const base; // Base address of Ethernet peripheral + volatile M480EthTxDescriptor txDescs[TX_NUM_DESCS]{}; // Tx descriptors + + void startDMA() override; + + void stopDMA() override; + + bool descOwnedByDMA(size_t descIdx) override; + + bool isDMAReadableBuffer(uint8_t const * start, size_t size) const override; + + void giveToDMA(size_t descIdx, uint8_t const * buffer, size_t len, bool firstDesc, bool lastDesc) override; + public: + explicit TxDMA(EMAC_T * const base): + GenericTxDMARing(0, false), // we do NOT support multiple descriptors in the hardware + base(base) + {} + }; + + class RxDMA : public GenericRxDMARing { + protected: + EMAC_T * const base; // Base address of Ethernet peripheral + volatile M480EthRxDescriptor rxDescs[RX_NUM_DESCS]{}; // Rx descriptors + + void startDMA() override; + + void stopDMA() override; + + bool descOwnedByDMA(size_t descIdx) override; + + bool isFirstDesc(size_t descIdx) override; + + bool isLastDesc(size_t descIdx) override; + + bool isErrorDesc(size_t descIdx) override; + + void returnDescriptor(size_t descIdx, uint8_t * buffer) override; + + size_t getTotalLen(size_t firstDescIdx, size_t lastDescIdx) override; + + public: + explicit RxDMA(EMAC_T * const base): + base(base) + {} + }; + + // Pointer to global instance, for ISR to use. + // TODO if we support more than 1 EMAC per MCU, this will need to be an array + static NuvotonM480EthMAC * instance; + + EMAC_T * const base; // Base address of Ethernet peripheral + + // Components of the ethernet MAC + TxDMA txDMA; + RxDMA rxDMA; + MACDriver macDriver; + +public: + NuvotonM480EthMAC(); + + // Interrupt callbacks + static void txIrqHandler(); + static void rxIrqHandler(); +}; + +} + +#endif //NUVOTONM480ETHMAC_H diff --git a/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/TARGET_M480/m480_eth.c b/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/TARGET_M480/m480_eth.c deleted file mode 100644 index 30de209f81c..00000000000 --- a/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/TARGET_M480/m480_eth.c +++ /dev/null @@ -1,498 +0,0 @@ -/* - * Copyright (c) 2018 Nuvoton Technology Corp. - * Copyright (c) 2018 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Description: M480 MAC driver source file - */ -#include -#include "m480_eth.h" -#include "mbed_toolchain.h" -#include "mbed_interface.h" -//#define NU_TRACE -#include "numaker_eth_hal.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_Msk;}while(0) -#define ETH_DISABLE_TX() do{EMAC->CTL &= ~EMAC_CTL_TXON;}while(0) -#define ETH_DISABLE_RX() do{EMAC->CTL &= ~EMAC_CTL_RXON_Msk;}while(0) - -#define EMAC_ENABLE_INT(emac, u32eIntSel) ((emac)->INTEN |= (u32eIntSel)) -#define EMAC_DISABLE_INT(emac, u32eIntSel) ((emac)->INTEN &= ~ (u32eIntSel)) - -MBED_ALIGN(4) struct eth_descriptor rx_desc[RX_DESCRIPTOR_NUM]; -MBED_ALIGN(4) struct eth_descriptor tx_desc[TX_DESCRIPTOR_NUM]; - -struct eth_descriptor volatile *cur_tx_desc_ptr, *cur_rx_desc_ptr, *fin_tx_desc_ptr; - -__attribute__((section("EMAC_RAM"))) -MBED_ALIGN(4) uint8_t rx_buf[RX_DESCRIPTOR_NUM][PACKET_BUFFER_SIZE]; -__attribute__((section("EMAC_RAM"))) -MBED_ALIGN(4) uint8_t tx_buf[TX_DESCRIPTOR_NUM][PACKET_BUFFER_SIZE]; - -eth_callback_t nu_eth_txrx_cb = NULL; -void *nu_userData = NULL; - -extern void ack_emac_rx_isr(void); - -static bool isPhyReset = false; -static uint16_t phyLPAval = 0; - -// 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) { - delayCnt--; - 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) { - delayCnt--; - 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); - phyLPAval = reg; - - 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); - } - } - printf("PHY ID 1:0x%x\r\n", mdio_read(CONFIG_PHY_ADDR, MII_PHYSID1)); - 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) % RX_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(); - - -} - -void numaker_eth_init(uint8_t *mac_addr) -{ - - // 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 - /* Limit the max receive frame length to 1514 + 4 */ - EMAC->MRFL = NU_ETH_MAX_FLEN; - - /* Set RX FIFO threshold as 8 words */ - EMAC->FIFOCTL = 0x00200100; - - if (isPhyReset != true) { - if (!reset_phy()) { - isPhyReset = true; - } - } else { - if (phyLPAval & ADVERTISE_100FULL) { - NU_DEBUGF(("100 full\n")); - EMAC->CTL |= (EMAC_CTL_OPMODE_Msk | EMAC_CTL_FUDUP_Msk); - } else if (phyLPAval & ADVERTISE_100HALF) { - NU_DEBUGF(("100 half\n")); - EMAC->CTL = (EMAC->CTL & ~EMAC_CTL_FUDUP_Msk) | EMAC_CTL_OPMODE_Msk; - } else if (phyLPAval & 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); - } - } - - EMAC_ENABLE_RX(); - EMAC_ENABLE_TX(); - -} - - - -void ETH_halt(void) -{ - - EMAC->CTL &= ~(EMAC_CTL_RXON_Msk | EMAC_CTL_TXON_Msk); -} - -unsigned int m_status; - -void EMAC_RX_IRQHandler(void) -{ - m_status = EMAC->INTSTS & 0xFFFF; - EMAC->INTSTS = m_status; - if (m_status & EMAC_INTSTS_RXBEIF_Msk) { - // Shouldn't goes here, unless descriptor corrupted - mbed_error_printf("### RX Bus error [0x%x]\r\n", m_status); - if (nu_eth_txrx_cb != NULL) { - nu_eth_txrx_cb('B', nu_userData); - } - return; - } - EMAC_DISABLE_INT(EMAC, (EMAC_INTEN_RDUIEN_Msk | EMAC_INTEN_RXGDIEN_Msk)); - if (nu_eth_txrx_cb != NULL) { - nu_eth_txrx_cb('R', nu_userData); - } -} - - -void numaker_eth_trigger_rx(void) -{ - EMAC_ENABLE_INT(EMAC, (EMAC_INTEN_RDUIEN_Msk | EMAC_INTEN_RXGDIEN_Msk)); - 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; - // length of payload should be <= 1514 - if (*len > (NU_ETH_MAX_FLEN - 4)) { - NU_DEBUGF(("%s... unexpected long packet length=%d, buf=0x%x\r\n", __FUNCTION__, *len, *buf)); - *len = 0; // Skip this unexpected long packet - } - if (*len == (NU_ETH_MAX_FLEN - 4)) { - NU_DEBUGF(("%s... length=%d, buf=0x%x\r\n", __FUNCTION__, *len, *buf)); - } - } - 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 - mbed_error_printf("### TX Bus error [0x%x]\r\n", status); - if (nu_eth_txrx_cb != NULL) { - nu_eth_txrx_cb('B', nu_userData); - } - return; - } - - cur_entry = EMAC->CTXDSA; - - while (cur_entry != (uint32_t)fin_tx_desc_ptr) { - - fin_tx_desc_ptr = fin_tx_desc_ptr->next; - } - - if (nu_eth_txrx_cb != NULL) { - nu_eth_txrx_cb('T', nu_userData); - } -} - -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; -} - -// Override mbed_mac_address of mbed_interface.c to provide ethernet devices with a semi-unique MAC address -void mbed_mac_address(char *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); -} diff --git a/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/TARGET_M480/m480_eth.h b/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/TARGET_M480/m480_eth.h deleted file mode 100644 index 2866516567b..00000000000 --- a/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/TARGET_M480/m480_eth.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (c) 2018 Nuvoton Technology Corp. - * Copyright (c) 2018 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Description: M480 EMAC driver header file - */ - -#include "numaker_emac_config.h" -#include "M480.h" -#ifndef _M480_ETH_ -#define _M480_ETH_ - -/* Generic MII registers. */ - -#define MII_BMCR 0x00 /* Basic mode control register */ -#define MII_BMSR 0x01 /* Basic mode status register */ -#define MII_PHYSID1 0x02 /* PHYS ID 1 */ -#define MII_PHYSID2 0x03 /* PHYS ID 2 */ -#define MII_ADVERTISE 0x04 /* Advertisement control reg */ -#define MII_LPA 0x05 /* Link partner ability reg */ -#define MII_EXPANSION 0x06 /* Expansion register */ -#define MII_DCOUNTER 0x12 /* Disconnect counter */ -#define MII_FCSCOUNTER 0x13 /* False carrier counter */ -#define MII_NWAYTEST 0x14 /* N-way auto-neg test reg */ -#define MII_RERRCOUNTER 0x15 /* Receive error counter */ -#define MII_SREVISION 0x16 /* Silicon revision */ -#define MII_RESV1 0x17 /* Reserved... */ -#define MII_LBRERROR 0x18 /* Lpback, rx, bypass error */ -#define MII_PHYADDR 0x19 /* PHY address */ -#define MII_RESV2 0x1a /* Reserved... */ -#define MII_TPISTATUS 0x1b /* TPI status for 10mbps */ -#define MII_NCONFIG 0x1c /* Network interface config */ - -/* Basic mode control register. */ -#define BMCR_RESV 0x007f /* Unused... */ -#define BMCR_CTST 0x0080 /* Collision test */ -#define BMCR_FULLDPLX 0x0100 /* Full duplex */ -#define BMCR_ANRESTART 0x0200 /* Auto negotiation restart */ -#define BMCR_ISOLATE 0x0400 /* Disconnect DP83840 from MII */ -#define BMCR_PDOWN 0x0800 /* Powerdown the DP83840 */ -#define BMCR_ANENABLE 0x1000 /* Enable auto negotiation */ -#define BMCR_SPEED100 0x2000 /* Select 100Mbps */ -#define BMCR_LOOPBACK 0x4000 /* TXD loopback bits */ -#define BMCR_RESET 0x8000 /* Reset the DP83840 */ - -/* Basic mode status register. */ -#define BMSR_ERCAP 0x0001 /* Ext-reg capability */ -#define BMSR_JCD 0x0002 /* Jabber detected */ -#define BMSR_LSTATUS 0x0004 /* Link status */ -#define BMSR_ANEGCAPABLE 0x0008 /* Able to do auto-negotiation */ -#define BMSR_RFAULT 0x0010 /* Remote fault detected */ -#define BMSR_ANEGCOMPLETE 0x0020 /* Auto-negotiation complete */ -#define BMSR_RESV 0x07c0 /* Unused... */ -#define BMSR_10HALF 0x0800 /* Can do 10mbps, half-duplex */ -#define BMSR_10FULL 0x1000 /* Can do 10mbps, full-duplex */ -#define BMSR_100HALF 0x2000 /* Can do 100mbps, half-duplex */ -#define BMSR_100FULL 0x4000 /* Can do 100mbps, full-duplex */ -#define BMSR_100BASE4 0x8000 /* Can do 100mbps, 4k packets */ - -/* Advertisement control register. */ -#define ADVERTISE_SLCT 0x001f /* Selector bits */ -#define ADVERTISE_CSMA 0x0001 /* Only selector supported */ -#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */ -#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */ -#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */ -#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */ -#define ADVERTISE_100BASE4 0x0200 /* Try for 100mbps 4k packets */ -#define ADVERTISE_RESV 0x1c00 /* Unused... */ -#define ADVERTISE_RFAULT 0x2000 /* Say we can detect faults */ -#define ADVERTISE_LPACK 0x4000 /* Ack link partners response */ -#define ADVERTISE_NPAGE 0x8000 /* Next page bit */ - -#define RX_DESCRIPTOR_NUM NU_RX_RING_LEN//4 //2 // 4: Max Number of Rx Frame Descriptors -#define TX_DESCRIPTOR_NUM NU_TX_RING_LEN//4 //2 // 4: Max number of Tx Frame Descriptors - -#define PACKET_BUFFER_SIZE ( NU_ETH_MAX_FLEN + ((NU_ETH_MAX_FLEN%4) ? (4 - (NU_ETH_MAX_FLEN%4)) : 0) ) //For DMA 4 bytes alignment - -#define CONFIG_PHY_ADDR 1 - - -// Frame Descriptor's Owner bit -#define OWNERSHIP_EMAC 0x80000000 // 1 = EMAC -//#define OWNERSHIP_CPU 0x7fffffff // 0 = CPU - - - -// Rx Frame Descriptor Status -#define RXFD_RXGD 0x00100000 // Receiving Good Packet Received -#define RXFD_RTSAS 0x00800000 // RX Time Stamp Available - - -// Tx Frame Descriptor's Control bits -#define TXFD_TTSEN 0x08 // Tx Time Stamp Enable -#define TXFD_INTEN 0x04 // Interrupt Enable -#define TXFD_CRCAPP 0x02 // Append CRC -#define TXFD_PADEN 0x01 // Padding Enable - -// Tx Frame Descriptor Status -#define TXFD_TXCP 0x00080000 // Transmission Completion -#define TXFD_TTSAS 0x08000000 // TX Time Stamp Available - -// Tx/Rx buffer descriptor structure -struct eth_descriptor; -struct eth_descriptor { - uint32_t status1; - uint8_t *buf; - uint32_t status2; - struct eth_descriptor *next; -#ifdef TIME_STAMPING - uint32_t backup1; - uint32_t backup2; - uint32_t reserved1; - uint32_t reserved2; -#endif -}; - -#ifdef TIME_STAMPING - -#define ETH_TS_ENABLE() do{EMAC->TSCTL = EMAC_TSCTL_TSEN_Msk;}while(0) -#define ETH_TS_START() do{EMAC->TSCTL |= (EMAC_TSCTL_TSMODE_Msk | EMAC_TSCTL_TSIEN_Msk);}while(0) -s32_t ETH_settime(u32_t sec, u32_t nsec); -s32_t ETH_gettime(u32_t *sec, u32_t *nsec); -s32_t ETH_updatetime(u32_t neg, u32_t sec, u32_t nsec); -s32_t ETH_adjtimex(int ppm); -void ETH_setinc(void); - -#endif - -#endif /* _M480_ETH_ */ diff --git a/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/TARGET_M480/m480_eth_descriptors.h b/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/TARGET_M480/m480_eth_descriptors.h new file mode 100644 index 00000000000..f8fd85b5e38 --- /dev/null +++ b/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/TARGET_M480/m480_eth_descriptors.h @@ -0,0 +1,96 @@ +/* Copyright (c) 2025 Jamie Smith +* SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef M480_ETH_DESCRIPTORS_H +#define M480_ETH_DESCRIPTORS_H + +namespace mbed { +/// Struct for a Tx descriptor in the M480 EMAC. +/// See M480 TRM page 1263. +struct __attribute__((packed, aligned(4))) M480EthTxDescriptor +{ + // TXDES0 fields + bool PADEN : 1; + bool CRCAPP : 1; + bool INTEN : 1; + bool TTSEN : 1; + uint32_t : 27; + bool EMAC_OWN : 1; + + // TXDES1 fields + union { + uint8_t const * TXBSA; + uint32_t TSSUBSEC; + }; + + // TXDES2 fields + uint16_t TBC; + bool TXIF : 1; + bool DEF : 1; + bool : 1; + bool TXCPIF : 1; + bool EXDEFIF : 1; + bool NCSIF : 1; + bool TXABTIF : 1; + bool LCIF : 1; + bool TXHALT : 1; + bool TXPAUSED : 1; + bool SQE : 1; + bool TTSAS : 1; + uint8_t COLCNT : 4; + + // TXDES3 fields + union { + M480EthTxDescriptor volatile * NTXDSA; + uint32_t TSSEC; + }; +}; + +/// Struct for an Rx descriptor in the M480 EMAC. +/// See M480 TRM page 1257. +struct __attribute__((packed, aligned(4))) M480EthRxDescriptor +{ + // RXDES0 fields + uint16_t RBC; + bool RXIF : 1; + bool CRCEIF : 1; + bool : 1; + bool LPIF : 1; + bool RXGDIF : 1; + bool ALIEIF : 1; + bool RPIF : 1; + bool RTSAS : 1; + uint8_t : 7; + bool EMAC_OWN : 1; + + // RXDES1 fields + union { + uint8_t * RXBSA; + uint32_t TSSUBSEC; + }; + + // RXDES2 fields + uint32_t : 32; + + // RXDES3 fields + union { + M480EthRxDescriptor volatile * NRXDSA; + uint32_t TSSEC; + }; +}; +} + +#endif //M480_ETH_DESCRIPTORS_H diff --git a/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/TARGET_M480/m480_eth_pins.c b/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/TARGET_M480/m480_eth_pins.c new file mode 100644 index 00000000000..91536a35f57 --- /dev/null +++ b/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/TARGET_M480/m480_eth_pins.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2018 Nuvoton Technology Corp. + * Copyright (c) 2018 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Description: M480 EMAC driver header file + */ + +#include "m480_eth_pins.h" + +#include +#include +#include +#include + +void nu_eth_clk_and_pin_init(void) +{ + /* 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 MHz + 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(); +} + +void nu_eth_clk_and_pin_deinit(void) { + /* Unlock protected registers */ + SYS_UnlockReg(); + + /* Disable IP clock */ + CLK_DisableModuleClock(EMAC_MODULE); + + // Reset all functions back to GPIO + SYS->GPA_MFPL &= ~(SYS_GPA_MFPL_PA6MFP_Msk | SYS_GPA_MFPL_PA7MFP_Msk); + SYS->GPC_MFPL &= ~(SYS_GPC_MFPL_PC6MFP_Msk | SYS_GPC_MFPL_PC7MFP_Msk); + SYS->GPC_MFPH &= ~SYS_GPC_MFPH_PC8MFP_Msk; + 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); + + /* Lock protected registers */ + SYS_LockReg(); +} + +PinName nu_eth_get_phy_reset_pin(void) { + return NC; +} diff --git a/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/TARGET_M480/m480_eth_pins.h b/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/TARGET_M480/m480_eth_pins.h new file mode 100644 index 00000000000..185f495157e --- /dev/null +++ b/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/TARGET_M480/m480_eth_pins.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018 Nuvoton Technology Corp. + * Copyright (c) 2018 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Description: M480 EMAC driver header file + */ + + +#ifndef M480_ETH_PINS_H +#define M480_ETH_PINS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "PinNames.h" + +/// Mux pins and enable clock for the Nuvoton EMAC peripheral +void nu_eth_clk_and_pin_init(void); + +/// Unmux pins and disable clock for the Nuvoton EMAC peripheral +void nu_eth_clk_and_pin_deinit(void); + +/// Get the phy reset pin, or NC if not connected +PinName nu_eth_get_phy_reset_pin(void); + +#ifdef __cplusplus +} +#endif + +#endif //M480_ETH_PINS_H diff --git a/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/numaker_eth_hal.h b/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/numaker_eth_hal.h index e83fe8c3a40..cae0c96441e 100644 --- a/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/numaker_eth_hal.h +++ b/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/numaker_eth_hal.h @@ -29,6 +29,7 @@ extern "C" { #include #include #include +#include #ifdef NU_TRACE #define NU_DEBUGF(x) { printf x; } diff --git a/connectivity/drivers/emac/include/GenericEthDMA.h b/connectivity/drivers/emac/include/GenericEthDMA.h index 05cb0c08635..f31fcee1c4e 100644 --- a/connectivity/drivers/emac/include/GenericEthDMA.h +++ b/connectivity/drivers/emac/include/GenericEthDMA.h @@ -43,6 +43,10 @@ namespace mbed { /// This is used to support Eth MACs that don't allow enqueuing every single descriptor at a time. const size_t extraTxDescsToLeave; + /// Whether the hardware supports chaining multiple descriptors together to send one + /// packet that's split across multiple buffers. + const bool supportsDescChaining; + /// Pointer to first memory buffer in the chain associated with descriptor n. /// The buffer address shall only be set for the *last* descriptor, so that the entire chain is freed /// when the last descriptor is returned. @@ -60,8 +64,9 @@ namespace mbed { size_t txReclaimIndex; ///< Index of the next Tx descriptor that will be reclaimed by the mac thread calling reclaimTxDescs(). /// Construct, passing a value for extraTxDescsToLeave - GenericTxDMARing(size_t extraTxDescsToLeave = 0): - extraTxDescsToLeave(extraTxDescsToLeave) + GenericTxDMARing(size_t extraTxDescsToLeave = 0, bool supportsDescChaining = true): + extraTxDescsToLeave(extraTxDescsToLeave), + supportsDescChaining(supportsDescChaining) {} /// Configure DMA registers to point to the DMA ring, @@ -193,6 +198,14 @@ namespace mbed { size_t packetDescsUsed = memory_manager->count_buffers(buf); size_t neededFreeDescs = packetDescsUsed + extraTxDescsToLeave; bool needToCopy = false; + + if(packetDescsUsed > 1 && !supportsDescChaining) + { + /// Packet uses more than 1 descriptor and the hardware doesn't support that so + /// we have to copy it into one single descriptor. + needToCopy = true; + } + if(neededFreeDescs >= TX_NUM_DESCS) { // Packet uses too many buffers, we have to copy it into a continuous buffer. @@ -228,9 +241,6 @@ namespace mbed { } } - tr_debug("Transmitting packet of length %lu in %zu buffers and %zu descs\n", - memory_manager->get_total_len(buf), memory_manager->count_buffers(buf), neededDescs); - // Step 2: Copy packet if needed if(needToCopy) { @@ -252,6 +262,9 @@ namespace mbed { buf = newBuf; } + tr_debug("Transmitting packet of length %lu in %zu buffers and %zu descs\n", + memory_manager->get_total_len(buf), memory_manager->count_buffers(buf), packetDescsUsed); + // Step 3: Wait for needed amount of buffers to be available. // Note that, in my experience, it's better to block here, as dropping the packet // due to not having enough buffers can create weird effects when the application sends @@ -319,7 +332,7 @@ namespace mbed { * that the subclass must override. */ class GenericRxDMARing : public CompositeEMAC::RxDMA { - protected: + public: /// How many extra buffers to leave in the Rx pool, relative to how many we keep assigned to Rx descriptors. /// We want to keep some amount of extra buffers because constantly hitting the network stack with failed pool /// allocations can produce some negative consequences in some cases. @@ -330,14 +343,6 @@ namespace mbed { // TODO: When we add multiple Ethernet support, this calculation may need to be changed, because the pool buffers will be split between multiple EMACs static constexpr size_t RX_NUM_DESCS = MBED_CONF_NSAPI_EMAC_RX_POOL_NUM_BUFS - RX_POOL_EXTRA_BUFFERS + 1; - /// Pointer to the network stack buffer associated with the corresponding Rx descriptor. - net_stack_mem_buf_t * rxDescStackBufs[RX_NUM_DESCS]; - - // Indexes for descriptor rings. - size_t rxBuildIndex; ///< Index of the next Rx descriptor that needs to be built. Updated by application and used by ISR. - size_t rxDescsOwnedByApplication; ///< Number of Rx descriptors owned by the application and needing buffers allocated. - std::atomic rxNextIndex; ///< Index of the next descriptor that the DMA will populate. Updated by application but used by ISR. - // Alignment required for Rx memory buffers. Normally they don't need more than word alignment but // if we are doing cache operations they need to be cache aligned. #if __DCACHE_PRESENT @@ -345,7 +350,16 @@ namespace mbed { #else static constexpr size_t RX_BUFFER_ALIGN = sizeof(uint32_t); #endif + protected: + /// Pointer to the network stack buffer associated with the corresponding Rx descriptor. + net_stack_mem_buf_t * rxDescStackBufs[RX_NUM_DESCS]; + // Indexes for descriptor rings. + size_t rxBuildIndex; ///< Index of the next Rx descriptor that needs to be built. Updated by application and used by ISR. + size_t rxDescsOwnedByApplication; ///< Number of Rx descriptors owned by the application and needing buffers allocated. + std::atomic rxNextIndex; ///< Index of the next descriptor that the DMA will populate. Updated by application but used by ISR. + + protected: /// Payload size of buffers allocated from the Rx pool. This is the allocation unit size /// of the pool minus any overhead needed for alignment. size_t rxPoolPayloadSize; @@ -527,7 +541,7 @@ namespace mbed { std::optional firstDescIdx, lastDescIdx; // Packet length is stored here once we check it - size_t pktLen; + size_t pktLen{}; // Prevent looping around into descriptors waiting for rebuild by limiting how many // we can process. diff --git a/connectivity/drivers/emac/sources/PhyDrivers.cpp b/connectivity/drivers/emac/sources/PhyDrivers.cpp index 75bc001b62f..f0f47e7a192 100644 --- a/connectivity/drivers/emac/sources/PhyDrivers.cpp +++ b/connectivity/drivers/emac/sources/PhyDrivers.cpp @@ -44,6 +44,30 @@ class Driver : public GenericEthPhy { } +namespace IP101G { + + /// Driver for the IC+ IP101Gx PHY + /// Datasheet: https://www.lcsc.com/datasheet/lcsc_datasheet_IC-Plus-IP101GR_C79324.pdf + /// @{ + + inline constexpr GenericEthPhy::Config DefaultConfig = { + .OUI = 0x90C3, + .model = 0x5, + .address = 1, // Address set via strapping pins, 1 is used on Nuvoton boards + }; + + class Driver : public GenericEthPhy { + public: + explicit Driver(GenericEthPhy::Config const & config = DefaultConfig): + GenericEthPhy(config) + {} + }; + + + /// @} + +} + /** * @brief Obtains the PHY driver for Ethernet port 0. * diff --git a/connectivity/lwipstack/mbed_lib.json5 b/connectivity/lwipstack/mbed_lib.json5 index 0de4f1750a2..876491abf7d 100644 --- a/connectivity/lwipstack/mbed_lib.json5 +++ b/connectivity/lwipstack/mbed_lib.json5 @@ -116,11 +116,11 @@ }, "tcpip-thread-priority": { "help": "Priority of lwip TCPIP thread", - "value": "osPriorityNormal" + "value": "osPriorityHigh" }, "mem-size": { - "help": "Size of heap (bytes) - used for outgoing packets, and also used by some drivers for reception, see LWIP's opt.h for more information. Current default is 1600.", - "value": 1600 + "help": "Size of heap (bytes) - used for outgoing packets, and also used by some drivers for reception, see LWIP's opt.h for more information.", + "value": 4000 }, "tcpip-thread-stacksize": { "help": "Stack size for lwip TCPIP thread", @@ -160,9 +160,6 @@ } }, "target_overrides": { - "STM": { - "mem-size": 2310 - }, "Freescale": { "mem-size": 33270 }, diff --git a/connectivity/nanostack/mbed-mesh-api/source/NanostackMemoryManager.cpp b/connectivity/nanostack/mbed-mesh-api/source/NanostackMemoryManager.cpp index dcb93b1cbe1..12ced935d64 100644 --- a/connectivity/nanostack/mbed-mesh-api/source/NanostackMemoryManager.cpp +++ b/connectivity/nanostack/mbed-mesh-api/source/NanostackMemoryManager.cpp @@ -62,7 +62,7 @@ emac_mem_buf_t *NanostackMemoryManager::alloc_pool(uint32_t size, uint32_t align uint32_t NanostackMemoryManager::get_pool_alloc_unit(uint32_t align) const { - return MBED_CONF_NSAPI_EMAC_RX_POOL_BUF_SIZE; + return MBED_CONF_NSAPI_EMAC_RX_POOL_BUF_SIZE - align; } void NanostackMemoryManager::free(emac_mem_buf_t *mem) diff --git a/connectivity/netsocket/mbed_lib.json5 b/connectivity/netsocket/mbed_lib.json5 index 3b1097f8ab9..c3e7e12418a 100644 --- a/connectivity/netsocket/mbed_lib.json5 +++ b/connectivity/netsocket/mbed_lib.json5 @@ -115,6 +115,18 @@ // First-party STM32 boards generally use a LAN8742 PHY at MDIO address 0 "nsapi.emac-phy-model": "LAN8742", "nsapi.emac-phy-mdio-address": 0 + }, + "MCU_M480": { + "nsapi.emac-phy-model": "IP101G", + "nsapi.emac-phy-mdio-address": 1, + "nsapi.emac-max-mcast-subscribes": 14, // Max supported by hardware + + // The M480 EMAC sadly does not support splitting one frame across multiple Rx descriptors. + // So, we need to make each Rx descriptor large enough to fit a max size Ethernet packet. + // Normally that would be 1518 (1514 byte MTU plus 4 bytes for alignment overhead), but this EMAC also always + // receives the CRC and length at the end of the packet, with no way to turn it off. So, we need an extra 6 + // bytes padding that can be clobbered here. + "emac-rx-pool-buf-size": 1524, } } } diff --git a/connectivity/netsocket/tests/TESTS/netsocket/tcp/tcpsocket_echotest.cpp b/connectivity/netsocket/tests/TESTS/netsocket/tcp/tcpsocket_echotest.cpp index 7cfa5aa0710..c03d62c3c38 100644 --- a/connectivity/netsocket/tests/TESTS/netsocket/tcp/tcpsocket_echotest.cpp +++ b/connectivity/netsocket/tests/TESTS/netsocket/tcp/tcpsocket_echotest.cpp @@ -65,11 +65,14 @@ void TCPSOCKET_ECHOTEST() TEST_FAIL(); return; } + tr_info("Connected to echo server"); int recvd; int sent; for (unsigned int s_idx = 0; s_idx < sizeof(pkt_sizes) / sizeof(*pkt_sizes); s_idx++) { int pkt_s = pkt_sizes[s_idx]; + tr_info("Sending packet of size %d", pkt_s); + fill_tx_buffer_ascii(tcp_global::tx_buffer, BUFF_SIZE); sent = sock.send(tcp_global::tx_buffer, pkt_s); if (sent < 0) { diff --git a/connectivity/netsocket/tests/TESTS/netsocket/udp/udpsocket_echotest_burst.cpp b/connectivity/netsocket/tests/TESTS/netsocket/udp/udpsocket_echotest_burst.cpp index d3351b3f7f7..f0e9a13473b 100644 --- a/connectivity/netsocket/tests/TESTS/netsocket/udp/udpsocket_echotest_burst.cpp +++ b/connectivity/netsocket/tests/TESTS/netsocket/udp/udpsocket_echotest_burst.cpp @@ -186,6 +186,7 @@ void UDPSOCKET_ECHOTEST_BURST_NONBLOCK() int recvd = 0; int bt_total = 0; for (int i = 0; i < BURST_CNT; i++) { + tr_info("Sending burst #%d", i); for (int x = 0; x < BURST_PKTS; x++) { nsapi_size_or_error_t sent = sock.sendto(udp_addr, tx_buffers[x].payload, tx_buffers[x].len); if (sent != NSAPI_ERROR_WOULD_BLOCK) { diff --git a/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.cpp b/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.cpp index b75355c8cb2..0296ede8967 100644 --- a/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.cpp +++ b/connectivity/netsocket/tests/emac_test_utils/EmacTestMemoryManager.cpp @@ -598,7 +598,6 @@ void EmacTestMemoryManager::validate_list() const char *buffer_tail = static_cast(mem_buf->ptr) + mem_buf->orig_len; if (memcmp(buffer_tail, BUF_TAIL, BUF_TAIL_SIZE) != 0) { CHECK_ASSERT(0, "validate_list(): %p tail overwrite", mem_buf); - } } diff --git a/targets/TARGET_NUVOTON/TARGET_M480/mbed_overrides.c b/targets/TARGET_NUVOTON/TARGET_M480/mbed_overrides.c index 986a3f8d054..53004bcd1ba 100644 --- a/targets/TARGET_NUVOTON/TARGET_M480/mbed_overrides.c +++ b/targets/TARGET_NUVOTON/TARGET_M480/mbed_overrides.c @@ -105,3 +105,42 @@ void mbed_sdk_init(void) SYS_LockReg(); } } + +// Override mbed_mac_address of mbed_interface.c to provide ethernet devices with a semi-unique MAC address +void mbed_mac_address(char *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); +}