drivers: ethernet: add NXP i.MX NETC driver
Added NXP i.MX NETC driver based on NXP MCUX SDK driver APIs. Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
This commit is contained in:
parent
4986809581
commit
553079350c
7 changed files with 753 additions and 0 deletions
|
|
@ -47,6 +47,11 @@ if(CONFIG_ETH_NXP_S32_NETC)
|
|||
zephyr_library_sources_ifdef(CONFIG_DT_HAS_NXP_S32_NETC_VSI_ENABLED eth_nxp_s32_netc_vsi.c)
|
||||
endif()
|
||||
|
||||
if(CONFIG_ETH_NXP_IMX_NETC)
|
||||
zephyr_library_sources(eth_nxp_imx_netc.c)
|
||||
zephyr_library_sources(eth_nxp_imx_netc_psi.c)
|
||||
endif()
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_ETH_NXP_S32_GMAC eth_nxp_s32_gmac.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_ETH_NUMAKER eth_numaker.c)
|
||||
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ source "drivers/ethernet/Kconfig.w5500"
|
|||
source "drivers/ethernet/Kconfig.dsa"
|
||||
source "drivers/ethernet/Kconfig.xlnx_gem"
|
||||
source "drivers/ethernet/Kconfig.cyclonev"
|
||||
source "drivers/ethernet/Kconfig.nxp_imx_netc"
|
||||
source "drivers/ethernet/Kconfig.nxp_s32_netc"
|
||||
source "drivers/ethernet/Kconfig.nxp_s32_gmac"
|
||||
source "drivers/ethernet/Kconfig.smsc91x"
|
||||
|
|
|
|||
87
drivers/ethernet/Kconfig.nxp_imx_netc
Normal file
87
drivers/ethernet/Kconfig.nxp_imx_netc
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
# Copyright 2024 NXP
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig ETH_NXP_IMX_NETC
|
||||
bool "NXP IMX Ethernet and Network Controller (NETC) driver"
|
||||
default y
|
||||
depends on DT_HAS_NXP_IMX_NETC_PSI_ENABLED
|
||||
select MDIO
|
||||
select NOCACHE_MEMORY if ARCH_HAS_NOCACHE_MEMORY_SUPPORT
|
||||
help
|
||||
Enable Ethernet and Network Controller (NETC) driver for NXP IMX SoCs.
|
||||
|
||||
if ETH_NXP_IMX_NETC
|
||||
|
||||
config ETH_NXP_IMX_MSGINTR
|
||||
int "Message Interrupt module select"
|
||||
default 1
|
||||
help
|
||||
Message Interrupt module select.
|
||||
|
||||
config ETH_NXP_IMX_RX_THREAD_PRIO
|
||||
int "RX thread priority"
|
||||
default 2
|
||||
help
|
||||
RX thread priority. RX thread is a cooperative thread.
|
||||
|
||||
config ETH_NXP_IMX_RX_THREAD_STACK_SIZE
|
||||
int "RX thread stack size"
|
||||
default 1500
|
||||
help
|
||||
RX thread stack size.
|
||||
|
||||
config ETH_NXP_IMX_RX_BUDGET
|
||||
int "RX thread budget"
|
||||
default 128
|
||||
range 1 1024
|
||||
help
|
||||
The budget parameter places a limit on the amount of work the driver may
|
||||
do in the RX thread before yielding the processor, in case there is more
|
||||
work to do. This is to prevent the RX thread to starve other threads. Each
|
||||
received frame counts as one unit of work.
|
||||
|
||||
config ETH_NXP_IMX_TX_RING_NUM
|
||||
int "TX ring number"
|
||||
default 1
|
||||
range 1 1023
|
||||
help
|
||||
TX ring number used. The actual maximum value may varies from platforms.
|
||||
|
||||
config ETH_NXP_IMX_TX_RING_LEN
|
||||
int "TX ring length"
|
||||
default 8
|
||||
range 8 256
|
||||
help
|
||||
Length of the TX ring. The value must be a multiple of 8.
|
||||
|
||||
config ETH_NXP_IMX_TX_RING_BUF_SIZE
|
||||
int "TX ring data buffer size"
|
||||
default 1000
|
||||
range 64 1536
|
||||
help
|
||||
Size, in bytes, of the TX data buffer. The size must be big enough to
|
||||
store one complete Ethernet frame, and be a multiple of 8.
|
||||
|
||||
config ETH_NXP_IMX_RX_RING_NUM
|
||||
int "RX ring number"
|
||||
default 1
|
||||
range 1 1023
|
||||
help
|
||||
RX ring number used. The actual maximum value may varies from platforms.
|
||||
|
||||
config ETH_NXP_IMX_RX_RING_LEN
|
||||
int "RX ring length"
|
||||
default 8
|
||||
range 8 256
|
||||
help
|
||||
Length of the RX ring. The value must be a multiple of 8.
|
||||
|
||||
config ETH_NXP_IMX_RX_RING_BUF_SIZE
|
||||
int "RX ring data buffer size"
|
||||
default 1518
|
||||
range 64 1536
|
||||
help
|
||||
Size, in bytes, of the RX data buffer. The size must be big enough to
|
||||
store one complete Ethernet frame, and be a multiple of 8.
|
||||
|
||||
endif # ETH_NXP_IMX_NETC
|
||||
321
drivers/ethernet/eth_nxp_imx_netc.c
Normal file
321
drivers/ethernet/eth_nxp_imx_netc.c
Normal file
|
|
@ -0,0 +1,321 @@
|
|||
/*
|
||||
* Copyright 2024 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define LOG_LEVEL CONFIG_ETHERNET_LOG_LEVEL
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(nxp_imx_eth);
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/mbox.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 "eth.h"
|
||||
#include "eth_nxp_imx_netc_priv.h"
|
||||
|
||||
const struct device *netc_dev_list[NETC_DRV_MAX_INST_SUPPORT];
|
||||
|
||||
static int netc_eth_rx(const struct device *dev)
|
||||
{
|
||||
struct netc_eth_data *data = dev->data;
|
||||
struct net_pkt *pkt;
|
||||
int key;
|
||||
int ret = 0;
|
||||
status_t result;
|
||||
uint32_t length;
|
||||
|
||||
key = irq_lock();
|
||||
|
||||
/* Check rx frame */
|
||||
result = EP_GetRxFrameSize(&data->handle, 0, &length);
|
||||
if (result == kStatus_NETC_RxFrameEmpty) {
|
||||
ret = -ENOBUFS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (result != kStatus_Success) {
|
||||
LOG_ERR("Error on received frame");
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Receive frame */
|
||||
result = EP_ReceiveFrameCopy(&data->handle, 0, data->rx_frame, length, NULL);
|
||||
if (result != kStatus_Success) {
|
||||
LOG_ERR("Error on received frame");
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Copy to pkt */
|
||||
pkt = net_pkt_rx_alloc_with_buffer(data->iface, length, AF_UNSPEC, 0, NETC_TIMEOUT);
|
||||
if (!pkt) {
|
||||
eth_stats_update_errors_rx(data->iface);
|
||||
ret = -ENOBUFS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = net_pkt_write(pkt, data->rx_frame, length);
|
||||
if (ret) {
|
||||
eth_stats_update_errors_rx(data->iface);
|
||||
net_pkt_unref(pkt);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Send to upper layer */
|
||||
ret = net_recv_data(data->iface, pkt);
|
||||
if (ret < 0) {
|
||||
eth_stats_update_errors_rx(data->iface);
|
||||
net_pkt_unref(pkt);
|
||||
LOG_ERR("Failed to enqueue frame into rx queue: %d", ret);
|
||||
}
|
||||
out:
|
||||
irq_unlock(key);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void netc_eth_rx_thread(void *arg1, void *unused1, void *unused2)
|
||||
{
|
||||
const struct device *dev = (const struct device *)arg1;
|
||||
struct netc_eth_data *data = dev->data;
|
||||
int ret;
|
||||
int work;
|
||||
|
||||
ARG_UNUSED(unused1);
|
||||
ARG_UNUSED(unused2);
|
||||
|
||||
while (1) {
|
||||
ret = k_sem_take(&data->rx_sem, K_FOREVER);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Take rx_sem error: %d", ret);
|
||||
continue;
|
||||
}
|
||||
|
||||
work = 0;
|
||||
while (netc_eth_rx(dev) != -ENOBUFS) {
|
||||
if (++work == CONFIG_ETH_NXP_IMX_RX_BUDGET) {
|
||||
/* more work to do, reschedule */
|
||||
work = 0;
|
||||
k_yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void msgintr_isr(void)
|
||||
{
|
||||
uint32_t irqs = NETC_MSGINTR->MSI[NETC_MSGINTR_CHANNEL].MSIR;
|
||||
|
||||
for (int i = 0; i < NETC_DRV_MAX_INST_SUPPORT; i++) {
|
||||
const struct device *dev = netc_dev_list[i];
|
||||
const struct netc_eth_config *config;
|
||||
struct netc_eth_data *data;
|
||||
|
||||
if (!dev) {
|
||||
return;
|
||||
}
|
||||
|
||||
config = dev->config;
|
||||
data = dev->data;
|
||||
/* Transmit interrupt */
|
||||
if (irqs & (1 << config->tx_intr_msg_data)) {
|
||||
EP_CleanTxIntrFlags(&data->handle, 1, 0);
|
||||
data->tx_done = true;
|
||||
}
|
||||
/* Receive interrupt */
|
||||
if (irqs & (1 << config->rx_intr_msg_data)) {
|
||||
EP_CleanRxIntrFlags(&data->handle, 1);
|
||||
k_sem_give(&data->rx_sem);
|
||||
}
|
||||
}
|
||||
|
||||
SDK_ISR_EXIT_BARRIER;
|
||||
}
|
||||
|
||||
static status_t netc_eth_reclaim_callback(ep_handle_t *handle, uint8_t ring,
|
||||
netc_tx_frame_info_t *frameInfo, void *userData)
|
||||
{
|
||||
struct netc_eth_data *data = userData;
|
||||
|
||||
data->tx_info = *frameInfo;
|
||||
return kStatus_Success;
|
||||
}
|
||||
|
||||
int netc_eth_init_common(const struct device *dev)
|
||||
{
|
||||
const struct netc_eth_config *config = dev->config;
|
||||
struct netc_eth_data *data = dev->data;
|
||||
netc_msix_entry_t msix_entry[NETC_MSIX_ENTRY_NUM];
|
||||
netc_rx_bdr_config_t rx_bdr_config = {0};
|
||||
netc_tx_bdr_config_t tx_bdr_config = {0};
|
||||
netc_bdr_config_t bdr_config = {0};
|
||||
ep_config_t ep_config;
|
||||
uint32_t msg_addr;
|
||||
status_t result;
|
||||
|
||||
config->bdr_init(&bdr_config, &rx_bdr_config, &tx_bdr_config);
|
||||
|
||||
/* MSIX entry configuration */
|
||||
msg_addr = MSGINTR_GetIntrSelectAddr(NETC_MSGINTR, NETC_MSGINTR_CHANNEL);
|
||||
msix_entry[NETC_TX_MSIX_ENTRY_IDX].control = kNETC_MsixIntrMaskBit;
|
||||
msix_entry[NETC_TX_MSIX_ENTRY_IDX].msgAddr = msg_addr;
|
||||
msix_entry[NETC_TX_MSIX_ENTRY_IDX].msgData = config->tx_intr_msg_data;
|
||||
|
||||
msix_entry[NETC_RX_MSIX_ENTRY_IDX].control = kNETC_MsixIntrMaskBit;
|
||||
msix_entry[NETC_RX_MSIX_ENTRY_IDX].msgAddr = msg_addr;
|
||||
msix_entry[NETC_RX_MSIX_ENTRY_IDX].msgData = config->rx_intr_msg_data;
|
||||
|
||||
if (!irq_is_enabled(NETC_MSGINTR_IRQ)) {
|
||||
IRQ_CONNECT(NETC_MSGINTR_IRQ, 0, msgintr_isr, 0, 0);
|
||||
irq_enable(NETC_MSGINTR_IRQ);
|
||||
}
|
||||
|
||||
/* Endpoint configuration. */
|
||||
EP_GetDefaultConfig(&ep_config);
|
||||
ep_config.si = config->si_idx;
|
||||
ep_config.siConfig.txRingUse = 1;
|
||||
ep_config.siConfig.rxRingUse = 1;
|
||||
ep_config.userData = data;
|
||||
ep_config.reclaimCallback = netc_eth_reclaim_callback;
|
||||
ep_config.msixEntry = &msix_entry[0];
|
||||
ep_config.entryNum = NETC_MSIX_ENTRY_NUM;
|
||||
ep_config.port.ethMac.miiMode = kNETC_RmiiMode;
|
||||
ep_config.port.ethMac.miiSpeed = kNETC_MiiSpeed100M;
|
||||
ep_config.port.ethMac.miiDuplex = kNETC_MiiFullDuplex;
|
||||
ep_config.rxCacheMaintain = true;
|
||||
ep_config.txCacheMaintain = true;
|
||||
|
||||
config->generate_mac(&data->mac_addr[0]);
|
||||
|
||||
result = EP_Init(&data->handle, &data->mac_addr[0], &ep_config, &bdr_config);
|
||||
if (result != kStatus_Success) {
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
for (int i = 0; i < NETC_DRV_MAX_INST_SUPPORT; i++) {
|
||||
if (!netc_dev_list[i]) {
|
||||
netc_dev_list[i] = dev;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Unmask MSIX message interrupt. */
|
||||
EP_MsixSetEntryMask(&data->handle, NETC_TX_MSIX_ENTRY_IDX, false);
|
||||
EP_MsixSetEntryMask(&data->handle, NETC_RX_MSIX_ENTRY_IDX, false);
|
||||
|
||||
k_mutex_init(&data->tx_mutex);
|
||||
|
||||
k_sem_init(&data->rx_sem, 0, 1);
|
||||
k_thread_create(&data->rx_thread, data->rx_thread_stack,
|
||||
K_KERNEL_STACK_SIZEOF(data->rx_thread_stack), netc_eth_rx_thread,
|
||||
(void *)dev, NULL, NULL, K_PRIO_COOP(CONFIG_ETH_NXP_IMX_RX_THREAD_PRIO), 0,
|
||||
K_NO_WAIT);
|
||||
k_thread_name_set(&data->rx_thread, "netc_eth_rx");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int netc_eth_tx(const struct device *dev, struct net_pkt *pkt)
|
||||
{
|
||||
struct netc_eth_data *data = dev->data;
|
||||
netc_buffer_struct_t buff = {.buffer = data->tx_buff, .length = sizeof(data->tx_buff)};
|
||||
netc_frame_struct_t frame = {.buffArray = &buff, .length = 1};
|
||||
size_t pkt_len = net_pkt_get_len(pkt);
|
||||
status_t result;
|
||||
int ret;
|
||||
|
||||
__ASSERT(pkt, "Packet pointer is NULL");
|
||||
|
||||
k_mutex_lock(&data->tx_mutex, K_FOREVER);
|
||||
|
||||
/* Copy packet to tx buffer */
|
||||
buff.length = (uint16_t)pkt_len;
|
||||
ret = net_pkt_read(pkt, buff.buffer, pkt_len);
|
||||
if (ret) {
|
||||
LOG_ERR("Failed to copy packet to tx buffer: %d", ret);
|
||||
ret = -ENOBUFS;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Send */
|
||||
data->tx_done = false;
|
||||
result = EP_SendFrame(&data->handle, 0, &frame, NULL, NULL);
|
||||
if (result != kStatus_Success) {
|
||||
LOG_ERR("Failed to tx frame");
|
||||
ret = -EIO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
while (!data->tx_done) {
|
||||
}
|
||||
|
||||
EP_ReclaimTxDescriptor(&data->handle, 0);
|
||||
if (data->tx_info.status != kNETC_EPTxSuccess) {
|
||||
LOG_ERR("Failed to tx frame");
|
||||
ret = -EIO;
|
||||
goto error;
|
||||
}
|
||||
ret = 0;
|
||||
error:
|
||||
k_mutex_unlock(&data->tx_mutex);
|
||||
|
||||
if (ret != 0) {
|
||||
eth_stats_update_errors_tx(data->iface);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum ethernet_hw_caps netc_eth_get_capabilities(const struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
return (ETHERNET_LINK_10BASE_T | ETHERNET_LINK_100BASE_T | ETHERNET_LINK_1000BASE_T |
|
||||
ETHERNET_HW_RX_CHKSUM_OFFLOAD | ETHERNET_HW_FILTERING
|
||||
#if defined(CONFIG_NET_VLAN)
|
||||
| ETHERNET_HW_VLAN
|
||||
#endif
|
||||
#if defined(CONFIG_NET_PROMISCUOUS_MODE)
|
||||
| ETHERNET_PROMISC_MODE
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
int netc_eth_set_config(const struct device *dev, enum ethernet_config_type type,
|
||||
const struct ethernet_config *config)
|
||||
{
|
||||
struct netc_eth_data *data = dev->data;
|
||||
const struct netc_eth_config *cfg = dev->config;
|
||||
status_t result;
|
||||
int ret = 0;
|
||||
|
||||
switch (type) {
|
||||
case ETHERNET_CONFIG_TYPE_MAC_ADDRESS:
|
||||
/* Set new Ethernet MAC address and register it with the upper layer */
|
||||
memcpy(data->mac_addr, config->mac_address.addr, sizeof(data->mac_addr));
|
||||
result = EP_SetPrimaryMacAddr(&data->handle, (uint8_t *)data->mac_addr);
|
||||
if (result != kStatus_Success) {
|
||||
LOG_ERR("PHY device (%p) is not ready, cannot init iface", cfg->phy_dev);
|
||||
ret = -ENOTSUP;
|
||||
break;
|
||||
}
|
||||
net_if_set_link_addr(data->iface, data->mac_addr, sizeof(data->mac_addr),
|
||||
NET_LINK_ETHERNET);
|
||||
LOG_INF("SI%d MAC set to: %02x:%02x:%02x:%02x:%02x:%02x", getSiIdx(cfg->si_idx),
|
||||
data->mac_addr[0], data->mac_addr[1], data->mac_addr[2], data->mac_addr[3],
|
||||
data->mac_addr[4], data->mac_addr[5]);
|
||||
break;
|
||||
default:
|
||||
ret = -ENOTSUP;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
121
drivers/ethernet/eth_nxp_imx_netc_priv.h
Normal file
121
drivers/ethernet/eth_nxp_imx_netc_priv.h
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright 2024 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_ETHERNET_ETH_NXP_IMX_NETC_PRIV_H_
|
||||
#define ZEPHYR_DRIVERS_ETHERNET_ETH_NXP_IMX_NETC_PRIV_H_
|
||||
|
||||
#include "fsl_netc_endpoint.h"
|
||||
#include "fsl_msgintr.h"
|
||||
|
||||
/* Buffer and descriptor alignment */
|
||||
#define NETC_BD_ALIGN 128
|
||||
#define NETC_BUFF_ALIGN 64
|
||||
#define NETC_RX_RING_BUF_SIZE_ALIGN \
|
||||
SDK_SIZEALIGN(CONFIG_ETH_NXP_IMX_RX_RING_BUF_SIZE, NETC_BUFF_ALIGN)
|
||||
|
||||
/* MSIX definitions */
|
||||
#define NETC_TX_MSIX_ENTRY_IDX 0
|
||||
#define NETC_RX_MSIX_ENTRY_IDX 1
|
||||
#define NETC_MSIX_ENTRY_NUM 2
|
||||
|
||||
#define NETC_MSIX_EVENTS_COUNT NETC_MSIX_ENTRY_NUM
|
||||
#define NETC_TX_INTR_MSG_DATA_START 0
|
||||
#define NETC_RX_INTR_MSG_DATA_START 16
|
||||
#define NETC_DRV_MAX_INST_SUPPORT 16
|
||||
|
||||
/* MSGINTR */
|
||||
#define NETC_MSGINTR_CHANNEL 0
|
||||
|
||||
#if (CONFIG_ETH_NXP_IMX_MSGINTR == 1)
|
||||
#define NETC_MSGINTR MSGINTR1
|
||||
#define NETC_MSGINTR_IRQ MSGINTR1_IRQn
|
||||
#elif (CONFIG_ETH_NXP_IMX_MSGINTR == 2)
|
||||
#define NETC_MSGINTR MSGINTR2
|
||||
#define NETC_MSGINTR_IRQ MSGINTR2_IRQn
|
||||
#else
|
||||
#error "Current CONFIG_ETH_NXP_IMX_MSGINTR not support"
|
||||
#endif
|
||||
|
||||
/* Timeout for various operations */
|
||||
#define NETC_TIMEOUT K_MSEC(20)
|
||||
|
||||
/* Helper macros to convert from Zephyr PHY speed to NETC speed/duplex types */
|
||||
#define PHY_TO_NETC_SPEED(x) \
|
||||
(PHY_LINK_IS_SPEED_1000M(x) \
|
||||
? kNETC_MiiSpeed1000M \
|
||||
: (PHY_LINK_IS_SPEED_100M(x) ? kNETC_MiiSpeed100M : kNETC_MiiSpeed10M))
|
||||
|
||||
#define PHY_TO_NETC_DUPLEX_MODE(x) \
|
||||
(PHY_LINK_IS_FULL_DUPLEX(x) ? kNETC_MiiFullDuplex : kNETC_MiiHalfDuplex)
|
||||
|
||||
/* Helper function to generate an Ethernet MAC address for a given ENETC instance */
|
||||
#define FREESCALE_OUI_B0 0x00
|
||||
#define FREESCALE_OUI_B1 0x04
|
||||
#define FREESCALE_OUI_B2 0x9f
|
||||
|
||||
#define _NETC_GENERATE_MAC_ADDRESS_RANDOM \
|
||||
gen_random_mac(mac_addr, FREESCALE_OUI_B0, FREESCALE_OUI_B1, FREESCALE_OUI_B2)
|
||||
|
||||
#define _NETC_GENERATE_MAC_ADDRESS_UNIQUE(n) \
|
||||
do { \
|
||||
uint32_t id = 0x001100; \
|
||||
\
|
||||
mac_addr[0] = FREESCALE_OUI_B0; \
|
||||
mac_addr[1] = FREESCALE_OUI_B1; \
|
||||
/* Set MAC address locally administered bit (LAA) */ \
|
||||
mac_addr[2] = FREESCALE_OUI_B2 | 0x02; \
|
||||
mac_addr[3] = (id >> 16) & 0xff; \
|
||||
mac_addr[4] = (id >> 8) & 0xff; \
|
||||
mac_addr[5] = (id + n) & 0xff; \
|
||||
} while (0)
|
||||
|
||||
#define NETC_GENERATE_MAC_ADDRESS(n) \
|
||||
static void netc_eth##n##_generate_mac(uint8_t mac_addr[6]) \
|
||||
{ \
|
||||
COND_CODE_1(DT_INST_PROP(n, zephyr_random_mac_address), \
|
||||
(_NETC_GENERATE_MAC_ADDRESS_RANDOM), \
|
||||
(COND_CODE_0(DT_INST_NODE_HAS_PROP(n, local_mac_address), \
|
||||
(_NETC_GENERATE_MAC_ADDRESS_UNIQUE(n)), \
|
||||
(ARG_UNUSED(mac_addr))))); \
|
||||
}
|
||||
|
||||
struct netc_eth_config {
|
||||
uint16_t si_idx;
|
||||
const struct device *phy_dev;
|
||||
void (*generate_mac)(uint8_t *mac_addr);
|
||||
void (*bdr_init)(netc_bdr_config_t *bdr_config, netc_rx_bdr_config_t *rx_bdr_config,
|
||||
netc_tx_bdr_config_t *tx_bdr_config);
|
||||
const struct pinctrl_dev_config *pincfg;
|
||||
uint8_t tx_intr_msg_data;
|
||||
uint8_t rx_intr_msg_data;
|
||||
};
|
||||
|
||||
typedef uint8_t rx_buffer_t[NETC_RX_RING_BUF_SIZE_ALIGN];
|
||||
|
||||
struct netc_eth_data {
|
||||
ep_handle_t handle;
|
||||
struct net_if *iface;
|
||||
uint8_t mac_addr[6];
|
||||
/* TX */
|
||||
struct k_mutex tx_mutex;
|
||||
netc_tx_frame_info_t tx_info;
|
||||
uint8_t *tx_buff;
|
||||
volatile bool tx_done;
|
||||
/* RX */
|
||||
struct k_sem rx_sem;
|
||||
struct k_thread rx_thread;
|
||||
|
||||
K_KERNEL_STACK_MEMBER(rx_thread_stack, CONFIG_ETH_NXP_IMX_RX_THREAD_STACK_SIZE);
|
||||
uint8_t *rx_frame;
|
||||
};
|
||||
|
||||
int netc_eth_init_common(const struct device *dev);
|
||||
int netc_eth_tx(const struct device *dev, struct net_pkt *pkt);
|
||||
enum ethernet_hw_caps netc_eth_get_capabilities(const struct device *dev);
|
||||
int netc_eth_set_config(const struct device *dev, enum ethernet_config_type type,
|
||||
const struct ethernet_config *config);
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_ETHERNET_ETH_NXP_IMX_NETC_PRIV_H_ */
|
||||
193
drivers/ethernet/eth_nxp_imx_netc_psi.c
Normal file
193
drivers/ethernet/eth_nxp_imx_netc_psi.c
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* Copyright 2024 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT nxp_imx_netc_psi
|
||||
|
||||
#define LOG_LEVEL CONFIG_ETHERNET_LOG_LEVEL
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(nxp_imx_eth_psi);
|
||||
|
||||
#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 "eth.h"
|
||||
#include "eth_nxp_imx_netc_priv.h"
|
||||
|
||||
static void netc_eth_phylink_callback(const struct device *pdev, struct phy_link_state *state,
|
||||
void *user_data)
|
||||
{
|
||||
const struct device *dev = (struct device *)user_data;
|
||||
struct netc_eth_data *data = dev->data;
|
||||
status_t result;
|
||||
|
||||
ARG_UNUSED(pdev);
|
||||
|
||||
if (state->is_up) {
|
||||
LOG_DBG("Link up");
|
||||
result = EP_Up(&data->handle, PHY_TO_NETC_SPEED(state->speed),
|
||||
PHY_TO_NETC_DUPLEX_MODE(state->speed));
|
||||
if (result != kStatus_Success) {
|
||||
LOG_ERR("Failed to set MAC up");
|
||||
}
|
||||
net_eth_carrier_on(data->iface);
|
||||
} else {
|
||||
LOG_DBG("Link down");
|
||||
result = EP_Down(&data->handle);
|
||||
if (result != kStatus_Success) {
|
||||
LOG_ERR("Failed to set MAC down");
|
||||
}
|
||||
net_eth_carrier_off(data->iface);
|
||||
}
|
||||
}
|
||||
|
||||
static void netc_eth_iface_init(struct net_if *iface)
|
||||
{
|
||||
const struct device *dev = net_if_get_device(iface);
|
||||
struct netc_eth_data *data = dev->data;
|
||||
const struct netc_eth_config *cfg = dev->config;
|
||||
status_t result;
|
||||
|
||||
/*
|
||||
* For VLAN, this value is only used to get the correct L2 driver.
|
||||
* The iface pointer in context should contain the main interface
|
||||
* if the VLANs are enabled.
|
||||
*/
|
||||
if (data->iface == NULL) {
|
||||
data->iface = iface;
|
||||
}
|
||||
|
||||
/* Set MAC address */
|
||||
result = EP_SetPrimaryMacAddr(&data->handle, (uint8_t *)data->mac_addr);
|
||||
if (result != kStatus_Success) {
|
||||
LOG_ERR("Failed to set MAC address");
|
||||
}
|
||||
|
||||
net_if_set_link_addr(iface, data->mac_addr, sizeof(data->mac_addr), NET_LINK_ETHERNET);
|
||||
|
||||
LOG_INF("SI%d MAC: %02x:%02x:%02x:%02x:%02x:%02x", cfg->si_idx, data->mac_addr[0],
|
||||
data->mac_addr[1], data->mac_addr[2], data->mac_addr[3], data->mac_addr[4],
|
||||
data->mac_addr[5]);
|
||||
|
||||
ethernet_init(iface);
|
||||
|
||||
/*
|
||||
* PSI 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, &netc_eth_phylink_callback, (void *)dev);
|
||||
|
||||
/* Do not start the interface until PHY link is up */
|
||||
net_if_carrier_off(iface);
|
||||
}
|
||||
|
||||
static int netc_eth_init(const struct device *dev)
|
||||
{
|
||||
const struct netc_eth_config *cfg = dev->config;
|
||||
int err;
|
||||
|
||||
err = pinctrl_apply_state(cfg->pincfg, PINCTRL_STATE_DEFAULT);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return netc_eth_init_common(dev);
|
||||
}
|
||||
|
||||
static const struct device *netc_eth_get_phy(const struct device *dev)
|
||||
{
|
||||
const struct netc_eth_config *cfg = dev->config;
|
||||
|
||||
return cfg->phy_dev;
|
||||
}
|
||||
|
||||
static const struct ethernet_api netc_eth_api = {.iface_api.init = netc_eth_iface_init,
|
||||
.get_capabilities = netc_eth_get_capabilities,
|
||||
.get_phy = netc_eth_get_phy,
|
||||
.set_config = netc_eth_set_config,
|
||||
.send = netc_eth_tx};
|
||||
|
||||
#define NETC_PSI_INSTANCE_DEFINE(n) \
|
||||
PINCTRL_DT_INST_DEFINE(n); \
|
||||
NETC_GENERATE_MAC_ADDRESS(n) \
|
||||
AT_NONCACHEABLE_SECTION_ALIGN( \
|
||||
static uint8_t eth##n##_tx_buff[CONFIG_ETH_NXP_IMX_TX_RING_BUF_SIZE], \
|
||||
NETC_BUFF_ALIGN); \
|
||||
AT_NONCACHEABLE_SECTION_ALIGN( \
|
||||
static netc_tx_bd_t eth##n##_txbd_array[CONFIG_ETH_NXP_IMX_TX_RING_NUM] \
|
||||
[CONFIG_ETH_NXP_IMX_TX_RING_LEN], \
|
||||
NETC_BD_ALIGN); \
|
||||
static netc_tx_frame_info_t eth##n##_txdirty_array[CONFIG_ETH_NXP_IMX_TX_RING_NUM] \
|
||||
[CONFIG_ETH_NXP_IMX_TX_RING_LEN]; \
|
||||
AT_NONCACHEABLE_SECTION_ALIGN( \
|
||||
static rx_buffer_t eth##n##_rx_buff[CONFIG_ETH_NXP_IMX_RX_RING_NUM] \
|
||||
[CONFIG_ETH_NXP_IMX_RX_RING_LEN], \
|
||||
NETC_BUFF_ALIGN); \
|
||||
static uint64_t eth##n##_rx_buff_addr_array[CONFIG_ETH_NXP_IMX_RX_RING_NUM] \
|
||||
[CONFIG_ETH_NXP_IMX_RX_RING_LEN]; \
|
||||
AT_NONCACHEABLE_SECTION(static uint8_t eth##n##_rx_frame[NETC_RX_RING_BUF_SIZE_ALIGN]); \
|
||||
AT_NONCACHEABLE_SECTION_ALIGN( \
|
||||
static netc_rx_bd_t eth##n##_rxbd_array[CONFIG_ETH_NXP_IMX_RX_RING_NUM] \
|
||||
[CONFIG_ETH_NXP_IMX_RX_RING_LEN], \
|
||||
NETC_BD_ALIGN); \
|
||||
static void netc_eth##n##_bdr_init(netc_bdr_config_t *bdr_config, \
|
||||
netc_rx_bdr_config_t *rx_bdr_config, \
|
||||
netc_tx_bdr_config_t *tx_bdr_config) \
|
||||
{ \
|
||||
for (uint8_t ring = 0U; ring < CONFIG_ETH_NXP_IMX_RX_RING_NUM; ring++) { \
|
||||
for (uint8_t bd = 0U; bd < CONFIG_ETH_NXP_IMX_RX_RING_LEN; bd++) { \
|
||||
eth##n##_rx_buff_addr_array[ring][bd] = \
|
||||
(uint64_t)(uintptr_t)ð##n##_rx_buff[ring][bd]; \
|
||||
} \
|
||||
} \
|
||||
memset(bdr_config, 0, sizeof(netc_bdr_config_t)); \
|
||||
memset(rx_bdr_config, 0, sizeof(netc_rx_bdr_config_t)); \
|
||||
memset(tx_bdr_config, 0, sizeof(netc_tx_bdr_config_t)); \
|
||||
bdr_config->rxBdrConfig = rx_bdr_config; \
|
||||
bdr_config->txBdrConfig = tx_bdr_config; \
|
||||
bdr_config->rxBdrConfig[0].bdArray = ð##n##_rxbd_array[0][0]; \
|
||||
bdr_config->rxBdrConfig[0].len = CONFIG_ETH_NXP_IMX_RX_RING_LEN; \
|
||||
bdr_config->rxBdrConfig[0].buffAddrArray = ð##n##_rx_buff_addr_array[0][0]; \
|
||||
bdr_config->rxBdrConfig[0].buffSize = NETC_RX_RING_BUF_SIZE_ALIGN; \
|
||||
bdr_config->rxBdrConfig[0].msixEntryIdx = NETC_RX_MSIX_ENTRY_IDX; \
|
||||
bdr_config->rxBdrConfig[0].extendDescEn = false; \
|
||||
bdr_config->rxBdrConfig[0].enThresIntr = true; \
|
||||
bdr_config->rxBdrConfig[0].enCoalIntr = true; \
|
||||
bdr_config->rxBdrConfig[0].intrThreshold = 1; \
|
||||
bdr_config->txBdrConfig[0].bdArray = ð##n##_txbd_array[0][0]; \
|
||||
bdr_config->txBdrConfig[0].len = CONFIG_ETH_NXP_IMX_TX_RING_LEN; \
|
||||
bdr_config->txBdrConfig[0].dirtyArray = ð##n##_txdirty_array[0][0]; \
|
||||
bdr_config->txBdrConfig[0].msixEntryIdx = NETC_TX_MSIX_ENTRY_IDX; \
|
||||
bdr_config->txBdrConfig[0].enIntr = true; \
|
||||
} \
|
||||
static struct netc_eth_data netc_eth##n##_data = { \
|
||||
.mac_addr = DT_INST_PROP_OR(n, local_mac_address, {0}), \
|
||||
.tx_buff = eth##n##_tx_buff, \
|
||||
.rx_frame = eth##n##_rx_frame, \
|
||||
}; \
|
||||
static const struct netc_eth_config netc_eth##n##_config = { \
|
||||
.generate_mac = netc_eth##n##_generate_mac, \
|
||||
.bdr_init = netc_eth##n##_bdr_init, \
|
||||
.phy_dev = DEVICE_DT_GET(DT_INST_PHANDLE(n, phy_handle)), \
|
||||
.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
|
||||
.si_idx = (DT_INST_PROP(n, mac_index) << 8) | DT_INST_PROP(n, si_index), \
|
||||
.tx_intr_msg_data = NETC_TX_INTR_MSG_DATA_START + n, \
|
||||
.rx_intr_msg_data = NETC_RX_INTR_MSG_DATA_START + n, \
|
||||
}; \
|
||||
ETH_NET_DEVICE_DT_INST_DEFINE(n, netc_eth_init, NULL, &netc_eth##n##_data, \
|
||||
&netc_eth##n##_config, CONFIG_ETH_INIT_PRIORITY, \
|
||||
&netc_eth_api, NET_ETH_MTU);
|
||||
DT_INST_FOREACH_STATUS_OKAY(NETC_PSI_INSTANCE_DEFINE)
|
||||
25
dts/bindings/ethernet/nxp,imx-netc-psi.yaml
Normal file
25
dts/bindings/ethernet/nxp,imx-netc-psi.yaml
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# Copyright 2024 NXP
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: NXP i.MX NETC Physical Station Interface (PSI)
|
||||
|
||||
compatible: "nxp,imx-netc-psi"
|
||||
|
||||
include: [ethernet-controller.yaml, pinctrl-device.yaml]
|
||||
|
||||
properties:
|
||||
reg:
|
||||
required: true
|
||||
|
||||
phy-handle:
|
||||
required: true
|
||||
|
||||
mac-index:
|
||||
required: true
|
||||
type: int
|
||||
description: The MAC index of this PSI.
|
||||
|
||||
si-index:
|
||||
required: true
|
||||
type: int
|
||||
description: The SI index of this PSI.
|
||||
Loading…
Reference in a new issue