Turn on GPIO at boot

Hello community!

I’m trying to figure out, how to turn on GPIOs at boot, but cannot seem to find anything about it. Neither documentation nor guides.

More or less any reference that can be found is about the Raspberry Pi.

From what I’ve learned the /boot/config.txt files are rather analogous to each other on Raspberry’s and Rock SBCs.

However running Debian on my Rock 5B, there isn’t even a /boot/config.txt file available, even though it’s explicitly supported. I already tried Radxa Debian r39 as well as one with the latest upstream kernel (6.6).

So basically my questions are:
Is the /boot/config.txt file even the right place to look?
How would I turn on for instance the physical PIN_40 at boot?

Best Rastafabi

(I don’t want anything at the OS level like systemd or local.rc – This needs to work at firmware level before the kernel boots.)

@RadxaYuntian can you give some advices?

1 Like

Whether a GPIO is enabled on boot is hardware defined, so we can’t change that.

The next best thing is to change it in firmware, which is closed source, so we can’t change that.

Then it has to be done at U-Boot/Linux level, which you said is not OK.

If you have to have a GPIO pin at a specific state on-boot, you have to adjust your design to use a pin with your desired behavior. Currently it is not possible to change it from software.

wiki.radxa.com is also deprecated with outdated info. Please visit our new documentation site.

Hello @RadxaYuntian Thank you for the detailed reply.

How could I proceed, to change GPIO at the earliest point during the boot process. I presume this would be u-boot? Is it possible via some kind of config file or an DT overlay? If so, how can it be achieved?

The problem is that when Linux is booted, it will reinitialize the GPIO controller, so not only you need to configure the GPIO on both U-Boot and Linux, your connected device might see a sudden logic level flip when Linux is loaded, which might trigger unintended action.

In that case the best method appears to be handling the GPIOs by kernel. Do you have any probable guess how much delay would this involve to get from power on to GPIO interaction? Would systemd in that case be the earliest point to execute the desired GPIO function? Or shall that rather be implemented using a DT overlay?
Would this be a good start for the latter one?

Thank you very much for your support.

Boot your system and run sudo dmesg as soon as the login shell is available. The first line contains the timestamp counted since power on, and that’s a good estimate of when the GPIO state will be updated, since device initialization happens shortly after that.

Overlay is the preferred way. It initialize earlier than userspace (a.k.a systemd) and there is less likelihood of being disabled accidentally.

That is for 1-Wire protocol which is not what you want, although you could use it as the boilerplate template. Replace line 20-24 with something like this, which defines a dummy GPIO regulator that is always on. You need to change the name to not interference the existing node, and adjust the GPIO definition to match the one you want.

The example above pull the GPIO high. If you want to pull it low you need to remove enable-active-high line due to regulator-fixed's quirkiness.

If you need to adjust the state of GPIO at run time, you may want to use either use gpio-leds binding and adjust the trigger from default-on to none, which is cumbersome to control with a program, or you have to use a systemd service and control it with standard GPIO API instead of fake devices/

I got as far as to write two different .dts which I compiled. However I couldn’t get those to apply (manually editing /boot/extlinux/extlinux.conf and adding fdtoverlay /boot/overlay.DTBO.dtbo). The kernel would just fail to boot with this modification. Altering the .dtb directly by just attaching the modifications to the end of the file didn’t help either, as apparently the GPIO enumeration has changes upstream.
That’s on me though, as I’m running Debian Bookworm with kernel 6.7 rc1. I plan to try using Armbian with a 6.2 kernel build and rsetup, but currently with only one SD card I’ll postpone further testing.

At least I got to better understand the inner workings of those device-tree files and re-added GPIO PIN definitions. I’m content with that, as this was my first encounter with those mechanics.

1 Like

rsetup is unlikely to work 100% correctly on non Radxa systems though, since it is very tightly coupled to our specific system configuration.

1 Like

I finally managed to apply a device tree overlay on the latest current upstream kernel. I’m still facing issues, though. While I managed to extract the previously mentioned GPIO-line-name modification into a .dts file, I couldn’t address the GPIO with the &gpioX handler but needed to use the gpio@00000000 addressing scheme.

rk3588-rock-5b-gpio-line-names.dts
/dts-v1/;
/plugin/;

/ {
	compatible = "radxa,rock-5b", "rockchip,rk3588";
};

&{/} {
	pinctrl {
gpio@fd8a0000 {
	gpio-line-names =
		/* GPIO0_A0-A3 */
		"", "", "", "",
		/* GPIO0_A4-A7 */
		"", "", "", "",

		/* GPIO0_B0-B3 */
		"", "", "", "",
		/* GPIO0_B4-B7 */
		"", "PIN_8", "PIN_10", "",

		/* GPIO0_C0-C3 */
		"", "", "", "",
		/* GPIO0_C4-C7 */
		"", "", "", "",

		/* GPIO0_D0-D3 */
		"", "", "", "",
		/* GPIO0_D4-D7 */
		"", "", "", "";
};

gpio@fec20000 {
	gpio-line-names =
		/* GPIO1_A0-A3 */
		"", "", "", "",
		/* GPIO1_A4-A7 */
		"", "", "", "",

		/* GPIO1_B0-B3 */
		"", "PIN_21", "PIN_19", "PIN_23",
		/* GPIO1_B4-B7 */
		"PIN_24", "PIN_26", "", "PIN_31",

		/* GPIO1_C0-C3 */
		"", "", "", "",
		/* GPIO1_C4-C7 */
		"", "", "", "",

		/* GPIO1_D0-D3 */
		"", "", "", "",
		/* GPIO1_D4-D7 */
		"", "", "", "PIN_29";
};

gpio@fec30000 {
	gpio-line-names =
		/* GPIO2_A0-A3 */
		"", "", "", "",
		/* GPIO2_A4-A7 */
		"", "", "", "",

		/* GPIO2_B0-B3 */
		"", "", "", "",
		/* GPIO2_B4-B7 */
		"", "", "", "",

		/* GPIO2_C0-C3 */
		"", "", "", "",
		/* GPIO2_C4-C7 */
		"", "", "", "",

		/* GPIO2_D0-D3 */
		"", "", "", "",
		/* GPIO2_D4-D7 */
		"", "", "", "";
};

gpio@fec40000 {
	gpio-line-names =
		/* GPIO3_A0-A3 */
		"", "", "", "",
		/* GPIO3_A4-A7 */
		"PIN_16", "", "", "PIN_33",

		/* GPIO3_B0-B3 */
		"", "PIN_36", "PIN_38", "PIN_40",
		/* GPIO3_B4-B7 */
		"", "PIN_12", "PIN_35", "PIN_13",

		/* GPIO3_C0-C3 */
		"PIN_15", "PIN_11", "PIN_32", "PIN_7",
		/* GPIO3_C4-C7 */
		"", "", "", "",

		/* GPIO3_D0-D3 */
		"", "", "", "",
		/* GPIO3_D4-D7 */
		"", "", "", "";
};

gpio@fec50000 {

	gpio-line-names =
		/* GPIO4_A0-A3 */
		"", "", "", "",
		/* GPIO4_A4-A7 */
		"", "", "", "",

		/* GPIO4_B0-B3 */
		"", "", "PIN_5", "PIN_3",
		/* GPIO4_B4-B7 */
		"", "", "", "",

		/* GPIO4_C0-C3 */
		"", "", "", "",
		/* GPIO4_C4-C7 */
		"PIN_18", "PIN_28", "PIN_27", "",

		/* GPIO4_D0-D3 */
		"", "", "", "",
		/* GPIO4_D4-D7 */
		"", "", "", "";
		};
	};
};

While this finally worked I cannot seem to get anything running which relies on a gpio = <&gpioX … configuration – not even than substituting this like just described. The resulting .dts wouldn’t compile with that change (neither directly nor flattened), while it does compile without it though it doesn’t do anything in that case.

If you got some more input on this behaviour that would be greatly appreciated.

For testing I went with this "known-good".dts file and just change the GPIO-pin to GPIO3_B3 (PIN 40).

Apart from this I got everything else I tried so far working with the current rc kernel (6.7).