Commit | Line | Data |
---|---|---|
97fb5e8d | 1 | // SPDX-License-Identifier: GPL-2.0-only |
79f664ed | 2 | /* Copyright (c) 2016, The Linux Foundation. All rights reserved. |
79f664ed TT |
3 | */ |
4 | ||
5 | #include <linux/ethtool.h> | |
6 | #include <linux/phy.h> | |
7 | ||
8 | #include "emac.h" | |
9 | ||
10 | static const char * const emac_ethtool_stat_strings[] = { | |
11 | "rx_ok", | |
12 | "rx_bcast", | |
13 | "rx_mcast", | |
14 | "rx_pause", | |
15 | "rx_ctrl", | |
16 | "rx_fcs_err", | |
17 | "rx_len_err", | |
18 | "rx_byte_cnt", | |
19 | "rx_runt", | |
20 | "rx_frag", | |
21 | "rx_sz_64", | |
22 | "rx_sz_65_127", | |
23 | "rx_sz_128_255", | |
24 | "rx_sz_256_511", | |
25 | "rx_sz_512_1023", | |
26 | "rx_sz_1024_1518", | |
27 | "rx_sz_1519_max", | |
28 | "rx_sz_ov", | |
29 | "rx_rxf_ov", | |
30 | "rx_align_err", | |
31 | "rx_bcast_byte_cnt", | |
32 | "rx_mcast_byte_cnt", | |
33 | "rx_err_addr", | |
34 | "rx_crc_align", | |
35 | "rx_jabbers", | |
36 | "tx_ok", | |
37 | "tx_bcast", | |
38 | "tx_mcast", | |
39 | "tx_pause", | |
40 | "tx_exc_defer", | |
41 | "tx_ctrl", | |
42 | "tx_defer", | |
43 | "tx_byte_cnt", | |
44 | "tx_sz_64", | |
45 | "tx_sz_65_127", | |
46 | "tx_sz_128_255", | |
47 | "tx_sz_256_511", | |
48 | "tx_sz_512_1023", | |
49 | "tx_sz_1024_1518", | |
50 | "tx_sz_1519_max", | |
51 | "tx_1_col", | |
52 | "tx_2_col", | |
53 | "tx_late_col", | |
54 | "tx_abort_col", | |
55 | "tx_underrun", | |
56 | "tx_rd_eop", | |
57 | "tx_len_err", | |
58 | "tx_trunc", | |
59 | "tx_bcast_byte", | |
60 | "tx_mcast_byte", | |
61 | "tx_col", | |
62 | }; | |
63 | ||
64 | #define EMAC_STATS_LEN ARRAY_SIZE(emac_ethtool_stat_strings) | |
65 | ||
66 | static u32 emac_get_msglevel(struct net_device *netdev) | |
67 | { | |
68 | struct emac_adapter *adpt = netdev_priv(netdev); | |
69 | ||
70 | return adpt->msg_enable; | |
71 | } | |
72 | ||
73 | static void emac_set_msglevel(struct net_device *netdev, u32 data) | |
74 | { | |
75 | struct emac_adapter *adpt = netdev_priv(netdev); | |
76 | ||
77 | adpt->msg_enable = data; | |
78 | } | |
79 | ||
80 | static int emac_get_sset_count(struct net_device *netdev, int sset) | |
81 | { | |
82 | switch (sset) { | |
4a7a3860 TT |
83 | case ETH_SS_PRIV_FLAGS: |
84 | return 1; | |
79f664ed TT |
85 | case ETH_SS_STATS: |
86 | return EMAC_STATS_LEN; | |
87 | default: | |
88 | return -EOPNOTSUPP; | |
89 | } | |
90 | } | |
91 | ||
92 | static void emac_get_strings(struct net_device *netdev, u32 stringset, u8 *data) | |
93 | { | |
94 | unsigned int i; | |
95 | ||
96 | switch (stringset) { | |
4a7a3860 TT |
97 | case ETH_SS_PRIV_FLAGS: |
98 | strcpy(data, "single-pause-mode"); | |
99 | break; | |
100 | ||
79f664ed TT |
101 | case ETH_SS_STATS: |
102 | for (i = 0; i < EMAC_STATS_LEN; i++) { | |
103 | strlcpy(data, emac_ethtool_stat_strings[i], | |
104 | ETH_GSTRING_LEN); | |
105 | data += ETH_GSTRING_LEN; | |
106 | } | |
107 | break; | |
108 | } | |
109 | } | |
110 | ||
111 | static void emac_get_ethtool_stats(struct net_device *netdev, | |
112 | struct ethtool_stats *stats, | |
113 | u64 *data) | |
114 | { | |
115 | struct emac_adapter *adpt = netdev_priv(netdev); | |
116 | ||
117 | spin_lock(&adpt->stats.lock); | |
118 | ||
119 | emac_update_hw_stats(adpt); | |
120 | memcpy(data, &adpt->stats, EMAC_STATS_LEN * sizeof(u64)); | |
121 | ||
122 | spin_unlock(&adpt->stats.lock); | |
123 | } | |
124 | ||
125 | static int emac_nway_reset(struct net_device *netdev) | |
126 | { | |
127 | struct phy_device *phydev = netdev->phydev; | |
128 | ||
129 | if (!phydev) | |
130 | return -ENODEV; | |
131 | ||
132 | return genphy_restart_aneg(phydev); | |
133 | } | |
134 | ||
135 | static void emac_get_ringparam(struct net_device *netdev, | |
136 | struct ethtool_ringparam *ring) | |
137 | { | |
138 | struct emac_adapter *adpt = netdev_priv(netdev); | |
139 | ||
140 | ring->rx_max_pending = EMAC_MAX_RX_DESCS; | |
141 | ring->tx_max_pending = EMAC_MAX_TX_DESCS; | |
142 | ring->rx_pending = adpt->rx_desc_cnt; | |
143 | ring->tx_pending = adpt->tx_desc_cnt; | |
144 | } | |
145 | ||
038b9404 TT |
146 | static int emac_set_ringparam(struct net_device *netdev, |
147 | struct ethtool_ringparam *ring) | |
148 | { | |
149 | struct emac_adapter *adpt = netdev_priv(netdev); | |
150 | ||
151 | /* We don't have separate queues/rings for small/large frames, so | |
152 | * reject any attempt to specify those values separately. | |
153 | */ | |
154 | if (ring->rx_mini_pending || ring->rx_jumbo_pending) | |
155 | return -EINVAL; | |
156 | ||
157 | adpt->tx_desc_cnt = | |
158 | clamp_val(ring->tx_pending, EMAC_MIN_TX_DESCS, EMAC_MAX_TX_DESCS); | |
159 | ||
160 | adpt->rx_desc_cnt = | |
161 | clamp_val(ring->rx_pending, EMAC_MIN_RX_DESCS, EMAC_MAX_RX_DESCS); | |
162 | ||
163 | if (netif_running(netdev)) | |
164 | return emac_reinit_locked(adpt); | |
165 | ||
166 | return 0; | |
167 | } | |
168 | ||
79f664ed TT |
169 | static void emac_get_pauseparam(struct net_device *netdev, |
170 | struct ethtool_pauseparam *pause) | |
171 | { | |
b44700e9 | 172 | struct emac_adapter *adpt = netdev_priv(netdev); |
79f664ed | 173 | |
b44700e9 TT |
174 | pause->autoneg = adpt->automatic ? AUTONEG_ENABLE : AUTONEG_DISABLE; |
175 | pause->rx_pause = adpt->rx_flow_control ? 1 : 0; | |
3c19bd6c | 176 | pause->tx_pause = adpt->tx_flow_control ? 1 : 0; |
b44700e9 TT |
177 | } |
178 | ||
179 | static int emac_set_pauseparam(struct net_device *netdev, | |
180 | struct ethtool_pauseparam *pause) | |
181 | { | |
182 | struct emac_adapter *adpt = netdev_priv(netdev); | |
183 | ||
184 | adpt->automatic = pause->autoneg == AUTONEG_ENABLE; | |
185 | adpt->rx_flow_control = pause->rx_pause != 0; | |
186 | adpt->tx_flow_control = pause->tx_pause != 0; | |
187 | ||
188 | if (netif_running(netdev)) | |
189 | return emac_reinit_locked(adpt); | |
190 | ||
191 | return 0; | |
79f664ed TT |
192 | } |
193 | ||
c4e7beea TT |
194 | /* Selected registers that might want to track during runtime. */ |
195 | static const u16 emac_regs[] = { | |
196 | EMAC_DMA_MAS_CTRL, | |
197 | EMAC_MAC_CTRL, | |
198 | EMAC_TXQ_CTRL_0, | |
199 | EMAC_RXQ_CTRL_0, | |
200 | EMAC_DMA_CTRL, | |
201 | EMAC_INT_MASK, | |
202 | EMAC_AXI_MAST_CTRL, | |
203 | EMAC_CORE_HW_VERSION, | |
204 | EMAC_MISC_CTRL, | |
205 | }; | |
206 | ||
207 | /* Every time emac_regs[] above is changed, increase this version number. */ | |
208 | #define EMAC_REGS_VERSION 0 | |
209 | ||
210 | #define EMAC_MAX_REG_SIZE ARRAY_SIZE(emac_regs) | |
211 | ||
212 | static void emac_get_regs(struct net_device *netdev, | |
213 | struct ethtool_regs *regs, void *buff) | |
214 | { | |
215 | struct emac_adapter *adpt = netdev_priv(netdev); | |
216 | u32 *val = buff; | |
217 | unsigned int i; | |
218 | ||
219 | regs->version = EMAC_REGS_VERSION; | |
220 | regs->len = EMAC_MAX_REG_SIZE * sizeof(u32); | |
221 | ||
222 | for (i = 0; i < EMAC_MAX_REG_SIZE; i++) | |
223 | val[i] = readl(adpt->base + emac_regs[i]); | |
224 | } | |
225 | ||
226 | static int emac_get_regs_len(struct net_device *netdev) | |
227 | { | |
2194bd10 | 228 | return EMAC_MAX_REG_SIZE * sizeof(u32); |
c4e7beea TT |
229 | } |
230 | ||
4a7a3860 TT |
231 | #define EMAC_PRIV_ENABLE_SINGLE_PAUSE BIT(0) |
232 | ||
233 | static int emac_set_priv_flags(struct net_device *netdev, u32 flags) | |
234 | { | |
235 | struct emac_adapter *adpt = netdev_priv(netdev); | |
236 | ||
237 | adpt->single_pause_mode = !!(flags & EMAC_PRIV_ENABLE_SINGLE_PAUSE); | |
238 | ||
239 | if (netif_running(netdev)) | |
240 | return emac_reinit_locked(adpt); | |
241 | ||
242 | return 0; | |
243 | } | |
244 | ||
245 | static u32 emac_get_priv_flags(struct net_device *netdev) | |
246 | { | |
247 | struct emac_adapter *adpt = netdev_priv(netdev); | |
248 | ||
249 | return adpt->single_pause_mode ? EMAC_PRIV_ENABLE_SINGLE_PAUSE : 0; | |
250 | } | |
251 | ||
79f664ed TT |
252 | static const struct ethtool_ops emac_ethtool_ops = { |
253 | .get_link_ksettings = phy_ethtool_get_link_ksettings, | |
254 | .set_link_ksettings = phy_ethtool_set_link_ksettings, | |
255 | ||
256 | .get_msglevel = emac_get_msglevel, | |
257 | .set_msglevel = emac_set_msglevel, | |
258 | ||
259 | .get_sset_count = emac_get_sset_count, | |
260 | .get_strings = emac_get_strings, | |
261 | .get_ethtool_stats = emac_get_ethtool_stats, | |
262 | ||
263 | .get_ringparam = emac_get_ringparam, | |
038b9404 | 264 | .set_ringparam = emac_set_ringparam, |
b44700e9 | 265 | |
79f664ed | 266 | .get_pauseparam = emac_get_pauseparam, |
b44700e9 | 267 | .set_pauseparam = emac_set_pauseparam, |
79f664ed TT |
268 | |
269 | .nway_reset = emac_nway_reset, | |
270 | ||
271 | .get_link = ethtool_op_get_link, | |
c4e7beea TT |
272 | |
273 | .get_regs_len = emac_get_regs_len, | |
274 | .get_regs = emac_get_regs, | |
4a7a3860 TT |
275 | |
276 | .set_priv_flags = emac_set_priv_flags, | |
277 | .get_priv_flags = emac_get_priv_flags, | |
79f664ed TT |
278 | }; |
279 | ||
280 | void emac_set_ethtool_ops(struct net_device *netdev) | |
281 | { | |
282 | netdev->ethtool_ops = &emac_ethtool_ops; | |
283 | } |