mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 01:04:41 +01:00
Documentation: gpio: pca953x: clarify interrupt source detection
There are multiple design tradeoffs and considerations in how the PCA953x driver detects the source(s) of an interrupt. This driver supports PCAL variants with input latching, a feature that is constrained by the fact that the interrupt status and input port registers cannot be read atomically. These limits and the design decisions deserve an in-depth explanation. Update the documentation to clarify these hardware limits and describe how the driver determines pending interrupts, and how it makes use of the PCAL input latching. Signed-off-by: Ernest Van Hoecke <ernest.vanhoecke@toradex.com> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Link: https://lore.kernel.org/r/20260107093125.4053468-1-ernestvanhoecke@gmail.com Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
This commit is contained in:
parent
da64eb5159
commit
8ba379879a
1 changed files with 75 additions and 0 deletions
|
|
@ -389,6 +389,13 @@ disabled.
|
|||
Currently the driver enables the latch for each line with interrupt
|
||||
enabled.
|
||||
|
||||
An interrupt status register records which pins triggered an interrupt.
|
||||
However, the status register and the input port register must be read
|
||||
separately; there is no atomic mechanism to read both simultaneously, so races
|
||||
are possible. Refer to the chapter `Interrupt source detection`_ to understand
|
||||
the implications of this and how the driver still makes use of the latching
|
||||
feature.
|
||||
|
||||
1. base offset 0x40, bank 2, bank offsets of 2^n
|
||||
- pcal6408
|
||||
- pcal6416
|
||||
|
|
@ -523,6 +530,74 @@ bits drive strength
|
|||
|
||||
Currently not supported by the driver.
|
||||
|
||||
Interrupt source detection
|
||||
==========================
|
||||
|
||||
When triggered by the GPIO expander's interrupt, the driver determines which
|
||||
IRQs are pending by reading the input port register.
|
||||
|
||||
To be able to filter on specific interrupt events for all compatible devices,
|
||||
the driver keeps track of the previous input state of the lines, and emits an
|
||||
IRQ only for the correct edge or level. This system works irrespective of the
|
||||
number of enabled interrupts. Events will not be missed even if they occur
|
||||
between the GPIO expander's interrupt and the actual I2C read. Edges could of
|
||||
course be missed if the related signal level changes back to the value
|
||||
previously saved by the driver before the I2C read. PCAL variants offer input
|
||||
latching for that reason.
|
||||
|
||||
PCAL input latching
|
||||
-------------------
|
||||
|
||||
The PCAL variants have an input latch and the driver enables this for all
|
||||
interrupt-enabled lines. The interrupt is then only cleared when the input port
|
||||
is read out. These variants provide an interrupt status register that records
|
||||
which pins triggered an interrupt, but the status and input registers cannot be
|
||||
read atomically. If another interrupt occurs on a different line after the
|
||||
status register has been read but before the input port register is sampled,
|
||||
that event will not be reflected in the earlier status snapshot, so relying
|
||||
solely on the interrupt status register is insufficient.
|
||||
|
||||
Thus, the PCAL variants also have to use the existing level-change logic.
|
||||
|
||||
For short pulses, the first edge is captured when the input register is read,
|
||||
but if the signal returns to its previous level before this read, the second
|
||||
edge is not observed. As a result, successive pulses can produce identical
|
||||
input values at read time and no level change is detected, causing interrupts
|
||||
to be missed. Below timing diagram shows this situation where the top signal is
|
||||
the input pin level and the bottom signal indicates the latched value::
|
||||
|
||||
─────┐ ┌──*───────────────┐ ┌──*─────────────────┐ ┌──*───
|
||||
│ │ . │ │ . │ │ .
|
||||
│ │ │ │ │ │ │ │ │
|
||||
└──*──┘ │ └──*──┘ │ └──*──┘ │
|
||||
Input │ │ │ │ │ │
|
||||
▼ │ ▼ │ ▼ │
|
||||
IRQ │ IRQ │ IRQ │
|
||||
. . .
|
||||
─────┐ .┌──────────────┐ .┌────────────────┐ .┌──
|
||||
│ │ │ │ │ │
|
||||
│ │ │ │ │ │
|
||||
└────────*┘ └────────*┘ └────────*┘
|
||||
Latched │ │ │
|
||||
▼ ▼ ▼
|
||||
READ 0 READ 0 READ 0
|
||||
NO CHANGE NO CHANGE
|
||||
|
||||
To deal with this, events indicated by the interrupt status register are merged
|
||||
with events detected through the existing level-change logic. As a result:
|
||||
|
||||
- short pulses, whose second edges are invisible, are detected via the
|
||||
interrupt status register, and
|
||||
- interrupts that occur between the status and input reads are still
|
||||
caught by the generic level-change logic.
|
||||
|
||||
Note that this is still best-effort: the status and input registers are read
|
||||
separately, and short pulses on other lines may occur in between those reads.
|
||||
Such pulses can still be latched as an interrupt without leaving an observable
|
||||
level change at read time, and may not be attributable to a specific edge. This
|
||||
does not reduce detection compared to the generic path, but reflects inherent
|
||||
atomicity limitations.
|
||||
|
||||
Datasheets
|
||||
==========
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue