1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * RZ/N1 GPIO Interrupt Multiplexer 4 * 5 * Copyright 2025 Schneider Electric 6 * Author: Herve Codina <herve.codina@bootlin.com> 7 */ 8 9 #include <linux/bitmap.h> 10 #include <linux/bitops.h> 11 #include <linux/mod_devicetable.h> 12 #include <linux/module.h> 13 #include <linux/of.h> 14 #include <linux/of_irq.h> 15 #include <linux/platform_device.h> 16 #include <dt-bindings/interrupt-controller/arm-gic.h> 17 18 /* 19 * Up to 8 output lines are connected to GIC SPI interrupt controller 20 * starting at IRQ 103. 21 */ 22 #define RZN1_IRQMUX_GIC_SPI_BASE 103 23 #define RZN1_IRQMUX_NUM_OUTPUTS 8 24 25 static int rzn1_irqmux_parent_args_to_line_index(struct device *dev, 26 const struct of_phandle_args *parent_args) 27 { 28 /* 29 * The parent interrupt should be one of the GIC controller. 30 * Three arguments must be provided. 31 * - args[0]: GIC_SPI 32 * - args[1]: The GIC interrupt number 33 * - args[2]: The interrupt flags 34 * 35 * We retrieve the line index based on the GIC interrupt number 36 * provided. 37 */ 38 39 if (parent_args->args_count != 3 || parent_args->args[0] != GIC_SPI) { 40 dev_err(dev, "Invalid interrupt-map item\n"); 41 return -EINVAL; 42 } 43 44 if (parent_args->args[1] < RZN1_IRQMUX_GIC_SPI_BASE || 45 parent_args->args[1] >= RZN1_IRQMUX_GIC_SPI_BASE + RZN1_IRQMUX_NUM_OUTPUTS) { 46 dev_err(dev, "Invalid GIC interrupt %u\n", parent_args->args[1]); 47 return -EINVAL; 48 } 49 50 return parent_args->args[1] - RZN1_IRQMUX_GIC_SPI_BASE; 51 } 52 53 static int rzn1_irqmux_probe(struct platform_device *pdev) 54 { 55 DECLARE_BITMAP(index_done, RZN1_IRQMUX_NUM_OUTPUTS) = {}; 56 struct device *dev = &pdev->dev; 57 struct device_node *np = dev->of_node; 58 struct of_imap_parser imap_parser; 59 struct of_imap_item imap_item; 60 u32 __iomem *regs; 61 int index; 62 int ret; 63 u32 tmp; 64 65 regs = devm_platform_ioremap_resource(pdev, 0); 66 if (IS_ERR(regs)) 67 return PTR_ERR(regs); 68 69 /* We support only #interrupt-cells = <1> and #address-cells = <0> */ 70 ret = of_property_read_u32(np, "#interrupt-cells", &tmp); 71 if (ret) 72 return ret; 73 if (tmp != 1) 74 return -EINVAL; 75 76 ret = of_property_read_u32(np, "#address-cells", &tmp); 77 if (ret) 78 return ret; 79 if (tmp != 0) 80 return -EINVAL; 81 82 ret = of_imap_parser_init(&imap_parser, np, &imap_item); 83 if (ret) 84 return ret; 85 86 for_each_of_imap_item(&imap_parser, &imap_item) { 87 index = rzn1_irqmux_parent_args_to_line_index(dev, &imap_item.parent_args); 88 if (index < 0) { 89 of_node_put(imap_item.parent_args.np); 90 return index; 91 } 92 93 if (test_and_set_bit(index, index_done)) { 94 of_node_put(imap_item.parent_args.np); 95 dev_err(dev, "Mux output line %d already defined in interrupt-map\n", 96 index); 97 return -EINVAL; 98 } 99 100 /* 101 * The child #address-cells is 0 (already checked). The first 102 * value in imap item is the src hwirq. 103 */ 104 writel(imap_item.child_imap[0], regs + index); 105 } 106 107 return 0; 108 } 109 110 static const struct of_device_id rzn1_irqmux_of_match[] = { 111 { .compatible = "renesas,rzn1-gpioirqmux", }, 112 { /* sentinel */ } 113 }; 114 MODULE_DEVICE_TABLE(of, rzn1_irqmux_of_match); 115 116 static struct platform_driver rzn1_irqmux_driver = { 117 .probe = rzn1_irqmux_probe, 118 .driver = { 119 .name = "rzn1_irqmux", 120 .of_match_table = rzn1_irqmux_of_match, 121 }, 122 }; 123 module_platform_driver(rzn1_irqmux_driver); 124 125 MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>"); 126 MODULE_DESCRIPTION("Renesas RZ/N1 GPIO IRQ Multiplexer Driver"); 127 MODULE_LICENSE("GPL"); 128