From 9d5804662ce1f9bdde0a14c3c40940acbbf09538 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 28 Aug 2018 19:48:17 +0300 Subject: [PATCH 01/51] ath10k: retrieve MAC address from system firmware if provided Devices may provide their own MAC address via system firmware (e.g., device tree), especially in the case where the device doesn't have a useful EEPROM on which to store its MAC address (e.g., for integrated Wifi). Use the generic device helper to retrieve the MAC address, and (if present) honor it above the MAC address advertised by the card. Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 3 +++ drivers/net/wireless/ath/ath10k/wmi.c | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index ebc213884a9a..f5f71c02fc2b 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -2617,6 +2618,8 @@ static int ath10k_core_probe_fw(struct ath10k *ar) ath10k_debug_print_board_info(ar); } + device_get_mac_address(ar->dev, ar->mac_addr, sizeof(ar->mac_addr)); + ret = ath10k_core_init_firmware_features(ar); if (ret) { ath10k_err(ar, "fatal problem with firmware features: %d\n", diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 40ce0e4006bc..212005cd0646 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -5455,7 +5455,8 @@ int ath10k_wmi_event_ready(struct ath10k *ar, struct sk_buff *skb) arg.mac_addr, __le32_to_cpu(arg.status)); - ether_addr_copy(ar->mac_addr, arg.mac_addr); + if (is_zero_ether_addr(ar->mac_addr)) + ether_addr_copy(ar->mac_addr, arg.mac_addr); complete(&ar->wmi.unified_ready); return 0; } From 828853ac58265c93249b53ba81782213962d5d4e Mon Sep 17 00:00:00 2001 From: Wen Gong Date: Tue, 28 Aug 2018 19:48:42 +0300 Subject: [PATCH 02/51] ath10k: add waiting htt tx complete before wow enable If there are some tx packets pending in firmware, and then system enters suspend, firmware will fail for wow enable. This will trigger mac80211 to stop ath10k and download firmware again, then it is non-wow suspend. After add the waiting htt tx complete, then firmware will have some time window to send or flush the pending tx packets. Tested with QCA6174 PCI with firmware WLAN.RM.4.4.1-00109-QCARMSWPZ-1, but this will also affect QCA9377 PCI. It's not a regression with new firmware releases. Signed-off-by: Wen Gong Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 22 +++++++++++++--------- drivers/net/wireless/ath/ath10k/mac.h | 1 + drivers/net/wireless/ath/ath10k/wow.c | 2 ++ 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 496772d95d11..078058eef648 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -6769,23 +6769,17 @@ static int ath10k_mac_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value) return -EOPNOTSUPP; } -static void ath10k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - u32 queues, bool drop) +void ath10k_mac_wait_tx_complete(struct ath10k *ar) { - struct ath10k *ar = hw->priv; bool skip; long time_left; /* mac80211 doesn't care if we really xmit queued frames or not * we'll collect those frames either way if we stop/delete vdevs */ - if (drop) - return; - - mutex_lock(&ar->conf_mutex); if (ar->state == ATH10K_STATE_WEDGED) - goto skip; + return; time_left = wait_event_timeout(ar->htt.empty_tx_wq, ({ bool empty; @@ -6804,8 +6798,18 @@ static void ath10k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, if (time_left == 0 || skip) ath10k_warn(ar, "failed to flush transmit queue (skip %i ar-state %i): %ld\n", skip, ar->state, time_left); +} -skip: +static void ath10k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) +{ + struct ath10k *ar = hw->priv; + + if (drop) + return; + + mutex_lock(&ar->conf_mutex); + ath10k_mac_wait_tx_complete(ar); mutex_unlock(&ar->conf_mutex); } diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h index 81f8d6c0af35..570493d2d648 100644 --- a/drivers/net/wireless/ath/ath10k/mac.h +++ b/drivers/net/wireless/ath/ath10k/mac.h @@ -82,6 +82,7 @@ struct ieee80211_txq *ath10k_mac_txq_lookup(struct ath10k *ar, u16 peer_id, u8 tid); int ath10k_mac_ext_resource_config(struct ath10k *ar, u32 val); +void ath10k_mac_wait_tx_complete(struct ath10k *ar); static inline void ath10k_tx_h_seq_no(struct ieee80211_vif *vif, struct sk_buff *skb) diff --git a/drivers/net/wireless/ath/ath10k/wow.c b/drivers/net/wireless/ath/ath10k/wow.c index a6b179f88d36..af444dfecae9 100644 --- a/drivers/net/wireless/ath/ath10k/wow.c +++ b/drivers/net/wireless/ath/ath10k/wow.c @@ -374,6 +374,8 @@ int ath10k_wow_op_suspend(struct ieee80211_hw *hw, goto cleanup; } + ath10k_mac_wait_tx_complete(ar); + ret = ath10k_wow_enable(ar); if (ret) { ath10k_warn(ar, "failed to start wow: %d\n", ret); From e12e643c1dfb46a807a9c03149ad81b11c9db683 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 28 Aug 2018 19:48:46 +0300 Subject: [PATCH 03/51] ath6kl: convert to using %pOFn instead of device_node.name In preparation to remove the node name pointer from struct device_node, convert printf users to use the %pOFn format specifier. Cc: Kalle Valo Cc: "David S. Miller" Cc: linux-wireless@vger.kernel.org Cc: netdev@vger.kernel.org Signed-off-by: Rob Herring Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/init.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 58fb227a849f..54132af70094 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -710,8 +710,8 @@ static bool check_device_tree(struct ath6kl *ar) for_each_compatible_node(node, NULL, "atheros,ath6kl") { board_id = of_get_property(node, board_id_prop, NULL); if (board_id == NULL) { - ath6kl_warn("No \"%s\" property on %s node.\n", - board_id_prop, node->name); + ath6kl_warn("No \"%s\" property on %pOFn node.\n", + board_id_prop, node); continue; } snprintf(board_filename, sizeof(board_filename), From f1908735f141f662fbf04436e6efc1560418b5a0 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Tue, 12 Jun 2018 13:39:06 +0200 Subject: [PATCH 04/51] ath10k: allow ATH10K_SNOC with COMPILE_TEST ATH10K_SNOC builds just fine with COMPILE_TEST, so make that possible. Signed-off-by: Niklas Cassel Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig index 54ff5930126c..6572a43590a8 100644 --- a/drivers/net/wireless/ath/ath10k/Kconfig +++ b/drivers/net/wireless/ath/ath10k/Kconfig @@ -42,7 +42,8 @@ config ATH10K_USB config ATH10K_SNOC tristate "Qualcomm ath10k SNOC support (EXPERIMENTAL)" - depends on ATH10K && ARCH_QCOM + depends on ATH10K + depends on ARCH_QCOM || COMPILE_TEST ---help--- This module adds support for integrated WCN3990 chip connected to system NOC(SNOC). Currently work in progress and will not From 5849ed48d226523fd53863104d6aff5052674c1b Mon Sep 17 00:00:00 2001 From: Sathishkumar Muruganandam Date: Mon, 3 Sep 2018 20:07:50 +0300 Subject: [PATCH 05/51] ath10k: refactoring needed for extended board data download Just moving functions down in the file, no functional changes. Signed-off-by: Sathishkumar Muruganandam Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 402 ++++++++++++------------- 1 file changed, 201 insertions(+), 201 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index f5f71c02fc2b..2e40d891d67e 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -777,149 +777,6 @@ static int ath10k_push_board_ext_data(struct ath10k *ar, const void *data, 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 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, &address); - if (ret) { - ath10k_err(ar, "could not read board data addr (%d)\n", ret); - goto exit; - } - - ret = ath10k_bmi_write_memory(ar, 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; - } - -exit: - return ret; -} - -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_core_get_board_id_from_otp(struct ath10k *ar) { u32 result, address; @@ -1070,64 +927,6 @@ static int ath10k_core_check_dt(struct ath10k *ar) 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->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 %pK 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) - 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_fw(struct ath10k *ar) { u32 address, data_len; @@ -1505,6 +1304,207 @@ static int ath10k_core_fetch_board_file(struct ath10k *ar) 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 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, &address); + if (ret) { + ath10k_err(ar, "could not read board data addr (%d)\n", ret); + goto exit; + } + + ret = ath10k_bmi_write_memory(ar, 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; + } + +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 %pK 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) + 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; +} + int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name, struct ath10k_fw_file *fw_file) { From 31324d17976ed063839db5de3ce0b37a48dd0439 Mon Sep 17 00:00:00 2001 From: Sathishkumar Muruganandam Date: Mon, 3 Sep 2018 20:07:53 +0300 Subject: [PATCH 06/51] ath10k: support extended board data download for dual-band QCA9984 To support dual-band variant of QCA9984, new extended board data (eBDF) is introduced since existing board data ran out of space. Below is the brief implementation & design detail, ---------------------------------------------------- 1. New OTP changes to inform eBDF support in existing OTP download to fetch board ID and chip ID. This is backward compatible and older card sends 0 by default for eBDF support bit (bit 18 of OTP response) we check in ath10k driver. 2. If eBDF is supported, then we need to fetch eBDF ID which is bundled in downloaded board data. So again OTP is executed for knowing the eBDF ID. This is done once we set 'board_data_initialized' bit. If eBDF ID returned is zero, we continue booting with previous board data downloaded. 3. Based on the eBDF ID fetched, ath10k driver tries to download the extended board data to a new offset ahead of already downloaded board data address. 4. A new BD IE type, ATH10K_BD_IE_BOARD_EXT is added to differentiate in bundling eBDF separately in board-2.bin and also to parse through board bundle for eBDF download in ath10k boot. 5. If eBDF is not present in the board-2.bin bundle or when board ID is zero, we do a fallback boot to "eboard.bin" in the same QCA9984/hw1.0 dir. This is same as done to existing "board.bin" if board ID is not present in board-2.bin bundle. Current design is that eBDF size will be 2KB and eBDF ID will be byte value. Tested the above changes with dual-band variant of QCA9984 card. OTP update needed for the test will be part of next FW release 10.4-3.6-xxxx. Below are the logs with ath10k BOOT debugs enabled. First OTP response : --------------------- .. boot upload otp to 0x1234 len 9478 for board id boot get otp board id result 0x00040400 board_id 1 chip_id 0 ext_bid_support 1 .. Second OTP response : --------------------- .. boot upload otp to 0x1234 len 9478 for ext board id boot get otp ext board id result 0x00000005 ext_board_id 5 boot using eboard name 'bus=pci,bmi-chip-id=0,bmi-eboard-id=5' .. Extended board data download: ------------------------------ .. board name 00000000: 62 75 73 3d 70 63 69 2c 62 6d 69 2d 63 68 69 70 bus=pci,bmi-chip 00000010: 2d 69 64 3d 30 2c 62 6d 69 2d 65 62 6f 61 72 64 -id=0,bmi-eboard 00000020: 2d 69 64 3d 35 -id=5 boot found match for name 'bus=pci,bmi-chip-id=0,bmi-eboard-id=5' boot found eboard data for 'bus=pci,bmi-chip-id=0,bmi-eboard-id=5' using board api 2 boot writing ext board data to addr 0xc3000 .. Fallback Extended board data download from "eboard.bin": --------------------------------------------------------- .. board name 00000000: 62 75 73 3d 70 63 69 2c 62 6d 69 2d 63 68 69 70 bus=pci,bmi-chip 00000010: 2d 69 64 3d 30 2c 62 6d 69 2d 62 6f 61 72 64 2d -id=0,bmi-board- 00000020: 69 64 3d 31 30 id=10 failed to fetch board data for bus=pci,bmi-chip-id=0,bmi-eboard-id=5 from ath10k/QCA9984/hw1.0/board-2.bin boot fw request 'ath10k/QCA9984/hw1.0/eboard.bin': 0 using board api 1 boot writing ext board data to addr 0xc3000 .. Signed-off-by: Sathishkumar Muruganandam Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/bmi.h | 5 + drivers/net/wireless/ath/ath10k/core.c | 248 ++++++++++++++++---- drivers/net/wireless/ath/ath10k/core.h | 5 + drivers/net/wireless/ath/ath10k/hw.h | 4 + drivers/net/wireless/ath/ath10k/targaddrs.h | 4 + 5 files changed, 224 insertions(+), 42 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/bmi.h b/drivers/net/wireless/ath/ath10k/bmi.h index 9a396817aa55..28f49441ba46 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.h +++ b/drivers/net/wireless/ath/ath10k/bmi.h @@ -86,6 +86,10 @@ enum bmi_cmd_id { #define BMI_PARAM_GET_FLASH_BOARD_ID 0x8000 #define BMI_PARAM_FLASH_SECTION_ALL 0x10000 +/* Dual-band Extended Board ID */ +#define BMI_PARAM_GET_EXT_BOARD_ID 0x40000 +#define ATH10K_BMI_EXT_BOARD_ID_SUPPORT 0x40000 + #define ATH10K_BMI_BOARD_ID_FROM_OTP_MASK 0x7c00 #define ATH10K_BMI_BOARD_ID_FROM_OTP_LSB 10 @@ -93,6 +97,7 @@ enum bmi_cmd_id { #define ATH10K_BMI_CHIP_ID_FROM_OTP_LSB 15 #define ATH10K_BMI_BOARD_ID_STATUS_MASK 0xff +#define ATH10K_BMI_EBOARD_ID_STATUS_MASK 0xff struct bmi_cmd { __le32 id; /* enum bmi_cmd_id */ diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 2e40d891d67e..35899d775346 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -355,8 +355,10 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .fw = { .dir = QCA9984_HW_1_0_FW_DIR, .board = QCA9984_HW_1_0_BOARD_DATA_FILE, + .eboard = QCA9984_HW_1_0_EBOARD_DATA_FILE, .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, .hw_ops = &qca99x0_ops, @@ -781,6 +783,7 @@ 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; @@ -820,10 +823,13 @@ static int ath10k_core_get_board_id_from_otp(struct ath10k *ar) 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\n", - result, board_id, chip_id); + "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)) { @@ -964,9 +970,15 @@ static 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; } static void ath10k_core_free_firmware_files(struct ath10k *ar) @@ -1020,28 +1032,47 @@ static int ath10k_fetch_cal_file(struct ath10k *ar) return 0; } -static int ath10k_core_fetch_board_data_api_1(struct ath10k *ar) +static int ath10k_core_fetch_board_data_api_1(struct ath10k *ar, int bd_ie_type) { - if (!ar->hw_params.fw.board) { - ath10k_err(ar, "failed to find board file fw entry\n"); - return -EINVAL; + const struct firmware *fw; + + if (bd_ie_type == ATH10K_BD_IE_BOARD) { + if (!ar->hw_params.fw.board) { + ath10k_err(ar, "failed to find board file fw entry\n"); + return -EINVAL; + } + + ar->normal_mode_fw.board = ath10k_fetch_fw_file(ar, + ar->hw_params.fw.dir, + ar->hw_params.fw.board); + 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) { + if (!ar->hw_params.fw.eboard) { + ath10k_err(ar, "failed to find eboard file fw entry\n"); + return -EINVAL; + } + + fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, + ar->hw_params.fw.eboard); + 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; } - ar->normal_mode_fw.board = ath10k_fetch_fw_file(ar, - ar->hw_params.fw.dir, - ar->hw_params.fw.board); - 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; - return 0; } static int ath10k_core_parse_bd_ie_board(struct ath10k *ar, const void *buf, size_t buf_len, - const char *boardname) + const char *boardname, + int bd_ie_type) { const struct ath10k_fw_ie *hdr; bool name_match_found; @@ -1090,12 +1121,21 @@ static int ath10k_core_parse_bd_ie_board(struct ath10k *ar, /* no match found */ break; - ath10k_dbg(ar, ATH10K_DBG_BOOT, - "boot found board data for '%s'", - boardname); + 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; + 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; @@ -1145,7 +1185,18 @@ static int ath10k_core_search_bd(struct ath10k *ar, switch (ie_id) { case ATH10K_BD_IE_BOARD: ret = ath10k_core_parse_bd_ie_board(ar, data, ie_len, - boardname); + 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; @@ -1175,9 +1226,11 @@ static int ath10k_core_fetch_board_data_api_n(struct ath10k *ar, const u8 *data; int ret; - ar->normal_mode_fw.board = ath10k_fetch_fw_file(ar, - ar->hw_params.fw.dir, - filename); + /* 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); @@ -1265,23 +1318,49 @@ static int ath10k_core_create_board_name(struct ath10k *ar, char *name, return 0; } -static int ath10k_core_fetch_board_file(struct ath10k *ar) +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; +} + +static int ath10k_core_fetch_board_file(struct ath10k *ar, int bd_ie_type) { char boardname[100], fallback_boardname[100]; int ret; - ret = ath10k_core_create_board_name(ar, boardname, - sizeof(boardname), true); - if (ret) { - ath10k_err(ar, "failed to create board name: %d", ret); - return ret; - } + if (bd_ie_type == ATH10K_BD_IE_BOARD) { + ret = ath10k_core_create_board_name(ar, boardname, + sizeof(boardname), true); + if (ret) { + ath10k_err(ar, "failed to create board name: %d", ret); + return ret; + } - ret = ath10k_core_create_board_name(ar, fallback_boardname, - sizeof(boardname), false); - if (ret) { - ath10k_err(ar, "failed to create fallback board name: %d", ret); - return ret; + ret = ath10k_core_create_board_name(ar, fallback_boardname, + sizeof(boardname), false); + if (ret) { + ath10k_err(ar, "failed to create 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; @@ -1291,8 +1370,9 @@ static int ath10k_core_fetch_board_file(struct ath10k *ar) if (!ret) goto success; +fallback: ar->bd_api = 1; - ret = ath10k_core_fetch_board_data_api_1(ar); + 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); @@ -1304,11 +1384,65 @@ static int ath10k_core_fetch_board_file(struct ath10k *ar) return 0; } +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 address; + 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); @@ -1317,13 +1451,13 @@ static int ath10k_download_board_data(struct ath10k *ar, const void *data, goto exit; } - ret = ath10k_bmi_read32(ar, hi_board_data, &address); + 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, address, data, + ret = ath10k_bmi_write_memory(ar, board_address, data, min_t(u32, board_data_size, data_len)); if (ret) { @@ -1337,6 +1471,36 @@ static int ath10k_download_board_data(struct ath10k *ar, const void *data, 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; } @@ -2609,7 +2773,7 @@ static int ath10k_core_probe_fw(struct ath10k *ar) if (ret) ath10k_dbg(ar, ATH10K_DBG_BOOT, "DT bdf variant name not set.\n"); - ret = ath10k_core_fetch_board_file(ar); + ret = ath10k_core_fetch_board_file(ar, ATH10K_BD_IE_BOARD); if (ret) { ath10k_err(ar, "failed to fetch board file: %d\n", ret); goto err_free_firmware_files; diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 9feea02e7d37..f6f5b51848df 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -861,6 +861,9 @@ struct ath10k_fw_components { const struct firmware *board; const void *board_data; size_t board_len; + const struct firmware *ext_board; + const void *ext_board_data; + size_t ext_board_len; struct ath10k_fw_file fw_file; }; @@ -947,7 +950,9 @@ struct ath10k { bool bmi_ids_valid; u8 bmi_board_id; + u8 bmi_eboard_id; u8 bmi_chip_id; + bool ext_bid_supported; char bdf_ext[ATH10K_SMBIOS_BDF_EXT_STR_LENGTH]; } id; diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index fac58c3c576a..33833d010a76 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -109,6 +109,7 @@ enum qca9377_chip_id_rev { #define QCA9984_HW_1_0_CHIP_ID_REV 0x0 #define QCA9984_HW_1_0_FW_DIR ATH10K_FW_DIR "/QCA9984/hw1.0" #define QCA9984_HW_1_0_BOARD_DATA_FILE "board.bin" +#define QCA9984_HW_1_0_EBOARD_DATA_FILE "eboard.bin" #define QCA9984_HW_1_0_PATCH_LOAD_ADDR 0x1234 /* QCA9888 2.0 defines */ @@ -221,6 +222,7 @@ enum ath10k_fw_htt_op_version { enum ath10k_bd_ie_type { /* contains sub IEs of enum ath10k_bd_ie_board_type */ ATH10K_BD_IE_BOARD = 0, + ATH10K_BD_IE_BOARD_EXT = 1, }; enum ath10k_bd_ie_board_type { @@ -539,6 +541,8 @@ struct ath10k_hw_params { const char *dir; const char *board; size_t board_size; + const char *eboard; + size_t ext_board_size; size_t board_ext_size; } fw; diff --git a/drivers/net/wireless/ath/ath10k/targaddrs.h b/drivers/net/wireless/ath/ath10k/targaddrs.h index c2b5bad0459b..b11a1c3d87b4 100644 --- a/drivers/net/wireless/ath/ath10k/targaddrs.h +++ b/drivers/net/wireless/ath/ath10k/targaddrs.h @@ -484,6 +484,10 @@ struct host_interest { #define QCA99X0_BOARD_DATA_SZ 12288 #define QCA99X0_BOARD_EXT_DATA_SZ 0 +/* Dual band extended board data */ +#define QCA99X0_EXT_BOARD_DATA_SZ 2048 +#define EXT_BOARD_ADDRESS_OFFSET 0x3000 + #define QCA4019_BOARD_DATA_SZ 12064 #define QCA4019_BOARD_EXT_DATA_SZ 0 From 9abcb9371bccc8aed6fea273058c322f4a886a2d Mon Sep 17 00:00:00 2001 From: Govind Singh Date: Mon, 3 Sep 2018 20:07:58 +0300 Subject: [PATCH 07/51] ath10k: introduce CE_ATTR_POLL attribute for polling pipe Existing copy engine interrupt enable logic assumes that last CE is using polling mode and due to this interrupt for last copy engine are always disabled. WCN3990 uses last CE for pktlog and interrupt remains disabled with existing logic. To mitigate this issue, introduce CE_ATTR_POLL flag and control the interrupt based on the flag which can be set in ce_attr. Testing: Tested on WCN3990 and QCA6174 HW. Tested FW: WLAN.HL.2.0-01192-QCAHLSWMTPLZ-1, WLAN.RM.4.4.1-00109-QCARMSWPZ-1 Signed-off-by: Govind Singh Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/ce.c | 18 ++++++++++++++---- drivers/net/wireless/ath/ath10k/ce.h | 11 +++++++---- drivers/net/wireless/ath/ath10k/pci.c | 2 +- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index d0381aaad01c..f6d3ecbdd3a3 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -1280,10 +1280,17 @@ static void ath10k_ce_per_engine_handler_adjust(struct ath10k_ce_pipe *ce_state) int ath10k_ce_disable_interrupts(struct ath10k *ar) { + struct ath10k_ce *ce = ath10k_ce_priv(ar); + struct ath10k_ce_pipe *ce_state; + u32 ctrl_addr; int ce_id; for (ce_id = 0; ce_id < CE_COUNT; ce_id++) { - u32 ctrl_addr = ath10k_ce_base_address(ar, ce_id); + ce_state = &ce->ce_states[ce_id]; + if (ce_state->attr_flags & CE_ATTR_POLL) + continue; + + ctrl_addr = ath10k_ce_base_address(ar, ce_id); ath10k_ce_copy_complete_intr_disable(ar, ctrl_addr); ath10k_ce_error_intr_disable(ar, ctrl_addr); @@ -1300,11 +1307,14 @@ void ath10k_ce_enable_interrupts(struct ath10k *ar) int ce_id; struct ath10k_ce_pipe *ce_state; - /* Skip the last copy engine, CE7 the diagnostic window, as that - * uses polling and isn't initialized for interrupts. + /* Enable interrupts for copy engine that + * are not using polling mode. */ - for (ce_id = 0; ce_id < CE_COUNT - 1; ce_id++) { + for (ce_id = 0; ce_id < CE_COUNT; ce_id++) { ce_state = &ce->ce_states[ce_id]; + if (ce_state->attr_flags & CE_ATTR_POLL) + continue; + ath10k_ce_per_engine_handler_adjust(ce_state); } } diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h index b8fb5382dede..ead9987c3259 100644 --- a/drivers/net/wireless/ath/ath10k/ce.h +++ b/drivers/net/wireless/ath/ath10k/ce.h @@ -275,16 +275,19 @@ void ath10k_ce_free_rri(struct ath10k *ar); /* ce_attr.flags values */ /* Use NonSnooping PCIe accesses? */ -#define CE_ATTR_NO_SNOOP 1 +#define CE_ATTR_NO_SNOOP BIT(0) /* Byte swap data words */ -#define CE_ATTR_BYTE_SWAP_DATA 2 +#define CE_ATTR_BYTE_SWAP_DATA BIT(1) /* Swizzle descriptors? */ -#define CE_ATTR_SWIZZLE_DESCRIPTORS 4 +#define CE_ATTR_SWIZZLE_DESCRIPTORS BIT(2) /* no interrupt on copy completion */ -#define CE_ATTR_DIS_INTR 8 +#define CE_ATTR_DIS_INTR BIT(3) + +/* no interrupt, only polling */ +#define CE_ATTR_POLL BIT(4) /* Attributes of an instance of a Copy Engine */ struct ce_attr { diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 97fa5c74f2fe..7122e6aedc1e 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -192,7 +192,7 @@ static struct ce_attr host_ce_config_wlan[] = { /* CE7: ce_diag, the Diagnostic Window */ { - .flags = CE_ATTR_FLAGS, + .flags = CE_ATTR_FLAGS | CE_ATTR_POLL, .src_nentries = 2, .src_sz_max = DIAG_TRANSFER_LIMIT, .dest_nentries = 2, From 713358c321f4f8553afe956c0d5e9a87b43d561e Mon Sep 17 00:00:00 2001 From: Govind Singh Date: Mon, 3 Sep 2018 20:08:01 +0300 Subject: [PATCH 08/51] ath10k: enable pktlog for WCN3990 target WCN3990 target uses separate htc service for pktlog. Add pktlog service request and support for pktlog rx path handling. Testing: Tested on WCN3990 and QCA6174 HW. Tested FW: WLAN.HL.2.0-01192-QCAHLSWMTPLZ-1, WLAN.RM.4.4.1-00109-QCARMSWPZ-1 Signed-off-by: Govind Singh Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htc.c | 63 +++++++++++++++++++++++++- drivers/net/wireless/ath/ath10k/pci.c | 2 +- drivers/net/wireless/ath/ath10k/snoc.c | 13 +++++- 3 files changed, 74 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index 331b8d558791..4e70ff2f78ae 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -803,8 +803,11 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc, ep->service_id, &ep->ul_pipe_id, &ep->dl_pipe_id); - if (status) + if (status) { + ath10k_warn(ar, "unsupported HTC service id: %d\n", + ep->service_id); return status; + } ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot htc service '%s' ul pipe %d dl pipe %d eid %d ready\n", @@ -838,6 +841,56 @@ struct sk_buff *ath10k_htc_alloc_skb(struct ath10k *ar, int size) return skb; } +static void ath10k_htc_pktlog_process_rx(struct ath10k *ar, struct sk_buff *skb) +{ + trace_ath10k_htt_pktlog(ar, skb->data, skb->len); + dev_kfree_skb_any(skb); +} + +static int ath10k_htc_pktlog_connect(struct ath10k *ar) +{ + struct ath10k_htc_svc_conn_resp conn_resp; + struct ath10k_htc_svc_conn_req conn_req; + int status; + + memset(&conn_req, 0, sizeof(conn_req)); + memset(&conn_resp, 0, sizeof(conn_resp)); + + conn_req.ep_ops.ep_tx_complete = NULL; + conn_req.ep_ops.ep_rx_complete = ath10k_htc_pktlog_process_rx; + conn_req.ep_ops.ep_tx_credits = NULL; + + /* connect to control service */ + conn_req.service_id = ATH10K_HTC_SVC_ID_HTT_LOG_MSG; + status = ath10k_htc_connect_service(&ar->htc, &conn_req, &conn_resp); + if (status) { + ath10k_warn(ar, "failed to connect to PKTLOG service: %d\n", + status); + return status; + } + + return 0; +} + +static bool ath10k_htc_pktlog_svc_supported(struct ath10k *ar) +{ + u8 ul_pipe_id; + u8 dl_pipe_id; + int status; + + status = ath10k_hif_map_service_to_pipe(ar, ATH10K_HTC_SVC_ID_HTT_LOG_MSG, + &ul_pipe_id, + &dl_pipe_id); + if (status) { + ath10k_warn(ar, "unsupported HTC service id: %d\n", + ATH10K_HTC_SVC_ID_HTT_LOG_MSG); + + return false; + } + + return true; +} + int ath10k_htc_start(struct ath10k_htc *htc) { struct ath10k *ar = htc->ar; @@ -871,6 +924,14 @@ int ath10k_htc_start(struct ath10k_htc *htc) return status; } + if (ath10k_htc_pktlog_svc_supported(ar)) { + status = ath10k_htc_pktlog_connect(ar); + if (status) { + ath10k_err(ar, "failed to connect to pktlog: %d\n", status); + return status; + } + } + return 0; } diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 7122e6aedc1e..7db4a7915624 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -1839,7 +1839,7 @@ int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar, u16 service_id, } } - if (WARN_ON(!ul_set || !dl_set)) + if (!ul_set || !dl_set) return -ENOENT; return 0; diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index fa1843a7e0fd..eabf4229d929 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -62,6 +62,7 @@ static void ath10k_snoc_htt_tx_cb(struct ath10k_ce_pipe *ce_state); static void ath10k_snoc_htc_rx_cb(struct ath10k_ce_pipe *ce_state); static void ath10k_snoc_htt_rx_cb(struct ath10k_ce_pipe *ce_state); static void ath10k_snoc_htt_htc_rx_cb(struct ath10k_ce_pipe *ce_state); +static void ath10k_snoc_pktlog_rx_cb(struct ath10k_ce_pipe *ce_state); static const struct ath10k_snoc_drv_priv drv_priv = { .hw_rev = ATH10K_HW_WCN3990, @@ -171,7 +172,7 @@ static struct ce_attr host_ce_config_wlan[] = { .src_nentries = 0, .src_sz_max = 2048, .dest_nentries = 512, - .recv_cb = ath10k_snoc_htt_htc_rx_cb, + .recv_cb = ath10k_snoc_pktlog_rx_cb, }, }; @@ -436,6 +437,14 @@ static void ath10k_snoc_htt_htc_rx_cb(struct ath10k_ce_pipe *ce_state) ath10k_snoc_process_rx_cb(ce_state, ath10k_htc_rx_completion_handler); } +/* Called by lower (CE) layer when data is received from the Target. + * WCN3990 firmware uses separate CE(CE11) to transfer pktlog data. + */ +static void ath10k_snoc_pktlog_rx_cb(struct ath10k_ce_pipe *ce_state) +{ + ath10k_snoc_process_rx_cb(ce_state, ath10k_htc_rx_completion_handler); +} + static void ath10k_snoc_htt_rx_deliver(struct ath10k *ar, struct sk_buff *skb) { skb_pull(skb, sizeof(struct ath10k_htc_hdr)); @@ -616,7 +625,7 @@ static int ath10k_snoc_hif_map_service_to_pipe(struct ath10k *ar, } } - if (WARN_ON(!ul_set || !dl_set)) + if (!ul_set || !dl_set) return -ENOENT; return 0; From 49925f24701600433d0342135e9963818c6b0238 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 29 Aug 2018 12:50:18 -0500 Subject: [PATCH 09/51] wil6210: fix unsigned cid comparison with >= 0 The comparison of cid >= 0 is always true because cid is of type u8 (8 bits, unsigned). Fix this by removing such comparison and updating the type of variable cid to u8 in the caller function. Addresses-Coverity-ID: 1473079 ("Unsigned compared against 0") Fixes: b9010f105f21 ("wil6210: add FT roam support for AP and station") Signed-off-by: Gustavo A. R. Silva Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/wil6210.h | 2 +- drivers/net/wireless/ath/wil6210/wmi.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index cf6a69198b5d..abb82018d3b4 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -455,7 +455,7 @@ static inline void parse_cidxtid(u8 cidxtid, u8 *cid, u8 *tid) */ static inline bool wil_cid_valid(u8 cid) { - return (cid >= 0 && cid < WIL6210_MAX_CID); + return cid < WIL6210_MAX_CID; } struct wil6210_mbox_ring { diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index c3ad8e4df3ec..4859f0e43658 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -1177,7 +1177,7 @@ static void wmi_evt_ring_en(struct wil6210_vif *vif, int id, void *d, int len) u8 vri = evt->ring_index; struct wireless_dev *wdev = vif_to_wdev(vif); struct wil_sta_info *sta; - int cid; + u8 cid; struct key_params params; wil_dbg_wmi(wil, "Enable vring %d MID %d\n", vri, vif->mid); From 0189dbd71cbd18386c7395e41c5816ae541f3e8a Mon Sep 17 00:00:00 2001 From: Anilkumar Kolli Date: Tue, 4 Sep 2018 12:15:13 +0530 Subject: [PATCH 10/51] ath10k: get the legacy rate index to update the txrate table Get the legacy rate index to update the pkts/bytes counter against each possible tx rate. Signed-off-by: Anilkumar Kolli Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt_rx.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 4d1cd90d6d27..01eb4a7f4a69 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -2488,7 +2488,7 @@ void ath10k_htt_htc_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) dev_kfree_skb_any(skb); } -static inline bool is_valid_legacy_rate(u8 rate) +static inline int ath10k_get_legacy_rate_idx(struct ath10k *ar, u8 rate) { static const u8 legacy_rates[] = {1, 2, 5, 11, 6, 9, 12, 18, 24, 36, 48, 54}; @@ -2496,10 +2496,11 @@ static inline bool is_valid_legacy_rate(u8 rate) for (i = 0; i < ARRAY_SIZE(legacy_rates); i++) { if (rate == legacy_rates[i]) - return true; + return i; } - return false; + ath10k_warn(ar, "Invalid legacy rate %hhd peer stats", rate); + return -EINVAL; } static void @@ -2508,7 +2509,7 @@ ath10k_update_per_peer_tx_stats(struct ath10k *ar, struct ath10k_per_peer_tx_stats *peer_stats) { struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv; - u8 rate = 0, sgi; + u8 rate = 0, rate_idx = 0, sgi; struct rate_info txrate; lockdep_assert_held(&ar->data_lock); @@ -2536,17 +2537,12 @@ ath10k_update_per_peer_tx_stats(struct ath10k *ar, if (txrate.flags == WMI_RATE_PREAMBLE_CCK || txrate.flags == WMI_RATE_PREAMBLE_OFDM) { rate = ATH10K_HW_LEGACY_RATE(peer_stats->ratecode); - - if (!is_valid_legacy_rate(rate)) { - ath10k_warn(ar, "Invalid legacy rate %hhd peer stats", - rate); - return; - } - /* This is hacky, FW sends CCK rate 5.5Mbps as 6 */ - rate *= 10; - if (rate == 60 && txrate.flags == WMI_RATE_PREAMBLE_CCK) - rate = rate - 5; + if (rate == 6 && txrate.flags == WMI_RATE_PREAMBLE_CCK) + rate = 5; + rate_idx = ath10k_get_legacy_rate_idx(ar, rate); + if (rate_idx < 0) + return; arsta->txrate.legacy = rate; } else if (txrate.flags == WMI_RATE_PREAMBLE_HT) { arsta->txrate.flags = RATE_INFO_FLAGS_MCS; From 348cd95c8196818613980983b277faae8a3d1fd5 Mon Sep 17 00:00:00 2001 From: Anilkumar Kolli Date: Tue, 4 Sep 2018 12:15:14 +0530 Subject: [PATCH 11/51] ath10k: add debugfs entry to enable extended tx stats This patch adds debugfs entry to enable/disable extended tx statistics. Extended tx statistics are from peer stats feature. Signed-off-by: Anilkumar Kolli Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 1 + drivers/net/wireless/ath/ath10k/debug.c | 62 ++++++++++++++++++++++++- drivers/net/wireless/ath/ath10k/debug.h | 9 ++++ 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index f6f5b51848df..999a6d2ef578 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -607,6 +607,7 @@ struct ath10k_debug { u32 reg_addr; u32 nf_cal_period; void *cal_data; + u32 enable_extd_tx_stats; }; enum ath10k_state { diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 0baaad90b8d1..ada29a4cc9c8 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -2042,6 +2042,61 @@ static const struct file_operations fops_btcoex = { .open = simple_open }; +static ssize_t ath10k_write_enable_extd_tx_stats(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + u32 filter; + int ret; + + if (kstrtouint_from_user(ubuf, count, 0, &filter)) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH10K_STATE_ON) { + ar->debug.enable_extd_tx_stats = filter; + ret = count; + goto out; + } + + if (filter == ar->debug.enable_extd_tx_stats) { + ret = count; + goto out; + } + + ar->debug.enable_extd_tx_stats = filter; + ret = count; + +out: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static ssize_t ath10k_read_enable_extd_tx_stats(struct file *file, + char __user *ubuf, + size_t count, loff_t *ppos) + +{ + char buf[32]; + struct ath10k *ar = file->private_data; + int len = 0; + + mutex_lock(&ar->conf_mutex); + len = scnprintf(buf, sizeof(buf) - len, "%08x\n", + ar->debug.enable_extd_tx_stats); + mutex_unlock(&ar->conf_mutex); + + return simple_read_from_buffer(ubuf, count, ppos, buf, len); +} + +static const struct file_operations fops_enable_extd_tx_stats = { + .read = ath10k_read_enable_extd_tx_stats, + .write = ath10k_write_enable_extd_tx_stats, + .open = simple_open +}; + static ssize_t ath10k_write_peer_stats(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) @@ -2454,10 +2509,15 @@ int ath10k_debug_register(struct ath10k *ar) debugfs_create_file("btcoex", 0644, ar->debug.debugfs_phy, ar, &fops_btcoex); - if (test_bit(WMI_SERVICE_PEER_STATS, ar->wmi.svc_map)) + if (test_bit(WMI_SERVICE_PEER_STATS, ar->wmi.svc_map)) { debugfs_create_file("peer_stats", 0644, ar->debug.debugfs_phy, ar, &fops_peer_stats); + debugfs_create_file("enable_extd_tx_stats", 0644, + ar->debug.debugfs_phy, ar, + &fops_enable_extd_tx_stats); + } + debugfs_create_file("fw_checksums", 0400, ar->debug.debugfs_phy, ar, &fops_fw_checksums); diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h index 0afca5c106b6..3a6191cff2f9 100644 --- a/drivers/net/wireless/ath/ath10k/debug.h +++ b/drivers/net/wireless/ath/ath10k/debug.h @@ -128,6 +128,10 @@ static inline u32 ath10k_debug_get_fw_dbglog_level(struct ath10k *ar) return ar->debug.fw_dbglog_level; } +static inline int ath10k_debug_is_extd_tx_stats_enabled(struct ath10k *ar) +{ + return ar->debug.enable_extd_tx_stats; +} #else static inline int ath10k_debug_start(struct ath10k *ar) @@ -190,6 +194,11 @@ static inline u32 ath10k_debug_get_fw_dbglog_level(struct ath10k *ar) return 0; } +static inline int ath10k_debug_is_extd_tx_stats_enabled(struct ath10k *ar) +{ + return 0; +} + #define ATH10K_DFS_STAT_INC(ar, c) do { } while (0) #define ath10k_debug_get_et_strings NULL From a904417fc8761b78d46342d13a6fd08f4beb7075 Mon Sep 17 00:00:00 2001 From: Anilkumar Kolli Date: Tue, 4 Sep 2018 12:15:15 +0530 Subject: [PATCH 12/51] ath10k: add extended per sta tx statistics support This patch adds per station tx statistics support. Per station tx stats include - pkts/bytes transmitted at all possible rates(mcs/nss/bw/gi). - ACK fails count - ampdu bytes/pkts transmitted at all possible rates(mcs/nss/bw/gi). - BA fails count Tested on QCA9984/QCA4019/QCA988x Firmware: 10.4-3.5.3-00057 10.2.4-1.0-00037 Signed-off-by: Anilkumar Kolli Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 31 +++++++ drivers/net/wireless/ath/ath10k/htt_rx.c | 109 +++++++++++++++++++++++ drivers/net/wireless/ath/ath10k/mac.c | 10 +++ drivers/net/wireless/ath/ath10k/wmi.h | 11 ++- 4 files changed, 158 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 999a6d2ef578..27df8ac26254 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -461,6 +461,36 @@ struct ath10k_sta_tid_stats { unsigned long int rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_MAX]; }; +enum ath10k_counter_type { + ATH10K_COUNTER_TYPE_BYTES, + ATH10K_COUNTER_TYPE_PKTS, + ATH10K_COUNTER_TYPE_MAX, +}; + +enum ath10k_stats_type { + ATH10K_STATS_TYPE_SUCC, + ATH10K_STATS_TYPE_FAIL, + ATH10K_STATS_TYPE_RETRY, + ATH10K_STATS_TYPE_AMPDU, + ATH10K_STATS_TYPE_MAX, +}; + +struct ath10k_htt_data_stats { + u64 legacy[ATH10K_COUNTER_TYPE_MAX][ATH10K_LEGACY_NUM]; + u64 ht[ATH10K_COUNTER_TYPE_MAX][ATH10K_HT_MCS_NUM]; + u64 vht[ATH10K_COUNTER_TYPE_MAX][ATH10K_VHT_MCS_NUM]; + u64 bw[ATH10K_COUNTER_TYPE_MAX][ATH10K_BW_NUM]; + u64 nss[ATH10K_COUNTER_TYPE_MAX][ATH10K_NSS_NUM]; + u64 gi[ATH10K_COUNTER_TYPE_MAX][ATH10K_GI_NUM]; +}; + +struct ath10k_htt_tx_stats { + struct ath10k_htt_data_stats stats[ATH10K_STATS_TYPE_MAX]; + u64 tx_duration; + u64 ba_fails; + u64 ack_fails; +}; + struct ath10k_sta { struct ath10k_vif *arvif; @@ -474,6 +504,7 @@ struct ath10k_sta { struct work_struct update_wk; u64 rx_duration; + struct ath10k_htt_tx_stats *tx_stats; #ifdef CONFIG_MAC80211_DEBUGFS /* protected by conf_mutex */ diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 01eb4a7f4a69..4b9197b93be1 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -2503,6 +2503,111 @@ static inline int ath10k_get_legacy_rate_idx(struct ath10k *ar, u8 rate) return -EINVAL; } +static void +ath10k_accumulate_per_peer_tx_stats(struct ath10k *ar, + struct ath10k_sta *arsta, + struct ath10k_per_peer_tx_stats *pstats, + u8 legacy_rate_idx) +{ + struct rate_info *txrate = &arsta->txrate; + struct ath10k_htt_tx_stats *tx_stats; + int ht_idx, gi, mcs, bw, nss; + + if (!arsta->tx_stats) + return; + + tx_stats = arsta->tx_stats; + gi = (arsta->txrate.flags & RATE_INFO_FLAGS_SHORT_GI); + ht_idx = txrate->mcs + txrate->nss * 8; + mcs = txrate->mcs; + bw = txrate->bw; + nss = txrate->nss; + +#define STATS_OP_FMT(name) tx_stats->stats[ATH10K_STATS_TYPE_##name] + + if (txrate->flags == RATE_INFO_FLAGS_VHT_MCS) { + STATS_OP_FMT(SUCC).vht[0][mcs] += pstats->succ_bytes; + STATS_OP_FMT(SUCC).vht[1][mcs] += pstats->succ_pkts; + STATS_OP_FMT(FAIL).vht[0][mcs] += pstats->failed_bytes; + STATS_OP_FMT(FAIL).vht[1][mcs] += pstats->failed_pkts; + STATS_OP_FMT(RETRY).vht[0][mcs] += pstats->retry_bytes; + STATS_OP_FMT(RETRY).vht[1][mcs] += pstats->retry_pkts; + } else if (txrate->flags == RATE_INFO_FLAGS_MCS) { + STATS_OP_FMT(SUCC).ht[0][ht_idx] += pstats->succ_bytes; + STATS_OP_FMT(SUCC).ht[1][ht_idx] += pstats->succ_pkts; + STATS_OP_FMT(FAIL).ht[0][ht_idx] += pstats->failed_bytes; + STATS_OP_FMT(FAIL).ht[1][ht_idx] += pstats->failed_pkts; + STATS_OP_FMT(RETRY).ht[0][ht_idx] += pstats->retry_bytes; + STATS_OP_FMT(RETRY).ht[1][ht_idx] += pstats->retry_pkts; + } else { + mcs = legacy_rate_idx; + if (mcs < 0) + return; + + STATS_OP_FMT(SUCC).legacy[0][mcs] += pstats->succ_bytes; + STATS_OP_FMT(SUCC).legacy[1][mcs] += pstats->succ_pkts; + STATS_OP_FMT(FAIL).legacy[0][mcs] += pstats->failed_bytes; + STATS_OP_FMT(FAIL).legacy[1][mcs] += pstats->failed_pkts; + STATS_OP_FMT(RETRY).legacy[0][mcs] += pstats->retry_bytes; + STATS_OP_FMT(RETRY).legacy[1][mcs] += pstats->retry_pkts; + } + + if (ATH10K_HW_AMPDU(pstats->flags)) { + tx_stats->ba_fails += ATH10K_HW_BA_FAIL(pstats->flags); + + if (txrate->flags == RATE_INFO_FLAGS_MCS) { + STATS_OP_FMT(AMPDU).ht[0][ht_idx] += + pstats->succ_bytes + pstats->retry_bytes; + STATS_OP_FMT(AMPDU).ht[1][ht_idx] += + pstats->succ_pkts + pstats->retry_pkts; + } else { + STATS_OP_FMT(AMPDU).vht[0][mcs] += + pstats->succ_bytes + pstats->retry_bytes; + STATS_OP_FMT(AMPDU).vht[1][mcs] += + pstats->succ_pkts + pstats->retry_pkts; + } + STATS_OP_FMT(AMPDU).bw[0][bw] += + pstats->succ_bytes + pstats->retry_bytes; + STATS_OP_FMT(AMPDU).nss[0][nss] += + pstats->succ_bytes + pstats->retry_bytes; + STATS_OP_FMT(AMPDU).gi[0][gi] += + pstats->succ_bytes + pstats->retry_bytes; + STATS_OP_FMT(AMPDU).bw[1][bw] += + pstats->succ_pkts + pstats->retry_pkts; + STATS_OP_FMT(AMPDU).nss[1][nss] += + pstats->succ_pkts + pstats->retry_pkts; + STATS_OP_FMT(AMPDU).gi[1][gi] += + pstats->succ_pkts + pstats->retry_pkts; + } else { + tx_stats->ack_fails += + ATH10K_HW_BA_FAIL(pstats->flags); + } + + STATS_OP_FMT(SUCC).bw[0][bw] += pstats->succ_bytes; + STATS_OP_FMT(SUCC).nss[0][nss] += pstats->succ_bytes; + STATS_OP_FMT(SUCC).gi[0][gi] += pstats->succ_bytes; + + STATS_OP_FMT(SUCC).bw[1][bw] += pstats->succ_pkts; + STATS_OP_FMT(SUCC).nss[1][nss] += pstats->succ_pkts; + STATS_OP_FMT(SUCC).gi[1][gi] += pstats->succ_pkts; + + STATS_OP_FMT(FAIL).bw[0][bw] += pstats->failed_bytes; + STATS_OP_FMT(FAIL).nss[0][nss] += pstats->failed_bytes; + STATS_OP_FMT(FAIL).gi[0][gi] += pstats->failed_bytes; + + STATS_OP_FMT(FAIL).bw[1][bw] += pstats->failed_pkts; + STATS_OP_FMT(FAIL).nss[1][nss] += pstats->failed_pkts; + STATS_OP_FMT(FAIL).gi[1][gi] += pstats->failed_pkts; + + STATS_OP_FMT(RETRY).bw[0][bw] += pstats->retry_bytes; + STATS_OP_FMT(RETRY).nss[0][nss] += pstats->retry_bytes; + STATS_OP_FMT(RETRY).gi[0][gi] += pstats->retry_bytes; + + STATS_OP_FMT(RETRY).bw[1][bw] += pstats->retry_pkts; + STATS_OP_FMT(RETRY).nss[1][nss] += pstats->retry_pkts; + STATS_OP_FMT(RETRY).gi[1][gi] += pstats->retry_pkts; +} + static void ath10k_update_per_peer_tx_stats(struct ath10k *ar, struct ieee80211_sta *sta, @@ -2557,6 +2662,10 @@ ath10k_update_per_peer_tx_stats(struct ath10k *ar, arsta->txrate.nss = txrate.nss; arsta->txrate.bw = ath10k_bw_to_mac80211_bw(txrate.bw); + + if (ath10k_debug_is_extd_tx_stats_enabled(ar)) + ath10k_accumulate_per_peer_tx_stats(ar, arsta, peer_stats, + rate_idx); } static void ath10k_htt_fetch_peer_stats(struct ath10k *ar, diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 078058eef648..ef952a27b0ed 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -6244,6 +6244,13 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, ar->num_stations + 1, ar->max_num_stations, ar->num_peers + 1, ar->max_num_peers); + if (ath10k_debug_is_extd_tx_stats_enabled(ar)) { + arsta->tx_stats = kzalloc(sizeof(*arsta->tx_stats), + GFP_KERNEL); + if (!arsta->tx_stats) + goto exit; + } + num_tdls_stations = ath10k_mac_tdls_vif_stations_count(hw, vif); num_tdls_vifs = ath10k_mac_tdls_vifs_count(hw); @@ -6329,6 +6336,9 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, "mac vdev %d peer delete %pM sta %pK (sta gone)\n", arvif->vdev_id, sta->addr, sta); + if (ath10k_debug_is_extd_tx_stats_enabled(ar)) + kfree(arsta->tx_stats); + if (sta->tdls) { ret = ath10k_mac_tdls_peer_update(ar, arvif->vdev_id, sta, diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 36220258e3c7..a25bf9b876ec 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -4958,10 +4958,15 @@ enum wmi_rate_preamble { #define ATH10K_HW_GI(flags) (((flags) >> 5) & 0x1) #define ATH10K_HW_RATECODE(rate, nss, preamble) \ (((preamble) << 6) | ((nss) << 4) | (rate)) +#define ATH10K_HW_AMPDU(flags) ((flags) & 0x1) +#define ATH10K_HW_BA_FAIL(flags) (((flags) >> 1) & 0x3) -#define VHT_MCS_NUM 10 -#define VHT_BW_NUM 4 -#define VHT_NSS_NUM 4 +#define ATH10K_VHT_MCS_NUM 10 +#define ATH10K_BW_NUM 4 +#define ATH10K_NSS_NUM 4 +#define ATH10K_LEGACY_NUM 12 +#define ATH10K_GI_NUM 2 +#define ATH10K_HT_MCS_NUM 32 /* Value to disable fixed rate setting */ #define WMI_FIXED_RATE_NONE (0xff) From 85dd28eb9917fb22095aab0cf22eaa351bb3e24d Mon Sep 17 00:00:00 2001 From: Anilkumar Kolli Date: Tue, 4 Sep 2018 12:15:16 +0530 Subject: [PATCH 13/51] ath10k: add debugfs support to dump per sta tx stats This patch adds 'tx_stats' in per station debugfs entry. Use this command to dump tx_stats: cat /sys/kernel/debug/ieee80211/phy0/netdev\:wlan0/ stations//tx_stats Signed-off-by: Anilkumar Kolli Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/debugfs_sta.c | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/debugfs_sta.c b/drivers/net/wireless/ath/ath10k/debugfs_sta.c index a63c97e2c50c..929f2f66fdce 100644 --- a/drivers/net/wireless/ath/ath10k/debugfs_sta.c +++ b/drivers/net/wireless/ath/ath10k/debugfs_sta.c @@ -626,9 +626,105 @@ static const struct file_operations fops_tid_stats_dump = { .llseek = default_llseek, }; +static ssize_t ath10k_dbg_sta_dump_tx_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_sta *sta = file->private_data; + struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv; + struct ath10k *ar = arsta->arvif->ar; + struct ath10k_htt_data_stats *stats; + const char *str_name[ATH10K_STATS_TYPE_MAX] = {"succ", "fail", + "retry", "ampdu"}; + const char *str[ATH10K_COUNTER_TYPE_MAX] = {"bytes", "packets"}; + int len = 0, i, j, k, retval = 0; + const int size = 2 * 4096; + char *buf; + + buf = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); + for (k = 0; k < ATH10K_STATS_TYPE_MAX; k++) { + for (j = 0; j < ATH10K_COUNTER_TYPE_MAX; j++) { + stats = &arsta->tx_stats->stats[k]; + len += scnprintf(buf + len, size - len, "%s_%s\n", + str_name[k], + str[j]); + len += scnprintf(buf + len, size - len, + " VHT MCS %s\n", + str[j]); + for (i = 0; i < ATH10K_VHT_MCS_NUM; i++) + len += scnprintf(buf + len, size - len, + " %llu ", + stats->vht[j][i]); + len += scnprintf(buf + len, size - len, "\n"); + len += scnprintf(buf + len, size - len, " HT MCS %s\n", + str[j]); + for (i = 0; i < ATH10K_HT_MCS_NUM; i++) + len += scnprintf(buf + len, size - len, + " %llu ", stats->ht[j][i]); + len += scnprintf(buf + len, size - len, "\n"); + len += scnprintf(buf + len, size - len, + " BW %s (20,40,80,160 MHz)\n", str[j]); + len += scnprintf(buf + len, size - len, + " %llu %llu %llu %llu\n", + stats->bw[j][0], stats->bw[j][1], + stats->bw[j][2], stats->bw[j][3]); + len += scnprintf(buf + len, size - len, + " NSS %s (1x1,2x2,3x3,4x4)\n", str[j]); + len += scnprintf(buf + len, size - len, + " %llu %llu %llu %llu\n", + stats->nss[j][0], stats->nss[j][1], + stats->nss[j][2], stats->nss[j][3]); + len += scnprintf(buf + len, size - len, + " GI %s (LGI,SGI)\n", + str[j]); + len += scnprintf(buf + len, size - len, " %llu %llu\n", + stats->gi[j][0], stats->gi[j][1]); + len += scnprintf(buf + len, size - len, + " legacy rate %s (1,2 ... Mbps)\n ", + str[j]); + for (i = 0; i < ATH10K_LEGACY_NUM; i++) + len += scnprintf(buf + len, size - len, "%llu ", + stats->legacy[j][i]); + len += scnprintf(buf + len, size - len, "\n"); + } + } + + len += scnprintf(buf + len, size - len, + "\nTX duration\n %llu usecs\n", + arsta->tx_stats->tx_duration); + len += scnprintf(buf + len, size - len, + "BA fails\n %llu\n", arsta->tx_stats->ba_fails); + len += scnprintf(buf + len, size - len, + "ack fails\n %llu\n", arsta->tx_stats->ack_fails); + spin_unlock_bh(&ar->data_lock); + + if (len > size) + len = size; + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + mutex_unlock(&ar->conf_mutex); + return retval; +} + +static const struct file_operations fops_tx_stats = { + .read = ath10k_dbg_sta_dump_tx_stats, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct dentry *dir) { + struct ath10k *ar = hw->priv; + debugfs_create_file("aggr_mode", 0644, dir, sta, &fops_aggr_mode); debugfs_create_file("addba", 0200, dir, sta, &fops_addba); debugfs_create_file("addba_resp", 0200, dir, sta, &fops_addba_resp); @@ -637,4 +733,9 @@ void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, &fops_peer_debug_trigger); debugfs_create_file("dump_tid_stats", 0400, dir, sta, &fops_tid_stats_dump); + + if (ath10k_peer_stats_enabled(ar) && + ath10k_debug_is_extd_tx_stats_enabled(ar)) + debugfs_create_file("tx_stats", 0400, dir, sta, + &fops_tx_stats); } From 97c69a70dc2cecb2c3b96a66529e0082dabc2d2c Mon Sep 17 00:00:00 2001 From: "K.T.VIJAYAKUMAAR" Date: Mon, 3 Sep 2018 20:07:44 +0300 Subject: [PATCH 14/51] ath10k: avoid possible memory access violation array "ctl_power_table" access index "pream" is initialized with -1 and is raised as a static analysis tool issue. [drivers\net\wireless\ath\ath10k\wmi.c:4719] -> [drivers\net\wireless\ath\ath10k\wmi.c:4730]: (error) Array index -1 is out of bounds. Since the "pream" index for accessing ctl_power_table array is initialized with -1, there is a chance of memory access violation for the cases below. 1) wmi_pdev_tpc_final_table_event change frequency is between 2483 and 5180 2) pream_idx is out of the enumeration ranges of wmi_tpc_pream_2ghz, wmi_tpc_pream_5ghz Signed-off-by: K.T.VIJAYAKUMAAR [kvalo@codeaurora.org: clean up the warning message] Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 212005cd0646..43daa4e57bc1 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -4780,6 +4780,13 @@ ath10k_wmi_tpc_final_get_rate(struct ath10k *ar, } } + if (pream == -1) { + ath10k_warn(ar, "unknown wmi tpc final index and frequency: %u, %u\n", + pream_idx, __le32_to_cpu(ev->chan_freq)); + tpc = 0; + goto out; + } + if (pream == 4) tpc = min_t(u8, ev->rates_array[rate_idx], ev->max_reg_allow_pow[ch]); From c7fd8d237e2e7dff7642b94b32e09c273989ea21 Mon Sep 17 00:00:00 2001 From: Balaji Pothunoori Date: Tue, 4 Sep 2018 12:52:18 +0300 Subject: [PATCH 15/51] ath10k: average ack rssi support for data frames Average ack rssi value is weighted average of ack rssi for no of msdu's has been sent. This feature is enabled by the host driver if firmware is capable. After receiving event from host, firmware allocates the necessary memory to store the ack_rssi for data packets during the init time. After each successful transmission, If tx completion status is OK and 24th bit is set in HTT message header then host will fetch the ack_rssi else host can ignore the ack_rssi field. Signed-off-by: Balaji Pothunoori Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 4 ++++ drivers/net/wireless/ath/ath10k/htt.h | 4 +++- drivers/net/wireless/ath/ath10k/htt_rx.c | 28 +++++++++++++++++++++--- drivers/net/wireless/ath/ath10k/mac.c | 4 ++++ drivers/net/wireless/ath/ath10k/wmi.h | 8 +++++++ 5 files changed, 44 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 35899d775346..fd668e3d2bc8 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -2549,6 +2549,10 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode, ar->wmi.svc_map)) val |= WMI_10_4_TDLS_UAPSD_BUFFER_STA; + if (test_bit(WMI_SERVICE_TX_DATA_ACK_RSSI, + ar->wmi.svc_map)) + val |= WMI_10_4_TX_DATA_ACK_RSSI; + status = ath10k_mac_ext_resource_config(ar, val); if (status) { ath10k_err(ar, diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 5d3ff80f3a1f..26ce532e8196 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -577,6 +577,8 @@ struct htt_mgmt_tx_completion { #define HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES_MASK 0xFF000000 #define HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES_LSB 24 +#define HTT_TX_CMPL_FLAG_DATA_RSSI BIT(0) + struct htt_rx_indication_hdr { u8 info0; /* %HTT_RX_INDICATION_INFO0_ */ __le16 peer_id; @@ -840,7 +842,7 @@ struct htt_data_tx_completion { } __packed; } __packed; u8 num_msdus; - u8 rsvd0; + u8 flags2; /* HTT_TX_CMPL_FLAG_DATA_RSSI */ __le16 msdus[0]; /* variable length based on %num_msdus */ } __packed; diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 4b9197b93be1..daccf30916f0 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -1884,7 +1884,9 @@ static void ath10k_htt_rx_tx_compl_ind(struct ath10k *ar, struct htt_resp *resp = (struct htt_resp *)skb->data; struct htt_tx_done tx_done = {}; int status = MS(resp->data_tx_completion.flags, HTT_DATA_TX_STATUS); - __le16 msdu_id; + __le16 msdu_id, *msdus; + bool rssi_enabled = false; + u8 msdu_count = 0; int i; switch (status) { @@ -1908,10 +1910,30 @@ static void ath10k_htt_rx_tx_compl_ind(struct ath10k *ar, ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx completion num_msdus %d\n", resp->data_tx_completion.num_msdus); - for (i = 0; i < resp->data_tx_completion.num_msdus; i++) { - msdu_id = resp->data_tx_completion.msdus[i]; + msdu_count = resp->data_tx_completion.num_msdus; + + if (resp->data_tx_completion.flags2 & HTT_TX_CMPL_FLAG_DATA_RSSI) + rssi_enabled = true; + + for (i = 0; i < msdu_count; i++) { + msdus = resp->data_tx_completion.msdus; + msdu_id = msdus[i]; tx_done.msdu_id = __le16_to_cpu(msdu_id); + if (rssi_enabled) { + /* Total no of MSDUs should be even, + * if odd MSDUs are sent firmware fills + * last msdu id with 0xffff + */ + if (msdu_count & 0x01) { + msdu_id = msdus[msdu_count + i + 1]; + tx_done.ack_rssi = __le16_to_cpu(msdu_id); + } else { + msdu_id = msdus[msdu_count + i]; + tx_done.ack_rssi = __le16_to_cpu(msdu_id); + } + } + /* kfifo_put: In practice firmware shouldn't fire off per-CE * interrupt and main interrupt (MSI/-X range case) for the same * HTC service so it should be safe to use kfifo_put w/o lock. diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index ef952a27b0ed..97548f96a2f7 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -8479,6 +8479,10 @@ int ath10k_mac_register(struct ath10k *ar) wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_SET_SCAN_DWELL); + if (test_bit(WMI_SERVICE_TX_DATA_ACK_RSSI, ar->wmi.svc_map)) + wiphy_ext_feature_set(ar->hw->wiphy, + NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT); + /* * on LL hardware queues are managed entirely by the FW * so we only advertise to mac we can do the queues thing diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index a25bf9b876ec..15622943a885 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -203,6 +203,7 @@ enum wmi_service { WMI_SERVICE_TPC_STATS_FINAL, WMI_SERVICE_RESET_CHIP, WMI_SERVICE_SPOOF_MAC_SUPPORT, + WMI_SERVICE_TX_DATA_ACK_RSSI, /* keep last */ WMI_SERVICE_MAX, @@ -350,6 +351,8 @@ enum wmi_10_4_service { WMI_10_4_SERVICE_HTT_MGMT_TX_COMP_VALID_FLAGS, WMI_10_4_SERVICE_HOST_DFS_CHECK_SUPPORT, WMI_10_4_SERVICE_TPC_STATS_FINAL, + WMI_10_4_SERVICE_CFR_CAPTURE_SUPPORT, + WMI_10_4_SERVICE_TX_DATA_ACK_RSSI, }; static inline char *wmi_service_name(int service_id) @@ -463,6 +466,7 @@ static inline char *wmi_service_name(int service_id) SVCSTR(WMI_SERVICE_HOST_DFS_CHECK_SUPPORT); SVCSTR(WMI_SERVICE_TPC_STATS_FINAL); SVCSTR(WMI_SERVICE_RESET_CHIP); + SVCSTR(WMI_SERVICE_TX_DATA_ACK_RSSI); default: return NULL; } @@ -771,6 +775,8 @@ static inline void wmi_10_4_svc_map(const __le32 *in, unsigned long *out, WMI_SERVICE_HOST_DFS_CHECK_SUPPORT, len); SVCMAP(WMI_10_4_SERVICE_TPC_STATS_FINAL, WMI_SERVICE_TPC_STATS_FINAL, len); + SVCMAP(WMI_10_4_SERVICE_TX_DATA_ACK_RSSI, + WMI_SERVICE_TX_DATA_ACK_RSSI, len); } #undef SVCMAP @@ -2924,6 +2930,7 @@ enum wmi_coex_version { * @WMI_10_4_TDLS_CONN_TRACKER_IN_HOST_MODE: TDLS connection tracker in host * enable/disable * @WMI_10_4_TDLS_EXPLICIT_MODE_ONLY:Explicit TDLS mode enable/disable + * @WMI_10_4_TX_DATA_ACK_RSSI: Enable DATA ACK RSSI if firmware is capable */ enum wmi_10_4_feature_mask { WMI_10_4_LTEU_SUPPORT = BIT(0), @@ -2939,6 +2946,7 @@ enum wmi_10_4_feature_mask { WMI_10_4_TDLS_UAPSD_SLEEP_STA = BIT(10), WMI_10_4_TDLS_CONN_TRACKER_IN_HOST_MODE = BIT(11), WMI_10_4_TDLS_EXPLICIT_MODE_ONLY = BIT(12), + WMI_10_4_TX_DATA_ACK_RSSI = BIT(16), }; From d56bbeea25d181760ed4aeb5002df0f1e07f0420 Mon Sep 17 00:00:00 2001 From: Carl Huang Date: Tue, 4 Sep 2018 13:00:25 +0300 Subject: [PATCH 16/51] ath10k: optimize pci diag mem read & write operations Delay 1ms is too long for both diag read and write operations. This is observed when writing a big memory buffer to target or reading a big memory buffer from target. Take writing/reading 512k bytes as example, the delay itself is 256ms as the maximum length of every write/read is 2k size. Reduce the delay to 50us for read and write operations. Take the ath10k_pci_targ_cpu_to_ce_addr() out of loop and put it in the beginning of the loop for ath10k_pci_diag_read_mem(). The ath10k_pci_targ_cpu_to_ce_addr() is to convert the address from target cpu's perspective to CE's perspective, so it makes no sense to convert a CE's perspective address again in the loop. It's a wrong implementation but happens to work. If the target address is below 1M space, then the convert in the loop from the second time becomes wrong because the previously converted address is larger than 1M. The counterpart ath10k_pci_diag_write_mem() has the correct implementation. With this change, ath10k_pci_diage_read_mem() works correctly no matter the target address is below 1M or above 1M. It's tested with QCA6174 hw3.2 and firmware-6.bin_WLAN.RM.4.4.1-00111-QCARMSWP-1. QCA9377 is also affected. Signed-off-by: Carl Huang Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/pci.c | 40 +++++++++++++++------------ drivers/net/wireless/ath/ath10k/pci.h | 3 +- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 7db4a7915624..5ea5d40b4936 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -931,6 +931,15 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data, goto done; } + /* The address supplied by the caller is in the + * Target CPU virtual address space. + * + * In order to use this address with the diagnostic CE, + * convert it from Target CPU virtual address space + * to CE address space + */ + address = ath10k_pci_targ_cpu_to_ce_addr(ar, address); + remaining_bytes = nbytes; ce_data = ce_data_base; while (remaining_bytes) { @@ -942,16 +951,6 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data, goto done; /* Request CE to send from Target(!) address to Host buffer */ - /* - * The address supplied by the caller is in the - * Target CPU virtual address space. - * - * In order to use this address with the diagnostic CE, - * convert it from Target CPU virtual address space - * to CE address space - */ - address = ath10k_pci_targ_cpu_to_ce_addr(ar, address); - ret = ath10k_ce_send_nolock(ce_diag, NULL, (u32)address, nbytes, 0, 0); if (ret) @@ -960,8 +959,10 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data, i = 0; while (ath10k_ce_completed_send_next_nolock(ce_diag, NULL) != 0) { - mdelay(1); - if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) { + udelay(DIAG_ACCESS_CE_WAIT_US); + i += DIAG_ACCESS_CE_WAIT_US; + + if (i > DIAG_ACCESS_CE_TIMEOUT_US) { ret = -EBUSY; goto done; } @@ -972,9 +973,10 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data, (void **)&buf, &completed_nbytes) != 0) { - mdelay(1); + udelay(DIAG_ACCESS_CE_WAIT_US); + i += DIAG_ACCESS_CE_WAIT_US; - if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) { + if (i > DIAG_ACCESS_CE_TIMEOUT_US) { ret = -EBUSY; goto done; } @@ -1119,9 +1121,10 @@ int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address, i = 0; while (ath10k_ce_completed_send_next_nolock(ce_diag, NULL) != 0) { - mdelay(1); + udelay(DIAG_ACCESS_CE_WAIT_US); + i += DIAG_ACCESS_CE_WAIT_US; - if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) { + if (i > DIAG_ACCESS_CE_TIMEOUT_US) { ret = -EBUSY; goto done; } @@ -1132,9 +1135,10 @@ int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address, (void **)&buf, &completed_nbytes) != 0) { - mdelay(1); + udelay(DIAG_ACCESS_CE_WAIT_US); + i += DIAG_ACCESS_CE_WAIT_US; - if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) { + if (i > DIAG_ACCESS_CE_TIMEOUT_US) { ret = -EBUSY; goto done; } diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h index 0ed436657108..e8d86331c539 100644 --- a/drivers/net/wireless/ath/ath10k/pci.h +++ b/drivers/net/wireless/ath/ath10k/pci.h @@ -207,7 +207,8 @@ static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar) #define CDC_WAR_DATA_CE 4 /* Wait up to this many Ms for a Diagnostic Access CE operation to complete */ -#define DIAG_ACCESS_CE_TIMEOUT_MS 10 +#define DIAG_ACCESS_CE_TIMEOUT_US 10000 /* 10 ms */ +#define DIAG_ACCESS_CE_WAIT_US 50 void ath10k_pci_write32(struct ath10k *ar, u32 offset, u32 value); void ath10k_pci_soc_write32(struct ath10k *ar, u32 addr, u32 val); From bc346c9a24a41fba93f8f5c4cd3de2c64fd02cfd Mon Sep 17 00:00:00 2001 From: Carl Huang Date: Tue, 4 Sep 2018 13:00:39 +0300 Subject: [PATCH 17/51] ath10k: support to access target space below 1M for qca6174 and qca9377 To download firmware via diag interface, driver needs to write the target memory space below 1M. It means the bit20 should be zero for the converted address if the target memory space is below 1M. Otherwise, bit20 is one if the target address is larger or equal to 1M space. As downloading firmware via diag interface is only required for qca6174 and qca9377, a new specific function is introduced to convert the target address to ce address: ath10k_pci_qca6174_targ_cpu_to_ce_addri(). This function supports to convert any target address to ce address. It's tested with QCA6174 hw3.2 and firmware-6.bin_WLAN.RM.4.4.1-00111-QCARMSWP-1. QCA9377 is also affected. Signed-off-by: Carl Huang Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/pci.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 5ea5d40b4936..001eb13cd761 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -870,6 +870,21 @@ static u32 ath10k_pci_qca988x_targ_cpu_to_ce_addr(struct ath10k *ar, u32 addr) return val; } +/* Refactor from ath10k_pci_qca988x_targ_cpu_to_ce_addr. + * Support to access target space below 1M for qca6174 and qca9377. + * If target space is below 1M, the bit[20] of converted CE addr is 0. + * Otherwise bit[20] of converted CE addr is 1. + */ +static u32 ath10k_pci_qca6174_targ_cpu_to_ce_addr(struct ath10k *ar, u32 addr) +{ + u32 val = 0, region = addr & 0xfffff; + + val = (ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + CORE_CTRL_ADDRESS) + & 0x7ff) << 21; + val |= ((addr >= 0x100000) ? 0x100000 : 0) | region; + return val; +} + static u32 ath10k_pci_qca99x0_targ_cpu_to_ce_addr(struct ath10k *ar, u32 addr) { u32 val = 0, region = addr & 0xfffff; @@ -3514,7 +3529,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev, pci_ps = true; pci_soft_reset = ath10k_pci_warm_reset; pci_hard_reset = ath10k_pci_qca6174_chip_reset; - targ_cpu_to_ce_addr = ath10k_pci_qca988x_targ_cpu_to_ce_addr; + targ_cpu_to_ce_addr = ath10k_pci_qca6174_targ_cpu_to_ce_addr; break; case QCA99X0_2_0_DEVICE_ID: hw_rev = ATH10K_HW_QCA99X0; @@ -3542,7 +3557,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev, pci_ps = true; pci_soft_reset = NULL; pci_hard_reset = ath10k_pci_qca6174_chip_reset; - targ_cpu_to_ce_addr = ath10k_pci_qca988x_targ_cpu_to_ce_addr; + targ_cpu_to_ce_addr = ath10k_pci_qca6174_targ_cpu_to_ce_addr; break; default: WARN_ON(1); From 39501ea64116c665e9de7cf6cce91a1defcdbae8 Mon Sep 17 00:00:00 2001 From: Carl Huang Date: Tue, 4 Sep 2018 13:00:51 +0300 Subject: [PATCH 18/51] ath10k: download firmware via diag Copy Engine for QCA6174 and QCA9377. Downloading firmware via BMI protocol takes too long time. For example, a ~700K bytes firmware takes about 500ms to download via BMI protocol. This is too long especially in suspend and resume scenario where firmware is re-downloaded unless WoWLAN is enabled. Downloading firmware via diag CE can reduce the time to ~40ms for a ~700K bytes firmware binary. Ath10k driver parses the firmware to segments and downloads the segments to the specified address directly. If the firmware is compressed or has unsupported segments, ath10k driver will try BMI download again. It's tested with QCA6174 hw3.2 and firmware-6.bin_WLAN.RM.4.4.1-00111-QCARMSWP-1. QCA9377 is also affected. Signed-off-by: Carl Huang Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/bmi.c | 23 +++ drivers/net/wireless/ath/ath10k/bmi.h | 31 +++++ drivers/net/wireless/ath/ath10k/core.c | 36 ++++- drivers/net/wireless/ath/ath10k/hw.c | 185 +++++++++++++++++++++++++ drivers/net/wireless/ath/ath10k/hw.h | 19 +++ 5 files changed, 288 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c index af4978d6a14b..1750b182209b 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.c +++ b/drivers/net/wireless/ath/ath10k/bmi.c @@ -459,3 +459,26 @@ int ath10k_bmi_fast_download(struct ath10k *ar, return ret; } + +int ath10k_bmi_set_start(struct ath10k *ar, u32 address) +{ + struct bmi_cmd cmd; + u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.set_app_start); + int ret; + + if (ar->bmi.done_sent) { + ath10k_warn(ar, "bmi set start command disallowed\n"); + return -EBUSY; + } + + cmd.id = __cpu_to_le32(BMI_SET_APP_START); + cmd.set_app_start.addr = __cpu_to_le32(address); + + ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL); + if (ret) { + ath10k_warn(ar, "unable to set start to the device:%d\n", ret); + return ret; + } + + return 0; +} diff --git a/drivers/net/wireless/ath/ath10k/bmi.h b/drivers/net/wireless/ath/ath10k/bmi.h index 28f49441ba46..725c9afc63f2 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.h +++ b/drivers/net/wireless/ath/ath10k/bmi.h @@ -195,6 +195,35 @@ struct bmi_target_info { u32 type; }; +struct bmi_segmented_file_header { + __le32 magic_num; + __le32 file_flags; + u8 data[]; +}; + +struct bmi_segmented_metadata { + __le32 addr; + __le32 length; + u8 data[]; +}; + +#define BMI_SGMTFILE_MAGIC_NUM 0x544d4753 /* "SGMT" */ +#define BMI_SGMTFILE_FLAG_COMPRESS 1 + +/* Special values for bmi_segmented_metadata.length (all have high bit set) */ + +/* end of segmented data */ +#define BMI_SGMTFILE_DONE 0xffffffff + +/* Board Data segment */ +#define BMI_SGMTFILE_BDDATA 0xfffffffe + +/* set beginning address */ +#define BMI_SGMTFILE_BEGINADDR 0xfffffffd + +/* immediate function execution */ +#define BMI_SGMTFILE_EXEC 0xfffffffc + /* in jiffies */ #define BMI_COMMUNICATION_TIMEOUT_HZ (3 * HZ) @@ -244,4 +273,6 @@ int ath10k_bmi_fast_download(struct ath10k *ar, u32 address, const void *buffer, u32 length); int ath10k_bmi_read_soc_reg(struct ath10k *ar, u32 address, u32 *reg_val); int ath10k_bmi_write_soc_reg(struct ath10k *ar, u32 address, u32 reg_val); +int ath10k_bmi_set_start(struct ath10k *ar, u32 address); + #endif /* _BMI_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index fd668e3d2bc8..4a56b604f715 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -93,6 +93,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .shadow_reg_support = false, .rri_on_ddr = false, .hw_filter_reset_required = true, + .fw_diag_ce_download = false, }, { .id = QCA988X_HW_2_0_VERSION, @@ -127,6 +128,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .shadow_reg_support = false, .rri_on_ddr = false, .hw_filter_reset_required = true, + .fw_diag_ce_download = false, }, { .id = QCA9887_HW_1_0_VERSION, @@ -161,6 +163,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .shadow_reg_support = false, .rri_on_ddr = false, .hw_filter_reset_required = true, + .fw_diag_ce_download = false, }, { .id = QCA6174_HW_2_1_VERSION, @@ -194,6 +197,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .shadow_reg_support = false, .rri_on_ddr = false, .hw_filter_reset_required = true, + .fw_diag_ce_download = false, }, { .id = QCA6174_HW_2_1_VERSION, @@ -227,6 +231,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .shadow_reg_support = false, .rri_on_ddr = false, .hw_filter_reset_required = true, + .fw_diag_ce_download = false, }, { .id = QCA6174_HW_3_0_VERSION, @@ -260,6 +265,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .shadow_reg_support = false, .rri_on_ddr = false, .hw_filter_reset_required = true, + .fw_diag_ce_download = false, }, { .id = QCA6174_HW_3_2_VERSION, @@ -296,6 +302,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .shadow_reg_support = false, .rri_on_ddr = false, .hw_filter_reset_required = true, + .fw_diag_ce_download = true, }, { .id = QCA99X0_HW_2_0_DEV_VERSION, @@ -335,6 +342,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .shadow_reg_support = false, .rri_on_ddr = false, .hw_filter_reset_required = true, + .fw_diag_ce_download = false, }, { .id = QCA9984_HW_1_0_DEV_VERSION, @@ -381,6 +389,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .shadow_reg_support = false, .rri_on_ddr = false, .hw_filter_reset_required = true, + .fw_diag_ce_download = false, }, { .id = QCA9888_HW_2_0_DEV_VERSION, @@ -424,6 +433,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .shadow_reg_support = false, .rri_on_ddr = false, .hw_filter_reset_required = true, + .fw_diag_ce_download = false, }, { .id = QCA9377_HW_1_0_DEV_VERSION, @@ -457,6 +467,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .shadow_reg_support = false, .rri_on_ddr = false, .hw_filter_reset_required = true, + .fw_diag_ce_download = false, }, { .id = QCA9377_HW_1_1_DEV_VERSION, @@ -492,6 +503,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .shadow_reg_support = false, .rri_on_ddr = false, .hw_filter_reset_required = true, + .fw_diag_ce_download = true, }, { .id = QCA4019_HW_1_0_DEV_VERSION, @@ -532,6 +544,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .shadow_reg_support = false, .rri_on_ddr = false, .hw_filter_reset_required = true, + .fw_diag_ce_download = false, }, { .id = WCN3990_HW_1_0_DEV_VERSION, @@ -556,6 +569,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .shadow_reg_support = true, .rri_on_ddr = true, .hw_filter_reset_required = false, + .fw_diag_ce_download = false, }, }; @@ -955,14 +969,24 @@ static int ath10k_download_fw(struct ath10k *ar) "boot uploading firmware image %pK len %d\n", data, data_len); - ret = ath10k_bmi_fast_download(ar, address, data, data_len); - if (ret) { - ath10k_err(ar, "failed to download firmware: %d\n", - ret); - return ret; + /* 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); } - return ret; + return ath10k_bmi_fast_download(ar, address, + data, data_len); } static void ath10k_core_free_board_files(struct ath10k *ar) diff --git a/drivers/net/wireless/ath/ath10k/hw.c b/drivers/net/wireless/ath/ath10k/hw.c index 677535b3d207..2c2870e3e84d 100644 --- a/drivers/net/wireless/ath/ath10k/hw.c +++ b/drivers/net/wireless/ath/ath10k/hw.c @@ -16,6 +16,7 @@ #include #include +#include #include "core.h" #include "hw.h" #include "hif.h" @@ -918,6 +919,190 @@ static int ath10k_hw_qca6174_enable_pll_clock(struct ath10k *ar) return 0; } +/* Program CPU_ADDR_MSB to allow different memory + * region access. + */ +static void ath10k_hw_map_target_mem(struct ath10k *ar, u32 msb) +{ + u32 address = SOC_CORE_BASE_ADDRESS + FW_RAM_CONFIG_ADDRESS; + + ath10k_hif_write32(ar, address, msb); +} + +/* 1. Write to memory region of target, such as IRAM adn DRAM. + * 2. Target address( 0 ~ 00100000 & 0x00400000~0x00500000) + * can be written directly. See ath10k_pci_targ_cpu_to_ce_addr() too. + * 3. In order to access the region other than the above, + * we need to set the value of register CPU_ADDR_MSB. + * 4. Target memory access space is limited to 1M size. If the size is larger + * than 1M, need to split it and program CPU_ADDR_MSB accordingly. + */ +static int ath10k_hw_diag_segment_msb_download(struct ath10k *ar, + const void *buffer, + u32 address, + u32 length) +{ + u32 addr = address & REGION_ACCESS_SIZE_MASK; + int ret, remain_size, size; + const u8 *buf; + + ath10k_hw_map_target_mem(ar, CPU_ADDR_MSB_REGION_VAL(address)); + + if (addr + length > REGION_ACCESS_SIZE_LIMIT) { + size = REGION_ACCESS_SIZE_LIMIT - addr; + remain_size = length - size; + + ret = ath10k_hif_diag_write(ar, address, buffer, size); + if (ret) { + ath10k_warn(ar, + "failed to download the first %d bytes segment to address:0x%x: %d\n", + size, address, ret); + goto done; + } + + /* Change msb to the next memory region*/ + ath10k_hw_map_target_mem(ar, + CPU_ADDR_MSB_REGION_VAL(address) + 1); + buf = buffer + size; + ret = ath10k_hif_diag_write(ar, + address & ~REGION_ACCESS_SIZE_MASK, + buf, remain_size); + if (ret) { + ath10k_warn(ar, + "failed to download the second %d bytes segment to address:0x%x: %d\n", + remain_size, + address & ~REGION_ACCESS_SIZE_MASK, + ret); + goto done; + } + } else { + ret = ath10k_hif_diag_write(ar, address, buffer, length); + if (ret) { + ath10k_warn(ar, + "failed to download the only %d bytes segment to address:0x%x: %d\n", + length, address, ret); + goto done; + } + } + +done: + /* Change msb to DRAM */ + ath10k_hw_map_target_mem(ar, + CPU_ADDR_MSB_REGION_VAL(DRAM_BASE_ADDRESS)); + return ret; +} + +static int ath10k_hw_diag_segment_download(struct ath10k *ar, + const void *buffer, + u32 address, + u32 length) +{ + if (address >= DRAM_BASE_ADDRESS + REGION_ACCESS_SIZE_LIMIT) + /* Needs to change MSB for memory write */ + return ath10k_hw_diag_segment_msb_download(ar, buffer, + address, length); + else + return ath10k_hif_diag_write(ar, address, buffer, length); +} + +int ath10k_hw_diag_fast_download(struct ath10k *ar, + u32 address, + const void *buffer, + u32 length) +{ + const u8 *buf = buffer; + bool sgmt_end = false; + u32 base_addr = 0; + u32 base_len = 0; + u32 left = 0; + struct bmi_segmented_file_header *hdr; + struct bmi_segmented_metadata *metadata; + int ret = 0; + + if (length < sizeof(*hdr)) + return -EINVAL; + + /* check firmware header. If it has no correct magic number + * or it's compressed, returns error. + */ + hdr = (struct bmi_segmented_file_header *)buf; + if (__le32_to_cpu(hdr->magic_num) != BMI_SGMTFILE_MAGIC_NUM) { + ath10k_dbg(ar, ATH10K_DBG_BOOT, + "Not a supported firmware, magic_num:0x%x\n", + hdr->magic_num); + return -EINVAL; + } + + if (hdr->file_flags != 0) { + ath10k_dbg(ar, ATH10K_DBG_BOOT, + "Not a supported firmware, file_flags:0x%x\n", + hdr->file_flags); + return -EINVAL; + } + + metadata = (struct bmi_segmented_metadata *)hdr->data; + left = length - sizeof(*hdr); + + while (left > 0) { + base_addr = __le32_to_cpu(metadata->addr); + base_len = __le32_to_cpu(metadata->length); + buf = metadata->data; + left -= sizeof(*metadata); + + switch (base_len) { + case BMI_SGMTFILE_BEGINADDR: + /* base_addr is the start address to run */ + ret = ath10k_bmi_set_start(ar, base_addr); + base_len = 0; + break; + case BMI_SGMTFILE_DONE: + /* no more segment */ + base_len = 0; + sgmt_end = true; + ret = 0; + break; + case BMI_SGMTFILE_BDDATA: + case BMI_SGMTFILE_EXEC: + ath10k_warn(ar, + "firmware has unsupported segment:%d\n", + base_len); + ret = -EINVAL; + break; + default: + if (base_len > left) { + /* sanity check */ + ath10k_warn(ar, + "firmware has invalid segment length, %d > %d\n", + base_len, left); + ret = -EINVAL; + break; + } + + ret = ath10k_hw_diag_segment_download(ar, + buf, + base_addr, + base_len); + + if (ret) + ath10k_warn(ar, + "failed to download firmware via diag interface:%d\n", + ret); + break; + } + + if (ret || sgmt_end) + break; + + metadata = (struct bmi_segmented_metadata *)(buf + base_len); + left -= base_len; + } + + if (ret == 0) + ath10k_dbg(ar, ATH10K_DBG_BOOT, + "boot firmware fast diag download successfully.\n"); + return ret; +} + const struct ath10k_hw_ops qca988x_ops = { .set_coverage_class = ath10k_hw_qca988x_set_coverage_class, }; diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 33833d010a76..4c68a496d5d2 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -391,6 +391,11 @@ extern const struct ath10k_hw_ce_regs qcax_ce_regs; void ath10k_hw_fill_survey_time(struct ath10k *ar, struct survey_info *survey, u32 cc, u32 rcc, u32 cc_prev, u32 rcc_prev); +int ath10k_hw_diag_fast_download(struct ath10k *ar, + u32 address, + const void *buffer, + u32 length); + #define QCA_REV_988X(ar) ((ar)->hw_rev == ATH10K_HW_QCA988X) #define QCA_REV_9887(ar) ((ar)->hw_rev == ATH10K_HW_QCA9887) #define QCA_REV_6174(ar) ((ar)->hw_rev == ATH10K_HW_QCA6174) @@ -598,6 +603,9 @@ struct ath10k_hw_params { * to avoid it sending spurious acks. */ bool hw_filter_reset_required; + + /* target supporting fw download via diag ce */ + bool fw_diag_ce_download; }; struct htt_rx_desc; @@ -1133,4 +1141,15 @@ ath10k_rx_desc_get_l3_pad_bytes(struct ath10k_hw_params *hw, #define RTC_SYNC_STATUS_PLL_CHANGING_MASK 0x00000020 /* qca6174 PLL offset/mask end */ +/* CPU_ADDR_MSB is a register, bit[3:0] is to specify which memory + * region is accessed. The memory region size is 1M. + * If host wants to access 0xX12345 at target, then CPU_ADDR_MSB[3:0] + * is 0xX. + * The following MACROs are defined to get the 0xX and the size limit. + */ +#define CPU_ADDR_MSB_REGION_MASK GENMASK(23, 20) +#define CPU_ADDR_MSB_REGION_VAL(X) FIELD_GET(CPU_ADDR_MSB_REGION_MASK, X) +#define REGION_ACCESS_SIZE_LIMIT 0x100000 +#define REGION_ACCESS_SIZE_MASK (REGION_ACCESS_SIZE_LIMIT - 1) + #endif /* _HW_H_ */ From c0d8d565787c5d8f7d4b0ae319dc1d44d69a51d9 Mon Sep 17 00:00:00 2001 From: Erik Stromdahl Date: Tue, 4 Sep 2018 15:03:19 +0300 Subject: [PATCH 19/51] ath10k: add struct ath10k_bus_params This struct is used as argument to ath10k_core_register in order to make it easier to add more bus parameters in the future. Signed-off-by: Erik Stromdahl Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/ahb.c | 8 ++++---- drivers/net/wireless/ath/ath10k/core.c | 5 +++-- drivers/net/wireless/ath/ath10k/core.h | 7 ++++++- drivers/net/wireless/ath/ath10k/pci.c | 12 ++++++------ drivers/net/wireless/ath/ath10k/sdio.c | 7 ++++--- drivers/net/wireless/ath/ath10k/snoc.c | 4 +++- drivers/net/wireless/ath/ath10k/usb.c | 6 +++--- 7 files changed, 29 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/ahb.c b/drivers/net/wireless/ath/ath10k/ahb.c index be90c9e9e5bc..cfe45529d508 100644 --- a/drivers/net/wireless/ath/ath10k/ahb.c +++ b/drivers/net/wireless/ath/ath10k/ahb.c @@ -750,7 +750,7 @@ static int ath10k_ahb_probe(struct platform_device *pdev) enum ath10k_hw_rev hw_rev; size_t size; int ret; - u32 chip_id; + struct ath10k_bus_params bus_params; of_id = of_match_device(ath10k_ahb_of_match, &pdev->dev); if (!of_id) { @@ -806,14 +806,14 @@ static int ath10k_ahb_probe(struct platform_device *pdev) ath10k_pci_ce_deinit(ar); - chip_id = ath10k_ahb_soc_read32(ar, SOC_CHIP_ID_ADDRESS); - if (chip_id == 0xffffffff) { + bus_params.chip_id = ath10k_ahb_soc_read32(ar, SOC_CHIP_ID_ADDRESS); + if (bus_params.chip_id == 0xffffffff) { ath10k_err(ar, "failed to get chip id\n"); ret = -ENODEV; goto err_halt_device; } - ret = ath10k_core_register(ar, chip_id); + ret = ath10k_core_register(ar, &bus_params); if (ret) { ath10k_err(ar, "failed to register driver core: %d\n", ret); goto err_halt_device; diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 4a56b604f715..df8e7f7a8d2b 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -2924,9 +2924,10 @@ static void ath10k_core_register_work(struct work_struct *work) return; } -int ath10k_core_register(struct ath10k *ar, u32 chip_id) +int ath10k_core_register(struct ath10k *ar, + const struct ath10k_bus_params *bus_params) { - ar->chip_id = chip_id; + ar->chip_id = bus_params->chip_id; queue_work(ar->workqueue, &ar->register_work); return 0; diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 27df8ac26254..5505ead32206 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -915,6 +915,10 @@ struct ath10k_per_peer_tx_stats { u32 reserved2; }; +struct ath10k_bus_params { + u32 chip_id; +}; + struct ath10k { struct ath_common ath_common; struct ieee80211_hw *hw; @@ -1204,7 +1208,8 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode, const struct ath10k_fw_components *fw_components); int ath10k_wait_for_suspend(struct ath10k *ar, u32 suspend_opt); void ath10k_core_stop(struct ath10k *ar); -int ath10k_core_register(struct ath10k *ar, u32 chip_id); +int ath10k_core_register(struct ath10k *ar, + const struct ath10k_bus_params *bus_params); void ath10k_core_unregister(struct ath10k *ar); #endif /* _CORE_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 001eb13cd761..c337a8b6d166 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -3501,7 +3501,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev, struct ath10k *ar; struct ath10k_pci *ar_pci; enum ath10k_hw_rev hw_rev; - u32 chip_id; + struct ath10k_bus_params bus_params; bool pci_ps; int (*pci_soft_reset)(struct ath10k *ar); int (*pci_hard_reset)(struct ath10k *ar); @@ -3637,19 +3637,19 @@ static int ath10k_pci_probe(struct pci_dev *pdev, goto err_free_irq; } - chip_id = ath10k_pci_soc_read32(ar, SOC_CHIP_ID_ADDRESS); - if (chip_id == 0xffffffff) { + bus_params.chip_id = ath10k_pci_soc_read32(ar, SOC_CHIP_ID_ADDRESS); + if (bus_params.chip_id == 0xffffffff) { ath10k_err(ar, "failed to get chip id\n"); goto err_free_irq; } - if (!ath10k_pci_chip_is_supported(pdev->device, chip_id)) { + if (!ath10k_pci_chip_is_supported(pdev->device, bus_params.chip_id)) { ath10k_err(ar, "device %04x with chip_id %08x isn't supported\n", - pdev->device, chip_id); + pdev->device, bus_params.chip_id); goto err_free_irq; } - ret = ath10k_core_register(ar, chip_id); + ret = ath10k_core_register(ar, &bus_params); if (ret) { ath10k_err(ar, "failed to register driver core: %d\n", ret); goto err_free_irq; diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index 7f61591ce0de..1d4fd23f0134 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -1941,7 +1941,8 @@ static int ath10k_sdio_probe(struct sdio_func *func, struct ath10k_sdio *ar_sdio; struct ath10k *ar; enum ath10k_hw_rev hw_rev; - u32 chip_id, dev_id_base; + u32 dev_id_base; + struct ath10k_bus_params bus_params; int ret, i; /* Assumption: All SDIO based chipsets (so far) are QCA6174 based. @@ -2036,8 +2037,8 @@ static int ath10k_sdio_probe(struct sdio_func *func, } /* TODO: don't know yet how to get chip_id with SDIO */ - chip_id = 0; - ret = ath10k_core_register(ar, chip_id); + bus_params.chip_id = 0; + ret = ath10k_core_register(ar, &bus_params); if (ret) { ath10k_err(ar, "failed to register driver core: %d\n", ret); goto err_free_wq; diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index eabf4229d929..21b525a8ace3 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -1283,6 +1283,7 @@ static int ath10k_snoc_probe(struct platform_device *pdev) struct ath10k *ar; int ret; u32 i; + struct ath10k_bus_params bus_params; of_id = of_match_device(ath10k_snoc_dt_match, &pdev->dev); if (!of_id) { @@ -1350,7 +1351,8 @@ static int ath10k_snoc_probe(struct platform_device *pdev) goto err_free_irq; } - ret = ath10k_core_register(ar, drv_data->hw_rev); + bus_params.chip_id = drv_data->hw_rev; + ret = ath10k_core_register(ar, &bus_params); if (ret) { ath10k_err(ar, "failed to register driver core: %d\n", ret); goto err_hw_power_off; diff --git a/drivers/net/wireless/ath/ath10k/usb.c b/drivers/net/wireless/ath/ath10k/usb.c index d4803ff5a78a..275e00bafcb1 100644 --- a/drivers/net/wireless/ath/ath10k/usb.c +++ b/drivers/net/wireless/ath/ath10k/usb.c @@ -983,7 +983,7 @@ static int ath10k_usb_probe(struct usb_interface *interface, struct usb_device *dev = interface_to_usbdev(interface); int ret, vendor_id, product_id; enum ath10k_hw_rev hw_rev; - u32 chip_id; + struct ath10k_bus_params bus_params; /* Assumption: All USB based chipsets (so far) are QCA9377 based. * If there will be newer chipsets that does not use the hw reg @@ -1017,8 +1017,8 @@ static int ath10k_usb_probe(struct usb_interface *interface, ar->id.device = product_id; /* TODO: don't know yet how to get chip_id with USB */ - chip_id = 0; - ret = ath10k_core_register(ar, chip_id); + bus_params.chip_id = 0; + ret = ath10k_core_register(ar, &bus_params); if (ret) { ath10k_warn(ar, "failed to register driver core: %d\n", ret); goto err; From 7c2dd6154fc22e2aec1fcb384beb0a6372f2b439 Mon Sep 17 00:00:00 2001 From: Erik Stromdahl Date: Tue, 4 Sep 2018 15:03:44 +0300 Subject: [PATCH 20/51] ath10k: add device type enum to ath10k_bus_params Add dev_type parameter to struct ath10k_bus_params. The dev type specifies if the device is a high latency device (usb and sdio) or low latency device (pci, ahb and snoc) The setup of high latency chips is sometimes different than for chips using low latency interfaces. Signed-off-by: Erik Stromdahl Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/ahb.c | 1 + drivers/net/wireless/ath/ath10k/core.c | 1 + drivers/net/wireless/ath/ath10k/core.h | 7 +++++++ drivers/net/wireless/ath/ath10k/pci.c | 1 + drivers/net/wireless/ath/ath10k/sdio.c | 1 + drivers/net/wireless/ath/ath10k/snoc.c | 1 + drivers/net/wireless/ath/ath10k/usb.c | 1 + 7 files changed, 13 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/ahb.c b/drivers/net/wireless/ath/ath10k/ahb.c index cfe45529d508..4cd69aca75e2 100644 --- a/drivers/net/wireless/ath/ath10k/ahb.c +++ b/drivers/net/wireless/ath/ath10k/ahb.c @@ -806,6 +806,7 @@ static int ath10k_ahb_probe(struct platform_device *pdev) ath10k_pci_ce_deinit(ar); + bus_params.dev_type = ATH10K_DEV_TYPE_LL; bus_params.chip_id = ath10k_ahb_soc_read32(ar, SOC_CHIP_ID_ADDRESS); if (bus_params.chip_id == 0xffffffff) { ath10k_err(ar, "failed to get chip id\n"); diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index df8e7f7a8d2b..93f5a21de9c7 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -2928,6 +2928,7 @@ int ath10k_core_register(struct ath10k *ar, const struct ath10k_bus_params *bus_params) { ar->chip_id = bus_params->chip_id; + ar->dev_type = bus_params->dev_type; queue_work(ar->workqueue, &ar->register_work); return 0; diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 5505ead32206..61631ab30f65 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -915,8 +915,14 @@ struct ath10k_per_peer_tx_stats { u32 reserved2; }; +enum ath10k_dev_type { + ATH10K_DEV_TYPE_LL, + ATH10K_DEV_TYPE_HL, +}; + struct ath10k_bus_params { u32 chip_id; + enum ath10k_dev_type dev_type; }; struct ath10k { @@ -929,6 +935,7 @@ struct ath10k { enum ath10k_hw_rev hw_rev; u16 dev_id; u32 chip_id; + enum ath10k_dev_type dev_type; u32 target_version; u8 fw_version_major; u32 fw_version_minor; diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index c337a8b6d166..873dbb65439f 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -3637,6 +3637,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev, goto err_free_irq; } + bus_params.dev_type = ATH10K_DEV_TYPE_LL; bus_params.chip_id = ath10k_pci_soc_read32(ar, SOC_CHIP_ID_ADDRESS); if (bus_params.chip_id == 0xffffffff) { ath10k_err(ar, "failed to get chip id\n"); diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index 1d4fd23f0134..983ecfef1d28 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -2036,6 +2036,7 @@ static int ath10k_sdio_probe(struct sdio_func *func, goto err_free_wq; } + bus_params.dev_type = ATH10K_DEV_TYPE_HL; /* TODO: don't know yet how to get chip_id with SDIO */ bus_params.chip_id = 0; ret = ath10k_core_register(ar, &bus_params); diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index 21b525a8ace3..b83edb9144c3 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -1351,6 +1351,7 @@ static int ath10k_snoc_probe(struct platform_device *pdev) goto err_free_irq; } + bus_params.dev_type = ATH10K_DEV_TYPE_LL; bus_params.chip_id = drv_data->hw_rev; ret = ath10k_core_register(ar, &bus_params); if (ret) { diff --git a/drivers/net/wireless/ath/ath10k/usb.c b/drivers/net/wireless/ath/ath10k/usb.c index 275e00bafcb1..f731d35ee76d 100644 --- a/drivers/net/wireless/ath/ath10k/usb.c +++ b/drivers/net/wireless/ath/ath10k/usb.c @@ -1016,6 +1016,7 @@ static int ath10k_usb_probe(struct usb_interface *interface, ar->id.vendor = vendor_id; ar->id.device = product_id; + bus_params.dev_type = ATH10K_DEV_TYPE_HL; /* TODO: don't know yet how to get chip_id with USB */ bus_params.chip_id = 0; ret = ath10k_core_register(ar, &bus_params); From 367c899f622c1bf3443e56011561542d2eb5a917 Mon Sep 17 00:00:00 2001 From: Erik Stromdahl Date: Tue, 4 Sep 2018 15:04:07 +0300 Subject: [PATCH 21/51] ath10k: add bus type check in ath10k_init_hw_params The bus type is used together with the other hw parameters to find a matching entry in ath10k_hw_params_list for the device. This is necessary since HL devices can have the same dev_id and target_version as a corresponding LL device (same chipset) and yet use a totally different configuration. Signed-off-by: Erik Stromdahl Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 16 +++++++++++++++- drivers/net/wireless/ath/ath10k/core.h | 8 -------- drivers/net/wireless/ath/ath10k/hw.h | 9 +++++++++ 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 93f5a21de9c7..e30dcb310bd6 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -64,6 +64,7 @@ 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, @@ -133,6 +134,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { { .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, @@ -168,6 +170,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { { .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, @@ -202,6 +205,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { { .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, @@ -236,6 +240,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { { .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, @@ -270,6 +275,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { { .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, @@ -307,6 +313,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { { .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, @@ -347,6 +354,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { { .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, @@ -394,6 +402,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { { .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, @@ -438,6 +447,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { { .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, @@ -472,6 +482,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { { .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, @@ -508,6 +519,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { { .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, @@ -549,6 +561,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { { .id = WCN3990_HW_1_0_DEV_VERSION, .dev_id = 0, + .bus = ATH10K_BUS_PCI, .name = "wcn3990 hw1.0", .continuous_frag_desc = true, .tx_chain_mask = 0x7, @@ -2085,7 +2098,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->bus == ar->hif.bus && + hw_params->id == ar->target_version && hw_params->dev_id == ar->dev_id) break; } diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 61631ab30f65..f6e5c29f74e7 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -92,14 +92,6 @@ struct ath10k; -enum ath10k_bus { - ATH10K_BUS_PCI, - ATH10K_BUS_AHB, - ATH10K_BUS_SDIO, - ATH10K_BUS_USB, - ATH10K_BUS_SNOC, -}; - static inline const char *ath10k_bus_str(enum ath10k_bus bus) { switch (bus) { diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 4c68a496d5d2..1b5da272d18c 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -21,6 +21,14 @@ #include "targaddrs.h" +enum ath10k_bus { + ATH10K_BUS_PCI, + ATH10K_BUS_AHB, + ATH10K_BUS_SDIO, + ATH10K_BUS_USB, + ATH10K_BUS_SNOC, +}; + #define ATH10K_FW_DIR "ath10k" #define QCA988X_2_0_DEVICE_ID_UBNT (0x11ac) @@ -508,6 +516,7 @@ struct ath10k_hw_clk_params { struct ath10k_hw_params { u32 id; u16 dev_id; + enum ath10k_bus bus; const char *name; u32 patch_load_addr; int uart_pin; From 9faaa14387fbb13cf1a314025a047cc3bc27dbef Mon Sep 17 00:00:00 2001 From: Erik Stromdahl Date: Tue, 4 Sep 2018 15:04:26 +0300 Subject: [PATCH 22/51] ath10k: use hw_params.num_peers for num_tids in TLV init As TARGET_TLV_NUM_TIDS is calculated like this: #define TARGET_TLV_NUM_TIDS ((TARGET_TLV_NUM_PEERS) * 2) ... it is better to use the per device hw_params.num_peers value in the TLV init message (if set), rather than using a hard coded value. This makes the value used in the TLV init message match the hw_param value. Signed-off-by: Erik Stromdahl Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi-tlv.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index cdc1e64d52ad..98eaf49f62b6 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -1582,7 +1582,10 @@ static struct sk_buff *ath10k_wmi_tlv_op_gen_init(struct ath10k *ar) } cfg->num_peer_keys = __cpu_to_le32(2); - cfg->num_tids = __cpu_to_le32(TARGET_TLV_NUM_TIDS); + if (ar->hw_params.num_peers) + cfg->num_tids = __cpu_to_le32(ar->hw_params.num_peers * 2); + else + cfg->num_tids = __cpu_to_le32(TARGET_TLV_NUM_TIDS); cfg->tx_chain_mask = __cpu_to_le32(0x7); cfg->rx_chain_mask = __cpu_to_le32(0x7); cfg->rx_timeout_pri[0] = __cpu_to_le32(0x64); From 4875e0b5208555a9cc2c45558f016e0814cbd6cb Mon Sep 17 00:00:00 2001 From: Erik Stromdahl Date: Tue, 4 Sep 2018 15:05:04 +0300 Subject: [PATCH 23/51] ath10k: add per target config of max_num_peers This patch makes sure the value of max_num_peers matches num_peers in hw_params (if set to a non zero value). hw_params->num_peers is used in the TLV WMI init command. If ar->max_num_peers is not set to the same value, there is a risk that the user creates more peers than the maximum number of peers supported by the device. Signed-off-by: Erik Stromdahl Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index e30dcb310bd6..cf3c47b8cb2d 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -2200,6 +2200,7 @@ static void ath10k_core_set_coverage_class_work(struct work_struct *work) static int ath10k_core_init_firmware_features(struct ath10k *ar) { struct ath10k_fw_file *fw_file = &ar->normal_mode_fw.fw_file; + int max_num_peers; if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, fw_file->fw_features) && !test_bit(ATH10K_FW_FEATURE_WMI_10X, fw_file->fw_features)) { @@ -2279,7 +2280,7 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar) switch (fw_file->wmi_op_version) { case ATH10K_FW_WMI_OP_VERSION_MAIN: - ar->max_num_peers = TARGET_NUM_PEERS; + max_num_peers = TARGET_NUM_PEERS; ar->max_num_stations = TARGET_NUM_STATIONS; ar->max_num_vdevs = TARGET_NUM_VDEVS; ar->htt.max_num_pending_tx = TARGET_NUM_MSDU_DESC; @@ -2291,10 +2292,10 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar) case ATH10K_FW_WMI_OP_VERSION_10_2: case ATH10K_FW_WMI_OP_VERSION_10_2_4: if (ath10k_peer_stats_enabled(ar)) { - ar->max_num_peers = TARGET_10X_TX_STATS_NUM_PEERS; + max_num_peers = TARGET_10X_TX_STATS_NUM_PEERS; ar->max_num_stations = TARGET_10X_TX_STATS_NUM_STATIONS; } else { - ar->max_num_peers = TARGET_10X_NUM_PEERS; + max_num_peers = TARGET_10X_NUM_PEERS; ar->max_num_stations = TARGET_10X_NUM_STATIONS; } ar->max_num_vdevs = TARGET_10X_NUM_VDEVS; @@ -2303,7 +2304,7 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar) ar->max_spatial_stream = WMI_MAX_SPATIAL_STREAM; break; case ATH10K_FW_WMI_OP_VERSION_TLV: - ar->max_num_peers = TARGET_TLV_NUM_PEERS; + 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; @@ -2315,7 +2316,7 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar) ar->wmi.mgmt_max_num_pending_tx = TARGET_TLV_MGMT_NUM_MSDU_DESC; break; case ATH10K_FW_WMI_OP_VERSION_10_4: - ar->max_num_peers = TARGET_10_4_NUM_PEERS; + 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; @@ -2334,10 +2335,16 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar) break; case ATH10K_FW_WMI_OP_VERSION_UNSET: case ATH10K_FW_WMI_OP_VERSION_MAX: + default: WARN_ON(1); return -EINVAL; } + if (ar->hw_params.num_peers) + ar->max_num_peers = ar->hw_params.num_peers; + else + ar->max_num_peers = max_num_peers; + /* Backwards compatibility for firmwares without * ATH10K_FW_IE_HTT_OP_VERSION. */ From e66d5361127a485e5d136c870e4019a95907bd7c Mon Sep 17 00:00:00 2001 From: Erik Stromdahl Date: Tue, 4 Sep 2018 15:05:23 +0300 Subject: [PATCH 24/51] ath10k: DMA related fixes for high latency devices Several DMA related functions (such as the dma_map_xxx functions) are not used with high latency devices and don't need to be invoked in this case. Signed-off-by: Erik Stromdahl Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htc.c | 19 ++++++++++++------- drivers/net/wireless/ath/ath10k/htt_tx.c | 3 ++- drivers/net/wireless/ath/ath10k/txrx.c | 3 ++- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index 4e70ff2f78ae..28daed5981a1 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -53,7 +53,8 @@ static inline void ath10k_htc_restore_tx_skb(struct ath10k_htc *htc, { struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb); - dma_unmap_single(htc->ar->dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE); + if (htc->ar->dev_type != ATH10K_DEV_TYPE_HL) + dma_unmap_single(htc->ar->dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE); skb_pull(skb, sizeof(struct ath10k_htc_hdr)); } @@ -137,11 +138,14 @@ int ath10k_htc_send(struct ath10k_htc *htc, ath10k_htc_prepare_tx_skb(ep, skb); skb_cb->eid = eid; - skb_cb->paddr = dma_map_single(dev, skb->data, skb->len, DMA_TO_DEVICE); - ret = dma_mapping_error(dev, skb_cb->paddr); - if (ret) { - ret = -EIO; - goto err_credits; + if (ar->dev_type != ATH10K_DEV_TYPE_HL) { + skb_cb->paddr = dma_map_single(dev, skb->data, skb->len, + DMA_TO_DEVICE); + ret = dma_mapping_error(dev, skb_cb->paddr); + if (ret) { + ret = -EIO; + goto err_credits; + } } sg_item.transfer_id = ep->eid; @@ -157,7 +161,8 @@ int ath10k_htc_send(struct ath10k_htc *htc, return 0; err_unmap: - dma_unmap_single(dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE); + if (ar->dev_type != ATH10K_DEV_TYPE_HL) + dma_unmap_single(dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE); err_credits: if (ep->tx_credit_flow_enabled) { spin_lock_bh(&htc->tx_lock); diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index 7cff0d52338f..965acd57b22f 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -1123,7 +1123,8 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) return 0; err_unmap_msdu: - dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); + if (ar->dev_type != ATH10K_DEV_TYPE_HL) + dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); err_free_txdesc: dev_kfree_skb_any(txdesc); err_free_msdu_id: diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c index cda164f6e9f6..23606b6972d0 100644 --- a/drivers/net/wireless/ath/ath10k/txrx.c +++ b/drivers/net/wireless/ath/ath10k/txrx.c @@ -95,7 +95,8 @@ int ath10k_txrx_tx_unref(struct ath10k_htt *htt, wake_up(&htt->empty_tx_wq); spin_unlock_bh(&htt->tx_lock); - dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); + if (ar->dev_type != ATH10K_DEV_TYPE_HL) + dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); ath10k_report_offchan_tx(htt->ar, msdu); From 852d1bf86a5bbde228e2b760c9a097d454c7508f Mon Sep 17 00:00:00 2001 From: Erik Stromdahl Date: Tue, 4 Sep 2018 15:05:43 +0300 Subject: [PATCH 25/51] ath10k: add HTT TX HL ops Initial HTT TX ops for high latency devices. Signed-off-by: Erik Stromdahl Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt_tx.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index 965acd57b22f..9cbfc45b4b60 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -1562,11 +1562,17 @@ static const struct ath10k_htt_tx_ops htt_tx_ops_64 = { .htt_free_txbuff = ath10k_htt_tx_free_cont_txbuf_64, }; +static const struct ath10k_htt_tx_ops htt_tx_ops_hl = { + .htt_send_frag_desc_bank_cfg = ath10k_htt_send_frag_desc_bank_cfg_32, +}; + void ath10k_htt_set_tx_ops(struct ath10k_htt *htt) { struct ath10k *ar = htt->ar; - if (ar->hw_params.target_64bit) + if (ar->dev_type == ATH10K_DEV_TYPE_HL) + htt->tx_ops = &htt_tx_ops_hl; + else if (ar->hw_params.target_64bit) htt->tx_ops = &htt_tx_ops_64; else htt->tx_ops = &htt_tx_ops_32; From d4e7f553eec3b66232e78ba6a2dfe5e2d3eb8fa0 Mon Sep 17 00:00:00 2001 From: Erik Stromdahl Date: Tue, 4 Sep 2018 15:05:57 +0300 Subject: [PATCH 26/51] ath10k: add HTT RX HL ops Initial (empty) HTT RX ops for high latency devices. Signed-off-by: Erik Stromdahl Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt_rx.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index daccf30916f0..231e5914e16a 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -3113,11 +3113,16 @@ static const struct ath10k_htt_rx_ops htt_rx_ops_64 = { .htt_reset_paddrs_ring = ath10k_htt_reset_paddrs_ring_64, }; +static const struct ath10k_htt_rx_ops htt_rx_ops_hl = { +}; + void ath10k_htt_set_rx_ops(struct ath10k_htt *htt) { struct ath10k *ar = htt->ar; - if (ar->hw_params.target_64bit) + if (ar->dev_type == ATH10K_DEV_TYPE_HL) + htt->rx_ops = &htt_rx_ops_hl; + else if (ar->hw_params.target_64bit) htt->rx_ops = &htt_rx_ops_64; else htt->rx_ops = &htt_rx_ops_32; From 4daacc950d4d005929a5c73ecbb8d97b930a8ed9 Mon Sep 17 00:00:00 2001 From: Erik Stromdahl Date: Tue, 4 Sep 2018 15:06:13 +0300 Subject: [PATCH 27/51] ath10k: htt: RX ring config HL support Special HTT RX ring config message used by high latency devices. The main difference between HL and LL is that HL devices do not use shared memory between device and host and thus, no host paddr's are added to the RX config message. Signed-off-by: Erik Stromdahl Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt_tx.c | 52 ++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index 9cbfc45b4b60..f605be98bae8 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -934,6 +934,57 @@ static int ath10k_htt_send_rx_ring_cfg_64(struct ath10k_htt *htt) return 0; } +static int ath10k_htt_send_rx_ring_cfg_hl(struct ath10k_htt *htt) +{ + struct ath10k *ar = htt->ar; + struct sk_buff *skb; + struct htt_cmd *cmd; + struct htt_rx_ring_setup_ring32 *ring; + const int num_rx_ring = 1; + u16 flags; + int len; + int ret; + + /* + * the HW expects the buffer to be an integral number of 4-byte + * "words" + */ + BUILD_BUG_ON(!IS_ALIGNED(HTT_RX_BUF_SIZE, 4)); + BUILD_BUG_ON((HTT_RX_BUF_SIZE & HTT_MAX_CACHE_LINE_SIZE_MASK) != 0); + + len = sizeof(cmd->hdr) + sizeof(cmd->rx_setup_32.hdr) + + (sizeof(*ring) * num_rx_ring); + skb = ath10k_htc_alloc_skb(ar, len); + if (!skb) + return -ENOMEM; + + skb_put(skb, len); + + cmd = (struct htt_cmd *)skb->data; + ring = &cmd->rx_setup_32.rings[0]; + + cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_RX_RING_CFG; + cmd->rx_setup_32.hdr.num_rings = 1; + + flags = 0; + flags |= HTT_RX_RING_FLAGS_MSDU_PAYLOAD; + flags |= HTT_RX_RING_FLAGS_UNICAST_RX; + flags |= HTT_RX_RING_FLAGS_MULTICAST_RX; + + memset(ring, 0, sizeof(*ring)); + ring->rx_ring_len = __cpu_to_le16(HTT_RX_RING_SIZE_MIN); + ring->rx_ring_bufsize = __cpu_to_le16(HTT_RX_BUF_SIZE); + ring->flags = __cpu_to_le16(flags); + + ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + return 0; +} + int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt, u8 max_subfrms_ampdu, u8 max_subfrms_amsdu) @@ -1563,6 +1614,7 @@ static const struct ath10k_htt_tx_ops htt_tx_ops_64 = { }; static const struct ath10k_htt_tx_ops htt_tx_ops_hl = { + .htt_send_rx_ring_cfg = ath10k_htt_send_rx_ring_cfg_hl, .htt_send_frag_desc_bank_cfg = ath10k_htt_send_frag_desc_bank_cfg_32, }; From a2097d6444c3c38e58b9570b5ef70345a58b7c4f Mon Sep 17 00:00:00 2001 From: Erik Stromdahl Date: Tue, 4 Sep 2018 15:06:38 +0300 Subject: [PATCH 28/51] ath10k: htt: High latency TX support Add HTT TX function for HL interfaces. Intended for SDIO and USB. Signed-off-by: Erik Stromdahl Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt_tx.c | 92 ++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index f605be98bae8..ad05ab714c9b 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -495,6 +495,9 @@ int ath10k_htt_tx_start(struct ath10k_htt *htt) if (htt->tx_mem_allocated) return 0; + if (ar->dev_type == ATH10K_DEV_TYPE_HL) + return 0; + ret = ath10k_htt_tx_alloc_buf(htt); if (ret) goto free_idr_pending_tx; @@ -1186,6 +1189,94 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) return res; } +#define HTT_TX_HL_NEEDED_HEADROOM \ + (unsigned int)(sizeof(struct htt_cmd_hdr) + \ + sizeof(struct htt_data_tx_desc) + \ + sizeof(struct ath10k_htc_hdr)) + +static int ath10k_htt_tx_hl(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode, + struct sk_buff *msdu) +{ + struct ath10k *ar = htt->ar; + int res, data_len; + struct htt_cmd_hdr *cmd_hdr; + struct htt_data_tx_desc *tx_desc; + struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu); + struct sk_buff *tmp_skb; + bool is_eth = (txmode == ATH10K_HW_TXRX_ETHERNET); + u8 vdev_id = ath10k_htt_tx_get_vdev_id(ar, msdu); + u8 tid = ath10k_htt_tx_get_tid(msdu, is_eth); + u8 flags0 = 0; + u16 flags1 = 0; + + data_len = msdu->len; + + switch (txmode) { + case ATH10K_HW_TXRX_RAW: + case ATH10K_HW_TXRX_NATIVE_WIFI: + flags0 |= HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT; + /* fall through */ + case ATH10K_HW_TXRX_ETHERNET: + flags0 |= SM(txmode, HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE); + break; + case ATH10K_HW_TXRX_MGMT: + flags0 |= SM(ATH10K_HW_TXRX_MGMT, + HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE); + flags0 |= HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT; + break; + } + + if (skb_cb->flags & ATH10K_SKB_F_NO_HWCRYPT) + flags0 |= HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT; + + flags1 |= SM((u16)vdev_id, HTT_DATA_TX_DESC_FLAGS1_VDEV_ID); + flags1 |= SM((u16)tid, HTT_DATA_TX_DESC_FLAGS1_EXT_TID); + if (msdu->ip_summed == CHECKSUM_PARTIAL && + !test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) { + flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD; + flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD; + } + + /* Prepend the HTT header and TX desc struct to the data message + * and realloc the skb if it does not have enough headroom. + */ + if (skb_headroom(msdu) < HTT_TX_HL_NEEDED_HEADROOM) { + tmp_skb = msdu; + + ath10k_dbg(htt->ar, ATH10K_DBG_HTT, + "Not enough headroom in skb. Current headroom: %u, needed: %u. Reallocating...\n", + skb_headroom(msdu), HTT_TX_HL_NEEDED_HEADROOM); + msdu = skb_realloc_headroom(msdu, HTT_TX_HL_NEEDED_HEADROOM); + kfree_skb(tmp_skb); + if (!msdu) { + ath10k_warn(htt->ar, "htt hl tx: Unable to realloc skb!\n"); + res = -ENOMEM; + goto out; + } + } + + skb_push(msdu, sizeof(*cmd_hdr)); + skb_push(msdu, sizeof(*tx_desc)); + cmd_hdr = (struct htt_cmd_hdr *)msdu->data; + tx_desc = (struct htt_data_tx_desc *)(msdu->data + sizeof(*cmd_hdr)); + + cmd_hdr->msg_type = HTT_H2T_MSG_TYPE_TX_FRM; + tx_desc->flags0 = flags0; + tx_desc->flags1 = __cpu_to_le16(flags1); + tx_desc->len = __cpu_to_le16(data_len); + tx_desc->id = 0; + tx_desc->frags_paddr = 0; /* always zero */ + /* Initialize peer_id to INVALID_PEER because this is NOT + * Reinjection path + */ + tx_desc->peerid = __cpu_to_le32(HTT_INVALID_PEERID); + + res = ath10k_htc_send(&htt->ar->htc, htt->eid, msdu); + +out: + return res; +} + static int ath10k_htt_tx_32(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode, struct sk_buff *msdu) @@ -1616,6 +1707,7 @@ static const struct ath10k_htt_tx_ops htt_tx_ops_64 = { static const struct ath10k_htt_tx_ops htt_tx_ops_hl = { .htt_send_rx_ring_cfg = ath10k_htt_send_rx_ring_cfg_hl, .htt_send_frag_desc_bank_cfg = ath10k_htt_send_frag_desc_bank_cfg_32, + .htt_tx = ath10k_htt_tx_hl, }; void ath10k_htt_set_tx_ops(struct ath10k_htt *htt) From f88d493450403f5c5b7495232585e54cc4f89ccd Mon Sep 17 00:00:00 2001 From: Erik Stromdahl Date: Tue, 4 Sep 2018 15:06:58 +0300 Subject: [PATCH 29/51] ath10k: htt: High latency RX support Special HTT RX handling for high latency interfaces. Since no DMA physical addresses are used in the RX ring config message (this is not supported by the high latency devices), no RX ring is allocated. All RX skb's are allocated by the driver and passed directly to mac80211 in the HTT RX indication handler. A nice side effect of this is that no huge buffer will be allocated with dma_alloc_coherent. On embedded systems with limited memory resources, the allocation of the RX ring is prone to fail. Some tweaks made to "make it work": Removal of protected bit in 802.11 header frame control field. The chipset seems to do hw decryption but the frame_control protected bit is still set. This is necessary for mac80211 not to drop the frame. Signed-off-by: Erik Stromdahl Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt.h | 47 ++++++++ drivers/net/wireless/ath/ath10k/htt_rx.c | 128 +++++++++++++++++++++- drivers/net/wireless/ath/ath10k/rx_desc.h | 15 +++ 3 files changed, 187 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 26ce532e8196..3a7f8383871d 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -721,6 +721,15 @@ struct htt_rx_indication { struct htt_rx_indication_mpdu_range mpdu_ranges[0]; } __packed; +/* High latency version of the RX indication */ +struct htt_rx_indication_hl { + struct htt_rx_indication_hdr hdr; + struct htt_rx_indication_ppdu ppdu; + struct htt_rx_indication_prefix prefix; + struct fw_rx_desc_hl fw_desc; + struct htt_rx_indication_mpdu_range mpdu_ranges[0]; +} __packed; + static inline struct htt_rx_indication_mpdu_range * htt_rx_ind_get_mpdu_ranges(struct htt_rx_indication *rx_ind) { @@ -733,6 +742,18 @@ static inline struct htt_rx_indication_mpdu_range * return ptr; } +static inline struct htt_rx_indication_mpdu_range * + htt_rx_ind_get_mpdu_ranges_hl(struct htt_rx_indication_hl *rx_ind) +{ + void *ptr = rx_ind; + + ptr += sizeof(rx_ind->hdr) + + sizeof(rx_ind->ppdu) + + sizeof(rx_ind->prefix) + + sizeof(rx_ind->fw_desc); + return ptr; +} + enum htt_rx_flush_mpdu_status { HTT_RX_FLUSH_MPDU_DISCARD = 0, HTT_RX_FLUSH_MPDU_REORDER = 1, @@ -1643,6 +1664,7 @@ struct htt_resp { struct htt_mgmt_tx_completion mgmt_tx_completion; struct htt_data_tx_completion data_tx_completion; struct htt_rx_indication rx_ind; + struct htt_rx_indication_hl rx_ind_hl; struct htt_rx_fragment_indication rx_frag_ind; struct htt_rx_peer_map peer_map; struct htt_rx_peer_unmap peer_unmap; @@ -1996,6 +2018,31 @@ struct htt_rx_desc { u8 msdu_payload[0]; }; +#define HTT_RX_DESC_HL_INFO_SEQ_NUM_MASK 0x00000fff +#define HTT_RX_DESC_HL_INFO_SEQ_NUM_LSB 0 +#define HTT_RX_DESC_HL_INFO_ENCRYPTED_MASK 0x00001000 +#define HTT_RX_DESC_HL_INFO_ENCRYPTED_LSB 12 +#define HTT_RX_DESC_HL_INFO_CHAN_INFO_PRESENT_MASK 0x00002000 +#define HTT_RX_DESC_HL_INFO_CHAN_INFO_PRESENT_LSB 13 +#define HTT_RX_DESC_HL_INFO_MCAST_BCAST_MASK 0x00008000 +#define HTT_RX_DESC_HL_INFO_MCAST_BCAST_LSB 15 +#define HTT_RX_DESC_HL_INFO_FRAGMENT_MASK 0x00010000 +#define HTT_RX_DESC_HL_INFO_FRAGMENT_LSB 16 +#define HTT_RX_DESC_HL_INFO_KEY_ID_OCT_MASK 0x01fe0000 +#define HTT_RX_DESC_HL_INFO_KEY_ID_OCT_LSB 17 + +struct htt_rx_desc_base_hl { + __le32 info; /* HTT_RX_DESC_HL_INFO_ */ +}; + +struct htt_rx_chan_info { + __le16 primary_chan_center_freq_mhz; + __le16 contig_chan1_center_freq_mhz; + __le16 contig_chan2_center_freq_mhz; + u8 phy_mode; + u8 reserved; +} __packed; + #define HTT_RX_DESC_ALIGN 8 #define HTT_MAC_ADDR_LEN 6 diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 231e5914e16a..769da2d1f780 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -265,6 +265,9 @@ int ath10k_htt_rx_ring_refill(struct ath10k *ar) struct ath10k_htt *htt = &ar->htt; int ret; + if (ar->dev_type == ATH10K_DEV_TYPE_HL) + return 0; + spin_lock_bh(&htt->rx_ring.lock); ret = ath10k_htt_rx_ring_fill_n(htt, (htt->rx_ring.fill_level - htt->rx_ring.fill_cnt)); @@ -279,6 +282,9 @@ int ath10k_htt_rx_ring_refill(struct ath10k *ar) void ath10k_htt_rx_free(struct ath10k_htt *htt) { + if (htt->ar->dev_type == ATH10K_DEV_TYPE_HL) + return; + del_timer_sync(&htt->rx_ring.refill_retry_timer); skb_queue_purge(&htt->rx_msdus_q); @@ -570,6 +576,9 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt) size_t size; struct timer_list *timer = &htt->rx_ring.refill_retry_timer; + if (ar->dev_type == ATH10K_DEV_TYPE_HL) + return 0; + htt->rx_confused = false; /* XXX: The fill level could be changed during runtime in response to @@ -1846,8 +1855,116 @@ static int ath10k_htt_rx_handle_amsdu(struct ath10k_htt *htt) return 0; } -static void ath10k_htt_rx_proc_rx_ind(struct ath10k_htt *htt, - struct htt_rx_indication *rx) +static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt, + struct htt_rx_indication_hl *rx, + struct sk_buff *skb) +{ + struct ath10k *ar = htt->ar; + struct ath10k_peer *peer; + struct htt_rx_indication_mpdu_range *mpdu_ranges; + struct fw_rx_desc_hl *fw_desc; + struct ieee80211_hdr *hdr; + struct ieee80211_rx_status *rx_status; + u16 peer_id; + u8 rx_desc_len; + int num_mpdu_ranges; + size_t tot_hdr_len; + struct ieee80211_channel *ch; + + peer_id = __le16_to_cpu(rx->hdr.peer_id); + + spin_lock_bh(&ar->data_lock); + peer = ath10k_peer_find_by_id(ar, peer_id); + spin_unlock_bh(&ar->data_lock); + if (!peer) + ath10k_warn(ar, "Got RX ind from invalid peer: %u\n", peer_id); + + num_mpdu_ranges = MS(__le32_to_cpu(rx->hdr.info1), + HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES); + mpdu_ranges = htt_rx_ind_get_mpdu_ranges_hl(rx); + fw_desc = &rx->fw_desc; + rx_desc_len = fw_desc->len; + + /* I have not yet seen any case where num_mpdu_ranges > 1. + * qcacld does not seem handle that case either, so we introduce the + * same limitiation here as well. + */ + if (num_mpdu_ranges > 1) + ath10k_warn(ar, + "Unsupported number of MPDU ranges: %d, ignoring all but the first\n", + num_mpdu_ranges); + + if (mpdu_ranges->mpdu_range_status != + HTT_RX_IND_MPDU_STATUS_OK) { + ath10k_warn(ar, "MPDU range status: %d\n", + mpdu_ranges->mpdu_range_status); + goto err; + } + + /* Strip off all headers before the MAC header before delivery to + * mac80211 + */ + tot_hdr_len = sizeof(struct htt_resp_hdr) + sizeof(rx->hdr) + + sizeof(rx->ppdu) + sizeof(rx->prefix) + + sizeof(rx->fw_desc) + + sizeof(*mpdu_ranges) * num_mpdu_ranges + rx_desc_len; + skb_pull(skb, tot_hdr_len); + + hdr = (struct ieee80211_hdr *)skb->data; + rx_status = IEEE80211_SKB_RXCB(skb); + rx_status->chains |= BIT(0); + rx_status->signal = ATH10K_DEFAULT_NOISE_FLOOR + + rx->ppdu.combined_rssi; + rx_status->flag &= ~RX_FLAG_NO_SIGNAL_VAL; + + spin_lock_bh(&ar->data_lock); + ch = ar->scan_channel; + if (!ch) + ch = ar->rx_channel; + if (!ch) + ch = ath10k_htt_rx_h_any_channel(ar); + if (!ch) + ch = ar->tgt_oper_chan; + spin_unlock_bh(&ar->data_lock); + + if (ch) { + rx_status->band = ch->band; + rx_status->freq = ch->center_freq; + } + if (rx->fw_desc.flags & FW_RX_DESC_FLAGS_LAST_MSDU) + rx_status->flag &= ~RX_FLAG_AMSDU_MORE; + else + rx_status->flag |= RX_FLAG_AMSDU_MORE; + + /* Not entirely sure about this, but all frames from the chipset has + * the protected flag set even though they have already been decrypted. + * Unmasking this flag is necessary in order for mac80211 not to drop + * the frame. + * TODO: Verify this is always the case or find out a way to check + * if there has been hw decryption. + */ + if (ieee80211_has_protected(hdr->frame_control)) { + hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED); + rx_status->flag |= RX_FLAG_DECRYPTED | + RX_FLAG_IV_STRIPPED | + RX_FLAG_MMIC_STRIPPED; + } + + ieee80211_rx_ni(ar->hw, skb); + + /* We have delivered the skb to the upper layers (mac80211) so we + * must not free it. + */ + return false; +err: + /* Tell the caller that it must free the skb since we have not + * consumed it + */ + return true; +} + +static void ath10k_htt_rx_proc_rx_ind_ll(struct ath10k_htt *htt, + struct htt_rx_indication *rx) { struct ath10k *ar = htt->ar; struct htt_rx_indication_mpdu_range *mpdu_ranges; @@ -2829,7 +2946,12 @@ bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) break; } case HTT_T2H_MSG_TYPE_RX_IND: - ath10k_htt_rx_proc_rx_ind(htt, &resp->rx_ind); + if (ar->dev_type == ATH10K_DEV_TYPE_HL) + return ath10k_htt_rx_proc_rx_ind_hl(htt, + &resp->rx_ind_hl, + skb); + else + ath10k_htt_rx_proc_rx_ind_ll(htt, &resp->rx_ind); break; case HTT_T2H_MSG_TYPE_PEER_MAP: { struct htt_peer_map_event ev = { diff --git a/drivers/net/wireless/ath/ath10k/rx_desc.h b/drivers/net/wireless/ath/ath10k/rx_desc.h index ea4075d456fa..310674de3cb8 100644 --- a/drivers/net/wireless/ath/ath10k/rx_desc.h +++ b/drivers/net/wireless/ath/ath10k/rx_desc.h @@ -1277,4 +1277,19 @@ struct fw_rx_desc_base { u8 info0; } __packed; +#define FW_RX_DESC_FLAGS_FIRST_MSDU (1 << 0) +#define FW_RX_DESC_FLAGS_LAST_MSDU (1 << 1) +#define FW_RX_DESC_C3_FAILED (1 << 2) +#define FW_RX_DESC_C4_FAILED (1 << 3) +#define FW_RX_DESC_IPV6 (1 << 4) +#define FW_RX_DESC_TCP (1 << 5) +#define FW_RX_DESC_UDP (1 << 6) + +struct fw_rx_desc_hl { + u8 info0; + u8 version; + u8 len; + u8 flags; +} __packed; + #endif /* _RX_DESC_H_ */ From 37f62c0d5822f631b786b29a1b1069ab714d1a28 Mon Sep 17 00:00:00 2001 From: Erik Stromdahl Date: Tue, 4 Sep 2018 15:07:07 +0300 Subject: [PATCH 30/51] ath10k: wmi: disable softirq's while calling ieee80211_rx This is done in order not to trig the below warning in ieee80211_rx_napi: WARN_ON_ONCE(softirq_count() == 0); ieee80211_rx_napi requires that softirq's are disabled during execution. The High latency bus drivers (SDIO and USB) sometimes call the wmi ep_rx_complete callback from non softirq context, resulting in a trigger of the above warning. Calling ieee80211_rx_ni with softirq's already disabled (e.g., from softirq context) should be safe as the local_bh_disable and local_bh_enable functions (called from ieee80211_rx_ni) are fully reentrant. Signed-off-by: Erik Stromdahl Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 43daa4e57bc1..0f9fccc6322b 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -2482,7 +2482,8 @@ int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) status->freq, status->band, status->signal, status->rate_idx); - ieee80211_rx(ar->hw, skb); + ieee80211_rx_ni(ar->hw, skb); + return 0; } From 833fd34d743c728afe6d127ef7bee67e7d9199a8 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Thu, 6 Sep 2018 19:46:20 +0300 Subject: [PATCH 31/51] ath10k: fix vdev-start timeout on error The vdev-start-response message should cause the completion to fire, even in the error case. Otherwise, the user still gets no useful information and everything is blocked until the timeout period. Add some warning text to print out the invalid status code to aid debugging, and propagate failure code. Signed-off-by: Ben Greear Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 1 + drivers/net/wireless/ath/ath10k/mac.c | 2 +- drivers/net/wireless/ath/ath10k/wmi.c | 19 ++++++++++++++++--- drivers/net/wireless/ath/ath10k/wmi.h | 8 +++++++- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index f6e5c29f74e7..7328df1bf39f 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -1043,6 +1043,7 @@ struct ath10k { struct completion install_key_done; + int last_wmi_vdev_start_status; struct completion vdev_setup_done; struct workqueue_struct *workqueue; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 97548f96a2f7..9afc9dc67152 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -968,7 +968,7 @@ static inline int ath10k_vdev_setup_sync(struct ath10k *ar) if (time_left == 0) return -ETIMEDOUT; - return 0; + return ar->last_wmi_vdev_start_status; } static int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id) diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 0f9fccc6322b..8093d947ddd2 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -3243,18 +3243,31 @@ void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar, struct sk_buff *skb) { struct wmi_vdev_start_ev_arg arg = {}; int ret; + u32 status; ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_START_RESP_EVENTID\n"); + ar->last_wmi_vdev_start_status = 0; + ret = ath10k_wmi_pull_vdev_start(ar, skb, &arg); if (ret) { ath10k_warn(ar, "failed to parse vdev start event: %d\n", ret); - return; + ar->last_wmi_vdev_start_status = ret; + goto out; } - if (WARN_ON(__le32_to_cpu(arg.status))) - return; + status = __le32_to_cpu(arg.status); + if (WARN_ON_ONCE(status)) { + ath10k_warn(ar, "vdev-start-response reports status error: %d (%s)\n", + status, (status == WMI_VDEV_START_CHAN_INVALID) ? + "chan-invalid" : "unknown"); + /* Setup is done one way or another though, so we should still + * do the completion, so don't return here. + */ + ar->last_wmi_vdev_start_status = -EINVAL; + } +out: complete(&ar->vdev_setup_done); } diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 15622943a885..3c09b0e6bd9b 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -6655,11 +6655,17 @@ struct wmi_ch_info_ev_arg { __le32 rx_frame_count; }; +/* From 10.4 firmware, not sure all have the same values. */ +enum wmi_vdev_start_status { + WMI_VDEV_START_OK = 0, + WMI_VDEV_START_CHAN_INVALID, +}; + struct wmi_vdev_start_ev_arg { __le32 vdev_id; __le32 req_id; __le32 resp_type; /* %WMI_VDEV_RESP_ */ - __le32 status; + __le32 status; /* See wmi_vdev_start_status enum above */ }; struct wmi_peer_kick_ev_arg { From 058a7eab9d9ee12f57282eb0b606668dada70d7a Mon Sep 17 00:00:00 2001 From: Rakesh Pillai Date: Thu, 6 Sep 2018 18:57:27 +0530 Subject: [PATCH 32/51] ath10k: fix tx status flag setting for management frames The tx_status for management frames is being filled incorrectly in the flags of skb_cb. This incorrect flag setting causes the upper layers to consider that the particular frame was not transmitted properly, leading to improper behavior. Set the IEEE80211_TX_STAT_ACK flag in the info flags of skb_cb, to indicate the successful transmission of the management frame. Tested HW: WCN3990 Tested FW: WLAN.HL.2.0-01188-QCAHLSWMTPLZ-1 Fixes: dc405152bb64d4ae01c9ac669de25b2d1fb6fc2d Signed-off-by: Rakesh Pillai Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 8093d947ddd2..c1a46af5f13f 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -2342,7 +2342,12 @@ static int wmi_process_mgmt_tx_comp(struct ath10k *ar, u32 desc_id, dma_unmap_single(ar->dev, pkt_addr->paddr, msdu->len, DMA_FROM_DEVICE); info = IEEE80211_SKB_CB(msdu); - info->flags |= status; + + if (status) + info->flags &= ~IEEE80211_TX_STAT_ACK; + else + info->flags |= IEEE80211_TX_STAT_ACK; + ieee80211_tx_status_irqsafe(ar->hw, msdu); ret = 0; From 234e4307cf7f9b43842395295a224740e220d019 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 7 Sep 2018 10:21:57 -0700 Subject: [PATCH 33/51] ath10k: utilize random MAC address if none is provided Since commit 9d5804662ce1 ("ath10k: retrieve MAC address from system firmware if provided"), we support pulling our MAC address from system firmware. Along with this, we should admit that it's possible for system firmware to be screwed up as well, omitting the MAC address there. Provide a fallback, such that we choose a randomly-generated MAC address, with the local assignment bit set. This is not a preferred mode of operation and really should only serve the purpose of keeping the system chugging along while someone figures out what's wrong with the system. Grepping through other wireless drivers shows very similar fallback logic though, so why not? git grep eth_random_addr drivers/net/wireless/ Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 9afc9dc67152..2cb196ce307b 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -8325,6 +8325,10 @@ int ath10k_mac_register(struct ath10k *ar) void *channels; int ret; + if (!is_valid_ether_addr(ar->mac_addr)) { + ath10k_warn(ar, "invalid MAC address; choosing random\n"); + eth_random_addr(ar->mac_addr); + } SET_IEEE80211_PERM_ADDR(ar->hw, ar->mac_addr); SET_IEEE80211_DEV(ar->hw, ar->dev); From 8e8374db3424c0ee3e399525ac737cc6a0814d19 Mon Sep 17 00:00:00 2001 From: Erik Stromdahl Date: Thu, 13 Sep 2018 16:45:05 +0200 Subject: [PATCH 34/51] ath10k: add default value for num_peers in WMI TLV init The hw_params value will only be used if it was explicitly set in the global hw_params array. This makes it possible to have the num_peers member unset. Signed-off-by: Erik Stromdahl Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi-tlv.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index 98eaf49f62b6..0f78a0d42ebc 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -1569,7 +1569,10 @@ static struct sk_buff *ath10k_wmi_tlv_op_gen_init(struct ath10k *ar) cfg->num_vdevs = __cpu_to_le32(TARGET_TLV_NUM_VDEVS); - cfg->num_peers = __cpu_to_le32(ar->hw_params.num_peers); + if (ar->hw_params.num_peers) + cfg->num_peers = __cpu_to_le32(ar->hw_params.num_peers); + else + cfg->num_peers = __cpu_to_le32(TARGET_TLV_NUM_PEERS); cfg->ast_skid_limit = __cpu_to_le32(ar->hw_params.ast_skid_limit); cfg->num_wds_entries = __cpu_to_le32(ar->hw_params.num_wds_entries); From 08fafd646d633e77e958337087bd07ee163b31ac Mon Sep 17 00:00:00 2001 From: Erik Stromdahl Date: Thu, 13 Sep 2018 16:45:06 +0200 Subject: [PATCH 35/51] ath10k: remove num_peers in hw_params for most targets >From commit "ath10k: add per target config of max_num_peers", the num_peers value in struct ath10k_hw_params is used to override the value obtained from the WMI op version in the firmware file. Currently, only high latency devices (USB and SDIO) need to use this value, since the value obtained from the WMI op version is not applicable for these devices. Many devices in ath10k_hw_params_list have the wrong value set. An example of this is the QCA9880 hw2.0 chipset that had (before this patch) num_peers set to TARGET_TLV_NUM_PEERS although the firmware uses WMI version ATH10K_FW_WMI_OP_VERSION_10_2_4. Using the wrong value for the QCA9880 chip results in a fw crash: ath10k_pci 0000:01:00.0: firmware crashed! (guid ddf39880-3e64-4edf-b4e4-979d31724503) ath10k_pci 0000:01:00.0: qca988x hw2.0 target 0x4100016c chip_id 0x043202ff sub 0000:0000 ath10k_pci 0000:01:00.0: kconfig debug 1 debugfs 1 tracing 1 dfs 1 testmode 0 ath10k_pci 0000:01:00.0: firmware ver 10.2.4-1.0-00037 api 5 features no-p2p,raw-mode,mfp,allows-mesh-bcast crc32 a4a52adb ath10k_pci 0000:01:00.0: board_file api 1 bmi_id N/A crc32 bebc7c08 ath10k_pci 0000:01:00.0: htt-ver 0.0 wmi-op 5 htt-op 2 cal otp max-sta 128 raw 0 hwcrypto 1 ath10k_pci 0000:01:00.0: firmware register dump: ath10k_pci 0000:01:00.0: [00]: 0x4100016C 0x000015B3 0x009B54B7 0x00955B31 ath10k_pci 0000:01:00.0: [04]: 0x009B54B7 0x00060530 0x00000011 0x00400000 ath10k_pci 0000:01:00.0: [08]: 0x000001B8 0x00000007 0x00443138 0x00955A00 ath10k_pci 0000:01:00.0: [12]: 0x00000009 0x00000000 0x009B548E 0x009B54AB ath10k_pci 0000:01:00.0: [16]: 0x00958080 0x0094078E 0x00000000 0x00000000 ath10k_pci 0000:01:00.0: [20]: 0x409B54B7 0x0040AC64 0x00400000 0x00000087 ath10k_pci 0000:01:00.0: [24]: 0x809A3831 0x0040ACC4 0x000001B1 0xC09B54B7 ath10k_pci 0000:01:00.0: [28]: 0x809A2F08 0x0040AD04 0x00410410 0x00000001 ath10k_pci 0000:01:00.0: [32]: 0x80988856 0x0040AD24 0x00410410 0x00000001 ath10k_pci 0000:01:00.0: [36]: 0x8098AF64 0x0040AD44 0x00410410 0x00000001 ath10k_pci 0000:01:00.0: [40]: 0x809B5DEC 0x0040AD84 0x00410410 0x00410FF4 ath10k_pci 0000:01:00.0: [44]: 0x809B1F20 0x0040ADA4 0x00400000 0x00416C54 ath10k_pci 0000:01:00.0: [48]: 0x809C0295 0x0040ADE4 0x0040AE08 0x00411DC8 ath10k_pci 0000:01:00.0: [52]: 0x809486FA 0x0040AE04 0x00000001 0x00000000 ath10k_pci 0000:01:00.0: [56]: 0x80948E2C 0x0040AEA4 0x0041F290 0x004123D4 ath10k_pci 0000:01:00.0: Copy Engine register dump: ath10k_pci 0000:01:00.0: [00]: 0x00057400 10 10 3 3 ath10k_pci 0000:01:00.0: [01]: 0x00057800 3 3 8 9 ath10k_pci 0000:01:00.0: [02]: 0x00057c00 2 2 1 2 ath10k_pci 0000:01:00.0: [03]: 0x00058000 1 1 2 1 ath10k_pci 0000:01:00.0: [04]: 0x00058400 0 0 40 0 ath10k_pci 0000:01:00.0: [05]: 0x00058800 1 1 0 1 ath10k_pci 0000:01:00.0: [06]: 0x00058c00 0 0 0 0 ath10k_pci 0000:01:00.0: [07]: 0x00059000 0 0 0 0 Signed-off-by: Erik Stromdahl Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index cf3c47b8cb2d..203f30992c26 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -86,7 +86,6 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .vht160_mcs_rx_highest = 0, .vht160_mcs_tx_highest = 0, .n_cipher_suites = 8, - .num_peers = TARGET_TLV_NUM_PEERS, .ast_skid_limit = 0x10, .num_wds_entries = 0x20, .target_64bit = false, @@ -120,7 +119,6 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .vht160_mcs_rx_highest = 0, .vht160_mcs_tx_highest = 0, .n_cipher_suites = 8, - .num_peers = TARGET_TLV_NUM_PEERS, .ast_skid_limit = 0x10, .num_wds_entries = 0x20, .target_64bit = false, @@ -156,7 +154,6 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .vht160_mcs_rx_highest = 0, .vht160_mcs_tx_highest = 0, .n_cipher_suites = 8, - .num_peers = TARGET_TLV_NUM_PEERS, .ast_skid_limit = 0x10, .num_wds_entries = 0x20, .target_64bit = false, @@ -191,7 +188,6 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .vht160_mcs_rx_highest = 0, .vht160_mcs_tx_highest = 0, .n_cipher_suites = 8, - .num_peers = TARGET_TLV_NUM_PEERS, .ast_skid_limit = 0x10, .num_wds_entries = 0x20, .target_64bit = false, @@ -226,7 +222,6 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .vht160_mcs_rx_highest = 0, .vht160_mcs_tx_highest = 0, .n_cipher_suites = 8, - .num_peers = TARGET_TLV_NUM_PEERS, .ast_skid_limit = 0x10, .num_wds_entries = 0x20, .target_64bit = false, @@ -261,7 +256,6 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .vht160_mcs_rx_highest = 0, .vht160_mcs_tx_highest = 0, .n_cipher_suites = 8, - .num_peers = TARGET_TLV_NUM_PEERS, .ast_skid_limit = 0x10, .num_wds_entries = 0x20, .target_64bit = false, @@ -299,7 +293,6 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .vht160_mcs_rx_highest = 0, .vht160_mcs_tx_highest = 0, .n_cipher_suites = 8, - .num_peers = TARGET_TLV_NUM_PEERS, .ast_skid_limit = 0x10, .num_wds_entries = 0x20, .target_64bit = false, @@ -340,7 +333,6 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .vht160_mcs_rx_highest = 0, .vht160_mcs_tx_highest = 0, .n_cipher_suites = 11, - .num_peers = TARGET_TLV_NUM_PEERS, .ast_skid_limit = 0x10, .num_wds_entries = 0x20, .target_64bit = false, @@ -388,7 +380,6 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .vht160_mcs_rx_highest = 1560, .vht160_mcs_tx_highest = 1560, .n_cipher_suites = 11, - .num_peers = TARGET_TLV_NUM_PEERS, .ast_skid_limit = 0x10, .num_wds_entries = 0x20, .target_64bit = false, @@ -433,7 +424,6 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .vht160_mcs_rx_highest = 780, .vht160_mcs_tx_highest = 780, .n_cipher_suites = 11, - .num_peers = TARGET_TLV_NUM_PEERS, .ast_skid_limit = 0x10, .num_wds_entries = 0x20, .target_64bit = false, @@ -468,7 +458,6 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .vht160_mcs_rx_highest = 0, .vht160_mcs_tx_highest = 0, .n_cipher_suites = 8, - .num_peers = TARGET_TLV_NUM_PEERS, .ast_skid_limit = 0x10, .num_wds_entries = 0x20, .target_64bit = false, @@ -505,7 +494,6 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .vht160_mcs_rx_highest = 0, .vht160_mcs_tx_highest = 0, .n_cipher_suites = 8, - .num_peers = TARGET_TLV_NUM_PEERS, .ast_skid_limit = 0x10, .num_wds_entries = 0x20, .target_64bit = false, @@ -547,7 +535,6 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .vht160_mcs_rx_highest = 0, .vht160_mcs_tx_highest = 0, .n_cipher_suites = 11, - .num_peers = TARGET_TLV_NUM_PEERS, .ast_skid_limit = 0x10, .num_wds_entries = 0x20, .target_64bit = false, From f279294e9ee22a8f306fdc8e4181cf555e6f0f70 Mon Sep 17 00:00:00 2001 From: Sriram R Date: Mon, 10 Sep 2018 11:09:40 +0530 Subject: [PATCH 36/51] ath10k: add support for configuring management packet rate By default the firmware uses 1Mbps and 6Mbps rate for management packets in 2G and 5G bands respectively. But when the user selects different basic rates from the userspace, we need to send the management packets at the lowest basic rate selected by the user. This change makes use of WMI_VDEV_PARAM_MGMT_RATE param for configuring the management packets rate to the firmware. Chipsets Tested : QCA988X, QCA9887, QCA9984 FW Tested : 10.2.4-1.0-41, 10.4-3.6.104 Signed-off-by: Sriram R Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 45 +++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 2cb196ce307b..0d4cdc6c74cb 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -157,6 +157,22 @@ u8 ath10k_mac_bitrate_to_idx(const struct ieee80211_supported_band *sband, return 0; } +static int ath10k_mac_get_rate_hw_value(int bitrate) +{ + int i; + u8 hw_value_prefix = 0; + + if (ath10k_mac_bitrate_is_cck(bitrate)) + hw_value_prefix = WMI_RATE_PREAMBLE_CCK << 6; + + for (i = 0; i < sizeof(ath10k_rates); i++) { + if (ath10k_rates[i].bitrate == bitrate) + return hw_value_prefix | ath10k_rates[i].hw_value; + } + + return -EINVAL; +} + static int ath10k_mac_get_max_vht_mcs_map(u16 mcs_map, int nss) { switch ((mcs_map >> (2 * nss)) & 0x3) { @@ -5452,9 +5468,10 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, struct cfg80211_chan_def def; u32 vdev_param, pdev_param, slottime, preamble; u16 bitrate, hw_value; - u8 rate; - int rateidx, ret = 0; + u8 rate, basic_rate_idx; + int rateidx, ret = 0, hw_rate_code; enum nl80211_band band; + const struct ieee80211_supported_band *sband; mutex_lock(&ar->conf_mutex); @@ -5660,6 +5677,30 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, arvif->vdev_id, ret); } + if (changed & BSS_CHANGED_BASIC_RATES) { + if (WARN_ON(ath10k_mac_vif_chan(vif, &def))) { + mutex_unlock(&ar->conf_mutex); + return; + } + + sband = ar->hw->wiphy->bands[def.chan->band]; + basic_rate_idx = ffs(vif->bss_conf.basic_rates) - 1; + bitrate = sband->bitrates[basic_rate_idx].bitrate; + + hw_rate_code = ath10k_mac_get_rate_hw_value(bitrate); + if (hw_rate_code < 0) { + ath10k_warn(ar, "bitrate not supported %d\n", bitrate); + mutex_unlock(&ar->conf_mutex); + return; + } + + vdev_param = ar->wmi.vdev_param->mgmt_rate; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, + hw_rate_code); + if (ret) + ath10k_warn(ar, "failed to set mgmt tx rate %d\n", ret); + } + mutex_unlock(&ar->conf_mutex); } From 4be3b05e7a83c6820095005e794432ca1711fa7f Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Thu, 13 Sep 2018 22:01:21 +0800 Subject: [PATCH 37/51] ath10k: remove duplicated includes remove duplicated include from ath10k driver. Signed-off-by: YueHaibing Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt.h | 1 - drivers/net/wireless/ath/ath10k/mac.c | 1 - drivers/net/wireless/ath/ath10k/wmi-tlv.c | 1 - 3 files changed, 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 3a7f8383871d..a76f7c9e2199 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -29,7 +29,6 @@ #include "htc.h" #include "hw.h" #include "rx_desc.h" -#include "hw.h" enum htt_dbg_stats_type { HTT_DBG_STATS_WAL_PDEV_TXRX = 1 << 0, diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 0d4cdc6c74cb..79b3da25a1a1 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -30,7 +30,6 @@ #include "htt.h" #include "txrx.h" #include "testmode.h" -#include "wmi.h" #include "wmi-tlv.h" #include "wmi-ops.h" #include "wow.h" diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index 0f78a0d42ebc..731ceaed4d5a 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -19,7 +19,6 @@ #include "debug.h" #include "mac.h" #include "hw.h" -#include "mac.h" #include "wmi.h" #include "wmi-ops.h" #include "wmi-tlv.h" From d70c0d463f9dd67037426bad243f647efec527d3 Mon Sep 17 00:00:00 2001 From: Maharaja Kennadyrajan Date: Tue, 18 Sep 2018 12:04:27 +0530 Subject: [PATCH 38/51] ath10k: add debugfs support to get power save state change of STA This patch helps to get the power save state change of each peer connected to the AP. With WMI_10_4_PEER_STA_PS_STATECHG_EVENTID event, ps state of each peer is reported to user space via debugfs. Use the below command to get the ps state of each sta: cat /sys/kernel/debug/ieee80211/phyX/netdev::wlanX/stations/ XX:XX:XX:XX:XX:XX/peer_ps_state If STA is in power save state, we get the peer_ps_state value as 1. if STA is not in power save state, we get the peer_ps_state value as 0. If ps_state event is disabled, we get the peer_ps_state value as 2. We can enable/disable the ps_state events using the debugfs flag "ps_state_enable" echo Y > /sys/kernel/debug/ieee80211/phyX/ath10k/ps_state_enable Y = 1 to enable and Y = 0 to disable Tested in QCA4019 with firmware ver 10.4-3.2.1.1-00011 Signed-off-by: Maharaja Kennadyrajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 4 + drivers/net/wireless/ath/ath10k/debug.c | 82 +++++++++++++++++++ drivers/net/wireless/ath/ath10k/debugfs_sta.c | 29 +++++++ drivers/net/wireless/ath/ath10k/mac.c | 1 + drivers/net/wireless/ath/ath10k/wmi.c | 39 ++++++++- drivers/net/wireless/ath/ath10k/wmi.h | 7 ++ 6 files changed, 161 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 7328df1bf39f..c76af343db3d 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -505,6 +505,8 @@ struct ath10k_sta { /* Protected with ar->data_lock */ struct ath10k_sta_tid_stats tid_stats[IEEE80211_NUM_TIDS + 1]; #endif + /* Protected with ar->data_lock */ + u32 peer_ps_state; }; #define ATH10K_VDEV_SETUP_TIMEOUT_HZ (5 * HZ) @@ -946,6 +948,8 @@ struct ath10k { u32 low_5ghz_chan; u32 high_5ghz_chan; bool ani_enabled; + /* protected by conf_mutex */ + u8 ps_state_enable; bool p2p; diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index ada29a4cc9c8..2c0cb6757fc6 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -2398,6 +2398,85 @@ static const struct file_operations fops_warm_hw_reset = { .llseek = default_llseek, }; +static void ath10k_peer_ps_state_disable(void *data, + struct ieee80211_sta *sta) +{ + struct ath10k *ar = data; + struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv; + + spin_lock_bh(&ar->data_lock); + arsta->peer_ps_state = WMI_PEER_PS_STATE_DISABLED; + spin_unlock_bh(&ar->data_lock); +} + +static ssize_t ath10k_write_ps_state_enable(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + int ret; + u32 param; + u8 ps_state_enable; + + if (kstrtou8_from_user(user_buf, count, 0, &ps_state_enable)) + return -EINVAL; + + if (ps_state_enable > 1 || ps_state_enable < 0) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + + if (ar->ps_state_enable == ps_state_enable) { + ret = count; + goto exit; + } + + param = ar->wmi.pdev_param->peer_sta_ps_statechg_enable; + ret = ath10k_wmi_pdev_set_param(ar, param, ps_state_enable); + if (ret) { + ath10k_warn(ar, "failed to enable ps_state_enable: %d\n", + ret); + goto exit; + } + ar->ps_state_enable = ps_state_enable; + + if (!ar->ps_state_enable) + ieee80211_iterate_stations_atomic(ar->hw, + ath10k_peer_ps_state_disable, + ar); + + ret = count; + +exit: + mutex_unlock(&ar->conf_mutex); + + return ret; +} + +static ssize_t ath10k_read_ps_state_enable(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + int len = 0; + char buf[32]; + + mutex_lock(&ar->conf_mutex); + len = scnprintf(buf, sizeof(buf) - len, "%d\n", + ar->ps_state_enable); + mutex_unlock(&ar->conf_mutex); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_ps_state_enable = { + .read = ath10k_read_ps_state_enable, + .write = ath10k_write_ps_state_enable, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + int ath10k_debug_create(struct ath10k *ar) { ar->debug.cal_data = vzalloc(ATH10K_DEBUG_CAL_DATA_LEN); @@ -2534,6 +2613,9 @@ int ath10k_debug_register(struct ath10k *ar) debugfs_create_file("warm_hw_reset", 0600, ar->debug.debugfs_phy, ar, &fops_warm_hw_reset); + debugfs_create_file("ps_state_enable", 0600, ar->debug.debugfs_phy, ar, + &fops_ps_state_enable); + return 0; } diff --git a/drivers/net/wireless/ath/ath10k/debugfs_sta.c b/drivers/net/wireless/ath/ath10k/debugfs_sta.c index 929f2f66fdce..b09cdc699c69 100644 --- a/drivers/net/wireless/ath/ath10k/debugfs_sta.c +++ b/drivers/net/wireless/ath/ath10k/debugfs_sta.c @@ -460,6 +460,33 @@ static const struct file_operations fops_peer_debug_trigger = { .llseek = default_llseek, }; +static ssize_t ath10k_dbg_sta_read_peer_ps_state(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_sta *sta = file->private_data; + struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv; + struct ath10k *ar = arsta->arvif->ar; + char buf[20]; + int len = 0; + + spin_lock_bh(&ar->data_lock); + + len = scnprintf(buf, sizeof(buf) - len, "%d\n", + arsta->peer_ps_state); + + spin_unlock_bh(&ar->data_lock); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_peer_ps_state = { + .open = simple_open, + .read = ath10k_dbg_sta_read_peer_ps_state, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + static char *get_err_str(enum ath10k_pkt_rx_err i) { switch (i) { @@ -738,4 +765,6 @@ void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, ath10k_debug_is_extd_tx_stats_enabled(ar)) debugfs_create_file("tx_stats", 0400, dir, sta, &fops_tx_stats); + debugfs_create_file("peer_ps_state", 0400, dir, sta, + &fops_peer_ps_state); } diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 79b3da25a1a1..dc4e3cc2e882 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -6256,6 +6256,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, new_state == IEEE80211_STA_NONE) { memset(arsta, 0, sizeof(*arsta)); arsta->arvif = arvif; + arsta->peer_ps_state = WMI_PEER_PS_STATE_DISABLED; INIT_WORK(&arsta->update_wk, ath10k_sta_rc_update_wk); for (i = 0; i < ARRAY_SIZE(sta->txq); i++) diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index c1a46af5f13f..25e8fa789e8d 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -1307,7 +1307,8 @@ static struct wmi_pdev_param_map wmi_10_2_4_pdev_param_map = { .set_mcast2ucast_mode = WMI_PDEV_PARAM_UNSUPPORTED, .set_mcast2ucast_buffer = WMI_PDEV_PARAM_UNSUPPORTED, .remove_mcast2ucast_buffer = WMI_PDEV_PARAM_UNSUPPORTED, - .peer_sta_ps_statechg_enable = WMI_PDEV_PARAM_UNSUPPORTED, + .peer_sta_ps_statechg_enable = + WMI_10X_PDEV_PARAM_PEER_STA_PS_STATECHG_ENABLE, .igmpmld_ac_override = WMI_PDEV_PARAM_UNSUPPORTED, .block_interbss = WMI_PDEV_PARAM_UNSUPPORTED, .set_disable_reset_cmdid = WMI_PDEV_PARAM_UNSUPPORTED, @@ -5048,6 +5049,36 @@ ath10k_wmi_handle_tdls_peer_event(struct ath10k *ar, struct sk_buff *skb) } } +static void +ath10k_wmi_event_peer_sta_ps_state_chg(struct ath10k *ar, struct sk_buff *skb) +{ + struct wmi_peer_sta_ps_state_chg_event *ev; + struct ieee80211_sta *sta; + struct ath10k_sta *arsta; + u8 peer_addr[ETH_ALEN]; + + lockdep_assert_held(&ar->data_lock); + + ev = (struct wmi_peer_sta_ps_state_chg_event *)skb->data; + ether_addr_copy(peer_addr, ev->peer_macaddr.addr); + + rcu_read_lock(); + + sta = ieee80211_find_sta_by_ifaddr(ar->hw, peer_addr, NULL); + + if (!sta) { + ath10k_warn(ar, "failed to find station entry %pM\n", + peer_addr); + goto exit; + } + + arsta = (struct ath10k_sta *)sta->drv_priv; + arsta->peer_ps_state = __le32_to_cpu(ev->peer_ps_state); + +exit: + rcu_read_unlock(); +} + void ath10k_wmi_event_pdev_ftm_intg(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PDEV_FTM_INTG_EVENTID\n"); @@ -5978,6 +6009,9 @@ static void ath10k_wmi_10_2_op_rx(struct ath10k *ar, struct sk_buff *skb) ath10k_dbg(ar, ATH10K_DBG_WMI, "received event id %d not implemented\n", id); break; + case WMI_10_2_PEER_STA_PS_STATECHG_EVENTID: + ath10k_wmi_event_peer_sta_ps_state_chg(ar, skb); + break; default: ath10k_warn(ar, "Unknown eventid: %d\n", id); break; @@ -6095,6 +6129,9 @@ static void ath10k_wmi_10_4_op_rx(struct ath10k *ar, struct sk_buff *skb) case WMI_10_4_DFS_STATUS_CHECK_EVENTID: ath10k_wmi_event_dfs_status_check(ar, skb); break; + case WMI_10_4_PEER_STA_PS_STATECHG_EVENTID: + ath10k_wmi_event_peer_sta_ps_state_chg(ar, skb); + break; default: ath10k_warn(ar, "Unknown eventid: %d\n", id); break; diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 3c09b0e6bd9b..21d1e93632ec 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -4161,6 +4161,13 @@ enum wmi_tpc_pream_5ghz { WMI_TPC_PREAM_5GHZ_HTCUP, }; +#define WMI_PEER_PS_STATE_DISABLED 2 + +struct wmi_peer_sta_ps_state_chg_event { + struct wmi_mac_addr peer_macaddr; + __le32 peer_ps_state; +} __packed; + struct wmi_pdev_chanlist_update_event { /* number of channels */ __le32 num_chan; From 4600563f9319006782bdacef0dfff2f493202eb0 Mon Sep 17 00:00:00 2001 From: Maharaja Kennadyrajan Date: Tue, 18 Sep 2018 17:37:26 +0530 Subject: [PATCH 39/51] ath10k: advertize beacon_int_min_gcd as 100 while bring up multi vaps With the latest firmware design, the beacon interval should be greater than 100 to bring the multiple vaps. Set beacon_int_min_gcd to 100, when the wmi service WMI_SERVICE_VDEV_DIFFERENT_BEACON_INTERVAL_SUPPORT is enabled in the firmware. If not, beacon_int_min_gcd will be set to the default value 1. Tested in QCA4019 with firmware ver 10.4-3.2.1.1-00015 Tested in QCA9888 with firmware ver 10.4-3.5.1-0005 Signed-off-by: Maharaja Kennadyrajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 25 +++++++++++++++++++++++++ drivers/net/wireless/ath/ath10k/wmi.h | 9 +++++++++ 2 files changed, 34 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index dc4e3cc2e882..3933dd96da55 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -8204,6 +8204,24 @@ static const struct ieee80211_iface_combination ath10k_10_4_if_comb[] = { }, }; +static const struct +ieee80211_iface_combination ath10k_10_4_bcn_int_if_comb[] = { + { + .limits = ath10k_10_4_if_limits, + .n_limits = ARRAY_SIZE(ath10k_10_4_if_limits), + .max_interfaces = 16, + .num_different_channels = 1, + .beacon_int_infra_match = true, + .beacon_int_min_gcd = 100, +#ifdef CONFIG_ATH10K_DFS_CERTIFIED + .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80), +#endif + }, +}; + static void ath10k_get_arvif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { @@ -8571,6 +8589,13 @@ int ath10k_mac_register(struct ath10k *ar) ar->hw->wiphy->iface_combinations = ath10k_10_4_if_comb; ar->hw->wiphy->n_iface_combinations = ARRAY_SIZE(ath10k_10_4_if_comb); + if (test_bit(WMI_SERVICE_VDEV_DIFFERENT_BEACON_INTERVAL_SUPPORT, + ar->wmi.svc_map)) { + ar->hw->wiphy->iface_combinations = + ath10k_10_4_bcn_int_if_comb; + ar->hw->wiphy->n_iface_combinations = + ARRAY_SIZE(ath10k_10_4_bcn_int_if_comb); + } break; case ATH10K_FW_WMI_OP_VERSION_UNSET: case ATH10K_FW_WMI_OP_VERSION_MAX: diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 21d1e93632ec..f67c52757ea6 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -204,6 +204,7 @@ enum wmi_service { WMI_SERVICE_RESET_CHIP, WMI_SERVICE_SPOOF_MAC_SUPPORT, WMI_SERVICE_TX_DATA_ACK_RSSI, + WMI_SERVICE_VDEV_DIFFERENT_BEACON_INTERVAL_SUPPORT, /* keep last */ WMI_SERVICE_MAX, @@ -353,6 +354,11 @@ enum wmi_10_4_service { WMI_10_4_SERVICE_TPC_STATS_FINAL, WMI_10_4_SERVICE_CFR_CAPTURE_SUPPORT, WMI_10_4_SERVICE_TX_DATA_ACK_RSSI, + WMI_10_4_SERVICE_CFR_CAPTURE_IND_MSG_TYPE_LEGACY, + WMI_10_4_SERVICE_PER_PACKET_SW_ENCRYPT, + WMI_10_4_SERVICE_PEER_TID_CONFIGS_SUPPORT, + WMI_10_4_SERVICE_VDEV_BCN_RATE_CONTROL, + WMI_10_4_SERVICE_VDEV_DIFFERENT_BEACON_INTERVAL_SUPPORT, }; static inline char *wmi_service_name(int service_id) @@ -467,6 +473,7 @@ static inline char *wmi_service_name(int service_id) SVCSTR(WMI_SERVICE_TPC_STATS_FINAL); SVCSTR(WMI_SERVICE_RESET_CHIP); SVCSTR(WMI_SERVICE_TX_DATA_ACK_RSSI); + SVCSTR(WMI_SERVICE_VDEV_DIFFERENT_BEACON_INTERVAL_SUPPORT); default: return NULL; } @@ -777,6 +784,8 @@ static inline void wmi_10_4_svc_map(const __le32 *in, unsigned long *out, WMI_SERVICE_TPC_STATS_FINAL, len); SVCMAP(WMI_10_4_SERVICE_TX_DATA_ACK_RSSI, WMI_SERVICE_TX_DATA_ACK_RSSI, len); + SVCMAP(WMI_10_4_SERVICE_VDEV_DIFFERENT_BEACON_INTERVAL_SUPPORT, + WMI_SERVICE_VDEV_DIFFERENT_BEACON_INTERVAL_SUPPORT, len); } #undef SVCMAP From 752ed2a229c62f9301b9ab91d04048402fab6ff8 Mon Sep 17 00:00:00 2001 From: Govind Singh Date: Thu, 20 Sep 2018 10:34:25 +0530 Subject: [PATCH 40/51] ath10k: move napi_enable to hif_start for consistent pairing There is unbalanced napi_{enable,disable}() behavior as they are being called from hif_snoc_{power_up/stop). The fix is to call napi_enable() from ath10k_snoc_hif_start() so that it matches with napi_disable() being called from ath10k_snoc_hif_stop(). Signed-off-by: Govind Singh Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/snoc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index b83edb9144c3..34703101f569 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -739,6 +739,7 @@ static void ath10k_snoc_hif_stop(struct ath10k *ar) static int ath10k_snoc_hif_start(struct ath10k *ar) { + napi_enable(&ar->napi); ath10k_snoc_irq_enable(ar); ath10k_snoc_rx_post(ar); @@ -801,7 +802,6 @@ static int ath10k_snoc_hif_power_up(struct ath10k *ar) goto err_wlan_enable; } - napi_enable(&ar->napi); return 0; err_wlan_enable: From 393b9b0f830efc21c26a4ef9a9ce4d517eb98463 Mon Sep 17 00:00:00 2001 From: Govind Singh Date: Thu, 20 Sep 2018 10:34:26 +0530 Subject: [PATCH 41/51] ath10k: disable napi before resource cleanup to avoid "use after free" CE buffers are cleaned up prior to napi disable and this is causing NULL pointer dereference due to "use after free". Disable napi before resource cleanup to avoid "use after free". Signed-off-by: Govind Singh Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/snoc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index 34703101f569..f7b5b855aab2 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -731,9 +731,9 @@ static void ath10k_snoc_buffer_cleanup(struct ath10k *ar) static void ath10k_snoc_hif_stop(struct ath10k *ar) { ath10k_snoc_irq_disable(ar); - ath10k_snoc_buffer_cleanup(ar); napi_synchronize(&ar->napi); napi_disable(&ar->napi); + ath10k_snoc_buffer_cleanup(ar); ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif stop\n"); } From 3fa8d2186550e5c8ad209087dc856a5f35fd4d26 Mon Sep 17 00:00:00 2001 From: Louie Lu Date: Thu, 6 Sep 2018 11:53:26 +0800 Subject: [PATCH 42/51] ath9k: add reset for airtime station debugfs Let user can reset station airtime status by debugfs, it will reset all airtime deficit to ATH_AIRTIME_QUANTUM and reset rx/tx airtime accumulate to 0. Signed-off-by: Louie Lu Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/debug_sta.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/debug_sta.c b/drivers/net/wireless/ath/ath9k/debug_sta.c index ed8b77a74630..e8fcd3e1c470 100644 --- a/drivers/net/wireless/ath/ath9k/debug_sta.c +++ b/drivers/net/wireless/ath/ath9k/debug_sta.c @@ -286,9 +286,25 @@ static ssize_t read_airtime(struct file *file, char __user *user_buf, return retval; } +static ssize_t +write_airtime_reset_stub(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath_node *an = file->private_data; + struct ath_airtime_stats *astats; + int i; + + astats = &an->airtime_stats; + astats->rx_airtime = 0; + astats->tx_airtime = 0; + for (i = 0; i < 4; i++) + an->airtime_deficit[i] = ATH_AIRTIME_QUANTUM; + return count; +} static const struct file_operations fops_airtime = { .read = read_airtime, + .write = write_airtime_reset_stub, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, @@ -304,5 +320,5 @@ void ath9k_sta_add_debugfs(struct ieee80211_hw *hw, debugfs_create_file("node_aggr", 0444, dir, an, &fops_node_aggr); debugfs_create_file("node_recv", 0444, dir, an, &fops_node_recv); - debugfs_create_file("airtime", 0444, dir, an, &fops_airtime); + debugfs_create_file("airtime", 0644, dir, an, &fops_airtime); } From b8f3d16351034a7d3c25accc58d64ded9e1a03f9 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Mon, 10 Sep 2018 11:11:28 +0800 Subject: [PATCH 43/51] ath9k: debug: remove set but not used variable 'fops_dump_nfcal' 'fops_dump_nfcal' is not used since commit 4447d815fd0f ("ath: ath9k: use debugfs_create_devm_seqfile() helper for seq_file entries") Signed-off-by: YueHaibing Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/debug.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 0a6eb8a8c1ed..c871b7ec5011 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -990,19 +990,6 @@ static int read_file_dump_nfcal(struct seq_file *file, void *data) return 0; } -static int open_file_dump_nfcal(struct inode *inode, struct file *f) -{ - return single_open(f, read_file_dump_nfcal, inode->i_private); -} - -static const struct file_operations fops_dump_nfcal = { - .read = seq_read, - .open = open_file_dump_nfcal, - .owner = THIS_MODULE, - .llseek = seq_lseek, - .release = single_release, -}; - #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT static ssize_t read_file_btcoex(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) From ec95e84c39554737404047c36b3c76855a831a3e Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Tue, 11 Sep 2018 12:32:19 +0000 Subject: [PATCH 44/51] wil6210: remove set but not used variable 'start' Fixes gcc '-Wunused-but-set-variable' warning: drivers/net/wireless/ath/wil6210/pm.c: In function 'wil_suspend_keep_radio_on': drivers/net/wireless/ath/wil6210/pm.c:193:16: warning: variable 'start' set but not used [-Wunused-but-set-variable] Signed-off-by: YueHaibing Reviewed-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/pm.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/pm.c b/drivers/net/wireless/ath/wil6210/pm.c index 3a4194779ddf..75fe9323547c 100644 --- a/drivers/net/wireless/ath/wil6210/pm.c +++ b/drivers/net/wireless/ath/wil6210/pm.c @@ -190,7 +190,7 @@ static int wil_resume_keep_radio_on(struct wil6210_priv *wil) static int wil_suspend_keep_radio_on(struct wil6210_priv *wil) { int rc = 0; - unsigned long start, data_comp_to; + unsigned long data_comp_to; wil_dbg_pm(wil, "suspend keep radio on\n"); @@ -232,7 +232,6 @@ static int wil_suspend_keep_radio_on(struct wil6210_priv *wil) } /* Wait for completion of the pending RX packets */ - start = jiffies; data_comp_to = jiffies + msecs_to_jiffies(WIL_DATA_COMPLETION_TO_MS); if (test_bit(wil_status_napi_en, wil->status)) { while (!wil->txrx_ops.is_rx_idle(wil)) { From 03224678c0134bd5811b81919a4d39a8fbe03244 Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Mon, 1 Oct 2018 17:26:42 +0300 Subject: [PATCH 45/51] ath9k: add counters for good and errorneous FFT/spectral frames This is helpful to see whether spectral samples get discarded. Signed-off-by: Simon Wunderlich Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/common-debug.c | 2 ++ drivers/net/wireless/ath/ath9k/common-debug.h | 4 ++++ drivers/net/wireless/ath/ath9k/common-spectral.c | 15 +++++++++++++-- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/common-debug.c b/drivers/net/wireless/ath/ath9k/common-debug.c index 239429f10378..53ca4b063eb9 100644 --- a/drivers/net/wireless/ath/ath9k/common-debug.c +++ b/drivers/net/wireless/ath/ath9k/common-debug.c @@ -144,6 +144,8 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf, RXS_ERR("BEACONS", rx_beacons); RXS_ERR("FRAGS", rx_frags); RXS_ERR("SPECTRAL", rx_spectral); + RXS_ERR("SPECTRAL SMPL GOOD", rx_spectral_sample_good); + RXS_ERR("SPECTRAL SMPL ERR", rx_spectral_sample_err); RXS_ERR("CRC ERR", crc_err); RXS_ERR("DECRYPT CRC ERR", decrypt_crc_err); diff --git a/drivers/net/wireless/ath/ath9k/common-debug.h b/drivers/net/wireless/ath/ath9k/common-debug.h index 3376990d3a24..2938b5b96b07 100644 --- a/drivers/net/wireless/ath/ath9k/common-debug.h +++ b/drivers/net/wireless/ath/ath9k/common-debug.h @@ -39,6 +39,8 @@ * @rx_beacons: No. of beacons received. * @rx_frags: No. of rx-fragements received. * @rx_spectral: No of spectral packets received. + * @rx_spectral_sample_good: No. of good spectral samples + * @rx_spectral_sample_err: No. of good spectral samples */ struct ath_rx_stats { u32 rx_pkts_all; @@ -58,6 +60,8 @@ struct ath_rx_stats { u32 rx_beacons; u32 rx_frags; u32 rx_spectral; + u32 rx_spectral_sample_good; + u32 rx_spectral_sample_err; }; #ifdef CONFIG_ATH9K_COMMON_DEBUG diff --git a/drivers/net/wireless/ath/ath9k/common-spectral.c b/drivers/net/wireless/ath/ath9k/common-spectral.c index 440e16e641e4..0c5559009a28 100644 --- a/drivers/net/wireless/ath/ath9k/common-spectral.c +++ b/drivers/net/wireless/ath/ath9k/common-spectral.c @@ -501,6 +501,7 @@ int ath_cmn_process_fft(struct ath_spec_scan_priv *spec_priv, struct ieee80211_h u8 sample_buf[SPECTRAL_SAMPLE_MAX_LEN] = {0}; struct ath_hw *ah = spec_priv->ah; struct ath_common *common = ath9k_hw_common(spec_priv->ah); + struct ath_softc *sc = (struct ath_softc *)common->priv; u8 num_bins, *vdata = (u8 *)hdr; struct ath_radar_info *radar_info; int len = rs->rs_datalen; @@ -649,8 +650,13 @@ int ath_cmn_process_fft(struct ath_spec_scan_priv *spec_priv, struct ieee80211_h sample_buf, sample_len, sample_bytes); - fft_handler(rs, spec_priv, sample_buf, - tsf, freq, chan_type); + ret = fft_handler(rs, spec_priv, sample_buf, + tsf, freq, chan_type); + + if (ret == 0) + RX_STAT_INC(rx_spectral_sample_good); + else + RX_STAT_INC(rx_spectral_sample_err); memset(sample_buf, 0, SPECTRAL_SAMPLE_MAX_LEN); @@ -665,6 +671,11 @@ int ath_cmn_process_fft(struct ath_spec_scan_priv *spec_priv, struct ieee80211_h ret = fft_handler(rs, spec_priv, sample_start, tsf, freq, chan_type); + if (ret == 0) + RX_STAT_INC(rx_spectral_sample_good); + else + RX_STAT_INC(rx_spectral_sample_err); + /* Mix the received bins to the /dev/random * pool */ From b796a6c04e65e35033ad4cb390500ff03ef72350 Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Mon, 1 Oct 2018 17:26:46 +0300 Subject: [PATCH 46/51] ath9k: return when short FFT frame was handled With the loop break like this, there are false "FFT report truncated" messages because the iterator is not advanced as the check expects. Instead, just return, for a single frame there is nothing left to be done anyways. Cc: Nick Kossifidis Signed-off-by: Simon Wunderlich Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/common-spectral.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/common-spectral.c b/drivers/net/wireless/ath/ath9k/common-spectral.c index 0c5559009a28..f6dd0ecfbbf3 100644 --- a/drivers/net/wireless/ath/ath9k/common-spectral.c +++ b/drivers/net/wireless/ath/ath9k/common-spectral.c @@ -686,7 +686,7 @@ int ath_cmn_process_fft(struct ath_spec_scan_priv *spec_priv, struct ieee80211_h * loop. */ if (len <= fft_len + 2) - break; + return 1; sample_start = &vdata[i + 1]; From 2f85786b8a570fbfbc9f52a42723e5d8290ab12f Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Mon, 1 Oct 2018 17:26:51 +0300 Subject: [PATCH 47/51] ath9k: fix and simplify FFT max index retrieval FFT max index retrieval was not retrieved correctly for HT20/HT40 FFT frames. Fixing the retrieval allows us to remove the fixup function as well. While at it, split the spectral_max_index function into versions for ht20 and ht40 to simplify the code. Cc: Nick Kossifidis Signed-off-by: Simon Wunderlich Signed-off-by: Kalle Valo --- .../net/wireless/ath/ath9k/common-spectral.c | 45 +++---------------- .../net/wireless/ath/ath9k/common-spectral.h | 17 +++++++ 2 files changed, 23 insertions(+), 39 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/common-spectral.c b/drivers/net/wireless/ath/ath9k/common-spectral.c index f6dd0ecfbbf3..d10e3f29c356 100644 --- a/drivers/net/wireless/ath/ath9k/common-spectral.c +++ b/drivers/net/wireless/ath/ath9k/common-spectral.c @@ -59,8 +59,7 @@ ath_cmn_max_idx_verify_ht20_fft(u8 *sample_end, int bytes_read) sample = sample_end - SPECTRAL_HT20_SAMPLE_LEN + 1; - max_index = spectral_max_index(mag_info->all_bins, - SPECTRAL_HT20_NUM_BINS); + max_index = spectral_max_index_ht20(mag_info->all_bins); max_magnitude = spectral_max_magnitude(mag_info->all_bins); max_exp = mag_info->max_exp & 0xf; @@ -100,12 +99,10 @@ ath_cmn_max_idx_verify_ht20_40_fft(u8 *sample_end, int bytes_read) sample = sample_end - SPECTRAL_HT20_40_SAMPLE_LEN + 1; lower_mag = spectral_max_magnitude(mag_info->lower_bins); - lower_max_index = spectral_max_index(mag_info->lower_bins, - SPECTRAL_HT20_40_NUM_BINS); + lower_max_index = spectral_max_index_ht40(mag_info->lower_bins); upper_mag = spectral_max_magnitude(mag_info->upper_bins); - upper_max_index = spectral_max_index(mag_info->upper_bins, - SPECTRAL_HT20_40_NUM_BINS); + upper_max_index = spectral_max_index_ht40(mag_info->upper_bins); max_exp = mag_info->max_exp & 0xf; @@ -117,17 +114,6 @@ ath_cmn_max_idx_verify_ht20_40_fft(u8 *sample_end, int bytes_read) ((upper_max_index < 1) || (lower_max_index < 1))) return -1; - /* Some time hardware messes up the index and adds - * the index of the middle point (dc_pos). Try to fix it. - */ - if ((upper_max_index - dc_pos > 0) && - (sample[upper_max_index] == (upper_mag >> max_exp))) - upper_max_index -= dc_pos; - - if ((lower_max_index - dc_pos > 0) && - (sample[lower_max_index - dc_pos] == (lower_mag >> max_exp))) - lower_max_index -= dc_pos; - if ((sample[upper_max_index + dc_pos] != (upper_mag >> max_exp)) || (sample[lower_max_index] != (lower_mag >> max_exp))) return -1; @@ -169,8 +155,7 @@ ath_cmn_process_ht20_fft(struct ath_rx_status *rs, magnitude = spectral_max_magnitude(mag_info->all_bins); fft_sample_20.max_magnitude = __cpu_to_be16(magnitude); - max_index = spectral_max_index(mag_info->all_bins, - SPECTRAL_HT20_NUM_BINS); + max_index = spectral_max_index_ht20(mag_info->all_bins); fft_sample_20.max_index = max_index; bitmap_w = spectral_bitmap_weight(mag_info->all_bins); @@ -302,12 +287,10 @@ ath_cmn_process_ht20_40_fft(struct ath_rx_status *rs, upper_mag = spectral_max_magnitude(mag_info->upper_bins); fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag); - lower_max_index = spectral_max_index(mag_info->lower_bins, - SPECTRAL_HT20_40_NUM_BINS); + lower_max_index = spectral_max_index_ht40(mag_info->lower_bins); fft_sample_40.lower_max_index = lower_max_index; - upper_max_index = spectral_max_index(mag_info->upper_bins, - SPECTRAL_HT20_40_NUM_BINS); + upper_max_index = spectral_max_index_ht40(mag_info->upper_bins); fft_sample_40.upper_max_index = upper_max_index; lower_bitmap_w = spectral_bitmap_weight(mag_info->lower_bins); @@ -331,22 +314,6 @@ ath_cmn_process_ht20_40_fft(struct ath_rx_status *rs, upper_mag >> max_exp, upper_max_index); - /* Some time hardware messes up the index and adds - * the index of the middle point (dc_pos). Try to fix it. - */ - if ((upper_max_index - dc_pos > 0) && - (fft_sample_40.data[upper_max_index] == (upper_mag >> max_exp))) { - upper_max_index -= dc_pos; - fft_sample_40.upper_max_index = upper_max_index; - } - - if ((lower_max_index - dc_pos > 0) && - (fft_sample_40.data[lower_max_index - dc_pos] == - (lower_mag >> max_exp))) { - lower_max_index -= dc_pos; - fft_sample_40.lower_max_index = lower_max_index; - } - /* Check if we got the expected magnitude values at * the expected bins */ diff --git a/drivers/net/wireless/ath/ath9k/common-spectral.h b/drivers/net/wireless/ath/ath9k/common-spectral.h index 303ab470ce34..011d8ab8b974 100644 --- a/drivers/net/wireless/ath/ath9k/common-spectral.h +++ b/drivers/net/wireless/ath/ath9k/common-spectral.h @@ -145,6 +145,23 @@ static inline u8 spectral_max_index(u8 *bins, int num_bins) return m; } +static inline u8 spectral_max_index_ht40(u8 *bins) +{ + u8 idx; + + idx = spectral_max_index(bins, SPECTRAL_HT20_40_NUM_BINS); + + /* positive values and zero are starting at the beginning + * of the data field. + */ + return idx % (SPECTRAL_HT20_40_NUM_BINS / 2); +} + +static inline u8 spectral_max_index_ht20(u8 *bins) +{ + return spectral_max_index(bins, SPECTRAL_HT20_NUM_BINS); +} + /* return the bitmap weight from the all/upper/lower bins */ static inline u8 spectral_bitmap_weight(u8 *bins) { From 4e7a3fa5394e6a9e3865ef1cedaec5c0ca368dc7 Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Mon, 1 Oct 2018 17:26:56 +0300 Subject: [PATCH 48/51] ath9k: FFT magnitude check: don't consider lower 3 data bits There were a lot of Magnitude Mismatch while getting FFT samples on my hardware (Atheros AR9462. I've compared the reported magnitude with the data in the FFT bin, and the FFT bin was less accurate: [ 5395.193030] ath: phy0: FFT HT20 frame: max mag 0x89,max_mag_idx 28, ,magnitude 0x89 max_exp 0, data[28] = 0x88 [ 5395.194525] ath: phy0: FFT HT20 frame: max mag 0x89,max_mag_idx 28, ,magnitude 0x89 max_exp 0, data[28] = 0x88 [ 5395.196012] ath: phy0: FFT HT20 frame: max mag 0x88,max_mag_idx 28, ,magnitude 0x88 max_exp 0, data[28] = 0x88 [ 5395.197509] ath: phy0: FFT HT20 frame: max mag 0x6C,max_mag_idx 28, ,magnitude 0x6C max_exp 0, data[28] = 0x68 [ 5395.199015] ath: phy0: FFT HT20 frame: max mag 0x78,max_mag_idx 28, ,magnitude 0x78 max_exp 0, data[28] = 0x78 [ 5395.200497] ath: phy0: FFT HT20 frame: max mag 0xA1,max_mag_idx 28, ,magnitude 0xA1 max_exp 0, data[28] = 0xA0 [ 5395.202011] ath: phy0: FFT HT20 frame: max mag 0x91,max_mag_idx 28, ,magnitude 0x91 max_exp 0, data[28] = 0x90 [ 5395.203482] ath: phy0: FFT HT20 frame: max mag 0x89,max_mag_idx 28, ,magnitude 0x89 max_exp 0, data[28] = 0x88 [ 5395.204999] ath: phy0: FFT HT20 frame: max mag 0x27,max_mag_idx 4, ,magnitude 0x27 max_exp 0, data[4] = 0x20 [ 5395.206461] ath: phy0: FFT HT20 frame: max mag 0x41,max_mag_idx 28, ,magnitude 0x41 max_exp 0, data[28] = 0x40 [ 5395.207977] ath: phy0: FFT HT20 frame: max mag 0x51,max_mag_idx 28, ,magnitude 0x51 max_exp 0, data[28] = 0x50 [ 5395.209454] ath: phy0: FFT HT20 frame: max mag 0x53,max_mag_idx 28, ,magnitude 0x53 max_exp 0, data[28] = 0x50 [ 5395.210940] ath: phy0: FFT HT20 frame: max mag 0x40,max_mag_idx 28, ,magnitude 0x40 max_exp 0, data[28] = 0x40 [ 5395.212441] ath: phy0: FFT HT20 frame: max mag 0x59,max_mag_idx 28, ,magnitude 0x59 max_exp 0, data[28] = 0x58 [ 5395.213932] ath: phy0: FFT HT20 frame: max mag 0x53,max_mag_idx 28, ,magnitude 0x53 max_exp 0, data[28] = 0x50 [ 5395.215428] ath: phy0: FFT HT20 frame: max mag 0x7D,max_mag_idx 28, ,magnitude 0x7D max_exp 0, data[28] = 0x78 [ 5395.216910] ath: phy0: FFT HT20 frame: max mag 0x8C,max_mag_idx 28, ,magnitude 0x8C max_exp 0, data[28] = 0x88 [ 5395.218413] ath: phy0: FFT HT20 frame: max mag 0x7B,max_mag_idx 28, ,magnitude 0x7B max_exp 0, data[28] = 0x78 [ 5395.219900] ath: phy0: FFT HT20 frame: max mag 0x43,max_mag_idx 28, ,magnitude 0x43 max_exp 0, data[28] = 0x40 It seems like the lower 3 bits on my hardware are always zeroed, but the magnitude matches otherwise. Therefore, let's not make the magnitude check so strict so we can get those samples released to userspace. Cc: Nick Kossifidis Signed-off-by: Simon Wunderlich Signed-off-by: Kalle Valo --- .../net/wireless/ath/ath9k/common-spectral.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/common-spectral.c b/drivers/net/wireless/ath/ath9k/common-spectral.c index d10e3f29c356..70ddaf6199a0 100644 --- a/drivers/net/wireless/ath/ath9k/common-spectral.c +++ b/drivers/net/wireless/ath/ath9k/common-spectral.c @@ -71,7 +71,7 @@ ath_cmn_max_idx_verify_ht20_fft(u8 *sample_end, int bytes_read) if (bytes_read < SPECTRAL_HT20_SAMPLE_LEN && max_index < 1) return -1; - if (sample[max_index] != (max_magnitude >> max_exp)) + if ((sample[max_index] & 0xf8) != ((max_magnitude >> max_exp) & 0xf8)) return -1; else return 0; @@ -114,8 +114,10 @@ ath_cmn_max_idx_verify_ht20_40_fft(u8 *sample_end, int bytes_read) ((upper_max_index < 1) || (lower_max_index < 1))) return -1; - if ((sample[upper_max_index + dc_pos] != (upper_mag >> max_exp)) || - (sample[lower_max_index] != (lower_mag >> max_exp))) + if (((sample[upper_max_index + dc_pos] & 0xf8) != + ((upper_mag >> max_exp) & 0xf8)) || + ((sample[lower_max_index] & 0xf8) != + ((lower_mag >> max_exp) & 0xf8))) return -1; else return 0; @@ -173,7 +175,8 @@ ath_cmn_process_ht20_fft(struct ath_rx_status *rs, magnitude >> max_exp, max_index); - if (fft_sample_20.data[max_index] != (magnitude >> max_exp)) { + if ((fft_sample_20.data[max_index] & 0xf8) != + ((magnitude >> max_exp) & 0xf8)) { ath_dbg(common, SPECTRAL_SCAN, "Magnitude mismatch !\n"); ret = -1; } @@ -317,10 +320,10 @@ ath_cmn_process_ht20_40_fft(struct ath_rx_status *rs, /* Check if we got the expected magnitude values at * the expected bins */ - if ((fft_sample_40.data[upper_max_index + dc_pos] - != (upper_mag >> max_exp)) || - (fft_sample_40.data[lower_max_index] - != (lower_mag >> max_exp))) { + if (((fft_sample_40.data[upper_max_index + dc_pos] & 0xf8) + != ((upper_mag >> max_exp) & 0xf8)) || + ((fft_sample_40.data[lower_max_index] & 0xf8) + != ((lower_mag >> max_exp) & 0xf8))) { ath_dbg(common, SPECTRAL_SCAN, "Magnitude mismatch !\n"); ret = -1; } From 4fb5837ac2bd46a85620b297002c704e9958f64d Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Mon, 1 Oct 2018 17:26:59 +0300 Subject: [PATCH 49/51] ath9k: fix reporting calculated new FFT upper max Since the debug print code is outside of the loop, it shouldn't use the loop iterator anymore but instead print the found maximum index. Cc: Nick Kossifidis Signed-off-by: Simon Wunderlich Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/common-spectral.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/common-spectral.c b/drivers/net/wireless/ath/ath9k/common-spectral.c index 70ddaf6199a0..6a43d26276e5 100644 --- a/drivers/net/wireless/ath/ath9k/common-spectral.c +++ b/drivers/net/wireless/ath/ath9k/common-spectral.c @@ -381,7 +381,7 @@ ath_cmn_process_ht20_40_fft(struct ath_rx_status *rs, ath_dbg(common, SPECTRAL_SCAN, "Calculated new upper max 0x%X at %i\n", - tmp_mag, i); + tmp_mag, fft_sample_40.upper_max_index); } else for (i = dc_pos; i < SPECTRAL_HT20_40_NUM_BINS; i++) { if (fft_sample_40.data[i] == (upper_mag >> max_exp)) From e720ba1dcc33377231bcbc76e927f089e393ea60 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 21 Sep 2018 13:42:41 -0700 Subject: [PATCH 50/51] ath10k: add missing sanity check on diag download ath10k_hw_diag_fast_download() generally has good boundary checking, but it misses verifying that the next metadata header actually fits in the remaining buffer space. Add such a check. Fixes: 39501ea64116 ("ath10k: download firmware via diag Copy Engine for QCA6174 and QCA9377.") Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/hw.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/hw.c b/drivers/net/wireless/ath/ath10k/hw.c index 2c2870e3e84d..af8ae8117c62 100644 --- a/drivers/net/wireless/ath/ath10k/hw.c +++ b/drivers/net/wireless/ath/ath10k/hw.c @@ -1044,6 +1044,12 @@ int ath10k_hw_diag_fast_download(struct ath10k *ar, left = length - sizeof(*hdr); while (left > 0) { + if (left < sizeof(*metadata)) { + ath10k_warn(ar, "firmware segment is truncated: %d\n", + left); + ret = -EINVAL; + break; + } base_addr = __le32_to_cpu(metadata->addr); base_len = __le32_to_cpu(metadata->length); buf = metadata->data; From 6df0580be8bc30803c4d8b2ed9c2230a2740c795 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sat, 22 Sep 2018 18:49:05 +0200 Subject: [PATCH 51/51] ath9k: add back support for using active monitor interfaces for tx99 Various documented examples on how to set up tx99 with ath9k rely on setting up a regular monitor interface for setting the channel. My previous patch "ath9k: fix tx99 with monitor mode interface" made it possible to set it up this way again. However, it was removing support for using an active monitor interface, which is required for controlling the bitrate as well, since the bitrate is not passed down with a regular monitor interface. This patch partially reverts the previous one, but keeps support for using a regular monitor interface to keep documented steps working in cases where the bitrate does not matter Fixes: d9c52fd17cb48 ("ath9k: fix tx99 with monitor mode interface") Signed-off-by: Felix Fietkau Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/ath9k.h | 1 + drivers/net/wireless/ath/ath9k/main.c | 10 ++++++++-- drivers/net/wireless/ath/ath9k/tx99.c | 7 +++++++ drivers/net/wireless/ath/ath9k/xmit.c | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 003e9fb456ac..21ba20981a80 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -1074,6 +1074,7 @@ struct ath_softc { struct ath_spec_scan_priv spec_priv; + struct ieee80211_vif *tx99_vif; struct sk_buff *tx99_skb; bool tx99_state; s16 tx99_power; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 6ce4b9f1dcb4..c85f613e8ceb 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1251,8 +1251,13 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, struct ath_vif *avp = (void *)vif->drv_priv; struct ath_node *an = &avp->mcast_node; - if (IS_ENABLED(CONFIG_ATH9K_TX99)) - return -EOPNOTSUPP; + if (IS_ENABLED(CONFIG_ATH9K_TX99)) { + if (sc->cur_chan->nvifs >= 1) { + mutex_unlock(&sc->mutex); + return -EOPNOTSUPP; + } + sc->tx99_vif = vif; + } mutex_lock(&sc->mutex); @@ -1337,6 +1342,7 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, ath9k_p2p_remove_vif(sc, vif); sc->cur_chan->nvifs--; + sc->tx99_vif = NULL; if (!ath9k_is_chanctx_enabled()) list_del(&avp->list); diff --git a/drivers/net/wireless/ath/ath9k/tx99.c b/drivers/net/wireless/ath/ath9k/tx99.c index 9b05ffb68c34..95544ce05acf 100644 --- a/drivers/net/wireless/ath/ath9k/tx99.c +++ b/drivers/net/wireless/ath/ath9k/tx99.c @@ -54,6 +54,7 @@ static struct sk_buff *ath9k_build_tx99_skb(struct ath_softc *sc) struct ieee80211_hdr *hdr; struct ieee80211_tx_info *tx_info; struct sk_buff *skb; + struct ath_vif *avp; skb = alloc_skb(len, GFP_KERNEL); if (!skb) @@ -71,11 +72,17 @@ static struct sk_buff *ath9k_build_tx99_skb(struct ath_softc *sc) memcpy(hdr->addr2, hw->wiphy->perm_addr, ETH_ALEN); memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN); + if (sc->tx99_vif) { + avp = (struct ath_vif *) sc->tx99_vif->drv_priv; + hdr->seq_ctrl |= cpu_to_le16(avp->seq_no); + } + tx_info = IEEE80211_SKB_CB(skb); memset(tx_info, 0, sizeof(*tx_info)); rate = &tx_info->control.rates[0]; tx_info->band = sc->cur_chan->chandef.chan->band; tx_info->flags = IEEE80211_TX_CTL_NO_ACK; + tx_info->control.vif = sc->tx99_vif; rate->count = 1; if (ah->curchan && IS_CHAN_HT(ah->curchan)) { rate->flags |= IEEE80211_TX_RC_MCS; diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 66b6a8872c06..43b6c8508e49 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -2973,7 +2973,7 @@ int ath9k_tx99_send(struct ath_softc *sc, struct sk_buff *skb, return -EINVAL; } - ath_set_rates(NULL, NULL, bf); + ath_set_rates(sc->tx99_vif, NULL, bf); ath9k_hw_set_desc_link(sc->sc_ah, bf->bf_desc, bf->bf_daddr); ath9k_hw_tx99_start(sc->sc_ah, txctl->txq->axq_qnum);