mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Eliav Farber <farbere@amazon.com>
To: Thomas Gleixner <tglx@kernel.org>, Talel Shenhar <talel@amazon.com>
Cc: Radu Rendec <radu@rendec.net>, Rob Herring <robh@kernel.org>,
	"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>,
	"Eliav Farber" <farbere@amazon.com>, <devicetree@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>
Subject: [PATCH 4/6] irqchip/al-fic: add support for FIC v2
Date: Thu, 24 Sep 2026 06:23:09 +0000	[thread overview]
Message-ID: <20260924062311.37308-5-farbere@amazon.com> (raw)
In-Reply-To: <20260924062311.37308-1-farbere@amazon.com>

FIC v2 hardware adds two additional interrupt groups on top of the
legacy group: an "error" group and a "fatal" group, each with its own
mask register (AL_FIC_ERROR_MASK, AL_FIC_FATAL_MASK). A single FIC
instance exposes one of these groups, selected by the compatible string.

Detect the hardware version from the CONTROL register version id field
(bits 28-29) and, on v2, mask out the error and fatal groups during
initialization.

The driver now distinguishes the three groups via enum al_fic_id, wired
up through three IRQCHIP_DECLARE entries:

  amazon,al-fic         - legacy group (existing, unchanged behaviour)
  amazon,al-fic-error   - error group  (v2+)
  amazon,al-fic-fatal   - fatal group  (v2+)

Requesting an error/fatal group on a v1 device is rejected.

On v2 the error and fatal mask registers always read back as 0,
regardless of their actual contents, so IRQ_GC_INIT_MASK_CACHE seeds
mask_cache with 0 and makes every source appear unmasked on the next
access. Since al_fic_wire_init() has already masked all sources, override
mask_cache to match for these two groups and let the driver maintain it.
The legacy mask register is not affected, so the legacy group keeps the
register-seeded mask_cache.

Signed-off-by: Eliav Farber <farbere@amazon.com>
---
 drivers/irqchip/irq-al-fic.c | 101 ++++++++++++++++++++++++++++++-----
 1 file changed, 89 insertions(+), 12 deletions(-)

diff --git a/drivers/irqchip/irq-al-fic.c b/drivers/irqchip/irq-al-fic.c
index 9cf3eedac97f..90a64350be63 100644
--- a/drivers/irqchip/irq-al-fic.c
+++ b/drivers/irqchip/irq-al-fic.c
@@ -16,11 +16,14 @@
 /* FIC Registers */
 #define AL_FIC_CAUSE		0x00
 #define AL_FIC_SET_CAUSE	0x08
-#define AL_FIC_MASK		0x10
+#define AL_FIC_LEGACY_MASK	0x10
 #define AL_FIC_CONTROL		0x28
+#define AL_FIC_ERROR_MASK	0x2c
+#define AL_FIC_FATAL_MASK	0x34
 
 #define CONTROL_TRIGGER_RISING	BIT(3)
 #define CONTROL_MASK_MSI_X	BIT(5)
+#define CONTROL_VERSION_ID	GENMASK(29, 28)
 
 #define NR_FIC_IRQS 32
 
@@ -33,12 +36,32 @@ enum al_fic_state {
 	AL_FIC_CONFIGURED_RISING_EDGE,
 };
 
+enum al_fic_version {
+	AL_FIC_VERSION_V1,
+	AL_FIC_VERSION_V2,
+};
+
+enum al_fic_id {
+	AL_FIC_ID_LEGACY,
+	AL_FIC_ID_ERROR,
+	AL_FIC_ID_FATAL,
+	AL_FIC_ID_MAX,		/* keep last */
+};
+
+/* Mask register offset for each interrupt group */
+static const unsigned int al_fic_mask_offset[AL_FIC_ID_MAX] = {
+	[AL_FIC_ID_LEGACY] = AL_FIC_LEGACY_MASK,
+	[AL_FIC_ID_ERROR]  = AL_FIC_ERROR_MASK,
+	[AL_FIC_ID_FATAL]  = AL_FIC_FATAL_MASK,
+};
+
 struct al_fic {
 	void __iomem *base;
 	struct irq_domain *domain;
 	const char *name;
 	unsigned int parent_irq;
 	enum al_fic_state state;
+	enum al_fic_version version;
 };
 
 static void al_fic_set_trigger(struct al_fic *fic,
@@ -126,7 +149,8 @@ static int al_fic_irq_retrigger(struct irq_data *data)
 }
 
 static int al_fic_register(struct device_node *node,
-			   struct al_fic *fic)
+			   struct al_fic *fic,
+			   enum al_fic_id fic_id)
 {
 	struct irq_chip_generic *gc;
 	int ret;
@@ -152,7 +176,7 @@ static int al_fic_register(struct device_node *node,
 
 	gc = irq_get_domain_generic_chip(fic->domain, 0);
 	gc->reg_base = fic->base;
-	gc->chip_types->regs.mask = AL_FIC_MASK;
+	gc->chip_types->regs.mask = al_fic_mask_offset[fic_id];
 	gc->chip_types->regs.ack = AL_FIC_CAUSE;
 	gc->chip_types->chip.irq_mask = irq_gc_mask_set_bit;
 	gc->chip_types->chip.irq_unmask = irq_gc_mask_clr_bit;
@@ -162,6 +186,20 @@ static int al_fic_register(struct device_node *node,
 	gc->chip_types->chip.flags = IRQCHIP_SKIP_SET_WAKE;
 	gc->private = fic;
 
+	/*
+	 * On FIC v2 the error and fatal mask registers always read back as 0,
+	 * regardless of their actual contents, so IRQ_GC_INIT_MASK_CACHE seeds
+	 * mask_cache with 0 and makes every source appear unmasked on the next
+	 * access. al_fic_wire_init() has already masked all sources, so override
+	 * mask_cache to match and let the driver maintain it from here.
+	 *
+	 * The legacy mask register is not affected, and the erratum is fixed
+	 * from v3 onwards, so those paths keep the register-seeded mask_cache.
+	 */
+	if (fic->version == AL_FIC_VERSION_V2 &&
+	    (fic_id == AL_FIC_ID_ERROR || fic_id == AL_FIC_ID_FATAL))
+		gc->mask_cache = 0xFFFFFFFF;
+
 	ret = request_irq(fic->parent_irq, al_fic_irq_handler,
 			  IRQF_NO_THREAD | IRQF_SHARED, fic->name, fic);
 	if (ret) {
@@ -194,11 +232,13 @@ static int al_fic_register(struct device_node *node,
 static struct al_fic *al_fic_wire_init(struct device_node *node,
 				       void __iomem *base,
 				       const char *name,
-				       unsigned int parent_irq)
+				       unsigned int parent_irq,
+				       enum al_fic_id fic_id)
 {
 	struct al_fic *fic;
+	u32 version_id;
+	u32 control;
 	int ret;
-	u32 control = CONTROL_MASK_MSI_X;
 
 	fic = kzalloc_obj(*fic);
 	if (!fic)
@@ -208,22 +248,37 @@ static struct al_fic *al_fic_wire_init(struct device_node *node,
 	fic->parent_irq = parent_irq;
 	fic->name = name;
 
+	control = readl_relaxed(fic->base + AL_FIC_CONTROL);
+	version_id = FIELD_GET(CONTROL_VERSION_ID, control);
+	fic->version = version_id;
+	if (version_id == AL_FIC_VERSION_V1 && fic_id != AL_FIC_ID_LEGACY) {
+		pr_err("%s unexpected fic id (%d)\n", fic->name, fic_id);
+		ret = -EINVAL;
+		goto err_free;
+	}
+
 	/* mask out all interrupts */
-	writel_relaxed(0xFFFFFFFF, fic->base + AL_FIC_MASK);
+	writel_relaxed(0xFFFFFFFF, fic->base + AL_FIC_LEGACY_MASK);
+	if (version_id > AL_FIC_VERSION_V1) {
+		writel_relaxed(0xFFFFFFFF, fic->base + AL_FIC_ERROR_MASK);
+		writel_relaxed(0xFFFFFFFF, fic->base + AL_FIC_FATAL_MASK);
+	}
 
 	/* clear any pending interrupt */
 	writel_relaxed(0, fic->base + AL_FIC_CAUSE);
 
+	/* make sure the controller works in non msi_x mode */
+	control |= CONTROL_MASK_MSI_X;
 	writel_relaxed(control, fic->base + AL_FIC_CONTROL);
 
-	ret = al_fic_register(node, fic);
+	ret = al_fic_register(node, fic, fic_id);
 	if (ret) {
 		pr_err("fail to register irqchip\n");
 		goto err_free;
 	}
 
-	pr_info("%s initialized successfully in Legacy mode (parent-irq=%u)\n",
-		fic->name, parent_irq);
+	pr_info("%s initialized successfully (fic_id=%d parent-irq=%u)\n",
+		fic->name, fic_id, parent_irq);
 
 	return fic;
 
@@ -233,7 +288,8 @@ static struct al_fic *al_fic_wire_init(struct device_node *node,
 }
 
 static int __init al_fic_init_dt(struct device_node *node,
-				 struct device_node *parent)
+				 struct device_node *parent,
+				 enum al_fic_id fic_id)
 {
 	int ret;
 	void __iomem *base;
@@ -262,7 +318,8 @@ static int __init al_fic_init_dt(struct device_node *node,
 	fic = al_fic_wire_init(node,
 			       base,
 			       node->full_name,
-			       parent_irq);
+			       parent_irq,
+			       fic_id);
 	if (IS_ERR(fic)) {
 		pr_err("%s: fail to initialize irqchip (%lu)\n",
 		       node->full_name,
@@ -281,4 +338,24 @@ static int __init al_fic_init_dt(struct device_node *node,
 	return ret;
 }
 
-IRQCHIP_DECLARE(al_fic, "amazon,al-fic", al_fic_init_dt);
+static int __init al_fic_init_dt_legacy(struct device_node *node,
+					struct device_node *parent)
+{
+	return al_fic_init_dt(node, parent, AL_FIC_ID_LEGACY);
+}
+
+static int __init al_fic_init_dt_error(struct device_node *node,
+				       struct device_node *parent)
+{
+	return al_fic_init_dt(node, parent, AL_FIC_ID_ERROR);
+}
+
+static int __init al_fic_init_dt_fatal(struct device_node *node,
+				       struct device_node *parent)
+{
+	return al_fic_init_dt(node, parent, AL_FIC_ID_FATAL);
+}
+
+IRQCHIP_DECLARE(al_fic_legacy, "amazon,al-fic", al_fic_init_dt_legacy);
+IRQCHIP_DECLARE(al_fic_error, "amazon,al-fic-error", al_fic_init_dt_error);
+IRQCHIP_DECLARE(al_fic_fatal, "amazon,al-fic-fatal", al_fic_init_dt_fatal);
-- 
2.47.3


  parent reply	other threads:[~2026-09-24  6:23 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-24  6:23 [PATCH 0/6] irqchip/al-fic: shared parent IRQ, FIC v2/v3 and affinity Eliav Farber
2026-09-24  6:23 ` [PATCH 1/6] irqchip/al-fic: use full node name and raise init log level Eliav Farber
2026-09-24  6:23 ` [PATCH 2/6] irqchip/al-fic: switch to shared parent interrupt Eliav Farber
2026-09-24  6:23 ` [PATCH 3/6] dt-bindings: interrupt-controller: amazon,al-fic: add error/fatal groups Eliav Farber
2026-09-24 17:13   ` Conor Dooley
2026-09-25 10:24     ` [PATCH 3/6] dt-bindings: interrupt-controller: amazon, al-fic: " Farber, Eliav
2026-09-25 17:13       ` Conor Dooley
2026-09-24  6:23 ` Eliav Farber [this message]
2026-09-24  6:26 ` [PATCH 5/6] irqchip/al-fic: add support for FIC v3 Eliav Farber
2026-09-24  6:26   ` [PATCH 6/6] irqchip/al-fic: add irq_set_affinity callback Eliav Farber

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260924062311.37308-5-farbere@amazon.com \
    --to=farbere@amazon.com \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=radu@rendec.net \
    --cc=robh@kernel.org \
    --cc=talel@amazon.com \
    --cc=tglx@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®