* [PATCH 1/2] dt-bindings: interrupt-controller: introduce toshiba,tc9564-msigen.yaml
2026-10-12 19:02 [PATCH 0/2] irqchip: add TC9564 support Alex Elder
@ 2026-10-12 19:02 ` Alex Elder
2026-10-12 19:02 ` [PATCH 2/2] irqchip: introduce TC9564 SoC MSIGEN support Alex Elder
1 sibling, 0 replies; 3+ messages in thread
From: Alex Elder @ 2026-10-12 19:02 UTC (permalink / raw)
To: tglx, radu, robh, krzk+dt, conor+dt
Cc: p.zabel, mohd.anwar, lorenzo.bianconi, danielt, devicetree,
linux-kernel, Daniel Thompson
Define the binding for the MSIGEN interrupt controller present in the
Toshiba TC9564 SoC.
Co-developed-by: Daniel Thompson <daniel@riscstar.com>
Signed-off-by: Daniel Thompson <daniel@riscstar.com>
Signed-off-by: Alex Elder <elder@riscstar.com>
---
.../toshiba,tc9564-msigen.yaml | 70 +++++++++++++++++++
MAINTAINERS | 6 ++
2 files changed, 76 insertions(+)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/toshiba,tc9564-msigen.yaml
diff --git a/Documentation/devicetree/bindings/interrupt-controller/toshiba,tc9564-msigen.yaml b/Documentation/devicetree/bindings/interrupt-controller/toshiba,tc9564-msigen.yaml
new file mode 100644
index 0000000000000..39dc63964bd52
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/toshiba,tc9564-msigen.yaml
@@ -0,0 +1,70 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/interrupt-controller/toshiba,tc9564-msigen.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Toshiba TC9564 MSIGEN Interrupt Controller
+
+maintainers:
+ - Daniel Thompson <daniel@riscstar.com>
+ - Alex Elder <elder@riscstar.com>
+
+description: |
+ The Toshiba TC9564 is an SoC accessed by a host system through the
+ upstream PCIe port on the PCIe switch it implements. The switch
+ includes an embedded PCIe endpoint that provides access to various
+ SoC peripherals (including an interrupt controller) via its BARs.
+
+ The TC9564 MSIGEN (MSI Generator) provides an interrupt controller
+ with 25 hardware interrupt lines. It aggregates internal interrupts
+ and generates a single PCIe MSI to the host. Each PCIe function
+ implements an MSIGEN instance.
+
+ Each MSIGEN uses a gated clock and reset line. These are provided
+ by the clock and reset controllers internal to the TC9564.
+
+allOf:
+ - $ref: /schemas/interrupt-controller.yaml#
+
+properties:
+ compatible:
+ const: toshiba,tc9564-msigen
+
+ reg:
+ maxItems: 1
+
+ interrupt-controller: true
+
+ '#interrupt-cells':
+ const: 1
+
+ clocks:
+ maxItems: 1
+ description: Gate clock from the TC9564 clock controller
+
+ resets:
+ maxItems: 1
+ description: Reset signal from the TC9564 reset controller
+
+required:
+ - compatible
+ - reg
+ - interrupt-controller
+ - '#interrupt-cells'
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/toshiba,tc9564.h>
+ #include <dt-bindings/reset/toshiba,tc9564.h>
+
+ interrupt-controller@f000 {
+ compatible = "toshiba,tc9564-msigen";
+ reg = <0xf000 0x100>;
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ clocks = <&tc9564_clock0 CLOCK_MSIGEN>;
+ resets = <&tc9564_reset0 RESET_MSIGEN>;
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 252b6d7ee1638..9e73a912f0c0f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -28173,6 +28173,12 @@ F: Documentation/devicetree/bindings/media/i2c/toshiba,tc358743.yaml
F: drivers/media/i2c/tc358743*
F: include/media/i2c/tc358743.h
+TOSHIBA TC9564 MSIGEN DRIVER
+M: Alex Elder <elder@kernel.org>
+M: Daniel Thompson <danielt@kernel.org>
+S: Maintained
+F: Documentation/devicetree/bindings/interrupt-controller/toshiba,tc9564-msigen.yaml
+
TOSHIBA TC9564 PCI DRIVER
M: Alex Elder <elder@kernel.org>
M: Daniel Thompson <danielt@kernel.org>
--
2.53.0
^ permalink raw reply [flat|nested] 3+ messages in thread* [PATCH 2/2] irqchip: introduce TC9564 SoC MSIGEN support
2026-10-12 19:02 [PATCH 0/2] irqchip: add TC9564 support Alex Elder
2026-10-12 19:02 ` [PATCH 1/2] dt-bindings: interrupt-controller: introduce toshiba,tc9564-msigen.yaml Alex Elder
@ 2026-10-12 19:02 ` Alex Elder
1 sibling, 0 replies; 3+ messages in thread
From: Alex Elder @ 2026-10-12 19:02 UTC (permalink / raw)
To: tglx, radu, robh, krzk+dt, conor+dt
Cc: Daniel Thompson, p.zabel, mohd.anwar, lorenzo.bianconi, danielt,
devicetree, linux-kernel
From: Daniel Thompson <daniel@riscstar.com>
Define a new irqchip platform driver that represents the MSIGEN
functionality present in the TC9564 SoC.
Signed-off-by: Daniel Thompson <daniel@riscstar.com>
Co-developed-by: Alex Elder <elder@riscstar.com>
Signed-off-by: Alex Elder <elder@riscstar.com>
---
MAINTAINERS | 1 +
drivers/irqchip/Kconfig | 10 ++
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-tc9564-msigen.c | 203 ++++++++++++++++++++++++++++
4 files changed, 215 insertions(+)
create mode 100644 drivers/irqchip/irq-tc9564-msigen.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 9e73a912f0c0f..5b0ed7c1ab40a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -28178,6 +28178,7 @@ M: Alex Elder <elder@kernel.org>
M: Daniel Thompson <danielt@kernel.org>
S: Maintained
F: Documentation/devicetree/bindings/interrupt-controller/toshiba,tc9564-msigen.yaml
+F: drivers/irqchip/irq-tc9564-msigen.c
TOSHIBA TC9564 PCI DRIVER
M: Alex Elder <elder@kernel.org>
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index a8f8c423b7957..205b5a167c805 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -842,4 +842,14 @@ config SUNPLUS_SP7021_INTC
chained controller, routing all interrupt source in P-Chip to
the primary controller on C-Chip.
+config TC9564_MSIGEN
+ tristate "Toshiba TC9564 MSI distributor"
+ depends on TC9564_PCI
+ depends on GENERIC_IRQ_CHIP
+ default TC9564_PCI
+ help
+ This enables support for the "MSIGEN" MSI distributor embedded
+ in the Toshiba TC9564. This device receives wired interrupts
+ within the TC9564 and raises them to the host using MSI.
+
endmenu
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index ab33cccd84719..c82e00802c2b4 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -135,3 +135,4 @@ obj-$(CONFIG_APPLE_AIC) += irq-apple-aic.o
obj-$(CONFIG_MCHP_EIC) += irq-mchp-eic.o
obj-$(CONFIG_SOPHGO_SG2042_MSI) += irq-sg2042-msi.o
obj-$(CONFIG_SUNPLUS_SP7021_INTC) += irq-sp7021-intc.o
+obj-$(CONFIG_TC9564_MSIGEN) += irq-tc9564-msigen.o
diff --git a/drivers/irqchip/irq-tc9564-msigen.c b/drivers/irqchip/irq-tc9564-msigen.c
new file mode 100644
index 0000000000000..a27edd5859e43
--- /dev/null
+++ b/drivers/irqchip/irq-tc9564-msigen.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/clk.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+/**
+ * struct tc9564_msigen - Context for MSIGEN IRQ domain
+ * @ioaddr: Pointer to mapped MSIGEN memory
+ * @irq: IRQ number for MSIGEN
+ */
+struct tc9564_msigen {
+ void __iomem *ioaddr;
+ unsigned int irq;
+};
+
+#define HWIRQ_COUNT 25
+
+#define MSI_OUT_EN_OFFSET 0x0000
+#define MSI_MASK_CLR_OFFSET 0x000c
+#define MSI_MASK_VALUE BIT(0)
+#define MSI_INT_STS_OFFSET 0x0010
+
+static void tc9564_msigen_irq_handler(struct irq_desc *desc)
+{
+ struct irq_domain *irq_domain = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct irq_chip_generic *gc;
+ unsigned long status;
+ unsigned long hwirq;
+
+ gc = irq_get_domain_generic_chip(irq_domain, 0);
+
+ chained_irq_enter(chip, desc);
+
+ status = irq_reg_readl(gc, MSI_INT_STS_OFFSET);
+ for_each_set_bit(hwirq, &status, HWIRQ_COUNT)
+ generic_handle_domain_irq(irq_domain, hwirq);
+
+ /*
+ * Clear the MSI flag. Most interrupts within TC9564 are level-high
+ * type. If any interrupts are still asserted then clearing this flag
+ * will cause the (edge-triggered) MSI to be regenerated.
+ */
+ irq_reg_writel(gc, MSI_MASK_VALUE, MSI_MASK_CLR_OFFSET);
+
+ chained_irq_exit(chip, desc);
+}
+
+static int tc9564_msigen_irq_chip_init(struct irq_chip_generic *gc)
+{
+ struct tc9564_msigen *msigen = gc->domain->host_data;
+
+ gc->reg_base = msigen->ioaddr;
+ gc->chip_types[0].regs.mask = MSI_OUT_EN_OFFSET;
+ gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
+ gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
+
+ /* Disable all interrupts */
+ irq_reg_writel(gc, 0, MSI_OUT_EN_OFFSET);
+
+ return 0;
+}
+
+static void tc9564_msigen_irq_chip_exit(struct irq_chip_generic *gc)
+{
+ irq_reg_writel(gc, 0, MSI_OUT_EN_OFFSET);
+}
+
+static int tc9564_msigen_irq_domain_init(struct irq_domain *irq_domain)
+{
+ struct tc9564_msigen *msigen = irq_domain->host_data;
+
+ irq_set_chained_handler_and_data(msigen->irq, tc9564_msigen_irq_handler,
+ irq_domain);
+ return 0;
+}
+
+static void tc9564_msigen_irq_domain_exit(struct irq_domain *irq_domain)
+{
+ struct tc9564_msigen *msigen = irq_domain->host_data;
+
+ irq_set_chained_handler_and_data(msigen->irq, NULL, NULL);
+}
+
+/* Find the PCI device we're associated with (not necessarily the parent) */
+static struct pci_dev *tc9564_msigen_find_pci_dev(struct device *dev)
+{
+ struct device *ancestor;
+
+ for (ancestor = dev->parent; ancestor; ancestor = ancestor->parent) {
+ if (dev_is_pci(ancestor)) {
+ /* We currently only support physical functions */
+ if (!dev_is_pf(ancestor))
+ return ERR_PTR(-EINVAL);
+
+ return to_pci_dev(ancestor);
+ }
+ }
+
+ return ERR_PTR(-ENXIO);
+}
+
+static void tc9564_msigen_free_irq_vectors(void *pci_dev)
+{
+ pci_free_irq_vectors(pci_dev);
+}
+
+static int tc9564_msigen_probe(struct platform_device *pdev)
+{
+ struct irq_domain_chip_generic_info dgc_info;
+ struct device *dev = &pdev->dev;
+ struct irq_domain *irq_domain;
+ struct tc9564_msigen *msigen;
+ struct irq_domain_info info;
+ struct reset_control *reset;
+ struct pci_dev *pci_dev;
+ struct clk *clk;
+ int ret;
+
+ pci_dev = tc9564_msigen_find_pci_dev(dev);
+ if (IS_ERR(pci_dev))
+ return dev_err_probe(dev, PTR_ERR(pci_dev),
+ "failed to find PCI device\n");
+
+ msigen = devm_kzalloc(dev, sizeof(*msigen), GFP_KERNEL);
+ if (!msigen)
+ return -ENOMEM;
+
+ msigen->ioaddr = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(msigen->ioaddr))
+ return dev_err_probe(dev, PTR_ERR(msigen->ioaddr),
+ "failed to map registers\n");
+
+ clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(clk))
+ return dev_err_probe(dev, PTR_ERR(clk),
+ "failed to get/enable clock\n");
+
+ reset = devm_reset_control_get_shared_deasserted(dev, NULL);
+ if (IS_ERR(reset))
+ return dev_err_probe(dev, PTR_ERR(reset),
+ "failed to get/deassert reset\n");
+
+ ret = pci_alloc_irq_vectors(pci_dev, 1, 1, PCI_IRQ_MSI);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to alloc irq vector\n");
+
+ ret = devm_add_action_or_reset(dev, tc9564_msigen_free_irq_vectors,
+ pci_dev);
+ if (ret)
+ return ret;
+
+ msigen->irq = pci_irq_vector(pci_dev, 0);
+
+ dgc_info.name = "tc9564-msigen";
+ dgc_info.handler = handle_level_irq;
+ dgc_info.irqs_per_chip = HWIRQ_COUNT;
+ dgc_info.num_ct = 1;
+ dgc_info.init = tc9564_msigen_irq_chip_init;
+ dgc_info.exit = tc9564_msigen_irq_chip_exit;
+
+ info.fwnode = of_fwnode_handle(dev->of_node);
+ info.domain_flags = IRQ_DOMAIN_FLAG_DESTROY_GC;
+ info.size = HWIRQ_COUNT;
+ info.hwirq_max = HWIRQ_COUNT;
+ info.ops = &irq_generic_chip_ops;
+ info.host_data = msigen;
+ info.dgc_info = &dgc_info;
+ info.init = tc9564_msigen_irq_domain_init;
+ info.exit = tc9564_msigen_irq_domain_exit;
+
+ irq_domain = devm_irq_domain_instantiate(dev, &info);
+ if (IS_ERR(irq_domain))
+ return dev_err_probe(dev, PTR_ERR(irq_domain),
+ "failed to instantiate IRQ domain\n");
+
+ return 0;
+}
+
+static const struct of_device_id tc9564_msigen_of_match[] = {
+ { .compatible = "toshiba,tc9564-msigen" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, tc9564_msigen_of_match);
+
+static struct platform_driver tc9564_msigen_driver = {
+ .probe = tc9564_msigen_probe,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = tc9564_msigen_of_match,
+ },
+};
+module_platform_driver(tc9564_msigen_driver);
+
+MODULE_AUTHOR("Daniel Thompson <danielt@kernel.org>");
+MODULE_DESCRIPTION("TC9564 MSIGEN IRQ Controller Driver");
+MODULE_LICENSE("GPL");
--
2.53.0
^ permalink raw reply [flat|nested] 3+ messages in thread