PLIC and interrupts received while no hart enabled

Vincent Pelletier plr.vincent at gmail.com
Sat Jul 3 23:41:14 PDT 2021


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,
-- 
Vincent Pelletier
GPG fingerprint 983A E8B7 3B91 1598 7A92 3845 CAC9 3691 4257 B0C1
-------------- next part --------------
A non-text attachment was scrubbed...
Name: unmatched_gpio_irq_debug.py
Type: text/x-python
Size: 4619 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-riscv/attachments/20210704/d410225e/attachment.py>


More information about the linux-riscv mailing list