1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* Applied Micro X-Gene SoC Ethernet Driver |
3 | * |
4 | * Copyright (c) 2014, Applied Micro Circuits Corporation |
5 | * Authors: Iyappan Subramanian <isubramanian@apm.com> |
6 | */ |
7 | |
8 | #include <linux/ethtool.h> |
9 | #include "xgene_enet_main.h" |
10 | |
11 | struct xgene_gstrings_stats { |
12 | char name[ETH_GSTRING_LEN]; |
13 | int offset; |
14 | u32 addr; |
15 | u32 mask; |
16 | }; |
17 | |
18 | #define XGENE_STAT(m) { #m, offsetof(struct rtnl_link_stats64, m) } |
19 | #define XGENE_EXTD_STAT(s, a, m) \ |
20 | { \ |
21 | .name = #s, \ |
22 | .addr = a ## _ADDR, \ |
23 | .mask = m \ |
24 | } |
25 | |
26 | static const struct xgene_gstrings_stats gstrings_stats[] = { |
27 | XGENE_STAT(rx_packets), |
28 | XGENE_STAT(tx_packets), |
29 | XGENE_STAT(rx_bytes), |
30 | XGENE_STAT(tx_bytes), |
31 | XGENE_STAT(rx_errors), |
32 | XGENE_STAT(tx_errors), |
33 | XGENE_STAT(rx_length_errors), |
34 | XGENE_STAT(rx_crc_errors), |
35 | XGENE_STAT(rx_frame_errors), |
36 | XGENE_STAT(rx_fifo_errors) |
37 | }; |
38 | |
39 | static const struct xgene_gstrings_stats gstrings_extd_stats[] = { |
40 | XGENE_EXTD_STAT(tx_rx_64b_frame_cntr, TR64, 31), |
41 | XGENE_EXTD_STAT(tx_rx_127b_frame_cntr, TR127, 31), |
42 | XGENE_EXTD_STAT(tx_rx_255b_frame_cntr, TR255, 31), |
43 | XGENE_EXTD_STAT(tx_rx_511b_frame_cntr, TR511, 31), |
44 | XGENE_EXTD_STAT(tx_rx_1023b_frame_cntr, TR1K, 31), |
45 | XGENE_EXTD_STAT(tx_rx_1518b_frame_cntr, TRMAX, 31), |
46 | XGENE_EXTD_STAT(tx_rx_1522b_frame_cntr, TRMGV, 31), |
47 | XGENE_EXTD_STAT(rx_fcs_error_cntr, RFCS, 16), |
48 | XGENE_EXTD_STAT(rx_multicast_pkt_cntr, RMCA, 31), |
49 | XGENE_EXTD_STAT(rx_broadcast_pkt_cntr, RBCA, 31), |
50 | XGENE_EXTD_STAT(rx_ctrl_frame_pkt_cntr, RXCF, 16), |
51 | XGENE_EXTD_STAT(rx_pause_frame_pkt_cntr, RXPF, 16), |
52 | XGENE_EXTD_STAT(rx_unk_opcode_cntr, RXUO, 16), |
53 | XGENE_EXTD_STAT(rx_align_err_cntr, RALN, 16), |
54 | XGENE_EXTD_STAT(rx_frame_len_err_cntr, RFLR, 16), |
55 | XGENE_EXTD_STAT(rx_frame_len_err_recov_cntr, DUMP, 0), |
56 | XGENE_EXTD_STAT(rx_code_err_cntr, RCDE, 16), |
57 | XGENE_EXTD_STAT(rx_carrier_sense_err_cntr, RCSE, 16), |
58 | XGENE_EXTD_STAT(rx_undersize_pkt_cntr, RUND, 16), |
59 | XGENE_EXTD_STAT(rx_oversize_pkt_cntr, ROVR, 16), |
60 | XGENE_EXTD_STAT(rx_fragments_cntr, RFRG, 16), |
61 | XGENE_EXTD_STAT(rx_jabber_cntr, RJBR, 16), |
62 | XGENE_EXTD_STAT(rx_jabber_recov_cntr, DUMP, 0), |
63 | XGENE_EXTD_STAT(rx_dropped_pkt_cntr, RDRP, 16), |
64 | XGENE_EXTD_STAT(rx_overrun_cntr, DUMP, 0), |
65 | XGENE_EXTD_STAT(tx_multicast_pkt_cntr, TMCA, 31), |
66 | XGENE_EXTD_STAT(tx_broadcast_pkt_cntr, TBCA, 31), |
67 | XGENE_EXTD_STAT(tx_pause_ctrl_frame_cntr, TXPF, 16), |
68 | XGENE_EXTD_STAT(tx_defer_pkt_cntr, TDFR, 31), |
69 | XGENE_EXTD_STAT(tx_excv_defer_pkt_cntr, TEDF, 31), |
70 | XGENE_EXTD_STAT(tx_single_col_pkt_cntr, TSCL, 31), |
71 | XGENE_EXTD_STAT(tx_multi_col_pkt_cntr, TMCL, 31), |
72 | XGENE_EXTD_STAT(tx_late_col_pkt_cntr, TLCL, 31), |
73 | XGENE_EXTD_STAT(tx_excv_col_pkt_cntr, TXCL, 31), |
74 | XGENE_EXTD_STAT(tx_total_col_cntr, TNCL, 31), |
75 | XGENE_EXTD_STAT(tx_pause_frames_hnrd_cntr, TPFH, 16), |
76 | XGENE_EXTD_STAT(tx_drop_frame_cntr, TDRP, 16), |
77 | XGENE_EXTD_STAT(tx_jabber_frame_cntr, TJBR, 12), |
78 | XGENE_EXTD_STAT(tx_fcs_error_cntr, TFCS, 12), |
79 | XGENE_EXTD_STAT(tx_ctrl_frame_cntr, TXCF, 12), |
80 | XGENE_EXTD_STAT(tx_oversize_frame_cntr, TOVR, 12), |
81 | XGENE_EXTD_STAT(tx_undersize_frame_cntr, TUND, 12), |
82 | XGENE_EXTD_STAT(tx_fragments_cntr, TFRG, 12), |
83 | XGENE_EXTD_STAT(tx_underrun_cntr, DUMP, 0) |
84 | }; |
85 | |
86 | #define XGENE_STATS_LEN ARRAY_SIZE(gstrings_stats) |
87 | #define XGENE_EXTD_STATS_LEN ARRAY_SIZE(gstrings_extd_stats) |
88 | #define RFCS_IDX 7 |
89 | #define RALN_IDX 13 |
90 | #define RFLR_IDX 14 |
91 | #define FALSE_RFLR_IDX 15 |
92 | #define RUND_IDX 18 |
93 | #define FALSE_RJBR_IDX 22 |
94 | #define RX_OVERRUN_IDX 24 |
95 | #define TFCS_IDX 38 |
96 | #define TFRG_IDX 42 |
97 | #define TX_UNDERRUN_IDX 43 |
98 | |
99 | static void xgene_get_drvinfo(struct net_device *ndev, |
100 | struct ethtool_drvinfo *info) |
101 | { |
102 | struct xgene_enet_pdata *pdata = netdev_priv(dev: ndev); |
103 | struct platform_device *pdev = pdata->pdev; |
104 | |
105 | strcpy(p: info->driver, q: "xgene_enet" ); |
106 | sprintf(buf: info->bus_info, fmt: "%s" , pdev->name); |
107 | } |
108 | |
109 | static int xgene_get_link_ksettings(struct net_device *ndev, |
110 | struct ethtool_link_ksettings *cmd) |
111 | { |
112 | struct xgene_enet_pdata *pdata = netdev_priv(dev: ndev); |
113 | struct phy_device *phydev = ndev->phydev; |
114 | u32 supported; |
115 | |
116 | if (phy_interface_mode_is_rgmii(mode: pdata->phy_mode)) { |
117 | if (phydev == NULL) |
118 | return -ENODEV; |
119 | |
120 | phy_ethtool_ksettings_get(phydev, cmd); |
121 | |
122 | return 0; |
123 | } else if (pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) { |
124 | if (pdata->mdio_driver) { |
125 | if (!phydev) |
126 | return -ENODEV; |
127 | |
128 | phy_ethtool_ksettings_get(phydev, cmd); |
129 | |
130 | return 0; |
131 | } |
132 | |
133 | supported = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | |
134 | SUPPORTED_MII; |
135 | ethtool_convert_legacy_u32_to_link_mode( |
136 | dst: cmd->link_modes.supported, |
137 | legacy_u32: supported); |
138 | ethtool_convert_legacy_u32_to_link_mode( |
139 | dst: cmd->link_modes.advertising, |
140 | legacy_u32: supported); |
141 | |
142 | cmd->base.speed = SPEED_1000; |
143 | cmd->base.duplex = DUPLEX_FULL; |
144 | cmd->base.port = PORT_MII; |
145 | cmd->base.autoneg = AUTONEG_ENABLE; |
146 | } else { |
147 | supported = SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE; |
148 | ethtool_convert_legacy_u32_to_link_mode( |
149 | dst: cmd->link_modes.supported, |
150 | legacy_u32: supported); |
151 | ethtool_convert_legacy_u32_to_link_mode( |
152 | dst: cmd->link_modes.advertising, |
153 | legacy_u32: supported); |
154 | |
155 | cmd->base.speed = SPEED_10000; |
156 | cmd->base.duplex = DUPLEX_FULL; |
157 | cmd->base.port = PORT_FIBRE; |
158 | cmd->base.autoneg = AUTONEG_DISABLE; |
159 | } |
160 | |
161 | return 0; |
162 | } |
163 | |
164 | static int xgene_set_link_ksettings(struct net_device *ndev, |
165 | const struct ethtool_link_ksettings *cmd) |
166 | { |
167 | struct xgene_enet_pdata *pdata = netdev_priv(dev: ndev); |
168 | struct phy_device *phydev = ndev->phydev; |
169 | |
170 | if (phy_interface_mode_is_rgmii(mode: pdata->phy_mode)) { |
171 | if (!phydev) |
172 | return -ENODEV; |
173 | |
174 | return phy_ethtool_ksettings_set(phydev, cmd); |
175 | } |
176 | |
177 | if (pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) { |
178 | if (pdata->mdio_driver) { |
179 | if (!phydev) |
180 | return -ENODEV; |
181 | |
182 | return phy_ethtool_ksettings_set(phydev, cmd); |
183 | } |
184 | } |
185 | |
186 | return -EINVAL; |
187 | } |
188 | |
189 | static void xgene_get_strings(struct net_device *ndev, u32 stringset, u8 *data) |
190 | { |
191 | int i; |
192 | u8 *p = data; |
193 | |
194 | if (stringset != ETH_SS_STATS) |
195 | return; |
196 | |
197 | for (i = 0; i < XGENE_STATS_LEN; i++) { |
198 | memcpy(p, gstrings_stats[i].name, ETH_GSTRING_LEN); |
199 | p += ETH_GSTRING_LEN; |
200 | } |
201 | |
202 | for (i = 0; i < XGENE_EXTD_STATS_LEN; i++) { |
203 | memcpy(p, gstrings_extd_stats[i].name, ETH_GSTRING_LEN); |
204 | p += ETH_GSTRING_LEN; |
205 | } |
206 | } |
207 | |
208 | static int xgene_get_sset_count(struct net_device *ndev, int sset) |
209 | { |
210 | if (sset != ETH_SS_STATS) |
211 | return -EINVAL; |
212 | |
213 | return XGENE_STATS_LEN + XGENE_EXTD_STATS_LEN; |
214 | } |
215 | |
216 | static void xgene_get_extd_stats(struct xgene_enet_pdata *pdata) |
217 | { |
218 | u32 rx_drop, tx_drop; |
219 | u32 mask, tmp; |
220 | int i; |
221 | |
222 | for (i = 0; i < XGENE_EXTD_STATS_LEN; i++) { |
223 | tmp = xgene_enet_rd_stat(pdata, rd_addr: gstrings_extd_stats[i].addr); |
224 | if (gstrings_extd_stats[i].mask) { |
225 | mask = GENMASK(gstrings_extd_stats[i].mask - 1, 0); |
226 | pdata->extd_stats[i] += (tmp & mask); |
227 | } |
228 | } |
229 | |
230 | if (pdata->phy_mode == PHY_INTERFACE_MODE_XGMII) { |
231 | /* Errata 10GE_10 - SW should intepret RALN as 0 */ |
232 | pdata->extd_stats[RALN_IDX] = 0; |
233 | } else { |
234 | /* Errata ENET_15 - Fixes RFCS, RFLR, TFCS counter */ |
235 | pdata->extd_stats[RFCS_IDX] -= pdata->extd_stats[RALN_IDX]; |
236 | pdata->extd_stats[RFLR_IDX] -= pdata->extd_stats[RUND_IDX]; |
237 | pdata->extd_stats[TFCS_IDX] -= pdata->extd_stats[TFRG_IDX]; |
238 | } |
239 | |
240 | pdata->mac_ops->get_drop_cnt(pdata, &rx_drop, &tx_drop); |
241 | pdata->extd_stats[RX_OVERRUN_IDX] += rx_drop; |
242 | pdata->extd_stats[TX_UNDERRUN_IDX] += tx_drop; |
243 | |
244 | /* Errata 10GE_8 - Update Frame recovered from Errata 10GE_8/ENET_11 */ |
245 | pdata->extd_stats[FALSE_RFLR_IDX] = pdata->false_rflr; |
246 | /* Errata ENET_15 - Jabber Frame recov'ed from Errata 10GE_10/ENET_15 */ |
247 | pdata->extd_stats[FALSE_RJBR_IDX] = pdata->vlan_rjbr; |
248 | } |
249 | |
250 | int xgene_extd_stats_init(struct xgene_enet_pdata *pdata) |
251 | { |
252 | pdata->extd_stats = devm_kmalloc_array(dev: &pdata->pdev->dev, |
253 | XGENE_EXTD_STATS_LEN, size: sizeof(u64), GFP_KERNEL); |
254 | if (!pdata->extd_stats) |
255 | return -ENOMEM; |
256 | |
257 | xgene_get_extd_stats(pdata); |
258 | memset(pdata->extd_stats, 0, XGENE_EXTD_STATS_LEN * sizeof(u64)); |
259 | |
260 | return 0; |
261 | } |
262 | |
263 | static void xgene_get_ethtool_stats(struct net_device *ndev, |
264 | struct ethtool_stats *dummy, |
265 | u64 *data) |
266 | { |
267 | struct xgene_enet_pdata *pdata = netdev_priv(dev: ndev); |
268 | struct rtnl_link_stats64 stats; |
269 | int i; |
270 | |
271 | dev_get_stats(dev: ndev, storage: &stats); |
272 | for (i = 0; i < XGENE_STATS_LEN; i++) |
273 | data[i] = *(u64 *)((char *)&stats + gstrings_stats[i].offset); |
274 | |
275 | xgene_get_extd_stats(pdata); |
276 | for (i = 0; i < XGENE_EXTD_STATS_LEN; i++) |
277 | data[i + XGENE_STATS_LEN] = pdata->extd_stats[i]; |
278 | } |
279 | |
280 | static void xgene_get_pauseparam(struct net_device *ndev, |
281 | struct ethtool_pauseparam *pp) |
282 | { |
283 | struct xgene_enet_pdata *pdata = netdev_priv(dev: ndev); |
284 | |
285 | pp->autoneg = pdata->pause_autoneg; |
286 | pp->tx_pause = pdata->tx_pause; |
287 | pp->rx_pause = pdata->rx_pause; |
288 | } |
289 | |
290 | static int xgene_set_pauseparam(struct net_device *ndev, |
291 | struct ethtool_pauseparam *pp) |
292 | { |
293 | struct xgene_enet_pdata *pdata = netdev_priv(dev: ndev); |
294 | struct phy_device *phydev = ndev->phydev; |
295 | |
296 | if (phy_interface_mode_is_rgmii(mode: pdata->phy_mode) || |
297 | pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) { |
298 | if (!phydev) |
299 | return -EINVAL; |
300 | |
301 | if (!phy_validate_pause(phydev, pp)) |
302 | return -EINVAL; |
303 | |
304 | pdata->pause_autoneg = pp->autoneg; |
305 | pdata->tx_pause = pp->tx_pause; |
306 | pdata->rx_pause = pp->rx_pause; |
307 | |
308 | phy_set_asym_pause(phydev, rx: pp->rx_pause, tx: pp->tx_pause); |
309 | |
310 | if (!pp->autoneg) { |
311 | pdata->mac_ops->flowctl_tx(pdata, pdata->tx_pause); |
312 | pdata->mac_ops->flowctl_rx(pdata, pdata->rx_pause); |
313 | } |
314 | } else { |
315 | if (pp->autoneg) |
316 | return -EINVAL; |
317 | |
318 | pdata->tx_pause = pp->tx_pause; |
319 | pdata->rx_pause = pp->rx_pause; |
320 | |
321 | pdata->mac_ops->flowctl_tx(pdata, pdata->tx_pause); |
322 | pdata->mac_ops->flowctl_rx(pdata, pdata->rx_pause); |
323 | } |
324 | |
325 | return 0; |
326 | } |
327 | |
328 | static const struct ethtool_ops xgene_ethtool_ops = { |
329 | .get_drvinfo = xgene_get_drvinfo, |
330 | .get_link = ethtool_op_get_link, |
331 | .get_strings = xgene_get_strings, |
332 | .get_sset_count = xgene_get_sset_count, |
333 | .get_ethtool_stats = xgene_get_ethtool_stats, |
334 | .get_link_ksettings = xgene_get_link_ksettings, |
335 | .set_link_ksettings = xgene_set_link_ksettings, |
336 | .get_pauseparam = xgene_get_pauseparam, |
337 | .set_pauseparam = xgene_set_pauseparam |
338 | }; |
339 | |
340 | void xgene_enet_set_ethtool_ops(struct net_device *ndev) |
341 | { |
342 | ndev->ethtool_ops = &xgene_ethtool_ops; |
343 | } |
344 | |