blob: 6a2ee666e15abc121cd6ff35ad914e42c5336ee8 [file] [log] [blame]
/*
* Copyright (c) 2025 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(eth_intel_igc, CONFIG_ETHERNET_LOG_LEVEL);
#include <errno.h>
#include <soc.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <zephyr/device.h>
#include <zephyr/init.h>
#include <zephyr/kernel.h>
#include <zephyr/net/net_core.h>
#include <zephyr/net/net_pkt.h>
#include <zephyr/net/ethernet.h>
#include <zephyr/net/phy.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/sys/crc.h>
#include "../eth.h"
#include "eth_intel_igc_priv.h"
#define DT_DRV_COMPAT intel_igc_mac
/**
* @brief Amend the register value as per the mask and set/clear the bit.
*/
static void igc_modify(mm_reg_t base, uint32_t offset, uint32_t config, bool set)
{
uint32_t val = sys_read32(base + offset);
if (set) {
val |= config;
} else {
val &= ~config;
}
sys_write32(val, base + offset);
}
/**
* @brief Significant register changes required another register operation
* to take effect. This status register read mimics that logic.
*/
static void igc_reg_refresh(mm_reg_t base)
{
sys_read32(base + INTEL_IGC_STATUS);
}
/**
* @brief Get the index of a specific transmit descriptor within the ring.
* This getter also works for multiple queues.
*/
static uint16_t get_tx_desc_idx(const struct device *dev, union dma_tx_desc *current_desc,
uint8_t queue)
{
const struct eth_intel_igc_mac_cfg *cfg = dev->config;
struct eth_intel_igc_mac_data *data = dev->data;
union dma_tx_desc *tx_desc_base;
tx_desc_base = (union dma_tx_desc *)&data->tx.desc[queue * cfg->num_tx_desc];
return (current_desc - tx_desc_base);
}
/**
* @brief Get the index of a specific receive descriptor within the ring.
* This getter also works for multiple queues.
*/
static uint16_t get_rx_desc_idx(const struct device *dev, union dma_rx_desc *current_desc,
uint8_t queue)
{
const struct eth_intel_igc_mac_cfg *cfg = dev->config;
struct eth_intel_igc_mac_data *data = dev->data;
union dma_rx_desc *rx_desc_base;
rx_desc_base = (union dma_rx_desc *)&data->rx.desc[queue * cfg->num_rx_desc];
return (current_desc - rx_desc_base);
}
/**
* @brief Check if the next descriptor is unavailable for DMA operation.
*/
static bool is_desc_unavailable(uint32_t next_desc_idx, uint32_t current_rd_ptr_idx)
{
return (next_desc_idx == current_rd_ptr_idx);
}
/**
* @brief Check if the DMA operation is complete by using the writeback.dd bit.
*/
static bool is_dma_done(bool dd)
{
return dd;
}
/**
* @brief This function retrieves the next available transmit descriptor from the ring
* and ensures that the descriptor is available for DMA operation.
*/
static union dma_tx_desc *eth_intel_igc_get_tx_desc(const struct device *dev, uint8_t queue)
{
const struct eth_intel_igc_mac_cfg *cfg = dev->config;
struct eth_intel_igc_mac_data *data = dev->data;
uint32_t current_wr_idx, next_wr_idx;
union dma_tx_desc *desc;
k_sem_take(&data->tx.sem[queue], K_FOREVER);
current_wr_idx = data->tx.ring_wr_ptr[queue];
next_wr_idx = (current_wr_idx + 1) % cfg->num_tx_desc;
if (is_desc_unavailable(next_wr_idx, data->tx.ring_rd_ptr[queue])) {
k_sem_give(&data->tx.sem[queue]);
return NULL;
}
desc = data->tx.desc + (queue * cfg->num_tx_desc + current_wr_idx);
data->tx.ring_wr_ptr[queue] = next_wr_idx;
k_sem_give(&data->tx.sem[queue]);
return desc;
}
/**
* @brief This function checks if the DMA operation is complete by verifying the
* writeback.dd bit. If the DMA operation is complete, it updates the read pointer
* and clears the descriptor.
*/
static union dma_tx_desc *eth_intel_igc_release_tx_desc(const struct device *dev, uint8_t queue)
{
const struct eth_intel_igc_mac_cfg *cfg = dev->config;
struct eth_intel_igc_mac_data *data = dev->data;
uint32_t current_rd_idx, next_rd_idx;
union dma_tx_desc *desc;
k_sem_take(&data->tx.sem[queue], K_FOREVER);
current_rd_idx = data->tx.ring_rd_ptr[queue];
desc = data->tx.desc + (queue * cfg->num_tx_desc + current_rd_idx);
if (is_dma_done(desc->writeback.dd)) {
next_rd_idx = (current_rd_idx + 1) % cfg->num_tx_desc;
data->tx.ring_rd_ptr[queue] = next_rd_idx;
memset((void *)desc, 0, sizeof(union dma_tx_desc));
} else {
desc = NULL;
}
k_sem_give(&data->tx.sem[queue]);
return desc;
}
/**
* @brief This function returns the total number of consumed transmit descriptors from
* overall transmit descriptor ring of the mentioned queue.
*/
static uint32_t eth_intel_igc_completed_txdesc_num(const struct device *dev, uint8_t queue)
{
const struct eth_intel_igc_mac_cfg *cfg = dev->config;
struct eth_intel_igc_mac_data *data = dev->data;
uint32_t rd_idx, count = 0;
union dma_tx_desc *desc;
rd_idx = data->tx.ring_rd_ptr[queue];
while (rd_idx != data->tx.ring_wr_ptr[queue]) {
desc = (data->tx.desc + (queue * cfg->num_tx_desc + rd_idx));
if (!is_dma_done(desc->writeback.dd)) {
break;
}
rd_idx = (rd_idx + 1) % cfg->num_tx_desc;
count++;
}
return count;
}
/**
* @brief This function retrieves the next available receive descriptor from the ring
* and ensures that the descriptor is available for DMA operation.
*/
static union dma_rx_desc *eth_intel_igc_get_rx_desc(const struct device *dev, uint8_t queue)
{
const struct eth_intel_igc_mac_cfg *cfg = dev->config;
struct eth_intel_igc_mac_data *data = dev->data;
uint32_t current_wr_idx, next_wr_idx;
union dma_rx_desc *desc;
k_sem_take(&data->rx.sem[queue], K_FOREVER);
current_wr_idx = data->rx.ring_wr_ptr[queue];
next_wr_idx = (current_wr_idx + 1) % cfg->num_rx_desc;
if (is_desc_unavailable(next_wr_idx, data->rx.ring_rd_ptr[queue])) {
k_sem_give(&data->rx.sem[queue]);
return NULL;
}
desc = data->rx.desc + (queue * cfg->num_rx_desc + current_wr_idx);
data->rx.ring_wr_ptr[queue] = next_wr_idx;
k_sem_give(&data->rx.sem[queue]);
return desc;
}
/**
* @brief This function checks if the DMA operation is complete by verifying the
* writeback.dd bit. If the DMA operation is complete, it updates the read pointer
* and clears the descriptor.
*/
static union dma_rx_desc *eth_intel_igc_release_rx_desc(const struct device *dev, uint8_t queue)
{
const struct eth_intel_igc_mac_cfg *cfg = dev->config;
struct eth_intel_igc_mac_data *data = dev->data;
uint32_t current_rd_idx, next_rd_idx;
union dma_rx_desc *desc = NULL;
k_sem_take(&data->rx.sem[queue], K_FOREVER);
current_rd_idx = data->rx.ring_rd_ptr[queue];
desc = data->rx.desc + (queue * cfg->num_rx_desc + current_rd_idx);
if (is_dma_done(desc->writeback.dd)) {
next_rd_idx = (current_rd_idx + 1) % cfg->num_rx_desc;
data->rx.ring_rd_ptr[queue] = next_rd_idx;
} else {
desc = NULL;
}
k_sem_give(&data->rx.sem[queue]);
return desc;
}
/**
* @brief This function return total number of consumed receive descriptors from overall
* receive descriptor ring of the mentioned queue.
*/
static uint32_t eth_intel_igc_completed_rxdesc_num(const struct device *dev, uint8_t queue)
{
const struct eth_intel_igc_mac_cfg *cfg = dev->config;
struct eth_intel_igc_mac_data *data = dev->data;
union dma_rx_desc *desc = NULL;
uint32_t idx, count = 0;
idx = data->rx.ring_rd_ptr[queue];
while (idx != data->rx.ring_wr_ptr[queue]) {
desc = (data->rx.desc + (queue * cfg->num_rx_desc + idx));
if (!is_dma_done(desc->writeback.dd)) {
break;
}
idx = (idx + 1) % cfg->num_rx_desc;
count++;
}
return count;
}
/**
* @brief Interrupt Service Routine for handling queue interrupts.
*/
static void eth_intel_igc_queue_isr(void *arg)
{
const struct eth_intel_igc_intr_info *info = (struct eth_intel_igc_intr_info *)arg;
const struct device *dev = info->mac;
struct eth_intel_igc_mac_data *data = dev->data;
uint8_t msix = info->id;
igc_modify(data->base, INTEL_IGC_EICS, BIT(msix), false);
k_work_submit(&data->tx_work[msix]);
k_work_schedule(&data->rx_work[msix], K_MSEC(0));
sys_read32(data->base + INTEL_IGC_ICR);
igc_modify(data->base, INTEL_IGC_EIMS, BIT(msix), true);
}
static int eth_intel_igc_connect_queue_msix_vector(pcie_bdf_t bdf, const struct device *dev)
{
const struct eth_intel_igc_mac_cfg *cfg = dev->config;
struct eth_intel_igc_mac_data *data = dev->data;
for (uint8_t msix = 0; msix < cfg->num_queues; msix++) {
struct eth_intel_igc_intr_info *info = &data->intr_info[msix];
info->id = msix;
info->mac = dev;
if (!pcie_msi_vector_connect(bdf, &data->msi_vec[msix],
(void *)eth_intel_igc_queue_isr, info, 0)) {
LOG_ERR("Failed to connect queue_%d interrupt handler", msix);
return -EIO;
}
}
return 0;
}
static int eth_intel_igc_pcie_msix_setup(const struct device *dev)
{
const struct eth_intel_igc_mac_cfg *cfg = dev->config;
struct eth_intel_igc_mac_data *data = dev->data;
pcie_bdf_t bdf;
int ret;
bdf = eth_intel_get_pcie_bdf(cfg->platform);
if (bdf == PCIE_BDF_NONE) {
LOG_ERR("Failed to get PCIe BDF");
return -EINVAL;
}
ret = pcie_msi_vectors_allocate(bdf, CONFIG_ETH_INTEL_IGC_INT_PRIORITY, data->msi_vec,
cfg->num_msix);
if (ret < cfg->num_msix) {
LOG_ERR("Failed to allocate MSI-X vectors");
return -EIO;
}
ret = eth_intel_igc_connect_queue_msix_vector(bdf, dev);
if (ret < 0) {
return ret;
}
if (!pcie_msi_enable(bdf, data->msi_vec, cfg->num_msix, 0)) {
LOG_ERR("Failed to enable MSI-X vectors");
return -EIO;
}
return 0;
}
/**
* @brief This function maps the IGC device interrupts order to MSI-X vectors.
*/
static void eth_intel_igc_map_intr_to_vector(const struct device *dev)
{
const struct eth_intel_igc_mac_cfg *cfg = dev->config;
struct eth_intel_igc_mac_data *data = dev->data;
uint32_t config, reg_idx;
/* Set MSI-X capability */
config = INTEL_IGC_GPIE_NSICR | INTEL_IGC_GPIE_MSIX_MODE | INTEL_IGC_GPIE_EIAME |
INTEL_IGC_GPIE_PBA;
igc_modify(data->base, INTEL_IGC_GPIE, config, true);
/* Configure IVAR RX and TX for each queue interrupt */
for (uint8_t msix = 0; msix < cfg->num_queues; msix++) {
reg_idx = msix >> 1;
config = sys_read32(data->base + (INTEL_IGC_IVAR_BASE_ADDR + (reg_idx << 2)));
if (msix % 2) {
config &= INTEL_IGC_IVAR_MSI_CLEAR_TX1_TX3;
config |= (msix | INTEL_IGC_IVAR_INT_VALID_BIT) << 24;
config &= INTEL_IGC_IVAR_MSI_CLEAR_RX1_RX3;
config |= (msix | INTEL_IGC_IVAR_INT_VALID_BIT) << 16;
} else {
config &= INTEL_IGC_IVAR_MSI_CLEAR_TX0_TX2;
config |= (msix | INTEL_IGC_IVAR_INT_VALID_BIT) << 8;
config &= INTEL_IGC_IVAR_MSI_CLEAR_RX0_RX2;
config |= (msix | INTEL_IGC_IVAR_INT_VALID_BIT);
}
sys_write32(config, data->base + (INTEL_IGC_IVAR_BASE_ADDR + (reg_idx << 2)));
}
}
/**
* @brief Helper function to write MAC address to RAL and RAH registers.
*/
static void eth_intel_igc_set_mac_addr(mm_reg_t base, int index, const uint8_t *mac_addr,
uint32_t rah_config)
{
uint32_t mac_addr_hi = (mac_addr[5] << 8) | mac_addr[4] | rah_config;
uint32_t mac_addr_lo =
(mac_addr[3] << 24) | (mac_addr[2] << 16) | (mac_addr[1] << 8) | mac_addr[0];
sys_write32(mac_addr_hi, base + INTEL_IGC_RAH(index));
igc_reg_refresh(base);
sys_write32(mac_addr_lo, base + INTEL_IGC_RAL(index));
igc_reg_refresh(base);
}
/**
* @brief This function configures the MAC address filtering for the IGC, allowing it
* to filter packets based on the MAC address and filter mode.
*/
static void eth_intel_igc_set_mac_filter(const struct device *dev,
enum eth_igc_mac_filter_mode mode, const uint8_t *mac_addr,
int index, uint8_t queue)
{
struct eth_intel_igc_mac_data *data = dev->data;
uint32_t config = 0;
/* Queue number Settings */
config &= ~RAH_QSEL_MASK;
config |= ((queue << RAH_QSEL_SHIFT) | RAH_QSEL_ENABLE);
if (mode == SRC_ADDR) {
config |= (config & ~RAH_ASEL_MASK) | RAH_ASEL_SRC_ADDR;
}
config |= INTEL_IGC_RAH_AV;
eth_intel_igc_set_mac_addr(data->base, index, mac_addr, config);
}
static void eth_intel_igc_phylink_cb(const struct device *phy, struct phy_link_state *state,
void *user_data)
{
struct eth_intel_igc_mac_data *data = (struct eth_intel_igc_mac_data *)user_data;
if (state->is_up) {
net_eth_carrier_on(data->iface);
} else {
net_eth_carrier_off(data->iface);
}
}
static void eth_intel_igc_intr_enable(const struct device *dev)
{
const struct eth_intel_igc_mac_cfg *cfg = dev->config;
struct eth_intel_igc_mac_data *data = dev->data;
uint32_t config = 0;
/* Clear pending interrupt */
sys_read32(data->base + INTEL_IGC_ICR);
/* Prepare bit mask of Queue interrupt */
for (uint8_t msix = 0; msix < cfg->num_queues; msix++) {
config |= BIT(msix);
}
sys_write32(config, data->base + INTEL_IGC_EIAC);
igc_modify(data->base, INTEL_IGC_EIAM, config, true);
sys_write32(config, data->base + INTEL_IGC_EIMS);
igc_reg_refresh(data->base);
}
static void eth_intel_igc_iface_init(struct net_if *iface)
{
const struct device *dev = net_if_get_device(iface);
const struct eth_intel_igc_mac_cfg *cfg = dev->config;
struct eth_intel_igc_mac_data *data = dev->data;
/* Set interface */
data->iface = iface;
/* Initialize ethernet L2 */
ethernet_init(iface);
/* Set MAC address */
if (net_if_set_link_addr(data->iface, data->mac_addr, sizeof(data->mac_addr),
NET_LINK_ETHERNET) < 0) {
LOG_ERR("Failed to set mac address");
return;
}
eth_intel_igc_set_mac_filter(dev, DEST_ADDR, data->mac_addr, 0, 0);
if (device_is_ready(cfg->phy)) {
phy_link_callback_set(cfg->phy, eth_intel_igc_phylink_cb, (void *)data);
} else {
LOG_ERR("PHY device is not ready");
return;
}
eth_intel_igc_intr_enable(dev);
}
static enum ethernet_hw_caps eth_intel_igc_get_caps(const struct device *dev)
{
ARG_UNUSED(dev);
return ETHERNET_LINK_10BASE | ETHERNET_LINK_100BASE | ETHERNET_LINK_1000BASE;
}
static int eth_intel_igc_set_config(const struct device *dev, enum ethernet_config_type type,
const struct ethernet_config *eth_cfg)
{
struct eth_intel_igc_mac_data *data = dev->data;
if (type == ETHERNET_CONFIG_TYPE_MAC_ADDRESS) {
memcpy(data->mac_addr, eth_cfg->mac_address.addr, sizeof(eth_cfg->mac_address));
net_if_set_link_addr(data->iface, data->mac_addr, sizeof(data->mac_addr),
NET_LINK_ETHERNET);
eth_intel_igc_set_mac_filter(dev, DEST_ADDR, data->mac_addr, 0, 0);
return 0;
}
return -ENOTSUP;
}
static const struct device *eth_intel_igc_get_phy(const struct device *dev)
{
const struct eth_intel_igc_mac_cfg *cfg = dev->config;
return cfg->phy;
}
#if defined(CONFIG_NET_STATISTICS_ETHERNET)
static struct net_stats_eth *eth_intel_igc_get_stats(const struct device *dev)
{
struct eth_intel_igc_mac_data *data = dev->data;
const struct eth_intel_igc_mac_cfg *cfg = dev->config;
struct net_stats_eth *stats = &data->stats;
stats->bytes.sent += sys_read32(data->base + INTEL_IGC_TOTL);
stats->bytes.received += sys_read32(data->base + INTEL_IGC_TORL);
stats->pkts.tx += sys_read32(data->base + INTEL_IGC_TPT);
stats->pkts.rx += sys_read32(data->base + INTEL_IGC_TPR);
stats->broadcast.tx += sys_read32(data->base + INTEL_IGC_BPTC);
stats->broadcast.rx += sys_read32(data->base + INTEL_IGC_BPRC);
stats->multicast.tx += sys_read32(data->base + INTEL_IGC_MPTC);
stats->multicast.rx += sys_read32(data->base + INTEL_IGC_MPRC);
stats->errors.rx += sys_read32(data->base + INTEL_IGC_RERC);
stats->error_details.rx_length_errors += sys_read32(data->base + INTEL_IGC_RLEC);
stats->error_details.rx_crc_errors += sys_read32(data->base + INTEL_IGC_CRCERRS);
stats->error_details.rx_frame_errors += sys_read32(data->base + INTEL_IGC_RJC);
stats->error_details.rx_no_buffer_count += sys_read32(data->base + INTEL_IGC_RNBC);
stats->error_details.rx_long_length_errors += sys_read32(data->base + INTEL_IGC_ROC);
stats->error_details.rx_short_length_errors += sys_read32(data->base + INTEL_IGC_RUC);
stats->error_details.rx_align_errors += sys_read32(data->base + INTEL_IGC_ALGNERRC);
stats->error_details.rx_buf_alloc_failed += sys_read32(data->base + INTEL_IGC_MPC);
stats->error_details.tx_aborted_errors += sys_read32(data->base + INTEL_IGC_DC);
stats->flow_control.rx_flow_control_xon += sys_read32(data->base + INTEL_IGC_XONRXC);
stats->flow_control.rx_flow_control_xoff += sys_read32(data->base + INTEL_IGC_XOFFRXC);
stats->flow_control.tx_flow_control_xon += sys_read32(data->base + INTEL_IGC_XONTXC);
stats->flow_control.tx_flow_control_xoff += sys_read32(data->base + INTEL_IGC_XOFFTXC);
stats->collisions += sys_read32(data->base + INTEL_IGC_COLC);
for (uint8_t queue = 0; queue < cfg->num_queues; queue++) {
stats->tx_dropped += sys_read32(data->base + INTEL_IGC_TQDPC(queue));
}
return &data->stats;
}
#endif /* CONFIG_NET_STATISTICS_ETHERNET */
/**
* @brief This function releases completed transmit descriptors, cleans up the associated
* net_buf and net_pkt, and frees any allocated resources.
*/
static void eth_intel_igc_tx_clean(struct eth_intel_igc_mac_data *data, uint8_t queue)
{
const struct eth_intel_igc_mac_cfg *cfg = data->mac->config;
uint32_t idx = 0, clean_count;
union dma_tx_desc *desc;
clean_count = eth_intel_igc_completed_txdesc_num(data->mac, queue);
for (uint8_t count = 0; count < clean_count; count++) {
desc = eth_intel_igc_release_tx_desc(data->mac, queue);
if (desc == NULL) {
LOG_ERR("No more transmit descriptor available to release");
continue;
}
idx = get_tx_desc_idx(data->mac, desc, queue);
net_pkt_frag_unref(*(data->tx.frag + queue * cfg->num_tx_desc + idx));
net_pkt_unref(*(data->tx.pkt + queue * cfg->num_tx_desc + idx));
}
}
/**
* @brief This function retrieves the next available transmit descriptor from the ring,
* sets up the descriptor with the fragment data, and updates the write pointer.
*/
static int eth_intel_igc_tx_frag(const struct device *dev, struct net_pkt *pkt,
struct net_buf *frag, uint8_t queue)
{
const struct eth_intel_igc_mac_cfg *cfg = dev->config;
struct eth_intel_igc_mac_data *data = dev->data;
uint16_t total_len = net_pkt_get_len(pkt);
union dma_tx_desc *desc;
uint32_t idx = 0;
desc = eth_intel_igc_get_tx_desc(dev, queue);
if (desc == NULL) {
LOG_ERR("No more transmit descriptors available");
return -ENOMEM;
}
idx = get_tx_desc_idx(dev, desc, queue);
/* Store the pkt and header frag, then clear it during transmit clean. */
*(data->tx.frag + queue * cfg->num_tx_desc + idx) = frag->frags ? 0 : pkt->frags;
*(data->tx.pkt + queue * cfg->num_tx_desc + idx) = frag->frags ? 0 : pkt;
desc->read.data_buf_addr = (uint64_t)sys_cpu_to_le64((uint64_t)frag->data);
/* Copy the total payload length */
desc->read.payloadlen = total_len;
/* Copy the particular frag's buffer length */
desc->read.data_len = frag->len;
desc->read.desc_type = INTEL_IGC_TX_DESC_TYPE;
desc->read.ifcs = true;
desc->read.dext = true;
/* DMA engine processes the entire packet as a single unit */
if (frag->frags == NULL) {
desc->read.eop = true;
desc->read.rs = true;
sys_write32(data->tx.ring_wr_ptr[queue], data->base + INTEL_IGC_TDT(queue));
}
return 0;
}
/**
* @brief This function handles the network packet transmission by processing each
* fragmented frames and sending it through appropriate queue.
*/
static int eth_intel_igc_tx_data(const struct device *dev, struct net_pkt *pkt)
{
const struct eth_intel_igc_mac_cfg *cfg = dev->config;
struct eth_intel_igc_mac_data *data = dev->data;
uint8_t queue = 0;
int ret = 0;
if (!net_if_is_up(data->iface)) {
LOG_ERR("Ethernet interface is down");
return -ENETDOWN;
}
/* Hold the packet until transmit clean done. */
net_pkt_ref(pkt);
queue = net_tx_priority2tc(net_pkt_priority(pkt));
if (queue >= cfg->num_queues) {
queue = cfg->num_queues - 1;
}
for (struct net_buf *frag = pkt->frags; frag; frag = frag->frags) {
/* Hold the Header fragment until transmit clean done */
if (frag == pkt->frags) {
net_pkt_frag_ref(frag);
}
ret = eth_intel_igc_tx_frag(dev, pkt, frag, queue);
if (ret < 0) {
LOG_ERR("Failed to transmit in queue number: %d", queue);
}
}
return ret;
}
/**
* @brief Identify the address family of received packets as per header type.
*/
static sa_family_t eth_intel_igc_get_sa_family(uint8_t *rx_buf)
{
struct net_eth_hdr *eth_hdr = (struct net_eth_hdr *)rx_buf;
switch (ntohs(eth_hdr->type)) {
case NET_ETH_PTYPE_IP:
return AF_INET;
case NET_ETH_PTYPE_IPV6:
return AF_INET6;
default:
break;
}
return AF_UNSPEC;
}
/**
* @brief This function updates the tail pointer of the RX descriptor ring, retrieves the
* next available RX descriptor, and prepare it for receiving incoming packets by setting
* the packet buffer address.
*/
static void eth_intel_igc_rx_data_hdl(struct eth_intel_igc_mac_data *data, uint8_t queue,
uint32_t idx, union dma_rx_desc *desc)
{
const struct eth_intel_igc_mac_cfg *cfg = data->mac->config;
sys_write32(idx, data->base + INTEL_IGC_RDT(queue));
desc = eth_intel_igc_get_rx_desc(data->mac, queue);
if (desc == NULL) {
LOG_ERR("No more rx descriptor available");
return;
}
/* Find descriptor position and prepare it for next DMA cycle */
idx = get_rx_desc_idx(data->mac, desc, queue);
desc->read.pkt_buf_addr = (uint64_t)sys_cpu_to_le64(
(uint64_t)(data->rx.buf + (queue * cfg->num_rx_desc + idx) * ETH_MAX_FRAME_SZ));
desc->writeback.dd = 0;
}
static void eth_intel_igc_rx_data_hdl_err(struct eth_intel_igc_mac_data *data, uint8_t queue,
uint32_t idx, union dma_rx_desc *desc,
struct net_pkt *pkt)
{
net_pkt_unref(pkt);
eth_intel_igc_rx_data_hdl(data, queue, idx, desc);
}
/**
* @brief This function retrieves completed receive descriptors, allocates net_pkt
* buffers, copies the received data into the buffers, and submits the packets to
* the network stack.
*/
static void eth_intel_igc_rx_data(struct eth_intel_igc_mac_data *data, uint8_t queue)
{
const struct eth_intel_igc_mac_cfg *cfg = data->mac->config;
uint32_t count, idx = 0, completed_count = 0;
uint8_t *rx_buf;
union dma_rx_desc *desc;
struct net_pkt *pkt;
int ret;
completed_count = eth_intel_igc_completed_rxdesc_num(data->mac, queue);
for (count = 0; count < completed_count; count++) {
/* Retrieve the position of next descriptor to be processed */
idx = data->rx.ring_rd_ptr[queue];
desc = eth_intel_igc_release_rx_desc(data->mac, queue);
if (desc == NULL) {
LOG_ERR("RX descriptor is NULL");
continue;
}
if (!net_if_is_up(data->iface) || !desc->writeback.pkt_len) {
LOG_ERR("RX interface is down or pkt_len is %d", desc->writeback.pkt_len);
eth_intel_igc_rx_data_hdl_err(data, queue, idx, desc, NULL);
continue;
}
/* Get the DMA buffer pointer as per index */
rx_buf = data->rx.buf + ((queue * cfg->num_rx_desc + idx) * ETH_MAX_FRAME_SZ);
/* Allocate packet buffer as per address family */
pkt = net_pkt_rx_alloc_with_buffer(data->iface, desc->writeback.pkt_len,
eth_intel_igc_get_sa_family(rx_buf), 0,
K_MSEC(200));
if (pkt == NULL) {
LOG_ERR("Failed to allocate Receive buffer");
eth_intel_igc_rx_data_hdl_err(data, queue, idx, desc, NULL);
continue;
}
/* Write DMA buffer to packet */
ret = net_pkt_write(pkt, (void *)rx_buf, desc->writeback.pkt_len);
if (ret < 0) {
LOG_ERR("Failed to write Receive buffer to packet");
eth_intel_igc_rx_data_hdl_err(data, queue, idx, desc, pkt);
continue;
}
/* Process received packet */
ret = net_recv_data(data->iface, pkt);
if (ret < 0) {
LOG_ERR("Failed to enqueue the Receive packet: %d", queue);
eth_intel_igc_rx_data_hdl_err(data, queue, idx, desc, pkt);
continue;
}
eth_intel_igc_rx_data_hdl(data, queue, idx, desc);
}
}
/**
* @brief Configure and Enable the Transmit Control Register.
*/
static void eth_intel_igc_tctl_setup(mm_reg_t tctl)
{
uint32_t config = sys_read32(tctl);
config &= ~INTEL_IGC_TCTL_CT;
config |= INTEL_IGC_TCTL_EN | INTEL_IGC_TCTL_PSP | INTEL_IGC_TCTL_RTLC |
INTEL_IGC_COLLISION_THRESHOLD << INTEL_IGC_TCTL_CT_SHIFT;
sys_write32(config, tctl);
}
/**
* @brief IGC expects the DMA descriptors to be aligned to 128-byte boundaries for efficient
* access and to avoid potential data corruption or performance issues.
*/
static void *eth_intel_igc_aligned_alloc(uint32_t size)
{
void *desc_base = k_aligned_alloc(4096, size);
if (desc_base == NULL) {
return NULL;
}
memset(desc_base, 0, size);
return desc_base;
}
/**
* @brief This function initializes the transmit DMA descriptor ring for all queues.
*/
static void eth_intel_igc_init_tx_desc_ring(const struct eth_intel_igc_mac_cfg *cfg,
struct eth_intel_igc_mac_data *data)
{
uint64_t desc_addr;
uint32_t config;
for (uint8_t queue = 0; queue < cfg->num_queues; queue++) {
k_sem_init(&data->tx.sem[queue], 1, 1);
/* Disable the transmit descriptor ring */
sys_write32(0, data->base + INTEL_IGC_TXDCTL(queue));
igc_reg_refresh(data->base);
/* Program the transmit descriptor ring total length */
sys_write32(cfg->num_tx_desc * sizeof(union dma_tx_desc),
data->base + INTEL_IGC_TDLEN(queue));
/* Program the descriptor base address */
desc_addr = (uint64_t)(data->tx.desc + (queue * cfg->num_tx_desc));
sys_write32((uint32_t)(desc_addr >> 32), data->base + INTEL_IGC_TDBAH(queue));
sys_write32((uint32_t)desc_addr, data->base + INTEL_IGC_TDBAL(queue));
/* Reset Head and Tail Descriptor Pointers */
sys_write32(0, data->base + INTEL_IGC_TDH(queue));
sys_write32(0, data->base + INTEL_IGC_TDT(queue));
/* Configure TX DMA interrupt trigger thresholds */
config = INTEL_IGC_TX_PTHRESH_VAL << INTEL_IGC_TX_PTHRESH_SHIFT |
INTEL_IGC_TX_HTHRESH_VAL << INTEL_IGC_TX_HTHRESH_SHIFT |
INTEL_IGC_TX_WTHRESH_VAL << INTEL_IGC_TX_WTHRESH_SHIFT;
/* Enable the transmit descriptor ring */
config |= INTEL_IGC_TXDCTL_QUEUE_ENABLE;
sys_write32(config, data->base + INTEL_IGC_TXDCTL(queue));
}
}
/**
* @brief This function initializes the transmit DMA descriptor ring.
* It sets up the descriptor base addresses, lengths, and control registers.
*/
static int eth_intel_igc_tx_dma_init(const struct device *dev)
{
const struct eth_intel_igc_mac_cfg *cfg = dev->config;
struct eth_intel_igc_mac_data *data = dev->data;
uint32_t size;
/* Calculate the total size of the TX descriptor buffer */
size = cfg->num_queues * cfg->num_tx_desc * sizeof(union dma_tx_desc);
/* Allocate memory for the TX descriptor buffer */
size = ROUND_UP(size, sizeof(union dma_tx_desc));
data->tx.desc = (union dma_tx_desc *)eth_intel_igc_aligned_alloc(size);
if (data->tx.desc == NULL) {
LOG_ERR("Transmit descriptor buffer alloc failed");
return -ENOBUFS;
}
eth_intel_igc_init_tx_desc_ring(cfg, data);
/* Configure internal transmit descriptor buffer size */
sys_write32(INTEL_IGC_TXPBS_TXPBSIZE_DEFAULT, data->base + INTEL_IGC_TXPBS);
eth_intel_igc_tctl_setup(data->base + INTEL_IGC_TCTL);
return 0;
}
/**
* @brief Configure and Enable the Receive Control Register.
*/
static void eth_intel_igc_rctl_setup(mm_reg_t rctl)
{
uint32_t config = sys_read32(rctl);
/* Multicast / Unicast Table Offset */
config &= ~(0x3 << INTEL_IGC_RCTL_MO_SHIFT);
/* Do not store bad packets */
config &= ~INTEL_IGC_RCTL_SBP;
/* Turn off VLAN filters */
config &= ~INTEL_IGC_RCTL_VFE;
config |= INTEL_IGC_RCTL_EN | INTEL_IGC_RCTL_BAM |
/* Strip the CRC */
INTEL_IGC_RCTL_SECRC | INTEL_IGC_RCTL_SZ_2048;
sys_write32(config, rctl);
}
/**
* @brief This function ensures that each Receive DMA descriptor is populated with
* a pre-allocated packet buffer, enabling the device to receive and store incoming
* packets efficiently.
*/
static int eth_intel_igc_rx_desc_prepare(const struct device *dev)
{
const struct eth_intel_igc_mac_cfg *cfg = dev->config;
struct eth_intel_igc_mac_data *data = dev->data;
union dma_rx_desc *desc;
uint8_t *buf;
/* Allocate memory for receive DMA buffer */
data->rx.buf = (uint8_t *)k_calloc(cfg->num_queues * cfg->num_rx_desc, ETH_MAX_FRAME_SZ);
if (data->rx.buf == NULL) {
LOG_ERR("Receive DMA buffer alloc failed");
return -ENOBUFS;
}
/* Assign allocated memory to each descriptor as per it's index */
for (uint8_t queue = 0; queue < cfg->num_queues; queue++) {
desc = &data->rx.desc[queue * cfg->num_rx_desc];
for (uint32_t desc_idx = 0; desc_idx < cfg->num_rx_desc; desc_idx++) {
/* Set the pkt buffer address */
buf = data->rx.buf +
((queue * cfg->num_rx_desc + desc_idx) * ETH_MAX_FRAME_SZ);
desc[desc_idx].read.pkt_buf_addr = (uint64_t)sys_cpu_to_le64((uint64_t)buf);
desc[desc_idx].read.hdr_buf_addr = (uint64_t)&desc[desc_idx];
}
/* Update tail pointer in hardware and copy the same for driver reference */
sys_write32(cfg->num_rx_desc - 1, data->base + INTEL_IGC_RDT(queue));
data->rx.ring_wr_ptr[queue] = cfg->num_rx_desc - 1;
}
return 0;
}
/**
* @brief This function initializes the receive DMA descriptor ring for all queues.
*/
static void eth_intel_igc_init_rx_desc_ring(const struct eth_intel_igc_mac_cfg *cfg,
struct eth_intel_igc_mac_data *data)
{
uint64_t desc_addr;
uint32_t config;
for (uint8_t queue = 0; queue < cfg->num_queues; queue++) {
k_sem_init(&data->rx.sem[queue], 1, 1);
/* Disable the receive descriptor ring */
sys_write32(0, data->base + INTEL_IGC_RXDCTL(queue));
igc_reg_refresh(data->base);
/* Program the receive descriptor ring total length */
sys_write32(cfg->num_rx_desc * sizeof(union dma_rx_desc),
data->base + INTEL_IGC_RDLEN(queue));
/* Program the descriptor base address */
desc_addr = (uint64_t)(data->rx.desc + (queue * cfg->num_rx_desc));
sys_write32((uint32_t)(desc_addr >> 32), data->base + INTEL_IGC_RDBAH(queue));
sys_write32((uint32_t)desc_addr, data->base + INTEL_IGC_RDBAL(queue));
/* Configure the receive descriptor control */
config = INTEL_IGC_SRRCTL_BSIZEPKT(ETH_MAX_FRAME_SZ);
config |= INTEL_IGC_SRRCTL_BSIZEHDR(INTEL_IGC_RXBUFFER_256);
config |= INTEL_IGC_SRRCTL_DESCTYPE_ADV_ONEBUF;
config |= INTEL_IGC_SRRCTL_DROP_EN;
sys_write32(config, data->base + INTEL_IGC_SRRCTL(queue));
/* Reset Head and Tail Descriptor Pointers */
sys_write32(0, data->base + INTEL_IGC_RDH(queue));
sys_write32(0, data->base + INTEL_IGC_RDT(queue));
config = sys_read32(data->base + INTEL_IGC_RXDCTL(queue));
config &= INTEL_IGC_RX_THRESH_RESET;
/* Configure RX DMA interrupt trigger thresholds */
config |= INTEL_IGC_RX_PTHRESH_VAL << INTEL_IGC_RX_PTHRESH_SHIFT |
INTEL_IGC_RX_HTHRESH_VAL << INTEL_IGC_RX_HTHRESH_SHIFT |
INTEL_IGC_RX_WTHRESH_VAL << INTEL_IGC_RX_WTHRESH_SHIFT;
/* Enable the receive descriptor ring */
config |= INTEL_IGC_RXDCTL_QUEUE_ENABLE;
sys_write32(config, data->base + INTEL_IGC_RXDCTL(queue));
igc_reg_refresh(data->base);
}
}
/**
* @brief This function initializes the receive descriptor ring.
* It sets up the descriptor base address, length, and control registers.
*/
static int eth_intel_igc_rx_dma_init(const struct device *dev)
{
const struct eth_intel_igc_mac_cfg *cfg = dev->config;
struct eth_intel_igc_mac_data *data = dev->data;
uint32_t size;
int ret;
/*
* TODO: enable RSS mapping.
* TODO: enable interrupt moderation here.
* TODO: enable Checksum offload here.
* TODO: enable VLAN offload here.
*/
/* Disable receive logic until descriptor setup */
igc_modify(data->base, INTEL_IGC_RCTL, INTEL_IGC_RCTL_EN, false);
sys_write32(0, data->base + INTEL_IGC_RXCSUM);
/* Calculate the total size of the RX descriptor buffer */
size = cfg->num_queues * cfg->num_rx_desc * sizeof(union dma_rx_desc);
size = ROUND_UP(size, sizeof(union dma_rx_desc));
/* Allocate memory for the RX descriptor buffer */
data->rx.desc = (union dma_rx_desc *)eth_intel_igc_aligned_alloc(size);
if (data->rx.desc == NULL) {
LOG_ERR("Receive descriptor buffer alloc failed");
return -ENOBUFS;
}
eth_intel_igc_init_rx_desc_ring(cfg, data);
/* Configure internal receive descriptor buffer size */
sys_write32(INTEL_IGC_RXPBS_RXPBSIZE_DEFAULT, data->base + INTEL_IGC_RXPBS);
ret = eth_intel_igc_rx_desc_prepare(dev);
if (ret < 0) {
LOG_ERR("Receive descriptor prepare failed");
return ret;
}
eth_intel_igc_rctl_setup(data->base + INTEL_IGC_RCTL);
return ret;
}
/**
* @brief This function validates the MAC address and returns true on success.
*/
static bool eth_intel_igc_is_valid_mac_addr(uint8_t *mac_addr)
{
if (UNALIGNED_GET((uint32_t *)mac_addr) == INTEL_IGC_DEF_MAC_ADDR) {
return false;
}
if (UNALIGNED_GET((uint32_t *)(mac_addr + 0)) == 0x00000000 &&
UNALIGNED_GET((uint16_t *)(mac_addr + 4)) == 0x0000) {
LOG_DBG("Invalid Mac Address");
return false;
}
if (mac_addr[0] & 0x01) {
LOG_DBG("Multicast MAC address");
return false;
}
return true;
}
/* @brief When the device is configured to use MAC address from EEPROM, i226 firmware
* will populate both RAL and RAH with the user provided MAC address.
*/
static void eth_intel_igc_get_preloaded_mac_addr(mm_reg_t base, uint8_t *mac_addr)
{
uint32_t mac_addr_hi, mac_addr_lo;
mac_addr_lo = sys_read32(base + INTEL_IGC_RAL(0));
mac_addr_hi = sys_read32(base + INTEL_IGC_RAH(0));
mac_addr[0] = (uint8_t)(mac_addr_lo & 0xFF);
mac_addr[1] = (uint8_t)((mac_addr_lo >> 8) & 0xFF);
mac_addr[2] = (uint8_t)((mac_addr_lo >> 16) & 0xFF);
mac_addr[3] = (uint8_t)((mac_addr_lo >> 24) & 0xFF);
mac_addr[4] = (uint8_t)(mac_addr_hi & 0xFF);
mac_addr[5] = (uint8_t)((mac_addr_hi >> 8) & 0xFF);
}
static void eth_intel_igc_get_mac_addr(const struct device *dev)
{
const struct eth_intel_igc_mac_cfg *cfg = dev->config;
struct eth_intel_igc_mac_data *data = dev->data;
if (cfg->random_mac_address) {
LOG_INF("Assign Random MAC address");
gen_random_mac(data->mac_addr, 0, 0xA0, 0xC9);
return;
}
if (eth_intel_igc_is_valid_mac_addr(data->mac_addr)) {
LOG_INF("Assign MAC address from Device Tree");
return;
}
eth_intel_igc_get_preloaded_mac_addr(data->base, data->mac_addr);
if (eth_intel_igc_is_valid_mac_addr(data->mac_addr)) {
LOG_INF("Assign MAC address from EEPROM");
return;
}
}
static void eth_intel_igc_rx_addrs_init(const struct device *dev)
{
struct eth_intel_igc_mac_data *data = dev->data;
uint8_t reset_addr[NET_ETH_ADDR_LEN] = {0};
uint16_t rar_count = 128;
eth_intel_igc_get_mac_addr(dev);
LOG_INF("IGC MAC addr %02X:%02X:%02X:%02X:%02X:%02X", data->mac_addr[0], data->mac_addr[1],
data->mac_addr[2], data->mac_addr[3], data->mac_addr[4], data->mac_addr[5]);
/* Program valid MAC address in index 0 */
eth_intel_igc_set_mac_addr(data->base, 0, data->mac_addr, INTEL_IGC_RAH_AV);
/* Program reset_addr to unused rar index */
for (uint8_t rar = 1; rar < rar_count; rar++) {
eth_intel_igc_set_mac_addr(data->base, rar, reset_addr, INTEL_IGC_RAH_AV);
}
}
/**
* @brief This function disables the PCIe master access to the device, ensuring that
* the device is ready to be controlled by the driver.
*/
static int eth_intel_igc_disable_pcie_master(mm_reg_t base)
{
uint32_t timeout = INTEL_IGC_GIO_MASTER_DISABLE_TIMEOUT;
mm_reg_t igc_stat = base + INTEL_IGC_STATUS;
igc_modify(base, INTEL_IGC_CTRL, INTEL_IGC_CTRL_GIO_MASTER_DISABLE, true);
/* Wait for the INTEL_IGC_STATUS_GIO_MASTER_ENABLE bit to clear */
if (WAIT_FOR((sys_read32(igc_stat) & INTEL_IGC_STATUS_GIO_MASTER_ENABLE) == 0, timeout,
k_msleep(1))) {
return 0;
}
LOG_ERR("Timeout waiting for GIO Master Request to complete");
return -ETIMEDOUT;
}
static void eth_intel_igc_init_speed(struct eth_intel_igc_mac_data *data)
{
mm_reg_t base = data->base;
igc_modify(base, INTEL_IGC_CTRL, INTEL_IGC_CTRL_FRCSPD | INTEL_IGC_CTRL_FRCDPX, false);
igc_modify(base, INTEL_IGC_CTRL, INTEL_IGC_CTRL_SLU, true);
}
static void eth_intel_igc_get_dev_ownership(struct eth_intel_igc_mac_data *data)
{
igc_modify(data->base, INTEL_IGC_CTRL_EXT, INTEL_IGC_CTRL_EXT_DRV_LOAD, true);
}
static int eth_intel_igc_init_mac_hw(const struct device *dev)
{
struct eth_intel_igc_mac_data *data = dev->data;
int ret = 0;
ret = eth_intel_igc_disable_pcie_master(data->base);
if (ret < 0) {
return ret;
}
sys_write32(UINT32_MAX, data->base + INTEL_IGC_IMC);
sys_write32(0, data->base + INTEL_IGC_RCTL);
sys_write32(INTEL_IGC_TCTL_PSP, data->base + INTEL_IGC_TCTL);
igc_reg_refresh(data->base);
/* MAC Reset */
igc_modify(data->base, INTEL_IGC_CTRL, INTEL_IGC_CTRL_DEV_RST, true);
k_msleep(INTEL_IGC_RESET_DELAY);
/* MAC receive address Init */
eth_intel_igc_rx_addrs_init(dev);
eth_intel_igc_get_dev_ownership(data);
eth_intel_igc_map_intr_to_vector(dev);
eth_intel_igc_init_speed(data);
return ret;
}
static int eth_intel_igc_init(const struct device *dev)
{
const struct eth_intel_igc_mac_cfg *cfg = dev->config;
struct eth_intel_igc_mac_data *data = dev->data;
int ret = 0;
data->mac = dev;
data->base = DEVICE_MMIO_GET(cfg->platform);
if (!data->base) {
LOG_ERR("Failed to get MMIO base address");
return -ENODEV;
}
ret = eth_intel_igc_init_mac_hw(dev);
if (ret < 0) {
return ret;
}
ret = eth_intel_igc_pcie_msix_setup(dev);
if (ret < 0) {
return ret;
}
ret = eth_intel_igc_tx_dma_init(dev);
if (ret < 0) {
return ret;
}
ret = eth_intel_igc_rx_dma_init(dev);
if (ret < 0) {
return ret;
}
cfg->config_func(dev);
return 0;
}
static const struct ethernet_api eth_api = {
.iface_api.init = eth_intel_igc_iface_init,
.get_capabilities = eth_intel_igc_get_caps,
.set_config = eth_intel_igc_set_config,
.send = eth_intel_igc_tx_data,
.get_phy = eth_intel_igc_get_phy,
#if defined(CONFIG_NET_STATISTICS_ETHERNET)
.get_stats = eth_intel_igc_get_stats,
#endif
};
#define NUM_QUEUES(n) DT_INST_PROP(n, num_queues)
#define NUM_TX_DESC(n) DT_INST_PROP(n, num_tx_desc)
#define NUM_RX_DESC(n) DT_INST_PROP(n, num_rx_desc)
#define NUM_MSIX(n) NUM_QUEUES(n) + ETH_IGC_NUM_MISC
/**
* @brief Generate TX and RX interrupt handling functions as per queue.
*/
#define INTEL_IGC_SETUP_QUEUE_WORK_EXP(n, queue) \
static void eth_tx_irq_queue##n##_##queue(struct k_work *work) \
{ \
struct eth_intel_igc_mac_data *data = \
CONTAINER_OF(work, struct eth_intel_igc_mac_data, tx_work[queue]); \
eth_intel_igc_tx_clean(data, queue); \
} \
static void eth_rx_irq_queue##n##_##queue(struct k_work *work) \
{ \
struct k_work_delayable *dwork = k_work_delayable_from_work(work); \
struct eth_intel_igc_mac_data *data = \
CONTAINER_OF(dwork, struct eth_intel_igc_mac_data, rx_work[queue]); \
eth_intel_igc_rx_data(data, queue); \
}
#define INTEL_IGC_SETUP_QUEUE_WORK(n) \
INTEL_IGC_SETUP_QUEUE_WORK_EXP(n, 3) \
INTEL_IGC_SETUP_QUEUE_WORK_EXP(n, 2) \
INTEL_IGC_SETUP_QUEUE_WORK_EXP(n, 1) \
INTEL_IGC_SETUP_QUEUE_WORK_EXP(n, 0)
#define INTEL_IGC_INIT_QUEUE_WORK_EXP(n, queue) \
k_work_init(&data->tx_work[queue], eth_tx_irq_queue##n##_##queue); \
k_work_init_delayable(&data->rx_work[queue], eth_rx_irq_queue##n##_##queue);
/**
* @brief Initialize deferred work for each hardware queue.
*/
#define INTEL_IGC_MAC_CONFIG_IRQ(n) \
static void eth##n##_cfg_irq(const struct device *dev) \
{ \
struct eth_intel_igc_mac_data *data = dev->data; \
uint8_t queue = NUM_QUEUES(n); \
if (queue > 3) { \
INTEL_IGC_INIT_QUEUE_WORK_EXP(n, 3); \
} \
if (queue > 2) { \
INTEL_IGC_INIT_QUEUE_WORK_EXP(n, 2); \
} \
if (queue > 1) { \
INTEL_IGC_INIT_QUEUE_WORK_EXP(n, 1); \
} \
if (queue > 0) { \
INTEL_IGC_INIT_QUEUE_WORK_EXP(n, 0); \
} \
}
/**
* @brief Allocate various global objects required for managing tx and rx operations.
*/
#define INTEL_IGC_ALLOC_GLOBAL_OBJECTS(n) \
struct k_sem tx_ring_lock_##n[NUM_QUEUES(n)]; \
struct k_sem rx_ring_lock_##n[NUM_QUEUES(n)]; \
static unsigned int tx_ring_wr_ptr_##n[NUM_QUEUES(n)]; \
static unsigned int rx_ring_wr_ptr_##n[NUM_QUEUES(n)]; \
static unsigned int tx_ring_rd_ptr_##n[NUM_QUEUES(n)]; \
static unsigned int rx_ring_rd_ptr_##n[NUM_QUEUES(n)]; \
static struct net_buf tx_frag_##n[NUM_QUEUES(n)][NUM_TX_DESC(n)]; \
static struct net_pkt tx_pkt_##n[NUM_QUEUES(n)][NUM_TX_DESC(n)]; \
static struct eth_intel_igc_intr_info intr_info_##n[NUM_MSIX(n)]; \
static msi_vector_t msi_vec_##n[NUM_MSIX(n)];
#define INTEL_IGC_MAC_DATA(n) \
static struct eth_intel_igc_mac_data eth_data_##n = { \
.tx = \
{ \
.sem = tx_ring_lock_##n, \
.ring_wr_ptr = tx_ring_wr_ptr_##n, \
.ring_rd_ptr = tx_ring_rd_ptr_##n, \
.pkt = (struct net_pkt **)tx_pkt_##n, \
.frag = (struct net_buf **)tx_frag_##n, \
}, \
.rx = \
{ \
.sem = rx_ring_lock_##n, \
.ring_wr_ptr = rx_ring_wr_ptr_##n, \
.ring_rd_ptr = rx_ring_rd_ptr_##n, \
}, \
.intr_info = intr_info_##n, \
.msi_vec = msi_vec_##n, \
.mac = DEVICE_DT_GET(DT_DRV_INST(n)), \
.mac_addr = DT_INST_PROP_OR(n, local_mac_address, {0}), \
};
/**
* @brief Initializes the configuration structure of each driver instance.
*/
#define INTEL_IGC_MAC_CONFIG(n) \
static void eth##n##_cfg_irq(const struct device *dev); \
static const struct eth_intel_igc_mac_cfg eth_cfg_##n = { \
.platform = DEVICE_DT_GET(DT_INST_PARENT(n)), \
.phy = DEVICE_DT_GET(DT_INST_PHANDLE(n, phy_handle)), \
.random_mac_address = DT_INST_PROP(n, zephyr_random_mac_address), \
.config_func = eth##n##_cfg_irq, \
.num_queues = NUM_QUEUES(n), \
.num_msix = NUM_MSIX(n), \
.num_tx_desc = NUM_TX_DESC(n), \
.num_rx_desc = NUM_RX_DESC(n), \
};
#define INTEL_IGC_MAC_INIT(n) \
DEVICE_PCIE_INST_DECLARE(n); \
INTEL_IGC_MAC_CONFIG(n) \
INTEL_IGC_ALLOC_GLOBAL_OBJECTS(n) \
INTEL_IGC_MAC_DATA(n) \
INTEL_IGC_SETUP_QUEUE_WORK(n); \
ETH_NET_DEVICE_DT_INST_DEFINE(n, eth_intel_igc_init, NULL, &eth_data_##n, &eth_cfg_##n, \
CONFIG_ETH_INIT_PRIORITY, &eth_api, \
CONFIG_ETH_INTEL_IGC_NET_MTU); \
INTEL_IGC_MAC_CONFIG_IRQ(n)
DT_INST_FOREACH_STATUS_OKAY(INTEL_IGC_MAC_INIT)