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
| ||
|
Date: Mon, 24 Jan 2022 12:56:52 +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 v2] 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> --- v1: https://lore.kernel.org/lkml/20220119201741.717770-1-nikita.yoush@cogentembedded.com/ Changes from v1: - fixed order of kzalloc() args, caught by kbuild robot 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..0f461fda6610 --- /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(sizeof(*ch) + count * sizeof(ch->out[0]), GFP_KERNEL); + 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