Re: [PATCH] irqchip: add TS7800v1 fpga based controller driver

From: firas ashkar
Date: Thu Oct 20 2022 - 10:03:52 EST


thank you for the comments, pls check below for replies and acks

On Tue, 2022-10-18 at 17:36 +0100, Marc Zyngier wrote:
> +Arnd
>
> On Tue, 18 Oct 2022 14:30:34 +0100,
> Firas Ashkar <firas.ashkar@xxxxxxxxxxxxxxxxxxxx> wrote:
> >
> > 1. add TS-7800v1 fpga based irq controller driver, and
> > 2. add related memory and irq resources
> >
> > By default only mapped FPGA interrupts will be chained/multiplexed,
> > these
> > chained interrupts will then be available to other device drivers
> > to
> > request via the corresponding Linux IRQs.
> >
> > $ cat /proc/cpuinfo
> > processor       : 0
> > model name      : Feroceon rev 0 (v5l)
> > BogoMIPS        : 333.33
> > Features        : swp half thumb fastmult edsp
> > CPU implementer : 0x41
> > CPU architecture: 5TEJ
> > CPU variant     : 0x0
> > CPU part        : 0x926
> > CPU revision    : 0
> >
> > Hardware        : Technologic Systems TS-78xx SBC
> > Revision        : 0000
> > Serial          : 0000000000000000
> > $
> >
> > $ cat /proc/interrupts
> >            CPU0
> >   1:        902  orion_irq     Level     orion_tick
> >   4:        795  orion_irq     Level     ttyS0
> >  13:          0  orion_irq     Level     ehci_hcd:usb2
> >  18:          0  orion_irq     Level     ehci_hcd:usb1
> >  22:         69  orion_irq     Level     eth0
> >  23:        171  orion_irq     Level     orion-mdio
> >  29:          0  orion_irq     Level     mv_crypto
> >  31:          2  orion_irq     Level     mv_xor.0
> >  65:          1  ts7800-irqc   0 Edge      ts-dmac-cpupci
> > Err:          0
> > $
> >
> > $ cat /proc/iomem
> > 00000000-07ffffff : System RAM
> >   00008000-00978fff : Kernel code
> >   00d20000-00e54f97 : Kernel data
> > e8000044-e8000047 : timeriomem_rng
> >   e8000044-e8000047 : timeriomem_rng timeriomem_rng
> > e8000200-e80002ff : ts_irqc
> >   e8000200-e80002ff : ts7800-irqc ts_irqc
> > e8000400-e80004ff : ts_dmac
> >   e8000400-e80004ff : ts7800-dmac ts_dmac
> > e8000804-e8000807 : gen_nand
> >   e8000804-e8000807 : gen_nand gen_nand
> > e8000808-e8000808 : rtc-m48t86
> >   e8000808-e8000808 : rtc-m48t86 rtc-m48t86
> > e800080c-e800080c : rtc-m48t86
> >   e800080c-e800080c : rtc-m48t86 rtc-m48t86
> > f1012000-f10120ff : serial8250.0
> >   f1012000-f101201f : serial
> > f1012100-f10121ff : serial8250.1
> >   f1012100-f101211f : serial
> > f1020108-f102010b : orion_wdt
> > f1020300-f1020303 : orion_wdt
> > f1020400-f102040f : ts_bridge
> >   f1020400-f102040f : ts7800-irqc ts_bridge
> > f1050000-f1050fff : orion-ehci.0
> >   f1050000-f1050fff : orion-ehci.0 orion-ehci.0
> > f1060900-f10609ff : xor 0 low
> > f1060b00-f1060bff : xor 0 high
> > f1072000-f1075fff : ge00 base
> >   f1072004-f1072087 : ge00 mvmdio base
> > f1080000-f1084fff : sata base
> > f1090000-f109ffff : regs
> >   f1090000-f109ffff : mv_crypto regs
> > f10a0000-f10a0fff : orion-ehci.1
> >   f10a0000-f10a0fff : orion-ehci.1 orion-ehci.1
> > f2200000-f2201fff : sram
> >   f2200000-f2201fff : mv_crypto sram
> > $
> >
> > $ uname -a
> > Linux ts-7800 6.1.0-rc1 #2 PREEMPT Mon Oct 17 11:19:12 EDT 2022
> > armv5tel
> >  GNU/Linux
> > $
>
> I don't see how useful this information is. Please limit the commit
> message to the provided functionality.
ack
>
> >
> > Signed-off-by: Firas Ashkar <firas.ashkar@xxxxxxxxxxxxxxxxxxxx>
> > ---
> > :100644 100644 2f4fe3ca5c1a ed8378893208 M      arch/arm/mach-
> > orion5x/ts78xx-fpga.h
> > :100644 100644 af810e7ccd79 d319a68b7160 M      arch/arm/mach-
> > orion5x/ts78xx-setup.c
> > :100644 100644 7ef9f5e696d3 d184fb435c5d
> > M      drivers/irqchip/Kconfig
> > :100644 100644 87b49a10962c b022eece2042
> > M      drivers/irqchip/Makefile
> > :000000 100644 000000000000 178f43548b2a
> > A      drivers/irqchip/irq-ts7800.c
>
> How did you generated this patch?
$ git format-patch --stat -p --raw --signoff -1

>
> >  arch/arm/mach-orion5x/ts78xx-fpga.h  |   1 +
> >  arch/arm/mach-orion5x/ts78xx-setup.c |  55 +++++
> >  drivers/irqchip/Kconfig              |  12 +
> >  drivers/irqchip/Makefile             |   1 +
> >  drivers/irqchip/irq-ts7800.c         | 319
> > +++++++++++++++++++++++++++
> >  5 files changed, 388 insertions(+)
> >
> > diff --git a/arch/arm/mach-orion5x/ts78xx-fpga.h b/arch/arm/mach-
> > orion5x/ts78xx-fpga.h
> > index 2f4fe3ca5c1a..ed8378893208 100644
> > --- a/arch/arm/mach-orion5x/ts78xx-fpga.h
> > +++ b/arch/arm/mach-orion5x/ts78xx-fpga.h
> > @@ -32,6 +32,7 @@ struct fpga_devices {
> >         struct fpga_device      ts_rtc;
> >         struct fpga_device      ts_nand;
> >         struct fpga_device      ts_rng;
> > +       struct fpga_device      ts_irqc;
> >  };
> >  
> >  struct ts78xx_fpga_data {
> > diff --git a/arch/arm/mach-orion5x/ts78xx-setup.c b/arch/arm/mach-
> > orion5x/ts78xx-setup.c
> > index af810e7ccd79..d319a68b7160 100644
> > --- a/arch/arm/mach-orion5x/ts78xx-setup.c
> > +++ b/arch/arm/mach-orion5x/ts78xx-setup.c
> > @@ -322,6 +322,49 @@ static void ts78xx_ts_rng_unload(void)
> >         platform_device_del(&ts78xx_ts_rng_device);
> >  }
> >  
> > +/*****************************************************************
> > ************
> > + * fpga irq controller
> > +
> > *******************************************************************
> > *********/
> > +#define TS_IRQC_CTRL (TS78XX_FPGA_REGS_PHYS_BASE + 0x200)
> > +#define TS_BRIDGE_CTRL (ORION5X_REGS_PHYS_BASE + 0x20400)
> > +#define TS_IRQC_PARENT 0x2
> > +static struct resource ts_irqc_resources[] = {
> > +       DEFINE_RES_MEM_NAMED(TS_IRQC_CTRL, 0x100, "ts_irqc"),
> > +       DEFINE_RES_MEM_NAMED(TS_BRIDGE_CTRL, 0x10, "ts_bridge"),
> > +       DEFINE_RES_IRQ_NAMED(TS_IRQC_PARENT, "ts_irqc_parent"),
> > +};
> > +
> > +static struct platform_device ts_irqc_device = {
> > +       .name = "ts7800-irqc",
> > +       .id = -1,
> > +       .resource = ts_irqc_resources,
> > +       .num_resources = ARRAY_SIZE(ts_irqc_resources),
> > +};
> > +
> > +static int ts_irqc_load(void)
> > +{
> > +       int rc;
> > +
> > +       if (ts78xx_fpga.supports.ts_irqc.init == 0) {
> > +               rc = platform_device_register(&ts_irqc_device);
> > +               if (!rc)
> > +                       ts78xx_fpga.supports.ts_irqc.init = 1;
> > +       } else {
> > +               rc = platform_device_add(&ts_irqc_device);
> > +       }
> > +
> > +       if (rc)
> > +               pr_info("FPGA based IRQ controller could not be
> > registered: %d\n",
> > +                       rc);
> > +
> > +       return rc;
> > +}
> > +
> > +static void ts_irqc_unload(void)
> > +{
> > +       platform_device_del(&ts_irqc_device);
> > +}
> > +
> >  /*****************************************************************
> > ************
> >   * FPGA 'hotplug' support code
> >  
> > *******************************************************************
> > *********/
> > @@ -330,6 +373,7 @@ static void ts78xx_fpga_devices_zero_init(void)
> >         ts78xx_fpga.supports.ts_rtc.init = 0;
> >         ts78xx_fpga.supports.ts_nand.init = 0;
> >         ts78xx_fpga.supports.ts_rng.init = 0;
> > +       ts78xx_fpga.supports.ts_irqc.init = 0;
> >  }
> >  
> >  static void ts78xx_fpga_supports(void)
> > @@ -348,6 +392,7 @@ static void ts78xx_fpga_supports(void)
> >                 ts78xx_fpga.supports.ts_rtc.present = 1;
> >                 ts78xx_fpga.supports.ts_nand.present = 1;
> >                 ts78xx_fpga.supports.ts_rng.present = 1;
> > +               ts78xx_fpga.supports.ts_irqc.present = 1;
> >                 break;
> >         default:
> >                 /* enable devices if magic matches */
> > @@ -358,11 +403,13 @@ static void ts78xx_fpga_supports(void)
> >                         ts78xx_fpga.supports.ts_rtc.present = 1;
> >                         ts78xx_fpga.supports.ts_nand.present = 1;
> >                         ts78xx_fpga.supports.ts_rng.present = 1;
> > +                       ts78xx_fpga.supports.ts_irqc.present = 1;
> >                         break;
> >                 default:
> >                         ts78xx_fpga.supports.ts_rtc.present = 0;
> >                         ts78xx_fpga.supports.ts_nand.present = 0;
> >                         ts78xx_fpga.supports.ts_rng.present = 0;
> > +                       ts78xx_fpga.supports.ts_irqc.present = 0;
> >                 }
> >         }
> >  }
> > @@ -371,6 +418,12 @@ static int ts78xx_fpga_load_devices(void)
> >  {
> >         int tmp, ret = 0;
> >  
> > +       if (ts78xx_fpga.supports.ts_irqc.present == 1) {
> > +               tmp = ts_irqc_load();
> > +               if (tmp)
> > +                       ts78xx_fpga.supports.ts_irqc.present = 0;
> > +               ret |= tmp;
> > +       }
> >         if (ts78xx_fpga.supports.ts_rtc.present == 1) {
> >                 tmp = ts78xx_ts_rtc_load();
> >                 if (tmp)
> > @@ -402,6 +455,8 @@ static int ts78xx_fpga_unload_devices(void)
> >                 ts78xx_ts_nand_unload();
> >         if (ts78xx_fpga.supports.ts_rng.present == 1)
> >                 ts78xx_ts_rng_unload();
> > +       if (ts78xx_fpga.supports.ts_irqc.present == 1)
> > +               ts_irqc_unload();
> >  
> >         return 0;
> >  }
>
> I am absolutely *NOT* keen on adding more non-DT stuff in this day
> and
> age. The DT effort has been going on for over 10 years, and maybe it
> is time that this board makes the jump before we add anything new to
> it, specially given that there is a DT board for this platform.
ack, though adding OF logic should not be a problem
>
> Arnd, what's your call on this?
>
> > diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> > index 7ef9f5e696d3..d184fb435c5d 100644
> > --- a/drivers/irqchip/Kconfig
> > +++ b/drivers/irqchip/Kconfig
> > @@ -290,6 +290,18 @@ config TS4800_IRQ
> >         help
> >           Support for the TS-4800 FPGA IRQ controller
> >  
> > +config TS7800_IRQ
> > +       tristate "TS-7800 IRQ controller"
> > +       select IRQ_DOMAIN
> > +       depends on HAS_IOMEM
> > +       depends on MACH_TS78XX
> > +       help
> > +         Support for TS-7800 FPGA based IRQ controller.
> > +         This IRQ controller acts as a multiplexer chaining mapped
> > +         FPGA interrupts to a single system bridge interrupt.
> > +
> > +         If you have an FPGA IP corresponding to this driver, say
> > Y or M here.
> > +
>
> How can the user tell they have this IP or another one?
This FPGA IP, is present on EmbeddedTS TS7800 boards, as indicated in
https://docs.embeddedts.com/TS-7800#FPGA_IRQ_Controller

>  Can they
> freely load it? Can the make *changes* to it?
FPGA byte stream is available from EmbeddedTS, but not sure if end user
can/should change it

>
> >  config VERSATILE_FPGA_IRQ
> >         bool
> >         select IRQ_DOMAIN
> > diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> > index 87b49a10962c..b022eece2042 100644
> > --- a/drivers/irqchip/Makefile
> > +++ b/drivers/irqchip/Makefile
> > @@ -58,6 +58,7 @@ obj-$(CONFIG_ARCH_VT8500)             += irq-
> > vt8500.o
> >  obj-$(CONFIG_ST_IRQCHIP)               += irq-st.o
> >  obj-$(CONFIG_TB10X_IRQC)               += irq-tb10x.o
> >  obj-$(CONFIG_TS4800_IRQ)               += irq-ts4800.o
> > +obj-$(CONFIG_TS7800_IRQ)               += irq-ts7800.o
> >  obj-$(CONFIG_XTENSA)                   += irq-xtensa-pic.o
> >  obj-$(CONFIG_XTENSA_MX)                        += irq-xtensa-mx.o
> >  obj-$(CONFIG_XILINX_INTC)              += irq-xilinx-intc.o
> > diff --git a/drivers/irqchip/irq-ts7800.c b/drivers/irqchip/irq-
> > ts7800.c
> > new file mode 100644
> > index 000000000000..178f43548b2a
> > --- /dev/null
> > +++ b/drivers/irqchip/irq-ts7800.c
> > @@ -0,0 +1,319 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * FPGA based IRQ contorller driver for TS-7800v1
> > + *
> > + * Copyright (C) 2022 Savoir-faire Linux Inc.
> > + *
> > + */
> > +
> > +#include <linux/interrupt.h>
> > +#include <linux/io.h>
> > +#include <linux/irq.h>
> > +#include <linux/irqchip.h>
> > +#include <linux/irqchip/chained_irq.h>
> > +#include <linux/irqdomain.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/platform_device.h>
> > +
> > +#define DRIVER_NAME "ts7800-irqc"
> > +
> > +#define IRQ_MASK_REG 0x4
> > +#define IRQ_STATUS_REG 0x8
> > +#define TS7800_IRQ_NUM 0x20
> > +
> > +/*
> > + * FPGA IRQ Controller, list of all mappable/chainable interrupts
> > + *
> > + * IRQ  Description
> > + * ---  -----------
> > + * 0x0  dma_cpu_pci_dack_o
> > + * 0x1  dma_fpga_dack_o
> > + * 0x2  SD Busy#
> > + * 0x3  isa_irq3
> > + * 0x4  isa_irq4
> > + * 0x5  isa_irq5
> > + * 0x6  isa_irq6
> > + * 0x7  isa_irq7
> > + * 0x8  Reserved
> > + * 0x9  isa_irq9
> > + * 0xa  isa_irq10
> > + * 0xb  isa_irq11
> > + * 0xc  isa_irq12
> > + * 0xd  isa_irq14
> > + * 0xe  isa_irq15
> > + * 0x10:0x19  tsuart_irqs
> > + *
> > + * To map or enable a specific FPGA interrupt, add its
> > corresponding number to 'enabled_mappings'.
> > + * Example:
> > + *  1. For 'dma_cpu_pci_dack_o' (FPGA based DMA controller), add
> > 0x0 to 'enabled_mappings'
> > + *  2. For 'tsuart_irqs' (FPGA based UARTs), add their FPGA
> > interrupt range of 0x10-0x19
> > + *     to 'enabled_mappings'
> > + *
> > + * By default only mapped/enabled interrupts will be
> > chained/multiplexed,
> > + * these chained interrupts will then be available to other device
> > drivers to request via the
> > + * corresponding Linux IRQs.
> > + *
> > + * Each mapped FPGA interrupt will have a corresponding Linux IRQ,
> > this new IRQ will be appended
> > + * to the system's current list of Linux IRQs, on TS78xx, the
> > first mapped FPGA interrupt will have
> > + * a corresponding new Linux IRQ starting at 65. The next mapped
> > FPGA interrupt will have a Linux
> > + * IRQ of 66 and so forth.
> > + *
> > + * Example-1:
> > + *  In order for a device driver, say the FPGA based DMA
> > controller driver 'TS-DMAC',
> > + *  to use either of the corresponding FPGA interrupts
> > 'dma_cpu_pci_dack_o' and 'dma_fpga_dack_o',
> > + *  the 'TS-DMAC' driver has to:
> > + *  1. add FPGA interrupt numbers 0x0 and 0x1 to
> > 'enabled_mappings', in this file, and
> > + *  2. request the corresponding Linux IRQ numbers 65 and 66
> > respectively in its implementation.
> > + *
> > + * Eample-2:
> > + *  In order for another device driver, say the FPGA based
> > 'TSUART' driver, to use the related FPGA
> > + *  interrupts, the 'TSUART' driver has to:
> > + *  1. append FPGA interrupt numbers 0x10, 0x11, 0x12, 0x13, 0x14,
> > 0x15, 0x16, 0x17, 0x18, 0x19 to
> > + *     'enabled_mappings', in this file, and
> > + *  2. request the corresponding Linux IRQ numbers, these IRQs
> > could start from 65 till 74, if no
> > + *     previous FPGA interrupts where mapped. However, if other
> > FPGA interrupts, say those of
> > + *     'TS-DMAC' in Example-1 were also mapped/enabled, then the
> > correct corresponding Linux IRQs
> > + *     would start from 67 till 76.
> > + */
>
> See why this really should be a DT-based driver?
agreed, though when this board starts using Open Firmware's Device-Tree
scheme, will port this driver accordingly

>  And please keep
> comments to less than 80 chars per line.
ack
>
> > +
> > +static irq_hw_number_t enabled_mappings[] = { 0x0 };
> > +
> > +struct ts7800_irq_data {
> > +       int mpp7_virq;
> > +       void __iomem *base;
> > +       void __iomem *bridge;
> > +       struct irq_domain *domain;
> > +       struct irq_chip irq_chip;
> > +};
> > +
> > +static void ts7800_irq_mask(struct irq_data *d)
> > +{
> > +       struct ts7800_irq_data *data =
> > irq_data_get_irq_chip_data(d);
> > +       u32 fpga_mask_reg = readl(data->base + IRQ_MASK_REG);
> > +       u32 mask_bits = 1 << d->hwirq;
> > +
> > +       writel(fpga_mask_reg & ~mask_bits, data->base +
> > IRQ_MASK_REG);
> > +       writel(fpga_mask_reg & ~mask_bits, data->bridge +
> > IRQ_MASK_REG);
>
> I know your HW is UP, but seeing these RMW sequences without a lock
> makes me jump. You probably want to either forbid this driver on SMP
> systems, or add the required locks.
this is single CPU, nevertheless we'll add locking
>
> > +}
> > +
> > +static void ts7800_irq_unmask(struct irq_data *d)
> > +{
> > +       struct ts7800_irq_data *data =
> > irq_data_get_irq_chip_data(d);
> > +       u32 fpga_mask_reg = readl(data->base + IRQ_MASK_REG);
> > +       u32 mask_bits = 1 << d->hwirq;
> > +
> > +       writel(fpga_mask_reg | mask_bits, data->base +
> > IRQ_MASK_REG);
> > +       writel(fpga_mask_reg | mask_bits, data->bridge +
> > IRQ_MASK_REG);
> > +}
> > +
> > +static int ts7800_irqdomain_map(struct irq_domain *d, unsigned int
> > irq,
> > +                               irq_hw_number_t hwirq)
> > +{
> > +       struct ts7800_irq_data *data = d->host_data;
> > +
> > +       irq_set_chip_and_handler(irq, &data->irq_chip,
> > handle_simple_irq);
>
> Are you sure of this? Edge and level interrupts have very different
> semantics, and handle_simple_irq seems like a massive cop-out.
ok, we'll update to 'handle_edge_irq' and 'ts7800_irq_ack' logic
instead

>
> > +       irq_set_chip_data(irq, data);
> > +       irq_set_noprobe(irq);
> > +
> > +       return 0;
> > +}
> > +
> > +static const struct irq_domain_ops ts7800_ic_ops = {
> > +       .map = ts7800_irqdomain_map,
> > +       .xlate = irq_domain_xlate_onecell,
>
> I don't really get the usage model of this thing. Who provides the
> interrupt specifier?
ack, to be removed
>
> > +};
> > +
> > +static void ts7800_ic_chained_handle_irq(struct irq_desc *desc)
> > +{
> > +       struct ts7800_irq_data *data =
> > irq_desc_get_handler_data(desc);
> > +       struct irq_chip *chip = irq_desc_get_chip(desc);
> > +       u32 mask_bits = readl(data->base + IRQ_STATUS_REG);
> > +       u32 status = mask_bits;
> > +
> > +       chained_irq_enter(chip, desc);
> > +
> > +       if (unlikely(status == 0)) {
> > +               handle_bad_irq(desc);
> > +               goto out;
> > +       }
> > +
> > +       do {
> > +               unsigned int bit = __ffs(status);
> > +               int irq = irq_find_mapping(data->domain, bit);
> > +
> > +               status &= ~(1 << bit);
> > +
> > +               if (irq && (mask_bits & BIT(bit))) {
> > +                       u32 x32 = readl(data->bridge);
> > +
> > +                       generic_handle_irq(irq);
>
> Please use generic handle_dopmain_irq().
this is a chained/multiplexed logic, starts with 'chained_irq_enter'
calls 'generic_handle_irq' and terminates with 'chained_irq_exit',
we followed same logic in other similar drivers, 'qcom-irq-combiner.c',
'exynos-combiner.c', etc
>
> > +
> > +                       x32 &= ~(mask_bits & BIT(bit));
> > +                       writel(x32, data->bridge);
>
> What does this write do? If this is an Ack of the interrupt, you've
> just lost an edge.
to be removed, and use 'ts7800_irq_ack' logic instead

>
> > +               }
> > +       } while (status);
> > +
> > +out:
> > +       chained_irq_exit(chip, desc);
> > +}
> > +
> > +static int ts7800_ic_probe(struct platform_device *pdev)
> > +{
> > +       struct ts7800_irq_data *data = NULL;
> > +       struct irq_chip *irq_chip = NULL;
> > +       struct resource *mem_res = NULL, *brdg_res = NULL, *irq_res
> > = NULL;
> > +       unsigned int irqdomain;
> > +       int i, ret;
> > +
> > +       data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
> > +       if (IS_ERR_OR_NULL(data)) {
> > +               dev_err(&pdev->dev,
> > +                       "Failed to allocate TS7800 data, error
> > %ld\n",
> > +                       PTR_ERR(data));
> > +               ret = PTR_ERR(data);
> > +               goto devm_kzalloc_err;
> > +       }
> > +
> > +       mem_res = platform_get_resource_byname(pdev,
> > IORESOURCE_MEM, "ts_irqc");
> > +       if (IS_ERR_OR_NULL(mem_res)) {
> > +               dev_err(&pdev->dev,
> > +                       "Failed to get platform memory resource,
> > error %ld\n",
> > +                       PTR_ERR(mem_res));
> > +               ret = PTR_ERR(mem_res);
> > +               goto pltfrm_get_res_mem_err;
> > +       }
> > +
> > +       data->base = devm_ioremap_resource(&pdev->dev, mem_res);
> > +       if (IS_ERR_OR_NULL(data->base)) {
> > +               dev_err(&pdev->dev,
> > +                       "Failed to IO map mem-region %s, error
> > %ld\n",
> > +                       mem_res->name, PTR_ERR(data->base));
> > +               ret = PTR_ERR(data->base);
> > +               goto devm_ioremap_res_mem_err;
> > +       }
> > +
> > +       brdg_res =
> > +               platform_get_resource_byname(pdev, IORESOURCE_MEM,
> > "ts_bridge");
>
> Don't break assignments.
ack
>
> > +       if (IS_ERR_OR_NULL(brdg_res)) {
> > +               dev_err(&pdev->dev,
> > +                       "Failed to get platform bridge resource,
> > error %ld\n",
> > +                       PTR_ERR(brdg_res));
> > +               ret = PTR_ERR(brdg_res);
> > +               goto pltfrm_get_res_brdg_err;
> > +       }
> > +
> > +       data->bridge = devm_ioremap_resource(&pdev->dev, brdg_res);
> > +       if (IS_ERR_OR_NULL(data->bridge)) {
> > +               dev_err(&pdev->dev,
> > +                       "Failed to IO map bridge-region %s, error
> > %ld\n",
> > +                       mem_res->name, PTR_ERR(data->bridge));
> > +               ret = PTR_ERR(data->bridge);
> > +               goto devm_ioremap_res_brdge_err;
> > +       }
> > +
> > +       irq_res = platform_get_resource_byname(pdev,
> > IORESOURCE_IRQ,
> > +                                              "ts_irqc_parent");
> > +       if (IS_ERR_OR_NULL(irq_res)) {
> > +               dev_err(&pdev->dev,
> > +                       "Failed to get platform parent irq
> > resource, error %ld\n",
> > +                       PTR_ERR(irq_res));
> > +               ret = PTR_ERR(irq_res);
> > +               goto pltfrm_get_res_irq_err;
> > +       }
> > +
> > +       data->mpp7_virq = irq_res->start;
> > +       writel(0x0, data->base + IRQ_MASK_REG);
> > +       writel(0x0, data->bridge + IRQ_MASK_REG);
> > +       writel(0x0, data->bridge);
> > +
> > +       irq_chip = &data->irq_chip;
> > +       irq_chip->name = dev_name(&pdev->dev);
>
> No. We don't pretty-print these things. And really, the irq_chip
> structure should be as static as possible (if possible const).
ack
>
> > +       irq_chip->irq_mask = ts7800_irq_mask;
> > +       irq_chip->irq_unmask = ts7800_irq_unmask;
> > +
> > +       data->domain = irq_domain_add_linear(pdev->dev.of_node,
> > TS7800_IRQ_NUM,
> > +                                            &ts7800_ic_ops, data);
> > +       if (IS_ERR_OR_NULL(data->domain)) {
> > +               dev_err(&pdev->dev, "cannot add IRQ domain\n");
> > +               ret = PTR_ERR(data->domain);
> > +               goto irq_domain_add_linear_err;
> > +       }
> > +
> > +       for (i = 0; i < ARRAY_SIZE(enabled_mappings); ++i) {
> > +               irqdomain =
> > +                       irq_create_mapping(data->domain,
> > enabled_mappings[i]);
> > +       }
> > +
> > +       irq_set_chained_handler_and_data(data->mpp7_virq,
> > +                                       
> > ts7800_ic_chained_handle_irq, data);
> > +
> > +       irq_set_status_flags(data->mpp7_virq,
> > +                            IRQ_DISABLE_UNLAZY | IRQ_LEVEL |
> > IRQ_NOPROBE);
>
> Why the IRQ_DISABLE_UNLAZY flag?
ack, to be removed
>
> > +
> > +       platform_set_drvdata(pdev, data);
> > +
> > +       return 0;
> > +
> > +irq_domain_add_linear_err:
> > +pltfrm_get_res_irq_err:
> > +       devm_iounmap(&pdev->dev, data->bridge);
> > +
> > +devm_ioremap_res_brdge_err:
> > +pltfrm_get_res_brdg_err:
> > +       devm_iounmap(&pdev->dev, data->base);
> > +
> > +devm_ioremap_res_mem_err:
> > +pltfrm_get_res_mem_err:
> > +       devm_kfree(&pdev->dev, data);
> > +
> > +devm_kzalloc_err:
> > +       return ret;
> > +}
> > +
> > +static int ts7800_ic_remove(struct platform_device *pdev)
> > +{
> > +       struct ts7800_irq_data *data = platform_get_drvdata(pdev);
> > +       int i;
> > +
> > +       if (!IS_ERR_OR_NULL(data)) {
> > +               irq_set_chained_handler_and_data(data->mpp7_virq,
> > NULL, NULL);
> > +
> > +               for (i = 0; i < ARRAY_SIZE(enabled_mappings); ++i)
> > +                       irq_dispose_mapping(irq_find_mapping(
> > +                               data->domain,
> > enabled_mappings[i]));
> > +
> > +               irq_domain_remove(data->domain);
> > +       }
> > +
> > +       return 0;
> > +}
>
> We don't remove interrupt controllers. What happens if someone still
> had a mapping?
becomes permanent

# lsmod
Module Size Used by Not tainted
ts7800v1_sdmmc 45056 0
ts_dmac 24576 1
virt_dma 16384 1 ts_dmac
irq_ts7800 16384 0 [permanent]

# rmmod irq_ts7800
rmmod: can't unload module 'irq_ts7800': Device or resource busy
#
>
> > +
> > +static const struct platform_device_id ts7800v1_ic_ids[] = {
> > +       {
> > +               .name = DRIVER_NAME,
> > +       },
> > +       {
> > +               /* sentinel */
> > +       }
> > +};
> > +
> > +MODULE_DEVICE_TABLE(platform, ts7800v1_ic_ids);
> > +
> > +static struct platform_driver ts7800_ic_driver = {
> > +       .probe  = ts7800_ic_probe,
> > +       .remove = ts7800_ic_remove,
> > +       .id_table       = ts7800v1_ic_ids,
> > +       .driver = {
> > +               .name = DRIVER_NAME,
> > +               .probe_type = PROBE_PREFER_ASYNCHRONOUS,
> > +       },
> > +};
> > +module_platform_driver(ts7800_ic_driver);
>
> Please use the existing helpers.
ack
>
> > +
> > +MODULE_ALIAS("platform:ts7800-irqc");
> > +MODULE_DESCRIPTION("TS-7800v1 FPGA based IRQ controller Driver");
> > +MODULE_AUTHOR("Firas Ashkar <firas.ashkar@xxxxxxxxxxxxxxxxxxxx>");
> > +MODULE_LICENSE("GPL");
>
>         M.
>