162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright (C) 2021 Gerhard Engleder <gerhard@engleder-embedded.com> */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include "tsnep.h"
562306a36Sopenharmony_ci
662306a36Sopenharmony_cistatic const char tsnep_stats_strings[][ETH_GSTRING_LEN] = {
762306a36Sopenharmony_ci	"rx_packets",
862306a36Sopenharmony_ci	"rx_bytes",
962306a36Sopenharmony_ci	"rx_dropped",
1062306a36Sopenharmony_ci	"rx_multicast",
1162306a36Sopenharmony_ci	"rx_alloc_failed",
1262306a36Sopenharmony_ci	"rx_phy_errors",
1362306a36Sopenharmony_ci	"rx_forwarded_phy_errors",
1462306a36Sopenharmony_ci	"rx_invalid_frame_errors",
1562306a36Sopenharmony_ci	"tx_packets",
1662306a36Sopenharmony_ci	"tx_bytes",
1762306a36Sopenharmony_ci	"tx_dropped",
1862306a36Sopenharmony_ci};
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistruct tsnep_stats {
2162306a36Sopenharmony_ci	u64 rx_packets;
2262306a36Sopenharmony_ci	u64 rx_bytes;
2362306a36Sopenharmony_ci	u64 rx_dropped;
2462306a36Sopenharmony_ci	u64 rx_multicast;
2562306a36Sopenharmony_ci	u64 rx_alloc_failed;
2662306a36Sopenharmony_ci	u64 rx_phy_errors;
2762306a36Sopenharmony_ci	u64 rx_forwarded_phy_errors;
2862306a36Sopenharmony_ci	u64 rx_invalid_frame_errors;
2962306a36Sopenharmony_ci	u64 tx_packets;
3062306a36Sopenharmony_ci	u64 tx_bytes;
3162306a36Sopenharmony_ci	u64 tx_dropped;
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define TSNEP_STATS_COUNT (sizeof(struct tsnep_stats) / sizeof(u64))
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic const char tsnep_rx_queue_stats_strings[][ETH_GSTRING_LEN] = {
3762306a36Sopenharmony_ci	"rx_%d_packets",
3862306a36Sopenharmony_ci	"rx_%d_bytes",
3962306a36Sopenharmony_ci	"rx_%d_dropped",
4062306a36Sopenharmony_ci	"rx_%d_multicast",
4162306a36Sopenharmony_ci	"rx_%d_alloc_failed",
4262306a36Sopenharmony_ci	"rx_%d_no_descriptor_errors",
4362306a36Sopenharmony_ci	"rx_%d_buffer_too_small_errors",
4462306a36Sopenharmony_ci	"rx_%d_fifo_overflow_errors",
4562306a36Sopenharmony_ci	"rx_%d_invalid_frame_errors",
4662306a36Sopenharmony_ci};
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistruct tsnep_rx_queue_stats {
4962306a36Sopenharmony_ci	u64 rx_packets;
5062306a36Sopenharmony_ci	u64 rx_bytes;
5162306a36Sopenharmony_ci	u64 rx_dropped;
5262306a36Sopenharmony_ci	u64 rx_multicast;
5362306a36Sopenharmony_ci	u64 rx_alloc_failed;
5462306a36Sopenharmony_ci	u64 rx_no_descriptor_errors;
5562306a36Sopenharmony_ci	u64 rx_buffer_too_small_errors;
5662306a36Sopenharmony_ci	u64 rx_fifo_overflow_errors;
5762306a36Sopenharmony_ci	u64 rx_invalid_frame_errors;
5862306a36Sopenharmony_ci};
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci#define TSNEP_RX_QUEUE_STATS_COUNT (sizeof(struct tsnep_rx_queue_stats) / \
6162306a36Sopenharmony_ci				    sizeof(u64))
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic const char tsnep_tx_queue_stats_strings[][ETH_GSTRING_LEN] = {
6462306a36Sopenharmony_ci	"tx_%d_packets",
6562306a36Sopenharmony_ci	"tx_%d_bytes",
6662306a36Sopenharmony_ci	"tx_%d_dropped",
6762306a36Sopenharmony_ci};
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistruct tsnep_tx_queue_stats {
7062306a36Sopenharmony_ci	u64 tx_packets;
7162306a36Sopenharmony_ci	u64 tx_bytes;
7262306a36Sopenharmony_ci	u64 tx_dropped;
7362306a36Sopenharmony_ci};
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci#define TSNEP_TX_QUEUE_STATS_COUNT (sizeof(struct tsnep_tx_queue_stats) / \
7662306a36Sopenharmony_ci				    sizeof(u64))
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic void tsnep_ethtool_get_drvinfo(struct net_device *netdev,
7962306a36Sopenharmony_ci				      struct ethtool_drvinfo *drvinfo)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	struct tsnep_adapter *adapter = netdev_priv(netdev);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	strscpy(drvinfo->driver, TSNEP, sizeof(drvinfo->driver));
8462306a36Sopenharmony_ci	strscpy(drvinfo->bus_info, dev_name(&adapter->pdev->dev),
8562306a36Sopenharmony_ci		sizeof(drvinfo->bus_info));
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic int tsnep_ethtool_get_regs_len(struct net_device *netdev)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	struct tsnep_adapter *adapter = netdev_priv(netdev);
9162306a36Sopenharmony_ci	int len;
9262306a36Sopenharmony_ci	int num_additional_queues;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	len = TSNEP_MAC_SIZE;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	/* first queue pair is within TSNEP_MAC_SIZE, only queues additional to
9762306a36Sopenharmony_ci	 * the first queue pair extend the register length by TSNEP_QUEUE_SIZE
9862306a36Sopenharmony_ci	 */
9962306a36Sopenharmony_ci	num_additional_queues =
10062306a36Sopenharmony_ci		max(adapter->num_tx_queues, adapter->num_rx_queues) - 1;
10162306a36Sopenharmony_ci	len += TSNEP_QUEUE_SIZE * num_additional_queues;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	return len;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic void tsnep_ethtool_get_regs(struct net_device *netdev,
10762306a36Sopenharmony_ci				   struct ethtool_regs *regs,
10862306a36Sopenharmony_ci				   void *p)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct tsnep_adapter *adapter = netdev_priv(netdev);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	regs->version = 1;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	memcpy_fromio(p, adapter->addr, regs->len);
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic u32 tsnep_ethtool_get_msglevel(struct net_device *netdev)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	struct tsnep_adapter *adapter = netdev_priv(netdev);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	return adapter->msg_enable;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic void tsnep_ethtool_set_msglevel(struct net_device *netdev, u32 data)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	struct tsnep_adapter *adapter = netdev_priv(netdev);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	adapter->msg_enable = data;
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic void tsnep_ethtool_get_strings(struct net_device *netdev, u32 stringset,
13262306a36Sopenharmony_ci				      u8 *data)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct tsnep_adapter *adapter = netdev_priv(netdev);
13562306a36Sopenharmony_ci	int rx_count = adapter->num_rx_queues;
13662306a36Sopenharmony_ci	int tx_count = adapter->num_tx_queues;
13762306a36Sopenharmony_ci	int i, j;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	switch (stringset) {
14062306a36Sopenharmony_ci	case ETH_SS_STATS:
14162306a36Sopenharmony_ci		memcpy(data, tsnep_stats_strings, sizeof(tsnep_stats_strings));
14262306a36Sopenharmony_ci		data += sizeof(tsnep_stats_strings);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci		for (i = 0; i < rx_count; i++) {
14562306a36Sopenharmony_ci			for (j = 0; j < TSNEP_RX_QUEUE_STATS_COUNT; j++) {
14662306a36Sopenharmony_ci				snprintf(data, ETH_GSTRING_LEN,
14762306a36Sopenharmony_ci					 tsnep_rx_queue_stats_strings[j], i);
14862306a36Sopenharmony_ci				data += ETH_GSTRING_LEN;
14962306a36Sopenharmony_ci			}
15062306a36Sopenharmony_ci		}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci		for (i = 0; i < tx_count; i++) {
15362306a36Sopenharmony_ci			for (j = 0; j < TSNEP_TX_QUEUE_STATS_COUNT; j++) {
15462306a36Sopenharmony_ci				snprintf(data, ETH_GSTRING_LEN,
15562306a36Sopenharmony_ci					 tsnep_tx_queue_stats_strings[j], i);
15662306a36Sopenharmony_ci				data += ETH_GSTRING_LEN;
15762306a36Sopenharmony_ci			}
15862306a36Sopenharmony_ci		}
15962306a36Sopenharmony_ci		break;
16062306a36Sopenharmony_ci	case ETH_SS_TEST:
16162306a36Sopenharmony_ci		tsnep_ethtool_get_test_strings(data);
16262306a36Sopenharmony_ci		break;
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic void tsnep_ethtool_get_ethtool_stats(struct net_device *netdev,
16762306a36Sopenharmony_ci					    struct ethtool_stats *stats,
16862306a36Sopenharmony_ci					    u64 *data)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	struct tsnep_adapter *adapter = netdev_priv(netdev);
17162306a36Sopenharmony_ci	int rx_count = adapter->num_rx_queues;
17262306a36Sopenharmony_ci	int tx_count = adapter->num_tx_queues;
17362306a36Sopenharmony_ci	struct tsnep_stats tsnep_stats;
17462306a36Sopenharmony_ci	struct tsnep_rx_queue_stats tsnep_rx_queue_stats;
17562306a36Sopenharmony_ci	struct tsnep_tx_queue_stats tsnep_tx_queue_stats;
17662306a36Sopenharmony_ci	u32 reg;
17762306a36Sopenharmony_ci	int i;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	memset(&tsnep_stats, 0, sizeof(tsnep_stats));
18062306a36Sopenharmony_ci	for (i = 0; i < adapter->num_rx_queues; i++) {
18162306a36Sopenharmony_ci		tsnep_stats.rx_packets += adapter->rx[i].packets;
18262306a36Sopenharmony_ci		tsnep_stats.rx_bytes += adapter->rx[i].bytes;
18362306a36Sopenharmony_ci		tsnep_stats.rx_dropped += adapter->rx[i].dropped;
18462306a36Sopenharmony_ci		tsnep_stats.rx_multicast += adapter->rx[i].multicast;
18562306a36Sopenharmony_ci		tsnep_stats.rx_alloc_failed += adapter->rx[i].alloc_failed;
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci	reg = ioread32(adapter->addr + ECM_STAT);
18862306a36Sopenharmony_ci	tsnep_stats.rx_phy_errors =
18962306a36Sopenharmony_ci		(reg & ECM_STAT_RX_ERR_MASK) >> ECM_STAT_RX_ERR_SHIFT;
19062306a36Sopenharmony_ci	tsnep_stats.rx_forwarded_phy_errors =
19162306a36Sopenharmony_ci		(reg & ECM_STAT_FWD_RX_ERR_MASK) >> ECM_STAT_FWD_RX_ERR_SHIFT;
19262306a36Sopenharmony_ci	tsnep_stats.rx_invalid_frame_errors =
19362306a36Sopenharmony_ci		(reg & ECM_STAT_INV_FRM_MASK) >> ECM_STAT_INV_FRM_SHIFT;
19462306a36Sopenharmony_ci	for (i = 0; i < adapter->num_tx_queues; i++) {
19562306a36Sopenharmony_ci		tsnep_stats.tx_packets += adapter->tx[i].packets;
19662306a36Sopenharmony_ci		tsnep_stats.tx_bytes += adapter->tx[i].bytes;
19762306a36Sopenharmony_ci		tsnep_stats.tx_dropped += adapter->tx[i].dropped;
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci	memcpy(data, &tsnep_stats, sizeof(tsnep_stats));
20062306a36Sopenharmony_ci	data += TSNEP_STATS_COUNT;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	for (i = 0; i < rx_count; i++) {
20362306a36Sopenharmony_ci		memset(&tsnep_rx_queue_stats, 0, sizeof(tsnep_rx_queue_stats));
20462306a36Sopenharmony_ci		tsnep_rx_queue_stats.rx_packets = adapter->rx[i].packets;
20562306a36Sopenharmony_ci		tsnep_rx_queue_stats.rx_bytes = adapter->rx[i].bytes;
20662306a36Sopenharmony_ci		tsnep_rx_queue_stats.rx_dropped = adapter->rx[i].dropped;
20762306a36Sopenharmony_ci		tsnep_rx_queue_stats.rx_multicast = adapter->rx[i].multicast;
20862306a36Sopenharmony_ci		tsnep_rx_queue_stats.rx_alloc_failed =
20962306a36Sopenharmony_ci			adapter->rx[i].alloc_failed;
21062306a36Sopenharmony_ci		reg = ioread32(adapter->addr + TSNEP_QUEUE(i) +
21162306a36Sopenharmony_ci			       TSNEP_RX_STATISTIC);
21262306a36Sopenharmony_ci		tsnep_rx_queue_stats.rx_no_descriptor_errors =
21362306a36Sopenharmony_ci			(reg & TSNEP_RX_STATISTIC_NO_DESC_MASK) >>
21462306a36Sopenharmony_ci			TSNEP_RX_STATISTIC_NO_DESC_SHIFT;
21562306a36Sopenharmony_ci		tsnep_rx_queue_stats.rx_buffer_too_small_errors =
21662306a36Sopenharmony_ci			(reg & TSNEP_RX_STATISTIC_BUFFER_TOO_SMALL_MASK) >>
21762306a36Sopenharmony_ci			TSNEP_RX_STATISTIC_BUFFER_TOO_SMALL_SHIFT;
21862306a36Sopenharmony_ci		tsnep_rx_queue_stats.rx_fifo_overflow_errors =
21962306a36Sopenharmony_ci			(reg & TSNEP_RX_STATISTIC_FIFO_OVERFLOW_MASK) >>
22062306a36Sopenharmony_ci			TSNEP_RX_STATISTIC_FIFO_OVERFLOW_SHIFT;
22162306a36Sopenharmony_ci		tsnep_rx_queue_stats.rx_invalid_frame_errors =
22262306a36Sopenharmony_ci			(reg & TSNEP_RX_STATISTIC_INVALID_FRAME_MASK) >>
22362306a36Sopenharmony_ci			TSNEP_RX_STATISTIC_INVALID_FRAME_SHIFT;
22462306a36Sopenharmony_ci		memcpy(data, &tsnep_rx_queue_stats,
22562306a36Sopenharmony_ci		       sizeof(tsnep_rx_queue_stats));
22662306a36Sopenharmony_ci		data += TSNEP_RX_QUEUE_STATS_COUNT;
22762306a36Sopenharmony_ci	}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	for (i = 0; i < tx_count; i++) {
23062306a36Sopenharmony_ci		memset(&tsnep_tx_queue_stats, 0, sizeof(tsnep_tx_queue_stats));
23162306a36Sopenharmony_ci		tsnep_tx_queue_stats.tx_packets += adapter->tx[i].packets;
23262306a36Sopenharmony_ci		tsnep_tx_queue_stats.tx_bytes += adapter->tx[i].bytes;
23362306a36Sopenharmony_ci		tsnep_tx_queue_stats.tx_dropped += adapter->tx[i].dropped;
23462306a36Sopenharmony_ci		memcpy(data, &tsnep_tx_queue_stats,
23562306a36Sopenharmony_ci		       sizeof(tsnep_tx_queue_stats));
23662306a36Sopenharmony_ci		data += TSNEP_TX_QUEUE_STATS_COUNT;
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic int tsnep_ethtool_get_sset_count(struct net_device *netdev, int sset)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	struct tsnep_adapter *adapter = netdev_priv(netdev);
24362306a36Sopenharmony_ci	int rx_count;
24462306a36Sopenharmony_ci	int tx_count;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	switch (sset) {
24762306a36Sopenharmony_ci	case ETH_SS_STATS:
24862306a36Sopenharmony_ci		rx_count = adapter->num_rx_queues;
24962306a36Sopenharmony_ci		tx_count = adapter->num_tx_queues;
25062306a36Sopenharmony_ci		return TSNEP_STATS_COUNT +
25162306a36Sopenharmony_ci		       TSNEP_RX_QUEUE_STATS_COUNT * rx_count +
25262306a36Sopenharmony_ci		       TSNEP_TX_QUEUE_STATS_COUNT * tx_count;
25362306a36Sopenharmony_ci	case ETH_SS_TEST:
25462306a36Sopenharmony_ci		return tsnep_ethtool_get_test_count();
25562306a36Sopenharmony_ci	default:
25662306a36Sopenharmony_ci		return -EOPNOTSUPP;
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic int tsnep_ethtool_get_rxnfc(struct net_device *netdev,
26162306a36Sopenharmony_ci				   struct ethtool_rxnfc *cmd, u32 *rule_locs)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	struct tsnep_adapter *adapter = netdev_priv(netdev);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	switch (cmd->cmd) {
26662306a36Sopenharmony_ci	case ETHTOOL_GRXRINGS:
26762306a36Sopenharmony_ci		cmd->data = adapter->num_rx_queues;
26862306a36Sopenharmony_ci		return 0;
26962306a36Sopenharmony_ci	case ETHTOOL_GRXCLSRLCNT:
27062306a36Sopenharmony_ci		cmd->rule_cnt = adapter->rxnfc_count;
27162306a36Sopenharmony_ci		cmd->data = adapter->rxnfc_max;
27262306a36Sopenharmony_ci		cmd->data |= RX_CLS_LOC_SPECIAL;
27362306a36Sopenharmony_ci		return 0;
27462306a36Sopenharmony_ci	case ETHTOOL_GRXCLSRULE:
27562306a36Sopenharmony_ci		return tsnep_rxnfc_get_rule(adapter, cmd);
27662306a36Sopenharmony_ci	case ETHTOOL_GRXCLSRLALL:
27762306a36Sopenharmony_ci		return tsnep_rxnfc_get_all(adapter, cmd, rule_locs);
27862306a36Sopenharmony_ci	default:
27962306a36Sopenharmony_ci		return -EOPNOTSUPP;
28062306a36Sopenharmony_ci	}
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic int tsnep_ethtool_set_rxnfc(struct net_device *netdev,
28462306a36Sopenharmony_ci				   struct ethtool_rxnfc *cmd)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	struct tsnep_adapter *adapter = netdev_priv(netdev);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	switch (cmd->cmd) {
28962306a36Sopenharmony_ci	case ETHTOOL_SRXCLSRLINS:
29062306a36Sopenharmony_ci		return tsnep_rxnfc_add_rule(adapter, cmd);
29162306a36Sopenharmony_ci	case ETHTOOL_SRXCLSRLDEL:
29262306a36Sopenharmony_ci		return tsnep_rxnfc_del_rule(adapter, cmd);
29362306a36Sopenharmony_ci	default:
29462306a36Sopenharmony_ci		return -EOPNOTSUPP;
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic void tsnep_ethtool_get_channels(struct net_device *netdev,
29962306a36Sopenharmony_ci				       struct ethtool_channels *ch)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	struct tsnep_adapter *adapter = netdev_priv(netdev);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	ch->max_combined = adapter->num_queues;
30462306a36Sopenharmony_ci	ch->combined_count = adapter->num_queues;
30562306a36Sopenharmony_ci}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistatic int tsnep_ethtool_get_ts_info(struct net_device *netdev,
30862306a36Sopenharmony_ci				     struct ethtool_ts_info *info)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	struct tsnep_adapter *adapter = netdev_priv(netdev);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
31362306a36Sopenharmony_ci				SOF_TIMESTAMPING_RX_SOFTWARE |
31462306a36Sopenharmony_ci				SOF_TIMESTAMPING_SOFTWARE |
31562306a36Sopenharmony_ci				SOF_TIMESTAMPING_TX_HARDWARE |
31662306a36Sopenharmony_ci				SOF_TIMESTAMPING_RX_HARDWARE |
31762306a36Sopenharmony_ci				SOF_TIMESTAMPING_RAW_HARDWARE;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	if (adapter->ptp_clock)
32062306a36Sopenharmony_ci		info->phc_index = ptp_clock_index(adapter->ptp_clock);
32162306a36Sopenharmony_ci	else
32262306a36Sopenharmony_ci		info->phc_index = -1;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	info->tx_types = BIT(HWTSTAMP_TX_OFF) |
32562306a36Sopenharmony_ci			 BIT(HWTSTAMP_TX_ON);
32662306a36Sopenharmony_ci	info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
32762306a36Sopenharmony_ci			   BIT(HWTSTAMP_FILTER_ALL);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	return 0;
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic struct tsnep_queue *tsnep_get_queue_with_tx(struct tsnep_adapter *adapter,
33362306a36Sopenharmony_ci						   int index)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	int i;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	for (i = 0; i < adapter->num_queues; i++) {
33862306a36Sopenharmony_ci		if (adapter->queue[i].tx) {
33962306a36Sopenharmony_ci			if (index == 0)
34062306a36Sopenharmony_ci				return &adapter->queue[i];
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci			index--;
34362306a36Sopenharmony_ci		}
34462306a36Sopenharmony_ci	}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	return NULL;
34762306a36Sopenharmony_ci}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistatic struct tsnep_queue *tsnep_get_queue_with_rx(struct tsnep_adapter *adapter,
35062306a36Sopenharmony_ci						   int index)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	int i;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	for (i = 0; i < adapter->num_queues; i++) {
35562306a36Sopenharmony_ci		if (adapter->queue[i].rx) {
35662306a36Sopenharmony_ci			if (index == 0)
35762306a36Sopenharmony_ci				return &adapter->queue[i];
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci			index--;
36062306a36Sopenharmony_ci		}
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	return NULL;
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic int tsnep_ethtool_get_coalesce(struct net_device *netdev,
36762306a36Sopenharmony_ci				      struct ethtool_coalesce *ec,
36862306a36Sopenharmony_ci				      struct kernel_ethtool_coalesce *kernel_coal,
36962306a36Sopenharmony_ci				      struct netlink_ext_ack *extack)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	struct tsnep_adapter *adapter = netdev_priv(netdev);
37262306a36Sopenharmony_ci	struct tsnep_queue *queue;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	queue = tsnep_get_queue_with_rx(adapter, 0);
37562306a36Sopenharmony_ci	if (queue)
37662306a36Sopenharmony_ci		ec->rx_coalesce_usecs = tsnep_get_irq_coalesce(queue);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	queue = tsnep_get_queue_with_tx(adapter, 0);
37962306a36Sopenharmony_ci	if (queue)
38062306a36Sopenharmony_ci		ec->tx_coalesce_usecs = tsnep_get_irq_coalesce(queue);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	return 0;
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cistatic int tsnep_ethtool_set_coalesce(struct net_device *netdev,
38662306a36Sopenharmony_ci				      struct ethtool_coalesce *ec,
38762306a36Sopenharmony_ci				      struct kernel_ethtool_coalesce *kernel_coal,
38862306a36Sopenharmony_ci				      struct netlink_ext_ack *extack)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	struct tsnep_adapter *adapter = netdev_priv(netdev);
39162306a36Sopenharmony_ci	int i;
39262306a36Sopenharmony_ci	int retval;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	for (i = 0; i < adapter->num_queues; i++) {
39562306a36Sopenharmony_ci		/* RX coalesce has priority for queues with TX and RX */
39662306a36Sopenharmony_ci		if (adapter->queue[i].rx)
39762306a36Sopenharmony_ci			retval = tsnep_set_irq_coalesce(&adapter->queue[i],
39862306a36Sopenharmony_ci							ec->rx_coalesce_usecs);
39962306a36Sopenharmony_ci		else
40062306a36Sopenharmony_ci			retval = tsnep_set_irq_coalesce(&adapter->queue[i],
40162306a36Sopenharmony_ci							ec->tx_coalesce_usecs);
40262306a36Sopenharmony_ci		if (retval != 0)
40362306a36Sopenharmony_ci			return retval;
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	return 0;
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic int tsnep_ethtool_get_per_queue_coalesce(struct net_device *netdev,
41062306a36Sopenharmony_ci						u32 queue,
41162306a36Sopenharmony_ci						struct ethtool_coalesce *ec)
41262306a36Sopenharmony_ci{
41362306a36Sopenharmony_ci	struct tsnep_adapter *adapter = netdev_priv(netdev);
41462306a36Sopenharmony_ci	struct tsnep_queue *queue_with_rx;
41562306a36Sopenharmony_ci	struct tsnep_queue *queue_with_tx;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	if (queue >= max(adapter->num_tx_queues, adapter->num_rx_queues))
41862306a36Sopenharmony_ci		return -EINVAL;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	queue_with_rx = tsnep_get_queue_with_rx(adapter, queue);
42162306a36Sopenharmony_ci	if (queue_with_rx)
42262306a36Sopenharmony_ci		ec->rx_coalesce_usecs = tsnep_get_irq_coalesce(queue_with_rx);
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	queue_with_tx = tsnep_get_queue_with_tx(adapter, queue);
42562306a36Sopenharmony_ci	if (queue_with_tx)
42662306a36Sopenharmony_ci		ec->tx_coalesce_usecs = tsnep_get_irq_coalesce(queue_with_tx);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	return 0;
42962306a36Sopenharmony_ci}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cistatic int tsnep_ethtool_set_per_queue_coalesce(struct net_device *netdev,
43262306a36Sopenharmony_ci						u32 queue,
43362306a36Sopenharmony_ci						struct ethtool_coalesce *ec)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	struct tsnep_adapter *adapter = netdev_priv(netdev);
43662306a36Sopenharmony_ci	struct tsnep_queue *queue_with_rx;
43762306a36Sopenharmony_ci	struct tsnep_queue *queue_with_tx;
43862306a36Sopenharmony_ci	int retval;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	if (queue >= max(adapter->num_tx_queues, adapter->num_rx_queues))
44162306a36Sopenharmony_ci		return -EINVAL;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	queue_with_rx = tsnep_get_queue_with_rx(adapter, queue);
44462306a36Sopenharmony_ci	if (queue_with_rx) {
44562306a36Sopenharmony_ci		retval = tsnep_set_irq_coalesce(queue_with_rx, ec->rx_coalesce_usecs);
44662306a36Sopenharmony_ci		if (retval != 0)
44762306a36Sopenharmony_ci			return retval;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	/* RX coalesce has priority for queues with TX and RX */
45162306a36Sopenharmony_ci	queue_with_tx = tsnep_get_queue_with_tx(adapter, queue);
45262306a36Sopenharmony_ci	if (queue_with_tx && !queue_with_tx->rx) {
45362306a36Sopenharmony_ci		retval = tsnep_set_irq_coalesce(queue_with_tx, ec->tx_coalesce_usecs);
45462306a36Sopenharmony_ci		if (retval != 0)
45562306a36Sopenharmony_ci			return retval;
45662306a36Sopenharmony_ci	}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	return 0;
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ciconst struct ethtool_ops tsnep_ethtool_ops = {
46262306a36Sopenharmony_ci	.supported_coalesce_params = ETHTOOL_COALESCE_USECS,
46362306a36Sopenharmony_ci	.get_drvinfo = tsnep_ethtool_get_drvinfo,
46462306a36Sopenharmony_ci	.get_regs_len = tsnep_ethtool_get_regs_len,
46562306a36Sopenharmony_ci	.get_regs = tsnep_ethtool_get_regs,
46662306a36Sopenharmony_ci	.get_msglevel = tsnep_ethtool_get_msglevel,
46762306a36Sopenharmony_ci	.set_msglevel = tsnep_ethtool_set_msglevel,
46862306a36Sopenharmony_ci	.nway_reset = phy_ethtool_nway_reset,
46962306a36Sopenharmony_ci	.get_link = ethtool_op_get_link,
47062306a36Sopenharmony_ci	.self_test = tsnep_ethtool_self_test,
47162306a36Sopenharmony_ci	.get_strings = tsnep_ethtool_get_strings,
47262306a36Sopenharmony_ci	.get_ethtool_stats = tsnep_ethtool_get_ethtool_stats,
47362306a36Sopenharmony_ci	.get_sset_count = tsnep_ethtool_get_sset_count,
47462306a36Sopenharmony_ci	.get_rxnfc = tsnep_ethtool_get_rxnfc,
47562306a36Sopenharmony_ci	.set_rxnfc = tsnep_ethtool_set_rxnfc,
47662306a36Sopenharmony_ci	.get_channels = tsnep_ethtool_get_channels,
47762306a36Sopenharmony_ci	.get_ts_info = tsnep_ethtool_get_ts_info,
47862306a36Sopenharmony_ci	.get_coalesce = tsnep_ethtool_get_coalesce,
47962306a36Sopenharmony_ci	.set_coalesce = tsnep_ethtool_set_coalesce,
48062306a36Sopenharmony_ci	.get_per_queue_coalesce = tsnep_ethtool_get_per_queue_coalesce,
48162306a36Sopenharmony_ci	.set_per_queue_coalesce = tsnep_ethtool_set_per_queue_coalesce,
48262306a36Sopenharmony_ci	.get_link_ksettings = phy_ethtool_get_link_ksettings,
48362306a36Sopenharmony_ci	.set_link_ksettings = phy_ethtool_set_link_ksettings,
48462306a36Sopenharmony_ci};
485