diff options
| -rw-r--r-- | Documentation/devicetree/bindings/interrupt-controller/apple,aic2.yaml | 30 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/interrupt-controller/loongson,pch-lpc.yaml | 52 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/interrupt-controller/renesas,rzg2l-irqc.yaml | 157 | ||||
| -rw-r--r-- | arch/loongarch/kernel/irq.c | 6 | ||||
| -rw-r--r-- | arch/mips/loongson64/init.c | 6 | ||||
| -rw-r--r-- | arch/mips/pic32/Kconfig | 1 | ||||
| -rw-r--r-- | drivers/irqchip/Kconfig | 12 | ||||
| -rw-r--r-- | drivers/irqchip/irq-apple-aic.c | 24 | ||||
| -rw-r--r-- | drivers/irqchip/irq-gic-v3.c | 10 | ||||
| -rw-r--r-- | drivers/irqchip/irq-imx-irqsteer.c | 53 | ||||
| -rw-r--r-- | drivers/irqchip/irq-loongson-pch-lpc.c | 92 | ||||
| -rw-r--r-- | drivers/irqchip/irq-mvebu-odmi.c | 2 | ||||
| -rw-r--r-- | drivers/irqchip/irq-pic32-evic.c | 8 | ||||
| -rw-r--r-- | drivers/irqchip/irq-renesas-rzg2l.c | 576 | ||||
| -rw-r--r-- | drivers/irqchip/irq-renesas-rzv2h.c | 467 |
15 files changed, 1128 insertions, 368 deletions
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@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': 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@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>; + }; +... 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 @@ -185,6 +115,24 @@ allOf: 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: + contains: + enum: - renesas,r9a08g045-irqc then: properties: @@ -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 @@ 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); 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 @@ 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 */ 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 @@ 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); 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@bp.renesas.com>"); 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@renesas.com>"); |
