176 lines · c
1// SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause2 3/* Ethtool support for Mellanox Gigabit Ethernet driver4 *5 * Copyright (C) 2020-2021 NVIDIA CORPORATION & AFFILIATES6 */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 */14static int mlxbf_gige_get_regs_len(struct net_device *netdev)15{16 return MLXBF_GIGE_MMIO_REG_SZ;17}18 19static 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 results27 * into the provided buffer. By design, a read to an28 * offset without an existing register will be29 * acknowledged and return zero.30 */31 memcpy_fromio(p, priv->base, MLXBF_GIGE_MMIO_REG_SZ);32}33 34static void35mlxbf_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 48static 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 65static 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 72static 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 81static 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 statistics88 *89 * NOTE: the data writes must be in90 * sync with the strings shown in91 * the mlxbf_gige_ethtool_stats_keys[] array92 *93 * NOTE2: certain statistics below are zeroed upon94 * port disable, so the calculation below95 * must include the "cached" value of the stat96 * plus the value read directly from hardware.97 * Cached statistics are currently:98 * rx_din_dropped_pkts99 * rx_filter_passed_pkts100 * rx_filter_discard_pkts101 */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 119static 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 127static 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 144static 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 162const 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