PLIC and interrupts received while no hart enabled

Vincent Pelletier plr.vincent at gmail.com
Thu Jul 22 20:21:48 PDT 2021


On Sun, 4 Jul 2021 06:41:14 +0000, Vincent Pelletier <plr.vincent at gmail.com> wrote:
> Hello,
> 
> I've recently purchased a sifive unmatched board, and have tried to
> complete its devicetree using the schematics.
> 
> Doing so, I enabled some sub-functions of its PMIC chip (DA9063). Those
> which matter for this email are:
> - power button, notified by interrupt
> - hwmon, based on an ADC which signals conversion completion with an
>   interrupt
> 
> The PMIC has an interrupt line, active low, connected to line 1 of the
> GPIO controller embedded in the board's SoC (fu740).
> Internally to the SoC, the GPIO has one interrupt line per GPIO line,
> connected to the PLIC.
> 
> The issue I originally had was, which I noticed with the ADC but could
> equally reproduce with the power button, I could only ever get a single
> IRQ to be processed.
> 
> Reading the corresponding registers (with the script attached),
> enabling IRQ and regmap tracing, I saw the output at the end of this email.
> 
> Here is how I read the register states:
> - initial state:
>   The PMIC interrupt line is in its idle state (high).
>   The GPIO controller noticed the high state, and armed the
>   corresponding interrupt-pending bit - this an important first piece of
>   information.
>   As the corresponding interrupt-enable is cleared, no interrupt is
>   signalled and the PLIC does not see an interrupt pending. This is all
>   good.
> - after one button press:
>   The PMIC interrupt line is again in its idle state, so the interrupt
>   source was cleared. I know it triggered as the power button input
>   event does fire as intended.
>   This time, the GPIO controller noticed 3 interrupt conditions on this
>   line: low, rising edge, and high. This means it was cleared *before*
>   the PMIC line went high:
>   - if it was cleared after, only "high" should be visible
>   - if is was never cleared, all low/rise/high/fall interrupt
>     conditions would be present
>   As the "low" interrupt is enabled, I believe (but cannot check) that
>   it notified the PLIC of the interrupt.
>   But like before, the PLIC does not see this interrupt being signalled.
> 
> As a result, the hart assigned to this interrupt (here, hart4) does not
> get interrupted even after it gets re-assigned to the corresponding
> PLIC line, so the GPIO interrupt pending bits are never cleared, and it
> cannot signal any further interrupt to the PLIC.
> Pressing the key a second time, the only difference is that the GPIO
> controller noticed the falling edge interrupt condition, which is
> masked so nothing happens.
> 
> The kernel/debug/tracing/trace buffer tells a similar story:
> - the GPIO regmap clear writes happen first
> - later, the PMIC interrupt source register is cleared (the write to
>   register 6)
> 
> Checking gpio-sifive.c and irq-sifive-plic.c, I see:
> - the GPIO propagates irq_mask and irq_unmask to the PLIC
> - the PLIC implements irq masking by making the interrupt line handled
>   by no hart
> 
> Reading the PLIC documentation, I cannot tell if this behaviour of the
> PLIC ("losing" an interrupt if there is no hart servicing it) is
> intended or not.
> 
> If it is intended, then what is the proper way of masking an
> interrupt ?
> Is it to lower that interrupt priority ? (which I did not try)
> Is it to mask it outside the PLIC (so on the GPIO controller here) ?
> 
> I did try the latter as a quick hack, and it allows repeated
> interrupts, fixing both the power button and the hwmon subsystems.
> But I am not sure it is possible to do this while being certain to never
> lose an interrupt - but I am very new to interrupt handling in Linux.
> 
> Traces:
> 
>   # ./unmatched_gpio_irq_debug.py 1
>   gpio_pin=1 gpio_mask=0b10 plic_irq=24 plic_mask=0b1000000000000000000000000
>   GPIO 1: dir=in  in=1 out=0 irq_en=low irq_pending=high
>   PLIC 24: priority=1 irq_en=hart4 irq_pending=False
>   hart min prio: 1=0, 2=0, 3=0, 4=0
>   # echo 1 > /sys/kernel/debug/tracing/events/regmap/enable; echo 1 > /sys/kernel/debug/tracing/events/irq/enable
> 
>   button pressed once here
> 
>   # echo 0 > /sys/kernel/debug/tracing/events/regmap/enable; echo 0 > /sys/kernel/debug/tracing/events/irq/enable
>   # ./unmatched_gpio_irq_debug.py 1
>   gpio_pin=1 gpio_mask=0b10 plic_irq=24 plic_mask=0b1000000000000000000000000
>   GPIO 1: dir=in  in=1 out=0 irq_en=low irq_pending=rise,high,low
>   PLIC 24: priority=1 irq_en=hart4 irq_pending=False
>   hart min prio: 1=0, 2=0, 3=0, 4=0
> 
> Trace output with i2c interrupts trimmed for brevity:
>           <idle>-0       [000] d.h2   102.707972: irq_handler_entry: irq=53 name=da9063-irq
>           <idle>-0       [000] d.h2   102.707976: irq_handler_exit: irq=53 ret=handled
>           <idle>-0       [000] d.h4   102.708019: regmap_reg_write: 10060000.gpio reg=1c val=2
>           <idle>-0       [000] d.h4   102.708022: regmap_reg_write: 10060000.gpio reg=24 val=2
>           <idle>-0       [000] d.h4   102.708024: regmap_reg_write: 10060000.gpio reg=2c val=2
>           <idle>-0       [000] d.h4   102.708026: regmap_reg_write: 10060000.gpio reg=34 val=2
>  irq/53-da9063-i-140     [002] ...1   102.708072: regmap_hw_read_start: 0-0058 reg=0 count=1
>  irq/53-da9063-i-140     [002] d.h2   102.708121: irq_handler_entry: irq=5 name=riscv-timer
>  irq/53-da9063-i-140     [002] dnh2   102.708159: irq_handler_exit: irq=5 ret=handled
> (snip)
>  irq/53-da9063-i-140     [002] ...1   102.709089: regmap_hw_read_done: 0-0058 reg=0 count=1
>  irq/53-da9063-i-140     [002] ...1   102.709095: regmap_reg_read: 0-0058 reg=0 val=0
>  irq/53-da9063-i-140     [002] ...1   102.709099: regmap_hw_read_start: 0-0058 reg=6 count=4
> (snip)
>  irq/53-da9063-i-140     [002] ...1   102.709879: regmap_hw_read_done: 0-0058 reg=6 count=4
>  irq/53-da9063-i-140     [002] ...1   102.709893: regmap_hw_read_start: 0-0058 reg=0 count=1
> (snip)
>  irq/53-da9063-i-140     [002] ...1   102.710371: regmap_hw_read_done: 0-0058 reg=0 count=1
>  irq/53-da9063-i-140     [002] ...1   102.710374: regmap_reg_read: 0-0058 reg=0 val=0
>  irq/53-da9063-i-140     [002] ...1   102.710378: regmap_hw_write_start: 0-0058 reg=6 count=1
> (snip)
>  irq/53-da9063-i-140     [002] ...1   102.710763: regmap_hw_write_done: 0-0058 reg=6 count=1
>  irq/53-da9063-i-140     [002] ...1   102.710767: regmap_reg_write: 0-0058 reg=6 val=1
>  irq/53-da9063-i-140     [002] ...1   102.710783: regmap_hw_read_start: 0-0058 reg=0 count=1
> (snip)
>  irq/53-da9063-i-140     [002] ...1   102.711268: regmap_hw_read_done: 0-0058 reg=0 count=1
>  irq/53-da9063-i-140     [002] ...1   102.711271: regmap_reg_read: 0-0058 reg=0 val=0
>  irq/53-da9063-i-140     [002] ...1   102.711274: regmap_hw_read_start: 0-0058 reg=1 count=1
> (snip)
>  irq/53-da9063-i-140     [002] ...1   102.711770: regmap_hw_read_done: 0-0058 reg=1 count=1
>  irq/53-da9063-i-140     [002] ...1   102.711773: regmap_reg_read: 0-0058 reg=1 val=0
>  irq/53-da9063-i-140     [002] d..3   102.712183: softirq_raise: vec=6 [action=TASKLET]
>      ksoftirqd/2-23      [002] ..s1   102.714162: softirq_entry: vec=6 [action=TASKLET]
>      ksoftirqd/2-23      [002] ..s1   102.714173: softirq_exit: vec=6 [action=TASKLET]
> 
> Regards,

I have just been told that someone has the same issue on the previous
sifive board (hifive unleashed) which uses the same GPIO and PLIC
modules, although they were using the 5.10.42 kernel.

So I would like to go forward with my RFC gpio patch,
  https://patchwork.kernel.org/project/linux-riscv/patch/8c36c1a28ce63b5120765fd3c636944bfec8bee9.1625882423.git.plr.vincent@gmail.com/
but before this if possible I would prefer to get feedback about the
PLIC:
Is the behaviour I describe above expected ?

I do not find anything in the PLIC documentations (neither riscv.org
nor sifive) which mention whether the PLIC gateways should be edge- or
level-sensitive.

I suspect in the sifive SoC it would be edge-sensitive whereas the
embedded GPIO chip would produce a level IRQ output, something like
  (pending && enabled)
so both GPIO-level sensitivity types would produce an edge output when
"pending" gets set, but level interrupts would not re-trigger. If this
is the case, then I believe my patch works around the issue: by
changing "enabled" at GPIO level it forces the generation of a new edge
even on level inputs.

But maybe I am missing something more fundamental about how the PLIC
should work.

Regards,
-- 
Vincent Pelletier
GPG fingerprint 983A E8B7 3B91 1598 7A92 3845 CAC9 3691 4257 B0C1



More information about the linux-riscv mailing list