Return-path: Received: from smtp.codeaurora.org ([198.145.29.96]:56410 "EHLO smtp.codeaurora.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751064AbeBZIkd (ORCPT ); Mon, 26 Feb 2018 03:40:33 -0500 From: mkenna@codeaurora.org To: ath10k@lists.infradead.org Cc: linux-wireless@vger.kernel.org, Maharaja Kennadyrajan Subject: [PATCH v2] ath10k: debugfs support to get final TPC stats for 10.4 variants Date: Mon, 26 Feb 2018 14:10:37 +0530 Message-Id: <1519634437-6672-1-git-send-email-mkenna@codeaurora.org> (sfid-20180226_094049_973583_7912E29B) Sender: linux-wireless-owner@vger.kernel.org List-ID: From: Maharaja Kennadyrajan 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 --- v2: Addressed sparse warnings: (new ones prefixed by >>) drivers/net/wireless/ath/ath10k/core.h | 23 +++ drivers/net/wireless/ath/ath10k/debug.c | 107 +++++++++++ drivers/net/wireless/ath/ath10k/debug.h | 11 ++ drivers/net/wireless/ath/ath10k/wmi-ops.h | 21 ++ drivers/net/wireless/ath/ath10k/wmi.c | 308 ++++++++++++++++++++++++++++-- drivers/net/wireless/ath/ath10k/wmi.h | 66 +++++++ 6 files changed, 521 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index fe6b303..a7cbd79 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 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 @@ -324,6 +325,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; @@ -487,6 +509,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; diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 554cd78..a726f7c 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -1480,6 +1480,19 @@ void ath10k_debug_tpc_stats_process(struct ath10k *ar, 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) { @@ -2143,6 +2156,95 @@ static ssize_t ath10k_debug_fw_checksums_read(struct file *file, .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.cal_data = vzalloc(ATH10K_DEBUG_CAL_DATA_LEN); @@ -2258,6 +2360,11 @@ int ath10k_debug_register(struct ath10k *ar) 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; } diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h index e543088..1d08afe 100644 --- a/drivers/net/wireless/ath/ath10k/debug.h +++ b/drivers/net/wireless/ath/ath10k/debug.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 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 @@ -101,6 +102,9 @@ struct ath10k_pktlog_hdr { 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); void ath10k_debug_dbglog_add(struct ath10k *ar, u8 *buffer, int len); #define ATH10K_DFS_STAT_INC(ar, c) (ar->debug.dfs_stats.c++) @@ -164,6 +168,13 @@ static inline void ath10k_debug_tpc_stats_process(struct ath10k *ar, 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) { diff --git a/drivers/net/wireless/ath/ath10k/wmi-ops.h b/drivers/net/wireless/ath/ath10k/wmi-ops.h index 14093cf..5692ef7 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-ops.h +++ b/drivers/net/wireless/ath/ath10k/wmi-ops.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 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 @@ 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); @@ -1425,4 +1429,21 @@ struct wmi_ops { 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 diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 58dc218..77c6bc6 100644 --- 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-2017 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 @@ .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 @@ .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 @@ .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 @@ .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_tables(struct ath10k *ar, } } -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(struct ath10k *ar, struct sk_buff *skb) 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(struct ath10k *ar, struct sk_buff *skb) 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(struct ath10k *ar, struct sk_buff *skb) 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(struct ath10k *ar, struct sk_buff *skb) __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) { @@ -5549,6 +5804,9 @@ static void ath10k_wmi_10_4_op_rx(struct ath10k *ar, struct sk_buff *skb) 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(u8 uapsd_queues, u8 sp) } 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 u32 ath10k_wmi_prepare_peer_qos(u8 uapsd_queues, u8 sp) .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, diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index c7b30ed..c8fc45d 100644 --- 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-2017 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 service_id) 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(const __le32 *in, unsigned long *out, 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 @@ -3993,10 +4009,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, @@ -4039,6 +4057,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; @@ -6979,5 +7042,8 @@ void ath10k_wmi_10_4_op_fw_stats_fill(struct ath10k *ar, 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_ */ -- 1.9.1