Linux Audio

Check our new training course

Loading...
  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 */
 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 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
 34static void
 35mlxbf_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 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
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};
1