#!/bin/bash # Copyright 2019 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. . /usr/share/misc/shflags DEFINE_boolean 'read' "${FLAGS_FALSE}" 'Read instead of write' 'r' FLAGS_HELP="Usage: ${0} [flags] ec.bin" # Process commandline flags FLAGS "${@}" || exit 1 eval set -- "${FLAGS_ARGV}" if [[ "$#" -eq 0 ]]; then echo "Missing filename" flags_help exit 1 fi # print out canonical path to differentiate between /usr/local/bin and # /usr/bin installs echo "$(readlink -f "$0")" readonly CROS_EC_SPI_MODALIAS_STR="of:NcrfpTCgoogle,cros-ec-spi" check_hardware_write_protect_disabled() { local hardware_write_protect_state="$(crossystem wpsw_cur)" if [[ "${hardware_write_protect_state}" != "0" ]]; then echo "Please make sure hardware write protect is disabled." echo "See https://www.chromium.org/chromium-os/firmware-porting-guide/firmware-ec-write-protection" exit 1 fi } # Get the spiid for the fingerprint sensor based on the modalias # string: https://crbug.com/955117 get_spiid() { for dev in /sys/bus/spi/devices/*; do if [[ "$(cat "${dev}/modalias")" == "${CROS_EC_SPI_MODALIAS_STR}" ]]; then echo "$(basename "${dev}")" exit 0 fi done exit 1 } # Taken verbatim from # https://chromium.googlesource.com/chromiumos/docs/+/master/lsb-release.md#shell # This should not be used by anything except get_platform_name. # See https://crbug.com/98462. lsbval() { local key="$1" local lsbfile="${2:-/etc/lsb-release}" if ! echo "${key}" | grep -Eq '^[a-zA-Z0-9_]+$'; then return 1 fi sed -E -n -e \ "/^[[:space:]]*${key}[[:space:]]*=/{ s:^[^=]+=[[:space:]]*:: s:[[:space:]]+$:: p }" "${lsbfile}" } # Get the underlying board (reference design) that we're running on (not the # FPMCU or sensor). get_platform_name() { # "-l" converts to lowercase local -l platform_name="$(cros_config /identity platform-name)" # If we don't get any result from cros_config, then we have to possible cases: # # 1) We're running on a non-unibuild device (the only one with FP is nocturne) # 2) We're running on a proto device during bringup and the cros_config # settings haven't yet been setup. # # In either case we can fall back to /etc/lsb-release. It's not recommended # to do this, but we don't have any other options in this case. # # TODO(https://crbug.com/984629): cros_config should handle reading # /etc/lsb-release as backup for us. if [[ -z "${platform_name}" ]]; then echo "Getting platform name from cros_config failed." \ "Falling back to /etc/lsb-release." 1>&2 platform_name="$(lsbval "CHROMEOS_RELEASE_BOARD")" if [[ -z "${platform_name}" ]]; then exit 1 fi fi echo "${platform_name}" } check_gpio_chip_exists() { local gpiochip="$1" if [[ ! -e "/sys/class/gpio/${gpiochip}" ]]; then echo "Cannot find GPIO chip: ${gpiochip}" exit 1 fi } flash_fp_mcu_stm32() { local spidev="${1}" local gpio_nrst="${2}" local gpio_boot0="${3}" local gpio_pwren="${4}" local file="${5}" local spiid local STM32MON_READ_FLAGS=" -U -u -p -s ${spidev} -r" local STM32MON_WRITE_FLAGS="-U -u -p -s ${spidev} -e -w" local stm32mon_flags="" if [[ "${FLAGS_read}" -eq "${FLAGS_TRUE}" ]]; then if [[ -e "${file}" ]]; then echo "Output file already exists: ${file}" exit 1 fi stm32mon_flags="${STM32MON_READ_FLAGS}" else if [[ ! -f "${file}" ]]; then echo "Invalid image file: ${file}" exit 1 fi stm32mon_flags="${STM32MON_WRITE_FLAGS}" fi check_hardware_write_protect_disabled spiid="$(get_spiid)" if [[ $? -ne 0 ]]; then echo "Unable to find FP sensor SPI device: ${CROS_EC_SPI_MODALIAS_STR}" exit 1 fi echo "Flashing SPI device ID: ${spiid}" # Ensure the ACPI is not cutting power when unloading cros-ec-spi if [[ -n "${gpio_pwren}" ]]; then echo "${gpio_pwren}" > /sys/class/gpio/export echo "out" > "/sys/class/gpio/gpio${gpio_pwren}/direction" echo 1 > "/sys/class/gpio/gpio${gpio_pwren}/value" fi # Remove cros_fp if present echo "${spiid}" > /sys/bus/spi/drivers/cros-ec-spi/unbind # Configure the MCU Boot0 and NRST GPIOs echo "${gpio_boot0}" > /sys/class/gpio/export echo "out" > "/sys/class/gpio/gpio${gpio_boot0}/direction" echo "${gpio_nrst}" > /sys/class/gpio/export echo "out" > "/sys/class/gpio/gpio${gpio_nrst}/direction" # Reset sequence to enter bootloader mode echo 1 > "/sys/class/gpio/gpio${gpio_boot0}/value" echo 0 > "/sys/class/gpio/gpio${gpio_nrst}/value" sleep 0.001 # load spidev (fail on cros-ec-spi first to change modalias) echo "${spiid}" > /sys/bus/spi/drivers/cros-ec-spi/bind 2>/dev/null echo "${spiid}" > /sys/bus/spi/drivers/spidev/bind # Release reset as the SPI bus is now ready echo 1 > "/sys/class/gpio/gpio${gpio_nrst}/value" echo "in" > "/sys/class/gpio/gpio${gpio_nrst}/direction" stm32mon ${stm32mon_flags} "${file}" # unload spidev echo "${spiid}" > /sys/bus/spi/drivers/spidev/unbind # Go back to normal mode echo "out" > "/sys/class/gpio/gpio${gpio_nrst}/direction" echo 0 > "/sys/class/gpio/gpio${gpio_boot0}/value" echo 0 > "/sys/class/gpio/gpio${gpio_nrst}/value" echo 1 > "/sys/class/gpio/gpio${gpio_nrst}/value" # Give up GPIO control echo "in" > "/sys/class/gpio/gpio${gpio_boot0}/direction" echo "in" > "/sys/class/gpio/gpio${gpio_nrst}/direction" echo "${gpio_boot0}" > /sys/class/gpio/unexport echo "${gpio_nrst}" > /sys/class/gpio/unexport # wait for FP MCU to come back up (including RWSIG delay) sleep 2 # Put back cros_fp driver echo "${spiid}" > /sys/bus/spi/drivers/cros-ec-spi/bind # Kernel driver is back, we are no longer controlling power if [[ -n "${gpio_pwren}" ]]; then echo "${gpio_pwren}" > /sys/class/gpio/unexport fi # Test it ectool --name=cros_fp version } config_hatch() { check_gpio_chip_exists "gpiochip200" readonly SPIDEV="/dev/spidev1.1" # FPMCU RST_ODL is on GPP_A12 = 200 + 12 = 212 readonly GPIO_NRST=212 # FPMCU BOOT0 is on GPP_A22 = 200 + 22 = 222 readonly GPIO_BOOT0=222 # No PWREN GPIO on Hatch, FPMCU is always on readonly GPIO_PWREN="" } config_nami() { check_gpio_chip_exists "gpiochip360" readonly SPIDEV="/dev/spidev32765.0" # FPMCU RST_ODL is on GPP_C9 = 360 + 57 = 417 readonly GPIO_NRST=417 # FPMCU BOOT0 is on GPP_D5 = 360 + 77 = 437 readonly GPIO_BOOT0=437 # FP_PWR_EN is on GPP_B11 = 360 + 35 = 395 readonly GPIO_PWREN=395 } config_nocturne() { check_gpio_chip_exists "gpiochip360" readonly SPIDEV="/dev/spidev32765.0" # FPMCU RST_ODL is on GPP_C10 = 360 + 58 = 418 readonly GPIO_NRST=418 # FPMCU BOOT0 is on GPP_C8 = 360 + 56 = 416 readonly GPIO_BOOT0=416 # FP_PWR_EN is on GPP_A11 = 360 + 11 = 371 readonly GPIO_PWREN=371 } # The "platform name" corresponds to the underlying board (reference design) # that we're running on (not the FPMCU or sensor). At the moment all of the # reference designs use the same GPIOs. If for some reason a design differs in # the future, we will want to add a nested check in the config_ # function. Doing it in this manner allows us to reduce the number of # configurations that we have to maintain (and reduces the amount of testing # if we're only updating a specific config_). readonly PLATFORM_NAME="$(get_platform_name)" echo "Using config for ${PLATFORM_NAME}" # Check that the config function exists if [[ "$(type -t "config_${PLATFORM_NAME}")" != "function" ]]; then echo "No config for platform ${PLATFORM_NAME}" exit 1 fi config_${PLATFORM_NAME} if [[ $? -ne 0 ]]; then echo "Configuration failed for platform ${PLATFORM_NAME}" exit 1 fi flash_fp_mcu_stm32 \ "${SPIDEV}" \ "${GPIO_NRST}" \ "${GPIO_BOOT0}" \ "${GPIO_PWREN}" \ "${1}"