Commit | Line | Data |
---|---|---|
d896ac62 JW |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright IBM Corp. 2018 | |
4 | */ | |
5 | ||
6 | #define KMSG_COMPONENT "qeth" | |
7 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | |
8 | ||
9 | #include <linux/ethtool.h> | |
10 | #include "qeth_core.h" | |
11 | ||
b0abc4f5 JW |
12 | |
13 | #define QETH_TXQ_STAT(_name, _stat) { \ | |
14 | .name = _name, \ | |
15 | .offset = offsetof(struct qeth_out_q_stats, _stat) \ | |
16 | } | |
17 | ||
18 | #define QETH_CARD_STAT(_name, _stat) { \ | |
19 | .name = _name, \ | |
20 | .offset = offsetof(struct qeth_card_stats, _stat) \ | |
21 | } | |
22 | ||
23 | struct qeth_stats { | |
24 | char name[ETH_GSTRING_LEN]; | |
25 | unsigned int offset; | |
26 | }; | |
27 | ||
28 | static const struct qeth_stats txq_stats[] = { | |
29 | QETH_TXQ_STAT("IO buffers", bufs), | |
30 | QETH_TXQ_STAT("IO buffer elements", buf_elements), | |
31 | QETH_TXQ_STAT("packed IO buffers", bufs_pack), | |
32 | QETH_TXQ_STAT("skbs", tx_packets), | |
33 | QETH_TXQ_STAT("packed skbs", skbs_pack), | |
34 | QETH_TXQ_STAT("SG skbs", skbs_sg), | |
35 | QETH_TXQ_STAT("HW csum skbs", skbs_csum), | |
36 | QETH_TXQ_STAT("TSO skbs", skbs_tso), | |
37 | QETH_TXQ_STAT("linearized skbs", skbs_linearized), | |
38 | QETH_TXQ_STAT("linearized+error skbs", skbs_linearized_fail), | |
39 | QETH_TXQ_STAT("TSO bytes", tso_bytes), | |
40 | QETH_TXQ_STAT("Packing mode switches", packing_mode_switch), | |
54a50941 | 41 | QETH_TXQ_STAT("Queue stopped", stopped), |
b0abc4f5 JW |
42 | }; |
43 | ||
44 | static const struct qeth_stats card_stats[] = { | |
45 | QETH_CARD_STAT("rx0 IO buffers", rx_bufs), | |
46 | QETH_CARD_STAT("rx0 HW csum skbs", rx_skb_csum), | |
47 | QETH_CARD_STAT("rx0 SG skbs", rx_sg_skbs), | |
48 | QETH_CARD_STAT("rx0 SG page frags", rx_sg_frags), | |
49 | QETH_CARD_STAT("rx0 SG page allocs", rx_sg_alloc_page), | |
d896ac62 JW |
50 | }; |
51 | ||
b0abc4f5 JW |
52 | #define TXQ_STATS_LEN ARRAY_SIZE(txq_stats) |
53 | #define CARD_STATS_LEN ARRAY_SIZE(card_stats) | |
54 | ||
55 | static void qeth_add_stat_data(u64 **dst, void *src, | |
56 | const struct qeth_stats stats[], | |
57 | unsigned int size) | |
58 | { | |
59 | unsigned int i; | |
60 | char *stat; | |
61 | ||
62 | for (i = 0; i < size; i++) { | |
63 | stat = (char *)src + stats[i].offset; | |
64 | **dst = *(u64 *)stat; | |
65 | (*dst)++; | |
66 | } | |
67 | } | |
68 | ||
69 | static void qeth_add_stat_strings(u8 **data, const char *prefix, | |
70 | const struct qeth_stats stats[], | |
71 | unsigned int size) | |
72 | { | |
73 | unsigned int i; | |
74 | ||
75 | for (i = 0; i < size; i++) { | |
76 | snprintf(*data, ETH_GSTRING_LEN, "%s%s", prefix, stats[i].name); | |
77 | *data += ETH_GSTRING_LEN; | |
78 | } | |
79 | } | |
80 | ||
d896ac62 JW |
81 | static int qeth_get_sset_count(struct net_device *dev, int stringset) |
82 | { | |
b0abc4f5 JW |
83 | struct qeth_card *card = dev->ml_priv; |
84 | ||
d896ac62 JW |
85 | switch (stringset) { |
86 | case ETH_SS_STATS: | |
b0abc4f5 JW |
87 | return CARD_STATS_LEN + |
88 | card->qdio.no_out_queues * TXQ_STATS_LEN; | |
d896ac62 JW |
89 | default: |
90 | return -EINVAL; | |
91 | } | |
92 | } | |
93 | ||
94 | static void qeth_get_ethtool_stats(struct net_device *dev, | |
95 | struct ethtool_stats *stats, u64 *data) | |
96 | { | |
97 | struct qeth_card *card = dev->ml_priv; | |
b0abc4f5 | 98 | unsigned int i; |
d896ac62 | 99 | |
b0abc4f5 JW |
100 | qeth_add_stat_data(&data, &card->stats, card_stats, CARD_STATS_LEN); |
101 | for (i = 0; i < card->qdio.no_out_queues; i++) | |
102 | qeth_add_stat_data(&data, &card->qdio.out_qs[i]->stats, | |
103 | txq_stats, TXQ_STATS_LEN); | |
d896ac62 JW |
104 | } |
105 | ||
1b4d5e1c JW |
106 | static void qeth_get_ringparam(struct net_device *dev, |
107 | struct ethtool_ringparam *param) | |
108 | { | |
109 | struct qeth_card *card = dev->ml_priv; | |
110 | ||
111 | param->rx_max_pending = QDIO_MAX_BUFFERS_PER_Q; | |
112 | param->rx_mini_max_pending = 0; | |
113 | param->rx_jumbo_max_pending = 0; | |
114 | param->tx_max_pending = QDIO_MAX_BUFFERS_PER_Q; | |
115 | ||
116 | param->rx_pending = card->qdio.in_buf_pool.buf_count; | |
117 | param->rx_mini_pending = 0; | |
118 | param->rx_jumbo_pending = 0; | |
119 | param->tx_pending = QDIO_MAX_BUFFERS_PER_Q; | |
120 | } | |
121 | ||
d896ac62 JW |
122 | static void qeth_get_strings(struct net_device *dev, u32 stringset, u8 *data) |
123 | { | |
b0abc4f5 JW |
124 | struct qeth_card *card = dev->ml_priv; |
125 | char prefix[ETH_GSTRING_LEN] = ""; | |
126 | unsigned int i; | |
127 | ||
d896ac62 JW |
128 | switch (stringset) { |
129 | case ETH_SS_STATS: | |
b0abc4f5 JW |
130 | qeth_add_stat_strings(&data, prefix, card_stats, |
131 | CARD_STATS_LEN); | |
132 | for (i = 0; i < card->qdio.no_out_queues; i++) { | |
133 | snprintf(prefix, ETH_GSTRING_LEN, "tx%u ", i); | |
134 | qeth_add_stat_strings(&data, prefix, txq_stats, | |
135 | TXQ_STATS_LEN); | |
136 | } | |
d896ac62 JW |
137 | break; |
138 | default: | |
139 | WARN_ON(1); | |
140 | break; | |
141 | } | |
142 | } | |
143 | ||
144 | static void qeth_get_drvinfo(struct net_device *dev, | |
145 | struct ethtool_drvinfo *info) | |
146 | { | |
147 | struct qeth_card *card = dev->ml_priv; | |
148 | ||
149 | strlcpy(info->driver, IS_LAYER2(card) ? "qeth_l2" : "qeth_l3", | |
150 | sizeof(info->driver)); | |
151 | strlcpy(info->version, "1.0", sizeof(info->version)); | |
152 | strlcpy(info->fw_version, card->info.mcl_level, | |
153 | sizeof(info->fw_version)); | |
154 | snprintf(info->bus_info, sizeof(info->bus_info), "%s/%s/%s", | |
155 | CARD_RDEV_ID(card), CARD_WDEV_ID(card), CARD_DDEV_ID(card)); | |
156 | } | |
157 | ||
3a18d754 JW |
158 | static void qeth_get_channels(struct net_device *dev, |
159 | struct ethtool_channels *channels) | |
160 | { | |
161 | struct qeth_card *card = dev->ml_priv; | |
162 | ||
163 | channels->max_rx = dev->num_rx_queues; | |
164 | channels->max_tx = card->qdio.no_out_queues; | |
165 | channels->max_other = 0; | |
166 | channels->max_combined = 0; | |
167 | channels->rx_count = dev->real_num_rx_queues; | |
168 | channels->tx_count = dev->real_num_tx_queues; | |
169 | channels->other_count = 0; | |
170 | channels->combined_count = 0; | |
171 | } | |
172 | ||
d896ac62 JW |
173 | /* Helper function to fill 'advertising' and 'supported' which are the same. */ |
174 | /* Autoneg and full-duplex are supported and advertised unconditionally. */ | |
175 | /* Always advertise and support all speeds up to specified, and only one */ | |
176 | /* specified port type. */ | |
177 | static void qeth_set_cmd_adv_sup(struct ethtool_link_ksettings *cmd, | |
178 | int maxspeed, int porttype) | |
179 | { | |
180 | ethtool_link_ksettings_zero_link_mode(cmd, supported); | |
181 | ethtool_link_ksettings_zero_link_mode(cmd, advertising); | |
182 | ethtool_link_ksettings_zero_link_mode(cmd, lp_advertising); | |
183 | ||
184 | ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg); | |
185 | ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg); | |
186 | ||
187 | switch (porttype) { | |
188 | case PORT_TP: | |
189 | ethtool_link_ksettings_add_link_mode(cmd, supported, TP); | |
190 | ethtool_link_ksettings_add_link_mode(cmd, advertising, TP); | |
191 | break; | |
192 | case PORT_FIBRE: | |
193 | ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE); | |
194 | ethtool_link_ksettings_add_link_mode(cmd, advertising, FIBRE); | |
195 | break; | |
196 | default: | |
197 | ethtool_link_ksettings_add_link_mode(cmd, supported, TP); | |
198 | ethtool_link_ksettings_add_link_mode(cmd, advertising, TP); | |
199 | WARN_ON_ONCE(1); | |
200 | } | |
201 | ||
202 | /* partially does fall through, to also select lower speeds */ | |
203 | switch (maxspeed) { | |
204 | case SPEED_25000: | |
205 | ethtool_link_ksettings_add_link_mode(cmd, supported, | |
206 | 25000baseSR_Full); | |
207 | ethtool_link_ksettings_add_link_mode(cmd, advertising, | |
208 | 25000baseSR_Full); | |
209 | break; | |
210 | case SPEED_10000: | |
211 | ethtool_link_ksettings_add_link_mode(cmd, supported, | |
212 | 10000baseT_Full); | |
213 | ethtool_link_ksettings_add_link_mode(cmd, advertising, | |
214 | 10000baseT_Full); | |
215 | /* fall through */ | |
216 | case SPEED_1000: | |
217 | ethtool_link_ksettings_add_link_mode(cmd, supported, | |
218 | 1000baseT_Full); | |
219 | ethtool_link_ksettings_add_link_mode(cmd, advertising, | |
220 | 1000baseT_Full); | |
221 | ethtool_link_ksettings_add_link_mode(cmd, supported, | |
222 | 1000baseT_Half); | |
223 | ethtool_link_ksettings_add_link_mode(cmd, advertising, | |
224 | 1000baseT_Half); | |
225 | /* fall through */ | |
226 | case SPEED_100: | |
227 | ethtool_link_ksettings_add_link_mode(cmd, supported, | |
228 | 100baseT_Full); | |
229 | ethtool_link_ksettings_add_link_mode(cmd, advertising, | |
230 | 100baseT_Full); | |
231 | ethtool_link_ksettings_add_link_mode(cmd, supported, | |
232 | 100baseT_Half); | |
233 | ethtool_link_ksettings_add_link_mode(cmd, advertising, | |
234 | 100baseT_Half); | |
235 | /* fall through */ | |
236 | case SPEED_10: | |
237 | ethtool_link_ksettings_add_link_mode(cmd, supported, | |
238 | 10baseT_Full); | |
239 | ethtool_link_ksettings_add_link_mode(cmd, advertising, | |
240 | 10baseT_Full); | |
241 | ethtool_link_ksettings_add_link_mode(cmd, supported, | |
242 | 10baseT_Half); | |
243 | ethtool_link_ksettings_add_link_mode(cmd, advertising, | |
244 | 10baseT_Half); | |
245 | break; | |
246 | default: | |
247 | ethtool_link_ksettings_add_link_mode(cmd, supported, | |
248 | 10baseT_Full); | |
249 | ethtool_link_ksettings_add_link_mode(cmd, advertising, | |
250 | 10baseT_Full); | |
251 | ethtool_link_ksettings_add_link_mode(cmd, supported, | |
252 | 10baseT_Half); | |
253 | ethtool_link_ksettings_add_link_mode(cmd, advertising, | |
254 | 10baseT_Half); | |
255 | WARN_ON_ONCE(1); | |
256 | } | |
257 | } | |
258 | ||
259 | ||
260 | static int qeth_get_link_ksettings(struct net_device *netdev, | |
261 | struct ethtool_link_ksettings *cmd) | |
262 | { | |
263 | struct qeth_card *card = netdev->ml_priv; | |
264 | enum qeth_link_types link_type; | |
265 | struct carrier_info carrier_info; | |
266 | int rc; | |
267 | ||
268 | if (IS_IQD(card) || IS_VM_NIC(card)) | |
269 | link_type = QETH_LINK_TYPE_10GBIT_ETH; | |
270 | else | |
271 | link_type = card->info.link_type; | |
272 | ||
273 | cmd->base.duplex = DUPLEX_FULL; | |
274 | cmd->base.autoneg = AUTONEG_ENABLE; | |
275 | cmd->base.phy_address = 0; | |
276 | cmd->base.mdio_support = 0; | |
277 | cmd->base.eth_tp_mdix = ETH_TP_MDI_INVALID; | |
278 | cmd->base.eth_tp_mdix_ctrl = ETH_TP_MDI_INVALID; | |
279 | ||
280 | switch (link_type) { | |
281 | case QETH_LINK_TYPE_FAST_ETH: | |
282 | case QETH_LINK_TYPE_LANE_ETH100: | |
283 | cmd->base.speed = SPEED_100; | |
284 | cmd->base.port = PORT_TP; | |
285 | break; | |
286 | case QETH_LINK_TYPE_GBIT_ETH: | |
287 | case QETH_LINK_TYPE_LANE_ETH1000: | |
288 | cmd->base.speed = SPEED_1000; | |
289 | cmd->base.port = PORT_FIBRE; | |
290 | break; | |
291 | case QETH_LINK_TYPE_10GBIT_ETH: | |
292 | cmd->base.speed = SPEED_10000; | |
293 | cmd->base.port = PORT_FIBRE; | |
294 | break; | |
295 | case QETH_LINK_TYPE_25GBIT_ETH: | |
296 | cmd->base.speed = SPEED_25000; | |
297 | cmd->base.port = PORT_FIBRE; | |
298 | break; | |
299 | default: | |
300 | cmd->base.speed = SPEED_10; | |
301 | cmd->base.port = PORT_TP; | |
302 | } | |
303 | qeth_set_cmd_adv_sup(cmd, cmd->base.speed, cmd->base.port); | |
304 | ||
305 | /* Check if we can obtain more accurate information. */ | |
306 | /* If QUERY_CARD_INFO command is not supported or fails, */ | |
307 | /* just return the heuristics that was filled above. */ | |
308 | rc = qeth_query_card_info(card, &carrier_info); | |
309 | if (rc == -EOPNOTSUPP) /* for old hardware, return heuristic */ | |
310 | return 0; | |
311 | if (rc) /* report error from the hardware operation */ | |
312 | return rc; | |
313 | /* on success, fill in the information got from the hardware */ | |
314 | ||
315 | netdev_dbg(netdev, | |
316 | "card info: card_type=0x%02x, port_mode=0x%04x, port_speed=0x%08x\n", | |
317 | carrier_info.card_type, | |
318 | carrier_info.port_mode, | |
319 | carrier_info.port_speed); | |
320 | ||
321 | /* Update attributes for which we've obtained more authoritative */ | |
322 | /* information, leave the rest the way they where filled above. */ | |
323 | switch (carrier_info.card_type) { | |
324 | case CARD_INFO_TYPE_1G_COPPER_A: | |
325 | case CARD_INFO_TYPE_1G_COPPER_B: | |
326 | cmd->base.port = PORT_TP; | |
327 | qeth_set_cmd_adv_sup(cmd, SPEED_1000, cmd->base.port); | |
328 | break; | |
329 | case CARD_INFO_TYPE_1G_FIBRE_A: | |
330 | case CARD_INFO_TYPE_1G_FIBRE_B: | |
331 | cmd->base.port = PORT_FIBRE; | |
332 | qeth_set_cmd_adv_sup(cmd, SPEED_1000, cmd->base.port); | |
333 | break; | |
334 | case CARD_INFO_TYPE_10G_FIBRE_A: | |
335 | case CARD_INFO_TYPE_10G_FIBRE_B: | |
336 | cmd->base.port = PORT_FIBRE; | |
337 | qeth_set_cmd_adv_sup(cmd, SPEED_10000, cmd->base.port); | |
338 | break; | |
339 | } | |
340 | ||
341 | switch (carrier_info.port_mode) { | |
342 | case CARD_INFO_PORTM_FULLDUPLEX: | |
343 | cmd->base.duplex = DUPLEX_FULL; | |
344 | break; | |
345 | case CARD_INFO_PORTM_HALFDUPLEX: | |
346 | cmd->base.duplex = DUPLEX_HALF; | |
347 | break; | |
348 | } | |
349 | ||
350 | switch (carrier_info.port_speed) { | |
351 | case CARD_INFO_PORTS_10M: | |
352 | cmd->base.speed = SPEED_10; | |
353 | break; | |
354 | case CARD_INFO_PORTS_100M: | |
355 | cmd->base.speed = SPEED_100; | |
356 | break; | |
357 | case CARD_INFO_PORTS_1G: | |
358 | cmd->base.speed = SPEED_1000; | |
359 | break; | |
360 | case CARD_INFO_PORTS_10G: | |
361 | cmd->base.speed = SPEED_10000; | |
362 | break; | |
363 | case CARD_INFO_PORTS_25G: | |
364 | cmd->base.speed = SPEED_25000; | |
365 | break; | |
366 | } | |
367 | ||
368 | return 0; | |
369 | } | |
370 | ||
371 | const struct ethtool_ops qeth_ethtool_ops = { | |
372 | .get_link = ethtool_op_get_link, | |
1b4d5e1c | 373 | .get_ringparam = qeth_get_ringparam, |
d896ac62 JW |
374 | .get_strings = qeth_get_strings, |
375 | .get_ethtool_stats = qeth_get_ethtool_stats, | |
376 | .get_sset_count = qeth_get_sset_count, | |
377 | .get_drvinfo = qeth_get_drvinfo, | |
3a18d754 | 378 | .get_channels = qeth_get_channels, |
d896ac62 JW |
379 | .get_link_ksettings = qeth_get_link_ksettings, |
380 | }; | |
381 | ||
382 | const struct ethtool_ops qeth_osn_ethtool_ops = { | |
383 | .get_strings = qeth_get_strings, | |
384 | .get_ethtool_stats = qeth_get_ethtool_stats, | |
385 | .get_sset_count = qeth_get_sset_count, | |
386 | .get_drvinfo = qeth_get_drvinfo, | |
387 | }; |