diff options
Diffstat (limited to 'package/kernel/mac80211/patches/380-0003-ath10k-debugfs-support-to-get-final-TPC-stats-for-10.patch')
-rw-r--r-- | package/kernel/mac80211/patches/380-0003-ath10k-debugfs-support-to-get-final-TPC-stats-for-10.patch | 824 |
1 files changed, 824 insertions, 0 deletions
diff --git a/package/kernel/mac80211/patches/380-0003-ath10k-debugfs-support-to-get-final-TPC-stats-for-10.patch b/package/kernel/mac80211/patches/380-0003-ath10k-debugfs-support-to-get-final-TPC-stats-for-10.patch new file mode 100644 index 0000000000..2e828c4265 --- /dev/null +++ b/package/kernel/mac80211/patches/380-0003-ath10k-debugfs-support-to-get-final-TPC-stats-for-10.patch @@ -0,0 +1,824 @@ +From bc64d05220f3e34cf432a166b83c8fff14cd7a3d Mon Sep 17 00:00:00 2001 +From: Maharaja Kennadyrajan <mkenna@codeaurora.org> +Date: Wed, 14 Mar 2018 12:14:08 +0200 +Subject: [PATCH] ath10k: debugfs support to get final TPC stats for 10.4 + variants + +Export the final Transmit Power Control (TPC) value, which is the +minimum of control power and existing TPC value to user space via +a new debugfs file "tpc_stats_final" to help with debugging. +It works with the new wmi cmd and event introduced in 10.4 firmware +branch. + +WMI command ID: WMI_PDEV_GET_TPC_TABLE_CMDID +WMI event ID: WMI_PDEV_TPC_TABLE_EVENTID + +cat /sys/kernel/debug/ieee80211/phyX/ath10k/tpc_stats_final + +$ cat /sys/kernel/debug/ieee80211/phyX/ath10k/tpc_stats_final + +TPC config for channel 5180 mode 10 + +CTL = 0x 0 Reg. Domain = 58 +Antenna Gain = 0 Reg. Max Antenna Gain = 0 +Power Limit = 60 Reg. Max Power = 60 +Num tx chains = 2 Num supported rates = 109 + +******************* CDD POWER TABLE **************** + +No. Preamble Rate_code tpc_value1 tpc_value2 tpc_value3 +0 CCK 0x40 0 0 +1 CCK 0x41 0 0 +[...] +107 HTCUP 0x 0 46 46 +108 HTCUP 0x 0 46 46 + +******************* STBC POWER TABLE **************** + +No. Preamble Rate_code tpc_value1 tpc_value2 tpc_value3 +0 CCK 0x40 0 0 +1 CCK 0x41 0 0 +[...] +107 HTCUP 0x 0 46 46 +108 HTCUP 0x 0 46 46 + +*********************************** +TXBF not supported +********************************** + +The existing tpc_stats debugfs file provides the dump +which is minimum of target power and regulatory domain. + +cat /sys/kernel/debug/ieee80211/phyX/ath10k/tpc_stats + +Hardware_used: QCA4019 +Firmware version: firmware-5.bin_10.4-3.0-00209 + +Signed-off-by: Maharaja Kennadyrajan <mkenna@codeaurora.org> +Signed-off-by: Kalle Valo <kvalo@codeaurora.org> +--- + drivers/net/wireless/ath/ath10k/core.h | 22 +++ + drivers/net/wireless/ath/ath10k/debug.c | 107 +++++++++++ + drivers/net/wireless/ath/ath10k/debug.h | 10 + + drivers/net/wireless/ath/ath10k/wmi-ops.h | 20 ++ + drivers/net/wireless/ath/ath10k/wmi.c | 308 ++++++++++++++++++++++++++++-- + drivers/net/wireless/ath/ath10k/wmi.h | 66 +++++++ + 6 files changed, 518 insertions(+), 15 deletions(-) + +--- a/drivers/net/wireless/ath/ath10k/core.h ++++ b/drivers/net/wireless/ath/ath10k/core.h +@@ -322,6 +322,27 @@ struct ath10k_tpc_stats { + struct ath10k_tpc_table tpc_table[WMI_TPC_FLAG]; + }; + ++struct ath10k_tpc_table_final { ++ u32 pream_idx[WMI_TPC_FINAL_RATE_MAX]; ++ u8 rate_code[WMI_TPC_FINAL_RATE_MAX]; ++ char tpc_value[WMI_TPC_FINAL_RATE_MAX][WMI_TPC_TX_N_CHAIN * WMI_TPC_BUF_SIZE]; ++}; ++ ++struct ath10k_tpc_stats_final { ++ u32 reg_domain; ++ u32 chan_freq; ++ u32 phy_mode; ++ u32 twice_antenna_reduction; ++ u32 twice_max_rd_power; ++ s32 twice_antenna_gain; ++ u32 power_limit; ++ u32 num_tx_chain; ++ u32 ctl; ++ u32 rate_max; ++ u8 flag[WMI_TPC_FLAG]; ++ struct ath10k_tpc_table_final tpc_table_final[WMI_TPC_FLAG]; ++}; ++ + struct ath10k_dfs_stats { + u32 phy_errors; + u32 pulses_total; +@@ -482,6 +503,7 @@ struct ath10k_debug { + + /* used for tpc-dump storage, protected by data-lock */ + struct ath10k_tpc_stats *tpc_stats; ++ struct ath10k_tpc_stats_final *tpc_stats_final; + + struct completion tpc_complete; + +--- a/drivers/net/wireless/ath/ath10k/debug.c ++++ b/drivers/net/wireless/ath/ath10k/debug.c +@@ -1737,6 +1737,19 @@ void ath10k_debug_tpc_stats_process(stru + spin_unlock_bh(&ar->data_lock); + } + ++void ++ath10k_debug_tpc_stats_final_process(struct ath10k *ar, ++ struct ath10k_tpc_stats_final *tpc_stats) ++{ ++ spin_lock_bh(&ar->data_lock); ++ ++ kfree(ar->debug.tpc_stats_final); ++ ar->debug.tpc_stats_final = tpc_stats; ++ complete(&ar->debug.tpc_complete); ++ ++ spin_unlock_bh(&ar->data_lock); ++} ++ + static void ath10k_tpc_stats_print(struct ath10k_tpc_stats *tpc_stats, + unsigned int j, char *buf, size_t *len) + { +@@ -2400,6 +2413,95 @@ static const struct file_operations fops + .llseek = default_llseek, + }; + ++static int ath10k_debug_tpc_stats_final_request(struct ath10k *ar) ++{ ++ int ret; ++ unsigned long time_left; ++ ++ lockdep_assert_held(&ar->conf_mutex); ++ ++ reinit_completion(&ar->debug.tpc_complete); ++ ++ ret = ath10k_wmi_pdev_get_tpc_table_cmdid(ar, WMI_TPC_CONFIG_PARAM); ++ if (ret) { ++ ath10k_warn(ar, "failed to request tpc table cmdid: %d\n", ret); ++ return ret; ++ } ++ ++ time_left = wait_for_completion_timeout(&ar->debug.tpc_complete, ++ 1 * HZ); ++ if (time_left == 0) ++ return -ETIMEDOUT; ++ ++ return 0; ++} ++ ++static int ath10k_tpc_stats_final_open(struct inode *inode, struct file *file) ++{ ++ struct ath10k *ar = inode->i_private; ++ void *buf; ++ int ret; ++ ++ mutex_lock(&ar->conf_mutex); ++ ++ if (ar->state != ATH10K_STATE_ON) { ++ ret = -ENETDOWN; ++ goto err_unlock; ++ } ++ ++ buf = vmalloc(ATH10K_TPC_CONFIG_BUF_SIZE); ++ if (!buf) { ++ ret = -ENOMEM; ++ goto err_unlock; ++ } ++ ++ ret = ath10k_debug_tpc_stats_final_request(ar); ++ if (ret) { ++ ath10k_warn(ar, "failed to request tpc stats final: %d\n", ++ ret); ++ goto err_free; ++ } ++ ++ ath10k_tpc_stats_fill(ar, ar->debug.tpc_stats, buf); ++ file->private_data = buf; ++ ++ mutex_unlock(&ar->conf_mutex); ++ return 0; ++ ++err_free: ++ vfree(buf); ++ ++err_unlock: ++ mutex_unlock(&ar->conf_mutex); ++ return ret; ++} ++ ++static int ath10k_tpc_stats_final_release(struct inode *inode, ++ struct file *file) ++{ ++ vfree(file->private_data); ++ ++ return 0; ++} ++ ++static ssize_t ath10k_tpc_stats_final_read(struct file *file, ++ char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ const char *buf = file->private_data; ++ unsigned int len = strlen(buf); ++ ++ return simple_read_from_buffer(user_buf, count, ppos, buf, len); ++} ++ ++static const struct file_operations fops_tpc_stats_final = { ++ .open = ath10k_tpc_stats_final_open, ++ .release = ath10k_tpc_stats_final_release, ++ .read = ath10k_tpc_stats_final_read, ++ .owner = THIS_MODULE, ++ .llseek = default_llseek, ++}; ++ + int ath10k_debug_create(struct ath10k *ar) + { + ar->debug.fw_crash_data = vzalloc(sizeof(*ar->debug.fw_crash_data)); +@@ -2525,6 +2627,11 @@ int ath10k_debug_register(struct ath10k + debugfs_create_file("fw_checksums", 0400, ar->debug.debugfs_phy, ar, + &fops_fw_checksums); + ++ if (test_bit(WMI_SERVICE_TPC_STATS_FINAL, ar->wmi.svc_map)) ++ debugfs_create_file("tpc_stats_final", 0400, ++ ar->debug.debugfs_phy, ar, ++ &fops_tpc_stats_final); ++ + return 0; + } + +--- a/drivers/net/wireless/ath/ath10k/debug.h ++++ b/drivers/net/wireless/ath/ath10k/debug.h +@@ -84,6 +84,9 @@ void ath10k_debug_unregister(struct ath1 + void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb); + void ath10k_debug_tpc_stats_process(struct ath10k *ar, + struct ath10k_tpc_stats *tpc_stats); ++void ++ath10k_debug_tpc_stats_final_process(struct ath10k *ar, ++ struct ath10k_tpc_stats_final *tpc_stats); + struct ath10k_fw_crash_data * + ath10k_debug_get_new_fw_crash_data(struct ath10k *ar); + +@@ -151,6 +154,13 @@ static inline void ath10k_debug_tpc_stat + { + kfree(tpc_stats); + } ++ ++static inline void ++ath10k_debug_tpc_stats_final_process(struct ath10k *ar, ++ struct ath10k_tpc_stats_final *tpc_stats) ++{ ++ kfree(tpc_stats); ++} + + static inline void ath10k_debug_dbglog_add(struct ath10k *ar, u8 *buffer, + int len) +--- a/drivers/net/wireless/ath/ath10k/wmi-ops.h ++++ b/drivers/net/wireless/ath/ath10k/wmi-ops.h +@@ -197,6 +197,9 @@ struct wmi_ops { + (struct ath10k *ar, + enum wmi_bss_survey_req_type type); + struct sk_buff *(*gen_echo)(struct ath10k *ar, u32 value); ++ struct sk_buff *(*gen_pdev_get_tpc_table_cmdid)(struct ath10k *ar, ++ u32 param); ++ + }; + + int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id); +@@ -1418,4 +1421,21 @@ ath10k_wmi_echo(struct ath10k *ar, u32 v + return ath10k_wmi_cmd_send(ar, skb, wmi->cmd->echo_cmdid); + } + ++static inline int ++ath10k_wmi_pdev_get_tpc_table_cmdid(struct ath10k *ar, u32 param) ++{ ++ struct sk_buff *skb; ++ ++ if (!ar->wmi.ops->gen_pdev_get_tpc_table_cmdid) ++ return -EOPNOTSUPP; ++ ++ skb = ar->wmi.ops->gen_pdev_get_tpc_table_cmdid(ar, param); ++ ++ if (IS_ERR(skb)) ++ return PTR_ERR(skb); ++ ++ return ath10k_wmi_cmd_send(ar, skb, ++ ar->wmi.cmd->pdev_get_tpc_table_cmdid); ++} ++ + #endif +--- a/drivers/net/wireless/ath/ath10k/wmi.c ++++ b/drivers/net/wireless/ath/ath10k/wmi.c +@@ -1,6 +1,7 @@ + /* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. ++ * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above +@@ -196,6 +197,7 @@ static struct wmi_cmd_map wmi_cmd_map = + .mu_cal_start_cmdid = WMI_CMD_UNSUPPORTED, + .set_cca_params_cmdid = WMI_CMD_UNSUPPORTED, + .pdev_bss_chan_info_request_cmdid = WMI_CMD_UNSUPPORTED, ++ .pdev_get_tpc_table_cmdid = WMI_CMD_UNSUPPORTED, + }; + + /* 10.X WMI cmd track */ +@@ -362,6 +364,7 @@ static struct wmi_cmd_map wmi_10x_cmd_ma + .mu_cal_start_cmdid = WMI_CMD_UNSUPPORTED, + .set_cca_params_cmdid = WMI_CMD_UNSUPPORTED, + .pdev_bss_chan_info_request_cmdid = WMI_CMD_UNSUPPORTED, ++ .pdev_get_tpc_table_cmdid = WMI_CMD_UNSUPPORTED, + }; + + /* 10.2.4 WMI cmd track */ +@@ -528,6 +531,7 @@ static struct wmi_cmd_map wmi_10_2_4_cmd + .set_cca_params_cmdid = WMI_CMD_UNSUPPORTED, + .pdev_bss_chan_info_request_cmdid = + WMI_10_2_PDEV_BSS_CHAN_INFO_REQUEST_CMDID, ++ .pdev_get_tpc_table_cmdid = WMI_CMD_UNSUPPORTED, + }; + + /* 10.4 WMI cmd track */ +@@ -1480,6 +1484,7 @@ static struct wmi_cmd_map wmi_10_2_cmd_m + .pdev_get_ani_cck_config_cmdid = WMI_CMD_UNSUPPORTED, + .pdev_get_ani_ofdm_config_cmdid = WMI_CMD_UNSUPPORTED, + .pdev_reserve_ast_entry_cmdid = WMI_CMD_UNSUPPORTED, ++ .pdev_get_tpc_table_cmdid = WMI_CMD_UNSUPPORTED, + }; + + static struct wmi_pdev_param_map wmi_10_4_pdev_param_map = { +@@ -4313,19 +4318,11 @@ static void ath10k_tpc_config_disp_table + } + } + +-void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, struct sk_buff *skb) ++void ath10k_wmi_tpc_config_get_rate_code(u8 *rate_code, u16 *pream_table, ++ u32 num_tx_chain) + { +- u32 i, j, pream_idx, num_tx_chain; +- u8 rate_code[WMI_TPC_RATE_MAX], rate_idx; +- u16 pream_table[WMI_TPC_PREAM_TABLE_MAX]; +- struct wmi_pdev_tpc_config_event *ev; +- struct ath10k_tpc_stats *tpc_stats; +- +- ev = (struct wmi_pdev_tpc_config_event *)skb->data; +- +- tpc_stats = kzalloc(sizeof(*tpc_stats), GFP_ATOMIC); +- if (!tpc_stats) +- return; ++ u32 i, j, pream_idx; ++ u8 rate_idx; + + /* Create the rate code table based on the chains supported */ + rate_idx = 0; +@@ -4349,8 +4346,6 @@ void ath10k_wmi_event_pdev_tpc_config(st + pream_table[pream_idx] = rate_idx; + pream_idx++; + +- num_tx_chain = __le32_to_cpu(ev->num_tx_chain); +- + /* Fill HT20 rate code */ + for (i = 0; i < num_tx_chain; i++) { + for (j = 0; j < 8; j++) { +@@ -4374,7 +4369,7 @@ void ath10k_wmi_event_pdev_tpc_config(st + pream_idx++; + + /* Fill VHT20 rate code */ +- for (i = 0; i < __le32_to_cpu(ev->num_tx_chain); i++) { ++ for (i = 0; i < num_tx_chain; i++) { + for (j = 0; j < 10; j++) { + rate_code[rate_idx] = + ATH10K_HW_RATECODE(j, i, WMI_RATE_PREAMBLE_VHT); +@@ -4418,6 +4413,26 @@ void ath10k_wmi_event_pdev_tpc_config(st + ATH10K_HW_RATECODE(0, 0, WMI_RATE_PREAMBLE_OFDM); + + pream_table[pream_idx] = ATH10K_TPC_PREAM_TABLE_END; ++} ++ ++void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, struct sk_buff *skb) ++{ ++ u32 num_tx_chain; ++ u8 rate_code[WMI_TPC_RATE_MAX]; ++ u16 pream_table[WMI_TPC_PREAM_TABLE_MAX]; ++ struct wmi_pdev_tpc_config_event *ev; ++ struct ath10k_tpc_stats *tpc_stats; ++ ++ ev = (struct wmi_pdev_tpc_config_event *)skb->data; ++ ++ tpc_stats = kzalloc(sizeof(*tpc_stats), GFP_ATOMIC); ++ if (!tpc_stats) ++ return; ++ ++ num_tx_chain = __le32_to_cpu(ev->num_tx_chain); ++ ++ ath10k_wmi_tpc_config_get_rate_code(rate_code, pream_table, ++ num_tx_chain); + + tpc_stats->chan_freq = __le32_to_cpu(ev->chan_freq); + tpc_stats->phy_mode = __le32_to_cpu(ev->phy_mode); +@@ -4457,6 +4472,246 @@ void ath10k_wmi_event_pdev_tpc_config(st + __le32_to_cpu(ev->rate_max)); + } + ++static u8 ++ath10k_wmi_tpc_final_get_rate(struct ath10k *ar, ++ struct wmi_pdev_tpc_final_table_event *ev, ++ u32 rate_idx, u32 num_chains, ++ u32 rate_code, u8 type, u32 pream_idx) ++{ ++ u8 tpc, num_streams, preamble, ch, stm_idx; ++ s8 pow_agcdd, pow_agstbc, pow_agtxbf; ++ int pream; ++ ++ num_streams = ATH10K_HW_NSS(rate_code); ++ preamble = ATH10K_HW_PREAMBLE(rate_code); ++ ch = num_chains - 1; ++ stm_idx = num_streams - 1; ++ pream = -1; ++ ++ if (__le32_to_cpu(ev->chan_freq) <= 2483) { ++ switch (pream_idx) { ++ case WMI_TPC_PREAM_2GHZ_CCK: ++ pream = 0; ++ break; ++ case WMI_TPC_PREAM_2GHZ_OFDM: ++ pream = 1; ++ break; ++ case WMI_TPC_PREAM_2GHZ_HT20: ++ case WMI_TPC_PREAM_2GHZ_VHT20: ++ pream = 2; ++ break; ++ case WMI_TPC_PREAM_2GHZ_HT40: ++ case WMI_TPC_PREAM_2GHZ_VHT40: ++ pream = 3; ++ break; ++ case WMI_TPC_PREAM_2GHZ_VHT80: ++ pream = 4; ++ break; ++ default: ++ pream = -1; ++ break; ++ } ++ } ++ ++ if (__le32_to_cpu(ev->chan_freq) >= 5180) { ++ switch (pream_idx) { ++ case WMI_TPC_PREAM_5GHZ_OFDM: ++ pream = 0; ++ break; ++ case WMI_TPC_PREAM_5GHZ_HT20: ++ case WMI_TPC_PREAM_5GHZ_VHT20: ++ pream = 1; ++ break; ++ case WMI_TPC_PREAM_5GHZ_HT40: ++ case WMI_TPC_PREAM_5GHZ_VHT40: ++ pream = 2; ++ break; ++ case WMI_TPC_PREAM_5GHZ_VHT80: ++ pream = 3; ++ break; ++ case WMI_TPC_PREAM_5GHZ_HTCUP: ++ pream = 4; ++ break; ++ default: ++ pream = -1; ++ break; ++ } ++ } ++ ++ if (pream == 4) ++ tpc = min_t(u8, ev->rates_array[rate_idx], ++ ev->max_reg_allow_pow[ch]); ++ else ++ tpc = min_t(u8, min_t(u8, ev->rates_array[rate_idx], ++ ev->max_reg_allow_pow[ch]), ++ ev->ctl_power_table[0][pream][stm_idx]); ++ ++ if (__le32_to_cpu(ev->num_tx_chain) <= 1) ++ goto out; ++ ++ if (preamble == WMI_RATE_PREAMBLE_CCK) ++ goto out; ++ ++ if (num_chains <= num_streams) ++ goto out; ++ ++ switch (type) { ++ case WMI_TPC_TABLE_TYPE_STBC: ++ pow_agstbc = ev->max_reg_allow_pow_agstbc[ch - 1][stm_idx]; ++ if (pream == 4) ++ tpc = min_t(u8, tpc, pow_agstbc); ++ else ++ tpc = min_t(u8, min_t(u8, tpc, pow_agstbc), ++ ev->ctl_power_table[0][pream][stm_idx]); ++ break; ++ case WMI_TPC_TABLE_TYPE_TXBF: ++ pow_agtxbf = ev->max_reg_allow_pow_agtxbf[ch - 1][stm_idx]; ++ if (pream == 4) ++ tpc = min_t(u8, tpc, pow_agtxbf); ++ else ++ tpc = min_t(u8, min_t(u8, tpc, pow_agtxbf), ++ ev->ctl_power_table[1][pream][stm_idx]); ++ break; ++ case WMI_TPC_TABLE_TYPE_CDD: ++ pow_agcdd = ev->max_reg_allow_pow_agcdd[ch - 1][stm_idx]; ++ if (pream == 4) ++ tpc = min_t(u8, tpc, pow_agcdd); ++ else ++ tpc = min_t(u8, min_t(u8, tpc, pow_agcdd), ++ ev->ctl_power_table[0][pream][stm_idx]); ++ break; ++ default: ++ ath10k_warn(ar, "unknown wmi tpc final table type: %d\n", type); ++ tpc = 0; ++ break; ++ } ++ ++out: ++ return tpc; ++} ++ ++static void ++ath10k_wmi_tpc_stats_final_disp_tables(struct ath10k *ar, ++ struct wmi_pdev_tpc_final_table_event *ev, ++ struct ath10k_tpc_stats_final *tpc_stats, ++ u8 *rate_code, u16 *pream_table, u8 type) ++{ ++ u32 i, j, pream_idx, flags; ++ u8 tpc[WMI_TPC_TX_N_CHAIN]; ++ char tpc_value[WMI_TPC_TX_N_CHAIN * WMI_TPC_BUF_SIZE]; ++ char buff[WMI_TPC_BUF_SIZE]; ++ ++ flags = __le32_to_cpu(ev->flags); ++ ++ switch (type) { ++ case WMI_TPC_TABLE_TYPE_CDD: ++ if (!(flags & WMI_TPC_CONFIG_EVENT_FLAG_TABLE_CDD)) { ++ ath10k_dbg(ar, ATH10K_DBG_WMI, "CDD not supported\n"); ++ tpc_stats->flag[type] = ATH10K_TPC_TABLE_TYPE_FLAG; ++ return; ++ } ++ break; ++ case WMI_TPC_TABLE_TYPE_STBC: ++ if (!(flags & WMI_TPC_CONFIG_EVENT_FLAG_TABLE_STBC)) { ++ ath10k_dbg(ar, ATH10K_DBG_WMI, "STBC not supported\n"); ++ tpc_stats->flag[type] = ATH10K_TPC_TABLE_TYPE_FLAG; ++ return; ++ } ++ break; ++ case WMI_TPC_TABLE_TYPE_TXBF: ++ if (!(flags & WMI_TPC_CONFIG_EVENT_FLAG_TABLE_TXBF)) { ++ ath10k_dbg(ar, ATH10K_DBG_WMI, "TXBF not supported\n"); ++ tpc_stats->flag[type] = ATH10K_TPC_TABLE_TYPE_FLAG; ++ return; ++ } ++ break; ++ default: ++ ath10k_dbg(ar, ATH10K_DBG_WMI, ++ "invalid table type in wmi tpc event: %d\n", type); ++ return; ++ } ++ ++ pream_idx = 0; ++ for (i = 0; i < __le32_to_cpu(ev->rate_max); i++) { ++ memset(tpc_value, 0, sizeof(tpc_value)); ++ memset(buff, 0, sizeof(buff)); ++ if (i == pream_table[pream_idx]) ++ pream_idx++; ++ ++ for (j = 0; j < WMI_TPC_TX_N_CHAIN; j++) { ++ if (j >= __le32_to_cpu(ev->num_tx_chain)) ++ break; ++ ++ tpc[j] = ath10k_wmi_tpc_final_get_rate(ar, ev, i, j + 1, ++ rate_code[i], ++ type, pream_idx); ++ snprintf(buff, sizeof(buff), "%8d ", tpc[j]); ++ strncat(tpc_value, buff, strlen(buff)); ++ } ++ tpc_stats->tpc_table_final[type].pream_idx[i] = pream_idx; ++ tpc_stats->tpc_table_final[type].rate_code[i] = rate_code[i]; ++ memcpy(tpc_stats->tpc_table_final[type].tpc_value[i], ++ tpc_value, sizeof(tpc_value)); ++ } ++} ++ ++void ath10k_wmi_event_tpc_final_table(struct ath10k *ar, struct sk_buff *skb) ++{ ++ u32 num_tx_chain; ++ u8 rate_code[WMI_TPC_FINAL_RATE_MAX]; ++ u16 pream_table[WMI_TPC_PREAM_TABLE_MAX]; ++ struct wmi_pdev_tpc_final_table_event *ev; ++ struct ath10k_tpc_stats_final *tpc_stats; ++ ++ ev = (struct wmi_pdev_tpc_final_table_event *)skb->data; ++ ++ tpc_stats = kzalloc(sizeof(*tpc_stats), GFP_ATOMIC); ++ if (!tpc_stats) ++ return; ++ ++ num_tx_chain = __le32_to_cpu(ev->num_tx_chain); ++ ++ ath10k_wmi_tpc_config_get_rate_code(rate_code, pream_table, ++ num_tx_chain); ++ ++ tpc_stats->chan_freq = __le32_to_cpu(ev->chan_freq); ++ tpc_stats->phy_mode = __le32_to_cpu(ev->phy_mode); ++ tpc_stats->ctl = __le32_to_cpu(ev->ctl); ++ tpc_stats->reg_domain = __le32_to_cpu(ev->reg_domain); ++ tpc_stats->twice_antenna_gain = a_sle32_to_cpu(ev->twice_antenna_gain); ++ tpc_stats->twice_antenna_reduction = ++ __le32_to_cpu(ev->twice_antenna_reduction); ++ tpc_stats->power_limit = __le32_to_cpu(ev->power_limit); ++ tpc_stats->twice_max_rd_power = __le32_to_cpu(ev->twice_max_rd_power); ++ tpc_stats->num_tx_chain = __le32_to_cpu(ev->num_tx_chain); ++ tpc_stats->rate_max = __le32_to_cpu(ev->rate_max); ++ ++ ath10k_wmi_tpc_stats_final_disp_tables(ar, ev, tpc_stats, ++ rate_code, pream_table, ++ WMI_TPC_TABLE_TYPE_CDD); ++ ath10k_wmi_tpc_stats_final_disp_tables(ar, ev, tpc_stats, ++ rate_code, pream_table, ++ WMI_TPC_TABLE_TYPE_STBC); ++ ath10k_wmi_tpc_stats_final_disp_tables(ar, ev, tpc_stats, ++ rate_code, pream_table, ++ WMI_TPC_TABLE_TYPE_TXBF); ++ ++ ath10k_debug_tpc_stats_final_process(ar, tpc_stats); ++ ++ ath10k_dbg(ar, ATH10K_DBG_WMI, ++ "wmi event tpc final table channel %d mode %d ctl %d regd %d gain %d %d limit %d max_power %d tx_chanins %d rates %d\n", ++ __le32_to_cpu(ev->chan_freq), ++ __le32_to_cpu(ev->phy_mode), ++ __le32_to_cpu(ev->ctl), ++ __le32_to_cpu(ev->reg_domain), ++ a_sle32_to_cpu(ev->twice_antenna_gain), ++ __le32_to_cpu(ev->twice_antenna_reduction), ++ __le32_to_cpu(ev->power_limit), ++ __le32_to_cpu(ev->twice_max_rd_power) / 2, ++ __le32_to_cpu(ev->num_tx_chain), ++ __le32_to_cpu(ev->rate_max)); ++} ++ + static void + ath10k_wmi_handle_tdls_peer_event(struct ath10k *ar, struct sk_buff *skb) + { +@@ -5550,6 +5805,9 @@ static void ath10k_wmi_10_4_op_rx(struct + case WMI_10_4_TDLS_PEER_EVENTID: + ath10k_wmi_handle_tdls_peer_event(ar, skb); + break; ++ case WMI_10_4_PDEV_TPC_TABLE_EVENTID: ++ ath10k_wmi_event_tpc_final_table(ar, skb); ++ break; + default: + ath10k_warn(ar, "Unknown eventid: %d\n", id); + break; +@@ -7990,6 +8248,24 @@ static u32 ath10k_wmi_prepare_peer_qos(u + } + + static struct sk_buff * ++ath10k_wmi_10_4_op_gen_pdev_get_tpc_table_cmdid(struct ath10k *ar, u32 param) ++{ ++ struct wmi_pdev_get_tpc_table_cmd *cmd; ++ struct sk_buff *skb; ++ ++ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); ++ if (!skb) ++ return ERR_PTR(-ENOMEM); ++ ++ cmd = (struct wmi_pdev_get_tpc_table_cmd *)skb->data; ++ cmd->param = __cpu_to_le32(param); ++ ++ ath10k_dbg(ar, ATH10K_DBG_WMI, ++ "wmi pdev get tpc table param:%d\n", param); ++ return skb; ++} ++ ++static struct sk_buff * + ath10k_wmi_10_4_gen_tdls_peer_update(struct ath10k *ar, + const struct wmi_tdls_peer_update_cmd_arg *arg, + const struct wmi_tdls_peer_capab_arg *cap, +@@ -8430,6 +8706,8 @@ static const struct wmi_ops wmi_10_4_ops + .ext_resource_config = ath10k_wmi_10_4_ext_resource_config, + .gen_update_fw_tdls_state = ath10k_wmi_10_4_gen_update_fw_tdls_state, + .gen_tdls_peer_update = ath10k_wmi_10_4_gen_tdls_peer_update, ++ .gen_pdev_get_tpc_table_cmdid = ++ ath10k_wmi_10_4_op_gen_pdev_get_tpc_table_cmdid, + + /* shared with 10.2 */ + .pull_echo_ev = ath10k_wmi_op_pull_echo_ev, +--- a/drivers/net/wireless/ath/ath10k/wmi.h ++++ b/drivers/net/wireless/ath/ath10k/wmi.h +@@ -1,6 +1,7 @@ + /* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. ++ * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above +@@ -197,6 +198,9 @@ enum wmi_service { + WMI_SERVICE_TDLS_EXPLICIT_MODE_ONLY, + WMI_SERVICE_MGMT_TX_WMI, + WMI_SERVICE_TDLS_WIDER_BANDWIDTH, ++ WMI_SERVICE_HTT_MGMT_TX_COMP_VALID_FLAGS, ++ WMI_SERVICE_HOST_DFS_CHECK_SUPPORT, ++ WMI_SERVICE_TPC_STATS_FINAL, + + /* keep last */ + WMI_SERVICE_MAX, +@@ -339,6 +343,9 @@ enum wmi_10_4_service { + WMI_10_4_SERVICE_TDLS_CONN_TRACKER_IN_HOST_MODE, + WMI_10_4_SERVICE_TDLS_EXPLICIT_MODE_ONLY, + WMI_10_4_SERVICE_TDLS_WIDER_BANDWIDTH, ++ 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, + }; + + static inline char *wmi_service_name(int service_id) +@@ -448,6 +455,9 @@ static inline char *wmi_service_name(int + SVCSTR(WMI_SERVICE_TDLS_CONN_TRACKER_IN_HOST_MODE); + SVCSTR(WMI_SERVICE_TDLS_EXPLICIT_MODE_ONLY); + SVCSTR(WMI_SERVICE_TDLS_WIDER_BANDWIDTH); ++ SVCSTR(WMI_SERVICE_HTT_MGMT_TX_COMP_VALID_FLAGS); ++ SVCSTR(WMI_SERVICE_HOST_DFS_CHECK_SUPPORT); ++ SVCSTR(WMI_SERVICE_TPC_STATS_FINAL); + default: + return NULL; + } +@@ -746,6 +756,12 @@ static inline void wmi_10_4_svc_map(cons + WMI_SERVICE_TDLS_EXPLICIT_MODE_ONLY, len); + SVCMAP(WMI_10_4_SERVICE_TDLS_WIDER_BANDWIDTH, + WMI_SERVICE_TDLS_WIDER_BANDWIDTH, len); ++ SVCMAP(WMI_10_4_SERVICE_HTT_MGMT_TX_COMP_VALID_FLAGS, ++ WMI_SERVICE_HTT_MGMT_TX_COMP_VALID_FLAGS, len); ++ SVCMAP(WMI_10_4_SERVICE_HOST_DFS_CHECK_SUPPORT, ++ WMI_SERVICE_HOST_DFS_CHECK_SUPPORT, len); ++ SVCMAP(WMI_10_4_SERVICE_TPC_STATS_FINAL, ++ WMI_SERVICE_TPC_STATS_FINAL, len); + } + + #undef SVCMAP +@@ -3992,10 +4008,12 @@ struct wmi_pdev_get_tpc_config_cmd { + + #define WMI_TPC_CONFIG_PARAM 1 + #define WMI_TPC_RATE_MAX 160 ++#define WMI_TPC_FINAL_RATE_MAX 240 + #define WMI_TPC_TX_N_CHAIN 4 + #define WMI_TPC_PREAM_TABLE_MAX 10 + #define WMI_TPC_FLAG 3 + #define WMI_TPC_BUF_SIZE 10 ++#define WMI_TPC_BEAMFORMING 2 + + enum wmi_tpc_table_type { + WMI_TPC_TABLE_TYPE_CDD = 0, +@@ -4038,6 +4056,51 @@ enum wmi_tp_scale { + WMI_TP_SCALE_SIZE = 5, /* max num of enum */ + }; + ++struct wmi_pdev_tpc_final_table_event { ++ __le32 reg_domain; ++ __le32 chan_freq; ++ __le32 phy_mode; ++ __le32 twice_antenna_reduction; ++ __le32 twice_max_rd_power; ++ a_sle32 twice_antenna_gain; ++ __le32 power_limit; ++ __le32 rate_max; ++ __le32 num_tx_chain; ++ __le32 ctl; ++ __le32 flags; ++ s8 max_reg_allow_pow[WMI_TPC_TX_N_CHAIN]; ++ s8 max_reg_allow_pow_agcdd[WMI_TPC_TX_N_CHAIN][WMI_TPC_TX_N_CHAIN]; ++ s8 max_reg_allow_pow_agstbc[WMI_TPC_TX_N_CHAIN][WMI_TPC_TX_N_CHAIN]; ++ s8 max_reg_allow_pow_agtxbf[WMI_TPC_TX_N_CHAIN][WMI_TPC_TX_N_CHAIN]; ++ u8 rates_array[WMI_TPC_FINAL_RATE_MAX]; ++ u8 ctl_power_table[WMI_TPC_BEAMFORMING][WMI_TPC_TX_N_CHAIN] ++ [WMI_TPC_TX_N_CHAIN]; ++} __packed; ++ ++struct wmi_pdev_get_tpc_table_cmd { ++ __le32 param; ++} __packed; ++ ++enum wmi_tpc_pream_2ghz { ++ WMI_TPC_PREAM_2GHZ_CCK = 0, ++ WMI_TPC_PREAM_2GHZ_OFDM, ++ WMI_TPC_PREAM_2GHZ_HT20, ++ WMI_TPC_PREAM_2GHZ_HT40, ++ WMI_TPC_PREAM_2GHZ_VHT20, ++ WMI_TPC_PREAM_2GHZ_VHT40, ++ WMI_TPC_PREAM_2GHZ_VHT80, ++}; ++ ++enum wmi_tpc_pream_5ghz { ++ WMI_TPC_PREAM_5GHZ_OFDM = 1, ++ WMI_TPC_PREAM_5GHZ_HT20, ++ WMI_TPC_PREAM_5GHZ_HT40, ++ WMI_TPC_PREAM_5GHZ_VHT20, ++ WMI_TPC_PREAM_5GHZ_VHT40, ++ WMI_TPC_PREAM_5GHZ_VHT80, ++ WMI_TPC_PREAM_5GHZ_HTCUP, ++}; ++ + struct wmi_pdev_chanlist_update_event { + /* number of channels */ + __le32 num_chan; +@@ -6977,5 +7040,8 @@ void ath10k_wmi_10_4_op_fw_stats_fill(st + int ath10k_wmi_op_get_vdev_subtype(struct ath10k *ar, + enum wmi_vdev_subtype subtype); + int ath10k_wmi_barrier(struct ath10k *ar); ++void ath10k_wmi_tpc_config_get_rate_code(u8 *rate_code, u16 *pream_table, ++ u32 num_tx_chain); ++void ath10k_wmi_event_tpc_final_table(struct ath10k *ar, struct sk_buff *skb); + + #endif /* _WMI_H_ */ |