Skip to content

Commit a7241c1

Browse files
neuschaeferlinusw
authored andcommitted
gpio: hlwd: Implement edge trigger emulation
Like the Spreadtrum EIC driver[1], this driver needs to emulate edge triggered interrupts to support the generic gpio-keys driver. [1]: https://www.spinics.net/lists/kernel/msg2764576.html Signed-off-by: Jonathan Neuschäfer <j.neuschaefer@gmx.net> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
1 parent 588de43 commit a7241c1

File tree

1 file changed

+56
-0
lines changed

1 file changed

+56
-0
lines changed

drivers/gpio/gpio-hlwd.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ struct hlwd_gpio {
5151
struct irq_chip irqc;
5252
void __iomem *regs;
5353
int irq;
54+
u32 edge_emulation;
55+
u32 rising_edge, falling_edge;
5456
};
5557

5658
static void hlwd_gpio_irqhandler(struct irq_desc *desc)
@@ -61,10 +63,36 @@ static void hlwd_gpio_irqhandler(struct irq_desc *desc)
6163
unsigned long flags;
6264
unsigned long pending;
6365
int hwirq;
66+
u32 emulated_pending;
6467

6568
spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags);
6669
pending = ioread32be(hlwd->regs + HW_GPIOB_INTFLAG);
6770
pending &= ioread32be(hlwd->regs + HW_GPIOB_INTMASK);
71+
72+
/* Treat interrupts due to edge trigger emulation separately */
73+
emulated_pending = hlwd->edge_emulation & pending;
74+
pending &= ~emulated_pending;
75+
if (emulated_pending) {
76+
u32 level, rising, falling;
77+
78+
level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL);
79+
rising = level & emulated_pending;
80+
falling = ~level & emulated_pending;
81+
82+
/* Invert the levels */
83+
iowrite32be(level ^ emulated_pending,
84+
hlwd->regs + HW_GPIOB_INTLVL);
85+
86+
/* Ack all emulated-edge interrupts */
87+
iowrite32be(emulated_pending, hlwd->regs + HW_GPIOB_INTFLAG);
88+
89+
/* Signal interrupts only on the correct edge */
90+
rising &= hlwd->rising_edge;
91+
falling &= hlwd->falling_edge;
92+
93+
/* Mark emulated interrupts as pending */
94+
pending |= rising | falling;
95+
}
6896
spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags);
6997

7098
chained_irq_enter(chip, desc);
@@ -120,6 +148,27 @@ static void hlwd_gpio_irq_enable(struct irq_data *data)
120148
hlwd_gpio_irq_unmask(data);
121149
}
122150

151+
static void hlwd_gpio_irq_setup_emulation(struct hlwd_gpio *hlwd, int hwirq,
152+
unsigned int flow_type)
153+
{
154+
u32 level, state;
155+
156+
/* Set the trigger level to the inactive level */
157+
level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL);
158+
state = ioread32be(hlwd->regs + HW_GPIOB_IN) & BIT(hwirq);
159+
level &= ~BIT(hwirq);
160+
level |= state ^ BIT(hwirq);
161+
iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL);
162+
163+
hlwd->edge_emulation |= BIT(hwirq);
164+
hlwd->rising_edge &= ~BIT(hwirq);
165+
hlwd->falling_edge &= ~BIT(hwirq);
166+
if (flow_type & IRQ_TYPE_EDGE_RISING)
167+
hlwd->rising_edge |= BIT(hwirq);
168+
if (flow_type & IRQ_TYPE_EDGE_FALLING)
169+
hlwd->falling_edge |= BIT(hwirq);
170+
}
171+
123172
static int hlwd_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type)
124173
{
125174
struct hlwd_gpio *hlwd =
@@ -129,6 +178,8 @@ static int hlwd_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type)
129178

130179
spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags);
131180

181+
hlwd->edge_emulation &= ~BIT(data->hwirq);
182+
132183
switch (flow_type) {
133184
case IRQ_TYPE_LEVEL_HIGH:
134185
level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL);
@@ -140,6 +191,11 @@ static int hlwd_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type)
140191
level &= ~BIT(data->hwirq);
141192
iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL);
142193
break;
194+
case IRQ_TYPE_EDGE_RISING:
195+
case IRQ_TYPE_EDGE_FALLING:
196+
case IRQ_TYPE_EDGE_BOTH:
197+
hlwd_gpio_irq_setup_emulation(hlwd, data->hwirq, flow_type);
198+
break;
143199
default:
144200
spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags);
145201
return -EINVAL;

0 commit comments

Comments
 (0)