| #!/bin/bash |
| # Copyright 2019 The ChromiumOS Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| # Disable check that incorrectly detects the "config_*" functions as unreachable. |
| # shellcheck disable=SC2317 |
| |
| shflags_path="" |
| for path in \ |
| "/usr/share/misc/shflags" \ |
| "/system/usr/share/misc/shflags" |
| do |
| if [ -f "${path}" ]; then |
| shflags_path="${path}" |
| break |
| fi |
| done |
| |
| # shellcheck source=../../../scripts/lib/shflags/shflags |
| . "${SHFLAGS:-${shflags_path}}" || exit 1 |
| |
| readonly DEFAULT_RETRIES=${DEFAULT_RETRIES:-4} |
| readonly STM32MON_CONNECT_RETRIES=${STM32MON_CONNECT_RETRIES:-6} |
| readonly STM32MON_SERIAL_BAUDRATE=${STM32MON_SERIAL_BAUDRATE:-115200} |
| |
| 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' |
| DEFINE_integer 'retries' "${DEFAULT_RETRIES}" 'Specify number of retries' 'R' |
| DEFINE_integer 'baudrate' "${STM32MON_SERIAL_BAUDRATE}" 'Specify UART baudrate' 'B' |
| DEFINE_boolean 'hello' "${FLAGS_FALSE}" 'Only ping the bootloader' 'H' |
| DEFINE_boolean 'services' "${FLAGS_TRUE}" \ |
| 'Stop the biod service before flashing and then resume it afterwards' 's' |
| FLAGS_HELP="Usage: ${0} [flags] [ec.bin]" |
| |
| # EXIT_SUCCESS=0 |
| # EXIT_FAILURE=1 |
| # EXIT_BASHBUILTIN=2 |
| readonly EXIT_ARGUMENT=3 |
| readonly EXIT_CONFIG=4 |
| readonly EXIT_PRECONDITION=5 |
| readonly EXIT_RUNTIME=6 |
| |
| # Process commandline flags |
| FLAGS "${@}" || exit "${EXIT_ARGUMENT}" |
| eval set -- "${FLAGS_ARGV}" |
| |
| readonly CROS_EC_SPI_MODALIAS_STR="of:NcrfpTCgoogle,cros-ec-spi" |
| readonly CROS_EC_FP_SPI_MODALIAS_STR="spi:cros-ec-fp" |
| |
| readonly CROS_EC_UART_MODALIAS_STR="of:NcrfpTCgoogle,cros-ec-uart" |
| |
| # Clean up any remaining gpioset processes on exit. |
| trap 'kill "${GPIO_ACTIVE_PIDS[@]}" 2>/dev/null' EXIT |
| |
| klog() { |
| echo "flash_fp_mcu: $*" > /dev/kmsg |
| } |
| |
| # POSIX-compliant helper to check if a name is a defined function. |
| # Usage: is_function <function_name> |
| is_function() { |
| type "$1" 2>/dev/null | grep -q 'function' |
| } |
| |
| os() { |
| if [[ -f "/system/build.prop" ]]; then |
| echo "android" |
| return 0 |
| fi |
| |
| if [[ -f "/etc/lsb-release" ]] && grep -q "CHROMEOS_RELEASE" "/etc/lsb-release" 2>/dev/null; then |
| echo "chromeos" |
| return 0 |
| fi |
| |
| echo "unknown" |
| return 1 |
| } |
| |
| check_spidev() { |
| if [[ -d "/sys/module/spidev" ]]; then |
| return 0 |
| fi |
| |
| if [[ "$(os)" == "android" ]]; then |
| if insmod /vendor/lib/modules/spidev.ko; then |
| return 0 |
| fi |
| fi |
| |
| echo "Failed to load spidev kernel module" >&2 |
| exit "${EXIT_PRECONDITION}" |
| } |
| |
| stop_services() { |
| if [[ "$(os)" == "android" ]]; then |
| echo "# Stopping vendor.biometrics.fingerprint-service.crosfp" |
| stop vendor.biometrics.fingerprint-service.crosfp |
| return 0 |
| fi |
| |
| if [[ "${FLAGS_services}" -eq "${FLAGS_TRUE}" ]]; then |
| echo "# Stopping biod" |
| stop biod |
| fi |
| echo "# Stopping timberslide" |
| stop timberslide LOG_PATH=/sys/kernel/debug/cros_fp/console_log |
| } |
| |
| start_services() { |
| if [[ "$(os)" == "android" ]]; then |
| echo "# Restarting vendor.biometrics.fingerprint-service.crosfp" |
| start vendor.biometrics.fingerprint-service.crosfp |
| return 0 |
| fi |
| |
| if [[ "${FLAGS_services}" -eq "${FLAGS_TRUE}" ]]; then |
| echo "# Restarting timberslide" |
| start timberslide LOG_PATH=/sys/kernel/debug/cros_fp/console_log |
| echo "# Restarting biod" |
| start biod |
| fi |
| } |
| |
| echo_service_warning() { |
| if [[ "$(os)" == "android" ]]; then |
| echo "Try 'stop vendor.biometrics.fingerprint-service.crosfp'" >&2 |
| else |
| echo "Try 'stop biod' and" >&2 |
| echo "'stop timberslide LOG_PATH=/sys/kernel/debug/cros_fp/console_log'" >&2 |
| fi |
| |
| echo "before running this script." >&2 |
| } |
| |
| check_hardware_write_protect_disabled() { |
| local hardware_write_protect_state |
| if ! hardware_write_protect_state="$(crossystem wpsw_cur)"; then |
| echo "Failed to get hardware write protect status" >&2 |
| exit "${EXIT_PRECONDITION}" |
| fi |
| if [[ "${hardware_write_protect_state}" != "0" ]]; then |
| echo "Unable to proceed because hardware write protect is enabled." |
| echo "Please disable the hardware write protect, so that we can" \ |
| "reboot the FPMCU into the ROM bootloader." |
| echo "See https://www.chromium.org/chromium-os/firmware-porting-guide/firmware-ec-write-protection" |
| exit "${EXIT_PRECONDITION}" |
| fi |
| } |
| |
| # Get the gpiochip base number from sysfs that matches |
| # a device path |
| get_sysfs_gpiochip_base() { |
| local match="$1" |
| |
| for chip in /sys/class/gpio/gpiochip*; do |
| if readlink "${chip}" | grep -q "${match}"; then |
| echo "${chip#/sys/class/gpio/gpiochip}" |
| return 0 |
| fi |
| done |
| |
| return 1 |
| } |
| |
| # 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 |
| case "$(cat "${dev}/modalias")" in |
| "${CROS_EC_SPI_MODALIAS_STR}"|"${CROS_EC_FP_SPI_MODALIAS_STR}") |
| basename "${dev}" |
| return 0 |
| ;; |
| esac |
| done |
| |
| return 1 |
| } |
| |
| # Get the uartid for the fingerprint sensor based on the modalias |
| get_uartid() { |
| for dev in /sys/bus/serial/devices/*; do |
| if [ -f "${dev}/modalias" ]; then |
| if [[ "$(cat "${dev}/modalias")" == "${CROS_EC_UART_MODALIAS_STR}" ]]; then |
| basename "${dev}" |
| return 0 |
| fi |
| fi |
| done |
| |
| return 1 |
| } |
| |
| # Usage: gpio <unexport|export|in|out|0|1|get> <signal> [signal...] |
| _gpio_legacy() { |
| local cmd="$1" |
| shift |
| |
| for signal in "$@"; do |
| case "${cmd}" in |
| unexport|export) |
| klog "Set gpio ${signal} to ${cmd}" |
| echo "${signal}" > "/sys/class/gpio/${cmd}" |
| ;; |
| in|out) |
| local direction="${cmd}" |
| klog "Set gpio ${signal} direction to ${direction}" |
| echo "${direction}" > "/sys/class/gpio/gpio${signal}/direction" |
| ;; |
| 0|1) |
| local value="${cmd}" |
| klog "Set gpio ${signal} to ${value}" |
| echo "${value}" > "/sys/class/gpio/gpio${signal}/value" |
| ;; |
| get) |
| local value="${cmd}" |
| klog "Get gpio ${signal}" |
| cat "/sys/class/gpio/gpio${signal}/value" |
| ;; |
| *) |
| echo "Invalid gpio command: ${cmd}" >&2 |
| exit "${EXIT_RUNTIME}" |
| ;; |
| esac |
| done |
| } |
| |
| # Usage: _gpioset_hold <gpio_chip> <signal> <value> |
| _gpioset_hold() { |
| local gpio_chip="$1" |
| local signal="$2" |
| local value="$3" |
| |
| # Kill any gpioset processes for this GPIO before spawning a new one. |
| if [[ -n "${GPIO_ACTIVE_PIDS[${signal}]}" ]]; then |
| kill "${GPIO_ACTIVE_PIDS[${signal}]}" |
| unset "GPIO_ACTIVE_PIDS[${signal}]" |
| fi |
| |
| # We need to run gpioset as a background process in order to hold the |
| # gpio at a specific value. |
| gpioset --mode=signal "${gpio_chip}" "${signal}=${value}" & |
| GPIO_ACTIVE_PIDS[${signal}]=$! |
| klog "Set and hold GPIO ${gpio_chip}/${signal} to ${value} (PID: ${GPIO_ACTIVE_PIDS[${signal}]})" |
| } |
| |
| # Usage: _gpioset <gpio_chip> <signal> <value> |
| _gpioset() { |
| local gpio_chip="$1" |
| local signal="$2" |
| local value="$3" |
| gpioset "${gpio_chip}" "${signal}=${value}" |
| klog "Set GPIO ${gpio_chip}/${signal} to ${value}" |
| } |
| |
| # Usage: gpio <unexport|export|in|out|0|1|get> <signal> [signal...] |
| _gpio_new() { |
| local cmd="$1" |
| shift |
| |
| if [[ -z "${GPIO_CHIP}" ]]; then |
| echo "Error: GPIO_CHIP variable is not set." >&2 |
| exit "${EXIT_CONFIG}" |
| fi |
| |
| for signal in "$@"; do |
| case "${cmd}" in |
| 0|1) |
| if [[ "${GPIOSET_SUPPORTS_HOLD}" -eq 1 ]]; then |
| _gpioset_hold "${GPIO_CHIP}" "${signal}" "${cmd}" |
| else |
| _gpioset "${GPIO_CHIP}" "${signal}" "${cmd}" |
| fi |
| ;; |
| get) |
| klog "Get GPIO ${GPIO_CHIP}/${signal}" |
| gpioget "${GPIO_CHIP}" "${signal}" |
| ;; |
| unexport|export|in|out) |
| # no-op |
| ;; |
| *) |
| echo "Invalid gpio command: ${cmd}" >&2 |
| exit "${EXIT_RUNTIME}" |
| ;; |
| esac |
| done |
| } |
| |
| # Usage: gpio <0|1|get> <signal> [signal...] |
| gpio() { |
| if [[ "${USE_LEGACY_GPIO_API}" -eq 1 ]]; then |
| _gpio_legacy "$@" |
| else |
| _gpio_new "$@" |
| fi |
| } |
| |
| gpio_chip_exists() { |
| if [[ "${USE_LEGACY_GPIO_API}" -eq 1 ]]; then |
| [[ -e "/sys/class/gpio/${GPIO_CHIP}" ]] |
| return |
| fi |
| |
| gpioinfo "${GPIO_CHIP}" > /dev/null 2>&1 |
| } |
| |
| # Usage: warn_gpio <signal> <expected_value> <msg> |
| warn_gpio() { |
| local signal=$1 |
| local expected_value=$2 |
| local msg=$3 |
| |
| local value |
| if ! value="$(gpio get "${signal}")"; then |
| echo "Error fetching gpio value ${signal}" >&2 |
| exit "${EXIT_RUNTIME}" |
| fi |
| |
| if [[ "${value}" != "${expected_value}" ]]; then |
| echo "${msg}" >&2 |
| return 1 |
| fi |
| } |
| |
| # Taken verbatim from |
| # https://chromium.googlesource.com/chromiumos/docs/+/HEAD/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). |
| # This may be an extended platform name, like nami-kernelnext, hatch-arc-r, |
| # or hatch-borealis. |
| get_platform_name() { |
| local platform_name |
| |
| case "$(os)" in |
| "chromeos") |
| # We used to use "cros_config /identity platform-name", but that is specific |
| # to mosys and does not actually provide the board name in all cases. |
| # cros_config intentionally does not provide a way to get the board |
| # name: b/156650654. |
| |
| # If there was a way to get the board name from cros_config, it's possible |
| # that it could fail in the following 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 all cases 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. |
| echo "Getting platform name from /etc/lsb-release." 1>&2 |
| platform_name="$(lsbval "CHROMEOS_RELEASE_BOARD")" |
| if [[ -z "${platform_name}" ]]; then |
| return 1 |
| fi |
| ;; |
| "android") |
| echo "Getting platform name from ro.product.device." 1>&2 |
| platform_name="$(getprop ro.product.device 2>/dev/null)" |
| if [[ -z "${platform_name}" ]]; then |
| return 1 |
| fi |
| ;; |
| *) |
| echo "Unsupported operating system" 1>&2 |
| return 1 |
| ;; |
| esac |
| |
| echo "${platform_name}" |
| } |
| |
| # Get the model name as a string. |
| get_model_name() { |
| local model_name |
| |
| echo "Getting model name." 1>&2 |
| model_name="$(cros_config / name)" |
| if [[ -z "${model_name}" ]]; then |
| return 1 |
| fi |
| |
| echo "${model_name}" |
| } |
| |
| # Return the board version as a string. The board version corresponds to EVT, |
| # DVT, PVT, etc. |
| # https://chromium.googlesource.com/chromiumos/docs/+/HEAD/design_docs/cros_board_info.md#board_version |
| # NOTE: This doesn't work on all platforms and should only be used for |
| # short-term workarounds that are fixed in later hardware. |
| get_board_version() { |
| local board_version |
| |
| echo "Getting board version." 1>&2 |
| board_version="$(ectool cbi get 0 | grep -o '[0-9]\+' | head -n 1)" |
| if [[ -z "${board_version}" ]]; then |
| return 1 |
| fi |
| |
| echo "${board_version}" |
| } |
| |
| power_cycle_fpmcu() { |
| local gpio_pwren="${1}" |
| |
| if [[ "${gpio_pwren}" -gt 0 ]]; then |
| echo "Power cycling the FPMCU." |
| gpio export "${gpio_pwren}" |
| gpio out "${gpio_pwren}" |
| gpio 0 "${gpio_pwren}" |
| # Must outlast hardware soft start, which is typically ~3ms. |
| sleep 0.5 |
| gpio 1 "${gpio_pwren}" |
| # Power enable line is externally pulled down, so leave as output-high. |
| gpio unexport "${gpio_pwren}" |
| fi |
| } |
| |
| # Given a full platform name, extract the base platform. |
| # |
| # Tests are also run on modified images, like hatch-arc-r, hatch-borealis, or |
| # hatch-kernelnext. These devices still have fingerprint and are expected to |
| # pass tests. The full platform name reflects these modifications and might |
| # be needed to apply an alternative configuration (kernelnext). Other modified |
| # tests (arc-r) just need to default to the base platform config, which is |
| # identified by this function. |
| # See b/186697064. |
| # |
| # Examples: |
| # * platform_base_name "hatch-kernelnext" --> "hatch" |
| # * platform_base_name "hatch-arc-r" --> "hatch" |
| # * platform_base_name "hatch-borealis" --> "hatch" |
| # * platform_base_name "hatch" --> "hatch" |
| # * platform_base_name "strongbad64" --> "strongbad" |
| # |
| # Usage: platform_base_name <platform_name> |
| platform_base_name() { |
| local platform_name="$1" |
| |
| # We remove any suffix starting at the first '-'. |
| local base_name="${platform_name%%-*}" |
| |
| # Remove any numbers from the base name. |
| echo "${base_name//[0-9]/}" |
| } |
| |
| get_fp_mcu_board() { |
| local board |
| |
| case "$(os)" in |
| "chromeos") |
| board="$(cros_config /fingerprint board)" |
| ;; |
| "android") |
| board="$(dev_config Fingerprint board)" |
| ;; |
| *) |
| echo "Unsupported operating system" 1>&2 |
| exit "${EXIT_CONFIG}" |
| ;; |
| esac |
| |
| echo "${board}" |
| } |
| |
| get_chip() { |
| local board |
| board="$(get_fp_mcu_board)" |
| |
| case "${board}" in |
| bloonchipper|dartmonkey|nocturne_fp|nami_fp) |
| echo "STM32" |
| ;; |
| buccaneer|gwendolin|helipilot|rosalia) |
| echo "NPCX" |
| ;; |
| *) |
| echo "Error - Unknown board '${board}'." >&2 |
| return "${EXIT_CONFIG}" |
| ;; |
| esac |
| } |
| |
| get_firmware_dir() { |
| if [[ "$(os)" == "android" ]]; then |
| echo "/apex/com.android.hardware.fingerprint.crosfp/etc/firmware" |
| else |
| echo "/opt/google/biod/fw" |
| fi |
| } |
| |
| get_default_fw() { |
| local board |
| board="$(get_fp_mcu_board)" |
| |
| # If cros_config returns "", that is okay assuming there is only |
| # one firmware file on disk. |
| |
| local tmp_file |
| tmp_file="$(mktemp)" |
| |
| find "$(get_firmware_dir)" -maxdepth 1 -name "${board}*.bin" 2>/dev/null > "${tmp_file}" |
| |
| local num_fw_files |
| num_fw_files="$(wc -l < "${tmp_file}")" |
| local fw_file |
| fw_file="$(head -n 1 "${tmp_file}" 2>/dev/null)" |
| |
| rm -f "${tmp_file}" |
| |
| if [[ "${num_fw_files}" -ne 1 ]]; then |
| return 1 |
| fi |
| |
| echo "${fw_file}" |
| } |
| |
| # Find processes that have the named file, active or deleted, open. |
| # |
| # Deleted files are important because unbinding/rebinding cros-ec |
| # with biod/timberslide running will result in the processes holding open |
| # a deleted version of the files. Issues can arise if the process continue |
| # to interact with the deleted files (ex kernel panic) while the raw driver |
| # is being used in flash_fp_mcu. The lsof and fuser tools can't seem to |
| # identify usages of the deleted named file directly, without listing all |
| # files. This takes a large amount out time on Chromebooks, thus we need this |
| # custom search routine. |
| # |
| # Usage: proc_open_file [file_pattern] |
| proc_open_file() { |
| local file_pattern="$1" |
| |
| find /proc/ -maxdepth 3 -type l -path '*/fd/*' -exec ls -l {} + 2>/dev/null | \ |
| awk -v pattern="${file_pattern}" ' |
| { |
| proc_fd_col = "" |
| filename_col = 0 |
| |
| for (i = 1; i <= NF; i++) { |
| if ($i ~ /^\/proc\//) { |
| proc_fd_col = $i |
| } |
| if ($i == "->") { |
| filename_col = i + 1 |
| } |
| } |
| |
| if (proc_fd_col == "" || filename_col == 0) { |
| next |
| } |
| |
| if ($(filename_col) ~ pattern) { |
| split(proc_fd_col, p, "/") |
| print "PID", p[3], "->", $(filename_col), $(filename_col + 1) |
| found = 1 |
| } |
| } |
| END { |
| # 0 if found, 1 if not found. |
| exit !found |
| } |
| ' |
| return $? |
| } |
| |
| flash_fp_mcu_npcx() { |
| local transport="${1}" |
| local device="${2}" |
| local gpio_nrst="${3}" |
| local file="${4}" |
| |
| local cmd_flags="" |
| |
| if [[ "${transport}" == "SPI" ]]; then |
| cmd_flags+=" -p linux_spi:dev=${device},spispeed=250k" |
| |
| if [[ "${FLAGS_read}" -eq "${FLAGS_TRUE}" ]]; then |
| # Read from FPMCU to file |
| if [[ -e "${file}" ]]; then |
| echo "Output file already exists: ${file}" |
| return "${EXIT_PRECONDITION}" |
| fi |
| echo "# Reading to '${file}' over ${transport}" |
| cmd_flags+=" -r ${file}" |
| else |
| # Write to FPMCU from file |
| if [[ ! -f "${file}" ]]; then |
| echo "Invalid image file: ${file}" |
| return "${EXIT_PRECONDITION}" |
| fi |
| echo "# Flashing '${file}' over ${transport}" |
| cmd_flags+=" -w ${file}" |
| fi |
| elif [[ "${transport}" == "UART" ]]; then |
| local port="${device#/dev/}" |
| cmd_flags+="--port=${port} --baudrate ${FLAGS_baudrate}" |
| fi |
| |
| echo "# cmd flags= '${cmd_flags}'" |
| |
| local attempt=0 |
| local cmd_exit_status=1 |
| local cmd="" |
| |
| if [[ "${transport}" == "SPI" ]]; then |
| cmd="flashrom ${cmd_flags}" |
| elif [[ "${transport}" == "UART" ]]; then |
| # need to flash monitor first before flashing image over UART |
| cmd="uartupdatetool ${cmd_flags}" |
| |
| local cmd_flash_monitor=${cmd} |
| cmd_flash_monitor+=" --opr=wr --addr=0x200c3020" |
| |
| #TODO(b/294944527): Make sure this file is available in image |
| cmd_flash_monitor+=" \ |
| --file /usr/local/share/npcx/npcx_monitor_$(get_fp_mcu_name).bin" |
| |
| # Reset sequence to enter bootloader mode |
| gpio 0 "${gpio_nrst}" |
| sleep 0.01 |
| |
| # Release reset as the UART bus is now ready |
| gpio 1 "${gpio_nrst}" |
| |
| # Print out the actual underlying command we're running and run it |
| echo "# ${cmd_flash_monitor}" |
| ${cmd_flash_monitor} |
| cmd_exit_status=$? |
| |
| if [[ "${cmd_exit_status}" -eq 0 ]]; then |
| echo "# NPCX: Flashing monitor PASSED" |
| else |
| # No point to continue if flashing the monitor failed |
| echo "# NPCX: Flashing monitor FAILED!" |
| return "${EXIT_RUNTIME}" |
| fi |
| |
| if [[ "${FLAGS_read}" -eq "${FLAGS_TRUE}" ]]; then |
| # Read from FPMCU to file |
| cmd+=" --read-flash --file ${file}" |
| else |
| # Write to FPMCU from file |
| cmd+=" --auto --offset=0 --file ${file}" |
| fi |
| fi |
| |
| for attempt in $(seq "${FLAGS_retries}"); do |
| # Reset sequence to enter bootloader mode |
| gpio 0 "${gpio_nrst}" |
| sleep 0.01 |
| |
| # Release reset as the SPI bus is now ready |
| gpio 1 "${gpio_nrst}" |
| |
| # Keeping some margin, add delay of 100 ms to consider minimum bootloader |
| # startup time after the reset for npcx devices. |
| sleep 0.1 |
| |
| # Print out the actual underlying command we're running and run it |
| echo "# ${cmd}" |
| ${cmd} |
| cmd_exit_status=$? |
| |
| if [[ "${cmd_exit_status}" -eq 0 ]]; then |
| break |
| fi |
| echo "# Attempt ${attempt} failed." |
| echo |
| sleep 1 |
| done |
| |
| return "${cmd_exit_status}" |
| } |
| |
| flash_fp_mcu_stm32() { |
| local transport="${1}" |
| local device="${2}" |
| local gpio_nrst="${3}" |
| local file="${4}" |
| |
| local cmd_flags="-p --retries ${STM32MON_CONNECT_RETRIES}" |
| |
| if [[ "${transport}" == "UART" ]]; then |
| cmd_flags+=" --baudrate ${FLAGS_baudrate} --device ${device}" |
| else |
| cmd_flags+=" -s ${device}" |
| fi |
| |
| if [[ "${FLAGS_hello}" -eq "${FLAGS_FALSE}" ]]; then |
| if [[ "${FLAGS_remove_flash_write_protect}" -eq "${FLAGS_TRUE}" ]]; then |
| cmd_flags+=" -u" |
| fi |
| |
| if [[ "${FLAGS_remove_flash_read_protect}" -eq "${FLAGS_TRUE}" ]]; then |
| cmd_flags+=" -U" |
| fi |
| |
| if [[ "${FLAGS_read}" -eq "${FLAGS_TRUE}" ]]; then |
| # Read from FPMCU to file |
| if [[ -e "${file}" ]]; then |
| echo "Output file already exists: ${file}" |
| return "${EXIT_PRECONDITION}" |
| fi |
| echo "# Reading to '${file}' over ${transport}" |
| cmd_flags+=" -r ${file}" |
| else |
| # Write to FPMCU from file |
| if [[ ! -f "${file}" ]]; then |
| echo "Invalid image file: ${file}" |
| return "${EXIT_PRECONDITION}" |
| fi |
| echo "# Flashing '${file}' over ${transport}" |
| cmd_flags+=" -e -w ${file}" |
| fi |
| else |
| echo "# Saying hello over ${transport}" |
| fi |
| |
| local attempt=0 |
| local cmd_exit_status=1 |
| local cmd="stm32mon ${cmd_flags}" |
| |
| for attempt in $(seq "${FLAGS_retries}"); do |
| # Reset sequence to enter bootloader mode |
| gpio 0 "${gpio_nrst}" |
| sleep 0.01 |
| |
| # Release reset as the SPI bus is now ready |
| gpio 1 "${gpio_nrst}" |
| |
| # As per section '68: Bootloader timings' from application note below: |
| # https://www.st.com/resource/en/application_note/cd00167594-stm32-microcontroller-system-memory-boot-mode-stmicroelectronics.pdf |
| # bootloader startup time is 16.63 ms for STM32F74xxx/75xxx and 53.975 ms |
| # for STM32H74xxx/75xxx. SPI needs 1 us delay for one SPI byte sending. |
| # Keeping some margin, add delay of 100 ms to consider minimum bootloader |
| # startup time after the reset for stm32 devices. |
| sleep 0.1 |
| |
| # Print out the actual underlying command we're running and run it |
| echo "# ${cmd}" |
| ${cmd} |
| cmd_exit_status=$? |
| |
| if [[ "${cmd_exit_status}" -eq 0 ]]; then |
| break |
| fi |
| echo "# Attempt ${attempt} failed." |
| echo |
| sleep 1 |
| done |
| |
| return "${cmd_exit_status}" |
| } |
| |
| flash_fp_mcu_board() { |
| local transport="${1}" |
| local device="${2}" |
| local gpio_nrst="${3}" |
| local gpio_boot0="${4}" |
| local gpio_pwren="${5}" |
| local file="${6}" |
| local chip_type="${7}" |
| |
| local deviceid="" |
| |
| echo "# Flashing '${chip_type}' with '${file}' over ${transport}" |
| |
| if [[ "${transport}" == "UART" ]]; then |
| if ! deviceid="$(get_uartid)"; then |
| # Failure to get Device ID when transport is UART is not critical. |
| # Cros-ec-uart driver was built on top of serial bus. Serial bus is |
| # created on top of UART driver (in our case it's dw-apb-uart). |
| # To get /dev/ttyS1 device, we need to unbind cros-ec-uart and rebind |
| # dw-apb-uart driver which creates ttyS1, but destroyes the serial bus. |
| # If we can't get the Device ID, it means that dw-apb-uart was rebound |
| # previously. |
| echo "Unable to find FP sensor UART device: ${CROS_EC_UART_MODALIAS_STR}" |
| fi |
| else |
| if ! deviceid="$(get_spiid)"; then |
| # Failure to get Device ID when transport is SPI is critical. |
| # It means that the FPMCU is missing on the device or it's not defined |
| # in ACPI properly. |
| echo "Unable to find FP sensor SPI device: ${CROS_EC_SPI_MODALIAS_STR}" |
| return "${EXIT_PRECONDITION}" |
| fi |
| fi |
| |
| # Unbind cros-ec driver |
| if [[ -n "${deviceid}" ]]; then |
| klog "Unbinding cros-ec driver" |
| if [[ "${transport}" == "UART" ]]; then |
| echo "${deviceid}" > /sys/bus/serial/drivers/cros-ec-uart/unbind |
| else |
| echo "${deviceid}" > /sys/bus/spi/drivers/cros-ec-spi/unbind |
| fi |
| |
| echo "Flashing ${transport} device ID: ${deviceid}" |
| fi |
| |
| # Configure the MCU Boot0 and NRST GPIOs |
| gpio export "${gpio_boot0}" "${gpio_nrst}" |
| gpio out "${gpio_boot0}" "${gpio_nrst}" |
| |
| # Reset sequence to enter bootloader mode |
| gpio 1 "${gpio_boot0}" |
| gpio 0 "${gpio_nrst}" |
| sleep 0.001 |
| |
| klog "Binding raw driver" |
| if [[ "${transport}" == "UART" ]]; then |
| # load AMDI0020:01 ttyS1 |
| echo AMDI0020:01 > /sys/bus/platform/drivers/dw-apb-uart/unbind; |
| echo AMDI0020:01 > /sys/bus/platform/drivers/dw-apb-uart/bind; |
| else |
| echo spidev > "/sys/bus/spi/devices/${deviceid}/driver_override" |
| echo "${deviceid}" > /sys/bus/spi/drivers/spidev/bind |
| # The following sleep is a workaround to mitigate the effects of a |
| # poorly behaved chip select line. See b/145023809. |
| fi |
| sleep 0.5 |
| |
| # When using gpioset, we can only do this check if it's not holding the |
| # GPIO. |
| if [[ "${#GPIO_ACTIVE_PIDS[@]}" -eq 0 ]]; then |
| # We do not expect the drivers to change the pin state when binding. |
| # If you receive this warning, the driver needs to be fixed on this board |
| # and this flash attempt will probably fail. |
| warn_gpio "${gpio_boot0}" 1 \ |
| "WARNING: One of the drivers changed BOOT0 pin state on bind attempt." |
| warn_gpio "${gpio_nrst}" 0 \ |
| "WARNING: One of the drivers changed NRST pin state on bind attempt." |
| fi |
| |
| if [[ ! -c "${device}" ]]; then |
| echo "Failed to bind raw device driver." >&2 |
| return "${EXIT_RUNTIME}" |
| fi |
| |
| local cmd_exit_status=1 |
| |
| if [[ "${chip_type}" == "STM32" ]]; then |
| flash_fp_mcu_stm32 \ |
| "${transport}" \ |
| "${device}" \ |
| "${gpio_nrst}" \ |
| "${file}" |
| cmd_exit_status=$? |
| elif [[ "${chip_type}" == "NPCX" ]]; then |
| flash_fp_mcu_npcx \ |
| "${transport}" \ |
| "${device}" \ |
| "${gpio_nrst}" \ |
| "${file}" |
| cmd_exit_status=$? |
| else |
| echo "# Unsupported chip type found: ${chip_type}" |
| return "${EXIT_CONFIG}" |
| fi |
| |
| # unload device |
| if [[ "${transport}" != "UART" ]]; then |
| klog "Unbinding raw driver" |
| echo "${deviceid}" > /sys/bus/spi/drivers/spidev/unbind |
| fi |
| |
| # Go back to normal mode |
| gpio 0 "${gpio_boot0}" |
| |
| # Dartmonkey's RO has a flashprotect logic issue that forces reboot loops |
| # when SW-WP is enabled and HW-WP is disabled. It is avoided if a POR is |
| # detected on boot. We force a POR here to ensure we avoid this reboot loop. |
| # See to b/146428434. |
| if [[ "${gpio_pwren}" -gt 0 ]]; then |
| power_cycle_fpmcu "${gpio_pwren}" |
| else |
| echo "Reset the FPMCU." |
| gpio out "${gpio_nrst}" |
| gpio 0 "${gpio_nrst}" |
| # Make sure that we keep nRST line low long enough. |
| sleep 0.01 |
| gpio 1 "${gpio_nrst}" |
| fi |
| |
| # Give up GPIO control, unless we need to keep these driving as |
| # outputs because they're not open-drain signals. |
| # TODO(b/179839337): Make this the default and properly support |
| # open-drain outputs on other platforms. |
| if [[ "${PLATFORM_BASE_NAME}" != "strongbad" ]] && |
| [[ "${PLATFORM_BASE_NAME}" != "zork" ]] && |
| [[ "${PLATFORM_BASE_NAME}" != "rauru" ]] && |
| [[ "${PLATFORM_BASE_NAME}" != "skyrim" ]] && |
| [[ "${PLATFORM_BASE_NAME}" != "skywalker" ]]; then |
| gpio in "${gpio_boot0}" "${gpio_nrst}" |
| fi |
| gpio unexport "${gpio_boot0}" "${gpio_nrst}" |
| |
| # Put back cros_fp driver if transport is SPI |
| if [[ "${transport}" != "UART" ]]; then |
| # wait for FP MCU to come back up (including RWSIG delay) |
| sleep 2 |
| klog "Binding cros-ec driver" |
| echo "" > "/sys/bus/spi/devices/${deviceid}/driver_override" |
| echo "${deviceid}" > /sys/bus/spi/drivers/cros-ec-spi/bind |
| |
| # Factory expects SWWP = 0 after flash_fp_mcu, so disable it, see |
| # b/341193125. |
| if [[ "${chip_type}" == "NPCX" ]]; then |
| cmd="ectool --name=cros_fp flashprotect disable" |
| echo "# ${cmd}" |
| ${cmd} |
| cmd_exit_status=$? |
| if [[ "${cmd_exit_status}" -eq 0 ]]; then |
| echo "Failed to disable WP after flashing NPCX." |
| fi |
| fi |
| else |
| # FPMCU can still have ro_now protection (sector write protection) enabled |
| # e.g. when flash_fp_mcu was run with --read option. It is disabled when |
| # FPMCU boots with HW write protect disabled. |
| # Disabling sector write protection takes some time due to flash internal |
| # structures update. During that time we can't reset or power cycle FPMCU |
| # otherwise we will corrupt internal flash state which will brick FPMCU. |
| # This situation can occur if DUT is rebooted immediately after flash_fp_mcu |
| # finishes, so give some time to remove sector write protection. No need to |
| # wait if transport is SPI because later we query for version and reset |
| # flags. |
| sleep 3 |
| fi |
| |
| if [[ "${cmd_exit_status}" -ne 0 ]]; then |
| return "${EXIT_RUNTIME}" |
| fi |
| |
| # Inform user to reboot if transport is UART. |
| # Display fw version is transport is SPI |
| if [[ "${transport}" == "UART" ]]; then |
| echo "Please reboot this device." |
| else |
| # Test it |
| klog "Query version and reset flags" |
| ectool --name=cros_fp version |
| |
| # TODO(b/408245482): re-enable when implemented. |
| if [[ "$(os)" != "android" ]]; then |
| ectool --name=cros_fp uptimeinfo |
| fi |
| fi |
| } |
| |
| config_hatch() { |
| readonly USE_LEGACY_GPIO_API=0 |
| |
| readonly TRANSPORT="SPI" |
| readonly DEVICE="/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. |
| |
| readonly GPIO_CHIP="gpiochip0" |
| |
| # FPMCU RST_ODL is on GPP_A12 = 12 |
| readonly GPIO_NRST=12 |
| # FPMCU BOOT0 is on GPP_A22 = 22 |
| readonly GPIO_BOOT0=22 |
| # FP_PWR_EN is on GPP_C11 = 256 + 11 = 267 |
| readonly GPIO_PWREN=267 |
| } |
| |
| config_nami() { |
| readonly USE_LEGACY_GPIO_API=0 |
| |
| readonly TRANSPORT="SPI" |
| readonly DEVICE="/dev/spidev1.0" |
| |
| # See kernel/v5.4/drivers/pinctrl/intel/pinctrl-sunrisepoint.c |
| # for pin name and pin number. |
| |
| readonly GPIO_CHIP="gpiochip0" |
| |
| # FPMCU RST_ODL is on GPP_C9 = 57 |
| readonly GPIO_NRST=57 |
| # FPMCU BOOT0 is on GPP_D5 = 77 |
| readonly GPIO_BOOT0=77 |
| # FP_PWR_EN is on GPP_B11 = 35 |
| readonly GPIO_PWREN=35 |
| } |
| |
| config_nocturne() { |
| readonly USE_LEGACY_GPIO_API=0 |
| |
| readonly TRANSPORT="SPI" |
| readonly DEVICE="/dev/spidev1.0" |
| |
| # See kernel/v5.4/drivers/pinctrl/intel/pinctrl-sunrisepoint.c |
| # for pin name and pin number. |
| readonly GPIO_CHIP="gpiochip0" |
| |
| # FPMCU RST_ODL is on GPP_C10 = 58 |
| readonly GPIO_NRST=58 |
| # FPMCU BOOT0 is on GPP_C8 = 56 |
| readonly GPIO_BOOT0=56 |
| # FP_PWR_EN is on GPP_A11 = 11 |
| readonly GPIO_PWREN=11 |
| } |
| |
| config_rauru() { |
| readonly USE_LEGACY_GPIO_API=0 |
| |
| readonly TRANSPORT="SPI" |
| readonly DEVICE="/dev/spidev5.0" |
| |
| readonly GPIO_CHIP="gpiochip0" |
| # FPMCU RST_ODL is $(gpiofind FP_RST_1V8_S3_L) is gpiochip0 26 |
| GPIO_NRST=$(gpiofind FP_RST_1V8_S3_L|cut -f2 -d" ") |
| readonly GPIO_NRST |
| # FPMCU BOOT0 is $(gpiofind AP_FP_FW_UP_STRAP) is gpiochip0 27 |
| GPIO_BOOT0=$(gpiofind AP_FP_FW_UP_STRAP|cut -f2 -d" ") |
| readonly GPIO_BOOT0 |
| # EN FP RAILS is $(gpiofind EN_PWR_FP) is gpiochip0 173 |
| GPIO_PWREN=$(gpiofind EN_PWR_FP|cut -f2 -d" ") |
| readonly GPIO_PWREN |
| } |
| |
| config_strongbad() { |
| readonly USE_LEGACY_GPIO_API=0 |
| |
| readonly TRANSPORT="SPI" |
| readonly DEVICE="/dev/spidev10.0" |
| |
| readonly GPIO_CHIP="gpiochip0" |
| # FPMCU RST_ODL is $(gpiofind FP_RST_L) is gpiochip0 22 |
| GPIO_NRST=$(gpiofind FP_RST_L|cut -f2 -d" ") |
| readonly GPIO_NRST |
| # FPMCU BOOT0 is $(gpiofind FPMCU_BOOT0) is gpiochip0 10 |
| GPIO_BOOT0=$(gpiofind FPMCU_BOOT0|cut -f2 -d" ") |
| readonly GPIO_BOOT0 |
| |
| # EN FP RAILS is $(gpiofind EN_FP_RAILS) is gpiochip0 74 (if available) |
| local en_fp_rails |
| if en_fp_rails=$(gpiofind EN_FP_RAILS); then |
| GPIO_PWREN=$(echo "${en_fp_rails}"|cut -f2 -d" ") |
| readonly GPIO_PWREN |
| else |
| readonly GPIO_PWREN=-1 |
| fi |
| } |
| |
| config_volteer() { |
| readonly USE_LEGACY_GPIO_API=0 |
| |
| readonly TRANSPORT="SPI" |
| readonly DEVICE="/dev/spidev1.0" |
| |
| # See kernel/v5.4/drivers/pinctrl/intel/pinctrl-tigerlake.c |
| # for pin name and pin number. |
| # Examine `cat /sys/kernel/debug/pinctrl/INT34C5:00/gpio-ranges` on a |
| # volteer device to determine gpio number from pin number. |
| # For example: GPP_C23 is UART2_CTS which can be queried from EDS |
| # the pin number is 194. From the gpio-ranges, the gpio value is |
| # 920 + (194-171) = 943. The relative gpio value is 943 - 664 = 279. |
| |
| readonly GPIO_CHIP="gpiochip0" |
| |
| # FPMCU RST_ODL is on GPP_C23 = 256 + (194 - 171) = 279 |
| readonly GPIO_NRST=279 |
| # FPMCU BOOT0 is on GPP_C22 = 256 + (193 - 171) = 278 |
| readonly GPIO_BOOT0=278 |
| # FP_PWR_EN is on GPP_A21 = 64 + (63 - 42) = 85 |
| readonly GPIO_PWREN=85 |
| } |
| |
| config_brya() { |
| readonly USE_LEGACY_GPIO_API=0 |
| |
| readonly TRANSPORT="SPI" |
| |
| DEVICE="/dev/spidev0.0" |
| |
| if [[ "$(os)" == "android" ]]; then |
| DEVICE="/dev/spidev1.0" |
| fi |
| readonly DEVICE |
| |
| # See kernel/v5.10/drivers/pinctrl/intel/pinctrl-tigerlake.c |
| # for pin name and pin number. |
| # Examine `cat /sys/kernel/debug/pinctrl/INTC1055:00/gpio-ranges` on a |
| # brya device to determine gpio number from pin number. |
| # For example: GPP_D1 is ISH_GP_1 which can be queried from EDS |
| # the pin number is 100 from the pinctrl-tigerlake.c. |
| # From the gpio-ranges, the gpio value is 824 + (100-99) = 825 |
| |
| readonly GPIO_CHIP="gpiochip0" |
| |
| # FPMCU RST_ODL is on GPP_D1 = 160 + (100 - 99) = 161 |
| readonly GPIO_NRST=161 |
| # FPMCU BOOT0 is on GPP_D0 = 160 + (99 - 99) = 160 |
| readonly GPIO_BOOT0=160 |
| # FP_PWR_EN is on GPP_D2 = 160 + (101 - 99) = 162 |
| readonly GPIO_PWREN=162 |
| } |
| |
| config_brask() { |
| # Let's call the config_brya since brask follows the brya HW design |
| config_brya |
| } |
| |
| config_ghost() { |
| # No changes to brya. |
| config_brya |
| } |
| |
| config_rex() { |
| readonly USE_LEGACY_GPIO_API=0 |
| |
| readonly TRANSPORT="SPI" |
| readonly DEVICE="/dev/spidev0.0" |
| |
| # See kernel/v6.1/drivers/pinctrl/intel/pinctrl-meteorlake.c |
| # for pin name and pin number. |
| # Examine `cat /sys/kernel/debug/pinctrl/INTC1083:00/gpio-ranges` on a |
| # rex device to determine gpio number from pin number. |
| # For example: GPP_B11, the pin number is 215 from the pinctrl-meteorlake.c. |
| # From the gpio-ranges, the gpio value is 925 + (215-204) = 936 |
| |
| readonly GPIO_CHIP="gpiochip0" |
| |
| local model |
| model=$(cros_config / name) |
| case "${model}" in |
| (screebo | screebo4es) |
| # Screebo is based off Rex but with different pins for FPMCU |
| # FPMCU RST_ODL is on GPP_C21 = 64 + (50 - 29) = 85 |
| readonly GPIO_NRST=85 |
| # FPMCU BOOT0 is on GPP_C20 = 64 + (49 - 29) = 84 |
| readonly GPIO_BOOT0=84 |
| # FP_PWR_EN is on GPP_B08 = 352 + (212-204) - 573 = 360 |
| readonly GPIO_PWREN=360 |
| ;; |
| *) |
| # FPMCU RST_ODL is on GPP_C23 = 64 + (52 - 29) = 87 |
| readonly GPIO_NRST=87 |
| # FPMCU BOOT0 is on GPP_C22 = 64 + (51 - 29) = 86 |
| readonly GPIO_BOOT0=86 |
| # FP_PWR_EN is on GPP_B11 = 352 + (215-204) = 363 |
| readonly GPIO_PWREN=363 |
| ;; |
| esac |
| } |
| |
| config_zork() { |
| readonly USE_LEGACY_GPIO_API=0 |
| |
| readonly TRANSPORT="UART" |
| readonly DEVICE="/dev/ttyS1" |
| |
| readonly GPIO_CHIP="gpiochip0" |
| |
| # FPMCU RST_ODL is on AGPIO 11 |
| readonly GPIO_NRST=11 |
| # FPMCU BOOT0 is on AGPIO 69 |
| readonly GPIO_BOOT0=69 |
| # FPMCU PWR_EN is on AGPIO 32 |
| readonly GPIO_PWREN=32 |
| } |
| |
| config_guybrush() { |
| readonly USE_LEGACY_GPIO_API=0 |
| |
| readonly TRANSPORT="UART" |
| readonly DEVICE="/dev/ttyS1" |
| |
| readonly GPIO_CHIP="gpiochip0" |
| |
| # FPMCU RST_ODL is on AGPIO 11 |
| readonly GPIO_NRST=11 |
| # FPMCU BOOT0 is on AGPIO 144 |
| readonly GPIO_BOOT0=144 |
| # FPMCU PWR_EN is on AGPIO 3 |
| readonly GPIO_PWREN=3 |
| } |
| |
| config_skyrim() { |
| readonly USE_LEGACY_GPIO_API=0 |
| |
| readonly TRANSPORT="UART" |
| readonly DEVICE="/dev/ttyS1" |
| |
| readonly GPIO_CHIP="gpiochip0" |
| |
| # FPMCU RST_ODL is on AGPIO 12 |
| readonly GPIO_NRST=12 |
| # FPMCU BOOT0 is on AGPIO 130 |
| readonly GPIO_BOOT0=130 |
| # FPMCU PWR_EN is on AGPIO 4 |
| readonly GPIO_PWREN=4 |
| } |
| |
| config_brox() { |
| readonly USE_LEGACY_GPIO_API=0 |
| |
| readonly TRANSPORT="SPI" |
| readonly DEVICE="/dev/spidev0.0" |
| |
| # Raptorlake uses Alderlake-P PCH and hence |
| # exports INTC1055 ACPI ID matching with tigerlake-pinctrl.c |
| # See kernel/v6.6/drivers/pinctrl/intel/pinctrl-tigerlake.c |
| # for pin name and pin number. |
| # Examine `cat /sys/kernel/debug/pinctrl/INTC1055:00/gpio-ranges` on a |
| # lotso device to determine gpio number from pin number. |
| # For example: GPP_E21, the pin number is 247 from the pinctrl-tigerlake.c. |
| # From the gpio-ranges, the gpio value is 832 + (247-226) = 853 |
| |
| readonly GPIO_CHIP="gpiochip0" |
| |
| local model |
| model=$(cros_config / name) |
| case "${model}" in |
| (lotso) |
| # FPMCU RST_ODL is on GPP_E21 = 320 + 21 = 341 |
| readonly GPIO_NRST=341 |
| # FPMCU BOOT0 is on GPP_D9 = 160 + 9 = 169 |
| readonly GPIO_BOOT0=169 |
| # FP_PWR_EN is on GPP_E20 = 320 + 20 = 340 |
| readonly GPIO_PWREN=340 |
| ;; |
| (jubilant|jubileum) |
| # FPMCU RST_ODL is on GPP_D15 = 160 + 15 = 175 |
| readonly GPIO_NRST=175 |
| # FPMCU BOOT0 is on GPP_D3 = 160 + 3 = 163 |
| readonly GPIO_BOOT0=163 |
| # FP_PWR_EN is on GPP_D2 = 160 + 2 = 162 |
| readonly GPIO_PWREN=162 |
| ;; |
| (caboc) |
| # FPMCU RST_ODL is on GPP_D9 = 160 + 9 = 169 |
| readonly GPIO_NRST=169 |
| # FPMCU BOOT0 is on GPP_D8 = 160 + 8 = 168 |
| readonly GPIO_BOOT0=168 |
| # FP_PWR_EN is on GPP_D11 = 160 + 11 = 171 |
| readonly GPIO_PWREN=171 |
| ;; |
| *) |
| echo "FPMCU not supported on model ${model}" |
| return "${EXIT_PRECONDITION}" |
| ;; |
| esac |
| } |
| |
| config_fatcat() { |
| readonly USE_LEGACY_GPIO_API=0 |
| |
| readonly TRANSPORT="SPI" |
| readonly DEVICE="/dev/spidev0.0" |
| |
| # Examine `cat /sys/kernel/debug/pinctrl/INTC1083:00/gpio-ranges` on a |
| # fatcat device to determine gpio number from pin number. |
| |
| readonly GPIO_CHIP="gpiochip0" |
| |
| local model |
| model=$(cros_config / name) |
| |
| |
| case "${model}" in |
| (francka) |
| # FPMCU FPS_RST_N(RST_ODL) is on GPP_H17 = 626 + 17 |
| readonly GPIO_NRST=17 |
| |
| # FPMCU FPMCU_FW_UPDATE(BOOT0) is on GPP_F20 = 560 + 20 |
| readonly GPIO_BOOT0=20 |
| |
| # FPMCU_PWREN(FP_PWR_EN) is on GPP_H03 = 626 + 3 |
| readonly GPIO_PWREN=3 |
| ;; |
| *) |
| # FPMCU FPS_RST_N(RST_ODL) is on GPP_C15 = 24 + (39 - 24) = 39 |
| readonly GPIO_NRST=39 |
| # FPMCU FPMCU_FW_UPDATE(BOOT0) is on GPP_E20 = 26 + (46 - 26) = 46 |
| readonly GPIO_BOOT0=46 |
| # FPMCU_PWREN(FP_PWR_EN) is on GPP_E19 = 26 + (45 - 26) = 45 |
| readonly GPIO_PWREN=45 |
| ;; |
| esac |
| } |
| |
| config_nissa() { |
| readonly USE_LEGACY_GPIO_API=0 |
| |
| readonly TRANSPORT="SPI" |
| readonly DEVICE="/dev/spidev1.0" |
| |
| # Twinlake uses Alderlake-P PCH and hence |
| # exports INTC1057 ACPI ID matching with pinctrl-alderlake.c |
| # See kernel/v6.6/drivers/pinctrl/intel/pinctrl-alderlake.c |
| # for pin name and pin number. |
| # Examine `cat /sys/kernel/debug/pinctrl/INTC1057:00/gpio-ranges` on a |
| # pujjoniru device to determine gpio number from pin number. |
| # For example: GPP_E7, the pin number is 231 from the pinctrl-alderlake.c. |
| # From the gpio-ranges, the gpio value is 320 + (231 - 224) = 327 |
| |
| readonly GPIO_CHIP="gpiochip0" |
| |
| local model |
| model=$(cros_config / name) |
| case "${model}" in |
| (pujjoniru | pujjoquince) |
| # FPMCU RST_ODL is on GPP_E7 = 320 + (231 - 224) = 327 |
| readonly GPIO_NRST=327 |
| # FPMCU BOOT0 is on GPP_D0 = 192 + (119 - 119) = 192 |
| readonly GPIO_BOOT0=192 |
| # FP_PWR_EN is on GPP_D2 = 192 + (121-119) = 194 |
| readonly GPIO_PWREN=194 |
| ;; |
| *) |
| echo "FPMCU not supported on model ${model}" |
| return "${EXIT_PRECONDITION}" |
| ;; |
| esac |
| } |
| |
| config_skywalker() { |
| readonly USE_LEGACY_GPIO_API=0 |
| |
| readonly TRANSPORT="SPI" |
| readonly DEVICE="/dev/spidev1.0" |
| |
| readonly GPIO_CHIP="gpiochip0" |
| # FPMCU RST_ODL is $(gpiofind FP_RST_1V8_S3_L) is gpiochip0 134 |
| GPIO_NRST=$(gpiofind FP_RST_1V8_S3_L|cut -f2 -d" ") |
| readonly GPIO_NRST |
| # FPMCU BOOT0 is $(gpiofind AP_FP_FW_UP_STRAP) is gpiochip0 133 |
| GPIO_BOOT0=$(gpiofind AP_FP_FW_UP_STRAP|cut -f2 -d" ") |
| readonly GPIO_BOOT0 |
| # EN FP RAILS is $(gpiofind EN_PWR_FP) is gpiochip0 12 |
| GPIO_PWREN=$(gpiofind EN_PWR_FP|cut -f2 -d" ") |
| readonly GPIO_PWREN |
| } |
| |
| main() { |
| local filename="$1" |
| local chip_type |
| |
| # print out canonical path to differentiate between /usr/local/bin and |
| # /usr/bin installs |
| readlink -f "$0" |
| |
| echo "OS: $(os)" |
| |
| if [[ "$(os)" == "android" ]]; then |
| USE_LEGACY_GPIO_API=0 |
| readonly GPIOSET_SUPPORTS_HOLD=0 |
| else |
| USE_LEGACY_GPIO_API=1 |
| readonly GPIOSET_SUPPORTS_HOLD=1 |
| fi |
| |
| # 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>). |
| if ! PLATFORM_NAME="$(get_platform_name)"; then |
| echo "Failed to get platform name" |
| exit "${EXIT_CONFIG}" |
| fi |
| readonly PLATFORM_NAME |
| |
| if ! PLATFORM_BASE_NAME="$(platform_base_name "${PLATFORM_NAME}")"; then |
| echo "Failed to get platform base name" |
| exit "${EXIT_CONFIG}" |
| fi |
| readonly PLATFORM_BASE_NAME |
| |
| echo "Platform name is ${PLATFORM_NAME} (${PLATFORM_BASE_NAME})." |
| |
| # Check that the config function exists |
| if is_function "config_${PLATFORM_NAME}"; then |
| readonly PLATFORM_CONFIG="${PLATFORM_NAME}" |
| elif is_function "config_${PLATFORM_BASE_NAME}"; then |
| readonly PLATFORM_CONFIG="${PLATFORM_BASE_NAME}" |
| else |
| echo "No config for platform ${PLATFORM_NAME}." >&2 |
| exit "${EXIT_CONFIG}" |
| fi |
| |
| echo "Using config for ${PLATFORM_CONFIG}." |
| |
| if ! "config_${PLATFORM_CONFIG}"; then |
| echo "Configuration failed for platform ${PLATFORM_CONFIG}." >&2 |
| exit "${EXIT_CONFIG}" |
| fi |
| |
| # Help the user out with defaults, if no *file* was given. |
| if [[ "$#" -eq 0 ]]; then |
| # If we are actually reading, set to a timestamped temp file. |
| if [[ "${FLAGS_read}" -eq "${FLAGS_TRUE}" ]]; then |
| filename="/tmp/fpmcu-fw-$(date --iso-8601=seconds).bin" |
| else |
| # Assume we are "writing" the default firmware to the FPMCU. |
| if ! filename="$(get_default_fw)"; then |
| echo "Failed to identify a default firmware file" >&2 |
| exit "${EXIT_CONFIG}" |
| fi |
| fi |
| fi |
| |
| # Check that the gpiochip exists |
| if ! gpio_chip_exists; then |
| echo "Cannot find GPIO chip: ${GPIO_CHIP}" |
| exit "${EXIT_CONFIG}" |
| fi |
| |
| # Ensure spidev module is loaded. |
| check_spidev |
| |
| # Ensure hardware write-protect is disabled. |
| check_hardware_write_protect_disabled |
| |
| stop_services |
| |
| # If cros-ec driver isn't bound on startup, this means the final rebinding |
| # may fail. |
| if [[ ! -c "/dev/cros_fp" ]]; then |
| echo "WARNING: The cros-ec driver was not bound on startup." >&2 |
| fi |
| if [[ -c "${DEVICE}" ]]; then |
| echo "WARNING: The raw driver was bound on startup." >&2 |
| fi |
| local files_open |
| # Ensure no processes have cros_fp device or debug device open. |
| if files_open=$(proc_open_file "/dev/cros_fp|/sys/kernel/debug/cros_fp/.*") |
| then |
| echo "WARNING: Another process has a cros_fp device file open." >&2 |
| echo "${files_open}" >&2 |
| echo_service_warning |
| echo "See b/188985272." >&2 |
| fi |
| # Ensure no processes are using the raw driver. This might be a wedged |
| # stm32mon process spawned by this script. |
| if files_open=$(proc_open_file "${DEVICE}"); then |
| echo "WARNING: Another process has ${DEVICE} open." >&2 |
| echo "${files_open}" >&2 |
| echo "Try 'fuser -k ${DEVICE}' before running this script." >&2 |
| echo "See b/188985272." >&2 |
| fi |
| |
| if ! chip_type="$(get_chip)"; then |
| echo "Failed to get chip type." >&2 |
| exit "${EXIT_CONFIG}" |
| fi |
| echo "Detected chip ${chip_type}." |
| |
| local ret |
| flash_fp_mcu_board \ |
| "${TRANSPORT}" \ |
| "${DEVICE}" \ |
| "${GPIO_NRST}" \ |
| "${GPIO_BOOT0}" \ |
| "${GPIO_PWREN}" \ |
| "${filename}" \ |
| "${chip_type}" |
| ret=$? |
| |
| start_services |
| |
| exit "${ret}" |
| } |
| |
| main "$@" |