Linux Audio

Check our new training course

Linux debugging, profiling, tracing and performance analysis training

Mar 24-27, 2025, special US time zones
Register
Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0
  2/* Copyright (C) 2021 Gerhard Engleder <gerhard@engleder-embedded.com> */
  3
  4#include "tsnep.h"
  5
  6static 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
 20struct 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
 36static 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
 48struct 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
 63static const char tsnep_tx_queue_stats_strings[][ETH_GSTRING_LEN] = {
 64	"tx_%d_packets",
 65	"tx_%d_bytes",
 66	"tx_%d_dropped",
 67};
 68
 69struct 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
 78static 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
 88static 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
106static 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
117static 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
124static 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
131static 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
166static 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
240static 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
260static 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
283static 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
298static 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_combined = adapter->num_queues;
304	ch->combined_count = adapter->num_queues;
305}
306
307static int tsnep_ethtool_get_ts_info(struct net_device *netdev,
308				     struct kernel_ethtool_ts_info *info)
309{
310	struct tsnep_adapter *adapter = netdev_priv(netdev);
311
312	info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
313				SOF_TIMESTAMPING_TX_HARDWARE |
314				SOF_TIMESTAMPING_RX_HARDWARE |
315				SOF_TIMESTAMPING_RAW_HARDWARE;
316
317	if (adapter->ptp_clock)
318		info->phc_index = ptp_clock_index(adapter->ptp_clock);
319
320	info->tx_types = BIT(HWTSTAMP_TX_OFF) |
321			 BIT(HWTSTAMP_TX_ON);
322	info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
323			   BIT(HWTSTAMP_FILTER_ALL);
324
325	return 0;
326}
327
328static struct tsnep_queue *tsnep_get_queue_with_tx(struct tsnep_adapter *adapter,
329						   int index)
330{
331	int i;
332
333	for (i = 0; i < adapter->num_queues; i++) {
334		if (adapter->queue[i].tx) {
335			if (index == 0)
336				return &adapter->queue[i];
337
338			index--;
339		}
340	}
341
342	return NULL;
343}
344
345static struct tsnep_queue *tsnep_get_queue_with_rx(struct tsnep_adapter *adapter,
346						   int index)
347{
348	int i;
349
350	for (i = 0; i < adapter->num_queues; i++) {
351		if (adapter->queue[i].rx) {
352			if (index == 0)
353				return &adapter->queue[i];
354
355			index--;
356		}
357	}
358
359	return NULL;
360}
361
362static int tsnep_ethtool_get_coalesce(struct net_device *netdev,
363				      struct ethtool_coalesce *ec,
364				      struct kernel_ethtool_coalesce *kernel_coal,
365				      struct netlink_ext_ack *extack)
366{
367	struct tsnep_adapter *adapter = netdev_priv(netdev);
368	struct tsnep_queue *queue;
369
370	queue = tsnep_get_queue_with_rx(adapter, 0);
371	if (queue)
372		ec->rx_coalesce_usecs = tsnep_get_irq_coalesce(queue);
373
374	queue = tsnep_get_queue_with_tx(adapter, 0);
375	if (queue)
376		ec->tx_coalesce_usecs = tsnep_get_irq_coalesce(queue);
377
378	return 0;
379}
380
381static int tsnep_ethtool_set_coalesce(struct net_device *netdev,
382				      struct ethtool_coalesce *ec,
383				      struct kernel_ethtool_coalesce *kernel_coal,
384				      struct netlink_ext_ack *extack)
385{
386	struct tsnep_adapter *adapter = netdev_priv(netdev);
387	int i;
388	int retval;
389
390	for (i = 0; i < adapter->num_queues; i++) {
391		/* RX coalesce has priority for queues with TX and RX */
392		if (adapter->queue[i].rx)
393			retval = tsnep_set_irq_coalesce(&adapter->queue[i],
394							ec->rx_coalesce_usecs);
395		else
396			retval = tsnep_set_irq_coalesce(&adapter->queue[i],
397							ec->tx_coalesce_usecs);
398		if (retval != 0)
399			return retval;
400	}
401
402	return 0;
403}
404
405static int tsnep_ethtool_get_per_queue_coalesce(struct net_device *netdev,
406						u32 queue,
407						struct ethtool_coalesce *ec)
408{
409	struct tsnep_adapter *adapter = netdev_priv(netdev);
410	struct tsnep_queue *queue_with_rx;
411	struct tsnep_queue *queue_with_tx;
412
413	if (queue >= max(adapter->num_tx_queues, adapter->num_rx_queues))
414		return -EINVAL;
415
416	queue_with_rx = tsnep_get_queue_with_rx(adapter, queue);
417	if (queue_with_rx)
418		ec->rx_coalesce_usecs = tsnep_get_irq_coalesce(queue_with_rx);
419
420	queue_with_tx = tsnep_get_queue_with_tx(adapter, queue);
421	if (queue_with_tx)
422		ec->tx_coalesce_usecs = tsnep_get_irq_coalesce(queue_with_tx);
423
424	return 0;
425}
426
427static int tsnep_ethtool_set_per_queue_coalesce(struct net_device *netdev,
428						u32 queue,
429						struct ethtool_coalesce *ec)
430{
431	struct tsnep_adapter *adapter = netdev_priv(netdev);
432	struct tsnep_queue *queue_with_rx;
433	struct tsnep_queue *queue_with_tx;
434	int retval;
435
436	if (queue >= max(adapter->num_tx_queues, adapter->num_rx_queues))
437		return -EINVAL;
438
439	queue_with_rx = tsnep_get_queue_with_rx(adapter, queue);
440	if (queue_with_rx) {
441		retval = tsnep_set_irq_coalesce(queue_with_rx, ec->rx_coalesce_usecs);
442		if (retval != 0)
443			return retval;
444	}
445
446	/* RX coalesce has priority for queues with TX and RX */
447	queue_with_tx = tsnep_get_queue_with_tx(adapter, queue);
448	if (queue_with_tx && !queue_with_tx->rx) {
449		retval = tsnep_set_irq_coalesce(queue_with_tx, ec->tx_coalesce_usecs);
450		if (retval != 0)
451			return retval;
452	}
453
454	return 0;
455}
456
457const struct ethtool_ops tsnep_ethtool_ops = {
458	.supported_coalesce_params = ETHTOOL_COALESCE_USECS,
459	.get_drvinfo = tsnep_ethtool_get_drvinfo,
460	.get_regs_len = tsnep_ethtool_get_regs_len,
461	.get_regs = tsnep_ethtool_get_regs,
462	.get_msglevel = tsnep_ethtool_get_msglevel,
463	.set_msglevel = tsnep_ethtool_set_msglevel,
464	.nway_reset = phy_ethtool_nway_reset,
465	.get_link = ethtool_op_get_link,
466	.self_test = tsnep_ethtool_self_test,
467	.get_strings = tsnep_ethtool_get_strings,
468	.get_ethtool_stats = tsnep_ethtool_get_ethtool_stats,
469	.get_sset_count = tsnep_ethtool_get_sset_count,
470	.get_rxnfc = tsnep_ethtool_get_rxnfc,
471	.set_rxnfc = tsnep_ethtool_set_rxnfc,
472	.get_channels = tsnep_ethtool_get_channels,
473	.get_ts_info = tsnep_ethtool_get_ts_info,
474	.get_coalesce = tsnep_ethtool_get_coalesce,
475	.set_coalesce = tsnep_ethtool_set_coalesce,
476	.get_per_queue_coalesce = tsnep_ethtool_get_per_queue_coalesce,
477	.set_per_queue_coalesce = tsnep_ethtool_set_per_queue_coalesce,
478	.get_link_ksettings = phy_ethtool_get_link_ksettings,
479	.set_link_ksettings = phy_ethtool_set_link_ksettings,
480};