Commit 4527e837 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'irq-msi-2024-03-10' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull MSI updates from Thomas Gleixner:
 "Updates for the MSI interrupt subsystem and initial RISC-V MSI
  support.

  The core changes have been adopted from previous work which converted
  ARM[64] to the new per device MSI domain model, which was merged to
  support multiple MSI domain per device. The ARM[64] changes are being
  worked on too, but have not been ready yet. The core and platform-MSI
  changes have been split out to not hold up RISC-V and to avoid that
  RISC-V builds on the scheduled for removal interfaces.

  The core support provides new interfaces to handle wire to MSI bridges
  in a straight forward way and introduces new platform-MSI interfaces
  which are built on top of the per device MSI domain model.

  Once ARM[64] is converted over the old platform-MSI interfaces and the
  related ugliness in the MSI core code will be removed.

  The actual MSI parts for RISC-V were finalized late and have been
  post-poned for the next merge window.

  Drivers:

   - Add a new driver for the Andes hart-level interrupt controller

   - Rework the SiFive PLIC driver to prepare for MSI suport

   - Expand the RISC-V INTC driver to support the new RISC-V AIA
     controller which provides the basis for MSI on RISC-V

   - A few fixup for the fallout of the core changes"

* tag 'irq-msi-2024-03-10' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (29 commits)
  irqchip/riscv-intc: Fix low-level interrupt handler setup for AIA
  x86/apic/msi: Use DOMAIN_BUS_GENERIC_MSI for HPET/IO-APIC domain search
  genirq/matrix: Dynamic bitmap allocation
  irqchip/riscv-intc: Add support for RISC-V AIA
  irqchip/sifive-plic: Improve locking safety by using irqsave/irqrestore
  irqchip/sifive-plic: Parse number of interrupts and contexts early in plic_probe()
  irqchip/sifive-plic: Cleanup PLIC contexts upon irqdomain creation failure
  irqchip/sifive-plic: Use riscv_get_intc_hwnode() to get parent fwnode
  irqchip/sifive-plic: Use devm_xyz() for managed allocation
  irqchip/sifive-plic: Use dev_xyz() in-place of pr_xyz()
  irqchip/sifive-plic: Convert PLIC driver into a platform driver
  irqchip/riscv-intc: Introduce Andes hart-level interrupt controller
  irqchip/riscv-intc: Allow large non-standard interrupt number
  genirq/irqdomain: Don't call ops->select for DOMAIN_BUS_ANY tokens
  irqchip/imx-intmux: Handle pure domain searches correctly
  genirq/msi: Provide MSI_FLAG_PARENT_PM_DEV
  genirq/irqdomain: Reroute device MSI create_mapping
  genirq/msi: Provide allocation/free functions for "wired" MSI interrupts
  genirq/msi: Optionally use dev->fwnode for device domain
  genirq/msi: Provide DOMAIN_BUS_WIRED_TO_MSI
  ...
parents 02d4df78 678c607e
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

#include <asm/irq_vectors.h>

#define IRQ_MATRIX_BITS		NR_VECTORS

#ifndef __ASSEMBLY__

#include <linux/percpu.h>
+1 −1
Original line number Diff line number Diff line
@@ -2354,7 +2354,7 @@ static int mp_irqdomain_create(int ioapic)
	fwspec.param_count = 1;
	fwspec.param[0] = mpc_ioapic_id(ioapic);

	parent = irq_find_matching_fwspec(&fwspec, DOMAIN_BUS_ANY);
	parent = irq_find_matching_fwspec(&fwspec, DOMAIN_BUS_GENERIC_MSI);
	if (!parent) {
		if (!cfg->dev)
			irq_domain_free_fwnode(fn);
+1 −1
Original line number Diff line number Diff line
@@ -568,7 +568,7 @@ static struct irq_domain *hpet_create_irq_domain(int hpet_id)
	fwspec.param_count = 1;
	fwspec.param[0] = hpet_id;

	parent = irq_find_matching_fwspec(&fwspec, DOMAIN_BUS_ANY);
	parent = irq_find_matching_fwspec(&fwspec, DOMAIN_BUS_GENERIC_MSI);
	if (!parent) {
		irq_domain_free_fwnode(fn);
		kfree(domain_info);
+105 −14
Original line number Diff line number Diff line
@@ -13,6 +13,8 @@
#include <linux/msi.h>
#include <linux/slab.h>

/* Begin of removal area. Once everything is converted over. Cleanup the includes too! */

#define DEV_ID_SHIFT	21
#define MAX_DEV_MSIS	(1 << (32 - DEV_ID_SHIFT))

@@ -204,7 +206,7 @@ static void platform_msi_free_priv_data(struct device *dev)
 * Returns:
 * Zero for success, or an error code in case of failure
 */
int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
static int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
					  irq_write_msi_msg_t write_msi_msg)
{
	int err;
@@ -219,18 +221,6 @@ int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,

	return err;
}
EXPORT_SYMBOL_GPL(platform_msi_domain_alloc_irqs);

/**
 * platform_msi_domain_free_irqs - Free MSI interrupts for @dev
 * @dev:	The device for which to free interrupts
 */
void platform_msi_domain_free_irqs(struct device *dev)
{
	msi_domain_free_irqs_all(dev, MSI_DEFAULT_DOMAIN);
	platform_msi_free_priv_data(dev);
}
EXPORT_SYMBOL_GPL(platform_msi_domain_free_irqs);

/**
 * platform_msi_get_host_data - Query the private data associated with
@@ -350,3 +340,104 @@ int platform_msi_device_domain_alloc(struct irq_domain *domain, unsigned int vir

	return msi_domain_populate_irqs(domain->parent, dev, virq, nr_irqs, &data->arg);
}

/* End of removal area */

/* Real per device domain interfaces */

/*
 * This indirection can go when platform_device_msi_init_and_alloc_irqs()
 * is switched to a proper irq_chip::irq_write_msi_msg() callback. Keep it
 * simple for now.
 */
static void platform_msi_write_msi_msg(struct irq_data *d, struct msi_msg *msg)
{
	irq_write_msi_msg_t cb = d->chip_data;

	cb(irq_data_get_msi_desc(d), msg);
}

static void platform_msi_set_desc_byindex(msi_alloc_info_t *arg, struct msi_desc *desc)
{
	arg->desc = desc;
	arg->hwirq = desc->msi_index;
}

static const struct msi_domain_template platform_msi_template = {
	.chip = {
		.name			= "pMSI",
		.irq_mask		= irq_chip_mask_parent,
		.irq_unmask		= irq_chip_unmask_parent,
		.irq_write_msi_msg	= platform_msi_write_msi_msg,
		/* The rest is filled in by the platform MSI parent */
	},

	.ops = {
		.set_desc		= platform_msi_set_desc_byindex,
	},

	.info = {
		.bus_token		= DOMAIN_BUS_DEVICE_MSI,
	},
};

/**
 * platform_device_msi_init_and_alloc_irqs - Initialize platform device MSI
 *					     and allocate interrupts for @dev
 * @dev:		The device for which to allocate interrupts
 * @nvec:		The number of interrupts to allocate
 * @write_msi_msg:	Callback to write an interrupt message for @dev
 *
 * Returns:
 * Zero for success, or an error code in case of failure
 *
 * This creates a MSI domain on @dev which has @dev->msi.domain as
 * parent. The parent domain sets up the new domain. The domain has
 * a fixed size of @nvec. The domain is managed by devres and will
 * be removed when the device is removed.
 *
 * Note: For migration purposes this falls back to the original platform_msi code
 *	 up to the point where all platforms have been converted to the MSI
 *	 parent model.
 */
int platform_device_msi_init_and_alloc_irqs(struct device *dev, unsigned int nvec,
					    irq_write_msi_msg_t write_msi_msg)
{
	struct irq_domain *domain = dev->msi.domain;

	if (!domain || !write_msi_msg)
		return -EINVAL;

	/* Migration support. Will go away once everything is converted */
	if (!irq_domain_is_msi_parent(domain))
		return platform_msi_domain_alloc_irqs(dev, nvec, write_msi_msg);

	/*
	 * @write_msi_msg is stored in the resulting msi_domain_info::data.
	 * The underlying domain creation mechanism will assign that
	 * callback to the resulting irq chip.
	 */
	if (!msi_create_device_irq_domain(dev, MSI_DEFAULT_DOMAIN,
					  &platform_msi_template,
					  nvec, NULL, write_msi_msg))
		return -ENODEV;

	return msi_domain_alloc_irqs_range(dev, MSI_DEFAULT_DOMAIN, 0, nvec - 1);
}
EXPORT_SYMBOL_GPL(platform_device_msi_init_and_alloc_irqs);

/**
 * platform_device_msi_free_irqs_all - Free all interrupts for @dev
 * @dev:	The device for which to free interrupts
 */
void platform_device_msi_free_irqs_all(struct device *dev)
{
	struct irq_domain *domain = dev->msi.domain;

	msi_domain_free_irqs_all(dev, MSI_DEFAULT_DOMAIN);

	/* Migration support. Will go away once everything is converted */
	if (!irq_domain_is_msi_parent(domain))
		platform_msi_free_priv_data(dev);
}
EXPORT_SYMBOL_GPL(platform_device_msi_free_irqs_all);
+4 −4
Original line number Diff line number Diff line
@@ -747,7 +747,7 @@ static int mv_xor_v2_probe(struct platform_device *pdev)
	if (IS_ERR(xor_dev->clk))
		return PTR_ERR(xor_dev->clk);

	ret = platform_msi_domain_alloc_irqs(&pdev->dev, 1,
	ret = platform_device_msi_init_and_alloc_irqs(&pdev->dev, 1,
						      mv_xor_v2_set_msi_msg);
	if (ret)
		return ret;
@@ -851,7 +851,7 @@ static int mv_xor_v2_probe(struct platform_device *pdev)
			  xor_dev->desc_size * MV_XOR_V2_DESC_NUM,
			  xor_dev->hw_desq_virt, xor_dev->hw_desq);
free_msi_irqs:
	platform_msi_domain_free_irqs(&pdev->dev);
	platform_device_msi_free_irqs_all(&pdev->dev);
	return ret;
}

@@ -867,7 +867,7 @@ static void mv_xor_v2_remove(struct platform_device *pdev)

	devm_free_irq(&pdev->dev, xor_dev->irq, xor_dev);

	platform_msi_domain_free_irqs(&pdev->dev);
	platform_device_msi_free_irqs_all(&pdev->dev);

	tasklet_kill(&xor_dev->irq_tasklet);
}
Loading