lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date:   Wed, 19 Jan 2022 23:17:41 +0300
From:   Nikita Yushchenko <nikita.yoush@...entembedded.com>
To:     Thomas Gleixner <tglx@...utronix.de>,
        Marc Zyngier <maz@...nel.org>,
        Geert Uytterhoeven <geert@...ux-m68k.org>,
        Eugeniu Rosca <erosca@...adit-jv.com>
Cc:     linux-kernel@...r.kernel.org,
        Nikita Yushchenko <nikita.yoush@...entembedded.com>
Subject: [PATCH] drivers: irqchip: add irq-type-changer

Irq type changer is a virtual irqchip useful to support boards that
change (e.g. invert) interrupt signal between producer and consumer.

Usage example, for Kingfisher extension board for Renesas Gen-3 Soc,
that has WiFi interrupt delivered over inverting level-shifter:

/ {
	gpio1_25_inverted: inverter {
		compatible = "linux,irq-type-changer";
		interrupt-controller;
		#interrupt-cells = <2>;
		interrupt-parent = <&gpio1>;
		interrupts = <25 IRQ_TYPE_EDGE_FALLING>;
	};
};

&wlcore {
        interrupt-parent = <&gpio1_25_inverted>;
	interrupts = <0 IRQ_TYPE_EDGE_RISING>;
};

Then, wlcore driver observes IRQ_TYPE_EDGE_RISING trigger type and
configures interrupt output as such. At the same time, gpio-rcar driver
gets interrupt configured for IRQ_TYPE_EDGE_FALLING.

This version uses hierarchical irq_domain API, and works only with
parent interrupt domains compatible with that API.

Signed-off-by: Nikita Yushchenko <nikita.yoush@...entembedded.com>
---
My previous attempt to solve the issue:
  https://lore.kernel.org/lkml/20211228165642.2514766-1-nikita.yoush@cogentembedded.com/

Additional patches required for this to work with gpio-rcar:
  https://lore.kernel.org/lkml/20220119160715.650535-1-nikita.yoush@cogentembedded.com/
  https://lore.kernel.org/lkml/20220119143211.633399-1-nikita.yoush@cogentembedded.com/

 drivers/irqchip/Kconfig            |  18 ++++
 drivers/irqchip/Makefile           |   1 +
 drivers/irqchip/irq-type-changer.c | 162 +++++++++++++++++++++++++++++
 3 files changed, 181 insertions(+)
 create mode 100644 drivers/irqchip/irq-type-changer.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 7038957f4a77..7f348016e82c 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -617,4 +617,22 @@ config MCHP_EIC
 	help
 	  Support for Microchip External Interrupt Controller.
 
+config IRQ_TYPE_CHANGER
+	bool "Interrupt trigger type changer"
+	select IRQ_DOMAIN
+	select IRQ_DOMAIN_HIERARCHY
+	help
+	  Interrupt trigger type changer is designed to support boards that
+	  modify (e.g. invert) signal between interrupt source and interrupt
+	  controller input. So trigger type configured by a driver for some
+	  interrupt output pin does not match trigger type that shall be used
+	  to configure interrupt controller's input where that pin is connected.
+
+	  In this case, board device tree shall add an interrupt trigger
+	  type changer node and use it as the interrupt parent for the node
+	  representing interrupt source. Then, interrupt trigger type defined
+	  in the interrupt source node will be visible for the interrupt source
+	  driver, and (different) trigger type defined inside the changer node
+	  will be used to configure the interrupt controller.
+
 endmenu
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index c1f611cbfbf8..57a664837857 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -117,3 +117,4 @@ obj-$(CONFIG_WPCM450_AIC)		+= irq-wpcm450-aic.o
 obj-$(CONFIG_IRQ_IDT3243X)		+= irq-idt3243x.o
 obj-$(CONFIG_APPLE_AIC)			+= irq-apple-aic.o
 obj-$(CONFIG_MCHP_EIC)			+= irq-mchp-eic.o
+obj-$(CONFIG_IRQ_TYPE_CHANGER)		+= irq-type-changer.o
diff --git a/drivers/irqchip/irq-type-changer.c b/drivers/irqchip/irq-type-changer.c
new file mode 100644
index 000000000000..731ea61e7fcc
--- /dev/null
+++ b/drivers/irqchip/irq-type-changer.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/of_irq.h>
+
+struct changer {
+	unsigned long count;
+	struct {
+		struct irq_fwspec fwspec;
+		unsigned int type;
+	} out[0];
+};
+
+static int changer_set_type(struct irq_data *data, unsigned int type)
+{
+	struct changer *ch = data->domain->host_data;
+	struct irq_data *parent_data = data->parent_data;
+
+	return parent_data->chip->irq_set_type(parent_data,
+					       ch->out[data->hwirq].type);
+}
+
+static struct irq_chip changer_chip = {
+	.name			= "type-changer",
+	.irq_mask		= irq_chip_mask_parent,
+	.irq_unmask		= irq_chip_unmask_parent,
+	.irq_eoi		= irq_chip_eoi_parent,
+	.irq_set_type		= changer_set_type,
+	.irq_retrigger		= irq_chip_retrigger_hierarchy,
+	.irq_set_affinity	= irq_chip_set_affinity_parent,
+	.irq_set_wake		= irq_chip_set_wake_parent,
+};
+
+static int changer_domain_translate(struct irq_domain *domain,
+				    struct irq_fwspec *fwspec,
+				    unsigned long *hwirq,
+				    unsigned int *type)
+{
+	struct changer *ch = domain->host_data;
+
+	if (fwspec->param_count != 2)
+		return -EINVAL;
+	if (fwspec->param[0] >= ch->count)
+		return -ENXIO;
+
+	*hwirq = fwspec->param[0];
+	*type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
+	return 0;
+}
+
+static int changer_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				unsigned int nr_irqs, void *arg)
+{
+	struct changer *ch = domain->host_data;
+	struct irq_fwspec *fwspec = arg;
+	irq_hw_number_t hwirq;
+	unsigned int type;
+	int ret;
+
+	if (WARN_ON(nr_irqs != 1))
+		return -EINVAL;
+
+	ret = changer_domain_translate(domain, fwspec, &hwirq, &type);
+	if (ret)
+		return ret;
+
+	irq_domain_set_hwirq_and_chip(domain, virq, hwirq, &changer_chip, ch);
+
+	return irq_domain_alloc_irqs_parent(domain, virq, 1,
+					    &ch->out[hwirq].fwspec);
+}
+
+static const struct irq_domain_ops changer_domain_ops = {
+	.translate	= changer_domain_translate,
+	.alloc		= changer_domain_alloc,
+	.free		= irq_domain_free_irqs_common,
+};
+
+static int __init changer_of_init(struct device_node *node,
+				  struct device_node *parent)
+{
+	struct irq_domain *domain, *parent_domain;
+	int count, i, ret;
+	struct changer *ch;
+	struct of_phandle_args pargs;
+	irq_hw_number_t unused;
+
+	if (!parent) {
+		pr_err("%pOF: no parent node\n", node);
+		return -EINVAL;
+	}
+
+	parent_domain = irq_find_host(parent);
+	if (!parent_domain) {
+		pr_err("%pOF: no parent domain\n", node);
+		return -EINVAL;
+	}
+
+	if (WARN_ON(!parent_domain->ops->translate))
+		return -EINVAL;
+
+	count = of_irq_count(node);
+	if (count < 1) {
+		pr_err("%pOF: no interrupts defined\n", node);
+		return -EINVAL;
+	}
+
+	ch = kzalloc(GFP_KERNEL, sizeof(*ch) + count * sizeof(ch->out[0]));
+	if (!ch)
+		return -ENOMEM;
+	ch->count = count;
+
+	for (i = 0; i < count; i++) {
+		ret = of_irq_parse_one(node, i, &pargs);
+		if (ret) {
+			pr_err("%pOF: interrupt %d: error %d parsing\n",
+			       node, i, ret);
+			goto out_free;
+		}
+		of_phandle_args_to_fwspec(pargs.np, pargs.args,
+					  pargs.args_count,
+					  &ch->out[i].fwspec);
+		ret = parent_domain->ops->translate(parent_domain,
+						    &ch->out[i].fwspec,
+						    &unused,
+						    &ch->out[i].type);
+		if (ret) {
+			pr_err("%pOF: interrupt %d: error %d extracting type\n",
+			       node, i, ret);
+			goto out_free;
+		}
+		if (ch->out[i].type == IRQ_TYPE_NONE) {
+			pr_err("%pOF: interrupt %d: no type\n", node, i);
+			ret = -ENXIO;
+			goto out_free;
+		}
+	}
+
+	domain = irq_domain_create_hierarchy(parent_domain, 0, count,
+					     of_node_to_fwnode(node),
+					     &changer_domain_ops, ch);
+	if (!domain) {
+		ret = -ENOMEM;
+		goto out_free;
+	}
+
+	return 0;
+
+out_free:
+	kfree(ch);
+	return ret;
+}
+
+IRQCHIP_PLATFORM_DRIVER_BEGIN(changer)
+IRQCHIP_MATCH("linux,irq-type-changer", changer_of_init)
+IRQCHIP_PLATFORM_DRIVER_END(changer)
+MODULE_AUTHOR("Nikita Yushchenko <nikita.yoush.cogentembedded.com>");
+MODULE_DESCRIPTION("Virtual irqchip to support trigger type change in route");
+MODULE_LICENSE("GPL v2");
-- 
2.30.2

Powered by blists - more mailing lists