[PATCH 6.12.y-cip 26/26] irqchip/renesas-rzg2l: Add NMI support
Biju <[email protected]>
| Newsgroups | org.cip-project.lists.cip-dev |
|---|---|
| Message-ID | <[email protected]> |
From: Biju Das <[email protected]> [ Upstream commit 0e5988549dc73d985cc12c4cb438771ced74f522 ] The RZ/G2L SoC has an NMI interrupt. Add support for the NMI interrupt. Signed-off-by: Biju Das <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Biju Das <[email protected]> --- drivers/irqchip/irq-renesas-rzg2l.c | 99 +++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 4 deletions(-) diff --git a/drivers/irqchip/irq-renesas-rzg2l.c b/drivers/irqchip/irq-renesas-rzg2l.c index 93510bcd79c2..8a339e5e8946 100644 --- a/drivers/irqchip/irq-renesas-rzg2l.c +++ b/drivers/irqchip/irq-renesas-rzg2l.c @@ -21,11 +21,14 @@ #include <linux/spinlock.h> #include <linux/syscore_ops.h> +#define IRQC_NMI 0 #define IRQC_IRQ_START 1 #define IRQC_TINT_COUNT 32 #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 @@ -44,6 +47,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 @@ -64,11 +71,13 @@ /** * struct rzg2l_irqc_reg_cache - registers cache (necessary for suspend/resume) - * @iitsr: IITSR register - * @inttsel: INTTSEL 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]; @@ -117,6 +126,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; @@ -156,6 +187,14 @@ static void rzg2l_clear_tint_int(struct rzg2l_irqc_priv *priv, unsigned int hwir } } +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); @@ -342,6 +381,26 @@ static void rzg2l_irqc_tint_enable(struct irq_data *d) 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); @@ -468,11 +527,23 @@ static int rzg2l_irqc_tint_set_type(struct irq_data *d, unsigned int type) 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; + + return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH); +} + static int rzg2l_irqc_irq_suspend(void) { 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); @@ -497,6 +568,7 @@ static void rzg2l_irqc_irq_resume(void) 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 struct syscore_ops rzg2l_irqc_syscore_ops = { @@ -504,6 +576,23 @@ static struct syscore_ops rzg2l_irqc_syscore_ops = { .resume = rzg2l_irqc_irq_resume, }; +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_irq_eoi, @@ -659,7 +748,9 @@ 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 > priv->info.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); chip = priv->tint_chip; -- 2.43.0