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) {
1415d8d84e9SIgor Russkikh 		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 
205c5760d03SDavid VomLehn static int aq_ethtool_get_rxnfc(struct net_device *ndev,
206c5760d03SDavid VomLehn 				struct ethtool_rxnfc *cmd,
207c5760d03SDavid VomLehn 				u32 *rule_locs)
208c5760d03SDavid VomLehn {
209c5760d03SDavid VomLehn 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
210c5760d03SDavid VomLehn 	struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
211c5760d03SDavid VomLehn 	int err = 0;
212c5760d03SDavid VomLehn 
213c5760d03SDavid VomLehn 	switch (cmd->cmd) {
214c5760d03SDavid VomLehn 	case ETHTOOL_GRXRINGS:
215c5760d03SDavid VomLehn 		cmd->data = cfg->vecs;
216c5760d03SDavid VomLehn 		break;
2178d0bcb01SDmitry Bogdanov 	case ETHTOOL_GRXCLSRLCNT:
2188d0bcb01SDmitry Bogdanov 		cmd->rule_cnt = aq_get_rxnfc_count_all_rules(aq_nic);
2198d0bcb01SDmitry Bogdanov 		break;
2208d0bcb01SDmitry Bogdanov 	case ETHTOOL_GRXCLSRULE:
2218d0bcb01SDmitry Bogdanov 		err = aq_get_rxnfc_rule(aq_nic, cmd);
2228d0bcb01SDmitry Bogdanov 		break;
2238d0bcb01SDmitry Bogdanov 	case ETHTOOL_GRXCLSRLALL:
2248d0bcb01SDmitry Bogdanov 		err = aq_get_rxnfc_all_rules(aq_nic, cmd, rule_locs);
2258d0bcb01SDmitry Bogdanov 		break;
2268d0bcb01SDmitry Bogdanov 	default:
2278d0bcb01SDmitry Bogdanov 		err = -EOPNOTSUPP;
2288d0bcb01SDmitry Bogdanov 		break;
2298d0bcb01SDmitry Bogdanov 	}
230c5760d03SDavid VomLehn 
2318d0bcb01SDmitry Bogdanov 	return err;
2328d0bcb01SDmitry Bogdanov }
2338d0bcb01SDmitry Bogdanov 
2348d0bcb01SDmitry Bogdanov static int aq_ethtool_set_rxnfc(struct net_device *ndev,
2358d0bcb01SDmitry Bogdanov 				struct ethtool_rxnfc *cmd)
2368d0bcb01SDmitry Bogdanov {
2378d0bcb01SDmitry Bogdanov 	int err = 0;
2388d0bcb01SDmitry Bogdanov 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
2398d0bcb01SDmitry Bogdanov 
2408d0bcb01SDmitry Bogdanov 	switch (cmd->cmd) {
2418d0bcb01SDmitry Bogdanov 	case ETHTOOL_SRXCLSRLINS:
2428d0bcb01SDmitry Bogdanov 		err = aq_add_rxnfc_rule(aq_nic, cmd);
2438d0bcb01SDmitry Bogdanov 		break;
2448d0bcb01SDmitry Bogdanov 	case ETHTOOL_SRXCLSRLDEL:
2458d0bcb01SDmitry Bogdanov 		err = aq_del_rxnfc_rule(aq_nic, cmd);
2468d0bcb01SDmitry Bogdanov 		break;
247c5760d03SDavid VomLehn 	default:
248c5760d03SDavid VomLehn 		err = -EOPNOTSUPP;
249c5760d03SDavid VomLehn 		break;
250c5760d03SDavid VomLehn 	}
251c5760d03SDavid VomLehn 
252c5760d03SDavid VomLehn 	return err;
253c5760d03SDavid VomLehn }
254c5760d03SDavid VomLehn 
2552660d226SWei Yongjun static int aq_ethtool_get_coalesce(struct net_device *ndev,
256b82ee71aSIgor Russkikh 				   struct ethtool_coalesce *coal)
257b82ee71aSIgor Russkikh {
258b82ee71aSIgor Russkikh 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
259b82ee71aSIgor Russkikh 	struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
260b82ee71aSIgor Russkikh 
261b82ee71aSIgor Russkikh 	if (cfg->itr == AQ_CFG_INTERRUPT_MODERATION_ON ||
262b82ee71aSIgor Russkikh 	    cfg->itr == AQ_CFG_INTERRUPT_MODERATION_AUTO) {
263b82ee71aSIgor Russkikh 		coal->rx_coalesce_usecs = cfg->rx_itr;
264b82ee71aSIgor Russkikh 		coal->tx_coalesce_usecs = cfg->tx_itr;
265b82ee71aSIgor Russkikh 		coal->rx_max_coalesced_frames = 0;
266b82ee71aSIgor Russkikh 		coal->tx_max_coalesced_frames = 0;
267b82ee71aSIgor Russkikh 	} else {
268b82ee71aSIgor Russkikh 		coal->rx_coalesce_usecs = 0;
269b82ee71aSIgor Russkikh 		coal->tx_coalesce_usecs = 0;
270b82ee71aSIgor Russkikh 		coal->rx_max_coalesced_frames = 1;
271b82ee71aSIgor Russkikh 		coal->tx_max_coalesced_frames = 1;
272b82ee71aSIgor Russkikh 	}
273b82ee71aSIgor Russkikh 	return 0;
274b82ee71aSIgor Russkikh }
275b82ee71aSIgor Russkikh 
2762660d226SWei Yongjun static int aq_ethtool_set_coalesce(struct net_device *ndev,
277b82ee71aSIgor Russkikh 				   struct ethtool_coalesce *coal)
278b82ee71aSIgor Russkikh {
279b82ee71aSIgor Russkikh 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
280b82ee71aSIgor Russkikh 	struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
281b82ee71aSIgor Russkikh 
282b82ee71aSIgor Russkikh 	/* This is not yet supported
283b82ee71aSIgor Russkikh 	 */
284b82ee71aSIgor Russkikh 	if (coal->use_adaptive_rx_coalesce || coal->use_adaptive_tx_coalesce)
285b82ee71aSIgor Russkikh 		return -EOPNOTSUPP;
286b82ee71aSIgor Russkikh 
287b82ee71aSIgor Russkikh 	/* Atlantic only supports timing based coalescing
288b82ee71aSIgor Russkikh 	 */
289b82ee71aSIgor Russkikh 	if (coal->rx_max_coalesced_frames > 1 ||
290b82ee71aSIgor Russkikh 	    coal->rx_coalesce_usecs_irq ||
291b82ee71aSIgor Russkikh 	    coal->rx_max_coalesced_frames_irq)
292b82ee71aSIgor Russkikh 		return -EOPNOTSUPP;
293b82ee71aSIgor Russkikh 
294b82ee71aSIgor Russkikh 	if (coal->tx_max_coalesced_frames > 1 ||
295b82ee71aSIgor Russkikh 	    coal->tx_coalesce_usecs_irq ||
296b82ee71aSIgor Russkikh 	    coal->tx_max_coalesced_frames_irq)
297b82ee71aSIgor Russkikh 		return -EOPNOTSUPP;
298b82ee71aSIgor Russkikh 
299b82ee71aSIgor Russkikh 	/* We do not support frame counting. Check this
300b82ee71aSIgor Russkikh 	 */
301b82ee71aSIgor Russkikh 	if (!(coal->rx_max_coalesced_frames == !coal->rx_coalesce_usecs))
302b82ee71aSIgor Russkikh 		return -EOPNOTSUPP;
303b82ee71aSIgor Russkikh 	if (!(coal->tx_max_coalesced_frames == !coal->tx_coalesce_usecs))
304b82ee71aSIgor Russkikh 		return -EOPNOTSUPP;
305b82ee71aSIgor Russkikh 
306b82ee71aSIgor Russkikh 	if (coal->rx_coalesce_usecs > AQ_CFG_INTERRUPT_MODERATION_USEC_MAX ||
307b82ee71aSIgor Russkikh 	    coal->tx_coalesce_usecs > AQ_CFG_INTERRUPT_MODERATION_USEC_MAX)
308b82ee71aSIgor Russkikh 		return -EINVAL;
309b82ee71aSIgor Russkikh 
310b82ee71aSIgor Russkikh 	cfg->itr = AQ_CFG_INTERRUPT_MODERATION_ON;
311b82ee71aSIgor Russkikh 
312b82ee71aSIgor Russkikh 	cfg->rx_itr = coal->rx_coalesce_usecs;
313b82ee71aSIgor Russkikh 	cfg->tx_itr = coal->tx_coalesce_usecs;
314b82ee71aSIgor Russkikh 
315b82ee71aSIgor Russkikh 	return aq_nic_update_interrupt_moderation_settings(aq_nic);
316b82ee71aSIgor Russkikh }
317b82ee71aSIgor Russkikh 
318a0da96c0SYana Esina static void aq_ethtool_get_wol(struct net_device *ndev,
319a0da96c0SYana Esina 			       struct ethtool_wolinfo *wol)
320a0da96c0SYana Esina {
321a0da96c0SYana Esina 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
322a0da96c0SYana Esina 	struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
323a0da96c0SYana Esina 
324a0da96c0SYana Esina 	wol->supported = WAKE_MAGIC;
325a0da96c0SYana Esina 	wol->wolopts = 0;
326a0da96c0SYana Esina 
327a0da96c0SYana Esina 	if (cfg->wol)
328a0da96c0SYana Esina 		wol->wolopts |= WAKE_MAGIC;
329a0da96c0SYana Esina }
330a0da96c0SYana Esina 
331a0da96c0SYana Esina static int aq_ethtool_set_wol(struct net_device *ndev,
332a0da96c0SYana Esina 			      struct ethtool_wolinfo *wol)
333a0da96c0SYana Esina {
334a0da96c0SYana Esina 	struct pci_dev *pdev = to_pci_dev(ndev->dev.parent);
335a0da96c0SYana Esina 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
336a0da96c0SYana Esina 	struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
337a0da96c0SYana Esina 	int err = 0;
338a0da96c0SYana Esina 
339a0da96c0SYana Esina 	if (wol->wolopts & WAKE_MAGIC)
340a0da96c0SYana Esina 		cfg->wol |= AQ_NIC_WOL_ENABLED;
341a0da96c0SYana Esina 	else
342a0da96c0SYana Esina 		cfg->wol &= ~AQ_NIC_WOL_ENABLED;
343a0da96c0SYana Esina 	err = device_set_wakeup_enable(&pdev->dev, wol->wolopts);
344a0da96c0SYana Esina 
345a0da96c0SYana Esina 	return err;
346a0da96c0SYana Esina }
347a0da96c0SYana Esina 
34892ab6407SYana Esina static enum hw_atl_fw2x_rate eee_mask_to_ethtool_mask(u32 speed)
34992ab6407SYana Esina {
35092ab6407SYana Esina 	u32 rate = 0;
35192ab6407SYana Esina 
35292ab6407SYana Esina 	if (speed & AQ_NIC_RATE_EEE_10G)
35392ab6407SYana Esina 		rate |= SUPPORTED_10000baseT_Full;
35492ab6407SYana Esina 
35592ab6407SYana Esina 	if (speed & AQ_NIC_RATE_EEE_2GS)
35692ab6407SYana Esina 		rate |= SUPPORTED_2500baseX_Full;
35792ab6407SYana Esina 
35892ab6407SYana Esina 	if (speed & AQ_NIC_RATE_EEE_1G)
35992ab6407SYana Esina 		rate |= SUPPORTED_1000baseT_Full;
36092ab6407SYana Esina 
36192ab6407SYana Esina 	return rate;
36292ab6407SYana Esina }
36392ab6407SYana Esina 
36492ab6407SYana Esina static int aq_ethtool_get_eee(struct net_device *ndev, struct ethtool_eee *eee)
36592ab6407SYana Esina {
36692ab6407SYana Esina 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
36792ab6407SYana Esina 	u32 rate, supported_rates;
36892ab6407SYana Esina 	int err = 0;
36992ab6407SYana Esina 
37092ab6407SYana Esina 	if (!aq_nic->aq_fw_ops->get_eee_rate)
37192ab6407SYana Esina 		return -EOPNOTSUPP;
37292ab6407SYana Esina 
37392ab6407SYana Esina 	err = aq_nic->aq_fw_ops->get_eee_rate(aq_nic->aq_hw, &rate,
37492ab6407SYana Esina 					      &supported_rates);
37592ab6407SYana Esina 	if (err < 0)
37692ab6407SYana Esina 		return err;
37792ab6407SYana Esina 
37892ab6407SYana Esina 	eee->supported = eee_mask_to_ethtool_mask(supported_rates);
37992ab6407SYana Esina 
38092ab6407SYana Esina 	if (aq_nic->aq_nic_cfg.eee_speeds)
38192ab6407SYana Esina 		eee->advertised = eee->supported;
38292ab6407SYana Esina 
38392ab6407SYana Esina 	eee->lp_advertised = eee_mask_to_ethtool_mask(rate);
38492ab6407SYana Esina 
38592ab6407SYana Esina 	eee->eee_enabled = !!eee->advertised;
38692ab6407SYana Esina 
38792ab6407SYana Esina 	eee->tx_lpi_enabled = eee->eee_enabled;
38892ab6407SYana Esina 	if (eee->advertised & eee->lp_advertised)
38992ab6407SYana Esina 		eee->eee_active = true;
39092ab6407SYana Esina 
39192ab6407SYana Esina 	return 0;
39292ab6407SYana Esina }
39392ab6407SYana Esina 
39492ab6407SYana Esina static int aq_ethtool_set_eee(struct net_device *ndev, struct ethtool_eee *eee)
39592ab6407SYana Esina {
39692ab6407SYana Esina 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
39792ab6407SYana Esina 	u32 rate, supported_rates;
39892ab6407SYana Esina 	struct aq_nic_cfg_s *cfg;
39992ab6407SYana Esina 	int err = 0;
40092ab6407SYana Esina 
40192ab6407SYana Esina 	cfg = aq_nic_get_cfg(aq_nic);
40292ab6407SYana Esina 
40392ab6407SYana Esina 	if (unlikely(!aq_nic->aq_fw_ops->get_eee_rate ||
40492ab6407SYana Esina 		     !aq_nic->aq_fw_ops->set_eee_rate))
40592ab6407SYana Esina 		return -EOPNOTSUPP;
40692ab6407SYana Esina 
40792ab6407SYana Esina 	err = aq_nic->aq_fw_ops->get_eee_rate(aq_nic->aq_hw, &rate,
40892ab6407SYana Esina 					      &supported_rates);
40992ab6407SYana Esina 	if (err < 0)
41092ab6407SYana Esina 		return err;
41192ab6407SYana Esina 
41292ab6407SYana Esina 	if (eee->eee_enabled) {
41392ab6407SYana Esina 		rate = supported_rates;
41492ab6407SYana Esina 		cfg->eee_speeds = rate;
41592ab6407SYana Esina 	} else {
41692ab6407SYana Esina 		rate = 0;
41792ab6407SYana Esina 		cfg->eee_speeds = 0;
41892ab6407SYana Esina 	}
41992ab6407SYana Esina 
42092ab6407SYana Esina 	return aq_nic->aq_fw_ops->set_eee_rate(aq_nic->aq_hw, rate);
42192ab6407SYana Esina }
42292ab6407SYana Esina 
423b8d68b62SAnton Mikaev static int aq_ethtool_nway_reset(struct net_device *ndev)
424b8d68b62SAnton Mikaev {
425b8d68b62SAnton Mikaev 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
426b8d68b62SAnton Mikaev 
427b8d68b62SAnton Mikaev 	if (unlikely(!aq_nic->aq_fw_ops->renegotiate))
428b8d68b62SAnton Mikaev 		return -EOPNOTSUPP;
429b8d68b62SAnton Mikaev 
430b8d68b62SAnton Mikaev 	if (netif_running(ndev))
431b8d68b62SAnton Mikaev 		return aq_nic->aq_fw_ops->renegotiate(aq_nic->aq_hw);
432b8d68b62SAnton Mikaev 
433b8d68b62SAnton Mikaev 	return 0;
434b8d68b62SAnton Mikaev }
435b8d68b62SAnton Mikaev 
436288551deSIgor Russkikh static void aq_ethtool_get_pauseparam(struct net_device *ndev,
437288551deSIgor Russkikh 				      struct ethtool_pauseparam *pause)
438288551deSIgor Russkikh {
439288551deSIgor Russkikh 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
44035e8e8b4SIgor Russkikh 	u32 fc = aq_nic->aq_nic_cfg.flow_control;
441288551deSIgor Russkikh 
442288551deSIgor Russkikh 	pause->autoneg = 0;
443288551deSIgor Russkikh 
44435e8e8b4SIgor Russkikh 	pause->rx_pause = !!(fc & AQ_NIC_FC_RX);
44535e8e8b4SIgor Russkikh 	pause->tx_pause = !!(fc & AQ_NIC_FC_TX);
44635e8e8b4SIgor Russkikh 
447288551deSIgor Russkikh }
448288551deSIgor Russkikh 
449288551deSIgor Russkikh static int aq_ethtool_set_pauseparam(struct net_device *ndev,
450288551deSIgor Russkikh 				     struct ethtool_pauseparam *pause)
451288551deSIgor Russkikh {
452288551deSIgor Russkikh 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
453288551deSIgor Russkikh 	int err = 0;
454288551deSIgor Russkikh 
455288551deSIgor Russkikh 	if (!aq_nic->aq_fw_ops->set_flow_control)
456288551deSIgor Russkikh 		return -EOPNOTSUPP;
457288551deSIgor Russkikh 
458288551deSIgor Russkikh 	if (pause->autoneg == AUTONEG_ENABLE)
459288551deSIgor Russkikh 		return -EOPNOTSUPP;
460288551deSIgor Russkikh 
461288551deSIgor Russkikh 	if (pause->rx_pause)
462288551deSIgor Russkikh 		aq_nic->aq_hw->aq_nic_cfg->flow_control |= AQ_NIC_FC_RX;
463288551deSIgor Russkikh 	else
464288551deSIgor Russkikh 		aq_nic->aq_hw->aq_nic_cfg->flow_control &= ~AQ_NIC_FC_RX;
465288551deSIgor Russkikh 
466288551deSIgor Russkikh 	if (pause->tx_pause)
467288551deSIgor Russkikh 		aq_nic->aq_hw->aq_nic_cfg->flow_control |= AQ_NIC_FC_TX;
468288551deSIgor Russkikh 	else
469288551deSIgor Russkikh 		aq_nic->aq_hw->aq_nic_cfg->flow_control &= ~AQ_NIC_FC_TX;
470288551deSIgor Russkikh 
471288551deSIgor Russkikh 	err = aq_nic->aq_fw_ops->set_flow_control(aq_nic->aq_hw);
472288551deSIgor Russkikh 
473288551deSIgor Russkikh 	return err;
474288551deSIgor Russkikh }
475288551deSIgor Russkikh 
476c1af5427SAnton Mikaev static void aq_get_ringparam(struct net_device *ndev,
477c1af5427SAnton Mikaev 			     struct ethtool_ringparam *ring)
478c1af5427SAnton Mikaev {
479c1af5427SAnton Mikaev 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
480c1af5427SAnton Mikaev 	struct aq_nic_cfg_s *aq_nic_cfg = aq_nic_get_cfg(aq_nic);
481c1af5427SAnton Mikaev 
482c1af5427SAnton Mikaev 	ring->rx_pending = aq_nic_cfg->rxds;
483c1af5427SAnton Mikaev 	ring->tx_pending = aq_nic_cfg->txds;
484c1af5427SAnton Mikaev 
485c1af5427SAnton Mikaev 	ring->rx_max_pending = aq_nic_cfg->aq_hw_caps->rxds_max;
486c1af5427SAnton Mikaev 	ring->tx_max_pending = aq_nic_cfg->aq_hw_caps->txds_max;
487c1af5427SAnton Mikaev }
488c1af5427SAnton Mikaev 
489c1af5427SAnton Mikaev static int aq_set_ringparam(struct net_device *ndev,
490c1af5427SAnton Mikaev 			    struct ethtool_ringparam *ring)
491c1af5427SAnton Mikaev {
492c1af5427SAnton Mikaev 	int err = 0;
493c1af5427SAnton Mikaev 	bool ndev_running = false;
494c1af5427SAnton Mikaev 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
495c1af5427SAnton Mikaev 	struct aq_nic_cfg_s *aq_nic_cfg = aq_nic_get_cfg(aq_nic);
496c1af5427SAnton Mikaev 	const struct aq_hw_caps_s *hw_caps = aq_nic_cfg->aq_hw_caps;
497c1af5427SAnton Mikaev 
498c1af5427SAnton Mikaev 	if (ring->rx_mini_pending || ring->rx_jumbo_pending) {
499c1af5427SAnton Mikaev 		err = -EOPNOTSUPP;
500c1af5427SAnton Mikaev 		goto err_exit;
501c1af5427SAnton Mikaev 	}
502c1af5427SAnton Mikaev 
503c1af5427SAnton Mikaev 	if (netif_running(ndev)) {
504c1af5427SAnton Mikaev 		ndev_running = true;
505c1af5427SAnton Mikaev 		dev_close(ndev);
506c1af5427SAnton Mikaev 	}
507c1af5427SAnton Mikaev 
508c1af5427SAnton Mikaev 	aq_nic_free_vectors(aq_nic);
509c1af5427SAnton Mikaev 
510c1af5427SAnton Mikaev 	aq_nic_cfg->rxds = max(ring->rx_pending, hw_caps->rxds_min);
511c1af5427SAnton Mikaev 	aq_nic_cfg->rxds = min(aq_nic_cfg->rxds, hw_caps->rxds_max);
512c1af5427SAnton Mikaev 	aq_nic_cfg->rxds = ALIGN(aq_nic_cfg->rxds, AQ_HW_RXD_MULTIPLE);
513c1af5427SAnton Mikaev 
514c1af5427SAnton Mikaev 	aq_nic_cfg->txds = max(ring->tx_pending, hw_caps->txds_min);
515c1af5427SAnton Mikaev 	aq_nic_cfg->txds = min(aq_nic_cfg->txds, hw_caps->txds_max);
516c1af5427SAnton Mikaev 	aq_nic_cfg->txds = ALIGN(aq_nic_cfg->txds, AQ_HW_TXD_MULTIPLE);
517c1af5427SAnton Mikaev 
518c1af5427SAnton Mikaev 	for (aq_nic->aq_vecs = 0; aq_nic->aq_vecs < aq_nic_cfg->vecs;
519c1af5427SAnton Mikaev 	     aq_nic->aq_vecs++) {
520c1af5427SAnton Mikaev 		aq_nic->aq_vec[aq_nic->aq_vecs] =
521c1af5427SAnton Mikaev 		    aq_vec_alloc(aq_nic, aq_nic->aq_vecs, aq_nic_cfg);
522c1af5427SAnton Mikaev 		if (unlikely(!aq_nic->aq_vec[aq_nic->aq_vecs])) {
523c1af5427SAnton Mikaev 			err = -ENOMEM;
524c1af5427SAnton Mikaev 			goto err_exit;
525c1af5427SAnton Mikaev 		}
526c1af5427SAnton Mikaev 	}
527c1af5427SAnton Mikaev 	if (ndev_running)
528*00f54e68SPetr Machata 		err = dev_open(ndev, NULL);
529c1af5427SAnton Mikaev 
530c1af5427SAnton Mikaev err_exit:
531c1af5427SAnton Mikaev 	return err;
532c1af5427SAnton Mikaev }
533c1af5427SAnton Mikaev 
534c5760d03SDavid VomLehn const struct ethtool_ops aq_ethtool_ops = {
535c5760d03SDavid VomLehn 	.get_link            = aq_ethtool_get_link,
536c5760d03SDavid VomLehn 	.get_regs_len        = aq_ethtool_get_regs_len,
537c5760d03SDavid VomLehn 	.get_regs            = aq_ethtool_get_regs,
538c5760d03SDavid VomLehn 	.get_drvinfo         = aq_ethtool_get_drvinfo,
539c5760d03SDavid VomLehn 	.get_strings         = aq_ethtool_get_strings,
540c5760d03SDavid VomLehn 	.get_rxfh_indir_size = aq_ethtool_get_rss_indir_size,
541a0da96c0SYana Esina 	.get_wol             = aq_ethtool_get_wol,
542a0da96c0SYana Esina 	.set_wol             = aq_ethtool_set_wol,
543b8d68b62SAnton Mikaev 	.nway_reset          = aq_ethtool_nway_reset,
544c1af5427SAnton Mikaev 	.get_ringparam       = aq_get_ringparam,
545c1af5427SAnton Mikaev 	.set_ringparam       = aq_set_ringparam,
54692ab6407SYana Esina 	.get_eee             = aq_ethtool_get_eee,
54792ab6407SYana Esina 	.set_eee             = aq_ethtool_set_eee,
548288551deSIgor Russkikh 	.get_pauseparam      = aq_ethtool_get_pauseparam,
549288551deSIgor Russkikh 	.set_pauseparam      = aq_ethtool_set_pauseparam,
550c5760d03SDavid VomLehn 	.get_rxfh_key_size   = aq_ethtool_get_rss_key_size,
551c5760d03SDavid VomLehn 	.get_rxfh            = aq_ethtool_get_rss,
552c5760d03SDavid VomLehn 	.get_rxnfc           = aq_ethtool_get_rxnfc,
5538d0bcb01SDmitry Bogdanov 	.set_rxnfc           = aq_ethtool_set_rxnfc,
554c5760d03SDavid VomLehn 	.get_sset_count      = aq_ethtool_get_sset_count,
555f8244ab5SPhilippe Reynes 	.get_ethtool_stats   = aq_ethtool_stats,
556f8244ab5SPhilippe Reynes 	.get_link_ksettings  = aq_ethtool_get_link_ksettings,
557f8244ab5SPhilippe Reynes 	.set_link_ksettings  = aq_ethtool_set_link_ksettings,
558b82ee71aSIgor Russkikh 	.get_coalesce	     = aq_ethtool_get_coalesce,
559b82ee71aSIgor Russkikh 	.set_coalesce	     = aq_ethtool_set_coalesce,
560c5760d03SDavid VomLehn };
561