fdbba0341c
Deprecate ETH_MCUX, by: - Marking it as DEPRECATED in Kconfig, obviously. - Unmarking the new driver as experimental. - Putting the new and old drivers in the same folder. - Reworking the menu appearance of the driver selection. Note that technically now it is possible to choose the wrong driver than what is enabled in DT, this is intentional, but the correct one will obviously be enabled by default. - Convert all sample overlays to the new Kconfigs. This was part of the motivation for the shared overlays, as it was causing twister/CI logs to look ugly and misleading due to Kconfig warnings. Signed-off-by: Declan Snyder <declan.snyder@nxp.com>
911 lines
25 KiB
C
911 lines
25 KiB
C
/* NXP ENET MAC Driver
|
|
*
|
|
* Copyright 2023-2024 NXP
|
|
*
|
|
* Inspiration from eth_mcux.c, which was:
|
|
* Copyright (c) 2016-2017 ARM Ltd
|
|
* Copyright (c) 2016 Linaro Ltd
|
|
* Copyright (c) 2018 Intel Corporation
|
|
* Copyright 2023 NXP
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT nxp_enet_mac
|
|
|
|
/* Set up logging module for this driver */
|
|
#define LOG_MODULE_NAME eth_nxp_enet_mac
|
|
#define LOG_LEVEL CONFIG_ETHERNET_LOG_LEVEL
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
|
|
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/sys/util.h>
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/sys/__assert.h>
|
|
#include <zephyr/kernel/thread_stack.h>
|
|
|
|
#include <zephyr/net/net_pkt.h>
|
|
#include <zephyr/net/net_if.h>
|
|
#include <zephyr/net/ethernet.h>
|
|
#include <zephyr/net/phy.h>
|
|
#include <zephyr/net/mii.h>
|
|
#include <ethernet/eth_stats.h>
|
|
|
|
#include <zephyr/drivers/pinctrl.h>
|
|
#include <zephyr/drivers/clock_control.h>
|
|
|
|
#ifdef CONFIG_PTP_CLOCK
|
|
#include <zephyr/drivers/ptp_clock.h>
|
|
#endif
|
|
|
|
#ifdef CONFIG_NET_DSA
|
|
#include <zephyr/net/dsa.h>
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_POWER_MANAGEMENT) && defined(CONFIG_PM_DEVICE)
|
|
#include <zephyr/pm/device.h>
|
|
#endif
|
|
|
|
#include "../eth.h"
|
|
#include <zephyr/drivers/ethernet/eth_nxp_enet.h>
|
|
#include <zephyr/dt-bindings/ethernet/nxp_enet.h>
|
|
#include <fsl_enet.h>
|
|
|
|
#define FREESCALE_OUI_B0 0x00
|
|
#define FREESCALE_OUI_B1 0x04
|
|
#define FREESCALE_OUI_B2 0x9f
|
|
|
|
#if defined(CONFIG_SOC_SERIES_IMXRT10XX)
|
|
#define ETH_NXP_ENET_UNIQUE_ID (OCOTP->CFG1 ^ OCOTP->CFG2)
|
|
#elif defined(CONFIG_SOC_SERIES_IMXRT11XX)
|
|
#define ETH_NXP_ENET_UNIQUE_ID (OCOTP->FUSEN[40].FUSE)
|
|
#elif defined(CONFIG_SOC_SERIES_KINETIS_K6X)
|
|
#define ETH_NXP_ENET_UNIQUE_ID (SIM->UIDH ^ SIM->UIDMH ^ SIM->UIDML ^ SIM->UIDL)
|
|
#else
|
|
#define ETH_NXP_ENET_UNIQUE_ID 0xFFFFFF
|
|
#error "Unsupported SOC"
|
|
#endif
|
|
|
|
#define RING_ID 0
|
|
|
|
struct nxp_enet_mac_config {
|
|
ENET_Type *base;
|
|
const struct device *clock_dev;
|
|
clock_control_subsys_t clock_subsys;
|
|
bool generate_mac;
|
|
bool unique_mac;
|
|
const struct pinctrl_dev_config *pincfg;
|
|
enet_buffer_config_t buffer_config;
|
|
uint8_t phy_mode;
|
|
void (*irq_config_func)(void);
|
|
const struct device *phy_dev;
|
|
const struct device *mdio;
|
|
#ifdef CONFIG_PTP_CLOCK_NXP_ENET
|
|
const struct device *ptp_clock;
|
|
#endif
|
|
};
|
|
|
|
struct nxp_enet_mac_data {
|
|
struct net_if *iface;
|
|
uint8_t mac_addr[6];
|
|
enet_handle_t enet_handle;
|
|
struct k_sem tx_buf_sem;
|
|
struct k_work rx_work;
|
|
const struct device *dev;
|
|
struct k_sem rx_thread_sem;
|
|
struct k_mutex tx_frame_buf_mutex;
|
|
struct k_mutex rx_frame_buf_mutex;
|
|
#ifdef CONFIG_PTP_CLOCK_NXP_ENET
|
|
struct k_sem ptp_ts_sem;
|
|
struct k_mutex *ptp_mutex; /* created in PTP driver */
|
|
#endif
|
|
uint8_t *tx_frame_buf;
|
|
uint8_t *rx_frame_buf;
|
|
};
|
|
|
|
static K_THREAD_STACK_DEFINE(enet_rx_stack, CONFIG_ETH_NXP_ENET_RX_THREAD_STACK_SIZE);
|
|
static struct k_work_q rx_work_queue;
|
|
|
|
static int rx_queue_init(void)
|
|
{
|
|
struct k_work_queue_config cfg = {.name = "ENET_RX"};
|
|
|
|
k_work_queue_init(&rx_work_queue);
|
|
k_work_queue_start(&rx_work_queue, enet_rx_stack,
|
|
K_THREAD_STACK_SIZEOF(enet_rx_stack),
|
|
K_PRIO_COOP(CONFIG_ETH_NXP_ENET_RX_THREAD_PRIORITY),
|
|
&cfg);
|
|
|
|
return 0;
|
|
}
|
|
|
|
SYS_INIT(rx_queue_init, POST_KERNEL, 0);
|
|
|
|
static inline struct net_if *get_iface(struct nxp_enet_mac_data *data)
|
|
{
|
|
return data->iface;
|
|
}
|
|
|
|
#if defined(CONFIG_PTP_CLOCK_NXP_ENET)
|
|
static bool eth_get_ptp_data(struct net_if *iface, struct net_pkt *pkt)
|
|
{
|
|
struct net_eth_vlan_hdr *hdr_vlan = (struct net_eth_vlan_hdr *)NET_ETH_HDR(pkt);
|
|
struct ethernet_context *eth_ctx = net_if_l2_data(iface);
|
|
bool pkt_is_ptp;
|
|
|
|
if (net_eth_is_vlan_enabled(eth_ctx, iface)) {
|
|
pkt_is_ptp = ntohs(hdr_vlan->type) == NET_ETH_PTYPE_PTP;
|
|
} else {
|
|
pkt_is_ptp = ntohs(NET_ETH_HDR(pkt)->type) == NET_ETH_PTYPE_PTP;
|
|
}
|
|
|
|
if (pkt_is_ptp) {
|
|
net_pkt_set_priority(pkt, NET_PRIORITY_CA);
|
|
}
|
|
|
|
return pkt_is_ptp;
|
|
}
|
|
|
|
|
|
static inline void ts_register_tx_event(const struct device *dev,
|
|
enet_frame_info_t *frameinfo)
|
|
{
|
|
struct nxp_enet_mac_data *data = dev->data;
|
|
struct net_pkt *pkt = frameinfo->context;
|
|
|
|
if (pkt && atomic_get(&pkt->atomic_ref) > 0) {
|
|
if (eth_get_ptp_data(net_pkt_iface(pkt), pkt) && frameinfo->isTsAvail) {
|
|
k_mutex_lock(data->ptp_mutex, K_FOREVER);
|
|
|
|
pkt->timestamp.nanosecond = frameinfo->timeStamp.nanosecond;
|
|
pkt->timestamp.second = frameinfo->timeStamp.second;
|
|
|
|
net_if_add_tx_timestamp(pkt);
|
|
k_sem_give(&data->ptp_ts_sem);
|
|
|
|
k_mutex_unlock(data->ptp_mutex);
|
|
}
|
|
net_pkt_unref(pkt);
|
|
}
|
|
}
|
|
|
|
static inline void eth_wait_for_ptp_ts(const struct device *dev, struct net_pkt *pkt)
|
|
{
|
|
struct nxp_enet_mac_data *data = dev->data;
|
|
|
|
net_pkt_ref(pkt);
|
|
k_sem_take(&data->ptp_ts_sem, K_FOREVER);
|
|
}
|
|
#else
|
|
#define eth_get_ptp_data(...) false
|
|
#define ts_register_tx_event(...)
|
|
#define eth_wait_for_ptp_ts(...)
|
|
#endif /* CONFIG_PTP_CLOCK_NXP_ENET */
|
|
|
|
#ifdef CONFIG_PTP_CLOCK
|
|
static const struct device *eth_nxp_enet_get_ptp_clock(const struct device *dev)
|
|
{
|
|
const struct nxp_enet_mac_config *config = dev->config;
|
|
|
|
return config->ptp_clock;
|
|
}
|
|
#endif /* CONFIG_PTP_CLOCK */
|
|
|
|
static int eth_nxp_enet_tx(const struct device *dev, struct net_pkt *pkt)
|
|
{
|
|
const struct nxp_enet_mac_config *config = dev->config;
|
|
struct nxp_enet_mac_data *data = dev->data;
|
|
uint16_t total_len = net_pkt_get_len(pkt);
|
|
bool frame_is_timestamped;
|
|
status_t ret;
|
|
|
|
/* Wait for a TX buffer descriptor to be available */
|
|
k_sem_take(&data->tx_buf_sem, K_FOREVER);
|
|
|
|
/* Enter critical section for TX frame buffer access */
|
|
k_mutex_lock(&data->tx_frame_buf_mutex, K_FOREVER);
|
|
|
|
ret = net_pkt_read(pkt, data->tx_frame_buf, total_len);
|
|
if (ret) {
|
|
k_sem_give(&data->tx_buf_sem);
|
|
goto exit;
|
|
}
|
|
|
|
frame_is_timestamped = eth_get_ptp_data(net_pkt_iface(pkt), pkt);
|
|
|
|
ret = ENET_SendFrame(config->base, &data->enet_handle, data->tx_frame_buf,
|
|
total_len, RING_ID, frame_is_timestamped, pkt);
|
|
if (ret == kStatus_Success) {
|
|
goto exit;
|
|
}
|
|
|
|
if (frame_is_timestamped) {
|
|
eth_wait_for_ptp_ts(dev, pkt);
|
|
} else {
|
|
LOG_ERR("ENET_SendFrame error: %d", ret);
|
|
ENET_ReclaimTxDescriptor(config->base, &data->enet_handle, RING_ID);
|
|
}
|
|
|
|
exit:
|
|
/* Leave critical section for TX frame buffer access */
|
|
k_mutex_unlock(&data->tx_frame_buf_mutex);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void eth_nxp_enet_iface_init(struct net_if *iface)
|
|
{
|
|
const struct device *dev = net_if_get_device(iface);
|
|
struct nxp_enet_mac_data *data = dev->data;
|
|
const struct nxp_enet_mac_config *config = dev->config;
|
|
|
|
net_if_set_link_addr(iface, data->mac_addr,
|
|
sizeof(data->mac_addr),
|
|
NET_LINK_ETHERNET);
|
|
|
|
if (data->iface == NULL) {
|
|
data->iface = iface;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_DSA)
|
|
dsa_register_master_tx(iface, ð_nxp_enet_tx);
|
|
#endif
|
|
|
|
ethernet_init(iface);
|
|
net_eth_carrier_off(data->iface);
|
|
|
|
config->irq_config_func();
|
|
}
|
|
|
|
static enum ethernet_hw_caps eth_nxp_enet_get_capabilities(const struct device *dev)
|
|
{
|
|
ARG_UNUSED(dev);
|
|
|
|
return ETHERNET_LINK_10BASE_T |
|
|
ETHERNET_HW_FILTERING |
|
|
#if defined(CONFIG_NET_VLAN)
|
|
ETHERNET_HW_VLAN |
|
|
#endif
|
|
#if defined(CONFIG_PTP_CLOCK_NXP_ENET)
|
|
ETHERNET_PTP |
|
|
#endif
|
|
#if defined(CONFIG_NET_DSA)
|
|
ETHERNET_DSA_MASTER_PORT |
|
|
#endif
|
|
#if defined(CONFIG_ETH_NXP_ENET_HW_ACCELERATION)
|
|
ETHERNET_HW_TX_CHKSUM_OFFLOAD |
|
|
ETHERNET_HW_RX_CHKSUM_OFFLOAD |
|
|
#endif
|
|
ETHERNET_LINK_100BASE_T;
|
|
}
|
|
|
|
static int eth_nxp_enet_set_config(const struct device *dev,
|
|
enum ethernet_config_type type,
|
|
const struct ethernet_config *cfg)
|
|
{
|
|
struct nxp_enet_mac_data *data = dev->data;
|
|
const struct nxp_enet_mac_config *config = dev->config;
|
|
|
|
switch (type) {
|
|
case ETHERNET_CONFIG_TYPE_MAC_ADDRESS:
|
|
memcpy(data->mac_addr,
|
|
cfg->mac_address.addr,
|
|
sizeof(data->mac_addr));
|
|
ENET_SetMacAddr(config->base, data->mac_addr);
|
|
net_if_set_link_addr(data->iface, data->mac_addr,
|
|
sizeof(data->mac_addr),
|
|
NET_LINK_ETHERNET);
|
|
LOG_DBG("%s MAC set to %02x:%02x:%02x:%02x:%02x:%02x",
|
|
dev->name,
|
|
data->mac_addr[0], data->mac_addr[1],
|
|
data->mac_addr[2], data->mac_addr[3],
|
|
data->mac_addr[4], data->mac_addr[5]);
|
|
return 0;
|
|
case ETHERNET_CONFIG_TYPE_FILTER:
|
|
/* The ENET driver does not modify the address buffer but the API is not const */
|
|
if (cfg->filter.set) {
|
|
ENET_AddMulticastGroup(config->base,
|
|
(uint8_t *)cfg->filter.mac_address.addr);
|
|
} else {
|
|
ENET_LeaveMulticastGroup(config->base,
|
|
(uint8_t *)cfg->filter.mac_address.addr);
|
|
}
|
|
return 0;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
static int eth_nxp_enet_rx(const struct device *dev)
|
|
{
|
|
const struct nxp_enet_mac_config *config = dev->config;
|
|
struct nxp_enet_mac_data *data = dev->data;
|
|
uint32_t frame_length = 0U;
|
|
struct net_if *iface;
|
|
struct net_pkt *pkt = NULL;
|
|
status_t status;
|
|
uint32_t ts;
|
|
|
|
status = ENET_GetRxFrameSize(&data->enet_handle,
|
|
(uint32_t *)&frame_length, RING_ID);
|
|
if (status == kStatus_ENET_RxFrameEmpty) {
|
|
return 0;
|
|
} else if (status == kStatus_ENET_RxFrameError) {
|
|
enet_data_error_stats_t error_stats;
|
|
|
|
LOG_ERR("ENET_GetRxFrameSize return: %d", (int)status);
|
|
|
|
ENET_GetRxErrBeforeReadFrame(&data->enet_handle,
|
|
&error_stats, RING_ID);
|
|
goto flush;
|
|
}
|
|
|
|
if (frame_length > NET_ETH_MAX_FRAME_SIZE) {
|
|
LOG_ERR("Frame too large (%d)", frame_length);
|
|
goto flush;
|
|
}
|
|
|
|
/* Using root iface. It will be updated in net_recv_data() */
|
|
pkt = net_pkt_rx_alloc_with_buffer(data->iface, frame_length,
|
|
AF_UNSPEC, 0, K_NO_WAIT);
|
|
if (!pkt) {
|
|
goto flush;
|
|
}
|
|
|
|
k_mutex_lock(&data->rx_frame_buf_mutex, K_FOREVER);
|
|
status = ENET_ReadFrame(config->base, &data->enet_handle,
|
|
data->rx_frame_buf, frame_length, RING_ID, &ts);
|
|
k_mutex_unlock(&data->rx_frame_buf_mutex);
|
|
|
|
if (status) {
|
|
LOG_ERR("ENET_ReadFrame failed: %d", (int)status);
|
|
goto error;
|
|
}
|
|
|
|
if (net_pkt_write(pkt, data->rx_frame_buf, frame_length)) {
|
|
LOG_ERR("Unable to write frame into the packet");
|
|
goto error;
|
|
}
|
|
|
|
#if defined(CONFIG_PTP_CLOCK_NXP_ENET)
|
|
k_mutex_lock(data->ptp_mutex, K_FOREVER);
|
|
|
|
/* Invalid value by default. */
|
|
pkt->timestamp.nanosecond = UINT32_MAX;
|
|
pkt->timestamp.second = UINT64_MAX;
|
|
|
|
/* Timestamp the packet using PTP clock */
|
|
if (eth_get_ptp_data(get_iface(data), pkt)) {
|
|
struct net_ptp_time ptp_time;
|
|
|
|
ptp_clock_get(config->ptp_clock, &ptp_time);
|
|
|
|
/* If latest timestamp reloads after getting from Rx BD,
|
|
* then second - 1 to make sure the actual Rx timestamp is accurate
|
|
*/
|
|
if (ptp_time.nanosecond < ts) {
|
|
ptp_time.second--;
|
|
}
|
|
|
|
pkt->timestamp.nanosecond = ts;
|
|
pkt->timestamp.second = ptp_time.second;
|
|
}
|
|
k_mutex_unlock(data->ptp_mutex);
|
|
#endif /* CONFIG_PTP_CLOCK_NXP_ENET */
|
|
|
|
iface = get_iface(data);
|
|
#if defined(CONFIG_NET_DSA)
|
|
iface = dsa_net_recv(iface, &pkt);
|
|
#endif
|
|
if (net_recv_data(iface, pkt) < 0) {
|
|
goto error;
|
|
}
|
|
|
|
return 1;
|
|
flush:
|
|
/* Flush the current read buffer. This operation can
|
|
* only report failure if there is no frame to flush,
|
|
* which cannot happen in this context.
|
|
*/
|
|
status = ENET_ReadFrame(config->base, &data->enet_handle, NULL,
|
|
0, RING_ID, NULL);
|
|
__ASSERT_NO_MSG(status == kStatus_Success);
|
|
error:
|
|
if (pkt) {
|
|
net_pkt_unref(pkt);
|
|
}
|
|
eth_stats_update_errors_rx(get_iface(data));
|
|
return -EIO;
|
|
}
|
|
|
|
static void eth_nxp_enet_rx_thread(struct k_work *work)
|
|
{
|
|
struct nxp_enet_mac_data *data =
|
|
CONTAINER_OF(work, struct nxp_enet_mac_data, rx_work);
|
|
const struct device *dev = data->dev;
|
|
const struct nxp_enet_mac_config *config = dev->config;
|
|
int ret;
|
|
|
|
if (k_sem_take(&data->rx_thread_sem, K_FOREVER)) {
|
|
return;
|
|
}
|
|
|
|
do {
|
|
ret = eth_nxp_enet_rx(dev);
|
|
} while (ret == 1);
|
|
|
|
ENET_EnableInterrupts(config->base, kENET_RxFrameInterrupt);
|
|
}
|
|
|
|
static int nxp_enet_phy_reset_and_configure(const struct device *phy)
|
|
{
|
|
int ret;
|
|
|
|
/* Reset the PHY */
|
|
ret = phy_write(phy, MII_BMCR, MII_BMCR_RESET);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
/* 802.3u standard says reset takes up to 0.5s */
|
|
k_busy_wait(500000);
|
|
|
|
/* Configure the PHY */
|
|
return phy_configure_link(phy, LINK_HALF_10BASE_T | LINK_FULL_10BASE_T |
|
|
LINK_HALF_100BASE_T | LINK_FULL_100BASE_T);
|
|
}
|
|
|
|
static void nxp_enet_phy_cb(const struct device *phy,
|
|
struct phy_link_state *state,
|
|
void *eth_dev)
|
|
{
|
|
const struct device *dev = eth_dev;
|
|
struct nxp_enet_mac_data *data = dev->data;
|
|
|
|
if (!data->iface) {
|
|
return;
|
|
}
|
|
|
|
if (!state->is_up) {
|
|
net_eth_carrier_off(data->iface);
|
|
nxp_enet_phy_reset_and_configure(phy);
|
|
} else {
|
|
net_eth_carrier_on(data->iface);
|
|
}
|
|
|
|
LOG_INF("Link is %s", state->is_up ? "up" : "down");
|
|
}
|
|
|
|
|
|
static int nxp_enet_phy_init(const struct device *dev)
|
|
{
|
|
const struct nxp_enet_mac_config *config = dev->config;
|
|
int ret = 0;
|
|
|
|
ret = nxp_enet_phy_reset_and_configure(config->phy_dev);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = phy_link_callback_set(config->phy_dev, nxp_enet_phy_cb, (void *)dev);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void nxp_enet_driver_cb(const struct device *dev, enum nxp_enet_driver dev_type,
|
|
enum nxp_enet_callback_reason event, void *data)
|
|
{
|
|
if (dev_type == NXP_ENET_MDIO) {
|
|
nxp_enet_mdio_callback(dev, event, data);
|
|
} else if (dev_type == NXP_ENET_PTP_CLOCK) {
|
|
nxp_enet_ptp_clock_callback(dev, event, data);
|
|
}
|
|
}
|
|
|
|
static void eth_callback(ENET_Type *base, enet_handle_t *handle,
|
|
#if FSL_FEATURE_ENET_QUEUE > 1
|
|
uint32_t ringId,
|
|
#endif /* FSL_FEATURE_ENET_QUEUE > 1 */
|
|
enet_event_t event, enet_frame_info_t *frameinfo, void *param)
|
|
{
|
|
const struct device *dev = param;
|
|
const struct nxp_enet_mac_config *config = dev->config;
|
|
struct nxp_enet_mac_data *data = dev->data;
|
|
|
|
switch (event) {
|
|
case kENET_RxEvent:
|
|
k_sem_give(&data->rx_thread_sem);
|
|
break;
|
|
case kENET_TxEvent:
|
|
ts_register_tx_event(dev, frameinfo);
|
|
k_sem_give(&data->tx_buf_sem);
|
|
break;
|
|
case kENET_TimeStampEvent:
|
|
/* Reset periodic timer to default value. */
|
|
config->base->ATPER = NSEC_PER_SEC;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
#if FSL_FEATURE_ENET_QUEUE > 1
|
|
#define ENET_IRQ_HANDLER_ARGS(base, handle) base, handle, 0
|
|
#else
|
|
#define ENET_IRQ_HANDLER_ARGS(base, handle) base, handle
|
|
#endif /* FSL_FEATURE_ENET_QUEUE > 1 */
|
|
|
|
static void eth_nxp_enet_isr(const struct device *dev)
|
|
{
|
|
const struct nxp_enet_mac_config *config = dev->config;
|
|
struct nxp_enet_mac_data *data = dev->data;
|
|
unsigned int irq_lock_key = irq_lock();
|
|
|
|
uint32_t eir = ENET_GetInterruptStatus(config->base);
|
|
|
|
if (eir & (kENET_RxFrameInterrupt)) {
|
|
ENET_ReceiveIRQHandler(ENET_IRQ_HANDLER_ARGS(config->base, &data->enet_handle));
|
|
ENET_DisableInterrupts(config->base, kENET_RxFrameInterrupt);
|
|
k_work_submit_to_queue(&rx_work_queue, &data->rx_work);
|
|
}
|
|
|
|
if (eir & kENET_TxFrameInterrupt) {
|
|
ENET_TransmitIRQHandler(ENET_IRQ_HANDLER_ARGS(config->base, &data->enet_handle));
|
|
}
|
|
|
|
if (eir & ENET_EIR_MII_MASK) {
|
|
nxp_enet_driver_cb(config->mdio, NXP_ENET_MDIO, NXP_ENET_INTERRUPT, NULL);
|
|
}
|
|
|
|
irq_unlock(irq_lock_key);
|
|
}
|
|
|
|
static inline void nxp_enet_unique_mac(uint8_t *mac_addr)
|
|
{
|
|
uint32_t id = ETH_NXP_ENET_UNIQUE_ID;
|
|
|
|
mac_addr[0] = FREESCALE_OUI_B0;
|
|
mac_addr[1] = FREESCALE_OUI_B1;
|
|
mac_addr[2] = FREESCALE_OUI_B2;
|
|
mac_addr[3] = FIELD_GET(0xFF0000, id);
|
|
mac_addr[4] = FIELD_GET(0x00FF00, id);
|
|
mac_addr[5] = FIELD_GET(0x0000FF, id);
|
|
}
|
|
|
|
static int eth_nxp_enet_init(const struct device *dev)
|
|
{
|
|
struct nxp_enet_mac_data *data = dev->data;
|
|
const struct nxp_enet_mac_config *config = dev->config;
|
|
enet_config_t enet_config;
|
|
uint32_t enet_module_clock_rate;
|
|
int err;
|
|
|
|
err = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
k_mutex_init(&data->rx_frame_buf_mutex);
|
|
k_mutex_init(&data->tx_frame_buf_mutex);
|
|
k_sem_init(&data->rx_thread_sem, 0, CONFIG_ETH_NXP_ENET_RX_BUFFERS);
|
|
k_sem_init(&data->tx_buf_sem,
|
|
CONFIG_ETH_NXP_ENET_TX_BUFFERS, CONFIG_ETH_NXP_ENET_TX_BUFFERS);
|
|
#if defined(CONFIG_PTP_CLOCK_NXP_ENET)
|
|
k_sem_init(&data->ptp_ts_sem, 0, 1);
|
|
#endif
|
|
k_work_init(&data->rx_work, eth_nxp_enet_rx_thread);
|
|
|
|
if (config->unique_mac) {
|
|
nxp_enet_unique_mac(data->mac_addr);
|
|
}
|
|
|
|
if (config->generate_mac) {
|
|
gen_random_mac(data->mac_addr,
|
|
FREESCALE_OUI_B0, FREESCALE_OUI_B1, FREESCALE_OUI_B2);
|
|
}
|
|
|
|
err = clock_control_get_rate(config->clock_dev, config->clock_subsys,
|
|
&enet_module_clock_rate);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
ENET_GetDefaultConfig(&enet_config);
|
|
|
|
if (IS_ENABLED(CONFIG_NET_PROMISCUOUS_MODE)) {
|
|
enet_config.macSpecialConfig |= kENET_ControlPromiscuousEnable;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_NET_VLAN)) {
|
|
enet_config.macSpecialConfig |= kENET_ControlVLANTagEnable;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_ETH_NXP_ENET_HW_ACCELERATION)) {
|
|
enet_config.txAccelerConfig |=
|
|
kENET_TxAccelIpCheckEnabled | kENET_TxAccelProtoCheckEnabled;
|
|
enet_config.rxAccelerConfig |=
|
|
kENET_RxAccelIpCheckEnabled | kENET_RxAccelProtoCheckEnabled;
|
|
}
|
|
|
|
enet_config.interrupt |= kENET_RxFrameInterrupt;
|
|
enet_config.interrupt |= kENET_TxFrameInterrupt;
|
|
|
|
if (config->phy_mode == NXP_ENET_MII_MODE) {
|
|
enet_config.miiMode = kENET_MiiMode;
|
|
} else if (config->phy_mode == NXP_ENET_RMII_MODE) {
|
|
enet_config.miiMode = kENET_RmiiMode;
|
|
} else {
|
|
return -EINVAL;
|
|
}
|
|
|
|
enet_config.callback = eth_callback;
|
|
enet_config.userData = (void *)dev;
|
|
|
|
ENET_Up(config->base,
|
|
&data->enet_handle,
|
|
&enet_config,
|
|
&config->buffer_config,
|
|
data->mac_addr,
|
|
enet_module_clock_rate);
|
|
|
|
nxp_enet_driver_cb(config->mdio, NXP_ENET_MDIO, NXP_ENET_MODULE_RESET, NULL);
|
|
|
|
#if defined(CONFIG_PTP_CLOCK_NXP_ENET)
|
|
nxp_enet_driver_cb(config->ptp_clock, NXP_ENET_PTP_CLOCK,
|
|
NXP_ENET_MODULE_RESET, &data->ptp_mutex);
|
|
ENET_SetTxReclaim(&data->enet_handle, true, 0);
|
|
#endif
|
|
|
|
ENET_ActiveRead(config->base);
|
|
|
|
err = nxp_enet_phy_init(dev);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
LOG_DBG("%s MAC %02x:%02x:%02x:%02x:%02x:%02x",
|
|
dev->name,
|
|
data->mac_addr[0], data->mac_addr[1],
|
|
data->mac_addr[2], data->mac_addr[3],
|
|
data->mac_addr[4], data->mac_addr[5]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_POWER_MANAGEMENT)
|
|
static int eth_nxp_enet_device_pm_action(const struct device *dev, enum pm_device_action action)
|
|
{
|
|
const struct nxp_enet_mac_config *config = dev->config;
|
|
struct nxp_enet_mac_data *data = dev->data;
|
|
int ret;
|
|
|
|
if (!device_is_ready(config->clock_dev)) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (action == PM_DEVICE_ACTION_SUSPEND) {
|
|
LOG_DBG("Suspending");
|
|
|
|
ret = net_if_suspend(data->iface);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ENET_Reset(config->base);
|
|
ENET_Down(config->base);
|
|
clock_control_off(config->clock_dev, (clock_control_subsys_t)config->clock_subsys);
|
|
} else if (action == PM_DEVICE_ACTION_RESUME) {
|
|
LOG_DBG("Resuming");
|
|
|
|
clock_control_on(config->clock_dev, (clock_control_subsys_t)config->clock_subsys);
|
|
eth_nxp_enet_init(dev);
|
|
net_if_resume(data->iface);
|
|
} else {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define ETH_NXP_ENET_PM_DEVICE_INIT(n) \
|
|
PM_DEVICE_DT_INST_DEFINE(n, eth_nxp_enet_device_pm_action);
|
|
#define ETH_NXP_ENET_PM_DEVICE_GET(n) PM_DEVICE_DT_INST_GET(n)
|
|
|
|
#else
|
|
#define ETH_NXP_ENET_PM_DEVICE_INIT(n)
|
|
#define ETH_NXP_ENET_PM_DEVICE_GET(n) NULL
|
|
#endif /* CONFIG_NET_POWER_MANAGEMENT */
|
|
|
|
#ifdef CONFIG_NET_DSA
|
|
#define NXP_ENET_SEND_FUNC dsa_tx
|
|
#else
|
|
#define NXP_ENET_SEND_FUNC eth_nxp_enet_tx
|
|
#endif /* CONFIG_NET_DSA */
|
|
|
|
static const struct ethernet_api api_funcs = {
|
|
.iface_api.init = eth_nxp_enet_iface_init,
|
|
.get_capabilities = eth_nxp_enet_get_capabilities,
|
|
.set_config = eth_nxp_enet_set_config,
|
|
.send = NXP_ENET_SEND_FUNC,
|
|
#if defined(CONFIG_PTP_CLOCK)
|
|
.get_ptp_clock = eth_nxp_enet_get_ptp_clock,
|
|
#endif
|
|
};
|
|
|
|
#define NXP_ENET_CONNECT_IRQ(node_id, irq_names, idx) \
|
|
do { \
|
|
IRQ_CONNECT(DT_IRQ_BY_IDX(node_id, idx, irq), \
|
|
DT_IRQ_BY_IDX(node_id, idx, priority), \
|
|
eth_nxp_enet_isr, \
|
|
DEVICE_DT_GET(node_id), \
|
|
0); \
|
|
irq_enable(DT_IRQ_BY_IDX(node_id, idx, irq)); \
|
|
} while (false);
|
|
|
|
#define NXP_ENET_DT_PHY_DEV(node_id, phy_phandle, idx) \
|
|
DEVICE_DT_GET(DT_PHANDLE_BY_IDX(node_id, phy_phandle, idx))
|
|
|
|
#if DT_NODE_HAS_STATUS(DT_CHOSEN(zephyr_dtcm), okay) && \
|
|
CONFIG_ETH_NXP_ENET_USE_DTCM_FOR_DMA_BUFFER
|
|
#define _nxp_enet_dma_desc_section __dtcm_bss_section
|
|
#define _nxp_enet_dma_buffer_section __dtcm_noinit_section
|
|
#define _nxp_enet_driver_buffer_section __dtcm_noinit_section
|
|
#elif defined(CONFIG_NOCACHE_MEMORY)
|
|
#define _nxp_enet_dma_desc_section __nocache
|
|
#define _nxp_enet_dma_buffer_section __nocache
|
|
#define _nxp_enet_driver_buffer_section
|
|
#else
|
|
#define _nxp_enet_dma_desc_section
|
|
#define _nxp_enet_dma_buffer_section
|
|
#define _nxp_enet_driver_buffer_section
|
|
#endif
|
|
|
|
/* Use ENET_FRAME_MAX_VLANFRAMELEN for VLAN frame size
|
|
* Use ENET_FRAME_MAX_FRAMELEN for Ethernet frame size
|
|
*/
|
|
#if defined(CONFIG_NET_VLAN)
|
|
#if !defined(ENET_FRAME_MAX_VLANFRAMELEN)
|
|
#define ENET_FRAME_MAX_VLANFRAMELEN (ENET_FRAME_MAX_FRAMELEN + 4)
|
|
#endif
|
|
#define ETH_NXP_ENET_BUFFER_SIZE \
|
|
ROUND_UP(ENET_FRAME_MAX_VLANFRAMELEN, ENET_BUFF_ALIGNMENT)
|
|
#else
|
|
#define ETH_NXP_ENET_BUFFER_SIZE \
|
|
ROUND_UP(ENET_FRAME_MAX_FRAMELEN, ENET_BUFF_ALIGNMENT)
|
|
#endif /* CONFIG_NET_VLAN */
|
|
|
|
#define NXP_ENET_PHY_MODE(node_id) \
|
|
DT_ENUM_HAS_VALUE(node_id, phy_connection_type, mii) ? NXP_ENET_MII_MODE : \
|
|
(DT_ENUM_HAS_VALUE(node_id, phy_connection_type, rmii) ? NXP_ENET_RMII_MODE : \
|
|
NXP_ENET_INVALID_MII_MODE)
|
|
|
|
#ifdef CONFIG_PTP_CLOCK_NXP_ENET
|
|
#define NXP_ENET_PTP_DEV(n) .ptp_clock = DEVICE_DT_GET(DT_INST_PHANDLE(n, nxp_ptp_clock)),
|
|
#define NXP_ENET_FRAMEINFO_ARRAY(n) \
|
|
static enet_frame_info_t \
|
|
nxp_enet_##n##_tx_frameinfo_array[CONFIG_ETH_NXP_ENET_TX_BUFFERS];
|
|
#define NXP_ENET_FRAMEINFO(n) \
|
|
.txFrameInfo = nxp_enet_##n##_tx_frameinfo_array,
|
|
#else
|
|
#define NXP_ENET_PTP_DEV(n)
|
|
#define NXP_ENET_FRAMEINFO_ARRAY(n)
|
|
#define NXP_ENET_FRAMEINFO(n) \
|
|
.txFrameInfo = NULL
|
|
#endif
|
|
|
|
#define NXP_ENET_NODE_HAS_MAC_ADDR_CHECK(n) \
|
|
BUILD_ASSERT(NODE_HAS_VALID_MAC_ADDR(DT_DRV_INST(n)) || \
|
|
DT_INST_PROP(n, zephyr_random_mac_address) || \
|
|
DT_INST_PROP(n, nxp_unique_mac), \
|
|
"MAC address not specified on ENET DT node");
|
|
|
|
#define NXP_ENET_MAC_INIT(n) \
|
|
NXP_ENET_NODE_HAS_MAC_ADDR_CHECK(n) \
|
|
\
|
|
PINCTRL_DT_INST_DEFINE(n); \
|
|
\
|
|
NXP_ENET_FRAMEINFO_ARRAY(n) \
|
|
\
|
|
static void nxp_enet_##n##_irq_config_func(void) \
|
|
{ \
|
|
DT_INST_FOREACH_PROP_ELEM(n, interrupt_names, \
|
|
NXP_ENET_CONNECT_IRQ); \
|
|
} \
|
|
\
|
|
volatile static __aligned(ENET_BUFF_ALIGNMENT) \
|
|
_nxp_enet_dma_desc_section \
|
|
enet_rx_bd_struct_t \
|
|
nxp_enet_##n##_rx_buffer_desc[CONFIG_ETH_NXP_ENET_RX_BUFFERS]; \
|
|
\
|
|
volatile static __aligned(ENET_BUFF_ALIGNMENT) \
|
|
_nxp_enet_dma_desc_section \
|
|
enet_tx_bd_struct_t \
|
|
nxp_enet_##n##_tx_buffer_desc[CONFIG_ETH_NXP_ENET_TX_BUFFERS]; \
|
|
\
|
|
static uint8_t __aligned(ENET_BUFF_ALIGNMENT) \
|
|
_nxp_enet_dma_buffer_section \
|
|
nxp_enet_##n##_rx_buffer[CONFIG_ETH_NXP_ENET_RX_BUFFERS] \
|
|
[ETH_NXP_ENET_BUFFER_SIZE]; \
|
|
\
|
|
static uint8_t __aligned(ENET_BUFF_ALIGNMENT) \
|
|
_nxp_enet_dma_buffer_section \
|
|
nxp_enet_##n##_tx_buffer[CONFIG_ETH_NXP_ENET_TX_BUFFERS] \
|
|
[ETH_NXP_ENET_BUFFER_SIZE]; \
|
|
\
|
|
const struct nxp_enet_mac_config nxp_enet_##n##_config = { \
|
|
.base = (ENET_Type *)DT_REG_ADDR(DT_INST_PARENT(n)), \
|
|
.irq_config_func = nxp_enet_##n##_irq_config_func, \
|
|
.clock_dev = DEVICE_DT_GET(DT_CLOCKS_CTLR(DT_INST_PARENT(n))), \
|
|
.clock_subsys = (void *)DT_CLOCKS_CELL_BY_IDX( \
|
|
DT_INST_PARENT(n), 0, name), \
|
|
.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
|
|
.buffer_config = { \
|
|
.rxBdNumber = CONFIG_ETH_NXP_ENET_RX_BUFFERS, \
|
|
.txBdNumber = CONFIG_ETH_NXP_ENET_TX_BUFFERS, \
|
|
.rxBuffSizeAlign = ETH_NXP_ENET_BUFFER_SIZE, \
|
|
.txBuffSizeAlign = ETH_NXP_ENET_BUFFER_SIZE, \
|
|
.rxBdStartAddrAlign = nxp_enet_##n##_rx_buffer_desc, \
|
|
.txBdStartAddrAlign = nxp_enet_##n##_tx_buffer_desc, \
|
|
.rxBufferAlign = nxp_enet_##n##_rx_buffer[0], \
|
|
.txBufferAlign = nxp_enet_##n##_tx_buffer[0], \
|
|
.rxMaintainEnable = true, \
|
|
.txMaintainEnable = true, \
|
|
NXP_ENET_FRAMEINFO(n) \
|
|
}, \
|
|
.phy_mode = NXP_ENET_PHY_MODE(DT_DRV_INST(n)), \
|
|
.phy_dev = DEVICE_DT_GET(DT_INST_PHANDLE(n, phy_handle)), \
|
|
.mdio = DEVICE_DT_GET(DT_INST_PHANDLE(n, nxp_mdio)), \
|
|
NXP_ENET_PTP_DEV(n) \
|
|
.generate_mac = DT_INST_PROP(n, \
|
|
zephyr_random_mac_address), \
|
|
.unique_mac = DT_INST_PROP(n, nxp_unique_mac), \
|
|
}; \
|
|
\
|
|
static _nxp_enet_driver_buffer_section uint8_t \
|
|
nxp_enet_##n##_tx_frame_buf[NET_ETH_MAX_FRAME_SIZE]; \
|
|
static _nxp_enet_driver_buffer_section uint8_t \
|
|
nxp_enet_##n##_rx_frame_buf[NET_ETH_MAX_FRAME_SIZE]; \
|
|
\
|
|
struct nxp_enet_mac_data nxp_enet_##n##_data = { \
|
|
.tx_frame_buf = nxp_enet_##n##_tx_frame_buf, \
|
|
.rx_frame_buf = nxp_enet_##n##_rx_frame_buf, \
|
|
.dev = DEVICE_DT_INST_GET(n), \
|
|
.mac_addr = DT_INST_PROP_OR(n, local_mac_address, {0}), \
|
|
}; \
|
|
\
|
|
ETH_NXP_ENET_PM_DEVICE_INIT(n) \
|
|
\
|
|
ETH_NET_DEVICE_DT_INST_DEFINE(n, eth_nxp_enet_init, \
|
|
ETH_NXP_ENET_PM_DEVICE_GET(n), \
|
|
&nxp_enet_##n##_data, &nxp_enet_##n##_config, \
|
|
CONFIG_ETH_INIT_PRIORITY, \
|
|
&api_funcs, NET_ETH_MTU);
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(NXP_ENET_MAC_INIT)
|
|
|
|
#undef DT_DRV_COMPAT
|
|
#define DT_DRV_COMPAT nxp_enet
|
|
|
|
#define NXP_ENET_INIT(n) \
|
|
\
|
|
int nxp_enet_##n##_init(void) \
|
|
{ \
|
|
clock_control_on(DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \
|
|
(void *)DT_INST_CLOCKS_CELL_BY_IDX(n, 0, name)); \
|
|
\
|
|
ENET_Reset((ENET_Type *)DT_INST_REG_ADDR(n)); \
|
|
\
|
|
return 0; \
|
|
} \
|
|
\
|
|
/* Init the module before any of the MAC, MDIO, or PTP clock */ \
|
|
SYS_INIT(nxp_enet_##n##_init, POST_KERNEL, 0);
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(NXP_ENET_INIT)
|