Skip to content

Instantly share code, notes, and snippets.

@Entrpi
Created December 30, 2025 00:55
Show Gist options
  • Select an option

  • Save Entrpi/6a300bbacfcee4983a45c9661d1dfb15 to your computer and use it in GitHub Desktop.

Select an option

Save Entrpi/6a300bbacfcee4983a45c9661d1dfb15 to your computer and use it in GitHub Desktop.
Orion O6 - Minimum Power Testing
#!/bin/bash
# Minimum power test script for Radxa Orion O6 / CIX Sky1
# Modular design with individual power controls
# Run as root.
set -e
#=============================================================================
# Configuration
#=============================================================================
DDR_LP="/sys/devices/platform/cix_ddr_lp/on"
CPU_IPA="/sys/devices/platform/83bf0300.cix_cpu_ipa/cpu_power"
CI700_DEVFREQ="/sys/class/devfreq/cix_bus_ci700"
NI700_DEVFREQ="/sys/class/devfreq/cix_bus_ni700"
USB_CONTROLLERS="9010000 9080000 90f0000 9160000 91d0000 91e0000 9260000 9290000 92c0000 92f0000"
DISPLAY_CONTROLLERS="14010000 14080000 140f0000 14160000 141d0000"
# PCIe root ports for 5GbE R8126 controllers
PCIE_5GBE_PORTS="a0d0000.pcie a0e0000.pcie"
# PCIe root port for WiFi (RTL8852BE)
PCIE_WIFI_PORT="a0c0000.pcie"
# PCIe root port for NVMe
PCIE_NVME_PORT="a070000.pcie"
# Ramdisk location - MUST be on existing tmpfs (not under root fs)
RAMROOT="/run/ramroot"
#=============================================================================
# Utility Functions
#=============================================================================
hold_for() {
local secs=$1 label=$2
echo ">>> $label - hold ${secs}s ($(date '+%H:%M:%S'))"
for i in $(seq $secs -1 1); do
printf "\r %3d seconds..." $i
sleep 1
done
echo ""
}
#=============================================================================
# Individual Power Controls (on/off functions)
#=============================================================================
# --- GDM / Desktop ---
ctl_gdm() {
case $1 in
off)
systemctl stop gdm 2>/dev/null || true
pkill -9 gnome-shell 2>/dev/null || true
pkill -9 Xwayland 2>/dev/null || true
pkill -9 gsd- 2>/dev/null || true
sleep 2
echo " GDM/desktop: stopped"
;;
on)
systemctl start gdm 2>/dev/null || true
echo " GDM/desktop: started"
;;
esac
}
# --- GPU (panthor) ---
ctl_gpu() {
case $1 in
off)
rmmod panthor 2>/dev/null && echo " GPU: unloaded" || echo " GPU: already unloaded"
;;
on)
modprobe panthor 2>/dev/null || true
if [ ! -e "/sys/devices/platform/soc@0/15000000.gpu/driver" ]; then
echo "15000000.gpu" > /sys/bus/platform/drivers/panthor/bind 2>/dev/null || true
fi
echo " GPU: loaded"
;;
esac
}
# --- Display (DPU + DP TX) ---
ctl_display() {
case $1 in
off)
rmmod trilin_dpsub 2>/dev/null || true
rmmod linlon_dp 2>/dev/null || true
echo " Display: unloaded"
;;
on)
modprobe linlon_dp 2>/dev/null || true
modprobe trilin_dpsub 2>/dev/null || true
for disp in $DISPLAY_CONTROLLERS; do
if [ ! -e "/sys/bus/platform/drivers/linlondp/${disp}.disp-controller" ]; then
echo "${disp}.disp-controller" > /sys/bus/platform/drivers/linlondp/bind 2>/dev/null || true
fi
done
echo " Display: loaded"
;;
esac
}
# --- WiFi ---
ctl_wifi() {
local deep=${2:-false} # "deep" = also remove PCI device
case $1 in
off)
/usr/sbin/rfkill block wifi 2>/dev/null || true
ip link set wlp97s0 down 2>/dev/null || true
rmmod rtw89_8852be rtw89_8852b rtw89_8852b_common rtw89_pci rtw89_core 2>/dev/null || true
rmmod mac80211 cfg80211 2>/dev/null || true
if [ "$deep" = "deep" ]; then
# Remove WiFi PCI device (RTL8852BE = 10ec:b852)
for dev in /sys/bus/pci/devices/*; do
vendor=$(cat "$dev/vendor" 2>/dev/null)
device=$(cat "$dev/device" 2>/dev/null)
if [ "$vendor" = "0x10ec" ] && [ "$device" = "0xb852" ]; then
echo 1 > "$dev/remove" 2>/dev/null || true
echo " WiFi: disabled + PCI device removed"
return
fi
done
echo " WiFi: disabled (device already removed)"
else
echo " WiFi: disabled"
fi
;;
on)
# Rescan to restore removed device
echo 1 > /sys/bus/pci/rescan 2>/dev/null || true
sleep 1
modprobe rtw89_8852be 2>/dev/null || true
/usr/sbin/rfkill unblock wifi 2>/dev/null || true
echo " WiFi: enabled"
;;
esac
}
# --- Bluetooth ---
ctl_bluetooth() {
case $1 in
off)
/usr/sbin/rfkill block bluetooth 2>/dev/null || true
rmmod btusb btrtl btintel btbcm btmtk 2>/dev/null || true
echo " Bluetooth: disabled"
;;
on)
modprobe btusb 2>/dev/null || true
/usr/sbin/rfkill unblock bluetooth 2>/dev/null || true
echo " Bluetooth: enabled"
;;
esac
}
# --- USB Controllers ---
ctl_usb() {
local keep=${2:-} # optional controller to keep
case $1 in
off)
for usb in $USB_CONTROLLERS; do
[ "$usb" = "$keep" ] && continue
if [ -e "/sys/bus/platform/drivers/cdns-usbssp/${usb}.usb-controller" ]; then
echo "${usb}.usb-controller" > /sys/bus/platform/drivers/cdns-usbssp/unbind 2>/dev/null || true
fi
done
echo " USB: unbound${keep:+ (kept $keep)}"
;;
on)
for usb in $USB_CONTROLLERS; do
if [ ! -e "/sys/bus/platform/drivers/cdns-usbssp/${usb}.usb-controller" ]; then
echo "${usb}.usb-controller" > /sys/bus/platform/drivers/cdns-usbssp/bind 2>/dev/null || true
fi
done
echo " USB: bound"
;;
esac
}
# --- Audio ---
ctl_audio() {
case $1 in
off)
rmmod snd_hda_codec_alc269 snd_hda_scodec_component snd_hda_codec_realtek_lib 2>/dev/null || true
rmmod snd_hda_codec_generic snd_hda_cix_ipbloq snd_hda_codec snd_hda_core 2>/dev/null || true
rmmod snd_soc_hdmi_codec snd_soc_sky1_card snd_soc_cdns_i2s_mc snd_soc_core 2>/dev/null || true
rmmod snd_compress snd_pcm_dmaengine snd_pcm snd_timer snd soundcore 2>/dev/null || true
if [ -e "/sys/bus/platform/drivers/arm-dma350/7010000.dma-controller" ]; then
echo "7010000.dma-controller" > /sys/bus/platform/drivers/arm-dma350/unbind 2>/dev/null || true
fi
echo " Audio: unloaded"
;;
on)
modprobe snd_hda_cix_ipbloq 2>/dev/null || true
echo " Audio: loaded (may need reboot)"
;;
esac
}
# --- PCIe ASPM ---
ctl_pcie_aspm() {
case $1 in
off) # powersupersave
echo powersupersave > /sys/module/pcie_aspm/parameters/policy 2>/dev/null || true
for d in /sys/bus/pci/devices/*/power/control; do
echo auto > "$d" 2>/dev/null || true
done
# L1 substates
setpci -s 0001:90:00.0 0x908.b=0x0f 2>/dev/null || true
setpci -s 0001:91:00.0 0x230.b=0x0f 2>/dev/null || true
setpci -s 0000:60:00.0 0x908.b=0x0f 2>/dev/null || true
setpci -s 0002:30:00.0 0x908.b=0x0f 2>/dev/null || true
setpci -s 0002:31:00.0 0x22c.b=0x0f 2>/dev/null || true
setpci -s 0003:00:00.0 0x908.b=0x0f 2>/dev/null || true
setpci -s 0003:01:00.0 0x22c.b=0x0f 2>/dev/null || true
echo " PCIe ASPM: powersupersave + L1 substates"
;;
on) # default
echo default > /sys/module/pcie_aspm/parameters/policy 2>/dev/null || true
echo " PCIe ASPM: default"
;;
esac
}
# --- DDR Low Power ---
# Note: "on" = LP enabled (low power), "off" = LP disabled (full performance)
ctl_ddr_lp() {
case $1 in
on) # enable LP mode (low power)
[ -f "$DDR_LP" ] && echo 1 > "$DDR_LP"
echo " DDR LP: enabled (low power)"
;;
off) # disable LP mode (full performance)
[ -f "$DDR_LP" ] && echo 0 > "$DDR_LP"
echo " DDR LP: disabled (full performance)"
;;
esac
}
# --- Bus Frequency ---
ctl_bus_freq() {
local mode=$1 # min or max
[ ! -d "$CI700_DEVFREQ" ] && return
[ ! -d "$NI700_DEVFREQ" ] && return
local ci_freqs=$(cat "$CI700_DEVFREQ/available_frequencies")
local ni_freqs=$(cat "$NI700_DEVFREQ/available_frequencies")
if [ "$mode" = "min" ]; then
local ci_freq=$(echo $ci_freqs | awk '{print $1}')
local ni_freq=$(echo $ni_freqs | awk '{print $1}')
else
local ci_freq=$(echo $ci_freqs | awk '{print $NF}')
local ni_freq=$(echo $ni_freqs | awk '{print $NF}')
fi
echo "$ci_freq" > "$CI700_DEVFREQ/min_freq"
echo "$ci_freq" > "$CI700_DEVFREQ/max_freq"
echo "$ni_freq" > "$NI700_DEVFREQ/min_freq"
echo "$ni_freq" > "$NI700_DEVFREQ/max_freq"
echo " Bus freq: CI-700=$((ci_freq/1000000))MHz NI-700=$((ni_freq/1000000))MHz"
}
# --- CPU ---
ctl_cpu() {
case $1 in
off) # powersave, single core
for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
echo powersave > "$cpu" 2>/dev/null || true
done
for cpu in /sys/devices/system/cpu/cpu{1..11}/online; do
echo 0 > "$cpu" 2>/dev/null || true
done
echo " CPU: powersave, 1 core online"
;;
on) # schedutil, all cores
for cpu in /sys/devices/system/cpu/cpu{1..11}/online; do
echo 1 > "$cpu" 2>/dev/null || true
done
for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
echo schedutil > "$cpu" 2>/dev/null || true
done
for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_max_freq; do
max=$(cat $(dirname $cpu)/scaling_available_frequencies 2>/dev/null | awk '{print $NF}')
[ -n "$max" ] && echo "$max" > "$cpu" 2>/dev/null || true
done
echo " CPU: schedutil, all cores online"
;;
esac
}
# --- Misc modules ---
ctl_misc() {
case $1 in
off)
rmmod goodix_ts 2>/dev/null || true
rmmod cix_dsp_rproc 2>/dev/null || true
echo " Misc: unloaded (touchscreen, DSP)"
;;
on)
modprobe goodix_ts 2>/dev/null || true
modprobe cix_dsp_rproc 2>/dev/null || true
echo " Misc: loaded"
;;
esac
}
# --- 5GbE R8126 (remove PCI devices) ---
# Note: We use PCI device removal since sky1-pcie driver lacks bind/unbind sysfs.
# To restore after removal, need to rescan the bus or reboot.
ctl_5gbe() {
case $1 in
off)
# Find and remove R8126 devices (vendor:device = 10ec:8126)
for dev in /sys/bus/pci/devices/*; do
vendor=$(cat "$dev/vendor" 2>/dev/null)
device=$(cat "$dev/device" 2>/dev/null)
if [ "$vendor" = "0x10ec" ] && [ "$device" = "0x8126" ]; then
echo 1 > "$dev/remove" 2>/dev/null || true
echo " Removed $(basename $dev)"
fi
done
echo " 5GbE: R8126 devices removed from bus"
;;
on)
# Rescan PCIe buses to re-enumerate removed devices
echo 1 > /sys/bus/pci/rescan 2>/dev/null || true
echo " 5GbE: PCIe bus rescanned"
;;
esac
}
# --- PMU (Performance Monitoring Units - not needed for headless) ---
# NOTE: cix_pmu is used by devfreq for bus frequency scaling.
# We must stop devfreq polling before unloading it, or the kernel crashes.
ctl_pmu() {
case $1 in
off)
# Stop devfreq polling first to prevent use-after-free crash
for gov in "$CI700_DEVFREQ/governor" "$NI700_DEVFREQ/governor"; do
[ -f "$gov" ] && echo "userspace" > "$gov" 2>/dev/null || true
done
sleep 0.5 # Let devfreq workqueue drain
rmmod arm_cmn arm_dsu_pmu 2>/dev/null || true
# cix_pmu is too dangerous to unload - tied to devfreq events
# rmmod cix_pmu 2>/dev/null || true
echo " PMU: unloaded (arm_cmn, arm_dsu_pmu) - cix_pmu kept for devfreq"
;;
on)
modprobe arm_cmn 2>/dev/null || true
modprobe arm_dsu_pmu 2>/dev/null || true
# Restore devfreq governor
for gov in "$CI700_DEVFREQ/governor" "$NI700_DEVFREQ/governor"; do
[ -f "$gov" ] && echo "simple_ondemand" > "$gov" 2>/dev/null || true
done
echo " PMU: loaded"
;;
esac
}
#=============================================================================
# High-Level Commands
#=============================================================================
usage() {
echo "Usage: $0 <command> [options]"
echo ""
echo "Commands:"
echo " status - Show current power-related settings"
echo " baseline - Set known HIGH power state for measurement"
echo " apply - Apply power savings (keeps display)"
echo " headless - Maximum power savings (serial only!)"
echo " restore - Restore normal operation"
echo " benchmark - Run memory bandwidth test at different bus frequencies"
echo " powertest [secs] - Bus frequency power test (default 30s holds)"
echo " powersteps [secs] - Step-by-step power test (default 20s holds)"
echo " ramtest [secs] - Pivot to ramdisk, disable NVMe (default 60s hold)"
echo ""
echo "Individual controls (for testing):"
echo " ctl <name> <on|off> - Control individual subsystem"
echo " Names: gdm, gpu, display, wifi, bluetooth, usb, audio, pcie, ddr, bus, cpu, misc, 5gbe, pmu"
exit 1
}
show_status() {
echo "=== Power Status ==="
echo ""
echo "CPU: $(cat /sys/devices/system/cpu/online) online, $(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor 2>/dev/null) governor"
if [ -d "$CI700_DEVFREQ" ]; then
echo "CI-700: $(( $(cat $CI700_DEVFREQ/cur_freq) / 1000000 )) MHz"
fi
if [ -d "$NI700_DEVFREQ" ]; then
echo "NI-700: $(( $(cat $NI700_DEVFREQ/cur_freq) / 1000000 )) MHz"
fi
[ -f "$DDR_LP" ] && echo "DDR LP: $([ $(cat $DDR_LP) = 1 ] && echo enabled || echo disabled)"
echo "PCIe ASPM: $(cat /sys/module/pcie_aspm/parameters/policy)"
echo ""
echo "GPU: $([ -e /sys/devices/platform/soc@0/15000000.gpu/driver ] && echo bound || echo unbound)"
echo "Display: $(lsmod | grep -q linlon_dp && echo loaded || echo unloaded)"
echo "WiFi: $(/usr/sbin/rfkill list wifi 2>/dev/null | grep -q 'Soft blocked: yes' && echo blocked || echo unblocked)"
echo "Bluetooth: $(/usr/sbin/rfkill list bluetooth 2>/dev/null | grep -q 'Soft blocked: yes' && echo blocked || echo unblocked)"
local usb_bound=0
for usb in $USB_CONTROLLERS; do
[ -e "/sys/bus/platform/drivers/cdns-usbssp/${usb}.usb-controller" ] && usb_bound=$((usb_bound + 1))
done
echo "USB: $usb_bound/10 controllers bound"
}
set_baseline() {
echo "=== Setting HIGH Power Baseline ==="
echo ""
echo "This sets all subsystems to maximum power state"
echo "for accurate power savings measurements."
echo ""
# Ensure all CPUs online and at max performance
echo "CPUs: all online, performance governor..."
for cpu in /sys/devices/system/cpu/cpu{1..11}/online; do
echo 1 > "$cpu" 2>/dev/null || true
done
for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
echo performance > "$cpu" 2>/dev/null || true
done
for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_max_freq; do
max=$(cat $(dirname $cpu)/scaling_available_frequencies 2>/dev/null | awk '{print $NF}')
[ -n "$max" ] && echo "$max" > "$cpu" 2>/dev/null || true
done
# Bus frequencies to MAX
echo "Bus freq: MAX..."
ctl_bus_freq max
# DDR LP disabled (full performance)
echo "DDR LP: disabled (full performance)..."
ctl_ddr_lp off
# PCIe ASPM off (default policy)
echo "PCIe ASPM: default..."
ctl_pcie_aspm on
# Ensure all USB controllers bound
echo "USB: binding all controllers..."
ctl_usb on
# Ensure display/GPU loaded
echo "Display/GPU: loading..."
ctl_display on
ctl_gpu on
# WiFi/BT unblocked
echo "WiFi/Bluetooth: unblocking..."
ctl_wifi on
ctl_bluetooth on
echo ""
echo "=== Baseline Set ==="
show_status
echo ""
echo "System is now at MAXIMUM power state."
echo "Wait 10-20s for power to stabilize before measuring."
}
apply_power_savings() {
local headless=${1:-false}
local skip_confirm=${2:-false}
echo "=== Applying Power Savings ==="
[ "$headless" = "true" ] && echo "MODE: HEADLESS" || echo "MODE: WITH DISPLAY"
if [ "$skip_confirm" != "true" ]; then
read -p "Continue? [y/N] " -n 1 -r
echo
[[ ! $REPLY =~ ^[Yy]$ ]] && exit 1
fi
ctl_gdm off
[ "$headless" = "true" ] && ctl_gpu off
[ "$headless" = "true" ] && ctl_display off
[ "$headless" = "true" ] && ctl_wifi off deep || ctl_wifi off
ctl_bluetooth off
[ "$headless" = "true" ] && ctl_usb off || ctl_usb off 9260000
ctl_audio off
ctl_5gbe off
ctl_pmu off
ctl_pcie_aspm off
ctl_ddr_lp on
ctl_bus_freq min
ctl_cpu off
ctl_misc off
echo ""
echo "=== MINIMUM POWER MODE ACTIVE ==="
echo "To restore: $0 restore"
}
restore_normal() {
echo "=== Restoring Normal Operation ==="
ctl_cpu on
ctl_bus_freq max
ctl_ddr_lp on # Keep LP enabled - it's automatic
ctl_pcie_aspm on
ctl_pmu on
ctl_5gbe on
ctl_misc on
ctl_audio on
ctl_usb on
ctl_bluetooth on
ctl_wifi on
ctl_display on
ctl_gpu on
ctl_gdm on
echo ""
echo "=== Normal Operation Restored ==="
}
powersteps() {
local hold=${1:-20}
echo "=========================================="
echo "=== Step-by-Step Power Measurement ==="
echo "=========================================="
echo "Hold time: ${hold}s per step"
echo ""
echo "This will:"
echo " 1. Set HIGH power baseline (buses MAX, DDR LP off, all CPUs)"
echo " 2. Step through each power saving with ${hold}s holds"
echo ""
read -p "Continue? [y/N] " -n 1 -r
echo
[[ ! $REPLY =~ ^[Yy]$ ]] && exit 1
# Set known high-power baseline first
set_baseline
echo ""
hold_for $hold "BASELINE (max power)"
ctl_gdm off
hold_for $hold "GDM stopped"
ctl_gpu off
hold_for $hold "GPU unloaded"
ctl_display off
hold_for $hold "Display unloaded"
ctl_wifi off deep
hold_for $hold "WiFi disabled + PCIe unbound"
ctl_bluetooth off
hold_for $hold "Bluetooth disabled"
ctl_usb off
hold_for $hold "USB unbound"
ctl_audio off
hold_for $hold "Audio unloaded"
ctl_5gbe off
hold_for $hold "5GbE PCIe ports unbound"
ctl_pmu off
hold_for $hold "PMU modules unloaded"
ctl_pcie_aspm off
hold_for $hold "PCIe ASPM powersupersave"
ctl_ddr_lp on
hold_for $hold "DDR LP enabled"
ctl_bus_freq min
hold_for $hold "Bus freq MIN"
ctl_cpu off
hold_for $hold "CPU powersave + 1 core"
echo ""
echo "=== All steps complete ==="
echo "System at minimum power. To restore: $0 restore"
}
powertest() {
local duration=${1:-30}
echo "=== Bus Frequency Power Test ==="
echo "Duration: ${duration}s per phase"
read -p "Continue? [y/N] " -n 1 -r
echo
[[ ! $REPLY =~ ^[Yy]$ ]] && exit 1
apply_power_savings true true
echo ""
echo "Stabilizing for 10s..."
sleep 10
[ ! -d "$CI700_DEVFREQ" ] && { echo "ERROR: devfreq not available"; exit 1; }
local ci_freqs=$(cat "$CI700_DEVFREQ/available_frequencies")
local ni_freqs=$(cat "$NI700_DEVFREQ/available_frequencies")
local ci_min=$(echo $ci_freqs | awk '{print $1}')
local ci_max=$(echo $ci_freqs | awk '{print $NF}')
local ni_min=$(echo $ni_freqs | awk '{print $1}')
local ni_max=$(echo $ni_freqs | awk '{print $NF}')
trap 'echo "Interrupted"; restore_normal; exit 0' INT TERM
ctl_bus_freq min
hold_for $duration "Phase 1: MIN/MIN"
ctl_bus_freq max
hold_for $duration "Phase 2: MAX/MAX"
echo "$ci_max" > "$CI700_DEVFREQ/min_freq"; echo "$ci_max" > "$CI700_DEVFREQ/max_freq"
echo "$ni_min" > "$NI700_DEVFREQ/min_freq"; echo "$ni_min" > "$NI700_DEVFREQ/max_freq"
hold_for $duration "Phase 3: CI MAX, NI MIN"
echo "$ci_min" > "$CI700_DEVFREQ/min_freq"; echo "$ci_min" > "$CI700_DEVFREQ/max_freq"
echo "$ni_max" > "$NI700_DEVFREQ/min_freq"; echo "$ni_max" > "$NI700_DEVFREQ/max_freq"
hold_for $duration "Phase 4: CI MIN, NI MAX"
restore_normal
echo "=== Power test complete ==="
}
benchmark() {
echo "=== Memory Bandwidth Benchmark ==="
[ ! -d "$CI700_DEVFREQ" ] && { echo "ERROR: devfreq not available"; exit 1; }
local ci_freqs=$(cat "$CI700_DEVFREQ/available_frequencies")
local ni_freqs=$(cat "$NI700_DEVFREQ/available_frequencies")
local ci_min=$(echo $ci_freqs | awk '{print $1}')
local ci_max=$(echo $ci_freqs | awk '{print $NF}')
local ni_min=$(echo $ni_freqs | awk '{print $1}')
local ni_max=$(echo $ni_freqs | awk '{print $NF}')
run_bw() {
sync; echo 3 > /proc/sys/vm/drop_caches 2>/dev/null || true
dd if=/dev/zero of=/dev/null bs=1M count=2048 2>&1 | tail -1 | grep -oP '[\d.]+\s*[GM]B/s' || echo "N/A"
}
echo ""
echo "MAX frequencies:"
ctl_bus_freq max
sleep 1
echo " Bandwidth: $(run_bw)"
echo ""
echo "MIN frequencies:"
ctl_bus_freq min
sleep 1
echo " Bandwidth: $(run_bw)"
ctl_bus_freq max
echo ""
echo "=== Benchmark complete ==="
}
#=============================================================================
# Ramdisk Power Test (pivot root, disable NVMe)
#=============================================================================
setup_ramroot() {
echo "Setting up ramdisk root..."
# Use existing tmpfs (/run is already tmpfs)
# Don't mount a new one - just create directory
mkdir -p "$RAMROOT"
# Create directory structure
mkdir -p "$RAMROOT"/{bin,sbin,proc,sys,dev,tmp,oldroot}
mkdir -p "$RAMROOT"/sys/bus/platform/drivers/sky1-pcie
mkdir -p "$RAMROOT"/sys/class/devfreq
mkdir -p "$RAMROOT"/sys/devices/system/cpu
mkdir -p "$RAMROOT"/sys/devices/platform
# Copy busybox and create symlinks
cp /bin/busybox "$RAMROOT/bin/"
for cmd in sh ash cat echo sleep ls mkdir mount umount sync \
grep awk sed tr head tail printf date seq chroot; do
ln -sf busybox "$RAMROOT/bin/$cmd"
done
# Copy this script
cp "$0" "$RAMROOT/bin/power.sh"
chmod +x "$RAMROOT/bin/power.sh"
# Create restore script
cat > "$RAMROOT/bin/restore.sh" << 'RESTORE_EOF'
#!/bin/busybox sh
# Restore NVMe and pivot back to real root
echo "=== Restoring NVMe and root filesystem ==="
# Rescan PCI bus to re-enumerate NVMe
echo " Rescanning PCI bus..."
echo 1 > /sys/bus/pci/rescan 2>/dev/null
sleep 2
# Wait for NVMe to appear
echo " Waiting for NVMe..."
for i in $(seq 1 15); do
[ -b /dev/nvme0n1p3 ] && break
sleep 1
echo " $i..."
done
if [ ! -b /dev/nvme0n1p3 ]; then
echo "ERROR: NVMe device not found!"
echo "Try: echo 1 > /sys/bus/pci/rescan"
echo "Then: mount /dev/nvme0n1p3 /oldroot"
exit 1
fi
# Mount old root
mount /dev/nvme0n1p3 /oldroot
if [ $? -ne 0 ]; then
echo "ERROR: Failed to mount root filesystem!"
exit 1
fi
echo " Root filesystem mounted"
# Pivot back
cd /oldroot
mount --move /sys /oldroot/sys
mount --move /proc /oldroot/proc
mount --move /dev /oldroot/dev
pivot_root . tmp/ramroot
exec chroot . /bin/sh -c 'umount /tmp/ramroot 2>/dev/null; exec /bin/bash'
RESTORE_EOF
chmod +x "$RAMROOT/bin/restore.sh"
echo " Ramdisk ready at $RAMROOT"
}
pivot_to_ramroot() {
echo "Switching to ramdisk environment..."
# Sync filesystems
sync
# We don't do a full pivot_root (complex requirements).
# Instead, we'll lazy unmount root and remove NVMe.
# The kernel keeps pages in memory until all refs are gone.
# Make sure we're running from ramdisk copy
if [ ! -f "$RAMROOT/bin/busybox" ]; then
echo "ERROR: Ramdisk not set up properly"
return 1
fi
# Pre-cache binaries we'll need after unmount
# This loads them into page cache so they stay available
date +%s >/dev/null 2>&1
seq 1 1 >/dev/null 2>&1
printf "" 2>&1
sleep 0.01 2>&1
lspci >/dev/null 2>&1
# Export for subshells
export PATH="$RAMROOT/bin:$PATH"
export RAMROOT
echo " Ramdisk environment ready"
echo " Binaries pre-cached for post-unmount operation"
}
stop_background_services() {
echo "Aggressively stopping all services and freezing system..."
# Mask services so systemd won't try to restart them
systemctl mask --runtime systemd-journald rpcbind nfs-server \
irqbalance cron cups avahi-daemon ModemManager NetworkManager \
wpa_supplicant polkit accounts-daemon udisks2 colord thermald \
packagekit rsyslog 2>/dev/null || true
# Stop all non-essential services
systemctl stop irqbalance cron cups rpcbind avahi-daemon \
ModemManager NetworkManager wpa_supplicant polkit \
accounts-daemon udisks2 colord thermald packagekit \
nfs-server nfs-mountd nfs-idmapd 2>/dev/null || true
# Stop logging
systemctl stop systemd-journald rsyslog 2>/dev/null || true
# Kill absolutely everything except essential processes
# Get our own PID and parent PIDs to avoid killing ourselves
local my_pid=$$
local my_ppid=$(ps -o ppid= -p $my_pid | tr -d ' ')
# Kill all user processes
pkill -9 -u radxa 2>/dev/null || true
# Kill specific troublemakers
pkill -9 gvfsd gvfs dconf at-spi ibus evolution tracker gmain 2>/dev/null || true
pkill -9 -f "/usr/lib" 2>/dev/null || true
# Send SIGSTOP to all processes except our shell and kernel threads
echo " Freezing remaining processes..."
for pid in $(ps -eo pid= | tr -d ' '); do
# Skip kernel threads (ppid=2), our process, and PID 1
[ "$pid" = "1" ] && continue
[ "$pid" = "2" ] && continue
[ "$pid" = "$my_pid" ] && continue
[ "$pid" = "$my_ppid" ] && continue
ppid=$(ps -o ppid= -p $pid 2>/dev/null | tr -d ' ')
[ "$ppid" = "2" ] && continue # kernel thread
kill -STOP $pid 2>/dev/null || true
done
echo " All processes frozen"
}
unmount_nvme() {
echo "Unmounting NVMe filesystem..."
# Sync and drop caches first
sync
echo 3 > /proc/sys/vm/drop_caches 2>/dev/null || true
# Lazy unmount the old root
umount -l /oldroot 2>/dev/null || true
echo " NVMe unmounted"
}
unbind_nvme_pcie() {
echo "Removing NVMe from PCI bus..."
# Find and remove NVMe device (class 0108 = Non-Volatile memory controller)
for dev in /sys/bus/pci/devices/*; do
class=$(cat "$dev/class" 2>/dev/null)
# Class 0x010802 = NVMe controller
if [ "${class:0:6}" = "0x0108" ]; then
devname=$(basename $dev)
echo 1 > "$dev/remove" 2>/dev/null || true
echo " NVMe removed: $devname"
return
fi
done
echo " NVMe device not found (already removed?)"
}
ramtest() {
local duration=${1:-60}
echo "=========================================="
echo "=== RAMDISK POWER TEST ==="
echo "=========================================="
echo ""
echo "This will:"
echo " 1. Apply all power savings (headless mode)"
echo " 2. Lazy-unmount root filesystem"
echo " 3. Remove NVMe from PCI bus"
echo " 4. Hold for ${duration}s for measurement"
echo " 5. Restore NVMe (rescan PCI bus)"
echo ""
echo "WARNING: This is experimental! Have serial console ready."
echo " If something goes wrong, you'll need to reboot."
echo ""
read -p "Continue? [y/N] " -n 1 -r
echo
[[ ! $REPLY =~ ^[Yy]$ ]] && exit 1
# Step 1: Setup ramdisk while we still have full system
setup_ramroot
# Step 2: Apply power savings (except NVMe-related)
echo ""
echo "=== Applying power savings ==="
ctl_gdm off
ctl_gpu off
ctl_display off
ctl_wifi off deep
ctl_bluetooth off
ctl_usb off
ctl_audio off
ctl_5gbe off
ctl_pmu off
ctl_pcie_aspm off
ctl_ddr_lp on
ctl_bus_freq min
ctl_cpu off
ctl_misc off
echo ""
echo "=== Preparing ramdisk ==="
pivot_to_ramroot
echo ""
echo "=== Stopping background services ==="
stop_background_services
echo ""
echo "=== Disabling NVMe ==="
unmount_nvme
unbind_nvme_pcie
echo ""
echo "=========================================="
echo "=== MINIMUM POWER - NVMe REMOVED ==="
echo "=========================================="
echo "All subsystems disabled including NVMe."
echo "Root filesystem lazy-unmounted (cached in RAM)."
echo ""
echo ">>> Measure power now!"
echo ""
echo "Press ENTER to restore NVMe and exit..."
read dummy
echo ""
echo "=== Restoring NVMe ==="
echo 1 > /sys/bus/pci/rescan 2>/dev/null || true
# Use bash built-in for delay (read with timeout)
read -t 3 dummy 2>/dev/null || true
# NVMe should be back after rescan
echo " PCI rescan complete"
echo ""
echo "=========================================="
echo "Test complete. Please REBOOT to restore system."
echo "=========================================="
}
#=============================================================================
# Main
#=============================================================================
case "${1:-}" in
status) show_status ;;
baseline) set_baseline ;;
apply) apply_power_savings false ;;
headless) apply_power_savings true ;;
restore) restore_normal ;;
benchmark) benchmark ;;
powertest) powertest "${2:-30}" ;;
powersteps) powersteps "${2:-20}" ;;
ramtest) ramtest "${2:-60}" ;;
ctl)
{ [ -z "$2" ] || [ -z "$3" ]; } && { echo "Usage: $0 ctl <name> <on|off>"; exit 1; }
case "$2" in
gdm) ctl_gdm "$3" ;;
gpu) ctl_gpu "$3" ;;
display) ctl_display "$3" ;;
wifi) ctl_wifi "$3" ;;
bluetooth) ctl_bluetooth "$3" ;;
usb) ctl_usb "$3" ;;
audio) ctl_audio "$3" ;;
pcie) ctl_pcie_aspm "$3" ;;
ddr) ctl_ddr_lp "$3" ;;
bus) ctl_bus_freq "$3" ;; # min/max instead of on/off
cpu) ctl_cpu "$3" ;;
misc) ctl_misc "$3" ;;
5gbe) ctl_5gbe "$3" ;;
pmu) ctl_pmu "$3" ;;
*) echo "Unknown: $2"; exit 1 ;;
esac
;;
*) usage ;;
esac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment