Making Rockchip IO domains dependency from other devices explicit

From: Quentin Schulz
Date: Tue Jun 14 2022 - 08:42:57 EST


Hi all,

We have hit an issue for a product for which will send patches in the next few months and I'm asking for guidance on how to properly fix this in upstream Linux kernel (we are currently carrying a patch in our downstream U-Boot bootloader instead).

On some Rockchip SoCs, some SoC pins are split in what are called IO domains.

An IO domain is supplied power externally, by regulators from a PMIC for example. This external power supply is then used by the IO domain as "supply" for the IO pins if they are outputs.

Each IO domain can configure which voltage the IO pins will be operating on (1.8V or 3.0V). It is unclear if there's a voltage divider in IO domains for lowering from whatever the regulator is supplying to the supported 3.0V or 1.8V.

There already exists an IO domain driver for Rockchip SoCs[1]. This driver allows to explicit the relationship between the external power supplies and IO domains[2]. This makes sure the regulators are enabled by the Linux kernel so the IO domains are supplied with power.
This driver is a regulator consumer and does not offer any other interface for device dependency.
Currently, the IO pin 1.8/3.0V selection is made based on the voltage supported by the regulator attached to a specific IO domain.

And this is where the issue lies. The product has a camera sensor which didn't even appear on i2c until we figured out that its MCLK was actually oscillating between 0 and ~150mV. It appeared that after a soft reboot, the camera sensor would then appear on i2c. The driver of this camera sensor attempts an I2C transfer to check its ID, which fails the probe of the driver because the IO domain of the MCLK pin is not supplied with power yet/correctly configured (the io-domains driver probes after the camera driver). After configuring the IO domain correctly in the bootloader prior to booting the Linux kernel, the camera sensor is now working just fine.

In order to have my camera sensor working and not rely on what the bootloader set before Linux boots, I need to have the IO domain(s) correctly configured before the camera sensor driver probes. The issue is how to represent this dependency in the device tree.

It was suggested to me that we could use the power subsystem and use xxx-supply in DT for this, by making the io-domains driver expose a regulator per IO domain. This works fine if the IO pin connected to the device is already a power supply for that device, so we can just make this gpio-regulator depend on the regulator of the IO domain this IO pin is from. If it is not a regulator, this would mean we need to modify drivers to add a new power supply DT property for each driver. This can blow up pretty quickly and require, IMO, non-generic changes to driver to support SoC "quirks". Such would be the case for us, because I'd need to add this power supply DT property to the clock driver so that when the clock driver is probed, the regulators for the IO domains of the clocks exposed on IO pins are actually also enabled and the IO domains properly configured.

So I think the best way would be to have something transparent to the driver. Something handled on the subsystem level?

Looking a bit in the other kernel drivers to fish for inspiration and I stumbled upon pinctrl-sunxi driver.[3][4] This driver allows to specify a regulator as power supply per GPIO bank and will enable/disable it if there's a new user/none. The user would just need to add pinctrl-0/pinctrl-names to the device tree node of the device to both configure the GPIO correctly and enable the regulator for the bank this GPIO is attached to.

Technically, since the pinctrl subsystem has much of its behavior for consumers abstracted, this would make it a good fit for doing things transparently for drivers. The plan would be to have the IO domain driver expose pinctrl configuration nodes that are linked to a specific IO domain which would enable the appropriate regulator. I feel this is quite a reach/abuse of the pinctrl subsystem. Since the IO domain registers actually are configurable for 3.0V/1.8V, maybe this is actually "pin control" in the broad sense?

My suggestion would look like the following (based on rk3399-puma.dtsi):
'''
&io_domains {
status = "okay";
bt656-supply = <&vcc_1v8>;
audio-supply = <&vcc_1v8>;
sdmmc-supply = <&vcc_sd>;
gpio1830-supply = <&vcc_1v8>;

pinctrl_bt656_3v0: pinctrl-bt656-3v0 {
pins = "bt656";
function = "3v0";
};

pinctrl_bt656_1v8: pinctrl-bt656-1v8 {
pins = "bt656";
function = "1v8";
};
};

&camera {
[...]
pinctrl-0 = <&pinctrl_bt656_3v0>;
pinctrl-names = "default";
};
'''

We could also do checks in the driver so that the selected pinmux matches what the HW is capable of (e.g. if an 1.8V regulator supplies the IO domain and the pinctrl is requested for 3.0V it should fail). But that's just implementation details.

I'm looking for an upstreamable solution, would that fit the criteria?

Does anyone have something else to suggest or some feedback to give?

Feel free to correct me on my definitions or assumptions :)

I've added some contributors of this driver in Cc, I hope you don't mind.

Thanks,
Quentin

[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/soc/rockchip/io-domain.c
[2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/power/rockchip-io-domain.yaml
[3] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/pinctrl/sunxi/pinctrl-sunxi.c
[4] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/pinctrl/allwinner,sun4i-a10-pinctrl.yaml