diff --git a/features/lwipstack/mbed_lib.json b/features/lwipstack/mbed_lib.json
index 83266eae4a..88c22d998c 100644
--- a/features/lwipstack/mbed_lib.json
+++ b/features/lwipstack/mbed_lib.json
@@ -124,6 +124,9 @@
},
"LPC546XX": {
"mem-size": 36496
+ },
+ "EFM32GG11-STK3701": {
+ "mem-size": 36560
}
}
}
diff --git a/features/netsocket/emac-drivers/TARGET_Silicon_Labs/mbed_lib.json b/features/netsocket/emac-drivers/TARGET_Silicon_Labs/mbed_lib.json
new file mode 100644
index 0000000000..a7d2bf676c
--- /dev/null
+++ b/features/netsocket/emac-drivers/TARGET_Silicon_Labs/mbed_lib.json
@@ -0,0 +1,34 @@
+{
+ "name": "sl-eth",
+ "config": {
+ "rmii-location": {
+ "help": "Location number to use for the RMII pins, see chip datasheet",
+ "value": null
+ },
+ "mdio-location": {
+ "help": "Location number to use for the MDIO pins, see chip datasheet",
+ "value": null
+ },
+ "refclk-location": {
+ "help": "Location number to use for the REFCLK output from CMU (CLKOUTSEL2), see chip datasheet",
+ "value": null
+ },
+ "phy-enable-pin": {
+ "help": "Pin attached to the PHY enable line",
+ "value": null
+ },
+ "phy-power-pin": {
+ "help": "Pin used to switch on power to the PHY. If not defined, we assume the PHY is always powered.",
+ "value": null
+ }
+ },
+ "target_overrides": {
+ "EFM32GG11_STK3701": {
+ "rmii-location": 1,
+ "mdio-location": 1,
+ "refclk-location": 5,
+ "phy-enable-pin": "PH7",
+ "phy-power-pin": "PI10"
+ }
+ }
+}
diff --git a/features/netsocket/emac-drivers/TARGET_Silicon_Labs/sl_emac.cpp b/features/netsocket/emac-drivers/TARGET_Silicon_Labs/sl_emac.cpp
new file mode 100644
index 0000000000..d632290426
--- /dev/null
+++ b/features/netsocket/emac-drivers/TARGET_Silicon_Labs/sl_emac.cpp
@@ -0,0 +1,712 @@
+/***************************************************************************//**
+ * @file sl_emac.cpp
+ *******************************************************************************
+ * @section License
+ * (C) Copyright 2018 Silicon Labs, http://www.silabs.com
+ *******************************************************************************
+ *
+ * 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 "device.h"
+
+#if defined(ETH_PRESENT)
+#include "sl_emac_config.h"
+#include "sl_emac.h"
+#include "sl_eth_hw.h"
+#include "sl_eth_phy.h"
+#include "mbed_power_mgmt.h"
+
+#include
+
+#include "mbed-trace/mbed_trace.h"
+#define TRACE_GROUP "SEth"
+
+#define FLAG_TX 1
+#define FLAG_RX 2
+#define FLAG_POLL 4
+
+// -----------------------------------------------------------------------------
+// Setup
+// -----------------------------------------------------------------------------
+bool SL_EMAC::power_up()
+{
+ // If the worker thread doesn't exist, launch it
+ if(thread == 0) {
+ /* Worker thread */
+ osThreadAttr_t attr = {0};
+ attr.name = "sl_emac_thread";
+ attr.stack_mem = malloc(SL_ETH_THREAD_STACKSIZE);
+ attr.cb_mem = &thread_cb;
+ attr.stack_size = SL_ETH_THREAD_STACKSIZE;
+ attr.cb_size = sizeof(mbed_rtos_storage_thread_t);
+ attr.priority = SL_ETH_THREAD_PRIORITY;
+ thread = osThreadNew(&this->eth_thread, this, &attr);
+ }
+
+ // Can't turn off HF clock as long as Ethernet is active
+ sleep_manager_lock_deep_sleep();
+
+ // Bring up data structures
+ data_init();
+
+ // Bring up clocks
+ sl_eth_hw_init();
+
+ // Point to RX queue
+ ETH->RXQPTR = (uint32_t)rx_bds;
+ ETH->DMACFG = (ETH->DMACFG & ~_ETH_DMACFG_RXBUFSIZE_MASK)
+ | ((SL_ETH_RX_BUF_SIZE/64) << _ETH_DMACFG_RXBUFSIZE_SHIFT);
+
+ // Set up MAC address
+ uint8_t addr[6];
+ get_hwaddr(addr);
+ set_hwaddr(addr);
+
+ ETH->IFCR |= _ETH_IFCR_MASK;
+ ETH->RXSTATUS = 0xFFFFFFFF;
+ ETH->TXSTATUS = 0xFFFFFFFF;
+ ETH->IENS = ETH_IENS_RXCMPLT |
+ ETH_IENS_RXUSEDBITREAD |
+ ETH_IENS_TXCMPLT |
+ ETH_IENS_TXUNDERRUN |
+ ETH_IENS_RTRYLMTORLATECOL |
+ ETH_IENS_TXUSEDBITREAD |
+ ETH_IENS_AMBAERR |
+ ETH_IENS_MNGMNTDONE;
+
+ ETH->NETWORKCFG |= ETH_NETWORKCFG_FCSREMOVE |
+ ETH_NETWORKCFG_UNICASTHASHEN |
+ ETH_NETWORKCFG_MULTICASTHASHEN |
+ ETH_NETWORKCFG_RXCHKSUMOFFLOADEN;
+
+ ETH->NETWORKCFG |= ETH_NETWORKCFG_FULLDUPLEX |
+ ETH_NETWORKCFG_SPEED;
+
+ ETH->DMACFG |= _ETH_DMACFG_AMBABRSTLEN_MASK |
+ ETH_DMACFG_FRCDISCARDONERR |
+ ETH_DMACFG_TXPBUFTCPEN;
+
+ ETH->DMACFG &= ~ETH_DMACFG_HDRDATASPLITEN;
+
+ ETH->NETWORKCTRL |= ETH_NETWORKCTRL_ENBTX |
+ ETH_NETWORKCTRL_ENBRX |
+ ETH_NETWORKCTRL_MANPORTEN;
+
+ phy_init();
+
+ NVIC_EnableIRQ(ETH_IRQn);
+
+ up = true;
+
+ tr_debug("Link booted");
+
+ osThreadFlagsSet(thread, FLAG_POLL);
+
+ return true;
+}
+
+void SL_EMAC::power_down()
+{
+ up = false;
+
+ tr_debug("Link coming down, waiting for TX to be done.");
+
+ tx_sem.wait();
+
+ NVIC_DisableIRQ(ETH_IRQn);
+
+ sl_eth_hw_deinit();
+
+ data_deinit();
+
+ /* link is down */
+ if(connected && emac_link_state_cb) {
+ emac_link_state_cb(false);
+ }
+ connected = false;
+
+ tr_debug("Link down");
+
+ // Ethernet went down, HF clock is no longer required here
+ sleep_manager_unlock_deep_sleep();
+}
+
+void SL_EMAC::data_init(void)
+{
+ size_t i;
+
+ /* Allocate a full-frame buffer for each RX BD and set up said BD */
+ for(i = 0; i < SL_ETH_NUM_RX_BD; i++) {
+ rx_bufs[i] = memory_manager->alloc_heap(SL_ETH_RX_BUF_SIZE, SL_ETH_ALIGN);
+ rx_bds[i].addr = ((uint32_t)memory_manager->get_ptr(rx_bufs[i])) & ~0x3;
+ if(i == SL_ETH_NUM_RX_BD-1) {
+ rx_bds[i].addr |= 0x2;
+ }
+ rx_bds[i].status = 0;
+ }
+
+ /* Set up TX BDs */
+ tx_buf = (emac_mem_buf_t*)NULL;
+ for(i = 0; i < SL_ETH_NUM_TX_BD; i++) {
+ tx_bds[i].addr = 0;
+ tx_bds[i].status = 0;
+ if(i == SL_ETH_NUM_TX_BD-1) {
+ tx_bds[i].status |= (0x1 << 30);
+ }
+ }
+
+ /* Start RX reception at index 0 */
+ rx_idx = 0;
+}
+
+void SL_EMAC::data_deinit(void)
+{
+ for(size_t i = 0; i < SL_ETH_NUM_RX_BD; i++) {
+ memory_manager->free(rx_bufs[i]);
+ }
+}
+
+// -----------------------------------------------------------------------------
+// IRQ & IRQ de-escalation logic
+// -----------------------------------------------------------------------------
+/* IRQ handler for ethernet interrupts */
+void ETH_IRQHandler(void)
+{
+ uint32_t int_clr = 0;
+ uint32_t int_status = ETH->IFCR;
+ uint32_t txdone_mask = ETH_IFCR_TXCMPLT |
+ ETH_IFCR_TXUNDERRUN |
+ ETH_IFCR_RTRYLMTORLATECOL |
+ ETH_IFCR_TXUSEDBITREAD |
+ ETH_IFCR_AMBAERR;
+ uint32_t rxdone_mask = ETH_IFCR_RXCMPLT |
+ ETH_IFCR_RXUSEDBITREAD;
+
+
+ SL_EMAC &emac = SL_EMAC::get_instance();
+
+ if(int_status & rxdone_mask) {
+ if (emac.thread) {
+ osThreadFlagsSet(emac.thread, FLAG_RX);
+ }
+ int_clr |= rxdone_mask;
+ }
+
+ if(int_status & txdone_mask) {
+ if (emac.thread) {
+ osThreadFlagsSet(emac.thread, FLAG_TX);
+ }
+ int_clr |= txdone_mask;
+ }
+
+ int_clr |= ETH_IFCR_MNGMNTDONE;
+ ETH->IFCR = int_clr;
+}
+
+void SL_EMAC::eth_thread(void* instance)
+{
+ struct SL_EMAC *emac = static_cast(instance);
+
+ for (;;) {
+ uint32_t flags = osThreadFlagsWait(FLAG_RX|FLAG_TX|FLAG_POLL, osFlagsWaitAny, SL_ETH_LINK_POLL_PERIOD_MS);
+ if ((flags == osFlagsErrorTimeout) && emac->up) {
+ // Rather than calling strictly every period, we call when idle
+ // for that period - hopefully good enough. We run this task
+ // from lwIP's thread rather than our RX/TX thread, as PHY reads can
+ // be slow, and we don't want them to interfere with data pumping.
+ // This is analogous to the way the PHY polling works in the Nanostack
+ // version of the driver
+ emac->link_state_poll();
+ continue;
+ }
+
+ if((flags & FLAG_POLL)) {
+ emac->link_state_poll();
+ continue;
+ }
+
+ MBED_ASSERT((flags & osFlagsError) == 0);
+
+ /* Packet received */
+ if ((flags & FLAG_RX) && emac->up) {
+ /* Find packets in the RX BD chain which have been fully received. Feed the
+ * corresponding buffer upstream, and attach a new buffer to the BD. */
+ while(emac->rx_bds[emac->rx_idx].addr & 0x1) {
+ /* DMA has relinquished control over this packet */
+ emac_mem_buf_t* buf = emac->rx_bufs[emac->rx_idx];
+ emac->memory_manager->set_len(buf, emac->rx_bds[emac->rx_idx].status & 0x1FFF);
+
+ tr_debug("Received packet of size %d", emac->memory_manager->get_len(buf));
+ /* Attempt to queue new buffer */
+ emac_mem_buf_t* temp_rxbuf = emac->memory_manager->alloc_heap(SL_ETH_RX_BUF_SIZE, 4);
+ if (NULL == temp_rxbuf) {
+ /* out of memory, drop packet */
+ tr_warn("Packet index %d dropped for OOM",
+ emac->rx_idx);
+ emac->rx_bds[emac->rx_idx].addr &= ~0x1;
+ } else {
+ emac->rx_bufs[emac->rx_idx] = temp_rxbuf;
+
+ emac->rx_bds[emac->rx_idx].status = 0;
+ if(emac->rx_bds[emac->rx_idx].addr & 0x2) {
+ emac->rx_bds[emac->rx_idx].addr = (uint32_t)emac->memory_manager->get_ptr(temp_rxbuf) | 0x2;
+ } else {
+ emac->rx_bds[emac->rx_idx].addr = (uint32_t)emac->memory_manager->get_ptr(temp_rxbuf);
+ }
+
+ /* pass all packets to ethernet_input, which decides what packets it supports */
+ if(emac->emac_link_input_cb) {
+ emac->emac_link_input_cb(buf);
+ }
+ }
+ emac->rx_idx = (emac->rx_idx + 1) % SL_ETH_NUM_RX_BD;
+ }
+ }
+
+ /* Packet transmission done */
+ if (flags & FLAG_TX) {
+ /* Free the buffer */
+ if(emac->tx_buf != NULL) {
+ emac->memory_manager->free(emac->tx_buf);
+ emac->tx_buf = NULL;
+ }
+
+ /* Signal TX thread(s) we're ready to start TX'ing */
+ emac->tx_sem.release();
+ }
+ }
+}
+
+// -----------------------------------------------------------------------------
+// PHY manipulation
+// -----------------------------------------------------------------------------
+void SL_EMAC::phy_init(void)
+{
+ uint8_t phy_addr = 0;
+ uint16_t regid1, regid2;
+ /* PHY has been enabled by hw_init. Figure out address first */
+ for(; phy_addr < 32; phy_addr++) {
+ read_phy(PHY_PHYSID1, ®id1);
+ read_phy(PHY_PHYSID2, ®id2);
+
+ if (((regid1 == 0x0000u) && (regid2 == 0x0000u)) ||
+ ((regid1 == 0x3FFFu) && (regid2 == 0x0000u)) ||
+ ((regid1 == 0x0000u) && (regid2 == 0x3FFFu)) ||
+ ((regid1 == 0x3FFFu) && (regid2 == 0x3FFFu)) ||
+ ((regid1 == 0xFFFFu) && (regid2 == 0x0000u)) ||
+ ((regid1 == 0x0000u) && (regid2 == 0xFFFFu)) ||
+ ((regid1 == 0x3FFFu) && (regid2 == 0xFFFFu)) ||
+ ((regid1 == 0xFFFFu) && (regid2 == 0xFFFFu))) {
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ if(phy_addr >= 32) {
+ /* no PHY found */
+ this->phy_addr = 0xFF;
+ return;
+ } else {
+ this->phy_addr = phy_addr;
+ }
+
+ /* Reset PHY */
+ write_phy(PHY_BMCR, BMCR_RESET);
+ read_phy(PHY_BMCR, ®id1);
+
+ /* wait for PHY to come out of reset */
+ while(regid1 & BMCR_RESET) {
+ osDelay(2);
+ read_phy(PHY_BMCR, ®id1);
+ }
+
+ /* Enable PHY */
+ if(regid1 & BMCR_PDOWN) {
+ write_phy(PHY_BMCR, regid1 & (~BMCR_PDOWN));
+ }
+
+ /* Set up auto-negotiation */
+ read_phy(PHY_BMCR, ®id1);
+ regid1 |= BMCR_ANENABLE | BMCR_ANRESTART;
+ write_phy(PHY_BMCR, regid1);
+}
+
+void SL_EMAC::write_phy(uint8_t reg_addr, uint16_t data)
+{
+ unsigned int timeout;
+
+ ETH->PHYMNGMNT = ETH_PHYMNGMNT_WRITE0_DEFAULT
+ | ETH_PHYMNGMNT_WRITE1
+ | (0x01 << _ETH_PHYMNGMNT_OPERATION_SHIFT)
+ | ((phy_addr << _ETH_PHYMNGMNT_PHYADDR_SHIFT)
+ & _ETH_PHYMNGMNT_PHYADDR_MASK)
+ | ((reg_addr << _ETH_PHYMNGMNT_REGADDR_SHIFT)
+ & _ETH_PHYMNGMNT_REGADDR_MASK)
+ | (0x2 << _ETH_PHYMNGMNT_WRITE10_SHIFT)
+ | (data & _ETH_PHYMNGMNT_PHYRWDATA_MASK);
+
+ for(timeout = 0; timeout < 10000u; timeout++) {
+ if(ETH->NETWORKSTATUS & ETH_NETWORKSTATUS_MANDONE) {
+ break;
+ }
+ }
+}
+
+void SL_EMAC::read_phy(uint8_t reg_addr, uint16_t *data)
+{
+ unsigned int timeout;
+
+ ETH->PHYMNGMNT = ETH_PHYMNGMNT_WRITE0_DEFAULT
+ | ETH_PHYMNGMNT_WRITE1
+ | (0x02 << _ETH_PHYMNGMNT_OPERATION_SHIFT)
+ | ((phy_addr << _ETH_PHYMNGMNT_PHYADDR_SHIFT)
+ & _ETH_PHYMNGMNT_PHYADDR_MASK)
+ | ((reg_addr << _ETH_PHYMNGMNT_REGADDR_SHIFT)
+ & _ETH_PHYMNGMNT_REGADDR_MASK)
+ | (0x2 << _ETH_PHYMNGMNT_WRITE10_SHIFT);
+
+ for(timeout = 0; timeout < 10000u; timeout++) {
+ if(ETH->NETWORKSTATUS & ETH_NETWORKSTATUS_MANDONE) {
+ break;
+ }
+ }
+
+ *data = ETH->PHYMNGMNT & _ETH_PHYMNGMNT_PHYRWDATA_MASK;
+}
+
+void SL_EMAC::link_state_poll(void)
+{
+ uint16_t phy_val, link_val;
+
+ /* read BMSR twice, since it needs to latch */
+ read_phy(PHY_BMSR, &phy_val);
+ read_phy(PHY_BMSR, &phy_val);
+
+ if((phy_val & BMSR_LSTATUS) == 0) {
+ /* link is down */
+ tr_debug("link down");
+ if(connected && emac_link_state_cb) {
+ emac_link_state_cb(false);
+ /* TODO: Reset all buffers here */
+ /* For now, this is not a problem. In-transit buffers will
+ * still be sent the next time the link comes up, so the
+ * only impact is that we'd be sending stale packets. */
+ }
+ connected = false;
+ return;
+ }
+
+ /* link is up, get speed and duplex status */
+ read_phy(PHY_ANAR, &phy_val);
+ read_phy(PHY_ANLPAR, &link_val);
+
+ link_val &= (ANLPAR_100BASE4 |
+ ANLPAR_100FULL |
+ ANLPAR_100HALF |
+ ANLPAR_10FULL |
+ ANLPAR_10HALF);
+
+ phy_val &= link_val;
+
+ uint32_t old_link_state = ETH->NETWORKCFG & 0x3;
+
+ if (phy_val >= ANLPAR_100FULL) {
+ /* 100 mbit full duplex */
+ if (old_link_state != 0x3 || !connected) {
+ tr_debug("renegotiated to 100 full");
+ ETH->NETWORKCFG = (ETH->NETWORKCFG & ~0x3) | 0x3;
+ }
+ } else if (phy_val >= ANLPAR_100HALF) {
+ /* 100 mbit half duplex */
+ if (old_link_state != 0x1 || !connected) {
+ tr_debug("renegotiated to 100 half");
+ ETH->NETWORKCFG = (ETH->NETWORKCFG & ~0x3) | 0x1;
+ }
+ } else if (phy_val >= ANLPAR_10FULL) {
+ /* 10 mbit full duplex */
+ if (old_link_state != 0x2 || !connected) {
+ tr_debug("renegotiated to 10 full");
+ ETH->NETWORKCFG = (ETH->NETWORKCFG & ~0x3) | 0x2;
+ }
+ } else {
+ /* 10 mbit half duplex */
+ if (old_link_state != 0x0 || !connected) {
+ tr_debug("renegotiated to 10 half");
+ ETH->NETWORKCFG = (ETH->NETWORKCFG & ~0x3) | 0x0;
+ }
+ }
+
+ if(!connected && emac_link_state_cb) {
+ tr_debug("link up");
+ emac_link_state_cb(true);
+ }
+ connected = true;
+}
+
+// -----------------------------------------------------------------------------
+// Receive
+// -----------------------------------------------------------------------------
+
+/* Handled inside processing thread */
+
+// -----------------------------------------------------------------------------
+// Send
+// -----------------------------------------------------------------------------
+bool SL_EMAC::link_out(emac_mem_buf_t *buf)
+{
+ size_t num_bufs = 1, i;
+ emac_mem_buf_t * next = buf;
+
+ /* If the link is down (or going down), don't even attempt sending anything */
+ if(!up) {
+ tr_debug("Trying to send a packet while link is down");
+ memory_manager->free(buf);
+ return false;
+ }
+
+ /* Figure out how many buffers the buffer consists of */
+ while((next = memory_manager->get_next(next)) != (emac_mem_buf_t*)NULL) {
+ num_bufs++;
+ }
+
+ if(num_bufs >= SL_ETH_NUM_TX_BD) {
+ /* We've been passed more chained buffers than we can handle */
+ tr_err("More TX buffers passed than provisioned!");
+ memory_manager->free(buf);
+ return false;
+ }
+
+ /* Wait for previous packet to finish transmitting */
+ int32_t stat = tx_sem.wait(100);
+ if (stat <= 0) {
+ tr_warn("TX process didn't complete within 100ms");
+ memory_manager->free(buf);
+ return false;
+ }
+
+ tr_debug("Sending packet of %d bytes over %d buffers", memory_manager->get_total_len(buf), num_bufs);
+
+ /* Set up TX descriptors with buffer, keep track of buffer reference */
+ tx_buf = buf;
+ for(i = 0; i < num_bufs; i++) {
+ uint32_t statusword = memory_manager->get_len(buf) & 0x3FFF;
+
+ if(i == (SL_ETH_NUM_TX_BD-1)) {
+ /* Mark as last BD in list */
+ statusword |= (0x1 << 30);
+ }
+ if(i == num_bufs - 1) {
+ /* Mark as last BD for this frame */
+ statusword |= (0x1 << 15);
+ }
+
+ tx_bds[i].addr = (uint32_t)memory_manager->get_ptr(buf);
+ tx_bds[i].status = statusword;
+
+ buf = memory_manager->get_next(buf);
+ }
+
+ /* (Re-)Kick off ETH TX */
+ ETH->TXQPTR = (uint32_t)tx_bds;
+ ETH->NETWORKCTRL |= ETH_NETWORKCTRL_TXSTRT;
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+// Multicast manipulation
+// -----------------------------------------------------------------------------
+static uint8_t sl_eth_calc_hash(const uint8_t* const mac)
+{
+ return (uint8_t)(( (mac[0] & 0x3F) & 0x3F)
+ ^ ((((mac[0] >> 6) & 0x3) | ((mac[1] & 0xF) << 2)) & 0x3F)
+ ^ ((((mac[1] >> 4) & 0xF) | ((mac[2] & 0x3) << 4)) & 0x3F)
+ ^ ((((mac[2] >> 2) & 0x3F)) & 0x3F)
+ ^ ((mac[3] & 0x3F) & 0x3F)
+ ^ ((((mac[3] >> 6) & 0x3) | ((mac[4] & 0xF) << 2)) & 0x3F)
+ ^ ((((mac[4] >> 4) & 0xF) | ((mac[5] & 0x3) << 4)) & 0x3F)
+ ^ ((((mac[5] >> 2) & 0x3F)) & 0x3F));
+}
+
+void SL_EMAC::add_multicast_group(const uint8_t *address)
+{
+ uint8_t bitnr;
+ /* Calculate bit number for hash of this address */
+ bitnr = sl_eth_calc_hash(address);
+ /* Increment refcnt */
+ if (mcast_hash_refcnt[bitnr] == 0) {
+ if(bitnr > 31) {
+ ETH->HASHTOP |= (0x1 << (bitnr - 32));
+ } else {
+ ETH->HASHBOTTOM |= (0x1 << bitnr);
+ }
+ }
+ mcast_hash_refcnt[bitnr]++;
+}
+
+void SL_EMAC::remove_multicast_group(const uint8_t *address)
+{
+ uint8_t bitnr;
+ /* Calculate bit number for hash of this address */
+ bitnr = sl_eth_calc_hash(address);
+ /* Decrement refcnt, remove bit if 0 */
+ if(mcast_hash_refcnt[bitnr] == 1) {
+ mcast_hash_refcnt[bitnr] = 0;
+ if(bitnr > 31) {
+ ETH->HASHTOP &= ~(0x1 << (bitnr - 32));
+ } else {
+ ETH->HASHBOTTOM &= ~(0x1 << bitnr);
+ }
+ } else {
+ mcast_hash_refcnt[bitnr]--;
+ }
+}
+
+void SL_EMAC::set_all_multicast(bool all)
+{
+ uint32_t bottom = 0, top = 0;
+
+ if(all == true) {
+ /* temporarily allow all packets to get processed */
+ tr_debug("Accept all multicast packets");
+ top = 0xFFFFFFFFUL;
+ bottom = 0xFFFFFFFFUL;
+ } else {
+ /* Revert to what was in the refcount */
+ tr_debug("Revert to multicast filtering");
+ size_t i = 0;
+ for(; i < 32; i++) {
+ if(mcast_hash_refcnt[i] > 0) {
+ bottom |= (1 << i);
+ }
+ }
+ for(; i < 64; i++) {
+ if(mcast_hash_refcnt[i-32] > 0) {
+ top |= (1 << (i-32));
+ }
+ }
+ }
+
+ /* Commit to peripheral */
+ ETH->HASHTOP = top;
+ ETH->HASHBOTTOM = bottom;
+}
+
+// -----------------------------------------------------------------------------
+// MAC manipulation
+// -----------------------------------------------------------------------------
+uint8_t SL_EMAC::get_hwaddr_size() const
+{
+ // Ethernet uses EUI48
+ return 6;
+}
+
+bool SL_EMAC::get_hwaddr(uint8_t *addr) const
+{
+ if (DEVINFO->EUI48L != 0xFFFFFFFF) {
+ addr[0] = DEVINFO->EUI48H >> 8;
+ addr[1] = DEVINFO->EUI48H >> 0;
+ addr[2] = DEVINFO->EUI48L >> 24;
+ addr[3] = DEVINFO->EUI48L >> 16;
+ addr[4] = DEVINFO->EUI48L >> 8;
+ addr[5] = DEVINFO->EUI48L >> 0;
+ } else {
+ addr[0] = DEVINFO->UNIQUEH >> 24;
+ addr[1] = DEVINFO->UNIQUEH >> 16;
+ addr[2] = DEVINFO->UNIQUEH >> 8;
+ addr[3] = DEVINFO->UNIQUEL >> 16;
+ addr[4] = DEVINFO->UNIQUEL >> 8;
+ addr[5] = DEVINFO->UNIQUEL >> 0;
+ }
+ return true;
+}
+
+void SL_EMAC::set_hwaddr(const uint8_t *addr)
+{
+ tr_debug("Setting MAC address %02x:%02x:%02x:%02x:%02x:%02x",
+ addr[0],
+ addr[1],
+ addr[2],
+ addr[3],
+ addr[4],
+ addr[5]);
+ ETH->SPECADDR1BOTTOM = ((uint32_t)addr[0] << (0)) |
+ ((uint32_t)addr[1] << (8)) |
+ ((uint32_t)addr[2] << (16))|
+ ((uint32_t)addr[3] << (24));
+
+ ETH->SPECADDR1TOP = ((uint32_t)addr[4] << (0)) |
+ ((uint32_t)addr[5] << (8));
+}
+
+// -----------------------------------------------------------------------------
+// Boilerplate
+// -----------------------------------------------------------------------------
+SL_EMAC::SL_EMAC()
+ : thread(0),
+ tx_sem(1, 1),
+ phy_addr(0xFF),
+ rx_idx(0),
+ mcast_hash_refcnt(),
+ emac_link_input_cb(NULL),
+ emac_link_state_cb(NULL),
+ memory_manager(NULL),
+ connected(false),
+ up(false)
+{
+
+}
+
+uint32_t SL_EMAC::get_mtu_size() const
+{
+ return SL_ETH_MTU;
+}
+
+uint32_t SL_EMAC::get_align_preference() const
+{
+ return SL_ETH_ALIGN;
+}
+
+void SL_EMAC::get_ifname(char *name, uint8_t size) const
+{
+ memcpy(name, SL_ETH_IF_NAME, (size < sizeof(SL_ETH_IF_NAME)) ? size : sizeof(SL_ETH_IF_NAME));
+}
+
+void SL_EMAC::set_link_input_cb(emac_link_input_cb_t input_cb)
+{
+ emac_link_input_cb = input_cb;
+}
+
+void SL_EMAC::set_link_state_cb(emac_link_state_change_cb_t state_cb)
+{
+ emac_link_state_cb = state_cb;
+}
+
+void SL_EMAC::set_memory_manager(EMACMemoryManager &mem_mngr)
+{
+ memory_manager = &mem_mngr;
+}
+
+SL_EMAC &SL_EMAC::get_instance() {
+ static SL_EMAC emac;
+ return emac;
+}
+
+MBED_WEAK EMAC &EMAC::get_default_instance() {
+ return SL_EMAC::get_instance();
+}
+
+#endif //ETH_PRESENT
diff --git a/features/netsocket/emac-drivers/TARGET_Silicon_Labs/sl_emac.h b/features/netsocket/emac-drivers/TARGET_Silicon_Labs/sl_emac.h
new file mode 100644
index 0000000000..4fdc6d7f7a
--- /dev/null
+++ b/features/netsocket/emac-drivers/TARGET_Silicon_Labs/sl_emac.h
@@ -0,0 +1,256 @@
+/***************************************************************************//**
+ * @file sl_eth_phy.h
+ *******************************************************************************
+ * @section License
+ * modifications (C) Copyright 2018 Silicon Labs, http://www.silabs.com
+ * original Copyright (c) 2015 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.
+ *
+ ******************************************************************************/
+#ifndef SL_EMAC_H_
+#define SL_EMAC_H_
+
+#include "EMAC.h"
+#include "rtos/Semaphore.h"
+#include "rtos/Mutex.h"
+
+class SL_EMAC : public EMAC {
+public:
+ SL_EMAC();
+
+ static SL_EMAC &get_instance();
+
+ /**
+ * Return maximum transmission unit
+ *
+ * @return MTU in bytes
+ */
+ virtual uint32_t get_mtu_size() const;
+
+ /**
+ * Gets memory buffer alignment preference
+ *
+ * Gets preferred memory buffer alignment of the Emac device.
+ * IP stack may or may not align link out memory buffer chains
+ * using the alignment.
+ *
+ * @return Memory alignment requirement in bytes
+ */
+ virtual uint32_t get_align_preference() const;
+
+ /**
+ * Return interface name
+ *
+ * @param name Pointer to where the name should be written
+ * @param size Maximum number of character to copy
+ */
+ virtual void get_ifname(char *name, uint8_t size) const;
+
+ /**
+ * Returns size of the underlying interface HW address size.
+ *
+ * @return HW address size in bytes
+ */
+ virtual uint8_t get_hwaddr_size() const;
+
+ /**
+ * Return interface-supplied HW address
+ *
+ * Copies HW address to provided memory, @param addr has to be of correct
+ * size see @a get_hwaddr_size
+ *
+ * HW address need not be provided if this interface does not have its own
+ * HW address configuration; stack will choose address from central system
+ * configuration if the function returns false and does not write to addr.
+ *
+ * @param addr HW address for underlying interface
+ * @return true if HW address is available
+ */
+ virtual bool get_hwaddr(uint8_t *addr) const;
+
+ /**
+ * Set HW address for interface
+ *
+ * Provided address has to be of correct size, see @a get_hwaddr_size
+ *
+ * Called to set the MAC address to actually use - if @a get_hwaddr is
+ * provided the stack would normally use that, but it could be overridden,
+ * eg for test purposes.
+ *
+ * @param addr Address to be set
+ */
+ virtual void set_hwaddr(const uint8_t *addr);
+
+ /**
+ * Sends the packet over the link
+ *
+ * That can not be called from an interrupt context.
+ *
+ * @param buf Packet to be send
+ * @return True if the packet was send successfully, False otherwise
+ */
+ virtual bool link_out(emac_mem_buf_t *buf);
+
+ /**
+ * Initializes the HW
+ *
+ * @return True on success, False in case of an error.
+ */
+ virtual bool power_up();
+
+ /**
+ * Deinitializes the HW
+ *
+ */
+ virtual void power_down();
+
+ /**
+ * Sets a callback that needs to be called for packets received for that
+ * interface
+ *
+ * @param input_cb Function to be register as a callback
+ */
+ virtual void set_link_input_cb(emac_link_input_cb_t input_cb);
+
+ /**
+ * Sets a callback that needs to be called on link status changes for given
+ * interface
+ *
+ * @param state_cb Function to be register as a callback
+ */
+ virtual void set_link_state_cb(emac_link_state_change_cb_t state_cb);
+
+ /** Add device to a multicast group
+ *
+ * @param address A multicast group hardware address
+ */
+ virtual void add_multicast_group(const uint8_t *address);
+
+ /** Remove device from a multicast group
+ *
+ * @param address A multicast group hardware address
+ */
+ virtual void remove_multicast_group(const uint8_t *address);
+
+ /** Request reception of all multicast packets
+ *
+ * @param all True to receive all multicasts
+ * False to receive only multicasts addressed to specified groups
+ */
+ virtual void set_all_multicast(bool all);
+
+ /** Sets memory manager that is used to handle memory buffers
+ *
+ * @param mem_mngr Pointer to memory manager
+ */
+ virtual void set_memory_manager(EMACMemoryManager &mem_mngr);
+
+ osThreadId_t thread; /** Ethernet driver thread */
+
+private:
+
+ /* Instance variables */
+
+ /** Semaphore protecting the TX state.
+ * Not a mutex since we're posting from IRQ
+ */
+ rtos::Semaphore tx_sem;
+ /** (R)MII address where the detected PHY is residing */
+ uint8_t phy_addr;
+ /** Index in RX queue for next packet to read */
+ uint8_t rx_idx;
+
+ /** Multicast mask reference count. Multicast filtering is done using a hash
+ * bit, so multiple multicast addresses might map to the same bit. That's
+ * why there needs to be a reference count for every bit in the 64-bit mask
+ */
+ uint8_t mcast_hash_refcnt[64];
+
+ /** Local reference to the buffer that's in the process of being sent */
+ emac_mem_buf_t *tx_buf;
+ /** List of current RX buffers, which autonomously get filled by the
+ * Ethernet peripheral.
+ */
+ emac_mem_buf_t *rx_bufs[SL_ETH_NUM_RX_BD];
+
+ typedef struct {
+ uint32_t addr;
+ uint32_t status;
+ } sl_eth_bd_t;
+
+ /** Internal list of DMA descriptors for the RX buffer pool */
+ sl_eth_bd_t rx_bds[SL_ETH_NUM_RX_BD];
+ /** Internal list of DMA descriptors to point to the current buffer being
+ * sent */
+ sl_eth_bd_t tx_bds[SL_ETH_NUM_TX_BD];
+
+ /**< Processing thread */
+ mbed_rtos_storage_thread_t thread_cb;
+
+ /**< Callback for incoming data */
+ emac_link_input_cb_t emac_link_input_cb;
+ /**< Callback for link state change */
+ emac_link_state_change_cb_t emac_link_state_cb;
+
+ /**< Memory manager instance */
+ EMACMemoryManager *memory_manager;
+
+ bool connected;
+ bool up;
+
+ /* private functions */
+ /**
+ * Thread to de-escalate Ethernet peripheral IRQ's
+ */
+ static void eth_thread(void* instance);
+
+ /**
+ * This function polls the (R)MII bus for the first
+ * available attached PHY chip, resets and enables the PHY
+ * in auto-negotiation mode.
+ */
+ void phy_init(void);
+
+ /**
+ * Write to detected PHY register. Nop if no PHY initialized.
+ */
+ void write_phy(uint8_t reg_addr, uint16_t data);
+
+ /**
+ * Read from detected PHY register. Nop if no PHY initialized.
+ */
+ void read_phy(uint8_t reg_addr, uint16_t *data);
+
+ /**
+ * This function checks the detected PHY for its
+ * current link status. Nop if no PHY was previously detected.
+ * Fires callback set by set_link_state_cb on change in link state.
+ */
+ void link_state_poll(void);
+
+ /**
+ * Initializes buffer structures
+ */
+ void data_init(void);
+
+ /**
+ * De-initializes buffer structures
+ */
+ void data_deinit(void);
+};
+
+#endif /* SL_EMAC_H_ */
diff --git a/features/netsocket/emac-drivers/TARGET_Silicon_Labs/sl_emac_config.h b/features/netsocket/emac-drivers/TARGET_Silicon_Labs/sl_emac_config.h
new file mode 100644
index 0000000000..cd60f95d47
--- /dev/null
+++ b/features/netsocket/emac-drivers/TARGET_Silicon_Labs/sl_emac_config.h
@@ -0,0 +1,47 @@
+/***************************************************************************//**
+ * @file sl_emac_config.h
+ *******************************************************************************
+ * @section License
+ * (C) Copyright 2018 Silicon Labs, http://www.silabs.com
+ *******************************************************************************
+ *
+ * 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 SL_EMAC_CONFIG_H
+#define SL_EMAC_CONFIG_H
+// -----------------------------------------------------------------------------
+// Config options
+// -----------------------------------------------------------------------------
+/** Number of descriptors in receive list */
+#define SL_ETH_NUM_RX_BD (16)
+/** Number of descriptors in transmit list */
+#define SL_ETH_NUM_TX_BD (8)
+/** Size of one buffer in a buffer descriptor (must be multiple of 64) */
+#define SL_ETH_RX_BUF_SIZE (1536)
+/** Timeout in milliseconds for polling the PHY link status */
+#define SL_ETH_LINK_POLL_PERIOD_MS (500)
+/** Default Ethernet worker thread stack size in bytes */
+#define SL_ETH_THREAD_STACKSIZE (512)
+/** Default Ethernet worker thread stack priority */
+#define SL_ETH_THREAD_PRIORITY (osPriorityHigh)
+/** Name of interface */
+#define SL_ETH_IF_NAME "sl"
+/** Required alignment (in bytes) for packet buffers */
+#define SL_ETH_ALIGN (16)
+/** Link MTU */
+#define SL_ETH_MTU (1500)
+
+#endif /* SL_EMAC_CONFIG_H */
diff --git a/features/netsocket/emac-drivers/TARGET_Silicon_Labs/sl_eth_hw.c b/features/netsocket/emac-drivers/TARGET_Silicon_Labs/sl_eth_hw.c
new file mode 100644
index 0000000000..ffb7f7fffc
--- /dev/null
+++ b/features/netsocket/emac-drivers/TARGET_Silicon_Labs/sl_eth_hw.c
@@ -0,0 +1,157 @@
+/***************************************************************************//**
+ * @file sl_eth_hw.c
+ *******************************************************************************
+ * @section License
+ * (C) Copyright 2017 Silicon Labs, http://www.silabs.com
+ *******************************************************************************
+ *
+ * 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 "sl_eth_hw.h"
+#include "device.h"
+#include "em_cmu.h"
+#include "em_gpio.h"
+#include "hal/gpio_api.h"
+
+#if defined(ETH_PRESENT)
+
+void sl_eth_hw_init(void)
+{
+ /* Turn on clocks to ETH */
+ CMU_ClockEnable(cmuClock_HFPER, true);
+ CMU_ClockEnable(cmuClock_ETH, true);
+ CMU_ClockEnable(cmuClock_GPIO, true);
+
+ /* Drive RMII from the MCU-side 50MHz clock */
+ GPIO_PinModeSet(AF_CMU_CLK2_PORT(MBED_CONF_SL_ETH_REFCLK_LOCATION),
+ AF_CMU_CLK2_PIN(MBED_CONF_SL_ETH_REFCLK_LOCATION),
+ gpioModePushPull, 0);
+ CMU->CTRL |= CMU_CTRL_CLKOUTSEL2_HFXO;
+ CMU->ROUTELOC0 = (CMU->ROUTELOC0 & ~_CMU_ROUTELOC0_CLKOUT2LOC_MASK) | (MBED_CONF_SL_ETH_REFCLK_LOCATION << _CMU_ROUTELOC0_CLKOUT2LOC_SHIFT);
+ CMU->ROUTEPEN |= CMU_ROUTEPEN_CLKOUT2PEN;
+
+ ETH->CTRL = ETH_CTRL_GBLCLKEN | ETH_CTRL_MIISEL_RMII;
+
+ /* Set pins to ETH for RMII config */
+ GPIO_PinModeSet(AF_ETH_RMIICRSDV_PORT(MBED_CONF_SL_ETH_RMII_LOCATION),
+ AF_ETH_RMIICRSDV_PIN(MBED_CONF_SL_ETH_RMII_LOCATION),
+ gpioModeInput, 0); /* CRS_DV */
+ GPIO_PinModeSet(AF_ETH_RMIITXD0_PORT(MBED_CONF_SL_ETH_RMII_LOCATION),
+ AF_ETH_RMIITXD0_PIN(MBED_CONF_SL_ETH_RMII_LOCATION),
+ gpioModePushPull, 0); /* TXD0 */
+ GPIO_PinModeSet(AF_ETH_RMIITXD1_PORT(MBED_CONF_SL_ETH_RMII_LOCATION),
+ AF_ETH_RMIITXD1_PIN(MBED_CONF_SL_ETH_RMII_LOCATION),
+ gpioModePushPull, 0); /* TXD1 */
+ GPIO_PinModeSet(AF_ETH_RMIITXEN_PORT(MBED_CONF_SL_ETH_RMII_LOCATION),
+ AF_ETH_RMIITXEN_PIN(MBED_CONF_SL_ETH_RMII_LOCATION),
+ gpioModePushPull, 0); /* TX_EN */
+ GPIO_PinModeSet(AF_ETH_RMIIRXD0_PORT(MBED_CONF_SL_ETH_RMII_LOCATION),
+ AF_ETH_RMIIRXD0_PIN(MBED_CONF_SL_ETH_RMII_LOCATION),
+ gpioModeInput, 0); /* RXD0 */
+ GPIO_PinModeSet(AF_ETH_RMIIRXD1_PORT(MBED_CONF_SL_ETH_RMII_LOCATION),
+ AF_ETH_RMIIRXD1_PIN(MBED_CONF_SL_ETH_RMII_LOCATION),
+ gpioModeInput, 0); /* RXD1 */
+ GPIO_PinModeSet(AF_ETH_RMIIRXER_PORT(MBED_CONF_SL_ETH_RMII_LOCATION),
+ AF_ETH_RMIIRXER_PIN(MBED_CONF_SL_ETH_RMII_LOCATION),
+ gpioModeInput, 0); /* RX_ER */
+
+ /* Setup route locations and enable pins */
+ ETH->ROUTELOC1 = (MBED_CONF_SL_ETH_RMII_LOCATION << _ETH_ROUTELOC1_RMIILOC_SHIFT)
+ | (MBED_CONF_SL_ETH_MDIO_LOCATION << _ETH_ROUTELOC1_MDIOLOC_SHIFT);
+ ETH->ROUTEPEN = ETH_ROUTEPEN_RMIIPEN | ETH_ROUTEPEN_MDIOPEN;
+ ETH->ROUTEPEN = ETH_ROUTEPEN_RMIIPEN | ETH_ROUTEPEN_MDIOPEN;
+
+ /* Setup the MDIO pins */
+ GPIO_PinModeSet(AF_ETH_MDIO_PORT(MBED_CONF_SL_ETH_MDIO_LOCATION),
+ AF_ETH_MDIO_PIN(MBED_CONF_SL_ETH_MDIO_LOCATION),
+ gpioModePushPull, 0); /* MDIO */
+ GPIO_PinModeSet(AF_ETH_MDC_PORT(MBED_CONF_SL_ETH_MDIO_LOCATION),
+ AF_ETH_MDC_PIN(MBED_CONF_SL_ETH_MDIO_LOCATION),
+ gpioModePushPull, 0); /* MDC */
+
+ /* Enable the PHY on the STK */
+#if defined(MBED_CONF_SL_ETH_PHY_POWER_PIN)
+ gpio_t pwr_pin;
+ gpio_init_out_ex(&pwr_pin, MBED_CONF_SL_ETH_PHY_POWER_PIN, 1);
+#endif
+
+#if defined(MBED_CONF_SL_ETH_PHY_ENABLE_PIN)
+ gpio_t en_pin;
+ gpio_init_out_ex(&en_pin, MBED_CONF_SL_ETH_PHY_ENABLE_PIN, 1);
+#endif
+}
+
+void sl_eth_hw_deinit(void)
+{
+ /* Turn off PHY */
+#if defined(MBED_CONF_SL_ETH_PHY_POWER_PIN)
+ gpio_t pwr_pin;
+ gpio_init(&pwr_pin, MBED_CONF_SL_ETH_PHY_ENABLE_PIN);
+ gpio_write(&pwr_pin, 0);
+ gpio_mode(&pwr_pin, Disabled);
+#endif
+
+#if defined(MBED_CONF_SL_ETH_PHY_ENABLE_PIN)
+ gpio_t en_pin;
+ gpio_init(&en_pin, MBED_CONF_SL_ETH_PHY_POWER_PIN);
+ gpio_write(&en_pin, 0);
+ gpio_mode(&en_pin, Disabled);
+#endif
+
+ /* Turn off MAC */
+ ETH->ROUTEPEN = 0;
+ ETH->CTRL = _ETH_CTRL_RESETVALUE;
+
+ /* Turn off clock */
+ CMU->CTRL &= ~CMU_CTRL_CLKOUTSEL2_HFXO;
+ CMU->ROUTEPEN &= ~CMU_ROUTEPEN_CLKOUT2PEN;
+
+ CMU_ClockEnable(cmuClock_ETH, false);
+
+ /* Set used pins back to disabled */
+ GPIO_PinModeSet(AF_ETH_MDIO_PORT(MBED_CONF_SL_ETH_MDIO_LOCATION),
+ AF_ETH_MDIO_PIN(MBED_CONF_SL_ETH_MDIO_LOCATION),
+ gpioModeDisabled, 0); /* MDIO */
+ GPIO_PinModeSet(AF_ETH_MDC_PORT(MBED_CONF_SL_ETH_MDIO_LOCATION),
+ AF_ETH_MDC_PIN(MBED_CONF_SL_ETH_MDIO_LOCATION),
+ gpioModeDisabled, 0); /* MDC */
+ GPIO_PinModeSet(AF_ETH_RMIICRSDV_PORT(MBED_CONF_SL_ETH_RMII_LOCATION),
+ AF_ETH_RMIICRSDV_PIN(MBED_CONF_SL_ETH_RMII_LOCATION),
+ gpioModeDisabled, 0); /* CRS_DV */
+ GPIO_PinModeSet(AF_ETH_RMIITXD0_PORT(MBED_CONF_SL_ETH_RMII_LOCATION),
+ AF_ETH_RMIITXD0_PIN(MBED_CONF_SL_ETH_RMII_LOCATION),
+ gpioModeDisabled, 0); /* TXD0 */
+ GPIO_PinModeSet(AF_ETH_RMIITXD1_PORT(MBED_CONF_SL_ETH_RMII_LOCATION),
+ AF_ETH_RMIITXD1_PIN(MBED_CONF_SL_ETH_RMII_LOCATION),
+ gpioModeDisabled, 0); /* TXD1 */
+ GPIO_PinModeSet(AF_ETH_RMIITXEN_PORT(MBED_CONF_SL_ETH_RMII_LOCATION),
+ AF_ETH_RMIITXEN_PIN(MBED_CONF_SL_ETH_RMII_LOCATION),
+ gpioModeDisabled, 0); /* TX_EN */
+ GPIO_PinModeSet(AF_ETH_RMIIRXD0_PORT(MBED_CONF_SL_ETH_RMII_LOCATION),
+ AF_ETH_RMIIRXD0_PIN(MBED_CONF_SL_ETH_RMII_LOCATION),
+ gpioModeDisabled, 0); /* RXD0 */
+ GPIO_PinModeSet(AF_ETH_RMIIRXD1_PORT(MBED_CONF_SL_ETH_RMII_LOCATION),
+ AF_ETH_RMIIRXD1_PIN(MBED_CONF_SL_ETH_RMII_LOCATION),
+ gpioModeDisabled, 0); /* RXD1 */
+ GPIO_PinModeSet(AF_ETH_RMIIRXER_PORT(MBED_CONF_SL_ETH_RMII_LOCATION),
+ AF_ETH_RMIIRXER_PIN(MBED_CONF_SL_ETH_RMII_LOCATION),
+ gpioModeDisabled, 0); /* RX_ER */
+ GPIO_PinModeSet(AF_CMU_CLK2_PORT(MBED_CONF_SL_ETH_REFCLK_LOCATION),
+ AF_CMU_CLK2_PIN(MBED_CONF_SL_ETH_REFCLK_LOCATION),
+ gpioModeDisabled, 0); /* REF_CLK */
+}
+
+#endif //ETH_PRESENT
diff --git a/features/netsocket/emac-drivers/TARGET_Silicon_Labs/sl_eth_hw.h b/features/netsocket/emac-drivers/TARGET_Silicon_Labs/sl_eth_hw.h
new file mode 100644
index 0000000000..ebca47f427
--- /dev/null
+++ b/features/netsocket/emac-drivers/TARGET_Silicon_Labs/sl_eth_hw.h
@@ -0,0 +1,44 @@
+/***************************************************************************//**
+ * @file sl_eth_hw.h
+ *******************************************************************************
+ * @section License
+ * (C) Copyright 2018 Silicon Labs, http://www.silabs.com
+ *******************************************************************************
+ *
+ * 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 SL_ETH_HW_H
+#define SL_ETH_HW_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/**
+ * Sets up hardware pins and configures internal clocks
+ */
+void sl_eth_hw_init(void);
+
+/**
+ * Releases hardware pins and turns off internal clocks
+ */
+void sl_eth_hw_deinit(void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/features/netsocket/emac-drivers/TARGET_Silicon_Labs/sl_eth_phy.h b/features/netsocket/emac-drivers/TARGET_Silicon_Labs/sl_eth_phy.h
new file mode 100644
index 0000000000..3e76859612
--- /dev/null
+++ b/features/netsocket/emac-drivers/TARGET_Silicon_Labs/sl_eth_phy.h
@@ -0,0 +1,94 @@
+/***************************************************************************//**
+ * @file sl_eth_phy.h
+ *******************************************************************************
+ * @section License
+ * (C) Copyright 2017 Silicon Labs, http://www.silabs.com
+ *******************************************************************************
+ *
+ * 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 __SL_ETH_PHY_H__
+#define __SL_ETH_PHY_H__
+
+#define PHY_BMCR 0x00u /* Basic mode control reg. */
+#define PHY_BMSR 0x01u /* Basic mode status reg. */
+#define PHY_PHYSID1 0x02u /* PHYS ID 1 reg. */
+#define PHY_PHYSID2 0x03u /* PHYS ID 2 reg. */
+#define PHY_ANAR 0x04u /* Advertisement control reg. */
+#define PHY_ANLPAR 0x05u /* Link partner ability reg. */
+#define PHY_ANER 0x06u /* Expansion reg. */
+#define PHY_ANNPTR 0x07u /* Next page transmit reg. */
+
+/* -------------- PHY_BMCR REGISTER BITS -------------- */
+#define BMCR_CTST (0x1 << 7) /* Collision test. */
+#define BMCR_FULLDPLX (0x1 << 8) /* Full duplex. */
+#define BMCR_ANRESTART (0x1 << 9) /* Auto negotiation restart. */
+#define BMCR_ISOLATE (0x1 << 10) /* Disconnect Phy from MII. */
+#define BMCR_PDOWN (0x1 << 11) /* Power down. */
+#define BMCR_ANENABLE (0x1 << 12) /* Enable auto negotiation. */
+#define BMCR_SPEED100 (0x1 << 13) /* Select 100Mbps. */
+#define BMCR_LOOPBACK (0x1 << 14) /* TXD loopback bits. */
+#define BMCR_RESET (0x1 << 15) /* Reset. */
+
+/* -------------- PHY_BMSR REGISTER BITS -------------- */
+#define BMSR_ERCAP (0x1 << 0) /* Ext-reg capability. */
+#define BMSR_JCD (0x1 << 1) /* Jabber detected. */
+#define BMSR_LSTATUS (0x1 << 2) /* Link status. */
+#define BMSR_ANEGCAPABLE (0x1 << 3) /* Able to do auto-negotiation. */
+#define BMSR_RFAULT (0x1 << 4) /* Remote fault detected. */
+#define BMSR_ANEGCOMPLETE (0x1 << 5) /* Auto-negotiation complete. */
+#define BMSR_10HALF (0x1 << 11) /* Can do 10mbps, half-duplex. */
+#define BMSR_10FULL (0x1 << 12) /* Can do 10mbps, full-duplex. */
+#define BMSR_100HALF (0x1 << 13) /* Can do 100mbps, half-duplex. */
+#define BMSR_100FULL (0x1 << 14) /* Can do 100mbps, full-duplex. */
+#define BMSR_100BASE4 (0x1 << 15) /* Can do 100mbps, 4k packets. */
+
+/* -------------- PHY_ANAR REGISTER BITS -------------- */
+#define ANAR_SLCT 0x001Fu /* Selector bits. */
+#define ANAR_CSMA DEF_BIT_04 /* Only selector supported. */
+#define ANAR_10HALF DEF_BIT_05 /* Try for 10mbps half-duplex. */
+#define ANAR_10FULL DEF_BIT_06 /* Try for 10mbps full-duplex. */
+#define ANAR_100HALF DEF_BIT_07 /* Try for 100mbps half-duplex. */
+#define ANAR_100FULL DEF_BIT_08 /* Try for 100mbps full-duplex. */
+#define ANAR_100BASE4 DEF_BIT_09 /* Try for 100mbps 4k packets. */
+#define ANAR_RFAULT DEF_BIT_13 /* Say we can detect faults. */
+#define ANAR_LPACK DEF_BIT_14 /* Ack link partners response. */
+#define ANAR_NPAGE DEF_BIT_15 /* Next page bit. */
+
+#define ANAR_FULL (ANAR_100FULL | ANAR_10FULL | ANAR_CSMA)
+#define ANAR_ALL (ANAR_100BASE4 | ANAR_100FULL | ANAR_10FULL | ANAR_100HALF | ANAR_10HALF)
+
+/* ------------- PHY_ANLPAR REGISTER BITS ------------- */
+#define ANLPAR_10HALF (0x1 << 5) /* Can do 10mbps half-duplex. */
+#define ANLPAR_10FULL (0x1 << 6) /* Can do 10mbps full-duplex. */
+#define ANLPAR_100HALF (0x1 << 7) /* Can do 100mbps half-duplex. */
+#define ANLPAR_100FULL (0x1 << 8) /* Can do 100mbps full-duplex. */
+#define ANLPAR_100BASE4 (0x1 << 9) /* Can do 100mbps 4k packets. */
+#define ANLPAR_RFAULT (0x1 << 13) /* Link partner faulted. */
+#define ANLPAR_LPACK (0x1 << 14) /* Link partner acked us. */
+#define ANLPAR_NPAGE (0x1 << 15) /* Next page bit. */
+
+#define ANLPAR_DUPLEX (ANLPAR_10FULL | ANLPAR_100FULL)
+#define ANLPAR_100 (ANLPAR_100FULL | ANLPAR_100HALF | ANLPAR_100BASE4)
+
+/* -------------- PHY_ANER REGISTER BITS -------------- */
+#define ANER_NWAY (0x1 << 0) /* Can do N-way auto-negotiation. */
+#define ANER_LCWP (0x1 << 1) /* Got new RX page code word. */
+#define ANER_ENABLENPAGE (0x1 << 2) /* This enables npage words. */
+#define ANER_NPCAPABLE (0x1 << 3) /* Link partner supports npage. */
+#define ANER_MFAULTS (0x1 << 4) /* Multiple faults detected. */
+
+#endif