diff options
Diffstat (limited to 'drivers/irqchip')
128 files changed, 2179 insertions, 1023 deletions
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 990674713b86..5f47e9a9c127 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -28,6 +28,7 @@ config ARM_GIC_V2M select ARM_GIC select IRQ_MSI_LIB select PCI_MSI + select IRQ_MSI_IOMMU config GIC_NON_BANKED bool @@ -38,12 +39,14 @@ config ARM_GIC_V3 select PARTITION_PERCPU select GENERIC_IRQ_EFFECTIVE_AFF_MASK if SMP select HAVE_ARM_SMCCC_DISCOVERY + select IRQ_MSI_IOMMU config ARM_GIC_V3_ITS bool select GENERIC_MSI_IRQ select IRQ_MSI_LIB default ARM_GIC_V3 + select IRQ_MSI_IOMMU config ARM_GIC_V3_ITS_FSL_MC bool @@ -71,6 +74,7 @@ config ARM_VIC_NR config IRQ_MSI_LIB bool + select GENERIC_MSI_IRQ config ARMADA_370_XP_IRQ bool @@ -109,6 +113,22 @@ config I8259 bool select IRQ_DOMAIN +config BCM2712_MIP + tristate "Broadcom BCM2712 MSI-X Interrupt Peripheral support" + depends on ARCH_BRCMSTB || ARCH_BCM2835 || COMPILE_TEST + default m if ARCH_BRCMSTB || ARCH_BCM2835 + depends on ARM_GIC + select GENERIC_IRQ_CHIP + select IRQ_DOMAIN_HIERARCHY + select GENERIC_MSI_IRQ + select IRQ_MSI_LIB + help + Enable support for the Broadcom BCM2712 MSI-X target peripheral + (MIP) needed by brcmstb PCIe to handle MSI-X interrupts on + Raspberry Pi 5. + + If unsure say n. + config BCM6345_L1_IRQ bool select GENERIC_IRQ_CHIP @@ -147,6 +167,11 @@ config DW_APB_ICTL select GENERIC_IRQ_CHIP select IRQ_DOMAIN_HIERARCHY +config ECONET_EN751221_INTC + bool + select GENERIC_IRQ_CHIP + select IRQ_DOMAIN + config FARADAY_FTINTC010 bool select IRQ_DOMAIN @@ -408,6 +433,7 @@ config LS_EXTIRQ config LS_SCFG_MSI def_bool y if SOC_LS1021A || ARCH_LAYERSCAPE + select IRQ_MSI_IOMMU depends on PCI_MSI config PARTITION_PERCPU @@ -513,6 +539,7 @@ config IMX_MU_MSI tristate "i.MX MU used as MSI controller" depends on OF && HAS_IOMEM depends on ARCH_MXC || COMPILE_TEST + depends on ARM || ARM64 default m if ARCH_MXC select IRQ_DOMAIN select IRQ_DOMAIN_HIERARCHY @@ -535,8 +562,9 @@ config LS1X_IRQ Support for the Loongson-1 platform Interrupt Controller. config TI_SCI_INTR_IRQCHIP - bool + tristate "TI SCI INTR Interrupt Controller" depends on TI_SCI_PROTOCOL + depends on ARCH_K3 || COMPILE_TEST select IRQ_DOMAIN_HIERARCHY help This enables the irqchip driver support for K3 Interrupt router @@ -545,8 +573,9 @@ config TI_SCI_INTR_IRQCHIP TI System Controller, say Y here. Otherwise, say N. config TI_SCI_INTA_IRQCHIP - bool + tristate "TI SCI INTA Interrupt Controller" depends on TI_SCI_PROTOCOL + depends on ARCH_K3 || (COMPILE_TEST && ARM64) select IRQ_DOMAIN_HIERARCHY select TI_SCI_INTA_MSI_DOMAIN help @@ -588,13 +617,7 @@ config RISCV_IMSIC select IRQ_DOMAIN_HIERARCHY select GENERIC_IRQ_MATRIX_ALLOCATOR select GENERIC_MSI_IRQ - -config RISCV_IMSIC_PCI - bool - depends on RISCV_IMSIC - depends on PCI - depends on PCI_MSI - default RISCV_IMSIC + select IRQ_MSI_LIB config SIFIVE_PLIC bool @@ -750,6 +773,18 @@ config MCHP_EIC help Support for Microchip External Interrupt Controller. +config SOPHGO_SG2042_MSI + bool "Sophgo SG2042 MSI Controller" + depends on ARCH_SOPHGO || COMPILE_TEST + depends on PCI + select IRQ_DOMAIN_HIERARCHY + select IRQ_MSI_LIB + select PCI_MSI + help + Support for the Sophgo SG2042 MSI Controller. + This on-chip interrupt controller enables MSI sources to be + routed to the primary PLIC controller on SoC. + config SUNPLUS_SP7021_INTC bool "Sunplus SP7021 interrupt controller" if COMPILE_TEST default SOC_SP7021 diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 25e9ad29b8c4..23ca4959e6ce 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2836.o obj-$(CONFIG_ARCH_ACTIONS) += irq-owl-sirq.o obj-$(CONFIG_DAVINCI_CP_INTC) += irq-davinci-cp-intc.o obj-$(CONFIG_EXYNOS_IRQ_COMBINER) += exynos-combiner.o +obj-$(CONFIG_ECONET_EN751221_INTC) += irq-econet-en751221.o obj-$(CONFIG_FARADAY_FTINTC010) += irq-ftintc010.o obj-$(CONFIG_ARCH_HIP04) += irq-hip04.o obj-$(CONFIG_ARCH_LPC32XX) += irq-lpc32xx.o @@ -63,6 +64,7 @@ obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o obj-$(CONFIG_XILINX_INTC) += irq-xilinx-intc.o obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o obj-$(CONFIG_SOC_VF610) += irq-vf610-mscm-ir.o +obj-$(CONFIG_BCM2712_MIP) += irq-bcm2712-mip.o obj-$(CONFIG_BCM6345_L1_IRQ) += irq-bcm6345-l1.o obj-$(CONFIG_BCM7038_L1_IRQ) += irq-bcm7038-l1.o obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o @@ -128,4 +130,5 @@ obj-$(CONFIG_WPCM450_AIC) += irq-wpcm450-aic.o obj-$(CONFIG_IRQ_IDT3243X) += irq-idt3243x.o obj-$(CONFIG_APPLE_AIC) += irq-apple-aic.o obj-$(CONFIG_MCHP_EIC) += irq-mchp-eic.o +obj-$(CONFIG_SOPHGO_SG2042_MSI) += irq-sg2042-msi.o obj-$(CONFIG_SUNPLUS_SP7021_INTC) += irq-sp7021-intc.o diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c index 552aa04ff063..e7dfcf0cda43 100644 --- a/drivers/irqchip/exynos-combiner.c +++ b/drivers/irqchip/exynos-combiner.c @@ -180,7 +180,7 @@ static void __init combiner_init(void __iomem *combiner_base, if (!combiner_data) return; - combiner_irq_domain = irq_domain_add_linear(np, nr_irq, + combiner_irq_domain = irq_domain_create_linear(of_fwnode_handle(np), nr_irq, &combiner_irq_domain_ops, combiner_data); if (WARN_ON(!combiner_irq_domain)) { pr_warn("%s: irq domain init failed\n", __func__); diff --git a/drivers/irqchip/irq-al-fic.c b/drivers/irqchip/irq-al-fic.c index dfb761e86c9c..8f300843bbca 100644 --- a/drivers/irqchip/irq-al-fic.c +++ b/drivers/irqchip/irq-al-fic.c @@ -65,15 +65,13 @@ static int al_fic_irq_set_type(struct irq_data *data, unsigned int flow_type) struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); struct al_fic *fic = gc->private; enum al_fic_state new_state; - int ret = 0; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); if (((flow_type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_LEVEL_HIGH) && ((flow_type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_EDGE_RISING)) { pr_debug("fic doesn't support flow type %d\n", flow_type); - ret = -EINVAL; - goto err; + return -EINVAL; } new_state = (flow_type & IRQ_TYPE_LEVEL_HIGH) ? @@ -91,16 +89,10 @@ static int al_fic_irq_set_type(struct irq_data *data, unsigned int flow_type) if (fic->state == AL_FIC_UNCONFIGURED) { al_fic_set_trigger(fic, gc, new_state); } else if (fic->state != new_state) { - pr_debug("fic %s state already configured to %d\n", - fic->name, fic->state); - ret = -EINVAL; - goto err; + pr_debug("fic %s state already configured to %d\n", fic->name, fic->state); + return -EINVAL; } - -err: - irq_gc_unlock(gc); - - return ret; + return 0; } static void al_fic_irq_handler(struct irq_desc *desc) @@ -139,7 +131,7 @@ static int al_fic_register(struct device_node *node, struct irq_chip_generic *gc; int ret; - fic->domain = irq_domain_add_linear(node, + fic->domain = irq_domain_create_linear(of_fwnode_handle(node), NR_FIC_IRQS, &irq_generic_chip_ops, fic); diff --git a/drivers/irqchip/irq-alpine-msi.c b/drivers/irqchip/irq-alpine-msi.c index a1430ab60a8a..a5289dc26dca 100644 --- a/drivers/irqchip/irq-alpine-msi.c +++ b/drivers/irqchip/irq-alpine-msi.c @@ -205,15 +205,14 @@ static int alpine_msix_init_domains(struct alpine_msix_data *priv, return -ENXIO; } - middle_domain = irq_domain_add_hierarchy(gic_domain, 0, 0, NULL, - &alpine_msix_middle_domain_ops, - priv); + middle_domain = irq_domain_create_hierarchy(gic_domain, 0, 0, NULL, + &alpine_msix_middle_domain_ops, priv); if (!middle_domain) { pr_err("Failed to create the MSIX middle domain\n"); return -ENOMEM; } - msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node), + msi_domain = pci_msi_create_irq_domain(of_fwnode_handle(node), &alpine_msix_domain_info, middle_domain); if (!msi_domain) { diff --git a/drivers/irqchip/irq-apple-aic.c b/drivers/irqchip/irq-apple-aic.c index 2b1684c60e3c..032d66dceb8e 100644 --- a/drivers/irqchip/irq-apple-aic.c +++ b/drivers/irqchip/irq-apple-aic.c @@ -409,15 +409,15 @@ static void __exception_irq_entry aic_handle_irq(struct pt_regs *regs) * in use, and be cleared when coming back from the handler. */ if (is_kernel_in_hyp_mode() && - (read_sysreg_s(SYS_ICH_HCR_EL2) & ICH_HCR_EN) && + (read_sysreg_s(SYS_ICH_HCR_EL2) & ICH_HCR_EL2_En) && read_sysreg_s(SYS_ICH_MISR_EL2) != 0) { generic_handle_domain_irq(aic_irqc->hw_domain, AIC_FIQ_HWIRQ(AIC_VGIC_MI)); - if (unlikely((read_sysreg_s(SYS_ICH_HCR_EL2) & ICH_HCR_EN) && + if (unlikely((read_sysreg_s(SYS_ICH_HCR_EL2) & ICH_HCR_EL2_En) && read_sysreg_s(SYS_ICH_MISR_EL2))) { pr_err_ratelimited("vGIC IRQ fired and not handled by KVM, disabling.\n"); - sysreg_clear_set_s(SYS_ICH_HCR_EL2, ICH_HCR_EN, 0); + sysreg_clear_set_s(SYS_ICH_HCR_EL2, ICH_HCR_EL2_En, 0); } } } @@ -841,7 +841,7 @@ static int aic_init_cpu(unsigned int cpu) VM_TMR_FIQ_ENABLE_V | VM_TMR_FIQ_ENABLE_P, 0); /* vGIC maintenance IRQ */ - sysreg_clear_set_s(SYS_ICH_HCR_EL2, ICH_HCR_EN, 0); + sysreg_clear_set_s(SYS_ICH_HCR_EL2, ICH_HCR_EL2_En, 0); } /* PMC FIQ */ @@ -1014,7 +1014,7 @@ static int __init aic_of_ic_init(struct device_node *node, struct device_node *p irqc->info.die_stride = off - start_off; - irqc->hw_domain = irq_domain_create_tree(of_node_to_fwnode(node), + irqc->hw_domain = irq_domain_create_tree(of_fwnode_handle(node), &aic_irq_domain_ops, irqc); if (WARN_ON(!irqc->hw_domain)) goto err_unmap; @@ -1067,7 +1067,7 @@ static int __init aic_of_ic_init(struct device_node *node, struct device_node *p if (is_kernel_in_hyp_mode()) { struct irq_fwspec mi = { - .fwnode = of_node_to_fwnode(node), + .fwnode = of_fwnode_handle(node), .param_count = 3, .param = { [0] = AIC_FIQ, /* This is a lie */ diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c index d7c5ef248474..67b672a78862 100644 --- a/drivers/irqchip/irq-armada-370-xp.c +++ b/drivers/irqchip/irq-armada-370-xp.c @@ -348,12 +348,12 @@ static int __init mpic_msi_init(struct mpic *mpic, struct device_node *node, mpic->msi_doorbell_mask = PCI_MSI_FULL_DOORBELL_MASK; } - mpic->msi_inner_domain = irq_domain_add_linear(NULL, mpic->msi_doorbell_size, + mpic->msi_inner_domain = irq_domain_create_linear(NULL, mpic->msi_doorbell_size, &mpic_msi_domain_ops, mpic); if (!mpic->msi_inner_domain) return -ENOMEM; - mpic->msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node), &mpic_msi_domain_info, + mpic->msi_domain = pci_msi_create_irq_domain(of_fwnode_handle(node), &mpic_msi_domain_info, mpic->msi_inner_domain); if (!mpic->msi_domain) { irq_domain_remove(mpic->msi_inner_domain); @@ -492,7 +492,7 @@ static int __init mpic_ipi_init(struct mpic *mpic, struct device_node *node) { int base_ipi; - mpic->ipi_domain = irq_domain_create_linear(of_node_to_fwnode(node), IPI_DOORBELL_NR, + mpic->ipi_domain = irq_domain_create_linear(of_fwnode_handle(node), IPI_DOORBELL_NR, &mpic_ipi_domain_ops, mpic); if (WARN_ON(!mpic->ipi_domain)) return -ENOMEM; @@ -546,7 +546,7 @@ static void mpic_reenable_percpu(struct mpic *mpic) { /* Re-enable per-CPU interrupts that were enabled before suspend */ for (irq_hw_number_t i = 0; i < MPIC_PER_CPU_IRQS_NR; i++) { - unsigned int virq = irq_linear_revmap(mpic->domain, i); + unsigned int virq = irq_find_mapping(mpic->domain, i); struct irq_data *d; if (!virq || !irq_percpu_is_enabled(virq)) @@ -564,7 +564,7 @@ static void mpic_reenable_percpu(struct mpic *mpic) static int mpic_starting_cpu(unsigned int cpu) { - struct mpic *mpic = irq_get_default_host()->host_data; + struct mpic *mpic = irq_get_default_domain()->host_data; mpic_perf_init(mpic); mpic_smp_cpu_init(mpic); @@ -700,7 +700,7 @@ static void mpic_handle_cascade_irq(struct irq_desc *desc) static void __exception_irq_entry mpic_handle_irq(struct pt_regs *regs) { - struct mpic *mpic = irq_get_default_host()->host_data; + struct mpic *mpic = irq_get_default_domain()->host_data; irq_hw_number_t i; u32 irqstat; @@ -740,7 +740,7 @@ static void mpic_resume(void) /* Re-enable interrupts */ for (irq_hw_number_t i = 0; i < mpic->domain->hwirq_max; i++) { - unsigned int virq = irq_linear_revmap(mpic->domain, i); + unsigned int virq = irq_find_mapping(mpic->domain, i); struct irq_data *d; if (!virq) @@ -861,7 +861,7 @@ static int __init mpic_of_init(struct device_node *node, struct device_node *par if (!mpic_is_ipi_available(mpic)) nr_irqs = MPIC_PER_CPU_IRQS_NR; - mpic->domain = irq_domain_add_linear(node, nr_irqs, &mpic_irq_ops, mpic); + mpic->domain = irq_domain_create_linear(of_fwnode_handle(node), nr_irqs, &mpic_irq_ops, mpic); if (!mpic->domain) { pr_err("%pOF: Unable to add IRQ domain\n", node); return -ENOMEM; @@ -880,7 +880,7 @@ static int __init mpic_of_init(struct device_node *node, struct device_node *par } if (mpic_is_ipi_available(mpic)) { - irq_set_default_host(mpic->domain); + irq_set_default_domain(mpic->domain); set_handle_irq(mpic_handle_irq); #ifdef CONFIG_SMP err = mpic_ipi_init(mpic, node); diff --git a/drivers/irqchip/irq-aspeed-i2c-ic.c b/drivers/irqchip/irq-aspeed-i2c-ic.c index 9c9fc3e2967e..87c1feb999ff 100644 --- a/drivers/irqchip/irq-aspeed-i2c-ic.c +++ b/drivers/irqchip/irq-aspeed-i2c-ic.c @@ -82,7 +82,7 @@ static int __init aspeed_i2c_ic_of_init(struct device_node *node, goto err_iounmap; } - i2c_ic->irq_domain = irq_domain_add_linear(node, ASPEED_I2C_IC_NUM_BUS, + i2c_ic->irq_domain = irq_domain_create_linear(of_fwnode_handle(node), ASPEED_I2C_IC_NUM_BUS, &aspeed_i2c_ic_irq_domain_ops, NULL); if (!i2c_ic->irq_domain) { diff --git a/drivers/irqchip/irq-aspeed-intc.c b/drivers/irqchip/irq-aspeed-intc.c index bd3b759b4b2c..8330221799a0 100644 --- a/drivers/irqchip/irq-aspeed-intc.c +++ b/drivers/irqchip/irq-aspeed-intc.c @@ -102,7 +102,7 @@ static int __init aspeed_intc_ic_of_init(struct device_node *node, writel(0xffffffff, intc_ic->base + INTC_INT_STATUS_REG); writel(0x0, intc_ic->base + INTC_INT_ENABLE_REG); - intc_ic->irq_domain = irq_domain_add_linear(node, INTC_IRQS_PER_WORD, + intc_ic->irq_domain = irq_domain_create_linear(of_fwnode_handle(node), INTC_IRQS_PER_WORD, &aspeed_intc_ic_irq_domain_ops, intc_ic); if (!intc_ic->irq_domain) { ret = -ENOMEM; diff --git a/drivers/irqchip/irq-aspeed-scu-ic.c b/drivers/irqchip/irq-aspeed-scu-ic.c index 94a7223e95df..1c7045467c48 100644 --- a/drivers/irqchip/irq-aspeed-scu-ic.c +++ b/drivers/irqchip/irq-aspeed-scu-ic.c @@ -165,7 +165,7 @@ static int aspeed_scu_ic_of_init_common(struct aspeed_scu_ic *scu_ic, goto err; } - scu_ic->irq_domain = irq_domain_add_linear(node, scu_ic->num_irqs, + scu_ic->irq_domain = irq_domain_create_linear(of_fwnode_handle(node), scu_ic->num_irqs, &aspeed_scu_ic_domain_ops, scu_ic); if (!scu_ic->irq_domain) { diff --git a/drivers/irqchip/irq-aspeed-vic.c b/drivers/irqchip/irq-aspeed-vic.c index 62ccf2c0c414..9b665b5bb531 100644 --- a/drivers/irqchip/irq-aspeed-vic.c +++ b/drivers/irqchip/irq-aspeed-vic.c @@ -211,8 +211,8 @@ static int __init avic_of_init(struct device_node *node, set_handle_irq(avic_handle_irq); /* Register our domain */ - vic->dom = irq_domain_add_simple(node, NUM_IRQS, 0, - &avic_dom_ops, vic); + vic->dom = irq_domain_create_simple(of_fwnode_handle(node), NUM_IRQS, 0, + &avic_dom_ops, vic); return 0; } diff --git a/drivers/irqchip/irq-ath79-misc.c b/drivers/irqchip/irq-ath79-misc.c index 92f001a5ff8d..258b8e9a2d57 100644 --- a/drivers/irqchip/irq-ath79-misc.c +++ b/drivers/irqchip/irq-ath79-misc.c @@ -15,6 +15,8 @@ #include <linux/of_address.h> #include <linux/of_irq.h> +#include <asm/time.h> + #define AR71XX_RESET_REG_MISC_INT_STATUS 0 #define AR71XX_RESET_REG_MISC_INT_ENABLE 4 @@ -147,7 +149,7 @@ static int __init ath79_misc_intc_of_init( return -ENOMEM; } - domain = irq_domain_add_linear(node, ATH79_MISC_IRQ_COUNT, + domain = irq_domain_create_linear(of_fwnode_handle(node), ATH79_MISC_IRQ_COUNT, &misc_irq_domain_ops, base); if (!domain) { pr_err("Failed to add MISC irqdomain\n"); @@ -177,21 +179,3 @@ static int __init ar7240_misc_intc_of_init( IRQCHIP_DECLARE(ar7240_misc_intc, "qca,ar7240-misc-intc", ar7240_misc_intc_of_init); - -void __init ath79_misc_irq_init(void __iomem *regs, int irq, - int irq_base, bool is_ar71xx) -{ - struct irq_domain *domain; - - if (is_ar71xx) - ath79_misc_irq_chip.irq_mask_ack = ar71xx_misc_irq_mask; - else - ath79_misc_irq_chip.irq_ack = ar724x_misc_irq_ack; - - domain = irq_domain_add_legacy(NULL, ATH79_MISC_IRQ_COUNT, - irq_base, 0, &misc_irq_domain_ops, regs); - if (!domain) - panic("Failed to create MISC irqdomain"); - - ath79_misc_intc_domain_init(domain, irq); -} diff --git a/drivers/irqchip/irq-atmel-aic-common.c b/drivers/irqchip/irq-atmel-aic-common.c index 4525366d16d6..3cad30a40c19 100644 --- a/drivers/irqchip/irq-atmel-aic-common.c +++ b/drivers/irqchip/irq-atmel-aic-common.c @@ -228,7 +228,7 @@ struct irq_domain *__init aic_common_of_init(struct device_node *node, goto err_iounmap; } - domain = irq_domain_add_linear(node, nchips * 32, ops, aic); + domain = irq_domain_create_linear(of_fwnode_handle(node), nchips * 32, ops, aic); if (!domain) { ret = -ENOMEM; goto err_free_aic; diff --git a/drivers/irqchip/irq-atmel-aic.c b/drivers/irqchip/irq-atmel-aic.c index 3839ad79ad31..03aeed39a4d2 100644 --- a/drivers/irqchip/irq-atmel-aic.c +++ b/drivers/irqchip/irq-atmel-aic.c @@ -78,9 +78,8 @@ static int aic_retrigger(struct irq_data *d) struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); /* Enable interrupt on AIC5 */ - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); irq_reg_writel(gc, d->mask, AT91_AIC_ISCR); - irq_gc_unlock(gc); return 1; } @@ -106,30 +105,27 @@ static void aic_suspend(struct irq_data *d) { struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); irq_reg_writel(gc, gc->mask_cache, AT91_AIC_IDCR); irq_reg_writel(gc, gc->wake_active, AT91_AIC_IECR); - irq_gc_unlock(gc); } static void aic_resume(struct irq_data *d) { struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); irq_reg_writel(gc, gc->wake_active, AT91_AIC_IDCR); irq_reg_writel(gc, gc->mask_cache, AT91_AIC_IECR); - irq_gc_unlock(gc); } static void aic_pm_shutdown(struct irq_data *d) { struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); irq_reg_writel(gc, 0xffffffff, AT91_AIC_IDCR); irq_reg_writel(gc, 0xffffffff, AT91_AIC_ICCR); - irq_gc_unlock(gc); } #else #define aic_suspend NULL @@ -175,10 +171,8 @@ static int aic_irq_domain_xlate(struct irq_domain *d, { struct irq_domain_chip_generic *dgc = d->gc; struct irq_chip_generic *gc; - unsigned long flags; unsigned smr; - int idx; - int ret; + int idx, ret; if (!dgc) return -EINVAL; @@ -194,11 +188,10 @@ static int aic_irq_domain_xlate(struct irq_domain *d, gc = dgc->gc[idx]; - irq_gc_lock_irqsave(gc, flags); + guard(raw_spinlock_irq)(&gc->lock); smr = irq_reg_readl(gc, AT91_AIC_SMR(*out_hwirq)); aic_common_set_priority(intspec[2], &smr); irq_reg_writel(gc, smr, AT91_AIC_SMR(*out_hwirq)); - irq_gc_unlock_irqrestore(gc, flags); return ret; } diff --git a/drivers/irqchip/irq-atmel-aic5.c b/drivers/irqchip/irq-atmel-aic5.c index e98c2875af9e..60b00d2c3d7a 100644 --- a/drivers/irqchip/irq-atmel-aic5.c +++ b/drivers/irqchip/irq-atmel-aic5.c @@ -92,11 +92,10 @@ static void aic5_mask(struct irq_data *d) * Disable interrupt on AIC5. We always take the lock of the * first irq chip as all chips share the same registers. */ - irq_gc_lock(bgc); + guard(raw_spinlock)(&bgc->lock); irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR); irq_reg_writel(gc, 1, AT91_AIC5_IDCR); gc->mask_cache &= ~d->mask; - irq_gc_unlock(bgc); } static void aic5_unmask(struct irq_data *d) @@ -109,11 +108,10 @@ static void aic5_unmask(struct irq_data *d) * Enable interrupt on AIC5. We always take the lock of the * first irq chip as all chips share the same registers. */ - irq_gc_lock(bgc); + guard(raw_spinlock)(&bgc->lock); irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR); irq_reg_writel(gc, 1, AT91_AIC5_IECR); gc->mask_cache |= d->mask; - irq_gc_unlock(bgc); } static int aic5_retrigger(struct irq_data *d) @@ -122,11 +120,9 @@ static int aic5_retrigger(struct irq_data *d) struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0); /* Enable interrupt on AIC5 */ - irq_gc_lock(bgc); + guard(raw_spinlock)(&bgc->lock); irq_reg_writel(bgc, d->hwirq, AT91_AIC5_SSR); irq_reg_writel(bgc, 1, AT91_AIC5_ISCR); - irq_gc_unlock(bgc); - return 1; } @@ -137,14 +133,12 @@ static int aic5_set_type(struct irq_data *d, unsigned type) unsigned int smr; int ret; - irq_gc_lock(bgc); + guard(raw_spinlock)(&bgc->lock); irq_reg_writel(bgc, d->hwirq, AT91_AIC5_SSR); smr = irq_reg_readl(bgc, AT91_AIC5_SMR); ret = aic_common_set_type(d, type, &smr); if (!ret) irq_reg_writel(bgc, smr, AT91_AIC5_SMR); - irq_gc_unlock(bgc); - return ret; } @@ -166,7 +160,7 @@ static void aic5_suspend(struct irq_data *d) smr_cache[i] = irq_reg_readl(bgc, AT91_AIC5_SMR); } - irq_gc_lock(bgc); + guard(raw_spinlock)(&bgc->lock); for (i = 0; i < dgc->irqs_per_chip; i++) { mask = 1 << i; if ((mask & gc->mask_cache) == (mask & gc->wake_active)) @@ -178,7 +172,6 @@ static void aic5_suspend(struct irq_data *d) else irq_reg_writel(bgc, 1, AT91_AIC5_IDCR); } - irq_gc_unlock(bgc); } static void aic5_resume(struct irq_data *d) @@ -190,7 +183,7 @@ static void aic5_resume(struct irq_data *d) int i; u32 mask; - irq_gc_lock(bgc); + guard(raw_spinlock)(&bgc->lock); if (smr_cache) { irq_reg_writel(bgc, 0xffffffff, AT91_AIC5_SPU); @@ -214,7 +207,6 @@ static void aic5_resume(struct irq_data *d) else irq_reg_writel(bgc, 1, AT91_AIC5_IDCR); } - irq_gc_unlock(bgc); } static void aic5_pm_shutdown(struct irq_data *d) @@ -225,13 +217,12 @@ static void aic5_pm_shutdown(struct irq_data *d) struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); int i; - irq_gc_lock(bgc); + guard(raw_spinlock)(&bgc->lock); for (i = 0; i < dgc->irqs_per_chip; i++) { irq_reg_writel(bgc, i + gc->irq_base, AT91_AIC5_SSR); irq_reg_writel(bgc, 1, AT91_AIC5_IDCR); irq_reg_writel(bgc, 1, AT91_AIC5_ICCR); } - irq_gc_unlock(bgc); } #else #define aic5_suspend NULL @@ -277,7 +268,6 @@ static int aic5_irq_domain_xlate(struct irq_domain *d, unsigned int *out_type) { struct irq_chip_generic *bgc = irq_get_domain_generic_chip(d, 0); - unsigned long flags; unsigned smr; int ret; @@ -289,13 +279,11 @@ static int aic5_irq_domain_xlate(struct irq_domain *d, if (ret) return ret; - irq_gc_lock_irqsave(bgc, flags); + guard(raw_spinlock_irq)(&bgc->lock); irq_reg_writel(bgc, *out_hwirq, AT91_AIC5_SSR); smr = irq_reg_readl(bgc, AT91_AIC5_SMR); aic_common_set_priority(intspec[2], &smr); irq_reg_writel(bgc, smr, AT91_AIC5_SMR); - irq_gc_unlock_irqrestore(bgc, flags); - return ret; } diff --git a/drivers/irqchip/irq-bcm2712-mip.c b/drivers/irqchip/irq-bcm2712-mip.c new file mode 100644 index 000000000000..63de5ef6cf2d --- /dev/null +++ b/drivers/irqchip/irq-bcm2712-mip.c @@ -0,0 +1,293 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2024 Raspberry Pi Ltd., All Rights Reserved. + * Copyright (c) 2024 SUSE + */ + +#include <linux/bitmap.h> +#include <linux/irqchip.h> +#include <linux/irqdomain.h> +#include <linux/msi.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> + +#include <linux/irqchip/irq-msi-lib.h> + +#define MIP_INT_RAISE 0x00 +#define MIP_INT_CLEAR 0x10 +#define MIP_INT_CFGL_HOST 0x20 +#define MIP_INT_CFGH_HOST 0x30 +#define MIP_INT_MASKL_HOST 0x40 +#define MIP_INT_MASKH_HOST 0x50 +#define MIP_INT_MASKL_VPU 0x60 +#define MIP_INT_MASKH_VPU 0x70 +#define MIP_INT_STATUSL_HOST 0x80 +#define MIP_INT_STATUSH_HOST 0x90 +#define MIP_INT_STATUSL_VPU 0xa0 +#define MIP_INT_STATUSH_VPU 0xb0 + +/** + * struct mip_priv - MSI-X interrupt controller data + * @lock: Used to protect bitmap alloc/free + * @base: Base address of MMIO area + * @msg_addr: PCIe MSI-X address + * @msi_base: MSI base + * @num_msis: Count of MSIs + * @msi_offset: MSI offset + * @bitmap: A bitmap for hwirqs + * @parent: Parent domain (GIC) + * @dev: A device pointer + */ +struct mip_priv { + spinlock_t lock; + void __iomem *base; + u64 msg_addr; + u32 msi_base; + u32 num_msis; + u32 msi_offset; + unsigned long *bitmap; + struct irq_domain *parent; + struct device *dev; +}; + +static void mip_compose_msi_msg(struct irq_data *d, struct msi_msg *msg) +{ + struct mip_priv *mip = irq_data_get_irq_chip_data(d); + + msg->address_hi = upper_32_bits(mip->msg_addr); + msg->address_lo = lower_32_bits(mip->msg_addr); + msg->data = d->hwirq; +} + +static struct irq_chip mip_middle_irq_chip = { + .name = "MIP", + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_eoi = irq_chip_eoi_parent, + .irq_set_affinity = irq_chip_set_affinity_parent, + .irq_set_type = irq_chip_set_type_parent, + .irq_compose_msi_msg = mip_compose_msi_msg, +}; + +static int mip_alloc_hwirq(struct mip_priv *mip, unsigned int nr_irqs) +{ + guard(spinlock)(&mip->lock); + return bitmap_find_free_region(mip->bitmap, mip->num_msis, ilog2(nr_irqs)); +} + +static void mip_free_hwirq(struct mip_priv *mip, unsigned int hwirq, + unsigned int nr_irqs) +{ + guard(spinlock)(&mip->lock); + bitmap_release_region(mip->bitmap, hwirq, ilog2(nr_irqs)); +} + +static int mip_middle_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *arg) +{ + struct mip_priv *mip = domain->host_data; + struct irq_fwspec fwspec = {0}; + unsigned int hwirq, i; + struct irq_data *irqd; + int irq, ret; + + irq = mip_alloc_hwirq(mip, nr_irqs); + if (irq < 0) + return irq; + + hwirq = irq + mip->msi_offset; + + fwspec.fwnode = domain->parent->fwnode; + fwspec.param_count = 3; + fwspec.param[0] = 0; + fwspec.param[1] = hwirq + mip->msi_base; + fwspec.param[2] = IRQ_TYPE_EDGE_RISING; + + ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &fwspec); + if (ret) + goto err_free_hwirq; + + for (i = 0; i < nr_irqs; i++) { + irqd = irq_domain_get_irq_data(domain->parent, virq + i); + irqd->chip->irq_set_type(irqd, IRQ_TYPE_EDGE_RISING); + + ret = irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, + &mip_middle_irq_chip, mip); + if (ret) + goto err_free; + + irqd = irq_get_irq_data(virq + i); + irqd_set_single_target(irqd); + irqd_set_affinity_on_activate(irqd); + } + + return 0; + +err_free: + irq_domain_free_irqs_parent(domain, virq, nr_irqs); +err_free_hwirq: + mip_free_hwirq(mip, irq, nr_irqs); + return ret; +} + +static void mip_middle_domain_free(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs) +{ + struct irq_data *irqd = irq_domain_get_irq_data(domain, virq); + struct mip_priv *mip; + unsigned int hwirq; + + if (!irqd) + return; + + mip = irq_data_get_irq_chip_data(irqd); + hwirq = irqd_to_hwirq(irqd); + irq_domain_free_irqs_parent(domain, virq, nr_irqs); + mip_free_hwirq(mip, hwirq - mip->msi_offset, nr_irqs); +} + +static const struct irq_domain_ops mip_middle_domain_ops = { + .select = msi_lib_irq_domain_select, + .alloc = mip_middle_domain_alloc, + .free = mip_middle_domain_free, +}; + +#define MIP_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ + MSI_FLAG_USE_DEF_CHIP_OPS | \ + MSI_FLAG_PCI_MSI_MASK_PARENT) + +#define MIP_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \ + MSI_FLAG_MULTI_PCI_MSI | \ + MSI_FLAG_PCI_MSIX) + +static const struct msi_parent_ops mip_msi_parent_ops = { + .supported_flags = MIP_MSI_FLAGS_SUPPORTED, + .required_flags = MIP_MSI_FLAGS_REQUIRED, + .chip_flags = MSI_CHIP_FLAG_SET_EOI | MSI_CHIP_FLAG_SET_ACK, + .bus_select_token = DOMAIN_BUS_GENERIC_MSI, + .bus_select_mask = MATCH_PCI_MSI, + .prefix = "MIP-MSI-", + .init_dev_msi_info = msi_lib_init_dev_msi_info, +}; + +static int mip_init_domains(struct mip_priv *mip, struct device_node *np) +{ + struct irq_domain *middle; + + middle = irq_domain_create_hierarchy(mip->parent, 0, mip->num_msis, of_fwnode_handle(np), + &mip_middle_domain_ops, mip); + if (!middle) + return -ENOMEM; + + irq_domain_update_bus_token(middle, DOMAIN_BUS_GENERIC_MSI); + middle->dev = mip->dev; + middle->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT; + middle->msi_parent_ops = &mip_msi_parent_ops; + + /* + * All MSI-X unmasked for the host, masked for the VPU, and edge-triggered. + */ + writel(0, mip->base + MIP_INT_MASKL_HOST); + writel(0, mip->base + MIP_INT_MASKH_HOST); + writel(~0, mip->base + MIP_INT_MASKL_VPU); + writel(~0, mip->base + MIP_INT_MASKH_VPU); + writel(~0, mip->base + MIP_INT_CFGL_HOST); + writel(~0, mip->base + MIP_INT_CFGH_HOST); + + return 0; +} + +static int mip_parse_dt(struct mip_priv *mip, struct device_node *np) +{ + struct of_phandle_args args; + u64 size; + int ret; + + ret = of_property_read_u32(np, "brcm,msi-offset", &mip->msi_offset); + if (ret) + mip->msi_offset = 0; + + ret = of_parse_phandle_with_args(np, "msi-ranges", "#interrupt-cells", + 0, &args); + if (ret) + return ret; + + ret = of_property_read_u32_index(np, "msi-ranges", args.args_count + 1, + &mip->num_msis); + if (ret) + goto err_put; + + ret = of_property_read_reg(np, 1, &mip->msg_addr, &size); + if (ret) + goto err_put; + + mip->msi_base = args.args[1]; + + mip->parent = irq_find_host(args.np); + if (!mip->parent) + ret = -EINVAL; + +err_put: + of_node_put(args.np); + return ret; +} + +static int __init mip_of_msi_init(struct device_node *node, struct device_node *parent) +{ + struct platform_device *pdev; + struct mip_priv *mip; + int ret; + + pdev = of_find_device_by_node(node); + of_node_put(node); + if (!pdev) + return -EPROBE_DEFER; + + mip = kzalloc(sizeof(*mip), GFP_KERNEL); + if (!mip) + return -ENOMEM; + + spin_lock_init(&mip->lock); + mip->dev = &pdev->dev; + + ret = mip_parse_dt(mip, node); + if (ret) + goto err_priv; + + mip->base = of_iomap(node, 0); + if (!mip->base) { + ret = -ENXIO; + goto err_priv; + } + + mip->bitmap = bitmap_zalloc(mip->num_msis, GFP_KERNEL); + if (!mip->bitmap) { + ret = -ENOMEM; + goto err_base; + } + + ret = mip_init_domains(mip, node); + if (ret) + goto err_map; + + dev_dbg(&pdev->dev, "MIP: MSI-X count: %u, base: %u, offset: %u, msg_addr: %llx\n", + mip->num_msis, mip->msi_base, mip->msi_offset, mip->msg_addr); + + return 0; + +err_map: + bitmap_free(mip->bitmap); +err_base: + iounmap(mip->base); +err_priv: + kfree(mip); + return ret; +} + +IRQCHIP_PLATFORM_DRIVER_BEGIN(mip_msi) +IRQCHIP_MATCH("brcm,bcm2712-mip", mip_of_msi_init) +IRQCHIP_PLATFORM_DRIVER_END(mip_msi) +MODULE_DESCRIPTION("Broadcom BCM2712 MSI-X interrupt controller"); +MODULE_AUTHOR("Phil Elwell <phil@raspberrypi.com>"); +MODULE_AUTHOR("Stanimir Varbanov <svarbanov@suse.de>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/irqchip/irq-bcm2835.c b/drivers/irqchip/irq-bcm2835.c index 6c20604c2242..1e384c870350 100644 --- a/drivers/irqchip/irq-bcm2835.c +++ b/drivers/irqchip/irq-bcm2835.c @@ -144,7 +144,7 @@ static int __init armctrl_of_init(struct device_node *node, if (!base) panic("%pOF: unable to map IC registers\n", node); - intc.domain = irq_domain_add_linear(node, MAKE_HWIRQ(NR_BANKS, 0), + intc.domain = irq_domain_create_linear(of_fwnode_handle(node), MAKE_HWIRQ(NR_BANKS, 0), &armctrl_ops, NULL); if (!intc.domain) panic("%pOF: unable to create IRQ domain\n", node); diff --git a/drivers/irqchip/irq-bcm2836.c b/drivers/irqchip/irq-bcm2836.c index e366257684b5..fafd1f71348e 100644 --- a/drivers/irqchip/irq-bcm2836.c +++ b/drivers/irqchip/irq-bcm2836.c @@ -325,7 +325,7 @@ static int __init bcm2836_arm_irqchip_l1_intc_of_init(struct device_node *node, bcm2835_init_local_timer_frequency(); - intc.domain = irq_domain_add_linear(node, LAST_IRQ + 1, + intc.domain = irq_domain_create_linear(of_fwnode_handle(node), LAST_IRQ + 1, &bcm2836_arm_irqchip_intc_ops, NULL); if (!intc.domain) diff --git a/drivers/irqchip/irq-bcm6345-l1.c b/drivers/irqchip/irq-bcm6345-l1.c index 90daa274ef23..ca4e141c5bc2 100644 --- a/drivers/irqchip/irq-bcm6345-l1.c +++ b/drivers/irqchip/irq-bcm6345-l1.c @@ -316,7 +316,7 @@ static int __init bcm6345_l1_of_init(struct device_node *dn, raw_spin_lock_init(&intc->lock); - intc->domain = irq_domain_add_linear(dn, IRQS_PER_WORD * intc->n_words, + intc->domain = irq_domain_create_linear(of_fwnode_handle(dn), IRQS_PER_WORD * intc->n_words, &bcm6345_l1_domain_ops, intc); if (!intc->domain) { diff --git a/drivers/irqchip/irq-bcm7038-l1.c b/drivers/irqchip/irq-bcm7038-l1.c index 36e71af054e9..04fac0cc857f 100644 --- a/drivers/irqchip/irq-bcm7038-l1.c +++ b/drivers/irqchip/irq-bcm7038-l1.c @@ -416,7 +416,7 @@ static int __init bcm7038_l1_of_init(struct device_node *dn, } } - intc->domain = irq_domain_add_linear(dn, IRQS_PER_WORD * intc->n_words, + intc->domain = irq_domain_create_linear(of_fwnode_handle(dn), IRQS_PER_WORD * intc->n_words, &bcm7038_l1_domain_ops, intc); if (!intc->domain) { diff --git a/drivers/irqchip/irq-bcm7120-l2.c b/drivers/irqchip/irq-bcm7120-l2.c index 1e9dab6e0d86..ff22c3104401 100644 --- a/drivers/irqchip/irq-bcm7120-l2.c +++ b/drivers/irqchip/irq-bcm7120-l2.c @@ -63,16 +63,15 @@ static void bcm7120_l2_intc_irq_handle(struct irq_desc *desc) for (idx = 0; idx < b->n_words; idx++) { int base = idx * IRQS_PER_WORD; - struct irq_chip_generic *gc = - irq_get_domain_generic_chip(b->domain, base); + struct irq_chip_generic *gc; unsigned long pending; int hwirq; - irq_gc_lock(gc); - pending = irq_reg_readl(gc, b->stat_offset[idx]) & - gc->mask_cache & - data->irq_map_mask[idx]; - irq_gc_unlock(gc); + gc = irq_get_domain_generic_chip(b->domain, base); + scoped_guard (raw_spinlock, &gc->lock) { + pending = irq_reg_readl(gc, b->stat_offset[idx]) & gc->mask_cache & + data->irq_map_mask[idx]; + } for_each_set_bit(hwirq, &pending, IRQS_PER_WORD) generic_handle_domain_irq(b->domain, base + hwirq); @@ -86,11 +85,9 @@ static void bcm7120_l2_intc_suspend(struct irq_chip_generic *gc) struct bcm7120_l2_intc_data *b = gc->private; struct irq_chip_type *ct = gc->chip_types; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); if (b->can_wake) - irq_reg_writel(gc, gc->mask_cache | gc->wake_active, - ct->regs.mask); - irq_gc_unlock(gc); + irq_reg_writel(gc, gc->mask_cache | gc->wake_active, ct->regs.mask); } static void bcm7120_l2_intc_resume(struct irq_chip_generic *gc) @@ -98,9 +95,8 @@ static void bcm7120_l2_intc_resume(struct irq_chip_generic *gc) struct irq_chip_type *ct = gc->chip_types; /* Restore the saved mask */ - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); irq_reg_writel(gc, gc->mask_cache, ct->regs.mask); - irq_gc_unlock(gc); } static int bcm7120_l2_intc_init_one(struct device_node *dn, @@ -264,7 +260,7 @@ static int __init bcm7120_l2_intc_probe(struct device_node *dn, goto out_free_l1_data; } - data->domain = irq_domain_add_linear(dn, IRQS_PER_WORD * data->n_words, + data->domain = irq_domain_create_linear(of_fwnode_handle(dn), IRQS_PER_WORD * data->n_words, &irq_generic_chip_ops, NULL); if (!data->domain) { ret = -ENOMEM; diff --git a/drivers/irqchip/irq-brcmstb-l2.c b/drivers/irqchip/irq-brcmstb-l2.c index c988886917f7..1bec5b2cd3f0 100644 --- a/drivers/irqchip/irq-brcmstb-l2.c +++ b/drivers/irqchip/irq-brcmstb-l2.c @@ -61,32 +61,6 @@ struct brcmstb_l2_intc_data { u32 saved_mask; /* for suspend/resume */ }; -/** - * brcmstb_l2_mask_and_ack - Mask and ack pending interrupt - * @d: irq_data - * - * Chip has separate enable/disable registers instead of a single mask - * register and pending interrupt is acknowledged by setting a bit. - * - * Note: This function is generic and could easily be added to the - * generic irqchip implementation if there ever becomes a will to do so. - * Perhaps with a name like irq_gc_mask_disable_and_ack_set(). - * - * e.g.: https://patchwork.kernel.org/patch/9831047/ - */ -static void brcmstb_l2_mask_and_ack(struct irq_data *d) -{ - struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); - struct irq_chip_type *ct = irq_data_get_chip_type(d); - u32 mask = d->mask; - - irq_gc_lock(gc); - irq_reg_writel(gc, mask, ct->regs.disable); - *ct->mask_cache &= ~mask; - irq_reg_writel(gc, mask, ct->regs.ack); - irq_gc_unlock(gc); -} - static void brcmstb_l2_intc_irq_handle(struct irq_desc *desc) { struct brcmstb_l2_intc_data *b = irq_desc_get_handler_data(desc); @@ -123,9 +97,8 @@ static void __brcmstb_l2_intc_suspend(struct irq_data *d, bool save) struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); struct irq_chip_type *ct = irq_data_get_chip_type(d); struct brcmstb_l2_intc_data *b = gc->private; - unsigned long flags; - irq_gc_lock_irqsave(gc, flags); + guard(raw_spinlock_irqsave)(&gc->lock); /* Save the current mask */ if (save) b->saved_mask = irq_reg_readl(gc, ct->regs.mask); @@ -135,7 +108,6 @@ static void __brcmstb_l2_intc_suspend(struct irq_data *d, bool save) irq_reg_writel(gc, ~gc->wake_active, ct->regs.disable); irq_reg_writel(gc, gc->wake_active, ct->regs.enable); } - irq_gc_unlock_irqrestore(gc, flags); } static void brcmstb_l2_intc_shutdown(struct irq_data *d) @@ -153,9 +125,8 @@ static void brcmstb_l2_intc_resume(struct irq_data *d) struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); struct irq_chip_type *ct = irq_data_get_chip_type(d); struct brcmstb_l2_intc_data *b = gc->private; - unsigned long flags; - irq_gc_lock_irqsave(gc, flags); + guard(raw_spinlock_irqsave)(&gc->lock); if (ct->chip.irq_ack) { /* Clear unmasked non-wakeup interrupts */ irq_reg_writel(gc, ~b->saved_mask & ~gc->wake_active, @@ -165,7 +136,6 @@ static void brcmstb_l2_intc_resume(struct irq_data *d) /* Restore the saved mask */ irq_reg_writel(gc, b->saved_mask, ct->regs.disable); irq_reg_writel(gc, ~b->saved_mask, ct->regs.enable); - irq_gc_unlock_irqrestore(gc, flags); } static int __init brcmstb_l2_intc_of_init(struct device_node *np, @@ -208,7 +178,7 @@ static int __init brcmstb_l2_intc_of_init(struct device_node *np, goto out_unmap; } - data->domain = irq_domain_add_linear(np, 32, + data->domain = irq_domain_create_linear(of_fwnode_handle(np), 32, &irq_generic_chip_ops, NULL); if (!data->domain) { ret = -ENOMEM; @@ -248,7 +218,7 @@ static int __init brcmstb_l2_intc_of_init(struct device_node *np, if (init_params->cpu_clear >= 0) { ct->regs.ack = init_params->cpu_clear; ct->chip.irq_ack = irq_gc_ack_set_bit; - ct->chip.irq_mask_ack = brcmstb_l2_mask_and_ack; + ct->chip.irq_mask_ack = irq_gc_mask_disable_and_ack_set; } else { /* No Ack - but still slightly more efficient to define this */ ct->chip.irq_mask_ack = irq_gc_mask_disable_reg; diff --git a/drivers/irqchip/irq-clps711x.c b/drivers/irqchip/irq-clps711x.c index 806ebb1de201..c4b73ba2323b 100644 --- a/drivers/irqchip/irq-clps711x.c +++ b/drivers/irqchip/irq-clps711x.c @@ -184,14 +184,14 @@ static int __init _clps711x_intc_init(struct device_node *np, clps711x_intc->ops.map = clps711x_intc_irq_map; clps711x_intc->ops.xlate = irq_domain_xlate_onecell; clps711x_intc->domain = - irq_domain_add_legacy(np, ARRAY_SIZE(clps711x_irqs), - 0, 0, &clps711x_intc->ops, NULL); + irq_domain_create_legacy(of_fwnode_handle(np), ARRAY_SIZE(clps711x_irqs), 0, 0, + &clps711x_intc->ops, NULL); if (!clps711x_intc->domain) { err = -ENOMEM; goto out_irqfree; } - irq_set_default_host(clps711x_intc->domain); + irq_set_default_domain(clps711x_intc->domain); set_handle_irq(clps711x_irqh); #ifdef CONFIG_FIQ diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index a05a7501e107..66bb39e24a52 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -351,10 +351,8 @@ static int __init irqcrossbar_init(struct device_node *node, if (err) return err; - domain = irq_domain_add_hierarchy(parent_domain, 0, - cb->max_crossbar_sources, - node, &crossbar_domain_ops, - NULL); + domain = irq_domain_create_hierarchy(parent_domain, 0, cb->max_crossbar_sources, + of_fwnode_handle(node), &crossbar_domain_ops, NULL); if (!domain) { pr_err("%pOF: failed to allocated domain\n", node); return -ENOMEM; diff --git a/drivers/irqchip/irq-csky-apb-intc.c b/drivers/irqchip/irq-csky-apb-intc.c index 6710691e4c25..5b7150705d29 100644 --- a/drivers/irqchip/irq-csky-apb-intc.c +++ b/drivers/irqchip/irq-csky-apb-intc.c @@ -50,11 +50,10 @@ static void irq_ck_mask_set_bit(struct irq_data *d) unsigned long ifr = ct->regs.mask - 8; u32 mask = d->mask; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); *ct->mask_cache |= mask; irq_reg_writel(gc, *ct->mask_cache, ct->regs.mask); irq_reg_writel(gc, irq_reg_readl(gc, ifr) & ~mask, ifr); - irq_gc_unlock(gc); } static void __init ck_set_gc(struct device_node *node, void __iomem *reg_base, @@ -114,7 +113,7 @@ ck_intc_init_comm(struct device_node *node, struct device_node *parent) return -EINVAL; } - root_domain = irq_domain_add_linear(node, nr_irq, + root_domain = irq_domain_create_linear(of_fwnode_handle(node), nr_irq, &irq_generic_chip_ops, NULL); if (!root_domain) { pr_err("C-SKY Intc irq_domain_add failed.\n"); diff --git a/drivers/irqchip/irq-csky-mpintc.c b/drivers/irqchip/irq-csky-mpintc.c index 4aebd67d4f8f..1d1f5091f26f 100644 --- a/drivers/irqchip/irq-csky-mpintc.c +++ b/drivers/irqchip/irq-csky-mpintc.c @@ -255,7 +255,7 @@ csky_mpintc_init(struct device_node *node, struct device_node *parent) writel_relaxed(BIT(0), INTCG_base + INTCG_ICTLR); } - root_domain = irq_domain_add_linear(node, nr_irq, &csky_irqdomain_ops, + root_domain = irq_domain_create_linear(of_fwnode_handle(node), nr_irq, &csky_irqdomain_ops, NULL); if (!root_domain) return -ENXIO; diff --git a/drivers/irqchip/irq-davinci-cp-intc.c b/drivers/irqchip/irq-davinci-cp-intc.c index f4f8e9fadbbf..00cdcc90f614 100644 --- a/drivers/irqchip/irq-davinci-cp-intc.c +++ b/drivers/irqchip/irq-davinci-cp-intc.c @@ -11,7 +11,6 @@ #include <linux/init.h> #include <linux/irq.h> #include <linux/irqchip.h> -#include <linux/irqchip/irq-davinci-cp-intc.h> #include <linux/irqdomain.h> #include <linux/io.h> #include <linux/of.h> @@ -154,24 +153,20 @@ static const struct irq_domain_ops davinci_cp_intc_irq_domain_ops = { .xlate = irq_domain_xlate_onetwocell, }; -static int __init -davinci_cp_intc_do_init(const struct davinci_cp_intc_config *config, - struct device_node *node) +static int __init davinci_cp_intc_do_init(struct resource *res, unsigned int num_irqs, + struct device_node *node) { - unsigned int num_regs = BITS_TO_LONGS(config->num_irqs); + unsigned int num_regs = BITS_TO_LONGS(num_irqs); int offset, irq_base; void __iomem *req; - req = request_mem_region(config->reg.start, - resource_size(&config->reg), - "davinci-cp-intc"); + req = request_mem_region(res->start, resource_size(res), "davinci-cp-intc"); if (!req) { pr_err("%s: register range busy\n", __func__); return -EBUSY; } - davinci_cp_intc_base = ioremap(config->reg.start, - resource_size(&config->reg)); + davinci_cp_intc_base = ioremap(res->start, resource_size(res)); if (!davinci_cp_intc_base) { pr_err("%s: unable to ioremap register range\n", __func__); return -EINVAL; @@ -184,8 +179,7 @@ davinci_cp_intc_do_init(const struct davinci_cp_intc_config *config, /* Disable system interrupts */ for (offset = 0; offset < num_regs; offset++) - davinci_cp_intc_write(~0, - DAVINCI_CP_INTC_SYS_ENABLE_CLR(offset)); + davinci_cp_intc_write(~0, DAVINCI_CP_INTC_SYS_ENABLE_CLR(offset)); /* Set to normal mode, no nesting, no priority hold */ davinci_cp_intc_write(0, DAVINCI_CP_INTC_CTRL); @@ -193,28 +187,27 @@ davinci_cp_intc_do_init(const struct davinci_cp_intc_config *config, /* Clear system interrupt status */ for (offset = 0; offset < num_regs; offset++) - davinci_cp_intc_write(~0, - DAVINCI_CP_INTC_SYS_STAT_CLR(offset)); + davinci_cp_intc_write(~0, DAVINCI_CP_INTC_SYS_STAT_CLR(offset)); /* Enable nIRQ (what about nFIQ?) */ davinci_cp_intc_write(1, DAVINCI_CP_INTC_HOST_ENABLE_IDX_SET); + /* 4 channels per register */ + num_regs = (num_irqs + 3) >> 2; /* Default all priorities to channel 7. */ - num_regs = (config->num_irqs + 3) >> 2; /* 4 channels per register */ for (offset = 0; offset < num_regs; offset++) - davinci_cp_intc_write(0x07070707, - DAVINCI_CP_INTC_CHAN_MAP(offset)); + davinci_cp_intc_write(0x07070707, DAVINCI_CP_INTC_CHAN_MAP(offset)); - irq_base = irq_alloc_descs(-1, 0, config->num_irqs, 0); + irq_base = irq_alloc_descs(-1, 0, num_irqs, 0); if (irq_base < 0) { - pr_err("%s: unable to allocate interrupt descriptors: %d\n", - __func__, irq_base); + pr_err("%s: unable to allocate interrupt descriptors: %d\n", __func__, irq_base); return irq_base; } - davinci_cp_intc_irq_domain = irq_domain_add_legacy( - node, config->num_irqs, irq_base, 0, - &davinci_cp_intc_irq_domain_ops, NULL); + davinci_cp_intc_irq_domain = irq_domain_create_legacy(of_fwnode_handle(node), num_irqs, + irq_base, 0, + &davinci_cp_intc_irq_domain_ops, + NULL); if (!davinci_cp_intc_irq_domain) { pr_err("%s: unable to create an interrupt domain\n", __func__); @@ -229,31 +222,25 @@ davinci_cp_intc_do_init(const struct davinci_cp_intc_config *config, return 0; } -int __init davinci_cp_intc_init(const struct davinci_cp_intc_config *config) -{ - return davinci_cp_intc_do_init(config, NULL); -} - static int __init davinci_cp_intc_of_init(struct device_node *node, struct device_node *parent) { - struct davinci_cp_intc_config config = { }; + unsigned int num_irqs; + struct resource res; int ret; - ret = of_address_to_resource(node, 0, &config.reg); + ret = of_address_to_resource(node, 0, &res); if (ret) { - pr_err("%s: unable to get the register range from device-tree\n", - __func__); + pr_err("%s: unable to get the register range from device-tree\n", __func__); return ret; } - ret = of_property_read_u32(node, "ti,intc-size", &config.num_irqs); + ret = of_property_read_u32(node, "ti,intc-size", &num_irqs); if (ret) { - pr_err("%s: unable to read the 'ti,intc-size' property\n", - __func__); + pr_err("%s: unable to read the 'ti,intc-size' property\n", __func__); return ret; } - return davinci_cp_intc_do_init(&config, node); + return davinci_cp_intc_do_init(&res, num_irqs, node); } IRQCHIP_DECLARE(cp_intc, "ti,cp-intc", davinci_cp_intc_of_init); diff --git a/drivers/irqchip/irq-digicolor.c b/drivers/irqchip/irq-digicolor.c index 3b0d78aac13b..eb5a8de82751 100644 --- a/drivers/irqchip/irq-digicolor.c +++ b/drivers/irqchip/irq-digicolor.c @@ -95,7 +95,7 @@ static int __init digicolor_of_init(struct device_node *node, regmap_write(ucregs, UC_IRQ_CONTROL, 1); digicolor_irq_domain = - irq_domain_add_linear(node, 64, &irq_generic_chip_ops, NULL); + irq_domain_create_linear(of_fwnode_handle(node), 64, &irq_generic_chip_ops, NULL); if (!digicolor_irq_domain) { pr_err("%pOF: unable to create IRQ domain\n", node); return -ENOMEM; diff --git a/drivers/irqchip/irq-dw-apb-ictl.c b/drivers/irqchip/irq-dw-apb-ictl.c index d5c1c750c8d2..4240a0dbf627 100644 --- a/drivers/irqchip/irq-dw-apb-ictl.c +++ b/drivers/irqchip/irq-dw-apb-ictl.c @@ -101,10 +101,9 @@ static void dw_apb_ictl_resume(struct irq_data *d) struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); struct irq_chip_type *ct = irq_data_get_chip_type(d); - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); writel_relaxed(~0, gc->reg_base + ct->regs.enable); writel_relaxed(*ct->mask_cache, gc->reg_base + ct->regs.mask); - irq_gc_unlock(gc); } #else #define dw_apb_ictl_resume NULL @@ -173,7 +172,7 @@ static int __init dw_apb_ictl_init(struct device_node *np, else nrirqs = fls(readl_relaxed(iobase + APB_INT_ENABLE_L)); - domain = irq_domain_add_linear(np, nrirqs, domain_ops, NULL); + domain = irq_domain_create_linear(of_fwnode_handle(np), nrirqs, domain_ops, NULL); if (!domain) { pr_err("%pOF: unable to add irq domain\n", np); ret = -ENOMEM; diff --git a/drivers/irqchip/irq-econet-en751221.c b/drivers/irqchip/irq-econet-en751221.c new file mode 100644 index 000000000000..d83d5eb12795 --- /dev/null +++ b/drivers/irqchip/irq-econet-en751221.c @@ -0,0 +1,310 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * EN751221 Interrupt Controller Driver. + * + * The EcoNet EN751221 Interrupt Controller is a simple interrupt controller + * designed for the MIPS 34Kc MT SMP processor with 2 VPEs. Each interrupt can + * be routed to either VPE but not both, so to support per-CPU interrupts, a + * secondary IRQ number is allocated to control masking/unmasking on VPE#1. In + * this driver, these are called "shadow interrupts". The assignment of shadow + * interrupts is defined by the SoC integrator when wiring the interrupt lines, + * so they are configurable in the device tree. + * + * If an interrupt (say 30) needs per-CPU capability, the SoC integrator + * allocates another IRQ number (say 29) to be its shadow. The device tree + * reflects this by adding the pair <30 29> to the "econet,shadow-interrupts" + * property. + * + * When VPE#1 requests IRQ 30, the driver manipulates the mask bit for IRQ 29, + * telling the hardware to mask VPE#1's view of IRQ 30. + * + * Copyright (C) 2025 Caleb James DeLisle <cjd@cjdns.fr> + */ + +#include <linux/cleanup.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/irqdomain.h> +#include <linux/irqchip.h> +#include <linux/irqchip/chained_irq.h> + +#define IRQ_COUNT 40 + +#define NOT_PERCPU 0xff +#define IS_SHADOW 0xfe + +#define REG_MASK0 0x04 +#define REG_MASK1 0x50 +#define REG_PENDING0 0x08 +#define REG_PENDING1 0x54 + +/** + * @membase: Base address of the interrupt controller registers + * @interrupt_shadows: Array of all interrupts, for each value, + * - NOT_PERCPU: This interrupt is not per-cpu, so it has no shadow + * - IS_SHADOW: This interrupt is a shadow of another per-cpu interrupt + * - else: This is a per-cpu interrupt whose shadow is the value + */ +static struct { + void __iomem *membase; + u8 interrupt_shadows[IRQ_COUNT]; +} econet_intc __ro_after_init; + +static DEFINE_RAW_SPINLOCK(irq_lock); + +/* IRQs must be disabled */ +static void econet_wreg(u32 reg, u32 val, u32 mask) +{ + u32 v; + + guard(raw_spinlock)(&irq_lock); + + v = ioread32(econet_intc.membase + reg); + v &= ~mask; + v |= val & mask; + iowrite32(v, econet_intc.membase + reg); +} + +/* IRQs must be disabled */ +static void econet_chmask(u32 hwirq, bool unmask) +{ + u32 reg, mask; + u8 shadow; + + /* + * If the IRQ is a shadow, it should never be manipulated directly. + * It should only be masked/unmasked as a result of the "real" per-cpu + * irq being manipulated by a thread running on VPE#1. + * If it is per-cpu (has a shadow), and we're on VPE#1, the shadow is what we mask. + * This is single processor only, so smp_processor_id() never exceeds 1. + */ + shadow = econet_intc.interrupt_shadows[hwirq]; + if (WARN_ON_ONCE(shadow == IS_SHADOW)) + return; + else if (shadow != NOT_PERCPU && smp_processor_id() == 1) + hwirq = shadow; + + if (hwirq >= 32) { + reg = REG_MASK1; + mask = BIT(hwirq - 32); + } else { + reg = REG_MASK0; + mask = BIT(hwirq); + } + + econet_wreg(reg, unmask ? mask : 0, mask); +} + +/* IRQs must be disabled */ +static void econet_intc_mask(struct irq_data *d) +{ + econet_chmask(d->hwirq, false); +} + +/* IRQs must be disabled */ +static void econet_intc_unmask(struct irq_data *d) +{ + econet_chmask(d->hwirq, true); +} + +static void econet_mask_all(void) +{ + /* IRQs are generally disabled during init, but guarding here makes it non-obligatory. */ + guard(irqsave)(); + econet_wreg(REG_MASK0, 0, ~0); + econet_wreg(REG_MASK1, 0, ~0); +} + +static void econet_intc_handle_pending(struct irq_domain *d, u32 pending, u32 offset) +{ + int hwirq; + + while (pending) { + hwirq = fls(pending) - 1; + generic_handle_domain_irq(d, hwirq + offset); + pending &= ~BIT(hwirq); + } +} + +static void econet_intc_from_parent(struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + struct irq_domain *domain; + u32 pending0, pending1; + + chained_irq_enter(chip, desc); + + pending0 = ioread32(econet_intc.membase + REG_PENDING0); + pending1 = ioread32(econet_intc.membase + REG_PENDING1); + + if (unlikely(!(pending0 | pending1))) { + spurious_interrupt(); + } else { + domain = irq_desc_get_handler_data(desc); + econet_intc_handle_pending(domain, pending0, 0); + econet_intc_handle_pending(domain, pending1, 32); + } + + chained_irq_exit(chip, desc); +} + +static const struct irq_chip econet_irq_chip; + +static int econet_intc_map(struct irq_domain *d, u32 irq, irq_hw_number_t hwirq) +{ + int ret; + + if (hwirq >= IRQ_COUNT) { + pr_err("%s: hwirq %lu out of range\n", __func__, hwirq); + return -EINVAL; + } else if (econet_intc.interrupt_shadows[hwirq] == IS_SHADOW) { + pr_err("%s: can't map hwirq %lu, it is a shadow interrupt\n", __func__, hwirq); + return -EINVAL; + } + + if (econet_intc.interrupt_shadows[hwirq] == NOT_PERCPU) { + irq_set_chip_and_handler(irq, &econet_irq_chip, handle_level_irq); + } else { + irq_set_chip_and_handler(irq, &econet_irq_chip, handle_percpu_devid_irq); + ret = irq_set_percpu_devid(irq); + if (ret) + pr_warn("%s: Failed irq_set_percpu_devid for %u: %d\n", d->name, irq, ret); + } + + irq_set_chip_data(irq, NULL); + return 0; +} + +static const struct irq_chip econet_irq_chip = { + .name = "en751221-intc", + .irq_unmask = econet_intc_unmask, + .irq_mask = econet_intc_mask, + .irq_mask_ack = econet_intc_mask, +}; + +static const struct irq_domain_ops econet_domain_ops = { + .xlate = irq_domain_xlate_onecell, + .map = econet_intc_map +}; + +static int __init get_shadow_interrupts(struct device_node *node) +{ + const char *field = "econet,shadow-interrupts"; + int num_shadows; + + num_shadows = of_property_count_u32_elems(node, field); + + memset(econet_intc.interrupt_shadows, NOT_PERCPU, + sizeof(econet_intc.interrupt_shadows)); + + if (num_shadows <= 0) { + return 0; + } else if (num_shadows % 2) { + pr_err("%pOF: %s count is odd, ignoring\n", node, field); + return 0; + } + + u32 *shadows __free(kfree) = kmalloc_array(num_shadows, sizeof(u32), GFP_KERNEL); + if (!shadows) + return -ENOMEM; + + if (of_property_read_u32_array(node, field, shadows, num_shadows)) { + pr_err("%pOF: Failed to read %s\n", node, field); + return -EINVAL; + } + + for (int i = 0; i < num_shadows; i += 2) { + u32 shadow = shadows[i + 1]; + u32 target = shadows[i]; + + if (shadow > IRQ_COUNT) { + pr_err("%pOF: %s[%d] shadow(%d) out of range\n", + node, field, i + 1, shadow); + continue; + } + + if (target >= IRQ_COUNT) { + pr_err("%pOF: %s[%d] target(%d) out of range\n", node, field, i, target); + continue; + } + + if (econet_intc.interrupt_shadows[target] != NOT_PERCPU) { + pr_err("%pOF: %s[%d] target(%d) already has a shadow\n", + node, field, i, target); + continue; + } + + if (econet_intc.interrupt_shadows[shadow] != NOT_PERCPU) { + pr_err("%pOF: %s[%d] shadow(%d) already has a target\n", + node, field, i + 1, shadow); + continue; + } + + econet_intc.interrupt_shadows[target] = shadow; + econet_intc.interrupt_shadows[shadow] = IS_SHADOW; + } + + return 0; +} + +static int __init econet_intc_of_init(struct device_node *node, struct device_node *parent) +{ + struct irq_domain *domain; + struct resource res; + int ret, irq; + + ret = get_shadow_interrupts(node); + if (ret) + return ret; + + irq = irq_of_parse_and_map(node, 0); + if (!irq) { + pr_err("%pOF: DT: Failed to get IRQ from 'interrupts'\n", node); + return -EINVAL; + } + + if (of_address_to_resource(node, 0, &res)) { + pr_err("%pOF: DT: Failed to get 'reg'\n", node); + ret = -EINVAL; + goto err_dispose_mapping; + } + + if (!request_mem_region(res.start, resource_size(&res), res.name)) { + pr_err("%pOF: Failed to request memory\n", node); + ret = -EBUSY; + goto err_dispose_mapping; + } + + econet_intc.membase = ioremap(res.start, resource_size(&res)); + if (!econet_intc.membase) { + pr_err("%pOF: Failed to remap membase\n", node); + ret = -ENOMEM; + goto err_release; + } + + econet_mask_all(); + + domain = irq_domain_create_linear(of_fwnode_handle(node), IRQ_COUNT, + &econet_domain_ops, NULL); + if (!domain) { + pr_err("%pOF: Failed to add irqdomain\n", node); + ret = -ENOMEM; + goto err_unmap; + } + + irq_set_chained_handler_and_data(irq, econet_intc_from_parent, domain); + + return 0; + +err_unmap: + iounmap(econet_intc.membase); +err_release: + release_mem_region(res.start, resource_size(&res)); +err_dispose_mapping: + irq_dispose_mapping(irq); + return ret; +} + +IRQCHIP_DECLARE(econet_en751221_intc, "econet,en751221-intc", econet_intc_of_init); diff --git a/drivers/irqchip/irq-ftintc010.c b/drivers/irqchip/irq-ftintc010.c index b91c358ea6db..a59a66d79da6 100644 --- a/drivers/irqchip/irq-ftintc010.c +++ b/drivers/irqchip/irq-ftintc010.c @@ -180,8 +180,9 @@ static int __init ft010_of_init_irq(struct device_node *node, writel(0, FT010_IRQ_MASK(f->base)); writel(0, FT010_FIQ_MASK(f->base)); - f->domain = irq_domain_add_simple(node, FT010_NUM_IRQS, 0, - &ft010_irqdomain_ops, f); + f->domain = irq_domain_create_simple(of_fwnode_handle(node), + FT010_NUM_IRQS, 0, + &ft010_irqdomain_ops, f); set_handle_irq(ft010_irqchip_handle_irq); return 0; diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index be35c5349986..24ef5af569fe 100644 --- a/drivers/irqchip/irq-gic-v2m.c +++ b/drivers/irqchip/irq-gic-v2m.c @@ -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: @@ -87,9 +87,6 @@ static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) struct v2m_data *v2m = irq_data_get_irq_chip_data(data); phys_addr_t addr = gicv2m_get_msi_addr(v2m, data->hwirq); - msg->address_hi = upper_32_bits(addr); - msg->address_lo = lower_32_bits(addr); - if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY) msg->data = 0; else @@ -97,7 +94,7 @@ static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET) msg->data -= v2m->spi_offset; - iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), msg); + msi_msg_set_addr(irq_data_get_msi_desc(data), msg, addr); } static struct irq_chip gicv2m_irq_chip = { @@ -255,6 +252,7 @@ static void __init gicv2m_teardown(void) static struct msi_parent_ops gicv2m_msi_parent_ops = { .supported_flags = GICV2M_MSI_FLAGS_SUPPORTED, .required_flags = GICV2M_MSI_FLAGS_REQUIRED, + .chip_flags = MSI_CHIP_FLAG_SET_EOI, .bus_select_token = DOMAIN_BUS_NEXUS, .bus_select_mask = MATCH_PCI_MSI | MATCH_PLATFORM_MSI, .prefix = "GICv2m-", @@ -263,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; } @@ -423,7 +421,7 @@ static int __init gicv2m_of_init(struct fwnode_handle *parent_handle, #ifdef CONFIG_ACPI static int acpi_num_msi; -static __init struct fwnode_handle *gicv2m_get_fwnode(struct device *dev) +static struct fwnode_handle *gicv2m_get_fwnode(struct device *dev) { struct v2m_data *data; diff --git a/drivers/irqchip/irq-gic-v3-its-fsl-mc-msi.c b/drivers/irqchip/irq-gic-v3-its-fsl-mc-msi.c index 8e87fc35f8aa..11549d85f23b 100644 --- a/drivers/irqchip/irq-gic-v3-its-fsl-mc-msi.c +++ b/drivers/irqchip/irq-gic-v3-its-fsl-mc-msi.c @@ -152,7 +152,7 @@ static void __init its_fsl_mc_of_msi_init(void) if (!of_property_read_bool(np, "msi-controller")) continue; - its_fsl_mc_msi_init_one(of_node_to_fwnode(np), + its_fsl_mc_msi_init_one(of_fwnode_handle(np), np->full_name); } } diff --git a/drivers/irqchip/irq-gic-v3-its-msi-parent.c b/drivers/irqchip/irq-gic-v3-its-msi-parent.c index e150365fbe89..a5e110ffdd88 100644 --- a/drivers/irqchip/irq-gic-v3-its-msi-parent.c +++ b/drivers/irqchip/irq-gic-v3-its-msi-parent.c @@ -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 | \ @@ -68,17 +68,6 @@ static int its_pci_msi_prepare(struct irq_domain *domain, struct device *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 * appear as DevID 0). For that reason, we generously allocate a @@ -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? */ @@ -203,6 +202,7 @@ static bool its_init_dev_msi_info(struct device *dev, struct irq_domain *domain, const struct msi_parent_ops gic_v3_its_msi_parent_ops = { .supported_flags = ITS_MSI_FLAGS_SUPPORTED, .required_flags = ITS_MSI_FLAGS_REQUIRED, + .chip_flags = MSI_CHIP_FLAG_SET_EOI, .bus_select_token = DOMAIN_BUS_NEXUS, .bus_select_mask = MATCH_PCI_MSI | MATCH_PLATFORM_MSI, .prefix = "ITS-", diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 8c3ec5734f1e..d54fa0638dc4 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -41,7 +41,7 @@ #include <asm/exception.h> #include "irq-gic-common.h" -#include "irq-msi-lib.h" +#include <linux/irqchip/irq-msi-lib.h> #define ITS_FLAGS_CMDQ_NEEDS_FLUSHING (1ULL << 0) #define ITS_FLAGS_WORKAROUND_CAVIUM_22375 (1ULL << 1) @@ -125,6 +125,8 @@ struct its_node { int vlpi_redist_offset; }; +static DEFINE_PER_CPU(struct its_node *, local_4_1_its); + #define is_v4(its) (!!((its)->typer & GITS_TYPER_VLPIS)) #define is_v4_1(its) (!!((its)->typer & GITS_TYPER_VMAPP)) #define device_ids(its) (FIELD_GET(GITS_TYPER_DEVBITS, (its)->typer) + 1) @@ -205,13 +207,15 @@ static DEFINE_IDA(its_vpeid_ida); #define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base) #define gic_data_rdist_vlpi_base() (gic_data_rdist_rd_base() + SZ_128K) +static gfp_t gfp_flags_quirk; + static struct page *its_alloc_pages_node(int node, gfp_t gfp, unsigned int order) { struct page *page; int ret = 0; - page = alloc_pages_node(node, gfp, order); + page = alloc_pages_node(node, gfp | gfp_flags_quirk, order); if (!page) return NULL; @@ -1809,17 +1813,10 @@ static u64 its_irq_get_msi_base(struct its_device *its_dev) static void its_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg) { struct its_device *its_dev = irq_data_get_irq_chip_data(d); - struct its_node *its; - u64 addr; - its = its_dev->its; - addr = its->get_msi_base(its_dev); - - msg->address_lo = lower_32_bits(addr); - msg->address_hi = upper_32_bits(addr); - msg->data = its_get_event_id(d); - - iommu_dma_compose_msi_msg(irq_data_get_msi_desc(d), msg); + msg->data = its_get_event_id(d); + msi_msg_set_addr(irq_data_get_msi_desc(d), msg, + its_dev->its->get_msi_base(its_dev)); } static int its_irq_set_irqchip_state(struct irq_data *d, @@ -2783,6 +2780,7 @@ static u64 inherit_vpe_l1_table_from_its(void) } val |= FIELD_PREP(GICR_VPROPBASER_4_1_SIZE, GITS_BASER_NR_PAGES(baser) - 1); + *this_cpu_ptr(&local_4_1_its) = its; return val; } @@ -2820,6 +2818,7 @@ static u64 inherit_vpe_l1_table_from_rd(cpumask_t **mask) gic_data_rdist()->vpe_l1_base = gic_data_rdist_cpu(cpu)->vpe_l1_base; *mask = gic_data_rdist_cpu(cpu)->vpe_table_mask; + *this_cpu_ptr(&local_4_1_its) = *per_cpu_ptr(&local_4_1_its, cpu); return val; } @@ -3625,8 +3624,33 @@ out: return err; } +static void its_msi_teardown(struct irq_domain *domain, msi_alloc_info_t *info) +{ + struct its_device *its_dev = info->scratchpad[0].ptr; + + guard(mutex)(&its_dev->its->dev_alloc_lock); + + /* If the device is shared, keep everything around */ + if (its_dev->shared) + return; + + /* LPIs should have been already unmapped at this stage */ + if (WARN_ON_ONCE(!bitmap_empty(its_dev->event_map.lpi_map, + its_dev->event_map.nr_lpis))) + return; + + its_lpi_free(its_dev->event_map.lpi_map, + its_dev->event_map.lpi_base, + its_dev->event_map.nr_lpis); + + /* Unmap device/itt, and get rid of the tracking */ + its_send_mapd(its_dev, 0); + its_free_device(its_dev); +} + static struct msi_domain_ops its_msi_domain_ops = { .msi_prepare = its_msi_prepare, + .msi_teardown = its_msi_teardown, }; static int its_irq_gic_domain_alloc(struct irq_domain *domain, @@ -3727,7 +3751,6 @@ static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq, { struct irq_data *d = irq_domain_get_irq_data(domain, virq); struct its_device *its_dev = irq_data_get_irq_chip_data(d); - struct its_node *its = its_dev->its; int i; bitmap_release_region(its_dev->event_map.lpi_map, @@ -3741,26 +3764,6 @@ static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq, irq_domain_reset_irq_data(data); } - mutex_lock(&its->dev_alloc_lock); - - /* - * If all interrupts have been freed, start mopping the - * floor. This is conditioned on the device not being shared. - */ - if (!its_dev->shared && - bitmap_empty(its_dev->event_map.lpi_map, - its_dev->event_map.nr_lpis)) { - its_lpi_free(its_dev->event_map.lpi_map, - its_dev->event_map.lpi_base, - its_dev->event_map.nr_lpis); - - /* Unmap device/itt */ - its_send_mapd(its_dev, 0); - its_free_device(its_dev); - } - - mutex_unlock(&its->dev_alloc_lock); - irq_domain_free_irqs_parent(domain, virq, nr_irqs); } @@ -4185,7 +4188,7 @@ static struct irq_chip its_vpe_irq_chip = { static struct its_node *find_4_1_its(void) { - static struct its_node *its = NULL; + struct its_node *its = *this_cpu_ptr(&local_4_1_its); if (!its) { list_for_each_entry(its, &its_nodes, entry) { @@ -4887,6 +4890,17 @@ static bool __maybe_unused its_enable_quirk_hip09_162100801(void *data) return true; } +static bool __maybe_unused its_enable_rk3568002(void *data) +{ + if (!of_machine_is_compatible("rockchip,rk3566") && + !of_machine_is_compatible("rockchip,rk3568")) + return false; + + gfp_flags_quirk |= GFP_DMA32; + + return true; +} + static const struct gic_quirk its_quirks[] = { #ifdef CONFIG_CAVIUM_ERRATUM_22375 { @@ -4954,6 +4968,14 @@ static const struct gic_quirk its_quirks[] = { .property = "dma-noncoherent", .init = its_set_non_coherent, }, +#ifdef CONFIG_ROCKCHIP_ERRATUM_3568002 + { + .desc = "ITS: Rockchip erratum RK3568002", + .iidr = 0x0201743b, + .mask = 0xffffffff, + .init = its_enable_rk3568002, + }, +#endif { } }; @@ -5104,7 +5126,12 @@ out_unmap: static int its_init_domain(struct its_node *its) { - struct irq_domain *inner_domain; + struct irq_domain_info dom_info = { + .fwnode = its->fwnode_handle, + .ops = &its_domain_ops, + .domain_flags = its->msi_domain_flags, + .parent = its_parent, + }; struct msi_domain_info *info; info = kzalloc(sizeof(*info), GFP_KERNEL); @@ -5113,21 +5140,12 @@ static int its_init_domain(struct its_node *its) info->ops = &its_msi_domain_ops; info->data = its; + dom_info.host_data = info; - inner_domain = irq_domain_create_hierarchy(its_parent, - its->msi_domain_flags, 0, - its->fwnode_handle, &its_domain_ops, - info); - if (!inner_domain) { + if (!msi_create_parent_irq_domain(&dom_info, &gic_v3_its_msi_parent_ops)) { kfree(info); return -ENOMEM; } - - irq_domain_update_bus_token(inner_domain, DOMAIN_BUS_NEXUS); - - inner_domain->msi_parent_ops = &gic_v3_its_msi_parent_ops; - inner_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT; - return 0; } @@ -5504,7 +5522,7 @@ static struct its_node __init *its_node_init(struct resource *res, its->base = its_base; its->phys_base = res->start; its->get_msi_base = its_irq_get_msi_base; - its->msi_domain_flags = IRQ_DOMAIN_FLAG_ISOLATED_MSI; + its->msi_domain_flags = IRQ_DOMAIN_FLAG_ISOLATED_MSI | IRQ_DOMAIN_FLAG_MSI_IMMUTABLE; its->numa_node = numa_node; its->fwnode_handle = handle; diff --git a/drivers/irqchip/irq-gic-v3-mbi.c b/drivers/irqchip/irq-gic-v3-mbi.c index 3fe870f8ee17..aa11bbe8026a 100644 --- a/drivers/irqchip/irq-gic-v3-mbi.c +++ b/drivers/irqchip/irq-gic-v3-mbi.c @@ -18,7 +18,7 @@ #include <linux/irqchip/arm-gic-v3.h> -#include "irq-msi-lib.h" +#include <linux/irqchip/irq-msi-lib.h> struct mbi_range { u32 spi_start; @@ -147,22 +147,18 @@ static const struct irq_domain_ops mbi_domain_ops = { static void mbi_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) { - msg[0].address_hi = upper_32_bits(mbi_phys_base + GICD_SETSPI_NSR); - msg[0].address_lo = lower_32_bits(mbi_phys_base + GICD_SETSPI_NSR); msg[0].data = data->parent_data->hwirq; - - iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), msg); + msi_msg_set_addr(irq_data_get_msi_desc(data), &msg[0], + mbi_phys_base + GICD_SETSPI_NSR); } static void mbi_compose_mbi_msg(struct irq_data *data, struct msi_msg *msg) { mbi_compose_msi_msg(data, msg); - msg[1].address_hi = upper_32_bits(mbi_phys_base + GICD_CLRSPI_NSR); - msg[1].address_lo = lower_32_bits(mbi_phys_base + GICD_CLRSPI_NSR); msg[1].data = data->parent_data->hwirq; - - iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), &msg[1]); + msi_msg_set_addr(irq_data_get_msi_desc(data), &msg[1], + mbi_phys_base + GICD_CLRSPI_NSR); } static bool mbi_init_dev_msi_info(struct device *dev, struct irq_domain *domain, @@ -201,6 +197,7 @@ static bool mbi_init_dev_msi_info(struct device *dev, struct irq_domain *domain, static const struct msi_parent_ops gic_v3_mbi_msi_parent_ops = { .supported_flags = MBI_MSI_FLAGS_SUPPORTED, .required_flags = MBI_MSI_FLAGS_REQUIRED, + .chip_flags = MSI_CHIP_FLAG_SET_EOI, .bus_select_token = DOMAIN_BUS_NEXUS, .bus_select_mask = MATCH_PCI_MSI | MATCH_PLATFORM_MSI, .prefix = "MBI-", @@ -209,17 +206,13 @@ static const struct msi_parent_ops gic_v3_mbi_msi_parent_ops = { static int mbi_allocate_domain(struct irq_domain *parent) { - struct irq_domain *nexus_domain; + struct irq_domain_info info = { + .fwnode = parent->fwnode, + .ops = &mbi_domain_ops, + .parent = parent, + }; - nexus_domain = irq_domain_create_hierarchy(parent, 0, 0, parent->fwnode, - &mbi_domain_ops, NULL); - if (!nexus_domain) - return -ENOMEM; - - irq_domain_update_bus_token(nexus_domain, DOMAIN_BUS_NEXUS); - nexus_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT; - nexus_domain->msi_parent_ops = &gic_v3_mbi_msi_parent_ops; - return 0; + return msi_create_parent_irq_domain(&info, &gic_v3_mbi_msi_parent_ops) ? 0 : -ENOMEM; } int __init mbi_init(struct fwnode_handle *fwnode, struct irq_domain *parent) diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 270d7a4d85a6..efc791c43d44 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -1826,7 +1826,7 @@ static int partition_domain_translate(struct irq_domain *d, ppi_idx = __gic_get_ppi_index(ppi_intid); ret = partition_translate_id(gic_data.ppi_descs[ppi_idx], - of_node_to_fwnode(np)); + of_fwnode_handle(np)); if (ret < 0) return ret; @@ -2192,7 +2192,7 @@ static void __init gic_populate_ppi_partitions(struct device_node *gic_node) part = &parts[part_idx]; - part->partition_id = of_node_to_fwnode(child_part); + part->partition_id = of_fwnode_handle(child_part); pr_info("GIC: PPI partition %pOFn[%d] { ", child_part, part_idx); diff --git a/drivers/irqchip/irq-goldfish-pic.c b/drivers/irqchip/irq-goldfish-pic.c index 513f6edbbe95..a8b23b507ecd 100644 --- a/drivers/irqchip/irq-goldfish-pic.c +++ b/drivers/irqchip/irq-goldfish-pic.c @@ -101,10 +101,9 @@ static int __init goldfish_pic_of_init(struct device_node *of_node, irq_setup_generic_chip(gc, IRQ_MSK(GFPIC_NR_IRQS), 0, IRQ_NOPROBE | IRQ_LEVEL, 0); - gfpic->irq_domain = irq_domain_add_legacy(of_node, GFPIC_NR_IRQS, - GFPIC_IRQ_BASE, 0, - &goldfish_irq_domain_ops, - NULL); + gfpic->irq_domain = irq_domain_create_legacy(of_fwnode_handle(of_node), GFPIC_NR_IRQS, + GFPIC_IRQ_BASE, 0, &goldfish_irq_domain_ops, + NULL); if (!gfpic->irq_domain) { pr_err("Failed to add irqdomain!\n"); ret = -ENOMEM; diff --git a/drivers/irqchip/irq-hip04.c b/drivers/irqchip/irq-hip04.c index 31c3f70a5d5e..b7958c5a1221 100644 --- a/drivers/irqchip/irq-hip04.c +++ b/drivers/irqchip/irq-hip04.c @@ -386,10 +386,8 @@ hip04_of_init(struct device_node *node, struct device_node *parent) return -EINVAL; } - hip04_data.domain = irq_domain_add_legacy(node, nr_irqs, irq_base, - 0, - &hip04_irq_domain_ops, - &hip04_data); + hip04_data.domain = irq_domain_create_legacy(of_fwnode_handle(node), nr_irqs, irq_base, 0, + &hip04_irq_domain_ops, &hip04_data); if (WARN_ON(!hip04_data.domain)) return -EINVAL; diff --git a/drivers/irqchip/irq-i8259.c b/drivers/irqchip/irq-i8259.c index 115bdcffab24..91b2f587119c 100644 --- a/drivers/irqchip/irq-i8259.c +++ b/drivers/irqchip/irq-i8259.c @@ -313,8 +313,8 @@ struct irq_domain * __init __init_i8259_irqs(struct device_node *node) init_8259A(0); - domain = irq_domain_add_legacy(node, 16, I8259A_IRQ_BASE, 0, - &i8259A_ops, NULL); + domain = irq_domain_create_legacy(of_fwnode_handle(node), 16, I8259A_IRQ_BASE, 0, + &i8259A_ops, NULL); if (!domain) panic("Failed to add i8259 IRQ domain"); diff --git a/drivers/irqchip/irq-idt3243x.c b/drivers/irqchip/irq-idt3243x.c index 0732a0e9af62..f8324fb1fe8f 100644 --- a/drivers/irqchip/irq-idt3243x.c +++ b/drivers/irqchip/irq-idt3243x.c @@ -72,7 +72,7 @@ static int idt_pic_init(struct device_node *of_node, struct device_node *parent) goto out_unmap_irq; } - domain = irq_domain_add_linear(of_node, IDT_PIC_NR_IRQS, + domain = irq_domain_create_linear(of_fwnode_handle(of_node), IDT_PIC_NR_IRQS, &irq_generic_chip_ops, NULL); if (!domain) { pr_err("Failed to add irqdomain!\n"); diff --git a/drivers/irqchip/irq-imgpdc.c b/drivers/irqchip/irq-imgpdc.c index 85f80bac0961..f0410d5d7315 100644 --- a/drivers/irqchip/irq-imgpdc.c +++ b/drivers/irqchip/irq-imgpdc.c @@ -372,7 +372,7 @@ static int pdc_intc_probe(struct platform_device *pdev) priv->syswake_irq = irq; /* Set up an IRQ domain */ - priv->domain = irq_domain_add_linear(node, 16, &irq_generic_chip_ops, + priv->domain = irq_domain_create_linear(of_fwnode_handle(node), 16, &irq_generic_chip_ops, priv); if (unlikely(!priv->domain)) { dev_err(&pdev->dev, "cannot add IRQ domain\n"); diff --git a/drivers/irqchip/irq-imx-gpcv2.c b/drivers/irqchip/irq-imx-gpcv2.c index 8a0e82067924..b91f5c14b405 100644 --- a/drivers/irqchip/irq-imx-gpcv2.c +++ b/drivers/irqchip/irq-imx-gpcv2.c @@ -240,14 +240,14 @@ static int __init imx_gpcv2_irqchip_init(struct device_node *node, return -ENOMEM; } - domain = irq_domain_add_hierarchy(parent_domain, 0, GPC_MAX_IRQS, - node, &gpcv2_irqchip_data_domain_ops, cd); + domain = irq_domain_create_hierarchy(parent_domain, 0, GPC_MAX_IRQS, + of_fwnode_handle(node), &gpcv2_irqchip_data_domain_ops, cd); if (!domain) { iounmap(cd->gpc_base); kfree(cd); return -ENOMEM; } - irq_set_default_host(domain); + irq_set_default_domain(domain); /* Initially mask all interrupts */ for (i = 0; i < IMR_NUM; i++) { diff --git a/drivers/irqchip/irq-imx-intmux.c b/drivers/irqchip/irq-imx-intmux.c index 787543d07565..5f9b204d350b 100644 --- a/drivers/irqchip/irq-imx-intmux.c +++ b/drivers/irqchip/irq-imx-intmux.c @@ -254,7 +254,7 @@ static int imx_intmux_probe(struct platform_device *pdev) goto out; } - domain = irq_domain_add_linear(np, 32, &imx_intmux_domain_ops, + domain = irq_domain_create_linear(of_fwnode_handle(np), 32, &imx_intmux_domain_ops, &data->irqchip_data[i]); if (!domain) { ret = -ENOMEM; diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c index b0e9788c0045..6dc9ac48fee5 100644 --- a/drivers/irqchip/irq-imx-irqsteer.c +++ b/drivers/irqchip/irq-imx-irqsteer.c @@ -24,7 +24,7 @@ #define CHAN_MINTDIS(t) (CTRL_STRIDE_OFF(t, 3) + 0x4) #define CHAN_MASTRSTAT(t) (CTRL_STRIDE_OFF(t, 3) + 0x8) -#define CHAN_MAX_OUTPUT_INT 0x8 +#define CHAN_MAX_OUTPUT_INT 0xF struct irqsteer_data { void __iomem *regs; @@ -212,7 +212,7 @@ static int imx_irqsteer_probe(struct platform_device *pdev) /* steer all IRQs into configured channel */ writel_relaxed(BIT(data->channel), data->regs + CHANCTRL); - data->domain = irq_domain_add_linear(np, data->reg_num * 32, + data->domain = irq_domain_create_linear(of_fwnode_handle(np), data->reg_num * 32, &imx_irqsteer_domain_ops, data); if (!data->domain) { dev_err(&pdev->dev, "failed to create IRQ domain\n"); @@ -228,10 +228,8 @@ static int imx_irqsteer_probe(struct platform_device *pdev) for (i = 0; i < data->irq_count; i++) { data->irq[i] = irq_of_parse_and_map(np, i); - if (!data->irq[i]) { - ret = -EINVAL; - goto out; - } + if (!data->irq[i]) + break; irq_set_chained_handler_and_data(data->irq[i], imx_irqsteer_irq_handler, @@ -254,9 +252,13 @@ static void imx_irqsteer_remove(struct platform_device *pdev) struct irqsteer_data *irqsteer_data = platform_get_drvdata(pdev); int i; - for (i = 0; i < irqsteer_data->irq_count; i++) + for (i = 0; i < irqsteer_data->irq_count; i++) { + if (!irqsteer_data->irq[i]) + break; + irq_set_chained_handler_and_data(irqsteer_data->irq[i], NULL, NULL); + } irq_domain_remove(irqsteer_data->domain); diff --git a/drivers/irqchip/irq-imx-mu-msi.c b/drivers/irqchip/irq-imx-mu-msi.c index 4342a21de1eb..137da1927d14 100644 --- a/drivers/irqchip/irq-imx-mu-msi.c +++ b/drivers/irqchip/irq-imx-mu-msi.c @@ -24,7 +24,7 @@ #include <linux/pm_domain.h> #include <linux/spinlock.h> -#include "irq-msi-lib.h" +#include <linux/irqchip/irq-msi-lib.h> #define IMX_MU_CHANS 4 @@ -214,6 +214,7 @@ static void imx_mu_msi_irq_handler(struct irq_desc *desc) static const struct msi_parent_ops imx_mu_msi_parent_ops = { .supported_flags = IMX_MU_MSI_FLAGS_SUPPORTED, .required_flags = IMX_MU_MSI_FLAGS_REQUIRED, + .chip_flags = MSI_CHIP_FLAG_SET_EOI | MSI_CHIP_FLAG_SET_ACK, .bus_select_token = DOMAIN_BUS_NEXUS, .bus_select_mask = MATCH_PLATFORM_MSI, .prefix = "MU-MSI-", diff --git a/drivers/irqchip/irq-ingenic-tcu.c b/drivers/irqchip/irq-ingenic-tcu.c index 3363f83bd7e9..794ecba717c9 100644 --- a/drivers/irqchip/irq-ingenic-tcu.c +++ b/drivers/irqchip/irq-ingenic-tcu.c @@ -52,11 +52,10 @@ static void ingenic_tcu_gc_unmask_enable_reg(struct irq_data *d) struct regmap *map = gc->private; u32 mask = d->mask; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); regmap_write(map, ct->regs.ack, mask); regmap_write(map, ct->regs.enable, mask); *ct->mask_cache |= mask; - irq_gc_unlock(gc); } static void ingenic_tcu_gc_mask_disable_reg(struct irq_data *d) @@ -66,10 +65,9 @@ static void ingenic_tcu_gc_mask_disable_reg(struct irq_data *d) struct regmap *map = gc->private; u32 mask = d->mask; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); regmap_write(map, ct->regs.disable, mask); *ct->mask_cache &= ~mask; - irq_gc_unlock(gc); } static void ingenic_tcu_gc_mask_disable_reg_and_ack(struct irq_data *d) @@ -79,10 +77,9 @@ static void ingenic_tcu_gc_mask_disable_reg_and_ack(struct irq_data *d) struct regmap *map = gc->private; u32 mask = d->mask; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); regmap_write(map, ct->regs.ack, mask); regmap_write(map, ct->regs.disable, mask); - irq_gc_unlock(gc); } static int __init ingenic_tcu_irq_init(struct device_node *np, @@ -114,8 +111,8 @@ static int __init ingenic_tcu_irq_init(struct device_node *np, tcu->nb_parent_irqs = irqs; - tcu->domain = irq_domain_add_linear(np, 32, &irq_generic_chip_ops, - NULL); + tcu->domain = irq_domain_create_linear(of_fwnode_handle(np), 32, &irq_generic_chip_ops, + NULL); if (!tcu->domain) { ret = -ENOMEM; goto err_free_tcu; diff --git a/drivers/irqchip/irq-ingenic.c b/drivers/irqchip/irq-ingenic.c index cee839ca627e..52393724f213 100644 --- a/drivers/irqchip/irq-ingenic.c +++ b/drivers/irqchip/irq-ingenic.c @@ -90,8 +90,8 @@ static int __init ingenic_intc_of_init(struct device_node *node, goto out_unmap_irq; } - domain = irq_domain_add_linear(node, num_chips * 32, - &irq_generic_chip_ops, NULL); + domain = irq_domain_create_linear(of_fwnode_handle(node), num_chips * 32, + &irq_generic_chip_ops, NULL); if (!domain) { err = -ENOMEM; goto out_unmap_base; diff --git a/drivers/irqchip/irq-ixp4xx.c b/drivers/irqchip/irq-ixp4xx.c index f23b02f62a5c..a9a5a52b818a 100644 --- a/drivers/irqchip/irq-ixp4xx.c +++ b/drivers/irqchip/irq-ixp4xx.c @@ -261,7 +261,7 @@ static int __init ixp4xx_of_init_irq(struct device_node *np, pr_crit("IXP4XX: could not ioremap interrupt controller\n"); return -ENODEV; } - fwnode = of_node_to_fwnode(np); + fwnode = of_fwnode_handle(np); /* These chip variants have 64 interrupts */ is_356 = of_device_is_compatible(np, "intel,ixp43x-interrupt") || diff --git a/drivers/irqchip/irq-jcore-aic.c b/drivers/irqchip/irq-jcore-aic.c index 1f613eb7b7f0..94c05cf974be 100644 --- a/drivers/irqchip/irq-jcore-aic.c +++ b/drivers/irqchip/irq-jcore-aic.c @@ -107,9 +107,8 @@ static int __init aic_irq_of_init(struct device_node *node, if (ret < 0) return ret; - domain = irq_domain_add_legacy(node, dom_sz - min_irq, min_irq, min_irq, - &jcore_aic_irqdomain_ops, - &jcore_aic); + domain = irq_domain_create_legacy(of_fwnode_handle(node), dom_sz - min_irq, min_irq, + min_irq, &jcore_aic_irqdomain_ops, &jcore_aic); if (!domain) return -ENOMEM; diff --git a/drivers/irqchip/irq-keystone.c b/drivers/irqchip/irq-keystone.c index 808c781e2548..c9e902b7bf48 100644 --- a/drivers/irqchip/irq-keystone.c +++ b/drivers/irqchip/irq-keystone.c @@ -141,18 +141,11 @@ static int keystone_irq_probe(struct platform_device *pdev) if (!kirq) return -ENOMEM; - kirq->devctrl_regs = - syscon_regmap_lookup_by_phandle(np, "ti,syscon-dev"); + kirq->devctrl_regs = syscon_regmap_lookup_by_phandle_args(np, "ti,syscon-dev", + 1, &kirq->devctrl_offset); if (IS_ERR(kirq->devctrl_regs)) return PTR_ERR(kirq->devctrl_regs); - ret = of_property_read_u32_index(np, "ti,syscon-dev", 1, - &kirq->devctrl_offset); - if (ret) { - dev_err(dev, "couldn't read the devctrl_offset offset!\n"); - return ret; - } - kirq->irq = platform_get_irq(pdev, 0); if (kirq->irq < 0) return kirq->irq; @@ -164,8 +157,8 @@ static int keystone_irq_probe(struct platform_device *pdev) kirq->chip.irq_mask = keystone_irq_setmask; kirq->chip.irq_unmask = keystone_irq_unmask; - kirq->irqd = irq_domain_add_linear(np, KEYSTONE_N_IRQ, - &keystone_irq_ops, kirq); + kirq->irqd = irq_domain_create_linear(of_fwnode_handle(np), KEYSTONE_N_IRQ, + &keystone_irq_ops, kirq); if (!kirq->irqd) { dev_err(dev, "IRQ domain registration failed\n"); return -ENODEV; diff --git a/drivers/irqchip/irq-lan966x-oic.c b/drivers/irqchip/irq-lan966x-oic.c index 41ac880e3b87..11d3a0ffa261 100644 --- a/drivers/irqchip/irq-lan966x-oic.c +++ b/drivers/irqchip/irq-lan966x-oic.c @@ -71,14 +71,12 @@ static unsigned int lan966x_oic_irq_startup(struct irq_data *data) struct lan966x_oic_chip_regs *chip_regs = gc->private; u32 map; - irq_gc_lock(gc); - - /* Map the source interrupt to the destination */ - map = irq_reg_readl(gc, chip_regs->reg_off_map); - map |= data->mask; - irq_reg_writel(gc, map, chip_regs->reg_off_map); - - irq_gc_unlock(gc); + scoped_guard (raw_spinlock, &gc->lock) { + /* Map the source interrupt to the destination */ + map = irq_reg_readl(gc, chip_regs->reg_off_map); + map |= data->mask; + irq_reg_writel(gc, map, chip_regs->reg_off_map); + } ct->chip.irq_ack(data); ct->chip.irq_unmask(data); @@ -95,14 +93,12 @@ static void lan966x_oic_irq_shutdown(struct irq_data *data) ct->chip.irq_mask(data); - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); /* Unmap the interrupt */ map = irq_reg_readl(gc, chip_regs->reg_off_map); map &= ~data->mask; irq_reg_writel(gc, map, chip_regs->reg_off_map); - - irq_gc_unlock(gc); } static int lan966x_oic_irq_set_type(struct irq_data *data, @@ -224,7 +220,7 @@ static int lan966x_oic_probe(struct platform_device *pdev) .exit = lan966x_oic_chip_exit, }; struct irq_domain_info d_info = { - .fwnode = of_node_to_fwnode(pdev->dev.of_node), + .fwnode = of_fwnode_handle(pdev->dev.of_node), .domain_flags = IRQ_DOMAIN_FLAG_DESTROY_GC, .size = LAN966X_OIC_NR_IRQ, .hwirq_max = LAN966X_OIC_NR_IRQ, diff --git a/drivers/irqchip/irq-loongarch-avec.c b/drivers/irqchip/irq-loongarch-avec.c index 0f6e465dd309..bf52dc8345f5 100644 --- a/drivers/irqchip/irq-loongarch-avec.c +++ b/drivers/irqchip/irq-loongarch-avec.c @@ -18,7 +18,7 @@ #include <asm/loongarch.h> #include <asm/setup.h> -#include "irq-msi-lib.h" +#include <linux/irqchip/irq-msi-lib.h> #include "irq-loongson.h" #define VECTORS_PER_REG 64 @@ -56,6 +56,15 @@ struct avecintc_data { unsigned int moving; }; +static inline void avecintc_enable(void) +{ + u64 value; + + value = iocsr_read64(LOONGARCH_IOCSR_MISC_FUNC); + value |= IOCSR_MISC_FUNC_AVEC_EN; + iocsr_write64(value, LOONGARCH_IOCSR_MISC_FUNC); +} + static inline void avecintc_ack_irq(struct irq_data *d) { } @@ -127,6 +136,8 @@ static int avecintc_cpu_online(unsigned int cpu) guard(raw_spinlock)(&loongarch_avec.lock); + avecintc_enable(); + irq_matrix_online(loongarch_avec.vector_matrix); pending_list_init(cpu); @@ -339,7 +350,6 @@ static int __init irq_matrix_init(void) static int __init avecintc_init(struct irq_domain *parent) { int ret, parent_irq; - unsigned long value; raw_spin_lock_init(&loongarch_avec.lock); @@ -378,9 +388,7 @@ static int __init avecintc_init(struct irq_domain *parent) "irqchip/loongarch/avecintc:starting", avecintc_cpu_online, avecintc_cpu_offline); #endif - value = iocsr_read64(LOONGARCH_IOCSR_MISC_FUNC); - value |= IOCSR_MISC_FUNC_AVEC_EN; - iocsr_write64(value, LOONGARCH_IOCSR_MISC_FUNC); + avecintc_enable(); return ret; diff --git a/drivers/irqchip/irq-loongarch-cpu.c b/drivers/irqchip/irq-loongarch-cpu.c index e62dab4c97fc..950bc087e388 100644 --- a/drivers/irqchip/irq-loongarch-cpu.c +++ b/drivers/irqchip/irq-loongarch-cpu.c @@ -100,7 +100,7 @@ static const struct irq_domain_ops loongarch_cpu_intc_irq_domain_ops = { static int __init cpuintc_of_init(struct device_node *of_node, struct device_node *parent) { - cpuintc_handle = of_node_to_fwnode(of_node); + cpuintc_handle = of_fwnode_handle(of_node); irq_domain = irq_domain_create_linear(cpuintc_handle, EXCCODE_INT_NUM, &loongarch_cpu_intc_irq_domain_ops, NULL); diff --git a/drivers/irqchip/irq-loongson-eiointc.c b/drivers/irqchip/irq-loongson-eiointc.c index bb79e19dfb59..b2860eb2d32c 100644 --- a/drivers/irqchip/irq-loongson-eiointc.c +++ b/drivers/irqchip/irq-loongson-eiointc.c @@ -554,7 +554,7 @@ static int __init eiointc_of_init(struct device_node *of_node, priv->vec_count = VEC_COUNT; priv->node = 0; - priv->domain_handle = of_node_to_fwnode(of_node); + priv->domain_handle = of_fwnode_handle(of_node); ret = eiointc_init(priv, parent_irq, 0); if (ret < 0) diff --git a/drivers/irqchip/irq-loongson-htvec.c b/drivers/irqchip/irq-loongson-htvec.c index 5da02c7ad0b3..d8558eb35044 100644 --- a/drivers/irqchip/irq-loongson-htvec.c +++ b/drivers/irqchip/irq-loongson-htvec.c @@ -248,7 +248,7 @@ static int htvec_of_init(struct device_node *node, } err = htvec_init(res.start, resource_size(&res), - num_parents, parent_irq, of_node_to_fwnode(node)); + num_parents, parent_irq, of_fwnode_handle(node)); if (err < 0) return err; diff --git a/drivers/irqchip/irq-loongson-liointc.c b/drivers/irqchip/irq-loongson-liointc.c index 2b1bd4a96665..0033c2188abc 100644 --- a/drivers/irqchip/irq-loongson-liointc.c +++ b/drivers/irqchip/irq-loongson-liointc.c @@ -116,9 +116,8 @@ static int liointc_set_type(struct irq_data *data, unsigned int type) { struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); u32 mask = data->mask; - unsigned long flags; - irq_gc_lock_irqsave(gc, flags); + guard(raw_spinlock)(&gc->lock); switch (type) { case IRQ_TYPE_LEVEL_HIGH: liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, false); @@ -137,10 +136,8 @@ static int liointc_set_type(struct irq_data *data, unsigned int type) liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, true); break; default: - irq_gc_unlock_irqrestore(gc, flags); return -EINVAL; } - irq_gc_unlock_irqrestore(gc, flags); irqd_set_trigger_type(data, type); return 0; @@ -157,10 +154,9 @@ static void liointc_suspend(struct irq_chip_generic *gc) static void liointc_resume(struct irq_chip_generic *gc) { struct liointc_priv *priv = gc->private; - unsigned long flags; int i; - irq_gc_lock_irqsave(gc, flags); + guard(raw_spinlock_irqsave)(&gc->lock); /* Disable all at first */ writel(0xffffffff, gc->reg_base + LIOINTC_REG_INTC_DISABLE); /* Restore map cache */ @@ -170,7 +166,6 @@ static void liointc_resume(struct irq_chip_generic *gc) writel(priv->int_edge, gc->reg_base + LIOINTC_REG_INTC_EDGE); /* Restore mask cache */ writel(gc->mask_cache, gc->reg_base + LIOINTC_REG_INTC_ENABLE); - irq_gc_unlock_irqrestore(gc, flags); } static int parent_irq[LIOINTC_NUM_PARENT]; @@ -363,7 +358,7 @@ static int __init liointc_of_init(struct device_node *node, } err = liointc_init(res.start, resource_size(&res), - revision, of_node_to_fwnode(node), node); + revision, of_fwnode_handle(node), node); if (err < 0) return err; diff --git a/drivers/irqchip/irq-loongson-pch-msi.c b/drivers/irqchip/irq-loongson-pch-msi.c index bd337ecddb40..a0257c7bef10 100644 --- a/drivers/irqchip/irq-loongson-pch-msi.c +++ b/drivers/irqchip/irq-loongson-pch-msi.c @@ -15,7 +15,7 @@ #include <linux/pci.h> #include <linux/slab.h> -#include "irq-msi-lib.h" +#include <linux/irqchip/irq-msi-lib.h> #include "irq-loongson.h" static int nr_pics; @@ -146,6 +146,7 @@ static const struct irq_domain_ops pch_msi_middle_domain_ops = { static struct msi_parent_ops pch_msi_parent_ops = { .required_flags = PCH_MSI_FLAGS_REQUIRED, .supported_flags = PCH_MSI_FLAGS_SUPPORTED, + .chip_flags = MSI_CHIP_FLAG_SET_EOI | MSI_CHIP_FLAG_SET_ACK, .bus_select_mask = MATCH_PCI_MSI, .bus_select_token = DOMAIN_BUS_NEXUS, .prefix = "PCH-", @@ -242,7 +243,7 @@ static int pch_msi_of_init(struct device_node *node, struct device_node *parent) return -EINVAL; } - err = pch_msi_init(res.start, irq_base, irq_count, parent_domain, of_node_to_fwnode(node)); + err = pch_msi_init(res.start, irq_base, irq_count, parent_domain, of_fwnode_handle(node)); if (err < 0) return err; diff --git a/drivers/irqchip/irq-loongson-pch-pic.c b/drivers/irqchip/irq-loongson-pch-pic.c index 69efda35a8e7..62e6bf3a0611 100644 --- a/drivers/irqchip/irq-loongson-pch-pic.c +++ b/drivers/irqchip/irq-loongson-pch-pic.c @@ -392,7 +392,7 @@ static int pch_pic_of_init(struct device_node *node, } err = pch_pic_init(res.start, resource_size(&res), vec_base, - parent_domain, of_node_to_fwnode(node), 0); + parent_domain, of_fwnode_handle(node), 0); if (err < 0) return err; diff --git a/drivers/irqchip/irq-lpc32xx.c b/drivers/irqchip/irq-lpc32xx.c index 4d70a857133f..14cca44baa14 100644 --- a/drivers/irqchip/irq-lpc32xx.c +++ b/drivers/irqchip/irq-lpc32xx.c @@ -210,8 +210,8 @@ static int __init lpc32xx_of_ic_init(struct device_node *node, return -EINVAL; } - irqc->domain = irq_domain_add_linear(node, NR_LPC32XX_IC_IRQS, - &lpc32xx_irq_domain_ops, irqc); + irqc->domain = irq_domain_create_linear(of_fwnode_handle(node), NR_LPC32XX_IC_IRQS, + &lpc32xx_irq_domain_ops, irqc); if (!irqc->domain) { pr_err("unable to add irq domain\n"); iounmap(irqc->base); diff --git a/drivers/irqchip/irq-ls-extirq.c b/drivers/irqchip/irq-ls-extirq.c index 139f26b0a6ef..50a7b38381b9 100644 --- a/drivers/irqchip/irq-ls-extirq.c +++ b/drivers/irqchip/irq-ls-extirq.c @@ -208,8 +208,8 @@ ls_extirq_of_init(struct device_node *node, struct device_node *parent) of_device_is_compatible(node, "fsl,ls1043a-extirq"); raw_spin_lock_init(&priv->lock); - domain = irq_domain_add_hierarchy(parent_domain, 0, priv->nirq, node, - &extirq_domain_ops, priv); + domain = irq_domain_create_hierarchy(parent_domain, 0, priv->nirq, of_fwnode_handle(node), + &extirq_domain_ops, priv); if (!domain) { ret = -ENOMEM; goto err_add_hierarchy; diff --git a/drivers/irqchip/irq-ls-scfg-msi.c b/drivers/irqchip/irq-ls-scfg-msi.c index c0e1aafe468c..84bc5e4b47cf 100644 --- a/drivers/irqchip/irq-ls-scfg-msi.c +++ b/drivers/irqchip/irq-ls-scfg-msi.c @@ -87,8 +87,6 @@ static void ls_scfg_msi_compose_msg(struct irq_data *data, struct msi_msg *msg) { struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(data); - msg->address_hi = upper_32_bits(msi_data->msiir_addr); - msg->address_lo = lower_32_bits(msi_data->msiir_addr); msg->data = data->hwirq; if (msi_affinity_flag) { @@ -98,7 +96,8 @@ static void ls_scfg_msi_compose_msg(struct irq_data *data, struct msi_msg *msg) msg->data |= cpumask_first(mask); } - iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), msg); + msi_msg_set_addr(irq_data_get_msi_desc(data), msg, + msi_data->msiir_addr); } static int ls_scfg_msi_set_affinity(struct irq_data *irq_data, @@ -216,17 +215,17 @@ static void ls_scfg_msi_irq_handler(struct irq_desc *desc) static int ls_scfg_msi_domains_init(struct ls_scfg_msi *msi_data) { /* Initialize MSI domain parent */ - msi_data->parent = irq_domain_add_linear(NULL, - msi_data->irqs_num, - &ls_scfg_msi_domain_ops, - msi_data); + msi_data->parent = irq_domain_create_linear(NULL, + msi_data->irqs_num, + &ls_scfg_msi_domain_ops, + msi_data); if (!msi_data->parent) { dev_err(&msi_data->pdev->dev, "failed to create IRQ domain\n"); return -ENOMEM; } msi_data->msi_domain = pci_msi_create_irq_domain( - of_node_to_fwnode(msi_data->pdev->dev.of_node), + of_fwnode_handle(msi_data->pdev->dev.of_node), &ls_scfg_msi_domain_info, msi_data->parent); if (!msi_data->msi_domain) { diff --git a/drivers/irqchip/irq-ls1x.c b/drivers/irqchip/irq-ls1x.c index 77a3f7dfaaf0..589d32007fca 100644 --- a/drivers/irqchip/irq-ls1x.c +++ b/drivers/irqchip/irq-ls1x.c @@ -126,8 +126,8 @@ static int __init ls1x_intc_of_init(struct device_node *node, } /* Set up an IRQ domain */ - priv->domain = irq_domain_add_linear(node, 32, &irq_generic_chip_ops, - NULL); + priv->domain = irq_domain_create_linear(of_fwnode_handle(node), 32, &irq_generic_chip_ops, + NULL); if (!priv->domain) { pr_err("ls1x-irq: cannot add IRQ domain\n"); err = -ENOMEM; diff --git a/drivers/irqchip/irq-mchp-eic.c b/drivers/irqchip/irq-mchp-eic.c index 5dcd94c000a2..516a3a0e359c 100644 --- a/drivers/irqchip/irq-mchp-eic.c +++ b/drivers/irqchip/irq-mchp-eic.c @@ -248,8 +248,9 @@ static int mchp_eic_init(struct device_node *node, struct device_node *parent) eic->irqs[i] = irq.args[1]; } - eic->domain = irq_domain_add_hierarchy(parent_domain, 0, MCHP_EIC_NIRQ, - node, &mchp_eic_domain_ops, eic); + eic->domain = irq_domain_create_hierarchy(parent_domain, 0, MCHP_EIC_NIRQ, + of_fwnode_handle(node), &mchp_eic_domain_ops, + eic); if (!eic->domain) { pr_err("%pOF: Failed to add domain\n", node); ret = -ENODEV; diff --git a/drivers/irqchip/irq-meson-gpio.c b/drivers/irqchip/irq-meson-gpio.c index cd789fa51519..7d177626d64b 100644 --- a/drivers/irqchip/irq-meson-gpio.c +++ b/drivers/irqchip/irq-meson-gpio.c @@ -26,8 +26,6 @@ /* use for A1 like chips */ #define REG_PIN_A1_SEL 0x04 -/* Used for s4 chips */ -#define REG_EDGE_POL_S4 0x1c /* * Note: The S905X3 datasheet reports that BOTH_EDGE is controlled by @@ -72,6 +70,7 @@ struct meson_gpio_irq_params { bool support_edge_both; unsigned int edge_both_offset; unsigned int edge_single_offset; + unsigned int edge_pol_reg; unsigned int pol_low_offset; unsigned int pin_sel_mask; struct irq_ctl_ops ops; @@ -105,6 +104,18 @@ struct meson_gpio_irq_params { .pin_sel_mask = 0x7f, \ .nr_channels = 8, \ +#define INIT_MESON_A4_AO_COMMON_DATA(irqs) \ + INIT_MESON_COMMON(irqs, meson_a1_gpio_irq_init, \ + meson_a1_gpio_irq_sel_pin, \ + meson_s4_gpio_irq_set_type) \ + .support_edge_both = true, \ + .edge_both_offset = 0, \ + .edge_single_offset = 12, \ + .edge_pol_reg = 0x8, \ + .pol_low_offset = 0, \ + .pin_sel_mask = 0xff, \ + .nr_channels = 2, \ + #define INIT_MESON_S4_COMMON_DATA(irqs) \ INIT_MESON_COMMON(irqs, meson_a1_gpio_irq_init, \ meson_a1_gpio_irq_sel_pin, \ @@ -112,6 +123,7 @@ struct meson_gpio_irq_params { .support_edge_both = true, \ .edge_both_offset = 0, \ .edge_single_offset = 12, \ + .edge_pol_reg = 0x1c, \ .pol_low_offset = 0, \ .pin_sel_mask = 0xff, \ .nr_channels = 12, \ @@ -146,6 +158,18 @@ static const struct meson_gpio_irq_params a1_params = { INIT_MESON_A1_COMMON_DATA(62) }; +static const struct meson_gpio_irq_params a4_params = { + INIT_MESON_S4_COMMON_DATA(81) +}; + +static const struct meson_gpio_irq_params a4_ao_params = { + INIT_MESON_A4_AO_COMMON_DATA(8) +}; + +static const struct meson_gpio_irq_params a5_params = { + INIT_MESON_S4_COMMON_DATA(99) +}; + static const struct meson_gpio_irq_params s4_params = { INIT_MESON_S4_COMMON_DATA(82) }; @@ -168,6 +192,9 @@ static const struct of_device_id meson_irq_gpio_matches[] __maybe_unused = { { .compatible = "amlogic,meson-sm1-gpio-intc", .data = &sm1_params }, { .compatible = "amlogic,meson-a1-gpio-intc", .data = &a1_params }, { .compatible = "amlogic,meson-s4-gpio-intc", .data = &s4_params }, + { .compatible = "amlogic,a4-gpio-ao-intc", .data = &a4_ao_params }, + { .compatible = "amlogic,a4-gpio-intc", .data = &a4_params }, + { .compatible = "amlogic,a5-gpio-intc", .data = &a5_params }, { .compatible = "amlogic,c3-gpio-intc", .data = &c3_params }, { .compatible = "amlogic,t7-gpio-intc", .data = &t7_params }, { } @@ -299,11 +326,10 @@ meson_gpio_irq_release_channel(struct meson_gpio_irq_controller *ctl, static int meson8_gpio_irq_set_type(struct meson_gpio_irq_controller *ctl, unsigned int type, u32 *channel_hwirq) { - u32 val = 0; + const struct meson_gpio_irq_params *params = ctl->params; unsigned int idx; - const struct meson_gpio_irq_params *params; + u32 val = 0; - params = ctl->params; idx = meson_gpio_irq_get_channel_idx(ctl, channel_hwirq); /* @@ -356,19 +382,19 @@ static int meson8_gpio_irq_set_type(struct meson_gpio_irq_controller *ctl, static int meson_s4_gpio_irq_set_type(struct meson_gpio_irq_controller *ctl, unsigned int type, u32 *channel_hwirq) { - u32 val = 0; + const struct meson_gpio_irq_params *params = ctl->params; unsigned int idx; + u32 val = 0; idx = meson_gpio_irq_get_channel_idx(ctl, channel_hwirq); type &= IRQ_TYPE_SENSE_MASK; - meson_gpio_irq_update_bits(ctl, REG_EDGE_POL_S4, BIT(idx), 0); + meson_gpio_irq_update_bits(ctl, params->edge_pol_reg, BIT(idx), 0); if (type == IRQ_TYPE_EDGE_BOTH) { - val |= BIT(ctl->params->edge_both_offset + idx); - meson_gpio_irq_update_bits(ctl, REG_EDGE_POL_S4, - BIT(ctl->params->edge_both_offset + idx), val); + val = BIT(ctl->params->edge_both_offset + idx); + meson_gpio_irq_update_bits(ctl, params->edge_pol_reg, val, val); return 0; } @@ -378,7 +404,7 @@ static int meson_s4_gpio_irq_set_type(struct meson_gpio_irq_controller *ctl, if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) val |= BIT(ctl->params->edge_single_offset + idx); - meson_gpio_irq_update_bits(ctl, REG_EDGE_POL, + meson_gpio_irq_update_bits(ctl, params->edge_pol_reg, BIT(idx) | BIT(12 + idx), val); return 0; }; @@ -581,7 +607,7 @@ static int meson_gpio_irq_of_init(struct device_node *node, struct device_node * domain = irq_domain_create_hierarchy(parent_domain, 0, ctl->params->nr_hwirq, - of_node_to_fwnode(node), + of_fwnode_handle(node), &meson_gpio_irq_domain_ops, ctl); if (!domain) { diff --git a/drivers/irqchip/irq-mips-cpu.c b/drivers/irqchip/irq-mips-cpu.c index 0c7ae71a0af0..ac784ef3ed4b 100644 --- a/drivers/irqchip/irq-mips-cpu.c +++ b/drivers/irqchip/irq-mips-cpu.c @@ -238,11 +238,9 @@ static void mips_cpu_register_ipi_domain(struct device_node *of_node) struct cpu_ipi_domain_state *ipi_domain_state; ipi_domain_state = kzalloc(sizeof(*ipi_domain_state), GFP_KERNEL); - ipi_domain = irq_domain_add_hierarchy(irq_domain, - IRQ_DOMAIN_FLAG_IPI_SINGLE, - 2, of_node, - &mips_cpu_ipi_chip_ops, - ipi_domain_state); + ipi_domain = irq_domain_create_hierarchy(irq_domain, IRQ_DOMAIN_FLAG_IPI_SINGLE, 2, + of_fwnode_handle(of_node), + &mips_cpu_ipi_chip_ops, ipi_domain_state); if (!ipi_domain) panic("Failed to add MIPS CPU IPI domain"); irq_domain_update_bus_token(ipi_domain, DOMAIN_BUS_IPI); @@ -260,9 +258,8 @@ static void __init __mips_cpu_irq_init(struct device_node *of_node) clear_c0_status(ST0_IM); clear_c0_cause(CAUSEF_IP); - irq_domain = irq_domain_add_legacy(of_node, 8, MIPS_CPU_IRQ_BASE, 0, - &mips_cpu_intc_irq_domain_ops, - NULL); + irq_domain = irq_domain_create_legacy(of_fwnode_handle(of_node), 8, MIPS_CPU_IRQ_BASE, 0, + &mips_cpu_intc_irq_domain_ops, NULL); if (!irq_domain) panic("Failed to add irqdomain for MIPS CPU"); diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c index bca8053864b2..19a57c5e2b2e 100644 --- a/drivers/irqchip/irq-mips-gic.c +++ b/drivers/irqchip/irq-mips-gic.c @@ -375,9 +375,13 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *cpumask, /* * The GIC specifies that we can only route an interrupt to one VP(E), * ie. CPU in Linux parlance, at a time. Therefore we always route to - * the first online CPU in the mask. + * the first forced or online CPU in the mask. */ - cpu = cpumask_first_and(cpumask, cpu_online_mask); + if (force) + cpu = cpumask_first(cpumask); + else + cpu = cpumask_first_and(cpumask, cpu_online_mask); + if (cpu >= NR_CPUS) return -EINVAL; @@ -841,10 +845,10 @@ static int gic_register_ipi_domain(struct device_node *node) struct irq_domain *gic_ipi_domain; unsigned int v[2], num_ipis; - gic_ipi_domain = irq_domain_add_hierarchy(gic_irq_domain, - IRQ_DOMAIN_FLAG_IPI_PER_CPU, - GIC_NUM_LOCAL_INTRS + gic_shared_intrs, - node, &gic_ipi_domain_ops, NULL); + gic_ipi_domain = irq_domain_create_hierarchy(gic_irq_domain, IRQ_DOMAIN_FLAG_IPI_PER_CPU, + GIC_NUM_LOCAL_INTRS + gic_shared_intrs, + of_fwnode_handle(node), &gic_ipi_domain_ops, + NULL); if (!gic_ipi_domain) { pr_err("Failed to add IPI domain"); return -ENXIO; @@ -963,9 +967,10 @@ static int __init gic_of_init(struct device_node *node, gic_irq_dispatch); } - gic_irq_domain = irq_domain_add_simple(node, GIC_NUM_LOCAL_INTRS + - gic_shared_intrs, 0, - &gic_irq_domain_ops, NULL); + gic_irq_domain = irq_domain_create_simple(of_fwnode_handle(node), + GIC_NUM_LOCAL_INTRS + + gic_shared_intrs, 0, + &gic_irq_domain_ops, NULL); if (!gic_irq_domain) { pr_err("Failed to add IRQ domain"); return -ENXIO; diff --git a/drivers/irqchip/irq-mmp.c b/drivers/irqchip/irq-mmp.c index 25cf4f80e767..09e640430208 100644 --- a/drivers/irqchip/irq-mmp.c +++ b/drivers/irqchip/irq-mmp.c @@ -261,9 +261,9 @@ static int __init mmp_init_bases(struct device_node *node) } icu_data[0].virq_base = 0; - icu_data[0].domain = irq_domain_add_linear(node, nr_irqs, - &mmp_irq_domain_ops, - &icu_data[0]); + icu_data[0].domain = irq_domain_create_linear(of_fwnode_handle(node), nr_irqs, + &mmp_irq_domain_ops, + &icu_data[0]); for (irq = 0; irq < nr_irqs; irq++) { ret = irq_create_mapping(icu_data[0].domain, irq); if (!ret) { @@ -391,9 +391,9 @@ static int __init mmp2_mux_of_init(struct device_node *node, return -EINVAL; icu_data[i].virq_base = 0; - icu_data[i].domain = irq_domain_add_linear(node, nr_irqs, - &mmp_irq_domain_ops, - &icu_data[i]); + icu_data[i].domain = irq_domain_create_linear(of_fwnode_handle(node), nr_irqs, + &mmp_irq_domain_ops, + &icu_data[i]); for (irq = 0; irq < nr_irqs; irq++) { ret = irq_create_mapping(icu_data[i].domain, irq); if (!ret) { diff --git a/drivers/irqchip/irq-mscc-ocelot.c b/drivers/irqchip/irq-mscc-ocelot.c index 3dc745b14caf..8cbc191f750b 100644 --- a/drivers/irqchip/irq-mscc-ocelot.c +++ b/drivers/irqchip/irq-mscc-ocelot.c @@ -83,7 +83,7 @@ static void ocelot_irq_unmask(struct irq_data *data) unsigned int mask = data->mask; u32 val; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); /* * Clear sticky bits for edge mode interrupts. * Serval has only one trigger register replication, but the adjacent @@ -97,7 +97,6 @@ static void ocelot_irq_unmask(struct irq_data *data) *ct->mask_cache &= ~mask; irq_reg_writel(gc, mask, p->reg_off_ena_set); - irq_gc_unlock(gc); } static void ocelot_irq_handler(struct irq_desc *desc) @@ -132,8 +131,8 @@ static int __init vcoreiii_irq_init(struct device_node *node, if (!parent_irq) return -EINVAL; - domain = irq_domain_add_linear(node, p->n_irq, - &irq_generic_chip_ops, NULL); + domain = irq_domain_create_linear(of_fwnode_handle(node), p->n_irq, + &irq_generic_chip_ops, NULL); if (!domain) { pr_err("%pOFn: unable to add irq domain\n", node); return -ENOMEM; diff --git a/drivers/irqchip/irq-msi-lib.c b/drivers/irqchip/irq-msi-lib.c index d8e29fc0d406..246c30205af4 100644 --- a/drivers/irqchip/irq-msi-lib.c +++ b/drivers/irqchip/irq-msi-lib.c @@ -4,7 +4,7 @@ #include <linux/export.h> -#include "irq-msi-lib.h" +#include <linux/irqchip/irq-msi-lib.h> /** * msi_lib_init_dev_msi_info - Domain info setup for MSI domains @@ -28,6 +28,7 @@ bool msi_lib_init_dev_msi_info(struct device *dev, struct irq_domain *domain, struct msi_domain_info *info) { const struct msi_parent_ops *pops = real_parent->msi_parent_ops; + struct irq_chip *chip = info->chip; u32 required_flags; /* Parent ops available? */ @@ -92,10 +93,10 @@ bool msi_lib_init_dev_msi_info(struct device *dev, struct irq_domain *domain, info->flags |= required_flags; /* Chip updates for all child bus types */ - if (!info->chip->irq_eoi) - info->chip->irq_eoi = irq_chip_eoi_parent; - if (!info->chip->irq_ack) - info->chip->irq_ack = irq_chip_ack_parent; + if (!chip->irq_eoi && (pops->chip_flags & MSI_CHIP_FLAG_SET_EOI)) + chip->irq_eoi = irq_chip_eoi_parent; + if (!chip->irq_ack && (pops->chip_flags & MSI_CHIP_FLAG_SET_ACK)) + chip->irq_ack = irq_chip_ack_parent; /* * The device MSI domain can never have a set affinity callback. It @@ -104,8 +105,13 @@ bool msi_lib_init_dev_msi_info(struct device *dev, struct irq_domain *domain, * MSI message into the hardware which is the whole purpose of the * device MSI domain aside of mask/unmask which is provided e.g. by * PCI/MSI device domains. + * + * The exception to the rule is when the underlying domain + * tells you that affinity is not a thing -- for example when + * everything is muxed behind a single interrupt. */ - info->chip->irq_set_affinity = msi_domain_set_affinity; + if (!chip->irq_set_affinity && !(info->flags & MSI_FLAG_NO_AFFINITY)) + chip->irq_set_affinity = msi_domain_set_affinity; return true; } EXPORT_SYMBOL_GPL(msi_lib_init_dev_msi_info); diff --git a/drivers/irqchip/irq-msi-lib.h b/drivers/irqchip/irq-msi-lib.h deleted file mode 100644 index 681ceabb7bc7..000000000000 --- a/drivers/irqchip/irq-msi-lib.h +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -// Copyright (C) 2022 Linutronix GmbH -// Copyright (C) 2022 Intel - -#ifndef _DRIVERS_IRQCHIP_IRQ_MSI_LIB_H -#define _DRIVERS_IRQCHIP_IRQ_MSI_LIB_H - -#include <linux/bits.h> -#include <linux/irqdomain.h> -#include <linux/msi.h> - -#ifdef CONFIG_PCI_MSI -#define MATCH_PCI_MSI BIT(DOMAIN_BUS_PCI_MSI) -#else -#define MATCH_PCI_MSI (0) -#endif - -#define MATCH_PLATFORM_MSI BIT(DOMAIN_BUS_PLATFORM_MSI) - -int msi_lib_irq_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec, - enum irq_domain_bus_token bus_token); - -bool msi_lib_init_dev_msi_info(struct device *dev, struct irq_domain *domain, - struct irq_domain *real_parent, - struct msi_domain_info *info); - -#endif /* _DRIVERS_IRQCHIP_IRQ_MSI_LIB_H */ diff --git a/drivers/irqchip/irq-mst-intc.c b/drivers/irqchip/irq-mst-intc.c index f6133ae28155..9643cc3a77d7 100644 --- a/drivers/irqchip/irq-mst-intc.c +++ b/drivers/irqchip/irq-mst-intc.c @@ -273,8 +273,8 @@ static int __init mst_intc_of_init(struct device_node *dn, raw_spin_lock_init(&cd->lock); cd->irq_start = irq_start; cd->nr_irqs = irq_end - irq_start + 1; - domain = irq_domain_add_hierarchy(domain_parent, 0, cd->nr_irqs, dn, - &mst_intc_domain_ops, cd); + domain = irq_domain_create_hierarchy(domain_parent, 0, cd->nr_irqs, of_fwnode_handle(dn), + &mst_intc_domain_ops, cd); if (!domain) { iounmap(cd->base); kfree(cd); diff --git a/drivers/irqchip/irq-mtk-cirq.c b/drivers/irqchip/irq-mtk-cirq.c index 76bc0283e3b9..de481ba340f8 100644 --- a/drivers/irqchip/irq-mtk-cirq.c +++ b/drivers/irqchip/irq-mtk-cirq.c @@ -336,9 +336,8 @@ static int __init mtk_cirq_of_init(struct device_node *node, cirq_data->offsets = match->data; irq_num = cirq_data->ext_irq_end - cirq_data->ext_irq_start + 1; - domain = irq_domain_add_hierarchy(domain_parent, 0, - irq_num, node, - &cirq_domain_ops, cirq_data); + domain = irq_domain_create_hierarchy(domain_parent, 0, irq_num, of_fwnode_handle(node), + &cirq_domain_ops, cirq_data); if (!domain) { ret = -ENOMEM; goto out_unmap; diff --git a/drivers/irqchip/irq-mtk-sysirq.c b/drivers/irqchip/irq-mtk-sysirq.c index 586e52d5442b..6895e7096b27 100644 --- a/drivers/irqchip/irq-mtk-sysirq.c +++ b/drivers/irqchip/irq-mtk-sysirq.c @@ -207,8 +207,8 @@ static int __init mtk_sysirq_of_init(struct device_node *node, chip_data->which_word[i] = word; } - domain = irq_domain_add_hierarchy(domain_parent, 0, intpol_num, node, - &sysirq_domain_ops, chip_data); + domain = irq_domain_create_hierarchy(domain_parent, 0, intpol_num, of_fwnode_handle(node), + &sysirq_domain_ops, chip_data); if (!domain) { ret = -ENOMEM; goto out_free_which_word; diff --git a/drivers/irqchip/irq-mvebu-gicp.c b/drivers/irqchip/irq-mvebu-gicp.c index 2b6183919ea4..54833717f8a7 100644 --- a/drivers/irqchip/irq-mvebu-gicp.c +++ b/drivers/irqchip/irq-mvebu-gicp.c @@ -17,7 +17,7 @@ #include <linux/of_platform.h> #include <linux/platform_device.h> -#include "irq-msi-lib.h" +#include <linux/irqchip/irq-msi-lib.h> #include <dt-bindings/interrupt-controller/arm-gic.h> @@ -161,6 +161,7 @@ static const struct irq_domain_ops gicp_domain_ops = { static const struct msi_parent_ops gicp_msi_parent_ops = { .supported_flags = GICP_MSI_FLAGS_SUPPORTED, .required_flags = GICP_MSI_FLAGS_REQUIRED, + .chip_flags = MSI_CHIP_FLAG_SET_EOI, .bus_select_token = DOMAIN_BUS_GENERIC_MSI, .bus_select_mask = MATCH_PLATFORM_MSI, .prefix = "GICP-", @@ -169,10 +170,14 @@ static const struct msi_parent_ops gicp_msi_parent_ops = { static int mvebu_gicp_probe(struct platform_device *pdev) { - struct irq_domain *inner_domain, *parent_domain; struct device_node *node = pdev->dev.of_node; struct device_node *irq_parent_dn; + struct irq_domain_info info = { + .fwnode = of_fwnode_handle(node), + .ops = &gicp_domain_ops, + }; struct mvebu_gicp *gicp; + void __iomem *base; int ret, i; gicp = devm_kzalloc(&pdev->dev, sizeof(*gicp), GFP_KERNEL); @@ -216,30 +221,32 @@ static int mvebu_gicp_probe(struct platform_device *pdev) if (!gicp->spi_bitmap) return -ENOMEM; + info.size = gicp->spi_cnt; + info.host_data = gicp; + irq_parent_dn = of_irq_find_parent(node); if (!irq_parent_dn) { dev_err(&pdev->dev, "failed to find parent IRQ node\n"); return -ENODEV; } - parent_domain = irq_find_host(irq_parent_dn); + info.parent = irq_find_host(irq_parent_dn); of_node_put(irq_parent_dn); - if (!parent_domain) { + if (!info.parent) { dev_err(&pdev->dev, "failed to find parent IRQ domain\n"); return -ENODEV; } - inner_domain = irq_domain_create_hierarchy(parent_domain, 0, - gicp->spi_cnt, - of_node_to_fwnode(node), - &gicp_domain_ops, gicp); - if (!inner_domain) - return -ENOMEM; + base = ioremap(gicp->res->start, resource_size(gicp->res)); + if (IS_ERR(base)) { + dev_err(&pdev->dev, "ioremap() failed. Unable to clear pending interrupts.\n"); + } else { + for (i = 0; i < 64; i++) + writel(i, base + GICP_CLRSPI_NSR_OFFSET); + iounmap(base); + } - irq_domain_update_bus_token(inner_domain, DOMAIN_BUS_GENERIC_MSI); - inner_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT; - inner_domain->msi_parent_ops = &gicp_msi_parent_ops; - return 0; + return msi_create_parent_irq_domain(&info, &gicp_msi_parent_ops) ? 0 : -ENOMEM; } static const struct of_device_id mvebu_gicp_of_match[] = { diff --git a/drivers/irqchip/irq-mvebu-icu.c b/drivers/irqchip/irq-mvebu-icu.c index 4eebed39880a..db5dbc6e88b0 100644 --- a/drivers/irqchip/irq-mvebu-icu.c +++ b/drivers/irqchip/irq-mvebu-icu.c @@ -20,7 +20,7 @@ #include <linux/of_platform.h> #include <linux/platform_device.h> -#include "irq-msi-lib.h" +#include <linux/irqchip/irq-msi-lib.h> #include <dt-bindings/interrupt-controller/mvebu-icu.h> diff --git a/drivers/irqchip/irq-mvebu-odmi.c b/drivers/irqchip/irq-mvebu-odmi.c index ff19bfd258dc..e5b2bde3d933 100644 --- a/drivers/irqchip/irq-mvebu-odmi.c +++ b/drivers/irqchip/irq-mvebu-odmi.c @@ -18,7 +18,7 @@ #include <linux/of_address.h> #include <linux/slab.h> -#include "irq-msi-lib.h" +#include <linux/irqchip/irq-msi-lib.h> #include <dt-bindings/interrupt-controller/arm-gic.h> @@ -157,6 +157,7 @@ static const struct irq_domain_ops odmi_domain_ops = { static const struct msi_parent_ops odmi_msi_parent_ops = { .supported_flags = ODMI_MSI_FLAGS_SUPPORTED, .required_flags = ODMI_MSI_FLAGS_REQUIRED, + .chip_flags = MSI_CHIP_FLAG_SET_EOI, .bus_select_token = DOMAIN_BUS_GENERIC_MSI, .bus_select_mask = MATCH_PLATFORM_MSI, .prefix = "ODMI-", @@ -166,7 +167,12 @@ static const struct msi_parent_ops odmi_msi_parent_ops = { static int __init mvebu_odmi_init(struct device_node *node, struct device_node *parent) { - struct irq_domain *parent_domain, *inner_domain; + struct irq_domain_info info = { + .fwnode = of_fwnode_handle(node), + .ops = &odmi_domain_ops, + .size = odmis_count * NODMIS_PER_FRAME, + .parent = irq_find_host(parent), + }; int ret, i; if (of_property_read_u32(node, "marvell,odmi-frames", &odmis_count)) @@ -202,22 +208,10 @@ static int __init mvebu_odmi_init(struct device_node *node, } } - parent_domain = irq_find_host(parent); + if (msi_create_parent_irq_domain(&info, &odmi_msi_parent_ops)) + return 0; - inner_domain = irq_domain_create_hierarchy(parent_domain, 0, - odmis_count * NODMIS_PER_FRAME, - of_node_to_fwnode(node), - &odmi_domain_ops, NULL); - if (!inner_domain) { - ret = -ENOMEM; - goto err_unmap; - } - - irq_domain_update_bus_token(inner_domain, DOMAIN_BUS_GENERIC_MSI); - inner_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT; - inner_domain->msi_parent_ops = &odmi_msi_parent_ops; - - return 0; + ret = -ENOMEM; err_unmap: for (i = 0; i < odmis_count; i++) { diff --git a/drivers/irqchip/irq-mvebu-pic.c b/drivers/irqchip/irq-mvebu-pic.c index 3888b7585981..8db638aa21d2 100644 --- a/drivers/irqchip/irq-mvebu-pic.c +++ b/drivers/irqchip/irq-mvebu-pic.c @@ -150,8 +150,8 @@ static int mvebu_pic_probe(struct platform_device *pdev) return -EINVAL; } - pic->domain = irq_domain_add_linear(node, PIC_MAX_IRQS, - &mvebu_pic_domain_ops, pic); + pic->domain = irq_domain_create_linear(of_fwnode_handle(node), PIC_MAX_IRQS, + &mvebu_pic_domain_ops, pic); if (!pic->domain) { dev_err(&pdev->dev, "Failed to allocate irq domain\n"); return -ENOMEM; diff --git a/drivers/irqchip/irq-mvebu-sei.c b/drivers/irqchip/irq-mvebu-sei.c index 065166ab5dbc..5822ea864765 100644 --- a/drivers/irqchip/irq-mvebu-sei.c +++ b/drivers/irqchip/irq-mvebu-sei.c @@ -14,7 +14,7 @@ #include <linux/of_irq.h> #include <linux/of_platform.h> -#include "irq-msi-lib.h" +#include <linux/irqchip/irq-msi-lib.h> /* Cause register */ #define GICP_SECR(idx) (0x0 + ((idx) * 0x4)) @@ -356,6 +356,7 @@ static void mvebu_sei_reset(struct mvebu_sei *sei) static const struct msi_parent_ops sei_msi_parent_ops = { .supported_flags = SEI_MSI_FLAGS_SUPPORTED, .required_flags = SEI_MSI_FLAGS_REQUIRED, + .chip_flags = MSI_CHIP_FLAG_SET_EOI | MSI_CHIP_FLAG_SET_ACK, .bus_select_mask = MATCH_PLATFORM_MSI, .bus_select_token = DOMAIN_BUS_GENERIC_MSI, .prefix = "SEI-", @@ -365,6 +366,10 @@ static const struct msi_parent_ops sei_msi_parent_ops = { static int mvebu_sei_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; + struct irq_domain_info info = { + .fwnode = of_fwnode_handle(node), + .ops = &mvebu_sei_cp_domain_ops, + }; struct mvebu_sei *sei; u32 parent_irq; int ret; @@ -401,7 +406,7 @@ static int mvebu_sei_probe(struct platform_device *pdev) } /* Create the root SEI domain */ - sei->sei_domain = irq_domain_create_linear(of_node_to_fwnode(node), + sei->sei_domain = irq_domain_create_linear(of_fwnode_handle(node), (sei->caps->ap_range.size + sei->caps->cp_range.size), &mvebu_sei_domain_ops, @@ -417,7 +422,7 @@ static int mvebu_sei_probe(struct platform_device *pdev) /* Create the 'wired' domain */ sei->ap_domain = irq_domain_create_hierarchy(sei->sei_domain, 0, sei->caps->ap_range.size, - of_node_to_fwnode(node), + of_fwnode_handle(node), &mvebu_sei_ap_domain_ops, sei); if (!sei->ap_domain) { @@ -429,21 +434,17 @@ static int mvebu_sei_probe(struct platform_device *pdev) irq_domain_update_bus_token(sei->ap_domain, DOMAIN_BUS_WIRED); /* Create the 'MSI' domain */ - sei->cp_domain = irq_domain_create_hierarchy(sei->sei_domain, 0, - sei->caps->cp_range.size, - of_node_to_fwnode(node), - &mvebu_sei_cp_domain_ops, - sei); + info.size = sei->caps->cp_range.size; + info.host_data = sei; + info.parent = sei->sei_domain; + + sei->cp_domain = msi_create_parent_irq_domain(&info, &sei_msi_parent_ops); if (!sei->cp_domain) { pr_err("Failed to create CPs IRQ domain\n"); ret = -ENOMEM; goto remove_ap_domain; } - irq_domain_update_bus_token(sei->cp_domain, DOMAIN_BUS_GENERIC_MSI); - sei->cp_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT; - sei->cp_domain->msi_parent_ops = &sei_msi_parent_ops; - mvebu_sei_reset(sei); irq_set_chained_handler_and_data(parent_irq, mvebu_sei_handle_cascade_irq, sei); diff --git a/drivers/irqchip/irq-mxs.c b/drivers/irqchip/irq-mxs.c index d67b5da38982..0bb423dd5280 100644 --- a/drivers/irqchip/irq-mxs.c +++ b/drivers/irqchip/irq-mxs.c @@ -162,8 +162,8 @@ static const struct irq_domain_ops icoll_irq_domain_ops = { static void __init icoll_add_domain(struct device_node *np, int num) { - icoll_domain = irq_domain_add_linear(np, num, - &icoll_irq_domain_ops, NULL); + icoll_domain = irq_domain_create_linear(of_fwnode_handle(np), num, + &icoll_irq_domain_ops, NULL); if (!icoll_domain) panic("%pOF: unable to create irq domain", np); diff --git a/drivers/irqchip/irq-nvic.c b/drivers/irqchip/irq-nvic.c index ba6332b00a0a..76e11cac9631 100644 --- a/drivers/irqchip/irq-nvic.c +++ b/drivers/irqchip/irq-nvic.c @@ -90,7 +90,7 @@ static int __init nvic_of_init(struct device_node *node, irqs = NVIC_MAX_IRQ; nvic_irq_domain = - irq_domain_add_linear(node, irqs, &nvic_irq_domain_ops, NULL); + irq_domain_create_linear(of_fwnode_handle(node), irqs, &nvic_irq_domain_ops, NULL); if (!nvic_irq_domain) { pr_warn("Failed to allocate irq domain\n"); diff --git a/drivers/irqchip/irq-omap-intc.c b/drivers/irqchip/irq-omap-intc.c index ad84a2f03368..16f00db570e7 100644 --- a/drivers/irqchip/irq-omap-intc.c +++ b/drivers/irqchip/irq-omap-intc.c @@ -248,7 +248,7 @@ static int __init omap_init_irq_of(struct device_node *node) if (WARN_ON(!omap_irq_base)) return -ENOMEM; - domain = irq_domain_add_linear(node, omap_nr_irqs, + domain = irq_domain_create_linear(of_fwnode_handle(node), omap_nr_irqs, &irq_generic_chip_ops, NULL); omap_irq_soft_reset(); @@ -274,7 +274,7 @@ static int __init omap_init_irq_legacy(u32 base, struct device_node *node) irq_base = 0; } - domain = irq_domain_add_legacy(node, omap_nr_irqs, irq_base, 0, + domain = irq_domain_create_legacy(of_fwnode_handle(node), omap_nr_irqs, irq_base, 0, &irq_domain_simple_ops, NULL); omap_irq_soft_reset(); diff --git a/drivers/irqchip/irq-or1k-pic.c b/drivers/irqchip/irq-or1k-pic.c index f289ccd95291..48126067c54b 100644 --- a/drivers/irqchip/irq-or1k-pic.c +++ b/drivers/irqchip/irq-or1k-pic.c @@ -144,8 +144,8 @@ static int __init or1k_pic_init(struct device_node *node, /* Disable all interrupts until explicitly requested */ mtspr(SPR_PICMR, (0UL)); - root_domain = irq_domain_add_linear(node, 32, &or1k_irq_domain_ops, - pic); + root_domain = irq_domain_create_linear(of_fwnode_handle(node), 32, &or1k_irq_domain_ops, + pic); set_handle_irq(or1k_pic_handle_irq); diff --git a/drivers/irqchip/irq-orion.c b/drivers/irqchip/irq-orion.c index 4e4e874e09a8..dddbc05917c0 100644 --- a/drivers/irqchip/irq-orion.c +++ b/drivers/irqchip/irq-orion.c @@ -59,7 +59,7 @@ static int __init orion_irq_init(struct device_node *np, /* count number of irq chips by valid reg addresses */ num_chips = of_address_count(np); - orion_irq_domain = irq_domain_add_linear(np, + orion_irq_domain = irq_domain_create_linear(of_fwnode_handle(np), num_chips * ORION_IRQS_PER_CHIP, &irq_generic_chip_ops, NULL); if (!orion_irq_domain) @@ -146,8 +146,8 @@ static int __init orion_bridge_irq_init(struct device_node *np, /* get optional number of interrupts provided */ of_property_read_u32(np, "marvell,#interrupts", &nrirqs); - domain = irq_domain_add_linear(np, nrirqs, - &irq_generic_chip_ops, NULL); + domain = irq_domain_create_linear(of_fwnode_handle(np), nrirqs, + &irq_generic_chip_ops, NULL); if (!domain) { pr_err("%pOFn: unable to add irq domain\n", np); return -ENOMEM; diff --git a/drivers/irqchip/irq-owl-sirq.c b/drivers/irqchip/irq-owl-sirq.c index 6e4127465094..3d93d21f6732 100644 --- a/drivers/irqchip/irq-owl-sirq.c +++ b/drivers/irqchip/irq-owl-sirq.c @@ -323,8 +323,8 @@ static int __init owl_sirq_init(const struct owl_sirq_params *params, owl_sirq_clear_set_extctl(chip_data, 0, INTC_EXTCTL_CLK_SEL, i); } - domain = irq_domain_add_hierarchy(parent_domain, 0, NUM_SIRQ, node, - &owl_sirq_domain_ops, chip_data); + domain = irq_domain_create_hierarchy(parent_domain, 0, NUM_SIRQ, of_fwnode_handle(node), + &owl_sirq_domain_ops, chip_data); if (!domain) { pr_err("%pOF: failed to add domain\n", node); ret = -ENOMEM; diff --git a/drivers/irqchip/irq-pic32-evic.c b/drivers/irqchip/irq-pic32-evic.c index eb6ca516a166..5dfda8e8df10 100644 --- a/drivers/irqchip/irq-pic32-evic.c +++ b/drivers/irqchip/irq-pic32-evic.c @@ -227,9 +227,9 @@ static int __init pic32_of_init(struct device_node *node, goto err_iounmap; } - evic_irq_domain = irq_domain_add_linear(node, nchips * 32, - &pic32_irq_domain_ops, - priv); + evic_irq_domain = irq_domain_create_linear(of_fwnode_handle(node), nchips * 32, + &pic32_irq_domain_ops, + priv); if (!evic_irq_domain) { ret = -ENOMEM; goto err_free_priv; @@ -291,7 +291,7 @@ static int __init pic32_of_init(struct device_node *node, gc->private = &priv[i]; } - irq_set_default_host(evic_irq_domain); + irq_set_default_domain(evic_irq_domain); /* * External interrupts have software configurable edge polarity. These diff --git a/drivers/irqchip/irq-pruss-intc.c b/drivers/irqchip/irq-pruss-intc.c index bee01980b463..87a5813fd835 100644 --- a/drivers/irqchip/irq-pruss-intc.c +++ b/drivers/irqchip/irq-pruss-intc.c @@ -555,8 +555,8 @@ static int pruss_intc_probe(struct platform_device *pdev) mutex_init(&intc->lock); - intc->domain = irq_domain_add_linear(dev->of_node, max_system_events, - &pruss_intc_irq_domain_ops, intc); + intc->domain = irq_domain_create_linear(of_fwnode_handle(dev->of_node), max_system_events, + &pruss_intc_irq_domain_ops, intc); if (!intc->domain) return -ENOMEM; @@ -581,8 +581,7 @@ static int pruss_intc_probe(struct platform_device *pdev) host_data->intc = intc; host_data->host_irq = i; - irq_set_handler_data(irq, host_data); - irq_set_chained_handler(irq, pruss_intc_irq_handler); + irq_set_chained_handler_and_data(irq, pruss_intc_irq_handler, host_data); } return 0; diff --git a/drivers/irqchip/irq-qcom-mpm.c b/drivers/irqchip/irq-qcom-mpm.c index 7942d8eb3d00..8d569f7c5a7a 100644 --- a/drivers/irqchip/irq-qcom-mpm.c +++ b/drivers/irqchip/irq-qcom-mpm.c @@ -227,6 +227,9 @@ static int qcom_mpm_alloc(struct irq_domain *domain, unsigned int virq, if (ret) return ret; + if (pin == GPIO_NO_WAKE_IRQ) + return irq_domain_disconnect_hierarchy(domain, virq); + ret = irq_domain_set_hwirq_and_chip(domain, virq, pin, &qcom_mpm_chip, priv); if (ret) @@ -447,7 +450,7 @@ static int qcom_mpm_init(struct device_node *np, struct device_node *parent) priv->domain = irq_domain_create_hierarchy(parent_domain, IRQ_DOMAIN_FLAG_QCOM_MPM_WAKEUP, pin_cnt, - of_node_to_fwnode(np), &qcom_mpm_ops, priv); + of_fwnode_handle(np), &qcom_mpm_ops, priv); if (!priv->domain) { dev_err(dev, "failed to create MPM domain\n"); ret = -ENOMEM; diff --git a/drivers/irqchip/irq-realtek-rtl.c b/drivers/irqchip/irq-realtek-rtl.c index 2a349082af81..942c1f8c363d 100644 --- a/drivers/irqchip/irq-realtek-rtl.c +++ b/drivers/irqchip/irq-realtek-rtl.c @@ -162,7 +162,7 @@ static int __init realtek_rtl_of_init(struct device_node *node, struct device_no else if (!parent_irq) return -ENODEV; - domain = irq_domain_add_linear(node, RTL_ICTL_NUM_INPUTS, &irq_domain_ops, NULL); + domain = irq_domain_create_linear(of_fwnode_handle(node), RTL_ICTL_NUM_INPUTS, &irq_domain_ops, NULL); if (!domain) return -ENOMEM; diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c index 954419f2460d..0959ed43b1a9 100644 --- a/drivers/irqchip/irq-renesas-intc-irqpin.c +++ b/drivers/irqchip/irq-renesas-intc-irqpin.c @@ -513,8 +513,10 @@ static int intc_irqpin_probe(struct platform_device *pdev) irq_chip->irq_set_wake = intc_irqpin_irq_set_wake; irq_chip->flags = IRQCHIP_MASK_ON_SUSPEND; - p->irq_domain = irq_domain_add_simple(dev->of_node, nirqs, 0, - &intc_irqpin_irq_domain_ops, p); + p->irq_domain = irq_domain_create_simple(of_fwnode_handle(dev->of_node), + nirqs, 0, + &intc_irqpin_irq_domain_ops, + p); if (!p->irq_domain) { ret = -ENXIO; dev_err(dev, "cannot initialize irq domain\n"); diff --git a/drivers/irqchip/irq-renesas-irqc.c b/drivers/irqchip/irq-renesas-irqc.c index cbce8ffc7de4..5c3196e5a437 100644 --- a/drivers/irqchip/irq-renesas-irqc.c +++ b/drivers/irqchip/irq-renesas-irqc.c @@ -168,8 +168,8 @@ static int irqc_probe(struct platform_device *pdev) p->cpu_int_base = p->iomem + IRQC_INT_CPU_BASE(0); /* SYS-SPI */ - p->irq_domain = irq_domain_add_linear(dev->of_node, p->number_of_irqs, - &irq_generic_chip_ops, p); + p->irq_domain = irq_domain_create_linear(of_fwnode_handle(dev->of_node), p->number_of_irqs, + &irq_generic_chip_ops, p); if (!p->irq_domain) { ret = -ENXIO; dev_err(dev, "cannot initialize irq domain\n"); diff --git a/drivers/irqchip/irq-renesas-rza1.c b/drivers/irqchip/irq-renesas-rza1.c index d4e6a68889ec..0a9640ba0adb 100644 --- a/drivers/irqchip/irq-renesas-rza1.c +++ b/drivers/irqchip/irq-renesas-rza1.c @@ -231,9 +231,9 @@ static int rza1_irqc_probe(struct platform_device *pdev) priv->chip.irq_set_type = rza1_irqc_set_type; priv->chip.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE; - priv->irq_domain = irq_domain_add_hierarchy(parent, 0, IRQC_NUM_IRQ, - np, &rza1_irqc_domain_ops, - priv); + priv->irq_domain = irq_domain_create_hierarchy(parent, 0, IRQC_NUM_IRQ, + of_fwnode_handle(np), &rza1_irqc_domain_ops, + priv); if (!priv->irq_domain) { dev_err(dev, "cannot initialize irq domain\n"); ret = -ENOMEM; diff --git a/drivers/irqchip/irq-renesas-rzg2l.c b/drivers/irqchip/irq-renesas-rzg2l.c index 99e27e01b0b1..1e861bd64f97 100644 --- a/drivers/irqchip/irq-renesas-rzg2l.c +++ b/drivers/irqchip/irq-renesas-rzg2l.c @@ -541,53 +541,45 @@ static int rzg2l_irqc_common_init(struct device_node *node, struct device_node * return -ENODEV; parent_domain = irq_find_host(parent); - if (!parent_domain) { - dev_err(&pdev->dev, "cannot find parent domain\n"); - return -ENODEV; - } + if (!parent_domain) + return dev_err_probe(dev, -ENODEV, "cannot find parent domain\n"); - rzg2l_irqc_data = devm_kzalloc(&pdev->dev, sizeof(*rzg2l_irqc_data), GFP_KERNEL); + rzg2l_irqc_data = devm_kzalloc(dev, sizeof(*rzg2l_irqc_data), GFP_KERNEL); if (!rzg2l_irqc_data) return -ENOMEM; rzg2l_irqc_data->irqchip = irq_chip; - rzg2l_irqc_data->base = devm_of_iomap(&pdev->dev, pdev->dev.of_node, 0, NULL); + rzg2l_irqc_data->base = devm_of_iomap(dev, dev->of_node, 0, NULL); if (IS_ERR(rzg2l_irqc_data->base)) return PTR_ERR(rzg2l_irqc_data->base); ret = rzg2l_irqc_parse_interrupts(rzg2l_irqc_data, node); - if (ret) { - dev_err(&pdev->dev, "cannot parse interrupts: %d\n", ret); - return ret; - } - - resetn = devm_reset_control_get_exclusive(&pdev->dev, NULL); - if (IS_ERR(resetn)) - return PTR_ERR(resetn); + if (ret) + return dev_err_probe(dev, ret, "cannot parse interrupts: %d\n", ret); - ret = reset_control_deassert(resetn); - if (ret) { - dev_err(&pdev->dev, "failed to deassert resetn pin, %d\n", ret); - return ret; + resetn = devm_reset_control_get_exclusive_deasserted(dev, NULL); + if (IS_ERR(resetn)) { + return dev_err_probe(dev, PTR_ERR(resetn), + "failed to acquire deasserted reset: %d\n", ret); } - pm_runtime_enable(&pdev->dev); - ret = pm_runtime_resume_and_get(&pdev->dev); - if (ret < 0) { - dev_err(&pdev->dev, "pm_runtime_resume_and_get failed: %d\n", ret); - goto pm_disable; - } + ret = devm_pm_runtime_enable(dev); + if (ret) + return dev_err_probe(dev, ret, "devm_pm_runtime_enable failed: %d\n", ret); + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return dev_err_probe(dev, ret, "pm_runtime_resume_and_get failed: %d\n", ret); raw_spin_lock_init(&rzg2l_irqc_data->lock); - irq_domain = irq_domain_add_hierarchy(parent_domain, 0, IRQC_NUM_IRQ, - node, &rzg2l_irqc_domain_ops, - rzg2l_irqc_data); + irq_domain = irq_domain_create_hierarchy(parent_domain, 0, IRQC_NUM_IRQ, + of_fwnode_handle(node), &rzg2l_irqc_domain_ops, + rzg2l_irqc_data); if (!irq_domain) { - dev_err(&pdev->dev, "failed to add irq domain\n"); - ret = -ENOMEM; - goto pm_put; + pm_runtime_put(dev); + return dev_err_probe(dev, -ENOMEM, "failed to add irq domain\n"); } register_syscore_ops(&rzg2l_irqc_syscore_ops); @@ -604,13 +596,6 @@ static int rzg2l_irqc_common_init(struct device_node *node, struct device_node * dev = NULL; return 0; - -pm_put: - pm_runtime_put(&pdev->dev); -pm_disable: - pm_runtime_disable(&pdev->dev); - reset_control_assert(resetn); - return ret; } static int __init rzg2l_irqc_init(struct device_node *node, diff --git a/drivers/irqchip/irq-renesas-rzv2h.c b/drivers/irqchip/irq-renesas-rzv2h.c index fe2d29e91026..76fb1354e2aa 100644 --- a/drivers/irqchip/irq-renesas-rzv2h.c +++ b/drivers/irqchip/irq-renesas-rzv2h.c @@ -15,6 +15,7 @@ #include <linux/err.h> #include <linux/io.h> #include <linux/irqchip.h> +#include <linux/irqchip/irq-renesas-rzv2h.h> #include <linux/irqdomain.h> #include <linux/of_address.h> #include <linux/of_platform.h> @@ -41,6 +42,8 @@ #define ICU_TSCLR 0x24 #define ICU_TITSR(k) (0x28 + (k) * 4) #define ICU_TSSR(k) (0x30 + (k) * 4) +#define ICU_DMkSELy(k, y) (0x420 + (k) * 0x20 + (y) * 4) +#define ICU_DMACKSELk(k) (0x500 + (k) * 4) /* NMI */ #define ICU_NMI_EDGE_FALLING 0 @@ -64,11 +67,18 @@ #define ICU_TINT_LEVEL_HIGH 2 #define ICU_TINT_LEVEL_LOW 3 -#define ICU_TSSR_K(tint_nr) ((tint_nr) / 4) -#define ICU_TSSR_TSSEL_N(tint_nr) ((tint_nr) % 4) -#define ICU_TSSR_TSSEL_PREP(tssel, n) ((tssel) << ((n) * 8)) -#define ICU_TSSR_TSSEL_MASK(n) ICU_TSSR_TSSEL_PREP(0x7F, n) -#define ICU_TSSR_TIEN(n) (BIT(7) << ((n) * 8)) +#define ICU_TSSR_TSSEL_PREP(tssel, n, field_width) ((tssel) << ((n) * (field_width))) +#define ICU_TSSR_TSSEL_MASK(n, field_width) \ +({\ + typeof(field_width) (_field_width) = (field_width); \ + ICU_TSSR_TSSEL_PREP((GENMASK(((_field_width) - 2), 0)), (n), _field_width); \ +}) + +#define ICU_TSSR_TIEN(n, field_width) \ +({\ + typeof(field_width) (_field_width) = (field_width); \ + BIT((_field_width) - 1) << ((n) * (_field_width)); \ +}) #define ICU_TITSR_K(tint_nr) ((tint_nr) / 16) #define ICU_TITSR_TITSEL_N(tint_nr) ((tint_nr) % 16) @@ -78,22 +88,68 @@ #define ICU_TINT_EXTRACT_HWIRQ(x) FIELD_GET(GENMASK(15, 0), (x)) #define ICU_TINT_EXTRACT_GPIOINT(x) FIELD_GET(GENMASK(31, 16), (x)) -#define ICU_PB5_TINT 0x55 +#define ICU_RZG3E_TINT_OFFSET 0x800 +#define ICU_RZG3E_TSSEL_MAX_VAL 0x8c +#define ICU_RZV2H_TSSEL_MAX_VAL 0x55 + +/** + * struct rzv2h_hw_info - Interrupt Control Unit controller hardware info structure. + * @tssel_lut: TINT lookup table + * @t_offs: TINT offset + * @max_tssel: TSSEL max value + * @field_width: TSSR field width + */ +struct rzv2h_hw_info { + const u8 *tssel_lut; + u16 t_offs; + u8 max_tssel; + u8 field_width; +}; + +/* DMAC */ +#define ICU_DMAC_DkRQ_SEL_MASK GENMASK(9, 0) + +#define ICU_DMAC_DMAREQ_SHIFT(up) ((up) * 16) +#define ICU_DMAC_DMAREQ_MASK(up) (ICU_DMAC_DkRQ_SEL_MASK \ + << ICU_DMAC_DMAREQ_SHIFT(up)) +#define ICU_DMAC_PREP_DMAREQ(sel, up) (FIELD_PREP(ICU_DMAC_DkRQ_SEL_MASK, (sel)) \ + << ICU_DMAC_DMAREQ_SHIFT(up)) /** * struct rzv2h_icu_priv - Interrupt Control Unit controller private data structure. * @base: Controller's base address - * @irqchip: Pointer to struct irq_chip * @fwspec: IRQ firmware specific data * @lock: Lock to serialize access to hardware registers + * @info: Pointer to struct rzv2h_hw_info */ struct rzv2h_icu_priv { void __iomem *base; - const struct irq_chip *irqchip; struct irq_fwspec fwspec[ICU_NUM_IRQ]; raw_spinlock_t lock; + const struct rzv2h_hw_info *info; }; +void rzv2h_icu_register_dma_req(struct platform_device *icu_dev, u8 dmac_index, u8 dmac_channel, + u16 req_no) +{ + struct rzv2h_icu_priv *priv = platform_get_drvdata(icu_dev); + u32 icu_dmksely, dmareq, dmareq_mask; + u8 y, upper; + + y = dmac_channel / 2; + upper = dmac_channel % 2; + + dmareq = ICU_DMAC_PREP_DMAREQ(req_no, upper); + dmareq_mask = ICU_DMAC_DMAREQ_MASK(upper); + + guard(raw_spinlock_irqsave)(&priv->lock); + + icu_dmksely = readl(priv->base + ICU_DMkSELy(dmac_index, y)); + icu_dmksely = (icu_dmksely & ~dmareq_mask) | dmareq; + writel(icu_dmksely, priv->base + ICU_DMkSELy(dmac_index, y)); +} +EXPORT_SYMBOL_GPL(rzv2h_icu_register_dma_req); + static inline struct rzv2h_icu_priv *irq_data_to_priv(struct irq_data *data) { return data->domain->host_data; @@ -111,7 +167,7 @@ static void rzv2h_icu_eoi(struct irq_data *d) tintirq_nr = hw_irq - ICU_TINT_START; bit = BIT(tintirq_nr); if (!irqd_is_level_type(d)) - writel_relaxed(bit, priv->base + ICU_TSCLR); + writel_relaxed(bit, priv->base + priv->info->t_offs + ICU_TSCLR); } else if (hw_irq >= ICU_IRQ_START) { tintirq_nr = hw_irq - ICU_IRQ_START; bit = BIT(tintirq_nr); @@ -130,21 +186,31 @@ static void rzv2h_tint_irq_endisable(struct irq_data *d, bool enable) struct rzv2h_icu_priv *priv = irq_data_to_priv(d); unsigned int hw_irq = irqd_to_hwirq(d); u32 tint_nr, tssel_n, k, tssr; + u8 nr_tint; if (hw_irq < ICU_TINT_START) return; tint_nr = hw_irq - ICU_TINT_START; - k = ICU_TSSR_K(tint_nr); - tssel_n = ICU_TSSR_TSSEL_N(tint_nr); + nr_tint = 32 / priv->info->field_width; + k = tint_nr / nr_tint; + tssel_n = tint_nr % nr_tint; guard(raw_spinlock)(&priv->lock); - tssr = readl_relaxed(priv->base + ICU_TSSR(k)); + tssr = readl_relaxed(priv->base + priv->info->t_offs + ICU_TSSR(k)); if (enable) - tssr |= ICU_TSSR_TIEN(tssel_n); + tssr |= ICU_TSSR_TIEN(tssel_n, priv->info->field_width); else - tssr &= ~ICU_TSSR_TIEN(tssel_n); - writel_relaxed(tssr, priv->base + ICU_TSSR(k)); + tssr &= ~ICU_TSSR_TIEN(tssel_n, priv->info->field_width); + writel_relaxed(tssr, priv->base + priv->info->t_offs + ICU_TSSR(k)); + + /* + * A glitch in the edge detection circuit can cause a spurious + * interrupt. Clear the status flag after setting the ICU_TSSRk + * registers, which is recommended by the hardware manual as a + * countermeasure. + */ + writel_relaxed(BIT(tint_nr), priv->base + priv->info->t_offs + ICU_TSCLR); } static void rzv2h_icu_irq_disable(struct irq_data *d) @@ -247,8 +313,8 @@ static void rzv2h_clear_tint_int(struct rzv2h_icu_priv *priv, unsigned int hwirq u32 bit = BIT(tint_nr); int k = tint_nr / 16; - tsctr = readl_relaxed(priv->base + ICU_TSCTR); - titsr = readl_relaxed(priv->base + ICU_TITSR(k)); + tsctr = readl_relaxed(priv->base + priv->info->t_offs + ICU_TSCTR); + titsr = readl_relaxed(priv->base + priv->info->t_offs + ICU_TITSR(k)); titsel = ICU_TITSR_TITSEL_GET(titsr, titsel_n); /* @@ -257,7 +323,7 @@ static void rzv2h_clear_tint_int(struct rzv2h_icu_priv *priv, unsigned int hwirq */ if ((tsctr & bit) && ((titsel == ICU_TINT_EDGE_RISING) || (titsel == ICU_TINT_EDGE_FALLING))) - writel_relaxed(bit, priv->base + ICU_TSCLR); + writel_relaxed(bit, priv->base + priv->info->t_offs + ICU_TSCLR); } static int rzv2h_tint_set_type(struct irq_data *d, unsigned int type) @@ -268,6 +334,7 @@ static int rzv2h_tint_set_type(struct irq_data *d, unsigned int type) unsigned int hwirq; u32 tint, sense; int tint_nr; + u8 nr_tint; switch (type & IRQ_TYPE_SENSE_MASK) { case IRQ_TYPE_LEVEL_LOW: @@ -290,39 +357,42 @@ static int rzv2h_tint_set_type(struct irq_data *d, unsigned int type) return -EINVAL; } + priv = irq_data_to_priv(d); tint = (u32)(uintptr_t)irq_data_get_irq_chip_data(d); - if (tint > ICU_PB5_TINT) + if (tint > priv->info->max_tssel) return -EINVAL; - priv = irq_data_to_priv(d); - hwirq = irqd_to_hwirq(d); + if (priv->info->tssel_lut) + tint = priv->info->tssel_lut[tint]; + hwirq = irqd_to_hwirq(d); tint_nr = hwirq - ICU_TINT_START; - tssr_k = ICU_TSSR_K(tint_nr); - tssel_n = ICU_TSSR_TSSEL_N(tint_nr); + nr_tint = 32 / priv->info->field_width; + tssr_k = tint_nr / nr_tint; + tssel_n = tint_nr % nr_tint; + tien = ICU_TSSR_TIEN(tssel_n, priv->info->field_width); titsr_k = ICU_TITSR_K(tint_nr); titsel_n = ICU_TITSR_TITSEL_N(tint_nr); - tien = ICU_TSSR_TIEN(titsel_n); guard(raw_spinlock)(&priv->lock); - tssr = readl_relaxed(priv->base + ICU_TSSR(tssr_k)); - tssr &= ~(ICU_TSSR_TSSEL_MASK(tssel_n) | tien); - tssr |= ICU_TSSR_TSSEL_PREP(tint, tssel_n); + tssr = readl_relaxed(priv->base + priv->info->t_offs + ICU_TSSR(tssr_k)); + tssr &= ~(ICU_TSSR_TSSEL_MASK(tssel_n, priv->info->field_width) | tien); + tssr |= ICU_TSSR_TSSEL_PREP(tint, tssel_n, priv->info->field_width); - writel_relaxed(tssr, priv->base + ICU_TSSR(tssr_k)); + writel_relaxed(tssr, priv->base + priv->info->t_offs + ICU_TSSR(tssr_k)); - titsr = readl_relaxed(priv->base + ICU_TITSR(titsr_k)); + titsr = readl_relaxed(priv->base + priv->info->t_offs + ICU_TITSR(titsr_k)); titsr &= ~ICU_TITSR_TITSEL_MASK(titsel_n); titsr |= ICU_TITSR_TITSEL_PREP(sense, titsel_n); - writel_relaxed(titsr, priv->base + ICU_TITSR(titsr_k)); + writel_relaxed(titsr, priv->base + priv->info->t_offs + ICU_TITSR(titsr_k)); rzv2h_clear_tint_int(priv, hwirq); - writel_relaxed(tssr | tien, priv->base + ICU_TSSR(tssr_k)); + writel_relaxed(tssr | tien, priv->base + priv->info->t_offs + ICU_TSSR(tssr_k)); return 0; } @@ -357,7 +427,9 @@ static const struct irq_chip rzv2h_icu_chip = { .irq_retrigger = irq_chip_retrigger_hierarchy, .irq_set_type = rzv2h_icu_set_type, .irq_set_affinity = irq_chip_set_affinity_parent, - .flags = IRQCHIP_SET_TYPE_MASKED, + .flags = IRQCHIP_MASK_ON_SUSPEND | + IRQCHIP_SET_TYPE_MASKED | + IRQCHIP_SKIP_SET_WAKE, }; static int rzv2h_icu_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs, @@ -390,7 +462,7 @@ static int rzv2h_icu_alloc(struct irq_domain *domain, unsigned int virq, unsigne if (hwirq > (ICU_NUM_IRQ - 1)) return -EINVAL; - ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, priv->irqchip, + ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, &rzv2h_icu_chip, (void *)(uintptr_t)tint); if (ret) return ret; @@ -421,7 +493,13 @@ static int rzv2h_icu_parse_interrupts(struct rzv2h_icu_priv *priv, struct device return 0; } -static int rzv2h_icu_init(struct device_node *node, struct device_node *parent) +static void rzv2h_icu_put_device(void *data) +{ + put_device(data); +} + +static int rzv2h_icu_init_common(struct device_node *node, struct device_node *parent, + const struct rzv2h_hw_info *hw_info) { struct irq_domain *irq_domain, *parent_domain; struct rzv2h_icu_priv *rzv2h_icu_data; @@ -433,62 +511,65 @@ static int rzv2h_icu_init(struct device_node *node, struct device_node *parent) if (!pdev) return -ENODEV; + ret = devm_add_action_or_reset(&pdev->dev, rzv2h_icu_put_device, + &pdev->dev); + if (ret < 0) + return ret; + parent_domain = irq_find_host(parent); if (!parent_domain) { dev_err(&pdev->dev, "cannot find parent domain\n"); - ret = -ENODEV; - goto put_dev; + return -ENODEV; } rzv2h_icu_data = devm_kzalloc(&pdev->dev, sizeof(*rzv2h_icu_data), GFP_KERNEL); - if (!rzv2h_icu_data) { - ret = -ENOMEM; - goto put_dev; - } + if (!rzv2h_icu_data) + return -ENOMEM; - rzv2h_icu_data->irqchip = &rzv2h_icu_chip; + platform_set_drvdata(pdev, rzv2h_icu_data); rzv2h_icu_data->base = devm_of_iomap(&pdev->dev, pdev->dev.of_node, 0, NULL); - if (IS_ERR(rzv2h_icu_data->base)) { - ret = PTR_ERR(rzv2h_icu_data->base); - goto put_dev; - } + if (IS_ERR(rzv2h_icu_data->base)) + return PTR_ERR(rzv2h_icu_data->base); ret = rzv2h_icu_parse_interrupts(rzv2h_icu_data, node); if (ret) { dev_err(&pdev->dev, "cannot parse interrupts: %d\n", ret); - goto put_dev; + return ret; } - resetn = devm_reset_control_get_exclusive(&pdev->dev, NULL); + resetn = devm_reset_control_get_exclusive_deasserted(&pdev->dev, NULL); if (IS_ERR(resetn)) { ret = PTR_ERR(resetn); - goto put_dev; + dev_err(&pdev->dev, "failed to acquire deasserted reset: %d\n", ret); + return ret; } - ret = reset_control_deassert(resetn); - if (ret) { - dev_err(&pdev->dev, "failed to deassert resetn pin, %d\n", ret); - goto put_dev; + ret = devm_pm_runtime_enable(&pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "devm_pm_runtime_enable failed, %d\n", ret); + return ret; } - pm_runtime_enable(&pdev->dev); ret = pm_runtime_resume_and_get(&pdev->dev); if (ret < 0) { dev_err(&pdev->dev, "pm_runtime_resume_and_get failed: %d\n", ret); - goto pm_disable; + return ret; } raw_spin_lock_init(&rzv2h_icu_data->lock); - irq_domain = irq_domain_add_hierarchy(parent_domain, 0, ICU_NUM_IRQ, node, - &rzv2h_icu_domain_ops, rzv2h_icu_data); + irq_domain = irq_domain_create_hierarchy(parent_domain, 0, ICU_NUM_IRQ, + of_fwnode_handle(node), &rzv2h_icu_domain_ops, + rzv2h_icu_data); if (!irq_domain) { dev_err(&pdev->dev, "failed to add irq domain\n"); ret = -ENOMEM; goto pm_put; } + rzv2h_icu_data->info = hw_info; + /* * coccicheck complains about a missing put_device call before returning, but it's a false * positive. We still need &pdev->dev after successfully returning from this function. @@ -497,16 +578,61 @@ static int rzv2h_icu_init(struct device_node *node, struct device_node *parent) pm_put: pm_runtime_put(&pdev->dev); -pm_disable: - pm_runtime_disable(&pdev->dev); - reset_control_assert(resetn); -put_dev: - put_device(&pdev->dev); return ret; } +/* Mapping based on port index on Table 4.2-6 and TSSEL bits on Table 4.6-4 */ +static const u8 rzg3e_tssel_lut[] = { + 81, 82, 83, 84, 85, 86, 87, 88, /* P00-P07 */ + 89, 90, 91, 92, 93, 94, 95, 96, /* P10-P17 */ + 111, 112, /* P20-P21 */ + 97, 98, 99, 100, 101, 102, 103, 104, /* P30-P37 */ + 105, 106, 107, 108, 109, 110, /* P40-P45 */ + 113, 114, 115, 116, 117, 118, 119, /* P50-P56 */ + 120, 121, 122, 123, 124, 125, 126, /* P60-P66 */ + 127, 128, 129, 130, 131, 132, 133, 134, /* P70-P77 */ + 135, 136, 137, 138, 139, 140, /* P80-P85 */ + 43, 44, 45, 46, 47, 48, 49, 50, /* PA0-PA7 */ + 51, 52, 53, 54, 55, 56, 57, 58, /* PB0-PB7 */ + 59, 60, 61, /* PC0-PC2 */ + 62, 63, 64, 65, 66, 67, 68, 69, /* PD0-PD7 */ + 70, 71, 72, 73, 74, 75, 76, 77, /* PE0-PE7 */ + 78, 79, 80, /* PF0-PF2 */ + 25, 26, 27, 28, 29, 30, 31, 32, /* PG0-PG7 */ + 33, 34, 35, 36, 37, 38, /* PH0-PH5 */ + 4, 5, 6, 7, 8, /* PJ0-PJ4 */ + 39, 40, 41, 42, /* PK0-PK3 */ + 9, 10, 11, 12, 21, 22, 23, 24, /* PL0-PL7 */ + 13, 14, 15, 16, 17, 18, 19, 20, /* PM0-PM7 */ + 0, 1, 2, 3 /* PS0-PS3 */ +}; + +static const struct rzv2h_hw_info rzg3e_hw_params = { + .tssel_lut = rzg3e_tssel_lut, + .t_offs = ICU_RZG3E_TINT_OFFSET, + .max_tssel = ICU_RZG3E_TSSEL_MAX_VAL, + .field_width = 16, +}; + +static const struct rzv2h_hw_info rzv2h_hw_params = { + .t_offs = 0, + .max_tssel = ICU_RZV2H_TSSEL_MAX_VAL, + .field_width = 8, +}; + +static int rzg3e_icu_init(struct device_node *node, struct device_node *parent) +{ + return rzv2h_icu_init_common(node, parent, &rzg3e_hw_params); +} + +static int rzv2h_icu_init(struct device_node *node, struct device_node *parent) +{ + return rzv2h_icu_init_common(node, parent, &rzv2h_hw_params); +} + IRQCHIP_PLATFORM_DRIVER_BEGIN(rzv2h_icu) +IRQCHIP_MATCH("renesas,r9a09g047-icu", rzg3e_icu_init) IRQCHIP_MATCH("renesas,r9a09g057-icu", rzv2h_icu_init) IRQCHIP_PLATFORM_DRIVER_END(rzv2h_icu) MODULE_AUTHOR("Fabrizio Castro <fabrizio.castro.jz@renesas.com>"); diff --git a/drivers/irqchip/irq-riscv-aplic-direct.c b/drivers/irqchip/irq-riscv-aplic-direct.c index 7cd6b646774b..205ad61d15e4 100644 --- a/drivers/irqchip/irq-riscv-aplic-direct.c +++ b/drivers/irqchip/irq-riscv-aplic-direct.c @@ -31,7 +31,7 @@ struct aplic_direct { }; struct aplic_idc { - unsigned int hart_index; + u32 hart_index; void __iomem *regs; struct aplic_direct *direct; }; @@ -219,6 +219,20 @@ static int aplic_direct_parse_parent_hwirq(struct device *dev, u32 index, return 0; } +static int aplic_direct_get_hart_index(struct device *dev, u32 logical_index, + u32 *hart_index) +{ + const char *prop_hart_index = "riscv,hart-indexes"; + struct device_node *np = to_of_node(dev->fwnode); + + if (!np || !of_property_present(np, prop_hart_index)) { + *hart_index = logical_index; + return 0; + } + + return of_property_read_u32_index(np, prop_hart_index, logical_index, hart_index); +} + int aplic_direct_setup(struct device *dev, void __iomem *regs) { int i, j, rc, cpu, current_cpu, setup_count = 0; @@ -265,8 +279,12 @@ int aplic_direct_setup(struct device *dev, void __iomem *regs) cpumask_set_cpu(cpu, &direct->lmask); idc = per_cpu_ptr(&aplic_idcs, cpu); - idc->hart_index = i; - idc->regs = priv->regs + APLIC_IDC_BASE + i * APLIC_IDC_SIZE; + rc = aplic_direct_get_hart_index(dev, i, &idc->hart_index); + if (rc) { + dev_warn(dev, "hart index not found for IDC%d\n", i); + continue; + } + idc->regs = priv->regs + APLIC_IDC_BASE + idc->hart_index * APLIC_IDC_SIZE; idc->direct = direct; aplic_idc_set_delivery(idc, true); diff --git a/drivers/irqchip/irq-riscv-imsic-early.c b/drivers/irqchip/irq-riscv-imsic-early.c index c5c2e6929a2f..d9ae87808651 100644 --- a/drivers/irqchip/irq-riscv-imsic-early.c +++ b/drivers/irqchip/irq-riscv-imsic-early.c @@ -27,7 +27,7 @@ static void imsic_ipi_send(unsigned int cpu) { struct imsic_local_config *local = per_cpu_ptr(imsic->global.local, cpu); - writel_relaxed(IMSIC_IPI_ID, local->msi_va); + writel(IMSIC_IPI_ID, local->msi_va); } static void imsic_ipi_starting_cpu(void) @@ -73,10 +73,16 @@ static int __init imsic_ipi_domain_init(void) { return 0; } static void imsic_handle_irq(struct irq_desc *desc) { struct irq_chip *chip = irq_desc_get_chip(desc); - int err, cpu = smp_processor_id(); + int cpu = smp_processor_id(); struct imsic_vector *vec; unsigned long local_id; + /* + * Process pending local synchronization instead of waiting + * for per-CPU local timer to expire. + */ + imsic_local_sync_all(false); + chained_irq_enter(chip, desc); while ((local_id = csr_swap(CSR_TOPEI, 0))) { @@ -97,9 +103,7 @@ static void imsic_handle_irq(struct irq_desc *desc) continue; } - err = generic_handle_domain_irq(imsic->base_domain, vec->hwirq); - if (unlikely(err)) - pr_warn_ratelimited("hwirq 0x%x mapping not found\n", vec->hwirq); + generic_handle_irq(vec->irq); } chained_irq_exit(chip, desc); @@ -120,7 +124,7 @@ static int imsic_starting_cpu(unsigned int cpu) * Interrupts identities might have been enabled/disabled while * this CPU was not running so sync-up local enable/disable state. */ - imsic_local_sync_all(); + imsic_local_sync_all(true); /* Enable local interrupt delivery */ imsic_local_delivery(true); diff --git a/drivers/irqchip/irq-riscv-imsic-platform.c b/drivers/irqchip/irq-riscv-imsic-platform.c index c708780e8760..1b9fbfce9581 100644 --- a/drivers/irqchip/irq-riscv-imsic-platform.c +++ b/drivers/irqchip/irq-riscv-imsic-platform.c @@ -20,6 +20,7 @@ #include <linux/spinlock.h> #include <linux/smp.h> +#include <linux/irqchip/irq-msi-lib.h> #include "irq-riscv-imsic-state.h" static bool imsic_cpu_page_phys(unsigned int cpu, unsigned int guest_index, @@ -63,6 +64,11 @@ static int imsic_irq_retrigger(struct irq_data *d) return 0; } +static void imsic_irq_ack(struct irq_data *d) +{ + irq_move_irq(d); +} + static void imsic_irq_compose_vector_msg(struct imsic_vector *vec, struct msi_msg *msg) { phys_addr_t msi_addr; @@ -96,9 +102,23 @@ static int imsic_irq_set_affinity(struct irq_data *d, const struct cpumask *mask bool force) { struct imsic_vector *old_vec, *new_vec; - struct irq_data *pd = d->parent_data; + struct imsic_vector tmp_vec; + + /* + * Requirements for the downstream irqdomains (or devices): + * + * 1) Downstream irqdomains (or devices) with atomic MSI update can + * happily do imsic_irq_set_affinity() in the process-context on + * any CPU so the irqchip of such irqdomains must not set the + * IRQCHIP_MOVE_DEFERRED flag. + * + * 2) Downstream irqdomains (or devices) with non-atomic MSI update + * must use imsic_irq_set_affinity() in nterrupt-context upon + * the next device interrupt so the irqchip of such irqdomains + * must set the IRQCHIP_MOVE_DEFERRED flag. + */ - old_vec = irq_data_get_irq_chip_data(pd); + old_vec = irq_data_get_irq_chip_data(d); if (WARN_ON(!old_vec)) return -ENOENT; @@ -111,34 +131,95 @@ static int imsic_irq_set_affinity(struct irq_data *d, const struct cpumask *mask return -EBUSY; /* Get a new vector on the desired set of CPUs */ - new_vec = imsic_vector_alloc(old_vec->hwirq, mask_val); + new_vec = imsic_vector_alloc(old_vec->irq, mask_val); if (!new_vec) return -ENOSPC; + /* + * Device having non-atomic MSI update might see an intermediate + * state when changing target IMSIC vector from one CPU to another. + * + * To avoid losing interrupt to such intermediate state, do the + * following (just like x86 APIC): + * + * 1) First write a temporary IMSIC vector to the device which + * has MSI address same as the old IMSIC vector but MSI data + * matches the new IMSIC vector. + * + * 2) Next write the new IMSIC vector to the device. + * + * Based on the above, __imsic_local_sync() must check pending + * status of both old MSI data and new MSI data on the old CPU. + */ + if (!irq_can_move_in_process_context(d) && + new_vec->local_id != old_vec->local_id) { + /* Setup temporary vector */ + tmp_vec.cpu = old_vec->cpu; + tmp_vec.local_id = new_vec->local_id; + + /* Point device to the temporary vector */ + imsic_msi_update_msg(irq_get_irq_data(d->irq), &tmp_vec); + } + /* Point device to the new vector */ - imsic_msi_update_msg(d, new_vec); + imsic_msi_update_msg(irq_get_irq_data(d->irq), new_vec); /* Update irq descriptors with the new vector */ - pd->chip_data = new_vec; + d->chip_data = new_vec; - /* Update effective affinity of parent irq data */ - irq_data_update_effective_affinity(pd, cpumask_of(new_vec->cpu)); + /* Update effective affinity */ + irq_data_update_effective_affinity(d, cpumask_of(new_vec->cpu)); /* Move state of the old vector to the new vector */ imsic_vector_move(old_vec, new_vec); return IRQ_SET_MASK_OK_DONE; } + +static void imsic_irq_force_complete_move(struct irq_data *d) +{ + struct imsic_vector *mvec, *vec = irq_data_get_irq_chip_data(d); + unsigned int cpu = smp_processor_id(); + + if (WARN_ON(!vec)) + return; + + /* Do nothing if there is no in-flight move */ + mvec = imsic_vector_get_move(vec); + if (!mvec) + return; + + /* Do nothing if the old IMSIC vector does not belong to current CPU */ + if (mvec->cpu != cpu) + return; + + /* + * The best we can do is force cleanup the old IMSIC vector. + * + * The challenges over here are same as x86 vector domain so + * refer to the comments in irq_force_complete_move() function + * implemented at arch/x86/kernel/apic/vector.c. + */ + + /* Force cleanup in-flight move */ + pr_info("IRQ fixup: irq %d move in progress, old vector cpu %d local_id %d\n", + d->irq, mvec->cpu, mvec->local_id); + imsic_vector_force_move_cleanup(vec); +} #endif static struct irq_chip imsic_irq_base_chip = { - .name = "IMSIC", - .irq_mask = imsic_irq_mask, - .irq_unmask = imsic_irq_unmask, - .irq_retrigger = imsic_irq_retrigger, - .irq_compose_msi_msg = imsic_irq_compose_msg, - .flags = IRQCHIP_SKIP_SET_WAKE | - IRQCHIP_MASK_ON_SUSPEND, + .name = "IMSIC", + .irq_mask = imsic_irq_mask, + .irq_unmask = imsic_irq_unmask, +#ifdef CONFIG_SMP + .irq_set_affinity = imsic_irq_set_affinity, + .irq_force_complete_move = imsic_irq_force_complete_move, +#endif + .irq_retrigger = imsic_irq_retrigger, + .irq_ack = imsic_irq_ack, + .irq_compose_msi_msg = imsic_irq_compose_msg, + .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND, }; static int imsic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, @@ -155,7 +236,7 @@ static int imsic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, return -ENOSPC; irq_domain_set_info(domain, virq, virq, &imsic_irq_base_chip, vec, - handle_simple_irq, NULL, NULL); + handle_edge_irq, NULL, NULL); irq_set_noprobe(virq); irq_set_affinity(virq, cpu_online_mask); irq_data_update_effective_affinity(irq_get_irq_data(virq), cpumask_of(vec->cpu)); @@ -172,22 +253,6 @@ static void imsic_irq_domain_free(struct irq_domain *domain, unsigned int virq, irq_domain_free_irqs_parent(domain, virq, nr_irqs); } -static int imsic_irq_domain_select(struct irq_domain *domain, struct irq_fwspec *fwspec, - enum irq_domain_bus_token bus_token) -{ - const struct msi_parent_ops *ops = domain->msi_parent_ops; - u32 busmask = BIT(bus_token); - - if (fwspec->fwnode != domain->fwnode || fwspec->param_count != 0) - return 0; - - /* Handle pure domain searches */ - if (bus_token == ops->bus_select_token) - return 1; - - return !!(ops->bus_select_mask & busmask); -} - #ifdef CONFIG_GENERIC_IRQ_DEBUGFS static void imsic_irq_debug_show(struct seq_file *m, struct irq_domain *d, struct irq_data *irqd, int ind) @@ -204,107 +269,37 @@ static void imsic_irq_debug_show(struct seq_file *m, struct irq_domain *d, static const struct irq_domain_ops imsic_base_domain_ops = { .alloc = imsic_irq_domain_alloc, .free = imsic_irq_domain_free, - .select = imsic_irq_domain_select, + .select = msi_lib_irq_domain_select, #ifdef CONFIG_GENERIC_IRQ_DEBUGFS .debug_show = imsic_irq_debug_show, #endif }; -#ifdef CONFIG_RISCV_IMSIC_PCI - -static void imsic_pci_mask_irq(struct irq_data *d) -{ - pci_msi_mask_irq(d); - irq_chip_mask_parent(d); -} - -static void imsic_pci_unmask_irq(struct irq_data *d) +static bool imsic_init_dev_msi_info(struct device *dev, struct irq_domain *domain, + struct irq_domain *real_parent, struct msi_domain_info *info) { - irq_chip_unmask_parent(d); - pci_msi_unmask_irq(d); -} - -#define MATCH_PCI_MSI BIT(DOMAIN_BUS_PCI_MSI) - -#else - -#define MATCH_PCI_MSI 0 - -#endif - -static bool imsic_init_dev_msi_info(struct device *dev, - struct irq_domain *domain, - struct irq_domain *real_parent, - struct msi_domain_info *info) -{ - const struct msi_parent_ops *pops = real_parent->msi_parent_ops; - - /* MSI parent domain specific settings */ - switch (real_parent->bus_token) { - case DOMAIN_BUS_NEXUS: - if (WARN_ON_ONCE(domain != real_parent)) - return false; -#ifdef CONFIG_SMP - info->chip->irq_set_affinity = imsic_irq_set_affinity; -#endif - break; - default: - WARN_ON_ONCE(1); + if (!msi_lib_init_dev_msi_info(dev, domain, real_parent, info)) return false; - } - /* Is the target supported? */ switch (info->bus_token) { -#ifdef CONFIG_RISCV_IMSIC_PCI case DOMAIN_BUS_PCI_DEVICE_MSI: case DOMAIN_BUS_PCI_DEVICE_MSIX: - info->chip->irq_mask = imsic_pci_mask_irq; - info->chip->irq_unmask = imsic_pci_unmask_irq; - break; -#endif - case DOMAIN_BUS_DEVICE_MSI: - /* - * Per-device MSI should never have any MSI feature bits - * set. It's sole purpose is to create a dumb interrupt - * chip which has a device specific irq_write_msi_msg() - * callback. - */ - if (WARN_ON_ONCE(info->flags)) - return false; - - /* Core managed MSI descriptors */ - info->flags |= MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS | - MSI_FLAG_FREE_MSI_DESCS; - break; - case DOMAIN_BUS_WIRED_TO_MSI: + info->chip->flags |= IRQCHIP_MOVE_DEFERRED; break; default: - WARN_ON_ONCE(1); - return false; + break; } - /* Use hierarchial chip operations re-trigger */ - info->chip->irq_retrigger = irq_chip_retrigger_hierarchy; - - /* - * Mask out the domain specific MSI feature flags which are not - * supported by the real parent. - */ - info->flags &= pops->supported_flags; - - /* Enforce the required flags */ - info->flags |= pops->required_flags; - return true; } -#define MATCH_PLATFORM_MSI BIT(DOMAIN_BUS_PLATFORM_MSI) - static const struct msi_parent_ops imsic_msi_parent_ops = { .supported_flags = MSI_GENERIC_FLAGS_MASK | MSI_FLAG_PCI_MSIX, .required_flags = MSI_FLAG_USE_DEF_DOM_OPS | - MSI_FLAG_USE_DEF_CHIP_OPS, + MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_PCI_MSI_MASK_PARENT, + .chip_flags = MSI_CHIP_FLAG_SET_ACK, .bus_select_token = DOMAIN_BUS_NEXUS, .bus_select_mask = MATCH_PCI_MSI | MATCH_PLATFORM_MSI, .init_dev_msi_info = imsic_init_dev_msi_info, diff --git a/drivers/irqchip/irq-riscv-imsic-state.c b/drivers/irqchip/irq-riscv-imsic-state.c index b97e6cd89ed7..77670dd645ac 100644 --- a/drivers/irqchip/irq-riscv-imsic-state.c +++ b/drivers/irqchip/irq-riscv-imsic-state.c @@ -124,10 +124,11 @@ void __imsic_eix_update(unsigned long base_id, unsigned long num_id, bool pend, } } -static void __imsic_local_sync(struct imsic_local_priv *lpriv) +static bool __imsic_local_sync(struct imsic_local_priv *lpriv) { - struct imsic_local_config *mlocal; - struct imsic_vector *vec, *mvec; + struct imsic_local_config *tlocal, *mlocal; + struct imsic_vector *vec, *tvec, *mvec; + bool ret = true; int i; lockdep_assert_held(&lpriv->lock); @@ -143,35 +144,97 @@ static void __imsic_local_sync(struct imsic_local_priv *lpriv) __imsic_id_clear_enable(i); /* - * If the ID was being moved to a new ID on some other CPU - * then we can get a MSI during the movement so check the - * ID pending bit and re-trigger the new ID on other CPU - * using MMIO write. + * Clear the previous vector pointer of the new vector only + * after the movement is complete on the old CPU. */ - mvec = READ_ONCE(vec->move); - WRITE_ONCE(vec->move, NULL); - if (mvec && mvec != vec) { - if (__imsic_id_read_clear_pending(i)) { + mvec = READ_ONCE(vec->move_prev); + if (mvec) { + /* + * If the old vector has not been updated then + * try again in the next sync-up call. + */ + if (READ_ONCE(mvec->move_next)) { + ret = false; + continue; + } + + WRITE_ONCE(vec->move_prev, NULL); + } + + /* + * If a vector was being moved to a new vector on some other + * CPU then we can get a MSI during the movement so check the + * ID pending bit and re-trigger the new ID on other CPU using + * MMIO write. + */ + mvec = READ_ONCE(vec->move_next); + if (mvec) { + /* + * Devices having non-atomic MSI update might see + * an intermediate state so check both old ID and + * new ID for pending interrupts. + * + * For details, see imsic_irq_set_affinity(). + */ + tvec = vec->local_id == mvec->local_id ? + NULL : &lpriv->vectors[mvec->local_id]; + + if (tvec && !irq_can_move_in_process_context(irq_get_irq_data(vec->irq)) && + __imsic_id_read_clear_pending(tvec->local_id)) { + /* Retrigger temporary vector if it was already in-use */ + if (READ_ONCE(tvec->enable)) { + tlocal = per_cpu_ptr(imsic->global.local, tvec->cpu); + writel_relaxed(tvec->local_id, tlocal->msi_va); + } + + mlocal = per_cpu_ptr(imsic->global.local, mvec->cpu); + writel_relaxed(mvec->local_id, mlocal->msi_va); + } + + if (__imsic_id_read_clear_pending(vec->local_id)) { mlocal = per_cpu_ptr(imsic->global.local, mvec->cpu); writel_relaxed(mvec->local_id, mlocal->msi_va); } - imsic_vector_free(&lpriv->vectors[i]); + WRITE_ONCE(vec->move_next, NULL); + imsic_vector_free(vec); } skip: bitmap_clear(lpriv->dirty_bitmap, i, 1); } + + return ret; } -void imsic_local_sync_all(void) +#ifdef CONFIG_SMP +static void __imsic_local_timer_start(struct imsic_local_priv *lpriv, unsigned int cpu) +{ + lockdep_assert_held(&lpriv->lock); + + if (!timer_pending(&lpriv->timer)) { + lpriv->timer.expires = jiffies + 1; + add_timer_on(&lpriv->timer, cpu); + } +} +#else +static inline void __imsic_local_timer_start(struct imsic_local_priv *lpriv, unsigned int cpu) +{ +} +#endif + +void imsic_local_sync_all(bool force_all) { struct imsic_local_priv *lpriv = this_cpu_ptr(imsic->lpriv); unsigned long flags; raw_spin_lock_irqsave(&lpriv->lock, flags); - bitmap_fill(lpriv->dirty_bitmap, imsic->global.nr_ids + 1); - __imsic_local_sync(lpriv); + + if (force_all) + bitmap_fill(lpriv->dirty_bitmap, imsic->global.nr_ids + 1); + if (!__imsic_local_sync(lpriv)) + __imsic_local_timer_start(lpriv, smp_processor_id()); + raw_spin_unlock_irqrestore(&lpriv->lock, flags); } @@ -190,12 +253,7 @@ void imsic_local_delivery(bool enable) #ifdef CONFIG_SMP static void imsic_local_timer_callback(struct timer_list *timer) { - struct imsic_local_priv *lpriv = this_cpu_ptr(imsic->lpriv); - unsigned long flags; - - raw_spin_lock_irqsave(&lpriv->lock, flags); - __imsic_local_sync(lpriv); - raw_spin_unlock_irqrestore(&lpriv->lock, flags); + imsic_local_sync_all(false); } static void __imsic_remote_sync(struct imsic_local_priv *lpriv, unsigned int cpu) @@ -216,14 +274,11 @@ static void __imsic_remote_sync(struct imsic_local_priv *lpriv, unsigned int cpu */ if (cpu_online(cpu)) { if (cpu == smp_processor_id()) { - __imsic_local_sync(lpriv); - return; + if (__imsic_local_sync(lpriv)) + return; } - if (!timer_pending(&lpriv->timer)) { - lpriv->timer.expires = jiffies + 1; - add_timer_on(&lpriv->timer, cpu); - } + __imsic_local_timer_start(lpriv, cpu); } } #else @@ -278,8 +333,26 @@ void imsic_vector_unmask(struct imsic_vector *vec) raw_spin_unlock(&lpriv->lock); } -static bool imsic_vector_move_update(struct imsic_local_priv *lpriv, struct imsic_vector *vec, - bool new_enable, struct imsic_vector *new_move) +void imsic_vector_force_move_cleanup(struct imsic_vector *vec) +{ + struct imsic_local_priv *lpriv; + struct imsic_vector *mvec; + unsigned long flags; + + lpriv = per_cpu_ptr(imsic->lpriv, vec->cpu); + raw_spin_lock_irqsave(&lpriv->lock, flags); + + mvec = READ_ONCE(vec->move_prev); + WRITE_ONCE(vec->move_prev, NULL); + if (mvec) + imsic_vector_free(mvec); + + raw_spin_unlock_irqrestore(&lpriv->lock, flags); +} + +static bool imsic_vector_move_update(struct imsic_local_priv *lpriv, + struct imsic_vector *vec, bool is_old_vec, + bool new_enable, struct imsic_vector *move_vec) { unsigned long flags; bool enabled; @@ -289,7 +362,10 @@ static bool imsic_vector_move_update(struct imsic_local_priv *lpriv, struct imsi /* Update enable and move details */ enabled = READ_ONCE(vec->enable); WRITE_ONCE(vec->enable, new_enable); - WRITE_ONCE(vec->move, new_move); + if (is_old_vec) + WRITE_ONCE(vec->move_next, move_vec); + else + WRITE_ONCE(vec->move_prev, move_vec); /* Mark the vector as dirty and synchronize */ bitmap_set(lpriv->dirty_bitmap, vec->local_id, 1); @@ -322,8 +398,8 @@ void imsic_vector_move(struct imsic_vector *old_vec, struct imsic_vector *new_ve * interrupt on the old vector while device was being moved * to the new vector. */ - enabled = imsic_vector_move_update(old_lpriv, old_vec, false, new_vec); - imsic_vector_move_update(new_lpriv, new_vec, enabled, new_vec); + enabled = imsic_vector_move_update(old_lpriv, old_vec, true, false, new_vec); + imsic_vector_move_update(new_lpriv, new_vec, false, enabled, old_vec); } #ifdef CONFIG_GENERIC_IRQ_DEBUGFS @@ -368,7 +444,7 @@ struct imsic_vector *imsic_vector_from_local_id(unsigned int cpu, unsigned int l return &lpriv->vectors[local_id]; } -struct imsic_vector *imsic_vector_alloc(unsigned int hwirq, const struct cpumask *mask) +struct imsic_vector *imsic_vector_alloc(unsigned int irq, const struct cpumask *mask) { struct imsic_vector *vec = NULL; struct imsic_local_priv *lpriv; @@ -384,9 +460,10 @@ struct imsic_vector *imsic_vector_alloc(unsigned int hwirq, const struct cpumask lpriv = per_cpu_ptr(imsic->lpriv, cpu); vec = &lpriv->vectors[local_id]; - vec->hwirq = hwirq; + vec->irq = irq; vec->enable = false; - vec->move = NULL; + vec->move_next = NULL; + vec->move_prev = NULL; return vec; } @@ -396,7 +473,7 @@ void imsic_vector_free(struct imsic_vector *vec) unsigned long flags; raw_spin_lock_irqsave(&imsic->matrix_lock, flags); - vec->hwirq = UINT_MAX; + vec->irq = 0; irq_matrix_free(imsic->matrix, vec->cpu, vec->local_id, false); raw_spin_unlock_irqrestore(&imsic->matrix_lock, flags); } @@ -455,7 +532,7 @@ static int __init imsic_local_init(void) vec = &lpriv->vectors[i]; vec->cpu = cpu; vec->local_id = i; - vec->hwirq = UINT_MAX; + vec->irq = 0; } } @@ -487,7 +564,7 @@ void imsic_state_offline(void) struct imsic_local_priv *lpriv = this_cpu_ptr(imsic->lpriv); raw_spin_lock_irqsave(&lpriv->lock, flags); - WARN_ON_ONCE(try_to_del_timer_sync(&lpriv->timer) < 0); + WARN_ON_ONCE(timer_delete_sync_try(&lpriv->timer) < 0); raw_spin_unlock_irqrestore(&lpriv->lock, flags); #endif } diff --git a/drivers/irqchip/irq-riscv-imsic-state.h b/drivers/irqchip/irq-riscv-imsic-state.h index 391e44280827..3202ffa4e849 100644 --- a/drivers/irqchip/irq-riscv-imsic-state.h +++ b/drivers/irqchip/irq-riscv-imsic-state.h @@ -20,10 +20,11 @@ struct imsic_vector { unsigned int cpu; unsigned int local_id; /* Details saved by driver in the vector */ - unsigned int hwirq; + unsigned int irq; /* Details accessed using local lock held */ bool enable; - struct imsic_vector *move; + struct imsic_vector *move_next; + struct imsic_vector *move_prev; }; struct imsic_local_priv { @@ -74,7 +75,7 @@ static inline void __imsic_id_clear_enable(unsigned long id) __imsic_eix_update(id, 1, false, false); } -void imsic_local_sync_all(void); +void imsic_local_sync_all(bool force_all); void imsic_local_delivery(bool enable); void imsic_vector_mask(struct imsic_vector *vec); @@ -87,14 +88,15 @@ static inline bool imsic_vector_isenabled(struct imsic_vector *vec) static inline struct imsic_vector *imsic_vector_get_move(struct imsic_vector *vec) { - return READ_ONCE(vec->move); + return READ_ONCE(vec->move_prev); } +void imsic_vector_force_move_cleanup(struct imsic_vector *vec); void imsic_vector_move(struct imsic_vector *old_vec, struct imsic_vector *new_vec); struct imsic_vector *imsic_vector_from_local_id(unsigned int cpu, unsigned int local_id); -struct imsic_vector *imsic_vector_alloc(unsigned int hwirq, const struct cpumask *mask); +struct imsic_vector *imsic_vector_alloc(unsigned int irq, const struct cpumask *mask); void imsic_vector_free(struct imsic_vector *vector); void imsic_vector_debug_show(struct seq_file *m, struct imsic_vector *vec, int ind); diff --git a/drivers/irqchip/irq-riscv-intc.c b/drivers/irqchip/irq-riscv-intc.c index f653c13de62b..e5805885394e 100644 --- a/drivers/irqchip/irq-riscv-intc.c +++ b/drivers/irqchip/irq-riscv-intc.c @@ -242,7 +242,7 @@ static int __init riscv_intc_init(struct device_node *node, chip = &andes_intc_chip; } - return riscv_intc_init_common(of_node_to_fwnode(node), chip); + return riscv_intc_init_common(of_fwnode_handle(node), chip); } IRQCHIP_DECLARE(riscv, "riscv,cpu-intc", riscv_intc_init); diff --git a/drivers/irqchip/irq-sa11x0.c b/drivers/irqchip/irq-sa11x0.c index 9d0b80271949..d8d4dff16276 100644 --- a/drivers/irqchip/irq-sa11x0.c +++ b/drivers/irqchip/irq-sa11x0.c @@ -162,7 +162,7 @@ void __init sa11x0_init_irq_nodt(int irq_start, resource_size_t io_start) */ writel_relaxed(1, iobase + ICCR); - sa1100_normal_irqdomain = irq_domain_add_simple(NULL, + sa1100_normal_irqdomain = irq_domain_create_simple(NULL, 32, irq_start, &sa1100_normal_irqdomain_ops, NULL); diff --git a/drivers/irqchip/irq-sg2042-msi.c b/drivers/irqchip/irq-sg2042-msi.c new file mode 100644 index 000000000000..af16bc5a3c8b --- /dev/null +++ b/drivers/irqchip/irq-sg2042-msi.c @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SG2042 MSI Controller + * + * Copyright (C) 2024 Sophgo Technology Inc. + * Copyright (C) 2024 Chen Wang <unicorn_wang@outlook.com> + */ + +#include <linux/cleanup.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/msi.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/slab.h> + +#include <linux/irqchip/irq-msi-lib.h> + +struct sg204x_msi_chip_info { + const struct irq_chip *irqchip; + const struct msi_parent_ops *parent_ops; +}; + +/** + * struct sg204x_msi_chipdata - chip data for the SG204x MSI IRQ controller + * @reg_clr: clear reg, see TRM, 10.1.33, GP_INTR0_CLR + * @doorbell_addr: see TRM, 10.1.32, GP_INTR0_SET + * @irq_first: First vectors number that MSIs starts + * @num_irqs: Number of vectors for MSIs + * @msi_map: mapping for allocated MSI vectors. + * @msi_map_lock: Lock for msi_map + * @chip_info: chip specific infomations + */ +struct sg204x_msi_chipdata { + void __iomem *reg_clr; + + phys_addr_t doorbell_addr; + + u32 irq_first; + u32 num_irqs; + + unsigned long *msi_map; + struct mutex msi_map_lock; + + const struct sg204x_msi_chip_info *chip_info; +}; + +static int sg204x_msi_allocate_hwirq(struct sg204x_msi_chipdata *data, int num_req) +{ + int first; + + guard(mutex)(&data->msi_map_lock); + first = bitmap_find_free_region(data->msi_map, data->num_irqs, + get_count_order(num_req)); + return first >= 0 ? first : -ENOSPC; +} + +static void sg204x_msi_free_hwirq(struct sg204x_msi_chipdata *data, int hwirq, int num_req) +{ + guard(mutex)(&data->msi_map_lock); + bitmap_release_region(data->msi_map, hwirq, get_count_order(num_req)); +} + +static void sg2042_msi_irq_ack(struct irq_data *d) +{ + struct sg204x_msi_chipdata *data = irq_data_get_irq_chip_data(d); + int bit_off = d->hwirq; + + writel(1 << bit_off, data->reg_clr); + + irq_chip_ack_parent(d); +} + +static void sg2042_msi_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg) +{ + struct sg204x_msi_chipdata *data = irq_data_get_irq_chip_data(d); + + msg->address_hi = upper_32_bits(data->doorbell_addr); + msg->address_lo = lower_32_bits(data->doorbell_addr); + msg->data = 1 << d->hwirq; +} + +static const struct irq_chip sg2042_msi_middle_irq_chip = { + .name = "SG2042 MSI", + .irq_ack = sg2042_msi_irq_ack, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, +#ifdef CONFIG_SMP + .irq_set_affinity = irq_chip_set_affinity_parent, +#endif + .irq_compose_msi_msg = sg2042_msi_irq_compose_msi_msg, +}; + +static void sg2044_msi_irq_ack(struct irq_data *d) +{ + struct sg204x_msi_chipdata *data = irq_data_get_irq_chip_data(d); + + writel(0, (u32 __iomem *)data->reg_clr + d->hwirq); + irq_chip_ack_parent(d); +} + +static void sg2044_msi_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg) +{ + struct sg204x_msi_chipdata *data = irq_data_get_irq_chip_data(d); + phys_addr_t doorbell = data->doorbell_addr + 4 * (d->hwirq / 32); + + msg->address_lo = lower_32_bits(doorbell); + msg->address_hi = upper_32_bits(doorbell); + msg->data = d->hwirq % 32; +} + +static struct irq_chip sg2044_msi_middle_irq_chip = { + .name = "SG2044 MSI", + .irq_ack = sg2044_msi_irq_ack, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, +#ifdef CONFIG_SMP + .irq_set_affinity = irq_chip_set_affinity_parent, +#endif + .irq_compose_msi_msg = sg2044_msi_irq_compose_msi_msg, +}; + +static int sg204x_msi_parent_domain_alloc(struct irq_domain *domain, unsigned int virq, int hwirq) +{ + struct sg204x_msi_chipdata *data = domain->host_data; + struct irq_fwspec fwspec; + struct irq_data *d; + int ret; + + fwspec.fwnode = domain->parent->fwnode; + fwspec.param_count = 2; + fwspec.param[0] = data->irq_first + hwirq; + fwspec.param[1] = IRQ_TYPE_EDGE_RISING; + + ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec); + if (ret) + return ret; + + d = irq_domain_get_irq_data(domain->parent, virq); + return d->chip->irq_set_type(d, IRQ_TYPE_EDGE_RISING); +} + +static int sg204x_msi_middle_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *args) +{ + struct sg204x_msi_chipdata *data = domain->host_data; + int hwirq, err, i; + + hwirq = sg204x_msi_allocate_hwirq(data, nr_irqs); + if (hwirq < 0) + return hwirq; + + for (i = 0; i < nr_irqs; i++) { + err = sg204x_msi_parent_domain_alloc(domain, virq + i, hwirq + i); + if (err) + goto err_hwirq; + + irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, + data->chip_info->irqchip, data); + } + return 0; + +err_hwirq: + sg204x_msi_free_hwirq(data, hwirq, nr_irqs); + irq_domain_free_irqs_parent(domain, virq, i); + return err; +} + +static void sg204x_msi_middle_domain_free(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs) +{ + struct irq_data *d = irq_domain_get_irq_data(domain, virq); + struct sg204x_msi_chipdata *data = irq_data_get_irq_chip_data(d); + + irq_domain_free_irqs_parent(domain, virq, nr_irqs); + sg204x_msi_free_hwirq(data, d->hwirq, nr_irqs); +} + +static const struct irq_domain_ops sg204x_msi_middle_domain_ops = { + .alloc = sg204x_msi_middle_domain_alloc, + .free = sg204x_msi_middle_domain_free, + .select = msi_lib_irq_domain_select, +}; + +#define SG2042_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ + MSI_FLAG_USE_DEF_CHIP_OPS) + +#define SG2042_MSI_FLAGS_SUPPORTED MSI_GENERIC_FLAGS_MASK + +static const struct msi_parent_ops sg2042_msi_parent_ops = { + .required_flags = SG2042_MSI_FLAGS_REQUIRED, + .supported_flags = SG2042_MSI_FLAGS_SUPPORTED, + .chip_flags = MSI_CHIP_FLAG_SET_ACK, + .bus_select_mask = MATCH_PCI_MSI, + .bus_select_token = DOMAIN_BUS_NEXUS, + .prefix = "SG2042-", + .init_dev_msi_info = msi_lib_init_dev_msi_info, +}; + +#define SG2044_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ + MSI_FLAG_USE_DEF_CHIP_OPS) + +#define SG2044_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \ + MSI_FLAG_PCI_MSIX) + +static const struct msi_parent_ops sg2044_msi_parent_ops = { + .required_flags = SG2044_MSI_FLAGS_REQUIRED, + .supported_flags = SG2044_MSI_FLAGS_SUPPORTED, + .chip_flags = MSI_CHIP_FLAG_SET_EOI | MSI_CHIP_FLAG_SET_ACK, + .bus_select_mask = MATCH_PCI_MSI, + .bus_select_token = DOMAIN_BUS_NEXUS, + .prefix = "SG2044-", + .init_dev_msi_info = msi_lib_init_dev_msi_info, +}; + +static int sg204x_msi_init_domains(struct sg204x_msi_chipdata *data, + struct irq_domain *plic_domain, struct device *dev) +{ + struct fwnode_handle *fwnode = dev_fwnode(dev); + struct irq_domain *middle_domain; + + middle_domain = irq_domain_create_hierarchy(plic_domain, 0, data->num_irqs, fwnode, + &sg204x_msi_middle_domain_ops, data); + if (!middle_domain) { + pr_err("Failed to create the MSI middle domain\n"); + return -ENOMEM; + } + + irq_domain_update_bus_token(middle_domain, DOMAIN_BUS_NEXUS); + + middle_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT; + middle_domain->msi_parent_ops = data->chip_info->parent_ops; + return 0; +} + +static int sg2042_msi_probe(struct platform_device *pdev) +{ + struct fwnode_reference_args args = { }; + struct sg204x_msi_chipdata *data; + struct device *dev = &pdev->dev; + struct irq_domain *plic_domain; + struct resource *res; + int ret; + + data = devm_kzalloc(dev, sizeof(struct sg204x_msi_chipdata), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->chip_info = device_get_match_data(&pdev->dev); + if (!data->chip_info) { + dev_err(&pdev->dev, "Failed to get irqchip\n"); + return -EINVAL; + } + + data->reg_clr = devm_platform_ioremap_resource_byname(pdev, "clr"); + if (IS_ERR(data->reg_clr)) { + dev_err(dev, "Failed to map clear register\n"); + return PTR_ERR(data->reg_clr); + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "doorbell"); + if (!res) { + dev_err(dev, "Failed get resource from set\n"); + return -EINVAL; + } + data->doorbell_addr = res->start; + + ret = fwnode_property_get_reference_args(dev_fwnode(dev), "msi-ranges", + "#interrupt-cells", 0, 0, &args); + if (ret) { + dev_err(dev, "Unable to parse MSI vec base\n"); + return ret; + } + fwnode_handle_put(args.fwnode); + + ret = fwnode_property_get_reference_args(dev_fwnode(dev), "msi-ranges", NULL, + args.nargs + 1, 0, &args); + if (ret) { + dev_err(dev, "Unable to parse MSI vec number\n"); + return ret; + } + + plic_domain = irq_find_matching_fwnode(args.fwnode, DOMAIN_BUS_ANY); + fwnode_handle_put(args.fwnode); + if (!plic_domain) { + pr_err("Failed to find the PLIC domain\n"); + return -ENXIO; + } + + data->irq_first = (u32)args.args[0]; + data->num_irqs = (u32)args.args[args.nargs - 1]; + + mutex_init(&data->msi_map_lock); + + data->msi_map = devm_bitmap_zalloc(&pdev->dev, data->num_irqs, GFP_KERNEL); + if (!data->msi_map) { + dev_err(&pdev->dev, "Unable to allocate msi mapping\n"); + return -ENOMEM; + } + + return sg204x_msi_init_domains(data, plic_domain, dev); +} + +static const struct sg204x_msi_chip_info sg2042_chip_info = { + .irqchip = &sg2042_msi_middle_irq_chip, + .parent_ops = &sg2042_msi_parent_ops, +}; + +static const struct sg204x_msi_chip_info sg2044_chip_info = { + .irqchip = &sg2044_msi_middle_irq_chip, + .parent_ops = &sg2044_msi_parent_ops, +}; + +static const struct of_device_id sg2042_msi_of_match[] = { + { .compatible = "sophgo,sg2042-msi", .data = &sg2042_chip_info }, + { .compatible = "sophgo,sg2044-msi", .data = &sg2044_chip_info }, + { } +}; + +static struct platform_driver sg2042_msi_driver = { + .driver = { + .name = "sg2042-msi", + .of_match_table = sg2042_msi_of_match, + }, + .probe = sg2042_msi_probe, +}; +builtin_platform_driver(sg2042_msi_driver); diff --git a/drivers/irqchip/irq-sni-exiu.c b/drivers/irqchip/irq-sni-exiu.c index c7db617e1a2f..0cad68aa8388 100644 --- a/drivers/irqchip/irq-sni-exiu.c +++ b/drivers/irqchip/irq-sni-exiu.c @@ -249,12 +249,12 @@ static int __init exiu_dt_init(struct device_node *node, return -ENXIO; } - data = exiu_init(of_node_to_fwnode(node), &res); + data = exiu_init(of_fwnode_handle(node), &res); if (IS_ERR(data)) return PTR_ERR(data); - domain = irq_domain_add_hierarchy(parent_domain, 0, NUM_IRQS, node, - &exiu_domain_ops, data); + domain = irq_domain_create_hierarchy(parent_domain, 0, NUM_IRQS, of_fwnode_handle(node), + &exiu_domain_ops, data); if (!domain) { pr_err("%pOF: failed to allocate domain\n", node); goto out_unmap; diff --git a/drivers/irqchip/irq-sp7021-intc.c b/drivers/irqchip/irq-sp7021-intc.c index bed78d1def3d..2a6eda9ab62e 100644 --- a/drivers/irqchip/irq-sp7021-intc.c +++ b/drivers/irqchip/irq-sp7021-intc.c @@ -256,8 +256,8 @@ static int __init sp_intc_init_dt(struct device_node *node, struct device_node * writel_relaxed(~0, REG_INTR_CLEAR + i * 4); } - sp_intc.domain = irq_domain_add_linear(node, SP_INTC_NR_IRQS, - &sp_intc_dm_ops, &sp_intc); + sp_intc.domain = irq_domain_create_linear(of_fwnode_handle(node), SP_INTC_NR_IRQS, + &sp_intc_dm_ops, &sp_intc); if (!sp_intc.domain) { ret = -ENOMEM; goto out_unmap1; diff --git a/drivers/irqchip/irq-starfive-jh8100-intc.c b/drivers/irqchip/irq-starfive-jh8100-intc.c index 0f5837176e53..2460798ec158 100644 --- a/drivers/irqchip/irq-starfive-jh8100-intc.c +++ b/drivers/irqchip/irq-starfive-jh8100-intc.c @@ -158,8 +158,8 @@ static int __init starfive_intc_init(struct device_node *intc, raw_spin_lock_init(&irqc->lock); - irqc->domain = irq_domain_add_linear(intc, STARFIVE_INTC_SRC_IRQ_NUM, - &starfive_intc_domain_ops, irqc); + irqc->domain = irq_domain_create_linear(of_fwnode_handle(intc), STARFIVE_INTC_SRC_IRQ_NUM, + &starfive_intc_domain_ops, irqc); if (!irqc->domain) { pr_err("Unable to create IRQ domain\n"); ret = -EINVAL; diff --git a/drivers/irqchip/irq-stm32-exti.c b/drivers/irqchip/irq-stm32-exti.c index 7c6a0080c330..978811f2abe8 100644 --- a/drivers/irqchip/irq-stm32-exti.c +++ b/drivers/irqchip/irq-stm32-exti.c @@ -169,22 +169,18 @@ static int stm32_irq_set_type(struct irq_data *d, unsigned int type) u32 rtsr, ftsr; int err; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); rtsr = irq_reg_readl(gc, stm32_bank->rtsr_ofst); ftsr = irq_reg_readl(gc, stm32_bank->ftsr_ofst); err = stm32_exti_set_type(d, type, &rtsr, &ftsr); if (err) - goto unlock; + return err; irq_reg_writel(gc, rtsr, stm32_bank->rtsr_ofst); irq_reg_writel(gc, ftsr, stm32_bank->ftsr_ofst); - -unlock: - irq_gc_unlock(gc); - - return err; + return 0; } static void stm32_chip_suspend(struct stm32_exti_chip_data *chip_data, @@ -217,18 +213,16 @@ static void stm32_irq_suspend(struct irq_chip_generic *gc) { struct stm32_exti_chip_data *chip_data = gc->private; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); stm32_chip_suspend(chip_data, gc->wake_active); - irq_gc_unlock(gc); } static void stm32_irq_resume(struct irq_chip_generic *gc) { struct stm32_exti_chip_data *chip_data = gc->private; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); stm32_chip_resume(chip_data, gc->mask_cache); - irq_gc_unlock(gc); } static int stm32_exti_alloc(struct irq_domain *d, unsigned int virq, @@ -265,11 +259,8 @@ static void stm32_irq_ack(struct irq_data *d) struct stm32_exti_chip_data *chip_data = gc->private; const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; - irq_gc_lock(gc); - + guard(raw_spinlock)(&gc->lock); irq_reg_writel(gc, d->mask, stm32_bank->rpr_ofst); - - irq_gc_unlock(gc); } static struct @@ -344,8 +335,8 @@ static int __init stm32_exti_init(const struct stm32_exti_drv_data *drv_data, if (!host_data) return -ENOMEM; - domain = irq_domain_add_linear(node, drv_data->bank_nr * IRQS_PER_BANK, - &irq_exti_domain_ops, NULL); + domain = irq_domain_create_linear(of_fwnode_handle(node), drv_data->bank_nr * IRQS_PER_BANK, + &irq_exti_domain_ops, NULL); if (!domain) { pr_err("%pOFn: Could not register interrupt domain.\n", node); diff --git a/drivers/irqchip/irq-stm32mp-exti.c b/drivers/irqchip/irq-stm32mp-exti.c index cb83d6cc6113..c6b4407d05f9 100644 --- a/drivers/irqchip/irq-stm32mp-exti.c +++ b/drivers/irqchip/irq-stm32mp-exti.c @@ -531,7 +531,7 @@ static int stm32mp_exti_domain_alloc(struct irq_domain *dm, if (ret) return ret; /* we only support one parent, so far */ - if (of_node_to_fwnode(out_irq.np) != dm->parent->fwnode) + if (of_fwnode_handle(out_irq.np) != dm->parent->fwnode) return -EINVAL; of_phandle_args_to_fwspec(out_irq.np, out_irq.args, @@ -682,10 +682,9 @@ static int stm32mp_exti_probe(struct platform_device *pdev) return -EINVAL; } - domain = irq_domain_add_hierarchy(parent_domain, 0, - drv_data->bank_nr * IRQS_PER_BANK, - np, &stm32mp_exti_domain_ops, - host_data); + domain = irq_domain_create_hierarchy(parent_domain, 0, drv_data->bank_nr * IRQS_PER_BANK, + of_fwnode_handle(np), &stm32mp_exti_domain_ops, + host_data); if (!domain) { dev_err(dev, "Could not register exti domain\n"); diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c index dd506ebfdacb..9c2c9caeca2a 100644 --- a/drivers/irqchip/irq-sun4i.c +++ b/drivers/irqchip/irq-sun4i.c @@ -133,7 +133,7 @@ static int __init sun4i_of_init(struct device_node *node, /* Configure the external interrupt source type */ writel(0x00, irq_ic_data->irq_base + SUN4I_IRQ_NMI_CTRL_REG); - irq_ic_data->irq_domain = irq_domain_add_linear(node, 3 * 32, + irq_ic_data->irq_domain = irq_domain_create_linear(of_fwnode_handle(node), 3 * 32, &sun4i_irq_ops, NULL); if (!irq_ic_data->irq_domain) panic("%pOF: unable to create IRQ domain\n", node); diff --git a/drivers/irqchip/irq-sun6i-r.c b/drivers/irqchip/irq-sun6i-r.c index 99958d470d62..37d4b29763bc 100644 --- a/drivers/irqchip/irq-sun6i-r.c +++ b/drivers/irqchip/irq-sun6i-r.c @@ -338,8 +338,8 @@ static int __init sun6i_r_intc_init(struct device_node *node, return PTR_ERR(base); } - domain = irq_domain_add_hierarchy(parent_domain, 0, 0, node, - &sun6i_r_intc_domain_ops, NULL); + domain = irq_domain_create_hierarchy(parent_domain, 0, 0, of_fwnode_handle(node), + &sun6i_r_intc_domain_ops, NULL); if (!domain) { pr_err("%pOF: Failed to allocate domain\n", node); iounmap(base); diff --git a/drivers/irqchip/irq-sunxi-nmi.c b/drivers/irqchip/irq-sunxi-nmi.c index 0b4312152024..fe32dfdc2dd0 100644 --- a/drivers/irqchip/irq-sunxi-nmi.c +++ b/drivers/irqchip/irq-sunxi-nmi.c @@ -48,32 +48,41 @@ enum { SUNXI_SRC_TYPE_EDGE_RISING, }; -struct sunxi_sc_nmi_reg_offs { - u32 ctrl; - u32 pend; - u32 enable; +struct sunxi_sc_nmi_data { + struct { + u32 ctrl; + u32 pend; + u32 enable; + } reg_offs; + u32 enable_val; }; -static const struct sunxi_sc_nmi_reg_offs sun6i_reg_offs __initconst = { - .ctrl = SUN6I_NMI_CTRL, - .pend = SUN6I_NMI_PENDING, - .enable = SUN6I_NMI_ENABLE, +static const struct sunxi_sc_nmi_data sun6i_data __initconst = { + .reg_offs.ctrl = SUN6I_NMI_CTRL, + .reg_offs.pend = SUN6I_NMI_PENDING, + .reg_offs.enable = SUN6I_NMI_ENABLE, }; -static const struct sunxi_sc_nmi_reg_offs sun7i_reg_offs __initconst = { - .ctrl = SUN7I_NMI_CTRL, - .pend = SUN7I_NMI_PENDING, - .enable = SUN7I_NMI_ENABLE, +static const struct sunxi_sc_nmi_data sun7i_data __initconst = { + .reg_offs.ctrl = SUN7I_NMI_CTRL, + .reg_offs.pend = SUN7I_NMI_PENDING, + .reg_offs.enable = SUN7I_NMI_ENABLE, }; -static const struct sunxi_sc_nmi_reg_offs sun9i_reg_offs __initconst = { - .ctrl = SUN9I_NMI_CTRL, - .pend = SUN9I_NMI_PENDING, - .enable = SUN9I_NMI_ENABLE, +static const struct sunxi_sc_nmi_data sun9i_data __initconst = { + .reg_offs.ctrl = SUN9I_NMI_CTRL, + .reg_offs.pend = SUN9I_NMI_PENDING, + .reg_offs.enable = SUN9I_NMI_ENABLE, }; -static inline void sunxi_sc_nmi_write(struct irq_chip_generic *gc, u32 off, - u32 val) +static const struct sunxi_sc_nmi_data sun55i_a523_data __initconst = { + .reg_offs.ctrl = SUN9I_NMI_CTRL, + .reg_offs.pend = SUN9I_NMI_PENDING, + .reg_offs.enable = SUN9I_NMI_ENABLE, + .enable_val = BIT(31), +}; + +static inline void sunxi_sc_nmi_write(struct irq_chip_generic *gc, u32 off, u32 val) { irq_reg_writel(gc, val, off); } @@ -102,7 +111,7 @@ static int sunxi_sc_nmi_set_type(struct irq_data *data, unsigned int flow_type) unsigned int src_type; unsigned int i; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); switch (flow_type & IRQF_TRIGGER_MASK) { case IRQ_TYPE_EDGE_FALLING: @@ -119,9 +128,7 @@ static int sunxi_sc_nmi_set_type(struct irq_data *data, unsigned int flow_type) src_type = SUNXI_SRC_TYPE_LEVEL_LOW; break; default: - irq_gc_unlock(gc); - pr_err("Cannot assign multiple trigger modes to IRQ %d.\n", - data->irq); + pr_err("Cannot assign multiple trigger modes to IRQ %d.\n", data->irq); return -EBADR; } @@ -136,23 +143,18 @@ static int sunxi_sc_nmi_set_type(struct irq_data *data, unsigned int flow_type) src_type_reg &= ~SUNXI_NMI_SRC_TYPE_MASK; src_type_reg |= src_type; sunxi_sc_nmi_write(gc, ctrl_off, src_type_reg); - - irq_gc_unlock(gc); - return IRQ_SET_MASK_OK; } static int __init sunxi_sc_nmi_irq_init(struct device_node *node, - const struct sunxi_sc_nmi_reg_offs *reg_offs) + const struct sunxi_sc_nmi_data *data) { - struct irq_domain *domain; + unsigned int irq, clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; struct irq_chip_generic *gc; - unsigned int irq; - unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; + struct irq_domain *domain; int ret; - - domain = irq_domain_add_linear(node, 1, &irq_generic_chip_ops, NULL); + domain = irq_domain_create_linear(of_fwnode_handle(node), 1, &irq_generic_chip_ops, NULL); if (!domain) { pr_err("Could not register interrupt domain.\n"); return -ENOMEM; @@ -186,27 +188,28 @@ static int __init sunxi_sc_nmi_irq_init(struct device_node *node, gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit; gc->chip_types[0].chip.irq_eoi = irq_gc_ack_set_bit; gc->chip_types[0].chip.irq_set_type = sunxi_sc_nmi_set_type; - gc->chip_types[0].chip.flags = IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED | + gc->chip_types[0].chip.flags = IRQCHIP_EOI_THREADED | + IRQCHIP_EOI_IF_HANDLED | IRQCHIP_SKIP_SET_WAKE; - gc->chip_types[0].regs.ack = reg_offs->pend; - gc->chip_types[0].regs.mask = reg_offs->enable; - gc->chip_types[0].regs.type = reg_offs->ctrl; + gc->chip_types[0].regs.ack = data->reg_offs.pend; + gc->chip_types[0].regs.mask = data->reg_offs.enable; + gc->chip_types[0].regs.type = data->reg_offs.ctrl; gc->chip_types[1].type = IRQ_TYPE_EDGE_BOTH; gc->chip_types[1].chip.irq_ack = irq_gc_ack_set_bit; gc->chip_types[1].chip.irq_mask = irq_gc_mask_clr_bit; gc->chip_types[1].chip.irq_unmask = irq_gc_mask_set_bit; gc->chip_types[1].chip.irq_set_type = sunxi_sc_nmi_set_type; - gc->chip_types[1].regs.ack = reg_offs->pend; - gc->chip_types[1].regs.mask = reg_offs->enable; - gc->chip_types[1].regs.type = reg_offs->ctrl; + gc->chip_types[1].regs.ack = data->reg_offs.pend; + gc->chip_types[1].regs.mask = data->reg_offs.enable; + gc->chip_types[1].regs.type = data->reg_offs.ctrl; gc->chip_types[1].handler = handle_edge_irq; /* Disable any active interrupts */ - sunxi_sc_nmi_write(gc, reg_offs->enable, 0); + sunxi_sc_nmi_write(gc, data->reg_offs.enable, data->enable_val); /* Clear any pending NMI interrupts */ - sunxi_sc_nmi_write(gc, reg_offs->pend, SUNXI_NMI_IRQ_BIT); + sunxi_sc_nmi_write(gc, data->reg_offs.pend, SUNXI_NMI_IRQ_BIT); irq_set_chained_handler_and_data(irq, sunxi_sc_nmi_handle_irq, domain); @@ -221,20 +224,27 @@ fail_irqd_remove: static int __init sun6i_sc_nmi_irq_init(struct device_node *node, struct device_node *parent) { - return sunxi_sc_nmi_irq_init(node, &sun6i_reg_offs); + return sunxi_sc_nmi_irq_init(node, &sun6i_data); } IRQCHIP_DECLARE(sun6i_sc_nmi, "allwinner,sun6i-a31-sc-nmi", sun6i_sc_nmi_irq_init); static int __init sun7i_sc_nmi_irq_init(struct device_node *node, struct device_node *parent) { - return sunxi_sc_nmi_irq_init(node, &sun7i_reg_offs); + return sunxi_sc_nmi_irq_init(node, &sun7i_data); } IRQCHIP_DECLARE(sun7i_sc_nmi, "allwinner,sun7i-a20-sc-nmi", sun7i_sc_nmi_irq_init); static int __init sun9i_nmi_irq_init(struct device_node *node, struct device_node *parent) { - return sunxi_sc_nmi_irq_init(node, &sun9i_reg_offs); + return sunxi_sc_nmi_irq_init(node, &sun9i_data); } IRQCHIP_DECLARE(sun9i_nmi, "allwinner,sun9i-a80-nmi", sun9i_nmi_irq_init); + +static int __init sun55i_nmi_irq_init(struct device_node *node, + struct device_node *parent) +{ + return sunxi_sc_nmi_irq_init(node, &sun55i_a523_data); +} +IRQCHIP_DECLARE(sun55i_nmi, "allwinner,sun55i-a523-nmi", sun55i_nmi_irq_init); diff --git a/drivers/irqchip/irq-tb10x.c b/drivers/irqchip/irq-tb10x.c index d59bfbe8c6d0..94cbc5111d7e 100644 --- a/drivers/irqchip/irq-tb10x.c +++ b/drivers/irqchip/irq-tb10x.c @@ -41,11 +41,9 @@ static inline u32 ab_irqctl_readreg(struct irq_chip_generic *gc, u32 reg) static int tb10x_irq_set_type(struct irq_data *data, unsigned int flow_type) { struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); - uint32_t im, mod, pol; + uint32_t mod, pol, im = data->mask; - im = data->mask; - - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); mod = ab_irqctl_readreg(gc, AB_IRQCTL_SRC_MODE) | im; pol = ab_irqctl_readreg(gc, AB_IRQCTL_SRC_POLARITY) | im; @@ -67,9 +65,7 @@ static int tb10x_irq_set_type(struct irq_data *data, unsigned int flow_type) case IRQ_TYPE_EDGE_RISING: break; default: - irq_gc_unlock(gc); - pr_err("%s: Cannot assign multiple trigger modes to IRQ %d.\n", - __func__, data->irq); + pr_err("%s: Cannot assign multiple trigger modes to IRQ %d.\n", __func__, data->irq); return -EBADR; } @@ -79,9 +75,6 @@ static int tb10x_irq_set_type(struct irq_data *data, unsigned int flow_type) ab_irqctl_writereg(gc, AB_IRQCTL_SRC_MODE, mod); ab_irqctl_writereg(gc, AB_IRQCTL_SRC_POLARITY, pol); ab_irqctl_writereg(gc, AB_IRQCTL_INT_STATUS, im); - - irq_gc_unlock(gc); - return IRQ_SET_MASK_OK; } @@ -121,13 +114,13 @@ static int __init of_tb10x_init_irq(struct device_node *ictl, goto ioremap_fail; } - domain = irq_domain_add_linear(ictl, AB_IRQCTL_MAXIRQ, - &irq_generic_chip_ops, NULL); + domain = irq_domain_create_linear(of_fwnode_handle(ictl), AB_IRQCTL_MAXIRQ, + &irq_generic_chip_ops, NULL); if (!domain) { ret = -ENOMEM; pr_err("%pOFn: Could not register interrupt domain.\n", ictl); - goto irq_domain_add_fail; + goto irq_domain_create_fail; } ret = irq_alloc_domain_generic_chips(domain, AB_IRQCTL_MAXIRQ, @@ -174,7 +167,7 @@ static int __init of_tb10x_init_irq(struct device_node *ictl, gc_alloc_fail: irq_domain_remove(domain); -irq_domain_add_fail: +irq_domain_create_fail: iounmap(reg_base); ioremap_fail: release_mem_region(mem.start, resource_size(&mem)); diff --git a/drivers/irqchip/irq-tegra.c b/drivers/irqchip/irq-tegra.c index ad3e2c1b3c87..66cbb9f77ff3 100644 --- a/drivers/irqchip/irq-tegra.c +++ b/drivers/irqchip/irq-tegra.c @@ -330,9 +330,8 @@ static int __init tegra_ictlr_init(struct device_node *node, node, num_ictlrs, soc->num_ictlrs); - domain = irq_domain_add_hierarchy(parent_domain, 0, num_ictlrs * 32, - node, &tegra_ictlr_domain_ops, - lic); + domain = irq_domain_create_hierarchy(parent_domain, 0, num_ictlrs * 32, + of_fwnode_handle(node), &tegra_ictlr_domain_ops, lic); if (!domain) { pr_err("%pOF: failed to allocated domain\n", node); err = -ENOMEM; diff --git a/drivers/irqchip/irq-thead-c900-aclint-sswi.c b/drivers/irqchip/irq-thead-c900-aclint-sswi.c index b0e366ade427..8ff6e7a1363b 100644 --- a/drivers/irqchip/irq-thead-c900-aclint-sswi.c +++ b/drivers/irqchip/irq-thead-c900-aclint-sswi.c @@ -31,7 +31,7 @@ static DEFINE_PER_CPU(void __iomem *, sswi_cpu_regs); static void thead_aclint_sswi_ipi_send(unsigned int cpu) { - writel_relaxed(0x1, per_cpu(sswi_cpu_regs, cpu)); + writel(0x1, per_cpu(sswi_cpu_regs, cpu)); } static void thead_aclint_sswi_ipi_clear(void) diff --git a/drivers/irqchip/irq-ti-sci-inta.c b/drivers/irqchip/irq-ti-sci-inta.c index b83f5cbab123..7de59238e6b0 100644 --- a/drivers/irqchip/irq-ti-sci-inta.c +++ b/drivers/irqchip/irq-ti-sci-inta.c @@ -233,7 +233,7 @@ static struct ti_sci_inta_vint_desc *ti_sci_inta_alloc_parent_irq(struct irq_dom INIT_LIST_HEAD(&vint_desc->list); parent_node = of_irq_find_parent(dev_of_node(&inta->pdev->dev)); - parent_fwspec.fwnode = of_node_to_fwnode(parent_node); + parent_fwspec.fwnode = of_fwnode_handle(parent_node); if (of_device_is_compatible(parent_node, "arm,gic-v3")) { /* Parent is GIC */ @@ -701,15 +701,15 @@ static int ti_sci_inta_irq_domain_probe(struct platform_device *pdev) if (ret) return ret; - domain = irq_domain_add_linear(dev_of_node(dev), - ti_sci_get_num_resources(inta->vint), - &ti_sci_inta_irq_domain_ops, inta); + domain = irq_domain_create_linear(of_fwnode_handle(dev_of_node(dev)), + ti_sci_get_num_resources(inta->vint), + &ti_sci_inta_irq_domain_ops, inta); if (!domain) { dev_err(dev, "Failed to allocate IRQ domain\n"); return -ENOMEM; } - msi_domain = ti_sci_inta_msi_create_irq_domain(of_node_to_fwnode(node), + msi_domain = ti_sci_inta_msi_create_irq_domain(of_fwnode_handle(node), &ti_sci_inta_msi_domain_info, domain); if (!msi_domain) { @@ -743,3 +743,4 @@ module_platform_driver(ti_sci_inta_irq_domain_driver); MODULE_AUTHOR("Lokesh Vutla <lokeshvutla@ti.com>"); MODULE_DESCRIPTION("K3 Interrupt Aggregator driver over TI SCI protocol"); +MODULE_LICENSE("GPL"); diff --git a/drivers/irqchip/irq-ti-sci-intr.c b/drivers/irqchip/irq-ti-sci-intr.c index c027cd9e4a69..07fff5ae5ce0 100644 --- a/drivers/irqchip/irq-ti-sci-intr.c +++ b/drivers/irqchip/irq-ti-sci-intr.c @@ -149,7 +149,7 @@ static int ti_sci_intr_alloc_parent_irq(struct irq_domain *domain, goto err_irqs; parent_node = of_irq_find_parent(dev_of_node(intr->dev)); - fwspec.fwnode = of_node_to_fwnode(parent_node); + fwspec.fwnode = of_fwnode_handle(parent_node); if (of_device_is_compatible(parent_node, "arm,gic-v3")) { /* Parent is GIC */ @@ -274,8 +274,9 @@ static int ti_sci_intr_irq_domain_probe(struct platform_device *pdev) return PTR_ERR(intr->out_irqs); } - domain = irq_domain_add_hierarchy(parent_domain, 0, 0, dev_of_node(dev), - &ti_sci_intr_irq_domain_ops, intr); + domain = irq_domain_create_hierarchy(parent_domain, 0, 0, + of_fwnode_handle(dev_of_node(dev)), + &ti_sci_intr_irq_domain_ops, intr); if (!domain) { dev_err(dev, "Failed to allocate IRQ domain\n"); return -ENOMEM; @@ -303,3 +304,4 @@ module_platform_driver(ti_sci_intr_irq_domain_driver); MODULE_AUTHOR("Lokesh Vutla <lokeshvutla@ticom>"); MODULE_DESCRIPTION("K3 Interrupt Router driver over TI SCI protocol"); +MODULE_LICENSE("GPL"); diff --git a/drivers/irqchip/irq-ts4800.c b/drivers/irqchip/irq-ts4800.c index cc219f28d317..e625f4fb2bb8 100644 --- a/drivers/irqchip/irq-ts4800.c +++ b/drivers/irqchip/irq-ts4800.c @@ -52,7 +52,7 @@ static void ts4800_irq_print_chip(struct irq_data *d, struct seq_file *p) { struct ts4800_irq_data *data = irq_data_get_irq_chip_data(d); - seq_printf(p, "%s", dev_name(&data->pdev->dev)); + seq_puts(p, dev_name(&data->pdev->dev)); } static const struct irq_chip ts4800_chip = { @@ -125,7 +125,7 @@ static int ts4800_ic_probe(struct platform_device *pdev) return -EINVAL; } - data->domain = irq_domain_add_linear(node, 8, &ts4800_ic_ops, data); + data->domain = irq_domain_create_linear(of_fwnode_handle(node), 8, &ts4800_ic_ops, data); if (!data->domain) { dev_err(&pdev->dev, "cannot add IRQ domain\n"); return -ENOMEM; diff --git a/drivers/irqchip/irq-uniphier-aidet.c b/drivers/irqchip/irq-uniphier-aidet.c index 601f9343d5b3..6005c2d28dd9 100644 --- a/drivers/irqchip/irq-uniphier-aidet.c +++ b/drivers/irqchip/irq-uniphier-aidet.c @@ -188,7 +188,7 @@ static int uniphier_aidet_probe(struct platform_device *pdev) priv->domain = irq_domain_create_hierarchy( parent_domain, 0, UNIPHIER_AIDET_NR_IRQS, - of_node_to_fwnode(dev->of_node), + of_fwnode_handle(dev->of_node), &uniphier_aidet_domain_ops, priv); if (!priv->domain) return -ENOMEM; diff --git a/drivers/irqchip/irq-versatile-fpga.c b/drivers/irqchip/irq-versatile-fpga.c index 0abc8934c2ee..034ce6afe170 100644 --- a/drivers/irqchip/irq-versatile-fpga.c +++ b/drivers/irqchip/irq-versatile-fpga.c @@ -176,8 +176,8 @@ static void __init fpga_irq_init(void __iomem *base, int parent_irq, f); } - f->domain = irq_domain_add_linear(node, fls(valid), - &fpga_irqdomain_ops, f); + f->domain = irq_domain_create_linear(of_fwnode_handle(node), fls(valid), + &fpga_irqdomain_ops, f); /* This will allocate all valid descriptors in the linear case */ for (i = 0; i < fls(valid); i++) diff --git a/drivers/irqchip/irq-vf610-mscm-ir.c b/drivers/irqchip/irq-vf610-mscm-ir.c index 2b9a8ba58e26..5d9c7503aa7f 100644 --- a/drivers/irqchip/irq-vf610-mscm-ir.c +++ b/drivers/irqchip/irq-vf610-mscm-ir.c @@ -209,9 +209,9 @@ static int __init vf610_mscm_ir_of_init(struct device_node *node, regmap_read(mscm_cp_regmap, MSCM_CPxNUM, &cpuid); mscm_ir_data->cpu_mask = 0x1 << cpuid; - domain = irq_domain_add_hierarchy(domain_parent, 0, - MSCM_IRSPRC_NUM, node, - &mscm_irq_domain_ops, mscm_ir_data); + domain = irq_domain_create_hierarchy(domain_parent, 0, MSCM_IRSPRC_NUM, + of_fwnode_handle(node), &mscm_irq_domain_ops, + mscm_ir_data); if (!domain) { ret = -ENOMEM; goto out_unmap; diff --git a/drivers/irqchip/irq-vic.c b/drivers/irqchip/irq-vic.c index ea93e7236c4a..2bcdf216a000 100644 --- a/drivers/irqchip/irq-vic.c +++ b/drivers/irqchip/irq-vic.c @@ -289,8 +289,9 @@ static void __init vic_register(void __iomem *base, unsigned int parent_irq, vic_handle_irq_cascaded, v); } - v->domain = irq_domain_add_simple(node, fls(valid_sources), irq, - &vic_irqdomain_ops, v); + v->domain = irq_domain_create_simple(of_fwnode_handle(node), + fls(valid_sources), irq, + &vic_irqdomain_ops, v); /* create an IRQ mapping for each valid IRQ */ for (i = 0; i < fls(valid_sources); i++) if (valid_sources & (1 << i)) diff --git a/drivers/irqchip/irq-vt8500.c b/drivers/irqchip/irq-vt8500.c index e17dd3a8c2d5..3b742590aec8 100644 --- a/drivers/irqchip/irq-vt8500.c +++ b/drivers/irqchip/irq-vt8500.c @@ -15,6 +15,7 @@ #include <linux/io.h> #include <linux/irq.h> #include <linux/irqchip.h> +#include <linux/irqchip/chained_irq.h> #include <linux/irqdomain.h> #include <linux/interrupt.h> #include <linux/bitops.h> @@ -63,29 +64,28 @@ struct vt8500_irq_data { struct irq_domain *domain; /* Domain for this controller */ }; -/* Global variable for accessing io-mem addresses */ -static struct vt8500_irq_data intc[VT8500_INTC_MAX]; -static u32 active_cnt = 0; +/* Primary interrupt controller data */ +static struct vt8500_irq_data *primary_intc; -static void vt8500_irq_mask(struct irq_data *d) +static void vt8500_irq_ack(struct irq_data *d) { struct vt8500_irq_data *priv = d->domain->host_data; void __iomem *base = priv->base; void __iomem *stat_reg = base + VT8500_ICIS + (d->hwirq < 32 ? 0 : 4); - u8 edge, dctr; - u32 status; + u32 status = (1 << (d->hwirq & 0x1f)); - edge = readb(base + VT8500_ICDC + d->hwirq) & VT8500_EDGE; - if (edge) { - status = readl(stat_reg); + writel(status, stat_reg); +} - status |= (1 << (d->hwirq & 0x1f)); - writel(status, stat_reg); - } else { - dctr = readb(base + VT8500_ICDC + d->hwirq); - dctr &= ~VT8500_INT_ENABLE; - writeb(dctr, base + VT8500_ICDC + d->hwirq); - } +static void vt8500_irq_mask(struct irq_data *d) +{ + struct vt8500_irq_data *priv = d->domain->host_data; + void __iomem *base = priv->base; + u8 dctr; + + dctr = readb(base + VT8500_ICDC + d->hwirq); + dctr &= ~VT8500_INT_ENABLE; + writeb(dctr, base + VT8500_ICDC + d->hwirq); } static void vt8500_irq_unmask(struct irq_data *d) @@ -130,11 +130,11 @@ static int vt8500_irq_set_type(struct irq_data *d, unsigned int flow_type) } static struct irq_chip vt8500_irq_chip = { - .name = "vt8500", - .irq_ack = vt8500_irq_mask, - .irq_mask = vt8500_irq_mask, - .irq_unmask = vt8500_irq_unmask, - .irq_set_type = vt8500_irq_set_type, + .name = "vt8500", + .irq_ack = vt8500_irq_ack, + .irq_mask = vt8500_irq_mask, + .irq_unmask = vt8500_irq_unmask, + .irq_set_type = vt8500_irq_set_type, }; static void __init vt8500_init_irq_hw(void __iomem *base) @@ -163,82 +163,89 @@ static const struct irq_domain_ops vt8500_irq_domain_ops = { .xlate = irq_domain_xlate_onecell, }; +static inline void vt8500_handle_irq_common(struct vt8500_irq_data *intc) +{ + unsigned long irqnr = readl_relaxed(intc->base) & 0x3F; + unsigned long stat; + + /* + * Highest Priority register default = 63, so check that this + * is a real interrupt by checking the status register + */ + if (irqnr == 63) { + stat = readl_relaxed(intc->base + VT8500_ICIS + 4); + if (!(stat & BIT(31))) + return; + } + + generic_handle_domain_irq(intc->domain, irqnr); +} + static void __exception_irq_entry vt8500_handle_irq(struct pt_regs *regs) { - u32 stat, i; - int irqnr; - void __iomem *base; - - /* Loop through each active controller */ - for (i=0; i<active_cnt; i++) { - base = intc[i].base; - irqnr = readl_relaxed(base) & 0x3F; - /* - Highest Priority register default = 63, so check that this - is a real interrupt by checking the status register - */ - if (irqnr == 63) { - stat = readl_relaxed(base + VT8500_ICIS + 4); - if (!(stat & BIT(31))) - continue; - } + vt8500_handle_irq_common(primary_intc); +} - generic_handle_domain_irq(intc[i].domain, irqnr); - } +static void vt8500_handle_irq_chained(struct irq_desc *desc) +{ + struct irq_domain *d = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + struct vt8500_irq_data *intc = d->host_data; + + chained_irq_enter(chip, desc); + vt8500_handle_irq_common(intc); + chained_irq_exit(chip, desc); } static int __init vt8500_irq_init(struct device_node *node, struct device_node *parent) { - int irq, i; - struct device_node *np = node; + struct vt8500_irq_data *intc; + int irq, i, ret = 0; - if (active_cnt == VT8500_INTC_MAX) { - pr_err("%s: Interrupt controllers > VT8500_INTC_MAX\n", - __func__); - goto out; - } + intc = kzalloc(sizeof(*intc), GFP_KERNEL); + if (!intc) + return -ENOMEM; - intc[active_cnt].base = of_iomap(np, 0); - intc[active_cnt].domain = irq_domain_add_linear(node, 64, - &vt8500_irq_domain_ops, &intc[active_cnt]); - - if (!intc[active_cnt].base) { + intc->base = of_iomap(node, 0); + if (!intc->base) { pr_err("%s: Unable to map IO memory\n", __func__); - goto out; + ret = -ENOMEM; + goto err_free; } - if (!intc[active_cnt].domain) { + intc->domain = irq_domain_create_linear(of_fwnode_handle(node), 64, + &vt8500_irq_domain_ops, intc); + if (!intc->domain) { pr_err("%s: Unable to add irq domain!\n", __func__); - goto out; + ret = -ENOMEM; + goto err_unmap; } - set_handle_irq(vt8500_handle_irq); - - vt8500_init_irq_hw(intc[active_cnt].base); + vt8500_init_irq_hw(intc->base); pr_info("vt8500-irq: Added interrupt controller\n"); - active_cnt++; - - /* check if this is a slaved controller */ - if (of_irq_count(np) != 0) { - /* check that we have the correct number of interrupts */ - if (of_irq_count(np) != 8) { - pr_err("%s: Incorrect IRQ map for slaved controller\n", - __func__); - return -EINVAL; - } - - for (i = 0; i < 8; i++) { - irq = irq_of_parse_and_map(np, i); - enable_irq(irq); + /* check if this is a chained controller */ + if (of_irq_count(node) != 0) { + for (i = 0; i < of_irq_count(node); i++) { + irq = irq_of_parse_and_map(node, i); + irq_set_chained_handler_and_data(irq, vt8500_handle_irq_chained, + intc); } pr_info("vt8500-irq: Enabled slave->parent interrupts\n"); + } else { + primary_intc = intc; + set_handle_irq(vt8500_handle_irq); } -out: return 0; + +err_unmap: + iounmap(intc->base); +err_free: + kfree(intc); + return ret; } IRQCHIP_DECLARE(vt8500_irq, "via,vt8500-intc", vt8500_irq_init); diff --git a/drivers/irqchip/irq-wpcm450-aic.c b/drivers/irqchip/irq-wpcm450-aic.c index 91df62a64cd9..a8ed4894d29e 100644 --- a/drivers/irqchip/irq-wpcm450-aic.c +++ b/drivers/irqchip/irq-wpcm450-aic.c @@ -154,7 +154,7 @@ static int __init wpcm450_aic_of_init(struct device_node *node, set_handle_irq(wpcm450_aic_handle_irq); - aic->domain = irq_domain_add_linear(node, AIC_NUM_IRQS, &wpcm450_aic_ops, aic); + aic->domain = irq_domain_create_linear(of_fwnode_handle(node), AIC_NUM_IRQS, &wpcm450_aic_ops, aic); return 0; } diff --git a/drivers/irqchip/irq-xilinx-intc.c b/drivers/irqchip/irq-xilinx-intc.c index 7e08714d507f..92dcb9fdcb25 100644 --- a/drivers/irqchip/irq-xilinx-intc.c +++ b/drivers/irqchip/irq-xilinx-intc.c @@ -212,8 +212,8 @@ static int __init xilinx_intc_of_init(struct device_node *intc, xintc_write(irqc, MER, MER_HIE | MER_ME); } - irqc->root_domain = irq_domain_add_linear(intc, irqc->nr_irq, - &xintc_irq_domain_ops, irqc); + irqc->root_domain = irq_domain_create_linear(of_fwnode_handle(intc), irqc->nr_irq, + &xintc_irq_domain_ops, irqc); if (!irqc->root_domain) { pr_err("irq-xilinx: Unable to create IRQ domain\n"); ret = -EINVAL; @@ -233,7 +233,7 @@ static int __init xilinx_intc_of_init(struct device_node *intc, } } else { primary_intc = irqc; - irq_set_default_host(primary_intc->root_domain); + irq_set_default_domain(primary_intc->root_domain); set_handle_irq(xil_intc_handle_irq); } diff --git a/drivers/irqchip/irq-xtensa-mx.c b/drivers/irqchip/irq-xtensa-mx.c index 7f314e58f3ce..9fdacbd89a63 100644 --- a/drivers/irqchip/irq-xtensa-mx.c +++ b/drivers/irqchip/irq-xtensa-mx.c @@ -156,7 +156,7 @@ static void __init xtensa_mx_init_common(struct irq_domain *root_domain) { unsigned int i; - irq_set_default_host(root_domain); + irq_set_default_domain(root_domain); secondary_init_irq(); /* Initialize default IRQ routing to CPU 0 */ @@ -167,8 +167,7 @@ static void __init xtensa_mx_init_common(struct irq_domain *root_domain) int __init xtensa_mx_init_legacy(struct device_node *interrupt_parent) { struct irq_domain *root_domain = - irq_domain_add_legacy(NULL, NR_IRQS - 1, 1, 0, - &xtensa_mx_irq_domain_ops, + irq_domain_create_legacy(NULL, NR_IRQS - 1, 1, 0, &xtensa_mx_irq_domain_ops, &xtensa_mx_irq_chip); xtensa_mx_init_common(root_domain); return 0; @@ -178,7 +177,7 @@ static int __init xtensa_mx_init(struct device_node *np, struct device_node *interrupt_parent) { struct irq_domain *root_domain = - irq_domain_add_linear(np, NR_IRQS, &xtensa_mx_irq_domain_ops, + irq_domain_create_linear(of_fwnode_handle(np), NR_IRQS, &xtensa_mx_irq_domain_ops, &xtensa_mx_irq_chip); xtensa_mx_init_common(root_domain); return 0; diff --git a/drivers/irqchip/irq-xtensa-pic.c b/drivers/irqchip/irq-xtensa-pic.c index f9d6fce4da33..44e7be051a2e 100644 --- a/drivers/irqchip/irq-xtensa-pic.c +++ b/drivers/irqchip/irq-xtensa-pic.c @@ -85,9 +85,9 @@ static struct irq_chip xtensa_irq_chip = { int __init xtensa_pic_init_legacy(struct device_node *interrupt_parent) { struct irq_domain *root_domain = - irq_domain_add_legacy(NULL, NR_IRQS - 1, 1, 0, + irq_domain_create_legacy(NULL, NR_IRQS - 1, 1, 0, &xtensa_irq_domain_ops, &xtensa_irq_chip); - irq_set_default_host(root_domain); + irq_set_default_domain(root_domain); return 0; } @@ -95,9 +95,9 @@ static int __init xtensa_pic_init(struct device_node *np, struct device_node *interrupt_parent) { struct irq_domain *root_domain = - irq_domain_add_linear(np, NR_IRQS, &xtensa_irq_domain_ops, + irq_domain_create_linear(of_fwnode_handle(np), NR_IRQS, &xtensa_irq_domain_ops, &xtensa_irq_chip); - irq_set_default_host(root_domain); + irq_set_default_domain(root_domain); return 0; } IRQCHIP_DECLARE(xtensa_irq_chip, "cdns,xtensa-pic", xtensa_pic_init); diff --git a/drivers/irqchip/irq-zevio.c b/drivers/irqchip/irq-zevio.c index 7a72620fc478..22d46c246594 100644 --- a/drivers/irqchip/irq-zevio.c +++ b/drivers/irqchip/irq-zevio.c @@ -92,8 +92,8 @@ static int __init zevio_of_init(struct device_node *node, zevio_init_irq_base(zevio_irq_io + IO_IRQ_BASE); zevio_init_irq_base(zevio_irq_io + IO_FIQ_BASE); - zevio_irq_domain = irq_domain_add_linear(node, MAX_INTRS, - &irq_generic_chip_ops, NULL); + zevio_irq_domain = irq_domain_create_linear(of_fwnode_handle(node), MAX_INTRS, + &irq_generic_chip_ops, NULL); BUG_ON(!zevio_irq_domain); ret = irq_alloc_domain_generic_chips(zevio_irq_domain, MAX_INTRS, 1, diff --git a/drivers/irqchip/qcom-pdc.c b/drivers/irqchip/qcom-pdc.c index 74b2f124116e..52d77546aacb 100644 --- a/drivers/irqchip/qcom-pdc.c +++ b/drivers/irqchip/qcom-pdc.c @@ -21,9 +21,11 @@ #include <linux/types.h> #define PDC_MAX_GPIO_IRQS 256 +#define PDC_DRV_OFFSET 0x10000 /* Valid only on HW version < 3.2 */ #define IRQ_ENABLE_BANK 0x10 +#define IRQ_ENABLE_BANK_MAX (IRQ_ENABLE_BANK + BITS_TO_BYTES(PDC_MAX_GPIO_IRQS)) #define IRQ_i_CFG 0x110 /* Valid only on HW version >= 3.2 */ @@ -46,13 +48,20 @@ struct pdc_pin_region { static DEFINE_RAW_SPINLOCK(pdc_lock); static void __iomem *pdc_base; +static void __iomem *pdc_prev_base; static struct pdc_pin_region *pdc_region; static int pdc_region_cnt; static unsigned int pdc_version; +static bool pdc_x1e_quirk; + +static void pdc_base_reg_write(void __iomem *base, int reg, u32 i, u32 val) +{ + writel_relaxed(val, base + reg + i * sizeof(u32)); +} static void pdc_reg_write(int reg, u32 i, u32 val) { - writel_relaxed(val, pdc_base + reg + i * sizeof(u32)); + pdc_base_reg_write(pdc_base, reg, i, val); } static u32 pdc_reg_read(int reg, u32 i) @@ -60,6 +69,34 @@ static u32 pdc_reg_read(int reg, u32 i) return readl_relaxed(pdc_base + reg + i * sizeof(u32)); } +static void pdc_x1e_irq_enable_write(u32 bank, u32 enable) +{ + void __iomem *base; + + /* Remap the write access to work around a hardware bug on X1E */ + switch (bank) { + case 0 ... 1: + /* Use previous DRV (client) region and shift to bank 3-4 */ + base = pdc_prev_base; + bank += 3; + break; + case 2 ... 4: + /* Use our own region and shift to bank 0-2 */ + base = pdc_base; + bank -= 2; + break; + case 5: + /* No fixup required for bank 5 */ + base = pdc_base; + break; + default: + WARN_ON(1); + return; + } + + pdc_base_reg_write(base, IRQ_ENABLE_BANK, bank, enable); +} + static void __pdc_enable_intr(int pin_out, bool on) { unsigned long enable; @@ -72,7 +109,11 @@ static void __pdc_enable_intr(int pin_out, bool on) enable = pdc_reg_read(IRQ_ENABLE_BANK, index); __assign_bit(mask, &enable, on); - pdc_reg_write(IRQ_ENABLE_BANK, index, enable); + + if (pdc_x1e_quirk) + pdc_x1e_irq_enable_write(index, enable); + else + pdc_reg_write(IRQ_ENABLE_BANK, index, enable); } else { enable = pdc_reg_read(IRQ_i_CFG, pin_out); __assign_bit(IRQ_i_CFG_IRQ_ENABLE, &enable, on); @@ -324,10 +365,29 @@ static int qcom_pdc_init(struct device_node *node, struct device_node *parent) if (res_size > resource_size(&res)) pr_warn("%pOF: invalid reg size, please fix DT\n", node); + /* + * PDC has multiple DRV regions, each one provides the same set of + * registers for a particular client in the system. Due to a hardware + * bug on X1E, some writes to the IRQ_ENABLE_BANK register must be + * issued inside the previous region. This region belongs to + * a different client and is not described in the device tree. Map the + * region with the expected offset to preserve support for old DTs. + */ + if (of_device_is_compatible(node, "qcom,x1e80100-pdc")) { + pdc_prev_base = ioremap(res.start - PDC_DRV_OFFSET, IRQ_ENABLE_BANK_MAX); + if (!pdc_prev_base) { + pr_err("%pOF: unable to map previous PDC DRV region\n", node); + return -ENXIO; + } + + pdc_x1e_quirk = true; + } + pdc_base = ioremap(res.start, res_size); if (!pdc_base) { pr_err("%pOF: unable to map PDC registers\n", node); - return -ENXIO; + ret = -ENXIO; + goto fail; } pdc_version = pdc_reg_read(PDC_VERSION_REG, 0); @@ -363,6 +423,7 @@ static int qcom_pdc_init(struct device_node *node, struct device_node *parent) fail: kfree(pdc_region); iounmap(pdc_base); + iounmap(pdc_prev_base); return ret; } diff --git a/drivers/irqchip/spear-shirq.c b/drivers/irqchip/spear-shirq.c index 7c17a6f643ef..576e55569d77 100644 --- a/drivers/irqchip/spear-shirq.c +++ b/drivers/irqchip/spear-shirq.c @@ -239,7 +239,7 @@ static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr, goto err_unmap; } - shirq_domain = irq_domain_add_legacy(np, nr_irqs, virq_base, 0, + shirq_domain = irq_domain_create_legacy(of_fwnode_handle(np), nr_irqs, virq_base, 0, &irq_domain_simple_ops, NULL); if (WARN_ON(!shirq_domain)) { pr_warn("%s: irq domain init failed\n", __func__); |