From nobody Thu Apr 2 15:43:36 2026 Received: from smtpout-02.galae.net (smtpout-02.galae.net [185.246.84.56]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6B00D39183F; Fri, 27 Mar 2026 20:11:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.246.84.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774642268; cv=none; b=jieJsPoGfmyeG6sN10bhPjgROhEjX9RLVzx6Z1jo12KqyIhiBQYrcQ3/a8UkQYcTWzzgrgCUqJUSSLphBWrBDujRCWtvDTpfXTtwsFP6GB+tOr8D8dd4Eu14nm+W2JnlzgOCQPOjMpOvxLcu4pLu4rrLBW+KcAQYfwC4KeJZQdg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774642268; c=relaxed/simple; bh=Texga47YbF0bZROj1zWl0qeWjvFGYpvoTnvEHB7VFHw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=rFVa89fh0/8lOVEOYVNI57rUI+KVJXOnwMHyZ4FgmC0aKBdySEM59fWFZJn6dIxjyHV/oasukqgymIJC3AvOxTFMVFLf+EOjNaIzQKvIZAh3UqCZVwRJYwem6vC9VCDP597L16uocpU88lkooSWY3K1XiamYAyTJPrPJIzWtDFY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=2kJla1Qu; arc=none smtp.client-ip=185.246.84.56 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="2kJla1Qu" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-02.galae.net (Postfix) with ESMTPS id 344791A3032; Fri, 27 Mar 2026 20:11:05 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id 0239860268; Fri, 27 Mar 2026 20:11:05 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id CEC3A10451AF7; Fri, 27 Mar 2026 21:11:00 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1774642263; h=from:subject:date:message-id:to:cc:mime-version:content-type: content-transfer-encoding:in-reply-to:references; bh=XECnex5AFDMpEwhqr4QZ6gZV3+vm9bgxJLNcNDz5vuw=; b=2kJla1Qu5t5vjjuuuvIX0AAD2r79XGuc3E+lVgebFaDqaIGXTo4lMe3Dtbk1Qvr7fZEK7q CVwg4CYmPkcNwTgnEpzuBAwY0irxIXFjgT7Eqp526scDhn746byL8+xmEZyexoIdBf030y HFGyoHxSSd9HQda3KskIc38k3XyFoivhZs2IaoDH8jC+/QhhY9o7hRF7uanFXTG3SZB6pv KQKTn0MlBgrRawir0Hi3m6IttNrXbRbouF3hQS7OdMo8U7iHo8rOxfaPMv7QkYCBtevn26 BpAGuo6tpnGL1nf0vhyiQoOzp+Zb+m3TOg9TiZhpLGNYQh6gFkADuILY3rJNbQ== From: "Miquel Raynal (Schneider Electric)" Date: Fri, 27 Mar 2026 21:09:34 +0100 Subject: [PATCH 12/16] irqchip/eip201-aic: Add support for Safexcel EIP-201 AIC Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260327-schneider-v7-0-rc1-crypto-v1-12-5e6ff7853994@bootlin.com> References: <20260327-schneider-v7-0-rc1-crypto-v1-0-5e6ff7853994@bootlin.com> In-Reply-To: <20260327-schneider-v7-0-rc1-crypto-v1-0-5e6ff7853994@bootlin.com> To: Michael Turquette , Stephen Boyd , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Thomas Gleixner , Olivia Mackall , Herbert Xu , Jayesh Choudhary , "David S. Miller" , Christian Marangi , Antoine Tenart , Geert Uytterhoeven , Magnus Damm Cc: Thomas Petazzoni , Pascal EBERHARD , Wolfram Sang , linux-clk@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-crypto@vger.kernel.org, linux-renesas-soc@vger.kernel.org, "Miquel Raynal (Schneider Electric)" X-Mailer: b4 0.14.3 X-Last-TLS-Session-Version: TLSv1.3 Describe the EIP-201 Advanced Interrupt Controller from Inside Secure, typically found in a bigger block named EIP-150. This controller is rather simple and is driven using the generic irqchip model. Its own interrupt domain is limited to just a few interrupts connected to other inner blocks, such as a Random Number Generator and a Public Key Accelerator. The one I used receives only rising edge interrupts and uses its own logic to track them. It is theoretically possible to wire devices with level interrupts, but not in the context of the EIP-150. Signed-off-by: Miquel Raynal (Schneider Electric) --- drivers/irqchip/Kconfig | 8 ++ drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-eip201-aic.c | 221 +++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 230 insertions(+) diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index f07b00d7fef9..b098bb00a224 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -826,4 +826,12 @@ config SUNPLUS_SP7021_INTC chained controller, routing all interrupt source in P-Chip to the primary controller on C-Chip. =20 +config SAFEXCEL_EIP201_AIC + tristate "Safexcel EIP201 AIC" + select IRQ_DOMAIN + help + Support for the Advanced Interrupt Controller (AIC) typically + inside Safexcel EIP150 IPs, gathering Public Key Accelerator + and True Random Number Generator interrupts. + endmenu diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 26aa3b6ec99f..80784a02f4a8 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -136,3 +136,4 @@ obj-$(CONFIG_APPLE_AIC) +=3D irq-apple-aic.o obj-$(CONFIG_MCHP_EIC) +=3D irq-mchp-eic.o obj-$(CONFIG_SOPHGO_SG2042_MSI) +=3D irq-sg2042-msi.o obj-$(CONFIG_SUNPLUS_SP7021_INTC) +=3D irq-sp7021-intc.o +obj-$(CONFIG_SAFEXCEL_EIP201_AIC) +=3D irq-eip201-aic.o diff --git a/drivers/irqchip/irq-eip201-aic.c b/drivers/irqchip/irq-eip201-= aic.c new file mode 100644 index 000000000000..514fd39e2777 --- /dev/null +++ b/drivers/irqchip/irq-eip201-aic.c @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Schneider Electric + * Authored by Miquel Raynal + * Based on the work from Mathieu Hadjimegrian + */ + +#include "linux/irq.h" +#include "linux/stddef.h" +#include +#include +#include +#include +#include +#include +#include + +#include + +#define EIP201_AIC_POL_CTRL 0x0 /* RO */ +#define EIP201_AIC_POL_LOW_FALLING 0 +#define EIP201_AIC_POL_HIGH_RISING 1 + +#define EIP201_AIC_TYP_CTRL 0x4 /* RO */ +#define EIP201_AIC_TYP_LEVEL 0 +#define EIP201_AIC_TYP_EDGE 1 + +#define EIP201_AIC_ENABLE_SET 0xC /* WO */ +#define EIP201_AIC_ENABLED_STAT 0x10 /* RO */ +#define EIP201_AIC_ENABLE_CLR 0x14 /* WO */ +#define EIP201_AIC_ACK 0x10 /* WO */ + +#define EIP201_AIC_REVISION 0x3FFC +#define EIP201_AIC_REV_NUM(reg) FIELD_GET(GENMASK(7, 0), reg) +#define EIP201_AIC_REV_COMP_NUM(reg) FIELD_GET(GENMASK(15, 8), reg) + +#define EIP201_AIC_INT(reg, int) field_get(BIT(int), reg) +#define EIP201_AIC_NINT 7 +#define EIP201_AIC_INT_MASK (BIT(EIP201_AIC_NINT) - 1) + +struct eip201_aic { + struct device *dev; + void __iomem *regs; + struct irq_domain *domain; + struct irq_chip_generic *gc; + u32 type; + u32 pol; +}; + +static struct eip201_aic *irq_domain_to_aic(struct irq_domain *d) +{ + return d->host_data; +} + +static int eip201_aic_irq_domain_xlate(struct irq_domain *d, struct device= _node *ctrlr, + const u32 *intspec, unsigned int intsize, + irq_hw_number_t *out_hwirq, unsigned int *out_type) +{ + struct eip201_aic *aic =3D irq_domain_to_aic(d); + int ret; + + ret =3D irq_domain_xlate_twocell(d, ctrlr, intspec, intsize, out_hwirq, o= ut_type); + if (ret) + return ret; + + /* One interrupt is reserved, two are for Inside Secure debugging purpose= s only */ + switch (*out_hwirq) { + case AIC_PKA_INT0: + case AIC_PKA_INT1: + case AIC_RESERVED: + return -EINVAL; + default: + break; + } + + /* + * Flow type is implementation specific and hardcoded, make sure it is co= rect, + * even though the documentation says EIP blocks generate edge interrupts. + */ + + /* Type register indicates: + * - '1' for edge interrupts + * - '0' for level interrupts + */ + if (*out_type & IRQ_TYPE_LEVEL_MASK && + EIP201_AIC_INT(aic->type, *out_hwirq)) + return -EINVAL; + + /* + * Polarity register indicates: + * - '1' for level high or rising edge + * - '0' for level low or falling edge + */ + if (*out_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_EDGE_FALLING) && + EIP201_AIC_INT(aic->pol, *out_hwirq)) + return -EINVAL; + + return 0; +} + +const struct irq_domain_ops eip201_aic_chip_ops =3D { + .map =3D irq_map_generic_chip, + .unmap =3D irq_unmap_generic_chip, + .xlate =3D eip201_aic_irq_domain_xlate, +}; + +static irqreturn_t eip201_aic_irq_handler(int irq, void *dev_id) +{ + struct eip201_aic *aic =3D dev_id; + unsigned long pending; + irq_hw_number_t hwirq; + + pending =3D readl(aic->regs + EIP201_AIC_ENABLED_STAT); + if (!pending) + return IRQ_NONE; + + /* Ack interrupts ASAP to decrease the likelyhood of missing an edge one = */ + writel(pending, aic->regs + EIP201_AIC_ACK); + + for_each_set_bit(hwirq, &pending, EIP201_AIC_NINT) + generic_handle_domain_irq(aic->domain, hwirq); + + return IRQ_HANDLED; +} + +static int eip201_aic_probe(struct platform_device *pdev) +{ + struct eip201_aic *aic; + struct clk *clk; + u32 rev; + int irq; + int ret; + + aic =3D devm_kzalloc(&pdev->dev, sizeof(*aic), GFP_KERNEL); + if (!aic) + return -ENOMEM; + + aic->regs =3D devm_platform_ioremap_resource(pdev, 0); + if (!aic->regs) + return -EINVAL; + + clk =3D devm_clk_get_enabled(&pdev->dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + rev =3D readl(aic->regs + EIP201_AIC_REVISION); + if (!(EIP201_AIC_REV_NUM(rev) ^ EIP201_AIC_REV_COMP_NUM(rev))) + return -ENXIO; + + platform_set_drvdata(pdev, aic); + aic->dev =3D &pdev->dev; + + /* Cache the RO type and polarity of all interrupts */ + aic->type =3D readl(aic->regs + EIP201_AIC_TYP_CTRL); + aic->pol =3D readl(aic->regs + EIP201_AIC_POL_CTRL); + + /* Disable/clear all interrupts */ + writel(EIP201_AIC_INT_MASK, aic->regs + EIP201_AIC_ENABLE_CLR); + writel(EIP201_AIC_INT_MASK, aic->regs + EIP201_AIC_ACK); + + aic->domain =3D irq_domain_create_linear(dev_fwnode(&pdev->dev), EIP201_A= IC_NINT, + &eip201_aic_chip_ops, aic); + if (!aic->domain) + return -ENXIO; + + ret =3D irq_alloc_domain_generic_chips(aic->domain, EIP201_AIC_NINT, 1, "= eip201-aic", + handle_edge_irq, 0, 0, 0); + if (ret) + goto remove_domain; + + aic->gc =3D irq_get_domain_generic_chip(aic->domain, 0); + aic->gc->reg_base =3D aic->regs; + aic->gc->chip_types[0].regs.ack =3D EIP201_AIC_ACK; + aic->gc->chip_types[0].regs.enable =3D EIP201_AIC_ENABLE_SET; + aic->gc->chip_types[0].regs.disable =3D EIP201_AIC_ENABLE_CLR; + aic->gc->chip_types[0].chip.irq_ack =3D irq_gc_ack_set_bit; + aic->gc->chip_types[0].chip.irq_mask =3D irq_gc_mask_disable_reg; + aic->gc->chip_types[0].chip.irq_unmask =3D irq_gc_unmask_enable_reg; + aic->gc->chip_types[0].chip.flags =3D IRQCHIP_MASK_ON_SUSPEND; + + irq =3D platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret =3D devm_request_irq(&pdev->dev, irq, eip201_aic_irq_handler, 0, + dev_name(&pdev->dev), aic); + if (ret < 0) + goto remove_gc; + + return 0; + +remove_gc: + irq_remove_generic_chip(aic->gc, EIP201_AIC_INT_MASK, 0, 0); +remove_domain: + irq_domain_remove(aic->domain); + + return ret; +} + +static void eip201_aic_remove(struct platform_device *pdev) +{ + struct eip201_aic *aic =3D platform_get_drvdata(pdev); + + irq_remove_generic_chip(aic->gc, EIP201_AIC_INT_MASK, 0, 0); + irq_domain_remove(aic->domain); +} + +static const struct of_device_id eip201_aic_of_match[] =3D { + { .compatible =3D "inside-secure,safexcel-eip201", }, + {}, +}; + +static struct platform_driver eip201_aic_driver =3D { + .probe =3D eip201_aic_probe, + .remove =3D eip201_aic_remove, + .driver =3D { + .name =3D "safexcel-eip201-aic", + .of_match_table =3D eip201_aic_of_match, + }, +}; +module_platform_driver(eip201_aic_driver); --=20 2.51.1