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