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_pcs_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->pcs_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 phylink_pcs_change(&fbn->phylink_pcs, false); 135 136 return IRQ_HANDLED; 137 } 138 139 /** 140 * fbnic_pcs_request_irq - Configure the PCS to enable it to advertise link 141 * @fbd: Pointer to device to initialize 142 * 143 * This function provides basic bringup for the MAC/PCS IRQ. For now the IRQ 144 * will remain disabled until we start the MAC/PCS/PHY logic via phylink. 145 * 146 * Return: non-zero on failure. 147 **/ 148 int fbnic_pcs_request_irq(struct fbnic_dev *fbd) 149 { 150 struct pci_dev *pdev = to_pci_dev(fbd->dev); 151 int vector, err; 152 153 WARN_ON(fbd->pcs_msix_vector); 154 155 vector = pci_irq_vector(pdev, FBNIC_PCS_MSIX_ENTRY); 156 if (vector < 0) 157 return vector; 158 159 /* Request the IRQ for PCS link vector. 160 * Map PCS cause to it, and unmask it 161 */ 162 err = request_irq(vector, &fbnic_pcs_msix_intr, 0, 163 fbd->netdev->name, fbd); 164 if (err) 165 return err; 166 167 /* Map and enable interrupt, unmask vector after link is configured */ 168 fbnic_wr32(fbd, FBNIC_INTR_MSIX_CTRL(FBNIC_INTR_MSIX_CTRL_PCS_IDX), 169 FBNIC_PCS_MSIX_ENTRY | FBNIC_INTR_MSIX_CTRL_ENABLE); 170 171 fbd->pcs_msix_vector = vector; 172 173 return 0; 174 } 175 176 /** 177 * fbnic_pcs_free_irq - Teardown the PCS IRQ to prepare for stopping 178 * @fbd: Pointer to device that is stopping 179 * 180 * This function undoes the work done in fbnic_pcs_request_irq and prepares 181 * the device to no longer receive traffic on the host interface. 182 **/ 183 void fbnic_pcs_free_irq(struct fbnic_dev *fbd) 184 { 185 /* Vector has already been freed */ 186 if (!fbd->pcs_msix_vector) 187 return; 188 189 /* Disable interrupt */ 190 fbnic_wr32(fbd, FBNIC_INTR_MSIX_CTRL(FBNIC_INTR_MSIX_CTRL_PCS_IDX), 191 FBNIC_PCS_MSIX_ENTRY); 192 fbnic_wrfl(fbd); 193 194 /* Synchronize IRQ to prevent race that would unmask vector */ 195 synchronize_irq(fbd->pcs_msix_vector); 196 197 /* Mask the vector */ 198 fbnic_wr32(fbd, FBNIC_INTR_MASK_SET(0), 1u << FBNIC_PCS_MSIX_ENTRY); 199 200 /* Free the vector */ 201 free_irq(fbd->pcs_msix_vector, fbd); 202 fbd->pcs_msix_vector = 0; 203 } 204 205 void fbnic_synchronize_irq(struct fbnic_dev *fbd, int nr) 206 { 207 struct pci_dev *pdev = to_pci_dev(fbd->dev); 208 int irq = pci_irq_vector(pdev, nr); 209 210 if (irq < 0) 211 return; 212 213 synchronize_irq(irq); 214 } 215 216 int fbnic_request_irq(struct fbnic_dev *fbd, int nr, irq_handler_t handler, 217 unsigned long flags, const char *name, void *data) 218 { 219 struct pci_dev *pdev = to_pci_dev(fbd->dev); 220 int irq = pci_irq_vector(pdev, nr); 221 222 if (irq < 0) 223 return irq; 224 225 return request_irq(irq, handler, flags, name, data); 226 } 227 228 void fbnic_free_irq(struct fbnic_dev *fbd, int nr, void *data) 229 { 230 struct pci_dev *pdev = to_pci_dev(fbd->dev); 231 int irq = pci_irq_vector(pdev, nr); 232 233 if (irq < 0) 234 return; 235 236 free_irq(irq, data); 237 } 238 239 void fbnic_napi_name_irqs(struct fbnic_dev *fbd) 240 { 241 unsigned int i; 242 243 for (i = 0; i < ARRAY_SIZE(fbd->napi_irq); i++) 244 snprintf(fbd->napi_irq[i].name, 245 sizeof(fbd->napi_irq[i].name), 246 "%s-TxRx-%u", fbd->netdev->name, i); 247 } 248 249 int fbnic_napi_request_irq(struct fbnic_dev *fbd, 250 struct fbnic_napi_vector *nv) 251 { 252 struct fbnic_net *fbn = netdev_priv(fbd->netdev); 253 int i = fbnic_napi_idx(nv); 254 int err; 255 256 if (!fbd->napi_irq[i].users) { 257 err = fbnic_request_irq(fbd, nv->v_idx, 258 fbnic_msix_clean_rings, 0, 259 fbd->napi_irq[i].name, 260 &fbn->napi[i]); 261 if (err) 262 return err; 263 } 264 265 fbd->napi_irq[i].users++; 266 return 0; 267 } 268 269 void fbnic_napi_free_irq(struct fbnic_dev *fbd, 270 struct fbnic_napi_vector *nv) 271 { 272 struct fbnic_net *fbn = netdev_priv(fbd->netdev); 273 int i = fbnic_napi_idx(nv); 274 275 if (--fbd->napi_irq[i].users) 276 return; 277 278 fbnic_free_irq(fbd, nv->v_idx, &fbn->napi[i]); 279 } 280 281 void fbnic_free_irqs(struct fbnic_dev *fbd) 282 { 283 struct pci_dev *pdev = to_pci_dev(fbd->dev); 284 285 fbd->num_irqs = 0; 286 287 pci_free_irq_vectors(pdev); 288 } 289 290 int fbnic_alloc_irqs(struct fbnic_dev *fbd) 291 { 292 unsigned int wanted_irqs = FBNIC_NON_NAPI_VECTORS; 293 struct pci_dev *pdev = to_pci_dev(fbd->dev); 294 int num_irqs; 295 296 wanted_irqs += min_t(unsigned int, num_online_cpus(), FBNIC_MAX_RXQS); 297 num_irqs = pci_alloc_irq_vectors(pdev, FBNIC_NON_NAPI_VECTORS + 1, 298 wanted_irqs, PCI_IRQ_MSIX); 299 if (num_irqs < 0) { 300 dev_err(fbd->dev, "Failed to allocate MSI-X entries\n"); 301 return num_irqs; 302 } 303 304 if (num_irqs < wanted_irqs) 305 dev_warn(fbd->dev, "Allocated %d IRQs, expected %d\n", 306 num_irqs, wanted_irqs); 307 308 fbd->num_irqs = num_irqs; 309 310 return 0; 311 } 312