|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
| 2 | +/* Copyright (c) Meta Platforms, Inc. and affiliates. */ |
| 3 | + |
| 4 | +#include <linux/init.h> |
| 5 | +#include <linux/module.h> |
| 6 | +#include <linux/pci.h> |
| 7 | +#include <linux/types.h> |
| 8 | + |
| 9 | +#include "fbnic.h" |
| 10 | +#include "fbnic_drvinfo.h" |
| 11 | + |
| 12 | +char fbnic_driver_name[] = DRV_NAME; |
| 13 | + |
| 14 | +MODULE_DESCRIPTION(DRV_SUMMARY); |
| 15 | +MODULE_LICENSE("GPL"); |
| 16 | + |
| 17 | +static const struct fbnic_info fbnic_asic_info = { |
| 18 | + .bar_mask = BIT(0) | BIT(4) |
| 19 | +}; |
| 20 | + |
| 21 | +static const struct fbnic_info *fbnic_info_tbl[] = { |
| 22 | + [fbnic_board_asic] = &fbnic_asic_info, |
| 23 | +}; |
| 24 | + |
| 25 | +static const struct pci_device_id fbnic_pci_tbl[] = { |
| 26 | + { PCI_DEVICE_DATA(META, FBNIC_ASIC, fbnic_board_asic) }, |
| 27 | + /* Required last entry */ |
| 28 | + {0, } |
| 29 | +}; |
| 30 | +MODULE_DEVICE_TABLE(pci, fbnic_pci_tbl); |
| 31 | + |
| 32 | +/** |
| 33 | + * fbnic_probe - Device Initialization Routine |
| 34 | + * @pdev: PCI device information struct |
| 35 | + * @ent: entry in fbnic_pci_tbl |
| 36 | + * |
| 37 | + * Initializes a PCI device identified by a pci_dev structure. |
| 38 | + * The OS initialization, configuring of the adapter private structure, |
| 39 | + * and a hardware reset occur. |
| 40 | + * |
| 41 | + * Return: 0 on success, negative on failure |
| 42 | + **/ |
| 43 | +static int fbnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) |
| 44 | +{ |
| 45 | + const struct fbnic_info *info = fbnic_info_tbl[ent->driver_data]; |
| 46 | + int err; |
| 47 | + |
| 48 | + if (pdev->error_state != pci_channel_io_normal) { |
| 49 | + dev_err(&pdev->dev, |
| 50 | + "PCI device still in an error state. Unable to load...\n"); |
| 51 | + return -EIO; |
| 52 | + } |
| 53 | + |
| 54 | + err = pcim_enable_device(pdev); |
| 55 | + if (err) { |
| 56 | + dev_err(&pdev->dev, "PCI enable device failed: %d\n", err); |
| 57 | + return err; |
| 58 | + } |
| 59 | + |
| 60 | + err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(46)); |
| 61 | + if (err) |
| 62 | + err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); |
| 63 | + if (err) { |
| 64 | + dev_err(&pdev->dev, "DMA configuration failed: %d\n", err); |
| 65 | + return err; |
| 66 | + } |
| 67 | + |
| 68 | + err = pcim_iomap_regions(pdev, info->bar_mask, fbnic_driver_name); |
| 69 | + if (err) { |
| 70 | + dev_err(&pdev->dev, |
| 71 | + "pci_request_selected_regions failed: %d\n", err); |
| 72 | + return err; |
| 73 | + } |
| 74 | + |
| 75 | + pci_set_master(pdev); |
| 76 | + pci_save_state(pdev); |
| 77 | + |
| 78 | + return 0; |
| 79 | +} |
| 80 | + |
| 81 | +/** |
| 82 | + * fbnic_remove - Device Removal Routine |
| 83 | + * @pdev: PCI device information struct |
| 84 | + * |
| 85 | + * Called by the PCI subsystem to alert the driver that it should release |
| 86 | + * a PCI device. The could be caused by a Hot-Plug event, or because the |
| 87 | + * driver is going to be removed from memory. |
| 88 | + **/ |
| 89 | +static void fbnic_remove(struct pci_dev *pdev) |
| 90 | +{ |
| 91 | + pci_disable_device(pdev); |
| 92 | +} |
| 93 | + |
| 94 | +static int fbnic_pm_suspend(struct device *dev) |
| 95 | +{ |
| 96 | + return 0; |
| 97 | +} |
| 98 | + |
| 99 | +static int __fbnic_pm_resume(struct device *dev) |
| 100 | +{ |
| 101 | + return 0; |
| 102 | +} |
| 103 | + |
| 104 | +static int __maybe_unused fbnic_pm_resume(struct device *dev) |
| 105 | +{ |
| 106 | + int err; |
| 107 | + |
| 108 | + err = __fbnic_pm_resume(dev); |
| 109 | + |
| 110 | + return err; |
| 111 | +} |
| 112 | + |
| 113 | +static const struct dev_pm_ops fbnic_pm_ops = { |
| 114 | + SET_SYSTEM_SLEEP_PM_OPS(fbnic_pm_suspend, fbnic_pm_resume) |
| 115 | +}; |
| 116 | + |
| 117 | +static void fbnic_shutdown(struct pci_dev *pdev) |
| 118 | +{ |
| 119 | + fbnic_pm_suspend(&pdev->dev); |
| 120 | +} |
| 121 | + |
| 122 | +static pci_ers_result_t fbnic_err_error_detected(struct pci_dev *pdev, |
| 123 | + pci_channel_state_t state) |
| 124 | +{ |
| 125 | + /* Disconnect device if failure is not recoverable via reset */ |
| 126 | + if (state == pci_channel_io_perm_failure) |
| 127 | + return PCI_ERS_RESULT_DISCONNECT; |
| 128 | + |
| 129 | + fbnic_pm_suspend(&pdev->dev); |
| 130 | + |
| 131 | + /* Request a slot reset */ |
| 132 | + return PCI_ERS_RESULT_NEED_RESET; |
| 133 | +} |
| 134 | + |
| 135 | +static pci_ers_result_t fbnic_err_slot_reset(struct pci_dev *pdev) |
| 136 | +{ |
| 137 | + pci_set_power_state(pdev, PCI_D0); |
| 138 | + pci_restore_state(pdev); |
| 139 | + pci_save_state(pdev); |
| 140 | + |
| 141 | + if (pci_enable_device_mem(pdev)) { |
| 142 | + dev_err(&pdev->dev, |
| 143 | + "Cannot re-enable PCI device after reset.\n"); |
| 144 | + return PCI_ERS_RESULT_DISCONNECT; |
| 145 | + } |
| 146 | + |
| 147 | + return PCI_ERS_RESULT_RECOVERED; |
| 148 | +} |
| 149 | + |
| 150 | +static void fbnic_err_resume(struct pci_dev *pdev) |
| 151 | +{ |
| 152 | +} |
| 153 | + |
| 154 | +static const struct pci_error_handlers fbnic_err_handler = { |
| 155 | + .error_detected = fbnic_err_error_detected, |
| 156 | + .slot_reset = fbnic_err_slot_reset, |
| 157 | + .resume = fbnic_err_resume, |
| 158 | +}; |
| 159 | + |
| 160 | +static struct pci_driver fbnic_driver = { |
| 161 | + .name = fbnic_driver_name, |
| 162 | + .id_table = fbnic_pci_tbl, |
| 163 | + .probe = fbnic_probe, |
| 164 | + .remove = fbnic_remove, |
| 165 | + .driver.pm = &fbnic_pm_ops, |
| 166 | + .shutdown = fbnic_shutdown, |
| 167 | + .err_handler = &fbnic_err_handler, |
| 168 | +}; |
| 169 | + |
| 170 | +/** |
| 171 | + * fbnic_init_module - Driver Registration Routine |
| 172 | + * |
| 173 | + * The first routine called when the driver is loaded. All it does is |
| 174 | + * register with the PCI subsystem. |
| 175 | + * |
| 176 | + * Return: 0 on success, negative on failure |
| 177 | + **/ |
| 178 | +static int __init fbnic_init_module(void) |
| 179 | +{ |
| 180 | + int err; |
| 181 | + |
| 182 | + err = pci_register_driver(&fbnic_driver); |
| 183 | + if (err) |
| 184 | + goto out; |
| 185 | + |
| 186 | + pr_info(DRV_SUMMARY " (%s)", fbnic_driver.name); |
| 187 | +out: |
| 188 | + return err; |
| 189 | +} |
| 190 | +module_init(fbnic_init_module); |
| 191 | + |
| 192 | +/** |
| 193 | + * fbnic_exit_module - Driver Exit Cleanup Routine |
| 194 | + * |
| 195 | + * Called just before the driver is removed from memory. |
| 196 | + **/ |
| 197 | +static void __exit fbnic_exit_module(void) |
| 198 | +{ |
| 199 | + pci_unregister_driver(&fbnic_driver); |
| 200 | +} |
| 201 | +module_exit(fbnic_exit_module); |
0 commit comments