1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) Meta Platforms, Inc. and affiliates. */ 3 4 #include <linux/pci.h> 5 #include <linux/types.h> 6 7 #include "fbnic.h" 8 #include "fbnic_netdev.h" 9 #include "fbnic_txrx.h" 10 11 static irqreturn_t fbnic_fw_msix_intr(int __always_unused irq, void *data) 12 { 13 struct fbnic_dev *fbd = (struct fbnic_dev *)data; 14 15 fbnic_mbx_poll(fbd); 16 17 fbnic_wr32(fbd, FBNIC_INTR_MASK_CLEAR(0), 1u << FBNIC_FW_MSIX_ENTRY); 18 19 return IRQ_HANDLED; 20 } 21 22 static int __fbnic_fw_enable_mbx(struct fbnic_dev *fbd, int vector) 23 { 24 int err; 25 26 /* Initialize mailbox and attempt to poll it into ready state */ 27 fbnic_mbx_init(fbd); 28 err = fbnic_mbx_poll_tx_ready(fbd); 29 if (err) { 30 dev_warn(fbd->dev, "FW mailbox did not enter ready state\n"); 31 return err; 32 } 33 34 /* Enable interrupt and unmask the vector */ 35 enable_irq(vector); 36 fbnic_wr32(fbd, FBNIC_INTR_MASK_CLEAR(0), 1u << FBNIC_FW_MSIX_ENTRY); 37 38 return 0; 39 } 40 41 /** 42 * fbnic_fw_request_mbx - Configure and initialize Firmware Mailbox 43 * @fbd: Pointer to device to initialize 44 * 45 * This function will allocate the IRQ and then reinitialize the mailbox 46 * starting communication between the host and firmware. 47 * 48 * Return: non-zero on failure. 49 **/ 50 int fbnic_fw_request_mbx(struct fbnic_dev *fbd) 51 { 52 struct pci_dev *pdev = to_pci_dev(fbd->dev); 53 int vector, err; 54 55 WARN_ON(fbd->fw_msix_vector); 56 57 vector = pci_irq_vector(pdev, FBNIC_FW_MSIX_ENTRY); 58 if (vector < 0) 59 return vector; 60 61 /* Request the IRQ for FW Mailbox vector. */ 62 err = request_threaded_irq(vector, NULL, &fbnic_fw_msix_intr, 63 IRQF_ONESHOT | IRQF_NO_AUTOEN, 64 dev_name(fbd->dev), fbd); 65 if (err) 66 return err; 67 68 /* Initialize mailbox and attempt to poll it into ready state */ 69 err = __fbnic_fw_enable_mbx(fbd, vector); 70 if (err) 71 free_irq(vector, fbd); 72 73 fbd->fw_msix_vector = vector; 74 75 return err; 76 } 77 78 /** 79 * fbnic_fw_disable_mbx - Temporarily place mailbox in standby state 80 * @fbd: Pointer to device 81 * 82 * Shutdown the mailbox by notifying the firmware to stop sending us logs, mask 83 * and synchronize the IRQ, and then clean up the rings. 84 **/ 85 static void fbnic_fw_disable_mbx(struct fbnic_dev *fbd) 86 { 87 /* Disable interrupt and synchronize the IRQ */ 88 disable_irq(fbd->fw_msix_vector); 89 90 /* Mask the vector */ 91 fbnic_wr32(fbd, FBNIC_INTR_MASK_SET(0), 1u << FBNIC_FW_MSIX_ENTRY); 92 93 /* Make sure disabling logs message is sent, must be done here to 94 * avoid risk of completing without a running interrupt. 95 */ 96 fbnic_mbx_flush_tx(fbd); 97 fbnic_mbx_clean(fbd); 98 } 99 100 /** 101 * fbnic_fw_free_mbx - Disable mailbox and place it in standby state 102 * @fbd: Pointer to device to disable 103 * 104 * This function will disable the mailbox interrupt, free any messages still 105 * in the mailbox and place it into a disabled state. The firmware is 106 * expected to see the update and assume that the host is in the reset state. 107 **/ 108 void fbnic_fw_free_mbx(struct fbnic_dev *fbd) 109 { 110 /* Vector has already been freed */ 111 if (!fbd->fw_msix_vector) 112 return; 113 114 fbnic_fw_disable_mbx(fbd); 115 116 /* Free the vector */ 117 free_irq(fbd->fw_msix_vector, fbd); 118 fbd->fw_msix_vector = 0; 119 } 120 121 static irqreturn_t fbnic_mac_msix_intr(int __always_unused irq, void *data) 122 { 123 struct fbnic_dev *fbd = data; 124 struct fbnic_net *fbn; 125 126 if (fbd->mac->get_link_event(fbd) == FBNIC_LINK_EVENT_NONE) { 127 fbnic_wr32(fbd, FBNIC_INTR_MASK_CLEAR(0), 128 1u << FBNIC_PCS_MSIX_ENTRY); 129 return IRQ_HANDLED; 130 } 131 132 fbn = netdev_priv(fbd->netdev); 133 134 /* Record link down events */ 135 if (!fbd->mac->get_link(fbd, fbn->aui, fbn->fec)) 136 phylink_pcs_change(fbn->pcs, false); 137 138 return IRQ_HANDLED; 139 } 140 141 /** 142 * fbnic_mac_request_irq - Configure the MAC to enable it to advertise link 143 * @fbd: Pointer to device to initialize 144 * 145 * This function provides basic bringup for the MAC/PHY IRQ. For now the IRQ 146 * will remain disabled until we start the MAC/PCS/PHY logic via phylink. 147 * 148 * Return: non-zero on failure. 149 **/ 150 int fbnic_mac_request_irq(struct fbnic_dev *fbd) 151 { 152 struct pci_dev *pdev = to_pci_dev(fbd->dev); 153 int vector, err; 154 155 WARN_ON(fbd->mac_msix_vector); 156 157 vector = pci_irq_vector(pdev, FBNIC_PCS_MSIX_ENTRY); 158 if (vector < 0) 159 return vector; 160 161 /* Request the IRQ for PCS link vector. 162 * Map PCS cause to it, and unmask it 163 */ 164 err = request_irq(vector, &fbnic_mac_msix_intr, 0, 165 fbd->netdev->name, fbd); 166 if (err) 167 return err; 168 169 /* Map and enable interrupt, unmask vector after link is configured */ 170 fbnic_wr32(fbd, FBNIC_INTR_MSIX_CTRL(FBNIC_INTR_MSIX_CTRL_PCS_IDX), 171 FBNIC_PCS_MSIX_ENTRY | FBNIC_INTR_MSIX_CTRL_ENABLE); 172 173 fbd->mac_msix_vector = vector; 174 175 return 0; 176 } 177 178 /** 179 * fbnic_mac_free_irq - Teardown the MAC IRQ to prepare for stopping 180 * @fbd: Pointer to device that is stopping 181 * 182 * This function undoes the work done in fbnic_mac_request_irq and prepares 183 * the device to no longer receive traffic on the host interface. 184 **/ 185 void fbnic_mac_free_irq(struct fbnic_dev *fbd) 186 { 187 /* Vector has already been freed */ 188 if (!fbd->mac_msix_vector) 189 return; 190 191 /* Disable interrupt */ 192 fbnic_wr32(fbd, FBNIC_INTR_MSIX_CTRL(FBNIC_INTR_MSIX_CTRL_PCS_IDX), 193 FBNIC_PCS_MSIX_ENTRY); 194 fbnic_wrfl(fbd); 195 196 /* Synchronize IRQ to prevent race that would unmask vector */ 197 synchronize_irq(fbd->mac_msix_vector); 198 199 /* Mask the vector */ 200 fbnic_wr32(fbd, FBNIC_INTR_MASK_SET(0), 1u << FBNIC_PCS_MSIX_ENTRY); 201 202 /* Free the vector */ 203 free_irq(fbd->mac_msix_vector, fbd); 204 fbd->mac_msix_vector = 0; 205 } 206 207 void fbnic_synchronize_irq(struct fbnic_dev *fbd, int nr) 208 { 209 struct pci_dev *pdev = to_pci_dev(fbd->dev); 210 int irq = pci_irq_vector(pdev, nr); 211 212 if (irq < 0) 213 return; 214 215 synchronize_irq(irq); 216 } 217 218 int fbnic_request_irq(struct fbnic_dev *fbd, int nr, irq_handler_t handler, 219 unsigned long flags, const char *name, void *data) 220 { 221 struct pci_dev *pdev = to_pci_dev(fbd->dev); 222 int irq = pci_irq_vector(pdev, nr); 223 224 if (irq < 0) 225 return irq; 226 227 return request_irq(irq, handler, flags, name, data); 228 } 229 230 void fbnic_free_irq(struct fbnic_dev *fbd, int nr, void *data) 231 { 232 struct pci_dev *pdev = to_pci_dev(fbd->dev); 233 int irq = pci_irq_vector(pdev, nr); 234 235 if (irq < 0) 236 return; 237 238 free_irq(irq, data); 239 } 240 241 void fbnic_napi_name_irqs(struct fbnic_dev *fbd) 242 { 243 unsigned int i; 244 245 for (i = 0; i < ARRAY_SIZE(fbd->napi_irq); i++) 246 snprintf(fbd->napi_irq[i].name, 247 sizeof(fbd->napi_irq[i].name), 248 "%s-TxRx-%u", fbd->netdev->name, i); 249 } 250 251 int fbnic_napi_request_irq(struct fbnic_dev *fbd, 252 struct fbnic_napi_vector *nv) 253 { 254 struct fbnic_net *fbn = netdev_priv(fbd->netdev); 255 int i = fbnic_napi_idx(nv); 256 int err; 257 258 if (!fbd->napi_irq[i].users) { 259 err = fbnic_request_irq(fbd, nv->v_idx, 260 fbnic_msix_clean_rings, 0, 261 fbd->napi_irq[i].name, 262 &fbn->napi[i]); 263 if (err) 264 return err; 265 } 266 267 fbd->napi_irq[i].users++; 268 return 0; 269 } 270 271 void fbnic_napi_free_irq(struct fbnic_dev *fbd, 272 struct fbnic_napi_vector *nv) 273 { 274 struct fbnic_net *fbn = netdev_priv(fbd->netdev); 275 int i = fbnic_napi_idx(nv); 276 277 if (--fbd->napi_irq[i].users) 278 return; 279 280 fbnic_free_irq(fbd, nv->v_idx, &fbn->napi[i]); 281 } 282 283 void fbnic_free_irqs(struct fbnic_dev *fbd) 284 { 285 struct pci_dev *pdev = to_pci_dev(fbd->dev); 286 287 fbd->num_irqs = 0; 288 289 pci_free_irq_vectors(pdev); 290 } 291 292 int fbnic_alloc_irqs(struct fbnic_dev *fbd) 293 { 294 unsigned int wanted_irqs = FBNIC_NON_NAPI_VECTORS; 295 struct pci_dev *pdev = to_pci_dev(fbd->dev); 296 int num_irqs; 297 298 wanted_irqs += min_t(unsigned int, num_online_cpus(), FBNIC_MAX_RXQS); 299 num_irqs = pci_alloc_irq_vectors(pdev, FBNIC_NON_NAPI_VECTORS + 1, 300 wanted_irqs, PCI_IRQ_MSIX); 301 if (num_irqs < 0) { 302 dev_err(fbd->dev, "Failed to allocate MSI-X entries\n"); 303 return num_irqs; 304 } 305 306 if (num_irqs < wanted_irqs) 307 dev_warn(fbd->dev, "Allocated %d IRQs, expected %d\n", 308 num_irqs, wanted_irqs); 309 310 fbd->num_irqs = num_irqs; 311 312 return 0; 313 } 314