1 /*
2  * aQuantia Corporation Network Driver
3  * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms and conditions of the GNU General Public License,
7  * version 2, as published by the Free Software Foundation.
8  */
9 
10 /* File aq_ethtool.c: Definition of ethertool related functions. */
11 
12 #include "aq_ethtool.h"
13 #include "aq_nic.h"
14 
15 static void aq_ethtool_get_regs(struct net_device *ndev,
16 				struct ethtool_regs *regs, void *p)
17 {
18 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
19 	u32 regs_count = aq_nic_get_regs_count(aq_nic);
20 
21 	memset(p, 0, regs_count * sizeof(u32));
22 	aq_nic_get_regs(aq_nic, regs, p);
23 }
24 
25 static int aq_ethtool_get_regs_len(struct net_device *ndev)
26 {
27 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
28 	u32 regs_count = aq_nic_get_regs_count(aq_nic);
29 
30 	return regs_count * sizeof(u32);
31 }
32 
33 static u32 aq_ethtool_get_link(struct net_device *ndev)
34 {
35 	return ethtool_op_get_link(ndev);
36 }
37 
38 static int aq_ethtool_get_link_ksettings(struct net_device *ndev,
39 					 struct ethtool_link_ksettings *cmd)
40 {
41 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
42 
43 	aq_nic_get_link_ksettings(aq_nic, cmd);
44 	cmd->base.speed = netif_carrier_ok(ndev) ?
45 				aq_nic_get_link_speed(aq_nic) : 0U;
46 
47 	return 0;
48 }
49 
50 static int
51 aq_ethtool_set_link_ksettings(struct net_device *ndev,
52 			      const struct ethtool_link_ksettings *cmd)
53 {
54 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
55 
56 	return aq_nic_set_link_ksettings(aq_nic, cmd);
57 }
58 
59 static const char aq_ethtool_stat_names[][ETH_GSTRING_LEN] = {
60 	"InPackets",
61 	"InUCast",
62 	"InMCast",
63 	"InBCast",
64 	"InErrors",
65 	"OutPackets",
66 	"OutUCast",
67 	"OutMCast",
68 	"OutBCast",
69 	"InUCastOctets",
70 	"OutUCastOctets",
71 	"InMCastOctets",
72 	"OutMCastOctets",
73 	"InBCastOctets",
74 	"OutBCastOctets",
75 	"InOctets",
76 	"OutOctets",
77 	"InPacketsDma",
78 	"OutPacketsDma",
79 	"InOctetsDma",
80 	"OutOctetsDma",
81 	"InDroppedDma",
82 };
83 
84 static const char aq_ethtool_queue_stat_names[][ETH_GSTRING_LEN] = {
85 	"Queue[%d] InPackets",
86 	"Queue[%d] OutPackets",
87 	"Queue[%d] Restarts",
88 	"Queue[%d] InJumboPackets",
89 	"Queue[%d] InLroPackets",
90 	"Queue[%d] InErrors",
91 };
92 
93 static void aq_ethtool_stats(struct net_device *ndev,
94 			     struct ethtool_stats *stats, u64 *data)
95 {
96 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
97 	struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
98 
99 	memset(data, 0, (ARRAY_SIZE(aq_ethtool_stat_names) +
100 				ARRAY_SIZE(aq_ethtool_queue_stat_names) *
101 				cfg->vecs) * sizeof(u64));
102 	aq_nic_get_stats(aq_nic, data);
103 }
104 
105 static void aq_ethtool_get_drvinfo(struct net_device *ndev,
106 				   struct ethtool_drvinfo *drvinfo)
107 {
108 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
109 	struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
110 	struct pci_dev *pdev = to_pci_dev(ndev->dev.parent);
111 	u32 firmware_version = aq_nic_get_fw_version(aq_nic);
112 	u32 regs_count = aq_nic_get_regs_count(aq_nic);
113 
114 	strlcat(drvinfo->driver, AQ_CFG_DRV_NAME, sizeof(drvinfo->driver));
115 	strlcat(drvinfo->version, AQ_CFG_DRV_VERSION, sizeof(drvinfo->version));
116 
117 	snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
118 		 "%u.%u.%u", firmware_version >> 24,
119 		 (firmware_version >> 16) & 0xFFU, firmware_version & 0xFFFFU);
120 
121 	strlcpy(drvinfo->bus_info, pdev ? pci_name(pdev) : "",
122 		sizeof(drvinfo->bus_info));
123 	drvinfo->n_stats = ARRAY_SIZE(aq_ethtool_stat_names) +
124 		cfg->vecs * ARRAY_SIZE(aq_ethtool_queue_stat_names);
125 	drvinfo->testinfo_len = 0;
126 	drvinfo->regdump_len = regs_count;
127 	drvinfo->eedump_len = 0;
128 }
129 
130 static void aq_ethtool_get_strings(struct net_device *ndev,
131 				   u32 stringset, u8 *data)
132 {
133 	int i, si;
134 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
135 	struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
136 	u8 *p = data;
137 
138 	if (stringset == ETH_SS_STATS) {
139 		memcpy(p, *aq_ethtool_stat_names,
140 		       sizeof(aq_ethtool_stat_names));
141 		p = p + sizeof(aq_ethtool_stat_names);
142 		for (i = 0; i < cfg->vecs; i++) {
143 			for (si = 0;
144 				si < ARRAY_SIZE(aq_ethtool_queue_stat_names);
145 				si++) {
146 				snprintf(p, ETH_GSTRING_LEN,
147 					 aq_ethtool_queue_stat_names[si], i);
148 				p += ETH_GSTRING_LEN;
149 			}
150 		}
151 	}
152 }
153 
154 static int aq_ethtool_get_sset_count(struct net_device *ndev, int stringset)
155 {
156 	int ret = 0;
157 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
158 	struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
159 
160 	switch (stringset) {
161 	case ETH_SS_STATS:
162 		ret = ARRAY_SIZE(aq_ethtool_stat_names) +
163 			cfg->vecs * ARRAY_SIZE(aq_ethtool_queue_stat_names);
164 		break;
165 	default:
166 		ret = -EOPNOTSUPP;
167 	}
168 	return ret;
169 }
170 
171 static u32 aq_ethtool_get_rss_indir_size(struct net_device *ndev)
172 {
173 	return AQ_CFG_RSS_INDIRECTION_TABLE_MAX;
174 }
175 
176 static u32 aq_ethtool_get_rss_key_size(struct net_device *ndev)
177 {
178 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
179 	struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
180 
181 	return sizeof(cfg->aq_rss.hash_secret_key);
182 }
183 
184 static int aq_ethtool_get_rss(struct net_device *ndev, u32 *indir, u8 *key,
185 			      u8 *hfunc)
186 {
187 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
188 	struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
189 	unsigned int i = 0U;
190 
191 	if (hfunc)
192 		*hfunc = ETH_RSS_HASH_TOP; /* Toeplitz */
193 	if (indir) {
194 		for (i = 0; i < AQ_CFG_RSS_INDIRECTION_TABLE_MAX; i++)
195 			indir[i] = cfg->aq_rss.indirection_table[i];
196 	}
197 	if (key)
198 		memcpy(key, cfg->aq_rss.hash_secret_key,
199 		       sizeof(cfg->aq_rss.hash_secret_key));
200 	return 0;
201 }
202 
203 static int aq_ethtool_get_rxnfc(struct net_device *ndev,
204 				struct ethtool_rxnfc *cmd,
205 				u32 *rule_locs)
206 {
207 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
208 	struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
209 	int err = 0;
210 
211 	switch (cmd->cmd) {
212 	case ETHTOOL_GRXRINGS:
213 		cmd->data = cfg->vecs;
214 		break;
215 
216 	default:
217 		err = -EOPNOTSUPP;
218 		break;
219 	}
220 
221 	return err;
222 }
223 
224 static int aq_ethtool_get_coalesce(struct net_device *ndev,
225 				   struct ethtool_coalesce *coal)
226 {
227 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
228 	struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
229 
230 	if (cfg->itr == AQ_CFG_INTERRUPT_MODERATION_ON ||
231 	    cfg->itr == AQ_CFG_INTERRUPT_MODERATION_AUTO) {
232 		coal->rx_coalesce_usecs = cfg->rx_itr;
233 		coal->tx_coalesce_usecs = cfg->tx_itr;
234 		coal->rx_max_coalesced_frames = 0;
235 		coal->tx_max_coalesced_frames = 0;
236 	} else {
237 		coal->rx_coalesce_usecs = 0;
238 		coal->tx_coalesce_usecs = 0;
239 		coal->rx_max_coalesced_frames = 1;
240 		coal->tx_max_coalesced_frames = 1;
241 	}
242 	return 0;
243 }
244 
245 static int aq_ethtool_set_coalesce(struct net_device *ndev,
246 				   struct ethtool_coalesce *coal)
247 {
248 	struct aq_nic_s *aq_nic = netdev_priv(ndev);
249 	struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
250 
251 	/* This is not yet supported
252 	 */
253 	if (coal->use_adaptive_rx_coalesce || coal->use_adaptive_tx_coalesce)
254 		return -EOPNOTSUPP;
255 
256 	/* Atlantic only supports timing based coalescing
257 	 */
258 	if (coal->rx_max_coalesced_frames > 1 ||
259 	    coal->rx_coalesce_usecs_irq ||
260 	    coal->rx_max_coalesced_frames_irq)
261 		return -EOPNOTSUPP;
262 
263 	if (coal->tx_max_coalesced_frames > 1 ||
264 	    coal->tx_coalesce_usecs_irq ||
265 	    coal->tx_max_coalesced_frames_irq)
266 		return -EOPNOTSUPP;
267 
268 	/* We do not support frame counting. Check this
269 	 */
270 	if (!(coal->rx_max_coalesced_frames == !coal->rx_coalesce_usecs))
271 		return -EOPNOTSUPP;
272 	if (!(coal->tx_max_coalesced_frames == !coal->tx_coalesce_usecs))
273 		return -EOPNOTSUPP;
274 
275 	if (coal->rx_coalesce_usecs > AQ_CFG_INTERRUPT_MODERATION_USEC_MAX ||
276 	    coal->tx_coalesce_usecs > AQ_CFG_INTERRUPT_MODERATION_USEC_MAX)
277 		return -EINVAL;
278 
279 	cfg->itr = AQ_CFG_INTERRUPT_MODERATION_ON;
280 
281 	cfg->rx_itr = coal->rx_coalesce_usecs;
282 	cfg->tx_itr = coal->tx_coalesce_usecs;
283 
284 	return aq_nic_update_interrupt_moderation_settings(aq_nic);
285 }
286 
287 const struct ethtool_ops aq_ethtool_ops = {
288 	.get_link            = aq_ethtool_get_link,
289 	.get_regs_len        = aq_ethtool_get_regs_len,
290 	.get_regs            = aq_ethtool_get_regs,
291 	.get_drvinfo         = aq_ethtool_get_drvinfo,
292 	.get_strings         = aq_ethtool_get_strings,
293 	.get_rxfh_indir_size = aq_ethtool_get_rss_indir_size,
294 	.get_rxfh_key_size   = aq_ethtool_get_rss_key_size,
295 	.get_rxfh            = aq_ethtool_get_rss,
296 	.get_rxnfc           = aq_ethtool_get_rxnfc,
297 	.get_sset_count      = aq_ethtool_get_sset_count,
298 	.get_ethtool_stats   = aq_ethtool_stats,
299 	.get_link_ksettings  = aq_ethtool_get_link_ksettings,
300 	.set_link_ksettings  = aq_ethtool_set_link_ksettings,
301 	.get_coalesce	     = aq_ethtool_get_coalesce,
302 	.set_coalesce	     = aq_ethtool_set_coalesce,
303 };
304