mirror of https://github.com/ARMmbed/mbed-os.git
Add EFM32GG11 Ethernet driver
parent
001844231b
commit
da377c6ed3
|
|
@ -124,6 +124,9 @@
|
|||
},
|
||||
"LPC546XX": {
|
||||
"mem-size": 36496
|
||||
},
|
||||
"EFM32GG11-STK3701": {
|
||||
"mem-size": 36560
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,712 @@
|
|||
/***************************************************************************//**
|
||||
* @file sl_emac.cpp
|
||||
*******************************************************************************
|
||||
* @section License
|
||||
* <b>(C) Copyright 2018 Silicon Labs, http://www.silabs.com</b>
|
||||
*******************************************************************************
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
|
||||
#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<SL_EMAC *>(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
|
||||
|
|
@ -0,0 +1,256 @@
|
|||
/***************************************************************************//**
|
||||
* @file sl_eth_phy.h
|
||||
*******************************************************************************
|
||||
* @section License
|
||||
* <b>modifications (C) Copyright 2018 Silicon Labs, http://www.silabs.com</b>
|
||||
* <b>original Copyright (c) 2015 ARM Limited</b>
|
||||
*******************************************************************************
|
||||
*
|
||||
* 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_ */
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/***************************************************************************//**
|
||||
* @file sl_emac_config.h
|
||||
*******************************************************************************
|
||||
* @section License
|
||||
* <b>(C) Copyright 2018 Silicon Labs, http://www.silabs.com</b>
|
||||
*******************************************************************************
|
||||
*
|
||||
* 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 */
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
/***************************************************************************//**
|
||||
* @file sl_eth_hw.c
|
||||
*******************************************************************************
|
||||
* @section License
|
||||
* <b>(C) Copyright 2017 Silicon Labs, http://www.silabs.com</b>
|
||||
*******************************************************************************
|
||||
*
|
||||
* 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
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
/***************************************************************************//**
|
||||
* @file sl_eth_hw.h
|
||||
*******************************************************************************
|
||||
* @section License
|
||||
* <b>(C) Copyright 2018 Silicon Labs, http://www.silabs.com</b>
|
||||
*******************************************************************************
|
||||
*
|
||||
* 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
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
/***************************************************************************//**
|
||||
* @file sl_eth_phy.h
|
||||
*******************************************************************************
|
||||
* @section License
|
||||
* <b>(C) Copyright 2017 Silicon Labs, http://www.silabs.com</b>
|
||||
*******************************************************************************
|
||||
*
|
||||
* 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
|
||||
Loading…
Reference in New Issue