1c5760d03SDavid VomLehn /*
2c5760d03SDavid VomLehn  * aQuantia Corporation Network Driver
3c5760d03SDavid VomLehn  * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
4c5760d03SDavid VomLehn  *
5c5760d03SDavid VomLehn  * This program is free software; you can redistribute it and/or modify it
6c5760d03SDavid VomLehn  * under the terms and conditions of the GNU General Public License,
7c5760d03SDavid VomLehn  * version 2, as published by the Free Software Foundation.
8c5760d03SDavid VomLehn  */
9c5760d03SDavid VomLehn 
10c5760d03SDavid VomLehn /* File aq_ethtool.c: Definition of ethertool related functions. */
11c5760d03SDavid VomLehn 
12c5760d03SDavid VomLehn #include "aq_ethtool.h"
13c5760d03SDavid VomLehn #include "aq_nic.h"
14c1af5427SAnton Mikaev #include "aq_vec.h"
158d0bcb01SDmitry Bogdanov #include "aq_filters.h"
16c5760d03SDavid VomLehn 
17c5760d03SDavid VomLehn static void aq_ethtool_get_regs(struct net_device *ndev,
18c5760d03SDavid VomLehn 				struct ethtool_regs *regs, void *p)
19c5760d03SDavid VomLehn {
20c5760d03SDavid VomLehn 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
21c5760d03SDavid VomLehn 	u32 regs_count = aq_nic_get_regs_count(aq_nic);
22c5760d03SDavid VomLehn 
23c5760d03SDavid VomLehn 	memset(p, 0, regs_count * sizeof(u32));
24c5760d03SDavid VomLehn 	aq_nic_get_regs(aq_nic, regs, p);
25c5760d03SDavid VomLehn }
26c5760d03SDavid VomLehn 
27c5760d03SDavid VomLehn static int aq_ethtool_get_regs_len(struct net_device *ndev)
28c5760d03SDavid VomLehn {
29c5760d03SDavid VomLehn 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
30c5760d03SDavid VomLehn 	u32 regs_count = aq_nic_get_regs_count(aq_nic);
31c5760d03SDavid VomLehn 
32c5760d03SDavid VomLehn 	return regs_count * sizeof(u32);
33c5760d03SDavid VomLehn }
34c5760d03SDavid VomLehn 
35c5760d03SDavid VomLehn static u32 aq_ethtool_get_link(struct net_device *ndev)
36c5760d03SDavid VomLehn {
37c5760d03SDavid VomLehn 	return ethtool_op_get_link(ndev);
38c5760d03SDavid VomLehn }
39c5760d03SDavid VomLehn 
40f8244ab5SPhilippe Reynes static int aq_ethtool_get_link_ksettings(struct net_device *ndev,
41f8244ab5SPhilippe Reynes 					 struct ethtool_link_ksettings *cmd)
42c5760d03SDavid VomLehn {
43c5760d03SDavid VomLehn 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
44c5760d03SDavid VomLehn 
45f8244ab5SPhilippe Reynes 	aq_nic_get_link_ksettings(aq_nic, cmd);
46f8244ab5SPhilippe Reynes 	cmd->base.speed = netif_carrier_ok(ndev) ?
47f8244ab5SPhilippe Reynes 				aq_nic_get_link_speed(aq_nic) : 0U;
48c5760d03SDavid VomLehn 
49c5760d03SDavid VomLehn 	return 0;
50c5760d03SDavid VomLehn }
51c5760d03SDavid VomLehn 
52f8244ab5SPhilippe Reynes static int
53f8244ab5SPhilippe Reynes aq_ethtool_set_link_ksettings(struct net_device *ndev,
54f8244ab5SPhilippe Reynes 			      const struct ethtool_link_ksettings *cmd)
55c5760d03SDavid VomLehn {
56c5760d03SDavid VomLehn 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
57c5760d03SDavid VomLehn 
58f8244ab5SPhilippe Reynes 	return aq_nic_set_link_ksettings(aq_nic, cmd);
59c5760d03SDavid VomLehn }
60c5760d03SDavid VomLehn 
61c5760d03SDavid VomLehn static const char aq_ethtool_stat_names[][ETH_GSTRING_LEN] = {
62c5760d03SDavid VomLehn 	"InPackets",
63c5760d03SDavid VomLehn 	"InUCast",
64c5760d03SDavid VomLehn 	"InMCast",
65c5760d03SDavid VomLehn 	"InBCast",
66c5760d03SDavid VomLehn 	"InErrors",
67c5760d03SDavid VomLehn 	"OutPackets",
68c5760d03SDavid VomLehn 	"OutUCast",
69c5760d03SDavid VomLehn 	"OutMCast",
70c5760d03SDavid VomLehn 	"OutBCast",
7198bc036dSIgor Russkikh 	"InUCastOctets",
7298bc036dSIgor Russkikh 	"OutUCastOctets",
7398bc036dSIgor Russkikh 	"InMCastOctets",
7498bc036dSIgor Russkikh 	"OutMCastOctets",
7598bc036dSIgor Russkikh 	"InBCastOctets",
7698bc036dSIgor Russkikh 	"OutBCastOctets",
7798bc036dSIgor Russkikh 	"InOctets",
7898bc036dSIgor Russkikh 	"OutOctets",
79c5760d03SDavid VomLehn 	"InPacketsDma",
80c5760d03SDavid VomLehn 	"OutPacketsDma",
81c5760d03SDavid VomLehn 	"InOctetsDma",
82c5760d03SDavid VomLehn 	"OutOctetsDma",
83c5760d03SDavid VomLehn 	"InDroppedDma",
845d8d84e9SIgor Russkikh };
855d8d84e9SIgor Russkikh 
865d8d84e9SIgor Russkikh static const char aq_ethtool_queue_stat_names[][ETH_GSTRING_LEN] = {
875d8d84e9SIgor Russkikh 	"Queue[%d] InPackets",
885d8d84e9SIgor Russkikh 	"Queue[%d] OutPackets",
895d8d84e9SIgor Russkikh 	"Queue[%d] Restarts",
905d8d84e9SIgor Russkikh 	"Queue[%d] InJumboPackets",
915d8d84e9SIgor Russkikh 	"Queue[%d] InLroPackets",
925d8d84e9SIgor Russkikh 	"Queue[%d] InErrors",
93c5760d03SDavid VomLehn };
94c5760d03SDavid VomLehn 
95c5760d03SDavid VomLehn static void aq_ethtool_stats(struct net_device *ndev,
96c5760d03SDavid VomLehn 			     struct ethtool_stats *stats, u64 *data)
97c5760d03SDavid VomLehn {
98c5760d03SDavid VomLehn 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
995d8d84e9SIgor Russkikh 	struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
100c5760d03SDavid VomLehn 
1015d8d84e9SIgor Russkikh 	memset(data, 0, (ARRAY_SIZE(aq_ethtool_stat_names) +
1025d8d84e9SIgor Russkikh 			 ARRAY_SIZE(aq_ethtool_queue_stat_names) *
1035d8d84e9SIgor Russkikh 			 cfg->vecs) * sizeof(u64));
104c5760d03SDavid VomLehn 	aq_nic_get_stats(aq_nic, data);
105c5760d03SDavid VomLehn }
106c5760d03SDavid VomLehn 
107c5760d03SDavid VomLehn static void aq_ethtool_get_drvinfo(struct net_device *ndev,
108c5760d03SDavid VomLehn 				   struct ethtool_drvinfo *drvinfo)
109c5760d03SDavid VomLehn {
110c5760d03SDavid VomLehn 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
111c5760d03SDavid VomLehn 	struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
112c5760d03SDavid VomLehn 	struct pci_dev *pdev = to_pci_dev(ndev->dev.parent);
113c5760d03SDavid VomLehn 	u32 firmware_version = aq_nic_get_fw_version(aq_nic);
114c5760d03SDavid VomLehn 	u32 regs_count = aq_nic_get_regs_count(aq_nic);
115c5760d03SDavid VomLehn 
116c5760d03SDavid VomLehn 	strlcat(drvinfo->driver, AQ_CFG_DRV_NAME, sizeof(drvinfo->driver));
117c5760d03SDavid VomLehn 	strlcat(drvinfo->version, AQ_CFG_DRV_VERSION, sizeof(drvinfo->version));
118c5760d03SDavid VomLehn 
119c5760d03SDavid VomLehn 	snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
120c5760d03SDavid VomLehn 		 "%u.%u.%u", firmware_version >> 24,
121c5760d03SDavid VomLehn 		 (firmware_version >> 16) & 0xFFU, firmware_version & 0xFFFFU);
122c5760d03SDavid VomLehn 
123c5760d03SDavid VomLehn 	strlcpy(drvinfo->bus_info, pdev ? pci_name(pdev) : "",
124c5760d03SDavid VomLehn 		sizeof(drvinfo->bus_info));
1255d8d84e9SIgor Russkikh 	drvinfo->n_stats = ARRAY_SIZE(aq_ethtool_stat_names) +
1265d8d84e9SIgor Russkikh 		cfg->vecs * ARRAY_SIZE(aq_ethtool_queue_stat_names);
127c5760d03SDavid VomLehn 	drvinfo->testinfo_len = 0;
128c5760d03SDavid VomLehn 	drvinfo->regdump_len = regs_count;
129c5760d03SDavid VomLehn 	drvinfo->eedump_len = 0;
130c5760d03SDavid VomLehn }
131c5760d03SDavid VomLehn 
132c5760d03SDavid VomLehn static void aq_ethtool_get_strings(struct net_device *ndev,
133c5760d03SDavid VomLehn 				   u32 stringset, u8 *data)
134c5760d03SDavid VomLehn {
1355d8d84e9SIgor Russkikh 	int i, si;
136c5760d03SDavid VomLehn 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
137c5760d03SDavid VomLehn 	struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
1385d8d84e9SIgor Russkikh 	u8 *p = data;
139c5760d03SDavid VomLehn 
1405d8d84e9SIgor Russkikh 	if (stringset == ETH_SS_STATS) {
141ff83dbf2SNikita Danilov 		memcpy(p, aq_ethtool_stat_names,
1425d8d84e9SIgor Russkikh 		       sizeof(aq_ethtool_stat_names));
1435d8d84e9SIgor Russkikh 		p = p + sizeof(aq_ethtool_stat_names);
1445d8d84e9SIgor Russkikh 		for (i = 0; i < cfg->vecs; i++) {
1455d8d84e9SIgor Russkikh 			for (si = 0;
1465d8d84e9SIgor Russkikh 				si < ARRAY_SIZE(aq_ethtool_queue_stat_names);
1475d8d84e9SIgor Russkikh 				si++) {
1485d8d84e9SIgor Russkikh 				snprintf(p, ETH_GSTRING_LEN,
1495d8d84e9SIgor Russkikh 					 aq_ethtool_queue_stat_names[si], i);
1505d8d84e9SIgor Russkikh 				p += ETH_GSTRING_LEN;
1515d8d84e9SIgor Russkikh 			}
1525d8d84e9SIgor Russkikh 		}
1535d8d84e9SIgor Russkikh 	}
154c5760d03SDavid VomLehn }
155c5760d03SDavid VomLehn 
156c5760d03SDavid VomLehn static int aq_ethtool_get_sset_count(struct net_device *ndev, int stringset)
157c5760d03SDavid VomLehn {
158c5760d03SDavid VomLehn 	int ret = 0;
159c5760d03SDavid VomLehn 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
160c5760d03SDavid VomLehn 	struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
161c5760d03SDavid VomLehn 
162c5760d03SDavid VomLehn 	switch (stringset) {
163c5760d03SDavid VomLehn 	case ETH_SS_STATS:
1645d8d84e9SIgor Russkikh 		ret = ARRAY_SIZE(aq_ethtool_stat_names) +
1655d8d84e9SIgor Russkikh 			cfg->vecs * ARRAY_SIZE(aq_ethtool_queue_stat_names);
166c5760d03SDavid VomLehn 		break;
167c5760d03SDavid VomLehn 	default:
168c5760d03SDavid VomLehn 		ret = -EOPNOTSUPP;
169c5760d03SDavid VomLehn 	}
170c5760d03SDavid VomLehn 	return ret;
171c5760d03SDavid VomLehn }
172c5760d03SDavid VomLehn 
173c5760d03SDavid VomLehn static u32 aq_ethtool_get_rss_indir_size(struct net_device *ndev)
174c5760d03SDavid VomLehn {
175c5760d03SDavid VomLehn 	return AQ_CFG_RSS_INDIRECTION_TABLE_MAX;
176c5760d03SDavid VomLehn }
177c5760d03SDavid VomLehn 
178c5760d03SDavid VomLehn static u32 aq_ethtool_get_rss_key_size(struct net_device *ndev)
179c5760d03SDavid VomLehn {
180c5760d03SDavid VomLehn 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
181c5760d03SDavid VomLehn 	struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
182c5760d03SDavid VomLehn 
183c5760d03SDavid VomLehn 	return sizeof(cfg->aq_rss.hash_secret_key);
184c5760d03SDavid VomLehn }
185c5760d03SDavid VomLehn 
186c5760d03SDavid VomLehn static int aq_ethtool_get_rss(struct net_device *ndev, u32 *indir, u8 *key,
187c5760d03SDavid VomLehn 			      u8 *hfunc)
188c5760d03SDavid VomLehn {
189c5760d03SDavid VomLehn 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
190c5760d03SDavid VomLehn 	struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
191c5760d03SDavid VomLehn 	unsigned int i = 0U;
192c5760d03SDavid VomLehn 
193c5760d03SDavid VomLehn 	if (hfunc)
194c5760d03SDavid VomLehn 		*hfunc = ETH_RSS_HASH_TOP; /* Toeplitz */
195c5760d03SDavid VomLehn 	if (indir) {
196c5760d03SDavid VomLehn 		for (i = 0; i < AQ_CFG_RSS_INDIRECTION_TABLE_MAX; i++)
197c5760d03SDavid VomLehn 			indir[i] = cfg->aq_rss.indirection_table[i];
198c5760d03SDavid VomLehn 	}
199c5760d03SDavid VomLehn 	if (key)
200c5760d03SDavid VomLehn 		memcpy(key, cfg->aq_rss.hash_secret_key,
201c5760d03SDavid VomLehn 		       sizeof(cfg->aq_rss.hash_secret_key));
202c5760d03SDavid VomLehn 	return 0;
203c5760d03SDavid VomLehn }
204c5760d03SDavid VomLehn 
20539163767SDmitry Bogdanov static int aq_ethtool_set_rss(struct net_device *netdev, const u32 *indir,
20639163767SDmitry Bogdanov 			      const u8 *key, const u8 hfunc)
20739163767SDmitry Bogdanov {
20839163767SDmitry Bogdanov 	struct aq_nic_s *aq_nic = netdev_priv(netdev);
20939163767SDmitry Bogdanov 	struct aq_nic_cfg_s *cfg;
21039163767SDmitry Bogdanov 	unsigned int i = 0U;
21139163767SDmitry Bogdanov 	u32 rss_entries;
21239163767SDmitry Bogdanov 	int err = 0;
21339163767SDmitry Bogdanov 
21439163767SDmitry Bogdanov 	cfg = aq_nic_get_cfg(aq_nic);
21539163767SDmitry Bogdanov 	rss_entries = cfg->aq_rss.indirection_table_size;
21639163767SDmitry Bogdanov 
21739163767SDmitry Bogdanov 	/* We do not allow change in unsupported parameters */
21839163767SDmitry Bogdanov 	if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)
21939163767SDmitry Bogdanov 		return -EOPNOTSUPP;
22039163767SDmitry Bogdanov 	/* Fill out the redirection table */
22139163767SDmitry Bogdanov 	if (indir)
22239163767SDmitry Bogdanov 		for (i = 0; i < rss_entries; i++)
22339163767SDmitry Bogdanov 			cfg->aq_rss.indirection_table[i] = indir[i];
22439163767SDmitry Bogdanov 
22539163767SDmitry Bogdanov 	/* Fill out the rss hash key */
22639163767SDmitry Bogdanov 	if (key) {
22739163767SDmitry Bogdanov 		memcpy(cfg->aq_rss.hash_secret_key, key,
22839163767SDmitry Bogdanov 		       sizeof(cfg->aq_rss.hash_secret_key));
22939163767SDmitry Bogdanov 		err = aq_nic->aq_hw_ops->hw_rss_hash_set(aq_nic->aq_hw,
23039163767SDmitry Bogdanov 			&cfg->aq_rss);
23139163767SDmitry Bogdanov 		if (err)
23239163767SDmitry Bogdanov 			return err;
23339163767SDmitry Bogdanov 	}
23439163767SDmitry Bogdanov 
23539163767SDmitry Bogdanov 	err = aq_nic->aq_hw_ops->hw_rss_set(aq_nic->aq_hw, &cfg->aq_rss);
23639163767SDmitry Bogdanov 
23739163767SDmitry Bogdanov 	return err;
23839163767SDmitry Bogdanov }
23939163767SDmitry Bogdanov 
240c5760d03SDavid VomLehn static int aq_ethtool_get_rxnfc(struct net_device *ndev,
241c5760d03SDavid VomLehn 				struct ethtool_rxnfc *cmd,
242c5760d03SDavid VomLehn 				u32 *rule_locs)
243c5760d03SDavid VomLehn {
244c5760d03SDavid VomLehn 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
245c5760d03SDavid VomLehn 	struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
246c5760d03SDavid VomLehn 	int err = 0;
247c5760d03SDavid VomLehn 
248c5760d03SDavid VomLehn 	switch (cmd->cmd) {
249c5760d03SDavid VomLehn 	case ETHTOOL_GRXRINGS:
250c5760d03SDavid VomLehn 		cmd->data = cfg->vecs;
251c5760d03SDavid VomLehn 		break;
2528d0bcb01SDmitry Bogdanov 	case ETHTOOL_GRXCLSRLCNT:
2538d0bcb01SDmitry Bogdanov 		cmd->rule_cnt = aq_get_rxnfc_count_all_rules(aq_nic);
2548d0bcb01SDmitry Bogdanov 		break;
2558d0bcb01SDmitry Bogdanov 	case ETHTOOL_GRXCLSRULE:
2568d0bcb01SDmitry Bogdanov 		err = aq_get_rxnfc_rule(aq_nic, cmd);
2578d0bcb01SDmitry Bogdanov 		break;
2588d0bcb01SDmitry Bogdanov 	case ETHTOOL_GRXCLSRLALL:
2598d0bcb01SDmitry Bogdanov 		err = aq_get_rxnfc_all_rules(aq_nic, cmd, rule_locs);
2608d0bcb01SDmitry Bogdanov 		break;
2618d0bcb01SDmitry Bogdanov 	default:
2628d0bcb01SDmitry Bogdanov 		err = -EOPNOTSUPP;
2638d0bcb01SDmitry Bogdanov 		break;
2648d0bcb01SDmitry Bogdanov 	}
265c5760d03SDavid VomLehn 
2668d0bcb01SDmitry Bogdanov 	return err;
2678d0bcb01SDmitry Bogdanov }
2688d0bcb01SDmitry Bogdanov 
2698d0bcb01SDmitry Bogdanov static int aq_ethtool_set_rxnfc(struct net_device *ndev,
2708d0bcb01SDmitry Bogdanov 				struct ethtool_rxnfc *cmd)
2718d0bcb01SDmitry Bogdanov {
2728d0bcb01SDmitry Bogdanov 	int err = 0;
2738d0bcb01SDmitry Bogdanov 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
2748d0bcb01SDmitry Bogdanov 
2758d0bcb01SDmitry Bogdanov 	switch (cmd->cmd) {
2768d0bcb01SDmitry Bogdanov 	case ETHTOOL_SRXCLSRLINS:
2778d0bcb01SDmitry Bogdanov 		err = aq_add_rxnfc_rule(aq_nic, cmd);
2788d0bcb01SDmitry Bogdanov 		break;
2798d0bcb01SDmitry Bogdanov 	case ETHTOOL_SRXCLSRLDEL:
2808d0bcb01SDmitry Bogdanov 		err = aq_del_rxnfc_rule(aq_nic, cmd);
2818d0bcb01SDmitry Bogdanov 		break;
282c5760d03SDavid VomLehn 	default:
283c5760d03SDavid VomLehn 		err = -EOPNOTSUPP;
284c5760d03SDavid VomLehn 		break;
285c5760d03SDavid VomLehn 	}
286c5760d03SDavid VomLehn 
287c5760d03SDavid VomLehn 	return err;
288c5760d03SDavid VomLehn }
289c5760d03SDavid VomLehn 
2902660d226SWei Yongjun static int aq_ethtool_get_coalesce(struct net_device *ndev,
291b82ee71aSIgor Russkikh 				   struct ethtool_coalesce *coal)
292b82ee71aSIgor Russkikh {
293b82ee71aSIgor Russkikh 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
294b82ee71aSIgor Russkikh 	struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
295b82ee71aSIgor Russkikh 
296b82ee71aSIgor Russkikh 	if (cfg->itr == AQ_CFG_INTERRUPT_MODERATION_ON ||
297b82ee71aSIgor Russkikh 	    cfg->itr == AQ_CFG_INTERRUPT_MODERATION_AUTO) {
298b82ee71aSIgor Russkikh 		coal->rx_coalesce_usecs = cfg->rx_itr;
299b82ee71aSIgor Russkikh 		coal->tx_coalesce_usecs = cfg->tx_itr;
300b82ee71aSIgor Russkikh 		coal->rx_max_coalesced_frames = 0;
301b82ee71aSIgor Russkikh 		coal->tx_max_coalesced_frames = 0;
302b82ee71aSIgor Russkikh 	} else {
303b82ee71aSIgor Russkikh 		coal->rx_coalesce_usecs = 0;
304b82ee71aSIgor Russkikh 		coal->tx_coalesce_usecs = 0;
305b82ee71aSIgor Russkikh 		coal->rx_max_coalesced_frames = 1;
306b82ee71aSIgor Russkikh 		coal->tx_max_coalesced_frames = 1;
307b82ee71aSIgor Russkikh 	}
308b82ee71aSIgor Russkikh 	return 0;
309b82ee71aSIgor Russkikh }
310b82ee71aSIgor Russkikh 
3112660d226SWei Yongjun static int aq_ethtool_set_coalesce(struct net_device *ndev,
312b82ee71aSIgor Russkikh 				   struct ethtool_coalesce *coal)
313b82ee71aSIgor Russkikh {
314b82ee71aSIgor Russkikh 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
315b82ee71aSIgor Russkikh 	struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
316b82ee71aSIgor Russkikh 
317b82ee71aSIgor Russkikh 	/* This is not yet supported
318b82ee71aSIgor Russkikh 	 */
319b82ee71aSIgor Russkikh 	if (coal->use_adaptive_rx_coalesce || coal->use_adaptive_tx_coalesce)
320b82ee71aSIgor Russkikh 		return -EOPNOTSUPP;
321b82ee71aSIgor Russkikh 
322b82ee71aSIgor Russkikh 	/* Atlantic only supports timing based coalescing
323b82ee71aSIgor Russkikh 	 */
324b82ee71aSIgor Russkikh 	if (coal->rx_max_coalesced_frames > 1 ||
325b82ee71aSIgor Russkikh 	    coal->rx_coalesce_usecs_irq ||
326b82ee71aSIgor Russkikh 	    coal->rx_max_coalesced_frames_irq)
327b82ee71aSIgor Russkikh 		return -EOPNOTSUPP;
328b82ee71aSIgor Russkikh 
329b82ee71aSIgor Russkikh 	if (coal->tx_max_coalesced_frames > 1 ||
330b82ee71aSIgor Russkikh 	    coal->tx_coalesce_usecs_irq ||
331b82ee71aSIgor Russkikh 	    coal->tx_max_coalesced_frames_irq)
332b82ee71aSIgor Russkikh 		return -EOPNOTSUPP;
333b82ee71aSIgor Russkikh 
334b82ee71aSIgor Russkikh 	/* We do not support frame counting. Check this
335b82ee71aSIgor Russkikh 	 */
336b82ee71aSIgor Russkikh 	if (!(coal->rx_max_coalesced_frames == !coal->rx_coalesce_usecs))
337b82ee71aSIgor Russkikh 		return -EOPNOTSUPP;
338b82ee71aSIgor Russkikh 	if (!(coal->tx_max_coalesced_frames == !coal->tx_coalesce_usecs))
339b82ee71aSIgor Russkikh 		return -EOPNOTSUPP;
340b82ee71aSIgor Russkikh 
341b82ee71aSIgor Russkikh 	if (coal->rx_coalesce_usecs > AQ_CFG_INTERRUPT_MODERATION_USEC_MAX ||
342b82ee71aSIgor Russkikh 	    coal->tx_coalesce_usecs > AQ_CFG_INTERRUPT_MODERATION_USEC_MAX)
343b82ee71aSIgor Russkikh 		return -EINVAL;
344b82ee71aSIgor Russkikh 
345b82ee71aSIgor Russkikh 	cfg->itr = AQ_CFG_INTERRUPT_MODERATION_ON;
346b82ee71aSIgor Russkikh 
347b82ee71aSIgor Russkikh 	cfg->rx_itr = coal->rx_coalesce_usecs;
348b82ee71aSIgor Russkikh 	cfg->tx_itr = coal->tx_coalesce_usecs;
349b82ee71aSIgor Russkikh 
350b82ee71aSIgor Russkikh 	return aq_nic_update_interrupt_moderation_settings(aq_nic);
351b82ee71aSIgor Russkikh }
352b82ee71aSIgor Russkikh 
353a0da96c0SYana Esina static void aq_ethtool_get_wol(struct net_device *ndev,
354a0da96c0SYana Esina 			       struct ethtool_wolinfo *wol)
355a0da96c0SYana Esina {
356a0da96c0SYana Esina 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
357a0da96c0SYana Esina 	struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
358a0da96c0SYana Esina 
359a0da96c0SYana Esina 	wol->supported = WAKE_MAGIC;
360a0da96c0SYana Esina 	wol->wolopts = 0;
361a0da96c0SYana Esina 
362a0da96c0SYana Esina 	if (cfg->wol)
363a0da96c0SYana Esina 		wol->wolopts |= WAKE_MAGIC;
364a0da96c0SYana Esina }
365a0da96c0SYana Esina 
366a0da96c0SYana Esina static int aq_ethtool_set_wol(struct net_device *ndev,
367a0da96c0SYana Esina 			      struct ethtool_wolinfo *wol)
368a0da96c0SYana Esina {
369a0da96c0SYana Esina 	struct pci_dev *pdev = to_pci_dev(ndev->dev.parent);
370a0da96c0SYana Esina 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
371a0da96c0SYana Esina 	struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
372a0da96c0SYana Esina 	int err = 0;
373a0da96c0SYana Esina 
374a0da96c0SYana Esina 	if (wol->wolopts & WAKE_MAGIC)
375a0da96c0SYana Esina 		cfg->wol |= AQ_NIC_WOL_ENABLED;
376a0da96c0SYana Esina 	else
377a0da96c0SYana Esina 		cfg->wol &= ~AQ_NIC_WOL_ENABLED;
378a0da96c0SYana Esina 	err = device_set_wakeup_enable(&pdev->dev, wol->wolopts);
379a0da96c0SYana Esina 
380a0da96c0SYana Esina 	return err;
381a0da96c0SYana Esina }
382a0da96c0SYana Esina 
38392ab6407SYana Esina static enum hw_atl_fw2x_rate eee_mask_to_ethtool_mask(u32 speed)
38492ab6407SYana Esina {
38592ab6407SYana Esina 	u32 rate = 0;
38692ab6407SYana Esina 
38792ab6407SYana Esina 	if (speed & AQ_NIC_RATE_EEE_10G)
38892ab6407SYana Esina 		rate |= SUPPORTED_10000baseT_Full;
38992ab6407SYana Esina 
39092ab6407SYana Esina 	if (speed & AQ_NIC_RATE_EEE_2GS)
39192ab6407SYana Esina 		rate |= SUPPORTED_2500baseX_Full;
39292ab6407SYana Esina 
39392ab6407SYana Esina 	if (speed & AQ_NIC_RATE_EEE_1G)
39492ab6407SYana Esina 		rate |= SUPPORTED_1000baseT_Full;
39592ab6407SYana Esina 
39692ab6407SYana Esina 	return rate;
39792ab6407SYana Esina }
39892ab6407SYana Esina 
39992ab6407SYana Esina static int aq_ethtool_get_eee(struct net_device *ndev, struct ethtool_eee *eee)
40092ab6407SYana Esina {
40192ab6407SYana Esina 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
40292ab6407SYana Esina 	u32 rate, supported_rates;
40392ab6407SYana Esina 	int err = 0;
40492ab6407SYana Esina 
40592ab6407SYana Esina 	if (!aq_nic->aq_fw_ops->get_eee_rate)
40692ab6407SYana Esina 		return -EOPNOTSUPP;
40792ab6407SYana Esina 
40892ab6407SYana Esina 	err = aq_nic->aq_fw_ops->get_eee_rate(aq_nic->aq_hw, &rate,
40992ab6407SYana Esina 					      &supported_rates);
41092ab6407SYana Esina 	if (err < 0)
41192ab6407SYana Esina 		return err;
41292ab6407SYana Esina 
41392ab6407SYana Esina 	eee->supported = eee_mask_to_ethtool_mask(supported_rates);
41492ab6407SYana Esina 
41592ab6407SYana Esina 	if (aq_nic->aq_nic_cfg.eee_speeds)
41692ab6407SYana Esina 		eee->advertised = eee->supported;
41792ab6407SYana Esina 
41892ab6407SYana Esina 	eee->lp_advertised = eee_mask_to_ethtool_mask(rate);
41992ab6407SYana Esina 
42092ab6407SYana Esina 	eee->eee_enabled = !!eee->advertised;
42192ab6407SYana Esina 
42292ab6407SYana Esina 	eee->tx_lpi_enabled = eee->eee_enabled;
42392ab6407SYana Esina 	if (eee->advertised & eee->lp_advertised)
42492ab6407SYana Esina 		eee->eee_active = true;
42592ab6407SYana Esina 
42692ab6407SYana Esina 	return 0;
42792ab6407SYana Esina }
42892ab6407SYana Esina 
42992ab6407SYana Esina static int aq_ethtool_set_eee(struct net_device *ndev, struct ethtool_eee *eee)
43092ab6407SYana Esina {
43192ab6407SYana Esina 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
43292ab6407SYana Esina 	u32 rate, supported_rates;
43392ab6407SYana Esina 	struct aq_nic_cfg_s *cfg;
43492ab6407SYana Esina 	int err = 0;
43592ab6407SYana Esina 
43692ab6407SYana Esina 	cfg = aq_nic_get_cfg(aq_nic);
43792ab6407SYana Esina 
43892ab6407SYana Esina 	if (unlikely(!aq_nic->aq_fw_ops->get_eee_rate ||
43992ab6407SYana Esina 		     !aq_nic->aq_fw_ops->set_eee_rate))
44092ab6407SYana Esina 		return -EOPNOTSUPP;
44192ab6407SYana Esina 
44292ab6407SYana Esina 	err = aq_nic->aq_fw_ops->get_eee_rate(aq_nic->aq_hw, &rate,
44392ab6407SYana Esina 					      &supported_rates);
44492ab6407SYana Esina 	if (err < 0)
44592ab6407SYana Esina 		return err;
44692ab6407SYana Esina 
44792ab6407SYana Esina 	if (eee->eee_enabled) {
44892ab6407SYana Esina 		rate = supported_rates;
44992ab6407SYana Esina 		cfg->eee_speeds = rate;
45092ab6407SYana Esina 	} else {
45192ab6407SYana Esina 		rate = 0;
45292ab6407SYana Esina 		cfg->eee_speeds = 0;
45392ab6407SYana Esina 	}
45492ab6407SYana Esina 
45592ab6407SYana Esina 	return aq_nic->aq_fw_ops->set_eee_rate(aq_nic->aq_hw, rate);
45692ab6407SYana Esina }
45792ab6407SYana Esina 
458b8d68b62SAnton Mikaev static int aq_ethtool_nway_reset(struct net_device *ndev)
459b8d68b62SAnton Mikaev {
460b8d68b62SAnton Mikaev 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
461b8d68b62SAnton Mikaev 
462b8d68b62SAnton Mikaev 	if (unlikely(!aq_nic->aq_fw_ops->renegotiate))
463b8d68b62SAnton Mikaev 		return -EOPNOTSUPP;
464b8d68b62SAnton Mikaev 
465b8d68b62SAnton Mikaev 	if (netif_running(ndev))
466b8d68b62SAnton Mikaev 		return aq_nic->aq_fw_ops->renegotiate(aq_nic->aq_hw);
467b8d68b62SAnton Mikaev 
468b8d68b62SAnton Mikaev 	return 0;
469b8d68b62SAnton Mikaev }
470b8d68b62SAnton Mikaev 
471288551deSIgor Russkikh static void aq_ethtool_get_pauseparam(struct net_device *ndev,
472288551deSIgor Russkikh 				      struct ethtool_pauseparam *pause)
473288551deSIgor Russkikh {
474288551deSIgor Russkikh 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
47535e8e8b4SIgor Russkikh 	u32 fc = aq_nic->aq_nic_cfg.flow_control;
476288551deSIgor Russkikh 
477288551deSIgor Russkikh 	pause->autoneg = 0;
478288551deSIgor Russkikh 
47935e8e8b4SIgor Russkikh 	pause->rx_pause = !!(fc & AQ_NIC_FC_RX);
48035e8e8b4SIgor Russkikh 	pause->tx_pause = !!(fc & AQ_NIC_FC_TX);
48135e8e8b4SIgor Russkikh 
482288551deSIgor Russkikh }
483288551deSIgor Russkikh 
484288551deSIgor Russkikh static int aq_ethtool_set_pauseparam(struct net_device *ndev,
485288551deSIgor Russkikh 				     struct ethtool_pauseparam *pause)
486288551deSIgor Russkikh {
487288551deSIgor Russkikh 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
488288551deSIgor Russkikh 	int err = 0;
489288551deSIgor Russkikh 
490288551deSIgor Russkikh 	if (!aq_nic->aq_fw_ops->set_flow_control)
491288551deSIgor Russkikh 		return -EOPNOTSUPP;
492288551deSIgor Russkikh 
493288551deSIgor Russkikh 	if (pause->autoneg == AUTONEG_ENABLE)
494288551deSIgor Russkikh 		return -EOPNOTSUPP;
495288551deSIgor Russkikh 
496288551deSIgor Russkikh 	if (pause->rx_pause)
497288551deSIgor Russkikh 		aq_nic->aq_hw->aq_nic_cfg->flow_control |= AQ_NIC_FC_RX;
498288551deSIgor Russkikh 	else
499288551deSIgor Russkikh 		aq_nic->aq_hw->aq_nic_cfg->flow_control &= ~AQ_NIC_FC_RX;
500288551deSIgor Russkikh 
501288551deSIgor Russkikh 	if (pause->tx_pause)
502288551deSIgor Russkikh 		aq_nic->aq_hw->aq_nic_cfg->flow_control |= AQ_NIC_FC_TX;
503288551deSIgor Russkikh 	else
504288551deSIgor Russkikh 		aq_nic->aq_hw->aq_nic_cfg->flow_control &= ~AQ_NIC_FC_TX;
505288551deSIgor Russkikh 
506288551deSIgor Russkikh 	err = aq_nic->aq_fw_ops->set_flow_control(aq_nic->aq_hw);
507288551deSIgor Russkikh 
508288551deSIgor Russkikh 	return err;
509288551deSIgor Russkikh }
510288551deSIgor Russkikh 
511c1af5427SAnton Mikaev static void aq_get_ringparam(struct net_device *ndev,
512c1af5427SAnton Mikaev 			     struct ethtool_ringparam *ring)
513c1af5427SAnton Mikaev {
514c1af5427SAnton Mikaev 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
515c1af5427SAnton Mikaev 	struct aq_nic_cfg_s *aq_nic_cfg = aq_nic_get_cfg(aq_nic);
516c1af5427SAnton Mikaev 
517c1af5427SAnton Mikaev 	ring->rx_pending = aq_nic_cfg->rxds;
518c1af5427SAnton Mikaev 	ring->tx_pending = aq_nic_cfg->txds;
519c1af5427SAnton Mikaev 
520c1af5427SAnton Mikaev 	ring->rx_max_pending = aq_nic_cfg->aq_hw_caps->rxds_max;
521c1af5427SAnton Mikaev 	ring->tx_max_pending = aq_nic_cfg->aq_hw_caps->txds_max;
522c1af5427SAnton Mikaev }
523c1af5427SAnton Mikaev 
524c1af5427SAnton Mikaev static int aq_set_ringparam(struct net_device *ndev,
525c1af5427SAnton Mikaev 			    struct ethtool_ringparam *ring)
526c1af5427SAnton Mikaev {
527c1af5427SAnton Mikaev 	int err = 0;
528c1af5427SAnton Mikaev 	bool ndev_running = false;
529c1af5427SAnton Mikaev 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
530c1af5427SAnton Mikaev 	struct aq_nic_cfg_s *aq_nic_cfg = aq_nic_get_cfg(aq_nic);
531c1af5427SAnton Mikaev 	const struct aq_hw_caps_s *hw_caps = aq_nic_cfg->aq_hw_caps;
532c1af5427SAnton Mikaev 
533c1af5427SAnton Mikaev 	if (ring->rx_mini_pending || ring->rx_jumbo_pending) {
534c1af5427SAnton Mikaev 		err = -EOPNOTSUPP;
535c1af5427SAnton Mikaev 		goto err_exit;
536c1af5427SAnton Mikaev 	}
537c1af5427SAnton Mikaev 
538c1af5427SAnton Mikaev 	if (netif_running(ndev)) {
539c1af5427SAnton Mikaev 		ndev_running = true;
540c1af5427SAnton Mikaev 		dev_close(ndev);
541c1af5427SAnton Mikaev 	}
542c1af5427SAnton Mikaev 
543c1af5427SAnton Mikaev 	aq_nic_free_vectors(aq_nic);
544c1af5427SAnton Mikaev 
545c1af5427SAnton Mikaev 	aq_nic_cfg->rxds = max(ring->rx_pending, hw_caps->rxds_min);
546c1af5427SAnton Mikaev 	aq_nic_cfg->rxds = min(aq_nic_cfg->rxds, hw_caps->rxds_max);
547c1af5427SAnton Mikaev 	aq_nic_cfg->rxds = ALIGN(aq_nic_cfg->rxds, AQ_HW_RXD_MULTIPLE);
548c1af5427SAnton Mikaev 
549c1af5427SAnton Mikaev 	aq_nic_cfg->txds = max(ring->tx_pending, hw_caps->txds_min);
550c1af5427SAnton Mikaev 	aq_nic_cfg->txds = min(aq_nic_cfg->txds, hw_caps->txds_max);
551c1af5427SAnton Mikaev 	aq_nic_cfg->txds = ALIGN(aq_nic_cfg->txds, AQ_HW_TXD_MULTIPLE);
552c1af5427SAnton Mikaev 
553c1af5427SAnton Mikaev 	for (aq_nic->aq_vecs = 0; aq_nic->aq_vecs < aq_nic_cfg->vecs;
554c1af5427SAnton Mikaev 	     aq_nic->aq_vecs++) {
555c1af5427SAnton Mikaev 		aq_nic->aq_vec[aq_nic->aq_vecs] =
556c1af5427SAnton Mikaev 		    aq_vec_alloc(aq_nic, aq_nic->aq_vecs, aq_nic_cfg);
557c1af5427SAnton Mikaev 		if (unlikely(!aq_nic->aq_vec[aq_nic->aq_vecs])) {
558c1af5427SAnton Mikaev 			err = -ENOMEM;
559c1af5427SAnton Mikaev 			goto err_exit;
560c1af5427SAnton Mikaev 		}
561c1af5427SAnton Mikaev 	}
562c1af5427SAnton Mikaev 	if (ndev_running)
56300f54e68SPetr Machata 		err = dev_open(ndev, NULL);
564c1af5427SAnton Mikaev 
565c1af5427SAnton Mikaev err_exit:
566c1af5427SAnton Mikaev 	return err;
567c1af5427SAnton Mikaev }
568c1af5427SAnton Mikaev 
569c5760d03SDavid VomLehn const struct ethtool_ops aq_ethtool_ops = {
570c5760d03SDavid VomLehn 	.get_link            = aq_ethtool_get_link,
571c5760d03SDavid VomLehn 	.get_regs_len        = aq_ethtool_get_regs_len,
572c5760d03SDavid VomLehn 	.get_regs            = aq_ethtool_get_regs,
573c5760d03SDavid VomLehn 	.get_drvinfo         = aq_ethtool_get_drvinfo,
574c5760d03SDavid VomLehn 	.get_strings         = aq_ethtool_get_strings,
575c5760d03SDavid VomLehn 	.get_rxfh_indir_size = aq_ethtool_get_rss_indir_size,
576a0da96c0SYana Esina 	.get_wol             = aq_ethtool_get_wol,
577a0da96c0SYana Esina 	.set_wol             = aq_ethtool_set_wol,
578b8d68b62SAnton Mikaev 	.nway_reset          = aq_ethtool_nway_reset,
579c1af5427SAnton Mikaev 	.get_ringparam       = aq_get_ringparam,
580c1af5427SAnton Mikaev 	.set_ringparam       = aq_set_ringparam,
58192ab6407SYana Esina 	.get_eee             = aq_ethtool_get_eee,
58292ab6407SYana Esina 	.set_eee             = aq_ethtool_set_eee,
583288551deSIgor Russkikh 	.get_pauseparam      = aq_ethtool_get_pauseparam,
584288551deSIgor Russkikh 	.set_pauseparam      = aq_ethtool_set_pauseparam,
585c5760d03SDavid VomLehn 	.get_rxfh_key_size   = aq_ethtool_get_rss_key_size,
586c5760d03SDavid VomLehn 	.get_rxfh            = aq_ethtool_get_rss,
58739163767SDmitry Bogdanov 	.set_rxfh            = aq_ethtool_set_rss,
588c5760d03SDavid VomLehn 	.get_rxnfc           = aq_ethtool_get_rxnfc,
5898d0bcb01SDmitry Bogdanov 	.set_rxnfc           = aq_ethtool_set_rxnfc,
590c5760d03SDavid VomLehn 	.get_sset_count      = aq_ethtool_get_sset_count,
591f8244ab5SPhilippe Reynes 	.get_ethtool_stats   = aq_ethtool_stats,
592f8244ab5SPhilippe Reynes 	.get_link_ksettings  = aq_ethtool_get_link_ksettings,
593f8244ab5SPhilippe Reynes 	.set_link_ksettings  = aq_ethtool_set_link_ksettings,
594b82ee71aSIgor Russkikh 	.get_coalesce	     = aq_ethtool_get_coalesce,
595b82ee71aSIgor Russkikh 	.set_coalesce	     = aq_ethtool_set_coalesce,
596c5760d03SDavid VomLehn };
597