1403f69bbSGerhard Engleder // SPDX-License-Identifier: GPL-2.0
2403f69bbSGerhard Engleder /* Copyright (C) 2021 Gerhard Engleder <gerhard@engleder-embedded.com> */
3403f69bbSGerhard Engleder 
4403f69bbSGerhard Engleder #include "tsnep.h"
5403f69bbSGerhard Engleder 
6403f69bbSGerhard Engleder static const char tsnep_stats_strings[][ETH_GSTRING_LEN] = {
7403f69bbSGerhard Engleder 	"rx_packets",
8403f69bbSGerhard Engleder 	"rx_bytes",
9403f69bbSGerhard Engleder 	"rx_dropped",
10403f69bbSGerhard Engleder 	"rx_multicast",
11dbadae92SGerhard Engleder 	"rx_alloc_failed",
12403f69bbSGerhard Engleder 	"rx_phy_errors",
13403f69bbSGerhard Engleder 	"rx_forwarded_phy_errors",
14403f69bbSGerhard Engleder 	"rx_invalid_frame_errors",
15403f69bbSGerhard Engleder 	"tx_packets",
16403f69bbSGerhard Engleder 	"tx_bytes",
17403f69bbSGerhard Engleder 	"tx_dropped",
18403f69bbSGerhard Engleder };
19403f69bbSGerhard Engleder 
20403f69bbSGerhard Engleder struct tsnep_stats {
21403f69bbSGerhard Engleder 	u64 rx_packets;
22403f69bbSGerhard Engleder 	u64 rx_bytes;
23403f69bbSGerhard Engleder 	u64 rx_dropped;
24403f69bbSGerhard Engleder 	u64 rx_multicast;
25dbadae92SGerhard Engleder 	u64 rx_alloc_failed;
26403f69bbSGerhard Engleder 	u64 rx_phy_errors;
27403f69bbSGerhard Engleder 	u64 rx_forwarded_phy_errors;
28403f69bbSGerhard Engleder 	u64 rx_invalid_frame_errors;
29403f69bbSGerhard Engleder 	u64 tx_packets;
30403f69bbSGerhard Engleder 	u64 tx_bytes;
31403f69bbSGerhard Engleder 	u64 tx_dropped;
32403f69bbSGerhard Engleder };
33403f69bbSGerhard Engleder 
34403f69bbSGerhard Engleder #define TSNEP_STATS_COUNT (sizeof(struct tsnep_stats) / sizeof(u64))
35403f69bbSGerhard Engleder 
36403f69bbSGerhard Engleder static const char tsnep_rx_queue_stats_strings[][ETH_GSTRING_LEN] = {
37403f69bbSGerhard Engleder 	"rx_%d_packets",
38403f69bbSGerhard Engleder 	"rx_%d_bytes",
39403f69bbSGerhard Engleder 	"rx_%d_dropped",
40403f69bbSGerhard Engleder 	"rx_%d_multicast",
41dbadae92SGerhard Engleder 	"rx_%d_alloc_failed",
42403f69bbSGerhard Engleder 	"rx_%d_no_descriptor_errors",
43403f69bbSGerhard Engleder 	"rx_%d_buffer_too_small_errors",
44403f69bbSGerhard Engleder 	"rx_%d_fifo_overflow_errors",
45403f69bbSGerhard Engleder 	"rx_%d_invalid_frame_errors",
46403f69bbSGerhard Engleder };
47403f69bbSGerhard Engleder 
48403f69bbSGerhard Engleder struct tsnep_rx_queue_stats {
49403f69bbSGerhard Engleder 	u64 rx_packets;
50403f69bbSGerhard Engleder 	u64 rx_bytes;
51403f69bbSGerhard Engleder 	u64 rx_dropped;
52403f69bbSGerhard Engleder 	u64 rx_multicast;
53dbadae92SGerhard Engleder 	u64 rx_alloc_failed;
54403f69bbSGerhard Engleder 	u64 rx_no_descriptor_errors;
55403f69bbSGerhard Engleder 	u64 rx_buffer_too_small_errors;
56403f69bbSGerhard Engleder 	u64 rx_fifo_overflow_errors;
57403f69bbSGerhard Engleder 	u64 rx_invalid_frame_errors;
58403f69bbSGerhard Engleder };
59403f69bbSGerhard Engleder 
60403f69bbSGerhard Engleder #define TSNEP_RX_QUEUE_STATS_COUNT (sizeof(struct tsnep_rx_queue_stats) / \
61403f69bbSGerhard Engleder 				    sizeof(u64))
62403f69bbSGerhard Engleder 
63403f69bbSGerhard Engleder static const char tsnep_tx_queue_stats_strings[][ETH_GSTRING_LEN] = {
64403f69bbSGerhard Engleder 	"tx_%d_packets",
65403f69bbSGerhard Engleder 	"tx_%d_bytes",
66403f69bbSGerhard Engleder 	"tx_%d_dropped",
67403f69bbSGerhard Engleder };
68403f69bbSGerhard Engleder 
69403f69bbSGerhard Engleder struct tsnep_tx_queue_stats {
70403f69bbSGerhard Engleder 	u64 tx_packets;
71403f69bbSGerhard Engleder 	u64 tx_bytes;
72403f69bbSGerhard Engleder 	u64 tx_dropped;
73403f69bbSGerhard Engleder };
74403f69bbSGerhard Engleder 
75403f69bbSGerhard Engleder #define TSNEP_TX_QUEUE_STATS_COUNT (sizeof(struct tsnep_tx_queue_stats) / \
76403f69bbSGerhard Engleder 				    sizeof(u64))
77403f69bbSGerhard Engleder 
tsnep_ethtool_get_drvinfo(struct net_device * netdev,struct ethtool_drvinfo * drvinfo)78403f69bbSGerhard Engleder static void tsnep_ethtool_get_drvinfo(struct net_device *netdev,
79403f69bbSGerhard Engleder 				      struct ethtool_drvinfo *drvinfo)
80403f69bbSGerhard Engleder {
81403f69bbSGerhard Engleder 	struct tsnep_adapter *adapter = netdev_priv(netdev);
82403f69bbSGerhard Engleder 
83403f69bbSGerhard Engleder 	strscpy(drvinfo->driver, TSNEP, sizeof(drvinfo->driver));
84403f69bbSGerhard Engleder 	strscpy(drvinfo->bus_info, dev_name(&adapter->pdev->dev),
85403f69bbSGerhard Engleder 		sizeof(drvinfo->bus_info));
86403f69bbSGerhard Engleder }
87403f69bbSGerhard Engleder 
tsnep_ethtool_get_regs_len(struct net_device * netdev)88403f69bbSGerhard Engleder static int tsnep_ethtool_get_regs_len(struct net_device *netdev)
89403f69bbSGerhard Engleder {
90403f69bbSGerhard Engleder 	struct tsnep_adapter *adapter = netdev_priv(netdev);
91403f69bbSGerhard Engleder 	int len;
92403f69bbSGerhard Engleder 	int num_additional_queues;
93403f69bbSGerhard Engleder 
94403f69bbSGerhard Engleder 	len = TSNEP_MAC_SIZE;
95403f69bbSGerhard Engleder 
96403f69bbSGerhard Engleder 	/* first queue pair is within TSNEP_MAC_SIZE, only queues additional to
97403f69bbSGerhard Engleder 	 * the first queue pair extend the register length by TSNEP_QUEUE_SIZE
98403f69bbSGerhard Engleder 	 */
99403f69bbSGerhard Engleder 	num_additional_queues =
100403f69bbSGerhard Engleder 		max(adapter->num_tx_queues, adapter->num_rx_queues) - 1;
101403f69bbSGerhard Engleder 	len += TSNEP_QUEUE_SIZE * num_additional_queues;
102403f69bbSGerhard Engleder 
103403f69bbSGerhard Engleder 	return len;
104403f69bbSGerhard Engleder }
105403f69bbSGerhard Engleder 
tsnep_ethtool_get_regs(struct net_device * netdev,struct ethtool_regs * regs,void * p)106403f69bbSGerhard Engleder static void tsnep_ethtool_get_regs(struct net_device *netdev,
107403f69bbSGerhard Engleder 				   struct ethtool_regs *regs,
108403f69bbSGerhard Engleder 				   void *p)
109403f69bbSGerhard Engleder {
110403f69bbSGerhard Engleder 	struct tsnep_adapter *adapter = netdev_priv(netdev);
111403f69bbSGerhard Engleder 
112403f69bbSGerhard Engleder 	regs->version = 1;
113403f69bbSGerhard Engleder 
114403f69bbSGerhard Engleder 	memcpy_fromio(p, adapter->addr, regs->len);
115403f69bbSGerhard Engleder }
116403f69bbSGerhard Engleder 
tsnep_ethtool_get_msglevel(struct net_device * netdev)117403f69bbSGerhard Engleder static u32 tsnep_ethtool_get_msglevel(struct net_device *netdev)
118403f69bbSGerhard Engleder {
119403f69bbSGerhard Engleder 	struct tsnep_adapter *adapter = netdev_priv(netdev);
120403f69bbSGerhard Engleder 
121403f69bbSGerhard Engleder 	return adapter->msg_enable;
122403f69bbSGerhard Engleder }
123403f69bbSGerhard Engleder 
tsnep_ethtool_set_msglevel(struct net_device * netdev,u32 data)124403f69bbSGerhard Engleder static void tsnep_ethtool_set_msglevel(struct net_device *netdev, u32 data)
125403f69bbSGerhard Engleder {
126403f69bbSGerhard Engleder 	struct tsnep_adapter *adapter = netdev_priv(netdev);
127403f69bbSGerhard Engleder 
128403f69bbSGerhard Engleder 	adapter->msg_enable = data;
129403f69bbSGerhard Engleder }
130403f69bbSGerhard Engleder 
tsnep_ethtool_get_strings(struct net_device * netdev,u32 stringset,u8 * data)131403f69bbSGerhard Engleder static void tsnep_ethtool_get_strings(struct net_device *netdev, u32 stringset,
132403f69bbSGerhard Engleder 				      u8 *data)
133403f69bbSGerhard Engleder {
134403f69bbSGerhard Engleder 	struct tsnep_adapter *adapter = netdev_priv(netdev);
135403f69bbSGerhard Engleder 	int rx_count = adapter->num_rx_queues;
136403f69bbSGerhard Engleder 	int tx_count = adapter->num_tx_queues;
137403f69bbSGerhard Engleder 	int i, j;
138403f69bbSGerhard Engleder 
139403f69bbSGerhard Engleder 	switch (stringset) {
140403f69bbSGerhard Engleder 	case ETH_SS_STATS:
141403f69bbSGerhard Engleder 		memcpy(data, tsnep_stats_strings, sizeof(tsnep_stats_strings));
142403f69bbSGerhard Engleder 		data += sizeof(tsnep_stats_strings);
143403f69bbSGerhard Engleder 
144403f69bbSGerhard Engleder 		for (i = 0; i < rx_count; i++) {
145403f69bbSGerhard Engleder 			for (j = 0; j < TSNEP_RX_QUEUE_STATS_COUNT; j++) {
146403f69bbSGerhard Engleder 				snprintf(data, ETH_GSTRING_LEN,
147403f69bbSGerhard Engleder 					 tsnep_rx_queue_stats_strings[j], i);
148403f69bbSGerhard Engleder 				data += ETH_GSTRING_LEN;
149403f69bbSGerhard Engleder 			}
150403f69bbSGerhard Engleder 		}
151403f69bbSGerhard Engleder 
152403f69bbSGerhard Engleder 		for (i = 0; i < tx_count; i++) {
153403f69bbSGerhard Engleder 			for (j = 0; j < TSNEP_TX_QUEUE_STATS_COUNT; j++) {
154403f69bbSGerhard Engleder 				snprintf(data, ETH_GSTRING_LEN,
155403f69bbSGerhard Engleder 					 tsnep_tx_queue_stats_strings[j], i);
156403f69bbSGerhard Engleder 				data += ETH_GSTRING_LEN;
157403f69bbSGerhard Engleder 			}
158403f69bbSGerhard Engleder 		}
159403f69bbSGerhard Engleder 		break;
160403f69bbSGerhard Engleder 	case ETH_SS_TEST:
161403f69bbSGerhard Engleder 		tsnep_ethtool_get_test_strings(data);
162403f69bbSGerhard Engleder 		break;
163403f69bbSGerhard Engleder 	}
164403f69bbSGerhard Engleder }
165403f69bbSGerhard Engleder 
tsnep_ethtool_get_ethtool_stats(struct net_device * netdev,struct ethtool_stats * stats,u64 * data)166403f69bbSGerhard Engleder static void tsnep_ethtool_get_ethtool_stats(struct net_device *netdev,
167403f69bbSGerhard Engleder 					    struct ethtool_stats *stats,
168403f69bbSGerhard Engleder 					    u64 *data)
169403f69bbSGerhard Engleder {
170403f69bbSGerhard Engleder 	struct tsnep_adapter *adapter = netdev_priv(netdev);
171403f69bbSGerhard Engleder 	int rx_count = adapter->num_rx_queues;
172403f69bbSGerhard Engleder 	int tx_count = adapter->num_tx_queues;
173403f69bbSGerhard Engleder 	struct tsnep_stats tsnep_stats;
174403f69bbSGerhard Engleder 	struct tsnep_rx_queue_stats tsnep_rx_queue_stats;
175403f69bbSGerhard Engleder 	struct tsnep_tx_queue_stats tsnep_tx_queue_stats;
176403f69bbSGerhard Engleder 	u32 reg;
177403f69bbSGerhard Engleder 	int i;
178403f69bbSGerhard Engleder 
179403f69bbSGerhard Engleder 	memset(&tsnep_stats, 0, sizeof(tsnep_stats));
180403f69bbSGerhard Engleder 	for (i = 0; i < adapter->num_rx_queues; i++) {
181403f69bbSGerhard Engleder 		tsnep_stats.rx_packets += adapter->rx[i].packets;
182403f69bbSGerhard Engleder 		tsnep_stats.rx_bytes += adapter->rx[i].bytes;
183403f69bbSGerhard Engleder 		tsnep_stats.rx_dropped += adapter->rx[i].dropped;
184403f69bbSGerhard Engleder 		tsnep_stats.rx_multicast += adapter->rx[i].multicast;
185dbadae92SGerhard Engleder 		tsnep_stats.rx_alloc_failed += adapter->rx[i].alloc_failed;
186403f69bbSGerhard Engleder 	}
187403f69bbSGerhard Engleder 	reg = ioread32(adapter->addr + ECM_STAT);
188403f69bbSGerhard Engleder 	tsnep_stats.rx_phy_errors =
189403f69bbSGerhard Engleder 		(reg & ECM_STAT_RX_ERR_MASK) >> ECM_STAT_RX_ERR_SHIFT;
190403f69bbSGerhard Engleder 	tsnep_stats.rx_forwarded_phy_errors =
191403f69bbSGerhard Engleder 		(reg & ECM_STAT_FWD_RX_ERR_MASK) >> ECM_STAT_FWD_RX_ERR_SHIFT;
192403f69bbSGerhard Engleder 	tsnep_stats.rx_invalid_frame_errors =
193403f69bbSGerhard Engleder 		(reg & ECM_STAT_INV_FRM_MASK) >> ECM_STAT_INV_FRM_SHIFT;
194403f69bbSGerhard Engleder 	for (i = 0; i < adapter->num_tx_queues; i++) {
195403f69bbSGerhard Engleder 		tsnep_stats.tx_packets += adapter->tx[i].packets;
196403f69bbSGerhard Engleder 		tsnep_stats.tx_bytes += adapter->tx[i].bytes;
197403f69bbSGerhard Engleder 		tsnep_stats.tx_dropped += adapter->tx[i].dropped;
198403f69bbSGerhard Engleder 	}
199403f69bbSGerhard Engleder 	memcpy(data, &tsnep_stats, sizeof(tsnep_stats));
200403f69bbSGerhard Engleder 	data += TSNEP_STATS_COUNT;
201403f69bbSGerhard Engleder 
202403f69bbSGerhard Engleder 	for (i = 0; i < rx_count; i++) {
203403f69bbSGerhard Engleder 		memset(&tsnep_rx_queue_stats, 0, sizeof(tsnep_rx_queue_stats));
204403f69bbSGerhard Engleder 		tsnep_rx_queue_stats.rx_packets = adapter->rx[i].packets;
205403f69bbSGerhard Engleder 		tsnep_rx_queue_stats.rx_bytes = adapter->rx[i].bytes;
206403f69bbSGerhard Engleder 		tsnep_rx_queue_stats.rx_dropped = adapter->rx[i].dropped;
207403f69bbSGerhard Engleder 		tsnep_rx_queue_stats.rx_multicast = adapter->rx[i].multicast;
208dbadae92SGerhard Engleder 		tsnep_rx_queue_stats.rx_alloc_failed =
209dbadae92SGerhard Engleder 			adapter->rx[i].alloc_failed;
210403f69bbSGerhard Engleder 		reg = ioread32(adapter->addr + TSNEP_QUEUE(i) +
211403f69bbSGerhard Engleder 			       TSNEP_RX_STATISTIC);
212403f69bbSGerhard Engleder 		tsnep_rx_queue_stats.rx_no_descriptor_errors =
213403f69bbSGerhard Engleder 			(reg & TSNEP_RX_STATISTIC_NO_DESC_MASK) >>
214403f69bbSGerhard Engleder 			TSNEP_RX_STATISTIC_NO_DESC_SHIFT;
215403f69bbSGerhard Engleder 		tsnep_rx_queue_stats.rx_buffer_too_small_errors =
216403f69bbSGerhard Engleder 			(reg & TSNEP_RX_STATISTIC_BUFFER_TOO_SMALL_MASK) >>
217403f69bbSGerhard Engleder 			TSNEP_RX_STATISTIC_BUFFER_TOO_SMALL_SHIFT;
218403f69bbSGerhard Engleder 		tsnep_rx_queue_stats.rx_fifo_overflow_errors =
219403f69bbSGerhard Engleder 			(reg & TSNEP_RX_STATISTIC_FIFO_OVERFLOW_MASK) >>
220403f69bbSGerhard Engleder 			TSNEP_RX_STATISTIC_FIFO_OVERFLOW_SHIFT;
221403f69bbSGerhard Engleder 		tsnep_rx_queue_stats.rx_invalid_frame_errors =
222403f69bbSGerhard Engleder 			(reg & TSNEP_RX_STATISTIC_INVALID_FRAME_MASK) >>
223403f69bbSGerhard Engleder 			TSNEP_RX_STATISTIC_INVALID_FRAME_SHIFT;
224403f69bbSGerhard Engleder 		memcpy(data, &tsnep_rx_queue_stats,
225403f69bbSGerhard Engleder 		       sizeof(tsnep_rx_queue_stats));
226403f69bbSGerhard Engleder 		data += TSNEP_RX_QUEUE_STATS_COUNT;
227403f69bbSGerhard Engleder 	}
228403f69bbSGerhard Engleder 
229403f69bbSGerhard Engleder 	for (i = 0; i < tx_count; i++) {
230403f69bbSGerhard Engleder 		memset(&tsnep_tx_queue_stats, 0, sizeof(tsnep_tx_queue_stats));
231403f69bbSGerhard Engleder 		tsnep_tx_queue_stats.tx_packets += adapter->tx[i].packets;
232403f69bbSGerhard Engleder 		tsnep_tx_queue_stats.tx_bytes += adapter->tx[i].bytes;
233403f69bbSGerhard Engleder 		tsnep_tx_queue_stats.tx_dropped += adapter->tx[i].dropped;
234403f69bbSGerhard Engleder 		memcpy(data, &tsnep_tx_queue_stats,
235403f69bbSGerhard Engleder 		       sizeof(tsnep_tx_queue_stats));
236403f69bbSGerhard Engleder 		data += TSNEP_TX_QUEUE_STATS_COUNT;
237403f69bbSGerhard Engleder 	}
238403f69bbSGerhard Engleder }
239403f69bbSGerhard Engleder 
tsnep_ethtool_get_sset_count(struct net_device * netdev,int sset)240403f69bbSGerhard Engleder static int tsnep_ethtool_get_sset_count(struct net_device *netdev, int sset)
241403f69bbSGerhard Engleder {
242403f69bbSGerhard Engleder 	struct tsnep_adapter *adapter = netdev_priv(netdev);
243403f69bbSGerhard Engleder 	int rx_count;
244403f69bbSGerhard Engleder 	int tx_count;
245403f69bbSGerhard Engleder 
246403f69bbSGerhard Engleder 	switch (sset) {
247403f69bbSGerhard Engleder 	case ETH_SS_STATS:
248403f69bbSGerhard Engleder 		rx_count = adapter->num_rx_queues;
249403f69bbSGerhard Engleder 		tx_count = adapter->num_tx_queues;
250403f69bbSGerhard Engleder 		return TSNEP_STATS_COUNT +
251403f69bbSGerhard Engleder 		       TSNEP_RX_QUEUE_STATS_COUNT * rx_count +
252403f69bbSGerhard Engleder 		       TSNEP_TX_QUEUE_STATS_COUNT * tx_count;
253403f69bbSGerhard Engleder 	case ETH_SS_TEST:
254403f69bbSGerhard Engleder 		return tsnep_ethtool_get_test_count();
255403f69bbSGerhard Engleder 	default:
256403f69bbSGerhard Engleder 		return -EOPNOTSUPP;
257403f69bbSGerhard Engleder 	}
258403f69bbSGerhard Engleder }
259403f69bbSGerhard Engleder 
tsnep_ethtool_get_rxnfc(struct net_device * netdev,struct ethtool_rxnfc * cmd,u32 * rule_locs)26091644df1SGerhard Engleder static int tsnep_ethtool_get_rxnfc(struct net_device *netdev,
261308ce142SGerhard Engleder 				   struct ethtool_rxnfc *cmd, u32 *rule_locs)
262308ce142SGerhard Engleder {
26391644df1SGerhard Engleder 	struct tsnep_adapter *adapter = netdev_priv(netdev);
264308ce142SGerhard Engleder 
265308ce142SGerhard Engleder 	switch (cmd->cmd) {
266308ce142SGerhard Engleder 	case ETHTOOL_GRXRINGS:
267308ce142SGerhard Engleder 		cmd->data = adapter->num_rx_queues;
268308ce142SGerhard Engleder 		return 0;
269308ce142SGerhard Engleder 	case ETHTOOL_GRXCLSRLCNT:
270308ce142SGerhard Engleder 		cmd->rule_cnt = adapter->rxnfc_count;
271308ce142SGerhard Engleder 		cmd->data = adapter->rxnfc_max;
272308ce142SGerhard Engleder 		cmd->data |= RX_CLS_LOC_SPECIAL;
273308ce142SGerhard Engleder 		return 0;
274308ce142SGerhard Engleder 	case ETHTOOL_GRXCLSRULE:
275308ce142SGerhard Engleder 		return tsnep_rxnfc_get_rule(adapter, cmd);
276308ce142SGerhard Engleder 	case ETHTOOL_GRXCLSRLALL:
277308ce142SGerhard Engleder 		return tsnep_rxnfc_get_all(adapter, cmd, rule_locs);
278308ce142SGerhard Engleder 	default:
279308ce142SGerhard Engleder 		return -EOPNOTSUPP;
280308ce142SGerhard Engleder 	}
281308ce142SGerhard Engleder }
282308ce142SGerhard Engleder 
tsnep_ethtool_set_rxnfc(struct net_device * netdev,struct ethtool_rxnfc * cmd)28391644df1SGerhard Engleder static int tsnep_ethtool_set_rxnfc(struct net_device *netdev,
284308ce142SGerhard Engleder 				   struct ethtool_rxnfc *cmd)
285308ce142SGerhard Engleder {
28691644df1SGerhard Engleder 	struct tsnep_adapter *adapter = netdev_priv(netdev);
287308ce142SGerhard Engleder 
288308ce142SGerhard Engleder 	switch (cmd->cmd) {
289308ce142SGerhard Engleder 	case ETHTOOL_SRXCLSRLINS:
290308ce142SGerhard Engleder 		return tsnep_rxnfc_add_rule(adapter, cmd);
291308ce142SGerhard Engleder 	case ETHTOOL_SRXCLSRLDEL:
292308ce142SGerhard Engleder 		return tsnep_rxnfc_del_rule(adapter, cmd);
293308ce142SGerhard Engleder 	default:
294308ce142SGerhard Engleder 		return -EOPNOTSUPP;
295308ce142SGerhard Engleder 	}
296308ce142SGerhard Engleder }
297308ce142SGerhard Engleder 
tsnep_ethtool_get_channels(struct net_device * netdev,struct ethtool_channels * ch)2984f661ccfSGerhard Engleder static void tsnep_ethtool_get_channels(struct net_device *netdev,
2994f661ccfSGerhard Engleder 				       struct ethtool_channels *ch)
3004f661ccfSGerhard Engleder {
3014f661ccfSGerhard Engleder 	struct tsnep_adapter *adapter = netdev_priv(netdev);
3024f661ccfSGerhard Engleder 
303*a7f99195SGerhard Engleder 	ch->max_combined = adapter->num_queues;
304*a7f99195SGerhard Engleder 	ch->combined_count = adapter->num_queues;
3054f661ccfSGerhard Engleder }
3064f661ccfSGerhard Engleder 
tsnep_ethtool_get_ts_info(struct net_device * netdev,struct ethtool_ts_info * info)30791644df1SGerhard Engleder static int tsnep_ethtool_get_ts_info(struct net_device *netdev,
308403f69bbSGerhard Engleder 				     struct ethtool_ts_info *info)
309403f69bbSGerhard Engleder {
31091644df1SGerhard Engleder 	struct tsnep_adapter *adapter = netdev_priv(netdev);
311403f69bbSGerhard Engleder 
312403f69bbSGerhard Engleder 	info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
313403f69bbSGerhard Engleder 				SOF_TIMESTAMPING_RX_SOFTWARE |
314403f69bbSGerhard Engleder 				SOF_TIMESTAMPING_SOFTWARE |
315403f69bbSGerhard Engleder 				SOF_TIMESTAMPING_TX_HARDWARE |
316403f69bbSGerhard Engleder 				SOF_TIMESTAMPING_RX_HARDWARE |
317403f69bbSGerhard Engleder 				SOF_TIMESTAMPING_RAW_HARDWARE;
318403f69bbSGerhard Engleder 
319403f69bbSGerhard Engleder 	if (adapter->ptp_clock)
320403f69bbSGerhard Engleder 		info->phc_index = ptp_clock_index(adapter->ptp_clock);
321403f69bbSGerhard Engleder 	else
322403f69bbSGerhard Engleder 		info->phc_index = -1;
323403f69bbSGerhard Engleder 
324403f69bbSGerhard Engleder 	info->tx_types = BIT(HWTSTAMP_TX_OFF) |
325403f69bbSGerhard Engleder 			 BIT(HWTSTAMP_TX_ON);
326403f69bbSGerhard Engleder 	info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
327403f69bbSGerhard Engleder 			   BIT(HWTSTAMP_FILTER_ALL);
328403f69bbSGerhard Engleder 
329403f69bbSGerhard Engleder 	return 0;
330403f69bbSGerhard Engleder }
331403f69bbSGerhard Engleder 
tsnep_get_queue_with_tx(struct tsnep_adapter * adapter,int index)332d3dfe8d6SGerhard Engleder static struct tsnep_queue *tsnep_get_queue_with_tx(struct tsnep_adapter *adapter,
333d3dfe8d6SGerhard Engleder 						   int index)
334d3dfe8d6SGerhard Engleder {
335d3dfe8d6SGerhard Engleder 	int i;
336d3dfe8d6SGerhard Engleder 
337d3dfe8d6SGerhard Engleder 	for (i = 0; i < adapter->num_queues; i++) {
338d3dfe8d6SGerhard Engleder 		if (adapter->queue[i].tx) {
339d3dfe8d6SGerhard Engleder 			if (index == 0)
340d3dfe8d6SGerhard Engleder 				return &adapter->queue[i];
341d3dfe8d6SGerhard Engleder 
342d3dfe8d6SGerhard Engleder 			index--;
343d3dfe8d6SGerhard Engleder 		}
344d3dfe8d6SGerhard Engleder 	}
345d3dfe8d6SGerhard Engleder 
346d3dfe8d6SGerhard Engleder 	return NULL;
347d3dfe8d6SGerhard Engleder }
348d3dfe8d6SGerhard Engleder 
tsnep_get_queue_with_rx(struct tsnep_adapter * adapter,int index)349d3dfe8d6SGerhard Engleder static struct tsnep_queue *tsnep_get_queue_with_rx(struct tsnep_adapter *adapter,
350d3dfe8d6SGerhard Engleder 						   int index)
351d3dfe8d6SGerhard Engleder {
352d3dfe8d6SGerhard Engleder 	int i;
353d3dfe8d6SGerhard Engleder 
354d3dfe8d6SGerhard Engleder 	for (i = 0; i < adapter->num_queues; i++) {
355d3dfe8d6SGerhard Engleder 		if (adapter->queue[i].rx) {
356d3dfe8d6SGerhard Engleder 			if (index == 0)
357d3dfe8d6SGerhard Engleder 				return &adapter->queue[i];
358d3dfe8d6SGerhard Engleder 
359d3dfe8d6SGerhard Engleder 			index--;
360d3dfe8d6SGerhard Engleder 		}
361d3dfe8d6SGerhard Engleder 	}
362d3dfe8d6SGerhard Engleder 
363d3dfe8d6SGerhard Engleder 	return NULL;
364d3dfe8d6SGerhard Engleder }
365d3dfe8d6SGerhard Engleder 
tsnep_ethtool_get_coalesce(struct net_device * netdev,struct ethtool_coalesce * ec,struct kernel_ethtool_coalesce * kernel_coal,struct netlink_ext_ack * extack)366d3dfe8d6SGerhard Engleder static int tsnep_ethtool_get_coalesce(struct net_device *netdev,
367d3dfe8d6SGerhard Engleder 				      struct ethtool_coalesce *ec,
368d3dfe8d6SGerhard Engleder 				      struct kernel_ethtool_coalesce *kernel_coal,
369d3dfe8d6SGerhard Engleder 				      struct netlink_ext_ack *extack)
370d3dfe8d6SGerhard Engleder {
371d3dfe8d6SGerhard Engleder 	struct tsnep_adapter *adapter = netdev_priv(netdev);
372d3dfe8d6SGerhard Engleder 	struct tsnep_queue *queue;
373d3dfe8d6SGerhard Engleder 
374d3dfe8d6SGerhard Engleder 	queue = tsnep_get_queue_with_rx(adapter, 0);
375d3dfe8d6SGerhard Engleder 	if (queue)
376d3dfe8d6SGerhard Engleder 		ec->rx_coalesce_usecs = tsnep_get_irq_coalesce(queue);
377d3dfe8d6SGerhard Engleder 
378d3dfe8d6SGerhard Engleder 	queue = tsnep_get_queue_with_tx(adapter, 0);
379d3dfe8d6SGerhard Engleder 	if (queue)
380d3dfe8d6SGerhard Engleder 		ec->tx_coalesce_usecs = tsnep_get_irq_coalesce(queue);
381d3dfe8d6SGerhard Engleder 
382d3dfe8d6SGerhard Engleder 	return 0;
383d3dfe8d6SGerhard Engleder }
384d3dfe8d6SGerhard Engleder 
tsnep_ethtool_set_coalesce(struct net_device * netdev,struct ethtool_coalesce * ec,struct kernel_ethtool_coalesce * kernel_coal,struct netlink_ext_ack * extack)385d3dfe8d6SGerhard Engleder static int tsnep_ethtool_set_coalesce(struct net_device *netdev,
386d3dfe8d6SGerhard Engleder 				      struct ethtool_coalesce *ec,
387d3dfe8d6SGerhard Engleder 				      struct kernel_ethtool_coalesce *kernel_coal,
388d3dfe8d6SGerhard Engleder 				      struct netlink_ext_ack *extack)
389d3dfe8d6SGerhard Engleder {
390d3dfe8d6SGerhard Engleder 	struct tsnep_adapter *adapter = netdev_priv(netdev);
391d3dfe8d6SGerhard Engleder 	int i;
392d3dfe8d6SGerhard Engleder 	int retval;
393d3dfe8d6SGerhard Engleder 
394d3dfe8d6SGerhard Engleder 	for (i = 0; i < adapter->num_queues; i++) {
395d3dfe8d6SGerhard Engleder 		/* RX coalesce has priority for queues with TX and RX */
396d3dfe8d6SGerhard Engleder 		if (adapter->queue[i].rx)
397d3dfe8d6SGerhard Engleder 			retval = tsnep_set_irq_coalesce(&adapter->queue[i],
398d3dfe8d6SGerhard Engleder 							ec->rx_coalesce_usecs);
399d3dfe8d6SGerhard Engleder 		else
400d3dfe8d6SGerhard Engleder 			retval = tsnep_set_irq_coalesce(&adapter->queue[i],
401d3dfe8d6SGerhard Engleder 							ec->tx_coalesce_usecs);
402d3dfe8d6SGerhard Engleder 		if (retval != 0)
403d3dfe8d6SGerhard Engleder 			return retval;
404d3dfe8d6SGerhard Engleder 	}
405d3dfe8d6SGerhard Engleder 
406d3dfe8d6SGerhard Engleder 	return 0;
407d3dfe8d6SGerhard Engleder }
408d3dfe8d6SGerhard Engleder 
tsnep_ethtool_get_per_queue_coalesce(struct net_device * netdev,u32 queue,struct ethtool_coalesce * ec)409d3dfe8d6SGerhard Engleder static int tsnep_ethtool_get_per_queue_coalesce(struct net_device *netdev,
410d3dfe8d6SGerhard Engleder 						u32 queue,
411d3dfe8d6SGerhard Engleder 						struct ethtool_coalesce *ec)
412d3dfe8d6SGerhard Engleder {
413d3dfe8d6SGerhard Engleder 	struct tsnep_adapter *adapter = netdev_priv(netdev);
414d3dfe8d6SGerhard Engleder 	struct tsnep_queue *queue_with_rx;
415d3dfe8d6SGerhard Engleder 	struct tsnep_queue *queue_with_tx;
416d3dfe8d6SGerhard Engleder 
417d3dfe8d6SGerhard Engleder 	if (queue >= max(adapter->num_tx_queues, adapter->num_rx_queues))
418d3dfe8d6SGerhard Engleder 		return -EINVAL;
419d3dfe8d6SGerhard Engleder 
420d3dfe8d6SGerhard Engleder 	queue_with_rx = tsnep_get_queue_with_rx(adapter, queue);
421d3dfe8d6SGerhard Engleder 	if (queue_with_rx)
422d3dfe8d6SGerhard Engleder 		ec->rx_coalesce_usecs = tsnep_get_irq_coalesce(queue_with_rx);
423d3dfe8d6SGerhard Engleder 
424d3dfe8d6SGerhard Engleder 	queue_with_tx = tsnep_get_queue_with_tx(adapter, queue);
425d3dfe8d6SGerhard Engleder 	if (queue_with_tx)
426d3dfe8d6SGerhard Engleder 		ec->tx_coalesce_usecs = tsnep_get_irq_coalesce(queue_with_tx);
427d3dfe8d6SGerhard Engleder 
428d3dfe8d6SGerhard Engleder 	return 0;
429d3dfe8d6SGerhard Engleder }
430d3dfe8d6SGerhard Engleder 
tsnep_ethtool_set_per_queue_coalesce(struct net_device * netdev,u32 queue,struct ethtool_coalesce * ec)431d3dfe8d6SGerhard Engleder static int tsnep_ethtool_set_per_queue_coalesce(struct net_device *netdev,
432d3dfe8d6SGerhard Engleder 						u32 queue,
433d3dfe8d6SGerhard Engleder 						struct ethtool_coalesce *ec)
434d3dfe8d6SGerhard Engleder {
435d3dfe8d6SGerhard Engleder 	struct tsnep_adapter *adapter = netdev_priv(netdev);
436d3dfe8d6SGerhard Engleder 	struct tsnep_queue *queue_with_rx;
437d3dfe8d6SGerhard Engleder 	struct tsnep_queue *queue_with_tx;
438d3dfe8d6SGerhard Engleder 	int retval;
439d3dfe8d6SGerhard Engleder 
440d3dfe8d6SGerhard Engleder 	if (queue >= max(adapter->num_tx_queues, adapter->num_rx_queues))
441d3dfe8d6SGerhard Engleder 		return -EINVAL;
442d3dfe8d6SGerhard Engleder 
443d3dfe8d6SGerhard Engleder 	queue_with_rx = tsnep_get_queue_with_rx(adapter, queue);
444d3dfe8d6SGerhard Engleder 	if (queue_with_rx) {
445d3dfe8d6SGerhard Engleder 		retval = tsnep_set_irq_coalesce(queue_with_rx, ec->rx_coalesce_usecs);
446d3dfe8d6SGerhard Engleder 		if (retval != 0)
447d3dfe8d6SGerhard Engleder 			return retval;
448d3dfe8d6SGerhard Engleder 	}
449d3dfe8d6SGerhard Engleder 
450d3dfe8d6SGerhard Engleder 	/* RX coalesce has priority for queues with TX and RX */
451d3dfe8d6SGerhard Engleder 	queue_with_tx = tsnep_get_queue_with_tx(adapter, queue);
452d3dfe8d6SGerhard Engleder 	if (queue_with_tx && !queue_with_tx->rx) {
453d3dfe8d6SGerhard Engleder 		retval = tsnep_set_irq_coalesce(queue_with_tx, ec->tx_coalesce_usecs);
454d3dfe8d6SGerhard Engleder 		if (retval != 0)
455d3dfe8d6SGerhard Engleder 			return retval;
456d3dfe8d6SGerhard Engleder 	}
457d3dfe8d6SGerhard Engleder 
458d3dfe8d6SGerhard Engleder 	return 0;
459d3dfe8d6SGerhard Engleder }
460d3dfe8d6SGerhard Engleder 
461403f69bbSGerhard Engleder const struct ethtool_ops tsnep_ethtool_ops = {
462d3dfe8d6SGerhard Engleder 	.supported_coalesce_params = ETHTOOL_COALESCE_USECS,
463403f69bbSGerhard Engleder 	.get_drvinfo = tsnep_ethtool_get_drvinfo,
464403f69bbSGerhard Engleder 	.get_regs_len = tsnep_ethtool_get_regs_len,
465403f69bbSGerhard Engleder 	.get_regs = tsnep_ethtool_get_regs,
466403f69bbSGerhard Engleder 	.get_msglevel = tsnep_ethtool_get_msglevel,
467403f69bbSGerhard Engleder 	.set_msglevel = tsnep_ethtool_set_msglevel,
468403f69bbSGerhard Engleder 	.nway_reset = phy_ethtool_nway_reset,
469403f69bbSGerhard Engleder 	.get_link = ethtool_op_get_link,
470403f69bbSGerhard Engleder 	.self_test = tsnep_ethtool_self_test,
471403f69bbSGerhard Engleder 	.get_strings = tsnep_ethtool_get_strings,
472403f69bbSGerhard Engleder 	.get_ethtool_stats = tsnep_ethtool_get_ethtool_stats,
473403f69bbSGerhard Engleder 	.get_sset_count = tsnep_ethtool_get_sset_count,
474308ce142SGerhard Engleder 	.get_rxnfc = tsnep_ethtool_get_rxnfc,
475308ce142SGerhard Engleder 	.set_rxnfc = tsnep_ethtool_set_rxnfc,
4764f661ccfSGerhard Engleder 	.get_channels = tsnep_ethtool_get_channels,
477403f69bbSGerhard Engleder 	.get_ts_info = tsnep_ethtool_get_ts_info,
478d3dfe8d6SGerhard Engleder 	.get_coalesce = tsnep_ethtool_get_coalesce,
479d3dfe8d6SGerhard Engleder 	.set_coalesce = tsnep_ethtool_set_coalesce,
480d3dfe8d6SGerhard Engleder 	.get_per_queue_coalesce = tsnep_ethtool_get_per_queue_coalesce,
481d3dfe8d6SGerhard Engleder 	.set_per_queue_coalesce = tsnep_ethtool_set_per_queue_coalesce,
482403f69bbSGerhard Engleder 	.get_link_ksettings = phy_ethtool_get_link_ksettings,
483403f69bbSGerhard Engleder 	.set_link_ksettings = phy_ethtool_set_link_ksettings,
484403f69bbSGerhard Engleder };
485