* [PATCH v5 0/4] gpio: realtek: Add support for Realtek DHC RTD1625
@ 2026-07-02 9:01 Yu-Chun Lin
2026-07-02 9:01 ` [PATCH v5 1/4] dt-bindings: gpio: realtek: Add realtek,rtd1625-gpio Yu-Chun Lin
` (3 more replies)
0 siblings, 4 replies; 7+ messages in thread
From: Yu-Chun Lin @ 2026-07-02 9:01 UTC (permalink / raw)
To: linusw, brgl, robh, krzk+dt, conor+dt, afaerber, mwalle,
andriy.shevchenko, tychang
Cc: linux-gpio, devicetree, linux-kernel, linux-arm-kernel,
linux-realtek-soc, cy.huang, stanley_chang, eleanor.lin,
james.tai
This series adds GPIO support for the Realtek DHC RTD1625 SoC.
Unlike the existing driver (gpio-rtd.c) which uses shared bank registers,
the RTD1625 features a per-pin register architecture where each GPIO line
is managed by its own dedicated 32-bit control register. This distinct
hardware design requires a new, separate driver.
Best Regards,
Yu-Chun Lin
---
Changes in v5:
Patch 3 (driver):
- Add comment for @base_offset.
- Rename the local 'gpio_irq_chip' pointer to 'girq' to match common
GPIO IRQ naming.
- Drop the intermediate variable.
v4: https://lore.kernel.org/lkml/20260622092335.1166876-1-eleanor.lin@realtek.com/
v3: https://lore.kernel.org/lkml/20260512033317.1602537-1-eleanor.lin@realtek.com/
v2: https://lore.kernel.org/lkml/20260408025243.1155482-1-eleanor.lin@realtek.com/
v1: https://lore.kernel.org/lkml/20260331113835.3510341-1-eleanor.lin@realtek.com/
Tzuyi Chang (2):
dt-bindings: gpio: realtek: Add realtek,rtd1625-gpio
gpio: realtek: Add driver for Realtek DHC RTD1625 SoC
Yu-Chun Lin (2):
gpio: Replace "default y" with "default ARCH_REALTEK" in Kconfig
arm64: dts: realtek: Add GPIO support for RTD1625
.../bindings/gpio/realtek,rtd1625-gpio.yaml | 71 ++
arch/arm64/boot/dts/realtek/kent.dtsi | 39 ++
drivers/gpio/Kconfig | 14 +-
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-rtd1625.c | 610 ++++++++++++++++++
5 files changed, 734 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/gpio/realtek,rtd1625-gpio.yaml
create mode 100644 drivers/gpio/gpio-rtd1625.c
--
2.43.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v5 1/4] dt-bindings: gpio: realtek: Add realtek,rtd1625-gpio
2026-07-02 9:01 [PATCH v5 0/4] gpio: realtek: Add support for Realtek DHC RTD1625 Yu-Chun Lin
@ 2026-07-02 9:01 ` Yu-Chun Lin
2026-07-02 9:01 ` [PATCH v5 2/4] gpio: Replace "default y" with "default ARCH_REALTEK" in Kconfig Yu-Chun Lin
` (2 subsequent siblings)
3 siblings, 0 replies; 7+ messages in thread
From: Yu-Chun Lin @ 2026-07-02 9:01 UTC (permalink / raw)
To: linusw, brgl, robh, krzk+dt, conor+dt, afaerber, mwalle,
andriy.shevchenko, tychang
Cc: linux-gpio, devicetree, linux-kernel, linux-arm-kernel,
linux-realtek-soc, cy.huang, stanley_chang, eleanor.lin,
james.tai, Krzysztof Kozlowski
From: Tzuyi Chang <tychang@realtek.com>
Add the device tree bindings for the Realtek DHC (Digital Home Center)
RTD1625 GPIO controllers.
The RTD1625 GPIO controller features a per-pin register architecture
that differs significantly from previous generations. It utilizes
separate register blocks for GPIO configuration and interrupt control.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Tzuyi Chang <tychang@realtek.com>
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
---
Changes in v5:
- None.
---
.../bindings/gpio/realtek,rtd1625-gpio.yaml | 71 +++++++++++++++++++
1 file changed, 71 insertions(+)
create mode 100644 Documentation/devicetree/bindings/gpio/realtek,rtd1625-gpio.yaml
diff --git a/Documentation/devicetree/bindings/gpio/realtek,rtd1625-gpio.yaml b/Documentation/devicetree/bindings/gpio/realtek,rtd1625-gpio.yaml
new file mode 100644
index 000000000000..f13c910b73c6
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/realtek,rtd1625-gpio.yaml
@@ -0,0 +1,71 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# Copyright 2023 Realtek Semiconductor Corporation
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/gpio/realtek,rtd1625-gpio.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Realtek DHC RTD1625 GPIO controller
+
+maintainers:
+ - Tzuyi Chang <tychang@realtek.com>
+
+description: |
+ GPIO controller for the Realtek RTD1625 SoC, featuring a per-pin register
+ architecture that differs significantly from earlier RTD series controllers.
+ Each GPIO has dedicated registers for configuration (direction, input/output
+ values, debounce), and interrupt control supporting edge and level detection
+ modes.
+
+properties:
+ compatible:
+ enum:
+ - realtek,rtd1625-iso-gpio
+ - realtek,rtd1625-isom-gpio
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ items:
+ - description: Interrupt number of the assert GPIO interrupt, which is
+ triggered when there is a rising edge.
+ - description: Interrupt number of the deassert GPIO interrupt, which is
+ triggered when there is a falling edge.
+ - description: Interrupt number of the level-sensitive GPIO interrupt,
+ triggered by a configured logic level.
+
+ interrupt-controller: true
+
+ "#interrupt-cells":
+ const: 2
+
+ gpio-ranges: true
+
+ gpio-controller: true
+
+ "#gpio-cells":
+ const: 2
+
+required:
+ - compatible
+ - reg
+ - gpio-ranges
+ - gpio-controller
+ - "#gpio-cells"
+
+additionalProperties: false
+
+examples:
+ - |
+ gpio@89100 {
+ compatible = "realtek,rtd1625-isom-gpio";
+ reg = <0x89100 0x30>;
+ interrupt-parent = <&iso_m_irq_mux>;
+ interrupts = <0>, <1>, <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ gpio-ranges = <&isom_pinctrl 0 0 4>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v5 2/4] gpio: Replace "default y" with "default ARCH_REALTEK" in Kconfig
2026-07-02 9:01 [PATCH v5 0/4] gpio: realtek: Add support for Realtek DHC RTD1625 Yu-Chun Lin
2026-07-02 9:01 ` [PATCH v5 1/4] dt-bindings: gpio: realtek: Add realtek,rtd1625-gpio Yu-Chun Lin
@ 2026-07-02 9:01 ` Yu-Chun Lin
2026-07-02 9:01 ` [PATCH v5 3/4] gpio: realtek: Add driver for Realtek DHC RTD1625 SoC Yu-Chun Lin
2026-07-02 9:01 ` [PATCH v5 4/4] arm64: dts: realtek: Add GPIO support for RTD1625 Yu-Chun Lin
3 siblings, 0 replies; 7+ messages in thread
From: Yu-Chun Lin @ 2026-07-02 9:01 UTC (permalink / raw)
To: linusw, brgl, robh, krzk+dt, conor+dt, afaerber, mwalle,
andriy.shevchenko, tychang
Cc: linux-gpio, devicetree, linux-kernel, linux-arm-kernel,
linux-realtek-soc, cy.huang, stanley_chang, eleanor.lin,
james.tai
Replace "default y" with "default ARCH_REALTEK" to avoid bloating the build
for non-Realtek platforms when COMPILE_TEST is enabled on other platforms.
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
---
Changes in v5:
- None.
---
drivers/gpio/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 28cf6d2e83c2..ed2bc3113374 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -646,7 +646,7 @@ config GPIO_ROCKCHIP
config GPIO_RTD
tristate "Realtek DHC GPIO support"
depends on ARCH_REALTEK || COMPILE_TEST
- default y
+ default ARCH_REALTEK
select GPIOLIB_IRQCHIP
help
This option enables support for GPIOs found on Realtek DHC(Digital
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v5 3/4] gpio: realtek: Add driver for Realtek DHC RTD1625 SoC
2026-07-02 9:01 [PATCH v5 0/4] gpio: realtek: Add support for Realtek DHC RTD1625 Yu-Chun Lin
2026-07-02 9:01 ` [PATCH v5 1/4] dt-bindings: gpio: realtek: Add realtek,rtd1625-gpio Yu-Chun Lin
2026-07-02 9:01 ` [PATCH v5 2/4] gpio: Replace "default y" with "default ARCH_REALTEK" in Kconfig Yu-Chun Lin
@ 2026-07-02 9:01 ` Yu-Chun Lin
2026-07-02 9:16 ` sashiko-bot
2026-07-02 9:01 ` [PATCH v5 4/4] arm64: dts: realtek: Add GPIO support for RTD1625 Yu-Chun Lin
3 siblings, 1 reply; 7+ messages in thread
From: Yu-Chun Lin @ 2026-07-02 9:01 UTC (permalink / raw)
To: linusw, brgl, robh, krzk+dt, conor+dt, afaerber, mwalle,
andriy.shevchenko, tychang
Cc: linux-gpio, devicetree, linux-kernel, linux-arm-kernel,
linux-realtek-soc, cy.huang, stanley_chang, eleanor.lin,
james.tai
From: Tzuyi Chang <tychang@realtek.com>
Add support for the GPIO controller found on Realtek DHC RTD1625 SoCs.
Unlike the existing Realtek GPIO driver (drivers/gpio/gpio-rtd.c),
which manages pins via shared bank registers, the RTD1625 introduces
a per-pin register architecture. Each GPIO line now has its own
dedicated 32-bit control register to manage configuration independently,
including direction, output value, input value, interrupt enable, and
debounce. Therefore, this distinct hardware design requires a separate
driver.
Additionally, the RTD1625 GPIO controller has a specific hardware quirk:
it fires both 'assert' and 'de-assert' interrupts simultaneously on any
edge toggle. To handle this, we utilize the polarity register to route
the requested edge (rising/falling) to the 'assert' IRQ line. The driver
then filters out the unwanted 'de-assert' interrupt in the IRQ handler
and pre-clears edge interrupts to prevent interrupt storms caused by
unhandled dropped interrupts.
Interrupt support is optional for this device, matching the dt-bindings.
If the interrupts property is not provided, the driver simply skips IRQ
initialization and operates purely as a basic GPIO controller.
Reviewed-by: Linus Walleij <linusw@kernel.org>
Signed-off-by: Tzuyi Chang <tychang@realtek.com>
Co-developed-by: Yu-Chun Lin <eleanor.lin@realtek.com>
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
---
Changes in v5:
- Add comment for @base_offset.
- Rename the local 'gpio_irq_chip' pointer to 'girq' to match common
GPIO IRQ naming.
- Drop the intermediate variable.
---
drivers/gpio/Kconfig | 12 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-rtd1625.c | 610 ++++++++++++++++++++++++++++++++++++
3 files changed, 623 insertions(+)
create mode 100644 drivers/gpio/gpio-rtd1625.c
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index ed2bc3113374..f03c05288376 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -656,6 +656,18 @@ config GPIO_RTD
Say yes here to support GPIO functionality and GPIO interrupt on
Realtek DHC SoCs.
+config GPIO_RTD1625
+ tristate "Realtek DHC RTD1625 GPIO support"
+ depends on ARCH_REALTEK || COMPILE_TEST
+ default ARCH_REALTEK
+ select GPIOLIB_IRQCHIP
+ help
+ This option enables support for the GPIO controller on Realtek
+ DHC (Digital Home Center) RTD1625 SoC.
+
+ Say yes here to support both basic GPIO line functionality
+ and GPIO interrupt handling capabilities for this platform.
+
config GPIO_SAMA5D2_PIOBU
tristate "SAMA5D2 PIOBU GPIO support"
depends on OF
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 4d0e900402fc..fa14581e3995 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -160,6 +160,7 @@ obj-$(CONFIG_GPIO_REALTEK_OTTO) += gpio-realtek-otto.o
obj-$(CONFIG_GPIO_REG) += gpio-reg.o
obj-$(CONFIG_GPIO_ROCKCHIP) += gpio-rockchip.o
obj-$(CONFIG_GPIO_RTD) += gpio-rtd.o
+obj-$(CONFIG_GPIO_RTD1625) += gpio-rtd1625.o
obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o
obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o
diff --git a/drivers/gpio/gpio-rtd1625.c b/drivers/gpio/gpio-rtd1625.c
new file mode 100644
index 000000000000..27af1ac65d28
--- /dev/null
+++ b/drivers/gpio/gpio-rtd1625.c
@@ -0,0 +1,610 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Realtek DHC RTD1625 gpio driver
+ *
+ * Copyright (c) 2023-2026 Realtek Semiconductor Corp.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/cleanup.h>
+#include <linux/gpio/driver.h>
+#include <linux/interrupt.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#define RTD1625_GPIO_DIR BIT(0)
+#define RTD1625_GPIO_OUT BIT(2)
+#define RTD1625_GPIO_IN BIT(4)
+#define RTD1625_GPIO_EDGE_INT_DP BIT(6)
+#define RTD1625_GPIO_EDGE_INT_EN BIT(8)
+#define RTD1625_GPIO_LEVEL_INT_EN BIT(16)
+#define RTD1625_GPIO_LEVEL_INT_DP BIT(18)
+#define RTD1625_GPIO_DEBOUNCE GENMASK(30, 28)
+#define RTD1625_GPIO_DEBOUNCE_WREN BIT(31)
+
+#define RTD1625_GPIO_WREN(x) ((x) << 1)
+
+/* Write-enable masks for all GPIO configs and reserved hardware bits */
+#define RTD1625_ISO_GPIO_WREN_ALL 0x8000aa8a
+#define RTD1625_ISOM_GPIO_WREN_ALL 0x800aaa8a
+
+#define RTD1625_GPIO_DEBOUNCE_1US 0
+#define RTD1625_GPIO_DEBOUNCE_10US 1
+#define RTD1625_GPIO_DEBOUNCE_100US 2
+#define RTD1625_GPIO_DEBOUNCE_1MS 3
+#define RTD1625_GPIO_DEBOUNCE_10MS 4
+#define RTD1625_GPIO_DEBOUNCE_20MS 5
+#define RTD1625_GPIO_DEBOUNCE_30MS 6
+#define RTD1625_GPIO_DEBOUNCE_50MS 7
+
+#define GPIO_CONTROL(gpio) ((gpio) * 4)
+
+enum rtd1625_irq_index {
+ RTD1625_IRQ_ASSERT,
+ RTD1625_IRQ_DEASSERT,
+ RTD1625_IRQ_LEVEL,
+ RTD1625_MAX_IRQS
+};
+
+/**
+ * struct rtd1625_gpio_info - Specific GPIO register information
+ * @num_gpios: The number of GPIOs
+ * @irq_type_support: Supported IRQ types
+ * @base_offset: Offset for GPIO controller register
+ * @gpa_offset: Offset for GPIO assert interrupt status registers
+ * @gpda_offset: Offset for GPIO deassert interrupt status registers
+ * @level_offset: Offset of level interrupt status register
+ * @write_en_all: Write-enable mask for all configurable bits
+ */
+struct rtd1625_gpio_info {
+ unsigned int num_gpios;
+ unsigned int irq_type_support;
+ unsigned int base_offset;
+ unsigned int gpa_offset;
+ unsigned int gpda_offset;
+ unsigned int level_offset;
+ unsigned int write_en_all;
+};
+
+struct rtd1625_gpio {
+ struct gpio_chip gpio_chip;
+ const struct rtd1625_gpio_info *info;
+ void __iomem *base;
+ void __iomem *irq_base;
+ unsigned int irqs[RTD1625_MAX_IRQS];
+ raw_spinlock_t lock;
+ unsigned int *save_regs;
+};
+
+static unsigned int rtd1625_gpio_gpa_offset(struct rtd1625_gpio *data, unsigned int offset)
+{
+ return data->info->gpa_offset + ((offset / 32) * 4);
+}
+
+static unsigned int rtd1625_gpio_gpda_offset(struct rtd1625_gpio *data, unsigned int offset)
+{
+ return data->info->gpda_offset + ((offset / 32) * 4);
+}
+
+static unsigned int rtd1625_gpio_level_offset(struct rtd1625_gpio *data, unsigned int offset)
+{
+ return data->info->level_offset + ((offset / 32) * 4);
+}
+
+static int rtd1625_gpio_set_debounce(struct gpio_chip *chip, unsigned int offset,
+ unsigned int debounce)
+{
+ struct rtd1625_gpio *data = gpiochip_get_data(chip);
+ u8 deb_val;
+ u32 val;
+
+ switch (debounce) {
+ case 1:
+ deb_val = RTD1625_GPIO_DEBOUNCE_1US;
+ break;
+ case 10:
+ deb_val = RTD1625_GPIO_DEBOUNCE_10US;
+ break;
+ case 100:
+ deb_val = RTD1625_GPIO_DEBOUNCE_100US;
+ break;
+ case 1000:
+ deb_val = RTD1625_GPIO_DEBOUNCE_1MS;
+ break;
+ case 10000:
+ deb_val = RTD1625_GPIO_DEBOUNCE_10MS;
+ break;
+ case 20000:
+ deb_val = RTD1625_GPIO_DEBOUNCE_20MS;
+ break;
+ case 30000:
+ deb_val = RTD1625_GPIO_DEBOUNCE_30MS;
+ break;
+ case 50000:
+ deb_val = RTD1625_GPIO_DEBOUNCE_50MS;
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ val = FIELD_PREP(RTD1625_GPIO_DEBOUNCE, deb_val) | RTD1625_GPIO_DEBOUNCE_WREN;
+
+ guard(raw_spinlock_irqsave)(&data->lock);
+
+ writel_relaxed(val, data->base + GPIO_CONTROL(offset));
+
+ return 0;
+}
+
+static int rtd1625_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
+ unsigned long config)
+{
+ u32 debounce;
+
+ if (pinconf_to_config_param(config) == PIN_CONFIG_INPUT_DEBOUNCE) {
+ debounce = pinconf_to_config_argument(config);
+ return rtd1625_gpio_set_debounce(chip, offset, debounce);
+ }
+
+ return gpiochip_generic_config(chip, offset, config);
+}
+
+static int rtd1625_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
+{
+ struct rtd1625_gpio *data = gpiochip_get_data(chip);
+ u32 val = RTD1625_GPIO_WREN(RTD1625_GPIO_OUT);
+
+ if (value)
+ val |= RTD1625_GPIO_OUT;
+
+ guard(raw_spinlock_irqsave)(&data->lock);
+
+ writel_relaxed(val, data->base + GPIO_CONTROL(offset));
+
+ return 0;
+}
+
+static int rtd1625_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct rtd1625_gpio *data = gpiochip_get_data(chip);
+ u32 val;
+
+ guard(raw_spinlock_irqsave)(&data->lock);
+
+ val = readl_relaxed(data->base + GPIO_CONTROL(offset));
+
+ if (val & RTD1625_GPIO_DIR)
+ return !!(val & RTD1625_GPIO_OUT);
+ else
+ return !!(val & RTD1625_GPIO_IN);
+}
+
+static int rtd1625_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
+{
+ struct rtd1625_gpio *data = gpiochip_get_data(chip);
+ u32 val;
+
+ guard(raw_spinlock_irqsave)(&data->lock);
+
+ val = readl_relaxed(data->base + GPIO_CONTROL(offset));
+
+ if (val & RTD1625_GPIO_DIR)
+ return GPIO_LINE_DIRECTION_OUT;
+
+ return GPIO_LINE_DIRECTION_IN;
+}
+
+static int rtd1625_gpio_set_direction(struct gpio_chip *chip, unsigned int offset, bool out)
+{
+ struct rtd1625_gpio *data = gpiochip_get_data(chip);
+ u32 val = RTD1625_GPIO_WREN(RTD1625_GPIO_DIR);
+
+ if (out)
+ val |= RTD1625_GPIO_DIR;
+
+ guard(raw_spinlock_irqsave)(&data->lock);
+
+ writel_relaxed(val, data->base + GPIO_CONTROL(offset));
+
+ return 0;
+}
+
+static int rtd1625_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
+{
+ return rtd1625_gpio_set_direction(chip, offset, false);
+}
+
+static int rtd1625_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value)
+{
+ rtd1625_gpio_set(chip, offset, value);
+
+ return rtd1625_gpio_set_direction(chip, offset, true);
+}
+
+static void rtd1625_gpio_irq_handle(struct irq_desc *desc)
+{
+ unsigned int (*get_reg_offset)(struct rtd1625_gpio *gpio, unsigned int offset);
+ struct rtd1625_gpio *data = irq_desc_get_handler_data(desc);
+ struct irq_domain *domain = data->gpio_chip.irq.domain;
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ unsigned int irq = irq_desc_get_irq(desc);
+ unsigned long status;
+ unsigned int reg_offset, i, j;
+ irq_hw_number_t hwirq;
+ u32 irq_type;
+
+ if (irq == data->irqs[RTD1625_IRQ_ASSERT])
+ get_reg_offset = &rtd1625_gpio_gpa_offset;
+ else if (irq == data->irqs[RTD1625_IRQ_DEASSERT])
+ get_reg_offset = &rtd1625_gpio_gpda_offset;
+ else if (irq == data->irqs[2])
+ get_reg_offset = &rtd1625_gpio_level_offset;
+ else
+ return;
+
+ chained_irq_enter(chip, desc);
+
+ for (i = 0; i < data->info->num_gpios; i += 32) {
+ reg_offset = get_reg_offset(data, i);
+ status = readl_relaxed(data->irq_base + reg_offset);
+
+ /*
+ * Hardware quirk: The controller fires both "assert" and "de-assert"
+ * interrupts simultaneously on any edge toggle.
+ * We must pre-clear edge interrupts here. If we drop an unwanted
+ * de-assert interrupt below, it will never reach the IRQ core
+ * (generic_handle_domain_irq), meaning ->irq_ack() won't be called.
+ * Failing to clear it here leads to an interrupt storm.
+ */
+ if (irq != data->irqs[RTD1625_IRQ_LEVEL])
+ writel_relaxed(status, data->irq_base + reg_offset);
+
+ for_each_set_bit(j, &status, 32) {
+ hwirq = i + j;
+ irq_type = irq_get_trigger_type(irq_find_mapping(domain, hwirq));
+
+ /*
+ * Filter out the hardware-forced de-assert interrupt unless
+ * the user explicitly requested IRQ_TYPE_EDGE_BOTH.
+ */
+ if (irq == data->irqs[RTD1625_IRQ_DEASSERT] &&
+ irq_type != IRQ_TYPE_EDGE_BOTH)
+ continue;
+
+ generic_handle_domain_irq(domain, hwirq);
+ }
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+static void rtd1625_gpio_ack_irq(struct irq_data *d)
+{
+ struct rtd1625_gpio *data = irq_data_get_irq_chip_data(d);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+ u32 irq_type = irqd_get_trigger_type(d);
+ u32 bit_mask = BIT(hwirq % 32);
+ int reg_offset;
+
+ if (irq_type & IRQ_TYPE_LEVEL_MASK) {
+ reg_offset = rtd1625_gpio_level_offset(data, hwirq);
+ writel_relaxed(bit_mask, data->irq_base + reg_offset);
+ }
+}
+
+static void rtd1625_gpio_enable_edge_irq(struct rtd1625_gpio *data, irq_hw_number_t hwirq)
+{
+ int gpda_reg_offset = rtd1625_gpio_gpda_offset(data, hwirq);
+ int gpa_reg_offset = rtd1625_gpio_gpa_offset(data, hwirq);
+ u32 clr_mask = BIT(hwirq % 32);
+ u32 val;
+
+ guard(raw_spinlock_irqsave)(&data->lock);
+
+ writel_relaxed(clr_mask, data->irq_base + gpa_reg_offset);
+ writel_relaxed(clr_mask, data->irq_base + gpda_reg_offset);
+ val = RTD1625_GPIO_EDGE_INT_EN | RTD1625_GPIO_WREN(RTD1625_GPIO_EDGE_INT_EN);
+ writel_relaxed(val, data->base + GPIO_CONTROL(hwirq));
+}
+
+static void rtd1625_gpio_disable_edge_irq(struct rtd1625_gpio *data, irq_hw_number_t hwirq)
+{
+ u32 val;
+
+ guard(raw_spinlock_irqsave)(&data->lock);
+
+ val = RTD1625_GPIO_WREN(RTD1625_GPIO_EDGE_INT_EN);
+ writel_relaxed(val, data->base + GPIO_CONTROL(hwirq));
+}
+
+static void rtd1625_gpio_enable_level_irq(struct rtd1625_gpio *data, irq_hw_number_t hwirq)
+{
+ int level_reg_offset = rtd1625_gpio_level_offset(data, hwirq);
+ u32 clr_mask = BIT(hwirq % 32);
+ u32 val;
+
+ guard(raw_spinlock_irqsave)(&data->lock);
+
+ writel_relaxed(clr_mask, data->irq_base + level_reg_offset);
+ val = RTD1625_GPIO_LEVEL_INT_EN | RTD1625_GPIO_WREN(RTD1625_GPIO_LEVEL_INT_EN);
+ writel_relaxed(val, data->base + GPIO_CONTROL(hwirq));
+}
+
+static void rtd1625_gpio_disable_level_irq(struct rtd1625_gpio *data, irq_hw_number_t hwirq)
+{
+ u32 val;
+
+ guard(raw_spinlock_irqsave)(&data->lock);
+
+ val = RTD1625_GPIO_WREN(RTD1625_GPIO_LEVEL_INT_EN);
+ writel_relaxed(val, data->base + GPIO_CONTROL(hwirq));
+}
+
+static void rtd1625_gpio_enable_irq(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct rtd1625_gpio *data = gpiochip_get_data(gc);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+ u32 irq_type = irqd_get_trigger_type(d);
+
+ gpiochip_enable_irq(gc, hwirq);
+
+ if (irq_type & IRQ_TYPE_EDGE_BOTH)
+ rtd1625_gpio_enable_edge_irq(data, hwirq);
+ else if (irq_type & IRQ_TYPE_LEVEL_MASK)
+ rtd1625_gpio_enable_level_irq(data, hwirq);
+}
+
+static void rtd1625_gpio_disable_irq(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct rtd1625_gpio *data = gpiochip_get_data(gc);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+ u32 irq_type = irqd_get_trigger_type(d);
+
+ if (irq_type & IRQ_TYPE_EDGE_BOTH)
+ rtd1625_gpio_disable_edge_irq(data, hwirq);
+ else if (irq_type & IRQ_TYPE_LEVEL_MASK)
+ rtd1625_gpio_disable_level_irq(data, hwirq);
+
+ gpiochip_disable_irq(gc, hwirq);
+}
+
+static int rtd1625_gpio_irq_set_level_type(struct irq_data *d, bool level)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct rtd1625_gpio *data = gpiochip_get_data(gc);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+ u32 val = RTD1625_GPIO_WREN(RTD1625_GPIO_LEVEL_INT_DP);
+
+ if (!(data->info->irq_type_support & IRQ_TYPE_LEVEL_MASK))
+ return -EINVAL;
+
+ if (level)
+ val |= RTD1625_GPIO_LEVEL_INT_DP;
+
+ scoped_guard(raw_spinlock_irqsave, &data->lock)
+ writel_relaxed(val, data->base + GPIO_CONTROL(hwirq));
+
+ irq_set_handler_locked(d, handle_level_irq);
+
+ return 0;
+}
+
+static int rtd1625_gpio_irq_set_edge_type(struct irq_data *d, bool polarity)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct rtd1625_gpio *data = gpiochip_get_data(gc);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+ u32 val = RTD1625_GPIO_WREN(RTD1625_GPIO_EDGE_INT_DP);
+
+ if (!(data->info->irq_type_support & IRQ_TYPE_EDGE_BOTH))
+ return -EINVAL;
+
+ if (polarity)
+ val |= RTD1625_GPIO_EDGE_INT_DP;
+
+ scoped_guard(raw_spinlock_irqsave, &data->lock)
+ writel_relaxed(val, data->base + GPIO_CONTROL(hwirq));
+
+ irq_set_handler_locked(d, handle_edge_irq);
+
+ return 0;
+}
+
+static int rtd1625_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ switch (type & IRQ_TYPE_SENSE_MASK) {
+ case IRQ_TYPE_EDGE_RISING:
+ return rtd1625_gpio_irq_set_edge_type(d, 1);
+
+ case IRQ_TYPE_EDGE_FALLING:
+ return rtd1625_gpio_irq_set_edge_type(d, 0);
+
+ case IRQ_TYPE_EDGE_BOTH:
+ return rtd1625_gpio_irq_set_edge_type(d, 1);
+
+ case IRQ_TYPE_LEVEL_HIGH:
+ return rtd1625_gpio_irq_set_level_type(d, 0);
+
+ case IRQ_TYPE_LEVEL_LOW:
+ return rtd1625_gpio_irq_set_level_type(d, 1);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static struct irq_chip rtd1625_iso_gpio_irq_chip = {
+ .name = "rtd1625-gpio",
+ .irq_ack = rtd1625_gpio_ack_irq,
+ .irq_mask = rtd1625_gpio_disable_irq,
+ .irq_unmask = rtd1625_gpio_enable_irq,
+ .irq_set_type = rtd1625_gpio_irq_set_type,
+ .flags = IRQCHIP_IMMUTABLE | IRQCHIP_SKIP_SET_WAKE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
+};
+
+static int rtd1625_gpio_setup_irq(struct platform_device *pdev, struct rtd1625_gpio *data)
+{
+ struct gpio_irq_chip *girq;
+ unsigned int num_irqs;
+ int irq;
+
+ /*
+ * Interrupt support is optional. All IRQs must be provided together.
+ * If index 0 is missing, we assume no interrupts are configured in DT
+ * and fall back to basic GPIO operation.
+ */
+ irq = platform_get_irq_optional(pdev, 0);
+ if (irq == -ENXIO)
+ return 0;
+ if (irq < 0)
+ return irq;
+
+ num_irqs = (data->info->irq_type_support & IRQ_TYPE_LEVEL_MASK) ? 3 : 2;
+ data->irqs[RTD1625_IRQ_ASSERT] = irq;
+
+ for (unsigned int i = 1; i < num_irqs; i++) {
+ irq = platform_get_irq(pdev, i);
+ if (irq < 0)
+ return irq;
+ data->irqs[i] = irq;
+ }
+
+ girq = &data->gpio_chip.irq;
+ girq->handler = handle_bad_irq;
+ girq->default_type = IRQ_TYPE_NONE;
+ girq->parent_handler = rtd1625_gpio_irq_handle;
+ girq->parent_handler_data = data;
+ girq->num_parents = num_irqs;
+ girq->parents = data->irqs;
+
+ gpio_irq_chip_set_chip(girq, &rtd1625_iso_gpio_irq_chip);
+
+ return 0;
+}
+
+static int rtd1625_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rtd1625_gpio *data;
+ void __iomem *irq_base;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->info = device_get_match_data(dev);
+ if (!data->info)
+ return -EINVAL;
+
+ raw_spin_lock_init(&data->lock);
+
+ irq_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(irq_base))
+ return PTR_ERR(irq_base);
+
+ data->irq_base = irq_base;
+ data->base = irq_base + data->info->base_offset;
+
+ data->save_regs = devm_kcalloc(dev, data->info->num_gpios, sizeof(*data->save_regs),
+ GFP_KERNEL);
+ if (!data->save_regs)
+ return -ENOMEM;
+
+ data->gpio_chip.label = dev_name(dev);
+ data->gpio_chip.base = -1;
+ data->gpio_chip.ngpio = data->info->num_gpios;
+ data->gpio_chip.request = gpiochip_generic_request;
+ data->gpio_chip.free = gpiochip_generic_free;
+ data->gpio_chip.get_direction = rtd1625_gpio_get_direction;
+ data->gpio_chip.direction_input = rtd1625_gpio_direction_input;
+ data->gpio_chip.direction_output = rtd1625_gpio_direction_output;
+ data->gpio_chip.set = rtd1625_gpio_set;
+ data->gpio_chip.get = rtd1625_gpio_get;
+ data->gpio_chip.set_config = rtd1625_gpio_set_config;
+ data->gpio_chip.parent = dev;
+
+ ret = rtd1625_gpio_setup_irq(pdev, data);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, data);
+
+ return devm_gpiochip_add_data(dev, &data->gpio_chip, data);
+}
+
+static const struct rtd1625_gpio_info rtd1625_iso_gpio_info = {
+ .num_gpios = 166,
+ .irq_type_support = IRQ_TYPE_EDGE_BOTH,
+ .base_offset = 0x100,
+ .gpa_offset = 0x000,
+ .gpda_offset = 0x020,
+ .write_en_all = RTD1625_ISO_GPIO_WREN_ALL,
+};
+
+static const struct rtd1625_gpio_info rtd1625_isom_gpio_info = {
+ .num_gpios = 4,
+ .irq_type_support = IRQ_TYPE_EDGE_BOTH | IRQ_TYPE_LEVEL_LOW |
+ IRQ_TYPE_LEVEL_HIGH,
+ .base_offset = 0x20,
+ .gpa_offset = 0x00,
+ .gpda_offset = 0x04,
+ .level_offset = 0x18,
+ .write_en_all = RTD1625_ISOM_GPIO_WREN_ALL,
+};
+
+static int rtd1625_gpio_suspend(struct device *dev)
+{
+ struct rtd1625_gpio *data = dev_get_drvdata(dev);
+ const struct rtd1625_gpio_info *info = data->info;
+
+ for (unsigned int i = 0; i < info->num_gpios; i++)
+ data->save_regs[i] = readl_relaxed(data->base + GPIO_CONTROL(i));
+
+ return 0;
+}
+
+static int rtd1625_gpio_resume(struct device *dev)
+{
+ struct rtd1625_gpio *data = dev_get_drvdata(dev);
+ const struct rtd1625_gpio_info *info = data->info;
+
+ for (unsigned int i = 0; i < info->num_gpios; i++)
+ writel_relaxed(data->save_regs[i] | info->write_en_all,
+ data->base + GPIO_CONTROL(i));
+
+ return 0;
+}
+
+static DEFINE_NOIRQ_DEV_PM_OPS(rtd1625_gpio_pm_ops, rtd1625_gpio_suspend, rtd1625_gpio_resume);
+
+static const struct of_device_id rtd1625_gpio_of_matches[] = {
+ { .compatible = "realtek,rtd1625-iso-gpio", .data = &rtd1625_iso_gpio_info },
+ { .compatible = "realtek,rtd1625-isom-gpio", .data = &rtd1625_isom_gpio_info },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rtd1625_gpio_of_matches);
+
+static struct platform_driver rtd1625_gpio_platform_driver = {
+ .driver = {
+ .name = "gpio-rtd1625",
+ .of_match_table = rtd1625_gpio_of_matches,
+ .pm = pm_sleep_ptr(&rtd1625_gpio_pm_ops),
+ },
+ .probe = rtd1625_gpio_probe,
+};
+module_platform_driver(rtd1625_gpio_platform_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Realtek Semiconductor Corporation");
+MODULE_DESCRIPTION("Realtek DHC SoC RTD1625 gpio driver");
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v5 4/4] arm64: dts: realtek: Add GPIO support for RTD1625
2026-07-02 9:01 [PATCH v5 0/4] gpio: realtek: Add support for Realtek DHC RTD1625 Yu-Chun Lin
` (2 preceding siblings ...)
2026-07-02 9:01 ` [PATCH v5 3/4] gpio: realtek: Add driver for Realtek DHC RTD1625 SoC Yu-Chun Lin
@ 2026-07-02 9:01 ` Yu-Chun Lin
2026-07-02 9:07 ` sashiko-bot
3 siblings, 1 reply; 7+ messages in thread
From: Yu-Chun Lin @ 2026-07-02 9:01 UTC (permalink / raw)
To: linusw, brgl, robh, krzk+dt, conor+dt, afaerber, mwalle,
andriy.shevchenko, tychang
Cc: linux-gpio, devicetree, linux-kernel, linux-arm-kernel,
linux-realtek-soc, cy.huang, stanley_chang, eleanor.lin,
james.tai, Bartosz Golaszewski
Add the GPIO node for the Realtek RTD1625 SoC.
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Yu-Chun Lin <eleanor.lin@realtek.com>
---
Changes in v5:
- None.
---
arch/arm64/boot/dts/realtek/kent.dtsi | 39 +++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/arch/arm64/boot/dts/realtek/kent.dtsi b/arch/arm64/boot/dts/realtek/kent.dtsi
index 8d4293cd4c03..228b82dfdb7a 100644
--- a/arch/arm64/boot/dts/realtek/kent.dtsi
+++ b/arch/arm64/boot/dts/realtek/kent.dtsi
@@ -151,6 +151,37 @@ uart0: serial@7800 {
status = "disabled";
};
+ gpio: gpio@31000 {
+ compatible = "realtek,rtd1625-iso-gpio";
+ reg = <0x31000 0x398>;
+ gpio-controller;
+ gpio-ranges = <&isom_pinctrl 0 0 2>,
+ <&ve4_pinctrl 2 0 6>,
+ <&iso_pinctrl 8 0 4>,
+ <&ve4_pinctrl 12 6 2>,
+ <&main2_pinctrl 14 0 2>,
+ <&ve4_pinctrl 16 8 4>,
+ <&main2_pinctrl 20 2 3>,
+ <&ve4_pinctrl 23 12 3>,
+ <&iso_pinctrl 26 4 2>,
+ <&isom_pinctrl 28 2 2>,
+ <&ve4_pinctrl 30 15 6>,
+ <&main2_pinctrl 36 5 6>,
+ <&ve4_pinctrl 42 21 3>,
+ <&iso_pinctrl 45 6 6>,
+ <&ve4_pinctrl 51 24 1>,
+ <&iso_pinctrl 52 12 1>,
+ <&ve4_pinctrl 53 25 11>,
+ <&main2_pinctrl 64 11 28>,
+ <&ve4_pinctrl 92 36 2>,
+ <&iso_pinctrl 94 13 19>,
+ <&iso_pinctrl 128 32 4>,
+ <&ve4_pinctrl 132 38 13>,
+ <&iso_pinctrl 145 36 19>,
+ <&ve4_pinctrl 164 51 2>;
+ #gpio-cells = <2>;
+ };
+
iso_pinctrl: pinctrl@4e000 {
compatible = "realtek,rtd1625-iso-pinctrl";
reg = <0x4e000 0x1a4>;
@@ -161,6 +192,14 @@ main2_pinctrl: pinctrl@4f200 {
reg = <0x4f200 0x50>;
};
+ iso_m_gpio: gpio@89100 {
+ compatible = "realtek,rtd1625-isom-gpio";
+ reg = <0x89100 0x30>;
+ gpio-controller;
+ gpio-ranges = <&isom_pinctrl 0 0 4>;
+ #gpio-cells = <2>;
+ };
+
isom_pinctrl: pinctrl@146200 {
compatible = "realtek,rtd1625-isom-pinctrl";
reg = <0x146200 0x34>;
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v5 4/4] arm64: dts: realtek: Add GPIO support for RTD1625
2026-07-02 9:01 ` [PATCH v5 4/4] arm64: dts: realtek: Add GPIO support for RTD1625 Yu-Chun Lin
@ 2026-07-02 9:07 ` sashiko-bot
0 siblings, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-07-02 9:07 UTC (permalink / raw)
To: Yu-Chun Lin; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Device Tree mapping conflict where two distinct GPIO controllers claim the exact same pins from the `isom_pinctrl` controller.
--
commit ca40dcb33ea07a0408e101ec08ff651fed48479b
Author: Yu-Chun Lin <eleanor.lin@realtek.com>
arm64: dts: realtek: Add GPIO support for RTD1625
This commit adds the GPIO nodes for the Realtek RTD1625 SoC.
It configures multiple GPIO controllers and establishes their
pin controller mapping ranges.
> diff --git a/arch/arm64/boot/dts/realtek/kent.dtsi b/arch/arm64/boot/dts/realtek/kent.dtsi
> index 8d4293cd4c036..228b82dfdb7ac 100644
> --- a/arch/arm64/boot/dts/realtek/kent.dtsi
> +++ b/arch/arm64/boot/dts/realtek/kent.dtsi
> @@ -151,6 +151,37 @@ uart0: serial@7800 {
> status = "disabled";
> };
>
> + gpio: gpio@31000 {
> + compatible = "realtek,rtd1625-iso-gpio";
> + reg = <0x31000 0x398>;
> + gpio-controller;
> + gpio-ranges = <&isom_pinctrl 0 0 2>,
> + <&ve4_pinctrl 2 0 6>,
> + <&iso_pinctrl 8 0 4>,
[ ... ]
> + <&iso_pinctrl 26 4 2>,
> + <&isom_pinctrl 28 2 2>,
[ ... ]
> + iso_m_gpio: gpio@89100 {
> + compatible = "realtek,rtd1625-isom-gpio";
> + reg = <0x89100 0x30>;
> + gpio-controller;
> + gpio-ranges = <&isom_pinctrl 0 0 4>;
[Severity: Medium]
Is it intended for both the gpio@31000 and iso_m_gpio controllers to claim
the exact same pins from the isom_pinctrl controller?
Looking at gpio@31000, it maps pins 0-1 and 2-3 from isom_pinctrl. Down here
in iso_m_gpio, the gpio-ranges property identically maps isom_pinctrl pins 0,
1, 2, and 3.
Could this cause unexpected behavior in the pinctrl subsystem? Functions like
pinctrl_find_gpio_range_from_pin() strictly return the first matched range,
which might silently shadow the other controller's reverse lookups. Also, if
both controllers attempt to request the same pin via pinctrl_gpio_request(),
would the second request fail because the pin will be marked as already in-use?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260702090115.2564318-1-eleanor.lin@realtek.com?part=4
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v5 3/4] gpio: realtek: Add driver for Realtek DHC RTD1625 SoC
2026-07-02 9:01 ` [PATCH v5 3/4] gpio: realtek: Add driver for Realtek DHC RTD1625 SoC Yu-Chun Lin
@ 2026-07-02 9:16 ` sashiko-bot
0 siblings, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-07-02 9:16 UTC (permalink / raw)
To: Yu-Chun Lin; +Cc: robh, conor+dt, devicetree
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Incorrect implementation of IRQ_TYPE_EDGE_BOTH causes double-dispatch on rising edges and missed falling edges.
- [High] Missing bounds check on computed hwirq causes out-of-bounds processing and infinite interrupt storms.
- [High] Missing interrupt enable sanitization and flawed mask/ack logic causes kernel hangs from bootloader-enabled interrupts.
- [Medium] The driver returns -ENOTSUPP for a debounce value of 0, breaking standard API requests to disable debouncing.
--
commit 70be814576adfb9c031580980614a470a5f6afe1
Author: Tzuyi Chang <tychang@realtek.com>
Subject: gpio: realtek: Add driver for Realtek DHC RTD1625 SoC
This commit introduces a new GPIO driver for the Realtek DHC RTD1625 SoC,
which uses a per-pin register architecture. It supports basic GPIO operations
and optional interrupt handling, addressing a hardware quirk where both
assert and de-assert interrupts fire simultaneously on edge toggles.
> diff --git a/drivers/gpio/gpio-rtd1625.c b/drivers/gpio/gpio-rtd1625.c
> new file mode 100644
> index 0000000000000..27af1ac65d281
> --- /dev/null
> +++ b/drivers/gpio/gpio-rtd1625.c
[ ... ]
> +static int rtd1625_gpio_set_debounce(struct gpio_chip *chip, unsigned int offset,
> + unsigned int debounce)
> +{
> + struct rtd1625_gpio *data = gpiochip_get_data(chip);
> + u8 deb_val;
> + u32 val;
> +
> + switch (debounce) {
> + case 1:
> + deb_val = RTD1625_GPIO_DEBOUNCE_1US;
> + break;
[ ... ]
> + case 50000:
> + deb_val = RTD1625_GPIO_DEBOUNCE_50MS;
> + break;
> + default:
> + return -ENOTSUPP;
> + }
[Severity: Medium]
Will this return -ENOTSUPP if callers request a debounce value of 0?
Standard kernel callers, such as gpiolib or the input subsystem, typically
pass a debounce value of 0 to disable GPIO debouncing. By not handling
debounce == 0, does this break standard API requests to disable debouncing?
[ ... ]
> +static void rtd1625_gpio_irq_handle(struct irq_desc *desc)
> +{
[ ... ]
> + for (i = 0; i < data->info->num_gpios; i += 32) {
> + reg_offset = get_reg_offset(data, i);
> + status = readl_relaxed(data->irq_base + reg_offset);
> +
> + /*
> + * Hardware quirk: The controller fires both "assert" and "de-assert"
> + * interrupts simultaneously on any edge toggle.
> + * We must pre-clear edge interrupts here. If we drop an unwanted
> + * de-assert interrupt below, it will never reach the IRQ core
> + * (generic_handle_domain_irq), meaning ->irq_ack() won't be called.
> + * Failing to clear it here leads to an interrupt storm.
> + */
> + if (irq != data->irqs[RTD1625_IRQ_LEVEL])
> + writel_relaxed(status, data->irq_base + reg_offset);
> +
> + for_each_set_bit(j, &status, 32) {
> + hwirq = i + j;
> + irq_type = irq_get_trigger_type(irq_find_mapping(domain, hwirq));
[Severity: High]
Is there a missing bounds check on the computed hwirq against num_gpios?
If num_gpios is not a multiple of 32 (like 166 or 4), the final iteration
could include unused or reserved hardware bits. If any of these bits read
as 1, hwirq will exceed num_gpios. Calling generic_handle_domain_irq() with
an unmapped hwirq returns -EINVAL.
For level interrupts where the status is not pre-cleared, since the unmapped
IRQ is rejected, irq_ack() would never be invoked. Could this leave the level
interrupt unacknowledged in hardware and cause an infinite interrupt storm?
> +
> + /*
> + * Filter out the hardware-forced de-assert interrupt unless
> + * the user explicitly requested IRQ_TYPE_EDGE_BOTH.
> + */
> + if (irq == data->irqs[RTD1625_IRQ_DEASSERT] &&
> + irq_type != IRQ_TYPE_EDGE_BOTH)
> + continue;
> +
> + generic_handle_domain_irq(domain, hwirq);
> + }
> + }
> +
> + chained_irq_exit(chip, desc);
> +}
> +
> +static void rtd1625_gpio_ack_irq(struct irq_data *d)
> +{
> + struct rtd1625_gpio *data = irq_data_get_irq_chip_data(d);
> + irq_hw_number_t hwirq = irqd_to_hwirq(d);
> + u32 irq_type = irqd_get_trigger_type(d);
> + u32 bit_mask = BIT(hwirq % 32);
> + int reg_offset;
> +
> + if (irq_type & IRQ_TYPE_LEVEL_MASK) {
> + reg_offset = rtd1625_gpio_level_offset(data, hwirq);
> + writel_relaxed(bit_mask, data->irq_base + reg_offset);
> + }
> +}
[Severity: High]
Does this correctly handle unrequested interrupts, where irq_type defaults
to 0 (IRQ_TYPE_NONE)?
If a bootloader leaves a GPIO interrupt active, it can fire as soon as the OS
registers the IRQ domain. Because the interrupt isn't requested, irq_type is 0.
In rtd1625_gpio_ack_irq(), the code only acks if the type matches
IRQ_TYPE_LEVEL_MASK.
Similarly, in rtd1625_gpio_disable_irq(), it fails to mask the interrupt if
the type is 0:
if (irq_type & IRQ_TYPE_EDGE_BOTH)
rtd1625_gpio_disable_edge_irq(data, hwirq);
else if (irq_type & IRQ_TYPE_LEVEL_MASK)
rtd1625_gpio_disable_level_irq(data, hwirq);
Could this cause the driver to do nothing to clear or mask the interrupt,
defeating the core IRQ subsystem's spurious interrupt handling
(handle_bad_irq) and causing a hang from an infinite interrupt storm?
[ ... ]
> +static int rtd1625_gpio_irq_set_type(struct irq_data *d, unsigned int type)
> +{
> + switch (type & IRQ_TYPE_SENSE_MASK) {
> + case IRQ_TYPE_EDGE_RISING:
> + return rtd1625_gpio_irq_set_edge_type(d, 1);
> +
> + case IRQ_TYPE_EDGE_FALLING:
> + return rtd1625_gpio_irq_set_edge_type(d, 0);
> +
> + case IRQ_TYPE_EDGE_BOTH:
> + return rtd1625_gpio_irq_set_edge_type(d, 1);
[Severity: High]
Does this code configure EDGE_BOTH identically to EDGE_RISING by passing 1 to
rtd1625_gpio_irq_set_edge_type()?
If the hardware only detects the configured edge, will all falling edges be
completely missed? Additionally, in rtd1625_gpio_irq_handle(), the code
explicitly skips filtering the DEASSERT interrupt for EDGE_BOTH requests:
if (irq == data->irqs[RTD1625_IRQ_DEASSERT] &&
irq_type != IRQ_TYPE_EDGE_BOTH)
continue;
Since a rising edge fires both ASSERT and DEASSERT simultaneously due to the
hardware quirk, will a single rising edge be processed twice, leading to
incorrect input event counting and dropped events?
> +
> + case IRQ_TYPE_LEVEL_HIGH:
> + return rtd1625_gpio_irq_set_level_type(d, 0);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260702090115.2564318-1-eleanor.lin@realtek.com?part=3
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-07-02 9:16 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-02 9:01 [PATCH v5 0/4] gpio: realtek: Add support for Realtek DHC RTD1625 Yu-Chun Lin
2026-07-02 9:01 ` [PATCH v5 1/4] dt-bindings: gpio: realtek: Add realtek,rtd1625-gpio Yu-Chun Lin
2026-07-02 9:01 ` [PATCH v5 2/4] gpio: Replace "default y" with "default ARCH_REALTEK" in Kconfig Yu-Chun Lin
2026-07-02 9:01 ` [PATCH v5 3/4] gpio: realtek: Add driver for Realtek DHC RTD1625 SoC Yu-Chun Lin
2026-07-02 9:16 ` sashiko-bot
2026-07-02 9:01 ` [PATCH v5 4/4] arm64: dts: realtek: Add GPIO support for RTD1625 Yu-Chun Lin
2026-07-02 9:07 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox