From 5f2142ef7c0f94cb4c4874432071208c613101f5 Mon Sep 17 00:00:00 2001 From: George Hung Date: Fri, 19 Jun 2020 19:45:43 +0800 Subject: meta-quanta: gbs: add GBS system initial script system initial script: 1. get MB/HSBP/Fan boards REV/SKU ID 2. read FRUs on PE slots 3. reset PHY 4. SATA power enable 5. check HSBP/Fan board cables present 5. verfiy BIOS image 6. Host power on 7. gpio persistence (From meta-quanta rev: 3a9aa27f5ba286922f4d93470f30ac66cd9ff2d6) Signed-off-by: George Hung Change-Id: Ib15171e1828b08b159ff835150ceb4a577fe1a01 Signed-off-by: Andrew Geissler --- .../gbs-sysinit/files/gbs-gpio-common.sh | 165 ++++++++++++ .../gbs-sysinit/files/gbs-sysinit.service | 13 + .../recipes-gbs/gbs-sysinit/files/gbs-sysinit.sh | 297 +++++++++++++++++++++ .../recipes-gbs/gbs-sysinit/gbs-sysinit.bb | 31 +++ 4 files changed, 506 insertions(+) create mode 100644 meta-quanta/meta-gbs/recipes-gbs/gbs-sysinit/files/gbs-gpio-common.sh create mode 100644 meta-quanta/meta-gbs/recipes-gbs/gbs-sysinit/files/gbs-sysinit.service create mode 100644 meta-quanta/meta-gbs/recipes-gbs/gbs-sysinit/files/gbs-sysinit.sh create mode 100644 meta-quanta/meta-gbs/recipes-gbs/gbs-sysinit/gbs-sysinit.bb diff --git a/meta-quanta/meta-gbs/recipes-gbs/gbs-sysinit/files/gbs-gpio-common.sh b/meta-quanta/meta-gbs/recipes-gbs/gbs-sysinit/files/gbs-gpio-common.sh new file mode 100644 index 000000000..55f631a5d --- /dev/null +++ b/meta-quanta/meta-gbs/recipes-gbs/gbs-sysinit/files/gbs-gpio-common.sh @@ -0,0 +1,165 @@ +#!/bin/bash +# Copyright 2020 Google LLC +# Copyright 2020 Quanta Computer Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Common GPIO functions. + +# Map names of GPIOs to GPIO number +declare -A GPIO_NAMES_TO_NUMBER=( + ['RST_BMC_PHY_N']=15 + ['BMC_BRD_REV_ID6']=37 + ['BMC_BRD_REV_ID5']=38 + ['BMC_BRD_SKU_ID3']=39 + ['BMC_BRD_SKU_ID2']=40 + ['FM_BMC_CPU_UART_EN']=76 + ['RST_BMC_RSMRST_N']=87 + ['RST_KBRST_BMC_CPLD_N']=94 + ['FAN_BRD_REV_ID0']=122 + ['FAN_BRD_REV_ID1']=123 + ['HSBP_BRD_REV_ID3']=124 + ['HSBP_BRD_REV_ID2']=125 + ['HSBP_BRD_REV_ID1']=126 + ['BMC_BRD_REV_ID0']=136 + ['BMC_BRD_REV_ID1']=137 + ['BMC_BRD_REV_ID2']=138 + ['BMC_BRD_REV_ID3']=139 + ['BMC_BRD_REV_ID4']=140 + ['BMC_BRD_SKU_ID0']=141 + ['BMC_BRD_SKU_ID1']=142 + ['HDD_PRSNT_N']=160 + ['SPI_SW_SELECT']=169 + ['BMC_BRD_REV_ID7']=194 + ['HSBP_BRD_REV_ID0']=196 +) + +# 1 is active_low 0 is active_high +declare -A GPIO_NAMES_TO_ACTIVE_LOW=( + ['RST_BMC_PHY_N']=1 + ['BMC_BRD_REV_ID6']=0 + ['BMC_BRD_REV_ID5']=0 + ['BMC_BRD_SKU_ID3']=0 + ['BMC_BRD_SKU_ID2']=0 + ['FM_BMC_CPU_UART_EN']=0 + ['RST_BMC_RSMRST_N']=1 + ['RST_KBRST_BMC_CPLD_N']=1 + ['FAN_BRD_REV_ID0']=0 + ['FAN_BRD_REV_ID1']=0 + ['HSBP_BRD_REV_ID3']=0 + ['HSBP_BRD_REV_ID2']=0 + ['HSBP_BRD_REV_ID1']=0 + ['BMC_BRD_REV_ID0']=0 + ['BMC_BRD_REV_ID1']=0 + ['BMC_BRD_REV_ID2']=0 + ['BMC_BRD_REV_ID3']=0 + ['BMC_BRD_REV_ID4']=0 + ['BMC_BRD_SKU_ID0']=0 + ['BMC_BRD_SKU_ID1']=0 + ['HDD_PRSNT_N']=1 + ['SPI_SW_SELECT']=0 + ['BMC_BRD_REV_ID7']=0 + ['HSBP_BRD_REV_ID0']=0 +) + +################################################## +# Initializes the gpio state +# This operation is idempotent and can be applied +# repeatedly to the same gpio. It will make sure the +# gpio ends up in the initialized state even if it +# was. +# Arguments: +# $1: GPIO name +# Return: +# 0 if success, non-zero if error +################################################## +init_gpio() { + if (( $# != 1 )); then + echo "Usage: init_gpio name" >&2 + return 1 + fi + + local name=$1 + + local number=${GPIO_NAMES_TO_NUMBER["${name}"]} + if [[ -z ${number} ]]; then + echo "Missing number info for: ${name}" >&2 + return 2 + fi + + local active_low=${GPIO_NAMES_TO_ACTIVE_LOW["${name}"]} + if [[ -z ${active_low} ]]; then + echo "Missing active_low info for: ${name}" >&2 + return 2 + fi + + if [[ ! -e "/sys/class/gpio/gpio${number}" ]]; then + echo "${number}" >'/sys/class/gpio/export' + fi + echo "${active_low}" >"/sys/class/gpio/gpio${number}/active_low" +} + +################################################## +# Set output GPIO direction. +# Arguments: +# $1: GPIO name +# $2: GPIO direction, "high" or "low" +# Return: +# 0 if success, non-zero if error +################################################## +set_gpio_direction() { + if (( $# != 2 )); then + echo 'Usage: set_gpio_direction name direction' >&2 + return 1 + fi + + local name=$1 + local direction=$2 + + local number=${GPIO_NAMES_TO_NUMBER["${name}"]} + if [[ -z ${number} ]]; then + echo "Missing number info for: ${name}" >&2 + return 2 + fi + + init_gpio "${name}" || return + echo "${direction}" >"/sys/class/gpio/gpio${number}/direction" + echo "Set gpio ${name} #${number} to direction ${direction}" >&2 +} + +################################################## +# Get GPIO value +# Arguments: +# $1: GPIO name +# Return: +# 0 if success, non-zero if error +# stdout: The value of the gpio +################################################## +get_gpio_value() { + if (( $# != 1 )); then + echo 'Usage: get_gpio_value name' >&2 + return 1 + fi + + local name=$1 + + local number=${GPIO_NAMES_TO_NUMBER["${name}"]} + if [[ -z ${number} ]]; then + echo "Missing number info for: ${name}" >&2 + return 2 + fi + + init_gpio "${name}" || return + cat "/sys/class/gpio/gpio${number}/value" +} diff --git a/meta-quanta/meta-gbs/recipes-gbs/gbs-sysinit/files/gbs-sysinit.service b/meta-quanta/meta-gbs/recipes-gbs/gbs-sysinit/files/gbs-sysinit.service new file mode 100644 index 000000000..90cbc63fa --- /dev/null +++ b/meta-quanta/meta-gbs/recipes-gbs/gbs-sysinit/files/gbs-sysinit.service @@ -0,0 +1,13 @@ +[Unit] +Description = Initialization for GBS boot up +Wants=mapper-wait@-xyz-openbmc_project-inventory.service +After=mapper-wait@-xyz-openbmc_project-inventory.service +Wants=mapper-wait@-xyz-openbmc_project-Control-Nvme-Power.service +After=mapper-wait@-xyz-openbmc_project-Control-Nvme-Power.service + +[Service] +Type=oneshot +ExecStart=/usr/bin/gbs-sysinit.sh + +[Install] +WantedBy=multi-user.target diff --git a/meta-quanta/meta-gbs/recipes-gbs/gbs-sysinit/files/gbs-sysinit.sh b/meta-quanta/meta-gbs/recipes-gbs/gbs-sysinit/files/gbs-sysinit.sh new file mode 100644 index 000000000..7b4571091 --- /dev/null +++ b/meta-quanta/meta-gbs/recipes-gbs/gbs-sysinit/files/gbs-sysinit.sh @@ -0,0 +1,297 @@ +#!/bin/bash +# Copyright 2020 Google LLC +# Copyright 2020 Quanta Computer Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +source /usr/libexec/gbs-gpio-common.sh + +WD1RCR_ADDR=0xf080103c +CORSTC_ADDR=0xf080105c +BOARD_VER="" # Set by check_board_ver +pe_eeprom_addr=( 50 54 ) + +SERVICE_NAME="xyz.openbmc_project.Inventory.Manager" +INTERFACE_NAME="xyz.openbmc_project.Inventory.Item" + +PE_PRESENT_OBJPATH=("/xyz/openbmc_project/inventory/system/chassis/gpios/pe_slot0_prsnt" +"/xyz/openbmc_project/inventory/system/chassis/gpios/pe_slot1_prsnt") +HSBP_PRESENT_OBJPATH="/xyz/openbmc_project/inventory/system/chassis/gpios/hsbp_cab_prsnt" +FANBD_PRESENT_OBJPATH="/xyz/openbmc_project/inventory/system/chassis/gpios/fanbd_cab_prsnt" +BP12V_PRESENT_OBJPATH="/xyz/openbmc_project/inventory/system/chassis/gpios/bp12v_cab_prsnt" +SATA0_PRESENT_OBJPATH="/xyz/openbmc_project/inventory/system/chassis/gpios/sata0_prsnt" + +set_gpio_persistence() { + reg_val=$(devmem ${WD1RCR_ADDR} 32) + # Clear bit 16-23 to perserve all GPIO states across warm resets + reg_val=$(printf "0x%08x" $((reg_val & ~0xff0000))) + echo "Setting WD1RCR_ADDR to ${reg_val}" + devmem "${WD1RCR_ADDR}" 32 "${reg_val}" + + reg_val=$(devmem ${CORSTC_ADDR} 32) + # Clear bit 16-23 of CORSTC + reg_val=$(printf "0x%08x" $((reg_val & ~0xff0000))) + echo "Setting CORSTC_ADDR to ${reg_val}" + devmem "${CORSTC_ADDR}" 32 "${reg_val}" +} + +get_board_rev_id() { + echo $(get_gpio_value 'BMC_BRD_REV_ID7')\ + $(get_gpio_value 'BMC_BRD_REV_ID6')\ + $(get_gpio_value 'BMC_BRD_REV_ID5')\ + $(get_gpio_value 'BMC_BRD_REV_ID4')\ + $(get_gpio_value 'BMC_BRD_REV_ID3')\ + $(get_gpio_value 'BMC_BRD_REV_ID2')\ + $(get_gpio_value 'BMC_BRD_REV_ID1')\ + $(get_gpio_value 'BMC_BRD_REV_ID0')\ + | sed 's/ //g' > ~/board_rev_id.txt +} + +get_board_sku_id() { + echo $(get_gpio_value 'BMC_BRD_SKU_ID3')\ + $(get_gpio_value 'BMC_BRD_SKU_ID2')\ + $(get_gpio_value 'BMC_BRD_SKU_ID1')\ + $(get_gpio_value 'BMC_BRD_SKU_ID0')\ + | sed 's/ //g' > ~/board_sku_id.txt +} + +get_hsp_board_rev_id() { + echo $(get_gpio_value 'HSBP_BRD_REV_ID3')\ + $(get_gpio_value 'HSBP_BRD_REV_ID2')\ + $(get_gpio_value 'HSBP_BRD_REV_ID1')\ + $(get_gpio_value 'HSBP_BRD_REV_ID0')\ + | sed 's/ //g' > ~/hsp_board_rev_id.txt +} + +get_fan_board_rev_id() { + echo $(get_gpio_value 'FAN_BRD_REV_ID1')\ + $(get_gpio_value 'FAN_BRD_REV_ID0')\ + | sed 's/ //g' > ~/fan_board_rev_id.txt +} + +check_board_ver() { + # Sets BOARD_VER to either "PREPVT" or "PVT" + # + # BOARD_REV_ID[7:6] = + # 0x00 - EVT + # 0x01 - DVT + # 0x10 - PVT + # 0x11 - MP + + rev7_val=$(get_gpio_value 'BMC_BRD_REV_ID7') + if (( rev7_val == 0 )); then + echo "EVT/DVT rev!" + BOARD_VER="PREPVT" + else + echo "PVT/MP rev!" + BOARD_VER="PVT" + fi +} + +check_board_sku() { + sku1_val=$(get_gpio_value 'BMC_BRD_SKU_ID1') + if (( sku1_val == 1 )); then + echo "GBS SKU!" + BOARD_SKU="GBS" + else + echo "Other SKU!" + BOARD_SKU="TBD" + fi +} + +set_uart_en_low() { + # GPIO76 UART_EN polarity inverted between DVT/PVT + # Pin direction was set high in the kernel. + set_gpio_direction 'FM_BMC_CPU_UART_EN' low +} + +set_hdd_prsnt() { + # On PVT need to forward SATA0_PRSNT_N to HDD_PRSNT_N + # The signal is safe to set on DVT boards so just set universally. + sata_prsnt_n="$(busctl get-property $SERVICE_NAME ${SATA0_PRESENT_OBJPATH} \ + $INTERFACE_NAME Present | awk '{print $2}')" + if [[ ${sata_prsnt_n} != "true" ]]; then + return 1 + fi + # sata_prsnt_n is active low => value "true" means low + if [[ ${sata_prsnt_n} == "true" ]]; then + set_gpio_direction 'HDD_PRSNT_N' low + else + set_gpio_direction 'HDD_PRSNT_N' high + fi +} + +KERNEL_FIU_ID="c0000000.fiu" +KERNEL_SYSFS_FIU="/sys/bus/platform/drivers/NPCM-FIU" + +bind_host_mtd() { + set_gpio_direction 'SPI_SW_SELECT' high + if [[ -d ${KERNEL_SYSFS_FIU}/${KERNEL_FIU_ID} ]]; then + echo "${KERNEL_FIU_ID}" > "${KERNEL_SYSFS_FIU}"/unbind + fi + echo "${KERNEL_FIU_ID}" > "${KERNEL_SYSFS_FIU}"/bind +} + +unbind_host_mtd() { + if [[ -d ${KERNEL_SYSFS_FIU}/${KERNEL_FIU_ID} ]]; then + echo "${KERNEL_FIU_ID}" > "${KERNEL_SYSFS_FIU}"/unbind + fi + set_gpio_direction 'SPI_SW_SELECT' low +} +trap unbind_host_mtd EXIT SIGHUP SIGINT SIGTERM + +# Taken from /run/initramfs/update +# Given label name, return mtd node. e.g. `findmtd bmc` returns 'mtd0' +findmtd() { + m=$(grep -xl "$1" /sys/class/mtd/*/name) + m=${m%/name} + m=${m##*/} + echo $m +} + +verify_host_bios() { + echo "BIOS verification start!" + + # placeholder for verifying host BIOS. For now time BIOS read + # with dd + bind_host_mtd || { echo "Failed to bind FIU driver for host MTD"; return 1; } + + pnor_mtd=$(findmtd pnor) + [[ -z "${pnor_mtd}" ]] && { echo "Failed to find host MTD partition!"; return 1; } + + # Test timing by computing SHA256SUM. + sha256sum /dev/${pnor_mtd}ro + + echo "BIOS verification complete!" + unbind_host_mtd +} + +reset_phy() { + ifconfig eth1 down + set_gpio_direction 'RST_BMC_PHY_N' low + set_gpio_direction 'RST_BMC_PHY_N' high + ifconfig eth1 up +} + +parse_pe_fru() { + pe_fruid=3 + for i in {1..2}; + do + pe_prsnt_n="$(busctl get-property $SERVICE_NAME ${PE_PRESENT_OBJPATH[$(($i-1))]} \ + $INTERFACE_NAME Present | awk '{print $2}')" + + if [[ ${pe_prsnt_n} != "true" ]]; then + pe_fruid=$(($pe_fruid+1)) + continue + fi + + # Output is the i2c bus number for the PCIE cards on PE0/PE1 + # i2c-0 -> i2c mux (addr: 0x71) -> PE0/PE1 + # PE0: channel 0 + # PE1: channel 1 + pe_fru_bus="$(ls -al /sys/bus/i2c/drivers/pca954x/0-0071/ | grep channel \ + | awk -F "/" '{print $(NF)}' | awk -F "-" '{print $2}' | sed -n "${i}p")" + + # If the PE FRU EEPROM syspath does not exist, create it ("24c02" is the + # EEPROM part number) and perform a phosphor-read-eeprom + for ((j=0; j < ${#pe_eeprom_addr[@]}; j++)); + do + i2cget -f -y $pe_fru_bus "0x${pe_eeprom_addr[$j]}" 0x01 > /dev/null 2>&1 + if [ $? -eq 0 ]; then + if [ ! -f "/sys/bus/i2c/devices/$pe_fru_bus-00${pe_eeprom_addr[$j]}/eeprom" ]; then + echo 24c02 "0x${pe_eeprom_addr[$j]}" > "/sys/bus/i2c/devices/i2c-$pe_fru_bus/new_device" + fi + pe_fru_bus="/sys/bus/i2c/devices/$pe_fru_bus-00${pe_eeprom_addr[$j]}/eeprom" + phosphor-read-eeprom --eeprom $pe_fru_bus --fruid $pe_fruid + break + fi + done + pe_fruid=$(($pe_fruid+1)) + done +} + +check_power_status() { + res0="$(busctl get-property -j xyz.openbmc_project.State.Chassis \ + /xyz/openbmc_project/state/chassis0 xyz.openbmc_project.State.Chassis \ + CurrentPowerState | jq -r '.["data"]')" + echo $res0 +} + +main() { + get_board_rev_id + get_board_sku_id + + check_board_ver + if [[ "${BOARD_VER}" == "PREPVT" ]]; then + set_uart_en_low + fi + + check_board_sku + + hsbp_prsnt="$(busctl get-property $SERVICE_NAME ${HSBP_PRESENT_OBJPATH} \ + $INTERFACE_NAME Present | awk '{print $2}')" + if [[ ${hsbp_prsnt} == "true" ]]; then + get_hsp_board_rev_id + hsbp_12v_prsnt="$(busctl get-property $SERVICE_NAME ${BP12V_PRESENT_OBJPATH} \ + $INTERFACE_NAME Present | awk '{print $2}')" + if [[ ${hsbp_12v_prsnt} != "true" ]]; then + echo "HSBP board power cable(12V) not present !!" + fi + else + echo "HSBP board sideband cable not present !!" + echo "Stop NVMe Power/LED control Service " + systemctl stop xyz.openbmc_project.Control.Nvme.Power + systemctl stop xyz.openbmc_project.nvme.manager + fi + + fan_prsnt="$(busctl get-property $SERVICE_NAME ${FANBD_PRESENT_OBJPATH} \ + $INTERFACE_NAME Present | awk '{print $2}')" + if [[ ${fan_prsnt} == "true" ]]; then + get_fan_board_rev_id + else + echo "Fan board sideband cable not present !!" + fi + + set_hdd_prsnt + + reset_phy + + if [[ $(check_power_status) != \ + 'xyz.openbmc_project.State.Chassis.PowerState.On' ]]; then + verify_host_bios + + echo "Release host from reset!" >&2 + set_gpio_direction 'RST_BMC_RSMRST_N' high + set_gpio_direction 'RST_KBRST_BMC_CPLD_N' high + # TODO: remove the hack once kernel driver is ready + # Set the GPIO states to preserve across reboots + set_gpio_persistence + + echo "Starting host power!" >&2 + busctl set-property xyz.openbmc_project.State.Host \ + /xyz/openbmc_project/state/host0 \ + xyz.openbmc_project.State.Host \ + RequestedHostTransition s \ + xyz.openbmc_project.State.Host.Transition.On + else + echo "Host is already running, doing nothing!" >&2 + fi + + parse_pe_fru +} + +# Exit without running main() if sourced +return 0 2>/dev/null + +main "$@" diff --git a/meta-quanta/meta-gbs/recipes-gbs/gbs-sysinit/gbs-sysinit.bb b/meta-quanta/meta-gbs/recipes-gbs/gbs-sysinit/gbs-sysinit.bb new file mode 100644 index 000000000..68a9cf181 --- /dev/null +++ b/meta-quanta/meta-gbs/recipes-gbs/gbs-sysinit/gbs-sysinit.bb @@ -0,0 +1,31 @@ +SUMMARY = "Phosphor OpenBMC Quanta GBS System Initialization Service" +DESCRIPTION = "Phosphor OpenBMC Quanta GBS System Init" +PR = "r1" +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10" + +inherit systemd + +DEPENDS += "systemd" +RDEPENDS_${PN} += "bash" +RDEPENDS_${PN} += "libsystemd" +RDEPENDS_${PN} += "jq" + +SRC_URI = "file://gbs-sysinit.sh \ + file://gbs-gpio-common.sh \ + file://gbs-sysinit.service \ + " + +do_install () { + install -d ${D}${bindir} + install -m 0755 ${WORKDIR}/gbs-sysinit.sh ${D}${bindir}/ + + install -d ${D}${libexecdir} + install -m 0755 ${WORKDIR}/gbs-gpio-common.sh ${D}${libexecdir}/ + + install -d ${D}${systemd_system_unitdir} + install -m 0644 ${WORKDIR}/gbs-sysinit.service ${D}${systemd_system_unitdir} +} + +SYSTEMD_PACKAGES = "${PN}" +SYSTEMD_SERVICE_${PN} = "gbs-sysinit.service" -- cgit v1.2.3