diff options
author | David VomLehn <vomlehn@texas.net> | 2017-01-24 01:09:18 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-01-24 15:03:41 -0500 |
commit | c5760d03d4ebd12f1b2d6723aec4b45327a1d2eb (patch) | |
tree | c12eca3c8803f1d333e741dc7f6bd3b2c3fd1b2e | |
parent | 753f4783be7b60b2c936c6985ddf42f8ce268ad9 (diff) |
net: ethernet: aquantia: Ethtool support
Add the driver interfaces required for support by the ethtool utility.
Signed-off-by: Alexander Loktionov <Alexander.Loktionov@aquantia.com>
Signed-off-by: Dmitrii Tarakanov <Dmitrii.Tarakanov@aquantia.com>
Signed-off-by: Pavel Belous <Pavel.Belous@aquantia.com>
Signed-off-by: Dmitry Bezrukov <Dmitry.Bezrukov@aquantia.com>
Signed-off-by: David M. VomLehn <vomlehn@texas.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c | 261 | ||||
-rw-r--r-- | drivers/net/ethernet/aquantia/atlantic/aq_ethtool.h | 19 |
2 files changed, 280 insertions, 0 deletions
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c b/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c new file mode 100644 index 000000000000..c5b025e0dc10 --- /dev/null +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c | |||
@@ -0,0 +1,261 @@ | |||
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_settings(struct net_device *ndev, | ||
39 | struct ethtool_cmd *cmd) | ||
40 | { | ||
41 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | ||
42 | |||
43 | aq_nic_get_link_settings(aq_nic, cmd); | ||
44 | ethtool_cmd_speed_set(cmd, netif_carrier_ok(ndev) ? | ||
45 | aq_nic_get_link_speed(aq_nic) : 0U); | ||
46 | |||
47 | return 0; | ||
48 | } | ||
49 | |||
50 | static int aq_ethtool_set_settings(struct net_device *ndev, | ||
51 | struct ethtool_cmd *cmd) | ||
52 | { | ||
53 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | ||
54 | |||
55 | return aq_nic_set_link_settings(aq_nic, cmd); | ||
56 | } | ||
57 | |||
58 | /* there "5U" is number of queue[#] stats lines (InPackets+...+InErrors) */ | ||
59 | static const unsigned int aq_ethtool_stat_queue_lines = 5U; | ||
60 | static const unsigned int aq_ethtool_stat_queue_chars = | ||
61 | 5U * ETH_GSTRING_LEN; | ||
62 | static const char aq_ethtool_stat_names[][ETH_GSTRING_LEN] = { | ||
63 | "InPackets", | ||
64 | "InUCast", | ||
65 | "InMCast", | ||
66 | "InBCast", | ||
67 | "InErrors", | ||
68 | "OutPackets", | ||
69 | "OutUCast", | ||
70 | "OutMCast", | ||
71 | "OutBCast", | ||
72 | "InUCastOctects", | ||
73 | "OutUCastOctects", | ||
74 | "InMCastOctects", | ||
75 | "OutMCastOctects", | ||
76 | "InBCastOctects", | ||
77 | "OutBCastOctects", | ||
78 | "InOctects", | ||
79 | "OutOctects", | ||
80 | "InPacketsDma", | ||
81 | "OutPacketsDma", | ||
82 | "InOctetsDma", | ||
83 | "OutOctetsDma", | ||
84 | "InDroppedDma", | ||
85 | "Queue[0] InPackets", | ||
86 | "Queue[0] OutPackets", | ||
87 | "Queue[0] InJumboPackets", | ||
88 | "Queue[0] InLroPackets", | ||
89 | "Queue[0] InErrors", | ||
90 | "Queue[1] InPackets", | ||
91 | "Queue[1] OutPackets", | ||
92 | "Queue[1] InJumboPackets", | ||
93 | "Queue[1] InLroPackets", | ||
94 | "Queue[1] InErrors", | ||
95 | "Queue[2] InPackets", | ||
96 | "Queue[2] OutPackets", | ||
97 | "Queue[2] InJumboPackets", | ||
98 | "Queue[2] InLroPackets", | ||
99 | "Queue[2] InErrors", | ||
100 | "Queue[3] InPackets", | ||
101 | "Queue[3] OutPackets", | ||
102 | "Queue[3] InJumboPackets", | ||
103 | "Queue[3] InLroPackets", | ||
104 | "Queue[3] InErrors", | ||
105 | "Queue[4] InPackets", | ||
106 | "Queue[4] OutPackets", | ||
107 | "Queue[4] InJumboPackets", | ||
108 | "Queue[4] InLroPackets", | ||
109 | "Queue[4] InErrors", | ||
110 | "Queue[5] InPackets", | ||
111 | "Queue[5] OutPackets", | ||
112 | "Queue[5] InJumboPackets", | ||
113 | "Queue[5] InLroPackets", | ||
114 | "Queue[5] InErrors", | ||
115 | "Queue[6] InPackets", | ||
116 | "Queue[6] OutPackets", | ||
117 | "Queue[6] InJumboPackets", | ||
118 | "Queue[6] InLroPackets", | ||
119 | "Queue[6] InErrors", | ||
120 | "Queue[7] InPackets", | ||
121 | "Queue[7] OutPackets", | ||
122 | "Queue[7] InJumboPackets", | ||
123 | "Queue[7] InLroPackets", | ||
124 | "Queue[7] InErrors", | ||
125 | }; | ||
126 | |||
127 | static void aq_ethtool_stats(struct net_device *ndev, | ||
128 | struct ethtool_stats *stats, u64 *data) | ||
129 | { | ||
130 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | ||
131 | |||
132 | /* ASSERT: Need add lines to aq_ethtool_stat_names if AQ_CFG_VECS_MAX > 8 */ | ||
133 | BUILD_BUG_ON(AQ_CFG_VECS_MAX > 8); | ||
134 | memset(data, 0, ARRAY_SIZE(aq_ethtool_stat_names) * sizeof(u64)); | ||
135 | aq_nic_get_stats(aq_nic, data); | ||
136 | } | ||
137 | |||
138 | static void aq_ethtool_get_drvinfo(struct net_device *ndev, | ||
139 | struct ethtool_drvinfo *drvinfo) | ||
140 | { | ||
141 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | ||
142 | struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); | ||
143 | struct pci_dev *pdev = to_pci_dev(ndev->dev.parent); | ||
144 | u32 firmware_version = aq_nic_get_fw_version(aq_nic); | ||
145 | u32 regs_count = aq_nic_get_regs_count(aq_nic); | ||
146 | |||
147 | strlcat(drvinfo->driver, AQ_CFG_DRV_NAME, sizeof(drvinfo->driver)); | ||
148 | strlcat(drvinfo->version, AQ_CFG_DRV_VERSION, sizeof(drvinfo->version)); | ||
149 | |||
150 | snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), | ||
151 | "%u.%u.%u", firmware_version >> 24, | ||
152 | (firmware_version >> 16) & 0xFFU, firmware_version & 0xFFFFU); | ||
153 | |||
154 | strlcpy(drvinfo->bus_info, pdev ? pci_name(pdev) : "", | ||
155 | sizeof(drvinfo->bus_info)); | ||
156 | drvinfo->n_stats = ARRAY_SIZE(aq_ethtool_stat_names) - | ||
157 | (AQ_CFG_VECS_MAX - cfg->vecs) * aq_ethtool_stat_queue_lines; | ||
158 | drvinfo->testinfo_len = 0; | ||
159 | drvinfo->regdump_len = regs_count; | ||
160 | drvinfo->eedump_len = 0; | ||
161 | } | ||
162 | |||
163 | static void aq_ethtool_get_strings(struct net_device *ndev, | ||
164 | u32 stringset, u8 *data) | ||
165 | { | ||
166 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | ||
167 | struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); | ||
168 | |||
169 | if (stringset == ETH_SS_STATS) | ||
170 | memcpy(data, *aq_ethtool_stat_names, | ||
171 | sizeof(aq_ethtool_stat_names) - | ||
172 | (AQ_CFG_VECS_MAX - cfg->vecs) * | ||
173 | aq_ethtool_stat_queue_chars); | ||
174 | } | ||
175 | |||
176 | static int aq_ethtool_get_sset_count(struct net_device *ndev, int stringset) | ||
177 | { | ||
178 | int ret = 0; | ||
179 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | ||
180 | struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); | ||
181 | |||
182 | switch (stringset) { | ||
183 | case ETH_SS_STATS: | ||
184 | ret = ARRAY_SIZE(aq_ethtool_stat_names) - | ||
185 | (AQ_CFG_VECS_MAX - cfg->vecs) * | ||
186 | aq_ethtool_stat_queue_lines; | ||
187 | break; | ||
188 | default: | ||
189 | ret = -EOPNOTSUPP; | ||
190 | } | ||
191 | return ret; | ||
192 | } | ||
193 | |||
194 | static u32 aq_ethtool_get_rss_indir_size(struct net_device *ndev) | ||
195 | { | ||
196 | return AQ_CFG_RSS_INDIRECTION_TABLE_MAX; | ||
197 | } | ||
198 | |||
199 | static u32 aq_ethtool_get_rss_key_size(struct net_device *ndev) | ||
200 | { | ||
201 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | ||
202 | struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); | ||
203 | |||
204 | return sizeof(cfg->aq_rss.hash_secret_key); | ||
205 | } | ||
206 | |||
207 | static int aq_ethtool_get_rss(struct net_device *ndev, u32 *indir, u8 *key, | ||
208 | u8 *hfunc) | ||
209 | { | ||
210 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | ||
211 | struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); | ||
212 | unsigned int i = 0U; | ||
213 | |||
214 | if (hfunc) | ||
215 | *hfunc = ETH_RSS_HASH_TOP; /* Toeplitz */ | ||
216 | if (indir) { | ||
217 | for (i = 0; i < AQ_CFG_RSS_INDIRECTION_TABLE_MAX; i++) | ||
218 | indir[i] = cfg->aq_rss.indirection_table[i]; | ||
219 | } | ||
220 | if (key) | ||
221 | memcpy(key, cfg->aq_rss.hash_secret_key, | ||
222 | sizeof(cfg->aq_rss.hash_secret_key)); | ||
223 | return 0; | ||
224 | } | ||
225 | |||
226 | static int aq_ethtool_get_rxnfc(struct net_device *ndev, | ||
227 | struct ethtool_rxnfc *cmd, | ||
228 | u32 *rule_locs) | ||
229 | { | ||
230 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | ||
231 | struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); | ||
232 | int err = 0; | ||
233 | |||
234 | switch (cmd->cmd) { | ||
235 | case ETHTOOL_GRXRINGS: | ||
236 | cmd->data = cfg->vecs; | ||
237 | break; | ||
238 | |||
239 | default: | ||
240 | err = -EOPNOTSUPP; | ||
241 | break; | ||
242 | } | ||
243 | |||
244 | return err; | ||
245 | } | ||
246 | |||
247 | const struct ethtool_ops aq_ethtool_ops = { | ||
248 | .get_link = aq_ethtool_get_link, | ||
249 | .get_regs_len = aq_ethtool_get_regs_len, | ||
250 | .get_regs = aq_ethtool_get_regs, | ||
251 | .get_settings = aq_ethtool_get_settings, | ||
252 | .set_settings = aq_ethtool_set_settings, | ||
253 | .get_drvinfo = aq_ethtool_get_drvinfo, | ||
254 | .get_strings = aq_ethtool_get_strings, | ||
255 | .get_rxfh_indir_size = aq_ethtool_get_rss_indir_size, | ||
256 | .get_rxfh_key_size = aq_ethtool_get_rss_key_size, | ||
257 | .get_rxfh = aq_ethtool_get_rss, | ||
258 | .get_rxnfc = aq_ethtool_get_rxnfc, | ||
259 | .get_sset_count = aq_ethtool_get_sset_count, | ||
260 | .get_ethtool_stats = aq_ethtool_stats | ||
261 | }; | ||
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.h b/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.h new file mode 100644 index 000000000000..21c126eeb5eb --- /dev/null +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.h | |||
@@ -0,0 +1,19 @@ | |||
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.h: Declaration of ethertool related functions. */ | ||
11 | |||
12 | #ifndef AQ_ETHTOOL_H | ||
13 | #define AQ_ETHTOOL_H | ||
14 | |||
15 | #include "aq_common.h" | ||
16 | |||
17 | extern const struct ethtool_ops aq_ethtool_ops; | ||
18 | |||
19 | #endif /* AQ_ETHTOOL_H */ | ||