Linux-libre 4.15.7-gnu
[librecmc/linux-libre.git] / drivers / net / ethernet / aquantia / atlantic / aq_ethtool.c
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 };