1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* Copyright (c) 2016, The Linux Foundation. All rights reserved. |
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(dev: 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(dev: 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) { |
83 | case ETH_SS_PRIV_FLAGS: |
84 | return 1; |
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) { |
97 | case ETH_SS_PRIV_FLAGS: |
98 | strcpy(p: data, q: "single-pause-mode" ); |
99 | break; |
100 | |
101 | case ETH_SS_STATS: |
102 | for (i = 0; i < EMAC_STATS_LEN; i++) { |
103 | strscpy(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(dev: netdev); |
116 | |
117 | spin_lock(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(lock: &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 | struct kernel_ethtool_ringparam *kernel_ring, |
138 | struct netlink_ext_ack *extack) |
139 | { |
140 | struct emac_adapter *adpt = netdev_priv(dev: netdev); |
141 | |
142 | ring->rx_max_pending = EMAC_MAX_RX_DESCS; |
143 | ring->tx_max_pending = EMAC_MAX_TX_DESCS; |
144 | ring->rx_pending = adpt->rx_desc_cnt; |
145 | ring->tx_pending = adpt->tx_desc_cnt; |
146 | } |
147 | |
148 | static int emac_set_ringparam(struct net_device *netdev, |
149 | struct ethtool_ringparam *ring, |
150 | struct kernel_ethtool_ringparam *kernel_ring, |
151 | struct netlink_ext_ack *extack) |
152 | { |
153 | struct emac_adapter *adpt = netdev_priv(dev: netdev); |
154 | |
155 | /* We don't have separate queues/rings for small/large frames, so |
156 | * reject any attempt to specify those values separately. |
157 | */ |
158 | if (ring->rx_mini_pending || ring->rx_jumbo_pending) |
159 | return -EINVAL; |
160 | |
161 | adpt->tx_desc_cnt = |
162 | clamp_val(ring->tx_pending, EMAC_MIN_TX_DESCS, EMAC_MAX_TX_DESCS); |
163 | |
164 | adpt->rx_desc_cnt = |
165 | clamp_val(ring->rx_pending, EMAC_MIN_RX_DESCS, EMAC_MAX_RX_DESCS); |
166 | |
167 | if (netif_running(dev: netdev)) |
168 | return emac_reinit_locked(adpt); |
169 | |
170 | return 0; |
171 | } |
172 | |
173 | static void emac_get_pauseparam(struct net_device *netdev, |
174 | struct ethtool_pauseparam *pause) |
175 | { |
176 | struct emac_adapter *adpt = netdev_priv(dev: netdev); |
177 | |
178 | pause->autoneg = adpt->automatic ? AUTONEG_ENABLE : AUTONEG_DISABLE; |
179 | pause->rx_pause = adpt->rx_flow_control ? 1 : 0; |
180 | pause->tx_pause = adpt->tx_flow_control ? 1 : 0; |
181 | } |
182 | |
183 | static int emac_set_pauseparam(struct net_device *netdev, |
184 | struct ethtool_pauseparam *pause) |
185 | { |
186 | struct emac_adapter *adpt = netdev_priv(dev: netdev); |
187 | |
188 | adpt->automatic = pause->autoneg == AUTONEG_ENABLE; |
189 | adpt->rx_flow_control = pause->rx_pause != 0; |
190 | adpt->tx_flow_control = pause->tx_pause != 0; |
191 | |
192 | if (netif_running(dev: netdev)) |
193 | return emac_reinit_locked(adpt); |
194 | |
195 | return 0; |
196 | } |
197 | |
198 | /* Selected registers that might want to track during runtime. */ |
199 | static const u16 emac_regs[] = { |
200 | EMAC_DMA_MAS_CTRL, |
201 | EMAC_MAC_CTRL, |
202 | EMAC_TXQ_CTRL_0, |
203 | EMAC_RXQ_CTRL_0, |
204 | EMAC_DMA_CTRL, |
205 | EMAC_INT_MASK, |
206 | EMAC_AXI_MAST_CTRL, |
207 | EMAC_CORE_HW_VERSION, |
208 | EMAC_MISC_CTRL, |
209 | }; |
210 | |
211 | /* Every time emac_regs[] above is changed, increase this version number. */ |
212 | #define EMAC_REGS_VERSION 0 |
213 | |
214 | #define EMAC_MAX_REG_SIZE ARRAY_SIZE(emac_regs) |
215 | |
216 | static void emac_get_regs(struct net_device *netdev, |
217 | struct ethtool_regs *regs, void *buff) |
218 | { |
219 | struct emac_adapter *adpt = netdev_priv(dev: netdev); |
220 | u32 *val = buff; |
221 | unsigned int i; |
222 | |
223 | regs->version = EMAC_REGS_VERSION; |
224 | regs->len = EMAC_MAX_REG_SIZE * sizeof(u32); |
225 | |
226 | for (i = 0; i < EMAC_MAX_REG_SIZE; i++) |
227 | val[i] = readl(addr: adpt->base + emac_regs[i]); |
228 | } |
229 | |
230 | static int emac_get_regs_len(struct net_device *netdev) |
231 | { |
232 | return EMAC_MAX_REG_SIZE * sizeof(u32); |
233 | } |
234 | |
235 | #define EMAC_PRIV_ENABLE_SINGLE_PAUSE BIT(0) |
236 | |
237 | static int emac_set_priv_flags(struct net_device *netdev, u32 flags) |
238 | { |
239 | struct emac_adapter *adpt = netdev_priv(dev: netdev); |
240 | |
241 | adpt->single_pause_mode = !!(flags & EMAC_PRIV_ENABLE_SINGLE_PAUSE); |
242 | |
243 | if (netif_running(dev: netdev)) |
244 | return emac_reinit_locked(adpt); |
245 | |
246 | return 0; |
247 | } |
248 | |
249 | static u32 emac_get_priv_flags(struct net_device *netdev) |
250 | { |
251 | struct emac_adapter *adpt = netdev_priv(dev: netdev); |
252 | |
253 | return adpt->single_pause_mode ? EMAC_PRIV_ENABLE_SINGLE_PAUSE : 0; |
254 | } |
255 | |
256 | static const struct ethtool_ops emac_ethtool_ops = { |
257 | .get_link_ksettings = phy_ethtool_get_link_ksettings, |
258 | .set_link_ksettings = phy_ethtool_set_link_ksettings, |
259 | |
260 | .get_msglevel = emac_get_msglevel, |
261 | .set_msglevel = emac_set_msglevel, |
262 | |
263 | .get_sset_count = emac_get_sset_count, |
264 | .get_strings = emac_get_strings, |
265 | .get_ethtool_stats = emac_get_ethtool_stats, |
266 | |
267 | .get_ringparam = emac_get_ringparam, |
268 | .set_ringparam = emac_set_ringparam, |
269 | |
270 | .get_pauseparam = emac_get_pauseparam, |
271 | .set_pauseparam = emac_set_pauseparam, |
272 | |
273 | .nway_reset = emac_nway_reset, |
274 | |
275 | .get_link = ethtool_op_get_link, |
276 | |
277 | .get_regs_len = emac_get_regs_len, |
278 | .get_regs = emac_get_regs, |
279 | |
280 | .set_priv_flags = emac_set_priv_flags, |
281 | .get_priv_flags = emac_get_priv_flags, |
282 | }; |
283 | |
284 | void emac_set_ethtool_ops(struct net_device *netdev) |
285 | { |
286 | netdev->ethtool_ops = &emac_ethtool_ops; |
287 | } |
288 | |