Commit | Line | Data |
---|---|---|
4c352362 JK |
1 | /* |
2 | * Copyright (C) 2015 Netronome Systems, Inc. | |
3 | * | |
4 | * This software is dual licensed under the GNU General License Version 2, | |
5 | * June 1991 as shown in the file COPYING in the top-level directory of this | |
6 | * source tree or the BSD 2-Clause License provided below. You have the | |
7 | * option to license this software under the complete terms of either license. | |
8 | * | |
9 | * The BSD 2-Clause License: | |
10 | * | |
11 | * Redistribution and use in source and binary forms, with or | |
12 | * without modification, are permitted provided that the following | |
13 | * conditions are met: | |
14 | * | |
15 | * 1. Redistributions of source code must retain the above | |
16 | * copyright notice, this list of conditions and the following | |
17 | * disclaimer. | |
18 | * | |
19 | * 2. Redistributions in binary form must reproduce the above | |
20 | * copyright notice, this list of conditions and the following | |
21 | * disclaimer in the documentation and/or other materials | |
22 | * provided with the distribution. | |
23 | * | |
24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
28 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
29 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
30 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
31 | * SOFTWARE. | |
32 | */ | |
33 | ||
34 | /* | |
35 | * nfp_netvf_main.c | |
36 | * Netronome virtual function network device driver: Main entry point | |
37 | * Author: Jason McMullan <jason.mcmullan@netronome.com> | |
38 | * Rolf Neugebauer <rolf.neugebauer@netronome.com> | |
39 | */ | |
40 | ||
4c352362 JK |
41 | #include <linux/module.h> |
42 | #include <linux/kernel.h> | |
43 | #include <linux/init.h> | |
44 | #include <linux/etherdevice.h> | |
45 | ||
46 | #include "nfp_net_ctrl.h" | |
47 | #include "nfp_net.h" | |
48 | ||
49 | const char nfp_net_driver_name[] = "nfp_netvf"; | |
50 | const char nfp_net_driver_version[] = "0.1"; | |
51 | #define PCI_DEVICE_NFP6000VF 0x6003 | |
52 | static const struct pci_device_id nfp_netvf_pci_device_ids[] = { | |
53 | { PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_NFP6000VF, | |
54 | PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID, | |
55 | PCI_ANY_ID, 0, | |
56 | }, | |
57 | { 0, } /* Required last entry. */ | |
58 | }; | |
59 | MODULE_DEVICE_TABLE(pci, nfp_netvf_pci_device_ids); | |
60 | ||
61 | static void nfp_netvf_get_mac_addr(struct nfp_net *nn) | |
62 | { | |
63 | u8 mac_addr[ETH_ALEN]; | |
64 | ||
65 | put_unaligned_be32(nn_readl(nn, NFP_NET_CFG_MACADDR + 0), &mac_addr[0]); | |
66 | /* We can't do readw for NFP-3200 compatibility */ | |
67 | put_unaligned_be16(nn_readl(nn, NFP_NET_CFG_MACADDR + 4) >> 16, | |
68 | &mac_addr[4]); | |
69 | ||
70 | if (!is_valid_ether_addr(mac_addr)) { | |
71 | eth_hw_addr_random(nn->netdev); | |
72 | return; | |
73 | } | |
74 | ||
75 | ether_addr_copy(nn->netdev->dev_addr, mac_addr); | |
76 | ether_addr_copy(nn->netdev->perm_addr, mac_addr); | |
77 | } | |
78 | ||
79 | static int nfp_netvf_pci_probe(struct pci_dev *pdev, | |
80 | const struct pci_device_id *pci_id) | |
81 | { | |
82 | struct nfp_net_fw_version fw_ver; | |
83 | int max_tx_rings, max_rx_rings; | |
84 | u32 tx_bar_off, rx_bar_off; | |
85 | u32 tx_bar_sz, rx_bar_sz; | |
86 | int tx_bar_no, rx_bar_no; | |
87 | u8 __iomem *ctrl_bar; | |
88 | struct nfp_net *nn; | |
89 | int is_nfp3200; | |
90 | u32 startq; | |
91 | int stride; | |
92 | int err; | |
93 | ||
94 | err = pci_enable_device_mem(pdev); | |
95 | if (err) | |
96 | return err; | |
97 | ||
98 | err = pci_request_regions(pdev, nfp_net_driver_name); | |
99 | if (err) { | |
100 | dev_err(&pdev->dev, "Unable to allocate device memory.\n"); | |
101 | goto err_pci_disable; | |
102 | } | |
103 | ||
104 | switch (pdev->device) { | |
105 | case PCI_DEVICE_NFP6000VF: | |
106 | is_nfp3200 = 0; | |
107 | break; | |
108 | default: | |
109 | err = -ENODEV; | |
110 | goto err_pci_regions; | |
111 | } | |
112 | ||
113 | pci_set_master(pdev); | |
114 | ||
115 | err = dma_set_mask_and_coherent(&pdev->dev, | |
116 | DMA_BIT_MASK(NFP_NET_MAX_DMA_BITS)); | |
117 | if (err) | |
118 | goto err_pci_regions; | |
119 | ||
120 | /* Map the Control BAR. | |
121 | * | |
122 | * Irrespective of the advertised BAR size we only map the | |
123 | * first NFP_NET_CFG_BAR_SZ of the BAR. This keeps the code | |
124 | * the identical for PF and VF drivers. | |
125 | */ | |
796312cd | 126 | ctrl_bar = ioremap_nocache(pci_resource_start(pdev, NFP_NET_CTRL_BAR), |
4c352362 JK |
127 | NFP_NET_CFG_BAR_SZ); |
128 | if (!ctrl_bar) { | |
129 | dev_err(&pdev->dev, | |
796312cd | 130 | "Failed to map resource %d\n", NFP_NET_CTRL_BAR); |
4c352362 JK |
131 | err = -EIO; |
132 | goto err_pci_regions; | |
133 | } | |
134 | ||
135 | nfp_net_get_fw_version(&fw_ver, ctrl_bar); | |
313b345c | 136 | if (fw_ver.resv || fw_ver.class != NFP_NET_CFG_VERSION_CLASS_GENERIC) { |
4c352362 JK |
137 | dev_err(&pdev->dev, "Unknown Firmware ABI %d.%d.%d.%d\n", |
138 | fw_ver.resv, fw_ver.class, fw_ver.major, fw_ver.minor); | |
139 | err = -EINVAL; | |
140 | goto err_ctrl_unmap; | |
141 | } | |
142 | ||
143 | /* Determine stride */ | |
313b345c | 144 | if (nfp_net_fw_ver_eq(&fw_ver, 0, 0, 0, 1)) { |
4c352362 JK |
145 | stride = 2; |
146 | tx_bar_no = NFP_NET_Q0_BAR; | |
147 | rx_bar_no = NFP_NET_Q1_BAR; | |
148 | dev_warn(&pdev->dev, "OBSOLETE Firmware detected - VF isolation not available\n"); | |
149 | } else { | |
150 | switch (fw_ver.major) { | |
151 | case 1 ... 3: | |
152 | if (is_nfp3200) { | |
153 | stride = 2; | |
154 | tx_bar_no = NFP_NET_Q0_BAR; | |
155 | rx_bar_no = NFP_NET_Q1_BAR; | |
156 | } else { | |
157 | stride = 4; | |
158 | tx_bar_no = NFP_NET_Q0_BAR; | |
159 | rx_bar_no = tx_bar_no; | |
160 | } | |
161 | break; | |
162 | default: | |
163 | dev_err(&pdev->dev, "Unsupported Firmware ABI %d.%d.%d.%d\n", | |
164 | fw_ver.resv, fw_ver.class, | |
165 | fw_ver.major, fw_ver.minor); | |
166 | err = -EINVAL; | |
167 | goto err_ctrl_unmap; | |
168 | } | |
169 | } | |
170 | ||
171 | /* Find out how many rings are supported */ | |
172 | max_tx_rings = readl(ctrl_bar + NFP_NET_CFG_MAX_TXRINGS); | |
173 | max_rx_rings = readl(ctrl_bar + NFP_NET_CFG_MAX_RXRINGS); | |
174 | ||
175 | tx_bar_sz = NFP_QCP_QUEUE_ADDR_SZ * max_tx_rings * stride; | |
176 | rx_bar_sz = NFP_QCP_QUEUE_ADDR_SZ * max_rx_rings * stride; | |
177 | ||
178 | /* Sanity checks */ | |
179 | if (tx_bar_sz > pci_resource_len(pdev, tx_bar_no)) { | |
180 | dev_err(&pdev->dev, | |
181 | "TX BAR too small for number of TX rings. Adjusting\n"); | |
182 | tx_bar_sz = pci_resource_len(pdev, tx_bar_no); | |
183 | max_tx_rings = (tx_bar_sz / NFP_QCP_QUEUE_ADDR_SZ) / 2; | |
184 | } | |
185 | if (rx_bar_sz > pci_resource_len(pdev, rx_bar_no)) { | |
186 | dev_err(&pdev->dev, | |
187 | "RX BAR too small for number of RX rings. Adjusting\n"); | |
188 | rx_bar_sz = pci_resource_len(pdev, rx_bar_no); | |
189 | max_rx_rings = (rx_bar_sz / NFP_QCP_QUEUE_ADDR_SZ) / 2; | |
190 | } | |
191 | ||
192 | /* XXX Implement a workaround for THB-350 here. Ideally, we | |
193 | * have a different PCI ID for A rev VFs. | |
194 | */ | |
195 | switch (pdev->device) { | |
196 | case PCI_DEVICE_NFP6000VF: | |
197 | startq = readl(ctrl_bar + NFP_NET_CFG_START_TXQ); | |
198 | tx_bar_off = NFP_PCIE_QUEUE(startq); | |
199 | startq = readl(ctrl_bar + NFP_NET_CFG_START_RXQ); | |
200 | rx_bar_off = NFP_PCIE_QUEUE(startq); | |
201 | break; | |
202 | default: | |
203 | err = -ENODEV; | |
204 | goto err_ctrl_unmap; | |
205 | } | |
206 | ||
207 | /* Allocate and initialise the netdev */ | |
208 | nn = nfp_net_netdev_alloc(pdev, max_tx_rings, max_rx_rings); | |
209 | if (IS_ERR(nn)) { | |
210 | err = PTR_ERR(nn); | |
211 | goto err_ctrl_unmap; | |
212 | } | |
213 | ||
214 | nn->fw_ver = fw_ver; | |
215 | nn->ctrl_bar = ctrl_bar; | |
216 | nn->is_vf = 1; | |
217 | nn->is_nfp3200 = is_nfp3200; | |
218 | nn->stride_tx = stride; | |
219 | nn->stride_rx = stride; | |
220 | ||
221 | if (rx_bar_no == tx_bar_no) { | |
222 | u32 bar_off, bar_sz; | |
223 | resource_size_t map_addr; | |
224 | ||
225 | /* Make a single overlapping BAR mapping */ | |
226 | if (tx_bar_off < rx_bar_off) | |
227 | bar_off = tx_bar_off; | |
228 | else | |
229 | bar_off = rx_bar_off; | |
230 | ||
231 | if ((tx_bar_off + tx_bar_sz) > (rx_bar_off + rx_bar_sz)) | |
232 | bar_sz = (tx_bar_off + tx_bar_sz) - bar_off; | |
233 | else | |
234 | bar_sz = (rx_bar_off + rx_bar_sz) - bar_off; | |
235 | ||
236 | map_addr = pci_resource_start(pdev, tx_bar_no) + bar_off; | |
237 | nn->q_bar = ioremap_nocache(map_addr, bar_sz); | |
238 | if (!nn->q_bar) { | |
239 | nn_err(nn, "Failed to map resource %d\n", tx_bar_no); | |
240 | err = -EIO; | |
241 | goto err_netdev_free; | |
242 | } | |
243 | ||
244 | /* TX queues */ | |
245 | nn->tx_bar = nn->q_bar + (tx_bar_off - bar_off); | |
246 | /* RX queues */ | |
247 | nn->rx_bar = nn->q_bar + (rx_bar_off - bar_off); | |
248 | } else { | |
249 | resource_size_t map_addr; | |
250 | ||
251 | /* TX queues */ | |
252 | map_addr = pci_resource_start(pdev, tx_bar_no) + tx_bar_off; | |
253 | nn->tx_bar = ioremap_nocache(map_addr, tx_bar_sz); | |
254 | if (!nn->tx_bar) { | |
255 | nn_err(nn, "Failed to map resource %d\n", tx_bar_no); | |
256 | err = -EIO; | |
257 | goto err_netdev_free; | |
258 | } | |
259 | ||
260 | /* RX queues */ | |
261 | map_addr = pci_resource_start(pdev, rx_bar_no) + rx_bar_off; | |
262 | nn->rx_bar = ioremap_nocache(map_addr, rx_bar_sz); | |
263 | if (!nn->rx_bar) { | |
264 | nn_err(nn, "Failed to map resource %d\n", rx_bar_no); | |
265 | err = -EIO; | |
266 | goto err_unmap_tx; | |
267 | } | |
268 | } | |
269 | ||
270 | nfp_netvf_get_mac_addr(nn); | |
271 | ||
272 | err = nfp_net_irqs_alloc(nn); | |
273 | if (!err) { | |
274 | nn_warn(nn, "Unable to allocate MSI-X Vectors. Exiting\n"); | |
275 | err = -EIO; | |
276 | goto err_unmap_rx; | |
277 | } | |
278 | ||
279 | /* Get ME clock frequency from ctrl BAR | |
280 | * XXX for now frequency is hardcoded until we figure out how | |
281 | * to get the value from nfp-hwinfo into ctrl bar | |
282 | */ | |
283 | nn->me_freq_mhz = 1200; | |
284 | ||
285 | err = nfp_net_netdev_init(nn->netdev); | |
286 | if (err) | |
287 | goto err_irqs_disable; | |
288 | ||
289 | pci_set_drvdata(pdev, nn); | |
290 | ||
291 | nfp_net_info(nn); | |
292 | nfp_net_debugfs_adapter_add(nn); | |
293 | ||
294 | return 0; | |
295 | ||
296 | err_irqs_disable: | |
297 | nfp_net_irqs_disable(nn); | |
298 | err_unmap_rx: | |
299 | if (!nn->q_bar) | |
300 | iounmap(nn->rx_bar); | |
301 | err_unmap_tx: | |
302 | if (!nn->q_bar) | |
303 | iounmap(nn->tx_bar); | |
304 | else | |
305 | iounmap(nn->q_bar); | |
306 | err_netdev_free: | |
307 | pci_set_drvdata(pdev, NULL); | |
308 | nfp_net_netdev_free(nn); | |
309 | err_ctrl_unmap: | |
310 | iounmap(ctrl_bar); | |
311 | err_pci_regions: | |
312 | pci_release_regions(pdev); | |
313 | err_pci_disable: | |
314 | pci_disable_device(pdev); | |
315 | return err; | |
316 | } | |
317 | ||
318 | static void nfp_netvf_pci_remove(struct pci_dev *pdev) | |
319 | { | |
320 | struct nfp_net *nn = pci_get_drvdata(pdev); | |
321 | ||
322 | /* Note, the order is slightly different from above as we need | |
323 | * to keep the nn pointer around till we have freed everything. | |
324 | */ | |
325 | nfp_net_debugfs_adapter_del(nn); | |
326 | ||
327 | nfp_net_netdev_clean(nn->netdev); | |
328 | ||
329 | nfp_net_irqs_disable(nn); | |
330 | ||
331 | if (!nn->q_bar) { | |
332 | iounmap(nn->rx_bar); | |
333 | iounmap(nn->tx_bar); | |
334 | } else { | |
335 | iounmap(nn->q_bar); | |
336 | } | |
337 | iounmap(nn->ctrl_bar); | |
338 | ||
339 | pci_set_drvdata(pdev, NULL); | |
340 | ||
341 | nfp_net_netdev_free(nn); | |
342 | ||
343 | pci_release_regions(pdev); | |
344 | pci_disable_device(pdev); | |
345 | } | |
346 | ||
347 | static struct pci_driver nfp_netvf_pci_driver = { | |
348 | .name = nfp_net_driver_name, | |
349 | .id_table = nfp_netvf_pci_device_ids, | |
350 | .probe = nfp_netvf_pci_probe, | |
351 | .remove = nfp_netvf_pci_remove, | |
352 | }; | |
353 | ||
354 | static int __init nfp_netvf_init(void) | |
355 | { | |
356 | int err; | |
357 | ||
358 | pr_info("%s: NFP VF Network driver, Copyright (C) 2014-2015 Netronome Systems\n", | |
359 | nfp_net_driver_name); | |
360 | ||
361 | nfp_net_debugfs_create(); | |
362 | err = pci_register_driver(&nfp_netvf_pci_driver); | |
363 | if (err) { | |
364 | nfp_net_debugfs_destroy(); | |
365 | return err; | |
366 | } | |
367 | ||
368 | return 0; | |
369 | } | |
370 | ||
371 | static void __exit nfp_netvf_exit(void) | |
372 | { | |
373 | pci_unregister_driver(&nfp_netvf_pci_driver); | |
374 | nfp_net_debugfs_destroy(); | |
375 | } | |
376 | ||
377 | module_init(nfp_netvf_init); | |
378 | module_exit(nfp_netvf_exit); | |
379 | ||
380 | MODULE_AUTHOR("Netronome Systems <oss-drivers@netronome.com>"); | |
381 | MODULE_LICENSE("GPL"); | |
382 | MODULE_DESCRIPTION("NFP VF network device driver"); |