Hi, all
To continue the topic, I would like to share my experience in creating a hardware power-off button to safely shutdown Duo256m. There are a lot of such guides for Raspberry Pi, but as we do not have such luxury as prebuilt device tree overlays, all-in-one kernel, etc, it makes sense to describe how to achieve the same with Duo Buildroot SDK v2. I built RISCV version, but this is applicable for ARM, and original Duo
The outline of steps is as follows:
- Connect a button (push button) to Duo via some GPIO pin
- Make it recognized by linux kernel as power key
- Handle key press in user space to power off the board
For step 1 select a pin and make sure it is configured as GPIO in duo-pinmux (pinmux | Milk-V ). Connect it to the one contact of the button, and other contact to the ground.
For step 2 we will utilize the gpio-keys feature (gpio-keys). First, we have to add our button to the device tree as a “device”. For this we have to add its definition to the root node in build/boards/cv181x/sg2002_milkv_duo256m_musl_riscv64_sd/dts_riscv/sg2002_milkv_duo256m_musl_riscv64_sd.dts ( path will be slightly different for other boards/platforms). Using this file is most straightforward but not the most elegant way to add a new device.
gpio-keys {
compatible = "gpio-keys";
power-button {
label = "power";
linux,code = <116>; // KEY_POWER
gpios = <&porte 4 GPIO_ACTIVE_LOW>;
wakeup-source;
};
};
Key codes are in dt-bindings/input/linux-event-codes.h. You have to lookup your pin GPIO bank and offset in Duo256M | Milk-V I use GP22, which belongs to porte and has offset 4.
Launch kernel menuconfig as described in Using a usb camera on milk-v Duos and select Device Drivers → Input Device Support → Keyboards → GPIO Buttons to buit in kernel [*] ( CONFIG_KEYBOARD_GPIO =y). Rebuild and flash firmware. If everything is ok, in dmesg there will be something like:
[ 1.207892] input: gpio-keys as /devices/platform/gpio-keys/input/input0
There will also be /dev/input/event0 device node.
To test the button, it makes sense also add evtest utility in buildroot configuration before build as described in Introduction | Milk-V
After reflashing, launch it and press the button a couple of times . If everything is set up properly, evtest will report key up/down events as below:
[root@milkv-duo]~# evtest /dev/input/event0
Input driver version is 1.0.1
Input device ID: bus 0x19 vendor 0x1 product 0x1 version 0x100
Input device name: "gpio-keys"
Supported events:
Event type 0 (EV_SYN)
Event type 1 (EV_KEY)
Event code 116 (KEY_POWER)
Properties:
Testing ... (interrupt to exit)
Event: time 1747244396.860994, type 1 (EV_KEY), code 116 (KEY_POWER), value 1
Event: time 1747244396.860994, -------------- SYN_REPORT ------------
Event: time 1747244397.138591, type 1 (EV_KEY), code 116 (KEY_POWER), value 0
Event: time 1747244397.138591, -------------- SYN_REPORT ------------
Event: time 1747244399.938112, type 1 (EV_KEY), code 116 (KEY_POWER), value 1
Event: time 1747244399.938112, -------------- SYN_REPORT ------------
Event: time 1747244400.164853, type 1 (EV_KEY), code 116 (KEY_POWER), value 0
Event: time 1747244400.164853, -------------- SYN_REPORT ------------
Now step 3 - handle key press to power off. Most common options are systemd-logind or acpid daemons. But 1) we do not have systemd-logind and definitely do not want it 2) we don’t have ACPI as well… There is acpid implementation in busybox, and before I’ve realized 2), I had built it as described in Introduction | Milk-V by including
CONFIG_ACPID=y
CONFIG_FEATURE_ACPID_COMPAT=y
In busybox.config. To my surprise, it is pretty able to detect key presses!
[root@milkv-duo]~# acpid -d
acpid: power
Now we have to configure acpid to execute the poweroff command at key press. There is a tutorial Power management - Alpine Linux for this. The last step is to launch acpid at system start-up. For this let’s create /etc/init.d/S03acpid script, using S49ntp as an example and make it executable. Now we have a fully working power-off button!
The last step to finalize the design would be creating an external circuit to safely disconnect the PSU after power off and re-apply power to turn the board on.