[PATCH v1 1/4] gpio: phytium: improve core IRQ and wake handling
From: Zhu Ling
Date: Sun Mar 01 2026 - 20:52:20 EST
Refactor the core GPIO IRQ path to better match gpiolib IRQ chip
expectations and suspend/resume behavior.
This updates the core to:
- add IRQ chip print and wake hooks
- handle both single and multi-parent IRQ routing
- fix wake IRQ lookup by checking valid parent IRQs
- use GPIO clear/version macros shared by frontends
- track resume state while restoring IRQ registers
The change also keeps direction/output programming order consistent
with other GPIO controllers.
Signed-off-by: Zhu Ling <1536943441@xxxxxx>
---
drivers/gpio/gpio-phytium-core.c | 114 +++++++++++++++++++++++++------
drivers/gpio/gpio-phytium-core.h | 8 ++-
2 files changed, 102 insertions(+), 20 deletions(-)
diff --git a/drivers/gpio/gpio-phytium-core.c b/drivers/gpio/gpio-phytium-core.c
index 7d7695acc..df7755ad7 100644
--- a/drivers/gpio/gpio-phytium-core.c
+++ b/drivers/gpio/gpio-phytium-core.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright (c) 2019-2023, Phytium Phytium Technology Co., Ltd.
+ * Copyright (c) 2019-2023, Phytium Technology Co., Ltd.
*/
#include <linux/err.h>
@@ -8,11 +8,13 @@
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/bitops.h>
+#include <linux/seq_file.h>
+#include <linux/interrupt.h>
#include "gpio-phytium-core.h"
static int get_pin_location(struct phytium_gpio *gpio, unsigned int offset,
- struct pin_loc *pl)
+ struct pin_loc *pl)
{
int ret;
@@ -32,7 +34,7 @@ static int get_pin_location(struct phytium_gpio *gpio, unsigned int offset,
}
static void phytium_gpio_toggle_trigger(struct phytium_gpio *gpio,
- unsigned int offset)
+ unsigned int offset)
{
struct gpio_chip *gc;
u32 pol;
@@ -128,14 +130,13 @@ int phytium_gpio_direction_output(struct gpio_chip *gc, unsigned int offset,
return -EINVAL;
ddr = gpio->regs + GPIO_SWPORTA_DDR + (loc.port * GPIO_PORT_STRIDE);
+ phytium_gpio_set(gc, offset, value);
raw_spin_lock_irqsave(&gpio->lock, flags);
writel(readl(ddr) | BIT(loc.offset), ddr);
raw_spin_unlock_irqrestore(&gpio->lock, flags);
- phytium_gpio_set(gc, offset, value);
-
return 0;
}
EXPORT_SYMBOL_GPL(phytium_gpio_direction_output);
@@ -171,6 +172,8 @@ void phytium_gpio_irq_mask(struct irq_data *d)
writel(val, gpio->regs + GPIO_INTMASK);
raw_spin_unlock(&gpio->lock);
+
+ gpiochip_disable_irq(gc, irqd_to_hwirq(d));
}
EXPORT_SYMBOL_GPL(phytium_gpio_irq_mask);
@@ -184,6 +187,8 @@ void phytium_gpio_irq_unmask(struct irq_data *d)
if (irqd_to_hwirq(d) >= gpio->ngpio[0])
return;
+ gpiochip_enable_irq(gc, irqd_to_hwirq(d));
+
raw_spin_lock(&gpio->lock);
val = readl(gpio->regs + GPIO_INTMASK);
@@ -267,10 +272,14 @@ void phytium_gpio_irq_enable(struct irq_data *d)
unsigned long flags;
u32 val;
+ if (gpio->is_resuming)
+ return;
/* Only port A can provide interrupt source */
if (irqd_to_hwirq(d) >= gpio->ngpio[0])
return;
+ gpiochip_enable_irq(gc, irqd_to_hwirq(d));
+
raw_spin_lock_irqsave(&gpio->lock, flags);
val = readl(gpio->regs + GPIO_INTEN);
@@ -278,6 +287,8 @@ void phytium_gpio_irq_enable(struct irq_data *d)
writel(val, gpio->regs + GPIO_INTEN);
raw_spin_unlock_irqrestore(&gpio->lock, flags);
+
+ gpiochip_disable_irq(gc, irqd_to_hwirq(d));
}
EXPORT_SYMBOL_GPL(phytium_gpio_irq_enable);
@@ -302,6 +313,14 @@ void phytium_gpio_irq_disable(struct irq_data *d)
}
EXPORT_SYMBOL_GPL(phytium_gpio_irq_disable);
+void phytium_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));
+}
+EXPORT_SYMBOL_GPL(phytium_gpio_irq_print_chip);
+
void phytium_gpio_irq_handler(struct irq_desc *desc)
{
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
@@ -309,19 +328,36 @@ void phytium_gpio_irq_handler(struct irq_desc *desc)
struct irq_chip *irqchip = irq_desc_get_chip(desc);
unsigned long pending;
int offset;
+ int index = -1;
+ unsigned int index_found = 0;
chained_irq_enter(irqchip, desc);
pending = readl(gpio->regs + GPIO_INTSTATUS);
+
+ if (gc->irq.num_parents > 1) {
+ for (index = 0 ; index < gc->irq.num_parents; index++) {
+ if (gc->irq.parents[index] == desc->irq_data.irq) {
+ index_found = 1;
+ break;
+ }
+ }
+ if (index_found == 0) {
+ pr_err("Can't find index for this gpio interrupt.\n");
+ index = -1;
+ }
+ }
+
if (pending) {
for_each_set_bit(offset, &pending, gpio->ngpio[0]) {
- int gpio_irq = irq_find_mapping(gc->irq.domain,
- offset);
- generic_handle_irq(gpio_irq);
-
- if ((irq_get_trigger_type(gpio_irq) &
- IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH)
- phytium_gpio_toggle_trigger(gpio, offset);
+ if (index == -1 || offset == index) {
+ int gpio_irq = irq_find_mapping(gc->irq.domain,
+ offset);
+ generic_handle_irq(gpio_irq);
+ if ((irq_get_trigger_type(gpio_irq) &
+ IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH)
+ phytium_gpio_toggle_trigger(gpio, offset);
+ }
}
}
@@ -343,13 +379,54 @@ int phytium_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
}
EXPORT_SYMBOL_GPL(phytium_gpio_get_direction);
-#if CONFIG_SMP
-int
-phytium_gpio_irq_set_affinity(struct irq_data *d, const struct cpumask *mask_val, bool force)
+int phytium_gpio_irq_set_wake(struct irq_data *d, unsigned int enable)
{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct phytium_gpio *gpio = gpiochip_get_data(gc);
+ struct phytium_gpio_ctx *ctx = &gpio->ctx;
+ irq_hw_number_t bit = irqd_to_hwirq(d);
+ unsigned long flags;
+ int ret;
+
+ if (gpio->irq[bit] >= 0)
+ ret = irq_set_irq_wake(gpio->irq[bit], enable);
+ else
+ ret = irq_set_irq_wake(gpio->irq[0], enable);
+
+ if (ret < 0)
+ dev_err(gc->parent, "set gpio irq wake failed!\n");
+
+ raw_spin_lock_irqsave(&gpio->lock, flags);
+
+ if (enable) {
+ ctx->wake_en |= BIT(bit);
+ if (gpio->is_resuming == 1) {
+ writel(~ctx->wake_en, gpio->regs + GPIO_INTMASK);
+ writel(ctx->wake_en, gpio->regs + GPIO_INTEN);
+ }
+ } else {
+ ctx->wake_en &= ~BIT(bit);
+ }
+
+ raw_spin_unlock_irqrestore(&gpio->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(phytium_gpio_irq_set_wake);
+
+int phytium_gpio_irq_set_affinity(struct irq_data *d,
+ const struct cpumask *mask_val, bool force)
+{
+ int hwirq = irqd_to_hwirq(d);
struct gpio_chip *chip_data = irq_data_get_irq_chip_data(d);
- struct irq_chip *chip = irq_get_chip(chip_data->irq.num_parents);
- struct irq_data *data = irq_get_irq_data(chip_data->irq.num_parents);
+ struct irq_chip *chip;
+ struct irq_data *data;
+
+ if (chip_data->irq.num_parents == 1)
+ hwirq = 0;
+
+ chip = irq_get_chip(chip_data->irq.parents[hwirq]);
+ data = irq_get_irq_data(chip_data->irq.parents[hwirq]);
if (chip && chip->irq_set_affinity)
return chip->irq_set_affinity(data, mask_val, force);
@@ -357,7 +434,6 @@ phytium_gpio_irq_set_affinity(struct irq_data *d, const struct cpumask *mask_val
return -EINVAL;
}
EXPORT_SYMBOL_GPL(phytium_gpio_irq_set_affinity);
-#endif
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Phytium GPIO Controller core");
diff --git a/drivers/gpio/gpio-phytium-core.h b/drivers/gpio/gpio-phytium-core.h
index 5a99e1fd6..bb3f5af26 100644
--- a/drivers/gpio/gpio-phytium-core.h
+++ b/drivers/gpio/gpio-phytium-core.h
@@ -32,6 +32,9 @@
#define NGPIO_DEFAULT 8
#define NGPIO_MAX 32
#define GPIO_PORT_STRIDE (GPIO_EXT_PORTB - GPIO_EXT_PORTA)
+#define GPIO_CLEAR_IRQ 0xffffffff
+
+#define PHYTIUM_GPIO_DRIVER_VERSION "1.1.2"
struct pin_loc {
unsigned int port;
@@ -54,6 +57,7 @@ struct phytium_gpio_ctx {
u32 raw_intstatus;
u32 ls_sync;
u32 debounce;
+ u32 wake_en;
};
#endif
@@ -61,9 +65,9 @@ struct phytium_gpio {
raw_spinlock_t lock;
void __iomem *regs;
struct gpio_chip gc;
- struct irq_chip irq_chip;
unsigned int ngpio[2];
int irq[32];
+ int is_resuming;
#ifdef CONFIG_PM_SLEEP
struct phytium_gpio_ctx ctx;
#endif
@@ -80,8 +84,10 @@ void phytium_gpio_irq_ack(struct irq_data *d);
void phytium_gpio_irq_mask(struct irq_data *d);
void phytium_gpio_irq_unmask(struct irq_data *d);
int phytium_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type);
+void phytium_gpio_irq_print_chip(struct irq_data *data, struct seq_file *p);
void phytium_gpio_irq_enable(struct irq_data *d);
void phytium_gpio_irq_disable(struct irq_data *d);
void phytium_gpio_irq_handler(struct irq_desc *desc);
+int phytium_gpio_irq_set_wake(struct irq_data *d, unsigned int enable);
int phytium_gpio_irq_set_affinity(struct irq_data *d, const struct cpumask *mask_val, bool force);
#endif
--
2.34.1