[PATCH v2 3/4] gpio: davinci: Add support for level-triggered interrupts
From: Aniket Limaye
Date: Fri Oct 16 2026 - 00:35:34 EST
Add support for IRQ_TYPE_LEVEL_HIGH and IRQ_TYPE_LEVEL_LOW through the
extended registers: set_high/clr_high/set_low/clr_low. Extend struct
davinci_gpio_match_data with an ext_reg flag to describe such
controllers.
The level enables are cleared on setup and are only set when a level
trigger is requested, so the default trigger of both edges is
preserved.
No existing compatible sets ext_reg, so there is no functional change
for current users.
Signed-off-by: Aniket Limaye <a-limaye@xxxxxx>
---
Changes in v2:
- davinci_gpio_irq_setup() now only sets IRQ_TYPE_EDGE_BOTH by default
- For level-triggered interrupts use handle_level_irq()
- Link to v1: https://lore.kernel.org/r/20260923-upstream-tda54-gpio-v1-2-d1a0a199c836@xxxxxx
---
drivers/gpio/gpio-davinci.c | 113 ++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 108 insertions(+), 5 deletions(-)
diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c
index ab1b465dfcd15..8d0ebf4afd169 100644
--- a/drivers/gpio/gpio-davinci.c
+++ b/drivers/gpio/gpio-davinci.c
@@ -39,16 +39,25 @@ struct davinci_gpio_regs {
u32 intstat;
};
+struct davinci_gpio_regs_ext {
+ u32 set_high;
+ u32 clr_high;
+ u32 set_low;
+ u32 clr_low;
+};
+
typedef struct irq_chip *(*gpio_get_irq_chip_cb_t)(unsigned int irq);
struct davinci_gpio_match_data {
gpio_get_irq_chip_cb_t get_irq_chip;
+ bool ext_reg;
};
#define BINTEN 0x8 /* GPIO Interrupt Per-Bank Enable Register */
static void __iomem *gpio_base;
static unsigned int offset_array[5] = {0x10, 0x38, 0x60, 0x88, 0xb0};
+static unsigned int offset_array_ext[5] = {0xd8, 0xe8, 0xf8, 0x108, 0x118};
struct davinci_gpio_irq_data {
void __iomem *regs;
@@ -62,9 +71,11 @@ struct davinci_gpio_controller {
/* Serialize access to GPIO registers */
spinlock_t lock;
void __iomem *regs[MAX_REGS_BANKS];
+ void __iomem *regs_ext[MAX_REGS_BANKS];
int gpio_unbanked;
int irqs[MAX_INT_PER_BANK];
struct davinci_gpio_regs context[MAX_REGS_BANKS];
+ struct davinci_gpio_regs_ext context_ext[MAX_REGS_BANKS];
u32 binten_context;
};
@@ -172,6 +183,8 @@ static int davinci_gpio_probe(struct platform_device *pdev)
unsigned int ngpio, nbank, nirq, gpio_unbanked;
struct davinci_gpio_controller *chips;
struct device *dev = &pdev->dev;
+ const struct davinci_gpio_match_data *match_data;
+ bool ext_reg = false;
/*
* The gpio banks conceptually expose a segmented bitmap,
@@ -194,6 +207,10 @@ static int davinci_gpio_probe(struct platform_device *pdev)
if (ret)
return dev_err_probe(dev, ret, "Failed to get the unbanked GPIOs property\n");
+ match_data = device_get_match_data(dev);
+ if (match_data)
+ ext_reg = match_data->ext_reg;
+
if (gpio_unbanked)
nirq = gpio_unbanked;
else
@@ -239,8 +256,11 @@ static int davinci_gpio_probe(struct platform_device *pdev)
chips->gpio_unbanked = gpio_unbanked;
nbank = DIV_ROUND_UP(ngpio, 32);
- for (bank = 0; bank < nbank; bank++)
+ for (bank = 0; bank < nbank; bank++) {
chips->regs[bank] = gpio_base + offset_array[bank];
+ if (ext_reg)
+ chips->regs_ext[bank] = gpio_base + offset_array_ext[bank];
+ }
ret = devm_gpiochip_add_data(dev, &chips->chip, chips);
if (ret)
@@ -271,10 +291,15 @@ static void gpio_irq_mask(struct irq_data *d)
struct davinci_gpio_controller *chips = irq_data_get_irq_chip_data(d);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
struct davinci_gpio_regs __iomem *g = chips->regs[hwirq / 32];
+ struct davinci_gpio_regs_ext __iomem *g_ext = chips->regs_ext[hwirq / 32];
uintptr_t mask = (uintptr_t)irq_data_get_irq_handler_data(d);
writel_relaxed(mask, &g->clr_falling);
writel_relaxed(mask, &g->clr_rising);
+ if (g_ext) {
+ writel_relaxed(mask, &g_ext->clr_high);
+ writel_relaxed(mask, &g_ext->clr_low);
+ }
gpiochip_disable_irq(&chips->chip, hwirq);
}
@@ -284,12 +309,16 @@ static void gpio_irq_unmask(struct irq_data *d)
struct davinci_gpio_controller *chips = irq_data_get_irq_chip_data(d);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
struct davinci_gpio_regs __iomem *g = chips->regs[hwirq / 32];
+ struct davinci_gpio_regs_ext __iomem *g_ext = chips->regs_ext[hwirq / 32];
uintptr_t mask = (uintptr_t)irq_data_get_irq_handler_data(d);
unsigned status = irqd_get_trigger_type(d);
gpiochip_enable_irq(&chips->chip, hwirq);
- status &= IRQ_TYPE_EDGE_BOTH;
+ if (g_ext)
+ status &= IRQ_TYPE_SENSE_MASK;
+ else
+ status &= IRQ_TYPE_EDGE_BOTH;
if (!status)
status = IRQ_TYPE_EDGE_BOTH;
@@ -297,6 +326,31 @@ static void gpio_irq_unmask(struct irq_data *d)
writel_relaxed(mask, &g->set_falling);
if (status & IRQ_TYPE_EDGE_RISING)
writel_relaxed(mask, &g->set_rising);
+ if (status & IRQ_TYPE_LEVEL_HIGH)
+ writel_relaxed(mask, &g_ext->set_high);
+ if (status & IRQ_TYPE_LEVEL_LOW)
+ writel_relaxed(mask, &g_ext->set_low);
+}
+
+static bool gpio_irq_trigger_valid_ext(unsigned int trigger)
+{
+ if (trigger & ~IRQ_TYPE_SENSE_MASK)
+ return false;
+
+ return (trigger & IRQ_TYPE_LEVEL_MASK) != IRQ_TYPE_LEVEL_MASK;
+}
+
+static int gpio_irq_type_ext(struct irq_data *d, unsigned int trigger)
+{
+ if (!gpio_irq_trigger_valid_ext(trigger))
+ return -EINVAL;
+
+ if (trigger & IRQ_TYPE_LEVEL_MASK)
+ irq_set_handler_locked(d, handle_level_irq);
+ else
+ irq_set_handler_locked(d, handle_simple_irq);
+
+ return 0;
}
static int gpio_irq_type(struct irq_data *d, unsigned trigger)
@@ -307,6 +361,15 @@ static int gpio_irq_type(struct irq_data *d, unsigned trigger)
return 0;
}
+static const struct irq_chip gpio_irqchip_ext = {
+ .name = "GPIO",
+ .irq_unmask = gpio_irq_unmask,
+ .irq_mask = gpio_irq_mask,
+ .irq_set_type = gpio_irq_type_ext,
+ .flags = IRQCHIP_IMMUTABLE | IRQCHIP_SET_TYPE_MASKED | IRQCHIP_SKIP_SET_WAKE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
+};
+
static const struct irq_chip gpio_irqchip = {
.name = "GPIO",
.irq_unmask = gpio_irq_unmask,
@@ -391,10 +454,12 @@ static int gpio_irq_type_unbanked(struct irq_data *data, unsigned trigger)
{
struct davinci_gpio_controller *d;
struct davinci_gpio_regs __iomem *g;
+ struct davinci_gpio_regs_ext __iomem *g_ext;
u32 mask, i;
d = (struct davinci_gpio_controller *)irq_data_get_irq_handler_data(data);
g = (struct davinci_gpio_regs __iomem *)d->regs[0];
+ g_ext = (struct davinci_gpio_regs_ext __iomem *)d->regs_ext[0];
for (i = 0; i < MAX_INT_PER_BANK; i++)
if (data->irq == d->irqs[i])
break;
@@ -404,13 +469,20 @@ static int gpio_irq_type_unbanked(struct irq_data *data, unsigned trigger)
mask = __gpio_mask(i);
- if (trigger & ~IRQ_TYPE_EDGE_BOTH)
+ if (g_ext ? !gpio_irq_trigger_valid_ext(trigger) :
+ (trigger & ~IRQ_TYPE_EDGE_BOTH))
return -EINVAL;
writel_relaxed(mask, (trigger & IRQ_TYPE_EDGE_FALLING)
? &g->set_falling : &g->clr_falling);
writel_relaxed(mask, (trigger & IRQ_TYPE_EDGE_RISING)
? &g->set_rising : &g->clr_rising);
+ if (g_ext) {
+ writel_relaxed(mask, (trigger & IRQ_TYPE_LEVEL_HIGH)
+ ? &g_ext->set_high : &g_ext->clr_high);
+ writel_relaxed(mask, (trigger & IRQ_TYPE_LEVEL_LOW)
+ ? &g_ext->set_low : &g_ext->clr_low);
+ }
return 0;
}
@@ -421,9 +493,11 @@ davinci_gpio_irq_map(struct irq_domain *d, unsigned int irq,
{
struct davinci_gpio_controller *chips =
(struct davinci_gpio_controller *)d->host_data;
+ const struct irq_chip *chip = chips->regs_ext[0] ? &gpio_irqchip_ext :
+ &gpio_irqchip;
- irq_set_chip_and_handler_name(irq, &gpio_irqchip, handle_simple_irq,
- "davinci_gpio");
+ irq_set_chip_and_handler_name(irq, chip, handle_simple_irq,
+ "davinci_gpio");
irq_set_irq_type(irq, IRQ_TYPE_NONE);
irq_set_chip_data(irq, (__force void *)chips);
irq_set_handler_data(irq, (void *)(uintptr_t)__gpio_mask(hw));
@@ -481,6 +555,7 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct davinci_gpio_controller *chips = platform_get_drvdata(pdev);
struct davinci_gpio_regs __iomem *g;
+ struct davinci_gpio_regs_ext __iomem *g_ext;
struct irq_domain *irq_domain = NULL;
struct irq_chip *irq_chip;
struct davinci_gpio_irq_data *irqdata;
@@ -551,6 +626,11 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev)
g = chips->regs[0];
writel_relaxed(~0, &g->set_falling);
writel_relaxed(~0, &g->set_rising);
+ g_ext = chips->regs_ext[0];
+ if (g_ext) {
+ writel_relaxed(~0, &g_ext->clr_high);
+ writel_relaxed(~0, &g_ext->clr_low);
+ }
/* set the direct IRQs up to use that irqchip */
for (gpio = 0; gpio < chips->gpio_unbanked; gpio++) {
@@ -575,6 +655,11 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev)
g = chips->regs[bank / 2];
writel_relaxed(~0, &g->clr_falling);
writel_relaxed(~0, &g->clr_rising);
+ g_ext = chips->regs_ext[bank / 2];
+ if (g_ext) {
+ writel_relaxed(~0, &g_ext->clr_high);
+ writel_relaxed(~0, &g_ext->clr_low);
+ }
/*
* Each chip handles 32 gpios, and each irq bank consists of 16
@@ -614,7 +699,9 @@ static void davinci_gpio_save_context(struct davinci_gpio_controller *chips,
u32 nbank)
{
struct davinci_gpio_regs __iomem *g;
+ struct davinci_gpio_regs_ext __iomem *g_ext;
struct davinci_gpio_regs *context;
+ struct davinci_gpio_regs_ext *context_ext;
u32 bank;
void __iomem *base;
@@ -628,6 +715,12 @@ static void davinci_gpio_save_context(struct davinci_gpio_controller *chips,
context->set_data = readl_relaxed(&g->set_data);
context->set_rising = readl_relaxed(&g->set_rising);
context->set_falling = readl_relaxed(&g->set_falling);
+ g_ext = chips->regs_ext[bank];
+ if (g_ext) {
+ context_ext = &chips->context_ext[bank];
+ context_ext->set_high = readl_relaxed(&g_ext->set_high);
+ context_ext->set_low = readl_relaxed(&g_ext->set_low);
+ }
}
/* Clear all interrupt status registers */
@@ -638,7 +731,9 @@ static void davinci_gpio_restore_context(struct davinci_gpio_controller *chips,
u32 nbank)
{
struct davinci_gpio_regs __iomem *g;
+ struct davinci_gpio_regs_ext __iomem *g_ext;
struct davinci_gpio_regs *context;
+ struct davinci_gpio_regs_ext *context_ext;
u32 bank;
void __iomem *base;
@@ -658,6 +753,14 @@ static void davinci_gpio_restore_context(struct davinci_gpio_controller *chips,
writel_relaxed(context->set_rising, &g->set_rising);
if (readl_relaxed(&g->set_falling) != context->set_falling)
writel_relaxed(context->set_falling, &g->set_falling);
+ g_ext = chips->regs_ext[bank];
+ if (g_ext) {
+ context_ext = &chips->context_ext[bank];
+ if (readl_relaxed(&g_ext->set_high) != context_ext->set_high)
+ writel_relaxed(context_ext->set_high, &g_ext->set_high);
+ if (readl_relaxed(&g_ext->set_low) != context_ext->set_low)
+ writel_relaxed(context_ext->set_low, &g_ext->set_low);
+ }
}
}
--
2.53.0