diff --git a/README.md b/README.md index 46c9ea4..802a775 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ A collection of scripts and simple applications * [rpifwcrypto](rpifwcrypto/) - A command line application and shared library for the firmware cryptography service. Intended for use with Raspberry Pi Connect and secure-boot provisioner. +* [rpipmic](rpipmic/) - A command line utility to report any stored PMIC events. * [rpi-gpu-usage](rpi-gpu-usage/) - A tool for showing the per-process usage of the V3D GPU on Raspberry Pi 4 and 5. * [vcgencmd](vcgencmd/) - A tool to send commands to the VideoCore firmware and diff --git a/rpipmic/README.md b/rpipmic/README.md new file mode 100644 index 0000000..952632e --- /dev/null +++ b/rpipmic/README.md @@ -0,0 +1,9 @@ +# rpipmic + +Tool to report PMIC events on Raspberry Pi 5 family devices (Pi 5, Pi 500, +CM5). + +The bootloader clears fatal PMIC events at power on, so the reason for the +last PMIC reset is read from /proc/device-tree/chosen/power/power_reset. +Events latched since boot are read from PMIC register 0x3018 using vcgencmd, +if available. diff --git a/rpipmic/rpipmic b/rpipmic/rpipmic new file mode 100644 index 0000000..231d4f6 --- /dev/null +++ b/rpipmic/rpipmic @@ -0,0 +1,101 @@ +#!/usr/bin/env bash +# +# Reports PMIC events on Raspberry Pi 5 (BCM2712) family devices. +# +# The bootloader clears fatal PMIC events at power on and passes them on in +# /proc/device-tree/chosen/power/power_reset. Events latched since boot are +# read from the PMIC EVT status register via vcgencmd, if available. +# +# Usage: ./rpipmic + +set -euo pipefail + +DT=/proc/device-tree +POWER_RESET="$DT/chosen/power/power_reset" +REG_ADDR="0x3018" + +# bit -> "NAME:description" for power_reset +RESET_REASONS=( + "0:OVER_VOLTAGE: over voltage" + "1:UNDER_VOLTAGE: under voltage" + "2:OVER_TEMPERATURE: over temperature" + "3:ENABLE: chip reset/halt caused by a low on the ENABLE pin" + "4:WATCHDOG: watchdog timer timed out" +) + +# bit -> "NAME:description" for the EVT register at REG_ADDR +EVENTS=( + "0:EVT_PB_SHORT_PRESS: short press on the power button was detected" + "1:EVT_OVERTEMP_WARN: over-temperature warning" + "2:EVT_OVERTEMP_RESET: over-temperature reset occurred" + "3:EVT_WDT_RESET: watchdog timer timed out" + "4:EVT_ENABLE_LOW: chip reset/halt caused by a low on the ENABLE pin" + "5:EVT_UVLO_WARN: supply voltage fell below the warning threshold" + "6:EVT_UVLO_SHUT: last shutdown was caused by supply voltage falling below the critical threshold" + "7:EVT_VDD_SNS_OV: over-voltage detected on the VDD_SNS pin" +) + +error_found=0 + +report_bits() { + local value=$1 + shift + local entry bit rest name desc found=0 + + for entry in "$@"; do + bit="${entry%%:*}" + rest="${entry#*:}" + name="${rest%%:*}" + desc="${rest#*:}" + + if (( (value >> bit) & 1 )); then + echo " [ERROR] $name -$desc" + found=1 + fi + done + + if (( found )); then + error_found=1 + else + echo " No error events detected." + fi +} + +if ! tr '\0' '\n' < "$DT/compatible" 2>/dev/null | grep -qx 'brcm,bcm2712'; then + echo "error: this tool only supports the Raspberry Pi 5 family PMIC" >&2 + exit 1 +fi + +if [[ -r "$POWER_RESET" ]]; then + reset_value=0 + for byte in $(od -An -v -tu1 "$POWER_RESET"); do + reset_value=$(( (reset_value << 8) | byte )) + done + echo "PMIC reset reason at power on ($POWER_RESET) = 0x$(printf '%08x' "$reset_value")" + report_bits "$reset_value" "${RESET_REASONS[@]}" +else + echo "warning: $POWER_RESET not found (bootloader too old?)" >&2 +fi + +if ! command -v vcgencmd >/dev/null 2>&1; then + echo "warning: vcgencmd not found, skipping PMIC EVT register" >&2 + exit "$error_found" +fi + +raw_output=$(vcgencmd pmicrd "$REG_ADDR" 2>&1 || true) + +# expected form: "[3018] = 00" +hex_value=$(echo "$raw_output" | awk -F'=' '{print $2}' | tr -d '[:space:]') +hex_value=${hex_value#0x} + +if [[ -z "$hex_value" || ! "$hex_value" =~ ^[0-9a-fA-F]+$ ]]; then + echo "warning: could not read PMIC EVT register: '$raw_output'" >&2 + exit "$error_found" +fi + +reg_value=$((16#$hex_value)) + +echo "PMIC EVT register since boot ($REG_ADDR) = 0x$(printf '%02x' "$reg_value")" +report_bits "$reg_value" "${EVENTS[@]}" + +exit "$error_found"