diff options
Diffstat (limited to 'drivers/pci/msi/irqdomain.c')
| -rw-r--r-- | drivers/pci/msi/irqdomain.c | 229 |
1 files changed, 81 insertions, 148 deletions
diff --git a/drivers/pci/msi/irqdomain.c b/drivers/pci/msi/irqdomain.c index e33bcc872699..a329060287b5 100644 --- a/drivers/pci/msi/irqdomain.c +++ b/drivers/pci/msi/irqdomain.c @@ -49,103 +49,52 @@ static void pci_msi_domain_write_msg(struct irq_data *irq_data, struct msi_msg * __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. +/* + * Per device MSI[-X] domain functionality */ -static irq_hw_number_t pci_msi_domain_calc_hwirq(struct msi_desc *desc) -{ - struct pci_dev *dev = msi_desc_to_pci_dev(desc); - - return (irq_hw_number_t)desc->msi_index | - pci_dev_id(dev) << 11 | - (pci_domain_nr(dev->bus) & 0xFFFFFFFF) << 27; -} - -static void pci_msi_domain_set_desc(msi_alloc_info_t *arg, - struct msi_desc *desc) +static void pci_device_domain_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc) { arg->desc = desc; - arg->hwirq = pci_msi_domain_calc_hwirq(desc); + arg->hwirq = desc->msi_index; } -static struct msi_domain_ops pci_msi_domain_ops_default = { - .set_desc = pci_msi_domain_set_desc, -}; - -static void pci_msi_domain_update_dom_ops(struct msi_domain_info *info) +static void cond_shutdown_parent(struct irq_data *data) { - struct msi_domain_ops *ops = info->ops; - - if (ops == NULL) { - info->ops = &pci_msi_domain_ops_default; - } else { - if (ops->set_desc == NULL) - ops->set_desc = pci_msi_domain_set_desc; - } -} + struct msi_domain_info *info = data->domain->host_data; -static void pci_msi_domain_update_chip_ops(struct msi_domain_info *info) -{ - struct irq_chip *chip = info->chip; - - 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; + if (unlikely(info->flags & MSI_FLAG_PCI_MSI_STARTUP_PARENT)) + irq_chip_shutdown_parent(data); + else if (unlikely(info->flags & MSI_FLAG_PCI_MSI_MASK_PARENT)) + irq_chip_mask_parent(data); } -/** - * 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) +static unsigned int cond_startup_parent(struct irq_data *data) { - 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); + struct msi_domain_info *info = data->domain->host_data; - /* Let the core code free MSI descriptors when freeing interrupts */ - info->flags |= MSI_FLAG_FREE_MSI_DESCS; + if (unlikely(info->flags & MSI_FLAG_PCI_MSI_STARTUP_PARENT)) + return irq_chip_startup_parent(data); + else if (unlikely(info->flags & MSI_FLAG_PCI_MSI_MASK_PARENT)) + irq_chip_unmask_parent(data); - info->flags |= MSI_FLAG_ACTIVATE_EARLY | MSI_FLAG_DEV_SYSFS; - if (IS_ENABLED(CONFIG_GENERIC_IRQ_RESERVATION_MODE)) - info->flags |= MSI_FLAG_MUST_REACTIVATE; + return 0; +} - /* 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; +static void pci_irq_shutdown_msi(struct irq_data *data) +{ + struct msi_desc *desc = irq_data_get_msi_desc(data); - return msi_create_irq_domain(fwnode, info, parent); + pci_msi_mask(desc, BIT(data->irq - desc->irq)); + cond_shutdown_parent(data); } -EXPORT_SYMBOL_GPL(pci_msi_create_irq_domain); -/* - * Per device MSI[-X] domain functionality - */ -static void pci_device_domain_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc) +static unsigned int pci_irq_startup_msi(struct irq_data *data) { - arg->desc = desc; - arg->hwirq = desc->msi_index; + struct msi_desc *desc = irq_data_get_msi_desc(data); + unsigned int ret = cond_startup_parent(data); + + pci_msi_unmask(desc, BIT(data->irq - desc->irq)); + return ret; } static void pci_irq_mask_msi(struct irq_data *data) @@ -176,6 +125,8 @@ static void pci_irq_unmask_msi(struct irq_data *data) static const struct msi_domain_template pci_msi_template = { .chip = { .name = "PCI-MSI", + .irq_startup = pci_irq_startup_msi, + .irq_shutdown = pci_irq_shutdown_msi, .irq_mask = pci_irq_mask_msi, .irq_unmask = pci_irq_unmask_msi, .irq_write_msi_msg = pci_msi_domain_write_msg, @@ -192,6 +143,20 @@ static const struct msi_domain_template pci_msi_template = { }, }; +static void pci_irq_shutdown_msix(struct irq_data *data) +{ + pci_msix_mask(irq_data_get_msi_desc(data)); + cond_shutdown_parent(data); +} + +static unsigned int pci_irq_startup_msix(struct irq_data *data) +{ + unsigned int ret = cond_startup_parent(data); + + pci_msix_unmask(irq_data_get_msi_desc(data)); + return ret; +} + static void pci_irq_mask_msix(struct irq_data *data) { pci_msix_mask(irq_data_get_msi_desc(data)); @@ -202,17 +167,20 @@ static void pci_irq_unmask_msix(struct irq_data *data) pci_msix_unmask(irq_data_get_msi_desc(data)); } -static void pci_msix_prepare_desc(struct irq_domain *domain, msi_alloc_info_t *arg, - struct msi_desc *desc) +void pci_msix_prepare_desc(struct irq_domain *domain, msi_alloc_info_t *arg, + struct msi_desc *desc) { /* Don't fiddle with preallocated MSI descriptors */ if (!desc->pci.mask_base) msix_prepare_msi_desc(to_pci_dev(desc->dev), desc); } +EXPORT_SYMBOL_GPL(pci_msix_prepare_desc); static const struct msi_domain_template pci_msix_template = { .chip = { .name = "PCI-MSIX", + .irq_startup = pci_irq_startup_msix, + .irq_shutdown = pci_irq_shutdown_msix, .irq_mask = pci_irq_mask_msix, .irq_unmask = pci_irq_unmask_msix, .irq_write_msi_msg = pci_msi_domain_write_msg, @@ -251,6 +219,7 @@ static bool pci_create_device_domain(struct pci_dev *pdev, const struct msi_doma /** * 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: @@ -267,7 +236,7 @@ static bool pci_create_device_domain(struct pci_dev *pdev, const struct msi_doma * - The device is removed * - MSI is disabled and a MSI-X domain is created */ -bool pci_setup_msi_device_domain(struct pci_dev *pdev) +bool pci_setup_msi_device_domain(struct pci_dev *pdev, unsigned int hwsize) { if (WARN_ON_ONCE(pdev->msix_enabled)) return false; @@ -277,7 +246,7 @@ bool pci_setup_msi_device_domain(struct pci_dev *pdev) if (pci_match_device_domain(pdev, DOMAIN_BUS_PCI_DEVICE_MSIX)) msi_remove_device_irq_domain(&pdev->dev, MSI_DEFAULT_DOMAIN); - return pci_create_device_domain(pdev, &pci_msi_template, 1); + return pci_create_device_domain(pdev, &pci_msi_template, hwsize); } /** @@ -330,13 +299,16 @@ bool pci_msi_domain_supports(struct pci_dev *pdev, unsigned int feature_mask, domain = dev_get_msi_domain(&pdev->dev); - if (!domain || !irq_domain_is_hierarchy(domain)) - return mode == ALLOW_LEGACY; + if (!domain || !irq_domain_is_hierarchy(domain)) { + if (IS_ENABLED(CONFIG_PCI_MSI_ARCH_FALLBACKS)) + return mode == ALLOW_LEGACY; + return false; + } if (!irq_domain_is_msi_parent(domain)) { /* * For "global" PCI/MSI interrupt domains the associated - * msi_domain_info::flags is the authoritive source of + * msi_domain_info::flags is the authoritative source of * information. */ info = domain->host_data; @@ -344,7 +316,7 @@ bool pci_msi_domain_supports(struct pci_dev *pdev, unsigned int feature_mask, } else { /* * For MSI parent domains the supported feature set - * is avaliable in the parent ops. This makes checks + * 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. @@ -355,65 +327,6 @@ bool pci_msi_domain_supports(struct pci_dev *pdev, unsigned int feature_mask, return (supported & feature_mask) == feature_mask; } -/** - * pci_create_ims_domain - Create a secondary IMS domain for a PCI device - * @pdev: The PCI device to operate on - * @template: The MSI info template which describes the domain - * @hwsize: The size of the hardware entry table or 0 if the domain - * is purely software managed - * @data: Optional pointer to domain specific data to be stored - * in msi_domain_info::data - * - * Return: True on success, false otherwise - * - * An IMS domain is expected to have the following constraints: - * - The index space is managed by the core code - * - * - There is no requirement for consecutive index ranges - * - * - The interrupt chip must provide the following callbacks: - * - irq_mask() - * - irq_unmask() - * - irq_write_msi_msg() - * - * - The interrupt chip must provide the following optional callbacks - * when the irq_mask(), irq_unmask() and irq_write_msi_msg() callbacks - * cannot operate directly on hardware, e.g. in the case that the - * interrupt message store is in queue memory: - * - irq_bus_lock() - * - irq_bus_unlock() - * - * These callbacks are invoked from preemptible task context and are - * allowed to sleep. In this case the mandatory callbacks above just - * store the information. The irq_bus_unlock() callback is supposed - * to make the change effective before returning. - * - * - Interrupt affinity setting is handled by the underlying parent - * interrupt domain and communicated to the IMS domain via - * irq_write_msi_msg(). - * - * The domain is automatically destroyed when the PCI device is removed. - */ -bool pci_create_ims_domain(struct pci_dev *pdev, const struct msi_domain_template *template, - unsigned int hwsize, void *data) -{ - struct irq_domain *domain = dev_get_msi_domain(&pdev->dev); - - if (!domain || !irq_domain_is_msi_parent(domain)) - return false; - - if (template->info.bus_token != DOMAIN_BUS_PCI_DEVICE_IMS || - !(template->info.flags & MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS) || - !(template->info.flags & MSI_FLAG_FREE_MSI_DESCS) || - !template->chip.irq_mask || !template->chip.irq_unmask || - !template->chip.irq_write_msi_msg || template->chip.irq_set_affinity) - return false; - - return msi_create_device_irq_domain(&pdev->dev, MSI_SECONDARY_DOMAIN, template, - hwsize, data, NULL); -} -EXPORT_SYMBOL_GPL(pci_create_ims_domain); - /* * 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 @@ -456,13 +369,33 @@ u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev) pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid); of_node = irq_domain_get_of_node(domain); - rid = of_node ? of_msi_map_id(&pdev->dev, of_node, rid) : + rid = of_node ? of_msi_xlate(&pdev->dev, &of_node, rid) : iort_msi_map_id(&pdev->dev, rid); return rid; } /** + * 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_for_each_dma_alias(pdev, get_msi_id_cb, &rid); + + return of_msi_xlate(&pdev->dev, node, rid); +} + +/** * pci_msi_get_device_domain - Get the MSI domain for a given PCI device * @pdev: The PCI device * |
