/** * pci_msi_domain_write_msg - Helper to write MSI message to PCI config space * @irq_data: Pointer to interrupt data of the MSI interrupt * @msg: Pointer to the message
*/ staticvoid pci_msi_domain_write_msg(struct irq_data *irq_data, struct msi_msg *msg)
{ struct msi_desc *desc = irq_data_get_msi_desc(irq_data);
/* * For MSI-X desc->irq is always equal to irq_data->irq. For * MSI only the first interrupt of MULTI MSI passes the test.
*/ if (desc->irq == irq_data->irq)
__pci_write_msi_msg(desc, msg);
}
/** * pci_msi_domain_calc_hwirq - Generate a unique ID for an MSI source * @desc: Pointer to the MSI descriptor * * The ID number is only used within the irqdomain.
*/ static irq_hw_number_t pci_msi_domain_calc_hwirq(struct msi_desc *desc)
{ struct pci_dev *dev = msi_desc_to_pci_dev(desc);
BUG_ON(!chip); if (!chip->irq_write_msi_msg)
chip->irq_write_msi_msg = pci_msi_domain_write_msg; if (!chip->irq_mask)
chip->irq_mask = pci_msi_mask_irq; if (!chip->irq_unmask)
chip->irq_unmask = pci_msi_unmask_irq;
}
/** * pci_msi_create_irq_domain - Create a MSI interrupt domain * @fwnode: Optional fwnode of the interrupt controller * @info: MSI domain info * @parent: Parent irq domain * * Updates the domain and chip ops and creates a MSI interrupt domain. * * Returns: * A domain pointer or NULL in case of failure.
*/ struct irq_domain *pci_msi_create_irq_domain(struct fwnode_handle *fwnode, struct msi_domain_info *info, struct irq_domain *parent)
{ if (WARN_ON(info->flags & MSI_FLAG_LEVEL_CAPABLE))
info->flags &= ~MSI_FLAG_LEVEL_CAPABLE;
if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
pci_msi_domain_update_dom_ops(info); if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
pci_msi_domain_update_chip_ops(info);
/* Let the core code free MSI descriptors when freeing interrupts */
info->flags |= MSI_FLAG_FREE_MSI_DESCS;
info->flags |= MSI_FLAG_ACTIVATE_EARLY | MSI_FLAG_DEV_SYSFS; if (IS_ENABLED(CONFIG_GENERIC_IRQ_RESERVATION_MODE))
info->flags |= MSI_FLAG_MUST_REACTIVATE;
/* PCI-MSI is oneshot-safe */
info->chip->flags |= IRQCHIP_ONESHOT_SAFE; /* Let the core update the bus token */
info->bus_token = DOMAIN_BUS_PCI_MSI;
/** * pci_setup_msi_device_domain - Setup a device MSI interrupt domain * @pdev: The PCI device to create the domain on * @hwsize: The maximum number of MSI vectors * * Return: * True when: * - The device does not have a MSI parent irq domain associated, * which keeps the legacy architecture specific and the global * PCI/MSI domain models working * - The MSI domain exists already * - The MSI domain was successfully allocated * False when: * - MSI-X is enabled * - The domain creation fails. * * The created MSI domain is preserved until: * - The device is removed * - MSI is disabled and a MSI-X domain is created
*/ bool pci_setup_msi_device_domain(struct pci_dev *pdev, unsignedint hwsize)
{ if (WARN_ON_ONCE(pdev->msix_enabled)) returnfalse;
if (pci_match_device_domain(pdev, DOMAIN_BUS_PCI_DEVICE_MSI)) returntrue; if (pci_match_device_domain(pdev, DOMAIN_BUS_PCI_DEVICE_MSIX))
msi_remove_device_irq_domain(&pdev->dev, MSI_DEFAULT_DOMAIN);
/** * pci_setup_msix_device_domain - Setup a device MSI-X interrupt domain * @pdev: The PCI device to create the domain on * @hwsize: The size of the MSI-X vector table * * Return: * True when: * - The device does not have a MSI parent irq domain associated, * which keeps the legacy architecture specific and the global * PCI/MSI domain models working * - The MSI-X domain exists already * - The MSI-X domain was successfully allocated * False when: * - MSI is enabled * - The domain creation fails. * * The created MSI-X domain is preserved until: * - The device is removed * - MSI-X is disabled and a MSI domain is created
*/ bool pci_setup_msix_device_domain(struct pci_dev *pdev, unsignedint hwsize)
{ if (WARN_ON_ONCE(pdev->msi_enabled)) returnfalse;
if (pci_match_device_domain(pdev, DOMAIN_BUS_PCI_DEVICE_MSIX)) returntrue; if (pci_match_device_domain(pdev, DOMAIN_BUS_PCI_DEVICE_MSI))
msi_remove_device_irq_domain(&pdev->dev, MSI_DEFAULT_DOMAIN);
/** * pci_msi_domain_supports - Check for support of a particular feature flag * @pdev: The PCI device to operate on * @feature_mask: The feature mask to check for (full match) * @mode: If ALLOW_LEGACY this grants the feature when there is no irq domain * associated to the device. If DENY_LEGACY the lack of an irq domain * makes the feature unsupported
*/ bool pci_msi_domain_supports(struct pci_dev *pdev, unsignedint feature_mask, enum support_mode mode)
{ struct msi_domain_info *info; struct irq_domain *domain; unsignedint supported;
domain = dev_get_msi_domain(&pdev->dev);
if (!domain || !irq_domain_is_hierarchy(domain)) { if (IS_ENABLED(CONFIG_PCI_MSI_ARCH_FALLBACKS)) return mode == ALLOW_LEGACY; returnfalse;
}
if (!irq_domain_is_msi_parent(domain)) { /* * For "global" PCI/MSI interrupt domains the associated * msi_domain_info::flags is the authoritative source of * information.
*/
info = domain->host_data;
supported = info->flags;
} else { /* * For MSI parent domains the supported feature set * is available in the parent ops. This makes checks * possible before actually instantiating the * per device domain because the parent is never * expanding the PCI/MSI functionality.
*/
supported = domain->msi_parent_ops->supported_flags;
}
/* * Users of the generic MSI infrastructure expect a device to have a single ID, * so with DMA aliases we have to pick the least-worst compromise. Devices with * DMA phantom functions tend to still emit MSIs from the real function number, * so we ignore those and only consider topological aliases where either the * alias device or RID appears on a different bus number. We also make the * reasonable assumption that bridges are walked in an upstream direction (so * the last one seen wins), and the much braver assumption that the most likely * case is that of PCI->PCIe so we should always use the alias RID. This echoes * the logic from intel_irq_remapping's set_msi_sid(), which presumably works * well enough in practice; in the face of the horrible PCIe<->PCI-X conditions * for taking ownership all we can really do is close our eyes and hope...
*/ staticint get_msi_id_cb(struct pci_dev *pdev, u16 alias, void *data)
{
u32 *pa = data;
u8 bus = PCI_BUS_NUM(*pa);
if (pdev->bus->number != bus || PCI_BUS_NUM(alias) != bus)
*pa = alias;
return 0;
}
/** * pci_msi_domain_get_msi_rid - Get the MSI requester id (RID) * @domain: The interrupt domain * @pdev: The PCI device. * * The RID for a device is formed from the alias, with a firmware * supplied mapping applied * * Returns: The RID.
*/
u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev)
{ struct device_node *of_node;
u32 rid = pci_dev_id(pdev);
/** * pci_msi_map_rid_ctlr_node - Get the MSI controller node and MSI requester id (RID) * @pdev: The PCI device * @node: Pointer to store the MSI controller device node * * Use the firmware data to find the MSI controller node for @pdev. * If found map the RID and initialize @node with it. @node value must * be set to NULL on entry. * * Returns: The RID.
*/
u32 pci_msi_map_rid_ctlr_node(struct pci_dev *pdev, struct device_node **node)
{
u32 rid = pci_dev_id(pdev);
/** * pci_msi_get_device_domain - Get the MSI domain for a given PCI device * @pdev: The PCI device * * Use the firmware data to find a device-specific MSI domain * (i.e. not one that is set as a default). * * Returns: The corresponding MSI domain or NULL if none has been found.
*/ struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev)
{ struct irq_domain *dom;
u32 rid = pci_dev_id(pdev);
pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid);
dom = of_msi_map_get_device_domain(&pdev->dev, rid, DOMAIN_BUS_PCI_MSI); if (!dom)
dom = iort_get_device_domain(&pdev->dev, rid,
DOMAIN_BUS_PCI_MSI); return dom;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.