[PATCH v2 1/5] soc: fsl: qe: Add an interrupt controller for QUICC Engine Ports
Christophe Leroy
christophe.leroy at csgroup.eu
Mon Aug 18 18:45:54 AEST 2025
The QUICC Engine provides interrupts for a few I/O ports. This is
handled via a separate interrupt ID and managed via a triplet of
dedicated registers hosted by the SoC.
Implement an interrupt driver for it for that those IRQs can then
be linked to the related GPIOs.
The number of ports for which interrupts are supported depends on
the microcontroller:
- mpc8323 has 10 interrupts
- mpc8360 has 28 interrupts
- mpc8568 has 18 interrupts
So add this information as data of the compatible.
Signed-off-by: Christophe Leroy <christophe.leroy at csgroup.eu>
---
v2: Properly cast (void *) to long instead of int to enable warning-free build on PPC64
---
drivers/soc/fsl/qe/Makefile | 2 +-
drivers/soc/fsl/qe/qe_ports_ic.c | 156 +++++++++++++++++++++++++++++++
2 files changed, 157 insertions(+), 1 deletion(-)
create mode 100644 drivers/soc/fsl/qe/qe_ports_ic.c
diff --git a/drivers/soc/fsl/qe/Makefile b/drivers/soc/fsl/qe/Makefile
index ec8506e13113..901a9c40d5eb 100644
--- a/drivers/soc/fsl/qe/Makefile
+++ b/drivers/soc/fsl/qe/Makefile
@@ -11,4 +11,4 @@ obj-$(CONFIG_UCC_SLOW) += ucc_slow.o
obj-$(CONFIG_UCC_FAST) += ucc_fast.o
obj-$(CONFIG_QE_TDM) += qe_tdm.o
obj-$(CONFIG_QE_USB) += usb.o
-obj-$(CONFIG_QE_GPIO) += gpio.o
+obj-$(CONFIG_QE_GPIO) += gpio.o qe_ports_ic.o
diff --git a/drivers/soc/fsl/qe/qe_ports_ic.c b/drivers/soc/fsl/qe/qe_ports_ic.c
new file mode 100644
index 000000000000..9715643d36a6
--- /dev/null
+++ b/drivers/soc/fsl/qe/qe_ports_ic.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * QUICC ENGINE I/O Ports Interrupt Controller
+ *
+ * Copyright (c) 2025 Christophe Leroy CS GROUP France (christophe.leroy at csgroup.eu)
+ */
+
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/platform_device.h>
+
+/* QE IC registers offset */
+#define CEPIER 0x0c
+#define CEPIMR 0x10
+#define CEPICR 0x14
+
+struct qepic_data {
+ void __iomem *reg;
+ struct irq_domain *host;
+};
+
+static void qepic_mask(struct irq_data *d)
+{
+ struct qepic_data *data = irq_data_get_irq_chip_data(d);
+
+ clrbits32(data->reg + CEPIMR, 1 << (31 - irqd_to_hwirq(d)));
+}
+
+static void qepic_unmask(struct irq_data *d)
+{
+ struct qepic_data *data = irq_data_get_irq_chip_data(d);
+
+ setbits32(data->reg + CEPIMR, 1 << (31 - irqd_to_hwirq(d)));
+}
+
+static void qepic_end(struct irq_data *d)
+{
+ struct qepic_data *data = irq_data_get_irq_chip_data(d);
+
+ out_be32(data->reg + CEPIER, 1 << (31 - irqd_to_hwirq(d)));
+}
+
+static int qepic_set_type(struct irq_data *d, unsigned int flow_type)
+{
+ struct qepic_data *data = irq_data_get_irq_chip_data(d);
+ unsigned int vec = (unsigned int)irqd_to_hwirq(d);
+
+ switch (flow_type & IRQ_TYPE_SENSE_MASK) {
+ case IRQ_TYPE_EDGE_FALLING:
+ setbits32(data->reg + CEPICR, 1 << (31 - vec));
+ return 0;
+ case IRQ_TYPE_EDGE_BOTH:
+ case IRQ_TYPE_NONE:
+ clrbits32(data->reg + CEPICR, 1 << (31 - vec));
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static struct irq_chip qepic = {
+ .name = "QEPIC",
+ .irq_mask = qepic_mask,
+ .irq_unmask = qepic_unmask,
+ .irq_eoi = qepic_end,
+ .irq_set_type = qepic_set_type,
+};
+
+static int qepic_get_irq(struct irq_desc *desc)
+{
+ struct qepic_data *data = irq_desc_get_handler_data(desc);
+ u32 event = in_be32(data->reg + CEPIER);
+
+ if (!event)
+ return -1;
+
+ return irq_find_mapping(data->host, 32 - ffs(event));
+}
+
+static void qepic_cascade(struct irq_desc *desc)
+{
+ generic_handle_irq(qepic_get_irq(desc));
+}
+
+static int qepic_host_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hw)
+{
+ irq_set_chip_data(virq, h->host_data);
+ irq_set_chip_and_handler(virq, &qepic, handle_fasteoi_irq);
+ return 0;
+}
+
+static const struct irq_domain_ops qepic_host_ops = {
+ .map = qepic_host_map,
+};
+
+static int qepic_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct qepic_data *data;
+ unsigned long nb;
+ int irq;
+
+ nb = (unsigned long)of_device_get_match_data(dev);
+ if (nb < 1 || nb > 32)
+ return -EINVAL;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->reg = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(data->reg))
+ return PTR_ERR(data->reg);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ data->host = irq_domain_add_linear(dev->of_node, nb, &qepic_host_ops, data);
+ if (!data->host)
+ return -ENODEV;
+
+ irq_set_handler_data(irq, data);
+ irq_set_chained_handler(irq, qepic_cascade);
+
+ return 0;
+}
+
+static const struct of_device_id qepic_match[] = {
+ {
+ .compatible = "fsl,mpc8323-qe-ports-ic",
+ .data = (void *)10,
+ },
+ {
+ .compatible = "fsl,mpc8360-qe-ports-ic",
+ .data = (void *)28,
+ },
+ {
+ .compatible = "fsl,mpc8568-qe-ports-ic",
+ .data = (void *)18,
+ },
+ {},
+};
+
+static struct platform_driver qepic_driver = {
+ .driver = {
+ .name = "qe_ports_ic",
+ .of_match_table = qepic_match,
+ },
+ .probe = qepic_probe,
+};
+
+static int __init qepic_init(void)
+{
+ return platform_driver_register(&qepic_driver);
+}
+arch_initcall(qepic_init);
--
2.49.0
More information about the Linuxppc-dev
mailing list