Message ID | 20201209163818.v3.1.I2702919afc253e2a451bebc3b701b462b2d22344@changeid |
---|---|
State | Superseded |
Headers | show |
Series | [v3,1/3] irqchip: qcom-pdc: Fix phantom irq when changing between rising/falling | expand |
Quoting Douglas Anderson (2020-12-09 16:41:01) > We have a problem if we use gpio-keys and configure wakeups such that > we only want one edge to wake us up. AKA: > wakeup-event-action = <EV_ACT_DEASSERTED>; > wakeup-source; > > Specifically we end up with a phantom interrupt that blocks suspend if > the line was already high and we want wakeups on rising edges (AKA we > want the GPIO to go low and then high again before we wake up). The > opposite is also problematic. > > Specifically, here's what's happening today: > 1. Normally, gpio-keys configures to look for both edges. Due to the > current workaround introduced in commit c3c0c2e18d94 ("pinctrl: > qcom: Handle broken/missing PDC dual edge IRQs on sc7180"), if the > line was high we'd configure for falling edges. > 2. At suspend time, we change to look for rising edges. > 3. After qcom_pdc_gic_set_type() runs, we get a phantom interrupt. > > We can solve this by just clearing the phantom interrupt. > > NOTE: it is possible that this could cause problems for a client with > very specific needs, but there's not much we can do with this > hardware. As an example, let's say the interrupt signal is currently > high and the client is looking for falling edges. The client now > changes to look for rising edges. The client could possibly expect > that if the line has a short pulse low (and back high) that it would > always be detected. Specifically no matter when the pulse happened, > it should either have tripped the (old) falling edge trigger or the > (new) rising edge trigger. We will simply not trip it. We could > narrow down the race a bit by polling our parent before changing > types, but no matter what we do there will still be a period of time > where we can't tell the difference between a real transition (or more > than one transition) and the phantom. > > Fixes: f55c73aef890 ("irqchip/pdc: Add PDC interrupt controller for QCOM SoCs") > Signed-off-by: Douglas Anderson <dianders@chromium.org> > Reviewed-by: Maulik Shah <mkshah@codeaurora.org> > Tested-by: Maulik Shah <mkshah@codeaurora.org> > --- Reviewed-by: Stephen Boyd <swboyd@chromium.org>
Hi, On Thu, Dec 10, 2020 at 1:56 PM Stephen Boyd <swboyd@chromium.org> wrote: > > Quoting Douglas Anderson (2020-12-09 16:41:03) > > Conceptually, we can envision the input on Qualcomm SoCs to pass > > through a bunch of blocks between coming into the chip and becoming a > > GPIO interrupt. From guessing and running a handful of tests, I > > believe that we can represent the state of the world with a drawing > > that looks something like this: > > > > +-----------------+ +-----------------+ +-----------------+ > > | INPUT | --> | PINMUX | | IS_INPUT | > > +-----------------+ | | --> | | > > | output bogus (?)| | output bogus (?)| > > | if not muxed | | if input disab. | > > +-----------------+ +-----------------+ > > | > > +---------------------------------------------------+--> to PDC > > | > > V > > +-----------------+ +-----------------+ +-----------------+ > > | INTR RAW ENABLE | | DETECTION LOGIC | | STATUS REGISTER | > > | | | | | | > > | output bogus (?)| --> | maybe handles | | latches inputs | > > | if disabled | | polarity diffs | --> | that are high | > > +-----------------+ | | | | > > | maybe debounces | | write 1 to clr | > > | level irqs | +-----------------+ > > +-----------------+ | > > | > > +---------------------------------------------------+ > > | > > V > > +-----------------+ > > | ENABLE | > > | | +-----------------+ > > | nothing passes | --> | SUMMARY IRQ | > > | through if | +-----------------+ > > | disabled | > > +-----------------+ > > This diagram doesn't make sense to me. I've gutted most of this code for v4 after Maulik pointed out why my testing was flawed. Hopefully v4 looks saner... -Doug
diff --git a/drivers/irqchip/qcom-pdc.c b/drivers/irqchip/qcom-pdc.c index bd39e9de6ecf..5dc63c20b67e 100644 --- a/drivers/irqchip/qcom-pdc.c +++ b/drivers/irqchip/qcom-pdc.c @@ -159,6 +159,8 @@ static int qcom_pdc_gic_set_type(struct irq_data *d, unsigned int type) { int pin_out = d->hwirq; enum pdc_irq_config_bits pdc_type; + enum pdc_irq_config_bits old_pdc_type; + int ret; if (pin_out == GPIO_NO_WAKE_IRQ) return 0; @@ -187,9 +189,26 @@ static int qcom_pdc_gic_set_type(struct irq_data *d, unsigned int type) return -EINVAL; } + old_pdc_type = pdc_reg_read(IRQ_i_CFG, pin_out); pdc_reg_write(IRQ_i_CFG, pin_out, pdc_type); - return irq_chip_set_type_parent(d, type); + ret = irq_chip_set_type_parent(d, type); + if (ret) + return ret; + + /* + * When we change types the PDC can give a phantom interrupt. + * Clear it. Specifically the phantom shows up when reconfiguring + * polarity of interrupt without changing the state of the signal + * but let's be consistent and clear it always. + * + * Doing this works because we have IRQCHIP_SET_TYPE_MASKED so the + * interrupt will be cleared before the rest of the system sees it. + */ + if (old_pdc_type != pdc_type) + irq_chip_set_parent_state(d, IRQCHIP_STATE_PENDING, false); + + return 0; } static struct irq_chip qcom_pdc_gic_chip = {