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
next prev 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®