mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] gpio: rockchip: restore interrupt settings after hibernation
@ 2026-11-07  5:55 Igor Velkov via B4 Relay
  2026-11-07 14:38 ` Heiko Stuebner
  0 siblings, 1 reply; 2+ messages in thread
From: Igor Velkov via B4 Relay @ 2026-11-07  5:55 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski
  Cc: Heiko Stuebner, Jianqun Xu, linux-gpio, linux-arm-kernel,
	linux-rockchip, linux-kernel, Igor Velkov, stable

From: Igor Velkov <iav@iav.lv>

Restore from hibernation follows a cold boot. The boot kernel probes
the GPIO banks, sets every interrupt enable bit, leaves the trigger of
each line it did not request at the reset value, and then hands over
to the image. The image restores only the interrupt masks, so a line
it had set up for both edges comes back with the reset trigger.

On ODROID-M1 the headphone detect line (GPIO0_B0) is requested for
both edges and is low with nothing plugged in. After a power loss in
hybrid sleep its reset trigger fires again as soon as the edge handler
acknowledges it, and the system does not come back. With this patch
the board resumed 4 of 4 times, without it 0 of 5.

Save the interrupt enable, type, polarity and both-edge registers with
the masks and write them back before unmasking. In a normal suspend
the bank keeps its registers, so this writes back what is already
there. For lines that emulate both edges on GPIO v1, take the polarity
from the current pin level.

Fixes: 936ee2675eee ("gpio/rockchip: add driver for rockchip gpio")
Cc: stable@vger.kernel.org
Signed-off-by: Igor Velkov <iav@iav.lv>
---
Tested on ODROID-M1 (RK3568, GPIO v2): hybrid sleep with power removed
during sleep 4/4, deep sleep 10/10. The GPIO v1 path was not tested on
hardware.
---
 drivers/gpio/gpio-rockchip.c       | 21 +++++++++++++++++++++
 drivers/pinctrl/pinctrl-rockchip.h |  8 ++++++++
 2 files changed, 29 insertions(+)

diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c
index 0f46ff095a24..83a5cc8f3fe6 100644
--- a/drivers/gpio/gpio-rockchip.c
+++ b/drivers/gpio/gpio-rockchip.c
@@ -489,6 +489,11 @@ static void rockchip_irq_suspend(struct irq_data *d)
 	struct rockchip_pin_bank *bank = gc->private;
 
 	bank->saved_masks = irq_reg_readl(gc, bank->gpio_regs->int_mask);
+	bank->saved_en = rockchip_gpio_readl(bank, bank->gpio_regs->int_en);
+	bank->saved_type = rockchip_gpio_readl(bank, bank->gpio_regs->int_type);
+	bank->saved_polarity = rockchip_gpio_readl(bank, bank->gpio_regs->int_polarity);
+	if (bank->gpio_type == GPIO_TYPE_V2)
+		bank->saved_bothedge = rockchip_gpio_readl(bank, bank->gpio_regs->int_bothedge);
 	irq_reg_writel(gc, ~gc->wake_active, bank->gpio_regs->int_mask);
 }
 
@@ -496,7 +501,23 @@ static void rockchip_irq_resume(struct irq_data *d)
 {
 	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
 	struct rockchip_pin_bank *bank = gc->private;
+	u32 polarity = bank->saved_polarity;
+
+	/*
+	 * Restore from hibernation follows a cold boot: write the enables and
+	 * the trigger back before we unmask.
+	 */
+	if (bank->toggle_edge_mode) {
+		u32 data = readl(bank->reg_base + bank->gpio_regs->ext_port);
 
+		polarity &= ~bank->toggle_edge_mode;
+		polarity |= ~data & bank->toggle_edge_mode;
+	}
+	rockchip_gpio_writel(bank, bank->saved_en, bank->gpio_regs->int_en);
+	rockchip_gpio_writel(bank, bank->saved_type, bank->gpio_regs->int_type);
+	rockchip_gpio_writel(bank, polarity, bank->gpio_regs->int_polarity);
+	if (bank->gpio_type == GPIO_TYPE_V2)
+		rockchip_gpio_writel(bank, bank->saved_bothedge, bank->gpio_regs->int_bothedge);
 	irq_reg_writel(gc, bank->saved_masks, bank->gpio_regs->int_mask);
 }
 
diff --git a/drivers/pinctrl/pinctrl-rockchip.h b/drivers/pinctrl/pinctrl-rockchip.h
index 950097699643..ea280843fcb9 100644
--- a/drivers/pinctrl/pinctrl-rockchip.h
+++ b/drivers/pinctrl/pinctrl-rockchip.h
@@ -303,6 +303,10 @@ struct rockchip_drv {
  * @db_clk: clock of the gpio debounce
  * @irq: interrupt of the gpio bank
  * @saved_masks: Saved content of GPIO_INTEN at suspend time.
+ * @saved_en: interrupt enables, saved at suspend time.
+ * @saved_type: interrupt level/edge type, saved at suspend time.
+ * @saved_polarity: interrupt polarity, saved at suspend time.
+ * @saved_bothedge: interrupt both-edge enables (GPIO v2), saved at suspend time.
  * @pin_base: first pin number
  * @nr_pins: number of pins in this bank
  * @name: name of the bank
@@ -332,6 +336,10 @@ struct rockchip_pin_bank {
 	struct clk			*db_clk;
 	int				irq;
 	u32				saved_masks;
+	u32				saved_en;
+	u32				saved_type;
+	u32				saved_polarity;
+	u32				saved_bothedge;
 	u32				pin_base;
 	u8				nr_pins;
 	char				*name;

---
base-commit: 0fa38ba784896f2fc32c9dacd33822f844b21906
change-id: 20261107-b4-gpio-rockchip-hib-705c3fd52b49

Best regards,
-- 
Igor Velkov <iav@iav.lv>



^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-11-07 14:38 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-07  5:55 [PATCH] gpio: rockchip: restore interrupt settings after hibernation Igor Velkov via B4 Relay
2026-11-07 14:38 ` Heiko Stuebner

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®