Updates for interrupt chip drivers:

- Remove the futile attempts to cleanup resources when the probing of the
     main interrupt chip fails for SiFIVE/PLIC devices. If that probe fails
     the system will stall immediately afterwards in the boot process
     without reaching a state where the error could be diagnosed. The lack
     of resource cleanup led to a frenzy of AI detected/assisted "fixes"
     which just make the code pretend to be correct for no value.
 
     Remove the existing half baked cleanup and immediately panic the
     machine when the probe fails. That's far more useful than letting it
     proceed and stall out a millisecond later with no information about the
     root cause.
 
   - A series of fixes for the ARM GIC-V3 driver addressing a potential
     double free, a bitmask overrun on 32-bit ARM and a OF node leak.
 
   - Automatically set the IRQ_DOMAIN_FLAG_DESTROY_GC when a interrupt
     domain is created that also allocates the generic interrupt chip during
     domain creation. That removes the requirement for manually setting the
     flag in the domain creation info structure when it contains a valid
     entry for allocating the generic interrupt chip. Remove the therefore
     redundant setters of IRQ_DOMAIN_FLAG_DESTROY_GC in the existing
     drivers.
 
   - Add support for Annapurna Labs FIC version 2 and 3 and implement
     affinity control for the demultiplexed interrupts by utilizing the
     recently merged interrupt redirection infrastructure.
 
   - Convert the ACPI device ID structures to named initializers
 
   - Consolidate the IPI related constants for RISC-V interrupt controllers
 
   - Add support for Qualcomm Purwa SoC interrupt chip and consolidate the
     workaround for the previous version in Hamoa SoCs.
 
   - The usual small fixes and improvements all over the place
 -----BEGIN PGP SIGNATURE-----
 
 iQJEBAABCgAuFiEEQp8+kY+LLUocC4bMphj1TA10mKEFAmrX0aEQHHRnbHhAa2Vy
 bmVsLm9yZwAKCRCmGPVMDXSYoatNEACU5naAkM1k+hTaPXSRcd/JpizpVrM5jpM5
 tOGe5ml6h9Mda+9uFSwR5rOF5kHXofZh70E97zR7C4jD7JNcuXPqgFg202mKfcjg
 ZWcuYKOR5DaAmiNgpXYPyK9UpXQ9YOPwE7MMpv7sv5yfYDVWFzVEOSb5F294D+gK
 vuQLY9jeysDWNRSuXD9Zo8pBoXJZBCMvTWrJJO4fdh8ZxIWJJv35nRoAgYs8ZlWq
 hF1om+Ei8KbftW9ox/ZJ+nomM6UG+H6iw8+7jZ/mQ9kadRExCNNoFF2NDfH/XFX2
 +o+2HVmXW5KTCSmy28aPLBTZojeAoEGvqI5IXu8bKYR6IfYrzdZIe+9UuabPg0lO
 NWOFj1wGbUD84hoo0gI9f1MsTORdNBqccG132MvDKzOyMYQ8cbOEsk44QTjLC+kT
 A46iM1eFC43g2YWpl9qZRKmq+PomCwMwBmjCKNPzG1a24iUzeDdGSs+Es2tNzlQt
 MVPXGHzgBfXb1thEP/28nBpEnAJ6XyXaXvIvp0GcSVdbTmT3MrkEOb0YmI1QUyot
 FRM9WVpy9X0XNo4yqxKAFGVlmGeR9XKMYgU3bgaZ7CgZMe507MAjTGU44mk+LjKa
 Ndyl7ztYrrcf3GMoe1IIbfKI6bcUZ588d6VPLjH8BKCd2ZqRDfnIPNX+oIpoomd3
 FxacuEXq4Q==
 =ma3a
 -----END PGP SIGNATURE-----

Merge tag 'irq-drivers-2026-10-20' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull irq chip driver updates from Thomas Gleixner:
 "Updates for interrupt chip drivers:

   - Remove the futile attempts to cleanup resources when the probing of
     the main interrupt chip fails for SiFIVE/PLIC devices. If that
     probe fails the system will stall immediately afterwards in the
     boot process without reaching a state where the error could be
     diagnosed. The lack of resource cleanup led to a frenzy of AI
     detected/assisted "fixes" which just make the code pretend to be
     correct for no value.

     Remove the existing half baked cleanup and immediately panic the
     machine when the probe fails. That's far more useful than letting
     it proceed and stall out a millisecond later with no information
     about the root cause.

   - A series of fixes for the ARM GIC-V3 driver addressing a potential
     double free, a bitmask overrun on 32-bit ARM and a OF node leak.

   - Automatically set the IRQ_DOMAIN_FLAG_DESTROY_GC when a interrupt
     domain is created that also allocates the generic interrupt chip
     during domain creation. That removes the requirement for manually
     setting the flag in the domain creation info structure when it
     contains a valid entry for allocating the generic interrupt chip.
     Remove the therefore redundant setters of
     IRQ_DOMAIN_FLAG_DESTROY_GC in the existing drivers.

   - Add support for Annapurna Labs FIC version 2 and 3 and implement
     affinity control for the demultiplexed interrupts by utilizing the
     recently merged interrupt redirection infrastructure.

   - Convert the ACPI device ID structures to named initializers

   - Consolidate the IPI related constants for RISC-V interrupt
     controllers

   - Add support for Qualcomm Purwa SoC interrupt chip and consolidate
     the workaround for the previous version in Hamoa SoCs.

   - The usual small fixes and improvements all over the place"

* tag 'irq-drivers-2026-10-20' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (61 commits)
  irqchip/al-fic: Support per-child affinity via interrupt redirection
  irqchip/al-fic: Add support for FIC v3
  irqchip/al-fic: Support error and fatal outputs and FIC v2
  dt-bindings: interrupt-controller: amazon,al-fic: Add mask selection
  irqchip/al-fic: Switch to shared parent interrupt
  irqchip/al-fic: Keep the device_node instead of a cached name string
  irqchip/al-fic: Use %pOF and raise init log level
  irqchip/al-fic: Use %pe to print init error
  irqchip/al-fic: Fix coding style issues
  irqchip: Use named initializers for acpi_device_id
  irqchip/gic-v5: Fix standalone IRSes OF probe
  dt-bindings: interrupt-controller: gicv5: Relax IRS properties constraints
  irqchip/renesas-rzv2h: Preserve TIEN state in rzv2h_tint_set_type()
  irqchip/loongson-eiointc: Use pa_to_nid() rather than hard-code
  irqchip/riscv-imsic: Expose the fwnode via the global config
  irqchip/meson-gpio: Allow the GPIO interrupts to wake the system
  irqchip/gic-v3: Ensure change to DS is visible
  irqchip/mips-gic: Transfer interrupt mask state across clusters
  irqchip/mips-gic: Fix recursive acquisition of gic_lock in gic_set_affinity()
  irqchip/mips-gic: Fix unbalanced cm_core_lock in for_each_online_cpu_gic()
  ...
This commit is contained in:
Linus Torvalds 2026-10-21 11:16:50 -10:00
commit 2e973d4882
55 changed files with 798 additions and 268 deletions

View File

@ -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 |

View File

@ -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 = <GIC_SPI 0x0 IRQ_TYPE_LEVEL_HIGH>;
amazon,al-fic-mask = "error";
};

View File

@ -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

View File

@ -58,6 +58,7 @@ properties:
- qcom,sm8650-pdc
- qcom,sm8750-pdc
- qcom,x1e80100-pdc
- qcom,x1p42100-pdc
- const: qcom,pdc
reg:

View File

@ -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 <anirudha.sarangi@amd.com>
- Tushar Nimkar <tushar.nimkar@amd.com>
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 <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interrupt-controller/irq.h>
interrupt-controller@eca10000 {
compatible = "xlnx,versal-net-smmu-csr";
reg = <0xeca10000 0x1000>;
interrupt-controller;
#interrupt-cells = <1>;
interrupt-parent = <&gic>;
interrupts = <GIC_SPI 169 IRQ_TYPE_LEVEL_HIGH>;
};

View File

@ -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 <anirudha.sarangi@amd.com>
M: Tushar Nimkar <tushar.nimkar@amd.com>
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 <mounika.botcha@amd.com>
M: Harsh Jain <h.jain@amd.com>

View File

@ -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

View File

@ -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,

View File

@ -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>;

View File

@ -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

View File

@ -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 <linux/jump_label.h>

View File

@ -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");
/*

View File

@ -28,18 +28,6 @@
#include <asm/cacheflush.h>
#include <asm/cpu_ops.h>
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",

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -4,9 +4,9 @@
*/
#include <linux/bitfield.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqchip.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/of.h>
@ -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;
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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();

View File

@ -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;

View File

@ -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

View File

@ -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);
}

View File

@ -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,

View File

@ -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)

View File

@ -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);

View File

@ -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:

View File

@ -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,

View File

@ -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)) {

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -7,6 +7,7 @@
#define pr_fmt(fmt) "riscv-imsic: " fmt
#include <linux/acpi.h>
#include <linux/cpu.h>
#include <linux/bits.h>
#include <linux/bitmap.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
@ -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) {

View File

@ -13,7 +13,6 @@
#include <linux/timer.h>
#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;

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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,

View File

@ -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
*/

View File

@ -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 <linux/bitops.h>
#include <linux/io.h>
#include <linux/irqchip.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
/* 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);

View File

@ -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" },
{ }
};

View File

@ -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;

View File

@ -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,

View File

@ -46,6 +46,9 @@ struct imsic_local_config {
};
struct imsic_global_config {
/* Device details */
struct fwnode_handle *fwnode;
/*
* MSI Target Address Scheme
*

View File

@ -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) {