827 lines
24 KiB
C
827 lines
24 KiB
C
/*
|
|
* Copyright 2023 NXP
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT nxp_s32_gmac
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(nxp_s32_eth, CONFIG_ETHERNET_LOG_LEVEL);
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/drivers/pinctrl.h>
|
|
#include <zephyr/net/ethernet.h>
|
|
#include <zephyr/net/net_if.h>
|
|
#include <zephyr/net/net_pkt.h>
|
|
#include <zephyr/net/phy.h>
|
|
#include <ethernet/eth_stats.h>
|
|
#include <soc.h>
|
|
|
|
#include <Gmac_Ip.h>
|
|
#include <Gmac_Ip_Hw_Access.h>
|
|
#include <Gmac_Ip_Irq.h>
|
|
#include <Clock_Ip.h>
|
|
|
|
#include "eth.h"
|
|
|
|
#define ETH_NXP_S32_BUF_TIMEOUT K_MSEC(20)
|
|
#define ETH_NXP_S32_DMA_TX_TIMEOUT K_MSEC(20)
|
|
|
|
#define ETH_NXP_S32_MAC_ADDR_LEN 6U
|
|
|
|
#define FREESCALE_OUI_B0 0x00
|
|
#define FREESCALE_OUI_B1 0x04
|
|
#define FREESCALE_OUI_B2 0x9f
|
|
|
|
struct eth_nxp_s32_config {
|
|
uint8_t instance;
|
|
uint8_t tx_ring_idx;
|
|
uint8_t rx_ring_idx;
|
|
uint32_t rx_irq;
|
|
uint32_t tx_irq;
|
|
void (*do_config)(void);
|
|
const struct pinctrl_dev_config *pincfg;
|
|
const struct device *phy_dev;
|
|
|
|
const Gmac_CtrlConfigType ctrl_cfg;
|
|
GMAC_Type *base;
|
|
};
|
|
|
|
struct eth_nxp_s32_data {
|
|
struct net_if *iface;
|
|
uint8_t mac_addr[ETH_NXP_S32_MAC_ADDR_LEN];
|
|
uint8_t if_suspended;
|
|
struct k_mutex tx_mutex;
|
|
struct k_sem rx_sem;
|
|
struct k_sem tx_sem;
|
|
struct k_thread rx_thread;
|
|
|
|
K_KERNEL_STACK_MEMBER(rx_thread_stack, CONFIG_ETH_NXP_S32_RX_THREAD_STACK_SIZE);
|
|
};
|
|
|
|
static void eth_nxp_s32_rx_thread(void *arg1, void *unused1, void *unused2);
|
|
|
|
static inline struct net_if *get_iface(struct eth_nxp_s32_data *ctx)
|
|
{
|
|
return ctx->iface;
|
|
}
|
|
|
|
static void convert_phy_to_mac_config(Gmac_Ip_ConfigType *gmac_cfg, enum phy_link_speed phy_speed)
|
|
{
|
|
switch (phy_speed) {
|
|
case LINK_HALF_10BASE_T:
|
|
gmac_cfg->Speed = GMAC_SPEED_10M;
|
|
gmac_cfg->Duplex = GMAC_HALF_DUPLEX;
|
|
break;
|
|
case LINK_FULL_10BASE_T:
|
|
gmac_cfg->Speed = GMAC_SPEED_10M;
|
|
gmac_cfg->Duplex = GMAC_FULL_DUPLEX;
|
|
break;
|
|
case LINK_HALF_100BASE_T:
|
|
gmac_cfg->Speed = GMAC_SPEED_100M;
|
|
gmac_cfg->Duplex = GMAC_HALF_DUPLEX;
|
|
break;
|
|
case LINK_FULL_100BASE_T:
|
|
gmac_cfg->Speed = GMAC_SPEED_100M;
|
|
gmac_cfg->Duplex = GMAC_FULL_DUPLEX;
|
|
break;
|
|
case LINK_HALF_1000BASE_T:
|
|
gmac_cfg->Speed = GMAC_SPEED_1G;
|
|
gmac_cfg->Duplex = GMAC_HALF_DUPLEX;
|
|
break;
|
|
case LINK_FULL_1000BASE_T:
|
|
__fallthrough;
|
|
default:
|
|
gmac_cfg->Speed = GMAC_SPEED_1G;
|
|
gmac_cfg->Duplex = GMAC_FULL_DUPLEX;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void phy_link_state_changed(const struct device *pdev,
|
|
struct phy_link_state *state,
|
|
void *user_data)
|
|
{
|
|
const struct device *dev = (struct device *)user_data;
|
|
const struct eth_nxp_s32_config *cfg = dev->config;
|
|
struct eth_nxp_s32_data *ctx = dev->data;
|
|
Gmac_Ip_ConfigType gmac_cfg;
|
|
|
|
ARG_UNUSED(pdev);
|
|
|
|
if (state->is_up) {
|
|
/* Porting phy link config to mac */
|
|
convert_phy_to_mac_config(&gmac_cfg, state->speed);
|
|
/* Set MAC configuration */
|
|
Gmac_Ip_SetSpeed(cfg->instance, gmac_cfg.Speed);
|
|
|
|
cfg->base->MAC_CONFIGURATION |= GMAC_MAC_CONFIGURATION_DM(gmac_cfg.Duplex);
|
|
|
|
/* net iface should be down even if PHY link state is up
|
|
* till the upper network layers have suspended the iface.
|
|
*/
|
|
if (ctx->if_suspended) {
|
|
return;
|
|
}
|
|
|
|
LOG_DBG("Link up");
|
|
net_eth_carrier_on(ctx->iface);
|
|
} else {
|
|
LOG_DBG("Link down");
|
|
net_eth_carrier_off(ctx->iface);
|
|
}
|
|
}
|
|
|
|
static const struct device *eth_nxp_s32_get_phy(const struct device *dev)
|
|
{
|
|
const struct eth_nxp_s32_config *cfg = dev->config;
|
|
|
|
return cfg->phy_dev;
|
|
}
|
|
|
|
#if defined(CONFIG_SOC_SERIES_S32K3)
|
|
static int select_phy_interface(Gmac_Ip_MiiModeType mode)
|
|
{
|
|
uint32_t regval;
|
|
|
|
switch (mode) {
|
|
case GMAC_MII_MODE:
|
|
regval = DCM_GPR_DCMRWF1_EMAC_CONF_SEL(0U);
|
|
break;
|
|
case GMAC_RMII_MODE:
|
|
regval = DCM_GPR_DCMRWF1_EMAC_CONF_SEL(2U);
|
|
break;
|
|
#if (FEATURE_GMAC_RGMII_EN == 1U)
|
|
case GMAC_RGMII_MODE:
|
|
regval = DCM_GPR_DCMRWF1_EMAC_CONF_SEL(1U);
|
|
break;
|
|
#endif
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
IP_DCM_GPR->DCMRWF1 = (IP_DCM_GPR->DCMRWF1 & ~DCM_GPR_DCMRWF1_EMAC_CONF_SEL_MASK) | regval;
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
#error "SoC not supported"
|
|
#endif /* CONFIG_SOC_SERIES_S32K3 */
|
|
|
|
static int eth_nxp_s32_init(const struct device *dev)
|
|
{
|
|
const struct eth_nxp_s32_config *cfg = dev->config;
|
|
struct eth_nxp_s32_data *ctx = dev->data;
|
|
Gmac_Ip_StatusType mac_status;
|
|
Clock_Ip_StatusType clk_status;
|
|
int err;
|
|
|
|
err = pinctrl_apply_state(cfg->pincfg, PINCTRL_STATE_DEFAULT);
|
|
if (err != 0) {
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* Currently, clock control shim driver does not support configuring clock
|
|
* muxes individually, so use the HAL directly.
|
|
*/
|
|
clk_status = Clock_Ip_Init(&Clock_Ip_aClockConfig[CONFIG_ETH_NXP_S32_CLOCK_CONFIG_IDX]);
|
|
if (clk_status != CLOCK_IP_SUCCESS) {
|
|
LOG_ERR("Failed to configure clocks (%d)", clk_status);
|
|
return -EIO;
|
|
}
|
|
|
|
/*
|
|
* PHY mode selection must be done before the controller is reset,
|
|
* because the interface type is latched at controller's reset
|
|
*/
|
|
err = select_phy_interface(cfg->ctrl_cfg.Gmac_pCtrlConfig->MiiMode);
|
|
if (err != 0) {
|
|
LOG_ERR("Failed to select PHY interface (%d)", err);
|
|
return -EIO;
|
|
}
|
|
|
|
mac_status = Gmac_Ip_Init(cfg->instance, &cfg->ctrl_cfg);
|
|
if (mac_status != GMAC_STATUS_SUCCESS) {
|
|
LOG_ERR("Failed to initialize GMAC%d (%d)", cfg->instance, mac_status);
|
|
return -EIO;
|
|
}
|
|
|
|
k_mutex_init(&ctx->tx_mutex);
|
|
k_sem_init(&ctx->rx_sem, 0, 1);
|
|
k_sem_init(&ctx->tx_sem, 0, 1);
|
|
|
|
k_thread_create(&ctx->rx_thread, ctx->rx_thread_stack,
|
|
K_KERNEL_STACK_SIZEOF(ctx->rx_thread_stack),
|
|
eth_nxp_s32_rx_thread, (void *)dev, NULL, NULL,
|
|
K_PRIO_COOP(CONFIG_ETH_NXP_S32_RX_THREAD_PRIO),
|
|
0, K_NO_WAIT);
|
|
k_thread_name_set(&ctx->rx_thread, "eth_nxp_s32_rx");
|
|
|
|
if (cfg->do_config != NULL) {
|
|
cfg->do_config();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int eth_nxp_s32_start(const struct device *dev)
|
|
{
|
|
const struct eth_nxp_s32_config *cfg = dev->config;
|
|
struct eth_nxp_s32_data *ctx = dev->data;
|
|
struct phy_link_state state;
|
|
|
|
Gmac_Ip_EnableController(cfg->instance);
|
|
|
|
irq_enable(cfg->rx_irq);
|
|
irq_enable(cfg->tx_irq);
|
|
|
|
/* If upper layers enable the net iface then mark it as
|
|
* not suspended so that PHY Link changes can have the impact
|
|
*/
|
|
ctx->if_suspended = false;
|
|
|
|
if (cfg->phy_dev) {
|
|
phy_get_link_state(cfg->phy_dev, &state);
|
|
|
|
/* Enable net_iface only when Ethernet PHY link is up or else
|
|
* if net_iface is enabled when link is down and tx happens
|
|
* in this state then the used tx buffers will never be recovered back.
|
|
*/
|
|
if (state.is_up == true) {
|
|
net_eth_carrier_on(ctx->iface);
|
|
}
|
|
} else {
|
|
net_eth_carrier_on(ctx->iface);
|
|
}
|
|
|
|
LOG_DBG("GMAC%d started", cfg->instance);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int eth_nxp_s32_stop(const struct device *dev)
|
|
{
|
|
const struct eth_nxp_s32_config *cfg = dev->config;
|
|
struct eth_nxp_s32_data *ctx = dev->data;
|
|
Gmac_Ip_StatusType status;
|
|
int err = 0;
|
|
|
|
irq_disable(cfg->rx_irq);
|
|
irq_disable(cfg->tx_irq);
|
|
|
|
/* If upper layers disable the net iface then mark it as suspended
|
|
* in order to save it from the PHY link state changes
|
|
*/
|
|
ctx->if_suspended = true;
|
|
|
|
net_eth_carrier_off(ctx->iface);
|
|
|
|
status = Gmac_Ip_DisableController(cfg->instance);
|
|
if (status != GMAC_STATUS_SUCCESS) {
|
|
LOG_ERR("Failed to disable controller GMAC%d (%d)", cfg->instance, status);
|
|
err = -EIO;
|
|
}
|
|
|
|
LOG_DBG("GMAC%d stopped", cfg->instance);
|
|
|
|
return err;
|
|
}
|
|
|
|
static void eth_nxp_s32_iface_init(struct net_if *iface)
|
|
{
|
|
const struct device *dev = net_if_get_device(iface);
|
|
const struct eth_nxp_s32_config *cfg = dev->config;
|
|
struct eth_nxp_s32_data *ctx = dev->data;
|
|
|
|
if (ctx->iface == NULL) {
|
|
ctx->iface = iface;
|
|
}
|
|
|
|
ethernet_init(iface);
|
|
|
|
net_if_set_link_addr(iface, ctx->mac_addr, sizeof(ctx->mac_addr), NET_LINK_ETHERNET);
|
|
|
|
LOG_INF("GMAC%d MAC address %02x:%02x:%02x:%02x:%02x:%02x", cfg->instance,
|
|
ctx->mac_addr[0], ctx->mac_addr[1], ctx->mac_addr[2],
|
|
ctx->mac_addr[3], ctx->mac_addr[4], ctx->mac_addr[5]);
|
|
|
|
/* Make sure that the net iface state is not suspended unless
|
|
* upper layers explicitly stop the iface
|
|
*/
|
|
ctx->if_suspended = false;
|
|
|
|
/* No PHY available, link is always up and MAC speed/duplex settings are fixed */
|
|
if (cfg->phy_dev == NULL) {
|
|
net_if_carrier_on(iface);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* GMAC controls the PHY. If PHY is configured either as fixed
|
|
* link or autoneg, the callback is executed at least once
|
|
* immediately after setting it.
|
|
*/
|
|
if (!device_is_ready(cfg->phy_dev)) {
|
|
LOG_ERR("PHY device (%p) is not ready, cannot init iface",
|
|
cfg->phy_dev);
|
|
return;
|
|
}
|
|
|
|
phy_link_callback_set(cfg->phy_dev, &phy_link_state_changed, (void *)dev);
|
|
}
|
|
|
|
static int eth_nxp_s32_tx(const struct device *dev, struct net_pkt *pkt)
|
|
{
|
|
struct eth_nxp_s32_data *ctx = dev->data;
|
|
const struct eth_nxp_s32_config *cfg = dev->config;
|
|
size_t pkt_len = net_pkt_get_len(pkt);
|
|
int res = 0;
|
|
Gmac_Ip_BufferType buf;
|
|
Gmac_Ip_TxInfoType tx_info;
|
|
Gmac_Ip_StatusType status;
|
|
Gmac_Ip_TxOptionsType tx_options = {
|
|
.NoInt = FALSE,
|
|
.CrcPadIns = GMAC_CRC_AND_PAD_INSERTION,
|
|
.ChecksumIns = GMAC_CHECKSUM_INSERTION_PROTO_PSEUDOH
|
|
};
|
|
|
|
__ASSERT(pkt, "Packet pointer is NULL");
|
|
|
|
k_mutex_lock(&ctx->tx_mutex, K_FOREVER);
|
|
k_sem_reset(&ctx->tx_sem);
|
|
|
|
buf.Length = (uint16_t)pkt_len;
|
|
buf.Data = NULL;
|
|
status = Gmac_Ip_GetTxBuff(cfg->instance, cfg->tx_ring_idx, &buf, NULL);
|
|
if (status != GMAC_STATUS_SUCCESS) {
|
|
LOG_ERR("Failed to get tx buffer (%d)", status);
|
|
res = -ENOBUFS;
|
|
goto error;
|
|
}
|
|
|
|
res = net_pkt_read(pkt, buf.Data, pkt_len);
|
|
if (res) {
|
|
LOG_ERR("Failed to copy packet to tx buffer (%d)", res);
|
|
res = -ENOBUFS;
|
|
goto error;
|
|
}
|
|
|
|
buf.Length = (uint16_t)pkt_len;
|
|
status = Gmac_Ip_SendFrame(cfg->instance, cfg->tx_ring_idx, &buf, &tx_options);
|
|
if (status != GMAC_STATUS_SUCCESS) {
|
|
LOG_ERR("Failed to tx frame (%d)", status);
|
|
res = -EIO;
|
|
goto error;
|
|
}
|
|
|
|
/* Wait for the transmission to complete */
|
|
if (k_sem_take(&ctx->tx_sem, ETH_NXP_S32_DMA_TX_TIMEOUT) != 0) {
|
|
LOG_ERR("Timeout transmitting frame");
|
|
res = -EIO;
|
|
goto error;
|
|
}
|
|
|
|
/* Restore the buffer address pointer and clear the descriptor after the status is read */
|
|
status = Gmac_Ip_GetTransmitStatus(cfg->instance, cfg->tx_ring_idx, &buf, &tx_info);
|
|
if (status != GMAC_STATUS_SUCCESS) {
|
|
LOG_ERR("Failed to restore tx buffer: %s (%d) ",
|
|
(status == GMAC_STATUS_BUSY ? "busy" : "buf not found"), status);
|
|
res = -EIO;
|
|
} else if (tx_info.ErrMask != 0U) {
|
|
LOG_ERR("Tx frame has errors (error mask 0x%X)", tx_info.ErrMask);
|
|
res = -EIO;
|
|
}
|
|
|
|
error:
|
|
k_mutex_unlock(&ctx->tx_mutex);
|
|
|
|
if (res != 0) {
|
|
eth_stats_update_errors_tx(ctx->iface);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static struct net_pkt *eth_nxp_s32_get_pkt(const struct device *dev,
|
|
Gmac_Ip_BufferType *buf,
|
|
Gmac_Ip_RxInfoType *rx_info)
|
|
{
|
|
struct eth_nxp_s32_data *ctx = dev->data;
|
|
struct net_pkt *pkt = NULL;
|
|
int res = 0;
|
|
|
|
/* Using root iface, it will be updated in net_recv_data() */
|
|
pkt = net_pkt_rx_alloc_with_buffer(ctx->iface, rx_info->PktLen,
|
|
AF_UNSPEC, 0, ETH_NXP_S32_BUF_TIMEOUT);
|
|
if (!pkt) {
|
|
LOG_ERR("Failed to allocate rx buffer of length %u", rx_info->PktLen);
|
|
goto exit;
|
|
}
|
|
|
|
res = net_pkt_write(pkt, buf->Data, rx_info->PktLen);
|
|
if (res) {
|
|
LOG_ERR("Failed to write rx frame into pkt buffer (%d)", res);
|
|
net_pkt_unref(pkt);
|
|
pkt = NULL;
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
if (!pkt) {
|
|
eth_stats_update_errors_rx(get_iface(ctx));
|
|
}
|
|
|
|
return pkt;
|
|
}
|
|
|
|
static void eth_nxp_s32_rx(const struct device *dev)
|
|
{
|
|
struct eth_nxp_s32_data *ctx = dev->data;
|
|
const struct eth_nxp_s32_config *cfg = dev->config;
|
|
struct net_pkt *pkt;
|
|
int res = 0;
|
|
Gmac_Ip_RxInfoType rx_info = {0};
|
|
Gmac_Ip_BufferType buf;
|
|
Gmac_Ip_StatusType status;
|
|
|
|
status = Gmac_Ip_ReadFrame(cfg->instance, cfg->rx_ring_idx, &buf, &rx_info);
|
|
if (rx_info.ErrMask != 0U) {
|
|
Gmac_Ip_ProvideRxBuff(cfg->instance, cfg->rx_ring_idx, &buf);
|
|
LOG_ERR("Rx frame has errors (error mask 0x%X)", rx_info.ErrMask);
|
|
} else if (status == GMAC_STATUS_SUCCESS) {
|
|
pkt = eth_nxp_s32_get_pkt(dev, &buf, &rx_info);
|
|
Gmac_Ip_ProvideRxBuff(cfg->instance, cfg->rx_ring_idx, &buf);
|
|
if (pkt != NULL) {
|
|
res = net_recv_data(get_iface(ctx), pkt);
|
|
if (res < 0) {
|
|
eth_stats_update_errors_rx(get_iface(ctx));
|
|
net_pkt_unref(pkt);
|
|
LOG_ERR("Failed to enqueue frame into rx queue (%d)", res);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void eth_nxp_s32_rx_thread(void *arg1, void *unused1, void *unused2)
|
|
{
|
|
const struct device *dev = (const struct device *)arg1;
|
|
struct eth_nxp_s32_data *ctx = dev->data;
|
|
const struct eth_nxp_s32_config *cfg = dev->config;
|
|
int res;
|
|
int work;
|
|
|
|
ARG_UNUSED(unused1);
|
|
ARG_UNUSED(unused2);
|
|
__ASSERT_NO_MSG(arg1 != NULL);
|
|
__ASSERT_NO_MSG(ctx != NULL);
|
|
|
|
while (1) {
|
|
res = k_sem_take(&ctx->rx_sem, K_FOREVER);
|
|
__ASSERT_NO_MSG(res == 0);
|
|
|
|
work = 0;
|
|
while (Gmac_Ip_IsFrameAvailable(cfg->instance, cfg->rx_ring_idx)) {
|
|
eth_nxp_s32_rx(dev);
|
|
if (++work == CONFIG_ETH_NXP_S32_RX_BUDGET) {
|
|
/* More work to do, reschedule */
|
|
work = 0;
|
|
k_yield();
|
|
}
|
|
}
|
|
|
|
/* All work done, re-enable rx interrupt and exit polling */
|
|
irq_enable(cfg->rx_irq);
|
|
|
|
/* In case a frame arrived after last eth_nxp_s32_rx() and before irq_enable() */
|
|
if (Gmac_Ip_IsFrameAvailable(cfg->instance, cfg->rx_ring_idx)) {
|
|
eth_nxp_s32_rx(dev);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int eth_nxp_s32_set_config(const struct device *dev,
|
|
enum ethernet_config_type type,
|
|
const struct ethernet_config *config)
|
|
{
|
|
struct eth_nxp_s32_data *ctx = dev->data;
|
|
const struct eth_nxp_s32_config *cfg = dev->config;
|
|
int res = 0;
|
|
uint32_t regval;
|
|
|
|
ARG_UNUSED(cfg);
|
|
ARG_UNUSED(regval);
|
|
|
|
switch (type) {
|
|
case ETHERNET_CONFIG_TYPE_MAC_ADDRESS:
|
|
/* Set new Ethernet MAC address and register it with the upper layer */
|
|
memcpy(ctx->mac_addr, config->mac_address.addr, sizeof(ctx->mac_addr));
|
|
Gmac_Ip_SetMacAddr(cfg->instance, (const uint8_t *)ctx->mac_addr);
|
|
net_if_set_link_addr(ctx->iface, ctx->mac_addr, sizeof(ctx->mac_addr),
|
|
NET_LINK_ETHERNET);
|
|
LOG_INF("MAC set to: %02x:%02x:%02x:%02x:%02x:%02x",
|
|
ctx->mac_addr[0], ctx->mac_addr[1], ctx->mac_addr[2],
|
|
ctx->mac_addr[3], ctx->mac_addr[4], ctx->mac_addr[5]);
|
|
break;
|
|
#if defined(CONFIG_NET_PROMISCUOUS_MODE)
|
|
case ETHERNET_CONFIG_TYPE_PROMISC_MODE:
|
|
regval = cfg->base->MAC_PACKET_FILTER;
|
|
if (config->promisc_mode && !(regval & GMAC_MAC_PACKET_FILTER_PR_MASK)) {
|
|
cfg->base->MAC_PACKET_FILTER |= GMAC_MAC_PACKET_FILTER_PR_MASK;
|
|
} else if (!config->promisc_mode && (regval & GMAC_MAC_PACKET_FILTER_PR_MASK)) {
|
|
cfg->base->MAC_PACKET_FILTER &= ~GMAC_MAC_PACKET_FILTER_PR_MASK;
|
|
} else {
|
|
res = -EALREADY;
|
|
}
|
|
break;
|
|
#endif
|
|
#if defined(CONFIG_ETH_NXP_S32_MULTICAST_FILTER)
|
|
case ETHERNET_HW_FILTERING:
|
|
if (config->filter.set) {
|
|
Gmac_Ip_AddDstAddrToHashFilter(cfg->instance,
|
|
config->filter.mac_address.addr);
|
|
} else {
|
|
Gmac_Ip_RemoveDstAddrFromHashFilter(cfg->instance,
|
|
config->filter.mac_address.addr);
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
res = -ENOTSUP;
|
|
break;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static enum ethernet_hw_caps eth_nxp_s32_get_capabilities(const struct device *dev)
|
|
{
|
|
ARG_UNUSED(dev);
|
|
|
|
return (ETHERNET_LINK_10BASE_T
|
|
| ETHERNET_LINK_100BASE_T
|
|
#if (FEATURE_GMAC_RGMII_EN == 1U)
|
|
| ETHERNET_LINK_1000BASE_T
|
|
#endif
|
|
| ETHERNET_DUPLEX_SET
|
|
| ETHERNET_HW_TX_CHKSUM_OFFLOAD
|
|
| ETHERNET_HW_RX_CHKSUM_OFFLOAD
|
|
#if defined(CONFIG_NET_VLAN)
|
|
| ETHERNET_HW_VLAN
|
|
#endif
|
|
#if defined(CONFIG_NET_PROMISCUOUS_MODE)
|
|
| ETHERNET_PROMISC_MODE
|
|
#endif
|
|
#if defined(CONFIG_ETH_NXP_S32_MULTICAST_FILTER)
|
|
| ETHERNET_HW_FILTERING
|
|
#endif
|
|
);
|
|
}
|
|
|
|
static void eth_nxp_s32_tx_irq(const struct device *dev)
|
|
{
|
|
const struct eth_nxp_s32_config *cfg = dev->config;
|
|
|
|
GMAC_TxIRQHandler(cfg->instance, cfg->tx_ring_idx);
|
|
}
|
|
|
|
static void eth_nxp_s32_rx_irq(const struct device *dev)
|
|
{
|
|
const struct eth_nxp_s32_config *cfg = dev->config;
|
|
|
|
GMAC_RxIRQHandler(cfg->instance, cfg->rx_ring_idx);
|
|
}
|
|
|
|
static const struct ethernet_api eth_api = {
|
|
.iface_api.init = eth_nxp_s32_iface_init,
|
|
.get_capabilities = eth_nxp_s32_get_capabilities,
|
|
.get_phy = eth_nxp_s32_get_phy,
|
|
.start = eth_nxp_s32_start,
|
|
.stop = eth_nxp_s32_stop,
|
|
.send = eth_nxp_s32_tx,
|
|
.set_config = eth_nxp_s32_set_config,
|
|
};
|
|
|
|
|
|
BUILD_ASSERT(((CONFIG_ETH_NXP_S32_RX_RING_BUF_SIZE * CONFIG_ETH_NXP_S32_RX_RING_LEN)
|
|
% FEATURE_GMAC_MTL_RX_FIFO_BLOCK_SIZE) == 0,
|
|
"CONFIG_ETH_NXP_S32_RX_RING_BUF_SIZE * CONFIG_ETH_NXP_S32_RX_RING_LEN "
|
|
"must be multiple of RX FIFO block size");
|
|
BUILD_ASSERT(((CONFIG_ETH_NXP_S32_TX_RING_BUF_SIZE * CONFIG_ETH_NXP_S32_TX_RING_LEN)
|
|
% FEATURE_GMAC_MTL_TX_FIFO_BLOCK_SIZE) == 0,
|
|
"CONFIG_ETH_NXP_S32_TX_RING_BUF_SIZE * CONFIG_ETH_NXP_S32_TX_RING_LEN "
|
|
"must be multiple of TX FIFO block size");
|
|
BUILD_ASSERT((CONFIG_ETH_NXP_S32_RX_RING_BUF_SIZE % FEATURE_GMAC_DATA_BUS_WIDTH_BYTES) == 0,
|
|
"CONFIG_ETH_NXP_S32_RX_RING_BUF_SIZE must be multiple of the data bus width");
|
|
BUILD_ASSERT((CONFIG_ETH_NXP_S32_TX_RING_BUF_SIZE % FEATURE_GMAC_DATA_BUS_WIDTH_BYTES) == 0,
|
|
"CONFIG_ETH_NXP_S32_TX_RING_BUF_SIZE must be multiple of the data bus width");
|
|
|
|
#define ETH_NXP_S32_MAC_MII(n) \
|
|
_CONCAT(_CONCAT(GMAC_, DT_INST_STRING_UPPER_TOKEN(n, phy_connection_type)), _MODE)
|
|
|
|
#define ETH_NXP_S32_IRQ_INIT(n, name) \
|
|
IRQ_CONNECT(DT_INST_IRQ_BY_NAME(n, name, irq), \
|
|
DT_INST_IRQ_BY_NAME(n, name, priority), \
|
|
eth_nxp_s32_##name##_irq, \
|
|
DEVICE_DT_INST_GET(n), \
|
|
COND_CODE_1(DT_INST_IRQ_HAS_CELL(n, flags), \
|
|
(DT_INST_IRQ_BY_NAME(n, name, flags)), (0)));
|
|
|
|
#define ETH_NXP_S32_INIT_CONFIG(n) \
|
|
static void eth_nxp_s32_init_config_##n(void) \
|
|
{ \
|
|
const struct device *dev = DEVICE_DT_INST_GET(n); \
|
|
struct eth_nxp_s32_data *ctx = dev->data; \
|
|
const struct eth_nxp_s32_config *cfg = dev->config; \
|
|
\
|
|
ETH_NXP_S32_IRQ_INIT(n, tx); \
|
|
ETH_NXP_S32_IRQ_INIT(n, rx); \
|
|
\
|
|
COND_CODE_1(DT_INST_PROP(n, zephyr_random_mac_address), ( \
|
|
gen_random_mac(ctx->mac_addr, FREESCALE_OUI_B0, \
|
|
FREESCALE_OUI_B1, FREESCALE_OUI_B2); \
|
|
Gmac_Ip_SetMacAddr(cfg->instance, ctx->mac_addr); \
|
|
), ( \
|
|
Gmac_Ip_GetMacAddr(cfg->instance, ctx->mac_addr); \
|
|
)) \
|
|
}
|
|
|
|
#define ETH_NXP_S32_RX_CALLBACK(n) \
|
|
static void eth_nxp_s32_rx_callback_##n(uint8_t inst, uint8_t chan) \
|
|
{ \
|
|
const struct device *dev = DEVICE_DT_INST_GET(n); \
|
|
struct eth_nxp_s32_data *ctx = dev->data; \
|
|
const struct eth_nxp_s32_config *cfg = dev->config; \
|
|
\
|
|
ARG_UNUSED(inst); \
|
|
ARG_UNUSED(chan); \
|
|
\
|
|
/* Rx irq will be re-enabled from Rx thread */ \
|
|
irq_disable(cfg->rx_irq); \
|
|
k_sem_give(&ctx->rx_sem); \
|
|
}
|
|
|
|
#define ETH_NXP_S32_TX_CALLBACK(n) \
|
|
static void eth_nxp_s32_tx_callback_##n(uint8_t inst, uint8_t chan) \
|
|
{ \
|
|
const struct device *dev = DEVICE_DT_INST_GET(n); \
|
|
struct eth_nxp_s32_data *ctx = dev->data; \
|
|
\
|
|
ARG_UNUSED(inst); \
|
|
ARG_UNUSED(chan); \
|
|
\
|
|
k_sem_give(&ctx->tx_sem); \
|
|
}
|
|
|
|
#define _ETH_NXP_S32_RING(n, name, len, buf_size) \
|
|
static Gmac_Ip_BufferDescriptorType eth_nxp_s32_##name##ring_desc_##n[len] \
|
|
__nocache __aligned(FEATURE_GMAC_BUFFDESCR_ALIGNMENT_BYTES); \
|
|
static uint8_t eth_nxp_s32_##name##ring_buf_##n[len * buf_size] \
|
|
__nocache __aligned(FEATURE_GMAC_BUFF_ALIGNMENT_BYTES)
|
|
|
|
#define ETH_NXP_S32_RX_RING(n) \
|
|
_ETH_NXP_S32_RING(n, rx, \
|
|
CONFIG_ETH_NXP_S32_RX_RING_LEN, \
|
|
CONFIG_ETH_NXP_S32_RX_RING_BUF_SIZE)
|
|
|
|
#define ETH_NXP_S32_TX_RING(n) \
|
|
_ETH_NXP_S32_RING(n, tx, \
|
|
CONFIG_ETH_NXP_S32_TX_RING_LEN, \
|
|
CONFIG_ETH_NXP_S32_TX_RING_BUF_SIZE)
|
|
|
|
#define ETH_NXP_S32_MAC_TXTIMESHAPER_CONFIG(n) \
|
|
static const Gmac_Ip_TxTimeAwareShaper eth_nxp_s32_mac_txtimeshaper_config_##n = {\
|
|
.GateControlList = NULL, \
|
|
}
|
|
|
|
#define ETH_NXP_S32_MAC_RXRING_CONFIG(n) \
|
|
static const Gmac_Ip_RxRingConfigType eth_nxp_s32_mac_rxring_config_##n = { \
|
|
.RingDesc = eth_nxp_s32_rxring_desc_##n, \
|
|
.Callback = eth_nxp_s32_rx_callback_##n, \
|
|
.Buffer = eth_nxp_s32_rxring_buf_##n, \
|
|
.Interrupts = (uint32_t)GMAC_CH_INTERRUPT_RI, \
|
|
.BufferLen = CONFIG_ETH_NXP_S32_RX_RING_BUF_SIZE, \
|
|
.RingSize = CONFIG_ETH_NXP_S32_RX_RING_LEN, \
|
|
.PriorityMask = 0U, \
|
|
.DmaBurstLength = 32U, \
|
|
}
|
|
|
|
#define ETH_NXP_S32_MAC_TXRING_CONFIG(n) \
|
|
static const Gmac_Ip_TxRingConfigType eth_nxp_s32_mac_txring_config_##n = { \
|
|
.Weight = 0U, \
|
|
.IdleSlopeCredit = 0U, \
|
|
.SendSlopeCredit = 0U, \
|
|
.HiCredit = 0U, \
|
|
.LoCredit = 0, \
|
|
.RingDesc = eth_nxp_s32_txring_desc_##n, \
|
|
.Callback = eth_nxp_s32_tx_callback_##n, \
|
|
.Buffer = eth_nxp_s32_txring_buf_##n, \
|
|
.Interrupts = (uint32_t)GMAC_CH_INTERRUPT_TI, \
|
|
.BufferLen = CONFIG_ETH_NXP_S32_TX_RING_BUF_SIZE, \
|
|
.RingSize = CONFIG_ETH_NXP_S32_TX_RING_LEN, \
|
|
.PriorityMask = 0U, \
|
|
.DmaBurstLength = 32U, \
|
|
.QueueOpMode = GMAC_OP_MODE_DCB_GEN, \
|
|
}
|
|
|
|
#define ETH_NXP_S32_MAC_PKT_FILTER(n) \
|
|
((uint32_t)(0U \
|
|
COND_CODE_1(CONFIG_ETH_NXP_S32_MULTICAST_FILTER, \
|
|
(|GMAC_PKT_FILTER_HASH_MULTICAST), \
|
|
(|GMAC_PKT_FILTER_PASS_ALL_MULTICAST)) \
|
|
))
|
|
|
|
#define ETH_NXP_S32_MAC_CONF(n) \
|
|
((uint32_t)(GMAC_MAC_CONFIG_CRC_STRIPPING \
|
|
| GMAC_MAC_CONFIG_AUTO_PAD \
|
|
| GMAC_MAC_CONFIG_CHECKSUM_OFFLOAD \
|
|
IF_ENABLED(CONFIG_ETH_NXP_S32_LOOPBACK, \
|
|
(|GMAC_MAC_CONFIG_LOOPBACK)) \
|
|
))
|
|
|
|
#define ETH_NXP_S32_MAC_CONFIG(n) \
|
|
static const Gmac_Ip_ConfigType eth_nxp_s32_mac_config_##n = { \
|
|
.RxRingCount = 1U, \
|
|
.TxRingCount = 1U, \
|
|
.Interrupts = 0U, \
|
|
.Callback = NULL, \
|
|
.TxSchedAlgo = GMAC_SCHED_ALGO_SP, \
|
|
.MiiMode = ETH_NXP_S32_MAC_MII(n), \
|
|
.Speed = GMAC_SPEED_100M, \
|
|
.Duplex = GMAC_FULL_DUPLEX, \
|
|
.MacConfig = ETH_NXP_S32_MAC_CONF(n), \
|
|
.MacPktFilterConfig = ETH_NXP_S32_MAC_PKT_FILTER(n), \
|
|
.EnableCtrl = false, \
|
|
}
|
|
|
|
#define ETH_NXP_S32_MAC_ADDR(n) \
|
|
BUILD_ASSERT(DT_INST_PROP(n, zephyr_random_mac_address) || \
|
|
NODE_HAS_VALID_MAC_ADDR(DT_DRV_INST(n)), \
|
|
"eth_nxp_s32_gmac requires either a fixed or random MAC address"); \
|
|
static const uint8_t eth_nxp_s32_mac_addr_##n[ETH_NXP_S32_MAC_ADDR_LEN] = \
|
|
DT_INST_PROP_OR(n, local_mac_address, {0U})
|
|
|
|
#define ETH_NXP_S32_MAC_STATE(n) Gmac_Ip_StateType eth_nxp_s32_mac_state_##n
|
|
|
|
#define ETH_NXP_S32_CTRL_CONFIG(n) \
|
|
{ \
|
|
.Gmac_pCtrlState = ð_nxp_s32_mac_state_##n, \
|
|
.Gmac_pCtrlConfig = ð_nxp_s32_mac_config_##n, \
|
|
.Gmac_paCtrlRxRingConfig = ð_nxp_s32_mac_rxring_config_##n, \
|
|
.Gmac_paCtrlTxRingConfig = ð_nxp_s32_mac_txring_config_##n, \
|
|
.Gmac_pau8CtrlPhysAddr = ð_nxp_s32_mac_addr_##n[0], \
|
|
.Gmac_pCtrlTxTimeAwareShaper = ð_nxp_s32_mac_txtimeshaper_config_##n,\
|
|
}
|
|
|
|
#define ETH_NXP_S32_HW_INSTANCE_CHECK(i, n) \
|
|
((DT_INST_REG_ADDR(n) == IP_GMAC_##i##_BASE) ? i : 0)
|
|
|
|
#define ETH_NXP_S32_HW_INSTANCE(n) \
|
|
LISTIFY(__DEBRACKET FEATURE_GMAC_NUM_INSTANCES, \
|
|
ETH_NXP_S32_HW_INSTANCE_CHECK, (|), n)
|
|
|
|
#define ETH_NXP_S32_PHY_DEV(n) \
|
|
(COND_CODE_1(DT_INST_NODE_HAS_PROP(n, phy_handle), \
|
|
(DEVICE_DT_GET(DT_INST_PHANDLE(n, phy_handle))), NULL))
|
|
|
|
#define ETH_NXP_S32_DEVICE(n) \
|
|
ETH_NXP_S32_TX_CALLBACK(n) \
|
|
ETH_NXP_S32_RX_CALLBACK(n) \
|
|
ETH_NXP_S32_INIT_CONFIG(n) \
|
|
ETH_NXP_S32_RX_RING(n); \
|
|
ETH_NXP_S32_TX_RING(n); \
|
|
ETH_NXP_S32_MAC_STATE(n); \
|
|
ETH_NXP_S32_MAC_TXTIMESHAPER_CONFIG(n); \
|
|
ETH_NXP_S32_MAC_RXRING_CONFIG(n); \
|
|
ETH_NXP_S32_MAC_TXRING_CONFIG(n); \
|
|
ETH_NXP_S32_MAC_CONFIG(n); \
|
|
ETH_NXP_S32_MAC_ADDR(n); \
|
|
PINCTRL_DT_INST_DEFINE(n); \
|
|
\
|
|
static const struct eth_nxp_s32_config eth_nxp_s32_config_##n = { \
|
|
.instance = ETH_NXP_S32_HW_INSTANCE(n), \
|
|
.base = (GMAC_Type *)DT_INST_REG_ADDR(n), \
|
|
.ctrl_cfg = ETH_NXP_S32_CTRL_CONFIG(n), \
|
|
.do_config = eth_nxp_s32_init_config_##n, \
|
|
.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
|
|
.phy_dev = ETH_NXP_S32_PHY_DEV(n), \
|
|
.rx_irq = DT_INST_IRQ_BY_NAME(n, rx, irq), \
|
|
.tx_irq = DT_INST_IRQ_BY_NAME(n, tx, irq), \
|
|
.tx_ring_idx = 0U, \
|
|
.rx_ring_idx = 0U, \
|
|
}; \
|
|
\
|
|
static struct eth_nxp_s32_data eth_nxp_s32_data_##n; \
|
|
\
|
|
ETH_NET_DEVICE_DT_INST_DEFINE(n, \
|
|
eth_nxp_s32_init, \
|
|
NULL, \
|
|
ð_nxp_s32_data_##n, \
|
|
ð_nxp_s32_config_##n, \
|
|
CONFIG_ETH_INIT_PRIORITY, \
|
|
ð_api, \
|
|
NET_ETH_MTU);
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(ETH_NXP_S32_DEVICE)
|