Commit 44ed0f35 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'irq-msi-2025-05-25' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull MSI updates from Thomas Gleixner:
 "Updates for the MSI subsystem (core code and PCI):

   - Switch the MSI descriptor locking to lock guards

   - Replace a broken and naive implementation of PCI/MSI-X control word
     updates in the PCI/TPH driver with a properly serialized variant in
     the PCI/MSI core code.

   - Remove the MSI descriptor abuse in the SCCI/UFS/QCOM driver by
     replacing the direct access to the MSI descriptors with the proper
     API function calls. People will never understand that APIs exist
     for a reason...

   - Provide core infrastructre for the upcoming PCI endpoint library
     extensions. Currently limited to ARM GICv3+, but in theory
     extensible to other architectures.

   - Provide a MSI domain::teardown() callback, which allows drivers to
     undo the effects of the prepare() callback.

   - Move the MSI domain::prepare() callback invocation to domain
     creation time to avoid redundant (and in case of ARM/GIC-V3-ITS
     confusing) invocations on every allocation.

     In combination with the new teardown callback this removes some
     ugly hacks in the GIC-V3-ITS driver, which pretended to work around
     the short comings of the core code so far. With this update the
     code is correct by design and implementation.

   - Make the irqchip MSI library globally available, provide a MSI
     parent domain creation helper and convert a bunch of (PCI/)MSI
     drivers over to the modern MSI parent mechanism. This is the first
     step to get rid of at least one incarnation of the three PCI/MSI
     management schemes.

   - The usual small cleanups and improvements"

* tag 'irq-msi-2025-05-25' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (33 commits)
  PCI/MSI: Use bool for MSI enable state tracking
  PCI: tegra: Convert to MSI parent infrastructure
  PCI: xgene: Convert to MSI parent infrastructure
  PCI: apple: Convert to MSI parent infrastructure
  irqchip/msi-lib: Honour the MSI_FLAG_NO_AFFINITY flag
  irqchip/mvebu: Convert to msi_create_parent_irq_domain() helper
  irqchip/gic: Convert to msi_create_parent_irq_domain() helper
  genirq/msi: Add helper for creating MSI-parent irq domains
  irqchip: Make irq-msi-lib.h globally available
  irqchip/gic-v3-its: Use allocation size from the prepare call
  genirq/msi: Engage the .msi_teardown() callback on domain removal
  genirq/msi: Move prepare() call to per-device allocation
  irqchip/gic-v3-its: Implement .msi_teardown() callback
  genirq/msi: Add .msi_teardown() callback as the reverse of .msi_prepare()
  irqchip/gic-v3-its: Add support for device tree msi-map and msi-mask
  dt-bindings: PCI: pci-ep: Add support for iommu-map and msi-map
  irqchip/gic-v3-its: Set IRQ_DOMAIN_FLAG_MSI_IMMUTABLE for ITS
  irqdomain: Add IRQ_DOMAIN_FLAG_MSI_IMMUTABLE and irq_domain_is_msi_immutable()
  platform-msi: Add msi_remove_device_irq_domain() in platform_device_msi_free_irqs_all()
  genirq/msi: Rename msi_[un]lock_descs()
  ...
parents 2bd1bea5 4e7bca76
Loading
Loading
Loading
Loading
+68 −0
Original line number Diff line number Diff line
@@ -17,6 +17,24 @@ properties:
  $nodename:
    pattern: "^pcie-ep@"

  iommu-map:
    $ref: /schemas/types.yaml#/definitions/uint32-matrix
    items:
      items:
        - description: Device ID (see msi-map) base
          maximum: 0x7ffff
        - description: phandle to IOMMU
        - description: IOMMU specifier base (currently always 1 cell)
        - description: Number of Device IDs
          maximum: 0x80000

  iommu-map-mask:
    description:
      A mask to be applied to each Device ID prior to being mapped to an
      IOMMU specifier per the iommu-map property.
    $ref: /schemas/types.yaml#/definitions/uint32
    maximum: 0x7ffff

  max-functions:
    description: Maximum number of functions that can be configured
    $ref: /schemas/types.yaml#/definitions/uint8
@@ -35,6 +53,56 @@ properties:
    $ref: /schemas/types.yaml#/definitions/uint32
    enum: [ 1, 2, 3, 4 ]

  msi-map:
    description: |
      Maps a Device ID to an MSI and associated MSI specifier data.

      A PCI Endpoint (EP) can use MSI as a doorbell function. This is achieved by
      mapping the MSI controller's address into PCI BAR<n>. The PCI Root Complex
      can write to this BAR<n>, triggering the EP to generate IRQ. This notifies
      the EP-side driver of an event, eliminating the need for the driver to
      continuously poll for status changes.

      However, the EP cannot rely on Requester ID (RID) because the RID is
      determined by the PCI topology of the host system. Since the EP may be
      connected to different PCI hosts, the RID can vary between systems and is
      therefore not a reliable identifier.

      Each EP can support up to 8 physical functions and up to 65,536 virtual
      functions. To uniquely identify each child device, a device ID is defined
      as
         - Bits [2:0] for the function number (func)
         - Bits [18:3] for the virtual function index (vfunc)

      The resulting device ID is computed as:

        (func & 0x7) | (vfunc << 3)

      The property is an arbitrary number of tuples of
      (device-id-base, msi, msi-base,length).

      Any Device ID id in the interval [id-base, id-base + length) is
      associated with the listed MSI, with the MSI specifier
      (id - id-base + msi-base).
    $ref: /schemas/types.yaml#/definitions/uint32-matrix
    items:
      items:
        - description: The Device ID base matched by the entry
          maximum: 0x7ffff
        - description: phandle to msi-controller node
        - description: (optional) The msi-specifier produced for the first
            Device ID matched by the entry. Currently, msi-specifier is 0 or
            1 cells.
        - description: The length of consecutive Device IDs following the
            Device ID base
          maximum: 0x80000

  msi-map-mask:
    description: A mask to be applied to each Device ID prior to being
      mapped to an msi-specifier per the msi-map property.
    $ref: /schemas/types.yaml#/definitions/uint32
    maximum: 0x7ffff

  num-lanes:
    description: maximum number of lanes
    $ref: /schemas/types.yaml#/definitions/uint32
+1 −0
Original line number Diff line number Diff line
@@ -95,5 +95,6 @@ EXPORT_SYMBOL_GPL(platform_device_msi_init_and_alloc_irqs);
void platform_device_msi_free_irqs_all(struct device *dev)
{
	msi_domain_free_irqs_all(dev, MSI_DEFAULT_DOMAIN);
	msi_remove_device_irq_domain(dev, MSI_DEFAULT_DOMAIN);
}
EXPORT_SYMBOL_GPL(platform_device_msi_free_irqs_all);
+1 −1
Original line number Diff line number Diff line
@@ -11,7 +11,7 @@
#include <linux/of_address.h>
#include <linux/of_platform.h>

#include "irq-msi-lib.h"
#include <linux/irqchip/irq-msi-lib.h>

#define MIP_INT_RAISE		0x00
#define MIP_INT_CLEAR		0x10
+9 −9
Original line number Diff line number Diff line
@@ -26,7 +26,7 @@
#include <linux/irqchip/arm-gic.h>
#include <linux/irqchip/arm-gic-common.h>

#include "irq-msi-lib.h"
#include <linux/irqchip/irq-msi-lib.h>

/*
* MSI_TYPER:
@@ -261,23 +261,23 @@ static struct msi_parent_ops gicv2m_msi_parent_ops = {

static __init int gicv2m_allocate_domains(struct irq_domain *parent)
{
	struct irq_domain *inner_domain;
	struct irq_domain_info info = {
		.ops		= &gicv2m_domain_ops,
		.parent		= parent,
	};
	struct v2m_data *v2m;

	v2m = list_first_entry_or_null(&v2m_nodes, struct v2m_data, entry);
	if (!v2m)
		return 0;

	inner_domain = irq_domain_create_hierarchy(parent, 0, 0, v2m->fwnode,
						   &gicv2m_domain_ops, v2m);
	if (!inner_domain) {
	info.host_data = v2m;
	info.fwnode = v2m->fwnode;

	if (!msi_create_parent_irq_domain(&info, &gicv2m_msi_parent_ops)) {
		pr_err("Failed to create GICv2m domain\n");
		return -ENOMEM;
	}

	irq_domain_update_bus_token(inner_domain, DOMAIN_BUS_NEXUS);
	inner_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT;
	inner_domain->msi_parent_ops = &gicv2m_msi_parent_ops;
	return 0;
}

+19 −20
Original line number Diff line number Diff line
@@ -8,7 +8,7 @@
#include <linux/pci.h>

#include "irq-gic-common.h"
#include "irq-msi-lib.h"
#include <linux/irqchip/irq-msi-lib.h>

#define ITS_MSI_FLAGS_REQUIRED  (MSI_FLAG_USE_DEF_DOM_OPS |	\
				 MSI_FLAG_USE_DEF_CHIP_OPS |	\
@@ -67,17 +67,6 @@ static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
	/* ITS specific DeviceID, as the core ITS ignores dev. */
	info->scratchpad[0].ul = pci_msi_domain_get_msi_rid(domain->parent, pdev);

	/*
	 * @domain->msi_domain_info->hwsize contains the size of the
	 * MSI[-X] domain, but vector allocation happens one by one. This
	 * needs some thought when MSI comes into play as the size of MSI
	 * might be unknown at domain creation time and therefore set to
	 * MSI_MAX_INDEX.
	 */
	msi_info = msi_get_domain_info(domain);
	if (msi_info->hwsize > nvec)
		nvec = msi_info->hwsize;

	/*
	 * Always allocate a power of 2, and special case device 0 for
	 * broken systems where the DevID is not wired (and all devices
@@ -118,6 +107,14 @@ static int of_pmsi_get_dev_id(struct irq_domain *domain, struct device *dev,
		index++;
	} while (!ret);

	if (ret) {
		struct device_node *np = NULL;

		ret = of_map_id(dev->of_node, dev->id, "msi-map", "msi-map-mask", &np, dev_id);
		if (np)
			of_node_put(np);
	}

	return ret;
}

@@ -143,14 +140,6 @@ static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
	/* ITS specific DeviceID, as the core ITS ignores dev. */
	info->scratchpad[0].ul = dev_id;

	/*
	 * @domain->msi_domain_info->hwsize contains the size of the device
	 * domain, but vector allocation happens one by one.
	 */
	msi_info = msi_get_domain_info(domain);
	if (msi_info->hwsize > nvec)
		nvec = msi_info->hwsize;

	/* Allocate at least 32 MSIs, and always as a power of 2 */
	nvec = max_t(int, 32, roundup_pow_of_two(nvec));

@@ -159,6 +148,14 @@ static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
					  dev, nvec, info);
}

static void its_msi_teardown(struct irq_domain *domain, msi_alloc_info_t *info)
{
	struct msi_domain_info *msi_info;

	msi_info = msi_get_domain_info(domain->parent);
	msi_info->ops->msi_teardown(domain->parent, info);
}

static bool its_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
				  struct irq_domain *real_parent, struct msi_domain_info *info)
{
@@ -182,6 +179,7 @@ static bool its_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
		 * %MSI_MAX_INDEX.
		 */
		info->ops->msi_prepare = its_pci_msi_prepare;
		info->ops->msi_teardown = its_msi_teardown;
		break;
	case DOMAIN_BUS_DEVICE_MSI:
	case DOMAIN_BUS_WIRED_TO_MSI:
@@ -190,6 +188,7 @@ static bool its_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
		 * size is also known at domain creation time.
		 */
		info->ops->msi_prepare = its_pmsi_prepare;
		info->ops->msi_teardown = its_msi_teardown;
		break;
	default:
		/* Confused. How did the lib return true? */
Loading