All of lore.kernel.org
 help / color / mirror / Atom feed
From: Claudiu <claudiu.beznea@tuxon.dev>
To: nobuhiro1.iwamatsu@toshiba.co.jp, pavel@denx.de
Cc: cip-dev@lists.cip-project.org, biju.das.jz@bp.renesas.com,
	prabhakar.mahadev-lad.rj@bp.renesas.com,
	claudiu.beznea@tuxon.dev
Subject: [PATCH 5.10.y-cip 03/36] pinctrl: renesas: pinctrl-rzg2l: Add IRQ domain to handle GPIO interrupt
Date: Wed, 27 Mar 2024 10:17:23 +0200	[thread overview]
Message-ID: <20240327081756.2228036-4-claudiu.beznea.uj@bp.renesas.com> (raw)
In-Reply-To: <20240327081756.2228036-1-claudiu.beznea.uj@bp.renesas.com>

From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>

commit db2e5f21a48edf1d1110d348add54bf22050643b upstream.

Add IRQ domain to RZ/G2L pinctrl driver to handle GPIO interrupt.

GPIO0-GPIO122 pins can be used as IRQ lines but only 32 pins can be
used as IRQ lines at a given time. Selection of pins as IRQ lines
is handled by IA55 (which is the IRQC block) which sits in between the
GPIO and GIC.

Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20220707182314.66610-7-prabhakar.mahadev-lad.rj@bp.renesas.com
[claudiu.beznea:
 - fixed conflict in bitmap_lock initialization
 - removed IRQCHIP_IMMUTABLE from rzg2l_gpio_irqchip.flags as it is not
   defined in v5.10
 - removed GPIOCHIP_IRQ_RESOURCE_HELPERS from rzg2l_gpio_irqchip as it
   is not defined in v5.10
 - adapted rzg2l_gpio_populate_parent_fwspec() to have only 3 arguments,
   allocate an object of type struct irq_fwspec and return it; also the
   return type of rzg2l_gpio_populate_parent_fwspec() has been changed
   to void * to cope with current framework capabilities
 - do not use gpio_irq_chip_set_chip() to assign the girq->chip in
   rzg2l_gpio_register() but use direct assignment as
   gpio_irq_chip_set_chip() is not available in v5.10]
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
 drivers/pinctrl/renesas/pinctrl-rzg2l.c | 234 ++++++++++++++++++++++++
 1 file changed, 234 insertions(+)

diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c
index bad9f3f8cd70..8e33d3a34c06 100644
--- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c
+++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c
@@ -9,9 +9,11 @@
 #include <linux/clk.h>
 #include <linux/gpio/driver.h>
 #include <linux/io.h>
+#include <linux/interrupt.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/of_device.h>
+#include <linux/of_irq.h>
 #include <linux/pinctrl/pinconf-generic.h>
 #include <linux/pinctrl/pinconf.h>
 #include <linux/pinctrl/pinctrl.h>
@@ -90,6 +92,7 @@
 #define PIN(n)			(0x0800 + 0x10 + (n))
 #define IOLH(n)			(0x1000 + (n) * 8)
 #define IEN(n)			(0x1800 + (n) * 8)
+#define ISEL(n)			(0x2c80 + (n) * 8)
 #define PWPR			(0x3014)
 #define SD_CH(n)		(0x3000 + (n) * 4)
 #define QSPI			(0x3008)
@@ -113,6 +116,10 @@
 #define RZG2L_PIN_ID_TO_PORT_OFFSET(id)	(RZG2L_PIN_ID_TO_PORT(id) + 0x10)
 #define RZG2L_PIN_ID_TO_PIN(id)		((id) % RZG2L_PINS_PER_PORT)
 
+#define RZG2L_TINT_MAX_INTERRUPT	32
+#define RZG2L_TINT_IRQ_START_INDEX	9
+#define RZG2L_PACK_HWIRQ(t, i)		(((t) << 16) | (i))
+
 struct rzg2l_dedicated_configs {
 	const char *name;
 	u32 config;
@@ -138,6 +145,9 @@ struct rzg2l_pinctrl {
 
 	struct gpio_chip		gpio_chip;
 	struct pinctrl_gpio_range	gpio_range;
+	DECLARE_BITMAP(tint_slot, RZG2L_TINT_MAX_INTERRUPT);
+	spinlock_t			bitmap_lock;
+	unsigned int			hwirq[RZG2L_TINT_MAX_INTERRUPT];
 
 	spinlock_t			lock; /* lock read/write registers */
 	struct mutex			mutex; /* serialize adding groups and functions */
@@ -904,8 +914,14 @@ static int rzg2l_gpio_get(struct gpio_chip *chip, unsigned int offset)
 
 static void rzg2l_gpio_free(struct gpio_chip *chip, unsigned int offset)
 {
+	unsigned int virq;
+
 	pinctrl_gpio_free(chip->base + offset);
 
+	virq = irq_find_mapping(chip->irq.domain, offset);
+	if (virq)
+		irq_dispose_mapping(virq);
+
 	/*
 	 * Set the GPIO as an input to ensure that the next GPIO request won't
 	 * drive the GPIO pin as an output.
@@ -1125,14 +1141,222 @@ static struct {
 	}
 };
 
+static int rzg2l_gpio_get_gpioint(unsigned int virq)
+{
+	unsigned int gpioint;
+	unsigned int i;
+	u32 port, bit;
+
+	port = virq / 8;
+	bit = virq % 8;
+
+	if (port >= ARRAY_SIZE(rzg2l_gpio_configs) ||
+	    bit >= RZG2L_GPIO_PORT_GET_PINCNT(rzg2l_gpio_configs[port]))
+		return -EINVAL;
+
+	gpioint = bit;
+	for (i = 0; i < port; i++)
+		gpioint += RZG2L_GPIO_PORT_GET_PINCNT(rzg2l_gpio_configs[i]);
+
+	return gpioint;
+}
+
+static void rzg2l_gpio_irq_disable(struct irq_data *d)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct rzg2l_pinctrl *pctrl = container_of(gc, struct rzg2l_pinctrl, gpio_chip);
+	unsigned int hwirq = irqd_to_hwirq(d);
+	unsigned long flags;
+	void __iomem *addr;
+	u32 port;
+	u8 bit;
+
+	port = RZG2L_PIN_ID_TO_PORT(hwirq);
+	bit = RZG2L_PIN_ID_TO_PIN(hwirq);
+
+	addr = pctrl->base + ISEL(port);
+	if (bit >= 4) {
+		bit -= 4;
+		addr += 4;
+	}
+
+	spin_lock_irqsave(&pctrl->lock, flags);
+	writel(readl(addr) & ~BIT(bit * 8), addr);
+	spin_unlock_irqrestore(&pctrl->lock, flags);
+
+	gpiochip_disable_irq(gc, hwirq);
+	irq_chip_disable_parent(d);
+}
+
+static void rzg2l_gpio_irq_enable(struct irq_data *d)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct rzg2l_pinctrl *pctrl = container_of(gc, struct rzg2l_pinctrl, gpio_chip);
+	unsigned int hwirq = irqd_to_hwirq(d);
+	unsigned long flags;
+	void __iomem *addr;
+	u32 port;
+	u8 bit;
+
+	gpiochip_enable_irq(gc, hwirq);
+
+	port = RZG2L_PIN_ID_TO_PORT(hwirq);
+	bit = RZG2L_PIN_ID_TO_PIN(hwirq);
+
+	addr = pctrl->base + ISEL(port);
+	if (bit >= 4) {
+		bit -= 4;
+		addr += 4;
+	}
+
+	spin_lock_irqsave(&pctrl->lock, flags);
+	writel(readl(addr) | BIT(bit * 8), addr);
+	spin_unlock_irqrestore(&pctrl->lock, flags);
+
+	irq_chip_enable_parent(d);
+}
+
+static int rzg2l_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+	return irq_chip_set_type_parent(d, type);
+}
+
+static void rzg2l_gpio_irqc_eoi(struct irq_data *d)
+{
+	irq_chip_eoi_parent(d);
+}
+
+static void rzg2l_gpio_irq_print_chip(struct irq_data *data, struct seq_file *p)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
+
+	seq_printf(p, dev_name(gc->parent));
+}
+
+static const struct irq_chip rzg2l_gpio_irqchip = {
+	.name = "rzg2l-gpio",
+	.irq_disable = rzg2l_gpio_irq_disable,
+	.irq_enable = rzg2l_gpio_irq_enable,
+	.irq_mask = irq_chip_mask_parent,
+	.irq_unmask = irq_chip_unmask_parent,
+	.irq_set_type = rzg2l_gpio_irq_set_type,
+	.irq_eoi = rzg2l_gpio_irqc_eoi,
+	.irq_print_chip = rzg2l_gpio_irq_print_chip,
+};
+
+static int rzg2l_gpio_child_to_parent_hwirq(struct gpio_chip *gc,
+					    unsigned int child,
+					    unsigned int child_type,
+					    unsigned int *parent,
+					    unsigned int *parent_type)
+{
+	struct rzg2l_pinctrl *pctrl = gpiochip_get_data(gc);
+	unsigned long flags;
+	int gpioint, irq;
+
+	gpioint = rzg2l_gpio_get_gpioint(child);
+	if (gpioint < 0)
+		return gpioint;
+
+	spin_lock_irqsave(&pctrl->bitmap_lock, flags);
+	irq = bitmap_find_free_region(pctrl->tint_slot, RZG2L_TINT_MAX_INTERRUPT, get_order(1));
+	spin_unlock_irqrestore(&pctrl->bitmap_lock, flags);
+	if (irq < 0)
+		return -ENOSPC;
+	pctrl->hwirq[irq] = child;
+	irq += RZG2L_TINT_IRQ_START_INDEX;
+
+	/* All these interrupts are level high in the CPU */
+	*parent_type = IRQ_TYPE_LEVEL_HIGH;
+	*parent = RZG2L_PACK_HWIRQ(gpioint, irq);
+	return 0;
+}
+
+static void *rzg2l_gpio_populate_parent_fwspec(struct gpio_chip *chip,
+					     unsigned int parent_hwirq,
+					     unsigned int parent_type)
+{
+	struct irq_fwspec *fwspec;
+
+	fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
+	if (!fwspec)
+		return NULL;
+
+	fwspec->fwnode = chip->irq.parent_domain->fwnode;
+	fwspec->param_count = 2;
+	fwspec->param[0] = parent_hwirq;
+	fwspec->param[1] = parent_type;
+
+	return fwspec;
+}
+
+static void rzg2l_gpio_irq_domain_free(struct irq_domain *domain, unsigned int virq,
+				       unsigned int nr_irqs)
+{
+	struct irq_data *d;
+
+	d = irq_domain_get_irq_data(domain, virq);
+	if (d) {
+		struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+		struct rzg2l_pinctrl *pctrl = container_of(gc, struct rzg2l_pinctrl, gpio_chip);
+		irq_hw_number_t hwirq = irqd_to_hwirq(d);
+		unsigned long flags;
+		unsigned int i;
+
+		for (i = 0; i < RZG2L_TINT_MAX_INTERRUPT; i++) {
+			if (pctrl->hwirq[i] == hwirq) {
+				spin_lock_irqsave(&pctrl->bitmap_lock, flags);
+				bitmap_release_region(pctrl->tint_slot, i, get_order(1));
+				spin_unlock_irqrestore(&pctrl->bitmap_lock, flags);
+				pctrl->hwirq[i] = 0;
+				break;
+			}
+		}
+	}
+	irq_domain_free_irqs_common(domain, virq, nr_irqs);
+}
+
+static void rzg2l_init_irq_valid_mask(struct gpio_chip *gc,
+				      unsigned long *valid_mask,
+				      unsigned int ngpios)
+{
+	struct rzg2l_pinctrl *pctrl = gpiochip_get_data(gc);
+	struct gpio_chip *chip = &pctrl->gpio_chip;
+	unsigned int offset;
+
+	/* Forbid unused lines to be mapped as IRQs */
+	for (offset = 0; offset < chip->ngpio; offset++) {
+		u32 port, bit;
+
+		port = offset / 8;
+		bit = offset % 8;
+
+		if (port >= ARRAY_SIZE(rzg2l_gpio_configs) ||
+		    bit >= RZG2L_GPIO_PORT_GET_PINCNT(rzg2l_gpio_configs[port]))
+			clear_bit(offset, valid_mask);
+	}
+}
+
 static int rzg2l_gpio_register(struct rzg2l_pinctrl *pctrl)
 {
 	struct device_node *np = pctrl->dev->of_node;
 	struct gpio_chip *chip = &pctrl->gpio_chip;
 	const char *name = dev_name(pctrl->dev);
+	struct irq_domain *parent_domain;
 	struct of_phandle_args of_args;
+	struct device_node *parent_np;
+	struct gpio_irq_chip *girq;
 	int ret;
 
+	parent_np = of_irq_find_parent(np);
+	if (!parent_np)
+		return -ENXIO;
+
+	parent_domain = irq_find_host(parent_np);
+	of_node_put(parent_np);
+	if (!parent_domain)
+		return -EPROBE_DEFER;
+
 	ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0, &of_args);
 	if (ret) {
 		dev_err(pctrl->dev, "Unable to parse gpio-ranges\n");
@@ -1159,6 +1383,15 @@ static int rzg2l_gpio_register(struct rzg2l_pinctrl *pctrl)
 	chip->base = -1;
 	chip->ngpio = of_args.args[2];
 
+	girq = &chip->irq;
+	girq->chip = (struct irq_chip *)&rzg2l_gpio_irqchip;
+	girq->fwnode = of_node_to_fwnode(np);
+	girq->parent_domain = parent_domain;
+	girq->child_to_parent_hwirq = rzg2l_gpio_child_to_parent_hwirq;
+	girq->populate_parent_alloc_arg = rzg2l_gpio_populate_parent_fwspec;
+	girq->child_irq_domain_ops.free = rzg2l_gpio_irq_domain_free;
+	girq->init_valid_mask = rzg2l_init_irq_valid_mask;
+
 	pctrl->gpio_range.id = 0;
 	pctrl->gpio_range.pin_base = 0;
 	pctrl->gpio_range.base = 0;
@@ -1274,6 +1507,7 @@ static int rzg2l_pinctrl_probe(struct platform_device *pdev)
 	}
 
 	spin_lock_init(&pctrl->lock);
+	spin_lock_init(&pctrl->bitmap_lock);
 	mutex_init(&pctrl->mutex);
 
 	platform_set_drvdata(pdev, pctrl);
-- 
2.39.2



  parent reply	other threads:[~2024-03-27 10:37 UTC|newest]

Thread overview: 46+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-03-27  8:17 [PATCH 5.10.y-cip 00/36] Add IA55 interrupt controller support Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 01/36] dt-bindings: pinctrl: renesas,rzg2l-pinctrl: Document the properties to handle GPIO IRQ Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 02/36] pinctrl: renesas: rzg2l: Select GPIOLIB_IRQCHIP and IRQ_DOMAIN_HIERARCHY Claudiu
2024-03-27  8:17 ` Claudiu [this message]
2024-03-27  8:17 ` [PATCH 5.10.y-cip 04/36] pinctrl: renesas: rzg2l: Fix configuring the GPIO pins as interrupts Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 05/36] pinctrl: renesas: rzg2l: Add BUILD_BUG_ON() checks Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 06/36] pinctrl: renesas: rzg2l: Use devm_clk_get_enabled() helper Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 07/36] pinctrl: renesas: rzg2l: Enhance driver to support interrupt affinity setting Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 08/36] dt-bindings: interrupt-controller: Add Renesas RZ/G2L Interrupt Controller Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 09/36] irqdomain: Make of_phandle_args_to_fwspec() generally available Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 10/36] of: platform: Skip populating IRQ to device resource table Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 11/36] irqchip: Add RZ/G2L IA55 Interrupt Controller driver Claudiu
2024-03-27 12:07   ` Pavel Machek
2024-03-28 11:44     ` claudiu beznea
2024-03-27  8:17 ` [PATCH 5.10.y-cip 12/36] irqchip: remove MODULE_LICENSE in non-modules Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 13/36] irqchip: renesas-rzg2l: Fix logic to clear TINT interrupt source Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 14/36] irqchip/renesas-rzg2l: Convert to irq_data_get_irq_chip_data() Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 15/36] irqchip/renesas-rzg2l: Enhance driver to support interrupt affinity setting Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 16/36] irqchip/renesas-rzg2l: Use tabs instead of spaces Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 17/36] irqchip/renesas-rzg2l: Align struct member names to tabs Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 18/36] irqchip/renesas-rzg2l: Document structure members Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 19/36] irqchip/renesas-rzg2l: Implement restriction when writing ISCR register Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 20/36] irqchip/renesas-rzg2l: Add macro to retrieve TITSR register offset based on register's index Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 21/36] irqchip/renesas-rzg2l: Flush posted write in irq_eoi() Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 22/36] irqchip/renesas-rzg2l: Rename rzg2l_tint_eoi() Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 23/36] irqchip/renesas-rzg2l: Rename rzg2l_irq_eoi() Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 24/36] irqchip/renesas-rzg2l: Prevent spurious interrupts when setting trigger type Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 25/36] irqchip/renesas-rzg2l: Do not set TIEN and TINT source at the same time Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 26/36] soc: renesas: Kconfig: Enable IRQC driver for RZ/G2L SoC Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 27/36] arm64: dts: renesas: r9a07g043u: Add IRQC node Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 28/36] arm64: dts: renesas: r9a07g044: " Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 29/36] arm64: dts: renesas: r9a07g054: " Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 30/36] arm64: dts: renesas: r9a07g043u: Update pinctrl node to handle GPIO interrupts Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 31/36] arm64: dts: renesas: r9a07g044: " Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 32/36] arm64: dts: renesas: r9a07g054: " Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 33/36] dt-bindings: interrupt-controller: Add macros for NMI and IRQ0-7 interrupts present on RZ/G2L SoC Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 34/36] arm64: dts: renesas: rzg2l-smarc-som: Add PHY interrupt support for ETH{0/1} Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 35/36] arm64: dts: renesas: rzg2lc-smarc-som: Add PHY interrupt support for ETH0 Claudiu
2024-03-27  8:17 ` [PATCH 5.10.y-cip 36/36] arm64: dts: renesas: rzg2ul-smarc-som: Add PHY interrupt support for ETH{0/1} Claudiu
2024-03-27 12:02 ` [PATCH 5.10.y-cip 00/36] Add IA55 interrupt controller support Pavel Machek
2024-03-28 10:45   ` Pavel Machek
2024-04-19 17:51 ` Krzysztof Kozlowski
2024-04-19 19:43   ` Pavel Machek
2024-04-20 11:20     ` Krzysztof Kozlowski
2024-04-21 14:11       ` Pavel Machek
2024-04-21 14:14         ` Krzysztof Kozlowski

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=20240327081756.2228036-4-claudiu.beznea.uj@bp.renesas.com \
    --to=claudiu.beznea@tuxon.dev \
    --cc=biju.das.jz@bp.renesas.com \
    --cc=cip-dev@lists.cip-project.org \
    --cc=nobuhiro1.iwamatsu@toshiba.co.jp \
    --cc=pavel@denx.de \
    --cc=prabhakar.mahadev-lad.rj@bp.renesas.com \
    /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.