diff options
Diffstat (limited to 'drivers/pci/msi.c')
-rw-r--r-- | drivers/pci/msi.c | 27 |
1 files changed, 22 insertions, 5 deletions
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 2225afc1cbbb..496ed9130600 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c | |||
@@ -1451,13 +1451,30 @@ struct irq_domain *pci_msi_create_irq_domain(struct fwnode_handle *fwnode, | |||
1451 | } | 1451 | } |
1452 | EXPORT_SYMBOL_GPL(pci_msi_create_irq_domain); | 1452 | EXPORT_SYMBOL_GPL(pci_msi_create_irq_domain); |
1453 | 1453 | ||
1454 | /* | ||
1455 | * Users of the generic MSI infrastructure expect a device to have a single ID, | ||
1456 | * so with DMA aliases we have to pick the least-worst compromise. Devices with | ||
1457 | * DMA phantom functions tend to still emit MSIs from the real function number, | ||
1458 | * so we ignore those and only consider topological aliases where either the | ||
1459 | * alias device or RID appears on a different bus number. We also make the | ||
1460 | * reasonable assumption that bridges are walked in an upstream direction (so | ||
1461 | * the last one seen wins), and the much braver assumption that the most likely | ||
1462 | * case is that of PCI->PCIe so we should always use the alias RID. This echoes | ||
1463 | * the logic from intel_irq_remapping's set_msi_sid(), which presumably works | ||
1464 | * well enough in practice; in the face of the horrible PCIe<->PCI-X conditions | ||
1465 | * for taking ownership all we can really do is close our eyes and hope... | ||
1466 | */ | ||
1454 | static int get_msi_id_cb(struct pci_dev *pdev, u16 alias, void *data) | 1467 | static int get_msi_id_cb(struct pci_dev *pdev, u16 alias, void *data) |
1455 | { | 1468 | { |
1456 | u32 *pa = data; | 1469 | u32 *pa = data; |
1470 | u8 bus = PCI_BUS_NUM(*pa); | ||
1471 | |||
1472 | if (pdev->bus->number != bus || PCI_BUS_NUM(alias) != bus) | ||
1473 | *pa = alias; | ||
1457 | 1474 | ||
1458 | *pa = alias; | ||
1459 | return 0; | 1475 | return 0; |
1460 | } | 1476 | } |
1477 | |||
1461 | /** | 1478 | /** |
1462 | * pci_msi_domain_get_msi_rid - Get the MSI requester id (RID) | 1479 | * pci_msi_domain_get_msi_rid - Get the MSI requester id (RID) |
1463 | * @domain: The interrupt domain | 1480 | * @domain: The interrupt domain |
@@ -1471,7 +1488,7 @@ static int get_msi_id_cb(struct pci_dev *pdev, u16 alias, void *data) | |||
1471 | u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev) | 1488 | u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev) |
1472 | { | 1489 | { |
1473 | struct device_node *of_node; | 1490 | struct device_node *of_node; |
1474 | u32 rid = 0; | 1491 | u32 rid = PCI_DEVID(pdev->bus->number, pdev->devfn); |
1475 | 1492 | ||
1476 | pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid); | 1493 | pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid); |
1477 | 1494 | ||
@@ -1487,14 +1504,14 @@ u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev) | |||
1487 | * @pdev: The PCI device | 1504 | * @pdev: The PCI device |
1488 | * | 1505 | * |
1489 | * Use the firmware data to find a device-specific MSI domain | 1506 | * Use the firmware data to find a device-specific MSI domain |
1490 | * (i.e. not one that is ste as a default). | 1507 | * (i.e. not one that is set as a default). |
1491 | * | 1508 | * |
1492 | * Returns: The coresponding MSI domain or NULL if none has been found. | 1509 | * Returns: The corresponding MSI domain or NULL if none has been found. |
1493 | */ | 1510 | */ |
1494 | struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev) | 1511 | struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev) |
1495 | { | 1512 | { |
1496 | struct irq_domain *dom; | 1513 | struct irq_domain *dom; |
1497 | u32 rid = 0; | 1514 | u32 rid = PCI_DEVID(pdev->bus->number, pdev->devfn); |
1498 | 1515 | ||
1499 | pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid); | 1516 | pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid); |
1500 | dom = of_msi_map_get_device_domain(&pdev->dev, rid); | 1517 | dom = of_msi_map_get_device_domain(&pdev->dev, rid); |