Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/net/wireless/ath/ath10k/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 99 kB image not shown  

Quelle  core.c   Sprache: C

 
// SPDX-License-Identifier: ISC
/*
 * Copyright (c) 2005-2011 Atheros Communications Inc.
 * Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
 * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
 * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
 * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
 */


#include <linux/export.h>
#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/of.h>
#include <linux/property.h>
#include <linux/dmi.h>
#include <linux/ctype.h>
#include <linux/pm_qos.h>
#include <linux/nvmem-consumer.h>
#include <asm/byteorder.h>

#include "core.h"
#include "mac.h"
#include "htc.h"
#include "hif.h"
#include "wmi.h"
#include "bmi.h"
#include "debug.h"
#include "htt.h"
#include "testmode.h"
#include "wmi-ops.h"
#include "coredump.h"
#include "leds.h"

unsigned int ath10k_debug_mask;
EXPORT_SYMBOL(ath10k_debug_mask);

static unsigned int ath10k_cryptmode_param;
static bool uart_print;
static bool skip_otp;
static bool fw_diag_log;

/* frame mode values are mapped as per enum ath10k_hw_txrx_mode */
unsigned int ath10k_frame_mode = ATH10K_HW_TXRX_NATIVE_WIFI;

unsigned long ath10k_coredump_mask = BIT(ATH10K_FW_CRASH_DUMP_REGISTERS) |
         BIT(ATH10K_FW_CRASH_DUMP_CE_DATA);

/* FIXME: most of these should be readonly */
module_param_named(debug_mask, ath10k_debug_mask, uint, 0644);
module_param_named(cryptmode, ath10k_cryptmode_param, uint, 0644);
module_param(uart_print, bool, 0644);
module_param(skip_otp, bool, 0644);
module_param(fw_diag_log, bool, 0644);
module_param_named(frame_mode, ath10k_frame_mode, uint, 0644);
module_param_named(coredump_mask, ath10k_coredump_mask, ulong, 0444);

MODULE_PARM_DESC(debug_mask, "Debugging mask");
MODULE_PARM_DESC(uart_print, "Uart target debugging");
MODULE_PARM_DESC(skip_otp, "Skip otp failure for calibration in testmode");
MODULE_PARM_DESC(cryptmode, "Crypto mode: 0-hardware, 1-software");
MODULE_PARM_DESC(frame_mode,
   "Datapath frame mode (0: raw, 1: native wifi (default), 2: ethernet)");
MODULE_PARM_DESC(coredump_mask, "Bitfield of what to include in firmware crash file");
MODULE_PARM_DESC(fw_diag_log, "Diag based fw log debugging");

static const struct ath10k_hw_params ath10k_hw_params_list[] = {
 {
  .id = QCA988X_HW_2_0_VERSION,
  .dev_id = QCA988X_2_0_DEVICE_ID,
  .bus = ATH10K_BUS_PCI,
  .name = "qca988x hw2.0",
  .patch_load_addr = QCA988X_HW_2_0_PATCH_LOAD_ADDR,
  .uart_pin = 7,
  .led_pin = 1,
  .cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_ALL,
  .otp_exe_param = 0,
  .channel_counters_freq_hz = 88000,
  .max_probe_resp_desc_thres = 0,
  .cal_data_len = 2116,
  .fw = {
   .dir = QCA988X_HW_2_0_FW_DIR,
   .board_size = QCA988X_BOARD_DATA_SZ,
   .board_ext_size = QCA988X_BOARD_EXT_DATA_SZ,
  },
  .rx_desc_ops = &qca988x_rx_desc_ops,
  .hw_ops = &qca988x_ops,
  .decap_align_bytes = 4,
  .spectral_bin_discard = 0,
  .spectral_bin_offset = 0,
  .vht160_mcs_rx_highest = 0,
  .vht160_mcs_tx_highest = 0,
  .n_cipher_suites = 8,
  .ast_skid_limit = 0x10,
  .num_wds_entries = 0x20,
  .target_64bit = false,
  .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
  .shadow_reg_support = false,
  .rri_on_ddr = false,
  .hw_filter_reset_required = true,
  .fw_diag_ce_download = false,
  .credit_size_workaround = false,
  .tx_stats_over_pktlog = true,
  .dynamic_sar_support = false,
  .hw_restart_disconnect = false,
  .use_fw_tx_credits = true,
  .delay_unmap_buffer = false,
  .mcast_frame_registration = false,
 },
 {
  .id = QCA988X_HW_2_0_VERSION,
  .dev_id = QCA988X_2_0_DEVICE_ID_UBNT,
  .name = "qca988x hw2.0 ubiquiti",
  .patch_load_addr = QCA988X_HW_2_0_PATCH_LOAD_ADDR,
  .uart_pin = 7,
  .led_pin = 0,
  .cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_ALL,
  .otp_exe_param = 0,
  .channel_counters_freq_hz = 88000,
  .max_probe_resp_desc_thres = 0,
  .cal_data_len = 2116,
  .fw = {
   .dir = QCA988X_HW_2_0_FW_DIR,
   .board_size = QCA988X_BOARD_DATA_SZ,
   .board_ext_size = QCA988X_BOARD_EXT_DATA_SZ,
  },
  .rx_desc_ops = &qca988x_rx_desc_ops,
  .hw_ops = &qca988x_ops,
  .decap_align_bytes = 4,
  .spectral_bin_discard = 0,
  .spectral_bin_offset = 0,
  .vht160_mcs_rx_highest = 0,
  .vht160_mcs_tx_highest = 0,
  .n_cipher_suites = 8,
  .ast_skid_limit = 0x10,
  .num_wds_entries = 0x20,
  .target_64bit = false,
  .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
  .shadow_reg_support = false,
  .rri_on_ddr = false,
  .hw_filter_reset_required = true,
  .fw_diag_ce_download = false,
  .credit_size_workaround = false,
  .tx_stats_over_pktlog = true,
  .dynamic_sar_support = false,
  .hw_restart_disconnect = false,
  .use_fw_tx_credits = true,
  .delay_unmap_buffer = false,
  .mcast_frame_registration = false,
 },
 {
  .id = QCA9887_HW_1_0_VERSION,
  .dev_id = QCA9887_1_0_DEVICE_ID,
  .bus = ATH10K_BUS_PCI,
  .name = "qca9887 hw1.0",
  .patch_load_addr = QCA9887_HW_1_0_PATCH_LOAD_ADDR,
  .uart_pin = 7,
  .led_pin = 1,
  .cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_ALL,
  .otp_exe_param = 0,
  .channel_counters_freq_hz = 88000,
  .max_probe_resp_desc_thres = 0,
  .cal_data_len = 2116,
  .fw = {
   .dir = QCA9887_HW_1_0_FW_DIR,
   .board_size = QCA9887_BOARD_DATA_SZ,
   .board_ext_size = QCA9887_BOARD_EXT_DATA_SZ,
  },
  .rx_desc_ops = &qca988x_rx_desc_ops,
  .hw_ops = &qca988x_ops,
  .decap_align_bytes = 4,
  .spectral_bin_discard = 0,
  .spectral_bin_offset = 0,
  .vht160_mcs_rx_highest = 0,
  .vht160_mcs_tx_highest = 0,
  .n_cipher_suites = 8,
  .ast_skid_limit = 0x10,
  .num_wds_entries = 0x20,
  .target_64bit = false,
  .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
  .shadow_reg_support = false,
  .rri_on_ddr = false,
  .hw_filter_reset_required = true,
  .fw_diag_ce_download = false,
  .credit_size_workaround = false,
  .tx_stats_over_pktlog = false,
  .dynamic_sar_support = false,
  .hw_restart_disconnect = false,
  .use_fw_tx_credits = true,
  .delay_unmap_buffer = false,
  .mcast_frame_registration = false,
 },
 {
  .id = QCA6174_HW_3_2_VERSION,
  .dev_id = QCA6174_3_2_DEVICE_ID,
  .bus = ATH10K_BUS_SDIO,
  .name = "qca6174 hw3.2 sdio",
  .patch_load_addr = QCA6174_HW_3_0_PATCH_LOAD_ADDR,
  .uart_pin = 19,
  .led_pin = 0,
  .otp_exe_param = 0,
  .channel_counters_freq_hz = 88000,
  .max_probe_resp_desc_thres = 0,
  .cal_data_len = 0,
  .fw = {
   .dir = QCA6174_HW_3_0_FW_DIR,
   .board_size = QCA6174_BOARD_DATA_SZ,
   .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ,
  },
  .rx_desc_ops = &qca988x_rx_desc_ops,
  .hw_ops = &qca6174_sdio_ops,
  .hw_clk = qca6174_clk,
  .target_cpu_freq = 176000000,
  .decap_align_bytes = 4,
  .n_cipher_suites = 8,
  .num_peers = 10,
  .ast_skid_limit = 0x10,
  .num_wds_entries = 0x20,
  .uart_pin_workaround = true,
  .tx_stats_over_pktlog = false,
  .credit_size_workaround = false,
  .bmi_large_size_download = true,
  .supports_peer_stats_info = true,
  .dynamic_sar_support = true,
  .hw_restart_disconnect = false,
  .use_fw_tx_credits = true,
  .delay_unmap_buffer = false,
  .mcast_frame_registration = false,
 },
 {
  .id = QCA6174_HW_2_1_VERSION,
  .dev_id = QCA6164_2_1_DEVICE_ID,
  .bus = ATH10K_BUS_PCI,
  .name = "qca6164 hw2.1",
  .patch_load_addr = QCA6174_HW_2_1_PATCH_LOAD_ADDR,
  .uart_pin = 6,
  .led_pin = 0,
  .otp_exe_param = 0,
  .channel_counters_freq_hz = 88000,
  .max_probe_resp_desc_thres = 0,
  .cal_data_len = 8124,
  .fw = {
   .dir = QCA6174_HW_2_1_FW_DIR,
   .board_size = QCA6174_BOARD_DATA_SZ,
   .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ,
  },
  .rx_desc_ops = &qca988x_rx_desc_ops,
  .hw_ops = &qca988x_ops,
  .decap_align_bytes = 4,
  .spectral_bin_discard = 0,
  .spectral_bin_offset = 0,
  .vht160_mcs_rx_highest = 0,
  .vht160_mcs_tx_highest = 0,
  .n_cipher_suites = 8,
  .ast_skid_limit = 0x10,
  .num_wds_entries = 0x20,
  .target_64bit = false,
  .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
  .shadow_reg_support = false,
  .rri_on_ddr = false,
  .hw_filter_reset_required = true,
  .fw_diag_ce_download = false,
  .credit_size_workaround = false,
  .tx_stats_over_pktlog = false,
  .dynamic_sar_support = false,
  .hw_restart_disconnect = false,
  .use_fw_tx_credits = true,
  .delay_unmap_buffer = false,
  .mcast_frame_registration = false,
 },
 {
  .id = QCA6174_HW_2_1_VERSION,
  .dev_id = QCA6174_2_1_DEVICE_ID,
  .bus = ATH10K_BUS_PCI,
  .name = "qca6174 hw2.1",
  .patch_load_addr = QCA6174_HW_2_1_PATCH_LOAD_ADDR,
  .uart_pin = 6,
  .led_pin = 0,
  .otp_exe_param = 0,
  .channel_counters_freq_hz = 88000,
  .max_probe_resp_desc_thres = 0,
  .cal_data_len = 8124,
  .fw = {
   .dir = QCA6174_HW_2_1_FW_DIR,
   .board_size = QCA6174_BOARD_DATA_SZ,
   .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ,
  },
  .rx_desc_ops = &qca988x_rx_desc_ops,
  .hw_ops = &qca988x_ops,
  .decap_align_bytes = 4,
  .spectral_bin_discard = 0,
  .spectral_bin_offset = 0,
  .vht160_mcs_rx_highest = 0,
  .vht160_mcs_tx_highest = 0,
  .n_cipher_suites = 8,
  .ast_skid_limit = 0x10,
  .num_wds_entries = 0x20,
  .target_64bit = false,
  .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
  .shadow_reg_support = false,
  .rri_on_ddr = false,
  .hw_filter_reset_required = true,
  .fw_diag_ce_download = false,
  .credit_size_workaround = false,
  .tx_stats_over_pktlog = false,
  .dynamic_sar_support = false,
  .hw_restart_disconnect = false,
  .use_fw_tx_credits = true,
  .delay_unmap_buffer = false,
  .mcast_frame_registration = false,
 },
 {
  .id = QCA6174_HW_3_0_VERSION,
  .dev_id = QCA6174_2_1_DEVICE_ID,
  .bus = ATH10K_BUS_PCI,
  .name = "qca6174 hw3.0",
  .patch_load_addr = QCA6174_HW_3_0_PATCH_LOAD_ADDR,
  .uart_pin = 6,
  .led_pin = 0,
  .otp_exe_param = 0,
  .channel_counters_freq_hz = 88000,
  .max_probe_resp_desc_thres = 0,
  .cal_data_len = 8124,
  .fw = {
   .dir = QCA6174_HW_3_0_FW_DIR,
   .board_size = QCA6174_BOARD_DATA_SZ,
   .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ,
  },
  .rx_desc_ops = &qca988x_rx_desc_ops,
  .hw_ops = &qca988x_ops,
  .decap_align_bytes = 4,
  .spectral_bin_discard = 0,
  .spectral_bin_offset = 0,
  .vht160_mcs_rx_highest = 0,
  .vht160_mcs_tx_highest = 0,
  .n_cipher_suites = 8,
  .ast_skid_limit = 0x10,
  .num_wds_entries = 0x20,
  .target_64bit = false,
  .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
  .shadow_reg_support = false,
  .rri_on_ddr = false,
  .hw_filter_reset_required = true,
  .fw_diag_ce_download = false,
  .credit_size_workaround = false,
  .tx_stats_over_pktlog = false,
  .dynamic_sar_support = false,
  .hw_restart_disconnect = false,
  .use_fw_tx_credits = true,
  .delay_unmap_buffer = false,
  .mcast_frame_registration = false,
 },
 {
  .id = QCA6174_HW_3_2_VERSION,
  .dev_id = QCA6174_2_1_DEVICE_ID,
  .bus = ATH10K_BUS_PCI,
  .name = "qca6174 hw3.2",
  .patch_load_addr = QCA6174_HW_3_0_PATCH_LOAD_ADDR,
  .uart_pin = 6,
  .led_pin = 0,
  .otp_exe_param = 0,
  .channel_counters_freq_hz = 88000,
  .max_probe_resp_desc_thres = 0,
  .cal_data_len = 8124,
  .fw = {
   /* uses same binaries as hw3.0 */
   .dir = QCA6174_HW_3_0_FW_DIR,
   .board_size = QCA6174_BOARD_DATA_SZ,
   .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ,
  },
  .rx_desc_ops = &qca988x_rx_desc_ops,
  .hw_ops = &qca6174_ops,
  .hw_clk = qca6174_clk,
  .target_cpu_freq = 176000000,
  .decap_align_bytes = 4,
  .spectral_bin_discard = 0,
  .spectral_bin_offset = 0,
  .vht160_mcs_rx_highest = 0,
  .vht160_mcs_tx_highest = 0,
  .n_cipher_suites = 8,
  .ast_skid_limit = 0x10,
  .num_wds_entries = 0x20,
  .target_64bit = false,
  .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
  .shadow_reg_support = false,
  .rri_on_ddr = false,
  .hw_filter_reset_required = true,
  .fw_diag_ce_download = true,
  .credit_size_workaround = false,
  .tx_stats_over_pktlog = false,
  .supports_peer_stats_info = true,
  .dynamic_sar_support = true,
  .hw_restart_disconnect = false,
  .use_fw_tx_credits = true,
  .delay_unmap_buffer = false,
  .mcast_frame_registration = true,
 },
 {
  .id = QCA99X0_HW_2_0_DEV_VERSION,
  .dev_id = QCA99X0_2_0_DEVICE_ID,
  .bus = ATH10K_BUS_PCI,
  .name = "qca99x0 hw2.0",
  .patch_load_addr = QCA99X0_HW_2_0_PATCH_LOAD_ADDR,
  .uart_pin = 7,
  .led_pin = 17,
  .otp_exe_param = 0x00000700,
  .continuous_frag_desc = true,
  .cck_rate_map_rev2 = true,
  .channel_counters_freq_hz = 150000,
  .max_probe_resp_desc_thres = 24,
  .tx_chain_mask = 0xf,
  .rx_chain_mask = 0xf,
  .max_spatial_stream = 4,
  .cal_data_len = 12064,
  .fw = {
   .dir = QCA99X0_HW_2_0_FW_DIR,
   .board_size = QCA99X0_BOARD_DATA_SZ,
   .board_ext_size = QCA99X0_BOARD_EXT_DATA_SZ,
  },
  .sw_decrypt_mcast_mgmt = true,
  .rx_desc_ops = &qca99x0_rx_desc_ops,
  .hw_ops = &qca99x0_ops,
  .decap_align_bytes = 1,
  .spectral_bin_discard = 4,
  .spectral_bin_offset = 0,
  .vht160_mcs_rx_highest = 0,
  .vht160_mcs_tx_highest = 0,
  .n_cipher_suites = 11,
  .ast_skid_limit = 0x10,
  .num_wds_entries = 0x20,
  .target_64bit = false,
  .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
  .shadow_reg_support = false,
  .rri_on_ddr = false,
  .hw_filter_reset_required = true,
  .fw_diag_ce_download = false,
  .credit_size_workaround = false,
  .tx_stats_over_pktlog = false,
  .dynamic_sar_support = false,
  .hw_restart_disconnect = false,
  .use_fw_tx_credits = true,
  .delay_unmap_buffer = false,
  .mcast_frame_registration = false,
 },
 {
  .id = QCA9984_HW_1_0_DEV_VERSION,
  .dev_id = QCA9984_1_0_DEVICE_ID,
  .bus = ATH10K_BUS_PCI,
  .name = "qca9984/qca9994 hw1.0",
  .patch_load_addr = QCA9984_HW_1_0_PATCH_LOAD_ADDR,
  .uart_pin = 7,
  .led_pin = 17,
  .cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_EACH,
  .otp_exe_param = 0x00000700,
  .continuous_frag_desc = true,
  .cck_rate_map_rev2 = true,
  .channel_counters_freq_hz = 150000,
  .max_probe_resp_desc_thres = 24,
  .tx_chain_mask = 0xf,
  .rx_chain_mask = 0xf,
  .max_spatial_stream = 4,
  .cal_data_len = 12064,
  .fw = {
   .dir = QCA9984_HW_1_0_FW_DIR,
   .board_size = QCA99X0_BOARD_DATA_SZ,
   .board_ext_size = QCA99X0_BOARD_EXT_DATA_SZ,
   .ext_board_size = QCA99X0_EXT_BOARD_DATA_SZ,
  },
  .sw_decrypt_mcast_mgmt = true,
  .rx_desc_ops = &qca99x0_rx_desc_ops,
  .hw_ops = &qca99x0_ops,
  .decap_align_bytes = 1,
  .spectral_bin_discard = 12,
  .spectral_bin_offset = 8,

  /* Can do only 2x2 VHT160 or 80+80. 1560Mbps is 4x4 80Mhz
 * or 2x2 160Mhz, long-guard-interval.
 */

  .vht160_mcs_rx_highest = 1560,
  .vht160_mcs_tx_highest = 1560,
  .n_cipher_suites = 11,
  .ast_skid_limit = 0x10,
  .num_wds_entries = 0x20,
  .target_64bit = false,
  .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
  .shadow_reg_support = false,
  .rri_on_ddr = false,
  .hw_filter_reset_required = true,
  .fw_diag_ce_download = false,
  .credit_size_workaround = false,
  .tx_stats_over_pktlog = false,
  .dynamic_sar_support = false,
  .hw_restart_disconnect = false,
  .use_fw_tx_credits = true,
  .delay_unmap_buffer = false,
  .mcast_frame_registration = false,
 },
 {
  .id = QCA9888_HW_2_0_DEV_VERSION,
  .dev_id = QCA9888_2_0_DEVICE_ID,
  .bus = ATH10K_BUS_PCI,
  .name = "qca9888 hw2.0",
  .patch_load_addr = QCA9888_HW_2_0_PATCH_LOAD_ADDR,
  .uart_pin = 7,
  .led_pin = 17,
  .cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_EACH,
  .otp_exe_param = 0x00000700,
  .continuous_frag_desc = true,
  .channel_counters_freq_hz = 150000,
  .max_probe_resp_desc_thres = 24,
  .tx_chain_mask = 3,
  .rx_chain_mask = 3,
  .max_spatial_stream = 2,
  .cal_data_len = 12064,
  .fw = {
   .dir = QCA9888_HW_2_0_FW_DIR,
   .board_size = QCA99X0_BOARD_DATA_SZ,
   .board_ext_size = QCA99X0_BOARD_EXT_DATA_SZ,
  },
  .sw_decrypt_mcast_mgmt = true,
  .rx_desc_ops = &qca99x0_rx_desc_ops,
  .hw_ops = &qca99x0_ops,
  .decap_align_bytes = 1,
  .spectral_bin_discard = 12,
  .spectral_bin_offset = 8,

  /* Can do only 1x1 VHT160 or 80+80. 780Mbps is 2x2 80Mhz or
 * 1x1 160Mhz, long-guard-interval.
 */

  .vht160_mcs_rx_highest = 780,
  .vht160_mcs_tx_highest = 780,
  .n_cipher_suites = 11,
  .ast_skid_limit = 0x10,
  .num_wds_entries = 0x20,
  .target_64bit = false,
  .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
  .shadow_reg_support = false,
  .rri_on_ddr = false,
  .hw_filter_reset_required = true,
  .fw_diag_ce_download = false,
  .credit_size_workaround = false,
  .tx_stats_over_pktlog = false,
  .dynamic_sar_support = false,
  .hw_restart_disconnect = false,
  .use_fw_tx_credits = true,
  .delay_unmap_buffer = false,
  .mcast_frame_registration = false,
 },
 {
  .id = QCA9377_HW_1_0_DEV_VERSION,
  .dev_id = QCA9377_1_0_DEVICE_ID,
  .bus = ATH10K_BUS_PCI,
  .name = "qca9377 hw1.0",
  .patch_load_addr = QCA9377_HW_1_0_PATCH_LOAD_ADDR,
  .uart_pin = 6,
  .led_pin = 0,
  .otp_exe_param = 0,
  .channel_counters_freq_hz = 88000,
  .max_probe_resp_desc_thres = 0,
  .cal_data_len = 8124,
  .fw = {
   .dir = QCA9377_HW_1_0_FW_DIR,
   .board_size = QCA9377_BOARD_DATA_SZ,
   .board_ext_size = QCA9377_BOARD_EXT_DATA_SZ,
  },
  .rx_desc_ops = &qca988x_rx_desc_ops,
  .hw_ops = &qca988x_ops,
  .decap_align_bytes = 4,
  .spectral_bin_discard = 0,
  .spectral_bin_offset = 0,
  .vht160_mcs_rx_highest = 0,
  .vht160_mcs_tx_highest = 0,
  .n_cipher_suites = 8,
  .ast_skid_limit = 0x10,
  .num_wds_entries = 0x20,
  .target_64bit = false,
  .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
  .shadow_reg_support = false,
  .rri_on_ddr = false,
  .hw_filter_reset_required = true,
  .fw_diag_ce_download = false,
  .credit_size_workaround = false,
  .tx_stats_over_pktlog = false,
  .dynamic_sar_support = false,
  .hw_restart_disconnect = false,
  .use_fw_tx_credits = true,
  .delay_unmap_buffer = false,
  .mcast_frame_registration = false,
 },
 {
  .id = QCA9377_HW_1_1_DEV_VERSION,
  .dev_id = QCA9377_1_0_DEVICE_ID,
  .bus = ATH10K_BUS_PCI,
  .name = "qca9377 hw1.1",
  .patch_load_addr = QCA9377_HW_1_0_PATCH_LOAD_ADDR,
  .uart_pin = 6,
  .led_pin = 0,
  .otp_exe_param = 0,
  .channel_counters_freq_hz = 88000,
  .max_probe_resp_desc_thres = 0,
  .cal_data_len = 8124,
  .fw = {
   .dir = QCA9377_HW_1_0_FW_DIR,
   .board_size = QCA9377_BOARD_DATA_SZ,
   .board_ext_size = QCA9377_BOARD_EXT_DATA_SZ,
  },
  .rx_desc_ops = &qca988x_rx_desc_ops,
  .hw_ops = &qca6174_ops,
  .hw_clk = qca6174_clk,
  .target_cpu_freq = 176000000,
  .decap_align_bytes = 4,
  .spectral_bin_discard = 0,
  .spectral_bin_offset = 0,
  .vht160_mcs_rx_highest = 0,
  .vht160_mcs_tx_highest = 0,
  .n_cipher_suites = 8,
  .ast_skid_limit = 0x10,
  .num_wds_entries = 0x20,
  .target_64bit = false,
  .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
  .shadow_reg_support = false,
  .rri_on_ddr = false,
  .hw_filter_reset_required = true,
  .fw_diag_ce_download = true,
  .credit_size_workaround = false,
  .tx_stats_over_pktlog = false,
  .dynamic_sar_support = false,
  .hw_restart_disconnect = false,
  .use_fw_tx_credits = true,
  .delay_unmap_buffer = false,
  .mcast_frame_registration = false,
 },
 {
  .id = QCA9377_HW_1_1_DEV_VERSION,
  .dev_id = QCA9377_1_0_DEVICE_ID,
  .bus = ATH10K_BUS_SDIO,
  .name = "qca9377 hw1.1 sdio",
  .patch_load_addr = QCA9377_HW_1_0_PATCH_LOAD_ADDR,
  .uart_pin = 19,
  .led_pin = 0,
  .otp_exe_param = 0,
  .channel_counters_freq_hz = 88000,
  .max_probe_resp_desc_thres = 0,
  .cal_data_len = 8124,
  .fw = {
   .dir = QCA9377_HW_1_0_FW_DIR,
   .board_size = QCA9377_BOARD_DATA_SZ,
   .board_ext_size = QCA9377_BOARD_EXT_DATA_SZ,
  },
  .rx_desc_ops = &qca988x_rx_desc_ops,
  .hw_ops = &qca6174_ops,
  .hw_clk = qca6174_clk,
  .target_cpu_freq = 176000000,
  .decap_align_bytes = 4,
  .n_cipher_suites = 8,
  .num_peers = TARGET_QCA9377_HL_NUM_PEERS,
  .ast_skid_limit = 0x10,
  .num_wds_entries = 0x20,
  .uart_pin_workaround = true,
  .credit_size_workaround = true,
  .dynamic_sar_support = false,
  .hw_restart_disconnect = false,
  .use_fw_tx_credits = true,
  .delay_unmap_buffer = false,
  .mcast_frame_registration = false,
 },
 {
  .id = QCA4019_HW_1_0_DEV_VERSION,
  .dev_id = 0,
  .bus = ATH10K_BUS_AHB,
  .name = "qca4019 hw1.0",
  .patch_load_addr = QCA4019_HW_1_0_PATCH_LOAD_ADDR,
  .uart_pin = 7,
  .led_pin = 0,
  .cc_wraparound_type = ATH10K_HW_CC_WRAP_SHIFTED_EACH,
  .otp_exe_param = 0x0010000,
  .continuous_frag_desc = true,
  .cck_rate_map_rev2 = true,
  .channel_counters_freq_hz = 125000,
  .max_probe_resp_desc_thres = 24,
  .tx_chain_mask = 0x3,
  .rx_chain_mask = 0x3,
  .max_spatial_stream = 2,
  .cal_data_len = 12064,
  .fw = {
   .dir = QCA4019_HW_1_0_FW_DIR,
   .board_size = QCA4019_BOARD_DATA_SZ,
   .board_ext_size = QCA4019_BOARD_EXT_DATA_SZ,
  },
  .sw_decrypt_mcast_mgmt = true,
  .rx_desc_ops = &qca99x0_rx_desc_ops,
  .hw_ops = &qca99x0_ops,
  .decap_align_bytes = 1,
  .spectral_bin_discard = 4,
  .spectral_bin_offset = 0,
  .vht160_mcs_rx_highest = 0,
  .vht160_mcs_tx_highest = 0,
  .n_cipher_suites = 11,
  .ast_skid_limit = 0x10,
  .num_wds_entries = 0x20,
  .target_64bit = false,
  .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
  .shadow_reg_support = false,
  .rri_on_ddr = false,
  .hw_filter_reset_required = true,
  .fw_diag_ce_download = false,
  .credit_size_workaround = false,
  .tx_stats_over_pktlog = false,
  .dynamic_sar_support = false,
  .hw_restart_disconnect = false,
  .use_fw_tx_credits = true,
  .delay_unmap_buffer = false,
  .mcast_frame_registration = false,
 },
 {
  .id = WCN3990_HW_1_0_DEV_VERSION,
  .dev_id = 0,
  .bus = ATH10K_BUS_SNOC,
  .name = "wcn3990 hw1.0",
  .led_pin = 0,
  .continuous_frag_desc = true,
  .tx_chain_mask = 0x7,
  .rx_chain_mask = 0x7,
  .max_spatial_stream = 4,
  .fw = {
   .dir = WCN3990_HW_1_0_FW_DIR,
   .board_size = WCN3990_BOARD_DATA_SZ,
   .board_ext_size = WCN3990_BOARD_EXT_DATA_SZ,
  },
  .sw_decrypt_mcast_mgmt = true,
  .rx_desc_ops = &wcn3990_rx_desc_ops,
  .hw_ops = &wcn3990_ops,
  .decap_align_bytes = 1,
  .num_peers = TARGET_HL_TLV_NUM_PEERS,
  .n_cipher_suites = 11,
  .ast_skid_limit = TARGET_HL_TLV_AST_SKID_LIMIT,
  .num_wds_entries = TARGET_HL_TLV_NUM_WDS_ENTRIES,
  .target_64bit = true,
  .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL_DUAL_MAC,
  .shadow_reg_support = true,
  .rri_on_ddr = true,
  .hw_filter_reset_required = false,
  .fw_diag_ce_download = false,
  .credit_size_workaround = false,
  .tx_stats_over_pktlog = false,
  .dynamic_sar_support = true,
  .hw_restart_disconnect = true,
  .use_fw_tx_credits = false,
  .delay_unmap_buffer = true,
  .mcast_frame_registration = false,
 },
};

static const char *const ath10k_core_fw_feature_str[] = {
 [ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX] = "wmi-mgmt-rx",
 [ATH10K_FW_FEATURE_WMI_10X] = "wmi-10.x",
 [ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX] = "has-wmi-mgmt-tx",
 [ATH10K_FW_FEATURE_NO_P2P] = "no-p2p",
 [ATH10K_FW_FEATURE_WMI_10_2] = "wmi-10.2",
 [ATH10K_FW_FEATURE_MULTI_VIF_PS_SUPPORT] = "multi-vif-ps",
 [ATH10K_FW_FEATURE_WOWLAN_SUPPORT] = "wowlan",
 [ATH10K_FW_FEATURE_IGNORE_OTP_RESULT] = "ignore-otp",
 [ATH10K_FW_FEATURE_NO_NWIFI_DECAP_4ADDR_PADDING] = "no-4addr-pad",
 [ATH10K_FW_FEATURE_SUPPORTS_SKIP_CLOCK_INIT] = "skip-clock-init",
 [ATH10K_FW_FEATURE_RAW_MODE_SUPPORT] = "raw-mode",
 [ATH10K_FW_FEATURE_SUPPORTS_ADAPTIVE_CCA] = "adaptive-cca",
 [ATH10K_FW_FEATURE_MFP_SUPPORT] = "mfp",
 [ATH10K_FW_FEATURE_PEER_FLOW_CONTROL] = "peer-flow-ctrl",
 [ATH10K_FW_FEATURE_BTCOEX_PARAM] = "btcoex-param",
 [ATH10K_FW_FEATURE_SKIP_NULL_FUNC_WAR] = "skip-null-func-war",
 [ATH10K_FW_FEATURE_ALLOWS_MESH_BCAST] = "allows-mesh-bcast",
 [ATH10K_FW_FEATURE_NO_PS] = "no-ps",
 [ATH10K_FW_FEATURE_MGMT_TX_BY_REF] = "mgmt-tx-by-reference",
 [ATH10K_FW_FEATURE_NON_BMI] = "non-bmi",
 [ATH10K_FW_FEATURE_SINGLE_CHAN_INFO_PER_CHANNEL] = "single-chan-info-per-channel",
 [ATH10K_FW_FEATURE_PEER_FIXED_RATE] = "peer-fixed-rate",
 [ATH10K_FW_FEATURE_IRAM_RECOVERY] = "iram-recovery",
};

static unsigned int ath10k_core_get_fw_feature_str(char *buf,
         size_t buf_len,
         enum ath10k_fw_features feat)
{
 /* make sure that ath10k_core_fw_feature_str[] gets updated */
 BUILD_BUG_ON(ARRAY_SIZE(ath10k_core_fw_feature_str) !=
       ATH10K_FW_FEATURE_COUNT);

 if (feat >= ARRAY_SIZE(ath10k_core_fw_feature_str) ||
     WARN_ON(!ath10k_core_fw_feature_str[feat])) {
  return scnprintf(buf, buf_len, "bit%d", feat);
 }

 return scnprintf(buf, buf_len, "%s", ath10k_core_fw_feature_str[feat]);
}

void ath10k_core_get_fw_features_str(struct ath10k *ar,
         char *buf,
         size_t buf_len)
{
 size_t len = 0;
 int i;

 for (i = 0; i < ATH10K_FW_FEATURE_COUNT; i++) {
  if (test_bit(i, ar->normal_mode_fw.fw_file.fw_features)) {
   if (len > 0)
    len += scnprintf(buf + len, buf_len - len, ",");

   len += ath10k_core_get_fw_feature_str(buf + len,
             buf_len - len,
             i);
  }
 }
}

static void ath10k_send_suspend_complete(struct ath10k *ar)
{
 ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot suspend complete\n");

 complete(&ar->target_suspend);
}

static int ath10k_init_sdio(struct ath10k *ar, enum ath10k_firmware_mode mode)
{
 bool mtu_workaround = ar->hw_params.credit_size_workaround;
 int ret;
 u32 param = 0;

 ret = ath10k_bmi_write32(ar, hi_mbox_io_block_sz, 256);
 if (ret)
  return ret;

 ret = ath10k_bmi_write32(ar, hi_mbox_isr_yield_limit, 99);
 if (ret)
  return ret;

 ret = ath10k_bmi_read32(ar, hi_acs_flags, ¶m);
 if (ret)
  return ret;

 param |= HI_ACS_FLAGS_SDIO_REDUCE_TX_COMPL_SET;

 if (mode == ATH10K_FIRMWARE_MODE_NORMAL && !mtu_workaround)
  param |= HI_ACS_FLAGS_ALT_DATA_CREDIT_SIZE;
 else
  param &= ~HI_ACS_FLAGS_ALT_DATA_CREDIT_SIZE;

 if (mode == ATH10K_FIRMWARE_MODE_UTF)
  param &= ~HI_ACS_FLAGS_SDIO_SWAP_MAILBOX_SET;
 else
  param |= HI_ACS_FLAGS_SDIO_SWAP_MAILBOX_SET;

 ret = ath10k_bmi_write32(ar, hi_acs_flags, param);
 if (ret)
  return ret;

 ret = ath10k_bmi_read32(ar, hi_option_flag2, ¶m);
 if (ret)
  return ret;

 param |= HI_OPTION_SDIO_CRASH_DUMP_ENHANCEMENT_HOST;

 ret = ath10k_bmi_write32(ar, hi_option_flag2, param);
 if (ret)
  return ret;

 return 0;
}

static int ath10k_init_configure_target(struct ath10k *ar)
{
 u32 param_host;
 int ret;

 /* tell target which HTC version it is used*/
 ret = ath10k_bmi_write32(ar, hi_app_host_interest,
     HTC_PROTOCOL_VERSION);
 if (ret) {
  ath10k_err(ar, "settings HTC version failed\n");
  return ret;
 }

 /* set the firmware mode to STA/IBSS/AP */
 ret = ath10k_bmi_read32(ar, hi_option_flag, ¶m_host);
 if (ret) {
  ath10k_err(ar, "setting firmware mode (1/2) failed\n");
  return ret;
 }

 /* TODO following parameters need to be re-visited. */
 /* num_device */
 param_host |= (1 << HI_OPTION_NUM_DEV_SHIFT);
 /* Firmware mode */
 /* FIXME: Why FW_MODE_AP ??.*/
 param_host |= (HI_OPTION_FW_MODE_AP << HI_OPTION_FW_MODE_SHIFT);
 /* mac_addr_method */
 param_host |= (1 << HI_OPTION_MAC_ADDR_METHOD_SHIFT);
 /* firmware_bridge */
 param_host |= (0 << HI_OPTION_FW_BRIDGE_SHIFT);
 /* fwsubmode */
 param_host |= (0 << HI_OPTION_FW_SUBMODE_SHIFT);

 ret = ath10k_bmi_write32(ar, hi_option_flag, param_host);
 if (ret) {
  ath10k_err(ar, "setting firmware mode (2/2) failed\n");
  return ret;
 }

 /* We do all byte-swapping on the host */
 ret = ath10k_bmi_write32(ar, hi_be, 0);
 if (ret) {
  ath10k_err(ar, "setting host CPU BE mode failed\n");
  return ret;
 }

 /* FW descriptor/Data swap flags */
 ret = ath10k_bmi_write32(ar, hi_fw_swap, 0);

 if (ret) {
  ath10k_err(ar, "setting FW data/desc swap flags failed\n");
  return ret;
 }

 /* Some devices have a special sanity check that verifies the PCI
 * Device ID is written to this host interest var. It is known to be
 * required to boot QCA6164.
 */

 ret = ath10k_bmi_write32(ar, hi_hci_uart_pwr_mgmt_params_ext,
     ar->dev_id);
 if (ret) {
  ath10k_err(ar, "failed to set pwr_mgmt_params: %d\n", ret);
  return ret;
 }

 return 0;
}

static const struct firmware *ath10k_fetch_fw_file(struct ath10k *ar,
         const char *dir,
         const char *file)
{
 char filename[100];
 const struct firmware *fw;
 int ret;

 if (file == NULL)
  return ERR_PTR(-ENOENT);

 if (dir == NULL)
  dir = ".";

 if (ar->board_name) {
  snprintf(filename, sizeof(filename), "%s/%s/%s",
    dir, ar->board_name, file);
  ret = firmware_request_nowarn(&fw, filename, ar->dev);
  ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot fw request '%s': %d\n",
      filename, ret);
  if (!ret)
   return fw;
 }

 snprintf(filename, sizeof(filename), "%s/%s", dir, file);
 ret = firmware_request_nowarn(&fw, filename, ar->dev);
 ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot fw request '%s': %d\n",
     filename, ret);
 if (ret)
  return ERR_PTR(ret);

 return fw;
}

static int ath10k_push_board_ext_data(struct ath10k *ar, const void *data,
          size_t data_len)
{
 u32 board_data_size = ar->hw_params.fw.board_size;
 u32 board_ext_data_size = ar->hw_params.fw.board_ext_size;
 u32 board_ext_data_addr;
 int ret;

 ret = ath10k_bmi_read32(ar, hi_board_ext_data, &board_ext_data_addr);
 if (ret) {
  ath10k_err(ar, "could not read board ext data addr (%d)\n",
      ret);
  return ret;
 }

 ath10k_dbg(ar, ATH10K_DBG_BOOT,
     "boot push board extended data addr 0x%x\n",
     board_ext_data_addr);

 if (board_ext_data_addr == 0)
  return 0;

 if (data_len != (board_data_size + board_ext_data_size)) {
  ath10k_err(ar, "invalid board (ext) data sizes %zu != %d+%d\n",
      data_len, board_data_size, board_ext_data_size);
  return -EINVAL;
 }

 ret = ath10k_bmi_write_memory(ar, board_ext_data_addr,
          data + board_data_size,
          board_ext_data_size);
 if (ret) {
  ath10k_err(ar, "could not write board ext data (%d)\n", ret);
  return ret;
 }

 ret = ath10k_bmi_write32(ar, hi_board_ext_data_config,
     (board_ext_data_size << 16) | 1);
 if (ret) {
  ath10k_err(ar, "could not write board ext data bit (%d)\n",
      ret);
  return ret;
 }

 return 0;
}

static int ath10k_core_get_board_id_from_otp(struct ath10k *ar)
{
 u32 result, address;
 u8 board_id, chip_id;
 bool ext_bid_support;
 int ret, bmi_board_id_param;

 address = ar->hw_params.patch_load_addr;

 if (!ar->normal_mode_fw.fw_file.otp_data ||
     !ar->normal_mode_fw.fw_file.otp_len) {
  ath10k_warn(ar,
       "failed to retrieve board id because of invalid otp\n");
  return -ENODATA;
 }

 if (ar->id.bmi_ids_valid) {
  ath10k_dbg(ar, ATH10K_DBG_BOOT,
      "boot already acquired valid otp board id,skip download, board_id %d chip_id %d\n",
      ar->id.bmi_board_id, ar->id.bmi_chip_id);
  goto skip_otp_download;
 }

 ath10k_dbg(ar, ATH10K_DBG_BOOT,
     "boot upload otp to 0x%x len %zd for board id\n",
     address, ar->normal_mode_fw.fw_file.otp_len);

 ret = ath10k_bmi_fast_download(ar, address,
           ar->normal_mode_fw.fw_file.otp_data,
           ar->normal_mode_fw.fw_file.otp_len);
 if (ret) {
  ath10k_err(ar, "could not write otp for board id check: %d\n",
      ret);
  return ret;
 }

 if (ar->cal_mode == ATH10K_PRE_CAL_MODE_DT ||
     ar->cal_mode == ATH10K_PRE_CAL_MODE_FILE ||
     ar->cal_mode == ATH10K_PRE_CAL_MODE_NVMEM)
  bmi_board_id_param = BMI_PARAM_GET_FLASH_BOARD_ID;
 else
  bmi_board_id_param = BMI_PARAM_GET_EEPROM_BOARD_ID;

 ret = ath10k_bmi_execute(ar, address, bmi_board_id_param, &result);
 if (ret) {
  ath10k_err(ar, "could not execute otp for board id check: %d\n",
      ret);
  return ret;
 }

 board_id = MS(result, ATH10K_BMI_BOARD_ID_FROM_OTP);
 chip_id = MS(result, ATH10K_BMI_CHIP_ID_FROM_OTP);
 ext_bid_support = (result & ATH10K_BMI_EXT_BOARD_ID_SUPPORT);

 ath10k_dbg(ar, ATH10K_DBG_BOOT,
     "boot get otp board id result 0x%08x board_id %d chip_id %d ext_bid_support %d\n",
     result, board_id, chip_id, ext_bid_support);

 ar->id.ext_bid_supported = ext_bid_support;

 if ((result & ATH10K_BMI_BOARD_ID_STATUS_MASK) != 0 ||
     (board_id == 0)) {
  ath10k_dbg(ar, ATH10K_DBG_BOOT,
      "board id does not exist in otp, ignore it\n");
  return -EOPNOTSUPP;
 }

 ar->id.bmi_ids_valid = true;
 ar->id.bmi_board_id = board_id;
 ar->id.bmi_chip_id = chip_id;

skip_otp_download:

 return 0;
}

static void ath10k_core_check_bdfext(const struct dmi_header *hdr, void *data)
{
 struct ath10k *ar = data;
 const char *bdf_ext;
 const char *magic = ATH10K_SMBIOS_BDF_EXT_MAGIC;
 u8 bdf_enabled;
 int i;

 if (hdr->type != ATH10K_SMBIOS_BDF_EXT_TYPE)
  return;

 if (hdr->length != ATH10K_SMBIOS_BDF_EXT_LENGTH) {
  ath10k_dbg(ar, ATH10K_DBG_BOOT,
      "wrong smbios bdf ext type length (%d).\n",
      hdr->length);
  return;
 }

 bdf_enabled = *((u8 *)hdr + ATH10K_SMBIOS_BDF_EXT_OFFSET);
 if (!bdf_enabled) {
  ath10k_dbg(ar, ATH10K_DBG_BOOT, "bdf variant name not found.\n");
  return;
 }

 /* Only one string exists (per spec) */
 bdf_ext = (char *)hdr + hdr->length;

 if (memcmp(bdf_ext, magic, strlen(magic)) != 0) {
  ath10k_dbg(ar, ATH10K_DBG_BOOT,
      "bdf variant magic does not match.\n");
  return;
 }

 for (i = 0; i < strlen(bdf_ext); i++) {
  if (!isascii(bdf_ext[i]) || !isprint(bdf_ext[i])) {
   ath10k_dbg(ar, ATH10K_DBG_BOOT,
       "bdf variant name contains non ascii chars.\n");
   return;
  }
 }

 /* Copy extension name without magic suffix */
 if (strscpy(ar->id.bdf_ext, bdf_ext + strlen(magic),
      sizeof(ar->id.bdf_ext)) < 0) {
  ath10k_dbg(ar, ATH10K_DBG_BOOT,
      "bdf variant string is longer than the buffer can accommodate (variant: %s)\n",
       bdf_ext);
  return;
 }

 ath10k_dbg(ar, ATH10K_DBG_BOOT,
     "found and validated bdf variant smbios_type 0x%x bdf %s\n",
     ATH10K_SMBIOS_BDF_EXT_TYPE, bdf_ext);
}

static int ath10k_core_check_smbios(struct ath10k *ar)
{
 ar->id.bdf_ext[0] = '\0';
 dmi_walk(ath10k_core_check_bdfext, ar);

 if (ar->id.bdf_ext[0] == '\0')
  return -ENODATA;

 return 0;
}

int ath10k_core_check_dt(struct ath10k *ar)
{
 struct device_node *node;
 const char *variant = NULL;

 node = ar->dev->of_node;
 if (!node)
  return -ENOENT;

 of_property_read_string(node, "qcom,calibration-variant",
    &variant);
 if (!variant)
  of_property_read_string(node, "qcom,ath10k-calibration-variant",
     &variant);
 if (!variant)
  return -ENODATA;

 if (strscpy(ar->id.bdf_ext, variant, sizeof(ar->id.bdf_ext)) < 0)
  ath10k_dbg(ar, ATH10K_DBG_BOOT,
      "bdf variant string is longer than the buffer can accommodate (variant: %s)\n",
       variant);

 return 0;
}
EXPORT_SYMBOL(ath10k_core_check_dt);

static int ath10k_download_fw(struct ath10k *ar)
{
 u32 address, data_len;
 const void *data;
 int ret;
 struct pm_qos_request latency_qos;

 address = ar->hw_params.patch_load_addr;

 data = ar->running_fw->fw_file.firmware_data;
 data_len = ar->running_fw->fw_file.firmware_len;

 ret = ath10k_swap_code_seg_configure(ar, &ar->running_fw->fw_file);
 if (ret) {
  ath10k_err(ar, "failed to configure fw code swap: %d\n",
      ret);
  return ret;
 }

 ath10k_dbg(ar, ATH10K_DBG_BOOT,
     "boot uploading firmware image %p len %d\n",
     data, data_len);

 /* Check if device supports to download firmware via
 * diag copy engine. Downloading firmware via diag CE
 * greatly reduces the time to download firmware.
 */

 if (ar->hw_params.fw_diag_ce_download) {
  ret = ath10k_hw_diag_fast_download(ar, address,
         data, data_len);
  if (ret == 0)
   /* firmware upload via diag ce was successful */
   return 0;

  ath10k_warn(ar,
       "failed to upload firmware via diag ce, trying BMI: %d",
       ret);
 }

 memset(&latency_qos, 0, sizeof(latency_qos));
 cpu_latency_qos_add_request(&latency_qos, 0);

 ret = ath10k_bmi_fast_download(ar, address, data, data_len);

 cpu_latency_qos_remove_request(&latency_qos);

 return ret;
}

void ath10k_core_free_board_files(struct ath10k *ar)
{
 if (!IS_ERR(ar->normal_mode_fw.board))
  release_firmware(ar->normal_mode_fw.board);

 if (!IS_ERR(ar->normal_mode_fw.ext_board))
  release_firmware(ar->normal_mode_fw.ext_board);

 ar->normal_mode_fw.board = NULL;
 ar->normal_mode_fw.board_data = NULL;
 ar->normal_mode_fw.board_len = 0;
 ar->normal_mode_fw.ext_board = NULL;
 ar->normal_mode_fw.ext_board_data = NULL;
 ar->normal_mode_fw.ext_board_len = 0;
}
EXPORT_SYMBOL(ath10k_core_free_board_files);

static void ath10k_core_free_firmware_files(struct ath10k *ar)
{
 if (!IS_ERR(ar->normal_mode_fw.fw_file.firmware))
  release_firmware(ar->normal_mode_fw.fw_file.firmware);

 if (!IS_ERR(ar->cal_file))
  release_firmware(ar->cal_file);

 if (!IS_ERR(ar->pre_cal_file))
  release_firmware(ar->pre_cal_file);

 ath10k_swap_code_seg_release(ar, &ar->normal_mode_fw.fw_file);

 ar->normal_mode_fw.fw_file.otp_data = NULL;
 ar->normal_mode_fw.fw_file.otp_len = 0;

 ar->normal_mode_fw.fw_file.firmware = NULL;
 ar->normal_mode_fw.fw_file.firmware_data = NULL;
 ar->normal_mode_fw.fw_file.firmware_len = 0;

 ar->cal_file = NULL;
 ar->pre_cal_file = NULL;
}

static int ath10k_fetch_cal_file(struct ath10k *ar)
{
 char filename[100];

 /* pre-cal-<bus>-<id>.bin */
 scnprintf(filename, sizeof(filename), "pre-cal-%s-%s.bin",
    ath10k_bus_str(ar->hif.bus), dev_name(ar->dev));

 ar->pre_cal_file = ath10k_fetch_fw_file(ar, ATH10K_FW_DIR, filename);
 if (!IS_ERR(ar->pre_cal_file))
  goto success;

 /* cal-<bus>-<id>.bin */
 scnprintf(filename, sizeof(filename), "cal-%s-%s.bin",
    ath10k_bus_str(ar->hif.bus), dev_name(ar->dev));

 ar->cal_file = ath10k_fetch_fw_file(ar, ATH10K_FW_DIR, filename);
 if (IS_ERR(ar->cal_file))
  /* calibration file is optional, don't print any warnings */
  return PTR_ERR(ar->cal_file);
success:
 ath10k_dbg(ar, ATH10K_DBG_BOOT, "found calibration file %s/%s\n",
     ATH10K_FW_DIR, filename);

 return 0;
}

static int ath10k_core_fetch_board_data_api_1(struct ath10k *ar, int bd_ie_type)
{
 const struct firmware *fw;
 char boardname[100];

 if (bd_ie_type == ATH10K_BD_IE_BOARD) {
  scnprintf(boardname, sizeof(boardname), "board-%s-%s.bin",
     ath10k_bus_str(ar->hif.bus), dev_name(ar->dev));

  ar->normal_mode_fw.board = ath10k_fetch_fw_file(ar,
        ar->hw_params.fw.dir,
        boardname);
  if (IS_ERR(ar->normal_mode_fw.board)) {
   fw = ath10k_fetch_fw_file(ar,
        ar->hw_params.fw.dir,
        ATH10K_BOARD_DATA_FILE);
   ar->normal_mode_fw.board = fw;
  }

  if (IS_ERR(ar->normal_mode_fw.board))
   return PTR_ERR(ar->normal_mode_fw.board);

  ar->normal_mode_fw.board_data = ar->normal_mode_fw.board->data;
  ar->normal_mode_fw.board_len = ar->normal_mode_fw.board->size;
 } else if (bd_ie_type == ATH10K_BD_IE_BOARD_EXT) {
  fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
       ATH10K_EBOARD_DATA_FILE);
  ar->normal_mode_fw.ext_board = fw;
  if (IS_ERR(ar->normal_mode_fw.ext_board))
   return PTR_ERR(ar->normal_mode_fw.ext_board);

  ar->normal_mode_fw.ext_board_data = ar->normal_mode_fw.ext_board->data;
  ar->normal_mode_fw.ext_board_len = ar->normal_mode_fw.ext_board->size;
 }

 return 0;
}

static int ath10k_core_parse_bd_ie_board(struct ath10k *ar,
      const void *buf, size_t buf_len,
      const char *boardname,
      int bd_ie_type)
{
 const struct ath10k_fw_ie *hdr;
 bool name_match_found;
 int ret, board_ie_id;
 size_t board_ie_len;
 const void *board_ie_data;

 name_match_found = false;

 /* go through ATH10K_BD_IE_BOARD_ elements */
 while (buf_len > sizeof(struct ath10k_fw_ie)) {
  hdr = buf;
  board_ie_id = le32_to_cpu(hdr->id);
  board_ie_len = le32_to_cpu(hdr->len);
  board_ie_data = hdr->data;

  buf_len -= sizeof(*hdr);
  buf += sizeof(*hdr);

  if (buf_len < ALIGN(board_ie_len, 4)) {
   ath10k_err(ar, "invalid ATH10K_BD_IE_BOARD length: %zu < %zu\n",
       buf_len, ALIGN(board_ie_len, 4));
   ret = -EINVAL;
   goto out;
  }

  switch (board_ie_id) {
  case ATH10K_BD_IE_BOARD_NAME:
   ath10k_dbg_dump(ar, ATH10K_DBG_BOOT, "board name""",
     board_ie_data, board_ie_len);

   if (board_ie_len != strlen(boardname))
    break;

   ret = memcmp(board_ie_data, boardname, strlen(boardname));
   if (ret)
    break;

   name_match_found = true;
   ath10k_dbg(ar, ATH10K_DBG_BOOT,
       "boot found match for name '%s'",
       boardname);
   break;
  case ATH10K_BD_IE_BOARD_DATA:
   if (!name_match_found)
    /* no match found */
    break;

   if (bd_ie_type == ATH10K_BD_IE_BOARD) {
    ath10k_dbg(ar, ATH10K_DBG_BOOT,
        "boot found board data for '%s'",
      boardname);

    ar->normal_mode_fw.board_data = board_ie_data;
    ar->normal_mode_fw.board_len = board_ie_len;
   } else if (bd_ie_type == ATH10K_BD_IE_BOARD_EXT) {
    ath10k_dbg(ar, ATH10K_DBG_BOOT,
        "boot found eboard data for '%s'",
      boardname);

    ar->normal_mode_fw.ext_board_data = board_ie_data;
    ar->normal_mode_fw.ext_board_len = board_ie_len;
   }

   ret = 0;
   goto out;
  default:
   ath10k_warn(ar, "unknown ATH10K_BD_IE_BOARD found: %d\n",
        board_ie_id);
   break;
  }

  /* jump over the padding */
  board_ie_len = ALIGN(board_ie_len, 4);

  buf_len -= board_ie_len;
  buf += board_ie_len;
 }

 /* no match found */
 ret = -ENOENT;

out:
 return ret;
}

static int ath10k_core_search_bd(struct ath10k *ar,
     const char *boardname,
     const u8 *data,
     size_t len)
{
 size_t ie_len;
 struct ath10k_fw_ie *hdr;
 int ret = -ENOENT, ie_id;

 while (len > sizeof(struct ath10k_fw_ie)) {
  hdr = (struct ath10k_fw_ie *)data;
  ie_id = le32_to_cpu(hdr->id);
  ie_len = le32_to_cpu(hdr->len);

  len -= sizeof(*hdr);
  data = hdr->data;

  if (len < ALIGN(ie_len, 4)) {
   ath10k_err(ar, "invalid length for board ie_id %d ie_len %zu len %zu\n",
       ie_id, ie_len, len);
   return -EINVAL;
  }

  switch (ie_id) {
  case ATH10K_BD_IE_BOARD:
   ret = ath10k_core_parse_bd_ie_board(ar, data, ie_len,
           boardname,
           ATH10K_BD_IE_BOARD);
   if (ret == -ENOENT)
    /* no match found, continue */
    break;

   /* either found or error, so stop searching */
   goto out;
  case ATH10K_BD_IE_BOARD_EXT:
   ret = ath10k_core_parse_bd_ie_board(ar, data, ie_len,
           boardname,
           ATH10K_BD_IE_BOARD_EXT);
   if (ret == -ENOENT)
    /* no match found, continue */
    break;

   /* either found or error, so stop searching */
   goto out;
  }

  /* jump over the padding */
  ie_len = ALIGN(ie_len, 4);

  len -= ie_len;
  data += ie_len;
 }

out:
 /* return result of parse_bd_ie_board() or -ENOENT */
 return ret;
}

static int ath10k_core_fetch_board_data_api_n(struct ath10k *ar,
           const char *boardname,
           const char *fallback_boardname1,
           const char *fallback_boardname2,
           const char *filename)
{
 size_t len, magic_len;
 const u8 *data;
 int ret;

 /* Skip if already fetched during board data download */
 if (!ar->normal_mode_fw.board)
  ar->normal_mode_fw.board = ath10k_fetch_fw_file(ar,
        ar->hw_params.fw.dir,
        filename);
 if (IS_ERR(ar->normal_mode_fw.board))
  return PTR_ERR(ar->normal_mode_fw.board);

 data = ar->normal_mode_fw.board->data;
 len = ar->normal_mode_fw.board->size;

 /* magic has extra null byte padded */
 magic_len = strlen(ATH10K_BOARD_MAGIC) + 1;
 if (len < magic_len) {
  ath10k_err(ar, "failed to find magic value in %s/%s, file too short: %zu\n",
      ar->hw_params.fw.dir, filename, len);
  ret = -EINVAL;
  goto err;
 }

 if (memcmp(data, ATH10K_BOARD_MAGIC, magic_len)) {
  ath10k_err(ar, "found invalid board magic\n");
  ret = -EINVAL;
  goto err;
 }

 /* magic is padded to 4 bytes */
 magic_len = ALIGN(magic_len, 4);
 if (len < magic_len) {
  ath10k_err(ar, "failed: %s/%s too small to contain board data, len: %zu\n",
      ar->hw_params.fw.dir, filename, len);
  ret = -EINVAL;
  goto err;
 }

 data += magic_len;
 len -= magic_len;

 /* attempt to find boardname in the IE list */
 ret = ath10k_core_search_bd(ar, boardname, data, len);

 /* if we didn't find it and have a fallback name, try that */
 if (ret == -ENOENT && fallback_boardname1)
  ret = ath10k_core_search_bd(ar, fallback_boardname1, data, len);

 if (ret == -ENOENT && fallback_boardname2)
  ret = ath10k_core_search_bd(ar, fallback_boardname2, data, len);

 if (ret == -ENOENT) {
  ath10k_err(ar,
      "failed to fetch board data for %s from %s/%s\n",
      boardname, ar->hw_params.fw.dir, filename);
  ret = -ENODATA;
 }

 if (ret)
  goto err;

 return 0;

err:
 ath10k_core_free_board_files(ar);
 return ret;
}

static int ath10k_core_create_board_name(struct ath10k *ar, char *name,
      size_t name_len, bool with_variant,
      bool with_chip_id)
{
 /* strlen(',variant=') + strlen(ar->id.bdf_ext) */
 char variant[9 + ATH10K_SMBIOS_BDF_EXT_STR_LENGTH] = {};

 if (with_variant && ar->id.bdf_ext[0] != '\0')
  scnprintf(variant, sizeof(variant), ",variant=%s",
     ar->id.bdf_ext);

 if (ar->id.bmi_ids_valid) {
  scnprintf(name, name_len,
     "bus=%s,bmi-chip-id=%d,bmi-board-id=%d%s",
     ath10k_bus_str(ar->hif.bus),
     ar->id.bmi_chip_id,
     ar->id.bmi_board_id, variant);
  goto out;
 }

 if (ar->id.qmi_ids_valid) {
  if (with_chip_id)
   scnprintf(name, name_len,
      "bus=%s,qmi-board-id=%x,qmi-chip-id=%x%s",
      ath10k_bus_str(ar->hif.bus),
      ar->id.qmi_board_id, ar->id.qmi_chip_id,
      variant);
  else
   scnprintf(name, name_len,
      "bus=%s,qmi-board-id=%x",
      ath10k_bus_str(ar->hif.bus),
      ar->id.qmi_board_id);
  goto out;
 }

 scnprintf(name, name_len,
    "bus=%s,vendor=%04x,device=%04x,subsystem-vendor=%04x,subsystem-device=%04x%s",
    ath10k_bus_str(ar->hif.bus),
    ar->id.vendor, ar->id.device,
    ar->id.subsystem_vendor, ar->id.subsystem_device, variant);
out:
 ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot using board name '%s'\n", name);

 return 0;
}

static int ath10k_core_create_eboard_name(struct ath10k *ar, char *name,
       size_t name_len)
{
 if (ar->id.bmi_ids_valid) {
  scnprintf(name, name_len,
     "bus=%s,bmi-chip-id=%d,bmi-eboard-id=%d",
     ath10k_bus_str(ar->hif.bus),
     ar->id.bmi_chip_id,
     ar->id.bmi_eboard_id);

  ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot using eboard name '%s'\n", name);
  return 0;
 }
 /* Fallback if returned board id is zero */
 return -1;
}

int ath10k_core_fetch_board_file(struct ath10k *ar, int bd_ie_type)
{
 char boardname[100], fallback_boardname1[100], fallback_boardname2[100];
 int ret;

 if (bd_ie_type == ATH10K_BD_IE_BOARD) {
  /* With variant and chip id */
  ret = ath10k_core_create_board_name(ar, boardname,
          sizeof(boardname), true,
          true);
  if (ret) {
   ath10k_err(ar, "failed to create board name: %d", ret);
   return ret;
  }

  /* Without variant and only chip-id */
  ret = ath10k_core_create_board_name(ar, fallback_boardname1,
          sizeof(boardname), false,
          true);
  if (ret) {
   ath10k_err(ar, "failed to create 1st fallback board name: %d",
       ret);
   return ret;
  }

  /* Without variant and without chip-id */
  ret = ath10k_core_create_board_name(ar, fallback_boardname2,
          sizeof(boardname), false,
          false);
  if (ret) {
   ath10k_err(ar, "failed to create 2nd fallback board name: %d",
       ret);
   return ret;
  }
 } else if (bd_ie_type == ATH10K_BD_IE_BOARD_EXT) {
  ret = ath10k_core_create_eboard_name(ar, boardname,
           sizeof(boardname));
  if (ret) {
   ath10k_err(ar, "fallback to eboard.bin since board id 0");
   goto fallback;
  }
 }

 ar->bd_api = 2;
 ret = ath10k_core_fetch_board_data_api_n(ar, boardname,
       fallback_boardname1,
       fallback_boardname2,
       ATH10K_BOARD_API2_FILE);
 if (!ret)
  goto success;

fallback:
 ar->bd_api = 1;
 ret = ath10k_core_fetch_board_data_api_1(ar, bd_ie_type);
 if (ret) {
  ath10k_err(ar, "failed to fetch board-2.bin or board.bin from %s\n",
      ar->hw_params.fw.dir);
  return ret;
 }

success:
 ath10k_dbg(ar, ATH10K_DBG_BOOT, "using board api %d\n", ar->bd_api);
 return 0;
}
EXPORT_SYMBOL(ath10k_core_fetch_board_file);

static int ath10k_core_get_ext_board_id_from_otp(struct ath10k *ar)
{
 u32 result, address;
 u8 ext_board_id;
 int ret;

 address = ar->hw_params.patch_load_addr;

 if (!ar->normal_mode_fw.fw_file.otp_data ||
     !ar->normal_mode_fw.fw_file.otp_len) {
  ath10k_warn(ar,
       "failed to retrieve extended board id due to otp binary missing\n");
  return -ENODATA;
 }

 ath10k_dbg(ar, ATH10K_DBG_BOOT,
     "boot upload otp to 0x%x len %zd for ext board id\n",
     address, ar->normal_mode_fw.fw_file.otp_len);

 ret = ath10k_bmi_fast_download(ar, address,
           ar->normal_mode_fw.fw_file.otp_data,
           ar->normal_mode_fw.fw_file.otp_len);
 if (ret) {
  ath10k_err(ar, "could not write otp for ext board id check: %d\n",
      ret);
  return ret;
 }

 ret = ath10k_bmi_execute(ar, address, BMI_PARAM_GET_EXT_BOARD_ID, &result);
 if (ret) {
  ath10k_err(ar, "could not execute otp for ext board id check: %d\n",
      ret);
  return ret;
 }

 if (!result) {
  ath10k_dbg(ar, ATH10K_DBG_BOOT,
      "ext board id does not exist in otp, ignore it\n");
  return -EOPNOTSUPP;
 }

 ext_board_id = result & ATH10K_BMI_EBOARD_ID_STATUS_MASK;

 ath10k_dbg(ar, ATH10K_DBG_BOOT,
     "boot get otp ext board id result 0x%08x ext_board_id %d\n",
     result, ext_board_id);

 ar->id.bmi_eboard_id = ext_board_id;

 return 0;
}

static int ath10k_download_board_data(struct ath10k *ar, const void *data,
          size_t data_len)
{
 u32 board_data_size = ar->hw_params.fw.board_size;
 u32 eboard_data_size = ar->hw_params.fw.ext_board_size;
 u32 board_address;
 u32 ext_board_address;
 int ret;

 ret = ath10k_push_board_ext_data(ar, data, data_len);
 if (ret) {
  ath10k_err(ar, "could not push board ext data (%d)\n", ret);
  goto exit;
 }

 ret = ath10k_bmi_read32(ar, hi_board_data, &board_address);
 if (ret) {
  ath10k_err(ar, "could not read board data addr (%d)\n", ret);
  goto exit;
 }

 ret = ath10k_bmi_write_memory(ar, board_address, data,
          min_t(u32, board_data_size,
         data_len));
 if (ret) {
  ath10k_err(ar, "could not write board data (%d)\n", ret);
  goto exit;
 }

 ret = ath10k_bmi_write32(ar, hi_board_data_initialized, 1);
 if (ret) {
  ath10k_err(ar, "could not write board data bit (%d)\n", ret);
  goto exit;
 }

 if (!ar->id.ext_bid_supported)
  goto exit;

 /* Extended board data download */
 ret = ath10k_core_get_ext_board_id_from_otp(ar);
 if (ret == -EOPNOTSUPP) {
  /* Not fetching ext_board_data if ext board id is 0 */
  ath10k_dbg(ar, ATH10K_DBG_BOOT, "otp returned ext board id 0\n");
  return 0;
 } else if (ret) {
  ath10k_err(ar, "failed to get extended board id: %d\n", ret);
  goto exit;
 }

 ret = ath10k_core_fetch_board_file(ar, ATH10K_BD_IE_BOARD_EXT);
 if (ret)
  goto exit;

 if (ar->normal_mode_fw.ext_board_data) {
  ext_board_address = board_address + EXT_BOARD_ADDRESS_OFFSET;
  ath10k_dbg(ar, ATH10K_DBG_BOOT,
      "boot writing ext board data to addr 0x%x",
      ext_board_address);
  ret = ath10k_bmi_write_memory(ar, ext_board_address,
           ar->normal_mode_fw.ext_board_data,
           min_t(u32, eboard_data_size, data_len));
  if (ret)
   ath10k_err(ar, "failed to write ext board data: %d\n", ret);
 }

exit:
 return ret;
}

static int ath10k_download_and_run_otp(struct ath10k *ar)
{
 u32 result, address = ar->hw_params.patch_load_addr;
 u32 bmi_otp_exe_param = ar->hw_params.otp_exe_param;
 int ret;

 ret = ath10k_download_board_data(ar,
      ar->running_fw->board_data,
      ar->running_fw->board_len);
 if (ret) {
  ath10k_err(ar, "failed to download board data: %d\n", ret);
  return ret;
 }

 /* OTP is optional */

 if (!ar->running_fw->fw_file.otp_data ||
     !ar->running_fw->fw_file.otp_len) {
  ath10k_warn(ar, "Not running otp, calibration will be incorrect (otp-data %p otp_len %zd)!\n",
       ar->running_fw->fw_file.otp_data,
       ar->running_fw->fw_file.otp_len);
  return 0;
 }

 ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot upload otp to 0x%x len %zd\n",
     address, ar->running_fw->fw_file.otp_len);

 ret = ath10k_bmi_fast_download(ar, address,
           ar->running_fw->fw_file.otp_data,
           ar->running_fw->fw_file.otp_len);
 if (ret) {
  ath10k_err(ar, "could not write otp (%d)\n", ret);
  return ret;
 }

 /* As of now pre-cal is valid for 10_4 variants */
 if (ar->cal_mode == ATH10K_PRE_CAL_MODE_DT ||
     ar->cal_mode == ATH10K_PRE_CAL_MODE_FILE ||
     ar->cal_mode == ATH10K_PRE_CAL_MODE_NVMEM)
  bmi_otp_exe_param = BMI_PARAM_FLASH_SECTION_ALL;

 ret = ath10k_bmi_execute(ar, address, bmi_otp_exe_param, &result);
 if (ret) {
  ath10k_err(ar, "could not execute otp (%d)\n", ret);
  return ret;
 }

 ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot otp execute result %d\n", result);

 if (!(skip_otp || test_bit(ATH10K_FW_FEATURE_IGNORE_OTP_RESULT,
       ar->running_fw->fw_file.fw_features)) &&
     result != 0) {
  ath10k_err(ar, "otp calibration failed: %d", result);
  return -EINVAL;
 }

 return 0;
}

static int ath10k_download_cal_file(struct ath10k *ar,
        const struct firmware *file)
{
 int ret;

 if (!file)
  return -ENOENT;

 if (IS_ERR(file))
  return PTR_ERR(file);

 ret = ath10k_download_board_data(ar, file->data, file->size);
 if (ret) {
  ath10k_err(ar, "failed to download cal_file data: %d\n", ret);
  return ret;
 }

 ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot cal file downloaded\n");

 return 0;
}

static int ath10k_download_cal_dt(struct ath10k *ar, const char *dt_name)
{
 struct device_node *node;
 int data_len;
 void *data;
 int ret;

 node = ar->dev->of_node;
 if (!node)
  /* Device Tree is optional, don't print any warnings if
 * there's no node for ath10k.
 */

  return -ENOENT;

 if (!of_get_property(node, dt_name, &data_len)) {
  /* The calibration data node is optional */
  return -ENOENT;
 }

 if (data_len != ar->hw_params.cal_data_len) {
  ath10k_warn(ar, "invalid calibration data length in DT: %d\n",
       data_len);
  ret = -EMSGSIZE;
  goto out;
 }

 data = kmalloc(data_len, GFP_KERNEL);
 if (!data) {
  ret = -ENOMEM;
  goto out;
 }

 ret = of_property_read_u8_array(node, dt_name, data, data_len);
 if (ret) {
  ath10k_warn(ar, "failed to read calibration data from DT: %d\n",
       ret);
  goto out_free;
 }

 ret = ath10k_download_board_data(ar, data, data_len);
 if (ret) {
  ath10k_warn(ar, "failed to download calibration data from Device Tree: %d\n",
       ret);
  goto out_free;
 }

 ret = 0;

out_free:
 kfree(data);

out:
 return ret;
}

static int ath10k_download_cal_eeprom(struct ath10k *ar)
{
 size_t data_len;
 void *data = NULL;
 int ret;

 ret = ath10k_hif_fetch_cal_eeprom(ar, &data, &data_len);
 if (ret) {
  if (ret != -EOPNOTSUPP)
   ath10k_warn(ar, "failed to read calibration data from EEPROM: %d\n",
        ret);
  goto out_free;
 }

 ret = ath10k_download_board_data(ar, data, data_len);
 if (ret) {
  ath10k_warn(ar, "failed to download calibration data from EEPROM: %d\n",
       ret);
  goto out_free;
 }

 ret = 0;

out_free:
 kfree(data);

 return ret;
}

static int ath10k_download_cal_nvmem(struct ath10k *ar, const char *cell_name)
{
 struct nvmem_cell *cell;
 void *buf;
 size_t len;
 int ret;

 cell = devm_nvmem_cell_get(ar->dev, cell_name);
 if (IS_ERR(cell)) {
  ret = PTR_ERR(cell);
  return ret;
 }

 buf = nvmem_cell_read(cell, &len);
 if (IS_ERR(buf))
  return PTR_ERR(buf);

 if (ar->hw_params.cal_data_len != len) {
  kfree(buf);
  ath10k_warn(ar, "invalid calibration data length in nvmem-cell '%s': %zu != %u\n",
       cell_name, len, ar->hw_params.cal_data_len);
  return -EMSGSIZE;
 }

 ret = ath10k_download_board_data(ar, buf, len);
 kfree(buf);
 if (ret)
  ath10k_warn(ar, "failed to download calibration data from nvmem-cell '%s': %d\n",
       cell_name, ret);

 return ret;
}

int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name,
         struct ath10k_fw_file *fw_file)
{
 size_t magic_len, len, ie_len;
 int ie_id, i, index, bit, ret;
 struct ath10k_fw_ie *hdr;
 const u8 *data;
 __le32 *timestamp, *version;

 /* first fetch the firmware file (firmware-*.bin) */
 fw_file->firmware = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
       name);
 if (IS_ERR(fw_file->firmware))
  return PTR_ERR(fw_file->firmware);

 data = fw_file->firmware->data;
 len = fw_file->firmware->size;

 /* magic also includes the null byte, check that as well */
 magic_len = strlen(ATH10K_FIRMWARE_MAGIC) + 1;

 if (len < magic_len) {
  ath10k_err(ar, "firmware file '%s/%s' too small to contain magic: %zu\n",
      ar->hw_params.fw.dir, name, len);
  ret = -EINVAL;
  goto err;
 }

 if (memcmp(data, ATH10K_FIRMWARE_MAGIC, magic_len) != 0) {
  ath10k_err(ar, "invalid firmware magic\n");
  ret = -EINVAL;
  goto err;
 }

 /* jump over the padding */
 magic_len = ALIGN(magic_len, 4);

 len -= magic_len;
 data += magic_len;

 /* loop elements */
 while (len > sizeof(struct ath10k_fw_ie)) {
  hdr = (struct ath10k_fw_ie *)data;

  ie_id = le32_to_cpu(hdr->id);
  ie_len = le32_to_cpu(hdr->len);

  len -= sizeof(*hdr);
  data += sizeof(*hdr);

  if (len < ie_len) {
   ath10k_err(ar, "invalid length for FW IE %d (%zu < %zu)\n",
       ie_id, len, ie_len);
   ret = -EINVAL;
   goto err;
  }

  switch (ie_id) {
  case ATH10K_FW_IE_FW_VERSION:
   if (ie_len > sizeof(fw_file->fw_version) - 1)
    break;

   memcpy(fw_file->fw_version, data, ie_len);
   fw_file->fw_version[ie_len] = '\0';

   ath10k_dbg(ar, ATH10K_DBG_BOOT,
       "found fw version %s\n",
        fw_file->fw_version);
   break;
  case ATH10K_FW_IE_TIMESTAMP:
   if (ie_len != sizeof(u32))
    break;

   timestamp = (__le32 *)data;

   ath10k_dbg(ar, ATH10K_DBG_BOOT, "found fw timestamp %d\n",
       le32_to_cpup(timestamp));
   break;
  case ATH10K_FW_IE_FEATURES:
   ath10k_dbg(ar, ATH10K_DBG_BOOT,
       "found firmware features ie (%zd B)\n",
       ie_len);

   for (i = 0; i < ATH10K_FW_FEATURE_COUNT; i++) {
    index = i / 8;
    bit = i % 8;

    if (index == ie_len)
     break;

    if (data[index] & (1 << bit)) {
     ath10k_dbg(ar, ATH10K_DBG_BOOT,
         "Enabling feature bit: %i\n",
         i);
     __set_bit(i, fw_file->fw_features);
    }
   }

   ath10k_dbg_dump(ar, ATH10K_DBG_BOOT, "features""",
     fw_file->fw_features,
     sizeof(fw_file->fw_features));
   break;
  case ATH10K_FW_IE_FW_IMAGE:
   ath10k_dbg(ar, ATH10K_DBG_BOOT,
       "found fw image ie (%zd B)\n",
       ie_len);

   fw_file->firmware_data = data;
   fw_file->firmware_len = ie_len;

   break;
  case ATH10K_FW_IE_OTP_IMAGE:
   ath10k_dbg(ar, ATH10K_DBG_BOOT,
       "found otp image ie (%zd B)\n",
       ie_len);

   fw_file->otp_data = data;
   fw_file->otp_len = ie_len;

   break;
  case ATH10K_FW_IE_WMI_OP_VERSION:
   if (ie_len != sizeof(u32))
    break;

   version = (__le32 *)data;

   fw_file->wmi_op_version = le32_to_cpup(version);

   ath10k_dbg(ar, ATH10K_DBG_BOOT, "found fw ie wmi op version %d\n",
       fw_file->wmi_op_version);
   break;
  case ATH10K_FW_IE_HTT_OP_VERSION:
   if (ie_len != sizeof(u32))
    break;

   version = (__le32 *)data;

   fw_file->htt_op_version = le32_to_cpup(version);

   ath10k_dbg(ar, ATH10K_DBG_BOOT, "found fw ie htt op version %d\n",
       fw_file->htt_op_version);
   break;
  case ATH10K_FW_IE_FW_CODE_SWAP_IMAGE:
   ath10k_dbg(ar, ATH10K_DBG_BOOT,
       "found fw code swap image ie (%zd B)\n",
       ie_len);
   fw_file->codeswap_data = data;
   fw_file->codeswap_len = ie_len;
   break;
  default:
   ath10k_warn(ar, "Unknown FW IE: %u\n",
        le32_to_cpu(hdr->id));
   break;
  }

  /* jump over the padding */
  ie_len = ALIGN(ie_len, 4);

  len -= ie_len;
  data += ie_len;
 }

 if (!test_bit(ATH10K_FW_FEATURE_NON_BMI, fw_file->fw_features) &&
     (!fw_file->firmware_data || !fw_file->firmware_len)) {
  ath10k_warn(ar, "No ATH10K_FW_IE_FW_IMAGE found from '%s/%s', skipping\n",
       ar->hw_params.fw.dir, name);
  ret = -ENOMEDIUM;
  goto err;
 }

 return 0;

err:
 ath10k_core_free_firmware_files(ar);
 return ret;
}

static void ath10k_core_get_fw_name(struct ath10k *ar, char *fw_name,
        size_t fw_name_len, int fw_api)
{
 switch (ar->hif.bus) {
 case ATH10K_BUS_SDIO:
 case ATH10K_BUS_USB:
  scnprintf(fw_name, fw_name_len, "%s-%s-%d.bin",
     ATH10K_FW_FILE_BASE, ath10k_bus_str(ar->hif.bus),
     fw_api);
  break;
 case ATH10K_BUS_PCI:
 case ATH10K_BUS_AHB:
 case ATH10K_BUS_SNOC:
  scnprintf(fw_name, fw_name_len, "%s-%d.bin",
     ATH10K_FW_FILE_BASE, fw_api);
  break;
 }
}

static int ath10k_core_fetch_firmware_files(struct ath10k *ar)
{
 int ret, i;
 char fw_name[100];

 /* calibration file is optional, don't check for any errors */
 ath10k_fetch_cal_file(ar);

 for (i = ATH10K_FW_API_MAX; i >= ATH10K_FW_API_MIN; i--) {
  ar->fw_api = i;
  ath10k_dbg(ar, ATH10K_DBG_BOOT, "trying fw api %d\n",
      ar->fw_api);

  ath10k_core_get_fw_name(ar, fw_name, sizeof(fw_name), ar->fw_api);
  ret = ath10k_core_fetch_firmware_api_n(ar, fw_name,
             &ar->normal_mode_fw.fw_file);
  if (!ret)
   goto success;
 }

 /* we end up here if we couldn't fetch any firmware */

 ath10k_err(ar, "Failed to find firmware-N.bin (N between %d and %d) from %s: %d",
     ATH10K_FW_API_MIN, ATH10K_FW_API_MAX, ar->hw_params.fw.dir,
     ret);

 return ret;

success:
 ath10k_dbg(ar, ATH10K_DBG_BOOT, "using fw api %d\n", ar->fw_api);

 return 0;
}

static int ath10k_core_pre_cal_download(struct ath10k *ar)
{
 int ret;

 ret = ath10k_download_cal_nvmem(ar, "pre-calibration");
 if (ret == 0) {
  ar->cal_mode = ATH10K_PRE_CAL_MODE_NVMEM;
  goto success;
 } else if (ret == -EPROBE_DEFER) {
  return ret;
 }

 ath10k_dbg(ar, ATH10K_DBG_BOOT,
     "boot did not find a pre-calibration nvmem-cell, try file next: %d\n",
     ret);

 ret = ath10k_download_cal_file(ar, ar->pre_cal_file);
 if (ret == 0) {
  ar->cal_mode = ATH10K_PRE_CAL_MODE_FILE;
  goto success;
 }

 ath10k_dbg(ar, ATH10K_DBG_BOOT,
     "boot did not find a pre calibration file, try DT next: %d\n",
     ret);

 ret = ath10k_download_cal_dt(ar, "qcom,pre-calibration-data");
 if (ret == -ENOENT)
  ret = ath10k_download_cal_dt(ar, "qcom,ath10k-pre-calibration-data");
 if (ret) {
  ath10k_dbg(ar, ATH10K_DBG_BOOT,
      "unable to load pre cal data from DT: %d\n", ret);
  return ret;
 }
 ar->cal_mode = ATH10K_PRE_CAL_MODE_DT;

success:
 ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot using calibration mode %s\n",
     ath10k_cal_mode_str(ar->cal_mode));

 return 0;
}

static int ath10k_core_pre_cal_config(struct ath10k *ar)
{
 int ret;

 ret = ath10k_core_pre_cal_download(ar);
 if (ret) {
  ath10k_dbg(ar, ATH10K_DBG_BOOT,
      "failed to load pre cal data: %d\n", ret);
  return ret;
 }

 ret = ath10k_core_get_board_id_from_otp(ar);
 if (ret) {
  ath10k_err(ar, "failed to get board id: %d\n", ret);
  return ret;
 }

 ret = ath10k_download_and_run_otp(ar);
 if (ret) {
  ath10k_err(ar, "failed to run otp: %d\n", ret);
  return ret;
 }

 ath10k_dbg(ar, ATH10K_DBG_BOOT,
     "pre cal configuration done successfully\n");

 return 0;
}

static int ath10k_download_cal_data(struct ath10k *ar)
{
 int ret;

 ret = ath10k_core_pre_cal_config(ar);
 if (ret == 0)
  return 0;

 ath10k_dbg(ar, ATH10K_DBG_BOOT,
     "pre cal download procedure failed, try cal file: %d\n",
     ret);

 ret = ath10k_download_cal_nvmem(ar, "calibration");
 if (ret == 0) {
  ar->cal_mode = ATH10K_CAL_MODE_NVMEM;
  goto done;
 } else if (ret == -EPROBE_DEFER) {
  return ret;
 }

 ath10k_dbg(ar, ATH10K_DBG_BOOT,
     "boot did not find a calibration nvmem-cell, try file next: %d\n",
     ret);

 ret = ath10k_download_cal_file(ar, ar->cal_file);
 if (ret == 0) {
  ar->cal_mode = ATH10K_CAL_MODE_FILE;
  goto done;
 }

 ath10k_dbg(ar, ATH10K_DBG_BOOT,
     "boot did not find a calibration file, try DT next: %d\n",
     ret);

 ret = ath10k_download_cal_dt(ar, "qcom,calibration-data");
 if (ret == -ENOENT)
  ret = ath10k_download_cal_dt(ar, "qcom,ath10k-calibration-data");
 if (ret == 0) {
  ar->cal_mode = ATH10K_CAL_MODE_DT;
  goto done;
 }

 ath10k_dbg(ar, ATH10K_DBG_BOOT,
     "boot did not find DT entry, try target EEPROM next: %d\n",
     ret);

 ret = ath10k_download_cal_eeprom(ar);
 if (ret == 0) {
  ar->cal_mode = ATH10K_CAL_MODE_EEPROM;
  goto done;
 }

 ath10k_dbg(ar, ATH10K_DBG_BOOT,
     "boot did not find target EEPROM entry, try OTP next: %d\n",
     ret);

 ret = ath10k_download_and_run_otp(ar);
 if (ret) {
  ath10k_err(ar, "failed to run otp: %d\n", ret);
  return ret;
 }

 ar->cal_mode = ATH10K_CAL_MODE_OTP;

done:
 ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot using calibration mode %s\n",
     ath10k_cal_mode_str(ar->cal_mode));
 return 0;
}

static void ath10k_core_fetch_btcoex_dt(struct ath10k *ar)
{
 struct device_node *node;
 u8 coex_support = 0;
 int ret;

 node = ar->dev->of_node;
 if (!node)
  goto out;

 ret = of_property_read_u8(node, "qcom,coexist-support", &coex_support);
 if (ret) {
  ar->coex_support = true;
  goto out;
 }

 if (coex_support) {
  ar->coex_support = true;
 } else {
  ar->coex_support = false;
  ar->coex_gpio_pin = -1;
  goto out;
 }

 ret = of_property_read_u32(node, "qcom,coexist-gpio-pin",
       &ar->coex_gpio_pin);
 if (ret)
  ar->coex_gpio_pin = -1;

out:
 ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot coex_support %d coex_gpio_pin %d\n",
     ar->coex_support, ar->coex_gpio_pin);
}

static int ath10k_init_uart(struct ath10k *ar)
{
 int ret;

 /*
 * Explicitly setting UART prints to zero as target turns it on
 * based on scratch registers.
 */

 ret = ath10k_bmi_write32(ar, hi_serial_enable, 0);
 if (ret) {
  ath10k_warn(ar, "could not disable UART prints (%d)\n", ret);
  return ret;
 }

 if (!uart_print) {
  if (ar->hw_params.uart_pin_workaround) {
   ret = ath10k_bmi_write32(ar, hi_dbg_uart_txpin,
       ar->hw_params.uart_pin);
   if (ret) {
    ath10k_warn(ar, "failed to set UART TX pin: %d",
         ret);
    return ret;
   }
  }

  return 0;
 }

 ret = ath10k_bmi_write32(ar, hi_dbg_uart_txpin, ar->hw_params.uart_pin);
 if (ret) {
  ath10k_warn(ar, "could not enable UART prints (%d)\n", ret);
  return ret;
 }

 ret = ath10k_bmi_write32(ar, hi_serial_enable, 1);
 if (ret) {
  ath10k_warn(ar, "could not enable UART prints (%d)\n", ret);
  return ret;
 }

 /* Set the UART baud rate to 19200. */
 ret = ath10k_bmi_write32(ar, hi_desired_baud_rate, 19200);
 if (ret) {
  ath10k_warn(ar, "could not set the baud rate (%d)\n", ret);
  return ret;
 }

 ath10k_info(ar, "UART prints enabled\n");
 return 0;
}

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=97 H=92 G=94

¤ Dauer der Verarbeitung: 0.26 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.