| #!/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' |
| # Both flash read and write protection are removed by default, but you |
| # can optionally enable them (for testing purposes). |
| DEFINE_boolean 'remove_flash_read_protect' "${FLAGS_TRUE}" \ |
| 'Remove flash read protection while performing command' 'U' |
| DEFINE_boolean 'remove_flash_write_protect' "${FLAGS_TRUE}" \ |
| 'Remove flash write protection while performing command' 'u' |
| 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=" -p -s ${spidev} -r" |
| local STM32MON_WRITE_FLAGS="-p -s ${spidev} -e -w" |
| local stm32mon_flags="" |
| |
| if [[ "${FLAGS_remove_flash_write_protect}" -eq "${FLAGS_TRUE}" ]]; then |
| STM32MON_READ_FLAGS=" -u ${STM32MON_READ_FLAGS}" |
| STM32MON_WRITE_FLAGS="-u ${STM32MON_WRITE_FLAGS}" |
| fi |
| |
| if [[ "${FLAGS_remove_flash_read_protect}" -eq "${FLAGS_TRUE}" ]]; then |
| STM32MON_READ_FLAGS=" -U ${STM32MON_READ_FLAGS}" |
| STM32MON_WRITE_FLAGS="-U ${STM32MON_WRITE_FLAGS}" |
| fi |
| |
| 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" |
| |
| # Print out the actual underlying command we're running and run it |
| local cmd="stm32mon ${stm32mon_flags} ${file}" |
| echo "${cmd}" |
| ${cmd} |
| |
| local cmd_exit_status=$? |
| |
| # 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 |
| |
| if [[ "${cmd_exit_status}" -ne 0 ]]; then |
| exit 1 |
| fi |
| |
| # Test it |
| ectool --name=cros_fp version |
| } |
| |
| config_hatch() { |
| check_gpio_chip_exists "gpiochip200" |
| |
| readonly SPIDEV="/dev/spidev1.1" |
| |
| # See |
| # third_party/coreboot/src/soc/intel/cannonlake/include/soc/gpio_soc_defs.h |
| # for pin name to number mapping. |
| # Examine `cat /sys/kernel/debug/pinctrl/INT34BB:00/gpio-ranges` on a hatch |
| # device to determine gpio number from pin number. |
| |
| # 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 |
| # FP_PWR_EN is on GPP_C11 = 456 + (192 - 181) = 456 + 11 = 467 |
| readonly GPIO_PWREN=467 |
| } |
| |
| 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_<platform_name> |
| # 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_<platform_name>). |
| 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}" |