drivers/ioexpander: List the registered pins in /proc/gpio - #19878
Merged
Conversation
Fishwaldo
requested review from
Donny9,
acassis,
davids5,
jerpelea,
michallenc,
raiden00pl and
xiaoxiang781216
as code owners
August 17, 2026 05:21
jerpelea
previously approved these changes
Aug 17, 2026
Fishwaldo
force-pushed
the
upstream-gpio-procfs
branch
from
August 17, 2026 11:32
ff3c276 to
d32e713
Compare
Fishwaldo
force-pushed
the
upstream-gpio-procfs
branch
from
August 17, 2026 12:23
d32e713 to
1735fa8
Compare
The pins a board publishes are visible in /dev and each can be read through its own node, but surveying a whole board that way means an open and two ioctls per pin, and the signal and interrupt counters the upper half keeps are not reachable through any of them. Adds a list of registered pins and publishes it as /proc/gpio, behind GPIO_PROCFS: a quality of life view of the same kind as /proc/pinctrl and /proc/reset. Every common field comes from state the upper half already holds: the pin type, the value through go_read(), how many times the pin has been registered for signals, and how many interrupts it has taken. Lines carry the same key:value tokens in the same order, so the file is machine parseable. Lower halves may supply an optional go_describe() adding what only they can say, such as which pad carries the line or how its trigger is configured. It writes text into a caller supplied buffer and the upper half owns the line, so a lower half needs no procfs knowledge. A lower half without it is listed with the common tokens alone. The pin type index is bounded before use: it comes from the lower half and the name table cannot cover a type the enumeration does not define. A pin that cannot be read reports val:- rather than a zero that would read as a real level. procfs_register() appends without checking for duplicates, so the entry is claimed once for the lifetime of the system rather than whenever the list is empty; pins come and go at run time. The name is held in a buffer as long as the one gpio_pin_register() accepts, so a listing always names the same string as /dev. The pin type name table is declared without an explicit size so that the assertion beside it compares against the enumeration and can fail; sized as [GPIO_NPINTYPES] it would have been tautological. Documents the entry, its tokens, and how a lower half describes a pin. Off by default: with GPIO_PROCFS unset the list, the lock and the procfs entry are compiled out, and go_describe() is one more member at the end of a structure existing lower halves do not reach. Assisted-by: Claude:claude-opus-5 Signed-off-by: Justin Hammond <justin@dynam.ac>
Fishwaldo
force-pushed
the
upstream-gpio-procfs
branch
from
August 18, 2026 04:01
1735fa8 to
79db3a6
Compare
xiaoxiang781216
approved these changes
Aug 18, 2026
fdcavalcanti
approved these changes
Aug 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
/proc/gpio, which lists every pin registered with the GPIO characterdriver, with its type, value and counters in one place.
The pins are already visible in
/devand each can be read through its ownnode with
GPIOC_READandGPIOC_PINTYPE. Surveying a whole board that waymeans an open and two ioctls per pin, and the signal and interrupt counters the
upper half keeps are not reachable through any ioctl at all. This is a quality
of life view of the same kind as
/proc/pinctrland/proc/reset.Every line carries the same
key:valuetokens in the same order, so the filecan be parsed as well as read. The common fields all come from state the upper
half already holds — the pin type, the value through
go_read(), and theexisting
register_countandint_count— so nothing is added to any hotpath.
Lower halves may supply an optional
go_describe()adding what only they cansay, such as which pad carries the line or how its trigger is armed:
It writes into a caller supplied buffer and the upper half owns the line, so a
lower half needs no procfs knowledge of its own. A lower half without it is
listed with the common tokens alone — the
gpio128line above is exactly thatcase, a TCA6416 expander pin.
Two existing hazards in the paths this touches are fixed as well.
procfs_register()appends without checking for duplicates, so the entry isnow claimed once for the lifetime of the system rather than whenever the list
is empty — pins genuinely come and go at run time. And the pin type index is
bounded before use, since it comes from the lower half and the name table
cannot cover a type the enumeration does not define.
Impact
New feature, off by default.
CONFIG_GPIO_PROCFSdepends onFS_PROCFS_REGISTER; with it unset the list, the lock and the procfs entry arecompiled out entirely.
struct gpio_operations_sgains one optional member at the end. Every existinglower half initialises that structure by name or by position without reaching
it, so none need updating. One small allocation per registered pin when the
option is on.
Worth knowing: the renderer calls
go_read()for every pin on everyread(),and procfs re-walks the list each time. For a memory mapped controller that is
a register read; for an I2C expander it is a bus transaction per pin per read.
Measured on the board below, with 32 of its 34 pins on TCA6416 expanders, one
catof the file cost 144 I2C reads. That is inherent to the procfspattern rather than new here, and caching would trade staleness for speed in a
view whose whole purpose is to be current.
Testing
Host: macOS 26.5.1 (arm64),
riscv-none-elf-gcc15.2.0, Sphinx 6.2.1.Build:
sim:nshwithDEV_GPIOalone, and again withFS_PROCFS,FS_PROCFS_REGISTERandGPIO_PROCFSadded, each from a clean tree: bothcompile
drivers/ioexpander/gpio.c. The EIC7700 EVB configuration builds withthe option on and off. Documentation builds with no new warnings.
Hardware: ESWIN EIC7700 EVB (EIC7700X, 4 x RV64GC), 34 pins registered: two
from the SoC's own GPIO lower half, which implements
go_describe, and 32 fromtwo TCA6416 I2C expanders, whose lower half does not.
Checked on that output:
line at any read boundary
awkpass countingtype:,val:,regs:andints:finds no line that deviatesgpio6andgpio28carry the SoC lowerhalf's
pad:andport:fields,gpio28being an interrupt pin, while theexpander pins from
gpio128up show the common tokens alonepad:SPI2_CS0_Nongpio6is correct: that pad is muxed to function 2,GPIO, which
/proc/pinctrlreports independently