Commit 0e598854 authored by Biju Das's avatar Biju Das Committed by Thomas Gleixner
Browse files

irqchip/renesas-rzg2l: Add NMI support



The RZ/G2L SoC has an NMI interrupt. Add support for the NMI interrupt.

Signed-off-by: default avatarBiju Das <biju.das.jz@bp.renesas.com>
Signed-off-by: default avatarThomas Gleixner <tglx@kernel.org>
Link: https://patch.msgid.link/20260401114504.332825-1-biju.das.jz@bp.renesas.com
parent d3689cd0
Loading
Loading
Loading
Loading
+95 −4
Original line number Diff line number Diff line
@@ -20,11 +20,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
@@ -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
@@ -63,11 +70,13 @@

/**
 * struct rzg2l_irqc_reg_cache - registers cache (necessary for suspend/resume)
 * @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];
@@ -116,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;
@@ -155,6 +186,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);
@@ -341,6 +380,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);
@@ -467,11 +526,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 *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);
@@ -496,6 +567,7 @@ static void rzg2l_irqc_irq_resume(void *data)
	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 = {
@@ -507,6 +579,23 @@ static struct syscore rzg2l_irqc_syscore = {
	.ops = &rzg2l_irqc_syscore_ops,
};

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,
@@ -662,7 +751,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;