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 */
mlxbf_gige_get_regs_len(struct net_device * netdev)14 static int mlxbf_gige_get_regs_len(struct net_device *netdev)
15 {
16 return MLXBF_GIGE_MMIO_REG_SZ;
17 }
18
mlxbf_gige_get_regs(struct net_device * netdev,struct ethtool_regs * regs,void * p)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
mlxbf_gige_get_ringparam(struct net_device * netdev,struct ethtool_ringparam * ering,struct kernel_ethtool_ringparam * kernel_ering,struct netlink_ext_ack * extack)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
mlxbf_gige_get_sset_count(struct net_device * netdev,int stringset)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
mlxbf_gige_get_strings(struct net_device * netdev,u32 stringset,u8 * buf)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
mlxbf_gige_get_ethtool_stats(struct net_device * netdev,struct ethtool_stats * estats,u64 * data)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
mlxbf_gige_get_pauseparam(struct net_device * netdev,struct ethtool_pauseparam * pause)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 const struct ethtool_ops mlxbf_gige_ethtool_ops = {
128 .get_link = ethtool_op_get_link,
129 .get_ringparam = mlxbf_gige_get_ringparam,
130 .get_regs_len = mlxbf_gige_get_regs_len,
131 .get_regs = mlxbf_gige_get_regs,
132 .get_strings = mlxbf_gige_get_strings,
133 .get_sset_count = mlxbf_gige_get_sset_count,
134 .get_ethtool_stats = mlxbf_gige_get_ethtool_stats,
135 .nway_reset = phy_ethtool_nway_reset,
136 .get_pauseparam = mlxbf_gige_get_pauseparam,
137 .get_link_ksettings = phy_ethtool_get_link_ksettings,
138 .set_link_ksettings = phy_ethtool_set_link_ksettings,
139 };
140