Commit 5a927501 authored by Heiko Stübner's avatar Heiko Stübner Committed by Linus Walleij

pinctrl: rockchip: emulate both edge triggered interrupts

The gpio interrupt controller on Rockchip socs can do edge triggers only
for single edges but not both. Nevertheless a lot of gpio users rely on
the availability of both-edge triggered interrupts - i.e. gpio-keys.

Therefore implement a solution similar to pinctrl-coh901 re-setting the
triggering edge depending on the gpio value in the interrupt demuxer.
Signed-off-by: default avatarHeiko Stuebner <heiko@sntech.de>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent 6ca5274d
......@@ -101,7 +101,7 @@ struct rockchip_pin_bank {
struct gpio_chip gpio_chip;
struct pinctrl_gpio_range grange;
spinlock_t slock;
u32 toggle_edge_mode;
};
#define PIN_BANK(id, pins, label) \
......@@ -1078,7 +1078,9 @@ static void rockchip_irq_demux(unsigned int irq, struct irq_desc *desc)
{
struct irq_chip *chip = irq_get_chip(irq);
struct rockchip_pin_bank *bank = irq_get_handler_data(irq);
u32 polarity = 0, data = 0;
u32 pend;
bool edge_changed = false;
dev_dbg(bank->drvdata->dev, "got irq for bank %s\n", bank->name);
......@@ -1086,6 +1088,12 @@ static void rockchip_irq_demux(unsigned int irq, struct irq_desc *desc)
pend = readl_relaxed(bank->reg_base + GPIO_INT_STATUS);
if (bank->toggle_edge_mode) {
polarity = readl_relaxed(bank->reg_base +
GPIO_INT_POLARITY);
data = readl_relaxed(bank->reg_base + GPIO_EXT_PORT);
}
while (pend) {
unsigned int virq;
......@@ -1100,9 +1108,30 @@ static void rockchip_irq_demux(unsigned int irq, struct irq_desc *desc)
dev_dbg(bank->drvdata->dev, "handling irq %d\n", irq);
/*
* Triggering IRQ on both rising and falling edge
* needs manual intervention.
*/
if (bank->toggle_edge_mode & BIT(irq)) {
if (data & BIT(irq))
polarity &= ~BIT(irq);
else
polarity |= BIT(irq);
edge_changed = true;
}
generic_handle_irq(virq);
}
if (bank->toggle_edge_mode && edge_changed) {
/* Interrupt params should only be set with ints disabled */
data = readl_relaxed(bank->reg_base + GPIO_INTEN);
writel_relaxed(0, bank->reg_base + GPIO_INTEN);
writel(polarity, bank->reg_base + GPIO_INT_POLARITY);
writel(data, bank->reg_base + GPIO_INTEN);
}
chained_irq_exit(chip, desc);
}
......@@ -1115,6 +1144,12 @@ static int rockchip_irq_set_type(struct irq_data *d, unsigned int type)
u32 level;
u32 data;
/* make sure the pin is configured as gpio input */
rockchip_set_mux(bank, d->hwirq, RK_FUNC_GPIO);
data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR);
data &= ~mask;
writel_relaxed(data, bank->reg_base + GPIO_SWPORT_DDR);
if (type & IRQ_TYPE_EDGE_BOTH)
__irq_set_handler_locked(d->irq, handle_edge_irq);
else
......@@ -1126,19 +1161,37 @@ static int rockchip_irq_set_type(struct irq_data *d, unsigned int type)
polarity = readl_relaxed(gc->reg_base + GPIO_INT_POLARITY);
switch (type) {
case IRQ_TYPE_EDGE_BOTH:
bank->toggle_edge_mode |= mask;
level |= mask;
/*
* Determine gpio state. If 1 next interrupt should be falling
* otherwise rising.
*/
data = readl(bank->reg_base + GPIO_EXT_PORT);
if (data & mask)
polarity &= ~mask;
else
polarity |= mask;
break;
case IRQ_TYPE_EDGE_RISING:
bank->toggle_edge_mode &= ~mask;
level |= mask;
polarity |= mask;
break;
case IRQ_TYPE_EDGE_FALLING:
bank->toggle_edge_mode &= ~mask;
level |= mask;
polarity &= ~mask;
break;
case IRQ_TYPE_LEVEL_HIGH:
bank->toggle_edge_mode &= ~mask;
level &= ~mask;
polarity |= mask;
break;
case IRQ_TYPE_LEVEL_LOW:
bank->toggle_edge_mode &= ~mask;
level &= ~mask;
polarity &= ~mask;
break;
......@@ -1152,12 +1205,6 @@ static int rockchip_irq_set_type(struct irq_data *d, unsigned int type)
irq_gc_unlock(gc);
/* make sure the pin is configured as gpio input */
rockchip_set_mux(bank, d->hwirq, RK_FUNC_GPIO);
data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR);
data &= ~mask;
writel_relaxed(data, bank->reg_base + GPIO_SWPORT_DDR);
return 0;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment