Power management

The port exposes CPU frequency control, firmware-assisted idle, diagnostic suspend-to-idle, a retention path, and shutdown/deep-sleep requests. This guide covers their controls and current limitations.

CPU frequency

Both HP cores share one policy with 80, 160, 240 and 320 MHz operating points. Read the available frequencies and governor:

cat /sys/devices/system/cpu/cpufreq/policy0/scaling_available_frequencies
cat /sys/devices/system/cpu/cpufreq/policy0/scaling_governor

Select the userspace governor and request 160 MHz, expressed in kHz:

echo userspace > /sys/devices/system/cpu/cpufreq/policy0/scaling_governor
echo 160000 > /sys/devices/system/cpu/cpufreq/policy0/scaling_setspeed

Return to the supplied default governor:

echo performance > /sys/devices/system/cpu/cpufreq/policy0/scaling_governor

Linux timekeeping uses the separate 16 MHz SYSTIMER. See the clock relationships and configured governors.

CPU idle

The supplied command line selects esp32s31_idle=wfi. Linux enables the firmware-assisted WFI path only when the required SBI extension is present; if the option is disabled or that capability is absent, polling remains the fallback. This is a capability check, not a test of firmware age. See the activation code.

OpenSBI reserves timer 1 in each timer group for a guard configured as 10,000 ticks at 1 MHz (10 ms). The timers overlay leaves those channels reserved and exposes timer 0 to applications. Exact assignments are in Interrupt routing.

Test LP wake handling while Linux is awake

s31-lpctl ping
s31-lpctl sleep-test 1000

These commands check the mailbox and LP timer with Linux running. The LP reference gives GPIO transition examples, argument ranges and result fields. Start there before testing system sleep.

Prepare for system suspend

With LP firmware ready, stop Wi-Fi activity and bring the interface down before either freeze or mem testing. Current SoftMAC returns EBUSY for a running interface; the radio module propagates that veto before suspending Bluetooth or stopping the payload. Active Wi-Fi replay/reassociation is not implemented. Unmount USB storage and disable USB-backed swap before a suspend experiment, since devices may reconnect.

Suspend-to-idle (freeze)

Enable a one-second diagnostic timer and enter freeze:

echo 1000 > /sys/module/esp32s31_lp/parameters/s2idle_wake_ms
echo freeze > /sys/power/state
dmesg | tail -n 80

The range is 10–600000 ms; zero (the default) disables the automatic timer. Validation happens while preparing suspend, so accepting a parameter write does not itself establish that the requested interval is valid. Clear the explicit test timer when finished:

echo 0 > /sys/module/esp32s31_lp/parameters/s2idle_wake_ms

This path exercises Linux device suspend/resume and LP transactions. The HP side polls for LP completion during the noirq phase; it is not evidence of an HP low-power state. See the polling implementation.

The S31 DWC2 suspend callback disables its IRQ, the controller’s global interrupt enable and low-level hardware, and sets phy_off_for_suspend. There is no freeze-specific exception that keeps the controller/PHY active. The resume path re-enables and restores the controller as needed; USB devices may reconnect. See DWC2 suspend/resume.

Suspend-to-RAM (mem / deep)

The current Linux/LP/OpenSBI sources agree on ABI 1 and the 28-word control layout. The existing checks cover ABI-version agreement and the LP timer’s start condition. See the source contract tests and LP development guide.

Parameters under /sys/module/esp32s31_lp/parameters/ are:

Parameter

Accepted values

Default

mem_wake_ms

Timer interval, 10–600000 ms

2000

mem_wake_gpio

LP GPIO0–7, or -1 to disable GPIO wake

-1

mem_gpio_active_high

Y for high, N for low

Y

mem_gpio_pull

0 none, 1 pull-up, 2 pull-down

0

The timer remains required as a recovery source even when GPIO wake is added. A selected GPIO must be inactive when ARM runs, as in the awake GPIO test. LP starts the retention timer after OpenSBI publishes HP_ASLEEP. The power policy and memory reservations are in Power domains and Memory map.

For a development test, first inspect the states exposed by the running kernel. Only proceed with the following retention request if deep is listed:

cat /sys/power/state
cat /sys/power/mem_sleep
echo 2000 > /sys/module/esp32s31_lp/parameters/mem_wake_ms
echo -1 > /sys/module/esp32s31_lp/parameters/mem_wake_gpio
echo deep > /sys/power/mem_sleep
echo mem > /sys/power/state
dmesg | tail -n 100

mem_sleep selects what mem means; deep selects the platform retention path. See the Linux suspend registration and MEM request validation.

Shut down

poweroff

Linux stops services and synchronizes filesystems before requesting firmware shutdown. The clock provider prepares the 40 MHz XTAL handoff before the secondary hart is stopped; OpenSBI verifies that state before entering the PMU sequence. If untimed shutdown cannot complete, firmware halts instead of rebooting. Reset/EN or a power cycle starts Linux again. This command does not remove the board’s external supply. See the clock handoff and firmware fallback.

Timed deep sleep

Timed deep sleep requests an orderly Linux shutdown followed by a fresh cold boot, without preserving the running application. Save application state first. With LP firmware ready, request a four-second interval:

for attr in /sys/bus/platform/devices/*/deep_sleep; do
    [ -w "$attr" ] || continue
    echo 4000 > "$attr"
    break
done

The public control accepts 1000–600000 ms and a timer wake source. Linux latches the request and starts orderly power-off; OpenSBI programs the RTC and selects the cold-boot path. See the request handler and RTC setup.

The RTC conversion uses a fixed 155386 Hz slow-clock value, so the actual delay can differ between boards. previous and wake_reason are software markers for the requested operation, not proof that physical sleep succeeded. A failed timed transition can fall back to a reset; inspect the serial log before treating a reboot as a successful sleep cycle.

GPIO wake is implemented for the awake test and retention request, not the public cold-boot deep-sleep control. LP-UART and WoWLAN packet wake are not implemented wake paths here.