Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests: Add test to verify power supply properties

Add a kselftest that verifies power supply properties from sysfs and
uevent. It checks whether they are present, readable and return valid
values.

This initial set of properties is not comprehensive, but rather the ones
that I was able to validate locally.

Co-developed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Signed-off-by: Nícolas F. R. A. Prado <nfraprado@collabora.com>
Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>

authored by

Nícolas F. R. A. Prado and committed by
Shuah Khan
4a679c5a 2dd0b5a8

+298
+1
MAINTAINERS
··· 17524 17524 F: drivers/power/supply/ 17525 17525 F: include/linux/power/ 17526 17526 F: include/linux/power_supply.h 17527 + F: tools/testing/selftests/power_supply/ 17527 17528 17528 17529 POWERNV OPERATOR PANEL LCD DISPLAY DRIVER 17529 17530 M: Suraj Jitindar Singh <sjitindarsingh@gmail.com>
+1
tools/testing/selftests/Makefile
··· 67 67 TARGETS += perf_events 68 68 TARGETS += pidfd 69 69 TARGETS += pid_namespace 70 + TARGETS += power_supply 70 71 TARGETS += powerpc 71 72 TARGETS += prctl 72 73 TARGETS += proc
+4
tools/testing/selftests/power_supply/Makefile
··· 1 + TEST_PROGS := test_power_supply_properties.sh 2 + TEST_FILES := helpers.sh 3 + 4 + include ../lib.mk
+178
tools/testing/selftests/power_supply/helpers.sh
··· 1 + #!/bin/sh 2 + # SPDX-License-Identifier: GPL-2.0 3 + # 4 + # Copyright (c) 2022, 2024 Collabora Ltd 5 + SYSFS_SUPPLIES=/sys/class/power_supply 6 + 7 + calc() { 8 + awk "BEGIN { print $* }"; 9 + } 10 + 11 + test_sysfs_prop() { 12 + PROP="$1" 13 + VALUE="$2" # optional 14 + 15 + PROP_PATH="$SYSFS_SUPPLIES"/"$DEVNAME"/"$PROP" 16 + TEST_NAME="$DEVNAME".sysfs."$PROP" 17 + 18 + if [ -z "$VALUE" ]; then 19 + ktap_test_result "$TEST_NAME" [ -f "$PROP_PATH" ] 20 + else 21 + ktap_test_result "$TEST_NAME" grep -q "$VALUE" "$PROP_PATH" 22 + fi 23 + } 24 + 25 + to_human_readable_unit() { 26 + VALUE="$1" 27 + UNIT="$2" 28 + 29 + case "$VALUE" in 30 + *[!0-9]* ) return ;; # Not a number 31 + esac 32 + 33 + if [ "$UNIT" = "uA" ]; then 34 + new_unit="mA" 35 + div=1000 36 + elif [ "$UNIT" = "uV" ]; then 37 + new_unit="V" 38 + div=1000000 39 + elif [ "$UNIT" = "uAh" ]; then 40 + new_unit="Ah" 41 + div=1000000 42 + elif [ "$UNIT" = "uW" ]; then 43 + new_unit="mW" 44 + div=1000 45 + elif [ "$UNIT" = "uWh" ]; then 46 + new_unit="Wh" 47 + div=1000000 48 + else 49 + return 50 + fi 51 + 52 + value_converted=$(calc "$VALUE"/"$div") 53 + echo "$value_converted" "$new_unit" 54 + } 55 + 56 + _check_sysfs_prop_available() { 57 + PROP=$1 58 + 59 + PROP_PATH="$SYSFS_SUPPLIES"/"$DEVNAME"/"$PROP" 60 + TEST_NAME="$DEVNAME".sysfs."$PROP" 61 + 62 + if [ ! -e "$PROP_PATH" ] ; then 63 + ktap_test_skip "$TEST_NAME" 64 + return 1 65 + fi 66 + 67 + if ! cat "$PROP_PATH" >/dev/null; then 68 + ktap_print_msg "Failed to read" 69 + ktap_test_fail "$TEST_NAME" 70 + return 1 71 + fi 72 + 73 + return 0 74 + } 75 + 76 + test_sysfs_prop_optional() { 77 + PROP=$1 78 + UNIT=$2 # optional 79 + 80 + TEST_NAME="$DEVNAME".sysfs."$PROP" 81 + 82 + _check_sysfs_prop_available "$PROP" || return 83 + DATA=$(cat "$SYSFS_SUPPLIES"/"$DEVNAME"/"$PROP") 84 + 85 + ktap_print_msg "Reported: '$DATA' $UNIT ($(to_human_readable_unit "$DATA" "$UNIT"))" 86 + ktap_test_pass "$TEST_NAME" 87 + } 88 + 89 + test_sysfs_prop_optional_range() { 90 + PROP=$1 91 + MIN=$2 92 + MAX=$3 93 + UNIT=$4 # optional 94 + 95 + TEST_NAME="$DEVNAME".sysfs."$PROP" 96 + 97 + _check_sysfs_prop_available "$PROP" || return 98 + DATA=$(cat "$SYSFS_SUPPLIES"/"$DEVNAME"/"$PROP") 99 + 100 + if [ "$DATA" -lt "$MIN" ] || [ "$DATA" -gt "$MAX" ]; then 101 + ktap_print_msg "'$DATA' is out of range (min=$MIN, max=$MAX)" 102 + ktap_test_fail "$TEST_NAME" 103 + else 104 + ktap_print_msg "Reported: '$DATA' $UNIT ($(to_human_readable_unit "$DATA" "$UNIT"))" 105 + ktap_test_pass "$TEST_NAME" 106 + fi 107 + } 108 + 109 + test_sysfs_prop_optional_list() { 110 + PROP=$1 111 + LIST=$2 112 + 113 + TEST_NAME="$DEVNAME".sysfs."$PROP" 114 + 115 + _check_sysfs_prop_available "$PROP" || return 116 + DATA=$(cat "$SYSFS_SUPPLIES"/"$DEVNAME"/"$PROP") 117 + 118 + valid=0 119 + 120 + OLDIFS=$IFS 121 + IFS="," 122 + for item in $LIST; do 123 + if [ "$DATA" = "$item" ]; then 124 + valid=1 125 + break 126 + fi 127 + done 128 + if [ "$valid" -eq 1 ]; then 129 + ktap_print_msg "Reported: '$DATA'" 130 + ktap_test_pass "$TEST_NAME" 131 + else 132 + ktap_print_msg "'$DATA' is not a valid value for this property" 133 + ktap_test_fail "$TEST_NAME" 134 + fi 135 + IFS=$OLDIFS 136 + } 137 + 138 + dump_file() { 139 + FILE="$1" 140 + while read -r line; do 141 + ktap_print_msg "$line" 142 + done < "$FILE" 143 + } 144 + 145 + __test_uevent_prop() { 146 + PROP="$1" 147 + OPTIONAL="$2" 148 + VALUE="$3" # optional 149 + 150 + UEVENT_PATH="$SYSFS_SUPPLIES"/"$DEVNAME"/uevent 151 + TEST_NAME="$DEVNAME".uevent."$PROP" 152 + 153 + if ! grep -q "POWER_SUPPLY_$PROP=" "$UEVENT_PATH"; then 154 + if [ "$OPTIONAL" -eq 1 ]; then 155 + ktap_test_skip "$TEST_NAME" 156 + else 157 + ktap_print_msg "Missing property" 158 + ktap_test_fail "$TEST_NAME" 159 + fi 160 + return 161 + fi 162 + 163 + if ! grep -q "POWER_SUPPLY_$PROP=$VALUE" "$UEVENT_PATH"; then 164 + ktap_print_msg "Invalid value for uevent property, dumping..." 165 + dump_file "$UEVENT_PATH" 166 + ktap_test_fail "$TEST_NAME" 167 + else 168 + ktap_test_pass "$TEST_NAME" 169 + fi 170 + } 171 + 172 + test_uevent_prop() { 173 + __test_uevent_prop "$1" 0 "$2" 174 + } 175 + 176 + test_uevent_prop_optional() { 177 + __test_uevent_prop "$1" 1 "$2" 178 + }
+114
tools/testing/selftests/power_supply/test_power_supply_properties.sh
··· 1 + #!/bin/sh 2 + # SPDX-License-Identifier: GPL-2.0 3 + # 4 + # Copyright (c) 2022, 2024 Collabora Ltd 5 + # 6 + # This test validates the power supply uAPI: namely, the files in sysfs and 7 + # lines in uevent that expose the power supply properties. 8 + # 9 + # By default all power supplies available are tested. Optionally the name of a 10 + # power supply can be passed as a parameter to test only that one instead. 11 + DIR="$(dirname "$(readlink -f "$0")")" 12 + 13 + . "${DIR}"/../kselftest/ktap_helpers.sh 14 + 15 + . "${DIR}"/helpers.sh 16 + 17 + count_tests() { 18 + SUPPLIES=$1 19 + 20 + # This needs to be updated every time a new test is added. 21 + NUM_TESTS=33 22 + 23 + total_tests=0 24 + 25 + for i in $SUPPLIES; do 26 + total_tests=$(("$total_tests" + "$NUM_TESTS")) 27 + done 28 + 29 + echo "$total_tests" 30 + } 31 + 32 + ktap_print_header 33 + 34 + SYSFS_SUPPLIES=/sys/class/power_supply/ 35 + 36 + if [ $# -eq 0 ]; then 37 + supplies=$(ls "$SYSFS_SUPPLIES") 38 + else 39 + supplies=$1 40 + fi 41 + 42 + ktap_set_plan "$(count_tests "$supplies")" 43 + 44 + for DEVNAME in $supplies; do 45 + ktap_print_msg Testing device "$DEVNAME" 46 + 47 + if [ ! -d "$SYSFS_SUPPLIES"/"$DEVNAME" ]; then 48 + ktap_test_fail "$DEVNAME".exists 49 + ktap_exit_fail_msg Device does not exist 50 + fi 51 + 52 + ktap_test_pass "$DEVNAME".exists 53 + 54 + test_uevent_prop NAME "$DEVNAME" 55 + 56 + test_sysfs_prop type 57 + SUPPLY_TYPE=$(cat "$SYSFS_SUPPLIES"/"$DEVNAME"/type) 58 + # This fails on kernels < 5.8 (needs 2ad3d74e3c69f) 59 + test_uevent_prop TYPE "$SUPPLY_TYPE" 60 + 61 + test_sysfs_prop_optional usb_type 62 + 63 + test_sysfs_prop_optional_range online 0 2 64 + test_sysfs_prop_optional_range present 0 1 65 + 66 + test_sysfs_prop_optional_list status "Unknown","Charging","Discharging","Not charging","Full" 67 + 68 + # Capacity is reported as percentage, thus any value less than 0 and 69 + # greater than 100 are not allowed. 70 + test_sysfs_prop_optional_range capacity 0 100 "%" 71 + 72 + test_sysfs_prop_optional_list capacity_level "Unknown","Critical","Low","Normal","High","Full" 73 + 74 + test_sysfs_prop_optional model_name 75 + test_sysfs_prop_optional manufacturer 76 + test_sysfs_prop_optional serial_number 77 + test_sysfs_prop_optional_list technology "Unknown","NiMH","Li-ion","Li-poly","LiFe","NiCd","LiMn" 78 + 79 + test_sysfs_prop_optional cycle_count 80 + 81 + test_sysfs_prop_optional_list scope "Unknown","System","Device" 82 + 83 + test_sysfs_prop_optional input_current_limit "uA" 84 + test_sysfs_prop_optional input_voltage_limit "uV" 85 + 86 + # Technically the power-supply class does not limit reported values. 87 + # E.g. one could expose an RTC backup-battery, which goes below 1.5V or 88 + # an electric vehicle battery with over 300V. But most devices do not 89 + # have a step-up capable regulator behind the battery and operate with 90 + # voltages considered safe to touch, so we limit the allowed range to 91 + # 1.8V-60V to catch drivers reporting incorrectly scaled values. E.g. a 92 + # common mistake is reporting data in mV instead of µV. 93 + test_sysfs_prop_optional_range voltage_now 1800000 60000000 "uV" 94 + test_sysfs_prop_optional_range voltage_min 1800000 60000000 "uV" 95 + test_sysfs_prop_optional_range voltage_max 1800000 60000000 "uV" 96 + test_sysfs_prop_optional_range voltage_min_design 1800000 60000000 "uV" 97 + test_sysfs_prop_optional_range voltage_max_design 1800000 60000000 "uV" 98 + 99 + # current based systems 100 + test_sysfs_prop_optional current_now "uA" 101 + test_sysfs_prop_optional current_max "uA" 102 + test_sysfs_prop_optional charge_now "uAh" 103 + test_sysfs_prop_optional charge_full "uAh" 104 + test_sysfs_prop_optional charge_full_design "uAh" 105 + 106 + # power based systems 107 + test_sysfs_prop_optional power_now "uW" 108 + test_sysfs_prop_optional energy_now "uWh" 109 + test_sysfs_prop_optional energy_full "uWh" 110 + test_sysfs_prop_optional energy_full_design "uWh" 111 + test_sysfs_prop_optional energy_full_design "uWh" 112 + done 113 + 114 + ktap_finished