* [PATCH net-next v5 1/6] net: xilinx: axienet: Introduce axienet_config for MAC-specific ops
2026-11-14 18:29 [PATCH net-next v5 0/6] net: xilinx: axienet: Add 10G/25G (XXV) ethernet support Suraj Gupta
@ 2026-11-14 18:29 ` Suraj Gupta
2026-11-14 18:29 ` [PATCH net-next v5 2/6] dt-bindings: net: xlnx,xxv-ethernet: Add Xilinx XXV 10G/25G Ethernet Suraj Gupta
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Suraj Gupta @ 2026-11-14 18:29 UTC (permalink / raw)
To: Radhey Shyam Pandey, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: Michal Simek, Russell King, netdev, devicetree, linux-arm-kernel,
linux-kernel
Introduce an axienet_config structure to abstract MAC-type-specific
operations, preparing the driver for supporting additional MAC types
beyond the existing 1G Ethernet.
Add axienet_config with capability flags (mdio, mac_irq, non_dmaengine,
uc_filter, mc_filter, vlan, jumbo, tx_csum, sw_padding, internal_pcs),
the register-dump count, the MAC option table and callbacks (clk_init,
probe_init, gt_reset, mac_init, mac_link_up, get_regs, phylink_set_caps,
pcs_ops, stats_update), and populate it from the OF match table. Convert
the common code to consult these capabilities and callbacks instead of
open-coding 1G behavior, so a new MAC type can be added without adding
MAC-specific conditionals to the shared paths.
The MDIO teardown in axienet_remove() is now guarded by a NULL check on
lp->mii_bus. This is required by the refactor, because MAC types that do
not provide an MDIO bus (such as the XXV MAC added later in this series)
leave lp->mii_bus NULL. It also hardens the existing 1G path: a failed
axienet_mdio_setup() already left lp->mii_bus NULL while probe only warns
and continues (e.g. a 1G device using a fixed-link), so unbinding such a
device would dereference NULL in mdiobus_unregister(). Apart from this
guard, no functional change is intended for the existing 1G path.
Signed-off-by: Suraj Gupta <suraj.gupta2@amd.com>
---
drivers/net/ethernet/xilinx/xilinx_axienet.h | 56 +++
.../net/ethernet/xilinx/xilinx_axienet_main.c | 387 ++++++++++++------
2 files changed, 324 insertions(+), 119 deletions(-)
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h
index c63d99686d0b..a87cfa079502 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet.h
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h
@@ -547,6 +547,7 @@ struct skbuf_dma_descriptor {
* @rx_ring_head: RX skb ring buffer head index.
* @rx_ring_tail: RX skb ring buffer tail index.
* @rx_submit_lock: Protects RX ring resubmission vs teardown in dmaengine path.
+ * @axienet_config: MAC-type specific configuration and operations.
*/
struct axienet_local {
struct net_device *ndev;
@@ -629,6 +630,61 @@ struct axienet_local {
int rx_ring_head;
int rx_ring_tail;
spinlock_t rx_submit_lock;
+ const struct axienet_config *axienet_config;
+};
+
+/**
+ * struct axienet_config - MAC-type specific configuration and operations
+ * @mdio: MAC has an MDIO bus for external PHY management
+ * @mac_irq: MAC has a dedicated core interrupt
+ * @non_dmaengine: MAC supports the non-dmaengine flow
+ * @uc_filter: MAC has unicast address filter registers
+ * @mc_filter: MAC has multicast address filter registers
+ * @vlan: MAC supports VLAN frames by default
+ * @jumbo: MAC supports jumbo frames
+ * @tx_csum: MAC supports TX checksum offload
+ * @sw_padding: Short frames must be padded in software before transmit
+ * @internal_pcs: MAC has an internal PCS that phylink should poll; when set,
+ * the PCS is registered in poll mode at probe
+ * @regs_n: Number of MAC registers exposed through ethtool -d
+ * @options: Table mapping the XAE_OPTION_* flags to the MAC register bits
+ * that enable them, terminated by a zero @opt entry
+ * @clk_init: Callback to get and enable the MAC-specific clocks
+ * @probe_init: Callback for MAC-specific probe-time init, or NULL if unused
+ * @gt_reset: Callback to reset the serial transceiver (GT), or NULL if unused
+ * @mac_init: Callback for MAC-specific bring-up after the DMA reset
+ * @mac_link_up: Callback to program link speed and pause on link up, or NULL
+ * @get_regs: Callback to dump the MAC registers for ethtool -d
+ * @phylink_set_caps: Callback to set phylink MAC capabilities
+ * @pcs_ops: phylink PCS operations for this MAC; required when @internal_pcs
+ * is set, as the PCS is registered without a further NULL check
+ * @stats_update: Callback to latch/accumulate the periodic MAC counters, or
+ * NULL if the MAC has no periodic statistics counters
+ */
+struct axienet_config {
+ bool mdio;
+ bool mac_irq;
+ bool non_dmaengine;
+ bool uc_filter;
+ bool mc_filter;
+ bool vlan;
+ bool jumbo;
+ bool tx_csum;
+ bool sw_padding;
+ bool internal_pcs;
+ unsigned int regs_n;
+ const struct axienet_option *options;
+ int (*clk_init)(struct axienet_local *lp);
+ void (*probe_init)(struct axienet_local *lp);
+ void (*gt_reset)(struct axienet_local *lp);
+ int (*mac_init)(struct net_device *ndev);
+ void (*mac_link_up)(struct net_device *ndev, int speed,
+ bool tx_pause, bool rx_pause);
+ void (*get_regs)(struct axienet_local *lp, u32 *data);
+ void (*phylink_set_caps)(struct axienet_local *lp,
+ struct phylink_config *config);
+ const struct phylink_pcs_ops *pcs_ops;
+ void (*stats_update)(struct axienet_local *lp);
};
/**
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
index 5b4998e2ca14..83f35effbf37 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
@@ -66,18 +66,8 @@
static void axienet_rx_submit_desc(struct net_device *ndev);
-/* Match table for of_platform binding */
-static const struct of_device_id axienet_of_match[] = {
- { .compatible = "xlnx,axi-ethernet-1.00.a", },
- { .compatible = "xlnx,axi-ethernet-1.01.a", },
- { .compatible = "xlnx,axi-ethernet-2.01.a", },
- {},
-};
-
-MODULE_DEVICE_TABLE(of, axienet_of_match);
-
/* Option table for setting up Axi Ethernet hardware options */
-static struct axienet_option axienet_options[] = {
+static const struct axienet_option axienet_options[] = {
/* Turn on jumbo packet support for both Rx and Tx */
{
.opt = XAE_OPTION_JUMBO,
@@ -470,6 +460,9 @@ static void axienet_set_mac_address(struct net_device *ndev,
if (!is_valid_ether_addr(ndev->dev_addr))
eth_hw_addr_random(ndev);
+ if (!lp->axienet_config->uc_filter)
+ return;
+
/* Set up unicast MAC address filter set its mac address */
axienet_iow(lp, XAE_UAW0_OFFSET,
(ndev->dev_addr[0]) |
@@ -519,6 +512,9 @@ static void axienet_set_multicast_list(struct net_device *ndev)
u32 reg, af0reg, af1reg;
struct axienet_local *lp = netdev_priv(ndev);
+ if (!lp->axienet_config->mc_filter)
+ return;
+
reg = axienet_ior(lp, XAE_FMI_OFFSET);
reg &= ~XAE_FMI_PM_MASK;
if (ndev->flags & IFF_PROMISC)
@@ -581,14 +577,14 @@ static void axienet_set_multicast_list(struct net_device *ndev)
* The Axi Ethernet core has multiple features which can be selectively turned
* on or off. The typical options could be jumbo frame option, basic VLAN
* option, promiscuous mode option etc. This function is used to set or clear
- * these options in the Axi Ethernet hardware. This is done through
- * axienet_option structure .
+ * these options in the Axi Ethernet hardware. This is done through the
+ * MAC-specific axienet_option table in &struct axienet_config.
*/
static void axienet_setoptions(struct net_device *ndev, u32 options)
{
int reg;
struct axienet_local *lp = netdev_priv(ndev);
- struct axienet_option *tp = &axienet_options[0];
+ const struct axienet_option *tp = lp->axienet_config->options;
while (tp->opt) {
reg = ((axienet_ior(lp, tp->reg)) & ~(tp->m_or));
@@ -627,14 +623,25 @@ static void axienet_stats_update(struct axienet_local *lp, bool reset)
write_seqcount_end(&lp->hw_stats_seqcount);
}
+/**
+ * axienet_1g_stats_update - Refresh 1G MAC hardware statistics
+ * @lp: Pointer to the axienet_local structure
+ */
+static void axienet_1g_stats_update(struct axienet_local *lp)
+{
+ axienet_stats_update(lp, false);
+}
+
static void axienet_refresh_stats(struct work_struct *work)
{
struct axienet_local *lp = container_of(work, struct axienet_local,
stats_work.work);
- mutex_lock(&lp->stats_lock);
- axienet_stats_update(lp, false);
- mutex_unlock(&lp->stats_lock);
+ if (lp->axienet_config->stats_update) {
+ mutex_lock(&lp->stats_lock);
+ lp->axienet_config->stats_update(lp);
+ mutex_unlock(&lp->stats_lock);
+ }
/* Just less than 2^32 bytes at 2.5 GBit/s */
schedule_delayed_work(&lp->stats_work, 13 * HZ);
@@ -745,6 +752,32 @@ static void axienet_dma_stop(struct axienet_local *lp)
axienet_unlock_mii(lp);
}
+/**
+ * axienet_1g_mac_init - 1G MAC-specific bring-up after the DMA reset
+ * @ndev: Pointer to the net_device structure
+ *
+ * Return: 0 on success or a negative error number otherwise.
+ */
+static int axienet_1g_mac_init(struct net_device *ndev)
+{
+ struct axienet_local *lp = netdev_priv(ndev);
+ u32 axienet_status;
+
+ axienet_status = axienet_ior(lp, XAE_RCW1_OFFSET);
+ axienet_status &= ~XAE_RCW1_RX_MASK;
+ axienet_iow(lp, XAE_RCW1_OFFSET, axienet_status);
+
+ axienet_status = axienet_ior(lp, XAE_IP_OFFSET);
+ if (axienet_status & XAE_INT_RXRJECT_MASK)
+ axienet_iow(lp, XAE_IS_OFFSET, XAE_INT_RXRJECT_MASK);
+ axienet_iow(lp, XAE_IE_OFFSET, lp->eth_irq > 0 ?
+ XAE_INT_RECV_ERROR_MASK : 0);
+
+ axienet_iow(lp, XAE_FCC_OFFSET, XAE_FCC_FCRX_MASK);
+
+ return 0;
+}
+
/**
* axienet_device_reset - Reset and initialize the Axi Ethernet hardware.
* @ndev: Pointer to the net_device structure
@@ -760,19 +793,23 @@ static void axienet_dma_stop(struct axienet_local *lp)
*/
static int axienet_device_reset(struct net_device *ndev)
{
- u32 axienet_status;
struct axienet_local *lp = netdev_priv(ndev);
int ret;
+ if (lp->axienet_config->gt_reset)
+ lp->axienet_config->gt_reset(lp);
+
lp->max_frm_size = XAE_MAX_VLAN_FRAME_SIZE;
- lp->options |= XAE_OPTION_VLAN;
+
+ if (lp->axienet_config->vlan)
+ lp->options |= XAE_OPTION_VLAN;
lp->options &= (~XAE_OPTION_JUMBO);
if (ndev->mtu > XAE_MTU && ndev->mtu <= XAE_JUMBO_MTU) {
lp->max_frm_size = ndev->mtu + VLAN_ETH_HLEN +
XAE_TRL_SIZE;
- if (lp->max_frm_size <= lp->rxmem)
+ if (lp->max_frm_size <= lp->rxmem && lp->axienet_config->jumbo)
lp->options |= XAE_OPTION_JUMBO;
}
@@ -789,17 +826,9 @@ static int axienet_device_reset(struct net_device *ndev)
}
}
- axienet_status = axienet_ior(lp, XAE_RCW1_OFFSET);
- axienet_status &= ~XAE_RCW1_RX_MASK;
- axienet_iow(lp, XAE_RCW1_OFFSET, axienet_status);
-
- axienet_status = axienet_ior(lp, XAE_IP_OFFSET);
- if (axienet_status & XAE_INT_RXRJECT_MASK)
- axienet_iow(lp, XAE_IS_OFFSET, XAE_INT_RXRJECT_MASK);
- axienet_iow(lp, XAE_IE_OFFSET, lp->eth_irq > 0 ?
- XAE_INT_RECV_ERROR_MASK : 0);
-
- axienet_iow(lp, XAE_FCC_OFFSET, XAE_FCC_FCRX_MASK);
+ ret = lp->axienet_config->mac_init(ndev);
+ if (ret)
+ return ret;
/* Sync default options with HW but leave receiver and
* transmitter disabled.
@@ -967,6 +996,13 @@ axienet_start_xmit_dmaengine(struct sk_buff *skb, struct net_device *ndev)
int sg_len;
int ret;
+ if (lp->axienet_config->sw_padding && eth_skb_pad(skb)) {
+ /* Pad short frames; the MAC does not append Ethernet padding. */
+ ndev->stats.tx_dropped++;
+ ndev->stats.tx_errors++;
+ return NETDEV_TX_OK;
+ }
+
dma_dev = lp->tx_chan->device;
sg_len = skb_shinfo(skb)->nr_frags + 1;
if (CIRC_SPACE(lp->tx_ring_head, lp->tx_ring_tail, TX_BD_NUM_MAX) <= 1) {
@@ -990,19 +1026,21 @@ axienet_start_xmit_dmaengine(struct sk_buff *skb, struct net_device *ndev)
goto xmit_error_drop_skb;
/* Fill up app fields for checksum */
- if (skb->ip_summed == CHECKSUM_PARTIAL) {
- if (lp->features & XAE_FEATURE_FULL_TX_CSUM) {
- /* Tx Full Checksum Offload Enabled */
- app_metadata[0] |= 2;
- } else if (lp->features & XAE_FEATURE_PARTIAL_TX_CSUM) {
- csum_start_off = skb_transport_offset(skb);
- csum_index_off = csum_start_off + skb->csum_offset;
- /* Tx Partial Checksum Offload Enabled */
- app_metadata[0] |= 1;
- app_metadata[1] = (csum_start_off << 16) | csum_index_off;
+ if (lp->axienet_config->tx_csum) {
+ if (skb->ip_summed == CHECKSUM_PARTIAL) {
+ if (lp->features & XAE_FEATURE_FULL_TX_CSUM) {
+ /* Tx Full Checksum Offload Enabled */
+ app_metadata[0] |= 2;
+ } else if (lp->features & XAE_FEATURE_PARTIAL_TX_CSUM) {
+ csum_start_off = skb_transport_offset(skb);
+ csum_index_off = csum_start_off + skb->csum_offset;
+ /* Tx Partial Checksum Offload Enabled */
+ app_metadata[0] |= 1;
+ app_metadata[1] = (csum_start_off << 16) | csum_index_off;
+ }
+ } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
+ app_metadata[0] |= 2; /* Tx Full Checksum Offload Enabled */
}
- } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
- app_metadata[0] |= 2; /* Tx Full Checksum Offload Enabled */
}
dma_tx_desc = dma_dev->device_prep_slave_sg(lp->tx_chan, skbuf_dma->sgl,
@@ -1855,7 +1893,8 @@ static int axienet_stop(struct net_device *ndev)
}
netdev_reset_queue(ndev);
- axienet_iow(lp, XAE_IE_OFFSET, 0);
+ if (lp->axienet_config->mac_irq)
+ axienet_iow(lp, XAE_IE_OFFSET, 0);
if (lp->eth_irq > 0)
free_irq(lp->eth_irq, ndev);
@@ -2024,30 +2063,18 @@ static void axienet_ethtools_get_drvinfo(struct net_device *ndev,
*/
static int axienet_ethtools_get_regs_len(struct net_device *ndev)
{
- return sizeof(u32) * AXIENET_REGS_N;
+ struct axienet_local *lp = netdev_priv(ndev);
+
+ return sizeof(u32) * lp->axienet_config->regs_n;
}
/**
- * axienet_ethtools_get_regs - Dump the contents of all registers present
- * in AxiEthernet core.
- * @ndev: Pointer to net_device structure
- * @regs: Pointer to ethtool_regs structure
- * @ret: Void pointer used to return the contents of the registers.
- *
- * This implements ethtool command for getting the Axi Ethernet register dump.
- * Issue "ethtool -d ethX" to execute this function.
+ * axienet_1g_get_regs - Dump 1G MAC registers for ethtool
+ * @lp: Pointer to the axienet_local structure
+ * @data: Buffer for register values (zeroed and sized by the caller)
*/
-static void axienet_ethtools_get_regs(struct net_device *ndev,
- struct ethtool_regs *regs, void *ret)
+static void axienet_1g_get_regs(struct axienet_local *lp, u32 *data)
{
- u32 *data = (u32 *)ret;
- size_t len = sizeof(u32) * AXIENET_REGS_N;
- struct axienet_local *lp = netdev_priv(ndev);
-
- regs->version = 0;
- regs->len = len;
-
- memset(data, 0, len);
data[0] = axienet_ior(lp, XAE_RAF_OFFSET);
data[1] = axienet_ior(lp, XAE_TPF_OFFSET);
data[2] = axienet_ior(lp, XAE_IFGP_OFFSET);
@@ -2088,6 +2115,31 @@ static void axienet_ethtools_get_regs(struct net_device *ndev,
}
}
+/**
+ * axienet_ethtools_get_regs - Dump the contents of all registers present
+ * in AxiEthernet core.
+ * @ndev: Pointer to net_device structure
+ * @regs: Pointer to ethtool_regs structure
+ * @ret: Void pointer used to return the contents of the registers.
+ *
+ * This implements ethtool command for getting the Axi Ethernet register dump.
+ * Issue "ethtool -d ethX" to execute this function.
+ */
+static void axienet_ethtools_get_regs(struct net_device *ndev,
+ struct ethtool_regs *regs, void *ret)
+{
+ struct axienet_local *lp = netdev_priv(ndev);
+ u32 *data = (u32 *)ret;
+ size_t len;
+
+ len = sizeof(u32) * lp->axienet_config->regs_n;
+ regs->version = 0;
+ regs->len = len;
+
+ memset(data, 0, len);
+ lp->axienet_config->get_regs(lp, data);
+}
+
static void
axienet_ethtools_get_ringparam(struct net_device *ndev,
struct ethtool_ringparam *ering,
@@ -2724,13 +2776,9 @@ static void axienet_mac_link_down(struct phylink_config *config,
/* nothing meaningful to do */
}
-static void axienet_mac_link_up(struct phylink_config *config,
- struct phy_device *phy,
- unsigned int mode, phy_interface_t interface,
- int speed, int duplex,
- bool tx_pause, bool rx_pause)
+static void axienet_1g_mac_link_up(struct net_device *ndev, int speed,
+ bool tx_pause, bool rx_pause)
{
- struct net_device *ndev = to_net_dev(config->dev);
struct axienet_local *lp = netdev_priv(ndev);
u32 emmc_reg, fcc_reg;
@@ -2767,6 +2815,19 @@ static void axienet_mac_link_up(struct phylink_config *config,
axienet_iow(lp, XAE_FCC_OFFSET, fcc_reg);
}
+static void axienet_mac_link_up(struct phylink_config *config,
+ struct phy_device *phy,
+ unsigned int mode, phy_interface_t interface,
+ int speed, int duplex,
+ bool tx_pause, bool rx_pause)
+{
+ struct net_device *ndev = to_net_dev(config->dev);
+ struct axienet_local *lp = netdev_priv(ndev);
+
+ if (lp->axienet_config->mac_link_up)
+ lp->axienet_config->mac_link_up(ndev, speed, tx_pause, rx_pause);
+}
+
static const struct phylink_mac_ops axienet_phylink_ops = {
.mac_select_pcs = axienet_mac_select_pcs,
.mac_config = axienet_mac_config,
@@ -2784,7 +2845,6 @@ static const struct phylink_mac_ops axienet_phylink_ops = {
static void axienet_dma_err_handler(struct work_struct *work)
{
u32 i;
- u32 axienet_status;
struct axidma_bd *cur_p;
struct axienet_local *lp = container_of(work, struct axienet_local,
dma_err_task);
@@ -2821,16 +2881,10 @@ static void axienet_dma_err_handler(struct work_struct *work)
axienet_dma_start(lp);
- axienet_status = axienet_ior(lp, XAE_RCW1_OFFSET);
- axienet_status &= ~XAE_RCW1_RX_MASK;
- axienet_iow(lp, XAE_RCW1_OFFSET, axienet_status);
-
- axienet_status = axienet_ior(lp, XAE_IP_OFFSET);
- if (axienet_status & XAE_INT_RXRJECT_MASK)
- axienet_iow(lp, XAE_IS_OFFSET, XAE_INT_RXRJECT_MASK);
- axienet_iow(lp, XAE_IE_OFFSET, lp->eth_irq > 0 ?
- XAE_INT_RECV_ERROR_MASK : 0);
- axienet_iow(lp, XAE_FCC_OFFSET, XAE_FCC_FCRX_MASK);
+ /* This error handler runs only for the non-dmaengine flow (1G MAC),
+ * whose mac_init() cannot fail, so its return value is not checked.
+ */
+ lp->axienet_config->mac_init(ndev);
/* Sync default options with HW but leave receiver and
* transmitter disabled.
@@ -2844,6 +2898,100 @@ static void axienet_dma_err_handler(struct work_struct *work)
axienet_setoptions(ndev, lp->options);
}
+/**
+ * axienet_1g_phylink_set_caps - Set 1G phylink capabilities and interfaces
+ * @lp: Pointer to the axienet_local structure
+ * @config: Pointer to the phylink_config structure
+ */
+static void axienet_1g_phylink_set_caps(struct axienet_local *lp,
+ struct phylink_config *config)
+{
+ config->mac_capabilities |= MAC_10FD | MAC_100FD | MAC_1000FD;
+ __set_bit(lp->phy_mode, config->supported_interfaces);
+ if (lp->switch_x_sgmii) {
+ __set_bit(PHY_INTERFACE_MODE_1000BASEX,
+ config->supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_SGMII,
+ config->supported_interfaces);
+ }
+}
+
+/**
+ * axienet_1g_clk_init - Get and enable the 1G MAC clocks
+ * @lp: Pointer to the axienet_local structure
+ *
+ * Return: 0 on success or a negative error number otherwise.
+ */
+static int axienet_1g_clk_init(struct axienet_local *lp)
+{
+ struct device *dev = lp->dev;
+ int ret;
+
+ lp->axi_clk = devm_clk_get_optional_enabled(dev, "s_axi_lite_clk");
+ if (!lp->axi_clk) {
+ /* For backward compatibility, if named AXI clock is not
+ * present, treat the first clock specified as the AXI clock.
+ */
+ lp->axi_clk = devm_clk_get_optional_enabled(dev, NULL);
+ }
+ if (IS_ERR(lp->axi_clk))
+ return dev_err_probe(dev, PTR_ERR(lp->axi_clk),
+ "could not get AXI clock\n");
+
+ lp->misc_clks[0].id = "axis_clk";
+ lp->misc_clks[1].id = "ref_clk";
+ lp->misc_clks[2].id = "mgt_clk";
+
+ ret = devm_clk_bulk_get_optional_enable(dev, XAE_NUM_MISC_CLOCKS,
+ lp->misc_clks);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "could not get/enable misc. clocks\n");
+
+ return 0;
+}
+
+/**
+ * axienet_1g_probe_init - 1G MAC-specific probe-time initialization
+ * @lp: Pointer to the axienet_local structure
+ */
+static void axienet_1g_probe_init(struct axienet_local *lp)
+{
+ if (axienet_ior(lp, XAE_ABILITY_OFFSET) & XAE_ABILITY_STATS)
+ lp->features |= XAE_FEATURE_STATS;
+}
+
+static const struct axienet_config axienet_1g_config = {
+ .mdio = true,
+ .mac_irq = true,
+ .non_dmaengine = true,
+ .uc_filter = true,
+ .mc_filter = true,
+ .vlan = true,
+ .jumbo = true,
+ .tx_csum = true,
+ .regs_n = AXIENET_REGS_N,
+ .options = axienet_options,
+ .clk_init = axienet_1g_clk_init,
+ .probe_init = axienet_1g_probe_init,
+ .mac_init = axienet_1g_mac_init,
+ .mac_link_up = axienet_1g_mac_link_up,
+ .get_regs = axienet_1g_get_regs,
+ .phylink_set_caps = axienet_1g_phylink_set_caps,
+ .pcs_ops = &axienet_pcs_ops,
+ .stats_update = axienet_1g_stats_update,
+};
+
+/* Match table for of_platform binding */
+static const struct of_device_id axienet_of_match[] = {
+ { .compatible = "xlnx,axi-ethernet-1.00.a", .data = &axienet_1g_config },
+ { .compatible = "xlnx,axi-ethernet-1.01.a", .data = &axienet_1g_config },
+ { .compatible = "xlnx,axi-ethernet-2.01.a", .data = &axienet_1g_config },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, axienet_of_match);
+
/**
* axienet_probe - Axi Ethernet probe function.
* @pdev: Pointer to platform device structure.
@@ -2894,27 +3042,13 @@ static int axienet_probe(struct platform_device *pdev)
seqcount_mutex_init(&lp->hw_stats_seqcount, &lp->stats_lock);
INIT_DEFERRABLE_WORK(&lp->stats_work, axienet_refresh_stats);
- lp->axi_clk = devm_clk_get_optional_enabled(&pdev->dev,
- "s_axi_lite_clk");
- if (!lp->axi_clk) {
- /* For backward compatibility, if named AXI clock is not present,
- * treat the first clock specified as the AXI clock.
- */
- lp->axi_clk = devm_clk_get_optional_enabled(&pdev->dev, NULL);
- }
- if (IS_ERR(lp->axi_clk))
- return dev_err_probe(&pdev->dev, PTR_ERR(lp->axi_clk),
- "could not get AXI clock\n");
-
- lp->misc_clks[0].id = "axis_clk";
- lp->misc_clks[1].id = "ref_clk";
- lp->misc_clks[2].id = "mgt_clk";
+ lp->axienet_config = device_get_match_data(&pdev->dev);
+ if (!lp->axienet_config)
+ return -ENODEV;
- ret = devm_clk_bulk_get_optional_enable(&pdev->dev, XAE_NUM_MISC_CLOCKS,
- lp->misc_clks);
+ ret = lp->axienet_config->clk_init(lp);
if (ret)
- return dev_err_probe(&pdev->dev, ret,
- "could not get/enable misc. clocks\n");
+ return ret;
/* Map device registers */
lp->regs = devm_platform_get_and_ioremap_resource(pdev, 0, ðres);
@@ -2925,11 +3059,12 @@ static int axienet_probe(struct platform_device *pdev)
/* Setup checksum offload, but default to off if not specified */
lp->features = 0;
- if (axienet_ior(lp, XAE_ABILITY_OFFSET) & XAE_ABILITY_STATS)
- lp->features |= XAE_FEATURE_STATS;
-
+ /* Only honour the TX checksum offload property on MACs whose DMA path
+ * can complete checksums. Otherwise the stack would hand down partial
+ * checksums that the hardware never finishes.
+ */
ret = of_property_read_u32(pdev->dev.of_node, "xlnx,txcsum", &value);
- if (!ret) {
+ if (!ret && lp->axienet_config->tx_csum) {
switch (value) {
case 1:
lp->features |= XAE_FEATURE_PARTIAL_TX_CSUM;
@@ -3005,7 +3140,17 @@ static int axienet_probe(struct platform_device *pdev)
return -EINVAL;
}
+ if (lp->axienet_config->probe_init)
+ lp->axienet_config->probe_init(lp);
+
if (!of_property_present(pdev->dev.of_node, "dmas")) {
+ /* Non-dmaengine mode is only supported by some MACs */
+ if (!lp->axienet_config->non_dmaengine) {
+ dev_err(&pdev->dev,
+ "Non-dmaengine mode not supported by this MAC, use dmaengine\n");
+ return -EINVAL;
+ }
+
/* Find the DMA node, map the DMA registers, and decode the DMA IRQs */
np = of_parse_phandle(pdev->dev.of_node, "axistream-connected", 0);
@@ -3091,7 +3236,10 @@ static int axienet_probe(struct platform_device *pdev)
struct xilinx_vdma_config cfg;
struct dma_chan *tx_chan;
- lp->eth_irq = platform_get_irq_optional(pdev, 0);
+ if (lp->axienet_config->mac_irq)
+ lp->eth_irq = platform_get_irq_optional(pdev, 0);
+ else
+ lp->eth_irq = -ENXIO;
if (lp->eth_irq < 0 && lp->eth_irq != -ENXIO) {
return lp->eth_irq;
}
@@ -3145,10 +3293,12 @@ static int axienet_probe(struct platform_device *pdev)
lp->tx_dma_cr = axienet_calc_cr(lp, XAXIDMA_DFT_TX_THRESHOLD,
XAXIDMA_DFT_TX_USEC);
- ret = axienet_mdio_setup(lp);
- if (ret)
- dev_warn(&pdev->dev,
- "error registering MDIO bus: %d\n", ret);
+ if (lp->axienet_config->mdio) {
+ ret = axienet_mdio_setup(lp);
+ if (ret)
+ dev_warn(&pdev->dev,
+ "error registering MDIO bus: %d\n", ret);
+ }
if (lp->phy_mode == PHY_INTERFACE_MODE_SGMII ||
lp->phy_mode == PHY_INTERFACE_MODE_1000BASEX) {
@@ -3172,23 +3322,21 @@ static int axienet_probe(struct platform_device *pdev)
goto cleanup_mdio;
}
of_node_put(np);
- lp->pcs.ops = &axienet_pcs_ops;
+ lp->pcs.ops = lp->axienet_config->pcs_ops;
+ lp->pcs.poll = true;
+ }
+
+ if (lp->axienet_config->internal_pcs) {
+ lp->pcs.ops = lp->axienet_config->pcs_ops;
lp->pcs.poll = true;
}
lp->phylink_config.dev = &ndev->dev;
lp->phylink_config.type = PHYLINK_NETDEV;
lp->phylink_config.mac_managed_pm = true;
- lp->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE |
- MAC_10FD | MAC_100FD | MAC_1000FD;
+ lp->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE;
- __set_bit(lp->phy_mode, lp->phylink_config.supported_interfaces);
- if (lp->switch_x_sgmii) {
- __set_bit(PHY_INTERFACE_MODE_1000BASEX,
- lp->phylink_config.supported_interfaces);
- __set_bit(PHY_INTERFACE_MODE_SGMII,
- lp->phylink_config.supported_interfaces);
- }
+ lp->axienet_config->phylink_set_caps(lp, &lp->phylink_config);
lp->phylink = phylink_create(&lp->phylink_config, pdev->dev.fwnode,
lp->phy_mode,
@@ -3231,7 +3379,8 @@ static void axienet_remove(struct platform_device *pdev)
if (lp->pcs_phy)
put_device(&lp->pcs_phy->dev);
- axienet_mdio_teardown(lp);
+ if (lp->mii_bus)
+ axienet_mdio_teardown(lp);
}
static void axienet_shutdown(struct platform_device *pdev)
--
2.25.1
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH net-next v5 2/6] dt-bindings: net: xlnx,xxv-ethernet: Add Xilinx XXV 10G/25G Ethernet
2026-11-14 18:29 [PATCH net-next v5 0/6] net: xilinx: axienet: Add 10G/25G (XXV) ethernet support Suraj Gupta
2026-11-14 18:29 ` [PATCH net-next v5 1/6] net: xilinx: axienet: Introduce axienet_config for MAC-specific ops Suraj Gupta
@ 2026-11-14 18:29 ` Suraj Gupta
2026-11-14 18:29 ` [PATCH net-next v5 3/6] net: xilinx: axienet: Propagate device reset failure from ndo_open Suraj Gupta
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Suraj Gupta @ 2026-11-14 18:29 UTC (permalink / raw)
To: Radhey Shyam Pandey, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: Michal Simek, Russell King, netdev, devicetree, linux-arm-kernel,
linux-kernel
The XXV Ethernet IP integrates a 10G/25G Ethernet MAC with an in-core
PCS/PMA driving a high-speed serial transceiver. Its payload is moved by
an external AXI DMA (or multichannel DMA) controller referenced through
the dmas/dma-names properties. Document the IP in a dedicated schema
file.
Add the "xlnx,xxv-ethernet-1.0" compatible with the 10gbase-r and
25gbase-r phy-modes, the rx_core/drp/s_axi clocks and the AXI DMA
channel properties. The GT transceiver reference clock is described as
an optional fourth "ref" clock, for boards where it is supplied by a
Linux-managed clock provider rather than a fixed oscillator.
Add "xlnx,has-statistics-counters" as well. The statistics counters are
an optional part of the IP, selected by the "Include Statistics Counters"
build option (PG210), and unlike the 1G MAC the subsystem has no ability
register that reports whether they were included, so the devicetree has
to describe it.
Require "xlnx,rxmem". The driver reads it unconditionally at probe time
and fails if it is absent, and the value is what bounds the largest frame
the device accepts, both when validating a new MTU and when deciding
whether jumbo frames can be enabled.
Disallow phy-handle, its deprecated phy and phy-device spellings, and
pcs-handle. All are inherited from ethernet-controller.yaml, so
unevaluatedProperties does not reject them, but the subsystem has no MDIO
bus master and its PCS is in-core, so neither an external PHY nor an
external PCS can be attached. Constrain managed to in-band-status for the
same reason; it stays optional, as the link is always managed in-band.
Signed-off-by: Suraj Gupta <suraj.gupta2@amd.com>
---
.../bindings/net/xlnx,xxv-ethernet.yaml | 146 ++++++++++++++++++
MAINTAINERS | 1 +
2 files changed, 147 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/xlnx,xxv-ethernet.yaml
diff --git a/Documentation/devicetree/bindings/net/xlnx,xxv-ethernet.yaml b/Documentation/devicetree/bindings/net/xlnx,xxv-ethernet.yaml
new file mode 100644
index 000000000000..caf582c72a8d
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/xlnx,xxv-ethernet.yaml
@@ -0,0 +1,146 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/xlnx,xxv-ethernet.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Xilinx XXV 10G/25G Ethernet Subsystem
+
+maintainers:
+ - Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
+ - Suraj Gupta <suraj.gupta2@amd.com>
+
+description:
+ The Xilinx XXV Ethernet Subsystem implements a 10G/25G Ethernet MAC with an
+ in-core PCS/PMA driving a high-speed serial transceiver. It supports the
+ 10GBASE-R and 25GBASE-R line rates, with 25G operation following the 25G
+ Ethernet Consortium specification. Because the PCS/PMA is integrated into
+ the IP, the subsystem has no MDIO bus master and is not managed through an
+ external MDIO PHY; link state is read from the MAC's own status registers.
+ As there is no PHY to manage, the link is always managed in-band.
+ Any SFP module management (e.g. I2C) is handled by separate IP outside this
+ binding. Its payload is moved by an external AXI DMA (or multichannel DMA)
+ controller referenced through the "dmas" property.
+
+properties:
+ compatible:
+ const: xlnx,xxv-ethernet-1.0
+
+ reg:
+ maxItems: 1
+ description: MAC control and status register space.
+
+ clocks:
+ minItems: 3
+ items:
+ - description: RX AXI4-Stream clock.
+ - description: GT helper (dclk) stable clock.
+ - description: AXI4-Lite register clock.
+ - description: GT transceiver reference clock (refclk).
+
+ clock-names:
+ minItems: 3
+ items:
+ - const: rx_core
+ - const: drp
+ - const: s_axi
+ - const: ref
+
+ phy-mode:
+ enum:
+ - 10gbase-r
+ - 25gbase-r
+
+ # The subsystem has no MDIO bus master and its PCS is in-core, so neither
+ # an external PHY nor an external PCS can be attached. phy and phy-device
+ # are the deprecated spellings of phy-handle.
+ phy-handle: false
+ phy: false
+ phy-device: false
+ pcs-handle: false
+
+ managed:
+ const: in-band-status
+
+ xlnx,rxmem:
+ description:
+ Size in bytes of the Rx/Tx packet buffer memory built into the IP. It is
+ fixed by the hardware design and bounds the largest frame (jumbo MTU) the
+ device can accept.
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ xlnx,has-statistics-counters:
+ description:
+ The statistics counters are an optional part of the IP, selected by the
+ "Include Statistics Counters" build option, and the subsystem does not
+ advertise whether they were included. Present when the design was built
+ with them, in which case the driver reports the MAC counters through
+ ethtool and ndo_get_stats64.
+ type: boolean
+
+ dmas:
+ minItems: 2
+ maxItems: 32
+ description:
+ TX and RX DMA channel phandles, one per entry in "dma-names".
+
+ dma-names:
+ minItems: 2
+ maxItems: 32
+ items:
+ pattern: "^[tr]x_chan([0-9]|1[0-5])$"
+ allOf:
+ - contains:
+ const: tx_chan0
+ - contains:
+ const: rx_chan0
+ description:
+ Should be "tx_chan0", "tx_chan1" ... "tx_chan15" for DMA Tx channel and
+ "rx_chan0", "rx_chan1" ... "rx_chan15" for DMA Rx channel. The first
+ Tx/Rx channel pair ("tx_chan0" and "rx_chan0") is always required. There
+ is one name per entry in "dmas", in the same order.
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - phy-mode
+ - xlnx,rxmem
+ - dmas
+ - dma-names
+
+allOf:
+ - $ref: /schemas/net/ethernet-controller.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ # Single AXI DMA channel pair
+ - |
+ ethernet@80000000 {
+ compatible = "xlnx,xxv-ethernet-1.0";
+ reg = <0x80000000 0x10000>;
+ clock-names = "rx_core", "drp", "s_axi";
+ clocks = <&rx_clk>, <&drp_clk>, <&axi_clk>;
+ dma-names = "tx_chan0", "rx_chan0";
+ dmas = <&axi_dma 0>, <&axi_dma 1>;
+ phy-mode = "25gbase-r";
+ managed = "in-band-status";
+ xlnx,rxmem = <0x8000>;
+ xlnx,has-statistics-counters;
+ };
+ # Multichannel DMA (MCDMA) with multiple Tx/Rx channels
+ - |
+ ethernet@80010000 {
+ compatible = "xlnx,xxv-ethernet-1.0";
+ reg = <0x80010000 0x10000>;
+ clock-names = "rx_core", "drp", "s_axi";
+ clocks = <&rx_clk>, <&drp_clk>, <&axi_clk>;
+ dma-names = "tx_chan0", "tx_chan1", "rx_chan0", "rx_chan1";
+ dmas = <&axi_mcdma 0>, <&axi_mcdma 1>,
+ <&axi_mcdma 16>, <&axi_mcdma 17>;
+ phy-mode = "10gbase-r";
+ managed = "in-band-status";
+ xlnx,rxmem = <0x8000>;
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 893b0670a7b6..b33fd70d242f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -30683,6 +30683,7 @@ XILINX AXI ETHERNET DRIVER
M: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
S: Maintained
F: Documentation/devicetree/bindings/net/xlnx,axi-ethernet.yaml
+F: Documentation/devicetree/bindings/net/xlnx,xxv-ethernet.yaml
F: drivers/net/ethernet/xilinx/xilinx_axienet*
XILINX CAN DRIVER
--
2.25.1
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH net-next v5 3/6] net: xilinx: axienet: Propagate device reset failure from ndo_open
2026-11-14 18:29 [PATCH net-next v5 0/6] net: xilinx: axienet: Add 10G/25G (XXV) ethernet support Suraj Gupta
2026-11-14 18:29 ` [PATCH net-next v5 1/6] net: xilinx: axienet: Introduce axienet_config for MAC-specific ops Suraj Gupta
2026-11-14 18:29 ` [PATCH net-next v5 2/6] dt-bindings: net: xlnx,xxv-ethernet: Add Xilinx XXV 10G/25G Ethernet Suraj Gupta
@ 2026-11-14 18:29 ` Suraj Gupta
2026-11-14 18:29 ` [PATCH net-next v5 4/6] net: xilinx: axienet: Add 10G/25G (XXV) ethernet support Suraj Gupta
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Suraj Gupta @ 2026-11-14 18:29 UTC (permalink / raw)
To: Radhey Shyam Pandey, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: Michal Simek, Russell King, netdev, devicetree, linux-arm-kernel,
linux-kernel
axienet_open() overwrites the axienet_device_reset() return value with the
phylink_fwnode_phy_connect() one before it is ever checked, so a failed
reset brings the device up anyway. Check and propagate it.
That makes a failed ndo_open reachable, which matters for axienet_resume():
it calls ndo_open directly, so unlike the netdev core it leaves the device
running when open fails. A later ndo_stop then tears down resources that
open had already unwound and never re-created. For the dmaengine flow that
is a double free of the skb rings and a second dma_release_channel(); for
the non-dmaengine flow it is napi_disable() on an already disabled NAPI,
which never returns, and free_irq() on interrupts that were never
requested.
Track whether ndo_open completed and make axienet_stop() a no-op otherwise.
axienet_resume() now propagates the error, but it still has to undo the
netif_device_detach() that axienet_suspend() did. Leaving the device
detached while it is still IFF_UP wedges the interface: ndo_stop is a
no-op, so bringing it down appears to work, while bringing it up again
fails with -ENODEV because the device is not present. Drop IFF_UP with
netif_close() before reattaching, so the interface can be brought up again
once the cause of the failure is resolved, and so the stack cannot
transmit on the datapath open has already torn down.
Clearing the error path of __axienet_device_reset() goes with this: it
jumps over the block that clears lp->reset_in_progress, and the statistics
work that used to clear it on the next run is no longer scheduled once the
reset error is propagated. Without this, axienet_stat() would keep
returning the frozen base after a reset timeout.
Signed-off-by: Suraj Gupta <suraj.gupta2@amd.com>
---
drivers/net/ethernet/xilinx/xilinx_axienet.h | 2 +
.../net/ethernet/xilinx/xilinx_axienet_main.c | 44 +++++++++++++++++--
2 files changed, 42 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h
index a87cfa079502..140383749996 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet.h
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h
@@ -526,6 +526,7 @@ struct skbuf_dma_descriptor {
* @stopping: Set when we are about to stop the device: makes @dma_err_task
* a no-op (legacy DMA path) and fences RX descriptor
* resubmission in axienet_dma_rx_cb() (dmaengine path).
+ * @datapath_up: Set while the resources allocated by ndo_open are live.
* @tx_irq: Axidma TX IRQ number
* @rx_irq: Axidma RX IRQ number
* @eth_irq: Ethernet core IRQ number
@@ -608,6 +609,7 @@ struct axienet_local {
struct work_struct dma_err_task;
bool stopping;
+ bool datapath_up;
int tx_irq;
int rx_irq;
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
index 83f35effbf37..f338029cf870 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
@@ -702,6 +702,17 @@ static int __axienet_device_reset(struct axienet_local *lp)
}
out:
+ /* On failure the block above is skipped, so clear the flag here.
+ * axienet_open() now returns on a reset error without scheduling the
+ * statistics work that used to clear it, which would otherwise leave
+ * axienet_stat() returning the frozen base until a reset succeeds.
+ */
+ if (ret && (lp->features & XAE_FEATURE_STATS)) {
+ write_seqcount_begin(&lp->hw_stats_seqcount);
+ lp->reset_in_progress = false;
+ write_seqcount_end(&lp->hw_stats_seqcount);
+ }
+
mutex_unlock(&lp->stats_lock);
return ret;
}
@@ -1772,6 +1783,10 @@ static int axienet_open(struct net_device *ndev)
axienet_lock_mii(lp);
ret = axienet_device_reset(ndev);
axienet_unlock_mii(lp);
+ if (ret) {
+ dev_err(lp->dev, "axienet_device_reset() failed: %d\n", ret);
+ return ret;
+ }
ret = phylink_fwnode_phy_connect(lp->phylink, dev_fwnode(lp->dev), 0);
if (ret) {
@@ -1802,6 +1817,8 @@ static int axienet_open(struct net_device *ndev)
goto err_phy;
}
+ lp->datapath_up = true;
+
return 0;
err_free_eth_irq:
@@ -1830,6 +1847,16 @@ static int axienet_stop(struct net_device *ndev)
struct axienet_local *lp = netdev_priv(ndev);
int i;
+ /* axienet_resume() calls ndo_open directly, so unlike the netdev core
+ * it can leave the device running after a failed open. Tearing down
+ * below would then stop a phylink that open already stopped and
+ * disconnected, and free interrupts it never requested.
+ */
+ if (!lp->datapath_up)
+ return 0;
+
+ lp->datapath_up = false;
+
if (!lp->use_dmaengine) {
WRITE_ONCE(lp->stopping, true);
flush_work(&lp->dma_err_task);
@@ -3415,17 +3442,26 @@ static int axienet_suspend(struct device *dev)
static int axienet_resume(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
+ int ret;
if (!netif_running(ndev))
return 0;
rtnl_lock();
- axienet_open(ndev);
- rtnl_unlock();
-
+ ret = axienet_open(ndev);
+ if (ret) {
+ netdev_err(ndev, "failed to reopen device on resume: %d\n", ret);
+ /* Drop IFF_UP before reattaching, so that the interface can be
+ * brought up again once whatever made open fail is resolved.
+ * Reattaching while still up would also let the stack transmit
+ * on the datapath open has already torn down.
+ */
+ netif_close(ndev);
+ }
netif_device_attach(ndev);
+ rtnl_unlock();
- return 0;
+ return ret;
}
static DEFINE_SIMPLE_DEV_PM_OPS(axienet_pm_ops,
--
2.25.1
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH net-next v5 4/6] net: xilinx: axienet: Add 10G/25G (XXV) ethernet support
2026-11-14 18:29 [PATCH net-next v5 0/6] net: xilinx: axienet: Add 10G/25G (XXV) ethernet support Suraj Gupta
` (2 preceding siblings ...)
2026-11-14 18:29 ` [PATCH net-next v5 3/6] net: xilinx: axienet: Propagate device reset failure from ndo_open Suraj Gupta
@ 2026-11-14 18:29 ` Suraj Gupta
2026-11-14 18:29 ` [PATCH net-next v5 5/6] net: xilinx: axienet: Dispatch statistics through axienet_config ops Suraj Gupta
2026-11-14 18:29 ` [PATCH net-next v5 6/6] net: xilinx: axienet: Add statistics support for XXV ethernet Suraj Gupta
5 siblings, 0 replies; 7+ messages in thread
From: Suraj Gupta @ 2026-11-14 18:29 UTC (permalink / raw)
To: Radhey Shyam Pandey, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: Michal Simek, Russell King, netdev, devicetree, linux-arm-kernel,
linux-kernel
The AXI Ethernet driver currently supports only the AXI 1G Ethernet IP.
Add support for the Xilinx XXV Ethernet IP[1], which implements a 10G/25G
Ethernet MAC with PCS/PMA driving a high-speed serial transceiver, as
specified by the 25G Ethernet Consortium.
Add the XXV-specific logic in new xilinx_axienet_xxv.c/.h files and
describe the MAC through an axienet_10g25g_config that provides the
capability flags, the MAC option table and callbacks (GT reset,
link-ready polling, phylink caps, PCS ops, register dump and probe-time
init). The shared paths consult those capabilities and callbacks instead
of MAC-type checks; the only interface-specific addition is handling the
10GBASER and 25GBASER modes in axienet_mac_select_pcs().
The register, RX and GT DRP clocks are required. The GT reference clock
is requested as optional, because it is usually a fixed oscillator that
Linux does not model; where a board does describe it as a gated provider
it has to be enabled, as the GT otherwise never reports reset-done.
The advertised rate comes from the required phy-mode property rather than
from the STAT_CORE_SPEED register, which PG210 provides only on the 64-bit
AXI4-Stream builds; a node declaring any other phy-mode is rejected at
probe. Cores built with runtime rate switching are not reconfigured to the
other rate, and flow control is not wired up, so pause support is not
advertised either.
The XXV mac_init() can fail on a GT reset-done timeout, which is why
ndo_open has to propagate a device reset failure as of the preceding
patch.
[1]: https://docs.amd.com/r/en-US/pg210-25g-ethernet
Signed-off-by: Suraj Gupta <suraj.gupta2@amd.com>
---
drivers/net/ethernet/xilinx/Makefile | 3 +-
drivers/net/ethernet/xilinx/xilinx_axienet.h | 9 +
.../net/ethernet/xilinx/xilinx_axienet_main.c | 23 +-
.../net/ethernet/xilinx/xilinx_axienet_xxv.c | 351 ++++++++++++++++++
.../net/ethernet/xilinx/xilinx_axienet_xxv.h | 93 +++++
5 files changed, 470 insertions(+), 9 deletions(-)
create mode 100644 drivers/net/ethernet/xilinx/xilinx_axienet_xxv.c
create mode 100644 drivers/net/ethernet/xilinx/xilinx_axienet_xxv.h
diff --git a/drivers/net/ethernet/xilinx/Makefile b/drivers/net/ethernet/xilinx/Makefile
index 7d7dc1771423..db90e691fec5 100644
--- a/drivers/net/ethernet/xilinx/Makefile
+++ b/drivers/net/ethernet/xilinx/Makefile
@@ -6,5 +6,6 @@
ll_temac-objs := ll_temac_main.o ll_temac_mdio.o
obj-$(CONFIG_XILINX_LL_TEMAC) += ll_temac.o
obj-$(CONFIG_XILINX_EMACLITE) += xilinx_emaclite.o
-xilinx_emac-objs := xilinx_axienet_main.o xilinx_axienet_mdio.o
+xilinx_emac-objs := xilinx_axienet_main.o xilinx_axienet_mdio.o \
+ xilinx_axienet_xxv.o
obj-$(CONFIG_XILINX_AXI_EMAC) += xilinx_emac.o
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h
index 140383749996..56570b49574e 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet.h
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h
@@ -17,6 +17,8 @@
#include <linux/phylink.h>
#include <linux/skbuff.h>
+#include "xilinx_axienet_xxv.h"
+
/* Packet size info */
#define XAE_HDR_SIZE 14 /* Size of Ethernet header */
#define XAE_TRL_SIZE 4 /* Size of Ethernet trailer (FCS) */
@@ -548,6 +550,7 @@ struct skbuf_dma_descriptor {
* @rx_ring_head: RX skb ring buffer head index.
* @rx_ring_tail: RX skb ring buffer tail index.
* @rx_submit_lock: Protects RX ring resubmission vs teardown in dmaengine path.
+ * @xxv_ip_version: XXV IP version.
* @axienet_config: MAC-type specific configuration and operations.
*/
struct axienet_local {
@@ -632,6 +635,7 @@ struct axienet_local {
int rx_ring_head;
int rx_ring_tail;
spinlock_t rx_submit_lock;
+ u32 xxv_ip_version;
const struct axienet_config *axienet_config;
};
@@ -689,6 +693,11 @@ struct axienet_config {
void (*stats_update)(struct axienet_local *lp);
};
+static inline struct axienet_local *pcs_to_axienet_local(struct phylink_pcs *pcs)
+{
+ return container_of(pcs, struct axienet_local, pcs);
+}
+
/**
* struct axienet_option - Used to set axi ethernet hardware options
* @opt: Option to be set.
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
index f338029cf870..a37aad4432ce 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
@@ -800,6 +800,12 @@ static int axienet_1g_mac_init(struct net_device *ndev)
* Ethernet core. No separate hardware reset is done for the Axi Ethernet
* core.
*
+ * It also recomputes @max_frm_size from the current MTU and runs the
+ * MAC-specific mac_init() callback, so it is the point at which length-related
+ * MAC configuration (such as the XXV RX max-packet-length) is reprogrammed.
+ * Because it is reached from the open path, a new MTU takes effect on the next
+ * interface up.
+ *
* Return: 0 on success or a negative error number otherwise.
*/
static int axienet_device_reset(struct net_device *ndev)
@@ -2720,11 +2726,6 @@ static const struct ethtool_ops axienet_ethtool_dmaengine_ops = {
.get_rmon_stats = axienet_ethtool_get_rmon_stats,
};
-static struct axienet_local *pcs_to_axienet_local(struct phylink_pcs *pcs)
-{
- return container_of(pcs, struct axienet_local, pcs);
-}
-
static void axienet_pcs_get_state(struct phylink_pcs *pcs,
unsigned int neg_mode,
struct phylink_link_state *state)
@@ -2784,7 +2785,9 @@ static struct phylink_pcs *axienet_mac_select_pcs(struct phylink_config *config,
struct axienet_local *lp = netdev_priv(ndev);
if (interface == PHY_INTERFACE_MODE_1000BASEX ||
- interface == PHY_INTERFACE_MODE_SGMII)
+ interface == PHY_INTERFACE_MODE_SGMII ||
+ interface == PHY_INTERFACE_MODE_10GBASER ||
+ interface == PHY_INTERFACE_MODE_25GBASER)
return &lp->pcs;
return NULL;
@@ -3014,6 +3017,7 @@ static const struct of_device_id axienet_of_match[] = {
{ .compatible = "xlnx,axi-ethernet-1.00.a", .data = &axienet_1g_config },
{ .compatible = "xlnx,axi-ethernet-1.01.a", .data = &axienet_1g_config },
{ .compatible = "xlnx,axi-ethernet-2.01.a", .data = &axienet_1g_config },
+ { .compatible = "xlnx,xxv-ethernet-1.0", .data = &axienet_10g25g_config },
{},
};
@@ -3051,9 +3055,10 @@ static int axienet_probe(struct platform_device *pdev)
SET_NETDEV_DEV(ndev, &pdev->dev);
ndev->features = NETIF_F_SG;
- /* MTU range: 64 - 9000 */
+ /* MTU range: min is 64; max is finalized once the MAC config (which
+ * tells us whether jumbo frames are supported) is known, see below.
+ */
ndev->min_mtu = 64;
- ndev->max_mtu = XAE_JUMBO_MTU;
lp = netdev_priv(ndev);
lp->ndev = ndev;
@@ -3073,6 +3078,8 @@ static int axienet_probe(struct platform_device *pdev)
if (!lp->axienet_config)
return -ENODEV;
+ ndev->max_mtu = lp->axienet_config->jumbo ? XAE_JUMBO_MTU : XAE_MTU;
+
ret = lp->axienet_config->clk_init(lp);
if (ret)
return ret;
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_xxv.c b/drivers/net/ethernet/xilinx/xilinx_axienet_xxv.c
new file mode 100644
index 000000000000..3c204dbbf6ae
--- /dev/null
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_xxv.c
@@ -0,0 +1,351 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * XXV (10G/25G) support
+ *
+ * Copyright (C) 2026 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/netdevice.h>
+#include <linux/phylink.h>
+
+#include "xilinx_axienet.h"
+#include "xilinx_axienet_xxv.h"
+
+/* readl_poll_timeout() sleep interval (us) for the GT reset-done and PCS
+ * block-lock polls, which both run to a DELAY_OF_ONE_MILLISEC timeout. The
+ * interval and that bound come from ZynqMP/Versal bring-up; PG210 does not
+ * specify either.
+ */
+#define XXV_LINK_POLL_INTERVAL_US 10
+
+/* Number of XXV (10G/25G) MAC registers exposed via ethtool -d.
+ * Covers configuration and status registers per Xilinx PG210.
+ */
+static const u32 axienet_xxv_reg_dump_offsets[XXV_REGS_N] = {
+ XXV_GT_RESET_OFFSET,
+ XXV_RESET_OFFSET,
+ XXV_MODE_OFFSET,
+ XXV_TC_OFFSET,
+ XXV_RCW1_OFFSET,
+ XXV_JUM_OFFSET,
+ XXV_VL_LENGTH_OFFSET,
+ XXV_TICKREG_OFFSET,
+ XXV_CONFIG_REVISION_OFFSET,
+ XXV_CONFIG_1588_OFFSET,
+ XXV_CONFIG_TX_FLOW_CTRL1_OFFSET,
+ XXV_CONFIG_RX_FLOW_CTRL1_OFFSET,
+ XXV_CONFIG_RX_FLOW_CTRL2_OFFSET,
+ XXV_CONFIG_RSFEC_OFFSET,
+ XXV_CONFIG_FEC_OFFSET,
+ XXV_AN_CTL1_OFFSET,
+ XXV_AN_CTL2_OFFSET,
+ XXV_AN_ABILITY_OFFSET,
+ XXV_LT_CTL1_OFFSET,
+ XXV_SWITCH_CORE_SPEED_OFFSET,
+ XXV_CONFIG_1588_32BIT_OFFSET,
+ XXV_TX_CONFIG_1588_OFFSET,
+ XXV_RX_CONFIG_1588_OFFSET,
+ XXV_GT_CTRL_OFFSET,
+ XXV_STAT_TX_STATUS_OFFSET,
+ XXV_STAT_RX_STATUS_OFFSET,
+ XXV_STAT_STATUS_OFFSET,
+ XXV_STAT_RX_BLOCK_LOCK_OFFSET,
+ XXV_STAT_RX_RSFEC_STATUS_OFFSET,
+ XXV_STAT_RX_FEC_STATUS_OFFSET,
+ XXV_STAT_TX_RSFEC_STATUS_OFFSET,
+ XXV_STAT_TX_FLOW_CTRL1_OFFSET,
+ XXV_STAT_RX_FLOW_CTRL1_OFFSET,
+ XXV_STAT_AN_STS_OFFSET,
+ XXV_STAT_AN_ABILITY_OFFSET,
+ XXV_STAT_AN_LINK_CTL_OFFSET,
+ XXV_STAT_LT_STATUS1_OFFSET,
+ XXV_STAT_LT_STATUS2_OFFSET,
+ XXV_STAT_LT_STATUS3_OFFSET,
+ XXV_STAT_LT_STATUS4_OFFSET,
+ XXV_STAT_LT_COEFF0_OFFSET,
+ XXV_STAT_RX_VALID_CTRL_CODE_OFFSET,
+ XXV_STAT_CORE_SPEED_OFFSET,
+ XXV_STAT_TSN_OFFSET,
+ XXV_STAT_GT_OFFSET,
+ XXV_STAT_AN_LINK_CTL2_OFFSET,
+};
+
+/* Option table for setting up XXV Ethernet hardware options */
+static const struct axienet_option xxvenet_options[] = {
+ { /* Turn on FCS stripping on receive packets */
+ .opt = XAE_OPTION_FCS_STRIP,
+ .reg = XXV_RCW1_OFFSET,
+ .m_or = XXV_RCW1_FCS_MASK,
+ }, { /* Turn on FCS insertion on transmit packets */
+ .opt = XAE_OPTION_FCS_INSERT,
+ .reg = XXV_TC_OFFSET,
+ .m_or = XXV_TC_FCS_MASK,
+ }, { /* Enable transmitter */
+ .opt = XAE_OPTION_TXEN,
+ .reg = XXV_TC_OFFSET,
+ .m_or = XXV_TC_TX_MASK,
+ }, { /* Enable receiver */
+ .opt = XAE_OPTION_RXEN,
+ .reg = XXV_RCW1_OFFSET,
+ .m_or = XXV_RCW1_RX_MASK,
+ },
+ {}
+};
+
+static bool axienet_xxv_ip_has_gt_status(u32 ip_version)
+{
+ u8 minor = FIELD_GET(XXV_MIN_MASK, ip_version);
+ u8 maj = FIELD_GET(XXV_MAJ_MASK, ip_version);
+
+ if (maj == XXV_IP_VER_GT_MAJ_MIN)
+ return minor >= XXV_IP_VER_GT_MIN_MIN;
+ return maj > XXV_IP_VER_GT_MAJ_MIN;
+}
+
+/**
+ * axienet_xxv_probe_init - Initialize XXV-specific state at probe time
+ * @lp: Pointer to the axienet_local structure
+ */
+static void axienet_xxv_probe_init(struct axienet_local *lp)
+{
+ lp->xxv_ip_version = axienet_ior(lp, XXV_CONFIG_REVISION_OFFSET);
+}
+
+/**
+ * axienet_xxv_gt_reset - Pulse the XXV GT reset line
+ * @lp: Pointer to the axienet_local structure
+ */
+static void axienet_xxv_gt_reset(struct axienet_local *lp)
+{
+ u32 val;
+
+ val = axienet_ior(lp, XXV_GT_RESET_OFFSET);
+ val |= XXV_GT_RESET_MASK;
+ axienet_iow(lp, XXV_GT_RESET_OFFSET, val);
+ /* Give the GT time to enter reset so that axienet_xxv_poll_link_ready()
+ * cannot sample a reset-done left over from before the pulse. The delay
+ * is not a documented hold time (see timeout note above).
+ */
+ usleep_range(1000, 2000);
+ val = axienet_ior(lp, XXV_GT_RESET_OFFSET);
+ val &= ~XXV_GT_RESET_MASK;
+ axienet_iow(lp, XXV_GT_RESET_OFFSET, val);
+}
+
+/**
+ * axienet_xxv_poll_link_ready - Wait for XXV GT and PCS block lock
+ * @ndev: Pointer to the net_device structure
+ *
+ * Poll GT reset-done on IP v3.2+, then RX PCS block lock. GT reset-done
+ * depends only on the GT reference clock, so its timeout is treated as fatal.
+ * Block-lock additionally needs a live link partner, so its failure is logged
+ * but not propagated: an unplugged cable must not fail device bring-up.
+ *
+ * Return: 0 on success, or a negative error if GT reset-done times out.
+ */
+static int axienet_xxv_poll_link_ready(struct net_device *ndev)
+{
+ struct axienet_local *lp = netdev_priv(ndev);
+ u32 val;
+ int ret;
+
+ /* Confirm XXV Ethernet is up: the GT status register exists only
+ * on IP v3.2+, so wait there for GT reset-done, then poll until
+ * RX PCS block lock is asserted.
+ */
+ if (axienet_xxv_ip_has_gt_status(lp->xxv_ip_version)) {
+ ret = readl_poll_timeout(lp->regs + XXV_STAT_GT_OFFSET,
+ val,
+ (val & XXV_GT_RESET_DONE) == XXV_GT_RESET_DONE,
+ XXV_LINK_POLL_INTERVAL_US,
+ DELAY_OF_ONE_MILLISEC);
+ if (ret) {
+ netdev_err(ndev, "XXV MAC GT reset not complete! Cross-check the MAC ref clock configuration\n");
+ return ret;
+ }
+ }
+
+ ret = readl_poll_timeout(lp->regs + XXV_STAT_RX_BLOCK_LOCK_OFFSET,
+ val, (val & XXV_STAT_RX_BLOCK_LOCK_MASK),
+ XXV_LINK_POLL_INTERVAL_US,
+ DELAY_OF_ONE_MILLISEC);
+ if (ret)
+ netdev_dbg(ndev, "XXV RX block lock not yet asserted (no link partner?)\n");
+
+ return 0;
+}
+
+/**
+ * axienet_xxv_mac_init - XXV MAC-specific bring-up after the DMA reset
+ * @ndev: Pointer to the net_device structure
+ *
+ * Return: 0 on success or a negative error number otherwise.
+ */
+static int axienet_xxv_mac_init(struct net_device *ndev)
+{
+ struct axienet_local *lp = netdev_priv(ndev);
+ int ret;
+
+ ret = axienet_xxv_poll_link_ready(ndev);
+ if (ret)
+ return ret;
+
+ /* Bound the MAC's RX max packet length to the posted buffer size so an
+ * oversized frame is truncated to a single buffer instead of spilling
+ * across several DMA descriptors (PG210 CONFIGURATION_RX_MTU).
+ */
+ axienet_iow(lp, XXV_JUM_OFFSET,
+ FIELD_PREP(XXV_RX_MAX_LEN_MASK, lp->max_frm_size) |
+ XXV_RX_MIN_LEN_DEFAULT);
+
+ lp->options |= XAE_OPTION_FCS_STRIP;
+ lp->options |= XAE_OPTION_FCS_INSERT;
+
+ return 0;
+}
+
+static void axienet_xxv_phylink_set_capabilities(struct axienet_local *lp,
+ struct phylink_config *cfg)
+{
+ switch (lp->phy_mode) {
+ case PHY_INTERFACE_MODE_10GBASER:
+ cfg->mac_capabilities |= MAC_10000FD;
+ break;
+ case PHY_INTERFACE_MODE_25GBASER:
+ cfg->mac_capabilities |= MAC_25000FD;
+ break;
+ default:
+ /* The binding allows only the two rates above. Leave
+ * supported_interfaces empty rather than guessing a rate;
+ * phylink_create() then rejects the devicetree and probe
+ * fails.
+ */
+ dev_err(lp->dev, "unsupported phy-mode %s for XXV ethernet\n",
+ phy_modes(lp->phy_mode));
+ return;
+ }
+
+ __set_bit(lp->phy_mode, cfg->supported_interfaces);
+
+ /* The integrated PCS/PMA manages the link in-band and there is no
+ * phy-handle, so resolve to MLO_AN_INBAND.
+ */
+ cfg->default_an_inband = true;
+
+ /* Flow control is not wired up for XXV yet; do not advertise pause. */
+ cfg->mac_capabilities &= ~(MAC_SYM_PAUSE | MAC_ASYM_PAUSE);
+}
+
+static void axienet_xxv_pcs_get_state(struct phylink_pcs *pcs,
+ unsigned int neg_mode,
+ struct phylink_link_state *state)
+{
+ struct axienet_local *lp = pcs_to_axienet_local(pcs);
+
+ state->duplex = DUPLEX_FULL;
+ state->an_complete = !!(axienet_ior(lp, XXV_STAT_AN_STS_OFFSET) &
+ XXV_AN_COMPLETE_MASK);
+ state->link = axienet_ior(lp, XXV_STAT_RX_STATUS_OFFSET) &
+ XXV_STAT_RX_STATUS_MASK;
+
+ /* The core runs at the single rate phylink was configured for, so
+ * derive the speed from the interface rather than from STAT_CORE_SPEED,
+ * which PG210 provides only on the 64-bit AXI4-Stream builds.
+ */
+ if (state->interface == PHY_INTERFACE_MODE_10GBASER)
+ state->speed = SPEED_10000;
+ else
+ state->speed = SPEED_25000;
+}
+
+static int axienet_xxv_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
+ phy_interface_t interface,
+ const unsigned long *advertising,
+ bool permit_pause_to_mac)
+{
+ return 0;
+}
+
+static const struct phylink_pcs_ops axienet_xxv_pcs_ops = {
+ .pcs_get_state = axienet_xxv_pcs_get_state,
+ .pcs_config = axienet_xxv_pcs_config,
+};
+
+/**
+ * axienet_xxv_get_regs - Dump XXV MAC registers for ethtool
+ * @lp: Pointer to the axienet_local structure
+ * @data: Buffer for register values (zeroed and sized by the caller)
+ */
+static void axienet_xxv_get_regs(struct axienet_local *lp, u32 *data)
+{
+ bool has_gt_status = axienet_xxv_ip_has_gt_status(lp->xxv_ip_version);
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(axienet_xxv_reg_dump_offsets); i++) {
+ u32 off = axienet_xxv_reg_dump_offsets[i];
+
+ /* The GT registers only exist on the IP versions that report
+ * reset-done. Leave their slots zero on the others so the
+ * dump keeps a fixed layout.
+ */
+ if (!has_gt_status && (off == XXV_GT_CTRL_OFFSET ||
+ off == XXV_STAT_GT_OFFSET))
+ continue;
+
+ data[i] = axienet_ior(lp, off);
+ }
+}
+
+static int axienet_10g25g_clk_init(struct axienet_local *lp)
+{
+ struct device *dev = lp->dev;
+ struct clk *ref_clk;
+ int ret;
+
+ /* The register, RX and GT DRP clocks are mandatory for MAC+PCS
+ * operation, so fetch them as required.
+ */
+ lp->axi_clk = devm_clk_get_enabled(dev, "s_axi");
+ if (IS_ERR(lp->axi_clk))
+ return dev_err_probe(dev, PTR_ERR(lp->axi_clk),
+ "could not get AXI clock\n");
+
+ lp->misc_clks[0].id = "rx_core";
+ lp->misc_clks[1].id = "drp";
+
+ ret = devm_clk_bulk_get_enable(dev, XXV_NUM_MISC_CLOCKS,
+ lp->misc_clks);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "could not get/enable XXV clocks\n");
+
+ /* The GT reference clock is usually a fixed oscillator that Linux does
+ * not model, but enable it for boards that describe it as a gated
+ * provider. Without it the GT never reports reset-done.
+ */
+ ref_clk = devm_clk_get_optional_enabled(dev, "ref");
+ if (IS_ERR(ref_clk))
+ return dev_err_probe(dev, PTR_ERR(ref_clk),
+ "could not get/enable GT reference clock\n");
+
+ return 0;
+}
+
+const struct axienet_config axienet_10g25g_config = {
+ .jumbo = true,
+ .sw_padding = true,
+ .internal_pcs = true,
+ .regs_n = XXV_REGS_N,
+ .options = xxvenet_options,
+ .clk_init = axienet_10g25g_clk_init,
+ .probe_init = axienet_xxv_probe_init,
+ .gt_reset = axienet_xxv_gt_reset,
+ .mac_init = axienet_xxv_mac_init,
+ .get_regs = axienet_xxv_get_regs,
+ .phylink_set_caps = axienet_xxv_phylink_set_capabilities,
+ .pcs_ops = &axienet_xxv_pcs_ops,
+};
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_xxv.h b/drivers/net/ethernet/xilinx/xilinx_axienet_xxv.h
new file mode 100644
index 000000000000..6530344e40c7
--- /dev/null
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_xxv.h
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * XXV (10G/25G) support
+ *
+ * Copyright (C) 2026 Advanced Micro Devices, Inc.
+ */
+
+#ifndef XILINX_AXIENET_XXV_H
+#define XILINX_AXIENET_XXV_H
+
+#include <linux/bits.h>
+
+struct axienet_local;
+struct axienet_config;
+
+#define XXV_REGS_N 46
+
+/* RX core and GT DRP clocks, fetched through lp->misc_clks */
+#define XXV_NUM_MISC_CLOCKS 2
+
+/* XXV MAC Register Definitions */
+#define XXV_GT_RESET_OFFSET 0x00000000
+#define XXV_RESET_OFFSET 0x00000004
+#define XXV_MODE_OFFSET 0x00000008
+#define XXV_TC_OFFSET 0x0000000C
+#define XXV_RCW1_OFFSET 0x00000014
+#define XXV_JUM_OFFSET 0x00000018
+#define XXV_VL_LENGTH_OFFSET 0x0000001C
+#define XXV_TICKREG_OFFSET 0x00000020
+#define XXV_CONFIG_REVISION_OFFSET 0x00000024
+#define XXV_CONFIG_1588_OFFSET 0x00000038
+#define XXV_CONFIG_TX_FLOW_CTRL1_OFFSET 0x00000040
+#define XXV_CONFIG_RX_FLOW_CTRL1_OFFSET 0x00000094
+#define XXV_CONFIG_RX_FLOW_CTRL2_OFFSET 0x00000098
+#define XXV_CONFIG_RSFEC_OFFSET 0x000000D0
+#define XXV_CONFIG_FEC_OFFSET 0x000000D4
+#define XXV_AN_CTL1_OFFSET 0x000000E0
+#define XXV_AN_CTL2_OFFSET 0x000000E4
+#define XXV_AN_ABILITY_OFFSET 0x000000F8
+#define XXV_LT_CTL1_OFFSET 0x00000100
+#define XXV_SWITCH_CORE_SPEED_OFFSET 0x00000138
+#define XXV_CONFIG_1588_32BIT_OFFSET 0x0000013C
+#define XXV_TX_CONFIG_1588_OFFSET 0x00000140
+#define XXV_RX_CONFIG_1588_OFFSET 0x00000144
+#define XXV_GT_CTRL_OFFSET 0x00000154
+#define XXV_STAT_TX_STATUS_OFFSET 0x00000400
+#define XXV_STAT_RX_STATUS_OFFSET 0x00000404
+#define XXV_STAT_STATUS_OFFSET 0x00000408
+#define XXV_STAT_RX_BLOCK_LOCK_OFFSET 0x0000040C
+#define XXV_STAT_RX_RSFEC_STATUS_OFFSET 0x0000043C
+#define XXV_STAT_RX_FEC_STATUS_OFFSET 0x00000448
+#define XXV_STAT_TX_RSFEC_STATUS_OFFSET 0x0000044C
+#define XXV_STAT_TX_FLOW_CTRL1_OFFSET 0x00000450
+#define XXV_STAT_RX_FLOW_CTRL1_OFFSET 0x00000454
+#define XXV_STAT_AN_STS_OFFSET 0x00000458
+#define XXV_STAT_AN_ABILITY_OFFSET 0x0000045C
+#define XXV_STAT_AN_LINK_CTL_OFFSET 0x00000460
+#define XXV_STAT_LT_STATUS1_OFFSET 0x00000464
+#define XXV_STAT_LT_STATUS2_OFFSET 0x00000468
+#define XXV_STAT_LT_STATUS3_OFFSET 0x0000046C
+#define XXV_STAT_LT_STATUS4_OFFSET 0x00000470
+#define XXV_STAT_LT_COEFF0_OFFSET 0x00000474
+#define XXV_STAT_RX_VALID_CTRL_CODE_OFFSET 0x00000494
+#define XXV_STAT_CORE_SPEED_OFFSET 0x00000498
+#define XXV_STAT_TSN_OFFSET 0x0000049C
+#define XXV_STAT_GT_OFFSET 0x000004A0
+#define XXV_STAT_AN_LINK_CTL2_OFFSET 0x000009F0
+
+/* XXV MAC Register Mask Definitions */
+#define XXV_GT_RESET_MASK BIT(0)
+#define XXV_TC_TX_MASK BIT(0)
+#define XXV_RCW1_RX_MASK BIT(0)
+#define XXV_RCW1_FCS_MASK BIT(1)
+#define XXV_TC_FCS_MASK BIT(1)
+#define XXV_STAT_RX_BLOCK_LOCK_MASK BIT(0)
+#define XXV_GT_RESET_DONE (BIT(0) | BIT(1))
+#define XXV_MAJ_MASK GENMASK(7, 0)
+#define XXV_MIN_MASK GENMASK(15, 8)
+/* The GT control and status registers exist from IP v3.2 on (PG210). */
+#define XXV_IP_VER_GT_MAJ_MIN 3
+#define XXV_IP_VER_GT_MIN_MIN 2
+#define XXV_AN_COMPLETE_MASK BIT(2)
+#define XXV_STAT_RX_STATUS_MASK BIT(0)
+
+/* CONFIGURATION_RX_MTU (XXV_JUM_OFFSET) field layout (PG210): bits [30:16]
+ * hold ctl_rx_max_packet_len, bits [7:0] hold ctl_rx_min_packet_len.
+ */
+#define XXV_RX_MAX_LEN_MASK GENMASK(30, 16)
+#define XXV_RX_MIN_LEN_DEFAULT 64
+
+extern const struct axienet_config axienet_10g25g_config;
+
+#endif /* XILINX_AXIENET_XXV_H */
--
2.25.1
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH net-next v5 5/6] net: xilinx: axienet: Dispatch statistics through axienet_config ops
2026-11-14 18:29 [PATCH net-next v5 0/6] net: xilinx: axienet: Add 10G/25G (XXV) ethernet support Suraj Gupta
` (3 preceding siblings ...)
2026-11-14 18:29 ` [PATCH net-next v5 4/6] net: xilinx: axienet: Add 10G/25G (XXV) ethernet support Suraj Gupta
@ 2026-11-14 18:29 ` Suraj Gupta
2026-11-14 18:29 ` [PATCH net-next v5 6/6] net: xilinx: axienet: Add statistics support for XXV ethernet Suraj Gupta
5 siblings, 0 replies; 7+ messages in thread
From: Suraj Gupta @ 2026-11-14 18:29 UTC (permalink / raw)
To: Radhey Shyam Pandey, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: Michal Simek, Russell King, netdev, devicetree, linux-arm-kernel,
linux-kernel
The ndo_get_stats64() and ethtool statistics callbacks read the 1G MAC
hardware counters directly. Supporting a second MAC type this way would
require an "if (mactype == ...)" dispatch branch in each of the eight
statistics callbacks.
Add statistics operation function pointers to struct axienet_config and
route get_stats64, get_ethtool_stats, get_strings, get_sset_count,
get_pause_stats, get_eth_mac_stats, get_eth_ctrl_stats and
get_rmon_stats through them. Factor the existing 1G counter reads into
axienet_1g_*() helpers and populate axienet_1g_config with them.
A NULL op means the MAC does not expose that statistics group, so a MAC
without control-frame counters simply leaves get_eth_ctrl_stats unset
instead of open-coding the special case in the callback.
No functional change intended.
Signed-off-by: Suraj Gupta <suraj.gupta2@amd.com>
---
drivers/net/ethernet/xilinx/xilinx_axienet.h | 23 +++
.../net/ethernet/xilinx/xilinx_axienet_main.c | 171 +++++++++++++-----
2 files changed, 146 insertions(+), 48 deletions(-)
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h
index 56570b49574e..7468fed1416e 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet.h
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h
@@ -666,6 +666,15 @@ struct axienet_local {
* is set, as the PCS is registered without a further NULL check
* @stats_update: Callback to latch/accumulate the periodic MAC counters, or
* NULL if the MAC has no periodic statistics counters
+ * @get_stats64: Callback to read MAC counters into rtnl_link_stats64
+ * @get_ethtool_stats: Callback to read MAC counters for ethtool -S
+ * @get_strings: Callback to fill the ethtool -S statistic name strings
+ * @get_sset_count: Callback returning the number of ethtool -S statistics
+ * @get_pause_stats: Callback to read MAC pause-frame counters
+ * @get_eth_mac_stats: Callback to read IEEE 802.3 MAC counters
+ * @get_eth_ctrl_stats: Callback to read MAC-control frame counters, or NULL
+ * if the MAC exposes no control-frame counters
+ * @get_rmon_stats: Callback to read RMON counters and histogram ranges
*/
struct axienet_config {
bool mdio;
@@ -691,6 +700,20 @@ struct axienet_config {
struct phylink_config *config);
const struct phylink_pcs_ops *pcs_ops;
void (*stats_update)(struct axienet_local *lp);
+ void (*get_stats64)(struct axienet_local *lp,
+ struct rtnl_link_stats64 *stats);
+ void (*get_ethtool_stats)(struct axienet_local *lp, u64 *data);
+ void (*get_strings)(u8 *data);
+ int (*get_sset_count)(void);
+ void (*get_pause_stats)(struct axienet_local *lp,
+ struct ethtool_pause_stats *pause_stats);
+ void (*get_eth_mac_stats)(struct axienet_local *lp,
+ struct ethtool_eth_mac_stats *mac_stats);
+ void (*get_eth_ctrl_stats)(struct axienet_local *lp,
+ struct ethtool_eth_ctrl_stats *ctrl_stats);
+ void (*get_rmon_stats)(struct axienet_local *lp,
+ struct ethtool_rmon_stats *rmon_stats,
+ const struct ethtool_rmon_hist_range **ranges);
};
static inline struct axienet_local *pcs_to_axienet_local(struct phylink_pcs *pcs)
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
index a37aad4432ce..329b6adf2475 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
@@ -1992,29 +1992,11 @@ static int axienet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
return phylink_mii_ioctl(lp->phylink, rq, cmd);
}
-static void
-axienet_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
+static void axienet_1g_get_stats64(struct axienet_local *lp,
+ struct rtnl_link_stats64 *stats)
{
- struct axienet_local *lp = netdev_priv(dev);
unsigned int start;
- netdev_stats_to_stats64(stats, &dev->stats);
-
- do {
- start = u64_stats_fetch_begin(&lp->rx_stat_sync);
- stats->rx_packets = u64_stats_read(&lp->rx_packets);
- stats->rx_bytes = u64_stats_read(&lp->rx_bytes);
- } while (u64_stats_fetch_retry(&lp->rx_stat_sync, start));
-
- do {
- start = u64_stats_fetch_begin(&lp->tx_stat_sync);
- stats->tx_packets = u64_stats_read(&lp->tx_packets);
- stats->tx_bytes = u64_stats_read(&lp->tx_bytes);
- } while (u64_stats_fetch_retry(&lp->tx_stat_sync, start));
-
- if (!(lp->features & XAE_FEATURE_STATS))
- return;
-
do {
start = read_seqcount_begin(&lp->hw_stats_seqcount);
stats->rx_length_errors =
@@ -2042,6 +2024,33 @@ axienet_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
} while (read_seqcount_retry(&lp->hw_stats_seqcount, start));
}
+static void
+axienet_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
+{
+ struct axienet_local *lp = netdev_priv(dev);
+ unsigned int start;
+
+ netdev_stats_to_stats64(stats, &dev->stats);
+
+ do {
+ start = u64_stats_fetch_begin(&lp->rx_stat_sync);
+ stats->rx_packets = u64_stats_read(&lp->rx_packets);
+ stats->rx_bytes = u64_stats_read(&lp->rx_bytes);
+ } while (u64_stats_fetch_retry(&lp->rx_stat_sync, start));
+
+ do {
+ start = u64_stats_fetch_begin(&lp->tx_stat_sync);
+ stats->tx_packets = u64_stats_read(&lp->tx_packets);
+ stats->tx_bytes = u64_stats_read(&lp->tx_bytes);
+ } while (u64_stats_fetch_retry(&lp->tx_stat_sync, start));
+
+ if (!(lp->features & XAE_FEATURE_STATS))
+ return;
+
+ if (lp->axienet_config->get_stats64)
+ lp->axienet_config->get_stats64(lp, stats);
+}
+
static const struct net_device_ops axienet_netdev_ops = {
.ndo_open = axienet_open,
.ndo_stop = axienet_stop,
@@ -2474,11 +2483,8 @@ static int axienet_ethtools_nway_reset(struct net_device *dev)
return phylink_ethtool_nway_reset(lp->phylink);
}
-static void axienet_ethtools_get_ethtool_stats(struct net_device *dev,
- struct ethtool_stats *stats,
- u64 *data)
+static void axienet_1g_get_ethtool_stats(struct axienet_local *lp, u64 *data)
{
- struct axienet_local *lp = netdev_priv(dev);
unsigned int start;
do {
@@ -2495,6 +2501,17 @@ static void axienet_ethtools_get_ethtool_stats(struct net_device *dev,
} while (read_seqcount_retry(&lp->hw_stats_seqcount, start));
}
+static void axienet_ethtools_get_ethtool_stats(struct net_device *dev,
+ struct ethtool_stats *stats,
+ u64 *data)
+{
+ struct axienet_local *lp = netdev_priv(dev);
+
+ if (lp->features & XAE_FEATURE_STATS &&
+ lp->axienet_config->get_ethtool_stats)
+ lp->axienet_config->get_ethtool_stats(lp, data);
+}
+
static const char axienet_ethtool_stats_strings[][ETH_GSTRING_LEN] = {
"Received bytes",
"Transmitted bytes",
@@ -2507,12 +2524,26 @@ static const char axienet_ethtool_stats_strings[][ETH_GSTRING_LEN] = {
"User Defined Counter 2",
};
+static void axienet_1g_get_strings(u8 *data)
+{
+ memcpy(data, axienet_ethtool_stats_strings,
+ sizeof(axienet_ethtool_stats_strings));
+}
+
+static int axienet_1g_get_sset_count(void)
+{
+ return ARRAY_SIZE(axienet_ethtool_stats_strings);
+}
+
static void axienet_ethtools_get_strings(struct net_device *dev, u32 stringset, u8 *data)
{
+ struct axienet_local *lp = netdev_priv(dev);
+
switch (stringset) {
case ETH_SS_STATS:
- memcpy(data, axienet_ethtool_stats_strings,
- sizeof(axienet_ethtool_stats_strings));
+ if (lp->features & XAE_FEATURE_STATS &&
+ lp->axienet_config->get_strings)
+ lp->axienet_config->get_strings(data);
break;
}
}
@@ -2523,24 +2554,20 @@ static int axienet_ethtools_get_sset_count(struct net_device *dev, int sset)
switch (sset) {
case ETH_SS_STATS:
- if (lp->features & XAE_FEATURE_STATS)
- return ARRAY_SIZE(axienet_ethtool_stats_strings);
+ if (lp->features & XAE_FEATURE_STATS &&
+ lp->axienet_config->get_sset_count)
+ return lp->axienet_config->get_sset_count();
fallthrough;
default:
return -EOPNOTSUPP;
}
}
-static void
-axienet_ethtools_get_pause_stats(struct net_device *dev,
- struct ethtool_pause_stats *pause_stats)
+static void axienet_1g_get_pause_stats(struct axienet_local *lp,
+ struct ethtool_pause_stats *pause_stats)
{
- struct axienet_local *lp = netdev_priv(dev);
unsigned int start;
- if (!(lp->features & XAE_FEATURE_STATS))
- return;
-
do {
start = read_seqcount_begin(&lp->hw_stats_seqcount);
pause_stats->tx_pause_frames =
@@ -2551,15 +2578,23 @@ axienet_ethtools_get_pause_stats(struct net_device *dev,
}
static void
-axienet_ethtool_get_eth_mac_stats(struct net_device *dev,
- struct ethtool_eth_mac_stats *mac_stats)
+axienet_ethtools_get_pause_stats(struct net_device *dev,
+ struct ethtool_pause_stats *pause_stats)
{
struct axienet_local *lp = netdev_priv(dev);
- unsigned int start;
if (!(lp->features & XAE_FEATURE_STATS))
return;
+ if (lp->axienet_config->get_pause_stats)
+ lp->axienet_config->get_pause_stats(lp, pause_stats);
+}
+
+static void axienet_1g_get_eth_mac_stats(struct axienet_local *lp,
+ struct ethtool_eth_mac_stats *mac_stats)
+{
+ unsigned int start;
+
do {
start = read_seqcount_begin(&lp->hw_stats_seqcount);
mac_stats->FramesTransmittedOK =
@@ -2596,15 +2631,24 @@ axienet_ethtool_get_eth_mac_stats(struct net_device *dev,
}
static void
-axienet_ethtool_get_eth_ctrl_stats(struct net_device *dev,
- struct ethtool_eth_ctrl_stats *ctrl_stats)
+axienet_ethtool_get_eth_mac_stats(struct net_device *dev,
+ struct ethtool_eth_mac_stats *mac_stats)
{
struct axienet_local *lp = netdev_priv(dev);
- unsigned int start;
if (!(lp->features & XAE_FEATURE_STATS))
return;
+ if (lp->axienet_config->get_eth_mac_stats)
+ lp->axienet_config->get_eth_mac_stats(lp, mac_stats);
+}
+
+static void
+axienet_1g_get_eth_ctrl_stats(struct axienet_local *lp,
+ struct ethtool_eth_ctrl_stats *ctrl_stats)
+{
+ unsigned int start;
+
do {
start = read_seqcount_begin(&lp->hw_stats_seqcount);
ctrl_stats->MACControlFramesTransmitted =
@@ -2616,6 +2660,19 @@ axienet_ethtool_get_eth_ctrl_stats(struct net_device *dev,
} while (read_seqcount_retry(&lp->hw_stats_seqcount, start));
}
+static void
+axienet_ethtool_get_eth_ctrl_stats(struct net_device *dev,
+ struct ethtool_eth_ctrl_stats *ctrl_stats)
+{
+ struct axienet_local *lp = netdev_priv(dev);
+
+ if (!(lp->features & XAE_FEATURE_STATS))
+ return;
+
+ if (lp->axienet_config->get_eth_ctrl_stats)
+ lp->axienet_config->get_eth_ctrl_stats(lp, ctrl_stats);
+}
+
static const struct ethtool_rmon_hist_range axienet_rmon_ranges[] = {
{ 64, 64 },
{ 65, 127 },
@@ -2628,16 +2685,12 @@ static const struct ethtool_rmon_hist_range axienet_rmon_ranges[] = {
};
static void
-axienet_ethtool_get_rmon_stats(struct net_device *dev,
- struct ethtool_rmon_stats *rmon_stats,
- const struct ethtool_rmon_hist_range **ranges)
+axienet_1g_get_rmon_stats(struct axienet_local *lp,
+ struct ethtool_rmon_stats *rmon_stats,
+ const struct ethtool_rmon_hist_range **ranges)
{
- struct axienet_local *lp = netdev_priv(dev);
unsigned int start;
- if (!(lp->features & XAE_FEATURE_STATS))
- return;
-
do {
start = read_seqcount_begin(&lp->hw_stats_seqcount);
rmon_stats->undersize_pkts =
@@ -2681,6 +2734,20 @@ axienet_ethtool_get_rmon_stats(struct net_device *dev,
*ranges = axienet_rmon_ranges;
}
+static void
+axienet_ethtool_get_rmon_stats(struct net_device *dev,
+ struct ethtool_rmon_stats *rmon_stats,
+ const struct ethtool_rmon_hist_range **ranges)
+{
+ struct axienet_local *lp = netdev_priv(dev);
+
+ if (!(lp->features & XAE_FEATURE_STATS))
+ return;
+
+ if (lp->axienet_config->get_rmon_stats)
+ lp->axienet_config->get_rmon_stats(lp, rmon_stats, ranges);
+}
+
static const struct ethtool_ops axienet_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_MAX_FRAMES |
ETHTOOL_COALESCE_USECS |
@@ -3010,6 +3077,14 @@ static const struct axienet_config axienet_1g_config = {
.phylink_set_caps = axienet_1g_phylink_set_caps,
.pcs_ops = &axienet_pcs_ops,
.stats_update = axienet_1g_stats_update,
+ .get_stats64 = axienet_1g_get_stats64,
+ .get_ethtool_stats = axienet_1g_get_ethtool_stats,
+ .get_strings = axienet_1g_get_strings,
+ .get_sset_count = axienet_1g_get_sset_count,
+ .get_pause_stats = axienet_1g_get_pause_stats,
+ .get_eth_mac_stats = axienet_1g_get_eth_mac_stats,
+ .get_eth_ctrl_stats = axienet_1g_get_eth_ctrl_stats,
+ .get_rmon_stats = axienet_1g_get_rmon_stats,
};
/* Match table for of_platform binding */
--
2.25.1
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH net-next v5 6/6] net: xilinx: axienet: Add statistics support for XXV ethernet
2026-11-14 18:29 [PATCH net-next v5 0/6] net: xilinx: axienet: Add 10G/25G (XXV) ethernet support Suraj Gupta
` (4 preceding siblings ...)
2026-11-14 18:29 ` [PATCH net-next v5 5/6] net: xilinx: axienet: Dispatch statistics through axienet_config ops Suraj Gupta
@ 2026-11-14 18:29 ` Suraj Gupta
5 siblings, 0 replies; 7+ messages in thread
From: Suraj Gupta @ 2026-11-14 18:29 UTC (permalink / raw)
To: Radhey Shyam Pandey, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: Michal Simek, Russell King, netdev, devicetree, linux-arm-kernel,
linux-kernel
The XXV ethernet exposes a dedicated statistics block whose counters are
clear-on-tick (PG210): writing TICK_REG latches the internal accumulators
into the readable registers and clears them, so each post-TICK read yields
the count for the interval since the previous TICK. Software accumulates
those intervals, so no last-counter delta (as used by the 1G MAC) is
needed. The readable registers are not reset along with the accumulators
and hold unknown data until the first TICK, so axienet_xxv_mac_init()
drains them once the GT is ready and before RX/TX are enabled.
Feed both ndo_get_stats64() and the ethtool statistics (-S, pause, MAC and
RMON) from this accumulator path through the axienet_config statistics ops.
MAC control statistics stay unsupported as the hardware exposes no matching
counters.
The counters are an optional part of the IP, selected by the "Include
Statistics Counters" build option. Unlike the 1G MAC, which advertises
XAE_ABILITY_STATS, the XXV register map does not report whether they were
included, so gate XAE_FEATURE_STATS on the "xlnx,has-statistics-counters"
devicetree property instead. Gate the TICK_REG drain and the periodic
statistics work on that same flag, so that a design built without the
block is never touched in the statistics register range. The work
previously keyed off the presence of the stats_update hook alone, which
a config installs unconditionally.
Readers copy the software accumulator and do not tick. The periodic
statistics work latches it, and axienet_stop() takes a final snapshot.
ethtool -S and the pause, MAC and RMON callbacks use that same copy, so
they can lag the work interval; the work is deferrable, so on an idle CPU
the lag can exceed it. ndo_get_stats64() may run atomically and cannot tick
these clear-on-tick counters itself. PG210 defines the counters as 48 bits
(LSB [31:0], MSB [15:0] holds [47:32]), saturating at 0xFFFFFFFFFFFF, so
the existing work interval cannot wrap them at 25 Gbit/s. Mask the MSB
register to those 16 bits when a counter is read, so that bits outside the
counter cannot be accumulated into the software base.
The final statistics snapshot added to axienet_stop() is gated on
XAE_FEATURE_STATS, so it also runs on the 1G MAC when that MAC implements
the statistics block; it is therefore not strictly XXV-only.
Signed-off-by: Suraj Gupta <suraj.gupta2@amd.com>
---
drivers/net/ethernet/xilinx/xilinx_axienet.h | 6 +-
.../net/ethernet/xilinx/xilinx_axienet_main.c | 36 ++-
.../net/ethernet/xilinx/xilinx_axienet_xxv.c | 282 ++++++++++++++++++
.../net/ethernet/xilinx/xilinx_axienet_xxv.h | 58 ++++
4 files changed, 378 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h
index 7468fed1416e..10cb73bc460d 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet.h
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h
@@ -520,7 +520,7 @@ struct skbuf_dma_descriptor {
* @reset_in_progress: Set while we are performing a reset and statistics
* counters may be invalid
* @hw_stats_seqcount: Sequence counter for @hw_stat_base, @hw_last_counter,
- * and @reset_in_progress.
+ * @reset_in_progress and, on XXV, @xxv_stat_base.
* @stats_lock: Lock for @hw_stats_seqcount
* @stats_work: Work for reading the hardware statistics counters often enough
* to catch overflows.
@@ -551,6 +551,9 @@ struct skbuf_dma_descriptor {
* @rx_ring_tail: RX skb ring buffer tail index.
* @rx_submit_lock: Protects RX ring resubmission vs teardown in dmaengine path.
* @xxv_ip_version: XXV IP version.
+ * @xxv_stat_base: Software accumulators for the XXV clear-on-tick counters.
+ * Serialized by @stats_lock and published under
+ * @hw_stats_seqcount.
* @axienet_config: MAC-type specific configuration and operations.
*/
struct axienet_local {
@@ -605,6 +608,7 @@ struct axienet_local {
u64 hw_stat_base[STAT_COUNT];
u32 hw_last_counter[STAT_COUNT];
+ u64 xxv_stat_base[XXV_STAT_COUNT];
seqcount_mutex_t hw_stats_seqcount;
struct mutex stats_lock;
struct delayed_work stats_work;
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
index 329b6adf2475..c2ee2cc17e4e 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
@@ -637,13 +637,23 @@ static void axienet_refresh_stats(struct work_struct *work)
struct axienet_local *lp = container_of(work, struct axienet_local,
stats_work.work);
- if (lp->axienet_config->stats_update) {
+ /* Gate on XAE_FEATURE_STATS as axienet_stop() does: a config installs
+ * the hook unconditionally, but the counters themselves are optional,
+ * and on XXV the registers are absent when they were not built in.
+ */
+ if (lp->features & XAE_FEATURE_STATS && lp->axienet_config->stats_update) {
mutex_lock(&lp->stats_lock);
lp->axienet_config->stats_update(lp);
mutex_unlock(&lp->stats_lock);
}
- /* Just less than 2^32 bytes at 2.5 GBit/s */
+ /* Re-arm the poll before any counter can overflow between updates: the
+ * 1G byte counter reaches just under 2^32 in this interval at 2.5 Gbit/s.
+ * The XXV counters are 48 bits (PG210): the LSB register holds [31:0] and
+ * MSB[15:0] holds [47:32]. They saturate at 0xFFFFFFFFFFFF and clear on
+ * tick, so this interval cannot wrap them at 25 Gbit/s. This work is
+ * what latches them between queries.
+ */
schedule_delayed_work(&lp->stats_work, 13 * HZ);
}
@@ -652,7 +662,16 @@ static int __axienet_device_reset(struct axienet_local *lp)
u32 value;
int ret;
- /* Save statistics counters in case they will be reset */
+ /* Save statistics counters in case they will be reset.
+ *
+ * The XAE_FEATURE_STATS blocks in this function read the TEMAC
+ * statistics registers at XAE_STATS_OFFSET, i.e. they assume the 1G
+ * counter layout. This is safe because __axienet_device_reset() is
+ * only reached on the non-dmaengine path, which the
+ * non_dmaengine capability restricts to the 1G MAC. MACs that use their
+ * own statistics mechanism (e.g. XXV, which is dmaengine-only) never
+ * execute this path.
+ */
mutex_lock(&lp->stats_lock);
if (lp->features & XAE_FEATURE_STATS)
axienet_stats_update(lp, true);
@@ -1880,6 +1899,17 @@ static int axienet_stop(struct net_device *ndev)
axienet_setoptions(ndev, lp->options &
~(XAE_OPTION_TXEN | XAE_OPTION_RXEN));
+ /* Final snapshot after RX/TX is disabled, so no frames are missed.
+ * Gate on XAE_FEATURE_STATS rather than the stats_update hook: a config
+ * always installs the hook, but the counters are optional in both MACs,
+ * so only probe_init() knows whether they are there.
+ */
+ if (lp->features & XAE_FEATURE_STATS && lp->axienet_config->stats_update) {
+ mutex_lock(&lp->stats_lock);
+ lp->axienet_config->stats_update(lp);
+ mutex_unlock(&lp->stats_lock);
+ }
+
if (!lp->use_dmaengine) {
axienet_dma_stop(lp);
cancel_work_sync(&lp->dma_err_task);
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_xxv.c b/drivers/net/ethernet/xilinx/xilinx_axienet_xxv.c
index 3c204dbbf6ae..83782bfbce4c 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_xxv.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_xxv.c
@@ -8,9 +8,12 @@
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/ethtool.h>
#include <linux/iopoll.h>
#include <linux/netdevice.h>
#include <linux/phylink.h>
+#include <linux/property.h>
+#include <linux/string.h>
#include "xilinx_axienet.h"
#include "xilinx_axienet_xxv.h"
@@ -96,6 +99,66 @@ static const struct axienet_option xxvenet_options[] = {
{}
};
+/* PG210 statistics-block offsets indexed by enum xxv_stat. Each entry is
+ * the 32-bit LSB register address; the corresponding MSB is at +4.
+ */
+static const u32 axienet_xxv_stat_offsets[XXV_STAT_COUNT] = {
+ [XXV_STAT_RX_TOTAL_PACKETS] = XXV_STAT_RX_TOTAL_PACKETS_OFFSET,
+ [XXV_STAT_RX_TOTAL_GOOD_PACKETS] = XXV_STAT_RX_TOTAL_GOOD_PACKETS_OFFSET,
+ [XXV_STAT_RX_TOTAL_BYTES] = XXV_STAT_RX_TOTAL_BYTES_OFFSET,
+ [XXV_STAT_RX_TOTAL_GOOD_BYTES] = XXV_STAT_RX_TOTAL_GOOD_BYTES_OFFSET,
+ [XXV_STAT_RX_PACKET_BAD_FCS] = XXV_STAT_RX_PACKET_BAD_FCS_OFFSET,
+ [XXV_STAT_RX_INRANGEERR] = XXV_STAT_RX_INRANGEERR_OFFSET,
+ [XXV_STAT_RX_MULTICAST] = XXV_STAT_RX_MULTICAST_OFFSET,
+ [XXV_STAT_RX_BROADCAST] = XXV_STAT_RX_BROADCAST_OFFSET,
+ [XXV_STAT_RX_UNDERSIZE] = XXV_STAT_RX_UNDERSIZE_OFFSET,
+ [XXV_STAT_RX_FRAGMENT] = XXV_STAT_RX_FRAGMENT_OFFSET,
+ [XXV_STAT_RX_JABBER] = XXV_STAT_RX_JABBER_OFFSET,
+ [XXV_STAT_RX_OVERSIZE] = XXV_STAT_RX_OVERSIZE_OFFSET,
+ [XXV_STAT_RX_PAUSE] = XXV_STAT_RX_PAUSE_OFFSET,
+ [XXV_STAT_TX_TOTAL_PACKETS] = XXV_STAT_TX_TOTAL_PACKETS_OFFSET,
+ [XXV_STAT_TX_TOTAL_GOOD_PACKETS] = XXV_STAT_TX_TOTAL_GOOD_PACKETS_OFFSET,
+ [XXV_STAT_TX_TOTAL_BYTES] = XXV_STAT_TX_TOTAL_BYTES_OFFSET,
+ [XXV_STAT_TX_TOTAL_GOOD_BYTES] = XXV_STAT_TX_TOTAL_GOOD_BYTES_OFFSET,
+ [XXV_STAT_TX_BAD_FCS] = XXV_STAT_TX_BAD_FCS_OFFSET,
+ [XXV_STAT_TX_FRAME_ERROR] = XXV_STAT_TX_FRAME_ERROR_OFFSET,
+ [XXV_STAT_TX_MULTICAST] = XXV_STAT_TX_MULTICAST_OFFSET,
+ [XXV_STAT_TX_BROADCAST] = XXV_STAT_TX_BROADCAST_OFFSET,
+ [XXV_STAT_TX_PAUSE] = XXV_STAT_TX_PAUSE_OFFSET,
+};
+
+/* XXV (PG210) counters exposed via ethtool -S. Only counters that have no
+ * standard uAPI are listed here; the totals include errored frames, so they
+ * are distinct from the good-frame counts in rtnl_link_stats64 and the
+ * IEEE MAC statistics. Every other XXV counter is reported through the
+ * dedicated stats64, pause, MAC and RMON callbacks instead.
+ */
+static const enum xxv_stat axienet_xxv_priv_stats[] = {
+ XXV_STAT_RX_TOTAL_PACKETS,
+ XXV_STAT_RX_TOTAL_BYTES,
+ XXV_STAT_TX_TOTAL_PACKETS,
+ XXV_STAT_TX_TOTAL_BYTES,
+};
+
+static const char axienet_xxv_ethtool_stats_strings[][ETH_GSTRING_LEN] = {
+ "RX Total Packets",
+ "RX Total Bytes",
+ "TX Total Packets",
+ "TX Total Bytes",
+};
+
+static u64 axienet_xxv_read_counter(struct axienet_local *lp, enum xxv_stat stat)
+{
+ u32 off = axienet_xxv_stat_offsets[stat];
+ u32 lsb, msb;
+
+ lsb = axienet_ior(lp, off);
+ msb = axienet_ior(lp, off + XXV_STAT_MSB_OFFSET);
+
+ /* MSB[31:16] is not part of the 48-bit counter. */
+ return ((u64)FIELD_GET(XXV_STAT_MSB_MASK, msb) << 32) | lsb;
+}
+
static bool axienet_xxv_ip_has_gt_status(u32 ip_version)
{
u8 minor = FIELD_GET(XXV_MIN_MASK, ip_version);
@@ -113,6 +176,13 @@ static bool axienet_xxv_ip_has_gt_status(u32 ip_version)
static void axienet_xxv_probe_init(struct axienet_local *lp)
{
lp->xxv_ip_version = axienet_ior(lp, XXV_CONFIG_REVISION_OFFSET);
+
+ /* The counters are an optional part of the IP (PG210 "Include
+ * Statistics Counters") and nothing in the register map reports
+ * whether they were built in, so trust the devicetree.
+ */
+ if (device_property_read_bool(lp->dev, "xlnx,has-statistics-counters"))
+ lp->features |= XAE_FEATURE_STATS;
}
/**
@@ -202,6 +272,12 @@ static int axienet_xxv_mac_init(struct net_device *ndev)
FIELD_PREP(XXV_RX_MAX_LEN_MASK, lp->max_frm_size) |
XXV_RX_MIN_LEN_DEFAULT);
+ /* Drain pre-open counts now that GT reset-done is complete and RX/TX
+ * are still disabled, so statistics start from zero.
+ */
+ if (lp->features & XAE_FEATURE_STATS)
+ axienet_iow(lp, XXV_TICKREG_OFFSET, XXV_TICKREG_STATEN_MASK);
+
lp->options |= XAE_OPTION_FCS_STRIP;
lp->options |= XAE_OPTION_FCS_INSERT;
@@ -335,6 +411,204 @@ static int axienet_10g25g_clk_init(struct axienet_local *lp)
return 0;
}
+/**
+ * axienet_xxv_stats_update - Latch and accumulate XXV hardware counters
+ * @lp: Pointer to the axienet_local structure
+ *
+ * The XXV statistics counters are clear-on-tick (PG210): writing TICK_REG
+ * latches the internal accumulators into the readable STAT_*_LSB/MSB registers
+ * and clears the internal accumulators. Each post-TICK read therefore returns
+ * the count for the interval since the previous TICK, so software accumulates
+ * those intervals into lp->xxv_stat_base. A last-counter delta (as used by the
+ * free-running 1G MAC path) is not needed.
+ *
+ * Readers copy @xxv_stat_base and do not tick. ndo_get_stats64() may run in
+ * atomic context, so it cannot take @stats_lock and tick itself. The periodic
+ * statistics work, and the final snapshot in axienet_stop(), are the latches.
+ */
+static void axienet_xxv_stats_update(struct axienet_local *lp)
+{
+ u64 counter[XXV_STAT_COUNT];
+ enum xxv_stat stat;
+
+ /* Latch the clear-on-tick snapshot and read the counters outside the
+ * seqcount write section, so only the accumulator updates run inside it
+ * and the reader retry window stays short.
+ */
+ axienet_iow(lp, XXV_TICKREG_OFFSET, XXV_TICKREG_STATEN_MASK);
+ for (stat = 0; stat < XXV_STAT_COUNT; stat++)
+ counter[stat] = axienet_xxv_read_counter(lp, stat);
+
+ write_seqcount_begin(&lp->hw_stats_seqcount);
+ for (stat = 0; stat < XXV_STAT_COUNT; stat++)
+ lp->xxv_stat_base[stat] += counter[stat];
+ write_seqcount_end(&lp->hw_stats_seqcount);
+}
+
+/**
+ * axienet_xxv_get_stats64 - Fill rtnl_link_stats64 from XXV counters
+ * @lp: Pointer to the axienet_local structure
+ * @stats: Output rtnl_link_stats64 structure to populate
+ */
+static void axienet_xxv_get_stats64(struct axienet_local *lp,
+ struct rtnl_link_stats64 *stats)
+{
+ unsigned int start;
+ u64 tx_hw_errors;
+
+ do {
+ start = read_seqcount_begin(&lp->hw_stats_seqcount);
+ stats->rx_crc_errors =
+ lp->xxv_stat_base[XXV_STAT_RX_PACKET_BAD_FCS];
+ /* Both in-range length errors and frame-too-long (oversize)
+ * frames are IEEE 802.3 length errors, so fold them together
+ * into rx_length_errors; rx_over_errors is reserved for receiver
+ * FIFO/ring overflow, which the XXV MAC does not expose.
+ */
+ stats->rx_length_errors =
+ lp->xxv_stat_base[XXV_STAT_RX_INRANGEERR] +
+ lp->xxv_stat_base[XXV_STAT_RX_OVERSIZE];
+ stats->rx_errors = lp->xxv_stat_base[XXV_STAT_RX_UNDERSIZE] +
+ lp->xxv_stat_base[XXV_STAT_RX_FRAGMENT] +
+ lp->xxv_stat_base[XXV_STAT_RX_JABBER] +
+ stats->rx_crc_errors +
+ stats->rx_length_errors;
+ stats->multicast = lp->xxv_stat_base[XXV_STAT_RX_MULTICAST];
+ tx_hw_errors = lp->xxv_stat_base[XXV_STAT_TX_BAD_FCS] +
+ lp->xxv_stat_base[XXV_STAT_TX_FRAME_ERROR];
+ } while (read_seqcount_retry(&lp->hw_stats_seqcount, start));
+
+ /* Fold the HW TX error count into the software tx_errors seeded from
+ * dev->stats by the caller. The XXV xmit path bumps dev->stats.tx_errors
+ * for frames dropped on pad failure before they reach the MAC, while the
+ * HW counters only cover frames the MAC actually transmitted; the two are
+ * disjoint. This runs once, outside the seqcount retry loop, so a racing
+ * stats update cannot make the accumulation re-add on retry.
+ */
+ stats->tx_errors += tx_hw_errors;
+}
+
+/**
+ * axienet_xxv_get_ethtool_stats - Copy XXV counters for ethtool -S
+ * @lp: Pointer to the axienet_local structure
+ * @data: Output buffer sized for ARRAY_SIZE(axienet_xxv_priv_stats) u64 values
+ */
+static void axienet_xxv_get_ethtool_stats(struct axienet_local *lp, u64 *data)
+{
+ unsigned int i, start;
+
+ do {
+ start = read_seqcount_begin(&lp->hw_stats_seqcount);
+ for (i = 0; i < ARRAY_SIZE(axienet_xxv_priv_stats); i++)
+ data[i] = lp->xxv_stat_base[axienet_xxv_priv_stats[i]];
+ } while (read_seqcount_retry(&lp->hw_stats_seqcount, start));
+}
+
+/**
+ * axienet_xxv_get_strings - Copy XXV ethtool statistics name strings
+ * @data: Output buffer for the statistics name strings
+ */
+static void axienet_xxv_get_strings(u8 *data)
+{
+ memcpy(data, axienet_xxv_ethtool_stats_strings,
+ sizeof(axienet_xxv_ethtool_stats_strings));
+}
+
+/**
+ * axienet_xxv_get_sset_count - Number of XXV ethtool statistics
+ *
+ * Return: Count of XXV hardware statistics reported via ethtool -S.
+ */
+static int axienet_xxv_get_sset_count(void)
+{
+ return ARRAY_SIZE(axienet_xxv_ethtool_stats_strings);
+}
+
+/**
+ * axienet_xxv_get_pause_stats - Fill ethtool pause frame statistics
+ * @lp: Pointer to the axienet_local structure
+ * @pause_stats: Output ethtool_pause_stats structure to populate
+ */
+static void axienet_xxv_get_pause_stats(struct axienet_local *lp,
+ struct ethtool_pause_stats *pause_stats)
+{
+ unsigned int start;
+
+ do {
+ start = read_seqcount_begin(&lp->hw_stats_seqcount);
+ pause_stats->tx_pause_frames =
+ lp->xxv_stat_base[XXV_STAT_TX_PAUSE];
+ pause_stats->rx_pause_frames =
+ lp->xxv_stat_base[XXV_STAT_RX_PAUSE];
+ } while (read_seqcount_retry(&lp->hw_stats_seqcount, start));
+}
+
+/**
+ * axienet_xxv_get_eth_mac_stats - Fill IEEE 802.3 MAC statistics
+ * @lp: Pointer to the axienet_local structure
+ * @mac_stats: Output ethtool_eth_mac_stats structure to populate
+ */
+static void axienet_xxv_get_eth_mac_stats(struct axienet_local *lp,
+ struct ethtool_eth_mac_stats *mac_stats)
+{
+ unsigned int start;
+
+ do {
+ start = read_seqcount_begin(&lp->hw_stats_seqcount);
+ mac_stats->FramesTransmittedOK =
+ lp->xxv_stat_base[XXV_STAT_TX_TOTAL_GOOD_PACKETS];
+ mac_stats->FramesReceivedOK =
+ lp->xxv_stat_base[XXV_STAT_RX_TOTAL_GOOD_PACKETS];
+ mac_stats->OctetsTransmittedOK =
+ lp->xxv_stat_base[XXV_STAT_TX_TOTAL_GOOD_BYTES];
+ mac_stats->OctetsReceivedOK =
+ lp->xxv_stat_base[XXV_STAT_RX_TOTAL_GOOD_BYTES];
+ mac_stats->FrameCheckSequenceErrors =
+ lp->xxv_stat_base[XXV_STAT_RX_PACKET_BAD_FCS];
+ mac_stats->MulticastFramesXmittedOK =
+ lp->xxv_stat_base[XXV_STAT_TX_MULTICAST];
+ mac_stats->BroadcastFramesXmittedOK =
+ lp->xxv_stat_base[XXV_STAT_TX_BROADCAST];
+ mac_stats->MulticastFramesReceivedOK =
+ lp->xxv_stat_base[XXV_STAT_RX_MULTICAST];
+ mac_stats->BroadcastFramesReceivedOK =
+ lp->xxv_stat_base[XXV_STAT_RX_BROADCAST];
+ mac_stats->InRangeLengthErrors =
+ lp->xxv_stat_base[XXV_STAT_RX_INRANGEERR];
+ } while (read_seqcount_retry(&lp->hw_stats_seqcount, start));
+}
+
+/**
+ * axienet_xxv_get_rmon_stats - Fill RMON statistics and histogram ranges
+ * @lp: Pointer to the axienet_local structure
+ * @rmon_stats: Output ethtool_rmon_stats structure to populate
+ * @ranges: Set to NULL; XXV exposes aggregate RMON counters only, not
+ * per-bin histogram ranges.
+ */
+static void axienet_xxv_get_rmon_stats(struct axienet_local *lp,
+ struct ethtool_rmon_stats *rmon_stats,
+ const struct ethtool_rmon_hist_range **ranges)
+{
+ unsigned int start;
+
+ do {
+ start = read_seqcount_begin(&lp->hw_stats_seqcount);
+ rmon_stats->undersize_pkts =
+ lp->xxv_stat_base[XXV_STAT_RX_UNDERSIZE];
+ rmon_stats->oversize_pkts =
+ lp->xxv_stat_base[XXV_STAT_RX_OVERSIZE];
+ rmon_stats->fragments =
+ lp->xxv_stat_base[XXV_STAT_RX_FRAGMENT];
+ rmon_stats->jabbers =
+ lp->xxv_stat_base[XXV_STAT_RX_JABBER];
+ } while (read_seqcount_retry(&lp->hw_stats_seqcount, start));
+
+ /* XXV currently exposes only aggregate RMON counters, not per-bin
+ * histogram buckets. Keep ranges NULL until histogram bins are wired.
+ */
+ *ranges = NULL;
+}
+
const struct axienet_config axienet_10g25g_config = {
.jumbo = true,
.sw_padding = true,
@@ -348,4 +622,12 @@ const struct axienet_config axienet_10g25g_config = {
.get_regs = axienet_xxv_get_regs,
.phylink_set_caps = axienet_xxv_phylink_set_capabilities,
.pcs_ops = &axienet_xxv_pcs_ops,
+ .stats_update = axienet_xxv_stats_update,
+ .get_stats64 = axienet_xxv_get_stats64,
+ .get_ethtool_stats = axienet_xxv_get_ethtool_stats,
+ .get_strings = axienet_xxv_get_strings,
+ .get_sset_count = axienet_xxv_get_sset_count,
+ .get_pause_stats = axienet_xxv_get_pause_stats,
+ .get_eth_mac_stats = axienet_xxv_get_eth_mac_stats,
+ .get_rmon_stats = axienet_xxv_get_rmon_stats,
};
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_xxv.h b/drivers/net/ethernet/xilinx/xilinx_axienet_xxv.h
index 6530344e40c7..83682d1524ed 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_xxv.h
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_xxv.h
@@ -66,6 +66,36 @@ struct axienet_config;
#define XXV_STAT_GT_OFFSET 0x000004A0
#define XXV_STAT_AN_LINK_CTL2_OFFSET 0x000009F0
+/* XXV statistics counter registers (PG210). Each counter is a pair of 32-bit
+ * registers; the MSB register is XXV_STAT_MSB_OFFSET bytes above the LSB.
+ * MSB[15:0] holds counter bits [47:32]. The upper half is not part of the
+ * counter and must be masked off before the value is accumulated.
+ */
+#define XXV_STAT_MSB_OFFSET 4
+#define XXV_STAT_MSB_MASK GENMASK(15, 0)
+#define XXV_STAT_TX_FRAME_ERROR_OFFSET 0x000006A0
+#define XXV_STAT_TX_TOTAL_PACKETS_OFFSET 0x00000700
+#define XXV_STAT_TX_TOTAL_GOOD_PACKETS_OFFSET 0x00000708
+#define XXV_STAT_TX_TOTAL_BYTES_OFFSET 0x00000710
+#define XXV_STAT_TX_TOTAL_GOOD_BYTES_OFFSET 0x00000718
+#define XXV_STAT_TX_BAD_FCS_OFFSET 0x000007B8
+#define XXV_STAT_TX_MULTICAST_OFFSET 0x000007D8
+#define XXV_STAT_TX_BROADCAST_OFFSET 0x000007E0
+#define XXV_STAT_TX_PAUSE_OFFSET 0x000007F0
+#define XXV_STAT_RX_TOTAL_PACKETS_OFFSET 0x00000808
+#define XXV_STAT_RX_TOTAL_GOOD_PACKETS_OFFSET 0x00000810
+#define XXV_STAT_RX_TOTAL_BYTES_OFFSET 0x00000818
+#define XXV_STAT_RX_TOTAL_GOOD_BYTES_OFFSET 0x00000820
+#define XXV_STAT_RX_UNDERSIZE_OFFSET 0x00000898
+#define XXV_STAT_RX_FRAGMENT_OFFSET 0x000008A0
+#define XXV_STAT_RX_OVERSIZE_OFFSET 0x000008A8
+#define XXV_STAT_RX_JABBER_OFFSET 0x000008B8
+#define XXV_STAT_RX_PACKET_BAD_FCS_OFFSET 0x000008C8
+#define XXV_STAT_RX_MULTICAST_OFFSET 0x000008E0
+#define XXV_STAT_RX_BROADCAST_OFFSET 0x000008E8
+#define XXV_STAT_RX_PAUSE_OFFSET 0x000008F8
+#define XXV_STAT_RX_INRANGEERR_OFFSET 0x00000908
+
/* XXV MAC Register Mask Definitions */
#define XXV_GT_RESET_MASK BIT(0)
#define XXV_TC_TX_MASK BIT(0)
@@ -73,6 +103,7 @@ struct axienet_config;
#define XXV_RCW1_FCS_MASK BIT(1)
#define XXV_TC_FCS_MASK BIT(1)
#define XXV_STAT_RX_BLOCK_LOCK_MASK BIT(0)
+#define XXV_TICKREG_STATEN_MASK BIT(0)
#define XXV_GT_RESET_DONE (BIT(0) | BIT(1))
#define XXV_MAJ_MASK GENMASK(7, 0)
#define XXV_MIN_MASK GENMASK(15, 8)
@@ -88,6 +119,33 @@ struct axienet_config;
#define XXV_RX_MAX_LEN_MASK GENMASK(30, 16)
#define XXV_RX_MIN_LEN_DEFAULT 64
+/* XXV (10G/25G) MAC statistics counters, used to index xxv_stat_base[]. */
+enum xxv_stat {
+ XXV_STAT_RX_TOTAL_PACKETS = 0,
+ XXV_STAT_RX_TOTAL_GOOD_PACKETS,
+ XXV_STAT_RX_TOTAL_BYTES,
+ XXV_STAT_RX_TOTAL_GOOD_BYTES,
+ XXV_STAT_RX_PACKET_BAD_FCS,
+ XXV_STAT_RX_INRANGEERR,
+ XXV_STAT_RX_MULTICAST,
+ XXV_STAT_RX_BROADCAST,
+ XXV_STAT_RX_UNDERSIZE,
+ XXV_STAT_RX_FRAGMENT,
+ XXV_STAT_RX_JABBER,
+ XXV_STAT_RX_OVERSIZE,
+ XXV_STAT_RX_PAUSE,
+ XXV_STAT_TX_TOTAL_PACKETS,
+ XXV_STAT_TX_TOTAL_GOOD_PACKETS,
+ XXV_STAT_TX_TOTAL_BYTES,
+ XXV_STAT_TX_TOTAL_GOOD_BYTES,
+ XXV_STAT_TX_BAD_FCS,
+ XXV_STAT_TX_FRAME_ERROR,
+ XXV_STAT_TX_MULTICAST,
+ XXV_STAT_TX_BROADCAST,
+ XXV_STAT_TX_PAUSE,
+ XXV_STAT_COUNT,
+};
+
extern const struct axienet_config axienet_10g25g_config;
#endif /* XILINX_AXIENET_XXV_H */
--
2.25.1
^ permalink raw reply [flat|nested] 7+ messages in thread