From: Radu Rendec <radu@rendec.net>
To: Eliav Farber <farbere@amazon.com>,
Thomas Gleixner <tglx@kernel.org>,
Talel Shenhar <talel@amazon.com>
Cc: Rob Herring <robh@kernel.org>,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
Conor Dooley <conor+dt@kernel.org>,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v2 6/8] irqchip/al-fic: support error and fatal outputs and FIC v2
Date: Sun, 04 Oct 2026 21:01:44 -0400 [thread overview]
Message-ID: <a74366542ddd5e0e3fca71c5dee5d535cc9c560e.camel@rendec.net> (raw)
In-Reply-To: <20260927080637.27285-7-farbere@amazon.com>
On Sun, 2026-09-27 at 08:06 +0000, Eliav Farber wrote:
> 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).
>
> On v2 the error and fatal mask registers always read back as 0, regardless
> of their actual contents. IRQ_GC_INIT_MASK_CACHE seeds mask_cache from the
> mask register on the first child mapping, so on those two outputs it would
> seed 0: every source would appear unmasked, and the first unmask would
> write that 0 back and clear the whole mask register. Drop the flag for
> those two outputs and seed mask_cache with the value al_fic_wire_init()
> programmed instead. The info mask register is not affected, so the info
> output keeps the register-seeded mask_cache.
>
> Signed-off-by: Eliav Farber <farbere@amazon.com>
> ---
> 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 | 137 ++++++++++++++++++++++++++++++++---
> 1 file changed, 126 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/irqchip/irq-al-fic.c b/drivers/irqchip/irq-al-fic.c
> index 091a06abc0bb..35e366b4da30 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;
> @@ -126,11 +160,32 @@ 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,
> + enum al_fic_version version)
> {
> struct irq_chip_generic *gc;
> + enum irq_gc_flags gc_flags;
> int ret;
>
> + /*
> + * On FIC v2 the error and fatal mask registers always read back as 0,
> + * regardless of their actual contents. IRQ_GC_INIT_MASK_CACHE seeds
> + * mask_cache from the mask register on the first child mapping, so on
> + * those two outputs it would seed 0 and make every source appear
> + * unmasked - and the first unmask would then clear the whole mask
> + * register. Suppress the seeding there and set mask_cache below to
> + * match what al_fic_wire_init() programmed.
> + *
> + * The info mask register is not affected, so the info output keeps the
> + * register-seeded mask_cache.
> + */
> + if (version == AL_FIC_VERSION_V2 &&
> + (fic_id == AL_FIC_ID_ERROR || fic_id == AL_FIC_ID_FATAL))
> + gc_flags = 0;
> + else
> + gc_flags = IRQ_GC_INIT_MASK_CACHE;
> +
This is correct but gc_flags can be set to IRQ_GC_INIT_MASK_CACHE at
the declaration, and then the "else" branch is not needed. Or it can be
initialized to 0 and set to IRQ_GC_INIT_MASK_CACHE for the version/id
that support it; the condition would have to be flipped of course e.g.
if (version != AL_FIC_VERSION_V2 || fic_id == AL_FIC_ID_INFO)
gc_flags = IRQ_GC_INIT_MASK_CACHE;
That's just a suggestion, and it's totally fine with me if you prefer
to keep it like that.
> fic->domain = irq_domain_create_linear(of_fwnode_handle(node),
> NR_FIC_IRQS,
> &irq_generic_chip_ops,
> @@ -144,7 +199,7 @@ static int al_fic_register(struct device_node *node,
> NR_FIC_IRQS,
> 1, fic->node->full_name,
> handle_level_irq,
> - 0, 0, IRQ_GC_INIT_MASK_CACHE);
> + 0, 0, gc_flags);
> if (ret) {
> pr_err("fail to allocate generic chip (%d)\n", ret);
> goto err_domain_remove;
> @@ -152,7 +207,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 +217,13 @@ static int al_fic_register(struct device_node *node,
> gc->chip_types->chip.flags = IRQCHIP_SKIP_SET_WAKE;
> gc->private = fic;
>
> + /*
> + * Seed the mask cache the driver maintains itself, matching the mask
> + * al_fic_wire_init() programmed (see the gc_flags comment above).
> + */
> + if (!(gc_flags & IRQ_GC_INIT_MASK_CACHE))
> + gc->mask_cache = 0xFFFFFFFF;
> +
This is correct but I prefer to write it as ~0U. It's easy to miss one
'F', both when writing and reading it. Again, it's just a suggestion
and a matter of style.
> ret = request_irq(fic->parent_irq, al_fic_irq_handler,
> IRQF_NO_THREAD | IRQF_SHARED, fic->node->full_name,
> fic);
> @@ -185,6 +247,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.
> @@ -193,11 +257,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)
> @@ -207,22 +273,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(0xFFFFFFFF, fic->base + AL_FIC_INFO_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);
> + }
Same note about 0xFFFFFFFF vs. ~0U here. Again, just a matter of style;
I don't feel strongly about it, so feel free to ignore.
>
> /* 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;
The side effect of this is that all the other bits previously set in
the AL_FIC_CONTROL register are preserved, whereas before this patch
they were reset by initializing "control" to CONTROL_MASK_MSI_X. Is
this intentional? If it is, then perhaps it deserves a comment because
it looks like a behavior change.
> writel_relaxed(control, fic->base + AL_FIC_CONTROL);
>
> - ret = al_fic_register(node, fic);
> + ret = al_fic_register(node, fic, fic_id, version_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;
>
> @@ -231,11 +312,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;
>
> @@ -244,6 +352,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);
> @@ -259,7 +373,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));
next prev parent reply other threads:[~2026-10-05 1:01 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-27 8:06 [PATCH v2 0/8] irqchip/al-fic: shared parent IRQ, error/fatal outputs and affinity Eliav Farber
2026-09-27 8:06 ` [PATCH v2 1/8] irqchip/al-fic: fix argument alignment and a repeated word Eliav Farber
2026-10-04 16:15 ` Radu Rendec
2026-09-27 8:06 ` [PATCH v2 2/8] irqchip/al-fic: use %pOF and raise init log level Eliav Farber
2026-10-04 16:25 ` Radu Rendec
2026-09-27 8:06 ` [PATCH v2 3/8] irqchip/al-fic: keep the device_node instead of a cached name string Eliav Farber
2026-10-04 17:40 ` Radu Rendec
2026-10-05 11:17 ` Farber, Eliav
2026-09-27 8:06 ` [PATCH v2 4/8] irqchip/al-fic: switch to shared parent interrupt Eliav Farber
2026-10-04 19:30 ` Radu Rendec
2026-10-05 11:18 ` Farber, Eliav
2026-09-27 8:06 ` [PATCH v2 5/8] dt-bindings: interrupt-controller: amazon,al-fic: add mask selection Eliav Farber
2026-09-28 16:50 ` Conor Dooley
2026-10-04 21:06 ` Radu Rendec
2026-09-27 8:06 ` [PATCH v2 6/8] irqchip/al-fic: support error and fatal outputs and FIC v2 Eliav Farber
2026-10-05 1:01 ` Radu Rendec [this message]
2026-10-05 11:19 ` Farber, Eliav
2026-09-27 8:06 ` [PATCH v2 7/8] irqchip/al-fic: add support for FIC v3 Eliav Farber
2026-10-05 1:04 ` Radu Rendec
2026-09-27 8:06 ` [PATCH v2 8/8] irqchip/al-fic: add irq_set_affinity callback Eliav Farber
2026-10-05 1:25 ` Radu Rendec
2026-10-05 11:20 ` Farber, Eliav
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=a74366542ddd5e0e3fca71c5dee5d535cc9c560e.camel@rendec.net \
--to=radu@rendec.net \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=farbere@amazon.com \
--cc=krzk+dt@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--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®