forked from luck/tmp_suning_uos_patched
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>
This commit is contained in:
parent
588de43ca1
commit
a7241c1b18
|
@ -51,6 +51,8 @@ struct hlwd_gpio {
|
||||||
struct irq_chip irqc;
|
struct irq_chip irqc;
|
||||||
void __iomem *regs;
|
void __iomem *regs;
|
||||||
int irq;
|
int irq;
|
||||||
|
u32 edge_emulation;
|
||||||
|
u32 rising_edge, falling_edge;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void hlwd_gpio_irqhandler(struct irq_desc *desc)
|
static void hlwd_gpio_irqhandler(struct irq_desc *desc)
|
||||||
|
@ -61,10 +63,36 @@ static void hlwd_gpio_irqhandler(struct irq_desc *desc)
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
unsigned long pending;
|
unsigned long pending;
|
||||||
int hwirq;
|
int hwirq;
|
||||||
|
u32 emulated_pending;
|
||||||
|
|
||||||
spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags);
|
spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags);
|
||||||
pending = ioread32be(hlwd->regs + HW_GPIOB_INTFLAG);
|
pending = ioread32be(hlwd->regs + HW_GPIOB_INTFLAG);
|
||||||
pending &= ioread32be(hlwd->regs + HW_GPIOB_INTMASK);
|
pending &= ioread32be(hlwd->regs + HW_GPIOB_INTMASK);
|
||||||
|
|
||||||
|
/* Treat interrupts due to edge trigger emulation separately */
|
||||||
|
emulated_pending = hlwd->edge_emulation & pending;
|
||||||
|
pending &= ~emulated_pending;
|
||||||
|
if (emulated_pending) {
|
||||||
|
u32 level, rising, falling;
|
||||||
|
|
||||||
|
level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL);
|
||||||
|
rising = level & emulated_pending;
|
||||||
|
falling = ~level & emulated_pending;
|
||||||
|
|
||||||
|
/* Invert the levels */
|
||||||
|
iowrite32be(level ^ emulated_pending,
|
||||||
|
hlwd->regs + HW_GPIOB_INTLVL);
|
||||||
|
|
||||||
|
/* Ack all emulated-edge interrupts */
|
||||||
|
iowrite32be(emulated_pending, hlwd->regs + HW_GPIOB_INTFLAG);
|
||||||
|
|
||||||
|
/* Signal interrupts only on the correct edge */
|
||||||
|
rising &= hlwd->rising_edge;
|
||||||
|
falling &= hlwd->falling_edge;
|
||||||
|
|
||||||
|
/* Mark emulated interrupts as pending */
|
||||||
|
pending |= rising | falling;
|
||||||
|
}
|
||||||
spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags);
|
spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags);
|
||||||
|
|
||||||
chained_irq_enter(chip, desc);
|
chained_irq_enter(chip, desc);
|
||||||
|
@ -120,6 +148,27 @@ static void hlwd_gpio_irq_enable(struct irq_data *data)
|
||||||
hlwd_gpio_irq_unmask(data);
|
hlwd_gpio_irq_unmask(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void hlwd_gpio_irq_setup_emulation(struct hlwd_gpio *hlwd, int hwirq,
|
||||||
|
unsigned int flow_type)
|
||||||
|
{
|
||||||
|
u32 level, state;
|
||||||
|
|
||||||
|
/* Set the trigger level to the inactive level */
|
||||||
|
level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL);
|
||||||
|
state = ioread32be(hlwd->regs + HW_GPIOB_IN) & BIT(hwirq);
|
||||||
|
level &= ~BIT(hwirq);
|
||||||
|
level |= state ^ BIT(hwirq);
|
||||||
|
iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL);
|
||||||
|
|
||||||
|
hlwd->edge_emulation |= BIT(hwirq);
|
||||||
|
hlwd->rising_edge &= ~BIT(hwirq);
|
||||||
|
hlwd->falling_edge &= ~BIT(hwirq);
|
||||||
|
if (flow_type & IRQ_TYPE_EDGE_RISING)
|
||||||
|
hlwd->rising_edge |= BIT(hwirq);
|
||||||
|
if (flow_type & IRQ_TYPE_EDGE_FALLING)
|
||||||
|
hlwd->falling_edge |= BIT(hwirq);
|
||||||
|
}
|
||||||
|
|
||||||
static int hlwd_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type)
|
static int hlwd_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type)
|
||||||
{
|
{
|
||||||
struct hlwd_gpio *hlwd =
|
struct hlwd_gpio *hlwd =
|
||||||
|
@ -129,6 +178,8 @@ static int hlwd_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type)
|
||||||
|
|
||||||
spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags);
|
spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags);
|
||||||
|
|
||||||
|
hlwd->edge_emulation &= ~BIT(data->hwirq);
|
||||||
|
|
||||||
switch (flow_type) {
|
switch (flow_type) {
|
||||||
case IRQ_TYPE_LEVEL_HIGH:
|
case IRQ_TYPE_LEVEL_HIGH:
|
||||||
level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL);
|
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)
|
||||||
level &= ~BIT(data->hwirq);
|
level &= ~BIT(data->hwirq);
|
||||||
iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL);
|
iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL);
|
||||||
break;
|
break;
|
||||||
|
case IRQ_TYPE_EDGE_RISING:
|
||||||
|
case IRQ_TYPE_EDGE_FALLING:
|
||||||
|
case IRQ_TYPE_EDGE_BOTH:
|
||||||
|
hlwd_gpio_irq_setup_emulation(hlwd, data->hwirq, flow_type);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags);
|
spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user