[GIT pull] irq/drivers for v7.1-rc1
From: Thomas Gleixner
Date: Sun Apr 12 2026 - 13:46:41 EST
Linus,
please pull the latest irq/drivers branch from:
git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git irq-drivers-2026-04-12
up to: 1fac04a0a473: irqchip/irq-pic32-evic: Add __maybe_unused for board_bind_eic_interrupt in COMPILE_TEST
Updates for the interrupt chip driver subsystem:
- A large refactoring for the Renesas RZV2H driver to add new interrupt
types cleanly.
- A large refactoring for the Renesas RZG2L driver to add support the new
RZ/G3L variant.
- Add support for the new NXP S32N79 chip in the IMX irq-steer driver.
- Add support for the Apple AICv3 variant
- Enhance the Loongson PCH LPC driver so it can be used on MIPS with
device tree firmware
- Allow the PIC32 EVIC driver to be built independent of MIPS in compile
tests.
- The usual small fixes and enhancements all over the place
Thanks,
tglx
------------------>
Biju Das (19):
dt-bindings: interrupt-controller: renesas,rzg2l-irqc: Use pattern for interrupt-names
dt-bindings: interrupt-controller: renesas,rzg2l-irqc: Document RZ/G3L SoC
irqchip/renesas-rzg2l: Fix error path in rzg2l_irqc_common_probe()
irqchip/renesas-rzg2l: Drop redundant IRQC_TINT_START check in rzg2l_irqc_alloc()
irqchip/renesas-rzg2l: Replace single irq_chip with per-region irq_chip instances
irqchip/renesas-rzg2l: Split EOI handler into separate IRQ and TINT functions
irqchip/renesas-rzg2l: Split set_type handler into separate IRQ and TINT functions
irqchip/renesas-rzg2l: Replace rzg2l_irqc_irq_{enable,disable} with TINT-specific handlers
irqchip/renesas-rzg2l: Split rzfive_tint_irq_endisable() into separate IRQ and TINT helpers
irqchip/renesas-rzg2l: Split rzfive_irqc_{mask,unmask} into separate IRQ and TINT handlers
irqchip/renesas-rzg2l: Dynamically allocate fwspec array
irqchip/renesas-rzg2l: Drop IRQC_NUM_IRQ macro
irqchip/renesas-rzg2l: Drop IRQC_TINT_START macro
irqchip/renesas-rzg2l: Drop IRQC_IRQ_COUNT macro
irqchip/renesas-rzg2l: Add RZ/G3L support
irqchip/renesas-rzg2l: Add shared interrupt support
irqchip/renesas-rzg2l: Replace raw_spin_{lock,unlock} with guard() in rzg2l_irq_set_type()
irqchip/renesas-rzg2l: Clear the shared interrupt bit in rzg2l_irqc_free()
irqchip/renesas-rzg2l: Add NMI support
Brian Masney (6):
irqchip/irq-pic32-evic: Address warning related to wrong printf() formatter
irqchip/irq-pic32-evic: Don't define plat_irq_dispatch() for !MIPS builds
irqchip/irq-pic32-evic: Define board_bind_eic_interrupt for !MIPS builds
irqchip/irq-pic32-evic: Only include asm headers when compiling for MIPS
irqchip/irq-pic32-evic: Allow driver to be compiled with COMPILE_TEST
irqchip/irq-pic32-evic: Add __maybe_unused for board_bind_eic_interrupt in COMPILE_TEST
Ciprian Marian Costea (1):
irqchip/imx-irqsteer: Add NXP S32N79 support
Geert Uytterhoeven (4):
irqchip/gic-v3: Print a warning for out-of-range interrupt numbers
irqchip/renesas-rzv2h: Kill swint_idx[]
irqchip/renesas-rzv2h: Kill swint_names[]
irqchip/renesas-rzv2h: Kill icu_err string
Icenowy Zheng (6):
MIPS: loongson64: Override arch_dynirq_lower_bound to reserve LPC IRQs
LoongArch: Override arch_dynirq_lower_bound to reserve LPC IRQs
dt-bindings: interrupt-controller: Add LS7A PCH LPC
irqchip/loongson-pch-lpc: Extract non-ACPI-related code from ACPI init
irqchip/loongson-pch-lpc: Add OF init code
irqchip/loongson-pch-lpc: Enable building on MIPS Loongson64
Janne Grunau (2):
dt-bindings: interrupt-controller: apple,aic2: Add AICv3
irqchip/apple-aic: Add support for "apple,t8122-aic3"
Lad Prabhakar (7):
irqchip/renesas-rzv2h: Use local node pointer
irqchip/renesas-rzv2h: Use local device pointer in ICU probe
irqchip/renesas-rzv2h: Switch to using dev_err_probe()
irqchip/renesas-rzv2h: Clarify IRQ range definitions and tighten TINT validation
irqchip/renesas-rzv2h: Replace single irq_chip with per-region irq_chip instances
irqchip/renesas-rzv2h: Add CA55 software interrupt support
irqchip/renesas-rzv2h: Handle ICU error IRQ and add SWPE trigger
Philipp Hahn (1):
irqchip: Use IS_ERR_OR_NULL() instead of NULL and IS_ERR() checks
.../bindings/interrupt-controller/apple,aic2.yaml | 30 +-
.../interrupt-controller/loongson,pch-lpc.yaml | 52 ++
.../interrupt-controller/renesas,rzg2l-irqc.yaml | 157 ++----
arch/loongarch/kernel/irq.c | 6 +
arch/mips/loongson64/init.c | 6 +
arch/mips/pic32/Kconfig | 1 -
drivers/irqchip/Kconfig | 12 +-
drivers/irqchip/irq-apple-aic.c | 24 +-
drivers/irqchip/irq-gic-v3.c | 10 +-
drivers/irqchip/irq-imx-irqsteer.c | 53 +-
drivers/irqchip/irq-loongson-pch-lpc.c | 92 +++-
drivers/irqchip/irq-mvebu-odmi.c | 2 +-
drivers/irqchip/irq-pic32-evic.c | 8 +-
drivers/irqchip/irq-renesas-rzg2l.c | 576 +++++++++++++++++----
drivers/irqchip/irq-renesas-rzv2h.c | 467 +++++++++++++----
15 files changed, 1128 insertions(+), 368 deletions(-)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/loongson,pch-lpc.yaml
diff --git a/Documentation/devicetree/bindings/interrupt-controller/apple,aic2.yaml b/Documentation/devicetree/bindings/interrupt-controller/apple,aic2.yaml
index ee5a0dfff437..d0d9a90e96e7 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/apple,aic2.yaml
+++ b/Documentation/devicetree/bindings/interrupt-controller/apple,aic2.yaml
@@ -4,10 +4,10 @@
$id: http://devicetree.org/schemas/interrupt-controller/apple,aic2.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: Apple Interrupt Controller 2
+title: Apple Interrupt Controller 2 and 3
maintainers:
- - Hector Martin <marcan@xxxxxxxxx>
+ - Janne Grunau <j@xxxxxxxxxx>
description: |
The Apple Interrupt Controller 2 is a simple interrupt controller present on
@@ -28,14 +28,24 @@ description: |
which do not go through a discrete interrupt controller. It also handles
FIQ-based Fast IPIs.
+ The Apple Interrupt Controller 3 is in its base functionality very similar to
+ the Apple Interrupt Controller 2 and uses the same device tree bindings. It is
+ found on Apple ARM SoCs platforms starting with t8122 (M3).
+
properties:
compatible:
- items:
- - enum:
- - apple,t8112-aic
- - apple,t6000-aic
- - apple,t6020-aic
- - const: apple,aic2
+ oneOf:
+ - items:
+ - enum:
+ - apple,t6000-aic
+ - apple,t6020-aic
+ - apple,t8112-aic
+ - const: apple,aic2
+ - items:
+ - enum:
+ - apple,t6030-aic3
+ - const: apple,t8122-aic3
+ - const: apple,t8122-aic3
interrupt-controller: true
@@ -117,7 +127,9 @@ allOf:
properties:
compatible:
contains:
- const: apple,t8112-aic
+ enum:
+ - apple,t8112-aic
+ - apple,t8122-aic3
then:
properties:
'#interrupt-cells':
diff --git a/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-lpc.yaml b/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-lpc.yaml
new file mode 100644
index 000000000000..ff2a425b6f0b
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/loongson,pch-lpc.yaml
@@ -0,0 +1,52 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/interrupt-controller/loongson,pch-lpc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Loongson PCH LPC Controller
+
+maintainers:
+ - Jiaxun Yang <jiaxun.yang@xxxxxxxxxxx>
+
+description:
+ This interrupt controller is found in the Loongson LS7A family of PCH for
+ accepting interrupts sent by LPC-connected peripherals and signalling PIC
+ via a single interrupt line when interrupts are available.
+
+properties:
+ compatible:
+ const: loongson,ls7a-lpc
+
+ reg:
+ maxItems: 1
+
+ interrupt-controller: true
+
+ interrupts:
+ maxItems: 1
+
+ '#interrupt-cells':
+ const: 2
+
+required:
+ - compatible
+ - reg
+ - interrupt-controller
+ - interrupts
+ - '#interrupt-cells'
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+ lpc: interrupt-controller@10002000 {
+ compatible = "loongson,ls7a-lpc";
+ reg = <0x10002000 0x400>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ interrupt-parent = <&pic>;
+ interrupts = <19 IRQ_TYPE_LEVEL_HIGH>;
+ };
+...
diff --git a/Documentation/devicetree/bindings/interrupt-controller/renesas,rzg2l-irqc.yaml b/Documentation/devicetree/bindings/interrupt-controller/renesas,rzg2l-irqc.yaml
index 44b6ae5fc802..3a221e1800a0 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/renesas,rzg2l-irqc.yaml
+++ b/Documentation/devicetree/bindings/interrupt-controller/renesas,rzg2l-irqc.yaml
@@ -30,7 +30,9 @@ properties:
- renesas,r9a08g045-irqc # RZ/G3S
- const: renesas,rzg2l-irqc
- - const: renesas,r9a07g043f-irqc # RZ/Five
+ - enum:
+ - renesas,r9a07g043f-irqc # RZ/Five
+ - renesas,r9a08g046-irqc # RZ/G3L
'#interrupt-cells':
description: The first cell should contain a macro RZG2L_{NMI,IRQX} included in the
@@ -48,107 +50,35 @@ properties:
interrupts:
minItems: 45
- items:
- - description: NMI interrupt
- - description: IRQ0 interrupt
- - description: IRQ1 interrupt
- - description: IRQ2 interrupt
- - description: IRQ3 interrupt
- - description: IRQ4 interrupt
- - description: IRQ5 interrupt
- - description: IRQ6 interrupt
- - description: IRQ7 interrupt
- - description: GPIO interrupt, TINT0
- - description: GPIO interrupt, TINT1
- - description: GPIO interrupt, TINT2
- - description: GPIO interrupt, TINT3
- - description: GPIO interrupt, TINT4
- - description: GPIO interrupt, TINT5
- - description: GPIO interrupt, TINT6
- - description: GPIO interrupt, TINT7
- - description: GPIO interrupt, TINT8
- - description: GPIO interrupt, TINT9
- - description: GPIO interrupt, TINT10
- - description: GPIO interrupt, TINT11
- - description: GPIO interrupt, TINT12
- - description: GPIO interrupt, TINT13
- - description: GPIO interrupt, TINT14
- - description: GPIO interrupt, TINT15
- - description: GPIO interrupt, TINT16
- - description: GPIO interrupt, TINT17
- - description: GPIO interrupt, TINT18
- - description: GPIO interrupt, TINT19
- - description: GPIO interrupt, TINT20
- - description: GPIO interrupt, TINT21
- - description: GPIO interrupt, TINT22
- - description: GPIO interrupt, TINT23
- - description: GPIO interrupt, TINT24
- - description: GPIO interrupt, TINT25
- - description: GPIO interrupt, TINT26
- - description: GPIO interrupt, TINT27
- - description: GPIO interrupt, TINT28
- - description: GPIO interrupt, TINT29
- - description: GPIO interrupt, TINT30
- - description: GPIO interrupt, TINT31
- - description: Bus error interrupt
- - description: ECCRAM0 or combined ECCRAM0/1 1bit error interrupt
- - description: ECCRAM0 or combined ECCRAM0/1 2bit error interrupt
- - description: ECCRAM0 or combined ECCRAM0/1 error overflow interrupt
- - description: ECCRAM1 1bit error interrupt
- - description: ECCRAM1 2bit error interrupt
- - description: ECCRAM1 error overflow interrupt
+ maxItems: 61
interrupt-names:
minItems: 45
+ maxItems: 61
items:
- - const: nmi
- - const: irq0
- - const: irq1
- - const: irq2
- - const: irq3
- - const: irq4
- - const: irq5
- - const: irq6
- - const: irq7
- - const: tint0
- - const: tint1
- - const: tint2
- - const: tint3
- - const: tint4
- - const: tint5
- - const: tint6
- - const: tint7
- - const: tint8
- - const: tint9
- - const: tint10
- - const: tint11
- - const: tint12
- - const: tint13
- - const: tint14
- - const: tint15
- - const: tint16
- - const: tint17
- - const: tint18
- - const: tint19
- - const: tint20
- - const: tint21
- - const: tint22
- - const: tint23
- - const: tint24
- - const: tint25
- - const: tint26
- - const: tint27
- - const: tint28
- - const: tint29
- - const: tint30
- - const: tint31
- - const: bus-err
- - const: ec7tie1-0
- - const: ec7tie2-0
- - const: ec7tiovf-0
- - const: ec7tie1-1
- - const: ec7tie2-1
- - const: ec7tiovf-1
+ oneOf:
+ - description: NMI interrupt
+ const: nmi
+ - description: External IRQ interrupt
+ pattern: '^irq([0-9]|1[0-5])$'
+ - description: GPIO interrupt
+ pattern: '^tint([0-9]|1[0-9]|2[0-9]|3[0-1])$'
+ - description: Bus error interrupt
+ const: bus-err
+ - description: ECCRAM0 or combined ECCRAM0/1 1bit error interrupt
+ const: ec7tie1-0
+ - description: ECCRAM0 or combined ECCRAM0/1 2bit error interrupt
+ const: ec7tie2-0
+ - description: ECCRAM0 or combined ECCRAM0/1 error overflow interrupt
+ const: ec7tiovf-0
+ - description: ECCRAM1 1bit error interrupt
+ const: ec7tie1-1
+ - description: ECCRAM1 2bit error interrupt
+ const: ec7tie2-1
+ - description: ECCRAM1 error overflow interrupt
+ const: ec7tiovf-1
+ - description: Integrated GPT Error interrupt
+ pattern: '^ovfunf([0-7])$'
clocks:
maxItems: 2
@@ -180,6 +110,24 @@ required:
allOf:
- $ref: /schemas/interrupt-controller.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - renesas,r9a07g043f-irqc
+ - renesas,r9a07g043u-irqc
+ - renesas,r9a07g044-irqc
+ - renesas,r9a07g054-irqc
+ then:
+ properties:
+ interrupts:
+ minItems: 48
+ maxItems: 48
+ interrupt-names:
+ minItems: 48
+ maxItems: 48
+
- if:
properties:
compatible:
@@ -192,12 +140,19 @@ allOf:
maxItems: 45
interrupt-names:
maxItems: 45
- else:
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - renesas,r9a08g046-irqc
+ then:
properties:
interrupts:
- minItems: 48
+ minItems: 61
interrupt-names:
- minItems: 48
+ minItems: 61
unevaluatedProperties: false
diff --git a/arch/loongarch/kernel/irq.c b/arch/loongarch/kernel/irq.c
index 80946cafaec1..7bf68a7a5f4b 100644
--- a/arch/loongarch/kernel/irq.c
+++ b/arch/loongarch/kernel/irq.c
@@ -11,6 +11,7 @@
#include <linux/irqchip.h>
#include <linux/kernel_stat.h>
#include <linux/proc_fs.h>
+#include <linux/minmax.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/seq_file.h>
@@ -99,6 +100,11 @@ int __init arch_probe_nr_irqs(void)
return NR_IRQS_LEGACY;
}
+unsigned int arch_dynirq_lower_bound(unsigned int from)
+{
+ return MAX(from, NR_IRQS_LEGACY);
+}
+
void __init init_IRQ(void)
{
int i;
diff --git a/arch/mips/loongson64/init.c b/arch/mips/loongson64/init.c
index 5f73f8663ab2..c7cc5a3d7817 100644
--- a/arch/mips/loongson64/init.c
+++ b/arch/mips/loongson64/init.c
@@ -7,6 +7,7 @@
#include <linux/irqchip.h>
#include <linux/logic_pio.h>
#include <linux/memblock.h>
+#include <linux/minmax.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <asm/bootinfo.h>
@@ -227,3 +228,8 @@ void __init arch_init_irq(void)
reserve_pio_range();
irqchip_init();
}
+
+unsigned int arch_dynirq_lower_bound(unsigned int from)
+{
+ return MAX(from, NR_IRQS_LEGACY);
+}
diff --git a/arch/mips/pic32/Kconfig b/arch/mips/pic32/Kconfig
index bb6ab1f3e80d..cd14a071e631 100644
--- a/arch/mips/pic32/Kconfig
+++ b/arch/mips/pic32/Kconfig
@@ -20,7 +20,6 @@ config PIC32MZDA
select LIBFDT
select USE_OF
select PINCTRL
- select PIC32_EVIC
help
Support for the Microchip PIC32MZDA microcontroller.
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index f07b00d7fef9..e755a2a05209 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -252,9 +252,12 @@ config ORION_IRQCHIP
select IRQ_DOMAIN
config PIC32_EVIC
- bool
+ def_bool MACH_PIC32 || COMPILE_TEST
select GENERIC_IRQ_CHIP
select IRQ_DOMAIN
+ help
+ Enable support for the interrupt controller on the Microchip PIC32
+ family of platforms.
config JCORE_AIC
bool "J-Core integrated AIC" if COMPILE_TEST
@@ -541,11 +544,11 @@ config CSKY_APB_INTC
config IMX_IRQSTEER
bool "i.MX IRQSTEER support"
- depends on ARCH_MXC || COMPILE_TEST
- default ARCH_MXC
+ depends on ARCH_MXC || ARCH_S32 || COMPILE_TEST
+ default y if ARCH_MXC || ARCH_S32
select IRQ_DOMAIN
help
- Support for the i.MX IRQSTEER interrupt multiplexer/remapper.
+ Support for the i.MX and S32 IRQSTEER interrupt multiplexer/remapper.
config IMX_INTMUX
bool "i.MX INTMUX support" if COMPILE_TEST
@@ -761,7 +764,6 @@ config LOONGSON_PCH_MSI
config LOONGSON_PCH_LPC
bool "Loongson PCH LPC Controller"
- depends on LOONGARCH
depends on MACH_LOONGSON64 || LOONGARCH
default MACH_LOONGSON64
select IRQ_DOMAIN_HIERARCHY
diff --git a/drivers/irqchip/irq-apple-aic.c b/drivers/irqchip/irq-apple-aic.c
index 2b24c82bb0df..4a3141d9f914 100644
--- a/drivers/irqchip/irq-apple-aic.c
+++ b/drivers/irqchip/irq-apple-aic.c
@@ -134,8 +134,12 @@
#define AIC2_IRQ_CFG 0x2000
+/* AIC v3 registers (MMIO) */
+#define AIC3_IRQ_CFG 0x10000
+
/*
* AIC2 registers are laid out like this, starting at AIC2_IRQ_CFG:
+ * AIC3 registers use the same layout but start at AIC3_IRQ_CFG:
*
* Repeat for each die:
* IRQ_CFG: u32 * MAX_IRQS
@@ -293,6 +297,15 @@ static const struct aic_info aic2_info __initconst = {
.local_fast_ipi = true,
};
+static const struct aic_info aic3_info __initconst = {
+ .version = 3,
+
+ .irq_cfg = AIC3_IRQ_CFG,
+
+ .fast_ipi = true,
+ .local_fast_ipi = true,
+};
+
static const struct of_device_id aic_info_match[] = {
{
.compatible = "apple,t8103-aic",
@@ -310,6 +323,10 @@ static const struct of_device_id aic_info_match[] = {
.compatible = "apple,aic2",
.data = &aic2_info,
},
+ {
+ .compatible = "apple,t8122-aic3",
+ .data = &aic3_info,
+ },
{}
};
@@ -620,7 +637,7 @@ static int aic_irq_domain_map(struct irq_domain *id, unsigned int irq,
u32 type = FIELD_GET(AIC_EVENT_TYPE, hw);
struct irq_chip *chip = &aic_chip;
- if (ic->info.version == 2)
+ if (ic->info.version == 2 || ic->info.version == 3)
chip = &aic2_chip;
if (type == AIC_EVENT_TYPE_IRQ) {
@@ -991,7 +1008,7 @@ static int __init aic_of_ic_init(struct device_node *node, struct device_node *p
break;
}
- case 2: {
+ case 2 ... 3: {
u32 info1, info3;
info1 = aic_ic_read(irqc, AIC2_INFO1);
@@ -1065,7 +1082,7 @@ static int __init aic_of_ic_init(struct device_node *node, struct device_node *p
off += irqc->info.die_stride;
}
- if (irqc->info.version == 2) {
+ if (irqc->info.version == 2 || irqc->info.version == 3) {
u32 config = aic_ic_read(irqc, AIC2_CONFIG);
config |= AIC2_CONFIG_ENABLE;
@@ -1116,3 +1133,4 @@ static int __init aic_of_ic_init(struct device_node *node, struct device_node *p
IRQCHIP_DECLARE(apple_aic, "apple,aic", aic_of_ic_init);
IRQCHIP_DECLARE(apple_aic2, "apple,aic2", aic_of_ic_init);
+IRQCHIP_DECLARE(apple_aic3, "apple,t8122-aic3", aic_of_ic_init);
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 20f13b686ab2..99444a1b2ffa 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -1603,15 +1603,23 @@ static int gic_irq_domain_translate(struct irq_domain *d,
switch (fwspec->param[0]) {
case 0: /* SPI */
+ if (fwspec->param[1] > 987)
+ pr_warn_once("SPI %u out of range (use ESPI?)\n", fwspec->param[1]);
*hwirq = fwspec->param[1] + 32;
break;
case 1: /* PPI */
+ if (fwspec->param[1] > 15)
+ pr_warn_once("PPI %u out of range (use EPPI?)\n", fwspec->param[1]);
*hwirq = fwspec->param[1] + 16;
break;
case 2: /* ESPI */
+ if (fwspec->param[1] > 1023)
+ pr_warn_once("ESPI %u out of range\n", fwspec->param[1]);
*hwirq = fwspec->param[1] + ESPI_BASE_INTID;
break;
case 3: /* EPPI */
+ if (fwspec->param[1] > 63)
+ pr_warn_once("EPPI %u out of range\n", fwspec->param[1]);
*hwirq = fwspec->param[1] + EPPI_BASE_INTID;
break;
case GIC_IRQ_TYPE_LPI: /* LPI */
@@ -2252,7 +2260,7 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
out_unmap_rdist:
for (i = 0; i < nr_redist_regions; i++)
- if (rdist_regs[i].redist_base && !IS_ERR(rdist_regs[i].redist_base))
+ if (!IS_ERR_OR_NULL(rdist_regs[i].redist_base))
iounmap(rdist_regs[i].redist_base);
kfree(rdist_regs);
out_unmap_dist:
diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c
index 4682ce5bf8d3..87b07f517be3 100644
--- a/drivers/irqchip/irq-imx-irqsteer.c
+++ b/drivers/irqchip/irq-imx-irqsteer.c
@@ -26,19 +26,38 @@
#define CHAN_MAX_OUTPUT_INT 0xF
+/* SoC does not implement the CHANCTRL register */
+#define IRQSTEER_QUIRK_NO_CHANCTRL BIT(0)
+
+struct irqsteer_devtype_data {
+ u32 quirks;
+};
+
struct irqsteer_data {
- void __iomem *regs;
- struct clk *ipg_clk;
- int irq[CHAN_MAX_OUTPUT_INT];
- int irq_count;
- raw_spinlock_t lock;
- int reg_num;
- int channel;
- struct irq_domain *domain;
- u32 *saved_reg;
- struct device *dev;
+ void __iomem *regs;
+ struct clk *ipg_clk;
+ int irq[CHAN_MAX_OUTPUT_INT];
+ int irq_count;
+ raw_spinlock_t lock;
+ int reg_num;
+ int channel;
+ struct irq_domain *domain;
+ u32 *saved_reg;
+ struct device *dev;
+ const struct irqsteer_devtype_data *devtype_data;
+};
+
+static const struct irqsteer_devtype_data imx_data = { };
+
+static const struct irqsteer_devtype_data s32n79_data = {
+ .quirks = IRQSTEER_QUIRK_NO_CHANCTRL,
};
+static bool irqsteer_has_chanctrl(const struct irqsteer_devtype_data *data)
+{
+ return !(data->quirks & IRQSTEER_QUIRK_NO_CHANCTRL);
+}
+
static int imx_irqsteer_get_reg_index(struct irqsteer_data *data,
unsigned long irqnum)
{
@@ -188,6 +207,10 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
if (ret)
return ret;
+ data->devtype_data = device_get_match_data(&pdev->dev);
+ if (!data->devtype_data)
+ return dev_err_probe(&pdev->dev, -ENODEV, "failed to match device data\n");
+
/*
* There is one output irq for each group of 64 inputs.
* One register bit map can represent 32 input interrupts.
@@ -210,7 +233,8 @@ static int imx_irqsteer_probe(struct platform_device *pdev)
}
/* steer all IRQs into configured channel */
- writel_relaxed(BIT(data->channel), data->regs + CHANCTRL);
+ if (irqsteer_has_chanctrl(data->devtype_data))
+ writel_relaxed(BIT(data->channel), data->regs + CHANCTRL);
data->domain = irq_domain_create_linear(dev_fwnode(&pdev->dev), data->reg_num * 32,
&imx_irqsteer_domain_ops, data);
@@ -279,7 +303,9 @@ static void imx_irqsteer_restore_regs(struct irqsteer_data *data)
{
int i;
- writel_relaxed(BIT(data->channel), data->regs + CHANCTRL);
+ if (irqsteer_has_chanctrl(data->devtype_data))
+ writel_relaxed(BIT(data->channel), data->regs + CHANCTRL);
+
for (i = 0; i < data->reg_num; i++)
writel_relaxed(data->saved_reg[i],
data->regs + CHANMASK(i, data->reg_num));
@@ -319,7 +345,8 @@ static const struct dev_pm_ops imx_irqsteer_pm_ops = {
};
static const struct of_device_id imx_irqsteer_dt_ids[] = {
- { .compatible = "fsl,imx-irqsteer", },
+ { .compatible = "fsl,imx-irqsteer", .data = &imx_data },
+ { .compatible = "nxp,s32n79-irqsteer", .data = &s32n79_data },
{},
};
diff --git a/drivers/irqchip/irq-loongson-pch-lpc.c b/drivers/irqchip/irq-loongson-pch-lpc.c
index 3ad46ec94e3c..7117ca6fc2f0 100644
--- a/drivers/irqchip/irq-loongson-pch-lpc.c
+++ b/drivers/irqchip/irq-loongson-pch-lpc.c
@@ -13,6 +13,8 @@
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include <linux/syscore_ops.h>
#include "irq-loongson.h"
@@ -175,13 +177,10 @@ static struct syscore pch_lpc_syscore = {
.ops = &pch_lpc_syscore_ops,
};
-int __init pch_lpc_acpi_init(struct irq_domain *parent,
- struct acpi_madt_lpc_pic *acpi_pchlpc)
+static int __init pch_lpc_init(phys_addr_t addr, unsigned long size,
+ struct fwnode_handle *irq_handle, int parent_irq)
{
- int parent_irq;
struct pch_lpc *priv;
- struct irq_fwspec fwspec;
- struct fwnode_handle *irq_handle;
priv = kzalloc_obj(*priv);
if (!priv)
@@ -189,7 +188,7 @@ int __init pch_lpc_acpi_init(struct irq_domain *parent,
raw_spin_lock_init(&priv->lpc_lock);
- priv->base = ioremap(acpi_pchlpc->address, acpi_pchlpc->size);
+ priv->base = ioremap(addr, size);
if (!priv->base)
goto free_priv;
@@ -198,12 +197,6 @@ int __init pch_lpc_acpi_init(struct irq_domain *parent,
goto iounmap_base;
}
- irq_handle = irq_domain_alloc_named_fwnode("lpcintc");
- if (!irq_handle) {
- pr_err("Unable to allocate domain handle\n");
- goto iounmap_base;
- }
-
/*
* The LPC interrupt controller is a legacy i8259-compatible device,
* which requires a static 1:1 mapping for IRQs 0-15.
@@ -213,15 +206,10 @@ int __init pch_lpc_acpi_init(struct irq_domain *parent,
&pch_lpc_domain_ops, priv);
if (!priv->lpc_domain) {
pr_err("Failed to create IRQ domain\n");
- goto free_irq_handle;
+ goto iounmap_base;
}
pch_lpc_reset(priv);
- fwspec.fwnode = parent->fwnode;
- fwspec.param[0] = acpi_pchlpc->cascade + GSI_MIN_PCH_IRQ;
- fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH;
- fwspec.param_count = 2;
- parent_irq = irq_create_fwspec_mapping(&fwspec);
irq_set_chained_handler_and_data(parent_irq, lpc_irq_dispatch, priv);
pch_lpc_priv = priv;
@@ -230,8 +218,6 @@ int __init pch_lpc_acpi_init(struct irq_domain *parent,
return 0;
-free_irq_handle:
- irq_domain_free_fwnode(irq_handle);
iounmap_base:
iounmap(priv->base);
free_priv:
@@ -239,3 +225,69 @@ int __init pch_lpc_acpi_init(struct irq_domain *parent,
return -ENOMEM;
}
+
+#ifdef CONFIG_ACPI
+int __init pch_lpc_acpi_init(struct irq_domain *parent, struct acpi_madt_lpc_pic *acpi_pchlpc)
+{
+ struct fwnode_handle *irq_handle;
+ struct irq_fwspec fwspec;
+ int parent_irq, ret;
+
+ irq_handle = irq_domain_alloc_named_fwnode("lpcintc");
+ if (!irq_handle) {
+ pr_err("Unable to allocate domain handle\n");
+ return -ENOMEM;
+ }
+
+ fwspec.fwnode = parent->fwnode;
+ fwspec.param[0] = acpi_pchlpc->cascade + GSI_MIN_PCH_IRQ;
+ fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH;
+ fwspec.param_count = 2;
+ parent_irq = irq_create_fwspec_mapping(&fwspec);
+ if (parent_irq <= 0) {
+ pr_err("Unable to map LPC parent interrupt\n");
+ irq_domain_free_fwnode(irq_handle);
+ return -ENOMEM;
+ }
+
+ ret = pch_lpc_init(acpi_pchlpc->address, acpi_pchlpc->size, irq_handle, parent_irq);
+ if (ret) {
+ irq_dispose_mapping(parent_irq);
+ irq_domain_free_fwnode(irq_handle);
+ return ret;
+ }
+
+ return 0;
+}
+#endif /* CONFIG_ACPI */
+
+#ifdef CONFIG_OF
+static int __init pch_lpc_of_init(struct device_node *node, struct device_node *parent)
+{
+ struct fwnode_handle *irq_handle;
+ struct resource res;
+ int parent_irq, ret;
+
+ if (of_address_to_resource(node, 0, &res))
+ return -EINVAL;
+
+ parent_irq = irq_of_parse_and_map(node, 0);
+ if (!parent_irq) {
+ pr_err("Failed to get the parent IRQ for LPC IRQs\n");
+ return -EINVAL;
+ }
+
+ irq_handle = of_fwnode_handle(node);
+
+ ret = pch_lpc_init(res.start, resource_size(&res), irq_handle,
+ parent_irq);
+ if (ret) {
+ irq_dispose_mapping(parent_irq);
+ return ret;
+ }
+
+ return 0;
+}
+
+IRQCHIP_DECLARE(pch_lpc, "loongson,ls7a-lpc", pch_lpc_of_init);
+#endif /* CONFIG_OF */
diff --git a/drivers/irqchip/irq-mvebu-odmi.c b/drivers/irqchip/irq-mvebu-odmi.c
index b99ab9dcc14b..94e7eda46e81 100644
--- a/drivers/irqchip/irq-mvebu-odmi.c
+++ b/drivers/irqchip/irq-mvebu-odmi.c
@@ -217,7 +217,7 @@ static int __init mvebu_odmi_init(struct device_node *node,
for (i = 0; i < odmis_count; i++) {
struct odmi_data *odmi = &odmis[i];
- if (odmi->base && !IS_ERR(odmi->base))
+ if (!IS_ERR_OR_NULL(odmi->base))
iounmap(odmis[i].base);
}
bitmap_free(odmis_bm);
diff --git a/drivers/irqchip/irq-pic32-evic.c b/drivers/irqchip/irq-pic32-evic.c
index e85c3e300701..3c48288c9e6c 100644
--- a/drivers/irqchip/irq-pic32-evic.c
+++ b/drivers/irqchip/irq-pic32-evic.c
@@ -15,8 +15,10 @@
#include <linux/irq.h>
#include <linux/platform_data/pic32.h>
+#ifdef CONFIG_MIPS
#include <asm/irq.h>
#include <asm/traps.h>
+#endif
#define REG_INTCON 0x0000
#define REG_INTSTAT 0x0020
@@ -40,6 +42,7 @@ struct evic_chip_data {
static struct irq_domain *evic_irq_domain;
static void __iomem *evic_base;
+#ifdef CONFIG_MIPS
asmlinkage void __weak plat_irq_dispatch(void)
{
unsigned int hwirq;
@@ -47,6 +50,9 @@ asmlinkage void __weak plat_irq_dispatch(void)
hwirq = readl(evic_base + REG_INTSTAT) & 0xFF;
do_domain_IRQ(evic_irq_domain, hwirq);
}
+#else
+static __maybe_unused void (*board_bind_eic_interrupt)(int irq, int regset);
+#endif
static struct evic_chip_data *irqd_to_priv(struct irq_data *data)
{
@@ -196,7 +202,7 @@ static void __init pic32_ext_irq_of_init(struct irq_domain *domain)
of_property_for_each_u32(node, pname, hwirq) {
if (i >= ARRAY_SIZE(priv->ext_irqs)) {
- pr_warn("More than %d external irq, skip rest\n",
+ pr_warn("More than %zu external irq, skip rest\n",
ARRAY_SIZE(priv->ext_irqs));
break;
}
diff --git a/drivers/irqchip/irq-renesas-rzg2l.c b/drivers/irqchip/irq-renesas-rzg2l.c
index e73d426cea6d..f6b2e69a2f4e 100644
--- a/drivers/irqchip/irq-renesas-rzg2l.c
+++ b/drivers/irqchip/irq-renesas-rzg2l.c
@@ -20,18 +20,21 @@
#include <linux/spinlock.h>
#include <linux/syscore_ops.h>
+#define IRQC_NMI 0
#define IRQC_IRQ_START 1
-#define IRQC_IRQ_COUNT 8
-#define IRQC_TINT_START (IRQC_IRQ_START + IRQC_IRQ_COUNT)
#define IRQC_TINT_COUNT 32
-#define IRQC_NUM_IRQ (IRQC_TINT_START + IRQC_TINT_COUNT)
+#define IRQC_SHARED_IRQ_COUNT 8
+#define IRQC_IRQ_SHARED_START (IRQC_IRQ_START + IRQC_SHARED_IRQ_COUNT)
+#define NSCR 0x0
+#define NITSR 0x4
#define ISCR 0x10
#define IITSR 0x14
#define TSCR 0x20
#define TITSR(n) (0x24 + (n) * 4)
#define TITSR0_MAX_INT 16
#define TITSEL_WIDTH 0x2
+#define INTTSEL 0x2c
#define TSSR(n) (0x30 + ((n) * 4))
#define TIEN BIT(7)
#define TSSEL_SHIFT(n) (8 * (n))
@@ -43,6 +46,10 @@
#define TSSR_OFFSET(n) ((n) % 4)
#define TSSR_INDEX(n) ((n) / 4)
+#define NSCR_NSTAT 0
+#define NITSR_NTSEL_EDGE_FALLING 0
+#define NITSR_NTSEL_EDGE_RISING 1
+
#define TITSR_TITSEL_EDGE_RISING 0
#define TITSR_TITSEL_EDGE_FALLING 1
#define TITSR_TITSEL_LEVEL_HIGH 2
@@ -55,33 +62,62 @@
#define IITSR_IITSEL_EDGE_BOTH 3
#define IITSR_IITSEL_MASK(n) IITSR_IITSEL((n), 3)
+#define INTTSEL_TINTSEL(n) BIT(n)
+#define INTTSEL_TINTSEL_START 24
+
#define TINT_EXTRACT_HWIRQ(x) FIELD_GET(GENMASK(15, 0), (x))
#define TINT_EXTRACT_GPIOINT(x) FIELD_GET(GENMASK(31, 16), (x))
/**
* struct rzg2l_irqc_reg_cache - registers cache (necessary for suspend/resume)
- * @iitsr: IITSR register
- * @titsr: TITSR registers
+ * @nitsr: NITSR register
+ * @iitsr: IITSR register
+ * @inttsel: INTTSEL register
+ * @titsr: TITSR registers
*/
struct rzg2l_irqc_reg_cache {
+ u32 nitsr;
u32 iitsr;
+ u32 inttsel;
u32 titsr[2];
};
+/**
+ * struct rzg2l_hw_info - Interrupt Control Unit controller hardware info structure.
+ * @tssel_lut: TINT lookup table
+ * @irq_count: Number of IRQC interrupts
+ * @tint_start: Start of TINT interrupts
+ * @num_irq: Total Number of interrupts
+ * @shared_irq_cnt: Number of shared interrupts
+ */
+struct rzg2l_hw_info {
+ const u8 *tssel_lut;
+ unsigned int irq_count;
+ unsigned int tint_start;
+ unsigned int num_irq;
+ unsigned int shared_irq_cnt;
+};
+
/**
* struct rzg2l_irqc_priv - IRQ controller private data structure
* @base: Controller's base address
- * @irqchip: Pointer to struct irq_chip
+ * @irq_chip: Pointer to struct irq_chip for irq
+ * @tint_chip: Pointer to struct irq_chip for tint
* @fwspec: IRQ firmware specific data
* @lock: Lock to serialize access to hardware registers
+ * @info: Hardware specific data
* @cache: Registers cache for suspend/resume
+ * @used_irqs: Bitmap to manage the shared interrupts
*/
static struct rzg2l_irqc_priv {
void __iomem *base;
- const struct irq_chip *irqchip;
- struct irq_fwspec fwspec[IRQC_NUM_IRQ];
+ const struct irq_chip *irq_chip;
+ const struct irq_chip *tint_chip;
+ struct irq_fwspec *fwspec;
raw_spinlock_t lock;
+ struct rzg2l_hw_info info;
struct rzg2l_irqc_reg_cache cache;
+ DECLARE_BITMAP(used_irqs, IRQC_SHARED_IRQ_COUNT);
} *rzg2l_irqc_data;
static struct rzg2l_irqc_priv *irq_data_to_priv(struct irq_data *data)
@@ -89,6 +125,28 @@ static struct rzg2l_irqc_priv *irq_data_to_priv(struct irq_data *data)
return data->domain->host_data;
}
+static void rzg2l_clear_nmi_int(struct rzg2l_irqc_priv *priv)
+{
+ u32 bit = BIT(NSCR_NSTAT);
+ u32 reg;
+
+ /*
+ * No locking required as the register is not shared
+ * with other interrupts.
+ *
+ * Writing is allowed only when NSTAT is 1
+ */
+ reg = readl_relaxed(priv->base + NSCR);
+ if (reg & bit) {
+ writel_relaxed(reg & ~bit, priv->base + NSCR);
+ /*
+ * Enforce that the posted write is flushed to prevent that the
+ * just handled interrupt is raised again.
+ */
+ readl_relaxed(priv->base + NSCR);
+ }
+}
+
static void rzg2l_clear_irq_int(struct rzg2l_irqc_priv *priv, unsigned int hwirq)
{
unsigned int hw_irq = hwirq - IRQC_IRQ_START;
@@ -114,7 +172,7 @@ static void rzg2l_clear_irq_int(struct rzg2l_irqc_priv *priv, unsigned int hwirq
static void rzg2l_clear_tint_int(struct rzg2l_irqc_priv *priv, unsigned int hwirq)
{
- u32 bit = BIT(hwirq - IRQC_TINT_START);
+ u32 bit = BIT(hwirq - priv->info.tint_start);
u32 reg;
reg = readl_relaxed(priv->base + TSCR);
@@ -128,17 +186,33 @@ static void rzg2l_clear_tint_int(struct rzg2l_irqc_priv *priv, unsigned int hwir
}
}
-static void rzg2l_irqc_eoi(struct irq_data *d)
+static void rzg2l_irqc_nmi_eoi(struct irq_data *d)
+{
+ struct rzg2l_irqc_priv *priv = irq_data_to_priv(d);
+
+ rzg2l_clear_nmi_int(priv);
+ irq_chip_eoi_parent(d);
+}
+
+static void rzg2l_irqc_irq_eoi(struct irq_data *d)
{
struct rzg2l_irqc_priv *priv = irq_data_to_priv(d);
unsigned int hw_irq = irqd_to_hwirq(d);
- raw_spin_lock(&priv->lock);
- if (hw_irq >= IRQC_IRQ_START && hw_irq <= IRQC_IRQ_COUNT)
+ scoped_guard(raw_spinlock, &priv->lock)
rzg2l_clear_irq_int(priv, hw_irq);
- else if (hw_irq >= IRQC_TINT_START && hw_irq < IRQC_NUM_IRQ)
+
+ irq_chip_eoi_parent(d);
+}
+
+static void rzg2l_irqc_tint_eoi(struct irq_data *d)
+{
+ struct rzg2l_irqc_priv *priv = irq_data_to_priv(d);
+ unsigned int hw_irq = irqd_to_hwirq(d);
+
+ scoped_guard(raw_spinlock, &priv->lock)
rzg2l_clear_tint_int(priv, hw_irq);
- raw_spin_unlock(&priv->lock);
+
irq_chip_eoi_parent(d);
}
@@ -161,7 +235,7 @@ static void rzfive_irqc_unmask_irq_interrupt(struct rzg2l_irqc_priv *priv,
static void rzfive_irqc_mask_tint_interrupt(struct rzg2l_irqc_priv *priv,
unsigned int hwirq)
{
- u32 bit = BIT(hwirq - IRQC_TINT_START);
+ u32 bit = BIT(hwirq - priv->info.tint_start);
writel_relaxed(readl_relaxed(priv->base + TMSK) | bit, priv->base + TMSK);
}
@@ -169,125 +243,170 @@ static void rzfive_irqc_mask_tint_interrupt(struct rzg2l_irqc_priv *priv,
static void rzfive_irqc_unmask_tint_interrupt(struct rzg2l_irqc_priv *priv,
unsigned int hwirq)
{
- u32 bit = BIT(hwirq - IRQC_TINT_START);
+ u32 bit = BIT(hwirq - priv->info.tint_start);
writel_relaxed(readl_relaxed(priv->base + TMSK) & ~bit, priv->base + TMSK);
}
-static void rzfive_irqc_mask(struct irq_data *d)
+static void rzfive_irqc_irq_mask(struct irq_data *d)
{
struct rzg2l_irqc_priv *priv = irq_data_to_priv(d);
unsigned int hwirq = irqd_to_hwirq(d);
- raw_spin_lock(&priv->lock);
- if (hwirq >= IRQC_IRQ_START && hwirq <= IRQC_IRQ_COUNT)
+ scoped_guard(raw_spinlock, &priv->lock)
rzfive_irqc_mask_irq_interrupt(priv, hwirq);
- else if (hwirq >= IRQC_TINT_START && hwirq < IRQC_NUM_IRQ)
+
+ irq_chip_mask_parent(d);
+}
+
+static void rzfive_irqc_tint_mask(struct irq_data *d)
+{
+ struct rzg2l_irqc_priv *priv = irq_data_to_priv(d);
+ unsigned int hwirq = irqd_to_hwirq(d);
+
+ scoped_guard(raw_spinlock, &priv->lock)
rzfive_irqc_mask_tint_interrupt(priv, hwirq);
- raw_spin_unlock(&priv->lock);
+
irq_chip_mask_parent(d);
}
-static void rzfive_irqc_unmask(struct irq_data *d)
+static void rzfive_irqc_irq_unmask(struct irq_data *d)
{
struct rzg2l_irqc_priv *priv = irq_data_to_priv(d);
unsigned int hwirq = irqd_to_hwirq(d);
- raw_spin_lock(&priv->lock);
- if (hwirq >= IRQC_IRQ_START && hwirq <= IRQC_IRQ_COUNT)
+ scoped_guard(raw_spinlock, &priv->lock)
rzfive_irqc_unmask_irq_interrupt(priv, hwirq);
- else if (hwirq >= IRQC_TINT_START && hwirq < IRQC_NUM_IRQ)
+
+ irq_chip_unmask_parent(d);
+}
+
+static void rzfive_irqc_tint_unmask(struct irq_data *d)
+{
+ struct rzg2l_irqc_priv *priv = irq_data_to_priv(d);
+ unsigned int hwirq = irqd_to_hwirq(d);
+
+ scoped_guard(raw_spinlock, &priv->lock)
rzfive_irqc_unmask_tint_interrupt(priv, hwirq);
- raw_spin_unlock(&priv->lock);
+
irq_chip_unmask_parent(d);
}
-static void rzfive_tint_irq_endisable(struct irq_data *d, bool enable)
+static void rzfive_irq_endisable(struct irq_data *d, bool enable)
{
struct rzg2l_irqc_priv *priv = irq_data_to_priv(d);
unsigned int hwirq = irqd_to_hwirq(d);
- if (hwirq >= IRQC_TINT_START && hwirq < IRQC_NUM_IRQ) {
- u32 offset = hwirq - IRQC_TINT_START;
- u32 tssr_offset = TSSR_OFFSET(offset);
- u8 tssr_index = TSSR_INDEX(offset);
- u32 reg;
+ guard(raw_spinlock)(&priv->lock);
+ if (enable)
+ rzfive_irqc_unmask_irq_interrupt(priv, hwirq);
+ else
+ rzfive_irqc_mask_irq_interrupt(priv, hwirq);
+}
- raw_spin_lock(&priv->lock);
- if (enable)
- rzfive_irqc_unmask_tint_interrupt(priv, hwirq);
- else
- rzfive_irqc_mask_tint_interrupt(priv, hwirq);
- reg = readl_relaxed(priv->base + TSSR(tssr_index));
- if (enable)
- reg |= TIEN << TSSEL_SHIFT(tssr_offset);
- else
- reg &= ~(TIEN << TSSEL_SHIFT(tssr_offset));
- writel_relaxed(reg, priv->base + TSSR(tssr_index));
- raw_spin_unlock(&priv->lock);
- } else {
- raw_spin_lock(&priv->lock);
- if (enable)
- rzfive_irqc_unmask_irq_interrupt(priv, hwirq);
- else
- rzfive_irqc_mask_irq_interrupt(priv, hwirq);
- raw_spin_unlock(&priv->lock);
- }
+static void rzfive_tint_endisable(struct irq_data *d, bool enable)
+{
+ struct rzg2l_irqc_priv *priv = irq_data_to_priv(d);
+ unsigned int hwirq = irqd_to_hwirq(d);
+ unsigned int offset = hwirq - priv->info.tint_start;
+ unsigned int tssr_offset = TSSR_OFFSET(offset);
+ unsigned int tssr_index = TSSR_INDEX(offset);
+ u32 reg;
+
+ guard(raw_spinlock)(&priv->lock);
+ if (enable)
+ rzfive_irqc_unmask_tint_interrupt(priv, hwirq);
+ else
+ rzfive_irqc_mask_tint_interrupt(priv, hwirq);
+ reg = readl_relaxed(priv->base + TSSR(tssr_index));
+ if (enable)
+ reg |= TIEN << TSSEL_SHIFT(tssr_offset);
+ else
+ reg &= ~(TIEN << TSSEL_SHIFT(tssr_offset));
+ writel_relaxed(reg, priv->base + TSSR(tssr_index));
}
static void rzfive_irqc_irq_disable(struct irq_data *d)
{
irq_chip_disable_parent(d);
- rzfive_tint_irq_endisable(d, false);
+ rzfive_irq_endisable(d, false);
}
static void rzfive_irqc_irq_enable(struct irq_data *d)
{
- rzfive_tint_irq_endisable(d, true);
+ rzfive_irq_endisable(d, true);
+ irq_chip_enable_parent(d);
+}
+
+static void rzfive_irqc_tint_disable(struct irq_data *d)
+{
+ irq_chip_disable_parent(d);
+ rzfive_tint_endisable(d, false);
+}
+
+static void rzfive_irqc_tint_enable(struct irq_data *d)
+{
+ rzfive_tint_endisable(d, true);
irq_chip_enable_parent(d);
}
static void rzg2l_tint_irq_endisable(struct irq_data *d, bool enable)
{
+ struct rzg2l_irqc_priv *priv = irq_data_to_priv(d);
unsigned int hw_irq = irqd_to_hwirq(d);
+ unsigned int offset = hw_irq - priv->info.tint_start;
+ unsigned int tssr_offset = TSSR_OFFSET(offset);
+ unsigned int tssr_index = TSSR_INDEX(offset);
+ u32 reg;
- if (hw_irq >= IRQC_TINT_START && hw_irq < IRQC_NUM_IRQ) {
- struct rzg2l_irqc_priv *priv = irq_data_to_priv(d);
- u32 offset = hw_irq - IRQC_TINT_START;
- u32 tssr_offset = TSSR_OFFSET(offset);
- u8 tssr_index = TSSR_INDEX(offset);
- u32 reg;
-
- raw_spin_lock(&priv->lock);
- reg = readl_relaxed(priv->base + TSSR(tssr_index));
- if (enable)
- reg |= TIEN << TSSEL_SHIFT(tssr_offset);
- else
- reg &= ~(TIEN << TSSEL_SHIFT(tssr_offset));
- writel_relaxed(reg, priv->base + TSSR(tssr_index));
- raw_spin_unlock(&priv->lock);
- }
+ guard(raw_spinlock)(&priv->lock);
+ reg = readl_relaxed(priv->base + TSSR(tssr_index));
+ if (enable)
+ reg |= TIEN << TSSEL_SHIFT(tssr_offset);
+ else
+ reg &= ~(TIEN << TSSEL_SHIFT(tssr_offset));
+ writel_relaxed(reg, priv->base + TSSR(tssr_index));
}
-static void rzg2l_irqc_irq_disable(struct irq_data *d)
+static void rzg2l_irqc_tint_disable(struct irq_data *d)
{
irq_chip_disable_parent(d);
rzg2l_tint_irq_endisable(d, false);
}
-static void rzg2l_irqc_irq_enable(struct irq_data *d)
+static void rzg2l_irqc_tint_enable(struct irq_data *d)
{
rzg2l_tint_irq_endisable(d, true);
irq_chip_enable_parent(d);
}
+static int rzg2l_nmi_set_type(struct irq_data *d, unsigned int type)
+{
+ struct rzg2l_irqc_priv *priv = irq_data_to_priv(d);
+ u32 sense;
+
+ switch (type & IRQ_TYPE_SENSE_MASK) {
+ case IRQ_TYPE_EDGE_FALLING:
+ sense = NITSR_NTSEL_EDGE_FALLING;
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ sense = NITSR_NTSEL_EDGE_RISING;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ writel_relaxed(sense, priv->base + NITSR);
+ return 0;
+}
+
static int rzg2l_irq_set_type(struct irq_data *d, unsigned int type)
{
struct rzg2l_irqc_priv *priv = irq_data_to_priv(d);
unsigned int hwirq = irqd_to_hwirq(d);
- u32 iitseln = hwirq - IRQC_IRQ_START;
+ unsigned int iitseln = hwirq - IRQC_IRQ_START;
bool clear_irq_int = false;
- u16 sense, tmp;
+ unsigned int sense, tmp;
switch (type & IRQ_TYPE_SENSE_MASK) {
case IRQ_TYPE_LEVEL_LOW:
@@ -313,14 +432,13 @@ static int rzg2l_irq_set_type(struct irq_data *d, unsigned int type)
return -EINVAL;
}
- raw_spin_lock(&priv->lock);
+ guard(raw_spinlock)(&priv->lock);
tmp = readl_relaxed(priv->base + IITSR);
tmp &= ~IITSR_IITSEL_MASK(iitseln);
tmp |= IITSR_IITSEL(iitseln, sense);
if (clear_irq_int)
rzg2l_clear_irq_int(priv, hwirq);
writel_relaxed(tmp, priv->base + IITSR);
- raw_spin_unlock(&priv->lock);
return 0;
}
@@ -331,6 +449,11 @@ static u32 rzg2l_disable_tint_and_set_tint_source(struct irq_data *d, struct rzg
u32 tint = (u32)(uintptr_t)irq_data_get_irq_chip_data(d);
u32 tien = reg & (TIEN << TSSEL_SHIFT(tssr_offset));
+ if (priv->info.tssel_lut)
+ tint = priv->info.tssel_lut[tint];
+ else
+ tint = (u32)(uintptr_t)irq_data_get_irq_chip_data(d);
+
/* Clear the relevant byte in reg */
reg &= ~(TSSEL_MASK << TSSEL_SHIFT(tssr_offset));
/* Set TINT and leave TIEN clear */
@@ -344,10 +467,10 @@ static int rzg2l_tint_set_edge(struct irq_data *d, unsigned int type)
{
struct rzg2l_irqc_priv *priv = irq_data_to_priv(d);
unsigned int hwirq = irqd_to_hwirq(d);
- u32 titseln = hwirq - IRQC_TINT_START;
- u32 tssr_offset = TSSR_OFFSET(titseln);
- u8 tssr_index = TSSR_INDEX(titseln);
- u8 index, sense;
+ unsigned int titseln = hwirq - priv->info.tint_start;
+ unsigned int tssr_offset = TSSR_OFFSET(titseln);
+ unsigned int tssr_index = TSSR_INDEX(titseln);
+ unsigned int index, sense;
u32 reg, tssr;
switch (type & IRQ_TYPE_SENSE_MASK) {
@@ -383,15 +506,31 @@ static int rzg2l_tint_set_edge(struct irq_data *d, unsigned int type)
return 0;
}
-static int rzg2l_irqc_set_type(struct irq_data *d, unsigned int type)
+static int rzg2l_irqc_irq_set_type(struct irq_data *d, unsigned int type)
{
- unsigned int hw_irq = irqd_to_hwirq(d);
- int ret = -EINVAL;
+ int ret = rzg2l_irq_set_type(d, type);
+
+ if (ret)
+ return ret;
+
+ return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH);
+}
+
+static int rzg2l_irqc_tint_set_type(struct irq_data *d, unsigned int type)
+{
+ int ret = rzg2l_tint_set_edge(d, type);
- if (hw_irq >= IRQC_IRQ_START && hw_irq <= IRQC_IRQ_COUNT)
- ret = rzg2l_irq_set_type(d, type);
- else if (hw_irq >= IRQC_TINT_START && hw_irq < IRQC_NUM_IRQ)
- ret = rzg2l_tint_set_edge(d, type);
+ if (ret)
+ return ret;
+
+ return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH);
+}
+
+static int rzg2l_irqc_nmi_set_type(struct irq_data *d, unsigned int type)
+{
+ int ret;
+
+ ret = rzg2l_nmi_set_type(d, type);
if (ret)
return ret;
@@ -403,7 +542,10 @@ static int rzg2l_irqc_irq_suspend(void *data)
struct rzg2l_irqc_reg_cache *cache = &rzg2l_irqc_data->cache;
void __iomem *base = rzg2l_irqc_data->base;
+ cache->nitsr = readl_relaxed(base + NITSR);
cache->iitsr = readl_relaxed(base + IITSR);
+ if (rzg2l_irqc_data->info.shared_irq_cnt)
+ cache->inttsel = readl_relaxed(base + INTTSEL);
for (u8 i = 0; i < 2; i++)
cache->titsr[i] = readl_relaxed(base + TITSR(i));
@@ -422,7 +564,10 @@ static void rzg2l_irqc_irq_resume(void *data)
*/
for (u8 i = 0; i < 2; i++)
writel_relaxed(cache->titsr[i], base + TITSR(i));
+ if (rzg2l_irqc_data->info.shared_irq_cnt)
+ writel_relaxed(cache->inttsel, base + INTTSEL);
writel_relaxed(cache->iitsr, base + IITSR);
+ writel_relaxed(cache->nitsr, base + NITSR);
}
static const struct syscore_ops rzg2l_irqc_syscore_ops = {
@@ -434,44 +579,162 @@ static struct syscore rzg2l_irqc_syscore = {
.ops = &rzg2l_irqc_syscore_ops,
};
-static const struct irq_chip rzg2l_irqc_chip = {
+static const struct irq_chip rzg2l_irqc_nmi_chip = {
+ .name = "rzg2l-irqc",
+ .irq_eoi = rzg2l_irqc_nmi_eoi,
+ .irq_mask = irq_chip_mask_parent,
+ .irq_unmask = irq_chip_unmask_parent,
+ .irq_disable = irq_chip_disable_parent,
+ .irq_enable = irq_chip_enable_parent,
+ .irq_get_irqchip_state = irq_chip_get_parent_state,
+ .irq_set_irqchip_state = irq_chip_set_parent_state,
+ .irq_retrigger = irq_chip_retrigger_hierarchy,
+ .irq_set_type = rzg2l_irqc_nmi_set_type,
+ .irq_set_affinity = irq_chip_set_affinity_parent,
+ .flags = IRQCHIP_MASK_ON_SUSPEND |
+ IRQCHIP_SET_TYPE_MASKED |
+ IRQCHIP_SKIP_SET_WAKE,
+};
+
+static const struct irq_chip rzg2l_irqc_irq_chip = {
.name = "rzg2l-irqc",
- .irq_eoi = rzg2l_irqc_eoi,
+ .irq_eoi = rzg2l_irqc_irq_eoi,
.irq_mask = irq_chip_mask_parent,
.irq_unmask = irq_chip_unmask_parent,
- .irq_disable = rzg2l_irqc_irq_disable,
- .irq_enable = rzg2l_irqc_irq_enable,
+ .irq_disable = irq_chip_disable_parent,
+ .irq_enable = irq_chip_enable_parent,
.irq_get_irqchip_state = irq_chip_get_parent_state,
.irq_set_irqchip_state = irq_chip_set_parent_state,
.irq_retrigger = irq_chip_retrigger_hierarchy,
- .irq_set_type = rzg2l_irqc_set_type,
+ .irq_set_type = rzg2l_irqc_irq_set_type,
.irq_set_affinity = irq_chip_set_affinity_parent,
.flags = IRQCHIP_MASK_ON_SUSPEND |
IRQCHIP_SET_TYPE_MASKED |
IRQCHIP_SKIP_SET_WAKE,
};
-static const struct irq_chip rzfive_irqc_chip = {
+static const struct irq_chip rzg2l_irqc_tint_chip = {
+ .name = "rzg2l-irqc",
+ .irq_eoi = rzg2l_irqc_tint_eoi,
+ .irq_mask = irq_chip_mask_parent,
+ .irq_unmask = irq_chip_unmask_parent,
+ .irq_disable = rzg2l_irqc_tint_disable,
+ .irq_enable = rzg2l_irqc_tint_enable,
+ .irq_get_irqchip_state = irq_chip_get_parent_state,
+ .irq_set_irqchip_state = irq_chip_set_parent_state,
+ .irq_retrigger = irq_chip_retrigger_hierarchy,
+ .irq_set_type = rzg2l_irqc_tint_set_type,
+ .irq_set_affinity = irq_chip_set_affinity_parent,
+ .flags = IRQCHIP_MASK_ON_SUSPEND |
+ IRQCHIP_SET_TYPE_MASKED |
+ IRQCHIP_SKIP_SET_WAKE,
+};
+
+static const struct irq_chip rzfive_irqc_irq_chip = {
.name = "rzfive-irqc",
- .irq_eoi = rzg2l_irqc_eoi,
- .irq_mask = rzfive_irqc_mask,
- .irq_unmask = rzfive_irqc_unmask,
+ .irq_eoi = rzg2l_irqc_irq_eoi,
+ .irq_mask = rzfive_irqc_irq_mask,
+ .irq_unmask = rzfive_irqc_irq_unmask,
.irq_disable = rzfive_irqc_irq_disable,
.irq_enable = rzfive_irqc_irq_enable,
.irq_get_irqchip_state = irq_chip_get_parent_state,
.irq_set_irqchip_state = irq_chip_set_parent_state,
.irq_retrigger = irq_chip_retrigger_hierarchy,
- .irq_set_type = rzg2l_irqc_set_type,
+ .irq_set_type = rzg2l_irqc_irq_set_type,
.irq_set_affinity = irq_chip_set_affinity_parent,
.flags = IRQCHIP_MASK_ON_SUSPEND |
IRQCHIP_SET_TYPE_MASKED |
IRQCHIP_SKIP_SET_WAKE,
};
+static const struct irq_chip rzfive_irqc_tint_chip = {
+ .name = "rzfive-irqc",
+ .irq_eoi = rzg2l_irqc_tint_eoi,
+ .irq_mask = rzfive_irqc_tint_mask,
+ .irq_unmask = rzfive_irqc_tint_unmask,
+ .irq_disable = rzfive_irqc_tint_disable,
+ .irq_enable = rzfive_irqc_tint_enable,
+ .irq_get_irqchip_state = irq_chip_get_parent_state,
+ .irq_set_irqchip_state = irq_chip_set_parent_state,
+ .irq_retrigger = irq_chip_retrigger_hierarchy,
+ .irq_set_type = rzg2l_irqc_tint_set_type,
+ .irq_set_affinity = irq_chip_set_affinity_parent,
+ .flags = IRQCHIP_MASK_ON_SUSPEND |
+ IRQCHIP_SET_TYPE_MASKED |
+ IRQCHIP_SKIP_SET_WAKE,
+};
+
+static bool rzg2l_irqc_is_shared_irqc(const struct rzg2l_hw_info info, unsigned int hw_irq)
+{
+ return ((hw_irq >= (info.tint_start - info.shared_irq_cnt)) && hw_irq < info.tint_start);
+}
+
+static bool rzg2l_irqc_is_shared_tint(const struct rzg2l_hw_info info, unsigned int hw_irq)
+{
+ return ((hw_irq >= (info.num_irq - info.shared_irq_cnt)) && hw_irq < info.num_irq);
+}
+
+static bool rzg2l_irqc_is_shared_and_get_irq_num(struct rzg2l_irqc_priv *priv,
+ irq_hw_number_t hwirq, unsigned int *irq_num)
+{
+ bool is_shared = false;
+
+ if (rzg2l_irqc_is_shared_irqc(priv->info, hwirq)) {
+ *irq_num = hwirq - IRQC_IRQ_SHARED_START;
+ is_shared = true;
+ } else if (rzg2l_irqc_is_shared_tint(priv->info, hwirq)) {
+ *irq_num = hwirq - IRQC_TINT_COUNT - IRQC_IRQ_SHARED_START;
+ is_shared = true;
+ }
+
+ return is_shared;
+}
+
+static void rzg2l_irqc_set_inttsel(struct rzg2l_irqc_priv *priv, unsigned int offset,
+ unsigned int select_irq)
+{
+ u32 reg;
+
+ guard(raw_spinlock_irqsave)(&priv->lock);
+ reg = readl_relaxed(priv->base + INTTSEL);
+ if (select_irq)
+ reg |= INTTSEL_TINTSEL(offset);
+ else
+ reg &= ~INTTSEL_TINTSEL(offset);
+ writel_relaxed(reg, priv->base + INTTSEL);
+}
+
+static int rzg2l_irqc_shared_irq_alloc(struct rzg2l_irqc_priv *priv, irq_hw_number_t hwirq)
+{
+ unsigned int irq_num;
+
+ if (rzg2l_irqc_is_shared_and_get_irq_num(priv, hwirq, &irq_num)) {
+ if (test_and_set_bit(irq_num, priv->used_irqs))
+ return -EBUSY;
+
+ if (hwirq < priv->info.tint_start)
+ rzg2l_irqc_set_inttsel(priv, INTTSEL_TINTSEL_START + irq_num, 1);
+ else
+ rzg2l_irqc_set_inttsel(priv, INTTSEL_TINTSEL_START + irq_num, 0);
+ }
+
+ return 0;
+}
+
+static void rzg2l_irqc_shared_irq_free(struct rzg2l_irqc_priv *priv, irq_hw_number_t hwirq)
+{
+ unsigned int irq_num;
+
+ if (rzg2l_irqc_is_shared_and_get_irq_num(priv, hwirq, &irq_num) &&
+ test_and_clear_bit(irq_num, priv->used_irqs))
+ rzg2l_irqc_set_inttsel(priv, INTTSEL_TINTSEL_START + irq_num, 0);
+}
+
static int rzg2l_irqc_alloc(struct irq_domain *domain, unsigned int virq,
unsigned int nr_irqs, void *arg)
{
struct rzg2l_irqc_priv *priv = domain->host_data;
+ const struct irq_chip *chip;
unsigned long tint = 0;
irq_hw_number_t hwirq;
unsigned int type;
@@ -488,28 +751,57 @@ static int rzg2l_irqc_alloc(struct irq_domain *domain, unsigned int virq,
* from 16-31 bits. TINT from the pinctrl driver needs to be programmed
* in IRQC registers to enable a given gpio pin as interrupt.
*/
- if (hwirq > IRQC_IRQ_COUNT) {
+ if (hwirq == IRQC_NMI) {
+ chip = &rzg2l_irqc_nmi_chip;
+ } else if (hwirq > priv->info.irq_count) {
tint = TINT_EXTRACT_GPIOINT(hwirq);
hwirq = TINT_EXTRACT_HWIRQ(hwirq);
-
- if (hwirq < IRQC_TINT_START)
- return -EINVAL;
+ chip = priv->tint_chip;
+ } else {
+ chip = priv->irq_chip;
}
- if (hwirq > (IRQC_NUM_IRQ - 1))
+ if (hwirq >= priv->info.num_irq)
return -EINVAL;
- ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, priv->irqchip,
- (void *)(uintptr_t)tint);
+ if (priv->info.shared_irq_cnt) {
+ ret = rzg2l_irqc_shared_irq_alloc(priv, hwirq);
+ if (ret)
+ return ret;
+ }
+
+ ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, chip, (void *)(uintptr_t)tint);
if (ret)
- return ret;
+ goto shared_irq_free;
+
+ ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &priv->fwspec[hwirq]);
+ if (ret)
+ goto shared_irq_free;
+
+ return 0;
+
+shared_irq_free:
+ if (priv->info.shared_irq_cnt)
+ rzg2l_irqc_shared_irq_free(priv, hwirq);
- return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &priv->fwspec[hwirq]);
+ return ret;
+}
+
+static void rzg2l_irqc_free(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs)
+{
+ struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+ struct rzg2l_irqc_priv *priv = domain->host_data;
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+
+ irq_domain_free_irqs_common(domain, virq, nr_irqs);
+
+ if (priv->info.shared_irq_cnt)
+ rzg2l_irqc_shared_irq_free(priv, hwirq);
}
static const struct irq_domain_ops rzg2l_irqc_domain_ops = {
.alloc = rzg2l_irqc_alloc,
- .free = irq_domain_free_irqs_common,
+ .free = rzg2l_irqc_free,
.translate = irq_domain_translate_twocell,
};
@@ -520,7 +812,7 @@ static int rzg2l_irqc_parse_interrupts(struct rzg2l_irqc_priv *priv,
unsigned int i;
int ret;
- for (i = 0; i < IRQC_NUM_IRQ; i++) {
+ for (i = 0; i < priv->info.num_irq; i++) {
ret = of_irq_parse_one(np, i, &map);
if (ret)
return ret;
@@ -532,7 +824,9 @@ static int rzg2l_irqc_parse_interrupts(struct rzg2l_irqc_priv *priv,
}
static int rzg2l_irqc_common_probe(struct platform_device *pdev, struct device_node *parent,
- const struct irq_chip *irq_chip)
+ const struct irq_chip *irq_chip,
+ const struct irq_chip *tint_chip,
+ const struct rzg2l_hw_info info)
{
struct irq_domain *irq_domain, *parent_domain;
struct device_node *node = pdev->dev.of_node;
@@ -548,12 +842,20 @@ static int rzg2l_irqc_common_probe(struct platform_device *pdev, struct device_n
if (!rzg2l_irqc_data)
return -ENOMEM;
- rzg2l_irqc_data->irqchip = irq_chip;
+ rzg2l_irqc_data->irq_chip = irq_chip;
+ rzg2l_irqc_data->tint_chip = tint_chip;
rzg2l_irqc_data->base = devm_of_iomap(dev, dev->of_node, 0, NULL);
if (IS_ERR(rzg2l_irqc_data->base))
return PTR_ERR(rzg2l_irqc_data->base);
+ rzg2l_irqc_data->info = info;
+
+ rzg2l_irqc_data->fwspec = devm_kcalloc(&pdev->dev, info.num_irq,
+ sizeof(*rzg2l_irqc_data->fwspec), GFP_KERNEL);
+ if (!rzg2l_irqc_data->fwspec)
+ return -ENOMEM;
+
ret = rzg2l_irqc_parse_interrupts(rzg2l_irqc_data, node);
if (ret)
return dev_err_probe(dev, ret, "cannot parse interrupts: %d\n", ret);
@@ -574,10 +876,10 @@ static int rzg2l_irqc_common_probe(struct platform_device *pdev, struct device_n
raw_spin_lock_init(&rzg2l_irqc_data->lock);
- irq_domain = irq_domain_create_hierarchy(parent_domain, 0, IRQC_NUM_IRQ, dev_fwnode(dev),
+ irq_domain = irq_domain_create_hierarchy(parent_domain, 0, info.num_irq, dev_fwnode(dev),
&rzg2l_irqc_domain_ops, rzg2l_irqc_data);
if (!irq_domain) {
- pm_runtime_put(dev);
+ pm_runtime_put_sync(dev);
return -ENOMEM;
}
@@ -586,18 +888,64 @@ static int rzg2l_irqc_common_probe(struct platform_device *pdev, struct device_n
return 0;
}
+/* Mapping based on port index on Table 4.2-1 and GPIOINT on Table 4.6-7 */
+static const u8 rzg3l_tssel_lut[] = {
+ 83, 84, /* P20-P21 */
+ 7, 8, 9, 10, 11, 12, 13, /* P30-P36 */
+ 85, 86, 87, 88, 89, 90, 91, /* P50-P56 */
+ 92, 93, 94, 95, 96, 97, 98, /* P60-P66 */
+ 99, 100, 101, 102, 103, 104, 105, 106, /* P70-P77 */
+ 107, 108, 109, 110, 111, 112, /* P80-P85 */
+ 45, 46, 47, 48, 49, 50, 51, 52, /* PA0-PA7 */
+ 53, 54, 55, 56, 57, 58, 59, 60, /* PB0-PB7 */
+ 61, 62, 63, /* PC0-PC2 */
+ 64, 65, 66, 67, 68, 69, 70, 71, /* PD0-PD7 */
+ 72, 73, 74, 75, 76, 77, 78, 79, /* PE0-PE7 */
+ 80, 81, 82, /* PF0-PF2 */
+ 27, 28, 29, 30, 31, 32, 33, 34, /* PG0-PG7 */
+ 35, 36, 37, 38, 39, 40, /* PH0-PH5 */
+ 2, 3, 4, 5, 6, /* PJ0-PJ4 */
+ 41, 42, 43, 44, /* PK0-PK3 */
+ 14, 15, 16, 17, 26, /* PL0-PL4 */
+ 18, 19, 20, 21, 22, 23, 24, 25, /* PM0-PM7 */
+ 0, 1 /* PS0-PS1 */
+};
+
+static const struct rzg2l_hw_info rzg3l_hw_params = {
+ .tssel_lut = rzg3l_tssel_lut,
+ .irq_count = 16,
+ .tint_start = IRQC_IRQ_START + 16,
+ .num_irq = IRQC_IRQ_START + 16 + IRQC_TINT_COUNT,
+ .shared_irq_cnt = IRQC_SHARED_IRQ_COUNT,
+};
+
+static const struct rzg2l_hw_info rzg2l_hw_params = {
+ .irq_count = 8,
+ .tint_start = IRQC_IRQ_START + 8,
+ .num_irq = IRQC_IRQ_START + 8 + IRQC_TINT_COUNT,
+};
+
static int rzg2l_irqc_probe(struct platform_device *pdev, struct device_node *parent)
{
- return rzg2l_irqc_common_probe(pdev, parent, &rzg2l_irqc_chip);
+ return rzg2l_irqc_common_probe(pdev, parent, &rzg2l_irqc_irq_chip, &rzg2l_irqc_tint_chip,
+ rzg2l_hw_params);
+}
+
+static int rzg3l_irqc_probe(struct platform_device *pdev, struct device_node *parent)
+{
+ return rzg2l_irqc_common_probe(pdev, parent, &rzg2l_irqc_irq_chip, &rzg2l_irqc_tint_chip,
+ rzg3l_hw_params);
}
static int rzfive_irqc_probe(struct platform_device *pdev, struct device_node *parent)
{
- return rzg2l_irqc_common_probe(pdev, parent, &rzfive_irqc_chip);
+ return rzg2l_irqc_common_probe(pdev, parent, &rzfive_irqc_irq_chip, &rzfive_irqc_tint_chip,
+ rzg2l_hw_params);
}
IRQCHIP_PLATFORM_DRIVER_BEGIN(rzg2l_irqc)
IRQCHIP_MATCH("renesas,rzg2l-irqc", rzg2l_irqc_probe)
+IRQCHIP_MATCH("renesas,r9a08g046-irqc", rzg3l_irqc_probe)
IRQCHIP_MATCH("renesas,r9a07g043f-irqc", rzfive_irqc_probe)
IRQCHIP_PLATFORM_DRIVER_END(rzg2l_irqc)
MODULE_AUTHOR("Lad Prabhakar <prabhakar.mahadev-lad.rj@xxxxxxxxxxxxxx>");
diff --git a/drivers/irqchip/irq-renesas-rzv2h.c b/drivers/irqchip/irq-renesas-rzv2h.c
index 03e93b061edd..31c543c876b1 100644
--- a/drivers/irqchip/irq-renesas-rzv2h.c
+++ b/drivers/irqchip/irq-renesas-rzv2h.c
@@ -12,6 +12,7 @@
#include <linux/bitfield.h>
#include <linux/cleanup.h>
#include <linux/err.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irqchip.h>
#include <linux/irqchip/irq-renesas-rzv2h.h>
@@ -25,9 +26,17 @@
/* DT "interrupts" indexes */
#define ICU_IRQ_START 1
#define ICU_IRQ_COUNT 16
-#define ICU_TINT_START (ICU_IRQ_START + ICU_IRQ_COUNT)
+#define ICU_IRQ_LAST (ICU_IRQ_START + ICU_IRQ_COUNT - 1)
+#define ICU_TINT_START (ICU_IRQ_LAST + 1)
#define ICU_TINT_COUNT 32
-#define ICU_NUM_IRQ (ICU_TINT_START + ICU_TINT_COUNT)
+#define ICU_TINT_LAST (ICU_TINT_START + ICU_TINT_COUNT - 1)
+#define ICU_CA55_INT_START (ICU_TINT_LAST + 1)
+#define ICU_CA55_INT_COUNT 4
+#define ICU_CA55_INT_LAST (ICU_CA55_INT_START + ICU_CA55_INT_COUNT - 1)
+#define ICU_ERR_INT_START (ICU_CA55_INT_LAST + 1)
+#define ICU_ERR_INT_COUNT 1
+#define ICU_ERR_INT_LAST (ICU_ERR_INT_START + ICU_ERR_INT_COUNT - 1)
+#define ICU_NUM_IRQ (ICU_ERR_INT_LAST + 1)
/* Registers */
#define ICU_NSCNT 0x00
@@ -40,6 +49,15 @@
#define ICU_TSCLR 0x24
#define ICU_TITSR(k) (0x28 + (k) * 4)
#define ICU_TSSR(k) (0x30 + (k) * 4)
+#define ICU_BEISR(k) (0x70 + (k) * 4)
+#define ICU_BECLR(k) (0x80 + (k) * 4)
+#define ICU_EREISR(k) (0x90 + (k) * 4)
+#define ICU_ERCLR(k) (0xE0 + (k) * 4)
+#define ICU_SWINT 0x130
+#define ICU_ERINTA55CTL(k) (0x338 + (k) * 4)
+#define ICU_ERINTA55CRL(k) (0x348 + (k) * 4)
+#define ICU_ERINTA55MSK(k) (0x358 + (k) * 4)
+#define ICU_SWPE 0x370
#define ICU_DMkSELy(k, y) (0x420 + (k) * 0x20 + (y) * 4)
#define ICU_DMACKSELk(k) (0x500 + (k) * 4)
@@ -90,6 +108,10 @@
#define ICU_RZG3E_TSSEL_MAX_VAL 0x8c
#define ICU_RZV2H_TSSEL_MAX_VAL 0x55
+#define ICU_SWPE_NUM 16
+#define ICU_NUM_BE 4
+#define ICU_NUM_A55ERR 4
+
/**
* struct rzv2h_irqc_reg_cache - registers cache (necessary for suspend/resume)
* @nitsr: ICU_NITSR register
@@ -108,12 +130,16 @@ struct rzv2h_irqc_reg_cache {
* @t_offs: TINT offset
* @max_tssel: TSSEL max value
* @field_width: TSSR field width
+ * @ecc_start: Start index of ECC RAM interrupts
+ * @ecc_end: End index of ECC RAM interrupts
*/
struct rzv2h_hw_info {
const u8 *tssel_lut;
u16 t_offs;
u8 max_tssel;
u8 field_width;
+ u8 ecc_start;
+ u8 ecc_end;
};
/* DMAC */
@@ -167,32 +193,47 @@ static inline struct rzv2h_icu_priv *irq_data_to_priv(struct irq_data *data)
return data->domain->host_data;
}
-static void rzv2h_icu_eoi(struct irq_data *d)
+static void rzv2h_icu_tint_eoi(struct irq_data *d)
{
struct rzv2h_icu_priv *priv = irq_data_to_priv(d);
unsigned int hw_irq = irqd_to_hwirq(d);
unsigned int tintirq_nr;
u32 bit;
- scoped_guard(raw_spinlock, &priv->lock) {
- if (hw_irq >= ICU_TINT_START) {
- tintirq_nr = hw_irq - ICU_TINT_START;
- bit = BIT(tintirq_nr);
- if (!irqd_is_level_type(d))
- writel_relaxed(bit, priv->base + priv->info->t_offs + ICU_TSCLR);
- } else if (hw_irq >= ICU_IRQ_START) {
- tintirq_nr = hw_irq - ICU_IRQ_START;
- bit = BIT(tintirq_nr);
- if (!irqd_is_level_type(d))
- writel_relaxed(bit, priv->base + ICU_ISCLR);
- } else {
- writel_relaxed(ICU_NSCLR_NCLR, priv->base + ICU_NSCLR);
- }
+ if (!irqd_is_level_type(d)) {
+ tintirq_nr = hw_irq - ICU_TINT_START;
+ bit = BIT(tintirq_nr);
+ writel_relaxed(bit, priv->base + priv->info->t_offs + ICU_TSCLR);
}
irq_chip_eoi_parent(d);
}
+static void rzv2h_icu_irq_eoi(struct irq_data *d)
+{
+ struct rzv2h_icu_priv *priv = irq_data_to_priv(d);
+ unsigned int hw_irq = irqd_to_hwirq(d);
+ unsigned int tintirq_nr;
+ u32 bit;
+
+ if (!irqd_is_level_type(d)) {
+ tintirq_nr = hw_irq - ICU_IRQ_START;
+ bit = BIT(tintirq_nr);
+ writel_relaxed(bit, priv->base + ICU_ISCLR);
+ }
+
+ irq_chip_eoi_parent(d);
+}
+
+static void rzv2h_icu_nmi_eoi(struct irq_data *d)
+{
+ struct rzv2h_icu_priv *priv = irq_data_to_priv(d);
+
+ writel_relaxed(ICU_NSCLR_NCLR, priv->base + ICU_NSCLR);
+
+ irq_chip_eoi_parent(d);
+}
+
static void rzv2h_tint_irq_endisable(struct irq_data *d, bool enable)
{
struct rzv2h_icu_priv *priv = irq_data_to_priv(d);
@@ -200,9 +241,6 @@ static void rzv2h_tint_irq_endisable(struct irq_data *d, bool enable)
u32 tint_nr, tssel_n, k, tssr;
u8 nr_tint;
- if (hw_irq < ICU_TINT_START)
- return;
-
tint_nr = hw_irq - ICU_TINT_START;
nr_tint = 32 / priv->info->field_width;
k = tint_nr / nr_tint;
@@ -225,13 +263,13 @@ static void rzv2h_tint_irq_endisable(struct irq_data *d, bool enable)
writel_relaxed(BIT(tint_nr), priv->base + priv->info->t_offs + ICU_TSCLR);
}
-static void rzv2h_icu_irq_disable(struct irq_data *d)
+static void rzv2h_icu_tint_disable(struct irq_data *d)
{
irq_chip_disable_parent(d);
rzv2h_tint_irq_endisable(d, false);
}
-static void rzv2h_icu_irq_enable(struct irq_data *d)
+static void rzv2h_icu_tint_enable(struct irq_data *d)
{
rzv2h_tint_irq_endisable(d, true);
irq_chip_enable_parent(d);
@@ -257,7 +295,7 @@ static int rzv2h_nmi_set_type(struct irq_data *d, unsigned int type)
writel_relaxed(sense, priv->base + ICU_NITSR);
- return 0;
+ return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH);
}
static void rzv2h_clear_irq_int(struct rzv2h_icu_priv *priv, unsigned int hwirq)
@@ -307,14 +345,15 @@ static int rzv2h_irq_set_type(struct irq_data *d, unsigned int type)
return -EINVAL;
}
- guard(raw_spinlock)(&priv->lock);
- iitsr = readl_relaxed(priv->base + ICU_IITSR);
- iitsr &= ~ICU_IITSR_IITSEL_MASK(irq_nr);
- iitsr |= ICU_IITSR_IITSEL_PREP(sense, irq_nr);
- rzv2h_clear_irq_int(priv, hwirq);
- writel_relaxed(iitsr, priv->base + ICU_IITSR);
+ scoped_guard(raw_spinlock, &priv->lock) {
+ iitsr = readl_relaxed(priv->base + ICU_IITSR);
+ iitsr &= ~ICU_IITSR_IITSEL_MASK(irq_nr);
+ iitsr |= ICU_IITSR_IITSEL_PREP(sense, irq_nr);
+ rzv2h_clear_irq_int(priv, hwirq);
+ writel_relaxed(iitsr, priv->base + ICU_IITSR);
+ }
- return 0;
+ return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH);
}
static void rzv2h_clear_tint_int(struct rzv2h_icu_priv *priv, unsigned int hwirq)
@@ -389,49 +428,82 @@ static int rzv2h_tint_set_type(struct irq_data *d, unsigned int type)
titsr_k = ICU_TITSR_K(tint_nr);
titsel_n = ICU_TITSR_TITSEL_N(tint_nr);
- guard(raw_spinlock)(&priv->lock);
+ scoped_guard(raw_spinlock, &priv->lock) {
+ tssr = readl_relaxed(priv->base + priv->info->t_offs + ICU_TSSR(tssr_k));
+ titsr = readl_relaxed(priv->base + priv->info->t_offs + ICU_TITSR(titsr_k));
- tssr = readl_relaxed(priv->base + priv->info->t_offs + ICU_TSSR(tssr_k));
- titsr = readl_relaxed(priv->base + priv->info->t_offs + ICU_TITSR(titsr_k));
+ tssr_cur = field_get(ICU_TSSR_TSSEL_MASK(tssel_n, priv->info->field_width), tssr);
+ titsr_cur = field_get(ICU_TITSR_TITSEL_MASK(titsel_n), titsr);
+ if (tssr_cur == tint && titsr_cur == sense)
+ goto set_parent_type;
- tssr_cur = field_get(ICU_TSSR_TSSEL_MASK(tssel_n, priv->info->field_width), tssr);
- titsr_cur = field_get(ICU_TITSR_TITSEL_MASK(titsel_n), titsr);
- if (tssr_cur == tint && titsr_cur == sense)
- return 0;
+ tssr &= ~(ICU_TSSR_TSSEL_MASK(tssel_n, priv->info->field_width) | tien);
+ tssr |= ICU_TSSR_TSSEL_PREP(tint, tssel_n, priv->info->field_width);
+
+ writel_relaxed(tssr, priv->base + priv->info->t_offs + ICU_TSSR(tssr_k));
+
+ titsr &= ~ICU_TITSR_TITSEL_MASK(titsel_n);
+ titsr |= ICU_TITSR_TITSEL_PREP(sense, titsel_n);
- tssr &= ~(ICU_TSSR_TSSEL_MASK(tssel_n, priv->info->field_width) | tien);
- tssr |= ICU_TSSR_TSSEL_PREP(tint, tssel_n, priv->info->field_width);
+ writel_relaxed(titsr, priv->base + priv->info->t_offs + ICU_TITSR(titsr_k));
+
+ rzv2h_clear_tint_int(priv, hwirq);
+
+ writel_relaxed(tssr | tien, priv->base + priv->info->t_offs + ICU_TSSR(tssr_k));
+ }
+set_parent_type:
+ return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH);
+}
- writel_relaxed(tssr, priv->base + priv->info->t_offs + ICU_TSSR(tssr_k));
+static int rzv2h_icu_swint_set_irqchip_state(struct irq_data *d, enum irqchip_irq_state which,
+ bool state)
+{
+ unsigned int hwirq = irqd_to_hwirq(d);
+ struct rzv2h_icu_priv *priv;
+ unsigned int bit;
- titsr &= ~ICU_TITSR_TITSEL_MASK(titsel_n);
- titsr |= ICU_TITSR_TITSEL_PREP(sense, titsel_n);
+ if (which != IRQCHIP_STATE_PENDING)
+ return irq_chip_set_parent_state(d, which, state);
- writel_relaxed(titsr, priv->base + priv->info->t_offs + ICU_TITSR(titsr_k));
+ if (!state)
+ return 0;
- rzv2h_clear_tint_int(priv, hwirq);
+ priv = irq_data_to_priv(d);
+ bit = BIT(hwirq - ICU_CA55_INT_START);
- writel_relaxed(tssr | tien, priv->base + priv->info->t_offs + ICU_TSSR(tssr_k));
+ /* Trigger the software interrupt */
+ writel_relaxed(bit, priv->base + ICU_SWINT);
return 0;
}
-static int rzv2h_icu_set_type(struct irq_data *d, unsigned int type)
+static int rzv2h_icu_swpe_set_irqchip_state(struct irq_data *d, enum irqchip_irq_state which,
+ bool state)
{
- unsigned int hw_irq = irqd_to_hwirq(d);
- int ret;
+ struct rzv2h_icu_priv *priv;
+ unsigned int bit;
+ static u8 swpe;
- if (hw_irq >= ICU_TINT_START)
- ret = rzv2h_tint_set_type(d, type);
- else if (hw_irq >= ICU_IRQ_START)
- ret = rzv2h_irq_set_type(d, type);
- else
- ret = rzv2h_nmi_set_type(d, type);
+ if (which != IRQCHIP_STATE_PENDING)
+ return irq_chip_set_parent_state(d, which, state);
- if (ret)
- return ret;
+ if (!state)
+ return 0;
- return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH);
+ priv = irq_data_to_priv(d);
+
+ bit = BIT(swpe);
+ /*
+ * SWPE has 16 bits; the bit position is rotated on each trigger
+ * and wraps around once all bits have been used.
+ */
+ if (++swpe >= ICU_SWPE_NUM)
+ swpe = 0;
+
+ /* Trigger the pseudo error interrupt */
+ writel_relaxed(bit, priv->base + ICU_SWPE);
+
+ return 0;
}
static int rzv2h_irqc_irq_suspend(void *data)
@@ -472,27 +544,98 @@ static struct syscore rzv2h_irqc_syscore = {
.ops = &rzv2h_irqc_syscore_ops,
};
-static const struct irq_chip rzv2h_icu_chip = {
+static const struct irq_chip rzv2h_icu_tint_chip = {
+ .name = "rzv2h-icu",
+ .irq_eoi = rzv2h_icu_tint_eoi,
+ .irq_mask = irq_chip_mask_parent,
+ .irq_unmask = irq_chip_unmask_parent,
+ .irq_disable = rzv2h_icu_tint_disable,
+ .irq_enable = rzv2h_icu_tint_enable,
+ .irq_get_irqchip_state = irq_chip_get_parent_state,
+ .irq_set_irqchip_state = irq_chip_set_parent_state,
+ .irq_retrigger = irq_chip_retrigger_hierarchy,
+ .irq_set_type = rzv2h_tint_set_type,
+ .irq_set_affinity = irq_chip_set_affinity_parent,
+ .flags = IRQCHIP_MASK_ON_SUSPEND |
+ IRQCHIP_SET_TYPE_MASKED |
+ IRQCHIP_SKIP_SET_WAKE,
+};
+
+static const struct irq_chip rzv2h_icu_irq_chip = {
.name = "rzv2h-icu",
- .irq_eoi = rzv2h_icu_eoi,
+ .irq_eoi = rzv2h_icu_irq_eoi,
.irq_mask = irq_chip_mask_parent,
.irq_unmask = irq_chip_unmask_parent,
- .irq_disable = rzv2h_icu_irq_disable,
- .irq_enable = rzv2h_icu_irq_enable,
+ .irq_disable = irq_chip_disable_parent,
+ .irq_enable = irq_chip_enable_parent,
.irq_get_irqchip_state = irq_chip_get_parent_state,
.irq_set_irqchip_state = irq_chip_set_parent_state,
.irq_retrigger = irq_chip_retrigger_hierarchy,
- .irq_set_type = rzv2h_icu_set_type,
+ .irq_set_type = rzv2h_irq_set_type,
.irq_set_affinity = irq_chip_set_affinity_parent,
.flags = IRQCHIP_MASK_ON_SUSPEND |
IRQCHIP_SET_TYPE_MASKED |
IRQCHIP_SKIP_SET_WAKE,
};
+static const struct irq_chip rzv2h_icu_nmi_chip = {
+ .name = "rzv2h-icu",
+ .irq_eoi = rzv2h_icu_nmi_eoi,
+ .irq_mask = irq_chip_mask_parent,
+ .irq_unmask = irq_chip_unmask_parent,
+ .irq_disable = irq_chip_disable_parent,
+ .irq_enable = irq_chip_enable_parent,
+ .irq_get_irqchip_state = irq_chip_get_parent_state,
+ .irq_set_irqchip_state = irq_chip_set_parent_state,
+ .irq_retrigger = irq_chip_retrigger_hierarchy,
+ .irq_set_type = rzv2h_nmi_set_type,
+ .irq_set_affinity = irq_chip_set_affinity_parent,
+ .flags = IRQCHIP_MASK_ON_SUSPEND |
+ IRQCHIP_SET_TYPE_MASKED |
+ IRQCHIP_SKIP_SET_WAKE,
+};
+
+static const struct irq_chip rzv2h_icu_swint_chip = {
+ .name = "rzv2h-icu",
+ .irq_eoi = irq_chip_eoi_parent,
+ .irq_mask = irq_chip_mask_parent,
+ .irq_unmask = irq_chip_unmask_parent,
+ .irq_disable = irq_chip_disable_parent,
+ .irq_enable = irq_chip_enable_parent,
+ .irq_get_irqchip_state = irq_chip_get_parent_state,
+ .irq_set_irqchip_state = rzv2h_icu_swint_set_irqchip_state,
+ .irq_retrigger = irq_chip_retrigger_hierarchy,
+ .irq_set_type = irq_chip_set_type_parent,
+ .irq_set_affinity = irq_chip_set_affinity_parent,
+ .flags = IRQCHIP_MASK_ON_SUSPEND |
+ IRQCHIP_SET_TYPE_MASKED |
+ IRQCHIP_SKIP_SET_WAKE,
+};
+
+static const struct irq_chip rzv2h_icu_swpe_err_chip = {
+ .name = "rzv2h-icu",
+ .irq_eoi = irq_chip_eoi_parent,
+ .irq_mask = irq_chip_mask_parent,
+ .irq_unmask = irq_chip_unmask_parent,
+ .irq_disable = irq_chip_disable_parent,
+ .irq_enable = irq_chip_enable_parent,
+ .irq_get_irqchip_state = irq_chip_get_parent_state,
+ .irq_set_irqchip_state = rzv2h_icu_swpe_set_irqchip_state,
+ .irq_retrigger = irq_chip_retrigger_hierarchy,
+ .irq_set_type = irq_chip_set_type_parent,
+ .irq_set_affinity = irq_chip_set_affinity_parent,
+ .flags = IRQCHIP_MASK_ON_SUSPEND |
+ IRQCHIP_SET_TYPE_MASKED |
+ IRQCHIP_SKIP_SET_WAKE,
+};
+
+#define hwirq_within(hwirq, which) ((hwirq) >= which##_START && (hwirq) <= which##_LAST)
+
static int rzv2h_icu_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs,
void *arg)
{
struct rzv2h_icu_priv *priv = domain->host_data;
+ const struct irq_chip *chip;
unsigned long tint = 0;
irq_hw_number_t hwirq;
unsigned int type;
@@ -508,19 +651,27 @@ static int rzv2h_icu_alloc(struct irq_domain *domain, unsigned int virq, unsigne
* hwirq is embedded in bits 0-15.
* TINT is embedded in bits 16-31.
*/
- if (hwirq >= ICU_TINT_START) {
- tint = ICU_TINT_EXTRACT_GPIOINT(hwirq);
+ tint = ICU_TINT_EXTRACT_GPIOINT(hwirq);
+ if (tint || hwirq_within(hwirq, ICU_TINT)) {
hwirq = ICU_TINT_EXTRACT_HWIRQ(hwirq);
- if (hwirq < ICU_TINT_START)
+ if (!hwirq_within(hwirq, ICU_TINT))
return -EINVAL;
+ chip = &rzv2h_icu_tint_chip;
+ } else if (hwirq_within(hwirq, ICU_IRQ)) {
+ chip = &rzv2h_icu_irq_chip;
+ } else if (hwirq_within(hwirq, ICU_CA55_INT)) {
+ chip = &rzv2h_icu_swint_chip;
+ } else if (hwirq_within(hwirq, ICU_ERR_INT)) {
+ chip = &rzv2h_icu_swpe_err_chip;
+ } else {
+ chip = &rzv2h_icu_nmi_chip;
}
if (hwirq > (ICU_NUM_IRQ - 1))
return -EINVAL;
- ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, &rzv2h_icu_chip,
- (void *)(uintptr_t)tint);
+ ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, chip, (void *)(uintptr_t)tint);
if (ret)
return ret;
@@ -550,62 +701,160 @@ static int rzv2h_icu_parse_interrupts(struct rzv2h_icu_priv *priv, struct device
return 0;
}
+static irqreturn_t rzv2h_icu_error_irq(int irq, void *data)
+{
+ struct rzv2h_icu_priv *priv = data;
+ const struct rzv2h_hw_info *hw_info = priv->info;
+ void __iomem *base = priv->base;
+ unsigned int k;
+ u32 st;
+
+ /* 1) Bus errors (BEISR0..3) */
+ for (k = 0; k < ICU_NUM_BE; k++) {
+ st = readl(base + ICU_BEISR(k));
+ if (!st)
+ continue;
+
+ writel_relaxed(st, base + ICU_BECLR(k));
+ pr_warn("rzv2h-icu: BUS error k=%u status=0x%08x\n", k, st);
+ }
+
+ /* 2) ECC RAM errors (EREISR0..X) */
+ for (k = hw_info->ecc_start; k <= hw_info->ecc_end; k++) {
+ st = readl(base + ICU_EREISR(k));
+ if (!st)
+ continue;
+
+ writel_relaxed(st, base + ICU_ERCLR(k));
+ pr_warn("rzv2h-icu: ECC error k=%u status=0x%08x\n", k, st);
+ }
+
+ /* 3) IP/CA55 error interrupt status (ERINTA55CTL0..3) */
+ for (k = 0; k < ICU_NUM_A55ERR; k++) {
+ st = readl(base + ICU_ERINTA55CTL(k));
+ if (!st)
+ continue;
+
+ /* there is no relation with status bits so clear all the interrupts */
+ writel_relaxed(0xffffffff, base + ICU_ERINTA55CRL(k));
+ pr_warn("rzv2h-icu: IP/CA55 error k=%u status=0x%08x\n", k, st);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t rzv2h_icu_swint_irq(int irq, void *data)
+{
+ unsigned int cpu = (uintptr_t)data;
+
+ pr_info("SWINT interrupt for CA55 core %u\n", cpu);
+ return IRQ_HANDLED;
+}
+
+static int rzv2h_icu_setup_irqs(struct platform_device *pdev, struct irq_domain *irq_domain)
+{
+ const struct rzv2h_hw_info *hw_info = rzv2h_icu_data->info;
+ bool irq_inject = IS_ENABLED(CONFIG_GENERIC_IRQ_INJECTION);
+ void __iomem *base = rzv2h_icu_data->base;
+ struct device *dev = &pdev->dev;
+ struct irq_fwspec fwspec;
+ unsigned int i, virq;
+ int ret;
+
+ for (i = 0; i < ICU_CA55_INT_COUNT && irq_inject; i++) {
+ fwspec.fwnode = irq_domain->fwnode;
+ fwspec.param_count = 2;
+ fwspec.param[0] = ICU_CA55_INT_START + i;
+ fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
+
+ virq = irq_create_fwspec_mapping(&fwspec);
+ if (!virq) {
+ return dev_err_probe(dev, -EINVAL,
+ "failed to create int-ca55-%u IRQ mapping\n", i);
+ }
+
+ ret = devm_request_irq(dev, virq, rzv2h_icu_swint_irq, 0, dev_name(dev),
+ (void *)(uintptr_t)i);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to request int-ca55-%u IRQ\n", i);
+ }
+
+ /* Unmask and clear all IP/CA55 error interrupts */
+ for (i = 0; i < ICU_NUM_A55ERR; i++) {
+ writel_relaxed(0xffffff, base + ICU_ERINTA55CRL(i));
+ writel_relaxed(0x0, base + ICU_ERINTA55MSK(i));
+ }
+
+ /* Clear all Bus errors */
+ for (i = 0; i < ICU_NUM_BE; i++)
+ writel_relaxed(0xffffffff, base + ICU_BECLR(i));
+
+ /* Clear all ECCRAM errors */
+ for (i = hw_info->ecc_start; i <= hw_info->ecc_end; i++)
+ writel_relaxed(0xffffffff, base + ICU_ERCLR(i));
+
+ fwspec.fwnode = irq_domain->fwnode;
+ fwspec.param_count = 2;
+ fwspec.param[0] = ICU_ERR_INT_START;
+ fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH;
+
+ virq = irq_create_fwspec_mapping(&fwspec);
+ if (!virq)
+ return dev_err_probe(dev, -EINVAL, "failed to create icu-error-ca55 IRQ mapping\n");
+
+ ret = devm_request_irq(dev, virq, rzv2h_icu_error_irq, 0, dev_name(dev), rzv2h_icu_data);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to request icu-error-ca55 IRQ\n");
+
+ return 0;
+}
+
static int rzv2h_icu_probe_common(struct platform_device *pdev, struct device_node *parent,
const struct rzv2h_hw_info *hw_info)
{
struct irq_domain *irq_domain, *parent_domain;
struct device_node *node = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
struct reset_control *resetn;
int ret;
parent_domain = irq_find_host(parent);
- if (!parent_domain) {
- dev_err(&pdev->dev, "cannot find parent domain\n");
- return -ENODEV;
- }
+ if (!parent_domain)
+ return dev_err_probe(dev, -ENODEV, "cannot find parent domain\n");
- rzv2h_icu_data = devm_kzalloc(&pdev->dev, sizeof(*rzv2h_icu_data), GFP_KERNEL);
+ rzv2h_icu_data = devm_kzalloc(dev, sizeof(*rzv2h_icu_data), GFP_KERNEL);
if (!rzv2h_icu_data)
return -ENOMEM;
platform_set_drvdata(pdev, rzv2h_icu_data);
- rzv2h_icu_data->base = devm_of_iomap(&pdev->dev, pdev->dev.of_node, 0, NULL);
+ rzv2h_icu_data->base = devm_of_iomap(dev, node, 0, NULL);
if (IS_ERR(rzv2h_icu_data->base))
return PTR_ERR(rzv2h_icu_data->base);
ret = rzv2h_icu_parse_interrupts(rzv2h_icu_data, node);
- if (ret) {
- dev_err(&pdev->dev, "cannot parse interrupts: %d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "cannot parse interrupts\n");
- resetn = devm_reset_control_get_exclusive_deasserted(&pdev->dev, NULL);
- if (IS_ERR(resetn)) {
- ret = PTR_ERR(resetn);
- dev_err(&pdev->dev, "failed to acquire deasserted reset: %d\n", ret);
- return ret;
- }
+ resetn = devm_reset_control_get_exclusive_deasserted(dev, NULL);
+ if (IS_ERR(resetn))
+ return dev_err_probe(dev, PTR_ERR(resetn), "failed to acquire deasserted reset\n");
- ret = devm_pm_runtime_enable(&pdev->dev);
- if (ret < 0) {
- dev_err(&pdev->dev, "devm_pm_runtime_enable failed, %d\n", ret);
- return ret;
- }
+ ret = devm_pm_runtime_enable(dev);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "devm_pm_runtime_enable failed\n");
- ret = pm_runtime_resume_and_get(&pdev->dev);
- if (ret < 0) {
- dev_err(&pdev->dev, "pm_runtime_resume_and_get failed: %d\n", ret);
- return ret;
- }
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "pm_runtime_resume_and_get failed\n");
raw_spin_lock_init(&rzv2h_icu_data->lock);
irq_domain = irq_domain_create_hierarchy(parent_domain, 0, ICU_NUM_IRQ,
- dev_fwnode(&pdev->dev), &rzv2h_icu_domain_ops,
+ dev_fwnode(dev), &rzv2h_icu_domain_ops,
rzv2h_icu_data);
if (!irq_domain) {
- dev_err(&pdev->dev, "failed to add irq domain\n");
+ dev_err(dev, "failed to add irq domain\n");
ret = -ENOMEM;
goto pm_put;
}
@@ -614,15 +863,18 @@ static int rzv2h_icu_probe_common(struct platform_device *pdev, struct device_no
register_syscore(&rzv2h_irqc_syscore);
+ ret = rzv2h_icu_setup_irqs(pdev, irq_domain);
+ if (ret)
+ goto pm_put;
+
/*
* coccicheck complains about a missing put_device call before returning, but it's a false
- * positive. We still need &pdev->dev after successfully returning from this function.
+ * positive. We still need dev after successfully returning from this function.
*/
return 0;
pm_put:
- pm_runtime_put_sync(&pdev->dev);
-
+ pm_runtime_put_sync(dev);
return ret;
}
@@ -657,12 +909,24 @@ static const struct rzv2h_hw_info rzg3e_hw_params = {
.t_offs = ICU_RZG3E_TINT_OFFSET,
.max_tssel = ICU_RZG3E_TSSEL_MAX_VAL,
.field_width = 16,
+ .ecc_start = 1,
+ .ecc_end = 4,
+};
+
+static const struct rzv2h_hw_info rzv2n_hw_params = {
+ .t_offs = 0,
+ .max_tssel = ICU_RZV2H_TSSEL_MAX_VAL,
+ .field_width = 8,
+ .ecc_start = 0,
+ .ecc_end = 2,
};
static const struct rzv2h_hw_info rzv2h_hw_params = {
.t_offs = 0,
.max_tssel = ICU_RZV2H_TSSEL_MAX_VAL,
.field_width = 8,
+ .ecc_start = 0,
+ .ecc_end = 11,
};
static int rzg3e_icu_probe(struct platform_device *pdev, struct device_node *parent)
@@ -670,6 +934,11 @@ static int rzg3e_icu_probe(struct platform_device *pdev, struct device_node *par
return rzv2h_icu_probe_common(pdev, parent, &rzg3e_hw_params);
}
+static int rzv2n_icu_probe(struct platform_device *pdev, struct device_node *parent)
+{
+ return rzv2h_icu_probe_common(pdev, parent, &rzv2n_hw_params);
+}
+
static int rzv2h_icu_probe(struct platform_device *pdev, struct device_node *parent)
{
return rzv2h_icu_probe_common(pdev, parent, &rzv2h_hw_params);
@@ -677,7 +946,7 @@ static int rzv2h_icu_probe(struct platform_device *pdev, struct device_node *par
IRQCHIP_PLATFORM_DRIVER_BEGIN(rzv2h_icu)
IRQCHIP_MATCH("renesas,r9a09g047-icu", rzg3e_icu_probe)
-IRQCHIP_MATCH("renesas,r9a09g056-icu", rzv2h_icu_probe)
+IRQCHIP_MATCH("renesas,r9a09g056-icu", rzv2n_icu_probe)
IRQCHIP_MATCH("renesas,r9a09g057-icu", rzv2h_icu_probe)
IRQCHIP_PLATFORM_DRIVER_END(rzv2h_icu)
MODULE_AUTHOR("Fabrizio Castro <fabrizio.castro.jz@xxxxxxxxxxx>");