diff mbox series

[RESEND,RFC,05/12] drivers/irqchip: Add SH7751 and boards specific irqchip.

Message ID 52f2118d33bea5e74d4ffda4b3d935772e743ec2.1693444193.git.ysato@users.sourceforge.jp (mailing list archive)
State New, archived
Headers show
Series DeviceTree support for SH7751 based boards. | expand

Commit Message

Yoshinori Sato Aug. 31, 2023, 1:11 a.m. UTC
- irq-renesas-sh7751: Renesas SH7751 internal INTC.
- irq-renesas-r2d: Renesas RTS7751R2D external interrupt encoder.
- irq-iodata-julian: IO DATA Device LANDISK external interrupt encoder.

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 drivers/irqchip/Kconfig              |   4 +
 drivers/irqchip/Makefile             |   1 +
 drivers/irqchip/irq-iodata-julian.c  | 163 +++++++++++++++++++++++
 drivers/irqchip/irq-renesas-r2d.c    | 175 +++++++++++++++++++++++++
 drivers/irqchip/irq-renesas-sh7751.c | 186 +++++++++++++++++++++++++++
 5 files changed, 529 insertions(+)
 create mode 100644 drivers/irqchip/irq-iodata-julian.c
 create mode 100644 drivers/irqchip/irq-renesas-r2d.c
 create mode 100644 drivers/irqchip/irq-renesas-sh7751.c

Comments

Geert Uytterhoeven Sept. 1, 2023, 12:49 p.m. UTC | #1
Hi Sato-san,

Thanks for your patch!

On Thu, Aug 31, 2023 at 4:37 PM Yoshinori Sato
<ysato@users.sourceforge.jp> wrote:
> - irq-renesas-sh7751: Renesas SH7751 internal INTC.
> - irq-renesas-r2d: Renesas RTS7751R2D external interrupt encoder.
> - irq-iodata-julian: IO DATA Device LANDISK external interrupt encoder.

Again, please split.

> Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>

> --- a/drivers/irqchip/Kconfig
> +++ b/drivers/irqchip/Kconfig
> @@ -660,6 +660,10 @@ config APPLE_AIC
>         help
>           Support for the Apple Interrupt Controller found on Apple Silicon SoCs,
>           such as the M1.

Please add a blank line here.

> +config RENESAS_SH_INTC
> +       def_bool y if SH_DEVICE_TREE
> +       select IRQ_DOMAIN
> +       select IRQ_DOMAIN_HIERARCHY
>
>  config MCHP_EIC
>         bool "Microchip External Interrupt Controller"
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index ffd945fe71aa..a3859bdd0442 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -120,3 +120,4 @@ 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_SUNPLUS_SP7021_INTC)      += irq-sp7021-intc.o
> +obj-$(CONFIG_RENESAS_SH_INTC)          += irq-renesas-sh7751.o irq-iodata-julian.o irq-renesas-r2d.o

I think it makes sense to split this in 3 different config symbols.

> diff --git a/drivers/irqchip/irq-iodata-julian.c b/drivers/irqchip/irq-iodata-julian.c
> new file mode 100644
> index 000000000000..95e502904a68
> --- /dev/null
> +++ b/drivers/irqchip/irq-iodata-julian.c
> @@ -0,0 +1,163 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * IO-DATA DEVICE LANDISK / USL-5P (a.k.a Julian) interrupt encoder
> + *
> + * Copyright (C) 2023 Yoshinori Sato
> + */
> +
> +#include <linux/init.h>
> +#include <linux/irq.h>
> +#include <linux/irqchip.h>
> +#include <linux/irqdomain.h>
> +#include <linux/of_address.h>

Do you need this?

> +#include <linux/of_irq.h>

Do you need this?

> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#define NUM_IRQ 16
> +
> +struct julian_intc_priv {
> +       struct device *dev;
> +       void __iomem *base;
> +       struct irq_domain *irq_domain;
> +};
> +
> +static struct julian_intc_priv *irq_data_to_priv(struct irq_data *data)

static inline

> +{
> +       return data->domain->host_data;
> +}

> +static __init int julian_intc_map(struct irq_domain *h, unsigned int virq,

I don't think this can be __init...

> +                              irq_hw_number_t hw_irq_num)
> +{
> +       irq_set_chip_and_handler(virq, &julian_intc_chip, handle_level_irq);
> +       irq_get_irq_data(virq)->chip_data = h->host_data;
> +       irq_modify_status(virq, IRQ_NOREQUEST, IRQ_NOPROBE);
> +       return 0;
> +}

> +static int julian_intc_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct device_node *np = dev->of_node;
> +       struct julian_intc_priv *priv;
> +       struct irq_domain *d;
> +       int ret = 0;
> +
> +       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +       if (!priv)
> +               return -ENOMEM;
> +
> +       priv->dev = dev;
> +
> +       priv->base = devm_platform_ioremap_resource(pdev, 0);
> +       if (IS_ERR(priv->base))
> +               return PTR_ERR(priv->base);
> +
> +       d = irq_domain_add_tree(np, &julian_intc_domain_ops, priv);
> +       if (d == NULL) {
> +               dev_err(dev, "cannot initialize irq domain\n");
> +               kfree(priv);
> +               ret = -ENOMEM;

"return -ENOMEM;", so you can bail out early, remove the else below,
and reduce indentation below.

> +       } else {
> +               priv->irq_domain = d;
> +               irq_domain_update_bus_token(d, DOMAIN_BUS_WIRED);
> +       }
> +       return ret;
> +}

> +static int julian_intc_remove(struct platform_device *pdev)
> +{
> +       struct julian_intc_priv *priv = platform_get_drvdata(pdev);

This won't work, as platform_set_drvdata() is never called.

> +
> +       irq_domain_remove(priv->irq_domain);
> +       return 0;
> +}
> +
> +static const struct of_device_id julian_intc_dt_ids[] = {
> +       { .compatible = "iodata,julian-intc" },
> +       {},

Please no comma after a sentinel.

> +};
> +MODULE_DEVICE_TABLE(of, julian_intc_dt_ids);

> --- /dev/null
> +++ b/drivers/irqchip/irq-renesas-r2d.c
> @@ -0,0 +1,175 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Renesas RTS7751R2D interrupt encoder
> + *
> + * Copyright (C) 2023 Yoshinori Sato
> + */
> +
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/irqdomain.h>
> +#include <linux/irq.h>
> +#include <linux/module.h>
> +#include <linux/of_irq.h>

Do you need this?

> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#define NUM_IRQ 15
> +
> +struct r2d_intc_priv {
> +       struct device *dev;
> +       void __iomem *base;
> +       struct irq_domain *irq_domain;
> +};
> +
> +static const int imask[] = {

unsigned int

> +       1 << 11,        /* PCI INTD */
> +       1 << 9,         /* CF IDE */
> +       1 << 8,         /* CF CD */
> +       1 << 12,        /* PCI INTC */
> +       1 << 10,        /* SM501 */
> +       1 << 6,         /* AX88796 / KEY */
> +       1 << 5,         /* RTC ALARM */
> +       1 << 4,         /* RTC T */
> +       1 << 7,         /* SDCARD */
> +       1 << 14,        /* PCI INTA */
> +       1 << 13,        /* PCI INTB */
> +       1 << 0,         /* EXT */
> +       1 << 15,        /* TP */

BIT(...)

> +};
> +
> +static struct r2d_intc_priv *irq_data_to_priv(struct irq_data *data)

static inline

> +{
> +       return data->domain->host_data;
> +}

> +static __init int r2d_intc_map(struct irq_domain *h, unsigned int virq,
> +                              irq_hw_number_t hw_irq_num)

I don't think this can be __init.

> +{
> +       irq_set_chip_and_handler(virq, &r2d_intc_chip, handle_level_irq);
> +       irq_get_irq_data(virq)->chip_data = h->host_data;
> +       irq_modify_status(virq, IRQ_NOREQUEST, IRQ_NOPROBE);
> +       return 0;
> +}

> +static const struct irq_domain_ops r2d_intc_domain_ops = {
> +//     .alloc = r2d_intc_alloc,

Remove this line?

> +       .map = r2d_intc_map,
> +       .translate = r2d_intc_translate,
> +};
> +
> +static int r2d_intc_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct device_node *np = dev->of_node;
> +       struct r2d_intc_priv *priv;
> +       struct irq_domain *d;
> +       int ret = 0;
> +
> +       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +       if (!priv)
> +               return -ENOMEM;
> +
> +       priv->dev = dev;
> +
> +       priv->base = devm_platform_ioremap_resource(pdev, 0);
> +       if (IS_ERR(priv->base))
> +               return PTR_ERR(priv->base);
> +
> +       d = irq_domain_add_tree(np, &r2d_intc_domain_ops, priv);
> +       if (d == NULL) {
> +               dev_err(dev, "cannot initialize irq domain\n");
> +               kfree(priv);
> +               ret = -ENOMEM;

"return -ENOMEM;", so you can bail out early, remove the else below,
and reduce indentation below.

> +       } else {
> +               priv->irq_domain = d;
> +               irq_domain_update_bus_token(d, DOMAIN_BUS_WIRED);
> +       }
> +       return ret;
> +}
> +
> +static int r2d_intc_remove(struct platform_device *pdev)
> +{
> +       struct r2d_intc_priv *priv = platform_get_drvdata(pdev);

This won't work, as platform_set_drvdata() is never called.

> +
> +       irq_domain_remove(priv->irq_domain);
> +       return 0;
> +}
> +
> +static const struct of_device_id r2d_intc_dt_ids[] = {
> +       { .compatible = "renesas,rts7751r2d-intc" },
> +       {},

Please no comma after a sentinel.

> +};
> +MODULE_DEVICE_TABLE(of, r2d_intc_dt_ids);

> --- /dev/null
> +++ b/drivers/irqchip/irq-renesas-sh7751.c
> @@ -0,0 +1,186 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Renesas SH7751 interrupt controller driver
> + *
> + * Copyright 2023 Yoshinori Sato <ysato@users.sourceforge.jp>
> + */
> +
> +#include <linux/irq.h>
> +#include <linux/irqchip.h>
> +#include <linux/of_address.h>

Do you need this?

> +#include <linux/of_irq.h>

Do you need this?

> +static void update_ipr(struct sh7751_intc_regs *reg, int irq, int on)
> +{
> +       unsigned int addr;
> +       int pos;

unsigned int

> +       uint16_t pri;
> +
> +       if (irq < 64) {
> +               if (ipr_table[irq] != 0xff) {
> +                       addr = (ipr_table[irq] & 0xf0) >> 2;
> +                       pos = (ipr_table[irq] & 0x0f) * 4;
> +                       pri = ~(0x000f << pos);
> +                       pri &= __raw_readw(reg->ipr + addr);
> +                       if (on)
> +                               pri |= 1 << pos;
> +                       __raw_writew(pri, reg->ipr + addr);
> +               }
> +       } else {
> +               if (pri_table[irq - 64] < 32) {

else if

> +                       pos = pri_table[irq - 64];
> +                       pri = ~(0x000f << pos);
> +                       pri &= __raw_readw(reg->intpri00);
> +                       if (on)
> +                               pri |= 1 << pos;
> +                       __raw_writew(pri, reg->intpri00);
> +               }
> +       }
> +}
> +
> +static void sh7751_disable_irq(struct irq_data *data)
> +{
> +       unsigned int irq = data->irq;
> +       struct sh7751_intc_regs *reg = data->chip_data;
> +       uint16_t icr = __raw_readw(reg->icr);
> +
> +       if (irq < 16 && (icr & ICR_IRLM) == 0) {
> +               clear_bit(irq, imask_mask);
> +               if (interrupt_priority < IMASK_PRIORITY - irq)
> +                       interrupt_priority = IMASK_PRIORITY - irq;
> +               set_interrupt_registers(interrupt_priority);
> +       } else
> +               update_ipr(reg, irq, 0);

Missing curly braces for else case.

> +}
> +
> +static void sh7751_enable_irq(struct irq_data *data)
> +{
> +       unsigned int irq = data->irq;
> +       struct sh7751_intc_regs *reg = data->chip_data;
> +       uint16_t icr = __raw_readw(reg->icr);
> +
> +       if (irq < 16 && (icr & ICR_IRLM) == 0) {
> +               set_bit(irq, imask_mask);
> +               interrupt_priority = IMASK_PRIORITY -
> +                 find_first_bit(imask_mask, IMASK_PRIORITY);

wrong indentation

> +               set_interrupt_registers(interrupt_priority);
> +       } else
> +               update_ipr(reg, irq, 1);

Missing curly braces for else case.

> +}

> +static int __init sh7751_intc_of_init(struct device_node *intc,
> +                                     struct device_node *parent)
> +{
> +       unsigned short icr;

u16

> +       struct irq_domain *domain;
> +       void *intc_baseaddr;
> +       void *intc_baseaddr2;
> +
> +       intc_baseaddr = of_iomap(intc, 0);
> +       intc_baseaddr2 = of_iomap(intc, 1);
> +       if (!intc_baseaddr) {
> +               pr_err("Invalid INTC address\n");
> +               return -EINVAL;
> +       }
> +
> +       sh7751_regs.icr = intc_baseaddr;
> +       sh7751_regs.ipr = intc_baseaddr + 4;
> +       sh7751_regs.intpri00 = intc_baseaddr2;
> +       sh7751_regs.intreq00 = intc_baseaddr2 + 0x20;
> +       sh7751_regs.intmsk00 = intc_baseaddr2 + 0x40;
> +       sh7751_regs.intmskclr00 = intc_baseaddr2 + 0x60;
> +
> +       if (of_property_read_bool(intc, "sh7751,irlm")) {
> +               icr = __raw_readw(sh7751_regs.icr);
> +               icr |= 0x80;    // IRLM = 1

#define ICR_IRLM BIT(7)

> +               __raw_writew(icr, sh7751_regs.icr);
> +       }
> +
> +       domain = irq_domain_add_linear(intc, NR_IRQS, &irq_ops, &sh7751_regs);
> +       if (!domain) {
> +               pr_err("Unable to allocate domain handle\n");
> +               return -ENOMEM;
> +       }
> +       irq_set_default_host(domain);
> +       return 0;
> +}
> +
> +IRQCHIP_DECLARE(sh_7751_intc,
> +               "renesas,sh7751-intc", sh7751_intc_of_init);

Gr{oetje,eeting}s,

                        Geert
diff mbox series

Patch

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 09e422da482f..372b22d5183f 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -660,6 +660,10 @@  config APPLE_AIC
 	help
 	  Support for the Apple Interrupt Controller found on Apple Silicon SoCs,
 	  such as the M1.
+config RENESAS_SH_INTC
+	def_bool y if SH_DEVICE_TREE
+	select IRQ_DOMAIN
+	select IRQ_DOMAIN_HIERARCHY
 
 config MCHP_EIC
 	bool "Microchip External Interrupt Controller"
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index ffd945fe71aa..a3859bdd0442 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -120,3 +120,4 @@  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_SUNPLUS_SP7021_INTC)	+= irq-sp7021-intc.o
+obj-$(CONFIG_RENESAS_SH_INTC)		+= irq-renesas-sh7751.o irq-iodata-julian.o irq-renesas-r2d.o
diff --git a/drivers/irqchip/irq-iodata-julian.c b/drivers/irqchip/irq-iodata-julian.c
new file mode 100644
index 000000000000..95e502904a68
--- /dev/null
+++ b/drivers/irqchip/irq-iodata-julian.c
@@ -0,0 +1,163 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * IO-DATA DEVICE LANDISK / USL-5P (a.k.a Julian) interrupt encoder
+ *
+ * Copyright (C) 2023 Yoshinori Sato
+ */
+
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define NUM_IRQ 16
+
+struct julian_intc_priv {
+	struct device *dev;
+	void __iomem *base;
+	struct irq_domain *irq_domain;
+};
+
+static struct julian_intc_priv *irq_data_to_priv(struct irq_data *data)
+{
+	return data->domain->host_data;
+}
+
+static void julian_intc_mask_irq(struct irq_data *data)
+{
+	struct julian_intc_priv *priv = irq_data_to_priv(data);
+	int irq = data->irq;
+	u8 mask;
+
+	mask = __raw_readb(priv->base + 5);
+	mask &= ~(1 << (irq - 5));
+	__raw_writeb(mask, priv->base + 5);
+}
+
+static void julian_intc_unmask_irq(struct irq_data *data)
+{
+	struct julian_intc_priv *priv = irq_data_to_priv(data);
+	int irq = data->irq;
+	u8 mask;
+
+	mask = __raw_readb(priv->base + 5);
+	mask |= (1 << (irq - 5));
+	__raw_writeb(mask, priv->base + 5);
+}
+
+static struct irq_chip julian_intc_chip = {
+	.name		= "JULIAN-INTC",
+	.irq_unmask	= julian_intc_unmask_irq,
+	.irq_mask	= julian_intc_mask_irq,
+};
+
+static __init int julian_intc_map(struct irq_domain *h, unsigned int virq,
+			       irq_hw_number_t hw_irq_num)
+{
+	irq_set_chip_and_handler(virq, &julian_intc_chip, handle_level_irq);
+	irq_get_irq_data(virq)->chip_data = h->host_data;
+	irq_modify_status(virq, IRQ_NOREQUEST, IRQ_NOPROBE);
+	return 0;
+}
+
+static int julian_intc_translate(struct irq_domain *domain,
+			       struct irq_fwspec *fwspec, unsigned long *hwirq,
+			       unsigned int *type)
+{
+	if (fwspec->param[0] >= NUM_IRQ)
+		return -EINVAL;
+
+	switch (fwspec->param_count) {
+	case 2:
+		*type = fwspec->param[1];
+		fallthrough;
+	case 1:
+		*hwirq = fwspec->param[0];
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static const struct irq_domain_ops julian_intc_domain_ops = {
+	.map = julian_intc_map,
+	.translate = julian_intc_translate,
+};
+
+static int julian_intc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct julian_intc_priv *priv;
+	struct irq_domain *d;
+	int ret = 0;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dev = dev;
+
+	priv->base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(priv->base))
+		return PTR_ERR(priv->base);
+
+	d = irq_domain_add_tree(np, &julian_intc_domain_ops, priv);
+	if (d == NULL) {
+		dev_err(dev, "cannot initialize irq domain\n");
+		kfree(priv);
+		ret = -ENOMEM;
+	} else {
+		priv->irq_domain = d;
+		irq_domain_update_bus_token(d, DOMAIN_BUS_WIRED);
+	}
+	return ret;
+}
+
+static int julian_intc_remove(struct platform_device *pdev)
+{
+	struct julian_intc_priv *priv = platform_get_drvdata(pdev);
+
+	irq_domain_remove(priv->irq_domain);
+	return 0;
+}
+
+static const struct of_device_id julian_intc_dt_ids[] = {
+	{ .compatible = "iodata,julian-intc" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, julian_intc_dt_ids);
+
+static struct platform_driver julian_intc_device_driver = {
+	.probe		= julian_intc_probe,
+	.remove		= julian_intc_remove,
+	.driver		= {
+		.name	= "iodata_julian_intc",
+		.of_match_table	= julian_intc_dt_ids,
+	}
+};
+
+static int __init julian_intc_init(void)
+{
+	return platform_driver_register(&julian_intc_device_driver);
+}
+postcore_initcall(julian_intc_init);
+
+static void __exit julian_intc_exit(void)
+{
+	platform_driver_unregister(&julian_intc_device_driver);
+}
+module_exit(julian_intc_exit);
+
+MODULE_AUTHOR("Yoshinori Sato");
+MODULE_DESCRIPTION("IO-DATA JULIAN external interrupt encoder");
diff --git a/drivers/irqchip/irq-renesas-r2d.c b/drivers/irqchip/irq-renesas-r2d.c
new file mode 100644
index 000000000000..595057c1f5aa
--- /dev/null
+++ b/drivers/irqchip/irq-renesas-r2d.c
@@ -0,0 +1,175 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas RTS7751R2D interrupt encoder
+ *
+ * Copyright (C) 2023 Yoshinori Sato
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irqdomain.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define NUM_IRQ 15
+
+struct r2d_intc_priv {
+	struct device *dev;
+	void __iomem *base;
+	struct irq_domain *irq_domain;
+};
+
+static const int imask[] = {
+	1 << 11,	/* PCI INTD */
+	1 << 9,		/* CF IDE */
+	1 << 8,		/* CF CD */
+	1 << 12,	/* PCI INTC */
+	1 << 10,	/* SM501 */
+	1 << 6,		/* AX88796 / KEY */
+	1 << 5,		/* RTC ALARM */
+	1 << 4,		/* RTC T */
+	1 << 7,		/* SDCARD */
+	1 << 14,	/* PCI INTA */
+	1 << 13,	/* PCI INTB */
+	1 << 0,		/* EXT */
+	1 << 15,	/* TP */
+};
+
+static struct r2d_intc_priv *irq_data_to_priv(struct irq_data *data)
+{
+	return data->domain->host_data;
+}
+
+static void r2d_intc_mask_irq(struct irq_data *data)
+{
+	struct r2d_intc_priv *priv = irq_data_to_priv(data);
+	u16 mask;
+
+	mask = __raw_readw(priv->base);
+	mask &= ~(imask[data->irq]);
+	__raw_writew(mask, priv->base);
+}
+
+static void r2d_intc_unmask_irq(struct irq_data *data)
+{
+	struct r2d_intc_priv *priv = irq_data_to_priv(data);
+	u16 mask;
+
+	mask = __raw_readw(priv->base);
+	mask |= imask[data->irq];
+	__raw_writew(mask, priv->base);
+}
+
+static struct irq_chip r2d_intc_chip = {
+	.name		= "R2D-INTC",
+	.irq_unmask	= r2d_intc_unmask_irq,
+	.irq_mask	= r2d_intc_mask_irq,
+};
+
+static __init int r2d_intc_map(struct irq_domain *h, unsigned int virq,
+			       irq_hw_number_t hw_irq_num)
+{
+	irq_set_chip_and_handler(virq, &r2d_intc_chip, handle_level_irq);
+	irq_get_irq_data(virq)->chip_data = h->host_data;
+	irq_modify_status(virq, IRQ_NOREQUEST, IRQ_NOPROBE);
+	return 0;
+}
+
+static int r2d_intc_translate(struct irq_domain *domain,
+			       struct irq_fwspec *fwspec, unsigned long *hwirq,
+			       unsigned int *type)
+{
+	if (fwspec->param[0] >= NUM_IRQ)
+		return -EINVAL;
+
+	switch (fwspec->param_count) {
+	case 2:
+		*type = fwspec->param[1];
+		fallthrough;
+	case 1:
+		*hwirq = fwspec->param[0];
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static const struct irq_domain_ops r2d_intc_domain_ops = {
+//	.alloc = r2d_intc_alloc,
+	.map = r2d_intc_map,
+	.translate = r2d_intc_translate,
+};
+
+static int r2d_intc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct r2d_intc_priv *priv;
+	struct irq_domain *d;
+	int ret = 0;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dev = dev;
+
+	priv->base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(priv->base))
+		return PTR_ERR(priv->base);
+
+	d = irq_domain_add_tree(np, &r2d_intc_domain_ops, priv);
+	if (d == NULL) {
+		dev_err(dev, "cannot initialize irq domain\n");
+		kfree(priv);
+		ret = -ENOMEM;
+	} else {
+		priv->irq_domain = d;
+		irq_domain_update_bus_token(d, DOMAIN_BUS_WIRED);
+	}
+	return ret;
+}
+
+static int r2d_intc_remove(struct platform_device *pdev)
+{
+	struct r2d_intc_priv *priv = platform_get_drvdata(pdev);
+
+	irq_domain_remove(priv->irq_domain);
+	return 0;
+}
+
+static const struct of_device_id r2d_intc_dt_ids[] = {
+	{ .compatible = "renesas,rts7751r2d-intc" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, r2d_intc_dt_ids);
+
+static struct platform_driver r2d_intc_device_driver = {
+	.probe		= r2d_intc_probe,
+	.remove		= r2d_intc_remove,
+	.driver		= {
+		.name	= "renesas_r2d_intc",
+		.of_match_table	= r2d_intc_dt_ids,
+	}
+};
+
+static int __init r2d_intc_init(void)
+{
+	return platform_driver_register(&r2d_intc_device_driver);
+}
+postcore_initcall(r2d_intc_init);
+
+static void __exit r2d_intc_exit(void)
+{
+	platform_driver_unregister(&r2d_intc_device_driver);
+}
+module_exit(r2d_intc_exit);
+
+MODULE_AUTHOR("Yoshinori Sato");
+MODULE_DESCRIPTION("Renesas RTS7751R2D external interrupt encoder");
diff --git a/drivers/irqchip/irq-renesas-sh7751.c b/drivers/irqchip/irq-renesas-sh7751.c
new file mode 100644
index 000000000000..1c4b2be44c27
--- /dev/null
+++ b/drivers/irqchip/irq-renesas-sh7751.c
@@ -0,0 +1,186 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas SH7751 interrupt controller driver
+ *
+ * Copyright 2023 Yoshinori Sato <ysato@users.sourceforge.jp>
+ */
+
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of.h>
+#include <linux/io.h>
+
+static struct sh7751_intc_regs {
+	void *icr;
+	void *ipr;
+	void *intpri00;
+	void *intreq00;
+	void *intmsk00;
+	void *intmskclr00;
+} sh7751_regs;
+
+#define ICR_IRLM (1 << 7)
+
+static const unsigned int ipr_table[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0 - 7 */
+	0x41, 0xff, 0xff, 0x40, 0xff, 0xff, 0xff, 0xff, /* 8 - 15 */
+	0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x11, /* 16 - 23 */
+	0x11, 0x11, 0x11, 0x13, 0x12, 0x12, 0xff, 0xff, /* 24 - 31 */
+	0x30, 0x33, 0x32, 0x32, 0x32, 0x32, 0x32, 0x21, /* 32 - 39 */
+	0x21, 0x21, 0x21, 0x21, 0x32, 0x32, 0x32, 0x32, /* 40 - 47 */
+	0xff, 0xff, 0xff, 0x40, 0xff, 0xff, 0xff, 0xff, /* 48 - 55 */
+	0xff, 0xff, 0xff, 0x40, 0xff, 0xff, 0xff, 0xff, /* 56 - 63 */
+};
+
+static const unsigned int pri_table[] = {
+	0, 4, 4, 4, 4, 4, 4, 4,
+	8, 32, 32, 32, 12, 32, 32, 32,
+};
+
+/* Bitmap of IRQ masked */
+#define IMASK_PRIORITY	15
+
+static DECLARE_BITMAP(imask_mask, IMASK_PRIORITY);
+static int interrupt_priority;
+
+static inline void set_interrupt_registers(int ip)
+{
+	unsigned long __dummy;
+
+	asm volatile(
+#ifdef CONFIG_CPU_HAS_SR_RB
+		     "ldc	%2, r6_bank\n\t"
+#endif
+		     "stc	sr, %0\n\t"
+		     "and	#0xf0, %0\n\t"
+		     "shlr2	%0\n\t"
+		     "cmp/eq	#0x3c, %0\n\t"
+		     "bt/s	1f	! CLI-ed\n\t"
+		     " stc	sr, %0\n\t"
+		     "and	%1, %0\n\t"
+		     "or	%2, %0\n\t"
+		     "ldc	%0, sr\n"
+		     "1:"
+		     : "=&z" (__dummy)
+		     : "r" (~0xf0), "r" (ip << 4)
+		     : "t");
+}
+
+static void update_ipr(struct sh7751_intc_regs *reg, int irq, int on)
+{
+	unsigned int addr;
+	int pos;
+	uint16_t pri;
+
+	if (irq < 64) {
+		if (ipr_table[irq] != 0xff) {
+			addr = (ipr_table[irq] & 0xf0) >> 2;
+			pos = (ipr_table[irq] & 0x0f) * 4;
+			pri = ~(0x000f << pos);
+			pri &= __raw_readw(reg->ipr + addr);
+			if (on)
+				pri |= 1 << pos;
+			__raw_writew(pri, reg->ipr + addr);
+		}
+	} else {
+		if (pri_table[irq - 64] < 32) {
+			pos = pri_table[irq - 64];
+			pri = ~(0x000f << pos);
+			pri &= __raw_readw(reg->intpri00);
+			if (on)
+				pri |= 1 << pos;
+			__raw_writew(pri, reg->intpri00);
+		}
+	}
+}
+
+static void sh7751_disable_irq(struct irq_data *data)
+{
+	unsigned int irq = data->irq;
+	struct sh7751_intc_regs *reg = data->chip_data;
+	uint16_t icr = __raw_readw(reg->icr);
+
+	if (irq < 16 && (icr & ICR_IRLM) == 0) {
+		clear_bit(irq, imask_mask);
+		if (interrupt_priority < IMASK_PRIORITY - irq)
+			interrupt_priority = IMASK_PRIORITY - irq;
+		set_interrupt_registers(interrupt_priority);
+	} else
+		update_ipr(reg, irq, 0);
+}
+
+static void sh7751_enable_irq(struct irq_data *data)
+{
+	unsigned int irq = data->irq;
+	struct sh7751_intc_regs *reg = data->chip_data;
+	uint16_t icr = __raw_readw(reg->icr);
+
+	if (irq < 16 && (icr & ICR_IRLM) == 0) {
+		set_bit(irq, imask_mask);
+		interrupt_priority = IMASK_PRIORITY -
+		  find_first_bit(imask_mask, IMASK_PRIORITY);
+		set_interrupt_registers(interrupt_priority);
+	} else
+		update_ipr(reg, irq, 1);
+}
+
+struct irq_chip sh7751_irq_chip = {
+	.name		= "SH7751-INTC",
+	.irq_unmask	= sh7751_enable_irq,
+	.irq_mask	= sh7751_disable_irq,
+};
+
+static int irq_sh7751_map(struct irq_domain *h, unsigned int virq,
+				  irq_hw_number_t hw_irq_num)
+{
+	irq_set_chip_and_handler(virq, &sh7751_irq_chip, handle_level_irq);
+	irq_get_irq_data(virq)->chip_data = h->host_data;
+	irq_modify_status(virq, IRQ_NOREQUEST, IRQ_NOPROBE);
+	return 0;
+}
+static const struct irq_domain_ops irq_ops = {
+	.map = irq_sh7751_map,
+	.xlate  = irq_domain_xlate_onecell,
+};
+
+static int __init sh7751_intc_of_init(struct device_node *intc,
+				      struct device_node *parent)
+{
+	unsigned short icr;
+	struct irq_domain *domain;
+	void *intc_baseaddr;
+	void *intc_baseaddr2;
+
+	intc_baseaddr = of_iomap(intc, 0);
+	intc_baseaddr2 = of_iomap(intc, 1);
+	if (!intc_baseaddr) {
+		pr_err("Invalid INTC address\n");
+		return -EINVAL;
+	}
+
+	sh7751_regs.icr = intc_baseaddr;
+	sh7751_regs.ipr = intc_baseaddr + 4;
+	sh7751_regs.intpri00 = intc_baseaddr2;
+	sh7751_regs.intreq00 = intc_baseaddr2 + 0x20;
+	sh7751_regs.intmsk00 = intc_baseaddr2 + 0x40;
+	sh7751_regs.intmskclr00 = intc_baseaddr2 + 0x60;
+
+	if (of_property_read_bool(intc, "sh7751,irlm")) {
+		icr = __raw_readw(sh7751_regs.icr);
+		icr |= 0x80;	// IRLM = 1
+		__raw_writew(icr, sh7751_regs.icr);
+	}
+
+	domain = irq_domain_add_linear(intc, NR_IRQS, &irq_ops, &sh7751_regs);
+	if (!domain) {
+		pr_err("Unable to allocate domain handle\n");
+		return -ENOMEM;
+	}
+	irq_set_default_host(domain);
+	return 0;
+}
+
+IRQCHIP_DECLARE(sh_7751_intc,
+		"renesas,sh7751-intc", sh7751_intc_of_init);