Skip to content

adsp: fix the race between mmc0 and gpio-hogs - #3462

Merged
sipraga merged 15 commits into
adsp-6.18.31-yfrom
alvin/adsp/sc598-som-mmc-mux
Jul 28, 2026
Merged

adsp: fix the race between mmc0 and gpio-hogs#3462
sipraga merged 15 commits into
adsp-6.18.31-yfrom
alvin/adsp/sc598-som-mmc-mux

Conversation

@sipraga

@sipraga sipraga commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

PR Description

This is the second, more proper attempt to fix the issue originally identified in #3420 - namely, that the initialization of the MMC signal muxes can race with the actual probing of MMC. I noticed it when I was making unrelated changes in the kernel which likely triggered some different timings.

This time I have described the hardware properly, using gpio-mux and pinctrl-multiplexer. A more precise explanation of the changes can be found in the commit arm64: dts: adi: sc598-som: describe the MMC muxes faithfully.

You will notice a ton of seemingly unrelated commits. This is because our kernel doesn't have pinctrl-multiplexer. I chose to simply backport the required patches. They are not many, and all apply cleanly without any conflicts, meaning that a later rebase of the patch stack should make them disappear. I have annotated each commit with a small [alvin: upstream commit xxxx] just in case. I could have cherry-picked fewer patches if I were to edit the pinctrl-multiplexer driver, but that would make it both harder to rebase and introduce possible bugs. And there'd still be quite a few patches anyway.

Another alternative which would produce fewer patches is to modify the mmc driver to accept a mux-states DT property. But I wouldn't choose this path if our kernel were newer, so I elected not to do that, as it will just make more work for us in the future after updating the kernel.

One thing I don't like about these changes is that I had to basically copy-paste the same stuff into som-revD and som-revE. Any suggestions for a better approach here are welcome. I know I could sneak it into the som.dtsi only once and it would still compile, but I dislike the idea of referencing the non-existent som_gpio_expander label in that file.

PR Type

  • Bug fix (a change that fixes an issue)
  • New feature (a change that adds new functionality)
  • Breaking change (a change that affects other repos or cause CIs to fail)

PR Checklist

  • I have conducted a self-review of my own code changes
  • I have compiled my changes, including the documentation
  • I have tested the changes on the relevant hardware
  • I have updated the documentation outside this repo accordingly
  • I have provided links for the relevant upstream lore

Add a generic function to allow creation of groups and functions at
runtime based on devicetree content, before setting up mux mappings.
It works similarly to pinconf_generic_dt_node_to_map(), and
therefore parses pinconf properties and maps those too, allowing it
to be used as the dt_node_to_map member of the pinctrl_ops struct.

Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Linus Walleij <linusw@kernel.org>
[alvin: upstream commit 4372257 applied with no conflicts]
Signed-off-by: Alvin Šipraga <alvin.sipraga@analog.com>
Josua-SR and others added 12 commits July 27, 2026 16:30
Rename the temporary devm_mux_state_get_optional function to avoid
conflict with upcoming implementation in multiplexer subsystem.

Signed-off-by: Josua Mayer <josua@solid-run.com>
Acked-by: Vinod Koul <vkoul@kernel.org>
Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
[alvin: upstream commit b6376cc applied with no conflicts]
Signed-off-by: Alvin Šipraga <alvin.sipraga@analog.com>
In-tree phy-can-transceiver and phy_rcar_gen3_usb2 have already
implemented local versions of devm_mux_state_get_optional.

The omap-i2c driver gets and selects an optional mux in its probe
function without using any helper.

Add new helper functions covering both aforementioned use-cases:

- mux_control_get_optional:
  Get a mux-control if specified in dt, return NULL otherwise.
- devm_mux_state_get_optional:
  Get a mux-state if specified in dt, return NULL otherwise.
- devm_mux_state_get_selected:
  Get and select a mux-state specified in dt, return error otherwise.
- devm_mux_state_get_optional_selected:
  Get and select a mux-state if specified in dt, return error or NULL.

Existing mux_get helper function is changed to take an extra argument
indicating whether the mux is optional.
In this case no error is printed, and NULL returned in case of ENOENT.

Calling code is adapted to handle NULL return case, and to pass optional
argument as required.

To support automatic deselect for _selected helper, a new structure is
created storing an exit pointer similar to clock core which is called on
release.

To facilitate code sharing between optional/mandatory/selected helpers,
a new internal helper function is added to handle quiet (optional) and
verbose (mandatory) errors, as well as storing the correct callback for
devm release: __devm_mux_state_get

Due to this structure devm_mux_state_get_*_selected can no longer print
a useful error message when select fails. Instead callers should print
errors where needed.

Commit e153fde ("phy: can-transceiver: Re-instate "mux-states"
property presence check") noted that "mux_get() always prints an error
message in case of an error, including when the property is not present,
confusing the user."

The first error message covers the case that a mux name is not matched
in dt. The second error message is based on of_parse_phandle_with_args
return value.

In optional case no error is printed and NULL is returned.
This ensures that the new helper functions will not confuse the user
either.

With the addition of optional helper functions it became clear that
drivers should compile and link even if CONFIG_MULTIPLEXER was not enabled.
Add stubs for all symbols exported by mux core.

Acked-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Signed-off-by: Josua Mayer <josua@solid-run.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
[alvin: upstream commit 993bcaf applied with no conflicts]
Signed-off-by: Alvin Šipraga <alvin.sipraga@analog.com>
Multiplexer subsystem has now added helpers for getting managed optional
mux-state.

Switch to the new devm_mux_state_get_optional helper.

This change is only compile-tested.

Acked-by: Vinod Koul <vkoul@kernel.org>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Signed-off-by: Josua Mayer <josua@solid-run.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
[alvin: upstream commit d76e0c5 applied with no conflicts]
Signed-off-by: Alvin Šipraga <alvin.sipraga@analog.com>
Add new API devm_mux_state_get_from_np() to retrieve a mux control from
a specified child device node.

Make devm_mux_state_get() call devm_mux_state_get_from_np() with a NULL
node parameter, which defaults to using the device's own of_node.

Support the following DT schema:

pinctrl@0 {
    uart-func {
            mux-state = <&mux_chip 0>;
    };

    spi-func {
            mux-state = <&mux_chip 1>;
    };
};

Signed-off-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Linus Walleij <linusw@kernel.org>
[alvin: upstream commit 05e58da applied with no conflicts]
Signed-off-by: Alvin Šipraga <alvin.sipraga@analog.com>
Add a generic pinctrl binding for board-level pinmux chips that are
controlled through the multiplexer subsystem.

On some boards, especially development boards, external mux chips are used
to switch SoC signals between different peripherals (e.g. MMC and UART).
The mux select lines are often driven by a GPIO expander over I2C,
as illustrated below:

	┌──────┐      ┌─────┐
	│ SOC  │      │     │    ┌───────┐
	│      │      │     │───►│ MMC   │
	│      │      │ MUX │    └───────┘
	│      ├─────►│     │    ┌───────┐
	│      │      │     │───►│ UART  │
	│      │      └─────┘    └───────┘
	│      │         ▲
	│      │    ┌────┴──────────────┐
	│ I2C  ├───►│ GPIO Expander     │
	└──────┘    └───────────────────┘

Traditionally, gpio-hog is used to configure the onboard mux at boot.
However, the GPIO expander may probe later than consumer devices such as
MMC. As a result, the MUX might not be configured when the peripheral
driver probes, leading to initialization failures or data transfer errors.

Introduce a generic pinctrl binding that models the board-level MUX as a
pin control provider and builds proper device links between the MUX, its
GPIO controller, and peripheral devices. This ensures correct probe
ordering and reliable mux configuration.

The implementation leverages the standard multiplexer subsystem, which
provides broad support for onboard mux controllers and avoids the need for
per-driver custom MUX handling.

Allow pinctrl-* pattern as node name because this pinctrl device have not
reg property.

Reviewed-by: Linus Walleij <linusw@kernel.org>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Linus Walleij <linusw@kernel.org>
[alvin: upstream commit 9e5e1de applied with no conflicts]
Signed-off-by: Alvin Šipraga <alvin.sipraga@analog.com>
…unction_dt_node_to_map()

Refactor pinctrl_generic_pins_function_dt_subnode_to_map() by separating DT
parsing logic from map creation. Introduce a new helper
pinctrl_generic_to_map() to handle mapping to kernel data structures, while
keeping DT property parsing in the subnode function.

Improve code structure and enables easier reuse for platforms using
different DT properties (e.g. pinmux) without modifying the
dt_node_to_map-style callback API. Avoid unnecessary coupling to
pinctrl_generic_pins_function_dt_node_to_map(), which provides
functionality not needed when the phandle target is unambiguous.

Maximize code reuse and provide a cleaner extension point for future
pinctrl drivers.

Suggested-by: Conor Dooley <conor.dooley@microchip.com>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Linus Walleij <linusw@kernel.org>
[alvin: upstream commit aaaf31b applied with no conflicts]
Signed-off-by: Alvin Šipraga <alvin.sipraga@analog.com>
Add an optional .release_mux() callback to struct pinmux_ops.

Some drivers acquire additional resources in .set_mux(), such as software
locks. These resources may need to be released when the mux function is no
longer active. Introducing a dedicated .release_mux() callback allows
drivers to clean up such resources.

The callback is optional and does not affect existing drivers.

Commit 2243a87 ("pinctrl: avoid duplicated calling
enable_pinmux_setting for a pin") removed the .disable() callback
to resolve two issues:

  1. desc->mux_usecount increasing monotonically
  2. Hardware glitches caused by repeated .disable()/.enable() calls

Adding .release_mux() does not reintroduce those problems. The callback is
intended only for releasing driver-side resources (e.g. locks) and must not
modify hardware registers.

Signed-off-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Linus Walleij <linusw@kernel.org>
[alvin: upstream commit 418a2bb applied with no conflicts]
Signed-off-by: Alvin Šipraga <alvin.sipraga@analog.com>
Many boards use on-board mux chips (often controlled by GPIOs from an I2C
expander) to switch shared signals between peripherals.

Add a generic pinctrl driver built on top of the mux framework to
centralize mux handling and avoid probe ordering issues. Keep board-level
routing out of individual drivers and supports boot-time only mux
selection.

Ensure correct probe ordering, especially when the GPIO expander is probed
later.

Signed-off-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Linus Walleij <linusw@kernel.org>
[alvin: upstream commit 34acc5a applied with no conflicts]
Signed-off-by: Alvin Šipraga <alvin.sipraga@analog.com>
CONFIG_MULTIPLEXER can only be enabled indirectly. If it's not already
enabled, then CONFIG_PINCTRL_GENERIC_MUX cannot be selected at all in
menuconfig. That's because it depends on MULTIPLEXER.

Follow similar consumers of the multiplexer core and select MULTIPLEXER
instead. This way the driver will be visible in menuconfig as long as
PINCTRL is selected.

Fixes: 34acc5a ("pinctrl: add generic board-level pinctrl driver using mux framework")
[alvin: submitted upstream, see Link]
Link: https://lore.kernel.org/all/20260727-pinctrl-generic-mux-fix-kconfig-v1-1-5abebd6f1a8d@analog.com/
Signed-off-by: Alvin Šipraga <alvin.sipraga@analog.com>
Just like on EZKIT, it's using the SoM's eMMC which is non-removable.

Signed-off-by: Alvin Šipraga <alvin.sipraga@analog.com>
Since the consumers of the SoM's dtsi are the ones describing how the
interface is set up (eMMC or SD card), they ought to enable explicitly.
For HTOL it's then already disabled, so the lines can just be dropped.

Signed-off-by: Alvin Šipraga <alvin.sipraga@analog.com>
The EV-SC598-SOM has several switches controlled by the MMC_EN* and
MMC_SOM_EN* signals and through which the SoC's MSI0 pins are routed.
This "soft config" allows for the signals either to be terminated,
routed to the on-SoM eMMC, or routed through the SoM board-to-board
connector into the carrier board. The whole network is essentially a
mux. Describe it as such, with states 0, 1, and 2 respectively.

Since this is not only a mux, but a pin mux, the generic pinctrl
multiplexer can also be employed to expose the valid states (1 and 2) as
consumable pinctrl groups. Define the two groups, mmc_som_grp and
mmc_carrier_grp, meaning "route the MMC pins to the SoM (resp. carrier
board)".

Doing so allows for a race-free initialization of the pins before the
MMC is probed. For each use-case in the downstream device trees for
EZKIT and EZLITE, select the appropriate pinctrl group in &mmc0's
pinctrl-0. In turn, drop the racy gpio-hogs.

Signed-off-by: Alvin Šipraga <alvin.sipraga@analog.com>
@sipraga
sipraga force-pushed the alvin/adsp/sc598-som-mmc-mux branch from b239c1a to 56cf224 Compare July 27, 2026 14:40
@sipraga

sipraga commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

Hmm, @gastmaier could you give me any pointers in how to fix this CI build failure? I don't understand the error, it seems spurious to me.

The last time I looked into the CI build, it seemed it was using a different defconfig (#3409 (comment))

But that doesn't make sense to me for this either... do you know what the actual defconfig is that it's using?

@gastmaier

Copy link
Copy Markdown
Collaborator

Hi @sipraga , I couldn't immediately replicate except this hint:

WARNING: unmet direct dependencies detected for GENERIC_PINCTRL
  Depends on [n]: PINCTRL [=y] && GENERIC_PINCONF [=n] && GENERIC_PINCTRL_GROUPS [=n] && GENERIC_PINMUX_FUNCTIONS [=n]
  Selected by [y]:
  - PINCTRL_GENERIC_MUX [=y] && PINCTRL [=y]

WARNING: unmet direct dependencies detected for GENERIC_PINCTRL
  Depends on [n]: PINCTRL [=y] && GENERIC_PINCONF [=n] && GENERIC_PINCTRL_GROUPS [=n] && GENERIC_PINMUX_FUNCTIONS [=n]
  Selected by [m]:
  - PINCTRL_GENERIC_MUX [=m] && PINCTRL [=y]

oddly, the missing symbol is from core.c, a -y source file, while the suggested requires extra symbol.

I believe you can safely ignore, and the fix would be pre-enabling the PINCTRL in the adi_ci_defconfig.

@sipraga

sipraga commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Hi @sipraga , I couldn't immediately replicate except this hint:

WARNING: unmet direct dependencies detected for GENERIC_PINCTRL
  Depends on [n]: PINCTRL [=y] && GENERIC_PINCONF [=n] && GENERIC_PINCTRL_GROUPS [=n] && GENERIC_PINMUX_FUNCTIONS [=n]
  Selected by [y]:
  - PINCTRL_GENERIC_MUX [=y] && PINCTRL [=y]

WARNING: unmet direct dependencies detected for GENERIC_PINCTRL
  Depends on [n]: PINCTRL [=y] && GENERIC_PINCONF [=n] && GENERIC_PINCTRL_GROUPS [=n] && GENERIC_PINMUX_FUNCTIONS [=n]
  Selected by [m]:
  - PINCTRL_GENERIC_MUX [=m] && PINCTRL [=y]

oddly, the missing symbol is from core.c, a -y source file, while the suggested requires extra symbol.

I believe you can safely ignore, and the fix would be pre-enabling the PINCTRL in the adi_ci_defconfig.

@gastmaier thanks a lot for taking a look. I still don't understand what the CI is actually running/building in order to produce this error (and the other). How did you reproduce it on your end?

@nunojsa nunojsa left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naturally there are some backports and an increase of the adsp patchset but I'm not going to debate on that :)! To me, the whole thing makes sense.

CONFIG_USB_EHCI_HCD_PLATFORM=y
CONFIG_USB_OHCI_HCD=y
CONFIG_USB_OHCI_HCD_PLATFORM=y
CONFIG_USB_STORAGE=y

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm seeing some unrelated stuff being enabled and changed! I might guess the reason for it but mentioning in the commit message would still make sense.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yikes! No, that was just a stupid mistake. Thanks for catching it! I have pushed the original commit now.

@sipraga
sipraga force-pushed the alvin/adsp/sc598-som-mmc-mux branch from 56cf224 to a3043f7 Compare July 28, 2026 09:21
@sipraga

sipraga commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @nunojsa for the review. I still don't want to merge until I have figured out the CI stuff though

@pamolloy

Copy link
Copy Markdown
Collaborator

Thanks @nunojsa for the review. I still don't want to merge until I have figured out the CI stuff though

I'm sure Jorge will follow-up, but in case it helps you should be able to reproduce it locally as follows:

https://analogdevicesinc.github.io/linux/ci.html#build-the-container-image

Also a good opportunity to check the documentation and see if anything can be made more clear. 😄

@gastmaier

Copy link
Copy Markdown
Collaborator

@sipraga all ci/cd steps are reproducible locally with a little of bootstrapping

sc598-som-mmc-mux$ cr adi/linux:v4
Sourced methods from '/mnt/wsl/data/repos/linux/_ci/ci/build.sh':
check_checkpatch                compile_many_devicetrees        compile_clang_analyzer          get_adi_ci_defconfig
check_dt_binding_check          prepare_compile_kernel          assert_compiled                 merge_latest_stable
check_coccicheck                compile_kernel                  apply_prerun                    set_step_warn
check_license                   compile_kernel_sparse           auto_set_kconfig                set_step_fail
check_assert_defconfigs         compile_kernel_smatch           set_arch
compile_devicetree              compile_gcc_fanalyzer           ensure_compiler
The git commit range was set to:
$base_sha..$head_sha: @~6..@
Adjust '$base_sha' and '$head_sha' to your work range.
Directory: /mnt/wsl/data/repos/linux/alvin/adsp/sc598-som-mmc-mux
Mon Jul 27 14:59:25 UTC 2026

runner@9624a5fb2cbf:/mnt/wsl/data/repos/linux/alvin/adsp/sc598-som-mmc-mux> set_arch gcc_arm
CXX=gcc-13
ARCH=arm
CROSS_COMPILE=arm-linux-
runner@9624a5fb2cbf:/mnt/wsl/data/repos/linux/alvin/adsp/sc598-som-mmc-mux> base_sha=1dbb0de5a768ea5865f088a9f5abe80dd960dc14~
runner@9624a5fb2cbf:/mnt/wsl/data/repos/linux/alvin/adsp/sc598-som-mmc-mux> get_adi_ci_defconfig
runner@9624a5fb2cbf:/mnt/wsl/data/repos/linux/alvin/adsp/sc598-som-mmc-mux> auto_set_kconfig
Symbols of touched files:
{'GENERIC_PINCTRL', 'PHY_CAN_TRANSCEIVER', 'PINMUX', 'PINCTRL_GENERIC_MUX', 'MULTIPLEXER'}
Resolved symbols:
{'I2C_MUX', 'PHYLIB', 'PINCTRL', 'IIO_MUX', 'OF', 'SPI', 'SPI_MUX', 'PHY_AM654_SERDES', 'ALPHA', 'PINMUX', 'MEDIA_PLATFORM_SUPPORT', 'NETDEVICES', 'PCI', 'MTD_ROM', 'MTD_CFI', 'PHY_CAN_TRANSCEIVER', 'MTD_LPDDR', 'VIDEO_MUX', 'DRM_NWL_MIPI_DSI', 'MTD_JEDECPROBE', 'MEDIA_SUPPORT', 'MDIO_BUS_MUX_MULTIPLEXER', 'SPI_DW_BT1', 'MTD_HYPERBUS', 'MMU', 'MTD_PHYSMAP_BT1_ROM', 'PARISC', 'MEDIA_PLATFORM_DRIVERS', 'MTD_PHYSMAP_OF', 'MTD_RAM', 'NET', 'I2C_OMAP', 'DRM', 'AGP', 'MTD', 'HBMC_AM654', 'I2C_MUX_GPMUX', 'VIDEO_DEV', 'COMPILE_TEST', 'I2C', 'PHY_J721E_WIZ', 'MIPS_BAIKAL_T1', 'IIO', 'V4L_PLATFORM_DRIVERS', 'SPI_DESIGNWARE', 'PPC', 'COMMON_CLK', 'PINCTRL_GENERIC_MUX', 'MTD_PHYSMAP'}
make[1]: Entering directory '/mnt/wsl/data/repos/linux/alvin/adsp/sc598-som-mmc-mux/build'
runner@9624a5fb2cbf:/mnt/wsl/data/repos/linux/alvin/adsp/sc598-som-mmc-mux> compile_kernel

https://analogdevicesinc.github.io/linux/ci

@sipraga

sipraga commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Thanks, I was able to get into that same shell after building the CI container and running it with the container-run.sh script (I failed to make sense of how to run it manually). I then ran a few times and understand now that the build failure is the result of a kconfig misconfiguration. I eventually gave up trying to figure out how that config is actually assembled though... In general it shouldn't be possible to select GENERIC_PINCTRL if it has unmet dependencies, so I agree with you Jorge that the failure can be ignored. :-)

Maybe the fix is indeed to add CONFIG_PINCTRL to the adi_ci_defconfig, but I still do not understand really how this works, so I am reluctant to make changes I don't understand.

Anyway thanx for the pointers, will help me in future!

They are needed to control the SoM's MMC mux.

Signed-off-by: Alvin Šipraga <alvin.sipraga@analog.com>
They're needed to control the SoM's MMC mux.

Signed-off-by: Alvin Šipraga <alvin.sipraga@analog.com>
@sipraga
sipraga force-pushed the alvin/adsp/sc598-som-mmc-mux branch from a3043f7 to f26e2be Compare July 28, 2026 12:57
@sipraga

sipraga commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Tested on sc598-som-ezkit, both eMMC and SD-card device trees. Verified also the consumer/supplier dependency chain, and that mmc returns -EPROBE_DEFER if the mux isn't available yet (e.g. CONFIG_MUX_GPIO=n ;-)).

Ignoring CI per above discussion.

@sipraga
sipraga merged commit e2e5009 into adsp-6.18.31-y Jul 28, 2026
11 of 14 checks passed
@sipraga
sipraga deleted the alvin/adsp/sc598-som-mmc-mux branch July 28, 2026 13:21
@github-project-automation github-project-automation Bot moved this to Done in ADSP Jul 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

7 participants