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
-----BEGIN PGP SIGNATURE-----
iQJEBAABCgAuFiEEQp8+kY+LLUocC4bMphj1TA10mKEFAmnbuaAQHHRnbHhAa2Vy
bmVsLm9yZwAKCRCmGPVMDXSYoQc6D/9i6QTUNw4ZqpZSjJvrxX2mv49ej3FkWsQf
1P59ej9A0/w1+0A8SyAFd6ExuvO3am9k64nhpsFlwv0lMTu57va3Oj14E28fjN+M
H1XWJYCLX3p7MWGsGFCmbHIeJJQnwCqyiZcs3DG0BMA5iYglJEcOijB+h+FCAwCN
jjiBW4bbqPxPcpT90uQZhwwa3eUvkrkwzZEedKkbtvgPy3jdmKvrNCAte9GJSQu3
CvLgAB1Zraq1yIDFQN/km5NByan7pVmbGuv10K/jqirqmjplM2H0X+fwXlWz4M0Q
uo33xDtJvtOegnySiJ6EimY+GTHDiloZpn2nqqUnHRIR0hxYjYudukSRorxbjXWv
c93CGk1/Mq9WHqCsWvX5xe75Ttuf5xsfej8DETYvP+cTkGV/Kk9EUNmDUoYnGkiv
UZyZBUMWejJLjnNmMpouUwW9Vc/08OLAtmqJgQukl2cbh/ujcmGC0TSA2SpBKlJT
FpGU6ZsHWxpm1UHVv+9Uhx5dArRVNeTvjNgLFtu+ZkGM+C2oBs/MKHpu44mBfAf8
Ex4sFzCslKXbYo6TwO48w9VIMQJ6DDPrwza1YN6hRn4IwBkRXl3SQsT3pJxG1WMg
NcP4dMRfe68o9y4ywxnoomH+qlux4j/tq3J7PqRY5s3WzWe8jXqfiQGNE1f10rop
gGZe5f+/Mw==
=kvZ2
-----END PGP SIGNATURE-----
Merge tag 'irq-drivers-2026-04-12' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull interrupt chip driver updates from Thomas Gleixner:
- 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
* tag 'irq-drivers-2026-04-12' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (46 commits)
irqchip/irq-pic32-evic: Add __maybe_unused for board_bind_eic_interrupt in COMPILE_TEST
irqchip/renesas-rzv2h: Kill icu_err string
irqchip/renesas-rzv2h: Kill swint_names[]
irqchip/renesas-rzv2h: Kill swint_idx[]
irqchip/renesas-rzg2l: Add NMI support
irqchip/renesas-rzg2l: Clear the shared interrupt bit in rzg2l_irqc_free()
irqchip/renesas-rzg2l: Replace raw_spin_{lock,unlock} with guard() in rzg2l_irq_set_type()
irqchip/gic-v3: Print a warning for out-of-range interrupt numbers
irqchip/renesas-rzg2l: Add shared interrupt support
irqchip/renesas-rzg2l: Add RZ/G3L support
irqchip/renesas-rzg2l: Drop IRQC_IRQ_COUNT macro
irqchip/renesas-rzg2l: Drop IRQC_TINT_START macro
irqchip/renesas-rzg2l: Drop IRQC_NUM_IRQ macro
irqchip/renesas-rzg2l: Dynamically allocate fwspec array
irqchip/renesas-rzg2l: Split rzfive_irqc_{mask,unmask} into separate IRQ and TINT handlers
irqchip/renesas-rzg2l: Split rzfive_tint_irq_endisable() into separate IRQ and TINT helpers
irqchip/renesas-rzg2l: Replace rzg2l_irqc_irq_{enable,disable} with TINT-specific handlers
irqchip/renesas-rzg2l: Split set_type handler into separate IRQ and TINT functions
irqchip/renesas-rzg2l: Split EOI handler into separate IRQ and TINT functions
irqchip/renesas-rzg2l: Replace single irq_chip with per-region irq_chip instances
...
master
commit
c0ecb2a9ee
|
|
@ -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@marcan.st>
|
||||
- Janne Grunau <j@jannau.net>
|
||||
|
||||
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':
|
||||
|
|
|
|||
|
|
@ -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@flygoat.com>
|
||||
|
||||
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>;
|
||||
};
|
||||
...
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ config PIC32MZDA
|
|||
select LIBFDT
|
||||
select USE_OF
|
||||
select PINCTRL
|
||||
select PIC32_EVIC
|
||||
help
|
||||
Support for the Microchip PIC32MZDA microcontroller.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 @@ err_unmap:
|
|||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -26,19 +26,38 @@
|
|||
|
||||
#define CHAN_MAX_OUTPUT_INT 0xF
|
||||
|
||||
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;
|
||||
/* 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;
|
||||
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 },
|
||||
{},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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 @@ free_priv:
|
|||
|
||||
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 */
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ err_unmap:
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
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_tint_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_tint_interrupt(priv, hwirq);
|
||||
|
||||
irq_chip_mask_parent(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);
|
||||
|
||||
scoped_guard(raw_spinlock, &priv->lock)
|
||||
rzfive_irqc_unmask_irq_interrupt(priv, hwirq);
|
||||
else if (hwirq >= IRQC_TINT_START && hwirq < IRQC_NUM_IRQ)
|
||||
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_irqc_tint_unmask(struct irq_data *d)
|
||||
{
|
||||
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;
|
||||
scoped_guard(raw_spinlock, &priv->lock)
|
||||
rzfive_irqc_unmask_tint_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);
|
||||
}
|
||||
irq_chip_unmask_parent(d);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
guard(raw_spinlock)(&priv->lock);
|
||||
if (enable)
|
||||
rzfive_irqc_unmask_irq_interrupt(priv, hwirq);
|
||||
else
|
||||
rzfive_irqc_mask_irq_interrupt(priv, hwirq);
|
||||
}
|
||||
|
||||
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 (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_tint_set_type(struct irq_data *d, unsigned int type)
|
||||
{
|
||||
int 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_eoi,
|
||||
.irq_eoi = rzg2l_irqc_nmi_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_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 rzfive_irqc_chip = {
|
||||
static const struct irq_chip rzg2l_irqc_irq_chip = {
|
||||
.name = "rzg2l-irqc",
|
||||
.irq_eoi = rzg2l_irqc_irq_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_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 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 (ret)
|
||||
return ret;
|
||||
if (priv->info.shared_irq_cnt) {
|
||||
ret = rzg2l_irqc_shared_irq_alloc(priv, hwirq);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &priv->fwspec[hwirq]);
|
||||
ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, chip, (void *)(uintptr_t)tint);
|
||||
if (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 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@bp.renesas.com>");
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (which != IRQCHIP_STATE_PENDING)
|
||||
return irq_chip_set_parent_state(d, which, state);
|
||||
|
||||
if (!state)
|
||||
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);
|
||||
priv = irq_data_to_priv(d);
|
||||
bit = BIT(hwirq - ICU_CA55_INT_START);
|
||||
|
||||
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);
|
||||
|
||||
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));
|
||||
/* 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_eoi,
|
||||
.irq_eoi = rzv2h_icu_tint_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 = 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_icu_set_type,
|
||||
.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_irq_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_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@renesas.com>");
|
||||
|
|
|
|||
Loading…
Reference in New Issue