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