#!/bin/bash # # Power Control tool # Enable/disable AC relay # On/off System by step moter to press power key export PATH=$PATH:/usr/sbin:/usr/libexec # shellcheck source=meta-facebook/meta-bletchley/recipes-bletchley/plat-tools/files/bletchley-common-functions source /usr/libexec/bletchley-common-functions DELAY_POWER_ON="0.5" DELAY_POWER_OFF="10" DELAY_POWER_RECOVERY_MODE="10" POWER_BTN_TIMEOUT_SEC=10 REV_EVT="EVT" REV_DVT="DVT" REV_UNKNOW="UNKNOW" DBUS_HOST_ST_ON="xyz.openbmc_project.State.Host.HostState.Running" DBUS_HOST_ST_OFF="xyz.openbmc_project.State.Host.HostState.Off" HOST_ST_UNKNOW="Unknow" HOST_ST_ON="On" HOST_ST_OFF="Off" HOST_ST_SLEEP="Sleep" HOST_ST_DFU="DFU" HOST_ST_RECOVERY="Recovery" HOST_AC_ON="AC On" HOST_AC_OFF="AC Off" ACTION_ON="on" ACTION_OFF="off" ACTION_DFU="dfu" ACTION_USB_PD_DFU="usb-pd-dfu" ACTION_RECOVERY="recovery" ACTION_CYCLE="cycle" ACTION_RESET="reset" ACTION_AC_ON="ac-on" ACTION_AC_OFF="ac-off" ACTION_STATUS="status" ACTION_BOOT_MODE="boot-from-bootmode" VALID_SLED_ACTIONS=" $ACTION_ON $ACTION_OFF $ACTION_AC_ON $ACTION_AC_OFF $ACTION_STATUS $ACTION_DFU $ACTION_USB_PD_DFU $ACTION_RECOVERY $ACTION_CYCLE $ACTION_RESET $ACTION_BOOT_MODE " BOOT_MODE_REGULAR="\"xyz.openbmc_project.Control.Boot.Mode.Modes.Regular\"" BOOT_MODE_SAFE="\"xyz.openbmc_project.Control.Boot.Mode.Modes.Safe\"" BOOT_MODE_SETUP="\"xyz.openbmc_project.Control.Boot.Mode.Modes.Setup\"" function is_valid_sled_action() { local ACTION=$1 for i in $VALID_SLED_ACTIONS do if [ "$i" = "$ACTION" ]; then return 0 fi done return 1 } function get_board_rev() { local rev_id0 local rev_id1 local rev_id2 local rev_val rev_id0=$(get_gpio "REV_ID0") rev_id1=$(get_gpio "REV_ID1") rev_id2=$(get_gpio "REV_ID2") rev_val=$((rev_id0+(rev_id1<<1)+(rev_id2<<2))) case $rev_val in 0) echo "$REV_EVT" ;; 1) echo "$REV_DVT" ;; *) echo "$REV_UNKNOW" return 1 ;; esac return 0 } function trigger_power_button() { local sled_num=$1 local delay_time=$2 local sled_gpio_num sled_gpio_num=$(sled_mapping "$1") #SLED{N}_MS_DETECT1 (initial position) GPIO_DETECT_PIN1="SLED${sled_gpio_num}_MS_DETECT1" #SLED{N}_MS_DETECT0 (MAC position) GPIO_DETECT_PIN0="SLED${sled_gpio_num}_MS_DETECT0" echo "Motor go forward to press Power key" motor-ctrl "sled${sled_num}" f >/dev/null wait_gpio_falling "${GPIO_DETECT_PIN0}" "$POWER_BTN_TIMEOUT_SEC" motor-ctrl "sled${sled_num}" s >/dev/null if [ "$(get_gpio "$GPIO_DETECT_PIN0")" -eq 0 ];then echo "Power key switch triggered" echo "Press power key for Sled${1} ${delay_time} seconds..." sleep "$delay_time" else echo "Power key switch not trigger, back motor to initial position" fi motor-ctrl "sled${sled_num}" r >/dev/null wait_gpio_falling "${GPIO_DETECT_PIN1}" "$POWER_BTN_TIMEOUT_SEC" motor-ctrl "sled${sled_num}" s >/dev/null if [ "$(get_gpio "$GPIO_DETECT_PIN1")" -eq 0 ];then echo "Motor reverse to initial position successful" else echo "Initial position switch not trigger, force stop motor" fi } function release_power_button() { local sled_num=$1 local sled_gpio_num sled_gpio_num=$(sled_mapping "$1") GPIO_DETECT_PIN1="SLED${sled_gpio_num}_MS_DETECT1" if [ "$(get_gpio "$GPIO_DETECT_PIN1")" -eq 0 ]; then echo "Motor at initial position already" return 0 fi motor-ctrl "sled${sled_num}" r >/dev/null wait_gpio_falling "${GPIO_DETECT_PIN1}" "$POWER_BTN_TIMEOUT_SEC" motor-ctrl "sled${sled_num}" s >/dev/null if [ "$(get_gpio "$GPIO_DETECT_PIN1")" -eq 0 ];then echo "Motor reverse to initial position successful" return 0 fi echo "Error: Initial position switch not trigger" return 1 } function press_power_button() { local sled_num=$1 local sled_gpio_num sled_gpio_num=$(sled_mapping "$1") GPIO_DETECT_PIN0="SLED${sled_gpio_num}_MS_DETECT0" echo "Motor go forward to press Power button" motor-ctrl "sled${sled_num}" f >/dev/null wait_gpio_falling "${GPIO_DETECT_PIN0}" "$POWER_BTN_TIMEOUT_SEC" motor-ctrl "sled${sled_num}" s >/dev/null if [ "$(get_gpio "$GPIO_DETECT_PIN0")" -eq 0 ];then echo "Power button switch triggered" return 0 fi echo "Error: Power button switch not trigger" return 1 } function get_ac_status() { local i2c_bus i2c_bus=$(get_bus_num "$1") p1_output_reg=$(i2cget -f -y "$i2c_bus" 0x76 0x03) p1_config_reg=$(i2cget -f -y "$i2c_bus" 0x76 0x07) host_pwr="$(( (p1_output_reg & 0x80)>>7 ))" is_output="$(( (~p1_config_reg & 0x80)>>7 ))" if [ "$(( host_pwr & is_output ))" -eq 1 ];then echo "$HOST_AC_ON" else echo "$HOST_AC_OFF" fi } function get_host_status_dbus() { local sled_num=$1 local object="/xyz/openbmc_project/state/host${sled_num}" local service="xyz.openbmc_project.State.Host${sled_num}" local interface="xyz.openbmc_project.State.Host" local property="CurrentHostState" local host_state host_state=$(busctl get-property "$service" "$object" "$interface" "$property" | cut -d '"' -f2) if [ "$host_state" = "$DBUS_HOST_ST_ON" ]; then echo "$HOST_ST_ON" elif [ "$host_state" = "$DBUS_HOST_ST_OFF" ]; then echo "$HOST_ST_OFF" else echo "$HOST_ST_UNKNOW" return 1 fi return 0 } function get_addr_from_dts_aliases() { local node_address node_address=$(awk -F '@' '{printf $2}' /sys/firmware/devicetree/base/aliases/"$1") echo "$node_address" } function get_host_status_mdio() { local SLED_NUM=$1 local MDIO_BUS MDIO_BUS="$(get_addr_from_dts_aliases mdio0).mdio-1" # check /dev/mem if ! create_dev_mem; then return 1 fi local CHECK_CNT=0 local MDIO_ERR_CNT=0 local CUR_HOST_ST=$HOST_ST_UNKNOW local SLED_LAST_ACTION if [ -f /tmp/sled"${SLED_NUM}"-last-action ]; then SLED_LAST_ACTION=$(cat /tmp/sled"${SLED_NUM}"-last-action) fi while true do if POST_ST_VAL=$(mdio "$MDIO_BUS" phy "${HOST_MDIO_PORT_MAP[SLED_NUM]}" 0); then if [ $((POST_ST_VAL&16#0800)) -eq $((16#0000)) ]; then case $SLED_LAST_ACTION in "$ACTION_DFU") TMP_HOST_ST="$HOST_ST_DFU" ;; *) TMP_HOST_ST="$HOST_ST_OFF" ;; esac elif [ $((POST_ST_VAL&16#0A00)) -eq $((16#0A00)) ]; then TMP_HOST_ST="$HOST_ST_ON" case $SLED_LAST_ACTION in "$ACTION_RECOVERY") TMP_HOST_ST="$HOST_ST_RECOVERY" ;; *) TMP_HOST_ST="$HOST_ST_ON" ;; esac elif [ $((POST_ST_VAL&16#0900)) -eq $((16#0900)) ]; then TMP_HOST_ST="$HOST_ST_SLEEP" else TMP_HOST_ST="$HOST_ST_UNKNOW" fi if [ "$CUR_HOST_ST" == "$TMP_HOST_ST" ]; then CHECK_CNT=$((CHECK_CNT+1)) else CUR_HOST_ST=$TMP_HOST_ST CHECK_CNT=0 fi if [ "$CHECK_CNT" -ge 5 ]; then echo "$CUR_HOST_ST" break fi else MDIO_ERR_CNT=$((MDIO_ERR_CNT+1)) if [ "$MDIO_ERR_CNT" -ge 5 ]; then echo "$HOST_ST_UNKNOW" return 1 fi fi done return 0 } function get_host_status() { local SLED_NUM=$1 if [ "$(get_ac_status "$SLED_NUM")" == "$HOST_AC_OFF" ];then echo "$HOST_AC_OFF" return 0 fi get_host_status_cmd "$SLED_NUM" "$(get_board_rev)" return $? } function get_host_bootmode() { local BUS_NAME="xyz.openbmc_project.Settings" local OBJ_PATH="/xyz/openbmc_project/control/host${1}/boot" local INTF_NAME="xyz.openbmc_project.Control.Boot.Mode" busctl get-property "${BUS_NAME}" "${OBJ_PATH}" "${INTF_NAME}" BootMode | awk '{print $2}' } function do_action_reset() { # 1. Power off # 2. Power on local SLED_NUM=$1 local CUR_ST=$2 if [ "$CUR_ST" != "$HOST_ST_OFF" ]; then do_action_off "$SLED_NUM" else echo "sled${SLED_NUM}: already powered off" fi sleep 3 do_action_on "$SLED_NUM" } function do_action_cycle() { # 1. AC off # 2. AC on # 3. Power on local SLED_NUM=$1 do_action_ac_off "$SLED_NUM" sleep 3 do_action_ac_on "$SLED_NUM" sleep 3 do_action_on "$SLED_NUM" } function do_action_ac_on() { local SLED_NUM=$1 local sled_gpio_num sled_gpio_num=$(sled_mapping "$1") echo "sled${SLED_NUM}: turn on AC" set_gpio "power-host${sled_gpio_num}" 1 sleep 3 # Newer versions of the managed system needs 3 second delay echo "$ACTION_AC_ON" > "/tmp/sled${SLED_NUM}-last-action" } function do_action_ac_off() { local SLED_NUM=$1 local sled_gpio_num sled_gpio_num=$(sled_mapping "$1") echo "sled${SLED_NUM}: turn off AC" set_gpio "power-host${sled_gpio_num}" 0 echo "$ACTION_AC_OFF" > "/tmp/sled${SLED_NUM}-last-action" } function do_action_on() { local SLED_NUM=$1 echo "sled${SLED_NUM}: power on host" trigger_power_button "$SLED_NUM" "$DELAY_POWER_ON" sleep 10 # Mac mini need about 10 second to stable link status echo "$ACTION_ON" > "/tmp/sled${SLED_NUM}-last-action" } function do_action_off() { local SLED_NUM=$1 echo "sled${SLED_NUM}: power off host" trigger_power_button "$SLED_NUM" "$DELAY_POWER_OFF" echo "$ACTION_OFF" > "/tmp/sled${SLED_NUM}-last-action" } function do_action_recovery() { local SLED_NUM=$1 echo "sled${SLED_NUM}: trigger host recovery mode" trigger_power_button "$SLED_NUM" "$DELAY_POWER_RECOVERY_MODE" echo "$ACTION_RECOVERY" > "/tmp/sled${SLED_NUM}-last-action" } function do_action_dfu() { local SLED_NUM=$1 echo "sled${SLED_NUM}: trigger host dfu mode" # turn ac off, and hold for 25 seconds do_action_ac_off "$SLED_NUM" sleep 25 # press power button echo "SLED$SLED_NUM: pressing power button" if ! press_power_button "$SLED_NUM"; then echo "SLED$SLED_NUM: press power button failed" echo "SLED$SLED_NUM: releasing power button" release_power_button "$SLED_NUM" return 1 fi sleep 1 # turn ac on echo "SLED$SLED_NUM: turn ac-on" do_action_ac_on "$SLED_NUM" sleep 3 # release power button echo "SLED$SLED_NUM: releasing host power button" if ! release_power_button "$SLED_NUM"; then echo "SLED$SLED_NUM: release power button failed" return 1 fi echo "$ACTION_DFU" > "/tmp/sled${SLED_NUM}-last-action" } function host_state_on_action_handler() { local SLED_NUM=$1 local ACTION=$2 case $ACTION in "$ACTION_OFF") do_action_off "$SLED_NUM" ;; "$ACTION_AC_OFF") do_action_ac_off "$SLED_NUM" ;; "$ACTION_DFU") do_action_dfu "$SLED_NUM" ;; "$ACTION_USB_PD_DFU") do_action_usb_pd_dfu "$SLED_NUM" ;; "$ACTION_RESET") do_action_reset "$SLED_NUM" "$HOST_ST_ON" ;; "$ACTION_CYCLE") do_action_cycle "$SLED_NUM" ;; "$ACTION_ON") echo "already on" ;; *) echo "Invalid action ($ACTION) for current host state (On)" return 1 ;; esac } function host_state_sleep_action_handler() { local SLED_NUM=$1 local ACTION=$2 case $ACTION in "$ACTION_ON") do_action_on "$SLED_NUM" ;; "$ACTION_OFF") do_action_off "$SLED_NUM" ;; "$ACTION_AC_OFF") do_action_ac_off "$SLED_NUM" ;; "$ACTION_DFU") do_action_dfu "$SLED_NUM" ;; "$ACTION_USB_PD_DFU") do_action_usb_pd_dfu "$SLED_NUM" ;; "$ACTION_RESET") do_action_reset "$SLED_NUM" "$HOST_ST_ON" ;; "$ACTION_CYCLE") do_action_cycle "$SLED_NUM" ;; *) echo "Invalid action ($ACTION) for current host state (Sleep)" return 1 ;; esac } function host_state_off_action_handler() { local SLED_NUM=$1 local ACTION=$2 case $ACTION in "$ACTION_ON") do_action_on "$SLED_NUM" ;; "$ACTION_RECOVERY") do_action_recovery "$SLED_NUM" ;; "$ACTION_DFU") do_action_dfu "$SLED_NUM" ;; "$ACTION_AC_OFF") do_action_ac_off "$SLED_NUM" ;; "$ACTION_RESET") do_action_reset "$SLED_NUM" "$HOST_ST_ON" ;; "$ACTION_CYCLE") do_action_cycle "$SLED_NUM" ;; "$ACTION_OFF") echo "already off" ;; *) echo "Invalid action ($ACTION) for current host state (Off)" return 1 ;; esac } function host_state_ac_off_action_handler() { local SLED_NUM=$1 local ACTION=$2 case $ACTION in "$ACTION_AC_ON") do_action_ac_on "$SLED_NUM" ;; "$ACTION_DFU") do_action_dfu "$SLED_NUM" ;; "$ACTION_AC_OFF") echo "sled${SLED_NUM}: already ac off" return 1 ;; "$ACTION_CYCLE") do_action_reset "$SLED_NUM" ;; *) echo "Invalid action ($ACTION) for current host state (AC Off)" return 1 ;; esac } function host_state_ac_on_action_handler() { local SLED_NUM=$1 local ACTION=$2 case $ACTION in "$ACTION_AC_OFF") do_action_ac_off "$SLED_NUM" ;; "$ACTION_DFU") do_action_dfu "$SLED_NUM" ;; "$ACTION_CYCLE") do_action_cycle "$SLED_NUM" ;; *) echo "sled${SLED_NUM}: already ac on" return 1 ;; esac } function host_state_recovery_action_handler() { local SLED_NUM=$1 local ACTION=$2 case $ACTION in "$ACTION_OFF") do_action_off "$SLED_NUM" ;; "$ACTION_AC_OFF") do_action_ac_off "$SLED_NUM" ;; "$ACTION_RESET") do_action_reset "$SLED_NUM" "$HOST_ST_ON" ;; "$ACTION_CYCLE") do_action_cycle "$SLED_NUM" ;; *) echo "Invalid action ($ACTION) for current host state (Recovery)" return 1 ;; esac } function host_state_dfu_action_handler() { local SLED_NUM=$1 local ACTION=$2 case $ACTION in "$ACTION_AC_OFF") do_action_ac_off "$SLED_NUM" ;; "$ACTION_RESET") do_action_reset "$SLED_NUM" "$HOST_ST_ON" ;; "$ACTION_CYCLE") do_action_cycle "$SLED_NUM" ;; *) echo "Invalid action ($ACTION) for current host state (DFU)" return 1 ;; esac } function create_dev_mem() { CHECK_CNT=0 while true do CHECK_CNT=$((CHECK_CNT+1)) if [ -c /dev/mem ]; then # /dev/mem already exist return 0 elif mknod /dev/mem c 1 1; then # mknod success return 0 elif [ "$CHECK_CNT" -ge 5 ]; then break fi sleep 1 done echo "create /dev/mem failed" return 1 } function do_action_usb_pd_dfu() { local SLED_INDEX="$1" local I2C_BUS I2C_BUS=$(get_bus_num "$1") local RETRY_MAX=5 local INTERVAL=1 # Polling interval in seconds local RETRY=0 local -a PORT_PATH=( /sys/bus/i2c/devices/"${I2C_BUS}"-0022/typec/port*/data_role ) # Check if the port exists if [ ! -e "${PORT_PATH[0]}" ]; then echo "Error: ${PORT_PATH[0]} not found" return 1 fi # Initial check for device role local DATA_ROLE DATA_ROLE=$(cat "${PORT_PATH[0]}") if [[ "$DATA_ROLE" == *"[device]"* ]]; then echo "Start to reboot Mac mini. Data role: $DATA_ROLE" i2cset -f -y "$I2C_BUS" 0x22 0x43 0x12 0x16 0x16 0x1b 0x8e 0x4f 0x30 0x12 0x80 0xac 0x05 0x05 0x01 0x00 0x00 0x00 0x00 0x00 0x80 0xff 0x14 0xfe 0xa1 i sleep "$INTERVAL" fi while [ "$RETRY" -lt "$RETRY_MAX" ]; do DATA_ROLE=$(cat "${PORT_PATH[0]}") if [[ "$DATA_ROLE" == *"[host]"* ]]; then echo "Start to execute DFU command, Data role: $DATA_ROLE" i2cset -f -y "$I2C_BUS" 0x22 0x43 0x12 0x16 0x1b 0x13 0x8e 0x6f 0x31 0x12 0x80 0xac 0x05 0x06 0x01 0x00 0x00 0x00 0x00 0x01 0x80 0xff 0x14 0xfe 0xa1 i echo "$ACTION_DFU" > "/tmp/sled${SLED_NUM}-last-action" return 0 fi RETRY=$((RETRY + 1)) # echo "Waiting for role switch. Data role: $DATA_ROLE, Retry=$RETRY" sleep "$INTERVAL" done echo "Timed out waiting for role switch to host" return 1 } function show_usage(){ echo "Usage: power-ctrl [$SLED_LIST] [$VALID_SLED_ACTIONS]" echo " power-ctrl chassis-cycle" } if [ $# -eq 1 ]; then if [ "$1" = "chassis-cycle" ];then echo "chassis cycle...." i2cset -y -f 12 0x11 0xd9 c exit 0 else echo "Invalid argument: [ $1 ]" show_usage exit 1; fi fi if [ $# -gt 2 ]; then echo "Too many arguments" show_usage exit 1; fi if is_sled_valid "$1"; then SLED=$1 ACTION=$2 SLED_NUM=${SLED:4} else echo "invalid sled name: ${1}" show_usage exit 1; fi #Check if sled is present if ! is_sled_present "${SLED_NUM}"; then echo "${SLED} is not present!" exit 1 elif ! is_valid_sled_action "$ACTION"; then echo "Unknown action: $ACTION" show_usage exit 1 fi if [ "$ACTION" = "$ACTION_AC_ON" ]; then if [ "$(get_ac_status "$SLED_NUM")" = "$HOST_AC_OFF" ]; then do_action_ac_on "$SLED_NUM" fi elif [ "$ACTION" = "$ACTION_AC_OFF" ]; then if [ "$(get_ac_status "$SLED_NUM")" != "$HOST_AC_OFF" ]; then do_action_ac_off "$SLED_NUM" fi elif [ "$ACTION" = "$ACTION_STATUS" ];then HOST_CURR_STATUS=$(get_host_status "$SLED_NUM") echo "$HOST_CURR_STATUS" else renice -10 $$ >/dev/null 2>&1 HOST_CURR_STATUS=$(get_host_status "$SLED_NUM") if [ "$ACTION" = "$ACTION_BOOT_MODE" ]; then BOOT_MODE=$(get_host_bootmode "$SLED_NUM") case "$BOOT_MODE" in "$BOOT_MODE_REGULAR") echo "Boot mode: on (regular)" ACTION="$ACTION_ON" ;; "$BOOT_MODE_SAFE") echo "Boot mode: recovery (safe)" ACTION="$ACTION_RECOVERY" ;; "$BOOT_MODE_SETUP") echo "Boot mode: dfu (setup)" ACTION="$ACTION_DFU" ;; *) echo "Boot mode: unknow" ;; esac fi case $HOST_CURR_STATUS in "$HOST_AC_OFF") host_state_ac_off_action_handler "$SLED_NUM" "$ACTION" ;; "$HOST_AC_ON") host_state_ac_on_action_handler "$SLED_NUM" "$ACTION" ;; "$HOST_ST_OFF") host_state_off_action_handler "$SLED_NUM" "$ACTION" ;; "$HOST_ST_ON") host_state_on_action_handler "$SLED_NUM" "$ACTION" ;; "$HOST_ST_SLEEP") host_state_sleep_action_handler "$SLED_NUM" "$ACTION" ;; "$HOST_ST_DFU") host_state_dfu_action_handler "$SLED_NUM" "$ACTION" ;; "$HOST_ST_RECOVERY") host_state_recovery_action_handler "$SLED_NUM" "$ACTION" ;; esac fi