Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Jonathan Neuschäfer and committed by
Linus Walleij
a7241c1b 588de43c

+56
+56
drivers/gpio/gpio-hlwd.c
··· 51 51 struct irq_chip irqc; 52 52 void __iomem *regs; 53 53 int irq; 54 + u32 edge_emulation; 55 + u32 rising_edge, falling_edge; 54 56 }; 55 57 56 58 static void hlwd_gpio_irqhandler(struct irq_desc *desc) ··· 63 61 unsigned long flags; 64 62 unsigned long pending; 65 63 int hwirq; 64 + u32 emulated_pending; 66 65 67 66 spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); 68 67 pending = ioread32be(hlwd->regs + HW_GPIOB_INTFLAG); 69 68 pending &= ioread32be(hlwd->regs + HW_GPIOB_INTMASK); 69 + 70 + /* Treat interrupts due to edge trigger emulation separately */ 71 + emulated_pending = hlwd->edge_emulation & pending; 72 + pending &= ~emulated_pending; 73 + if (emulated_pending) { 74 + u32 level, rising, falling; 75 + 76 + level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); 77 + rising = level & emulated_pending; 78 + falling = ~level & emulated_pending; 79 + 80 + /* Invert the levels */ 81 + iowrite32be(level ^ emulated_pending, 82 + hlwd->regs + HW_GPIOB_INTLVL); 83 + 84 + /* Ack all emulated-edge interrupts */ 85 + iowrite32be(emulated_pending, hlwd->regs + HW_GPIOB_INTFLAG); 86 + 87 + /* Signal interrupts only on the correct edge */ 88 + rising &= hlwd->rising_edge; 89 + falling &= hlwd->falling_edge; 90 + 91 + /* Mark emulated interrupts as pending */ 92 + pending |= rising | falling; 93 + } 70 94 spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags); 71 95 72 96 chained_irq_enter(chip, desc); ··· 148 120 hlwd_gpio_irq_unmask(data); 149 121 } 150 122 123 + static void hlwd_gpio_irq_setup_emulation(struct hlwd_gpio *hlwd, int hwirq, 124 + unsigned int flow_type) 125 + { 126 + u32 level, state; 127 + 128 + /* Set the trigger level to the inactive level */ 129 + level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); 130 + state = ioread32be(hlwd->regs + HW_GPIOB_IN) & BIT(hwirq); 131 + level &= ~BIT(hwirq); 132 + level |= state ^ BIT(hwirq); 133 + iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL); 134 + 135 + hlwd->edge_emulation |= BIT(hwirq); 136 + hlwd->rising_edge &= ~BIT(hwirq); 137 + hlwd->falling_edge &= ~BIT(hwirq); 138 + if (flow_type & IRQ_TYPE_EDGE_RISING) 139 + hlwd->rising_edge |= BIT(hwirq); 140 + if (flow_type & IRQ_TYPE_EDGE_FALLING) 141 + hlwd->falling_edge |= BIT(hwirq); 142 + } 143 + 151 144 static int hlwd_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type) 152 145 { 153 146 struct hlwd_gpio *hlwd = ··· 177 128 u32 level; 178 129 179 130 spin_lock_irqsave(&hlwd->gpioc.bgpio_lock, flags); 131 + 132 + hlwd->edge_emulation &= ~BIT(data->hwirq); 180 133 181 134 switch (flow_type) { 182 135 case IRQ_TYPE_LEVEL_HIGH: ··· 190 139 level = ioread32be(hlwd->regs + HW_GPIOB_INTLVL); 191 140 level &= ~BIT(data->hwirq); 192 141 iowrite32be(level, hlwd->regs + HW_GPIOB_INTLVL); 142 + break; 143 + case IRQ_TYPE_EDGE_RISING: 144 + case IRQ_TYPE_EDGE_FALLING: 145 + case IRQ_TYPE_EDGE_BOTH: 146 + hlwd_gpio_irq_setup_emulation(hlwd, data->hwirq, flow_type); 193 147 break; 194 148 default: 195 149 spin_unlock_irqrestore(&hlwd->gpioc.bgpio_lock, flags);