[PATCH v2 0/4] pinctrl: qcom: spmi-gpio: Add bidirectional level-shifter function support

From: Fenglin Wu

Date: Thu May 28 2026 - 21:05:54 EST


The PMH0101 PMIC introduces BIDIR_LVL_SHIFTER modules that provide
bidirectional voltage translation between 1.2 V and 1.8 V power
domains, targeting open-drain signal buses such as I2C. Each level
shifter shares its two physical pins with a corresponding pair of GPIO
modules, and its enable state is centrally managed by AOP firmware as
a shared RPMh "XOB" resource.

This series adds kernel support for controlling these level shifters
through the pinctrl subsystem. Patches are ordered from infrastructure
to driver to bindings:

Patch 1 (soc: qcom: rpmh) extends the RPMh driver to accept write
commands from devices that are not children of the RPMh controller.
The existing write path assumes the caller is a child of the RSC device;
however, the SPMI GPIO driver sits under the SPMI controller bus. Two
new APIs are introduced — rpmh_get_ctrlr_dev() for resolving the
controller from a "qcom,rpmh" DT phandle, and rpmh_write_ctrlr() /
rpmh_write_async_ctrlr() which accept the controller device pointer
directly rather than deriving it through the device parent chain.

Patch 2 (dt-bindings) documents the new "level-shifter" function, the
qcom,1p2v-1p8v-ls-en property, the qcom,rpmh phandle, and the
qcom,pmic-id string required on pmh0101 nodes. The pmh0101 conditional
block is split out from pmih0108 and given its own required properties.

Patch 3 (pinctrl: qcom: spmi-gpio, rearchitect) refactors the driver's
group and function registration to use the generic pinctrl group and
function APIs (pinctrl_generic_add_group, pinmux_generic_add_function).
The previous design treated every pin as its own group with access to
all functions. The new design allows multi-pin groups with restricted
function sets, which is a prerequisite for exposing the level-shifter
function that is tied to specific GPIO pairs only.

Patch 4 (pinctrl: qcom: spmi-gpio, level-shifter) builds on the above
to introduce the "level-shifter" function. When selected, both GPIO
pads in the pair are disabled (high-impedance), and the RPMh XOB vote
for the level shifter is controlled separately via the new
qcom,1p2v-1p8v-ls-en pinconf parameter, allowing enable and disable
to be represented as distinct pinctrl states with the same function and
group.

With all these changes, the BIDIR_LVL_SHIFTER in PMH0101 could be
controlled with following settings:

&pmh0101_gpios {
qcom,rpmh = <&apps_rsc>;
qcom,pmic-id = "B_E0";

pmh0101-ls1-en {
groups = "gpio11, gpio12";
function = "level-shifter";
qcom,1p2v-1p8v-ls-en = <1>;
};

pmh0101-ls1-dis {
groups = "gpio11, gpio12";
function = "level-shifter";
qcom,1p2v-1p8v-ls-en = <0>;
};
};

Signed-off-by: Fenglin Wu <fenglin.wu@xxxxxxxxxxxxxxxx>
---
Changes in v2:
Add following change to address review comments from Sashiko AI:

- PATCH 1, in drivers/soc/qcom/rpmh.c
1. Add rpmh_put_device() wrapper to avoid casting put_device directly to
void (*)(void *) in devm_add_action_or_reset(), which would trigger a
CFI kernel panic on indirect call type mismatch.
2. Add device_link_add() with DL_FLAG_AUTOREMOVE_CONSUMER | DL_FLAG_PM_RUNTIME
in rpmh_get_ctrlr_dev() to enforce probe/remove ordering and runtime PM
suspend/resume ordering between the RPMH controller and non-child consumer
devices, preventing use-after-free and hardware state machine violations on unbind.

- PATCH 2, in Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.yaml
1. Make qcom,rpmh and qcom,pmic-id optional for qcom,pmh0101-gpio by removing
them from the required list in the if/then conditional block. Update property
descriptions to clarify they are only needed when the level-shifter function is used.

- PATCH 3, in drivers/pinctrl/qcom/Kconfig
1. Add select GENERIC_PINCTRL_GROUPS and select GENERIC_PINMUX_FUNCTIONS to
PINCTRL_QCOM_SPMI_PMIC to declare explicit dependencies on the generic pinctrl
APIs (pinctrl_generic_add_group(), pinmux_generic_add_function(), etc.) used by the driver.

- PATCH 3, in drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
1. Switch from devm_pinctrl_register() to devm_pinctrl_register_and_init() + pinctrl_enable()
to defer hog application until after all pin groups, functions, are fully registered,
fixing a probe sequence bug where hogs would be applied against an incomplete pinctrl state.

- PATCH 4, in drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
1. Guard pad->is_enabled = true in pmic_gpio_pinconf_pin_set() to skip the assignment
when pad->function == PMIC_GPIO_FUNC_INDEX_LEVEL_SHIFTER, preventing pinconf
application from re-enabling GPIO pads that were explicitly disabled by the
level-shifter mux path.
2. Call cmd_db_ready() before cmd_db_read_addr() in pmic_gpio_register_level_shifters()
to propagate -EPROBE_DEFER when the command DB is not yet initialized, instead of
incorrectly returning -ENODEV which causes a permanent probe failure.
3. Add qcom,rpmh / qcom,pmic-id DT property check before calling pmic_gpio_register_level_shifters()
add make the level shifter function registration optional to match with the DT binding change.

- Link to v1: https://patch.msgid.link/20260527-pinctrl-level-shifter-v1-0-1965461d0a7c@xxxxxxxxxxxxxxxx

---
Fenglin Wu (4):
soc: qcom: rpmh: Allow non-child devices to issue write commands
dt-bindings: pinctrl: qcom,pmic-gpio: Add level-shifter function
pinctrl: qcom: spmi-gpio: Rearchitect for flexible group support
pinctrl: qcom: spmi-gpio: Add level-shifter function support

.../bindings/pinctrl/qcom,pmic-gpio.yaml | 66 +-
drivers/pinctrl/qcom/Kconfig | 2 +
drivers/pinctrl/qcom/pinctrl-spmi-gpio.c | 796 ++++++++++++++++-----
drivers/soc/qcom/rpmh.c | 173 ++++-
include/dt-bindings/pinctrl/qcom,pmic-gpio.h | 1 +
include/soc/qcom/rpmh.h | 21 +
6 files changed, 845 insertions(+), 214 deletions(-)
---
base-commit: b8f192cec7dcb2e4f04ee57ab78d51777b0a5729
change-id: 20260527-pinctrl-level-shifter-929b286f583d

Best regards,
--
Fenglin Wu <fenglin.wu@xxxxxxxxxxxxxxxx>