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

Signed-off-by: George Hung <george.hung@quantatw.com>
Change-Id: Ia28cbf93b04d09f519a0fa5fe8f8bda0b2eed347
diff --git a/meta-gbs/recipes-gbs/gbs-sysinit/files/gbs-gpio-common.sh b/meta-gbs/recipes-gbs/gbs-sysinit/files/gbs-gpio-common.sh
new file mode 100644
index 0000000..55f631a
--- /dev/null
+++ b/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-gbs/recipes-gbs/gbs-sysinit/files/gbs-sysinit.service b/meta-gbs/recipes-gbs/gbs-sysinit/files/gbs-sysinit.service
new file mode 100644
index 0000000..90cbc63
--- /dev/null
+++ b/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-gbs/recipes-gbs/gbs-sysinit/files/gbs-sysinit.sh b/meta-gbs/recipes-gbs/gbs-sysinit/files/gbs-sysinit.sh
new file mode 100644
index 0000000..7b45710
--- /dev/null
+++ b/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-gbs/recipes-gbs/gbs-sysinit/gbs-sysinit.bb b/meta-gbs/recipes-gbs/gbs-sysinit/gbs-sysinit.bb
new file mode 100644
index 0000000..68a9cf1
--- /dev/null
+++ b/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"