]>
Commit | Line | Data |
---|---|---|
75a6faf6 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
97bde5c4 DV |
2 | /* |
3 | * aQuantia Corporation Network Driver | |
4 | * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved | |
97bde5c4 DV |
5 | */ |
6 | ||
7 | /* File aq_main.c: Main file for aQuantia Linux driver. */ | |
8 | ||
9 | #include "aq_main.h" | |
10 | #include "aq_nic.h" | |
11 | #include "aq_pci_func.h" | |
12 | #include "aq_ethtool.h" | |
8d0bcb01 | 13 | #include "aq_filters.h" |
97bde5c4 DV |
14 | |
15 | #include <linux/netdevice.h> | |
16 | #include <linux/module.h> | |
17 | ||
97bde5c4 DV |
18 | MODULE_LICENSE("GPL v2"); |
19 | MODULE_VERSION(AQ_CFG_DRV_VERSION); | |
20 | MODULE_AUTHOR(AQ_CFG_DRV_AUTHOR); | |
21 | MODULE_DESCRIPTION(AQ_CFG_DRV_DESC); | |
22 | ||
44bec4b3 | 23 | static const char aq_ndev_driver_name[] = AQ_CFG_DRV_NAME; |
58608082 | 24 | |
5b97b0d1 IR |
25 | static const struct net_device_ops aq_ndev_ops; |
26 | ||
58608082 ND |
27 | static struct workqueue_struct *aq_ndev_wq; |
28 | ||
29 | void aq_ndev_schedule_work(struct work_struct *work) | |
30 | { | |
31 | queue_work(aq_ndev_wq, work); | |
32 | } | |
33 | ||
5b97b0d1 | 34 | struct net_device *aq_ndev_alloc(void) |
97bde5c4 | 35 | { |
5b97b0d1 IR |
36 | struct net_device *ndev = NULL; |
37 | struct aq_nic_s *aq_nic = NULL; | |
97bde5c4 | 38 | |
5b97b0d1 IR |
39 | ndev = alloc_etherdev_mq(sizeof(struct aq_nic_s), AQ_CFG_VECS_MAX); |
40 | if (!ndev) | |
41 | return NULL; | |
97bde5c4 | 42 | |
5b97b0d1 IR |
43 | aq_nic = netdev_priv(ndev); |
44 | aq_nic->ndev = ndev; | |
45 | ndev->netdev_ops = &aq_ndev_ops; | |
46 | ndev->ethtool_ops = &aq_ethtool_ops; | |
47 | ||
48 | return ndev; | |
97bde5c4 DV |
49 | } |
50 | ||
51 | static int aq_ndev_open(struct net_device *ndev) | |
52 | { | |
97bde5c4 | 53 | int err = 0; |
23ee07ad | 54 | struct aq_nic_s *aq_nic = netdev_priv(ndev); |
97bde5c4 | 55 | |
97bde5c4 DV |
56 | err = aq_nic_init(aq_nic); |
57 | if (err < 0) | |
58 | goto err_exit; | |
8d0bcb01 DB |
59 | |
60 | err = aq_reapply_rxnfc_all_rules(aq_nic); | |
61 | if (err < 0) | |
62 | goto err_exit; | |
63 | ||
c2ef057e DB |
64 | err = aq_filters_vlans_update(aq_nic); |
65 | if (err < 0) | |
66 | goto err_exit; | |
67 | ||
97bde5c4 | 68 | err = aq_nic_start(aq_nic); |
9a08b107 NR |
69 | if (err < 0) { |
70 | aq_nic_stop(aq_nic); | |
97bde5c4 | 71 | goto err_exit; |
9a08b107 | 72 | } |
97bde5c4 DV |
73 | |
74 | err_exit: | |
75 | if (err < 0) | |
76 | aq_nic_deinit(aq_nic); | |
77 | return err; | |
78 | } | |
79 | ||
80 | static int aq_ndev_close(struct net_device *ndev) | |
81 | { | |
82 | int err = 0; | |
83 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
84 | ||
85 | err = aq_nic_stop(aq_nic); | |
86 | if (err < 0) | |
87 | goto err_exit; | |
88 | aq_nic_deinit(aq_nic); | |
97bde5c4 DV |
89 | |
90 | err_exit: | |
91 | return err; | |
92 | } | |
93 | ||
94 | static int aq_ndev_start_xmit(struct sk_buff *skb, struct net_device *ndev) | |
95 | { | |
96 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
97bde5c4 | 97 | |
362f37b2 | 98 | return aq_nic_xmit(aq_nic, skb); |
97bde5c4 DV |
99 | } |
100 | ||
101 | static int aq_ndev_change_mtu(struct net_device *ndev, int new_mtu) | |
102 | { | |
103 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
5513e164 | 104 | int err = aq_nic_set_mtu(aq_nic, new_mtu + ETH_HLEN); |
97bde5c4 | 105 | |
97bde5c4 DV |
106 | if (err < 0) |
107 | goto err_exit; | |
bc9ab923 | 108 | ndev->mtu = new_mtu; |
97bde5c4 | 109 | |
97bde5c4 DV |
110 | err_exit: |
111 | return err; | |
112 | } | |
113 | ||
114 | static int aq_ndev_set_features(struct net_device *ndev, | |
115 | netdev_features_t features) | |
116 | { | |
04f207fb IR |
117 | bool is_vlan_rx_strip = !!(features & NETIF_F_HW_VLAN_CTAG_RX); |
118 | bool is_vlan_tx_insert = !!(features & NETIF_F_HW_VLAN_CTAG_TX); | |
97bde5c4 | 119 | struct aq_nic_s *aq_nic = netdev_priv(ndev); |
04f207fb IR |
120 | bool need_ndev_restart = false; |
121 | struct aq_nic_cfg_s *aq_cfg; | |
97bde5c4 | 122 | bool is_lro = false; |
bbb67a44 DB |
123 | int err = 0; |
124 | ||
04f207fb IR |
125 | aq_cfg = aq_nic_get_cfg(aq_nic); |
126 | ||
8d0bcb01 DB |
127 | if (!(features & NETIF_F_NTUPLE)) { |
128 | if (aq_nic->ndev->features & NETIF_F_NTUPLE) { | |
129 | err = aq_clear_rxnfc_all_rules(aq_nic); | |
130 | if (unlikely(err)) | |
131 | goto err_exit; | |
132 | } | |
133 | } | |
7975d2af DB |
134 | if (!(features & NETIF_F_HW_VLAN_CTAG_FILTER)) { |
135 | if (aq_nic->ndev->features & NETIF_F_HW_VLAN_CTAG_FILTER) { | |
136 | err = aq_filters_vlan_offload_off(aq_nic); | |
137 | if (unlikely(err)) | |
138 | goto err_exit; | |
139 | } | |
140 | } | |
8d0bcb01 | 141 | |
bbb67a44 | 142 | aq_cfg->features = features; |
97bde5c4 | 143 | |
bbb67a44 | 144 | if (aq_cfg->aq_hw_caps->hw_features & NETIF_F_LRO) { |
97bde5c4 DV |
145 | is_lro = features & NETIF_F_LRO; |
146 | ||
147 | if (aq_cfg->is_lro != is_lro) { | |
148 | aq_cfg->is_lro = is_lro; | |
04f207fb | 149 | need_ndev_restart = true; |
97bde5c4 DV |
150 | } |
151 | } | |
04f207fb IR |
152 | |
153 | if ((aq_nic->ndev->features ^ features) & NETIF_F_RXCSUM) { | |
bbb67a44 DB |
154 | err = aq_nic->aq_hw_ops->hw_set_offload(aq_nic->aq_hw, |
155 | aq_cfg); | |
97bde5c4 | 156 | |
04f207fb IR |
157 | if (unlikely(err)) |
158 | goto err_exit; | |
159 | } | |
160 | ||
161 | if (aq_cfg->is_vlan_rx_strip != is_vlan_rx_strip) { | |
162 | aq_cfg->is_vlan_rx_strip = is_vlan_rx_strip; | |
163 | need_ndev_restart = true; | |
164 | } | |
165 | if (aq_cfg->is_vlan_tx_insert != is_vlan_tx_insert) { | |
166 | aq_cfg->is_vlan_tx_insert = is_vlan_tx_insert; | |
167 | need_ndev_restart = true; | |
168 | } | |
169 | ||
170 | if (need_ndev_restart && netif_running(ndev)) { | |
171 | aq_ndev_close(ndev); | |
172 | aq_ndev_open(ndev); | |
173 | } | |
174 | ||
8d0bcb01 | 175 | err_exit: |
bbb67a44 | 176 | return err; |
97bde5c4 DV |
177 | } |
178 | ||
179 | static int aq_ndev_set_mac_address(struct net_device *ndev, void *addr) | |
180 | { | |
181 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
182 | int err = 0; | |
183 | ||
184 | err = eth_mac_addr(ndev, addr); | |
185 | if (err < 0) | |
186 | goto err_exit; | |
187 | err = aq_nic_set_mac(aq_nic, ndev); | |
188 | if (err < 0) | |
189 | goto err_exit; | |
190 | ||
191 | err_exit: | |
192 | return err; | |
193 | } | |
194 | ||
195 | static void aq_ndev_set_multicast_settings(struct net_device *ndev) | |
196 | { | |
197 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
97bde5c4 | 198 | |
9f051db5 | 199 | (void)aq_nic_set_multicast_list(aq_nic, ndev); |
97bde5c4 DV |
200 | } |
201 | ||
7975d2af DB |
202 | static int aq_ndo_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, |
203 | u16 vid) | |
204 | { | |
205 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
206 | ||
207 | if (!aq_nic->aq_hw_ops->hw_filter_vlan_set) | |
208 | return -EOPNOTSUPP; | |
209 | ||
210 | set_bit(vid, aq_nic->active_vlans); | |
211 | ||
212 | return aq_filters_vlans_update(aq_nic); | |
213 | } | |
214 | ||
215 | static int aq_ndo_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, | |
216 | u16 vid) | |
217 | { | |
218 | struct aq_nic_s *aq_nic = netdev_priv(ndev); | |
219 | ||
220 | if (!aq_nic->aq_hw_ops->hw_filter_vlan_set) | |
221 | return -EOPNOTSUPP; | |
222 | ||
223 | clear_bit(vid, aq_nic->active_vlans); | |
224 | ||
225 | if (-ENOENT == aq_del_fvlan_by_vlan(aq_nic, vid)) | |
226 | return aq_filters_vlans_update(aq_nic); | |
227 | ||
228 | return 0; | |
229 | } | |
230 | ||
97bde5c4 DV |
231 | static const struct net_device_ops aq_ndev_ops = { |
232 | .ndo_open = aq_ndev_open, | |
233 | .ndo_stop = aq_ndev_close, | |
234 | .ndo_start_xmit = aq_ndev_start_xmit, | |
235 | .ndo_set_rx_mode = aq_ndev_set_multicast_settings, | |
236 | .ndo_change_mtu = aq_ndev_change_mtu, | |
237 | .ndo_set_mac_address = aq_ndev_set_mac_address, | |
7975d2af DB |
238 | .ndo_set_features = aq_ndev_set_features, |
239 | .ndo_vlan_rx_add_vid = aq_ndo_vlan_rx_add_vid, | |
240 | .ndo_vlan_rx_kill_vid = aq_ndo_vlan_rx_kill_vid, | |
97bde5c4 | 241 | }; |
58608082 ND |
242 | |
243 | static int __init aq_ndev_init_module(void) | |
244 | { | |
245 | int ret; | |
246 | ||
247 | aq_ndev_wq = create_singlethread_workqueue(aq_ndev_driver_name); | |
248 | if (!aq_ndev_wq) { | |
249 | pr_err("Failed to create workqueue\n"); | |
250 | return -ENOMEM; | |
251 | } | |
252 | ||
253 | ret = aq_pci_func_register_driver(); | |
254 | if (ret) { | |
255 | destroy_workqueue(aq_ndev_wq); | |
256 | return ret; | |
257 | } | |
258 | ||
259 | return 0; | |
260 | } | |
261 | ||
262 | static void __exit aq_ndev_exit_module(void) | |
263 | { | |
264 | aq_pci_func_unregister_driver(); | |
265 | ||
266 | if (aq_ndev_wq) { | |
267 | destroy_workqueue(aq_ndev_wq); | |
268 | aq_ndev_wq = NULL; | |
269 | } | |
270 | } | |
271 | ||
272 | module_init(aq_ndev_init_module); | |
273 | module_exit(aq_ndev_exit_module); |