1 // SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause 2 3 /* Ethtool support for Mellanox Gigabit Ethernet driver 4 * 5 * Copyright (C) 2020-2021 NVIDIA CORPORATION & AFFILIATES 6 */ 7 8 #include <linux/phy.h> 9 10 #include "mlxbf_gige.h" 11 #include "mlxbf_gige_regs.h" 12 13 /* Start of struct ethtool_ops functions */ 14 static int mlxbf_gige_get_regs_len(struct net_device *netdev) 15 { 16 return MLXBF_GIGE_MMIO_REG_SZ; 17 } 18 19 static void mlxbf_gige_get_regs(struct net_device *netdev, 20 struct ethtool_regs *regs, void *p) 21 { 22 struct mlxbf_gige *priv = netdev_priv(netdev); 23 24 regs->version = MLXBF_GIGE_REGS_VERSION; 25 26 /* Read entire MMIO register space and store results 27 * into the provided buffer. By design, a read to an 28 * offset without an existing register will be 29 * acknowledged and return zero. 30 */ 31 memcpy_fromio(p, priv->base, MLXBF_GIGE_MMIO_REG_SZ); 32 } 33 34 static void 35 mlxbf_gige_get_ringparam(struct net_device *netdev, 36 struct ethtool_ringparam *ering, 37 struct kernel_ethtool_ringparam *kernel_ering, 38 struct netlink_ext_ack *extack) 39 { 40 struct mlxbf_gige *priv = netdev_priv(netdev); 41 42 ering->rx_max_pending = MLXBF_GIGE_MAX_RXQ_SZ; 43 ering->tx_max_pending = MLXBF_GIGE_MAX_TXQ_SZ; 44 ering->rx_pending = priv->rx_q_entries; 45 ering->tx_pending = priv->tx_q_entries; 46 } 47 48 static const struct { 49 const char string[ETH_GSTRING_LEN]; 50 } mlxbf_gige_ethtool_stats_keys[] = { 51 { "hw_access_errors" }, 52 { "tx_invalid_checksums" }, 53 { "tx_small_frames" }, 54 { "tx_index_errors" }, 55 { "sw_config_errors" }, 56 { "sw_access_errors" }, 57 { "rx_truncate_errors" }, 58 { "rx_mac_errors" }, 59 { "rx_din_dropped_pkts" }, 60 { "tx_fifo_full" }, 61 { "rx_filter_passed_pkts" }, 62 { "rx_filter_discard_pkts" }, 63 }; 64 65 static int mlxbf_gige_get_sset_count(struct net_device *netdev, int stringset) 66 { 67 if (stringset != ETH_SS_STATS) 68 return -EOPNOTSUPP; 69 return ARRAY_SIZE(mlxbf_gige_ethtool_stats_keys); 70 } 71 72 static void mlxbf_gige_get_strings(struct net_device *netdev, u32 stringset, 73 u8 *buf) 74 { 75 if (stringset != ETH_SS_STATS) 76 return; 77 memcpy(buf, &mlxbf_gige_ethtool_stats_keys, 78 sizeof(mlxbf_gige_ethtool_stats_keys)); 79 } 80 81 static void mlxbf_gige_get_ethtool_stats(struct net_device *netdev, 82 struct ethtool_stats *estats, 83 u64 *data) 84 { 85 struct mlxbf_gige *priv = netdev_priv(netdev); 86 87 /* Fill data array with interface statistics 88 * 89 * NOTE: the data writes must be in 90 * sync with the strings shown in 91 * the mlxbf_gige_ethtool_stats_keys[] array 92 * 93 * NOTE2: certain statistics below are zeroed upon 94 * port disable, so the calculation below 95 * must include the "cached" value of the stat 96 * plus the value read directly from hardware. 97 * Cached statistics are currently: 98 * rx_din_dropped_pkts 99 * rx_filter_passed_pkts 100 * rx_filter_discard_pkts 101 */ 102 *data++ = priv->stats.hw_access_errors; 103 *data++ = priv->stats.tx_invalid_checksums; 104 *data++ = priv->stats.tx_small_frames; 105 *data++ = priv->stats.tx_index_errors; 106 *data++ = priv->stats.sw_config_errors; 107 *data++ = priv->stats.sw_access_errors; 108 *data++ = priv->stats.rx_truncate_errors; 109 *data++ = priv->stats.rx_mac_errors; 110 *data++ = (priv->stats.rx_din_dropped_pkts + 111 readq(priv->base + MLXBF_GIGE_RX_DIN_DROP_COUNTER)); 112 *data++ = priv->stats.tx_fifo_full; 113 *data++ = (priv->stats.rx_filter_passed_pkts + 114 readq(priv->base + MLXBF_GIGE_RX_PASS_COUNTER_ALL)); 115 *data++ = (priv->stats.rx_filter_discard_pkts + 116 readq(priv->base + MLXBF_GIGE_RX_DISC_COUNTER_ALL)); 117 } 118 119 static void mlxbf_gige_get_pauseparam(struct net_device *netdev, 120 struct ethtool_pauseparam *pause) 121 { 122 pause->autoneg = AUTONEG_DISABLE; 123 pause->rx_pause = 1; 124 pause->tx_pause = 1; 125 } 126 127 static bool mlxbf_gige_llu_counters_enabled(struct mlxbf_gige *priv) 128 { 129 u32 data; 130 131 if (priv->hw_version == MLXBF_GIGE_VERSION_BF2) { 132 data = readl(priv->llu_base + MLXBF_GIGE_BF2_LLU_GENERAL_CONFIG); 133 if (data & MLXBF_GIGE_BF2_LLU_COUNTERS_EN) 134 return true; 135 } else { 136 data = readl(priv->llu_base + MLXBF_GIGE_BF3_LLU_GENERAL_CONFIG); 137 if (data & MLXBF_GIGE_BF3_LLU_COUNTERS_EN) 138 return true; 139 } 140 141 return false; 142 } 143 144 static void mlxbf_gige_get_pause_stats(struct net_device *netdev, 145 struct ethtool_pause_stats *pause_stats) 146 { 147 struct mlxbf_gige *priv = netdev_priv(netdev); 148 u64 data_lo, data_hi; 149 150 /* Read LLU counters to provide stats only if counters are enabled */ 151 if (mlxbf_gige_llu_counters_enabled(priv)) { 152 data_lo = readl(priv->llu_base + MLXBF_GIGE_TX_PAUSE_CNT_LO); 153 data_hi = readl(priv->llu_base + MLXBF_GIGE_TX_PAUSE_CNT_HI); 154 pause_stats->tx_pause_frames = (data_hi << 32) | data_lo; 155 156 data_lo = readl(priv->llu_base + MLXBF_GIGE_RX_PAUSE_CNT_LO); 157 data_hi = readl(priv->llu_base + MLXBF_GIGE_RX_PAUSE_CNT_HI); 158 pause_stats->rx_pause_frames = (data_hi << 32) | data_lo; 159 } 160 } 161 162 const struct ethtool_ops mlxbf_gige_ethtool_ops = { 163 .get_link = ethtool_op_get_link, 164 .get_ringparam = mlxbf_gige_get_ringparam, 165 .get_regs_len = mlxbf_gige_get_regs_len, 166 .get_regs = mlxbf_gige_get_regs, 167 .get_strings = mlxbf_gige_get_strings, 168 .get_sset_count = mlxbf_gige_get_sset_count, 169 .get_ethtool_stats = mlxbf_gige_get_ethtool_stats, 170 .nway_reset = phy_ethtool_nway_reset, 171 .get_pauseparam = mlxbf_gige_get_pauseparam, 172 .get_pause_stats = mlxbf_gige_get_pause_stats, 173 .get_link_ksettings = phy_ethtool_get_link_ksettings, 174 .set_link_ksettings = phy_ethtool_set_link_ksettings, 175 }; 176