#!/bin/bash

# Copyright 2020-present Facebook. All Rights Reserved.

# shellcheck disable=SC2034

SCMCPLD_SYSFS_DIR="$(i2c_device_sysfs_abspath 12-0043)"
SMBCPLD_SYSFS_DIR="$(i2c_device_sysfs_abspath 4-0023)"
FANCPLD_SYSFS_DIR="$(i2c_device_sysfs_abspath 6-0060)"
SCM_PWR_ON_SYSFS="${SCMCPLD_SYSFS_DIR}/cpu_control"
SCM_SMB_POWERGOOD_SYSFS="${SCMCPLD_SYSFS_DIR}/switchcard_powergood"
PWR_SYSTEM_SYSFS="${SCMCPLD_SYSFS_DIR}/chassis_power_cycle"
SMB_TH4_RST_ON_SYSFS="${SMBCPLD_SYSFS_DIR}/th4_reset"
SMB_TH4_PCI_RST_ON_SYSFS="${SMBCPLD_SYSFS_DIR}/th4_pci_reset"
SMB_TH4_I2C_EN_SYSFS="${SMBCPLD_SYSFS_DIR}/th4_i2c_bsc2_en"
SMB_FULL_POWER_SYSFS="${SMBCPLD_SYSFS_DIR}/smb_power_en"
PIM_SMB_MUX_RST="${SMBCPLD_SYSFS_DIR}/pim_smb_mux_rst"
PSU_SMB_MUX_RST="${SMBCPLD_SYSFS_DIR}/psu_smb_mux_rst"
SMB_P1_DETECT_PATH="/tmp/.smb_p1_board"
SMB_P2_DETECT_PATH="/tmp/.smb_p2_board"
DS4520_BUS=8
DS4520_DEV=$(( 0x50 ))
DS4520_IO0_REG=0xf2
UCD_SECURITY_REG=0xf1
WRITE_PROTECT_REG=0x10

wedge_iso_buf_enable() {
    return 0
}

wedge_iso_buf_disable() {
    return 0
}

wedge_is_bmc_personality() {
    io_reg_val=$(i2cget -y $DS4520_BUS $DS4520_DEV $DS4520_IO0_REG)
    if [ "$((io_reg_val & 0x1))" = "0" ]; then
        return 0
    else
        return 1
    fi
}

wedge_is_smb_p1() {
    # Check if we previously detected P1/P2 SMB
    if [ -f "$SMB_P1_DETECT_PATH" ]; then
        return 0
    elif [ -f "$SMB_P2_DETECT_PATH" ]; then
        return 1
    fi
    # Read SMB eeprom to determine if P1/P2
    if weutil smb | grep -q 'PCA014590[1|5]0[1|5|6]'; then
        touch "$SMB_P1_DETECT_PATH"
        return 0
    else
        touch "$SMB_P2_DETECT_PATH"
        return 1
    fi
}

wedge_is_us_on() {
    isWedgeOn="$(head -n 1 "$SCM_PWR_ON_SYSFS" 2> /dev/null)"
    if [ -z "$isWedgeOn" ]; then
        return 1
    elif [ "$isWedgeOn" = "0x1" ]; then
        return 0 # uServer is on
    else
        return 1
    fi
}

wedge_is_smb_powergood() {
    isPowerGood="$(head -n 1 "$SCM_SMB_POWERGOOD_SYSFS" 2> /dev/null)"
    if [ -z "$isPowerGood" ]; then
        return 1
    elif [ "$isPowerGood" = "0x1" ]; then
        return 0 # Power is good
    else
        return 1
    fi
}

wedge_board_type() {
    echo 'ELBERT'
}

wedge_slot_id() {
    printf "1\n"
}

wedge_board_rev() {
    # Assume P1
    return 1
}

wedge_should_enable_oob() {
    return 1
}

wedge_pim_bus() {
   # $1 -  pim range 2 - 9
   pim="$1"

   # PIM SMBus 16-23
   pim_index=(0 1 2 3 4 5 6 7)
   pim_bus=(16 17 18 19 20 21 22 23)
   if wedge_is_smb_p1; then
       # P1 has different PIM bus mapping
       pim_bus=(16 17 18 23 20 21 22 19)
   fi
   echo "${pim_bus[$((pim-2))]}"
}

wedge_is_pim_present() {
   # $1 -  pim range 2 - 9
   pim="$1"

   busId=$(wedge_pim_bus "$pim")
   pim_prsnt="$(head -n 1 "$SMBCPLD_SYSFS_DIR"/pim"$pim"_present)"
   if [ "$((pim_prsnt))" -eq 1 ]; then
      return 0
   prsnt_i2c="$(i2cget -f -y "$busId" 0x50 >/dev/null 2>&1 && echo 1 || echo 0)"
   elif [ "$((prsnt_i2c))" -eq 1 ]; then
      # PIM Eeprom found but PIM present bit is incorrect
      echo "PIM$pim present is false but eeprom detected."
      return 0
   fi
   return 1
}

wedge_pim_type() {
   pim="$1"

   busId=$(wedge_pim_bus "$pim")
   pim_type=$(/usr/bin/kv get pim"$pim"_type)
   echo "$pim_type"
}

wedge_power_off_asic() {
    # ASIC put into reset
    echo 1 > "$SMB_TH4_RST_ON_SYSFS"
    echo 1 > "$SMB_TH4_PCI_RST_ON_SYSFS"
    # Turn off power to ASIC
    echo 0 > "$SMB_FULL_POWER_SYSFS"
}

wedge_power_on_asic() {
    # Enable power to asic
    echo 1 > "$SMB_FULL_POWER_SYSFS"
    sleep 1

    if wedge_is_smb_powergood; then
        echo "Switchcard power is good..."
    else
        echo "FATAL ERROR: Switchcard power signal is NOT good."
    fi

    # Take ASIC out of reset. Order matters here
    echo 0 > "$SMB_TH4_RST_ON_SYSFS"
    usleep 250000
    echo 0 > "$SMB_TH4_PCI_RST_ON_SYSFS"
}

wedge_power_on_board() {
    wedge_power_off_asic
    wedge_power_on_asic
    echo 1 > "$SCM_PWR_ON_SYSFS"
}

wedge_power_off_board() {
    wedge_power_off_asic
    echo 0 > "$SCM_PWR_ON_SYSFS"
}

maybe_set_security_bitmask() {
    bus="$1"
    dev="$2"
    model="$3"

    if [[ "$model" == *"UCD9090"* ]]
    then
        # A 0 bit indicates the command is not write protected.
        # Each bit represents a command code where the codes
        # increment left to right. eg. byte 0, bit 7 = 00,
        # byte 0, bit 6 = 01, etc.
        #
        # The following commands are allowed:
        #   00       - PAGE
        #   03       - CLEAR_FAULTS
        #   D7       - RUN_TIME_CLOCK
        #   EA-ED,EF - FAULT commands
        #   F0       - EXECUTE_FLASH
        #   F1       - SECURITY
        #   F2       - SECURITY_BIT_MASK
        #   FA       - GPIO SELECT
        #   FB       - GPIO CONFIG
        #   FC       - MISC CONFIG
        i2cset -f -y "$bus" "$dev" 0xf2 \
           0x6f 0xff 0xff 0xff 0xff 0xff 0xff 0xff \
           0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff \
           0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff \
           0xff 0xff 0xfe 0xff 0xff 0xc2 0x1f 0xc7 s
    #else
    #    Model does not support security bitmask, so treat as no-op
    fi
}

enable_power_security_mode() {
    bus="$1"
    dev="$2"
    model="$3"

    # Some UCD models support a security bitmask
    maybe_set_security_bitmask "$bus" "$dev" "$model"

    locked="$(printf "%d" "$(i2cget -f -y "$bus" "$dev" "$UCD_SECURITY_REG" s | awk '{print $(NF)}')")"
    if [ "$locked" -eq 0 ]; then
        # Write a new 6-byte password, enabling security
        i2cset -f -y "$bus" "$dev" "$UCD_SECURITY_REG" 0x06 0x31 0x32 0x33 0x34 0x35 0x36 i
        locked="$(printf "%d" "$(i2cget -f -y "$bus" "$dev" "$UCD_SECURITY_REG" s | awk '{print $(NF)}')")"
        if [ "$locked" -eq 0 ]; then
            echo "Failed to lock $model $bus-00$dev"
            return 1
        fi
    else
        echo "UCD bus $bus device $dev already locked!"
        return 0
    fi
}

disable_power_security_mode() {
    bus="$1"
    dev="$2"
    model="$3"

    # If locked, unlock it
    locked="$(printf "%d" "$(i2cget -f -y "$bus" "$dev" "$UCD_SECURITY_REG" s | awk '{print $(NF)}')")"
    if [ "$locked" -eq 0 ]; then
        echo "UCD bus $bus device $dev already unlocked!"
        return 0
    else
        # If security is enabled, disable security by writing the correct password
        i2cset -f -y "$bus" "$dev" "$UCD_SECURITY_REG" 0x06 0x31 0x32 0x33 0x34 0x35 0x36 i
        # Make the security disable permanent. Otherwise a subsequent STORE_DEFAULT_ALL command
        # will persist the security setting.
        i2cset -f -y "$bus" "$dev" "$UCD_SECURITY_REG" 0x06 0xff 0xff 0xff 0xff 0xff 0xff i
        locked="$(printf "%d" "$(i2cget -f -y "$bus" "$dev" "$UCD_SECURITY_REG" s | awk '{print $(NF)}')")"
        if [ "$locked" -eq 0 ]; then
            return 0
        else
           echo "Failed to lock $model $bus-00$dev"
           return 1
        fi
    fi
}

enable_tps546d24_wp() {
    bus="$1"
    dev="$2"

    # Disable all writes except for WRITE_PROTECT and STORE_USER_ALL
    i2cset -f -y "$bus" "$dev" "$WRITE_PROTECT_REG" 0x80
}

disable_tps546d24_wp() {
    bus="$1"
    dev="$2"

    # Enable all writes
    i2cset -f -y "$bus" "$dev" "$WRITE_PROTECT_REG" 0x0
}

# Not all PIMs have the ISL68224. Calling this for a PIM
# without one is a no-op.
maybe_enable_isl_wp() {
    bus="$1"
    dev="$2"

    devhex=$(printf "%02x" "$dev")
    if [ ! -e /sys/bus/i2c/devices/"$bus"-00"$devhex" ]
    then
        return 0
    fi

    # Disable all writes except for WRITE_PROTECT, OPERATION, and PAGE
    i2cset -f -y "$bus" "$dev" "$WRITE_PROTECT_REG" 0x40
}

maybe_disable_isl_wp() {
    bus="$1"
    dev="$2"

    devhex=$(printf "%02x" "$dev")
    if [ ! -e /sys/bus/i2c/devices/"$bus"-00"$devhex" ]
    then
        return 0
    fi

    # Enable all writes
    i2cset -f -y "$bus" "$dev" 0x10 0x0
}

enable_switchcard_power_security() {
    retry_command 3 enable_power_security_mode 3 0x4e "UCD90160B"
    retry_command 3 maybe_enable_isl_wp 3 0x60
    retry_command 3 maybe_enable_isl_wp 3 0x62
}

disable_switchcard_power_security() {
    retry_command 3 disable_power_security_mode 3 0x4e "UCD90160B"
    retry_command 3 maybe_disable_isl_wp 3 0x60
    retry_command 3 maybe_disable_isl_wp 3 0x62
}

enable_psu_wp() {
    bus="$1"
    dev="$2"

    # Disable all writes except for WRITE_PROTECT, OPERATION, and PAGE
    i2cset -f -y "$bus" "$dev" "$WRITE_PROTECT_REG" 0x40
}

disable_psu_wp() {
    bus="$1"
    dev="$2"

    # Enable all writes
    i2cset -f -y "$bus" "$dev" "$WRITE_PROTECT_REG" 0x0
}

power_on_pim() {
    pim=$1
    old_rst_value=$(gpio_get PIM"${pim}"_FPGA_RESET_L keepdirection)
    if [ "$old_rst_value" -eq 1 ]; then
       # already powered on, skip
       return
    fi

    # Skip power_on if we are still powercycling
    if [ "$(head -n 1 "$SMBCPLD_SYSFS_DIR"/pim_reset)" != '0x0' ]; then
        logger pim_enable: skipping power_on_pim. PIM"$pim" is still powercycling
        return
    fi

    # Skip power_on if the FPGA is still being programmed
    pim_major=$(head -n 1 "$SMBCPLD_SYSFS_DIR"/pim"$pim"_fpga_rev_major)
    if [ "$((pim_major))" -eq 255 ]; then
        logger pim_enable: skipping power_on_pim. PIM"$pim" is fpga loading is not complete
        return
    fi

    fpga_done_value=$(gpio_get PIM"${pim}"_FPGA_DONE keepdirection)
    if [ "$fpga_done_value" -eq 0 ]; then
        logger pim_enable: skipping power_on_pim. PIM"$pim" is FPGA_DONE is not ready
        return
    fi

    sleep 0.5 # Give FPGA some time to finish initialization
    bus=$(wedge_pim_bus "$pim")
    dev="0x4e"
    gpio_set PIM"${pim}"_FPGA_RESET_L 1  # FPGA out of reset

    logger pim_enable: Locking PIM"$pim" UCD9090B security
    retry_command 3 enable_power_security_mode "$bus" "$dev" "UCD9090B"

    logger pim_enable: Enable WRITE_PROTECT on PIM"$pim" TPS/ISL chips
    retry_command 3 enable_tps546d24_wp "$bus" 0x16
    retry_command 3 enable_tps546d24_wp "$bus" 0x18
    retry_command 3 maybe_enable_isl_wp "$bus" 0x54

    sleep 0.1

    skip_pim_off_cache=$2
    pim_off_cache="/tmp/.pim${pim}_powered_off"
    if [ -z "${skip_pim_off_cache}" ] && [ -f "${pim_off_cache}" ]; then
       rm  "${pim_off_cache}"
    fi
    logger pim_enable: powered on PIM"${pim}"
}

power_off_pim() {
    pim=$1
    old_rst_value=$(gpio_get PIM"${pim}"_FPGA_RESET_L keepdirection)
    if [ "$old_rst_value" -eq 0 ]; then
       # already powered off, skip
       return
    fi

    bus=$(wedge_pim_bus "$pim")
    dev="0x4e"
    gpio_set PIM"${pim}"_FPGA_RESET_L 0  # FPGA in of reset
    sleep 0.1

    skip_pim_off_cache=$2
    pim_off_cache="/tmp/.pim${pim}_powered_off"
    if [ -z "${skip_pim_off_cache}" ]; then
       touch "${pim_off_cache}"
    fi
    logger pim_enable: powered off PIM"${pim}"
}

retry_command() {
    # Retry command up to $1 attempts
    local retries=$1
    shift

    local count=0
    until "$@"; do
        exit=$?
        count=$((count+1))
        if [ "$count" -lt "$retries" ]; then
            msg="Attempt $count/$retries failed with $exit, retrying..."
            echo "$msg"
            logger retry_command: "$msg"
        else
            msg="Retry $count/$retries failed with $exit, no more retries left"
            echo "$msg"
            logger retry_command: "$msg"
            return $exit
        fi
    done
    return 0
}

FWUPGRADE_PIDFILE="/var/run/firmware_upgrade.pid"
check_fwupgrade_running()
{
    exec 200>$FWUPGRADE_PIDFILE
    flock -n 200 || (echo "Another FW upgrade is running" && exit 1)
    ret=$?
    if [ $ret -eq 1 ]; then
      exit 1
    fi
    pid=$$
    echo $pid 1>&200
}


#!/bin/bash
#
# Copyright 2021-present Facebook. All Rights Reserved.
#
# This program file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program in a file named COPYING; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301 USA

function dpe_status(){
  regVal="$1"
  dpeResultStr="$2"
  if [ "$(head -n 1 /sys/bus/i2c/drivers/scmcpld/12-0043/dpe_image)" != "$regVal" ];
  then
    echo "${dpeResultStr} image detected. exiting..."
    exit 1
  fi
}

case "$1" in
    dpe)
        dpe_status "0x1" "dpe image"
        ;;
    nondpe)
        dpe_status "0x0" "non dpe image"
        ;;
esac


#!/bin/bash
#
# Copyright 2021-present Facebook. All Rights Reserved.
#
# This program file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program in a file named COPYING; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301 USA

# shellcheck disable=SC1091
source /usr/local/bin/dpm_utils.sh
source /usr/local/bin/openbmc-utils.sh

PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin

logfile="/mnt/data1/log/dpm_log"
logfile_old="${logfile}.1"
max_loglines=1000
switchcard_bus=3

# Check to see if the log file needs to be rotated
touch $logfile $logfile_old
lines="$(wc -l $logfile|cut -d' ' -f1)"
if [ "$lines" -gt $max_loglines ]
then
    echo "Rotating DPM log file"
    rm -f "$logfile_old"
    cp "$logfile" "$logfile_old"
    rm -f "$logfile"
fi

echo "Reading DPM logs..."
read_logs "$switchcard_bus" "SWITCHCARD" >> "$logfile"

# The UCD90160B does not support a security bitmask. Disable
# security in order to update the clock. Security will be
# re-enabled later in the boot.
retry_command 3 disable_power_security_mode 3 0x4e "UCD90160B"
update_clock "$switchcard_bus"
# For the switchcard UCD90160B, there are 18 bytes to clear
echo "Clearing SWITCHCARD DPM logs..."
i2cset -y 3 0x4e 0xea 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 s


#!/bin/bash
#
# Copyright 2021-present Facebook. All Rights Reserved.
#
# This program file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program in a file named COPYING; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301 USA

dpm_verbose="no"
print_debug() {
    if [ "$dpm_verbose" = "yes" ]
    then
        echo "$*" >&2
    fi
}

read_logs() {
    local bus="$1"
    local fault_source="$2"
    dpm_path="/sys/bus/i2c/drivers/ucd9000/$bus-004e"

    if [ -d "$dpm_path" ]; then
        echo "Script cannot run while driver present" >&2
        echo "Use the following commands to unbind/rebind" >&2
        echo 'echo "'"$bus"'-004e" > /sys/bus/i2c/drivers/ucd9000/unbind' >&2
        echo 'echo "'"$bus"'-004e" > /sys/bus/i2c/drivers/ucd9000/bind' >&2
        exit 1
    fi

    print_debug "Reading $fault_source DPM logs..."

    echo -n "{'Date':'$(date -u "+%Y-%m-%d %T")'"
    echo -n ",'Source':'$fault_source'"
    echo -n ",'Log':'$(i2cget -y "$bus" 0x4e 0xea s)'"

    eb="$(i2cget -y "$bus" 0x4e 0xeb w)"

    len="$((0x${eb:2:2}))"

    for (( i = 0 ; i < "$len" ; i++ ))
    do
      i2cset -y "$bus" 0x4e 0xeb "0x$len$(printf '%02x' "$i")" w
      man="$(i2cget -y "$bus" 0x4e 0xec s)"
      exp="$(i2cget -y "$bus" 0x4e 0x20 | tail -n 1)"
      echo -n ",'detail_$i':{'m':'$man','e':'$exp'}"
    done

    echo "}"
}

update_clock() {
    local bus="$1"

    # Make sure the time set set correctly on the DPM
    seconds="$(date +%s)"
    seconds_per_day="$((24 * 60 * 60))"
    days="$((seconds / seconds_per_day))"
    milliseconds="$((seconds % seconds_per_day * 1000))"
    d1="$((days >> 24))"
    d2="$((days >> 16 & 0xff))"
    d3="$((days >> 8 & 0xff))"
    d4="$((days & 0xff))"
    m1="$((milliseconds >> 24))"
    m2="$((milliseconds >> 16 & 0xff))"
    m3="$((milliseconds >> 8 & 0xff))"
    m4="$((milliseconds & 0xff))"

    print_debug "$fault_source: updating run time clock on DPM"
    i2cset -y "$bus" 0x4e 0xd7 "$m1" "$m2" "$m3" "$m4" "$d1" "$d2" "$d3" "$d4" s
}


#!/bin/bash

# shellcheck disable=SC1091
source /usr/local/bin/openbmc-utils.sh

show_dpm_ver() {
  name=$1
  bus=$2
  addr=$3
  dump=$(i2cdump -f -y "${bus}" "${addr}" s 0x9e | grep -v abcdef)
  dpm_ver="${dump##* }"
  echo "$name" "$dpm_ver"
}
echo "!!!"
echo "!!! WARNING: Displaying Digital Power Manager (DPM) version may disrupt sensor reading !!!"
echo "!!!"

scm_dpm_bus='9'
scm_dpm_addr='0x11'
smb_dpm_bus='3'
smb_dpm_addr='0x4e'

show_dpm_ver "SCM.:" "$scm_dpm_bus" "$scm_dpm_addr"
show_dpm_ver "SMB.:" "$smb_dpm_bus" "$smb_dpm_addr"

# PIM DPM
pim_index=(0 1 2 3 4 5 6 7)
# This is a map between PIM2-9 to associated i2c BUS
# Currently, this doesn't map in order between PIM2-9 to bus 16-23
pim_bus=(16 17 18 19 20 21 22 23)
if wedge_is_smb_p1; then
    # P1 has different PIM bus mapping
    pim_bus=(16 17 18 23 20 21 22 19)
fi
pim_dpm_addr='0x4e'

for i in "${pim_index[@]}"
do
    pim=$((i+2))
    # PIM numbered 2-9
    pim=$((i+2))
    if wedge_is_pim_present "$pim"; then
        pim_type=$(wedge_pim_type "$pim")
        if [[ "$pim_type" == "16q2" ]]; then
            echo "PIM${pim} has no DPM..."
        else
            # PIM 2-9, SMBUS 16-23
            bus_id="${pim_bus[$i]}"
            show_dpm_ver "PIM${pim}.:" "$bus_id" "$pim_dpm_addr"
        fi
    else
        echo "PIM${pim} not present... skipping."
    fi
done

show_isl_ver() {
  name="$1"
  bus="$2"
  addr="$3"

  mdl=$(i2cdump -f -y "${bus}" "${addr}" s 0x9a | grep 00: | awk '{print $5$4$3$2}')
  mfr=$(i2cdump -f -y "${bus}" "${addr}" s 0x9b | grep 00: | awk '{print $5$4$3$2}')
  rstr="SFT${mdl:1:5}${mdl:6:2}$(printf "%02d" "0x$mfr")"
  echo "$name: $rstr"
}

# ISL68226 Revision
show_isl_ver "SMB ISL68226" 3 0x62

# RAA228228 Revision
show_isl_ver "SMB RAA228228" 3 0x60

# PIM ISL Revision
pim_isl_addr='0x54'
for i in "${pim_index[@]}"
do
    pim=$((i+2))
    pim_type=$(wedge_pim_type "$pim")
    if [[ "$pim_type" == "8ddm" ]]; then
        bus_id="${pim_bus[$i]}"
        show_isl_ver "PIM${pim} ISL68224" "$bus_id" "$pim_isl_addr"
    elif [[ "$pim_type" == "16q2" ]]; then
        echo "PIM${pim} has no ISL... skipping."
    elif [[ "$pim_type" == "16q" ]]; then
        echo "PIM${pim} has no ISL... skipping."
    elif [[ "$pim_type" == "unplug" ]]; then
        echo "PIM${pim} not present... skipping."
    fi
done

echo "!!! Done."


#!/bin/bash

# shellcheck disable=SC1091
source /usr/local/bin/openbmc-utils.sh

printf "%-1s | %-3s | %-30s\n" "v" "dir" "GPIONAME"
for i in /tmp/gpionames/*
do
   gpio="$(basename "$i")"
   val="$(gpio_get_value "$gpio")"
   direction="$(gpio_get_direction "$gpio")"
   printf "%-1s | %-3s | %-30s\n" "$val" "$direction" "$gpio"
done


#!/bin/bash
#
# Copyright 2020-present Facebook. All Rights Reserved.
#
# This program file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program in a file named COPYING; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301 USA
#

# shellcheck disable=SC1091
source /usr/local/bin/openbmc-utils.sh

for index in 2 3 4 5 6 7 8 9
do
    pim_prsnt="$(head -n 1 "$SMBCPLD_SYSFS_DIR"/pim"$index"_present)"
    cache_file=/tmp/pim"$index"_serial.txt

    if [ ! -s "$cache_file" ] || [ -z "$(cat $cache_file)" ]; then
        if [ "$((pim_prsnt))" -eq 1 ]; then
            /usr/bin/weutil pim"$index" |grep Product|grep Serial|cut -d ' ' -f 4 > "$cache_file"
        else
            echo '' > "$cache_file"
        fi
    fi
    serial="$(cat $cache_file)"
    echo PIM${index} : "$serial"
done


#!/bin/bash
#
# Copyright 2020-present Facebook. All Rights Reserved.
#
# This program file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program in a file named COPYING; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301 USA
#

### BEGIN INIT INFO
# Provides:          eth0_mac_fixup.sh
# Required-Start:
# Required-Stop:
# Default-Start:     S
# Default-Stop:
# Short-Description:  Fixup the MAC address for eth0 based on wedge EEPROM
### END INIT INFO

PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin

# get the MAC from Elbert SCM EEPROM
mac_base=$(weutil scm | grep '^Extended MAC Base' | cut -d' ' -f4)

# get the MAC from u-boot environment
ethaddr=$(fw_printenv ethaddr | cut -d'=' -f2)

if [ -z "$mac_base" ]; then
    echo "No MAC Base programmed in EEPROM!"
    if [ -n "$ethaddr" ]; then
        echo "Using U-boot ethaddr for mac."
        mac="$ethaddr"
    fi
else
    # Increment MAC BASE as our MAC should be BASE + 1
    # Convert MAC Base to Decimal
    mac_base_hex=$(echo "$mac_base" |  tr '[:lower:]' '[:upper:]' | tr -d ':')
    mac_dec=$(printf '%d\n' 0x"$mac_base_hex")
    # Add 1 to MAC Base
    mac_dec=$((mac_dec + 1))
    # Convert base to MAC format
    mac_hex=$(printf '%X\n' "$mac_dec")
    mac=$(echo "$mac_hex" | sed 's/../&:/g;s/:$//')
fi

if [ -n "$mac" ]; then
    ifconfig eth0 hw ether "$mac"
else
    # No MAC from either EEPROM or u-boot, use whatever was assigned by kernel
    mac=$(ifconfig eth0 | grep HWaddr | awk '{ print $5 }')
fi

if [ "$ethaddr" != "$mac" ]; then
    # Set the MAC from EEPROM or ifconfig back to u-boot environment
    # so that u-boot can use it
    fw_setenv "ethaddr" "$mac"
fi


#!/bin/bash

# shellcheck disable=SC1091
# shellcheck disable=SC2012
# shellcheck disable=SC2034
. /usr/local/bin/openbmc-utils.sh

CPLD_JTAG_SEL_L="CPLD_JTAG_SEL_L"
JTAG_TRST_L="JTAG_TRST_L"
SCM_FPGA_LATCH_L="SCM_FPGA_LATCH_L"
JTAG_EN="${SCMCPLD_SYSFS_DIR}/jtag_en"
PROGRAM_SEL="${SCMCPLD_SYSFS_DIR}/program_sel"
SPI_CTRL="${SMBCPLD_SYSFS_DIR}/spi_ctrl"
SPI_PIM_SEL="${SMBCPLD_SYSFS_DIR}/spi_pim_en"
SPI_TH4_QSPI_SEL="${SMBCPLD_SYSFS_DIR}/spi_th4_qspi_en"
JTAG_CTRL="${SMBCPLD_SYSFS_DIR}/jtag_ctrl"
SMB_SPIDEV="spidev1.1"
SMB_SPI="spi1.1"
SMB_MTD=""

SCM_PROGRAM=false
SMB_PROGRAM=false
CACHED_SCM_PWR_ON_SYSFS="0x1"

# Temporary path for 32MB read
TEMP_SPI_IMAGE="/tmp/tmp_spi_image"

# Check if another fw upgrade is ongoing
check_fwupgrade_running

trap cleanup INT TERM QUIT EXIT

usage() {
    program=$(basename "$0")
    echo "Usage:"
    echo "$program <fpga> <action> <fpga file>"
    echo "      <fpga> : scm, smb, smb_cpld, fan, th4_qspi,"
    echo "               pim, pim_base, pim16q, pim8ddm"
    echo "      <action> : program, verify, erase (spi only)"
    exit 1
}

cleanup() {
    disconnect_program_paths
    rm -f $TEMP_SPI_IMAGE
}

disconnect_program_paths() {
    # Return values to defaults
    gpio_set_value $CPLD_JTAG_SEL_L 1
    gpio_set_value $JTAG_TRST_L 0
    if [ "$SCM_PROGRAM" = true ]; then
        # Give SCM cpld time to come back on SMBus
        sleep 2
        # Restore x86 power state
        echo "$CACHED_SCM_PWR_ON_SYSFS" > "$SCM_PWR_ON_SYSFS" || {
           echo "Failed to recover CPU power state."
        }
        sleep 1
        gpio_set_value $SCM_FPGA_LATCH_L 1
    else
        echo 0 > "$JTAG_EN"
        echo 0 > "$PROGRAM_SEL"
    fi

    if [ "$SMB_PROGRAM" = false ]; then
        echo 0 > "$SPI_CTRL"
        echo 0 > "$JTAG_CTRL"
    fi
    devmem_clear_bit 0x1e6e2438 8 # SPI1CS1 Function enable
}

connect_scm_jtag() {
    # Store initial state of x86 power in order to restore after programming
    CACHED_SCM_PWR_ON_SYSFS="$(head -n 1 "$SCM_PWR_ON_SYSFS" 2> /dev/null)"
    gpio_set_value $SCM_FPGA_LATCH_L 0

    gpio_set_value $CPLD_JTAG_SEL_L 0
    gpio_set_value $JTAG_TRST_L 1
    echo 0 > "$JTAG_EN"
}

connect_smb_jtag() {
    gpio_set_value $CPLD_JTAG_SEL_L 1
    gpio_set_value $JTAG_TRST_L 1
    echo 1 > "$JTAG_EN"
    echo 1 > "$PROGRAM_SEL"
}

connect_smb_cpld_jtag() {
    gpio_set_value $CPLD_JTAG_SEL_L 1
    gpio_set_value $JTAG_TRST_L 1
    echo 1 > "$JTAG_EN"
    echo 0 > "$PROGRAM_SEL"
    echo 0 > "$SPI_CTRL"
    echo 0x2 > "$JTAG_CTRL"
}

connect_fan_jtag() {
    gpio_set_value $CPLD_JTAG_SEL_L 1
    gpio_set_value $JTAG_TRST_L 1
    echo 1 > "$JTAG_EN"
    echo 0 > "$PROGRAM_SEL"
    echo 0 > "$SPI_CTRL"
    echo 0x1 > "$JTAG_CTRL"
}

bind_spi_nor() {
    # Unbind spidev1.1
    if [ -e /dev/spidev1.1 ]; then
        echo "$SMB_SPI" > /sys/bus/spi/drivers/spidev/unbind
    fi

    # Bind
    echo spi-nor  > /sys/bus/spi/devices/"$SMB_SPI"/driver_override
    if [ ! -e /sys/bus/spi/drivers/spi-nor/"$SMB_SPI" ]; then
        echo Binding "$SMB_SPI" to ...
        echo "$SMB_SPI" > /sys/bus/spi/drivers/spi-nor/bind
        sleep 0.5
    fi
    SMB_MTD="$(grep "$SMB_SPI" /proc/mtd |awk '{print$1}' | tr -d : | tr -d mtd)"
    if test -z "$SMB_MTD"; then
        echo "Failed to locate mtd partition for SPI Flash!"
        exit 1
    fi
}

unbind_spi_nor() {
    # Method for unloading spi-nor driver dynamically back to spidev
    echo > /sys/bus/spi/devices/"$SMB_SPI"/driver_override
    if grep "$SMB_SPI" /proc/mtd > /dev/null 2>&1 ; then
        echo "Unbinding spi-nor from $SMB_SPI"
        echo "$SMB_SPI" > /sys/bus/spi/drivers/spi-nor/unbind
    fi
    if [ ! -e /dev/spidev1.1 ]; then
        echo "Binding spidev back to $SMB_SPI"
        echo "$SMB_SPI" > /sys/bus/spi/drivers/spidev/bind
    fi
}

connect_pim_flash() {
    # Set SPI path to PIM SPI flash
    gpio_set_value $CPLD_JTAG_SEL_L 1
    echo 0 > "$PROGRAM_SEL"
    echo 1 > "$SPI_PIM_SEL"
    devmem_set_bit 0x1e6e2438 8 # SPI1CS1 Function enable
    sleep 0.1
    bind_spi_nor
}

connect_th4_qspi_flash() {
    gpio_set_value $CPLD_JTAG_SEL_L 1
    echo 0 > "$PROGRAM_SEL"
    echo 1 > "$SPI_TH4_QSPI_SEL"
    devmem_set_bit 0x1e6e2438 8 # SPI1CS1 Function enable
    sleep 0.1
}

do_scm() {
    # verify the fpga file
    if ! grep "DESIGN.*ird" "$2" > /dev/null; then
        echo "$2 is not a valid SCM FPGA file"
        exit 1
    fi
    SCM_PROGRAM=true
    connect_scm_jtag
    jam -l/usr/lib/libcpldupdate_dll_ioctl.so -v -a"${1^^}" "$2" \
      --ioctl IOCBITBANG
}

do_smb() {
    # verify the fpga file
    if ! grep "DESIGN.*onda" "$2" > /dev/null; then
        echo "$2 is not a valid SMB FPGA file"
        exit 1
    fi
    # P1 fpga - Revision <= 1, otherwise P2
    if [ "$(printf "%d" "$(grep A_REVISION "$2" | \
         awk '{print $3}' | tr -d '";')")" -le 1 ]; then
        # We are using P1 fpga_file
        if ! wedge_is_smb_p1; then
            echo "P1 FPGA FILE not compatible with P2 Hardware!"
            exit 1
        fi
    else
        if wedge_is_smb_p1; then
            echo "P2 FPGA FILE not compatible with P1 Hardware!"
            exit 1
        fi
    fi
    SMB_PROGRAM=true
    connect_smb_jtag
    jam -l/usr/lib/libcpldupdate_dll_ioctl.so -v -a"${1^^}" "$2" \
      --ioctl IOCBITBANG
}

do_smb_cpld() {
    # verify the fpga file
    if ! grep "DESIGN.*ide" "$2" > /dev/null; then
        echo "$2 is not a valid SMB CPLD file"
        exit 1
    fi
    # P1 fpga - Revision <= 4, otherwise P2
    if [ "$(printf "%d" "$(grep A_REVISION "$2" | \
         awk '{print $3}' | tr -d '";')")" -le 4 ]; then
        # We are using P1 fpga binary
        if ! wedge_is_smb_p1; then
            echo "P1 FPGA FILE not compatible with P2 Hardware!"
            exit 1
        fi
    else
        # We are using P2 fpga binary
        if wedge_is_smb_p1; then
            echo "P2 FPGA FILE not compatible with P1 Hardware!"
            exit 1
        fi
    fi
    connect_smb_cpld_jtag
    jam -l/usr/lib/libcpldupdate_dll_ioctl.so -v -a"${1^^}" "$2" \
      --ioctl IOCBITBANG
}

do_fan() {
    # verify the fpga file
    if ! grep "DESIGN.*ckels" "$2" > /dev/null; then
        echo "$2 is not a valid FAN FPGA file"
        exit 1
    fi
    connect_fan_jtag
    jam -l/usr/lib/libcpldupdate_dll_ioctl.so -v -a"${1^^}" "$2" \
      --ioctl IOCBITBANG
}

program_spi_image() {
    ORIGINAL_IMAGE="$1" # Image path
    ACTION="${4^^}"         # Program or verify

    case "${2^^}" in # Selects driver used
        FLASHROM)
           DRIVER=linux_spi:dev=/dev/"$SMB_SPIDEV"
           ;;
        SPINOR)
           DRIVER=linux_mtd:dev="$SMB_MTD"
           ;;
        *)
           echo "Unknown driver $2 specified"
           exit 1
           ;;
    esac

    case "${3^^}" in # Selects passed partition name
        PIM_BASE)
           PARTITION="pim_base"
           SKIP_MB=0
           # TODO: Restrict PIM_BASE later
           # FPGA_TYPE=0
           FPGA_TYPE=-1
           ;;
        TH4_QSPI)
           PARTITION="th4_qspi"
           SKIP_MB=0
           FPGA_TYPE=-1
           ;;
        PIM16Q)
           PARTITION="pim16q"
           SKIP_MB=1
           FPGA_TYPE=1
           ;;
        PIM8DDM)
           PARTITION="pim8ddm"
           SKIP_MB=2
           FPGA_TYPE=2
           ;;
        FULL)
           PARTITION="full" # Flash entire spi flash
           SKIP_MB=0
           FPGA_TYPE=-1
           ;;
        *)
           echo "Unknown region $3 specified"
           exit 1
           ;;
    esac

    echo "Selected partition $PARTITION to program."
    # Clear any cached pim header version files
    rm /tmp/.pim_spi_header* 2>/dev/null
    rm /tmp/.qspi_spi_image* 2>/dev/null

    if [ "$FPGA_TYPE" -ge 0 ] ; then
        # Verify the image type
        IMG_FPGA_TYPE=$(head -c3 "$ORIGINAL_IMAGE" | hexdump -v -e '/1 "%u\n"' | \
                        tail -n1)
        if [ "$IMG_FPGA_TYPE" != "$FPGA_TYPE" ]; then
            echo "FPGA type in image, $IMG_FPGA_TYPE, does not match expected" \
                 "FPGA type, $FPGA_TYPE, for partition $PARTITION"
            exit 1
        fi
    fi

    # We need to 32 MB binary, we will fill if needed with 1s.
    # We will only program/verify the specified image partition
    EEPROM_SIZE="33554432" # 32 MB
    IMAGE_SIZE="$(ls -l "$ORIGINAL_IMAGE" | awk '{print $5}')"
    TEMP_IMAGE="/tmp/pim_image_32mb.bin"

    if [ "$IMAGE_SIZE" -gt "$EEPROM_SIZE" ] ; then
        echo "$IMAGE size $IMAGE_SIZE is greater than eeprom capacity $EEPROM_SIZE"
        exit 1
    fi

    OPERATION="-w"
    if [ "$ACTION" = "VERIFY" ]
    then
        OPERATION="-v"
    fi

    fill=$((EEPROM_SIZE - IMAGE_SIZE))
    bs=1048576 # 1MB blocks

    echo "$ORIGINAL_IMAGE size $IMAGE_SIZE < $EEPROM_SIZE (32MB)"
    echo "Resizing $ORIGINAL_IMAGE by $fill bytes to $TEMP_IMAGE"

    # Let's make a copy of the program image and 0xff-fill it to 32MB
    # Prepend/append 0x1 bytes accordingly
    {
        dd if=/dev/zero bs=$bs count="$SKIP_MB" | tr "\000" "\377"
        dd if="$ORIGINAL_IMAGE"
    } 2>/dev/null > "$TEMP_IMAGE"

    div=$((fill/bs)) # Number of 1MB blocks to write
    div=$((div - SKIP_MB))
    rem=$((fill%bs)) # Remaining
    {
        dd if=/dev/zero bs=$bs count=$div | tr "\000" "\377"
        dd if=/dev/zero bs=$rem count=1 | tr "\000" "\377"
    } 2>/dev/null >> "$TEMP_IMAGE"

    TEMP_IMAGE_SIZE="$(ls -l "$TEMP_IMAGE" | awk '{print $5}')"
    if [ "$TEMP_IMAGE_SIZE" -ne "$EEPROM_SIZE" ] ; then
        echo "Failed to resize $ORIGINAL_IMAGE to 32MB... exiting."
        exit 1
    fi

    # Retry the programming if it fails up to 3 times
    n=1
    max=3
    while true; do
        # Program the 32MB pim image
        if flashrom -p "$DRIVER" -N --layout /etc/elbert_pim.layout \
           --image "$PARTITION" $OPERATION "$TEMP_IMAGE" ; then
            break
        else
            if [ $n -lt $max ]; then
                echo "Programming SPI failed, retry attempt $n/$max"
                n=$((n+1))
            else
                echo "Failed to program after $n attempts"
                rm "$TEMP_IMAGE"
                exit 1
            fi
        fi
    done

    rm "$TEMP_IMAGE"
}

read_spi_partition_image() {
    DEST="$1" # READ Image output path
    BS="1M" # Default partition is 1MB
    COUNT=1 # Default partition size is 1 * BS
    SKIP_MB=0

    case "${2^^}" in # Selects driver used
        FLASHROM)
           DRIVER=linux_spi:dev=/dev/"$SMB_SPIDEV"
           ;;
        SPINOR)
           DRIVER=linux_mtd:dev="$SMB_MTD"
           ;;
        *)
           echo "Unknown driver $2 specified"
           exit 1
           ;;
    esac

    case "${3^^}" in # Selects passed partition name
        PIM_BASE)
           PARTITION="pim_base"
           ;;
        HEADER_PIM_BASE)
           PARTITION="header_pim_base"
           ;;
        HEADER_TH4_QSPI)
           PARTITION="header_th4_qspi"
           ;;
        TH4_QSPI)
           BS=1K # TH4 QSPI is small, use 1K increments
           COUNT=32 # TH4 QSPI is <32KB
           PARTITION="th4_qspi"
           ;;
        PIM16Q)
           PARTITION="pim16q"
           SKIP_MB=1
           ;;
        HEADER_PIM16Q)
           PARTITION="header_pim16q"
           SKIP_MB=1
           ;;
        PIM8DDM)
           PARTITION="pim8ddm"
           SKIP_MB=2
           ;;
        HEADER_PIM8DDM)
           PARTITION="header_pim8ddm"
           SKIP_MB=2
           ;;
        FULL)
           PARTITION="full" # Read Entire SPI flash
           COUNT=32 # Full 32MB image
           ;;
        *)
           echo "Unknown region $3 specified"
           exit 1
           ;;
    esac

    echo "Selected partition $PARTITION to read."

    flashrom -p "$DRIVER" \
        --layout /etc/elbert_pim.layout --image "$PARTITION" -r "$TEMP_SPI_IMAGE" \
        || return 1
    sleep 1
    flashrom -p "$DRIVER" \
        --layout /etc/elbert_pim.layout --image "$PARTITION" -v "$TEMP_SPI_IMAGE" \
        || return 1
    dd if="$TEMP_SPI_IMAGE" of="$DEST" bs="$BS" count="$COUNT" skip="$SKIP_MB" 2> /dev/null
    return 0
}

strip_image_header() {
    ORIG_IMAGE="$1"
    NEW_IMAGE="$2"
    POS=$(head -c4096 "$ORIG_IMAGE" | hexdump -v -e '/1 "%u\n"' | \
          (grep -n -e '^0$' || echo 0) | head -n1 | cut -d':' -f1)
    if [ "$POS" -gt 0 ]
    then
        if ! dd if="$ORIG_IMAGE" bs=1 count="$POS" 2> /dev/null | grep _REVISION \
             > /dev/null
        then
            # No REVISION header found
            POS=0
        fi
    fi

    if [ "$POS" -gt 0 ]
    then
        dd if="$ORIG_IMAGE" of="$NEW_IMAGE" bs="$POS" skip=1 2> /dev/null
    else
        cp "$ORIG_IMAGE" "$NEW_IMAGE"
    fi
}

do_pim() {
    case "${1^^}" in
        PROGRAM|VERIFY)
            # Image with header stripped
            BIN_IMAGE="/tmp/stripped_pim_image"
            connect_pim_flash
            strip_image_header "$2" "$BIN_IMAGE"
            program_spi_image "$BIN_IMAGE" "SPINOR" "$3" "$1"
            rm "$BIN_IMAGE"
            ;;
        READ)
            connect_pim_flash
            if [ -z "$3" ]; then
                flashrom -p linux_mtd:dev="$SMB_MTD" -r "$2" || exit 1
                sleep 1
                flashrom -p linux_mtd:dev="$SMB_MTD" -v "$2" || exit 1
            else
                read_spi_partition_image "$2" "SPINOR" "$3" || exit 1
            fi
            ;;
        ERASE)
            connect_pim_flash
            if [ -z "$2" ]; then
                flashrom -p linux_mtd:dev="$SMB_MTD" -E || exit 1
            else
                flashrom -p linux_mtd:dev="$SMB_MTD" \
                    --layout /etc/elbert_pim.layout --image "$2" -E || exit 1
            fi
            ;;
        *)
            echo "PIM only supports program/verify/read/erase action. Exiting..."
            exit 1
            ;;
    esac
}

do_th4_qspi() {
    case "${1^^}" in
        PROGRAM|VERIFY)
            # Image with header stripped
            BIN_IMAGE="/tmp/stripped_qspi_image"
            unbind_spi_nor
            connect_th4_qspi_flash
            strip_image_header "$2" "$BIN_IMAGE"
            program_spi_image "$BIN_IMAGE" "FLASHROM" th4_qspi "$1"
            ;;
        READ)
            unbind_spi_nor
            connect_th4_qspi_flash
            if [ -z "$3" ]; then
                read_spi_partition_image "$2" "FLASHROM" th4_qspi || exit 1
            else
                read_spi_partition_image "$2" "FLASHROM" "$3" || exit 1
            fi
            ;;
        *)
            echo "TH4 QSPI only supports program/verify/read action. Exiting..."
            exit 1
            ;;
    esac
}

if [ $# -lt 2 ]; then
    usage
fi

case "$1" in
   smb) shift 1
      do_smb "$@"
      ;;
   smb_cpld) shift 1
      do_smb_cpld "$@"
      ;;
   scm) shift 1
      do_scm "$@"
      ;;
   pim) shift 1
      do_pim "$@" full
      ;;
   pim_base) shift 1
      do_pim "$@" pim_base
      ;;
   pim16q) shift 1
      do_pim "$@" pim16q
      ;;
   pim8ddm) shift 1
      do_pim "$@" pim8ddm
      ;;
   header_pim_base) shift 1
      do_pim "$@" header_pim_base
      ;;
   header_pim16q) shift 1
      do_pim "$@" header_pim16q
      ;;
   header_pim8ddm) shift 1
      do_pim "$@" header_pim8ddm
      ;;
   fan) shift 1
      do_fan "$@"
      ;;
   th4_qspi) shift 1
      do_th4_qspi "$@"
      ;;
   header_th4_qspi) shift 1
      do_th4_qspi "$@" header_th4_qspi
      ;;
   *)
      usage
      ;;
esac


#!/bin/bash
#
# Copyright 2020-present Facebook. All Rights Reserved.
#
# This program file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program in a file named COPYING; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301 USA
#

# shellcheck disable=SC1091
. /usr/local/bin/openbmc-utils.sh

maj_ver="cpld_ver_major"
min_ver="cpld_ver_minor"
exitCode=0

echo "------SCM-FPGA------"

if [ ! -d "$SCMCPLD_SYSFS_DIR" ]; then
    echo "SCM_FPGA: FPGA_DRIVER_NOT_DETECTED"
    exitCode=1
else
    val_major=$(head -n 1 "$SCMCPLD_SYSFS_DIR"/"$maj_ver")
    val_minor=$(head -n 1 "$SCMCPLD_SYSFS_DIR"/"$min_ver")
    echo "SCM_FPGA: $((val_major)).$((val_minor))"
fi

echo "------FAN-FPGA------"
if [ ! -d "$FANCPLD_SYSFS_DIR" ]; then
    echo "FAN_FPGA: FPGA_DRIVER_NOT_DETECTED"
    exitCode=1
else
    val_major=$(head -n 1 "$FANCPLD_SYSFS_DIR"/"$maj_ver")
    val_minor=$(head -n 1 "$FANCPLD_SYSFS_DIR"/"$min_ver")
    echo "FAN_FPGA: $((val_major)).$((val_minor))"
fi

echo "------SMB-FPGA------"
if [ ! -d "$SMBCPLD_SYSFS_DIR" ]; then
    echo "SMB_FPGA: FPGA_DRIVER_NOT_DETECTED"
    echo "Unable to retrieve PIM FPGA versions either"
    exitCode=1
else
    val_major=$(head -n 1 "$SMBCPLD_SYSFS_DIR"/"$maj_ver")
    val_minor=$(head -n 1 "$SMBCPLD_SYSFS_DIR"/"$min_ver")
    echo "SMB_FPGA: $((val_major)).$((val_minor))"

    echo "------SMB-CPLD------"
    val_major=$(head -n 1 "$SMBCPLD_SYSFS_DIR"/th4_cpld_ver_major)
    val_minor=$(head -n 1 "$SMBCPLD_SYSFS_DIR"/th4_cpld_ver_minor)
    echo "SMB_CPLD: $((val_major)).$((val_minor))"

    echo "------PIM-FPGA------"
    pim_list="2 3 4 5 6 7 8 9"
    for pim in ${pim_list}; do
      pim_present=$(head -n 1 "$SMBCPLD_SYSFS_DIR"/pim"$pim"_present)
      pim_major=$(head -n 1 "$SMBCPLD_SYSFS_DIR"/pim"$pim"_fpga_rev_major)
      pim_minor=$(head -n 1 "$SMBCPLD_SYSFS_DIR"/pim"$pim"_fpga_rev_minor)
      if [ "$((pim_present))" -eq 0 ]; then
        echo "PIM $pim: NOT_INSERTED"
      elif [ "$((pim_major))" -eq 255 ]; then
        # The FPGA version read was 0xFF, which indicates not detected
        echo "PIM $pim: VERSION_NOT_DETECTED"
        exitCode=1
      else
        echo "PIM $pim: $((pim_major)).$((pim_minor))"
      fi
    done
fi

if [ "$exitCode" -ne 0 ]; then
    echo "One or more fpga versions was not detected... exiting"
    exit 1
fi


#!/bin/bash
#
# Copyright 2020-present Facebook. All Rights Reserved.
#
# This program file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program in a file named COPYING; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301 USA
#

# This utility prints the meta information for specified boot flash
# mtd partition
START_OFFSET_KB=960
LEN_KB=64

usage() {
    echo "Usage $0 [flash0|flash1]"
}

case "$1" in
    flash0)
        mtd="$(grep flash0 /proc/mtd | awk '{print $1}' | tr -d ':')"
        ;;
    flash1)
        mtd="$(grep flash1 /proc/mtd | awk '{print $1}' | tr -d ':')"
        ;;
    *)
        usage
        exit 1
        ;;
esac

dd if=/dev/"$mtd" of=/tmp/."$1"_meta bs=1K skip="$START_OFFSET_KB" count="$LEN_KB"
strings /tmp/."$1"_meta


#!/bin/bash
#
# Copyright 2020-present Facebook. All Rights Reserved.
#
# This program file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program in a file named COPYING; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301 USA
#

# shellcheck disable=SC1091
# shellcheck disable=SC2039
# SC2059 is disabled to allow binary character printing using printf
# shellcheck disable=SC2059

. /usr/local/bin/openbmc-utils.sh

EWEN_CMD=0x0
EWEN_ADDR=0x300
EWDS_CMD=0x0
EWDS_ADDR=0x0
READ_CMD=0x2
WRITE_CMD=0x1

SPEED_1MHZ=0x1

DS4520_PROGRAMMED=0
WRITES_ENABLED=0

COMMAND_REG_PREFIX="oob_eeprom_cmd_"
RESPONSE_REG_PREFIX="oob_eeprom_resp_"
READ_TRIGGER_REG="oob_eeprom_resp_trigger"
EEPROM_SIZE=1024

DS4520_BUS=8
DS4520_DEV=$(( 0x50 ))

# DS4520 pullup enable register for IO_0 through IO_7
DS4520_PULL0_REG=0xf0
# DS4520 I/O control register for IO_0 through IO_7
DS4520_PULL1_REG=0xf1
# DS4520 pullup enable register for IO_8
DS4520_IO0_REG=0xf2
# DS4520 I/O control register for IO_8
DS4520_IO1_REG=0xf3

# Setting bit 0 in the DS4520 config register makes subsequent changes
# volatile. This ensures that default settings will be restored on power loss.
DS4520_CONFIG_REG=0xf4
DS4520_VOLATILE=0x1
DS4520_CONFIG_MASK=$(( 1 << 0 ))

# PULL0/IO0 bit definitions:
SC_SPI_BUF_EN_L=7
MSW_SPI_CTRL_EN_L=6
EEPROMMASTER_SEL_EN_L=5
EEPROMMASTER_SEL=4
CPUEEEPROM_SEL_EN_L=3
CPUEEEPROM_SEL=2
DS4520_IO0_MASK=$(( ( 1 << SC_SPI_BUF_EN_L ) \
                  | ( 1 << MSW_SPI_CTRL_EN_L ) \
                  | ( 1 << EEPROMMASTER_SEL_EN_L ) \
                  | ( 1 << EEPROMMASTER_SEL ) \
                  | ( 1 << CPUEEEPROM_SEL_EN_L ) \
                  | ( 1 << CPUEEEPROM_SEL ) ))

# PULL1/IO1 bit definitions:
MSW_SPI_CTRL_SEL=0
DS4520_IO1_MASK=$(( 1 << MSW_SPI_CTRL_SEL ))

CPLD_EEPROM_MODE_0=$(( ( 1 << SC_SPI_BUF_EN_L ) \
                     | ( 1 << EEPROMMASTER_SEL ) \
                     | ( 1 << CPUEEEPROM_SEL_EN_L ) ))
CPLD_EEPROM_MODE_1=$(( 1 << MSW_SPI_CTRL_SEL ))
EEPROM_MODE_0=$(( 1 << MSW_SPI_CTRL_EN_L ))
EEPROM_MODE_1=0

trap cleanup EXIT
trap handle_signal INT TERM QUIT

usage() {
    echo "Dumps or programs the OOB Eeprom"
    echo
    echo "dump [--full]"
    echo "save <filename>"
    echo "program <filename>"
    echo "read <index> [<num-words>]"
    echo "write <index> <word>"
    echo "version"
}

read_index() {
    local cmd_reg
    cmd_reg=$(( ( READ_CMD << 30 ) | ( SPEED_1MHZ << 28 ) | ( $1 << 16 ) ))
    desc="reading index $idx"
    send_cmd "$cmd_reg" "$desc" "trig_read"
}

write_index() {
    local idx=$1
    local val=$2
    local cmd_reg
    cmd_reg=$(( ( WRITE_CMD << 30 ) | ( SPEED_1MHZ << 28 ) \
              | ( idx << 16 ) | val ))
    desc="writing index $idx value $val"
    local resp_val
    local try_num=0
    while [ $try_num -lt 5 ]
    do
        resp_val=$( send_cmd "$cmd_reg" "$desc" "trig_read" ) || exit 1
        if [ $(( resp_val )) -eq $(( val )) ]
        then
            # verify the value read matches the value programmed
            read_val=$( read_index "$idx" )
            if [ $(( read_val )) = $(( val )) ]
            then
                return
            fi

            printf "Error: try %d: read value after write(" $try_num >&2
            printf "0x%x) does not match write val(0x%x)\n" "$read_val" "$val" >&2
        else
            printf "Error: try %d: response value from write(" $try_num >&2
            printf "0x%x) does not match write val(0x%x)\n" "$resp_val" "$val" >&2
        fi

        try_num=$(( try_num + 1 ))
    done

    echo "failed to write register" >&2
    exit 1
}

eeprom_disable_write() {
    echo "Write interrupted. Disabling writes." >&2
    eeprom_write_protect "disable"
}

set_cpld_eeprom_mode() {
    # Program the DS4520 to allow access to the EEPROM.
    DS4520_PROGRAMMED=1
    i2cset -y -m $DS4520_CONFIG_MASK $DS4520_BUS $DS4520_DEV $DS4520_CONFIG_REG \
                 $DS4520_VOLATILE
    # A small delay is needed after toggling persistence setting.
    sleep 1
    i2cset -y -m $DS4520_IO0_MASK $DS4520_BUS $DS4520_DEV $DS4520_PULL0_REG \
                 $CPLD_EEPROM_MODE_0
    i2cset -y -m $DS4520_IO1_MASK $DS4520_BUS $DS4520_DEV $DS4520_PULL1_REG \
                 $CPLD_EEPROM_MODE_1
    i2cset -y -m $DS4520_IO0_MASK $DS4520_BUS $DS4520_DEV $DS4520_IO0_REG \
                 $CPLD_EEPROM_MODE_0
    i2cset -y -m $DS4520_IO1_MASK $DS4520_BUS $DS4520_DEV $DS4520_IO1_REG \
                 $CPLD_EEPROM_MODE_1
}

restore_eeprom_mode() {
    i2cset -y -m $DS4520_CONFIG_MASK $DS4520_BUS $DS4520_DEV $DS4520_CONFIG_REG \
                 $DS4520_VOLATILE
    i2cset -y -m $DS4520_IO0_MASK $DS4520_BUS $DS4520_DEV $DS4520_PULL0_REG \
                 $EEPROM_MODE_0
    i2cset -y -m $DS4520_IO1_MASK $DS4520_BUS $DS4520_DEV $DS4520_PULL1_REG \
                 $EEPROM_MODE_1
    i2cset -y -m $DS4520_IO0_MASK $DS4520_BUS $DS4520_DEV $DS4520_IO0_REG \
                 $EEPROM_MODE_0
    i2cset -y -m $DS4520_IO1_MASK $DS4520_BUS $DS4520_DEV $DS4520_IO1_REG \
                 $EEPROM_MODE_1
    DS4520_PROGRAMMED=0
}

cleanup() {
    if [ $WRITES_ENABLED -eq 1 ]
    then
        eeprom_disable_write
        WRITES_ENABLED=0
    fi

    if [ $DS4520_PROGRAMMED -eq 1 ]
    then
        restore_eeprom_mode
    fi
}

handle_signal() {
    echo "Exiting because of signal" >&2
    cleanup
    exit 1
}

eeprom_write_protect() {
    local enable=$1

    local cmd cmd_addr
    if [ "$enable" = "enable" ]
    then
        cmd=$EWEN_CMD
        cmd_addr=$EWEN_ADDR
        WRITES_ENABLED=1
    else
        cmd=$EWDS_CMD
        cmd_addr=$EWDS_ADDR
    fi

    local cmd_reg
    cmd_reg=$(( ( cmd << 30 ) | ( SPEED_1MHZ << 28 ) \
           | ( cmd_addr << 16 ) ))
    desc="setting write protect to $enable"
    send_cmd "$cmd_reg" "$desc" "trig_read" > /dev/null

    if [ "$enable" = "disable" ]
    then
        WRITES_ENABLED=0
    fi
}

eeprom_init() {
    set_cpld_eeprom_mode

    # Clear write cycle error, if present
    i2c_set "oob_eeprom_resp_3" 0x8
    i2c_set "oob_eeprom_resp_trigger" 0x0
}

send_cmd() {
    local cmd_reg=$1
    local desc="$2"
    local trig_opt="$3"

    local cpld_reg reg_idx
    local try_num=0
    while [ $try_num -lt 5 ]
    do
        reg_idx=0
        while [ $reg_idx -lt 4 ]
        do
            cpld_reg=${COMMAND_REG_PREFIX}$(( 3 - reg_idx ))
            cpld_reg_val=$(( ( cmd_reg >> ( ( 3 - reg_idx ) * 8 ) ) & 0xff ))
            i2c_set $cpld_reg "$cpld_reg_val"
            reg_idx=$(( reg_idx + 1 ))
        done

        # The FPGA requires 10ms prior to triggering the read request.
        # Sleep for 20ms.
        sleep 0.02

        if [ "$trig_opt" = "trig_read" ]
        then
            i2c_set $READ_TRIGGER_REG 0x0
        fi

        get_response "$desc" && return
        if [ $? -gt 1 ]
        then
           exit 1
        fi

        printf "Send command failed on try %d\n" $try_num >&2
        try_num=$(( try_num + 1 ))
    done

    echo "Send command failed" >&2
    exit 1
}

get_response() {
    local desc=$1
    local timeout=5
    local interval=0.1
    local time_limit=0
    local reg_idx resp_val
    while true
    do
        resp_val=0
        reg_idx=0
        while [ $reg_idx -lt 4 ]
        do
            cpld_reg=${RESPONSE_REG_PREFIX}$(( 3 - reg_idx ))
            cpld_reg_val=$( i2c_get $cpld_reg )
            # Each CPLD register contains one byte of the response with the
            # MSB first and the LSB last.
            bit_offset=$(( ( 3 - reg_idx ) * 8 ))
            shifted_val=$(( cpld_reg_val << bit_offset ))
            resp_val=$(( resp_val | shifted_val ))
            reg_idx=$(( reg_idx + 1 ))
        done

        if [ $(( ( resp_val >> 27 ) & 0x1 )) -ne 0 ]
        then
            echo "Error: Write cycle error" >&2
            return 2
        fi

        if [ $(( ( resp_val >> 28 ) & 0x1 )) -ne 0 ]
        then
            echo $(( resp_val & 0xffff ))
            return 0
        fi

        if [ $time_limit -eq 0 ]
        then
            time_limit=$(( $( date +%s ) + timeout ))
        fi
        if [ "$( date +%s )" -gt $time_limit ]
        then
            break
        fi

        sleep $interval
    done

    echo "Timed out $desc" >&2
    return 1
}

i2c_set() {
    echo "$2" > "$SCMCPLD_SYSFS_DIR/$1"
}

i2c_get() {
    head -n 1 "$SCMCPLD_SYSFS_DIR/$1"
}

eeprom_read() {
    if [ $# -lt 1 ]
    then
        echo "Error: missing read index" >&2
        usage
        exit 1
    fi

    local start=$(( $1 ))
    local num_words=1
    if [ $# -gt 1 ]
    then
        num_words=$2
    fi
    local end=$(( start + num_words ))
    if [ $end -gt $EEPROM_SIZE ]
    then
        echo "Error: end index exceeds size" >&2
        exit 1
    fi

    eeprom_write_protect "disable"

    local idx=$start
    local val
    while [ $idx -lt "$end" ]
    do
        val=$( read_index $idx )
        printf "0x%x: 0x%x\n" $idx "$val"
        idx=$(( idx + 1 ))
    done
}

add_to_csum() {
    local csum=$1
    local idx=$2
    local val=$3
    csum=$(( csum + ( idx << 16 ) + val ))
    # To prevent overflowing 32 bits, modulus with bit 31.
    csum=$(( csum % 0x80000000 ))
    echo $csum
}

eeprom_dump() {
    local csum_lsb csum_msb header version

    local full="no"
    if [ $# -gt 0 ] && [ "$1" = "--full" ]
    then
        full="yes"
    fi

    eeprom_write_protect "disable"
    local calc_csum=0

    header=$( read_index 0 )
    printf "0x%x: 0x%x\n" 0 "$header"
    # As described in Broadcom's BCM53134P data sheet, the magic code
    # is in bits 15:11 of the first word in the EEPROM and the expected
    # value is 0x15.
    local magic_code=$(( header >> 11 ))
    if [ $magic_code -ne $(( 0x15 )) ]
    then
        printf "Error: magic code 0x%x does not match expected " $magic_code >&2
        printf "0x15\n" >&2
        if [ $full = "no" ]
        then
            exit 1
        fi
    fi
    calc_csum=$( add_to_csum $calc_csum 0 "$header" )

    local num_entries=$(( header & 0x3ff ))
    local stream_end=$(( 1 + num_entries ))
    echo "Number of data words: $num_entries"
    local start=1
    # 3 words are reserved at the end of the image.
    # 2 are for the checksum. 1 is for the version.
    local eeprom_end=$(( EEPROM_SIZE - 3 ))
    local end=$eeprom_end
    if [ $full = "no" ]
    then
        end=$stream_end
    fi

    local idx=$start
    while [ $idx -lt "$end" ]
    do
        val=$( read_index $idx )
        printf "0x%x: 0x%x\n" $idx "$val"
        if [ $idx -lt $stream_end ]
        then
            calc_csum=$( add_to_csum "$calc_csum" $idx "$val" )
        fi
        idx=$(( idx + 1 ))
    done

    idx=$eeprom_end
    csum_lsb=$( read_index idx )
    printf "0x%x: 0x%x\n" $idx "$csum_lsb"
    idx=$(( idx + 1 ))
    csum_msb=$( read_index idx )
    printf "0x%x: 0x%x\n" $idx "$csum_msb"
    idx=$(( idx + 1 ))
    version=$( read_index idx )
    printf "0x%x: 0x%x\n" $idx "$version"
    printf "Version: %x.%x\n" $(( version >> 8 )) $(( version & 0xff ))

    csum=$(( ( csum_msb << 16 ) | csum_lsb ))
    printf "Checksum: 0x%x\n" $csum
    printf "Calculated checksum: 0x%x\n" "$calc_csum"

    if [ "$csum" -ne "$calc_csum" ]
    then
        echo "Error: Checksum is not expected value" >&2
        exit 1
    fi
}

eeprom_version() {
    local idx version

    eeprom_write_protect "disable"

    # Last word at the end of the image contains version.
    idx=$(( EEPROM_SIZE - 1 ))
    version=$( read_index idx )
    printf "Version: %x.%x\n" $(( version >> 8 )) $(( version & 0xff ))
}

eeprom_save() {
    if [ $# -lt 1 ]
    then
        echo "Error: missing filename" >&2
        usage
        exit 1
    fi

    local filename=$1
    if [ -f "$filename" ]
    then
        echo "Error: file $filename already exists. Remove this file." >&2
        exit 1
    fi

    touch "$filename"
    local idx=0
    local val
    while [ $idx -lt "$EEPROM_SIZE" ]
    do
        val=$( read_index $idx )
        printf "\\$( printf "%o" $(( val & 0xff )) )" >> "$filename"
        printf "\\$( printf "%o" $(( val >> 8 )) )" >> "$filename"
        if [ $(( ( idx + 1 ) % 0x80 )) -eq 0 ]
        then
            printf "Saved word 0x%x\n" "$idx"
        fi
        idx=$(( idx + 1 ))
    done
    sync
}

eeprom_write() {
    if [ $# -lt 2 ]
    then
        echo "Error: missing arguments" >&2
        usage
        exit 1
    fi

    eeprom_write_protect "enable"

    local idx=$1
    local val=$2

    write_index "$idx" "$val"
    printf "Wrote 0x%x to 0x%x\n" "$val" "$idx"

    eeprom_write_protect "disable"
}

program_file() {
    local i=0
    local idx=0
    local val=0

    while read -r c
    do
        if [ $i -eq 0 ]
        then
            val=$c
            i=1
        else
            val=$(( val | ( c << 8 ) ))
            write_index "$idx" "$val"
            printf "Wrote 0x%x to 0x%x\n" "$val" "$idx"

            idx=$(( idx + 1 ))
            i=0
        fi
    done
}

eeprom_program() {
    if [ $# -lt 1 ]
    then
        echo "Error: missing filename" >&2
        usage
        exit 1
    fi

    local filename=$1

    if [ ! -f "$filename" ]
    then
        echo "Error: could not access file $filename" >&2
        exit 1
    fi

    size=$( stat -c %s "$filename" )
    words=$(( size / 2 ))
    if [ "$words" -ne $EEPROM_SIZE ]
    then
        printf "Error: words in file(%d) does not match eeprom " "$words" >&2
        printf "size(%d)\n" $EEPROM_SIZE >&2
        exit 1
    fi

    eeprom_write_protect "enable"
    hexdump -v -e '/1 "%u\n"' "$filename" | program_file
    eeprom_write_protect "disable"
}

if [ $# -lt 1 ]
then
    echo "Error: missing command" >&2
    usage
    exit 1
fi

# Only allow one instance of script to run at a time.
script=$(realpath "$0")
exec 100< "$script"
flock -n 100 || { echo "ERROR: $0 already running" && exit 1; }

if [ ! -d "$SCMCPLD_SYSFS_DIR" ]
then
    echo "Error: SCM CPLD not detected" >&2
    exit 1
fi

command="$1"
shift

eeprom_init

case "$command" in
    dump)
        eeprom_dump "$@"
        ;;
    save)
        eeprom_save "$@"
        ;;
    program)
        eeprom_program "$@"
        ;;
    read)
        eeprom_read "$@"
        ;;
    write)
        eeprom_write "$@"
        ;;
    version)
        eeprom_version "$@"
        ;;
    *)
        echo "Error: invalid command: $command" >&2
        usage
        exit 1
        ;;
esac


#!/bin/bash
#
# Copyright 2020-present Facebook. All Rights Reserved.
#
# This program file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program in a file named COPYING; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301 USA

# shellcheck disable=SC2039
usage() {
    echo "Reads/writes Bcm53134 registers"
    echo
    echo "read[(8|16|32|64)] <page> <addr>"
    echo "write[(8|16|32|64)] <page> <addr> <val>"
    echo
    echo "read/write are equivalent to read8/write8"
}

mdio0_base_reg=0x1e650000
mdio_ctrl_reg=0x0
mdio_read_reg=0x4
pseudo_phy_port_addr=0x1e

scu_reset_set2=0x1e6e2050
scu_reset_clear2=$((scu_reset_set2 + 0x4))
scu_reset_mii_controller=$((1 << 3))
scu_pin_control8=0x1e6e2430
scu_mdcmdio_enable=$(( (1 << 16) | (1 << 17) ))

trap cleanup EXIT INT TERM QUIT

MDIO_INITIALIZED=0
cleanup() {
    if [ $MDIO_INITIALIZED -eq 1 ]
    then
        mdio_restore
    fi
}

mdio_init() {
    MDIO_INITIALIZED=1
    devmem "$scu_reset_clear2" 32 "$scu_reset_mii_controller"

    val=$(devmem "$scu_pin_control8")
    devmem "$scu_pin_control8" 32 $((val | scu_mdcmdio_enable))
}

mdio_restore() {
    devmem "$scu_reset_set2" 32 "$scu_reset_mii_controller"
    val=$(devmem "$scu_pin_control8")
    devmem "$scu_pin_control8" 32 $((val & ~scu_mdcmdio_enable))
    MDIO_INITIALIZED=0
}

to_hex() {
    local val=$1
    printf "0x%x" "$val"
}

devmem_read() {
    local reg_addr=$1
    local mem_addr

    mem_addr=$(to_hex $((mdio0_base_reg + reg_addr)))
    devmem "$mem_addr"
}

devmem_write() {
    local reg_addr=$1
    local val mem_addr

    val=$(to_hex "$2")
    mem_addr=$(to_hex $((mdio0_base_reg + reg_addr)))
    devmem "$mem_addr" 32 "$val"
}

mdio_read() {
    local addr=$1
    local fire_busy clause_22 read_request reg_val read_reg_val ctrl_idle

    fire_busy=$(( 1 << 31 ))
    clause_22=$(( 1 << 28 ))
    read_request=$(( 1 << 27 ))
    reg_val=$(( fire_busy \
              | clause_22 \
              | read_request \
              | ( pseudo_phy_port_addr << 21 ) \
              | ( addr << 16 ) ))

    devmem_write $mdio_ctrl_reg $reg_val
    ctrl_idle=$(( 1 << 16 ))
    desc=$(printf "read 0x%x" "$addr")
    mdio_wait_op_complete "$mdio_read_reg" "$ctrl_idle" "$ctrl_idle" "$desc"

    read_reg_val=$(devmem_read $mdio_read_reg)
    echo $(( read_reg_val & 0xffff ))
}

mdio_write() {
    local addr=$1
    local val=$2
    local fire_busy clause_22 write_request reg_val

    fire_busy=$(( 1 << 31 ))
    clause_22=$(( 1 << 28 ))
    write_request=$(( 1 << 26 ))
    reg_val=$(( fire_busy \
              | clause_22 \
              | write_request \
              | ( pseudo_phy_port_addr << 21 ) \
              | ( addr << 16 ) \
              | val ))

    devmem_write $mdio_ctrl_reg "$reg_val"

    desc=$(printf "write 0x%x 0x%x" "$addr" "$val")
    mdio_wait_op_complete "$mdio_ctrl_reg" "$fire_busy" 0 "$desc"
}

mdio_wait_op_complete() {
    local mdio_reg=$1
    local mask=$2
    local wait_val=$3
    local desc=$4
    local timeout=5
    local interval=0.1
    local val

    local time_limit=$(( $(date +%s) + timeout ))
    while [ "$(date +%s)" -lt $time_limit ]
    do
        val=$(devmem_read "$mdio_reg")
        if [ $(( val & "$mask" )) -eq "$wait_val" ]
        then
            return
        fi
        sleep $interval
    done

    echo "Error: timed out waiting for operation to complete: $desc" >&2
    exit 1
}

oob_page_reg=16
oob_addr_reg=17
oob_data_reg_base=24

reg_read() {
    local page=$1
    local addr=$2
    local data_size=$3
    local data data_reg_num reg_val

    data=$(reg_page_data "$page" 1)
    mdio_write $oob_page_reg "$data"

    data=$(reg_addr_data "$addr" 0x2)
    mdio_write $oob_addr_reg "$data"

    reg_wait_addr_reg_acked

    # Read data registers
    local val=0
    num_data_regs=$(( ( data_size + 1 ) / 2 ))
    local reg_idx=0
    while [ $reg_idx -lt $num_data_regs ]
    do
        data_reg_num=$(( oob_data_reg_base + reg_idx ))
        reg_val=$(mdio_read $data_reg_num)
        val=$(( val | ( reg_val << ( reg_idx * 16 ) ) ))

        reg_idx=$(( reg_idx + 1 ))
    done

    binary_str=$(val_to_binary_str "$val" "$data_size")
    printf "0x%x/0x%x 0x%x == $binary_str\n" "$page" "$addr" $val

    reg_clear_access_enable
}

reg_write() {
    local page=$1
    local addr=$2
    local val=$3
    local data_size=$4
    local data reg_val data_reg_num

    data=$(reg_page_data "$page" 1)
    mdio_write $oob_page_reg "$data"

    # Write data registers
    num_data_regs=$(( ( data_size + 1 ) / 2 ))
    local reg_idx=0
    while [ $reg_idx -lt $num_data_regs ]
    do
        data_reg_num=$(( oob_data_reg_base + reg_idx ))
        reg_val=$(( ( val >> ( reg_idx * 16 ) ) & 0xffff ))
        mdio_write $data_reg_num $reg_val

        reg_idx=$(( reg_idx + 1 ))
    done

    data=$(reg_addr_data "$addr" 0x1)
    mdio_write $oob_addr_reg "$data"

    reg_wait_addr_reg_acked
    reg_clear_access_enable
}

reg_wait_addr_reg_acked() {
    local timeout=60
    local interval=0.1
    local time_limit response op

    time_limit=$(( $(date +%s) + timeout ))
    while [ "$(date +%s)" -lt $time_limit ]
    do
        response=$(mdio_read $oob_addr_reg)
        op=$(( response & 0x3 ))
        if [ $op -eq 0 ]
        then
            return
        fi

        sleep $interval
    done

    echo "Error: timed out waiting for addr reg to be acknowledged" >&2
    exit 1
}

reg_clear_access_enable() {
    data=$(reg_page_data 0 0)
    mdio_write $oob_page_reg "$data"
}

reg_page_data() {
    local page=$1
    local access_enable=$2
    local data=$(( page << 8 ))
    data=$(( data | access_enable ))
    echo $data
}

reg_addr_data() {
    local addr=$1
    local op=$2
    local data=$(( addr << 8 ))
    data=$(( data | op ))
    echo $data
}

val_to_binary_str() {
    local val=$1
    local data_size=$2
    local i=$(( data_size * 8 ))

    while [ $i -gt 0 ]
    do
        i=$(( i - 1 ))
        printf "%d" $(( ( val >> i ) & 0x1 ))
        if [ $i -gt 0 ] && [ $(( i % 4 )) -eq 0 ]
        then
            printf " "
        fi
    done
}

handle_read() {
    local size=$1
    shift
    if [ $# -lt 2 ]
    then
        echo "Error: missing arguments" >&2
        usage
        exit 1
    fi
    local page=$1
    local addr=$2
    local data_size=$(( size / 8 ))
    reg_read "$page" "$addr" "$data_size"
}

handle_write() {
    local size=$1
    shift
    if [ $# -lt 3 ]
    then
        echo "Error: missing arguments" >&2
        usage
        exit 1
    fi
    local page=$1
    local addr=$2
    local val=$3
    local data_size=$(( size / 8 ))
    reg_write "$page" "$addr" "$val" "$data_size"
}

if [ $# -lt 1 ]
then
    echo "Error: missing command" >&2
    usage
    exit 1
fi

command="$1"
shift
case "$command" in
    read8) cmd_type="read"; size="8";;
    read16) cmd_type="read"; size="16";;
    read32) cmd_type="read"; size="32";;
    read64) cmd_type="read"; size="64";;
    write8) cmd_type="write"; size="8";;
    write16) cmd_type="write"; size="16";;
    write32) cmd_type="write"; size="32";;
    write64) cmd_type="write"; size="64";;
    *)
        usage
        exit 1
        ;;
esac

mdio_init

if [ "$cmd_type" = "read" ]
then
    handle_read "$size" "$@"
else
    handle_write "$size" "$@"
fi


#!/bin/bash
#
# Copyright 2022-present Facebook. All Rights Reserved.
#
# This program file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program in a file named COPYING; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301 USA
#

# shellcheck disable=SC1091
# shellcheck disable=SC2235
. /usr/local/bin/openbmc-utils.sh

trap handle_signal INT TERM QUIT

# mdio prints in the format "PAGE_HEX/OFFSET_HEX VALUE_HEX == VALUE_BIN\n"
MDIO_READ_PATTERN="^.* (0x[a-fA-F0-9]+).*$"

# Global variable to stack tabs for formatting
tab=""

usage() {
    echo "Read BCM53134P (oob switch) port status registers"
    echo ""
    echo "# To dump all port status"
    echo "oob-status.sh"
    echo "# To dump port status for ports in 0, 1, 2, IMP"
    echo "oob-status.sh [Port(s)]"
    echo "# To print only link status"
    echo "oob-status.sh link_status [Port(s)]"
    echo "# To print only link speed"
    echo "oob-status.sh link_speed [Port(s)]"
    echo ""
    echo "Example:"
    echo ""
    echo "oob-status.sh link_status 0 IMP"
    echo "Link Status:"
    echo "	Port 0: Link Up"
    echo "	Port IMP: Link Up"

}

handle_signal() {
    echo "Exiting because of signal" >&2
    exit 1
}

check_sts_args() {
    # Convert arguments to port numbers
    local port val
    ret="$*"
    if [ $# -lt 1 ]; then
       # No arguments: do all ports
       ret="0 1 2 IMP"
    else
        for port in "$@"; do
            if [ "${port^^}" != "IMP" ] && (! [[ "$port" =~ ^[0-9]+$ ]] \
                || [ "$port" -lt 0 ] || [ "$port" -gt 2 ]); then
                echo "Invalid port $port"
                exit 1
            fi
        done
    fi
}

port_to_val() {
    local val
    if [ "$1" == "IMP" ]; then
        val=8
    else
        val="$1"
    fi
    echo "$val"
}

do_read_lnksts() {
    local page offset result ports port val
    page=0x1
    offset=0x0
    result=$(oob-mdio-util.sh read16 "$page" "$offset" \
        | sed -E "s/$MDIO_READ_PATTERN/\1/")
    ports="$*"

    printf "%sLink status:\n" "$tab"
    tab="	$tab"

    for port in $ports; do
        port=${port^^}
        val=$(port_to_val "$port")
        val=$(( (result >> val) & 0x1 ))
        case "$val" in
            0)
                printf "%sPort: %s Link Down\n" "$tab" "$port"
                ;;
            1)
                printf "%sPort: %s Link Up\n" "$tab" "$port"
                ;;
        esac
    done
    tab=${tab:1}
}

do_read_spdsts() {
    local page offset result ports port val
    page=0x1
    offset=0x4
    result=$(oob-mdio-util.sh read32 "$page" "$offset" \
        | sed -E "s/$MDIO_READ_PATTERN/\1/")
    ports="$*"

    printf "%sLink speed:\n" "$tab"
    tab="	$tab"

    for port in $ports; do
        port=${port^^}
        val=$(port_to_val "$port")
        val=$(( (result >> (val * 2)) & 0x3 ))
        case "$val" in
            0)
                printf "%sPort: %s 10 Mb/s\n" "$tab" "$port"
                ;;
            1)
                printf "%sPort: %s 100 Mb/s\n" "$tab" "$port"
                ;;
            2)
                printf "%sPort: %s 1000 Mb/s\n" "$tab" "$port"
                ;;
        esac
    done
    tab=${tab:1}
}

read_lnksts() {
    check_sts_args "$@"
    do_read_lnksts "$ret"
}

read_spdsts() {
    check_sts_args "$@"
    do_read_spdsts "$ret"
}

read_status() {
    check_sts_args "$@"
    printf "%sStatus:\n" "$tab"
    tab="	$tab"
    do_read_lnksts "$ret"
    do_read_spdsts "$ret"
    tab=${tab:1}
}

# Only allow one instance of script to run at a time.
script=$(realpath "$0")
exec 100< "$script"
flock -n 100 || { echo "ERROR: $0 already running" && exit 1; }

command="$1"

case "$command" in
    link_status)
        shift
        read_lnksts "$@"
        ;;
    link_speed)
        shift
        read_spdsts "$@"
        ;;
    "--help")
        usage
        exit 1
        ;;
    *)
        read_status "$@"
        ;;
esac


#!/bin/bash
#
# Copyright 2021-present Facebook. All Rights Reserved.
#
# This program file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program in a file named COPYING; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301 USA

# shellcheck disable=SC1091
# shellcheck disable=SC2034
source /usr/local/bin/dpm_utils.sh

prog="$0"
usage() {
    echo "Usage: $prog [--clear] [--verbose ] (2-9)"
    echo
    echo "options:"
    echo " --clear: clear faults after dumping them"
    echo " --verbose: print debug messages to stderr"
    echo "(2-9): PIM number"
}

clear_faults="no"
while [ $# -gt 0 ]
do
    case "$1" in
        --clear)
            clear_faults="yes"
            ;;
        --verbose)
            dpm_verbose="yes"
            ;;
        *)
            break
            ;;
    esac
    shift
done

if [ $# -lt 1 ]
then
    echo "Error: missing PIM number" >&2
    usage
    exit 1
fi

pim_num="$1"
if [ ! "${pim_num##*[!0-9]*}" ]
then
    echo "Error: invalid PIM number" >&2
    usage
    exit 1
fi
if [ "$pim_num" -lt 2 ] || [ "$pim_num" -gt 9 ]
then
    echo "Error: invalid PIM number" >&2
    usage
    exit 1
fi
pim_idx="$((pim_num - 2))"

pim_buses=(16 17 18 19 20 21 22 23)
# P1 has a different PIM bus mapping, but the
# wedge_is_smb_p1 function is not available at
# the time this script runs.
pim_bus="${pim_buses[$pim_idx]}"
fault_source="PIM$pim_num"

clear_faults() {
    bus="$pim_bus"

    update_clock "$bus"

    misc_config="$(i2cget -y "$bus" 0x4e 0xfc s)"
    config_byte="$(echo "$misc_config" | awk '{print $1}')"
    b2="$(echo "$misc_config" | awk '{print $2}')"
    b3="$(echo "$misc_config" | awk '{print $3}')"
    b4="$(echo "$misc_config" | awk '{print $4}')"

    # In order for faults to be cleared in non-volatile store, the
    # brownout bit in the misc_config byte needs to be cleared.
    no_brown=$((config_byte & ~0x1))

    print_debug "$fault_source: clearing the brownout bit..."
    i2cset -y "$bus" 0x4e 0xfc "$no_brown" "$b2" "$b3" "$b4" s

    print_debug "Clearing $fault_source DPM logs..."
    i2cset -y "$bus" 0x4e 0xea 0 0 0 0 0 0 0 0 0 0 0 0 s

    print_debug "$fault_source: restoring the brownout bit..."
    i2cset -y "$bus" 0x4e 0xfc "$config_byte" "$b2" "$b3" "$b4" s
}

read_logs "$pim_bus" "$fault_source"

if [ "$clear_faults" = "yes" ]
then
    clear_faults
fi


#!/bin/bash
#
# Copyright 2020-present Facebook. All Rights Reserved.
#
# This program file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program in a file named COPYING; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301 USA
#

# shellcheck disable=SC1091
. /usr/local/bin/openbmc-utils.sh

# This utility displays the inserted PIM types

pim_list="2 3 4 5 6 7 8 9"
for pim in ${pim_list}; do
    if ! wedge_is_pim_present "$pim"; then
        echo "PIM $pim: NOT INSERTED"
    else
        pim_type='NOT DETECTED'
        fru="$(/usr/local/bin/peutil "$pim" 2>&1)"
        if echo "$fru" | grep -q '88-16CD2'; then
            pim_type='PIM16Q2'
        elif echo "$fru" | grep -q '88-16CD'; then
            pim_type='PIM16Q'
        elif echo "$fru" | grep -q '88-8D'; then
            pim_type='PIM8DDM'
        fi
        echo "PIM $pim: $pim_type"
    fi
done


#!/bin/sh
#
# Copyright 2020-present Facebook. All Rights Reserved.
#
# This program file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program in a file named COPYING; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301 USA

### BEGIN INIT INFO
# Provides:          power-on
# Required-Start:
# Required-Stop:
# Default-Start:     S
# Default-Stop:
# Short-Description: Power on micro-server
### END INIT INFO

# shellcheck disable=SC1091
. /usr/local/bin/openbmc-utils.sh

PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin

if ! wedge_is_bmc_personality; then
    echo "uServer is not in BMC personality. Skipping all power-on sequences."
    return
fi

# Only run board power up sequence is CPU is not up, otherwise this could
# disrupt data plane
if wedge_is_us_on; then
    echo "uServer is already up. Skipping board power-on sequence"
else
    echo "uServer is not up. Running board power-on sequence"
    wedge_power_on_board
fi

# Now, double check if power is on, and do forced power up as needed.
echo "Checking microserver power status ... "
if wedge_is_us_on; then
    echo "on"
    on=1
else
    echo "off"
    on=0
fi

if [ $on -eq 0 ]; then
    # Power on now
    echo "uServer still not up. Running forced power-up sequence"
    wedge_power.sh on -f
fi


#!/bin/bash
#
# Copyright 2020-present Facebook. All Rights Reserved.
#
# This program file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program in a file named COPYING; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301 USA
#

### BEGIN INIT INFO
# Provides:          gpio-setup
# Required-Start:
# Required-Stop:
# Default-Start:     S
# Default-Stop:
# Short-Description:  Set up GPIO pins as appropriate
### END INIT INFO

# shellcheck disable=SC1091
. /usr/local/bin/openbmc-utils.sh

setup_gpio_elbert_evt() {
    gpio_export_by_name  "${ASPEED_GPIO}" GPIOB1 BMC_ALIVE
    gpio_export_by_name  "${ASPEED_GPIO}" GPIOB3 CPLD_JTAG_SEL_L
    gpio_export_by_name  "${ASPEED_GPIO}" GPIOB4 CPU_TEMP_ALERT
    gpio_export_by_name  "${ASPEED_GPIO}" GPIOB6 SCM_PWRGD
    gpio_export_by_name  "${ASPEED_GPIO}" GPIOB7 SMB_PWRGD
    gpio_export_by_name  "${ASPEED_GPIO}" GPIOF1 I2C_TPM_PIRQ_N
    gpio_export_by_name  "${ASPEED_GPIO}" GPIOH0 BMC_BOARD_EEPROM_WP_L
    gpio_export_by_name  "${ASPEED_GPIO}" GPIOH1 BMC_TEMP_ALERT
    gpio_export_by_name  "${ASPEED_GPIO}" GPIOI0 JTAG_TRST_L
    gpio_export_by_name  "${ASPEED_GPIO}" GPIOS7 SMB_INTERRUPT
    gpio_export_by_name  "${ASPEED_GPIO}" GPIOV0 BMC_SPI1_CS0_MUX_SEL
    gpio_export_by_name  "${ASPEED_GPIO}" GPIOV2 BMC_CHASSIS_EEPROM_WP_L
    gpio_export_by_name  "${ASPEED_GPIO}" GPIOX1 SPI2CS1#_GPIO86
    gpio_export_by_name  "${ASPEED_GPIO}" GPIOX2 SPI2CS2#_GPIO84
    gpio_export_by_name  "${ASPEED_GPIO}" GPIOX7 SPI_TPM_PIRQ_N
    gpio_export_by_name  "${ASPEED_GPIO}" GPIOY2 CPU_OVERTEMP
    gpio_export_by_name  "${ASPEED_GPIO}" GPIOY3 BMC_EMMC_RST_N
}

setup_gpio_elbert_evt

gpio_set_direction BMC_ALIVE in
gpio_set_direction CPLD_JTAG_SEL_L out
gpio_set_direction CPU_TEMP_ALERT in
gpio_set_direction SCM_PWRGD in
gpio_set_direction SMB_PWRGD in
gpio_set_direction I2C_TPM_PIRQ_N in
gpio_set_direction BMC_BOARD_EEPROM_WP_L out
gpio_set_direction BMC_TEMP_ALERT  in
gpio_set_direction SMB_INTERRUPT in
gpio_set_direction BMC_SPI1_CS0_MUX_SEL out
gpio_set_direction BMC_CHASSIS_EEPROM_WP_L out
gpio_set_direction SPI_TPM_PIRQ_N in
gpio_set_direction CPU_OVERTEMP in
gpio_set_direction JTAG_TRST_L out

# Once we set "out", output values will be random unless we set them
# Set Default GPO values
gpio_set_value CPLD_JTAG_SEL_L 1 # Disabled
gpio_set_value BMC_SPI1_CS0_MUX_SEL 0 # BIOS
gpio_set_value BMC_ALIVE 0 # Default
gpio_set_value BMC_BOARD_EEPROM_WP_L 1 # Disabled
gpio_set_value BMC_CHASSIS_EEPROM_WP_L 1 # Disabled
gpio_set_value JTAG_TRST_L 1 # Disabled
# Set BMC_EMMC_RST_N High to prevent EMMC disable
gpio_set BMC_EMMC_RST_N 1


#!/bin/bash
#
# Copyright 2021-present Facebook. All Rights Reserved.
#
# This program file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program in a file named COPYING; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301 USA
#

### BEGIN INIT INFO
# Provides:          setup_bcm53134.sh
# Required-Start:
# Required-Stop:
# Default-Start:     S
# Default-Stop:
# Short-Description:  Setup the speed on BCM53134 for eth0
### END INIT INFO

PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin

control_page="0x0"
port_state_reg="0xe"
port_state_reg_val="0x8b"
imp_rgmi_control_reg="0x60"
imp_rgmi_control_reg_val="0x0"

# Check BCM53134 registers
curr_speed=$(oob-mdio-util.sh read8 "$control_page" "$port_state_reg"|
             cut -d' ' -f2)
curr_clk_dly=$(oob-mdio-util.sh read8 "$control_page" "$imp_rgmi_control_reg"|
               cut -d' ' -f2)
if [ "$curr_speed" == "$port_state_reg_val" ] && \
   [ "$curr_clk_dly" == "$imp_rgmi_control_reg_val" ]
then
    # Register values are as expected
    exit 0
fi

echo "BCM53134 registers not as expected. Configuring now."
echo "Re-program the BCM53134 EEPROM to fix this permanently."

oob-mdio-util.sh write8 "$control_page" "$port_state_reg" "$port_state_reg_val"
oob-mdio-util.sh write8 "$control_page" "$imp_rgmi_control_reg" \
                        "$imp_rgmi_control_reg_val"
exit 0


#!/bin/sh
#
# Copyright 2020-present Facebook. All Rights Reserved.
#
# This program file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program in a file named COPYING; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301 USA

### BEGIN INIT INFO
# Provides:          setup_board.sh
# Required-Start:
# Required-Stop:
# Default-Start:     S
# Default-Stop:
# Short-Description: Setup the board
### END INIT INFO

PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin

# ELBERTTODO 442075 board_setup.sh
echo "ELBERT BOARD SETUP NOT IMPLEMENTED YET"


#!/bin/bash
#
# Copyright 2020-present Facebook. All Rights Reserved.
#
# This program file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program in a file named COPYING; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301 USA

PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin

# shellcheck disable=SC1091
. /usr/local/bin/openbmc-utils.sh

i2c_device_add 0 0x50 24c512
if ! wedge_is_bmc_personality; then
    echo "uServer is not in BMC personality. Skipping all power-on sequences."
    return
fi

# Some devices occasionally fail to probe. Retry if this happens.
hwmon_device_add() {
    bus="$1"
    devIdHex="$2"
    driver="$3"
    devId="${devIdHex//0x/}"
    i2c_device_add "$bus" "$devIdHex" "$driver"

    # Wait up to 2 seconds for the driver to load
    i=0
    while true; do
        i=$((i+1))
        # Check if device driver loaded successfully
        if [ -d "/sys/bus/i2c/devices/$bus-00$devId/hwmon/" ]; then
            break;
        fi
        if [ $i -gt 20 ]; then
            echo "Timed out waiting for $driver-$bus-$devId"
            break;
        fi
        sleep 0.1
    done

    # If device isn't loaded sucessfully, retry one more time
    if [ ! -d "/sys/bus/i2c/devices/$bus-00$devId/hwmon/" ]; then
        echo "Probe of $driver-$bus-$devId failed. Retrying..."
        i2c_device_delete "$bus" "$devIdHex"
        i2c_device_add "$bus" "$devIdHex" "$driver"
    fi
}

# Calibrate TH4 MAX6581
calibrate_th4_max6581() {
    i2cset -f -y 4 0x4d 0x4a 0x03
    i2cset -f -y 4 0x4d 0x4b 0x1f
    i2cset -f -y 4 0x4d 0x4c 0x03
}

# Probe FSCD devices first in case one module is taking too
# long or is in bad shape
# Supvervisor Inlet
hwmon_device_add 11 0x4c max6658
hwmon_device_add 4 0x4d max6581
calibrate_th4_max6581

# SMBus 0
# Currently not using I2C TPM
# i2c_device_add 0 0x20 tpm_i2c_infineon
# i2c_device_add 0 0x2e tpm_i2c_infineon

# SMBus 3 - Switchcard VRD
hwmon_device_add 3 0x4e ucd90160 # UCD90160
i2c_device_add 3 0x60 raa228228  # RA228228
i2c_device_add 3 0x62 isl68226   # ISL68226
enable_switchcard_power_security

# SMBus 4
i2c_device_add 4 0x23 smbcpld
echo 0 > "$PIM_SMB_MUX_RST"
echo 0 > "$PSU_SMB_MUX_RST"
echo 1 > "$SMB_TH4_I2C_EN_SYSFS"
# Switchcard EEEPROMs
i2c_device_add 4 0x50 24c512
i2c_device_add 4 0x51 24c512
i2c_device_add 4 0x44 net_brcm

# SMBus 6
i2c_device_add 6 0x60 fancpld
# Outlet Temperature
i2c_device_add 6 0x4c max6658

# SMBus 7 CHASSIS EEPROM
i2c_device_add 7 0x50 24c512

# SMBus 9 SCM DPM UCD90320
retry_command 3 enable_power_security_mode 9 0x11 "UCD90320U"
hwmon_device_add 9 0x11 ucd90320
gpio_export_by_offset 9-0011 13 SCM_FPGA_LATCH_L

# SMBus 10 SCM POWER
i2c_device_add 10 0x30 cpupwr
i2c_device_add 10 0x37 mempwr

# SMBus 11 SCM TEMP, POWER
i2c_device_add 11 0x40 pmbus

# Bus  12 SCM CPLD, SCM EEPROM
i2c_device_add 12 0x43 scmcpld
i2c_device_add 12 0x50 24c512

# SMBus 15
hwmon_device_add 15 0x4a lm73

# SMBus 2 PIM MUX, muxed as Bus 16-23
pim_index=(0 1 2 3 4 5 6 7)

pim_bus=(16 17 18 19 20 21 22 23)
if wedge_is_smb_p1; then
    # P1 has different PIM bus mapping
    pim_bus=(16 17 18 23 20 21 22 19)
    echo "P1 SMB detected"
fi

for i in "${pim_index[@]}"
do
    # PIM numbered 2-9
    pim=$((i+2))
    if wedge_is_pim_present "$pim"; then
        # PIM 2-9, SMBUS 16-23
        bus_id="${pim_bus[$i]}"
        i2c_device_add "$bus_id" 0x50 24c512  # EEPROM
        # Only allow writes to certain registers for
        # power controllers UCD9090/TPS546D24
        retry_command 3 enable_tps546d24_wp "$bus_id" 0x16
        retry_command 3 enable_tps546d24_wp "$bus_id" 0x18
        i2c_device_add "$bus_id" 0x16 pmbus   # TPS546D24
        i2c_device_add "$bus_id" 0x18 pmbus   # TPS546D24

        fru="$(peutil "$pim" 2>&1)"
        if echo "$fru" | grep -q '88-16CD2'; then
            echo "PIM$pim has no UCD9090/LM73"
        else
            retry_command 3 enable_power_security_mode "$bus_id" 0x4e "UCD9090B"
            i2c_device_add "$bus_id" 0x4a lm73    # Temp sensor
            i2c_device_add "$bus_id" 0x4e ucd9090 # UCD9090
        fi

        if echo "$fru" | grep -q '88-8D'; then
            i2c_device_add "$bus_id" 0x40 pmbus # MP5023
            i2c_device_add "$bus_id" 0x54 isl68224 # ISL68224
            retry_command 3 maybe_enable_isl_wp "$bus_id" 0x54
        fi
    else
        echo "PIM${pim} not present... skipping."
    fi
done

# SMBus 5 PSU MUX, muxed as Bus 24-27
for id in 1 2 3 4
do
    psu_prsnt="$(head -n 1 "$SMBCPLD_SYSFS_DIR"/psu"$id"_present)"
    if [ "$((psu_prsnt))" -eq 1 ]; then
        # PSU 1-4, SMBUS 22-25
        bus_id=$((23 + id))
        echo "Enable WRITE_PROTECT on PSU$id"
        retry_command 3 enable_psu_wp "$bus_id" 0x58
        i2c_device_add "$bus_id" 0x58 psu_driver
    else
        echo "PSU${id} not present... skipping."
    fi
done

# Print total number of devices.
i2c_check_driver_binding

# Apply lm_sensor threshold settings.
sensors -s


#!/bin/bash
#
# Copyright 2020-present Facebook. All Rights Reserved.
#
# This program file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program in a file named COPYING; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301 USA
#

# shellcheck disable=SC1091
. /usr/local/bin/openbmc-utils.sh

# This utility displays the FPGA versions in each PIM SPI Flash parition

echo "------PIM-FLASH-CONFIG------"
partition_list="header_pim_base header_pim16q header_pim8ddm"
for partition in ${partition_list}; do
    PIM_REVISION_FILE="/tmp/.pim_spi_header_$partition"
    if [ ! -f "$PIM_REVISION_FILE" ]; then
        # version is not cached, force read it
        /usr/local/bin/fpga_util.sh "$partition" read \
                                    "$PIM_REVISION_FILE" > /dev/null 2>&1
        if [ ! -f "$PIM_REVISION_FILE" ]; then
            echo "Failed to read header for $partition"
            exit 1
        fi
    fi
    ver="$(hexdump -n2 "$PIM_REVISION_FILE" | cut -c 9-)"
    val_major="0x${ver:2:2}"
    val_minor="0x${ver:0:2}"
    if [ "$((val_major))" -eq 0 ]; then
        echo "${partition^^}: NOT PRESENT"
     elif [ "$((val_major))" -eq 255 ] && [ "$((val_minor))" -eq 255 ]; then
        echo "${partition^^}: NOT PRESENT"
    else
        echo "${partition^^}: $((val_major)).$((val_minor))"
    fi
done


#!/bin/bash
#
# Copyright 2021-present Facebook. All Rights Reserved.
#
# This program file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program in a file named COPYING; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301 USA
#

# shellcheck disable=SC1091
# shellcheck disable=SC2059
. /usr/local/bin/openbmc-utils.sh

# This utility displays the TH4 QSPI version in the QSPI flash

echo "------QSPI-FLASH-CONFIG------"
QSPI_REVISION_FILE="/tmp/.qspi_spi_image"

partition="header_th4_qspi"
if [ ! -f "$QSPI_REVISION_FILE" ]; then
    # version is not cached, force read it
    /usr/local/bin/fpga_util.sh "$partition" read \
                                "$QSPI_REVISION_FILE" > /dev/null 2>&1
    if [ ! -f "$QSPI_REVISION_FILE" ]; then
        echo "Failed to read $partition"
        exit 1
    fi
fi

HEADER_OFFSET=0x2000
HEADER_LEN=32
LOADER_VERSION_OFFSET=0x4
VERSION_OFFSET=0x10
LOADER_BUILDDATE_OFFSET=0x1c
VERSION_LEN=12

hexbytes=()
while IFS='' read -r line; do hexbytes+=("$line"); done <<< "$(
   hexdump -v -e '/1 "0x%x\n"' -s "$HEADER_OFFSET" -n "$HEADER_LEN" \
      "$QSPI_REVISION_FILE"
)"

fwver=""
foundnull=0
for h in "${hexbytes[@]:$VERSION_OFFSET:$VERSION_LEN}"
do
    if (( h == 0 ))
    then
        foundnull=1
        break
    fi

    if (( h < 0x20 || h > 0x7e ))
    then
        echo "Error: non-printable characters detected"
        fwver=""
        break
    fi

    char=$(printf "\x""${h#0x}")
    fwver="$fwver$char"
done

if (( foundnull == 0 ))
then
    fwver=""
fi

if [[ -z "$fwver" ]]
then
    echo "Error: failed to parse th4_qspi version"
    exit 1
fi

# Decode the firmware loader version
LOADER_VERSION_OFFSET=0x4
mjhigh=${hexbytes[$((LOADER_VERSION_OFFSET + 3 ))]}
mjlow=${hexbytes[$((LOADER_VERSION_OFFSET + 2 ))]}
major=$(( (mjhigh << 8) | mjlow ))
mnhigh=${hexbytes[$((LOADER_VERSION_OFFSET + 1 ))]}
mnlow=${hexbytes[$LOADER_VERSION_OFFSET]}
minor=$(( (mnhigh << 8) | mnlow ))

# Convert version string into major/minor int
# e.g: D000_05 --> 53248.5
fwver_major="$(echo "$fwver" | cut -d '_' -f 1)"
fwver_major_int=$(python -c "print(int('$fwver_major',16))")
fwver_minor="$(echo "$fwver" | cut -d '_' -f 2)"
fwver_minor_int=$(python -c "print(int('$fwver_minor',16))")

printf "FIRMWARE_LOADER_VERSION: %d.%d\n" $major $minor
echo "FIRMWARE_VERSION: $fwver_major_int.$fwver_minor_int"
echo "FIRMWARE_STRING_VERSION: $fwver"

# Print the firmware loader build date
echo -n "FIRMWARE_LOADER_BUILD_DATE: "
for i in 3 2 1 0
do
    printf "%02d" "${hexbytes[$((LOADER_BUILDDATE_OFFSET + i))]#0x}"
done
echo