drivers/ethernet: Add support for SiLabs Giant Gecko GG11
Ethernet MAC present in Silicon Labs EFM32GG11B4xx and EFM32GG11B8xx SoCs. DMA based driver with support for link up/down detection. Signed-off-by: Oane Kingma <o.kingma@interay.com>
This commit is contained in:
parent
c52787c4d1
commit
437587af20
|
@ -7,6 +7,11 @@ zephyr_sources_ifdef(CONFIG_ETH_SAM_GMAC
|
|||
phy_sam_gmac.c
|
||||
)
|
||||
|
||||
zephyr_sources_ifdef(CONFIG_ETH_GECKO
|
||||
eth_gecko.c
|
||||
phy_gecko.c
|
||||
)
|
||||
|
||||
zephyr_sources_ifdef(CONFIG_ETH_STELLARIS eth_stellaris.c)
|
||||
zephyr_sources_ifdef(CONFIG_ETH_E1000 eth_e1000.c)
|
||||
zephyr_sources_ifdef(CONFIG_ETH_ENC28J60 eth_enc28j60.c)
|
||||
|
|
|
@ -31,5 +31,6 @@ source "drivers/ethernet/Kconfig.smsc911x"
|
|||
source "drivers/ethernet/Kconfig.native_posix"
|
||||
source "drivers/ethernet/Kconfig.stellaris"
|
||||
source "drivers/ethernet/Kconfig.liteeth"
|
||||
source "drivers/ethernet/Kconfig.gecko"
|
||||
|
||||
endmenu # "Ethernet Drivers"
|
||||
|
|
96
drivers/ethernet/Kconfig.gecko
Normal file
96
drivers/ethernet/Kconfig.gecko
Normal file
|
@ -0,0 +1,96 @@
|
|||
# Copyright (c) 2019 Interay Solutions B.V.
|
||||
# Copyright (c) 2019 Oane Kingma
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig ETH_GECKO
|
||||
bool "SiLabs Gecko Ethernet driver"
|
||||
help
|
||||
Enable Ethernet driver for Silicon Labs Gecko chips.
|
||||
|
||||
if ETH_GECKO
|
||||
|
||||
config ETH_GECKO_NAME
|
||||
string "Device name"
|
||||
default "ETH_0"
|
||||
help
|
||||
Device name allows user to obtain a handle to the device object
|
||||
required by all driver API functions. Device name has to be unique.
|
||||
|
||||
config ETH_GECKO_IRQ_PRI
|
||||
int "Interrupt priority"
|
||||
default 0
|
||||
help
|
||||
IRQ priority of Ethernet device
|
||||
|
||||
config ETH_GECKO_RX_THREAD_STACK_SIZE
|
||||
int "RX thread stack size"
|
||||
default 1500
|
||||
help
|
||||
RX thread stack size
|
||||
|
||||
config ETH_GECKO_RX_THREAD_PRIO
|
||||
int "RX thread priority"
|
||||
default 2
|
||||
help
|
||||
RX thread priority
|
||||
|
||||
choice ETH_GECKO_MAC_SELECT
|
||||
prompt "MAC address"
|
||||
help
|
||||
Choose how to configure MAC address.
|
||||
|
||||
config ETH_GECKO_RANDOM_MAC
|
||||
bool "Random MAC address"
|
||||
help
|
||||
Generate a random MAC address dynamically.
|
||||
|
||||
config ETH_GECKO_MAC_MANUAL
|
||||
bool "Manual"
|
||||
help
|
||||
Assign an arbitrary MAC address.
|
||||
|
||||
endchoice # ETH_GECKO_MAC_SELECT
|
||||
|
||||
if ETH_GECKO_MAC_MANUAL
|
||||
|
||||
config ETH_GECKO_MAC0
|
||||
hex "MAC Address Byte 0"
|
||||
default 0
|
||||
range 0 0xff
|
||||
|
||||
config ETH_GECKO_MAC1
|
||||
hex "MAC Address Byte 1"
|
||||
default 0
|
||||
range 0 0xff
|
||||
|
||||
config ETH_GECKO_MAC2
|
||||
hex "MAC Address Byte 2"
|
||||
default 0
|
||||
range 0 0xff
|
||||
|
||||
config ETH_GECKO_MAC3
|
||||
hex "MAC Address Byte 3"
|
||||
default 0
|
||||
range 0 0xff
|
||||
|
||||
config ETH_GECKO_MAC4
|
||||
hex "MAC Address Byte 4"
|
||||
default 0
|
||||
range 0 0xff
|
||||
|
||||
config ETH_GECKO_MAC5
|
||||
hex "MAC Address Byte 5"
|
||||
default 0
|
||||
range 0 0xff
|
||||
|
||||
endif # ETH_GECKO_MAC_MANUAL
|
||||
|
||||
config ETH_GECKO_CARRIER_CHECK_RX_IDLE_TIMEOUT_MS
|
||||
int "Carrier check timeout period (ms)"
|
||||
default 500
|
||||
range 100 30000
|
||||
help
|
||||
Set the RX idle timeout period in milliseconds after which the
|
||||
PHY's carrier status is re-evaluated.
|
||||
|
||||
endif # ETH_GECKO
|
676
drivers/ethernet/eth_gecko.c
Normal file
676
drivers/ethernet/eth_gecko.c
Normal file
|
@ -0,0 +1,676 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Interay Solutions B.V.
|
||||
* Copyright (c) 2019 Oane Kingma
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* Silicon Labs EFM32 Giant Gecko 11 Ethernet driver.
|
||||
* Limitations:
|
||||
* - no link monitoring through PHY interrupt
|
||||
*/
|
||||
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER(eth_gecko, CONFIG_ETHERNET_LOG_LEVEL);
|
||||
|
||||
#include <soc.h>
|
||||
#include <device.h>
|
||||
#include <init.h>
|
||||
#include <kernel.h>
|
||||
#include <errno.h>
|
||||
#include <net/net_pkt.h>
|
||||
#include <net/net_if.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <ethernet/eth_stats.h>
|
||||
#include <em_cmu.h>
|
||||
|
||||
#include "phy_gecko.h"
|
||||
#include "eth_gecko_priv.h"
|
||||
|
||||
|
||||
static u8_t dma_tx_buffer[ETH_TX_BUF_COUNT][ETH_TX_BUF_SIZE]
|
||||
__aligned(ETH_BUF_ALIGNMENT);
|
||||
static u8_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 u32_t tx_buf_idx;
|
||||
static u32_t rx_buf_idx;
|
||||
|
||||
|
||||
static void link_configure(ETH_TypeDef *eth, u32_t flags)
|
||||
{
|
||||
u32_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_init_tx_buf_desc(void)
|
||||
{
|
||||
u32_t address;
|
||||
int i;
|
||||
|
||||
/* Initialize TX buffer descriptors */
|
||||
for (i = 0; i < ETH_TX_BUF_COUNT; i++) {
|
||||
address = (u32_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)
|
||||
{
|
||||
u32_t address;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ETH_RX_BUF_COUNT; i++) {
|
||||
address = (u32_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 = (u32_t)dma_rx_desc_tab;
|
||||
|
||||
/* Restart reception */
|
||||
ETH_RX_ENABLE(eth);
|
||||
}
|
||||
|
||||
static struct net_pkt *frame_get(struct device *dev)
|
||||
{
|
||||
struct eth_gecko_dev_data *const dev_data = DEV_DATA(dev);
|
||||
const struct eth_gecko_dev_cfg *const cfg = DEV_CFG(dev);
|
||||
ETH_TypeDef *eth = cfg->regs;
|
||||
struct net_pkt *rx_frame = NULL;
|
||||
u16_t frag_len, total_len;
|
||||
u32_t sofIdx, eofIdx;
|
||||
u32_t i, j;
|
||||
|
||||
__ASSERT_NO_MSG(dev != NULL);
|
||||
__ASSERT_NO_MSG(dev_data != NULL);
|
||||
__ASSERT_NO_MSG(cfg != NULL);
|
||||
|
||||
/* Preset indeces 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 = (u32_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(struct device *dev)
|
||||
{
|
||||
struct eth_gecko_dev_data *const dev_data = DEV_DATA(dev);
|
||||
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(struct device *dev, struct net_pkt *pkt)
|
||||
{
|
||||
struct eth_gecko_dev_data *const dev_data = DEV_DATA(dev);
|
||||
const struct eth_gecko_dev_cfg *const cfg = DEV_CFG(dev);
|
||||
ETH_TypeDef *eth = cfg->regs;
|
||||
u16_t total_len;
|
||||
u8_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 = (u8_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)
|
||||
{
|
||||
struct device *dev = (struct device *)arg1;
|
||||
struct eth_gecko_dev_data *const dev_data = DEV_DATA(dev);
|
||||
const struct eth_gecko_dev_cfg *const cfg = DEV_CFG(dev);
|
||||
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;
|
||||
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;
|
||||
net_eth_carrier_on(dev_data->iface);
|
||||
}
|
||||
} else {
|
||||
if (dev_data->link_up != false) {
|
||||
dev_data->link_up = false;
|
||||
net_eth_carrier_off(dev_data->iface);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void eth_isr(void *arg)
|
||||
{
|
||||
struct device *dev = (struct device *)arg;
|
||||
struct eth_gecko_dev_data *const dev_data = DEV_DATA(dev);
|
||||
const struct eth_gecko_dev_cfg *const cfg = DEV_CFG(dev);
|
||||
ETH_TypeDef *eth = cfg->regs;
|
||||
u32_t int_clr = 0;
|
||||
u32_t int_stat = eth->IFCR;
|
||||
u32_t tx_irq_mask = (ETH_IENS_TXCMPLT | ETH_IENS_TXUNDERRUN |
|
||||
ETH_IENS_RTRYLMTORLATECOL |
|
||||
ETH_IENS_TXUSEDBITREAD |
|
||||
ETH_IENS_AMBAERR);
|
||||
u32_t rx_irq_mask = (ETH_IENS_RXCMPLT | ETH_IENS_RXUSEDBITREAD);
|
||||
|
||||
__ASSERT_NO_MSG(arg != NULL);
|
||||
__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(struct device *dev)
|
||||
{
|
||||
__ASSERT_NO_MSG(dev != NULL);
|
||||
|
||||
CMU_ClockEnable(cmuClock_HFPER, true);
|
||||
CMU_ClockEnable(cmuClock_ETH, true);
|
||||
}
|
||||
|
||||
static void eth_init_pins(struct device *dev)
|
||||
{
|
||||
const struct eth_gecko_dev_cfg *const cfg = DEV_CFG(dev);
|
||||
ETH_TypeDef *eth = cfg->regs;
|
||||
u32_t idx;
|
||||
|
||||
__ASSERT_NO_MSG(dev != NULL);
|
||||
__ASSERT_NO_MSG(cfg != NULL);
|
||||
|
||||
eth->ROUTELOC1 = 0;
|
||||
eth->ROUTEPEN = 0;
|
||||
|
||||
#if defined(DT_INST_0_SILABS_GECKO_ETHERNET_LOCATION_RMII)
|
||||
for (idx = 0; idx < ARRAY_SIZE(cfg->pin_list->rmii); idx++)
|
||||
soc_gpio_configure(&cfg->pin_list->rmii[idx]);
|
||||
|
||||
eth->ROUTELOC1 |= (DT_INST_0_SILABS_GECKO_ETHERNET_LOCATION_RMII <<
|
||||
_ETH_ROUTELOC1_RMIILOC_SHIFT);
|
||||
eth->ROUTEPEN |= ETH_ROUTEPEN_RMIIPEN;
|
||||
#endif
|
||||
|
||||
#if defined(DT_INST_0_SILABS_GECKO_ETHERNET_LOCATION_MDIO)
|
||||
for (idx = 0; idx < ARRAY_SIZE(cfg->pin_list->mdio); idx++)
|
||||
soc_gpio_configure(&cfg->pin_list->mdio[idx]);
|
||||
|
||||
eth->ROUTELOC1 |= (DT_INST_0_SILABS_GECKO_ETHERNET_LOCATION_MDIO <<
|
||||
_ETH_ROUTELOC1_MDIOLOC_SHIFT);
|
||||
eth->ROUTEPEN |= ETH_ROUTEPEN_MDIOPEN;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
static int eth_init(struct device *dev)
|
||||
{
|
||||
const struct eth_gecko_dev_cfg *const cfg = DEV_CFG(dev);
|
||||
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 defined(DT_INST_0_SILABS_GECKO_ETHERNET_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(dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_ETH_GECKO_RANDOM_MAC)
|
||||
static void generate_random_mac(u8_t mac_addr[6])
|
||||
{
|
||||
u32_t entropy;
|
||||
|
||||
entropy = sys_rand32_get();
|
||||
|
||||
/* SiLabs' OUI */
|
||||
mac_addr[0] = SILABS_OUI_B0;
|
||||
mac_addr[1] = SILABS_OUI_B1;
|
||||
mac_addr[2] = SILABS_OUI_B2;
|
||||
|
||||
mac_addr[3] = entropy >> 0;
|
||||
mac_addr[4] = entropy >> 8;
|
||||
mac_addr[5] = entropy >> 16;
|
||||
|
||||
/* Set MAC address locally administered, unicast (LAA) */
|
||||
mac_addr[0] |= 0x02;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void generate_mac(u8_t mac_addr[6])
|
||||
{
|
||||
#if defined(CONFIG_ETH_GECKO_RANDOM_MAC)
|
||||
generate_random_mac(mac_addr);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void eth_iface_init(struct net_if *iface)
|
||||
{
|
||||
struct device *const dev = net_if_get_device(iface);
|
||||
struct eth_gecko_dev_data *const dev_data = DEV_DATA(dev);
|
||||
const struct eth_gecko_dev_cfg *const cfg = DEV_CFG(dev);
|
||||
ETH_TypeDef *eth = cfg->regs;
|
||||
u32_t link_status;
|
||||
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_flag_set(iface, NET_IF_NO_AUTO_START);
|
||||
|
||||
/* 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 = (u32_t)dma_tx_desc_tab;
|
||||
eth->RXQPTR = (u32_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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Initialise TX/RX semaphores */
|
||||
k_sem_init(&dev_data->tx_sem, 1, ETH_TX_BUF_COUNT);
|
||||
k_sem_init(&dev_data->rx_sem, 0, UINT_MAX);
|
||||
|
||||
/* Start interruption-poll thread */
|
||||
k_thread_create(&dev_data->rx_thread, dev_data->rx_thread_stack,
|
||||
K_THREAD_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);
|
||||
|
||||
/* Set up link parameters and enable receiver/transmitter */
|
||||
link_configure(eth, link_status);
|
||||
}
|
||||
|
||||
static enum ethernet_hw_caps eth_gecko_get_capabilities(struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
return ETHERNET_LINK_10BASE_T | ETHERNET_LINK_100BASE_T;
|
||||
}
|
||||
|
||||
static const struct ethernet_api eth_api = {
|
||||
.iface_api.init = eth_iface_init,
|
||||
.get_capabilities = eth_gecko_get_capabilities,
|
||||
.send = eth_tx,
|
||||
};
|
||||
|
||||
static struct device DEVICE_NAME_GET(eth_gecko);
|
||||
|
||||
static void eth0_irq_config(void)
|
||||
{
|
||||
IRQ_CONNECT(DT_INST_0_SILABS_GECKO_ETHERNET_IRQ_0,
|
||||
DT_INST_0_SILABS_GECKO_ETHERNET_IRQ_0_PRIORITY, eth_isr,
|
||||
DEVICE_GET(eth_gecko), 0);
|
||||
irq_enable(DT_INST_0_SILABS_GECKO_ETHERNET_IRQ_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_0_SILABS_GECKO_ETHERNET_BASE_ADDRESS,
|
||||
.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_0_SILABS_GECKO_ETHERNET_BASE_ADDRESS,
|
||||
DT_INST_0_SILABS_GECKO_ETHERNET_PHY_ADDRESS },
|
||||
};
|
||||
|
||||
static struct eth_gecko_dev_data eth0_data = {
|
||||
#ifdef CONFIG_ETH_GECKO_MAC_MANUAL
|
||||
.mac_addr = {
|
||||
CONFIG_ETH_GECKO_MAC0,
|
||||
CONFIG_ETH_GECKO_MAC1,
|
||||
CONFIG_ETH_GECKO_MAC2,
|
||||
CONFIG_ETH_GECKO_MAC3,
|
||||
CONFIG_ETH_GECKO_MAC4,
|
||||
CONFIG_ETH_GECKO_MAC5,
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
ETH_NET_DEVICE_INIT(eth_gecko, CONFIG_ETH_GECKO_NAME, eth_init,
|
||||
ð0_data, ð0_config, CONFIG_ETH_INIT_PRIORITY,
|
||||
ð_api, ETH_GECKO_MTU);
|
143
drivers/ethernet/eth_gecko_priv.h
Normal file
143
drivers/ethernet/eth_gecko_priv.h
Normal file
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Interay Solutions B.V.
|
||||
* Copyright (c) 2019 Oane Kingma
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_ETHERNET_ETH_GECKO_PRIV_H_
|
||||
#define ZEPHYR_DRIVERS_ETHERNET_ETH_GECKO_PRIV_H_
|
||||
|
||||
#include <kernel.h>
|
||||
#include <zephyr/types.h>
|
||||
|
||||
#define ETH_GECKO_MTU NET_ETH_MTU
|
||||
|
||||
#define SILABS_OUI_B0 0x00
|
||||
#define SILABS_OUI_B1 0x0B
|
||||
#define SILABS_OUI_B2 0x57
|
||||
|
||||
#define ETH_TX_BUF_SIZE 1536
|
||||
#define ETH_TX_BUF_COUNT 2
|
||||
#define ETH_RX_BUF_SIZE 128
|
||||
#define ETH_RX_BUF_COUNT 32
|
||||
|
||||
#define ETH_BUF_ALIGNMENT 16
|
||||
|
||||
#define ETH_DESC_ALIGNMENT 4
|
||||
|
||||
#define ETH_TX_USED BIT(31)
|
||||
#define ETH_TX_WRAP BIT(30)
|
||||
#define ETH_TX_ERROR BIT(29)
|
||||
#define ETH_TX_UNDERRUN BIT(28)
|
||||
#define ETH_TX_EXHAUSTED BIT(27)
|
||||
#define ETH_TX_NO_CRC BIT(16)
|
||||
#define ETH_TX_LAST BIT(15)
|
||||
#define ETH_TX_LENGTH (2048-1)
|
||||
|
||||
#define ETH_RX_ADDRESS ~(ETH_DESC_ALIGNMENT-1)
|
||||
#define ETH_RX_WRAP BIT(1)
|
||||
#define ETH_RX_OWNERSHIP BIT(0)
|
||||
#define ETH_RX_BROADCAST BIT(31)
|
||||
#define ETH_RX_MULTICAST_HASH BIT(30)
|
||||
#define ETH_RX_UNICAST_HASH BIT(29)
|
||||
#define ETH_RX_EXT_ADDR BIT(28)
|
||||
#define ETH_RX_SAR1 BIT(26)
|
||||
#define ETH_RX_SAR2 BIT(25)
|
||||
#define ETH_RX_SAR3 BIT(24)
|
||||
#define ETH_RX_SAR4 BIT(23)
|
||||
#define ETH_RX_TYPE_ID BIT(22)
|
||||
#define ETH_RX_VLAN_TAG BIT(21)
|
||||
#define ETH_RX_PRIORITY_TAG BIT(20)
|
||||
#define ETH_RX_VLAN_PRIORITY (0x7UL<<17)
|
||||
#define ETH_RX_CFI BIT(16)
|
||||
#define ETH_RX_EOF BIT(15)
|
||||
#define ETH_RX_SOF BIT(14)
|
||||
#define ETH_RX_OFFSET (0x3UL<<12)
|
||||
#define ETH_RX_LENGTH (4096-1)
|
||||
|
||||
#define ETH_RX_ENABLE(base) (base->NETWORKCTRL |= ETH_NETWORKCTRL_ENBRX)
|
||||
#define ETH_RX_DISABLE(base) (base->NETWORKCTRL &= ~ETH_NETWORKCTRL_ENBRX)
|
||||
|
||||
#define ETH_TX_ENABLE(base) (base->NETWORKCTRL |= ETH_NETWORKCTRL_ENBTX)
|
||||
#define ETH_TX_DISABLE(base) (base->NETWORKCTRL &= ~ETH_NETWORKCTRL_ENBTX)
|
||||
|
||||
struct eth_buf_desc {
|
||||
u32_t address;
|
||||
u32_t status;
|
||||
};
|
||||
|
||||
struct eth_gecko_pin_list {
|
||||
struct soc_gpio_pin mdio[2];
|
||||
struct soc_gpio_pin rmii[7];
|
||||
};
|
||||
|
||||
/* Device constant configuration parameters */
|
||||
struct eth_gecko_dev_cfg {
|
||||
ETH_TypeDef *regs;
|
||||
const struct eth_gecko_pin_list *pin_list;
|
||||
u32_t pin_list_size;
|
||||
void (*config_func)(void);
|
||||
struct phy_gecko_dev phy;
|
||||
};
|
||||
|
||||
/* Device run time data */
|
||||
struct eth_gecko_dev_data {
|
||||
struct net_if *iface;
|
||||
u8_t mac_addr[6];
|
||||
struct k_sem tx_sem;
|
||||
struct k_sem rx_sem;
|
||||
|
||||
K_THREAD_STACK_MEMBER(rx_thread_stack,
|
||||
CONFIG_ETH_GECKO_RX_THREAD_STACK_SIZE);
|
||||
struct k_thread rx_thread;
|
||||
bool link_up;
|
||||
};
|
||||
|
||||
#define DEV_NAME(dev) ((dev)->config->name)
|
||||
#define DEV_CFG(dev) \
|
||||
((struct eth_gecko_dev_cfg *)(dev)->config->config_info)
|
||||
#define DEV_DATA(dev) \
|
||||
((struct eth_gecko_dev_data *)(dev)->driver_data)
|
||||
|
||||
/* PHY Management pins */
|
||||
#define PIN_PHY_MDC {DT_INST_0_SILABS_GECKO_ETHERNET_LOCATION_PHY_MDC_1, \
|
||||
DT_INST_0_SILABS_GECKO_ETHERNET_LOCATION_PHY_MDC_2, gpioModePushPull,\
|
||||
0}
|
||||
#define PIN_PHY_MDIO {DT_INST_0_SILABS_GECKO_ETHERNET_LOCATION_PHY_MDIO_1, \
|
||||
DT_INST_0_SILABS_GECKO_ETHERNET_LOCATION_PHY_MDIO_2, gpioModePushPull,\
|
||||
0}
|
||||
|
||||
#define PIN_LIST_PHY {PIN_PHY_MDC, PIN_PHY_MDIO}
|
||||
|
||||
/* RMII pins excluding reference clock, handled by board.c */
|
||||
#define PIN_RMII_CRSDV {DT_INST_0_SILABS_GECKO_ETHERNET_LOCATION_RMII_CRS_DV_1,\
|
||||
DT_INST_0_SILABS_GECKO_ETHERNET_LOCATION_RMII_CRS_DV_2, gpioModeInput, 0}
|
||||
|
||||
#define PIN_RMII_TXD0 {DT_INST_0_SILABS_GECKO_ETHERNET_LOCATION_RMII_TXD0_1,\
|
||||
DT_INST_0_SILABS_GECKO_ETHERNET_LOCATION_RMII_TXD0_2, gpioModePushPull, 0}
|
||||
|
||||
#define PIN_RMII_TXD1 {DT_INST_0_SILABS_GECKO_ETHERNET_LOCATION_RMII_TXD1_1,\
|
||||
DT_INST_0_SILABS_GECKO_ETHERNET_LOCATION_RMII_TXD1_2, gpioModePushPull, 0}
|
||||
|
||||
#define PIN_RMII_TX_EN {DT_INST_0_SILABS_GECKO_ETHERNET_LOCATION_RMII_TX_EN_1,\
|
||||
DT_INST_0_SILABS_GECKO_ETHERNET_LOCATION_RMII_TX_EN_2, gpioModePushPull, 0}
|
||||
|
||||
#define PIN_RMII_RXD0 {DT_INST_0_SILABS_GECKO_ETHERNET_LOCATION_RMII_RXD0_1,\
|
||||
DT_INST_0_SILABS_GECKO_ETHERNET_LOCATION_RMII_RXD0_2, gpioModeInput, 0}
|
||||
|
||||
#define PIN_RMII_RXD1 {DT_INST_0_SILABS_GECKO_ETHERNET_LOCATION_RMII_RXD1_1,\
|
||||
DT_INST_0_SILABS_GECKO_ETHERNET_LOCATION_RMII_RXD1_2, gpioModeInput, 0}
|
||||
|
||||
#define PIN_RMII_RX_ER {DT_INST_0_SILABS_GECKO_ETHERNET_LOCATION_RMII_RX_ER_1,\
|
||||
DT_INST_0_SILABS_GECKO_ETHERNET_LOCATION_RMII_RX_ER_2, gpioModeInput, 0}
|
||||
|
||||
#define PIN_LIST_RMII {PIN_RMII_CRSDV, PIN_RMII_TXD0, PIN_RMII_TXD1, \
|
||||
PIN_RMII_TX_EN, PIN_RMII_RXD0, PIN_RMII_RXD1, PIN_RMII_RX_ER}
|
||||
|
||||
/* RMII reference clock is not included in RMII pin set
|
||||
* #define PIN_RMII_REFCLK {DT_INST_0_SILABS_GECKO_ETHERNET_LOCATION_RMII_REFCLK_1,\
|
||||
* DT_INST_0_SILABS_GECKO_ETHERNET_LOCATION_RMII_REFCLK_2, gpioModePushPull, 0}
|
||||
*/
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_ETHERNET_ETH_GECKO_PRIV_H_ */
|
284
drivers/ethernet/phy_gecko.c
Normal file
284
drivers/ethernet/phy_gecko.c
Normal file
|
@ -0,0 +1,284 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Interay Solutions B.V.
|
||||
* Copyright (c) 2019 Oane Kingma
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* SiLabs Giant Gecko GG11 Ethernet PHY driver. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <kernel.h>
|
||||
#include <net/mii.h>
|
||||
#include "phy_gecko.h"
|
||||
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER(eth_gecko_phy, CONFIG_ETHERNET_LOG_LEVEL);
|
||||
|
||||
/* Maximum time to establish a link through auto-negotiation for
|
||||
* 10BASE-T, 100BASE-TX is 3.7s, to add an extra margin the timeout
|
||||
* is set at 4s.
|
||||
*/
|
||||
#define PHY_AUTONEG_TIMEOUT_MS 4000
|
||||
|
||||
/* Enable MDIO serial bus between MAC and PHY. */
|
||||
static void mdio_bus_enable(ETH_TypeDef *eth)
|
||||
{
|
||||
eth->NETWORKCTRL |= ETH_NETWORKCTRL_MANPORTEN;
|
||||
}
|
||||
|
||||
/* Enable MDIO serial bus between MAC and PHY. */
|
||||
static void mdio_bus_disable(ETH_TypeDef *eth)
|
||||
{
|
||||
eth->NETWORKCTRL &= ~ETH_NETWORKCTRL_MANPORTEN;
|
||||
}
|
||||
|
||||
/* Wait PHY operation complete. */
|
||||
static int mdio_bus_wait(ETH_TypeDef *eth)
|
||||
{
|
||||
u32_t retries = 100U; /* will wait up to 1 s */
|
||||
|
||||
while (!(eth->NETWORKSTATUS & ETH_NETWORKSTATUS_MANDONE)) {
|
||||
if (retries-- == 0U) {
|
||||
LOG_ERR("timeout");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
k_sleep(10);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Send command to PHY over MDIO serial bus */
|
||||
static int mdio_bus_send(ETH_TypeDef *eth, u8_t phy_addr, u8_t reg_addr,
|
||||
u8_t rw, u16_t data)
|
||||
{
|
||||
int retval;
|
||||
|
||||
/* Write PHY management register */
|
||||
eth->PHYMNGMNT = ETH_PHYMNGMNT_WRITE0_DEFAULT
|
||||
| ETH_PHYMNGMNT_WRITE1
|
||||
| ((rw ? 0x02 : 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);
|
||||
|
||||
/* Wait until PHY is ready */
|
||||
retval = mdio_bus_wait(eth);
|
||||
if (retval < 0) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read PHY register. */
|
||||
static int phy_read(const struct phy_gecko_dev *phy, u8_t reg_addr,
|
||||
u32_t *value)
|
||||
{
|
||||
ETH_TypeDef *const eth = phy->regs;
|
||||
u8_t phy_addr = phy->address;
|
||||
int retval;
|
||||
|
||||
retval = mdio_bus_send(eth, phy_addr, reg_addr, 1, 0);
|
||||
if (retval < 0) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Read data */
|
||||
*value = eth->PHYMNGMNT & _ETH_PHYMNGMNT_PHYRWDATA_MASK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Write PHY register. */
|
||||
static int phy_write(const struct phy_gecko_dev *phy, u8_t reg_addr,
|
||||
u32_t value)
|
||||
{
|
||||
ETH_TypeDef *const eth = phy->regs;
|
||||
u8_t phy_addr = phy->address;
|
||||
|
||||
return mdio_bus_send(eth, phy_addr, reg_addr, 0, value);
|
||||
}
|
||||
|
||||
/* Issue a PHY soft reset. */
|
||||
static int phy_soft_reset(const struct phy_gecko_dev *phy)
|
||||
{
|
||||
u32_t phy_reg;
|
||||
u32_t retries = 12U;
|
||||
int retval;
|
||||
|
||||
/* Issue a soft reset */
|
||||
retval = phy_write(phy, MII_BMCR, MII_BMCR_RESET);
|
||||
if (retval < 0) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Wait up to 0.6s for the reset sequence to finish. According to
|
||||
* IEEE 802.3, Section 2, Subsection 22.2.4.1.1 a PHY reset may take
|
||||
* up to 0.5 s.
|
||||
*/
|
||||
do {
|
||||
if (retries-- == 0U) {
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
k_sleep(50);
|
||||
|
||||
retval = phy_read(phy, MII_BMCR, &phy_reg);
|
||||
if (retval < 0) {
|
||||
return retval;
|
||||
}
|
||||
} while (phy_reg & MII_BMCR_RESET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int phy_gecko_init(const struct phy_gecko_dev *phy)
|
||||
{
|
||||
ETH_TypeDef *const eth = phy->regs;
|
||||
int phy_id;
|
||||
|
||||
mdio_bus_enable(eth);
|
||||
|
||||
LOG_INF("Soft Reset of ETH PHY");
|
||||
phy_soft_reset(phy);
|
||||
|
||||
/* Verify that the PHY device is responding */
|
||||
phy_id = phy_gecko_id_get(phy);
|
||||
if (phy_id == 0xFFFFFFFF) {
|
||||
LOG_ERR("Unable to detect a valid PHY");
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG_INF("PHYID: 0x%X at addr: %d", phy_id, phy->address);
|
||||
|
||||
mdio_bus_disable(eth);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32_t phy_gecko_id_get(const struct phy_gecko_dev *phy)
|
||||
{
|
||||
ETH_TypeDef *const eth = phy->regs;
|
||||
u32_t phy_reg;
|
||||
u32_t phy_id;
|
||||
|
||||
mdio_bus_enable(eth);
|
||||
|
||||
if (phy_read(phy, MII_PHYID1R, &phy_reg) < 0) {
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
phy_id = (phy_reg & 0xFFFF) << 16;
|
||||
|
||||
if (phy_read(phy, MII_PHYID2R, &phy_reg) < 0) {
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
phy_id |= (phy_reg & 0xFFFF);
|
||||
|
||||
mdio_bus_disable(eth);
|
||||
|
||||
return phy_id;
|
||||
}
|
||||
|
||||
int phy_gecko_auto_negotiate(const struct phy_gecko_dev *phy,
|
||||
u32_t *status)
|
||||
{
|
||||
ETH_TypeDef *const eth = phy->regs;
|
||||
u32_t val;
|
||||
u32_t ability_adv;
|
||||
u32_t ability_rcvd;
|
||||
u32_t retries = PHY_AUTONEG_TIMEOUT_MS / 100;
|
||||
int retval;
|
||||
|
||||
mdio_bus_enable(eth);
|
||||
|
||||
LOG_DBG("Starting ETH PHY auto-negotiate sequence");
|
||||
|
||||
/* Read PHY default advertising parameters */
|
||||
retval = phy_read(phy, MII_ANAR, &ability_adv);
|
||||
if (retval < 0) {
|
||||
goto auto_negotiate_exit;
|
||||
}
|
||||
|
||||
/* Configure and start auto-negotiation process */
|
||||
retval = phy_read(phy, MII_BMCR, &val);
|
||||
if (retval < 0) {
|
||||
goto auto_negotiate_exit;
|
||||
}
|
||||
|
||||
val |= MII_BMCR_AUTONEG_ENABLE | MII_BMCR_AUTONEG_RESTART;
|
||||
val &= ~MII_BMCR_ISOLATE; /* Don't isolate the PHY */
|
||||
|
||||
retval = phy_write(phy, MII_BMCR, val);
|
||||
if (retval < 0) {
|
||||
goto auto_negotiate_exit;
|
||||
}
|
||||
|
||||
/* Wait for the auto-negotiation process to complete */
|
||||
do {
|
||||
if (retries-- == 0U) {
|
||||
retval = -ETIMEDOUT;
|
||||
goto auto_negotiate_exit;
|
||||
}
|
||||
|
||||
k_sleep(100);
|
||||
|
||||
retval = phy_read(phy, MII_BMSR, &val);
|
||||
if (retval < 0) {
|
||||
goto auto_negotiate_exit;
|
||||
}
|
||||
} while (!(val & MII_BMSR_AUTONEG_COMPLETE));
|
||||
|
||||
LOG_DBG("PHY auto-negotiate sequence completed");
|
||||
|
||||
/* Read abilities of the remote device */
|
||||
retval = phy_read(phy, MII_ANLPAR, &ability_rcvd);
|
||||
if (retval < 0) {
|
||||
goto auto_negotiate_exit;
|
||||
}
|
||||
|
||||
/* Determine the best possible mode of operation */
|
||||
if ((ability_adv & ability_rcvd) & MII_ADVERTISE_100_FULL) {
|
||||
*status = ETH_NETWORKCFG_FULLDUPLEX | ETH_NETWORKCFG_SPEED;
|
||||
} else if ((ability_adv & ability_rcvd) & MII_ADVERTISE_100_HALF) {
|
||||
*status = ETH_NETWORKCFG_SPEED;
|
||||
} else if ((ability_adv & ability_rcvd) & MII_ADVERTISE_10_FULL) {
|
||||
*status = ETH_NETWORKCFG_FULLDUPLEX;
|
||||
} else {
|
||||
*status = 0;
|
||||
}
|
||||
|
||||
LOG_INF("common abilities: speed %s Mb, %s duplex",
|
||||
*status & ETH_NETWORKCFG_SPEED ? "100" : "10",
|
||||
*status & ETH_NETWORKCFG_FULLDUPLEX ? "full" : "half");
|
||||
|
||||
auto_negotiate_exit:
|
||||
mdio_bus_disable(eth);
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool phy_gecko_is_linked(const struct phy_gecko_dev *phy)
|
||||
{
|
||||
ETH_TypeDef *const eth = phy->regs;
|
||||
u32_t phy_reg;
|
||||
bool phy_linked = false;
|
||||
|
||||
mdio_bus_enable(eth);
|
||||
|
||||
if (phy_read(phy, MII_BMSR, &phy_reg) < 0) {
|
||||
return phy_linked;
|
||||
}
|
||||
|
||||
phy_linked = (phy_reg & MII_BMSR_LINK_STATUS);
|
||||
|
||||
mdio_bus_disable(eth);
|
||||
|
||||
return phy_linked;
|
||||
}
|
60
drivers/ethernet/phy_gecko.h
Normal file
60
drivers/ethernet/phy_gecko.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Interay Solutions B.V.
|
||||
* Copyright (c) 2019 Oane Kingma
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_ETHERNET_PHY_GECKO_H_
|
||||
#define ZEPHYR_DRIVERS_ETHERNET_PHY_GECKO_H_
|
||||
|
||||
#include <zephyr/types.h>
|
||||
#include <soc.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct phy_gecko_dev {
|
||||
ETH_TypeDef *regs;
|
||||
u8_t address;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Initialize Ethernet PHY device.
|
||||
*
|
||||
* @param phy PHY instance
|
||||
* @return 0 on success or a negative error value on failure
|
||||
*/
|
||||
int phy_gecko_init(const struct phy_gecko_dev *phy);
|
||||
|
||||
/**
|
||||
* @brief Auto-negotiate and configure link parameters.
|
||||
*
|
||||
* @param phy PHY instance
|
||||
* @param status link parameters common to remote and local PHY
|
||||
* @return 0 on success or a negative error value on failure
|
||||
*/
|
||||
int phy_gecko_auto_negotiate(const struct phy_gecko_dev *phy,
|
||||
u32_t *status);
|
||||
/**
|
||||
* @brief Get PHY ID value.
|
||||
*
|
||||
* @param phy PHY instance
|
||||
* @return PHY ID value or 0xFFFFFFFF on failure
|
||||
*/
|
||||
u32_t phy_gecko_id_get(const struct phy_gecko_dev *phy);
|
||||
|
||||
/**
|
||||
* @brief Get PHY linked status.
|
||||
*
|
||||
* @param phy PHY instance
|
||||
* @return PHY linked status
|
||||
*/
|
||||
bool phy_gecko_is_linked(const struct phy_gecko_dev *phy);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_ETHERNET_PHY_GECKO_H_ */
|
101
dts/bindings/ethernet/silabs,gecko-ethernet.yaml
Normal file
101
dts/bindings/ethernet/silabs,gecko-ethernet.yaml
Normal file
|
@ -0,0 +1,101 @@
|
|||
# Copyright (c) 2019 Interay Solutions B.V.
|
||||
# Copyright (c) 2019 Oane Kingma
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: SiLabs Gecko Ethernet
|
||||
|
||||
compatible: "silabs,gecko-ethernet"
|
||||
|
||||
include: ethernet.yaml
|
||||
|
||||
properties:
|
||||
reg:
|
||||
required: true
|
||||
description: mmio register space
|
||||
|
||||
interrupts:
|
||||
required: true
|
||||
description: required interrupts
|
||||
|
||||
# PHY address
|
||||
phy-address:
|
||||
type: int
|
||||
required: true
|
||||
description: address of the PHY on the MDIO bus
|
||||
|
||||
# RMII interface location
|
||||
location-rmii:
|
||||
type: int
|
||||
required: true
|
||||
description: location of RMII pins, configuration defined as <location>
|
||||
|
||||
# PHY management interface location
|
||||
location-mdio:
|
||||
type: int
|
||||
required: true
|
||||
description: location of MDC and MDIO pins, configuration defined as <location>
|
||||
|
||||
# PHY management pins
|
||||
location-phy_mdc:
|
||||
type: array
|
||||
required: true
|
||||
description: PHY MDC individual pin configuration defined as <location port pin>
|
||||
|
||||
location-phy_mdio:
|
||||
type: array
|
||||
required: true
|
||||
description: PHY MDIO individual pin configuration defined as <location port pin>
|
||||
|
||||
# RMII interface pins
|
||||
location-rmii_refclk:
|
||||
type: array
|
||||
required: true
|
||||
description: Reference clock individual pin configuration defined as <location port pin>
|
||||
|
||||
location-rmii_crs_dv:
|
||||
type: array
|
||||
required: true
|
||||
description: Receive data valid individual pin configuration defined as <location port pin>
|
||||
|
||||
location-rmii_txd0:
|
||||
type: array
|
||||
required: true
|
||||
description: Transmit data 0 individual pin configuration defined as <location port pin>
|
||||
|
||||
location-rmii_txd1:
|
||||
type: array
|
||||
required: true
|
||||
description: Transmit data 1 individual pin configuration defined as <location port pin>
|
||||
|
||||
location-rmii_tx_en:
|
||||
type: array
|
||||
required: true
|
||||
description: Transmit enable individual pin configuration defined as <location port pin>
|
||||
|
||||
location-rmii_rxd0:
|
||||
type: array
|
||||
required: true
|
||||
description: Receive data 0 individual pin configuration defined as <location port pin>
|
||||
|
||||
location-rmii_rxd1:
|
||||
type: array
|
||||
required: true
|
||||
description: Receive data 1 individual pin configuration defined as <location port pin>
|
||||
|
||||
location-rmii_rx_er:
|
||||
type: array
|
||||
required: true
|
||||
description: Receive error individual pin configuration defined as <location port pin>
|
||||
|
||||
# PHY control pins
|
||||
location-phy_pwr_enable:
|
||||
type: array
|
||||
description: PHY power enable individual pin configuration defined as <port pin>
|
||||
|
||||
location-phy_reset:
|
||||
type: array
|
||||
description: PHY reset individual pin configuration defined as <port pin>
|
||||
|
||||
location-phy_interrupt:
|
||||
type: array
|
||||
description: PHY interrupt individual pin configuration defined as <port pin>
|
Loading…
Reference in a new issue