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 v4 6/8] irqchip/al-fic: support error and fatal outputs and FIC v2
Date: Thu, 8 Oct 2026 09:00:56 +0000 [thread overview]
Message-ID: <20261008090058.38591-7-farbere@amazon.com> (raw)
In-Reply-To: <20261008090058.38591-1-farbere@amazon.com>
FIC v2 hardware adds two interrupt outputs on top of the info output: an
error output and a fatal output, each with its own mask register
(AL_FIC_ERROR_MASK, AL_FIC_FATAL_MASK). A group drives exactly one of the
three outputs.
Read which output a group drives from the amazon,al-fic-mask devicetree
property (info, error or fatal; absent means info) and program the
matching mask register. Detect the hardware revision from the CONTROL
register version field (bits 28-29). The revision is not in the
devicetree, so requesting the error or fatal output on a v1 device - which
has neither - is rejected at probe against the register.
Name the selected output in the probe log line, in place of the "Legacy
mode" text it replaces. A booted system then shows which output each group
drives.
The "v1" and "v2" names are this driver's labels for the CONTROL
version field encoding (0 and 1).
The driver seeds mask_cache from the value it programs rather than having
the generic chip read the mask register back, so the error and fatal mask
registers are never read - which also sidesteps the v2 erratum where they
always read as 0 regardless of their contents.
Reading CONTROL to get the version field turns the write that follows into
a read-modify-write instead of a value built from CONTROL_MASK_MSI_X
alone. Every RW bit in this register resets to 0, so the two are
equivalent at probe time; the read-modify-write is kept anyway as the
better practice; it costs nothing and does not depend on the reset value
staying 0.
Signed-off-by: Eliav Farber <farbere@amazon.com>
---
v4: drop the gc_flags local and the revision-gated condition that cleared
IRQ_GC_INIT_MASK_CACHE for the v2 error and fatal outputs. Patch 4 now
seeds mask_cache unconditionally and does not ask for the flag at all,
so there is nothing left for this patch to gate. The enum
al_fic_version parameter on al_fic_register() goes with it, since the
condition was its only user. The v2 read-back erratum is now stated in
the commit message as a consequence of seeding rather than as the
reason for a per-revision workaround.
v3:
- Initialise gc_flags to IRQ_GC_INIT_MASK_CACHE at its declaration and
only clear it on the FIC v2 error/fatal path, dropping the else
branch.
- Use ~0U instead of 0xFFFFFFFF, for the mask_cache seed and for the
three mask register writes.
- Explain the control register read-modify-write in the commit message.
Every writable bit in that register resets to 0, so preserving the
other bits is equivalent to the previous plain write at probe time. It
is better practice, not a behaviour fix.
v2:
- Fix the v2 mask_cache workaround, which was dead in v1. mask_cache is
not seeded until the first child mapping (irq_map_generic_chip), so the
v1 override was overwritten with 0 and the first unmask then cleared the
whole mask register, unmasking all 32 sources. Drop
IRQ_GC_INIT_MASK_CACHE for the error and fatal outputs and seed
mask_cache from the value al_fic_wire_init() programmed. Commit message
rewritten to state the real timing and consequence.
- Read the output from the new amazon,al-fic-mask property (was per-output
compatible in v1).
- Label the mask in the probe log line (mask=%s).
drivers/irqchip/irq-al-fic.c | 108 +++++++++++++++++++++++++++++++----
1 file changed, 98 insertions(+), 10 deletions(-)
diff --git a/drivers/irqchip/irq-al-fic.c b/drivers/irqchip/irq-al-fic.c
index fda9c05a639f..d5671b9624db 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_INFO_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,6 +36,37 @@ enum al_fic_state {
AL_FIC_CONFIGURED_RISING_EDGE,
};
+/*
+ * FIC hardware revision, as reported by the CONTROL register version field
+ * (CONTROL_VERSION_ID, bits 29-28). These are this driver's names for that
+ * field's encoding.
+ */
+enum al_fic_version {
+ AL_FIC_VERSION_V1,
+ AL_FIC_VERSION_V2,
+};
+
+enum al_fic_id {
+ AL_FIC_ID_INFO,
+ 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_INFO] = AL_FIC_INFO_MASK,
+ [AL_FIC_ID_ERROR] = AL_FIC_ERROR_MASK,
+ [AL_FIC_ID_FATAL] = AL_FIC_FATAL_MASK,
+};
+
+/* amazon,al-fic-mask property value for each interrupt group */
+static const char * const al_fic_mask_name[AL_FIC_ID_MAX] = {
+ [AL_FIC_ID_INFO] = "info",
+ [AL_FIC_ID_ERROR] = "error",
+ [AL_FIC_ID_FATAL] = "fatal",
+};
+
struct al_fic {
void __iomem *base;
struct irq_domain *domain;
@@ -123,7 +157,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;
@@ -155,7 +190,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;
@@ -194,6 +229,8 @@ static int al_fic_register(struct device_node *node,
* @node: pointer to the interrupt controller's device tree node
* @base: mmio to fic register
* @parent_irq: interrupt of parent
+ * @fic_id: which of the controller's outputs (info, error or fatal) this
+ * group drives
*
* This API will configure the fic hardware to work in wire mode.
* In wire mode, fic hardware is generating a wire ("wired") interrupt.
@@ -202,11 +239,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,
- 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)
@@ -216,22 +255,37 @@ static struct al_fic *al_fic_wire_init(struct device_node *node,
fic->parent_irq = parent_irq;
fic->node = node;
+ control = readl_relaxed(fic->base + AL_FIC_CONTROL);
+ version_id = FIELD_GET(CONTROL_VERSION_ID, control);
+ if (version_id == AL_FIC_VERSION_V1 && fic_id != AL_FIC_ID_INFO) {
+ pr_err("%pOF: amazon,al-fic-mask = \"%s\" not available on FIC v1\n",
+ node, al_fic_mask_name[fic_id]);
+ ret = -EINVAL;
+ goto err_free;
+ }
+
/* mask out all interrupts */
- writel_relaxed(0xFFFFFFFF, fic->base + AL_FIC_MASK);
+ writel_relaxed(~0U, fic->base + AL_FIC_INFO_MASK);
+ if (version_id > AL_FIC_VERSION_V1) {
+ writel_relaxed(~0U, fic->base + AL_FIC_ERROR_MASK);
+ writel_relaxed(~0U, 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("%pOF initialized successfully in Legacy mode (parent-irq=%u)\n",
- node, parent_irq);
+ pr_info("%pOF initialized successfully (mask=%s parent-irq=%u)\n",
+ node, al_fic_mask_name[fic_id], parent_irq);
return fic;
@@ -240,11 +294,38 @@ static struct al_fic *al_fic_wire_init(struct device_node *node,
return ERR_PTR(ret);
}
+/*
+ * Parse the amazon,al-fic-mask property into an enum al_fic_id, selecting
+ * which of the controller's outputs this group drives. The property is
+ * optional; an absent property means the info output.
+ */
+static int al_fic_parse_mask(struct device_node *node, enum al_fic_id *fic_id)
+{
+ const char *mask;
+ int ret;
+
+ ret = of_property_read_string(node, "amazon,al-fic-mask", &mask);
+ if (ret == -EINVAL) {
+ *fic_id = AL_FIC_ID_INFO;
+ return 0;
+ }
+ if (ret)
+ return ret;
+
+ ret = match_string(al_fic_mask_name, AL_FIC_ID_MAX, mask);
+ if (ret < 0)
+ return ret;
+
+ *fic_id = ret;
+ return 0;
+}
+
static int __init al_fic_init_dt(struct device_node *node,
struct device_node *parent)
{
int ret;
void __iomem *base;
+ enum al_fic_id fic_id;
unsigned int parent_irq;
struct al_fic *fic;
@@ -253,6 +334,12 @@ static int __init al_fic_init_dt(struct device_node *node,
return -EINVAL;
}
+ ret = al_fic_parse_mask(node, &fic_id);
+ if (ret) {
+ pr_err("%pOF: invalid amazon,al-fic-mask\n", node);
+ return ret;
+ }
+
base = of_iomap(node, 0);
if (!base) {
pr_err("%pOF: fail to map memory\n", node);
@@ -268,7 +355,8 @@ static int __init al_fic_init_dt(struct device_node *node,
fic = al_fic_wire_init(node,
base,
- parent_irq);
+ parent_irq,
+ fic_id);
if (IS_ERR(fic)) {
pr_err("%pOF: fail to initialize irqchip (%lu)\n",
node, PTR_ERR(fic));
--
2.47.3
next prev parent reply other threads:[~2026-10-08 9:01 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-08 9:00 [PATCH v4 0/8] irqchip/al-fic: shared parent IRQ, error/fatal outputs and affinity Eliav Farber
2026-10-08 9:00 ` [PATCH v4 1/8] irqchip/al-fic: fix argument alignment and a repeated word Eliav Farber
2026-10-08 9:00 ` [PATCH v4 2/8] irqchip/al-fic: use %pOF and raise init log level Eliav Farber
2026-10-08 9:09 ` sashiko-bot
2026-10-08 9:00 ` [PATCH v4 3/8] irqchip/al-fic: keep the device_node instead of a cached name string Eliav Farber
2026-10-08 9:00 ` [PATCH v4 4/8] irqchip/al-fic: switch to shared parent interrupt Eliav Farber
2026-10-08 9:00 ` [PATCH v4 5/8] dt-bindings: interrupt-controller: amazon,al-fic: add mask selection Eliav Farber
2026-10-08 9:00 ` Eliav Farber [this message]
2026-10-08 9:00 ` [PATCH v4 7/8] irqchip/al-fic: add support for FIC v3 Eliav Farber
2026-10-08 9:00 ` [PATCH v4 8/8] irqchip/al-fic: add irq_set_affinity callback Eliav Farber
2026-10-08 9:13 ` sashiko-bot
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=20261008090058.38591-7-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