From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 2EECE3A873A; Wed, 18 Mar 2026 11:04:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773831899; cv=none; b=nFu8LoHdagedBqxtekRf1t9ys96zKmUzpx+NuSfyyfTQD9SPOcpTZmKn5pUC6mb/4cpoM3Rb+3lm4uQ82wVSPDqDtT3PSRONSwurLwZpU36I3MnK/I7Ryat41lEXiekwj7NQWdTtltOvvGJ2RQ+JtMlt/KWr1Z3LnDcPAZoZIUk= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773831899; c=relaxed/simple; bh=QkhsuHxeBIhHrtgQizPa1cqFV48lv4Y3L5S4Fc3AdQ8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=OWgTW91P1ESssJ4Pwjy/4lyUCl6oOCrbwhqpVNzq1WTRdLEDQmY2xSnAajU6Pbkog55geXWJTgOLPWdHR4EyxN4H7oJWbYDsSlj3GC/iKjDt9qsBGx163HGPIwrAJYB66HxYc+dE4Oh80aqMXYAjmQpXpnQvzGCNlOqCc2Tyolo= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=pzthHmHj; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="pzthHmHj" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7D3F6C4AF0E; Wed, 18 Mar 2026 11:04:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1773831898; bh=QkhsuHxeBIhHrtgQizPa1cqFV48lv4Y3L5S4Fc3AdQ8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=pzthHmHjANpdtvpDH6RziZNfyMSEEYRgE6PLVZWEpmNtaawu7rXebG5y2GeoEAZgB NNw7Ult6VXrmISVqKFXuxPKF+EB1rUMa+wBFke7fo3tu9cVSo6Ko4wtcXWkcSCkhs4 UjfGJUfAPbbXPi6YRnbc33deAFV/G3CwNISFs0UFi8l9KbxX4ubfJmAyvbiccWr2W9 TCi3AYmNDb/TCx0OWHEiwfu4DdFh4WQaFd7tkj5877cTK81QVwhuTbb7LQY4X9weZq F8kiSTjPDL44sDn/RgNBV1pAfD8SIM5J+8iQfmmh9hmKAzdZajLWttRWWO0C/MFF6z YlhfdkB8SiRcw== From: Conor Dooley To: linux-gpio@vger.kernel.org Cc: conor@kernel.org, Conor Dooley , Thomas Gleixner , Herve Codina , Daire McNamara , Rob Herring , Krzysztof Kozlowski , Paul Walmsley , Palmer Dabbelt , Albert Ou , Alexandre Ghiti , Linus Walleij , Bartosz Golaszewski , linux-riscv@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Bartosz Golaszewski Subject: [PATCH v13 4/5] soc: microchip: add mpfs gpio interrupt mux driver Date: Wed, 18 Mar 2026 11:04:35 +0000 Message-ID: <20260318-capsize-agreed-d11a67fdfad2@spud> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260318-gift-nearest-fd3ef3e4819b@spud> References: <20260318-gift-nearest-fd3ef3e4819b@spud> Precedence: bulk X-Mailing-List: devicetree@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=8942; i=conor.dooley@microchip.com; h=from:subject:message-id; bh=0VdL10AsIya/09nL9LUhGVZIsekmdPpe92n4y+qOvNc=; b=owGbwMvMwCVWscWwfUFT0iXG02pJDJm72o4+e6+nc2dCc1XyQYWrHhusKyc8mnC3TrPFxWHKz CRJl4fyHaUsDGJcDLJiiiyJt/tapNb/cdnh3PMWZg4rE8gQBi5OAZjIql8M/5SUcxqygn6+1/N7 s+XcxAW7jp9yV77jZmRvH2sUX+L334vhn8G88qcbrL9ZvMjIYX0y48+BylO3Xge1N8kpfl5be6s wjR0A X-Developer-Key: i=conor.dooley@microchip.com; a=openpgp; fpr=F9ECA03CF54F12CD01F1655722E2C55B37CF380C Content-Transfer-Encoding: 8bit From: Conor Dooley On PolarFire SoC there are more GPIO interrupts than there are interrupt lines available on the PLIC, and a runtime configurable mux is used to decide which interrupts are assigned direct connections to the PLIC & which are relegated to sharing a line. Add a driver so that Linux can set the mux based on the interrupt mapping in the devicetree. Reviewed-by: Herve Codina Reviewed-by: Linus Walleij Reviewed-by: Bartosz Golaszewski Signed-off-by: Conor Dooley --- MAINTAINERS | 2 +- drivers/soc/microchip/Kconfig | 11 ++ drivers/soc/microchip/Makefile | 1 + drivers/soc/microchip/mpfs-irqmux.c | 181 ++++++++++++++++++++++++++++ 4 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 drivers/soc/microchip/mpfs-irqmux.c diff --git a/MAINTAINERS b/MAINTAINERS index 55af015174a54..723c58756a5c1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22734,7 +22734,7 @@ F: Documentation/devicetree/bindings/pinctrl/microchip,mpfs-pinctrl-mssio.yaml F: Documentation/devicetree/bindings/pinctrl/microchip,pic64gx-pinctrl-gpio2.yaml F: Documentation/devicetree/bindings/pwm/microchip,corepwm.yaml F: Documentation/devicetree/bindings/riscv/microchip.yaml -F: Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-sys-controller.yaml +F: Documentation/devicetree/bindings/soc/microchip/microchip,mpfs*.yaml F: Documentation/devicetree/bindings/spi/microchip,mpfs-spi.yaml F: Documentation/devicetree/bindings/usb/microchip,mpfs-musb.yaml F: arch/riscv/boot/dts/microchip/ diff --git a/drivers/soc/microchip/Kconfig b/drivers/soc/microchip/Kconfig index bcf5546025610..af7946741bce4 100644 --- a/drivers/soc/microchip/Kconfig +++ b/drivers/soc/microchip/Kconfig @@ -1,3 +1,14 @@ +config POLARFIRE_SOC_IRQ_MUX + bool "Microchip PolarFire SoC's GPIO IRQ Mux" + depends on ARCH_MICROCHIP + select REGMAP + select REGMAP_MMIO + default y + help + Support for the interrupt mux on Polarfire SoC. It sits between + the GPIO controllers and the PLIC, as only 41 interrupts are shared + between 3 GPIO controllers with a total of 70 interrupts. + config POLARFIRE_SOC_SYS_CTRL tristate "Microchip PolarFire SoC (MPFS) system controller support" depends on POLARFIRE_SOC_MAILBOX diff --git a/drivers/soc/microchip/Makefile b/drivers/soc/microchip/Makefile index 1a3a1594b089b..55775db45ee76 100644 --- a/drivers/soc/microchip/Makefile +++ b/drivers/soc/microchip/Makefile @@ -1,2 +1,3 @@ +obj-$(CONFIG_POLARFIRE_SOC_IRQ_MUX) += mpfs-irqmux.o obj-$(CONFIG_POLARFIRE_SOC_SYS_CTRL) += mpfs-sys-controller.o obj-$(CONFIG_POLARFIRE_SOC_SYSCONS) += mpfs-control-scb.o mpfs-mss-top-sysreg.o diff --git a/drivers/soc/microchip/mpfs-irqmux.c b/drivers/soc/microchip/mpfs-irqmux.c new file mode 100644 index 0000000000000..ae15e913e7802 --- /dev/null +++ b/drivers/soc/microchip/mpfs-irqmux.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Largely copied from rzn1_irqmux.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MPFS_IRQMUX_CR 0x54 +#define MPFS_IRQMUX_NUM_CHILDREN 96 +#define MPFS_IRQMUX_NUM_DIRECT 38 +#define MPFS_IRQMUX_DIRECT_START 13 +#define MPFS_IRQMUX_DIRECT_END 50 +#define MPFS_IRQMUX_NONDIRECT_END 53 + +static int mpfs_irqmux_is_direct_mode(struct device *dev, + const struct of_phandle_args *parent_args) +{ + if (parent_args->args_count != 1) { + dev_err(dev, "Invalid interrupt-map item\n"); + return -EINVAL; + } + + if (parent_args->args[0] < MPFS_IRQMUX_DIRECT_START || + parent_args->args[0] > MPFS_IRQMUX_NONDIRECT_END) { + dev_err(dev, "Invalid interrupt %u\n", parent_args->args[0]); + return -EINVAL; + } + + if (parent_args->args[0] > MPFS_IRQMUX_DIRECT_END) + return 0; + + return 1; +} + +static int mpfs_irqmux_probe(struct platform_device *pdev) +{ + DECLARE_BITMAP(child_done, MPFS_IRQMUX_NUM_CHILDREN) = {}; + DECLARE_BITMAP(parent_done, MPFS_IRQMUX_NUM_DIRECT) = {}; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct of_imap_parser imap_parser; + struct of_imap_item imap_item; + struct regmap *regmap; + int ret, direct_mode, line, controller, gpio, parent_line; + u32 tmp, val = 0, old; + + regmap = device_node_to_regmap(pdev->dev.parent->of_node); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "Failed to find syscon regmap\n"); + + /* We support only #interrupt-cells = <1> and #address-cells = <0> */ + ret = of_property_read_u32(np, "#interrupt-cells", &tmp); + if (ret) + return ret; + if (tmp != 1) + return -EINVAL; + + ret = of_property_read_u32(np, "#address-cells", &tmp); + if (ret) + return ret; + if (tmp != 0) + return -EINVAL; + + ret = of_imap_parser_init(&imap_parser, np, &imap_item); + if (ret) + return ret; + + for_each_of_imap_item(&imap_parser, &imap_item) { + + direct_mode = mpfs_irqmux_is_direct_mode(dev, &imap_item.parent_args); + if (direct_mode < 0) { + of_node_put(imap_item.parent_args.np); + return direct_mode; + } + + line = imap_item.child_imap[0]; + gpio = line % 32; + controller = line / 32; + + if (controller > 2) { + of_node_put(imap_item.parent_args.np); + dev_err(dev, "child interrupt number too large: %d\n", line); + return -EINVAL; + } + + if (test_and_set_bit(line, child_done)) { + of_node_put(imap_item.parent_args.np); + dev_err(dev, "mux child line %d already defined in interrupt-map\n", + line); + return -EINVAL; + } + + parent_line = imap_item.parent_args.args[0] - MPFS_IRQMUX_DIRECT_START; + if (direct_mode && test_and_set_bit(parent_line, parent_done)) { + of_node_put(imap_item.parent_args.np); + dev_err(dev, "mux parent line %d already defined in interrupt-map\n", + line); + return -EINVAL; + } + + /* + * There are 41 interrupts assigned to GPIOs, of which 38 are "direct". Since the + * mux has 32 bits only, 6 of these exclusive/"direct" interrupts remain. These + * are used by GPIO controller 1's lines 18 to 23. Nothing needs to be done + * for these interrupts. + */ + if (controller == 1 && gpio >= 18) + continue; + + /* + * The mux has a single register, where bits 0 to 13 mux between GPIO controller + * 1's 14 GPIOs and GPIO controller 2's first 14 GPIOs. The remaining bits mux + * between the first 18 GPIOs of controller 1 and the last 18 GPIOS of + * controller 2. If a bit in the mux's control register is set, the + * corresponding interrupt line for GPIO controller 0 or 1 will be put in + * "non-direct" mode. If cleared, the "fabric" controller's will. + * + * Register layout: + * GPIO 1 interrupt line 17 | mux bit 31 | GPIO 2 interrupt line 31 + * ... | ... | ... + * ... | ... | ... + * GPIO 1 interrupt line 0 | mux bit 14 | GPIO 2 interrupt line 14 + * GPIO 0 interrupt line 13 | mux bit 13 | GPIO 2 interrupt line 13 + * ... | ... | ... + * ... | ... | ... + * GPIO 0 interrupt line 0 | mux bit 0 | GPIO 2 interrupt line 0 + * + * As the binding mandates 70 items, one for each GPIO line, there's no need to + * handle anything for GPIO controller 2, since the bit will be set for the + * corresponding line in GPIO controller 0 or 1. + */ + if (controller == 2) + continue; + + /* + * If in direct mode, the bit is cleared, nothing needs to be done as val is zero + * initialised and that's the direct mode setting for GPIO controller 0 and 1. + */ + if (direct_mode) + continue; + + if (controller == 0) + val |= 1U << gpio; + else + val |= 1U << (gpio + 14); + } + + regmap_read(regmap, MPFS_IRQMUX_CR, &old); + regmap_write(regmap, MPFS_IRQMUX_CR, val); + + if (val != old) + dev_info(dev, "firmware mux setting of 0x%x overwritten to 0x%x\n", old, val); + + return 0; +} + +static const struct of_device_id mpfs_irqmux_of_match[] = { + { .compatible = "microchip,mpfs-irqmux", }, + { } +}; +MODULE_DEVICE_TABLE(of, mpfs_irqmux_of_match); + +static struct platform_driver mpfs_irqmux_driver = { + .probe = mpfs_irqmux_probe, + .driver = { + .name = "mpfs_irqmux", + .of_match_table = mpfs_irqmux_of_match, + }, +}; +module_platform_driver(mpfs_irqmux_driver); + +MODULE_AUTHOR("Conor Dooley "); +MODULE_DESCRIPTION("Polarfire SoC interrupt mux driver"); +MODULE_LICENSE("GPL"); -- 2.51.0