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(dev: 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(dev: 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(dev: 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(addr: 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(addr: priv->base + MLXBF_GIGE_RX_PASS_COUNTER_ALL)); |
115 | *data++ = (priv->stats.rx_filter_discard_pkts + |
116 | readq(addr: 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 | 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 | |