|
8 | 8 | */ |
9 | 9 |
|
10 | 10 | #include <linux/module.h> |
| 11 | +#include <linux/msi.h> |
11 | 12 | #include <asm/pci-bridge.h> |
12 | 13 | #include <asm/pnv-pci.h> |
13 | 14 | #include <asm/opal.h> |
@@ -281,3 +282,86 @@ void pnv_cxl_disable_device(struct pci_dev *dev) |
281 | 282 | cxl_pci_disable_device(dev); |
282 | 283 | cxl_afu_put(afu); |
283 | 284 | } |
| 285 | + |
| 286 | +/* |
| 287 | + * This is a special version of pnv_setup_msi_irqs for cards in cxl mode. This |
| 288 | + * function handles setting up the IVTE entries for the XSL to use. |
| 289 | + * |
| 290 | + * We are currently not filling out the MSIX table, since the only currently |
| 291 | + * supported adapter (CX4) uses a custom MSIX table format in cxl mode and it |
| 292 | + * is up to their driver to fill that out. In the future we may fill out the |
| 293 | + * MSIX table (and change the IVTE entries to be an index to the MSIX table) |
| 294 | + * for adapters implementing the Full MSI-X mode described in the CAIA. |
| 295 | + */ |
| 296 | +int pnv_cxl_cx4_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) |
| 297 | +{ |
| 298 | + struct pci_controller *hose = pci_bus_to_host(pdev->bus); |
| 299 | + struct pnv_phb *phb = hose->private_data; |
| 300 | + struct msi_desc *entry; |
| 301 | + struct cxl_context *ctx = NULL; |
| 302 | + unsigned int virq; |
| 303 | + int hwirq; |
| 304 | + int afu_irq = 0; |
| 305 | + int rc; |
| 306 | + |
| 307 | + if (WARN_ON(!phb) || !phb->msi_bmp.bitmap) |
| 308 | + return -ENODEV; |
| 309 | + |
| 310 | + if (pdev->no_64bit_msi && !phb->msi32_support) |
| 311 | + return -ENODEV; |
| 312 | + |
| 313 | + rc = cxl_cx4_setup_msi_irqs(pdev, nvec, type); |
| 314 | + if (rc) |
| 315 | + return rc; |
| 316 | + |
| 317 | + for_each_pci_msi_entry(entry, pdev) { |
| 318 | + if (!entry->msi_attrib.is_64 && !phb->msi32_support) { |
| 319 | + pr_warn("%s: Supports only 64-bit MSIs\n", |
| 320 | + pci_name(pdev)); |
| 321 | + return -ENXIO; |
| 322 | + } |
| 323 | + |
| 324 | + hwirq = cxl_next_msi_hwirq(pdev, &ctx, &afu_irq); |
| 325 | + if (WARN_ON(hwirq <= 0)) |
| 326 | + return (hwirq ? hwirq : -ENOMEM); |
| 327 | + |
| 328 | + virq = irq_create_mapping(NULL, hwirq); |
| 329 | + if (virq == NO_IRQ) { |
| 330 | + pr_warn("%s: Failed to map cxl mode MSI to linux irq\n", |
| 331 | + pci_name(pdev)); |
| 332 | + return -ENOMEM; |
| 333 | + } |
| 334 | + |
| 335 | + rc = pnv_cxl_ioda_msi_setup(pdev, hwirq, virq); |
| 336 | + if (rc) { |
| 337 | + pr_warn("%s: Failed to setup cxl mode MSI\n", pci_name(pdev)); |
| 338 | + irq_dispose_mapping(virq); |
| 339 | + return rc; |
| 340 | + } |
| 341 | + |
| 342 | + irq_set_msi_desc(virq, entry); |
| 343 | + } |
| 344 | + |
| 345 | + return 0; |
| 346 | +} |
| 347 | + |
| 348 | +void pnv_cxl_cx4_teardown_msi_irqs(struct pci_dev *pdev) |
| 349 | +{ |
| 350 | + struct pci_controller *hose = pci_bus_to_host(pdev->bus); |
| 351 | + struct pnv_phb *phb = hose->private_data; |
| 352 | + struct msi_desc *entry; |
| 353 | + irq_hw_number_t hwirq; |
| 354 | + |
| 355 | + if (WARN_ON(!phb)) |
| 356 | + return; |
| 357 | + |
| 358 | + for_each_pci_msi_entry(entry, pdev) { |
| 359 | + if (entry->irq == NO_IRQ) |
| 360 | + continue; |
| 361 | + hwirq = virq_to_hw(entry->irq); |
| 362 | + irq_set_msi_desc(entry->irq, NULL); |
| 363 | + irq_dispose_mapping(entry->irq); |
| 364 | + } |
| 365 | + |
| 366 | + cxl_cx4_teardown_msi_irqs(pdev); |
| 367 | +} |
0 commit comments