xref: /linux/drivers/net/ethernet/engleder/tsnep_ethtool.c (revision 9f2c9170934eace462499ba0bfe042cc72900173)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (C) 2021 Gerhard Engleder <gerhard@engleder-embedded.com> */
3 
4 #include "tsnep.h"
5 
6 static const char tsnep_stats_strings[][ETH_GSTRING_LEN] = {
7 	"rx_packets",
8 	"rx_bytes",
9 	"rx_dropped",
10 	"rx_multicast",
11 	"rx_alloc_failed",
12 	"rx_phy_errors",
13 	"rx_forwarded_phy_errors",
14 	"rx_invalid_frame_errors",
15 	"tx_packets",
16 	"tx_bytes",
17 	"tx_dropped",
18 };
19 
20 struct tsnep_stats {
21 	u64 rx_packets;
22 	u64 rx_bytes;
23 	u64 rx_dropped;
24 	u64 rx_multicast;
25 	u64 rx_alloc_failed;
26 	u64 rx_phy_errors;
27 	u64 rx_forwarded_phy_errors;
28 	u64 rx_invalid_frame_errors;
29 	u64 tx_packets;
30 	u64 tx_bytes;
31 	u64 tx_dropped;
32 };
33 
34 #define TSNEP_STATS_COUNT (sizeof(struct tsnep_stats) / sizeof(u64))
35 
36 static const char tsnep_rx_queue_stats_strings[][ETH_GSTRING_LEN] = {
37 	"rx_%d_packets",
38 	"rx_%d_bytes",
39 	"rx_%d_dropped",
40 	"rx_%d_multicast",
41 	"rx_%d_alloc_failed",
42 	"rx_%d_no_descriptor_errors",
43 	"rx_%d_buffer_too_small_errors",
44 	"rx_%d_fifo_overflow_errors",
45 	"rx_%d_invalid_frame_errors",
46 };
47 
48 struct tsnep_rx_queue_stats {
49 	u64 rx_packets;
50 	u64 rx_bytes;
51 	u64 rx_dropped;
52 	u64 rx_multicast;
53 	u64 rx_alloc_failed;
54 	u64 rx_no_descriptor_errors;
55 	u64 rx_buffer_too_small_errors;
56 	u64 rx_fifo_overflow_errors;
57 	u64 rx_invalid_frame_errors;
58 };
59 
60 #define TSNEP_RX_QUEUE_STATS_COUNT (sizeof(struct tsnep_rx_queue_stats) / \
61 				    sizeof(u64))
62 
63 static const char tsnep_tx_queue_stats_strings[][ETH_GSTRING_LEN] = {
64 	"tx_%d_packets",
65 	"tx_%d_bytes",
66 	"tx_%d_dropped",
67 };
68 
69 struct tsnep_tx_queue_stats {
70 	u64 tx_packets;
71 	u64 tx_bytes;
72 	u64 tx_dropped;
73 };
74 
75 #define TSNEP_TX_QUEUE_STATS_COUNT (sizeof(struct tsnep_tx_queue_stats) / \
76 				    sizeof(u64))
77 
78 static void tsnep_ethtool_get_drvinfo(struct net_device *netdev,
79 				      struct ethtool_drvinfo *drvinfo)
80 {
81 	struct tsnep_adapter *adapter = netdev_priv(netdev);
82 
83 	strscpy(drvinfo->driver, TSNEP, sizeof(drvinfo->driver));
84 	strscpy(drvinfo->bus_info, dev_name(&adapter->pdev->dev),
85 		sizeof(drvinfo->bus_info));
86 }
87 
88 static int tsnep_ethtool_get_regs_len(struct net_device *netdev)
89 {
90 	struct tsnep_adapter *adapter = netdev_priv(netdev);
91 	int len;
92 	int num_additional_queues;
93 
94 	len = TSNEP_MAC_SIZE;
95 
96 	/* first queue pair is within TSNEP_MAC_SIZE, only queues additional to
97 	 * the first queue pair extend the register length by TSNEP_QUEUE_SIZE
98 	 */
99 	num_additional_queues =
100 		max(adapter->num_tx_queues, adapter->num_rx_queues) - 1;
101 	len += TSNEP_QUEUE_SIZE * num_additional_queues;
102 
103 	return len;
104 }
105 
106 static void tsnep_ethtool_get_regs(struct net_device *netdev,
107 				   struct ethtool_regs *regs,
108 				   void *p)
109 {
110 	struct tsnep_adapter *adapter = netdev_priv(netdev);
111 
112 	regs->version = 1;
113 
114 	memcpy_fromio(p, adapter->addr, regs->len);
115 }
116 
117 static u32 tsnep_ethtool_get_msglevel(struct net_device *netdev)
118 {
119 	struct tsnep_adapter *adapter = netdev_priv(netdev);
120 
121 	return adapter->msg_enable;
122 }
123 
124 static void tsnep_ethtool_set_msglevel(struct net_device *netdev, u32 data)
125 {
126 	struct tsnep_adapter *adapter = netdev_priv(netdev);
127 
128 	adapter->msg_enable = data;
129 }
130 
131 static void tsnep_ethtool_get_strings(struct net_device *netdev, u32 stringset,
132 				      u8 *data)
133 {
134 	struct tsnep_adapter *adapter = netdev_priv(netdev);
135 	int rx_count = adapter->num_rx_queues;
136 	int tx_count = adapter->num_tx_queues;
137 	int i, j;
138 
139 	switch (stringset) {
140 	case ETH_SS_STATS:
141 		memcpy(data, tsnep_stats_strings, sizeof(tsnep_stats_strings));
142 		data += sizeof(tsnep_stats_strings);
143 
144 		for (i = 0; i < rx_count; i++) {
145 			for (j = 0; j < TSNEP_RX_QUEUE_STATS_COUNT; j++) {
146 				snprintf(data, ETH_GSTRING_LEN,
147 					 tsnep_rx_queue_stats_strings[j], i);
148 				data += ETH_GSTRING_LEN;
149 			}
150 		}
151 
152 		for (i = 0; i < tx_count; i++) {
153 			for (j = 0; j < TSNEP_TX_QUEUE_STATS_COUNT; j++) {
154 				snprintf(data, ETH_GSTRING_LEN,
155 					 tsnep_tx_queue_stats_strings[j], i);
156 				data += ETH_GSTRING_LEN;
157 			}
158 		}
159 		break;
160 	case ETH_SS_TEST:
161 		tsnep_ethtool_get_test_strings(data);
162 		break;
163 	}
164 }
165 
166 static void tsnep_ethtool_get_ethtool_stats(struct net_device *netdev,
167 					    struct ethtool_stats *stats,
168 					    u64 *data)
169 {
170 	struct tsnep_adapter *adapter = netdev_priv(netdev);
171 	int rx_count = adapter->num_rx_queues;
172 	int tx_count = adapter->num_tx_queues;
173 	struct tsnep_stats tsnep_stats;
174 	struct tsnep_rx_queue_stats tsnep_rx_queue_stats;
175 	struct tsnep_tx_queue_stats tsnep_tx_queue_stats;
176 	u32 reg;
177 	int i;
178 
179 	memset(&tsnep_stats, 0, sizeof(tsnep_stats));
180 	for (i = 0; i < adapter->num_rx_queues; i++) {
181 		tsnep_stats.rx_packets += adapter->rx[i].packets;
182 		tsnep_stats.rx_bytes += adapter->rx[i].bytes;
183 		tsnep_stats.rx_dropped += adapter->rx[i].dropped;
184 		tsnep_stats.rx_multicast += adapter->rx[i].multicast;
185 		tsnep_stats.rx_alloc_failed += adapter->rx[i].alloc_failed;
186 	}
187 	reg = ioread32(adapter->addr + ECM_STAT);
188 	tsnep_stats.rx_phy_errors =
189 		(reg & ECM_STAT_RX_ERR_MASK) >> ECM_STAT_RX_ERR_SHIFT;
190 	tsnep_stats.rx_forwarded_phy_errors =
191 		(reg & ECM_STAT_FWD_RX_ERR_MASK) >> ECM_STAT_FWD_RX_ERR_SHIFT;
192 	tsnep_stats.rx_invalid_frame_errors =
193 		(reg & ECM_STAT_INV_FRM_MASK) >> ECM_STAT_INV_FRM_SHIFT;
194 	for (i = 0; i < adapter->num_tx_queues; i++) {
195 		tsnep_stats.tx_packets += adapter->tx[i].packets;
196 		tsnep_stats.tx_bytes += adapter->tx[i].bytes;
197 		tsnep_stats.tx_dropped += adapter->tx[i].dropped;
198 	}
199 	memcpy(data, &tsnep_stats, sizeof(tsnep_stats));
200 	data += TSNEP_STATS_COUNT;
201 
202 	for (i = 0; i < rx_count; i++) {
203 		memset(&tsnep_rx_queue_stats, 0, sizeof(tsnep_rx_queue_stats));
204 		tsnep_rx_queue_stats.rx_packets = adapter->rx[i].packets;
205 		tsnep_rx_queue_stats.rx_bytes = adapter->rx[i].bytes;
206 		tsnep_rx_queue_stats.rx_dropped = adapter->rx[i].dropped;
207 		tsnep_rx_queue_stats.rx_multicast = adapter->rx[i].multicast;
208 		tsnep_rx_queue_stats.rx_alloc_failed =
209 			adapter->rx[i].alloc_failed;
210 		reg = ioread32(adapter->addr + TSNEP_QUEUE(i) +
211 			       TSNEP_RX_STATISTIC);
212 		tsnep_rx_queue_stats.rx_no_descriptor_errors =
213 			(reg & TSNEP_RX_STATISTIC_NO_DESC_MASK) >>
214 			TSNEP_RX_STATISTIC_NO_DESC_SHIFT;
215 		tsnep_rx_queue_stats.rx_buffer_too_small_errors =
216 			(reg & TSNEP_RX_STATISTIC_BUFFER_TOO_SMALL_MASK) >>
217 			TSNEP_RX_STATISTIC_BUFFER_TOO_SMALL_SHIFT;
218 		tsnep_rx_queue_stats.rx_fifo_overflow_errors =
219 			(reg & TSNEP_RX_STATISTIC_FIFO_OVERFLOW_MASK) >>
220 			TSNEP_RX_STATISTIC_FIFO_OVERFLOW_SHIFT;
221 		tsnep_rx_queue_stats.rx_invalid_frame_errors =
222 			(reg & TSNEP_RX_STATISTIC_INVALID_FRAME_MASK) >>
223 			TSNEP_RX_STATISTIC_INVALID_FRAME_SHIFT;
224 		memcpy(data, &tsnep_rx_queue_stats,
225 		       sizeof(tsnep_rx_queue_stats));
226 		data += TSNEP_RX_QUEUE_STATS_COUNT;
227 	}
228 
229 	for (i = 0; i < tx_count; i++) {
230 		memset(&tsnep_tx_queue_stats, 0, sizeof(tsnep_tx_queue_stats));
231 		tsnep_tx_queue_stats.tx_packets += adapter->tx[i].packets;
232 		tsnep_tx_queue_stats.tx_bytes += adapter->tx[i].bytes;
233 		tsnep_tx_queue_stats.tx_dropped += adapter->tx[i].dropped;
234 		memcpy(data, &tsnep_tx_queue_stats,
235 		       sizeof(tsnep_tx_queue_stats));
236 		data += TSNEP_TX_QUEUE_STATS_COUNT;
237 	}
238 }
239 
240 static int tsnep_ethtool_get_sset_count(struct net_device *netdev, int sset)
241 {
242 	struct tsnep_adapter *adapter = netdev_priv(netdev);
243 	int rx_count;
244 	int tx_count;
245 
246 	switch (sset) {
247 	case ETH_SS_STATS:
248 		rx_count = adapter->num_rx_queues;
249 		tx_count = adapter->num_tx_queues;
250 		return TSNEP_STATS_COUNT +
251 		       TSNEP_RX_QUEUE_STATS_COUNT * rx_count +
252 		       TSNEP_TX_QUEUE_STATS_COUNT * tx_count;
253 	case ETH_SS_TEST:
254 		return tsnep_ethtool_get_test_count();
255 	default:
256 		return -EOPNOTSUPP;
257 	}
258 }
259 
260 static int tsnep_ethtool_get_rxnfc(struct net_device *netdev,
261 				   struct ethtool_rxnfc *cmd, u32 *rule_locs)
262 {
263 	struct tsnep_adapter *adapter = netdev_priv(netdev);
264 
265 	switch (cmd->cmd) {
266 	case ETHTOOL_GRXRINGS:
267 		cmd->data = adapter->num_rx_queues;
268 		return 0;
269 	case ETHTOOL_GRXCLSRLCNT:
270 		cmd->rule_cnt = adapter->rxnfc_count;
271 		cmd->data = adapter->rxnfc_max;
272 		cmd->data |= RX_CLS_LOC_SPECIAL;
273 		return 0;
274 	case ETHTOOL_GRXCLSRULE:
275 		return tsnep_rxnfc_get_rule(adapter, cmd);
276 	case ETHTOOL_GRXCLSRLALL:
277 		return tsnep_rxnfc_get_all(adapter, cmd, rule_locs);
278 	default:
279 		return -EOPNOTSUPP;
280 	}
281 }
282 
283 static int tsnep_ethtool_set_rxnfc(struct net_device *netdev,
284 				   struct ethtool_rxnfc *cmd)
285 {
286 	struct tsnep_adapter *adapter = netdev_priv(netdev);
287 
288 	switch (cmd->cmd) {
289 	case ETHTOOL_SRXCLSRLINS:
290 		return tsnep_rxnfc_add_rule(adapter, cmd);
291 	case ETHTOOL_SRXCLSRLDEL:
292 		return tsnep_rxnfc_del_rule(adapter, cmd);
293 	default:
294 		return -EOPNOTSUPP;
295 	}
296 }
297 
298 static void tsnep_ethtool_get_channels(struct net_device *netdev,
299 				       struct ethtool_channels *ch)
300 {
301 	struct tsnep_adapter *adapter = netdev_priv(netdev);
302 
303 	ch->max_rx = adapter->num_rx_queues;
304 	ch->max_tx = adapter->num_tx_queues;
305 	ch->rx_count = adapter->num_rx_queues;
306 	ch->tx_count = adapter->num_tx_queues;
307 }
308 
309 static int tsnep_ethtool_get_ts_info(struct net_device *netdev,
310 				     struct ethtool_ts_info *info)
311 {
312 	struct tsnep_adapter *adapter = netdev_priv(netdev);
313 
314 	info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
315 				SOF_TIMESTAMPING_RX_SOFTWARE |
316 				SOF_TIMESTAMPING_SOFTWARE |
317 				SOF_TIMESTAMPING_TX_HARDWARE |
318 				SOF_TIMESTAMPING_RX_HARDWARE |
319 				SOF_TIMESTAMPING_RAW_HARDWARE;
320 
321 	if (adapter->ptp_clock)
322 		info->phc_index = ptp_clock_index(adapter->ptp_clock);
323 	else
324 		info->phc_index = -1;
325 
326 	info->tx_types = BIT(HWTSTAMP_TX_OFF) |
327 			 BIT(HWTSTAMP_TX_ON);
328 	info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
329 			   BIT(HWTSTAMP_FILTER_ALL);
330 
331 	return 0;
332 }
333 
334 static struct tsnep_queue *tsnep_get_queue_with_tx(struct tsnep_adapter *adapter,
335 						   int index)
336 {
337 	int i;
338 
339 	for (i = 0; i < adapter->num_queues; i++) {
340 		if (adapter->queue[i].tx) {
341 			if (index == 0)
342 				return &adapter->queue[i];
343 
344 			index--;
345 		}
346 	}
347 
348 	return NULL;
349 }
350 
351 static struct tsnep_queue *tsnep_get_queue_with_rx(struct tsnep_adapter *adapter,
352 						   int index)
353 {
354 	int i;
355 
356 	for (i = 0; i < adapter->num_queues; i++) {
357 		if (adapter->queue[i].rx) {
358 			if (index == 0)
359 				return &adapter->queue[i];
360 
361 			index--;
362 		}
363 	}
364 
365 	return NULL;
366 }
367 
368 static int tsnep_ethtool_get_coalesce(struct net_device *netdev,
369 				      struct ethtool_coalesce *ec,
370 				      struct kernel_ethtool_coalesce *kernel_coal,
371 				      struct netlink_ext_ack *extack)
372 {
373 	struct tsnep_adapter *adapter = netdev_priv(netdev);
374 	struct tsnep_queue *queue;
375 
376 	queue = tsnep_get_queue_with_rx(adapter, 0);
377 	if (queue)
378 		ec->rx_coalesce_usecs = tsnep_get_irq_coalesce(queue);
379 
380 	queue = tsnep_get_queue_with_tx(adapter, 0);
381 	if (queue)
382 		ec->tx_coalesce_usecs = tsnep_get_irq_coalesce(queue);
383 
384 	return 0;
385 }
386 
387 static int tsnep_ethtool_set_coalesce(struct net_device *netdev,
388 				      struct ethtool_coalesce *ec,
389 				      struct kernel_ethtool_coalesce *kernel_coal,
390 				      struct netlink_ext_ack *extack)
391 {
392 	struct tsnep_adapter *adapter = netdev_priv(netdev);
393 	int i;
394 	int retval;
395 
396 	for (i = 0; i < adapter->num_queues; i++) {
397 		/* RX coalesce has priority for queues with TX and RX */
398 		if (adapter->queue[i].rx)
399 			retval = tsnep_set_irq_coalesce(&adapter->queue[i],
400 							ec->rx_coalesce_usecs);
401 		else
402 			retval = tsnep_set_irq_coalesce(&adapter->queue[i],
403 							ec->tx_coalesce_usecs);
404 		if (retval != 0)
405 			return retval;
406 	}
407 
408 	return 0;
409 }
410 
411 static int tsnep_ethtool_get_per_queue_coalesce(struct net_device *netdev,
412 						u32 queue,
413 						struct ethtool_coalesce *ec)
414 {
415 	struct tsnep_adapter *adapter = netdev_priv(netdev);
416 	struct tsnep_queue *queue_with_rx;
417 	struct tsnep_queue *queue_with_tx;
418 
419 	if (queue >= max(adapter->num_tx_queues, adapter->num_rx_queues))
420 		return -EINVAL;
421 
422 	queue_with_rx = tsnep_get_queue_with_rx(adapter, queue);
423 	if (queue_with_rx)
424 		ec->rx_coalesce_usecs = tsnep_get_irq_coalesce(queue_with_rx);
425 
426 	queue_with_tx = tsnep_get_queue_with_tx(adapter, queue);
427 	if (queue_with_tx)
428 		ec->tx_coalesce_usecs = tsnep_get_irq_coalesce(queue_with_tx);
429 
430 	return 0;
431 }
432 
433 static int tsnep_ethtool_set_per_queue_coalesce(struct net_device *netdev,
434 						u32 queue,
435 						struct ethtool_coalesce *ec)
436 {
437 	struct tsnep_adapter *adapter = netdev_priv(netdev);
438 	struct tsnep_queue *queue_with_rx;
439 	struct tsnep_queue *queue_with_tx;
440 	int retval;
441 
442 	if (queue >= max(adapter->num_tx_queues, adapter->num_rx_queues))
443 		return -EINVAL;
444 
445 	queue_with_rx = tsnep_get_queue_with_rx(adapter, queue);
446 	if (queue_with_rx) {
447 		retval = tsnep_set_irq_coalesce(queue_with_rx, ec->rx_coalesce_usecs);
448 		if (retval != 0)
449 			return retval;
450 	}
451 
452 	/* RX coalesce has priority for queues with TX and RX */
453 	queue_with_tx = tsnep_get_queue_with_tx(adapter, queue);
454 	if (queue_with_tx && !queue_with_tx->rx) {
455 		retval = tsnep_set_irq_coalesce(queue_with_tx, ec->tx_coalesce_usecs);
456 		if (retval != 0)
457 			return retval;
458 	}
459 
460 	return 0;
461 }
462 
463 const struct ethtool_ops tsnep_ethtool_ops = {
464 	.supported_coalesce_params = ETHTOOL_COALESCE_USECS,
465 	.get_drvinfo = tsnep_ethtool_get_drvinfo,
466 	.get_regs_len = tsnep_ethtool_get_regs_len,
467 	.get_regs = tsnep_ethtool_get_regs,
468 	.get_msglevel = tsnep_ethtool_get_msglevel,
469 	.set_msglevel = tsnep_ethtool_set_msglevel,
470 	.nway_reset = phy_ethtool_nway_reset,
471 	.get_link = ethtool_op_get_link,
472 	.self_test = tsnep_ethtool_self_test,
473 	.get_strings = tsnep_ethtool_get_strings,
474 	.get_ethtool_stats = tsnep_ethtool_get_ethtool_stats,
475 	.get_sset_count = tsnep_ethtool_get_sset_count,
476 	.get_rxnfc = tsnep_ethtool_get_rxnfc,
477 	.set_rxnfc = tsnep_ethtool_set_rxnfc,
478 	.get_channels = tsnep_ethtool_get_channels,
479 	.get_ts_info = tsnep_ethtool_get_ts_info,
480 	.get_coalesce = tsnep_ethtool_get_coalesce,
481 	.set_coalesce = tsnep_ethtool_set_coalesce,
482 	.get_per_queue_coalesce = tsnep_ethtool_get_per_queue_coalesce,
483 	.set_per_queue_coalesce = tsnep_ethtool_set_per_queue_coalesce,
484 	.get_link_ksettings = phy_ethtool_get_link_ksettings,
485 	.set_link_ksettings = phy_ethtool_set_link_ksettings,
486 };
487