Raspberry Pi CM4: using nRPI_BOOT

The Raspberry Pi Compute Module 4 has a signal on it called nRPI_BOOT, mean to be pulled up via 10k to 3.3V. During boot, if this signal is LOW, booting from eMMC is disabled and the bootrom will wait for code over USB instead. It is the only way to flash the eMMC with an image, so all carrier boards will have some way to set this.

The product I’m working on is built around a CM4 and we indeed have a way to toggle nRPI_BOOT during power on for code loading. What we did NOT think far enough ahead for was a way to gracefully shut down our device in the field, to avoid potentially damaging hard power cycles. So the though occurred to me, can I use the switch we’ve already got to accomplish that task?

The answer, it turns out, is yes. Per this forum post from a Raspberry Pi engineer, and this blurb in the rpiboot documentation on github, the nRPI_BOOT signal is actually gpio40 on the BCM2711. It seems that it works by controlling the enable line of the SPI EEPROM containing the second-stage bootloader code – if the EEPROM doesn’t respond to the first stage, it looks for further instructions via USB. I’m not sure if I have that totally right, but anyway, it does NOT disable the eMMC directly in any way. Once booted to EMMC you can toggle it all you like.

Conveniently, there’s also a mechanism to get a clean shutdown from any GPIO you like: the gpio-shutdown dtoverlay. Simply add the following lines to the end of /boot/config.txt:

# Shut down when toggling the boot mode button/switch:
dtoverlay=gpio-shutdown,gpio_pin=40,active_low=1

Then, whenever that pin toggles from high to low (like pushing the boot mode button), the OS will see a shutdown key code and begin shutting down.

The second question then becomes “how do I know it’s safe to unplug the Pi?” An obvious answer is “light up an LED in the HALT state” but which, and how? Well, we have two LEDs on the carrier already, PWR and ACT, and both are software-controlled (PWR=led1, ACT=led0). By default, PWR is always lit and ACT is lit during EMMC access. The problem is, if there’s no EMMC access, ACT might be dark, which resembles the default halted state of red=on green=off, even if the Pi is running. Luckily, the behavior is easy to change. After some experimentation, the best way I’ve found is, again, a device tree setting in /boot/config.txt:

# Use pwr as CPU indicator, and act as normally-lit eMMC indicator (blinks min 1hz, so act-blinking means don't unplug!)
dtparam=pwr_led_trigger=cpu
dtparam=act_led_trigger=actpwr

In these modes, the power LED indicates CPU usage and is usually-on, and the act LED stays mostly-on but blinks OFF to indicate EMMC access, blinking ON at least once a second. This way, blinking green is a reliable indicator of “unsafe to power off” and solid-off green is a reliable indicator of “safe to power off.”

You can interrogate all the possible trigger options as follows:

pi@raspberrypi:~ $ cat /sys/class/leds/led0/trigger 
none rc-feedback kbd-scrolllock kbd-numlock kbd-capslock kbd-kanalock kbd-shiftlock kbd-altgrlock kbd-ctrllock kbd-altlock kbd-shiftllock kbd-shiftrlock kbd-ctrlllock kbd-ctrlrlock timer oneshot heartbeat backlight gpio [cpu] cpu0 cpu1 cpu2 cpu3 default-on input panic actpwr mmc1 mmc0 rfkill-any rfkill-none rfkill0

My original idea was to simply turn the LED off in userspace. If you do this, it immediately becomes lit again as soon as the chip halts and the default pulls take over. In other words, PWR=dark, ACT=flashing means the Pi is on and running. PWR=Lit means it’s safe to power cycle. If you need a more positive indication of power-on, you could also write 1 to the ACT LED brightness, so it’s always-on when the Pi is booted. The code I used for this previous scheme is below, for my notes and yours, implemented as a one-shot systemd service that just turns of the PWR LED on boot.

#power-led.service
[Unit]
Description=Power Led Killer
After=multi-user.target

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/home/pi/code/pwr_led
ExecStart=/home/pi/code/pwr_led/main.sh

[Install]
WantedBy=multi-user.target

and /home/pi/code/pwr_led/main.sh:

#!/bin/sh

# The (red) power LED onboard lights up solid red whenever there's power.
# This service shuts it off on boot, so it's only lit red when the CPU is halted i.e. safe to unplug

echo 0 | tee /sys/class/leds/led1/brightness

To install it, sudo cp power-led.service /etc/systemd/system and sudo systemctl enable power-led.

NOTE: The more traditional way of doing this if you have design control over the hardware (and haven’t already fabricated it) is to use another device tree overlay provided out of the box. See this forum thread for the details, but the gist is gpio-poweroff can set a pin high or low upon halt. Though I think it might break the gpio-shutdown overlay we already used above, a bit. This is the way to go if you need a physical signal indicator apart from built-in LEDs, though.

2 Comments

  1. HankB
    November 1, 2022

    Thanks for collecting this information and posting.

    Typo: s/systmctl/systemctl/

    Questions:
    1) Any reason to use ‘tee’ to write the value in ‘main.sh’? (Necessary when using ‘sudo’ but the script will run as root.)
    2) Any reason to use the directory ‘/home/pi/code/pwr_led/’ for the working directory and location of the script?

    Thanks!

  2. November 1, 2022

    Good catch, thanks! 1) Probably not, I imagine that’s just an artifact of me testing with sudo rather than root directly. 2) Nothing in particular, just where I had other stuff for the project I was working on.

Leave a Reply

Your email address will not be published. Required fields are marked *