These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / net / wireless / ath / ath10k / core.c
index c0e454b..0947cc2 100644 (file)
 #include "wmi-ops.h"
 
 unsigned int ath10k_debug_mask;
+static unsigned int ath10k_cryptmode_param;
 static bool uart_print;
 static bool skip_otp;
+static bool rawmode;
 
 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(rawmode, bool, 0644);
 
 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(rawmode, "Use raw 802.11 frame datapath");
 
 static const struct ath10k_hw_params ath10k_hw_params_list[] = {
        {
                .id = QCA988X_HW_2_0_VERSION,
+               .dev_id = QCA988X_2_0_DEVICE_ID,
                .name = "qca988x hw2.0",
                .patch_load_addr = QCA988X_HW_2_0_PATCH_LOAD_ADDR,
                .uart_pin = 7,
+               .has_shifted_cc_wraparound = true,
+               .otp_exe_param = 0,
+               .channel_counters_freq_hz = 88000,
+               .max_probe_resp_desc_thres = 0,
                .fw = {
                        .dir = QCA988X_HW_2_0_FW_DIR,
                        .fw = QCA988X_HW_2_0_FW_FILE,
@@ -59,9 +70,31 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
        },
        {
                .id = QCA6174_HW_2_1_VERSION,
+               .dev_id = QCA6164_2_1_DEVICE_ID,
+               .name = "qca6164 hw2.1",
+               .patch_load_addr = QCA6174_HW_2_1_PATCH_LOAD_ADDR,
+               .uart_pin = 6,
+               .otp_exe_param = 0,
+               .channel_counters_freq_hz = 88000,
+               .max_probe_resp_desc_thres = 0,
+               .fw = {
+                       .dir = QCA6174_HW_2_1_FW_DIR,
+                       .fw = QCA6174_HW_2_1_FW_FILE,
+                       .otp = QCA6174_HW_2_1_OTP_FILE,
+                       .board = QCA6174_HW_2_1_BOARD_DATA_FILE,
+                       .board_size = QCA6174_BOARD_DATA_SZ,
+                       .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ,
+               },
+       },
+       {
+               .id = QCA6174_HW_2_1_VERSION,
+               .dev_id = QCA6174_2_1_DEVICE_ID,
                .name = "qca6174 hw2.1",
                .patch_load_addr = QCA6174_HW_2_1_PATCH_LOAD_ADDR,
                .uart_pin = 6,
+               .otp_exe_param = 0,
+               .channel_counters_freq_hz = 88000,
+               .max_probe_resp_desc_thres = 0,
                .fw = {
                        .dir = QCA6174_HW_2_1_FW_DIR,
                        .fw = QCA6174_HW_2_1_FW_FILE,
@@ -73,9 +106,13 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
        },
        {
                .id = QCA6174_HW_3_0_VERSION,
+               .dev_id = QCA6174_2_1_DEVICE_ID,
                .name = "qca6174 hw3.0",
                .patch_load_addr = QCA6174_HW_3_0_PATCH_LOAD_ADDR,
                .uart_pin = 6,
+               .otp_exe_param = 0,
+               .channel_counters_freq_hz = 88000,
+               .max_probe_resp_desc_thres = 0,
                .fw = {
                        .dir = QCA6174_HW_3_0_FW_DIR,
                        .fw = QCA6174_HW_3_0_FW_FILE,
@@ -87,9 +124,13 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
        },
        {
                .id = QCA6174_HW_3_2_VERSION,
+               .dev_id = QCA6174_2_1_DEVICE_ID,
                .name = "qca6174 hw3.2",
                .patch_load_addr = QCA6174_HW_3_0_PATCH_LOAD_ADDR,
                .uart_pin = 6,
+               .otp_exe_param = 0,
+               .channel_counters_freq_hz = 88000,
+               .max_probe_resp_desc_thres = 0,
                .fw = {
                        /* uses same binaries as hw3.0 */
                        .dir = QCA6174_HW_3_0_FW_DIR,
@@ -100,8 +141,113 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
                        .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ,
                },
        },
+       {
+               .id = QCA99X0_HW_2_0_DEV_VERSION,
+               .dev_id = QCA99X0_2_0_DEVICE_ID,
+               .name = "qca99x0 hw2.0",
+               .patch_load_addr = QCA99X0_HW_2_0_PATCH_LOAD_ADDR,
+               .uart_pin = 7,
+               .otp_exe_param = 0x00000700,
+               .continuous_frag_desc = true,
+               .channel_counters_freq_hz = 150000,
+               .max_probe_resp_desc_thres = 24,
+               .fw = {
+                       .dir = QCA99X0_HW_2_0_FW_DIR,
+                       .fw = QCA99X0_HW_2_0_FW_FILE,
+                       .otp = QCA99X0_HW_2_0_OTP_FILE,
+                       .board = QCA99X0_HW_2_0_BOARD_DATA_FILE,
+                       .board_size = QCA99X0_BOARD_DATA_SZ,
+                       .board_ext_size = QCA99X0_BOARD_EXT_DATA_SZ,
+               },
+       },
+       {
+               .id = QCA9377_HW_1_0_DEV_VERSION,
+               .dev_id = QCA9377_1_0_DEVICE_ID,
+               .name = "qca9377 hw1.0",
+               .patch_load_addr = QCA9377_HW_1_0_PATCH_LOAD_ADDR,
+               .uart_pin = 6,
+               .otp_exe_param = 0,
+               .channel_counters_freq_hz = 88000,
+               .max_probe_resp_desc_thres = 0,
+               .fw = {
+                       .dir = QCA9377_HW_1_0_FW_DIR,
+                       .fw = QCA9377_HW_1_0_FW_FILE,
+                       .otp = QCA9377_HW_1_0_OTP_FILE,
+                       .board = QCA9377_HW_1_0_BOARD_DATA_FILE,
+                       .board_size = QCA9377_BOARD_DATA_SZ,
+                       .board_ext_size = QCA9377_BOARD_EXT_DATA_SZ,
+               },
+       },
+       {
+               .id = QCA9377_HW_1_1_DEV_VERSION,
+               .dev_id = QCA9377_1_0_DEVICE_ID,
+               .name = "qca9377 hw1.1",
+               .patch_load_addr = QCA9377_HW_1_0_PATCH_LOAD_ADDR,
+               .uart_pin = 6,
+               .otp_exe_param = 0,
+               .channel_counters_freq_hz = 88000,
+               .max_probe_resp_desc_thres = 0,
+               .fw = {
+                       .dir = QCA9377_HW_1_0_FW_DIR,
+                       .fw = QCA9377_HW_1_0_FW_FILE,
+                       .otp = QCA9377_HW_1_0_OTP_FILE,
+                       .board = QCA9377_HW_1_0_BOARD_DATA_FILE,
+                       .board_size = QCA9377_BOARD_DATA_SZ,
+                       .board_ext_size = QCA9377_BOARD_EXT_DATA_SZ,
+               },
+       },
+};
+
+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",
 };
 
+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)
+{
+       unsigned int len = 0;
+       int i;
+
+       for (i = 0; i < ATH10K_FW_FEATURE_COUNT; i++) {
+               if (test_bit(i, ar->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");
@@ -163,6 +309,17 @@ static int ath10k_init_configure_target(struct ath10k *ar)
                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;
 }
 
@@ -351,9 +508,60 @@ out:
        return ret;
 }
 
+static int ath10k_core_get_board_id_from_otp(struct ath10k *ar)
+{
+       u32 result, address;
+       u8 board_id, chip_id;
+       int ret;
+
+       address = ar->hw_params.patch_load_addr;
+
+       if (!ar->otp_data || !ar->otp_len) {
+               ath10k_warn(ar,
+                           "failed to retrieve board id because of invalid otp\n");
+               return -ENODATA;
+       }
+
+       ath10k_dbg(ar, ATH10K_DBG_BOOT,
+                  "boot upload otp to 0x%x len %zd for board id\n",
+                  address, ar->otp_len);
+
+       ret = ath10k_bmi_fast_download(ar, address, ar->otp_data, ar->otp_len);
+       if (ret) {
+               ath10k_err(ar, "could not write otp for board id check: %d\n",
+                          ret);
+               return ret;
+       }
+
+       ret = ath10k_bmi_execute(ar, address, BMI_PARAM_GET_EEPROM_BOARD_ID,
+                                &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);
+
+       ath10k_dbg(ar, ATH10K_DBG_BOOT,
+                  "boot get otp board id result 0x%08x board_id %d chip_id %d\n",
+                  result, board_id, chip_id);
+
+       if ((result & ATH10K_BMI_BOARD_ID_STATUS_MASK) != 0)
+               return -EOPNOTSUPP;
+
+       ar->id.bmi_ids_valid = true;
+       ar->id.bmi_board_id = board_id;
+       ar->id.bmi_chip_id = chip_id;
+
+       return 0;
+}
+
 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->board_data, ar->board_len);
@@ -379,7 +587,7 @@ static int ath10k_download_and_run_otp(struct ath10k *ar)
                return ret;
        }
 
-       ret = ath10k_bmi_execute(ar, address, 0, &result);
+       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;
@@ -387,7 +595,9 @@ static int ath10k_download_and_run_otp(struct ath10k *ar)
 
        ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot otp execute result %d\n", result);
 
-       if (!skip_otp && result != 0) {
+       if (!(skip_otp || test_bit(ATH10K_FW_FEATURE_IGNORE_OTP_RESULT,
+                                  ar->fw_features)) &&
+           result != 0) {
                ath10k_err(ar, "otp calibration failed: %d", result);
                return -EINVAL;
        }
@@ -409,10 +619,17 @@ static int ath10k_download_fw(struct ath10k *ar, enum ath10k_firmware_mode mode)
                data = ar->firmware_data;
                data_len = ar->firmware_len;
                mode_name = "normal";
+               ret = ath10k_swap_code_seg_configure(ar,
+                                                    ATH10K_SWAP_CODE_SEG_BIN_TYPE_FW);
+               if (ret) {
+                       ath10k_err(ar, "failed to configure fw code swap: %d\n",
+                                  ret);
+                       return ret;
+               }
                break;
        case ATH10K_FIRMWARE_MODE_UTF:
-               data = ar->testmode.utf->data;
-               data_len = ar->testmode.utf->size;
+               data = ar->testmode.utf_firmware_data;
+               data_len = ar->testmode.utf_firmware_len;
                mode_name = "utf";
                break;
        default:
@@ -434,11 +651,18 @@ static int ath10k_download_fw(struct ath10k *ar, enum ath10k_firmware_mode mode)
        return ret;
 }
 
-static void ath10k_core_free_firmware_files(struct ath10k *ar)
+static void ath10k_core_free_board_files(struct ath10k *ar)
 {
        if (!IS_ERR(ar->board))
                release_firmware(ar->board);
 
+       ar->board = NULL;
+       ar->board_data = NULL;
+       ar->board_len = 0;
+}
+
+static void ath10k_core_free_firmware_files(struct ath10k *ar)
+{
        if (!IS_ERR(ar->otp))
                release_firmware(ar->otp);
 
@@ -448,9 +672,7 @@ static void ath10k_core_free_firmware_files(struct ath10k *ar)
        if (!IS_ERR(ar->cal_file))
                release_firmware(ar->cal_file);
 
-       ar->board = NULL;
-       ar->board_data = NULL;
-       ar->board_len = 0;
+       ath10k_swap_code_seg_release(ar);
 
        ar->otp = NULL;
        ar->otp_data = NULL;
@@ -482,32 +704,263 @@ static int ath10k_fetch_cal_file(struct ath10k *ar)
        return 0;
 }
 
-static int ath10k_core_fetch_firmware_api_1(struct ath10k *ar)
+static int ath10k_core_fetch_board_data_api_1(struct ath10k *ar)
 {
-       int ret = 0;
-
-       if (ar->hw_params.fw.fw == NULL) {
-               ath10k_err(ar, "firmware file not defined\n");
-               return -EINVAL;
-       }
-
-       if (ar->hw_params.fw.board == NULL) {
-               ath10k_err(ar, "board data file not defined");
+       if (!ar->hw_params.fw.board) {
+               ath10k_err(ar, "failed to find board file fw entry\n");
                return -EINVAL;
        }
 
        ar->board = ath10k_fetch_fw_file(ar,
                                         ar->hw_params.fw.dir,
                                         ar->hw_params.fw.board);
-       if (IS_ERR(ar->board)) {
-               ret = PTR_ERR(ar->board);
-               ath10k_err(ar, "could not fetch board data (%d)\n", ret);
-               goto err;
-       }
+       if (IS_ERR(ar->board))
+               return PTR_ERR(ar->board);
 
        ar->board_data = ar->board->data;
        ar->board_len = ar->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)
+{
+       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;
+
+                       ath10k_dbg(ar, ATH10K_DBG_BOOT,
+                                  "boot found board data for '%s'",
+                                  boardname);
+
+                       ar->board_data = board_ie_data;
+                       ar->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_fetch_board_data_api_n(struct ath10k *ar,
+                                             const char *boardname,
+                                             const char *filename)
+{
+       size_t len, magic_len, ie_len;
+       struct ath10k_fw_ie *hdr;
+       const u8 *data;
+       int ret, ie_id;
+
+       ar->board = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, filename);
+       if (IS_ERR(ar->board))
+               return PTR_ERR(ar->board);
+
+       data = ar->board->data;
+       len = ar->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;
+
+       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);
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               switch (ie_id) {
+               case ATH10K_BD_IE_BOARD:
+                       ret = ath10k_core_parse_bd_ie_board(ar, data, ie_len,
+                                                           boardname);
+                       if (ret == -ENOENT)
+                               /* no match found, continue */
+                               break;
+                       else if (ret)
+                               /* there was an error, bail out */
+                               goto err;
+
+                       /* board data found */
+                       goto out;
+               }
+
+               /* jump over the padding */
+               ie_len = ALIGN(ie_len, 4);
+
+               len -= ie_len;
+               data += ie_len;
+       }
+
+out:
+       if (!ar->board_data || !ar->board_len) {
+               ath10k_err(ar,
+                          "failed to fetch board data for %s from %s/%s\n",
+                          ar->hw_params.fw.dir, boardname, filename);
+               ret = -ENODATA;
+               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)
+{
+       if (ar->id.bmi_ids_valid) {
+               scnprintf(name, name_len,
+                         "bus=%s,bmi-chip-id=%d,bmi-board-id=%d",
+                         ath10k_bus_str(ar->hif.bus),
+                         ar->id.bmi_chip_id,
+                         ar->id.bmi_board_id);
+               goto out;
+       }
+
+       scnprintf(name, name_len,
+                 "bus=%s,vendor=%04x,device=%04x,subsystem-vendor=%04x,subsystem-device=%04x",
+                 ath10k_bus_str(ar->hif.bus),
+                 ar->id.vendor, ar->id.device,
+                 ar->id.subsystem_vendor, ar->id.subsystem_device);
+
+out:
+       ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot using board name '%s'\n", name);
+
+       return 0;
+}
+
+static int ath10k_core_fetch_board_file(struct ath10k *ar)
+{
+       char boardname[100];
+       int ret;
+
+       ret = ath10k_core_create_board_name(ar, boardname, sizeof(boardname));
+       if (ret) {
+               ath10k_err(ar, "failed to create board name: %d", ret);
+               return ret;
+       }
+
+       ar->bd_api = 2;
+       ret = ath10k_core_fetch_board_data_api_n(ar, boardname,
+                                                ATH10K_BOARD_API2_FILE);
+       if (!ret)
+               goto success;
+
+       ar->bd_api = 1;
+       ret = ath10k_core_fetch_board_data_api_1(ar);
+       if (ret) {
+               ath10k_err(ar, "failed to fetch board data\n");
+               return ret;
+       }
+
+success:
+       ath10k_dbg(ar, ATH10K_DBG_BOOT, "using board api %d\n", ar->bd_api);
+       return 0;
+}
+
+static int ath10k_core_fetch_firmware_api_1(struct ath10k *ar)
+{
+       int ret = 0;
+
+       if (ar->hw_params.fw.fw == NULL) {
+               ath10k_err(ar, "firmware file not defined\n");
+               return -EINVAL;
+       }
+
        ar->firmware = ath10k_fetch_fw_file(ar,
                                            ar->hw_params.fw.dir,
                                            ar->hw_params.fw.fw);
@@ -675,6 +1128,24 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name)
                        ath10k_dbg(ar, ATH10K_DBG_BOOT, "found fw ie wmi op version %d\n",
                                   ar->wmi.op_version);
                        break;
+               case ATH10K_FW_IE_HTT_OP_VERSION:
+                       if (ie_len != sizeof(u32))
+                               break;
+
+                       version = (__le32 *)data;
+
+                       ar->htt.op_version = le32_to_cpup(version);
+
+                       ath10k_dbg(ar, ATH10K_DBG_BOOT, "found fw ie htt op version %d\n",
+                                  ar->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);
+                       ar->swap.firmware_codeswap_data = data;
+                       ar->swap.firmware_codeswap_len = ie_len;
+                       break;
                default:
                        ath10k_warn(ar, "Unknown FW IE: %u\n",
                                    le32_to_cpu(hdr->id));
@@ -695,27 +1166,6 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name)
                goto err;
        }
 
-       /* now fetch the board file */
-       if (ar->hw_params.fw.board == NULL) {
-               ath10k_err(ar, "board data file not defined");
-               ret = -EINVAL;
-               goto err;
-       }
-
-       ar->board = ath10k_fetch_fw_file(ar,
-                                        ar->hw_params.fw.dir,
-                                        ar->hw_params.fw.board);
-       if (IS_ERR(ar->board)) {
-               ret = PTR_ERR(ar->board);
-               ath10k_err(ar, "could not fetch board data '%s/%s' (%d)\n",
-                          ar->hw_params.fw.dir, ar->hw_params.fw.board,
-                          ret);
-               goto err;
-       }
-
-       ar->board_data = ar->board->data;
-       ar->board_len = ar->board->size;
-
        return 0;
 
 err:
@@ -730,6 +1180,13 @@ static int ath10k_core_fetch_firmware_files(struct ath10k *ar)
        /* calibration file is optional, don't check for any errors */
        ath10k_fetch_cal_file(ar);
 
+       ar->fw_api = 5;
+       ath10k_dbg(ar, ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api);
+
+       ret = ath10k_core_fetch_firmware_api_n(ar, ATH10K_FW_API5_FILE);
+       if (ret == 0)
+               goto success;
+
        ar->fw_api = 4;
        ath10k_dbg(ar, ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api);
 
@@ -850,7 +1307,8 @@ static int ath10k_init_hw_params(struct ath10k *ar)
        for (i = 0; i < ARRAY_SIZE(ath10k_hw_params_list); i++) {
                hw_params = &ath10k_hw_params_list[i];
 
-               if (hw_params->id == ar->target_version)
+               if (hw_params->id == ar->target_version &&
+                   hw_params->dev_id == ar->dev_id)
                        break;
        }
 
@@ -937,6 +1395,55 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar)
                return -EINVAL;
        }
 
+       ar->wmi.rx_decap_mode = ATH10K_HW_TXRX_NATIVE_WIFI;
+       switch (ath10k_cryptmode_param) {
+       case ATH10K_CRYPT_MODE_HW:
+               clear_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags);
+               clear_bit(ATH10K_FLAG_HW_CRYPTO_DISABLED, &ar->dev_flags);
+               break;
+       case ATH10K_CRYPT_MODE_SW:
+               if (!test_bit(ATH10K_FW_FEATURE_RAW_MODE_SUPPORT,
+                             ar->fw_features)) {
+                       ath10k_err(ar, "cryptmode > 0 requires raw mode support from firmware");
+                       return -EINVAL;
+               }
+
+               set_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags);
+               set_bit(ATH10K_FLAG_HW_CRYPTO_DISABLED, &ar->dev_flags);
+               break;
+       default:
+               ath10k_info(ar, "invalid cryptmode: %d\n",
+                           ath10k_cryptmode_param);
+               return -EINVAL;
+       }
+
+       ar->htt.max_num_amsdu = ATH10K_HTT_MAX_NUM_AMSDU_DEFAULT;
+       ar->htt.max_num_ampdu = ATH10K_HTT_MAX_NUM_AMPDU_DEFAULT;
+
+       if (rawmode) {
+               if (!test_bit(ATH10K_FW_FEATURE_RAW_MODE_SUPPORT,
+                             ar->fw_features)) {
+                       ath10k_err(ar, "rawmode = 1 requires support from firmware");
+                       return -EINVAL;
+               }
+               set_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags);
+       }
+
+       if (test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) {
+               ar->wmi.rx_decap_mode = ATH10K_HW_TXRX_RAW;
+
+               /* Workaround:
+                *
+                * Firmware A-MSDU aggregation breaks with RAW Tx encap mode
+                * and causes enormous performance issues (malformed frames,
+                * etc).
+                *
+                * Disabling A-MSDU makes RAW mode stable with heavy traffic
+                * albeit a bit slower compared to regular operation.
+                */
+               ar->htt.max_num_amsdu = 1;
+       }
+
        /* Backwards compatibility for firmwares without
         * ATH10K_FW_IE_WMI_OP_VERSION.
         */
@@ -958,6 +1465,9 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar)
                ar->max_num_stations = TARGET_NUM_STATIONS;
                ar->max_num_vdevs = TARGET_NUM_VDEVS;
                ar->htt.max_num_pending_tx = TARGET_NUM_MSDU_DESC;
+               ar->fw_stats_req_mask = WMI_STAT_PDEV | WMI_STAT_VDEV |
+                       WMI_STAT_PEER;
+               ar->max_spatial_stream = WMI_MAX_SPATIAL_STREAM;
                break;
        case ATH10K_FW_WMI_OP_VERSION_10_1:
        case ATH10K_FW_WMI_OP_VERSION_10_2:
@@ -966,12 +1476,29 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar)
                ar->max_num_stations = TARGET_10X_NUM_STATIONS;
                ar->max_num_vdevs = TARGET_10X_NUM_VDEVS;
                ar->htt.max_num_pending_tx = TARGET_10X_NUM_MSDU_DESC;
+               ar->fw_stats_req_mask = WMI_STAT_PEER;
+               ar->max_spatial_stream = WMI_MAX_SPATIAL_STREAM;
                break;
        case ATH10K_FW_WMI_OP_VERSION_TLV:
                ar->max_num_peers = TARGET_TLV_NUM_PEERS;
                ar->max_num_stations = TARGET_TLV_NUM_STATIONS;
                ar->max_num_vdevs = TARGET_TLV_NUM_VDEVS;
+               ar->max_num_tdls_vdevs = TARGET_TLV_NUM_TDLS_VDEVS;
                ar->htt.max_num_pending_tx = TARGET_TLV_NUM_MSDU_DESC;
+               ar->wow.max_num_patterns = TARGET_TLV_NUM_WOW_PATTERNS;
+               ar->fw_stats_req_mask = WMI_STAT_PDEV | WMI_STAT_VDEV |
+                       WMI_STAT_PEER;
+               ar->max_spatial_stream = WMI_MAX_SPATIAL_STREAM;
+               break;
+       case ATH10K_FW_WMI_OP_VERSION_10_4:
+               ar->max_num_peers = TARGET_10_4_NUM_PEERS;
+               ar->max_num_stations = TARGET_10_4_NUM_STATIONS;
+               ar->num_active_peers = TARGET_10_4_ACTIVE_PEERS;
+               ar->max_num_vdevs = TARGET_10_4_NUM_VDEVS;
+               ar->num_tids = TARGET_10_4_TGT_NUM_TIDS;
+               ar->htt.max_num_pending_tx = TARGET_10_4_NUM_MSDU_DESC;
+               ar->fw_stats_req_mask = WMI_STAT_PEER;
+               ar->max_spatial_stream = WMI_10_4_MAX_SPATIAL_STREAM;
                break;
        case ATH10K_FW_WMI_OP_VERSION_UNSET:
        case ATH10K_FW_WMI_OP_VERSION_MAX:
@@ -979,6 +1506,30 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar)
                return -EINVAL;
        }
 
+       /* Backwards compatibility for firmwares without
+        * ATH10K_FW_IE_HTT_OP_VERSION.
+        */
+       if (ar->htt.op_version == ATH10K_FW_HTT_OP_VERSION_UNSET) {
+               switch (ar->wmi.op_version) {
+               case ATH10K_FW_WMI_OP_VERSION_MAIN:
+                       ar->htt.op_version = ATH10K_FW_HTT_OP_VERSION_MAIN;
+                       break;
+               case ATH10K_FW_WMI_OP_VERSION_10_1:
+               case ATH10K_FW_WMI_OP_VERSION_10_2:
+               case ATH10K_FW_WMI_OP_VERSION_10_2_4:
+                       ar->htt.op_version = ATH10K_FW_HTT_OP_VERSION_10_1;
+                       break;
+               case ATH10K_FW_WMI_OP_VERSION_TLV:
+                       ar->htt.op_version = ATH10K_FW_HTT_OP_VERSION_TLV;
+                       break;
+               case ATH10K_FW_WMI_OP_VERSION_10_4:
+               case ATH10K_FW_WMI_OP_VERSION_UNSET:
+               case ATH10K_FW_WMI_OP_VERSION_MAX:
+                       WARN_ON(1);
+                       return -EINVAL;
+               }
+       }
+
        return 0;
 }
 
@@ -1001,6 +1552,22 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode)
        if (status)
                goto err;
 
+       /* Some of of qca988x solutions are having global reset issue
+        * during target initialization. Bypassing PLL setting before
+        * downloading firmware and letting the SoC run on REF_CLK is
+        * fixing the problem. Corresponding firmware change is also needed
+        * to set the clock source once the target is initialized.
+        */
+       if (test_bit(ATH10K_FW_FEATURE_SUPPORTS_SKIP_CLOCK_INIT,
+                    ar->fw_features)) {
+               status = ath10k_bmi_write32(ar, hi_skip_clock_init, 1);
+               if (status) {
+                       ath10k_err(ar, "could not write to skip_clock_init: %d\n",
+                                  status);
+                       goto err;
+               }
+       }
+
        status = ath10k_download_fw(ar, mode);
        if (status)
                goto err;
@@ -1080,9 +1647,8 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode)
 
        if (mode == ATH10K_FIRMWARE_MODE_NORMAL) {
                status = ath10k_wmi_wait_for_service_ready(ar);
-               if (status <= 0) {
+               if (status) {
                        ath10k_warn(ar, "wmi service ready event not received");
-                       status = -ETIMEDOUT;
                        goto err_hif_stop;
                }
        }
@@ -1098,9 +1664,8 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode)
        }
 
        status = ath10k_wmi_wait_for_unified_ready(ar);
-       if (status <= 0) {
+       if (status) {
                ath10k_err(ar, "wmi unified ready event not received\n");
-               status = -ETIMEDOUT;
                goto err_hif_stop;
        }
 
@@ -1151,6 +1716,7 @@ EXPORT_SYMBOL(ath10k_core_start);
 int ath10k_wait_for_suspend(struct ath10k *ar, u32 suspend_opt)
 {
        int ret;
+       unsigned long time_left;
 
        reinit_completion(&ar->target_suspend);
 
@@ -1160,9 +1726,9 @@ int ath10k_wait_for_suspend(struct ath10k *ar, u32 suspend_opt)
                return ret;
        }
 
-       ret = wait_for_completion_timeout(&ar->target_suspend, 1 * HZ);
+       time_left = wait_for_completion_timeout(&ar->target_suspend, 1 * HZ);
 
-       if (ret == 0) {
+       if (!time_left) {
                ath10k_warn(ar, "suspend timed out - target pause event never came\n");
                return -ETIMEDOUT;
        }
@@ -1173,13 +1739,13 @@ int ath10k_wait_for_suspend(struct ath10k *ar, u32 suspend_opt)
 void ath10k_core_stop(struct ath10k *ar)
 {
        lockdep_assert_held(&ar->conf_mutex);
+       ath10k_debug_stop(ar);
 
        /* try to suspend target */
        if (ar->state != ATH10K_STATE_RESTARTING &&
            ar->state != ATH10K_STATE_UTF)
                ath10k_wait_for_suspend(ar, WMI_PDEV_SUSPEND_AND_DISABLE_INTR);
 
-       ath10k_debug_stop(ar);
        ath10k_hif_stop(ar);
        ath10k_htt_tx_free(&ar->htt);
        ath10k_htt_rx_free(&ar->htt);
@@ -1224,6 +1790,19 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
                goto err_power_down;
        }
 
+       ret = ath10k_core_get_board_id_from_otp(ar);
+       if (ret && ret != -EOPNOTSUPP) {
+               ath10k_err(ar, "failed to get board id from otp for qca99x0: %d\n",
+                          ret);
+               return ret;
+       }
+
+       ret = ath10k_core_fetch_board_file(ar);
+       if (ret) {
+               ath10k_err(ar, "failed to fetch board file: %d\n", ret);
+               goto err_free_firmware_files;
+       }
+
        ret = ath10k_core_init_firmware_features(ar);
        if (ret) {
                ath10k_err(ar, "fatal problem with firmware features: %d\n",
@@ -1231,6 +1810,13 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
                goto err_free_firmware_files;
        }
 
+       ret = ath10k_swap_code_seg_init(ar);
+       if (ret) {
+               ath10k_err(ar, "failed to initialize code swap segment: %d\n",
+                          ret);
+               goto err_free_firmware_files;
+       }
+
        mutex_lock(&ar->conf_mutex);
 
        ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_NORMAL);
@@ -1344,6 +1930,7 @@ void ath10k_core_unregister(struct ath10k *ar)
        ath10k_testmode_destroy(ar);
 
        ath10k_core_free_firmware_files(ar);
+       ath10k_core_free_board_files(ar);
 
        ath10k_debug_unregister(ar);
 }
@@ -1371,9 +1958,16 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
        switch (hw_rev) {
        case ATH10K_HW_QCA988X:
                ar->regs = &qca988x_regs;
+               ar->hw_values = &qca988x_values;
                break;
        case ATH10K_HW_QCA6174:
+       case ATH10K_HW_QCA9377:
                ar->regs = &qca6174_regs;
+               ar->hw_values = &qca6174_values;
+               break;
+       case ATH10K_HW_QCA99X0:
+               ar->regs = &qca99x0_regs;
+               ar->hw_values = &qca99x0_values;
                break;
        default:
                ath10k_err(ar, "unsupported core hardware revision %d\n",
@@ -1386,6 +1980,7 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
        init_completion(&ar->scan.completed);
        init_completion(&ar->scan.on_channel);
        init_completion(&ar->target_suspend);
+       init_completion(&ar->wow.wakeup_completed);
 
        init_completion(&ar->install_key_done);
        init_completion(&ar->vdev_setup_done);
@@ -1397,6 +1992,10 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
        if (!ar->workqueue)
                goto err_free_mac;
 
+       ar->workqueue_aux = create_singlethread_workqueue("ath10k_aux_wq");
+       if (!ar->workqueue_aux)
+               goto err_free_wq;
+
        mutex_init(&ar->conf_mutex);
        spin_lock_init(&ar->data_lock);
 
@@ -1417,10 +2016,12 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
 
        ret = ath10k_debug_create(ar);
        if (ret)
-               goto err_free_wq;
+               goto err_free_aux_wq;
 
        return ar;
 
+err_free_aux_wq:
+       destroy_workqueue(ar->workqueue_aux);
 err_free_wq:
        destroy_workqueue(ar->workqueue);
 
@@ -1436,7 +2037,11 @@ void ath10k_core_destroy(struct ath10k *ar)
        flush_workqueue(ar->workqueue);
        destroy_workqueue(ar->workqueue);
 
+       flush_workqueue(ar->workqueue_aux);
+       destroy_workqueue(ar->workqueue_aux);
+
        ath10k_debug_destroy(ar);
+       ath10k_wmi_free_host_mem(ar);
        ath10k_mac_destroy(ar);
 }
 EXPORT_SYMBOL(ath10k_core_destroy);