diff --git a/Documentation/arch/arm64/silicon-errata.rst b/Documentation/arch/arm64/silicon-errata.rst index bd7e6c5a17b7..d78225079dbe 100644 --- a/Documentation/arch/arm64/silicon-errata.rst +++ b/Documentation/arch/arm64/silicon-errata.rst @@ -53,6 +53,9 @@ stable kernels. | Allwinner | A64/R18 | UNKNOWN1 | SUN50I_ERRATUM_UNKNOWN1 | +----------------+-----------------+-----------------+-----------------------------+ +----------------+-----------------+-----------------+-----------------------------+ +| Altera | SoCFPGA Agilex5 | 2.1.23 |ALTERA_ERRATUM_AGILEX5_2_1_23| ++----------------+-----------------+-----------------+-----------------------------+ ++----------------+-----------------+-----------------+-----------------------------+ | Ampere | AmpereOne | AC03_CPU_38 | AMPERE_ERRATUM_AC03_CPU_38 | +----------------+-----------------+-----------------+-----------------------------+ | Ampere | AmpereOne | AC03_CPU_57 | N/A | diff --git a/Documentation/devicetree/bindings/interrupt-controller/amazon,al-fic.yaml b/Documentation/devicetree/bindings/interrupt-controller/amazon,al-fic.yaml index 26bc05dee0bc..1ebf9a427f2f 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/amazon,al-fic.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/amazon,al-fic.yaml @@ -24,6 +24,19 @@ properties: interrupts: maxItems: 1 + amazon,al-fic-mask: + $ref: /schemas/types.yaml#/definitions/string + description: + Selects which of the controller's severity outputs this group drives, + and thus which mask register the driver programs. A group has one cause + register and three severity outputs - info, error and fatal - each + gated by its own mask register. Each output is aggregated into a + separate interrupt tree, and a group belongs to exactly one of them. + The error and fatal outputs exist only on controller revisions that + implement them; earlier revisions provide the info output only. + enum: [info, error, fatal] + default: info + required: - compatible - reg @@ -39,8 +52,9 @@ examples: interrupt-controller@fd8a8500 { compatible = "amazon,al-fic"; - reg = <0xfd8a8500 0x1000>; + reg = <0xfd8a8500 0x40>; interrupt-controller; #interrupt-cells = <2>; interrupts = ; + amazon,al-fic-mask = "error"; }; diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5.yaml b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5.yaml index 86ca7f3ac281..b901e4288816 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5.yaml @@ -120,13 +120,14 @@ patternProperties: cpus: description: - CPUs managed by the IRS. + CPUs managed by the IRS. Omit it if the IRS has no associated CPUs. arm,iaffids: $ref: /schemas/types.yaml#/definitions/uint16-array description: Interrupt AFFinity ID (IAFFID) associated with the CPU whose CPU node phandle is at the same index in the cpus array. + Omit it if the IRS has no associated CPUs. patternProperties: "^its@[0-9a-f]+$": @@ -210,12 +211,14 @@ patternProperties: - reg - reg-names + dependencies: + cpus: [ "arm,iaffids" ] + arm,iaffids: [ cpus ] + required: - compatible - reg - reg-names - - cpus - - arm,iaffids additionalProperties: false diff --git a/Documentation/devicetree/bindings/interrupt-controller/qcom,pdc.yaml b/Documentation/devicetree/bindings/interrupt-controller/qcom,pdc.yaml index 8162a49d49a6..b3d19c86a240 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/qcom,pdc.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/qcom,pdc.yaml @@ -58,6 +58,7 @@ properties: - qcom,sm8650-pdc - qcom,sm8750-pdc - qcom,x1e80100-pdc + - qcom,x1p42100-pdc - const: qcom,pdc reg: diff --git a/Documentation/devicetree/bindings/interrupt-controller/xlnx,versal-net-smmu-csr.yaml b/Documentation/devicetree/bindings/interrupt-controller/xlnx,versal-net-smmu-csr.yaml new file mode 100644 index 000000000000..38b63e51b732 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/xlnx,versal-net-smmu-csr.yaml @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interrupt-controller/xlnx,versal-net-smmu-csr.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Xilinx Versal NET SMMU CSR Interrupt Controller + +maintainers: + - Anirudha Sarangi + - Tushar Nimkar + +description: | + The Versal NET platform includes a Xilinx-specific SMMU CSR (Control and + Status Register) block that acts as an intermediate interrupt controller + for ARM SMMUv3. + + The block provides registers to enable, disable and clear the + standard SMMUv3 interrupt sources (EVENTQ, CMDQ_SYNC, GERROR and PRIQ) + before forwarding them to the parent interrupt controller, + typically a GIC. + + The controller receives one parent interrupt and demultiplexes CSR + status bits into the following child interrupt identifiers + 0 - EVENTQ + 1 - CMDQ_SYNC + 2 - GERROR (GLOBAL) + 3 - PRIQ + +allOf: + - $ref: /schemas/interrupt-controller.yaml# + +properties: + compatible: + const: xlnx,versal-net-smmu-csr + + reg: + maxItems: 1 + + interrupt-controller: true + + "#interrupt-cells": + const: 1 + description: | + Single cell containing the child interrupt identifier + 0 - EVENTQ + 1 - CMDQ_SYNC + 2 - GERROR (GLOBAL) + 3 - PRIQ + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - interrupt-controller + - "#interrupt-cells" + - interrupts + +additionalProperties: false + +examples: + - | + #include + #include + + interrupt-controller@eca10000 { + compatible = "xlnx,versal-net-smmu-csr"; + reg = <0xeca10000 0x1000>; + interrupt-controller; + #interrupt-cells = <1>; + interrupt-parent = <&gic>; + interrupts = ; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 322fa3270b10..d089474cd592 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -29983,6 +29983,16 @@ F: Documentation/misc-devices/xilinx_sdfec.rst F: drivers/misc/xilinx_sdfec.c F: include/uapi/misc/xilinx_sdfec.h +XILINX SMMU CSR IRQ DRIVER +M: Anirudha Sarangi +M: Tushar Nimkar +L: git@amd.com +L: linux-arm-kernel@lists.infradead.org +L: linux-kernel@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/interrupt-controller/xlnx,versal-net-smmu-csr.yaml +F: drivers/irqchip/irq-xilinx-smmu-csr.c + XILINX TRNG DRIVER M: Mounika Botcha M: Harsh Jain diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 906f408bc9b8..a8507da58eeb 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1461,6 +1461,15 @@ config NVIDIA_OLYMPUS_1027_ERRATUM If unsure, say Y. +config ALTERA_ERRATUM_AGILEX5_2_1_23 + bool + help + The Altera SoCFPGA Agilex5 GIC600 SoC integration has ACE-lite + addressing limited to the first 32bit of physical address space, + so the ITS cannot access memory above 4GB. + + Selected by ARCH_INTEL_SOCFPGA, as all Agilex5 devices are affected. + config RENESAS_ERRATUM_GEN4GICITS1 bool "Renesas R-Car Gen4: GIC600 can not access physical addresses above 4 GiB" default y diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms index d2acfac73003..07127f7291db 100644 --- a/arch/arm64/Kconfig.platforms +++ b/arch/arm64/Kconfig.platforms @@ -361,6 +361,7 @@ config ARCH_SEATTLE config ARCH_INTEL_SOCFPGA bool "Intel's SoCFPGA ARMv8 Families" + select ALTERA_ERRATUM_AGILEX5_2_1_23 help This enables support for Intel's SoCFPGA ARMv8 families: Stratix 10 (ex. Altera), Stratix10 Software Virtual Platform, diff --git a/arch/arm64/boot/dts/qcom/purwa.dtsi b/arch/arm64/boot/dts/qcom/purwa.dtsi index c698e6cb2543..4348dd3d1dc5 100644 --- a/arch/arm64/boot/dts/qcom/purwa.dtsi +++ b/arch/arm64/boot/dts/qcom/purwa.dtsi @@ -224,6 +224,11 @@ &pcie6a_phy { compatible = "qcom,x1p42100-qmp-gen4x4-pcie-phy"; }; +/* X1P42100 PDC is same as X1E80100, but without hardware register bug */ +&pdc { + compatible = "qcom,x1p42100-pdc", "qcom,pdc"; +}; + &qfprom { gpu_speed_bin: gpu-speed-bin@119 { reg = <0x119 0x2>; diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 6ec8d3a69c98..978e00891dca 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -1743,8 +1743,6 @@ CONFIG_PWM_VISCONTI=m CONFIG_PWM_XILINX=m CONFIG_SL28CPLD_INTC=y CONFIG_XILINX_INTC=y -CONFIG_QCOM_PDC=y -CONFIG_QCOM_MPM=y CONFIG_TI_SCI_INTR_IRQCHIP=y CONFIG_TI_SCI_INTA_IRQCHIP=y CONFIG_RESET_GPIO=m diff --git a/arch/riscv/include/asm/smp.h b/arch/riscv/include/asm/smp.h index 0ecc67641b09..bed39fff1f8a 100644 --- a/arch/riscv/include/asm/smp.h +++ b/arch/riscv/include/asm/smp.h @@ -15,6 +15,18 @@ struct seq_file; extern unsigned long boot_cpu_hartid; +enum ipi_message_type { + IPI_RESCHEDULE, + IPI_CALL_FUNC, + IPI_CPU_STOP, + IPI_CPU_CRASH_STOP, + IPI_IRQ_WORK, + IPI_TIMER, + IPI_CPU_BACKTRACE, + IPI_KGDB_ROUNDUP, + IPI_MAX +}; + #ifdef CONFIG_SMP #include diff --git a/arch/riscv/kernel/sbi-ipi.c b/arch/riscv/kernel/sbi-ipi.c index 0cc5559c08d8..eeec178a9b95 100644 --- a/arch/riscv/kernel/sbi-ipi.c +++ b/arch/riscv/kernel/sbi-ipi.c @@ -57,7 +57,7 @@ void __init sbi_ipi_init(void) return; } - virq = ipi_mux_create(BITS_PER_BYTE, sbi_send_ipi); + virq = ipi_mux_create(IPI_MAX, sbi_send_ipi); if (virq <= 0) { pr_err("unable to create muxed IPIs\n"); irq_dispose_mapping(sbi_ipi_virq); @@ -75,7 +75,7 @@ void __init sbi_ipi_init(void) "irqchip/sbi-ipi:starting", sbi_ipi_starting_cpu, NULL); - riscv_ipi_set_virq_range(virq, BITS_PER_BYTE); + riscv_ipi_set_virq_range(virq, IPI_MAX); pr_info("providing IPIs using SBI IPI extension\n"); /* diff --git a/arch/riscv/kernel/smp.c b/arch/riscv/kernel/smp.c index fa66f9c97d74..8930b62b15e7 100644 --- a/arch/riscv/kernel/smp.c +++ b/arch/riscv/kernel/smp.c @@ -28,18 +28,6 @@ #include #include -enum ipi_message_type { - IPI_RESCHEDULE, - IPI_CALL_FUNC, - IPI_CPU_STOP, - IPI_CPU_CRASH_STOP, - IPI_IRQ_WORK, - IPI_TIMER, - IPI_CPU_BACKTRACE, - IPI_KGDB_ROUNDUP, - IPI_MAX -}; - static const char * const ipi_names[] = { [IPI_RESCHEDULE] = "Rescheduling interrupts", [IPI_CALL_FUNC] = "Function call interrupts", diff --git a/drivers/clocksource/timer-clint.c b/drivers/clocksource/timer-clint.c index 0bdd9d7ec545..e56eee7e3781 100644 --- a/drivers/clocksource/timer-clint.c +++ b/drivers/clocksource/timer-clint.c @@ -243,7 +243,7 @@ static int __init clint_timer_init_dt(struct device_node *np) } #ifdef CONFIG_SMP - rc = ipi_mux_create(BITS_PER_BYTE, clint_send_ipi); + rc = ipi_mux_create(IPI_MAX, clint_send_ipi); if (rc <= 0) { pr_err("unable to create muxed IPIs\n"); rc = (rc < 0) ? rc : -ENODEV; @@ -251,7 +251,7 @@ static int __init clint_timer_init_dt(struct device_node *np) } irq_set_chained_handler(clint_ipi_irq, clint_ipi_interrupt); - riscv_ipi_set_virq_range(rc, BITS_PER_BYTE); + riscv_ipi_set_virq_range(rc, IPI_MAX); clint_clear_ipi(); #endif diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 20b77fbc51ee..136bab41f801 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -522,17 +522,19 @@ config GOLDFISH_PIC config QCOM_PDC tristate "Qualcomm PDC" - depends on ARCH_QCOM + depends on QCOM_SCM select IRQ_DOMAIN_HIERARCHY + default ARCH_QCOM help Power Domain Controller driver to manage and configure wakeup IRQs for Qualcomm Technologies Inc (QTI) mobile chips. config QCOM_MPM tristate "Qualcomm MPM" - depends on ARCH_QCOM + depends on ARCH_QCOM || COMPILE_TEST depends on MAILBOX select IRQ_DOMAIN_HIERARCHY + default ARCH_QCOM if ARM64 help MSM Power Manager driver to manage and configure wakeup IRQs for Qualcomm Technologies Inc (QTI) mobile chips. @@ -840,4 +842,14 @@ config SUNPLUS_SP7021_INTC chained controller, routing all interrupt source in P-Chip to the primary controller on C-Chip. +config IRQCHIP_XILINX_SMMU_CSR + bool "Xilinx SMMU CSR Interrupt Driver" + depends on ARCH_ZYNQMP + select IRQ_DOMAIN + help + This driver does the initial handling of SMMU interrupts before + forwarding them to the ARM SMMU v3 driver. It is intended for + ZynqMP-class platforms where a vendor CSR block mediates SMMU + interrupts. + endmenu diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index ab33cccd8471..16a5fd126c9a 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -135,3 +135,4 @@ 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 +obj-$(CONFIG_IRQCHIP_XILINX_SMMU_CSR) += irq-xilinx-smmu-csr.o diff --git a/drivers/irqchip/irq-aclint-sswi.c b/drivers/irqchip/irq-aclint-sswi.c index ca06efd86fa1..5e010fc401f7 100644 --- a/drivers/irqchip/irq-aclint-sswi.c +++ b/drivers/irqchip/irq-aclint-sswi.c @@ -138,7 +138,7 @@ static int __init aclint_sswi_probe(struct fwnode_handle *fwnode) } /* Register SSWI irq and handler */ - virq = ipi_mux_create(BITS_PER_BYTE, aclint_sswi_ipi_send); + virq = ipi_mux_create(IPI_MAX, aclint_sswi_ipi_send); if (virq <= 0) { pr_err("unable to create muxed IPIs\n"); irq_dispose_mapping(sswi_ipi_virq); @@ -152,7 +152,7 @@ static int __init aclint_sswi_probe(struct fwnode_handle *fwnode) aclint_sswi_starting_cpu, aclint_sswi_dying_cpu); - riscv_ipi_set_virq_range(virq, BITS_PER_BYTE); + riscv_ipi_set_virq_range(virq, IPI_MAX); return 0; } diff --git a/drivers/irqchip/irq-al-fic.c b/drivers/irqchip/irq-al-fic.c index d10ac9b63c99..e6068e0d0660 100644 --- a/drivers/irqchip/irq-al-fic.c +++ b/drivers/irqchip/irq-al-fic.c @@ -4,9 +4,9 @@ */ #include +#include #include #include -#include #include #include #include @@ -16,11 +16,14 @@ /* FIC Registers */ #define AL_FIC_CAUSE 0x00 #define AL_FIC_SET_CAUSE 0x08 -#define AL_FIC_MASK 0x10 +#define AL_FIC_INFO_MASK 0x10 #define AL_FIC_CONTROL 0x28 +#define AL_FIC_ERROR_MASK 0x2c +#define AL_FIC_FATAL_MASK 0x34 #define CONTROL_TRIGGER_RISING BIT(3) #define CONTROL_MASK_MSI_X BIT(5) +#define CONTROL_VERSION_ID GENMASK(29, 28) #define NR_FIC_IRQS 32 @@ -33,20 +36,51 @@ enum al_fic_state { AL_FIC_CONFIGURED_RISING_EDGE, }; -struct al_fic { - void __iomem *base; - struct irq_domain *domain; - const char *name; - unsigned int parent_irq; - enum al_fic_state state; +/* + * FIC hardware revision, as reported by the CONTROL register version field + * (CONTROL_VERSION_ID, bits 29-28). These are this driver's names for that + * field's encoding. + */ +enum al_fic_version { + AL_FIC_VERSION_V1, + AL_FIC_VERSION_V2, + AL_FIC_VERSION_V3, }; -static void al_fic_set_trigger(struct al_fic *fic, - struct irq_chip_generic *gc, +enum al_fic_id { + AL_FIC_ID_INFO, + AL_FIC_ID_ERROR, + AL_FIC_ID_FATAL, + AL_FIC_ID_MAX, +}; + +/* Mask register offset for each interrupt group */ +static const unsigned int al_fic_mask_offset[AL_FIC_ID_MAX] = { + [AL_FIC_ID_INFO] = AL_FIC_INFO_MASK, + [AL_FIC_ID_ERROR] = AL_FIC_ERROR_MASK, + [AL_FIC_ID_FATAL] = AL_FIC_FATAL_MASK, +}; + +/* amazon,al-fic-mask property value for each interrupt group */ +static const char * const al_fic_mask_name[AL_FIC_ID_MAX] = { + [AL_FIC_ID_INFO] = "info", + [AL_FIC_ID_ERROR] = "error", + [AL_FIC_ID_FATAL] = "fatal", +}; + +struct al_fic { + void __iomem *base; + struct irq_domain *domain; + struct device_node *node; + unsigned int parent_irq; + enum al_fic_state state; +}; + +static void al_fic_set_trigger(struct al_fic *fic, struct irq_chip_generic *gc, enum al_fic_state new_state) { - irq_flow_handler_t handler; u32 control = readl_relaxed(fic->base + AL_FIC_CONTROL); + irq_flow_handler_t handler; if (new_state == AL_FIC_CONFIGURED_LEVEL) { handler = handle_level_irq; @@ -89,30 +123,34 @@ 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); + pr_debug("fic %pOF state already configured to %d\n", fic->node, fic->state); return -EINVAL; } return 0; } -static void al_fic_irq_handler(struct irq_desc *desc) +static irqreturn_t al_fic_irq_handler(int irq, void *data) { - struct al_fic *fic = irq_desc_get_handler_data(desc); + struct al_fic *fic = data; struct irq_domain *domain = fic->domain; - struct irq_chip *irqchip = irq_desc_get_chip(desc); struct irq_chip_generic *gc = irq_get_domain_generic_chip(domain, 0); unsigned long pending; u32 hwirq; - chained_irq_enter(irqchip, desc); - pending = readl_relaxed(fic->base + AL_FIC_CAUSE); pending &= ~gc->mask_cache; - for_each_set_bit(hwirq, &pending, NR_FIC_IRQS) - generic_handle_domain_irq(domain, hwirq); + for_each_set_bit(hwirq, &pending, NR_FIC_IRQS) { + /* + * Redirect a child whose affinity excludes this CPU to its + * target CPU via irq_work. The ack moves to + * al_fic_irq_pre_redirect(), because the whole flow handler is + * deferred when that happens. + */ + generic_handle_demux_domain_irq(domain, hwirq); + } - chained_irq_exit(irqchip, desc); + return pending ? IRQ_HANDLED : IRQ_NONE; } static int al_fic_irq_retrigger(struct irq_data *data) @@ -125,26 +163,53 @@ static int al_fic_irq_retrigger(struct irq_data *data) return 1; } -static int al_fic_register(struct device_node *node, - struct al_fic *fic) +#ifdef CONFIG_SMP +/* The ack happens in al_fic_irq_pre_redirect() on both paths instead. */ +static void al_fic_ack_noop(struct irq_data *data) { } + +/* + * Runs in the parent interrupt context on every dispatch, redirected or not. + * Clear the cause bit here: once the flow handler is deferred, the FIC keeps + * driving its output until the irq_work runs, and a level parent SPI re-fires. + * + * A level configured group is masked too, because its cause bit re-latches + * while the input stays asserted; the deferred handle_level_irq() unmasks + * when the handler is done. An edge configured group is only acked, since + * handle_edge_irq() never unmasks unconditionally and the mask would stay. + */ +static void al_fic_irq_pre_redirect(struct irq_data *data) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); + struct al_fic *fic = gc->private; + + if (fic->state == AL_FIC_CONFIGURED_LEVEL) + irq_gc_mask_set_bit(data); + + irq_gc_ack_clr_bit(data); +} +#endif /* CONFIG_SMP */ + +static int al_fic_register(struct device_node *node, struct al_fic *fic, enum al_fic_id fic_id) { struct irq_chip_generic *gc; int ret; - fic->domain = irq_domain_create_linear(of_fwnode_handle(node), - NR_FIC_IRQS, - &irq_generic_chip_ops, - fic); + fic->domain = irq_domain_create_linear(of_fwnode_handle(node), NR_FIC_IRQS, + &irq_generic_chip_ops, fic); if (!fic->domain) { pr_err("fail to add irq domain\n"); return -ENOMEM; } - ret = irq_alloc_domain_generic_chips(fic->domain, - NR_FIC_IRQS, - 1, fic->name, - handle_level_irq, - 0, 0, IRQ_GC_INIT_MASK_CACHE); + /* + * Let irq_domain_remove() free the generic chips on either error path + * below, instead of calling irq_domain_remove_generic_chips() by hand. + */ + fic->domain->flags |= IRQ_DOMAIN_FLAG_DESTROY_GC; + + ret = irq_alloc_domain_generic_chips(fic->domain, NR_FIC_IRQS, + 1, of_node_full_name(fic->node), handle_level_irq, + 0, 0, 0); if (ret) { pr_err("fail to allocate generic chip (%d)\n", ret); goto err_domain_remove; @@ -152,47 +217,67 @@ static int al_fic_register(struct device_node *node, gc = irq_get_domain_generic_chip(fic->domain, 0); gc->reg_base = fic->base; - gc->chip_types->regs.mask = AL_FIC_MASK; + gc->chip_types->regs.mask = al_fic_mask_offset[fic_id]; gc->chip_types->regs.ack = AL_FIC_CAUSE; gc->chip_types->chip.irq_mask = irq_gc_mask_set_bit; gc->chip_types->chip.irq_unmask = irq_gc_mask_clr_bit; - gc->chip_types->chip.irq_ack = irq_gc_ack_clr_bit; gc->chip_types->chip.irq_set_type = al_fic_irq_set_type; gc->chip_types->chip.irq_retrigger = al_fic_irq_retrigger; +#ifdef CONFIG_SMP + /* + * The FIC has no hardware affinity control, so give each child real + * affinity by redirecting its handler to the target CPU. + */ + gc->chip_types->chip.irq_ack = al_fic_ack_noop; + gc->chip_types->chip.irq_pre_redirect = al_fic_irq_pre_redirect; + gc->chip_types->chip.irq_set_affinity = irq_chip_redirect_set_affinity; +#else + gc->chip_types->chip.irq_ack = irq_gc_ack_clr_bit; +#endif gc->chip_types->chip.flags = IRQCHIP_SKIP_SET_WAKE; gc->private = fic; - irq_set_chained_handler_and_data(fic->parent_irq, - al_fic_irq_handler, - fic); + /* + * Seed the mask cache with the value al_fic_wire_init() programmed, + * rather than having the generic chip read the register back on the + * first child mapping: that is later than the parent is requested, and + * never happens at all for a group with no consumer in the devicetree. + */ + gc->mask_cache = ~0U; + + ret = request_irq(fic->parent_irq, al_fic_irq_handler, IRQF_NO_THREAD | IRQF_SHARED, + of_node_full_name(fic->node), fic); + if (ret) { + pr_err("fail to request irq (%d)\n", ret); + goto err_domain_remove; + } + return 0; err_domain_remove: irq_domain_remove(fic->domain); - return ret; } /* * al_fic_wire_init() - initialize and configure fic in wire mode - * @of_node: optional pointer to interrupt controller's device tree node. + * @node: pointer to the interrupt controller's device tree node * @base: mmio to fic register - * @name: name of the fic * @parent_irq: interrupt of parent + * @fic_id: which of the controller's outputs (info, error or fatal) this + * group drives * - * This API will configure the fic hardware to to work in wire mode. + * This API will configure the fic hardware to work in wire mode. * In wire mode, fic hardware is generating a wire ("wired") interrupt. * Interrupt can be generated based on positive edge or level - configuration is * to be determined based on connected hardware to this fic. */ -static struct al_fic *al_fic_wire_init(struct device_node *node, - void __iomem *base, - const char *name, - unsigned int parent_irq) +static struct al_fic *al_fic_wire_init(struct device_node *node, void __iomem *base, + unsigned int parent_irq, enum al_fic_id fic_id) { + u32 control, version_id; struct al_fic *fic; int ret; - u32 control = CONTROL_MASK_MSI_X; fic = kzalloc_obj(*fic); if (!fic) @@ -200,67 +285,110 @@ static struct al_fic *al_fic_wire_init(struct device_node *node, fic->base = base; fic->parent_irq = parent_irq; - fic->name = name; + fic->node = of_node_get(node); + + control = readl_relaxed(fic->base + AL_FIC_CONTROL); + version_id = FIELD_GET(CONTROL_VERSION_ID, control); + if (version_id == AL_FIC_VERSION_V1 && fic_id != AL_FIC_ID_INFO) { + pr_err("%pOF: amazon,al-fic-mask = \"%s\" not available on FIC v1\n", + node, al_fic_mask_name[fic_id]); + ret = -EINVAL; + goto err_free; + } /* mask out all interrupts */ - writel_relaxed(0xFFFFFFFF, fic->base + AL_FIC_MASK); + writel_relaxed(~0U, fic->base + AL_FIC_INFO_MASK); + if (version_id > AL_FIC_VERSION_V1) { + writel_relaxed(~0U, fic->base + AL_FIC_ERROR_MASK); + writel_relaxed(~0U, fic->base + AL_FIC_FATAL_MASK); + } /* clear any pending interrupt */ writel_relaxed(0, fic->base + AL_FIC_CAUSE); + /* make sure the controller works in non msi_x mode */ + control |= CONTROL_MASK_MSI_X; writel_relaxed(control, fic->base + AL_FIC_CONTROL); - ret = al_fic_register(node, fic); + ret = al_fic_register(node, fic, fic_id); if (ret) { pr_err("fail to register irqchip\n"); goto err_free; } - pr_debug("%s initialized successfully in Legacy mode (parent-irq=%u)\n", - fic->name, parent_irq); + pr_info("%pOF initialized successfully (mask=%s parent-irq=%u)\n", + node, al_fic_mask_name[fic_id], parent_irq); return fic; err_free: + of_node_put(fic->node); kfree(fic); return ERR_PTR(ret); } -static int __init al_fic_init_dt(struct device_node *node, - struct device_node *parent) +/* + * Parse the amazon,al-fic-mask property into an enum al_fic_id, selecting + * which of the controller's outputs this group drives. The property is + * optional; an absent property means the info output. + */ +static int al_fic_parse_mask(struct device_node *node, enum al_fic_id *fic_id) { + const char *mask; int ret; - void __iomem *base; + + ret = of_property_read_string(node, "amazon,al-fic-mask", &mask); + if (ret == -EINVAL) { + *fic_id = AL_FIC_ID_INFO; + return 0; + } + if (ret) + return ret; + + ret = match_string(al_fic_mask_name, AL_FIC_ID_MAX, mask); + if (ret < 0) + return ret; + + *fic_id = ret; + return 0; +} + +static int __init al_fic_init_dt(struct device_node *node, struct device_node *parent) +{ unsigned int parent_irq; + enum al_fic_id fic_id; struct al_fic *fic; + void __iomem *base; + int ret; if (!parent) { - pr_err("%s: unsupported - device require a parent\n", - node->name); + pr_err("%pOF: unsupported - device requires a parent\n", node); return -EINVAL; } + ret = al_fic_parse_mask(node, &fic_id); + if (ret) { + pr_err("%pOF: invalid amazon,al-fic-mask\n", node); + return ret; + } + base = of_iomap(node, 0); if (!base) { - pr_err("%s: fail to map memory\n", node->name); + pr_err("%pOF: fail to map memory\n", node); return -ENOMEM; } parent_irq = irq_of_parse_and_map(node, 0); if (!parent_irq) { - pr_err("%s: fail to map irq\n", node->name); + pr_err("%pOF: fail to map irq\n", node); ret = -EINVAL; goto err_unmap; } - fic = al_fic_wire_init(node, - base, - node->name, - parent_irq); + fic = al_fic_wire_init(node, base, parent_irq, fic_id); + if (IS_ERR(fic)) { - pr_err("%s: fail to initialize irqchip (%lu)\n", - node->name, - PTR_ERR(fic)); + pr_err("%pOF: fail to initialize irqchip (%pe)\n", node, fic); ret = PTR_ERR(fic); goto err_irq_dispose; } @@ -271,7 +399,6 @@ static int __init al_fic_init_dt(struct device_node *node, irq_dispose_mapping(parent_irq); err_unmap: iounmap(base); - return ret; } diff --git a/drivers/irqchip/irq-atmel-aic.c b/drivers/irqchip/irq-atmel-aic.c index 1dcc52760eca..4a7935e75e4d 100644 --- a/drivers/irqchip/irq-atmel-aic.c +++ b/drivers/irqchip/irq-atmel-aic.c @@ -123,7 +123,7 @@ static void aic_pm_shutdown(struct irq_data *d) { struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); - guard(raw_spinlock)(&gc->lock); + guard(raw_spinlock_irqsave)(&gc->lock); irq_reg_writel(gc, 0xffffffff, AT91_AIC_IDCR); irq_reg_writel(gc, 0xffffffff, AT91_AIC_ICCR); } diff --git a/drivers/irqchip/irq-atmel-aic5.c b/drivers/irqchip/irq-atmel-aic5.c index 1f14b401f71d..07a317ed06ef 100644 --- a/drivers/irqchip/irq-atmel-aic5.c +++ b/drivers/irqchip/irq-atmel-aic5.c @@ -217,7 +217,7 @@ static void aic5_pm_shutdown(struct irq_data *d) struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); int i; - guard(raw_spinlock)(&bgc->lock); + guard(raw_spinlock_irqsave)(&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); diff --git a/drivers/irqchip/irq-gic-common.c b/drivers/irqchip/irq-gic-common.c index c776f9142610..8bd1eaa54295 100644 --- a/drivers/irqchip/irq-gic-common.c +++ b/drivers/irqchip/irq-gic-common.c @@ -48,7 +48,7 @@ void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks, int gic_configure_irq(unsigned int irq, unsigned int type, void __iomem *base) { - u32 confmask = 0x2 << ((irq % 16) * 2); + u32 confmask = BIT(((irq % 16) * 2) + 1); u32 confoff = (irq / 16) * 4; u32 val, oldval; int ret = 0; diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index e9807af23537..282e4bafb084 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -87,6 +87,8 @@ struct its_baser { u64 val; u32 order; u32 psz; + /* Inherited from a sibling ITS, not freed here */ + bool shared; }; struct its_device; @@ -2248,7 +2250,8 @@ static unsigned long *its_lpi_alloc(int nr_irqs, u32 *base, int *nr_ids) static void its_lpi_free(unsigned long *bitmap, u32 base, u32 nr_ids) { - WARN_ON(free_lpi_range(base, nr_ids)); + if (free_lpi_range(base, nr_ids)) + pr_err_ratelimited("ITS: failed to free LPI range %u:%u\n", base, nr_ids); bitmap_free(bitmap); } @@ -2604,7 +2607,8 @@ static void its_free_tables(struct its_node *its) for (i = 0; i < GITS_BASER_NR_REGS; i++) { if (its->tables[i].base) { - its_free_pages(its->tables[i].base, its->tables[i].order); + if (!its->tables[i].shared) + its_free_pages(its->tables[i].base, its->tables[i].order); its->tables[i].base = NULL; } } @@ -2703,6 +2707,7 @@ static int its_alloc_tables(struct its_node *its) WARN_ON(i != 2); if ((sibling = find_sibling_its(its))) { *baser = sibling->tables[2]; + baser->shared = true; its_write_baser(its, baser, baser->val); continue; } @@ -3484,7 +3489,7 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id, * Also honor the ITS's own EID limit. */ id_bits = FIELD_GET(GITS_TYPER_IDBITS, its->typer) + 1; - nvecs = min_t(unsigned int, nvecs, BIT(id_bits)); + nvecs = min_t(u64, nvecs, BIT_ULL(id_bits)); nr_ites = max(2, nvecs); sz = nr_ites * (FIELD_GET(GITS_TYPER_ITT_ENTRY_SIZE, its->typer) + 1); sz = max(sz, ITS_ITT_ALIGN); @@ -4599,6 +4604,7 @@ static void its_vpe_teardown(struct its_vpe *vpe) its_vpe_db_proxy_unmap(vpe); its_vpe_id_free(vpe->vpe_id); its_free_pending_table(vpe->vpt_page); + vpe->vpt_page = NULL; } static void its_vpe_irq_domain_free(struct irq_domain *domain, @@ -4895,6 +4901,9 @@ static bool __maybe_unused its_enable_quirk_hip09_162100801(void *data) } static const char * const dma_32bit_impaired_platforms[] = { +#ifdef CONFIG_ALTERA_ERRATUM_AGILEX5_2_1_23 + "intel,socfpga-agilex5", +#endif #ifdef CONFIG_RENESAS_ERRATUM_GEN4GICITS1 "renesas,r8a779f0", "renesas,r8a779g0", @@ -5328,16 +5337,18 @@ static int __init its_probe_one(struct its_node *its) } gits_write_cwriter(0, its->base + GITS_CWRITER); + + err = its_init_domain(its); + if (err) + goto out_free_collection; + + /* Enable last: the unwind frees memory an enabled ITS may access */ ctlr = readl_relaxed(its->base + GITS_CTLR); ctlr |= GITS_CTLR_ENABLE; if (is_v4(its)) ctlr |= GITS_CTLR_ImDe; writel_relaxed(ctlr, its->base + GITS_CTLR); - err = its_init_domain(its); - if (err) - goto out_free_collection; - raw_spin_lock(&its_lock); list_add(&its->entry, &its_nodes); raw_spin_unlock(&its_lock); diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 6e1fa5b247fc..94873646d46f 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -164,6 +164,7 @@ static inline bool gic_dist_security_disabled(void) static bool cpus_have_security_disabled __ro_after_init; static bool cpus_have_group0 __ro_after_init; +static void gic_dist_wait_for_rwp(void); static void __init gic_prio_init(void) { @@ -179,6 +180,7 @@ static void __init gic_prio_init(void) val = readl_relaxed(gic_data.dist_base + GICD_CTLR); val |= GICD_CTLR_DS; writel_relaxed(val, gic_data.dist_base + GICD_CTLR); + gic_dist_wait_for_rwp(); ds = gic_dist_security_disabled(); if (ds) @@ -457,7 +459,7 @@ static int gic_peek_irq(struct irq_data *d, u32 offset) u32 index, mask; offset = convert_offset_index(d, offset, &index); - mask = 1 << (index % 32); + mask = BIT(index % 32); if (gic_irq_in_rdist(d)) base = gic_data_rdist_sgi_base(); @@ -473,7 +475,7 @@ static void gic_poke_irq(struct irq_data *d, u32 offset) u32 index, mask; offset = convert_offset_index(d, offset, &index); - mask = 1 << (index % 32); + mask = BIT(index % 32); if (gic_irq_in_rdist(d)) base = gic_data_rdist_sgi_base(); @@ -687,7 +689,7 @@ static void gic_eoimode1_eoi_irq(struct irq_data *d) { /* * No need to deactivate an LPI, or an interrupt that - * is is getting forwarded to a vcpu. + * is getting forwarded to a vcpu. */ if (irqd_to_hwirq(d) >= 8192 || irqd_is_forwarded_to_vcpu(d)) return; @@ -1728,8 +1730,6 @@ static int gic_irq_get_fwspec_info(struct irq_fwspec *fwspec, struct irq_fwspec_ /* If the specifier provides an affinity, use it */ if (fwspec->param_count == 4 && fwspec->param[3]) { - struct fwnode_handle *fw; - switch (fwspec->param[0]) { case 1: /* PPI */ case 3: /* EPPI */ @@ -1738,7 +1738,10 @@ static int gic_irq_get_fwspec_info(struct irq_fwspec *fwspec, struct irq_fwspec_ return 0; } - fw = of_fwnode_handle(of_find_node_by_phandle(fwspec->param[3])); + struct device_node *np __free(device_node) = + of_find_node_by_phandle(fwspec->param[3]); + struct fwnode_handle *fw = of_fwnode_handle(np); + if (!fw) return -ENOENT; @@ -2276,7 +2279,6 @@ static struct bool single_redist; int enabled_rdists; u32 maint_irq; - int maint_irq_mode; phys_addr_t vcpu_base; } acpi_data __initdata; @@ -2344,6 +2346,17 @@ gic_acpi_parse_madt_gicc(union acpi_subtable_headers *header, return 0; } + /* Not counted by gic_acpi_match_gicc(), so there is no slot for it */ + if (!gicc->gicr_base_address) { + int cpu = get_cpu_for_acpi_id(gicc->uid); + + pr_warn(FW_BUG "GICC entry with ACPI UID %u has no GICR base address, CPU kept offline\n", + gicc->uid); + if (cpu >= 0) + cpumask_set_cpu(cpu, &broken_rdists); + return 0; + } + redist_base = ioremap(gicc->gicr_base_address, size); if (!redist_base) return -ENOMEM; @@ -2454,21 +2467,19 @@ static int __init gic_acpi_parse_virt_madt_gicc(union acpi_subtable_headers *hea { struct acpi_madt_generic_interrupt *gicc = (struct acpi_madt_generic_interrupt *)header; - int maint_irq_mode; static int first_madt = true; if (!(gicc->flags & (ACPI_MADT_ENABLED | ACPI_MADT_GICC_ONLINE_CAPABLE))) return 0; - maint_irq_mode = (gicc->flags & ACPI_MADT_VGIC_IRQ_MODE) ? - ACPI_EDGE_SENSITIVE : ACPI_LEVEL_SENSITIVE; + if (gicc->flags & ACPI_MADT_VGIC_IRQ_MODE) + pr_warn_once(FW_BUG "MI wrongly advertised as Edge-triggered\n"); if (first_madt) { first_madt = false; acpi_data.maint_irq = gicc->vgic_interrupt; - acpi_data.maint_irq_mode = maint_irq_mode; acpi_data.vcpu_base = gicc->gicv_base_address; return 0; @@ -2478,7 +2489,6 @@ static int __init gic_acpi_parse_virt_madt_gicc(union acpi_subtable_headers *hea * The maintenance interrupt and GICV should be the same for every CPU */ if ((acpi_data.maint_irq != gicc->vgic_interrupt) || - (acpi_data.maint_irq_mode != maint_irq_mode) || (acpi_data.vcpu_base != gicc->gicv_base_address)) return -EINVAL; @@ -2511,7 +2521,7 @@ static void __init gic_acpi_setup_kvm_info(void) gic_v3_kvm_info.type = GIC_V3; irq = acpi_register_gsi(NULL, acpi_data.maint_irq, - acpi_data.maint_irq_mode, + ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_HIGH); if (irq <= 0) return; diff --git a/drivers/irqchip/irq-gic-v5-irs.c b/drivers/irqchip/irq-gic-v5-irs.c index 4486645a4b6f..fb1cecef613b 100644 --- a/drivers/irqchip/irq-gic-v5-irs.c +++ b/drivers/irqchip/irq-gic-v5-irs.c @@ -609,6 +609,10 @@ static int __init gicv5_irs_of_init_affinity(struct device_node *node, u16 iaffid_mask = GENMASK(iaffid_bits - 1, 0); int ret, i, ncpus, niaffids; + /* An IRS is architecturally allowed to have no cpus associated with it */ + if (!of_property_present(node, "cpus") && !of_property_present(node, "arm,iaffids")) + return 0; + ncpus = of_count_phandle_with_args(node, "cpus", NULL); if (ncpus < 0) return -EINVAL; diff --git a/drivers/irqchip/irq-gic-v5-iwb.c b/drivers/irqchip/irq-gic-v5-iwb.c index 442c20614dbe..34afa036c34d 100644 --- a/drivers/irqchip/irq-gic-v5-iwb.c +++ b/drivers/irqchip/irq-gic-v5-iwb.c @@ -282,8 +282,8 @@ MODULE_DEVICE_TABLE(of, gicv5_iwb_of_match); #ifdef CONFIG_ACPI static const struct acpi_device_id iwb_acpi_match[] = { - { "ARMH0003", 0 }, - {} + { .id = "ARMH0003" }, + { } }; #endif diff --git a/drivers/irqchip/irq-gic-v5.c b/drivers/irqchip/irq-gic-v5.c index 5f2551cf077d..74be71be479a 100644 --- a/drivers/irqchip/irq-gic-v5.c +++ b/drivers/irqchip/irq-gic-v5.c @@ -1168,21 +1168,18 @@ static int __init gicv5_init_common(struct fwnode_handle *parent_domain) if (ret) goto out_int; - ret = set_handle_irq(gicv5_handle_irq); + ret = gicv5_irs_enable(); if (ret) goto out_int; - ret = gicv5_irs_enable(); - if (ret) - goto out_handle; + if (set_handle_irq(gicv5_handle_irq)) + panic("GICv5: unable to install root IRQ handler\n"); gicv5_smp_init(); gicv5_irs_its_probe(); return 0; -out_handle: - set_handle_irq(NULL); out_int: gicv5_cpu_disable_interrupts(); gicv5_free_domains(); diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index f6bc29f515fb..a5ca04e9be64 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -178,14 +178,14 @@ static inline bool cascading_gic_irq(struct irq_data *d) */ static void gic_poke_irq(struct irq_data *d, u32 offset) { - u32 mask = 1 << (irqd_to_hwirq(d) % 32); + u32 mask = BIT(irqd_to_hwirq(d) % 32); writel_relaxed(mask, gic_dist_base(d) + offset + (irqd_to_hwirq(d) / 32) * 4); } static int gic_peek_irq(struct irq_data *d, u32 offset) { - u32 mask = 1 << (irqd_to_hwirq(d) % 32); + u32 mask = BIT(irqd_to_hwirq(d) % 32); return !!(readl_relaxed(gic_dist_base(d) + offset + (irqd_to_hwirq(d) / 32) * 4) & mask); } @@ -1527,7 +1527,6 @@ static struct { phys_addr_t cpu_phys_base; u32 maint_irq; - int maint_irq_mode; phys_addr_t vctrl_base; phys_addr_t vcpu_base; } acpi_data __initdata; @@ -1553,10 +1552,11 @@ gic_acpi_parse_madt_cpu(union acpi_subtable_headers *header, if (cpu_base_assigned && gic_cpu_base != acpi_data.cpu_phys_base) return -EINVAL; + if (processor->flags & ACPI_MADT_VGIC_IRQ_MODE) + pr_warn_once(FW_BUG "MI wrongly advertised as Edge-triggered\n"); + acpi_data.cpu_phys_base = gic_cpu_base; acpi_data.maint_irq = processor->vgic_interrupt; - acpi_data.maint_irq_mode = (processor->flags & ACPI_MADT_VGIC_IRQ_MODE) ? - ACPI_EDGE_SENSITIVE : ACPI_LEVEL_SENSITIVE; acpi_data.vctrl_base = processor->gich_base_address; acpi_data.vcpu_base = processor->gicv_base_address; @@ -1616,7 +1616,7 @@ static void __init gic_acpi_setup_kvm_info(void) vcpu_res->end = vcpu_res->start + ACPI_GICV2_VCPU_MEM_SIZE - 1; irq = acpi_register_gsi(NULL, acpi_data.maint_irq, - acpi_data.maint_irq_mode, + ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_HIGH); if (irq <= 0) return; diff --git a/drivers/irqchip/irq-imx-intmux.c b/drivers/irqchip/irq-imx-intmux.c index 47c2681d138a..de2a8f98f625 100644 --- a/drivers/irqchip/irq-imx-intmux.c +++ b/drivers/irqchip/irq-imx-intmux.c @@ -288,12 +288,16 @@ static int imx_intmux_probe(struct platform_device *pdev) static void imx_intmux_remove(struct platform_device *pdev) { struct intmux_data *data = platform_get_drvdata(pdev); - int i; + int i, ret; + + ret = pm_runtime_resume_and_get(&pdev->dev); + if (ret < 0) + dev_warn(&pdev->dev, "failed to resume device: %d\n", ret); for (i = 0; i < data->channum; i++) { /* disable all interrupt sources of this channel */ - writel_relaxed(0, data->regs + CHANIER(i)); - + if (ret >= 0) + writel_relaxed(0, data->regs + CHANIER(i)); irq_set_chained_handler_and_data(data->irqchip_data[i].irq, NULL, NULL); @@ -301,6 +305,11 @@ static void imx_intmux_remove(struct platform_device *pdev) } pm_runtime_disable(&pdev->dev); + if (ret >= 0) { + pm_runtime_put_noidle(&pdev->dev); + clk_disable_unprepare(data->ipg_clk); + pm_runtime_set_suspended(&pdev->dev); + } } #ifdef CONFIG_PM diff --git a/drivers/irqchip/irq-jcore-aic.c b/drivers/irqchip/irq-jcore-aic.c index 94c05cf974be..b4fee02997ee 100644 --- a/drivers/irqchip/irq-jcore-aic.c +++ b/drivers/irqchip/irq-jcore-aic.c @@ -37,8 +37,10 @@ static struct irq_chip jcore_aic; static void handle_jcore_irq(struct irq_desc *desc) { - if (irqd_is_per_cpu(irq_desc_get_irq_data(desc))) + if (irq_is_percpu_devid(irq_desc_get_irq(desc))) handle_percpu_devid_irq(desc); + else if (irqd_is_per_cpu(irq_desc_get_irq_data(desc))) + handle_percpu_irq(desc); else handle_simple_irq(desc); } diff --git a/drivers/irqchip/irq-lan966x-oic.c b/drivers/irqchip/irq-lan966x-oic.c index 8af08d0e4182..5122f3f1b353 100644 --- a/drivers/irqchip/irq-lan966x-oic.c +++ b/drivers/irqchip/irq-lan966x-oic.c @@ -220,7 +220,6 @@ static int lan966x_oic_probe(struct platform_device *pdev) }; struct irq_domain_info d_info = { .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, .ops = &irq_generic_chip_ops, diff --git a/drivers/irqchip/irq-loongson-eiointc.c b/drivers/irqchip/irq-loongson-eiointc.c index a9e8b481d31d..ba6e8480e383 100644 --- a/drivers/irqchip/irq-loongson-eiointc.c +++ b/drivers/irqchip/irq-loongson-eiointc.c @@ -454,7 +454,7 @@ static int __init pch_pic_parse_madt(union acpi_subtable_headers *header, const unsigned long end) { struct acpi_madt_bio_pic *pchpic_entry = (struct acpi_madt_bio_pic *)header; - unsigned int node = (pchpic_entry->address >> 44) & 0xf; + unsigned int node = pa_to_nid(pchpic_entry->address) & 0xf; struct irq_domain *parent = acpi_get_vec_parent(node, pch_group); if (parent) diff --git a/drivers/irqchip/irq-mbigen.c b/drivers/irqchip/irq-mbigen.c index 12919836dadb..0b4d78a0bbde 100644 --- a/drivers/irqchip/irq-mbigen.c +++ b/drivers/irqchip/irq-mbigen.c @@ -247,8 +247,8 @@ static int mbigen_of_create_domain(struct platform_device *pdev, #ifdef CONFIG_ACPI static const struct acpi_device_id mbigen_acpi_match[] = { - { "HISI0152", 0 }, - {} + { .id = "HISI0152" }, + { } }; MODULE_DEVICE_TABLE(acpi, mbigen_acpi_match); diff --git a/drivers/irqchip/irq-mchp-eic.c b/drivers/irqchip/irq-mchp-eic.c index be007d325e67..5250e6baff63 100644 --- a/drivers/irqchip/irq-mchp-eic.c +++ b/drivers/irqchip/irq-mchp-eic.c @@ -233,7 +233,7 @@ static int mchp_eic_probe(struct platform_device *pdev, struct device_node *pare ret = clk_prepare_enable(eic->clk); if (ret) - goto unmap; + goto clk_put; for (i = 0; i < MCHP_EIC_NIRQ; i++) { struct of_phandle_args irq; @@ -270,6 +270,8 @@ static int mchp_eic_probe(struct platform_device *pdev, struct device_node *pare clk_unprepare: clk_disable_unprepare(eic->clk); +clk_put: + clk_put(eic->clk); unmap: iounmap(eic->base); free: diff --git a/drivers/irqchip/irq-meson-gpio.c b/drivers/irqchip/irq-meson-gpio.c index 91a9c337fe6d..e0329ef5e4a2 100644 --- a/drivers/irqchip/irq-meson-gpio.c +++ b/drivers/irqchip/irq-meson-gpio.c @@ -538,7 +538,7 @@ static struct irq_chip meson_gpio_irq_chip = { #ifdef CONFIG_SMP .irq_set_affinity = irq_chip_set_affinity_parent, #endif - .flags = IRQCHIP_SET_TYPE_MASKED, + .flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_SKIP_SET_WAKE, }; static int meson_gpio_irq_domain_translate(struct irq_domain *domain, diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c index 19a57c5e2b2e..be38989d8e73 100644 --- a/drivers/irqchip/irq-mips-gic.c +++ b/drivers/irqchip/irq-mips-gic.c @@ -70,6 +70,10 @@ static int __gic_with_next_online_cpu(int prev) { unsigned int cpu; + /* Release the redirect/other region lock to the previous CPU, if any. */ + if (prev >= 0) + mips_cm_unlock_other(); + /* Discover the next online CPU */ cpu = cpumask_next(prev, cpu_online_mask); @@ -77,23 +81,12 @@ static int __gic_with_next_online_cpu(int prev) if (cpu >= nr_cpu_ids) return cpu; - /* - * Move the access lock to the next CPU's GIC local register block. - * - * Set GIC_VL_OTHER. Since the caller holds gic_lock nothing can - * clobber the written value. - */ - write_gic_vl_other(mips_cm_vp_id(cpu)); + /* Lock access to redirect/other region to the next CPU */ + mips_cm_lock_other_cpu(cpu, CM_GCR_Cx_OTHER_BLOCK_LOCAL); return cpu; } -static inline void gic_unlock_cluster(void) -{ - if (mips_cps_multicluster_cpus()) - mips_cm_unlock_other(); -} - /** * for_each_online_cpu_gic() - Iterate over online CPUs, access local registers * @cpu: An integer variable to hold the current CPU number @@ -108,7 +101,6 @@ static inline void gic_unlock_cluster(void) guard(raw_spinlock_irqsave)(gic_lock); \ for ((cpu) = __gic_with_next_online_cpu(-1); \ (cpu) < nr_cpu_ids; \ - gic_unlock_cluster(), \ (cpu) = __gic_with_next_online_cpu(cpu)) /** @@ -305,14 +297,14 @@ static void gic_ack_irq(struct irq_data *d) } } -static int gic_set_type(struct irq_data *d, unsigned int type) +static int gic_set_type_locked(struct irq_data *d, unsigned int type) { unsigned int irq, pol, trig, dual; - unsigned long flags; + + lockdep_assert_held(&gic_lock); irq = GIC_HWIRQ_TO_SHARED(d->hwirq); - raw_spin_lock_irqsave(&gic_lock, flags); switch (type & IRQ_TYPE_SENSE_MASK) { case IRQ_TYPE_EDGE_FALLING: pol = GIC_POL_FALLING_EDGE; @@ -359,11 +351,16 @@ static int gic_set_type(struct irq_data *d, unsigned int type) else irq_set_chip_handler_name_locked(d, &gic_level_irq_controller, handle_level_irq, NULL); - raw_spin_unlock_irqrestore(&gic_lock, flags); return 0; } +static int gic_set_type(struct irq_data *d, unsigned int type) +{ + guard(raw_spinlock_irqsave)(&gic_lock); + return gic_set_type_locked(d, type); +} + #ifdef CONFIG_SMP static int gic_set_affinity(struct irq_data *d, const struct cpumask *cpumask, bool force) @@ -371,6 +368,7 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *cpumask, unsigned int irq = GIC_HWIRQ_TO_SHARED(d->hwirq); unsigned int cpu, cl, old_cpu, old_cl; unsigned long flags; + bool enabled; /* * The GIC specifies that we can only route an interrupt to one VP(E), @@ -392,15 +390,20 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *cpumask, raw_spin_lock_irqsave(&gic_lock, flags); /* - * If we're moving affinity between clusters, stop routing the - * interrupt to any VP(E) in the old cluster. + * If we're moving affinity between clusters, save the interrupt's + * mask state, stop routing it to any VP(E) in the old cluster and + * disable it there so it is no longer delivered. */ if (cl != old_cl) { if (gic_irq_lock_cluster(d)) { + enabled = read_gic_redir_mask(irq); write_gic_redir_map_vp(irq, 0); + write_gic_redir_rmask(irq); mips_cm_unlock_other(); } else { + enabled = read_gic_mask(irq); write_gic_map_vp(irq, 0); + write_gic_rmask(irq); } } @@ -412,10 +415,20 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *cpumask, /* * If we're moving affinity between clusters, configure the interrupt - * trigger type in the new cluster. + * trigger type and, if it was enabled in the old cluster, enable it + * in the new one. */ - if (cl != old_cl) - gic_set_type(d, irqd_get_trigger_type(d)); + if (cl != old_cl) { + gic_set_type_locked(d, irqd_get_trigger_type(d)); + if (enabled) { + if (gic_irq_lock_cluster(d)) { + write_gic_redir_smask(irq); + mips_cm_unlock_other(); + } else { + write_gic_smask(irq); + } + } + } /* Route the interrupt to its new VP(E) */ if (gic_irq_lock_cluster(d)) { diff --git a/drivers/irqchip/irq-mst-intc.c b/drivers/irqchip/irq-mst-intc.c index b5335f6fd6d6..1070de5de8cc 100644 --- a/drivers/irqchip/irq-mst-intc.c +++ b/drivers/irqchip/irq-mst-intc.c @@ -263,6 +263,9 @@ static int __init mst_intc_of_init(struct device_node *dn, of_property_read_u32_index(dn, "mstar,irqs-map-range", 1, &irq_end)) return -EINVAL; + if (irq_end < irq_start || irq_end - irq_start >= MST_INTC_MAX_IRQS) + return -EINVAL; + cd = kzalloc_obj(*cd); if (!cd) return -ENOMEM; diff --git a/drivers/irqchip/irq-mtk-cirq.c b/drivers/irqchip/irq-mtk-cirq.c index 914d1d639fe3..d30c34ce0f56 100644 --- a/drivers/irqchip/irq-mtk-cirq.c +++ b/drivers/irqchip/irq-mtk-cirq.c @@ -247,7 +247,7 @@ static int mtk_cirq_suspend(void *data) writel_relaxed(mask, reg); } - /* set edge_only mode, record edge-triggerd interrupts */ + /* set edge_only mode, record edge-triggered interrupts */ /* enable cirq */ reg = mtk_cirq_reg(cirq_data, CIRQ_CONTROL); value = readl_relaxed(reg); diff --git a/drivers/irqchip/irq-pruss-intc.c b/drivers/irqchip/irq-pruss-intc.c index 81078d56f38d..5a3e9e5bccbe 100644 --- a/drivers/irqchip/irq-pruss-intc.c +++ b/drivers/irqchip/irq-pruss-intc.c @@ -181,7 +181,7 @@ static void pruss_intc_map(struct pruss_intc *intc, unsigned long hwirq) u8 ch, host, reg_idx; u32 val; - mutex_lock(&intc->lock); + guard(mutex)(&intc->lock); intc->event_channel[hwirq].ref_count++; @@ -206,8 +206,6 @@ static void pruss_intc_map(struct pruss_intc *intc, unsigned long hwirq) dev_dbg(dev, "mapped system_event = %lu channel = %d host = %d", hwirq, ch, host); - - mutex_unlock(&intc->lock); } /** @@ -224,7 +222,7 @@ static void pruss_intc_unmap(struct pruss_intc *intc, unsigned long hwirq) u8 ch, host, reg_idx; u32 val; - mutex_lock(&intc->lock); + guard(mutex)(&intc->lock); ch = intc->event_channel[hwirq].value; host = intc->channel_host[ch].value; @@ -251,8 +249,6 @@ static void pruss_intc_unmap(struct pruss_intc *intc, unsigned long hwirq) dev_dbg(intc->dev, "unmapped system_event = %lu channel = %d host = %d\n", hwirq, ch, host); - - mutex_unlock(&intc->lock); } static void pruss_intc_init(struct pruss_intc *intc) @@ -376,17 +372,15 @@ static int pruss_intc_validate_mapping(struct pruss_intc *intc, int event, int channel, int host) { struct device *dev = intc->dev; - int ret = 0; - mutex_lock(&intc->lock); + guard(mutex)(&intc->lock); /* check if sysevent already assigned */ if (intc->event_channel[event].ref_count > 0 && intc->event_channel[event].value != channel) { dev_err(dev, "event %d (req. ch %d) already assigned to channel %d\n", event, channel, intc->event_channel[event].value); - ret = -EBUSY; - goto unlock; + return -EBUSY; } /* check if channel already assigned */ @@ -394,16 +388,13 @@ static int pruss_intc_validate_mapping(struct pruss_intc *intc, int event, intc->channel_host[channel].value != host) { dev_err(dev, "channel %d (req. host %d) already assigned to host %d\n", channel, host, intc->channel_host[channel].value); - ret = -EBUSY; - goto unlock; + return -EBUSY; } intc->event_channel[event].value = channel; intc->channel_host[channel].value = host; -unlock: - mutex_unlock(&intc->lock); - return ret; + return 0; } static int @@ -516,24 +507,21 @@ static const char * const irq_names[MAX_NUM_HOST_IRQS] = { static int pruss_intc_probe(struct platform_device *pdev) { - const struct pruss_intc_match_data *data; struct device *dev = &pdev->dev; struct pruss_intc *intc; struct pruss_host_irq_data *host_data; int i, irq, ret; u8 max_system_events, irqs_reserved = 0; - data = of_device_get_match_data(dev); - if (!data) - return -ENODEV; - - max_system_events = data->num_system_events; - intc = devm_kzalloc(dev, sizeof(*intc), GFP_KERNEL); if (!intc) return -ENOMEM; - intc->soc_config = data; + intc->soc_config = of_device_get_match_data(dev); + if (!intc->soc_config) + return -ENODEV; + max_system_events = intc->soc_config->num_system_events; + intc->dev = dev; platform_set_drvdata(pdev, intc); @@ -553,7 +541,9 @@ static int pruss_intc_probe(struct platform_device *pdev) pruss_intc_init(intc); - mutex_init(&intc->lock); + ret = devm_mutex_init(dev, &intc->lock); + if (ret) + return ret; intc->domain = irq_domain_create_linear(dev_fwnode(dev), max_system_events, &pruss_intc_irq_domain_ops, intc); diff --git a/drivers/irqchip/irq-renesas-rzv2h.c b/drivers/irqchip/irq-renesas-rzv2h.c index 1a4703a353e9..485c3eea7632 100644 --- a/drivers/irqchip/irq-renesas-rzv2h.c +++ b/drivers/irqchip/irq-renesas-rzv2h.c @@ -420,9 +420,9 @@ static void rzv2h_clear_tint_int(struct rzv2h_icu_priv *priv, unsigned int hwirq static int rzv2h_tint_set_type(struct irq_data *d, unsigned int type) { u32 titsr, titsr_k, titsel_n, tien; + u32 titsr_cur, tssr_cur, tien_cur; struct rzv2h_icu_priv *priv; u32 tssr, tssr_k, tssel_n; - u32 titsr_cur, tssr_cur; unsigned int hwirq; u32 tint, sense; int tint_nr; @@ -477,6 +477,8 @@ static int rzv2h_tint_set_type(struct irq_data *d, unsigned int type) if (tssr_cur == tint && titsr_cur == sense) goto set_parent_type; + tien_cur = tssr & tien; + tssr &= ~(ICU_TSSR_TSSEL_MASK(tssel_n, priv->info->field_width) | tien); tssr |= ICU_TSSR_TSSEL_PREP(tint, tssel_n, priv->info->field_width); @@ -489,7 +491,7 @@ static int rzv2h_tint_set_type(struct irq_data *d, unsigned int type) rzv2h_clear_tint_int(priv, hwirq); - writel_relaxed(tssr | tien, priv->base + priv->info->t_offs + ICU_TSSR(tssr_k)); + writel_relaxed(tssr | tien_cur, priv->base + priv->info->t_offs + ICU_TSSR(tssr_k)); } set_parent_type: return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH); diff --git a/drivers/irqchip/irq-riscv-aplic-main.c b/drivers/irqchip/irq-riscv-aplic-main.c index d9afb6ae98cf..1676d64e6e4a 100644 --- a/drivers/irqchip/irq-riscv-aplic-main.c +++ b/drivers/irqchip/irq-riscv-aplic-main.c @@ -305,8 +305,8 @@ static void aplic_init_hw_irqs(struct aplic_priv *priv) #ifdef CONFIG_ACPI static const struct acpi_device_id aplic_acpi_match[] = { - { "RSCV0002", 0 }, - {} + { .id = "RSCV0002" }, + { } }; MODULE_DEVICE_TABLE(acpi, aplic_acpi_match); diff --git a/drivers/irqchip/irq-riscv-imsic-early.c b/drivers/irqchip/irq-riscv-imsic-early.c index 12efd241ce88..06726a062b75 100644 --- a/drivers/irqchip/irq-riscv-imsic-early.c +++ b/drivers/irqchip/irq-riscv-imsic-early.c @@ -67,15 +67,15 @@ static int __init imsic_ipi_domain_init(void) return 0; /* Create IMSIC IPI multiplexing */ - virq = ipi_mux_create(IMSIC_NR_IPI, imsic_ipi_send); + virq = ipi_mux_create(IPI_MAX, imsic_ipi_send); if (virq <= 0) return virq < 0 ? virq : -ENOMEM; /* Set vIRQ range */ - riscv_ipi_set_virq_range(virq, IMSIC_NR_IPI); + riscv_ipi_set_virq_range(virq, IPI_MAX); /* Announce that IMSIC is providing IPIs */ - pr_info("%pfwP: providing IPIs using interrupt %d\n", imsic->fwnode, IMSIC_IPI_ID); + pr_info("%pfwP: providing IPIs using interrupt %d\n", imsic->global.fwnode, IMSIC_IPI_ID); return 0; } diff --git a/drivers/irqchip/irq-riscv-imsic-platform.c b/drivers/irqchip/irq-riscv-imsic-platform.c index 643c8e459611..95ff76e63a69 100644 --- a/drivers/irqchip/irq-riscv-imsic-platform.c +++ b/drivers/irqchip/irq-riscv-imsic-platform.c @@ -313,40 +313,40 @@ int imsic_irqdomain_init(void) }; struct imsic_global_config *global; - if (!imsic || !imsic->fwnode) { + if (!imsic || !imsic->global.fwnode) { pr_err("early driver not probed\n"); return -ENODEV; } if (imsic->base_domain) { - pr_err("%pfwP: irq domain already created\n", imsic->fwnode); + pr_err("%pfwP: irq domain already created\n", imsic->global.fwnode); return -ENODEV; } /* Create Base IRQ domain */ - info.fwnode = imsic->fwnode, + info.fwnode = imsic->global.fwnode, imsic->base_domain = msi_create_parent_irq_domain(&info, &imsic_msi_parent_ops); if (!imsic->base_domain) { - pr_err("%pfwP: failed to create IMSIC base domain\n", imsic->fwnode); + pr_err("%pfwP: failed to create IMSIC base domain\n", imsic->global.fwnode); return -ENOMEM; } global = &imsic->global; pr_info("%pfwP: hart-index-bits: %d, guest-index-bits: %d\n", - imsic->fwnode, global->hart_index_bits, global->guest_index_bits); + imsic->global.fwnode, global->hart_index_bits, global->guest_index_bits); pr_info("%pfwP: group-index-bits: %d, group-index-shift: %d\n", - imsic->fwnode, global->group_index_bits, global->group_index_shift); + imsic->global.fwnode, global->group_index_bits, global->group_index_shift); pr_info("%pfwP: per-CPU IDs %d at base address %pa\n", - imsic->fwnode, global->nr_ids, &global->base_addr); + imsic->global.fwnode, global->nr_ids, &global->base_addr); pr_info("%pfwP: total %d interrupts available\n", - imsic->fwnode, num_possible_cpus() * (global->nr_ids - 1)); + imsic->global.fwnode, num_possible_cpus() * (global->nr_ids - 1)); return 0; } static int imsic_platform_probe_common(struct fwnode_handle *fwnode) { - if (imsic && imsic->fwnode != fwnode) { + if (imsic && imsic->global.fwnode != fwnode) { pr_err("%pfwP: fwnode mismatch\n", fwnode); return -ENODEV; } diff --git a/drivers/irqchip/irq-riscv-imsic-state.c b/drivers/irqchip/irq-riscv-imsic-state.c index b8d1bbbf42f7..01620373b43a 100644 --- a/drivers/irqchip/irq-riscv-imsic-state.c +++ b/drivers/irqchip/irq-riscv-imsic-state.c @@ -7,6 +7,7 @@ #define pr_fmt(fmt) "riscv-imsic: " fmt #include #include +#include #include #include #include @@ -739,6 +740,14 @@ static int __init imsic_parse_fwnode(struct fwnode_handle *fwnode, return -EINVAL; } + /* Group index bits must not overlap guest and HART index bits. */ + i = IMSIC_MMIO_PAGE_SHIFT + global->guest_index_bits + + global->hart_index_bits; + if (global->group_index_bits && global->group_index_shift < i) { + pr_err("%pfwP: group index shift too small\n", fwnode); + return -EINVAL; + } + /* Sanity check group index shift */ i = global->group_index_bits + global->group_index_shift - 1; if (i >= BITS_PER_LONG) { @@ -769,9 +778,9 @@ static int __init imsic_parse_fwnode(struct fwnode_handle *fwnode, return -EINVAL; } global->base_addr = res.start; - global->base_addr &= ~(BIT(global->guest_index_bits + - global->hart_index_bits + - IMSIC_MMIO_PAGE_SHIFT) - 1); + global->base_addr &= ~GENMASK(global->guest_index_bits + + global->hart_index_bits + + IMSIC_MMIO_PAGE_SHIFT - 1, 0); global->base_addr &= ~((BIT(global->group_index_bits) - 1) << global->group_index_shift); @@ -813,9 +822,9 @@ int __init imsic_setup_state(struct fwnode_handle *fwnode, void *opaque) imsic = kzalloc_obj(*imsic); if (!imsic) return -ENOMEM; - imsic->fwnode = fwnode; global = &imsic->global; + global->fwnode = fwnode; global->local = alloc_percpu(typeof(*global->local)); if (!global->local) { rc = -ENOMEM; @@ -850,9 +859,9 @@ int __init imsic_setup_state(struct fwnode_handle *fwnode, void *opaque) } base_addr = mmios[i].start; - base_addr &= ~(BIT(global->guest_index_bits + - global->hart_index_bits + - IMSIC_MMIO_PAGE_SHIFT) - 1); + base_addr &= ~GENMASK(global->guest_index_bits + + global->hart_index_bits + + IMSIC_MMIO_PAGE_SHIFT - 1, 0); base_addr &= ~((BIT(global->group_index_bits) - 1) << global->group_index_shift); if (base_addr != global->base_addr) { diff --git a/drivers/irqchip/irq-riscv-imsic-state.h b/drivers/irqchip/irq-riscv-imsic-state.h index c42ee180b305..189829b8673a 100644 --- a/drivers/irqchip/irq-riscv-imsic-state.h +++ b/drivers/irqchip/irq-riscv-imsic-state.h @@ -13,7 +13,6 @@ #include #define IMSIC_IPI_ID 1 -#define IMSIC_NR_IPI 8 struct imsic_vector { /* Fixed details of the vector */ @@ -44,9 +43,6 @@ struct imsic_local_priv { }; struct imsic_priv { - /* Device details */ - struct fwnode_handle *fwnode; - /* Global configuration common for all HARTs */ struct imsic_global_config global; diff --git a/drivers/irqchip/irq-riscv-rpmi-sysmsi.c b/drivers/irqchip/irq-riscv-rpmi-sysmsi.c index 612f3972f7af..bc80c38bb864 100644 --- a/drivers/irqchip/irq-riscv-rpmi-sysmsi.c +++ b/drivers/irqchip/irq-riscv-rpmi-sysmsi.c @@ -313,8 +313,8 @@ static const struct of_device_id rpmi_sysmsi_match[] = { }; static const struct acpi_device_id acpi_rpmi_sysmsi_match[] = { - { "RSCV0006" }, - {} + { .id = "RSCV0006" }, + { } }; MODULE_DEVICE_TABLE(acpi, acpi_rpmi_sysmsi_match); diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive-plic.c index 5b0dac104814..55d94c49f083 100644 --- a/drivers/irqchip/irq-sifive-plic.c +++ b/drivers/irqchip/irq-sifive-plic.c @@ -108,7 +108,7 @@ static int plic_irq_set_type(struct irq_data *d, unsigned int type); static void __plic_toggle(struct plic_handler *handler, int hwirq, int enable) { u32 __iomem *base = handler->enable_base; - u32 hwirq_mask = 1 << (hwirq % 32); + u32 hwirq_mask = BIT(hwirq % 32); int group = hwirq / 32; u32 value; @@ -452,7 +452,7 @@ static irq_hw_number_t cp100_get_hwirq(struct plic_handler *handler, void __iome return 0; /* - * Interrupts delievered to hardware still become pending, but only + * Interrupts delivered to hardware still become pending, but only * interrupts that are both pending and enabled can be claimed. * Clearing the enable bit for all interrupts but the first pending * one avoids a hardware bug that occurs during read from the claim @@ -538,8 +538,8 @@ static const struct of_device_id plic_match[] = { #ifdef CONFIG_ACPI static const struct acpi_device_id plic_acpi_match[] = { - { "RSCV0001", 0 }, - {} + { .id = "RSCV0001" }, + { } }; MODULE_DEVICE_TABLE(acpi, plic_acpi_match); @@ -643,13 +643,11 @@ static int plic_probe(struct fwnode_handle *fwnode) error = plic_parse_nr_irqs_and_contexts(fwnode, &nr_irqs, &nr_contexts, &gsi_base, &id); if (error) - goto fail_free_regs; + return error; priv = kzalloc_obj(*priv); - if (!priv) { - error = -ENOMEM; - goto fail_free_regs; - } + if (!priv) + return -ENOMEM; priv->fwnode = fwnode; priv->plic_quirks = plic_quirks; @@ -668,10 +666,8 @@ static int plic_probe(struct fwnode_handle *fwnode) priv->acpi_plic_id = id; priv->prio_save = bitmap_zalloc(nr_irqs, GFP_KERNEL); - if (!priv->prio_save) { - error = -ENOMEM; - goto fail_free_priv; - } + if (!priv->prio_save) + return -ENOMEM; for (i = 0; i < nr_contexts; i++) { error = plic_parse_context_parent(fwnode, i, &parent_hwirq, &cpu, @@ -735,10 +731,8 @@ static int plic_probe(struct fwnode_handle *fwnode) handler->enable_save = kcalloc(priv->irq_groups, sizeof(*handler->enable_save), GFP_KERNEL); - if (!handler->enable_save) { - error = -ENOMEM; - goto fail_cleanup_contexts; - } + if (!handler->enable_save) + return -ENOMEM; done: for_each_device_irq(hwirq, priv) { plic_toggle(handler, hwirq, 0); @@ -748,10 +742,8 @@ static int plic_probe(struct fwnode_handle *fwnode) } priv->irqdomain = irq_domain_create_linear(fwnode, nr_irqs, &plic_irqdomain_ops, priv); - if (WARN_ON(!priv->irqdomain)) { - error = -ENOMEM; - goto fail_cleanup_contexts; - } + if (!priv->irqdomain) + return -ENOMEM; /* * We can have multiple PLIC instances so setup global state @@ -799,33 +791,13 @@ static int plic_probe(struct fwnode_handle *fwnode) pr_info("%pfwP: mapped %d interrupts with %d handlers for %d contexts.\n", fwnode, nr_irqs, nr_handlers, nr_contexts); return 0; - -fail_cleanup_contexts: - for (i = 0; i < nr_contexts; i++) { - if (plic_parse_context_parent(fwnode, i, &parent_hwirq, &cpu, priv->acpi_plic_id)) - continue; - if (parent_hwirq != RV_IRQ_EXT || cpu < 0) - continue; - - handler = per_cpu_ptr(&plic_handlers, cpu); - handler->present = false; - handler->hart_base = NULL; - handler->enable_base = NULL; - kfree(handler->enable_save); - handler->enable_save = NULL; - handler->priv = NULL; - } - bitmap_free(priv->prio_save); -fail_free_priv: - kfree(priv); -fail_free_regs: - iounmap(regs); - return error; } static int plic_platform_probe(struct platform_device *pdev) { - return plic_probe(pdev->dev.fwnode); + if (plic_probe(pdev->dev.fwnode)) + panic("%pfwP: Failed to initialize\n", pdev->dev.fwnode); + return 0; } static struct platform_driver plic_driver = { @@ -842,7 +814,9 @@ builtin_platform_driver(plic_driver); static int __init plic_early_probe(struct device_node *node, struct device_node *parent) { - return plic_probe(&node->fwnode); + if (plic_probe(&node->fwnode)) + panic("%pfwP: Failed to initialize\n", &node->fwnode); + return 0; } IRQCHIP_DECLARE(riscv, "allwinner,sun20i-d1-plic", plic_early_probe); diff --git a/drivers/irqchip/irq-sni-exiu.c b/drivers/irqchip/irq-sni-exiu.c index bfd2adedf8f3..108d7f2e59c4 100644 --- a/drivers/irqchip/irq-sni-exiu.c +++ b/drivers/irqchip/irq-sni-exiu.c @@ -307,8 +307,8 @@ static int exiu_acpi_probe(struct platform_device *pdev) } static const struct acpi_device_id exiu_acpi_ids[] = { - { "SCX0008" }, - { /* sentinel */ } + { .id = "SCX0008" }, + { } }; MODULE_DEVICE_TABLE(acpi, exiu_acpi_ids); diff --git a/drivers/irqchip/irq-st.c b/drivers/irqchip/irq-st.c index de71bb350d57..fef0521831aa 100644 --- a/drivers/irqchip/irq-st.c +++ b/drivers/irqchip/irq-st.c @@ -157,7 +157,7 @@ static int st_irq_syscfg_probe(struct platform_device *pdev) return st_irq_syscfg_enable(pdev); } -static int __maybe_unused st_irq_syscfg_resume(struct device *dev) +static int st_irq_syscfg_resume(struct device *dev) { struct st_irq_syscfg *ddata = dev_get_drvdata(dev); @@ -165,12 +165,12 @@ static int __maybe_unused st_irq_syscfg_resume(struct device *dev) ST_A9_IRQ_MASK, ddata->config); } -static SIMPLE_DEV_PM_OPS(st_irq_syscfg_pm_ops, NULL, st_irq_syscfg_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(st_irq_syscfg_pm_ops, NULL, st_irq_syscfg_resume); static struct platform_driver st_irq_syscfg_driver = { .driver = { .name = "st_irq_syscfg", - .pm = &st_irq_syscfg_pm_ops, + .pm = pm_sleep_ptr(&st_irq_syscfg_pm_ops), .of_match_table = st_irq_syscfg_match, }, .probe = st_irq_syscfg_probe, diff --git a/drivers/irqchip/irq-vic.c b/drivers/irqchip/irq-vic.c index e38104c5064e..607e3284f700 100644 --- a/drivers/irqchip/irq-vic.c +++ b/drivers/irqchip/irq-vic.c @@ -478,7 +478,7 @@ static void __init __vic_init(void __iomem *base, int parent_irq, int irq_start, /** * vic_init() - initialise a vectored interrupt controller * @base: iomem base address - * @irq_start: starting interrupt number, must be muliple of 32 + * @irq_start: starting interrupt number, must be multiple of 32 * @vic_sources: bitmask of interrupt sources to allow * @resume_sources: bitmask of interrupt sources to allow for resume */ diff --git a/drivers/irqchip/irq-xilinx-smmu-csr.c b/drivers/irqchip/irq-xilinx-smmu-csr.c new file mode 100644 index 000000000000..9bb625598107 --- /dev/null +++ b/drivers/irqchip/irq-xilinx-smmu-csr.c @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Xilinx Versal Net SMMU CSR interrupt controller driver + * + * Copyright (C) 2026 Advanced Micro Devices, Inc. + * + * Vendor CSR block that gates the ARM SMMUv3 wired interrupts. Chains + * off the single GIC parent line and demultiplexes the CSR status into + * three children (eventq, gerror, priq), each enabled/acked + * via SMMU_CSR_IER/IDR/ISR that the ARM SMMUv3 driver requests individually. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Interrupt types */ +#define SMMU_INTR_EVENT BIT(0) +#define SMMU_INTR_GLOBAL BIT(2) +#define SMMU_INTR_PRI BIT(3) + +/* Mask for the SMMU interrupts this driver demultiplexes */ +#define SMMU_INTR_ALL (SMMU_INTR_EVENT | SMMU_INTR_GLOBAL | SMMU_INTR_PRI) + +/* Every interrupt source the CSR block implements (incl. unsupported CMD_SYNC) */ +#define SMMU_INTR_HW_ALL GENMASK(3, 0) + +#define SMMU_CSR_ISR 0x24 /* Interrupt Status (raw, W1C) */ +#define SMMU_CSR_MASK 0x28 /* Interrupt Mask (RO): 1 = masked */ +#define SMMU_CSR_IER 0x2c /* Interrupt Enable (W1 to unmask) */ +#define SMMU_CSR_IDR 0x30 /* Interrupt Disable (W1 to mask) */ + +/** + * struct xilinx_smmu_csr - SMMU CSR interrupt controller context + * @base: MMIO base address of the CSR registers + * @domain: Interrupt domain for the child interrupts + * @parent_irq: parent (GIC) interrupt this block is chained to + * @lock: protects the SMMU_CSR_IER/IDR/ISR read and writes + */ +struct xilinx_smmu_csr { + void __iomem *base; + struct irq_domain *domain; + int parent_irq; + raw_spinlock_t lock; +}; + +enum xilinx_smmu_csr_irq { + SMMU_CSR_IRQ_EVENTQ = 0, + SMMU_CSR_IRQ_GERROR = 2, + SMMU_CSR_IRQ_PRIQ = 3, + SMMU_CSR_IRQ_NR, +}; + +static u32 xilinx_smmu_csr_hwirq_mask(irq_hw_number_t hwirq) +{ + if (hwirq >= SMMU_CSR_IRQ_NR) + return 0; + + return BIT(hwirq) & SMMU_INTR_ALL; +} + +static void xilinx_smmu_csr_irq_mask(struct irq_data *d) +{ + struct xilinx_smmu_csr *csr = irq_data_get_irq_chip_data(d); + u32 mask = xilinx_smmu_csr_hwirq_mask(d->hwirq); + + if (!mask) + return; + + guard(raw_spinlock)(&csr->lock); + writel(mask, csr->base + SMMU_CSR_IDR); +} + +static void xilinx_smmu_csr_irq_unmask(struct irq_data *d) +{ + struct xilinx_smmu_csr *csr = irq_data_get_irq_chip_data(d); + u32 mask = xilinx_smmu_csr_hwirq_mask(d->hwirq); + + if (!mask) + return; + + guard(raw_spinlock)(&csr->lock); + writel(mask, csr->base + SMMU_CSR_IER); +} + +static void xilinx_smmu_csr_irq_eoi(struct irq_data *d) +{ + struct xilinx_smmu_csr *csr = irq_data_get_irq_chip_data(d); + u32 mask = xilinx_smmu_csr_hwirq_mask(d->hwirq); + + if (!mask) + return; + + guard(raw_spinlock)(&csr->lock); + writel(mask, csr->base + SMMU_CSR_ISR); +} + +static struct irq_chip xilinx_smmu_csr_chip = { + .name = "xlnx-smmu-csr", + .irq_mask = xilinx_smmu_csr_irq_mask, + .irq_unmask = xilinx_smmu_csr_irq_unmask, + .irq_eoi = xilinx_smmu_csr_irq_eoi, + /* Defer sticky ISR clear until the threaded SMMU handler drains it. */ + .flags = IRQCHIP_EOI_THREADED, +}; + +static void xilinx_smmu_csr_irq_handler(struct irq_desc *desc) +{ + struct xilinx_smmu_csr *csr = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + u32 status, mask, active, pending, spurious; + + chained_irq_enter(chip, desc); + + /* + * SMMU_CSR_ISR latches raw, pre-mask status, so gate it with the mask + * register to only act on sources that are actually enabled. + */ + status = readl(csr->base + SMMU_CSR_ISR); + mask = readl(csr->base + SMMU_CSR_MASK); + active = status & ~mask; + + /* + * Defend against any enabled source this driver does not demultiplex + * (e.g. CMD_SYNC). It would never be acked and would livelock the + * parent line, so mask and clear it here. + */ + spurious = active & ~SMMU_INTR_ALL; + if (spurious) { + writel(spurious, csr->base + SMMU_CSR_IDR); + writel(spurious, csr->base + SMMU_CSR_ISR); + } + + pending = active & SMMU_INTR_ALL; + + while (pending) { + irq_hw_number_t hwirq = __ffs(pending); + + if (generic_handle_domain_irq(csr->domain, hwirq)) { + /* + * No mapped consumer: mask and clear so an unexpected + * source cannot spin the parent line. + */ + writel(BIT(hwirq), csr->base + SMMU_CSR_IDR); + writel(BIT(hwirq), csr->base + SMMU_CSR_ISR); + } + + pending &= ~BIT(hwirq); + } + + chained_irq_exit(chip, desc); +} + +static int xilinx_smmu_csr_domain_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hwirq) +{ + struct xilinx_smmu_csr *csr = d->host_data; + + if (!xilinx_smmu_csr_hwirq_mask(hwirq)) + return -EINVAL; + + irq_set_chip_and_handler(virq, &xilinx_smmu_csr_chip, handle_fasteoi_irq); + irq_set_chip_data(virq, csr); + irq_set_status_flags(virq, IRQ_LEVEL); + + return 0; +} + +static const struct irq_domain_ops xilinx_smmu_csr_domain_ops = { + .map = xilinx_smmu_csr_domain_map, + .xlate = irq_domain_xlate_onecell, +}; + +static int __init xilinx_smmu_csr_init(struct device_node *node, struct device_node *parent) +{ + struct xilinx_smmu_csr *csr; + int ret; + + if (WARN_ON_ONCE(!parent)) + return -EINVAL; + + if (irq_find_matching_fwnode(of_fwnode_handle(node), DOMAIN_BUS_ANY)) + return -ENODEV; + + csr = kzalloc(sizeof(*csr), GFP_KERNEL); + if (!csr) + return -ENOMEM; + + raw_spin_lock_init(&csr->lock); + + csr->base = of_iomap(node, 0); + if (!csr->base) { + ret = -ENOMEM; + goto free; + } + + /* + * Start from a known state: mask and clear *every* source the block + * implements, including CMD_SYNC (bit 1) which this driver does not + * service. The CSR ORs (status & ~mask) onto a single parent line, so + * a source left unmasked and latched by a prior boot stage (firmware, + * kexec) would assert the line forever with no one to ack it. Masking + * the whole set here makes that impossible; the ARM SMMUv3 driver + * unmasks the sources it needs via the child irq_unmask() callbacks. + */ + writel(SMMU_INTR_HW_ALL, csr->base + SMMU_CSR_IDR); + writel(SMMU_INTR_HW_ALL, csr->base + SMMU_CSR_ISR); + + csr->domain = irq_domain_create_linear(of_fwnode_handle(node), SMMU_CSR_IRQ_NR, + &xilinx_smmu_csr_domain_ops, csr); + if (!csr->domain) { + pr_err("%pOF: failed to create irq domain\n", node); + ret = -ENOMEM; + goto unmap; + } + + csr->parent_irq = irq_of_parse_and_map(node, 0); + if (!csr->parent_irq) { + pr_err("%pOF: failed to map parent irq\n", node); + ret = -EINVAL; + goto remove_domain; + } + + irq_set_chained_handler_and_data(csr->parent_irq, xilinx_smmu_csr_irq_handler, csr); + + pr_debug("%pOF: Xilinx SMMU CSR interrupt controller registered\n", node); + + return 0; + +remove_domain: + irq_domain_remove(csr->domain); +unmap: + iounmap(csr->base); +free: + kfree(csr); + return ret; +} + +IRQCHIP_DECLARE(xilinx_smmu_csr, "xlnx,versal-net-smmu-csr", xilinx_smmu_csr_init); diff --git a/drivers/irqchip/qcom-irq-combiner.c b/drivers/irqchip/qcom-irq-combiner.c index 09819007d08e..6d83e1e4a7ea 100644 --- a/drivers/irqchip/qcom-irq-combiner.c +++ b/drivers/irqchip/qcom-irq-combiner.c @@ -262,7 +262,7 @@ static int combiner_probe(struct platform_device *pdev) } static const struct acpi_device_id qcom_irq_combiner_ids[] = { - { "QCOM80B1", }, + { .id = "QCOM80B1" }, { } }; diff --git a/drivers/irqchip/qcom-pdc.c b/drivers/irqchip/qcom-pdc.c index ce6d80c7f17a..29025a212ece 100644 --- a/drivers/irqchip/qcom-pdc.c +++ b/drivers/irqchip/qcom-pdc.c @@ -715,7 +715,10 @@ static int qcom_pdc_probe(struct platform_device *pdev, struct device_node *pare } pdc->x1e_quirk = true; + } + if (of_device_is_compatible(node, "qcom,x1e80100-pdc") || + of_device_is_compatible(node, "qcom,x1p42100-pdc")) { if (!qcom_scm_is_available()) return -EPROBE_DEFER; diff --git a/drivers/soc/fsl/qe/qe_ports_ic.c b/drivers/soc/fsl/qe/qe_ports_ic.c index 7375f92f528b..fb3b92039547 100644 --- a/drivers/soc/fsl/qe/qe_ports_ic.c +++ b/drivers/soc/fsl/qe/qe_ports_ic.c @@ -140,7 +140,6 @@ static int qepic_probe(struct platform_device *pdev) }; struct irq_domain_info d_info = { .fwnode = of_fwnode_handle(pdev->dev.of_node), - .domain_flags = IRQ_DOMAIN_FLAG_DESTROY_GC, .size = 32, .hwirq_max = 32, .ops = &irq_generic_chip_ops, diff --git a/include/linux/irqchip/riscv-imsic.h b/include/linux/irqchip/riscv-imsic.h index 61af3a5bea09..b7be1aad7774 100644 --- a/include/linux/irqchip/riscv-imsic.h +++ b/include/linux/irqchip/riscv-imsic.h @@ -46,6 +46,9 @@ struct imsic_local_config { }; struct imsic_global_config { + /* Device details */ + struct fwnode_handle *fwnode; + /* * MSI Target Address Scheme * diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 57c819da30c2..4fdcb6df5306 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -344,6 +344,7 @@ static struct irq_domain *__irq_domain_instantiate(const struct irq_domain_info err = irq_domain_alloc_generic_chips(domain, info->dgc_info); if (err) goto err_domain_free; + domain->flags |= IRQ_DOMAIN_FLAG_DESTROY_GC; } if (info->init) {