This adds statistic counters for the network interfaces provided
by the driver.  It also adds CPU port counters (which are not
exposed by ethtool).
This also adds support for configuring the network interface
parameters via ethtool: speed, duplex, aneg etc.

Signed-off-by: Steen Hegelund <steen.hegel...@microchip.com>
Signed-off-by: Bjarni Jonasson <bjarni.jonas...@microchip.com>
Signed-off-by: Lars Povlsen <lars.povl...@microchip.com>
---
 .../net/ethernet/microchip/sparx5/Makefile    |   3 +-
 .../microchip/sparx5/sparx5_ethtool.c         | 969 ++++++++++++++++++
 .../ethernet/microchip/sparx5/sparx5_main.c   |   4 +
 .../ethernet/microchip/sparx5/sparx5_main.h   |  12 +
 .../ethernet/microchip/sparx5/sparx5_netdev.c |   2 +
 5 files changed, 989 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c

diff --git a/drivers/net/ethernet/microchip/sparx5/Makefile 
b/drivers/net/ethernet/microchip/sparx5/Makefile
index 17654ab90ce1..de3155cd582c 100644
--- a/drivers/net/ethernet/microchip/sparx5/Makefile
+++ b/drivers/net/ethernet/microchip/sparx5/Makefile
@@ -7,4 +7,5 @@ obj-$(CONFIG_SPARX5_SWITCH) += sparx5-switch.o
 
 sparx5-switch-objs  := sparx5_main.o sparx5_switchdev.o \
  sparx5_vlan.o sparx5_mactable.o sparx5_packet.o \
- sparx5_netdev.o sparx5_port.o sparx5_phylink.o sparx5_calendar.o
+ sparx5_ethtool.o sparx5_netdev.o sparx5_port.o \
+ sparx5_phylink.o sparx5_calendar.o
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c 
b/drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c
new file mode 100644
index 000000000000..4deb51011722
--- /dev/null
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c
@@ -0,0 +1,969 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Microchip Sparx5 Switch driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
+ */
+
+#include <linux/ethtool.h>
+
+#include "sparx5_main_regs.h"
+#include "sparx5_main.h"
+#include "sparx5_port.h"
+
+/* Add a potentially wrapping 32 bit value to a 64 bit counter */
+static void sparx5_update_counter(u64 *cnt, u32 val)
+{
+       if (val < (*cnt & U32_MAX))
+               *cnt += (u64)1 << 32; /* value has wrapped */
+
+       *cnt = (*cnt & ~(u64)U32_MAX) + val;
+}
+
+/* Get a set of Queue System statistics */
+static void sparx5_xqs_prio_stats(struct sparx5 *sparx5,
+                                 u32 addr,
+                                 u64 *stats)
+{
+       int idx;
+
+       for (idx = 0; idx < 2 * SPX5_PRIOS; ++idx, ++addr, ++stats)
+               sparx5_update_counter(stats, spx5_rd(sparx5, XQS_CNT(addr)));
+}
+
+#define SPX5_STAT_GET(sname)        portstats[spx5_stats_##sname]
+#define SPX5_STAT_SUM(sname)        (portstats[spx5_stats_##sname] + \
+                                    portstats[spx5_stats_pmac_##sname])
+#define SPX5_STAT_XQS_PRIOS_COUNTER_SUM(sname)    \
+       (portstats[spx5_stats_green_p0_##sname] + \
+       portstats[spx5_stats_green_p1_##sname] +  \
+       portstats[spx5_stats_green_p2_##sname] +  \
+       portstats[spx5_stats_green_p3_##sname] +  \
+       portstats[spx5_stats_green_p4_##sname] +  \
+       portstats[spx5_stats_green_p5_##sname] +  \
+       portstats[spx5_stats_green_p6_##sname] +  \
+       portstats[spx5_stats_green_p7_##sname] +  \
+       portstats[spx5_stats_yellow_p0_##sname] + \
+       portstats[spx5_stats_yellow_p1_##sname] + \
+       portstats[spx5_stats_yellow_p2_##sname] + \
+       portstats[spx5_stats_yellow_p3_##sname] + \
+       portstats[spx5_stats_yellow_p4_##sname] + \
+       portstats[spx5_stats_yellow_p5_##sname] + \
+       portstats[spx5_stats_yellow_p6_##sname] + \
+       portstats[spx5_stats_yellow_p7_##sname])
+
+enum sparx5_stats_entry {
+       spx5_stats_rx_in_bytes,
+       spx5_stats_rx_symbol_err,
+       spx5_stats_rx_pause,
+       spx5_stats_rx_unsup_opcode,
+       spx5_stats_rx_ok_bytes,
+       spx5_stats_rx_bad_bytes,
+       spx5_stats_rx_unicast,
+       spx5_stats_rx_multicast,
+       spx5_stats_rx_broadcast,
+       spx5_stats_rx_crc_err,
+       spx5_stats_rx_undersize,
+       spx5_stats_rx_fragments,
+       spx5_stats_rx_inrangelen_err,
+       spx5_stats_rx_outofrangelen_err,
+       spx5_stats_rx_oversize,
+       spx5_stats_rx_jabbers,
+       spx5_stats_rx_size64,
+       spx5_stats_rx_size65_127,
+       spx5_stats_rx_size128_255,
+       spx5_stats_rx_size256_511,
+       spx5_stats_rx_size512_1023,
+       spx5_stats_rx_size1024_1518,
+       spx5_stats_rx_size1519_max,
+       spx5_stats_pmac_rx_symbol_err,
+       spx5_stats_pmac_rx_pause,
+       spx5_stats_pmac_rx_unsup_opcode,
+       spx5_stats_pmac_rx_ok_bytes,
+       spx5_stats_pmac_rx_bad_bytes,
+       spx5_stats_pmac_rx_unicast,
+       spx5_stats_pmac_rx_multicast,
+       spx5_stats_pmac_rx_broadcast,
+       spx5_stats_pmac_rx_crc_err,
+       spx5_stats_pmac_rx_undersize,
+       spx5_stats_pmac_rx_fragments,
+       spx5_stats_pmac_rx_inrangelen_err,
+       spx5_stats_pmac_rx_outofrangelen_err,
+       spx5_stats_pmac_rx_oversize,
+       spx5_stats_pmac_rx_jabbers,
+       spx5_stats_pmac_rx_size64,
+       spx5_stats_pmac_rx_size65_127,
+       spx5_stats_pmac_rx_size128_255,
+       spx5_stats_pmac_rx_size256_511,
+       spx5_stats_pmac_rx_size512_1023,
+       spx5_stats_pmac_rx_size1024_1518,
+       spx5_stats_pmac_rx_size1519_max,
+       spx5_stats_green_p0_rx_fwd,
+       spx5_stats_green_p1_rx_fwd,
+       spx5_stats_green_p2_rx_fwd,
+       spx5_stats_green_p3_rx_fwd,
+       spx5_stats_green_p4_rx_fwd,
+       spx5_stats_green_p5_rx_fwd,
+       spx5_stats_green_p6_rx_fwd,
+       spx5_stats_green_p7_rx_fwd,
+       spx5_stats_yellow_p0_rx_fwd,
+       spx5_stats_yellow_p1_rx_fwd,
+       spx5_stats_yellow_p2_rx_fwd,
+       spx5_stats_yellow_p3_rx_fwd,
+       spx5_stats_yellow_p4_rx_fwd,
+       spx5_stats_yellow_p5_rx_fwd,
+       spx5_stats_yellow_p6_rx_fwd,
+       spx5_stats_yellow_p7_rx_fwd,
+       spx5_stats_green_p0_rx_port_drop,
+       spx5_stats_green_p1_rx_port_drop,
+       spx5_stats_green_p2_rx_port_drop,
+       spx5_stats_green_p3_rx_port_drop,
+       spx5_stats_green_p4_rx_port_drop,
+       spx5_stats_green_p5_rx_port_drop,
+       spx5_stats_green_p6_rx_port_drop,
+       spx5_stats_green_p7_rx_port_drop,
+       spx5_stats_yellow_p0_rx_port_drop,
+       spx5_stats_yellow_p1_rx_port_drop,
+       spx5_stats_yellow_p2_rx_port_drop,
+       spx5_stats_yellow_p3_rx_port_drop,
+       spx5_stats_yellow_p4_rx_port_drop,
+       spx5_stats_yellow_p5_rx_port_drop,
+       spx5_stats_yellow_p6_rx_port_drop,
+       spx5_stats_yellow_p7_rx_port_drop,
+       spx5_stats_rx_local_drop,
+       spx5_stats_rx_port_policer_drop,
+       spx5_stats_tx_out_bytes,
+       spx5_stats_tx_pause,
+       spx5_stats_tx_ok_bytes,
+       spx5_stats_tx_unicast,
+       spx5_stats_tx_multicast,
+       spx5_stats_tx_broadcast,
+       spx5_stats_tx_size64,
+       spx5_stats_tx_size65_127,
+       spx5_stats_tx_size128_255,
+       spx5_stats_tx_size256_511,
+       spx5_stats_tx_size512_1023,
+       spx5_stats_tx_size1024_1518,
+       spx5_stats_tx_size1519_max,
+       spx5_stats_tx_multi_coll,
+       spx5_stats_tx_late_coll,
+       spx5_stats_tx_xcoll,
+       spx5_stats_tx_defer,
+       spx5_stats_tx_xdefer,
+       spx5_stats_tx_backoff1,
+       spx5_stats_pmac_tx_pause,
+       spx5_stats_pmac_tx_ok_bytes,
+       spx5_stats_pmac_tx_unicast,
+       spx5_stats_pmac_tx_multicast,
+       spx5_stats_pmac_tx_broadcast,
+       spx5_stats_pmac_tx_size64,
+       spx5_stats_pmac_tx_size65_127,
+       spx5_stats_pmac_tx_size128_255,
+       spx5_stats_pmac_tx_size256_511,
+       spx5_stats_pmac_tx_size512_1023,
+       spx5_stats_pmac_tx_size1024_1518,
+       spx5_stats_pmac_tx_size1519_max,
+       spx5_stats_green_p0_tx_port,
+       spx5_stats_green_p1_tx_port,
+       spx5_stats_green_p2_tx_port,
+       spx5_stats_green_p3_tx_port,
+       spx5_stats_green_p4_tx_port,
+       spx5_stats_green_p5_tx_port,
+       spx5_stats_green_p6_tx_port,
+       spx5_stats_green_p7_tx_port,
+       spx5_stats_yellow_p0_tx_port,
+       spx5_stats_yellow_p1_tx_port,
+       spx5_stats_yellow_p2_tx_port,
+       spx5_stats_yellow_p3_tx_port,
+       spx5_stats_yellow_p4_tx_port,
+       spx5_stats_yellow_p5_tx_port,
+       spx5_stats_yellow_p6_tx_port,
+       spx5_stats_yellow_p7_tx_port,
+       spx5_stats_tx_local_drop,
+};
+
+static const char *const sparx5_stats_layout[] = {
+       "rx_in_bytes",
+       "rx_symbol_err",
+       "rx_pause",
+       "rx_unsup_opcode",
+       "rx_ok_bytes",
+       "rx_bad_bytes",
+       "rx_unicast",
+       "rx_multicast",
+       "rx_broadcast",
+       "rx_crc_err",
+       "rx_undersize",
+       "rx_fragments",
+       "rx_inrangelen_err",
+       "rx_outofrangelen_err",
+       "rx_oversize",
+       "rx_jabbers",
+       "rx_size64",
+       "rx_size65_127",
+       "rx_size128_255",
+       "rx_size256_511",
+       "rx_size512_1023",
+       "rx_size1024_1518",
+       "rx_size1519_max",
+       "pmac_rx_symbol_err",
+       "pmac_rx_pause",
+       "pmac_rx_unsup_opcode",
+       "pmac_rx_ok_bytes",
+       "pmac_rx_bad_bytes",
+       "pmac_rx_unicast",
+       "pmac_rx_multicast",
+       "pmac_rx_broadcast",
+       "pmac_rx_crc_err",
+       "pmac_rx_undersize",
+       "pmac_rx_fragments",
+       "pmac_rx_inrangelen_err",
+       "pmac_rx_outofrangelen_err",
+       "pmac_rx_oversize",
+       "pmac_rx_jabbers",
+       "pmac_rx_size64",
+       "pmac_rx_size65_127",
+       "pmac_rx_size128_255",
+       "pmac_rx_size256_511",
+       "pmac_rx_size512_1023",
+       "pmac_rx_size1024_1518",
+       "pmac_rx_size1519_max",
+       "rx_fwd_green_p0_q",
+       "rx_fwd_green_p1_q",
+       "rx_fwd_green_p2_q",
+       "rx_fwd_green_p3_q",
+       "rx_fwd_green_p4_q",
+       "rx_fwd_green_p5_q",
+       "rx_fwd_green_p6_q",
+       "rx_fwd_green_p7_q",
+       "rx_fwd_yellow_p0_q",
+       "rx_fwd_yellow_p1_q",
+       "rx_fwd_yellow_p2_q",
+       "rx_fwd_yellow_p3_q",
+       "rx_fwd_yellow_p4_q",
+       "rx_fwd_yellow_p5_q",
+       "rx_fwd_yellow_p6_q",
+       "rx_fwd_yellow_p7_q",
+       "rx_port_drop_green_p0_q",
+       "rx_port_drop_green_p1_q",
+       "rx_port_drop_green_p2_q",
+       "rx_port_drop_green_p3_q",
+       "rx_port_drop_green_p4_q",
+       "rx_port_drop_green_p5_q",
+       "rx_port_drop_green_p6_q",
+       "rx_port_drop_green_p7_q",
+       "rx_port_drop_yellow_p0_q",
+       "rx_port_drop_yellow_p1_q",
+       "rx_port_drop_yellow_p2_q",
+       "rx_port_drop_yellow_p3_q",
+       "rx_port_drop_yellow_p4_q",
+       "rx_port_drop_yellow_p5_q",
+       "rx_port_drop_yellow_p6_q",
+       "rx_port_drop_yellow_p7_q",
+       "rx_local_drop",
+       "rx_port_policer_drop",
+       "tx_out_bytes",
+       "tx_pause",
+       "tx_ok_bytes",
+       "tx_unicast",
+       "tx_multicast",
+       "tx_broadcast",
+       "tx_size64",
+       "tx_size65_127",
+       "tx_size128_255",
+       "tx_size256_511",
+       "tx_size512_1023",
+       "tx_size1024_1518",
+       "tx_size1519_max",
+       "tx_multi_coll",
+       "tx_late_coll",
+       "tx_xcoll",
+       "tx_defer",
+       "tx_xdefer",
+       "tx_backoff1",
+       "pmac_tx_pause",
+       "pmac_tx_ok_bytes",
+       "pmac_tx_unicast",
+       "pmac_tx_multicast",
+       "pmac_tx_broadcast",
+       "pmac_tx_size64",
+       "pmac_tx_size65_127",
+       "pmac_tx_size128_255",
+       "pmac_tx_size256_511",
+       "pmac_tx_size512_1023",
+       "pmac_tx_size1024_1518",
+       "pmac_tx_size1519_max",
+       "tx_port_green_p0_q",
+       "tx_port_green_p1_q",
+       "tx_port_green_p2_q",
+       "tx_port_green_p3_q",
+       "tx_port_green_p4_q",
+       "tx_port_green_p5_q",
+       "tx_port_green_p6_q",
+       "tx_port_green_p7_q",
+       "tx_port_yellow_p0_q",
+       "tx_port_yellow_p1_q",
+       "tx_port_yellow_p2_q",
+       "tx_port_yellow_p3_q",
+       "tx_port_yellow_p4_q",
+       "tx_port_yellow_p5_q",
+       "tx_port_yellow_p6_q",
+       "tx_port_yellow_p7_q",
+       "tx_local_drop",
+};
+
+/* Device Statistics */
+static void sparx5_get_device_stats(struct sparx5 *sparx5, int portno)
+{
+       u64 *portstats = &sparx5->stats[portno * sparx5->num_stats];
+       u32 dev = sparx5_to_high_dev(portno);
+       u32 tinst = sparx5_port_dev_index(portno);
+       void __iomem *inst = spx5_inst_get(sparx5, dev, tinst);
+
+       sparx5_update_counter(&portstats[spx5_stats_rx_in_bytes],
+                             spx5_inst_rd(inst,
+                                          DEV5G_RX_IN_BYTES_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_symbol_err],
+                             spx5_inst_rd(inst,
+                                          DEV5G_RX_SYMBOL_ERR_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_pause],
+                             spx5_inst_rd(inst,
+                                          DEV5G_RX_PAUSE_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_unsup_opcode],
+                             spx5_inst_rd(inst,
+                                          
DEV5G_PMAC_RX_UNSUP_OPCODE_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_ok_bytes],
+                             spx5_inst_rd(inst,
+                                          DEV5G_RX_OK_BYTES_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_bad_bytes],
+                             spx5_inst_rd(inst,
+                                          DEV5G_RX_BAD_BYTES_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_unicast],
+                             spx5_inst_rd(inst,
+                                          DEV5G_RX_UC_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_multicast],
+                             spx5_inst_rd(inst,
+                                          DEV5G_RX_MC_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_broadcast],
+                             spx5_inst_rd(inst,
+                                          DEV5G_RX_BC_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_crc_err],
+                             spx5_inst_rd(inst,
+                                          DEV5G_RX_CRC_ERR_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_undersize],
+                             spx5_inst_rd(inst,
+                                          DEV5G_RX_UNDERSIZE_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_fragments],
+                             spx5_inst_rd(inst,
+                                          DEV5G_RX_FRAGMENTS_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_inrangelen_err],
+                             spx5_inst_rd(inst,
+                                          
DEV5G_RX_IN_RANGE_LEN_ERR_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_outofrangelen_err],
+                             spx5_inst_rd(inst,
+                                          
DEV5G_RX_OUT_OF_RANGE_LEN_ERR_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_oversize],
+                             spx5_inst_rd(inst,
+                                          DEV5G_RX_OVERSIZE_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_jabbers],
+                             spx5_inst_rd(inst,
+                                          DEV5G_RX_JABBERS_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_size64],
+                             spx5_inst_rd(inst,
+                                          DEV5G_RX_SIZE64_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_size65_127],
+                             spx5_inst_rd(inst,
+                                          DEV5G_RX_SIZE65TO127_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_size128_255],
+                             spx5_inst_rd(inst,
+                                          DEV5G_RX_SIZE128TO255_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_size256_511],
+                             spx5_inst_rd(inst,
+                                          DEV5G_RX_SIZE256TO511_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_size512_1023],
+                             spx5_inst_rd(inst,
+                                          DEV5G_RX_SIZE512TO1023_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_size1024_1518],
+                             spx5_inst_rd(inst,
+                                          DEV5G_RX_SIZE1024TO1518_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_size1519_max],
+                             spx5_inst_rd(inst,
+                                          DEV5G_RX_SIZE1519TOMAX_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_symbol_err],
+                             spx5_inst_rd(inst,
+                                          
DEV5G_PMAC_RX_SYMBOL_ERR_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_pause],
+                             spx5_inst_rd(inst,
+                                          DEV5G_PMAC_RX_PAUSE_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_unsup_opcode],
+                             spx5_inst_rd(inst,
+                                          
DEV5G_PMAC_RX_UNSUP_OPCODE_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_ok_bytes],
+                             spx5_inst_rd(inst,
+                                          DEV5G_PMAC_RX_OK_BYTES_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_bad_bytes],
+                             spx5_inst_rd(inst,
+                                          DEV5G_PMAC_RX_BAD_BYTES_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_unicast],
+                             spx5_inst_rd(inst,
+                                          DEV5G_PMAC_RX_UC_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_multicast],
+                             spx5_inst_rd(inst,
+                                          DEV5G_PMAC_RX_MC_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_broadcast],
+                             spx5_inst_rd(inst,
+                                          DEV5G_PMAC_RX_BC_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_crc_err],
+                             spx5_inst_rd(inst,
+                                          DEV5G_PMAC_RX_CRC_ERR_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_undersize],
+                             spx5_inst_rd(inst,
+                                          DEV5G_PMAC_RX_UNDERSIZE_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_fragments],
+                             spx5_inst_rd(inst,
+                                          DEV5G_PMAC_RX_FRAGMENTS_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_inrangelen_err],
+                             spx5_inst_rd(inst,
+                                          
DEV5G_PMAC_RX_IN_RANGE_LEN_ERR_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_outofrangelen_err],
+                             spx5_inst_rd(inst,
+                                          
DEV5G_PMAC_RX_OUT_OF_RANGE_LEN_ERR_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_oversize],
+                             spx5_inst_rd(inst,
+                                          DEV5G_PMAC_RX_OVERSIZE_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_jabbers],
+                             spx5_inst_rd(inst,
+                                          DEV5G_PMAC_RX_JABBERS_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size64],
+                             spx5_inst_rd(inst,
+                                          DEV5G_PMAC_RX_SIZE64_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size65_127],
+                             spx5_inst_rd(inst,
+                                          
DEV5G_PMAC_RX_SIZE65TO127_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size128_255],
+                             spx5_inst_rd(inst,
+                                          
DEV5G_PMAC_RX_SIZE128TO255_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size256_511],
+                             spx5_inst_rd(inst,
+                                          
DEV5G_PMAC_RX_SIZE256TO511_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size512_1023],
+                             spx5_inst_rd(inst,
+                                          
DEV5G_PMAC_RX_SIZE512TO1023_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size1024_1518],
+                             spx5_inst_rd(inst,
+                                          
DEV5G_PMAC_RX_SIZE1024TO1518_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size1519_max],
+                             spx5_inst_rd(inst,
+                                          
DEV5G_PMAC_RX_SIZE1519TOMAX_CNT(tinst)));
+       sparx5_xqs_prio_stats(sparx5,
+                             0,
+                             &portstats[spx5_stats_green_p0_rx_fwd]);
+       sparx5_xqs_prio_stats(sparx5,
+                             16,
+                             &portstats[spx5_stats_green_p0_rx_port_drop]);
+       sparx5_update_counter(&portstats[spx5_stats_rx_local_drop],
+                             spx5_rd(sparx5, XQS_CNT(32)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_port_policer_drop],
+                             spx5_rd(sparx5,
+                                     ANA_AC_PORT_STAT_LSB_CNT(portno, 1)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_out_bytes],
+                             spx5_inst_rd(inst,
+                                          DEV5G_TX_OUT_BYTES_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_pause],
+                             spx5_inst_rd(inst,
+                                          DEV5G_TX_PAUSE_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_ok_bytes],
+                             spx5_inst_rd(inst,
+                                          DEV5G_TX_OK_BYTES_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_unicast],
+                             spx5_inst_rd(inst,
+                                          DEV5G_TX_UC_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_multicast],
+                             spx5_inst_rd(inst,
+                                          DEV5G_TX_MC_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_broadcast],
+                             spx5_inst_rd(inst,
+                                          DEV5G_TX_BC_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_size64],
+                             spx5_inst_rd(inst,
+                                          DEV5G_TX_SIZE64_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_size65_127],
+                             spx5_inst_rd(inst,
+                                          DEV5G_TX_SIZE65TO127_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_size128_255],
+                             spx5_inst_rd(inst,
+                                          DEV5G_TX_SIZE128TO255_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_size256_511],
+                             spx5_inst_rd(inst,
+                                          DEV5G_TX_SIZE256TO511_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_size512_1023],
+                             spx5_inst_rd(inst,
+                                          DEV5G_TX_SIZE512TO1023_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_size1024_1518],
+                             spx5_inst_rd(inst,
+                                          DEV5G_TX_SIZE1024TO1518_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_size1519_max],
+                             spx5_inst_rd(inst,
+                                          DEV5G_TX_SIZE1519TOMAX_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_tx_pause],
+                             spx5_inst_rd(inst,
+                                          DEV5G_PMAC_TX_PAUSE_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_tx_ok_bytes],
+                             spx5_inst_rd(inst,
+                                          DEV5G_PMAC_TX_OK_BYTES_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_tx_unicast],
+                             spx5_inst_rd(inst,
+                                          DEV5G_PMAC_TX_UC_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_tx_multicast],
+                             spx5_inst_rd(inst,
+                                          DEV5G_PMAC_TX_MC_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_tx_broadcast],
+                             spx5_inst_rd(inst,
+                                          DEV5G_PMAC_TX_BC_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size64],
+                             spx5_inst_rd(inst,
+                                          DEV5G_PMAC_TX_SIZE64_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size65_127],
+                             spx5_inst_rd(inst,
+                                          
DEV5G_PMAC_TX_SIZE65TO127_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size128_255],
+                             spx5_inst_rd(inst,
+                                          
DEV5G_PMAC_TX_SIZE128TO255_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size256_511],
+                             spx5_inst_rd(inst,
+                                          
DEV5G_PMAC_TX_SIZE256TO511_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size512_1023],
+                             spx5_inst_rd(inst,
+                                          
DEV5G_PMAC_TX_SIZE512TO1023_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size1024_1518],
+                             spx5_inst_rd(inst,
+                                          
DEV5G_PMAC_TX_SIZE1024TO1518_CNT(tinst)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size1519_max],
+                             spx5_inst_rd(inst,
+                                          
DEV5G_PMAC_TX_SIZE1519TOMAX_CNT(tinst)));
+       sparx5_xqs_prio_stats(sparx5,
+                             256,
+                             &portstats[spx5_stats_green_p0_tx_port]);
+       sparx5_update_counter(&portstats[spx5_stats_tx_local_drop],
+                             spx5_rd(sparx5, XQS_CNT(272)));
+}
+
+/* ASM Statistics */
+static void sparx5_get_asm_stats(struct sparx5 *sparx5, int portno)
+{
+       u64 *portstats = &sparx5->stats[portno * sparx5->num_stats];
+       u32 dev = TARGET_ASM;
+       void __iomem *inst = spx5_inst_get(sparx5, dev, 0);
+
+       sparx5_update_counter(&portstats[spx5_stats_rx_in_bytes],
+                             spx5_inst_rd(inst,
+                                          ASM_RX_IN_BYTES_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_symbol_err],
+                             spx5_inst_rd(inst,
+                                          ASM_RX_SYMBOL_ERR_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_pause],
+                             spx5_inst_rd(inst,
+                                          ASM_RX_PAUSE_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_unsup_opcode],
+                             spx5_inst_rd(inst,
+                                          
ASM_PMAC_RX_UNSUP_OPCODE_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_ok_bytes],
+                             spx5_inst_rd(inst,
+                                          ASM_RX_OK_BYTES_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_bad_bytes],
+                             spx5_inst_rd(inst,
+                                          ASM_RX_BAD_BYTES_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_unicast],
+                             spx5_inst_rd(inst,
+                                          ASM_RX_UC_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_multicast],
+                             spx5_inst_rd(inst,
+                                          ASM_RX_MC_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_broadcast],
+                             spx5_inst_rd(inst,
+                                          ASM_RX_BC_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_crc_err],
+                             spx5_inst_rd(inst,
+                                          ASM_RX_CRC_ERR_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_undersize],
+                             spx5_inst_rd(inst,
+                                          ASM_RX_UNDERSIZE_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_fragments],
+                             spx5_inst_rd(inst,
+                                          ASM_RX_FRAGMENTS_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_inrangelen_err],
+                             spx5_inst_rd(inst,
+                                          
ASM_RX_IN_RANGE_LEN_ERR_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_outofrangelen_err],
+                             spx5_inst_rd(inst,
+                                          
ASM_RX_OUT_OF_RANGE_LEN_ERR_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_oversize],
+                             spx5_inst_rd(inst,
+                                          ASM_RX_OVERSIZE_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_jabbers],
+                             spx5_inst_rd(inst,
+                                          ASM_RX_JABBERS_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_size64],
+                             spx5_inst_rd(inst,
+                                          ASM_RX_SIZE64_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_size65_127],
+                             spx5_inst_rd(inst,
+                                          ASM_RX_SIZE65TO127_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_size128_255],
+                             spx5_inst_rd(inst,
+                                          ASM_RX_SIZE128TO255_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_size256_511],
+                             spx5_inst_rd(inst,
+                                          ASM_RX_SIZE256TO511_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_size512_1023],
+                             spx5_inst_rd(inst,
+                                          ASM_RX_SIZE512TO1023_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_size1024_1518],
+                             spx5_inst_rd(inst,
+                                          ASM_RX_SIZE1024TO1518_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_size1519_max],
+                             spx5_inst_rd(inst,
+                                          ASM_RX_SIZE1519TOMAX_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_symbol_err],
+                             spx5_inst_rd(inst,
+                                          ASM_PMAC_RX_SYMBOL_ERR_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_pause],
+                             spx5_inst_rd(inst,
+                                          ASM_PMAC_RX_PAUSE_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_unsup_opcode],
+                             spx5_inst_rd(inst,
+                                          
ASM_PMAC_RX_UNSUP_OPCODE_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_ok_bytes],
+                             spx5_inst_rd(inst,
+                                          ASM_PMAC_RX_OK_BYTES_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_bad_bytes],
+                             spx5_inst_rd(inst,
+                                          ASM_PMAC_RX_BAD_BYTES_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_unicast],
+                             spx5_inst_rd(inst,
+                                          ASM_PMAC_RX_UC_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_multicast],
+                             spx5_inst_rd(inst,
+                                          ASM_PMAC_RX_MC_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_broadcast],
+                             spx5_inst_rd(inst,
+                                          ASM_PMAC_RX_BC_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_crc_err],
+                             spx5_inst_rd(inst,
+                                          ASM_PMAC_RX_CRC_ERR_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_undersize],
+                             spx5_inst_rd(inst,
+                                          ASM_PMAC_RX_UNDERSIZE_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_fragments],
+                             spx5_inst_rd(inst,
+                                          ASM_PMAC_RX_FRAGMENTS_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_inrangelen_err],
+                             spx5_inst_rd(inst,
+                                          
ASM_PMAC_RX_IN_RANGE_LEN_ERR_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_outofrangelen_err],
+                             spx5_inst_rd(inst,
+                                          
ASM_PMAC_RX_OUT_OF_RANGE_LEN_ERR_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_oversize],
+                             spx5_inst_rd(inst,
+                                          ASM_PMAC_RX_OVERSIZE_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_jabbers],
+                             spx5_inst_rd(inst,
+                                          ASM_PMAC_RX_JABBERS_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size64],
+                             spx5_inst_rd(inst,
+                                          ASM_PMAC_RX_SIZE64_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size65_127],
+                             spx5_inst_rd(inst,
+                                          
ASM_PMAC_RX_SIZE65TO127_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size128_255],
+                             spx5_inst_rd(inst,
+                                          
ASM_PMAC_RX_SIZE128TO255_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size256_511],
+                             spx5_inst_rd(inst,
+                                          
ASM_PMAC_RX_SIZE256TO511_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size512_1023],
+                             spx5_inst_rd(inst,
+                                          
ASM_PMAC_RX_SIZE512TO1023_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size1024_1518],
+                             spx5_inst_rd(inst,
+                                          
ASM_PMAC_RX_SIZE1024TO1518_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_rx_size1519_max],
+                             spx5_inst_rd(inst,
+                                          
ASM_PMAC_RX_SIZE1519TOMAX_CNT(portno)));
+       sparx5_xqs_prio_stats(sparx5,
+                             0,
+                             &portstats[spx5_stats_green_p0_rx_fwd]);
+       sparx5_xqs_prio_stats(sparx5,
+                             16,
+                             &portstats[spx5_stats_green_p0_rx_port_drop]);
+       sparx5_update_counter(&portstats[spx5_stats_rx_local_drop],
+                             spx5_rd(sparx5, XQS_CNT(32)));
+       sparx5_update_counter(&portstats[spx5_stats_rx_port_policer_drop],
+                             spx5_rd(sparx5,
+                                     ANA_AC_PORT_STAT_LSB_CNT(portno, 1)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_out_bytes],
+                             spx5_inst_rd(inst,
+                                          ASM_TX_OUT_BYTES_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_pause],
+                             spx5_inst_rd(inst,
+                                          ASM_TX_PAUSE_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_ok_bytes],
+                             spx5_inst_rd(inst,
+                                          ASM_TX_OK_BYTES_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_unicast],
+                             spx5_inst_rd(inst,
+                                          ASM_TX_UC_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_multicast],
+                             spx5_inst_rd(inst,
+                                          ASM_TX_MC_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_broadcast],
+                             spx5_inst_rd(inst,
+                                          ASM_TX_BC_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_size64],
+                             spx5_inst_rd(inst,
+                                          ASM_TX_SIZE64_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_size65_127],
+                             spx5_inst_rd(inst,
+                                          ASM_TX_SIZE65TO127_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_size128_255],
+                             spx5_inst_rd(inst,
+                                          ASM_TX_SIZE128TO255_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_size256_511],
+                             spx5_inst_rd(inst,
+                                          ASM_TX_SIZE256TO511_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_size512_1023],
+                             spx5_inst_rd(inst,
+                                          ASM_TX_SIZE512TO1023_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_size1024_1518],
+                             spx5_inst_rd(inst,
+                                          ASM_TX_SIZE1024TO1518_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_size1519_max],
+                             spx5_inst_rd(inst,
+                                          ASM_TX_SIZE1519TOMAX_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_multi_coll],
+                             spx5_inst_rd(inst,
+                                          ASM_TX_MULTI_COLL_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_late_coll],
+                             spx5_inst_rd(inst,
+                                          ASM_TX_LATE_COLL_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_xcoll],
+                             spx5_inst_rd(inst,
+                                          ASM_TX_XCOLL_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_defer],
+                             spx5_inst_rd(inst,
+                                          ASM_TX_DEFER_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_xdefer],
+                             spx5_inst_rd(inst,
+                                          ASM_TX_XDEFER_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_tx_backoff1],
+                             spx5_inst_rd(inst,
+                                          ASM_TX_BACKOFF1_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_tx_pause],
+                             spx5_inst_rd(inst,
+                                          ASM_PMAC_TX_PAUSE_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_tx_ok_bytes],
+                             spx5_inst_rd(inst,
+                                          ASM_PMAC_TX_OK_BYTES_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_tx_unicast],
+                             spx5_inst_rd(inst,
+                                          ASM_PMAC_TX_UC_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_tx_multicast],
+                             spx5_inst_rd(inst,
+                                          ASM_PMAC_TX_MC_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_tx_broadcast],
+                             spx5_inst_rd(inst,
+                                          ASM_PMAC_TX_BC_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size64],
+                             spx5_inst_rd(inst,
+                                          ASM_PMAC_TX_SIZE64_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size65_127],
+                             spx5_inst_rd(inst,
+                                          
ASM_PMAC_TX_SIZE65TO127_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size128_255],
+                             spx5_inst_rd(inst,
+                                          
ASM_PMAC_TX_SIZE128TO255_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size256_511],
+                             spx5_inst_rd(inst,
+                                          
ASM_PMAC_TX_SIZE256TO511_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size512_1023],
+                             spx5_inst_rd(inst,
+                                          
ASM_PMAC_TX_SIZE512TO1023_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size1024_1518],
+                             spx5_inst_rd(inst,
+                                          
ASM_PMAC_TX_SIZE1024TO1518_CNT(portno)));
+       sparx5_update_counter(&portstats[spx5_stats_pmac_tx_size1519_max],
+                             spx5_inst_rd(inst,
+                                          
ASM_PMAC_TX_SIZE1519TOMAX_CNT(portno)));
+       sparx5_xqs_prio_stats(sparx5,
+                             256,
+                             &portstats[spx5_stats_green_p0_tx_port]);
+       sparx5_update_counter(&portstats[spx5_stats_tx_local_drop],
+                             spx5_rd(sparx5, XQS_CNT(272)));
+}
+
+static void sparx5_update_port_stats(struct sparx5 *sparx5, int portno)
+{
+       /* Set XQS port number */
+       spx5_wr(XQS_STAT_CFG_STAT_VIEW_SET(portno), sparx5, XQS_STAT_CFG);
+       if (sparx5_is_high_speed_device(&sparx5->ports[portno]->conf))
+               sparx5_get_device_stats(sparx5, portno);
+       else
+               sparx5_get_asm_stats(sparx5, portno);
+}
+
+static void sparx5_update_stats(struct sparx5 *sparx5)
+{
+       int idx;
+
+       for (idx = 0; idx < SPX5_PORTS; idx++)
+               if (sparx5->ports[idx])
+                       sparx5_update_port_stats(sparx5, idx);
+}
+
+static void sparx5_check_stats_work(struct work_struct *work)
+{
+       struct delayed_work *dwork = to_delayed_work(work);
+       struct sparx5 *sparx5 = container_of(dwork, struct sparx5,
+                                            stats_work);
+
+       sparx5_update_stats(sparx5);
+
+       queue_delayed_work(sparx5->stats_queue, &sparx5->stats_work,
+                          SPX5_STATS_CHECK_DELAY);
+}
+
+static int sparx5_get_sset_count(struct net_device *ndev, int sset)
+{
+       struct sparx5_port *port = netdev_priv(ndev);
+       struct sparx5  *sparx5 = port->sparx5;
+
+       if (sset != ETH_SS_STATS)
+               return -EOPNOTSUPP;
+       return sparx5->num_stats;
+}
+
+static void sparx5_get_sset_strings(struct net_device *ndev, u32 sset, u8 
*data)
+{
+       struct sparx5_port *port = netdev_priv(ndev);
+       struct sparx5  *sparx5 = port->sparx5;
+       int idx;
+
+       if (sset != ETH_SS_STATS)
+               return;
+
+       for (idx = 0; idx < sparx5->num_stats; idx++)
+               strncpy(data + idx * ETH_GSTRING_LEN,
+                       sparx5->stats_layout[idx], ETH_GSTRING_LEN);
+}
+
+static void sparx5_get_stats(struct net_device *ndev,
+                            struct ethtool_stats *stats, u64 *data)
+{
+       struct sparx5_port *port = netdev_priv(ndev);
+       struct sparx5  *sparx5 = port->sparx5;
+       u64 *portstats = &sparx5->stats[port->portno * sparx5->num_stats];
+       int idx;
+
+       /* Copy port counters to the ethtool buffer */
+       for (idx = 0; idx < sparx5->num_stats; idx++)
+               *data++ = portstats[idx];
+}
+
+void sparx5_get_stats64(struct net_device *ndev,
+                       struct rtnl_link_stats64 *stats)
+{
+       struct sparx5_port *port = netdev_priv(ndev);
+       struct sparx5 *sparx5 = port->sparx5;
+       u64 *portstats;
+
+       if (!sparx5->stats)
+               return; /* Not initialized yet */
+
+       portstats = &sparx5->stats[port->portno * sparx5->num_stats];
+
+       stats->rx_errors = SPX5_STAT_SUM(rx_crc_err) +
+               SPX5_STAT_SUM(rx_undersize) + SPX5_STAT_SUM(rx_oversize) +
+               SPX5_STAT_SUM(rx_outofrangelen_err) +
+               SPX5_STAT_SUM(rx_symbol_err) + SPX5_STAT_SUM(rx_jabbers) +
+               SPX5_STAT_SUM(rx_fragments);
+
+       stats->rx_bytes = SPX5_STAT_SUM(rx_ok_bytes) +
+               SPX5_STAT_SUM(rx_bad_bytes);
+
+       stats->rx_packets = SPX5_STAT_SUM(rx_unicast) +
+               SPX5_STAT_SUM(rx_multicast) + SPX5_STAT_SUM(rx_broadcast) +
+               stats->rx_errors;
+
+       stats->multicast = SPX5_STAT_SUM(rx_unicast);
+
+       stats->rx_dropped = SPX5_STAT_GET(rx_port_policer_drop) +
+               SPX5_STAT_XQS_PRIOS_COUNTER_SUM(rx_port_drop);
+
+       /* Get Tx stats */
+       stats->tx_bytes = SPX5_STAT_SUM(tx_ok_bytes);
+
+       stats->tx_packets = SPX5_STAT_SUM(tx_unicast) +
+               SPX5_STAT_SUM(tx_multicast) + SPX5_STAT_SUM(tx_broadcast);
+
+       stats->tx_dropped = SPX5_STAT_GET(tx_local_drop);
+
+       stats->collisions = SPX5_STAT_GET(tx_multi_coll) +
+               SPX5_STAT_GET(tx_late_coll) +
+               SPX5_STAT_GET(tx_xcoll) +
+               SPX5_STAT_GET(tx_backoff1);
+}
+
+static int sparx5_get_link_settings(struct net_device *ndev,
+                                   struct ethtool_link_ksettings *cmd)
+{
+       struct sparx5_port *port = netdev_priv(ndev);
+
+       return phylink_ethtool_ksettings_get(port->phylink, cmd);
+}
+
+static int sparx5_set_link_settings(struct net_device *ndev,
+                                   const struct ethtool_link_ksettings *cmd)
+{
+       struct sparx5_port *port = netdev_priv(ndev);
+
+       return phylink_ethtool_ksettings_set(port->phylink, cmd);
+}
+
+const struct ethtool_ops sparx5_ethtool_ops = {
+       .get_sset_count         = sparx5_get_sset_count,
+       .get_strings            = sparx5_get_sset_strings,
+       .get_ethtool_stats      = sparx5_get_stats,
+       .get_link_ksettings     = sparx5_get_link_settings,
+       .set_link_ksettings     = sparx5_set_link_settings,
+       .get_link               = ethtool_op_get_link,
+};
+
+int sparx_stats_init(struct sparx5 *sparx5)
+{
+       char queue_name[32];
+       int portno;
+
+       sparx5->stats_layout = sparx5_stats_layout;
+       sparx5->num_stats = ARRAY_SIZE(sparx5_stats_layout);
+       sparx5->stats = devm_kcalloc(sparx5->dev,
+                                    SPX5_PORTS_ALL * sparx5->num_stats,
+                                    sizeof(u64), GFP_KERNEL);
+       if (!sparx5->stats)
+               return -ENOMEM;
+
+       for (portno = 0; portno < SPX5_PORTS; portno++)
+               if (sparx5->ports[portno]) {
+                       /* Clear Queue System counters */
+                       spx5_wr(XQS_STAT_CFG_STAT_VIEW_SET(portno) |
+                               XQS_STAT_CFG_STAT_CLEAR_SHOT_SET(3), sparx5,
+                               XQS_STAT_CFG);
+               }
+
+       snprintf(queue_name, sizeof(queue_name), "%s-stats",
+                dev_name(sparx5->dev));
+       sparx5->stats_queue = create_singlethread_workqueue(queue_name);
+       INIT_DELAYED_WORK(&sparx5->stats_work, sparx5_check_stats_work);
+       queue_delayed_work(sparx5->stats_queue, &sparx5->stats_work,
+                          SPX5_STATS_CHECK_DELAY);
+
+       return 0;
+}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c 
b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
index e612d7cab131..5ba6bfdceb8d 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
@@ -613,6 +613,10 @@ static int sparx5_start(struct sparx5 *sparx5)
        if (err)
                return err;
 
+       /* Init stats */
+       err = sparx_stats_init(sparx5);
+       if (err)
+               return err;
 
        /* Init mact_sw struct */
        mutex_init(&sparx5->mact_lock);
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h 
b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
index a375f6e236a7..3d0e80facfa5 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
@@ -130,6 +130,13 @@ struct sparx5 {
        /* port structures are in net device */
        struct sparx5_port                    *ports[SPX5_PORTS];
        enum sparx5_core_clockfreq            coreclock;
+       /* Statistics */
+       u32                                   num_stats;
+       const char * const                    *stats_layout;
+       u64                                   *stats;
+       /* Workqueue for reading stats */
+       struct delayed_work                   stats_work;
+       struct workqueue_struct               *stats_queue;
        /* Notifiers */
        struct notifier_block                 netdevice_nb;
        struct notifier_block                 switchdev_nb;
@@ -195,6 +202,10 @@ void sparx5_vlan_port_apply(struct sparx5 *sparx5, struct 
sparx5_port *port);
 int sparx5_config_auto_calendar(struct sparx5 *sparx5);
 int sparx5_config_dsm_calendar(struct sparx5 *sparx5);
 
+/* sparx5_ethtool.c */
+void sparx5_get_stats64(struct net_device *ndev, struct rtnl_link_stats64 
*stats);
+int sparx_stats_init(struct sparx5 *sparx5);
+
 /* sparx5_netdev.c */
 bool sparx5_netdevice_check(const struct net_device *dev);
 struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32 portno);
@@ -216,6 +227,7 @@ static inline u32 sparx5_clk_period(enum 
sparx5_core_clockfreq cclock)
 }
 
 extern const struct phylink_mac_ops sparx5_phylink_mac_ops;
+extern const struct ethtool_ops sparx5_ethtool_ops;
 
 /* Calculate raw offset */
 static inline __pure int spx5_offset(int id, int tinst, int tcnt,
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c 
b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
index cf9dc7a340cb..27922c3fc0f5 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
@@ -177,6 +177,7 @@ static const struct net_device_ops sparx5_port_netdev_ops = 
{
        .ndo_get_phys_port_name = sparx5_port_get_phys_port_name,
        .ndo_set_mac_address    = sparx5_set_mac_address,
        .ndo_validate_addr      = eth_validate_addr,
+       .ndo_get_stats64        = sparx5_get_stats64,
        .ndo_get_port_parent_id = sparx5_get_port_parent_id,
 };
 
@@ -204,6 +205,7 @@ struct net_device *sparx5_create_netdev(struct sparx5 
*sparx5, u32 portno)
        snprintf(ndev->name, IFNAMSIZ, "eth%d", portno);
 
        ndev->netdev_ops = &sparx5_port_netdev_ops;
+       ndev->ethtool_ops = &sparx5_ethtool_ops;
        ndev->features |= NETIF_F_LLTX; /* software tx */
 
        val = ether_addr_to_u64(sparx5->base_mac) + portno + 1;
-- 
2.29.2

Reply via email to