All of lore.kernel.org
 help / color / mirror / Atom feed
From: Yoshinori Sato <ysato@users.sourceforge.jp>
To: linux-sh@vger.kernel.org
Cc: Yoshinori Sato <ysato@users.sourceforge.jp>,
	Thomas Gleixner <tglx@linutronix.de>,
	Geert Uytterhoeven <geert+renesas@glider.be>,
	Magnus Damm <magnus.damm@gmail.com>
Subject: [PATCH v4 17/37] irqchip: Add SH7751 INTC driver
Date: Tue, 14 Nov 2023 17:00:08 +0900	[thread overview]
Message-ID: <e9f8cc0fc8a240adb11b3ad60197868401a3dd91.1699856600.git.ysato@users.sourceforge.jp> (raw)
In-Reply-To: <cover.1699856600.git.ysato@users.sourceforge.jp>

Renesas SH7751 Internal interrupt controller driver.

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
---
 drivers/irqchip/Kconfig              |   8 +
 drivers/irqchip/Makefile             |   1 +
 drivers/irqchip/irq-renesas-sh7751.c | 290 +++++++++++++++++++++++++++
 3 files changed, 299 insertions(+)
 create mode 100644 drivers/irqchip/irq-renesas-sh7751.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index f7149d0f3d45..658523f65b1d 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -679,4 +679,12 @@ config SUNPLUS_SP7021_INTC
 	  chained controller, routing all interrupt source in P-Chip to
 	  the primary controller on C-Chip.
 
+config RENESAS_SH7751_INTC
+	bool "Renesas SH7751 Interrupt Controller"
+	depends on SH_DEVICE_TREE || COMPILE_TEST
+	select IRQ_DOMAIN_HIERARCHY
+	help
+	  Support for the Renesas SH7751 On-chip interrupt controller.
+	  And external interrupt encoder for some targets.
+
 endmenu
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index ffd945fe71aa..26c91d075e25 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_SH7751_INTC)	+= irq-renesas-sh7751.o
diff --git a/drivers/irqchip/irq-renesas-sh7751.c b/drivers/irqchip/irq-renesas-sh7751.c
new file mode 100644
index 000000000000..f3bc150267c0
--- /dev/null
+++ b/drivers/irqchip/irq-renesas-sh7751.c
@@ -0,0 +1,290 @@
+// 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>
+#include <dt-bindings/interrupt-controller/sh_intc.h>
+
+struct ipr {
+	unsigned int off;
+	unsigned int idx;
+};
+
+struct sh7751_intc_priv {
+	void __iomem *base;
+	void __iomem *intpri00;
+	struct ipr   *iprmap[2];
+	bool	     irlm;
+};
+
+enum {
+	R_ICR         = 0x00,
+	R_IPR         = 0x04,
+	R_INTPRI00    = 0x00,
+	R_INTREQ00    = 0x20,
+	R_INTMSK00    = 0x40,
+	R_INTMSKCLR00 = 0x60,
+};
+
+#define ICR_IRLM BIT(7)
+
+/*
+ * SH7751 IRQ mapping
+ *  IRQ16 - 63: Group0 - IPRA to IPRD
+ *   IRQ16 - 31: external IRL input (ICR.IRLM is 0)
+ *  IRQ80 - 92: Group1 - INTPRI00
+ */
+#define IRQ_START	16
+#define MAX_IRL		(IRQ_START + NR_IRL)
+#define GRP0_IRQ_END	63
+#define GRP1_IRQ_START	80
+#define IRQ_END		92
+
+#define NR_IPRMAP0	(GRP0_IRQ_END - IRQ_START + 1)
+#define NR_IPRMAP1	(IRQ_END - GRP1_IRQ_START)
+#define IPR_PRI_MASK	0x000f
+
+/*
+ * IPR registers have 4bit priority x 4 entry (16bits)
+ * Interrupts can be masked by setting pri to 0.
+ */
+static void update_ipr(struct sh7751_intc_priv *priv, unsigned int irq, u16 pri)
+{
+	struct ipr *ipr = NULL;
+	void __iomem *ipr_base;
+	unsigned int offset;
+	u16 mask;
+
+	if (irq < GRP1_IRQ_START) {
+		/* Group0 */
+		ipr = priv->iprmap[0];
+		ipr += irq - IRQ_START;
+		ipr_base = priv->base + R_IPR;
+		offset = ipr->off;
+	} else {
+		/* Group1 */
+		ipr = priv->iprmap[1];
+		ipr += irq - GRP1_IRQ_START;
+		ipr_base = priv->intpri00;
+		offset = ipr->off - INTPRI00;
+	}
+	if (ipr->off != ~0) {
+		mask = ~(IPR_PRI_MASK << ipr->idx);
+		pri = (pri & IPR_PRI_MASK) << ipr->idx;
+		mask &= __raw_readw(ipr_base + offset);
+		__raw_writew(mask | pri, ipr_base + offset);
+	} else {
+		pr_warn_once("%s: undefined IPR in irq %u\n", __FILE__, irq);
+	}
+}
+
+static inline bool is_valid_irq(unsigned int irq)
+{
+	/* IRQ16 - 63 */
+	if (irq >= IRQ_START && irq < IRQ_START + NR_IPRMAP0)
+		return true;
+	/* IRQ80 - 92 */
+	if (irq >= GRP1_IRQ_START && irq <= IRQ_END)
+		return true;
+	return false;
+}
+
+static inline struct sh7751_intc_priv *irq_data_to_priv(struct irq_data *data)
+{
+	return data->domain->host_data;
+}
+
+static void endisable_irq(struct irq_data *data, int enable)
+{
+	struct sh7751_intc_priv *priv;
+	unsigned int irq;
+
+	priv = irq_data_to_priv(data);
+
+	irq = irqd_to_hwirq(data);
+	if (!is_valid_irq(irq)) {
+		/* IRQ out of range */
+		pr_warn_once("%s: IRQ %u is out of range\n", __FILE__, irq);
+		return;
+	}
+
+	if (irq <= MAX_IRL && !priv->irlm)
+		/* IRL encoded external interrupt */
+		/* disable for SR.IMASK */
+		update_sr_imask(irq - IRQ_START, enable);
+	else
+		/* Internal peripheral interrupt */
+		/* mask for IPR priority 0 */
+		update_ipr(priv, irq, enable);
+}
+
+static void sh7751_mask_irq(struct irq_data *data)
+{
+	endisable_irq(data, 0);
+}
+
+static void sh7751_unmask_irq(struct irq_data *data)
+{
+	endisable_irq(data, 1);
+}
+
+static const struct irq_chip sh7751_irq_chip = {
+	.name		= "SH7751-INTC",
+	.irq_unmask	= sh7751_unmask_irq,
+	.irq_mask	= sh7751_mask_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 load_ipr_map(struct device_node *intc,
+			       struct sh7751_intc_priv *priv)
+{
+	struct property *ipr_map;
+	unsigned int num_ipr, i;
+	struct ipr *ipr;
+	const __be32 *p;
+	u32 irq;
+
+	ipr_map = of_find_property(intc, "renesas,ipr-map", &num_ipr);
+	if (IS_ERR(ipr_map))
+		return PTR_ERR(ipr_map);
+	num_ipr /= sizeof(u32);
+	/* 3words per entry. */
+	if (num_ipr % 3)
+		goto error1;
+	num_ipr /= 3;
+	if (num_ipr >= NR_IPRMAP0 + NR_IPRMAP1)
+		goto error1;
+
+	/* Allocate map array and fill in unassigned */
+	priv->iprmap[0] = kmalloc_array(NR_IPRMAP0, sizeof(struct ipr), GFP_KERNEL);
+	if (priv->iprmap[0] == NULL)
+		return -ENOMEM;
+	memset(priv->iprmap[0], ~0, NR_IPRMAP0 * sizeof(struct ipr));
+	priv->iprmap[1] = kmalloc_array(NR_IPRMAP1, sizeof(struct ipr), GFP_KERNEL);
+	if (priv->iprmap[1] == NULL) {
+		kfree(priv->iprmap[0]);
+		return -ENOMEM;
+	}
+	memset(priv->iprmap[1], ~0, NR_IPRMAP1 * sizeof(struct ipr));
+
+	p = NULL;
+	for (; num_ipr > 0; num_ipr--) {
+		/* 1st word - INTEVT code */
+		p = of_prop_next_u32(ipr_map, p, &irq);
+		if (!p)
+			goto error;
+		irq = evt2irq(irq);
+		if (!is_valid_irq(irq))
+			goto error;
+		if (irq < GRP1_IRQ_START) {
+			ipr = priv->iprmap[0];
+			irq -= IRQ_START;
+		} else {
+			ipr = priv->iprmap[1];
+			irq -= GRP1_IRQ_START;
+		}
+		ipr += irq;
+		/* 2nd word - IPR register offset */
+		p = of_prop_next_u32(ipr_map, p, &ipr->off);
+		/* 3rd word - IPR register bit indx */
+		p = of_prop_next_u32(ipr_map, p, &ipr->idx);
+
+		if ((ipr->off != INTPRI00 && ipr->off > IPRD) ||
+		    ipr->idx > IPR_B12)
+			goto error;
+	}
+
+	for (ipr = priv->iprmap[0], i = 0; i < NR_IPRMAP0; ipr++, i++) {
+		if (ipr->off != ~0) {
+			pr_debug("INTEVT=%04x (%u) reg=IPR%c idx=%u\n",
+				 irq2evt(i + IRQ_START), i + IRQ_START,
+				 'A' + ipr->off / 4, ipr->idx);
+		}
+	}
+	for (ipr = priv->iprmap[1], i = 0; i < NR_IPRMAP1; ipr++, i++) {
+		if (ipr->off != ~0) {
+			pr_debug("INTEVT=%04x (%u) reg=INTPRI00 idx=%u\n",
+				 irq2evt(i + GRP1_IRQ_START), i + GRP1_IRQ_START,
+				 ipr->idx);
+		}
+	}
+	return 0;
+error:
+	kfree(priv->iprmap[0]);
+	kfree(priv->iprmap[1]);
+error1:
+	pr_err("%pOFP: Failed to load renesas,ipr-map\n", intc);
+	return -EINVAL;
+}
+
+static int __init sh7751_intc_of_init(struct device_node *intc,
+				      struct device_node *parent)
+{
+	struct sh7751_intc_priv *priv;
+	void __iomem *base, *base2;
+	struct irq_domain *domain;
+	u16 icr;
+	int ret;
+
+	base = of_iomap(intc, 0);
+	base2 = of_iomap(intc, 1);
+	if (!base || !base2) {
+		pr_err("%pOFP: Invalid register definition\n", intc);
+		return -EINVAL;
+	}
+
+	priv = kzalloc(sizeof(struct sh7751_intc_priv), GFP_KERNEL);
+	if (priv == NULL)
+		return -ENOMEM;
+
+	ret = load_ipr_map(intc, priv);
+	if (ret < 0) {
+		kfree(priv);
+		return ret;
+	}
+
+	priv->base = base;
+	priv->intpri00 = base2;
+
+	if (of_property_read_bool(intc, "renesas,irlm")) {
+		priv->irlm = true;
+		icr = __raw_readw(priv->base + R_ICR);
+		icr |= ICR_IRLM;
+		__raw_writew(icr, priv->base + R_ICR);
+	}
+
+	domain = irq_domain_add_linear(intc, NR_IRQS, &irq_ops, priv);
+	if (domain == NULL) {
+		pr_err("%pOFP: cannot initialize irq domain\n", intc);
+		kfree(priv);
+		return -ENOMEM;
+	}
+
+	irq_set_default_host(domain);
+	pr_info("%pOFP: SH7751 Interrupt controller (%s external IRQ)",
+		intc, priv->irlm ? "4 lines" : "15 level");
+	return 0;
+}
+
+IRQCHIP_DECLARE(sh_7751_intc,
+		"renesas,sh7751-intc", sh7751_intc_of_init);
-- 
2.39.2


  parent reply	other threads:[~2023-11-14  8:01 UTC|newest]

Thread overview: 55+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-11-14  7:59 [PATCH v4 00/37] Device Tree support for SH7751 based board Yoshinori Sato
2023-11-14  7:59 ` [PATCH v4 01/37] sh: passing FDT address to kernel startup Yoshinori Sato
2023-11-14  8:44   ` Sergei Shtylyov
2023-11-14  7:59 ` [PATCH v4 02/37] sh: Kconfig unified OF supported targets Yoshinori Sato
2023-11-14 12:46   ` Arnd Bergmann
2023-11-14  7:59 ` [PATCH v4 03/37] sh: Enable OF support for build and configuration Yoshinori Sato
2023-11-14  7:59 ` [PATCH v4 04/37] dt-bindings: interrupt-controller: Add header for Renesas SH3/4 INTC Yoshinori Sato
2023-11-14 21:25   ` Krzysztof Kozlowski
2023-11-14  7:59 ` [PATCH v4 05/37] sh: GENERIC_IRQ_CHIP support for CONFIG_OF=y Yoshinori Sato
2023-11-14  7:59 ` [PATCH v4 06/37] sh: kernel/setup Update DT support Yoshinori Sato
2023-11-14  7:59 ` [PATCH v4 07/37] sh: Fix COMMON_CLK support in CONFIG_OF=y Yoshinori Sato
2023-11-14  7:59 ` [PATCH v4 08/37] clocksource: sh_tmu: CLOCKSOURCE support Yoshinori Sato
2023-11-14  8:00 ` [PATCH v4 09/37] dt-bindings: timer: renesas,tmu: add renesas,tmu-sh7750 Yoshinori Sato
2023-11-14 21:26   ` Krzysztof Kozlowski
2023-11-15  8:17     ` Geert Uytterhoeven
2023-11-15 21:05       ` Krzysztof Kozlowski
2023-11-14  8:00 ` [PATCH v4 10/37] sh: Common PCI framework support Yoshinori Sato
2023-11-14 13:03   ` Arnd Bergmann
2023-11-14  8:00 ` [PATCH v4 11/37] sh: Add old PCI drivers compatible stub Yoshinori Sato
2023-11-14  8:00 ` [PATCH v4 12/37] pci: pci-sh7751: Add SH7751 PCI driver Yoshinori Sato
2023-11-20 18:16   ` Bjorn Helgaas
2023-11-14  8:00 ` [PATCH v4 13/37] dt-bindings: pci: pci-sh7751: Add SH7751 PCI Yoshinori Sato
2023-11-14 21:27   ` Krzysztof Kozlowski
2023-11-14  8:00 ` [PATCH v4 14/37] dt-bindings: clock: sh7750-cpg: Add renesas,sh7750-cpg header Yoshinori Sato
2023-11-14 21:29   ` Krzysztof Kozlowski
2023-11-14  8:00 ` [PATCH v4 15/37] clk: Compatible with narrow registers Yoshinori Sato
2023-11-14  8:00 ` [PATCH v4 16/37] clk: renesas: Add SH7750/7751 CPG Driver Yoshinori Sato
2023-11-14  8:00 ` Yoshinori Sato [this message]
2023-11-14  8:00 ` [PATCH v4 18/37] dt-bindings: interrupt-controller: renesas,sh7751-intc: Add json-schema Yoshinori Sato
2023-11-14  8:00 ` [PATCH v4 19/37] irqchip: SH7751 IRL external encoder with enable gate Yoshinori Sato
2023-11-14  8:00 ` [PATCH v4 20/37] dt-bindings: interrupt-controller: renesas,sh7751-irl-ext: Add json-schema Yoshinori Sato
2023-11-14  8:00 ` [PATCH v4 21/37] serial: sh-sci: fix SH4 OF support Yoshinori Sato
2023-11-14  8:00 ` [PATCH v4 22/37] dt-bindings: serial: renesas,scif: Add scif-sh7751 Yoshinori Sato
2023-11-14  8:00 ` [PATCH v4 23/37] dt-bindings: display: smi,sm501: SMI SM501 binding json-schema Yoshinori Sato
2023-11-14  8:00 ` [PATCH v4 24/37] mfd: sm501: Convert platform_data to OF property Yoshinori Sato
2023-11-23 13:59   ` Lee Jones
2023-11-14  8:00 ` [PATCH v4 25/37] dt-binding: sh: cpus: Add SH CPUs json-schema Yoshinori Sato
2023-11-14  8:00 ` [PATCH v4 26/37] dt-bindings: vendor-prefix: Add new vendor iodata and smi Yoshinori Sato
2023-11-14 16:35   ` Geert Uytterhoeven
2023-11-14  8:00 ` [PATCH v4 27/37] dt-bindings: ata: ata-generic: Add new targets Yoshinori Sato
2023-11-14 18:19   ` Rob Herring
2023-11-14  8:00 ` [PATCH v4 28/37] dt-bindings: soc: renesas: sh: Add SH7751 based target Yoshinori Sato
2023-11-14  8:00 ` [PATCH v4 29/37] sh: SH7751R SoC Internal peripheral definition dtsi Yoshinori Sato
2023-11-14  8:00 ` [PATCH v4 30/37] sh: add RTS7751R2D Plus DTS Yoshinori Sato
2023-11-14  8:00 ` [PATCH v4 31/37] sh: Add IO DATA LANDISK dts Yoshinori Sato
2023-11-14  8:00 ` [PATCH v4 32/37] sh: Add IO DATA USL-5P dts Yoshinori Sato
2023-11-14  9:02   ` Sergei Shtylyov
2023-11-14  9:04     ` Sergei Shtylyov
2023-11-14  8:00 ` [PATCH v4 33/37] sh: j2_mimas_v2.dts update Yoshinori Sato
2023-11-14  8:00 ` [PATCH v4 34/37] sh: Add dtbs target support Yoshinori Sato
2023-11-14  8:00 ` [PATCH v4 35/37] sh: RTS7751R2D Plus OF defconfig Yoshinori Sato
2023-11-14  8:00 ` [PATCH v4 36/37] sh: LANDISK " Yoshinori Sato
2023-11-14  8:00 ` [PATCH v4 37/37] sh: j2_defconfig: update Yoshinori Sato
2023-11-14  8:58 ` [PATCH v4 00/37] Device Tree support for SH7751 based board John Paul Adrian Glaubitz
2023-11-14 18:39   ` John Paul Adrian Glaubitz

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=e9f8cc0fc8a240adb11b3ad60197868401a3dd91.1699856600.git.ysato@users.sourceforge.jp \
    --to=ysato@users.sourceforge.jp \
    --cc=geert+renesas@glider.be \
    --cc=linux-sh@vger.kernel.org \
    --cc=magnus.damm@gmail.com \
    --cc=tglx@linutronix.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.