Alexander Duyck | b3890e3 | 2014-09-20 19:46:05 -0400 | [diff] [blame] | 1 | /* Intel Ethernet Switch Host Interface Driver |
| 2 | * Copyright(c) 2013 - 2014 Intel Corporation. |
| 3 | * |
| 4 | * This program is free software; you can redistribute it and/or modify it |
| 5 | * under the terms and conditions of the GNU General Public License, |
| 6 | * version 2, as published by the Free Software Foundation. |
| 7 | * |
| 8 | * This program is distributed in the hope it will be useful, but WITHOUT |
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 11 | * more details. |
| 12 | * |
| 13 | * The full GNU General Public License is included in this distribution in |
| 14 | * the file called "COPYING". |
| 15 | * |
| 16 | * Contact Information: |
| 17 | * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net> |
| 18 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 |
| 19 | */ |
| 20 | |
| 21 | #include <linux/module.h> |
| 22 | |
| 23 | #include "fm10k.h" |
| 24 | |
Alexander Duyck | 0e7b364 | 2014-09-20 19:48:10 -0400 | [diff] [blame] | 25 | static const struct fm10k_info *fm10k_info_tbl[] = { |
| 26 | [fm10k_device_pf] = &fm10k_pf_info, |
| 27 | }; |
| 28 | |
Alexander Duyck | b3890e3 | 2014-09-20 19:46:05 -0400 | [diff] [blame] | 29 | /** |
| 30 | * fm10k_pci_tbl - PCI Device ID Table |
| 31 | * |
| 32 | * Wildcard entries (PCI_ANY_ID) should come last |
| 33 | * Last entry must be all 0s |
| 34 | * |
| 35 | * { Vendor ID, Device ID, SubVendor ID, SubDevice ID, |
| 36 | * Class, Class Mask, private data (not used) } |
| 37 | */ |
| 38 | static const struct pci_device_id fm10k_pci_tbl[] = { |
Alexander Duyck | 0e7b364 | 2014-09-20 19:48:10 -0400 | [diff] [blame] | 39 | { PCI_VDEVICE(INTEL, FM10K_DEV_ID_PF), fm10k_device_pf }, |
Alexander Duyck | b3890e3 | 2014-09-20 19:46:05 -0400 | [diff] [blame] | 40 | /* required last entry */ |
| 41 | { 0, } |
| 42 | }; |
| 43 | MODULE_DEVICE_TABLE(pci, fm10k_pci_tbl); |
| 44 | |
Alexander Duyck | 04a5aef | 2014-09-20 19:46:45 -0400 | [diff] [blame] | 45 | u16 fm10k_read_pci_cfg_word(struct fm10k_hw *hw, u32 reg) |
| 46 | { |
| 47 | struct fm10k_intfc *interface = hw->back; |
| 48 | u16 value = 0; |
| 49 | |
| 50 | if (FM10K_REMOVED(hw->hw_addr)) |
| 51 | return ~value; |
| 52 | |
| 53 | pci_read_config_word(interface->pdev, reg, &value); |
| 54 | if (value == 0xFFFF) |
| 55 | fm10k_write_flush(hw); |
| 56 | |
| 57 | return value; |
| 58 | } |
| 59 | |
| 60 | u32 fm10k_read_reg(struct fm10k_hw *hw, int reg) |
| 61 | { |
| 62 | u32 __iomem *hw_addr = ACCESS_ONCE(hw->hw_addr); |
| 63 | u32 value = 0; |
| 64 | |
| 65 | if (FM10K_REMOVED(hw_addr)) |
| 66 | return ~value; |
| 67 | |
| 68 | value = readl(&hw_addr[reg]); |
Alexander Duyck | 0e7b364 | 2014-09-20 19:48:10 -0400 | [diff] [blame] | 69 | if (!(~value) && (!reg || !(~readl(hw_addr)))) { |
| 70 | struct fm10k_intfc *interface = hw->back; |
| 71 | struct net_device *netdev = interface->netdev; |
| 72 | |
Alexander Duyck | 04a5aef | 2014-09-20 19:46:45 -0400 | [diff] [blame] | 73 | hw->hw_addr = NULL; |
Alexander Duyck | 0e7b364 | 2014-09-20 19:48:10 -0400 | [diff] [blame] | 74 | netif_device_detach(netdev); |
| 75 | netdev_err(netdev, "PCIe link lost, device now detached\n"); |
| 76 | } |
Alexander Duyck | 04a5aef | 2014-09-20 19:46:45 -0400 | [diff] [blame] | 77 | |
| 78 | return value; |
| 79 | } |
| 80 | |
Alexander Duyck | 0e7b364 | 2014-09-20 19:48:10 -0400 | [diff] [blame] | 81 | static int fm10k_hw_ready(struct fm10k_intfc *interface) |
| 82 | { |
| 83 | struct fm10k_hw *hw = &interface->hw; |
| 84 | |
| 85 | fm10k_write_flush(hw); |
| 86 | |
| 87 | return FM10K_REMOVED(hw->hw_addr) ? -ENODEV : 0; |
| 88 | } |
| 89 | |
Alexander Duyck | 18283ca | 2014-09-20 19:48:51 -0400 | [diff] [blame] | 90 | static void fm10k_napi_enable_all(struct fm10k_intfc *interface) |
| 91 | { |
| 92 | struct fm10k_q_vector *q_vector; |
| 93 | int q_idx; |
| 94 | |
| 95 | for (q_idx = 0; q_idx < interface->num_q_vectors; q_idx++) { |
| 96 | q_vector = interface->q_vector[q_idx]; |
| 97 | napi_enable(&q_vector->napi); |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | static irqreturn_t fm10k_msix_clean_rings(int irq, void *data) |
| 102 | { |
| 103 | struct fm10k_q_vector *q_vector = data; |
| 104 | |
| 105 | if (q_vector->rx.count || q_vector->tx.count) |
| 106 | napi_schedule(&q_vector->napi); |
| 107 | |
| 108 | return IRQ_HANDLED; |
| 109 | } |
| 110 | |
| 111 | #define FM10K_ERR_MSG(type) case (type): error = #type; break |
| 112 | static void fm10k_print_fault(struct fm10k_intfc *interface, int type, |
| 113 | struct fm10k_fault *fault) |
| 114 | { |
| 115 | struct pci_dev *pdev = interface->pdev; |
| 116 | char *error; |
| 117 | |
| 118 | switch (type) { |
| 119 | case FM10K_PCA_FAULT: |
| 120 | switch (fault->type) { |
| 121 | default: |
| 122 | error = "Unknown PCA error"; |
| 123 | break; |
| 124 | FM10K_ERR_MSG(PCA_NO_FAULT); |
| 125 | FM10K_ERR_MSG(PCA_UNMAPPED_ADDR); |
| 126 | FM10K_ERR_MSG(PCA_BAD_QACCESS_PF); |
| 127 | FM10K_ERR_MSG(PCA_BAD_QACCESS_VF); |
| 128 | FM10K_ERR_MSG(PCA_MALICIOUS_REQ); |
| 129 | FM10K_ERR_MSG(PCA_POISONED_TLP); |
| 130 | FM10K_ERR_MSG(PCA_TLP_ABORT); |
| 131 | } |
| 132 | break; |
| 133 | case FM10K_THI_FAULT: |
| 134 | switch (fault->type) { |
| 135 | default: |
| 136 | error = "Unknown THI error"; |
| 137 | break; |
| 138 | FM10K_ERR_MSG(THI_NO_FAULT); |
| 139 | FM10K_ERR_MSG(THI_MAL_DIS_Q_FAULT); |
| 140 | } |
| 141 | break; |
| 142 | case FM10K_FUM_FAULT: |
| 143 | switch (fault->type) { |
| 144 | default: |
| 145 | error = "Unknown FUM error"; |
| 146 | break; |
| 147 | FM10K_ERR_MSG(FUM_NO_FAULT); |
| 148 | FM10K_ERR_MSG(FUM_UNMAPPED_ADDR); |
| 149 | FM10K_ERR_MSG(FUM_BAD_VF_QACCESS); |
| 150 | FM10K_ERR_MSG(FUM_ADD_DECODE_ERR); |
| 151 | FM10K_ERR_MSG(FUM_RO_ERROR); |
| 152 | FM10K_ERR_MSG(FUM_QPRC_CRC_ERROR); |
| 153 | FM10K_ERR_MSG(FUM_CSR_TIMEOUT); |
| 154 | FM10K_ERR_MSG(FUM_INVALID_TYPE); |
| 155 | FM10K_ERR_MSG(FUM_INVALID_LENGTH); |
| 156 | FM10K_ERR_MSG(FUM_INVALID_BE); |
| 157 | FM10K_ERR_MSG(FUM_INVALID_ALIGN); |
| 158 | } |
| 159 | break; |
| 160 | default: |
| 161 | error = "Undocumented fault"; |
| 162 | break; |
| 163 | } |
| 164 | |
| 165 | dev_warn(&pdev->dev, |
| 166 | "%s Address: 0x%llx SpecInfo: 0x%x Func: %02x.%0x\n", |
| 167 | error, fault->address, fault->specinfo, |
| 168 | PCI_SLOT(fault->func), PCI_FUNC(fault->func)); |
| 169 | } |
| 170 | |
| 171 | static void fm10k_report_fault(struct fm10k_intfc *interface, u32 eicr) |
| 172 | { |
| 173 | struct fm10k_hw *hw = &interface->hw; |
| 174 | struct fm10k_fault fault = { 0 }; |
| 175 | int type, err; |
| 176 | |
| 177 | for (eicr &= FM10K_EICR_FAULT_MASK, type = FM10K_PCA_FAULT; |
| 178 | eicr; |
| 179 | eicr >>= 1, type += FM10K_FAULT_SIZE) { |
| 180 | /* only check if there is an error reported */ |
| 181 | if (!(eicr & 0x1)) |
| 182 | continue; |
| 183 | |
| 184 | /* retrieve fault info */ |
| 185 | err = hw->mac.ops.get_fault(hw, type, &fault); |
| 186 | if (err) { |
| 187 | dev_err(&interface->pdev->dev, |
| 188 | "error reading fault\n"); |
| 189 | continue; |
| 190 | } |
| 191 | |
| 192 | fm10k_print_fault(interface, type, &fault); |
| 193 | } |
| 194 | } |
| 195 | |
| 196 | static void fm10k_reset_drop_on_empty(struct fm10k_intfc *interface, u32 eicr) |
| 197 | { |
| 198 | struct fm10k_hw *hw = &interface->hw; |
| 199 | const u32 rxdctl = FM10K_RXDCTL_WRITE_BACK_MIN_DELAY; |
| 200 | u32 maxholdq; |
| 201 | int q; |
| 202 | |
| 203 | if (!(eicr & FM10K_EICR_MAXHOLDTIME)) |
| 204 | return; |
| 205 | |
| 206 | maxholdq = fm10k_read_reg(hw, FM10K_MAXHOLDQ(7)); |
| 207 | if (maxholdq) |
| 208 | fm10k_write_reg(hw, FM10K_MAXHOLDQ(7), maxholdq); |
| 209 | for (q = 255;;) { |
| 210 | if (maxholdq & (1 << 31)) { |
| 211 | if (q < FM10K_MAX_QUEUES_PF) { |
| 212 | interface->rx_overrun_pf++; |
| 213 | fm10k_write_reg(hw, FM10K_RXDCTL(q), rxdctl); |
| 214 | } else { |
| 215 | interface->rx_overrun_vf++; |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | maxholdq *= 2; |
| 220 | if (!maxholdq) |
| 221 | q &= ~(32 - 1); |
| 222 | |
| 223 | if (!q) |
| 224 | break; |
| 225 | |
| 226 | if (q-- % 32) |
| 227 | continue; |
| 228 | |
| 229 | maxholdq = fm10k_read_reg(hw, FM10K_MAXHOLDQ(q / 32)); |
| 230 | if (maxholdq) |
| 231 | fm10k_write_reg(hw, FM10K_MAXHOLDQ(q / 32), maxholdq); |
| 232 | } |
| 233 | } |
| 234 | |
| 235 | static irqreturn_t fm10k_msix_mbx_pf(int irq, void *data) |
| 236 | { |
| 237 | struct fm10k_intfc *interface = data; |
| 238 | struct fm10k_hw *hw = &interface->hw; |
| 239 | struct fm10k_mbx_info *mbx = &hw->mbx; |
| 240 | u32 eicr; |
| 241 | |
| 242 | /* unmask any set bits related to this interrupt */ |
| 243 | eicr = fm10k_read_reg(hw, FM10K_EICR); |
| 244 | fm10k_write_reg(hw, FM10K_EICR, eicr & (FM10K_EICR_MAILBOX | |
| 245 | FM10K_EICR_SWITCHREADY | |
| 246 | FM10K_EICR_SWITCHNOTREADY)); |
| 247 | |
| 248 | /* report any faults found to the message log */ |
| 249 | fm10k_report_fault(interface, eicr); |
| 250 | |
| 251 | /* reset any queues disabled due to receiver overrun */ |
| 252 | fm10k_reset_drop_on_empty(interface, eicr); |
| 253 | |
| 254 | /* service mailboxes */ |
| 255 | if (fm10k_mbx_trylock(interface)) { |
| 256 | mbx->ops.process(hw, mbx); |
| 257 | fm10k_mbx_unlock(interface); |
| 258 | } |
| 259 | |
| 260 | /* re-enable mailbox interrupt and indicate 20us delay */ |
| 261 | fm10k_write_reg(hw, FM10K_ITR(FM10K_MBX_VECTOR), |
| 262 | FM10K_ITR_ENABLE | FM10K_MBX_INT_DELAY); |
| 263 | |
| 264 | return IRQ_HANDLED; |
| 265 | } |
| 266 | |
| 267 | void fm10k_mbx_free_irq(struct fm10k_intfc *interface) |
| 268 | { |
| 269 | struct msix_entry *entry = &interface->msix_entries[FM10K_MBX_VECTOR]; |
| 270 | struct fm10k_hw *hw = &interface->hw; |
| 271 | int itr_reg; |
| 272 | |
| 273 | /* disconnect the mailbox */ |
| 274 | hw->mbx.ops.disconnect(hw, &hw->mbx); |
| 275 | |
| 276 | /* disable Mailbox cause */ |
| 277 | if (hw->mac.type == fm10k_mac_pf) { |
| 278 | fm10k_write_reg(hw, FM10K_EIMR, |
| 279 | FM10K_EIMR_DISABLE(PCA_FAULT) | |
| 280 | FM10K_EIMR_DISABLE(FUM_FAULT) | |
| 281 | FM10K_EIMR_DISABLE(MAILBOX) | |
| 282 | FM10K_EIMR_DISABLE(SWITCHREADY) | |
| 283 | FM10K_EIMR_DISABLE(SWITCHNOTREADY) | |
| 284 | FM10K_EIMR_DISABLE(SRAMERROR) | |
| 285 | FM10K_EIMR_DISABLE(VFLR) | |
| 286 | FM10K_EIMR_DISABLE(MAXHOLDTIME)); |
| 287 | itr_reg = FM10K_ITR(FM10K_MBX_VECTOR); |
| 288 | } |
| 289 | |
| 290 | fm10k_write_reg(hw, itr_reg, FM10K_ITR_MASK_SET); |
| 291 | |
| 292 | free_irq(entry->vector, interface); |
| 293 | } |
| 294 | |
| 295 | /* generic error handler for mailbox issues */ |
| 296 | static s32 fm10k_mbx_error(struct fm10k_hw *hw, u32 **results, |
| 297 | struct fm10k_mbx_info *mbx) |
| 298 | { |
| 299 | struct fm10k_intfc *interface; |
| 300 | struct pci_dev *pdev; |
| 301 | |
| 302 | interface = container_of(hw, struct fm10k_intfc, hw); |
| 303 | pdev = interface->pdev; |
| 304 | |
| 305 | dev_err(&pdev->dev, "Unknown message ID %u\n", |
| 306 | **results & FM10K_TLV_ID_MASK); |
| 307 | |
| 308 | return 0; |
| 309 | } |
| 310 | |
| 311 | static s32 fm10k_lport_map(struct fm10k_hw *hw, u32 **results, |
| 312 | struct fm10k_mbx_info *mbx) |
| 313 | { |
| 314 | struct fm10k_intfc *interface; |
| 315 | u32 dglort_map = hw->mac.dglort_map; |
| 316 | s32 err; |
| 317 | |
| 318 | err = fm10k_msg_lport_map_pf(hw, results, mbx); |
| 319 | if (err) |
| 320 | return err; |
| 321 | |
| 322 | interface = container_of(hw, struct fm10k_intfc, hw); |
| 323 | |
| 324 | /* we need to reset if port count was just updated */ |
| 325 | if (dglort_map != hw->mac.dglort_map) |
| 326 | interface->flags |= FM10K_FLAG_RESET_REQUESTED; |
| 327 | |
| 328 | return 0; |
| 329 | } |
| 330 | |
| 331 | static s32 fm10k_update_pvid(struct fm10k_hw *hw, u32 **results, |
| 332 | struct fm10k_mbx_info *mbx) |
| 333 | { |
| 334 | struct fm10k_intfc *interface; |
| 335 | u16 glort, pvid; |
| 336 | u32 pvid_update; |
| 337 | s32 err; |
| 338 | |
| 339 | err = fm10k_tlv_attr_get_u32(results[FM10K_PF_ATTR_ID_UPDATE_PVID], |
| 340 | &pvid_update); |
| 341 | if (err) |
| 342 | return err; |
| 343 | |
| 344 | /* extract values from the pvid update */ |
| 345 | glort = FM10K_MSG_HDR_FIELD_GET(pvid_update, UPDATE_PVID_GLORT); |
| 346 | pvid = FM10K_MSG_HDR_FIELD_GET(pvid_update, UPDATE_PVID_PVID); |
| 347 | |
| 348 | /* if glort is not valid return error */ |
| 349 | if (!fm10k_glort_valid_pf(hw, glort)) |
| 350 | return FM10K_ERR_PARAM; |
| 351 | |
| 352 | /* verify VID is valid */ |
| 353 | if (pvid >= FM10K_VLAN_TABLE_VID_MAX) |
| 354 | return FM10K_ERR_PARAM; |
| 355 | |
| 356 | interface = container_of(hw, struct fm10k_intfc, hw); |
| 357 | |
| 358 | /* we need to reset if default VLAN was just updated */ |
| 359 | if (pvid != hw->mac.default_vid) |
| 360 | interface->flags |= FM10K_FLAG_RESET_REQUESTED; |
| 361 | |
| 362 | hw->mac.default_vid = pvid; |
| 363 | |
| 364 | return 0; |
| 365 | } |
| 366 | |
| 367 | static const struct fm10k_msg_data pf_mbx_data[] = { |
| 368 | FM10K_PF_MSG_ERR_HANDLER(XCAST_MODES, fm10k_msg_err_pf), |
| 369 | FM10K_PF_MSG_ERR_HANDLER(UPDATE_MAC_FWD_RULE, fm10k_msg_err_pf), |
| 370 | FM10K_PF_MSG_LPORT_MAP_HANDLER(fm10k_lport_map), |
| 371 | FM10K_PF_MSG_ERR_HANDLER(LPORT_CREATE, fm10k_msg_err_pf), |
| 372 | FM10K_PF_MSG_ERR_HANDLER(LPORT_DELETE, fm10k_msg_err_pf), |
| 373 | FM10K_PF_MSG_UPDATE_PVID_HANDLER(fm10k_update_pvid), |
| 374 | FM10K_TLV_MSG_ERROR_HANDLER(fm10k_mbx_error), |
| 375 | }; |
| 376 | |
| 377 | static int fm10k_mbx_request_irq_pf(struct fm10k_intfc *interface) |
| 378 | { |
| 379 | struct msix_entry *entry = &interface->msix_entries[FM10K_MBX_VECTOR]; |
| 380 | struct net_device *dev = interface->netdev; |
| 381 | struct fm10k_hw *hw = &interface->hw; |
| 382 | int err; |
| 383 | |
| 384 | /* Use timer0 for interrupt moderation on the mailbox */ |
| 385 | u32 mbx_itr = FM10K_INT_MAP_TIMER0 | entry->entry; |
| 386 | u32 other_itr = FM10K_INT_MAP_IMMEDIATE | entry->entry; |
| 387 | |
| 388 | /* register mailbox handlers */ |
| 389 | err = hw->mbx.ops.register_handlers(&hw->mbx, pf_mbx_data); |
| 390 | if (err) |
| 391 | return err; |
| 392 | |
| 393 | /* request the IRQ */ |
| 394 | err = request_irq(entry->vector, fm10k_msix_mbx_pf, 0, |
| 395 | dev->name, interface); |
| 396 | if (err) { |
| 397 | netif_err(interface, probe, dev, |
| 398 | "request_irq for msix_mbx failed: %d\n", err); |
| 399 | return err; |
| 400 | } |
| 401 | |
| 402 | /* Enable interrupts w/ no moderation for "other" interrupts */ |
| 403 | fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_PCIeFault), other_itr); |
| 404 | fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_SwitchUpDown), other_itr); |
| 405 | fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_SRAM), other_itr); |
| 406 | fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_MaxHoldTime), other_itr); |
| 407 | fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_VFLR), other_itr); |
| 408 | |
| 409 | /* Enable interrupts w/ moderation for mailbox */ |
| 410 | fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_Mailbox), mbx_itr); |
| 411 | |
| 412 | /* Enable individual interrupt causes */ |
| 413 | fm10k_write_reg(hw, FM10K_EIMR, FM10K_EIMR_ENABLE(PCA_FAULT) | |
| 414 | FM10K_EIMR_ENABLE(FUM_FAULT) | |
| 415 | FM10K_EIMR_ENABLE(MAILBOX) | |
| 416 | FM10K_EIMR_ENABLE(SWITCHREADY) | |
| 417 | FM10K_EIMR_ENABLE(SWITCHNOTREADY) | |
| 418 | FM10K_EIMR_ENABLE(SRAMERROR) | |
| 419 | FM10K_EIMR_ENABLE(VFLR) | |
| 420 | FM10K_EIMR_ENABLE(MAXHOLDTIME)); |
| 421 | |
| 422 | /* enable interrupt */ |
| 423 | fm10k_write_reg(hw, FM10K_ITR(entry->entry), FM10K_ITR_ENABLE); |
| 424 | |
| 425 | return 0; |
| 426 | } |
| 427 | |
| 428 | int fm10k_mbx_request_irq(struct fm10k_intfc *interface) |
| 429 | { |
| 430 | struct fm10k_hw *hw = &interface->hw; |
| 431 | int err; |
| 432 | |
| 433 | /* enable Mailbox cause */ |
| 434 | err = fm10k_mbx_request_irq_pf(interface); |
| 435 | |
| 436 | /* connect mailbox */ |
| 437 | if (!err) |
| 438 | err = hw->mbx.ops.connect(hw, &hw->mbx); |
| 439 | |
| 440 | return err; |
| 441 | } |
| 442 | |
| 443 | /** |
| 444 | * fm10k_qv_free_irq - release interrupts associated with queue vectors |
| 445 | * @interface: board private structure |
| 446 | * |
| 447 | * Release all interrupts associated with this interface |
| 448 | **/ |
| 449 | void fm10k_qv_free_irq(struct fm10k_intfc *interface) |
| 450 | { |
| 451 | int vector = interface->num_q_vectors; |
| 452 | struct fm10k_hw *hw = &interface->hw; |
| 453 | struct msix_entry *entry; |
| 454 | |
| 455 | entry = &interface->msix_entries[NON_Q_VECTORS(hw) + vector]; |
| 456 | |
| 457 | while (vector) { |
| 458 | struct fm10k_q_vector *q_vector; |
| 459 | |
| 460 | vector--; |
| 461 | entry--; |
| 462 | q_vector = interface->q_vector[vector]; |
| 463 | |
| 464 | if (!q_vector->tx.count && !q_vector->rx.count) |
| 465 | continue; |
| 466 | |
| 467 | /* disable interrupts */ |
| 468 | |
| 469 | writel(FM10K_ITR_MASK_SET, q_vector->itr); |
| 470 | |
| 471 | free_irq(entry->vector, q_vector); |
| 472 | } |
| 473 | } |
| 474 | |
| 475 | /** |
| 476 | * fm10k_qv_request_irq - initialize interrupts for queue vectors |
| 477 | * @interface: board private structure |
| 478 | * |
| 479 | * Attempts to configure interrupts using the best available |
| 480 | * capabilities of the hardware and kernel. |
| 481 | **/ |
| 482 | int fm10k_qv_request_irq(struct fm10k_intfc *interface) |
| 483 | { |
| 484 | struct net_device *dev = interface->netdev; |
| 485 | struct fm10k_hw *hw = &interface->hw; |
| 486 | struct msix_entry *entry; |
| 487 | int ri = 0, ti = 0; |
| 488 | int vector, err; |
| 489 | |
| 490 | entry = &interface->msix_entries[NON_Q_VECTORS(hw)]; |
| 491 | |
| 492 | for (vector = 0; vector < interface->num_q_vectors; vector++) { |
| 493 | struct fm10k_q_vector *q_vector = interface->q_vector[vector]; |
| 494 | |
| 495 | /* name the vector */ |
| 496 | if (q_vector->tx.count && q_vector->rx.count) { |
| 497 | snprintf(q_vector->name, sizeof(q_vector->name) - 1, |
| 498 | "%s-TxRx-%d", dev->name, ri++); |
| 499 | ti++; |
| 500 | } else if (q_vector->rx.count) { |
| 501 | snprintf(q_vector->name, sizeof(q_vector->name) - 1, |
| 502 | "%s-rx-%d", dev->name, ri++); |
| 503 | } else if (q_vector->tx.count) { |
| 504 | snprintf(q_vector->name, sizeof(q_vector->name) - 1, |
| 505 | "%s-tx-%d", dev->name, ti++); |
| 506 | } else { |
| 507 | /* skip this unused q_vector */ |
| 508 | continue; |
| 509 | } |
| 510 | |
| 511 | /* Assign ITR register to q_vector */ |
| 512 | q_vector->itr = &interface->uc_addr[FM10K_ITR(entry->entry)]; |
| 513 | |
| 514 | /* request the IRQ */ |
| 515 | err = request_irq(entry->vector, &fm10k_msix_clean_rings, 0, |
| 516 | q_vector->name, q_vector); |
| 517 | if (err) { |
| 518 | netif_err(interface, probe, dev, |
| 519 | "request_irq failed for MSIX interrupt Error: %d\n", |
| 520 | err); |
| 521 | goto err_out; |
| 522 | } |
| 523 | |
| 524 | /* Enable q_vector */ |
| 525 | writel(FM10K_ITR_ENABLE, q_vector->itr); |
| 526 | |
| 527 | entry++; |
| 528 | } |
| 529 | |
| 530 | return 0; |
| 531 | |
| 532 | err_out: |
| 533 | /* wind through the ring freeing all entries and vectors */ |
| 534 | while (vector) { |
| 535 | struct fm10k_q_vector *q_vector; |
| 536 | |
| 537 | entry--; |
| 538 | vector--; |
| 539 | q_vector = interface->q_vector[vector]; |
| 540 | |
| 541 | if (!q_vector->tx.count && !q_vector->rx.count) |
| 542 | continue; |
| 543 | |
| 544 | /* disable interrupts */ |
| 545 | |
| 546 | writel(FM10K_ITR_MASK_SET, q_vector->itr); |
| 547 | |
| 548 | free_irq(entry->vector, q_vector); |
| 549 | } |
| 550 | |
| 551 | return err; |
| 552 | } |
| 553 | |
Alexander Duyck | 504c5ea | 2014-09-20 19:48:29 -0400 | [diff] [blame] | 554 | void fm10k_up(struct fm10k_intfc *interface) |
| 555 | { |
| 556 | struct fm10k_hw *hw = &interface->hw; |
| 557 | |
| 558 | /* Enable Tx/Rx DMA */ |
| 559 | hw->mac.ops.start_hw(hw); |
| 560 | |
| 561 | /* configure interrupts */ |
| 562 | hw->mac.ops.update_int_moderator(hw); |
| 563 | |
| 564 | /* clear down bit to indicate we are ready to go */ |
| 565 | clear_bit(__FM10K_DOWN, &interface->state); |
| 566 | |
Alexander Duyck | 18283ca | 2014-09-20 19:48:51 -0400 | [diff] [blame] | 567 | /* enable polling cleanups */ |
| 568 | fm10k_napi_enable_all(interface); |
| 569 | |
Alexander Duyck | 504c5ea | 2014-09-20 19:48:29 -0400 | [diff] [blame] | 570 | /* re-establish Rx filters */ |
| 571 | fm10k_restore_rx_state(interface); |
| 572 | |
| 573 | /* enable transmits */ |
| 574 | netif_tx_start_all_queues(interface->netdev); |
| 575 | } |
| 576 | |
Alexander Duyck | 18283ca | 2014-09-20 19:48:51 -0400 | [diff] [blame] | 577 | static void fm10k_napi_disable_all(struct fm10k_intfc *interface) |
| 578 | { |
| 579 | struct fm10k_q_vector *q_vector; |
| 580 | int q_idx; |
| 581 | |
| 582 | for (q_idx = 0; q_idx < interface->num_q_vectors; q_idx++) { |
| 583 | q_vector = interface->q_vector[q_idx]; |
| 584 | napi_disable(&q_vector->napi); |
| 585 | } |
| 586 | } |
| 587 | |
Alexander Duyck | 504c5ea | 2014-09-20 19:48:29 -0400 | [diff] [blame] | 588 | void fm10k_down(struct fm10k_intfc *interface) |
| 589 | { |
| 590 | struct net_device *netdev = interface->netdev; |
| 591 | struct fm10k_hw *hw = &interface->hw; |
| 592 | |
| 593 | /* signal that we are down to the interrupt handler and service task */ |
| 594 | set_bit(__FM10K_DOWN, &interface->state); |
| 595 | |
| 596 | /* call carrier off first to avoid false dev_watchdog timeouts */ |
| 597 | netif_carrier_off(netdev); |
| 598 | |
| 599 | /* disable transmits */ |
| 600 | netif_tx_stop_all_queues(netdev); |
| 601 | netif_tx_disable(netdev); |
| 602 | |
| 603 | /* reset Rx filters */ |
| 604 | fm10k_reset_rx_state(interface); |
| 605 | |
| 606 | /* allow 10ms for device to quiesce */ |
| 607 | usleep_range(10000, 20000); |
| 608 | |
Alexander Duyck | 18283ca | 2014-09-20 19:48:51 -0400 | [diff] [blame] | 609 | /* disable polling routines */ |
| 610 | fm10k_napi_disable_all(interface); |
| 611 | |
Alexander Duyck | 504c5ea | 2014-09-20 19:48:29 -0400 | [diff] [blame] | 612 | /* Disable DMA engine for Tx/Rx */ |
| 613 | hw->mac.ops.stop_hw(hw); |
| 614 | } |
| 615 | |
Alexander Duyck | 0e7b364 | 2014-09-20 19:48:10 -0400 | [diff] [blame] | 616 | /** |
| 617 | * fm10k_sw_init - Initialize general software structures |
| 618 | * @interface: host interface private structure to initialize |
| 619 | * |
| 620 | * fm10k_sw_init initializes the interface private data structure. |
| 621 | * Fields are initialized based on PCI device information and |
| 622 | * OS network device settings (MTU size). |
| 623 | **/ |
| 624 | static int fm10k_sw_init(struct fm10k_intfc *interface, |
| 625 | const struct pci_device_id *ent) |
| 626 | { |
| 627 | static const u32 seed[FM10K_RSSRK_SIZE] = { 0xda565a6d, 0xc20e5b25, |
| 628 | 0x3d256741, 0xb08fa343, |
| 629 | 0xcb2bcad0, 0xb4307bae, |
| 630 | 0xa32dcb77, 0x0cf23080, |
| 631 | 0x3bb7426a, 0xfa01acbe }; |
| 632 | const struct fm10k_info *fi = fm10k_info_tbl[ent->driver_data]; |
| 633 | struct fm10k_hw *hw = &interface->hw; |
| 634 | struct pci_dev *pdev = interface->pdev; |
| 635 | struct net_device *netdev = interface->netdev; |
| 636 | unsigned int rss; |
| 637 | int err; |
| 638 | |
| 639 | /* initialize back pointer */ |
| 640 | hw->back = interface; |
| 641 | hw->hw_addr = interface->uc_addr; |
| 642 | |
| 643 | /* PCI config space info */ |
| 644 | hw->vendor_id = pdev->vendor; |
| 645 | hw->device_id = pdev->device; |
| 646 | hw->revision_id = pdev->revision; |
| 647 | hw->subsystem_vendor_id = pdev->subsystem_vendor; |
| 648 | hw->subsystem_device_id = pdev->subsystem_device; |
| 649 | |
| 650 | /* Setup hw api */ |
| 651 | memcpy(&hw->mac.ops, fi->mac_ops, sizeof(hw->mac.ops)); |
| 652 | hw->mac.type = fi->mac; |
| 653 | |
| 654 | /* Set common capability flags and settings */ |
| 655 | rss = min_t(int, FM10K_MAX_RSS_INDICES, num_online_cpus()); |
| 656 | interface->ring_feature[RING_F_RSS].limit = rss; |
| 657 | fi->get_invariants(hw); |
| 658 | |
| 659 | /* pick up the PCIe bus settings for reporting later */ |
| 660 | if (hw->mac.ops.get_bus_info) |
| 661 | hw->mac.ops.get_bus_info(hw); |
| 662 | |
| 663 | /* limit the usable DMA range */ |
| 664 | if (hw->mac.ops.set_dma_mask) |
| 665 | hw->mac.ops.set_dma_mask(hw, dma_get_mask(&pdev->dev)); |
| 666 | |
| 667 | /* update netdev with DMA restrictions */ |
| 668 | if (dma_get_mask(&pdev->dev) > DMA_BIT_MASK(32)) { |
| 669 | netdev->features |= NETIF_F_HIGHDMA; |
| 670 | netdev->vlan_features |= NETIF_F_HIGHDMA; |
| 671 | } |
| 672 | |
| 673 | /* reset and initialize the hardware so it is in a known state */ |
| 674 | err = hw->mac.ops.reset_hw(hw) ? : hw->mac.ops.init_hw(hw); |
| 675 | if (err) { |
| 676 | dev_err(&pdev->dev, "init_hw failed: %d\n", err); |
| 677 | return err; |
| 678 | } |
| 679 | |
| 680 | /* initialize hardware statistics */ |
| 681 | hw->mac.ops.update_hw_stats(hw, &interface->stats); |
| 682 | |
| 683 | /* Start with random Ethernet address */ |
| 684 | eth_random_addr(hw->mac.addr); |
| 685 | |
| 686 | /* Initialize MAC address from hardware */ |
| 687 | err = hw->mac.ops.read_mac_addr(hw); |
| 688 | if (err) { |
| 689 | dev_warn(&pdev->dev, |
| 690 | "Failed to obtain MAC address defaulting to random\n"); |
| 691 | /* tag address assignment as random */ |
| 692 | netdev->addr_assign_type |= NET_ADDR_RANDOM; |
| 693 | } |
| 694 | |
| 695 | memcpy(netdev->dev_addr, hw->mac.addr, netdev->addr_len); |
| 696 | memcpy(netdev->perm_addr, hw->mac.addr, netdev->addr_len); |
| 697 | |
| 698 | if (!is_valid_ether_addr(netdev->perm_addr)) { |
| 699 | dev_err(&pdev->dev, "Invalid MAC Address\n"); |
| 700 | return -EIO; |
| 701 | } |
| 702 | |
| 703 | /* Only the PF can support VXLAN and NVGRE offloads */ |
| 704 | if (hw->mac.type != fm10k_mac_pf) { |
| 705 | netdev->hw_enc_features = 0; |
| 706 | netdev->features &= ~NETIF_F_GSO_UDP_TUNNEL; |
| 707 | netdev->hw_features &= ~NETIF_F_GSO_UDP_TUNNEL; |
| 708 | } |
| 709 | |
Alexander Duyck | e27ef59 | 2014-09-20 19:49:03 -0400 | [diff] [blame^] | 710 | /* set default ring sizes */ |
| 711 | interface->tx_ring_count = FM10K_DEFAULT_TXD; |
| 712 | interface->rx_ring_count = FM10K_DEFAULT_RXD; |
| 713 | |
Alexander Duyck | 18283ca | 2014-09-20 19:48:51 -0400 | [diff] [blame] | 714 | /* set default interrupt moderation */ |
| 715 | interface->tx_itr = FM10K_ITR_10K; |
| 716 | interface->rx_itr = FM10K_ITR_ADAPTIVE | FM10K_ITR_20K; |
| 717 | |
Alexander Duyck | 0e7b364 | 2014-09-20 19:48:10 -0400 | [diff] [blame] | 718 | /* initialize vxlan_port list */ |
| 719 | INIT_LIST_HEAD(&interface->vxlan_port); |
| 720 | |
| 721 | /* initialize RSS key */ |
| 722 | memcpy(interface->rssrk, seed, sizeof(seed)); |
| 723 | |
| 724 | /* Start off interface as being down */ |
| 725 | set_bit(__FM10K_DOWN, &interface->state); |
| 726 | |
| 727 | return 0; |
| 728 | } |
| 729 | |
| 730 | static void fm10k_slot_warn(struct fm10k_intfc *interface) |
| 731 | { |
| 732 | struct device *dev = &interface->pdev->dev; |
| 733 | struct fm10k_hw *hw = &interface->hw; |
| 734 | |
| 735 | if (hw->mac.ops.is_slot_appropriate(hw)) |
| 736 | return; |
| 737 | |
| 738 | dev_warn(dev, |
| 739 | "For optimal performance, a %s %s slot is recommended.\n", |
| 740 | (hw->bus_caps.width == fm10k_bus_width_pcie_x1 ? "x1" : |
| 741 | hw->bus_caps.width == fm10k_bus_width_pcie_x4 ? "x4" : |
| 742 | "x8"), |
| 743 | (hw->bus_caps.speed == fm10k_bus_speed_2500 ? "2.5GT/s" : |
| 744 | hw->bus_caps.speed == fm10k_bus_speed_5000 ? "5.0GT/s" : |
| 745 | "8.0GT/s")); |
| 746 | dev_warn(dev, |
| 747 | "A slot with more lanes and/or higher speed is suggested.\n"); |
| 748 | } |
| 749 | |
Alexander Duyck | b3890e3 | 2014-09-20 19:46:05 -0400 | [diff] [blame] | 750 | /** |
| 751 | * fm10k_probe - Device Initialization Routine |
| 752 | * @pdev: PCI device information struct |
| 753 | * @ent: entry in fm10k_pci_tbl |
| 754 | * |
| 755 | * Returns 0 on success, negative on failure |
| 756 | * |
| 757 | * fm10k_probe initializes an interface identified by a pci_dev structure. |
| 758 | * The OS initialization, configuring of the interface private structure, |
| 759 | * and a hardware reset occur. |
| 760 | **/ |
| 761 | static int fm10k_probe(struct pci_dev *pdev, |
| 762 | const struct pci_device_id *ent) |
| 763 | { |
Alexander Duyck | 0e7b364 | 2014-09-20 19:48:10 -0400 | [diff] [blame] | 764 | struct net_device *netdev; |
| 765 | struct fm10k_intfc *interface; |
| 766 | struct fm10k_hw *hw; |
Alexander Duyck | b3890e3 | 2014-09-20 19:46:05 -0400 | [diff] [blame] | 767 | int err; |
| 768 | u64 dma_mask; |
| 769 | |
| 770 | err = pci_enable_device_mem(pdev); |
| 771 | if (err) |
| 772 | return err; |
| 773 | |
| 774 | /* By default fm10k only supports a 48 bit DMA mask */ |
| 775 | dma_mask = DMA_BIT_MASK(48) | dma_get_required_mask(&pdev->dev); |
| 776 | |
| 777 | if ((dma_mask <= DMA_BIT_MASK(32)) || |
| 778 | dma_set_mask_and_coherent(&pdev->dev, dma_mask)) { |
| 779 | dma_mask &= DMA_BIT_MASK(32); |
| 780 | |
| 781 | err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); |
| 782 | err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); |
| 783 | if (err) { |
| 784 | err = dma_set_coherent_mask(&pdev->dev, |
| 785 | DMA_BIT_MASK(32)); |
| 786 | if (err) { |
| 787 | dev_err(&pdev->dev, |
| 788 | "No usable DMA configuration, aborting\n"); |
| 789 | goto err_dma; |
| 790 | } |
| 791 | } |
| 792 | } |
| 793 | |
| 794 | err = pci_request_selected_regions(pdev, |
| 795 | pci_select_bars(pdev, |
| 796 | IORESOURCE_MEM), |
| 797 | fm10k_driver_name); |
| 798 | if (err) { |
| 799 | dev_err(&pdev->dev, |
| 800 | "pci_request_selected_regions failed 0x%x\n", err); |
| 801 | goto err_pci_reg; |
| 802 | } |
| 803 | |
| 804 | pci_set_master(pdev); |
| 805 | pci_save_state(pdev); |
| 806 | |
Alexander Duyck | 0e7b364 | 2014-09-20 19:48:10 -0400 | [diff] [blame] | 807 | netdev = fm10k_alloc_netdev(); |
| 808 | if (!netdev) { |
| 809 | err = -ENOMEM; |
| 810 | goto err_alloc_netdev; |
| 811 | } |
| 812 | |
| 813 | SET_NETDEV_DEV(netdev, &pdev->dev); |
| 814 | |
| 815 | interface = netdev_priv(netdev); |
| 816 | pci_set_drvdata(pdev, interface); |
| 817 | |
| 818 | interface->netdev = netdev; |
| 819 | interface->pdev = pdev; |
| 820 | hw = &interface->hw; |
| 821 | |
| 822 | interface->uc_addr = ioremap(pci_resource_start(pdev, 0), |
| 823 | FM10K_UC_ADDR_SIZE); |
| 824 | if (!interface->uc_addr) { |
| 825 | err = -EIO; |
| 826 | goto err_ioremap; |
| 827 | } |
| 828 | |
| 829 | err = fm10k_sw_init(interface, ent); |
| 830 | if (err) |
| 831 | goto err_sw_init; |
| 832 | |
Alexander Duyck | 18283ca | 2014-09-20 19:48:51 -0400 | [diff] [blame] | 833 | err = fm10k_init_queueing_scheme(interface); |
| 834 | if (err) |
| 835 | goto err_sw_init; |
| 836 | |
| 837 | err = fm10k_mbx_request_irq(interface); |
| 838 | if (err) |
| 839 | goto err_mbx_interrupt; |
| 840 | |
Alexander Duyck | 0e7b364 | 2014-09-20 19:48:10 -0400 | [diff] [blame] | 841 | /* final check of hardware state before registering the interface */ |
| 842 | err = fm10k_hw_ready(interface); |
| 843 | if (err) |
| 844 | goto err_register; |
| 845 | |
| 846 | err = register_netdev(netdev); |
| 847 | if (err) |
| 848 | goto err_register; |
| 849 | |
| 850 | /* carrier off reporting is important to ethtool even BEFORE open */ |
| 851 | netif_carrier_off(netdev); |
| 852 | |
| 853 | /* stop all the transmit queues from transmitting until link is up */ |
| 854 | netif_tx_stop_all_queues(netdev); |
| 855 | |
| 856 | /* print bus type/speed/width info */ |
| 857 | dev_info(&pdev->dev, "(PCI Express:%s Width: %s Payload: %s)\n", |
| 858 | (hw->bus.speed == fm10k_bus_speed_8000 ? "8.0GT/s" : |
| 859 | hw->bus.speed == fm10k_bus_speed_5000 ? "5.0GT/s" : |
| 860 | hw->bus.speed == fm10k_bus_speed_2500 ? "2.5GT/s" : |
| 861 | "Unknown"), |
| 862 | (hw->bus.width == fm10k_bus_width_pcie_x8 ? "x8" : |
| 863 | hw->bus.width == fm10k_bus_width_pcie_x4 ? "x4" : |
| 864 | hw->bus.width == fm10k_bus_width_pcie_x1 ? "x1" : |
| 865 | "Unknown"), |
| 866 | (hw->bus.payload == fm10k_bus_payload_128 ? "128B" : |
| 867 | hw->bus.payload == fm10k_bus_payload_256 ? "256B" : |
| 868 | hw->bus.payload == fm10k_bus_payload_512 ? "512B" : |
| 869 | "Unknown")); |
| 870 | |
| 871 | /* print warning for non-optimal configurations */ |
| 872 | fm10k_slot_warn(interface); |
| 873 | |
Alexander Duyck | b3890e3 | 2014-09-20 19:46:05 -0400 | [diff] [blame] | 874 | return 0; |
| 875 | |
Alexander Duyck | 0e7b364 | 2014-09-20 19:48:10 -0400 | [diff] [blame] | 876 | err_register: |
Alexander Duyck | 18283ca | 2014-09-20 19:48:51 -0400 | [diff] [blame] | 877 | fm10k_mbx_free_irq(interface); |
| 878 | err_mbx_interrupt: |
| 879 | fm10k_clear_queueing_scheme(interface); |
Alexander Duyck | 0e7b364 | 2014-09-20 19:48:10 -0400 | [diff] [blame] | 880 | err_sw_init: |
| 881 | iounmap(interface->uc_addr); |
| 882 | err_ioremap: |
| 883 | free_netdev(netdev); |
| 884 | err_alloc_netdev: |
| 885 | pci_release_selected_regions(pdev, |
| 886 | pci_select_bars(pdev, IORESOURCE_MEM)); |
Alexander Duyck | b3890e3 | 2014-09-20 19:46:05 -0400 | [diff] [blame] | 887 | err_pci_reg: |
| 888 | err_dma: |
| 889 | pci_disable_device(pdev); |
| 890 | return err; |
| 891 | } |
| 892 | |
| 893 | /** |
| 894 | * fm10k_remove - Device Removal Routine |
| 895 | * @pdev: PCI device information struct |
| 896 | * |
| 897 | * fm10k_remove is called by the PCI subsystem to alert the driver |
| 898 | * that it should release a PCI device. The could be caused by a |
| 899 | * Hot-Plug event, or because the driver is going to be removed from |
| 900 | * memory. |
| 901 | **/ |
| 902 | static void fm10k_remove(struct pci_dev *pdev) |
| 903 | { |
Alexander Duyck | 0e7b364 | 2014-09-20 19:48:10 -0400 | [diff] [blame] | 904 | struct fm10k_intfc *interface = pci_get_drvdata(pdev); |
| 905 | struct net_device *netdev = interface->netdev; |
| 906 | |
| 907 | /* free netdev, this may bounce the interrupts due to setup_tc */ |
| 908 | if (netdev->reg_state == NETREG_REGISTERED) |
| 909 | unregister_netdev(netdev); |
| 910 | |
Alexander Duyck | 18283ca | 2014-09-20 19:48:51 -0400 | [diff] [blame] | 911 | /* disable mailbox interrupt */ |
| 912 | fm10k_mbx_free_irq(interface); |
| 913 | |
| 914 | /* free interrupts */ |
| 915 | fm10k_clear_queueing_scheme(interface); |
| 916 | |
Alexander Duyck | 0e7b364 | 2014-09-20 19:48:10 -0400 | [diff] [blame] | 917 | iounmap(interface->uc_addr); |
| 918 | |
| 919 | free_netdev(netdev); |
| 920 | |
Alexander Duyck | b3890e3 | 2014-09-20 19:46:05 -0400 | [diff] [blame] | 921 | pci_release_selected_regions(pdev, |
| 922 | pci_select_bars(pdev, IORESOURCE_MEM)); |
| 923 | |
| 924 | pci_disable_device(pdev); |
| 925 | } |
| 926 | |
| 927 | static struct pci_driver fm10k_driver = { |
| 928 | .name = fm10k_driver_name, |
| 929 | .id_table = fm10k_pci_tbl, |
| 930 | .probe = fm10k_probe, |
| 931 | .remove = fm10k_remove, |
| 932 | }; |
| 933 | |
| 934 | /** |
| 935 | * fm10k_register_pci_driver - register driver interface |
| 936 | * |
| 937 | * This funciton is called on module load in order to register the driver. |
| 938 | **/ |
| 939 | int fm10k_register_pci_driver(void) |
| 940 | { |
| 941 | return pci_register_driver(&fm10k_driver); |
| 942 | } |
| 943 | |
| 944 | /** |
| 945 | * fm10k_unregister_pci_driver - unregister driver interface |
| 946 | * |
| 947 | * This funciton is called on module unload in order to remove the driver. |
| 948 | **/ |
| 949 | void fm10k_unregister_pci_driver(void) |
| 950 | { |
| 951 | pci_unregister_driver(&fm10k_driver); |
| 952 | } |