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