You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
676 lines
17 KiB
676 lines
17 KiB
/* |
|
* Copyright (c) 2019 Interay Solutions B.V. |
|
* Copyright (c) 2019 Oane Kingma |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#define DT_DRV_COMPAT silabs_gecko_ethernet |
|
|
|
/* Silicon Labs EFM32 Giant Gecko 11 Ethernet driver. |
|
* Limitations: |
|
* - no link monitoring through PHY interrupt |
|
*/ |
|
|
|
#include <zephyr/logging/log.h> |
|
LOG_MODULE_REGISTER(eth_gecko, CONFIG_ETHERNET_LOG_LEVEL); |
|
|
|
#include <soc.h> |
|
#include <zephyr/device.h> |
|
#include <zephyr/init.h> |
|
#include <zephyr/kernel.h> |
|
#include <errno.h> |
|
#include <zephyr/net/net_pkt.h> |
|
#include <zephyr/net/net_if.h> |
|
#include <zephyr/net/ethernet.h> |
|
#include <ethernet/eth_stats.h> |
|
#include <em_cmu.h> |
|
#include <zephyr/irq.h> |
|
|
|
#include "phy_gecko.h" |
|
#include "eth_gecko_priv.h" |
|
|
|
#include "eth.h" |
|
|
|
static uint8_t dma_tx_buffer[ETH_TX_BUF_COUNT][ETH_TX_BUF_SIZE] |
|
__aligned(ETH_BUF_ALIGNMENT); |
|
static uint8_t dma_rx_buffer[ETH_RX_BUF_COUNT][ETH_RX_BUF_SIZE] |
|
__aligned(ETH_BUF_ALIGNMENT); |
|
static struct eth_buf_desc dma_tx_desc_tab[ETH_TX_BUF_COUNT] |
|
__aligned(ETH_DESC_ALIGNMENT); |
|
static struct eth_buf_desc dma_rx_desc_tab[ETH_RX_BUF_COUNT] |
|
__aligned(ETH_DESC_ALIGNMENT); |
|
static uint32_t tx_buf_idx; |
|
static uint32_t rx_buf_idx; |
|
|
|
|
|
static void link_configure(ETH_TypeDef *eth, uint32_t flags) |
|
{ |
|
uint32_t val; |
|
|
|
__ASSERT_NO_MSG(eth != NULL); |
|
|
|
/* Disable receiver & transmitter */ |
|
eth->NETWORKCTRL &= ~(ETH_NETWORKCTRL_ENBTX | ETH_NETWORKCTRL_ENBRX); |
|
|
|
/* Set duplex mode and speed */ |
|
val = eth->NETWORKCFG; |
|
val &= ~(_ETH_NETWORKCFG_FULLDUPLEX_MASK | _ETH_NETWORKCFG_SPEED_MASK); |
|
val |= flags & |
|
(_ETH_NETWORKCFG_FULLDUPLEX_MASK | _ETH_NETWORKCFG_SPEED_MASK); |
|
eth->NETWORKCFG = val; |
|
|
|
/* Enable transmitter and receiver */ |
|
eth->NETWORKCTRL |= (ETH_NETWORKCTRL_ENBTX | ETH_NETWORKCTRL_ENBRX); |
|
} |
|
|
|
static void eth_gecko_setup_mac(const struct device *dev) |
|
{ |
|
const struct eth_gecko_dev_cfg *const cfg = dev->config; |
|
ETH_TypeDef *eth = cfg->regs; |
|
uint32_t link_status; |
|
int result; |
|
|
|
/* PHY auto-negotiate link parameters */ |
|
result = phy_gecko_auto_negotiate(&cfg->phy, &link_status); |
|
if (result < 0) { |
|
LOG_ERR("ETH PHY auto-negotiate sequence failed"); |
|
return; |
|
} |
|
|
|
LOG_INF("Speed %s Mb", |
|
link_status & ETH_NETWORKCFG_SPEED ? "100" : "10"); |
|
LOG_INF("%s duplex", |
|
link_status & ETH_NETWORKCFG_FULLDUPLEX ? "Full" : "Half"); |
|
|
|
/* Set up link parameters and enable receiver/transmitter */ |
|
link_configure(eth, link_status); |
|
} |
|
|
|
static void eth_init_tx_buf_desc(void) |
|
{ |
|
uint32_t address; |
|
int i; |
|
|
|
/* Initialize TX buffer descriptors */ |
|
for (i = 0; i < ETH_TX_BUF_COUNT; i++) { |
|
address = (uint32_t) dma_tx_buffer[i]; |
|
dma_tx_desc_tab[i].address = address; |
|
dma_tx_desc_tab[i].status = ETH_TX_USED; |
|
} |
|
|
|
/* Mark last descriptor entry with wrap flag */ |
|
dma_tx_desc_tab[i - 1].status |= ETH_TX_WRAP; |
|
tx_buf_idx = 0; |
|
} |
|
|
|
static void eth_init_rx_buf_desc(void) |
|
{ |
|
uint32_t address; |
|
int i; |
|
|
|
for (i = 0; i < ETH_RX_BUF_COUNT; i++) { |
|
address = (uint32_t) dma_rx_buffer[i]; |
|
dma_rx_desc_tab[i].address = address & ETH_RX_ADDRESS; |
|
dma_rx_desc_tab[i].status = 0; |
|
} |
|
|
|
/* Mark last descriptor entry with wrap flag */ |
|
dma_rx_desc_tab[i - 1].address |= ETH_RX_WRAP; |
|
rx_buf_idx = 0; |
|
} |
|
|
|
static void rx_error_handler(ETH_TypeDef *eth) |
|
{ |
|
__ASSERT_NO_MSG(eth != NULL); |
|
|
|
/* Stop reception */ |
|
ETH_RX_DISABLE(eth); |
|
|
|
/* Reset RX buffer descriptor list */ |
|
eth_init_rx_buf_desc(); |
|
eth->RXQPTR = (uint32_t)dma_rx_desc_tab; |
|
|
|
/* Restart reception */ |
|
ETH_RX_ENABLE(eth); |
|
} |
|
|
|
static struct net_pkt *frame_get(const struct device *dev) |
|
{ |
|
struct eth_gecko_dev_data *const dev_data = dev->data; |
|
const struct eth_gecko_dev_cfg *const cfg = dev->config; |
|
ETH_TypeDef *eth = cfg->regs; |
|
struct net_pkt *rx_frame = NULL; |
|
uint16_t frag_len, total_len; |
|
uint32_t sofIdx, eofIdx; |
|
uint32_t i, j; |
|
|
|
__ASSERT_NO_MSG(dev != NULL); |
|
__ASSERT_NO_MSG(dev_data != NULL); |
|
__ASSERT_NO_MSG(cfg != NULL); |
|
|
|
/* Preset indices and total frame length */ |
|
sofIdx = UINT32_MAX; |
|
eofIdx = UINT32_MAX; |
|
total_len = 0; |
|
|
|
/* Check if a full frame is received (SOF/EOF present) |
|
* and determine total length of frame |
|
*/ |
|
for (i = 0; i < ETH_RX_BUF_COUNT; i++) { |
|
j = (i + rx_buf_idx); |
|
if (j >= ETH_RX_BUF_COUNT) { |
|
j -= ETH_RX_BUF_COUNT; |
|
} |
|
|
|
/* Verify it is an ETH owned buffer */ |
|
if (!(dma_rx_desc_tab[j].address & ETH_RX_OWNERSHIP)) { |
|
/* No more ETH owned buffers to process */ |
|
break; |
|
} |
|
|
|
/* Check for SOF */ |
|
if (dma_rx_desc_tab[j].status & ETH_RX_SOF) { |
|
sofIdx = j; |
|
} |
|
|
|
if (sofIdx != UINT32_MAX) { |
|
total_len += (dma_rx_desc_tab[j].status & |
|
ETH_RX_LENGTH); |
|
|
|
/* Check for EOF */ |
|
if (dma_rx_desc_tab[j].status & ETH_RX_EOF) { |
|
eofIdx = j; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
LOG_DBG("sof/eof: %u/%u, rx_buf_idx: %u, len: %u", sofIdx, eofIdx, |
|
rx_buf_idx, total_len); |
|
|
|
/* Verify we found a full frame */ |
|
if (eofIdx != UINT32_MAX) { |
|
/* Allocate room for full frame */ |
|
rx_frame = net_pkt_rx_alloc_with_buffer(dev_data->iface, |
|
total_len, AF_UNSPEC, 0, K_NO_WAIT); |
|
if (!rx_frame) { |
|
LOG_ERR("Failed to obtain RX buffer"); |
|
ETH_RX_DISABLE(eth); |
|
eth_init_rx_buf_desc(); |
|
eth->RXQPTR = (uint32_t)dma_rx_desc_tab; |
|
ETH_RX_ENABLE(eth); |
|
return rx_frame; |
|
} |
|
|
|
/* Copy frame (fragments)*/ |
|
j = sofIdx; |
|
while (total_len) { |
|
frag_len = MIN(total_len, ETH_RX_BUF_SIZE); |
|
LOG_DBG("frag: %u, fraglen: %u, rx_buf_idx: %u", j, |
|
frag_len, rx_buf_idx); |
|
if (net_pkt_write(rx_frame, &dma_rx_buffer[j], |
|
frag_len) < 0) { |
|
LOG_ERR("Failed to append RX buffer"); |
|
dma_rx_desc_tab[j].address &= |
|
~ETH_RX_OWNERSHIP; |
|
net_pkt_unref(rx_frame); |
|
rx_frame = NULL; |
|
break; |
|
} |
|
|
|
dma_rx_desc_tab[j].address &= ~ETH_RX_OWNERSHIP; |
|
|
|
total_len -= frag_len; |
|
if (++j >= ETH_RX_BUF_COUNT) { |
|
j -= ETH_RX_BUF_COUNT; |
|
} |
|
|
|
if (++rx_buf_idx >= ETH_RX_BUF_COUNT) { |
|
rx_buf_idx -= ETH_RX_BUF_COUNT; |
|
} |
|
} |
|
} |
|
|
|
return rx_frame; |
|
} |
|
|
|
static void eth_rx(const struct device *dev) |
|
{ |
|
struct eth_gecko_dev_data *const dev_data = dev->data; |
|
struct net_pkt *rx_frame; |
|
int res = 0; |
|
|
|
__ASSERT_NO_MSG(dev != NULL); |
|
__ASSERT_NO_MSG(dev_data != NULL); |
|
|
|
/* Iterate across (possibly multiple) frames */ |
|
rx_frame = frame_get(dev); |
|
while (rx_frame) { |
|
/* All data for this frame received */ |
|
res = net_recv_data(dev_data->iface, rx_frame); |
|
if (res < 0) { |
|
LOG_ERR("Failed to enqueue frame into RX queue: %d", |
|
res); |
|
eth_stats_update_errors_rx(dev_data->iface); |
|
net_pkt_unref(rx_frame); |
|
} |
|
|
|
/* Check if more frames are received */ |
|
rx_frame = frame_get(dev); |
|
} |
|
} |
|
|
|
static int eth_tx(const struct device *dev, struct net_pkt *pkt) |
|
{ |
|
struct eth_gecko_dev_data *const dev_data = dev->data; |
|
const struct eth_gecko_dev_cfg *const cfg = dev->config; |
|
ETH_TypeDef *eth = cfg->regs; |
|
uint16_t total_len; |
|
uint8_t *dma_buffer; |
|
int res = 0; |
|
|
|
__ASSERT_NO_MSG(dev != NULL); |
|
__ASSERT_NO_MSG(dev_data != NULL); |
|
__ASSERT_NO_MSG(cfg != NULL); |
|
|
|
__ASSERT(pkt, "Buf pointer is NULL"); |
|
__ASSERT(pkt->frags, "Frame data missing"); |
|
|
|
/* Determine length of frame */ |
|
total_len = net_pkt_get_len(pkt); |
|
if (total_len > ETH_TX_BUF_SIZE) { |
|
LOG_ERR("PKT to big"); |
|
res = -EIO; |
|
goto error; |
|
} |
|
|
|
if (k_sem_take(&dev_data->tx_sem, K_MSEC(100)) != 0) { |
|
LOG_ERR("TX process did not complete within 100ms"); |
|
res = -EIO; |
|
goto error; |
|
} |
|
|
|
/* Make sure current buffer is available for writing */ |
|
if (!(dma_tx_desc_tab[tx_buf_idx].status & ETH_TX_USED)) { |
|
LOG_ERR("Buffer already in use"); |
|
res = -EIO; |
|
goto error; |
|
} |
|
|
|
dma_buffer = (uint8_t *)dma_tx_desc_tab[tx_buf_idx].address; |
|
if (net_pkt_read(pkt, dma_buffer, total_len)) { |
|
LOG_ERR("Failed to read packet into buffer"); |
|
res = -EIO; |
|
goto error; |
|
} |
|
|
|
if (tx_buf_idx < (ETH_TX_BUF_COUNT - 1)) { |
|
dma_tx_desc_tab[tx_buf_idx].status = |
|
(total_len & ETH_TX_LENGTH) | ETH_TX_LAST; |
|
tx_buf_idx++; |
|
} else { |
|
dma_tx_desc_tab[tx_buf_idx].status = |
|
(total_len & ETH_TX_LENGTH) | (ETH_TX_LAST | |
|
ETH_TX_WRAP); |
|
tx_buf_idx = 0; |
|
} |
|
|
|
/* Kick off transmission */ |
|
eth->NETWORKCTRL |= ETH_NETWORKCTRL_TXSTRT; |
|
|
|
error: |
|
return res; |
|
} |
|
|
|
static void rx_thread(void *arg1, void *unused1, void *unused2) |
|
{ |
|
const struct device *dev = (const struct device *)arg1; |
|
struct eth_gecko_dev_data *const dev_data = dev->data; |
|
const struct eth_gecko_dev_cfg *const cfg = dev->config; |
|
int res; |
|
|
|
__ASSERT_NO_MSG(arg1 != NULL); |
|
ARG_UNUSED(unused1); |
|
ARG_UNUSED(unused2); |
|
__ASSERT_NO_MSG(dev_data != NULL); |
|
__ASSERT_NO_MSG(cfg != NULL); |
|
|
|
while (1) { |
|
res = k_sem_take(&dev_data->rx_sem, K_MSEC( |
|
CONFIG_ETH_GECKO_CARRIER_CHECK_RX_IDLE_TIMEOUT_MS)); |
|
if (res == 0) { |
|
if (dev_data->link_up != true) { |
|
dev_data->link_up = true; |
|
LOG_INF("Link up"); |
|
eth_gecko_setup_mac(dev); |
|
net_eth_carrier_on(dev_data->iface); |
|
} |
|
|
|
/* Process received data */ |
|
eth_rx(dev); |
|
} else if (res == -EAGAIN) { |
|
if (phy_gecko_is_linked(&cfg->phy)) { |
|
if (dev_data->link_up != true) { |
|
dev_data->link_up = true; |
|
LOG_INF("Link up"); |
|
eth_gecko_setup_mac(dev); |
|
net_eth_carrier_on(dev_data->iface); |
|
} |
|
} else { |
|
if (dev_data->link_up != false) { |
|
dev_data->link_up = false; |
|
LOG_INF("Link down"); |
|
net_eth_carrier_off(dev_data->iface); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
static void eth_isr(const struct device *dev) |
|
{ |
|
struct eth_gecko_dev_data *const dev_data = dev->data; |
|
const struct eth_gecko_dev_cfg *const cfg = dev->config; |
|
ETH_TypeDef *eth = cfg->regs; |
|
uint32_t int_clr = 0; |
|
uint32_t int_stat = eth->IFCR; |
|
uint32_t tx_irq_mask = (ETH_IENS_TXCMPLT | ETH_IENS_TXUNDERRUN | |
|
ETH_IENS_RTRYLMTORLATECOL | |
|
ETH_IENS_TXUSEDBITREAD | |
|
ETH_IENS_AMBAERR); |
|
uint32_t rx_irq_mask = (ETH_IENS_RXCMPLT | ETH_IENS_RXUSEDBITREAD); |
|
|
|
__ASSERT_NO_MSG(dev_data != NULL); |
|
__ASSERT_NO_MSG(cfg != NULL); |
|
|
|
/* Receive handling */ |
|
if (int_stat & rx_irq_mask) { |
|
if (int_stat & ETH_IENS_RXCMPLT) { |
|
/* Receive complete */ |
|
k_sem_give(&dev_data->rx_sem); |
|
} else { |
|
/* Receive error */ |
|
LOG_DBG("RX Error"); |
|
rx_error_handler(eth); |
|
} |
|
|
|
int_clr |= rx_irq_mask; |
|
} |
|
|
|
/* Transmit handling */ |
|
if (int_stat & tx_irq_mask) { |
|
if (int_stat & ETH_IENS_TXCMPLT) { |
|
/* Transmit complete */ |
|
} else { |
|
/* Transmit error: no actual handling, the current |
|
* buffer is no longer used and we release the |
|
* semaphore which signals the user thread to |
|
* start TX of a new packet |
|
*/ |
|
} |
|
|
|
int_clr |= tx_irq_mask; |
|
|
|
/* Signal TX thread we're ready to start transmission */ |
|
k_sem_give(&dev_data->tx_sem); |
|
} |
|
|
|
/* Clear interrupts */ |
|
eth->IFCR = int_clr; |
|
} |
|
|
|
static void eth_init_clocks(const struct device *dev) |
|
{ |
|
__ASSERT_NO_MSG(dev != NULL); |
|
|
|
CMU_ClockEnable(cmuClock_HFPER, true); |
|
CMU_ClockEnable(cmuClock_ETH, true); |
|
} |
|
|
|
static void eth_init_pins(const struct device *dev) |
|
{ |
|
const struct eth_gecko_dev_cfg *const cfg = dev->config; |
|
ETH_TypeDef *eth = cfg->regs; |
|
uint32_t idx; |
|
|
|
__ASSERT_NO_MSG(dev != NULL); |
|
__ASSERT_NO_MSG(cfg != NULL); |
|
|
|
eth->ROUTELOC1 = 0; |
|
eth->ROUTEPEN = 0; |
|
|
|
#if DT_INST_NODE_HAS_PROP(0, location_rmii) |
|
for (idx = 0; idx < ARRAY_SIZE(cfg->pin_list->rmii); idx++) { |
|
GPIO_PinModeSet(cfg->pin_list->rmii[idx].port, cfg->pin_list->rmii[idx].pin, |
|
cfg->pin_list->rmii[idx].mode, cfg->pin_list->rmii[idx].out); |
|
} |
|
|
|
eth->ROUTELOC1 |= (DT_INST_PROP(0, location_rmii) << |
|
_ETH_ROUTELOC1_RMIILOC_SHIFT); |
|
eth->ROUTEPEN |= ETH_ROUTEPEN_RMIIPEN; |
|
#endif |
|
|
|
#if DT_INST_NODE_HAS_PROP(0, location_mdio) |
|
for (idx = 0; idx < ARRAY_SIZE(cfg->pin_list->mdio); idx++) { |
|
GPIO_PinModeSet(cfg->pin_list->mdio[idx].port, cfg->pin_list->mdio[idx].pin, |
|
cfg->pin_list->mdio[idx].mode, cfg->pin_list->mdio[idx].out); |
|
} |
|
|
|
eth->ROUTELOC1 |= (DT_INST_PROP(0, location_mdio) << |
|
_ETH_ROUTELOC1_MDIOLOC_SHIFT); |
|
eth->ROUTEPEN |= ETH_ROUTEPEN_MDIOPEN; |
|
#endif |
|
|
|
} |
|
|
|
static int eth_init(const struct device *dev) |
|
{ |
|
const struct eth_gecko_dev_cfg *const cfg = dev->config; |
|
ETH_TypeDef *eth = cfg->regs; |
|
|
|
__ASSERT_NO_MSG(dev != NULL); |
|
__ASSERT_NO_MSG(cfg != NULL); |
|
|
|
/* Enable clocks */ |
|
eth_init_clocks(dev); |
|
|
|
/* Connect pins to peripheral */ |
|
eth_init_pins(dev); |
|
|
|
#if DT_INST_NODE_HAS_PROP(0, location_rmii) |
|
/* Enable global clock and RMII operation */ |
|
eth->CTRL = ETH_CTRL_GBLCLKEN | ETH_CTRL_MIISEL_RMII; |
|
#endif |
|
|
|
/* Connect and enable IRQ */ |
|
cfg->config_func(); |
|
|
|
LOG_INF("Device %s initialized", dev->name); |
|
|
|
return 0; |
|
} |
|
|
|
static void generate_mac(uint8_t mac_addr[6]) |
|
{ |
|
#if DT_INST_PROP(0, zephyr_random_mac_address) |
|
gen_random_mac(mac_addr, SILABS_OUI_B0, SILABS_OUI_B1, SILABS_OUI_B2); |
|
#elif !NODE_HAS_VALID_MAC_ADDR(DT_DRV_INST(0)) |
|
mac_addr[0] = DEVINFO->EUI48H >> 8; |
|
mac_addr[1] = DEVINFO->EUI48H >> 0; |
|
mac_addr[2] = DEVINFO->EUI48L >> 24; |
|
mac_addr[3] = DEVINFO->EUI48L >> 16; |
|
mac_addr[4] = DEVINFO->EUI48L >> 8; |
|
mac_addr[5] = DEVINFO->EUI48L >> 0; |
|
#endif |
|
} |
|
|
|
static void eth_iface_init(struct net_if *iface) |
|
{ |
|
const struct device *dev = net_if_get_device(iface); |
|
struct eth_gecko_dev_data *const dev_data = dev->data; |
|
const struct eth_gecko_dev_cfg *const cfg = dev->config; |
|
ETH_TypeDef *eth = cfg->regs; |
|
int result; |
|
|
|
__ASSERT_NO_MSG(iface != NULL); |
|
__ASSERT_NO_MSG(dev != NULL); |
|
__ASSERT_NO_MSG(dev_data != NULL); |
|
__ASSERT_NO_MSG(cfg != NULL); |
|
|
|
LOG_DBG("eth_initialize"); |
|
|
|
dev_data->iface = iface; |
|
dev_data->link_up = false; |
|
ethernet_init(iface); |
|
|
|
net_if_carrier_off(iface); |
|
|
|
/* Generate MAC address, possibly used for filtering */ |
|
generate_mac(dev_data->mac_addr); |
|
|
|
/* Set link address */ |
|
LOG_DBG("MAC %02x:%02x:%02x:%02x:%02x:%02x", |
|
dev_data->mac_addr[0], dev_data->mac_addr[1], |
|
dev_data->mac_addr[2], dev_data->mac_addr[3], |
|
dev_data->mac_addr[4], dev_data->mac_addr[5]); |
|
|
|
net_if_set_link_addr(iface, dev_data->mac_addr, |
|
sizeof(dev_data->mac_addr), NET_LINK_ETHERNET); |
|
|
|
/* Disable transmit and receive circuits */ |
|
eth->NETWORKCTRL = 0; |
|
eth->NETWORKCFG = 0; |
|
|
|
/* Filtering MAC addresses */ |
|
eth->SPECADDR1BOTTOM = |
|
(dev_data->mac_addr[0] << 0) | |
|
(dev_data->mac_addr[1] << 8) | |
|
(dev_data->mac_addr[2] << 16) | |
|
(dev_data->mac_addr[3] << 24); |
|
eth->SPECADDR1TOP = |
|
(dev_data->mac_addr[4] << 0) | |
|
(dev_data->mac_addr[5] << 8); |
|
|
|
eth->SPECADDR2BOTTOM = 0; |
|
eth->SPECADDR3BOTTOM = 0; |
|
eth->SPECADDR4BOTTOM = 0; |
|
|
|
/* Initialise hash table */ |
|
eth->HASHBOTTOM = 0; |
|
eth->HASHTOP = 0; |
|
|
|
/* Initialise DMA buffers */ |
|
eth_init_tx_buf_desc(); |
|
eth_init_rx_buf_desc(); |
|
|
|
/* Point to locations of TX/RX DMA descriptor lists */ |
|
eth->TXQPTR = (uint32_t)dma_tx_desc_tab; |
|
eth->RXQPTR = (uint32_t)dma_rx_desc_tab; |
|
|
|
/* DMA RX size configuration */ |
|
eth->DMACFG = (eth->DMACFG & ~_ETH_DMACFG_RXBUFSIZE_MASK) | |
|
((ETH_RX_BUF_SIZE / 64) << _ETH_DMACFG_RXBUFSIZE_SHIFT); |
|
|
|
/* Clear status/interrupt registers */ |
|
eth->IFCR |= _ETH_IFCR_MASK; |
|
eth->TXSTATUS = ETH_TXSTATUS_TXUNDERRUN | ETH_TXSTATUS_TXCMPLT | |
|
ETH_TXSTATUS_AMBAERR | ETH_TXSTATUS_TXGO | |
|
ETH_TXSTATUS_RETRYLMTEXCD | ETH_TXSTATUS_COLOCCRD | |
|
ETH_TXSTATUS_USEDBITREAD; |
|
eth->RXSTATUS = ETH_RXSTATUS_RESPNOTOK | ETH_RXSTATUS_RXOVERRUN | |
|
ETH_RXSTATUS_FRMRX | ETH_RXSTATUS_BUFFNOTAVAIL; |
|
|
|
/* Enable interrupts */ |
|
eth->IENS = ETH_IENS_RXCMPLT | |
|
ETH_IENS_RXUSEDBITREAD | |
|
ETH_IENS_TXCMPLT | |
|
ETH_IENS_TXUNDERRUN | |
|
ETH_IENS_RTRYLMTORLATECOL | |
|
ETH_IENS_TXUSEDBITREAD | |
|
ETH_IENS_AMBAERR; |
|
|
|
/* Additional DMA configuration */ |
|
eth->DMACFG |= _ETH_DMACFG_AMBABRSTLEN_MASK | |
|
ETH_DMACFG_FRCDISCARDONERR | |
|
ETH_DMACFG_TXPBUFTCPEN; |
|
eth->DMACFG &= ~ETH_DMACFG_HDRDATASPLITEN; |
|
|
|
/* Set network configuration */ |
|
eth->NETWORKCFG |= ETH_NETWORKCFG_FCSREMOVE | |
|
ETH_NETWORKCFG_UNICASTHASHEN | |
|
ETH_NETWORKCFG_MULTICASTHASHEN | |
|
ETH_NETWORKCFG_RX1536BYTEFRAMES | |
|
ETH_NETWORKCFG_RXCHKSUMOFFLOADEN; |
|
|
|
/* Setup PHY management port */ |
|
eth->NETWORKCFG |= (4 << _ETH_NETWORKCFG_MDCCLKDIV_SHIFT) & |
|
_ETH_NETWORKCFG_MDCCLKDIV_MASK; |
|
eth->NETWORKCTRL |= ETH_NETWORKCTRL_MANPORTEN; |
|
|
|
/* Initialise PHY */ |
|
result = phy_gecko_init(&cfg->phy); |
|
if (result < 0) { |
|
LOG_ERR("ETH PHY Initialization Error"); |
|
return; |
|
} |
|
|
|
/* Initialise TX/RX semaphores */ |
|
k_sem_init(&dev_data->tx_sem, 1, ETH_TX_BUF_COUNT); |
|
k_sem_init(&dev_data->rx_sem, 0, K_SEM_MAX_LIMIT); |
|
|
|
/* Start interruption-poll thread */ |
|
k_thread_create(&dev_data->rx_thread, dev_data->rx_thread_stack, |
|
K_KERNEL_STACK_SIZEOF(dev_data->rx_thread_stack), |
|
rx_thread, (void *) dev, NULL, NULL, |
|
K_PRIO_COOP(CONFIG_ETH_GECKO_RX_THREAD_PRIO), |
|
0, K_NO_WAIT); |
|
} |
|
|
|
static enum ethernet_hw_caps eth_gecko_get_capabilities(const struct device *dev) |
|
{ |
|
ARG_UNUSED(dev); |
|
|
|
return (ETHERNET_LINK_10BASE | ETHERNET_LINK_100BASE); |
|
} |
|
|
|
static const struct ethernet_api eth_api = { |
|
.iface_api.init = eth_iface_init, |
|
.get_capabilities = eth_gecko_get_capabilities, |
|
.send = eth_tx, |
|
}; |
|
|
|
static void eth0_irq_config(void) |
|
{ |
|
IRQ_CONNECT(DT_INST_IRQN(0), |
|
DT_INST_IRQ(0, priority), eth_isr, |
|
DEVICE_DT_INST_GET(0), 0); |
|
irq_enable(DT_INST_IRQN(0)); |
|
} |
|
|
|
static const struct eth_gecko_pin_list pins_eth0 = { |
|
.mdio = PIN_LIST_PHY, |
|
.rmii = PIN_LIST_RMII |
|
}; |
|
|
|
static const struct eth_gecko_dev_cfg eth0_config = { |
|
.regs = (ETH_TypeDef *) |
|
DT_INST_REG_ADDR(0), |
|
.pin_list = &pins_eth0, |
|
.pin_list_size = ARRAY_SIZE(pins_eth0.mdio) + |
|
ARRAY_SIZE(pins_eth0.rmii), |
|
.config_func = eth0_irq_config, |
|
.phy = { (ETH_TypeDef *) |
|
DT_INST_REG_ADDR(0), |
|
DT_INST_PROP(0, phy_address) }, |
|
}; |
|
|
|
static struct eth_gecko_dev_data eth0_data = { |
|
#if NODE_HAS_VALID_MAC_ADDR(DT_DRV_INST(0)) |
|
.mac_addr = DT_INST_PROP(0, local_mac_address), |
|
#endif |
|
}; |
|
|
|
ETH_NET_DEVICE_DT_INST_DEFINE(0, eth_init, |
|
NULL, ð0_data, ð0_config, |
|
CONFIG_ETH_INIT_PRIORITY, ð_api, ETH_GECKO_MTU);
|
|
|