Add statistics support for the port counters. Chip registers are 32 bit, so this unit is responsible maintaining a 64bit software cache, and updating it frequently to handle overflows in hardware. Reviewed-by: Steen Hegelund Signed-off-by: Jens Emil Schulz Østergaard --- drivers/net/dsa/microchip/lan9645x/Makefile | 1 + drivers/net/dsa/microchip/lan9645x/lan9645x_main.c | 82 ++ drivers/net/dsa/microchip/lan9645x/lan9645x_main.h | 3 + .../net/dsa/microchip/lan9645x/lan9645x_stats.c | 825 +++++++++++++++++++++ .../net/dsa/microchip/lan9645x/lan9645x_stats.h | 288 +++++++ 5 files changed, 1199 insertions(+) diff --git a/drivers/net/dsa/microchip/lan9645x/Makefile b/drivers/net/dsa/microchip/lan9645x/Makefile index a90a46f81c72..486b005cf740 100644 --- a/drivers/net/dsa/microchip/lan9645x/Makefile +++ b/drivers/net/dsa/microchip/lan9645x/Makefile @@ -7,3 +7,4 @@ mchp-lan9645x-objs := lan9645x_main.o \ lan9645x_phylink.o \ lan9645x_vlan.o \ lan9645x_mac.o \ + lan9645x_stats.o \ diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c index ba76279b4414..8a1de2588ab8 100644 --- a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c +++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c @@ -7,6 +7,7 @@ #include #include "lan9645x_main.h" +#include "lan9645x_stats.h" static const char *lan9645x_resource_names[NUM_TARGETS + 1] = { [TARGET_GCB] = "gcb", @@ -79,6 +80,7 @@ static void lan9645x_teardown(struct dsa_switch *ds) debugfs_remove_recursive(lan9645x->debugfs_root); lan9645x_npi_port_deinit(lan9645x, lan9645x->npi); lan9645x_mac_deinit(lan9645x); + lan9645x_stats_deinit(lan9645x); } static int lan9645x_change_mtu(struct dsa_switch *ds, int port, int new_mtu) @@ -274,6 +276,12 @@ static int lan9645x_setup(struct dsa_switch *ds) lan9645x_port_set_tail_drop_wm(lan9645x); + err = lan9645x_stats_init(lan9645x); + if (err) { + dev_err(dev, "Lan9645x setup: failed to init stats."); + return err; + } + ds->mtu_enforcement_ingress = true; ds->assisted_learning_on_cpu_port = true; ds->fdb_isolation = true; @@ -636,6 +644,68 @@ static int lan9645x_fdb_del(struct dsa_switch *ds, int port, return __lan9645x_fdb_del(lan9645x, port, addr, vid, br); } +static void lan9645x_get_strings(struct dsa_switch *ds, int port, u32 stringset, + uint8_t *data) +{ + lan9645x_stats_get_strings(ds->priv, port, stringset, data); +} + +static void lan9645x_get_ethtool_stats(struct dsa_switch *ds, int port, + uint64_t *data) +{ + lan9645x_stats_get_ethtool_stats(ds->priv, port, data); +} + +static int lan9645x_get_sset_count(struct dsa_switch *ds, int port, int sset) +{ + return lan9645x_stats_get_sset_count(ds->priv, port, sset); +} + +static void lan9645x_get_eth_mac_stats(struct dsa_switch *ds, int port, + struct ethtool_eth_mac_stats *mac_stats) +{ + lan9645x_stats_get_eth_mac_stats(ds->priv, port, mac_stats); +} + +static void +lan9645x_get_rmon_stats(struct dsa_switch *ds, int port, + struct ethtool_rmon_stats *rmon_stats, + const struct ethtool_rmon_hist_range **ranges) +{ + lan9645x_stats_get_rmon_stats(ds->priv, port, rmon_stats, ranges); +} + +static void lan9645x_get_stats64(struct dsa_switch *ds, int port, + struct rtnl_link_stats64 *s) +{ + lan9645x_stats_get_stats64(ds->priv, port, s); +} + +static void lan9645x_get_pause_stats(struct dsa_switch *ds, int port, + struct ethtool_pause_stats *pause_stats) +{ + lan9645x_stats_get_pause_stats(ds->priv, port, pause_stats); +} + +static void lan9645x_get_mm_stats(struct dsa_switch *ds, int port, + struct ethtool_mm_stats *stats) +{ + lan9645x_stats_get_mm_stats(ds->priv, port, stats); +} + +static void lan9645x_get_eth_phy_stats(struct dsa_switch *ds, int port, + struct ethtool_eth_phy_stats *phy_stats) +{ + lan9645x_stats_get_eth_phy_stats(ds->priv, port, phy_stats); +} + +static void +lan9645x_get_eth_ctrl_stats(struct dsa_switch *ds, int port, + struct ethtool_eth_ctrl_stats *ctrl_stats) +{ + lan9645x_stats_get_eth_ctrl_stats(ds->priv, port, ctrl_stats); +} + static const struct dsa_switch_ops lan9645x_switch_ops = { .get_tag_protocol = lan9645x_get_tag_protocol, .connect_tag_protocol = lan9645x_connect_tag_protocol, @@ -668,6 +738,18 @@ static const struct dsa_switch_ops lan9645x_switch_ops = { .port_fdb_dump = lan9645x_fdb_dump, .port_fdb_add = lan9645x_fdb_add, .port_fdb_del = lan9645x_fdb_del, + + /* Port statistics counters. */ + .get_strings = lan9645x_get_strings, + .get_ethtool_stats = lan9645x_get_ethtool_stats, + .get_sset_count = lan9645x_get_sset_count, + .get_eth_mac_stats = lan9645x_get_eth_mac_stats, + .get_rmon_stats = lan9645x_get_rmon_stats, + .get_stats64 = lan9645x_get_stats64, + .get_pause_stats = lan9645x_get_pause_stats, + .get_mm_stats = lan9645x_get_mm_stats, + .get_eth_phy_stats = lan9645x_get_eth_phy_stats, + .get_eth_ctrl_stats = lan9645x_get_eth_ctrl_stats, }; static int lan9645x_request_target_regmaps(struct lan9645x *lan9645x) diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h index 4c7111375918..fe801d0ed39a 100644 --- a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h +++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.h @@ -217,6 +217,9 @@ struct lan9645x { u8 vlan_flags[VLAN_N_VID]; DECLARE_BITMAP(cpu_vlan_mask, VLAN_N_VID); /* CPU VLAN membership */ + /* Statistics */ + struct lan9645x_stats *stats; + int num_port_dis; bool dd_dis; bool tsn_dis; diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_stats.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_stats.c new file mode 100644 index 000000000000..43078e441e55 --- /dev/null +++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_stats.c @@ -0,0 +1,825 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (C) 2026 Microchip Technology Inc. + */ + +#include +#include + +#include "lan9645x_main.h" +#include "lan9645x_stats.h" + +#define LAN9645X_STATS_CHECK_DELAY (3 * HZ) + +static const struct lan9645x_stat_region lan9645x_port_stat_regions[] = { + /* RX region */ + { .base_offset = 0x0, .cnt = 67, .cnts_base_idx = 0 }, + /* TX region */ + { .base_offset = 0x80, .cnt = 48, .cnts_base_idx = 67 }, + /* DR region */ + { .base_offset = 0x100, .cnt = 18, .cnts_base_idx = 115 }, +}; + +static const struct lan9645x_stat_layout lan9645x_port_stats_layout[] = { + { .name = "rx_oct", .offset = 0x0 }, + { .name = "rx_uc", .offset = 0x1 }, + { .name = "rx_mc", .offset = 0x2 }, + { .name = "rx_bc", .offset = 0x3 }, + { .name = "rx_short", .offset = 0x4 }, + { .name = "rx_frag", .offset = 0x5 }, + { .name = "rx_jabber", .offset = 0x6 }, + { .name = "rx_crc", .offset = 0x7 }, + { .name = "rx_symbol_err", .offset = 0x8 }, + { .name = "rx_sz_64", .offset = 0x9 }, + { .name = "rx_sz_65_127", .offset = 0xa }, + { .name = "rx_sz_128_255", .offset = 0xb }, + { .name = "rx_sz_256_511", .offset = 0xc }, + { .name = "rx_sz_512_1023", .offset = 0xd }, + { .name = "rx_sz_1024_1526", .offset = 0xe }, + { .name = "rx_sz_jumbo", .offset = 0xf }, + { .name = "rx_pause", .offset = 0x10 }, + { .name = "rx_control", .offset = 0x11 }, + { .name = "rx_long", .offset = 0x12 }, + { .name = "rx_cat_drop", .offset = 0x13 }, + { .name = "rx_red_prio_0", .offset = 0x14 }, + { .name = "rx_red_prio_1", .offset = 0x15 }, + { .name = "rx_red_prio_2", .offset = 0x16 }, + { .name = "rx_red_prio_3", .offset = 0x17 }, + { .name = "rx_red_prio_4", .offset = 0x18 }, + { .name = "rx_red_prio_5", .offset = 0x19 }, + { .name = "rx_red_prio_6", .offset = 0x1a }, + { .name = "rx_red_prio_7", .offset = 0x1b }, + { .name = "rx_yellow_prio_0", .offset = 0x1c }, + { .name = "rx_yellow_prio_1", .offset = 0x1d }, + { .name = "rx_yellow_prio_2", .offset = 0x1e }, + { .name = "rx_yellow_prio_3", .offset = 0x1f }, + { .name = "rx_yellow_prio_4", .offset = 0x20 }, + { .name = "rx_yellow_prio_5", .offset = 0x21 }, + { .name = "rx_yellow_prio_6", .offset = 0x22 }, + { .name = "rx_yellow_prio_7", .offset = 0x23 }, + { .name = "rx_green_prio_0", .offset = 0x24 }, + { .name = "rx_green_prio_1", .offset = 0x25 }, + { .name = "rx_green_prio_2", .offset = 0x26 }, + { .name = "rx_green_prio_3", .offset = 0x27 }, + { .name = "rx_green_prio_4", .offset = 0x28 }, + { .name = "rx_green_prio_5", .offset = 0x29 }, + { .name = "rx_green_prio_6", .offset = 0x2a }, + { .name = "rx_green_prio_7", .offset = 0x2b }, + { .name = "rx_assembly_err", .offset = 0x2c }, + { .name = "rx_smd_err", .offset = 0x2d }, + { .name = "rx_assembly_ok", .offset = 0x2e }, + { .name = "rx_merge_frag", .offset = 0x2f }, + { .name = "rx_pmac_oct", .offset = 0x30 }, + { .name = "rx_pmac_uc", .offset = 0x31 }, + { .name = "rx_pmac_mc", .offset = 0x32 }, + { .name = "rx_pmac_bc", .offset = 0x33 }, + { .name = "rx_pmac_short", .offset = 0x34 }, + { .name = "rx_pmac_frag", .offset = 0x35 }, + { .name = "rx_pmac_jabber", .offset = 0x36 }, + { .name = "rx_pmac_crc", .offset = 0x37 }, + { .name = "rx_pmac_symbol_err", .offset = 0x38 }, + { .name = "rx_pmac_sz_64", .offset = 0x39 }, + { .name = "rx_pmac_sz_65_127", .offset = 0x3a }, + { .name = "rx_pmac_sz_128_255", .offset = 0x3b }, + { .name = "rx_pmac_sz_256_511", .offset = 0x3c }, + { .name = "rx_pmac_sz_512_1023", .offset = 0x3d }, + { .name = "rx_pmac_sz_1024_1526", .offset = 0x3e }, + { .name = "rx_pmac_sz_jumbo", .offset = 0x3f }, + { .name = "rx_pmac_pause", .offset = 0x40 }, + { .name = "rx_pmac_control", .offset = 0x41 }, + { .name = "rx_pmac_long", .offset = 0x42 }, + { .name = "tx_oct", .offset = 0x80 }, + { .name = "tx_uc", .offset = 0x81 }, + { .name = "tx_mc", .offset = 0x82 }, + { .name = "tx_bc", .offset = 0x83 }, + { .name = "tx_col", .offset = 0x84 }, + { .name = "tx_drop", .offset = 0x85 }, + { .name = "tx_pause", .offset = 0x86 }, + { .name = "tx_sz_64", .offset = 0x87 }, + { .name = "tx_sz_65_127", .offset = 0x88 }, + { .name = "tx_sz_128_255", .offset = 0x89 }, + { .name = "tx_sz_256_511", .offset = 0x8a }, + { .name = "tx_sz_512_1023", .offset = 0x8b }, + { .name = "tx_sz_1024_1526", .offset = 0x8c }, + { .name = "tx_sz_jumbo", .offset = 0x8d }, + { .name = "tx_yellow_prio_0", .offset = 0x8e }, + { .name = "tx_yellow_prio_1", .offset = 0x8f }, + { .name = "tx_yellow_prio_2", .offset = 0x90 }, + { .name = "tx_yellow_prio_3", .offset = 0x91 }, + { .name = "tx_yellow_prio_4", .offset = 0x92 }, + { .name = "tx_yellow_prio_5", .offset = 0x93 }, + { .name = "tx_yellow_prio_6", .offset = 0x94 }, + { .name = "tx_yellow_prio_7", .offset = 0x95 }, + { .name = "tx_green_prio_0", .offset = 0x96 }, + { .name = "tx_green_prio_1", .offset = 0x97 }, + { .name = "tx_green_prio_2", .offset = 0x98 }, + { .name = "tx_green_prio_3", .offset = 0x99 }, + { .name = "tx_green_prio_4", .offset = 0x9a }, + { .name = "tx_green_prio_5", .offset = 0x9b }, + { .name = "tx_green_prio_6", .offset = 0x9c }, + { .name = "tx_green_prio_7", .offset = 0x9d }, + { .name = "tx_aged", .offset = 0x9e }, + { .name = "tx_llct", .offset = 0x9f }, + { .name = "tx_ct", .offset = 0xa0 }, + { .name = "tx_bufdrop", .offset = 0xa1 }, + { .name = "tx_mm_hold", .offset = 0xa2 }, + { .name = "tx_merge_frag", .offset = 0xa3 }, + { .name = "tx_pmac_oct", .offset = 0xa4 }, + { .name = "tx_pmac_uc", .offset = 0xa5 }, + { .name = "tx_pmac_mc", .offset = 0xa6 }, + { .name = "tx_pmac_bc", .offset = 0xa7 }, + { .name = "tx_pmac_pause", .offset = 0xa8 }, + { .name = "tx_pmac_sz_64", .offset = 0xa9 }, + { .name = "tx_pmac_sz_65_127", .offset = 0xaa }, + { .name = "tx_pmac_sz_128_255", .offset = 0xab }, + { .name = "tx_pmac_sz_256_511", .offset = 0xac }, + { .name = "tx_pmac_sz_512_1023", .offset = 0xad }, + { .name = "tx_pmac_sz_1024_1526", .offset = 0xae }, + { .name = "tx_pmac_sz_jumbo", .offset = 0xaf }, + { .name = "dr_local", .offset = 0x100 }, + { .name = "dr_tail", .offset = 0x101 }, + { .name = "dr_yellow_prio_0", .offset = 0x102 }, + { .name = "dr_yellow_prio_1", .offset = 0x103 }, + { .name = "dr_yellow_prio_2", .offset = 0x104 }, + { .name = "dr_yellow_prio_3", .offset = 0x105 }, + { .name = "dr_yellow_prio_4", .offset = 0x106 }, + { .name = "dr_yellow_prio_5", .offset = 0x107 }, + { .name = "dr_yellow_prio_6", .offset = 0x108 }, + { .name = "dr_yellow_prio_7", .offset = 0x109 }, + { .name = "dr_green_prio_0", .offset = 0x10a }, + { .name = "dr_green_prio_1", .offset = 0x10b }, + { .name = "dr_green_prio_2", .offset = 0x10c }, + { .name = "dr_green_prio_3", .offset = 0x10d }, + { .name = "dr_green_prio_4", .offset = 0x10e }, + { .name = "dr_green_prio_5", .offset = 0x10f }, + { .name = "dr_green_prio_6", .offset = 0x110 }, + { .name = "dr_green_prio_7", .offset = 0x111 }, +}; + +static const struct lan9645x_view_stats lan9645x_view_stat_cfgs[] = { + [LAN9645X_STAT_PORTS] = { + .name = "ports", + .type = LAN9645X_STAT_PORTS, + .layout = lan9645x_port_stats_layout, + .num_cnts = ARRAY_SIZE(lan9645x_port_stats_layout), + .num_indexes = NUM_PHYS_PORTS, + .regions = lan9645x_port_stat_regions, + .num_regions = ARRAY_SIZE(lan9645x_port_stat_regions), + }, +}; + +static void __lan9645x_stats_view_idx_update(struct lan9645x *lan9645x, + enum lan9645x_view_stat_type vtype, + int idx) +{ + struct lan9645x_stat_region region; + struct lan9645x_view_stats *vstats; + u64 *idx_counters; + u32 *region_buf; + int cntr; + int err; + + lockdep_assert_held(&lan9645x->stats->hw_lock); + + vstats = lan9645x_get_vstats(lan9645x, vtype); + if (!vstats || idx < 0 || idx >= vstats->num_indexes) + return; + + lan_wr(SYS_STAT_CFG_STAT_VIEW_SET(idx), lan9645x, SYS_STAT_CFG); + + idx_counters = STATS_INDEX(vstats, idx); + region_buf = &vstats->buf[vstats->num_cnts * idx]; + + /* Each region for this index contains counters which are at sequential + * addresses, so we can use bulk reads to ease lock pressure a bit. + */ + for (int r = 0; r < vstats->num_regions; r++) { + region = vstats->regions[r]; + err = lan_bulk_rd(®ion_buf[region.cnts_base_idx], region.cnt, + lan9645x, SYS_CNT(region.base_offset)); + if (err) { + dev_err(lan9645x->dev, + "stats bulk read err vtype=%d idx=%d err=%d", + vtype, idx, err); + return; + } + } + + for (cntr = 0; cntr < vstats->num_cnts; cntr++) + lan9645x_stats_add_cnt(&idx_counters[cntr], region_buf[cntr]); +} + +void lan9645x_stats_view_idx_update(struct lan9645x *lan9645x, + enum lan9645x_view_stat_type vtype, int idx) +{ + struct lan9645x_stats *s = lan9645x->stats; + + mutex_lock(&s->hw_lock); + __lan9645x_stats_view_idx_update(lan9645x, vtype, idx); + mutex_unlock(&s->hw_lock); +} + +void lan9645x_stats_view_update(struct lan9645x *lan9645x, + enum lan9645x_view_stat_type vtype) +{ + struct lan9645x_stats *s = lan9645x->stats; + struct lan9645x_view_stats *vstats; + int idx = 0; + + vstats = lan9645x_get_vstats(lan9645x, vtype); + if (!vstats) + return; + + switch (vtype) { + case LAN9645X_STAT_PORTS: + mutex_lock(&s->hw_lock); + for (idx = 0; idx < vstats->num_indexes; idx++) { + if (lan9645x_port_is_used(lan9645x, idx)) + __lan9645x_stats_view_idx_update(lan9645x, + vtype, idx); + } + mutex_unlock(&s->hw_lock); + return; + default: + return; + } +} + +static void lan9645x_stats_update(struct lan9645x *lan9645x) +{ + for (int vtype = 0; vtype < LAN9645X_STAT_NUM; vtype++) + lan9645x_stats_view_update(lan9645x, vtype); +} + +void lan9645x_stats_get_strings(struct lan9645x *lan9645x, int port, + u32 stringset, u8 *data) +{ + struct lan9645x_view_stats *port_stats; + int i; + + if (stringset != ETH_SS_STATS) + return; + + port_stats = lan9645x_get_vstats(lan9645x, LAN9645X_STAT_PORTS); + + for (i = 0; i < port_stats->num_cnts; i++) + memcpy(data + i * ETH_GSTRING_LEN, port_stats->layout[i].name, + ETH_GSTRING_LEN); +} + +int lan9645x_stats_get_sset_count(struct lan9645x *lan9645x, int port, int sset) +{ + struct lan9645x_view_stats *port_stats; + + if (sset != ETH_SS_STATS) + return -EOPNOTSUPP; + + port_stats = lan9645x_get_vstats(lan9645x, LAN9645X_STAT_PORTS); + + return port_stats->num_cnts; +} + +void lan9645x_stats_get_ethtool_stats(struct lan9645x *lan9645x, int port, + u64 *data) +{ + struct lan9645x_view_stats *port_stats; + int cntr; + u64 *s; + + mutex_lock(&lan9645x->stats->hw_lock); + + __lan9645x_stats_view_idx_update(lan9645x, LAN9645X_STAT_PORTS, port); + + port_stats = lan9645x_get_vstats(lan9645x, LAN9645X_STAT_PORTS); + + s = STATS_INDEX(port_stats, port); + + for (cntr = 0; cntr < port_stats->num_cnts; cntr++) + *data++ = s[cntr]; + + mutex_unlock(&lan9645x->stats->hw_lock); +} + +void lan9645x_stats_get_eth_mac_stats(struct lan9645x *lan9645x, int port, + struct ethtool_eth_mac_stats *mac_stats) +{ + u64 *port_counters; + + mutex_lock(&lan9645x->stats->hw_lock); + + __lan9645x_stats_view_idx_update(lan9645x, LAN9645X_STAT_PORTS, port); + + port_counters = STAT_COUNTERS(lan9645x, LAN9645X_STAT_PORTS, port); + + mac_stats->FramesTransmittedOK = + port_counters[SCNT_TX_UC] + + port_counters[SCNT_TX_MC] + + port_counters[SCNT_TX_BC] + + port_counters[SCNT_TX_PMAC_UC] + + port_counters[SCNT_TX_PMAC_MC] + + port_counters[SCNT_TX_PMAC_BC]; + mac_stats->SingleCollisionFrames = port_counters[SCNT_TX_COL]; + mac_stats->FramesReceivedOK = port_counters[SCNT_RX_UC] + + port_counters[SCNT_RX_MC] + + port_counters[SCNT_RX_BC]; + mac_stats->FrameCheckSequenceErrors = + port_counters[SCNT_RX_CRC] + + port_counters[SCNT_RX_PMAC_CRC]; + mac_stats->OctetsTransmittedOK = + port_counters[SCNT_TX_OCT] + + port_counters[SCNT_TX_PMAC_OCT]; + mac_stats->FramesWithDeferredXmissions = port_counters[SCNT_TX_MM_HOLD]; + mac_stats->OctetsReceivedOK = + port_counters[SCNT_RX_OCT] + + port_counters[SCNT_RX_PMAC_OCT]; + mac_stats->MulticastFramesXmittedOK = + port_counters[SCNT_TX_MC] + + port_counters[SCNT_TX_PMAC_MC]; + mac_stats->BroadcastFramesXmittedOK = + port_counters[SCNT_TX_BC] + + port_counters[SCNT_TX_PMAC_BC]; + mac_stats->MulticastFramesReceivedOK = + port_counters[SCNT_RX_MC] + + port_counters[SCNT_RX_PMAC_MC]; + mac_stats->BroadcastFramesReceivedOK = + port_counters[SCNT_RX_BC] + + port_counters[SCNT_RX_PMAC_BC]; + mac_stats->InRangeLengthErrors = + port_counters[SCNT_RX_FRAG] + + port_counters[SCNT_RX_JABBER] + + port_counters[SCNT_RX_CRC] + + port_counters[SCNT_RX_PMAC_FRAG] + + port_counters[SCNT_RX_PMAC_JABBER] + + port_counters[SCNT_RX_PMAC_CRC]; + mac_stats->OutOfRangeLengthField = port_counters[SCNT_RX_SHORT] + + port_counters[SCNT_RX_PMAC_SHORT] + + port_counters[SCNT_RX_LONG] + + port_counters[SCNT_RX_PMAC_LONG]; + mac_stats->FrameTooLongErrors = + port_counters[SCNT_RX_LONG] + + port_counters[SCNT_RX_PMAC_LONG]; + + mutex_unlock(&lan9645x->stats->hw_lock); +} + +static const struct ethtool_rmon_hist_range lan9645x_rmon_ranges[] = { + { 0, 64 }, + { 65, 127 }, + { 128, 255 }, + { 256, 511 }, + { 512, 1023 }, + { 1024, 1526 }, + { 1527, 0xffff }, + {} +}; + +void +lan9645x_stats_get_rmon_stats(struct lan9645x *lan9645x, int port, + struct ethtool_rmon_stats *rmon_stats, + const struct ethtool_rmon_hist_range **ranges) +{ + u64 *port_cnt; + + mutex_lock(&lan9645x->stats->hw_lock); + + __lan9645x_stats_view_idx_update(lan9645x, LAN9645X_STAT_PORTS, port); + + port_cnt = STAT_COUNTERS(lan9645x, LAN9645X_STAT_PORTS, port); + + rmon_stats->undersize_pkts = + port_cnt[SCNT_RX_SHORT] + + port_cnt[SCNT_RX_PMAC_SHORT]; + rmon_stats->oversize_pkts = + port_cnt[SCNT_RX_LONG] + + port_cnt[SCNT_RX_PMAC_LONG]; + rmon_stats->fragments = + port_cnt[SCNT_RX_FRAG] + + port_cnt[SCNT_RX_PMAC_FRAG]; + rmon_stats->jabbers = + port_cnt[SCNT_RX_JABBER] + + port_cnt[SCNT_RX_PMAC_JABBER]; + + rmon_stats->hist[0] = + port_cnt[SCNT_RX_SZ_64] + + port_cnt[SCNT_RX_PMAC_SZ_64]; + rmon_stats->hist[1] = + port_cnt[SCNT_RX_SZ_65_127] + + port_cnt[SCNT_RX_PMAC_SZ_65_127]; + rmon_stats->hist[2] = + port_cnt[SCNT_RX_SZ_128_255] + + port_cnt[SCNT_RX_PMAC_SZ_128_255]; + rmon_stats->hist[3] = + port_cnt[SCNT_RX_SZ_256_511] + + port_cnt[SCNT_RX_PMAC_SZ_256_511]; + rmon_stats->hist[4] = + port_cnt[SCNT_RX_SZ_512_1023] + + port_cnt[SCNT_RX_PMAC_SZ_512_1023]; + rmon_stats->hist[5] = + port_cnt[SCNT_RX_SZ_1024_1526] + + port_cnt[SCNT_RX_PMAC_SZ_1024_1526]; + rmon_stats->hist[6] = + port_cnt[SCNT_RX_SZ_JUMBO] + + port_cnt[SCNT_RX_PMAC_SZ_JUMBO]; + + rmon_stats->hist_tx[0] = + port_cnt[SCNT_TX_SZ_64] + + port_cnt[SCNT_TX_PMAC_SZ_64]; + rmon_stats->hist_tx[1] = + port_cnt[SCNT_TX_SZ_65_127] + + port_cnt[SCNT_TX_PMAC_SZ_65_127]; + rmon_stats->hist_tx[2] = + port_cnt[SCNT_TX_SZ_128_255] + + port_cnt[SCNT_TX_PMAC_SZ_128_255]; + rmon_stats->hist_tx[3] = + port_cnt[SCNT_TX_SZ_256_511] + + port_cnt[SCNT_TX_PMAC_SZ_256_511]; + rmon_stats->hist_tx[4] = + port_cnt[SCNT_TX_SZ_512_1023] + + port_cnt[SCNT_TX_PMAC_SZ_512_1023]; + rmon_stats->hist_tx[5] = + port_cnt[SCNT_TX_SZ_1024_1526] + + port_cnt[SCNT_TX_PMAC_SZ_1024_1526]; + rmon_stats->hist_tx[6] = + port_cnt[SCNT_TX_SZ_JUMBO] + + port_cnt[SCNT_TX_PMAC_SZ_JUMBO]; + + mutex_unlock(&lan9645x->stats->hw_lock); + + *ranges = lan9645x_rmon_ranges; +} + +void lan9645x_stats_get_stats64(struct lan9645x *lan9645x, int port, + struct rtnl_link_stats64 *stats) +{ + u64 *port_cnt; + + /* Avoid stats update, as this is called very often by DSA. */ + mutex_lock(&lan9645x->stats->hw_lock); + + port_cnt = STAT_COUNTERS(lan9645x, LAN9645X_STAT_PORTS, port); + + stats->rx_bytes = port_cnt[SCNT_RX_OCT] + + port_cnt[SCNT_RX_PMAC_OCT]; + + stats->rx_packets = port_cnt[SCNT_RX_SHORT] + + port_cnt[SCNT_RX_FRAG] + + port_cnt[SCNT_RX_JABBER] + + port_cnt[SCNT_RX_CRC] + + port_cnt[SCNT_RX_SYMBOL_ERR] + + port_cnt[SCNT_RX_SZ_64] + + port_cnt[SCNT_RX_SZ_65_127] + + port_cnt[SCNT_RX_SZ_128_255] + + port_cnt[SCNT_RX_SZ_256_511] + + port_cnt[SCNT_RX_SZ_512_1023] + + port_cnt[SCNT_RX_SZ_1024_1526] + + port_cnt[SCNT_RX_SZ_JUMBO] + + port_cnt[SCNT_RX_LONG] + + port_cnt[SCNT_RX_PMAC_SHORT] + + port_cnt[SCNT_RX_PMAC_FRAG] + + port_cnt[SCNT_RX_PMAC_JABBER] + + port_cnt[SCNT_RX_PMAC_SZ_64] + + port_cnt[SCNT_RX_PMAC_SZ_65_127] + + port_cnt[SCNT_RX_PMAC_SZ_128_255] + + port_cnt[SCNT_RX_PMAC_SZ_256_511] + + port_cnt[SCNT_RX_PMAC_SZ_512_1023] + + port_cnt[SCNT_RX_PMAC_SZ_1024_1526] + + port_cnt[SCNT_RX_PMAC_SZ_JUMBO]; + + stats->multicast = port_cnt[SCNT_RX_MC] + + port_cnt[SCNT_RX_PMAC_MC]; + + stats->rx_errors = port_cnt[SCNT_RX_SHORT] + + port_cnt[SCNT_RX_FRAG] + + port_cnt[SCNT_RX_JABBER] + + port_cnt[SCNT_RX_CRC] + + port_cnt[SCNT_RX_SYMBOL_ERR] + + port_cnt[SCNT_RX_LONG] + + port_cnt[SCNT_RX_PMAC_SHORT] + + port_cnt[SCNT_RX_PMAC_FRAG] + + port_cnt[SCNT_RX_PMAC_JABBER] + + port_cnt[SCNT_RX_PMAC_CRC] + + port_cnt[SCNT_RX_PMAC_SYMBOL_ERR] + + port_cnt[SCNT_RX_PMAC_LONG]; + + stats->rx_dropped = port_cnt[SCNT_RX_LONG] + + port_cnt[SCNT_DR_LOCAL] + + port_cnt[SCNT_DR_TAIL] + + port_cnt[SCNT_RX_CAT_DROP] + + port_cnt[SCNT_RX_RED_PRIO_0] + + port_cnt[SCNT_RX_RED_PRIO_1] + + port_cnt[SCNT_RX_RED_PRIO_2] + + port_cnt[SCNT_RX_RED_PRIO_3] + + port_cnt[SCNT_RX_RED_PRIO_4] + + port_cnt[SCNT_RX_RED_PRIO_5] + + port_cnt[SCNT_RX_RED_PRIO_6] + + port_cnt[SCNT_RX_RED_PRIO_7]; + + for (int i = 0; i < LAN9645X_NUM_TC; i++) { + stats->rx_dropped += port_cnt[SCNT_DR_YELLOW_PRIO_0 + i] + + port_cnt[SCNT_DR_GREEN_PRIO_0 + i]; + } + + stats->tx_bytes = port_cnt[SCNT_TX_OCT] + + port_cnt[SCNT_TX_PMAC_OCT]; + + stats->tx_packets = port_cnt[SCNT_TX_SZ_64] + + port_cnt[SCNT_TX_SZ_65_127] + + port_cnt[SCNT_TX_SZ_128_255] + + port_cnt[SCNT_TX_SZ_256_511] + + port_cnt[SCNT_TX_SZ_512_1023] + + port_cnt[SCNT_TX_SZ_1024_1526] + + port_cnt[SCNT_TX_SZ_JUMBO] + + port_cnt[SCNT_TX_PMAC_SZ_64] + + port_cnt[SCNT_TX_PMAC_SZ_65_127] + + port_cnt[SCNT_TX_PMAC_SZ_128_255] + + port_cnt[SCNT_TX_PMAC_SZ_256_511] + + port_cnt[SCNT_TX_PMAC_SZ_512_1023] + + port_cnt[SCNT_TX_PMAC_SZ_1024_1526] + + port_cnt[SCNT_TX_PMAC_SZ_JUMBO]; + + stats->tx_dropped = port_cnt[SCNT_TX_DROP] + + port_cnt[SCNT_TX_AGED]; + + stats->collisions = port_cnt[SCNT_TX_COL]; + + mutex_unlock(&lan9645x->stats->hw_lock); +} + +void lan9645x_stats_get_eth_phy_stats(struct lan9645x *lan9645x, int port, + struct ethtool_eth_phy_stats *phy_stats) +{ + u64 *port_cnt; + + mutex_lock(&lan9645x->stats->hw_lock); + + __lan9645x_stats_view_idx_update(lan9645x, LAN9645X_STAT_PORTS, port); + + port_cnt = STAT_COUNTERS(lan9645x, LAN9645X_STAT_PORTS, port); + + switch (phy_stats->src) { + case ETHTOOL_MAC_STATS_SRC_EMAC: + phy_stats->SymbolErrorDuringCarrier = + port_cnt[SCNT_RX_SYMBOL_ERR]; + break; + case ETHTOOL_MAC_STATS_SRC_PMAC: + phy_stats->SymbolErrorDuringCarrier = + port_cnt[SCNT_RX_PMAC_SYMBOL_ERR]; + break; + default: + break; + } + + mutex_unlock(&lan9645x->stats->hw_lock); +} + +void +lan9645x_stats_get_eth_ctrl_stats(struct lan9645x *lan9645x, int port, + struct ethtool_eth_ctrl_stats *ctrl_stats) +{ + u64 *port_cnt; + + mutex_lock(&lan9645x->stats->hw_lock); + + __lan9645x_stats_view_idx_update(lan9645x, LAN9645X_STAT_PORTS, port); + + port_cnt = STAT_COUNTERS(lan9645x, LAN9645X_STAT_PORTS, port); + + switch (ctrl_stats->src) { + case ETHTOOL_MAC_STATS_SRC_EMAC: + ctrl_stats->MACControlFramesReceived = + port_cnt[SCNT_RX_CONTROL]; + break; + case ETHTOOL_MAC_STATS_SRC_PMAC: + ctrl_stats->MACControlFramesReceived = + port_cnt[SCNT_RX_PMAC_CONTROL]; + break; + default: + break; + } + + mutex_unlock(&lan9645x->stats->hw_lock); +} + +void lan9645x_stats_get_pause_stats(struct lan9645x *lan9645x, int port, + struct ethtool_pause_stats *ps) +{ + u64 *port_cnt; + + mutex_lock(&lan9645x->stats->hw_lock); + + __lan9645x_stats_view_idx_update(lan9645x, LAN9645X_STAT_PORTS, port); + + port_cnt = STAT_COUNTERS(lan9645x, LAN9645X_STAT_PORTS, port); + + switch (ps->src) { + case ETHTOOL_MAC_STATS_SRC_EMAC: + ps->tx_pause_frames = port_cnt[SCNT_TX_PAUSE]; + ps->rx_pause_frames = port_cnt[SCNT_RX_PAUSE]; + break; + case ETHTOOL_MAC_STATS_SRC_PMAC: + ps->tx_pause_frames = port_cnt[SCNT_TX_PMAC_PAUSE]; + ps->rx_pause_frames = port_cnt[SCNT_RX_PMAC_PAUSE]; + break; + default: + break; + } + + mutex_unlock(&lan9645x->stats->hw_lock); +} + +void lan9645x_stats_get_mm_stats(struct lan9645x *lan9645x, int port, + struct ethtool_mm_stats *stats) +{ + u64 *port_cnt; + + mutex_lock(&lan9645x->stats->hw_lock); + + __lan9645x_stats_view_idx_update(lan9645x, LAN9645X_STAT_PORTS, port); + + port_cnt = STAT_COUNTERS(lan9645x, LAN9645X_STAT_PORTS, port); + + stats->MACMergeFrameAssErrorCount = port_cnt[SCNT_RX_ASSEMBLY_ERR]; + stats->MACMergeFrameSmdErrorCount = port_cnt[SCNT_RX_SMD_ERR]; + stats->MACMergeFrameAssOkCount = port_cnt[SCNT_RX_ASSEMBLY_OK]; + stats->MACMergeFragCountRx = port_cnt[SCNT_RX_MERGE_FRAG]; + stats->MACMergeFragCountTx = port_cnt[SCNT_TX_MERGE_FRAG]; + stats->MACMergeHoldCount = port_cnt[SCNT_TX_MM_HOLD]; + + mutex_unlock(&lan9645x->stats->hw_lock); +} + +void lan9645x_stats_clear_counters(struct lan9645x *lan9645x, + enum lan9645x_view_stat_type type, int idx) +{ + struct lan9645x_view_stats *vstats = + lan9645x_get_vstats(lan9645x, type); + u64 *idx_grp; + int cntr; + u32 sel; + + switch (type) { + case LAN9645X_STAT_PORTS: + /* Drop, TX and RX counters */ + sel = BIT(2) | BIT(1) | BIT(0); + break; + case LAN9645X_STAT_ISDX: + /* ISDX and FRER seq gen */ + sel = BIT(5) | BIT(3); + break; + case LAN9645X_STAT_ESDX: + /* ESDX */ + sel = BIT(6); + break; + case LAN9645X_STAT_SFID: + /* Stream filter */ + sel = BIT(4); + break; + default: + return; + } + + mutex_lock(&lan9645x->stats->hw_lock); + + lan_wr(SYS_STAT_CFG_STAT_CLEAR_SHOT_SET(sel) | + SYS_STAT_CFG_STAT_VIEW_SET(idx), + lan9645x, SYS_STAT_CFG); + + idx_grp = STATS_INDEX(vstats, idx); + for (cntr = 0; cntr < vstats->num_cnts; cntr++) + idx_grp[cntr] = 0; + + mutex_unlock(&lan9645x->stats->hw_lock); +} + +static void lan9645x_check_stats_work(struct work_struct *work) +{ + struct delayed_work *del_work = to_delayed_work(work); + struct lan9645x_stats *stats; + + stats = container_of(del_work, struct lan9645x_stats, work); + + lan9645x_stats_update(stats->lan9645x); + + queue_delayed_work(stats->queue, &stats->work, + LAN9645X_STATS_CHECK_DELAY); +} + +static int lan9645x_stats_debugfs_show(struct seq_file *m, void *unused) +{ + struct lan9645x_view_stats *vstats = m->private; + int idx, cntr; + size_t total; + u64 *snap; + + total = vstats->num_cnts * vstats->num_indexes; + + /* Snapshot counters under lock to avoid holding hw_lock during + * slow seq_printf output. + */ + snap = kmalloc_array(total, sizeof(u64), GFP_KERNEL); + if (!snap) + return -ENOMEM; + + mutex_lock(&vstats->stats->hw_lock); + memcpy(snap, vstats->cnts, total * sizeof(u64)); + mutex_unlock(&vstats->stats->hw_lock); + + for (idx = 0; idx < vstats->num_indexes; idx++) { + for (cntr = 0; cntr < vstats->num_cnts; cntr++) { + seq_printf(m, "%s_%d_%-*s %llu\n", vstats->name, idx, + 30, vstats->layout[cntr].name, + snap[vstats->num_cnts * idx + cntr]); + } + } + + kfree(snap); + return 0; +} +DEFINE_SHOW_ATTRIBUTE(lan9645x_stats_debugfs); + +static void lan9645x_stats_debugfs(struct lan9645x *lan9645x, + struct dentry *parent) +{ + struct lan9645x_stats *stats = lan9645x->stats; + struct dentry *dir; + int i; + + dir = debugfs_create_dir("stats", parent); + if (PTR_ERR_OR_ZERO(dir)) + return; + + for (i = 0; i < ARRAY_SIZE(stats->view); i++) + debugfs_create_file(stats->view[i].name, 0444, dir, + &stats->view[i], + &lan9645x_stats_debugfs_fops); +} + +static int lan9645x_view_stat_init(struct lan9645x *lan9645x, + struct lan9645x_view_stats *vstat, + const struct lan9645x_view_stats *cfg) +{ + size_t total = cfg->num_cnts * cfg->num_indexes; + + memcpy(vstat, cfg, sizeof(*cfg)); + + vstat->cnts = devm_kcalloc(lan9645x->dev, total, sizeof(u64), + GFP_KERNEL); + if (!vstat->cnts) + return -ENOMEM; + + vstat->buf = devm_kcalloc(lan9645x->dev, total, sizeof(u32), + GFP_KERNEL); + if (!vstat->buf) + return -ENOMEM; + + vstat->stats = lan9645x->stats; + + return 0; +} + +int lan9645x_stats_init(struct lan9645x *lan9645x) +{ + const struct lan9645x_view_stats *vs; + struct lan9645x_stats *stats; + int err, i; + + lan9645x->stats = devm_kzalloc(lan9645x->dev, sizeof(*stats), + GFP_KERNEL); + if (!lan9645x->stats) + return -ENOMEM; + + stats = lan9645x->stats; + stats->lan9645x = lan9645x; + + mutex_init(&stats->hw_lock); + stats->queue = alloc_ordered_workqueue("lan9645x-stats", 0); + if (!stats->queue) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(lan9645x_view_stat_cfgs); i++) { + vs = &lan9645x_view_stat_cfgs[i]; + + if (!vs->num_cnts) + continue; + + err = lan9645x_view_stat_init(lan9645x, &stats->view[vs->type], + vs); + if (err) + return err; + } + + INIT_DELAYED_WORK(&stats->work, lan9645x_check_stats_work); + queue_delayed_work(stats->queue, &stats->work, + LAN9645X_STATS_CHECK_DELAY); + + lan9645x_stats_debugfs(lan9645x, lan9645x->debugfs_root); + + return 0; +} + +void lan9645x_stats_deinit(struct lan9645x *lan9645x) +{ + cancel_delayed_work_sync(&lan9645x->stats->work); + destroy_workqueue(lan9645x->stats->queue); + mutex_destroy(&lan9645x->stats->hw_lock); + lan9645x->stats->queue = NULL; +} diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_stats.h b/drivers/net/dsa/microchip/lan9645x/lan9645x_stats.h new file mode 100644 index 000000000000..0bb1e1140cc7 --- /dev/null +++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_stats.h @@ -0,0 +1,288 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (C) 2026 Microchip Technology Inc. + */ + +#ifndef _LAN9645X_STATS_H_ +#define _LAN9645X_STATS_H_ + +#include "lan9645x_main.h" + +#define STATS_INDEX(vstats, idx) (&(vstats)->cnts[(vstats)->num_cnts * (idx)]) + +#define STAT_COUNTERS(lan9645x, type, idx) \ + STATS_INDEX(lan9645x_get_vstats(lan9645x, type), idx) + +/* Counter indices into stat layout structs */ +#define SCNT_FRER_SID_IN_PKT 0 +#define SCNT_ISDX_GREEN_OCT 1 +#define SCNT_ISDX_GREEN_PKT 2 +#define SCNT_ISDX_YELLOW_OCT 3 +#define SCNT_ISDX_YELLOW_PKT 4 +#define SCNT_ISDX_RED_OCT 5 +#define SCNT_ISDX_RED_PKT 6 +#define SCNT_ISDX_DROP_GREEN_OCT 7 +#define SCNT_ISDX_DROP_GREEN_PKT 8 +#define SCNT_ISDX_DROP_YELLOW_OCT 9 +#define SCNT_ISDX_DROP_YELLOW_PKT 10 + +#define SCNT_SF_MATCHING_FRAMES_COUNT 0 +#define SCNT_SF_NOT_PASSING_FRAMES_COUNT 1 +#define SCNT_SF_NOT_PASSING_SDU_COUNT 2 +#define SCNT_SF_RED_FRAMES_COUNT 3 +#define SCNT_SF_STREAM_BLOCK_COUNT 4 + +#define SCNT_ESDX_GREEN_OCT 0 +#define SCNT_ESDX_GREEN_PKT 1 +#define SCNT_ESDX_YELLOW_OCT 2 +#define SCNT_ESDX_YELLOW_PKT 3 + +#define SCNT_RX_OCT 0 +#define SCNT_RX_UC 1 +#define SCNT_RX_MC 2 +#define SCNT_RX_BC 3 +#define SCNT_RX_SHORT 4 +#define SCNT_RX_FRAG 5 +#define SCNT_RX_JABBER 6 +#define SCNT_RX_CRC 7 +#define SCNT_RX_SYMBOL_ERR 8 +#define SCNT_RX_SZ_64 9 +#define SCNT_RX_SZ_65_127 10 +#define SCNT_RX_SZ_128_255 11 +#define SCNT_RX_SZ_256_511 12 +#define SCNT_RX_SZ_512_1023 13 +#define SCNT_RX_SZ_1024_1526 14 +#define SCNT_RX_SZ_JUMBO 15 +#define SCNT_RX_PAUSE 16 +#define SCNT_RX_CONTROL 17 +#define SCNT_RX_LONG 18 +#define SCNT_RX_CAT_DROP 19 +#define SCNT_RX_RED_PRIO_0 20 +#define SCNT_RX_RED_PRIO_1 21 +#define SCNT_RX_RED_PRIO_2 22 +#define SCNT_RX_RED_PRIO_3 23 +#define SCNT_RX_RED_PRIO_4 24 +#define SCNT_RX_RED_PRIO_5 25 +#define SCNT_RX_RED_PRIO_6 26 +#define SCNT_RX_RED_PRIO_7 27 +#define SCNT_RX_YELLOW_PRIO_0 28 +#define SCNT_RX_YELLOW_PRIO_1 29 +#define SCNT_RX_YELLOW_PRIO_2 30 +#define SCNT_RX_YELLOW_PRIO_3 31 +#define SCNT_RX_YELLOW_PRIO_4 32 +#define SCNT_RX_YELLOW_PRIO_5 33 +#define SCNT_RX_YELLOW_PRIO_6 34 +#define SCNT_RX_YELLOW_PRIO_7 35 +#define SCNT_RX_GREEN_PRIO_0 36 +#define SCNT_RX_GREEN_PRIO_1 37 +#define SCNT_RX_GREEN_PRIO_2 38 +#define SCNT_RX_GREEN_PRIO_3 39 +#define SCNT_RX_GREEN_PRIO_4 40 +#define SCNT_RX_GREEN_PRIO_5 41 +#define SCNT_RX_GREEN_PRIO_6 42 +#define SCNT_RX_GREEN_PRIO_7 43 +#define SCNT_RX_ASSEMBLY_ERR 44 +#define SCNT_RX_SMD_ERR 45 +#define SCNT_RX_ASSEMBLY_OK 46 +#define SCNT_RX_MERGE_FRAG 47 +#define SCNT_RX_PMAC_OCT 48 +#define SCNT_RX_PMAC_UC 49 +#define SCNT_RX_PMAC_MC 50 +#define SCNT_RX_PMAC_BC 51 +#define SCNT_RX_PMAC_SHORT 52 +#define SCNT_RX_PMAC_FRAG 53 +#define SCNT_RX_PMAC_JABBER 54 +#define SCNT_RX_PMAC_CRC 55 +#define SCNT_RX_PMAC_SYMBOL_ERR 56 +#define SCNT_RX_PMAC_SZ_64 57 +#define SCNT_RX_PMAC_SZ_65_127 58 +#define SCNT_RX_PMAC_SZ_128_255 59 +#define SCNT_RX_PMAC_SZ_256_511 60 +#define SCNT_RX_PMAC_SZ_512_1023 61 +#define SCNT_RX_PMAC_SZ_1024_1526 62 +#define SCNT_RX_PMAC_SZ_JUMBO 63 +#define SCNT_RX_PMAC_PAUSE 64 +#define SCNT_RX_PMAC_CONTROL 65 +#define SCNT_RX_PMAC_LONG 66 +#define SCNT_TX_OCT 67 +#define SCNT_TX_UC 68 +#define SCNT_TX_MC 69 +#define SCNT_TX_BC 70 +#define SCNT_TX_COL 71 +#define SCNT_TX_DROP 72 +#define SCNT_TX_PAUSE 73 +#define SCNT_TX_SZ_64 74 +#define SCNT_TX_SZ_65_127 75 +#define SCNT_TX_SZ_128_255 76 +#define SCNT_TX_SZ_256_511 77 +#define SCNT_TX_SZ_512_1023 78 +#define SCNT_TX_SZ_1024_1526 79 +#define SCNT_TX_SZ_JUMBO 80 +#define SCNT_TX_YELLOW_PRIO_0 81 +#define SCNT_TX_YELLOW_PRIO_1 82 +#define SCNT_TX_YELLOW_PRIO_2 83 +#define SCNT_TX_YELLOW_PRIO_3 84 +#define SCNT_TX_YELLOW_PRIO_4 85 +#define SCNT_TX_YELLOW_PRIO_5 86 +#define SCNT_TX_YELLOW_PRIO_6 87 +#define SCNT_TX_YELLOW_PRIO_7 88 +#define SCNT_TX_GREEN_PRIO_0 89 +#define SCNT_TX_GREEN_PRIO_1 90 +#define SCNT_TX_GREEN_PRIO_2 91 +#define SCNT_TX_GREEN_PRIO_3 92 +#define SCNT_TX_GREEN_PRIO_4 93 +#define SCNT_TX_GREEN_PRIO_5 94 +#define SCNT_TX_GREEN_PRIO_6 95 +#define SCNT_TX_GREEN_PRIO_7 96 +#define SCNT_TX_AGED 97 +#define SCNT_TX_LLCT 98 +#define SCNT_TX_CT 99 +#define SCNT_TX_BUFDROP 100 +#define SCNT_TX_MM_HOLD 101 +#define SCNT_TX_MERGE_FRAG 102 +#define SCNT_TX_PMAC_OCT 103 +#define SCNT_TX_PMAC_UC 104 +#define SCNT_TX_PMAC_MC 105 +#define SCNT_TX_PMAC_BC 106 +#define SCNT_TX_PMAC_PAUSE 107 +#define SCNT_TX_PMAC_SZ_64 108 +#define SCNT_TX_PMAC_SZ_65_127 109 +#define SCNT_TX_PMAC_SZ_128_255 110 +#define SCNT_TX_PMAC_SZ_256_511 111 +#define SCNT_TX_PMAC_SZ_512_1023 112 +#define SCNT_TX_PMAC_SZ_1024_1526 113 +#define SCNT_TX_PMAC_SZ_JUMBO 114 +#define SCNT_DR_LOCAL 115 +#define SCNT_DR_TAIL 116 +#define SCNT_DR_YELLOW_PRIO_0 117 +#define SCNT_DR_YELLOW_PRIO_1 118 +#define SCNT_DR_YELLOW_PRIO_2 119 +#define SCNT_DR_YELLOW_PRIO_3 120 +#define SCNT_DR_YELLOW_PRIO_4 121 +#define SCNT_DR_YELLOW_PRIO_5 122 +#define SCNT_DR_YELLOW_PRIO_6 123 +#define SCNT_DR_YELLOW_PRIO_7 124 +#define SCNT_DR_GREEN_PRIO_0 125 +#define SCNT_DR_GREEN_PRIO_1 126 +#define SCNT_DR_GREEN_PRIO_2 127 +#define SCNT_DR_GREEN_PRIO_3 128 +#define SCNT_DR_GREEN_PRIO_4 129 +#define SCNT_DR_GREEN_PRIO_5 130 +#define SCNT_DR_GREEN_PRIO_6 131 +#define SCNT_DR_GREEN_PRIO_7 132 + +struct lan9645x_stat_layout { + u32 offset; + char name[ETH_GSTRING_LEN]; +}; + +enum lan9645x_view_stat_type { + LAN9645X_STAT_PORTS = 0, + LAN9645X_STAT_ISDX, + LAN9645X_STAT_ESDX, + LAN9645X_STAT_SFID, + + LAN9645X_STAT_NUM, +}; + +struct lan9645x_stat_region { + u32 base_offset; + u32 cnt; + u32 cnts_base_idx; +}; + +/* Counters are organized by indices/views such as + * + * - physical ports + * - isdx + * - esdx + * - frer + * - sfid + * + * Each view contains regions, which is a linear address range of related + * stats. I.e. the ports index has RX, TX and Drop regions. + * + * + * and you have a given counter replicated per index. + */ +struct lan9645x_view_stats { + /* Individual counter descriptions in this view */ + const struct lan9645x_stat_layout *layout; + /* Region description for this view, used for bulk reading */ + const struct lan9645x_stat_region *regions; + struct lan9645x_stats *stats; + char name[16]; + /* 64bit software counters with the same addr layout hw */ + u64 *cnts; + /* Buffer for bulk reading counter regions from hw */ + u32 *buf; + /* Number of counters per index in view */ + u32 num_cnts; + /* Number of indexes in view */ + u32 num_indexes; + /* Number of counter regions with counters at sequential addresses */ + size_t num_regions; + enum lan9645x_view_stat_type type; +}; + +struct lan9645x_stats { + struct lan9645x *lan9645x; + struct mutex hw_lock; /* lock r/w to stat registers */ + struct delayed_work work; + struct workqueue_struct *queue; + + struct lan9645x_view_stats view[LAN9645X_STAT_NUM]; +}; + +static inline struct lan9645x_view_stats * +lan9645x_get_vstats(struct lan9645x *lan9645x, + enum lan9645x_view_stat_type type) +{ + if (WARN_ON(!(type < LAN9645X_STAT_NUM))) + return NULL; + + return &lan9645x->stats->view[type]; +} + +/* Add a possibly wrapping 32 bit value to a 64 bit counter */ +static inline void lan9645x_stats_add_cnt(u64 *cnt, u32 val) +{ + if (val < (*cnt & U32_MAX)) + *cnt += (u64)1 << 32; /* value has wrapped */ + + *cnt = (*cnt & ~(u64)U32_MAX) + val; +} + +void lan9645x_stats_clear_counters(struct lan9645x *lan9645x, + enum lan9645x_view_stat_type type, int idx); +int lan9645x_stats_init(struct lan9645x *lan9645x); +void lan9645x_stats_deinit(struct lan9645x *lan9645x); +void lan9645x_stats_get_strings(struct lan9645x *lan9645x, int port, + u32 stringset, u8 *data); +int lan9645x_stats_get_sset_count(struct lan9645x *lan9645x, int port, + int sset); +void lan9645x_stats_get_ethtool_stats(struct lan9645x *lan9645x, int port, + uint64_t *data); +void lan9645x_stats_get_eth_mac_stats(struct lan9645x *lan9645x, int port, + struct ethtool_eth_mac_stats *mac_stats); +void +lan9645x_stats_get_rmon_stats(struct lan9645x *lan9645x, int port, + struct ethtool_rmon_stats *rmon_stats, + const struct ethtool_rmon_hist_range **ranges); +void lan9645x_stats_get_stats64(struct lan9645x *lan9645x, int port, + struct rtnl_link_stats64 *s); +void lan9645x_stats_get_mm_stats(struct lan9645x *lan9645x, int port, + struct ethtool_mm_stats *stats); +void lan9645x_stats_get_pause_stats(struct lan9645x *lan9645x, int port, + struct ethtool_pause_stats *ps); +void +lan9645x_stats_get_eth_ctrl_stats(struct lan9645x *lan9645x, int port, + struct ethtool_eth_ctrl_stats *ctrl_stats); +void lan9645x_stats_get_eth_phy_stats(struct lan9645x *lan9645x, int port, + struct ethtool_eth_phy_stats *phy_stats); +void lan9645x_stats_view_update(struct lan9645x *lan9645x, + enum lan9645x_view_stat_type view_type); +void lan9645x_stats_view_idx_update(struct lan9645x *lan9645x, + enum lan9645x_view_stat_type vtype, + int idx); + +#endif -- 2.52.0