xref: /kernel/linux/linux-5.10/arch/x86/kernel/apic/msi.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Support of MSI, HPET and DMAR interrupts.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 1997, 1998, 1999, 2000, 2009 Ingo Molnar, Hajnalka Szabo
68c2ecf20Sopenharmony_ci *	Moved from arch/x86/kernel/apic/io_apic.c.
78c2ecf20Sopenharmony_ci * Jiang Liu <jiang.liu@linux.intel.com>
88c2ecf20Sopenharmony_ci *	Convert to hierarchical irqdomain
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci#include <linux/mm.h>
118c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
128c2ecf20Sopenharmony_ci#include <linux/irq.h>
138c2ecf20Sopenharmony_ci#include <linux/pci.h>
148c2ecf20Sopenharmony_ci#include <linux/dmar.h>
158c2ecf20Sopenharmony_ci#include <linux/hpet.h>
168c2ecf20Sopenharmony_ci#include <linux/msi.h>
178c2ecf20Sopenharmony_ci#include <asm/irqdomain.h>
188c2ecf20Sopenharmony_ci#include <asm/msidef.h>
198c2ecf20Sopenharmony_ci#include <asm/hpet.h>
208c2ecf20Sopenharmony_ci#include <asm/hw_irq.h>
218c2ecf20Sopenharmony_ci#include <asm/apic.h>
228c2ecf20Sopenharmony_ci#include <asm/irq_remapping.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistruct irq_domain *x86_pci_msi_default_domain __ro_after_init;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic void __irq_msi_compose_msg(struct irq_cfg *cfg, struct msi_msg *msg)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	msg->address_hi = MSI_ADDR_BASE_HI;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	if (x2apic_enabled())
318c2ecf20Sopenharmony_ci		msg->address_hi |= MSI_ADDR_EXT_DEST_ID(cfg->dest_apicid);
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	msg->address_lo =
348c2ecf20Sopenharmony_ci		MSI_ADDR_BASE_LO |
358c2ecf20Sopenharmony_ci		((apic->irq_dest_mode == 0) ?
368c2ecf20Sopenharmony_ci			MSI_ADDR_DEST_MODE_PHYSICAL :
378c2ecf20Sopenharmony_ci			MSI_ADDR_DEST_MODE_LOGICAL) |
388c2ecf20Sopenharmony_ci		MSI_ADDR_REDIRECTION_CPU |
398c2ecf20Sopenharmony_ci		MSI_ADDR_DEST_ID(cfg->dest_apicid);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	msg->data =
428c2ecf20Sopenharmony_ci		MSI_DATA_TRIGGER_EDGE |
438c2ecf20Sopenharmony_ci		MSI_DATA_LEVEL_ASSERT |
448c2ecf20Sopenharmony_ci		MSI_DATA_DELIVERY_FIXED |
458c2ecf20Sopenharmony_ci		MSI_DATA_VECTOR(cfg->vector);
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_civoid x86_vector_msi_compose_msg(struct irq_data *data, struct msi_msg *msg)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	__irq_msi_compose_msg(irqd_cfg(data), msg);
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic void irq_msi_update_msg(struct irq_data *irqd, struct irq_cfg *cfg)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	struct msi_msg msg[2] = { [1] = { }, };
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	__irq_msi_compose_msg(cfg, msg);
588c2ecf20Sopenharmony_ci	irq_data_get_irq_chip(irqd)->irq_write_msi_msg(irqd, msg);
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic int
628c2ecf20Sopenharmony_cimsi_set_affinity(struct irq_data *irqd, const struct cpumask *mask, bool force)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	struct irq_cfg old_cfg, *cfg = irqd_cfg(irqd);
658c2ecf20Sopenharmony_ci	struct irq_data *parent = irqd->parent_data;
668c2ecf20Sopenharmony_ci	unsigned int cpu;
678c2ecf20Sopenharmony_ci	int ret;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	/* Save the current configuration */
708c2ecf20Sopenharmony_ci	cpu = cpumask_first(irq_data_get_effective_affinity_mask(irqd));
718c2ecf20Sopenharmony_ci	old_cfg = *cfg;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	/* Allocate a new target vector */
748c2ecf20Sopenharmony_ci	ret = parent->chip->irq_set_affinity(parent, mask, force);
758c2ecf20Sopenharmony_ci	if (ret < 0 || ret == IRQ_SET_MASK_OK_DONE)
768c2ecf20Sopenharmony_ci		return ret;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	/*
798c2ecf20Sopenharmony_ci	 * For non-maskable and non-remapped MSI interrupts the migration
808c2ecf20Sopenharmony_ci	 * to a different destination CPU and a different vector has to be
818c2ecf20Sopenharmony_ci	 * done careful to handle the possible stray interrupt which can be
828c2ecf20Sopenharmony_ci	 * caused by the non-atomic update of the address/data pair.
838c2ecf20Sopenharmony_ci	 *
848c2ecf20Sopenharmony_ci	 * Direct update is possible when:
858c2ecf20Sopenharmony_ci	 * - The MSI is maskable (remapped MSI does not use this code path)).
868c2ecf20Sopenharmony_ci	 *   The quirk bit is not set in this case.
878c2ecf20Sopenharmony_ci	 * - The new vector is the same as the old vector
888c2ecf20Sopenharmony_ci	 * - The old vector is MANAGED_IRQ_SHUTDOWN_VECTOR (interrupt starts up)
898c2ecf20Sopenharmony_ci	 * - The interrupt is not yet started up
908c2ecf20Sopenharmony_ci	 * - The new destination CPU is the same as the old destination CPU
918c2ecf20Sopenharmony_ci	 */
928c2ecf20Sopenharmony_ci	if (!irqd_msi_nomask_quirk(irqd) ||
938c2ecf20Sopenharmony_ci	    cfg->vector == old_cfg.vector ||
948c2ecf20Sopenharmony_ci	    old_cfg.vector == MANAGED_IRQ_SHUTDOWN_VECTOR ||
958c2ecf20Sopenharmony_ci	    !irqd_is_started(irqd) ||
968c2ecf20Sopenharmony_ci	    cfg->dest_apicid == old_cfg.dest_apicid) {
978c2ecf20Sopenharmony_ci		irq_msi_update_msg(irqd, cfg);
988c2ecf20Sopenharmony_ci		return ret;
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	/*
1028c2ecf20Sopenharmony_ci	 * Paranoia: Validate that the interrupt target is the local
1038c2ecf20Sopenharmony_ci	 * CPU.
1048c2ecf20Sopenharmony_ci	 */
1058c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(cpu != smp_processor_id())) {
1068c2ecf20Sopenharmony_ci		irq_msi_update_msg(irqd, cfg);
1078c2ecf20Sopenharmony_ci		return ret;
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	/*
1118c2ecf20Sopenharmony_ci	 * Redirect the interrupt to the new vector on the current CPU
1128c2ecf20Sopenharmony_ci	 * first. This might cause a spurious interrupt on this vector if
1138c2ecf20Sopenharmony_ci	 * the device raises an interrupt right between this update and the
1148c2ecf20Sopenharmony_ci	 * update to the final destination CPU.
1158c2ecf20Sopenharmony_ci	 *
1168c2ecf20Sopenharmony_ci	 * If the vector is in use then the installed device handler will
1178c2ecf20Sopenharmony_ci	 * denote it as spurious which is no harm as this is a rare event
1188c2ecf20Sopenharmony_ci	 * and interrupt handlers have to cope with spurious interrupts
1198c2ecf20Sopenharmony_ci	 * anyway. If the vector is unused, then it is marked so it won't
1208c2ecf20Sopenharmony_ci	 * trigger the 'No irq handler for vector' warning in
1218c2ecf20Sopenharmony_ci	 * common_interrupt().
1228c2ecf20Sopenharmony_ci	 *
1238c2ecf20Sopenharmony_ci	 * This requires to hold vector lock to prevent concurrent updates to
1248c2ecf20Sopenharmony_ci	 * the affected vector.
1258c2ecf20Sopenharmony_ci	 */
1268c2ecf20Sopenharmony_ci	lock_vector_lock();
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	/*
1298c2ecf20Sopenharmony_ci	 * Mark the new target vector on the local CPU if it is currently
1308c2ecf20Sopenharmony_ci	 * unused. Reuse the VECTOR_RETRIGGERED state which is also used in
1318c2ecf20Sopenharmony_ci	 * the CPU hotplug path for a similar purpose. This cannot be
1328c2ecf20Sopenharmony_ci	 * undone here as the current CPU has interrupts disabled and
1338c2ecf20Sopenharmony_ci	 * cannot handle the interrupt before the whole set_affinity()
1348c2ecf20Sopenharmony_ci	 * section is done. In the CPU unplug case, the current CPU is
1358c2ecf20Sopenharmony_ci	 * about to vanish and will not handle any interrupts anymore. The
1368c2ecf20Sopenharmony_ci	 * vector is cleaned up when the CPU comes online again.
1378c2ecf20Sopenharmony_ci	 */
1388c2ecf20Sopenharmony_ci	if (IS_ERR_OR_NULL(this_cpu_read(vector_irq[cfg->vector])))
1398c2ecf20Sopenharmony_ci		this_cpu_write(vector_irq[cfg->vector], VECTOR_RETRIGGERED);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	/* Redirect it to the new vector on the local CPU temporarily */
1428c2ecf20Sopenharmony_ci	old_cfg.vector = cfg->vector;
1438c2ecf20Sopenharmony_ci	irq_msi_update_msg(irqd, &old_cfg);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	/* Now transition it to the target CPU */
1468c2ecf20Sopenharmony_ci	irq_msi_update_msg(irqd, cfg);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	/*
1498c2ecf20Sopenharmony_ci	 * All interrupts after this point are now targeted at the new
1508c2ecf20Sopenharmony_ci	 * vector/CPU.
1518c2ecf20Sopenharmony_ci	 *
1528c2ecf20Sopenharmony_ci	 * Drop vector lock before testing whether the temporary assignment
1538c2ecf20Sopenharmony_ci	 * to the local CPU was hit by an interrupt raised in the device,
1548c2ecf20Sopenharmony_ci	 * because the retrigger function acquires vector lock again.
1558c2ecf20Sopenharmony_ci	 */
1568c2ecf20Sopenharmony_ci	unlock_vector_lock();
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	/*
1598c2ecf20Sopenharmony_ci	 * Check whether the transition raced with a device interrupt and
1608c2ecf20Sopenharmony_ci	 * is pending in the local APICs IRR. It is safe to do this outside
1618c2ecf20Sopenharmony_ci	 * of vector lock as the irq_desc::lock of this interrupt is still
1628c2ecf20Sopenharmony_ci	 * held and interrupts are disabled: The check is not accessing the
1638c2ecf20Sopenharmony_ci	 * underlying vector store. It's just checking the local APIC's
1648c2ecf20Sopenharmony_ci	 * IRR.
1658c2ecf20Sopenharmony_ci	 */
1668c2ecf20Sopenharmony_ci	if (lapic_vector_set_in_irr(cfg->vector))
1678c2ecf20Sopenharmony_ci		irq_data_get_irq_chip(irqd)->irq_retrigger(irqd);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	return ret;
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci/*
1738c2ecf20Sopenharmony_ci * IRQ Chip for MSI PCI/PCI-X/PCI-Express Devices,
1748c2ecf20Sopenharmony_ci * which implement the MSI or MSI-X Capability Structure.
1758c2ecf20Sopenharmony_ci */
1768c2ecf20Sopenharmony_cistatic struct irq_chip pci_msi_controller = {
1778c2ecf20Sopenharmony_ci	.name			= "PCI-MSI",
1788c2ecf20Sopenharmony_ci	.irq_unmask		= pci_msi_unmask_irq,
1798c2ecf20Sopenharmony_ci	.irq_mask		= pci_msi_mask_irq,
1808c2ecf20Sopenharmony_ci	.irq_ack		= irq_chip_ack_parent,
1818c2ecf20Sopenharmony_ci	.irq_retrigger		= irq_chip_retrigger_hierarchy,
1828c2ecf20Sopenharmony_ci	.irq_set_affinity	= msi_set_affinity,
1838c2ecf20Sopenharmony_ci	.flags			= IRQCHIP_SKIP_SET_WAKE |
1848c2ecf20Sopenharmony_ci				  IRQCHIP_AFFINITY_PRE_STARTUP,
1858c2ecf20Sopenharmony_ci};
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ciint pci_msi_prepare(struct irq_domain *domain, struct device *dev, int nvec,
1888c2ecf20Sopenharmony_ci		    msi_alloc_info_t *arg)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev);
1918c2ecf20Sopenharmony_ci	struct msi_desc *desc = first_pci_msi_entry(pdev);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	init_irq_alloc_info(arg, NULL);
1948c2ecf20Sopenharmony_ci	if (desc->msi_attrib.is_msix) {
1958c2ecf20Sopenharmony_ci		arg->type = X86_IRQ_ALLOC_TYPE_PCI_MSIX;
1968c2ecf20Sopenharmony_ci	} else {
1978c2ecf20Sopenharmony_ci		arg->type = X86_IRQ_ALLOC_TYPE_PCI_MSI;
1988c2ecf20Sopenharmony_ci		arg->flags |= X86_IRQ_ALLOC_CONTIGUOUS_VECTORS;
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	return 0;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_msi_prepare);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic struct msi_domain_ops pci_msi_domain_ops = {
2068c2ecf20Sopenharmony_ci	.msi_prepare	= pci_msi_prepare,
2078c2ecf20Sopenharmony_ci};
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic struct msi_domain_info pci_msi_domain_info = {
2108c2ecf20Sopenharmony_ci	.flags		= MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
2118c2ecf20Sopenharmony_ci			  MSI_FLAG_PCI_MSIX,
2128c2ecf20Sopenharmony_ci	.ops		= &pci_msi_domain_ops,
2138c2ecf20Sopenharmony_ci	.chip		= &pci_msi_controller,
2148c2ecf20Sopenharmony_ci	.handler	= handle_edge_irq,
2158c2ecf20Sopenharmony_ci	.handler_name	= "edge",
2168c2ecf20Sopenharmony_ci};
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistruct irq_domain * __init native_create_pci_msi_domain(void)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	struct fwnode_handle *fn;
2218c2ecf20Sopenharmony_ci	struct irq_domain *d;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	if (disable_apic)
2248c2ecf20Sopenharmony_ci		return NULL;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	fn = irq_domain_alloc_named_fwnode("PCI-MSI");
2278c2ecf20Sopenharmony_ci	if (!fn)
2288c2ecf20Sopenharmony_ci		return NULL;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	d = pci_msi_create_irq_domain(fn, &pci_msi_domain_info,
2318c2ecf20Sopenharmony_ci				      x86_vector_domain);
2328c2ecf20Sopenharmony_ci	if (!d) {
2338c2ecf20Sopenharmony_ci		irq_domain_free_fwnode(fn);
2348c2ecf20Sopenharmony_ci		pr_warn("Failed to initialize PCI-MSI irqdomain.\n");
2358c2ecf20Sopenharmony_ci	} else {
2368c2ecf20Sopenharmony_ci		d->flags |= IRQ_DOMAIN_MSI_NOMASK_QUIRK;
2378c2ecf20Sopenharmony_ci	}
2388c2ecf20Sopenharmony_ci	return d;
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_civoid __init x86_create_pci_msi_domain(void)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	x86_pci_msi_default_domain = x86_init.irqs.create_pci_msi_domain();
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci#ifdef CONFIG_IRQ_REMAP
2478c2ecf20Sopenharmony_cistatic struct irq_chip pci_msi_ir_controller = {
2488c2ecf20Sopenharmony_ci	.name			= "IR-PCI-MSI",
2498c2ecf20Sopenharmony_ci	.irq_unmask		= pci_msi_unmask_irq,
2508c2ecf20Sopenharmony_ci	.irq_mask		= pci_msi_mask_irq,
2518c2ecf20Sopenharmony_ci	.irq_ack		= irq_chip_ack_parent,
2528c2ecf20Sopenharmony_ci	.irq_retrigger		= irq_chip_retrigger_hierarchy,
2538c2ecf20Sopenharmony_ci	.flags			= IRQCHIP_SKIP_SET_WAKE |
2548c2ecf20Sopenharmony_ci				  IRQCHIP_AFFINITY_PRE_STARTUP,
2558c2ecf20Sopenharmony_ci};
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic struct msi_domain_info pci_msi_ir_domain_info = {
2588c2ecf20Sopenharmony_ci	.flags		= MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
2598c2ecf20Sopenharmony_ci			  MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX,
2608c2ecf20Sopenharmony_ci	.ops		= &pci_msi_domain_ops,
2618c2ecf20Sopenharmony_ci	.chip		= &pci_msi_ir_controller,
2628c2ecf20Sopenharmony_ci	.handler	= handle_edge_irq,
2638c2ecf20Sopenharmony_ci	.handler_name	= "edge",
2648c2ecf20Sopenharmony_ci};
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistruct irq_domain *arch_create_remap_msi_irq_domain(struct irq_domain *parent,
2678c2ecf20Sopenharmony_ci						    const char *name, int id)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	struct fwnode_handle *fn;
2708c2ecf20Sopenharmony_ci	struct irq_domain *d;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	fn = irq_domain_alloc_named_id_fwnode(name, id);
2738c2ecf20Sopenharmony_ci	if (!fn)
2748c2ecf20Sopenharmony_ci		return NULL;
2758c2ecf20Sopenharmony_ci	d = pci_msi_create_irq_domain(fn, &pci_msi_ir_domain_info, parent);
2768c2ecf20Sopenharmony_ci	if (!d)
2778c2ecf20Sopenharmony_ci		irq_domain_free_fwnode(fn);
2788c2ecf20Sopenharmony_ci	return d;
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci#endif
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci#ifdef CONFIG_DMAR_TABLE
2838c2ecf20Sopenharmony_cistatic void dmar_msi_write_msg(struct irq_data *data, struct msi_msg *msg)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	dmar_msi_write(data->irq, msg);
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cistatic struct irq_chip dmar_msi_controller = {
2898c2ecf20Sopenharmony_ci	.name			= "DMAR-MSI",
2908c2ecf20Sopenharmony_ci	.irq_unmask		= dmar_msi_unmask,
2918c2ecf20Sopenharmony_ci	.irq_mask		= dmar_msi_mask,
2928c2ecf20Sopenharmony_ci	.irq_ack		= irq_chip_ack_parent,
2938c2ecf20Sopenharmony_ci	.irq_set_affinity	= msi_domain_set_affinity,
2948c2ecf20Sopenharmony_ci	.irq_retrigger		= irq_chip_retrigger_hierarchy,
2958c2ecf20Sopenharmony_ci	.irq_write_msi_msg	= dmar_msi_write_msg,
2968c2ecf20Sopenharmony_ci	.flags			= IRQCHIP_SKIP_SET_WAKE |
2978c2ecf20Sopenharmony_ci				  IRQCHIP_AFFINITY_PRE_STARTUP,
2988c2ecf20Sopenharmony_ci};
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic int dmar_msi_init(struct irq_domain *domain,
3018c2ecf20Sopenharmony_ci			 struct msi_domain_info *info, unsigned int virq,
3028c2ecf20Sopenharmony_ci			 irq_hw_number_t hwirq, msi_alloc_info_t *arg)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	irq_domain_set_info(domain, virq, arg->devid, info->chip, NULL,
3058c2ecf20Sopenharmony_ci			    handle_edge_irq, arg->data, "edge");
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	return 0;
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_cistatic struct msi_domain_ops dmar_msi_domain_ops = {
3118c2ecf20Sopenharmony_ci	.msi_init	= dmar_msi_init,
3128c2ecf20Sopenharmony_ci};
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic struct msi_domain_info dmar_msi_domain_info = {
3158c2ecf20Sopenharmony_ci	.ops		= &dmar_msi_domain_ops,
3168c2ecf20Sopenharmony_ci	.chip		= &dmar_msi_controller,
3178c2ecf20Sopenharmony_ci	.flags		= MSI_FLAG_USE_DEF_DOM_OPS,
3188c2ecf20Sopenharmony_ci};
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_cistatic struct irq_domain *dmar_get_irq_domain(void)
3218c2ecf20Sopenharmony_ci{
3228c2ecf20Sopenharmony_ci	static struct irq_domain *dmar_domain;
3238c2ecf20Sopenharmony_ci	static DEFINE_MUTEX(dmar_lock);
3248c2ecf20Sopenharmony_ci	struct fwnode_handle *fn;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	mutex_lock(&dmar_lock);
3278c2ecf20Sopenharmony_ci	if (dmar_domain)
3288c2ecf20Sopenharmony_ci		goto out;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	fn = irq_domain_alloc_named_fwnode("DMAR-MSI");
3318c2ecf20Sopenharmony_ci	if (fn) {
3328c2ecf20Sopenharmony_ci		dmar_domain = msi_create_irq_domain(fn, &dmar_msi_domain_info,
3338c2ecf20Sopenharmony_ci						    x86_vector_domain);
3348c2ecf20Sopenharmony_ci		if (!dmar_domain)
3358c2ecf20Sopenharmony_ci			irq_domain_free_fwnode(fn);
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ciout:
3388c2ecf20Sopenharmony_ci	mutex_unlock(&dmar_lock);
3398c2ecf20Sopenharmony_ci	return dmar_domain;
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ciint dmar_alloc_hwirq(int id, int node, void *arg)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	struct irq_domain *domain = dmar_get_irq_domain();
3458c2ecf20Sopenharmony_ci	struct irq_alloc_info info;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (!domain)
3488c2ecf20Sopenharmony_ci		return -1;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	init_irq_alloc_info(&info, NULL);
3518c2ecf20Sopenharmony_ci	info.type = X86_IRQ_ALLOC_TYPE_DMAR;
3528c2ecf20Sopenharmony_ci	info.devid = id;
3538c2ecf20Sopenharmony_ci	info.hwirq = id;
3548c2ecf20Sopenharmony_ci	info.data = arg;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	return irq_domain_alloc_irqs(domain, 1, node, &info);
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_civoid dmar_free_hwirq(int irq)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	irq_domain_free_irqs(irq, 1);
3628c2ecf20Sopenharmony_ci}
3638c2ecf20Sopenharmony_ci#endif
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci/*
3668c2ecf20Sopenharmony_ci * MSI message composition
3678c2ecf20Sopenharmony_ci */
3688c2ecf20Sopenharmony_ci#ifdef CONFIG_HPET_TIMER
3698c2ecf20Sopenharmony_cistatic inline int hpet_dev_id(struct irq_domain *domain)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	struct msi_domain_info *info = msi_get_domain_info(domain);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	return (int)(long)info->data;
3748c2ecf20Sopenharmony_ci}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_cistatic void hpet_msi_write_msg(struct irq_data *data, struct msi_msg *msg)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	hpet_msi_write(irq_data_get_irq_handler_data(data), msg);
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cistatic struct irq_chip hpet_msi_controller __ro_after_init = {
3828c2ecf20Sopenharmony_ci	.name = "HPET-MSI",
3838c2ecf20Sopenharmony_ci	.irq_unmask = hpet_msi_unmask,
3848c2ecf20Sopenharmony_ci	.irq_mask = hpet_msi_mask,
3858c2ecf20Sopenharmony_ci	.irq_ack = irq_chip_ack_parent,
3868c2ecf20Sopenharmony_ci	.irq_set_affinity = msi_domain_set_affinity,
3878c2ecf20Sopenharmony_ci	.irq_retrigger = irq_chip_retrigger_hierarchy,
3888c2ecf20Sopenharmony_ci	.irq_write_msi_msg = hpet_msi_write_msg,
3898c2ecf20Sopenharmony_ci	.flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_AFFINITY_PRE_STARTUP,
3908c2ecf20Sopenharmony_ci};
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cistatic int hpet_msi_init(struct irq_domain *domain,
3938c2ecf20Sopenharmony_ci			 struct msi_domain_info *info, unsigned int virq,
3948c2ecf20Sopenharmony_ci			 irq_hw_number_t hwirq, msi_alloc_info_t *arg)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	irq_set_status_flags(virq, IRQ_MOVE_PCNTXT);
3978c2ecf20Sopenharmony_ci	irq_domain_set_info(domain, virq, arg->hwirq, info->chip, NULL,
3988c2ecf20Sopenharmony_ci			    handle_edge_irq, arg->data, "edge");
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	return 0;
4018c2ecf20Sopenharmony_ci}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cistatic void hpet_msi_free(struct irq_domain *domain,
4048c2ecf20Sopenharmony_ci			  struct msi_domain_info *info, unsigned int virq)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	irq_clear_status_flags(virq, IRQ_MOVE_PCNTXT);
4078c2ecf20Sopenharmony_ci}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_cistatic struct msi_domain_ops hpet_msi_domain_ops = {
4108c2ecf20Sopenharmony_ci	.msi_init	= hpet_msi_init,
4118c2ecf20Sopenharmony_ci	.msi_free	= hpet_msi_free,
4128c2ecf20Sopenharmony_ci};
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_cistatic struct msi_domain_info hpet_msi_domain_info = {
4158c2ecf20Sopenharmony_ci	.ops		= &hpet_msi_domain_ops,
4168c2ecf20Sopenharmony_ci	.chip		= &hpet_msi_controller,
4178c2ecf20Sopenharmony_ci	.flags		= MSI_FLAG_USE_DEF_DOM_OPS,
4188c2ecf20Sopenharmony_ci};
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_cistruct irq_domain *hpet_create_irq_domain(int hpet_id)
4218c2ecf20Sopenharmony_ci{
4228c2ecf20Sopenharmony_ci	struct msi_domain_info *domain_info;
4238c2ecf20Sopenharmony_ci	struct irq_domain *parent, *d;
4248c2ecf20Sopenharmony_ci	struct irq_alloc_info info;
4258c2ecf20Sopenharmony_ci	struct fwnode_handle *fn;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	if (x86_vector_domain == NULL)
4288c2ecf20Sopenharmony_ci		return NULL;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	domain_info = kzalloc(sizeof(*domain_info), GFP_KERNEL);
4318c2ecf20Sopenharmony_ci	if (!domain_info)
4328c2ecf20Sopenharmony_ci		return NULL;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	*domain_info = hpet_msi_domain_info;
4358c2ecf20Sopenharmony_ci	domain_info->data = (void *)(long)hpet_id;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	init_irq_alloc_info(&info, NULL);
4388c2ecf20Sopenharmony_ci	info.type = X86_IRQ_ALLOC_TYPE_HPET_GET_PARENT;
4398c2ecf20Sopenharmony_ci	info.devid = hpet_id;
4408c2ecf20Sopenharmony_ci	parent = irq_remapping_get_irq_domain(&info);
4418c2ecf20Sopenharmony_ci	if (parent == NULL)
4428c2ecf20Sopenharmony_ci		parent = x86_vector_domain;
4438c2ecf20Sopenharmony_ci	else
4448c2ecf20Sopenharmony_ci		hpet_msi_controller.name = "IR-HPET-MSI";
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	fn = irq_domain_alloc_named_id_fwnode(hpet_msi_controller.name,
4478c2ecf20Sopenharmony_ci					      hpet_id);
4488c2ecf20Sopenharmony_ci	if (!fn) {
4498c2ecf20Sopenharmony_ci		kfree(domain_info);
4508c2ecf20Sopenharmony_ci		return NULL;
4518c2ecf20Sopenharmony_ci	}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	d = msi_create_irq_domain(fn, domain_info, parent);
4548c2ecf20Sopenharmony_ci	if (!d) {
4558c2ecf20Sopenharmony_ci		irq_domain_free_fwnode(fn);
4568c2ecf20Sopenharmony_ci		kfree(domain_info);
4578c2ecf20Sopenharmony_ci	}
4588c2ecf20Sopenharmony_ci	return d;
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ciint hpet_assign_irq(struct irq_domain *domain, struct hpet_channel *hc,
4628c2ecf20Sopenharmony_ci		    int dev_num)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	struct irq_alloc_info info;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	init_irq_alloc_info(&info, NULL);
4678c2ecf20Sopenharmony_ci	info.type = X86_IRQ_ALLOC_TYPE_HPET;
4688c2ecf20Sopenharmony_ci	info.data = hc;
4698c2ecf20Sopenharmony_ci	info.devid = hpet_dev_id(domain);
4708c2ecf20Sopenharmony_ci	info.hwirq = dev_num;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	return irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, &info);
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci#endif
475