Hi Marc,
Registering domain for each channel is indeed a bit unreasonable, I have
changed the driver to support only one channel (channel 0 by default) as
muti-channels is designed to route interrupt to different cores. The fixed
channel is enough for us.
Thanks for your kindly review.
ChangeLog:
V1->V2: *squash patches:
drivers/irqchip: enable INTMUX interrupt controller driver
drivers/irqchip: add NXP INTMUX interrupt multiplexer support
*remove properity "fsl,intmux_chans", only support channel 0 by
default.
*delete two unused macros.
*align the various field in struct intmux_data.
*turn to spin lock _irqsave version.
*delete struct intmux_irqchip_data
*disable interrupt in probe stage and clear pending status in remove
stage
Joakim Zhang (2):
dt-bindings/irq: add binding for NXP INTMUX interrupt multiplexer
drivers/irqchip: add NXP INTMUX interrupt multiplexer support
.../interrupt-controller/fsl,intmux.txt | 28 ++
drivers/irqchip/Kconfig | 6 +
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-imx-intmux.c | 240 ++++++++++++++++++
4 files changed, 275 insertions(+)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/fsl,intmux.txt
create mode 100644 drivers/irqchip/irq-imx-intmux.c
--
2.17.1
This patch adds the DT bindings for the NXP INTMUX interrupt multiplexer
found in the i.MX8 family SoCs.
Signed-off-by: Joakim Zhang <[email protected]>
---
.../interrupt-controller/fsl,intmux.txt | 28 +++++++++++++++++++
1 file changed, 28 insertions(+)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/fsl,intmux.txt
diff --git a/Documentation/devicetree/bindings/interrupt-controller/fsl,intmux.txt b/Documentation/devicetree/bindings/interrupt-controller/fsl,intmux.txt
new file mode 100644
index 000000000000..a9a16b5046c5
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/fsl,intmux.txt
@@ -0,0 +1,28 @@
+Freescale INTMUX interrupt multiplexer
+
+Required properties:
+
+- compatible: Should be:
+ - "fsl,imx-intmux"
+- reg: Physical base address and size of registers.
+- interrupts: Should contain one parent interrupt line used to multiplex the
+ input interrupt sources.
+- clocks: Should contain one clock for entry in clock-names.
+- clock-names:
+ - "ipg": main logic clock
+- interrupt-controller: Identifies the node as an interrupt controller.
+- #interrupt-cells: Specifies the number of cells needed to encode an
+ interrupt source. The value must be 1.
+
+Example:
+
+ intmux@37400000 {
+ compatible = "fsl,imx-intmux";
+ reg = <0x37400000 0x1000>;
+ interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk IMX8QM_CM40_IPG_CLK>;
+ clock-names = "ipg";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ };
+
--
2.17.1
The Interrupt Multiplexer (INTMUX) expands the number of peripherals
that can interrupt the core:
* The INTMUX has 8 channels that are assigned to 8 NVIC interrupt slots.
* Each INTMUX channel can receive up to 32 interrupt sources and has 1
interrupt output.
* The INTMUX routes the interrupt sources to the interrupt outputs.
In the driver, we only use channel 0 to steer 32 interrupt sources into
1 interrupt out.
Signed-off-by: Joakim Zhang <[email protected]>
---
drivers/irqchip/Kconfig | 6 +
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-imx-intmux.c | 240 +++++++++++++++++++++++++++++++
3 files changed, 247 insertions(+)
create mode 100644 drivers/irqchip/irq-imx-intmux.c
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index ba152954324b..7e2b1e9d0b45 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -457,6 +457,12 @@ config IMX_IRQSTEER
help
Support for the i.MX IRQSTEER interrupt multiplexer/remapper.
+config IMX_INTMUX
+ def_bool y if ARCH_MXC
+ select IRQ_DOMAIN
+ help
+ Support for the i.MX INTMUX interrupt multiplexer.
+
config LS1X_IRQ
bool "Loongson-1 Interrupt Controller"
depends on MACH_LOONGSON32
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index e806dda690ea..af976a79d1fb 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -100,6 +100,7 @@ obj-$(CONFIG_CSKY_MPINTC) += irq-csky-mpintc.o
obj-$(CONFIG_CSKY_APB_INTC) += irq-csky-apb-intc.o
obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o
obj-$(CONFIG_IMX_IRQSTEER) += irq-imx-irqsteer.o
+obj-$(CONFIG_IMX_INTMUX) += irq-imx-intmux.o
obj-$(CONFIG_MADERA_IRQ) += irq-madera.o
obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o
obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o
diff --git a/drivers/irqchip/irq-imx-intmux.c b/drivers/irqchip/irq-imx-intmux.c
new file mode 100644
index 000000000000..8a3a7de60219
--- /dev/null
+++ b/drivers/irqchip/irq-imx-intmux.c
@@ -0,0 +1,240 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright 2017 NXP
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/spinlock.h>
+
+#define CHANIER(n) (0x10 + (0x40 * n))
+#define CHANIPR(n) (0x20 + (0x40 * n))
+
+/* INTMUX Block Diagram
+ *
+ * ________________
+ * interrupt source # 0 +---->| |
+ * | | |
+ * interrupt source # 1 +++-->| |
+ * ... | | | channel # 0 |--------->interrupt out # 0
+ * ... | | | |
+ * ... | | | |
+ * interrupt source # X-1 +++-->|________________|
+ * | | |
+ * | | |
+ * | | | ________________
+ * +---->| |
+ * | | | | |
+ * | +-->| |
+ * | | | | channel # 1 |--------->interrupt out # 1
+ * | | +>| |
+ * | | | | |
+ * | | | |________________|
+ * | | |
+ * | | |
+ * | | | ...
+ * | | | ...
+ * | | |
+ * | | | ________________
+ * +---->| |
+ * | | | |
+ * +-->| |
+ * | | channel # N |--------->interrupt out # N
+ * +>| |
+ * | |
+ * |________________|
+ *
+ *
+ * N: Interrupt Channel Instance Number (N=7)
+ * X: Interrupt Source Number for each channel (X=32)
+ *
+ * The INTMUX interrupt multiplexer has 8 channels, each channel receives 32
+ * interrupt sources and generates 1 interrupt output.
+ *
+ * The aim of INTMUX design is to route same interrupt to different cores.
+ *
+ * In this driver, we only use channel 0 to steer 32 interrupt sources into 1
+ * interrupt out. We can extend the driver to support muti-channels in the
+ * future if has requirement.
+ *
+ */
+
+struct intmux_data {
+ void __iomem *regs;
+ struct clk *ipg_clk;
+ int chan_idx;
+ int irq;
+ struct irq_domain *domain;
+ raw_spinlock_t lock;
+};
+
+static void imx_intmux_irq_mask(struct irq_data *d)
+{
+ struct intmux_data *data = d->chip_data;
+ unsigned long flags;
+ void __iomem *reg;
+ u32 val;
+
+ raw_spin_lock_irqsave(&data->lock, flags);
+ reg = data->regs + CHANIER(data->chan_idx);
+ val = readl_relaxed(reg);
+ /* disable the interrupt source of this channel */
+ val &= ~(1 << d->hwirq);
+ writel_relaxed(val, reg);
+ raw_spin_unlock_irqrestore(&data->lock, flags);
+}
+
+static void imx_intmux_irq_unmask(struct irq_data *d)
+{
+ struct intmux_data *data = d->chip_data;
+ unsigned long flags;
+ void __iomem *reg;
+ u32 val;
+
+ raw_spin_lock_irqsave(&data->lock, flags);
+ reg = data->regs + CHANIER(data->chan_idx);
+ val = readl_relaxed(reg);
+ /* enable the interrupt source of this channel */
+ val |= 1 << d->hwirq;
+ writel_relaxed(val, reg);
+ raw_spin_unlock_irqrestore(&data->lock, flags);
+}
+
+static struct irq_chip imx_intmux_irq_chip = {
+ .name = "intmux",
+ .irq_mask = imx_intmux_irq_mask,
+ .irq_unmask = imx_intmux_irq_unmask,
+};
+
+static int imx_intmux_irq_map(struct irq_domain *h, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ irq_set_status_flags(irq, IRQ_LEVEL);
+ irq_set_chip_data(irq, h->host_data);
+ irq_set_chip_and_handler(irq, &imx_intmux_irq_chip, handle_level_irq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops imx_intmux_domain_ops = {
+ .map = imx_intmux_irq_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static void imx_intmux_irq_handler(struct irq_desc *desc)
+{
+ struct intmux_data *data = irq_desc_get_handler_data(desc);
+ unsigned long irqstat;
+ int pos, virq;
+
+ chained_irq_enter(irq_desc_get_chip(desc), desc);
+
+ /* read the interrupt source pending status of this channel */
+ irqstat = readl_relaxed(data->regs + CHANIPR(data->chan_idx));
+
+ for_each_set_bit(pos, &irqstat, 32) {
+ virq = irq_find_mapping(data->domain, pos);
+ if (virq)
+ generic_handle_irq(virq);
+ }
+
+ chained_irq_exit(irq_desc_get_chip(desc), desc);
+}
+
+static int imx_intmux_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct intmux_data *data;
+ int ret;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(data->regs)) {
+ dev_err(&pdev->dev, "failed to initialize reg\n");
+ return PTR_ERR(data->regs);
+ }
+
+ data->ipg_clk = devm_clk_get(&pdev->dev, "ipg");
+ if (IS_ERR(data->ipg_clk)) {
+ ret = PTR_ERR(data->ipg_clk);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "failed to get ipg clk: %d\n", ret);
+ return ret;
+ }
+
+ raw_spin_lock_init(&data->lock);
+
+ ret = clk_prepare_enable(data->ipg_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable ipg clk: %d\n", ret);
+ return ret;
+ }
+
+ data->domain = irq_domain_add_linear(np, 32, &imx_intmux_domain_ops,
+ data);
+ if (!data->domain) {
+ dev_err(&pdev->dev, "failed to create IRQ domain\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* use channel 0 for interrupt output */
+ data->chan_idx = 0;
+
+ data->irq = irq_of_parse_and_map(np, data->chan_idx);
+ if (!data->irq) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ irq_set_chained_handler_and_data(data->irq, imx_intmux_irq_handler,
+ data);
+
+ /* disable all interrupt sources of this channel */
+ writel_relaxed(0, data->regs + CHANIER(data->chan_idx));
+
+ platform_set_drvdata(pdev, data);
+
+ return 0;
+out:
+ clk_disable_unprepare(data->ipg_clk);
+ return ret;
+}
+
+static int imx_intmux_remove(struct platform_device *pdev)
+{
+ struct intmux_data *data = platform_get_drvdata(pdev);
+
+ /* clear all interrupt sources pending status of this channel */
+ writel_relaxed(0, data->regs + CHANIPR(data->chan_idx));
+
+ irq_set_chained_handler_and_data(data->irq, NULL, NULL);
+
+ irq_domain_remove(data->domain);
+
+ clk_disable_unprepare(data->ipg_clk);
+
+ return 0;
+}
+
+static const struct of_device_id imx_intmux_id[] = {
+ { .compatible = "fsl,imx-intmux", },
+ { /* sentinel */ },
+};
+
+static struct platform_driver imx_intmux_driver = {
+ .driver = {
+ .name = "imx-intmux",
+ .of_match_table = imx_intmux_id,
+ },
+ .probe = imx_intmux_probe,
+ .remove = imx_intmux_remove,
+};
+builtin_platform_driver(imx_intmux_driver);
--
2.17.1
On 2019-12-19 10:21, Joakim Zhang wrote:
> Hi Marc,
>
> Registering domain for each channel is indeed a bit unreasonable,
> I have
> changed the driver to support only one channel (channel 0 by default)
> as
> muti-channels is designed to route interrupt to different cores. The
> fixed
> channel is enough for us.
But that's not what the HW does, right? Next time, you will post
the patches that enabled multiple channels, and the interrupt specifier
will have to change, which will impact all existing DTs.
So please do the right thing from the start. Add channel selection to
the interrupt specifier. Pick the right irq domain with a .select()
callback, and your driver will magically work.
Thanks,
M.
> Thanks for your kindly review.
>
> ChangeLog:
> V1->V2: *squash patches:
> drivers/irqchip: enable INTMUX interrupt controller driver
> drivers/irqchip: add NXP INTMUX interrupt multiplexer support
> *remove properity "fsl,intmux_chans", only support channel 0 by
> default.
> *delete two unused macros.
> *align the various field in struct intmux_data.
> *turn to spin lock _irqsave version.
> *delete struct intmux_irqchip_data
> *disable interrupt in probe stage and clear pending status in remove
> stage
>
> Joakim Zhang (2):
> dt-bindings/irq: add binding for NXP INTMUX interrupt multiplexer
> drivers/irqchip: add NXP INTMUX interrupt multiplexer support
>
> .../interrupt-controller/fsl,intmux.txt | 28 ++
> drivers/irqchip/Kconfig | 6 +
> drivers/irqchip/Makefile | 1 +
> drivers/irqchip/irq-imx-intmux.c | 240
> ++++++++++++++++++
> 4 files changed, 275 insertions(+)
> create mode 100644
> Documentation/devicetree/bindings/interrupt-controller/fsl,intmux.txt
> create mode 100644 drivers/irqchip/irq-imx-intmux.c
--
Jazz is not dead. It just smells funny...