From 4f6620cd6bbd1e10018969ab52bdd8e083d91884 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 24 Oct 2022 16:39:54 +0100 Subject: [PATCH 001/144] wifi: ath9k: remove variable sent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Variable sent is just being incremented and it's never used anywhere else. The variable and the increment are redundant so remove it. Signed-off-by: Colin Ian King Acked-by: Toke Høiland-Jørgensen Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221024153954.2168503-1-colin.i.king@gmail.com --- drivers/net/wireless/ath/ath9k/xmit.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index ba271a10d4ab..39abb59d8771 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1678,7 +1678,6 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw, struct ieee80211_tx_info *info; struct list_head bf_q; struct ath_buf *bf_tail = NULL, *bf = NULL; - int sent = 0; int i, ret; INIT_LIST_HEAD(&bf_q); @@ -1707,7 +1706,6 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw, bf_tail = bf; nframes--; - sent++; TX_STAT_INC(sc, txq->axq_qnum, a_queued_hw); if (an->sta && skb_queue_empty(&tid->retry_q)) From ed3725e15a154ebebf44e0c34806c57525483f92 Mon Sep 17 00:00:00 2001 From: Rahul Bhattacharjee Date: Fri, 21 Oct 2022 14:31:26 +0530 Subject: [PATCH 002/144] wifi: ath11k: Fix qmi_msg_handler data structure initialization qmi_msg_handler is required to be null terminated by QMI module. There might be a case where a handler for a msg id is not present in the handlers array which can lead to infinite loop while searching the handler and therefore out of bound access in qmi_invoke_handler(). Hence update the initialization in qmi_msg_handler data structure. Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 Signed-off-by: Rahul Bhattacharjee Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221021090126.28626-1-quic_rbhattac@quicinc.com --- drivers/net/wireless/ath/ath11k/qmi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c index 145f20a681bd..72cfc13ef6a8 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -3090,6 +3090,9 @@ static const struct qmi_msg_handler ath11k_qmi_msg_handlers[] = { sizeof(struct qmi_wlfw_fw_init_done_ind_msg_v01), .fn = ath11k_qmi_msg_fw_init_done_cb, }, + + /* end of list */ + {}, }; static int ath11k_qmi_ops_new_server(struct qmi_handle *qmi_hdl, From dd1c2322694522f674c874f5fa02ac5ae39135dd Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 31 Oct 2022 12:43:41 +0100 Subject: [PATCH 003/144] wifi: ath11k: synchronize ath11k_mac_he_gi_to_nl80211_he_gi()'s return type ath11k_mac_he_gi_to_nl80211_he_gi() generates a valid warning with gcc-13: drivers/net/wireless/ath/ath11k/mac.c:321:20: error: conflicting types for 'ath11k_mac_he_gi_to_nl80211_he_gi' due to enum/integer mismatch; have 'enum nl80211_he_gi(u8)' drivers/net/wireless/ath/ath11k/mac.h:166:5: note: previous declaration of 'ath11k_mac_he_gi_to_nl80211_he_gi' with type 'u32(u8)' I.e. the type of the return value ath11k_mac_he_gi_to_nl80211_he_gi() in the declaration is u32, while the definition spells enum nl80211_he_gi. Synchronize them to the latter. Cc: Martin Liska Cc: Kalle Valo Cc: "David S. Miller" Cc: Eric Dumazet Cc: Jakub Kicinski Cc: Paolo Abeni Cc: ath11k@lists.infradead.org Cc: linux-wireless@vger.kernel.org Cc: netdev@vger.kernel.org Signed-off-by: Jiri Slaby (SUSE) Reviewed-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221031114341.10377-1-jirislaby@kernel.org --- drivers/net/wireless/ath/ath11k/mac.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.h b/drivers/net/wireless/ath/ath11k/mac.h index 2a0d3afb0c99..0231783ad754 100644 --- a/drivers/net/wireless/ath/ath11k/mac.h +++ b/drivers/net/wireless/ath/ath11k/mac.h @@ -163,7 +163,7 @@ void ath11k_mac_drain_tx(struct ath11k *ar); void ath11k_mac_peer_cleanup_all(struct ath11k *ar); int ath11k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx); u8 ath11k_mac_bw_to_mac80211_bw(u8 bw); -u32 ath11k_mac_he_gi_to_nl80211_he_gi(u8 sgi); +enum nl80211_he_gi ath11k_mac_he_gi_to_nl80211_he_gi(u8 sgi); enum nl80211_he_ru_alloc ath11k_mac_phy_he_ru_to_nl80211_he_ru_alloc(u16 ru_phy); enum nl80211_he_ru_alloc ath11k_mac_he_ru_tones_to_nl80211_he_ru_alloc(u16 ru_tones); enum ath11k_supported_bw ath11k_mac_mac80211_bw_to_ath11k_bw(enum rate_info_bw bw); From 11e1fcf2b494da810d551553c46f739e36501adc Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Wed, 14 Sep 2022 17:26:12 -0700 Subject: [PATCH 004/144] wifi: ath10k: Make QMI message rules const Commit ff6d365898d4 ("soc: qcom: qmi: use const for struct qmi_elem_info") allows QMI message encoding/decoding rules to be const, so do that for ath10k. Compile tested only. Signed-off-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220915002612.13394-1-quic_jjohnson@quicinc.com --- drivers/net/wireless/ath/ath10k/qmi.c | 2 +- .../net/wireless/ath/ath10k/qmi_wlfw_v01.c | 126 +++++++++--------- .../net/wireless/ath/ath10k/qmi_wlfw_v01.h | 102 +++++++------- 3 files changed, 115 insertions(+), 115 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c index 66cb7a1e628a..8930a0f4c42f 100644 --- a/drivers/net/wireless/ath/ath10k/qmi.c +++ b/drivers/net/wireless/ath/ath10k/qmi.c @@ -618,7 +618,7 @@ static int ath10k_qmi_host_cap_send_sync(struct ath10k_qmi *qmi) { struct wlfw_host_cap_resp_msg_v01 resp = {}; struct wlfw_host_cap_req_msg_v01 req = {}; - struct qmi_elem_info *req_ei; + const struct qmi_elem_info *req_ei; struct ath10k *ar = qmi->ar; struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); struct qmi_txn txn; diff --git a/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.c b/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.c index 86fcf4e1de5f..1c81e454f943 100644 --- a/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.c +++ b/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.c @@ -7,7 +7,7 @@ #include #include "qmi_wlfw_v01.h" -static struct qmi_elem_info wlfw_ce_tgt_pipe_cfg_s_v01_ei[] = { +static const struct qmi_elem_info wlfw_ce_tgt_pipe_cfg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -56,7 +56,7 @@ static struct qmi_elem_info wlfw_ce_tgt_pipe_cfg_s_v01_ei[] = { {} }; -static struct qmi_elem_info wlfw_ce_svc_pipe_cfg_s_v01_ei[] = { +static const struct qmi_elem_info wlfw_ce_svc_pipe_cfg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -87,7 +87,7 @@ static struct qmi_elem_info wlfw_ce_svc_pipe_cfg_s_v01_ei[] = { {} }; -static struct qmi_elem_info wlfw_shadow_reg_cfg_s_v01_ei[] = { +static const struct qmi_elem_info wlfw_shadow_reg_cfg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_2_BYTE, .elem_len = 1, @@ -109,7 +109,7 @@ static struct qmi_elem_info wlfw_shadow_reg_cfg_s_v01_ei[] = { {} }; -static struct qmi_elem_info wlfw_shadow_reg_v2_cfg_s_v01_ei[] = { +static const struct qmi_elem_info wlfw_shadow_reg_v2_cfg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -122,7 +122,7 @@ static struct qmi_elem_info wlfw_shadow_reg_v2_cfg_s_v01_ei[] = { {} }; -static struct qmi_elem_info wlfw_memory_region_info_s_v01_ei[] = { +static const struct qmi_elem_info wlfw_memory_region_info_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_8_BYTE, .elem_len = 1, @@ -153,7 +153,7 @@ static struct qmi_elem_info wlfw_memory_region_info_s_v01_ei[] = { {} }; -static struct qmi_elem_info wlfw_mem_cfg_s_v01_ei[] = { +static const struct qmi_elem_info wlfw_mem_cfg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_8_BYTE, .elem_len = 1, @@ -184,7 +184,7 @@ static struct qmi_elem_info wlfw_mem_cfg_s_v01_ei[] = { {} }; -static struct qmi_elem_info wlfw_mem_seg_s_v01_ei[] = { +static const struct qmi_elem_info wlfw_mem_seg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -225,7 +225,7 @@ static struct qmi_elem_info wlfw_mem_seg_s_v01_ei[] = { {} }; -static struct qmi_elem_info wlfw_mem_seg_resp_s_v01_ei[] = { +static const struct qmi_elem_info wlfw_mem_seg_resp_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_8_BYTE, .elem_len = 1, @@ -256,7 +256,7 @@ static struct qmi_elem_info wlfw_mem_seg_resp_s_v01_ei[] = { {} }; -static struct qmi_elem_info wlfw_rf_chip_info_s_v01_ei[] = { +static const struct qmi_elem_info wlfw_rf_chip_info_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -278,7 +278,7 @@ static struct qmi_elem_info wlfw_rf_chip_info_s_v01_ei[] = { {} }; -static struct qmi_elem_info wlfw_rf_board_info_s_v01_ei[] = { +static const struct qmi_elem_info wlfw_rf_board_info_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -291,7 +291,7 @@ static struct qmi_elem_info wlfw_rf_board_info_s_v01_ei[] = { {} }; -static struct qmi_elem_info wlfw_soc_info_s_v01_ei[] = { +static const struct qmi_elem_info wlfw_soc_info_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -304,7 +304,7 @@ static struct qmi_elem_info wlfw_soc_info_s_v01_ei[] = { {} }; -static struct qmi_elem_info wlfw_fw_version_info_s_v01_ei[] = { +static const struct qmi_elem_info wlfw_fw_version_info_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -326,7 +326,7 @@ static struct qmi_elem_info wlfw_fw_version_info_s_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_ind_register_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_ind_register_req_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -528,7 +528,7 @@ struct qmi_elem_info wlfw_ind_register_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_ind_register_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_ind_register_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -560,15 +560,15 @@ struct qmi_elem_info wlfw_ind_register_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_fw_ready_ind_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_fw_ready_ind_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_msa_ready_ind_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_msa_ready_ind_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_pin_connect_result_ind_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_pin_connect_result_ind_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -626,7 +626,7 @@ struct qmi_elem_info wlfw_pin_connect_result_ind_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_wlan_mode_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_wlan_mode_req_msg_v01_ei[] = { { .data_type = QMI_SIGNED_4_BYTE_ENUM, .elem_len = 1, @@ -657,7 +657,7 @@ struct qmi_elem_info wlfw_wlan_mode_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_wlan_mode_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_wlan_mode_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -671,7 +671,7 @@ struct qmi_elem_info wlfw_wlan_mode_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_wlan_cfg_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_wlan_cfg_req_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -805,7 +805,7 @@ struct qmi_elem_info wlfw_wlan_cfg_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_wlan_cfg_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_wlan_cfg_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -819,11 +819,11 @@ struct qmi_elem_info wlfw_wlan_cfg_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_cap_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_cap_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_cap_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_cap_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -949,7 +949,7 @@ struct qmi_elem_info wlfw_cap_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_bdf_download_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_bdf_download_req_msg_v01_ei[] = { { .data_type = QMI_UNSIGNED_1_BYTE, .elem_len = 1, @@ -1079,7 +1079,7 @@ struct qmi_elem_info wlfw_bdf_download_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_bdf_download_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_bdf_download_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1093,7 +1093,7 @@ struct qmi_elem_info wlfw_bdf_download_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_cal_report_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_cal_report_req_msg_v01_ei[] = { { .data_type = QMI_DATA_LEN, .elem_len = 1, @@ -1133,7 +1133,7 @@ struct qmi_elem_info wlfw_cal_report_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_cal_report_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_cal_report_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1147,7 +1147,7 @@ struct qmi_elem_info wlfw_cal_report_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[] = { { .data_type = QMI_SIGNED_4_BYTE_ENUM, .elem_len = 1, @@ -1160,7 +1160,7 @@ struct qmi_elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_cal_download_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_cal_download_req_msg_v01_ei[] = { { .data_type = QMI_UNSIGNED_1_BYTE, .elem_len = 1, @@ -1272,7 +1272,7 @@ struct qmi_elem_info wlfw_cal_download_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_cal_download_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_cal_download_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1286,7 +1286,7 @@ struct qmi_elem_info wlfw_cal_download_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_initiate_cal_update_ind_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_initiate_cal_update_ind_msg_v01_ei[] = { { .data_type = QMI_SIGNED_4_BYTE_ENUM, .elem_len = 1, @@ -1308,7 +1308,7 @@ struct qmi_elem_info wlfw_initiate_cal_update_ind_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_cal_update_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_cal_update_req_msg_v01_ei[] = { { .data_type = QMI_SIGNED_4_BYTE_ENUM, .elem_len = 1, @@ -1330,7 +1330,7 @@ struct qmi_elem_info wlfw_cal_update_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_cal_update_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_cal_update_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1443,7 +1443,7 @@ struct qmi_elem_info wlfw_cal_update_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_msa_info_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_msa_info_req_msg_v01_ei[] = { { .data_type = QMI_UNSIGNED_8_BYTE, .elem_len = 1, @@ -1465,7 +1465,7 @@ struct qmi_elem_info wlfw_msa_info_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_msa_info_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_msa_info_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1498,11 +1498,11 @@ struct qmi_elem_info wlfw_msa_info_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_msa_ready_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_msa_ready_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_msa_ready_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_msa_ready_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1516,7 +1516,7 @@ struct qmi_elem_info wlfw_msa_ready_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_ini_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_ini_req_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -1538,7 +1538,7 @@ struct qmi_elem_info wlfw_ini_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_ini_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_ini_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1552,7 +1552,7 @@ struct qmi_elem_info wlfw_ini_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_athdiag_read_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_athdiag_read_req_msg_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -1583,7 +1583,7 @@ struct qmi_elem_info wlfw_athdiag_read_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_athdiag_read_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_athdiag_read_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1624,7 +1624,7 @@ struct qmi_elem_info wlfw_athdiag_read_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_athdiag_write_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_athdiag_write_req_msg_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -1664,7 +1664,7 @@ struct qmi_elem_info wlfw_athdiag_write_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_athdiag_write_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_athdiag_write_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1678,7 +1678,7 @@ struct qmi_elem_info wlfw_athdiag_write_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_vbatt_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_vbatt_req_msg_v01_ei[] = { { .data_type = QMI_UNSIGNED_8_BYTE, .elem_len = 1, @@ -1691,7 +1691,7 @@ struct qmi_elem_info wlfw_vbatt_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_vbatt_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_vbatt_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1705,7 +1705,7 @@ struct qmi_elem_info wlfw_vbatt_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_mac_addr_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_mac_addr_req_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -1727,7 +1727,7 @@ struct qmi_elem_info wlfw_mac_addr_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_mac_addr_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_mac_addr_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1741,7 +1741,7 @@ struct qmi_elem_info wlfw_mac_addr_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_host_cap_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_host_cap_req_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -1988,7 +1988,7 @@ struct qmi_elem_info wlfw_host_cap_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_host_cap_8bit_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_host_cap_8bit_req_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -2010,7 +2010,7 @@ struct qmi_elem_info wlfw_host_cap_8bit_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_host_cap_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_host_cap_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -2024,7 +2024,7 @@ struct qmi_elem_info wlfw_host_cap_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_request_mem_ind_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_request_mem_ind_msg_v01_ei[] = { { .data_type = QMI_DATA_LEN, .elem_len = 1, @@ -2047,7 +2047,7 @@ struct qmi_elem_info wlfw_request_mem_ind_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_respond_mem_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_respond_mem_req_msg_v01_ei[] = { { .data_type = QMI_DATA_LEN, .elem_len = 1, @@ -2070,7 +2070,7 @@ struct qmi_elem_info wlfw_respond_mem_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_respond_mem_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_respond_mem_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -2084,15 +2084,15 @@ struct qmi_elem_info wlfw_respond_mem_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_mem_ready_ind_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_mem_ready_ind_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_fw_init_done_ind_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_fw_init_done_ind_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_rejuvenate_ind_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_rejuvenate_ind_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -2168,11 +2168,11 @@ struct qmi_elem_info wlfw_rejuvenate_ind_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_rejuvenate_ack_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_rejuvenate_ack_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_rejuvenate_ack_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_rejuvenate_ack_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -2186,7 +2186,7 @@ struct qmi_elem_info wlfw_rejuvenate_ack_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_dynamic_feature_mask_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_dynamic_feature_mask_req_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -2208,7 +2208,7 @@ struct qmi_elem_info wlfw_dynamic_feature_mask_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -2258,7 +2258,7 @@ struct qmi_elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_m3_info_req_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_m3_info_req_msg_v01_ei[] = { { .data_type = QMI_UNSIGNED_8_BYTE, .elem_len = 1, @@ -2280,7 +2280,7 @@ struct qmi_elem_info wlfw_m3_info_req_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_m3_info_resp_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_m3_info_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -2294,7 +2294,7 @@ struct qmi_elem_info wlfw_m3_info_resp_msg_v01_ei[] = { {} }; -struct qmi_elem_info wlfw_xo_cal_ind_msg_v01_ei[] = { +const struct qmi_elem_info wlfw_xo_cal_ind_msg_v01_ei[] = { { .data_type = QMI_UNSIGNED_1_BYTE, .elem_len = 1, diff --git a/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.h b/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.h index 4d107e1364a8..f0db991408dc 100644 --- a/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.h +++ b/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.h @@ -215,7 +215,7 @@ struct wlfw_ind_register_req_msg_v01 { }; #define WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN 50 -extern struct qmi_elem_info wlfw_ind_register_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_ind_register_req_msg_v01_ei[]; struct wlfw_ind_register_resp_msg_v01 { struct qmi_response_type_v01 resp; @@ -224,21 +224,21 @@ struct wlfw_ind_register_resp_msg_v01 { }; #define WLFW_IND_REGISTER_RESP_MSG_V01_MAX_MSG_LEN 18 -extern struct qmi_elem_info wlfw_ind_register_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_ind_register_resp_msg_v01_ei[]; struct wlfw_fw_ready_ind_msg_v01 { char placeholder; }; #define WLFW_FW_READY_IND_MSG_V01_MAX_MSG_LEN 0 -extern struct qmi_elem_info wlfw_fw_ready_ind_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_fw_ready_ind_msg_v01_ei[]; struct wlfw_msa_ready_ind_msg_v01 { char placeholder; }; #define WLFW_MSA_READY_IND_MSG_V01_MAX_MSG_LEN 0 -extern struct qmi_elem_info wlfw_msa_ready_ind_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_msa_ready_ind_msg_v01_ei[]; struct wlfw_pin_connect_result_ind_msg_v01 { u8 pwr_pin_result_valid; @@ -250,7 +250,7 @@ struct wlfw_pin_connect_result_ind_msg_v01 { }; #define WLFW_PIN_CONNECT_RESULT_IND_MSG_V01_MAX_MSG_LEN 21 -extern struct qmi_elem_info wlfw_pin_connect_result_ind_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_pin_connect_result_ind_msg_v01_ei[]; struct wlfw_wlan_mode_req_msg_v01 { enum wlfw_driver_mode_enum_v01 mode; @@ -259,14 +259,14 @@ struct wlfw_wlan_mode_req_msg_v01 { }; #define WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN 11 -extern struct qmi_elem_info wlfw_wlan_mode_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_wlan_mode_req_msg_v01_ei[]; struct wlfw_wlan_mode_resp_msg_v01 { struct qmi_response_type_v01 resp; }; #define WLFW_WLAN_MODE_RESP_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_wlan_mode_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_wlan_mode_resp_msg_v01_ei[]; struct wlfw_wlan_cfg_req_msg_v01 { u8 host_version_valid; @@ -286,21 +286,21 @@ struct wlfw_wlan_cfg_req_msg_v01 { }; #define WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN 803 -extern struct qmi_elem_info wlfw_wlan_cfg_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_wlan_cfg_req_msg_v01_ei[]; struct wlfw_wlan_cfg_resp_msg_v01 { struct qmi_response_type_v01 resp; }; #define WLFW_WLAN_CFG_RESP_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_wlan_cfg_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_wlan_cfg_resp_msg_v01_ei[]; struct wlfw_cap_req_msg_v01 { char placeholder; }; #define WLFW_CAP_REQ_MSG_V01_MAX_MSG_LEN 0 -extern struct qmi_elem_info wlfw_cap_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_cap_req_msg_v01_ei[]; struct wlfw_cap_resp_msg_v01 { struct qmi_response_type_v01 resp; @@ -319,7 +319,7 @@ struct wlfw_cap_resp_msg_v01 { }; #define WLFW_CAP_RESP_MSG_V01_MAX_MSG_LEN 207 -extern struct qmi_elem_info wlfw_cap_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_cap_resp_msg_v01_ei[]; struct wlfw_bdf_download_req_msg_v01 { u8 valid; @@ -339,14 +339,14 @@ struct wlfw_bdf_download_req_msg_v01 { }; #define WLFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN 6182 -extern struct qmi_elem_info wlfw_bdf_download_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_bdf_download_req_msg_v01_ei[]; struct wlfw_bdf_download_resp_msg_v01 { struct qmi_response_type_v01 resp; }; #define WLFW_BDF_DOWNLOAD_RESP_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_bdf_download_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_bdf_download_resp_msg_v01_ei[]; struct wlfw_cal_report_req_msg_v01 { u32 meta_data_len; @@ -356,21 +356,21 @@ struct wlfw_cal_report_req_msg_v01 { }; #define WLFW_CAL_REPORT_REQ_MSG_V01_MAX_MSG_LEN 28 -extern struct qmi_elem_info wlfw_cal_report_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_cal_report_req_msg_v01_ei[]; struct wlfw_cal_report_resp_msg_v01 { struct qmi_response_type_v01 resp; }; #define WLFW_CAL_REPORT_RESP_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_cal_report_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_cal_report_resp_msg_v01_ei[]; struct wlfw_initiate_cal_download_ind_msg_v01 { enum wlfw_cal_temp_id_enum_v01 cal_id; }; #define WLFW_INITIATE_CAL_DOWNLOAD_IND_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[]; struct wlfw_cal_download_req_msg_v01 { u8 valid; @@ -388,14 +388,14 @@ struct wlfw_cal_download_req_msg_v01 { }; #define WLFW_CAL_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN 6178 -extern struct qmi_elem_info wlfw_cal_download_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_cal_download_req_msg_v01_ei[]; struct wlfw_cal_download_resp_msg_v01 { struct qmi_response_type_v01 resp; }; #define WLFW_CAL_DOWNLOAD_RESP_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_cal_download_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_cal_download_resp_msg_v01_ei[]; struct wlfw_initiate_cal_update_ind_msg_v01 { enum wlfw_cal_temp_id_enum_v01 cal_id; @@ -403,7 +403,7 @@ struct wlfw_initiate_cal_update_ind_msg_v01 { }; #define WLFW_INITIATE_CAL_UPDATE_IND_MSG_V01_MAX_MSG_LEN 14 -extern struct qmi_elem_info wlfw_initiate_cal_update_ind_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_initiate_cal_update_ind_msg_v01_ei[]; struct wlfw_cal_update_req_msg_v01 { enum wlfw_cal_temp_id_enum_v01 cal_id; @@ -411,7 +411,7 @@ struct wlfw_cal_update_req_msg_v01 { }; #define WLFW_CAL_UPDATE_REQ_MSG_V01_MAX_MSG_LEN 14 -extern struct qmi_elem_info wlfw_cal_update_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_cal_update_req_msg_v01_ei[]; struct wlfw_cal_update_resp_msg_v01 { struct qmi_response_type_v01 resp; @@ -429,7 +429,7 @@ struct wlfw_cal_update_resp_msg_v01 { }; #define WLFW_CAL_UPDATE_RESP_MSG_V01_MAX_MSG_LEN 6181 -extern struct qmi_elem_info wlfw_cal_update_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_cal_update_resp_msg_v01_ei[]; struct wlfw_msa_info_req_msg_v01 { u64 msa_addr; @@ -437,7 +437,7 @@ struct wlfw_msa_info_req_msg_v01 { }; #define WLFW_MSA_INFO_REQ_MSG_V01_MAX_MSG_LEN 18 -extern struct qmi_elem_info wlfw_msa_info_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_msa_info_req_msg_v01_ei[]; struct wlfw_msa_info_resp_msg_v01 { struct qmi_response_type_v01 resp; @@ -446,21 +446,21 @@ struct wlfw_msa_info_resp_msg_v01 { }; #define WLFW_MSA_INFO_RESP_MSG_V01_MAX_MSG_LEN 37 -extern struct qmi_elem_info wlfw_msa_info_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_msa_info_resp_msg_v01_ei[]; struct wlfw_msa_ready_req_msg_v01 { char placeholder; }; #define WLFW_MSA_READY_REQ_MSG_V01_MAX_MSG_LEN 0 -extern struct qmi_elem_info wlfw_msa_ready_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_msa_ready_req_msg_v01_ei[]; struct wlfw_msa_ready_resp_msg_v01 { struct qmi_response_type_v01 resp; }; #define WLFW_MSA_READY_RESP_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_msa_ready_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_msa_ready_resp_msg_v01_ei[]; struct wlfw_ini_req_msg_v01 { u8 enablefwlog_valid; @@ -468,14 +468,14 @@ struct wlfw_ini_req_msg_v01 { }; #define WLFW_INI_REQ_MSG_V01_MAX_MSG_LEN 4 -extern struct qmi_elem_info wlfw_ini_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_ini_req_msg_v01_ei[]; struct wlfw_ini_resp_msg_v01 { struct qmi_response_type_v01 resp; }; #define WLFW_INI_RESP_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_ini_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_ini_resp_msg_v01_ei[]; struct wlfw_athdiag_read_req_msg_v01 { u32 offset; @@ -484,7 +484,7 @@ struct wlfw_athdiag_read_req_msg_v01 { }; #define WLFW_ATHDIAG_READ_REQ_MSG_V01_MAX_MSG_LEN 21 -extern struct qmi_elem_info wlfw_athdiag_read_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_athdiag_read_req_msg_v01_ei[]; struct wlfw_athdiag_read_resp_msg_v01 { struct qmi_response_type_v01 resp; @@ -494,7 +494,7 @@ struct wlfw_athdiag_read_resp_msg_v01 { }; #define WLFW_ATHDIAG_READ_RESP_MSG_V01_MAX_MSG_LEN 6156 -extern struct qmi_elem_info wlfw_athdiag_read_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_athdiag_read_resp_msg_v01_ei[]; struct wlfw_athdiag_write_req_msg_v01 { u32 offset; @@ -504,28 +504,28 @@ struct wlfw_athdiag_write_req_msg_v01 { }; #define WLFW_ATHDIAG_WRITE_REQ_MSG_V01_MAX_MSG_LEN 6163 -extern struct qmi_elem_info wlfw_athdiag_write_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_athdiag_write_req_msg_v01_ei[]; struct wlfw_athdiag_write_resp_msg_v01 { struct qmi_response_type_v01 resp; }; #define WLFW_ATHDIAG_WRITE_RESP_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_athdiag_write_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_athdiag_write_resp_msg_v01_ei[]; struct wlfw_vbatt_req_msg_v01 { u64 voltage_uv; }; #define WLFW_VBATT_REQ_MSG_V01_MAX_MSG_LEN 11 -extern struct qmi_elem_info wlfw_vbatt_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_vbatt_req_msg_v01_ei[]; struct wlfw_vbatt_resp_msg_v01 { struct qmi_response_type_v01 resp; }; #define WLFW_VBATT_RESP_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_vbatt_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_vbatt_resp_msg_v01_ei[]; struct wlfw_mac_addr_req_msg_v01 { u8 mac_addr_valid; @@ -533,14 +533,14 @@ struct wlfw_mac_addr_req_msg_v01 { }; #define WLFW_MAC_ADDR_REQ_MSG_V01_MAX_MSG_LEN 9 -extern struct qmi_elem_info wlfw_mac_addr_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_mac_addr_req_msg_v01_ei[]; struct wlfw_mac_addr_resp_msg_v01 { struct qmi_response_type_v01 resp; }; #define WLFW_MAC_ADDR_RESP_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_mac_addr_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_mac_addr_resp_msg_v01_ei[]; #define QMI_WLFW_MAX_NUM_GPIO_V01 32 struct wlfw_host_cap_req_msg_v01 { @@ -574,15 +574,15 @@ struct wlfw_host_cap_req_msg_v01 { }; #define WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN 189 -extern struct qmi_elem_info wlfw_host_cap_req_msg_v01_ei[]; -extern struct qmi_elem_info wlfw_host_cap_8bit_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_host_cap_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_host_cap_8bit_req_msg_v01_ei[]; struct wlfw_host_cap_resp_msg_v01 { struct qmi_response_type_v01 resp; }; #define WLFW_HOST_CAP_RESP_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_host_cap_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_host_cap_resp_msg_v01_ei[]; struct wlfw_request_mem_ind_msg_v01 { u32 mem_seg_len; @@ -590,7 +590,7 @@ struct wlfw_request_mem_ind_msg_v01 { }; #define WLFW_REQUEST_MEM_IND_MSG_V01_MAX_MSG_LEN 564 -extern struct qmi_elem_info wlfw_request_mem_ind_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_request_mem_ind_msg_v01_ei[]; struct wlfw_respond_mem_req_msg_v01 { u32 mem_seg_len; @@ -598,28 +598,28 @@ struct wlfw_respond_mem_req_msg_v01 { }; #define WLFW_RESPOND_MEM_REQ_MSG_V01_MAX_MSG_LEN 260 -extern struct qmi_elem_info wlfw_respond_mem_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_respond_mem_req_msg_v01_ei[]; struct wlfw_respond_mem_resp_msg_v01 { struct qmi_response_type_v01 resp; }; #define WLFW_RESPOND_MEM_RESP_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_respond_mem_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_respond_mem_resp_msg_v01_ei[]; struct wlfw_mem_ready_ind_msg_v01 { char placeholder; }; #define WLFW_MEM_READY_IND_MSG_V01_MAX_MSG_LEN 0 -extern struct qmi_elem_info wlfw_mem_ready_ind_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_mem_ready_ind_msg_v01_ei[]; struct wlfw_fw_init_done_ind_msg_v01 { char placeholder; }; #define WLFW_FW_INIT_DONE_IND_MSG_V01_MAX_MSG_LEN 0 -extern struct qmi_elem_info wlfw_fw_init_done_ind_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_fw_init_done_ind_msg_v01_ei[]; struct wlfw_rejuvenate_ind_msg_v01 { u8 cause_for_rejuvenation_valid; @@ -633,21 +633,21 @@ struct wlfw_rejuvenate_ind_msg_v01 { }; #define WLFW_REJUVENATE_IND_MSG_V01_MAX_MSG_LEN 144 -extern struct qmi_elem_info wlfw_rejuvenate_ind_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_rejuvenate_ind_msg_v01_ei[]; struct wlfw_rejuvenate_ack_req_msg_v01 { char placeholder; }; #define WLFW_REJUVENATE_ACK_REQ_MSG_V01_MAX_MSG_LEN 0 -extern struct qmi_elem_info wlfw_rejuvenate_ack_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_rejuvenate_ack_req_msg_v01_ei[]; struct wlfw_rejuvenate_ack_resp_msg_v01 { struct qmi_response_type_v01 resp; }; #define WLFW_REJUVENATE_ACK_RESP_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_rejuvenate_ack_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_rejuvenate_ack_resp_msg_v01_ei[]; struct wlfw_dynamic_feature_mask_req_msg_v01 { u8 mask_valid; @@ -655,7 +655,7 @@ struct wlfw_dynamic_feature_mask_req_msg_v01 { }; #define WLFW_DYNAMIC_FEATURE_MASK_REQ_MSG_V01_MAX_MSG_LEN 11 -extern struct qmi_elem_info wlfw_dynamic_feature_mask_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_dynamic_feature_mask_req_msg_v01_ei[]; struct wlfw_dynamic_feature_mask_resp_msg_v01 { struct qmi_response_type_v01 resp; @@ -666,7 +666,7 @@ struct wlfw_dynamic_feature_mask_resp_msg_v01 { }; #define WLFW_DYNAMIC_FEATURE_MASK_RESP_MSG_V01_MAX_MSG_LEN 29 -extern struct qmi_elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[]; struct wlfw_m3_info_req_msg_v01 { u64 addr; @@ -674,20 +674,20 @@ struct wlfw_m3_info_req_msg_v01 { }; #define WLFW_M3_INFO_REQ_MSG_V01_MAX_MSG_LEN 18 -extern struct qmi_elem_info wlfw_m3_info_req_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_m3_info_req_msg_v01_ei[]; struct wlfw_m3_info_resp_msg_v01 { struct qmi_response_type_v01 resp; }; #define WLFW_M3_INFO_RESP_MSG_V01_MAX_MSG_LEN 7 -extern struct qmi_elem_info wlfw_m3_info_resp_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_m3_info_resp_msg_v01_ei[]; struct wlfw_xo_cal_ind_msg_v01 { u8 xo_cal_data; }; #define WLFW_XO_CAL_IND_MSG_V01_MAX_MSG_LEN 4 -extern struct qmi_elem_info wlfw_xo_cal_ind_msg_v01_ei[]; +extern const struct qmi_elem_info wlfw_xo_cal_ind_msg_v01_ei[]; #endif From 93c1592889fca46d09d833455628bab05516cdbf Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Wed, 14 Sep 2022 17:23:03 -0700 Subject: [PATCH 005/144] wifi: ath11k: Make QMI message rules const Commit ff6d365898d4 ("soc: qcom: qmi: use const for struct qmi_elem_info") allows QMI message encoding/decoding rules to be const, so do that for ath11k. Compile tested only. Signed-off-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20220915002303.12206-1-quic_jjohnson@quicinc.com --- drivers/net/wireless/ath/ath11k/qmi.c | 72 +++++++++++++-------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c index 72cfc13ef6a8..ab923e24b0a9 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -29,7 +29,7 @@ module_param_named(cold_boot_cal, ath11k_cold_boot_cal, bool, 0644); MODULE_PARM_DESC(cold_boot_cal, "Decrease the channel switch time but increase the driver load time (Default: true)"); -static struct qmi_elem_info qmi_wlanfw_host_cap_req_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_host_cap_req_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -280,7 +280,7 @@ static struct qmi_elem_info qmi_wlanfw_host_cap_req_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_host_cap_resp_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_host_cap_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -297,7 +297,7 @@ static struct qmi_elem_info qmi_wlanfw_host_cap_resp_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_ind_register_req_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_ind_register_req_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -522,7 +522,7 @@ static struct qmi_elem_info qmi_wlanfw_ind_register_req_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_ind_register_resp_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_ind_register_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -558,7 +558,7 @@ static struct qmi_elem_info qmi_wlanfw_ind_register_resp_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_mem_cfg_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_mem_cfg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_8_BYTE, .elem_len = 1, @@ -590,7 +590,7 @@ static struct qmi_elem_info qmi_wlanfw_mem_cfg_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_mem_seg_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_mem_seg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -632,7 +632,7 @@ static struct qmi_elem_info qmi_wlanfw_mem_seg_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_request_mem_ind_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_request_mem_ind_msg_v01_ei[] = { { .data_type = QMI_DATA_LEN, .elem_len = 1, @@ -659,7 +659,7 @@ static struct qmi_elem_info qmi_wlanfw_request_mem_ind_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_mem_seg_resp_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_mem_seg_resp_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_8_BYTE, .elem_len = 1, @@ -699,7 +699,7 @@ static struct qmi_elem_info qmi_wlanfw_mem_seg_resp_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_respond_mem_req_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_respond_mem_req_msg_v01_ei[] = { { .data_type = QMI_DATA_LEN, .elem_len = 1, @@ -726,7 +726,7 @@ static struct qmi_elem_info qmi_wlanfw_respond_mem_req_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_respond_mem_resp_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_respond_mem_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -744,7 +744,7 @@ static struct qmi_elem_info qmi_wlanfw_respond_mem_resp_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_cap_req_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_cap_req_msg_v01_ei[] = { { .data_type = QMI_EOTI, .array_type = NO_ARRAY, @@ -752,7 +752,7 @@ static struct qmi_elem_info qmi_wlanfw_cap_req_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_device_info_req_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_device_info_req_msg_v01_ei[] = { { .data_type = QMI_EOTI, .array_type = NO_ARRAY, @@ -760,7 +760,7 @@ static struct qmi_elem_info qmi_wlanfw_device_info_req_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlfw_device_info_resp_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlfw_device_info_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -814,7 +814,7 @@ static struct qmi_elem_info qmi_wlfw_device_info_resp_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_rf_chip_info_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_rf_chip_info_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -840,7 +840,7 @@ static struct qmi_elem_info qmi_wlanfw_rf_chip_info_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_rf_board_info_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_rf_board_info_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -857,7 +857,7 @@ static struct qmi_elem_info qmi_wlanfw_rf_board_info_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_soc_info_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_soc_info_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -873,7 +873,7 @@ static struct qmi_elem_info qmi_wlanfw_soc_info_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_fw_version_info_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_fw_version_info_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -899,7 +899,7 @@ static struct qmi_elem_info qmi_wlanfw_fw_version_info_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_cap_resp_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_cap_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1100,7 +1100,7 @@ static struct qmi_elem_info qmi_wlanfw_cap_resp_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_bdf_download_req_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_bdf_download_req_msg_v01_ei[] = { { .data_type = QMI_UNSIGNED_1_BYTE, .elem_len = 1, @@ -1235,7 +1235,7 @@ static struct qmi_elem_info qmi_wlanfw_bdf_download_req_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_bdf_download_resp_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_bdf_download_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1253,7 +1253,7 @@ static struct qmi_elem_info qmi_wlanfw_bdf_download_resp_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_m3_info_req_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_m3_info_req_msg_v01_ei[] = { { .data_type = QMI_UNSIGNED_8_BYTE, .elem_len = 1, @@ -1277,7 +1277,7 @@ static struct qmi_elem_info qmi_wlanfw_m3_info_req_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_m3_info_resp_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_m3_info_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1294,7 +1294,7 @@ static struct qmi_elem_info qmi_wlanfw_m3_info_resp_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_ce_tgt_pipe_cfg_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_ce_tgt_pipe_cfg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -1347,7 +1347,7 @@ static struct qmi_elem_info qmi_wlanfw_ce_tgt_pipe_cfg_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_ce_svc_pipe_cfg_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_ce_svc_pipe_cfg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -1382,7 +1382,7 @@ static struct qmi_elem_info qmi_wlanfw_ce_svc_pipe_cfg_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_shadow_reg_cfg_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_shadow_reg_cfg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_2_BYTE, .elem_len = 1, @@ -1406,7 +1406,7 @@ static struct qmi_elem_info qmi_wlanfw_shadow_reg_cfg_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_shadow_reg_v2_cfg_s_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_shadow_reg_v2_cfg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -1423,7 +1423,7 @@ static struct qmi_elem_info qmi_wlanfw_shadow_reg_v2_cfg_s_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_wlan_mode_req_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_wlan_mode_req_msg_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, .elem_len = 1, @@ -1458,7 +1458,7 @@ static struct qmi_elem_info qmi_wlanfw_wlan_mode_req_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_wlan_mode_resp_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_wlan_mode_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1476,7 +1476,7 @@ static struct qmi_elem_info qmi_wlanfw_wlan_mode_resp_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_wlan_cfg_req_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_wlan_cfg_req_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -1615,7 +1615,7 @@ static struct qmi_elem_info qmi_wlanfw_wlan_cfg_req_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_wlan_cfg_resp_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_wlan_cfg_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1632,28 +1632,28 @@ static struct qmi_elem_info qmi_wlanfw_wlan_cfg_resp_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_mem_ready_ind_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_mem_ready_ind_msg_v01_ei[] = { { .data_type = QMI_EOTI, .array_type = NO_ARRAY, }, }; -static struct qmi_elem_info qmi_wlanfw_fw_ready_ind_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_fw_ready_ind_msg_v01_ei[] = { { .data_type = QMI_EOTI, .array_type = NO_ARRAY, }, }; -static struct qmi_elem_info qmi_wlanfw_cold_boot_cal_done_ind_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_cold_boot_cal_done_ind_msg_v01_ei[] = { { .data_type = QMI_EOTI, .array_type = NO_ARRAY, }, }; -static struct qmi_elem_info qmi_wlanfw_wlan_ini_req_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_wlan_ini_req_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, .elem_len = 1, @@ -1679,7 +1679,7 @@ static struct qmi_elem_info qmi_wlanfw_wlan_ini_req_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlanfw_wlan_ini_resp_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlanfw_wlan_ini_resp_msg_v01_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, @@ -1697,7 +1697,7 @@ static struct qmi_elem_info qmi_wlanfw_wlan_ini_resp_msg_v01_ei[] = { }, }; -static struct qmi_elem_info qmi_wlfw_fw_init_done_ind_msg_v01_ei[] = { +static const struct qmi_elem_info qmi_wlfw_fw_init_done_ind_msg_v01_ei[] = { { .data_type = QMI_EOTI, .array_type = NO_ARRAY, From a018750a2cceaf4427c4ee3d9ce3e83a171d5bd6 Mon Sep 17 00:00:00 2001 From: Youghandhar Chintala Date: Fri, 4 Nov 2022 14:24:03 +0530 Subject: [PATCH 006/144] wifi: ath11k: Trigger sta disconnect on hardware restart Currently after the hardware restart triggered from the driver, the station interface connection remains intact, since a disconnect trigger is not sent to userspace. This can lead to a problem in targets where the wifi mac sequence is added by the firmware. After the target restart, its wifi mac sequence number gets reset to zero. Hence AP to which our device is connected will receive frames with a wifi mac sequence number jump to the past, thereby resulting in the AP dropping all these frames, until the frame arrives with a wifi mac sequence number which AP was expecting. To avoid such frame drops, its better to trigger a station disconnect upon target hardware restart which can be done with API ieee80211_reconfig_disconnect exposed to mac80211. The other targets are not affected by this change, since the hardware params flag is not set. Reported-by: kernel test robot Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-00887-QCAMSLSWPLZ-1 Signed-off-by: Youghandhar Chintala Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221104085403.11025-1-quic_youghand@quicinc.com --- drivers/net/wireless/ath/ath11k/core.c | 6 ++++++ drivers/net/wireless/ath/ath11k/hw.h | 1 + drivers/net/wireless/ath/ath11k/mac.c | 7 +++++++ 3 files changed, 14 insertions(+) diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index 0ab2f190f1ec..edf78df9b12f 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -195,6 +195,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .tcl_ring_retry = true, .tx_ring_size = DP_TCL_DATA_RING_SIZE, .smp2p_wow_exit = false, + .support_fw_mac_sequence = false, }, { .name = "qca6390 hw2.0", @@ -277,6 +278,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .tcl_ring_retry = true, .tx_ring_size = DP_TCL_DATA_RING_SIZE, .smp2p_wow_exit = false, + .support_fw_mac_sequence = true, }, { .name = "qcn9074 hw1.0", @@ -356,6 +358,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .tcl_ring_retry = true, .tx_ring_size = DP_TCL_DATA_RING_SIZE, .smp2p_wow_exit = false, + .support_fw_mac_sequence = false, }, { .name = "wcn6855 hw2.0", @@ -438,6 +441,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .tcl_ring_retry = true, .tx_ring_size = DP_TCL_DATA_RING_SIZE, .smp2p_wow_exit = false, + .support_fw_mac_sequence = true, }, { .name = "wcn6855 hw2.1", @@ -519,6 +523,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .tcl_ring_retry = true, .tx_ring_size = DP_TCL_DATA_RING_SIZE, .smp2p_wow_exit = false, + .support_fw_mac_sequence = true, }, { .name = "wcn6750 hw1.0", @@ -597,6 +602,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .tcl_ring_retry = false, .tx_ring_size = DP_TCL_DATA_RING_SIZE_WCN6750, .smp2p_wow_exit = true, + .support_fw_mac_sequence = true, }, }; diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h index 8a3f24862edc..0c5ef8a526d8 100644 --- a/drivers/net/wireless/ath/ath11k/hw.h +++ b/drivers/net/wireless/ath/ath11k/hw.h @@ -219,6 +219,7 @@ struct ath11k_hw_params { bool tcl_ring_retry; u32 tx_ring_size; bool smp2p_wow_exit; + bool support_fw_mac_sequence; }; struct ath11k_hw_ops { diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 6426a38f8961..9e923ecb0891 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -8010,6 +8010,7 @@ ath11k_mac_op_reconfig_complete(struct ieee80211_hw *hw, struct ath11k *ar = hw->priv; struct ath11k_base *ab = ar->ab; int recovery_count; + struct ath11k_vif *arvif; if (reconfig_type != IEEE80211_RECONFIG_TYPE_RESTART) return; @@ -8045,6 +8046,12 @@ ath11k_mac_op_reconfig_complete(struct ieee80211_hw *hw, ath11k_dbg(ab, ATH11K_DBG_BOOT, "reset success\n"); } } + if (ar->ab->hw_params.support_fw_mac_sequence) { + list_for_each_entry(arvif, &ar->arvifs, list) { + if (arvif->is_up && arvif->vdev_type == WMI_VDEV_TYPE_STA) + ieee80211_hw_restart_disconnect(arvif->vif); + } + } } mutex_unlock(&ar->conf_mutex); From dc45398446be10c35e4272537605b75f87c5037d Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 4 Nov 2022 14:07:23 +0000 Subject: [PATCH 007/144] wifi: ath9k: Remove unused variable mismatch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Variable mismatch is just being incremented and it's never used anywhere else. The variable and the increment are redundant so remove it. Signed-off-by: Colin Ian King Acked-by: Toke Høiland-Jørgensen Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221104140723.226857-1-colin.i.king@gmail.com --- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index 9899661f9a60..8d7efd80d97a 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -585,7 +585,7 @@ static u32 ar9003_mci_wait_for_gpm(struct ath_hw *ah, u8 gpm_type, { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; - u32 *p_gpm = NULL, mismatch = 0, more_data; + u32 *p_gpm = NULL, more_data; u32 offset; u8 recv_type = 0, recv_opcode = 0; bool b_is_bt_cal_done = (gpm_type == MCI_GPM_BT_CAL_DONE); @@ -656,7 +656,6 @@ static u32 ar9003_mci_wait_for_gpm(struct ath_hw *ah, u8 gpm_type, } else { ath_dbg(common, MCI, "MCI GPM subtype not match 0x%x\n", *(p_gpm + 1)); - mismatch++; ar9003_mci_process_gpm_extra(ah, recv_type, recv_opcode, p_gpm); } From a60c0401729851bfe637c2c8af5f759ea985336f Mon Sep 17 00:00:00 2001 From: Zhi-Jun You Date: Mon, 7 Nov 2022 00:22:27 +0800 Subject: [PATCH 008/144] wifi: ath10k: Use IEEE80211_SEQ_TO_SN() for seq_ctrl conversion Use IEEE80211_SEQ_TO_SN() macro to convert seq_ctrl to sn for better readability. Signed-off-by: Zhi-Jun You Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221106162227.1119-1-hujy652@gmail.com --- drivers/net/wireless/ath/ath10k/htt_rx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index e76aab973320..3df52906b356 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -1379,7 +1379,7 @@ static void ath10k_process_rx(struct ath10k *ar, struct sk_buff *skb) ath10k_get_tid(hdr, tid, sizeof(tid)), is_multicast_ether_addr(ieee80211_get_DA(hdr)) ? "mcast" : "ucast", - (__le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4, + IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl)), (status->encoding == RX_ENC_LEGACY) ? "legacy" : "", (status->encoding == RX_ENC_HT) ? "ht" : "", (status->encoding == RX_ENC_VHT) ? "vht" : "", @@ -1902,7 +1902,7 @@ static bool ath10k_htt_rx_h_frag_pn_check(struct ath10k *ar, last_pn = &peer->frag_tids_last_pn[tid]; new_pn.pn48 = ath10k_htt_rx_h_get_pn(ar, skb, offset, enctype); frag_number = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG; - seq = (__le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; + seq = IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl)); if (frag_number == 0) { last_pn->pn48 = new_pn.pn48; @@ -2824,7 +2824,7 @@ static bool ath10k_htt_rx_proc_rx_frag_ind_hl(struct ath10k_htt *htt, hdr_space = ieee80211_hdrlen(hdr->frame_control); sc = __le16_to_cpu(hdr->seq_ctrl); - seq = (sc & IEEE80211_SCTL_SEQ) >> 4; + seq = IEEE80211_SEQ_TO_SN(sc); frag = sc & IEEE80211_SCTL_FRAG; sec_index = MS(rx_desc_info, HTT_RX_DESC_HL_INFO_MCAST_BCAST) ? From 2af7749047d8d6ad43feff69f555a13a6a6c2831 Mon Sep 17 00:00:00 2001 From: Xiu Jianfeng Date: Thu, 10 Nov 2022 14:19:26 +0800 Subject: [PATCH 009/144] wifi: ath10k: Fix return value in ath10k_pci_init() This driver is attempting to register to support two different buses. if either of these is successful then ath10k_pci_init() should return 0 so that hardware attached to the successful bus can be probed and supported. only if both of these are unsuccessful should ath10k_pci_init() return an errno. Fixes: 0b523ced9a3c ("ath10k: add basic skeleton to support ahb") Signed-off-by: Xiu Jianfeng Reviewed-by: Jeff Johnson Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221110061926.18163-1-xiujianfeng@huawei.com --- drivers/net/wireless/ath/ath10k/pci.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index e56c6a6b1379..728d607289c3 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -3792,18 +3792,22 @@ static struct pci_driver ath10k_pci_driver = { static int __init ath10k_pci_init(void) { - int ret; + int ret1, ret2; - ret = pci_register_driver(&ath10k_pci_driver); - if (ret) + ret1 = pci_register_driver(&ath10k_pci_driver); + if (ret1) printk(KERN_ERR "failed to register ath10k pci driver: %d\n", - ret); + ret1); - ret = ath10k_ahb_init(); - if (ret) - printk(KERN_ERR "ahb init failed: %d\n", ret); + ret2 = ath10k_ahb_init(); + if (ret2) + printk(KERN_ERR "ahb init failed: %d\n", ret2); - return ret; + if (ret1 && ret2) + return ret1; + + /* registered to at least one bus */ + return 0; } module_init(ath10k_pci_init); From d9e38350a7b10f2e1006cd85a664b8698d027e7c Mon Sep 17 00:00:00 2001 From: Zhi-Jun You Date: Fri, 11 Nov 2022 00:40:25 +0800 Subject: [PATCH 010/144] wifi: ath10k: Remove redundant argument offset This argument of ath10k_htt_rx_h_frag_pn_check() is always set to 0 so drop this. Signed-off-by: Zhi-Jun You Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221110164025.14418-1-hujy652@gmail.com --- drivers/net/wireless/ath/ath10k/htt_rx.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 3df52906b356..438b0caaceb7 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -1844,15 +1844,14 @@ static void ath10k_htt_rx_h_csum_offload(struct ath10k_hw_params *hw, } static u64 ath10k_htt_rx_h_get_pn(struct ath10k *ar, struct sk_buff *skb, - u16 offset, enum htt_rx_mpdu_encrypt_type enctype) { struct ieee80211_hdr *hdr; u64 pn = 0; u8 *ehdr; - hdr = (struct ieee80211_hdr *)(skb->data + offset); - ehdr = skb->data + offset + ieee80211_hdrlen(hdr->frame_control); + hdr = (struct ieee80211_hdr *)skb->data; + ehdr = skb->data + ieee80211_hdrlen(hdr->frame_control); if (enctype == HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2) { pn = ehdr[0]; @@ -1866,19 +1865,17 @@ static u64 ath10k_htt_rx_h_get_pn(struct ath10k *ar, struct sk_buff *skb, } static bool ath10k_htt_rx_h_frag_multicast_check(struct ath10k *ar, - struct sk_buff *skb, - u16 offset) + struct sk_buff *skb) { struct ieee80211_hdr *hdr; - hdr = (struct ieee80211_hdr *)(skb->data + offset); + hdr = (struct ieee80211_hdr *)skb->data; return !is_multicast_ether_addr(hdr->addr1); } static bool ath10k_htt_rx_h_frag_pn_check(struct ath10k *ar, struct sk_buff *skb, u16 peer_id, - u16 offset, enum htt_rx_mpdu_encrypt_type enctype) { struct ath10k_peer *peer; @@ -1893,14 +1890,14 @@ static bool ath10k_htt_rx_h_frag_pn_check(struct ath10k *ar, return false; } - hdr = (struct ieee80211_hdr *)(skb->data + offset); + hdr = (struct ieee80211_hdr *)skb->data; if (ieee80211_is_data_qos(hdr->frame_control)) tid = ieee80211_get_tid(hdr); else tid = ATH10K_TXRX_NON_QOS_TID; last_pn = &peer->frag_tids_last_pn[tid]; - new_pn.pn48 = ath10k_htt_rx_h_get_pn(ar, skb, offset, enctype); + new_pn.pn48 = ath10k_htt_rx_h_get_pn(ar, skb, enctype); frag_number = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG; seq = IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl)); @@ -2059,13 +2056,11 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar, frag_pn_check = ath10k_htt_rx_h_frag_pn_check(ar, msdu, peer_id, - 0, enctype); if (frag) multicast_check = ath10k_htt_rx_h_frag_multicast_check(ar, - msdu, - 0); + msdu); if (!frag_pn_check || !multicast_check) { /* Discard the fragment with invalid PN or multicast DA From 61b0853d0314a474f9135537ebf96ef85a7216df Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 14 Nov 2022 17:01:11 -0600 Subject: [PATCH 011/144] wifi: brcmfmac: Replace one-element array with flexible-array member One-element arrays are deprecated, and we are replacing them with flexible array members instead. So, replace one-element array with flexible-array member in struct brcmf_gscan_config. Important to mention is that doing a build before/after this patch results in no binary output differences. This helps with the ongoing efforts to tighten the FORTIFY_SOURCE routines on memcpy() and help us make progress towards globally enabling -fstrict-flex-arrays=3 [1]. Link: https://github.com/KSPP/linux/issues/79 Link: https://github.com/KSPP/linux/issues/241 Link: https://gcc.gnu.org/pipermail/gcc-patches/2022-October/602902.html [1] Signed-off-by: Gustavo A. R. Silva Reviewed-by: Kees Cook Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/7694550aa9a2753a73a687f61af9441c8cf52fd7.1668466470.git.gustavoars@kernel.org --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h | 2 +- drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h index f518e025d6e4..62f65dc6a3b9 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -1049,7 +1049,7 @@ struct brcmf_gscan_config { u8 count_of_channel_buckets; u8 retry_threshold; __le16 lost_ap_window; - struct brcmf_gscan_bucket_config bucket[1]; + struct brcmf_gscan_bucket_config bucket[]; }; /** diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c index 170c61c8136c..7c5da506637f 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c @@ -405,7 +405,7 @@ static int brcmf_pno_config_sched_scans(struct brcmf_if *ifp) if (n_buckets < 0) return n_buckets; - gsz = sizeof(*gscan_cfg) + (n_buckets - 1) * sizeof(*buckets); + gsz = sizeof(*gscan_cfg) + n_buckets * sizeof(*buckets); gscan_cfg = kzalloc(gsz, GFP_KERNEL); if (!gscan_cfg) { err = -ENOMEM; From f0e0897b4c7eb590581463125c553bd6bb029808 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 14 Nov 2022 17:02:06 -0600 Subject: [PATCH 012/144] wifi: brcmfmac: Use struct_size() and array_size() in code ralated to struct brcmf_gscan_config Prefer struct_size() over open-coded versions of idiom: sizeof(struct-with-flex-array) + sizeof(typeof-flex-array-elements) * count where count is the max number of items the flexible array is supposed to contain. Also, use array_size() in call to memcpy(). Link: https://github.com/KSPP/linux/issues/160 Signed-off-by: Gustavo A. R. Silva Reviewed-by: Kees Cook Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/de0226a549c8d000d8974e207ede786220a3df1a.1668466470.git.gustavoars@kernel.org --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c index 7c5da506637f..05f66ab13bed 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c @@ -405,7 +405,7 @@ static int brcmf_pno_config_sched_scans(struct brcmf_if *ifp) if (n_buckets < 0) return n_buckets; - gsz = sizeof(*gscan_cfg) + n_buckets * sizeof(*buckets); + gsz = struct_size(gscan_cfg, bucket, n_buckets); gscan_cfg = kzalloc(gsz, GFP_KERNEL); if (!gscan_cfg) { err = -ENOMEM; @@ -434,8 +434,8 @@ static int brcmf_pno_config_sched_scans(struct brcmf_if *ifp) gscan_cfg->flags = BRCMF_GSCAN_CFG_ALL_BUCKETS_IN_1ST_SCAN; gscan_cfg->count_of_channel_buckets = n_buckets; - memcpy(&gscan_cfg->bucket[0], buckets, - n_buckets * sizeof(*buckets)); + memcpy(gscan_cfg->bucket, buckets, + array_size(n_buckets, sizeof(*buckets))); err = brcmf_fil_iovar_data_set(ifp, "pfn_gscan_cfg", gscan_cfg, gsz); From 0001650b3d89683bbea0cc3262c2a509dac9e78e Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 15 Nov 2022 15:53:27 -0600 Subject: [PATCH 013/144] wifi: brcmfmac: replace one-element array with flexible-array member in struct brcmf_dload_data_le One-element arrays are deprecated, and we are replacing them with flexible array members instead. So, replace one-element array with flexible-array member in struct brcmf_dload_data_le. Important to mention is that doing a build before/after this patch results in no binary output differences. This helps with the ongoing efforts to tighten the FORTIFY_SOURCE routines on memcpy() and help us make progress towards globally enabling -fstrict-flex-arrays=3 [1]. Link: https://github.com/KSPP/linux/issues/230 Link: https://github.com/KSPP/linux/issues/79 Link: https://gcc.gnu.org/pipermail/gcc-patches/2022-October/602902.html [1] Signed-off-by: Gustavo A. R. Silva Reviewed-by: Kees Cook Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/905f5b68cf93c812360d081caae5b15221db09b6.1668548907.git.gustavoars@kernel.org --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c | 4 ++-- drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c index 22344e68fd59..2e836566e218 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c @@ -110,7 +110,7 @@ static int brcmf_c_download(struct brcmf_if *ifp, u16 flag, dload_buf->dload_type = cpu_to_le16(DL_TYPE_CLM); dload_buf->len = cpu_to_le32(len); dload_buf->crc = cpu_to_le32(0); - len = sizeof(*dload_buf) + len - 1; + len = sizeof(*dload_buf) + len; err = brcmf_fil_iovar_data_set(ifp, "clmload", dload_buf, len); @@ -139,7 +139,7 @@ static int brcmf_c_process_clm_blob(struct brcmf_if *ifp) return 0; } - chunk_buf = kzalloc(sizeof(*chunk_buf) + MAX_CHUNK_LEN - 1, GFP_KERNEL); + chunk_buf = kzalloc(sizeof(*chunk_buf) + MAX_CHUNK_LEN, GFP_KERNEL); if (!chunk_buf) { err = -ENOMEM; goto done; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h index 62f65dc6a3b9..04e1beedfd81 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -943,7 +943,7 @@ struct brcmf_dload_data_le { __le16 dload_type; __le32 len; __le32 crc; - u8 data[1]; + u8 data[]; }; /** From 633a9b6f514c12b3ee42b3a4e647f137aca1e198 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 15 Nov 2022 15:55:34 -0600 Subject: [PATCH 014/144] wifi: brcmfmac: Use struct_size() in code ralated to struct brcmf_dload_data_le Prefer struct_size() over open-coded versions of idiom: sizeof(struct-with-flex-array) + sizeof(typeof-flex-array-elements) * count where count is the max number of items the flexible array is supposed to contain. In this particular case, in the open-coded version sizeof(typeof-flex-array-elements) is implicit in _count_ because the type of the flex array data is u8: drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h:941: 941 struct brcmf_dload_data_le { 942 __le16 flag; 943 __le16 dload_type; 944 __le32 len; 945 __le32 crc; 946 u8 data[]; 947 }; Link: https://github.com/KSPP/linux/issues/160 Signed-off-by: Gustavo A. R. Silva Reviewed-by: Kees Cook Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/41845ad3660ed4375f0c03fd36a67b2e12fafed5.1668548907.git.gustavoars@kernel.org --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c index 2e836566e218..4a309e5a5707 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c @@ -110,9 +110,9 @@ static int brcmf_c_download(struct brcmf_if *ifp, u16 flag, dload_buf->dload_type = cpu_to_le16(DL_TYPE_CLM); dload_buf->len = cpu_to_le32(len); dload_buf->crc = cpu_to_le32(0); - len = sizeof(*dload_buf) + len; - err = brcmf_fil_iovar_data_set(ifp, "clmload", dload_buf, len); + err = brcmf_fil_iovar_data_set(ifp, "clmload", dload_buf, + struct_size(dload_buf, data, len)); return err; } @@ -139,7 +139,8 @@ static int brcmf_c_process_clm_blob(struct brcmf_if *ifp) return 0; } - chunk_buf = kzalloc(sizeof(*chunk_buf) + MAX_CHUNK_LEN, GFP_KERNEL); + chunk_buf = kzalloc(struct_size(chunk_buf, data, MAX_CHUNK_LEN), + GFP_KERNEL); if (!chunk_buf) { err = -ENOMEM; goto done; From 79ca91a3c1f1e5d871f393791e7538f9386a7711 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Thu, 17 Nov 2022 14:18:32 +0800 Subject: [PATCH 015/144] wifi: rtw89: 8852b: correct TX power controlled by BT-coexistence When coexistence mechanism is under free-run mode, it could adjust WiFi and BT TX power to avoid interference with each other. For other cases, it should keep original TX power from regular predefined tables, so set correct values to 255 for these cases. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221117061832.42057-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8852b.c | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 0df044b1c392..85dfc1ebb0d9 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -318,27 +318,27 @@ static const struct rtw89_dig_regs rtw8852b_dig_regs = { }; static const struct rtw89_btc_rf_trx_para rtw89_btc_8852b_rf_ul[] = { - {15, 0, 0, 7}, /* 0 -> original */ - {15, 2, 0, 7}, /* 1 -> for BT-connected ACI issue && BTG co-rx */ - {15, 0, 0, 7}, /* 2 ->reserved for shared-antenna */ - {15, 0, 0, 7}, /* 3- >reserved for shared-antenna */ - {15, 0, 0, 7}, /* 4 ->reserved for shared-antenna */ - {15, 0, 0, 7}, /* the below id is for non-shared-antenna free-run */ + {255, 0, 0, 7}, /* 0 -> original */ + {255, 2, 0, 7}, /* 1 -> for BT-connected ACI issue && BTG co-rx */ + {255, 0, 0, 7}, /* 2 ->reserved for shared-antenna */ + {255, 0, 0, 7}, /* 3- >reserved for shared-antenna */ + {255, 0, 0, 7}, /* 4 ->reserved for shared-antenna */ + {255, 0, 0, 7}, /* the below id is for non-shared-antenna free-run */ {6, 1, 0, 7}, {13, 1, 0, 7}, {13, 1, 0, 7} }; static const struct rtw89_btc_rf_trx_para rtw89_btc_8852b_rf_dl[] = { - {15, 0, 0, 7}, /* 0 -> original */ - {15, 2, 0, 7}, /* 1 -> reserved for shared-antenna */ - {15, 0, 0, 7}, /* 2 ->reserved for shared-antenna */ - {15, 0, 0, 7}, /* 3- >reserved for shared-antenna */ - {15, 0, 0, 7}, /* 4 ->reserved for shared-antenna */ - {15, 0, 0, 7}, /* the below id is for non-shared-antenna free-run */ - {15, 1, 0, 7}, - {15, 1, 0, 7}, - {15, 1, 0, 7} + {255, 0, 0, 7}, /* 0 -> original */ + {255, 2, 0, 7}, /* 1 -> reserved for shared-antenna */ + {255, 0, 0, 7}, /* 2 ->reserved for shared-antenna */ + {255, 0, 0, 7}, /* 3- >reserved for shared-antenna */ + {255, 0, 0, 7}, /* 4 ->reserved for shared-antenna */ + {255, 0, 0, 7}, /* the below id is for non-shared-antenna free-run */ + {255, 1, 0, 7}, + {255, 1, 0, 7}, + {255, 1, 0, 7} }; static const struct rtw89_btc_fbtc_mreg rtw89_btc_8852b_mon_reg[] = { From 10cd4092f67eaf0cade4f50c68ecb055d4ee3a93 Mon Sep 17 00:00:00 2001 From: Eric Huang Date: Thu, 17 Nov 2022 14:30:00 +0800 Subject: [PATCH 016/144] wifi: rtw89: read CFO from FD or preamble CFO field of phy status ie_type 1 accordingly Add macro to get FD(frequency domain) CFO field from ie_type 1, and correct the naming for preamble CFO field. Each IC could assign the CFO source to either FD CFO or preamble CFO in chip_info. Based on the suggestion from HW designer, rtw8852b and its derived versions will have better CFO tracking performance with FD CFO. Signed-off-by: Eric Huang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221117063001.42967-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 6 +++++- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/rtw8852a.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852b.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852c.c | 1 + drivers/net/wireless/realtek/rtw89/txrx.h | 4 +++- 6 files changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index f30aadc41f2b..c0a4667fd7e2 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -1196,7 +1196,11 @@ static void rtw89_core_parse_phy_status_ie01(struct rtw89_dev *rtwdev, u8 *addr, if (phy_ppdu->rate < RTW89_HW_RATE_OFDM6) return; /* sign conversion for S(12,2) */ - cfo = sign_extend32(RTW89_GET_PHY_STS_IE01_CFO(addr), 11); + if (rtwdev->chip->cfo_src_fd) + cfo = sign_extend32(RTW89_GET_PHY_STS_IE01_FD_CFO(addr), 11); + else + cfo = sign_extend32(RTW89_GET_PHY_STS_IE01_PREMB_CFO(addr), 11); + rtw89_phy_cfo_parse(rtwdev, cfo, phy_ppdu); } diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index b60de6662548..ba4ccbde5ecf 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -2755,6 +2755,7 @@ struct rtw89_chip_info { u32 c2h_ctrl_reg; const u32 *c2h_regs; const struct rtw89_page_regs *page_regs; + bool cfo_src_fd; const struct rtw89_reg_def *dcfo_comp; u8 dcfo_comp_sft; const struct rtw89_imr_info *imr_info; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 4cea5fb4327d..f38a330698e9 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2143,6 +2143,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .c2h_ctrl_reg = R_AX_C2HREG_CTRL, .c2h_regs = rtw8852a_c2h_regs, .page_regs = &rtw8852a_page_regs, + .cfo_src_fd = false, .dcfo_comp = &rtw8852a_dcfo_comp, .dcfo_comp_sft = 3, .imr_info = &rtw8852a_imr_info, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 85dfc1ebb0d9..22b3c86ee7d8 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -2512,6 +2512,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .c2h_ctrl_reg = R_AX_C2HREG_CTRL, .c2h_regs = rtw8852b_c2h_regs, .page_regs = &rtw8852b_page_regs, + .cfo_src_fd = true, .dcfo_comp = &rtw8852b_dcfo_comp, .dcfo_comp_sft = 3, .imr_info = &rtw8852b_imr_info, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 6619ba730719..01e6358fa1a2 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -2953,6 +2953,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .c2h_ctrl_reg = R_AX_C2HREG_CTRL_V1, .c2h_regs = rtw8852c_c2h_regs, .page_regs = &rtw8852c_page_regs, + .cfo_src_fd = false, .dcfo_comp = &rtw8852c_dcfo_comp, .dcfo_comp_sft = 5, .imr_info = &rtw8852c_imr_info, diff --git a/drivers/net/wireless/realtek/rtw89/txrx.h b/drivers/net/wireless/realtek/rtw89/txrx.h index b889e7bf34c0..9d4c6b6fa125 100644 --- a/drivers/net/wireless/realtek/rtw89/txrx.h +++ b/drivers/net/wireless/realtek/rtw89/txrx.h @@ -298,7 +298,9 @@ le32_get_bits(*((const __le32 *)ie), GENMASK(11, 5)) #define RTW89_GET_PHY_STS_IE01_CH_IDX(ie) \ le32_get_bits(*((const __le32 *)ie), GENMASK(23, 16)) -#define RTW89_GET_PHY_STS_IE01_CFO(ie) \ +#define RTW89_GET_PHY_STS_IE01_FD_CFO(ie) \ + le32_get_bits(*((const __le32 *)(ie) + 1), GENMASK(19, 8)) +#define RTW89_GET_PHY_STS_IE01_PREMB_CFO(ie) \ le32_get_bits(*((const __le32 *)(ie) + 1), GENMASK(31, 20)) enum rtw89_tx_channel { From 29136c95fdc5d9bbfb56131408388fefdba4ed95 Mon Sep 17 00:00:00 2001 From: Eric Huang Date: Thu, 17 Nov 2022 14:30:01 +0800 Subject: [PATCH 017/144] wifi: rtw89: switch BANDEDGE and TX_SHAPE based on OFDMA trigger frame There are some registers for transmit waveform control, two of them used in this change are for BANDEDGE and TX_SHAPE control. BANDEDGE controls whether to apply band edge filter to transmit waveform. TX_SHAPE controls whether to apply triangular mask to transmit waveform. It is found for some chip, these two should be turned off during OFDMA UL traffic for better performance. Signed-off-by: Eric Huang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221117063001.42967-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 2 + drivers/net/wireless/realtek/rtw89/core.h | 9 ++ drivers/net/wireless/realtek/rtw89/debug.h | 1 + drivers/net/wireless/realtek/rtw89/phy.c | 127 +++++++++++++++++- drivers/net/wireless/realtek/rtw89/phy.h | 5 + drivers/net/wireless/realtek/rtw89/rtw8852a.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852b.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852c.c | 1 + 8 files changed, 146 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index c0a4667fd7e2..a5fe45d79f18 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -2237,6 +2237,7 @@ static void rtw89_track_work(struct work_struct *work) rtw89_phy_ra_update(rtwdev); rtw89_phy_cfo_track(rtwdev); rtw89_phy_tx_path_div_track(rtwdev); + rtw89_phy_ul_tb_ctrl_track(rtwdev); if (rtwdev->lps_enabled && !rtwdev->btc.lps) rtw89_enter_lps_track(rtwdev); @@ -2560,6 +2561,7 @@ int rtw89_core_sta_assoc(struct rtw89_dev *rtwdev, rtw89_btc_ntfy_role_info(rtwdev, rtwvif, rtwsta, BTC_ROLE_MSTS_STA_CONN_END); rtw89_core_get_no_ul_ofdma_htc(rtwdev, &rtwsta->htc_template); + rtw89_phy_ul_tb_assoc(rtwdev, rtwvif); } return ret; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index ba4ccbde5ecf..62d834dbff67 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -2261,6 +2261,8 @@ struct rtw89_vif { bool wowlan_magic; bool is_hesta; bool last_a_ctrl; + bool dyn_tb_bedge_en; + u8 def_tri_idx; struct work_struct update_beacon_work; struct rtw89_addr_cam_entry addr_cam; struct rtw89_bssid_cam_entry bssid_cam; @@ -2646,6 +2648,11 @@ struct rtw89_dig_regs { struct rtw89_reg_def p1_s20_pagcugc_en; }; +struct rtw89_phy_ul_tb_info { + bool dyn_tb_tri_en; + u8 def_if_bandedge; +}; + struct rtw89_chip_info { enum rtw89_core_chip_id chip_id; const struct rtw89_chip_ops *ops; @@ -2663,6 +2670,7 @@ struct rtw89_chip_info { u8 support_chanctx_num; u8 support_bands; bool support_bw160; + bool support_ul_tb_ctrl; bool hw_sec_hdr; u8 rf_path_num; u8 tx_nss; @@ -3585,6 +3593,7 @@ struct rtw89_dev { struct rtw89_phy_ch_info ch_info; struct rtw89_phy_bb_gain_info bb_gain; struct rtw89_phy_efuse_gain efuse_gain; + struct rtw89_phy_ul_tb_info ul_tb_info; struct delayed_work track_work; struct delayed_work coex_act1_work; diff --git a/drivers/net/wireless/realtek/rtw89/debug.h b/drivers/net/wireless/realtek/rtw89/debug.h index e7971583acbc..d1de5e600836 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.h +++ b/drivers/net/wireless/realtek/rtw89/debug.h @@ -27,6 +27,7 @@ enum rtw89_debug_mask { RTW89_DBG_SAR = BIT(16), RTW89_DBG_STATE = BIT(17), RTW89_DBG_WOW = BIT(18), + RTW89_DBG_UL_TB = BIT(19), RTW89_DBG_UNEXP = BIT(31), }; diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index dd4685698983..baa48b1058c5 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -2,6 +2,7 @@ /* Copyright(c) 2019-2020 Realtek Corporation */ +#include "coex.h" #include "debug.h" #include "fw.h" #include "mac.h" @@ -9,7 +10,7 @@ #include "ps.h" #include "reg.h" #include "sar.h" -#include "coex.h" +#include "util.h" static u16 get_max_amsdu_len(struct rtw89_dev *rtwdev, const struct rtw89_ra_report *report) @@ -2794,6 +2795,129 @@ void rtw89_phy_cfo_parse(struct rtw89_dev *rtwdev, s16 cfo_val, cfo->packet_count++; } +void rtw89_phy_ul_tb_assoc(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); + struct rtw89_phy_ul_tb_info *ul_tb_info = &rtwdev->ul_tb_info; + + if (!chip->support_ul_tb_ctrl) + return; + + rtwvif->def_tri_idx = + rtw89_phy_read32_mask(rtwdev, R_DCFO_OPT, B_TXSHAPE_TRIANGULAR_CFG); + + if (chip->chip_id == RTL8852B && rtwdev->hal.cv > CHIP_CBV) + rtwvif->dyn_tb_bedge_en = false; + else if (chan->band_type >= RTW89_BAND_5G && + chan->band_width >= RTW89_CHANNEL_WIDTH_40) + rtwvif->dyn_tb_bedge_en = true; + else + rtwvif->dyn_tb_bedge_en = false; + + rtw89_debug(rtwdev, RTW89_DBG_UL_TB, + "[ULTB] def_if_bandedge=%d, def_tri_idx=%d\n", + ul_tb_info->def_if_bandedge, rtwvif->def_tri_idx); + rtw89_debug(rtwdev, RTW89_DBG_UL_TB, + "[ULTB] dyn_tb_begde_en=%d, dyn_tb_tri_en=%d\n", + rtwvif->dyn_tb_bedge_en, ul_tb_info->dyn_tb_tri_en); +} + +struct rtw89_phy_ul_tb_check_data { + bool valid; + bool high_tf_client; + bool low_tf_client; + bool dyn_tb_bedge_en; + u8 def_tri_idx; +}; + +static +void rtw89_phy_ul_tb_ctrl_check(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif, + struct rtw89_phy_ul_tb_check_data *ul_tb_data) +{ + struct rtw89_traffic_stats *stats = &rtwdev->stats; + struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif); + + if (rtwvif->wifi_role != RTW89_WIFI_ROLE_STATION) + return; + + if (!vif->cfg.assoc) + return; + + if (stats->rx_tf_periodic > UL_TB_TF_CNT_L2H_TH) + ul_tb_data->high_tf_client = true; + else if (stats->rx_tf_periodic < UL_TB_TF_CNT_H2L_TH) + ul_tb_data->low_tf_client = true; + + ul_tb_data->valid = true; + ul_tb_data->def_tri_idx = rtwvif->def_tri_idx; + ul_tb_data->dyn_tb_bedge_en = rtwvif->dyn_tb_bedge_en; +} + +void rtw89_phy_ul_tb_ctrl_track(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_phy_ul_tb_info *ul_tb_info = &rtwdev->ul_tb_info; + struct rtw89_phy_ul_tb_check_data ul_tb_data = {}; + struct rtw89_vif *rtwvif; + + if (!chip->support_ul_tb_ctrl) + return; + + if (rtwdev->total_sta_assoc != 1) + return; + + rtw89_for_each_rtwvif(rtwdev, rtwvif) + rtw89_phy_ul_tb_ctrl_check(rtwdev, rtwvif, &ul_tb_data); + + if (!ul_tb_data.valid) + return; + + if (ul_tb_data.dyn_tb_bedge_en) { + if (ul_tb_data.high_tf_client) { + rtw89_phy_write32_mask(rtwdev, R_BANDEDGE, B_BANDEDGE_EN, 0); + rtw89_debug(rtwdev, RTW89_DBG_UL_TB, + "[ULTB] Turn off if_bandedge\n"); + } else if (ul_tb_data.low_tf_client) { + rtw89_phy_write32_mask(rtwdev, R_BANDEDGE, B_BANDEDGE_EN, + ul_tb_info->def_if_bandedge); + rtw89_debug(rtwdev, RTW89_DBG_UL_TB, + "[ULTB] Set to default if_bandedge = %d\n", + ul_tb_info->def_if_bandedge); + } + } + + if (ul_tb_info->dyn_tb_tri_en) { + if (ul_tb_data.high_tf_client) { + rtw89_phy_write32_mask(rtwdev, R_DCFO_OPT, + B_TXSHAPE_TRIANGULAR_CFG, 0); + rtw89_debug(rtwdev, RTW89_DBG_UL_TB, + "[ULTB] Turn off Tx triangle\n"); + } else if (ul_tb_data.low_tf_client) { + rtw89_phy_write32_mask(rtwdev, R_DCFO_OPT, + B_TXSHAPE_TRIANGULAR_CFG, + ul_tb_data.def_tri_idx); + rtw89_debug(rtwdev, RTW89_DBG_UL_TB, + "[ULTB] Set to default tx_shap_idx = %d\n", + ul_tb_data.def_tri_idx); + } + } +} + +static void rtw89_phy_ul_tb_info_init(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_phy_ul_tb_info *ul_tb_info = &rtwdev->ul_tb_info; + + if (!chip->support_ul_tb_ctrl) + return; + + ul_tb_info->dyn_tb_tri_en = true; + ul_tb_info->def_if_bandedge = + rtw89_phy_read32_mask(rtwdev, R_BANDEDGE, B_BANDEDGE_EN); +} + static void rtw89_phy_stat_thermal_update(struct rtw89_dev *rtwdev) { struct rtw89_phy_stat *phystat = &rtwdev->phystat; @@ -3980,6 +4104,7 @@ void rtw89_phy_dm_init(struct rtw89_dev *rtwdev) rtw89_physts_parsing_init(rtwdev); rtw89_phy_dig_init(rtwdev); rtw89_phy_cfo_init(rtwdev); + rtw89_phy_ul_tb_info_init(rtwdev); rtw89_phy_init_rf_nctl(rtwdev); rtw89_chip_rfk_init(rtwdev); diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index dac69a02e868..21233f094644 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -64,6 +64,9 @@ #define MAX_CFO_TOLERANCE 30 #define CFO_TF_CNT_TH 300 +#define UL_TB_TF_CNT_L2H_TH 100 +#define UL_TB_TF_CNT_H2L_TH 70 + #define CCX_MAX_PERIOD 2097 #define CCX_MAX_PERIOD_UNIT 32 #define MS_TO_4US_RATIO 250 @@ -550,5 +553,7 @@ void rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif void rtw89_phy_tssi_ctrl_set_bandedge_cfg(struct rtw89_dev *rtwdev, enum rtw89_mac_idx mac_idx, enum rtw89_tssi_bandedge_cfg bandedge_cfg); +void rtw89_phy_ul_tb_assoc(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif); +void rtw89_phy_ul_tb_ctrl_track(struct rtw89_dev *rtwdev); #endif diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index f38a330698e9..eff6519cf019 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2082,6 +2082,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .support_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), .support_bw160 = false, + .support_ul_tb_ctrl = false, .hw_sec_hdr = false, .rf_path_num = 2, .tx_nss = 2, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 22b3c86ee7d8..2d4d572dc601 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -2452,6 +2452,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .support_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), .support_bw160 = false, + .support_ul_tb_ctrl = true, .hw_sec_hdr = false, .rf_path_num = 2, .tx_nss = 2, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 01e6358fa1a2..9bc98fd5d4ac 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -2891,6 +2891,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = { BIT(NL80211_BAND_5GHZ) | BIT(NL80211_BAND_6GHZ), .support_bw160 = true, + .support_ul_tb_ctrl = false, .hw_sec_hdr = true, .rf_path_num = 2, .tx_nss = 2, From ac3a9f1838d8f5e5f9c8b6e2582b65c48a1e7bc1 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Thu, 17 Nov 2022 16:52:35 +0800 Subject: [PATCH 018/144] wifi: rtw89: avoid inaccessible IO operations during doing change_interface() During doing change_interface(), hardware is power-off, so some components are inaccessible and return error. This causes things unexpected, and we don't have a warning message for that. So, ignore some IO operations in this situation, and add a warning message to indicate something wrong. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221117085235.53777-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/mac.c | 7 +++++++ drivers/net/wireless/realtek/rtw89/mac80211.c | 11 ++++++++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 62d834dbff67..1b0acb1c5450 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -2968,6 +2968,7 @@ enum rtw89_flags { RTW89_FLAG_CRASH_SIMULATING, RTW89_FLAG_WOWLAN, RTW89_FLAG_FORBIDDEN_TRACK_WROK, + RTW89_FLAG_CHANGING_INTERFACE, NUM_OF_RTW89_FLAGS, }; diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index ecd603a88134..6587cdf6ba62 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -3600,6 +3600,13 @@ int rtw89_mac_set_macid_pause(struct rtw89_dev *rtwdev, u8 macid, bool pause) u8 grp = macid >> 5; int ret; + /* If this is called by change_interface() in the case of P2P, it could + * be power-off, so ignore this operation. + */ + if (test_bit(RTW89_FLAG_CHANGING_INTERFACE, rtwdev->flags) && + !test_bit(RTW89_FLAG_POWERON, rtwdev->flags)) + return 0; + ret = rtw89_mac_check_mac_en(rtwdev, RTW89_MAC_0, RTW89_CMAC_SEL); if (ret) return ret; diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index 6e79bf899901..ce980d2f22c4 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -174,6 +174,9 @@ static int rtw89_ops_change_interface(struct ieee80211_hw *hw, enum nl80211_iftype type, bool p2p) { struct rtw89_dev *rtwdev = hw->priv; + int ret; + + set_bit(RTW89_FLAG_CHANGING_INTERFACE, rtwdev->flags); rtw89_debug(rtwdev, RTW89_DBG_STATE, "change vif %pM (%d)->(%d), p2p (%d)->(%d)\n", vif->addr, vif->type, type, vif->p2p, p2p); @@ -183,7 +186,13 @@ static int rtw89_ops_change_interface(struct ieee80211_hw *hw, vif->type = type; vif->p2p = p2p; - return rtw89_ops_add_interface(hw, vif); + ret = rtw89_ops_add_interface(hw, vif); + if (ret) + rtw89_warn(rtwdev, "failed to change interface %d\n", ret); + + clear_bit(RTW89_FLAG_CHANGING_INTERFACE, rtwdev->flags); + + return ret; } static void rtw89_ops_configure_filter(struct ieee80211_hw *hw, From 9e2f177de1bfb7d891bf38140bda54831ecef30d Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Fri, 18 Nov 2022 12:23:22 +0800 Subject: [PATCH 019/144] wifi: rtw89: fix physts IE page check The index RTW89_PHYSTS_BITMAP_NUM is not a valid physts IE page. So, fix the check condition. Fixes: eb4e52b3f38d ("rtw89: fix incorrect channel info during scan") Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221118042322.26794-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index baa48b1058c5..017710c580c7 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -3464,7 +3464,7 @@ void rtw89_phy_env_monitor_track(struct rtw89_dev *rtwdev) static bool rtw89_physts_ie_page_valid(enum rtw89_phy_status_bitmap *ie_page) { - if (*ie_page > RTW89_PHYSTS_BITMAP_NUM || + if (*ie_page >= RTW89_PHYSTS_BITMAP_NUM || *ie_page == RTW89_RSVD_9) return false; else if (*ie_page > RTW89_RSVD_9) From 3b79d4bad3a0e73d9becb409a5f6112bc61e2c93 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 18 Nov 2022 15:42:44 -0800 Subject: [PATCH 020/144] wifi: p54: Replace zero-length array of trailing structs with flex-array Zero-length arrays are deprecated[1] and are being replaced with flexible array members in support of the ongoing efforts to tighten the FORTIFY_SOURCE routines on memcpy(), correctly instrument array indexing with UBSAN_BOUNDS, and to globally enable -fstrict-flex-arrays=3. Replace zero-length array with flexible-array member. This results in no differences in binary output (most especially because struct pda_antenna_gain is unused). The struct is kept for future reference. [1] https://github.com/KSPP/linux/issues/78 Cc: Christian Lamparter Cc: Kalle Valo Cc: "David S. Miller" Cc: Eric Dumazet Cc: Jakub Kicinski Cc: Paolo Abeni Cc: "Gustavo A. R. Silva" Cc: linux-wireless@vger.kernel.org Cc: netdev@vger.kernel.org Signed-off-by: Kees Cook Acked-by: Christian Lamparter Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221118234240.gonna.369-kees@kernel.org --- drivers/net/wireless/intersil/p54/eeprom.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intersil/p54/eeprom.h b/drivers/net/wireless/intersil/p54/eeprom.h index 1d0aaf54389a..641c4e79879e 100644 --- a/drivers/net/wireless/intersil/p54/eeprom.h +++ b/drivers/net/wireless/intersil/p54/eeprom.h @@ -108,10 +108,10 @@ struct pda_country { } __packed; struct pda_antenna_gain { - struct { + DECLARE_FLEX_ARRAY(struct { u8 gain_5GHz; /* 0.25 dBi units */ u8 gain_2GHz; /* 0.25 dBi units */ - } __packed antenna[0]; + } __packed, antenna); } __packed; struct pda_custom_wrapper { From 4d79f6f34bbb01c6715b31ef457d5ab0390501a1 Mon Sep 17 00:00:00 2001 From: Youghandhar Chintala Date: Thu, 17 Nov 2022 23:35:34 +0530 Subject: [PATCH 021/144] wifi: ath10k: Store WLAN firmware version in SMEM image table In a SoC based solution, it would be useful to know the versions of the various binary firmware blobs the system is running on. On a QCOM based SoC, this info can be obtained from socinfo debugfs infrastructure. For this to work, respective subsystem drivers have to export the firmware version information to an SMEM based version information table. Having firmware version information at one place will help quickly figure out the firmware versions of various subsystems on the device instead of going through builds/logs in an event of a system crash. Fill WLAN firmware version information in SMEM version table to be printed as part of socinfo debugfs infrastructure on a Qualcomm based SoC. This change is applicable only for SNOC/QMI based targets. Example: cat /sys/kernel/debug/qcom_socinfo/cnss/name QC_IMAGE_VERSION_STRING=WLAN.HL.3.2.2.c10-00754-QCAHLSWMTPL-1 Tested-on: WCN3990 hw1.0 SNOC WLAN.HL.3.2.2.c10-00754-QCAHLSWMTPL-1 Signed-off-by: Youghandhar Chintala Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221117180534.2267-1-quic_youghand@quicinc.com --- drivers/net/wireless/ath/ath10k/Kconfig | 1 + drivers/net/wireless/ath/ath10k/qmi.c | 35 +++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig index ca007b800f75..e0a51dad8e42 100644 --- a/drivers/net/wireless/ath/ath10k/Kconfig +++ b/drivers/net/wireless/ath/ath10k/Kconfig @@ -44,6 +44,7 @@ config ATH10K_SNOC tristate "Qualcomm ath10k SNOC support" depends on ATH10K depends on ARCH_QCOM || COMPILE_TEST + select QCOM_SMEM select QCOM_SCM select QCOM_QMI_HELPERS help diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c index 8930a0f4c42f..3f94fbf83702 100644 --- a/drivers/net/wireless/ath/ath10k/qmi.c +++ b/drivers/net/wireless/ath/ath10k/qmi.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -22,6 +23,10 @@ #define ATH10K_QMI_CLIENT_ID 0x4b4e454c #define ATH10K_QMI_TIMEOUT 30 +#define SMEM_IMAGE_VERSION_TABLE 469 +#define SMEM_IMAGE_TABLE_CNSS_INDEX 13 +#define SMEM_IMAGE_VERSION_ENTRY_SIZE 128 +#define SMEM_IMAGE_VERSION_NAME_SIZE 75 static int ath10k_qmi_map_msa_permission(struct ath10k_qmi *qmi, struct ath10k_msa_mem_info *mem_info) @@ -536,6 +541,33 @@ int ath10k_qmi_wlan_disable(struct ath10k *ar) return ath10k_qmi_mode_send_sync_msg(ar, QMI_WLFW_OFF_V01); } +static void ath10k_qmi_add_wlan_ver_smem(struct ath10k *ar, const char *fw_build_id) +{ + u8 *table_ptr; + size_t smem_item_size; + const u32 smem_img_idx_wlan = SMEM_IMAGE_TABLE_CNSS_INDEX * + SMEM_IMAGE_VERSION_ENTRY_SIZE; + + table_ptr = qcom_smem_get(QCOM_SMEM_HOST_ANY, + SMEM_IMAGE_VERSION_TABLE, + &smem_item_size); + + if (IS_ERR(table_ptr)) { + ath10k_err(ar, "smem image version table not found\n"); + return; + } + + if (smem_img_idx_wlan + SMEM_IMAGE_VERSION_ENTRY_SIZE > + smem_item_size) { + ath10k_err(ar, "smem block size too small: %zu\n", + smem_item_size); + return; + } + + strscpy(table_ptr + smem_img_idx_wlan, fw_build_id, + SMEM_IMAGE_VERSION_NAME_SIZE); +} + static int ath10k_qmi_cap_send_sync_msg(struct ath10k_qmi *qmi) { struct wlfw_cap_resp_msg_v01 *resp; @@ -606,6 +638,9 @@ static int ath10k_qmi_cap_send_sync_msg(struct ath10k_qmi *qmi) qmi->fw_version, qmi->fw_build_timestamp, qmi->fw_build_id); } + if (resp->fw_build_id_valid) + ath10k_qmi_add_wlan_ver_smem(ar, qmi->fw_build_id); + kfree(resp); return 0; From 7256f28767fa40249254116a4bcb45efff35f31b Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 18 Nov 2022 13:11:47 -0800 Subject: [PATCH 022/144] wifi: carl9170: Replace zero-length array of trailing structs with flex-array Zero-length arrays are deprecated[1] and are being replaced with flexible array members in support of the ongoing efforts to tighten the FORTIFY_SOURCE routines on memcpy(), correctly instrument array indexing with UBSAN_BOUNDS, and to globally enable -fstrict-flex-arrays=3. Replace zero-length array with flexible-array member. This results in no differences in binary output. [1] https://github.com/KSPP/linux/issues/78 Cc: Christian Lamparter Cc: Kalle Valo Cc: "David S. Miller" Cc: Eric Dumazet Cc: Jakub Kicinski Cc: Paolo Abeni Cc: "Gustavo A. R. Silva" Cc: linux-wireless@vger.kernel.org Cc: netdev@vger.kernel.org Signed-off-by: Kees Cook Reviewed-by: Gustavo A. R. Silva Acked-by: Christian Lamparter Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221118211146.never.395-kees@kernel.org --- drivers/net/wireless/ath/carl9170/fwcmd.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/carl9170/fwcmd.h b/drivers/net/wireless/ath/carl9170/fwcmd.h index 4a500095555c..ff4b3b50250c 100644 --- a/drivers/net/wireless/ath/carl9170/fwcmd.h +++ b/drivers/net/wireless/ath/carl9170/fwcmd.h @@ -118,10 +118,10 @@ struct carl9170_reg_list { } __packed; struct carl9170_write_reg { - struct { + DECLARE_FLEX_ARRAY(struct { __le32 addr; __le32 val; - } regs[0] __packed; + } __packed, regs); } __packed; struct carl9170_write_reg_byte { From 895b3b06efc285c1245242e9638b9ae251dc13ec Mon Sep 17 00:00:00 2001 From: Ziyang Xuan Date: Sat, 19 Nov 2022 13:19:00 +0800 Subject: [PATCH 023/144] wifi: plfxlc: fix potential memory leak in __lf_x_usb_enable_rx() urbs does not be freed in exception paths in __lf_x_usb_enable_rx(). That will trigger memory leak. To fix it, add kfree() for urbs within "error" label. Compile tested only. Fixes: 68d57a07bfe5 ("wireless: add plfxlc driver for pureLiFi X, XL, XC devices") Signed-off-by: Ziyang Xuan Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221119051900.1192401-1-william.xuanziyang@huawei.com --- drivers/net/wireless/purelifi/plfxlc/usb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.c b/drivers/net/wireless/purelifi/plfxlc/usb.c index 39e54b3787d6..76d0a778636a 100644 --- a/drivers/net/wireless/purelifi/plfxlc/usb.c +++ b/drivers/net/wireless/purelifi/plfxlc/usb.c @@ -247,6 +247,7 @@ error: for (i = 0; i < RX_URBS_COUNT; i++) free_rx_urb(urbs[i]); } + kfree(urbs); return r; } From 7927afb5e27baac694f585b59c436ba323528dc2 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Mon, 21 Nov 2022 22:56:58 +0200 Subject: [PATCH 024/144] wifi: rtl8xxxu: Fix use after rcu_read_unlock in rtl8xxxu_bss_info_changed Commit a8b5aef2cca1 ("wifi: rtl8xxxu: gen2: Enable 40 MHz channel width") introduced a line where the pointer returned by ieee80211_find_sta() is used after rcu_read_unlock(). Move rcu_read_unlock() a bit lower to fix this. Fixes: a8b5aef2cca1 ("wifi: rtl8xxxu: gen2: Enable 40 MHz channel width") Signed-off-by: Bitterblue Smith Reviewed-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/3c82ad09-7593-3be1-1d2c-e58505fb43cb@gmail.com --- drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index e8fcd531c437..28f136064297 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -4645,7 +4645,6 @@ rtl8xxxu_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, if (sta->deflink.ht_cap.cap & (IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SGI_20)) sgi = 1; - rcu_read_unlock(); highest_rate = fls(ramask) - 1; if (highest_rate < DESC_RATE_MCS0) { @@ -4670,6 +4669,7 @@ rtl8xxxu_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, else rarpt->txrate.bw = RATE_INFO_BW_20; } + rcu_read_unlock(); bit_rate = cfg80211_calculate_bitrate(&rarpt->txrate); rarpt->bit_rate = bit_rate; rarpt->desc_rate = highest_rate; From 3ca7f0b2523cac5d436cd09758db07e10b1a322a Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Tue, 22 Nov 2022 14:12:48 +0100 Subject: [PATCH 025/144] wifi: b43: remove reference to removed config B43_PCMCIA Commit 399500da18f7 ("ssb: pick PCMCIA host code support from b43 driver") removes the config B43_PCMCIA. Clean up the last reference to this removed config B43_PCMCIA in the b43_print_driverinfo() function. Signed-off-by: Lukas Bulwahn Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221122131248.23738-1-lukas.bulwahn@gmail.com --- drivers/net/wireless/broadcom/b43/main.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c index 298febbdffc0..92ca0b2ca286 100644 --- a/drivers/net/wireless/broadcom/b43/main.c +++ b/drivers/net/wireless/broadcom/b43/main.c @@ -5784,15 +5784,12 @@ void b43_controller_restart(struct b43_wldev *dev, const char *reason) static void b43_print_driverinfo(void) { - const char *feat_pci = "", *feat_pcmcia = "", *feat_nphy = "", + const char *feat_pci = "", *feat_nphy = "", *feat_leds = "", *feat_sdio = ""; #ifdef CONFIG_B43_PCI_AUTOSELECT feat_pci = "P"; #endif -#ifdef CONFIG_B43_PCMCIA - feat_pcmcia = "M"; -#endif #ifdef CONFIG_B43_PHY_N feat_nphy = "N"; #endif @@ -5803,9 +5800,8 @@ static void b43_print_driverinfo(void) feat_sdio = "S"; #endif printk(KERN_INFO "Broadcom 43xx driver loaded " - "[ Features: %s%s%s%s%s ]\n", - feat_pci, feat_pcmcia, feat_nphy, - feat_leds, feat_sdio); + "[ Features: %s%s%s%s ]\n", + feat_pci, feat_nphy, feat_leds, feat_sdio); } static int __init b43_init(void) From 823092a53556ebf8656623d0e857626d30fe1e18 Mon Sep 17 00:00:00 2001 From: Ji-Pin Jou Date: Thu, 24 Nov 2022 14:44:42 +0800 Subject: [PATCH 026/144] wifi: rtw88: fix race condition when doing H2C command For SDIO/USB interface, since the tranferring speed is slower than that in PCIE, it may have race condition when the driver sets down H2C command to the FW. In the function rtw_fw_send_h2c_command, before the patch, box_reg is written first, then box_ex_reg is written. FW starts to work and fetch the value of box_ex_reg, when the most significant byte of box_reg(4 bytes) is written. Meanwhile, for SDIO/USB interface, since the transferring speed is slow, the driver is still in writing the new value of box_ex_reg through the bus, and FW may get the wrong value of box_ex_reg at the moment. To prevent the above driver/FW racing situation, box_ex_reg is written first then box_reg. Furthermore, it is written in 4 bytes at a time, instead of written in one byte one by one. It can increase the speed for SDIO/USB interface. Signed-off-by: Ji-Pin Jou Signed-off-by: Ping-Ke Shih Tested-by: Sascha Hauer Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221124064442.28042-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw88/fw.c | 8 +++----- drivers/net/wireless/realtek/rtw88/fw.h | 5 +++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/fw.c b/drivers/net/wireless/realtek/rtw88/fw.c index 0b5f903c0f36..b290811d75e1 100644 --- a/drivers/net/wireless/realtek/rtw88/fw.c +++ b/drivers/net/wireless/realtek/rtw88/fw.c @@ -311,10 +311,10 @@ EXPORT_SYMBOL(rtw_fw_c2h_cmd_isr); static void rtw_fw_send_h2c_command(struct rtw_dev *rtwdev, u8 *h2c) { + struct rtw_h2c_cmd *h2c_cmd = (struct rtw_h2c_cmd *)h2c; u8 box; u8 box_state; u32 box_reg, box_ex_reg; - int idx; int ret; rtw_dbg(rtwdev, RTW_DBG_FW, @@ -356,10 +356,8 @@ static void rtw_fw_send_h2c_command(struct rtw_dev *rtwdev, goto out; } - for (idx = 0; idx < 4; idx++) - rtw_write8(rtwdev, box_reg + idx, h2c[idx]); - for (idx = 0; idx < 4; idx++) - rtw_write8(rtwdev, box_ex_reg + idx, h2c[idx + 4]); + rtw_write32(rtwdev, box_ex_reg, le32_to_cpu(h2c_cmd->msg_ext)); + rtw_write32(rtwdev, box_reg, le32_to_cpu(h2c_cmd->msg)); if (++rtwdev->h2c.last_box_num >= 4) rtwdev->h2c.last_box_num = 0; diff --git a/drivers/net/wireless/realtek/rtw88/fw.h b/drivers/net/wireless/realtek/rtw88/fw.h index a5a965803a3c..bca610dc99ab 100644 --- a/drivers/net/wireless/realtek/rtw88/fw.h +++ b/drivers/net/wireless/realtek/rtw88/fw.h @@ -81,6 +81,11 @@ struct rtw_c2h_adaptivity { u8 option; } __packed; +struct rtw_h2c_cmd { + __le32 msg; + __le32 msg_ext; +} __packed; + enum rtw_rsvd_packet_type { RSVD_BEACON, RSVD_DUMMY, From 26d7cc0abe6181148c50982ec2e8e0e46f74b419 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Tue, 22 Nov 2022 22:10:32 +0200 Subject: [PATCH 027/144] wifi: iwlwifi: mvm: Advertise EHT capabilities Add support for advertising EHT capabilities if supported by device SKU and not disabled by module parameters. Signed-off-by: Ilan Peer Link: https://lore.kernel.org/r/20221122220713.6bc00d851794.I214005645f3da21d8f2458a70355deeca04a19e8@changeid Signed-off-by: Gregory Greenman --- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 3 + .../wireless/intel/iwlwifi/iwl-eeprom-parse.h | 3 +- .../wireless/intel/iwlwifi/iwl-modparams.h | 1 + .../wireless/intel/iwlwifi/iwl-nvm-parse.c | 178 ++++++++++++++++-- 4 files changed, 167 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index a2203f661321..ab7065c93826 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -1971,3 +1971,6 @@ MODULE_PARM_DESC(remove_when_gone, module_param_named(disable_11ax, iwlwifi_mod_params.disable_11ax, bool, S_IRUGO); MODULE_PARM_DESC(disable_11ax, "Disable HE capabilities (default: false)"); + +module_param_named(disable_11be, iwlwifi_mod_params.disable_11be, bool, 0444); +MODULE_PARM_DESC(disable_11be, "Disable EHT capabilities (default: false)"); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h index 0efffb6eeb1e..baa643386018 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2018 Intel Corporation + * Copyright (C) 2005-2014, 2018, 2020-2022 Intel Corporation * Copyright (C) 2015 Intel Mobile Communications GmbH */ #ifndef __iwl_eeprom_parse_h__ @@ -31,6 +31,7 @@ struct iwl_nvm_data { bool sku_cap_amt_enable; bool sku_cap_ipan_enable; bool sku_cap_mimo_disabled; + bool sku_cap_11be_enable; u16 radio_cfg_type; u8 radio_cfg_step; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h index d0b4d02bdab9..1cf26ab4f488 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h @@ -62,6 +62,7 @@ enum iwl_uapsd_disable { * @disable_11ac: disable VHT capabilities, default = false. * @remove_when_gone: remove an inaccessible device from the PCIe bus. * @enable_ini: enable new FW debug infratructure (INI TLVs) + * @disable_11be: disable EHT capabilities, default = false. */ struct iwl_mod_params { int swcrypto; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 9040da3dcce3..2b43fff4d3f9 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -546,7 +546,7 @@ static const u8 iwl_vendor_caps[] = { 0x00 }; -static const struct ieee80211_sband_iftype_data iwl_he_capa[] = { +static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = { { .types_mask = BIT(NL80211_IFTYPE_STATION), .he_cap = { @@ -631,6 +631,78 @@ static const struct ieee80211_sband_iftype_data iwl_he_capa[] = { */ .ppe_thres = {0x61, 0x1c, 0xc7, 0x71}, }, + .eht_cap = { + .has_eht = true, + .eht_cap_elem = { + .mac_cap_info[0] = + IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS | + IEEE80211_EHT_MAC_CAP0_OM_CONTROL | + IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1 | + IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE2, + .phy_cap_info[0] = + IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ | + IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI | + IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO | + IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE | + IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK, + .phy_cap_info[1] = + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK | + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK | + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK, + .phy_cap_info[3] = + IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK, + + .phy_cap_info[4] = + IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO | + IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP | + IEEE80211_EHT_PHY_CAP4_EHT_MU_PPDU_4_EHT_LTF_08_GI, + .phy_cap_info[5] = + IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK | + IEEE80211_EHT_PHY_CAP5_TX_LESS_242_TONE_RU_SUPP | + IEEE80211_EHT_PHY_CAP5_RX_LESS_242_TONE_RU_SUPP | + IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT, + .phy_cap_info[6] = + IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK | + IEEE80211_EHT_PHY_CAP6_EHT_DUP_6GHZ_SUPP, + .phy_cap_info[8] = + IEEE80211_EHT_PHY_CAP8_RX_1024QAM_WIDER_BW_DL_OFDMA | + IEEE80211_EHT_PHY_CAP8_RX_4096QAM_WIDER_BW_DL_OFDMA, + }, + + /* For all MCS and bandwidth, set 2 NSS for both Tx and + * Rx - note we don't set the only_20mhz, but due to this + * being a union, it gets set correctly anyway. + */ + .eht_mcs_nss_supp = { + .bw._80 = { + .rx_tx_mcs9_max_nss = 0x22, + .rx_tx_mcs11_max_nss = 0x22, + .rx_tx_mcs13_max_nss = 0x22, + }, + .bw._160 = { + .rx_tx_mcs9_max_nss = 0x22, + .rx_tx_mcs11_max_nss = 0x22, + .rx_tx_mcs13_max_nss = 0x22, + }, + .bw._320 = { + .rx_tx_mcs9_max_nss = 0x22, + .rx_tx_mcs11_max_nss = 0x22, + .rx_tx_mcs13_max_nss = 0x22, + }, + }, + + /* + * PPE thresholds for NSS = 2, and RU index bitmap set + * to 0xc. + */ + .eht_ppe_thres = {0xc1, 0x0e, 0xe0 } + }, }, { .types_mask = BIT(NL80211_IFTYPE_AP), @@ -687,6 +759,49 @@ static const struct ieee80211_sband_iftype_data iwl_he_capa[] = { */ .ppe_thres = {0x61, 0x1c, 0xc7, 0x71}, }, + .eht_cap = { + .has_eht = true, + .eht_cap_elem = { + .mac_cap_info[0] = + IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS | + IEEE80211_EHT_MAC_CAP0_OM_CONTROL | + IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1 | + IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE2, + .phy_cap_info[0] = + IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ | + IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI, + .phy_cap_info[5] = + IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT, + }, + + /* For all MCS and bandwidth, set 2 NSS for both Tx and + * Rx - note we don't set the only_20mhz, but due to this + * being a union, it gets set correctly anyway. + */ + .eht_mcs_nss_supp = { + .bw._80 = { + .rx_tx_mcs9_max_nss = 0x22, + .rx_tx_mcs11_max_nss = 0x22, + .rx_tx_mcs13_max_nss = 0x22, + }, + .bw._160 = { + .rx_tx_mcs9_max_nss = 0x22, + .rx_tx_mcs11_max_nss = 0x22, + .rx_tx_mcs13_max_nss = 0x22, + }, + .bw._320 = { + .rx_tx_mcs9_max_nss = 0x22, + .rx_tx_mcs11_max_nss = 0x22, + .rx_tx_mcs13_max_nss = 0x22, + }, + }, + + /* + * PPE thresholds for NSS = 2, and RU index bitmap set + * to 0xc. + */ + .eht_ppe_thres = {0xc1, 0x0e, 0xe0 } + }, }, }; @@ -738,6 +853,7 @@ static void iwl_init_he_6ghz_capa(struct iwl_trans *trans, static void iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, + struct iwl_nvm_data *data, struct ieee80211_supported_band *sband, struct ieee80211_sband_iftype_data *iftype_data, u8 tx_chains, u8 rx_chains, @@ -745,6 +861,9 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, { bool is_ap = iftype_data->types_mask & BIT(NL80211_IFTYPE_AP); + if (!data->sku_cap_11be_enable || iwlwifi_mod_params.disable_11be) + iftype_data->eht_cap.has_eht = false; + /* Advertise an A-MPDU exponent extension based on * operating band */ @@ -765,19 +884,44 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, iftype_data->he_cap.he_cap_elem.phy_cap_info[5] |= IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_2 | IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_2; - if (!is_ap) + if (!is_ap) { iftype_data->he_cap.he_cap_elem.phy_cap_info[7] |= IEEE80211_HE_PHY_CAP7_MAX_NC_2; - } else if (!is_ap) { - /* If not 2x2, we need to indicate 1x1 in the - * Midamble RX Max NSTS - but not for AP mode - */ - iftype_data->he_cap.he_cap_elem.phy_cap_info[1] &= - ~IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS; - iftype_data->he_cap.he_cap_elem.phy_cap_info[2] &= - ~IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS; - iftype_data->he_cap.he_cap_elem.phy_cap_info[7] |= - IEEE80211_HE_PHY_CAP7_MAX_NC_1; + + if (iftype_data->eht_cap.has_eht) { + /* + * Set the number of sounding dimensions for each + * bandwidth to 1 to indicate the maximal supported + * value of TXVECTOR parameter NUM_STS of 2 + */ + iftype_data->eht_cap.eht_cap_elem.phy_cap_info[2] |= 0x49; + + /* + * Set the MAX NC to 1 to indicate sounding feedback of + * 2 supported by the beamfomee. + */ + iftype_data->eht_cap.eht_cap_elem.phy_cap_info[4] |= 0x10; + } + } + } else { + if (iftype_data->eht_cap.has_eht) { + struct ieee80211_eht_mcs_nss_supp *mcs_nss = + &iftype_data->eht_cap.eht_mcs_nss_supp; + + memset(mcs_nss, 0x11, sizeof(*mcs_nss)); + } + + if (!is_ap) { + /* If not 2x2, we need to indicate 1x1 in the + * Midamble RX Max NSTS - but not for AP mode + */ + iftype_data->he_cap.he_cap_elem.phy_cap_info[1] &= + ~IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS; + iftype_data->he_cap.he_cap_elem.phy_cap_info[2] &= + ~IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS; + iftype_data->he_cap.he_cap_elem.phy_cap_info[7] |= + IEEE80211_HE_PHY_CAP7_MAX_NC_1; + } } switch (CSR_HW_RFID_TYPE(trans->hw_rf_id)) { @@ -816,8 +960,8 @@ static void iwl_init_he_hw_capab(struct iwl_trans *trans, if (WARN_ON(sband->iftype_data)) return; - BUILD_BUG_ON(sizeof(data->iftd.low) != sizeof(iwl_he_capa)); - BUILD_BUG_ON(sizeof(data->iftd.high) != sizeof(iwl_he_capa)); + BUILD_BUG_ON(sizeof(data->iftd.low) != sizeof(iwl_he_eht_capa)); + BUILD_BUG_ON(sizeof(data->iftd.high) != sizeof(iwl_he_eht_capa)); switch (sband->band) { case NL80211_BAND_2GHZ: @@ -832,13 +976,13 @@ static void iwl_init_he_hw_capab(struct iwl_trans *trans, return; } - memcpy(iftype_data, iwl_he_capa, sizeof(iwl_he_capa)); + memcpy(iftype_data, iwl_he_eht_capa, sizeof(iwl_he_eht_capa)); sband->iftype_data = iftype_data; - sband->n_iftype_data = ARRAY_SIZE(iwl_he_capa); + sband->n_iftype_data = ARRAY_SIZE(iwl_he_eht_capa); for (i = 0; i < sband->n_iftype_data; i++) - iwl_nvm_fixup_sband_iftd(trans, sband, &iftype_data[i], + iwl_nvm_fixup_sband_iftd(trans, data, sband, &iftype_data[i], tx_chains, rx_chains, fw); iwl_init_he_6ghz_capa(trans, data, sband, tx_chains, rx_chains); From 7ac875753a11589bfbebe707941ac8a11306ba25 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 22 Nov 2022 22:10:33 +0200 Subject: [PATCH 028/144] wifi: iwlwifi: mvm: support 320 MHz PHY configuration Support 320 MHz PHY configuration and while doing so rewrite the code since we'd otherwise double the number of cases in the switch statement. Signed-off-by: Johannes Berg Link: https://lore.kernel.org/r/20221122220713.964db911b733.If56c94a9bf20c050f35d2421b680e400a9f4aeb8@changeid Signed-off-by: Gregory Greenman --- .../wireless/intel/iwlwifi/fw/api/phy-ctxt.h | 33 ++++++------ .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 2 +- .../net/wireless/intel/iwlwifi/mvm/phy-ctxt.c | 54 +++++++++---------- 3 files changed, 44 insertions(+), 45 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h b/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h index e66f77924f83..2f7d8558becd 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h @@ -13,10 +13,12 @@ #define PHY_BAND_6 (2) /* Supported channel width, vary if there is VHT support */ -#define PHY_VHT_CHANNEL_MODE20 (0x0) -#define PHY_VHT_CHANNEL_MODE40 (0x1) -#define PHY_VHT_CHANNEL_MODE80 (0x2) -#define PHY_VHT_CHANNEL_MODE160 (0x3) +#define IWL_PHY_CHANNEL_MODE20 0x0 +#define IWL_PHY_CHANNEL_MODE40 0x1 +#define IWL_PHY_CHANNEL_MODE80 0x2 +#define IWL_PHY_CHANNEL_MODE160 0x3 +/* and 320 MHz for EHT */ +#define IWL_PHY_CHANNEL_MODE320 0x4 /* * Control channel position: @@ -24,20 +26,17 @@ * For VHT - bit-2 marks if the control is lower/upper relative to center-freq * bits-1:0 mark the distance from the center freq. for 20Mhz, offset is 0. * center_freq - * | - * 40Mhz |_______|_______| - * 80Mhz |_______|_______|_______|_______| - * 160Mhz |_______|_______|_______|_______|_______|_______|_______|_______| - * code 011 010 001 000 | 100 101 110 111 + * For EHT - bit-3 is used for extended distance + * | + * 40Mhz |____|____| + * 80Mhz |____|____|____|____| + * 160Mhz |____|____|____|____|____|____|____|____| + * 320MHz |____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____| + * code 1011 1010 1001 1000 0011 0010 0001 0000 0100 0101 0110 0111 1100 1101 1110 1111 */ -#define PHY_VHT_CTRL_POS_1_BELOW (0x0) -#define PHY_VHT_CTRL_POS_2_BELOW (0x1) -#define PHY_VHT_CTRL_POS_3_BELOW (0x2) -#define PHY_VHT_CTRL_POS_4_BELOW (0x3) -#define PHY_VHT_CTRL_POS_1_ABOVE (0x4) -#define PHY_VHT_CTRL_POS_2_ABOVE (0x5) -#define PHY_VHT_CTRL_POS_3_ABOVE (0x6) -#define PHY_VHT_CTRL_POS_4_ABOVE (0x7) +#define IWL_PHY_CTRL_POS_ABOVE 0x4 +#define IWL_PHY_CTRL_POS_OFFS_EXT 0x8 +#define IWL_PHY_CTRL_POS_OFFS_MSK 0x3 /* * struct iwl_fw_channel_info_v1 - channel information diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index a81dd488caa8..57aa10466637 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -3777,7 +3777,7 @@ static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm, /* Set the channel info data */ iwl_mvm_set_chan_info(mvm, &aux_roc_req.channel_info, channel->hw_value, iwl_mvm_phy_band_from_nl80211(channel->band), - PHY_VHT_CHANNEL_MODE20, + IWL_PHY_CHANNEL_MODE20, 0); /* Set the time and duration */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c index a3cefbc43e80..06f4203fb989 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c @@ -14,16 +14,18 @@ u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef) switch (chandef->width) { case NL80211_CHAN_WIDTH_20_NOHT: case NL80211_CHAN_WIDTH_20: - return PHY_VHT_CHANNEL_MODE20; + return IWL_PHY_CHANNEL_MODE20; case NL80211_CHAN_WIDTH_40: - return PHY_VHT_CHANNEL_MODE40; + return IWL_PHY_CHANNEL_MODE40; case NL80211_CHAN_WIDTH_80: - return PHY_VHT_CHANNEL_MODE80; + return IWL_PHY_CHANNEL_MODE80; case NL80211_CHAN_WIDTH_160: - return PHY_VHT_CHANNEL_MODE160; + return IWL_PHY_CHANNEL_MODE160; + case NL80211_CHAN_WIDTH_320: + return IWL_PHY_CHANNEL_MODE320; default: WARN(1, "Invalid channel width=%u", chandef->width); - return PHY_VHT_CHANNEL_MODE20; + return IWL_PHY_CHANNEL_MODE20; } } @@ -33,34 +35,32 @@ u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef) */ u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef) { - switch (chandef->chan->center_freq - chandef->center_freq1) { - case -70: - return PHY_VHT_CTRL_POS_4_BELOW; - case -50: - return PHY_VHT_CTRL_POS_3_BELOW; - case -30: - return PHY_VHT_CTRL_POS_2_BELOW; - case -10: - return PHY_VHT_CTRL_POS_1_BELOW; - case 10: - return PHY_VHT_CTRL_POS_1_ABOVE; - case 30: - return PHY_VHT_CTRL_POS_2_ABOVE; - case 50: - return PHY_VHT_CTRL_POS_3_ABOVE; - case 70: - return PHY_VHT_CTRL_POS_4_ABOVE; - default: - WARN(1, "Invalid channel definition"); - fallthrough; - case 0: + int offs = chandef->chan->center_freq - chandef->center_freq1; + int abs_offs = abs(offs); + u8 ret; + + if (offs == 0) { /* * The FW is expected to check the control channel position only * when in HT/VHT and the channel width is not 20MHz. Return * this value as the default one. */ - return PHY_VHT_CTRL_POS_1_BELOW; + return 0; } + + /* this results in a value 0-7, i.e. fitting into 0b0111 */ + ret = (abs_offs - 10) / 20; + /* + * But we need the value to be in 0b1011 because 0b0100 is + * IWL_PHY_CTRL_POS_ABOVE, so shift bit 2 up to land in + * IWL_PHY_CTRL_POS_OFFS_EXT (0b1000) + */ + ret = (ret & IWL_PHY_CTRL_POS_OFFS_MSK) | + ((ret & BIT(2)) << 1); + /* and add the above bit */ + ret |= (offs > 0) * IWL_PHY_CTRL_POS_ABOVE; + + return ret; } /* From 701404f1091d8b4cdccfa835ceb05a3982b2c614 Mon Sep 17 00:00:00 2001 From: Mordechay Goodstein Date: Tue, 22 Nov 2022 22:10:34 +0200 Subject: [PATCH 029/144] wifi: iwlwifi: rs: add support for parsing max MCS per NSS/BW in 11be Rate scale needs to be configured to what MCS it can use per BW and NSS, this is done by parsing our capabilities of TX and peer's capabilities of RX and setting the minimum for rate scale usage. Also do some cleanup removing redundant enum defines not used by FW/Driver. Signed-off-by: Mordechay Goodstein Link: https://lore.kernel.org/r/20221122220713.f71f3b4c4583.I7b5e8071df91146c4bee3e9bcb7ad62595b275e1@changeid Signed-off-by: Gregory Greenman --- .../net/wireless/intel/iwlwifi/fw/api/rs.h | 10 +- drivers/net/wireless/intel/iwlwifi/fw/rs.c | 2 + .../net/wireless/intel/iwlwifi/mvm/rs-fw.c | 125 +++++++++++++++++- 3 files changed, 129 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h index 687f804c46b7..ddacd5b45aea 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h @@ -36,14 +36,14 @@ enum iwl_tlc_mng_cfg_flags { * @IWL_TLC_MNG_CH_WIDTH_40MHZ: 40MHZ channel * @IWL_TLC_MNG_CH_WIDTH_80MHZ: 80MHZ channel * @IWL_TLC_MNG_CH_WIDTH_160MHZ: 160MHZ channel - * @IWL_TLC_MNG_CH_WIDTH_LAST: maximum value + * @IWL_TLC_MNG_CH_WIDTH_320MHZ: 320MHZ channel */ enum iwl_tlc_mng_cfg_cw { IWL_TLC_MNG_CH_WIDTH_20MHZ, IWL_TLC_MNG_CH_WIDTH_40MHZ, IWL_TLC_MNG_CH_WIDTH_80MHZ, IWL_TLC_MNG_CH_WIDTH_160MHZ, - IWL_TLC_MNG_CH_WIDTH_LAST = IWL_TLC_MNG_CH_WIDTH_160MHZ, + IWL_TLC_MNG_CH_WIDTH_320MHZ, }; /** @@ -64,8 +64,7 @@ enum iwl_tlc_mng_cfg_chains { * @IWL_TLC_MNG_MODE_HT: enable HT * @IWL_TLC_MNG_MODE_VHT: enable VHT * @IWL_TLC_MNG_MODE_HE: enable HE - * @IWL_TLC_MNG_MODE_INVALID: invalid value - * @IWL_TLC_MNG_MODE_NUM: a count of possible modes + * @IWL_TLC_MNG_MODE_EHT: enable EHT */ enum iwl_tlc_mng_cfg_mode { IWL_TLC_MNG_MODE_CCK = 0, @@ -74,8 +73,7 @@ enum iwl_tlc_mng_cfg_mode { IWL_TLC_MNG_MODE_HT, IWL_TLC_MNG_MODE_VHT, IWL_TLC_MNG_MODE_HE, - IWL_TLC_MNG_MODE_INVALID, - IWL_TLC_MNG_MODE_NUM = IWL_TLC_MNG_MODE_INVALID, + IWL_TLC_MNG_MODE_EHT, }; /** diff --git a/drivers/net/wireless/intel/iwlwifi/fw/rs.c b/drivers/net/wireless/intel/iwlwifi/fw/rs.c index a835214611ce..e128d2e07f38 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/rs.c @@ -218,6 +218,8 @@ int rs_pretty_print_rate(char *buf, int bufsz, const u32 rate) type = "HT"; else if (format == RATE_MCS_HE_MSK) type = "HE"; + else if (format == RATE_MCS_EHT_MSK) + type = "EHT"; else type = "Unknown"; /* shouldn't happen */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c index 2e9081cb6627..f30eeab5505b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c @@ -9,9 +9,11 @@ #include "iwl-op-mode.h" #include "mvm.h" -static u8 rs_fw_bw_from_sta_bw(struct ieee80211_sta *sta) +static u8 rs_fw_bw_from_sta_bw(const struct ieee80211_sta *sta) { switch (sta->deflink.bandwidth) { + case IEEE80211_STA_RX_BW_320: + return IWL_TLC_MNG_CH_WIDTH_320MHZ; case IEEE80211_STA_RX_BW_160: return IWL_TLC_MNG_CH_WIDTH_160MHZ; case IEEE80211_STA_RX_BW_80: @@ -238,6 +240,122 @@ rs_fw_he_set_enabled_rates(const struct ieee80211_sta *sta, } } +static u8 rs_fw_eht_max_nss(u8 rx_nss, u8 tx_nss) +{ + u8 tx = u8_get_bits(tx_nss, IEEE80211_EHT_MCS_NSS_TX); + u8 rx = u8_get_bits(rx_nss, IEEE80211_EHT_MCS_NSS_RX); + /* the max nss that can be used, + * is the min with our tx capa and the peer rx capa. + */ + return min(tx, rx); +} + +#define MAX_NSS_MCS(mcs_num, rx, tx) \ + rs_fw_eht_max_nss((rx)->rx_tx_mcs ##mcs_num## _max_nss, \ + (tx)->rx_tx_mcs ##mcs_num## _max_nss) + +static void rs_fw_set_eht_mcs_nss(__le16 ht_rates[][3], + enum IWL_TLC_MCS_PER_BW bw, + u8 max_nss, u16 mcs_msk) +{ + if (max_nss >= 2) + ht_rates[IWL_TLC_NSS_2][bw] |= cpu_to_le16(mcs_msk); + + if (max_nss >= 1) + ht_rates[IWL_TLC_NSS_1][bw] |= cpu_to_le16(mcs_msk); +} + +static const +struct ieee80211_eht_mcs_nss_supp_bw * +rs_fw_rs_mcs2eht_mcs(enum IWL_TLC_MCS_PER_BW bw, + const struct ieee80211_eht_mcs_nss_supp *eht_mcs) +{ + switch (bw) { + case IWL_TLC_MCS_PER_BW_80: + return &eht_mcs->bw._80; + case IWL_TLC_MCS_PER_BW_160: + return &eht_mcs->bw._160; + case IWL_TLC_MCS_PER_BW_320: + return &eht_mcs->bw._320; + default: + return NULL; + } +} + +static void rs_fw_eht_set_enabled_rates(const struct ieee80211_sta *sta, + struct ieee80211_supported_band *sband, + struct iwl_tlc_config_cmd_v4 *cmd) +{ + /* peer RX mcs capa */ + const struct ieee80211_eht_mcs_nss_supp *eht_rx_mcs = + &sta->deflink.eht_cap.eht_mcs_nss_supp; + /* our TX mcs capa */ + const struct ieee80211_eht_mcs_nss_supp *eht_tx_mcs = + &sband->iftype_data->eht_cap.eht_mcs_nss_supp; + + enum IWL_TLC_MCS_PER_BW bw; + struct ieee80211_eht_mcs_nss_supp_20mhz_only mcs_rx_20; + struct ieee80211_eht_mcs_nss_supp_20mhz_only mcs_tx_20; + + /* peer is 20Mhz only */ + if (!(sta->deflink.he_cap.he_cap_elem.phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) { + mcs_rx_20 = eht_rx_mcs->only_20mhz; + } else { + mcs_rx_20.rx_tx_mcs7_max_nss = eht_rx_mcs->bw._80.rx_tx_mcs9_max_nss; + mcs_rx_20.rx_tx_mcs9_max_nss = eht_rx_mcs->bw._80.rx_tx_mcs9_max_nss; + mcs_rx_20.rx_tx_mcs11_max_nss = eht_rx_mcs->bw._80.rx_tx_mcs11_max_nss; + mcs_rx_20.rx_tx_mcs13_max_nss = eht_rx_mcs->bw._80.rx_tx_mcs13_max_nss; + } + + /* nic is 20Mhz only */ + if (!(sband->iftype_data->he_cap.he_cap_elem.phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) { + mcs_tx_20 = eht_tx_mcs->only_20mhz; + } else { + mcs_tx_20.rx_tx_mcs7_max_nss = eht_tx_mcs->bw._80.rx_tx_mcs9_max_nss; + mcs_tx_20.rx_tx_mcs9_max_nss = eht_tx_mcs->bw._80.rx_tx_mcs9_max_nss; + mcs_tx_20.rx_tx_mcs11_max_nss = eht_tx_mcs->bw._80.rx_tx_mcs11_max_nss; + mcs_tx_20.rx_tx_mcs13_max_nss = eht_tx_mcs->bw._80.rx_tx_mcs13_max_nss; + } + + /* rates for 20/40/80 bw */ + bw = IWL_TLC_MCS_PER_BW_80; + rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw, + MAX_NSS_MCS(7, &mcs_rx_20, &mcs_tx_20), GENMASK(7, 0)); + rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw, + MAX_NSS_MCS(9, &mcs_rx_20, &mcs_tx_20), GENMASK(9, 8)); + rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw, + MAX_NSS_MCS(11, &mcs_rx_20, &mcs_tx_20), GENMASK(11, 10)); + rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw, + MAX_NSS_MCS(13, &mcs_rx_20, &mcs_tx_20), GENMASK(13, 12)); + + /* rate for 160/320 bw */ + for (bw = IWL_TLC_MCS_PER_BW_160; bw <= IWL_TLC_MCS_PER_BW_320; bw++) { + const struct ieee80211_eht_mcs_nss_supp_bw *mcs_rx = + rs_fw_rs_mcs2eht_mcs(bw, eht_rx_mcs); + const struct ieee80211_eht_mcs_nss_supp_bw *mcs_tx = + rs_fw_rs_mcs2eht_mcs(bw, eht_tx_mcs); + + /* got unsuppored index for bw */ + if (!mcs_rx || !mcs_tx) + continue; + + rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw, + MAX_NSS_MCS(9, mcs_rx, mcs_tx), GENMASK(9, 0)); + rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw, + MAX_NSS_MCS(11, mcs_rx, mcs_tx), GENMASK(11, 10)); + rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw, + MAX_NSS_MCS(13, mcs_rx, mcs_tx), GENMASK(13, 12)); + } + + /* the station support only a single receive chain */ + if (sta->deflink.smps_mode == IEEE80211_SMPS_STATIC || + sta->deflink.rx_nss < 2) + memset(cmd->ht_rates[IWL_TLC_NSS_2], 0, + sizeof(cmd->ht_rates[IWL_TLC_NSS_2])); +} + static void rs_fw_set_supp_rates(struct ieee80211_sta *sta, struct ieee80211_supported_band *sband, struct iwl_tlc_config_cmd_v4 *cmd) @@ -258,7 +376,10 @@ static void rs_fw_set_supp_rates(struct ieee80211_sta *sta, cmd->mode = IWL_TLC_MNG_MODE_NON_HT; /* HT/VHT rates */ - if (he_cap->has_he) { + if (sta->deflink.eht_cap.has_eht) { + cmd->mode = IWL_TLC_MNG_MODE_EHT; + rs_fw_eht_set_enabled_rates(sta, sband, cmd); + } else if (he_cap->has_he) { cmd->mode = IWL_TLC_MNG_MODE_HE; rs_fw_he_set_enabled_rates(sta, sband, cmd); } else if (vht_cap->vht_supported) { From 64e7dd3f9be68a53e760f184a6d8049a1121a060 Mon Sep 17 00:00:00 2001 From: Mordechay Goodstein Date: Tue, 22 Nov 2022 22:10:35 +0200 Subject: [PATCH 030/144] wifi: iwlwifi: mvm: add support for EHT 1K aggregation size In 11be we can receive 1K aggregation size so update our max HW rx aggregation for mac80211 usage. Signed-off-by: Mordechay Goodstein Link: https://lore.kernel.org/r/20221122220713.ef0431f64975.Ie4d90c376c822694f4523ff0fb5731f20004c24d@changeid Signed-off-by: Gregory Greenman --- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 9699433137bc..2a4db13c9dcf 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -1077,6 +1077,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, static const u8 no_reclaim_cmds[] = { TX_CMD, }; + u32 max_agg; size_t scan_size; u32 min_backoff; struct iwl_mvm_csme_conn_info *csme_conn_info __maybe_unused; @@ -1098,12 +1099,17 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, if (!hw) return NULL; - hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE; + if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) + max_agg = IEEE80211_MAX_AMPDU_BUF_EHT; + else + max_agg = IEEE80211_MAX_AMPDU_BUF_HE; + + hw->max_rx_aggregation_subframes = max_agg; if (cfg->max_tx_agg_size) hw->max_tx_aggregation_subframes = cfg->max_tx_agg_size; else - hw->max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE; + hw->max_tx_aggregation_subframes = max_agg; op_mode = hw->priv; From cb63eb438ee9264b086ec7e0b2f01c56983ebe61 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Tue, 22 Nov 2022 22:10:36 +0200 Subject: [PATCH 031/144] wifi: iwlwifi: mvm: support PPE Thresholds for EHT In an EHT connection fill in the HE context command with PPE Thresholds field from EHT Capabilities IE. If PPE Thresholds field presents only in HE Capabilities IE, take the thresholds from there. If it's not present in both - set the Common Nominal Packet Padding field from EHT Capabilities IE. Signed-off-by: Miri Korenblit Link: https://lore.kernel.org/r/20221122220713.062ac86110f2.I0bcd5ee35f4d987a49f15501985f90e0d3ce7590@changeid Signed-off-by: Gregory Greenman --- .../net/wireless/intel/iwlwifi/fw/api/mac.h | 6 +- .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 177 +++++++++++++++--- 2 files changed, 153 insertions(+), 30 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h index 9b7caf968346..e3eda251c728 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2021 Intel Corporation + * Copyright (C) 2012-2014, 2018-2022 Intel Corporation * Copyright (C) 2017 Intel Deutschland GmbH */ #ifndef __iwl_fw_api_mac_h__ @@ -398,7 +398,7 @@ struct iwl_he_backoff_conf { * @IWL_HE_PKT_EXT_64QAM: 64-QAM * @IWL_HE_PKT_EXT_256QAM: 256-QAM * @IWL_HE_PKT_EXT_1024QAM: 1024-QAM - * @IWL_HE_PKT_EXT_RESERVED: reserved value + * @IWL_HE_PKT_EXT_4096QAM: 4096-QAM, for EHT only * @IWL_HE_PKT_EXT_NONE: not defined */ enum iwl_he_pkt_ext_constellations { @@ -408,7 +408,7 @@ enum iwl_he_pkt_ext_constellations { IWL_HE_PKT_EXT_64QAM, IWL_HE_PKT_EXT_256QAM, IWL_HE_PKT_EXT_1024QAM, - IWL_HE_PKT_EXT_RESERVED, + IWL_HE_PKT_EXT_4096QAM, IWL_HE_PKT_EXT_NONE, }; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 57aa10466637..3fba69554f83 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1825,7 +1825,8 @@ static u8 iwl_mvm_he_get_ppe_val(u8 *ppe, u8 ppe_pos_bit) static void iwl_mvm_parse_ppe(struct iwl_mvm *mvm, struct iwl_he_pkt_ext_v2 *pkt_ext, u8 nss, - u8 ru_index_bitmap, u8 *ppe, u8 ppe_pos_bit) + u8 ru_index_bitmap, u8 *ppe, u8 ppe_pos_bit, + bool inheritance) { int i; @@ -1851,14 +1852,25 @@ static void iwl_mvm_parse_ppe(struct iwl_mvm *mvm, bw++) { ru_index_tmp >>= 1; - if (!(ru_index_tmp & 1)) - continue; + /* + * According to the 11be spec, if for a specific BW the PPE Thresholds + * isn't present - it should inherit the thresholds from the last + * BW for which we had PPE Thresholds. In 11ax though, we don't have + * this inheritance - continue in this case + */ + if (!(ru_index_tmp & 1)) { + if (inheritance) + goto set_thresholds; + else + continue; + } high_th = iwl_mvm_he_get_ppe_val(ppe, ppe_pos_bit); ppe_pos_bit += IEEE80211_PPE_THRES_INFO_PPET_SIZE; low_th = iwl_mvm_he_get_ppe_val(ppe, ppe_pos_bit); ppe_pos_bit += IEEE80211_PPE_THRES_INFO_PPET_SIZE; +set_thresholds: pkt_ext->pkt_ext_qam_th[i][bw][0] = low_th; pkt_ext->pkt_ext_qam_th[i][bw][1] = high_th; } @@ -1867,7 +1879,8 @@ static void iwl_mvm_parse_ppe(struct iwl_mvm *mvm, static void iwl_mvm_set_pkt_ext_from_he_ppe(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - struct iwl_he_pkt_ext_v2 *pkt_ext) + struct iwl_he_pkt_ext_v2 *pkt_ext, + bool inheritance) { u8 nss = (sta->deflink.he_cap.ppe_thres[0] & IEEE80211_PPE_THRES_NSS_MASK) + 1; u8 *ppe = &sta->deflink.he_cap.ppe_thres[0]; @@ -1877,7 +1890,8 @@ static void iwl_mvm_set_pkt_ext_from_he_ppe(struct iwl_mvm *mvm, /* Starting after PPE header */ u8 ppe_pos_bit = IEEE80211_HE_PPE_THRES_INFO_HEADER_SIZE; - iwl_mvm_parse_ppe(mvm, pkt_ext, nss, ru_index_bitmap, ppe, ppe_pos_bit); + iwl_mvm_parse_ppe(mvm, pkt_ext, nss, ru_index_bitmap, ppe, ppe_pos_bit, + inheritance); } static void iwl_mvm_set_pkt_ext_from_nominal_padding(struct iwl_he_pkt_ext_v2 *pkt_ext, @@ -1888,16 +1902,18 @@ static void iwl_mvm_set_pkt_ext_from_nominal_padding(struct iwl_he_pkt_ext_v2 *p int high_th = -1; int i; + /* all the macros are the same for EHT and HE */ switch (nominal_padding) { - case IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_0US: + case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_0US: low_th = IWL_HE_PKT_EXT_NONE; high_th = IWL_HE_PKT_EXT_NONE; break; - case IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_8US: + case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_8US: low_th = IWL_HE_PKT_EXT_BPSK; high_th = IWL_HE_PKT_EXT_NONE; break; - case IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US: + case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_16US: + case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_20US: low_th = IWL_HE_PKT_EXT_NONE; high_th = IWL_HE_PKT_EXT_BPSK; break; @@ -1920,6 +1936,31 @@ static void iwl_mvm_set_pkt_ext_from_nominal_padding(struct iwl_he_pkt_ext_v2 *p } } +static void iwl_mvm_get_optimal_ppe_info(struct iwl_he_pkt_ext_v2 *pkt_ext, + u8 nominal_padding) +{ + int i; + + for (i = 0; i < MAX_HE_SUPP_NSS; i++) { + u8 bw; + + for (bw = 0; bw < ARRAY_SIZE(pkt_ext->pkt_ext_qam_th[i]); + bw++) { + u8 *qam_th = &pkt_ext->pkt_ext_qam_th[i][bw][0]; + + if (nominal_padding > + IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_8US && + qam_th[1] == IWL_HE_PKT_EXT_NONE) + qam_th[1] = IWL_HE_PKT_EXT_4096QAM; + else if (nominal_padding == + IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_8US && + qam_th[0] == IWL_HE_PKT_EXT_NONE && + qam_th[1] == IWL_HE_PKT_EXT_NONE) + qam_th[0] = IWL_HE_PKT_EXT_4096QAM; + } + } +} + static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u8 sta_id) { @@ -1943,6 +1984,7 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm, struct ieee80211_chanctx_conf *chanctx_conf; const struct ieee80211_supported_band *sband; void *cmd; + u8 nominal_padding; if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_MBSSID_HE)) ver = 1; @@ -2032,22 +2074,96 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm, memset(&sta_ctxt_cmd.pkt_ext, IWL_HE_PKT_EXT_NONE, sizeof(sta_ctxt_cmd.pkt_ext)); - /* If PPE Thresholds exist, parse them into a FW-familiar format. */ - if (sta->deflink.he_cap.he_cap_elem.phy_cap_info[6] & - IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) { - iwl_mvm_set_pkt_ext_from_he_ppe(mvm, sta, - &sta_ctxt_cmd.pkt_ext); - flags |= STA_CTXT_HE_PACKET_EXT; - /* PPE Thresholds doesn't exist - set the API PPE values - * according to Common Nominal Packet Padding fiels. */ - } else { - u8 nominal_padding = - u8_get_bits(sta->deflink.he_cap.he_cap_elem.phy_cap_info[9], - IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK); - if (nominal_padding != IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_RESERVED) + if (sta->deflink.eht_cap.has_eht) { + nominal_padding = + u8_get_bits(sta->deflink.eht_cap.eht_cap_elem.phy_cap_info[5], + IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK); + + /* If PPE Thresholds exists, parse them into a FW-familiar format. */ + if (sta->deflink.eht_cap.eht_cap_elem.phy_cap_info[5] & + IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT) { + u8 nss = (sta->deflink.eht_cap.eht_ppe_thres[0] & + IEEE80211_EHT_PPE_THRES_NSS_MASK) + 1; + u8 *ppe = &sta->deflink.eht_cap.eht_ppe_thres[0]; + u8 ru_index_bitmap = + u16_get_bits(*ppe, + IEEE80211_EHT_PPE_THRES_RU_INDEX_BITMASK_MASK); + /* Starting after PPE header */ + u8 ppe_pos_bit = IEEE80211_EHT_PPE_THRES_INFO_HEADER_SIZE; + + iwl_mvm_parse_ppe(mvm, + &sta_ctxt_cmd.pkt_ext, + nss, ru_index_bitmap, ppe, + ppe_pos_bit, true); + flags |= STA_CTXT_HE_PACKET_EXT; + /* EHT PPE Thresholds doesn't exist - set the API according to HE PPE Tresholds*/ + } else if (sta->deflink.he_cap.he_cap_elem.phy_cap_info[6] & + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) { + struct iwl_he_pkt_ext_v2 *pkt_ext = + &sta_ctxt_cmd.pkt_ext; + + /* + * Even though HE Capabilities IE doesn't contain PPE + * Thresholds for BW 320Mhz, thresholds for this BW will + * be filled in with the same values as 160Mhz, due to + * the inheritance, as required. + */ + iwl_mvm_set_pkt_ext_from_he_ppe(mvm, sta, pkt_ext, + true); + + /* + * According to the requirements, for MCSs 12-13 the maximum value between + * HE PPE Threshold and Common Nominal Packet Padding needs to be taken + */ + iwl_mvm_get_optimal_ppe_info(pkt_ext, nominal_padding); + + flags |= STA_CTXT_HE_PACKET_EXT; + + /* + * if PPE Thresholds doesn't present in both EHT IE and HE IE - + * take the Thresholds from Common Nominal Packet Padding field + */ + } else { iwl_mvm_set_pkt_ext_from_nominal_padding(&sta_ctxt_cmd.pkt_ext, nominal_padding, &flags); + } + } else if (sta->deflink.he_cap.has_he) { + /* If PPE Thresholds exist, parse them into a FW-familiar format. */ + if (sta->deflink.he_cap.he_cap_elem.phy_cap_info[6] & + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) { + iwl_mvm_set_pkt_ext_from_he_ppe(mvm, sta, + &sta_ctxt_cmd.pkt_ext, + false); + flags |= STA_CTXT_HE_PACKET_EXT; + /* + * PPE Thresholds doesn't exist - set the API PPE values + * according to Common Nominal Packet Padding field. + */ + } else { + nominal_padding = + u8_get_bits(sta->deflink.he_cap.he_cap_elem.phy_cap_info[9], + IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK); + if (nominal_padding != IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_RESERVED) + iwl_mvm_set_pkt_ext_from_nominal_padding(&sta_ctxt_cmd.pkt_ext, + nominal_padding, + &flags); + } + } + + for (i = 0; i < MAX_HE_SUPP_NSS; i++) { + int bw; + + for (bw = 0; + bw < ARRAY_SIZE(sta_ctxt_cmd.pkt_ext.pkt_ext_qam_th[i]); + bw++) { + u8 *qam_th = + &sta_ctxt_cmd.pkt_ext.pkt_ext_qam_th[i][bw][0]; + + IWL_DEBUG_HT(mvm, + "PPE table: nss[%d] bw[%d] PPET8 = %d, PPET16 = %d\n", + i, bw, qam_th[0], qam_th[1]); + } } if (sta->deflink.he_cap.he_cap_elem.mac_cap_info[2] & @@ -2195,8 +2311,10 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, * interface was added. */ if (changes & BSS_CHANGED_ASSOC && vif->cfg.assoc) { - if (vif->bss_conf.he_support && - !iwlwifi_mod_params.disable_11ax) + if ((vif->bss_conf.he_support && + !iwlwifi_mod_params.disable_11ax) || + (vif->bss_conf.eht_support && + !iwlwifi_mod_params.disable_11be)) iwl_mvm_cfg_he_sta(mvm, vif, mvmvif->ap_sta_id); iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif); @@ -2204,8 +2322,11 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, /* Update MU EDCA params */ if (changes & BSS_CHANGED_QOS && mvmvif->associated && - vif->cfg.assoc && vif->bss_conf.he_support && - !iwlwifi_mod_params.disable_11ax) + vif->cfg.assoc && + ((vif->bss_conf.he_support && + !iwlwifi_mod_params.disable_11ax) || + (vif->bss_conf.eht_support && + !iwlwifi_mod_params.disable_11be))) iwl_mvm_cfg_he_sta(mvm, vif, mvmvif->ap_sta_id); /* @@ -3218,8 +3339,10 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, vif->bss_conf.he_support = sta->deflink.he_cap.has_he; mvmvif->ap_assoc_sta_count++; iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); - if (vif->bss_conf.he_support && - !iwlwifi_mod_params.disable_11ax) + if ((vif->bss_conf.he_support && + !iwlwifi_mod_params.disable_11ax) || + (vif->bss_conf.eht_support && + !iwlwifi_mod_params.disable_11be)) iwl_mvm_cfg_he_sta(mvm, vif, mvm_sta->sta_id); } else if (vif->type == NL80211_IFTYPE_STATION) { vif->bss_conf.he_support = sta->deflink.he_cap.has_he; From 3895f1609c2e4e274a9a02b412e318a3b11e8032 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 22 Nov 2022 22:10:37 +0200 Subject: [PATCH 032/144] wifi: iwlwifi: mvm: set HE PHY bandwidth according to band The bits are reserved on the opposite bands, so we shouldn't always send them, only the 2G bit on 2.4 GHz and the 5G bits on 5/6GHz. Fix that. Signed-off-by: Johannes Berg Link: https://lore.kernel.org/r/20221122220713.0a075d00c796.Ib4cac0b7f90dfadebceceb1e07c8cdfd7a4138f6@changeid Signed-off-by: Gregory Greenman --- .../wireless/intel/iwlwifi/iwl-nvm-parse.c | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 2b43fff4d3f9..e83382ded8af 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2005-2014, 2018-2021 Intel Corporation + * Copyright (C) 2005-2014, 2018-2022 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -571,10 +571,6 @@ static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = { IEEE80211_HE_MAC_CAP5_UL_2x996_TONE_RU | IEEE80211_HE_MAC_CAP5_HE_DYNAMIC_SM_PS | IEEE80211_HE_MAC_CAP5_HT_VHT_TRIG_FRAME_RX, - .phy_cap_info[0] = - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G | - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G, .phy_cap_info[1] = IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK | IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | @@ -716,9 +712,6 @@ static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = { IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8, .mac_cap_info[3] = IEEE80211_HE_MAC_CAP3_OMI_CONTROL, - .phy_cap_info[0] = - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G | - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G, .phy_cap_info[1] = IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD, .phy_cap_info[2] = @@ -874,9 +867,23 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, iftype_data->he_cap.he_cap_elem.mac_cap_info[3] |= IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3; - if (is_ap && iwlwifi_mod_params.nvm_file) + switch (sband->band) { + case NL80211_BAND_2GHZ: iftype_data->he_cap.he_cap_elem.phy_cap_info[0] |= - IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G; + break; + case NL80211_BAND_6GHZ: + case NL80211_BAND_5GHZ: + iftype_data->he_cap.he_cap_elem.phy_cap_info[0] |= + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G; + if (!is_ap || iwlwifi_mod_params.nvm_file) + iftype_data->he_cap.he_cap_elem.phy_cap_info[0] |= + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; + break; + default: + WARN_ON(1); + break; + } if ((tx_chains & rx_chains) == ANT_AB) { iftype_data->he_cap.he_cap_elem.phy_cap_info[2] |= From 35ea5f619480e81efb652c8e1cab5fd1a3964ae3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 22 Nov 2022 22:10:38 +0200 Subject: [PATCH 033/144] wifi: iwlwifi: mvm: advertise 320 MHz in 6 GHz only conditionally We can't have it in AP mode unless for debug, since we don't have 160 MHz in HE PHY capabilities, and also set it only in 6 GHz even if (unlike in HE) it's always defined. Signed-off-by: Johannes Berg Link: https://lore.kernel.org/r/20221122220713.293ef3e5a1c4.I867526a6bd3f93ee50076ff359436dfb5ec6101c@changeid Signed-off-by: Gregory Greenman --- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index e83382ded8af..b59e61a76b70 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -873,6 +873,10 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G; break; case NL80211_BAND_6GHZ: + if (!is_ap || iwlwifi_mod_params.nvm_file) + iftype_data->eht_cap.eht_cap_elem.phy_cap_info[0] |= + IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ; + fallthrough; case NL80211_BAND_5GHZ: iftype_data->he_cap.he_cap_elem.phy_cap_info[0] |= IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G; From 3f44d44f0685778e8b07e52dc1959dd40f1ba964 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 22 Nov 2022 22:10:39 +0200 Subject: [PATCH 034/144] wifi: iwlwifi: nvm-parse: support A-MPDU in EHT 2.4 GHz We support large A-MPDU in EHT in 2.4 GHz, so add the right bits for that in the EHT capabilities. Signed-off-by: Johannes Berg Link: https://lore.kernel.org/r/20221122220713.3e7449760415.Id394d7fd0acaca48d429feccda4e66dbadaab993@changeid Signed-off-by: Gregory Greenman --- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index b59e61a76b70..3a646bce7b42 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -871,6 +871,9 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, case NL80211_BAND_2GHZ: iftype_data->he_cap.he_cap_elem.phy_cap_info[0] |= IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G; + iftype_data->eht_cap.eht_cap_elem.mac_cap_info[0] |= + u8_encode_bits(IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_11454, + IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_MASK); break; case NL80211_BAND_6GHZ: if (!is_ap || iwlwifi_mod_params.nvm_file) From 0e21ec6edbb535126bef3a471cc5ec10374a81bb Mon Sep 17 00:00:00 2001 From: Abhishek Naik Date: Tue, 22 Nov 2022 22:10:41 +0200 Subject: [PATCH 035/144] wifi: iwlwifi: nvm: Update EHT capabilities for GL device Update EHT MAC and PHY capabilities for GL device for station and softap interface. Add relevant code in nvm_fixup_sband_iftd. Signed-off-by: Abhishek Naik Link: https://lore.kernel.org/r/20221122220713.1da816ab7ffc.Ief570e27007c9e2ad3a97bee4b074e2fc1c75bae@changeid Signed-off-by: Gregory Greenman --- .../wireless/intel/iwlwifi/iwl-nvm-parse.c | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 3a646bce7b42..476b90f32626 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -950,6 +950,29 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, break; } + if (CSR_HW_REV_TYPE(trans->hw_rev) == IWL_CFG_MAC_TYPE_GL && + iftype_data->eht_cap.has_eht) { + iftype_data->eht_cap.eht_cap_elem.mac_cap_info[0] &= + ~(IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS | + IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1 | + IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE2); + iftype_data->eht_cap.eht_cap_elem.phy_cap_info[3] &= + ~(IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO | + IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK); + iftype_data->eht_cap.eht_cap_elem.phy_cap_info[4] &= + ~(IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO | + IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP); + iftype_data->eht_cap.eht_cap_elem.phy_cap_info[5] &= + ~IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK; + iftype_data->eht_cap.eht_cap_elem.phy_cap_info[6] &= + ~(IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK | + IEEE80211_EHT_PHY_CAP6_EHT_DUP_6GHZ_SUPP); + } + if (fw_has_capa(&fw->ucode_capa, IWL_UCODE_TLV_CAPA_BROADCAST_TWT)) iftype_data->he_cap.he_cap_elem.mac_cap_info[2] |= IEEE80211_HE_MAC_CAP2_BCAST_TWT; From 56731878c68255745dc0116300d531b24d950f0d Mon Sep 17 00:00:00 2001 From: Daniel Gabay Date: Wed, 23 Nov 2022 23:02:05 +0200 Subject: [PATCH 036/144] wifi: iwlwifi: mvm: print OTP info after alive These are register values which are needed for debug regardless of alive fail/success. Signed-off-by: Daniel Gabay Link: https://lore.kernel.org/r/20221123225313.a5ae3679491b.Ic618934df084a9b269e56d892665523c79e40eee@changeid Signed-off-by: Gregory Greenman --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index ef43f6971cd9..b3101d12a0a1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -354,6 +354,18 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, */ ret = iwl_wait_notification(&mvm->notif_wait, &alive_wait, MVM_UCODE_ALIVE_TIMEOUT); + + if (mvm->trans->trans_cfg->device_family == + IWL_DEVICE_FAMILY_AX210) { + /* print these registers regardless of alive fail/success */ + IWL_INFO(mvm, "WFPM_UMAC_PD_NOTIFICATION: 0x%x\n", + iwl_read_umac_prph(mvm->trans, WFPM_ARC1_PD_NOTIFICATION)); + IWL_INFO(mvm, "WFPM_LMAC2_PD_NOTIFICATION: 0x%x\n", + iwl_read_umac_prph(mvm->trans, WFPM_LMAC2_PD_NOTIFICATION)); + IWL_INFO(mvm, "WFPM_AUTH_KEY_0: 0x%x\n", + iwl_read_umac_prph(mvm->trans, SB_MODIFY_CFG_FLAG)); + } + if (ret) { struct iwl_trans *trans = mvm->trans; From 0473cbae2137b963bd0eaa74336131cb1d3bc6c3 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Wed, 23 Nov 2022 23:02:06 +0200 Subject: [PATCH 037/144] wifi: iwlwifi: mvm: fix double free on tx path. We see kernel crashes and lockups and KASAN errors related to ax210 firmware crashes. One of the KASAN dumps pointed at the tx path, and it appears there is indeed a way to double-free an skb. If iwl_mvm_tx_skb_sta returns non-zero, then the 'skb' sent into the method will be freed. But, in case where we build TSO skb buffer, the skb may also be freed in error case. So, return 0 in that particular error case and do cleanup manually. BUG: KASAN: use-after-free in __list_del_entry_valid+0x12/0x90 iwlwifi 0000:06:00.0: 0x00000000 | tsf hi Read of size 8 at addr ffff88813cfa4ba0 by task btserver/9650 CPU: 4 PID: 9650 Comm: btserver Tainted: G W 5.19.8+ #5 iwlwifi 0000:06:00.0: 0x00000000 | time gp1 Hardware name: Default string Default string/SKYBAY, BIOS 5.12 02/19/2019 Call Trace: dump_stack_lvl+0x55/0x6d print_report.cold.12+0xf2/0x684 iwlwifi 0000:06:00.0: 0x1D0915A8 | time gp2 ? __list_del_entry_valid+0x12/0x90 kasan_report+0x8b/0x180 iwlwifi 0000:06:00.0: 0x00000001 | uCode revision type ? __list_del_entry_valid+0x12/0x90 __list_del_entry_valid+0x12/0x90 iwlwifi 0000:06:00.0: 0x00000048 | uCode version major tcp_update_skb_after_send+0x5d/0x170 __tcp_transmit_skb+0xb61/0x15c0 iwlwifi 0000:06:00.0: 0xDAA05125 | uCode version minor ? __tcp_select_window+0x490/0x490 iwlwifi 0000:06:00.0: 0x00000420 | hw version ? trace_kmalloc_node+0x29/0xd0 ? __kmalloc_node_track_caller+0x12a/0x260 ? memset+0x1f/0x40 ? __build_skb_around+0x125/0x150 ? __alloc_skb+0x1d4/0x220 ? skb_zerocopy_clone+0x55/0x230 iwlwifi 0000:06:00.0: 0x00489002 | board version ? kmalloc_reserve+0x80/0x80 ? rcu_read_lock_bh_held+0x60/0xb0 tcp_write_xmit+0x3f1/0x24d0 iwlwifi 0000:06:00.0: 0x034E001C | hcmd ? __check_object_size+0x180/0x350 iwlwifi 0000:06:00.0: 0x24020000 | isr0 tcp_sendmsg_locked+0x8a9/0x1520 iwlwifi 0000:06:00.0: 0x01400000 | isr1 ? tcp_sendpage+0x50/0x50 iwlwifi 0000:06:00.0: 0x48F0000A | isr2 ? lock_release+0xb9/0x400 ? tcp_sendmsg+0x14/0x40 iwlwifi 0000:06:00.0: 0x00C3080C | isr3 ? lock_downgrade+0x390/0x390 ? do_raw_spin_lock+0x114/0x1d0 iwlwifi 0000:06:00.0: 0x00200000 | isr4 ? rwlock_bug.part.2+0x50/0x50 iwlwifi 0000:06:00.0: 0x034A001C | last cmd Id ? rwlock_bug.part.2+0x50/0x50 ? lockdep_hardirqs_on_prepare+0xe/0x200 iwlwifi 0000:06:00.0: 0x0000C2F0 | wait_event ? __local_bh_enable_ip+0x87/0xe0 ? inet_send_prepare+0x220/0x220 iwlwifi 0000:06:00.0: 0x000000C4 | l2p_control tcp_sendmsg+0x22/0x40 sock_sendmsg+0x5f/0x70 iwlwifi 0000:06:00.0: 0x00010034 | l2p_duration __sys_sendto+0x19d/0x250 iwlwifi 0000:06:00.0: 0x00000007 | l2p_mhvalid ? __ia32_sys_getpeername+0x40/0x40 iwlwifi 0000:06:00.0: 0x00000000 | l2p_addr_match ? rcu_read_lock_held_common+0x12/0x50 ? rcu_read_lock_sched_held+0x5a/0xd0 ? rcu_read_lock_bh_held+0xb0/0xb0 ? rcu_read_lock_sched_held+0x5a/0xd0 ? rcu_read_lock_sched_held+0x5a/0xd0 ? lock_release+0xb9/0x400 ? lock_downgrade+0x390/0x390 ? ktime_get+0x64/0x130 ? ktime_get+0x8d/0x130 ? rcu_read_lock_held_common+0x12/0x50 ? rcu_read_lock_sched_held+0x5a/0xd0 ? rcu_read_lock_held_common+0x12/0x50 ? rcu_read_lock_sched_held+0x5a/0xd0 ? rcu_read_lock_bh_held+0xb0/0xb0 ? rcu_read_lock_bh_held+0xb0/0xb0 __x64_sys_sendto+0x6f/0x80 do_syscall_64+0x34/0xb0 entry_SYSCALL_64_after_hwframe+0x46/0xb0 RIP: 0033:0x7f1d126e4531 Code: 00 00 00 00 0f 1f 44 00 00 f3 0f 1e fa 48 8d 05 35 80 0c 00 41 89 ca 8b 00 85 c0 75 1c 45 31 c9 45 31 c0 b8 2c 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 67 c3 66 0f 1f 44 00 00 55 48 83 ec 20 48 89 RSP: 002b:00007ffe21a679d8 EFLAGS: 00000246 ORIG_RAX: 000000000000002c RAX: ffffffffffffffda RBX: 000000000000ffdc RCX: 00007f1d126e4531 RDX: 0000000000010000 RSI: 000000000374acf0 RDI: 0000000000000014 RBP: 00007ffe21a67ac0 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000010 R13: 0000000000000000 R14: 0000000000000001 R15: 0000000000000000 Allocated by task 9650: kasan_save_stack+0x1c/0x40 __kasan_slab_alloc+0x6d/0x90 kmem_cache_alloc_node+0xf3/0x2b0 __alloc_skb+0x191/0x220 tcp_stream_alloc_skb+0x3f/0x330 tcp_sendmsg_locked+0x67c/0x1520 tcp_sendmsg+0x22/0x40 sock_sendmsg+0x5f/0x70 __sys_sendto+0x19d/0x250 __x64_sys_sendto+0x6f/0x80 do_syscall_64+0x34/0xb0 entry_SYSCALL_64_after_hwframe+0x46/0xb0 Freed by task 9650: kasan_save_stack+0x1c/0x40 kasan_set_track+0x21/0x30 kasan_set_free_info+0x20/0x30 __kasan_slab_free+0x102/0x170 kmem_cache_free+0xc8/0x3e0 iwl_mvm_mac_itxq_xmit+0x124/0x270 [iwlmvm] ieee80211_queue_skb+0x874/0xd10 [mac80211] ieee80211_xmit_fast+0xf80/0x1180 [mac80211] __ieee80211_subif_start_xmit+0x287/0x680 [mac80211] ieee80211_subif_start_xmit+0xcd/0x730 [mac80211] dev_hard_start_xmit+0xf6/0x420 __dev_queue_xmit+0x165b/0x1b50 ip_finish_output2+0x66e/0xfb0 __ip_finish_output+0x487/0x6d0 ip_output+0x11c/0x350 __ip_queue_xmit+0x36b/0x9d0 __tcp_transmit_skb+0xb35/0x15c0 tcp_write_xmit+0x3f1/0x24d0 tcp_sendmsg_locked+0x8a9/0x1520 tcp_sendmsg+0x22/0x40 sock_sendmsg+0x5f/0x70 __sys_sendto+0x19d/0x250 __x64_sys_sendto+0x6f/0x80 do_syscall_64+0x34/0xb0 entry_SYSCALL_64_after_hwframe+0x46/0xb0 The buggy address belongs to the object at ffff88813cfa4b40 which belongs to the cache skbuff_fclone_cache of size 472 The buggy address is located 96 bytes inside of 472-byte region [ffff88813cfa4b40, ffff88813cfa4d18) The buggy address belongs to the physical page: page:ffffea0004f3e900 refcount:1 mapcount:0 mapping:0000000000000000 index:0xffff88813cfa6c40 pfn:0x13cfa4 head:ffffea0004f3e900 order:2 compound_mapcount:0 compound_pincount:0 flags: 0x5fff8000010200(slab|head|node=0|zone=2|lastcpupid=0x3fff) raw: 005fff8000010200 ffffea0004656b08 ffffea0008e8cf08 ffff8881081a5240 raw: ffff88813cfa6c40 0000000000170015 00000001ffffffff 0000000000000000 page dumped because: kasan: bad access detected Memory state around the buggy address: ffff88813cfa4a80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff88813cfa4b00: fc fc fc fc fc fc fc fc fa fb fb fb fb fb fb fb >ffff88813cfa4b80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ^ ffff88813cfa4c00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ffff88813cfa4c80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ================================================================== Fixes: 08f7d8b69aaf ("iwlwifi: mvm: bring back mvm GSO code") Link: https://lore.kernel.org/linux-wireless/20220928193057.16132-1-greearb@candelatech.com/ Tested-by: Amol Jawale Signed-off-by: Ben Greear Link: https://lore.kernel.org/r/20221123225313.21b1ee31d666.I3b3ba184433dd2a544d91eeeda29b467021824ae@changeid Signed-off-by: Gregory Greenman --- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index f460332333a7..fadaa683a416 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -1215,6 +1215,7 @@ int iwl_mvm_tx_skb_sta(struct iwl_mvm *mvm, struct sk_buff *skb, struct sk_buff_head mpdus_skbs; unsigned int payload_len; int ret; + struct sk_buff *orig_skb = skb; if (WARN_ON_ONCE(!mvmsta)) return -1; @@ -1247,8 +1248,17 @@ int iwl_mvm_tx_skb_sta(struct iwl_mvm *mvm, struct sk_buff *skb, ret = iwl_mvm_tx_mpdu(mvm, skb, &info, sta); if (ret) { + /* Free skbs created as part of TSO logic that have not yet been dequeued */ __skb_queue_purge(&mpdus_skbs); - return ret; + /* skb here is not necessarily same as skb that entered this method, + * so free it explicitly. + */ + if (skb == orig_skb) + ieee80211_free_txskb(mvm->hw, skb); + else + kfree_skb(skb); + /* there was error, but we consumed skb one way or another, so return 0 */ + return 0; } } From b8133439bda70ff24e3bdb89bb55d204c3fea606 Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Wed, 23 Nov 2022 23:02:07 +0200 Subject: [PATCH 038/144] wifi: iwlwifi: mvm: trigger PCI re-enumeration in case of PLDR sync When doing the PLDR flow, the fw goes through a re-read and needs PCI re-enumeration in order to recover. In this case, skip the mac start retry and fw dumps as all the fw and registers are invalid until the PCI re-enumeration. In addition, print the register that shows the re-read counter when loading the fw. Signed-off-by: Avraham Stern Link: https://lore.kernel.org/r/20221123225313.9ae77968961e.Ie06e886cef4b5921b65dacb7724db1276bed38cb@changeid Signed-off-by: Gregory Greenman --- drivers/net/wireless/intel/iwlwifi/iwl-prph.h | 1 + .../net/wireless/intel/iwlwifi/iwl-trans.h | 1 + drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 12 ++- .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 10 +++ drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 2 + drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 3 + .../net/wireless/intel/iwlwifi/pcie/trans.c | 85 ++++++++++--------- 7 files changed, 73 insertions(+), 41 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h index 82cf904e0b6d..62ce116d3783 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h @@ -368,6 +368,7 @@ enum { #define CNVR_AUX_MISC_CHIP 0xA2B800 #define CNVR_SCU_SD_REGS_SD_REG_DIG_DCDC_VTRIM 0xA29890 #define CNVR_SCU_SD_REGS_SD_REG_ACTIVE_VDIG_MIRROR 0xA29938 +#define CNVI_SCU_SEQ_DATA_DW9 0xA27488 #define PREG_AUX_BUS_WPROT_0 0xA04CC0 diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index d659ccd065f7..32bd7f19f1d5 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -1542,5 +1542,6 @@ void iwl_trans_free(struct iwl_trans *trans); ******************************************************/ int __must_check iwl_pci_register_driver(void); void iwl_pci_unregister_driver(void); +void iwl_trans_pcie_remove(struct iwl_trans *trans, bool rescan); #endif /* __iwl_trans_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index b3101d12a0a1..74354d044db9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -364,6 +364,8 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, iwl_read_umac_prph(mvm->trans, WFPM_LMAC2_PD_NOTIFICATION)); IWL_INFO(mvm, "WFPM_AUTH_KEY_0: 0x%x\n", iwl_read_umac_prph(mvm->trans, SB_MODIFY_CFG_FLAG)); + IWL_INFO(mvm, "CNVI_SCU_SEQ_DATA_DW9: 0x%x\n", + iwl_read_prph(mvm->trans, CNVI_SCU_SEQ_DATA_DW9)); } if (ret) { @@ -402,7 +404,7 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, UREG_LMAC2_CURRENT_PC)); } - if (ret == -ETIMEDOUT) + if (ret == -ETIMEDOUT && !mvm->pldr_sync) iwl_fw_dbg_error_collect(&mvm->fwrt, FW_DBG_TRIGGER_ALIVE_TIMEOUT); @@ -1479,18 +1481,22 @@ int iwl_mvm_up(struct iwl_mvm *mvm) return ret; sb_cfg = iwl_read_umac_prph(mvm->trans, SB_MODIFY_CFG_FLAG); - if (!(sb_cfg & SB_CFG_RESIDES_IN_OTP_MASK) && iwl_mei_pldr_req()) + mvm->pldr_sync = !(sb_cfg & SB_CFG_RESIDES_IN_OTP_MASK); + if (mvm->pldr_sync && iwl_mei_pldr_req()) return ret; ret = iwl_mvm_load_rt_fw(mvm); if (ret) { IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret); - if (ret != -ERFKILL) + if (ret != -ERFKILL && !mvm->pldr_sync) iwl_fw_dbg_error_collect(&mvm->fwrt, FW_DBG_TRIGGER_DRIVER); goto error; } + /* FW loaded successfully */ + mvm->pldr_sync = false; + iwl_get_shared_mem_conf(&mvm->fwrt); ret = iwl_mvm_sf_update(mvm, NULL, false); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 3fba69554f83..5273ade71117 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1068,6 +1068,16 @@ static int iwl_mvm_mac_start(struct ieee80211_hw *hw) if (!ret) break; + /* + * In PLDR sync PCI re-enumeration is needed. no point to retry + * mac start before that. + */ + if (mvm->pldr_sync) { + iwl_mei_alive_notif(false); + iwl_trans_pcie_remove(mvm->trans, true); + break; + } + IWL_ERR(mvm, "mac start retry %d\n", retry); } clear_bit(IWL_MVM_STATUS_STARTING, &mvm->status); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 962e304fc2b1..ce6b701f3f4c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1105,6 +1105,8 @@ struct iwl_mvm { unsigned long last_reset_or_resume_time_jiffies; bool sta_remove_requires_queue_remove; + + bool pldr_sync; }; /* Extract MVM priv from op_mode and _hw */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 2a4db13c9dcf..e78f5beaa2d0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -1888,6 +1888,9 @@ static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode, bool sync) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + if (mvm->pldr_sync) + return; + if (!test_bit(STATUS_TRANS_DEAD, &mvm->trans->status) && !test_and_clear_bit(IWL_MVM_STATUS_SUPPRESS_ERROR_LOG_ONCE, &mvm->status)) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index bd50f52a1aad..0a9af1ad1f20 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -2052,6 +2052,7 @@ static void iwl_trans_pcie_set_pmi(struct iwl_trans *trans, bool state) struct iwl_trans_pcie_removal { struct pci_dev *pdev; struct work_struct work; + bool rescan; }; static void iwl_trans_pcie_removal_wk(struct work_struct *wk) @@ -2060,18 +2061,61 @@ static void iwl_trans_pcie_removal_wk(struct work_struct *wk) container_of(wk, struct iwl_trans_pcie_removal, work); struct pci_dev *pdev = removal->pdev; static char *prop[] = {"EVENT=INACCESSIBLE", NULL}; + struct pci_bus *bus = pdev->bus; dev_err(&pdev->dev, "Device gone - attempting removal\n"); kobject_uevent_env(&pdev->dev.kobj, KOBJ_CHANGE, prop); pci_lock_rescan_remove(); pci_dev_put(pdev); pci_stop_and_remove_bus_device(pdev); + if (removal->rescan) + pci_rescan_bus(bus->parent); pci_unlock_rescan_remove(); kfree(removal); module_put(THIS_MODULE); } +void iwl_trans_pcie_remove(struct iwl_trans *trans, bool rescan) +{ + struct iwl_trans_pcie_removal *removal; + + if (test_bit(STATUS_TRANS_DEAD, &trans->status)) + return; + + IWL_ERR(trans, "Device gone - scheduling removal!\n"); + + /* + * get a module reference to avoid doing this + * while unloading anyway and to avoid + * scheduling a work with code that's being + * removed. + */ + if (!try_module_get(THIS_MODULE)) { + IWL_ERR(trans, + "Module is being unloaded - abort\n"); + return; + } + + removal = kzalloc(sizeof(*removal), GFP_ATOMIC); + if (!removal) { + module_put(THIS_MODULE); + return; + } + /* + * we don't need to clear this flag, because + * the trans will be freed and reallocated. + */ + set_bit(STATUS_TRANS_DEAD, &trans->status); + + removal->pdev = to_pci_dev(trans->dev); + removal->rescan = rescan; + INIT_WORK(&removal->work, iwl_trans_pcie_removal_wk); + pci_dev_get(removal->pdev); + schedule_work(&removal->work); +} +EXPORT_SYMBOL(iwl_trans_pcie_remove); + /* * This version doesn't disable BHs but rather assumes they're * already disabled. @@ -2131,47 +2175,12 @@ bool __iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans) iwl_trans_pcie_dump_regs(trans); - if (iwlwifi_mod_params.remove_when_gone && cntrl == ~0U) { - struct iwl_trans_pcie_removal *removal; - - if (test_bit(STATUS_TRANS_DEAD, &trans->status)) - goto err; - - IWL_ERR(trans, "Device gone - scheduling removal!\n"); - - /* - * get a module reference to avoid doing this - * while unloading anyway and to avoid - * scheduling a work with code that's being - * removed. - */ - if (!try_module_get(THIS_MODULE)) { - IWL_ERR(trans, - "Module is being unloaded - abort\n"); - goto err; - } - - removal = kzalloc(sizeof(*removal), GFP_ATOMIC); - if (!removal) { - module_put(THIS_MODULE); - goto err; - } - /* - * we don't need to clear this flag, because - * the trans will be freed and reallocated. - */ - set_bit(STATUS_TRANS_DEAD, &trans->status); - - removal->pdev = to_pci_dev(trans->dev); - INIT_WORK(&removal->work, iwl_trans_pcie_removal_wk); - pci_dev_get(removal->pdev); - schedule_work(&removal->work); - } else { + if (iwlwifi_mod_params.remove_when_gone && cntrl == ~0U) + iwl_trans_pcie_remove(trans, false); + else iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI); - } -err: spin_unlock(&trans_pcie->reg_lock); return false; } From 274d9aa97372094317fcf0d27af4fedb6659c7d4 Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Wed, 23 Nov 2022 23:02:08 +0200 Subject: [PATCH 039/144] wifi: iwlwifi: mvm: return error value in case PLDR sync failed In case PLDR sync is required but iwlmei failed to get a response from CSME, the device cannot be brought up, so return an error value. Reported-by: Dan Carpenter Link: https://lore.kernel.org/all/Y3uYUEFnMiBY2ABQ@kili/ Signed-off-by: Avraham Stern Link: https://lore.kernel.org/r/20221123225313.08b345123f83.I13bfacc2f6da7de8ada90127fe7108d1bb73af92@changeid Signed-off-by: Gregory Greenman --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 74354d044db9..2f4c9e653e99 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1483,7 +1483,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm) sb_cfg = iwl_read_umac_prph(mvm->trans, SB_MODIFY_CFG_FLAG); mvm->pldr_sync = !(sb_cfg & SB_CFG_RESIDES_IN_OTP_MASK); if (mvm->pldr_sync && iwl_mei_pldr_req()) - return ret; + return -EBUSY; ret = iwl_mvm_load_rt_fw(mvm); if (ret) { From f31f7cd9875c2ab744006bd2ad25641db53f079d Mon Sep 17 00:00:00 2001 From: Gregory Greenman Date: Wed, 23 Nov 2022 23:02:09 +0200 Subject: [PATCH 040/144] wifi: iwlwifi: mei: fix parameter passing to iwl_mei_alive_notif() Pass true explicitly to iwl_mei_alive_notif() when we can be sure that Alive notification was received. The issue was discovered by Smatch. Reported-by: Dan Carpenter Link: https://lore.kernel.org/all/Y3uYUEFnMiBY2ABQ@kili/ Link: https://lore.kernel.org/r/20221123225313.83c2251055c1.Ia028357e7ab36dfc2abf85106a5926ee7893a408@changeid Signed-off-by: Gregory Greenman --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 2f4c9e653e99..0a41cd0ab243 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -418,7 +418,8 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, return -EIO; } - iwl_mei_alive_notif(!ret); + /* if reached this point, Alive notification was received */ + iwl_mei_alive_notif(true); ret = iwl_pnvm_load(mvm->trans, &mvm->notif_wait); if (ret) { From 4920ab131b2dbae7464b72bdcac465d070254209 Mon Sep 17 00:00:00 2001 From: Minsuk Kang Date: Wed, 16 Nov 2022 23:29:52 +0900 Subject: [PATCH 041/144] wifi: brcmfmac: Check the count value of channel spec to prevent out-of-bounds reads This patch fixes slab-out-of-bounds reads in brcmfmac that occur in brcmf_construct_chaninfo() and brcmf_enable_bw40_2g() when the count value of channel specifications provided by the device is greater than the length of 'list->element[]', decided by the size of the 'list' allocated with kzalloc(). The patch adds checks that make the functions free the buffer and return -EINVAL if that is the case. Note that the negative return is handled by the caller, brcmf_setup_wiphybands() or brcmf_cfg80211_attach(). Found by a modified version of syzkaller. Crash Report from brcmf_construct_chaninfo(): ================================================================== BUG: KASAN: slab-out-of-bounds in brcmf_setup_wiphybands+0x1238/0x1430 Read of size 4 at addr ffff888115f24600 by task kworker/0:2/1896 CPU: 0 PID: 1896 Comm: kworker/0:2 Tainted: G W O 5.14.0+ #132 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.1-0-ga5cab58e9a3f-prebuilt.qemu.org 04/01/2014 Workqueue: usb_hub_wq hub_event Call Trace: dump_stack_lvl+0x57/0x7d print_address_description.constprop.0.cold+0x93/0x334 kasan_report.cold+0x83/0xdf brcmf_setup_wiphybands+0x1238/0x1430 brcmf_cfg80211_attach+0x2118/0x3fd0 brcmf_attach+0x389/0xd40 brcmf_usb_probe+0x12de/0x1690 usb_probe_interface+0x25f/0x710 really_probe+0x1be/0xa90 __driver_probe_device+0x2ab/0x460 driver_probe_device+0x49/0x120 __device_attach_driver+0x18a/0x250 bus_for_each_drv+0x123/0x1a0 __device_attach+0x207/0x330 bus_probe_device+0x1a2/0x260 device_add+0xa61/0x1ce0 usb_set_configuration+0x984/0x1770 usb_generic_driver_probe+0x69/0x90 usb_probe_device+0x9c/0x220 really_probe+0x1be/0xa90 __driver_probe_device+0x2ab/0x460 driver_probe_device+0x49/0x120 __device_attach_driver+0x18a/0x250 bus_for_each_drv+0x123/0x1a0 __device_attach+0x207/0x330 bus_probe_device+0x1a2/0x260 device_add+0xa61/0x1ce0 usb_new_device.cold+0x463/0xf66 hub_event+0x10d5/0x3330 process_one_work+0x873/0x13e0 worker_thread+0x8b/0xd10 kthread+0x379/0x450 ret_from_fork+0x1f/0x30 Allocated by task 1896: kasan_save_stack+0x1b/0x40 __kasan_kmalloc+0x7c/0x90 kmem_cache_alloc_trace+0x19e/0x330 brcmf_setup_wiphybands+0x290/0x1430 brcmf_cfg80211_attach+0x2118/0x3fd0 brcmf_attach+0x389/0xd40 brcmf_usb_probe+0x12de/0x1690 usb_probe_interface+0x25f/0x710 really_probe+0x1be/0xa90 __driver_probe_device+0x2ab/0x460 driver_probe_device+0x49/0x120 __device_attach_driver+0x18a/0x250 bus_for_each_drv+0x123/0x1a0 __device_attach+0x207/0x330 bus_probe_device+0x1a2/0x260 device_add+0xa61/0x1ce0 usb_set_configuration+0x984/0x1770 usb_generic_driver_probe+0x69/0x90 usb_probe_device+0x9c/0x220 really_probe+0x1be/0xa90 __driver_probe_device+0x2ab/0x460 driver_probe_device+0x49/0x120 __device_attach_driver+0x18a/0x250 bus_for_each_drv+0x123/0x1a0 __device_attach+0x207/0x330 bus_probe_device+0x1a2/0x260 device_add+0xa61/0x1ce0 usb_new_device.cold+0x463/0xf66 hub_event+0x10d5/0x3330 process_one_work+0x873/0x13e0 worker_thread+0x8b/0xd10 kthread+0x379/0x450 ret_from_fork+0x1f/0x30 The buggy address belongs to the object at ffff888115f24000 which belongs to the cache kmalloc-2k of size 2048 The buggy address is located 1536 bytes inside of 2048-byte region [ffff888115f24000, ffff888115f24800) Memory state around the buggy address: ffff888115f24500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ffff888115f24580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >ffff888115f24600: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ^ ffff888115f24680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff888115f24700: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ================================================================== Crash Report from brcmf_enable_bw40_2g(): ================================================================== BUG: KASAN: slab-out-of-bounds in brcmf_cfg80211_attach+0x3d11/0x3fd0 Read of size 4 at addr ffff888103787600 by task kworker/0:2/1896 CPU: 0 PID: 1896 Comm: kworker/0:2 Tainted: G W O 5.14.0+ #132 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.1-0-ga5cab58e9a3f-prebuilt.qemu.org 04/01/2014 Workqueue: usb_hub_wq hub_event Call Trace: dump_stack_lvl+0x57/0x7d print_address_description.constprop.0.cold+0x93/0x334 kasan_report.cold+0x83/0xdf brcmf_cfg80211_attach+0x3d11/0x3fd0 brcmf_attach+0x389/0xd40 brcmf_usb_probe+0x12de/0x1690 usb_probe_interface+0x25f/0x710 really_probe+0x1be/0xa90 __driver_probe_device+0x2ab/0x460 driver_probe_device+0x49/0x120 __device_attach_driver+0x18a/0x250 bus_for_each_drv+0x123/0x1a0 __device_attach+0x207/0x330 bus_probe_device+0x1a2/0x260 device_add+0xa61/0x1ce0 usb_set_configuration+0x984/0x1770 usb_generic_driver_probe+0x69/0x90 usb_probe_device+0x9c/0x220 really_probe+0x1be/0xa90 __driver_probe_device+0x2ab/0x460 driver_probe_device+0x49/0x120 __device_attach_driver+0x18a/0x250 bus_for_each_drv+0x123/0x1a0 __device_attach+0x207/0x330 bus_probe_device+0x1a2/0x260 device_add+0xa61/0x1ce0 usb_new_device.cold+0x463/0xf66 hub_event+0x10d5/0x3330 process_one_work+0x873/0x13e0 worker_thread+0x8b/0xd10 kthread+0x379/0x450 ret_from_fork+0x1f/0x30 Allocated by task 1896: kasan_save_stack+0x1b/0x40 __kasan_kmalloc+0x7c/0x90 kmem_cache_alloc_trace+0x19e/0x330 brcmf_cfg80211_attach+0x3302/0x3fd0 brcmf_attach+0x389/0xd40 brcmf_usb_probe+0x12de/0x1690 usb_probe_interface+0x25f/0x710 really_probe+0x1be/0xa90 __driver_probe_device+0x2ab/0x460 driver_probe_device+0x49/0x120 __device_attach_driver+0x18a/0x250 bus_for_each_drv+0x123/0x1a0 __device_attach+0x207/0x330 bus_probe_device+0x1a2/0x260 device_add+0xa61/0x1ce0 usb_set_configuration+0x984/0x1770 usb_generic_driver_probe+0x69/0x90 usb_probe_device+0x9c/0x220 really_probe+0x1be/0xa90 __driver_probe_device+0x2ab/0x460 driver_probe_device+0x49/0x120 __device_attach_driver+0x18a/0x250 bus_for_each_drv+0x123/0x1a0 __device_attach+0x207/0x330 bus_probe_device+0x1a2/0x260 device_add+0xa61/0x1ce0 usb_new_device.cold+0x463/0xf66 hub_event+0x10d5/0x3330 process_one_work+0x873/0x13e0 worker_thread+0x8b/0xd10 kthread+0x379/0x450 ret_from_fork+0x1f/0x30 The buggy address belongs to the object at ffff888103787000 which belongs to the cache kmalloc-2k of size 2048 The buggy address is located 1536 bytes inside of 2048-byte region [ffff888103787000, ffff888103787800) Memory state around the buggy address: ffff888103787500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ffff888103787580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >ffff888103787600: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ^ ffff888103787680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff888103787700: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ================================================================== Reported-by: Dokyung Song Reported-by: Jisoo Jang Reported-by: Minsuk Kang Reviewed-by: Arend van Spriel Signed-off-by: Minsuk Kang Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221116142952.518241-1-linuxlovemin@yonsei.ac.kr --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index ae9507dec74a..bff3128c2f26 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -101,6 +101,9 @@ #define BRCMF_ASSOC_PARAMS_FIXED_SIZE \ (sizeof(struct brcmf_assoc_params_le) - sizeof(u16)) +#define BRCMF_MAX_CHANSPEC_LIST \ + (BRCMF_DCMD_MEDLEN / sizeof(__le32) - 1) + struct brcmf_dump_survey { u32 obss; u32 ibss; @@ -6840,6 +6843,13 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, band->channels[i].flags = IEEE80211_CHAN_DISABLED; total = le32_to_cpu(list->count); + if (total > BRCMF_MAX_CHANSPEC_LIST) { + bphy_err(drvr, "Invalid count of channel Spec. (%u)\n", + total); + err = -EINVAL; + goto fail_pbuf; + } + for (i = 0; i < total; i++) { ch.chspec = (u16)le32_to_cpu(list->element[i]); cfg->d11inf.decchspec(&ch); @@ -6985,6 +6995,13 @@ static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg) band = cfg_to_wiphy(cfg)->bands[NL80211_BAND_2GHZ]; list = (struct brcmf_chanspec_list *)pbuf; num_chan = le32_to_cpu(list->count); + if (num_chan > BRCMF_MAX_CHANSPEC_LIST) { + bphy_err(drvr, "Invalid count of channel Spec. (%u)\n", + num_chan); + kfree(pbuf); + return -EINVAL; + } + for (i = 0; i < num_chan; i++) { ch.chspec = (u16)le32_to_cpu(list->element[i]); cfg->d11inf.decchspec(&ch); From cd9b6b3baf5278c73c91e242d41387684fc7f8d8 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Fri, 25 Nov 2022 15:24:14 +0800 Subject: [PATCH 042/144] wifi: rtw89: enable mac80211 virtual monitor interface For running with mac80211 channel context ops and using only as monitor, we need to enable WANT_MONITOR_VIF to let mac80211 process virtual monitor interface. Then, we are able to set channel on the monitor from user space. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221125072416.94752-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index a5fe45d79f18..af8daa101f45 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -1405,6 +1405,9 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; const u8 *bssid = iter_data->bssid; + if (!vif->bss_conf.bssid) + return; + if (ieee80211_is_trigger(hdr->frame_control)) { rtw89_stats_trigger_frame(rtwdev, vif, skb); return; @@ -2386,6 +2389,8 @@ void rtw89_vif_type_mapping(struct ieee80211_vif *vif, bool assoc) rtwvif->self_role = RTW89_SELF_ROLE_CLIENT; rtwvif->addr_cam.sec_ent_mode = RTW89_ADDR_CAM_SEC_NORMAL; break; + case NL80211_IFTYPE_MONITOR: + break; default: WARN_ON(1); break; @@ -3266,6 +3271,7 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev) ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS); ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID); + ieee80211_hw_set(hw, WANT_MONITOR_VIF); hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP) | From 51e8ed4e44b5efcf8da2c1f3478e52120a12cdf8 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 25 Nov 2022 15:24:15 +0800 Subject: [PATCH 043/144] wifi: rtw89: add HE radiotap for monitor mode With basic HE radiotap, we can check data rate in sniffer data. To store the radiotap data, we reserve headroom of aligned 64 bytes, and then update HE radiotap in monitor mode, so it doesn't affect performance in normal mode. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221125072416.94752-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 22 ++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/core.h | 18 ++++++++++++++++++ drivers/net/wireless/realtek/rtw89/pci.c | 2 +- 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index af8daa101f45..190d72df800f 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -1480,6 +1480,27 @@ static void rtw89_core_hw_to_sband_rate(struct ieee80211_rx_status *rx_status) rx_status->rate_idx -= 4; } +static void rtw89_core_update_radiotap(struct rtw89_dev *rtwdev, + struct sk_buff *skb, + struct ieee80211_rx_status *rx_status) +{ + static const struct ieee80211_radiotap_he known_he = { + .data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN), + .data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN), + }; + struct ieee80211_radiotap_he *he; + + if (!(rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR)) + return; + + if (rx_status->encoding == RX_ENC_HE) { + rx_status->flag |= RX_FLAG_RADIOTAP_HE; + he = skb_push(skb, sizeof(*he)); + *he = known_he; + } +} + static void rtw89_core_rx_to_mac80211(struct rtw89_dev *rtwdev, struct rtw89_rx_phy_ppdu *phy_ppdu, struct rtw89_rx_desc_info *desc_info, @@ -1494,6 +1515,7 @@ static void rtw89_core_rx_to_mac80211(struct rtw89_dev *rtwdev, rtw89_core_hw_to_sband_rate(rx_status); rtw89_core_rx_stats(rtwdev, phy_ppdu, desc_info, skb_ppdu); + rtw89_core_update_radiotap(rtwdev, skb_ppdu, rx_status); /* In low power mode, it does RX in thread context. */ local_bh_disable(); ieee80211_rx_napi(rtwdev->hw, NULL, skb_ppdu, napi); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 1b0acb1c5450..0be8f7bd3ca2 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -35,6 +35,7 @@ extern const struct ieee80211_ops rtw89_ops; #define RSSI_FACTOR 1 #define RTW89_RSSI_RAW_TO_DBM(rssi) ((s8)((rssi) >> RSSI_FACTOR) - MAX_RSSI) #define RTW89_TX_DIV_RSSI_RAW_TH (2 << RSSI_FACTOR) +#define RTW89_RADIOTAP_ROOM ALIGN(sizeof(struct ieee80211_radiotap_he), 64) #define RTW89_HTC_MASK_VARIANT GENMASK(1, 0) #define RTW89_HTC_VARIANT_HE 3 @@ -4383,6 +4384,23 @@ static inline struct rtw89_fw_suit *rtw89_fw_suit_get(struct rtw89_dev *rtwdev, return &fw_info->normal; } +static inline struct sk_buff *rtw89_alloc_skb_for_rx(struct rtw89_dev *rtwdev, + unsigned int length) +{ + struct sk_buff *skb; + + if (rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR) { + skb = dev_alloc_skb(length + RTW89_RADIOTAP_ROOM); + if (!skb) + return NULL; + + skb_reserve(skb, RTW89_RADIOTAP_ROOM); + return skb; + } + + return dev_alloc_skb(length); +} + int rtw89_core_tx_write(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct sk_buff *skb, int *qsel); int rtw89_h2c_tx(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index 7aa0af18cdd5..1c4500ba777c 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -267,7 +267,7 @@ static u32 rtw89_pci_rxbd_deliver_skbs(struct rtw89_dev *rtwdev, rtw89_core_query_rxdesc(rtwdev, desc_info, skb->data, rxinfo_size); - new = dev_alloc_skb(desc_info->pkt_size); + new = rtw89_alloc_skb_for_rx(rtwdev, desc_info->pkt_size); if (!new) goto err_sync_device; From a215b2b7055f02d8f7666f457d442e77097bb604 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 25 Nov 2022 15:24:16 +0800 Subject: [PATCH 044/144] wifi: rtw89: 8852b: turn off PoP function in monitor mode PoP stands for Packet on Packet that can improve performance in noisy environment, but it could get RX stuck suddenly. In normal mode, firmware can help to resolve the stuck, but firmware doesn't work in monitor mode. Therefore, turn off PoP to avoid RX stuck. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221125072416.94752-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/reg.h | 2 ++ drivers/net/wireless/realtek/rtw89/rtw8852b.c | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index dbe06542f443..f2634062f377 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -4236,6 +4236,8 @@ #define R_P1_NBIIDX 0x4770 #define B_P1_NBIIDX_VAL GENMASK(11, 0) #define B_P1_NBIIDX_NOTCH_EN BIT(12) +#define R_PKT_CTRL 0x47D4 +#define B_PKT_POP_EN BIT(8) #define R_SEG0R_PD 0x481C #define R_SEG0R_PD_V1 0x4860 #define B_SEG0R_PD_SPATIAL_REUSE_EN_MSK_V1 BIT(30) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 2d4d572dc601..b635ac1d1ca2 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -1411,6 +1411,12 @@ static void rtw8852b_bb_sethw(struct rtw89_dev *rtwdev) rtw89_phy_read32_mask(rtwdev, R_P1_RPL1, B_P0_RPL1_BIAS_MASK); } +static void rtw8852b_bb_set_pop(struct rtw89_dev *rtwdev) +{ + if (rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR) + rtw89_phy_write32_clr(rtwdev, R_PKT_CTRL, B_PKT_POP_EN); +} + static void rtw8852b_set_channel_bb(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) { @@ -1441,6 +1447,7 @@ static void rtw8852b_set_channel_bb(struct rtw89_dev *rtwdev, const struct rtw89 rtw89_phy_write32_mask(rtwdev, R_MAC_PIN_SEL, B_CH_IDX_SEG0, chan->primary_channel); rtw8852b_5m_mask(rtwdev, chan, phy_idx); + rtw8852b_bb_set_pop(rtwdev); rtw8852b_bb_reset_all(rtwdev, phy_idx); } From 7c57d3dc43812653726902aae95ac9b36aa7e3ec Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 28 Nov 2022 15:56:53 +0800 Subject: [PATCH 045/144] wifi: rtw88: 8821c: enable BT device recovery mechanism 8821ce is a combo card, and BT is a USB device that could get card lost during stress test, and need WiFi firmware to detect and recover it, so driver sends a H2C to enable this mechanism. Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221128075653.5221-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw88/fw.c | 10 ++++++++++ drivers/net/wireless/realtek/rtw88/fw.h | 6 ++++++ drivers/net/wireless/realtek/rtw88/mac.c | 18 +++++++++++++++++- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw88/fw.c b/drivers/net/wireless/realtek/rtw88/fw.c index b290811d75e1..5e760c884f89 100644 --- a/drivers/net/wireless/realtek/rtw88/fw.c +++ b/drivers/net/wireless/realtek/rtw88/fw.c @@ -822,6 +822,16 @@ void rtw_fw_set_nlo_info(struct rtw_dev *rtwdev, bool enable) rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } +void rtw_fw_set_recover_bt_device(struct rtw_dev *rtwdev) +{ + u8 h2c_pkt[H2C_PKT_SIZE] = {0}; + + SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_RECOVER_BT_DEV); + SET_RECOVER_BT_DEV_EN(h2c_pkt, 1); + + rtw_fw_send_h2c_command(rtwdev, h2c_pkt); +} + void rtw_fw_set_pg_info(struct rtw_dev *rtwdev) { struct rtw_lps_conf *conf = &rtwdev->lps_conf; diff --git a/drivers/net/wireless/realtek/rtw88/fw.h b/drivers/net/wireless/realtek/rtw88/fw.h index bca610dc99ab..0a386e6d6e0d 100644 --- a/drivers/net/wireless/realtek/rtw88/fw.h +++ b/drivers/net/wireless/realtek/rtw88/fw.h @@ -555,6 +555,8 @@ static inline void rtw_h2c_pkt_set_header(u8 *h2c_pkt, u8 sub_id) #define H2C_CMD_AOAC_GLOBAL_INFO 0x82 #define H2C_CMD_NLO_INFO 0x8C +#define H2C_CMD_RECOVER_BT_DEV 0xD1 + #define SET_H2C_CMD_ID_CLASS(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(7, 0)) @@ -754,6 +756,9 @@ static inline void rtw_h2c_pkt_set_header(u8 *h2c_pkt, u8 sub_id) #define SET_NLO_LOC_NLO_INFO(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(23, 16)) +#define SET_RECOVER_BT_DEV_EN(h2c_pkt, value) \ + le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(8)) + #define GET_FW_DUMP_LEN(_header) \ le32_get_bits(*((__le32 *)(_header) + 0x00), GENMASK(15, 0)) #define GET_FW_DUMP_SEQ(_header) \ @@ -843,6 +848,7 @@ void rtw_fw_set_aoac_global_info_cmd(struct rtw_dev *rtwdev, u8 group_key_enc); void rtw_fw_set_nlo_info(struct rtw_dev *rtwdev, bool enable); +void rtw_fw_set_recover_bt_device(struct rtw_dev *rtwdev); void rtw_fw_update_pkt_probe_req(struct rtw_dev *rtwdev, struct cfg80211_ssid *ssid); void rtw_fw_channel_switch(struct rtw_dev *rtwdev, bool enable); diff --git a/drivers/net/wireless/realtek/rtw88/mac.c b/drivers/net/wireless/realtek/rtw88/mac.c index 52076e89d59a..c7e64f7036ac 100644 --- a/drivers/net/wireless/realtek/rtw88/mac.c +++ b/drivers/net/wireless/realtek/rtw88/mac.c @@ -906,7 +906,8 @@ out: return ret; } -int rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw) +static +int _rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw) { if (rtw_chip_wcpu_11n(rtwdev)) return __rtw_download_firmware_legacy(rtwdev, fw); @@ -914,6 +915,21 @@ int rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw) return __rtw_download_firmware(rtwdev, fw); } +int rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw) +{ + int ret; + + ret = _rtw_download_firmware(rtwdev, fw); + if (ret) + return ret; + + if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE && + rtwdev->chip->id == RTW_CHIP_TYPE_8821C) + rtw_fw_set_recover_bt_device(rtwdev); + + return 0; +} + static u32 get_priority_queues(struct rtw_dev *rtwdev, u32 queues) { const struct rtw_rqpn *rqpn = rtwdev->fifo.rqpn; From 38f25dec521edfa289fa0b829676927b13fede91 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 29 Nov 2022 16:31:25 +0800 Subject: [PATCH 046/144] wifi: rtw89: rfk: rename rtw89_mcc_info to rtw89_rfk_mcc_info The `rtw89_mcc_info mcc` is only for RFK MCC stuffs instead of common MCC management info. Replace it with `rtw89_rfk_mcc_info rfk_mcc` to avoid confusion and reserve `struct rtw89_mcc_info mcc` for MCC management code. (No logic changes.) Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221129083130.45708-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 4 ++-- drivers/net/wireless/realtek/rtw89/fw.c | 10 +++++----- drivers/net/wireless/realtek/rtw89/rtw8852c.c | 4 ++-- .../net/wireless/realtek/rtw89/rtw8852c_rfk.c | 20 +++++++++---------- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 0be8f7bd3ca2..d0efeeabcb77 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3032,7 +3032,7 @@ struct rtw89_dack_info { #define RTW89_IQK_CHS_NR 2 #define RTW89_IQK_PATH_NR 4 -struct rtw89_mcc_info { +struct rtw89_rfk_mcc_info { u8 ch[RTW89_IQK_CHS_NR]; u8 band[RTW89_IQK_CHS_NR]; u8 table_idx; @@ -3578,7 +3578,7 @@ struct rtw89_dev { struct rtw89_dack_info dack; struct rtw89_iqk_info iqk; struct rtw89_dpk_info dpk; - struct rtw89_mcc_info mcc; + struct rtw89_rfk_mcc_info rfk_mcc; struct rtw89_lck_info lck; struct rtw89_rx_dck_info rx_dck; bool is_tssi_mode[RF_PATH_MAX]; diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 10c5ed21da02..3d7b99e15967 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -2263,7 +2263,7 @@ fail: int rtw89_fw_h2c_rf_ntfy_mcc(struct rtw89_dev *rtwdev) { const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); - struct rtw89_mcc_info *mcc_info = &rtwdev->mcc; + struct rtw89_rfk_mcc_info *rfk_mcc = &rtwdev->rfk_mcc; struct rtw89_fw_h2c_rf_get_mccch *mccch; struct sk_buff *skb; int ret; @@ -2276,10 +2276,10 @@ int rtw89_fw_h2c_rf_ntfy_mcc(struct rtw89_dev *rtwdev) skb_put(skb, sizeof(*mccch)); mccch = (struct rtw89_fw_h2c_rf_get_mccch *)skb->data; - mccch->ch_0 = cpu_to_le32(mcc_info->ch[0]); - mccch->ch_1 = cpu_to_le32(mcc_info->ch[1]); - mccch->band_0 = cpu_to_le32(mcc_info->band[0]); - mccch->band_1 = cpu_to_le32(mcc_info->band[1]); + mccch->ch_0 = cpu_to_le32(rfk_mcc->ch[0]); + mccch->ch_1 = cpu_to_le32(rfk_mcc->ch[1]); + mccch->band_0 = cpu_to_le32(rfk_mcc->band[0]); + mccch->band_1 = cpu_to_le32(rfk_mcc->band[1]); mccch->current_channel = cpu_to_le32(chan->channel); mccch->current_band_type = cpu_to_le32(chan->band_type); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 9bc98fd5d4ac..a87482cc25f5 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -1832,11 +1832,11 @@ static void rtw8852c_set_channel_help(struct rtw89_dev *rtwdev, bool enter, static void rtw8852c_rfk_init(struct rtw89_dev *rtwdev) { - struct rtw89_mcc_info *mcc_info = &rtwdev->mcc; + struct rtw89_rfk_mcc_info *rfk_mcc = &rtwdev->rfk_mcc; rtwdev->is_tssi_mode[RF_PATH_A] = false; rtwdev->is_tssi_mode[RF_PATH_B] = false; - memset(mcc_info, 0, sizeof(*mcc_info)); + memset(rfk_mcc, 0, sizeof(*rfk_mcc)); rtw8852c_lck_init(rtwdev); rtw8852c_rck(rtwdev); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c index b0672b906e7b..60cd676fe22c 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c @@ -1030,9 +1030,9 @@ static bool _iqk_nbtxk(struct rtw89_dev *rtwdev, static bool _lok_finetune_check(struct rtw89_dev *rtwdev, u8 path) { - struct rtw89_mcc_info *mcc_info = &rtwdev->mcc; + struct rtw89_rfk_mcc_info *rfk_mcc = &rtwdev->rfk_mcc; struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; - u8 idx = mcc_info->table_idx; + u8 idx = rfk_mcc->table_idx; bool is_fail1, is_fail2; u32 val; u32 core_i; @@ -1375,10 +1375,10 @@ static void _iqk_afebb_restore(struct rtw89_dev *rtwdev, static void _iqk_preset(struct rtw89_dev *rtwdev, u8 path) { - struct rtw89_mcc_info *mcc_info = &rtwdev->mcc; + struct rtw89_rfk_mcc_info *rfk_mcc = &rtwdev->rfk_mcc; u8 idx = 0; - idx = mcc_info->table_idx; + idx = rfk_mcc->table_idx; rtw89_phy_write32_mask(rtwdev, R_COEF_SEL + (path << 8), B_COEF_SEL_IQC, idx); rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT + (path << 8), B_CFIR_LUT_G3, idx); rtw89_write_rf(rtwdev, path, RR_RSV1, RR_RSV1_RST, 0x0); @@ -3824,20 +3824,20 @@ void rtw8852c_set_channel_rf(struct rtw89_dev *rtwdev, void rtw8852c_mcc_get_ch_info(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); - struct rtw89_mcc_info *mcc_info = &rtwdev->mcc; - u8 idx = mcc_info->table_idx; + struct rtw89_rfk_mcc_info *rfk_mcc = &rtwdev->rfk_mcc; + u8 idx = rfk_mcc->table_idx; int i; for (i = 0; i < RTW89_IQK_CHS_NR; i++) { - if (mcc_info->ch[idx] == 0) + if (rfk_mcc->ch[idx] == 0) break; if (++idx >= RTW89_IQK_CHS_NR) idx = 0; } - mcc_info->table_idx = idx; - mcc_info->ch[idx] = chan->channel; - mcc_info->band[idx] = chan->band_type; + rfk_mcc->table_idx = idx; + rfk_mcc->ch[idx] = chan->channel; + rfk_mcc->band[idx] = chan->band_type; } void rtw8852c_rck(struct rtw89_dev *rtwdev) From 860e8263ae92667a2002163886fd2ebd8c67f699 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 29 Nov 2022 16:31:26 +0800 Subject: [PATCH 047/144] wifi: rtw89: check if atomic before queuing c2h Before queuing C2H work, we check atomicity of the C2H's handler first now. If atomic or lock-free, handle it directly; otherwise, handle it with mutex in work as previous. This prepares for MAC MCC C2Hs which require to be processed directly. And, their handlers will be functions which can be considered atomic. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221129083130.45708-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 47 ++++++++++++++++++++++-- drivers/net/wireless/realtek/rtw89/fw.h | 14 +++++++ drivers/net/wireless/realtek/rtw89/mac.c | 10 +++++ drivers/net/wireless/realtek/rtw89/mac.h | 1 + 4 files changed, 68 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 3d7b99e15967..544f6d4b6873 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -11,6 +11,9 @@ #include "phy.h" #include "reg.h" +static void rtw89_fw_c2h_cmd_handle(struct rtw89_dev *rtwdev, + struct sk_buff *skb); + static struct sk_buff *rtw89_fw_h2c_alloc_skb(struct rtw89_dev *rtwdev, u32 len, bool header) { @@ -2382,8 +2385,43 @@ void rtw89_fw_free_all_early_h2c(struct rtw89_dev *rtwdev) mutex_unlock(&rtwdev->mutex); } +static void rtw89_fw_c2h_parse_attr(struct sk_buff *c2h) +{ + struct rtw89_fw_c2h_attr *attr = RTW89_SKB_C2H_CB(c2h); + + attr->category = RTW89_GET_C2H_CATEGORY(c2h->data); + attr->class = RTW89_GET_C2H_CLASS(c2h->data); + attr->func = RTW89_GET_C2H_FUNC(c2h->data); + attr->len = RTW89_GET_C2H_LEN(c2h->data); +} + +static bool rtw89_fw_c2h_chk_atomic(struct rtw89_dev *rtwdev, + struct sk_buff *c2h) +{ + struct rtw89_fw_c2h_attr *attr = RTW89_SKB_C2H_CB(c2h); + u8 category = attr->category; + u8 class = attr->class; + u8 func = attr->func; + + switch (category) { + default: + return false; + case RTW89_C2H_CAT_MAC: + return rtw89_mac_c2h_chk_atomic(rtwdev, class, func); + } +} + void rtw89_fw_c2h_irqsafe(struct rtw89_dev *rtwdev, struct sk_buff *c2h) { + rtw89_fw_c2h_parse_attr(c2h); + if (!rtw89_fw_c2h_chk_atomic(rtwdev, c2h)) + goto enqueue; + + rtw89_fw_c2h_cmd_handle(rtwdev, c2h); + dev_kfree_skb_any(c2h); + return; + +enqueue: skb_queue_tail(&rtwdev->c2h_queue, c2h); ieee80211_queue_work(rtwdev->hw, &rtwdev->c2h_work); } @@ -2391,10 +2429,11 @@ void rtw89_fw_c2h_irqsafe(struct rtw89_dev *rtwdev, struct sk_buff *c2h) static void rtw89_fw_c2h_cmd_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb) { - u8 category = RTW89_GET_C2H_CATEGORY(skb->data); - u8 class = RTW89_GET_C2H_CLASS(skb->data); - u8 func = RTW89_GET_C2H_FUNC(skb->data); - u16 len = RTW89_GET_C2H_LEN(skb->data); + struct rtw89_fw_c2h_attr *attr = RTW89_SKB_C2H_CB(skb); + u8 category = attr->category; + u8 class = attr->class; + u8 func = attr->func; + u16 len = attr->len; bool dump = true; if (!test_bit(RTW89_FLAG_RUNNING, rtwdev->flags)) diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 509a3eac5ffe..d76d0c80f025 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -2778,6 +2778,20 @@ static inline void RTW89_SET_FWCMD_TSF32_TOGL_EARLY(void *cmd, u32 val) #define RTW89_GET_C2H_LEN(c2h) \ le32_get_bits(*((const __le32 *)(c2h) + 1), GENMASK(13, 0)) +struct rtw89_fw_c2h_attr { + u8 category; + u8 class; + u8 func; + u16 len; +}; + +static inline struct rtw89_fw_c2h_attr *RTW89_SKB_C2H_CB(struct sk_buff *skb) +{ + static_assert(sizeof(skb->cb) >= sizeof(struct rtw89_fw_c2h_attr)); + + return (struct rtw89_fw_c2h_attr *)skb->cb; +} + #define RTW89_GET_C2H_LOG_SRT_PRT(c2h) (char *)((__le32 *)(c2h) + 2) #define RTW89_GET_C2H_LOG_LEN(len) ((len) - RTW89_C2H_HEADER_LEN) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 6587cdf6ba62..098637a84895 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -4208,6 +4208,16 @@ void (* const rtw89_mac_c2h_info_handler[])(struct rtw89_dev *rtwdev, [RTW89_MAC_C2H_FUNC_BCN_CNT] = rtw89_mac_c2h_bcn_cnt, }; +bool rtw89_mac_c2h_chk_atomic(struct rtw89_dev *rtwdev, u8 class, u8 func) +{ + switch (class) { + default: + return false; + case RTW89_MAC_C2H_CLASS_MCC: + return true; + } +} + void rtw89_mac_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, u32 len, u8 class, u8 func) { diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 045e8ec61a41..82b9e81fe474 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -894,6 +894,7 @@ static inline int rtw89_chip_disable_bb_rf(struct rtw89_dev *rtwdev) u32 rtw89_mac_get_err_status(struct rtw89_dev *rtwdev); int rtw89_mac_set_err_status(struct rtw89_dev *rtwdev, u32 err); +bool rtw89_mac_c2h_chk_atomic(struct rtw89_dev *rtwdev, u8 class, u8 func); void rtw89_mac_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, u32 len, u8 class, u8 func); int rtw89_mac_setup_phycap(struct rtw89_dev *rtwdev); From 22b10cdb73921cfb28ccde5ce8b47d7fc434e7c6 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 29 Nov 2022 16:31:27 +0800 Subject: [PATCH 048/144] wifi: rtw89: introduce helpers to wait/complete on condition MCC (multi-channel concurrency) related H2Cs (host to chip commands) require to wait for C2H (chip to host events) responses to judge the execution result and data. We introduce helpers to assist this process. Besides, we would like the helpers to be generic for use in driver even outside of MCC H2C/C2H, so we make a independent patch for them. In the following, I describe the things first. ``` (A) C2H is generated by FW, and then transferred upto driver. Hence, driver cannot get it immediately without a bit waitting/blocking. For this, we choose to use wait_for_completion_*() instead of busy polling. (B) From the driver management perspective, a scenario, e.g. MCC, may have mulitple kind of H2C functions requiring this process to wait for corresponding C2Hs. But, the driver management flow uses mutex to protect each behavior. So, one scenario triggers one H2C function at one time. To avoid rampant instances of struct completion for each H2C function, we choose to use one struct completion with one condition flag for one scenario. (C) C2Hs, which H2Cs will be waitting for, cannot be ordered with driver management flow, i.e. cannot enqueue work to the same ordered workqueue and cannot lock by the same mutex, to prevent H2C side from getting no C2H responses. So, those C2Hs are parsed in interrupt context directly as done in previous commit. (D) Following (C), the above underline H2Cs and C2Hs will be handled in different contexts without sync. So, we use atomic_cmpxchg() to compare and change the condition in atomic. ``` So, we introduce struct rtw89_wait_info which combines struct completion and atomic_t. Then, the below are the descriptions for helper functions. * rtw89_wait_for_cond() to wait for a completion based on a condition. * rtw89_complete_cond() to complete a given condition and carry data. Each rtw89_wait_info instance independently determines the meaning of its waitting conditions. But, RTW89_WAIT_COND_IDLE (UINT_MAX) is reserved. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221129083130.45708-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 35 +++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/core.h | 25 ++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 190d72df800f..9a45193c68cc 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -2974,6 +2974,41 @@ void rtw89_core_update_beacon_work(struct work_struct *work) mutex_unlock(&rtwdev->mutex); } +int rtw89_wait_for_cond(struct rtw89_wait_info *wait, unsigned int cond) +{ + struct completion *cmpl = &wait->completion; + unsigned long timeout; + unsigned int cur; + + cur = atomic_cmpxchg(&wait->cond, RTW89_WAIT_COND_IDLE, cond); + if (cur != RTW89_WAIT_COND_IDLE) + return -EBUSY; + + timeout = wait_for_completion_timeout(cmpl, RTW89_WAIT_FOR_COND_TIMEOUT); + if (timeout == 0) { + atomic_set(&wait->cond, RTW89_WAIT_COND_IDLE); + return -ETIMEDOUT; + } + + if (wait->data.err) + return -EFAULT; + + return 0; +} + +void rtw89_complete_cond(struct rtw89_wait_info *wait, unsigned int cond, + const struct rtw89_completion_data *data) +{ + unsigned int cur; + + cur = atomic_cmpxchg(&wait->cond, cond, RTW89_WAIT_COND_IDLE); + if (cur != cond) + return; + + wait->data = *data; + complete(&wait->completion); +} + int rtw89_core_start(struct rtw89_dev *rtwdev) { int ret; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index d0efeeabcb77..a9b9b1c901c1 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -2812,6 +2812,28 @@ struct rtw89_mac_info { u8 cpwm_seq_num; }; +#define RTW89_COMPLETION_BUF_SIZE 24 +#define RTW89_WAIT_COND_IDLE UINT_MAX + +struct rtw89_completion_data { + bool err; + u8 buf[RTW89_COMPLETION_BUF_SIZE]; +}; + +struct rtw89_wait_info { + atomic_t cond; + struct completion completion; + struct rtw89_completion_data data; +}; + +#define RTW89_WAIT_FOR_COND_TIMEOUT msecs_to_jiffies(100) + +static inline void rtw89_init_wait(struct rtw89_wait_info *wait) +{ + init_completion(&wait->completion); + atomic_set(&wait->cond, RTW89_WAIT_COND_IDLE); +} + enum rtw89_fw_type { RTW89_FW_NORMAL = 1, RTW89_FW_WOWLAN = 3, @@ -4469,6 +4491,9 @@ int rtw89_regd_init(struct rtw89_dev *rtwdev, void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request); void rtw89_traffic_stats_init(struct rtw89_dev *rtwdev, struct rtw89_traffic_stats *stats); +int rtw89_wait_for_cond(struct rtw89_wait_info *wait, unsigned int cond); +void rtw89_complete_cond(struct rtw89_wait_info *wait, unsigned int cond, + const struct rtw89_completion_data *data); int rtw89_core_start(struct rtw89_dev *rtwdev); void rtw89_core_stop(struct rtw89_dev *rtwdev); void rtw89_core_update_beacon_work(struct work_struct *work); From ef9dff4cb491210518ad3d249919a0971eff601b Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 29 Nov 2022 16:31:28 +0800 Subject: [PATCH 049/144] wifi: rtw89: mac: process MCC related C2H Process C2H(s) related to MCC (multi-channel concurrency). These handling, which either call rtw89_complete_cond() or show message in debug mode, can be considered atomic/lock-free. So, they should be safe to be processed directly after C2H pre-check in previous patch. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221129083130.45708-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 2 + drivers/net/wireless/realtek/rtw89/core.h | 5 + drivers/net/wireless/realtek/rtw89/fw.h | 68 +++++++++ drivers/net/wireless/realtek/rtw89/mac.c | 171 ++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/mac.h | 34 +++++ 5 files changed, 280 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 9a45193c68cc..5ab95250755d 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -3130,6 +3130,8 @@ int rtw89_core_init(struct rtw89_dev *rtwdev) mutex_init(&rtwdev->rf_mutex); rtwdev->total_sta_assoc = 0; + rtw89_init_wait(&rtwdev->mcc.wait); + INIT_WORK(&rtwdev->c2h_work, rtw89_fw_c2h_work); INIT_WORK(&rtwdev->ips_work, rtw89_ips_work); skb_queue_head_init(&rtwdev->c2h_queue); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index a9b9b1c901c1..f4603c2ce8f1 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3547,6 +3547,10 @@ struct rtw89_wow_param { struct list_head pkt_list; }; +struct rtw89_mcc_info { + struct rtw89_wait_info wait; +}; + struct rtw89_dev { struct ieee80211_hw *hw; struct device *dev; @@ -3557,6 +3561,7 @@ struct rtw89_dev { const struct rtw89_chip_info *chip; const struct rtw89_pci_info *pci_info; struct rtw89_hal hal; + struct rtw89_mcc_info mcc; struct rtw89_mac_info mac; struct rtw89_fw_info fw; struct rtw89_hci_info hci; diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index d76d0c80f025..5fb8faad9c67 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -2859,6 +2859,55 @@ static inline struct rtw89_fw_c2h_attr *RTW89_SKB_C2H_CB(struct sk_buff *skb) #define RTW89_GET_MAC_C2H_SCANOFLD_BAND(c2h) \ le32_get_bits(*((const __le32 *)(c2h) + 5), GENMASK(25, 24)) +#define RTW89_GET_MAC_C2H_MCC_RCV_ACK_GROUP(c2h) \ + le32_get_bits(*((const __le32 *)(c2h)), GENMASK(1, 0)) +#define RTW89_GET_MAC_C2H_MCC_RCV_ACK_H2C_FUNC(c2h) \ + le32_get_bits(*((const __le32 *)(c2h)), GENMASK(15, 8)) + +#define RTW89_GET_MAC_C2H_MCC_REQ_ACK_GROUP(c2h) \ + le32_get_bits(*((const __le32 *)(c2h)), GENMASK(1, 0)) +#define RTW89_GET_MAC_C2H_MCC_REQ_ACK_H2C_RETURN(c2h) \ + le32_get_bits(*((const __le32 *)(c2h)), GENMASK(7, 2)) +#define RTW89_GET_MAC_C2H_MCC_REQ_ACK_H2C_FUNC(c2h) \ + le32_get_bits(*((const __le32 *)(c2h)), GENMASK(15, 8)) + +struct rtw89_mac_mcc_tsf_rpt { + u32 macid_x; + u32 macid_y; + u32 tsf_x_low; + u32 tsf_x_high; + u32 tsf_y_low; + u32 tsf_y_high; +}; + +static_assert(sizeof(struct rtw89_mac_mcc_tsf_rpt) <= RTW89_COMPLETION_BUF_SIZE); + +#define RTW89_GET_MAC_C2H_MCC_TSF_RPT_MACID_X(c2h) \ + le32_get_bits(*((const __le32 *)(c2h)), GENMASK(7, 0)) +#define RTW89_GET_MAC_C2H_MCC_TSF_RPT_MACID_Y(c2h) \ + le32_get_bits(*((const __le32 *)(c2h)), GENMASK(15, 8)) +#define RTW89_GET_MAC_C2H_MCC_TSF_RPT_GROUP(c2h) \ + le32_get_bits(*((const __le32 *)(c2h)), GENMASK(17, 16)) +#define RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_LOW_X(c2h) \ + le32_get_bits(*((const __le32 *)(c2h) + 1), GENMASK(31, 0)) +#define RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_HIGH_X(c2h) \ + le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(31, 0)) +#define RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_LOW_Y(c2h) \ + le32_get_bits(*((const __le32 *)(c2h) + 3), GENMASK(31, 0)) +#define RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_HIGH_Y(c2h) \ + le32_get_bits(*((const __le32 *)(c2h) + 4), GENMASK(31, 0)) + +#define RTW89_GET_MAC_C2H_MCC_STATUS_RPT_STATUS(c2h) \ + le32_get_bits(*((const __le32 *)(c2h)), GENMASK(5, 0)) +#define RTW89_GET_MAC_C2H_MCC_STATUS_RPT_GROUP(c2h) \ + le32_get_bits(*((const __le32 *)(c2h)), GENMASK(7, 6)) +#define RTW89_GET_MAC_C2H_MCC_STATUS_RPT_MACID(c2h) \ + le32_get_bits(*((const __le32 *)(c2h)), GENMASK(15, 8)) +#define RTW89_GET_MAC_C2H_MCC_STATUS_RPT_TSF_LOW(c2h) \ + le32_get_bits(*((const __le32 *)(c2h) + 1), GENMASK(31, 0)) +#define RTW89_GET_MAC_C2H_MCC_STATUS_RPT_TSF_HIGH(c2h) \ + le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(31, 0)) + #define RTW89_FW_HDR_SIZE 32 #define RTW89_FW_SECTION_HDR_SIZE 16 @@ -2980,6 +3029,25 @@ struct rtw89_fw_h2c_rf_reg_info { #define H2C_CL_BA_CAM 0xc #define H2C_FUNC_MAC_BA_CAM 0x0 +/* CLASS 14 - MCC */ +#define H2C_CL_MCC 0xe +enum rtw89_mcc_h2c_func { + H2C_FUNC_ADD_MCC = 0x0, + H2C_FUNC_START_MCC = 0x1, + H2C_FUNC_STOP_MCC = 0x2, + H2C_FUNC_DEL_MCC_GROUP = 0x3, + H2C_FUNC_RESET_MCC_GROUP = 0x4, + H2C_FUNC_MCC_REQ_TSF = 0x5, + H2C_FUNC_MCC_MACID_BITMAP = 0x6, + H2C_FUNC_MCC_SYNC = 0x7, + H2C_FUNC_MCC_SET_DURATION = 0x8, + + NUM_OF_RTW89_MCC_H2C_FUNC, +}; + +#define RTW89_MCC_WAIT_COND(group, func) \ + ((group) * NUM_OF_RTW89_MCC_H2C_FUNC + (func)) + #define H2C_CAT_OUTSRC 0x2 #define H2C_CL_OUTSRC_RA 0x1 diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 098637a84895..d80050c2e9b3 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -4187,6 +4187,164 @@ rtw89_mac_c2h_tsf32_toggle_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, { } +static void +rtw89_mac_c2h_mcc_rcv_ack(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) +{ + u8 group = RTW89_GET_MAC_C2H_MCC_RCV_ACK_GROUP(c2h->data); + u8 func = RTW89_GET_MAC_C2H_MCC_RCV_ACK_H2C_FUNC(c2h->data); + + switch (func) { + case H2C_FUNC_ADD_MCC: + case H2C_FUNC_START_MCC: + case H2C_FUNC_STOP_MCC: + case H2C_FUNC_DEL_MCC_GROUP: + case H2C_FUNC_RESET_MCC_GROUP: + case H2C_FUNC_MCC_REQ_TSF: + case H2C_FUNC_MCC_MACID_BITMAP: + case H2C_FUNC_MCC_SYNC: + case H2C_FUNC_MCC_SET_DURATION: + break; + default: + rtw89_debug(rtwdev, RTW89_DBG_FW, + "invalid MCC C2H RCV ACK: func %d\n", func); + return; + } + + rtw89_debug(rtwdev, RTW89_DBG_FW, + "MCC C2H RCV ACK: group %d, func %d\n", group, func); +} + +static void +rtw89_mac_c2h_mcc_req_ack(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) +{ + u8 group = RTW89_GET_MAC_C2H_MCC_REQ_ACK_GROUP(c2h->data); + u8 func = RTW89_GET_MAC_C2H_MCC_REQ_ACK_H2C_FUNC(c2h->data); + u8 retcode = RTW89_GET_MAC_C2H_MCC_REQ_ACK_H2C_RETURN(c2h->data); + struct rtw89_completion_data data = {}; + unsigned int cond; + bool next = false; + + switch (func) { + case H2C_FUNC_MCC_REQ_TSF: + next = true; + break; + case H2C_FUNC_MCC_MACID_BITMAP: + case H2C_FUNC_MCC_SYNC: + case H2C_FUNC_MCC_SET_DURATION: + break; + case H2C_FUNC_ADD_MCC: + case H2C_FUNC_START_MCC: + case H2C_FUNC_STOP_MCC: + case H2C_FUNC_DEL_MCC_GROUP: + case H2C_FUNC_RESET_MCC_GROUP: + default: + rtw89_debug(rtwdev, RTW89_DBG_FW, + "invalid MCC C2H REQ ACK: func %d\n", func); + return; + } + + rtw89_debug(rtwdev, RTW89_DBG_FW, + "MCC C2H REQ ACK: group %d, func %d, return code %d\n", + group, func, retcode); + + if (!retcode && next) + return; + + data.err = !!retcode; + cond = RTW89_MCC_WAIT_COND(group, func); + rtw89_complete_cond(&rtwdev->mcc.wait, cond, &data); +} + +static void +rtw89_mac_c2h_mcc_tsf_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) +{ + u8 group = RTW89_GET_MAC_C2H_MCC_TSF_RPT_GROUP(c2h->data); + struct rtw89_completion_data data = {}; + struct rtw89_mac_mcc_tsf_rpt *rpt; + unsigned int cond; + + rpt = (struct rtw89_mac_mcc_tsf_rpt *)data.buf; + rpt->macid_x = RTW89_GET_MAC_C2H_MCC_TSF_RPT_MACID_X(c2h->data); + rpt->macid_y = RTW89_GET_MAC_C2H_MCC_TSF_RPT_MACID_Y(c2h->data); + rpt->tsf_x_low = RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_LOW_X(c2h->data); + rpt->tsf_x_high = RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_HIGH_X(c2h->data); + rpt->tsf_y_low = RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_LOW_Y(c2h->data); + rpt->tsf_y_high = RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_HIGH_Y(c2h->data); + + cond = RTW89_MCC_WAIT_COND(group, H2C_FUNC_MCC_REQ_TSF); + rtw89_complete_cond(&rtwdev->mcc.wait, cond, &data); +} + +static void +rtw89_mac_c2h_mcc_status_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) +{ + u8 group = RTW89_GET_MAC_C2H_MCC_STATUS_RPT_GROUP(c2h->data); + u8 macid = RTW89_GET_MAC_C2H_MCC_STATUS_RPT_MACID(c2h->data); + u8 status = RTW89_GET_MAC_C2H_MCC_STATUS_RPT_STATUS(c2h->data); + u32 tsf_low = RTW89_GET_MAC_C2H_MCC_STATUS_RPT_TSF_LOW(c2h->data); + u32 tsf_high = RTW89_GET_MAC_C2H_MCC_STATUS_RPT_TSF_HIGH(c2h->data); + struct rtw89_completion_data data = {}; + unsigned int cond; + bool rsp = true; + bool err; + u8 func; + + switch (status) { + case RTW89_MAC_MCC_ADD_ROLE_OK: + case RTW89_MAC_MCC_ADD_ROLE_FAIL: + func = H2C_FUNC_ADD_MCC; + err = status == RTW89_MAC_MCC_ADD_ROLE_FAIL; + break; + case RTW89_MAC_MCC_START_GROUP_OK: + case RTW89_MAC_MCC_START_GROUP_FAIL: + func = H2C_FUNC_START_MCC; + err = status == RTW89_MAC_MCC_START_GROUP_FAIL; + break; + case RTW89_MAC_MCC_STOP_GROUP_OK: + case RTW89_MAC_MCC_STOP_GROUP_FAIL: + func = H2C_FUNC_STOP_MCC; + err = status == RTW89_MAC_MCC_STOP_GROUP_FAIL; + break; + case RTW89_MAC_MCC_DEL_GROUP_OK: + case RTW89_MAC_MCC_DEL_GROUP_FAIL: + func = H2C_FUNC_DEL_MCC_GROUP; + err = status == RTW89_MAC_MCC_DEL_GROUP_FAIL; + break; + case RTW89_MAC_MCC_RESET_GROUP_OK: + case RTW89_MAC_MCC_RESET_GROUP_FAIL: + func = H2C_FUNC_RESET_MCC_GROUP; + err = status == RTW89_MAC_MCC_RESET_GROUP_FAIL; + break; + case RTW89_MAC_MCC_SWITCH_CH_OK: + case RTW89_MAC_MCC_SWITCH_CH_FAIL: + case RTW89_MAC_MCC_TXNULL0_OK: + case RTW89_MAC_MCC_TXNULL0_FAIL: + case RTW89_MAC_MCC_TXNULL1_OK: + case RTW89_MAC_MCC_TXNULL1_FAIL: + case RTW89_MAC_MCC_SWITCH_EARLY: + case RTW89_MAC_MCC_TBTT: + case RTW89_MAC_MCC_DURATION_START: + case RTW89_MAC_MCC_DURATION_END: + rsp = false; + break; + default: + rtw89_debug(rtwdev, RTW89_DBG_FW, + "invalid MCC C2H STS RPT: status %d\n", status); + return; + } + + rtw89_debug(rtwdev, RTW89_DBG_FW, + "MCC C2H STS RPT: group %d, macid %d, status %d, tsf {%d, %d}\n", + group, macid, status, tsf_low, tsf_high); + + if (!rsp) + return; + + data.err = err; + cond = RTW89_MCC_WAIT_COND(group, func); + rtw89_complete_cond(&rtwdev->mcc.wait, cond, &data); +} + static void (* const rtw89_mac_c2h_ofld_handler[])(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) = { @@ -4208,6 +4366,15 @@ void (* const rtw89_mac_c2h_info_handler[])(struct rtw89_dev *rtwdev, [RTW89_MAC_C2H_FUNC_BCN_CNT] = rtw89_mac_c2h_bcn_cnt, }; +static +void (* const rtw89_mac_c2h_mcc_handler[])(struct rtw89_dev *rtwdev, + struct sk_buff *c2h, u32 len) = { + [RTW89_MAC_C2H_FUNC_MCC_RCV_ACK] = rtw89_mac_c2h_mcc_rcv_ack, + [RTW89_MAC_C2H_FUNC_MCC_REQ_ACK] = rtw89_mac_c2h_mcc_req_ack, + [RTW89_MAC_C2H_FUNC_MCC_TSF_RPT] = rtw89_mac_c2h_mcc_tsf_rpt, + [RTW89_MAC_C2H_FUNC_MCC_STATUS_RPT] = rtw89_mac_c2h_mcc_status_rpt, +}; + bool rtw89_mac_c2h_chk_atomic(struct rtw89_dev *rtwdev, u8 class, u8 func) { switch (class) { @@ -4233,6 +4400,10 @@ void rtw89_mac_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, if (func < RTW89_MAC_C2H_FUNC_OFLD_MAX) handler = rtw89_mac_c2h_ofld_handler[func]; break; + case RTW89_MAC_C2H_CLASS_MCC: + if (func < NUM_OF_RTW89_MAC_C2H_FUNC_MCC) + handler = rtw89_mac_c2h_mcc_handler[func]; + break; case RTW89_MAC_C2H_CLASS_FWDBG: return; default: diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 82b9e81fe474..adb0c86a98d3 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -368,6 +368,15 @@ enum rtw89_mac_c2h_info_func { RTW89_MAC_C2H_FUNC_INFO_MAX, }; +enum rtw89_mac_c2h_mcc_func { + RTW89_MAC_C2H_FUNC_MCC_RCV_ACK = 0, + RTW89_MAC_C2H_FUNC_MCC_REQ_ACK = 1, + RTW89_MAC_C2H_FUNC_MCC_TSF_RPT = 2, + RTW89_MAC_C2H_FUNC_MCC_STATUS_RPT = 3, + + NUM_OF_RTW89_MAC_C2H_FUNC_MCC, +}; + enum rtw89_mac_c2h_class { RTW89_MAC_C2H_CLASS_INFO, RTW89_MAC_C2H_CLASS_OFLD, @@ -378,6 +387,31 @@ enum rtw89_mac_c2h_class { RTW89_MAC_C2H_CLASS_MAX, }; +enum rtw89_mac_mcc_status { + RTW89_MAC_MCC_ADD_ROLE_OK = 0, + RTW89_MAC_MCC_START_GROUP_OK = 1, + RTW89_MAC_MCC_STOP_GROUP_OK = 2, + RTW89_MAC_MCC_DEL_GROUP_OK = 3, + RTW89_MAC_MCC_RESET_GROUP_OK = 4, + RTW89_MAC_MCC_SWITCH_CH_OK = 5, + RTW89_MAC_MCC_TXNULL0_OK = 6, + RTW89_MAC_MCC_TXNULL1_OK = 7, + + RTW89_MAC_MCC_SWITCH_EARLY = 10, + RTW89_MAC_MCC_TBTT = 11, + RTW89_MAC_MCC_DURATION_START = 12, + RTW89_MAC_MCC_DURATION_END = 13, + + RTW89_MAC_MCC_ADD_ROLE_FAIL = 20, + RTW89_MAC_MCC_START_GROUP_FAIL = 21, + RTW89_MAC_MCC_STOP_GROUP_FAIL = 22, + RTW89_MAC_MCC_DEL_GROUP_FAIL = 23, + RTW89_MAC_MCC_RESET_GROUP_FAIL = 24, + RTW89_MAC_MCC_SWITCH_CH_FAIL = 25, + RTW89_MAC_MCC_TXNULL0_FAIL = 26, + RTW89_MAC_MCC_TXNULL1_FAIL = 27, +}; + struct rtw89_mac_ax_coex { #define RTW89_MAC_AX_COEX_RTK_MODE 0 #define RTW89_MAC_AX_COEX_CSR_MODE 1 From c008c4b011baa26b9545f7be10e746c97409d45b Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 29 Nov 2022 16:31:29 +0800 Subject: [PATCH 050/144] wifi: rtw89: fw: implement MCC related H2C These MCC H2C(s) require to wait for MCC C2H to determine if the execution is successful. Through rtw89_wait_for_cond(), we make them wait for either a completion with data from MCC C2H handlers, which calls rtw89_complete_cond(), or timeout. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221129083130.45708-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 329 +++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/fw.h | 369 +++++++++++++++++++++++- 2 files changed, 697 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 544f6d4b6873..7e682709232d 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -3263,3 +3263,332 @@ fail: return ret; } + +static int rtw89_h2c_tx_and_wait(struct rtw89_dev *rtwdev, struct sk_buff *skb, + struct rtw89_wait_info *wait, unsigned int cond) +{ + int ret; + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + dev_kfree_skb_any(skb); + return -EBUSY; + } + + return rtw89_wait_for_cond(wait, cond); +} + +#define H2C_ADD_MCC_LEN 16 +int rtw89_fw_h2c_add_mcc(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mcc_add_req *p) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct sk_buff *skb; + unsigned int cond; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_ADD_MCC_LEN); + if (!skb) { + rtw89_err(rtwdev, + "failed to alloc skb for add mcc\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_ADD_MCC_LEN); + RTW89_SET_FWCMD_ADD_MCC_MACID(skb->data, p->macid); + RTW89_SET_FWCMD_ADD_MCC_CENTRAL_CH_SEG0(skb->data, p->central_ch_seg0); + RTW89_SET_FWCMD_ADD_MCC_CENTRAL_CH_SEG1(skb->data, p->central_ch_seg1); + RTW89_SET_FWCMD_ADD_MCC_PRIMARY_CH(skb->data, p->primary_ch); + RTW89_SET_FWCMD_ADD_MCC_BANDWIDTH(skb->data, p->bandwidth); + RTW89_SET_FWCMD_ADD_MCC_GROUP(skb->data, p->group); + RTW89_SET_FWCMD_ADD_MCC_C2H_RPT(skb->data, p->c2h_rpt); + RTW89_SET_FWCMD_ADD_MCC_DIS_TX_NULL(skb->data, p->dis_tx_null); + RTW89_SET_FWCMD_ADD_MCC_DIS_SW_RETRY(skb->data, p->dis_sw_retry); + RTW89_SET_FWCMD_ADD_MCC_IN_CURR_CH(skb->data, p->in_curr_ch); + RTW89_SET_FWCMD_ADD_MCC_SW_RETRY_COUNT(skb->data, p->sw_retry_count); + RTW89_SET_FWCMD_ADD_MCC_TX_NULL_EARLY(skb->data, p->tx_null_early); + RTW89_SET_FWCMD_ADD_MCC_BTC_IN_2G(skb->data, p->btc_in_2g); + RTW89_SET_FWCMD_ADD_MCC_PTA_EN(skb->data, p->pta_en); + RTW89_SET_FWCMD_ADD_MCC_RFK_BY_PASS(skb->data, p->rfk_by_pass); + RTW89_SET_FWCMD_ADD_MCC_CH_BAND_TYPE(skb->data, p->ch_band_type); + RTW89_SET_FWCMD_ADD_MCC_DURATION(skb->data, p->duration); + RTW89_SET_FWCMD_ADD_MCC_COURTESY_EN(skb->data, p->courtesy_en); + RTW89_SET_FWCMD_ADD_MCC_COURTESY_NUM(skb->data, p->courtesy_num); + RTW89_SET_FWCMD_ADD_MCC_COURTESY_TARGET(skb->data, p->courtesy_target); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MCC, + H2C_FUNC_ADD_MCC, 0, 0, + H2C_ADD_MCC_LEN); + + cond = RTW89_MCC_WAIT_COND(p->group, H2C_FUNC_ADD_MCC); + return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); +} + +#define H2C_START_MCC_LEN 12 +int rtw89_fw_h2c_start_mcc(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mcc_start_req *p) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct sk_buff *skb; + unsigned int cond; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_START_MCC_LEN); + if (!skb) { + rtw89_err(rtwdev, + "failed to alloc skb for start mcc\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_START_MCC_LEN); + RTW89_SET_FWCMD_START_MCC_GROUP(skb->data, p->group); + RTW89_SET_FWCMD_START_MCC_BTC_IN_GROUP(skb->data, p->btc_in_group); + RTW89_SET_FWCMD_START_MCC_OLD_GROUP_ACTION(skb->data, p->old_group_action); + RTW89_SET_FWCMD_START_MCC_OLD_GROUP(skb->data, p->old_group); + RTW89_SET_FWCMD_START_MCC_NOTIFY_CNT(skb->data, p->notify_cnt); + RTW89_SET_FWCMD_START_MCC_NOTIFY_RXDBG_EN(skb->data, p->notify_rxdbg_en); + RTW89_SET_FWCMD_START_MCC_MACID(skb->data, p->macid); + RTW89_SET_FWCMD_START_MCC_TSF_LOW(skb->data, p->tsf_low); + RTW89_SET_FWCMD_START_MCC_TSF_HIGH(skb->data, p->tsf_high); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MCC, + H2C_FUNC_START_MCC, 0, 0, + H2C_START_MCC_LEN); + + cond = RTW89_MCC_WAIT_COND(p->group, H2C_FUNC_START_MCC); + return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); +} + +#define H2C_STOP_MCC_LEN 4 +int rtw89_fw_h2c_stop_mcc(struct rtw89_dev *rtwdev, u8 group, u8 macid, + bool prev_groups) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct sk_buff *skb; + unsigned int cond; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_STOP_MCC_LEN); + if (!skb) { + rtw89_err(rtwdev, + "failed to alloc skb for stop mcc\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_STOP_MCC_LEN); + RTW89_SET_FWCMD_STOP_MCC_MACID(skb->data, macid); + RTW89_SET_FWCMD_STOP_MCC_GROUP(skb->data, group); + RTW89_SET_FWCMD_STOP_MCC_PREV_GROUPS(skb->data, prev_groups); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MCC, + H2C_FUNC_STOP_MCC, 0, 0, + H2C_STOP_MCC_LEN); + + cond = RTW89_MCC_WAIT_COND(group, H2C_FUNC_STOP_MCC); + return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); +} + +#define H2C_DEL_MCC_GROUP_LEN 4 +int rtw89_fw_h2c_del_mcc_group(struct rtw89_dev *rtwdev, u8 group, + bool prev_groups) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct sk_buff *skb; + unsigned int cond; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_DEL_MCC_GROUP_LEN); + if (!skb) { + rtw89_err(rtwdev, + "failed to alloc skb for del mcc group\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_DEL_MCC_GROUP_LEN); + RTW89_SET_FWCMD_DEL_MCC_GROUP_GROUP(skb->data, group); + RTW89_SET_FWCMD_DEL_MCC_GROUP_PREV_GROUPS(skb->data, prev_groups); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MCC, + H2C_FUNC_DEL_MCC_GROUP, 0, 0, + H2C_DEL_MCC_GROUP_LEN); + + cond = RTW89_MCC_WAIT_COND(group, H2C_FUNC_DEL_MCC_GROUP); + return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); +} + +#define H2C_RESET_MCC_GROUP_LEN 4 +int rtw89_fw_h2c_reset_mcc_group(struct rtw89_dev *rtwdev, u8 group) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct sk_buff *skb; + unsigned int cond; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_RESET_MCC_GROUP_LEN); + if (!skb) { + rtw89_err(rtwdev, + "failed to alloc skb for reset mcc group\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_RESET_MCC_GROUP_LEN); + RTW89_SET_FWCMD_RESET_MCC_GROUP_GROUP(skb->data, group); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MCC, + H2C_FUNC_RESET_MCC_GROUP, 0, 0, + H2C_RESET_MCC_GROUP_LEN); + + cond = RTW89_MCC_WAIT_COND(group, H2C_FUNC_RESET_MCC_GROUP); + return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); +} + +#define H2C_MCC_REQ_TSF_LEN 4 +int rtw89_fw_h2c_mcc_req_tsf(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mcc_tsf_req *req, + struct rtw89_mac_mcc_tsf_rpt *rpt) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct rtw89_mac_mcc_tsf_rpt *tmp; + struct sk_buff *skb; + unsigned int cond; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_MCC_REQ_TSF_LEN); + if (!skb) { + rtw89_err(rtwdev, + "failed to alloc skb for mcc req tsf\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_MCC_REQ_TSF_LEN); + RTW89_SET_FWCMD_MCC_REQ_TSF_GROUP(skb->data, req->group); + RTW89_SET_FWCMD_MCC_REQ_TSF_MACID_X(skb->data, req->macid_x); + RTW89_SET_FWCMD_MCC_REQ_TSF_MACID_Y(skb->data, req->macid_y); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MCC, + H2C_FUNC_MCC_REQ_TSF, 0, 0, + H2C_MCC_REQ_TSF_LEN); + + cond = RTW89_MCC_WAIT_COND(req->group, H2C_FUNC_MCC_REQ_TSF); + ret = rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); + if (ret) + return ret; + + tmp = (struct rtw89_mac_mcc_tsf_rpt *)wait->data.buf; + *rpt = *tmp; + + return 0; +} + +#define H2C_MCC_MACID_BITMAP_DSC_LEN 4 +int rtw89_fw_h2c_mcc_macid_bitamp(struct rtw89_dev *rtwdev, u8 group, u8 macid, + u8 *bitmap) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct sk_buff *skb; + unsigned int cond; + u8 map_len; + u8 h2c_len; + + BUILD_BUG_ON(RTW89_MAX_MAC_ID_NUM % 8); + map_len = RTW89_MAX_MAC_ID_NUM / 8; + h2c_len = H2C_MCC_MACID_BITMAP_DSC_LEN + map_len; + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, h2c_len); + if (!skb) { + rtw89_err(rtwdev, + "failed to alloc skb for mcc macid bitmap\n"); + return -ENOMEM; + } + + skb_put(skb, h2c_len); + RTW89_SET_FWCMD_MCC_MACID_BITMAP_GROUP(skb->data, group); + RTW89_SET_FWCMD_MCC_MACID_BITMAP_MACID(skb->data, macid); + RTW89_SET_FWCMD_MCC_MACID_BITMAP_BITMAP_LENGTH(skb->data, map_len); + RTW89_SET_FWCMD_MCC_MACID_BITMAP_BITMAP(skb->data, bitmap, map_len); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MCC, + H2C_FUNC_MCC_MACID_BITMAP, 0, 0, + h2c_len); + + cond = RTW89_MCC_WAIT_COND(group, H2C_FUNC_MCC_MACID_BITMAP); + return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); +} + +#define H2C_MCC_SYNC_LEN 4 +int rtw89_fw_h2c_mcc_sync(struct rtw89_dev *rtwdev, u8 group, u8 source, + u8 target, u8 offset) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct sk_buff *skb; + unsigned int cond; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_MCC_SYNC_LEN); + if (!skb) { + rtw89_err(rtwdev, + "failed to alloc skb for mcc sync\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_MCC_SYNC_LEN); + RTW89_SET_FWCMD_MCC_SYNC_GROUP(skb->data, group); + RTW89_SET_FWCMD_MCC_SYNC_MACID_SOURCE(skb->data, source); + RTW89_SET_FWCMD_MCC_SYNC_MACID_TARGET(skb->data, target); + RTW89_SET_FWCMD_MCC_SYNC_SYNC_OFFSET(skb->data, offset); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MCC, + H2C_FUNC_MCC_SYNC, 0, 0, + H2C_MCC_SYNC_LEN); + + cond = RTW89_MCC_WAIT_COND(group, H2C_FUNC_MCC_SYNC); + return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); +} + +#define H2C_MCC_SET_DURATION_LEN 20 +int rtw89_fw_h2c_mcc_set_duration(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mcc_duration *p) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct sk_buff *skb; + unsigned int cond; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_MCC_SET_DURATION_LEN); + if (!skb) { + rtw89_err(rtwdev, + "failed to alloc skb for mcc set duration\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_MCC_SET_DURATION_LEN); + RTW89_SET_FWCMD_MCC_SET_DURATION_GROUP(skb->data, p->group); + RTW89_SET_FWCMD_MCC_SET_DURATION_BTC_IN_GROUP(skb->data, p->btc_in_group); + RTW89_SET_FWCMD_MCC_SET_DURATION_START_MACID(skb->data, p->start_macid); + RTW89_SET_FWCMD_MCC_SET_DURATION_MACID_X(skb->data, p->macid_x); + RTW89_SET_FWCMD_MCC_SET_DURATION_MACID_Y(skb->data, p->macid_y); + RTW89_SET_FWCMD_MCC_SET_DURATION_START_TSF_LOW(skb->data, + p->start_tsf_low); + RTW89_SET_FWCMD_MCC_SET_DURATION_START_TSF_HIGH(skb->data, + p->start_tsf_high); + RTW89_SET_FWCMD_MCC_SET_DURATION_DURATION_X(skb->data, p->duration_x); + RTW89_SET_FWCMD_MCC_SET_DURATION_DURATION_Y(skb->data, p->duration_y); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MCC, + H2C_FUNC_MCC_SET_DURATION, 0, 0, + H2C_MCC_SET_DURATION_LEN); + + cond = RTW89_MCC_WAIT_COND(p->group, H2C_FUNC_MCC_SET_DURATION); + return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); +} diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 5fb8faad9c67..46d57414f24e 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -2767,6 +2767,355 @@ static inline void RTW89_SET_FWCMD_TSF32_TOGL_EARLY(void *cmd, u32 val) le32p_replace_bits((__le32 *)cmd, val, GENMASK(31, 16)); } +enum rtw89_fw_mcc_c2h_rpt_cfg { + RTW89_FW_MCC_C2H_RPT_OFF = 0, + RTW89_FW_MCC_C2H_RPT_FAIL_ONLY = 1, + RTW89_FW_MCC_C2H_RPT_ALL = 2, +}; + +struct rtw89_fw_mcc_add_req { + u8 macid; + u8 central_ch_seg0; + u8 central_ch_seg1; + u8 primary_ch; + enum rtw89_bandwidth bandwidth: 4; + u32 group: 2; + u32 c2h_rpt: 2; + u32 dis_tx_null: 1; + u32 dis_sw_retry: 1; + u32 in_curr_ch: 1; + u32 sw_retry_count: 3; + u32 tx_null_early: 4; + u32 btc_in_2g: 1; + u32 pta_en: 1; + u32 rfk_by_pass: 1; + u32 ch_band_type: 2; + u32 rsvd0: 9; + u32 duration; + u8 courtesy_en; + u8 courtesy_num; + u8 courtesy_target; + u8 rsvd1; +}; + +static inline void RTW89_SET_FWCMD_ADD_MCC_MACID(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(7, 0)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_CENTRAL_CH_SEG0(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(15, 8)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_CENTRAL_CH_SEG1(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(23, 16)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_PRIMARY_CH(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(31, 24)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_BANDWIDTH(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 1, val, GENMASK(3, 0)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_GROUP(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 1, val, GENMASK(5, 4)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_C2H_RPT(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 1, val, GENMASK(7, 6)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_DIS_TX_NULL(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 1, val, BIT(8)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_DIS_SW_RETRY(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 1, val, BIT(9)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_IN_CURR_CH(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 1, val, BIT(10)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_SW_RETRY_COUNT(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 1, val, GENMASK(13, 11)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_TX_NULL_EARLY(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 1, val, GENMASK(17, 14)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_BTC_IN_2G(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 1, val, BIT(18)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_PTA_EN(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 1, val, BIT(19)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_RFK_BY_PASS(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 1, val, BIT(20)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_CH_BAND_TYPE(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 1, val, GENMASK(22, 21)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_DURATION(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 2, val, GENMASK(31, 0)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_COURTESY_EN(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 3, val, BIT(0)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_COURTESY_NUM(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 3, val, GENMASK(15, 8)); +} + +static inline void RTW89_SET_FWCMD_ADD_MCC_COURTESY_TARGET(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 3, val, GENMASK(23, 16)); +} + +struct rtw89_fw_mcc_start_req { + u32 group: 2; + u32 btc_in_group: 1; + u32 old_group_action: 2; + u32 old_group: 2; + u32 rsvd0: 9; + u32 notify_cnt: 3; + u32 rsvd1: 2; + u32 notify_rxdbg_en: 1; + u32 rsvd2: 2; + u32 macid: 8; + u32 tsf_low; + u32 tsf_high; +}; + +static inline void RTW89_SET_FWCMD_START_MCC_GROUP(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(1, 0)); +} + +static inline void RTW89_SET_FWCMD_START_MCC_BTC_IN_GROUP(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, BIT(2)); +} + +static inline void RTW89_SET_FWCMD_START_MCC_OLD_GROUP_ACTION(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(4, 3)); +} + +static inline void RTW89_SET_FWCMD_START_MCC_OLD_GROUP(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(6, 5)); +} + +static inline void RTW89_SET_FWCMD_START_MCC_NOTIFY_CNT(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(18, 16)); +} + +static inline void RTW89_SET_FWCMD_START_MCC_NOTIFY_RXDBG_EN(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, BIT(21)); +} + +static inline void RTW89_SET_FWCMD_START_MCC_MACID(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(31, 24)); +} + +static inline void RTW89_SET_FWCMD_START_MCC_TSF_LOW(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 1, val, GENMASK(31, 0)); +} + +static inline void RTW89_SET_FWCMD_START_MCC_TSF_HIGH(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 2, val, GENMASK(31, 0)); +} + +static inline void RTW89_SET_FWCMD_STOP_MCC_MACID(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(7, 0)); +} + +static inline void RTW89_SET_FWCMD_STOP_MCC_GROUP(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(9, 8)); +} + +static inline void RTW89_SET_FWCMD_STOP_MCC_PREV_GROUPS(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, BIT(10)); +} + +static inline void RTW89_SET_FWCMD_DEL_MCC_GROUP_GROUP(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(1, 0)); +} + +static inline void RTW89_SET_FWCMD_DEL_MCC_GROUP_PREV_GROUPS(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, BIT(2)); +} + +static inline void RTW89_SET_FWCMD_RESET_MCC_GROUP_GROUP(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(1, 0)); +} + +struct rtw89_fw_mcc_tsf_req { + u8 group: 2; + u8 rsvd0: 6; + u8 macid_x; + u8 macid_y; + u8 rsvd1; +}; + +static inline void RTW89_SET_FWCMD_MCC_REQ_TSF_GROUP(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(1, 0)); +} + +static inline void RTW89_SET_FWCMD_MCC_REQ_TSF_MACID_X(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(15, 8)); +} + +static inline void RTW89_SET_FWCMD_MCC_REQ_TSF_MACID_Y(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(23, 16)); +} + +static inline void RTW89_SET_FWCMD_MCC_MACID_BITMAP_GROUP(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(1, 0)); +} + +static inline void RTW89_SET_FWCMD_MCC_MACID_BITMAP_MACID(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(15, 8)); +} + +static inline void RTW89_SET_FWCMD_MCC_MACID_BITMAP_BITMAP_LENGTH(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(23, 16)); +} + +static inline void RTW89_SET_FWCMD_MCC_MACID_BITMAP_BITMAP(void *cmd, + u8 *bitmap, u8 len) +{ + memcpy((__le32 *)cmd + 1, bitmap, len); +} + +static inline void RTW89_SET_FWCMD_MCC_SYNC_GROUP(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(1, 0)); +} + +static inline void RTW89_SET_FWCMD_MCC_SYNC_MACID_SOURCE(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(15, 8)); +} + +static inline void RTW89_SET_FWCMD_MCC_SYNC_MACID_TARGET(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(23, 16)); +} + +static inline void RTW89_SET_FWCMD_MCC_SYNC_SYNC_OFFSET(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(31, 24)); +} + +struct rtw89_fw_mcc_duration { + u32 group: 2; + u32 btc_in_group: 1; + u32 rsvd0: 5; + u32 start_macid: 8; + u32 macid_x: 8; + u32 macid_y: 8; + u32 start_tsf_low; + u32 start_tsf_high; + u32 duration_x; + u32 duration_y; +}; + +static inline void RTW89_SET_FWCMD_MCC_SET_DURATION_GROUP(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(1, 0)); +} + +static +inline void RTW89_SET_FWCMD_MCC_SET_DURATION_BTC_IN_GROUP(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, BIT(2)); +} + +static +inline void RTW89_SET_FWCMD_MCC_SET_DURATION_START_MACID(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(15, 8)); +} + +static inline void RTW89_SET_FWCMD_MCC_SET_DURATION_MACID_X(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(23, 16)); +} + +static inline void RTW89_SET_FWCMD_MCC_SET_DURATION_MACID_Y(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd, val, GENMASK(31, 24)); +} + +static +inline void RTW89_SET_FWCMD_MCC_SET_DURATION_START_TSF_LOW(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 1, val, GENMASK(31, 0)); +} + +static +inline void RTW89_SET_FWCMD_MCC_SET_DURATION_START_TSF_HIGH(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 2, val, GENMASK(31, 0)); +} + +static +inline void RTW89_SET_FWCMD_MCC_SET_DURATION_DURATION_X(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 3, val, GENMASK(31, 0)); +} + +static +inline void RTW89_SET_FWCMD_MCC_SET_DURATION_DURATION_Y(void *cmd, u32 val) +{ + le32p_replace_bits((__le32 *)cmd + 4, val, GENMASK(31, 0)); +} + #define RTW89_C2H_HEADER_LEN 8 #define RTW89_GET_C2H_CATEGORY(c2h) \ @@ -3185,9 +3534,27 @@ int rtw89_fw_h2c_wow_global(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, bool enable); int rtw89_fw_h2c_wow_wakeup_ctrl(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, bool enable); - int rtw89_fw_wow_cam_update(struct rtw89_dev *rtwdev, struct rtw89_wow_cam_info *cam_info); +int rtw89_fw_h2c_add_mcc(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mcc_add_req *p); +int rtw89_fw_h2c_start_mcc(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mcc_start_req *p); +int rtw89_fw_h2c_stop_mcc(struct rtw89_dev *rtwdev, u8 group, u8 macid, + bool prev_groups); +int rtw89_fw_h2c_del_mcc_group(struct rtw89_dev *rtwdev, u8 group, + bool prev_groups); +int rtw89_fw_h2c_reset_mcc_group(struct rtw89_dev *rtwdev, u8 group); +int rtw89_fw_h2c_mcc_req_tsf(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mcc_tsf_req *req, + struct rtw89_mac_mcc_tsf_rpt *rpt); +int rtw89_fw_h2c_mcc_macid_bitamp(struct rtw89_dev *rtwdev, u8 group, u8 macid, + u8 *bitmap); +int rtw89_fw_h2c_mcc_sync(struct rtw89_dev *rtwdev, u8 group, u8 source, + u8 target, u8 offset); +int rtw89_fw_h2c_mcc_set_duration(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mcc_duration *p); + static inline void rtw89_fw_h2c_init_ba_cam(struct rtw89_dev *rtwdev) { const struct rtw89_chip_info *chip = rtwdev->chip; From 75ee07b03fc6ec0a7ac7407f9ea7b3e981efb28f Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 29 Nov 2022 16:31:30 +0800 Subject: [PATCH 051/144] wifi: rtw89: link rtw89_vif and chanctx stuffs First, introduce struct rtw89_sub_entity for chanctx related stuffs. Second, add enum rtw89_sub_entity_idx to rtw89_vif for vif operation to access its/right chanctx stuffs after future multi-channel support. Besides, RTW89_SUB_ENTITY_0 is the default chanctx entry throughout driver, i.e. it's used for things which may not have a target chanctx yet. So, we need to ensure that RTW89_SUB_ENTITY_0 is always working. If there is at least one alive chanctx, then one of them must take RTW89_SUB_ENTITY_0. If no alive chanctx, RTW89_SUB_ENTITY_0 will be filled by rtw89_config_default_chandef(). Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221129083130.45708-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/chan.c | 40 +++++++++++++++++-- drivers/net/wireless/realtek/rtw89/core.h | 20 ++++++---- drivers/net/wireless/realtek/rtw89/mac80211.c | 1 + 3 files changed, 50 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c index a4f61c2f6512..90596806bc93 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.c +++ b/drivers/net/wireless/realtek/rtw89/chan.c @@ -4,6 +4,7 @@ #include "chan.h" #include "debug.h" +#include "util.h" static enum rtw89_subband rtw89_get_subband_type(enum rtw89_band band, u8 center_chan) @@ -108,8 +109,8 @@ bool rtw89_assign_entity_chan(struct rtw89_dev *rtwdev, const struct rtw89_chan *new) { struct rtw89_hal *hal = &rtwdev->hal; - struct rtw89_chan *chan = &hal->chan[idx]; - struct rtw89_chan_rcd *rcd = &hal->chan_rcd[idx]; + struct rtw89_chan *chan = &hal->sub[idx].chan; + struct rtw89_chan_rcd *rcd = &hal->sub[idx].rcd; bool band_changed; rcd->prev_primary_channel = chan->primary_channel; @@ -127,7 +128,7 @@ static void __rtw89_config_entity_chandef(struct rtw89_dev *rtwdev, { struct rtw89_hal *hal = &rtwdev->hal; - hal->chandef[idx] = *chandef; + hal->sub[idx].chandef = *chandef; if (from_stack) set_bit(idx, hal->entity_map); @@ -195,6 +196,7 @@ int rtw89_chanctx_ops_add(struct rtw89_dev *rtwdev, rtw89_config_entity_chandef(rtwdev, idx, &ctx->def); rtw89_set_channel(rtwdev); cfg->idx = idx; + hal->sub[idx].cfg = cfg; return 0; } @@ -203,8 +205,34 @@ void rtw89_chanctx_ops_remove(struct rtw89_dev *rtwdev, { struct rtw89_hal *hal = &rtwdev->hal; struct rtw89_chanctx_cfg *cfg = (struct rtw89_chanctx_cfg *)ctx->drv_priv; + struct rtw89_vif *rtwvif; + u8 drop, roll; - clear_bit(cfg->idx, hal->entity_map); + drop = cfg->idx; + if (drop != RTW89_SUB_ENTITY_0) + goto out; + + roll = find_next_bit(hal->entity_map, NUM_OF_RTW89_SUB_ENTITY, drop + 1); + + /* Follow rtw89_config_default_chandef() when rtw89_entity_recalc(). */ + if (roll == NUM_OF_RTW89_SUB_ENTITY) + goto out; + + /* RTW89_SUB_ENTITY_0 is going to release, and another exists. + * Make another roll down to RTW89_SUB_ENTITY_0 to replace. + */ + hal->sub[roll].cfg->idx = RTW89_SUB_ENTITY_0; + hal->sub[RTW89_SUB_ENTITY_0] = hal->sub[roll]; + + rtw89_for_each_rtwvif(rtwdev, rtwvif) { + if (rtwvif->sub_entity_idx == roll) + rtwvif->sub_entity_idx = RTW89_SUB_ENTITY_0; + } + + drop = roll; + +out: + clear_bit(drop, hal->entity_map); rtw89_set_channel(rtwdev); } @@ -225,6 +253,9 @@ int rtw89_chanctx_ops_assign_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, struct ieee80211_chanctx_conf *ctx) { + struct rtw89_chanctx_cfg *cfg = (struct rtw89_chanctx_cfg *)ctx->drv_priv; + + rtwvif->sub_entity_idx = cfg->idx; return 0; } @@ -232,4 +263,5 @@ void rtw89_chanctx_ops_unassign_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, struct ieee80211_chanctx_conf *ctx) { + rtwvif->sub_entity_idx = RTW89_SUB_ENTITY_0; } diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index f4603c2ce8f1..2badb96d2ae3 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -2240,6 +2240,8 @@ struct rtw89_phy_rate_pattern { struct rtw89_vif { struct list_head list; struct rtw89_dev *rtwdev; + enum rtw89_sub_entity_idx sub_entity_idx; + u8 mac_id; u8 port; u8 mac_addr[ETH_ALEN]; @@ -2953,6 +2955,13 @@ enum rtw89_entity_mode { RTW89_ENTITY_MODE_SCC, }; +struct rtw89_sub_entity { + struct cfg80211_chan_def chandef; + struct rtw89_chan chan; + struct rtw89_chan_rcd rcd; + struct rtw89_chanctx_cfg *cfg; +}; + struct rtw89_hal { u32 rx_fltr; u8 cv; @@ -2966,13 +2975,10 @@ struct rtw89_hal { bool support_igi; DECLARE_BITMAP(entity_map, NUM_OF_RTW89_SUB_ENTITY); - struct cfg80211_chan_def chandef[NUM_OF_RTW89_SUB_ENTITY]; + struct rtw89_sub_entity sub[NUM_OF_RTW89_SUB_ENTITY]; bool entity_active; enum rtw89_entity_mode entity_mode; - - struct rtw89_chan chan[NUM_OF_RTW89_SUB_ENTITY]; - struct rtw89_chan_rcd chan_rcd[NUM_OF_RTW89_SUB_ENTITY]; }; #define RTW89_MAX_MAC_ID_NUM 128 @@ -4138,7 +4144,7 @@ const struct cfg80211_chan_def *rtw89_chandef_get(struct rtw89_dev *rtwdev, { struct rtw89_hal *hal = &rtwdev->hal; - return &hal->chandef[idx]; + return &hal->sub[idx].chandef; } static inline @@ -4147,7 +4153,7 @@ const struct rtw89_chan *rtw89_chan_get(struct rtw89_dev *rtwdev, { struct rtw89_hal *hal = &rtwdev->hal; - return &hal->chan[idx]; + return &hal->sub[idx].chan; } static inline @@ -4156,7 +4162,7 @@ const struct rtw89_chan_rcd *rtw89_chan_rcd_get(struct rtw89_dev *rtwdev, { struct rtw89_hal *hal = &rtwdev->hal; - return &hal->chan_rcd[idx]; + return &hal->sub[idx].rcd; } static inline void rtw89_chip_fem_setup(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index ce980d2f22c4..1a99267d710d 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -131,6 +131,7 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw, rtwvif->bcn_hit_cond = 0; rtwvif->mac_idx = RTW89_MAC_0; rtwvif->phy_idx = RTW89_PHY_0; + rtwvif->sub_entity_idx = RTW89_SUB_ENTITY_0; rtwvif->hit_rule = 0; ether_addr_copy(rtwvif->mac_addr, vif->addr); From 278ab9793116a8531ffaa52b4e61644971131e35 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 30 Nov 2022 13:26:45 -0800 Subject: [PATCH 052/144] wifi: ieee80211: Do not open-code qos address offsets When building with -Wstringop-overflow, GCC's KASAN implementation does not correctly perform bounds checking within some complex structures when faced with literal offsets, and can get very confused. For example, this warning is seen due to literal offsets into sturct ieee80211_hdr that may or may not be large enough: drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c: In function 'iwl_mvm_rx_mpdu_mq': drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c:2022:29: warning: writing 1 byte into a region of size 0 [-Wstringop-overflow=] 2022 | *qc &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT; In file included from drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h:32, from drivers/net/wireless/intel/iwlwifi/mvm/sta.h:15, from drivers/net/wireless/intel/iwlwifi/mvm/mvm.h:27, from drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c:10: drivers/net/wireless/intel/iwlwifi/mvm/../fw/api/rx.h:559:16: note: at offset [78, 166] into destination object 'mpdu_len' of size 2 559 | __le16 mpdu_len; | ^~~~~~~~ Refactor ieee80211_get_qos_ctl() to avoid using literal offsets, requiring the creation of the actual structure that is described in the comments. Explicitly choose the desired offset, making the code more human-readable too. This is one of the last remaining warning to fix before enabling -Wstringop-overflow globally. Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97490 Link: https://github.com/KSPP/linux/issues/181 Cc: Johannes Berg Cc: Kalle Valo Cc: Gregory Greenman Cc: "Gustavo A. R. Silva" Cc: linux-wireless@vger.kernel.org Cc: netdev@vger.kernel.org Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20221130212641.never.627-kees@kernel.org Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 6252f02f38b7..80d6308dea06 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -338,6 +338,17 @@ struct ieee80211_qos_hdr { __le16 qos_ctrl; } __packed __aligned(2); +struct ieee80211_qos_hdr_4addr { + __le16 frame_control; + __le16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + __le16 seq_ctrl; + u8 addr4[ETH_ALEN]; + __le16 qos_ctrl; +} __packed __aligned(2); + struct ieee80211_trigger { __le16 frame_control; __le16 duration; @@ -4060,16 +4071,21 @@ struct ieee80211_he_6ghz_capa { * @hdr: the frame * * The qos ctrl bytes come after the frame_control, duration, seq_num - * and 3 or 4 addresses of length ETH_ALEN. - * 3 addr: 2 + 2 + 2 + 3*6 = 24 - * 4 addr: 2 + 2 + 2 + 4*6 = 30 + * and 3 or 4 addresses of length ETH_ALEN. Checks frame_control to choose + * between struct ieee80211_qos_hdr_4addr and struct ieee80211_qos_hdr. */ static inline u8 *ieee80211_get_qos_ctl(struct ieee80211_hdr *hdr) { - if (ieee80211_has_a4(hdr->frame_control)) - return (u8 *)hdr + 30; + union { + struct ieee80211_qos_hdr addr3; + struct ieee80211_qos_hdr_4addr addr4; + } *qos; + + qos = (void *)hdr; + if (ieee80211_has_a4(qos->addr3.frame_control)) + return (u8 *)&qos->addr4.qos_ctrl; else - return (u8 *)hdr + 24; + return (u8 *)&qos->addr3.qos_ctrl; } /** From 5cc58b376675981386c6192405fe887cd29c527a Mon Sep 17 00:00:00 2001 From: Yuan Can Date: Tue, 29 Nov 2022 01:42:11 +0000 Subject: [PATCH 053/144] wifi: nl80211: Add checks for nla_nest_start() in nl80211_send_iface() As the nla_nest_start() may fail with NULL returned, the return value needs to be checked. Fixes: ce08cd344a00 ("wifi: nl80211: expose link information for interfaces") Signed-off-by: Yuan Can Link: https://lore.kernel.org/r/20221129014211.56558-1-yuancan@huawei.com Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 1ad0326ff4dc..33a82ecab9d5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3868,6 +3868,9 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag struct cfg80211_chan_def chandef = {}; int ret; + if (!link) + goto nla_put_failure; + if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) goto nla_put_failure; if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, From 13e5afd3d773c6fc6ca2b89027befaaaa1ea7293 Mon Sep 17 00:00:00 2001 From: Zhengchao Shao Date: Thu, 17 Nov 2022 14:45:00 +0800 Subject: [PATCH 054/144] wifi: mac80211: fix memory leak in ieee80211_if_add() When register_netdevice() failed in ieee80211_if_add(), ndev->tstats isn't released. Fix it. Fixes: 5a490510ba5f ("mac80211: use per-CPU TX/RX statistics") Signed-off-by: Zhengchao Shao Link: https://lore.kernel.org/r/20221117064500.319983-1-shaozhengchao@huawei.com Signed-off-by: Johannes Berg --- net/mac80211/iface.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 7c4ce716c939..a5782927a93b 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -2195,6 +2195,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, ret = cfg80211_register_netdevice(ndev); if (ret) { + ieee80211_if_free(ndev); free_netdev(ndev); return ret; } From 4d371d6e3746d84056f150fdaa66c85f65f60124 Mon Sep 17 00:00:00 2001 From: Philipp Hortmann Date: Mon, 14 Nov 2022 21:01:35 +0100 Subject: [PATCH 055/144] wifi: cfg80211: Correct example of ieee80211_iface_limit Correct wrong closing bracket. Signed-off-by: Philipp Hortmann Link: https://lore.kernel.org/r/20221114200135.GA100176@matrix-ESPRIMO-P710 Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 11a370e64143..03d4f4deadae 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4742,7 +4742,7 @@ struct ieee80211_iface_limit { * * struct ieee80211_iface_limit limits1[] = { * { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), }, - * { .max = 1, .types = BIT(NL80211_IFTYPE_AP}, }, + * { .max = 1, .types = BIT(NL80211_IFTYPE_AP), }, * }; * struct ieee80211_iface_combination combination1 = { * .limits = limits1, From 09d838a457a89883a926b8b0104d575158fd4b92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8D=C3=B1igo=20Huguet?= Date: Fri, 11 Nov 2022 16:36:22 +0100 Subject: [PATCH 056/144] wifi: mac80211: fix maybe-unused warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In ieee80211_lookup_key, the variable named `local` is unused if compiled without lockdep, getting this warning: net/mac80211/cfg.c: In function ‘ieee80211_lookup_key’: net/mac80211/cfg.c:542:26: error: unused variable ‘local’ [-Werror=unused-variable] struct ieee80211_local *local = sdata->local; ^~~~~ Fix it with __maybe_unused. Fixes: 8cbf0c2ab6df ("wifi: mac80211: refactor some key code") Signed-off-by: Íñigo Huguet Link: https://lore.kernel.org/r/20221111153622.29016-1-ihuguet@redhat.com Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index c848fe04dd44..8f9a2ab502b3 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -576,7 +576,7 @@ static struct ieee80211_key * ieee80211_lookup_key(struct ieee80211_sub_if_data *sdata, int link_id, u8 key_idx, bool pairwise, const u8 *mac_addr) { - struct ieee80211_local *local = sdata->local; + struct ieee80211_local *local __maybe_unused = sdata->local; struct ieee80211_link_data *link = &sdata->deflink; struct ieee80211_key *key; From c1d3214d61d93eb4a3959c6b402230988d0f362d Mon Sep 17 00:00:00 2001 From: JUN-KYU SHIN Date: Fri, 11 Nov 2022 11:33:04 +0900 Subject: [PATCH 057/144] wifi: cfg80211: fix comparison of BSS frequencies If the "channel->freq_offset" comparison is omitted in cmp_bss(), BSS with different kHz units cannot be distinguished in the S1G Band. So "freq_offset" should also be included in the comparison. Signed-off-by: JUN-KYU SHIN Link: https://lore.kernel.org/r/20221111023301.6395-1-jk.shin@newratek.com Signed-off-by: Johannes Berg --- net/wireless/scan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index e70302a30013..a39c93753213 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -1289,7 +1289,8 @@ static int cmp_bss(struct cfg80211_bss *a, int i, r; if (a->channel != b->channel) - return b->channel->center_freq - a->channel->center_freq; + return (b->channel->center_freq * 1000 + b->channel->freq_offset) - + (a->channel->center_freq * 1000 + a->channel->freq_offset); a_ies = rcu_access_pointer(a->ies); if (!a_ies) From 833a9fd28c9b7ccb39a334721379e992dc1c0c89 Mon Sep 17 00:00:00 2001 From: Chen Zhongjin Date: Wed, 9 Nov 2022 17:02:37 +0800 Subject: [PATCH 058/144] wifi: cfg80211: Fix not unregister reg_pdev when load_builtin_regdb_keys() fails In regulatory_init_db(), when it's going to return a error, reg_pdev should be unregistered. When load_builtin_regdb_keys() fails it doesn't do it and makes cfg80211 can't be reload with report: sysfs: cannot create duplicate filename '/devices/platform/regulatory.0' ... dump_stack_lvl+0x79/0x9b sysfs_warn_dup.cold+0x1c/0x29 sysfs_create_dir_ns+0x22d/0x290 kobject_add_internal+0x247/0x800 kobject_add+0x135/0x1b0 device_add+0x389/0x1be0 platform_device_add+0x28f/0x790 platform_device_register_full+0x376/0x4b0 regulatory_init+0x9a/0x4b2 [cfg80211] cfg80211_init+0x84/0x113 [cfg80211] ... Fixes: 90a53e4432b1 ("cfg80211: implement regdb signature checking") Signed-off-by: Chen Zhongjin Link: https://lore.kernel.org/r/20221109090237.214127-1-chenzhongjin@huawei.com Signed-off-by: Johannes Berg --- net/wireless/reg.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index c3d950d29432..4f3f31244e8b 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -4311,8 +4311,10 @@ static int __init regulatory_init_db(void) return -EINVAL; err = load_builtin_regdb_keys(); - if (err) + if (err) { + platform_device_unregister(reg_pdev); return err; + } /* We always try to get an update for the static regdomain */ err = regulatory_hint_core(cfg80211_world_regdom->alpha2); From 9445096319206814e462b904915b645c4f2bf514 Mon Sep 17 00:00:00 2001 From: Alexander Wetzel Date: Mon, 7 Nov 2022 17:13:28 +0100 Subject: [PATCH 059/144] wifi: mac80211: Drop not needed check for NULL ieee80211_get_txq() can only be called with vif != NULL. Remove not needed NULL test in function. Signed-off-by: Alexander Wetzel Reported-by: kernel test robot Reported-by: Dan Carpenter Link: https://lore.kernel.org/r/20221107161328.2883-1-alexander@wetzel-home.de Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 165ac0711d71..3ed5ca4a2bae 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1343,7 +1343,7 @@ static struct txq_info *ieee80211_get_txq(struct ieee80211_local *local, return NULL; txq = sta->sta.txq[tid]; - } else if (vif) { + } else { txq = vif->txq; } From b2ddde566de409a3dbf708fe89ecbe114dd14cc3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 2 Nov 2022 09:47:47 +0100 Subject: [PATCH 060/144] wifi: mac80211: remove unnecessary synchronize_net() The call to ieee80211_do_stop() right after will also do synchronize_rcu() to ensure the SDATA_STATE_RUNNING bit is cleared, so we don't need to synchronize_net() here. Change-Id: Id9f9ffcf195002013e5d9fde288877d219780864 Signed-off-by: Johannes Berg --- net/mac80211/iface.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index a5782927a93b..7b2843df3813 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1849,8 +1849,7 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata, ieee80211_stop_vif_queues(local, sdata, IEEE80211_QUEUE_STOP_REASON_IFTYPE_CHANGE); - synchronize_net(); - + /* do_stop will synchronize_rcu() first thing */ ieee80211_do_stop(sdata, false); ieee80211_teardown_sdata(sdata); From 61e41e5dfcc22e5e65b6537453fd2f03ac768b82 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 17 Oct 2022 09:03:48 +0200 Subject: [PATCH 061/144] wifi: cfg80211: use bss_from_pub() instead of container_of() There's no need to open-code container_of() when we have bss_from_pub(). Use it. Change-Id: I074723717909ba211a40e6499f0c36df0e2ba4be Signed-off-by: Johannes Berg --- net/wireless/scan.c | 41 +++++++++++------------------------------ 1 file changed, 11 insertions(+), 30 deletions(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index a39c93753213..8cf405776dde 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -158,9 +158,8 @@ static inline void bss_ref_put(struct cfg80211_registered_device *rdev, if (bss->pub.hidden_beacon_bss) { struct cfg80211_internal_bss *hbss; - hbss = container_of(bss->pub.hidden_beacon_bss, - struct cfg80211_internal_bss, - pub); + + hbss = bss_from_pub(bss->pub.hidden_beacon_bss); hbss->refcount--; if (hbss->refcount == 0) bss_free(hbss); @@ -169,9 +168,7 @@ static inline void bss_ref_put(struct cfg80211_registered_device *rdev, if (bss->pub.transmitted_bss) { struct cfg80211_internal_bss *tbss; - tbss = container_of(bss->pub.transmitted_bss, - struct cfg80211_internal_bss, - pub); + tbss = bss_from_pub(bss->pub.transmitted_bss); tbss->refcount--; if (tbss->refcount == 0) bss_free(tbss); @@ -1791,13 +1788,8 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev, /* This must be before the call to bss_ref_get */ if (tmp->pub.transmitted_bss) { - struct cfg80211_internal_bss *pbss = - container_of(tmp->pub.transmitted_bss, - struct cfg80211_internal_bss, - pub); - new->pub.transmitted_bss = tmp->pub.transmitted_bss; - bss_ref_get(rdev, pbss); + bss_ref_get(rdev, bss_from_pub(tmp->pub.transmitted_bss)); } list_add_tail(&new->list, &rdev->bss_list); @@ -2570,15 +2562,12 @@ EXPORT_SYMBOL(cfg80211_inform_bss_frame_data); void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) { struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); - struct cfg80211_internal_bss *bss; if (!pub) return; - bss = container_of(pub, struct cfg80211_internal_bss, pub); - spin_lock_bh(&rdev->bss_lock); - bss_ref_get(rdev, bss); + bss_ref_get(rdev, bss_from_pub(pub)); spin_unlock_bh(&rdev->bss_lock); } EXPORT_SYMBOL(cfg80211_ref_bss); @@ -2586,15 +2575,12 @@ EXPORT_SYMBOL(cfg80211_ref_bss); void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) { struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); - struct cfg80211_internal_bss *bss; if (!pub) return; - bss = container_of(pub, struct cfg80211_internal_bss, pub); - spin_lock_bh(&rdev->bss_lock); - bss_ref_put(rdev, bss); + bss_ref_put(rdev, bss_from_pub(pub)); spin_unlock_bh(&rdev->bss_lock); } EXPORT_SYMBOL(cfg80211_put_bss); @@ -2608,7 +2594,7 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) if (WARN_ON(!pub)) return; - bss = container_of(pub, struct cfg80211_internal_bss, pub); + bss = bss_from_pub(pub); spin_lock_bh(&rdev->bss_lock); if (list_empty(&bss->list)) @@ -2617,8 +2603,7 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) list_for_each_entry_safe(nontrans_bss, tmp, &pub->nontrans_list, nontrans_list) { - tmp1 = container_of(nontrans_bss, - struct cfg80211_internal_bss, pub); + tmp1 = bss_from_pub(nontrans_bss); if (__cfg80211_unlink_bss(rdev, tmp1)) rdev->bss_generation++; } @@ -2675,9 +2660,7 @@ void cfg80211_update_assoc_bss_entry(struct wireless_dev *wdev, /* use transmitting bss */ if (cbss->pub.transmitted_bss) - cbss = container_of(cbss->pub.transmitted_bss, - struct cfg80211_internal_bss, - pub); + cbss = bss_from_pub(cbss->pub.transmitted_bss); cbss->pub.channel = chan; @@ -2706,8 +2689,7 @@ void cfg80211_update_assoc_bss_entry(struct wireless_dev *wdev, list_for_each_entry_safe(nontrans_bss, tmp, &new->pub.nontrans_list, nontrans_list) { - bss = container_of(nontrans_bss, - struct cfg80211_internal_bss, pub); + bss = bss_from_pub(nontrans_bss); if (__cfg80211_unlink_bss(rdev, bss)) rdev->bss_generation++; } @@ -2724,8 +2706,7 @@ void cfg80211_update_assoc_bss_entry(struct wireless_dev *wdev, list_for_each_entry_safe(nontrans_bss, tmp, &cbss->pub.nontrans_list, nontrans_list) { - bss = container_of(nontrans_bss, - struct cfg80211_internal_bss, pub); + bss = bss_from_pub(nontrans_bss); bss->pub.channel = chan; rb_erase(&bss->rbn, &rdev->bss_tree); rb_insert_bss(rdev, bss); From 8950b5988a9a2957b4c2c6f20143dfc2a0230a74 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 14 Oct 2022 18:56:11 +0200 Subject: [PATCH 062/144] wifi: mac80211: don't parse multi-BSSID in assoc resp It's not valid to have the multiple BSSID element in the association response (per 802.11 REVme D1.0), so don't try to parse it there, but only in the fallback beacon elements if needed. The other case that was parsing association requests was already changed in a previous commit. Change-Id: I659d2ef1253e079cc71c46a017044e116e31c024 Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index a804e0220ed7..0aee2392dd29 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3932,7 +3932,6 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, struct ieee80211_elems_parse_params parse_params = { .start = elem_start, .len = elem_len, - .bss = cbss, .link_id = link_id == assoc_data->assoc_link_id ? -1 : link_id, .from_ap = true, }; @@ -4017,6 +4016,7 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, parse_params.start = bss_ies->data; parse_params.len = bss_ies->len; + parse_params.bss = cbss; bss_elems = ieee802_11_parse_elems_full(&parse_params); if (!bss_elems) { ret = false; From 209d70d34a7fd32e6fb6784f107913b0d18fe501 Mon Sep 17 00:00:00 2001 From: Kieran Frewen Date: Mon, 7 Nov 2022 11:16:02 +1300 Subject: [PATCH 063/144] wifi: mac80211: update TIM for S1G specification changes Updates to the TIM information element to match changes made in the IEEE Std 802.11ah-2020. Signed-off-by: Kieran Frewen Co-developed-by: Gilad Itzkovitch Signed-off-by: Gilad Itzkovitch Link: https://lore.kernel.org/r/20221106221602.25714-1-gilad.itzkovitch@morsemicro.com [use skb_put_data/skb_put_u8] Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 3ed5ca4a2bae..774e62860c45 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -4774,9 +4774,9 @@ static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, ps->dtim_count--; } - tim = pos = skb_put(skb, 6); + tim = pos = skb_put(skb, 5); *pos++ = WLAN_EID_TIM; - *pos++ = 4; + *pos++ = 3; *pos++ = ps->dtim_count; *pos++ = link_conf->dtim_period; @@ -4807,13 +4807,17 @@ static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, /* Bitmap control */ *pos++ = n1 | aid0; /* Part Virt Bitmap */ - skb_put(skb, n2 - n1); - memcpy(pos, ps->tim + n1, n2 - n1 + 1); + skb_put_data(skb, ps->tim + n1, n2 - n1 + 1); tim[1] = n2 - n1 + 4; } else { *pos++ = aid0; /* Bitmap control */ - *pos++ = 0; /* Part Virt Bitmap */ + + if (ieee80211_get_link_sband(link)->band != NL80211_BAND_S1GHZ) { + tim[1] = 4; + /* Part Virt Bitmap */ + skb_put_u8(skb, 0); + } } } From 7d360f6061db01830adfdb1eaa3977b19db0c30b Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 10 Oct 2022 11:43:38 +0200 Subject: [PATCH 064/144] wifi: mac80211: add support for restricting netdev features per vif This can be used to selectively disable feature flags for checksum offload, scatter/gather or GSO by changing vif->netdev_features. Removing features from vif->netdev_features does not affect the netdev features themselves, but instead fixes up skbs in the tx path so that the offloads are not needed in the driver. Aside from making it easier to deal with vif type based hardware limitations, this also makes it possible to optimize performance on hardware without native GSO support by declaring GSO support in hw->netdev_features and removing it from vif->netdev_features. This allows mac80211 to handle GSO segmentation after the sta lookup, but before itxq enqueue, thus reducing the number of unnecessary sta lookups, as well as some other per-packet processing. Signed-off-by: Felix Fietkau Link: https://lore.kernel.org/r/20221010094338.78070-1-nbd@nbd.name Signed-off-by: Johannes Berg --- include/net/fq_impl.h | 16 +- include/net/mac80211.h | 5 + net/mac80211/iface.c | 1 + net/mac80211/tx.c | 345 ++++++++++++++++++++++++++--------------- 4 files changed, 233 insertions(+), 134 deletions(-) diff --git a/include/net/fq_impl.h b/include/net/fq_impl.h index 524b510f1c68..9467e33dfb36 100644 --- a/include/net/fq_impl.h +++ b/include/net/fq_impl.h @@ -200,6 +200,7 @@ static void fq_tin_enqueue(struct fq *fq, fq_skb_free_t free_func) { struct fq_flow *flow; + struct sk_buff *next; bool oom; lockdep_assert_held(&fq->lock); @@ -214,11 +215,15 @@ static void fq_tin_enqueue(struct fq *fq, } flow->tin = tin; - flow->backlog += skb->len; - tin->backlog_bytes += skb->len; - tin->backlog_packets++; - fq->memory_usage += skb->truesize; - fq->backlog++; + skb_list_walk_safe(skb, skb, next) { + skb_mark_not_on_list(skb); + flow->backlog += skb->len; + tin->backlog_bytes += skb->len; + tin->backlog_packets++; + fq->memory_usage += skb->truesize; + fq->backlog++; + __skb_queue_tail(&flow->queue, skb); + } if (list_empty(&flow->flowchain)) { flow->deficit = fq->quantum; @@ -226,7 +231,6 @@ static void fq_tin_enqueue(struct fq *fq, &tin->new_flows); } - __skb_queue_tail(&flow->queue, skb); oom = (fq->memory_usage > fq->memory_limit); while (fq->backlog > fq->limit || oom) { flow = fq_find_fattest_flow(fq); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 721c450a9ccd..689da327ce2e 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1807,6 +1807,10 @@ struct ieee80211_vif_cfg { * @addr: address of this interface * @p2p: indicates whether this AP or STA interface is a p2p * interface, i.e. a GO or p2p-sta respectively + * @netdev_features: tx netdev features supported by the hardware for this + * vif. mac80211 initializes this to hw->netdev_features, and the driver + * can mask out specific tx features. mac80211 will handle software fixup + * for masked offloads (GSO, CSUM) * @driver_flags: flags/capabilities the driver has for this interface, * these need to be set (or cleared) when the interface is added * or, if supported by the driver, the interface type is changed @@ -1848,6 +1852,7 @@ struct ieee80211_vif { struct ieee80211_txq *txq; + netdev_features_t netdev_features; u32 driver_flags; u32 offload_flags; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 7b2843df3813..d49a5906a943 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -2178,6 +2178,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE; ndev->hw_features |= ndev->features & MAC80211_SUPPORTED_FEATURES_TX; + sdata->vif.netdev_features = local->hw.netdev_features; netdev_set_default_ethtool_ops(ndev, &ieee80211_ethtool_ops); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 774e62860c45..2171cd1ca807 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1355,7 +1355,11 @@ static struct txq_info *ieee80211_get_txq(struct ieee80211_local *local, static void ieee80211_set_skb_enqueue_time(struct sk_buff *skb) { - IEEE80211_SKB_CB(skb)->control.enqueue_time = codel_get_time(); + struct sk_buff *next; + codel_time_t now = codel_get_time(); + + skb_list_walk_safe(skb, skb, next) + IEEE80211_SKB_CB(skb)->control.enqueue_time = now; } static u32 codel_skb_len_func(const struct sk_buff *skb) @@ -3578,21 +3582,156 @@ ieee80211_xmit_fast_finish(struct ieee80211_sub_if_data *sdata, return TX_CONTINUE; } +static netdev_features_t +ieee80211_sdata_netdev_features(struct ieee80211_sub_if_data *sdata) +{ + if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN) + return sdata->vif.netdev_features; + + if (!sdata->bss) + return 0; + + sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); + return sdata->vif.netdev_features; +} + +static struct sk_buff * +ieee80211_tx_skb_fixup(struct sk_buff *skb, netdev_features_t features) +{ + if (skb_is_gso(skb)) { + struct sk_buff *segs; + + segs = skb_gso_segment(skb, features); + if (!segs) + return skb; + if (IS_ERR(segs)) + goto free; + + consume_skb(skb); + return segs; + } + + if (skb_needs_linearize(skb, features) && __skb_linearize(skb)) + goto free; + + if (skb->ip_summed == CHECKSUM_PARTIAL) { + int ofs = skb_checksum_start_offset(skb); + + if (skb->encapsulation) + skb_set_inner_transport_header(skb, ofs); + else + skb_set_transport_header(skb, ofs); + + if (skb_csum_hwoffload_help(skb, features)) + goto free; + } + + skb_mark_not_on_list(skb); + return skb; + +free: + kfree_skb(skb); + return NULL; +} + +static void __ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata, + struct sta_info *sta, + struct ieee80211_fast_tx *fast_tx, + struct sk_buff *skb, u8 tid, bool ampdu) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_hdr *hdr = (void *)fast_tx->hdr; + struct ieee80211_tx_info *info; + struct ieee80211_tx_data tx; + ieee80211_tx_result r; + int hw_headroom = sdata->local->hw.extra_tx_headroom; + int extra_head = fast_tx->hdr_len - (ETH_HLEN - 2); + struct ethhdr eth; + + skb = skb_share_check(skb, GFP_ATOMIC); + if (unlikely(!skb)) + return; + + if ((hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) && + ieee80211_amsdu_aggregate(sdata, sta, fast_tx, skb)) + return; + + /* will not be crypto-handled beyond what we do here, so use false + * as the may-encrypt argument for the resize to not account for + * more room than we already have in 'extra_head' + */ + if (unlikely(ieee80211_skb_resize(sdata, skb, + max_t(int, extra_head + hw_headroom - + skb_headroom(skb), 0), + ENCRYPT_NO))) + goto free; + + memcpy(ð, skb->data, ETH_HLEN - 2); + hdr = skb_push(skb, extra_head); + memcpy(skb->data, fast_tx->hdr, fast_tx->hdr_len); + memcpy(skb->data + fast_tx->da_offs, eth.h_dest, ETH_ALEN); + memcpy(skb->data + fast_tx->sa_offs, eth.h_source, ETH_ALEN); + + info = IEEE80211_SKB_CB(skb); + memset(info, 0, sizeof(*info)); + info->band = fast_tx->band; + info->control.vif = &sdata->vif; + info->flags = IEEE80211_TX_CTL_FIRST_FRAGMENT | + IEEE80211_TX_CTL_DONTFRAG | + (ampdu ? IEEE80211_TX_CTL_AMPDU : 0); + info->control.flags = IEEE80211_TX_CTRL_FAST_XMIT | + u32_encode_bits(IEEE80211_LINK_UNSPECIFIED, + IEEE80211_TX_CTRL_MLO_LINK); + +#ifdef CONFIG_MAC80211_DEBUGFS + if (local->force_tx_status) + info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; +#endif + + if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) { + tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; + *ieee80211_get_qos_ctl(hdr) = tid; + } + + __skb_queue_head_init(&tx.skbs); + + tx.flags = IEEE80211_TX_UNICAST; + tx.local = local; + tx.sdata = sdata; + tx.sta = sta; + tx.key = fast_tx->key; + + if (ieee80211_queue_skb(local, sdata, sta, skb)) + return; + + tx.skb = skb; + r = ieee80211_xmit_fast_finish(sdata, sta, fast_tx->pn_offs, + fast_tx->key, &tx); + tx.skb = NULL; + if (r == TX_DROP) + goto free; + + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + sdata = container_of(sdata->bss, + struct ieee80211_sub_if_data, u.ap); + + __skb_queue_tail(&tx.skbs, skb); + ieee80211_tx_frags(local, &sdata->vif, sta, &tx.skbs, false); + return; + +free: + kfree_skb(skb); +} + static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, struct ieee80211_fast_tx *fast_tx, struct sk_buff *skb) { - struct ieee80211_local *local = sdata->local; u16 ethertype = (skb->data[12] << 8) | skb->data[13]; - int extra_head = fast_tx->hdr_len - (ETH_HLEN - 2); - int hw_headroom = sdata->local->hw.extra_tx_headroom; - struct ethhdr eth; - struct ieee80211_tx_info *info; struct ieee80211_hdr *hdr = (void *)fast_tx->hdr; - struct ieee80211_tx_data tx; - ieee80211_tx_result r; struct tid_ampdu_tx *tid_tx = NULL; + struct sk_buff *next; u8 tid = IEEE80211_NUM_TIDS; /* control port protocol needs a lot of special handling */ @@ -3619,80 +3758,15 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata, } /* after this point (skb is modified) we cannot return false */ - - skb = skb_share_check(skb, GFP_ATOMIC); - if (unlikely(!skb)) + skb = ieee80211_tx_skb_fixup(skb, ieee80211_sdata_netdev_features(sdata)); + if (!skb) return true; - if ((hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) && - ieee80211_amsdu_aggregate(sdata, sta, fast_tx, skb)) - return true; - - /* will not be crypto-handled beyond what we do here, so use false - * as the may-encrypt argument for the resize to not account for - * more room than we already have in 'extra_head' - */ - if (unlikely(ieee80211_skb_resize(sdata, skb, - max_t(int, extra_head + hw_headroom - - skb_headroom(skb), 0), - ENCRYPT_NO))) { - kfree_skb(skb); - return true; + skb_list_walk_safe(skb, skb, next) { + skb_mark_not_on_list(skb); + __ieee80211_xmit_fast(sdata, sta, fast_tx, skb, tid, tid_tx); } - memcpy(ð, skb->data, ETH_HLEN - 2); - hdr = skb_push(skb, extra_head); - memcpy(skb->data, fast_tx->hdr, fast_tx->hdr_len); - memcpy(skb->data + fast_tx->da_offs, eth.h_dest, ETH_ALEN); - memcpy(skb->data + fast_tx->sa_offs, eth.h_source, ETH_ALEN); - - info = IEEE80211_SKB_CB(skb); - memset(info, 0, sizeof(*info)); - info->band = fast_tx->band; - info->control.vif = &sdata->vif; - info->flags = IEEE80211_TX_CTL_FIRST_FRAGMENT | - IEEE80211_TX_CTL_DONTFRAG | - (tid_tx ? IEEE80211_TX_CTL_AMPDU : 0); - info->control.flags = IEEE80211_TX_CTRL_FAST_XMIT | - u32_encode_bits(IEEE80211_LINK_UNSPECIFIED, - IEEE80211_TX_CTRL_MLO_LINK); - -#ifdef CONFIG_MAC80211_DEBUGFS - if (local->force_tx_status) - info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; -#endif - - if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) { - tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; - *ieee80211_get_qos_ctl(hdr) = tid; - } - - __skb_queue_head_init(&tx.skbs); - - tx.flags = IEEE80211_TX_UNICAST; - tx.local = local; - tx.sdata = sdata; - tx.sta = sta; - tx.key = fast_tx->key; - - if (ieee80211_queue_skb(local, sdata, sta, skb)) - return true; - - tx.skb = skb; - r = ieee80211_xmit_fast_finish(sdata, sta, fast_tx->pn_offs, - fast_tx->key, &tx); - tx.skb = NULL; - if (r == TX_DROP) { - kfree_skb(skb); - return true; - } - - if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) - sdata = container_of(sdata->bss, - struct ieee80211_sub_if_data, u.ap); - - __skb_queue_tail(&tx.skbs, skb); - ieee80211_tx_frags(local, &sdata->vif, sta, &tx.skbs, false); return true; } @@ -4192,31 +4266,14 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb, goto out; } - if (skb_is_gso(skb)) { - struct sk_buff *segs; - - segs = skb_gso_segment(skb, 0); - if (IS_ERR(segs)) { - goto out_free; - } else if (segs) { - consume_skb(skb); - skb = segs; - } - } else { - /* we cannot process non-linear frames on this path */ - if (skb_linearize(skb)) - goto out_free; - - /* the frame could be fragmented, software-encrypted, and other - * things so we cannot really handle checksum offload with it - - * fix it up in software before we handle anything else. - */ - if (skb->ip_summed == CHECKSUM_PARTIAL) { - skb_set_transport_header(skb, - skb_checksum_start_offset(skb)); - if (skb_checksum_help(skb)) - goto out_free; - } + /* the frame could be fragmented, software-encrypted, and other + * things so we cannot really handle checksum or GSO offload. + * fix it up in software before we handle anything else. + */ + skb = ieee80211_tx_skb_fixup(skb, 0); + if (!skb) { + len = 0; + goto out; } skb_list_walk_safe(skb, skb, next) { @@ -4434,9 +4491,11 @@ normal: return NETDEV_TX_OK; } -static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, struct sta_info *sta, - bool txpending) + + +static bool __ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, struct sta_info *sta, + bool txpending) { struct ieee80211_local *local = sdata->local; struct ieee80211_tx_control control = {}; @@ -4445,14 +4504,6 @@ static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata, unsigned long flags; int q = info->hw_queue; - if (sta) - sk_pacing_shift_update(skb->sk, local->hw.tx_sk_pacing_shift); - - ieee80211_tpt_led_trig_tx(local, skb->len); - - if (ieee80211_queue_skb(local, sdata, sta, skb)) - return true; - spin_lock_irqsave(&local->queue_stop_reason_lock, flags); if (local->queue_stop_reasons[q] || @@ -4479,6 +4530,26 @@ static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata, return true; } +static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, struct sta_info *sta, + bool txpending) +{ + struct ieee80211_local *local = sdata->local; + struct sk_buff *next; + bool ret = true; + + if (ieee80211_queue_skb(local, sdata, sta, skb)) + return true; + + skb_list_walk_safe(skb, skb, next) { + skb_mark_not_on_list(skb); + if (!__ieee80211_tx_8023(sdata, skb, sta, txpending)) + ret = false; + } + + return ret; +} + static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, struct net_device *dev, struct sta_info *sta, struct ieee80211_key *key, struct sk_buff *skb) @@ -4486,9 +4557,13 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, struct ieee80211_tx_info *info; struct ieee80211_local *local = sdata->local; struct tid_ampdu_tx *tid_tx; + struct sk_buff *seg, *next; + unsigned int skbs = 0, len = 0; + u16 queue; u8 tid; - skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb)); + queue = ieee80211_select_queue(sdata, sta, skb); + skb_set_queue_mapping(skb, queue); if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning)) && test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state)) @@ -4498,9 +4573,6 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, if (unlikely(!skb)) return; - info = IEEE80211_SKB_CB(skb); - memset(info, 0, sizeof(*info)); - ieee80211_aggr_check(sdata, sta, skb); tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; @@ -4514,22 +4586,20 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, return; } - info->flags |= IEEE80211_TX_CTL_AMPDU; if (tid_tx->timeout) tid_tx->last_tx = jiffies; } - if (unlikely(skb->sk && - skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) - info->ack_frame_id = ieee80211_store_ack_skb(local, skb, - &info->flags, NULL); + skb = ieee80211_tx_skb_fixup(skb, ieee80211_sdata_netdev_features(sdata)); + if (!skb) + return; - info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)]; + info = IEEE80211_SKB_CB(skb); + memset(info, 0, sizeof(*info)); + if (tid_tx) + info->flags |= IEEE80211_TX_CTL_AMPDU; - dev_sw_netstats_tx_add(dev, 1, skb->len); - - sta->deflink.tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len; - sta->deflink.tx_stats.packets[skb_get_queue_mapping(skb)]++; + info->hw_queue = sdata->vif.hw_queue[queue]; if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) sdata = container_of(sdata->bss, @@ -4541,6 +4611,24 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, if (key) info->control.hw_key = &key->conf; + skb_list_walk_safe(skb, seg, next) { + skbs++; + len += seg->len; + if (seg != skb) + memcpy(IEEE80211_SKB_CB(seg), info, sizeof(*info)); + } + + if (unlikely(skb->sk && + skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) + info->ack_frame_id = ieee80211_store_ack_skb(local, skb, + &info->flags, NULL); + + dev_sw_netstats_tx_add(dev, skbs, len); + sta->deflink.tx_stats.packets[queue] += skbs; + sta->deflink.tx_stats.bytes[queue] += len; + + ieee80211_tpt_led_trig_tx(local, len); + ieee80211_tx_8023(sdata, skb, sta, false); return; @@ -4582,6 +4670,7 @@ netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb, key->conf.cipher == WLAN_CIPHER_SUITE_TKIP)) goto skip_offload; + sk_pacing_shift_update(skb->sk, sdata->local->hw.tx_sk_pacing_shift); ieee80211_8023_xmit(sdata, dev, sta, key, skb); goto out; From 94b9b9de05b62ac54d8766caa9865fb4d82cc47e Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 1 Dec 2022 14:57:30 +0100 Subject: [PATCH 065/144] wifi: mac80211: fix and simplify unencrypted drop check for mesh ieee80211_drop_unencrypted is called from ieee80211_rx_h_mesh_fwding and ieee80211_frame_allowed. Since ieee80211_rx_h_mesh_fwding can forward packets for other mesh nodes and is called earlier, it needs to check the decryptions status and if the packet is using the control protocol on its own, instead of deferring to the later call from ieee80211_frame_allowed. Because of that, ieee80211_drop_unencrypted has a mesh specific check that skips over the mesh header in order to check the payload protocol. This code is invalid when called from ieee80211_frame_allowed, since that happens after the 802.11->802.3 conversion. Fix this by moving the mesh specific check directly into ieee80211_rx_h_mesh_fwding. Signed-off-by: Felix Fietkau Link: https://lore.kernel.org/r/20221201135730.19723-1-nbd@nbd.name Signed-off-by: Johannes Berg --- net/mac80211/rx.c | 38 ++++++++++---------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index c28c6fbf786e..7e3ab6e1b28f 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2403,7 +2403,6 @@ static int ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx) static int ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc) { - struct ieee80211_hdr *hdr = (void *)rx->skb->data; struct sk_buff *skb = rx->skb; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); @@ -2414,31 +2413,6 @@ static int ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc) if (status->flag & RX_FLAG_DECRYPTED) return 0; - /* check mesh EAPOL frames first */ - if (unlikely(rx->sta && ieee80211_vif_is_mesh(&rx->sdata->vif) && - ieee80211_is_data(fc))) { - struct ieee80211s_hdr *mesh_hdr; - u16 hdr_len = ieee80211_hdrlen(fc); - u16 ethertype_offset; - __be16 ethertype; - - if (!ether_addr_equal(hdr->addr1, rx->sdata->vif.addr)) - goto drop_check; - - /* make sure fixed part of mesh header is there, also checks skb len */ - if (!pskb_may_pull(rx->skb, hdr_len + 6)) - goto drop_check; - - mesh_hdr = (struct ieee80211s_hdr *)(skb->data + hdr_len); - ethertype_offset = hdr_len + ieee80211_get_mesh_hdrlen(mesh_hdr) + - sizeof(rfc1042_header); - - if (skb_copy_bits(rx->skb, ethertype_offset, ðertype, 2) == 0 && - ethertype == rx->sdata->control_port_protocol) - return 0; - } - -drop_check: /* Drop unencrypted frames if key is set. */ if (unlikely(!ieee80211_has_protected(fc) && !ieee80211_is_any_nullfunc(fc) && @@ -2892,8 +2866,16 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) hdr = (struct ieee80211_hdr *) skb->data; mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen); - if (ieee80211_drop_unencrypted(rx, hdr->frame_control)) - return RX_DROP_MONITOR; + if (ieee80211_drop_unencrypted(rx, hdr->frame_control)) { + int offset = hdrlen + ieee80211_get_mesh_hdrlen(mesh_hdr) + + sizeof(rfc1042_header); + __be16 ethertype; + + if (!ether_addr_equal(hdr->addr1, rx->sdata->vif.addr) || + skb_copy_bits(rx->skb, offset, ðertype, 2) != 0 || + ethertype != rx->sdata->control_port_protocol) + return RX_DROP_MONITOR; + } /* frame is in RMC, don't forward */ if (ieee80211_is_data(hdr->frame_control) && From b376d96362d815331dff0c498a338ebe4c8c5bde Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 28 Sep 2022 10:49:11 +0200 Subject: [PATCH 066/144] wifi: mt76: move mt76_rate_power from core to mt76x02 driver code Its layout and code is mt76x02 specific Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/debugfs.c | 19 ------------------- drivers/net/wireless/mediatek/mt76/mt76.h | 15 --------------- .../wireless/mediatek/mt76/mt76x0/eeprom.c | 4 ++-- .../wireless/mediatek/mt76/mt76x0/eeprom.h | 2 +- .../net/wireless/mediatek/mt76/mt76x0/init.c | 2 +- .../net/wireless/mediatek/mt76/mt76x0/phy.c | 10 +++++----- drivers/net/wireless/mediatek/mt76/mt76x02.h | 15 +++++++++++++++ .../wireless/mediatek/mt76/mt76x02_debugfs.c | 19 +++++++++++++++++++ .../net/wireless/mediatek/mt76/mt76x02_phy.c | 8 ++++---- .../net/wireless/mediatek/mt76/mt76x02_phy.h | 6 +++--- .../net/wireless/mediatek/mt76/mt76x02_txrx.c | 12 ++++++------ .../wireless/mediatek/mt76/mt76x2/eeprom.c | 2 +- .../wireless/mediatek/mt76/mt76x2/eeprom.h | 2 +- .../net/wireless/mediatek/mt76/mt76x2/init.c | 2 +- .../net/wireless/mediatek/mt76/mt76x2/phy.c | 6 +++--- 15 files changed, 62 insertions(+), 62 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/debugfs.c b/drivers/net/wireless/mediatek/mt76/debugfs.c index 47e9911ee9fe..11b0b3d62f29 100644 --- a/drivers/net/wireless/mediatek/mt76/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/debugfs.c @@ -100,23 +100,6 @@ void mt76_seq_puts_array(struct seq_file *file, const char *str, } EXPORT_SYMBOL_GPL(mt76_seq_puts_array); -static int mt76_read_rate_txpower(struct seq_file *s, void *data) -{ - struct mt76_dev *dev = dev_get_drvdata(s->private); - - mt76_seq_puts_array(s, "CCK", dev->rate_power.cck, - ARRAY_SIZE(dev->rate_power.cck)); - mt76_seq_puts_array(s, "OFDM", dev->rate_power.ofdm, - ARRAY_SIZE(dev->rate_power.ofdm)); - mt76_seq_puts_array(s, "STBC", dev->rate_power.stbc, - ARRAY_SIZE(dev->rate_power.stbc)); - mt76_seq_puts_array(s, "HT", dev->rate_power.ht, - ARRAY_SIZE(dev->rate_power.ht)); - mt76_seq_puts_array(s, "VHT", dev->rate_power.vht, - ARRAY_SIZE(dev->rate_power.vht)); - return 0; -} - struct dentry * mt76_register_debugfs_fops(struct mt76_phy *phy, const struct file_operations *ops) @@ -137,8 +120,6 @@ mt76_register_debugfs_fops(struct mt76_phy *phy, debugfs_create_blob("eeprom", 0400, dir, &dev->eeprom); if (dev->otp.data) debugfs_create_blob("otp", 0400, dir, &dev->otp); - debugfs_create_devm_seqfile(dev->dev, "rate_txpower", dir, - mt76_read_rate_txpower); debugfs_create_devm_seqfile(dev->dev, "rx-queues", dir, mt76_rx_queues_read); diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index 87db9498dea4..26febc0c261a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -470,19 +470,6 @@ struct mt76_sband { struct mt76_channel_state *chan; }; -struct mt76_rate_power { - union { - struct { - s8 cck[4]; - s8 ofdm[8]; - s8 stbc[10]; - s8 ht[16]; - s8 vht[10]; - }; - s8 all[48]; - }; -}; - /* addr req mask */ #define MT_VEND_TYPE_EEPROM BIT(31) #define MT_VEND_TYPE_CFG BIT(30) @@ -802,8 +789,6 @@ struct mt76_dev { struct debugfs_blob_wrapper eeprom; struct debugfs_blob_wrapper otp; - struct mt76_rate_power rate_power; - char alpha2[3]; enum nl80211_dfs_regions region; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c index da2ca2563ac9..103a6d3a1e13 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c @@ -151,7 +151,7 @@ static s8 mt76x0_get_delta(struct mt76x02_dev *dev) void mt76x0_get_tx_power_per_rate(struct mt76x02_dev *dev, struct ieee80211_channel *chan, - struct mt76_rate_power *t) + struct mt76x02_rate_power *t) { bool is_2ghz = chan->band == NL80211_BAND_2GHZ; u16 val, addr; @@ -235,7 +235,7 @@ void mt76x0_get_power_info(struct mt76x02_dev *dev, data = mt76x02_eeprom_get(dev, MT_EE_5G_TARGET_POWER); else data = mt76x02_eeprom_get(dev, MT_EE_2G_TARGET_POWER); - target_power = (data & 0xff) - dev->mt76.rate_power.ofdm[7]; + target_power = (data & 0xff) - dev->rate_power.ofdm[7]; *tp = target_power + mt76x0_get_delta(dev); return; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.h index 15540ce8db87..08f1b10bf3ba 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.h @@ -19,7 +19,7 @@ int mt76x0_eeprom_init(struct mt76x02_dev *dev); void mt76x0_read_rx_gain(struct mt76x02_dev *dev); void mt76x0_get_tx_power_per_rate(struct mt76x02_dev *dev, struct ieee80211_channel *chan, - struct mt76_rate_power *t); + struct mt76x02_rate_power *t); void mt76x0_get_power_info(struct mt76x02_dev *dev, struct ieee80211_channel *chan, s8 *tp); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/init.c b/drivers/net/wireless/mediatek/mt76/mt76x0/init.c index 66d47c70111a..6257460f8de5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/init.c @@ -217,7 +217,7 @@ mt76x0_init_txpower(struct mt76x02_dev *dev, struct ieee80211_supported_band *sband) { struct ieee80211_channel *chan; - struct mt76_rate_power t; + struct mt76x02_rate_power t; s8 tp; int i; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c b/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c index e91c314cdfac..8a89fe49db36 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c @@ -598,7 +598,7 @@ mt76x0_phy_get_target_power(struct mt76x02_dev *dev, u8 tx_mode, if (tx_rate > 3) return -EINVAL; - *target_power = cur_power + dev->mt76.rate_power.cck[tx_rate]; + *target_power = cur_power + dev->rate_power.cck[tx_rate]; *target_pa_power = mt76x0_phy_get_rf_pa_mode(dev, 0, tx_rate); break; case 1: { @@ -635,7 +635,7 @@ mt76x0_phy_get_target_power(struct mt76x02_dev *dev, u8 tx_mode, return -EINVAL; } - *target_power = cur_power + dev->mt76.rate_power.ofdm[index]; + *target_power = cur_power + dev->rate_power.ofdm[index]; *target_pa_power = mt76x0_phy_get_rf_pa_mode(dev, 0, index + 4); break; } @@ -645,7 +645,7 @@ mt76x0_phy_get_target_power(struct mt76x02_dev *dev, u8 tx_mode, if (tx_rate > 9) return -EINVAL; - *target_power = cur_power + dev->mt76.rate_power.vht[tx_rate]; + *target_power = cur_power + dev->rate_power.vht[tx_rate]; *target_pa_power = mt76x0_phy_get_rf_pa_mode(dev, 1, tx_rate); break; default: @@ -654,7 +654,7 @@ mt76x0_phy_get_target_power(struct mt76x02_dev *dev, u8 tx_mode, if (tx_rate > 9) return -EINVAL; - *target_power = cur_power + dev->mt76.rate_power.ht[tx_rate]; + *target_power = cur_power + dev->rate_power.ht[tx_rate]; *target_pa_power = mt76x0_phy_get_rf_pa_mode(dev, 1, tx_rate); break; } @@ -841,7 +841,7 @@ static void mt76x0_phy_tssi_calibrate(struct mt76x02_dev *dev) void mt76x0_phy_set_txpower(struct mt76x02_dev *dev) { - struct mt76_rate_power *t = &dev->mt76.rate_power; + struct mt76x02_rate_power *t = &dev->rate_power; s8 info; mt76x0_get_tx_power_per_rate(dev, dev->mphy.chandef.chan, t); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02.h b/drivers/net/wireless/mediatek/mt76/mt76x02.h index 50eaeff11af3..ae6ba35e863d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x02.h @@ -72,6 +72,19 @@ struct mt76x02_beacon_ops { #define mt76x02_pre_tbtt_enable(dev, enable) \ (dev)->beacon_ops->pre_tbtt_enable(dev, enable) +struct mt76x02_rate_power { + union { + struct { + s8 cck[4]; + s8 ofdm[8]; + s8 stbc[10]; + s8 ht[16]; + s8 vht[10]; + }; + s8 all[48]; + }; +}; + struct mt76x02_dev { union { /* must be first */ struct mt76_dev mt76; @@ -107,6 +120,8 @@ struct mt76x02_dev { u8 beacon_hang_check; u8 mcu_timeout; + struct mt76x02_rate_power rate_power; + struct mt76x02_calibration cal; int txpower_conf; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c b/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c index c4fe1c436aaa..066801620eb2 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c @@ -114,6 +114,23 @@ mt76_edcca_get(void *data, u64 *val) DEFINE_DEBUGFS_ATTRIBUTE(fops_edcca, mt76_edcca_get, mt76_edcca_set, "%lld\n"); +static int mt76x02_read_rate_txpower(struct seq_file *s, void *data) +{ + struct mt76x02_dev *dev = dev_get_drvdata(s->private); + + mt76_seq_puts_array(s, "CCK", dev->rate_power.cck, + ARRAY_SIZE(dev->rate_power.cck)); + mt76_seq_puts_array(s, "OFDM", dev->rate_power.ofdm, + ARRAY_SIZE(dev->rate_power.ofdm)); + mt76_seq_puts_array(s, "STBC", dev->rate_power.stbc, + ARRAY_SIZE(dev->rate_power.stbc)); + mt76_seq_puts_array(s, "HT", dev->rate_power.ht, + ARRAY_SIZE(dev->rate_power.ht)); + mt76_seq_puts_array(s, "VHT", dev->rate_power.vht, + ARRAY_SIZE(dev->rate_power.vht)); + return 0; +} + void mt76x02_init_debugfs(struct mt76x02_dev *dev) { struct dentry *dir; @@ -133,6 +150,8 @@ void mt76x02_init_debugfs(struct mt76x02_dev *dev) debugfs_create_devm_seqfile(dev->mt76.dev, "txpower", dir, read_txpower); + debugfs_create_devm_seqfile(dev->mt76.dev, "rate_txpower", dir, + mt76x02_read_rate_txpower); debugfs_create_devm_seqfile(dev->mt76.dev, "agc", dir, read_agc); debugfs_create_u32("tx_hang_reset", 0400, dir, &dev->tx_hang_reset); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c b/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c index 2e53b0c1afdd..722dc2a7dea6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c @@ -59,7 +59,7 @@ mt76x02_tx_power_mask(u8 v1, u8 v2, u8 v3, u8 v4) return val; } -int mt76x02_get_max_rate_power(struct mt76_rate_power *r) +int mt76x02_get_max_rate_power(struct mt76x02_rate_power *r) { s8 ret = 0; int i; @@ -71,7 +71,7 @@ int mt76x02_get_max_rate_power(struct mt76_rate_power *r) } EXPORT_SYMBOL_GPL(mt76x02_get_max_rate_power); -void mt76x02_limit_rate_power(struct mt76_rate_power *r, int limit) +void mt76x02_limit_rate_power(struct mt76x02_rate_power *r, int limit) { int i; @@ -81,7 +81,7 @@ void mt76x02_limit_rate_power(struct mt76_rate_power *r, int limit) } EXPORT_SYMBOL_GPL(mt76x02_limit_rate_power); -void mt76x02_add_rate_power_offset(struct mt76_rate_power *r, int offset) +void mt76x02_add_rate_power_offset(struct mt76x02_rate_power *r, int offset) { int i; @@ -92,7 +92,7 @@ EXPORT_SYMBOL_GPL(mt76x02_add_rate_power_offset); void mt76x02_phy_set_txpower(struct mt76x02_dev *dev, int txp_0, int txp_1) { - struct mt76_rate_power *t = &dev->mt76.rate_power; + struct mt76x02_rate_power *t = &dev->rate_power; mt76_rmw_field(dev, MT_TX_ALC_CFG_0, MT_TX_ALC_CFG_0_CH_INIT_0, txp_0); mt76_rmw_field(dev, MT_TX_ALC_CFG_0, MT_TX_ALC_CFG_0_CH_INIT_1, txp_1); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_phy.h b/drivers/net/wireless/mediatek/mt76/mt76x02_phy.h index 1def25bf735a..84d8a6138b3e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_phy.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_phy.h @@ -34,10 +34,10 @@ mt76x02_get_low_rssi_gain_thresh(struct mt76x02_dev *dev) } } -void mt76x02_add_rate_power_offset(struct mt76_rate_power *r, int offset); +void mt76x02_add_rate_power_offset(struct mt76x02_rate_power *r, int offset); void mt76x02_phy_set_txpower(struct mt76x02_dev *dev, int txp_0, int txp_2); -void mt76x02_limit_rate_power(struct mt76_rate_power *r, int limit); -int mt76x02_get_max_rate_power(struct mt76_rate_power *r); +void mt76x02_limit_rate_power(struct mt76x02_rate_power *r, int limit); +int mt76x02_get_max_rate_power(struct mt76x02_rate_power *r); void mt76x02_phy_set_rxpath(struct mt76x02_dev *dev); void mt76x02_phy_set_txdac(struct mt76x02_dev *dev); void mt76x02_phy_set_bw(struct mt76x02_dev *dev, int width, u8 ctrl); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c b/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c index 96fdf423a348..1e1ef2499977 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c @@ -62,23 +62,23 @@ s8 mt76x02_tx_get_max_txpwr_adj(struct mt76x02_dev *dev, u8 mcs = ieee80211_rate_get_vht_mcs(rate); if (mcs == 8 || mcs == 9) { - max_txpwr = dev->mt76.rate_power.vht[8]; + max_txpwr = dev->rate_power.vht[8]; } else { u8 nss, idx; nss = ieee80211_rate_get_vht_nss(rate); idx = ((nss - 1) << 3) + mcs; - max_txpwr = dev->mt76.rate_power.ht[idx & 0xf]; + max_txpwr = dev->rate_power.ht[idx & 0xf]; } } else if (rate->flags & IEEE80211_TX_RC_MCS) { - max_txpwr = dev->mt76.rate_power.ht[rate->idx & 0xf]; + max_txpwr = dev->rate_power.ht[rate->idx & 0xf]; } else { enum nl80211_band band = dev->mphy.chandef.chan->band; if (band == NL80211_BAND_2GHZ) { const struct ieee80211_rate *r; struct wiphy *wiphy = dev->mt76.hw->wiphy; - struct mt76_rate_power *rp = &dev->mt76.rate_power; + struct mt76x02_rate_power *rp = &dev->rate_power; r = &wiphy->bands[band]->bitrates[rate->idx]; if (r->flags & IEEE80211_RATE_SHORT_PREAMBLE) @@ -86,7 +86,7 @@ s8 mt76x02_tx_get_max_txpwr_adj(struct mt76x02_dev *dev, else max_txpwr = rp->ofdm[r->hw_value & 0x7]; } else { - max_txpwr = dev->mt76.rate_power.ofdm[rate->idx & 0x7]; + max_txpwr = dev->rate_power.ofdm[rate->idx & 0x7]; } } @@ -112,7 +112,7 @@ void mt76x02_tx_set_txpwr_auto(struct mt76x02_dev *dev, s8 txpwr) s8 txpwr_adj; txpwr_adj = mt76x02_tx_get_txpwr_adj(dev, txpwr, - dev->mt76.rate_power.ofdm[4]); + dev->rate_power.ofdm[4]); mt76_rmw_field(dev, MT_PROT_AUTO_TX_CFG, MT_PROT_AUTO_TX_CFG_PROT_PADJ, txpwr_adj); mt76_rmw_field(dev, MT_PROT_AUTO_TX_CFG, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c index c57e05a5c65e..d1d7e06c3fa5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c @@ -280,7 +280,7 @@ void mt76x2_read_rx_gain(struct mt76x02_dev *dev) } EXPORT_SYMBOL_GPL(mt76x2_read_rx_gain); -void mt76x2_get_rate_power(struct mt76x02_dev *dev, struct mt76_rate_power *t, +void mt76x2_get_rate_power(struct mt76x02_dev *dev, struct mt76x02_rate_power *t, struct ieee80211_channel *chan) { bool is_5ghz; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.h index 3755632e6494..43430ef98b11 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.h @@ -40,7 +40,7 @@ struct mt76x2_temp_comp { unsigned int low_slope; /* J / dB */ }; -void mt76x2_get_rate_power(struct mt76x02_dev *dev, struct mt76_rate_power *t, +void mt76x2_get_rate_power(struct mt76x02_dev *dev, struct mt76x02_rate_power *t, struct ieee80211_channel *chan); void mt76x2_get_power_info(struct mt76x02_dev *dev, struct mt76x2_tx_power_info *t, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/init.c b/drivers/net/wireless/mediatek/mt76/mt76x2/init.c index 7b01a06d7f8d..19c139290adb 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/init.c @@ -182,7 +182,7 @@ void mt76x2_init_txpower(struct mt76x02_dev *dev, { struct ieee80211_channel *chan; struct mt76x2_tx_power_info txp; - struct mt76_rate_power t = {}; + struct mt76x02_rate_power t = {}; int i; for (i = 0; i < sband->n_channels; i++) { diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/phy.c b/drivers/net/wireless/mediatek/mt76/mt76x2/phy.c index ed2dcb05d614..f84517d932dc 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/phy.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/phy.c @@ -116,7 +116,7 @@ void mt76x2_phy_set_txpower_regs(struct mt76x02_dev *dev, EXPORT_SYMBOL_GPL(mt76x2_phy_set_txpower_regs); static int -mt76x2_get_min_rate_power(struct mt76_rate_power *r) +mt76x2_get_min_rate_power(struct mt76x02_rate_power *r) { int i; s8 ret = 0; @@ -140,7 +140,7 @@ void mt76x2_phy_set_txpower(struct mt76x02_dev *dev) struct ieee80211_channel *chan = dev->mphy.chandef.chan; struct mt76x2_tx_power_info txp; int txp_0, txp_1, delta = 0; - struct mt76_rate_power t = {}; + struct mt76x02_rate_power t = {}; int base_power, gain; mt76x2_get_power_info(dev, &txp, chan); @@ -175,7 +175,7 @@ void mt76x2_phy_set_txpower(struct mt76x02_dev *dev) dev->target_power = txp.target_power; dev->target_power_delta[0] = txp_0 - txp.chain[0].target_power; dev->target_power_delta[1] = txp_1 - txp.chain[0].target_power; - dev->mt76.rate_power = t; + dev->rate_power = t; mt76x02_phy_set_txpower(dev, txp_0, txp_1); } From ba45841ca5eb29245f9c9f452a39586d9d68bc12 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 28 Sep 2022 11:29:45 +0200 Subject: [PATCH 067/144] wifi: mt76: mt76x02: simplify struct mt76x02_rate_power - remove stbc, because mt76x0 doesn't support it and mt76x2 uses the same values as HT/VHT - reduce vht array to 2 elements, because VHT MCS0-7 are the same as HT Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt76x0/eeprom.c | 24 +++++-------------- drivers/net/wireless/mediatek/mt76/mt76x02.h | 5 ++-- .../wireless/mediatek/mt76/mt76x02_debugfs.c | 2 -- .../wireless/mediatek/mt76/mt76x02_eeprom.h | 2 -- .../net/wireless/mediatek/mt76/mt76x02_phy.c | 14 +++++------ .../net/wireless/mediatek/mt76/mt76x02_txrx.c | 2 +- .../wireless/mediatek/mt76/mt76x2/eeprom.c | 14 +---------- 7 files changed, 17 insertions(+), 46 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c index 103a6d3a1e13..c3a392a1a659 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c @@ -179,31 +179,19 @@ void mt76x0_get_tx_power_per_rate(struct mt76x02_dev *dev, /* ht-vht mcs 1ss 0, 1, 2, 3 */ addr = is_2ghz ? MT_EE_TX_POWER_BYRATE_BASE + 6 : 0x124; val = mt76x02_eeprom_get(dev, addr); - t->ht[0] = t->ht[1] = t->vht[0] = t->vht[1] = s6_to_s8(val); - t->ht[2] = t->ht[3] = t->vht[2] = t->vht[3] = s6_to_s8(val >> 8); + t->ht[0] = t->ht[1] = s6_to_s8(val); + t->ht[2] = t->ht[3] = s6_to_s8(val >> 8); /* ht-vht mcs 1ss 4, 5, 6 */ addr = is_2ghz ? MT_EE_TX_POWER_BYRATE_BASE + 8 : 0x126; val = mt76x02_eeprom_get(dev, addr); - t->ht[4] = t->ht[5] = t->vht[4] = t->vht[5] = s6_to_s8(val); - t->ht[6] = t->ht[7] = t->vht[6] = t->vht[7] = s6_to_s8(val >> 8); - - /* ht-vht mcs 1ss 0, 1, 2, 3 stbc */ - addr = is_2ghz ? MT_EE_TX_POWER_BYRATE_BASE + 14 : 0xec; - val = mt76x02_eeprom_get(dev, addr); - t->stbc[0] = t->stbc[1] = s6_to_s8(val); - t->stbc[2] = t->stbc[3] = s6_to_s8(val >> 8); - - /* ht-vht mcs 1ss 4, 5, 6 stbc */ - addr = is_2ghz ? MT_EE_TX_POWER_BYRATE_BASE + 16 : 0xee; - val = mt76x02_eeprom_get(dev, addr); - t->stbc[4] = t->stbc[5] = s6_to_s8(val); - t->stbc[6] = t->stbc[7] = s6_to_s8(val >> 8); + t->ht[4] = t->ht[5] = s6_to_s8(val); + t->ht[6] = t->ht[7] = s6_to_s8(val >> 8); /* vht mcs 8, 9 5GHz */ val = mt76x02_eeprom_get(dev, 0x12c); - t->vht[8] = s6_to_s8(val); - t->vht[9] = s6_to_s8(val >> 8); + t->vht[0] = s6_to_s8(val); + t->vht[1] = s6_to_s8(val >> 8); delta = mt76x0_tssi_enabled(dev) ? 0 : mt76x0_get_delta(dev); mt76x02_add_rate_power_offset(t, delta); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02.h b/drivers/net/wireless/mediatek/mt76/mt76x02.h index ae6ba35e863d..a19176bb2433 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x02.h @@ -77,11 +77,10 @@ struct mt76x02_rate_power { struct { s8 cck[4]; s8 ofdm[8]; - s8 stbc[10]; s8 ht[16]; - s8 vht[10]; + s8 vht[2]; }; - s8 all[48]; + s8 all[30]; }; }; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c b/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c index 066801620eb2..981ac8e84807 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c @@ -122,8 +122,6 @@ static int mt76x02_read_rate_txpower(struct seq_file *s, void *data) ARRAY_SIZE(dev->rate_power.cck)); mt76_seq_puts_array(s, "OFDM", dev->rate_power.ofdm, ARRAY_SIZE(dev->rate_power.ofdm)); - mt76_seq_puts_array(s, "STBC", dev->rate_power.stbc, - ARRAY_SIZE(dev->rate_power.stbc)); mt76_seq_puts_array(s, "HT", dev->rate_power.ht, ARRAY_SIZE(dev->rate_power.ht)); mt76_seq_puts_array(s, "VHT", dev->rate_power.vht, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.h b/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.h index 99941a4700f3..13fa70853b0d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.h @@ -62,8 +62,6 @@ enum mt76x02_eeprom_field { MT_EE_TX_POWER_HT_MCS4 = 0x0a8, MT_EE_TX_POWER_HT_MCS8 = 0x0aa, MT_EE_TX_POWER_HT_MCS12 = 0x0ac, - MT_EE_TX_POWER_VHT_MCS0 = 0x0ba, - MT_EE_TX_POWER_VHT_MCS4 = 0x0bc, MT_EE_TX_POWER_VHT_MCS8 = 0x0be, MT_EE_2G_TARGET_POWER = 0x0d0, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c b/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c index 722dc2a7dea6..cbe7e6f0c29a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c @@ -107,17 +107,17 @@ void mt76x02_phy_set_txpower(struct mt76x02_dev *dev, int txp_0, int txp_1) mt76x02_tx_power_mask(t->ht[4], t->ht[6], t->ht[8], t->ht[10])); mt76_wr(dev, MT_TX_PWR_CFG_3, - mt76x02_tx_power_mask(t->ht[12], t->ht[14], t->stbc[0], - t->stbc[2])); + mt76x02_tx_power_mask(t->ht[12], t->ht[14], t->ht[0], + t->ht[2])); mt76_wr(dev, MT_TX_PWR_CFG_4, - mt76x02_tx_power_mask(t->stbc[4], t->stbc[6], 0, 0)); + mt76x02_tx_power_mask(t->ht[4], t->ht[6], 0, 0)); mt76_wr(dev, MT_TX_PWR_CFG_7, - mt76x02_tx_power_mask(t->ofdm[7], t->vht[8], t->ht[7], - t->vht[9])); + mt76x02_tx_power_mask(t->ofdm[7], t->vht[0], t->ht[7], + t->vht[1])); mt76_wr(dev, MT_TX_PWR_CFG_8, - mt76x02_tx_power_mask(t->ht[14], 0, t->vht[8], t->vht[9])); + mt76x02_tx_power_mask(t->ht[14], 0, t->vht[0], t->vht[1])); mt76_wr(dev, MT_TX_PWR_CFG_9, - mt76x02_tx_power_mask(t->ht[7], 0, t->stbc[8], t->stbc[9])); + mt76x02_tx_power_mask(t->ht[7], 0, t->vht[0], t->vht[1])); } EXPORT_SYMBOL_GPL(mt76x02_phy_set_txpower); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c b/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c index 1e1ef2499977..3a313075a9e3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c @@ -62,7 +62,7 @@ s8 mt76x02_tx_get_max_txpwr_adj(struct mt76x02_dev *dev, u8 mcs = ieee80211_rate_get_vht_mcs(rate); if (mcs == 8 || mcs == 9) { - max_txpwr = dev->rate_power.vht[8]; + max_txpwr = dev->rate_power.vht[0]; } else { u8 nss, idx; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c index d1d7e06c3fa5..d5809408d1d3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c @@ -324,22 +324,10 @@ void mt76x2_get_rate_power(struct mt76x02_dev *dev, struct mt76x02_rate_power *t t->ht[12] = t->ht[13] = mt76x02_rate_power_val(val); t->ht[14] = t->ht[15] = mt76x02_rate_power_val(val >> 8); - val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_VHT_MCS0); - t->vht[0] = t->vht[1] = mt76x02_rate_power_val(val); - t->vht[2] = t->vht[3] = mt76x02_rate_power_val(val >> 8); - - val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_VHT_MCS4); - t->vht[4] = t->vht[5] = mt76x02_rate_power_val(val); - t->vht[6] = t->vht[7] = mt76x02_rate_power_val(val >> 8); - val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_VHT_MCS8); if (!is_5ghz) val >>= 8; - t->vht[8] = t->vht[9] = mt76x02_rate_power_val(val >> 8); - - memcpy(t->stbc, t->ht, sizeof(t->stbc[0]) * 8); - t->stbc[8] = t->vht[8]; - t->stbc[9] = t->vht[9]; + t->vht[0] = t->vht[1] = mt76x02_rate_power_val(val >> 8); } EXPORT_SYMBOL_GPL(mt76x2_get_rate_power); From c256ba6b1909f28b517274282b6845567e974143 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Sat, 17 Sep 2022 06:46:45 +0800 Subject: [PATCH 068/144] wifi: mt76: mt7921: fix antenna signal are way off in monitor mode Group 3 in RxD is disabled in monitor mode. We should use the group 5 in RxD instead to fix antenna signal way off issue, e.g we would see the incorrect antenna signal value in wireshark. On the other hand, Group 5 wouldn't be used in STA or AP mode, so the patch shouldn't cause any harm to those modes. Fixes: cbaa0a404f8d ("mt76: mt7921: fix up the monitor mode") Reported-by: Adrian Granados Co-developed-by: Deren Wu Signed-off-by: Deren Wu Signed-off-by: Sean Wang Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7921/mac.c | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c index 650ab97ae052..6860468ed191 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c @@ -396,6 +396,27 @@ mt7921_mac_fill_rx(struct mt7921_dev *dev, struct sk_buff *skb) if (v0 & MT_PRXV_HT_AD_CODE) status->enc_flags |= RX_ENC_FLAG_LDPC; + ret = mt76_connac2_mac_fill_rx_rate(&dev->mt76, status, sband, + rxv, &mode); + if (ret < 0) + return ret; + + if (rxd1 & MT_RXD1_NORMAL_GROUP_5) { + rxd += 6; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + + rxv = rxd; + /* Monitor mode would use RCPI described in GROUP 5 + * instead. + */ + v1 = le32_to_cpu(rxv[0]); + + rxd += 12; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + status->chains = mphy->antenna_mask; status->chain_signal[0] = to_rssi(MT_PRXV_RCPI0, v1); status->chain_signal[1] = to_rssi(MT_PRXV_RCPI1, v1); @@ -410,17 +431,6 @@ mt7921_mac_fill_rx(struct mt7921_dev *dev, struct sk_buff *skb) status->signal = max(status->signal, status->chain_signal[i]); } - - ret = mt76_connac2_mac_fill_rx_rate(&dev->mt76, status, sband, - rxv, &mode); - if (ret < 0) - return ret; - - if (rxd1 & MT_RXD1_NORMAL_GROUP_5) { - rxd += 18; - if ((u8 *)rxd - skb->data >= skb->len) - return -EINVAL; - } } amsdu_info = FIELD_GET(MT_RXD4_NORMAL_PAYLOAD_FORMAT, rxd4); From 03c2dd4d01a20edbadee408a622dee5e8a27f1e6 Mon Sep 17 00:00:00 2001 From: Gaosheng Cui Date: Thu, 22 Sep 2022 15:47:11 +0800 Subject: [PATCH 069/144] wifi: mt76: Remove unused inline function mt76_wcid_mask_test() All uses of mt76_wcid_mask_test() have been removed since commit 8950a62f19c9 ("mt76: get rid of mt76_wcid_hw routine"), so remove it. Signed-off-by: Gaosheng Cui Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/util.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/util.h b/drivers/net/wireless/mediatek/mt76/util.h index 49c52d781f40..260965dde94c 100644 --- a/drivers/net/wireless/mediatek/mt76/util.h +++ b/drivers/net/wireless/mediatek/mt76/util.h @@ -29,12 +29,6 @@ enum { int mt76_wcid_alloc(u32 *mask, int size); -static inline bool -mt76_wcid_mask_test(u32 *mask, int idx) -{ - return mask[idx / 32] & BIT(idx % 32); -} - static inline void mt76_wcid_mask_set(u32 *mask, int idx) { From 2b685ba7d4247d7707af795719f4f33c9019feb6 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Thu, 29 Sep 2022 08:15:25 -0700 Subject: [PATCH 070/144] wifi: mt76: mt7915: fix bounds checking for tx-free-done command According to the tx-free-done documentation, the DW4 can be repeated, so have to be more careful about how we test for walking off the end of the array. Signed-off-by: Ben Greear Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index a4bcc617c1a3..89a3810ee53f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -905,17 +905,19 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, void *data, int len) total = le16_get_bits(free->ctrl, MT_TX_FREE_MSDU_CNT); v3 = (FIELD_GET(MT_TX_FREE_VER, txd) == 0x4); - if (WARN_ON_ONCE((void *)&tx_info[total >> v3] > end)) - return; for (cur_info = tx_info; count < total; cur_info++) { - u32 msdu, info = le32_to_cpu(*cur_info); + u32 msdu, info; u8 i; + if (WARN_ON_ONCE((void *)cur_info >= end)) + return; + /* * 1'b1: new wcid pair. * 1'b0: msdu_id with the same 'wcid pair' as above. */ + info = le32_to_cpu(*cur_info); if (info & MT_TX_FREE_PAIR) { struct mt7915_sta *msta; struct mt76_wcid *wcid; From 7624ffcd4edf2668e6db90f39c7343967a94c88f Mon Sep 17 00:00:00 2001 From: Evelyn Tsai Date: Fri, 30 Sep 2022 22:14:41 +0800 Subject: [PATCH 071/144] wifi: mt76: mt7915: reserve 8 bits for the index of rf registers The value of regidx of rf registers is combined with WF selection and offset. Extend the WF selection field from 4 to 8 bits since the adie index should also be specified. Signed-off-by: Evelyn Tsai Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c | 2 +- drivers/net/wireless/mediatek/mt76/mt7915/mcu.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c index 6ef3431cad64..e1d15394a621 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c @@ -963,7 +963,7 @@ mt7915_twt_stats(struct seq_file *s, void *data) } /* The index of RF registers use the generic regidx, combined with two parts: - * WF selection [31:28] and offset [27:0]. + * WF selection [31:24] and offset [23:0]. */ static int mt7915_rf_regval_get(void *data, u64 *val) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 8d297e4aa7d4..1b9f3da8ae96 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -3447,8 +3447,8 @@ int mt7915_mcu_rf_regval(struct mt7915_dev *dev, u32 regidx, u32 *val, bool set) __le32 ofs; __le32 data; } __packed req = { - .idx = cpu_to_le32(u32_get_bits(regidx, GENMASK(31, 28))), - .ofs = cpu_to_le32(u32_get_bits(regidx, GENMASK(27, 0))), + .idx = cpu_to_le32(u32_get_bits(regidx, GENMASK(31, 24))), + .ofs = cpu_to_le32(u32_get_bits(regidx, GENMASK(23, 0))), .data = set ? cpu_to_le32(*val) : 0, }; struct sk_buff *skb; From a7ec8bcf00034ce84d4c9a15dffd7577fbed4db2 Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Fri, 30 Sep 2022 23:13:10 +0800 Subject: [PATCH 072/144] wifi: mt76: mt7915: rework eeprom tx paths and streams init Rework tx paths and streams init part to improve readability, and make sure that the available tx streams should be smaller than or equal to the available tx paths. Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt7915/eeprom.c | 57 ++++++++----------- .../wireless/mediatek/mt76/mt7915/eeprom.h | 5 -- 2 files changed, 23 insertions(+), 39 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c index 4b1a9811646f..83bced0c0785 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c @@ -173,60 +173,49 @@ static void mt7915_eeprom_parse_band_config(struct mt7915_phy *phy) void mt7915_eeprom_parse_hw_cap(struct mt7915_dev *dev, struct mt7915_phy *phy) { - u8 nss, nss_band, nss_band_max, *eeprom = dev->mt76.eeprom.data; + u8 path, nss, nss_max = 4, *eeprom = dev->mt76.eeprom.data; struct mt76_phy *mphy = phy->mt76; - bool ext_phy = phy != &dev->phy; mt7915_eeprom_parse_band_config(phy); - /* read tx/rx mask from eeprom */ + /* read tx/rx path from eeprom */ if (is_mt7915(&dev->mt76)) { - nss = FIELD_GET(MT_EE_WIFI_CONF0_TX_PATH, - eeprom[MT_EE_WIFI_CONF]); + path = FIELD_GET(MT_EE_WIFI_CONF0_TX_PATH, + eeprom[MT_EE_WIFI_CONF]); } else { - nss = FIELD_GET(MT_EE_WIFI_CONF0_TX_PATH, - eeprom[MT_EE_WIFI_CONF + phy->band_idx]); + path = FIELD_GET(MT_EE_WIFI_CONF0_TX_PATH, + eeprom[MT_EE_WIFI_CONF + phy->band_idx]); } - if (!nss || nss > 4) - nss = 4; + if (!path || path > 4) + path = 4; /* read tx/rx stream */ - nss_band = nss; - + nss = path; if (dev->dbdc_support) { if (is_mt7915(&dev->mt76)) { - nss_band = FIELD_GET(MT_EE_WIFI_CONF3_TX_PATH_B0, - eeprom[MT_EE_WIFI_CONF + 3]); + nss = FIELD_GET(MT_EE_WIFI_CONF3_TX_PATH_B0, + eeprom[MT_EE_WIFI_CONF + 3]); if (phy->band_idx) - nss_band = FIELD_GET(MT_EE_WIFI_CONF3_TX_PATH_B1, - eeprom[MT_EE_WIFI_CONF + 3]); + nss = FIELD_GET(MT_EE_WIFI_CONF3_TX_PATH_B1, + eeprom[MT_EE_WIFI_CONF + 3]); } else { - nss_band = FIELD_GET(MT_EE_WIFI_CONF_STREAM_NUM, - eeprom[MT_EE_WIFI_CONF + 2 + phy->band_idx]); + nss = FIELD_GET(MT_EE_WIFI_CONF_STREAM_NUM, + eeprom[MT_EE_WIFI_CONF + 2 + phy->band_idx]); } - nss_band_max = is_mt7986(&dev->mt76) ? - MT_EE_NSS_MAX_DBDC_MA7986 : MT_EE_NSS_MAX_DBDC_MA7915; - } else { - nss_band_max = is_mt7986(&dev->mt76) ? - MT_EE_NSS_MAX_MA7986 : MT_EE_NSS_MAX_MA7915; + if (!is_mt7986(&dev->mt76)) + nss_max = 2; } - if (!nss_band || nss_band > nss_band_max) - nss_band = nss_band_max; + if (!nss) + nss = nss_max; + nss = min_t(u8, min_t(u8, nss_max, nss), path); - if (nss_band > nss) { - dev_warn(dev->mt76.dev, - "nss mismatch, nss(%d) nss_band(%d) band(%d) ext_phy(%d)\n", - nss, nss_band, phy->band_idx, ext_phy); - nss = nss_band; - } - - mphy->chainmask = BIT(nss) - 1; - if (ext_phy) + mphy->chainmask = BIT(path) - 1; + if (phy->band_idx) mphy->chainmask <<= dev->chainshift; - mphy->antenna_mask = BIT(nss_band) - 1; + mphy->antenna_mask = BIT(nss) - 1; dev->chainmask |= mphy->chainmask; dev->chainshift = hweight8(dev->mphy.chainmask); } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h index 7578ac6d0be6..f3e56817d36e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h @@ -58,11 +58,6 @@ enum mt7915_eeprom_field { #define MT_EE_RATE_DELTA_SIGN BIT(6) #define MT_EE_RATE_DELTA_EN BIT(7) -#define MT_EE_NSS_MAX_MA7915 4 -#define MT_EE_NSS_MAX_DBDC_MA7915 2 -#define MT_EE_NSS_MAX_MA7986 4 -#define MT_EE_NSS_MAX_DBDC_MA7986 4 - enum mt7915_adie_sku { MT7976_ONE_ADIE_DBDC = 0x7, MT7975_ONE_ADIE = 0x8, From ee0863aecdecbd9035cf45acb8017bce67fc835c Mon Sep 17 00:00:00 2001 From: Peter Chiu Date: Fri, 30 Sep 2022 23:13:11 +0800 Subject: [PATCH 073/144] wifi: mt76: mt7915: deal with special variant of mt7916 A variant of mt7916 supports up to 3 tx/rx paths but with only 2 spatial streams. An example usage of the 3rd path is to server as an auxiliary for beamforming. In order to deal with this case, this patch reworks some parts to correctly use paths or streams. Signed-off-by: Peter Chiu Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/init.c | 37 +++++++++---------- .../net/wireless/mediatek/mt76/mt7915/main.c | 11 +++--- .../net/wireless/mediatek/mt76/mt7915/mcu.c | 16 ++++---- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c index cc2aac86bcfb..324db5291c85 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c @@ -700,13 +700,13 @@ mt7915_init_hardware(struct mt7915_dev *dev, struct mt7915_phy *phy2) void mt7915_set_stream_vht_txbf_caps(struct mt7915_phy *phy) { - int nss; + int sts; u32 *cap; if (!phy->mt76->cap.has_5ghz) return; - nss = hweight8(phy->mt76->chainmask); + sts = hweight8(phy->mt76->chainmask); cap = &phy->mt76->sband_5g.sband.vht_cap.cap; *cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | @@ -717,28 +717,27 @@ void mt7915_set_stream_vht_txbf_caps(struct mt7915_phy *phy) IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE); - if (nss < 2) + if (sts < 2) return; *cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE | FIELD_PREP(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK, - nss - 1); + sts - 1); } static void -mt7915_set_stream_he_txbf_caps(struct mt7915_dev *dev, - struct ieee80211_sta_he_cap *he_cap, - int vif, int nss) +mt7915_set_stream_he_txbf_caps(struct mt7915_phy *phy, + struct ieee80211_sta_he_cap *he_cap, int vif) { + struct mt7915_dev *dev = phy->dev; struct ieee80211_he_cap_elem *elem = &he_cap->he_cap_elem; - u8 c, nss_160; + int sts = hweight8(phy->mt76->chainmask); + u8 c, sts_160 = sts; - /* Can do 1/2 of NSS streams in 160Mhz mode for mt7915 */ + /* Can do 1/2 of STS in 160Mhz mode for mt7915 */ if (is_mt7915(&dev->mt76) && !dev->dbdc_support) - nss_160 = nss / 2; - else - nss_160 = nss; + sts_160 /= 2; #ifdef CONFIG_MAC80211_MESH if (vif == NL80211_IFTYPE_MESH_POINT) @@ -778,11 +777,11 @@ mt7915_set_stream_he_txbf_caps(struct mt7915_dev *dev, elem->phy_cap_info[6] |= c; - if (nss < 2) + if (sts < 2) return; /* the maximum cap is 4 x 3, (Nr, Nc) = (3, 2) */ - elem->phy_cap_info[7] |= min_t(int, nss - 1, 2) << 3; + elem->phy_cap_info[7] |= min_t(int, sts - 1, 2) << 3; if (vif != NL80211_IFTYPE_AP) return; @@ -791,12 +790,12 @@ mt7915_set_stream_he_txbf_caps(struct mt7915_dev *dev, elem->phy_cap_info[4] |= IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER; /* num_snd_dim - * for mt7915, max supported nss is 2 for bw > 80MHz + * for mt7915, max supported sts is 2 for bw > 80MHz */ c = FIELD_PREP(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK, - nss - 1) | + sts - 1) | FIELD_PREP(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK, - nss_160 - 1); + sts_160 - 1); elem->phy_cap_info[5] |= c; c = IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB | @@ -836,7 +835,7 @@ mt7915_init_he_caps(struct mt7915_phy *phy, enum nl80211_band band, struct ieee80211_sband_iftype_data *data) { struct mt7915_dev *dev = phy->dev; - int i, idx = 0, nss = hweight8(phy->mt76->chainmask); + int i, idx = 0, nss = hweight8(phy->mt76->antenna_mask); u16 mcs_map = 0; u16 mcs_map_160 = 0; u8 nss_160; @@ -969,7 +968,7 @@ mt7915_init_he_caps(struct mt7915_phy *phy, enum nl80211_band band, he_mcs->rx_mcs_80p80 = cpu_to_le16(mcs_map_160); he_mcs->tx_mcs_80p80 = cpu_to_le16(mcs_map_160); - mt7915_set_stream_he_txbf_caps(dev, he_cap, i, nss); + mt7915_set_stream_he_txbf_caps(phy, he_cap, i); memset(he_cap->ppe_thres, 0, sizeof(he_cap->ppe_thres)); if (he_cap_elem->phy_cap_info[6] & diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 89b519cfd14c..0e852a7d3ecd 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -953,7 +953,7 @@ mt7915_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) struct mt7915_dev *dev = mt7915_hw_dev(hw); struct mt7915_phy *phy = mt7915_hw_phy(hw); int max_nss = hweight8(hw->wiphy->available_antennas_tx); - bool ext_phy = phy != &dev->phy; + u8 chainshift = dev->chainshift; if (!tx_ant || tx_ant != rx_ant || ffs(tx_ant) > max_nss) return -EINVAL; @@ -965,10 +965,11 @@ mt7915_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) phy->mt76->antenna_mask = tx_ant; - if (ext_phy) - tx_ant <<= dev->chainshift; - - phy->mt76->chainmask = tx_ant; + /* handle a variant of mt7916 which has 3T3R but nss2 on 5 GHz band */ + if (is_mt7916(&dev->mt76) && phy->band_idx && hweight8(tx_ant) == max_nss) + phy->mt76->chainmask = (dev->chainmask >> chainshift) << chainshift; + else + phy->mt76->chainmask = tx_ant << (chainshift * phy->band_idx); mt76_set_stream_caps(phy->mt76, true); mt7915_set_stream_vht_txbf_caps(phy); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 1b9f3da8ae96..b9ad24b1935a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -485,7 +485,7 @@ static void mt7915_mcu_bss_ra_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, struct mt7915_phy *phy) { - int max_nss = hweight8(phy->mt76->chainmask); + int max_nss = hweight8(phy->mt76->antenna_mask); struct bss_info_ra *ra; struct tlv *tlv; @@ -2617,8 +2617,8 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd) u8 control_ch; u8 center_ch; u8 bw; - u8 tx_streams_num; - u8 rx_streams; /* mask or num */ + u8 tx_path_num; + u8 rx_path; /* mask or num */ u8 switch_reason; u8 band_idx; u8 center_ch2; /* for 80+80 only */ @@ -2634,8 +2634,8 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd) .control_ch = chandef->chan->hw_value, .center_ch = ieee80211_frequency_to_channel(freq1), .bw = mt76_connac_chan_bw(chandef), - .tx_streams_num = hweight8(phy->mt76->antenna_mask), - .rx_streams = phy->mt76->antenna_mask, + .tx_path_num = hweight16(phy->mt76->chainmask), + .rx_path = phy->mt76->chainmask >> (dev->chainshift * phy->band_idx), .band_idx = phy->band_idx, .channel_band = ch_band[chandef->chan->band], }; @@ -2645,8 +2645,8 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd) (phy->mt76->test.state == MT76_TM_STATE_TX_FRAMES || phy->mt76->test.state == MT76_TM_STATE_RX_FRAMES || phy->mt76->test.state == MT76_TM_STATE_TX_CONT)) { - req.tx_streams_num = fls(phy->mt76->test.tx_antenna_mask); - req.rx_streams = phy->mt76->test.tx_antenna_mask; + req.tx_path_num = fls(phy->mt76->test.tx_antenna_mask); + req.rx_path = phy->mt76->test.tx_antenna_mask; if (phy != &dev->phy) req.rx_streams >>= dev->chainshift; @@ -2665,7 +2665,7 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd) req.switch_reason = CH_SWITCH_NORMAL; if (cmd == MCU_EXT_CMD(CHANNEL_SWITCH)) - req.rx_streams = hweight8(req.rx_streams); + req.rx_path = hweight8(req.rx_path); if (chandef->width == NL80211_CHAN_WIDTH_80P80) { int freq2 = chandef->center_freq2; From b61699d2cd5692cd8c366409c4715b2a1e0ff1af Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Fri, 30 Sep 2022 23:13:12 +0800 Subject: [PATCH 074/144] wifi: mt76: mt7915: rework testmode tx antenna setting Let the configuration of testmode antenna mask on both band0 and band1 become antenna_mask rather than chainmask. This could simplify the settings for user and get rid of the conversion when sending fw command. Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/mcu.c | 7 +------ .../wireless/mediatek/mt76/mt7915/testmode.c | 18 ++++++++---------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index b9ad24b1935a..4a2196f9df61 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -2642,14 +2642,9 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd) #ifdef CONFIG_NL80211_TESTMODE if (phy->mt76->test.tx_antenna_mask && - (phy->mt76->test.state == MT76_TM_STATE_TX_FRAMES || - phy->mt76->test.state == MT76_TM_STATE_RX_FRAMES || - phy->mt76->test.state == MT76_TM_STATE_TX_CONT)) { + mt76_testmode_enabled(phy->mt76)) { req.tx_path_num = fls(phy->mt76->test.tx_antenna_mask); req.rx_path = phy->mt76->test.tx_antenna_mask; - - if (phy != &dev->phy) - req.rx_streams >>= dev->chainshift; } #endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c index efb9bb8231e2..e1838f046568 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c @@ -447,15 +447,10 @@ mt7915_tm_set_tx_frames(struct mt7915_phy *phy, bool en) if (en) { mt7915_tm_update_channel(phy); - if (td->tx_spe_idx) { + if (td->tx_spe_idx) phy->test.spe_idx = td->tx_spe_idx; - } else { - u8 tx_ant = td->tx_antenna_mask; - - if (phy != &dev->phy) - tx_ant >>= dev->chainshift; - phy->test.spe_idx = spe_idx_map[tx_ant]; - } + else + phy->test.spe_idx = spe_idx_map[td->tx_antenna_mask]; } mt7915_tm_set_tam_arb(phy, en, @@ -696,7 +691,9 @@ mt7915_tm_set_params(struct mt76_phy *mphy, struct nlattr **tb, { struct mt76_testmode_data *td = &mphy->test; struct mt7915_phy *phy = mphy->priv; - u32 changed = 0; + struct mt7915_dev *dev = phy->dev; + u32 chainmask = mphy->chainmask, changed = 0; + bool ext_phy = phy != &dev->phy; int i; BUILD_BUG_ON(NUM_TM_CHANGED >= 32); @@ -705,7 +702,8 @@ mt7915_tm_set_params(struct mt76_phy *mphy, struct nlattr **tb, td->state == MT76_TM_STATE_OFF) return 0; - if (td->tx_antenna_mask & ~mphy->chainmask) + chainmask = ext_phy ? chainmask >> dev->chainshift : chainmask; + if (td->tx_antenna_mask > chainmask) return -EINVAL; for (i = 0; i < ARRAY_SIZE(tm_change_map); i++) { From faf2e7b5de08acd296bd12cfa20a32b92a8170cf Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Fri, 30 Sep 2022 23:13:13 +0800 Subject: [PATCH 075/144] wifi: mt76: connac: introduce mt76_connac_spe_idx() Add mt76_connac_spe_idx() for antenna mask to spatial extension index conversion. This is used to support flexible control of tx antenna. Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac.h | 11 +++++++++++ drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c | 11 ++++++++--- drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h | 2 +- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac.h index 635192c878cb..0915eb579539 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac.h @@ -261,6 +261,17 @@ mt76_connac_txwi_to_txp(struct mt76_dev *dev, struct mt76_txwi_cache *t) return (void *)(txwi + MT_TXD_SIZE); } +static inline u8 mt76_connac_spe_idx(u8 antenna_mask) +{ + static const u8 ant_to_spe[] = {0, 0, 1, 0, 3, 2, 4, 0, + 9, 8, 6, 10, 16, 12, 18, 0}; + + if (antenna_mask >= sizeof(ant_to_spe)) + return 0; + + return ant_to_spe[antenna_mask]; +} + int mt76_connac_pm_wake(struct mt76_phy *phy, struct mt76_connac_pm *pm); void mt76_connac_power_save_sched(struct mt76_phy *phy, struct mt76_connac_pm *pm); diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c index 34ac3d81a510..8b7ec64abc95 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c @@ -417,9 +417,6 @@ mt76_connac2_mac_write_txwi_80211(struct mt76_dev *dev, __le32 *txwi, if (ieee80211_is_beacon(fc)) { txwi[3] &= ~cpu_to_le32(MT_TXD3_SW_POWER_MGMT); txwi[3] |= cpu_to_le32(MT_TXD3_REM_TX_COUNT); - if (!is_mt7921(dev)) - txwi[7] |= cpu_to_le32(FIELD_PREP(MT_TXD7_SPE_IDX, - 0x18)); } if (info->flags & IEEE80211_TX_CTL_INJECTED) { @@ -550,6 +547,14 @@ void mt76_connac2_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi, val |= FIELD_PREP(MT_TXD6_TX_RATE, rate); txwi[6] |= cpu_to_le32(val); txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE); + + if (!is_mt7921(dev)) { + u8 spe_idx = mt76_connac_spe_idx(mphy->antenna_mask); + + if (!spe_idx) + spe_idx = 24 + phy_idx; + txwi[7] |= cpu_to_le32(FIELD_PREP(MT_TXD7_SPE_IDX, spe_idx)); + } } } EXPORT_SYMBOL_GPL(mt76_connac2_mac_write_txwi); diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index 718f427d8f6b..cf4ce3b1fc21 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -580,7 +580,7 @@ struct sta_rec_ra_fixed { struct sta_phy phy; - u8 spe_en; + u8 spe_idx; u8 short_preamble; u8 is_5g; u8 mmps_mode; From 7a9a957b2be6e894d7d207d987c343b566d71b1d Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Fri, 30 Sep 2022 23:13:14 +0800 Subject: [PATCH 076/144] wifi: mt76: mt7915: add spatial extension index support In previous, we only allow user to configure tx antenna mask contiguously (e.g. 0x3, 0xf). This patch allows user to configure tx antenna mask interleavingly (e.g. 0x5, 0x8). By setting proper antenna mask and nss, user can prioritized the signal of different antennas, which helps to test their performance in normal mode. Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/main.c | 3 --- .../net/wireless/mediatek/mt76/mt7915/mcu.c | 20 ++++++++++++++++++- .../net/wireless/mediatek/mt76/mt7915/mcu.h | 1 + .../wireless/mediatek/mt76/mt7915/testmode.c | 4 +--- 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 0e852a7d3ecd..409f57382cc7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -958,9 +958,6 @@ mt7915_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) if (!tx_ant || tx_ant != rx_ant || ffs(tx_ant) > max_nss) return -EINVAL; - if ((BIT(hweight8(tx_ant)) - 1) != tx_ant) - tx_ant = BIT(ffs(tx_ant) - 1) - 1; - mutex_lock(&dev->mt76.mutex); phy->mt76->antenna_mask = tx_ant; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 4a2196f9df61..0c90d3666255 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -1306,6 +1306,9 @@ int mt7915_mcu_set_fixed_rate_ctrl(struct mt7915_dev *dev, case RATE_PARAM_MMPS_UPDATE: ra->mmps_mode = mt7915_mcu_get_mmps_mode(sta->deflink.smps_mode); break; + case RATE_PARAM_SPE_UPDATE: + ra->spe_idx = *(u8 *)data; + break; default: break; } @@ -1348,6 +1351,18 @@ int mt7915_mcu_add_smps(struct mt7915_dev *dev, struct ieee80211_vif *vif, RATE_PARAM_MMPS_UPDATE); } +static int +mt7915_mcu_set_spe_idx(struct mt7915_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct mt76_phy *mphy = mvif->phy->mt76; + u8 spe_idx = mt76_connac_spe_idx(mphy->antenna_mask); + + return mt7915_mcu_set_fixed_rate_ctrl(dev, vif, sta, &spe_idx, + RATE_PARAM_SPE_UPDATE); +} + static int mt7915_mcu_add_rate_ctrl_fixed(struct mt7915_dev *dev, struct ieee80211_vif *vif, @@ -1435,7 +1450,7 @@ mt7915_mcu_add_rate_ctrl_fixed(struct mt7915_dev *dev, return ret; } - return 0; + return mt7915_mcu_set_spe_idx(dev, vif, sta); } static void @@ -2648,6 +2663,9 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd) } #endif + if (mt76_connac_spe_idx(phy->mt76->antenna_mask)) + req.tx_path_num = fls(phy->mt76->antenna_mask); + if (cmd == MCU_EXT_CMD(SET_RX_PATH) || dev->mt76.hw->conf.flags & IEEE80211_CONF_MONITOR) req.switch_reason = CH_SWITCH_NORMAL; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h index cd1edf553fc1..87cd1bfff3e6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h @@ -394,6 +394,7 @@ enum { RATE_PARAM_FIXED_MCS, RATE_PARAM_FIXED_GI = 11, RATE_PARAM_AUTO = 20, + RATE_PARAM_SPE_UPDATE = 22, }; #define RATE_CFG_MCS GENMASK(3, 0) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c index e1838f046568..a979460fad2d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c @@ -432,8 +432,6 @@ mt7915_tm_update_channel(struct mt7915_phy *phy) static void mt7915_tm_set_tx_frames(struct mt7915_phy *phy, bool en) { - static const u8 spe_idx_map[] = {0, 0, 1, 0, 3, 2, 4, 0, - 9, 8, 6, 10, 16, 12, 18, 0}; struct mt76_testmode_data *td = &phy->mt76->test; struct mt7915_dev *dev = phy->dev; struct ieee80211_tx_info *info; @@ -450,7 +448,7 @@ mt7915_tm_set_tx_frames(struct mt7915_phy *phy, bool en) if (td->tx_spe_idx) phy->test.spe_idx = td->tx_spe_idx; else - phy->test.spe_idx = spe_idx_map[td->tx_antenna_mask]; + phy->test.spe_idx = mt76_connac_spe_idx(td->tx_antenna_mask); } mt7915_tm_set_tam_arb(phy, en, From 7a12e06dacdcf2db82e11d6be42e66e366b467d6 Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Fri, 30 Sep 2022 23:13:15 +0800 Subject: [PATCH 077/144] wifi: mt76: mt7915: set correct antenna for radar detection on MT7915D For MT7915D, correct antenna index should be set to let DFS radar detection work on the 5 GHz band. Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 15 +++++++++++++++ .../net/wireless/mediatek/mt76/mt7915/mt7915.h | 1 + 2 files changed, 16 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 89a3810ee53f..d0285a02775a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -1818,6 +1818,13 @@ static int mt7915_dfs_start_rdd(struct mt7915_dev *dev, int chain) if (err < 0) return err; + if (is_mt7915(&dev->mt76)) { + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_SET_WF_ANT, chain, + 0, dev->dbdc_support ? 2 : 0); + if (err < 0) + return err; + } + return mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_DET_MODE, chain, MT_RX_SEL0, 1); } @@ -1938,6 +1945,14 @@ stop: if (err < 0) return err; + if (is_mt7915(&dev->mt76)) { + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_SET_WF_ANT, + phy->band_idx, 0, + dev->dbdc_support ? 2 : 0); + if (err < 0) + return err; + } + mt7915_dfs_stop_radar_detector(phy); phy->mt76->dfs_state = MT_DFS_STATE_DISABLED; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index 1eb11617a625..c90a148d6239 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -357,6 +357,7 @@ enum mt7915_rdd_cmd { RDD_DET_MODE, RDD_RADAR_EMULATE, RDD_START_TXQ = 20, + RDD_SET_WF_ANT = 30, RDD_CAC_START = 50, RDD_CAC_END, RDD_NORMAL_START, From 0c881dc08fd71ca2673f31a64989fbb28eac26f4 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Sat, 1 Oct 2022 09:42:44 +0800 Subject: [PATCH 078/144] wifi: mt76: mt7915: fix mt7915_mac_set_timing() Correct mac timiing settings for different hardware generations. This improves 40-60Mbps performance. Fixes: 9aac2969fe5f ("mt76: mt7915: update mac timing settings") Reported-By: Carson Vandegriffe Tested-by: Chad Monroe Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/mac.c | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index d0285a02775a..324f0f58572b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -1153,7 +1153,7 @@ void mt7915_mac_set_timing(struct mt7915_phy *phy) FIELD_PREP(MT_TIMEOUT_VAL_CCA, 48); u32 ofdm = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 60) | FIELD_PREP(MT_TIMEOUT_VAL_CCA, 28); - int offset; + int eifs_ofdm = 360, sifs = 10, offset; bool a_band = !(phy->mt76->chandef.chan->band == NL80211_BAND_2GHZ); if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state)) @@ -1171,17 +1171,26 @@ void mt7915_mac_set_timing(struct mt7915_phy *phy) reg_offset = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, offset) | FIELD_PREP(MT_TIMEOUT_VAL_CCA, offset); + if (!is_mt7915(&dev->mt76)) { + if (!a_band) { + mt76_wr(dev, MT_TMAC_ICR1(phy->band_idx), + FIELD_PREP(MT_IFS_EIFS_CCK, 314)); + eifs_ofdm = 78; + } else { + eifs_ofdm = 84; + } + } else if (a_band) { + sifs = 16; + } + mt76_wr(dev, MT_TMAC_CDTR(phy->band_idx), cck + reg_offset); mt76_wr(dev, MT_TMAC_ODTR(phy->band_idx), ofdm + reg_offset); mt76_wr(dev, MT_TMAC_ICR0(phy->band_idx), - FIELD_PREP(MT_IFS_EIFS_OFDM, a_band ? 84 : 78) | + FIELD_PREP(MT_IFS_EIFS_OFDM, eifs_ofdm) | FIELD_PREP(MT_IFS_RIFS, 2) | - FIELD_PREP(MT_IFS_SIFS, 10) | + FIELD_PREP(MT_IFS_SIFS, sifs) | FIELD_PREP(MT_IFS_SLOT, phy->slottime)); - mt76_wr(dev, MT_TMAC_ICR1(phy->band_idx), - FIELD_PREP(MT_IFS_EIFS_CCK, 314)); - if (phy->slottime < 20 || a_band) val = MT7915_CFEND_RATE_DEFAULT; else From 5498cee3c84f117286f70c687a9e78e7aa137f4e Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Wed, 28 Sep 2022 11:19:41 +0200 Subject: [PATCH 079/144] wifi: mt76: mt7915: move wed init routines in mmio.c This is a preliminary patch to enable wed support for mt7986-wmac chipset. Tested-by: Daniel Golle Co-developed-by: Bo Jiao Signed-off-by: Bo Jiao Co-developed-by: Sujuan Chen Signed-off-by: Sujuan Chen Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/mmio.c | 91 ++++++++++++++++++ .../wireless/mediatek/mt76/mt7915/mt7915.h | 2 + .../net/wireless/mediatek/mt76/mt7915/pci.c | 93 +------------------ 3 files changed, 94 insertions(+), 92 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c index 7bd5f6725d7b..e75f2229b6a1 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -10,6 +10,9 @@ #include "mac.h" #include "../trace.h" +static bool wed_enable; +module_param(wed_enable, bool, 0644); + static const u32 mt7915_reg[] = { [INT_SOURCE_CSR] = 0xd7010, [INT_MASK_CSR] = 0xd7014, @@ -472,6 +475,94 @@ static u32 mt7915_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val) return dev->bus_ops->rmw(mdev, addr, mask, val); } +#ifdef CONFIG_NET_MEDIATEK_SOC_WED +static int mt7915_mmio_wed_offload_enable(struct mtk_wed_device *wed) +{ + struct mt7915_dev *dev; + struct mt7915_phy *phy; + int ret; + + dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed); + + spin_lock_bh(&dev->mt76.token_lock); + dev->mt76.token_size = wed->wlan.token_start; + spin_unlock_bh(&dev->mt76.token_lock); + + ret = wait_event_timeout(dev->mt76.tx_wait, + !dev->mt76.wed_token_count, HZ); + if (!ret) + return -EAGAIN; + + phy = &dev->phy; + mt76_set(dev, MT_AGG_ACR4(phy->band_idx), MT_AGG_ACR_PPDU_TXS2H); + + phy = dev->mt76.phys[MT_BAND1] ? dev->mt76.phys[MT_BAND1]->priv : NULL; + if (phy) + mt76_set(dev, MT_AGG_ACR4(phy->band_idx), + MT_AGG_ACR_PPDU_TXS2H); + + return 0; +} + +static void mt7915_mmio_wed_offload_disable(struct mtk_wed_device *wed) +{ + struct mt7915_dev *dev; + struct mt7915_phy *phy; + + dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed); + + spin_lock_bh(&dev->mt76.token_lock); + dev->mt76.token_size = MT7915_TOKEN_SIZE; + spin_unlock_bh(&dev->mt76.token_lock); + + /* MT_TXD5_TX_STATUS_HOST (MPDU format) has higher priority than + * MT_AGG_ACR_PPDU_TXS2H (PPDU format) even though ACR bit is set. + */ + phy = &dev->phy; + mt76_clear(dev, MT_AGG_ACR4(phy->band_idx), MT_AGG_ACR_PPDU_TXS2H); + + phy = dev->mt76.phys[MT_BAND1] ? dev->mt76.phys[MT_BAND1]->priv : NULL; + if (phy) + mt76_clear(dev, MT_AGG_ACR4(phy->band_idx), + MT_AGG_ACR_PPDU_TXS2H); +} +#endif + +int mt7915_mmio_wed_init(struct mt7915_dev *dev, struct pci_dev *pdev, + int *irq) +{ +#ifdef CONFIG_NET_MEDIATEK_SOC_WED + struct mtk_wed_device *wed = &dev->mt76.mmio.wed; + int ret; + + if (!wed_enable) + return 0; + + wed->wlan.pci_dev = pdev; + wed->wlan.wpdma_phys = pci_resource_start(pdev, 0) + + MT_WFDMA_EXT_CSR_BASE; + wed->wlan.nbuf = 4096; + wed->wlan.token_start = MT7915_TOKEN_SIZE - wed->wlan.nbuf; + wed->wlan.init_buf = mt7915_wed_init_buf; + wed->wlan.offload_enable = mt7915_mmio_wed_offload_enable; + wed->wlan.offload_disable = mt7915_mmio_wed_offload_disable; + + if (mtk_wed_device_attach(wed) != 0) + return 0; + + *irq = wed->irq; + dev->mt76.dma_dev = wed->dev; + + ret = dma_set_mask(wed->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + return 1; +#else + return 0; +#endif +} + static int mt7915_mmio_init(struct mt76_dev *mdev, void __iomem *mem_base, u32 device_id) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index c90a148d6239..1c1e4058d048 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -591,5 +591,7 @@ bool mt7915_debugfs_rx_log(struct mt7915_dev *dev, const void *data, int len); void mt7915_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct dentry *dir); #endif +int mt7915_mmio_wed_init(struct mt7915_dev *dev, struct pci_dev *pdev, + int *irq); #endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/pci.c b/drivers/net/wireless/mediatek/mt76/mt7915/pci.c index 728a879c3b00..d73b78bba3e5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/pci.c @@ -12,9 +12,6 @@ #include "mac.h" #include "../trace.h" -static bool wed_enable = false; -module_param(wed_enable, bool, 0644); - static LIST_HEAD(hif_list); static DEFINE_SPINLOCK(hif_lock); static u32 hif_idx; @@ -95,94 +92,6 @@ static int mt7915_pci_hif2_probe(struct pci_dev *pdev) return 0; } -#ifdef CONFIG_NET_MEDIATEK_SOC_WED -static int mt7915_wed_offload_enable(struct mtk_wed_device *wed) -{ - struct mt7915_dev *dev; - struct mt7915_phy *phy; - int ret; - - dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed); - - spin_lock_bh(&dev->mt76.token_lock); - dev->mt76.token_size = wed->wlan.token_start; - spin_unlock_bh(&dev->mt76.token_lock); - - ret = wait_event_timeout(dev->mt76.tx_wait, - !dev->mt76.wed_token_count, HZ); - if (!ret) - return -EAGAIN; - - phy = &dev->phy; - mt76_set(dev, MT_AGG_ACR4(phy->band_idx), MT_AGG_ACR_PPDU_TXS2H); - - phy = dev->mt76.phys[MT_BAND1] ? dev->mt76.phys[MT_BAND1]->priv : NULL; - if (phy) - mt76_set(dev, MT_AGG_ACR4(phy->band_idx), - MT_AGG_ACR_PPDU_TXS2H); - - return 0; -} - -static void mt7915_wed_offload_disable(struct mtk_wed_device *wed) -{ - struct mt7915_dev *dev; - struct mt7915_phy *phy; - - dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed); - - spin_lock_bh(&dev->mt76.token_lock); - dev->mt76.token_size = MT7915_TOKEN_SIZE; - spin_unlock_bh(&dev->mt76.token_lock); - - /* MT_TXD5_TX_STATUS_HOST (MPDU format) has higher priority than - * MT_AGG_ACR_PPDU_TXS2H (PPDU format) even though ACR bit is set. - */ - phy = &dev->phy; - mt76_clear(dev, MT_AGG_ACR4(phy->band_idx), MT_AGG_ACR_PPDU_TXS2H); - - phy = dev->mt76.phys[MT_BAND1] ? dev->mt76.phys[MT_BAND1]->priv : NULL; - if (phy) - mt76_clear(dev, MT_AGG_ACR4(phy->band_idx), - MT_AGG_ACR_PPDU_TXS2H); -} -#endif - -static int -mt7915_pci_wed_init(struct mt7915_dev *dev, struct pci_dev *pdev, int *irq) -{ -#ifdef CONFIG_NET_MEDIATEK_SOC_WED - struct mtk_wed_device *wed = &dev->mt76.mmio.wed; - int ret; - - if (!wed_enable) - return 0; - - wed->wlan.pci_dev = pdev; - wed->wlan.wpdma_phys = pci_resource_start(pdev, 0) + - MT_WFDMA_EXT_CSR_BASE; - wed->wlan.nbuf = 4096; - wed->wlan.token_start = MT7915_TOKEN_SIZE - wed->wlan.nbuf; - wed->wlan.init_buf = mt7915_wed_init_buf; - wed->wlan.offload_enable = mt7915_wed_offload_enable; - wed->wlan.offload_disable = mt7915_wed_offload_disable; - - if (mtk_wed_device_attach(wed) != 0) - return 0; - - *irq = wed->irq; - dev->mt76.dma_dev = wed->dev; - - ret = dma_set_mask(wed->dev, DMA_BIT_MASK(32)); - if (ret) - return ret; - - return 1; -#else - return 0; -#endif -} - static int mt7915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -220,7 +129,7 @@ static int mt7915_pci_probe(struct pci_dev *pdev, mt7915_wfsys_reset(dev); hif2 = mt7915_pci_init_hif2(pdev); - ret = mt7915_pci_wed_init(dev, pdev, &irq); + ret = mt7915_mmio_wed_init(dev, pdev, &irq); if (ret < 0) goto free_wed_or_irq_vector; From b7ebf46e2526c860d46f98ab93cab547069461f6 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Wed, 28 Sep 2022 11:19:42 +0200 Subject: [PATCH 080/144] wifi: mt76: mt7915: enable wed for mt7986 chipset Introduce wed tx support for MT7986 chipset Tested-by: Daniel Golle Co-developed-by: Bo Jiao Signed-off-by: Bo Jiao Co-developed-by: Sujuan Chen Signed-off-by: Sujuan Chen Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/dma.c | 1 + drivers/net/wireless/mediatek/mt76/mt7915/mmio.c | 11 +++++++++++ drivers/net/wireless/mediatek/mt76/mt7915/regs.h | 8 ++++++++ 3 files changed, 20 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c index 00aafc2422f3..90a89cf493a9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c @@ -324,6 +324,7 @@ static int mt7915_dma_enable(struct mt7915_dev *dev) wed_irq_mask |= MT_INT_TX_DONE_BAND0 | MT_INT_TX_DONE_BAND1; mt76_wr(dev, MT_INT_WED_MASK_CSR, wed_irq_mask); + mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask); mtk_wed_device_start(&dev->mt76.mmio.wed, wed_irq_mask); } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c index e75f2229b6a1..5676d62933ad 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -541,7 +541,18 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, struct pci_dev *pdev, wed->wlan.pci_dev = pdev; wed->wlan.wpdma_phys = pci_resource_start(pdev, 0) + MT_WFDMA_EXT_CSR_BASE; + wed->wlan.wpdma_int = pci_resource_start(pdev, 0) + + MT_INT_WED_SOURCE_CSR; + wed->wlan.wpdma_mask = pci_resource_start(pdev, 0) + + MT_INT_WED_MASK_CSR; + wed->wlan.wpdma_tx = pci_resource_start(pdev, 0) + + MT_TXQ_WED_RING_BASE; + wed->wlan.wpdma_txfree = pci_resource_start(pdev, 0) + + MT_RXQ_WED_RING_BASE; wed->wlan.nbuf = 4096; + wed->wlan.tx_tbit[0] = MT_WED_TX_DONE_BAND0; + wed->wlan.tx_tbit[1] = MT_WED_TX_DONE_BAND1; + wed->wlan.txfree_tbit = MT_WED_TX_FREE_DONE; wed->wlan.token_start = MT7915_TOKEN_SIZE - wed->wlan.nbuf; wed->wlan.init_buf = mt7915_wed_init_buf; wed->wlan.offload_enable = mt7915_mmio_wed_offload_enable; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h index 5920e705835a..d9642c5817e9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h @@ -596,6 +596,7 @@ enum offs_rev { #define MT_PCIE_RECOG_ID_MASK GENMASK(30, 0) #define MT_PCIE_RECOG_ID_SEM BIT(31) +#define MT_INT_WED_SOURCE_CSR MT_WFDMA_EXT_CSR(0x200) #define MT_INT_WED_MASK_CSR MT_WFDMA_EXT_CSR(0x204) #define MT_WED_TX_RING_BASE MT_WFDMA_EXT_CSR(0x300) @@ -642,6 +643,13 @@ enum offs_rev { #define MT_TXQ_EXT_CTRL(q) (MT_Q_BASE(__TXQ(q)) + 0x600 + \ MT_TXQ_ID(q)* 0x4) +#define MT_TXQ_WED_RING_BASE 0xd7300 +#define MT_RXQ_WED_RING_BASE 0xd7410 + +#define MT_WED_TX_DONE_BAND0 4 +#define MT_WED_TX_DONE_BAND1 5 +#define MT_WED_TX_FREE_DONE 1 + #define MT_INT_SOURCE_CSR __REG(INT_SOURCE_CSR) #define MT_INT_MASK_CSR __REG(INT_MASK_CSR) From eebb70976be5054feff92bc6b96174f1cf342a32 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Wed, 28 Sep 2022 11:19:43 +0200 Subject: [PATCH 081/144] wifi: mt76: mt7915: enable wed for mt7986-wmac chipset Enable WED tx support for mt7986-wmac chipset available on mt7986 board. Tested-by: Daniel Golle Co-developed-by: Bo Jiao Signed-off-by: Bo Jiao Co-developed-by: Sujuan Chen Signed-off-by: Sujuan Chen Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/dma.c | 72 ++++++++++++++----- .../net/wireless/mediatek/mt76/mt7915/main.c | 2 +- .../net/wireless/mediatek/mt76/mt7915/mcu.c | 2 +- .../net/wireless/mediatek/mt76/mt7915/mmio.c | 67 ++++++++++++----- .../wireless/mediatek/mt76/mt7915/mt7915.h | 4 +- .../net/wireless/mediatek/mt76/mt7915/pci.c | 2 +- .../net/wireless/mediatek/mt76/mt7915/regs.h | 17 +++-- .../net/wireless/mediatek/mt76/mt7915/soc.c | 21 ++++-- 8 files changed, 133 insertions(+), 54 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c index 90a89cf493a9..e4fa240834d8 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c @@ -11,7 +11,11 @@ mt7915_init_tx_queues(struct mt7915_phy *phy, int idx, int n_desc, int ring_base struct mt7915_dev *dev = phy->dev; if (mtk_wed_device_active(&phy->dev->mt76.mmio.wed)) { - ring_base = MT_WED_TX_RING_BASE; + if (is_mt7986(&dev->mt76)) + ring_base += MT_TXQ_ID(0) * MT_RING_SIZE; + else + ring_base = MT_WED_TX_RING_BASE; + idx -= MT_TXQ_ID(0); } @@ -58,17 +62,32 @@ static void mt7915_dma_config(struct mt7915_dev *dev) MCUQ_CONFIG(MT_MCUQ_WA, WFDMA1, MT_INT_TX_DONE_MCU_WA, MT7915_TXQ_MCU_WA); MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA1, MT_INT_TX_DONE_FWDL, MT7915_TXQ_FWDL); } else { - RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0_MT7916, MT7916_RXQ_BAND0); RXQ_CONFIG(MT_RXQ_MCU, WFDMA0, MT_INT_RX_DONE_WM, MT7916_RXQ_MCU_WM); - RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA0, MT_INT_RX_DONE_WA, MT7916_RXQ_MCU_WA); - RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1_MT7916, MT7916_RXQ_BAND1); RXQ_CONFIG(MT_RXQ_BAND1_WA, WFDMA0, MT_INT_RX_DONE_WA_EXT_MT7916, MT7916_RXQ_MCU_WA_EXT); - RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA0, MT_INT_RX_DONE_WA_MAIN_MT7916, MT7916_RXQ_MCU_WA_MAIN); - TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7915_TXQ_BAND0); - TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7915_TXQ_BAND1); MCUQ_CONFIG(MT_MCUQ_WM, WFDMA0, MT_INT_TX_DONE_MCU_WM, MT7915_TXQ_MCU_WM); MCUQ_CONFIG(MT_MCUQ_WA, WFDMA0, MT_INT_TX_DONE_MCU_WA_MT7916, MT7915_TXQ_MCU_WA); MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA0, MT_INT_TX_DONE_FWDL, MT7915_TXQ_FWDL); + + if (is_mt7916(&dev->mt76) && mtk_wed_device_active(&dev->mt76.mmio.wed)) { + RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_WED_RX_DONE_BAND0_MT7916, + MT7916_RXQ_BAND0); + RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA0, MT_INT_WED_RX_DONE_WA_MT7916, + MT7916_RXQ_MCU_WA); + RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_WED_RX_DONE_BAND1_MT7916, + MT7916_RXQ_BAND1); + RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA0, MT_INT_WED_RX_DONE_WA_MAIN_MT7916, + MT7916_RXQ_MCU_WA_MAIN); + TXQ_CONFIG(0, WFDMA0, MT_INT_WED_TX_DONE_BAND0, MT7915_TXQ_BAND0); + TXQ_CONFIG(1, WFDMA0, MT_INT_WED_TX_DONE_BAND1, MT7915_TXQ_BAND1); + } else { + RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0_MT7916, MT7916_RXQ_BAND0); + RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA0, MT_INT_RX_DONE_WA, MT7916_RXQ_MCU_WA); + RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1_MT7916, MT7916_RXQ_BAND1); + RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA0, MT_INT_RX_DONE_WA_MAIN_MT7916, + MT7916_RXQ_MCU_WA_MAIN); + TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7915_TXQ_BAND0); + TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7915_TXQ_BAND1); + } } } @@ -323,7 +342,8 @@ static int mt7915_dma_enable(struct mt7915_dev *dev) u32 wed_irq_mask = irq_mask; wed_irq_mask |= MT_INT_TX_DONE_BAND0 | MT_INT_TX_DONE_BAND1; - mt76_wr(dev, MT_INT_WED_MASK_CSR, wed_irq_mask); + if (!is_mt7986(&dev->mt76)) + mt76_wr(dev, MT_INT_WED_MASK_CSR, wed_irq_mask); mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask); mtk_wed_device_start(&dev->mt76.mmio.wed, wed_irq_mask); } @@ -349,13 +369,18 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2) mt7915_dma_disable(dev, true); - if (mtk_wed_device_active(&dev->mt76.mmio.wed)) { - mt76_set(dev, MT_WFDMA_HOST_CONFIG, MT_WFDMA_HOST_CONFIG_WED); + if (mtk_wed_device_active(&mdev->mmio.wed)) { + if (!is_mt7986(mdev)) { + u8 wed_control_rx1 = is_mt7915(mdev) ? 1 : 2; - mt76_wr(dev, MT_WFDMA_WED_RING_CONTROL, - FIELD_PREP(MT_WFDMA_WED_RING_CONTROL_TX0, 18) | - FIELD_PREP(MT_WFDMA_WED_RING_CONTROL_TX1, 19) | - FIELD_PREP(MT_WFDMA_WED_RING_CONTROL_RX1, 1)); + mt76_set(dev, MT_WFDMA_HOST_CONFIG, + MT_WFDMA_HOST_CONFIG_WED); + mt76_wr(dev, MT_WFDMA_WED_RING_CONTROL, + FIELD_PREP(MT_WFDMA_WED_RING_CONTROL_TX0, 18) | + FIELD_PREP(MT_WFDMA_WED_RING_CONTROL_TX1, 19) | + FIELD_PREP(MT_WFDMA_WED_RING_CONTROL_RX1, + wed_control_rx1)); + } } else { mt76_clear(dev, MT_WFDMA_HOST_CONFIG, MT_WFDMA_HOST_CONFIG_WED); } @@ -411,7 +436,7 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2) return ret; /* event from WA */ - if (mtk_wed_device_active(&dev->mt76.mmio.wed)) { + if (mtk_wed_device_active(&mdev->mmio.wed) && is_mt7915(mdev)) { wa_rx_base = MT_WED_RX_RING_BASE; wa_rx_idx = MT7915_RXQ_MCU_WA; dev->mt76.q_rx[MT_RXQ_MCU_WA].flags = MT_WED_Q_TXFREE; @@ -438,11 +463,20 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2) /* tx free notify event from WA for band0 */ if (!is_mt7915(mdev)) { + wa_rx_base = MT_RXQ_RING_BASE(MT_RXQ_MAIN_WA); + wa_rx_idx = MT_RXQ_ID(MT_RXQ_MAIN_WA); + + if (mtk_wed_device_active(&mdev->mmio.wed)) { + mdev->q_rx[MT_RXQ_MAIN_WA].flags = MT_WED_Q_TXFREE; + if (is_mt7916(mdev)) { + wa_rx_base = MT_WED_RX_RING_BASE; + wa_rx_idx = MT7915_RXQ_MCU_WA; + } + } + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN_WA], - MT_RXQ_ID(MT_RXQ_MAIN_WA), - MT7915_RX_MCU_RING_SIZE, - MT_RX_BUF_SIZE, - MT_RXQ_RING_BASE(MT_RXQ_MAIN_WA)); + wa_rx_idx, MT7915_RX_MCU_RING_SIZE, + MT_RX_BUF_SIZE, wa_rx_base); if (ret) return ret; } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 409f57382cc7..0ebaed174d5d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -1429,7 +1429,7 @@ mt7915_net_fill_forward_path(struct ieee80211_hw *hw, path->dev = ctx->dev; path->mtk_wdma.wdma_idx = wed->wdma_idx; path->mtk_wdma.bss = mvif->mt76.idx; - path->mtk_wdma.wcid = msta->wcid.idx; + path->mtk_wdma.wcid = is_mt7915(&dev->mt76) ? msta->wcid.idx : 0x3ff; path->mtk_wdma.queue = phy != &dev->phy; ctx->dev = NULL; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 0c90d3666255..790d690fabc3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -2289,7 +2289,7 @@ int mt7915_mcu_init(struct mt7915_dev *dev) if (ret) return ret; - if (mtk_wed_device_active(&dev->mt76.mmio.wed)) + if (mtk_wed_device_active(&dev->mt76.mmio.wed) && is_mt7915(&dev->mt76)) mt7915_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(CAPABILITY), 0, 0, 0); ret = mt7915_mcu_set_mwds(dev, 1); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c index 5676d62933ad..2ffb509cad5c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -27,6 +27,8 @@ static const u32 mt7915_reg[] = { [INFRA_MCU_ADDR_END] = 0x7c3fffff, [FW_EXCEPTION_ADDR] = 0x219848, [SWDEF_BASE_ADDR] = 0x41f200, + [TXQ_WED_RING_BASE] = 0xd7300, + [RXQ_WED_RING_BASE] = 0xd7410, }; static const u32 mt7916_reg[] = { @@ -43,6 +45,8 @@ static const u32 mt7916_reg[] = { [INFRA_MCU_ADDR_END] = 0x7c085fff, [FW_EXCEPTION_ADDR] = 0x022050bc, [SWDEF_BASE_ADDR] = 0x411400, + [TXQ_WED_RING_BASE] = 0xd7300, + [RXQ_WED_RING_BASE] = 0xd7410, }; static const u32 mt7986_reg[] = { @@ -59,6 +63,8 @@ static const u32 mt7986_reg[] = { [INFRA_MCU_ADDR_END] = 0x7c085fff, [FW_EXCEPTION_ADDR] = 0x02204ffc, [SWDEF_BASE_ADDR] = 0x411400, + [TXQ_WED_RING_BASE] = 0x24420, + [RXQ_WED_RING_BASE] = 0x24520, }; static const u32 mt7915_offs[] = { @@ -528,8 +534,8 @@ static void mt7915_mmio_wed_offload_disable(struct mtk_wed_device *wed) } #endif -int mt7915_mmio_wed_init(struct mt7915_dev *dev, struct pci_dev *pdev, - int *irq) +int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr, + bool pci, int *irq) { #ifdef CONFIG_NET_MEDIATEK_SOC_WED struct mtk_wed_device *wed = &dev->mt76.mmio.wed; @@ -538,27 +544,46 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, struct pci_dev *pdev, if (!wed_enable) return 0; - wed->wlan.pci_dev = pdev; - wed->wlan.wpdma_phys = pci_resource_start(pdev, 0) + - MT_WFDMA_EXT_CSR_BASE; - wed->wlan.wpdma_int = pci_resource_start(pdev, 0) + - MT_INT_WED_SOURCE_CSR; - wed->wlan.wpdma_mask = pci_resource_start(pdev, 0) + - MT_INT_WED_MASK_CSR; - wed->wlan.wpdma_tx = pci_resource_start(pdev, 0) + - MT_TXQ_WED_RING_BASE; - wed->wlan.wpdma_txfree = pci_resource_start(pdev, 0) + - MT_RXQ_WED_RING_BASE; + if (pci) { + struct pci_dev *pci_dev = pdev_ptr; + + wed->wlan.pci_dev = pci_dev; + wed->wlan.bus_type = MTK_WED_BUS_PCIE; + wed->wlan.wpdma_int = pci_resource_start(pci_dev, 0) + + MT_INT_WED_SOURCE_CSR; + wed->wlan.wpdma_mask = pci_resource_start(pci_dev, 0) + + MT_INT_WED_MASK_CSR; + wed->wlan.wpdma_phys = pci_resource_start(pci_dev, 0) + + MT_WFDMA_EXT_CSR_BASE; + wed->wlan.wpdma_tx = pci_resource_start(pci_dev, 0) + + MT_TXQ_WED_RING_BASE; + wed->wlan.wpdma_txfree = pci_resource_start(pci_dev, 0) + + MT_RXQ_WED_RING_BASE; + } else { + struct platform_device *plat_dev = pdev_ptr; + struct resource *res; + + res = platform_get_resource(plat_dev, IORESOURCE_MEM, 0); + if (!res) + return -ENOMEM; + + wed->wlan.platform_dev = plat_dev; + wed->wlan.bus_type = MTK_WED_BUS_AXI; + wed->wlan.wpdma_int = res->start + MT_INT_SOURCE_CSR; + wed->wlan.wpdma_mask = res->start + MT_INT_MASK_CSR; + wed->wlan.wpdma_tx = res->start + MT_TXQ_WED_RING_BASE; + wed->wlan.wpdma_txfree = res->start + MT_RXQ_WED_RING_BASE; + } wed->wlan.nbuf = 4096; - wed->wlan.tx_tbit[0] = MT_WED_TX_DONE_BAND0; - wed->wlan.tx_tbit[1] = MT_WED_TX_DONE_BAND1; - wed->wlan.txfree_tbit = MT_WED_TX_FREE_DONE; + wed->wlan.tx_tbit[0] = is_mt7915(&dev->mt76) ? 4 : 30; + wed->wlan.tx_tbit[1] = is_mt7915(&dev->mt76) ? 5 : 31; + wed->wlan.txfree_tbit = is_mt7915(&dev->mt76) ? 1 : 2; wed->wlan.token_start = MT7915_TOKEN_SIZE - wed->wlan.nbuf; wed->wlan.init_buf = mt7915_wed_init_buf; wed->wlan.offload_enable = mt7915_mmio_wed_offload_enable; wed->wlan.offload_disable = mt7915_mmio_wed_offload_disable; - if (mtk_wed_device_attach(wed) != 0) + if (mtk_wed_device_attach(wed)) return 0; *irq = wed->irq; @@ -638,7 +663,11 @@ void mt7915_dual_hif_set_irq_mask(struct mt7915_dev *dev, mdev->mmio.irqmask |= set; if (write_reg) { - mt76_wr(dev, MT_INT_MASK_CSR, mdev->mmio.irqmask); + if (mtk_wed_device_active(&mdev->mmio.wed)) + mtk_wed_device_irq_set_mask(&mdev->mmio.wed, + mdev->mmio.irqmask); + else + mt76_wr(dev, MT_INT_MASK_CSR, mdev->mmio.irqmask); mt76_wr(dev, MT_INT1_MASK_CSR, mdev->mmio.irqmask); } @@ -662,6 +691,8 @@ static void mt7915_irq_tasklet(struct tasklet_struct *t) if (mtk_wed_device_active(wed)) { mtk_wed_device_irq_set_mask(wed, 0); + if (dev->hif2) + mt76_wr(dev, MT_INT1_MASK_CSR, 0); intr = mtk_wed_device_irq_get(wed, dev->mt76.mmio.irqmask); } else { mt76_wr(dev, MT_INT_MASK_CSR, 0); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index 1c1e4058d048..fe6a6d3b0a32 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -591,7 +591,7 @@ bool mt7915_debugfs_rx_log(struct mt7915_dev *dev, const void *data, int len); void mt7915_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct dentry *dir); #endif -int mt7915_mmio_wed_init(struct mt7915_dev *dev, struct pci_dev *pdev, - int *irq); +int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr, + bool pci, int *irq); #endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/pci.c b/drivers/net/wireless/mediatek/mt76/mt7915/pci.c index d73b78bba3e5..743f01f6eae9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/pci.c @@ -129,7 +129,7 @@ static int mt7915_pci_probe(struct pci_dev *pdev, mt7915_wfsys_reset(dev); hif2 = mt7915_pci_init_hif2(pdev); - ret = mt7915_mmio_wed_init(dev, pdev, &irq); + ret = mt7915_mmio_wed_init(dev, pdev, true, &irq); if (ret < 0) goto free_wed_or_irq_vector; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h index d9642c5817e9..5180dd931835 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h @@ -26,6 +26,8 @@ enum reg_rev { INFRA_MCU_ADDR_END, FW_EXCEPTION_ADDR, SWDEF_BASE_ADDR, + TXQ_WED_RING_BASE, + RXQ_WED_RING_BASE, __MT_REG_MAX, }; @@ -643,12 +645,8 @@ enum offs_rev { #define MT_TXQ_EXT_CTRL(q) (MT_Q_BASE(__TXQ(q)) + 0x600 + \ MT_TXQ_ID(q)* 0x4) -#define MT_TXQ_WED_RING_BASE 0xd7300 -#define MT_RXQ_WED_RING_BASE 0xd7410 - -#define MT_WED_TX_DONE_BAND0 4 -#define MT_WED_TX_DONE_BAND1 5 -#define MT_WED_TX_FREE_DONE 1 +#define MT_TXQ_WED_RING_BASE __REG(TXQ_WED_RING_BASE) +#define MT_RXQ_WED_RING_BASE __REG(RXQ_WED_RING_BASE) #define MT_INT_SOURCE_CSR __REG(INT_SOURCE_CSR) #define MT_INT_MASK_CSR __REG(INT_MASK_CSR) @@ -668,6 +666,11 @@ enum offs_rev { #define MT_INT_RX_DONE_WA_MAIN_MT7916 BIT(2) #define MT_INT_RX_DONE_WA_EXT_MT7916 BIT(3) +#define MT_INT_WED_RX_DONE_BAND0_MT7916 BIT(18) +#define MT_INT_WED_RX_DONE_BAND1_MT7916 BIT(19) +#define MT_INT_WED_RX_DONE_WA_MAIN_MT7916 BIT(1) +#define MT_INT_WED_RX_DONE_WA_MT7916 BIT(17) + #define MT_INT_RX(q) (dev->q_int_mask[__RXQ(q)]) #define MT_INT_TX_MCU(q) (dev->q_int_mask[(q)]) @@ -691,6 +694,8 @@ enum offs_rev { #define MT_INT_TX_DONE_BAND0 BIT(30) #define MT_INT_TX_DONE_BAND1 BIT(31) #define MT_INT_TX_DONE_MCU_WA_MT7916 BIT(25) +#define MT_INT_WED_TX_DONE_BAND0 BIT(4) +#define MT_INT_WED_TX_DONE_BAND1 BIT(5) #define MT_INT_TX_DONE_MCU (MT_INT_TX_MCU(MT_MCUQ_WA) | \ MT_INT_TX_MCU(MT_MCUQ_WM) | \ diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/soc.c b/drivers/net/wireless/mediatek/mt76/mt7915/soc.c index c74afa746251..c06c56a0270d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/soc.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/soc.c @@ -1172,10 +1172,6 @@ static int mt7986_wmac_probe(struct platform_device *pdev) chip_id = (uintptr_t)of_device_get_match_data(&pdev->dev); - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - mem_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(mem_base)) { dev_err(&pdev->dev, "Failed to get memory resource\n"); @@ -1187,6 +1183,18 @@ static int mt7986_wmac_probe(struct platform_device *pdev) return PTR_ERR(dev); mdev = &dev->mt76; + ret = mt7915_mmio_wed_init(dev, pdev, false, &irq); + if (ret < 0) + goto free_device; + + if (!ret) { + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = irq; + goto free_device; + } + } + ret = devm_request_irq(mdev->dev, irq, mt7915_irq_handler, IRQF_SHARED, KBUILD_MODNAME, dev); if (ret) @@ -1206,9 +1214,10 @@ static int mt7986_wmac_probe(struct platform_device *pdev) free_irq: devm_free_irq(mdev->dev, irq, dev); - free_device: - mt76_free_device(&dev->mt76); + if (mtk_wed_device_active(&mdev->mmio.wed)) + mtk_wed_device_detach(&mdev->mmio.wed); + mt76_free_device(mdev); return ret; } From 5b0fb852237622bd4f48e80079216107c2b9fc1c Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Sat, 1 Oct 2022 07:19:20 -0700 Subject: [PATCH 082/144] Revert "mt76: use IEEE80211_OFFLOAD_ENCAP_ENABLED instead of MT_DRV_AMSDU_OFFLOAD" This reverts commit f17f4864504d754bcbf31e4c89412cdf9946c409 and adds the MT_DRV_AMSDU_OFFLOAD flag for MT7921 USB/SDIO The reverted commit significantly decreases performance when running a test where two MT7915 radios have 16 station vdevs each, configured for AC mode, and transmitting UDP traffic to AP. Co-developed-by: Felix Fietkau Reported-by: Carson Vandegriffe Signed-off-by: Ben Greear Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mac80211.c | 8 ++++++-- drivers/net/wireless/mediatek/mt76/mt76.h | 1 + drivers/net/wireless/mediatek/mt76/mt7915/mmio.c | 3 ++- drivers/net/wireless/mediatek/mt76/mt7921/pci.c | 3 ++- drivers/net/wireless/mediatek/mt76/mt7921/sdio.c | 1 + drivers/net/wireless/mediatek/mt76/mt7921/usb.c | 3 ++- 6 files changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index 6de13d641438..4bdbfd0f6233 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -443,8 +443,12 @@ mt76_phy_init(struct mt76_phy *phy, struct ieee80211_hw *hw) ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS); ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU); ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER); - ieee80211_hw_set(hw, TX_AMSDU); - ieee80211_hw_set(hw, TX_FRAG_LIST); + + if (!(dev->drv->drv_flags & MT_DRV_AMSDU_OFFLOAD)) { + ieee80211_hw_set(hw, TX_AMSDU); + ieee80211_hw_set(hw, TX_FRAG_LIST); + } + ieee80211_hw_set(hw, MFP_CAPABLE); ieee80211_hw_set(hw, AP_LINK_PS); ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index 26febc0c261a..f9bdf16fc6c4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -415,6 +415,7 @@ struct mt76_hw_cap { #define MT_DRV_SW_RX_AIRTIME BIT(2) #define MT_DRV_RX_DMA_HDR BIT(3) #define MT_DRV_HW_MGMT_TXQ BIT(4) +#define MT_DRV_AMSDU_OFFLOAD BIT(5) struct mt76_driver_ops { u32 drv_flags; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c index 2ffb509cad5c..be1b8ea711c7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -781,7 +781,8 @@ struct mt7915_dev *mt7915_mmio_probe(struct device *pdev, static const struct mt76_driver_ops drv_ops = { /* txwi_size = txd size + txp size */ .txwi_size = MT_TXD_SIZE + sizeof(struct mt76_connac_fw_txp), - .drv_flags = MT_DRV_TXWI_NO_FREE | MT_DRV_HW_MGMT_TXQ, + .drv_flags = MT_DRV_TXWI_NO_FREE | MT_DRV_HW_MGMT_TXQ | + MT_DRV_AMSDU_OFFLOAD, .survey_flags = SURVEY_INFO_TIME_TX | SURVEY_INFO_TIME_RX | SURVEY_INFO_TIME_BSS_RX, diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c index 8a53d8f286db..b38d119b2ea9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c @@ -228,7 +228,8 @@ static int mt7921_pci_probe(struct pci_dev *pdev, static const struct mt76_driver_ops drv_ops = { /* txwi_size = txd size + txp size */ .txwi_size = MT_TXD_SIZE + sizeof(struct mt76_connac_hw_txp), - .drv_flags = MT_DRV_TXWI_NO_FREE | MT_DRV_HW_MGMT_TXQ, + .drv_flags = MT_DRV_TXWI_NO_FREE | MT_DRV_HW_MGMT_TXQ | + MT_DRV_AMSDU_OFFLOAD, .survey_flags = SURVEY_INFO_TIME_TX | SURVEY_INFO_TIME_RX | SURVEY_INFO_TIME_BSS_RX, diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c index 3b25a06fd946..377ca5fa3f6e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c @@ -89,6 +89,7 @@ static int mt7921s_probe(struct sdio_func *func, { static const struct mt76_driver_ops drv_ops = { .txwi_size = MT_SDIO_TXD_SIZE, + .drv_flags = MT_DRV_AMSDU_OFFLOAD, .survey_flags = SURVEY_INFO_TIME_TX | SURVEY_INFO_TIME_RX | SURVEY_INFO_TIME_BSS_RX, diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c index 29c0ee330dbe..89249f0b6aba 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c @@ -170,7 +170,8 @@ static int mt7921u_probe(struct usb_interface *usb_intf, { static const struct mt76_driver_ops drv_ops = { .txwi_size = MT_SDIO_TXD_SIZE, - .drv_flags = MT_DRV_RX_DMA_HDR | MT_DRV_HW_MGMT_TXQ, + .drv_flags = MT_DRV_RX_DMA_HDR | MT_DRV_HW_MGMT_TXQ | + MT_DRV_AMSDU_OFFLOAD, .survey_flags = SURVEY_INFO_TIME_TX | SURVEY_INFO_TIME_RX | SURVEY_INFO_TIME_BSS_RX, From b0bfa00595be6883a139a0b3a96c9c3d62874624 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Fri, 14 Oct 2022 10:57:47 +0800 Subject: [PATCH 083/144] wifi: mt76: mt7915: improve accuracy of time_busy calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MIB INFO command is fetching MIB_BUSY_TIME, MIB_TX_TIME, MIB_RX_TIME and MIB_OBSS_AIRTIME from the radio and filling out cc_busy, cc_tx, cc_bss_rx and cc_rx respectively. busy should be >= tx + rx >= tx + bss_rx but we don’t always quite see this. Sometimes tx + rx is a bit higher than busy due to inaccurate accounting, so this patch recalculates numbers to make them more reasonable. Reported-By: Kevin Schneider Tested-by: Kevin Schneider Tested-by: Chad Monroe Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/init.c | 17 +++++++++ .../net/wireless/mediatek/mt76/mt7915/mcu.c | 35 +++++++++++++------ .../net/wireless/mediatek/mt76/mt7915/mcu.h | 8 +++-- .../net/wireless/mediatek/mt76/mt7915/regs.h | 14 ++++++++ 4 files changed, 61 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c index 324db5291c85..e864c0ab6c7f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c @@ -444,6 +444,23 @@ mt7915_mac_init_band(struct mt7915_dev *dev, u8 band) /* mt7915: disable rx rate report by default due to hw issues */ mt76_clear(dev, MT_DMA_DCR0(band), MT_DMA_DCR0_RXD_G5_EN); + + /* clear estimated value of EIFS for Rx duration & OBSS time */ + mt76_wr(dev, MT_WF_RMAC_RSVD0(band), MT_WF_RMAC_RSVD0_EIFS_CLR); + + /* clear backoff time for Rx duration */ + mt76_clear(dev, MT_WF_RMAC_MIB_AIRTIME1(band), + MT_WF_RMAC_MIB_NONQOSD_BACKOFF); + mt76_clear(dev, MT_WF_RMAC_MIB_AIRTIME3(band), + MT_WF_RMAC_MIB_QOS01_BACKOFF); + mt76_clear(dev, MT_WF_RMAC_MIB_AIRTIME4(band), + MT_WF_RMAC_MIB_QOS23_BACKOFF); + + /* clear backoff time and set software compensation for OBSS time */ + mask = MT_WF_RMAC_MIB_OBSS_BACKOFF | MT_WF_RMAC_MIB_ED_OFFSET; + set = FIELD_PREP(MT_WF_RMAC_MIB_OBSS_BACKOFF, 0) | + FIELD_PREP(MT_WF_RMAC_MIB_ED_OFFSET, 4); + mt76_rmw(dev, MT_WF_RMAC_MIB_AIRTIME0(band), mask, set); } static void mt7915_mac_init(struct mt7915_dev *dev) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 790d690fabc3..4000fcd7132a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -2940,25 +2940,36 @@ int mt7915_mcu_get_chan_mib_info(struct mt7915_phy *phy, bool chan_switch) { /* strict order */ static const u32 offs[] = { - MIB_BUSY_TIME, MIB_TX_TIME, MIB_RX_TIME, MIB_OBSS_AIRTIME, - MIB_BUSY_TIME_V2, MIB_TX_TIME_V2, MIB_RX_TIME_V2, + MIB_NON_WIFI_TIME, + MIB_TX_TIME, + MIB_RX_TIME, + MIB_OBSS_AIRTIME, + MIB_TXOP_INIT_COUNT, + /* v2 */ + MIB_NON_WIFI_TIME_V2, + MIB_TX_TIME_V2, + MIB_RX_TIME_V2, MIB_OBSS_AIRTIME_V2 }; struct mt76_channel_state *state = phy->mt76->chan_state; struct mt76_channel_state *state_ts = &phy->state_ts; struct mt7915_dev *dev = phy->dev; - struct mt7915_mcu_mib *res, req[4]; + struct mt7915_mcu_mib *res, req[5]; struct sk_buff *skb; int i, ret, start = 0, ofs = 20; + u64 cc_tx; if (!is_mt7915(&dev->mt76)) { - start = 4; + start = 5; ofs = 0; } - for (i = 0; i < 4; i++) { + for (i = 0; i < 5; i++) { req[i].band = cpu_to_le32(phy != &dev->phy); req[i].offs = cpu_to_le32(offs[i + start]); + + if (!is_mt7915(&dev->mt76) && i == 3) + break; } ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_EXT_CMD(GET_MIB_INFO), @@ -2968,20 +2979,24 @@ int mt7915_mcu_get_chan_mib_info(struct mt7915_phy *phy, bool chan_switch) res = (struct mt7915_mcu_mib *)(skb->data + ofs); +#define __res_u64(s) le64_to_cpu(res[s].data) + /* subtract Tx backoff time from Tx duration */ + cc_tx = is_mt7915(&dev->mt76) ? __res_u64(1) - __res_u64(4) : __res_u64(1); + if (chan_switch) goto out; -#define __res_u64(s) le64_to_cpu(res[s].data) - state->cc_busy += __res_u64(0) - state_ts->cc_busy; - state->cc_tx += __res_u64(1) - state_ts->cc_tx; + state->cc_tx += cc_tx - state_ts->cc_tx; state->cc_bss_rx += __res_u64(2) - state_ts->cc_bss_rx; state->cc_rx += __res_u64(2) + __res_u64(3) - state_ts->cc_rx; + state->cc_busy += __res_u64(0) + cc_tx + __res_u64(2) + __res_u64(3) - + state_ts->cc_busy; out: - state_ts->cc_busy = __res_u64(0); - state_ts->cc_tx = __res_u64(1); + state_ts->cc_tx = cc_tx; state_ts->cc_bss_rx = __res_u64(2); state_ts->cc_rx = __res_u64(2) + __res_u64(3); + state_ts->cc_busy = __res_u64(0) + cc_tx + __res_u64(2) + __res_u64(3); #undef __res_u64 dev_kfree_skb(skb); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h index 87cd1bfff3e6..a4f903f88078 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h @@ -160,15 +160,17 @@ struct mt7915_mcu_mib { enum mt7915_chan_mib_offs { /* mt7915 */ - MIB_BUSY_TIME = 14, MIB_TX_TIME = 81, MIB_RX_TIME, MIB_OBSS_AIRTIME = 86, + MIB_NON_WIFI_TIME, + MIB_TXOP_INIT_COUNT, + /* mt7916 */ - MIB_BUSY_TIME_V2 = 0, MIB_TX_TIME_V2 = 6, MIB_RX_TIME_V2 = 8, - MIB_OBSS_AIRTIME_V2 = 490 + MIB_OBSS_AIRTIME_V2 = 490, + MIB_NON_WIFI_TIME_V2 }; struct edca { diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h index 5180dd931835..7db809671230 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h @@ -525,8 +525,22 @@ enum offs_rev { #define MT_WF_RFCR1_DROP_CFEND BIT(7) #define MT_WF_RFCR1_DROP_CFACK BIT(8) +#define MT_WF_RMAC_RSVD0(_band) MT_WF_RMAC(_band, 0x02e0) +#define MT_WF_RMAC_RSVD0_EIFS_CLR BIT(21) + #define MT_WF_RMAC_MIB_AIRTIME0(_band) MT_WF_RMAC(_band, 0x0380) #define MT_WF_RMAC_MIB_RXTIME_CLR BIT(31) +#define MT_WF_RMAC_MIB_OBSS_BACKOFF GENMASK(15, 0) +#define MT_WF_RMAC_MIB_ED_OFFSET GENMASK(20, 16) + +#define MT_WF_RMAC_MIB_AIRTIME1(_band) MT_WF_RMAC(_band, 0x0384) +#define MT_WF_RMAC_MIB_NONQOSD_BACKOFF GENMASK(31, 16) + +#define MT_WF_RMAC_MIB_AIRTIME3(_band) MT_WF_RMAC(_band, 0x038c) +#define MT_WF_RMAC_MIB_QOS01_BACKOFF GENMASK(31, 0) + +#define MT_WF_RMAC_MIB_AIRTIME4(_band) MT_WF_RMAC(_band, 0x0390) +#define MT_WF_RMAC_MIB_QOS23_BACKOFF GENMASK(31, 0) /* WFDMA0 */ #define MT_WFDMA0_BASE __REG(WFDMA0_ADDR) From a71b648e352787db13184b9355c0b3ffc8a0824a Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Fri, 14 Oct 2022 10:57:48 +0800 Subject: [PATCH 084/144] wifi: mt76: mt7915: add ack signal support This reports signal strength of ACK packets from the peer as measured at each interface. Tested-by: Shurong Wen Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mac80211.c | 9 ++++----- drivers/net/wireless/mediatek/mt76/mt76.h | 1 + .../net/wireless/mediatek/mt76/mt7915/init.c | 7 +++++++ .../net/wireless/mediatek/mt76/mt7915/mac.c | 19 ++++++++++++++++++- .../net/wireless/mediatek/mt76/mt7915/main.c | 8 ++++++++ .../wireless/mediatek/mt76/mt7915/mt7915.h | 6 +++++- .../net/wireless/mediatek/mt76/mt7915/regs.h | 8 ++++++++ 7 files changed, 51 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index 4bdbfd0f6233..c59d12004459 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -951,14 +951,12 @@ void mt76_wcid_key_setup(struct mt76_dev *dev, struct mt76_wcid *wcid, } EXPORT_SYMBOL(mt76_wcid_key_setup); -static int -mt76_rx_signal(struct mt76_rx_status *status) +int mt76_rx_signal(u8 chain_mask, s8 *chain_signal) { - s8 *chain_signal = status->chain_signal; int signal = -128; u8 chains; - for (chains = status->chains; chains; chains >>= 1, chain_signal++) { + for (chains = chain_mask; chains; chains >>= 1, chain_signal++) { int cur, diff; cur = *chain_signal; @@ -980,6 +978,7 @@ mt76_rx_signal(struct mt76_rx_status *status) return signal; } +EXPORT_SYMBOL(mt76_rx_signal); static void mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb, @@ -1009,7 +1008,7 @@ mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb, status->ampdu_reference = mstat.ampdu_ref; status->device_timestamp = mstat.timestamp; status->mactime = mstat.timestamp; - status->signal = mt76_rx_signal(&mstat); + status->signal = mt76_rx_signal(mstat.chains, mstat.chain_signal); if (status->signal <= -128) status->flag |= RX_FLAG_NO_SIGNAL_VAL; diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index f9bdf16fc6c4..c91fb9bde632 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -1149,6 +1149,7 @@ void mt76_update_survey(struct mt76_phy *phy); void mt76_update_survey_active_time(struct mt76_phy *phy, ktime_t time); int mt76_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey); +int mt76_rx_signal(u8 chain_mask, s8 *chain_signal); void mt76_set_stream_caps(struct mt76_phy *phy, bool vht); int mt76_rx_aggr_start(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tid, diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c index e864c0ab6c7f..a042e0154211 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c @@ -353,6 +353,7 @@ mt7915_init_wiphy(struct ieee80211_hw *hw) wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_HE); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_DISCOVERY); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT); if (!mdev->dev->of_node || !of_property_read_bool(mdev->dev->of_node, @@ -461,6 +462,12 @@ mt7915_mac_init_band(struct mt7915_dev *dev, u8 band) set = FIELD_PREP(MT_WF_RMAC_MIB_OBSS_BACKOFF, 0) | FIELD_PREP(MT_WF_RMAC_MIB_ED_OFFSET, 4); mt76_rmw(dev, MT_WF_RMAC_MIB_AIRTIME0(band), mask, set); + + /* filter out non-resp frames and get instanstaeous signal reporting */ + mask = MT_WTBLOFF_TOP_RSCR_RCPI_MODE | MT_WTBLOFF_TOP_RSCR_RCPI_PARAM; + set = FIELD_PREP(MT_WTBLOFF_TOP_RSCR_RCPI_MODE, 0) | + FIELD_PREP(MT_WTBLOFF_TOP_RSCR_RCPI_PARAM, 0x3); + mt76_rmw(dev, MT_WTBLOFF_TOP_RSCR(band), mask, set); } static void mt7915_mac_init(struct mt7915_dev *dev) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 324f0f58572b..760ad786e566 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -8,7 +8,7 @@ #include "mac.h" #include "mcu.h" -#define to_rssi(field, rxv) ((FIELD_GET(field, rxv) - 220) / 2) +#define to_rssi(field, rcpi) ((FIELD_GET(field, rcpi) - 220) / 2) static const struct mt7915_dfs_radar_spec etsi_radar_specs = { .pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 }, @@ -118,6 +118,7 @@ static void mt7915_mac_sta_poll(struct mt7915_dev *dev) bool clear = false; u32 addr, val; u16 idx; + s8 rssi[4]; u8 bw; spin_lock_bh(&dev->sta_poll_lock); @@ -131,6 +132,8 @@ static void mt7915_mac_sta_poll(struct mt7915_dev *dev) spin_unlock_bh(&dev->sta_poll_lock); idx = msta->wcid.idx; + + /* refresh peer's airtime reporting */ addr = mt7915_mac_wtbl_lmac_addr(dev, idx, 20); for (i = 0; i < IEEE80211_NUM_ACS; i++) { @@ -209,6 +212,20 @@ static void mt7915_mac_sta_poll(struct mt7915_dev *dev) else rate->flags &= ~RATE_INFO_FLAGS_SHORT_GI; } + + /* get signal strength of resp frames (CTS/BA/ACK) */ + addr = mt7915_mac_wtbl_lmac_addr(dev, idx, 30); + val = mt76_rr(dev, addr); + + rssi[0] = to_rssi(GENMASK(7, 0), val); + rssi[1] = to_rssi(GENMASK(15, 8), val); + rssi[2] = to_rssi(GENMASK(23, 16), val); + rssi[3] = to_rssi(GENMASK(31, 14), val); + + msta->ack_signal = + mt76_rx_signal(msta->vif->phy->mt76->antenna_mask, rssi); + + ewma_avg_signal_add(&msta->avg_ack_signal, -msta->ack_signal); } rcu_read_unlock(); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 0ebaed174d5d..7f1e76d59118 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -665,6 +665,8 @@ int mt7915_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, msta->wcid.tx_info |= MT_WCID_TX_INFO_SET; msta->jiffies = jiffies; + ewma_avg_signal_init(&msta->avg_ack_signal); + mt7915_mac_wtbl_update(dev, idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); @@ -1025,6 +1027,12 @@ static void mt7915_sta_statistics(struct ieee80211_hw *hw, sinfo->tx_retries = msta->wcid.stats.tx_retries; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES); } + + sinfo->ack_signal = (s8)msta->ack_signal; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL); + + sinfo->avg_ack_signal = -(s8)ewma_avg_signal_read(&msta->avg_ack_signal); + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL_AVG); } static void mt7915_sta_rc_work(void *data, struct ieee80211_sta *sta) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index fe6a6d3b0a32..e6ae02eb2731 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -114,6 +114,8 @@ struct mt7915_twt_flow { u8 sched:1; }; +DECLARE_EWMA(avg_signal, 10, 8) + struct mt7915_sta { struct mt76_wcid wcid; /* must be first */ @@ -123,10 +125,12 @@ struct mt7915_sta { struct list_head rc_list; u32 airtime_ac[8]; + int ack_signal; + struct ewma_avg_signal avg_ack_signal; + unsigned long changed; unsigned long jiffies; unsigned long ampdu_state; - struct mt76_connac_sta_key_conf bip; struct { diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h index 7db809671230..9924271d8e36 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h @@ -226,6 +226,14 @@ enum offs_rev { #define MT_DMA_DCR0_MAX_RX_LEN GENMASK(15, 3) #define MT_DMA_DCR0_RXD_G5_EN BIT(23) +/* WTBLOFF TOP: band 0(0x820e9000),band 1(0x820f9000) */ +#define MT_WTBLOFF_TOP_BASE(_band) ((_band) ? 0x820f9000 : 0x820e9000) +#define MT_WTBLOFF_TOP(_band, ofs) (MT_WTBLOFF_TOP_BASE(_band) + (ofs)) + +#define MT_WTBLOFF_TOP_RSCR(_band) MT_WTBLOFF_TOP(_band, 0x008) +#define MT_WTBLOFF_TOP_RSCR_RCPI_MODE GENMASK(31, 30) +#define MT_WTBLOFF_TOP_RSCR_RCPI_PARAM GENMASK(25, 24) + /* ETBF: band 0(0x820ea000), band 1(0x820fa000) */ #define MT_WF_ETBF_BASE(_band) ((_band) ? 0x820fa000 : 0x820ea000) #define MT_WF_ETBF(_band, ofs) (MT_WF_ETBF_BASE(_band) + (ofs)) From df2632b3cea3524f4bb20246346f9e729873dc0f Mon Sep 17 00:00:00 2001 From: Ming Yen Hsieh Date: Mon, 24 Oct 2022 20:45:08 +0800 Subject: [PATCH 085/144] wifi: mt76: fix bandwidth 80MHz link fail in 6GHz band Due to information missing, the firmware may be fail on bandwidth related settings in mt7921/mt7922. Add new cmd STA_REC_HE_V2 to apply additional capabilities in 6GHz band. Tested-by: Ben Greear Co-developed-by: Deren Wu Signed-off-by: Deren Wu Signed-off-by: Ming Yen Hsieh Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt76_connac_mcu.c | 34 +++++++++++++++++++ .../wireless/mediatek/mt76/mt76_connac_mcu.h | 11 ++++++ 2 files changed, 45 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index 011fc9729b38..9bba18d24c71 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -744,6 +744,39 @@ mt76_connac_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) he->pkt_ext = 2; } +static void +mt76_connac_mcu_sta_he_tlv_v2(struct sk_buff *skb, struct ieee80211_sta *sta) +{ + struct ieee80211_sta_he_cap *he_cap = &sta->deflink.he_cap; + struct ieee80211_he_cap_elem *elem = &he_cap->he_cap_elem; + struct sta_rec_he_v2 *he; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_V2, sizeof(*he)); + + he = (struct sta_rec_he_v2 *)tlv; + memcpy(he->he_phy_cap, elem->phy_cap_info, sizeof(he->he_phy_cap)); + memcpy(he->he_mac_cap, elem->mac_cap_info, sizeof(he->he_mac_cap)); + + switch (sta->deflink.bandwidth) { + case IEEE80211_STA_RX_BW_160: + if (elem->phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) + he->max_nss_mcs[CMD_HE_MCS_BW8080] = + he_cap->he_mcs_nss_supp.rx_mcs_80p80; + + he->max_nss_mcs[CMD_HE_MCS_BW160] = + he_cap->he_mcs_nss_supp.rx_mcs_160; + fallthrough; + default: + he->max_nss_mcs[CMD_HE_MCS_BW80] = + he_cap->he_mcs_nss_supp.rx_mcs_80; + break; + } + + he->pkt_ext = IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US; +} + static u8 mt76_connac_get_phy_mode_v2(struct mt76_phy *mphy, struct ieee80211_vif *vif, enum nl80211_band band, struct ieee80211_sta *sta) @@ -838,6 +871,7 @@ void mt76_connac_mcu_sta_tlv(struct mt76_phy *mphy, struct sk_buff *skb, /* starec he */ if (sta->deflink.he_cap.has_he) { mt76_connac_mcu_sta_he_tlv(skb, sta); + mt76_connac_mcu_sta_he_tlv_v2(skb, sta); if (band == NL80211_BAND_6GHZ && sta_state == MT76_STA_INFO_STATE_ASSOC) { struct sta_rec_he_6g_capa *he_6g_capa; diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index cf4ce3b1fc21..8166722d4717 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -354,6 +354,16 @@ struct sta_rec_he { u8 rsv2[2]; } __packed; +struct sta_rec_he_v2 { + __le16 tag; + __le16 len; + u8 he_mac_cap[6]; + u8 he_phy_cap[11]; + u8 pkt_ext; + /* 0: BW80, 1: BW160, 2: BW8080 */ + __le16 max_nss_mcs[CMD_HE_MCS_BW_NUM]; +} __packed; + struct sta_rec_amsdu { __le16 tag; __le16 len; @@ -779,6 +789,7 @@ enum { STA_REC_BFEE, STA_REC_PHY = 0x15, STA_REC_HE_6G = 0x17, + STA_REC_HE_V2 = 0x19, STA_REC_MAX_NUM }; From 150b91419d3dee828cf23a6aa7b24925b2783cea Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Tue, 25 Oct 2022 08:31:00 +0800 Subject: [PATCH 086/144] wifi: mt76: mt7915: enable use_cts_prot support This adds selectable RTC/CTS enablement for each interface. Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 13 +++++++++++++ drivers/net/wireless/mediatek/mt76/mt7915/main.c | 3 +++ drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h | 2 ++ 3 files changed, 18 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 760ad786e566..741ccffda5ee 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -231,6 +231,19 @@ static void mt7915_mac_sta_poll(struct mt7915_dev *dev) rcu_read_unlock(); } +void mt7915_mac_enable_rtscts(struct mt7915_dev *dev, + struct ieee80211_vif *vif, bool enable) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + u32 addr; + + addr = mt7915_mac_wtbl_lmac_addr(dev, mvif->sta.wcid.idx, 5); + if (enable) + mt76_set(dev, addr, BIT(5)); + else + mt76_clear(dev, addr, BIT(5)); +} + static int mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb) { diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 7f1e76d59118..56f43ba5ea05 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -598,6 +598,9 @@ static void mt7915_bss_info_changed(struct ieee80211_hw *hw, mt7915_mcu_add_obss_spr(dev, vif, info->he_obss_pd.enable); } + if (changed & BSS_CHANGED_ERP_CTS_PROT) + mt7915_mac_enable_rtscts(dev, vif, info->use_cts_prot); + if (changed & BSS_CHANGED_ERP_SLOT) { int slottime = info->use_short_slot ? 9 : 20; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index e6ae02eb2731..98c13b6c47d4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -552,6 +552,8 @@ bool mt7915_mac_wtbl_update(struct mt7915_dev *dev, int idx, u32 mask); void mt7915_mac_reset_counters(struct mt7915_phy *phy); void mt7915_mac_cca_stats_reset(struct mt7915_phy *phy); void mt7915_mac_enable_nf(struct mt7915_dev *dev, bool ext_phy); +void mt7915_mac_enable_rtscts(struct mt7915_dev *dev, + struct ieee80211_vif *vif, bool enable); void mt7915_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi, struct sk_buff *skb, struct mt76_wcid *wcid, int pid, struct ieee80211_key_conf *key, From e34235ccc5e378a234157ba4142f396c9662b985 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Tue, 25 Oct 2022 08:31:01 +0800 Subject: [PATCH 087/144] wifi: mt76: mt7615: enable use_cts_prot support This adds selectable RTC/CTS enablement for each interface. Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7615/mac.c | 15 +++++++++++++++ drivers/net/wireless/mediatek/mt76/mt7615/main.c | 3 +++ .../net/wireless/mediatek/mt76/mt7615/mt7615.h | 2 ++ drivers/net/wireless/mediatek/mt76/mt7615/regs.h | 2 ++ 4 files changed, 22 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c index 2ce1705c0f43..45cad911687d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c @@ -1177,6 +1177,21 @@ void mt7615_mac_set_rates(struct mt7615_phy *phy, struct mt7615_sta *sta, } EXPORT_SYMBOL_GPL(mt7615_mac_set_rates); +void mt7615_mac_enable_rtscts(struct mt7615_dev *dev, + struct ieee80211_vif *vif, bool enable) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + u32 addr; + + addr = mt7615_mac_wtbl_addr(dev, mvif->sta.wcid.idx) + 3 * 4; + + if (enable) + mt76_set(dev, addr, MT_WTBL_W3_RTS); + else + mt76_clear(dev, addr, MT_WTBL_W3_RTS); +} +EXPORT_SYMBOL_GPL(mt7615_mac_enable_rtscts); + static int mt7615_mac_wtbl_update_key(struct mt7615_dev *dev, struct mt76_wcid *wcid, struct ieee80211_key_conf *key, diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/main.c b/drivers/net/wireless/mediatek/mt76/mt7615/main.c index 8d4733f87cda..98f9e2155009 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/main.c @@ -572,6 +572,9 @@ static void mt7615_bss_info_changed(struct ieee80211_hw *hw, } } + if (changed & BSS_CHANGED_ERP_CTS_PROT) + mt7615_mac_enable_rtscts(dev, vif, info->use_cts_prot); + if (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon) { mt7615_mcu_add_bss_info(phy, vif, NULL, true); mt7615_mcu_sta_add(phy, vif, NULL, true); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h index 060d52c81d9e..f632e6a99038 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h @@ -473,6 +473,8 @@ void mt7615_mac_reset_counters(struct mt7615_dev *dev); void mt7615_mac_cca_stats_reset(struct mt7615_phy *phy); void mt7615_mac_set_scs(struct mt7615_phy *phy, bool enable); void mt7615_mac_enable_nf(struct mt7615_dev *dev, bool ext_phy); +void mt7615_mac_enable_rtscts(struct mt7615_dev *dev, + struct ieee80211_vif *vif, bool enable); void mt7615_mac_sta_poll(struct mt7615_dev *dev); int mt7615_mac_write_txwi(struct mt7615_dev *dev, __le32 *txwi, struct sk_buff *skb, struct mt76_wcid *wcid, diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/regs.h b/drivers/net/wireless/mediatek/mt76/mt7615/regs.h index 6712ad9faeaa..fa1b9b26b399 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7615/regs.h @@ -446,6 +446,8 @@ enum mt7615_reg_base { #define MT_WTBL_RIUCR3_RATE6 GENMASK(19, 8) #define MT_WTBL_RIUCR3_RATE7 GENMASK(31, 20) +#define MT_WTBL_W3_RTS BIT(22) + #define MT_WTBL_W5_CHANGE_BW_RATE GENMASK(7, 5) #define MT_WTBL_W5_SHORT_GI_20 BIT(8) #define MT_WTBL_W5_SHORT_GI_40 BIT(9) From 528d13e7f033b54d50e0077922dd52f005d648cf Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Wed, 2 Nov 2022 13:35:01 +0100 Subject: [PATCH 088/144] wifi: mt76: mt7915: fix reporting of TX AGGR histogram Fix stats clash between bins [4-7] in 802.11 tx aggregation histogram. Fixes: e57b7901469fc ("mt76: add mac80211 driver for MT7915 PCIe-based chipsets") Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 741ccffda5ee..0147936cc7b8 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -1641,7 +1641,7 @@ void mt7915_mac_update_stats(struct mt7915_phy *phy) aggr0 = phy->band_idx ? ARRAY_SIZE(dev->mt76.aggr_stats) / 2 : 0; if (is_mt7915(&dev->mt76)) { - for (i = 0, aggr1 = aggr0 + 4; i < 4; i++) { + for (i = 0, aggr1 = aggr0 + 8; i < 4; i++) { val = mt76_rr(dev, MT_MIB_MB_SDR1(phy->band_idx, (i << 4))); mib->ba_miss_cnt += FIELD_GET(MT_MIB_BA_MISS_COUNT_MASK, val); From 028b4f22b37b88821fd87b56ce47b180583c774e Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Wed, 2 Nov 2022 13:46:50 +0100 Subject: [PATCH 089/144] wifi: mt76: mt7921: fix reporting of TX AGGR histogram Similar to mt7915, fix stats clash between bins [4-7] in 802.11 tx aggregation histogram. Fixes: 163f4d22c118d ("mt76: mt7921: add MAC support") Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7921/mac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c index 6860468ed191..1c0d8cf19b8e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c @@ -984,7 +984,7 @@ void mt7921_mac_update_mib_stats(struct mt7921_phy *phy) mib->tx_amsdu_cnt += val; } - for (i = 0, aggr1 = aggr0 + 4; i < 4; i++) { + for (i = 0, aggr1 = aggr0 + 8; i < 4; i++) { u32 val2; val = mt76_rr(dev, MT_TX_AGG_CNT(0, i)); From 1b9ba30ec265c38aaef94a7c93f3f0c8f3aff3e4 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Wed, 2 Nov 2022 23:29:14 +0100 Subject: [PATCH 090/144] wifi: mt76: mt7615: rely on mt7615_phy in mt7615_mac_reset_counters This is a preliminary patch to move aggr_stats array in mt76_phy structure. Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7615/mac.c | 8 +++----- drivers/net/wireless/mediatek/mt76/mt7615/main.c | 4 ++-- drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c index 45cad911687d..c55d753e467a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c @@ -107,9 +107,9 @@ static struct mt76_wcid *mt7615_rx_get_wcid(struct mt7615_dev *dev, return &sta->vif->sta.wcid; } -void mt7615_mac_reset_counters(struct mt7615_dev *dev) +void mt7615_mac_reset_counters(struct mt7615_phy *phy) { - struct mt76_phy *mphy_ext = dev->mt76.phys[MT_BAND1]; + struct mt7615_dev *dev = phy->dev; int i; for (i = 0; i < 4; i++) { @@ -118,9 +118,7 @@ void mt7615_mac_reset_counters(struct mt7615_dev *dev) } memset(dev->mt76.aggr_stats, 0, sizeof(dev->mt76.aggr_stats)); - dev->mt76.phy.survey_time = ktime_get_boottime(); - if (mphy_ext) - mphy_ext->survey_time = ktime_get_boottime(); + phy->mt76->survey_time = ktime_get_boottime(); /* reset airtime counters */ mt76_rr(dev, MT_MIB_SDR9(0)); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/main.c b/drivers/net/wireless/mediatek/mt76/mt7615/main.c index 98f9e2155009..ab4c1b4478aa 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/main.c @@ -83,7 +83,7 @@ static int mt7615_start(struct ieee80211_hw *hw) ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work, timeout); if (!running) - mt7615_mac_reset_counters(dev); + mt7615_mac_reset_counters(phy); out: mt7615_mutex_release(dev); @@ -320,7 +320,7 @@ int mt7615_set_channel(struct mt7615_phy *phy) if (ret) goto out; - mt7615_mac_reset_counters(dev); + mt7615_mac_reset_counters(phy); phy->noise = 0; phy->chfreq = mt76_rr(dev, MT_CHFREQ(ext_phy)); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h index f632e6a99038..8b37f8259f52 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h @@ -469,7 +469,7 @@ void mt7615_init_work(struct mt7615_dev *dev); int mt7615_mcu_restart(struct mt76_dev *dev); void mt7615_update_channel(struct mt76_phy *mphy); bool mt7615_mac_wtbl_update(struct mt7615_dev *dev, int idx, u32 mask); -void mt7615_mac_reset_counters(struct mt7615_dev *dev); +void mt7615_mac_reset_counters(struct mt7615_phy *phy); void mt7615_mac_cca_stats_reset(struct mt7615_phy *phy); void mt7615_mac_set_scs(struct mt7615_phy *phy, bool enable); void mt7615_mac_enable_nf(struct mt7615_dev *dev, bool ext_phy); From d107501ae881c20c1c822c8080a645f73a042c68 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Wed, 2 Nov 2022 23:29:15 +0100 Subject: [PATCH 091/144] wifi: mt76: move aggr_stats array in mt76_phy Move aggregation stats array per-phy instead of share it between multiple interfaces. This is a preliminary patch to add mt7996 driver support. Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76.h | 4 ++-- .../wireless/mediatek/mt76/mt7603/debugfs.c | 2 +- .../net/wireless/mediatek/mt76/mt7603/mac.c | 6 +++--- .../wireless/mediatek/mt76/mt7615/debugfs.c | 6 ++---- .../net/wireless/mediatek/mt76/mt7615/mac.c | 9 ++++---- .../wireless/mediatek/mt76/mt76x02_debugfs.c | 2 +- .../net/wireless/mediatek/mt76/mt76x02_mac.c | 6 +++--- .../wireless/mediatek/mt76/mt7915/debugfs.c | 5 ++--- .../net/wireless/mediatek/mt76/mt7915/mac.c | 21 +++++++------------ .../net/wireless/mediatek/mt76/mt7915/main.c | 5 ++--- .../wireless/mediatek/mt76/mt7921/debugfs.c | 2 +- .../net/wireless/mediatek/mt76/mt7921/mac.c | 10 ++++----- .../net/wireless/mediatek/mt76/mt7921/main.c | 2 +- 13 files changed, 35 insertions(+), 45 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index c91fb9bde632..a2bccf6b6c54 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -693,6 +693,8 @@ struct mt76_phy { enum mt76_dfs_state dfs_state; ktime_t survey_time; + u32 aggr_stats[32]; + struct mt76_hw_cap cap; struct mt76_sband sband_2g; struct mt76_sband sband_5g; @@ -781,8 +783,6 @@ struct mt76_dev { u32 rev; - u32 aggr_stats[32]; - struct tasklet_struct pre_tbtt_tasklet; int beacon_int; u8 beacon_mask; diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c index f52165dff422..3967f2f05774 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c @@ -85,7 +85,7 @@ mt7603_ampdu_stat_show(struct seq_file *file, void *data) bound[i], bound[i + 1]); seq_puts(file, "\nCount: "); for (i = 0; i < ARRAY_SIZE(bound); i++) - seq_printf(file, "%8d | ", dev->mt76.aggr_stats[i]); + seq_printf(file, "%8d | ", dev->mphy.aggr_stats[i]); seq_puts(file, "\n"); return 0; diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c index 49a511ae8161..70a7f84af028 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c @@ -39,7 +39,7 @@ void mt7603_mac_reset_counters(struct mt7603_dev *dev) for (i = 0; i < 2; i++) mt76_rr(dev, MT_TX_AGG_CNT(i)); - memset(dev->mt76.aggr_stats, 0, sizeof(dev->mt76.aggr_stats)); + memset(dev->mphy.aggr_stats, 0, sizeof(dev->mphy.aggr_stats)); } void mt7603_mac_set_timing(struct mt7603_dev *dev) @@ -1827,8 +1827,8 @@ void mt7603_mac_work(struct work_struct *work) for (i = 0, idx = 0; i < 2; i++) { u32 val = mt76_rr(dev, MT_TX_AGG_CNT(i)); - dev->mt76.aggr_stats[idx++] += val & 0xffff; - dev->mt76.aggr_stats[idx++] += val >> 16; + dev->mphy.aggr_stats[idx++] += val & 0xffff; + dev->mphy.aggr_stats[idx++] += val >> 16; } if (dev->mphy.mac_work_count == 10) diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c index c26b45a09923..2a6d317db5e0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c @@ -278,7 +278,6 @@ mt7615_ampdu_stat_read_phy(struct mt7615_phy *phy, { struct mt7615_dev *dev = file->private; u32 reg = is_mt7663(&dev->mt76) ? MT_MIB_ARNG(0) : MT_AGG_ASRCR0; - bool ext_phy = phy != &dev->phy; int bound[7], i, range; if (!phy) @@ -292,7 +291,7 @@ mt7615_ampdu_stat_read_phy(struct mt7615_phy *phy, for (i = 0; i < 3; i++) bound[i + 4] = MT_AGG_ASRCR_RANGE(range, i) + 1; - seq_printf(file, "\nPhy %d\n", ext_phy); + seq_printf(file, "\nPhy %d\n", phy != &dev->phy); seq_printf(file, "Length: %8d | ", bound[0]); for (i = 0; i < ARRAY_SIZE(bound) - 1; i++) @@ -300,9 +299,8 @@ mt7615_ampdu_stat_read_phy(struct mt7615_phy *phy, bound[i], bound[i + 1]); seq_puts(file, "\nCount: "); - range = ext_phy ? ARRAY_SIZE(dev->mt76.aggr_stats) / 2 : 0; for (i = 0; i < ARRAY_SIZE(bound); i++) - seq_printf(file, "%8d | ", dev->mt76.aggr_stats[i + range]); + seq_printf(file, "%8d | ", phy->mt76->aggr_stats[i]); seq_puts(file, "\n"); seq_printf(file, "BA miss count: %d\n", phy->mib.ba_miss_cnt); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c index c55d753e467a..305bf1826a02 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c @@ -117,7 +117,7 @@ void mt7615_mac_reset_counters(struct mt7615_phy *phy) mt76_rr(dev, MT_TX_AGG_CNT(1, i)); } - memset(dev->mt76.aggr_stats, 0, sizeof(dev->mt76.aggr_stats)); + memset(phy->mt76->aggr_stats, 0, sizeof(phy->mt76->aggr_stats)); phy->mt76->survey_time = ktime_get_boottime(); /* reset airtime counters */ @@ -2012,7 +2012,7 @@ mt7615_mac_update_mib_stats(struct mt7615_phy *phy) struct mt7615_dev *dev = phy->dev; struct mib_stats *mib = &phy->mib; bool ext_phy = phy != &dev->phy; - int i, aggr; + int i, aggr = 0; u32 val, val2; mib->fcs_err_cnt += mt76_get_field(dev, MT_MIB_SDR3(ext_phy), @@ -2026,7 +2026,6 @@ mt7615_mac_update_mib_stats(struct mt7615_phy *phy) mib->aggr_per = 1000 * (val - val2) / val; } - aggr = ext_phy ? ARRAY_SIZE(dev->mt76.aggr_stats) / 2 : 0; for (i = 0; i < 4; i++) { val = mt76_rr(dev, MT_MIB_MB_SDR1(ext_phy, i)); mib->ba_miss_cnt += FIELD_GET(MT_MIB_BA_MISS_COUNT_MASK, val); @@ -2039,8 +2038,8 @@ mt7615_mac_update_mib_stats(struct mt7615_phy *phy) val); val = mt76_rr(dev, MT_TX_AGG_CNT(ext_phy, i)); - dev->mt76.aggr_stats[aggr++] += val & 0xffff; - dev->mt76.aggr_stats[aggr++] += val >> 16; + phy->mt76->aggr_stats[aggr++] += val & 0xffff; + phy->mt76->aggr_stats[aggr++] += val >> 16; } } diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c b/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c index 981ac8e84807..8ce4bf44733d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c @@ -20,7 +20,7 @@ mt76x02_ampdu_stat_show(struct seq_file *file, void *data) seq_puts(file, "Count: "); for (j = 0; j < 8; j++) seq_printf(file, "%8d | ", - dev->mt76.aggr_stats[i * 8 + j]); + dev->mphy.aggr_stats[i * 8 + j]); seq_puts(file, "\n"); seq_puts(file, "--------"); for (j = 0; j < 8; j++) diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c index 93d96739f802..d3f74473e6fb 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c @@ -25,7 +25,7 @@ void mt76x02_mac_reset_counters(struct mt76x02_dev *dev) for (i = 0; i < 16; i++) mt76_rr(dev, MT_TX_STAT_FIFO); - memset(dev->mt76.aggr_stats, 0, sizeof(dev->mt76.aggr_stats)); + memset(dev->mphy.aggr_stats, 0, sizeof(dev->mphy.aggr_stats)); } EXPORT_SYMBOL_GPL(mt76x02_mac_reset_counters); @@ -1191,8 +1191,8 @@ void mt76x02_mac_work(struct work_struct *work) for (i = 0, idx = 0; i < 16; i++) { u32 val = mt76_rr(dev, MT_TX_AGG_CNT(i)); - dev->mt76.aggr_stats[idx++] += val & 0xffff; - dev->mt76.aggr_stats[idx++] += val >> 16; + dev->mphy.aggr_stats[idx++] += val & 0xffff; + dev->mphy.aggr_stats[idx++] += val >> 16; } mt76x02_check_mac_err(dev); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c index e1d15394a621..cd774c1bc49f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c @@ -639,7 +639,7 @@ mt7915_ampdu_stat_read_phy(struct mt7915_phy *phy, { struct mt7915_dev *dev = phy->dev; bool ext_phy = phy != &dev->phy; - int bound[15], range[4], i, n; + int bound[15], range[4], i; /* Tx ampdu stat */ for (i = 0; i < ARRAY_SIZE(range); i++) @@ -656,9 +656,8 @@ mt7915_ampdu_stat_read_phy(struct mt7915_phy *phy, bound[i] + 1, bound[i + 1]); seq_puts(file, "\nCount: "); - n = phy->band_idx ? ARRAY_SIZE(dev->mt76.aggr_stats) / 2 : 0; for (i = 0; i < ARRAY_SIZE(bound); i++) - seq_printf(file, "%8d | ", dev->mt76.aggr_stats[i + n]); + seq_printf(file, "%8d | ", phy->mt76->aggr_stats[i]); seq_puts(file, "\n"); seq_printf(file, "BA miss count: %d\n", phy->mib.ba_miss_cnt); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 0147936cc7b8..27f9f6a8aaa8 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -1159,12 +1159,8 @@ void mt7915_mac_reset_counters(struct mt7915_phy *phy) mt76_rr(dev, MT_TX_AGG_CNT2(phy->band_idx, i)); } - i = 0; phy->mt76->survey_time = ktime_get_boottime(); - if (phy->band_idx) - i = ARRAY_SIZE(dev->mt76.aggr_stats) / 2; - - memset(&dev->mt76.aggr_stats[i], 0, sizeof(dev->mt76.aggr_stats) / 2); + memset(phy->mt76->aggr_stats, 0, sizeof(phy->mt76->aggr_stats)); /* reset airtime counters */ mt76_set(dev, MT_WF_RMAC_MIB_AIRTIME0(phy->band_idx), @@ -1507,7 +1503,7 @@ void mt7915_mac_update_stats(struct mt7915_phy *phy) { struct mt7915_dev *dev = phy->dev; struct mib_stats *mib = &phy->mib; - int i, aggr0, aggr1, cnt; + int i, aggr0 = 0, aggr1, cnt; u32 val; cnt = mt76_rr(dev, MT_MIB_SDR3(phy->band_idx)); @@ -1639,7 +1635,6 @@ void mt7915_mac_update_stats(struct mt7915_phy *phy) mib->tx_amsdu_cnt += cnt; } - aggr0 = phy->band_idx ? ARRAY_SIZE(dev->mt76.aggr_stats) / 2 : 0; if (is_mt7915(&dev->mt76)) { for (i = 0, aggr1 = aggr0 + 8; i < 4; i++) { val = mt76_rr(dev, MT_MIB_MB_SDR1(phy->band_idx, (i << 4))); @@ -1654,12 +1649,12 @@ void mt7915_mac_update_stats(struct mt7915_phy *phy) FIELD_GET(MT_MIB_RTS_RETRIES_COUNT_MASK, val); val = mt76_rr(dev, MT_TX_AGG_CNT(phy->band_idx, i)); - dev->mt76.aggr_stats[aggr0++] += val & 0xffff; - dev->mt76.aggr_stats[aggr0++] += val >> 16; + phy->mt76->aggr_stats[aggr0++] += val & 0xffff; + phy->mt76->aggr_stats[aggr0++] += val >> 16; val = mt76_rr(dev, MT_TX_AGG_CNT2(phy->band_idx, i)); - dev->mt76.aggr_stats[aggr1++] += val & 0xffff; - dev->mt76.aggr_stats[aggr1++] += val >> 16; + phy->mt76->aggr_stats[aggr1++] += val & 0xffff; + phy->mt76->aggr_stats[aggr1++] += val >> 16; } cnt = mt76_rr(dev, MT_MIB_SDR32(phy->band_idx)); @@ -1706,8 +1701,8 @@ void mt7915_mac_update_stats(struct mt7915_phy *phy) for (i = 0; i < 8; i++) { val = mt76_rr(dev, MT_TX_AGG_CNT(phy->band_idx, i)); - dev->mt76.aggr_stats[aggr0++] += FIELD_GET(GENMASK(15, 0), val); - dev->mt76.aggr_stats[aggr0++] += FIELD_GET(GENMASK(31, 16), val); + phy->mt76->aggr_stats[aggr0++] += FIELD_GET(GENMASK(15, 0), val); + phy->mt76->aggr_stats[aggr0++] += FIELD_GET(GENMASK(31, 16), val); } cnt = mt76_rr(dev, MT_MIB_SDR32(phy->band_idx)); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 56f43ba5ea05..820c7132bf3f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -1267,7 +1267,7 @@ void mt7915_get_et_stats(struct ieee80211_hw *hw, }; struct mib_stats *mib = &phy->mib; /* See mt7915_ampdu_stat_read_phy, etc */ - int i, n, ei = 0; + int i, ei = 0; mutex_lock(&dev->mt76.mutex); @@ -1283,9 +1283,8 @@ void mt7915_get_et_stats(struct ieee80211_hw *hw, data[ei++] = mib->tx_pkt_ibf_cnt; /* Tx ampdu stat */ - n = phy->band_idx ? ARRAY_SIZE(dev->mt76.aggr_stats) / 2 : 0; for (i = 0; i < 15 /*ARRAY_SIZE(bound)*/; i++) - data[ei++] = dev->mt76.aggr_stats[i + n]; + data[ei++] = phy->mt76->aggr_stats[i]; data[ei++] = phy->mib.ba_miss_cnt; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c index bce76417f95d..29d8883268f6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c @@ -85,7 +85,7 @@ mt7921_ampdu_stat_read_phy(struct mt7921_phy *phy, seq_puts(file, "\nCount: "); for (i = 0; i < ARRAY_SIZE(bound); i++) - seq_printf(file, "%8d | ", dev->mt76.aggr_stats[i]); + seq_printf(file, "%8d | ", phy->mt76->aggr_stats[i]); seq_puts(file, "\n"); seq_printf(file, "BA miss count: %d\n", phy->mib.ba_miss_cnt); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c index 1c0d8cf19b8e..9a4f307f8f24 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c @@ -745,7 +745,7 @@ void mt7921_mac_reset_counters(struct mt7921_phy *phy) } dev->mt76.phy.survey_time = ktime_get_boottime(); - memset(&dev->mt76.aggr_stats[0], 0, sizeof(dev->mt76.aggr_stats) / 2); + memset(phy->mt76->aggr_stats, 0, sizeof(phy->mt76->aggr_stats)); /* reset airtime counters */ mt76_rr(dev, MT_MIB_SDR9(0)); @@ -990,10 +990,10 @@ void mt7921_mac_update_mib_stats(struct mt7921_phy *phy) val = mt76_rr(dev, MT_TX_AGG_CNT(0, i)); val2 = mt76_rr(dev, MT_TX_AGG_CNT2(0, i)); - dev->mt76.aggr_stats[aggr0++] += val & 0xffff; - dev->mt76.aggr_stats[aggr0++] += val >> 16; - dev->mt76.aggr_stats[aggr1++] += val2 & 0xffff; - dev->mt76.aggr_stats[aggr1++] += val2 >> 16; + phy->mt76->aggr_stats[aggr0++] += val & 0xffff; + phy->mt76->aggr_stats[aggr0++] += val >> 16; + phy->mt76->aggr_stats[aggr1++] += val2 & 0xffff; + phy->mt76->aggr_stats[aggr1++] += val2 >> 16; } } diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c index 7e409ac7d9a8..092f683572f4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -1075,7 +1075,7 @@ void mt7921_get_et_stats(struct ieee80211_hw *hw, struct ieee80211_vif *vif, /* Tx ampdu stat */ for (i = 0; i < 15; i++) - data[ei++] = dev->mt76.aggr_stats[i]; + data[ei++] = phy->mt76->aggr_stats[i]; data[ei++] = phy->mib.ba_miss_cnt; From d493bb5b9d98cbbd37eda1619ae56a626298c8e3 Mon Sep 17 00:00:00 2001 From: Bo Jiao Date: Thu, 10 Nov 2022 04:36:31 +0800 Subject: [PATCH 092/144] wifi: mt76: mt7915: rework mt7915_dma_reset() Reuse mt7915_dma_disable() to reduce duplicated code. This is a preliminary patch to enable full system reset. Co-developed-by: Ryder Lee Signed-off-by: Ryder Lee Signed-off-by: Bo Jiao Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/dma.c | 110 ++++++++++++++---- .../net/wireless/mediatek/mt76/mt7915/mac.c | 69 +---------- .../wireless/mediatek/mt76/mt7915/mt7915.h | 1 + 3 files changed, 91 insertions(+), 89 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c index e4fa240834d8..38360f940747 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c @@ -50,23 +50,37 @@ static void mt7915_dma_config(struct mt7915_dev *dev) #define TXQ_CONFIG(q, wfdma, int, id) Q_CONFIG(__TXQ(q), (wfdma), (int), (id)) if (is_mt7915(&dev->mt76)) { - RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0, MT7915_RXQ_BAND0); - RXQ_CONFIG(MT_RXQ_MCU, WFDMA1, MT_INT_RX_DONE_WM, MT7915_RXQ_MCU_WM); - RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA1, MT_INT_RX_DONE_WA, MT7915_RXQ_MCU_WA); - RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1, MT7915_RXQ_BAND1); - RXQ_CONFIG(MT_RXQ_BAND1_WA, WFDMA1, MT_INT_RX_DONE_WA_EXT, MT7915_RXQ_MCU_WA_EXT); - RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA1, MT_INT_RX_DONE_WA_MAIN, MT7915_RXQ_MCU_WA); + RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0, + MT7915_RXQ_BAND0); + RXQ_CONFIG(MT_RXQ_MCU, WFDMA1, MT_INT_RX_DONE_WM, + MT7915_RXQ_MCU_WM); + RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA1, MT_INT_RX_DONE_WA, + MT7915_RXQ_MCU_WA); + RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1, + MT7915_RXQ_BAND1); + RXQ_CONFIG(MT_RXQ_BAND1_WA, WFDMA1, MT_INT_RX_DONE_WA_EXT, + MT7915_RXQ_MCU_WA_EXT); + RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA1, MT_INT_RX_DONE_WA_MAIN, + MT7915_RXQ_MCU_WA); TXQ_CONFIG(0, WFDMA1, MT_INT_TX_DONE_BAND0, MT7915_TXQ_BAND0); TXQ_CONFIG(1, WFDMA1, MT_INT_TX_DONE_BAND1, MT7915_TXQ_BAND1); - MCUQ_CONFIG(MT_MCUQ_WM, WFDMA1, MT_INT_TX_DONE_MCU_WM, MT7915_TXQ_MCU_WM); - MCUQ_CONFIG(MT_MCUQ_WA, WFDMA1, MT_INT_TX_DONE_MCU_WA, MT7915_TXQ_MCU_WA); - MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA1, MT_INT_TX_DONE_FWDL, MT7915_TXQ_FWDL); + MCUQ_CONFIG(MT_MCUQ_WM, WFDMA1, MT_INT_TX_DONE_MCU_WM, + MT7915_TXQ_MCU_WM); + MCUQ_CONFIG(MT_MCUQ_WA, WFDMA1, MT_INT_TX_DONE_MCU_WA, + MT7915_TXQ_MCU_WA); + MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA1, MT_INT_TX_DONE_FWDL, + MT7915_TXQ_FWDL); } else { - RXQ_CONFIG(MT_RXQ_MCU, WFDMA0, MT_INT_RX_DONE_WM, MT7916_RXQ_MCU_WM); - RXQ_CONFIG(MT_RXQ_BAND1_WA, WFDMA0, MT_INT_RX_DONE_WA_EXT_MT7916, MT7916_RXQ_MCU_WA_EXT); - MCUQ_CONFIG(MT_MCUQ_WM, WFDMA0, MT_INT_TX_DONE_MCU_WM, MT7915_TXQ_MCU_WM); - MCUQ_CONFIG(MT_MCUQ_WA, WFDMA0, MT_INT_TX_DONE_MCU_WA_MT7916, MT7915_TXQ_MCU_WA); - MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA0, MT_INT_TX_DONE_FWDL, MT7915_TXQ_FWDL); + RXQ_CONFIG(MT_RXQ_MCU, WFDMA0, MT_INT_RX_DONE_WM, + MT7916_RXQ_MCU_WM); + RXQ_CONFIG(MT_RXQ_BAND1_WA, WFDMA0, MT_INT_RX_DONE_WA_EXT_MT7916, + MT7916_RXQ_MCU_WA_EXT); + MCUQ_CONFIG(MT_MCUQ_WM, WFDMA0, MT_INT_TX_DONE_MCU_WM, + MT7915_TXQ_MCU_WM); + MCUQ_CONFIG(MT_MCUQ_WA, WFDMA0, MT_INT_TX_DONE_MCU_WA_MT7916, + MT7915_TXQ_MCU_WA); + MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA0, MT_INT_TX_DONE_FWDL, + MT7915_TXQ_FWDL); if (is_mt7916(&dev->mt76) && mtk_wed_device_active(&dev->mt76.mmio.wed)) { RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_WED_RX_DONE_BAND0_MT7916, @@ -77,16 +91,23 @@ static void mt7915_dma_config(struct mt7915_dev *dev) MT7916_RXQ_BAND1); RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA0, MT_INT_WED_RX_DONE_WA_MAIN_MT7916, MT7916_RXQ_MCU_WA_MAIN); - TXQ_CONFIG(0, WFDMA0, MT_INT_WED_TX_DONE_BAND0, MT7915_TXQ_BAND0); - TXQ_CONFIG(1, WFDMA0, MT_INT_WED_TX_DONE_BAND1, MT7915_TXQ_BAND1); + TXQ_CONFIG(0, WFDMA0, MT_INT_WED_TX_DONE_BAND0, + MT7915_TXQ_BAND0); + TXQ_CONFIG(1, WFDMA0, MT_INT_WED_TX_DONE_BAND1, + MT7915_TXQ_BAND1); } else { - RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0_MT7916, MT7916_RXQ_BAND0); - RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA0, MT_INT_RX_DONE_WA, MT7916_RXQ_MCU_WA); - RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1_MT7916, MT7916_RXQ_BAND1); + RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0_MT7916, + MT7916_RXQ_BAND0); + RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA0, MT_INT_RX_DONE_WA, + MT7916_RXQ_MCU_WA); + RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1_MT7916, + MT7916_RXQ_BAND1); RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA0, MT_INT_RX_DONE_WA_MAIN_MT7916, MT7916_RXQ_MCU_WA_MAIN); - TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7915_TXQ_BAND0); - TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7915_TXQ_BAND1); + TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, + MT7915_TXQ_BAND0); + TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, + MT7915_TXQ_BAND1); } } } @@ -514,6 +535,53 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2) return 0; } +int mt7915_dma_reset(struct mt7915_dev *dev, bool force) +{ + struct mt76_phy *mphy_ext = dev->mt76.phys[MT_BAND1]; + int i; + + /* clean up hw queues */ + for (i = 0; i < ARRAY_SIZE(dev->mt76.phy.q_tx); i++) { + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], true); + if (mphy_ext) + mt76_queue_tx_cleanup(dev, mphy_ext->q_tx[i], true); + } + + for (i = 0; i < ARRAY_SIZE(dev->mt76.q_mcu); i++) + mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[i], true); + + mt76_for_each_q_rx(&dev->mt76, i) + mt76_queue_rx_cleanup(dev, &dev->mt76.q_rx[i]); + + /* reset wfsys */ + if (force) + mt7915_wfsys_reset(dev); + + mt7915_dma_disable(dev, force); + + /* reset hw queues */ + for (i = 0; i < __MT_TXQ_MAX; i++) { + mt76_queue_reset(dev, dev->mphy.q_tx[i]); + if (mphy_ext) + mt76_queue_reset(dev, mphy_ext->q_tx[i]); + } + + for (i = 0; i < __MT_MCUQ_MAX; i++) + mt76_queue_reset(dev, dev->mt76.q_mcu[i]); + + mt76_for_each_q_rx(&dev->mt76, i) + mt76_queue_reset(dev, &dev->mt76.q_rx[i]); + + mt76_tx_status_check(&dev->mt76, true); + + mt7915_dma_enable(dev); + + mt76_for_each_q_rx(&dev->mt76, i) + mt76_queue_rx_reset(dev, i); + + return 0; +} + void mt7915_dma_cleanup(struct mt7915_dev *dev) { mt7915_dma_disable(dev, true); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 27f9f6a8aaa8..662671621cf0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -1332,73 +1332,6 @@ mt7915_update_beacons(struct mt7915_dev *dev) mt7915_update_vif_beacon, mphy_ext->hw); } -static void -mt7915_dma_reset(struct mt7915_dev *dev) -{ - struct mt76_phy *mphy_ext = dev->mt76.phys[MT_BAND1]; - u32 hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); - int i; - - mt76_clear(dev, MT_WFDMA0_GLO_CFG, - MT_WFDMA0_GLO_CFG_TX_DMA_EN | - MT_WFDMA0_GLO_CFG_RX_DMA_EN); - - if (is_mt7915(&dev->mt76)) - mt76_clear(dev, MT_WFDMA1_GLO_CFG, - MT_WFDMA1_GLO_CFG_TX_DMA_EN | - MT_WFDMA1_GLO_CFG_RX_DMA_EN); - if (dev->hif2) { - mt76_clear(dev, MT_WFDMA0_GLO_CFG + hif1_ofs, - MT_WFDMA0_GLO_CFG_TX_DMA_EN | - MT_WFDMA0_GLO_CFG_RX_DMA_EN); - - if (is_mt7915(&dev->mt76)) - mt76_clear(dev, MT_WFDMA1_GLO_CFG + hif1_ofs, - MT_WFDMA1_GLO_CFG_TX_DMA_EN | - MT_WFDMA1_GLO_CFG_RX_DMA_EN); - } - - usleep_range(1000, 2000); - - for (i = 0; i < __MT_TXQ_MAX; i++) { - mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], true); - if (mphy_ext) - mt76_queue_tx_cleanup(dev, mphy_ext->q_tx[i], true); - } - - for (i = 0; i < __MT_MCUQ_MAX; i++) - mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[i], true); - - mt76_for_each_q_rx(&dev->mt76, i) - mt76_queue_rx_reset(dev, i); - - mt76_tx_status_check(&dev->mt76, true); - - /* re-init prefetch settings after reset */ - mt7915_dma_prefetch(dev); - - mt76_set(dev, MT_WFDMA0_GLO_CFG, - MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN); - if (is_mt7915(&dev->mt76)) - mt76_set(dev, MT_WFDMA1_GLO_CFG, - MT_WFDMA1_GLO_CFG_TX_DMA_EN | - MT_WFDMA1_GLO_CFG_RX_DMA_EN | - MT_WFDMA1_GLO_CFG_OMIT_TX_INFO | - MT_WFDMA1_GLO_CFG_OMIT_RX_INFO); - if (dev->hif2) { - mt76_set(dev, MT_WFDMA0_GLO_CFG + hif1_ofs, - MT_WFDMA0_GLO_CFG_TX_DMA_EN | - MT_WFDMA0_GLO_CFG_RX_DMA_EN); - - if (is_mt7915(&dev->mt76)) - mt76_set(dev, MT_WFDMA1_GLO_CFG + hif1_ofs, - MT_WFDMA1_GLO_CFG_TX_DMA_EN | - MT_WFDMA1_GLO_CFG_RX_DMA_EN | - MT_WFDMA1_GLO_CFG_OMIT_TX_INFO | - MT_WFDMA1_GLO_CFG_OMIT_RX_INFO); - } -} - void mt7915_tx_token_put(struct mt7915_dev *dev) { struct mt76_txwi_cache *txwi; @@ -1450,7 +1383,7 @@ void mt7915_mac_reset_work(struct work_struct *work) mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_STOPPED); if (mt7915_wait_reset_state(dev, MT_MCU_CMD_RESET_DONE)) { - mt7915_dma_reset(dev); + mt7915_dma_reset(dev, false); mt7915_tx_token_put(dev); idr_init(&dev->mt76.token); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index 98c13b6c47d4..ecbab762b6ed 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -447,6 +447,7 @@ s8 mt7915_eeprom_get_power_delta(struct mt7915_dev *dev, int band); int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2); void mt7915_dma_prefetch(struct mt7915_dev *dev); void mt7915_dma_cleanup(struct mt7915_dev *dev); +int mt7915_dma_reset(struct mt7915_dev *dev, bool force); int mt7915_mcu_init(struct mt7915_dev *dev); int mt7915_mcu_twt_agrt_update(struct mt7915_dev *dev, struct mt7915_vif *mvif, From 8a55712d124fd8a919e8a69b70643e1a97280b4b Mon Sep 17 00:00:00 2001 From: Bo Jiao Date: Thu, 10 Nov 2022 04:36:32 +0800 Subject: [PATCH 093/144] wifi: mt76: mt7915: enable full system reset support Add mt7915_reset() and refactor mt7915_mac_reset_work() to support full system recovery. Co-developed-by: Ryder Lee Signed-off-by: Ryder Lee Signed-off-by: Bo Jiao Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/init.c | 11 +- .../net/wireless/mediatek/mt76/mt7915/mac.c | 211 +++++++++++++++++- .../net/wireless/mediatek/mt76/mt7915/main.c | 18 +- .../net/wireless/mediatek/mt76/mt7915/mcu.c | 24 +- .../net/wireless/mediatek/mt76/mt7915/mmio.c | 7 +- .../wireless/mediatek/mt76/mt7915/mt7915.h | 15 +- .../net/wireless/mediatek/mt76/mt7915/regs.h | 4 + 7 files changed, 264 insertions(+), 26 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c index a042e0154211..51593f380f84 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c @@ -262,9 +262,8 @@ static void mt7915_led_set_brightness(struct led_classdev *led_cdev, mt7915_led_set_config(led_cdev, 0xff, 0); } -static void -mt7915_init_txpower(struct mt7915_dev *dev, - struct ieee80211_supported_band *sband) +void mt7915_init_txpower(struct mt7915_dev *dev, + struct ieee80211_supported_band *sband) { int i, n_chains = hweight8(dev->mphy.antenna_mask); int nss_delta = mt76_tx_power_nss_delta(n_chains); @@ -470,7 +469,7 @@ mt7915_mac_init_band(struct mt7915_dev *dev, u8 band) mt76_rmw(dev, MT_WTBLOFF_TOP_RSCR(band), mask, set); } -static void mt7915_mac_init(struct mt7915_dev *dev) +void mt7915_mac_init(struct mt7915_dev *dev) { int i; u32 rx_len = is_mt7915(&dev->mt76) ? 0x400 : 0x680; @@ -500,7 +499,7 @@ static void mt7915_mac_init(struct mt7915_dev *dev) } } -static int mt7915_txbf_init(struct mt7915_dev *dev) +int mt7915_txbf_init(struct mt7915_dev *dev) { int ret; @@ -1141,6 +1140,8 @@ int mt7915_register_device(struct mt7915_dev *dev) goto unreg_thermal; } + dev->recovery.hw_init_done = true; + mt7915_init_debugfs(&dev->phy); return 0; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 662671621cf0..383b260eb0aa 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -1291,7 +1291,7 @@ mt7915_wait_reset_state(struct mt7915_dev *dev, u32 state) bool ret; ret = wait_event_timeout(dev->reset_wait, - (READ_ONCE(dev->reset_state) & state), + (READ_ONCE(dev->recovery.state) & state), MT7915_RESET_TIMEOUT); WARN(!ret, "Timeout waiting for MCU reset state %x\n", state); @@ -1346,6 +1346,168 @@ void mt7915_tx_token_put(struct mt7915_dev *dev) idr_destroy(&dev->mt76.token); } +static int +mt7915_mac_restart(struct mt7915_dev *dev) +{ + struct mt7915_phy *phy2; + struct mt76_phy *ext_phy; + struct mt76_dev *mdev = &dev->mt76; + int i, ret; + + ext_phy = dev->mt76.phys[MT_BAND1]; + phy2 = ext_phy ? ext_phy->priv : NULL; + + if (dev->hif2) { + mt76_wr(dev, MT_INT1_MASK_CSR, 0x0); + mt76_wr(dev, MT_INT1_SOURCE_CSR, ~0); + } + + if (dev_is_pci(mdev->dev)) { + mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0x0); + if (dev->hif2) + mt76_wr(dev, MT_PCIE1_MAC_INT_ENABLE, 0x0); + } + + set_bit(MT76_RESET, &dev->mphy.state); + set_bit(MT76_MCU_RESET, &dev->mphy.state); + wake_up(&dev->mt76.mcu.wait); + if (ext_phy) { + set_bit(MT76_RESET, &ext_phy->state); + set_bit(MT76_MCU_RESET, &ext_phy->state); + } + + /* lock/unlock all queues to ensure that no tx is pending */ + mt76_txq_schedule_all(&dev->mphy); + if (ext_phy) + mt76_txq_schedule_all(ext_phy); + + /* disable all tx/rx napi */ + mt76_worker_disable(&dev->mt76.tx_worker); + mt76_for_each_q_rx(mdev, i) { + if (mdev->q_rx[i].ndesc) + napi_disable(&dev->mt76.napi[i]); + } + napi_disable(&dev->mt76.tx_napi); + + /* token reinit */ + mt7915_tx_token_put(dev); + idr_init(&dev->mt76.token); + + mt7915_dma_reset(dev, true); + + local_bh_disable(); + mt76_for_each_q_rx(mdev, i) { + if (mdev->q_rx[i].ndesc) { + napi_enable(&dev->mt76.napi[i]); + napi_schedule(&dev->mt76.napi[i]); + } + } + local_bh_enable(); + clear_bit(MT76_MCU_RESET, &dev->mphy.state); + clear_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); + + mt76_wr(dev, MT_INT_MASK_CSR, dev->mt76.mmio.irqmask); + mt76_wr(dev, MT_INT_SOURCE_CSR, ~0); + + if (dev->hif2) { + mt76_wr(dev, MT_INT1_MASK_CSR, dev->mt76.mmio.irqmask); + mt76_wr(dev, MT_INT1_SOURCE_CSR, ~0); + } + if (dev_is_pci(mdev->dev)) { + mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); + if (dev->hif2) + mt76_wr(dev, MT_PCIE1_MAC_INT_ENABLE, 0xff); + } + + /* load firmware */ + ret = mt7915_mcu_init_firmware(dev); + if (ret) + goto out; + + /* set the necessary init items */ + ret = mt7915_mcu_set_eeprom(dev); + if (ret) + goto out; + + mt7915_mac_init(dev); + mt7915_init_txpower(dev, &dev->mphy.sband_2g.sband); + mt7915_init_txpower(dev, &dev->mphy.sband_5g.sband); + ret = mt7915_txbf_init(dev); + + if (test_bit(MT76_STATE_RUNNING, &dev->mphy.state)) { + ret = mt7915_run(dev->mphy.hw); + if (ret) + goto out; + } + + if (ext_phy && test_bit(MT76_STATE_RUNNING, &ext_phy->state)) { + ret = mt7915_run(ext_phy->hw); + if (ret) + goto out; + } + +out: + /* reset done */ + clear_bit(MT76_RESET, &dev->mphy.state); + if (phy2) + clear_bit(MT76_RESET, &phy2->mt76->state); + + local_bh_disable(); + napi_enable(&dev->mt76.tx_napi); + napi_schedule(&dev->mt76.tx_napi); + local_bh_enable(); + + mt76_worker_enable(&dev->mt76.tx_worker); + + return ret; +} + +static void +mt7915_mac_full_reset(struct mt7915_dev *dev) +{ + struct mt76_phy *ext_phy; + int i; + + ext_phy = dev->mt76.phys[MT_BAND1]; + + dev->recovery.hw_full_reset = true; + + wake_up(&dev->mt76.mcu.wait); + ieee80211_stop_queues(mt76_hw(dev)); + if (ext_phy) + ieee80211_stop_queues(ext_phy->hw); + + cancel_delayed_work_sync(&dev->mphy.mac_work); + if (ext_phy) + cancel_delayed_work_sync(&ext_phy->mac_work); + + mutex_lock(&dev->mt76.mutex); + for (i = 0; i < 10; i++) { + if (!mt7915_mac_restart(dev)) + break; + } + mutex_unlock(&dev->mt76.mutex); + + if (i == 10) + dev_err(dev->mt76.dev, "chip full reset failed\n"); + + ieee80211_restart_hw(mt76_hw(dev)); + if (ext_phy) + ieee80211_restart_hw(ext_phy->hw); + + ieee80211_wake_queues(mt76_hw(dev)); + if (ext_phy) + ieee80211_wake_queues(ext_phy->hw); + + dev->recovery.hw_full_reset = false; + ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mphy.mac_work, + MT7915_WATCHDOG_TIME); + if (ext_phy) + ieee80211_queue_delayed_work(ext_phy->hw, + &ext_phy->mac_work, + MT7915_WATCHDOG_TIME); +} + /* system error recovery */ void mt7915_mac_reset_work(struct work_struct *work) { @@ -1358,7 +1520,28 @@ void mt7915_mac_reset_work(struct work_struct *work) ext_phy = dev->mt76.phys[MT_BAND1]; phy2 = ext_phy ? ext_phy->priv : NULL; - if (!(READ_ONCE(dev->reset_state) & MT_MCU_CMD_STOP_DMA)) + /* chip full reset */ + if (dev->recovery.restart) { + /* disable WA/WM WDT */ + mt76_clear(dev, MT_WFDMA0_MCU_HOST_INT_ENA, + MT_MCU_CMD_WDT_MASK); + + mt7915_mac_full_reset(dev); + + /* enable mcu irq */ + mt7915_irq_enable(dev, MT_INT_MCU_CMD); + mt7915_irq_disable(dev, 0); + + /* enable WA/WM WDT */ + mt76_set(dev, MT_WFDMA0_MCU_HOST_INT_ENA, MT_MCU_CMD_WDT_MASK); + + dev->recovery.state = MT_MCU_CMD_NORMAL_STATE; + dev->recovery.restart = false; + return; + } + + /* chip partial reset */ + if (!(READ_ONCE(dev->recovery.state) & MT_MCU_CMD_STOP_DMA)) return; ieee80211_stop_queues(mt76_hw(dev)); @@ -1432,6 +1615,30 @@ void mt7915_mac_reset_work(struct work_struct *work) MT7915_WATCHDOG_TIME); } +void mt7915_reset(struct mt7915_dev *dev) +{ + if (!dev->recovery.hw_init_done) + return; + + if (dev->recovery.hw_full_reset) + return; + + /* wm/wa exception: do full recovery */ + if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WDT_MASK) { + dev->recovery.restart = true; + dev_info(dev->mt76.dev, + "%s indicated firmware crash, attempting recovery\n", + wiphy_name(dev->mt76.hw->wiphy)); + + mt7915_irq_disable(dev, MT_INT_MCU_CMD); + queue_work(dev->mt76.wq, &dev->reset_work); + return; + } + + queue_work(dev->mt76.wq, &dev->reset_work); + wake_up(&dev->reset_wait); +} + void mt7915_mac_update_stats(struct mt7915_phy *phy) { struct mt7915_dev *dev = phy->dev; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 820c7132bf3f..3933f4f2d71d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -20,17 +20,13 @@ static bool mt7915_dev_running(struct mt7915_dev *dev) return phy && test_bit(MT76_STATE_RUNNING, &phy->mt76->state); } -static int mt7915_start(struct ieee80211_hw *hw) +int mt7915_run(struct ieee80211_hw *hw) { struct mt7915_dev *dev = mt7915_hw_dev(hw); struct mt7915_phy *phy = mt7915_hw_phy(hw); bool running; int ret; - flush_work(&dev->init_work); - - mutex_lock(&dev->mt76.mutex); - running = mt7915_dev_running(dev); if (!running) { @@ -80,6 +76,18 @@ static int mt7915_start(struct ieee80211_hw *hw) mt7915_mac_reset_counters(phy); out: + return ret; +} + +static int mt7915_start(struct ieee80211_hw *hw) +{ + struct mt7915_dev *dev = mt7915_hw_dev(hw); + int ret; + + flush_work(&dev->init_work); + + mutex_lock(&dev->mt76.mutex); + ret = mt7915_run(hw); mutex_unlock(&dev->mt76.mutex); return ret; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 4000fcd7132a..17e13fd0ede9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -2249,18 +2249,10 @@ mt7915_mcu_init_rx_airtime(struct mt7915_dev *dev) sizeof(req), true); } -int mt7915_mcu_init(struct mt7915_dev *dev) +int mt7915_mcu_init_firmware(struct mt7915_dev *dev) { - static const struct mt76_mcu_ops mt7915_mcu_ops = { - .headroom = sizeof(struct mt76_connac2_mcu_txd), - .mcu_skb_send_msg = mt7915_mcu_send_message, - .mcu_parse_response = mt7915_mcu_parse_response, - .mcu_restart = mt76_connac_mcu_restart, - }; int ret; - dev->mt76.mcu_ops = &mt7915_mcu_ops; - /* force firmware operation mode into normal state, * which should be set before firmware download stage. */ @@ -2309,6 +2301,20 @@ int mt7915_mcu_init(struct mt7915_dev *dev) MCU_WA_PARAM_RED, 0, 0); } +int mt7915_mcu_init(struct mt7915_dev *dev) +{ + static const struct mt76_mcu_ops mt7915_mcu_ops = { + .headroom = sizeof(struct mt76_connac2_mcu_txd), + .mcu_skb_send_msg = mt7915_mcu_send_message, + .mcu_parse_response = mt7915_mcu_parse_response, + .mcu_restart = mt76_connac_mcu_restart, + }; + + dev->mt76.mcu_ops = &mt7915_mcu_ops; + + return mt7915_mcu_init_firmware(dev); +} + void mt7915_mcu_exit(struct mt7915_dev *dev) { __mt76_mcu_restart(&dev->mt76); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c index be1b8ea711c7..032af46289af 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -746,10 +746,9 @@ static void mt7915_irq_tasklet(struct tasklet_struct *t) u32 val = mt76_rr(dev, MT_MCU_CMD); mt76_wr(dev, MT_MCU_CMD, val); - if (val & MT_MCU_CMD_ERROR_MASK) { - dev->reset_state = val; - queue_work(dev->mt76.wq, &dev->reset_work); - wake_up(&dev->reset_wait); + if (val & (MT_MCU_CMD_ERROR_MASK | MT_MCU_CMD_WDT_MASK)) { + dev->recovery.state = val; + mt7915_reset(dev); } } } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index ecbab762b6ed..fce1fde72116 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -307,7 +307,13 @@ struct mt7915_dev { struct work_struct rc_work; struct work_struct reset_work; wait_queue_head_t reset_wait; - u32 reset_state; + + struct { + u32 state; + bool hw_full_reset:1; + bool hw_init_done:1; + bool restart:1; + } recovery; struct list_head sta_rc_list; struct list_head sta_poll_list; @@ -448,7 +454,13 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2); void mt7915_dma_prefetch(struct mt7915_dev *dev); void mt7915_dma_cleanup(struct mt7915_dev *dev); int mt7915_dma_reset(struct mt7915_dev *dev, bool force); +int mt7915_txbf_init(struct mt7915_dev *dev); +void mt7915_init_txpower(struct mt7915_dev *dev, + struct ieee80211_supported_band *sband); +void mt7915_reset(struct mt7915_dev *dev); +int mt7915_run(struct ieee80211_hw *hw); int mt7915_mcu_init(struct mt7915_dev *dev); +int mt7915_mcu_init_firmware(struct mt7915_dev *dev); int mt7915_mcu_twt_agrt_update(struct mt7915_dev *dev, struct mt7915_vif *mvif, struct mt7915_twt_flow *flow, @@ -548,6 +560,7 @@ static inline void mt7915_irq_disable(struct mt7915_dev *dev, u32 mask) mt76_set_irq_mask(&dev->mt76, MT_INT_MASK_CSR, mask, 0); } +void mt7915_mac_init(struct mt7915_dev *dev); u32 mt7915_mac_wtbl_lmac_addr(struct mt7915_dev *dev, u16 wcid, u8 dw); bool mt7915_mac_wtbl_update(struct mt7915_dev *dev, int idx, u32 mask); void mt7915_mac_reset_counters(struct mt7915_phy *phy); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h index 9924271d8e36..16214cee5d93 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h @@ -563,6 +563,8 @@ enum offs_rev { #define MT_WFDMA0_BUSY_ENA_TX_FIFO1 BIT(1) #define MT_WFDMA0_BUSY_ENA_RX_FIFO BIT(2) +#define MT_WFDMA0_MCU_HOST_INT_ENA MT_WFDMA0(0x1f4) + #define MT_WFDMA0_GLO_CFG MT_WFDMA0(0x208) #define MT_WFDMA0_GLO_CFG_TX_DMA_EN BIT(0) #define MT_WFDMA0_GLO_CFG_RX_DMA_EN BIT(2) @@ -731,6 +733,8 @@ enum offs_rev { #define MT_MCU_CMD_NORMAL_STATE BIT(5) #define MT_MCU_CMD_ERROR_MASK GENMASK(5, 1) +#define MT_MCU_CMD_WDT_MASK GENMASK(31, 30) + /* TOP RGU */ #define MT_TOP_RGU_BASE 0x18000000 #define MT_TOP_PWR_CTRL (MT_TOP_RGU_BASE + (0x0)) From b662b71ac3cccb50e9a45aae194591fc50e433ce Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Thu, 10 Nov 2022 04:36:33 +0800 Subject: [PATCH 094/144] wifi: mt76: mt7915: add full system reset into debugfs Trigger firmware crash and enable full system recovery through debugfs. This also renames knob "fw_ser" to a clear-cut name "sys_recovery". Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt7915/debugfs.c | 81 +++++++++++++++---- .../net/wireless/mediatek/mt76/mt7915/mac.c | 5 ++ .../net/wireless/mediatek/mt76/mt7915/mcu.h | 2 + .../wireless/mediatek/mt76/mt7915/mt7915.h | 2 + .../net/wireless/mediatek/mt76/mt7915/regs.h | 4 + 5 files changed, 80 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c index cd774c1bc49f..096cb8a4db3d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c @@ -46,8 +46,8 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_implicit_txbf, mt7915_implicit_txbf_get, /* test knob of system error recovery */ static ssize_t -mt7915_fw_ser_set(struct file *file, const char __user *user_buf, - size_t count, loff_t *ppos) +mt7915_sys_recovery_set(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) { struct mt7915_phy *phy = file->private_data; struct mt7915_dev *dev = phy->dev; @@ -71,8 +71,18 @@ mt7915_fw_ser_set(struct file *file, const char __user *user_buf, return -EINVAL; switch (val) { + /* + * 0: grab firmware current SER state. + * 1: trigger & enable system error L1 recovery. + * 2: trigger & enable system error L2 recovery. + * 3: trigger & enable system error L3 rx abort. + * 4: trigger & enable system error L3 tx abort + * 5: trigger & enable system error L3 tx disable. + * 6: trigger & enable system error L3 bf recovery. + * 7: trigger & enable system error full recovery. + * 8: trigger firmware crash. + */ case SER_QUERY: - /* grab firmware SER stats */ ret = mt7915_mcu_set_ser(dev, 0, 0, ext_phy); break; case SER_SET_RECOVER_L1: @@ -87,6 +97,23 @@ mt7915_fw_ser_set(struct file *file, const char __user *user_buf, ret = mt7915_mcu_set_ser(dev, SER_RECOVER, val, ext_phy); break; + + /* enable full chip reset */ + case SER_SET_RECOVER_FULL: + mt76_set(dev, MT_WFDMA0_MCU_HOST_INT_ENA, MT_MCU_CMD_WDT_MASK); + ret = mt7915_mcu_set_ser(dev, 1, 3, ext_phy); + if (ret) + return ret; + + dev->recovery.state |= MT_MCU_CMD_WDT_MASK; + mt7915_reset(dev); + break; + + /* WARNING: trigger firmware crash */ + case SER_SET_SYSTEM_ASSERT: + mt76_wr(dev, MT_MCU_WM_CIRQ_EINT_MASK_CLR_ADDR, BIT(18)); + mt76_wr(dev, MT_MCU_WM_CIRQ_EINT_SOFT_ADDR, BIT(18)); + break; default: break; } @@ -95,20 +122,45 @@ mt7915_fw_ser_set(struct file *file, const char __user *user_buf, } static ssize_t -mt7915_fw_ser_get(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) +mt7915_sys_recovery_get(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) { struct mt7915_phy *phy = file->private_data; struct mt7915_dev *dev = phy->dev; char *buff; int desc = 0; ssize_t ret; - static const size_t bufsz = 400; + static const size_t bufsz = 1024; buff = kmalloc(bufsz, GFP_KERNEL); if (!buff) return -ENOMEM; + /* HELP */ + desc += scnprintf(buff + desc, bufsz - desc, + "Please echo the correct value ...\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "0: grab firmware transient SER state\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "1: trigger system error L1 recovery\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "2: trigger system error L2 recovery\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "3: trigger system error L3 rx abort\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "4: trigger system error L3 tx abort\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "5: trigger system error L3 tx disable\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "6: trigger system error L3 bf recovery\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "7: trigger system error full recovery\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "8: trigger firmware crash\n"); + + /* SER statistics */ + desc += scnprintf(buff + desc, bufsz - desc, + "\nlet's dump firmware SER statistics...\n"); desc += scnprintf(buff + desc, bufsz - desc, "::E R , SER_STATUS = 0x%08x\n", mt76_rr(dev, MT_SWDEF_SER_STATS)); @@ -139,15 +191,19 @@ mt7915_fw_ser_get(struct file *file, char __user *user_buf, desc += scnprintf(buff + desc, bufsz - desc, "::E R , SER_LMAC_WISR7_B1 = 0x%08x\n", mt76_rr(dev, MT_SWDEF_LAMC_WISR7_BN1_STATS)); + desc += scnprintf(buff + desc, bufsz - desc, + "\nSYS_RESET_COUNT: WM %d, WA %d\n", + dev->recovery.wm_reset_count, + dev->recovery.wa_reset_count); ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); kfree(buff); return ret; } -static const struct file_operations mt7915_fw_ser_ops = { - .write = mt7915_fw_ser_set, - .read = mt7915_fw_ser_get, +static const struct file_operations mt7915_sys_recovery_ops = { + .write = mt7915_sys_recovery_set, + .read = mt7915_sys_recovery_get, .open = simple_open, .llseek = default_llseek, }; @@ -598,10 +654,6 @@ mt7915_fw_util_wm_show(struct seq_file *file, void *data) struct mt7915_dev *dev = file->private; seq_printf(file, "Program counter: 0x%x\n", mt76_rr(dev, MT_WM_MCU_PC)); - seq_printf(file, "Exception state: 0x%x\n", - is_mt7915(&dev->mt76) ? - (u32)mt76_get_field(dev, MT_FW_EXCEPTION, GENMASK(15, 8)) : - (u32)mt76_get_field(dev, MT_FW_EXCEPTION, GENMASK(7, 0))); if (dev->fw.debug_wm) { seq_printf(file, "Busy: %u%% Peak busy: %u%%\n", @@ -1009,7 +1061,8 @@ int mt7915_init_debugfs(struct mt7915_phy *phy) debugfs_create_file("xmit-queues", 0400, dir, phy, &mt7915_xmit_queues_fops); debugfs_create_file("tx_stats", 0400, dir, phy, &mt7915_tx_stats_fops); - debugfs_create_file("fw_ser", 0600, dir, phy, &mt7915_fw_ser_ops); + debugfs_create_file("sys_recovery", 0600, dir, phy, + &mt7915_sys_recovery_ops); debugfs_create_file("fw_debug_wm", 0600, dir, dev, &fops_fw_debug_wm); debugfs_create_file("fw_debug_wa", 0600, dir, dev, &fops_fw_debug_wa); debugfs_create_file("fw_debug_bin", 0600, dir, dev, &fops_fw_debug_bin); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 383b260eb0aa..3ac143a9e804 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -1526,6 +1526,11 @@ void mt7915_mac_reset_work(struct work_struct *work) mt76_clear(dev, MT_WFDMA0_MCU_HOST_INT_ENA, MT_MCU_CMD_WDT_MASK); + if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WA_WDT) + dev->recovery.wa_reset_count++; + else + dev->recovery.wm_reset_count++; + mt7915_mac_full_reset(dev); /* enable mcu irq */ diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h index a4f903f88078..c19b5d66c0e1 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h @@ -450,6 +450,8 @@ enum { SER_SET_RECOVER_L3_TX_ABORT, SER_SET_RECOVER_L3_TX_DISABLE, SER_SET_RECOVER_L3_BF, + SER_SET_RECOVER_FULL, + SER_SET_SYSTEM_ASSERT, /* action */ SER_ENABLE = 2, SER_RECOVER diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index fce1fde72116..8615a38bec06 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -310,6 +310,8 @@ struct mt7915_dev { struct { u32 state; + u32 wa_reset_count; + u32 wm_reset_count; bool hw_full_reset:1; bool hw_init_done:1; bool restart:1; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h index 16214cee5d93..dc360d52f49b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h @@ -733,6 +733,8 @@ enum offs_rev { #define MT_MCU_CMD_NORMAL_STATE BIT(5) #define MT_MCU_CMD_ERROR_MASK GENMASK(5, 1) +#define MT_MCU_CMD_WA_WDT BIT(31) +#define MT_MCU_CMD_WM_WDT BIT(30) #define MT_MCU_CMD_WDT_MASK GENMASK(31, 30) /* TOP RGU */ @@ -1151,5 +1153,7 @@ enum offs_rev { #define MT_MCU_WM_CIRQ(ofs) (MT_MCU_WM_CIRQ_BASE + (ofs)) #define MT_MCU_WM_CIRQ_IRQ_MASK_CLR_ADDR MT_MCU_WM_CIRQ(0x80) #define MT_MCU_WM_CIRQ_IRQ_SOFT_ADDR MT_MCU_WM_CIRQ(0xc0) +#define MT_MCU_WM_CIRQ_EINT_MASK_CLR_ADDR MT_MCU_WM_CIRQ(0x108) +#define MT_MCU_WM_CIRQ_EINT_SOFT_ADDR MT_MCU_WM_CIRQ(0x118) #endif From 4dbcb9125cc3e10a6d879c10e4f5816d05a87c49 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Thu, 10 Nov 2022 04:36:34 +0800 Subject: [PATCH 095/144] wifi: mt76: mt7915: enable coredump support Host triggered and catastrophic event triggered firmware core dumping for basic firmware issues triage, including state reporting, task/irq info, function calltrace and MCU memory dump. Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/Kconfig | 1 + .../wireless/mediatek/mt76/mt7915/Makefile | 3 +- .../wireless/mediatek/mt76/mt7915/coredump.c | 410 ++++++++++++++++++ .../wireless/mediatek/mt76/mt7915/coredump.h | 136 ++++++ .../net/wireless/mediatek/mt76/mt7915/init.c | 12 +- .../net/wireless/mediatek/mt76/mt7915/mac.c | 73 +++- .../net/wireless/mediatek/mt76/mt7915/mmio.c | 59 ++- .../wireless/mediatek/mt76/mt7915/mt7915.h | 22 + .../net/wireless/mediatek/mt76/mt7915/regs.h | 34 +- 9 files changed, 742 insertions(+), 8 deletions(-) create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/coredump.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/coredump.h diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7915/Kconfig index f21282cea845..d710726d47bf 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/Kconfig +++ b/drivers/net/wireless/mediatek/mt76/mt7915/Kconfig @@ -2,6 +2,7 @@ config MT7915E tristate "MediaTek MT7915E (PCIe) support" select MT76_CONNAC_LIB + select WANT_DEV_COREDUMP depends on MAC80211 depends on PCI select RELAY diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/Makefile b/drivers/net/wireless/mediatek/mt76/mt7915/Makefile index b794ceb79c37..797ae49805c3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/Makefile +++ b/drivers/net/wireless/mediatek/mt76/mt7915/Makefile @@ -6,4 +6,5 @@ mt7915e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \ debugfs.o mmio.o mt7915e-$(CONFIG_NL80211_TESTMODE) += testmode.o -mt7915e-$(CONFIG_MT7986_WMAC) += soc.o \ No newline at end of file +mt7915e-$(CONFIG_MT7986_WMAC) += soc.o +mt7915e-$(CONFIG_DEV_COREDUMP) += coredump.o diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/coredump.c b/drivers/net/wireless/mediatek/mt76/mt7915/coredump.c new file mode 100644 index 000000000000..d097a56dd33d --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7915/coredump.c @@ -0,0 +1,410 @@ +// SPDX-License-Identifier: ISC +/* Copyright (C) 2022 MediaTek Inc. */ + +#include +#include +#include +#include +#include "coredump.h" + +static bool coredump_memdump; +module_param(coredump_memdump, bool, 0644); +MODULE_PARM_DESC(coredump_memdump, "Optional ability to dump firmware memory"); + +static const struct mt7915_mem_region mt7915_mem_regions[] = { + { + .start = 0xe003b400, + .len = 0x00003bff, + .name = "CRAM", + }, +}; + +static const struct mt7915_mem_region mt7916_mem_regions[] = { + { + .start = 0x00800000, + .len = 0x0005ffff, + .name = "ROM", + }, + { + .start = 0x00900000, + .len = 0x00013fff, + .name = "ULM1", + }, + { + .start = 0x02200000, + .len = 0x0004ffff, + .name = "ULM2", + }, + { + .start = 0x02300000, + .len = 0x0004ffff, + .name = "ULM3", + }, + { + .start = 0x00400000, + .len = 0x00027fff, + .name = "SRAM", + }, + { + .start = 0xe0000000, + .len = 0x00157fff, + .name = "CRAM", + }, +}; + +static const struct mt7915_mem_region mt7986_mem_regions[] = { + { + .start = 0x00800000, + .len = 0x0005ffff, + .name = "ROM", + }, + { + .start = 0x00900000, + .len = 0x0000ffff, + .name = "ULM1", + }, + { + .start = 0x02200000, + .len = 0x0004ffff, + .name = "ULM2", + }, + { + .start = 0x02300000, + .len = 0x0004ffff, + .name = "ULM3", + }, + { + .start = 0x00400000, + .len = 0x00017fff, + .name = "SRAM", + }, + { + .start = 0xe0000000, + .len = 0x00113fff, + .name = "CRAM", + }, +}; + +const struct mt7915_mem_region* +mt7915_coredump_get_mem_layout(struct mt7915_dev *dev, u32 *num) +{ + switch (mt76_chip(&dev->mt76)) { + case 0x7915: + *num = ARRAY_SIZE(mt7915_mem_regions); + return &mt7915_mem_regions[0]; + case 0x7986: + *num = ARRAY_SIZE(mt7986_mem_regions); + return &mt7986_mem_regions[0]; + case 0x7916: + *num = ARRAY_SIZE(mt7916_mem_regions); + return &mt7916_mem_regions[0]; + default: + return NULL; + } +} + +static int mt7915_coredump_get_mem_size(struct mt7915_dev *dev) +{ + const struct mt7915_mem_region *mem_region; + size_t size = 0; + u32 num; + int i; + + mem_region = mt7915_coredump_get_mem_layout(dev, &num); + if (!mem_region) + return 0; + + for (i = 0; i < num; i++) { + size += mem_region->len; + mem_region++; + } + + /* reserve space for the headers */ + size += num * sizeof(struct mt7915_mem_hdr); + /* make sure it is aligned 4 bytes for debug message print out */ + size = ALIGN(size, 4); + + return size; +} + +struct mt7915_crash_data *mt7915_coredump_new(struct mt7915_dev *dev) +{ + struct mt7915_crash_data *crash_data = dev->coredump.crash_data; + + lockdep_assert_held(&dev->dump_mutex); + + guid_gen(&crash_data->guid); + ktime_get_real_ts64(&crash_data->timestamp); + + return crash_data; +} + +static void +mt7915_coredump_fw_state(struct mt7915_dev *dev, struct mt7915_coredump *dump, + bool *exception) +{ + u32 state, count, type; + + type = (u32)mt76_get_field(dev, MT_FW_EXCEPT_TYPE, GENMASK(7, 0)); + state = (u32)mt76_get_field(dev, MT_FW_ASSERT_STAT, GENMASK(7, 0)); + count = is_mt7915(&dev->mt76) ? + (u32)mt76_get_field(dev, MT_FW_EXCEPT_COUNT, GENMASK(15, 8)) : + (u32)mt76_get_field(dev, MT_FW_EXCEPT_COUNT, GENMASK(7, 0)); + + /* normal mode: driver can manually trigger assert for detail info */ + if (!count) + strscpy(dump->fw_state, "normal", sizeof(dump->fw_state)); + else if (state > 1 && (count == 1) && type == 5) + strscpy(dump->fw_state, "assert", sizeof(dump->fw_state)); + else if ((state > 1 && count == 1) || count > 1) + strscpy(dump->fw_state, "exception", sizeof(dump->fw_state)); + + *exception = !!count; +} + +static void +mt7915_coredump_fw_trace(struct mt7915_dev *dev, struct mt7915_coredump *dump, + bool exception) +{ + u32 n, irq, sch, base = MT_FW_EINT_INFO; + + /* trap or run? */ + dump->last_msg_id = mt76_rr(dev, MT_FW_LAST_MSG_ID); + + n = is_mt7915(&dev->mt76) ? + (u32)mt76_get_field(dev, base, GENMASK(7, 0)) : + (u32)mt76_get_field(dev, base, GENMASK(15, 8)); + dump->eint_info_idx = n; + + irq = mt76_rr(dev, base + 0x8); + n = is_mt7915(&dev->mt76) ? + FIELD_GET(GENMASK(7, 0), irq) : FIELD_GET(GENMASK(23, 16), irq); + dump->irq_info_idx = n; + + sch = mt76_rr(dev, MT_FW_SCHED_INFO); + n = is_mt7915(&dev->mt76) ? + FIELD_GET(GENMASK(7, 0), sch) : FIELD_GET(GENMASK(15, 8), sch); + dump->sched_info_idx = n; + + if (exception) { + u32 i, y; + + /* sched trace */ + n = is_mt7915(&dev->mt76) ? + FIELD_GET(GENMASK(15, 8), sch) : FIELD_GET(GENMASK(7, 0), sch); + n = n > 60 ? 60 : n; + + strscpy(dump->trace_sched, "(sched_info) id, time", + sizeof(dump->trace_sched)); + + for (y = dump->sched_info_idx, i = 0; i < n; i++, y++) { + mt7915_memcpy_fromio(dev, dump->sched, base + 0xc + y * 12, + sizeof(dump->sched)); + y = y >= n ? 0 : y; + } + + /* irq trace */ + n = is_mt7915(&dev->mt76) ? + FIELD_GET(GENMASK(15, 8), irq) : FIELD_GET(GENMASK(7, 0), irq); + n = n > 60 ? 60 : n; + + strscpy(dump->trace_irq, "(irq_info) id, time", + sizeof(dump->trace_irq)); + + for (y = dump->irq_info_idx, i = 0; i < n; i++, y++) { + mt7915_memcpy_fromio(dev, dump->irq, base + 0x4 + y * 16, + sizeof(dump->irq)); + y = y >= n ? 0 : y; + } + } +} + +static void +mt7915_coredump_fw_stack(struct mt7915_dev *dev, struct mt7915_coredump *dump, + bool exception) +{ + u32 oldest, i, idx; + + /* stop call stack record */ + if (!exception) + mt76_clear(dev, 0x89050200, BIT(0)); + + oldest = (u32)mt76_get_field(dev, 0x89050200, GENMASK(20, 16)) + 2; + for (i = 0; i < 16; i++) { + idx = ((oldest + 2 * i + 1) % 32); + dump->call_stack[i] = mt76_rr(dev, 0x89050204 + idx * 4); + } + + /* start call stack record */ + if (!exception) + mt76_set(dev, 0x89050200, BIT(0)); +} + +static void +mt7915_coredump_fw_task(struct mt7915_dev *dev, struct mt7915_coredump *dump) +{ + u32 offs = is_mt7915(&dev->mt76) ? 0xe0 : 0x170; + + strscpy(dump->task_qid, "(task queue id) read, write", + sizeof(dump->task_qid)); + + dump->taskq[0].read = mt76_rr(dev, MT_FW_TASK_QID1); + dump->taskq[0].write = mt76_rr(dev, MT_FW_TASK_QID1 - 4); + dump->taskq[1].read = mt76_rr(dev, MT_FW_TASK_QID2); + dump->taskq[1].write = mt76_rr(dev, MT_FW_TASK_QID2 - 4); + + strscpy(dump->task_info, "(task stack) start, end, size", + sizeof(dump->task_info)); + + dump->taski[0].start = mt76_rr(dev, MT_FW_TASK_START); + dump->taski[0].end = mt76_rr(dev, MT_FW_TASK_END); + dump->taski[0].size = mt76_rr(dev, MT_FW_TASK_SIZE); + dump->taski[1].start = mt76_rr(dev, MT_FW_TASK_START + offs); + dump->taski[1].end = mt76_rr(dev, MT_FW_TASK_END + offs); + dump->taski[1].size = mt76_rr(dev, MT_FW_TASK_SIZE + offs); +} + +static void +mt7915_coredump_fw_context(struct mt7915_dev *dev, struct mt7915_coredump *dump) +{ + u32 count, idx, id; + + count = mt76_rr(dev, MT_FW_CIRQ_COUNT); + + /* current context */ + if (!count) { + strscpy(dump->fw_context, "(context) interrupt", + sizeof(dump->fw_context)); + + idx = is_mt7915(&dev->mt76) ? + (u32)mt76_get_field(dev, MT_FW_CIRQ_IDX, GENMASK(31, 16)) : + (u32)mt76_get_field(dev, MT_FW_CIRQ_IDX, GENMASK(15, 0)); + dump->context.idx = idx; + dump->context.handler = mt76_rr(dev, MT_FW_CIRQ_LISR); + } else { + idx = mt76_rr(dev, MT_FW_TASK_IDX); + id = mt76_rr(dev, MT_FW_TASK_ID); + + if (!id && idx == 3) { + strscpy(dump->fw_context, "(context) idle", + sizeof(dump->fw_context)); + } else if (id && idx != 3) { + strscpy(dump->fw_context, "(context) task", + sizeof(dump->fw_context)); + + dump->context.idx = idx; + dump->context.handler = id; + } + } +} + +static struct mt7915_coredump *mt7915_coredump_build(struct mt7915_dev *dev) +{ + struct mt7915_crash_data *crash_data = dev->coredump.crash_data; + struct mt7915_coredump *dump; + struct mt7915_coredump_mem *dump_mem; + size_t len, sofar = 0, hdr_len = sizeof(*dump); + unsigned char *buf; + bool exception; + + len = hdr_len; + + if (coredump_memdump && crash_data->memdump_buf_len) + len += sizeof(*dump_mem) + crash_data->memdump_buf_len; + + sofar += hdr_len; + + /* this is going to get big when we start dumping memory and such, + * so go ahead and use vmalloc. + */ + buf = vzalloc(len); + if (!buf) + return NULL; + + mutex_lock(&dev->dump_mutex); + + dump = (struct mt7915_coredump *)(buf); + dump->len = len; + + /* plain text */ + strscpy(dump->magic, "mt76-crash-dump", sizeof(dump->magic)); + strscpy(dump->kernel, init_utsname()->release, sizeof(dump->kernel)); + strscpy(dump->fw_ver, dev->mt76.hw->wiphy->fw_version, + sizeof(dump->fw_ver)); + + guid_copy(&dump->guid, &crash_data->guid); + dump->tv_sec = crash_data->timestamp.tv_sec; + dump->tv_nsec = crash_data->timestamp.tv_nsec; + dump->device_id = mt76_chip(&dev->mt76); + + mt7915_coredump_fw_state(dev, dump, &exception); + mt7915_coredump_fw_trace(dev, dump, exception); + mt7915_coredump_fw_task(dev, dump); + mt7915_coredump_fw_context(dev, dump); + mt7915_coredump_fw_stack(dev, dump, exception); + + /* gather memory content */ + dump_mem = (struct mt7915_coredump_mem *)(buf + sofar); + dump_mem->len = crash_data->memdump_buf_len; + if (coredump_memdump && crash_data->memdump_buf_len) + memcpy(dump_mem->data, crash_data->memdump_buf, + crash_data->memdump_buf_len); + + mutex_unlock(&dev->dump_mutex); + + return dump; +} + +int mt7915_coredump_submit(struct mt7915_dev *dev) +{ + struct mt7915_coredump *dump; + + dump = mt7915_coredump_build(dev); + if (!dump) { + dev_warn(dev->mt76.dev, "no crash dump data found\n"); + return -ENODATA; + } + + dev_coredumpv(dev->mt76.dev, dump, dump->len, GFP_KERNEL); + + return 0; +} + +int mt7915_coredump_register(struct mt7915_dev *dev) +{ + struct mt7915_crash_data *crash_data; + + crash_data = vzalloc(sizeof(*dev->coredump.crash_data)); + if (!crash_data) + return -ENOMEM; + + dev->coredump.crash_data = crash_data; + + if (coredump_memdump) { + crash_data->memdump_buf_len = mt7915_coredump_get_mem_size(dev); + if (!crash_data->memdump_buf_len) + /* no memory content */ + return 0; + + crash_data->memdump_buf = vzalloc(crash_data->memdump_buf_len); + if (!crash_data->memdump_buf) { + vfree(crash_data); + return -ENOMEM; + } + } + + return 0; +} + +void mt7915_coredump_unregister(struct mt7915_dev *dev) +{ + if (dev->coredump.crash_data->memdump_buf) { + vfree(dev->coredump.crash_data->memdump_buf); + dev->coredump.crash_data->memdump_buf = NULL; + dev->coredump.crash_data->memdump_buf_len = 0; + } + + vfree(dev->coredump.crash_data); + dev->coredump.crash_data = NULL; +} + diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/coredump.h b/drivers/net/wireless/mediatek/mt76/mt7915/coredump.h new file mode 100644 index 000000000000..709f8e9c795c --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7915/coredump.h @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: ISC */ +/* Copyright (C) 2022 MediaTek Inc. */ + +#ifndef _COREDUMP_H_ +#define _COREDUMP_H_ + +#include "mt7915.h" + +struct trace { + u32 id; + u32 timestamp; +}; + +struct mt7915_coredump { + char magic[16]; + + u32 len; + + guid_t guid; + + /* time-of-day stamp */ + u64 tv_sec; + /* time-of-day stamp, nano-seconds */ + u64 tv_nsec; + /* kernel version */ + char kernel[64]; + /* firmware version */ + char fw_ver[ETHTOOL_FWVERS_LEN]; + + u32 device_id; + + /* exception state */ + char fw_state[12]; + + u32 last_msg_id; + u32 eint_info_idx; + u32 irq_info_idx; + u32 sched_info_idx; + + /* schedule info */ + char trace_sched[32]; + struct { + struct trace t; + u32 pc; + } sched[60]; + + /* irq info */ + char trace_irq[32]; + struct trace irq[60]; + + /* task queue status */ + char task_qid[32]; + struct { + u32 read; + u32 write; + } taskq[2]; + + /* task stack info */ + char task_info[32]; + struct { + u32 start; + u32 end; + u32 size; + } taski[2]; + + /* firmware context */ + char fw_context[24]; + struct { + u32 idx; + u32 handler; + } context; + + /* link registers calltrace */ + u32 call_stack[16]; + + /* memory content */ + u8 data[]; +} __packed; + +struct mt7915_coredump_mem { + u32 len; + u8 data[]; +} __packed; + +struct mt7915_mem_hdr { + u32 start; + u32 len; + u8 data[]; +}; + +struct mt7915_mem_region { + u32 start; + size_t len; + + const char *name; +}; + +#ifdef CONFIG_DEV_COREDUMP + +const struct mt7915_mem_region * +mt7915_coredump_get_mem_layout(struct mt7915_dev *dev, u32 *num); +struct mt7915_crash_data *mt7915_coredump_new(struct mt7915_dev *dev); +int mt7915_coredump_submit(struct mt7915_dev *dev); +int mt7915_coredump_register(struct mt7915_dev *dev); +void mt7915_coredump_unregister(struct mt7915_dev *dev); + +#else /* CONFIG_DEV_COREDUMP */ + +static inline const struct mt7915_mem_region * +mt7915_coredump_get_mem_layout(struct mt7915_dev *dev, u32 *num) +{ + return NULL; +} + +static inline int mt7915_coredump_submit(struct mt7915_dev *dev) +{ + return 0; +} + +static inline struct mt7915_crash_data *mt7915_coredump_new(struct mt7915_dev *dev) +{ + return NULL; +} + +static inline int mt7915_coredump_register(struct mt7915_dev *dev) +{ + return 0; +} + +static inline void mt7915_coredump_unregister(struct mt7915_dev *dev) +{ +} + +#endif /* CONFIG_DEV_COREDUMP */ + +#endif /* _COREDUMP_H_ */ diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c index 51593f380f84..e2e43a96416e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c @@ -8,6 +8,7 @@ #include "mt7915.h" #include "mac.h" #include "mcu.h" +#include "coredump.h" #include "eeprom.h" static const struct ieee80211_iface_limit if_limits[] = { @@ -1100,6 +1101,8 @@ int mt7915_register_device(struct mt7915_dev *dev) init_waitqueue_head(&dev->reset_wait); INIT_WORK(&dev->reset_work, mt7915_mac_reset_work); + INIT_WORK(&dev->dump_work, mt7915_mac_dump_work); + mutex_init(&dev->dump_mutex); dev->dbdc_support = mt7915_band_config(dev); @@ -1142,7 +1145,13 @@ int mt7915_register_device(struct mt7915_dev *dev) dev->recovery.hw_init_done = true; - mt7915_init_debugfs(&dev->phy); + ret = mt7915_init_debugfs(&dev->phy); + if (ret) + goto unreg_thermal; + + ret = mt7915_coredump_register(dev); + if (ret) + goto unreg_thermal; return 0; @@ -1161,6 +1170,7 @@ free_phy2: void mt7915_unregister_device(struct mt7915_dev *dev) { mt7915_unregister_ext_phy(dev); + mt7915_coredump_unregister(dev); mt7915_unregister_thermal(&dev->phy); mt76_unregister_device(&dev->mt76); mt7915_stop_hardware(dev); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 3ac143a9e804..117ddb00348c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -3,6 +3,7 @@ #include #include +#include "coredump.h" #include "mt7915.h" #include "../dma.h" #include "mac.h" @@ -1620,6 +1621,76 @@ void mt7915_mac_reset_work(struct work_struct *work) MT7915_WATCHDOG_TIME); } +/* firmware coredump */ +void mt7915_mac_dump_work(struct work_struct *work) +{ + const struct mt7915_mem_region *mem_region; + struct mt7915_crash_data *crash_data; + struct mt7915_dev *dev; + struct mt7915_mem_hdr *hdr; + size_t buf_len; + int i; + u32 num; + u8 *buf; + + dev = container_of(work, struct mt7915_dev, dump_work); + + mutex_lock(&dev->dump_mutex); + + crash_data = mt7915_coredump_new(dev); + if (!crash_data) { + mutex_unlock(&dev->dump_mutex); + goto skip_coredump; + } + + mem_region = mt7915_coredump_get_mem_layout(dev, &num); + if (!mem_region || !crash_data->memdump_buf_len) { + mutex_unlock(&dev->dump_mutex); + goto skip_memdump; + } + + buf = crash_data->memdump_buf; + buf_len = crash_data->memdump_buf_len; + + /* dumping memory content... */ + memset(buf, 0, buf_len); + for (i = 0; i < num; i++) { + if (mem_region->len > buf_len) { + dev_warn(dev->mt76.dev, "%s len %lu is too large\n", + mem_region->name, + (unsigned long)mem_region->len); + break; + } + + /* reserve space for the header */ + hdr = (void *)buf; + buf += sizeof(*hdr); + buf_len -= sizeof(*hdr); + + mt7915_memcpy_fromio(dev, buf, mem_region->start, + mem_region->len); + + hdr->start = mem_region->start; + hdr->len = mem_region->len; + + if (!mem_region->len) + /* note: the header remains, just with zero length */ + break; + + buf += mem_region->len; + buf_len -= mem_region->len; + + mem_region++; + } + + mutex_unlock(&dev->dump_mutex); + +skip_memdump: + mt7915_coredump_submit(dev); +skip_coredump: + queue_work(dev->mt76.wq, &dev->reset_work); +} + void mt7915_reset(struct mt7915_dev *dev) { if (!dev->recovery.hw_init_done) @@ -1636,7 +1707,7 @@ void mt7915_reset(struct mt7915_dev *dev) wiphy_name(dev->mt76.hw->wiphy)); mt7915_irq_disable(dev, MT_INT_MCU_CMD); - queue_work(dev->mt76.wq, &dev->reset_work); + queue_work(dev->mt76.wq, &dev->dump_work); return; } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c index 032af46289af..3c840853a2c9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -25,7 +25,22 @@ static const u32 mt7915_reg[] = { [WFDMA_EXT_CSR_ADDR] = 0xd7000, [CBTOP1_PHY_END] = 0x77ffffff, [INFRA_MCU_ADDR_END] = 0x7c3fffff, - [FW_EXCEPTION_ADDR] = 0x219848, + [FW_ASSERT_STAT_ADDR] = 0x219848, + [FW_EXCEPT_TYPE_ADDR] = 0x21987c, + [FW_EXCEPT_COUNT_ADDR] = 0x219848, + [FW_CIRQ_COUNT_ADDR] = 0x216f94, + [FW_CIRQ_IDX_ADDR] = 0x216ef8, + [FW_CIRQ_LISR_ADDR] = 0x2170ac, + [FW_TASK_ID_ADDR] = 0x216f90, + [FW_TASK_IDX_ADDR] = 0x216f9c, + [FW_TASK_QID1_ADDR] = 0x219680, + [FW_TASK_QID2_ADDR] = 0x219760, + [FW_TASK_START_ADDR] = 0x219558, + [FW_TASK_END_ADDR] = 0x219554, + [FW_TASK_SIZE_ADDR] = 0x219560, + [FW_LAST_MSG_ID_ADDR] = 0x216f70, + [FW_EINT_INFO_ADDR] = 0x219818, + [FW_SCHED_INFO_ADDR] = 0x219828, [SWDEF_BASE_ADDR] = 0x41f200, [TXQ_WED_RING_BASE] = 0xd7300, [RXQ_WED_RING_BASE] = 0xd7410, @@ -43,7 +58,22 @@ static const u32 mt7916_reg[] = { [WFDMA_EXT_CSR_ADDR] = 0xd7000, [CBTOP1_PHY_END] = 0x7fffffff, [INFRA_MCU_ADDR_END] = 0x7c085fff, - [FW_EXCEPTION_ADDR] = 0x022050bc, + [FW_ASSERT_STAT_ADDR] = 0x02204c14, + [FW_EXCEPT_TYPE_ADDR] = 0x022051a4, + [FW_EXCEPT_COUNT_ADDR] = 0x022050bc, + [FW_CIRQ_COUNT_ADDR] = 0x022001ac, + [FW_CIRQ_IDX_ADDR] = 0x02204f84, + [FW_CIRQ_LISR_ADDR] = 0x022050d0, + [FW_TASK_ID_ADDR] = 0x0220406c, + [FW_TASK_IDX_ADDR] = 0x0220500c, + [FW_TASK_QID1_ADDR] = 0x022028c8, + [FW_TASK_QID2_ADDR] = 0x02202a38, + [FW_TASK_START_ADDR] = 0x0220286c, + [FW_TASK_END_ADDR] = 0x02202870, + [FW_TASK_SIZE_ADDR] = 0x02202878, + [FW_LAST_MSG_ID_ADDR] = 0x02204fe8, + [FW_EINT_INFO_ADDR] = 0x0220525c, + [FW_SCHED_INFO_ADDR] = 0x0220516c, [SWDEF_BASE_ADDR] = 0x411400, [TXQ_WED_RING_BASE] = 0xd7300, [RXQ_WED_RING_BASE] = 0xd7410, @@ -61,7 +91,22 @@ static const u32 mt7986_reg[] = { [WFDMA_EXT_CSR_ADDR] = 0x27000, [CBTOP1_PHY_END] = 0x7fffffff, [INFRA_MCU_ADDR_END] = 0x7c085fff, - [FW_EXCEPTION_ADDR] = 0x02204ffc, + [FW_ASSERT_STAT_ADDR] = 0x02204b54, + [FW_EXCEPT_TYPE_ADDR] = 0x022050dc, + [FW_EXCEPT_COUNT_ADDR] = 0x02204ffc, + [FW_CIRQ_COUNT_ADDR] = 0x022001ac, + [FW_CIRQ_IDX_ADDR] = 0x02204ec4, + [FW_CIRQ_LISR_ADDR] = 0x02205010, + [FW_TASK_ID_ADDR] = 0x02204fac, + [FW_TASK_IDX_ADDR] = 0x02204f4c, + [FW_TASK_QID1_ADDR] = 0x02202814, + [FW_TASK_QID2_ADDR] = 0x02202984, + [FW_TASK_START_ADDR] = 0x022027b8, + [FW_TASK_END_ADDR] = 0x022027bc, + [FW_TASK_SIZE_ADDR] = 0x022027c4, + [FW_LAST_MSG_ID_ADDR] = 0x02204f28, + [FW_EINT_INFO_ADDR] = 0x02205194, + [FW_SCHED_INFO_ADDR] = 0x022051a4, [SWDEF_BASE_ADDR] = 0x411400, [TXQ_WED_RING_BASE] = 0x24420, [RXQ_WED_RING_BASE] = 0x24520, @@ -457,6 +502,14 @@ static u32 __mt7915_reg_addr(struct mt7915_dev *dev, u32 addr) return mt7915_reg_map_l2(dev, addr); } +void mt7915_memcpy_fromio(struct mt7915_dev *dev, void *buf, u32 offset, + size_t len) +{ + u32 addr = __mt7915_reg_addr(dev, offset); + + memcpy_fromio(buf, dev->mt76.mmio.regs + addr, len); +} + static u32 mt7915_rr(struct mt76_dev *mdev, u32 offset) { struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index 8615a38bec06..9cb680e7f223 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -224,6 +224,15 @@ struct mib_stats { u32 tx_amsdu_cnt; }; +/* crash-dump */ +struct mt7915_crash_data { + guid_t guid; + struct timespec64 timestamp; + + u8 *memdump_buf; + size_t memdump_buf_len; +}; + struct mt7915_hif { struct list_head list; @@ -305,6 +314,7 @@ struct mt7915_dev { struct work_struct init_work; struct work_struct rc_work; + struct work_struct dump_work; struct work_struct reset_work; wait_queue_head_t reset_wait; @@ -317,6 +327,14 @@ struct mt7915_dev { bool restart:1; } recovery; + /* protects coredump data */ + struct mutex dump_mutex; +#ifdef CONFIG_DEV_COREDUMP + struct { + struct mt7915_crash_data *crash_data; + } coredump; +#endif + struct list_head sta_rc_list; struct list_head sta_poll_list; struct list_head twt_list; @@ -562,6 +580,9 @@ static inline void mt7915_irq_disable(struct mt7915_dev *dev, u32 mask) mt76_set_irq_mask(&dev->mt76, MT_INT_MASK_CSR, mask, 0); } +void mt7915_memcpy_fromio(struct mt7915_dev *dev, void *buf, u32 offset, + size_t len); + void mt7915_mac_init(struct mt7915_dev *dev); u32 mt7915_mac_wtbl_lmac_addr(struct mt7915_dev *dev, u16 wcid, u8 dw); bool mt7915_mac_wtbl_update(struct mt7915_dev *dev, int idx, u32 mask); @@ -581,6 +602,7 @@ void mt7915_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, struct ieee80211_sta *sta); void mt7915_mac_work(struct work_struct *work); void mt7915_mac_reset_work(struct work_struct *work); +void mt7915_mac_dump_work(struct work_struct *work); void mt7915_mac_sta_rc_work(struct work_struct *work); void mt7915_mac_update_stats(struct mt7915_phy *phy); void mt7915_mac_twt_teardown_flow(struct mt7915_dev *dev, diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h index dc360d52f49b..0c61f1256f3b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h @@ -24,7 +24,22 @@ enum reg_rev { WFDMA_EXT_CSR_ADDR, CBTOP1_PHY_END, INFRA_MCU_ADDR_END, - FW_EXCEPTION_ADDR, + FW_ASSERT_STAT_ADDR, + FW_EXCEPT_TYPE_ADDR, + FW_EXCEPT_COUNT_ADDR, + FW_CIRQ_COUNT_ADDR, + FW_CIRQ_IDX_ADDR, + FW_CIRQ_LISR_ADDR, + FW_TASK_ID_ADDR, + FW_TASK_IDX_ADDR, + FW_TASK_QID1_ADDR, + FW_TASK_QID2_ADDR, + FW_TASK_START_ADDR, + FW_TASK_END_ADDR, + FW_TASK_SIZE_ADDR, + FW_LAST_MSG_ID_ADDR, + FW_EINT_INFO_ADDR, + FW_SCHED_INFO_ADDR, SWDEF_BASE_ADDR, TXQ_WED_RING_BASE, RXQ_WED_RING_BASE, @@ -979,7 +994,22 @@ enum offs_rev { #define MT_ADIE_TYPE_MASK BIT(1) /* FW MODE SYNC */ -#define MT_FW_EXCEPTION __REG(FW_EXCEPTION_ADDR) +#define MT_FW_ASSERT_STAT __REG(FW_ASSERT_STAT_ADDR) +#define MT_FW_EXCEPT_TYPE __REG(FW_EXCEPT_TYPE_ADDR) +#define MT_FW_EXCEPT_COUNT __REG(FW_EXCEPT_COUNT_ADDR) +#define MT_FW_CIRQ_COUNT __REG(FW_CIRQ_COUNT_ADDR) +#define MT_FW_CIRQ_IDX __REG(FW_CIRQ_IDX_ADDR) +#define MT_FW_CIRQ_LISR __REG(FW_CIRQ_LISR_ADDR) +#define MT_FW_TASK_ID __REG(FW_TASK_ID_ADDR) +#define MT_FW_TASK_IDX __REG(FW_TASK_IDX_ADDR) +#define MT_FW_TASK_QID1 __REG(FW_TASK_QID1_ADDR) +#define MT_FW_TASK_QID2 __REG(FW_TASK_QID2_ADDR) +#define MT_FW_TASK_START __REG(FW_TASK_START_ADDR) +#define MT_FW_TASK_END __REG(FW_TASK_END_ADDR) +#define MT_FW_TASK_SIZE __REG(FW_TASK_SIZE_ADDR) +#define MT_FW_LAST_MSG_ID __REG(FW_LAST_MSG_ID_ADDR) +#define MT_FW_EINT_INFO __REG(FW_EINT_INFO_ADDR) +#define MT_FW_SCHED_INFO __REG(FW_SCHED_INFO_ADDR) #define MT_SWDEF_BASE __REG(SWDEF_BASE_ADDR) From de147cc28985a2a09e5d6d179fc5ef59b22fc058 Mon Sep 17 00:00:00 2001 From: Nicolas Cavallari Date: Thu, 10 Nov 2022 16:39:51 +0100 Subject: [PATCH 096/144] wifi: mt76: mt7915: Fix chainmask calculation on mt7915 DBDC mt7915 does not have a per-band number of chains unlike the other chips, it only has a total number of chains. Yet the current code would consider the total number as a per-band number. For example, it would report that a 2x2 + 2x2 DBDC card have 4 chains on each band and set chainmask to 0b1111 for the first interface and 0b11110000 for the second. Fixes: 99ad32a4ca3a ("mt76: mt7915: add support for MT7986") Co-developed-by: Felix Fietkau Signed-off-by: Nicolas Cavallari Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c index 83bced0c0785..0bce0ce51be0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c @@ -194,6 +194,7 @@ void mt7915_eeprom_parse_hw_cap(struct mt7915_dev *dev, nss = path; if (dev->dbdc_support) { if (is_mt7915(&dev->mt76)) { + path = min_t(u8, path, 2); nss = FIELD_GET(MT_EE_WIFI_CONF3_TX_PATH_B0, eeprom[MT_EE_WIFI_CONF + 3]); if (phy->band_idx) From d44f5bca5166b5de65580c5854c3255e5980f6ad Mon Sep 17 00:00:00 2001 From: Nicolas Cavallari Date: Thu, 10 Nov 2022 16:39:52 +0100 Subject: [PATCH 097/144] wifi: mt76: mt7915: Fix VHT beamforming capabilities with DBDC Do not hardcode beamformee 4 space-time streams support, because DBDC can only support up to 2. Fixes: 00b2e16e0063 ("mt76: mt7915: add TxBF capabilities") Signed-off-by: Nicolas Cavallari Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/init.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c index e2e43a96416e..dbda7496bb5e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c @@ -735,7 +735,8 @@ void mt7915_set_stream_vht_txbf_caps(struct mt7915_phy *phy) *cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE | - (3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT); + FIELD_PREP(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK, + sts - 1); *cap &= ~(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK | IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | From 510ef2269428f87146ad5076229994d8e05b8193 Mon Sep 17 00:00:00 2001 From: Nicolas Cavallari Date: Thu, 10 Nov 2022 16:39:53 +0100 Subject: [PATCH 098/144] wifi: mt76: mt7915: don't claim 160MHz support with mt7915 DBDC The hardware simply does not support this in DBDC mode. Remove it from the HE capabilities. Signed-off-by: Nicolas Cavallari Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/init.c | 47 ++++++++++++------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c index dbda7496bb5e..b48c2ba9273d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c @@ -761,8 +761,12 @@ mt7915_set_stream_he_txbf_caps(struct mt7915_phy *phy, u8 c, sts_160 = sts; /* Can do 1/2 of STS in 160Mhz mode for mt7915 */ - if (is_mt7915(&dev->mt76) && !dev->dbdc_support) - sts_160 /= 2; + if (is_mt7915(&dev->mt76)) { + if (!dev->dbdc_support) + sts_160 /= 2; + else + sts_160 = 0; + } #ifdef CONFIG_MAC80211_MESH if (vif == NL80211_IFTYPE_MESH_POINT) @@ -772,8 +776,9 @@ mt7915_set_stream_he_txbf_caps(struct mt7915_phy *phy, elem->phy_cap_info[3] &= ~IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER; elem->phy_cap_info[4] &= ~IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER; - c = IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK | - IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK; + c = IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK; + if (sts_160) + c |= IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK; elem->phy_cap_info[5] &= ~c; c = IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB | @@ -789,8 +794,9 @@ mt7915_set_stream_he_txbf_caps(struct mt7915_phy *phy, elem->phy_cap_info[2] |= c; c = IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE | - IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4 | - IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4; + IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4; + if (sts_160) + c |= IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4; elem->phy_cap_info[4] |= c; /* do not support NG16 due to spec D4.0 changes subcarrier idx */ @@ -815,12 +821,13 @@ mt7915_set_stream_he_txbf_caps(struct mt7915_phy *phy, elem->phy_cap_info[4] |= IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER; /* num_snd_dim - * for mt7915, max supported sts is 2 for bw > 80MHz + * for mt7915, max supported sts is 2 for bw > 80MHz and 0 if dbdc */ c = FIELD_PREP(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK, - sts - 1) | - FIELD_PREP(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK, - sts_160 - 1); + sts - 1); + if (sts_160) + c |= FIELD_PREP(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK, + sts_160 - 1); elem->phy_cap_info[5] |= c; c = IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB | @@ -865,11 +872,14 @@ mt7915_init_he_caps(struct mt7915_phy *phy, enum nl80211_band band, u16 mcs_map_160 = 0; u8 nss_160; - /* Can do 1/2 of NSS streams in 160Mhz mode for mt7915 */ - if (is_mt7915(&dev->mt76) && !dev->dbdc_support) + if (!is_mt7915(&dev->mt76)) + nss_160 = nss; + else if (!dev->dbdc_support) + /* Can do 1/2 of NSS streams in 160Mhz mode for mt7915 */ nss_160 = nss / 2; else - nss_160 = nss; + /* Can't do 160MHz with mt7915 dbdc */ + nss_160 = 0; for (i = 0; i < 8; i++) { if (i < nss) @@ -915,11 +925,14 @@ mt7915_init_he_caps(struct mt7915_phy *phy, enum nl80211_band band, if (band == NL80211_BAND_2GHZ) he_cap_elem->phy_cap_info[0] = IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G; - else + else if (nss_160) he_cap_elem->phy_cap_info[0] = IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G; + else + he_cap_elem->phy_cap_info[0] = + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G; he_cap_elem->phy_cap_info[1] = IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD; @@ -973,9 +986,11 @@ mt7915_init_he_caps(struct mt7915_phy *phy, enum nl80211_band band, IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI; he_cap_elem->phy_cap_info[8] |= IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G | - IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU | - IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU | IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_484; + if (nss_160) + he_cap_elem->phy_cap_info[8] |= + IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU | + IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU; he_cap_elem->phy_cap_info[9] |= IEEE80211_HE_PHY_CAP9_LONGER_THAN_16_SIGB_OFDM_SYM | IEEE80211_HE_PHY_CAP9_NON_TRIGGERED_CQI_FEEDBACK | From fa67120bc8c78e66bc23d385e6dfbc1de6c25134 Mon Sep 17 00:00:00 2001 From: Evelyn Tsai Date: Fri, 11 Nov 2022 15:36:15 +0800 Subject: [PATCH 099/144] wifi: mt76: connac: update nss calculation in txs The hardware reports the number of transmitting antennas not the actual number of spatial streams Co-developed-by: Peter Chiu Signed-off-by: Peter Chiu Signed-off-by: Evelyn Tsai Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c index 8b7ec64abc95..fd60123fb284 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c @@ -567,7 +567,7 @@ bool mt76_connac2_mac_fill_txs(struct mt76_dev *dev, struct mt76_wcid *wcid, struct mt76_phy *mphy; struct rate_info rate = {}; bool cck = false; - u32 txrate, txs, mode; + u32 txrate, txs, mode, stbc; txs = le32_to_cpu(txs_data[0]); @@ -587,6 +587,10 @@ bool mt76_connac2_mac_fill_txs(struct mt76_dev *dev, struct mt76_wcid *wcid, rate.mcs = FIELD_GET(MT_TX_RATE_IDX, txrate); rate.nss = FIELD_GET(MT_TX_RATE_NSS, txrate) + 1; + stbc = FIELD_GET(MT_TX_RATE_STBC, txrate); + + if (stbc && rate.nss > 1) + rate.nss >>= 1; if (rate.nss - 1 < ARRAY_SIZE(stats->tx_nss)) stats->tx_nss[rate.nss - 1]++; From bd5dac7ced5a7c9faa4dc468ac9560c3256df845 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Thu, 13 Oct 2022 21:07:37 +0200 Subject: [PATCH 100/144] wifi: mt76: do not run mt76u_status_worker if the device is not running Fix the following NULL pointer dereference avoiding to run mt76u_status_worker thread if the device is not running yet. KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] CPU: 0 PID: 98 Comm: kworker/u2:2 Not tainted 5.14.0+ #78 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.1-0-ga5cab58e9a3f-prebuilt.qemu.org 04/01/2014 Workqueue: mt76 mt76u_tx_status_data RIP: 0010:mt76x02_mac_fill_tx_status.isra.0+0x82c/0x9e0 Code: c5 48 b8 00 00 00 00 00 fc ff df 80 3c 02 00 0f 85 94 01 00 00 48 b8 00 00 00 00 00 fc ff df 4d 8b 34 24 4c 89 f2 48 c1 ea 03 <0f> b6 04 02 84 c0 74 08 3c 03 0f 8e 89 01 00 00 41 8b 16 41 0f b7 RSP: 0018:ffffc900005af988 EFLAGS: 00010246 RAX: dffffc0000000000 RBX: ffffc900005afae8 RCX: 0000000000000000 RDX: 0000000000000000 RSI: ffffffff832fc661 RDI: ffffc900005afc2a RBP: ffffc900005afae0 R08: 0000000000000001 R09: fffff520000b5f3c R10: 0000000000000003 R11: fffff520000b5f3b R12: ffff88810b6132d8 R13: 000000000000ffff R14: 0000000000000000 R15: ffffc900005afc28 FS: 0000000000000000(0000) GS:ffff88811aa00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007fa0eda6a000 CR3: 0000000118f17000 CR4: 0000000000750ef0 PKRU: 55555554 Call Trace: mt76x02_send_tx_status+0x1d2/0xeb0 mt76x02_tx_status_data+0x8e/0xd0 mt76u_tx_status_data+0xe1/0x240 process_one_work+0x92b/0x1460 worker_thread+0x95/0xe00 kthread+0x3a1/0x480 ret_from_fork+0x1f/0x30 Modules linked in: --[ end trace 8df5d20fc5040f65 ]-- RIP: 0010:mt76x02_mac_fill_tx_status.isra.0+0x82c/0x9e0 Code: c5 48 b8 00 00 00 00 00 fc ff df 80 3c 02 00 0f 85 94 01 00 00 48 b8 00 00 00 00 00 fc ff df 4d 8b 34 24 4c 89 f2 48 c1 ea 03 <0f> b6 04 02 84 c0 74 08 3c 03 0f 8e 89 01 00 00 41 8b 16 41 0f b7 RSP: 0018:ffffc900005af988 EFLAGS: 00010246 RAX: dffffc0000000000 RBX: ffffc900005afae8 RCX: 0000000000000000 RDX: 0000000000000000 RSI: ffffffff832fc661 RDI: ffffc900005afc2a RBP: ffffc900005afae0 R08: 0000000000000001 R09: fffff520000b5f3c R10: 0000000000000003 R11: fffff520000b5f3b R12: ffff88810b6132d8 R13: 000000000000ffff R14: 0000000000000000 R15: ffffc900005afc28 FS: 0000000000000000(0000) GS:ffff88811aa00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007fa0eda6a000 CR3: 0000000118f17000 CR4: 0000000000750ef0 PKRU: 55555554 Moreover move stat_work schedule out of the for loop. Reported-by: Dokyung Song Co-developed-by: Deren Wu Signed-off-by: Deren Wu Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/usb.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c index 4c4033bb1bb3..0597df2729a6 100644 --- a/drivers/net/wireless/mediatek/mt76/usb.c +++ b/drivers/net/wireless/mediatek/mt76/usb.c @@ -766,6 +766,9 @@ static void mt76u_status_worker(struct mt76_worker *w) struct mt76_queue *q; int i; + if (!test_bit(MT76_STATE_RUNNING, &dev->phy.state)) + return; + for (i = 0; i < IEEE80211_NUM_ACS; i++) { q = dev->phy.q_tx[i]; if (!q) @@ -785,11 +788,11 @@ static void mt76u_status_worker(struct mt76_worker *w) wake_up(&dev->tx_wait); mt76_worker_schedule(&dev->tx_worker); - - if (dev->drv->tx_status_data && - !test_and_set_bit(MT76_READING_STATS, &dev->phy.state)) - queue_work(dev->wq, &dev->usb.stat_work); } + + if (dev->drv->tx_status_data && + !test_and_set_bit(MT76_READING_STATS, &dev->phy.state)) + queue_work(dev->wq, &dev->usb.stat_work); } static void mt76u_tx_status_data(struct work_struct *work) From c1eab2418fa44164a74448e62a5967f1e813e063 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Fri, 11 Nov 2022 05:23:43 +0800 Subject: [PATCH 101/144] wifi: mt76: connac: add mt76_connac_mcu_uni_set_chctx add mt76_connac_mcu_uni_set_chctx to set up the channel context per BSS in the firmware Signed-off-by: Sean Wang Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt76_connac_mcu.c | 147 ++++++++++-------- .../wireless/mediatek/mt76/mt76_connac_mcu.h | 3 + 2 files changed, 82 insertions(+), 68 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index 9bba18d24c71..aa1e2faeeafe 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -1347,44 +1347,13 @@ mt76_connac_mcu_uni_bss_he_tlv(struct mt76_phy *phy, struct ieee80211_vif *vif, he->max_nss_mcs[CMD_HE_MCS_BW8080] = cap->he_mcs_nss_supp.tx_mcs_80p80; } -int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy, - struct ieee80211_vif *vif, - struct mt76_wcid *wcid, - bool enable) +int mt76_connac_mcu_uni_set_chctx(struct mt76_phy *phy, struct mt76_vif *mvif, + struct ieee80211_chanctx_conf *ctx) { - struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv; - struct cfg80211_chan_def *chandef = &phy->chandef; + struct cfg80211_chan_def *chandef = ctx ? &ctx->def : &phy->chandef; int freq1 = chandef->center_freq1, freq2 = chandef->center_freq2; enum nl80211_band band = chandef->chan->band; struct mt76_dev *mdev = phy->dev; - struct { - struct { - u8 bss_idx; - u8 pad[3]; - } __packed hdr; - struct mt76_connac_bss_basic_tlv basic; - struct mt76_connac_bss_qos_tlv qos; - } basic_req = { - .hdr = { - .bss_idx = mvif->idx, - }, - .basic = { - .tag = cpu_to_le16(UNI_BSS_INFO_BASIC), - .len = cpu_to_le16(sizeof(struct mt76_connac_bss_basic_tlv)), - .bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int), - .dtim_period = vif->bss_conf.dtim_period, - .omac_idx = mvif->omac_idx, - .band_idx = mvif->band_idx, - .wmm_idx = mvif->wmm_idx, - .active = true, /* keep bss deactivated */ - .phymode = mt76_connac_get_phy_mode(phy, vif, band, NULL), - }, - .qos = { - .tag = cpu_to_le16(UNI_BSS_INFO_QBSS), - .len = cpu_to_le16(sizeof(struct mt76_connac_bss_qos_tlv)), - .qos = vif->bss_conf.qos, - }, - }; struct { struct { u8 bss_idx; @@ -1422,6 +1391,81 @@ int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy, .band = band, }, }; + + switch (chandef->width) { + case NL80211_CHAN_WIDTH_40: + rlm_req.rlm.bw = CMD_CBW_40MHZ; + break; + case NL80211_CHAN_WIDTH_80: + rlm_req.rlm.bw = CMD_CBW_80MHZ; + break; + case NL80211_CHAN_WIDTH_80P80: + rlm_req.rlm.bw = CMD_CBW_8080MHZ; + break; + case NL80211_CHAN_WIDTH_160: + rlm_req.rlm.bw = CMD_CBW_160MHZ; + break; + case NL80211_CHAN_WIDTH_5: + rlm_req.rlm.bw = CMD_CBW_5MHZ; + break; + case NL80211_CHAN_WIDTH_10: + rlm_req.rlm.bw = CMD_CBW_10MHZ; + break; + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + default: + rlm_req.rlm.bw = CMD_CBW_20MHZ; + rlm_req.rlm.ht_op_info = 0; + break; + } + + if (rlm_req.rlm.control_channel < rlm_req.rlm.center_chan) + rlm_req.rlm.sco = 1; /* SCA */ + else if (rlm_req.rlm.control_channel > rlm_req.rlm.center_chan) + rlm_req.rlm.sco = 3; /* SCB */ + + return mt76_mcu_send_msg(mdev, MCU_UNI_CMD(BSS_INFO_UPDATE), &rlm_req, + sizeof(rlm_req), true); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_uni_set_chctx); + +int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy, + struct ieee80211_vif *vif, + struct mt76_wcid *wcid, + bool enable) +{ + struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv; + struct cfg80211_chan_def *chandef = &phy->chandef; + enum nl80211_band band = chandef->chan->band; + struct mt76_dev *mdev = phy->dev; + struct { + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct mt76_connac_bss_basic_tlv basic; + struct mt76_connac_bss_qos_tlv qos; + } basic_req = { + .hdr = { + .bss_idx = mvif->idx, + }, + .basic = { + .tag = cpu_to_le16(UNI_BSS_INFO_BASIC), + .len = cpu_to_le16(sizeof(struct mt76_connac_bss_basic_tlv)), + .bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int), + .dtim_period = vif->bss_conf.dtim_period, + .omac_idx = mvif->omac_idx, + .band_idx = mvif->band_idx, + .wmm_idx = mvif->wmm_idx, + .active = true, /* keep bss deactivated */ + .phymode = mt76_connac_get_phy_mode(phy, vif, band, NULL), + }, + .qos = { + .tag = cpu_to_le16(UNI_BSS_INFO_QBSS), + .len = cpu_to_le16(sizeof(struct mt76_connac_bss_qos_tlv)), + .qos = vif->bss_conf.qos, + }, + }; int err, conn_type; u8 idx, basic_phy; @@ -1508,40 +1552,7 @@ int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy, return err; } - switch (chandef->width) { - case NL80211_CHAN_WIDTH_40: - rlm_req.rlm.bw = CMD_CBW_40MHZ; - break; - case NL80211_CHAN_WIDTH_80: - rlm_req.rlm.bw = CMD_CBW_80MHZ; - break; - case NL80211_CHAN_WIDTH_80P80: - rlm_req.rlm.bw = CMD_CBW_8080MHZ; - break; - case NL80211_CHAN_WIDTH_160: - rlm_req.rlm.bw = CMD_CBW_160MHZ; - break; - case NL80211_CHAN_WIDTH_5: - rlm_req.rlm.bw = CMD_CBW_5MHZ; - break; - case NL80211_CHAN_WIDTH_10: - rlm_req.rlm.bw = CMD_CBW_10MHZ; - break; - case NL80211_CHAN_WIDTH_20_NOHT: - case NL80211_CHAN_WIDTH_20: - default: - rlm_req.rlm.bw = CMD_CBW_20MHZ; - rlm_req.rlm.ht_op_info = 0; - break; - } - - if (rlm_req.rlm.control_channel < rlm_req.rlm.center_chan) - rlm_req.rlm.sco = 1; /* SCA */ - else if (rlm_req.rlm.control_channel > rlm_req.rlm.center_chan) - rlm_req.rlm.sco = 3; /* SCB */ - - return mt76_mcu_send_msg(mdev, MCU_UNI_CMD(BSS_INFO_UPDATE), &rlm_req, - sizeof(rlm_req), true); + return mt76_connac_mcu_uni_set_chctx(phy, mvif, NULL); } EXPORT_SYMBOL_GPL(mt76_connac_mcu_uni_add_bss); diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index 8166722d4717..7e1c9e8029cd 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -1747,6 +1747,9 @@ int mt76_connac_mcu_uni_add_dev(struct mt76_phy *phy, int mt76_connac_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif, struct ieee80211_ampdu_params *params, int cmd, bool enable, bool tx); +int mt76_connac_mcu_uni_set_chctx(struct mt76_phy *phy, + struct mt76_vif *vif, + struct ieee80211_chanctx_conf *ctx); int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy, struct ieee80211_vif *vif, struct mt76_wcid *wcid, From a0ab9c3160dfafece67ed39f43f35f533eeea428 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Fri, 11 Nov 2022 05:23:44 +0800 Subject: [PATCH 102/144] wifi: mt76: mt7921: add chanctx parameter to mt76_connac_mcu_uni_add_bss signature Add a chanctx parameter to mt76_connac_mcu_uni_add_bss signature to allow the firmware binds the BSS into the specific channel context. Signed-off-by: Sean Wang Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7615/mcu.c | 2 +- drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 7 ++++--- drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h | 3 ++- drivers/net/wireless/mediatek/mt76/mt7921/mac.c | 2 +- drivers/net/wireless/mediatek/mt76/mt7921/main.c | 10 ++++++---- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c index 3dac76e6df4d..83f30305414d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c @@ -1119,7 +1119,7 @@ mt7615_mcu_uni_add_bss(struct mt7615_phy *phy, struct ieee80211_vif *vif, struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; return mt76_connac_mcu_uni_add_bss(phy->mt76, vif, &mvif->sta.wcid, - enable); + enable, NULL); } static inline int diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index aa1e2faeeafe..92855d8264d1 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -1432,10 +1432,11 @@ EXPORT_SYMBOL_GPL(mt76_connac_mcu_uni_set_chctx); int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy, struct ieee80211_vif *vif, struct mt76_wcid *wcid, - bool enable) + bool enable, + struct ieee80211_chanctx_conf *ctx) { struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv; - struct cfg80211_chan_def *chandef = &phy->chandef; + struct cfg80211_chan_def *chandef = ctx ? &ctx->def : &phy->chandef; enum nl80211_band band = chandef->chan->band; struct mt76_dev *mdev = phy->dev; struct { @@ -1552,7 +1553,7 @@ int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy, return err; } - return mt76_connac_mcu_uni_set_chctx(phy, mvif, NULL); + return mt76_connac_mcu_uni_set_chctx(phy, mvif, ctx); } EXPORT_SYMBOL_GPL(mt76_connac_mcu_uni_add_bss); diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index 7e1c9e8029cd..cf34192a9d90 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -1753,7 +1753,8 @@ int mt76_connac_mcu_uni_set_chctx(struct mt76_phy *phy, int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy, struct ieee80211_vif *vif, struct mt76_wcid *wcid, - bool enable); + bool enable, + struct ieee80211_chanctx_conf *ctx); int mt76_connac_mcu_sta_cmd(struct mt76_phy *phy, struct mt76_sta_cmd_info *info); void mt76_connac_mcu_beacon_loss_iter(void *priv, u8 *mac, diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c index 9a4f307f8f24..eeab756240a2 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c @@ -866,7 +866,7 @@ mt7921_vif_connect_iter(void *priv, u8 *mac, if (vif->type == NL80211_IFTYPE_AP) { mt76_connac_mcu_uni_add_bss(dev->phy.mt76, vif, &mvif->sta.wcid, - true); + true, NULL); mt7921_mcu_sta_update(dev, NULL, vif, true, MT76_STA_INFO_STATE_NONE); mt7921_mcu_uni_add_beacon_offload(dev, hw, vif, true); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c index 092f683572f4..cc3365c4f8c5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -748,7 +748,7 @@ void mt7921_mac_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif, if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) mt76_connac_mcu_uni_add_bss(&dev->mphy, vif, &mvif->sta.wcid, - true); + true, NULL); mt7921_mac_wtbl_update(dev, msta->wcid.idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); @@ -780,7 +780,8 @@ void mt7921_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, ewma_rssi_init(&mvif->rssi); if (!sta->tdls) mt76_connac_mcu_uni_add_bss(&dev->mphy, vif, - &mvif->sta.wcid, false); + &mvif->sta.wcid, false, + NULL); } spin_lock_bh(&dev->sta_poll_lock); @@ -1534,7 +1535,7 @@ mt7921_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, mt7921_mutex_acquire(dev); err = mt76_connac_mcu_uni_add_bss(phy->mt76, vif, &mvif->sta.wcid, - true); + true, NULL); if (err) goto out; @@ -1565,7 +1566,8 @@ mt7921_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, if (err) goto out; - mt76_connac_mcu_uni_add_bss(phy->mt76, vif, &mvif->sta.wcid, false); + mt76_connac_mcu_uni_add_bss(phy->mt76, vif, &mvif->sta.wcid, false, + NULL); out: mt7921_mutex_release(dev); From 5b55b6da982c974fca3e521edb239163d133834f Mon Sep 17 00:00:00 2001 From: Quan Zhou Date: Fri, 11 Nov 2022 05:23:45 +0800 Subject: [PATCH 103/144] wifi: mt76: mt7921: add unified ROC cmd/event support Add unified ROC cmd/event which is only supported by the newer fw. Co-developed-by: Sean Wang Signed-off-by: Sean Wang Co-developed-by: Deren Wu Signed-off-by: Deren Wu Co-developed-by: Kaikai Hu Signed-off-by: Kaikai Hu Signed-off-by: Quan Zhou Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt76_connac_mcu.h | 10 +- .../net/wireless/mediatek/mt76/mt7921/mcu.c | 119 ++++++++++++++++++ .../wireless/mediatek/mt76/mt7921/mt7921.h | 19 +++ 3 files changed, 146 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index cf34192a9d90..a4893331b7c3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -121,11 +121,13 @@ struct mt76_connac2_mcu_rxd { u8 eid; u8 seq; - u8 rsv[2]; - + u8 option; + u8 rsv; u8 ext_eid; u8 rsv1[2]; u8 s2d_index; + + u8 tlv[0]; }; struct mt76_connac2_patch_hdr { @@ -957,6 +959,9 @@ enum { DEV_INFO_MAX_NUM }; +#define MCU_UNI_CMD_EVENT BIT(1) +#define MCU_UNI_CMD_UNSOLICITED_EVENT BIT(2) + /* event table */ enum { MCU_EVENT_TARGET_ADDRESS_LEN = 0x01, @@ -1163,6 +1168,7 @@ enum { MCU_UNI_CMD_OFFLOAD = 0x06, MCU_UNI_CMD_HIF_CTRL = 0x07, MCU_UNI_CMD_SNIFFER = 0x24, + MCU_UNI_CMD_ROC = 0x27, }; enum { diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c index 67bf92969a7b..2b36c5f6eb40 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c @@ -302,6 +302,23 @@ mt7921_mcu_rx_unsolicited_event(struct mt7921_dev *dev, struct sk_buff *skb) dev_kfree_skb(skb); } +static void +mt7921_mcu_uni_rx_unsolicited_event(struct mt7921_dev *dev, + struct sk_buff *skb) +{ + struct mt76_connac2_mcu_rxd *rxd; + + rxd = (struct mt76_connac2_mcu_rxd *)skb->data; + + switch (rxd->eid) { + case MCU_UNI_EVENT_ROC: + break; + default: + break; + } + dev_kfree_skb(skb); +} + void mt7921_mcu_rx_event(struct mt7921_dev *dev, struct sk_buff *skb) { struct mt76_connac2_mcu_rxd *rxd; @@ -311,6 +328,11 @@ void mt7921_mcu_rx_event(struct mt7921_dev *dev, struct sk_buff *skb) rxd = (struct mt76_connac2_mcu_rxd *)skb->data; + if (rxd->option & MCU_UNI_CMD_UNSOLICITED_EVENT) { + mt7921_mcu_uni_rx_unsolicited_event(dev, skb); + return; + } + if (rxd->eid == 0x6) { mt76_mcu_rx_event(&dev->mt76, skb); return; @@ -636,6 +658,103 @@ int mt7921_mcu_set_tx(struct mt7921_dev *dev, struct ieee80211_vif *vif) &req_mu, sizeof(req_mu), false); } +int mt7921_mcu_set_roc(struct mt7921_phy *phy, struct mt7921_vif *vif, + struct ieee80211_channel *chan, int duration, + enum mt7921_roc_req type, u8 token_id) +{ + int center_ch = ieee80211_frequency_to_channel(chan->center_freq); + struct mt7921_dev *dev = phy->dev; + struct { + struct { + u8 rsv[4]; + } __packed hdr; + struct roc_acquire_tlv { + __le16 tag; + __le16 len; + u8 bss_idx; + u8 tokenid; + u8 control_channel; + u8 sco; + u8 band; + u8 bw; + u8 center_chan; + u8 center_chan2; + u8 bw_from_ap; + u8 center_chan_from_ap; + u8 center_chan2_from_ap; + u8 reqtype; + __le32 maxinterval; + u8 dbdcband; + u8 rsv[3]; + } __packed roc; + } __packed req = { + .roc = { + .tag = cpu_to_le16(UNI_ROC_ACQUIRE), + .len = cpu_to_le16(sizeof(struct roc_acquire_tlv)), + .tokenid = token_id, + .reqtype = type, + .maxinterval = cpu_to_le32(duration), + .bss_idx = vif->mt76.idx, + .control_channel = chan->hw_value, + .bw = CMD_CBW_20MHZ, + .bw_from_ap = CMD_CBW_20MHZ, + .center_chan = center_ch, + .center_chan_from_ap = center_ch, + .dbdcband = 0xff, /* auto */ + }, + }; + + if (chan->hw_value < center_ch) + req.roc.sco = 1; /* SCA */ + else if (chan->hw_value > center_ch) + req.roc.sco = 3; /* SCB */ + + switch (chan->band) { + case NL80211_BAND_6GHZ: + req.roc.band = 3; + break; + case NL80211_BAND_5GHZ: + req.roc.band = 2; + break; + default: + req.roc.band = 1; + break; + } + + return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(ROC), + &req, sizeof(req), false); +} + +int mt7921_mcu_abort_roc(struct mt7921_phy *phy, struct mt7921_vif *vif, + u8 token_id) +{ + struct mt7921_dev *dev = phy->dev; + struct { + struct { + u8 rsv[4]; + } __packed hdr; + struct roc_abort_tlv { + __le16 tag; + __le16 len; + u8 bss_idx; + u8 tokenid; + u8 dbdcband; + u8 rsv[5]; + } __packed abort; + } __packed req = { + .abort = { + .tag = cpu_to_le16(UNI_ROC_ABORT), + .len = cpu_to_le16(sizeof(struct roc_abort_tlv)), + .tokenid = token_id, + .bss_idx = vif->mt76.idx, + .dbdcband = 0xff, /* auto*/ + }, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(ROC), + &req, sizeof(req), false); +} + int mt7921_mcu_set_chan_info(struct mt7921_phy *phy, int cmd) { struct mt7921_dev *dev = phy->dev; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h index eaba114a9c7e..d9d78f6b088e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h @@ -53,6 +53,20 @@ #define MT7921_SDIO_HDR_TX_BYTES GENMASK(15, 0) #define MT7921_SDIO_HDR_PKT_TYPE GENMASK(17, 16) +#define MCU_UNI_EVENT_ROC 0x27 + +enum { + UNI_ROC_ACQUIRE, + UNI_ROC_ABORT, + UNI_ROC_NUM +}; + +enum mt7921_roc_req { + MT7921_ROC_REQ_JOIN, + MT7921_ROC_REQ_ROC, + MT7921_ROC_REQ_NUM +}; + enum mt7921_sdio_pkt_type { MT7921_SDIO_TXD, MT7921_SDIO_DATA, @@ -508,4 +522,9 @@ int mt7921_set_tx_sar_pwr(struct ieee80211_hw *hw, int mt7921_mcu_set_clc(struct mt7921_dev *dev, u8 *alpha2, enum environment_cap env_cap); +int mt7921_mcu_set_roc(struct mt7921_phy *phy, struct mt7921_vif *vif, + struct ieee80211_channel *chan, int duration, + enum mt7921_roc_req type, u8 token_id); +int mt7921_mcu_abort_roc(struct mt7921_phy *phy, struct mt7921_vif *vif, + u8 token_id); #endif From 98678fc03c9599be3bac7a1e253d2697b3a26624 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Fri, 11 Nov 2022 05:23:46 +0800 Subject: [PATCH 104/144] wifi: mt76: mt7921: drop ieee80211_[start, stop]_queues in driver The firmware would be in charge of braking and continuing the traffic while the channel contexts are switching between different BSS and HW SCAN in the background. Signed-off-by: Sean Wang Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7921/mcu.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c index 2b36c5f6eb40..7a74abecb269 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c @@ -199,20 +199,6 @@ mt7921_mcu_connection_loss_event(struct mt7921_dev *dev, struct sk_buff *skb) mt7921_mcu_connection_loss_iter, event); } -static void -mt7921_mcu_bss_event(struct mt7921_dev *dev, struct sk_buff *skb) -{ - struct mt76_phy *mphy = &dev->mt76.phy; - struct mt76_connac_mcu_bss_event *event; - - skb_pull(skb, sizeof(struct mt76_connac2_mcu_rxd)); - event = (struct mt76_connac_mcu_bss_event *)skb->data; - if (event->is_absent) - ieee80211_stop_queues(mphy->hw); - else - ieee80211_wake_queues(mphy->hw); -} - static void mt7921_mcu_debug_msg_event(struct mt7921_dev *dev, struct sk_buff *skb) { @@ -279,9 +265,6 @@ mt7921_mcu_rx_unsolicited_event(struct mt7921_dev *dev, struct sk_buff *skb) case MCU_EVENT_SCAN_DONE: mt7921_mcu_scan_event(dev, skb); return; - case MCU_EVENT_BSS_ABSENCE: - mt7921_mcu_bss_event(dev, skb); - break; case MCU_EVENT_DBG_MSG: mt7921_mcu_debug_msg_event(dev, skb); break; @@ -341,7 +324,6 @@ void mt7921_mcu_rx_event(struct mt7921_dev *dev, struct sk_buff *skb) if (rxd->ext_eid == MCU_EXT_EVENT_RATE_REPORT || rxd->eid == MCU_EVENT_BSS_BEACON_LOSS || rxd->eid == MCU_EVENT_SCHED_SCAN_DONE || - rxd->eid == MCU_EVENT_BSS_ABSENCE || rxd->eid == MCU_EVENT_SCAN_DONE || rxd->eid == MCU_EVENT_TX_DONE || rxd->eid == MCU_EVENT_DBG_MSG || From fe62788b6233df0fe43920e32f30c9583d584117 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Fri, 11 Nov 2022 05:23:47 +0800 Subject: [PATCH 105/144] wifi: mt76: connac: accept hw scan request at a time The current hw scan cannot accept multiple hw scan requests at a time, we return the proper error code when the hw scan is still undergoing. Signed-off-by: Sean Wang Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index 92855d8264d1..6b2ce5fd3e4f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -1571,6 +1571,9 @@ int mt76_connac_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif, struct mt76_connac_hw_scan_req *req; struct sk_buff *skb; + if (test_bit(MT76_HW_SCANNING, &phy->state)) + return -EBUSY; + skb = mt76_mcu_msg_alloc(mdev, NULL, sizeof(*req)); if (!skb) return -ENOMEM; From 034ae28b56f13dc1f2beb3fa294b455f57ede9cb Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Tue, 15 Nov 2022 08:11:23 +0800 Subject: [PATCH 106/144] wifi: mt76: mt7921: introduce remain_on_channel support Introduce remain_on_channel support. Additionally, we add mt7921_check_offload_capability to disable .remain_on_channel and .cancel_remain_on_channel and related configuration because those operations would rely on the fundamental MCU commands that will be only supported with newer firmware. Co-developed-by: Deren Wu Signed-off-by: Deren Wu Signed-off-by: Sean Wang Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7921/init.c | 63 ++++++++++ .../net/wireless/mediatek/mt76/mt7921/main.c | 112 ++++++++++++++++++ .../net/wireless/mediatek/mt76/mt7921/mcu.c | 24 ++++ .../wireless/mediatek/mt76/mt7921/mt7921.h | 48 ++++++++ .../net/wireless/mediatek/mt76/mt7921/pci.c | 33 ++++-- .../net/wireless/mediatek/mt76/mt7921/sdio.c | 23 +++- .../net/wireless/mediatek/mt76/mt7921/usb.c | 12 +- 7 files changed, 303 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/init.c b/drivers/net/wireless/mediatek/mt76/mt7921/init.c index dcdb3cf04ac1..e9353e9929a9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/init.c @@ -2,6 +2,7 @@ /* Copyright (C) 2020 MediaTek Inc. */ #include +#include #include "mt7921.h" #include "mac.h" #include "mcu.h" @@ -65,12 +66,18 @@ mt7921_init_wiphy(struct ieee80211_hw *hw) hw->sta_data_size = sizeof(struct mt7921_sta); hw->vif_data_size = sizeof(struct mt7921_vif); + if (dev->fw_features & MT7921_FW_CAP_CNM) + wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + else + wiphy->flags &= ~WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + wiphy->iface_combinations = if_comb; wiphy->flags &= ~(WIPHY_FLAG_IBSS_RSN | WIPHY_FLAG_4ADDR_AP | WIPHY_FLAG_4ADDR_STATION); wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP); wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); + wiphy->max_remain_on_channel_duration = 5000; wiphy->max_scan_ie_len = MT76_CONNAC_SCAN_IE_LEN; wiphy->max_scan_ssids = 4; wiphy->max_sched_scan_plan_interval = @@ -129,6 +136,58 @@ mt7921_mac_init_band(struct mt7921_dev *dev, u8 band) mt76_clear(dev, MT_DMA_DCR0(band), MT_DMA_DCR0_RXD_G5_EN); } +u8 mt7921_check_offload_capability(struct device *dev, const char *fw_wm) +{ + struct mt7921_fw_features *features = NULL; + const struct mt76_connac2_fw_trailer *hdr; + struct mt7921_realease_info *rel_info; + const struct firmware *fw; + int ret, i, offset = 0; + const u8 *data, *end; + + ret = request_firmware(&fw, fw_wm, dev); + if (ret) + return ret; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) { + dev_err(dev, "Invalid firmware\n"); + return -EINVAL; + } + + data = fw->data; + hdr = (const void *)(fw->data + fw->size - sizeof(*hdr)); + + for (i = 0; i < hdr->n_region; i++) { + const struct mt76_connac2_fw_region *region; + + region = (const void *)((const u8 *)hdr - + (hdr->n_region - i) * sizeof(*region)); + offset += le32_to_cpu(region->len); + } + + data += offset + 16; + rel_info = (struct mt7921_realease_info *)data; + data += sizeof(*rel_info); + end = data + le16_to_cpu(rel_info->len); + + while (data < end) { + rel_info = (struct mt7921_realease_info *)data; + data += sizeof(*rel_info); + + if (rel_info->tag == MT7921_FW_TAG_FEATURE) { + features = (struct mt7921_fw_features *)data; + break; + } + + data += le16_to_cpu(rel_info->len) + rel_info->pad_len; + } + + release_firmware(fw); + + return features ? features->data : 0; +} +EXPORT_SYMBOL_GPL(mt7921_check_offload_capability); + int mt7921_mac_init(struct mt7921_dev *dev) { int i; @@ -278,6 +337,10 @@ int mt7921_register_device(struct mt7921_dev *dev) INIT_WORK(&dev->reset_work, mt7921_mac_reset_work); INIT_WORK(&dev->init_work, mt7921_init_work); + INIT_WORK(&dev->phy.roc_work, mt7921_roc_work); + timer_setup(&dev->phy.roc_timer, mt7921_roc_timer, 0); + init_waitqueue_head(&dev->phy.roc_wait); + dev->pm.idle_timeout = MT7921_PM_TIMEOUT; dev->pm.stats.last_wake_event = jiffies; dev->pm.stats.last_doze_event = jiffies; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c index cc3365c4f8c5..012c92d49f49 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -386,6 +386,116 @@ static void mt7921_remove_interface(struct ieee80211_hw *hw, mt76_packet_id_flush(&dev->mt76, &msta->wcid); } +static void mt7921_roc_iter(void *priv, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; + struct mt7921_phy *phy = priv; + + mt7921_mcu_abort_roc(phy, mvif, phy->roc_token_id); +} + +void mt7921_roc_work(struct work_struct *work) +{ + struct mt7921_phy *phy; + + phy = (struct mt7921_phy *)container_of(work, struct mt7921_phy, + roc_work); + + if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state)) + return; + + mt7921_mutex_acquire(phy->dev); + ieee80211_iterate_active_interfaces(phy->mt76->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7921_roc_iter, phy); + mt7921_mutex_release(phy->dev); + ieee80211_remain_on_channel_expired(phy->mt76->hw); +} + +void mt7921_roc_timer(struct timer_list *timer) +{ + struct mt7921_phy *phy = from_timer(phy, timer, roc_timer); + + ieee80211_queue_work(phy->mt76->hw, &phy->roc_work); +} + +static int mt7921_abort_roc(struct mt7921_phy *phy, struct mt7921_vif *vif) +{ + int err; + + if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state)) + return 0; + + del_timer_sync(&phy->roc_timer); + cancel_work_sync(&phy->roc_work); + err = mt7921_mcu_abort_roc(phy, vif, phy->roc_token_id); + clear_bit(MT76_STATE_ROC, &phy->mt76->state); + + return err; +} + +static int mt7921_set_roc(struct mt7921_phy *phy, + struct mt7921_vif *vif, + struct ieee80211_channel *chan, + int duration, + enum mt7921_roc_req type) +{ + int err; + + if (test_and_set_bit(MT76_STATE_ROC, &phy->mt76->state)) + return -EBUSY; + + phy->roc_grant = false; + + err = mt7921_mcu_set_roc(phy, vif, chan, duration, type, + ++phy->roc_token_id); + if (err < 0) { + clear_bit(MT76_STATE_ROC, &phy->mt76->state); + goto out; + } + + if (!wait_event_timeout(phy->roc_wait, phy->roc_grant, HZ)) { + mt7921_mcu_abort_roc(phy, vif, phy->roc_token_id); + clear_bit(MT76_STATE_ROC, &phy->mt76->state); + err = -ETIMEDOUT; + } + +out: + return err; +} + +static int mt7921_remain_on_channel(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel *chan, + int duration, + enum ieee80211_roc_type type) +{ + struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; + struct mt7921_phy *phy = mt7921_hw_phy(hw); + int err; + + mt7921_mutex_acquire(phy->dev); + err = mt7921_set_roc(phy, mvif, chan, duration, MT7921_ROC_REQ_ROC); + mt7921_mutex_release(phy->dev); + + return err; +} + +static int mt7921_cancel_remain_on_channel(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; + struct mt7921_phy *phy = mt7921_hw_phy(hw); + int err; + + mt7921_mutex_acquire(phy->dev); + err = mt7921_abort_roc(phy, mvif); + mt7921_mutex_release(phy->dev); + + return err; +} + static int mt7921_set_channel(struct mt7921_phy *phy) { struct mt7921_dev *dev = phy->dev; @@ -1623,6 +1733,8 @@ const struct ieee80211_ops mt7921_ops = { #endif /* CONFIG_PM */ .flush = mt7921_flush, .set_sar_specs = mt7921_set_sar_specs, + .remain_on_channel = mt7921_remain_on_channel, + .cancel_remain_on_channel = mt7921_cancel_remain_on_channel, }; EXPORT_SYMBOL_GPL(mt7921_ops); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c index 7a74abecb269..fb9c0f66cb27 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c @@ -154,6 +154,29 @@ void mt7921_mcu_set_suspend_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) #endif /* CONFIG_PM */ +static void +mt7921_mcu_uni_roc_event(struct mt7921_dev *dev, struct sk_buff *skb) +{ + struct mt7921_roc_grant_tlv *grant; + struct mt76_connac2_mcu_rxd *rxd; + int duration; + + rxd = (struct mt76_connac2_mcu_rxd *)skb->data; + grant = (struct mt7921_roc_grant_tlv *)(rxd->tlv + 4); + + /* should never happen */ + WARN_ON_ONCE((le16_to_cpu(grant->tag) != UNI_EVENT_ROC_GRANT)); + + if (grant->reqtype == MT7921_ROC_REQ_ROC) + ieee80211_ready_on_channel(dev->mt76.phy.hw); + + dev->phy.roc_grant = true; + wake_up(&dev->phy.roc_wait); + duration = le32_to_cpu(grant->max_interval); + mod_timer(&dev->phy.roc_timer, + round_jiffies_up(jiffies + msecs_to_jiffies(duration))); +} + static void mt7921_mcu_scan_event(struct mt7921_dev *dev, struct sk_buff *skb) { @@ -295,6 +318,7 @@ mt7921_mcu_uni_rx_unsolicited_event(struct mt7921_dev *dev, switch (rxd->eid) { case MCU_UNI_EVENT_ROC: + mt7921_mcu_uni_roc_event(dev, skb); break; default: break; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h index d9d78f6b088e..4f0147e40bc3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h @@ -32,6 +32,9 @@ #define MT7921_MCU_INIT_RETRY_COUNT 10 #define MT7921_WFSYS_INIT_RETRY_COUNT 2 +#define MT7921_FW_TAG_FEATURE 4 +#define MT7921_FW_CAP_CNM BIT(7) + #define MT7921_FIRMWARE_WM "mediatek/WIFI_RAM_CODE_MT7961_1.bin" #define MT7921_ROM_PATCH "mediatek/WIFI_MT7961_patch_mcu_1_2_hdr.bin" @@ -67,6 +70,41 @@ enum mt7921_roc_req { MT7921_ROC_REQ_NUM }; +enum { + UNI_EVENT_ROC_GRANT = 0, + UNI_EVENT_ROC_TAG_NUM +}; + +struct mt7921_realease_info { + __le16 len; + u8 pad_len; + u8 tag; +} __packed; + +struct mt7921_fw_features { + u8 segment; + u8 data; + u8 rsv[14]; +} __packed; + +struct mt7921_roc_grant_tlv { + __le16 tag; + __le16 len; + u8 bss_idx; + u8 tokenid; + u8 status; + u8 primarychannel; + u8 rfsco; + u8 rfband; + u8 channelwidth; + u8 centerfreqseg1; + u8 centerfreqseg2; + u8 reqtype; + u8 dbdcband; + u8 rsv[1]; + __le32 max_interval; +} __packed; + enum mt7921_sdio_pkt_type { MT7921_SDIO_TXD, MT7921_SDIO_DATA, @@ -214,6 +252,12 @@ struct mt7921_phy { #endif struct mt7921_clc *clc[MT7921_CLC_MAX_NUM]; + + struct work_struct roc_work; + struct timer_list roc_timer; + wait_queue_head_t roc_wait; + u8 roc_token_id; + bool roc_grant; }; #define mt7921_init_reset(dev) ((dev)->hif_ops->init_reset(dev)) @@ -250,6 +294,7 @@ struct mt7921_dev { struct work_struct init_work; u8 fw_debug; + u8 fw_features; struct mt76_connac_pm pm; struct mt76_connac_coredump coredump; @@ -439,6 +484,8 @@ int mt7921_mcu_uni_rx_ba(struct mt7921_dev *dev, struct ieee80211_ampdu_params *params, bool enable); void mt7921_scan_work(struct work_struct *work); +void mt7921_roc_work(struct work_struct *work); +void mt7921_roc_timer(struct timer_list *timer); int mt7921_mcu_uni_bss_ps(struct mt7921_dev *dev, struct ieee80211_vif *vif); int mt7921_mcu_drv_pmctrl(struct mt7921_dev *dev); int mt7921_mcu_fw_pmctrl(struct mt7921_dev *dev); @@ -527,4 +574,5 @@ int mt7921_mcu_set_roc(struct mt7921_phy *phy, struct mt7921_vif *vif, enum mt7921_roc_req type, u8 token_id); int mt7921_mcu_abort_roc(struct mt7921_phy *phy, struct mt7921_vif *vif, u8 token_id); +u8 mt7921_check_offload_capability(struct device *dev, const char *fw_wm); #endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c index b38d119b2ea9..32ad26ef3a68 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c @@ -13,10 +13,14 @@ #include "../trace.h" static const struct pci_device_id mt7921_pci_device_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7961) }, - { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7922) }, - { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0608) }, - { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0616) }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7961), + .driver_data = (kernel_ulong_t)MT7921_FIRMWARE_WM }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7922), + .driver_data = (kernel_ulong_t)MT7922_FIRMWARE_WM }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0608), + .driver_data = (kernel_ulong_t)MT7921_FIRMWARE_WM }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0616), + .driver_data = (kernel_ulong_t)MT7921_FIRMWARE_WM }, { }, }; @@ -253,9 +257,11 @@ static int mt7921_pci_probe(struct pci_dev *pdev, .fw_own = mt7921e_mcu_fw_pmctrl, }; + struct ieee80211_ops *ops; struct mt76_bus_ops *bus_ops; struct mt7921_dev *dev; struct mt76_dev *mdev; + u8 features; int ret; ret = pcim_enable_device(pdev); @@ -279,8 +285,21 @@ static int mt7921_pci_probe(struct pci_dev *pdev, if (mt7921_disable_aspm) mt76_pci_disable_aspm(pdev); - mdev = mt76_alloc_device(&pdev->dev, sizeof(*dev), &mt7921_ops, - &drv_ops); + features = mt7921_check_offload_capability(&pdev->dev, (const char *) + id->driver_data); + ops = devm_kmemdup(&pdev->dev, &mt7921_ops, sizeof(mt7921_ops), + GFP_KERNEL); + if (!ops) { + ret = -ENOMEM; + goto err_free_pci_vec; + } + + if (!(features & MT7921_FW_CAP_CNM)) { + ops->remain_on_channel = NULL; + ops->cancel_remain_on_channel = NULL; + } + + mdev = mt76_alloc_device(&pdev->dev, sizeof(*dev), ops, &drv_ops); if (!mdev) { ret = -ENOMEM; goto err_free_pci_vec; @@ -289,8 +308,8 @@ static int mt7921_pci_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, mdev); dev = container_of(mdev, struct mt7921_dev, mt76); + dev->fw_features = features; dev->hif_ops = &mt7921_pcie_ops; - mt76_mmio_init(&dev->mt76, pcim_iomap_table(pdev)[0]); tasklet_init(&dev->irq_tasklet, mt7921_irq_tasklet, (unsigned long)dev); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c index 377ca5fa3f6e..e07a8fb87421 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c @@ -17,7 +17,8 @@ #include "mcu.h" static const struct sdio_device_id mt7921s_table[] = { - { SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7901) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7901), + .driver_data = (kernel_ulong_t)MT7921_FIRMWARE_WM }, { } /* Terminating entry */ }; @@ -122,18 +123,32 @@ static int mt7921s_probe(struct sdio_func *func, .fw_own = mt7921s_mcu_fw_pmctrl, }; + struct ieee80211_ops *ops; struct mt7921_dev *dev; struct mt76_dev *mdev; + u8 features; int ret; - mdev = mt76_alloc_device(&func->dev, sizeof(*dev), &mt7921_ops, - &drv_ops); + features = mt7921_check_offload_capability(&func->dev, (const char *) + id->driver_data); + + ops = devm_kmemdup(&func->dev, &mt7921_ops, sizeof(mt7921_ops), + GFP_KERNEL); + if (!ops) + return -ENOMEM; + + if (!(features & MT7921_FW_CAP_CNM)) { + ops->remain_on_channel = NULL; + ops->cancel_remain_on_channel = NULL; + } + + mdev = mt76_alloc_device(&func->dev, sizeof(*dev), ops, &drv_ops); if (!mdev) return -ENOMEM; dev = container_of(mdev, struct mt7921_dev, mt76); + dev->fw_features = features; dev->hif_ops = &mt7921_sdio_ops; - sdio_set_drvdata(func, dev); ret = mt76s_init(mdev, func, &mt7921s_ops); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c index 89249f0b6aba..8a49d3de2073 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c @@ -13,7 +13,8 @@ #include "mac.h" static const struct usb_device_id mt7921u_device_table[] = { - { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7961, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7961, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)MT7921_FIRMWARE_WM }, { }, }; @@ -204,13 +205,21 @@ static int mt7921u_probe(struct usb_interface *usb_intf, struct ieee80211_hw *hw; struct mt7921_dev *dev; struct mt76_dev *mdev; + u8 features; int ret; + features = mt7921_check_offload_capability(&usb_intf->dev, (const char *) + id->driver_info); ops = devm_kmemdup(&usb_intf->dev, &mt7921_ops, sizeof(mt7921_ops), GFP_KERNEL); if (!ops) return -ENOMEM; + if (!(features & MT7921_FW_CAP_CNM)) { + ops->remain_on_channel = NULL; + ops->cancel_remain_on_channel = NULL; + } + ops->stop = mt7921u_stop; mdev = mt76_alloc_device(&usb_intf->dev, sizeof(*dev), ops, &drv_ops); @@ -218,6 +227,7 @@ static int mt7921u_probe(struct usb_interface *usb_intf, return -ENOMEM; dev = container_of(mdev, struct mt7921_dev, mt76); + dev->fw_features = features; dev->hif_ops = &hif_ops; udev = usb_get_dev(udev); From 41ac53c899bd1493a75ab9b52c8f76176b7419e1 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Tue, 15 Nov 2022 08:11:24 +0800 Subject: [PATCH 107/144] wifi: mt76: mt7921: introduce chanctx support The firmware can have the capability to manage the channel context scheduling on multiple roles running on the device including Station, AP and P2P GC/GO mode (will be extended based on the future patchset) to help users sharing the network with others on a single device. The firmware is able to support the channel chanctx up to 2 interface simultaneously running on the different channels. Another thing to be noted is that before the driver is going sent out the management frames, the driver has to get the privilege from the firmware to occupy the current channel context until the frame handshake is completed and then get the privilege back to the firmware. Co-developed-by: Deren Wu Signed-off-by: Deren Wu Signed-off-by: Sean Wang Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7921/init.c | 33 +++++- .../net/wireless/mediatek/mt76/mt7921/mac.c | 8 -- .../net/wireless/mediatek/mt76/mt7921/main.c | 111 +++++++++++++++++- .../wireless/mediatek/mt76/mt7921/mt7921.h | 1 + .../net/wireless/mediatek/mt76/mt7921/pci.c | 7 ++ .../net/wireless/mediatek/mt76/mt7921/sdio.c | 7 ++ .../net/wireless/mediatek/mt76/mt7921/usb.c | 7 ++ 7 files changed, 157 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/init.c b/drivers/net/wireless/mediatek/mt76/mt7921/init.c index e9353e9929a9..79b8055ce4c4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/init.c @@ -26,6 +26,27 @@ static const struct ieee80211_iface_combination if_comb[] = { .max_interfaces = MT7921_MAX_INTERFACES, .num_different_channels = 1, .beacon_int_infra_match = true, + }, +}; + +static const struct ieee80211_iface_limit if_limits_chanctx[] = { + { + .max = 2, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_AP), + } +}; + +static const struct ieee80211_iface_combination if_comb_chanctx[] = { + { + .limits = if_limits_chanctx, + .n_limits = ARRAY_SIZE(if_limits_chanctx), + .max_interfaces = 2, + .num_different_channels = 2, + .beacon_int_infra_match = false, } }; @@ -66,17 +87,19 @@ mt7921_init_wiphy(struct ieee80211_hw *hw) hw->sta_data_size = sizeof(struct mt7921_sta); hw->vif_data_size = sizeof(struct mt7921_vif); - if (dev->fw_features & MT7921_FW_CAP_CNM) + if (dev->fw_features & MT7921_FW_CAP_CNM) { wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; - else + wiphy->iface_combinations = if_comb_chanctx; + wiphy->n_iface_combinations = ARRAY_SIZE(if_comb_chanctx); + } else { wiphy->flags &= ~WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; - - wiphy->iface_combinations = if_comb; + wiphy->iface_combinations = if_comb; + wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); + } wiphy->flags &= ~(WIPHY_FLAG_IBSS_RSN | WIPHY_FLAG_4ADDR_AP | WIPHY_FLAG_4ADDR_STATION); wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP); - wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); wiphy->max_remain_on_channel_duration = 5000; wiphy->max_scan_ie_len = MT76_CONNAC_SCAN_IE_LEN; wiphy->max_scan_ssids = 4; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c index eeab756240a2..443a4a49bc9e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c @@ -168,14 +168,6 @@ static void mt7921_get_status_freq_info(struct mt7921_dev *dev, struct mt76_phy *mphy, struct mt76_rx_status *status, u8 chfreq) { - if (!test_bit(MT76_HW_SCANNING, &mphy->state) && - !test_bit(MT76_HW_SCHED_SCANNING, &mphy->state) && - !test_bit(MT76_STATE_ROC, &mphy->state)) { - status->freq = mphy->chandef.chan->center_freq; - status->band = mphy->chandef.chan->band; - return; - } - if (chfreq > 180) { status->band = NL80211_BAND_6GHZ; chfreq = (chfreq - 181) * 4 + 1; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c index 012c92d49f49..41df17efdb3a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -858,7 +858,7 @@ void mt7921_mac_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif, if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) mt76_connac_mcu_uni_add_bss(&dev->mphy, vif, &mvif->sta.wcid, - true, NULL); + true, mvif->ctx); mt7921_mac_wtbl_update(dev, msta->wcid.idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); @@ -891,7 +891,7 @@ void mt7921_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, if (!sta->tdls) mt76_connac_mcu_uni_add_bss(&dev->mphy, vif, &mvif->sta.wcid, false, - NULL); + mvif->ctx); } spin_lock_bh(&dev->sta_poll_lock); @@ -1645,7 +1645,7 @@ mt7921_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, mt7921_mutex_acquire(dev); err = mt76_connac_mcu_uni_add_bss(phy->mt76, vif, &mvif->sta.wcid, - true, NULL); + true, mvif->ctx); if (err) goto out; @@ -1677,12 +1677,108 @@ mt7921_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, goto out; mt76_connac_mcu_uni_add_bss(phy->mt76, vif, &mvif->sta.wcid, false, - NULL); + mvif->ctx); out: mt7921_mutex_release(dev); } +static int +mt7921_add_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + return 0; +} + +static void +mt7921_remove_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ +} + +static void mt7921_ctx_iter(void *priv, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; + struct ieee80211_chanctx_conf *ctx = priv; + + if (ctx != mvif->ctx) + return; + + mt76_connac_mcu_uni_set_chctx(mvif->phy->mt76, &mvif->mt76, ctx); +} + +static void +mt7921_change_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx, + u32 changed) +{ + struct mt7921_phy *phy = mt7921_hw_phy(hw); + + mt7921_mutex_acquire(phy->dev); + ieee80211_iterate_active_interfaces(phy->mt76->hw, + IEEE80211_IFACE_ITER_ACTIVE, + mt7921_ctx_iter, ctx); + mt7921_mutex_release(phy->dev); +} + +static int +mt7921_assign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_chanctx_conf *ctx) +{ + struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; + struct mt7921_dev *dev = mt7921_hw_dev(hw); + + mutex_lock(&dev->mt76.mutex); + mvif->ctx = ctx; + mutex_unlock(&dev->mt76.mutex); + + return 0; +} + +static void +mt7921_unassign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_chanctx_conf *ctx) +{ + struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; + struct mt7921_dev *dev = mt7921_hw_dev(hw); + + mutex_lock(&dev->mt76.mutex); + mvif->ctx = NULL; + mutex_unlock(&dev->mt76.mutex); +} + +static void mt7921_mgd_prepare_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_prep_tx_info *info) +{ + struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; + struct mt7921_dev *dev = mt7921_hw_dev(hw); + u16 duration = info->duration ? info->duration : + jiffies_to_msecs(HZ); + + mt7921_mutex_acquire(dev); + mt7921_set_roc(mvif->phy, mvif, mvif->ctx->def.chan, duration, + MT7921_ROC_REQ_JOIN); + mt7921_mutex_release(dev); +} + +static void mt7921_mgd_complete_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_prep_tx_info *info) +{ + struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv; + struct mt7921_dev *dev = mt7921_hw_dev(hw); + + mt7921_mutex_acquire(dev); + mt7921_abort_roc(mvif->phy, mvif); + mt7921_mutex_release(dev); +} + const struct ieee80211_ops mt7921_ops = { .tx = mt7921_tx, .start = mt7921_start, @@ -1735,6 +1831,13 @@ const struct ieee80211_ops mt7921_ops = { .set_sar_specs = mt7921_set_sar_specs, .remain_on_channel = mt7921_remain_on_channel, .cancel_remain_on_channel = mt7921_cancel_remain_on_channel, + .add_chanctx = mt7921_add_chanctx, + .remove_chanctx = mt7921_remove_chanctx, + .change_chanctx = mt7921_change_chanctx, + .assign_vif_chanctx = mt7921_assign_vif_chanctx, + .unassign_vif_chanctx = mt7921_unassign_vif_chanctx, + .mgd_prepare_tx = mt7921_mgd_prepare_tx, + .mgd_complete_tx = mt7921_mgd_complete_tx, }; EXPORT_SYMBOL_GPL(mt7921_ops); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h index 4f0147e40bc3..ac70a978dbed 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h @@ -171,6 +171,7 @@ struct mt7921_vif { struct ewma_rssi rssi; struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS]; + struct ieee80211_chanctx_conf *ctx; }; struct mib_stats { diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c index 32ad26ef3a68..28342ec940f0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c @@ -297,6 +297,13 @@ static int mt7921_pci_probe(struct pci_dev *pdev, if (!(features & MT7921_FW_CAP_CNM)) { ops->remain_on_channel = NULL; ops->cancel_remain_on_channel = NULL; + ops->add_chanctx = NULL; + ops->remove_chanctx = NULL; + ops->change_chanctx = NULL; + ops->assign_vif_chanctx = NULL; + ops->unassign_vif_chanctx = NULL; + ops->mgd_prepare_tx = NULL; + ops->mgd_complete_tx = NULL; } mdev = mt76_alloc_device(&pdev->dev, sizeof(*dev), ops, &drv_ops); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c index e07a8fb87421..8ce4252b8ae7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c @@ -140,6 +140,13 @@ static int mt7921s_probe(struct sdio_func *func, if (!(features & MT7921_FW_CAP_CNM)) { ops->remain_on_channel = NULL; ops->cancel_remain_on_channel = NULL; + ops->add_chanctx = NULL; + ops->remove_chanctx = NULL; + ops->change_chanctx = NULL; + ops->assign_vif_chanctx = NULL; + ops->unassign_vif_chanctx = NULL; + ops->mgd_prepare_tx = NULL; + ops->mgd_complete_tx = NULL; } mdev = mt76_alloc_device(&func->dev, sizeof(*dev), ops, &drv_ops); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c index 8a49d3de2073..5321d20dcdcb 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c @@ -218,6 +218,13 @@ static int mt7921u_probe(struct usb_interface *usb_intf, if (!(features & MT7921_FW_CAP_CNM)) { ops->remain_on_channel = NULL; ops->cancel_remain_on_channel = NULL; + ops->add_chanctx = NULL; + ops->remove_chanctx = NULL; + ops->change_chanctx = NULL; + ops->assign_vif_chanctx = NULL; + ops->unassign_vif_chanctx = NULL; + ops->mgd_prepare_tx = NULL; + ops->mgd_complete_tx = NULL; } ops->stop = mt7921u_stop; From 4c07129b05e42a0415ee409bf5958ac62e7e969e Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Thu, 10 Nov 2022 17:35:17 +0800 Subject: [PATCH 108/144] wifi: mt76: connac: rework macros for unified command Rework some macros for connac3 unified command. This is a preliminary patch to add mt7996 chipset support. Signed-off-by: Peter Chiu Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt76_connac_mcu.h | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index a4893331b7c3..d0a4331f6f63 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -63,7 +63,7 @@ struct mt76_connac2_mcu_txd { } __packed __aligned(4); /** - * struct mt76_connac2_mcu_uni_txd - mcu command descriptor for firmware v3 + * struct mt76_connac2_mcu_uni_txd - mcu command descriptor for connac2 and connac3 * @txd: hardware descriptor * @len: total length not including txd * @cid: command identifier @@ -1079,10 +1079,11 @@ enum { #define MCU_CMD_ACK BIT(0) #define MCU_CMD_UNI BIT(1) -#define MCU_CMD_QUERY BIT(2) +#define MCU_CMD_SET BIT(2) #define MCU_CMD_UNI_EXT_ACK (MCU_CMD_ACK | MCU_CMD_UNI | \ - MCU_CMD_QUERY) + MCU_CMD_SET) +#define MCU_CMD_UNI_QUERY_ACK (MCU_CMD_ACK | MCU_CMD_UNI) #define __MCU_CMD_FIELD_ID GENMASK(7, 0) #define __MCU_CMD_FIELD_EXT_ID GENMASK(15, 8) @@ -1090,6 +1091,7 @@ enum { #define __MCU_CMD_FIELD_UNI BIT(17) #define __MCU_CMD_FIELD_CE BIT(18) #define __MCU_CMD_FIELD_WA BIT(19) +#define __MCU_CMD_FIELD_WM BIT(20) #define MCU_CMD(_t) FIELD_PREP(__MCU_CMD_FIELD_ID, \ MCU_CMD_##_t) @@ -1111,6 +1113,16 @@ enum { FIELD_PREP(__MCU_CMD_FIELD_EXT_ID, \ MCU_WA_PARAM_CMD_##_t)) +#define MCU_WM_UNI_CMD(_t) (MCU_UNI_CMD(_t) | \ + __MCU_CMD_FIELD_WM) +#define MCU_WM_UNI_CMD_QUERY(_t) (MCU_UNI_CMD(_t) | \ + __MCU_CMD_FIELD_QUERY | \ + __MCU_CMD_FIELD_WM) +#define MCU_WA_UNI_CMD(_t) (MCU_UNI_CMD(_t) | \ + __MCU_CMD_FIELD_WA) +#define MCU_WMWA_UNI_CMD(_t) (MCU_WM_UNI_CMD(_t) | \ + __MCU_CMD_FIELD_WA) + enum { MCU_EXT_CMD_EFUSE_ACCESS = 0x01, MCU_EXT_CMD_RF_REG_ACCESS = 0x02, From 6deaf96ddd0b6894d7f36513d74635b40e337a26 Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Thu, 10 Nov 2022 17:35:18 +0800 Subject: [PATCH 109/144] wifi: mt76: connac: update struct sta_rec_phy For connac3 generation, max_ampdu_len should be filled in struct sta_rec_phy. This is a preliminary patch to add mt7996 chipset support. Signed-off-by: Peter Chiu Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index d0a4331f6f63..97965b03c5e2 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -403,7 +403,8 @@ struct sta_rec_phy { u8 ampdu; u8 rts_policy; u8 rcpi; - u8 rsv[2]; + u8 max_ampdu_len; /* connac3 */ + u8 rsv[1]; } __packed; struct sta_rec_he_6g_capa { From cade693944ea0799cb5f15168dcc4447401f7d9e Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Thu, 10 Nov 2022 17:35:19 +0800 Subject: [PATCH 110/144] wifi: mt76: connac: rework fields for larger bandwidth support in sta_rec_bf Rename nrow_bw160 and ncol_bw160 since channel width 320 MHz will share the same field. This is a preliminary patch to add mt7996 chipset support. Signed-off-by: MeiChia Chiu Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h | 4 ++-- drivers/net/wireless/mediatek/mt76/mt7915/mcu.c | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index 97965b03c5e2..d79fa68cf8b6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -465,8 +465,8 @@ struct sta_rec_bf { u8 ibf_dbw; u8 ibf_ncol; u8 ibf_nrow; - u8 nrow_bw160; - u8 ncol_bw160; + u8 nrow_gt_bw80; + u8 ncol_gt_bw80; u8 ru_start_idx; u8 ru_end_idx; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 17e13fd0ede9..8b903a7636a8 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -1131,7 +1131,7 @@ mt7915_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif, mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_160); nss_mcs = mt7915_mcu_get_sta_nss(mcs_map); - bf->ncol_bw160 = nss_mcs; + bf->ncol_gt_bw80 = nss_mcs; } if (pe->phy_cap_info[0] & @@ -1139,10 +1139,10 @@ mt7915_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif, mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80p80); nss_mcs = mt7915_mcu_get_sta_nss(mcs_map); - if (bf->ncol_bw160) - bf->ncol_bw160 = min_t(u8, bf->ncol_bw160, nss_mcs); + if (bf->ncol_gt_bw80) + bf->ncol_gt_bw80 = min_t(u8, bf->ncol_gt_bw80, nss_mcs); else - bf->ncol_bw160 = nss_mcs; + bf->ncol_gt_bw80 = nss_mcs; } snd_dim = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK, @@ -1150,7 +1150,7 @@ mt7915_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif, sts = HE_PHY(CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_MASK, pe->phy_cap_info[4]); - bf->nrow_bw160 = min_t(int, snd_dim, sts); + bf->nrow_gt_bw80 = min_t(int, snd_dim, sts); } static void From 779d34de055eb8b4cdf54d5429a50b560eb88727 Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Thu, 10 Nov 2022 17:35:20 +0800 Subject: [PATCH 111/144] wifi: mt76: connac: add more unified command IDs Add more unified command IDs which will be used in new chipset. This is the preliminary patch for mt7996 support. Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt76_connac_mcu.h | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index d79fa68cf8b6..e3c59b63295d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -1177,11 +1177,33 @@ enum { MCU_UNI_CMD_DEV_INFO_UPDATE = 0x01, MCU_UNI_CMD_BSS_INFO_UPDATE = 0x02, MCU_UNI_CMD_STA_REC_UPDATE = 0x03, + MCU_UNI_CMD_EDCA_UPDATE = 0x04, MCU_UNI_CMD_SUSPEND = 0x05, MCU_UNI_CMD_OFFLOAD = 0x06, MCU_UNI_CMD_HIF_CTRL = 0x07, + MCU_UNI_CMD_BAND_CONFIG = 0x08, + MCU_UNI_CMD_REPT_MUAR = 0x09, + MCU_UNI_CMD_WSYS_CONFIG = 0x0b, + MCU_UNI_CMD_REG_ACCESS = 0x0d, + MCU_UNI_CMD_POWER_CREL = 0x0f, + MCU_UNI_CMD_RX_HDR_TRANS = 0x12, + MCU_UNI_CMD_SER = 0x13, + MCU_UNI_CMD_TWT = 0x14, + MCU_UNI_CMD_RDD_CTRL = 0x19, + MCU_UNI_CMD_GET_MIB_INFO = 0x22, MCU_UNI_CMD_SNIFFER = 0x24, + MCU_UNI_CMD_SR = 0x25, MCU_UNI_CMD_ROC = 0x27, + MCU_UNI_CMD_TXPOWER = 0x2b, + MCU_UNI_CMD_EFUSE_CTRL = 0x2d, + MCU_UNI_CMD_RA = 0x2f, + MCU_UNI_CMD_MURU = 0x31, + MCU_UNI_CMD_BF = 0x33, + MCU_UNI_CMD_CHANNEL_SWITCH = 0x34, + MCU_UNI_CMD_THERMAL = 0x35, + MCU_UNI_CMD_VOW = 0x37, + MCU_UNI_CMD_RRO = 0x57, + MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58, }; enum { From ec361f7e415297dc07254e93ac6af1dbce4af87b Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Thu, 10 Nov 2022 17:35:21 +0800 Subject: [PATCH 112/144] wifi: mt76: connac: introduce unified event table Add event IDs and related bit fields for unified event support. This is a preliminary patch to add mt7996 chipset support. Signed-off-by: Bo Jiao Signed-off-by: Peter Chiu Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index e3c59b63295d..c46a93466727 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -998,6 +998,17 @@ enum { MCU_EXT_EVENT_MURU_CTRL = 0x9f, }; +/* unified event table */ +enum { + MCU_UNI_EVENT_RESULT = 0x01, + MCU_UNI_EVENT_FW_LOG_2_HOST = 0x04, + MCU_UNI_EVENT_IE_COUNTDOWN = 0x09, + MCU_UNI_EVENT_RDD_REPORT = 0x11, +}; + +#define MCU_UNI_CMD_EVENT BIT(1) +#define MCU_UNI_CMD_UNSOLICITED_EVENT BIT(2) + enum { MCU_Q_QUERY, MCU_Q_SET, From 9c402ac1c517907ae375d71dd2fee4703efc8bb4 Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Thu, 10 Nov 2022 17:35:22 +0800 Subject: [PATCH 113/144] wifi: mt76: connac: add more bss info command tags Add bss info tags which will be used in new chipset. This is a preliminary patch to add mt7996 chipset support. Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index c46a93466727..90cc9b8f628c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -1264,14 +1264,23 @@ enum { enum { UNI_BSS_INFO_BASIC = 0, + UNI_BSS_INFO_RA = 1, UNI_BSS_INFO_RLM = 2, UNI_BSS_INFO_BSS_COLOR = 4, UNI_BSS_INFO_HE_BASIC = 5, UNI_BSS_INFO_BCN_CONTENT = 7, + UNI_BSS_INFO_BCN_CSA = 8, + UNI_BSS_INFO_BCN_BCC = 9, + UNI_BSS_INFO_BCN_MBSSID = 10, + UNI_BSS_INFO_RATE = 11, UNI_BSS_INFO_QBSS = 15, + UNI_BSS_INFO_SEC = 16, + UNI_BSS_INFO_TXCMD = 18, UNI_BSS_INFO_UAPSD = 19, UNI_BSS_INFO_PS = 21, UNI_BSS_INFO_BCNFT = 22, + UNI_BSS_INFO_OFFLOAD = 25, + UNI_BSS_INFO_MLD = 26, }; enum { From 98f191b1d782f069709a0d77f5980ae83a17a6ab Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Thu, 10 Nov 2022 17:35:23 +0800 Subject: [PATCH 114/144] wifi: mt76: connac: add more starec command tags Add starec tags which will be used in new chipset. This is a preliminary patch to add mt7996 chipset support. Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index 90cc9b8f628c..e8049ac12a84 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -793,6 +793,8 @@ enum { STA_REC_PHY = 0x15, STA_REC_HE_6G = 0x17, STA_REC_HE_V2 = 0x19, + STA_REC_HDRT = 0x28, + STA_REC_HDR_TRANS = 0x2B, STA_REC_MAX_NUM }; From a6cf2b65fb5b37aef61f168b676a7ecd1ca85294 Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Thu, 10 Nov 2022 17:35:24 +0800 Subject: [PATCH 115/144] wifi: mt76: connac: introduce helper for mt7996 chipset Introduce is_mt7996() helper for new chipset. This is a preliminary patch to support mt7996 driver. Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac.h | 5 +++++ drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac.h index 0915eb579539..8ba883b03e50 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac.h @@ -187,6 +187,11 @@ static inline bool is_mt7986(struct mt76_dev *dev) return mt76_chip(dev) == 0x7986; } +static inline bool is_mt7996(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7990; +} + static inline bool is_mt7622(struct mt76_dev *dev) { if (!IS_ENABLED(CONFIG_MT7622_WMAC)) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index 6b2ce5fd3e4f..b8480d97c0f8 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -65,7 +65,8 @@ int mt76_connac_mcu_init_download(struct mt76_dev *dev, u32 addr, u32 len, int cmd; if ((!is_connac_v1(dev) && addr == MCU_PATCH_ADDRESS) || - (is_mt7921(dev) && addr == 0x900000)) + (is_mt7921(dev) && addr == 0x900000) || + (is_mt7996(dev) && addr == 0x900000)) cmd = MCU_CMD(PATCH_START_REQ); else cmd = MCU_CMD(TARGET_ADDRESS_LEN_REQ); From 7eefb93d4a6fbccd859e538d208c50fd10b44cb7 Mon Sep 17 00:00:00 2001 From: YN Chen Date: Wed, 16 Nov 2022 22:43:02 +0800 Subject: [PATCH 116/144] wifi: mt76: mt7921: fix wrong power after multiple SAR set We should update CLC config before SAR set to synchronize all related settings. Fixes: 23bdc5d8cadf ("wifi: mt76: mt7921: introduce Country Location Control support") Signed-off-by: YN Chen Signed-off-by: Deren Wu Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7921/init.c | 1 + drivers/net/wireless/mediatek/mt76/mt7921/main.c | 6 ++++++ drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h | 2 ++ 3 files changed, 9 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/init.c b/drivers/net/wireless/mediatek/mt76/mt7921/init.c index 79b8055ce4c4..542dfd425129 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/init.c @@ -59,6 +59,7 @@ mt7921_regd_notifier(struct wiphy *wiphy, memcpy(dev->mt76.alpha2, request->alpha2, sizeof(dev->mt76.alpha2)); dev->mt76.region = request->dfs_region; + dev->country_ie_env = request->country_ie_env; mt7921_mutex_acquire(dev); mt7921_mcu_set_clc(dev, request->alpha2, request->country_ie_env); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c index 41df17efdb3a..76ac5069638f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -1615,7 +1615,13 @@ static int mt7921_set_sar_specs(struct ieee80211_hw *hw, int err; mt7921_mutex_acquire(dev); + err = mt7921_mcu_set_clc(dev, dev->mt76.alpha2, + dev->country_ie_env); + if (err < 0) + goto out; + err = mt7921_set_tx_sar_pwr(hw, sar); +out: mt7921_mutex_release(dev); return err; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h index ac70a978dbed..2398c6fd0b39 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h @@ -304,6 +304,8 @@ struct mt7921_dev { struct work_struct ipv6_ns_work; /* IPv6 addresses for WoWLAN */ struct sk_buff_head ipv6_ns_list; + + enum environment_cap country_ie_env; }; enum { From 2666bece0905a3e8ccb792602dbc76a63aaafe4b Mon Sep 17 00:00:00 2001 From: Sujuan Chen Date: Sat, 12 Nov 2022 16:40:34 +0100 Subject: [PATCH 117/144] wifi: mt76: introduce rxwi and rx token utility routines This is a preliminary patch to introduce WED RX support for mt7915. Tested-by: Daniel Golle Co-developed-by: Lorenzo Bianconi Signed-off-by: Lorenzo Bianconi Signed-off-by: Sujuan Chen Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/dma.c | 68 +++++++++++++++++++ drivers/net/wireless/mediatek/mt76/dma.h | 8 +++ drivers/net/wireless/mediatek/mt76/mac80211.c | 5 ++ drivers/net/wireless/mediatek/mt76/mt76.h | 16 ++++- drivers/net/wireless/mediatek/mt76/tx.c | 30 ++++++++ 5 files changed, 126 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index 7378c4d1e156..d316bde01c6b 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -59,6 +59,19 @@ mt76_alloc_txwi(struct mt76_dev *dev) return t; } +static struct mt76_txwi_cache * +mt76_alloc_rxwi(struct mt76_dev *dev) +{ + struct mt76_txwi_cache *t; + + t = kzalloc(L1_CACHE_ALIGN(sizeof(*t)), GFP_ATOMIC); + if (!t) + return NULL; + + t->ptr = NULL; + return t; +} + static struct mt76_txwi_cache * __mt76_get_txwi(struct mt76_dev *dev) { @@ -75,6 +88,22 @@ __mt76_get_txwi(struct mt76_dev *dev) return t; } +static struct mt76_txwi_cache * +__mt76_get_rxwi(struct mt76_dev *dev) +{ + struct mt76_txwi_cache *t = NULL; + + spin_lock(&dev->wed_lock); + if (!list_empty(&dev->rxwi_cache)) { + t = list_first_entry(&dev->rxwi_cache, struct mt76_txwi_cache, + list); + list_del(&t->list); + } + spin_unlock(&dev->wed_lock); + + return t; +} + static struct mt76_txwi_cache * mt76_get_txwi(struct mt76_dev *dev) { @@ -86,6 +115,18 @@ mt76_get_txwi(struct mt76_dev *dev) return mt76_alloc_txwi(dev); } +struct mt76_txwi_cache * +mt76_get_rxwi(struct mt76_dev *dev) +{ + struct mt76_txwi_cache *t = __mt76_get_rxwi(dev); + + if (t) + return t; + + return mt76_alloc_rxwi(dev); +} +EXPORT_SYMBOL_GPL(mt76_get_rxwi); + void mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t) { @@ -98,6 +139,18 @@ mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t) } EXPORT_SYMBOL_GPL(mt76_put_txwi); +void +mt76_put_rxwi(struct mt76_dev *dev, struct mt76_txwi_cache *t) +{ + if (!t) + return; + + spin_lock(&dev->wed_lock); + list_add(&t->list, &dev->rxwi_cache); + spin_unlock(&dev->wed_lock); +} +EXPORT_SYMBOL_GPL(mt76_put_rxwi); + static void mt76_free_pending_txwi(struct mt76_dev *dev) { @@ -112,6 +165,20 @@ mt76_free_pending_txwi(struct mt76_dev *dev) local_bh_enable(); } +static void +mt76_free_pending_rxwi(struct mt76_dev *dev) +{ + struct mt76_txwi_cache *t; + + local_bh_disable(); + while ((t = __mt76_get_rxwi(dev)) != NULL) { + if (t->ptr) + skb_free_frag(t->ptr); + kfree(t); + } + local_bh_enable(); +} + static void mt76_dma_sync_idx(struct mt76_dev *dev, struct mt76_queue *q) { @@ -808,6 +875,7 @@ void mt76_dma_cleanup(struct mt76_dev *dev) } mt76_free_pending_txwi(dev); + mt76_free_pending_rxwi(dev); if (mtk_wed_device_active(&dev->mmio.wed)) mtk_wed_device_detach(&dev->mmio.wed); diff --git a/drivers/net/wireless/mediatek/mt76/dma.h b/drivers/net/wireless/mediatek/mt76/dma.h index fdf786f975ea..53c6ce2528b2 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.h +++ b/drivers/net/wireless/mediatek/mt76/dma.h @@ -15,6 +15,14 @@ #define MT_DMA_CTL_SD_LEN0 GENMASK(29, 16) #define MT_DMA_CTL_LAST_SEC0 BIT(30) #define MT_DMA_CTL_DMA_DONE BIT(31) +#define MT_DMA_CTL_TO_HOST BIT(8) +#define MT_DMA_CTL_TO_HOST_A BIT(12) +#define MT_DMA_CTL_DROP BIT(14) +#define MT_DMA_CTL_TOKEN GENMASK(31, 16) + +#define MT_DMA_PPE_CPU_REASON GENMASK(15, 11) +#define MT_DMA_PPE_ENTRY GENMASK(30, 16) +#define MT_DMA_INFO_PPE_VLD BIT(31) #define MT_DMA_HDR_LEN 4 #define MT_RX_INFO_LEN 4 diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index c59d12004459..3cd37a013dcc 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -572,6 +572,7 @@ mt76_alloc_device(struct device *pdev, unsigned int size, spin_lock_init(&dev->lock); spin_lock_init(&dev->cc_lock); spin_lock_init(&dev->status_lock); + spin_lock_init(&dev->wed_lock); mutex_init(&dev->mutex); init_waitqueue_head(&dev->tx_wait); @@ -594,9 +595,13 @@ mt76_alloc_device(struct device *pdev, unsigned int size, spin_lock_init(&dev->token_lock); idr_init(&dev->token); + spin_lock_init(&dev->rx_token_lock); + idr_init(&dev->rx_token); + INIT_LIST_HEAD(&dev->wcid_list); INIT_LIST_HEAD(&dev->txwi_cache); + INIT_LIST_HEAD(&dev->rxwi_cache); dev->token_size = dev->drv->token_size; for (i = 0; i < ARRAY_SIZE(dev->q_rx); i++) diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index a2bccf6b6c54..149dc6eb7eb9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -339,7 +339,10 @@ struct mt76_txwi_cache { struct list_head list; dma_addr_t dma_addr; - struct sk_buff *skb; + union { + struct sk_buff *skb; + void *ptr; + }; }; struct mt76_rx_tid { @@ -728,6 +731,7 @@ struct mt76_dev { struct ieee80211_hw *hw; + spinlock_t wed_lock; spinlock_t lock; spinlock_t cc_lock; @@ -754,6 +758,7 @@ struct mt76_dev { struct sk_buff_head rx_skb[__MT_RXQ_MAX]; struct list_head txwi_cache; + struct list_head rxwi_cache; struct mt76_queue *q_mcu[__MT_MCUQ_MAX]; struct mt76_queue q_rx[__MT_RXQ_MAX]; const struct mt76_queue_ops *queue_ops; @@ -768,6 +773,10 @@ struct mt76_dev { u16 token_count; u16 token_size; + spinlock_t rx_token_lock; + struct idr rx_token; + u16 rx_token_size; + wait_queue_head_t tx_wait; /* spinclock used to protect wcid pktid linked list */ spinlock_t status_lock; @@ -1247,6 +1256,8 @@ mt76_tx_status_get_hw(struct mt76_dev *dev, struct sk_buff *skb) } void mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t); +void mt76_put_rxwi(struct mt76_dev *dev, struct mt76_txwi_cache *t); +struct mt76_txwi_cache *mt76_get_rxwi(struct mt76_dev *dev); void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames, struct napi_struct *napi); void mt76_rx_poll_complete(struct mt76_dev *dev, enum mt76_rxq_id q, @@ -1391,6 +1402,9 @@ struct mt76_txwi_cache * mt76_token_release(struct mt76_dev *dev, int token, bool *wake); int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi); void __mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked); +struct mt76_txwi_cache *mt76_rx_token_release(struct mt76_dev *dev, int token); +int mt76_rx_token_consume(struct mt76_dev *dev, void *ptr, + struct mt76_txwi_cache *r, dma_addr_t phys); static inline void mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked) { diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c index 6c054850363f..24568b98ed9d 100644 --- a/drivers/net/wireless/mediatek/mt76/tx.c +++ b/drivers/net/wireless/mediatek/mt76/tx.c @@ -756,6 +756,23 @@ int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi) } EXPORT_SYMBOL_GPL(mt76_token_consume); +int mt76_rx_token_consume(struct mt76_dev *dev, void *ptr, + struct mt76_txwi_cache *t, dma_addr_t phys) +{ + int token; + + spin_lock_bh(&dev->rx_token_lock); + token = idr_alloc(&dev->rx_token, t, 0, dev->rx_token_size, + GFP_ATOMIC); + spin_unlock_bh(&dev->rx_token_lock); + + t->ptr = ptr; + t->dma_addr = phys; + + return token; +} +EXPORT_SYMBOL_GPL(mt76_rx_token_consume); + struct mt76_txwi_cache * mt76_token_release(struct mt76_dev *dev, int token, bool *wake) { @@ -784,3 +801,16 @@ mt76_token_release(struct mt76_dev *dev, int token, bool *wake) return txwi; } EXPORT_SYMBOL_GPL(mt76_token_release); + +struct mt76_txwi_cache * +mt76_rx_token_release(struct mt76_dev *dev, int token) +{ + struct mt76_txwi_cache *t; + + spin_lock_bh(&dev->rx_token_lock); + t = idr_remove(&dev->rx_token, token); + spin_unlock_bh(&dev->rx_token_lock); + + return t; +} +EXPORT_SYMBOL_GPL(mt76_rx_token_release); From cd372b8c99c5a5cf6a464acebb7e4a79af7ec8ae Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 12 Nov 2022 16:40:35 +0100 Subject: [PATCH 118/144] wifi: mt76: add WED RX support to mt76_dma_{add,get}_buf Introduce the capability to configure RX WED in mt76_dma_{add,get}_buf utility routines. Tested-by: Daniel Golle Co-developed-by: Sujuan Chen Signed-off-by: Sujuan Chen Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/dma.c | 121 +++++++++++++++------- drivers/net/wireless/mediatek/mt76/mt76.h | 2 + 2 files changed, 86 insertions(+), 37 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index d316bde01c6b..4239adde4cca 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -215,11 +215,6 @@ mt76_dma_add_buf(struct mt76_dev *dev, struct mt76_queue *q, u32 ctrl; int i, idx = -1; - if (txwi) { - q->entry[q->head].txwi = DMA_DUMMY_DATA; - q->entry[q->head].skip_buf0 = true; - } - for (i = 0; i < nbufs; i += 2, buf += 2) { u32 buf0 = buf[0].addr, buf1 = 0; @@ -229,28 +224,48 @@ mt76_dma_add_buf(struct mt76_dev *dev, struct mt76_queue *q, desc = &q->desc[idx]; entry = &q->entry[idx]; - if (buf[0].skip_unmap) - entry->skip_buf0 = true; - entry->skip_buf1 = i == nbufs - 1; + if ((q->flags & MT_QFLAG_WED) && + FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX) { + struct mt76_txwi_cache *t = txwi; + int rx_token; - entry->dma_addr[0] = buf[0].addr; - entry->dma_len[0] = buf[0].len; + if (!t) + return -ENOMEM; - ctrl = FIELD_PREP(MT_DMA_CTL_SD_LEN0, buf[0].len); - if (i < nbufs - 1) { - entry->dma_addr[1] = buf[1].addr; - entry->dma_len[1] = buf[1].len; - buf1 = buf[1].addr; - ctrl |= FIELD_PREP(MT_DMA_CTL_SD_LEN1, buf[1].len); - if (buf[1].skip_unmap) - entry->skip_buf1 = true; + rx_token = mt76_rx_token_consume(dev, (void *)skb, t, + buf[0].addr); + buf1 |= FIELD_PREP(MT_DMA_CTL_TOKEN, rx_token); + ctrl = FIELD_PREP(MT_DMA_CTL_SD_LEN0, buf[0].len) | + MT_DMA_CTL_TO_HOST; + } else { + if (txwi) { + q->entry[q->head].txwi = DMA_DUMMY_DATA; + q->entry[q->head].skip_buf0 = true; + } + + if (buf[0].skip_unmap) + entry->skip_buf0 = true; + entry->skip_buf1 = i == nbufs - 1; + + entry->dma_addr[0] = buf[0].addr; + entry->dma_len[0] = buf[0].len; + + ctrl = FIELD_PREP(MT_DMA_CTL_SD_LEN0, buf[0].len); + if (i < nbufs - 1) { + entry->dma_addr[1] = buf[1].addr; + entry->dma_len[1] = buf[1].len; + buf1 = buf[1].addr; + ctrl |= FIELD_PREP(MT_DMA_CTL_SD_LEN1, buf[1].len); + if (buf[1].skip_unmap) + entry->skip_buf1 = true; + } + + if (i == nbufs - 1) + ctrl |= MT_DMA_CTL_LAST_SEC0; + else if (i == nbufs - 2) + ctrl |= MT_DMA_CTL_LAST_SEC1; } - if (i == nbufs - 1) - ctrl |= MT_DMA_CTL_LAST_SEC0; - else if (i == nbufs - 2) - ctrl |= MT_DMA_CTL_LAST_SEC1; - WRITE_ONCE(desc->buf0, cpu_to_le32(buf0)); WRITE_ONCE(desc->buf1, cpu_to_le32(buf1)); WRITE_ONCE(desc->info, cpu_to_le32(info)); @@ -339,33 +354,60 @@ mt76_dma_tx_cleanup(struct mt76_dev *dev, struct mt76_queue *q, bool flush) static void * mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx, - int *len, u32 *info, bool *more) + int *len, u32 *info, bool *more, bool *drop) { struct mt76_queue_entry *e = &q->entry[idx]; struct mt76_desc *desc = &q->desc[idx]; - dma_addr_t buf_addr; - void *buf = e->buf; - int buf_len = SKB_WITH_OVERHEAD(q->buf_size); + void *buf; - buf_addr = e->dma_addr[0]; if (len) { - u32 ctl = le32_to_cpu(READ_ONCE(desc->ctrl)); - *len = FIELD_GET(MT_DMA_CTL_SD_LEN0, ctl); - *more = !(ctl & MT_DMA_CTL_LAST_SEC0); + u32 ctrl = le32_to_cpu(READ_ONCE(desc->ctrl)); + *len = FIELD_GET(MT_DMA_CTL_SD_LEN0, ctrl); + *more = !(ctrl & MT_DMA_CTL_LAST_SEC0); } if (info) *info = le32_to_cpu(desc->info); - dma_unmap_single(dev->dma_dev, buf_addr, buf_len, DMA_FROM_DEVICE); - e->buf = NULL; + if ((q->flags & MT_QFLAG_WED) && + FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX) { + u32 token = FIELD_GET(MT_DMA_CTL_TOKEN, + le32_to_cpu(desc->buf1)); + struct mt76_txwi_cache *t = mt76_rx_token_release(dev, token); + + if (!t) + return NULL; + + dma_unmap_single(dev->dma_dev, t->dma_addr, + SKB_WITH_OVERHEAD(q->buf_size), + DMA_FROM_DEVICE); + + buf = t->ptr; + t->dma_addr = 0; + t->ptr = NULL; + + mt76_put_rxwi(dev, t); + + if (drop) { + u32 ctrl = le32_to_cpu(READ_ONCE(desc->ctrl)); + + *drop = !!(ctrl & (MT_DMA_CTL_TO_HOST_A | + MT_DMA_CTL_DROP)); + } + } else { + buf = e->buf; + e->buf = NULL; + dma_unmap_single(dev->dma_dev, e->dma_addr[0], + SKB_WITH_OVERHEAD(q->buf_size), + DMA_FROM_DEVICE); + } return buf; } static void * mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush, - int *len, u32 *info, bool *more) + int *len, u32 *info, bool *more, bool *drop) { int idx = q->tail; @@ -381,7 +423,7 @@ mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush, q->tail = (q->tail + 1) % q->ndesc; q->queued--; - return mt76_dma_get_buf(dev, q, idx, len, info, more); + return mt76_dma_get_buf(dev, q, idx, len, info, more, drop); } static int @@ -641,7 +683,7 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q) spin_lock_bh(&q->lock); do { - buf = mt76_dma_dequeue(dev, q, true, NULL, NULL, &more); + buf = mt76_dma_dequeue(dev, q, true, NULL, NULL, &more, NULL); if (!buf) break; @@ -723,6 +765,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget) } while (done < budget) { + bool drop = false; u32 info; if (check_ddone) { @@ -733,10 +776,14 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget) break; } - data = mt76_dma_dequeue(dev, q, false, &len, &info, &more); + data = mt76_dma_dequeue(dev, q, false, &len, &info, &more, + &drop); if (!data) break; + if (drop) + goto free_frag; + if (q->rx_head) data_len = q->buf_size; else diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index 149dc6eb7eb9..738fb22d9198 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -35,6 +35,7 @@ FIELD_PREP(MT_QFLAG_WED_TYPE, _type) | \ FIELD_PREP(MT_QFLAG_WED_RING, _n)) #define MT_WED_Q_TX(_n) __MT_WED_Q(MT76_WED_Q_TX, _n) +#define MT_WED_Q_RX(_n) __MT_WED_Q(MT76_WED_Q_RX, _n) #define MT_WED_Q_TXFREE __MT_WED_Q(MT76_WED_Q_TXFREE, 0) struct mt76_dev; @@ -56,6 +57,7 @@ enum mt76_bus_type { enum mt76_wed_type { MT76_WED_Q_TX, MT76_WED_Q_TXFREE, + MT76_WED_Q_RX, }; struct mt76_bus_ops { From c601baaf5cd89deda3643beee4b8a57ef151985c Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 12 Nov 2022 16:40:36 +0100 Subject: [PATCH 119/144] wifi: mt76: add WED RX support to mt76_dma_rx_fill Introduce the capability to refill WED RX buffers in mt76_dma_rx_fill utility routine. Tested-by: Daniel Golle Co-developed-by: Sujuan Chen Signed-off-by: Sujuan Chen Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/dma.c | 33 +++++++++++++++++++----- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index 4239adde4cca..cb6e3b358aca 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -550,14 +550,26 @@ free_skb: return ret; } +static struct page_frag_cache * +mt76_dma_rx_get_frag_cache(struct mt76_dev *dev, struct mt76_queue *q) +{ + struct page_frag_cache *rx_page = &q->rx_page; + +#ifdef CONFIG_NET_MEDIATEK_SOC_WED + if ((q->flags & MT_QFLAG_WED) && + FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX) + rx_page = &dev->mmio.wed.rx_buf_ring.rx_page; +#endif + return rx_page; +} + static int mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q) { - dma_addr_t addr; - void *buf; - int frames = 0; + struct page_frag_cache *rx_page = mt76_dma_rx_get_frag_cache(dev, q); int len = SKB_WITH_OVERHEAD(q->buf_size); - int offset = q->buf_offset; + int frames = 0, offset = q->buf_offset; + dma_addr_t addr; if (!q->ndesc) return 0; @@ -565,9 +577,18 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q) spin_lock_bh(&q->lock); while (q->queued < q->ndesc - 1) { + struct mt76_txwi_cache *t = NULL; struct mt76_queue_buf qbuf; + void *buf = NULL; - buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC); + if ((q->flags & MT_QFLAG_WED) && + FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX) { + t = mt76_get_rxwi(dev); + if (!t) + break; + } + + buf = page_frag_alloc(rx_page, q->buf_size, GFP_ATOMIC); if (!buf) break; @@ -580,7 +601,7 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q) qbuf.addr = addr + offset; qbuf.len = len - offset; qbuf.skip_unmap = false; - mt76_dma_add_buf(dev, q, &qbuf, 1, 0, buf, NULL); + mt76_dma_add_buf(dev, q, &qbuf, 1, 0, buf, t); frames++; } From 52546e27787ed005413ac622ae9e4c3087100b50 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 12 Nov 2022 16:40:37 +0100 Subject: [PATCH 120/144] wifi: mt76: add WED RX support to dma queue alloc Introduce the capability to allocate WED RX buffers in mt76_dma_wed_setup routine. Tested-by: Daniel Golle Co-developed-by: Sujuan Chen Signed-off-by: Sujuan Chen Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/dma.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index cb6e3b358aca..58b41bda5eac 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -647,6 +647,11 @@ mt76_dma_wed_setup(struct mt76_dev *dev, struct mt76_queue *q) if (!ret) q->wed_regs = wed->txfree_ring.reg_base; break; + case MT76_WED_Q_RX: + ret = mtk_wed_device_rx_ring_setup(wed, ring, q->regs); + if (!ret) + q->wed_regs = wed->rx_ring[ring].reg_base; + break; default: ret = -EINVAL; } @@ -938,8 +943,11 @@ void mt76_dma_cleanup(struct mt76_dev *dev) mt76_dma_tx_cleanup(dev, dev->q_mcu[i], true); mt76_for_each_q_rx(dev, i) { + struct mt76_queue *q = &dev->q_rx[i]; + netif_napi_del(&dev->napi[i]); - mt76_dma_rx_cleanup(dev, &dev->q_rx[i]); + if (FIELD_GET(MT_QFLAG_WED_TYPE, q->flags)) + mt76_dma_rx_cleanup(dev, q); } mt76_free_pending_txwi(dev); From c3137942771eceef923158980b6cccae572e81d1 Mon Sep 17 00:00:00 2001 From: Sujuan Chen Date: Sat, 12 Nov 2022 16:40:38 +0100 Subject: [PATCH 121/144] wifi: mt76: add info parameter to rx_skb signature This is a preliminary patch to introduce WED RX support for mt7915. Tested-by: Daniel Golle Co-developed-by: Lorenzo Bianconi Signed-off-by: Lorenzo Bianconi Signed-off-by: Sujuan Chen Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/dma.c | 8 ++-- drivers/net/wireless/mediatek/mt76/mt76.h | 2 +- .../net/wireless/mediatek/mt76/mt7603/dma.c | 2 +- .../wireless/mediatek/mt76/mt7603/mt7603.h | 2 +- .../net/wireless/mediatek/mt76/mt7615/mac.c | 2 +- .../wireless/mediatek/mt76/mt7615/mt7615.h | 2 +- drivers/net/wireless/mediatek/mt76/mt76x02.h | 2 +- .../net/wireless/mediatek/mt76/mt76x02_txrx.c | 2 +- .../net/wireless/mediatek/mt76/mt7915/mac.c | 43 ++++++++++++++++--- .../wireless/mediatek/mt76/mt7915/mt7915.h | 2 +- .../net/wireless/mediatek/mt76/mt7921/mac.c | 2 +- .../wireless/mediatek/mt76/mt7921/mt7921.h | 2 +- drivers/net/wireless/mediatek/mt76/sdio.c | 2 +- drivers/net/wireless/mediatek/mt76/usb.c | 2 +- 14 files changed, 53 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index 58b41bda5eac..8dca8d2447b7 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -750,7 +750,7 @@ mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid) static void mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data, - int len, bool more) + int len, bool more, u32 info) { struct sk_buff *skb = q->rx_head; struct skb_shared_info *shinfo = skb_shinfo(skb); @@ -770,7 +770,7 @@ mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data, q->rx_head = NULL; if (nr_frags < ARRAY_SIZE(shinfo->frags)) - dev->drv->rx_skb(dev, q - dev->q_rx, skb); + dev->drv->rx_skb(dev, q - dev->q_rx, skb, &info); else dev_kfree_skb(skb); } @@ -822,7 +822,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget) } if (q->rx_head) { - mt76_add_fragment(dev, q, data, len, more); + mt76_add_fragment(dev, q, data, len, more, info); continue; } @@ -846,7 +846,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget) continue; } - dev->drv->rx_skb(dev, q - dev->q_rx, skb); + dev->drv->rx_skb(dev, q - dev->q_rx, skb, &info); continue; free_frag: diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index 738fb22d9198..bf4ad629df3f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -444,7 +444,7 @@ struct mt76_driver_ops { bool (*rx_check)(struct mt76_dev *dev, void *data, int len); void (*rx_skb)(struct mt76_dev *dev, enum mt76_rxq_id q, - struct sk_buff *skb); + struct sk_buff *skb, u32 *info); void (*rx_poll_complete)(struct mt76_dev *dev, enum mt76_rxq_id q); diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/dma.c b/drivers/net/wireless/mediatek/mt76/mt7603/dma.c index f9e5857850e7..03ba11a61c90 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt7603/dma.c @@ -69,7 +69,7 @@ free: } void mt7603_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, - struct sk_buff *skb) + struct sk_buff *skb, u32 *info) { struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); __le32 *rxd = (__le32 *)skb->data; diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h b/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h index 0fd46d907638..7c3be596da09 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h +++ b/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h @@ -244,7 +244,7 @@ int mt7603_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, void mt7603_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e); void mt7603_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, - struct sk_buff *skb); + struct sk_buff *skb, u32 *info); void mt7603_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q); void mt7603_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps); int mt7603_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c index 305bf1826a02..a95602473359 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c @@ -1666,7 +1666,7 @@ bool mt7615_rx_check(struct mt76_dev *mdev, void *data, int len) EXPORT_SYMBOL_GPL(mt7615_rx_check); void mt7615_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, - struct sk_buff *skb) + struct sk_buff *skb, u32 *info) { struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76); __le32 *rxd = (__le32 *)skb->data; diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h index 8b37f8259f52..087d4886162e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h @@ -513,7 +513,7 @@ void mt7615_tx_worker(struct mt76_worker *w); void mt7615_tx_token_put(struct mt7615_dev *dev); bool mt7615_rx_check(struct mt76_dev *mdev, void *data, int len); void mt7615_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, - struct sk_buff *skb); + struct sk_buff *skb, u32 *info); void mt7615_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps); int mt7615_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, struct ieee80211_sta *sta); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02.h b/drivers/net/wireless/mediatek/mt76/mt76x02.h index a19176bb2433..4cd63bacd742 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x02.h @@ -188,7 +188,7 @@ int mt76x02_set_rts_threshold(struct ieee80211_hw *hw, u32 val); void mt76x02_remove_hdr_pad(struct sk_buff *skb, int len); bool mt76x02_tx_status_data(struct mt76_dev *mdev, u8 *update); void mt76x02_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, - struct sk_buff *skb); + struct sk_buff *skb, u32 *info); void mt76x02_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q); irqreturn_t mt76x02_irq_handler(int irq, void *dev_instance); void mt76x02_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c b/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c index 3a313075a9e3..d8bc4ae185f5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c @@ -33,7 +33,7 @@ void mt76x02_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, EXPORT_SYMBOL_GPL(mt76x02_tx); void mt76x02_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, - struct sk_buff *skb) + struct sk_buff *skb, u32 *info) { struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76); void *rxwi = skb->data; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 117ddb00348c..4f0b3c57e545 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -165,9 +165,9 @@ static void mt7915_mac_sta_poll(struct mt7915_dev *dev) sta = container_of((void *)msta, struct ieee80211_sta, drv_priv); for (i = 0; i < IEEE80211_NUM_ACS; i++) { - u8 q = mt76_connac_lmac_mapping(i); - u32 tx_cur = tx_time[q]; - u32 rx_cur = rx_time[q]; + u8 queue = mt76_connac_lmac_mapping(i); + u32 tx_cur = tx_time[queue]; + u32 rx_cur = rx_time[queue]; u8 tid = ac_to_tid[i]; if (!tx_cur && !rx_cur) @@ -245,8 +245,37 @@ void mt7915_mac_enable_rtscts(struct mt7915_dev *dev, mt76_clear(dev, addr, BIT(5)); } +static void +mt7915_wed_check_ppe(struct mt7915_dev *dev, struct mt76_queue *q, + struct mt7915_sta *msta, struct sk_buff *skb, + u32 info) +{ + struct ieee80211_vif *vif; + struct wireless_dev *wdev; + + if (!msta || !msta->vif) + return; + + if (!(q->flags & MT_QFLAG_WED) || + FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) != MT76_WED_Q_RX) + return; + + if (!(info & MT_DMA_INFO_PPE_VLD)) + return; + + vif = container_of((void *)msta->vif, struct ieee80211_vif, + drv_priv); + wdev = ieee80211_vif_to_wdev(vif); + skb->dev = wdev->netdev; + + mtk_wed_device_ppe_check(&dev->mt76.mmio.wed, skb, + FIELD_GET(MT_DMA_PPE_CPU_REASON, info), + FIELD_GET(MT_DMA_PPE_ENTRY, info)); +} + static int -mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb) +mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb, + enum mt76_rxq_id q, u32 *info) { struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; struct mt76_phy *mphy = &dev->mt76.phy; @@ -513,6 +542,8 @@ mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb) } } else { status->flag |= RX_FLAG_8023; + mt7915_wed_check_ppe(dev, &dev->mt76.q_rx[q], msta, skb, + *info); } if (rxv && mode >= MT_PHY_TYPE_HE_SU && !(status->flag & RX_FLAG_8023)) @@ -1096,7 +1127,7 @@ bool mt7915_rx_check(struct mt76_dev *mdev, void *data, int len) } void mt7915_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, - struct sk_buff *skb) + struct sk_buff *skb, u32 *info) { struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); __le32 *rxd = (__le32 *)skb->data; @@ -1130,7 +1161,7 @@ void mt7915_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, dev_kfree_skb(skb); break; case PKT_TYPE_NORMAL: - if (!mt7915_mac_fill_rx(dev, skb)) { + if (!mt7915_mac_fill_rx(dev, skb, q, info)) { mt76_rx(&dev->mt76, q, skb); return; } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index 9cb680e7f223..460be184e617 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -617,7 +617,7 @@ int mt7915_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, struct mt76_tx_info *tx_info); void mt7915_tx_token_put(struct mt7915_dev *dev); void mt7915_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, - struct sk_buff *skb); + struct sk_buff *skb, u32 *info); bool mt7915_rx_check(struct mt76_dev *mdev, void *data, int len); void mt7915_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps); void mt7915_stats_work(struct work_struct *work); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c index 443a4a49bc9e..82db3762be33 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c @@ -684,7 +684,7 @@ bool mt7921_rx_check(struct mt76_dev *mdev, void *data, int len) EXPORT_SYMBOL_GPL(mt7921_rx_check); void mt7921_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, - struct sk_buff *skb) + struct sk_buff *skb, u32 *info) { struct mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76); __le32 *rxd = (__le32 *)skb->data; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h index 2398c6fd0b39..6fc04ed34ec3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h @@ -470,7 +470,7 @@ void mt7921_tx_worker(struct mt76_worker *w); void mt7921_tx_token_put(struct mt7921_dev *dev); bool mt7921_rx_check(struct mt76_dev *mdev, void *data, int len); void mt7921_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, - struct sk_buff *skb); + struct sk_buff *skb, u32 *info); void mt7921_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps); void mt7921_stats_work(struct work_struct *work); void mt7921_set_stream_he_caps(struct mt7921_phy *phy); diff --git a/drivers/net/wireless/mediatek/mt76/sdio.c b/drivers/net/wireless/mediatek/mt76/sdio.c index 0ec308f99af5..228bc7d45011 100644 --- a/drivers/net/wireless/mediatek/mt76/sdio.c +++ b/drivers/net/wireless/mediatek/mt76/sdio.c @@ -395,7 +395,7 @@ mt76s_process_rx_queue(struct mt76_dev *dev, struct mt76_queue *q) if (!e || !e->skb) break; - dev->drv->rx_skb(dev, MT_RXQ_MAIN, e->skb); + dev->drv->rx_skb(dev, MT_RXQ_MAIN, e->skb, NULL); e->skb = NULL; nframes++; } diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c index 0597df2729a6..3e281715fcd4 100644 --- a/drivers/net/wireless/mediatek/mt76/usb.c +++ b/drivers/net/wireless/mediatek/mt76/usb.c @@ -547,7 +547,7 @@ mt76u_process_rx_entry(struct mt76_dev *dev, struct urb *urb, len -= data_len; nsgs++; } - dev->drv->rx_skb(dev, MT_RXQ_MAIN, skb); + dev->drv->rx_skb(dev, MT_RXQ_MAIN, skb, NULL); return nsgs; } From d1369e515efe6b5d04e9a5f5884106643f7b1d8a Mon Sep 17 00:00:00 2001 From: Sujuan Chen Date: Sat, 12 Nov 2022 16:40:39 +0100 Subject: [PATCH 122/144] wifi: mt76: connac: introduce mt76_connac_mcu_sta_wed_update utility routine This is a preliminary patch to introduce WED RX support for mt7915. Tested-by: Daniel Golle Co-developed-by: Lorenzo Bianconi Signed-off-by: Lorenzo Bianconi Signed-off-by: Sujuan Chen Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt76_connac_mcu.c | 22 +++++++++++++++++++ .../wireless/mediatek/mt76/mt76_connac_mcu.h | 1 + 2 files changed, 23 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index b8480d97c0f8..96d91238ad8d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -1219,6 +1219,16 @@ void mt76_connac_mcu_sta_ba_tlv(struct sk_buff *skb, } EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_ba_tlv); +int mt76_connac_mcu_sta_wed_update(struct mt76_dev *dev, struct sk_buff *skb) +{ + if (!mtk_wed_device_active(&dev->mmio.wed)) + return 0; + + return mtk_wed_device_update_msg(&dev->mmio.wed, WED_WO_STA_REC, + skb->data, skb->len); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_wed_update); + int mt76_connac_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif, struct ieee80211_ampdu_params *params, int cmd, bool enable, bool tx) @@ -1244,6 +1254,10 @@ int mt76_connac_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif, mt76_connac_mcu_wtbl_ba_tlv(dev, skb, params, enable, tx, sta_wtbl, wtbl_hdr); + ret = mt76_connac_mcu_sta_wed_update(dev, skb); + if (ret) + return ret; + ret = mt76_mcu_skb_send_msg(dev, skb, cmd, true); if (ret) return ret; @@ -1254,6 +1268,10 @@ int mt76_connac_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif, mt76_connac_mcu_sta_ba_tlv(skb, params, enable, tx); + ret = mt76_connac_mcu_sta_wed_update(dev, skb); + if (ret) + return ret; + return mt76_mcu_skb_send_msg(dev, skb, cmd, true); } EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_ba); @@ -2696,6 +2714,10 @@ int mt76_connac_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif, if (ret) return ret; + ret = mt76_connac_mcu_sta_wed_update(dev, skb); + if (ret) + return ret; + return mt76_mcu_skb_send_msg(dev, skb, mcu_cmd, true); } EXPORT_SYMBOL_GPL(mt76_connac_mcu_add_key); diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index e8049ac12a84..f1e942b9a887 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -1891,6 +1891,7 @@ int mt76_connac_mcu_set_pm(struct mt76_dev *dev, int band, int enter); int mt76_connac_mcu_restart(struct mt76_dev *dev); int mt76_connac_mcu_rdd_cmd(struct mt76_dev *dev, int cmd, u8 index, u8 rx_sel, u8 val); +int mt76_connac_mcu_sta_wed_update(struct mt76_dev *dev, struct sk_buff *skb); int mt76_connac2_load_ram(struct mt76_dev *dev, const char *fw_wm, const char *fw_wa); int mt76_connac2_load_patch(struct mt76_dev *dev, const char *fw_name); From 4f831d18d12da80cec0bebe5b8ca8702a528195a Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 12 Nov 2022 16:40:40 +0100 Subject: [PATCH 123/144] wifi: mt76: mt7915: enable WED RX support Enable RX Wireless Ethernet Dispatch available on MT7986 Soc in oreder to offlad traffic received by WLAN NIC and forwarded to LAN/WAN one. Tested-by: Daniel Golle Co-developed-by: Sujuan Chen Signed-off-by: Sujuan Chen Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mac80211.c | 5 +- .../net/wireless/mediatek/mt76/mt7915/dma.c | 20 +- .../net/wireless/mediatek/mt76/mt7915/mcu.c | 4 + .../net/wireless/mediatek/mt76/mt7915/mmio.c | 293 ++++++++++++------ .../wireless/mediatek/mt76/mt7915/mt7915.h | 2 + .../net/wireless/mediatek/mt76/mt7915/regs.h | 7 + 6 files changed, 238 insertions(+), 93 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index 3cd37a013dcc..fc608b369b3c 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -1297,7 +1297,10 @@ void mt76_rx_poll_complete(struct mt76_dev *dev, enum mt76_rxq_id q, while ((skb = __skb_dequeue(&dev->rx_skb[q])) != NULL) { mt76_check_sta(dev, skb); - mt76_rx_aggr_reorder(skb, &frames); + if (mtk_wed_device_active(&dev->mmio.wed)) + __skb_queue_tail(&frames, skb); + else + mt76_rx_aggr_reorder(skb, &frames); } mt76_rx_complete(dev, &frames, napi); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c index 38360f940747..e102a717fc01 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c @@ -365,7 +365,8 @@ static int mt7915_dma_enable(struct mt7915_dev *dev) wed_irq_mask |= MT_INT_TX_DONE_BAND0 | MT_INT_TX_DONE_BAND1; if (!is_mt7986(&dev->mt76)) mt76_wr(dev, MT_INT_WED_MASK_CSR, wed_irq_mask); - mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask); + else + mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask); mtk_wed_device_start(&dev->mt76.mmio.wed, wed_irq_mask); } @@ -401,6 +402,9 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2) FIELD_PREP(MT_WFDMA_WED_RING_CONTROL_TX1, 19) | FIELD_PREP(MT_WFDMA_WED_RING_CONTROL_RX1, wed_control_rx1)); + if (is_mt7915(mdev)) + mt76_rmw(dev, MT_WFDMA0_EXT0_CFG, MT_WFDMA0_EXT0_RXWB_KEEP, + MT_WFDMA0_EXT0_RXWB_KEEP); } } else { mt76_clear(dev, MT_WFDMA_HOST_CONFIG, MT_WFDMA_HOST_CONFIG_WED); @@ -473,6 +477,13 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2) /* rx data queue for band0 */ if (!dev->phy.band_idx) { + if (mtk_wed_device_active(&mdev->mmio.wed) && + mtk_wed_get_rx_capa(&mdev->mmio.wed)) { + dev->mt76.q_rx[MT_RXQ_MAIN].flags = + MT_WED_Q_RX(MT7915_RXQ_BAND0); + dev->mt76.rx_token_size += MT7915_RX_RING_SIZE; + } + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN], MT_RXQ_ID(MT_RXQ_MAIN), MT7915_RX_RING_SIZE, @@ -503,6 +514,13 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2) } if (dev->dbdc_support || dev->phy.band_idx) { + if (mtk_wed_device_active(&mdev->mmio.wed) && + mtk_wed_get_rx_capa(&mdev->mmio.wed)) { + dev->mt76.q_rx[MT_RXQ_BAND1].flags = + MT_WED_Q_RX(MT7915_RXQ_BAND1); + dev->mt76.rx_token_size += MT7915_RX_RING_SIZE; + } + /* rx data queue for band1 */ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND1], MT_RXQ_ID(MT_RXQ_BAND1), diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 8b903a7636a8..c49efe20bf5e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -1677,6 +1677,10 @@ int mt7915_mcu_add_sta(struct mt7915_dev *dev, struct ieee80211_vif *vif, return ret; } out: + ret = mt76_connac_mcu_sta_wed_update(&dev->mt76, skb); + if (ret) + return ret; + return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_EXT_CMD(STA_REC_UPDATE), true); } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c index 3c840853a2c9..04f38755056f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -9,107 +9,111 @@ #include "mt7915.h" #include "mac.h" #include "../trace.h" +#include "../dma.h" static bool wed_enable; module_param(wed_enable, bool, 0644); static const u32 mt7915_reg[] = { - [INT_SOURCE_CSR] = 0xd7010, - [INT_MASK_CSR] = 0xd7014, - [INT1_SOURCE_CSR] = 0xd7088, - [INT1_MASK_CSR] = 0xd708c, - [INT_MCU_CMD_SOURCE] = 0xd51f0, - [INT_MCU_CMD_EVENT] = 0x3108, - [WFDMA0_ADDR] = 0xd4000, - [WFDMA0_PCIE1_ADDR] = 0xd8000, - [WFDMA_EXT_CSR_ADDR] = 0xd7000, - [CBTOP1_PHY_END] = 0x77ffffff, - [INFRA_MCU_ADDR_END] = 0x7c3fffff, - [FW_ASSERT_STAT_ADDR] = 0x219848, - [FW_EXCEPT_TYPE_ADDR] = 0x21987c, - [FW_EXCEPT_COUNT_ADDR] = 0x219848, - [FW_CIRQ_COUNT_ADDR] = 0x216f94, - [FW_CIRQ_IDX_ADDR] = 0x216ef8, - [FW_CIRQ_LISR_ADDR] = 0x2170ac, - [FW_TASK_ID_ADDR] = 0x216f90, - [FW_TASK_IDX_ADDR] = 0x216f9c, - [FW_TASK_QID1_ADDR] = 0x219680, - [FW_TASK_QID2_ADDR] = 0x219760, - [FW_TASK_START_ADDR] = 0x219558, - [FW_TASK_END_ADDR] = 0x219554, - [FW_TASK_SIZE_ADDR] = 0x219560, - [FW_LAST_MSG_ID_ADDR] = 0x216f70, - [FW_EINT_INFO_ADDR] = 0x219818, - [FW_SCHED_INFO_ADDR] = 0x219828, - [SWDEF_BASE_ADDR] = 0x41f200, - [TXQ_WED_RING_BASE] = 0xd7300, - [RXQ_WED_RING_BASE] = 0xd7410, + [INT_SOURCE_CSR] = 0xd7010, + [INT_MASK_CSR] = 0xd7014, + [INT1_SOURCE_CSR] = 0xd7088, + [INT1_MASK_CSR] = 0xd708c, + [INT_MCU_CMD_SOURCE] = 0xd51f0, + [INT_MCU_CMD_EVENT] = 0x3108, + [WFDMA0_ADDR] = 0xd4000, + [WFDMA0_PCIE1_ADDR] = 0xd8000, + [WFDMA_EXT_CSR_ADDR] = 0xd7000, + [CBTOP1_PHY_END] = 0x77ffffff, + [INFRA_MCU_ADDR_END] = 0x7c3fffff, + [FW_ASSERT_STAT_ADDR] = 0x219848, + [FW_EXCEPT_TYPE_ADDR] = 0x21987c, + [FW_EXCEPT_COUNT_ADDR] = 0x219848, + [FW_CIRQ_COUNT_ADDR] = 0x216f94, + [FW_CIRQ_IDX_ADDR] = 0x216ef8, + [FW_CIRQ_LISR_ADDR] = 0x2170ac, + [FW_TASK_ID_ADDR] = 0x216f90, + [FW_TASK_IDX_ADDR] = 0x216f9c, + [FW_TASK_QID1_ADDR] = 0x219680, + [FW_TASK_QID2_ADDR] = 0x219760, + [FW_TASK_START_ADDR] = 0x219558, + [FW_TASK_END_ADDR] = 0x219554, + [FW_TASK_SIZE_ADDR] = 0x219560, + [FW_LAST_MSG_ID_ADDR] = 0x216f70, + [FW_EINT_INFO_ADDR] = 0x219818, + [FW_SCHED_INFO_ADDR] = 0x219828, + [SWDEF_BASE_ADDR] = 0x41f200, + [TXQ_WED_RING_BASE] = 0xd7300, + [RXQ_WED_RING_BASE] = 0xd7410, + [RXQ_WED_DATA_RING_BASE] = 0xd4500, }; static const u32 mt7916_reg[] = { - [INT_SOURCE_CSR] = 0xd4200, - [INT_MASK_CSR] = 0xd4204, - [INT1_SOURCE_CSR] = 0xd8200, - [INT1_MASK_CSR] = 0xd8204, - [INT_MCU_CMD_SOURCE] = 0xd41f0, - [INT_MCU_CMD_EVENT] = 0x2108, - [WFDMA0_ADDR] = 0xd4000, - [WFDMA0_PCIE1_ADDR] = 0xd8000, - [WFDMA_EXT_CSR_ADDR] = 0xd7000, - [CBTOP1_PHY_END] = 0x7fffffff, - [INFRA_MCU_ADDR_END] = 0x7c085fff, - [FW_ASSERT_STAT_ADDR] = 0x02204c14, - [FW_EXCEPT_TYPE_ADDR] = 0x022051a4, - [FW_EXCEPT_COUNT_ADDR] = 0x022050bc, - [FW_CIRQ_COUNT_ADDR] = 0x022001ac, - [FW_CIRQ_IDX_ADDR] = 0x02204f84, - [FW_CIRQ_LISR_ADDR] = 0x022050d0, - [FW_TASK_ID_ADDR] = 0x0220406c, - [FW_TASK_IDX_ADDR] = 0x0220500c, - [FW_TASK_QID1_ADDR] = 0x022028c8, - [FW_TASK_QID2_ADDR] = 0x02202a38, - [FW_TASK_START_ADDR] = 0x0220286c, - [FW_TASK_END_ADDR] = 0x02202870, - [FW_TASK_SIZE_ADDR] = 0x02202878, - [FW_LAST_MSG_ID_ADDR] = 0x02204fe8, - [FW_EINT_INFO_ADDR] = 0x0220525c, - [FW_SCHED_INFO_ADDR] = 0x0220516c, - [SWDEF_BASE_ADDR] = 0x411400, - [TXQ_WED_RING_BASE] = 0xd7300, - [RXQ_WED_RING_BASE] = 0xd7410, + [INT_SOURCE_CSR] = 0xd4200, + [INT_MASK_CSR] = 0xd4204, + [INT1_SOURCE_CSR] = 0xd8200, + [INT1_MASK_CSR] = 0xd8204, + [INT_MCU_CMD_SOURCE] = 0xd41f0, + [INT_MCU_CMD_EVENT] = 0x2108, + [WFDMA0_ADDR] = 0xd4000, + [WFDMA0_PCIE1_ADDR] = 0xd8000, + [WFDMA_EXT_CSR_ADDR] = 0xd7000, + [CBTOP1_PHY_END] = 0x7fffffff, + [INFRA_MCU_ADDR_END] = 0x7c085fff, + [FW_ASSERT_STAT_ADDR] = 0x02204c14, + [FW_EXCEPT_TYPE_ADDR] = 0x022051a4, + [FW_EXCEPT_COUNT_ADDR] = 0x022050bc, + [FW_CIRQ_COUNT_ADDR] = 0x022001ac, + [FW_CIRQ_IDX_ADDR] = 0x02204f84, + [FW_CIRQ_LISR_ADDR] = 0x022050d0, + [FW_TASK_ID_ADDR] = 0x0220406c, + [FW_TASK_IDX_ADDR] = 0x0220500c, + [FW_TASK_QID1_ADDR] = 0x022028c8, + [FW_TASK_QID2_ADDR] = 0x02202a38, + [FW_TASK_START_ADDR] = 0x0220286c, + [FW_TASK_END_ADDR] = 0x02202870, + [FW_TASK_SIZE_ADDR] = 0x02202878, + [FW_LAST_MSG_ID_ADDR] = 0x02204fe8, + [FW_EINT_INFO_ADDR] = 0x0220525c, + [FW_SCHED_INFO_ADDR] = 0x0220516c, + [SWDEF_BASE_ADDR] = 0x411400, + [TXQ_WED_RING_BASE] = 0xd7300, + [RXQ_WED_RING_BASE] = 0xd7410, + [RXQ_WED_DATA_RING_BASE] = 0xd4540, }; static const u32 mt7986_reg[] = { - [INT_SOURCE_CSR] = 0x24200, - [INT_MASK_CSR] = 0x24204, - [INT1_SOURCE_CSR] = 0x28200, - [INT1_MASK_CSR] = 0x28204, - [INT_MCU_CMD_SOURCE] = 0x241f0, - [INT_MCU_CMD_EVENT] = 0x54000108, - [WFDMA0_ADDR] = 0x24000, - [WFDMA0_PCIE1_ADDR] = 0x28000, - [WFDMA_EXT_CSR_ADDR] = 0x27000, - [CBTOP1_PHY_END] = 0x7fffffff, - [INFRA_MCU_ADDR_END] = 0x7c085fff, - [FW_ASSERT_STAT_ADDR] = 0x02204b54, - [FW_EXCEPT_TYPE_ADDR] = 0x022050dc, - [FW_EXCEPT_COUNT_ADDR] = 0x02204ffc, - [FW_CIRQ_COUNT_ADDR] = 0x022001ac, - [FW_CIRQ_IDX_ADDR] = 0x02204ec4, - [FW_CIRQ_LISR_ADDR] = 0x02205010, - [FW_TASK_ID_ADDR] = 0x02204fac, - [FW_TASK_IDX_ADDR] = 0x02204f4c, - [FW_TASK_QID1_ADDR] = 0x02202814, - [FW_TASK_QID2_ADDR] = 0x02202984, - [FW_TASK_START_ADDR] = 0x022027b8, - [FW_TASK_END_ADDR] = 0x022027bc, - [FW_TASK_SIZE_ADDR] = 0x022027c4, - [FW_LAST_MSG_ID_ADDR] = 0x02204f28, - [FW_EINT_INFO_ADDR] = 0x02205194, - [FW_SCHED_INFO_ADDR] = 0x022051a4, - [SWDEF_BASE_ADDR] = 0x411400, - [TXQ_WED_RING_BASE] = 0x24420, - [RXQ_WED_RING_BASE] = 0x24520, + [INT_SOURCE_CSR] = 0x24200, + [INT_MASK_CSR] = 0x24204, + [INT1_SOURCE_CSR] = 0x28200, + [INT1_MASK_CSR] = 0x28204, + [INT_MCU_CMD_SOURCE] = 0x241f0, + [INT_MCU_CMD_EVENT] = 0x54000108, + [WFDMA0_ADDR] = 0x24000, + [WFDMA0_PCIE1_ADDR] = 0x28000, + [WFDMA_EXT_CSR_ADDR] = 0x27000, + [CBTOP1_PHY_END] = 0x7fffffff, + [INFRA_MCU_ADDR_END] = 0x7c085fff, + [FW_ASSERT_STAT_ADDR] = 0x02204b54, + [FW_EXCEPT_TYPE_ADDR] = 0x022050dc, + [FW_EXCEPT_COUNT_ADDR] = 0x02204ffc, + [FW_CIRQ_COUNT_ADDR] = 0x022001ac, + [FW_CIRQ_IDX_ADDR] = 0x02204ec4, + [FW_CIRQ_LISR_ADDR] = 0x02205010, + [FW_TASK_ID_ADDR] = 0x02204fac, + [FW_TASK_IDX_ADDR] = 0x02204f4c, + [FW_TASK_QID1_ADDR] = 0x02202814, + [FW_TASK_QID2_ADDR] = 0x02202984, + [FW_TASK_START_ADDR] = 0x022027b8, + [FW_TASK_END_ADDR] = 0x022027bc, + [FW_TASK_SIZE_ADDR] = 0x022027c4, + [FW_LAST_MSG_ID_ADDR] = 0x02204f28, + [FW_EINT_INFO_ADDR] = 0x02205194, + [FW_SCHED_INFO_ADDR] = 0x022051a4, + [SWDEF_BASE_ADDR] = 0x411400, + [TXQ_WED_RING_BASE] = 0x24420, + [RXQ_WED_RING_BASE] = 0x24520, + [RXQ_WED_DATA_RING_BASE] = 0x24540, }; static const u32 mt7915_offs[] = { @@ -585,6 +589,80 @@ static void mt7915_mmio_wed_offload_disable(struct mtk_wed_device *wed) mt76_clear(dev, MT_AGG_ACR4(phy->band_idx), MT_AGG_ACR_PPDU_TXS2H); } + +static void mt7915_wed_release_rx_buf(struct mtk_wed_device *wed) +{ + struct mt7915_dev *dev; + struct page *page; + int i; + + dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed); + for (i = 0; i < dev->mt76.rx_token_size; i++) { + struct mt76_txwi_cache *t; + + t = mt76_rx_token_release(&dev->mt76, i); + if (!t || !t->ptr) + continue; + + dma_unmap_single(dev->mt76.dma_dev, t->dma_addr, + wed->wlan.rx_size, DMA_FROM_DEVICE); + skb_free_frag(t->ptr); + t->ptr = NULL; + + mt76_put_rxwi(&dev->mt76, t); + } + + if (!wed->rx_buf_ring.rx_page.va) + return; + + page = virt_to_page(wed->rx_buf_ring.rx_page.va); + __page_frag_cache_drain(page, wed->rx_buf_ring.rx_page.pagecnt_bias); + memset(&wed->rx_buf_ring.rx_page, 0, sizeof(wed->rx_buf_ring.rx_page)); +} + +static u32 mt7915_wed_init_rx_buf(struct mtk_wed_device *wed, int size) +{ + struct mtk_rxbm_desc *desc = wed->rx_buf_ring.desc; + struct mt7915_dev *dev; + u32 length; + int i; + + dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed); + length = SKB_DATA_ALIGN(NET_SKB_PAD + wed->wlan.rx_size + + sizeof(struct skb_shared_info)); + + for (i = 0; i < size; i++) { + struct mt76_txwi_cache *t = mt76_get_rxwi(&dev->mt76); + dma_addr_t phy_addr; + int token; + void *ptr; + + ptr = page_frag_alloc(&wed->rx_buf_ring.rx_page, length, + GFP_KERNEL); + if (!ptr) + goto unmap; + + phy_addr = dma_map_single(dev->mt76.dma_dev, ptr, + wed->wlan.rx_size, + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(dev->mt76.dev, phy_addr))) { + skb_free_frag(ptr); + goto unmap; + } + + desc->buf0 = cpu_to_le32(phy_addr); + token = mt76_rx_token_consume(&dev->mt76, ptr, t, phy_addr); + desc->token |= cpu_to_le32(FIELD_PREP(MT_DMA_CTL_TOKEN, + token)); + desc++; + } + + return 0; + +unmap: + mt7915_wed_release_rx_buf(wed); + return -ENOMEM; +} #endif int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr, @@ -602,6 +680,10 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr, wed->wlan.pci_dev = pci_dev; wed->wlan.bus_type = MTK_WED_BUS_PCIE; + wed->wlan.base = devm_ioremap(dev->mt76.dev, + pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0)); + wed->wlan.phy_base = pci_resource_start(pci_dev, 0); wed->wlan.wpdma_int = pci_resource_start(pci_dev, 0) + MT_INT_WED_SOURCE_CSR; wed->wlan.wpdma_mask = pci_resource_start(pci_dev, 0) + @@ -612,6 +694,10 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr, MT_TXQ_WED_RING_BASE; wed->wlan.wpdma_txfree = pci_resource_start(pci_dev, 0) + MT_RXQ_WED_RING_BASE; + wed->wlan.wpdma_rx_glo = pci_resource_start(pci_dev, 0) + + MT_WPDMA_GLO_CFG; + wed->wlan.wpdma_rx = pci_resource_start(pci_dev, 0) + + MT_RXQ_WED_DATA_RING_BASE; } else { struct platform_device *plat_dev = pdev_ptr; struct resource *res; @@ -622,19 +708,44 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr, wed->wlan.platform_dev = plat_dev; wed->wlan.bus_type = MTK_WED_BUS_AXI; + wed->wlan.base = devm_ioremap(dev->mt76.dev, res->start, + resource_size(res)); + wed->wlan.phy_base = res->start; wed->wlan.wpdma_int = res->start + MT_INT_SOURCE_CSR; wed->wlan.wpdma_mask = res->start + MT_INT_MASK_CSR; wed->wlan.wpdma_tx = res->start + MT_TXQ_WED_RING_BASE; wed->wlan.wpdma_txfree = res->start + MT_RXQ_WED_RING_BASE; + wed->wlan.wpdma_rx_glo = res->start + MT_WPDMA_GLO_CFG; + wed->wlan.wpdma_rx = res->start + MT_RXQ_WED_DATA_RING_BASE; } wed->wlan.nbuf = 4096; wed->wlan.tx_tbit[0] = is_mt7915(&dev->mt76) ? 4 : 30; wed->wlan.tx_tbit[1] = is_mt7915(&dev->mt76) ? 5 : 31; - wed->wlan.txfree_tbit = is_mt7915(&dev->mt76) ? 1 : 2; + wed->wlan.txfree_tbit = is_mt7986(&dev->mt76) ? 2 : 1; wed->wlan.token_start = MT7915_TOKEN_SIZE - wed->wlan.nbuf; + wed->wlan.wcid_512 = !is_mt7915(&dev->mt76); + + wed->wlan.rx_nbuf = 65536; + wed->wlan.rx_npkt = MT7915_WED_RX_TOKEN_SIZE; + wed->wlan.rx_size = SKB_WITH_OVERHEAD(MT_RX_BUF_SIZE); + if (is_mt7915(&dev->mt76)) { + wed->wlan.rx_tbit[0] = 16; + wed->wlan.rx_tbit[1] = 17; + } else if (is_mt7986(&dev->mt76)) { + wed->wlan.rx_tbit[0] = 22; + wed->wlan.rx_tbit[1] = 23; + } else { + wed->wlan.rx_tbit[0] = 18; + wed->wlan.rx_tbit[1] = 19; + } + wed->wlan.init_buf = mt7915_wed_init_buf; wed->wlan.offload_enable = mt7915_mmio_wed_offload_enable; wed->wlan.offload_disable = mt7915_mmio_wed_offload_disable; + wed->wlan.init_rx_buf = mt7915_wed_init_rx_buf; + wed->wlan.release_rx_buf = mt7915_wed_release_rx_buf; + + dev->mt76.rx_token_size = wed->wlan.rx_npkt; if (mtk_wed_device_attach(wed)) return 0; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index 460be184e617..574f712b5fe1 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -68,6 +68,8 @@ #define MT7915_MIN_TWT_DUR 64 #define MT7915_MAX_QUEUE (MT_RXQ_BAND2 + __MT_MCUQ_MAX + 2) +#define MT7915_WED_RX_TOKEN_SIZE 12288 + struct mt7915_vif; struct mt7915_sta; struct mt7915_dfs_pulse; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h index 0c61f1256f3b..42a19e7b95d6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h @@ -43,6 +43,7 @@ enum reg_rev { SWDEF_BASE_ADDR, TXQ_WED_RING_BASE, RXQ_WED_RING_BASE, + RXQ_WED_DATA_RING_BASE, __MT_REG_MAX, }; @@ -588,9 +589,14 @@ enum offs_rev { #define MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2 BIT(21) #define MT_WFDMA0_RST_DTX_PTR MT_WFDMA0(0x20c) + +#define MT_WFDMA0_EXT0_CFG MT_WFDMA0(0x2b0) +#define MT_WFDMA0_EXT0_RXWB_KEEP BIT(10) + #define MT_WFDMA0_PRI_DLY_INT_CFG0 MT_WFDMA0(0x2f0) #define MT_WFDMA0_PRI_DLY_INT_CFG1 MT_WFDMA0(0x2f4) #define MT_WFDMA0_PRI_DLY_INT_CFG2 MT_WFDMA0(0x2f8) +#define MT_WPDMA_GLO_CFG MT_WFDMA0(0x208) /* WFDMA1 */ #define MT_WFDMA1_BASE 0xd5000 @@ -686,6 +692,7 @@ enum offs_rev { #define MT_TXQ_WED_RING_BASE __REG(TXQ_WED_RING_BASE) #define MT_RXQ_WED_RING_BASE __REG(RXQ_WED_RING_BASE) +#define MT_RXQ_WED_DATA_RING_BASE __REG(RXQ_WED_DATA_RING_BASE) #define MT_INT_SOURCE_CSR __REG(INT_SOURCE_CSR) #define MT_INT_MASK_CSR __REG(INT_MASK_CSR) From c6cde7b751ee6fb0d4c2216c2edd9b0b5ffee04d Mon Sep 17 00:00:00 2001 From: Sujuan Chen Date: Sat, 12 Nov 2022 16:40:41 +0100 Subject: [PATCH 124/144] wifi: mt76: mt7915: enable WED RX stats Introduce the capability to report WED RX stats to mac80211. Tested-by: Daniel Golle Co-developed-by: Lorenzo Bianconi Signed-off-by: Lorenzo Bianconi Signed-off-by: Sujuan Chen Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76.h | 6 +++++ .../net/wireless/mediatek/mt76/mt7915/dma.c | 6 +++++ .../net/wireless/mediatek/mt76/mt7915/main.c | 8 ++++++ .../net/wireless/mediatek/mt76/mt7915/mcu.c | 18 +++++++++++++ .../net/wireless/mediatek/mt76/mt7915/mmio.c | 26 +++++++++++++++++++ .../wireless/mediatek/mt76/mt7915/mt7915.h | 1 + 6 files changed, 65 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index bf4ad629df3f..33f87e518d68 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -273,9 +273,15 @@ struct mt76_sta_stats { u64 tx_nss[4]; /* 1, 2, 3, 4 */ u64 tx_mcs[16]; /* mcs idx */ u64 tx_bytes; + /* WED TX */ u32 tx_packets; u32 tx_retries; u32 tx_failed; + /* WED RX */ + u64 rx_bytes; + u32 rx_packets; + u32 rx_errors; + u32 rx_drops; }; enum mt76_wcid_flags { diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c index e102a717fc01..ae5be28fdd9d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c @@ -361,12 +361,18 @@ static int mt7915_dma_enable(struct mt7915_dev *dev) if (mtk_wed_device_active(&dev->mt76.mmio.wed)) { u32 wed_irq_mask = irq_mask; + int ret; wed_irq_mask |= MT_INT_TX_DONE_BAND0 | MT_INT_TX_DONE_BAND1; if (!is_mt7986(&dev->mt76)) mt76_wr(dev, MT_INT_WED_MASK_CSR, wed_irq_mask); else mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask); + + ret = mt7915_mcu_wed_enable_rx_stats(dev); + if (ret) + return ret; + mtk_wed_device_start(&dev->mt76.mmio.wed, wed_irq_mask); } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 3933f4f2d71d..90c5d8fe4f73 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -1037,6 +1037,14 @@ static void mt7915_sta_statistics(struct ieee80211_hw *hw, sinfo->tx_retries = msta->wcid.stats.tx_retries; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES); + + if (mtk_wed_get_rx_capa(&phy->dev->mt76.mmio.wed)) { + sinfo->rx_bytes = msta->wcid.stats.rx_bytes; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES64); + + sinfo->rx_packets = msta->wcid.stats.rx_packets; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS); + } } sinfo->ack_signal = (s8)msta->ack_signal; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index c49efe20bf5e..2769d6c897d9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -1685,6 +1685,24 @@ out: MCU_EXT_CMD(STA_REC_UPDATE), true); } +int mt7915_mcu_wed_enable_rx_stats(struct mt7915_dev *dev) +{ +#ifdef CONFIG_NET_MEDIATEK_SOC_WED + struct mtk_wed_device *wed = &dev->mt76.mmio.wed; + struct { + __le32 args[2]; + } req = { + .args[0] = cpu_to_le32(1), + .args[1] = cpu_to_le32(6), + }; + + return mtk_wed_device_update_msg(wed, MTK_WED_WO_CMD_RXCNT_CTRL, + &req, sizeof(req)); +#else + return 0; +#endif +} + int mt7915_mcu_add_dev_info(struct mt7915_phy *phy, struct ieee80211_vif *vif, bool enable) { diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c index 04f38755056f..1fcf34f57a16 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -663,6 +663,31 @@ unmap: mt7915_wed_release_rx_buf(wed); return -ENOMEM; } + +static void mt7915_mmio_wed_update_rx_stats(struct mtk_wed_device *wed, + struct mtk_wed_wo_rx_stats *stats) +{ + int idx = le16_to_cpu(stats->wlan_idx); + struct mt7915_dev *dev; + struct mt76_wcid *wcid; + + dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed); + + if (idx >= mt7915_wtbl_size(dev)) + return; + + rcu_read_lock(); + + wcid = rcu_dereference(dev->mt76.wcid[idx]); + if (wcid) { + wcid->stats.rx_bytes += le32_to_cpu(stats->rx_byte_cnt); + wcid->stats.rx_packets += le32_to_cpu(stats->rx_pkt_cnt); + wcid->stats.rx_errors += le32_to_cpu(stats->rx_err_cnt); + wcid->stats.rx_drops += le32_to_cpu(stats->rx_drop_cnt); + } + + rcu_read_unlock(); +} #endif int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr, @@ -744,6 +769,7 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr, wed->wlan.offload_disable = mt7915_mmio_wed_offload_disable; wed->wlan.init_rx_buf = mt7915_wed_init_rx_buf; wed->wlan.release_rx_buf = mt7915_wed_release_rx_buf; + wed->wlan.update_wo_rx_stats = mt7915_mmio_wed_update_rx_stats; dev->mt76.rx_token_size = wed->wlan.rx_npkt; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index 574f712b5fe1..3fc3c48997e4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -630,6 +630,7 @@ void mt7915_set_stream_vht_txbf_caps(struct mt7915_phy *phy); void mt7915_update_channel(struct mt76_phy *mphy); int mt7915_mcu_muru_debug_set(struct mt7915_dev *dev, bool enable); int mt7915_mcu_muru_debug_get(struct mt7915_phy *phy, void *ms); +int mt7915_mcu_wed_enable_rx_stats(struct mt7915_dev *dev); int mt7915_init_debugfs(struct mt7915_phy *phy); void mt7915_debugfs_rx_fw_monitor(struct mt7915_dev *dev, const void *data, int len); bool mt7915_debugfs_rx_log(struct mt7915_dev *dev, const void *data, int len); From 3a46582c91502df34c81946350344dd70b1c0a90 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Fri, 18 Nov 2022 01:09:46 +0800 Subject: [PATCH 125/144] wifi: mt76: mt7915: add missing MODULE_PARM_DESC Add documentation for module_param so that they're visible with modinfo command. Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/mmio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c index 1fcf34f57a16..3b4ede3b85ea 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -13,6 +13,7 @@ static bool wed_enable; module_param(wed_enable, bool, 0644); +MODULE_PARM_DESC(wed_enable, "Enable Wireless Ethernet Dispatch support"); static const u32 mt7915_reg[] = { [INT_SOURCE_CSR] = 0xd7010, From 3dc00ecf242ee3d75aa0b2aebaf65f69eafc8d7d Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Fri, 18 Nov 2022 01:09:47 +0800 Subject: [PATCH 126/144] wifi: mt76: mt7915: add support to configure spatial reuse parameter set The SPR parameter set comprises OBSS PD threshold for SRG and non SRG and Bitmap of BSS color and partial BSSID. This adds support to configure fields of SPR element to firmware. User can disable firmware SR algorithms by turning sr_scene_detect off. Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/main.c | 6 +- .../net/wireless/mediatek/mt76/mt7915/mcu.c | 202 ++++++++++++++++-- .../net/wireless/mediatek/mt76/mt7915/mcu.h | 22 ++ .../wireless/mediatek/mt76/mt7915/mt7915.h | 4 +- 4 files changed, 210 insertions(+), 24 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 90c5d8fe4f73..6ef4634fac6b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -601,10 +601,8 @@ static void mt7915_bss_info_changed(struct ieee80211_hw *hw, mt7915_mcu_add_sta(dev, vif, NULL, join); } - if (changed & BSS_CHANGED_ASSOC) { + if (changed & BSS_CHANGED_ASSOC) mt7915_mcu_add_bss_info(phy, vif, vif->cfg.assoc); - mt7915_mcu_add_obss_spr(dev, vif, info->he_obss_pd.enable); - } if (changed & BSS_CHANGED_ERP_CTS_PROT) mt7915_mac_enable_rtscts(dev, vif, info->use_cts_prot); @@ -628,7 +626,7 @@ static void mt7915_bss_info_changed(struct ieee80211_hw *hw, mt7915_mcu_set_tx(dev, vif); if (changed & BSS_CHANGED_HE_OBSS_PD) - mt7915_mcu_add_obss_spr(dev, vif, info->he_obss_pd.enable); + mt7915_mcu_add_obss_spr(phy, vif, &info->he_obss_pd); if (changed & BSS_CHANGED_HE_BSS_COLOR) mt7915_update_bss_color(hw, vif, &info->he_bss_color); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 2769d6c897d9..f27071f1b777 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -32,6 +32,10 @@ #define HE_PHY(p, c) u8_get_bits(c, IEEE80211_HE_PHY_##p) #define HE_MAC(m, c) u8_get_bits(c, IEEE80211_HE_MAC_##m) +static bool sr_scene_detect = true; +module_param(sr_scene_detect, bool, 0644); +MODULE_PARM_DESC(sr_scene_detect, "Enable firmware scene detection algorithm"); + static u8 mt7915_mcu_get_sta_nss(u16 mcs_map) { @@ -3292,31 +3296,193 @@ int mt7915_mcu_set_txbf(struct mt7915_dev *dev, u8 action) sizeof(req), true); } -int mt7915_mcu_add_obss_spr(struct mt7915_dev *dev, struct ieee80211_vif *vif, - bool enable) +static int +mt7915_mcu_enable_obss_spr(struct mt7915_phy *phy, u8 action, u8 val) { -#define MT_SPR_ENABLE 1 - struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; - struct { - u8 action; - u8 arg_num; - u8 band_idx; - u8 status; - u8 drop_tx_idx; - u8 sta_idx; /* 256 sta */ - u8 rsv[2]; - __le32 val; - } __packed req = { - .action = MT_SPR_ENABLE, - .arg_num = 1, - .band_idx = mvif->mt76.band_idx, - .val = cpu_to_le32(enable), + struct mt7915_dev *dev = phy->dev; + struct mt7915_mcu_sr_ctrl req = { + .action = action, + .argnum = 1, + .band_idx = phy != &dev->phy, + .val = cpu_to_le32(val), }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_SPR), &req, sizeof(req), true); } +static int +mt7915_mcu_set_obss_spr_pd(struct mt7915_phy *phy, + struct ieee80211_he_obss_pd *he_obss_pd) +{ + struct mt7915_dev *dev = phy->dev; + struct { + struct mt7915_mcu_sr_ctrl ctrl; + struct { + u8 pd_th_non_srg; + u8 pd_th_srg; + u8 period_offs; + u8 rcpi_src; + __le16 obss_pd_min; + __le16 obss_pd_min_srg; + u8 resp_txpwr_mode; + u8 txpwr_restrict_mode; + u8 txpwr_ref; + u8 rsv[3]; + } __packed param; + } __packed req = { + .ctrl = { + .action = SPR_SET_PARAM, + .argnum = 9, + .band_idx = phy != &dev->phy, + }, + }; + int ret; + u8 max_th = 82, non_srg_max_th = 62; + + /* disable firmware dynamical PD asjustment */ + ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE_DPD, false); + if (ret) + return ret; + + if (he_obss_pd->sr_ctrl & + IEEE80211_HE_SPR_NON_SRG_OBSS_PD_SR_DISALLOWED) + req.param.pd_th_non_srg = max_th; + else if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_NON_SRG_OFFSET_PRESENT) + req.param.pd_th_non_srg = max_th - he_obss_pd->non_srg_max_offset; + else + req.param.pd_th_non_srg = non_srg_max_th; + + if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_SRG_INFORMATION_PRESENT) + req.param.pd_th_srg = max_th - he_obss_pd->max_offset; + + req.param.obss_pd_min = cpu_to_le16(82); + req.param.obss_pd_min_srg = cpu_to_le16(82); + req.param.txpwr_restrict_mode = 2; + req.param.txpwr_ref = 21; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_SPR), &req, + sizeof(req), true); +} + +static int +mt7915_mcu_set_obss_spr_siga(struct mt7915_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_he_obss_pd *he_obss_pd) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct mt7915_dev *dev = phy->dev; + u8 omac = mvif->mt76.omac_idx; + struct { + struct mt7915_mcu_sr_ctrl ctrl; + struct { + u8 omac; + u8 rsv[3]; + u8 flag[20]; + } __packed siga; + } __packed req = { + .ctrl = { + .action = SPR_SET_SIGA, + .argnum = 1, + .band_idx = phy != &dev->phy, + }, + .siga = { + .omac = omac > HW_BSSID_MAX ? omac - 12 : omac, + }, + }; + int ret; + + if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_HESIGA_SR_VAL15_ALLOWED) + req.siga.flag[req.siga.omac] = 0xf; + else + return 0; + + /* switch to normal AP mode */ + ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE_MODE, 0); + if (ret) + return ret; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_SPR), &req, + sizeof(req), true); +} + +static int +mt7915_mcu_set_obss_spr_bitmap(struct mt7915_phy *phy, + struct ieee80211_he_obss_pd *he_obss_pd) +{ + struct mt7915_dev *dev = phy->dev; + struct { + struct mt7915_mcu_sr_ctrl ctrl; + struct { + __le32 color_l[2]; + __le32 color_h[2]; + __le32 bssid_l[2]; + __le32 bssid_h[2]; + } __packed bitmap; + } __packed req = { + .ctrl = { + .action = SPR_SET_SRG_BITMAP, + .argnum = 4, + .band_idx = phy != &dev->phy, + }, + }; + u32 bitmap; + + memcpy(&bitmap, he_obss_pd->bss_color_bitmap, sizeof(bitmap)); + req.bitmap.color_l[req.ctrl.band_idx] = cpu_to_le32(bitmap); + + memcpy(&bitmap, he_obss_pd->bss_color_bitmap + 4, sizeof(bitmap)); + req.bitmap.color_h[req.ctrl.band_idx] = cpu_to_le32(bitmap); + + memcpy(&bitmap, he_obss_pd->partial_bssid_bitmap, sizeof(bitmap)); + req.bitmap.bssid_l[req.ctrl.band_idx] = cpu_to_le32(bitmap); + + memcpy(&bitmap, he_obss_pd->partial_bssid_bitmap + 4, sizeof(bitmap)); + req.bitmap.bssid_h[req.ctrl.band_idx] = cpu_to_le32(bitmap); + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_SPR), &req, + sizeof(req), true); +} + +int mt7915_mcu_add_obss_spr(struct mt7915_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_he_obss_pd *he_obss_pd) +{ + int ret; + + /* enable firmware scene detection algorithms */ + ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE_SD, sr_scene_detect); + if (ret) + return ret; + + /* firmware dynamically adjusts PD threshold so skip manual control */ + if (sr_scene_detect && !he_obss_pd->enable) + return 0; + + /* enable spatial reuse */ + ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE, he_obss_pd->enable); + if (ret) + return ret; + + if (sr_scene_detect || !he_obss_pd->enable) + return 0; + + ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE_TX, true); + if (ret) + return ret; + + /* set SRG/non-SRG OBSS PD threshold */ + ret = mt7915_mcu_set_obss_spr_pd(phy, he_obss_pd); + if (ret) + return ret; + + /* Set SR prohibit */ + ret = mt7915_mcu_set_obss_spr_siga(phy, vif, he_obss_pd); + if (ret) + return ret; + + /* set SRG BSS color/BSSID bitmap */ + return mt7915_mcu_set_obss_spr_bitmap(phy, he_obss_pd); +} + int mt7915_mcu_get_rx_rate(struct mt7915_phy *phy, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct rate_info *rate) { diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h index c19b5d66c0e1..2fc09fd53777 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h @@ -129,6 +129,17 @@ struct mt7915_mcu_background_chain_ctrl { u8 rsv[2]; } __packed; +struct mt7915_mcu_sr_ctrl { + u8 action; + u8 argnum; + u8 band_idx; + u8 status; + u8 drop_ta_idx; + u8 sta_idx; /* 256 sta */ + u8 rsv[2]; + __le32 val; +} __packed; + struct mt7915_mcu_eeprom { u8 buffer_mode; u8 format; @@ -408,6 +419,17 @@ enum { #define RATE_CFG_PHY_TYPE GENMASK(27, 24) #define RATE_CFG_HE_LTF GENMASK(31, 28) +enum { + SPR_ENABLE = 0x1, + SPR_ENABLE_SD = 0x3, + SPR_ENABLE_MODE = 0x5, + SPR_ENABLE_DPD = 0x23, + SPR_ENABLE_TX = 0x25, + SPR_SET_SRG_BITMAP = 0x80, + SPR_SET_PARAM = 0xc2, + SPR_SET_SIGA = 0xdc, +}; + enum { THERMAL_PROTECT_PARAMETER_CTRL, THERMAL_PROTECT_BASIC_INFO, diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index 3fc3c48997e4..b5fa6c0b4775 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -503,8 +503,8 @@ int mt7915_mcu_update_bss_color(struct mt7915_dev *dev, struct ieee80211_vif *vi struct cfg80211_he_bss_color *he_bss_color); int mt7915_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int enable, u32 changed); -int mt7915_mcu_add_obss_spr(struct mt7915_dev *dev, struct ieee80211_vif *vif, - bool enable); +int mt7915_mcu_add_obss_spr(struct mt7915_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_he_obss_pd *he_obss_pd); int mt7915_mcu_add_rate_ctrl(struct mt7915_dev *dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta, bool changed); int mt7915_mcu_add_smps(struct mt7915_dev *dev, struct ieee80211_vif *vif, From bd2404d428215eda4c2466d59a780d568546cdfd Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Tue, 22 Nov 2022 15:53:10 +0800 Subject: [PATCH 127/144] wifi: mt76: mt7915: add basedband Txpower info into debugfs This helps user to debug Txpower propagation path easily. Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt7915/debugfs.c | 19 ++++++++++++++++--- .../net/wireless/mediatek/mt76/mt7915/regs.h | 4 ++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c index 096cb8a4db3d..c322fcc19d67 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c @@ -967,11 +967,18 @@ mt7915_rate_txpower_show(struct seq_file *file, void *data) "RU484/SU40", "RU996/SU80", "RU2x996/SU160" }; struct mt7915_phy *phy = file->private; + struct mt7915_dev *dev = phy->dev; s8 txpower[MT7915_SKU_RATE_NUM], *buf; - int i; + u32 reg; + int i, ret; + + ret = mt7915_mcu_get_txpower_sku(phy, txpower, sizeof(txpower)); + if (ret) + return ret; + + /* Txpower propagation path: TMAC -> TXV -> BBP */ + seq_printf(file, "\nPhy %d\n", phy != &dev->phy); - seq_printf(file, "\nBand %d\n", phy != &phy->dev->phy); - mt7915_mcu_get_txpower_sku(phy, txpower, sizeof(txpower)); for (i = 0, buf = txpower; i < ARRAY_SIZE(mt7915_sku_group_len); i++) { u8 mcs_num = mt7915_sku_group_len[i]; @@ -982,6 +989,12 @@ mt7915_rate_txpower_show(struct seq_file *file, void *data) buf += mt7915_sku_group_len[i]; } + reg = is_mt7915(&dev->mt76) ? MT_WF_PHY_TPC_CTRL_STAT(phy->band_idx) : + MT_WF_PHY_TPC_CTRL_STAT_MT7916(phy->band_idx); + + seq_printf(file, "\nBaseband transmit power %ld\n", + mt76_get_field(dev, reg, MT_WF_PHY_TPC_POWER)); + return 0; } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h index 42a19e7b95d6..aca1b2f1e9e3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h @@ -1186,6 +1186,10 @@ enum offs_rev { #define MT_WF_PHY_RXTD12_IRPI_SW_CLR_ONLY BIT(18) #define MT_WF_PHY_RXTD12_IRPI_SW_CLR BIT(29) +#define MT_WF_PHY_TPC_CTRL_STAT(_phy) MT_WF_PHY(0xe7a0 + ((_phy) << 16)) +#define MT_WF_PHY_TPC_CTRL_STAT_MT7916(_phy) MT_WF_PHY(0xe7a0 + ((_phy) << 20)) +#define MT_WF_PHY_TPC_POWER GENMASK(15, 8) + #define MT_MCU_WM_CIRQ_BASE 0x89010000 #define MT_MCU_WM_CIRQ(ofs) (MT_MCU_WM_CIRQ_BASE + (ofs)) #define MT_MCU_WM_CIRQ_IRQ_MASK_CLR_ADDR MT_MCU_WM_CIRQ(0x80) From 66b181b8a9914a26ff424311f5ac19d7ec2199b9 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Tue, 22 Nov 2022 15:53:11 +0800 Subject: [PATCH 128/144] wifi: mt76: mt7915: enable .sta_set_txpwr support This adds support for adjusting the Txpower level while pushing traffic to an associated station. The allowed range is from 0 to the maximum power of channel. Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7915/init.c | 3 + .../net/wireless/mediatek/mt76/mt7915/main.c | 34 +++++++ .../net/wireless/mediatek/mt76/mt7915/mcu.c | 94 +++++++++++++++++-- .../net/wireless/mediatek/mt76/mt7915/mcu.h | 8 ++ .../wireless/mediatek/mt76/mt7915/mt7915.h | 4 + 5 files changed, 137 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c index b48c2ba9273d..79bd7bf93f33 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c @@ -355,6 +355,9 @@ mt7915_init_wiphy(struct ieee80211_hw *hw) wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_DISCOVERY); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT); + if (!is_mt7915(&dev->mt76)) + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_STA_TX_PWR); + if (!mdev->dev->of_node || !of_property_read_bool(mdev->dev->of_node, "mediatek,disable-radar-background")) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 6ef4634fac6b..98ed56a5ed69 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -1134,6 +1134,39 @@ static void mt7915_sta_set_decap_offload(struct ieee80211_hw *hw, mt76_connac_mcu_wtbl_update_hdr_trans(&dev->mt76, vif, sta); } +static int mt7915_sta_set_txpwr(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7915_phy *phy = mt7915_hw_phy(hw); + struct mt7915_dev *dev = mt7915_hw_dev(hw); + s16 txpower = sta->deflink.txpwr.power; + int ret; + + if (sta->deflink.txpwr.type == NL80211_TX_POWER_AUTOMATIC) + txpower = 0; + + mutex_lock(&dev->mt76.mutex); + + /* NOTE: temporarily use 0 as minimum limit, which is a + * global setting and will be applied to all stations. + */ + ret = mt7915_mcu_set_txpower_frame_min(phy, 0); + if (ret) + goto out; + + /* This only applies to data frames while pushing traffic, + * whereas the management frames or other packets that are + * using fixed rate can be configured via TxD. + */ + ret = mt7915_mcu_set_txpower_frame(phy, vif, sta, txpower); + +out: + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + static const char mt7915_gstrings_stats[][ETH_GSTRING_LEN] = { "tx_ampdu_cnt", "tx_stop_q_empty_cnt", @@ -1499,6 +1532,7 @@ const struct ieee80211_ops mt7915_ops = { .set_bitrate_mask = mt7915_set_bitrate_mask, .set_coverage_class = mt7915_set_coverage_class, .sta_statistics = mt7915_sta_statistics, + .sta_set_txpwr = mt7915_sta_set_txpwr, .sta_set_4addr = mt7915_sta_set_4addr, .sta_set_decap_offload = mt7915_sta_set_decap_offload, .add_twt_setup = mt7915_mac_add_twt_setup, diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index f27071f1b777..ed20ffc58877 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -3105,6 +3105,88 @@ out: &req, sizeof(req), false); } +int mt7915_mcu_set_txpower_frame_min(struct mt7915_phy *phy, s8 txpower) +{ + struct mt7915_dev *dev = phy->dev; + struct { + u8 format_id; + u8 rsv; + u8 band_idx; + s8 txpower_min; + } __packed req = { + .format_id = TX_POWER_LIMIT_FRAME_MIN, + .band_idx = phy->band_idx, + .txpower_min = txpower * 2, /* 0.5db */ + }; + + return mt76_mcu_send_msg(&dev->mt76, + MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req, + sizeof(req), true); +} + +int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, s8 txpower) +{ + struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; + struct mt7915_dev *dev = phy->dev; + struct mt76_phy *mphy = phy->mt76; + struct { + u8 format_id; + u8 rsv[3]; + u8 band_idx; + s8 txpower_max; + __le16 wcid; + s8 txpower_offs[48]; + } __packed req = { + .format_id = TX_POWER_LIMIT_FRAME, + .band_idx = phy->band_idx, + .txpower_max = DIV_ROUND_UP(mphy->txpower_cur, 2), + .wcid = cpu_to_le16(msta->wcid.idx), + }; + int ret, n_chains = hweight8(mphy->antenna_mask); + s8 txpower_sku[MT7915_SKU_RATE_NUM]; + + ret = mt7915_mcu_get_txpower_sku(phy, txpower_sku, sizeof(txpower_sku)); + if (ret) + return ret; + + txpower = txpower * 2 - mt76_tx_power_nss_delta(n_chains); + if (txpower > mphy->txpower_cur || txpower < 0) + return -EINVAL; + + if (txpower) { + u32 offs, len, i; + + if (sta->deflink.ht_cap.ht_supported) { + const u8 *sku_len = mt7915_sku_group_len; + + offs = sku_len[SKU_CCK] + sku_len[SKU_OFDM]; + len = sku_len[SKU_HT_BW20] + sku_len[SKU_HT_BW40]; + + if (sta->deflink.vht_cap.vht_supported) { + offs += len; + len = sku_len[SKU_VHT_BW20] * 4; + + if (sta->deflink.he_cap.has_he) { + offs += len + sku_len[SKU_HE_RU26] * 3; + len = sku_len[SKU_HE_RU242] * 4; + } + } + } else { + return -EINVAL; + } + + for (i = 0; i < len; i++, offs++) + req.txpower_offs[i] = + DIV_ROUND_UP(txpower - txpower_sku[offs], 2); + } + + return mt76_mcu_send_msg(&dev->mt76, + MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req, + sizeof(req), true); +} + int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy) { struct mt7915_dev *dev = phy->dev; @@ -3116,7 +3198,7 @@ int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy) u8 dbdc_idx; s8 val[MT7915_SKU_RATE_NUM]; } __packed req = { - .format_id = 4, + .format_id = TX_POWER_LIMIT_TABLE, .dbdc_idx = phy != &dev->phy, }; struct mt76_power_limits limits_array; @@ -3166,11 +3248,11 @@ int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len) u8 band; u8 _rsv; } __packed req = { - .format_id = 7, + .format_id = TX_POWER_LIMIT_INFO, .category = RATE_POWER_INFO, .band = phy != &dev->phy, }; - s8 res[MT7915_SKU_RATE_NUM][2]; + s8 txpower_sku[MT7915_SKU_RATE_NUM][2]; struct sk_buff *skb; int ret, i; @@ -3180,9 +3262,9 @@ int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len) if (ret) return ret; - memcpy(res, skb->data + 4, sizeof(res)); + memcpy(txpower_sku, skb->data + 4, sizeof(txpower_sku)); for (i = 0; i < len; i++) - txpower[i] = res[i][req.band]; + txpower[i] = txpower_sku[i][req.band_idx]; dev_kfree_skb(skb); @@ -3220,7 +3302,7 @@ int mt7915_mcu_set_sku_en(struct mt7915_phy *phy, bool enable) u8 dbdc_idx; u8 rsv; } __packed req = { - .format_id = 0, + .format_id = TX_POWER_LIMIT_ENABLE, .dbdc_idx = phy != &dev->phy, .sku_enable = enable, }; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h index 2fc09fd53777..46c517e50ae4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h @@ -419,6 +419,14 @@ enum { #define RATE_CFG_PHY_TYPE GENMASK(27, 24) #define RATE_CFG_HE_LTF GENMASK(31, 28) +enum { + TX_POWER_LIMIT_ENABLE, + TX_POWER_LIMIT_TABLE = 0x4, + TX_POWER_LIMIT_INFO = 0x7, + TX_POWER_LIMIT_FRAME = 0x11, + TX_POWER_LIMIT_FRAME_MIN = 0x12, +}; + enum { SPR_ENABLE = 0x1, SPR_ENABLE_SD = 0x3, diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index b5fa6c0b4775..42f213430758 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -528,6 +528,10 @@ int mt7915_mcu_set_ser(struct mt7915_dev *dev, u8 action, u8 set, u8 band); int mt7915_mcu_set_sku_en(struct mt7915_phy *phy, bool enable); int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy); int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len); +int mt7915_mcu_set_txpower_frame_min(struct mt7915_phy *phy, s8 txpower); +int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, s8 txpower); int mt7915_mcu_set_txbf(struct mt7915_dev *dev, u8 action); int mt7915_mcu_set_fcc5_lpn(struct mt7915_dev *dev, int val); int mt7915_mcu_set_pulse_th(struct mt7915_dev *dev, From 6f917bba8709c1eb73c0ed6201c305975c2cf729 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Tue, 22 Nov 2022 15:53:12 +0800 Subject: [PATCH 129/144] wifi: mt76: mt7915: fix band_idx usage The commit 006b9d4ad5bf introduced phy->band_idx to accommodate the band definition change for mt7986 so that the band_idx of main_phy can be 0 or 1. Accordingly, the source of band_idx 1 has switched to "phy != &dev->phy" or "dev->phy.band_idx = 1". We still use ext_phy to represent band 1 somewhere in driver, so fix it. Also, band_idx sounds more reasonable than dbdc_idx, so change it. Fixes: 006b9d4ad5bf ("mt76: mt7915: introduce band_idx in mt7915_phy") Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt7915/debugfs.c | 10 ++--- .../net/wireless/mediatek/mt76/mt7915/mac.c | 13 +++--- .../net/wireless/mediatek/mt76/mt7915/main.c | 36 ++++++++--------- .../net/wireless/mediatek/mt76/mt7915/mcu.c | 40 +++++++++---------- .../wireless/mediatek/mt76/mt7915/testmode.c | 18 ++++----- 5 files changed, 58 insertions(+), 59 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c index c322fcc19d67..fef0ec83185b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c @@ -51,7 +51,7 @@ mt7915_sys_recovery_set(struct file *file, const char __user *user_buf, { struct mt7915_phy *phy = file->private_data; struct mt7915_dev *dev = phy->dev; - bool ext_phy = phy != &dev->phy; + bool band = phy->band_idx; char buf[16]; int ret = 0; u16 val; @@ -83,7 +83,7 @@ mt7915_sys_recovery_set(struct file *file, const char __user *user_buf, * 8: trigger firmware crash. */ case SER_QUERY: - ret = mt7915_mcu_set_ser(dev, 0, 0, ext_phy); + ret = mt7915_mcu_set_ser(dev, 0, 0, band); break; case SER_SET_RECOVER_L1: case SER_SET_RECOVER_L2: @@ -91,17 +91,17 @@ mt7915_sys_recovery_set(struct file *file, const char __user *user_buf, case SER_SET_RECOVER_L3_TX_ABORT: case SER_SET_RECOVER_L3_TX_DISABLE: case SER_SET_RECOVER_L3_BF: - ret = mt7915_mcu_set_ser(dev, SER_ENABLE, BIT(val), ext_phy); + ret = mt7915_mcu_set_ser(dev, SER_ENABLE, BIT(val), band); if (ret) return ret; - ret = mt7915_mcu_set_ser(dev, SER_RECOVER, val, ext_phy); + ret = mt7915_mcu_set_ser(dev, SER_RECOVER, val, band); break; /* enable full chip reset */ case SER_SET_RECOVER_FULL: mt76_set(dev, MT_WFDMA0_MCU_HOST_INT_ENA, MT_MCU_CMD_WDT_MASK); - ret = mt7915_mcu_set_ser(dev, 1, 3, ext_phy); + ret = mt7915_mcu_set_ser(dev, 1, 3, band); if (ret) return ret; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 4f0b3c57e545..eac3008225b0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -1259,18 +1259,18 @@ void mt7915_mac_set_timing(struct mt7915_phy *phy) MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE); } -void mt7915_mac_enable_nf(struct mt7915_dev *dev, bool ext_phy) +void mt7915_mac_enable_nf(struct mt7915_dev *dev, bool band) { u32 reg; - reg = is_mt7915(&dev->mt76) ? MT_WF_PHY_RXTD12(ext_phy) : - MT_WF_PHY_RXTD12_MT7916(ext_phy); + reg = is_mt7915(&dev->mt76) ? MT_WF_PHY_RXTD12(band) : + MT_WF_PHY_RXTD12_MT7916(band); mt76_set(dev, reg, MT_WF_PHY_RXTD12_IRPI_SW_CLR_ONLY | MT_WF_PHY_RXTD12_IRPI_SW_CLR); - reg = is_mt7915(&dev->mt76) ? MT_WF_PHY_RX_CTRL1(ext_phy) : - MT_WF_PHY_RX_CTRL1_MT7916(ext_phy); + reg = is_mt7915(&dev->mt76) ? MT_WF_PHY_RX_CTRL1(band) : + MT_WF_PHY_RX_CTRL1_MT7916(band); mt76_set(dev, reg, FIELD_PREP(MT_WF_PHY_RX_CTRL1_IPI_EN, 0x5)); } @@ -1979,7 +1979,6 @@ void mt7915_mac_update_stats(struct mt7915_phy *phy) static void mt7915_mac_severe_check(struct mt7915_phy *phy) { struct mt7915_dev *dev = phy->dev; - bool ext_phy = phy != &dev->phy; u32 trb; if (!phy->omac_mask) @@ -1997,7 +1996,7 @@ static void mt7915_mac_severe_check(struct mt7915_phy *phy) FIELD_GET(MT_TRB_RXPSR0_RX_WTBL_PTR, phy->trb_ts)) && trb == phy->trb_ts) mt7915_mcu_set_ser(dev, SER_RECOVER, SER_SET_RECOVER_L3_RX_ABORT, - ext_phy); + phy->band_idx); phy->trb_ts = trb; } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index 98ed56a5ed69..c40b6098f19a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -30,31 +30,31 @@ int mt7915_run(struct ieee80211_hw *hw) running = mt7915_dev_running(dev); if (!running) { - ret = mt76_connac_mcu_set_pm(&dev->mt76, 0, 0); + ret = mt76_connac_mcu_set_pm(&dev->mt76, dev->phy.band_idx, 0); if (ret) goto out; - ret = mt7915_mcu_set_mac(dev, 0, true, true); + ret = mt7915_mcu_set_mac(dev, dev->phy.band_idx, true, true); if (ret) goto out; - mt7915_mac_enable_nf(dev, 0); + mt7915_mac_enable_nf(dev, dev->phy.band_idx); } - if (phy != &dev->phy || phy->band_idx) { - ret = mt76_connac_mcu_set_pm(&dev->mt76, 1, 0); + if (phy != &dev->phy) { + ret = mt76_connac_mcu_set_pm(&dev->mt76, phy->band_idx, 0); if (ret) goto out; - ret = mt7915_mcu_set_mac(dev, 1, true, true); + ret = mt7915_mcu_set_mac(dev, phy->band_idx, true, true); if (ret) goto out; - mt7915_mac_enable_nf(dev, 1); + mt7915_mac_enable_nf(dev, phy->band_idx); } ret = mt76_connac_mcu_set_rts_thresh(&dev->mt76, 0x92b, - phy != &dev->phy); + phy->band_idx); if (ret) goto out; @@ -107,13 +107,13 @@ static void mt7915_stop(struct ieee80211_hw *hw) clear_bit(MT76_STATE_RUNNING, &phy->mt76->state); if (phy != &dev->phy) { - mt76_connac_mcu_set_pm(&dev->mt76, 1, 1); - mt7915_mcu_set_mac(dev, 1, false, false); + mt76_connac_mcu_set_pm(&dev->mt76, phy->band_idx, 1); + mt7915_mcu_set_mac(dev, phy->band_idx, false, false); } if (!mt7915_dev_running(dev)) { - mt76_connac_mcu_set_pm(&dev->mt76, 0, 1); - mt7915_mcu_set_mac(dev, 0, false, false); + mt76_connac_mcu_set_pm(&dev->mt76, dev->phy.band_idx, 1); + mt7915_mcu_set_mac(dev, dev->phy.band_idx, false, false); } mutex_unlock(&dev->mt76.mutex); @@ -440,7 +440,6 @@ static int mt7915_config(struct ieee80211_hw *hw, u32 changed) { struct mt7915_dev *dev = mt7915_hw_dev(hw); struct mt7915_phy *phy = mt7915_hw_phy(hw); - bool band = phy != &dev->phy; int ret; if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { @@ -468,6 +467,7 @@ static int mt7915_config(struct ieee80211_hw *hw, u32 changed) if (changed & IEEE80211_CONF_CHANGE_MONITOR) { bool enabled = !!(hw->conf.flags & IEEE80211_CONF_MONITOR); + bool band = phy->band_idx; if (!enabled) phy->rxfilter |= MT_WF_RFCR_DROP_OTHER_UC; @@ -506,7 +506,7 @@ static void mt7915_configure_filter(struct ieee80211_hw *hw, { struct mt7915_dev *dev = mt7915_hw_dev(hw); struct mt7915_phy *phy = mt7915_hw_phy(hw); - bool band = phy != &dev->phy; + bool band = phy->band_idx; u32 ctl_flags = MT_WF_RFCR1_DROP_ACK | MT_WF_RFCR1_DROP_BF_POLL | MT_WF_RFCR1_DROP_BA | @@ -743,7 +743,7 @@ static int mt7915_set_rts_threshold(struct ieee80211_hw *hw, u32 val) int ret; mutex_lock(&dev->mt76.mutex); - ret = mt76_connac_mcu_set_rts_thresh(&dev->mt76, val, phy != &dev->phy); + ret = mt76_connac_mcu_set_rts_thresh(&dev->mt76, val, phy->band_idx); mutex_unlock(&dev->mt76.mutex); return ret; @@ -846,7 +846,7 @@ u64 __mt7915_get_tsf(struct ieee80211_hw *hw, struct mt7915_vif *mvif) { struct mt7915_dev *dev = mt7915_hw_dev(hw); struct mt7915_phy *phy = mt7915_hw_phy(hw); - bool band = phy != &dev->phy; + bool band = phy->band_idx; union { u64 t64; u32 t32[2]; @@ -891,7 +891,7 @@ mt7915_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct mt7915_dev *dev = mt7915_hw_dev(hw); struct mt7915_phy *phy = mt7915_hw_phy(hw); - bool band = phy != &dev->phy; + bool band = phy->band_idx; union { u64 t64; u32 t32[2]; @@ -922,7 +922,7 @@ mt7915_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct mt7915_dev *dev = mt7915_hw_dev(hw); struct mt7915_phy *phy = mt7915_hw_phy(hw); - bool band = phy != &dev->phy; + bool band = phy->band_idx; union { u64 t64; u32 t32[2]; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index ed20ffc58877..718e52744994 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -599,7 +599,7 @@ mt7915_mcu_muar_config(struct mt7915_phy *phy, struct ieee80211_vif *vif, .mode = !!mask || enable, .entry_count = 1, .write = 1, - .band = phy != &dev->phy, + .band = phy->band_idx, .index = idx * 2 + bssid, }; @@ -1715,7 +1715,7 @@ int mt7915_mcu_add_dev_info(struct mt7915_phy *phy, struct { struct req_hdr { u8 omac_idx; - u8 dbdc_idx; + u8 band_idx; __le16 tlv_num; u8 is_tlv_append; u8 rsv[3]; @@ -1724,13 +1724,13 @@ int mt7915_mcu_add_dev_info(struct mt7915_phy *phy, __le16 tag; __le16 len; u8 active; - u8 dbdc_idx; + u8 band_idx; u8 omac_addr[ETH_ALEN]; } __packed tlv; } data = { .hdr = { .omac_idx = mvif->mt76.omac_idx, - .dbdc_idx = mvif->mt76.band_idx, + .band_idx = mvif->mt76.band_idx, .tlv_num = cpu_to_le16(1), .is_tlv_append = 1, }, @@ -1738,7 +1738,7 @@ int mt7915_mcu_add_dev_info(struct mt7915_phy *phy, .tag = cpu_to_le16(DEV_INFO_ACTIVE), .len = cpu_to_le16(sizeof(struct req_tlv)), .active = enable, - .dbdc_idx = mvif->mt76.band_idx, + .band_idx = mvif->mt76.band_idx, }, }; @@ -2585,7 +2585,7 @@ mt7915_mcu_background_chain_ctrl(struct mt7915_phy *phy, req.monitor_central_chan = ieee80211_frequency_to_channel(chandef->center_freq1); req.monitor_bw = mt76_connac_chan_bw(chandef); - req.band_idx = phy != &dev->phy; + req.band_idx = phy->band_idx; req.scan_mode = 1; break; } @@ -2593,7 +2593,7 @@ mt7915_mcu_background_chain_ctrl(struct mt7915_phy *phy, req.monitor_chan = chandef->chan->hw_value; req.monitor_central_chan = ieee80211_frequency_to_channel(chandef->center_freq1); - req.band_idx = phy != &dev->phy; + req.band_idx = phy->band_idx; req.scan_mode = 2; break; case CH_SWITCH_BACKGROUND_SCAN_STOP: @@ -2997,7 +2997,7 @@ int mt7915_mcu_get_chan_mib_info(struct mt7915_phy *phy, bool chan_switch) } for (i = 0; i < 5; i++) { - req[i].band = cpu_to_le32(phy != &dev->phy); + req[i].band = cpu_to_le32(phy->band_idx); req[i].offs = cpu_to_le32(offs[i + start]); if (!is_mt7915(&dev->mt76) && i == 3) @@ -3042,11 +3042,11 @@ int mt7915_mcu_get_temperature(struct mt7915_phy *phy) struct { u8 ctrl_id; u8 action; - u8 dbdc_idx; + u8 band_idx; u8 rsv[5]; } req = { .ctrl_id = THERMAL_SENSOR_TEMP_QUERY, - .dbdc_idx = phy != &dev->phy, + .band_idx = phy->band_idx, }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(THERMAL_CTRL), &req, @@ -3195,11 +3195,11 @@ int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy) struct mt7915_sku_val { u8 format_id; u8 limit_type; - u8 dbdc_idx; + u8 band_idx; s8 val[MT7915_SKU_RATE_NUM]; } __packed req = { .format_id = TX_POWER_LIMIT_TABLE, - .dbdc_idx = phy != &dev->phy, + .band_idx = phy->band_idx, }; struct mt76_power_limits limits_array; s8 *la = (s8 *)&limits_array; @@ -3245,12 +3245,12 @@ int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len) struct { u8 format_id; u8 category; - u8 band; + u8 band_idx; u8 _rsv; } __packed req = { .format_id = TX_POWER_LIMIT_INFO, .category = RATE_POWER_INFO, - .band = phy != &dev->phy, + .band_idx = phy->band_idx, }; s8 txpower_sku[MT7915_SKU_RATE_NUM][2]; struct sk_buff *skb; @@ -3299,11 +3299,11 @@ int mt7915_mcu_set_sku_en(struct mt7915_phy *phy, bool enable) struct mt7915_sku { u8 format_id; u8 sku_enable; - u8 dbdc_idx; + u8 band_idx; u8 rsv; } __packed req = { .format_id = TX_POWER_LIMIT_ENABLE, - .dbdc_idx = phy != &dev->phy, + .band_idx = phy->band_idx, .sku_enable = enable, }; @@ -3385,7 +3385,7 @@ mt7915_mcu_enable_obss_spr(struct mt7915_phy *phy, u8 action, u8 val) struct mt7915_mcu_sr_ctrl req = { .action = action, .argnum = 1, - .band_idx = phy != &dev->phy, + .band_idx = phy->band_idx, .val = cpu_to_le32(val), }; @@ -3416,7 +3416,7 @@ mt7915_mcu_set_obss_spr_pd(struct mt7915_phy *phy, .ctrl = { .action = SPR_SET_PARAM, .argnum = 9, - .band_idx = phy != &dev->phy, + .band_idx = phy->band_idx, }, }; int ret; @@ -3465,7 +3465,7 @@ mt7915_mcu_set_obss_spr_siga(struct mt7915_phy *phy, struct ieee80211_vif *vif, .ctrl = { .action = SPR_SET_SIGA, .argnum = 1, - .band_idx = phy != &dev->phy, + .band_idx = phy->band_idx, }, .siga = { .omac = omac > HW_BSSID_MAX ? omac - 12 : omac, @@ -3504,7 +3504,7 @@ mt7915_mcu_set_obss_spr_bitmap(struct mt7915_phy *phy, .ctrl = { .action = SPR_SET_SRG_BITMAP, .argnum = 4, - .band_idx = phy != &dev->phy, + .band_idx = phy->band_idx, }, }; u32 bitmap; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c index a979460fad2d..7ace05e0b63b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c @@ -44,14 +44,14 @@ mt7915_tm_set_tx_power(struct mt7915_phy *phy) int ret; struct { u8 format_id; - u8 dbdc_idx; + u8 band_idx; s8 tx_power; u8 ant_idx; /* Only 0 is valid */ u8 center_chan; u8 rsv[3]; } __packed req = { .format_id = 0xf, - .dbdc_idx = phy != &dev->phy, + .band_idx = phy->band_idx, .center_chan = ieee80211_frequency_to_channel(freq), }; u8 *tx_power = NULL; @@ -77,7 +77,7 @@ mt7915_tm_set_freq_offset(struct mt7915_phy *phy, bool en, u32 val) struct mt7915_tm_cmd req = { .testmode_en = en, .param_idx = MCU_ATE_SET_FREQ_OFFSET, - .param.freq.band = phy != &dev->phy, + .param.freq.band = phy->band_idx, .param.freq.freq_offset = cpu_to_le32(val), }; @@ -111,7 +111,7 @@ mt7915_tm_set_trx(struct mt7915_phy *phy, int type, bool en) .param_idx = MCU_ATE_SET_TRX, .param.trx.type = type, .param.trx.enable = en, - .param.trx.band = phy != &dev->phy, + .param.trx.band = phy->band_idx, }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req, @@ -126,7 +126,7 @@ mt7915_tm_clean_hwq(struct mt7915_phy *phy, u8 wcid) .testmode_en = 1, .param_idx = MCU_ATE_CLEAN_TXQUEUE, .param.clean.wcid = wcid, - .param.clean.band = phy != &dev->phy, + .param.clean.band = phy->band_idx, }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req, @@ -144,7 +144,7 @@ mt7915_tm_set_slot_time(struct mt7915_phy *phy, u8 slot_time, u8 sifs) .param.slot.sifs = sifs, .param.slot.rifs = 2, .param.slot.eifs = cpu_to_le16(60), - .param.slot.band = phy != &dev->phy, + .param.slot.band = phy->band_idx, }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req, @@ -488,7 +488,7 @@ mt7915_tm_set_rx_frames(struct mt7915_phy *phy, bool en) mt7915_tm_update_channel(phy); /* read-clear */ - mt76_rr(dev, MT_MIB_SDR3(phy != &dev->phy)); + mt76_rr(dev, MT_MIB_SDR3(phy->band_idx)); mt7915_tm_set_trx(phy, TM_MAC_RX_RXV, en); } } @@ -526,7 +526,7 @@ mt7915_tm_set_tx_cont(struct mt7915_phy *phy, bool en) tx_cont->control_ch = chandef->chan->hw_value; tx_cont->center_ch = freq1; tx_cont->tx_ant = td->tx_antenna_mask; - tx_cont->band = phy != &dev->phy; + tx_cont->band = phy->band_idx; switch (chandef->width) { case NL80211_CHAN_WIDTH_40: @@ -558,7 +558,7 @@ mt7915_tm_set_tx_cont(struct mt7915_phy *phy, bool en) } if (!en) { - req.op.rf.param.func_data = cpu_to_le32(phy != &dev->phy); + req.op.rf.param.func_data = cpu_to_le32(phy->band_idx); goto out; } From f4cfd3f95f297744e03fd494fcc21f3b65b95658 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Tue, 22 Nov 2022 14:52:08 +0100 Subject: [PATCH 130/144] wifi: mt76: mt76x0: remove dead code in mt76x0_phy_get_target_power tx_rate can't be greater than 3 in mt76x0_phy_get_target_power routine for cck rates. Get rid of dead code. Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76x0/phy.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c b/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c index 8a89fe49db36..6c6c8ada7943 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c @@ -595,9 +595,6 @@ mt76x0_phy_get_target_power(struct mt76x02_dev *dev, u8 tx_mode, case 0: /* cck rates */ tx_rate = (info[0] & 0x60) >> 5; - if (tx_rate > 3) - return -EINVAL; - *target_power = cur_power + dev->rate_power.cck[tx_rate]; *target_pa_power = mt76x0_phy_get_rf_pa_mode(dev, 0, tx_rate); break; From 98686cd21624c75a043e96812beadddf4f6f48e5 Mon Sep 17 00:00:00 2001 From: Shayne Chen Date: Tue, 22 Nov 2022 16:45:46 +0800 Subject: [PATCH 131/144] wifi: mt76: mt7996: add driver for MediaTek Wi-Fi 7 (802.11be) devices The driver first supports Filogic 680 PCI device, which is a Wi-Fi 7 chipset supporting concurrent tri-band operation at 6 GHz, 5 GHz, and 2.4 GHz with 4x4 antennas on each band. Currently, mt7996 only supports tri-band HE or older mode. EHT mode and more variants of Filogic 680 support will be introduced in further patches. Reviewed-by: Ryder Lee Co-developed-by: Peter Chiu Signed-off-by: Peter Chiu Co-developed-by: Bo Jiao Signed-off-by: Bo Jiao Co-developed-by: Howard Hsu Signed-off-by: Howard Hsu Co-developed-by: MeiChia Chiu Signed-off-by: MeiChia Chiu Co-developed-by: StanleyYP Wang Signed-off-by: StanleyYP Wang Co-developed-by: Money Wang Signed-off-by: Money Wang Co-developed-by: Evelyn Tsai Signed-off-by: Evelyn Tsai Signed-off-by: Shayne Chen Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/Kconfig | 1 + drivers/net/wireless/mediatek/mt76/Makefile | 1 + .../net/wireless/mediatek/mt76/mt7996/Kconfig | 12 + .../wireless/mediatek/mt76/mt7996/Makefile | 6 + .../wireless/mediatek/mt76/mt7996/debugfs.c | 851 ++++ .../net/wireless/mediatek/mt76/mt7996/dma.c | 360 ++ .../wireless/mediatek/mt76/mt7996/eeprom.c | 229 ++ .../wireless/mediatek/mt76/mt7996/eeprom.h | 75 + .../net/wireless/mediatek/mt76/mt7996/init.c | 816 ++++ .../net/wireless/mediatek/mt76/mt7996/mac.c | 2468 ++++++++++++ .../net/wireless/mediatek/mt76/mt7996/mac.h | 398 ++ .../net/wireless/mediatek/mt76/mt7996/main.c | 1325 +++++++ .../net/wireless/mediatek/mt76/mt7996/mcu.c | 3429 +++++++++++++++++ .../net/wireless/mediatek/mt76/mt7996/mcu.h | 662 ++++ .../net/wireless/mediatek/mt76/mt7996/mmio.c | 385 ++ .../wireless/mediatek/mt76/mt7996/mt7996.h | 516 +++ .../net/wireless/mediatek/mt76/mt7996/pci.c | 222 ++ .../net/wireless/mediatek/mt76/mt7996/regs.h | 533 +++ 18 files changed, 12289 insertions(+) create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/Kconfig create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/Makefile create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/dma.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/eeprom.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/init.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/mac.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/mac.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/main.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/mcu.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/mcu.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/mmio.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/pci.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/regs.h diff --git a/drivers/net/wireless/mediatek/mt76/Kconfig b/drivers/net/wireless/mediatek/mt76/Kconfig index 9ff43f1fc50d..d7f90a0eb21e 100644 --- a/drivers/net/wireless/mediatek/mt76/Kconfig +++ b/drivers/net/wireless/mediatek/mt76/Kconfig @@ -34,3 +34,4 @@ source "drivers/net/wireless/mediatek/mt76/mt7603/Kconfig" source "drivers/net/wireless/mediatek/mt76/mt7615/Kconfig" source "drivers/net/wireless/mediatek/mt76/mt7915/Kconfig" source "drivers/net/wireless/mediatek/mt76/mt7921/Kconfig" +source "drivers/net/wireless/mediatek/mt76/mt7996/Kconfig" diff --git a/drivers/net/wireless/mediatek/mt76/Makefile b/drivers/net/wireless/mediatek/mt76/Makefile index c78ae4b89761..84c99b7e57f9 100644 --- a/drivers/net/wireless/mediatek/mt76/Makefile +++ b/drivers/net/wireless/mediatek/mt76/Makefile @@ -35,3 +35,4 @@ obj-$(CONFIG_MT7603E) += mt7603/ obj-$(CONFIG_MT7615_COMMON) += mt7615/ obj-$(CONFIG_MT7915E) += mt7915/ obj-$(CONFIG_MT7921_COMMON) += mt7921/ +obj-$(CONFIG_MT7996E) += mt7996/ diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig new file mode 100644 index 000000000000..5c5fc569e6d5 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: ISC +config MT7996E + tristate "MediaTek MT7996 (PCIe) support" + select MT76_CONNAC_LIB + depends on MAC80211 + depends on PCI + help + This adds support for MT7996-based wireless PCIe devices, + which support concurrent tri-band operation at 6GHz, 5GHz, + and 2.4GHz IEEE 802.11be 4x4:4SS 4096-QAM, 320MHz channels. + + To compile this driver as a module, choose M here. diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/Makefile b/drivers/net/wireless/mediatek/mt76/mt7996/Makefile new file mode 100644 index 000000000000..bcb9a3c53149 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: ISC + +obj-$(CONFIG_MT7996E) += mt7996e.o + +mt7996e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \ + debugfs.o mmio.o diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c new file mode 100644 index 000000000000..2e4a8909b9e8 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c @@ -0,0 +1,851 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#include +#include "mt7996.h" +#include "eeprom.h" +#include "mcu.h" +#include "mac.h" + +#define FW_BIN_LOG_MAGIC 0x44d9c99a + +/** global debugfs **/ + +struct hw_queue_map { + const char *name; + u8 index; + u8 pid; + u8 qid; +}; + +static int +mt7996_implicit_txbf_set(void *data, u64 val) +{ + struct mt7996_dev *dev = data; + + /* The existing connected stations shall reconnect to apply + * new implicit txbf configuration. + */ + dev->ibf = !!val; + + return mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE); +} + +static int +mt7996_implicit_txbf_get(void *data, u64 *val) +{ + struct mt7996_dev *dev = data; + + *val = dev->ibf; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_implicit_txbf, mt7996_implicit_txbf_get, + mt7996_implicit_txbf_set, "%lld\n"); + +/* test knob of system error recovery */ +static ssize_t +mt7996_fw_ser_set(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct mt7996_phy *phy = file->private_data; + struct mt7996_dev *dev = phy->dev; + u8 band_idx = phy->mt76->band_idx; + char buf[16]; + int ret = 0; + u16 val; + + if (count >= sizeof(buf)) + return -EINVAL; + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + if (count && buf[count - 1] == '\n') + buf[count - 1] = '\0'; + else + buf[count] = '\0'; + + if (kstrtou16(buf, 0, &val)) + return -EINVAL; + + switch (val) { + case SER_SET_RECOVER_L1: + case SER_SET_RECOVER_L2: + case SER_SET_RECOVER_L3_RX_ABORT: + case SER_SET_RECOVER_L3_TX_ABORT: + case SER_SET_RECOVER_L3_TX_DISABLE: + case SER_SET_RECOVER_L3_BF: + ret = mt7996_mcu_set_ser(dev, SER_ENABLE, BIT(val), band_idx); + if (ret) + return ret; + + ret = mt7996_mcu_set_ser(dev, SER_RECOVER, val, band_idx); + break; + default: + break; + } + + return ret ? ret : count; +} + +static const struct file_operations mt7996_fw_ser_ops = { + .write = mt7996_fw_ser_set, + /* TODO: ser read */ + .open = simple_open, + .llseek = default_llseek, +}; + +static int +mt7996_radar_trigger(void *data, u64 val) +{ + struct mt7996_dev *dev = data; + + if (val > MT_RX_SEL2) + return -EINVAL; + + return mt7996_mcu_rdd_cmd(dev, RDD_RADAR_EMULATE, + val, 0, 0); +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_radar_trigger, NULL, + mt7996_radar_trigger, "%lld\n"); + +static int +mt7996_rdd_monitor(struct seq_file *s, void *data) +{ + struct mt7996_dev *dev = dev_get_drvdata(s->private); + struct cfg80211_chan_def *chandef = &dev->rdd2_chandef; + const char *bw; + int ret = 0; + + mutex_lock(&dev->mt76.mutex); + + if (!cfg80211_chandef_valid(chandef)) { + ret = -EINVAL; + goto out; + } + + if (!dev->rdd2_phy) { + seq_puts(s, "not running\n"); + goto out; + } + + switch (chandef->width) { + case NL80211_CHAN_WIDTH_40: + bw = "40"; + break; + case NL80211_CHAN_WIDTH_80: + bw = "80"; + break; + case NL80211_CHAN_WIDTH_160: + bw = "160"; + break; + case NL80211_CHAN_WIDTH_80P80: + bw = "80P80"; + break; + default: + bw = "20"; + break; + } + + seq_printf(s, "channel %d (%d MHz) width %s MHz center1: %d MHz\n", + chandef->chan->hw_value, chandef->chan->center_freq, + bw, chandef->center_freq1); +out: + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static int +mt7996_fw_debug_wm_set(void *data, u64 val) +{ + struct mt7996_dev *dev = data; + enum { + DEBUG_TXCMD = 62, + DEBUG_CMD_RPT_TX, + DEBUG_CMD_RPT_TRIG, + DEBUG_SPL, + DEBUG_RPT_RX, + DEBUG_RPT_RA = 68, + } debug; + bool tx, rx, en; + int ret; + + dev->fw_debug_wm = val ? MCU_FW_LOG_TO_HOST : 0; + + if (dev->fw_debug_bin) + val = MCU_FW_LOG_RELAY; + else + val = dev->fw_debug_wm; + + tx = dev->fw_debug_wm || (dev->fw_debug_bin & BIT(1)); + rx = dev->fw_debug_wm || (dev->fw_debug_bin & BIT(2)); + en = dev->fw_debug_wm || (dev->fw_debug_bin & BIT(0)); + + ret = mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WM, val); + if (ret) + return ret; + + for (debug = DEBUG_TXCMD; debug <= DEBUG_RPT_RA; debug++) { + if (debug == 67) + continue; + + if (debug == DEBUG_RPT_RX) + val = en && rx; + else + val = en && tx; + + ret = mt7996_mcu_fw_dbg_ctrl(dev, debug, val); + if (ret) + return ret; + } + + return 0; +} + +static int +mt7996_fw_debug_wm_get(void *data, u64 *val) +{ + struct mt7996_dev *dev = data; + + *val = dev->fw_debug_wm; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_wm, mt7996_fw_debug_wm_get, + mt7996_fw_debug_wm_set, "%lld\n"); + +static int +mt7996_fw_debug_wa_set(void *data, u64 val) +{ + struct mt7996_dev *dev = data; + int ret; + + dev->fw_debug_wa = val ? MCU_FW_LOG_TO_HOST : 0; + + ret = mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WA, dev->fw_debug_wa); + if (ret) + return ret; + + return mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET), MCU_WA_PARAM_PDMA_RX, + !!dev->fw_debug_wa, 0); +} + +static int +mt7996_fw_debug_wa_get(void *data, u64 *val) +{ + struct mt7996_dev *dev = data; + + *val = dev->fw_debug_wa; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_wa, mt7996_fw_debug_wa_get, + mt7996_fw_debug_wa_set, "%lld\n"); + +static struct dentry * +create_buf_file_cb(const char *filename, struct dentry *parent, umode_t mode, + struct rchan_buf *buf, int *is_global) +{ + struct dentry *f; + + f = debugfs_create_file("fwlog_data", mode, parent, buf, + &relay_file_operations); + if (IS_ERR(f)) + return NULL; + + *is_global = 1; + + return f; +} + +static int +remove_buf_file_cb(struct dentry *f) +{ + debugfs_remove(f); + + return 0; +} + +static int +mt7996_fw_debug_bin_set(void *data, u64 val) +{ + static struct rchan_callbacks relay_cb = { + .create_buf_file = create_buf_file_cb, + .remove_buf_file = remove_buf_file_cb, + }; + struct mt7996_dev *dev = data; + + if (!dev->relay_fwlog) + dev->relay_fwlog = relay_open("fwlog_data", dev->debugfs_dir, + 1500, 512, &relay_cb, NULL); + if (!dev->relay_fwlog) + return -ENOMEM; + + dev->fw_debug_bin = val; + + relay_reset(dev->relay_fwlog); + + return mt7996_fw_debug_wm_set(dev, dev->fw_debug_wm); +} + +static int +mt7996_fw_debug_bin_get(void *data, u64 *val) +{ + struct mt7996_dev *dev = data; + + *val = dev->fw_debug_bin; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_bin, mt7996_fw_debug_bin_get, + mt7996_fw_debug_bin_set, "%lld\n"); + +static int +mt7996_fw_util_wa_show(struct seq_file *file, void *data) +{ + struct mt7996_dev *dev = file->private; + + if (dev->fw_debug_wa) + return mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(QUERY), + MCU_WA_PARAM_CPU_UTIL, 0, 0); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mt7996_fw_util_wa); + +static void +mt7996_ampdu_stat_read_phy(struct mt7996_phy *phy, struct seq_file *file) +{ + struct mt7996_dev *dev = phy->dev; + int bound[15], range[8], i; + u8 band_idx = phy->mt76->band_idx; + + /* Tx ampdu stat */ + for (i = 0; i < ARRAY_SIZE(range); i++) + range[i] = mt76_rr(dev, MT_MIB_ARNG(band_idx, i)); + + for (i = 0; i < ARRAY_SIZE(bound); i++) + bound[i] = MT_MIB_ARNCR_RANGE(range[i / 2], i % 2) + 1; + + seq_printf(file, "\nPhy %s, Phy band %d\n", + wiphy_name(phy->mt76->hw->wiphy), band_idx); + + seq_printf(file, "Length: %8d | ", bound[0]); + for (i = 0; i < ARRAY_SIZE(bound) - 1; i++) + seq_printf(file, "%3d -%3d | ", + bound[i] + 1, bound[i + 1]); + + seq_puts(file, "\nCount: "); + for (i = 0; i < ARRAY_SIZE(bound); i++) + seq_printf(file, "%8d | ", phy->mt76->aggr_stats[i]); + seq_puts(file, "\n"); + + seq_printf(file, "BA miss count: %d\n", phy->mib.ba_miss_cnt); +} + +static void +mt7996_txbf_stat_read_phy(struct mt7996_phy *phy, struct seq_file *s) +{ + static const char * const bw[] = { + "BW20", "BW40", "BW80", "BW160" + }; + struct mib_stats *mib = &phy->mib; + + /* Tx Beamformer monitor */ + seq_puts(s, "\nTx Beamformer applied PPDU counts: "); + + seq_printf(s, "iBF: %d, eBF: %d\n", + mib->tx_bf_ibf_ppdu_cnt, + mib->tx_bf_ebf_ppdu_cnt); + + /* Tx Beamformer Rx feedback monitor */ + seq_puts(s, "Tx Beamformer Rx feedback statistics: "); + + seq_printf(s, "All: %d, HE: %d, VHT: %d, HT: %d, ", + mib->tx_bf_rx_fb_all_cnt, + mib->tx_bf_rx_fb_he_cnt, + mib->tx_bf_rx_fb_vht_cnt, + mib->tx_bf_rx_fb_ht_cnt); + + seq_printf(s, "%s, NC: %d, NR: %d\n", + bw[mib->tx_bf_rx_fb_bw], + mib->tx_bf_rx_fb_nc_cnt, + mib->tx_bf_rx_fb_nr_cnt); + + /* Tx Beamformee Rx NDPA & Tx feedback report */ + seq_printf(s, "Tx Beamformee successful feedback frames: %d\n", + mib->tx_bf_fb_cpl_cnt); + seq_printf(s, "Tx Beamformee feedback triggered counts: %d\n", + mib->tx_bf_fb_trig_cnt); + + /* Tx SU & MU counters */ + seq_printf(s, "Tx multi-user Beamforming counts: %d\n", + mib->tx_mu_bf_cnt); + seq_printf(s, "Tx multi-user MPDU counts: %d\n", mib->tx_mu_mpdu_cnt); + seq_printf(s, "Tx multi-user successful MPDU counts: %d\n", + mib->tx_mu_acked_mpdu_cnt); + seq_printf(s, "Tx single-user successful MPDU counts: %d\n", + mib->tx_su_acked_mpdu_cnt); + + seq_puts(s, "\n"); +} + +static int +mt7996_tx_stats_show(struct seq_file *file, void *data) +{ + struct mt7996_phy *phy = file->private; + struct mt7996_dev *dev = phy->dev; + struct mib_stats *mib = &phy->mib; + int i; + u32 attempts, success, per; + + mutex_lock(&dev->mt76.mutex); + + mt7996_mac_update_stats(phy); + mt7996_ampdu_stat_read_phy(phy, file); + + attempts = mib->tx_mpdu_attempts_cnt; + success = mib->tx_mpdu_success_cnt; + per = attempts ? 100 - success * 100 / attempts : 100; + seq_printf(file, "Tx attempts: %8u (MPDUs)\n", attempts); + seq_printf(file, "Tx success: %8u (MPDUs)\n", success); + seq_printf(file, "Tx PER: %u%%\n", per); + + mt7996_txbf_stat_read_phy(phy, file); + + /* Tx amsdu info */ + seq_puts(file, "Tx MSDU statistics:\n"); + for (i = 0; i < ARRAY_SIZE(mib->tx_amsdu); i++) { + seq_printf(file, "AMSDU pack count of %d MSDU in TXD: %8d ", + i + 1, mib->tx_amsdu[i]); + if (mib->tx_amsdu_cnt) + seq_printf(file, "(%3d%%)\n", + mib->tx_amsdu[i] * 100 / mib->tx_amsdu_cnt); + else + seq_puts(file, "\n"); + } + + mutex_unlock(&dev->mt76.mutex); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mt7996_tx_stats); + +static void +mt7996_hw_queue_read(struct seq_file *s, u32 size, + const struct hw_queue_map *map) +{ + struct mt7996_phy *phy = s->private; + struct mt7996_dev *dev = phy->dev; + u32 i, val; + + val = mt76_rr(dev, MT_FL_Q_EMPTY); + for (i = 0; i < size; i++) { + u32 ctrl, head, tail, queued; + + if (val & BIT(map[i].index)) + continue; + + ctrl = BIT(31) | (map[i].pid << 10) | (map[i].qid << 24); + mt76_wr(dev, MT_FL_Q0_CTRL, ctrl); + + head = mt76_get_field(dev, MT_FL_Q2_CTRL, + GENMASK(11, 0)); + tail = mt76_get_field(dev, MT_FL_Q2_CTRL, + GENMASK(27, 16)); + queued = mt76_get_field(dev, MT_FL_Q3_CTRL, + GENMASK(11, 0)); + + seq_printf(s, "\t%s: ", map[i].name); + seq_printf(s, "queued:0x%03x head:0x%03x tail:0x%03x\n", + queued, head, tail); + } +} + +static void +mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_dev *dev = msta->vif->phy->dev; + struct seq_file *s = data; + u8 ac; + + for (ac = 0; ac < 4; ac++) { + u32 qlen, ctrl, val; + u32 idx = msta->wcid.idx >> 5; + u8 offs = msta->wcid.idx & GENMASK(4, 0); + + ctrl = BIT(31) | BIT(11) | (ac << 24); + val = mt76_rr(dev, MT_PLE_AC_QEMPTY(ac, idx)); + + if (val & BIT(offs)) + continue; + + mt76_wr(dev, MT_FL_Q0_CTRL, ctrl | msta->wcid.idx); + qlen = mt76_get_field(dev, MT_FL_Q3_CTRL, + GENMASK(11, 0)); + seq_printf(s, "\tSTA %pM wcid %d: AC%d%d queued:%d\n", + sta->addr, msta->wcid.idx, + msta->vif->mt76.wmm_idx, ac, qlen); + } +} + +static int +mt7996_hw_queues_show(struct seq_file *file, void *data) +{ + struct mt7996_phy *phy = file->private; + struct mt7996_dev *dev = phy->dev; + static const struct hw_queue_map ple_queue_map[] = { + { "CPU_Q0", 0, 1, MT_CTX0 }, + { "CPU_Q1", 1, 1, MT_CTX0 + 1 }, + { "CPU_Q2", 2, 1, MT_CTX0 + 2 }, + { "CPU_Q3", 3, 1, MT_CTX0 + 3 }, + { "ALTX_Q0", 8, 2, MT_LMAC_ALTX0 }, + { "BMC_Q0", 9, 2, MT_LMAC_BMC0 }, + { "BCN_Q0", 10, 2, MT_LMAC_BCN0 }, + { "PSMP_Q0", 11, 2, MT_LMAC_PSMP0 }, + { "ALTX_Q1", 12, 2, MT_LMAC_ALTX0 + 4 }, + { "BMC_Q1", 13, 2, MT_LMAC_BMC0 + 4 }, + { "BCN_Q1", 14, 2, MT_LMAC_BCN0 + 4 }, + { "PSMP_Q1", 15, 2, MT_LMAC_PSMP0 + 4 }, + }; + static const struct hw_queue_map pse_queue_map[] = { + { "CPU Q0", 0, 1, MT_CTX0 }, + { "CPU Q1", 1, 1, MT_CTX0 + 1 }, + { "CPU Q2", 2, 1, MT_CTX0 + 2 }, + { "CPU Q3", 3, 1, MT_CTX0 + 3 }, + { "HIF_Q0", 8, 0, MT_HIF0 }, + { "HIF_Q1", 9, 0, MT_HIF0 + 1 }, + { "HIF_Q2", 10, 0, MT_HIF0 + 2 }, + { "HIF_Q3", 11, 0, MT_HIF0 + 3 }, + { "HIF_Q4", 12, 0, MT_HIF0 + 4 }, + { "HIF_Q5", 13, 0, MT_HIF0 + 5 }, + { "LMAC_Q", 16, 2, 0 }, + { "MDP_TXQ", 17, 2, 1 }, + { "MDP_RXQ", 18, 2, 2 }, + { "SEC_TXQ", 19, 2, 3 }, + { "SEC_RXQ", 20, 2, 4 }, + }; + u32 val, head, tail; + + /* ple queue */ + val = mt76_rr(dev, MT_PLE_FREEPG_CNT); + head = mt76_get_field(dev, MT_PLE_FREEPG_HEAD_TAIL, GENMASK(11, 0)); + tail = mt76_get_field(dev, MT_PLE_FREEPG_HEAD_TAIL, GENMASK(27, 16)); + seq_puts(file, "PLE page info:\n"); + seq_printf(file, + "\tTotal free page: 0x%08x head: 0x%03x tail: 0x%03x\n", + val, head, tail); + + val = mt76_rr(dev, MT_PLE_PG_HIF_GROUP); + head = mt76_get_field(dev, MT_PLE_HIF_PG_INFO, GENMASK(11, 0)); + tail = mt76_get_field(dev, MT_PLE_HIF_PG_INFO, GENMASK(27, 16)); + seq_printf(file, "\tHIF free page: 0x%03x res: 0x%03x used: 0x%03x\n", + val, head, tail); + + seq_puts(file, "PLE non-empty queue info:\n"); + mt7996_hw_queue_read(file, ARRAY_SIZE(ple_queue_map), + &ple_queue_map[0]); + + /* iterate per-sta ple queue */ + ieee80211_iterate_stations_atomic(phy->mt76->hw, + mt7996_sta_hw_queue_read, file); + /* pse queue */ + seq_puts(file, "PSE non-empty queue info:\n"); + mt7996_hw_queue_read(file, ARRAY_SIZE(pse_queue_map), + &pse_queue_map[0]); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mt7996_hw_queues); + +static int +mt7996_xmit_queues_show(struct seq_file *file, void *data) +{ + struct mt7996_phy *phy = file->private; + struct mt7996_dev *dev = phy->dev; + struct { + struct mt76_queue *q; + char *queue; + } queue_map[] = { + { phy->mt76->q_tx[MT_TXQ_BE], " MAIN" }, + { dev->mt76.q_mcu[MT_MCUQ_WM], " MCUWM" }, + { dev->mt76.q_mcu[MT_MCUQ_WA], " MCUWA" }, + { dev->mt76.q_mcu[MT_MCUQ_FWDL], "MCUFWDL" }, + }; + int i; + + seq_puts(file, " queue | hw-queued | head | tail |\n"); + for (i = 0; i < ARRAY_SIZE(queue_map); i++) { + struct mt76_queue *q = queue_map[i].q; + + if (!q) + continue; + + seq_printf(file, " %s | %9d | %9d | %9d |\n", + queue_map[i].queue, q->queued, q->head, + q->tail); + } + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mt7996_xmit_queues); + +static int +mt7996_twt_stats(struct seq_file *s, void *data) +{ + struct mt7996_dev *dev = dev_get_drvdata(s->private); + struct mt7996_twt_flow *iter; + + rcu_read_lock(); + + seq_puts(s, " wcid | id | flags | exp | mantissa"); + seq_puts(s, " | duration | tsf |\n"); + list_for_each_entry_rcu(iter, &dev->twt_list, list) + seq_printf(s, + "%9d | %8d | %5c%c%c%c | %8d | %8d | %8d | %14lld |\n", + iter->wcid, iter->id, + iter->sched ? 's' : 'u', + iter->protection ? 'p' : '-', + iter->trigger ? 't' : '-', + iter->flowtype ? '-' : 'a', + iter->exp, iter->mantissa, + iter->duration, iter->tsf); + + rcu_read_unlock(); + + return 0; +} + +/* The index of RF registers use the generic regidx, combined with two parts: + * WF selection [31:24] and offset [23:0]. + */ +static int +mt7996_rf_regval_get(void *data, u64 *val) +{ + struct mt7996_dev *dev = data; + u32 regval; + int ret; + + ret = mt7996_mcu_rf_regval(dev, dev->mt76.debugfs_reg, ®val, false); + if (ret) + return ret; + + *val = regval; + + return 0; +} + +static int +mt7996_rf_regval_set(void *data, u64 val) +{ + struct mt7996_dev *dev = data; + + return mt7996_mcu_rf_regval(dev, dev->mt76.debugfs_reg, (u32 *)&val, true); +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_rf_regval, mt7996_rf_regval_get, + mt7996_rf_regval_set, "0x%08llx\n"); + +int mt7996_init_debugfs(struct mt7996_phy *phy) +{ + struct mt7996_dev *dev = phy->dev; + struct dentry *dir; + + dir = mt76_register_debugfs_fops(phy->mt76, NULL); + if (!dir) + return -ENOMEM; + debugfs_create_file("hw-queues", 0400, dir, phy, + &mt7996_hw_queues_fops); + debugfs_create_file("xmit-queues", 0400, dir, phy, + &mt7996_xmit_queues_fops); + debugfs_create_file("tx_stats", 0400, dir, phy, &mt7996_tx_stats_fops); + debugfs_create_file("fw_debug_wm", 0600, dir, dev, &fops_fw_debug_wm); + debugfs_create_file("fw_debug_wa", 0600, dir, dev, &fops_fw_debug_wa); + debugfs_create_file("fw_debug_bin", 0600, dir, dev, &fops_fw_debug_bin); + /* TODO: wm fw cpu utilization */ + debugfs_create_file("fw_util_wa", 0400, dir, dev, + &mt7996_fw_util_wa_fops); + debugfs_create_file("implicit_txbf", 0600, dir, dev, + &fops_implicit_txbf); + debugfs_create_devm_seqfile(dev->mt76.dev, "twt_stats", dir, + mt7996_twt_stats); + debugfs_create_file("fw_ser", 0600, dir, phy, &mt7996_fw_ser_ops); + debugfs_create_file("rf_regval", 0600, dir, dev, &fops_rf_regval); + + if (phy->mt76->cap.has_5ghz) { + debugfs_create_u32("dfs_hw_pattern", 0400, dir, + &dev->hw_pattern); + debugfs_create_file("radar_trigger", 0200, dir, dev, + &fops_radar_trigger); + debugfs_create_devm_seqfile(dev->mt76.dev, "rdd_monitor", dir, + mt7996_rdd_monitor); + } + + if (phy == &dev->phy) + dev->debugfs_dir = dir; + + return 0; +} + +static void +mt7996_debugfs_write_fwlog(struct mt7996_dev *dev, const void *hdr, int hdrlen, + const void *data, int len) +{ + static DEFINE_SPINLOCK(lock); + unsigned long flags; + void *dest; + + spin_lock_irqsave(&lock, flags); + dest = relay_reserve(dev->relay_fwlog, hdrlen + len + 4); + if (dest) { + *(u32 *)dest = hdrlen + len; + dest += 4; + + if (hdrlen) { + memcpy(dest, hdr, hdrlen); + dest += hdrlen; + } + + memcpy(dest, data, len); + relay_flush(dev->relay_fwlog); + } + spin_unlock_irqrestore(&lock, flags); +} + +void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int len) +{ + struct { + __le32 magic; + u8 version; + u8 _rsv; + __le16 serial_id; + __le32 timestamp; + __le16 msg_type; + __le16 len; + } hdr = { + .version = 0x1, + .magic = cpu_to_le32(FW_BIN_LOG_MAGIC), + .msg_type = cpu_to_le16(PKT_TYPE_RX_FW_MONITOR), + }; + + if (!dev->relay_fwlog) + return; + + hdr.serial_id = cpu_to_le16(dev->fw_debug_seq++); + hdr.timestamp = cpu_to_le32(mt76_rr(dev, MT_LPON_FRCR(0))); + hdr.len = *(__le16 *)data; + mt7996_debugfs_write_fwlog(dev, &hdr, sizeof(hdr), data, len); +} + +bool mt7996_debugfs_rx_log(struct mt7996_dev *dev, const void *data, int len) +{ + if (get_unaligned_le32(data) != FW_BIN_LOG_MAGIC) + return false; + + if (dev->relay_fwlog) + mt7996_debugfs_write_fwlog(dev, NULL, 0, data, len); + + return true; +} + +#ifdef CONFIG_MAC80211_DEBUGFS +/** per-station debugfs **/ + +static ssize_t mt7996_sta_fixed_rate_set(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define SHORT_PREAMBLE 0 +#define LONG_PREAMBLE 1 + struct ieee80211_sta *sta = file->private_data; + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_dev *dev = msta->vif->phy->dev; + struct ra_rate phy = {}; + char buf[100]; + int ret; + u16 gi, ltf; + + if (count >= sizeof(buf)) + return -EINVAL; + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + if (count && buf[count - 1] == '\n') + buf[count - 1] = '\0'; + else + buf[count] = '\0'; + + /* mode - cck: 0, ofdm: 1, ht: 2, gf: 3, vht: 4, he_su: 8, he_er: 9 + * bw - bw20: 0, bw40: 1, bw80: 2, bw160: 3 + * nss - vht: 1~4, he: 1~4, others: ignore + * mcs - cck: 0~4, ofdm: 0~7, ht: 0~32, vht: 0~9, he_su: 0~11, he_er: 0~2 + * gi - (ht/vht) lgi: 0, sgi: 1; (he) 0.8us: 0, 1.6us: 1, 3.2us: 2 + * preamble - short: 1, long: 0 + * ldpc - off: 0, on: 1 + * stbc - off: 0, on: 1 + * ltf - 1xltf: 0, 2xltf: 1, 4xltf: 2 + */ + if (sscanf(buf, "%hhu %hhu %hhu %hhu %hu %hhu %hhu %hhu %hhu %hu", + &phy.mode, &phy.bw, &phy.mcs, &phy.nss, &gi, + &phy.preamble, &phy.stbc, &phy.ldpc, &phy.spe, <f) != 10) { + dev_warn(dev->mt76.dev, + "format: Mode BW MCS NSS GI Preamble STBC LDPC SPE ltf\n"); + goto out; + } + + phy.wlan_idx = cpu_to_le16(msta->wcid.idx); + phy.gi = cpu_to_le16(gi); + phy.ltf = cpu_to_le16(ltf); + phy.ldpc = phy.ldpc ? 7 : 0; + phy.preamble = phy.preamble ? SHORT_PREAMBLE : LONG_PREAMBLE; + + ret = mt7996_mcu_set_fixed_rate_ctrl(dev, &phy, 0); + if (ret) + return -EFAULT; + +out: + return count; +} + +static const struct file_operations fops_fixed_rate = { + .write = mt7996_sta_fixed_rate_set, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int +mt7996_queues_show(struct seq_file *s, void *data) +{ + struct ieee80211_sta *sta = s->private; + + mt7996_sta_hw_queue_read(s, sta); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mt7996_queues); + +void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, struct dentry *dir) +{ + debugfs_create_file("fixed_rate", 0600, dir, sta, &fops_fixed_rate); + debugfs_create_file("hw-queues", 0400, dir, sta, &mt7996_queues_fops); +} + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/dma.c b/drivers/net/wireless/mediatek/mt76/mt7996/dma.c new file mode 100644 index 000000000000..c09fe4274935 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/dma.c @@ -0,0 +1,360 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#include "mt7996.h" +#include "../dma.h" +#include "mac.h" + +static int mt7996_poll_tx(struct napi_struct *napi, int budget) +{ + struct mt7996_dev *dev; + + dev = container_of(napi, struct mt7996_dev, mt76.tx_napi); + + mt76_connac_tx_cleanup(&dev->mt76); + if (napi_complete_done(napi, 0)) + mt7996_irq_enable(dev, MT_INT_TX_DONE_MCU); + + return 0; +} + +static void mt7996_dma_config(struct mt7996_dev *dev) +{ +#define Q_CONFIG(q, wfdma, int, id) do { \ + if (wfdma) \ + dev->q_wfdma_mask |= (1 << (q)); \ + dev->q_int_mask[(q)] = int; \ + dev->q_id[(q)] = id; \ +} while (0) + +#define MCUQ_CONFIG(q, wfdma, int, id) Q_CONFIG(q, (wfdma), (int), (id)) +#define RXQ_CONFIG(q, wfdma, int, id) Q_CONFIG(__RXQ(q), (wfdma), (int), (id)) +#define TXQ_CONFIG(q, wfdma, int, id) Q_CONFIG(__TXQ(q), (wfdma), (int), (id)) + + /* rx queue */ + RXQ_CONFIG(MT_RXQ_MCU, WFDMA0, MT_INT_RX_DONE_WM, MT7996_RXQ_MCU_WM); + RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA0, MT_INT_RX_DONE_WA, MT7996_RXQ_MCU_WA); + + /* band0/band1 */ + RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0, MT7996_RXQ_BAND0); + RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA0, MT_INT_RX_DONE_WA_MAIN, MT7996_RXQ_MCU_WA_MAIN); + + /* band2 */ + RXQ_CONFIG(MT_RXQ_BAND2, WFDMA0, MT_INT_RX_DONE_BAND2, MT7996_RXQ_BAND2); + RXQ_CONFIG(MT_RXQ_BAND2_WA, WFDMA0, MT_INT_RX_DONE_WA_TRI, MT7996_RXQ_MCU_WA_TRI); + + /* data tx queue */ + TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7996_TXQ_BAND0); + TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1); + TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND2, MT7996_TXQ_BAND2); + + /* mcu tx queue */ + MCUQ_CONFIG(MT_MCUQ_WM, WFDMA0, MT_INT_TX_DONE_MCU_WM, MT7996_TXQ_MCU_WM); + MCUQ_CONFIG(MT_MCUQ_WA, WFDMA0, MT_INT_TX_DONE_MCU_WA, MT7996_TXQ_MCU_WA); + MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA0, MT_INT_TX_DONE_FWDL, MT7996_TXQ_FWDL); +} + +static void __mt7996_dma_prefetch(struct mt7996_dev *dev, u32 ofs) +{ +#define PREFETCH(_base, _depth) ((_base) << 16 | (_depth)) + /* prefetch SRAM wrapping boundary for tx/rx ring. */ + mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_FWDL) + ofs, PREFETCH(0x0, 0x2)); + mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_WM) + ofs, PREFETCH(0x20, 0x2)); + mt76_wr(dev, MT_TXQ_EXT_CTRL(0) + ofs, PREFETCH(0x40, 0x4)); + mt76_wr(dev, MT_TXQ_EXT_CTRL(1) + ofs, PREFETCH(0x80, 0x4)); + mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_WA) + ofs, PREFETCH(0xc0, 0x2)); + mt76_wr(dev, MT_TXQ_EXT_CTRL(2) + ofs, PREFETCH(0xe0, 0x4)); + mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MCU) + ofs, PREFETCH(0x120, 0x2)); + mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MCU_WA) + ofs, PREFETCH(0x140, 0x2)); + mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MAIN_WA) + ofs, PREFETCH(0x160, 0x2)); + mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_BAND2_WA) + ofs, PREFETCH(0x180, 0x2)); + mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MAIN) + ofs, PREFETCH(0x1a0, 0x10)); + mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_BAND2) + ofs, PREFETCH(0x2a0, 0x10)); + + mt76_set(dev, WF_WFDMA0_GLO_CFG_EXT1 + ofs, WF_WFDMA0_GLO_CFG_EXT1_CALC_MODE); +} + +void mt7996_dma_prefetch(struct mt7996_dev *dev) +{ + __mt7996_dma_prefetch(dev, 0); + if (dev->hif2) + __mt7996_dma_prefetch(dev, MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0)); +} + +static void mt7996_dma_disable(struct mt7996_dev *dev, bool reset) +{ + u32 hif1_ofs = 0; + + if (dev->hif2) + hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); + + if (reset) { + mt76_clear(dev, MT_WFDMA0_RST, + MT_WFDMA0_RST_DMASHDL_ALL_RST | + MT_WFDMA0_RST_LOGIC_RST); + + mt76_set(dev, MT_WFDMA0_RST, + MT_WFDMA0_RST_DMASHDL_ALL_RST | + MT_WFDMA0_RST_LOGIC_RST); + + if (dev->hif2) { + mt76_clear(dev, MT_WFDMA0_RST + hif1_ofs, + MT_WFDMA0_RST_DMASHDL_ALL_RST | + MT_WFDMA0_RST_LOGIC_RST); + + mt76_set(dev, MT_WFDMA0_RST + hif1_ofs, + MT_WFDMA0_RST_DMASHDL_ALL_RST | + MT_WFDMA0_RST_LOGIC_RST); + } + } + + /* disable */ + mt76_clear(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN | + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2); + + if (dev->hif2) { + mt76_clear(dev, MT_WFDMA0_GLO_CFG + hif1_ofs, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN | + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2); + } +} + +static int mt7996_dma_enable(struct mt7996_dev *dev) +{ + u32 hif1_ofs = 0; + u32 irq_mask; + + if (dev->hif2) + hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); + + /* reset dma idx */ + mt76_wr(dev, MT_WFDMA0_RST_DTX_PTR, ~0); + if (dev->hif2) + mt76_wr(dev, MT_WFDMA0_RST_DTX_PTR + hif1_ofs, ~0); + + /* configure delay interrupt off */ + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG0, 0); + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG1, 0); + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG2, 0); + + if (dev->hif2) { + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG0 + hif1_ofs, 0); + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG1 + hif1_ofs, 0); + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG2 + hif1_ofs, 0); + } + + /* configure perfetch settings */ + mt7996_dma_prefetch(dev); + + /* hif wait WFDMA idle */ + mt76_set(dev, MT_WFDMA0_BUSY_ENA, + MT_WFDMA0_BUSY_ENA_TX_FIFO0 | + MT_WFDMA0_BUSY_ENA_TX_FIFO1 | + MT_WFDMA0_BUSY_ENA_RX_FIFO); + + if (dev->hif2) + mt76_set(dev, MT_WFDMA0_BUSY_ENA + hif1_ofs, + MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO0 | + MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO1 | + MT_WFDMA0_PCIE1_BUSY_ENA_RX_FIFO); + + mt76_poll(dev, MT_WFDMA_EXT_CSR_HIF_MISC, + MT_WFDMA_EXT_CSR_HIF_MISC_BUSY, 0, 1000); + + /* set WFDMA Tx/Rx */ + mt76_set(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN | + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2); + + /* GLO_CFG_EXT0 */ + mt76_set(dev, WF_WFDMA0_GLO_CFG_EXT0, + WF_WFDMA0_GLO_CFG_EXT0_RX_WB_RXD | + WF_WFDMA0_GLO_CFG_EXT0_WED_MERGE_MODE); + + /* GLO_CFG_EXT1 */ + mt76_set(dev, WF_WFDMA0_GLO_CFG_EXT1, + WF_WFDMA0_GLO_CFG_EXT1_TX_FCTRL_MODE); + + if (dev->hif2) { + mt76_set(dev, MT_WFDMA0_GLO_CFG + hif1_ofs, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN | + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2); + + /* GLO_CFG_EXT0 */ + mt76_set(dev, WF_WFDMA0_GLO_CFG_EXT0 + hif1_ofs, + WF_WFDMA0_GLO_CFG_EXT0_RX_WB_RXD | + WF_WFDMA0_GLO_CFG_EXT0_WED_MERGE_MODE); + + /* GLO_CFG_EXT1 */ + mt76_set(dev, WF_WFDMA0_GLO_CFG_EXT1 + hif1_ofs, + WF_WFDMA0_GLO_CFG_EXT1_TX_FCTRL_MODE); + + mt76_set(dev, MT_WFDMA_HOST_CONFIG, + MT_WFDMA_HOST_CONFIG_PDMA_BAND); + } + + if (dev->hif2) { + /* fix hardware limitation, pcie1's rx ring3 is not available + * so, redirect pcie0 rx ring3 interrupt to pcie1 + */ + mt76_set(dev, MT_WFDMA0_RX_INT_PCIE_SEL, + MT_WFDMA0_RX_INT_SEL_RING3); + + /* TODO: redirect rx ring6 interrupt to pcie0 for wed function */ + } + + /* enable interrupts for TX/RX rings */ + irq_mask = MT_INT_RX_DONE_MCU | + MT_INT_TX_DONE_MCU | + MT_INT_MCU_CMD; + + if (!dev->mphy.band_idx) + irq_mask |= MT_INT_BAND0_RX_DONE; + + if (dev->dbdc_support) + irq_mask |= MT_INT_BAND1_RX_DONE; + + if (dev->tbtc_support) + irq_mask |= MT_INT_BAND2_RX_DONE; + + mt7996_irq_enable(dev, irq_mask); + + return 0; +} + +int mt7996_dma_init(struct mt7996_dev *dev) +{ + u32 hif1_ofs = 0; + int ret; + + mt7996_dma_config(dev); + + mt76_dma_attach(&dev->mt76); + + if (dev->hif2) + hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); + + mt7996_dma_disable(dev, true); + + /* init tx queue */ + ret = mt76_connac_init_tx_queues(dev->phy.mt76, + MT_TXQ_ID(dev->mphy.band_idx), + MT7996_TX_RING_SIZE, + MT_TXQ_RING_BASE(0), 0); + if (ret) + return ret; + + /* command to WM */ + ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WM, + MT_MCUQ_ID(MT_MCUQ_WM), + MT7996_TX_MCU_RING_SIZE, + MT_MCUQ_RING_BASE(MT_MCUQ_WM)); + if (ret) + return ret; + + /* command to WA */ + ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WA, + MT_MCUQ_ID(MT_MCUQ_WA), + MT7996_TX_MCU_RING_SIZE, + MT_MCUQ_RING_BASE(MT_MCUQ_WA)); + if (ret) + return ret; + + /* firmware download */ + ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_FWDL, + MT_MCUQ_ID(MT_MCUQ_FWDL), + MT7996_TX_FWDL_RING_SIZE, + MT_MCUQ_RING_BASE(MT_MCUQ_FWDL)); + if (ret) + return ret; + + /* event from WM */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU], + MT_RXQ_ID(MT_RXQ_MCU), + MT7996_RX_MCU_RING_SIZE, + MT_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_MCU)); + if (ret) + return ret; + + /* event from WA */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU_WA], + MT_RXQ_ID(MT_RXQ_MCU_WA), + MT7996_RX_MCU_RING_SIZE, + MT_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_MCU_WA)); + if (ret) + return ret; + + /* rx data queue for band0 and band1 */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN], + MT_RXQ_ID(MT_RXQ_MAIN), + MT7996_RX_RING_SIZE, + MT_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_MAIN)); + if (ret) + return ret; + + /* tx free notify event from WA for band0 */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN_WA], + MT_RXQ_ID(MT_RXQ_MAIN_WA), + MT7996_RX_MCU_RING_SIZE, + MT_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_MAIN_WA)); + if (ret) + return ret; + + if (dev->tbtc_support || dev->mphy.band_idx == MT_BAND2) { + /* rx data queue for band2 */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND2], + MT_RXQ_ID(MT_RXQ_BAND2), + MT7996_RX_RING_SIZE, + MT_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_BAND2) + hif1_ofs); + if (ret) + return ret; + + /* tx free notify event from WA for band2 + * use pcie0's rx ring3, but, redirect pcie0 rx ring3 interrupt to pcie1 + */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND2_WA], + MT_RXQ_ID(MT_RXQ_BAND2_WA), + MT7996_RX_MCU_RING_SIZE, + MT_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_BAND2_WA)); + if (ret) + return ret; + } + + ret = mt76_init_queues(dev, mt76_dma_rx_poll); + if (ret < 0) + return ret; + + netif_napi_add_tx(&dev->mt76.tx_napi_dev, &dev->mt76.tx_napi, + mt7996_poll_tx); + napi_enable(&dev->mt76.tx_napi); + + mt7996_dma_enable(dev); + + return 0; +} + +void mt7996_dma_cleanup(struct mt7996_dev *dev) +{ + mt7996_dma_disable(dev, true); + + mt76_dma_cleanup(&dev->mt76); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c new file mode 100644 index 000000000000..b9f62bedbc48 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#include +#include "mt7996.h" +#include "eeprom.h" + +static int mt7996_check_eeprom(struct mt7996_dev *dev) +{ + u8 *eeprom = dev->mt76.eeprom.data; + u16 val = get_unaligned_le16(eeprom); + + switch (val) { + case 0x7990: + return 0; + default: + return -EINVAL; + } +} + +static char *mt7996_eeprom_name(struct mt7996_dev *dev) +{ + /* reserve for future variants */ + return MT7996_EEPROM_DEFAULT; +} + +static int +mt7996_eeprom_load_default(struct mt7996_dev *dev) +{ + u8 *eeprom = dev->mt76.eeprom.data; + const struct firmware *fw = NULL; + int ret; + + ret = request_firmware(&fw, mt7996_eeprom_name(dev), dev->mt76.dev); + if (ret) + return ret; + + if (!fw || !fw->data) { + dev_err(dev->mt76.dev, "Invalid default bin\n"); + ret = -EINVAL; + goto out; + } + + memcpy(eeprom, fw->data, MT7996_EEPROM_SIZE); + dev->flash_mode = true; + +out: + release_firmware(fw); + + return ret; +} + +static int mt7996_eeprom_load(struct mt7996_dev *dev) +{ + int ret; + + ret = mt76_eeprom_init(&dev->mt76, MT7996_EEPROM_SIZE); + if (ret < 0) + return ret; + + if (ret) { + dev->flash_mode = true; + } else { + u8 free_block_num; + u32 block_num, i; + + /* TODO: check free block event */ + mt7996_mcu_get_eeprom_free_block(dev, &free_block_num); + /* efuse info not enough */ + if (free_block_num >= 59) + return -EINVAL; + + /* read eeprom data from efuse */ + block_num = DIV_ROUND_UP(MT7996_EEPROM_SIZE, MT7996_EEPROM_BLOCK_SIZE); + for (i = 0; i < block_num; i++) + mt7996_mcu_get_eeprom(dev, i * MT7996_EEPROM_BLOCK_SIZE); + } + + return mt7996_check_eeprom(dev); +} + +static int mt7996_eeprom_parse_band_config(struct mt7996_phy *phy) +{ + u8 *eeprom = phy->dev->mt76.eeprom.data; + u32 val = eeprom[MT_EE_WIFI_CONF]; + int ret = 0; + + switch (phy->mt76->band_idx) { + case MT_BAND1: + val = FIELD_GET(MT_EE_WIFI_CONF1_BAND_SEL, val); + break; + case MT_BAND2: + val = eeprom[MT_EE_WIFI_CONF + 1]; + val = FIELD_GET(MT_EE_WIFI_CONF2_BAND_SEL, val); + break; + default: + val = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val); + break; + } + + switch (val) { + case MT_EE_BAND_SEL_2GHZ: + phy->mt76->cap.has_2ghz = true; + break; + case MT_EE_BAND_SEL_5GHZ: + phy->mt76->cap.has_5ghz = true; + break; + case MT_EE_BAND_SEL_6GHZ: + phy->mt76->cap.has_6ghz = true; + break; + case MT_EE_BAND_SEL_5GHZ_6GHZ: + phy->mt76->cap.has_5ghz = true; + phy->mt76->cap.has_6ghz = true; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy) +{ + u8 path, nss, band_idx = phy->mt76->band_idx; + u8 *eeprom = dev->mt76.eeprom.data; + struct mt76_phy *mphy = phy->mt76; + + switch (band_idx) { + case MT_BAND1: + path = FIELD_GET(MT_EE_WIFI_CONF2_TX_PATH_BAND1, + eeprom[MT_EE_WIFI_CONF + 2]); + nss = FIELD_GET(MT_EE_WIFI_CONF5_STREAM_NUM_BAND1, + eeprom[MT_EE_WIFI_CONF + 5]); + break; + case MT_BAND2: + path = FIELD_GET(MT_EE_WIFI_CONF2_TX_PATH_BAND2, + eeprom[MT_EE_WIFI_CONF + 2]); + nss = FIELD_GET(MT_EE_WIFI_CONF5_STREAM_NUM_BAND2, + eeprom[MT_EE_WIFI_CONF + 5]); + break; + default: + path = FIELD_GET(MT_EE_WIFI_CONF1_TX_PATH_BAND0, + eeprom[MT_EE_WIFI_CONF + 1]); + nss = FIELD_GET(MT_EE_WIFI_CONF4_STREAM_NUM_BAND0, + eeprom[MT_EE_WIFI_CONF + 4]); + break; + } + + if (!path || path > 4) + path = 4; + + nss = min_t(u8, min_t(u8, 4, nss), path); + + mphy->antenna_mask = BIT(nss) - 1; + mphy->chainmask = (BIT(path) - 1) << dev->chainshift[band_idx]; + dev->chainmask |= mphy->chainmask; + if (band_idx < MT_BAND2) + dev->chainshift[band_idx + 1] = dev->chainshift[band_idx] + + hweight16(mphy->chainmask); + + return mt7996_eeprom_parse_band_config(phy); +} + +int mt7996_eeprom_init(struct mt7996_dev *dev) +{ + int ret; + + ret = mt7996_eeprom_load(dev); + if (ret < 0) { + if (ret != -EINVAL) + return ret; + + dev_warn(dev->mt76.dev, "eeprom load fail, use default bin\n"); + ret = mt7996_eeprom_load_default(dev); + if (ret) + return ret; + } + + ret = mt7996_eeprom_parse_hw_cap(dev, &dev->phy); + if (ret < 0) + return ret; + + memcpy(dev->mphy.macaddr, dev->mt76.eeprom.data + MT_EE_MAC_ADDR, ETH_ALEN); + mt76_eeprom_override(&dev->mphy); + + return 0; +} + +int mt7996_eeprom_get_target_power(struct mt7996_dev *dev, + struct ieee80211_channel *chan) +{ + u8 *eeprom = dev->mt76.eeprom.data; + int target_power; + + if (chan->band == NL80211_BAND_5GHZ) + target_power = eeprom[MT_EE_TX0_POWER_5G + + mt7996_get_channel_group_5g(chan->hw_value)]; + else if (chan->band == NL80211_BAND_6GHZ) + target_power = eeprom[MT_EE_TX0_POWER_6G + + mt7996_get_channel_group_6g(chan->hw_value)]; + else + target_power = eeprom[MT_EE_TX0_POWER_2G]; + + return target_power; +} + +s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band) +{ + u8 *eeprom = dev->mt76.eeprom.data; + u32 val; + s8 delta; + + if (band == NL80211_BAND_5GHZ) + val = eeprom[MT_EE_RATE_DELTA_5G]; + else if (band == NL80211_BAND_6GHZ) + val = eeprom[MT_EE_RATE_DELTA_6G]; + else + val = eeprom[MT_EE_RATE_DELTA_2G]; + + if (!(val & MT_EE_RATE_DELTA_EN)) + return 0; + + delta = FIELD_GET(MT_EE_RATE_DELTA_MASK, val); + + return val & MT_EE_RATE_DELTA_SIGN ? delta : -delta; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.h new file mode 100644 index 000000000000..8da599e0abea --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#ifndef __MT7996_EEPROM_H +#define __MT7996_EEPROM_H + +#include "mt7996.h" + +enum mt7996_eeprom_field { + MT_EE_CHIP_ID = 0x000, + MT_EE_VERSION = 0x002, + MT_EE_MAC_ADDR = 0x004, + MT_EE_MAC_ADDR2 = 0x00a, + MT_EE_WIFI_CONF = 0x190, + MT_EE_MAC_ADDR3 = 0x2c0, + MT_EE_RATE_DELTA_2G = 0x1400, + MT_EE_RATE_DELTA_5G = 0x147d, + MT_EE_RATE_DELTA_6G = 0x154a, + MT_EE_TX0_POWER_2G = 0x1300, + MT_EE_TX0_POWER_5G = 0x1301, + MT_EE_TX0_POWER_6G = 0x1310, + + __MT_EE_MAX = 0x1dff, +}; + +#define MT_EE_WIFI_CONF0_TX_PATH GENMASK(2, 0) +#define MT_EE_WIFI_CONF0_BAND_SEL GENMASK(2, 0) +#define MT_EE_WIFI_CONF1_BAND_SEL GENMASK(5, 3) +#define MT_EE_WIFI_CONF2_BAND_SEL GENMASK(2, 0) + +#define MT_EE_WIFI_CONF1_TX_PATH_BAND0 GENMASK(5, 3) +#define MT_EE_WIFI_CONF2_TX_PATH_BAND1 GENMASK(5, 3) +#define MT_EE_WIFI_CONF2_TX_PATH_BAND2 GENMASK(2, 0) +#define MT_EE_WIFI_CONF4_STREAM_NUM_BAND0 GENMASK(5, 3) +#define MT_EE_WIFI_CONF5_STREAM_NUM_BAND1 GENMASK(5, 3) +#define MT_EE_WIFI_CONF5_STREAM_NUM_BAND2 GENMASK(2, 0) + +#define MT_EE_RATE_DELTA_MASK GENMASK(5, 0) +#define MT_EE_RATE_DELTA_SIGN BIT(6) +#define MT_EE_RATE_DELTA_EN BIT(7) + +enum mt7996_eeprom_band { + MT_EE_BAND_SEL_DEFAULT, + MT_EE_BAND_SEL_2GHZ, + MT_EE_BAND_SEL_5GHZ, + MT_EE_BAND_SEL_6GHZ, + MT_EE_BAND_SEL_5GHZ_6GHZ, +}; + +static inline int +mt7996_get_channel_group_5g(int channel) +{ + if (channel <= 64) + return 0; + if (channel <= 96) + return 1; + if (channel <= 128) + return 2; + if (channel <= 144) + return 3; + return 4; +} + +static inline int +mt7996_get_channel_group_6g(int channel) +{ + if (channel <= 29) + return 0; + + return DIV_ROUND_UP(channel - 29, 32); +} + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/init.c b/drivers/net/wireless/mediatek/mt76/mt7996/init.c new file mode 100644 index 000000000000..cd1657e3585d --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/init.c @@ -0,0 +1,816 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#include +#include +#include "mt7996.h" +#include "mac.h" +#include "mcu.h" +#include "eeprom.h" + +static const struct ieee80211_iface_limit if_limits[] = { + { + .max = 1, + .types = BIT(NL80211_IFTYPE_ADHOC) + }, { + .max = 16, + .types = BIT(NL80211_IFTYPE_AP) +#ifdef CONFIG_MAC80211_MESH + | BIT(NL80211_IFTYPE_MESH_POINT) +#endif + }, { + .max = MT7996_MAX_INTERFACES, + .types = BIT(NL80211_IFTYPE_STATION) + } +}; + +static const struct ieee80211_iface_combination if_comb[] = { + { + .limits = if_limits, + .n_limits = ARRAY_SIZE(if_limits), + .max_interfaces = MT7996_MAX_INTERFACES, + .num_different_channels = 1, + .beacon_int_infra_match = true, + .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80) | + BIT(NL80211_CHAN_WIDTH_160) | + BIT(NL80211_CHAN_WIDTH_80P80), + } +}; + +static void mt7996_led_set_config(struct led_classdev *led_cdev, + u8 delay_on, u8 delay_off) +{ + struct mt7996_dev *dev; + struct mt76_dev *mt76; + u32 val; + + mt76 = container_of(led_cdev, struct mt76_dev, led_cdev); + dev = container_of(mt76, struct mt7996_dev, mt76); + + /* select TX blink mode, 2: only data frames */ + mt76_rmw_field(dev, MT_TMAC_TCR0(0), MT_TMAC_TCR0_TX_BLINK, 2); + + /* enable LED */ + mt76_wr(dev, MT_LED_EN(0), 1); + + /* set LED Tx blink on/off time */ + val = FIELD_PREP(MT_LED_TX_BLINK_ON_MASK, delay_on) | + FIELD_PREP(MT_LED_TX_BLINK_OFF_MASK, delay_off); + mt76_wr(dev, MT_LED_TX_BLINK(0), val); + + /* control LED */ + val = MT_LED_CTRL_BLINK_MODE | MT_LED_CTRL_KICK; + if (dev->mt76.led_al) + val |= MT_LED_CTRL_POLARITY; + + mt76_wr(dev, MT_LED_CTRL(0), val); + mt76_clear(dev, MT_LED_CTRL(0), MT_LED_CTRL_KICK); +} + +static int mt7996_led_set_blink(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + u16 delta_on = 0, delta_off = 0; + +#define HW_TICK 10 +#define TO_HW_TICK(_t) (((_t) > HW_TICK) ? ((_t) / HW_TICK) : HW_TICK) + + if (*delay_on) + delta_on = TO_HW_TICK(*delay_on); + if (*delay_off) + delta_off = TO_HW_TICK(*delay_off); + + mt7996_led_set_config(led_cdev, delta_on, delta_off); + + return 0; +} + +static void mt7996_led_set_brightness(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + if (!brightness) + mt7996_led_set_config(led_cdev, 0, 0xff); + else + mt7996_led_set_config(led_cdev, 0xff, 0); +} + +static void +mt7996_init_txpower(struct mt7996_dev *dev, + struct ieee80211_supported_band *sband) +{ + int i, nss = hweight8(dev->mphy.antenna_mask); + int nss_delta = mt76_tx_power_nss_delta(nss); + int pwr_delta = mt7996_eeprom_get_power_delta(dev, sband->band); + struct mt76_power_limits limits; + + for (i = 0; i < sband->n_channels; i++) { + struct ieee80211_channel *chan = &sband->channels[i]; + int target_power = mt7996_eeprom_get_target_power(dev, chan); + + target_power += pwr_delta; + target_power = mt76_get_rate_power_limits(&dev->mphy, chan, + &limits, + target_power); + target_power += nss_delta; + target_power = DIV_ROUND_UP(target_power, 2); + chan->max_power = min_t(int, chan->max_reg_power, + target_power); + chan->orig_mpwr = target_power; + } +} + +static void +mt7996_regd_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + + memcpy(dev->mt76.alpha2, request->alpha2, sizeof(dev->mt76.alpha2)); + dev->mt76.region = request->dfs_region; + + if (dev->mt76.region == NL80211_DFS_UNSET) + mt7996_mcu_rdd_background_enable(phy, NULL); + + mt7996_init_txpower(dev, &phy->mt76->sband_2g.sband); + mt7996_init_txpower(dev, &phy->mt76->sband_5g.sband); + mt7996_init_txpower(dev, &phy->mt76->sband_6g.sband); + + phy->mt76->dfs_state = MT_DFS_STATE_UNKNOWN; + mt7996_dfs_init_radar_detector(phy); +} + +static void +mt7996_init_wiphy(struct ieee80211_hw *hw) +{ + struct mt7996_phy *phy = mt7996_hw_phy(hw); + struct mt76_dev *mdev = &phy->dev->mt76; + struct wiphy *wiphy = hw->wiphy; + + hw->queues = 4; + hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE; + hw->max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE; + hw->netdev_features = NETIF_F_RXCSUM; + + hw->radiotap_timestamp.units_pos = + IEEE80211_RADIOTAP_TIMESTAMP_UNIT_US; + + phy->slottime = 9; + + hw->sta_data_size = sizeof(struct mt7996_sta); + hw->vif_data_size = sizeof(struct mt7996_vif); + + wiphy->iface_combinations = if_comb; + wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); + wiphy->reg_notifier = mt7996_regd_notifier; + wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; + + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BSS_COLOR); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_LEGACY); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_HT); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_VHT); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_HE); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_DISCOVERY); + + if (!mdev->dev->of_node || + !of_property_read_bool(mdev->dev->of_node, + "mediatek,disable-radar-background")) + wiphy_ext_feature_set(wiphy, + NL80211_EXT_FEATURE_RADAR_BACKGROUND); + + ieee80211_hw_set(hw, HAS_RATE_CONTROL); + ieee80211_hw_set(hw, SUPPORTS_TX_ENCAP_OFFLOAD); + ieee80211_hw_set(hw, SUPPORTS_RX_DECAP_OFFLOAD); + ieee80211_hw_set(hw, WANT_MONITOR_VIF); + + hw->max_tx_fragments = 4; + + if (phy->mt76->cap.has_2ghz) + phy->mt76->sband_2g.sband.ht_cap.cap |= + IEEE80211_HT_CAP_LDPC_CODING | + IEEE80211_HT_CAP_MAX_AMSDU; + + if (phy->mt76->cap.has_5ghz) { + phy->mt76->sband_5g.sband.ht_cap.cap |= + IEEE80211_HT_CAP_LDPC_CODING | + IEEE80211_HT_CAP_MAX_AMSDU; + + phy->mt76->sband_5g.sband.vht_cap.cap |= + IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK | + IEEE80211_VHT_CAP_SHORT_GI_160 | + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; + } + + mt76_set_stream_caps(phy->mt76, true); + mt7996_set_stream_vht_txbf_caps(phy); + mt7996_set_stream_he_caps(phy); + + wiphy->available_antennas_rx = phy->mt76->antenna_mask; + wiphy->available_antennas_tx = phy->mt76->antenna_mask; +} + +static void +mt7996_mac_init_band(struct mt7996_dev *dev, u8 band) +{ + u32 mask, set; + + /* clear estimated value of EIFS for Rx duration & OBSS time */ + mt76_wr(dev, MT_WF_RMAC_RSVD0(band), MT_WF_RMAC_RSVD0_EIFS_CLR); + + /* clear backoff time for Rx duration */ + mt76_clear(dev, MT_WF_RMAC_MIB_AIRTIME1(band), + MT_WF_RMAC_MIB_NONQOSD_BACKOFF); + mt76_clear(dev, MT_WF_RMAC_MIB_AIRTIME3(band), + MT_WF_RMAC_MIB_QOS01_BACKOFF); + mt76_clear(dev, MT_WF_RMAC_MIB_AIRTIME4(band), + MT_WF_RMAC_MIB_QOS23_BACKOFF); + + /* clear backoff time and set software compensation for OBSS time */ + mask = MT_WF_RMAC_MIB_OBSS_BACKOFF | MT_WF_RMAC_MIB_ED_OFFSET; + set = FIELD_PREP(MT_WF_RMAC_MIB_OBSS_BACKOFF, 0) | + FIELD_PREP(MT_WF_RMAC_MIB_ED_OFFSET, 4); + mt76_rmw(dev, MT_WF_RMAC_MIB_AIRTIME0(band), mask, set); +} + +static void mt7996_mac_init(struct mt7996_dev *dev) +{ +#define HIF_TXD_V2_1 4 + int i; + + mt76_clear(dev, MT_MDP_DCR2, MT_MDP_DCR2_RX_TRANS_SHORT); + + for (i = 0; i < MT7996_WTBL_SIZE; i++) + mt7996_mac_wtbl_update(dev, i, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + + if (IS_ENABLED(CONFIG_MT76_LEDS)) { + i = dev->mt76.led_pin ? MT_LED_GPIO_MUX3 : MT_LED_GPIO_MUX2; + mt76_rmw_field(dev, i, MT_LED_GPIO_SEL_MASK, 4); + } + + /* txs report queue */ + mt76_rmw_field(dev, MT_DMA_TCRF1(0), MT_DMA_TCRF1_QIDX, 0); + mt76_rmw_field(dev, MT_DMA_TCRF1(1), MT_DMA_TCRF1_QIDX, 6); + mt76_rmw_field(dev, MT_DMA_TCRF1(2), MT_DMA_TCRF1_QIDX, 0); + + /* rro module init */ + mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE, 2); + mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, 3); + mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, 1); + + mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET), + MCU_WA_PARAM_HW_PATH_HIF_VER, + HIF_TXD_V2_1, 0); + + for (i = MT_BAND0; i <= MT_BAND2; i++) + mt7996_mac_init_band(dev, i); +} + +static int mt7996_txbf_init(struct mt7996_dev *dev) +{ + int ret; + + if (dev->dbdc_support) { + ret = mt7996_mcu_set_txbf(dev, BF_MOD_EN_CTRL); + if (ret) + return ret; + } + + /* trigger sounding packets */ + ret = mt7996_mcu_set_txbf(dev, BF_SOUNDING_ON); + if (ret) + return ret; + + /* enable eBF */ + return mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE); +} + +static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy, + enum mt76_band_id band) +{ + struct mt76_phy *mphy; + u32 mac_ofs, hif1_ofs = 0; + int ret; + + if (band != MT_BAND1 && band != MT_BAND2) + return 0; + + if ((band == MT_BAND1 && !dev->dbdc_support) || + (band == MT_BAND2 && !dev->tbtc_support)) + return 0; + + if (phy) + return 0; + + if (band == MT_BAND2 && dev->hif2) + hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); + + mphy = mt76_alloc_phy(&dev->mt76, sizeof(*phy), &mt7996_ops, band); + if (!mphy) + return -ENOMEM; + + phy = mphy->priv; + phy->dev = dev; + phy->mt76 = mphy; + mphy->dev->phys[band] = mphy; + + INIT_DELAYED_WORK(&mphy->mac_work, mt7996_mac_work); + + ret = mt7996_eeprom_parse_hw_cap(dev, phy); + if (ret) + goto error; + + mac_ofs = band == MT_BAND2 ? MT_EE_MAC_ADDR3 : MT_EE_MAC_ADDR2; + memcpy(mphy->macaddr, dev->mt76.eeprom.data + mac_ofs, ETH_ALEN); + /* Make the extra PHY MAC address local without overlapping with + * the usual MAC address allocation scheme on multiple virtual interfaces + */ + if (!is_valid_ether_addr(mphy->macaddr)) { + memcpy(mphy->macaddr, dev->mt76.eeprom.data + MT_EE_MAC_ADDR, + ETH_ALEN); + mphy->macaddr[0] |= 2; + mphy->macaddr[0] ^= BIT(7); + if (band == MT_BAND2) + mphy->macaddr[0] ^= BIT(6); + } + mt76_eeprom_override(mphy); + + /* init wiphy according to mphy and phy */ + mt7996_init_wiphy(mphy->hw); + ret = mt76_connac_init_tx_queues(phy->mt76, + MT_TXQ_ID(band), + MT7996_TX_RING_SIZE, + MT_TXQ_RING_BASE(band) + hif1_ofs, 0); + if (ret) + goto error; + + ret = mt76_register_phy(mphy, true, mt76_rates, + ARRAY_SIZE(mt76_rates)); + if (ret) + goto error; + + ret = mt7996_init_debugfs(phy); + if (ret) + goto error; + + return 0; + +error: + mphy->dev->phys[band] = NULL; + ieee80211_free_hw(mphy->hw); + return ret; +} + +static void +mt7996_unregister_phy(struct mt7996_phy *phy, enum mt76_band_id band) +{ + struct mt76_phy *mphy; + + if (!phy) + return; + + mphy = phy->dev->mt76.phys[band]; + mt76_unregister_phy(mphy); + ieee80211_free_hw(mphy->hw); + phy->dev->mt76.phys[band] = NULL; +} + +static void mt7996_init_work(struct work_struct *work) +{ + struct mt7996_dev *dev = container_of(work, struct mt7996_dev, + init_work); + + mt7996_mcu_set_eeprom(dev); + mt7996_mac_init(dev); + mt7996_init_txpower(dev, &dev->mphy.sband_2g.sband); + mt7996_init_txpower(dev, &dev->mphy.sband_5g.sband); + mt7996_init_txpower(dev, &dev->mphy.sband_6g.sband); + mt7996_txbf_init(dev); +} + +void mt7996_wfsys_reset(struct mt7996_dev *dev) +{ + mt76_set(dev, MT_WF_SUBSYS_RST, 0x1); + msleep(20); + + mt76_clear(dev, MT_WF_SUBSYS_RST, 0x1); + msleep(20); +} + +static int mt7996_init_hardware(struct mt7996_dev *dev) +{ + int ret, idx; + + mt76_wr(dev, MT_INT_SOURCE_CSR, ~0); + + INIT_WORK(&dev->init_work, mt7996_init_work); + + dev->dbdc_support = true; + dev->tbtc_support = true; + + ret = mt7996_dma_init(dev); + if (ret) + return ret; + + set_bit(MT76_STATE_INITIALIZED, &dev->mphy.state); + + ret = mt7996_mcu_init(dev); + if (ret) + return ret; + + ret = mt7996_eeprom_init(dev); + if (ret < 0) + return ret; + + /* Beacon and mgmt frames should occupy wcid 0 */ + idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA); + if (idx) + return -ENOSPC; + + dev->mt76.global_wcid.idx = idx; + dev->mt76.global_wcid.hw_key_idx = -1; + dev->mt76.global_wcid.tx_info |= MT_WCID_TX_INFO_SET; + rcu_assign_pointer(dev->mt76.wcid[idx], &dev->mt76.global_wcid); + + return 0; +} + +void mt7996_set_stream_vht_txbf_caps(struct mt7996_phy *phy) +{ + int sts; + u32 *cap; + + if (!phy->mt76->cap.has_5ghz) + return; + + sts = hweight16(phy->mt76->chainmask); + cap = &phy->mt76->sband_5g.sband.vht_cap.cap; + + *cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | + IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE | + (3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT); + + *cap &= ~(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK | + IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | + IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE); + + if (sts < 2) + return; + + *cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | + IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE | + FIELD_PREP(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK, sts - 1); +} + +static void +mt7996_set_stream_he_txbf_caps(struct mt7996_phy *phy, + struct ieee80211_sta_he_cap *he_cap, int vif) +{ + struct ieee80211_he_cap_elem *elem = &he_cap->he_cap_elem; + int sts = hweight16(phy->mt76->chainmask); + u8 c; + +#ifdef CONFIG_MAC80211_MESH + if (vif == NL80211_IFTYPE_MESH_POINT) + return; +#endif + + elem->phy_cap_info[3] &= ~IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER; + elem->phy_cap_info[4] &= ~IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER; + + c = IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK | + IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK; + elem->phy_cap_info[5] &= ~c; + + c = IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB | + IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB; + elem->phy_cap_info[6] &= ~c; + + elem->phy_cap_info[7] &= ~IEEE80211_HE_PHY_CAP7_MAX_NC_MASK; + + c = IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US | + IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO | + IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO; + elem->phy_cap_info[2] |= c; + + c = IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE | + IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4 | + IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4; + elem->phy_cap_info[4] |= c; + + /* do not support NG16 due to spec D4.0 changes subcarrier idx */ + c = IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU | + IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU; + + if (vif == NL80211_IFTYPE_STATION) + c |= IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO; + + elem->phy_cap_info[6] |= c; + + if (sts < 2) + return; + + /* the maximum cap is 4 x 3, (Nr, Nc) = (3, 2) */ + elem->phy_cap_info[7] |= min_t(int, sts - 1, 2) << 3; + + if (vif != NL80211_IFTYPE_AP) + return; + + elem->phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER; + elem->phy_cap_info[4] |= IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER; + + c = FIELD_PREP(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK, + sts - 1) | + FIELD_PREP(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK, + sts - 1); + elem->phy_cap_info[5] |= c; + + c = IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB | + IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB; + elem->phy_cap_info[6] |= c; + + c = IEEE80211_HE_PHY_CAP7_STBC_TX_ABOVE_80MHZ | + IEEE80211_HE_PHY_CAP7_STBC_RX_ABOVE_80MHZ; + elem->phy_cap_info[7] |= c; +} + +static void +mt7996_gen_ppe_thresh(u8 *he_ppet, int nss) +{ + u8 i, ppet_bits, ppet_size, ru_bit_mask = 0x7; /* HE80 */ + static const u8 ppet16_ppet8_ru3_ru0[] = {0x1c, 0xc7, 0x71}; + + he_ppet[0] = FIELD_PREP(IEEE80211_PPE_THRES_NSS_MASK, nss - 1) | + FIELD_PREP(IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK, + ru_bit_mask); + + ppet_bits = IEEE80211_PPE_THRES_INFO_PPET_SIZE * + nss * hweight8(ru_bit_mask) * 2; + ppet_size = DIV_ROUND_UP(ppet_bits, 8); + + for (i = 0; i < ppet_size - 1; i++) + he_ppet[i + 1] = ppet16_ppet8_ru3_ru0[i % 3]; + + he_ppet[i + 1] = ppet16_ppet8_ru3_ru0[i % 3] & + (0xff >> (8 - (ppet_bits - 1) % 8)); +} + +static int +mt7996_init_he_caps(struct mt7996_phy *phy, enum nl80211_band band, + struct ieee80211_sband_iftype_data *data) +{ + int i, idx = 0, nss = hweight8(phy->mt76->antenna_mask); + u16 mcs_map = 0; + + for (i = 0; i < 8; i++) { + if (i < nss) + mcs_map |= (IEEE80211_HE_MCS_SUPPORT_0_11 << (i * 2)); + else + mcs_map |= (IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2)); + } + + for (i = 0; i < NUM_NL80211_IFTYPES; i++) { + struct ieee80211_sta_he_cap *he_cap = &data[idx].he_cap; + struct ieee80211_he_cap_elem *he_cap_elem = + &he_cap->he_cap_elem; + struct ieee80211_he_mcs_nss_supp *he_mcs = + &he_cap->he_mcs_nss_supp; + + switch (i) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_AP: +#ifdef CONFIG_MAC80211_MESH + case NL80211_IFTYPE_MESH_POINT: +#endif + break; + default: + continue; + } + + data[idx].types_mask = BIT(i); + he_cap->has_he = true; + + he_cap_elem->mac_cap_info[0] = + IEEE80211_HE_MAC_CAP0_HTC_HE; + he_cap_elem->mac_cap_info[3] = + IEEE80211_HE_MAC_CAP3_OMI_CONTROL | + IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3; + he_cap_elem->mac_cap_info[4] = + IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU; + + if (band == NL80211_BAND_2GHZ) + he_cap_elem->phy_cap_info[0] = + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G; + else + he_cap_elem->phy_cap_info[0] = + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G; + + he_cap_elem->phy_cap_info[1] = + IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD; + he_cap_elem->phy_cap_info[2] = + IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ | + IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ; + + switch (i) { + case NL80211_IFTYPE_AP: + he_cap_elem->mac_cap_info[0] |= + IEEE80211_HE_MAC_CAP0_TWT_RES; + he_cap_elem->mac_cap_info[2] |= + IEEE80211_HE_MAC_CAP2_BSR; + he_cap_elem->mac_cap_info[4] |= + IEEE80211_HE_MAC_CAP4_BQR; + he_cap_elem->mac_cap_info[5] |= + IEEE80211_HE_MAC_CAP5_OM_CTRL_UL_MU_DATA_DIS_RX; + he_cap_elem->phy_cap_info[3] |= + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_QPSK | + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_QPSK; + he_cap_elem->phy_cap_info[6] |= + IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE | + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT; + he_cap_elem->phy_cap_info[9] |= + IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU | + IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU; + break; + case NL80211_IFTYPE_STATION: + he_cap_elem->mac_cap_info[1] |= + IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US; + + if (band == NL80211_BAND_2GHZ) + he_cap_elem->phy_cap_info[0] |= + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_2G; + else + he_cap_elem->phy_cap_info[0] |= + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_5G; + + he_cap_elem->phy_cap_info[1] |= + IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | + IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US; + he_cap_elem->phy_cap_info[3] |= + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_QPSK | + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_QPSK; + he_cap_elem->phy_cap_info[6] |= + IEEE80211_HE_PHY_CAP6_TRIG_CQI_FB | + IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE | + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT; + he_cap_elem->phy_cap_info[7] |= + IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_SUPP | + IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI; + he_cap_elem->phy_cap_info[8] |= + IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G | + IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU | + IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU | + IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_484; + he_cap_elem->phy_cap_info[9] |= + IEEE80211_HE_PHY_CAP9_LONGER_THAN_16_SIGB_OFDM_SYM | + IEEE80211_HE_PHY_CAP9_NON_TRIGGERED_CQI_FEEDBACK | + IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU | + IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU | + IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB | + IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB; + break; + } + + he_mcs->rx_mcs_80 = cpu_to_le16(mcs_map); + he_mcs->tx_mcs_80 = cpu_to_le16(mcs_map); + he_mcs->rx_mcs_160 = cpu_to_le16(mcs_map); + he_mcs->tx_mcs_160 = cpu_to_le16(mcs_map); + he_mcs->rx_mcs_80p80 = cpu_to_le16(mcs_map); + he_mcs->tx_mcs_80p80 = cpu_to_le16(mcs_map); + + mt7996_set_stream_he_txbf_caps(phy, he_cap, i); + + memset(he_cap->ppe_thres, 0, sizeof(he_cap->ppe_thres)); + if (he_cap_elem->phy_cap_info[6] & + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) { + mt7996_gen_ppe_thresh(he_cap->ppe_thres, nss); + } else { + he_cap_elem->phy_cap_info[9] |= + IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US; + } + + if (band == NL80211_BAND_6GHZ) { + u16 cap = IEEE80211_HE_6GHZ_CAP_TX_ANTPAT_CONS | + IEEE80211_HE_6GHZ_CAP_RX_ANTPAT_CONS; + + cap |= u16_encode_bits(IEEE80211_HT_MPDU_DENSITY_2, + IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START) | + u16_encode_bits(IEEE80211_VHT_MAX_AMPDU_1024K, + IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP) | + u16_encode_bits(IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454, + IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN); + + data[idx].he_6ghz_capa.capa = cpu_to_le16(cap); + } + + idx++; + } + + return idx; +} + +void mt7996_set_stream_he_caps(struct mt7996_phy *phy) +{ + struct ieee80211_sband_iftype_data *data; + struct ieee80211_supported_band *band; + int n; + + if (phy->mt76->cap.has_2ghz) { + data = phy->iftype[NL80211_BAND_2GHZ]; + n = mt7996_init_he_caps(phy, NL80211_BAND_2GHZ, data); + + band = &phy->mt76->sband_2g.sband; + band->iftype_data = data; + band->n_iftype_data = n; + } + + if (phy->mt76->cap.has_5ghz) { + data = phy->iftype[NL80211_BAND_5GHZ]; + n = mt7996_init_he_caps(phy, NL80211_BAND_5GHZ, data); + + band = &phy->mt76->sband_5g.sband; + band->iftype_data = data; + band->n_iftype_data = n; + } + + if (phy->mt76->cap.has_6ghz) { + data = phy->iftype[NL80211_BAND_6GHZ]; + n = mt7996_init_he_caps(phy, NL80211_BAND_6GHZ, data); + + band = &phy->mt76->sband_6g.sband; + band->iftype_data = data; + band->n_iftype_data = n; + } +} + +int mt7996_register_device(struct mt7996_dev *dev) +{ + struct ieee80211_hw *hw = mt76_hw(dev); + int ret; + + dev->phy.dev = dev; + dev->phy.mt76 = &dev->mt76.phy; + dev->mt76.phy.priv = &dev->phy; + INIT_WORK(&dev->rc_work, mt7996_mac_sta_rc_work); + INIT_DELAYED_WORK(&dev->mphy.mac_work, mt7996_mac_work); + INIT_LIST_HEAD(&dev->sta_rc_list); + INIT_LIST_HEAD(&dev->sta_poll_list); + INIT_LIST_HEAD(&dev->twt_list); + spin_lock_init(&dev->sta_poll_lock); + + init_waitqueue_head(&dev->reset_wait); + INIT_WORK(&dev->reset_work, mt7996_mac_reset_work); + + ret = mt7996_init_hardware(dev); + if (ret) + return ret; + + mt7996_init_wiphy(hw); + + /* init led callbacks */ + if (IS_ENABLED(CONFIG_MT76_LEDS)) { + dev->mt76.led_cdev.brightness_set = mt7996_led_set_brightness; + dev->mt76.led_cdev.blink_set = mt7996_led_set_blink; + } + + ret = mt76_register_device(&dev->mt76, true, mt76_rates, + ARRAY_SIZE(mt76_rates)); + if (ret) + return ret; + + ieee80211_queue_work(mt76_hw(dev), &dev->init_work); + + ret = mt7996_register_phy(dev, mt7996_phy2(dev), MT_BAND1); + if (ret) + return ret; + + ret = mt7996_register_phy(dev, mt7996_phy3(dev), MT_BAND2); + if (ret) + return ret; + + return mt7996_init_debugfs(&dev->phy); +} + +void mt7996_unregister_device(struct mt7996_dev *dev) +{ + mt7996_unregister_phy(mt7996_phy3(dev), MT_BAND2); + mt7996_unregister_phy(mt7996_phy2(dev), MT_BAND1); + mt76_unregister_device(&dev->mt76); + mt7996_mcu_exit(dev); + mt7996_tx_token_put(dev); + mt7996_dma_cleanup(dev); + tasklet_disable(&dev->irq_tasklet); + + mt76_free_device(&dev->mt76); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c new file mode 100644 index 000000000000..1c35f0f15452 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -0,0 +1,2468 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#include +#include +#include "mt7996.h" +#include "../dma.h" +#include "mac.h" +#include "mcu.h" + +#define to_rssi(field, rxv) ((FIELD_GET(field, rxv) - 220) / 2) + +#define HE_BITS(f) cpu_to_le16(IEEE80211_RADIOTAP_HE_##f) +#define HE_PREP(f, m, v) le16_encode_bits(le32_get_bits(v, MT_CRXV_HE_##m),\ + IEEE80211_RADIOTAP_HE_##f) + +static const struct mt7996_dfs_radar_spec etsi_radar_specs = { + .pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 }, + .radar_pattern = { + [5] = { 1, 0, 6, 32, 28, 0, 990, 5010, 17, 1, 1 }, + [6] = { 1, 0, 9, 32, 28, 0, 615, 5010, 27, 1, 1 }, + [7] = { 1, 0, 15, 32, 28, 0, 240, 445, 27, 1, 1 }, + [8] = { 1, 0, 12, 32, 28, 0, 240, 510, 42, 1, 1 }, + [9] = { 1, 1, 0, 0, 0, 0, 2490, 3343, 14, 0, 0, 12, 32, 28, { }, 126 }, + [10] = { 1, 1, 0, 0, 0, 0, 2490, 3343, 14, 0, 0, 15, 32, 24, { }, 126 }, + [11] = { 1, 1, 0, 0, 0, 0, 823, 2510, 14, 0, 0, 18, 32, 28, { }, 54 }, + [12] = { 1, 1, 0, 0, 0, 0, 823, 2510, 14, 0, 0, 27, 32, 24, { }, 54 }, + }, +}; + +static const struct mt7996_dfs_radar_spec fcc_radar_specs = { + .pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 }, + .radar_pattern = { + [0] = { 1, 0, 8, 32, 28, 0, 508, 3076, 13, 1, 1 }, + [1] = { 1, 0, 12, 32, 28, 0, 140, 240, 17, 1, 1 }, + [2] = { 1, 0, 8, 32, 28, 0, 190, 510, 22, 1, 1 }, + [3] = { 1, 0, 6, 32, 28, 0, 190, 510, 32, 1, 1 }, + [4] = { 1, 0, 9, 255, 28, 0, 323, 343, 13, 1, 32 }, + }, +}; + +static const struct mt7996_dfs_radar_spec jp_radar_specs = { + .pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 }, + .radar_pattern = { + [0] = { 1, 0, 8, 32, 28, 0, 508, 3076, 13, 1, 1 }, + [1] = { 1, 0, 12, 32, 28, 0, 140, 240, 17, 1, 1 }, + [2] = { 1, 0, 8, 32, 28, 0, 190, 510, 22, 1, 1 }, + [3] = { 1, 0, 6, 32, 28, 0, 190, 510, 32, 1, 1 }, + [4] = { 1, 0, 9, 255, 28, 0, 323, 343, 13, 1, 32 }, + [13] = { 1, 0, 7, 32, 28, 0, 3836, 3856, 14, 1, 1 }, + [14] = { 1, 0, 6, 32, 28, 0, 615, 5010, 110, 1, 1 }, + [15] = { 1, 1, 0, 0, 0, 0, 15, 5010, 110, 0, 0, 12, 32, 28 }, + }, +}; + +static struct mt76_wcid *mt7996_rx_get_wcid(struct mt7996_dev *dev, + u16 idx, bool unicast) +{ + struct mt7996_sta *sta; + struct mt76_wcid *wcid; + + if (idx >= ARRAY_SIZE(dev->mt76.wcid)) + return NULL; + + wcid = rcu_dereference(dev->mt76.wcid[idx]); + if (unicast || !wcid) + return wcid; + + if (!wcid->sta) + return NULL; + + sta = container_of(wcid, struct mt7996_sta, wcid); + if (!sta->vif) + return NULL; + + return &sta->vif->sta.wcid; +} + +void mt7996_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps) +{ +} + +bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask) +{ + mt76_rmw(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_WLAN_IDX, + FIELD_PREP(MT_WTBL_UPDATE_WLAN_IDX, idx) | mask); + + return mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, + 0, 5000); +} + +u32 mt7996_mac_wtbl_lmac_addr(struct mt7996_dev *dev, u16 wcid, u8 dw) +{ + mt76_wr(dev, MT_WTBLON_TOP_WDUCR, + FIELD_PREP(MT_WTBLON_TOP_WDUCR_GROUP, (wcid >> 7))); + + return MT_WTBL_LMAC_OFFS(wcid, dw); +} + +static void mt7996_mac_sta_poll(struct mt7996_dev *dev) +{ + static const u8 ac_to_tid[] = { + [IEEE80211_AC_BE] = 0, + [IEEE80211_AC_BK] = 1, + [IEEE80211_AC_VI] = 4, + [IEEE80211_AC_VO] = 6 + }; + struct ieee80211_sta *sta; + struct mt7996_sta *msta; + struct rate_info *rate; + u32 tx_time[IEEE80211_NUM_ACS], rx_time[IEEE80211_NUM_ACS]; + LIST_HEAD(sta_poll_list); + int i; + + spin_lock_bh(&dev->sta_poll_lock); + list_splice_init(&dev->sta_poll_list, &sta_poll_list); + spin_unlock_bh(&dev->sta_poll_lock); + + rcu_read_lock(); + + while (true) { + bool clear = false; + u32 addr, val; + u16 idx; + u8 bw; + + spin_lock_bh(&dev->sta_poll_lock); + if (list_empty(&sta_poll_list)) { + spin_unlock_bh(&dev->sta_poll_lock); + break; + } + msta = list_first_entry(&sta_poll_list, + struct mt7996_sta, poll_list); + list_del_init(&msta->poll_list); + spin_unlock_bh(&dev->sta_poll_lock); + + idx = msta->wcid.idx; + addr = mt7996_mac_wtbl_lmac_addr(dev, idx, 20); + + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + u32 tx_last = msta->airtime_ac[i]; + u32 rx_last = msta->airtime_ac[i + 4]; + + msta->airtime_ac[i] = mt76_rr(dev, addr); + msta->airtime_ac[i + 4] = mt76_rr(dev, addr + 4); + + tx_time[i] = msta->airtime_ac[i] - tx_last; + rx_time[i] = msta->airtime_ac[i + 4] - rx_last; + + if ((tx_last | rx_last) & BIT(30)) + clear = true; + + addr += 8; + } + + if (clear) { + mt7996_mac_wtbl_update(dev, idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + memset(msta->airtime_ac, 0, sizeof(msta->airtime_ac)); + } + + if (!msta->wcid.sta) + continue; + + sta = container_of((void *)msta, struct ieee80211_sta, + drv_priv); + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + u8 q = mt76_connac_lmac_mapping(i); + u32 tx_cur = tx_time[q]; + u32 rx_cur = rx_time[q]; + u8 tid = ac_to_tid[i]; + + if (!tx_cur && !rx_cur) + continue; + + ieee80211_sta_register_airtime(sta, tid, tx_cur, rx_cur); + } + + /* We don't support reading GI info from txs packets. + * For accurate tx status reporting and AQL improvement, + * we need to make sure that flags match so polling GI + * from per-sta counters directly. + */ + rate = &msta->wcid.rate; + + switch (rate->bw) { + case RATE_INFO_BW_160: + bw = IEEE80211_STA_RX_BW_160; + break; + case RATE_INFO_BW_80: + bw = IEEE80211_STA_RX_BW_80; + break; + case RATE_INFO_BW_40: + bw = IEEE80211_STA_RX_BW_40; + break; + default: + bw = IEEE80211_STA_RX_BW_20; + break; + } + + addr = mt7996_mac_wtbl_lmac_addr(dev, idx, 6); + val = mt76_rr(dev, addr); + if (rate->flags & RATE_INFO_FLAGS_HE_MCS) { + u8 offs = 24 + 2 * bw; + + rate->he_gi = (val & (0x3 << offs)) >> offs; + } else if (rate->flags & + (RATE_INFO_FLAGS_VHT_MCS | RATE_INFO_FLAGS_MCS)) { + if (val & BIT(12 + bw)) + rate->flags |= RATE_INFO_FLAGS_SHORT_GI; + else + rate->flags &= ~RATE_INFO_FLAGS_SHORT_GI; + } + } + + rcu_read_unlock(); +} + +static void +mt7996_mac_decode_he_radiotap_ru(struct mt76_rx_status *status, + struct ieee80211_radiotap_he *he, + __le32 *rxv) +{ + u32 ru_h, ru_l; + u8 ru, offs = 0; + + ru_l = le32_get_bits(rxv[0], MT_PRXV_HE_RU_ALLOC_L); + ru_h = le32_get_bits(rxv[1], MT_PRXV_HE_RU_ALLOC_H); + ru = (u8)(ru_l | ru_h << 4); + + status->bw = RATE_INFO_BW_HE_RU; + + switch (ru) { + case 0 ... 36: + status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_26; + offs = ru; + break; + case 37 ... 52: + status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_52; + offs = ru - 37; + break; + case 53 ... 60: + status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_106; + offs = ru - 53; + break; + case 61 ... 64: + status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_242; + offs = ru - 61; + break; + case 65 ... 66: + status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_484; + offs = ru - 65; + break; + case 67: + status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_996; + break; + case 68: + status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_2x996; + break; + } + + he->data1 |= HE_BITS(DATA1_BW_RU_ALLOC_KNOWN); + he->data2 |= HE_BITS(DATA2_RU_OFFSET_KNOWN) | + le16_encode_bits(offs, + IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET); +} + +static void +mt7996_mac_decode_he_mu_radiotap(struct sk_buff *skb, __le32 *rxv) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + static const struct ieee80211_radiotap_he_mu mu_known = { + .flags1 = HE_BITS(MU_FLAGS1_SIG_B_MCS_KNOWN) | + HE_BITS(MU_FLAGS1_SIG_B_DCM_KNOWN) | + HE_BITS(MU_FLAGS1_CH1_RU_KNOWN) | + HE_BITS(MU_FLAGS1_SIG_B_SYMS_USERS_KNOWN), + .flags2 = HE_BITS(MU_FLAGS2_BW_FROM_SIG_A_BW_KNOWN), + }; + struct ieee80211_radiotap_he_mu *he_mu = NULL; + + status->flag |= RX_FLAG_RADIOTAP_HE_MU; + + he_mu = skb_push(skb, sizeof(mu_known)); + memcpy(he_mu, &mu_known, sizeof(mu_known)); + +#define MU_PREP(f, v) le16_encode_bits(v, IEEE80211_RADIOTAP_HE_MU_##f) + + he_mu->flags1 |= MU_PREP(FLAGS1_SIG_B_MCS, status->rate_idx); + if (status->he_dcm) + he_mu->flags1 |= MU_PREP(FLAGS1_SIG_B_DCM, status->he_dcm); + + he_mu->flags2 |= MU_PREP(FLAGS2_BW_FROM_SIG_A_BW, status->bw) | + MU_PREP(FLAGS2_SIG_B_SYMS_USERS, + le32_get_bits(rxv[2], MT_CRXV_HE_NUM_USER)); + + he_mu->ru_ch1[0] = le32_get_bits(rxv[3], MT_CRXV_HE_RU0); + + if (status->bw >= RATE_INFO_BW_40) { + he_mu->flags1 |= HE_BITS(MU_FLAGS1_CH2_RU_KNOWN); + he_mu->ru_ch2[0] = le32_get_bits(rxv[3], MT_CRXV_HE_RU1); + } + + if (status->bw >= RATE_INFO_BW_80) { + he_mu->ru_ch1[1] = le32_get_bits(rxv[3], MT_CRXV_HE_RU2); + he_mu->ru_ch2[1] = le32_get_bits(rxv[3], MT_CRXV_HE_RU3); + } +} + +static void +mt7996_mac_decode_he_radiotap(struct sk_buff *skb, __le32 *rxv, u8 mode) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + static const struct ieee80211_radiotap_he known = { + .data1 = HE_BITS(DATA1_DATA_MCS_KNOWN) | + HE_BITS(DATA1_DATA_DCM_KNOWN) | + HE_BITS(DATA1_STBC_KNOWN) | + HE_BITS(DATA1_CODING_KNOWN) | + HE_BITS(DATA1_LDPC_XSYMSEG_KNOWN) | + HE_BITS(DATA1_DOPPLER_KNOWN) | + HE_BITS(DATA1_SPTL_REUSE_KNOWN) | + HE_BITS(DATA1_BSS_COLOR_KNOWN), + .data2 = HE_BITS(DATA2_GI_KNOWN) | + HE_BITS(DATA2_TXBF_KNOWN) | + HE_BITS(DATA2_PE_DISAMBIG_KNOWN) | + HE_BITS(DATA2_TXOP_KNOWN), + }; + struct ieee80211_radiotap_he *he = NULL; + u32 ltf_size = le32_get_bits(rxv[2], MT_CRXV_HE_LTF_SIZE) + 1; + + status->flag |= RX_FLAG_RADIOTAP_HE; + + he = skb_push(skb, sizeof(known)); + memcpy(he, &known, sizeof(known)); + + he->data3 = HE_PREP(DATA3_BSS_COLOR, BSS_COLOR, rxv[14]) | + HE_PREP(DATA3_LDPC_XSYMSEG, LDPC_EXT_SYM, rxv[2]); + he->data4 = HE_PREP(DATA4_SU_MU_SPTL_REUSE, SR_MASK, rxv[11]); + he->data5 = HE_PREP(DATA5_PE_DISAMBIG, PE_DISAMBIG, rxv[2]) | + le16_encode_bits(ltf_size, + IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE); + if (le32_to_cpu(rxv[0]) & MT_PRXV_TXBF) + he->data5 |= HE_BITS(DATA5_TXBF); + he->data6 = HE_PREP(DATA6_TXOP, TXOP_DUR, rxv[14]) | + HE_PREP(DATA6_DOPPLER, DOPPLER, rxv[14]); + + switch (mode) { + case MT_PHY_TYPE_HE_SU: + he->data1 |= HE_BITS(DATA1_FORMAT_SU) | + HE_BITS(DATA1_UL_DL_KNOWN) | + HE_BITS(DATA1_BEAM_CHANGE_KNOWN) | + HE_BITS(DATA1_BW_RU_ALLOC_KNOWN); + + he->data3 |= HE_PREP(DATA3_BEAM_CHANGE, BEAM_CHNG, rxv[14]) | + HE_PREP(DATA3_UL_DL, UPLINK, rxv[2]); + break; + case MT_PHY_TYPE_HE_EXT_SU: + he->data1 |= HE_BITS(DATA1_FORMAT_EXT_SU) | + HE_BITS(DATA1_UL_DL_KNOWN) | + HE_BITS(DATA1_BW_RU_ALLOC_KNOWN); + + he->data3 |= HE_PREP(DATA3_UL_DL, UPLINK, rxv[2]); + break; + case MT_PHY_TYPE_HE_MU: + he->data1 |= HE_BITS(DATA1_FORMAT_MU) | + HE_BITS(DATA1_UL_DL_KNOWN); + + he->data3 |= HE_PREP(DATA3_UL_DL, UPLINK, rxv[2]); + he->data4 |= HE_PREP(DATA4_MU_STA_ID, MU_AID, rxv[7]); + + mt7996_mac_decode_he_radiotap_ru(status, he, rxv); + mt7996_mac_decode_he_mu_radiotap(skb, rxv); + break; + case MT_PHY_TYPE_HE_TB: + he->data1 |= HE_BITS(DATA1_FORMAT_TRIG) | + HE_BITS(DATA1_SPTL_REUSE2_KNOWN) | + HE_BITS(DATA1_SPTL_REUSE3_KNOWN) | + HE_BITS(DATA1_SPTL_REUSE4_KNOWN); + + he->data4 |= HE_PREP(DATA4_TB_SPTL_REUSE1, SR_MASK, rxv[11]) | + HE_PREP(DATA4_TB_SPTL_REUSE2, SR1_MASK, rxv[11]) | + HE_PREP(DATA4_TB_SPTL_REUSE3, SR2_MASK, rxv[11]) | + HE_PREP(DATA4_TB_SPTL_REUSE4, SR3_MASK, rxv[11]); + + mt7996_mac_decode_he_radiotap_ru(status, he, rxv); + break; + default: + break; + } +} + +/* The HW does not translate the mac header to 802.3 for mesh point */ +static int mt7996_reverse_frag0_hdr_trans(struct sk_buff *skb, u16 hdr_gap) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct ethhdr *eth_hdr = (struct ethhdr *)(skb->data + hdr_gap); + struct mt7996_sta *msta = (struct mt7996_sta *)status->wcid; + __le32 *rxd = (__le32 *)skb->data; + struct ieee80211_sta *sta; + struct ieee80211_vif *vif; + struct ieee80211_hdr hdr; + u16 frame_control; + + if (le32_get_bits(rxd[3], MT_RXD3_NORMAL_ADDR_TYPE) != + MT_RXD3_NORMAL_U2M) + return -EINVAL; + + if (!(le32_to_cpu(rxd[1]) & MT_RXD1_NORMAL_GROUP_4)) + return -EINVAL; + + if (!msta || !msta->vif) + return -EINVAL; + + sta = container_of((void *)msta, struct ieee80211_sta, drv_priv); + vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv); + + /* store the info from RXD and ethhdr to avoid being overridden */ + frame_control = le32_get_bits(rxd[8], MT_RXD8_FRAME_CONTROL); + hdr.frame_control = cpu_to_le16(frame_control); + hdr.seq_ctrl = cpu_to_le16(le32_get_bits(rxd[10], MT_RXD10_SEQ_CTRL)); + hdr.duration_id = 0; + + ether_addr_copy(hdr.addr1, vif->addr); + ether_addr_copy(hdr.addr2, sta->addr); + switch (frame_control & (IEEE80211_FCTL_TODS | + IEEE80211_FCTL_FROMDS)) { + case 0: + ether_addr_copy(hdr.addr3, vif->bss_conf.bssid); + break; + case IEEE80211_FCTL_FROMDS: + ether_addr_copy(hdr.addr3, eth_hdr->h_source); + break; + case IEEE80211_FCTL_TODS: + ether_addr_copy(hdr.addr3, eth_hdr->h_dest); + break; + case IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS: + ether_addr_copy(hdr.addr3, eth_hdr->h_dest); + ether_addr_copy(hdr.addr4, eth_hdr->h_source); + break; + default: + break; + } + + skb_pull(skb, hdr_gap + sizeof(struct ethhdr) - 2); + if (eth_hdr->h_proto == cpu_to_be16(ETH_P_AARP) || + eth_hdr->h_proto == cpu_to_be16(ETH_P_IPX)) + ether_addr_copy(skb_push(skb, ETH_ALEN), bridge_tunnel_header); + else if (be16_to_cpu(eth_hdr->h_proto) >= ETH_P_802_3_MIN) + ether_addr_copy(skb_push(skb, ETH_ALEN), rfc1042_header); + else + skb_pull(skb, 2); + + if (ieee80211_has_order(hdr.frame_control)) + memcpy(skb_push(skb, IEEE80211_HT_CTL_LEN), &rxd[11], + IEEE80211_HT_CTL_LEN); + if (ieee80211_is_data_qos(hdr.frame_control)) { + __le16 qos_ctrl; + + qos_ctrl = cpu_to_le16(le32_get_bits(rxd[10], MT_RXD10_QOS_CTL)); + memcpy(skb_push(skb, IEEE80211_QOS_CTL_LEN), &qos_ctrl, + IEEE80211_QOS_CTL_LEN); + } + + if (ieee80211_has_a4(hdr.frame_control)) + memcpy(skb_push(skb, sizeof(hdr)), &hdr, sizeof(hdr)); + else + memcpy(skb_push(skb, sizeof(hdr) - 6), &hdr, sizeof(hdr) - 6); + + return 0; +} + +static int +mt7996_mac_fill_rx_rate(struct mt7996_dev *dev, + struct mt76_rx_status *status, + struct ieee80211_supported_band *sband, + __le32 *rxv, u8 *mode) +{ + u32 v0, v2; + u8 stbc, gi, bw, dcm, nss; + int i, idx; + bool cck = false; + + v0 = le32_to_cpu(rxv[0]); + v2 = le32_to_cpu(rxv[2]); + + idx = FIELD_GET(MT_PRXV_TX_RATE, v0); + i = idx; + nss = FIELD_GET(MT_PRXV_NSTS, v0) + 1; + + stbc = FIELD_GET(MT_PRXV_HT_STBC, v2); + gi = FIELD_GET(MT_PRXV_HT_SHORT_GI, v2); + *mode = FIELD_GET(MT_PRXV_TX_MODE, v2); + dcm = FIELD_GET(MT_PRXV_DCM, v2); + bw = FIELD_GET(MT_PRXV_FRAME_MODE, v2); + + switch (*mode) { + case MT_PHY_TYPE_CCK: + cck = true; + fallthrough; + case MT_PHY_TYPE_OFDM: + i = mt76_get_rate(&dev->mt76, sband, i, cck); + break; + case MT_PHY_TYPE_HT_GF: + case MT_PHY_TYPE_HT: + status->encoding = RX_ENC_HT; + if (gi) + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + if (i > 31) + return -EINVAL; + break; + case MT_PHY_TYPE_VHT: + status->nss = nss; + status->encoding = RX_ENC_VHT; + if (gi) + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + if (i > 11) + return -EINVAL; + break; + case MT_PHY_TYPE_HE_MU: + case MT_PHY_TYPE_HE_SU: + case MT_PHY_TYPE_HE_EXT_SU: + case MT_PHY_TYPE_HE_TB: + status->nss = nss; + status->encoding = RX_ENC_HE; + i &= GENMASK(3, 0); + + if (gi <= NL80211_RATE_INFO_HE_GI_3_2) + status->he_gi = gi; + + status->he_dcm = dcm; + break; + default: + return -EINVAL; + } + status->rate_idx = i; + + switch (bw) { + case IEEE80211_STA_RX_BW_20: + break; + case IEEE80211_STA_RX_BW_40: + if (*mode & MT_PHY_TYPE_HE_EXT_SU && + (idx & MT_PRXV_TX_ER_SU_106T)) { + status->bw = RATE_INFO_BW_HE_RU; + status->he_ru = + NL80211_RATE_INFO_HE_RU_ALLOC_106; + } else { + status->bw = RATE_INFO_BW_40; + } + break; + case IEEE80211_STA_RX_BW_80: + status->bw = RATE_INFO_BW_80; + break; + case IEEE80211_STA_RX_BW_160: + status->bw = RATE_INFO_BW_160; + break; + default: + return -EINVAL; + } + + status->enc_flags |= RX_ENC_FLAG_STBC_MASK * stbc; + if (*mode < MT_PHY_TYPE_HE_SU && gi) + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + + return 0; +} + +static int +mt7996_mac_fill_rx(struct mt7996_dev *dev, struct sk_buff *skb) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt7996_phy *phy = &dev->phy; + struct ieee80211_supported_band *sband; + __le32 *rxd = (__le32 *)skb->data; + __le32 *rxv = NULL; + u32 rxd0 = le32_to_cpu(rxd[0]); + u32 rxd1 = le32_to_cpu(rxd[1]); + u32 rxd2 = le32_to_cpu(rxd[2]); + u32 rxd3 = le32_to_cpu(rxd[3]); + u32 rxd4 = le32_to_cpu(rxd[4]); + u32 csum_mask = MT_RXD0_NORMAL_IP_SUM | MT_RXD0_NORMAL_UDP_TCP_SUM; + u32 csum_status = *(u32 *)skb->cb; + bool unicast, insert_ccmp_hdr = false; + u8 remove_pad, amsdu_info, band_idx; + u8 mode = 0, qos_ctl = 0; + bool hdr_trans; + u16 hdr_gap; + u16 seq_ctrl = 0; + __le16 fc = 0; + int idx; + + memset(status, 0, sizeof(*status)); + + band_idx = FIELD_GET(MT_RXD1_NORMAL_BAND_IDX, rxd1); + mphy = dev->mt76.phys[band_idx]; + phy = mphy->priv; + status->phy_idx = mphy->band_idx; + + if (!test_bit(MT76_STATE_RUNNING, &mphy->state)) + return -EINVAL; + + if (rxd2 & MT_RXD2_NORMAL_AMSDU_ERR) + return -EINVAL; + + hdr_trans = rxd2 & MT_RXD2_NORMAL_HDR_TRANS; + if (hdr_trans && (rxd1 & MT_RXD1_NORMAL_CM)) + return -EINVAL; + + /* ICV error or CCMP/BIP/WPI MIC error */ + if (rxd1 & MT_RXD1_NORMAL_ICV_ERR) + status->flag |= RX_FLAG_ONLY_MONITOR; + + unicast = FIELD_GET(MT_RXD3_NORMAL_ADDR_TYPE, rxd3) == MT_RXD3_NORMAL_U2M; + idx = FIELD_GET(MT_RXD1_NORMAL_WLAN_IDX, rxd1); + status->wcid = mt7996_rx_get_wcid(dev, idx, unicast); + + if (status->wcid) { + struct mt7996_sta *msta; + + msta = container_of(status->wcid, struct mt7996_sta, wcid); + spin_lock_bh(&dev->sta_poll_lock); + if (list_empty(&msta->poll_list)) + list_add_tail(&msta->poll_list, &dev->sta_poll_list); + spin_unlock_bh(&dev->sta_poll_lock); + } + + status->freq = mphy->chandef.chan->center_freq; + status->band = mphy->chandef.chan->band; + if (status->band == NL80211_BAND_5GHZ) + sband = &mphy->sband_5g.sband; + else if (status->band == NL80211_BAND_6GHZ) + sband = &mphy->sband_6g.sband; + else + sband = &mphy->sband_2g.sband; + + if (!sband->channels) + return -EINVAL; + + if ((rxd0 & csum_mask) == csum_mask && + !(csum_status & (BIT(0) | BIT(2) | BIT(3)))) + skb->ip_summed = CHECKSUM_UNNECESSARY; + + if (rxd1 & MT_RXD3_NORMAL_FCS_ERR) + status->flag |= RX_FLAG_FAILED_FCS_CRC; + + if (rxd1 & MT_RXD1_NORMAL_TKIP_MIC_ERR) + status->flag |= RX_FLAG_MMIC_ERROR; + + if (FIELD_GET(MT_RXD2_NORMAL_SEC_MODE, rxd2) != 0 && + !(rxd1 & (MT_RXD1_NORMAL_CLM | MT_RXD1_NORMAL_CM))) { + status->flag |= RX_FLAG_DECRYPTED; + status->flag |= RX_FLAG_IV_STRIPPED; + status->flag |= RX_FLAG_MMIC_STRIPPED | RX_FLAG_MIC_STRIPPED; + } + + remove_pad = FIELD_GET(MT_RXD2_NORMAL_HDR_OFFSET, rxd2); + + if (rxd2 & MT_RXD2_NORMAL_MAX_LEN_ERROR) + return -EINVAL; + + rxd += 8; + if (rxd1 & MT_RXD1_NORMAL_GROUP_4) { + u32 v0 = le32_to_cpu(rxd[0]); + u32 v2 = le32_to_cpu(rxd[2]); + + fc = cpu_to_le16(FIELD_GET(MT_RXD8_FRAME_CONTROL, v0)); + qos_ctl = FIELD_GET(MT_RXD10_QOS_CTL, v2); + seq_ctrl = FIELD_GET(MT_RXD10_SEQ_CTRL, v2); + + rxd += 4; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + + if (rxd1 & MT_RXD1_NORMAL_GROUP_1) { + u8 *data = (u8 *)rxd; + + if (status->flag & RX_FLAG_DECRYPTED) { + switch (FIELD_GET(MT_RXD2_NORMAL_SEC_MODE, rxd2)) { + case MT_CIPHER_AES_CCMP: + case MT_CIPHER_CCMP_CCX: + case MT_CIPHER_CCMP_256: + insert_ccmp_hdr = + FIELD_GET(MT_RXD2_NORMAL_FRAG, rxd2); + fallthrough; + case MT_CIPHER_TKIP: + case MT_CIPHER_TKIP_NO_MIC: + case MT_CIPHER_GCMP: + case MT_CIPHER_GCMP_256: + status->iv[0] = data[5]; + status->iv[1] = data[4]; + status->iv[2] = data[3]; + status->iv[3] = data[2]; + status->iv[4] = data[1]; + status->iv[5] = data[0]; + break; + default: + break; + } + } + rxd += 4; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + + if (rxd1 & MT_RXD1_NORMAL_GROUP_2) { + status->timestamp = le32_to_cpu(rxd[0]); + status->flag |= RX_FLAG_MACTIME_START; + + if (!(rxd2 & MT_RXD2_NORMAL_NON_AMPDU)) { + status->flag |= RX_FLAG_AMPDU_DETAILS; + + /* all subframes of an A-MPDU have the same timestamp */ + if (phy->rx_ampdu_ts != status->timestamp) { + if (!++phy->ampdu_ref) + phy->ampdu_ref++; + } + phy->rx_ampdu_ts = status->timestamp; + + status->ampdu_ref = phy->ampdu_ref; + } + + rxd += 4; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + + /* RXD Group 3 - P-RXV */ + if (rxd1 & MT_RXD1_NORMAL_GROUP_3) { + u32 v3; + int ret; + + rxv = rxd; + rxd += 4; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + + v3 = le32_to_cpu(rxv[3]); + + status->chains = mphy->antenna_mask; + status->chain_signal[0] = to_rssi(MT_PRXV_RCPI0, v3); + status->chain_signal[1] = to_rssi(MT_PRXV_RCPI1, v3); + status->chain_signal[2] = to_rssi(MT_PRXV_RCPI2, v3); + status->chain_signal[3] = to_rssi(MT_PRXV_RCPI3, v3); + + /* RXD Group 5 - C-RXV */ + if (rxd1 & MT_RXD1_NORMAL_GROUP_5) { + rxd += 24; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + + ret = mt7996_mac_fill_rx_rate(dev, status, sband, rxv, &mode); + if (ret < 0) + return ret; + } + + amsdu_info = FIELD_GET(MT_RXD4_NORMAL_PAYLOAD_FORMAT, rxd4); + status->amsdu = !!amsdu_info; + if (status->amsdu) { + status->first_amsdu = amsdu_info == MT_RXD4_FIRST_AMSDU_FRAME; + status->last_amsdu = amsdu_info == MT_RXD4_LAST_AMSDU_FRAME; + } + + hdr_gap = (u8 *)rxd - skb->data + 2 * remove_pad; + if (hdr_trans && ieee80211_has_morefrags(fc)) { + if (mt7996_reverse_frag0_hdr_trans(skb, hdr_gap)) + return -EINVAL; + hdr_trans = false; + } else { + int pad_start = 0; + + skb_pull(skb, hdr_gap); + if (!hdr_trans && status->amsdu) { + pad_start = ieee80211_get_hdrlen_from_skb(skb); + } else if (hdr_trans && (rxd2 & MT_RXD2_NORMAL_HDR_TRANS_ERROR)) { + /* When header translation failure is indicated, + * the hardware will insert an extra 2-byte field + * containing the data length after the protocol + * type field. + */ + pad_start = 12; + if (get_unaligned_be16(skb->data + pad_start) == ETH_P_8021Q) + pad_start += 4; + else + pad_start = 0; + } + + if (pad_start) { + memmove(skb->data + 2, skb->data, pad_start); + skb_pull(skb, 2); + } + } + + if (!hdr_trans) { + struct ieee80211_hdr *hdr; + + if (insert_ccmp_hdr) { + u8 key_id = FIELD_GET(MT_RXD1_NORMAL_KEY_ID, rxd1); + + mt76_insert_ccmp_hdr(skb, key_id); + } + + hdr = mt76_skb_get_hdr(skb); + fc = hdr->frame_control; + if (ieee80211_is_data_qos(fc)) { + seq_ctrl = le16_to_cpu(hdr->seq_ctrl); + qos_ctl = *ieee80211_get_qos_ctl(hdr); + } + } else { + status->flag |= RX_FLAG_8023; + } + + if (rxv && mode >= MT_PHY_TYPE_HE_SU && !(status->flag & RX_FLAG_8023)) + mt7996_mac_decode_he_radiotap(skb, rxv, mode); + + if (!status->wcid || !ieee80211_is_data_qos(fc)) + return 0; + + status->aggr = unicast && + !ieee80211_is_qos_nullfunc(fc); + status->qos_ctl = qos_ctl; + status->seqno = IEEE80211_SEQ_TO_SN(seq_ctrl); + + return 0; +} + +static void +mt7996_mac_write_txwi_8023(struct mt7996_dev *dev, __le32 *txwi, + struct sk_buff *skb, struct mt76_wcid *wcid) +{ + u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK; + u8 fc_type, fc_stype; + u16 ethertype; + bool wmm = false; + u32 val; + + if (wcid->sta) { + struct ieee80211_sta *sta; + + sta = container_of((void *)wcid, struct ieee80211_sta, drv_priv); + wmm = sta->wme; + } + + val = FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_802_3) | + FIELD_PREP(MT_TXD1_TID, tid); + + ethertype = get_unaligned_be16(&skb->data[12]); + if (ethertype >= ETH_P_802_3_MIN) + val |= MT_TXD1_ETH_802_3; + + txwi[1] |= cpu_to_le32(val); + + fc_type = IEEE80211_FTYPE_DATA >> 2; + fc_stype = wmm ? IEEE80211_STYPE_QOS_DATA >> 4 : 0; + + val = FIELD_PREP(MT_TXD2_FRAME_TYPE, fc_type) | + FIELD_PREP(MT_TXD2_SUB_TYPE, fc_stype); + + txwi[2] |= cpu_to_le32(val); +} + +static void +mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, + struct sk_buff *skb, struct ieee80211_key_conf *key) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + bool multicast = is_multicast_ether_addr(hdr->addr1); + u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK; + __le16 fc = hdr->frame_control; + u8 fc_type, fc_stype; + u32 val; + + if (ieee80211_is_action(fc) && + mgmt->u.action.category == WLAN_CATEGORY_BACK && + mgmt->u.action.u.addba_req.action_code == WLAN_ACTION_ADDBA_REQ) + tid = MT_TX_ADDBA; + else if (ieee80211_is_mgmt(hdr->frame_control)) + tid = MT_TX_NORMAL; + + val = FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_802_11) | + FIELD_PREP(MT_TXD1_HDR_INFO, + ieee80211_get_hdrlen_from_skb(skb) / 2) | + FIELD_PREP(MT_TXD1_TID, tid); + + if (!ieee80211_is_data(fc) || multicast || + info->flags & IEEE80211_TX_CTL_USE_MINRATE) + val |= MT_TXD1_FIXED_RATE; + + if (key && multicast && ieee80211_is_robust_mgmt_frame(skb) && + key->cipher == WLAN_CIPHER_SUITE_AES_CMAC) { + val |= MT_TXD1_BIP; + txwi[3] &= ~cpu_to_le32(MT_TXD3_PROTECT_FRAME); + } + + txwi[1] |= cpu_to_le32(val); + + fc_type = (le16_to_cpu(fc) & IEEE80211_FCTL_FTYPE) >> 2; + fc_stype = (le16_to_cpu(fc) & IEEE80211_FCTL_STYPE) >> 4; + + val = FIELD_PREP(MT_TXD2_FRAME_TYPE, fc_type) | + FIELD_PREP(MT_TXD2_SUB_TYPE, fc_stype); + + txwi[2] |= cpu_to_le32(val); + + txwi[3] |= cpu_to_le32(FIELD_PREP(MT_TXD3_BCM, multicast)); + if (ieee80211_is_beacon(fc)) { + txwi[3] &= ~cpu_to_le32(MT_TXD3_SW_POWER_MGMT); + txwi[3] |= cpu_to_le32(MT_TXD3_REM_TX_COUNT); + } + + if (info->flags & IEEE80211_TX_CTL_INJECTED) { + u16 seqno = le16_to_cpu(hdr->seq_ctrl); + + if (ieee80211_is_back_req(hdr->frame_control)) { + struct ieee80211_bar *bar; + + bar = (struct ieee80211_bar *)skb->data; + seqno = le16_to_cpu(bar->start_seq_num); + } + + val = MT_TXD3_SN_VALID | + FIELD_PREP(MT_TXD3_SEQ, IEEE80211_SEQ_TO_SN(seqno)); + txwi[3] |= cpu_to_le32(val); + txwi[3] &= ~cpu_to_le32(MT_TXD3_HW_AMSDU); + } +} + +static u16 +mt7996_mac_tx_rate_val(struct mt76_phy *mphy, struct ieee80211_vif *vif, + bool beacon, bool mcast) +{ + u8 mode = 0, band = mphy->chandef.chan->band; + int rateidx = 0, mcast_rate; + + if (beacon) { + struct cfg80211_bitrate_mask *mask; + + mask = &vif->bss_conf.beacon_tx_rate; + if (hweight16(mask->control[band].he_mcs[0]) == 1) { + rateidx = ffs(mask->control[band].he_mcs[0]) - 1; + mode = MT_PHY_TYPE_HE_SU; + goto out; + } else if (hweight16(mask->control[band].vht_mcs[0]) == 1) { + rateidx = ffs(mask->control[band].vht_mcs[0]) - 1; + mode = MT_PHY_TYPE_VHT; + goto out; + } else if (hweight8(mask->control[band].ht_mcs[0]) == 1) { + rateidx = ffs(mask->control[band].ht_mcs[0]) - 1; + mode = MT_PHY_TYPE_HT; + goto out; + } else if (hweight32(mask->control[band].legacy) == 1) { + rateidx = ffs(mask->control[band].legacy) - 1; + goto legacy; + } + } + + mcast_rate = vif->bss_conf.mcast_rate[band]; + if (mcast && mcast_rate > 0) + rateidx = mcast_rate - 1; + else + rateidx = ffs(vif->bss_conf.basic_rates) - 1; + +legacy: + rateidx = mt76_calculate_default_rate(mphy, rateidx); + mode = rateidx >> 8; + rateidx &= GENMASK(7, 0); + +out: + return FIELD_PREP(MT_TX_RATE_IDX, rateidx) | + FIELD_PREP(MT_TX_RATE_MODE, mode); +} + +void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, + struct sk_buff *skb, struct mt76_wcid *wcid, int pid, + struct ieee80211_key_conf *key, u32 changed) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_vif *vif = info->control.vif; + struct mt76_phy *mphy = &dev->mphy; + u8 band_idx = (info->hw_queue & MT_TX_HW_QUEUE_PHY) >> 2; + u8 p_fmt, q_idx, omac_idx = 0, wmm_idx = 0; + bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP; + u16 tx_count = 15; + u32 val; + bool beacon = !!(changed & (BSS_CHANGED_BEACON | + BSS_CHANGED_BEACON_ENABLED)); + bool inband_disc = !!(changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP | + BSS_CHANGED_FILS_DISCOVERY)); + + if (vif) { + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + + omac_idx = mvif->mt76.omac_idx; + wmm_idx = mvif->mt76.wmm_idx; + band_idx = mvif->mt76.band_idx; + } + + mphy = mt76_dev_phy(&dev->mt76, band_idx); + + if (inband_disc) { + p_fmt = MT_TX_TYPE_FW; + q_idx = MT_LMAC_ALTX0; + } else if (beacon) { + p_fmt = MT_TX_TYPE_FW; + q_idx = MT_LMAC_BCN0; + } else if (skb_get_queue_mapping(skb) >= MT_TXQ_PSD) { + p_fmt = MT_TX_TYPE_CT; + q_idx = MT_LMAC_ALTX0; + } else { + p_fmt = MT_TX_TYPE_CT; + q_idx = wmm_idx * MT7996_MAX_WMM_SETS + + mt76_connac_lmac_mapping(skb_get_queue_mapping(skb)); + } + + val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len + MT_TXD_SIZE) | + FIELD_PREP(MT_TXD0_PKT_FMT, p_fmt) | + FIELD_PREP(MT_TXD0_Q_IDX, q_idx); + txwi[0] = cpu_to_le32(val); + + val = FIELD_PREP(MT_TXD1_WLAN_IDX, wcid->idx) | + FIELD_PREP(MT_TXD1_OWN_MAC, omac_idx); + + if (band_idx) + val |= FIELD_PREP(MT_TXD1_TGID, band_idx); + + txwi[1] = cpu_to_le32(val); + txwi[2] = 0; + + val = MT_TXD3_SW_POWER_MGMT | + FIELD_PREP(MT_TXD3_REM_TX_COUNT, tx_count); + if (key) + val |= MT_TXD3_PROTECT_FRAME; + if (info->flags & IEEE80211_TX_CTL_NO_ACK) + val |= MT_TXD3_NO_ACK; + if (wcid->amsdu) + val |= MT_TXD3_HW_AMSDU; + + txwi[3] = cpu_to_le32(val); + txwi[4] = 0; + + val = FIELD_PREP(MT_TXD5_PID, pid); + if (pid >= MT_PACKET_ID_FIRST) + val |= MT_TXD5_TX_STATUS_HOST; + txwi[5] = cpu_to_le32(val); + + val = MT_TXD6_DIS_MAT | MT_TXD6_DAS | + FIELD_PREP(MT_TXD6_MSDU_CNT, 1); + txwi[6] = cpu_to_le32(val); + txwi[7] = 0; + + if (is_8023) + mt7996_mac_write_txwi_8023(dev, txwi, skb, wcid); + else + mt7996_mac_write_txwi_80211(dev, txwi, skb, key); + + if (txwi[1] & cpu_to_le32(MT_TXD1_FIXED_RATE)) { + /* Fixed rata is available just for 802.11 txd */ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + bool multicast = is_multicast_ether_addr(hdr->addr1); + u16 rate = mt7996_mac_tx_rate_val(mphy, vif, beacon, multicast); + + /* fix to bw 20 */ + val = MT_TXD6_FIXED_BW | + FIELD_PREP(MT_TXD6_BW, 0) | + FIELD_PREP(MT_TXD6_TX_RATE, rate); + + txwi[6] |= cpu_to_le32(val); + txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE); + } +} + +int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx_info->skb->data; + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb); + struct ieee80211_key_conf *key = info->control.hw_key; + struct ieee80211_vif *vif = info->control.vif; + struct mt76_txwi_cache *t; + struct mt7996_txp *txp; + int id, i, pid, nbuf = tx_info->nbuf - 1; + bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP; + u8 *txwi = (u8 *)txwi_ptr; + + if (unlikely(tx_info->skb->len <= ETH_HLEN)) + return -EINVAL; + + if (!wcid) + wcid = &dev->mt76.global_wcid; + + if (sta) { + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + + if (time_after(jiffies, msta->jiffies + HZ / 4)) { + info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; + msta->jiffies = jiffies; + } + } + + t = (struct mt76_txwi_cache *)(txwi + mdev->drv->txwi_size); + t->skb = tx_info->skb; + + id = mt76_token_consume(mdev, &t); + if (id < 0) + return id; + + pid = mt76_tx_status_skb_add(mdev, wcid, tx_info->skb); + memset(txwi_ptr, 0, MT_TXD_SIZE); + /* Transmit non qos data by 802.11 header and need to fill txd by host*/ + if (!is_8023 || pid >= MT_PACKET_ID_FIRST) + mt7996_mac_write_txwi(dev, txwi_ptr, tx_info->skb, wcid, pid, + key, 0); + + txp = (struct mt7996_txp *)(txwi + MT_TXD_SIZE); + for (i = 0; i < nbuf; i++) { + txp->buf[i] = cpu_to_le32(tx_info->buf[i + 1].addr); + txp->len[i] = cpu_to_le16(tx_info->buf[i + 1].len); + } + txp->nbuf = nbuf; + + txp->flags = cpu_to_le16(MT_CT_INFO_FROM_HOST); + + if (!is_8023 || pid >= MT_PACKET_ID_FIRST) + txp->flags |= cpu_to_le16(MT_CT_INFO_APPLY_TXD); + + if (!key) + txp->flags |= cpu_to_le16(MT_CT_INFO_NONE_CIPHER_FRAME); + + if (!is_8023 && ieee80211_is_mgmt(hdr->frame_control)) + txp->flags |= cpu_to_le16(MT_CT_INFO_MGMT_FRAME); + + if (vif) { + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + + txp->bss_idx = mvif->mt76.idx; + } + + txp->token = cpu_to_le16(id); + if (test_bit(MT_WCID_FLAG_4ADDR, &wcid->flags)) + txp->rept_wds_wcid = cpu_to_le16(wcid->idx); + else + txp->rept_wds_wcid = cpu_to_le16(0xfff); + tx_info->skb = DMA_DUMMY_DATA; + + /* pass partial skb header to fw */ + tx_info->buf[1].len = MT_CT_PARSE_LEN; + tx_info->buf[1].skip_unmap = true; + tx_info->nbuf = MT_CT_DMA_BUF_NUM; + + return 0; +} + +static void +mt7996_tx_check_aggr(struct ieee80211_sta *sta, __le32 *txwi) +{ + struct mt7996_sta *msta; + u16 fc, tid; + u32 val; + + if (!sta || !(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he)) + return; + + tid = le32_get_bits(txwi[1], MT_TXD1_TID); + if (tid >= 6) /* skip VO queue */ + return; + + val = le32_to_cpu(txwi[2]); + fc = FIELD_GET(MT_TXD2_FRAME_TYPE, val) << 2 | + FIELD_GET(MT_TXD2_SUB_TYPE, val) << 4; + if (unlikely(fc != (IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA))) + return; + + msta = (struct mt7996_sta *)sta->drv_priv; + if (!test_and_set_bit(tid, &msta->ampdu_state)) + ieee80211_start_tx_ba_session(sta, tid, 0); +} + +static void +mt7996_txp_skb_unmap(struct mt76_dev *dev, struct mt76_txwi_cache *t) +{ + struct mt7996_txp *txp; + int i; + + txp = mt7996_txwi_to_txp(dev, t); + for (i = 0; i < txp->nbuf; i++) + dma_unmap_single(dev->dev, le32_to_cpu(txp->buf[i]), + le16_to_cpu(txp->len[i]), DMA_TO_DEVICE); +} + +static void +mt7996_txwi_free(struct mt7996_dev *dev, struct mt76_txwi_cache *t, + struct ieee80211_sta *sta, struct list_head *free_list) +{ + struct mt76_dev *mdev = &dev->mt76; + struct mt76_wcid *wcid; + __le32 *txwi; + u16 wcid_idx; + + mt7996_txp_skb_unmap(mdev, t); + if (!t->skb) + goto out; + + txwi = (__le32 *)mt76_get_txwi_ptr(mdev, t); + if (sta) { + wcid = (struct mt76_wcid *)sta->drv_priv; + wcid_idx = wcid->idx; + + if (likely(t->skb->protocol != cpu_to_be16(ETH_P_PAE))) + mt7996_tx_check_aggr(sta, txwi); + } else { + wcid_idx = le32_get_bits(txwi[1], MT_TXD1_WLAN_IDX); + } + + __mt76_tx_complete_skb(mdev, wcid_idx, t->skb, free_list); + +out: + t->skb = NULL; + mt76_put_txwi(mdev, t); +} + +static void +mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len) +{ + __le32 *tx_free = (__le32 *)data, *cur_info; + struct mt76_dev *mdev = &dev->mt76; + struct mt76_phy *phy2 = mdev->phys[MT_BAND1]; + struct mt76_phy *phy3 = mdev->phys[MT_BAND2]; + struct mt76_txwi_cache *txwi; + struct ieee80211_sta *sta = NULL; + LIST_HEAD(free_list); + struct sk_buff *skb, *tmp; + void *end = data + len; + bool wake = false; + u16 total, count = 0; + + /* clean DMA queues and unmap buffers first */ + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_PSD], false); + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_BE], false); + if (phy2) { + mt76_queue_tx_cleanup(dev, phy2->q_tx[MT_TXQ_PSD], false); + mt76_queue_tx_cleanup(dev, phy2->q_tx[MT_TXQ_BE], false); + } + if (phy3) { + mt76_queue_tx_cleanup(dev, phy3->q_tx[MT_TXQ_PSD], false); + mt76_queue_tx_cleanup(dev, phy3->q_tx[MT_TXQ_BE], false); + } + + if (WARN_ON_ONCE(le32_get_bits(tx_free[1], MT_TXFREE1_VER) < 4)) + return; + + total = le32_get_bits(tx_free[0], MT_TXFREE0_MSDU_CNT); + for (cur_info = &tx_free[2]; count < total; cur_info++) { + u32 msdu, info; + u8 i; + + if (WARN_ON_ONCE((void *)cur_info >= end)) + return; + /* 1'b1: new wcid pair. + * 1'b0: msdu_id with the same 'wcid pair' as above. + */ + info = le32_to_cpu(*cur_info); + if (info & MT_TXFREE_INFO_PAIR) { + struct mt7996_sta *msta; + struct mt76_wcid *wcid; + u16 idx; + + idx = FIELD_GET(MT_TXFREE_INFO_WLAN_ID, info); + wcid = rcu_dereference(dev->mt76.wcid[idx]); + sta = wcid_to_sta(wcid); + if (!sta) + continue; + + msta = container_of(wcid, struct mt7996_sta, wcid); + spin_lock_bh(&dev->sta_poll_lock); + if (list_empty(&msta->poll_list)) + list_add_tail(&msta->poll_list, &dev->sta_poll_list); + spin_unlock_bh(&dev->sta_poll_lock); + continue; + } + + if (info & MT_TXFREE_INFO_HEADER) + continue; + + for (i = 0; i < 2; i++) { + msdu = (info >> (15 * i)) & MT_TXFREE_INFO_MSDU_ID; + if (msdu == MT_TXFREE_INFO_MSDU_ID) + continue; + + count++; + txwi = mt76_token_release(mdev, msdu, &wake); + if (!txwi) + continue; + + mt7996_txwi_free(dev, txwi, sta, &free_list); + } + } + + mt7996_mac_sta_poll(dev); + + if (wake) + mt76_set_tx_blocked(&dev->mt76, false); + + mt76_worker_schedule(&dev->mt76.tx_worker); + + list_for_each_entry_safe(skb, tmp, &free_list, list) { + skb_list_del_init(skb); + napi_consume_skb(skb, 1); + } +} + +static bool +mt7996_mac_add_txs_skb(struct mt7996_dev *dev, struct mt76_wcid *wcid, int pid, + __le32 *txs_data, struct mt76_sta_stats *stats) +{ + struct ieee80211_supported_band *sband; + struct mt76_dev *mdev = &dev->mt76; + struct mt76_phy *mphy; + struct ieee80211_tx_info *info; + struct sk_buff_head list; + struct rate_info rate = {}; + struct sk_buff *skb; + bool cck = false; + u32 txrate, txs, mode, stbc; + + mt76_tx_status_lock(mdev, &list); + skb = mt76_tx_status_skb_get(mdev, wcid, pid, &list); + if (!skb) + goto out_no_skb; + + txs = le32_to_cpu(txs_data[0]); + + info = IEEE80211_SKB_CB(skb); + if (!(txs & MT_TXS0_ACK_ERROR_MASK)) + info->flags |= IEEE80211_TX_STAT_ACK; + + info->status.ampdu_len = 1; + info->status.ampdu_ack_len = !!(info->flags & + IEEE80211_TX_STAT_ACK); + + info->status.rates[0].idx = -1; + + txrate = FIELD_GET(MT_TXS0_TX_RATE, txs); + + rate.mcs = FIELD_GET(MT_TX_RATE_IDX, txrate); + rate.nss = FIELD_GET(MT_TX_RATE_NSS, txrate) + 1; + stbc = le32_get_bits(txs_data[3], MT_TXS3_RATE_STBC); + + if (stbc && rate.nss > 1) + rate.nss >>= 1; + + if (rate.nss - 1 < ARRAY_SIZE(stats->tx_nss)) + stats->tx_nss[rate.nss - 1]++; + if (rate.mcs < ARRAY_SIZE(stats->tx_mcs)) + stats->tx_mcs[rate.mcs]++; + + mode = FIELD_GET(MT_TX_RATE_MODE, txrate); + switch (mode) { + case MT_PHY_TYPE_CCK: + cck = true; + fallthrough; + case MT_PHY_TYPE_OFDM: + mphy = mt76_dev_phy(mdev, wcid->phy_idx); + + if (mphy->chandef.chan->band == NL80211_BAND_5GHZ) + sband = &mphy->sband_5g.sband; + else if (mphy->chandef.chan->band == NL80211_BAND_6GHZ) + sband = &mphy->sband_6g.sband; + else + sband = &mphy->sband_2g.sband; + + rate.mcs = mt76_get_rate(mphy->dev, sband, rate.mcs, cck); + rate.legacy = sband->bitrates[rate.mcs].bitrate; + break; + case MT_PHY_TYPE_HT: + case MT_PHY_TYPE_HT_GF: + if (rate.mcs > 31) + goto out; + + rate.flags = RATE_INFO_FLAGS_MCS; + if (wcid->rate.flags & RATE_INFO_FLAGS_SHORT_GI) + rate.flags |= RATE_INFO_FLAGS_SHORT_GI; + break; + case MT_PHY_TYPE_VHT: + if (rate.mcs > 9) + goto out; + + rate.flags = RATE_INFO_FLAGS_VHT_MCS; + break; + case MT_PHY_TYPE_HE_SU: + case MT_PHY_TYPE_HE_EXT_SU: + case MT_PHY_TYPE_HE_TB: + case MT_PHY_TYPE_HE_MU: + if (rate.mcs > 11) + goto out; + + rate.he_gi = wcid->rate.he_gi; + rate.he_dcm = FIELD_GET(MT_TX_RATE_DCM, txrate); + rate.flags = RATE_INFO_FLAGS_HE_MCS; + break; + default: + goto out; + } + + stats->tx_mode[mode]++; + + switch (FIELD_GET(MT_TXS0_BW, txs)) { + case IEEE80211_STA_RX_BW_160: + rate.bw = RATE_INFO_BW_160; + stats->tx_bw[3]++; + break; + case IEEE80211_STA_RX_BW_80: + rate.bw = RATE_INFO_BW_80; + stats->tx_bw[2]++; + break; + case IEEE80211_STA_RX_BW_40: + rate.bw = RATE_INFO_BW_40; + stats->tx_bw[1]++; + break; + default: + rate.bw = RATE_INFO_BW_20; + stats->tx_bw[0]++; + break; + } + wcid->rate = rate; + +out: + mt76_tx_status_skb_done(mdev, skb, &list); + +out_no_skb: + mt76_tx_status_unlock(mdev, &list); + + return !!skb; +} + +static void mt7996_mac_add_txs(struct mt7996_dev *dev, void *data) +{ + struct mt7996_sta *msta = NULL; + struct mt76_wcid *wcid; + __le32 *txs_data = data; + u16 wcidx; + u8 pid; + + if (le32_get_bits(txs_data[0], MT_TXS0_TXS_FORMAT) > 1) + return; + + wcidx = le32_get_bits(txs_data[2], MT_TXS2_WCID); + pid = le32_get_bits(txs_data[3], MT_TXS3_PID); + + if (pid < MT_PACKET_ID_FIRST) + return; + + if (wcidx >= MT7996_WTBL_SIZE) + return; + + rcu_read_lock(); + + wcid = rcu_dereference(dev->mt76.wcid[wcidx]); + if (!wcid) + goto out; + + msta = container_of(wcid, struct mt7996_sta, wcid); + + mt7996_mac_add_txs_skb(dev, wcid, pid, txs_data, &msta->stats); + + if (!wcid->sta) + goto out; + + spin_lock_bh(&dev->sta_poll_lock); + if (list_empty(&msta->poll_list)) + list_add_tail(&msta->poll_list, &dev->sta_poll_list); + spin_unlock_bh(&dev->sta_poll_lock); + +out: + rcu_read_unlock(); +} + +bool mt7996_rx_check(struct mt76_dev *mdev, void *data, int len) +{ + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + __le32 *rxd = (__le32 *)data; + __le32 *end = (__le32 *)&rxd[len / 4]; + enum rx_pkt_type type; + + type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE); + if (type != PKT_TYPE_NORMAL) { + u32 sw_type = le32_get_bits(rxd[0], MT_RXD0_SW_PKT_TYPE_MASK); + + if (unlikely((sw_type & MT_RXD0_SW_PKT_TYPE_MAP) == + MT_RXD0_SW_PKT_TYPE_FRAME)) + return true; + } + + switch (type) { + case PKT_TYPE_TXRX_NOTIFY: + mt7996_mac_tx_free(dev, data, len); + return false; + case PKT_TYPE_TXS: + for (rxd += 4; rxd + 8 <= end; rxd += 8) + mt7996_mac_add_txs(dev, rxd); + return false; + case PKT_TYPE_RX_FW_MONITOR: + mt7996_debugfs_rx_fw_monitor(dev, data, len); + return false; + default: + return true; + } +} + +void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, + struct sk_buff *skb, u32 *info) +{ + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + __le32 *rxd = (__le32 *)skb->data; + __le32 *end = (__le32 *)&skb->data[skb->len]; + enum rx_pkt_type type; + + type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE); + if (type != PKT_TYPE_NORMAL) { + u32 sw_type = le32_get_bits(rxd[0], MT_RXD0_SW_PKT_TYPE_MASK); + + if (unlikely((sw_type & MT_RXD0_SW_PKT_TYPE_MAP) == + MT_RXD0_SW_PKT_TYPE_FRAME)) + type = PKT_TYPE_NORMAL; + } + + switch (type) { + case PKT_TYPE_TXRX_NOTIFY: + mt7996_mac_tx_free(dev, skb->data, skb->len); + napi_consume_skb(skb, 1); + break; + case PKT_TYPE_RX_EVENT: + mt7996_mcu_rx_event(dev, skb); + break; + case PKT_TYPE_TXS: + for (rxd += 4; rxd + 8 <= end; rxd += 8) + mt7996_mac_add_txs(dev, rxd); + dev_kfree_skb(skb); + break; + case PKT_TYPE_RX_FW_MONITOR: + mt7996_debugfs_rx_fw_monitor(dev, skb->data, skb->len); + dev_kfree_skb(skb); + break; + case PKT_TYPE_NORMAL: + if (!mt7996_mac_fill_rx(dev, skb)) { + mt76_rx(&dev->mt76, q, skb); + return; + } + fallthrough; + default: + dev_kfree_skb(skb); + break; + } +} + +void mt7996_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e) +{ + if (!e->txwi) { + dev_kfree_skb_any(e->skb); + return; + } + + /* error path */ + if (e->skb == DMA_DUMMY_DATA) { + struct mt76_txwi_cache *t; + struct mt7996_txp *txp; + + txp = mt7996_txwi_to_txp(mdev, e->txwi); + t = mt76_token_put(mdev, le16_to_cpu(txp->token)); + e->skb = t ? t->skb : NULL; + } + + if (e->skb) + mt76_tx_complete_skb(mdev, e->wcid, e->skb); +} + +void mt7996_mac_cca_stats_reset(struct mt7996_phy *phy) +{ + struct mt7996_dev *dev = phy->dev; + u32 reg = MT_WF_PHYRX_BAND_RX_CTRL1(phy->mt76->band_idx); + + mt76_clear(dev, reg, MT_WF_PHYRX_BAND_RX_CTRL1_STSCNT_EN); + mt76_set(dev, reg, BIT(11) | BIT(9)); +} + +void mt7996_mac_reset_counters(struct mt7996_phy *phy) +{ + struct mt7996_dev *dev = phy->dev; + u8 band_idx = phy->mt76->band_idx; + int i; + + for (i = 0; i < 16; i++) + mt76_rr(dev, MT_TX_AGG_CNT(band_idx, i)); + + phy->mt76->survey_time = ktime_get_boottime(); + + memset(phy->mt76->aggr_stats, 0, sizeof(phy->mt76->aggr_stats)); + + /* reset airtime counters */ + mt76_set(dev, MT_WF_RMAC_MIB_AIRTIME0(band_idx), + MT_WF_RMAC_MIB_RXTIME_CLR); + + mt7996_mcu_get_chan_mib_info(phy, true); +} + +void mt7996_mac_set_timing(struct mt7996_phy *phy) +{ + s16 coverage_class = phy->coverage_class; + struct mt7996_dev *dev = phy->dev; + struct mt7996_phy *phy2 = mt7996_phy2(dev); + struct mt7996_phy *phy3 = mt7996_phy3(dev); + u32 val, reg_offset; + u32 cck = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 231) | + FIELD_PREP(MT_TIMEOUT_VAL_CCA, 48); + u32 ofdm = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 60) | + FIELD_PREP(MT_TIMEOUT_VAL_CCA, 28); + u8 band_idx = phy->mt76->band_idx; + int offset; + bool a_band = !(phy->mt76->chandef.chan->band == NL80211_BAND_2GHZ); + + if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state)) + return; + + if (phy2) + coverage_class = max_t(s16, dev->phy.coverage_class, + phy2->coverage_class); + + if (phy3) + coverage_class = max_t(s16, coverage_class, + phy3->coverage_class); + + mt76_set(dev, MT_ARB_SCR(band_idx), + MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE); + udelay(1); + + offset = 3 * coverage_class; + reg_offset = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, offset) | + FIELD_PREP(MT_TIMEOUT_VAL_CCA, offset); + + mt76_wr(dev, MT_TMAC_CDTR(band_idx), cck + reg_offset); + mt76_wr(dev, MT_TMAC_ODTR(band_idx), ofdm + reg_offset); + mt76_wr(dev, MT_TMAC_ICR0(band_idx), + FIELD_PREP(MT_IFS_EIFS_OFDM, a_band ? 84 : 78) | + FIELD_PREP(MT_IFS_RIFS, 2) | + FIELD_PREP(MT_IFS_SIFS, 10) | + FIELD_PREP(MT_IFS_SLOT, phy->slottime)); + + if (!a_band) + mt76_wr(dev, MT_TMAC_ICR1(band_idx), + FIELD_PREP(MT_IFS_EIFS_CCK, 314)); + + if (phy->slottime < 20 || a_band) + val = MT7996_CFEND_RATE_DEFAULT; + else + val = MT7996_CFEND_RATE_11B; + + mt76_rmw_field(dev, MT_AGG_ACR0(band_idx), MT_AGG_ACR_CFEND_RATE, val); + mt76_clear(dev, MT_ARB_SCR(band_idx), + MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE); +} + +void mt7996_mac_enable_nf(struct mt7996_dev *dev, u8 band) +{ + mt76_set(dev, MT_WF_PHYRX_CSD_BAND_RXTD12(band), + MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR_ONLY | + MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR); + + mt76_set(dev, MT_WF_PHYRX_BAND_RX_CTRL1(band), + FIELD_PREP(MT_WF_PHYRX_BAND_RX_CTRL1_IPI_EN, 0x5)); +} + +static u8 +mt7996_phy_get_nf(struct mt7996_phy *phy, u8 band_idx) +{ + static const u8 nf_power[] = { 92, 89, 86, 83, 80, 75, 70, 65, 60, 55, 52 }; + struct mt7996_dev *dev = phy->dev; + u32 val, sum = 0, n = 0; + int ant, i; + + for (ant = 0; ant < hweight8(phy->mt76->antenna_mask); ant++) { + u32 reg = MT_WF_PHYRX_CSD_IRPI(band_idx, ant); + + for (i = 0; i < ARRAY_SIZE(nf_power); i++, reg += 4) { + val = mt76_rr(dev, reg); + sum += val * nf_power[i]; + n += val; + } + } + + return n ? sum / n : 0; +} + +void mt7996_update_channel(struct mt76_phy *mphy) +{ + struct mt7996_phy *phy = (struct mt7996_phy *)mphy->priv; + struct mt76_channel_state *state = mphy->chan_state; + int nf; + + mt7996_mcu_get_chan_mib_info(phy, false); + + nf = mt7996_phy_get_nf(phy, mphy->band_idx); + if (!phy->noise) + phy->noise = nf << 4; + else if (nf) + phy->noise += nf - (phy->noise >> 4); + + state->noise = -(phy->noise >> 4); +} + +static bool +mt7996_wait_reset_state(struct mt7996_dev *dev, u32 state) +{ + bool ret; + + ret = wait_event_timeout(dev->reset_wait, + (READ_ONCE(dev->reset_state) & state), + MT7996_RESET_TIMEOUT); + + WARN(!ret, "Timeout waiting for MCU reset state %x\n", state); + return ret; +} + +static void +mt7996_update_vif_beacon(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct ieee80211_hw *hw = priv; + + switch (vif->type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_AP: + mt7996_mcu_add_beacon(hw, vif, vif->bss_conf.enable_beacon); + break; + default: + break; + } +} + +static void +mt7996_update_beacons(struct mt7996_dev *dev) +{ + struct mt76_phy *phy2, *phy3; + + ieee80211_iterate_active_interfaces(dev->mt76.hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7996_update_vif_beacon, dev->mt76.hw); + + phy2 = dev->mt76.phys[MT_BAND1]; + if (!phy2) + return; + + ieee80211_iterate_active_interfaces(phy2->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7996_update_vif_beacon, phy2->hw); + + phy3 = dev->mt76.phys[MT_BAND2]; + if (!phy3) + return; + + ieee80211_iterate_active_interfaces(phy3->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7996_update_vif_beacon, phy3->hw); +} + +static void +mt7996_dma_reset(struct mt7996_dev *dev) +{ + struct mt76_phy *phy2 = dev->mt76.phys[MT_BAND1]; + struct mt76_phy *phy3 = dev->mt76.phys[MT_BAND2]; + u32 hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); + int i; + + mt76_clear(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN); + + if (dev->hif2) + mt76_clear(dev, MT_WFDMA0_GLO_CFG + hif1_ofs, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN); + + usleep_range(1000, 2000); + + for (i = 0; i < __MT_TXQ_MAX; i++) { + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], true); + if (phy2) + mt76_queue_tx_cleanup(dev, phy2->q_tx[i], true); + if (phy3) + mt76_queue_tx_cleanup(dev, phy3->q_tx[i], true); + } + + for (i = 0; i < __MT_MCUQ_MAX; i++) + mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[i], true); + + mt76_for_each_q_rx(&dev->mt76, i) + mt76_queue_rx_reset(dev, i); + + mt76_tx_status_check(&dev->mt76, true); + + /* re-init prefetch settings after reset */ + mt7996_dma_prefetch(dev); + + mt76_set(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN); + + if (dev->hif2) + mt76_set(dev, MT_WFDMA0_GLO_CFG + hif1_ofs, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN); +} + +void mt7996_tx_token_put(struct mt7996_dev *dev) +{ + struct mt76_txwi_cache *txwi; + int id; + + spin_lock_bh(&dev->mt76.token_lock); + idr_for_each_entry(&dev->mt76.token, txwi, id) { + mt7996_txwi_free(dev, txwi, NULL, NULL); + dev->mt76.token_count--; + } + spin_unlock_bh(&dev->mt76.token_lock); + idr_destroy(&dev->mt76.token); +} + +/* system error recovery */ +void mt7996_mac_reset_work(struct work_struct *work) +{ + struct mt7996_phy *phy2, *phy3; + struct mt7996_dev *dev; + int i; + + dev = container_of(work, struct mt7996_dev, reset_work); + phy2 = mt7996_phy2(dev); + phy3 = mt7996_phy3(dev); + + if (!(READ_ONCE(dev->reset_state) & MT_MCU_CMD_STOP_DMA)) + return; + + ieee80211_stop_queues(mt76_hw(dev)); + if (phy2) + ieee80211_stop_queues(phy2->mt76->hw); + if (phy3) + ieee80211_stop_queues(phy3->mt76->hw); + + set_bit(MT76_RESET, &dev->mphy.state); + set_bit(MT76_MCU_RESET, &dev->mphy.state); + wake_up(&dev->mt76.mcu.wait); + cancel_delayed_work_sync(&dev->mphy.mac_work); + if (phy2) { + set_bit(MT76_RESET, &phy2->mt76->state); + cancel_delayed_work_sync(&phy2->mt76->mac_work); + } + if (phy3) { + set_bit(MT76_RESET, &phy3->mt76->state); + cancel_delayed_work_sync(&phy3->mt76->mac_work); + } + mt76_worker_disable(&dev->mt76.tx_worker); + mt76_for_each_q_rx(&dev->mt76, i) + napi_disable(&dev->mt76.napi[i]); + napi_disable(&dev->mt76.tx_napi); + + mutex_lock(&dev->mt76.mutex); + + mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_STOPPED); + + if (mt7996_wait_reset_state(dev, MT_MCU_CMD_RESET_DONE)) { + mt7996_dma_reset(dev); + + mt7996_tx_token_put(dev); + idr_init(&dev->mt76.token); + + mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_INIT); + mt7996_wait_reset_state(dev, MT_MCU_CMD_RECOVERY_DONE); + } + + clear_bit(MT76_MCU_RESET, &dev->mphy.state); + clear_bit(MT76_RESET, &dev->mphy.state); + if (phy2) + clear_bit(MT76_RESET, &phy2->mt76->state); + if (phy3) + clear_bit(MT76_RESET, &phy3->mt76->state); + + local_bh_disable(); + mt76_for_each_q_rx(&dev->mt76, i) { + napi_enable(&dev->mt76.napi[i]); + napi_schedule(&dev->mt76.napi[i]); + } + local_bh_enable(); + + tasklet_schedule(&dev->irq_tasklet); + + mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_RESET_DONE); + mt7996_wait_reset_state(dev, MT_MCU_CMD_NORMAL_STATE); + + mt76_worker_enable(&dev->mt76.tx_worker); + + local_bh_disable(); + napi_enable(&dev->mt76.tx_napi); + napi_schedule(&dev->mt76.tx_napi); + local_bh_enable(); + + ieee80211_wake_queues(mt76_hw(dev)); + if (phy2) + ieee80211_wake_queues(phy2->mt76->hw); + if (phy3) + ieee80211_wake_queues(phy3->mt76->hw); + + mutex_unlock(&dev->mt76.mutex); + + mt7996_update_beacons(dev); + + ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mphy.mac_work, + MT7996_WATCHDOG_TIME); + if (phy2) + ieee80211_queue_delayed_work(phy2->mt76->hw, + &phy2->mt76->mac_work, + MT7996_WATCHDOG_TIME); + if (phy3) + ieee80211_queue_delayed_work(phy3->mt76->hw, + &phy3->mt76->mac_work, + MT7996_WATCHDOG_TIME); +} + +void mt7996_mac_update_stats(struct mt7996_phy *phy) +{ + struct mt7996_dev *dev = phy->dev; + struct mib_stats *mib = &phy->mib; + u8 band_idx = phy->mt76->band_idx; + u32 cnt; + int i; + + cnt = mt76_rr(dev, MT_MIB_RSCR1(band_idx)); + mib->fcs_err_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_RSCR33(band_idx)); + mib->rx_fifo_full_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_RSCR31(band_idx)); + mib->rx_mpdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_SDR6(band_idx)); + mib->channel_idle_cnt += FIELD_GET(MT_MIB_SDR6_CHANNEL_IDL_CNT_MASK, cnt); + + cnt = mt76_rr(dev, MT_MIB_RVSR0(band_idx)); + mib->rx_vector_mismatch_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_RSCR35(band_idx)); + mib->rx_delimiter_fail_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_RSCR36(band_idx)); + mib->rx_len_mismatch_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_TSCR0(band_idx)); + mib->tx_ampdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_TSCR2(band_idx)); + mib->tx_stop_q_empty_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_TSCR3(band_idx)); + mib->tx_mpdu_attempts_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_TSCR4(band_idx)); + mib->tx_mpdu_success_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_RSCR27(band_idx)); + mib->rx_ampdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_RSCR28(band_idx)); + mib->rx_ampdu_bytes_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_RSCR29(band_idx)); + mib->rx_ampdu_valid_subframe_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_RSCR30(band_idx)); + mib->rx_ampdu_valid_subframe_bytes_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_SDR27(band_idx)); + mib->tx_rwp_fail_cnt += FIELD_GET(MT_MIB_SDR27_TX_RWP_FAIL_CNT, cnt); + + cnt = mt76_rr(dev, MT_MIB_SDR28(band_idx)); + mib->tx_rwp_need_cnt += FIELD_GET(MT_MIB_SDR28_TX_RWP_NEED_CNT, cnt); + + cnt = mt76_rr(dev, MT_UMIB_RPDCR(band_idx)); + mib->rx_pfdrop_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_RVSR1(band_idx)); + mib->rx_vec_queue_overflow_drop_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_TSCR1(band_idx)); + mib->rx_ba_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_BSCR0(band_idx)); + mib->tx_bf_ebf_ppdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_BSCR1(band_idx)); + mib->tx_bf_ibf_ppdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_BSCR2(band_idx)); + mib->tx_mu_bf_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_TSCR5(band_idx)); + mib->tx_mu_mpdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_TSCR6(band_idx)); + mib->tx_mu_acked_mpdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_TSCR7(band_idx)); + mib->tx_su_acked_mpdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_BSCR3(band_idx)); + mib->tx_bf_rx_fb_ht_cnt += cnt; + mib->tx_bf_rx_fb_all_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_BSCR4(band_idx)); + mib->tx_bf_rx_fb_vht_cnt += cnt; + mib->tx_bf_rx_fb_all_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_BSCR5(band_idx)); + mib->tx_bf_rx_fb_he_cnt += cnt; + mib->tx_bf_rx_fb_all_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_BSCR6(band_idx)); + mib->tx_bf_rx_fb_eht_cnt += cnt; + mib->tx_bf_rx_fb_all_cnt += cnt; + + cnt = mt76_rr(dev, MT_ETBF_RX_FB_CONT(band_idx)); + mib->tx_bf_rx_fb_bw = FIELD_GET(MT_ETBF_RX_FB_BW, cnt); + mib->tx_bf_rx_fb_nc_cnt += FIELD_GET(MT_ETBF_RX_FB_NC, cnt); + mib->tx_bf_rx_fb_nr_cnt += FIELD_GET(MT_ETBF_RX_FB_NR, cnt); + + cnt = mt76_rr(dev, MT_MIB_BSCR7(band_idx)); + mib->tx_bf_fb_trig_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_BSCR17(band_idx)); + mib->tx_bf_fb_cpl_cnt += cnt; + + for (i = 0; i < ARRAY_SIZE(mib->tx_amsdu); i++) { + cnt = mt76_rr(dev, MT_PLE_AMSDU_PACK_MSDU_CNT(i)); + mib->tx_amsdu[i] += cnt; + mib->tx_amsdu_cnt += cnt; + } + + /* rts count */ + cnt = mt76_rr(dev, MT_MIB_BTSCR5(band_idx)); + mib->rts_cnt += cnt; + + /* rts retry count */ + cnt = mt76_rr(dev, MT_MIB_BTSCR6(band_idx)); + mib->rts_retries_cnt += cnt; + + /* ba miss count */ + cnt = mt76_rr(dev, MT_MIB_BTSCR0(band_idx)); + mib->ba_miss_cnt += cnt; + + /* ack fail count */ + cnt = mt76_rr(dev, MT_MIB_BFTFCR(band_idx)); + mib->ack_fail_cnt += cnt; + + for (i = 0; i < 16; i++) { + cnt = mt76_rr(dev, MT_TX_AGG_CNT(band_idx, i)); + phy->mt76->aggr_stats[i] += cnt; + } +} + +void mt7996_mac_sta_rc_work(struct work_struct *work) +{ + struct mt7996_dev *dev = container_of(work, struct mt7996_dev, rc_work); + struct ieee80211_sta *sta; + struct ieee80211_vif *vif; + struct mt7996_sta *msta; + u32 changed; + LIST_HEAD(list); + + spin_lock_bh(&dev->sta_poll_lock); + list_splice_init(&dev->sta_rc_list, &list); + + while (!list_empty(&list)) { + msta = list_first_entry(&list, struct mt7996_sta, rc_list); + list_del_init(&msta->rc_list); + changed = msta->changed; + msta->changed = 0; + spin_unlock_bh(&dev->sta_poll_lock); + + sta = container_of((void *)msta, struct ieee80211_sta, drv_priv); + vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv); + + if (changed & (IEEE80211_RC_SUPP_RATES_CHANGED | + IEEE80211_RC_NSS_CHANGED | + IEEE80211_RC_BW_CHANGED)) + mt7996_mcu_add_rate_ctrl(dev, vif, sta, true); + + /* TODO: smps change */ + + spin_lock_bh(&dev->sta_poll_lock); + } + + spin_unlock_bh(&dev->sta_poll_lock); +} + +void mt7996_mac_work(struct work_struct *work) +{ + struct mt7996_phy *phy; + struct mt76_phy *mphy; + + mphy = (struct mt76_phy *)container_of(work, struct mt76_phy, + mac_work.work); + phy = mphy->priv; + + mutex_lock(&mphy->dev->mutex); + + mt76_update_survey(mphy); + if (++mphy->mac_work_count == 5) { + mphy->mac_work_count = 0; + + mt7996_mac_update_stats(phy); + } + + mutex_unlock(&mphy->dev->mutex); + + mt76_tx_status_check(mphy->dev, false); + + ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work, + MT7996_WATCHDOG_TIME); +} + +static void mt7996_dfs_stop_radar_detector(struct mt7996_phy *phy) +{ + struct mt7996_dev *dev = phy->dev; + + if (phy->rdd_state & BIT(0)) + mt7996_mcu_rdd_cmd(dev, RDD_STOP, 0, + MT_RX_SEL0, 0); + if (phy->rdd_state & BIT(1)) + mt7996_mcu_rdd_cmd(dev, RDD_STOP, 1, + MT_RX_SEL0, 0); +} + +static int mt7996_dfs_start_rdd(struct mt7996_dev *dev, int chain) +{ + int err, region; + + switch (dev->mt76.region) { + case NL80211_DFS_ETSI: + region = 0; + break; + case NL80211_DFS_JP: + region = 2; + break; + case NL80211_DFS_FCC: + default: + region = 1; + break; + } + + err = mt7996_mcu_rdd_cmd(dev, RDD_START, chain, + MT_RX_SEL0, region); + if (err < 0) + return err; + + return mt7996_mcu_rdd_cmd(dev, RDD_DET_MODE, chain, + MT_RX_SEL0, 1); +} + +static int mt7996_dfs_start_radar_detector(struct mt7996_phy *phy) +{ + struct cfg80211_chan_def *chandef = &phy->mt76->chandef; + struct mt7996_dev *dev = phy->dev; + u8 band_idx = phy->mt76->band_idx; + int err; + + /* start CAC */ + err = mt7996_mcu_rdd_cmd(dev, RDD_CAC_START, band_idx, + MT_RX_SEL0, 0); + if (err < 0) + return err; + + err = mt7996_dfs_start_rdd(dev, band_idx); + if (err < 0) + return err; + + phy->rdd_state |= BIT(band_idx); + + if (chandef->width == NL80211_CHAN_WIDTH_160 || + chandef->width == NL80211_CHAN_WIDTH_80P80) { + err = mt7996_dfs_start_rdd(dev, 1); + if (err < 0) + return err; + + phy->rdd_state |= BIT(1); + } + + return 0; +} + +static int +mt7996_dfs_init_radar_specs(struct mt7996_phy *phy) +{ + const struct mt7996_dfs_radar_spec *radar_specs; + struct mt7996_dev *dev = phy->dev; + int err, i; + + switch (dev->mt76.region) { + case NL80211_DFS_FCC: + radar_specs = &fcc_radar_specs; + err = mt7996_mcu_set_fcc5_lpn(dev, 8); + if (err < 0) + return err; + break; + case NL80211_DFS_ETSI: + radar_specs = &etsi_radar_specs; + break; + case NL80211_DFS_JP: + radar_specs = &jp_radar_specs; + break; + default: + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(radar_specs->radar_pattern); i++) { + err = mt7996_mcu_set_radar_th(dev, i, + &radar_specs->radar_pattern[i]); + if (err < 0) + return err; + } + + return mt7996_mcu_set_pulse_th(dev, &radar_specs->pulse_th); +} + +int mt7996_dfs_init_radar_detector(struct mt7996_phy *phy) +{ + struct mt7996_dev *dev = phy->dev; + enum mt76_dfs_state dfs_state, prev_state; + int err; + + prev_state = phy->mt76->dfs_state; + dfs_state = mt76_phy_dfs_state(phy->mt76); + + if (prev_state == dfs_state) + return 0; + + if (prev_state == MT_DFS_STATE_UNKNOWN) + mt7996_dfs_stop_radar_detector(phy); + + if (dfs_state == MT_DFS_STATE_DISABLED) + goto stop; + + if (prev_state <= MT_DFS_STATE_DISABLED) { + err = mt7996_dfs_init_radar_specs(phy); + if (err < 0) + return err; + + err = mt7996_dfs_start_radar_detector(phy); + if (err < 0) + return err; + + phy->mt76->dfs_state = MT_DFS_STATE_CAC; + } + + if (dfs_state == MT_DFS_STATE_CAC) + return 0; + + err = mt7996_mcu_rdd_cmd(dev, RDD_CAC_END, + phy->mt76->band_idx, MT_RX_SEL0, 0); + if (err < 0) { + phy->mt76->dfs_state = MT_DFS_STATE_UNKNOWN; + return err; + } + + phy->mt76->dfs_state = MT_DFS_STATE_ACTIVE; + return 0; + +stop: + err = mt7996_mcu_rdd_cmd(dev, RDD_NORMAL_START, + phy->mt76->band_idx, MT_RX_SEL0, 0); + if (err < 0) + return err; + + mt7996_dfs_stop_radar_detector(phy); + phy->mt76->dfs_state = MT_DFS_STATE_DISABLED; + + return 0; +} + +static int +mt7996_mac_twt_duration_align(int duration) +{ + return duration << 8; +} + +static u64 +mt7996_mac_twt_sched_list_add(struct mt7996_dev *dev, + struct mt7996_twt_flow *flow) +{ + struct mt7996_twt_flow *iter, *iter_next; + u32 duration = flow->duration << 8; + u64 start_tsf; + + iter = list_first_entry_or_null(&dev->twt_list, + struct mt7996_twt_flow, list); + if (!iter || !iter->sched || iter->start_tsf > duration) { + /* add flow as first entry in the list */ + list_add(&flow->list, &dev->twt_list); + return 0; + } + + list_for_each_entry_safe(iter, iter_next, &dev->twt_list, list) { + start_tsf = iter->start_tsf + + mt7996_mac_twt_duration_align(iter->duration); + if (list_is_last(&iter->list, &dev->twt_list)) + break; + + if (!iter_next->sched || + iter_next->start_tsf > start_tsf + duration) { + list_add(&flow->list, &iter->list); + goto out; + } + } + + /* add flow as last entry in the list */ + list_add_tail(&flow->list, &dev->twt_list); +out: + return start_tsf; +} + +static int mt7996_mac_check_twt_req(struct ieee80211_twt_setup *twt) +{ + struct ieee80211_twt_params *twt_agrt; + u64 interval, duration; + u16 mantissa; + u8 exp; + + /* only individual agreement supported */ + if (twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) + return -EOPNOTSUPP; + + /* only 256us unit supported */ + if (twt->control & IEEE80211_TWT_CONTROL_WAKE_DUR_UNIT) + return -EOPNOTSUPP; + + twt_agrt = (struct ieee80211_twt_params *)twt->params; + + /* explicit agreement not supported */ + if (!(twt_agrt->req_type & cpu_to_le16(IEEE80211_TWT_REQTYPE_IMPLICIT))) + return -EOPNOTSUPP; + + exp = FIELD_GET(IEEE80211_TWT_REQTYPE_WAKE_INT_EXP, + le16_to_cpu(twt_agrt->req_type)); + mantissa = le16_to_cpu(twt_agrt->mantissa); + duration = twt_agrt->min_twt_dur << 8; + + interval = (u64)mantissa << exp; + if (interval < duration) + return -EOPNOTSUPP; + + return 0; +} + +void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + struct ieee80211_twt_setup *twt) +{ + enum ieee80211_twt_setup_cmd setup_cmd = TWT_SETUP_CMD_REJECT; + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct ieee80211_twt_params *twt_agrt = (void *)twt->params; + u16 req_type = le16_to_cpu(twt_agrt->req_type); + enum ieee80211_twt_setup_cmd sta_setup_cmd; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_twt_flow *flow; + int flowid, table_id; + u8 exp; + + if (mt7996_mac_check_twt_req(twt)) + goto out; + + mutex_lock(&dev->mt76.mutex); + + if (dev->twt.n_agrt == MT7996_MAX_TWT_AGRT) + goto unlock; + + if (hweight8(msta->twt.flowid_mask) == ARRAY_SIZE(msta->twt.flow)) + goto unlock; + + flowid = ffs(~msta->twt.flowid_mask) - 1; + le16p_replace_bits(&twt_agrt->req_type, flowid, + IEEE80211_TWT_REQTYPE_FLOWID); + + table_id = ffs(~dev->twt.table_mask) - 1; + exp = FIELD_GET(IEEE80211_TWT_REQTYPE_WAKE_INT_EXP, req_type); + sta_setup_cmd = FIELD_GET(IEEE80211_TWT_REQTYPE_SETUP_CMD, req_type); + + flow = &msta->twt.flow[flowid]; + memset(flow, 0, sizeof(*flow)); + INIT_LIST_HEAD(&flow->list); + flow->wcid = msta->wcid.idx; + flow->table_id = table_id; + flow->id = flowid; + flow->duration = twt_agrt->min_twt_dur; + flow->mantissa = twt_agrt->mantissa; + flow->exp = exp; + flow->protection = !!(req_type & IEEE80211_TWT_REQTYPE_PROTECTION); + flow->flowtype = !!(req_type & IEEE80211_TWT_REQTYPE_FLOWTYPE); + flow->trigger = !!(req_type & IEEE80211_TWT_REQTYPE_TRIGGER); + + if (sta_setup_cmd == TWT_SETUP_CMD_REQUEST || + sta_setup_cmd == TWT_SETUP_CMD_SUGGEST) { + u64 interval = (u64)le16_to_cpu(twt_agrt->mantissa) << exp; + u64 flow_tsf, curr_tsf; + u32 rem; + + flow->sched = true; + flow->start_tsf = mt7996_mac_twt_sched_list_add(dev, flow); + curr_tsf = __mt7996_get_tsf(hw, msta->vif); + div_u64_rem(curr_tsf - flow->start_tsf, interval, &rem); + flow_tsf = curr_tsf + interval - rem; + twt_agrt->twt = cpu_to_le64(flow_tsf); + } else { + list_add_tail(&flow->list, &dev->twt_list); + } + flow->tsf = le64_to_cpu(twt_agrt->twt); + + if (mt7996_mcu_twt_agrt_update(dev, msta->vif, flow, MCU_TWT_AGRT_ADD)) + goto unlock; + + setup_cmd = TWT_SETUP_CMD_ACCEPT; + dev->twt.table_mask |= BIT(table_id); + msta->twt.flowid_mask |= BIT(flowid); + dev->twt.n_agrt++; + +unlock: + mutex_unlock(&dev->mt76.mutex); +out: + le16p_replace_bits(&twt_agrt->req_type, setup_cmd, + IEEE80211_TWT_REQTYPE_SETUP_CMD); + twt->control = (twt->control & IEEE80211_TWT_CONTROL_WAKE_DUR_UNIT) | + (twt->control & IEEE80211_TWT_CONTROL_RX_DISABLED); +} + +void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev, + struct mt7996_sta *msta, + u8 flowid) +{ + struct mt7996_twt_flow *flow; + + lockdep_assert_held(&dev->mt76.mutex); + + if (flowid >= ARRAY_SIZE(msta->twt.flow)) + return; + + if (!(msta->twt.flowid_mask & BIT(flowid))) + return; + + flow = &msta->twt.flow[flowid]; + if (mt7996_mcu_twt_agrt_update(dev, msta->vif, flow, + MCU_TWT_AGRT_DELETE)) + return; + + list_del_init(&flow->list); + msta->twt.flowid_mask &= ~BIT(flowid); + dev->twt.table_mask &= ~BIT(flow->table_id); + dev->twt.n_agrt--; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.h b/drivers/net/wireless/mediatek/mt76/mt7996/mac.h new file mode 100644 index 000000000000..9f68852012b9 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.h @@ -0,0 +1,398 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#ifndef __MT7996_MAC_H +#define __MT7996_MAC_H + +#define MT_CT_PARSE_LEN 72 +#define MT_CT_DMA_BUF_NUM 2 + +#define MT_RXD0_LENGTH GENMASK(15, 0) +#define MT_RXD0_PKT_TYPE GENMASK(31, 27) + +#define MT_RXD0_NORMAL_ETH_TYPE_OFS GENMASK(22, 16) +#define MT_RXD0_NORMAL_IP_SUM BIT(23) +#define MT_RXD0_NORMAL_UDP_TCP_SUM BIT(24) + +#define MT_RXD0_SW_PKT_TYPE_MASK GENMASK(31, 16) +#define MT_RXD0_SW_PKT_TYPE_MAP 0x380F +#define MT_RXD0_SW_PKT_TYPE_FRAME 0x3801 + +enum rx_pkt_type { + PKT_TYPE_TXS, + PKT_TYPE_TXRXV, + PKT_TYPE_NORMAL, + PKT_TYPE_RX_DUP_RFB, + PKT_TYPE_RX_TMR, + PKT_TYPE_RETRIEVE, + PKT_TYPE_TXRX_NOTIFY, + PKT_TYPE_RX_EVENT, + PKT_TYPE_RX_FW_MONITOR = 0x0c, +}; + +/* RXD DW1 */ +#define MT_RXD1_NORMAL_WLAN_IDX GENMASK(11, 0) +#define MT_RXD1_NORMAL_GROUP_1 BIT(16) +#define MT_RXD1_NORMAL_GROUP_2 BIT(17) +#define MT_RXD1_NORMAL_GROUP_3 BIT(18) +#define MT_RXD1_NORMAL_GROUP_4 BIT(19) +#define MT_RXD1_NORMAL_GROUP_5 BIT(20) +#define MT_RXD1_NORMAL_KEY_ID GENMASK(22, 21) +#define MT_RXD1_NORMAL_CM BIT(23) +#define MT_RXD1_NORMAL_CLM BIT(24) +#define MT_RXD1_NORMAL_ICV_ERR BIT(25) +#define MT_RXD1_NORMAL_TKIP_MIC_ERR BIT(26) +#define MT_RXD1_NORMAL_BAND_IDX GENMASK(28, 27) +#define MT_RXD1_NORMAL_SPP_EN BIT(29) +#define MT_RXD1_NORMAL_ADD_OM BIT(30) +#define MT_RXD1_NORMAL_SEC_DONE BIT(31) + +/* RXD DW2 */ +#define MT_RXD2_NORMAL_BSSID GENMASK(5, 0) +#define MT_RXD2_NORMAL_MAC_HDR_LEN GENMASK(12, 8) +#define MT_RXD2_NORMAL_HDR_TRANS BIT(7) +#define MT_RXD2_NORMAL_HDR_OFFSET GENMASK(15, 13) +#define MT_RXD2_NORMAL_SEC_MODE GENMASK(20, 16) +#define MT_RXD2_NORMAL_MU_BAR BIT(21) +#define MT_RXD2_NORMAL_SW_BIT BIT(22) +#define MT_RXD2_NORMAL_AMSDU_ERR BIT(23) +#define MT_RXD2_NORMAL_MAX_LEN_ERROR BIT(24) +#define MT_RXD2_NORMAL_HDR_TRANS_ERROR BIT(25) +#define MT_RXD2_NORMAL_INT_FRAME BIT(26) +#define MT_RXD2_NORMAL_FRAG BIT(27) +#define MT_RXD2_NORMAL_NULL_FRAME BIT(28) +#define MT_RXD2_NORMAL_NDATA BIT(29) +#define MT_RXD2_NORMAL_NON_AMPDU BIT(30) +#define MT_RXD2_NORMAL_BF_REPORT BIT(31) + +/* RXD DW3 */ +#define MT_RXD3_NORMAL_RXV_SEQ GENMASK(7, 0) +#define MT_RXD3_NORMAL_CH_FREQ GENMASK(15, 8) +#define MT_RXD3_NORMAL_ADDR_TYPE GENMASK(17, 16) +#define MT_RXD3_NORMAL_U2M BIT(0) +#define MT_RXD3_NORMAL_HTC_VLD BIT(18) +#define MT_RXD3_NORMAL_BEACON_MC BIT(20) +#define MT_RXD3_NORMAL_BEACON_UC BIT(21) +#define MT_RXD3_NORMAL_CO_ANT BIT(22) +#define MT_RXD3_NORMAL_FCS_ERR BIT(24) +#define MT_RXD3_NORMAL_VLAN2ETH BIT(31) + +/* RXD DW4 */ +#define MT_RXD4_NORMAL_PAYLOAD_FORMAT GENMASK(1, 0) +#define MT_RXD4_FIRST_AMSDU_FRAME GENMASK(1, 0) +#define MT_RXD4_MID_AMSDU_FRAME BIT(1) +#define MT_RXD4_LAST_AMSDU_FRAME BIT(0) + +#define MT_RXV_HDR_BAND_IDX BIT(24) + +/* RXD GROUP4 */ +#define MT_RXD8_FRAME_CONTROL GENMASK(15, 0) + +#define MT_RXD10_SEQ_CTRL GENMASK(15, 0) +#define MT_RXD10_QOS_CTL GENMASK(31, 16) + +#define MT_RXD11_HT_CONTROL GENMASK(31, 0) + +/* P-RXV */ +#define MT_PRXV_TX_RATE GENMASK(6, 0) +#define MT_PRXV_TX_DCM BIT(4) +#define MT_PRXV_TX_ER_SU_106T BIT(5) +#define MT_PRXV_NSTS GENMASK(10, 7) +#define MT_PRXV_TXBF BIT(11) +#define MT_PRXV_HT_AD_CODE BIT(12) +#define MT_PRXV_HE_RU_ALLOC_L GENMASK(31, 28) +#define MT_PRXV_HE_RU_ALLOC_H GENMASK(3, 0) +#define MT_PRXV_RCPI3 GENMASK(31, 24) +#define MT_PRXV_RCPI2 GENMASK(23, 16) +#define MT_PRXV_RCPI1 GENMASK(15, 8) +#define MT_PRXV_RCPI0 GENMASK(7, 0) +#define MT_PRXV_HT_SHORT_GI GENMASK(4, 3) +#define MT_PRXV_HT_STBC GENMASK(10, 9) +#define MT_PRXV_TX_MODE GENMASK(14, 11) +#define MT_PRXV_FRAME_MODE GENMASK(2, 0) +#define MT_PRXV_DCM BIT(5) +#define MT_PRXV_NUM_RX BIT(8, 6) + +/* C-RXV */ +#define MT_CRXV_HT_STBC GENMASK(1, 0) +#define MT_CRXV_TX_MODE GENMASK(7, 4) +#define MT_CRXV_FRAME_MODE GENMASK(10, 8) +#define MT_CRXV_HT_SHORT_GI GENMASK(14, 13) +#define MT_CRXV_HE_LTF_SIZE GENMASK(18, 17) +#define MT_CRXV_HE_LDPC_EXT_SYM BIT(20) +#define MT_CRXV_HE_PE_DISAMBIG BIT(23) +#define MT_CRXV_HE_NUM_USER GENMASK(30, 24) +#define MT_CRXV_HE_UPLINK BIT(31) +#define MT_CRXV_HE_RU0 GENMASK(7, 0) +#define MT_CRXV_HE_RU1 GENMASK(15, 8) +#define MT_CRXV_HE_RU2 GENMASK(23, 16) +#define MT_CRXV_HE_RU3 GENMASK(31, 24) + +#define MT_CRXV_HE_MU_AID GENMASK(30, 20) + +#define MT_CRXV_HE_SR_MASK GENMASK(11, 8) +#define MT_CRXV_HE_SR1_MASK GENMASK(16, 12) +#define MT_CRXV_HE_SR2_MASK GENMASK(20, 17) +#define MT_CRXV_HE_SR3_MASK GENMASK(24, 21) + +#define MT_CRXV_HE_BSS_COLOR GENMASK(5, 0) +#define MT_CRXV_HE_TXOP_DUR GENMASK(12, 6) +#define MT_CRXV_HE_BEAM_CHNG BIT(13) +#define MT_CRXV_HE_DOPPLER BIT(16) + +enum tx_header_format { + MT_HDR_FORMAT_802_3, + MT_HDR_FORMAT_CMD, + MT_HDR_FORMAT_802_11, + MT_HDR_FORMAT_802_11_EXT, +}; + +enum tx_pkt_type { + MT_TX_TYPE_CT, + MT_TX_TYPE_SF, + MT_TX_TYPE_CMD, + MT_TX_TYPE_FW, +}; + +enum tx_port_idx { + MT_TX_PORT_IDX_LMAC, + MT_TX_PORT_IDX_MCU +}; + +enum tx_mcu_port_q_idx { + MT_TX_MCU_PORT_RX_Q0 = 0x20, + MT_TX_MCU_PORT_RX_Q1, + MT_TX_MCU_PORT_RX_Q2, + MT_TX_MCU_PORT_RX_Q3, + MT_TX_MCU_PORT_RX_FWDL = 0x3e +}; + +enum tx_mgnt_type { + MT_TX_NORMAL, + MT_TX_TIMING, + MT_TX_ADDBA, +}; + +#define MT_CT_INFO_APPLY_TXD BIT(0) +#define MT_CT_INFO_COPY_HOST_TXD_ALL BIT(1) +#define MT_CT_INFO_MGMT_FRAME BIT(2) +#define MT_CT_INFO_NONE_CIPHER_FRAME BIT(3) +#define MT_CT_INFO_HSR2_TX BIT(4) +#define MT_CT_INFO_FROM_HOST BIT(7) + +#define MT_TXD_SIZE (8 * 4) + +#define MT_TXD0_Q_IDX GENMASK(31, 25) +#define MT_TXD0_PKT_FMT GENMASK(24, 23) +#define MT_TXD0_ETH_TYPE_OFFSET GENMASK(22, 16) +#define MT_TXD0_TX_BYTES GENMASK(15, 0) + +#define MT_TXD1_FIXED_RATE BIT(31) +#define MT_TXD1_OWN_MAC GENMASK(30, 25) +#define MT_TXD1_TID GENMASK(24, 21) +#define MT_TXD1_BIP BIT(24) +#define MT_TXD1_ETH_802_3 BIT(20) +#define MT_TXD1_HDR_INFO GENMASK(20, 16) +#define MT_TXD1_HDR_FORMAT GENMASK(15, 14) +#define MT_TXD1_TGID GENMASK(13, 12) +#define MT_TXD1_WLAN_IDX GENMASK(11, 0) + +#define MT_TXD2_POWER_OFFSET GENMASK(31, 26) +#define MT_TXD2_MAX_TX_TIME GENMASK(25, 16) +#define MT_TXD2_FRAG GENMASK(15, 14) +#define MT_TXD2_HTC_VLD BIT(13) +#define MT_TXD2_DURATION BIT(12) +#define MT_TXD2_HDR_PAD GENMASK(11, 10) +#define MT_TXD2_RTS BIT(9) +#define MT_TXD2_OWN_MAC_MAP BIT(8) +#define MT_TXD2_BF_TYPE GENMASK(6, 7) +#define MT_TXD2_FRAME_TYPE GENMASK(5, 4) +#define MT_TXD2_SUB_TYPE GENMASK(3, 0) + +#define MT_TXD3_SN_VALID BIT(31) +#define MT_TXD3_PN_VALID BIT(30) +#define MT_TXD3_SW_POWER_MGMT BIT(29) +#define MT_TXD3_BA_DISABLE BIT(28) +#define MT_TXD3_SEQ GENMASK(27, 16) +#define MT_TXD3_REM_TX_COUNT GENMASK(15, 11) +#define MT_TXD3_TX_COUNT GENMASK(10, 6) +#define MT_TXD3_HW_AMSDU BIT(5) +#define MT_TXD3_BCM BIT(4) +#define MT_TXD3_EEOSP BIT(3) +#define MT_TXD3_EMRD BIT(2) +#define MT_TXD3_PROTECT_FRAME BIT(1) +#define MT_TXD3_NO_ACK BIT(0) + +#define MT_TXD4_PN_LOW GENMASK(31, 0) + +#define MT_TXD5_PN_HIGH GENMASK(31, 16) +#define MT_TXD5_FL BIT(15) +#define MT_TXD5_BYPASS_TBB BIT(14) +#define MT_TXD5_BYPASS_RBB BIT(13) +#define MT_TXD5_BSS_COLOR_ZERO BIT(12) +#define MT_TXD5_TX_STATUS_HOST BIT(10) +#define MT_TXD5_TX_STATUS_MCU BIT(9) +#define MT_TXD5_TX_STATUS_FMT BIT(8) +#define MT_TXD5_PID GENMASK(7, 0) + +#define MT_TXD6_TX_SRC GENMASK(31, 30) +#define MT_TXD6_VTA BIT(28) +#define MT_TXD6_FIXED_BW BIT(25) +#define MT_TXD6_BW GENMASK(24, 22) +#define MT_TXD6_TX_RATE GENMASK(21, 16) +#define MT_TXD6_TIMESTAMP_OFS_EN BIT(15) +#define MT_TXD6_TIMESTAMP_OFS_IDX GENMASK(14, 10) +#define MT_TXD6_MSDU_CNT GENMASK(9, 4) +#define MT_TXD6_SPE_ID_IDX BIT(10) +#define MT_TXD6_ANT_ID GENMASK(7, 4) +#define MT_TXD6_DIS_MAT BIT(3) +#define MT_TXD6_DAS BIT(2) +#define MT_TXD6_AMSDU_CAP BIT(1) + +#define MT_TXD7_TXD_LEN GENMASK(31, 30) +#define MT_TXD7_IP_SUM BIT(29) +#define MT_TXD7_DROP_BY_SDO BIT(28) +#define MT_TXD7_MAC_TXD BIT(27) +#define MT_TXD7_CTXD BIT(26) +#define MT_TXD7_CTXD_CNT GENMASK(25, 22) +#define MT_TXD7_UDP_TCP_SUM BIT(15) +#define MT_TXD7_TX_TIME GENMASK(9, 0) + +#define MT_TX_RATE_STBC BIT(13) +#define MT_TX_RATE_NSS GENMASK(13, 10) +#define MT_TX_RATE_MODE GENMASK(9, 6) +#define MT_TX_RATE_SU_EXT_TONE BIT(5) +#define MT_TX_RATE_DCM BIT(4) +/* VHT/HE only use bits 0-3 */ +#define MT_TX_RATE_IDX GENMASK(5, 0) + +struct mt7996_txp { + __le16 flags; + __le16 token; + u8 bss_idx; + __le16 rept_wds_wcid; + u8 nbuf; +#define MT_TXP_MAX_BUF_NUM 6 + __le32 buf[MT_TXP_MAX_BUF_NUM]; + __le16 len[MT_TXP_MAX_BUF_NUM]; +} __packed __aligned(4); + +#define MT_TXFREE0_PKT_TYPE GENMASK(31, 27) +#define MT_TXFREE0_MSDU_CNT GENMASK(25, 16) +#define MT_TXFREE0_RX_BYTE GENMASK(15, 0) + +#define MT_TXFREE1_VER GENMASK(18, 16) + +#define MT_TXFREE_INFO_PAIR BIT(31) +#define MT_TXFREE_INFO_HEADER BIT(30) +#define MT_TXFREE_INFO_WLAN_ID GENMASK(23, 12) +#define MT_TXFREE_INFO_MSDU_ID GENMASK(14, 0) + +#define MT_TXS0_BW GENMASK(31, 29) +#define MT_TXS0_TID GENMASK(28, 26) +#define MT_TXS0_AMPDU BIT(25) +#define MT_TXS0_TXS_FORMAT GENMASK(24, 23) +#define MT_TXS0_BA_ERROR BIT(22) +#define MT_TXS0_PS_FLAG BIT(21) +#define MT_TXS0_TXOP_TIMEOUT BIT(20) +#define MT_TXS0_BIP_ERROR BIT(19) + +#define MT_TXS0_QUEUE_TIMEOUT BIT(18) +#define MT_TXS0_RTS_TIMEOUT BIT(17) +#define MT_TXS0_ACK_TIMEOUT BIT(16) +#define MT_TXS0_ACK_ERROR_MASK GENMASK(18, 16) + +#define MT_TXS0_TX_STATUS_HOST BIT(15) +#define MT_TXS0_TX_STATUS_MCU BIT(14) +#define MT_TXS0_TX_RATE GENMASK(13, 0) + +#define MT_TXS1_SEQNO GENMASK(31, 20) +#define MT_TXS1_RESP_RATE GENMASK(19, 16) +#define MT_TXS1_RXV_SEQNO GENMASK(15, 8) +#define MT_TXS1_TX_POWER_DBM GENMASK(7, 0) + +#define MT_TXS2_BF_STATUS GENMASK(31, 30) +#define MT_TXS2_BAND GENMASK(29, 28) +#define MT_TXS2_WCID GENMASK(27, 16) +#define MT_TXS2_TX_DELAY GENMASK(15, 0) + +#define MT_TXS3_PID GENMASK(31, 24) +#define MT_TXS3_RATE_STBC BIT(7) +#define MT_TXS3_FIXED_RATE BIT(6) +#define MT_TXS3_SRC GENMASK(5, 4) +#define MT_TXS3_SHARED_ANTENNA BIT(3) +#define MT_TXS3_LAST_TX_RATE GENMASK(2, 0) + +#define MT_TXS4_TIMESTAMP GENMASK(31, 0) + +#define MT_TXS5_F0_FINAL_MPDU BIT(31) +#define MT_TXS5_F0_QOS BIT(30) +#define MT_TXS5_F0_TX_COUNT GENMASK(29, 25) +#define MT_TXS5_F0_FRONT_TIME GENMASK(24, 0) +#define MT_TXS5_F1_MPDU_TX_COUNT GENMASK(31, 24) +#define MT_TXS5_F1_MPDU_TX_BYTES GENMASK(23, 0) + +#define MT_TXS6_F0_NOISE_3 GENMASK(31, 24) +#define MT_TXS6_F0_NOISE_2 GENMASK(23, 16) +#define MT_TXS6_F0_NOISE_1 GENMASK(15, 8) +#define MT_TXS6_F0_NOISE_0 GENMASK(7, 0) +#define MT_TXS6_F1_MPDU_FAIL_COUNT GENMASK(31, 24) +#define MT_TXS6_F1_MPDU_FAIL_BYTES GENMASK(23, 0) + +#define MT_TXS7_F0_RCPI_3 GENMASK(31, 24) +#define MT_TXS7_F0_RCPI_2 GENMASK(23, 16) +#define MT_TXS7_F0_RCPI_1 GENMASK(15, 8) +#define MT_TXS7_F0_RCPI_0 GENMASK(7, 0) +#define MT_TXS7_F1_MPDU_RETRY_COUNT GENMASK(31, 24) +#define MT_TXS7_F1_MPDU_RETRY_BYTES GENMASK(23, 0) + +struct mt7996_dfs_pulse { + u32 max_width; /* us */ + int max_pwr; /* dbm */ + int min_pwr; /* dbm */ + u32 min_stgr_pri; /* us */ + u32 max_stgr_pri; /* us */ + u32 min_cr_pri; /* us */ + u32 max_cr_pri; /* us */ +}; + +struct mt7996_dfs_pattern { + u8 enb; + u8 stgr; + u8 min_crpn; + u8 max_crpn; + u8 min_crpr; + u8 min_pw; + u32 min_pri; + u32 max_pri; + u8 max_pw; + u8 min_crbn; + u8 max_crbn; + u8 min_stgpn; + u8 max_stgpn; + u8 min_stgpr; + u8 rsv[2]; + u32 min_stgpr_diff; +} __packed; + +struct mt7996_dfs_radar_spec { + struct mt7996_dfs_pulse pulse_th; + struct mt7996_dfs_pattern radar_pattern[16]; +}; + +static inline struct mt7996_txp * +mt7996_txwi_to_txp(struct mt76_dev *dev, struct mt76_txwi_cache *t) +{ + u8 *txwi; + + if (!t) + return NULL; + + txwi = mt76_get_txwi_ptr(dev, t); + + return (struct mt7996_txp *)(txwi + MT_TXD_SIZE); +} + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c new file mode 100644 index 000000000000..21dea3fa7dc1 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -0,0 +1,1325 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#include "mt7996.h" +#include "mcu.h" + +static bool mt7996_dev_running(struct mt7996_dev *dev) +{ + struct mt7996_phy *phy; + + if (test_bit(MT76_STATE_RUNNING, &dev->mphy.state)) + return true; + + phy = mt7996_phy2(dev); + if (phy && test_bit(MT76_STATE_RUNNING, &phy->mt76->state)) + return true; + + phy = mt7996_phy3(dev); + + return phy && test_bit(MT76_STATE_RUNNING, &phy->mt76->state); +} + +static int mt7996_start(struct ieee80211_hw *hw) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + bool running; + int ret; + + flush_work(&dev->init_work); + + mutex_lock(&dev->mt76.mutex); + + running = mt7996_dev_running(dev); + if (!running) { + ret = mt7996_mcu_set_hdr_trans(dev, true); + if (ret) + goto out; + } + + mt7996_mac_enable_nf(dev, phy->mt76->band_idx); + + ret = mt7996_mcu_set_rts_thresh(phy, 0x92b); + if (ret) + goto out; + + ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_RX_PATH); + if (ret) + goto out; + + set_bit(MT76_STATE_RUNNING, &phy->mt76->state); + + ieee80211_iterate_interfaces(dev->mt76.hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7996_mcu_set_pm, dev->mt76.hw); + + ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work, + MT7996_WATCHDOG_TIME); + + if (!running) + mt7996_mac_reset_counters(phy); + +out: + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static void mt7996_stop(struct ieee80211_hw *hw) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + + cancel_delayed_work_sync(&phy->mt76->mac_work); + + mutex_lock(&dev->mt76.mutex); + + clear_bit(MT76_STATE_RUNNING, &phy->mt76->state); + + ieee80211_iterate_interfaces(dev->mt76.hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7996_mcu_set_pm, dev->mt76.hw); + + mutex_unlock(&dev->mt76.mutex); +} + +static inline int get_free_idx(u32 mask, u8 start, u8 end) +{ + return ffs(~mask & GENMASK(end, start)); +} + +static int get_omac_idx(enum nl80211_iftype type, u64 mask) +{ + int i; + + switch (type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_STATION: + /* prefer hw bssid slot 1-3 */ + i = get_free_idx(mask, HW_BSSID_1, HW_BSSID_3); + if (i) + return i - 1; + + if (type != NL80211_IFTYPE_STATION) + break; + + i = get_free_idx(mask, EXT_BSSID_1, EXT_BSSID_MAX); + if (i) + return i - 1; + + if (~mask & BIT(HW_BSSID_0)) + return HW_BSSID_0; + + break; + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_AP: + /* ap uses hw bssid 0 and ext bssid */ + if (~mask & BIT(HW_BSSID_0)) + return HW_BSSID_0; + + i = get_free_idx(mask, EXT_BSSID_1, EXT_BSSID_MAX); + if (i) + return i - 1; + + break; + default: + WARN_ON(1); + break; + } + + return -1; +} + +static void mt7996_init_bitrate_mask(struct ieee80211_vif *vif) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + int i; + + for (i = 0; i < ARRAY_SIZE(mvif->bitrate_mask.control); i++) { + mvif->bitrate_mask.control[i].gi = NL80211_TXRATE_DEFAULT_GI; + mvif->bitrate_mask.control[i].he_gi = 0xff; + mvif->bitrate_mask.control[i].he_ltf = 0xff; + mvif->bitrate_mask.control[i].legacy = GENMASK(31, 0); + memset(mvif->bitrate_mask.control[i].ht_mcs, 0xff, + sizeof(mvif->bitrate_mask.control[i].ht_mcs)); + memset(mvif->bitrate_mask.control[i].vht_mcs, 0xff, + sizeof(mvif->bitrate_mask.control[i].vht_mcs)); + memset(mvif->bitrate_mask.control[i].he_mcs, 0xff, + sizeof(mvif->bitrate_mask.control[i].he_mcs)); + } +} + +static int mt7996_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + struct mt76_txq *mtxq; + u8 band_idx = phy->mt76->band_idx; + int idx, ret = 0; + + mutex_lock(&dev->mt76.mutex); + + if (vif->type == NL80211_IFTYPE_MONITOR && + is_zero_ether_addr(vif->addr)) + phy->monitor_vif = vif; + + mvif->mt76.idx = __ffs64(~dev->mt76.vif_mask); + if (mvif->mt76.idx >= (MT7996_MAX_INTERFACES << dev->dbdc_support)) { + ret = -ENOSPC; + goto out; + } + + idx = get_omac_idx(vif->type, phy->omac_mask); + if (idx < 0) { + ret = -ENOSPC; + goto out; + } + mvif->mt76.omac_idx = idx; + mvif->phy = phy; + mvif->mt76.band_idx = band_idx; + mvif->mt76.wmm_idx = band_idx; + + ret = mt7996_mcu_add_dev_info(phy, vif, true); + if (ret) + goto out; + + ret = mt7996_mcu_set_radio_en(phy, true); + if (ret) + goto out; + + dev->mt76.vif_mask |= BIT_ULL(mvif->mt76.idx); + phy->omac_mask |= BIT_ULL(mvif->mt76.omac_idx); + + idx = MT7996_WTBL_RESERVED - mvif->mt76.idx; + + INIT_LIST_HEAD(&mvif->sta.rc_list); + INIT_LIST_HEAD(&mvif->sta.poll_list); + mvif->sta.wcid.idx = idx; + mvif->sta.wcid.phy_idx = band_idx; + mvif->sta.wcid.hw_key_idx = -1; + mvif->sta.wcid.tx_info |= MT_WCID_TX_INFO_SET; + mt76_packet_id_init(&mvif->sta.wcid); + + mt7996_mac_wtbl_update(dev, idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + + if (vif->txq) { + mtxq = (struct mt76_txq *)vif->txq->drv_priv; + mtxq->wcid = idx; + } + + if (vif->type != NL80211_IFTYPE_AP && + (!mvif->mt76.omac_idx || mvif->mt76.omac_idx > 3)) + vif->offload_flags = 0; + vif->offload_flags |= IEEE80211_OFFLOAD_ENCAP_4ADDR; + + mt7996_init_bitrate_mask(vif); + memset(&mvif->cap, -1, sizeof(mvif->cap)); + + mt7996_mcu_add_bss_info(phy, vif, true); + mt7996_mcu_add_sta(dev, vif, NULL, true); + rcu_assign_pointer(dev->mt76.wcid[idx], &mvif->sta.wcid); + +out: + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static void mt7996_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_sta *msta = &mvif->sta; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + int idx = msta->wcid.idx; + + mt7996_mcu_add_bss_info(phy, vif, false); + mt7996_mcu_add_sta(dev, vif, NULL, false); + + if (vif == phy->monitor_vif) + phy->monitor_vif = NULL; + + mt7996_mcu_add_dev_info(phy, vif, false); + mt7996_mcu_set_radio_en(phy, false); + + rcu_assign_pointer(dev->mt76.wcid[idx], NULL); + + mutex_lock(&dev->mt76.mutex); + dev->mt76.vif_mask &= ~BIT_ULL(mvif->mt76.idx); + phy->omac_mask &= ~BIT_ULL(mvif->mt76.omac_idx); + mutex_unlock(&dev->mt76.mutex); + + spin_lock_bh(&dev->sta_poll_lock); + if (!list_empty(&msta->poll_list)) + list_del_init(&msta->poll_list); + spin_unlock_bh(&dev->sta_poll_lock); + + mt76_packet_id_flush(&dev->mt76, &msta->wcid); +} + +int mt7996_set_channel(struct mt7996_phy *phy) +{ + struct mt7996_dev *dev = phy->dev; + int ret; + + cancel_delayed_work_sync(&phy->mt76->mac_work); + + mutex_lock(&dev->mt76.mutex); + set_bit(MT76_RESET, &phy->mt76->state); + + mt76_set_channel(phy->mt76); + + ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH); + if (ret) + goto out; + + mt7996_mac_set_timing(phy); + ret = mt7996_dfs_init_radar_detector(phy); + mt7996_mac_cca_stats_reset(phy); + + mt7996_mac_reset_counters(phy); + phy->noise = 0; + +out: + clear_bit(MT76_RESET, &phy->mt76->state); + mutex_unlock(&dev->mt76.mutex); + + mt76_txq_schedule_all(phy->mt76); + + ieee80211_queue_delayed_work(phy->mt76->hw, + &phy->mt76->mac_work, + MT7996_WATCHDOG_TIME); + + return ret; +} + +static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_sta *msta = sta ? (struct mt7996_sta *)sta->drv_priv : + &mvif->sta; + struct mt76_wcid *wcid = &msta->wcid; + u8 *wcid_keyidx = &wcid->hw_key_idx; + int idx = key->keyidx; + int err = 0; + + /* The hardware does not support per-STA RX GTK, fallback + * to software mode for these. + */ + if ((vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_MESH_POINT) && + (key->cipher == WLAN_CIPHER_SUITE_TKIP || + key->cipher == WLAN_CIPHER_SUITE_CCMP) && + !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + return -EOPNOTSUPP; + + /* fall back to sw encryption for unsupported ciphers */ + switch (key->cipher) { + case WLAN_CIPHER_SUITE_AES_CMAC: + wcid_keyidx = &wcid->hw_key_idx2; + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE; + break; + case WLAN_CIPHER_SUITE_TKIP: + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_CCMP_256: + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: + case WLAN_CIPHER_SUITE_SMS4: + break; + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + default: + return -EOPNOTSUPP; + } + + mutex_lock(&dev->mt76.mutex); + + if (cmd == SET_KEY && !sta && !mvif->mt76.cipher) { + mvif->mt76.cipher = mt76_connac_mcu_get_cipher(key->cipher); + mt7996_mcu_add_bss_info(phy, vif, true); + } + + if (cmd == SET_KEY) + *wcid_keyidx = idx; + else if (idx == *wcid_keyidx) + *wcid_keyidx = -1; + else + goto out; + + mt76_wcid_key_setup(&dev->mt76, wcid, + cmd == SET_KEY ? key : NULL); + + err = mt7996_mcu_add_key(&dev->mt76, vif, &msta->bip, + key, MCU_WMWA_UNI_CMD(STA_REC_UPDATE), + &msta->wcid, cmd); +out: + mutex_unlock(&dev->mt76.mutex); + + return err; +} + +static int mt7996_config(struct ieee80211_hw *hw, u32 changed) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + int ret; + + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + ieee80211_stop_queues(hw); + ret = mt7996_set_channel(phy); + if (ret) + return ret; + ieee80211_wake_queues(hw); + } + + mutex_lock(&dev->mt76.mutex); + + if (changed & IEEE80211_CONF_CHANGE_MONITOR) { + bool enabled = !!(hw->conf.flags & IEEE80211_CONF_MONITOR); + + if (!enabled) + phy->rxfilter |= MT_WF_RFCR_DROP_OTHER_UC; + else + phy->rxfilter &= ~MT_WF_RFCR_DROP_OTHER_UC; + + mt76_rmw_field(dev, MT_DMA_DCR0(phy->mt76->band_idx), + MT_DMA_DCR0_RXD_G5_EN, enabled); + mt76_wr(dev, MT_WF_RFCR(phy->mt76->band_idx), phy->rxfilter); + } + + mutex_unlock(&dev->mt76.mutex); + + return 0; +} + +static int +mt7996_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + unsigned int link_id, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + + /* no need to update right away, we'll get BSS_CHANGED_QOS */ + queue = mt76_connac_lmac_mapping(queue); + mvif->queue_params[queue] = *params; + + return 0; +} + +static void mt7996_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + u32 ctl_flags = MT_WF_RFCR1_DROP_ACK | + MT_WF_RFCR1_DROP_BF_POLL | + MT_WF_RFCR1_DROP_BA | + MT_WF_RFCR1_DROP_CFEND | + MT_WF_RFCR1_DROP_CFACK; + u32 flags = 0; + +#define MT76_FILTER(_flag, _hw) do { \ + flags |= *total_flags & FIF_##_flag; \ + phy->rxfilter &= ~(_hw); \ + phy->rxfilter |= !(flags & FIF_##_flag) * (_hw); \ + } while (0) + + mutex_lock(&dev->mt76.mutex); + + phy->rxfilter &= ~(MT_WF_RFCR_DROP_OTHER_BSS | + MT_WF_RFCR_DROP_OTHER_BEACON | + MT_WF_RFCR_DROP_FRAME_REPORT | + MT_WF_RFCR_DROP_PROBEREQ | + MT_WF_RFCR_DROP_MCAST_FILTERED | + MT_WF_RFCR_DROP_MCAST | + MT_WF_RFCR_DROP_BCAST | + MT_WF_RFCR_DROP_DUPLICATE | + MT_WF_RFCR_DROP_A2_BSSID | + MT_WF_RFCR_DROP_UNWANTED_CTL | + MT_WF_RFCR_DROP_STBC_MULTI); + + MT76_FILTER(OTHER_BSS, MT_WF_RFCR_DROP_OTHER_TIM | + MT_WF_RFCR_DROP_A3_MAC | + MT_WF_RFCR_DROP_A3_BSSID); + + MT76_FILTER(FCSFAIL, MT_WF_RFCR_DROP_FCSFAIL); + + MT76_FILTER(CONTROL, MT_WF_RFCR_DROP_CTS | + MT_WF_RFCR_DROP_RTS | + MT_WF_RFCR_DROP_CTL_RSV | + MT_WF_RFCR_DROP_NDPA); + + *total_flags = flags; + mt76_wr(dev, MT_WF_RFCR(phy->mt76->band_idx), phy->rxfilter); + + if (*total_flags & FIF_CONTROL) + mt76_clear(dev, MT_WF_RFCR1(phy->mt76->band_idx), ctl_flags); + else + mt76_set(dev, MT_WF_RFCR1(phy->mt76->band_idx), ctl_flags); + + mutex_unlock(&dev->mt76.mutex); +} + +static void +mt7996_update_bss_color(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_he_bss_color *bss_color) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + + switch (vif->type) { + case NL80211_IFTYPE_AP: { + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + + if (mvif->mt76.omac_idx > HW_BSSID_MAX) + return; + fallthrough; + } + case NL80211_IFTYPE_STATION: + mt7996_mcu_update_bss_color(dev, vif, bss_color); + break; + default: + break; + } +} + +static void mt7996_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u64 changed) +{ + struct mt7996_phy *phy = mt7996_hw_phy(hw); + struct mt7996_dev *dev = mt7996_hw_dev(hw); + + mutex_lock(&dev->mt76.mutex); + + /* station mode uses BSSID to map the wlan entry to a peer, + * and then peer references bss_info_rfch to set bandwidth cap. + */ + if (changed & BSS_CHANGED_BSSID && + vif->type == NL80211_IFTYPE_STATION) { + bool join = !is_zero_ether_addr(info->bssid); + + mt7996_mcu_add_bss_info(phy, vif, join); + mt7996_mcu_add_sta(dev, vif, NULL, join); + } + + if (changed & BSS_CHANGED_ASSOC) { + mt7996_mcu_add_bss_info(phy, vif, vif->cfg.assoc); + mt7996_mcu_add_obss_spr(dev, vif, info->he_obss_pd.enable); + } + + if (changed & BSS_CHANGED_ERP_SLOT) { + int slottime = info->use_short_slot ? 9 : 20; + + if (slottime != phy->slottime) { + phy->slottime = slottime; + mt7996_mac_set_timing(phy); + } + } + + if (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon) { + mt7996_mcu_add_bss_info(phy, vif, true); + mt7996_mcu_add_sta(dev, vif, NULL, true); + } + + /* ensure that enable txcmd_mode after bss_info */ + if (changed & (BSS_CHANGED_QOS | BSS_CHANGED_BEACON_ENABLED)) + mt7996_mcu_set_tx(dev, vif); + + if (changed & BSS_CHANGED_HE_OBSS_PD) + mt7996_mcu_add_obss_spr(dev, vif, info->he_obss_pd.enable); + + if (changed & BSS_CHANGED_HE_BSS_COLOR) + mt7996_update_bss_color(hw, vif, &info->he_bss_color); + + if (changed & (BSS_CHANGED_BEACON | + BSS_CHANGED_BEACON_ENABLED)) + mt7996_mcu_add_beacon(hw, vif, info->enable_beacon); + + if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP || + changed & BSS_CHANGED_FILS_DISCOVERY) + mt7996_mcu_beacon_inband_discov(dev, vif, changed); + + mutex_unlock(&dev->mt76.mutex); +} + +static void +mt7996_channel_switch_beacon(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_chan_def *chandef) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + + mutex_lock(&dev->mt76.mutex); + mt7996_mcu_add_beacon(hw, vif, true); + mutex_unlock(&dev->mt76.mutex); +} + +int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + u8 band_idx = mvif->phy->mt76->band_idx; + int ret, idx; + + idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA); + if (idx < 0) + return -ENOSPC; + + INIT_LIST_HEAD(&msta->rc_list); + INIT_LIST_HEAD(&msta->poll_list); + msta->vif = mvif; + msta->wcid.sta = 1; + msta->wcid.idx = idx; + msta->wcid.phy_idx = band_idx; + msta->wcid.tx_info |= MT_WCID_TX_INFO_SET; + msta->jiffies = jiffies; + + mt7996_mac_wtbl_update(dev, idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + + ret = mt7996_mcu_add_sta(dev, vif, sta, true); + if (ret) + return ret; + + return mt7996_mcu_add_rate_ctrl(dev, vif, sta, false); +} + +void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + int i; + + mt7996_mcu_add_sta(dev, vif, sta, false); + + mt7996_mac_wtbl_update(dev, msta->wcid.idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + + for (i = 0; i < ARRAY_SIZE(msta->twt.flow); i++) + mt7996_mac_twt_teardown_flow(dev, msta, i); + + spin_lock_bh(&dev->sta_poll_lock); + if (!list_empty(&msta->poll_list)) + list_del_init(&msta->poll_list); + if (!list_empty(&msta->rc_list)) + list_del_init(&msta->rc_list); + spin_unlock_bh(&dev->sta_poll_lock); +} + +static void mt7996_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt76_phy *mphy = hw->priv; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_vif *vif = info->control.vif; + struct mt76_wcid *wcid = &dev->mt76.global_wcid; + + if (control->sta) { + struct mt7996_sta *sta; + + sta = (struct mt7996_sta *)control->sta->drv_priv; + wcid = &sta->wcid; + } + + if (vif && !control->sta) { + struct mt7996_vif *mvif; + + mvif = (struct mt7996_vif *)vif->drv_priv; + wcid = &mvif->sta.wcid; + } + + mt76_tx(mphy, control->sta, wcid, skb); +} + +static int mt7996_set_rts_threshold(struct ieee80211_hw *hw, u32 val) +{ + struct mt7996_phy *phy = mt7996_hw_phy(hw); + int ret; + + mutex_lock(&phy->dev->mt76.mutex); + ret = mt7996_mcu_set_rts_thresh(phy, val); + mutex_unlock(&phy->dev->mt76.mutex); + + return ret; +} + +static int +mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params) +{ + enum ieee80211_ampdu_mlme_action action = params->action; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct ieee80211_sta *sta = params->sta; + struct ieee80211_txq *txq = sta->txq[params->tid]; + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + u16 tid = params->tid; + u16 ssn = params->ssn; + struct mt76_txq *mtxq; + int ret = 0; + + if (!txq) + return -EINVAL; + + mtxq = (struct mt76_txq *)txq->drv_priv; + + mutex_lock(&dev->mt76.mutex); + switch (action) { + case IEEE80211_AMPDU_RX_START: + mt76_rx_aggr_start(&dev->mt76, &msta->wcid, tid, ssn, + params->buf_size); + ret = mt7996_mcu_add_rx_ba(dev, params, true); + break; + case IEEE80211_AMPDU_RX_STOP: + mt76_rx_aggr_stop(&dev->mt76, &msta->wcid, tid); + ret = mt7996_mcu_add_rx_ba(dev, params, false); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + mtxq->aggr = true; + mtxq->send_bar = false; + ret = mt7996_mcu_add_tx_ba(dev, params, true); + break; + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + mtxq->aggr = false; + clear_bit(tid, &msta->ampdu_state); + ret = mt7996_mcu_add_tx_ba(dev, params, false); + break; + case IEEE80211_AMPDU_TX_START: + set_bit(tid, &msta->ampdu_state); + ret = IEEE80211_AMPDU_TX_START_IMMEDIATE; + break; + case IEEE80211_AMPDU_TX_STOP_CONT: + mtxq->aggr = false; + clear_bit(tid, &msta->ampdu_state); + ret = mt7996_mcu_add_tx_ba(dev, params, false); + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + } + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static int +mt7996_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + return mt76_sta_state(hw, vif, sta, IEEE80211_STA_NOTEXIST, + IEEE80211_STA_NONE); +} + +static int +mt7996_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + return mt76_sta_state(hw, vif, sta, IEEE80211_STA_NONE, + IEEE80211_STA_NOTEXIST); +} + +static int +mt7996_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) +{ + struct mt7996_phy *phy = mt7996_hw_phy(hw); + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mib_stats *mib = &phy->mib; + + mutex_lock(&dev->mt76.mutex); + + stats->dot11RTSSuccessCount = mib->rts_cnt; + stats->dot11RTSFailureCount = mib->rts_retries_cnt; + stats->dot11FCSErrorCount = mib->fcs_err_cnt; + stats->dot11ACKFailureCount = mib->ack_fail_cnt; + + mutex_unlock(&dev->mt76.mutex); + + return 0; +} + +u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + union { + u64 t64; + u32 t32[2]; + } tsf; + u16 n; + + lockdep_assert_held(&dev->mt76.mutex); + + n = mvif->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 + : mvif->mt76.omac_idx; + /* TSF software read */ + mt76_rmw(dev, MT_LPON_TCR(phy->mt76->band_idx, n), MT_LPON_TCR_SW_MODE, + MT_LPON_TCR_SW_READ); + tsf.t32[0] = mt76_rr(dev, MT_LPON_UTTR0(phy->mt76->band_idx)); + tsf.t32[1] = mt76_rr(dev, MT_LPON_UTTR1(phy->mt76->band_idx)); + + return tsf.t64; +} + +static u64 +mt7996_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + u64 ret; + + mutex_lock(&dev->mt76.mutex); + ret = __mt7996_get_tsf(hw, mvif); + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static void +mt7996_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u64 timestamp) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + union { + u64 t64; + u32 t32[2]; + } tsf = { .t64 = timestamp, }; + u16 n; + + mutex_lock(&dev->mt76.mutex); + + n = mvif->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 + : mvif->mt76.omac_idx; + mt76_wr(dev, MT_LPON_UTTR0(phy->mt76->band_idx), tsf.t32[0]); + mt76_wr(dev, MT_LPON_UTTR1(phy->mt76->band_idx), tsf.t32[1]); + /* TSF software overwrite */ + mt76_rmw(dev, MT_LPON_TCR(phy->mt76->band_idx, n), MT_LPON_TCR_SW_MODE, + MT_LPON_TCR_SW_WRITE); + + mutex_unlock(&dev->mt76.mutex); +} + +static void +mt7996_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + s64 timestamp) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + union { + u64 t64; + u32 t32[2]; + } tsf = { .t64 = timestamp, }; + u16 n; + + mutex_lock(&dev->mt76.mutex); + + n = mvif->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 + : mvif->mt76.omac_idx; + mt76_wr(dev, MT_LPON_UTTR0(phy->mt76->band_idx), tsf.t32[0]); + mt76_wr(dev, MT_LPON_UTTR1(phy->mt76->band_idx), tsf.t32[1]); + /* TSF software adjust*/ + mt76_rmw(dev, MT_LPON_TCR(phy->mt76->band_idx, n), MT_LPON_TCR_SW_MODE, + MT_LPON_TCR_SW_ADJUST); + + mutex_unlock(&dev->mt76.mutex); +} + +static void +mt7996_set_coverage_class(struct ieee80211_hw *hw, s16 coverage_class) +{ + struct mt7996_phy *phy = mt7996_hw_phy(hw); + struct mt7996_dev *dev = phy->dev; + + mutex_lock(&dev->mt76.mutex); + phy->coverage_class = max_t(s16, coverage_class, 0); + mt7996_mac_set_timing(phy); + mutex_unlock(&dev->mt76.mutex); +} + +static int +mt7996_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + int max_nss = hweight8(hw->wiphy->available_antennas_tx); + u8 band_idx = phy->mt76->band_idx, shift = dev->chainshift[band_idx]; + + if (!tx_ant || tx_ant != rx_ant || ffs(tx_ant) > max_nss) + return -EINVAL; + + if ((BIT(hweight8(tx_ant)) - 1) != tx_ant) + tx_ant = BIT(ffs(tx_ant) - 1) - 1; + + mutex_lock(&dev->mt76.mutex); + + phy->mt76->antenna_mask = tx_ant; + + /* restore to the origin chainmask which might have auxiliary path */ + if (hweight8(tx_ant) == max_nss) + phy->mt76->chainmask = (dev->chainmask >> shift) << shift; + else + phy->mt76->chainmask = tx_ant << shift; + + mt76_set_stream_caps(phy->mt76, true); + mt7996_set_stream_vht_txbf_caps(phy); + mt7996_set_stream_he_caps(phy); + + mutex_unlock(&dev->mt76.mutex); + + return 0; +} + +static void mt7996_sta_statistics(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct station_info *sinfo) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct rate_info *txrate = &msta->wcid.rate; + + if (!txrate->legacy && !txrate->flags) + return; + + if (txrate->legacy) { + sinfo->txrate.legacy = txrate->legacy; + } else { + sinfo->txrate.mcs = txrate->mcs; + sinfo->txrate.nss = txrate->nss; + sinfo->txrate.bw = txrate->bw; + sinfo->txrate.he_gi = txrate->he_gi; + sinfo->txrate.he_dcm = txrate->he_dcm; + sinfo->txrate.he_ru_alloc = txrate->he_ru_alloc; + } + sinfo->txrate.flags = txrate->flags; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); +} + +static void mt7996_sta_rc_work(void *data, struct ieee80211_sta *sta) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_dev *dev = msta->vif->phy->dev; + u32 *changed = data; + + spin_lock_bh(&dev->sta_poll_lock); + msta->changed |= *changed; + if (list_empty(&msta->rc_list)) + list_add_tail(&msta->rc_list, &dev->sta_rc_list); + spin_unlock_bh(&dev->sta_poll_lock); +} + +static void mt7996_sta_rc_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + u32 changed) +{ + struct mt7996_phy *phy = mt7996_hw_phy(hw); + struct mt7996_dev *dev = phy->dev; + + mt7996_sta_rc_work(&changed, sta); + ieee80211_queue_work(hw, &dev->rc_work); +} + +static int +mt7996_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + const struct cfg80211_bitrate_mask *mask) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_phy *phy = mt7996_hw_phy(hw); + struct mt7996_dev *dev = phy->dev; + u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED; + + mvif->bitrate_mask = *mask; + + /* if multiple rates across different preambles are given we can + * reconfigure this info with all peers using sta_rec command with + * the below exception cases. + * - single rate : if a rate is passed along with different preambles, + * we select the highest one as fixed rate. i.e VHT MCS for VHT peers. + * - multiple rates: if it's not in range format i.e 0-{7,8,9} for VHT + * then multiple MCS setting (MCS 4,5,6) is not supported. + */ + ieee80211_iterate_stations_atomic(hw, mt7996_sta_rc_work, &changed); + ieee80211_queue_work(hw, &dev->rc_work); + + return 0; +} + +static void mt7996_sta_set_4addr(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + bool enabled) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + + if (enabled) + set_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags); + else + clear_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags); + + mt7996_mcu_wtbl_update_hdr_trans(dev, vif, sta); +} + +static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + bool enabled) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + + if (enabled) + set_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags); + else + clear_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags); + + mt7996_mcu_wtbl_update_hdr_trans(dev, vif, sta); +} + +static const char mt7996_gstrings_stats[][ETH_GSTRING_LEN] = { + "tx_ampdu_cnt", + "tx_stop_q_empty_cnt", + "tx_mpdu_attempts", + "tx_mpdu_success", + "tx_rwp_fail_cnt", + "tx_rwp_need_cnt", + "tx_pkt_ebf_cnt", + "tx_pkt_ibf_cnt", + "tx_ampdu_len:0-1", + "tx_ampdu_len:2-10", + "tx_ampdu_len:11-19", + "tx_ampdu_len:20-28", + "tx_ampdu_len:29-37", + "tx_ampdu_len:38-46", + "tx_ampdu_len:47-55", + "tx_ampdu_len:56-79", + "tx_ampdu_len:80-103", + "tx_ampdu_len:104-127", + "tx_ampdu_len:128-151", + "tx_ampdu_len:152-175", + "tx_ampdu_len:176-199", + "tx_ampdu_len:200-223", + "tx_ampdu_len:224-247", + "ba_miss_count", + "tx_beamformer_ppdu_iBF", + "tx_beamformer_ppdu_eBF", + "tx_beamformer_rx_feedback_all", + "tx_beamformer_rx_feedback_he", + "tx_beamformer_rx_feedback_vht", + "tx_beamformer_rx_feedback_ht", + "tx_beamformer_rx_feedback_bw", /* zero based idx: 20, 40, 80, 160 */ + "tx_beamformer_rx_feedback_nc", + "tx_beamformer_rx_feedback_nr", + "tx_beamformee_ok_feedback_pkts", + "tx_beamformee_feedback_trig", + "tx_mu_beamforming", + "tx_mu_mpdu", + "tx_mu_successful_mpdu", + "tx_su_successful_mpdu", + "tx_msdu_pack_1", + "tx_msdu_pack_2", + "tx_msdu_pack_3", + "tx_msdu_pack_4", + "tx_msdu_pack_5", + "tx_msdu_pack_6", + "tx_msdu_pack_7", + "tx_msdu_pack_8", + + /* rx counters */ + "rx_fifo_full_cnt", + "rx_mpdu_cnt", + "channel_idle_cnt", + "rx_vector_mismatch_cnt", + "rx_delimiter_fail_cnt", + "rx_len_mismatch_cnt", + "rx_ampdu_cnt", + "rx_ampdu_bytes_cnt", + "rx_ampdu_valid_subframe_cnt", + "rx_ampdu_valid_subframe_b_cnt", + "rx_pfdrop_cnt", + "rx_vec_queue_overflow_drop_cnt", + "rx_ba_cnt", + + /* per vif counters */ + "v_tx_mode_cck", + "v_tx_mode_ofdm", + "v_tx_mode_ht", + "v_tx_mode_ht_gf", + "v_tx_mode_vht", + "v_tx_mode_he_su", + "v_tx_mode_he_ext_su", + "v_tx_mode_he_tb", + "v_tx_mode_he_mu", + "v_tx_bw_20", + "v_tx_bw_40", + "v_tx_bw_80", + "v_tx_bw_160", + "v_tx_mcs_0", + "v_tx_mcs_1", + "v_tx_mcs_2", + "v_tx_mcs_3", + "v_tx_mcs_4", + "v_tx_mcs_5", + "v_tx_mcs_6", + "v_tx_mcs_7", + "v_tx_mcs_8", + "v_tx_mcs_9", + "v_tx_mcs_10", + "v_tx_mcs_11", +}; + +#define MT7996_SSTATS_LEN ARRAY_SIZE(mt7996_gstrings_stats) + +/* Ethtool related API */ +static +void mt7996_get_et_strings(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u32 sset, u8 *data) +{ + if (sset == ETH_SS_STATS) + memcpy(data, *mt7996_gstrings_stats, + sizeof(mt7996_gstrings_stats)); +} + +static +int mt7996_get_et_sset_count(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, int sset) +{ + if (sset == ETH_SS_STATS) + return MT7996_SSTATS_LEN; + + return 0; +} + +static void mt7996_ethtool_worker(void *wi_data, struct ieee80211_sta *sta) +{ + struct mt76_ethtool_worker_info *wi = wi_data; + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + + if (msta->vif->mt76.idx != wi->idx) + return; + + mt76_ethtool_worker(wi, &msta->stats); +} + +static +void mt7996_get_et_stats(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ethtool_stats *stats, u64 *data) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt76_ethtool_worker_info wi = { + .data = data, + .idx = mvif->mt76.idx, + }; + struct mib_stats *mib = &phy->mib; + /* See mt7996_ampdu_stat_read_phy, etc */ + int i, ei = 0; + + mutex_lock(&dev->mt76.mutex); + + mt7996_mac_update_stats(phy); + + data[ei++] = mib->tx_ampdu_cnt; + data[ei++] = mib->tx_stop_q_empty_cnt; + data[ei++] = mib->tx_mpdu_attempts_cnt; + data[ei++] = mib->tx_mpdu_success_cnt; + data[ei++] = mib->tx_rwp_fail_cnt; + data[ei++] = mib->tx_rwp_need_cnt; + data[ei++] = mib->tx_bf_ebf_ppdu_cnt; + data[ei++] = mib->tx_bf_ibf_ppdu_cnt; + + /* Tx ampdu stat */ + for (i = 0; i < 15 /*ARRAY_SIZE(bound)*/; i++) + data[ei++] = phy->mt76->aggr_stats[i]; + data[ei++] = phy->mib.ba_miss_cnt; + + /* Tx Beamformer monitor */ + data[ei++] = mib->tx_bf_ibf_ppdu_cnt; + data[ei++] = mib->tx_bf_ebf_ppdu_cnt; + + /* Tx Beamformer Rx feedback monitor */ + data[ei++] = mib->tx_bf_rx_fb_all_cnt; + data[ei++] = mib->tx_bf_rx_fb_he_cnt; + data[ei++] = mib->tx_bf_rx_fb_vht_cnt; + data[ei++] = mib->tx_bf_rx_fb_ht_cnt; + + data[ei++] = mib->tx_bf_rx_fb_bw; + data[ei++] = mib->tx_bf_rx_fb_nc_cnt; + data[ei++] = mib->tx_bf_rx_fb_nr_cnt; + + /* Tx Beamformee Rx NDPA & Tx feedback report */ + data[ei++] = mib->tx_bf_fb_cpl_cnt; + data[ei++] = mib->tx_bf_fb_trig_cnt; + + /* Tx SU & MU counters */ + data[ei++] = mib->tx_mu_bf_cnt; + data[ei++] = mib->tx_mu_mpdu_cnt; + data[ei++] = mib->tx_mu_acked_mpdu_cnt; + data[ei++] = mib->tx_su_acked_mpdu_cnt; + + /* Tx amsdu info (pack-count histogram) */ + for (i = 0; i < ARRAY_SIZE(mib->tx_amsdu); i++) + data[ei++] = mib->tx_amsdu[i]; + + /* rx counters */ + data[ei++] = mib->rx_fifo_full_cnt; + data[ei++] = mib->rx_mpdu_cnt; + data[ei++] = mib->channel_idle_cnt; + data[ei++] = mib->rx_vector_mismatch_cnt; + data[ei++] = mib->rx_delimiter_fail_cnt; + data[ei++] = mib->rx_len_mismatch_cnt; + data[ei++] = mib->rx_ampdu_cnt; + data[ei++] = mib->rx_ampdu_bytes_cnt; + data[ei++] = mib->rx_ampdu_valid_subframe_cnt; + data[ei++] = mib->rx_ampdu_valid_subframe_bytes_cnt; + data[ei++] = mib->rx_pfdrop_cnt; + data[ei++] = mib->rx_vec_queue_overflow_drop_cnt; + data[ei++] = mib->rx_ba_cnt; + + /* Add values for all stations owned by this vif */ + wi.initial_stat_idx = ei; + ieee80211_iterate_stations_atomic(hw, mt7996_ethtool_worker, &wi); + + mutex_unlock(&dev->mt76.mutex); + + if (wi.sta_count == 0) + return; + + ei += wi.worker_stat_count; + if (ei != MT7996_SSTATS_LEN) + dev_err(dev->mt76.dev, "ei: %d MT7996_SSTATS_LEN: %d", + ei, (int)MT7996_SSTATS_LEN); +} + +static void +mt7996_twt_teardown_request(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + u8 flowid) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + + mutex_lock(&dev->mt76.mutex); + mt7996_mac_twt_teardown_flow(dev, msta, flowid); + mutex_unlock(&dev->mt76.mutex); +} + +static int +mt7996_set_radar_background(struct ieee80211_hw *hw, + struct cfg80211_chan_def *chandef) +{ + struct mt7996_phy *phy = mt7996_hw_phy(hw); + struct mt7996_dev *dev = phy->dev; + int ret = -EINVAL; + bool running; + + mutex_lock(&dev->mt76.mutex); + + if (dev->mt76.region == NL80211_DFS_UNSET) + goto out; + + if (dev->rdd2_phy && dev->rdd2_phy != phy) { + /* rdd2 is already locked */ + ret = -EBUSY; + goto out; + } + + /* rdd2 already configured on a radar channel */ + running = dev->rdd2_phy && + cfg80211_chandef_valid(&dev->rdd2_chandef) && + !!(dev->rdd2_chandef.chan->flags & IEEE80211_CHAN_RADAR); + + if (!chandef || running || + !(chandef->chan->flags & IEEE80211_CHAN_RADAR)) { + ret = mt7996_mcu_rdd_background_enable(phy, NULL); + if (ret) + goto out; + + if (!running) + goto update_phy; + } + + ret = mt7996_mcu_rdd_background_enable(phy, chandef); + if (ret) + goto out; + +update_phy: + dev->rdd2_phy = chandef ? phy : NULL; + if (chandef) + dev->rdd2_chandef = *chandef; +out: + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +const struct ieee80211_ops mt7996_ops = { + .tx = mt7996_tx, + .start = mt7996_start, + .stop = mt7996_stop, + .add_interface = mt7996_add_interface, + .remove_interface = mt7996_remove_interface, + .config = mt7996_config, + .conf_tx = mt7996_conf_tx, + .configure_filter = mt7996_configure_filter, + .bss_info_changed = mt7996_bss_info_changed, + .sta_add = mt7996_sta_add, + .sta_remove = mt7996_sta_remove, + .sta_pre_rcu_remove = mt76_sta_pre_rcu_remove, + .sta_rc_update = mt7996_sta_rc_update, + .set_key = mt7996_set_key, + .ampdu_action = mt7996_ampdu_action, + .set_rts_threshold = mt7996_set_rts_threshold, + .wake_tx_queue = mt76_wake_tx_queue, + .sw_scan_start = mt76_sw_scan, + .sw_scan_complete = mt76_sw_scan_complete, + .release_buffered_frames = mt76_release_buffered_frames, + .get_txpower = mt76_get_txpower, + .channel_switch_beacon = mt7996_channel_switch_beacon, + .get_stats = mt7996_get_stats, + .get_et_sset_count = mt7996_get_et_sset_count, + .get_et_stats = mt7996_get_et_stats, + .get_et_strings = mt7996_get_et_strings, + .get_tsf = mt7996_get_tsf, + .set_tsf = mt7996_set_tsf, + .offset_tsf = mt7996_offset_tsf, + .get_survey = mt76_get_survey, + .get_antenna = mt76_get_antenna, + .set_antenna = mt7996_set_antenna, + .set_bitrate_mask = mt7996_set_bitrate_mask, + .set_coverage_class = mt7996_set_coverage_class, + .sta_statistics = mt7996_sta_statistics, + .sta_set_4addr = mt7996_sta_set_4addr, + .sta_set_decap_offload = mt7996_sta_set_decap_offload, + .add_twt_setup = mt7996_mac_add_twt_setup, + .twt_teardown_request = mt7996_twt_teardown_request, +#ifdef CONFIG_MAC80211_DEBUGFS + .sta_add_debugfs = mt7996_sta_add_debugfs, +#endif + .set_radar_background = mt7996_set_radar_background, +}; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c new file mode 100644 index 000000000000..123076db8021 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -0,0 +1,3429 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#include +#include +#include "mt7996.h" +#include "mcu.h" +#include "mac.h" +#include "eeprom.h" + +struct mt7996_patch_hdr { + char build_date[16]; + char platform[4]; + __be32 hw_sw_ver; + __be32 patch_ver; + __be16 checksum; + u16 reserved; + struct { + __be32 patch_ver; + __be32 subsys; + __be32 feature; + __be32 n_region; + __be32 crc; + u32 reserved[11]; + } desc; +} __packed; + +struct mt7996_patch_sec { + __be32 type; + __be32 offs; + __be32 size; + union { + __be32 spec[13]; + struct { + __be32 addr; + __be32 len; + __be32 sec_key_idx; + __be32 align_len; + u32 reserved[9]; + } info; + }; +} __packed; + +struct mt7996_fw_trailer { + u8 chip_id; + u8 eco_code; + u8 n_region; + u8 format_ver; + u8 format_flag; + u8 reserved[2]; + char fw_ver[10]; + char build_date[15]; + u32 crc; +} __packed; + +struct mt7996_fw_region { + __le32 decomp_crc; + __le32 decomp_len; + __le32 decomp_blk_sz; + u8 reserved[4]; + __le32 addr; + __le32 len; + u8 feature_set; + u8 reserved1[15]; +} __packed; + +#define MCU_PATCH_ADDRESS 0x200000 + +#define HE_PHY(p, c) u8_get_bits(c, IEEE80211_HE_PHY_##p) +#define HE_MAC(m, c) u8_get_bits(c, IEEE80211_HE_MAC_##m) + +static u8 +mt7996_mcu_get_sta_nss(u16 mcs_map) +{ + u8 nss; + + for (nss = 8; nss > 0; nss--) { + u8 nss_mcs = (mcs_map >> (2 * (nss - 1))) & 3; + + if (nss_mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) + break; + } + + return nss - 1; +} + +static void +mt7996_mcu_set_sta_he_mcs(struct ieee80211_sta *sta, __le16 *he_mcs, + u16 mcs_map) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + enum nl80211_band band = msta->vif->phy->mt76->chandef.chan->band; + const u16 *mask = msta->vif->bitrate_mask.control[band].he_mcs; + int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss; + + for (nss = 0; nss < max_nss; nss++) { + int mcs; + + switch ((mcs_map >> (2 * nss)) & 0x3) { + case IEEE80211_HE_MCS_SUPPORT_0_11: + mcs = GENMASK(11, 0); + break; + case IEEE80211_HE_MCS_SUPPORT_0_9: + mcs = GENMASK(9, 0); + break; + case IEEE80211_HE_MCS_SUPPORT_0_7: + mcs = GENMASK(7, 0); + break; + default: + mcs = 0; + } + + mcs = mcs ? fls(mcs & mask[nss]) - 1 : -1; + + switch (mcs) { + case 0 ... 7: + mcs = IEEE80211_HE_MCS_SUPPORT_0_7; + break; + case 8 ... 9: + mcs = IEEE80211_HE_MCS_SUPPORT_0_9; + break; + case 10 ... 11: + mcs = IEEE80211_HE_MCS_SUPPORT_0_11; + break; + default: + mcs = IEEE80211_HE_MCS_NOT_SUPPORTED; + break; + } + mcs_map &= ~(0x3 << (nss * 2)); + mcs_map |= mcs << (nss * 2); + } + + *he_mcs = cpu_to_le16(mcs_map); +} + +static void +mt7996_mcu_set_sta_vht_mcs(struct ieee80211_sta *sta, __le16 *vht_mcs, + const u16 *mask) +{ + u16 mcs, mcs_map = le16_to_cpu(sta->deflink.vht_cap.vht_mcs.rx_mcs_map); + int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss; + + for (nss = 0; nss < max_nss; nss++, mcs_map >>= 2) { + switch (mcs_map & 0x3) { + case IEEE80211_VHT_MCS_SUPPORT_0_9: + mcs = GENMASK(9, 0); + break; + case IEEE80211_VHT_MCS_SUPPORT_0_8: + mcs = GENMASK(8, 0); + break; + case IEEE80211_VHT_MCS_SUPPORT_0_7: + mcs = GENMASK(7, 0); + break; + default: + mcs = 0; + } + + vht_mcs[nss] = cpu_to_le16(mcs & mask[nss]); + } +} + +static void +mt7996_mcu_set_sta_ht_mcs(struct ieee80211_sta *sta, u8 *ht_mcs, + const u8 *mask) +{ + int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss; + + for (nss = 0; nss < max_nss; nss++) + ht_mcs[nss] = sta->deflink.ht_cap.mcs.rx_mask[nss] & mask[nss]; +} + +static int +mt7996_mcu_parse_response(struct mt76_dev *mdev, int cmd, + struct sk_buff *skb, int seq) +{ + struct mt7996_mcu_rxd *rxd; + struct mt7996_mcu_uni_event *event; + int mcu_cmd = FIELD_GET(__MCU_CMD_FIELD_ID, cmd); + int ret = 0; + + if (!skb) { + dev_err(mdev->dev, "Message %08x (seq %d) timeout\n", + cmd, seq); + return -ETIMEDOUT; + } + + rxd = (struct mt7996_mcu_rxd *)skb->data; + if (seq != rxd->seq) + return -EAGAIN; + + if (cmd == MCU_CMD(PATCH_SEM_CONTROL)) { + skb_pull(skb, sizeof(*rxd) - 4); + ret = *skb->data; + } else if ((rxd->option & MCU_UNI_CMD_EVENT) && + rxd->eid == MCU_UNI_EVENT_RESULT) { + skb_pull(skb, sizeof(*rxd)); + event = (struct mt7996_mcu_uni_event *)skb->data; + ret = le32_to_cpu(event->status); + /* skip invalid event */ + if (mcu_cmd != event->cid) + ret = -EAGAIN; + } else { + skb_pull(skb, sizeof(struct mt7996_mcu_rxd)); + } + + return ret; +} + +static int +mt7996_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb, + int cmd, int *wait_seq) +{ + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + int txd_len, mcu_cmd = FIELD_GET(__MCU_CMD_FIELD_ID, cmd); + struct mt76_connac2_mcu_uni_txd *uni_txd; + struct mt76_connac2_mcu_txd *mcu_txd; + enum mt76_mcuq_id qid; + __le32 *txd; + u32 val; + u8 seq; + + mdev->mcu.timeout = 20 * HZ; + + seq = ++dev->mt76.mcu.msg_seq & 0xf; + if (!seq) + seq = ++dev->mt76.mcu.msg_seq & 0xf; + + if (cmd == MCU_CMD(FW_SCATTER)) { + qid = MT_MCUQ_FWDL; + goto exit; + } + + txd_len = cmd & __MCU_CMD_FIELD_UNI ? sizeof(*uni_txd) : sizeof(*mcu_txd); + txd = (__le32 *)skb_push(skb, txd_len); + if (test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state)) + qid = MT_MCUQ_WA; + else + qid = MT_MCUQ_WM; + + val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len) | + FIELD_PREP(MT_TXD0_PKT_FMT, MT_TX_TYPE_CMD) | + FIELD_PREP(MT_TXD0_Q_IDX, MT_TX_MCU_PORT_RX_Q0); + txd[0] = cpu_to_le32(val); + + val = FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_CMD); + txd[1] = cpu_to_le32(val); + + if (cmd & __MCU_CMD_FIELD_UNI) { + uni_txd = (struct mt76_connac2_mcu_uni_txd *)txd; + uni_txd->len = cpu_to_le16(skb->len - sizeof(uni_txd->txd)); + uni_txd->cid = cpu_to_le16(mcu_cmd); + uni_txd->s2d_index = MCU_S2D_H2CN; + uni_txd->pkt_type = MCU_PKT_ID; + uni_txd->seq = seq; + + if (cmd & __MCU_CMD_FIELD_QUERY) + uni_txd->option = MCU_CMD_UNI_QUERY_ACK; + else + uni_txd->option = MCU_CMD_UNI_EXT_ACK; + + if ((cmd & __MCU_CMD_FIELD_WA) && (cmd & __MCU_CMD_FIELD_WM)) + uni_txd->s2d_index = MCU_S2D_H2CN; + else if (cmd & __MCU_CMD_FIELD_WA) + uni_txd->s2d_index = MCU_S2D_H2C; + else if (cmd & __MCU_CMD_FIELD_WM) + uni_txd->s2d_index = MCU_S2D_H2N; + + goto exit; + } + + mcu_txd = (struct mt76_connac2_mcu_txd *)txd; + mcu_txd->len = cpu_to_le16(skb->len - sizeof(mcu_txd->txd)); + mcu_txd->pq_id = cpu_to_le16(MCU_PQ_ID(MT_TX_PORT_IDX_MCU, + MT_TX_MCU_PORT_RX_Q0)); + mcu_txd->pkt_type = MCU_PKT_ID; + mcu_txd->seq = seq; + + mcu_txd->cid = FIELD_GET(__MCU_CMD_FIELD_ID, cmd); + mcu_txd->set_query = MCU_Q_NA; + mcu_txd->ext_cid = FIELD_GET(__MCU_CMD_FIELD_EXT_ID, cmd); + if (mcu_txd->ext_cid) { + mcu_txd->ext_cid_ack = 1; + + if (cmd & __MCU_CMD_FIELD_QUERY) + mcu_txd->set_query = MCU_Q_QUERY; + else + mcu_txd->set_query = MCU_Q_SET; + } + + if (cmd & __MCU_CMD_FIELD_WA) + mcu_txd->s2d_index = MCU_S2D_H2C; + else + mcu_txd->s2d_index = MCU_S2D_H2N; + +exit: + if (wait_seq) + *wait_seq = seq; + + return mt76_tx_queue_skb_raw(dev, mdev->q_mcu[qid], skb, 0); +} + +int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3) +{ + struct { + __le32 args[3]; + } req = { + .args = { + cpu_to_le32(a1), + cpu_to_le32(a2), + cpu_to_le32(a3), + }, + }; + + return mt76_mcu_send_msg(&dev->mt76, cmd, &req, sizeof(req), false); +} + +static void +mt7996_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + if (vif->bss_conf.csa_active) + ieee80211_csa_finish(vif); +} + +static void +mt7996_mcu_rx_radar_detected(struct mt7996_dev *dev, struct sk_buff *skb) +{ + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt7996_mcu_rdd_report *r; + + r = (struct mt7996_mcu_rdd_report *)skb->data; + + mphy = dev->mt76.phys[r->band_idx]; + if (!mphy) + return; + + if (r->band_idx == MT_RX_SEL2) + cfg80211_background_radar_event(mphy->hw->wiphy, + &dev->rdd2_chandef, + GFP_ATOMIC); + else + ieee80211_radar_detected(mphy->hw); + dev->hw_pattern++; +} + +static void +mt7996_mcu_rx_log_message(struct mt7996_dev *dev, struct sk_buff *skb) +{ +#define UNI_EVENT_FW_LOG_FORMAT 0 + struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data; + const char *data = (char *)&rxd[1] + 4, *type; + struct tlv *tlv = (struct tlv *)data; + int len; + + if (!(rxd->option & MCU_UNI_CMD_EVENT)) { + len = skb->len - sizeof(*rxd); + data = (char *)&rxd[1]; + goto out; + } + + if (le16_to_cpu(tlv->tag) != UNI_EVENT_FW_LOG_FORMAT) + return; + + data += sizeof(*tlv) + 4; + len = le16_to_cpu(tlv->len) - sizeof(*tlv) - 4; + +out: + switch (rxd->s2d_index) { + case 0: + if (mt7996_debugfs_rx_log(dev, data, len)) + return; + + type = "WM"; + break; + case 2: + type = "WA"; + break; + default: + type = "unknown"; + break; + } + + wiphy_info(mt76_hw(dev)->wiphy, "%s: %.*s", type, len, data); +} + +static void +mt7996_mcu_cca_finish(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + if (!vif->bss_conf.color_change_active) + return; + + ieee80211_color_change_finish(vif); +} + +static void +mt7996_mcu_ie_countdown(struct mt7996_dev *dev, struct sk_buff *skb) +{ +#define UNI_EVENT_IE_COUNTDOWN_CSA 0 +#define UNI_EVENT_IE_COUNTDOWN_BCC 1 + struct header { + u8 band; + u8 rsv[3]; + }; + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data; + const char *data = (char *)&rxd[1], *tail; + struct header *hdr = (struct header *)data; + struct tlv *tlv = (struct tlv *)(data + 4); + + if (hdr->band && dev->mt76.phys[hdr->band]) + mphy = dev->mt76.phys[hdr->band]; + + tail = skb->data + le16_to_cpu(rxd->len); + while (data + sizeof(struct tlv) < tail && le16_to_cpu(tlv->len)) { + switch (le16_to_cpu(tlv->tag)) { + case UNI_EVENT_IE_COUNTDOWN_CSA: + ieee80211_iterate_active_interfaces_atomic(mphy->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7996_mcu_csa_finish, mphy->hw); + break; + case UNI_EVENT_IE_COUNTDOWN_BCC: + ieee80211_iterate_active_interfaces_atomic(mphy->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7996_mcu_cca_finish, mphy->hw); + break; + } + + data += le16_to_cpu(tlv->len); + tlv = (struct tlv *)data; + } +} + +static void +mt7996_mcu_rx_ext_event(struct mt7996_dev *dev, struct sk_buff *skb) +{ + struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data; + + switch (rxd->ext_eid) { + case MCU_EXT_EVENT_FW_LOG_2_HOST: + mt7996_mcu_rx_log_message(dev, skb); + break; + default: + break; + } +} + +static void +mt7996_mcu_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb) +{ + struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data; + + switch (rxd->eid) { + case MCU_EVENT_EXT: + mt7996_mcu_rx_ext_event(dev, skb); + break; + default: + break; + } + dev_kfree_skb(skb); +} + +static void +mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb) +{ + struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data; + + switch (rxd->eid) { + case MCU_UNI_EVENT_FW_LOG_2_HOST: + mt7996_mcu_rx_log_message(dev, skb); + break; + case MCU_UNI_EVENT_IE_COUNTDOWN: + mt7996_mcu_ie_countdown(dev, skb); + break; + case MCU_UNI_EVENT_RDD_REPORT: + mt7996_mcu_rx_radar_detected(dev, skb); + break; + default: + break; + } + dev_kfree_skb(skb); +} + +void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb) +{ + struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data; + + if (rxd->option & MCU_UNI_CMD_UNSOLICITED_EVENT) { + mt7996_mcu_uni_rx_unsolicited_event(dev, skb); + return; + } + + /* WA still uses legacy event*/ + if (rxd->ext_eid == MCU_EXT_EVENT_FW_LOG_2_HOST || + !rxd->seq) + mt7996_mcu_rx_unsolicited_event(dev, skb); + else + mt76_mcu_rx_event(&dev->mt76, skb); +} + +static struct tlv * +mt7996_mcu_add_uni_tlv(struct sk_buff *skb, u16 tag, u16 len) +{ + struct tlv *ptlv, tlv = { + .tag = cpu_to_le16(tag), + .len = cpu_to_le16(len), + }; + + ptlv = skb_put(skb, len); + memcpy(ptlv, &tlv, sizeof(tlv)); + + return ptlv; +} + +static void +mt7996_mcu_bss_rfch_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, + struct mt7996_phy *phy) +{ + static const u8 rlm_ch_band[] = { + [NL80211_BAND_2GHZ] = 1, + [NL80211_BAND_5GHZ] = 2, + [NL80211_BAND_6GHZ] = 3, + }; + struct cfg80211_chan_def *chandef = &phy->mt76->chandef; + struct bss_rlm_tlv *ch; + struct tlv *tlv; + int freq1 = chandef->center_freq1; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_RLM, sizeof(*ch)); + + ch = (struct bss_rlm_tlv *)tlv; + ch->control_channel = chandef->chan->hw_value; + ch->center_chan = ieee80211_frequency_to_channel(freq1); + ch->bw = mt76_connac_chan_bw(chandef); + ch->tx_streams = hweight8(phy->mt76->antenna_mask); + ch->rx_streams = hweight8(phy->mt76->antenna_mask); + ch->band = rlm_ch_band[chandef->chan->band]; + + if (chandef->width == NL80211_CHAN_WIDTH_80P80) { + int freq2 = chandef->center_freq2; + + ch->center_chan2 = ieee80211_frequency_to_channel(freq2); + } +} + +static void +mt7996_mcu_bss_ra_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, + struct mt7996_phy *phy) +{ + struct bss_ra_tlv *ra; + struct tlv *tlv; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_RA, sizeof(*ra)); + + ra = (struct bss_ra_tlv *)tlv; + ra->short_preamble = true; +} + +static void +mt7996_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, + struct mt7996_phy *phy) +{ +#define DEFAULT_HE_PE_DURATION 4 +#define DEFAULT_HE_DURATION_RTS_THRES 1023 + const struct ieee80211_sta_he_cap *cap; + struct bss_info_uni_he *he; + struct tlv *tlv; + + cap = mt76_connac_get_he_phy_cap(phy->mt76, vif); + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_HE_BASIC, sizeof(*he)); + + he = (struct bss_info_uni_he *)tlv; + he->he_pe_duration = vif->bss_conf.htc_trig_based_pkt_ext; + if (!he->he_pe_duration) + he->he_pe_duration = DEFAULT_HE_PE_DURATION; + + he->he_rts_thres = cpu_to_le16(vif->bss_conf.frame_time_rts_th); + if (!he->he_rts_thres) + he->he_rts_thres = cpu_to_le16(DEFAULT_HE_DURATION_RTS_THRES); + + he->max_nss_mcs[CMD_HE_MCS_BW80] = cap->he_mcs_nss_supp.tx_mcs_80; + he->max_nss_mcs[CMD_HE_MCS_BW160] = cap->he_mcs_nss_supp.tx_mcs_160; + he->max_nss_mcs[CMD_HE_MCS_BW8080] = cap->he_mcs_nss_supp.tx_mcs_80p80; +} + +static void +mt7996_mcu_bss_bmc_tlv(struct sk_buff *skb, struct mt7996_phy *phy) +{ + struct bss_rate_tlv *bmc; + struct cfg80211_chan_def *chandef = &phy->mt76->chandef; + enum nl80211_band band = chandef->chan->band; + struct tlv *tlv; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_RATE, sizeof(*bmc)); + + bmc = (struct bss_rate_tlv *)tlv; + if (band == NL80211_BAND_2GHZ) { + bmc->short_preamble = true; + } else { + bmc->bc_trans = cpu_to_le16(0x8080); + bmc->mc_trans = cpu_to_le16(0x8080); + bmc->bc_fixed_rate = 1; + bmc->mc_fixed_rate = 1; + bmc->short_preamble = 1; + } +} + +static void +mt7996_mcu_bss_txcmd_tlv(struct sk_buff *skb, bool en) +{ + struct bss_txcmd_tlv *txcmd; + struct tlv *tlv; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_TXCMD, sizeof(*txcmd)); + + txcmd = (struct bss_txcmd_tlv *)tlv; + txcmd->txcmd_mode = en; +} + +static void +mt7996_mcu_bss_mld_tlv(struct sk_buff *skb, struct ieee80211_vif *vif) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct bss_mld_tlv *mld; + struct tlv *tlv; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_MLD, sizeof(*mld)); + + mld = (struct bss_mld_tlv *)tlv; + mld->group_mld_id = 0xff; + mld->own_mld_id = mvif->mt76.idx; + mld->remap_idx = 0xff; +} + +static void +mt7996_mcu_bss_sec_tlv(struct sk_buff *skb, struct ieee80211_vif *vif) +{ + struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv; + struct bss_sec_tlv *sec; + struct tlv *tlv; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_SEC, sizeof(*sec)); + + sec = (struct bss_sec_tlv *)tlv; + sec->cipher = mvif->cipher; +} + +static int +mt7996_mcu_muar_config(struct mt7996_phy *phy, struct ieee80211_vif *vif, + bool bssid, bool enable) +{ +#define UNI_MUAR_ENTRY 2 + struct mt7996_dev *dev = phy->dev; + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + u32 idx = mvif->mt76.omac_idx - REPEATER_BSSID_START; + const u8 *addr = vif->addr; + + struct { + struct { + u8 band; + u8 __rsv[3]; + } hdr; + + __le16 tag; + __le16 len; + + bool smesh; + u8 bssid; + u8 index; + u8 entry_add; + u8 addr[ETH_ALEN]; + u8 __rsv[2]; + } __packed req = { + .hdr.band = phy->mt76->band_idx, + .tag = cpu_to_le16(UNI_MUAR_ENTRY), + .len = cpu_to_le16(sizeof(req) - sizeof(req.hdr)), + .smesh = false, + .index = idx * 2 + bssid, + .entry_add = true, + }; + + if (bssid) + addr = vif->bss_conf.bssid; + + if (enable) + memcpy(req.addr, addr, ETH_ALEN); + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(REPT_MUAR), &req, + sizeof(req), true); +} + +static int +mt7996_mcu_bss_basic_tlv(struct sk_buff *skb, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct mt76_phy *phy, u16 wlan_idx, + bool enable) +{ + struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv; + struct cfg80211_chan_def *chandef = &phy->chandef; + struct mt76_connac_bss_basic_tlv *bss; + u32 type = CONNECTION_INFRA_AP; + struct tlv *tlv; + int idx; + + switch (vif->type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_MONITOR: + break; + case NL80211_IFTYPE_STATION: + if (enable) { + rcu_read_lock(); + if (!sta) + sta = ieee80211_find_sta(vif, + vif->bss_conf.bssid); + /* TODO: enable BSS_INFO_UAPSD & BSS_INFO_PM */ + if (sta) { + struct mt76_wcid *wcid; + + wcid = (struct mt76_wcid *)sta->drv_priv; + wlan_idx = wcid->idx; + } + rcu_read_unlock(); + } + type = CONNECTION_INFRA_STA; + break; + case NL80211_IFTYPE_ADHOC: + type = CONNECTION_IBSS_ADHOC; + break; + default: + WARN_ON(1); + break; + } + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_BASIC, sizeof(*bss)); + + bss = (struct mt76_connac_bss_basic_tlv *)tlv; + bss->bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int); + bss->dtim_period = vif->bss_conf.dtim_period; + bss->bmc_tx_wlan_idx = cpu_to_le16(wlan_idx); + bss->sta_idx = cpu_to_le16(wlan_idx); + bss->conn_type = cpu_to_le32(type); + bss->omac_idx = mvif->omac_idx; + bss->band_idx = mvif->band_idx; + bss->wmm_idx = mvif->wmm_idx; + bss->conn_state = !enable; + bss->active = enable; + + idx = mvif->omac_idx > EXT_BSSID_START ? HW_BSSID_0 : mvif->omac_idx; + bss->hw_bss_idx = idx; + + if (vif->type == NL80211_IFTYPE_MONITOR) { + memcpy(bss->bssid, phy->macaddr, ETH_ALEN); + return 0; + } + + memcpy(bss->bssid, vif->bss_conf.bssid, ETH_ALEN); + bss->bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int); + bss->dtim_period = vif->bss_conf.dtim_period; + bss->phymode = mt76_connac_get_phy_mode(phy, vif, + chandef->chan->band, NULL); + + if (chandef->chan->band == NL80211_BAND_6GHZ) + bss->phymode_ext |= PHY_MODE_AX_6G; + + return 0; +} + +static struct sk_buff * +__mt7996_mcu_alloc_bss_req(struct mt76_dev *dev, struct mt76_vif *mvif, int len) +{ + struct bss_req_hdr hdr = { + .bss_idx = mvif->idx, + }; + struct sk_buff *skb; + + skb = mt76_mcu_msg_alloc(dev, NULL, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + skb_put_data(skb, &hdr, sizeof(hdr)); + + return skb; +} + +int mt7996_mcu_add_bss_info(struct mt7996_phy *phy, + struct ieee80211_vif *vif, int enable) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_dev *dev = phy->dev; + struct sk_buff *skb; + + if (mvif->mt76.omac_idx >= REPEATER_BSSID_START) { + mt7996_mcu_muar_config(phy, vif, false, enable); + mt7996_mcu_muar_config(phy, vif, true, enable); + } + + skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76, + MT7996_BSS_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + /* bss_basic must be first */ + mt7996_mcu_bss_basic_tlv(skb, vif, NULL, phy->mt76, + mvif->sta.wcid.idx, enable); + mt7996_mcu_bss_sec_tlv(skb, vif); + + if (vif->type == NL80211_IFTYPE_MONITOR) + goto out; + + if (enable) { + mt7996_mcu_bss_rfch_tlv(skb, vif, phy); + mt7996_mcu_bss_bmc_tlv(skb, phy); + mt7996_mcu_bss_ra_tlv(skb, vif, phy); + mt7996_mcu_bss_txcmd_tlv(skb, true); + + if (vif->bss_conf.he_support) + mt7996_mcu_bss_he_tlv(skb, vif, phy); + + /* this tag is necessary no matter if the vif is MLD */ + mt7996_mcu_bss_mld_tlv(skb, vif); + } +out: + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true); +} + +static int +mt7996_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif, + struct ieee80211_ampdu_params *params, + bool enable, bool tx) +{ + struct mt76_wcid *wcid = (struct mt76_wcid *)params->sta->drv_priv; + struct sta_rec_ba_uni *ba; + struct sk_buff *skb; + struct tlv *tlv; + + skb = __mt76_connac_mcu_alloc_sta_req(dev, mvif, wcid, + MT7996_STA_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BA, sizeof(*ba)); + + ba = (struct sta_rec_ba_uni *)tlv; + ba->ba_type = tx ? MT_BA_TYPE_ORIGINATOR : MT_BA_TYPE_RECIPIENT; + ba->winsize = cpu_to_le16(params->buf_size); + ba->ssn = cpu_to_le16(params->ssn); + ba->ba_en = enable << params->tid; + ba->amsdu = params->amsdu; + ba->tid = params->tid; + + return mt76_mcu_skb_send_msg(dev, skb, + MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); +} + +/** starec & wtbl **/ +int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev, + struct ieee80211_ampdu_params *params, + bool enable) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)params->sta->drv_priv; + struct mt7996_vif *mvif = msta->vif; + + if (enable && !params->amsdu) + msta->wcid.amsdu = false; + + return mt7996_mcu_sta_ba(&dev->mt76, &mvif->mt76, params, + enable, true); +} + +int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev, + struct ieee80211_ampdu_params *params, + bool enable) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)params->sta->drv_priv; + struct mt7996_vif *mvif = msta->vif; + + return mt7996_mcu_sta_ba(&dev->mt76, &mvif->mt76, params, + enable, false); +} + +static void +mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) +{ + struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem; + struct ieee80211_he_mcs_nss_supp mcs_map; + struct sta_rec_he_v2 *he; + struct tlv *tlv; + int i = 0; + + if (!sta->deflink.he_cap.has_he) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_V2, sizeof(*he)); + + he = (struct sta_rec_he_v2 *)tlv; + for (i = 0; i < 11; i++) { + if (i < 6) + he->he_mac_cap[i] = cpu_to_le16(elem->mac_cap_info[i]); + he->he_phy_cap[i] = cpu_to_le16(elem->phy_cap_info[i]); + } + + mcs_map = sta->deflink.he_cap.he_mcs_nss_supp; + switch (sta->deflink.bandwidth) { + case IEEE80211_STA_RX_BW_160: + if (elem->phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) + mt7996_mcu_set_sta_he_mcs(sta, + &he->max_nss_mcs[CMD_HE_MCS_BW8080], + le16_to_cpu(mcs_map.rx_mcs_80p80)); + + mt7996_mcu_set_sta_he_mcs(sta, + &he->max_nss_mcs[CMD_HE_MCS_BW160], + le16_to_cpu(mcs_map.rx_mcs_160)); + fallthrough; + default: + mt7996_mcu_set_sta_he_mcs(sta, + &he->max_nss_mcs[CMD_HE_MCS_BW80], + le16_to_cpu(mcs_map.rx_mcs_80)); + break; + } + + he->pkt_ext = 2; +} + +static void +mt7996_mcu_sta_he_6g_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) +{ + struct sta_rec_he_6g_capa *he_6g; + struct tlv *tlv; + + if (!sta->deflink.he_6ghz_capa.capa) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_6G, sizeof(*he_6g)); + + he_6g = (struct sta_rec_he_6g_capa *)tlv; + he_6g->capa = sta->deflink.he_6ghz_capa.capa; +} + +static void +mt7996_mcu_sta_ht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) +{ + struct sta_rec_ht *ht; + struct tlv *tlv; + + if (!sta->deflink.ht_cap.ht_supported) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HT, sizeof(*ht)); + + ht = (struct sta_rec_ht *)tlv; + ht->ht_cap = cpu_to_le16(sta->deflink.ht_cap.cap); +} + +static void +mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) +{ + struct sta_rec_vht *vht; + struct tlv *tlv; + + /* For 6G band, this tlv is necessary to let hw work normally */ + if (!sta->deflink.he_6ghz_capa.capa && !sta->deflink.vht_cap.vht_supported) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_VHT, sizeof(*vht)); + + vht = (struct sta_rec_vht *)tlv; + vht->vht_cap = cpu_to_le32(sta->deflink.vht_cap.cap); + vht->vht_rx_mcs_map = sta->deflink.vht_cap.vht_mcs.rx_mcs_map; + vht->vht_tx_mcs_map = sta->deflink.vht_cap.vht_mcs.tx_mcs_map; +} + +static void +mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb, + struct ieee80211_vif *vif, struct ieee80211_sta *sta) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct sta_rec_amsdu *amsdu; + struct tlv *tlv; + + if (vif->type != NL80211_IFTYPE_STATION && + vif->type != NL80211_IFTYPE_AP) + return; + + if (!sta->deflink.agg.max_amsdu_len) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HW_AMSDU, sizeof(*amsdu)); + amsdu = (struct sta_rec_amsdu *)tlv; + amsdu->max_amsdu_num = 8; + amsdu->amsdu_en = true; + msta->wcid.amsdu = true; + + switch (sta->deflink.agg.max_amsdu_len) { + case IEEE80211_MAX_MPDU_LEN_VHT_11454: + amsdu->max_mpdu_size = + IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454; + return; + case IEEE80211_MAX_MPDU_LEN_HT_7935: + case IEEE80211_MAX_MPDU_LEN_VHT_7991: + amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991; + return; + default: + amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895; + return; + } +} + +static inline bool +mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool bfee) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + int tx_ant = hweight8(phy->mt76->antenna_mask) - 1; + + if (vif->type != NL80211_IFTYPE_STATION && + vif->type != NL80211_IFTYPE_AP) + return false; + + if (!bfee && tx_ant < 2) + return false; + + if (sta->deflink.he_cap.has_he) { + struct ieee80211_he_cap_elem *pe = &sta->deflink.he_cap.he_cap_elem; + + if (bfee) + return mvif->cap.he_su_ebfee && + HE_PHY(CAP3_SU_BEAMFORMER, pe->phy_cap_info[3]); + else + return mvif->cap.he_su_ebfer && + HE_PHY(CAP4_SU_BEAMFORMEE, pe->phy_cap_info[4]); + } + + if (sta->deflink.vht_cap.vht_supported) { + u32 cap = sta->deflink.vht_cap.cap; + + if (bfee) + return mvif->cap.vht_su_ebfee && + (cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE); + else + return mvif->cap.vht_su_ebfer && + (cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE); + } + + return false; +} + +static void +mt7996_mcu_sta_sounding_rate(struct sta_rec_bf *bf) +{ + bf->sounding_phy = MT_PHY_TYPE_OFDM; + bf->ndp_rate = 0; /* mcs0 */ + bf->ndpa_rate = MT7996_CFEND_RATE_DEFAULT; /* ofdm 24m */ + bf->rept_poll_rate = MT7996_CFEND_RATE_DEFAULT; /* ofdm 24m */ +} + +static void +mt7996_mcu_sta_bfer_ht(struct ieee80211_sta *sta, struct mt7996_phy *phy, + struct sta_rec_bf *bf) +{ + struct ieee80211_mcs_info *mcs = &sta->deflink.ht_cap.mcs; + u8 n = 0; + + bf->tx_mode = MT_PHY_TYPE_HT; + + if ((mcs->tx_params & IEEE80211_HT_MCS_TX_RX_DIFF) && + (mcs->tx_params & IEEE80211_HT_MCS_TX_DEFINED)) + n = FIELD_GET(IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK, + mcs->tx_params); + else if (mcs->rx_mask[3]) + n = 3; + else if (mcs->rx_mask[2]) + n = 2; + else if (mcs->rx_mask[1]) + n = 1; + + bf->nrow = hweight8(phy->mt76->antenna_mask) - 1; + bf->ncol = min_t(u8, bf->nrow, n); + bf->ibf_ncol = n; +} + +static void +mt7996_mcu_sta_bfer_vht(struct ieee80211_sta *sta, struct mt7996_phy *phy, + struct sta_rec_bf *bf, bool explicit) +{ + struct ieee80211_sta_vht_cap *pc = &sta->deflink.vht_cap; + struct ieee80211_sta_vht_cap *vc = &phy->mt76->sband_5g.sband.vht_cap; + u16 mcs_map = le16_to_cpu(pc->vht_mcs.rx_mcs_map); + u8 nss_mcs = mt7996_mcu_get_sta_nss(mcs_map); + u8 tx_ant = hweight8(phy->mt76->antenna_mask) - 1; + + bf->tx_mode = MT_PHY_TYPE_VHT; + + if (explicit) { + u8 sts, snd_dim; + + mt7996_mcu_sta_sounding_rate(bf); + + sts = FIELD_GET(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK, + pc->cap); + snd_dim = FIELD_GET(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK, + vc->cap); + bf->nrow = min_t(u8, min_t(u8, snd_dim, sts), tx_ant); + bf->ncol = min_t(u8, nss_mcs, bf->nrow); + bf->ibf_ncol = bf->ncol; + + if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160) + bf->nrow = 1; + } else { + bf->nrow = tx_ant; + bf->ncol = min_t(u8, nss_mcs, bf->nrow); + bf->ibf_ncol = nss_mcs; + + if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160) + bf->ibf_nrow = 1; + } +} + +static void +mt7996_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif, + struct mt7996_phy *phy, struct sta_rec_bf *bf) +{ + struct ieee80211_sta_he_cap *pc = &sta->deflink.he_cap; + struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem; + const struct ieee80211_sta_he_cap *vc = + mt76_connac_get_he_phy_cap(phy->mt76, vif); + const struct ieee80211_he_cap_elem *ve = &vc->he_cap_elem; + u16 mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80); + u8 nss_mcs = mt7996_mcu_get_sta_nss(mcs_map); + u8 snd_dim, sts; + + bf->tx_mode = MT_PHY_TYPE_HE_SU; + + mt7996_mcu_sta_sounding_rate(bf); + + bf->trigger_su = HE_PHY(CAP6_TRIG_SU_BEAMFORMING_FB, + pe->phy_cap_info[6]); + bf->trigger_mu = HE_PHY(CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB, + pe->phy_cap_info[6]); + snd_dim = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK, + ve->phy_cap_info[5]); + sts = HE_PHY(CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_MASK, + pe->phy_cap_info[4]); + bf->nrow = min_t(u8, snd_dim, sts); + bf->ncol = min_t(u8, nss_mcs, bf->nrow); + bf->ibf_ncol = bf->ncol; + + if (sta->deflink.bandwidth != IEEE80211_STA_RX_BW_160) + return; + + /* go over for 160MHz and 80p80 */ + if (pe->phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G) { + mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_160); + nss_mcs = mt7996_mcu_get_sta_nss(mcs_map); + + bf->ncol_gt_bw80 = nss_mcs; + } + + if (pe->phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) { + mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80p80); + nss_mcs = mt7996_mcu_get_sta_nss(mcs_map); + + if (bf->ncol_gt_bw80) + bf->ncol_gt_bw80 = min_t(u8, bf->ncol_gt_bw80, nss_mcs); + else + bf->ncol_gt_bw80 = nss_mcs; + } + + snd_dim = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK, + ve->phy_cap_info[5]); + sts = HE_PHY(CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_MASK, + pe->phy_cap_info[4]); + + bf->nrow_gt_bw80 = min_t(int, snd_dim, sts); +} + +static void +mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb, + struct ieee80211_vif *vif, struct ieee80211_sta *sta) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_phy *phy = mvif->phy; + int tx_ant = hweight8(phy->mt76->antenna_mask) - 1; + struct sta_rec_bf *bf; + struct tlv *tlv; + const u8 matrix[4][4] = { + {0, 0, 0, 0}, + {1, 1, 0, 0}, /* 2x1, 2x2, 2x3, 2x4 */ + {2, 4, 4, 0}, /* 3x1, 3x2, 3x3, 3x4 */ + {3, 5, 6, 0} /* 4x1, 4x2, 4x3, 4x4 */ + }; + bool ebf; + + if (!(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he)) + return; + + ebf = mt7996_is_ebf_supported(phy, vif, sta, false); + if (!ebf && !dev->ibf) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BF, sizeof(*bf)); + bf = (struct sta_rec_bf *)tlv; + + /* he: eBF only, in accordance with spec + * vht: support eBF and iBF + * ht: iBF only, since mac80211 lacks of eBF support + */ + if (sta->deflink.he_cap.has_he && ebf) + mt7996_mcu_sta_bfer_he(sta, vif, phy, bf); + else if (sta->deflink.vht_cap.vht_supported) + mt7996_mcu_sta_bfer_vht(sta, phy, bf, ebf); + else if (sta->deflink.ht_cap.ht_supported) + mt7996_mcu_sta_bfer_ht(sta, phy, bf); + else + return; + + bf->bf_cap = ebf ? ebf : dev->ibf << 1; + bf->bw = sta->deflink.bandwidth; + bf->ibf_dbw = sta->deflink.bandwidth; + bf->ibf_nrow = tx_ant; + + if (!ebf && sta->deflink.bandwidth <= IEEE80211_STA_RX_BW_40 && !bf->ncol) + bf->ibf_timeout = 0x48; + else + bf->ibf_timeout = 0x18; + + if (ebf && bf->nrow != tx_ant) + bf->mem_20m = matrix[tx_ant][bf->ncol]; + else + bf->mem_20m = matrix[bf->nrow][bf->ncol]; + + switch (sta->deflink.bandwidth) { + case IEEE80211_STA_RX_BW_160: + case IEEE80211_STA_RX_BW_80: + bf->mem_total = bf->mem_20m * 2; + break; + case IEEE80211_STA_RX_BW_40: + bf->mem_total = bf->mem_20m; + break; + case IEEE80211_STA_RX_BW_20: + default: + break; + } +} + +static void +mt7996_mcu_sta_bfee_tlv(struct mt7996_dev *dev, struct sk_buff *skb, + struct ieee80211_vif *vif, struct ieee80211_sta *sta) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_phy *phy = mvif->phy; + int tx_ant = hweight8(phy->mt76->antenna_mask) - 1; + struct sta_rec_bfee *bfee; + struct tlv *tlv; + u8 nrow = 0; + + if (!(sta->deflink.vht_cap.vht_supported || sta->deflink.he_cap.has_he)) + return; + + if (!mt7996_is_ebf_supported(phy, vif, sta, true)) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BFEE, sizeof(*bfee)); + bfee = (struct sta_rec_bfee *)tlv; + + if (sta->deflink.he_cap.has_he) { + struct ieee80211_he_cap_elem *pe = &sta->deflink.he_cap.he_cap_elem; + + nrow = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK, + pe->phy_cap_info[5]); + } else if (sta->deflink.vht_cap.vht_supported) { + struct ieee80211_sta_vht_cap *pc = &sta->deflink.vht_cap; + + nrow = FIELD_GET(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK, + pc->cap); + } + + /* reply with identity matrix to avoid 2x2 BF negative gain */ + bfee->fb_identity_matrix = (nrow == 1 && tx_ant == 2); +} + +static void +mt7996_mcu_sta_phy_tlv(struct mt7996_dev *dev, struct sk_buff *skb, + struct ieee80211_vif *vif, struct ieee80211_sta *sta) +{ + struct sta_rec_phy *phy; + struct tlv *tlv; + u8 af = 0, mm = 0; + + if (!sta->deflink.ht_cap.ht_supported && !sta->deflink.he_6ghz_capa.capa) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_PHY, sizeof(*phy)); + + phy = (struct sta_rec_phy *)tlv; + if (sta->deflink.ht_cap.ht_supported) { + af = sta->deflink.ht_cap.ampdu_factor; + mm = sta->deflink.ht_cap.ampdu_density; + } + + if (sta->deflink.vht_cap.vht_supported) { + u8 vht_af = FIELD_GET(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK, + sta->deflink.vht_cap.cap); + + af = max_t(u8, af, vht_af); + } + + if (sta->deflink.he_6ghz_capa.capa) { + af = le16_get_bits(sta->deflink.he_6ghz_capa.capa, + IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP); + mm = le16_get_bits(sta->deflink.he_6ghz_capa.capa, + IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START); + } + + phy->ampdu = FIELD_PREP(IEEE80211_HT_AMPDU_PARM_FACTOR, af) | + FIELD_PREP(IEEE80211_HT_AMPDU_PARM_DENSITY, mm); + phy->max_ampdu_len = af; +} + +static void +mt7996_mcu_sta_hdrt_tlv(struct mt7996_dev *dev, struct sk_buff *skb) +{ + struct sta_rec_hdrt *hdrt; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HDRT, sizeof(*hdrt)); + + hdrt = (struct sta_rec_hdrt *)tlv; + hdrt->hdrt_mode = 1; +} + +static void +mt7996_mcu_sta_hdr_trans_tlv(struct mt7996_dev *dev, struct sk_buff *skb, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct sta_rec_hdr_trans *hdr_trans; + struct mt76_wcid *wcid; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HDR_TRANS, sizeof(*hdr_trans)); + hdr_trans = (struct sta_rec_hdr_trans *)tlv; + hdr_trans->dis_rx_hdr_tran = true; + + if (vif->type == NL80211_IFTYPE_STATION) + hdr_trans->to_ds = true; + else + hdr_trans->from_ds = true; + + wcid = (struct mt76_wcid *)sta->drv_priv; + if (!wcid) + return; + + hdr_trans->dis_rx_hdr_tran = !test_bit(MT_WCID_FLAG_HDR_TRANS, &wcid->flags); + if (test_bit(MT_WCID_FLAG_4ADDR, &wcid->flags)) { + hdr_trans->to_ds = true; + hdr_trans->from_ds = true; + } +} + +static enum mcu_mmps_mode +mt7996_mcu_get_mmps_mode(enum ieee80211_smps_mode smps) +{ + switch (smps) { + case IEEE80211_SMPS_OFF: + return MCU_MMPS_DISABLE; + case IEEE80211_SMPS_STATIC: + return MCU_MMPS_STATIC; + case IEEE80211_SMPS_DYNAMIC: + return MCU_MMPS_DYNAMIC; + default: + return MCU_MMPS_DISABLE; + } +} + +int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev, + void *data, u16 version) +{ + struct ra_fixed_rate *req; + struct uni_header hdr; + struct sk_buff *skb; + struct tlv *tlv; + int len; + + len = sizeof(hdr) + sizeof(*req); + + skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, &hdr, sizeof(hdr)); + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_RA_FIXED_RATE, sizeof(*req)); + req = (struct ra_fixed_rate *)tlv; + req->version = cpu_to_le16(version); + memcpy(&req->rate, data, sizeof(req->rate)); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WM_UNI_CMD(RA), true); +} + +static void +mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev, + struct ieee80211_vif *vif, struct ieee80211_sta *sta) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt76_phy *mphy = mvif->phy->mt76; + struct cfg80211_chan_def *chandef = &mphy->chandef; + struct cfg80211_bitrate_mask *mask = &mvif->bitrate_mask; + enum nl80211_band band = chandef->chan->band; + struct sta_rec_ra *ra; + struct tlv *tlv; + u32 supp_rate = sta->deflink.supp_rates[band]; + u32 cap = sta->wme ? STA_CAP_WMM : 0; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA, sizeof(*ra)); + ra = (struct sta_rec_ra *)tlv; + + ra->valid = true; + ra->auto_rate = true; + ra->phy_mode = mt76_connac_get_phy_mode(mphy, vif, band, sta); + ra->channel = chandef->chan->hw_value; + ra->bw = sta->deflink.bandwidth; + ra->phy.bw = sta->deflink.bandwidth; + ra->mmps_mode = mt7996_mcu_get_mmps_mode(sta->deflink.smps_mode); + + if (supp_rate) { + supp_rate &= mask->control[band].legacy; + ra->rate_len = hweight32(supp_rate); + + if (band == NL80211_BAND_2GHZ) { + ra->supp_mode = MODE_CCK; + ra->supp_cck_rate = supp_rate & GENMASK(3, 0); + + if (ra->rate_len > 4) { + ra->supp_mode |= MODE_OFDM; + ra->supp_ofdm_rate = supp_rate >> 4; + } + } else { + ra->supp_mode = MODE_OFDM; + ra->supp_ofdm_rate = supp_rate; + } + } + + if (sta->deflink.ht_cap.ht_supported) { + ra->supp_mode |= MODE_HT; + ra->af = sta->deflink.ht_cap.ampdu_factor; + ra->ht_gf = !!(sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD); + + cap |= STA_CAP_HT; + if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_20) + cap |= STA_CAP_SGI_20; + if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_40) + cap |= STA_CAP_SGI_40; + if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_TX_STBC) + cap |= STA_CAP_TX_STBC; + if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_RX_STBC) + cap |= STA_CAP_RX_STBC; + if (mvif->cap.ht_ldpc && + (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING)) + cap |= STA_CAP_LDPC; + + mt7996_mcu_set_sta_ht_mcs(sta, ra->ht_mcs, + mask->control[band].ht_mcs); + ra->supp_ht_mcs = *(__le32 *)ra->ht_mcs; + } + + if (sta->deflink.vht_cap.vht_supported) { + u8 af; + + ra->supp_mode |= MODE_VHT; + af = FIELD_GET(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK, + sta->deflink.vht_cap.cap); + ra->af = max_t(u8, ra->af, af); + + cap |= STA_CAP_VHT; + if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80) + cap |= STA_CAP_VHT_SGI_80; + if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_160) + cap |= STA_CAP_VHT_SGI_160; + if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_TXSTBC) + cap |= STA_CAP_VHT_TX_STBC; + if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_1) + cap |= STA_CAP_VHT_RX_STBC; + if (mvif->cap.vht_ldpc && + (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC)) + cap |= STA_CAP_VHT_LDPC; + + mt7996_mcu_set_sta_vht_mcs(sta, ra->supp_vht_mcs, + mask->control[band].vht_mcs); + } + + if (sta->deflink.he_cap.has_he) { + ra->supp_mode |= MODE_HE; + cap |= STA_CAP_HE; + + if (sta->deflink.he_6ghz_capa.capa) + ra->af = le16_get_bits(sta->deflink.he_6ghz_capa.capa, + IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP); + } + ra->sta_cap = cpu_to_le32(cap); +} + +int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool changed) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct sk_buff *skb; + + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, + &msta->wcid, + MT7996_STA_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + /* firmware rc algorithm refers to sta_rec_he for HE control. + * once dev->rc_work changes the settings driver should also + * update sta_rec_he here. + */ + if (changed) + mt7996_mcu_sta_he_tlv(skb, sta); + + /* sta_rec_ra accommodates BW, NSS and only MCS range format + * i.e 0-{7,8,9} for VHT. + */ + mt7996_mcu_sta_rate_ctrl_tlv(skb, dev, vif, sta); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); +} + +static int +mt7996_mcu_add_group(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ +#define MT_STA_BSS_GROUP 1 + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_sta *msta; + struct { + u8 __rsv1[4]; + + __le16 tag; + __le16 len; + __le16 wlan_idx; + u8 __rsv2[2]; + __le32 action; + __le32 val; + u8 __rsv3[8]; + } __packed req = { + .tag = cpu_to_le16(UNI_VOW_DRR_CTRL), + .len = cpu_to_le16(sizeof(req) - 4), + .action = cpu_to_le32(MT_STA_BSS_GROUP), + .val = cpu_to_le32(mvif->mt76.idx % 16), + }; + + msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->sta; + req.wlan_idx = cpu_to_le16(msta->wcid.idx); + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(VOW), &req, + sizeof(req), true); +} + +int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool enable) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_sta *msta; + struct sk_buff *skb; + int ret; + + msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->sta; + + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, + &msta->wcid, + MT7996_STA_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + /* starec basic */ + mt76_connac_mcu_sta_basic_tlv(skb, vif, sta, enable, + !rcu_access_pointer(dev->mt76.wcid[msta->wcid.idx])); + if (!enable) + goto out; + + /* tag order is in accordance with firmware dependency. */ + if (sta) { + /* starec phy */ + mt7996_mcu_sta_phy_tlv(dev, skb, vif, sta); + /* starec hdrt mode */ + mt7996_mcu_sta_hdrt_tlv(dev, skb); + /* starec bfer */ + mt7996_mcu_sta_bfer_tlv(dev, skb, vif, sta); + /* starec ht */ + mt7996_mcu_sta_ht_tlv(skb, sta); + /* starec vht */ + mt7996_mcu_sta_vht_tlv(skb, sta); + /* starec uapsd */ + mt76_connac_mcu_sta_uapsd(skb, vif, sta); + /* starec amsdu */ + mt7996_mcu_sta_amsdu_tlv(dev, skb, vif, sta); + /* starec he */ + mt7996_mcu_sta_he_tlv(skb, sta); + /* starec he 6g*/ + mt7996_mcu_sta_he_6g_tlv(skb, sta); + /* TODO: starec muru */ + /* starec bfee */ + mt7996_mcu_sta_bfee_tlv(dev, skb, vif, sta); + /* starec hdr trans */ + mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, sta); + } + + ret = mt7996_mcu_add_group(dev, vif, sta); + if (ret) { + dev_kfree_skb(skb); + return ret; + } +out: + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); +} + +static int +mt7996_mcu_sta_key_tlv(struct mt76_wcid *wcid, + struct mt76_connac_sta_key_conf *sta_key_conf, + struct sk_buff *skb, + struct ieee80211_key_conf *key, + enum set_key_cmd cmd) +{ + struct sta_rec_sec_uni *sec; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_KEY_V2, sizeof(*sec)); + sec = (struct sta_rec_sec_uni *)tlv; + sec->add = cmd; + + if (cmd == SET_KEY) { + struct sec_key_uni *sec_key; + u8 cipher; + + cipher = mt76_connac_mcu_get_cipher(key->cipher); + if (cipher == MCU_CIPHER_NONE) + return -EOPNOTSUPP; + + sec_key = &sec->key[0]; + sec_key->cipher_len = sizeof(*sec_key); + + if (cipher == MCU_CIPHER_BIP_CMAC_128) { + sec_key->wlan_idx = cpu_to_le16(wcid->idx); + sec_key->cipher_id = MCU_CIPHER_AES_CCMP; + sec_key->key_id = sta_key_conf->keyidx; + sec_key->key_len = 16; + memcpy(sec_key->key, sta_key_conf->key, 16); + + sec_key = &sec->key[1]; + sec_key->wlan_idx = cpu_to_le16(wcid->idx); + sec_key->cipher_id = MCU_CIPHER_BIP_CMAC_128; + sec_key->cipher_len = sizeof(*sec_key); + sec_key->key_len = 16; + memcpy(sec_key->key, key->key, 16); + sec->n_cipher = 2; + } else { + sec_key->wlan_idx = cpu_to_le16(wcid->idx); + sec_key->cipher_id = cipher; + sec_key->key_id = key->keyidx; + sec_key->key_len = key->keylen; + memcpy(sec_key->key, key->key, key->keylen); + + if (cipher == MCU_CIPHER_TKIP) { + /* Rx/Tx MIC keys are swapped */ + memcpy(sec_key->key + 16, key->key + 24, 8); + memcpy(sec_key->key + 24, key->key + 16, 8); + } + + /* store key_conf for BIP batch update */ + if (cipher == MCU_CIPHER_AES_CCMP) { + memcpy(sta_key_conf->key, key->key, key->keylen); + sta_key_conf->keyidx = key->keyidx; + } + + sec->n_cipher = 1; + } + } else { + sec->n_cipher = 0; + } + + return 0; +} + +int mt7996_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif, + struct mt76_connac_sta_key_conf *sta_key_conf, + struct ieee80211_key_conf *key, int mcu_cmd, + struct mt76_wcid *wcid, enum set_key_cmd cmd) +{ + struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv; + struct sk_buff *skb; + int ret; + + skb = __mt76_connac_mcu_alloc_sta_req(dev, mvif, wcid, + MT7996_STA_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + ret = mt7996_mcu_sta_key_tlv(wcid, sta_key_conf, skb, key, cmd); + if (ret) + return ret; + + return mt76_mcu_skb_send_msg(dev, skb, mcu_cmd, true); +} + +int mt7996_mcu_add_dev_info(struct mt7996_phy *phy, + struct ieee80211_vif *vif, bool enable) +{ + struct mt7996_dev *dev = phy->dev; + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct { + struct req_hdr { + u8 omac_idx; + u8 band_idx; + u8 __rsv[2]; + } __packed hdr; + struct req_tlv { + __le16 tag; + __le16 len; + u8 active; + u8 __rsv; + u8 omac_addr[ETH_ALEN]; + } __packed tlv; + } data = { + .hdr = { + .omac_idx = mvif->mt76.omac_idx, + .band_idx = mvif->mt76.band_idx, + }, + .tlv = { + .tag = cpu_to_le16(DEV_INFO_ACTIVE), + .len = cpu_to_le16(sizeof(struct req_tlv)), + .active = enable, + }, + }; + + if (mvif->mt76.omac_idx >= REPEATER_BSSID_START) + return mt7996_mcu_muar_config(phy, vif, false, enable); + + memcpy(data.tlv.omac_addr, vif->addr, ETH_ALEN); + return mt76_mcu_send_msg(&dev->mt76, MCU_WMWA_UNI_CMD(DEV_INFO_UPDATE), + &data, sizeof(data), true); +} + +static void +mt7996_mcu_beacon_cntdwn(struct ieee80211_vif *vif, struct sk_buff *rskb, + struct sk_buff *skb, + struct ieee80211_mutable_offsets *offs) +{ + struct bss_bcn_cntdwn_tlv *info; + struct tlv *tlv; + u16 tag; + + if (!offs->cntdwn_counter_offs[0]) + return; + + tag = vif->bss_conf.csa_active ? UNI_BSS_INFO_BCN_CSA : UNI_BSS_INFO_BCN_BCC; + + tlv = mt7996_mcu_add_uni_tlv(rskb, tag, sizeof(*info)); + + info = (struct bss_bcn_cntdwn_tlv *)tlv; + info->cnt = skb->data[offs->cntdwn_counter_offs[0]]; +} + +static void +mt7996_mcu_beacon_cont(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct sk_buff *rskb, struct sk_buff *skb, + struct bss_bcn_content_tlv *bcn, + struct ieee80211_mutable_offsets *offs) +{ + struct mt76_wcid *wcid = &dev->mt76.global_wcid; + u8 *buf; + + bcn->pkt_len = cpu_to_le16(MT_TXD_SIZE + skb->len); + bcn->tim_ie_pos = cpu_to_le16(offs->tim_offset); + + if (offs->cntdwn_counter_offs[0]) { + u16 offset = offs->cntdwn_counter_offs[0]; + + if (vif->bss_conf.csa_active) + bcn->csa_ie_pos = cpu_to_le16(offset - 4); + if (vif->bss_conf.color_change_active) + bcn->bcc_ie_pos = cpu_to_le16(offset - 3); + } + + buf = (u8 *)bcn + sizeof(*bcn) - MAX_BEACON_SIZE; + mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, 0, NULL, + BSS_CHANGED_BEACON); + memcpy(buf + MT_TXD_SIZE, skb->data, skb->len); +} + +static void +mt7996_mcu_beacon_check_caps(struct mt7996_phy *phy, struct ieee80211_vif *vif, + struct sk_buff *skb) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_vif_cap *vc = &mvif->cap; + const struct ieee80211_he_cap_elem *he; + const struct ieee80211_vht_cap *vht; + const struct ieee80211_ht_cap *ht; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + const u8 *ie; + u32 len, bc; + + /* Check missing configuration options to allow AP mode in mac80211 + * to remain in sync with hostapd settings, and get a subset of + * beacon and hardware capabilities. + */ + if (WARN_ON_ONCE(skb->len <= (mgmt->u.beacon.variable - skb->data))) + return; + + memset(vc, 0, sizeof(*vc)); + + len = skb->len - (mgmt->u.beacon.variable - skb->data); + + ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, mgmt->u.beacon.variable, + len); + if (ie && ie[1] >= sizeof(*ht)) { + ht = (void *)(ie + 2); + vc->ht_ldpc |= !!(le16_to_cpu(ht->cap_info) & + IEEE80211_HT_CAP_LDPC_CODING); + } + + ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, mgmt->u.beacon.variable, + len); + if (ie && ie[1] >= sizeof(*vht)) { + u32 pc = phy->mt76->sband_5g.sband.vht_cap.cap; + + vht = (void *)(ie + 2); + bc = le32_to_cpu(vht->vht_cap_info); + + vc->vht_ldpc |= !!(bc & IEEE80211_VHT_CAP_RXLDPC); + vc->vht_su_ebfer = + (bc & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE) && + (pc & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE); + vc->vht_su_ebfee = + (bc & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE) && + (pc & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE); + vc->vht_mu_ebfer = + (bc & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE) && + (pc & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE); + vc->vht_mu_ebfee = + (bc & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE) && + (pc & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE); + } + + ie = cfg80211_find_ext_ie(WLAN_EID_EXT_HE_CAPABILITY, + mgmt->u.beacon.variable, len); + if (ie && ie[1] >= sizeof(*he) + 1) { + const struct ieee80211_sta_he_cap *pc = + mt76_connac_get_he_phy_cap(phy->mt76, vif); + const struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem; + + he = (void *)(ie + 3); + + vc->he_ldpc = + HE_PHY(CAP1_LDPC_CODING_IN_PAYLOAD, pe->phy_cap_info[1]); + vc->he_su_ebfer = + HE_PHY(CAP3_SU_BEAMFORMER, he->phy_cap_info[3]) && + HE_PHY(CAP3_SU_BEAMFORMER, pe->phy_cap_info[3]); + vc->he_su_ebfee = + HE_PHY(CAP4_SU_BEAMFORMEE, he->phy_cap_info[4]) && + HE_PHY(CAP4_SU_BEAMFORMEE, pe->phy_cap_info[4]); + vc->he_mu_ebfer = + HE_PHY(CAP4_MU_BEAMFORMER, he->phy_cap_info[4]) && + HE_PHY(CAP4_MU_BEAMFORMER, pe->phy_cap_info[4]); + } +} + +int mt7996_mcu_add_beacon(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, int en) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct ieee80211_mutable_offsets offs; + struct ieee80211_tx_info *info; + struct sk_buff *skb, *rskb; + struct tlv *tlv; + struct bss_bcn_content_tlv *bcn; + + rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76, + MT7996_BEACON_UPDATE_SIZE); + if (IS_ERR(rskb)) + return PTR_ERR(rskb); + + tlv = mt7996_mcu_add_uni_tlv(rskb, + UNI_BSS_INFO_BCN_CONTENT, sizeof(*bcn)); + bcn = (struct bss_bcn_content_tlv *)tlv; + bcn->enable = en; + + if (!en) + goto out; + + skb = ieee80211_beacon_get_template(hw, vif, &offs, 0); + if (!skb) + return -EINVAL; + + if (skb->len > MAX_BEACON_SIZE - MT_TXD_SIZE) { + dev_err(dev->mt76.dev, "Bcn size limit exceed\n"); + dev_kfree_skb(skb); + return -EINVAL; + } + + info = IEEE80211_SKB_CB(skb); + info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, phy->mt76->band_idx); + + mt7996_mcu_beacon_check_caps(phy, vif, skb); + + mt7996_mcu_beacon_cont(dev, vif, rskb, skb, bcn, &offs); + /* TODO: subtag - 11v MBSSID */ + mt7996_mcu_beacon_cntdwn(vif, rskb, skb, &offs); + dev_kfree_skb(skb); +out: + return mt76_mcu_skb_send_msg(&phy->dev->mt76, rskb, + MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true); +} + +int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev, + struct ieee80211_vif *vif, u32 changed) +{ +#define OFFLOAD_TX_MODE_SU BIT(0) +#define OFFLOAD_TX_MODE_MU BIT(1) + struct ieee80211_hw *hw = mt76_hw(dev); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct cfg80211_chan_def *chandef = &mvif->phy->mt76->chandef; + enum nl80211_band band = chandef->chan->band; + struct mt76_wcid *wcid = &dev->mt76.global_wcid; + struct bss_inband_discovery_tlv *discov; + struct ieee80211_tx_info *info; + struct sk_buff *rskb, *skb = NULL; + struct tlv *tlv; + u8 *buf, interval; + + rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76, + MT7996_INBAND_FRAME_SIZE); + if (IS_ERR(rskb)) + return PTR_ERR(rskb); + + if (changed & BSS_CHANGED_FILS_DISCOVERY && + vif->bss_conf.fils_discovery.max_interval) { + interval = vif->bss_conf.fils_discovery.max_interval; + skb = ieee80211_get_fils_discovery_tmpl(hw, vif); + } else if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP && + vif->bss_conf.unsol_bcast_probe_resp_interval) { + interval = vif->bss_conf.unsol_bcast_probe_resp_interval; + skb = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, vif); + } + + if (!skb) + return -EINVAL; + + if (skb->len > MAX_INBAND_FRAME_SIZE - MT_TXD_SIZE) { + dev_err(dev->mt76.dev, "inband discovery size limit exceed\n"); + dev_kfree_skb(skb); + return -EINVAL; + } + + info = IEEE80211_SKB_CB(skb); + info->control.vif = vif; + info->band = band; + info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, phy->mt76->band_idx); + + tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_OFFLOAD, sizeof(*discov)); + + discov = (struct bss_inband_discovery_tlv *)tlv; + discov->tx_mode = OFFLOAD_TX_MODE_SU; + /* 0: UNSOL PROBE RESP, 1: FILS DISCOV */ + discov->tx_type = !!(changed & BSS_CHANGED_FILS_DISCOVERY); + discov->tx_interval = interval; + discov->prob_rsp_len = cpu_to_le16(MT_TXD_SIZE + skb->len); + discov->enable = true; + discov->wcid = cpu_to_le16(MT7996_WTBL_RESERVED); + + buf = (u8 *)tlv + sizeof(*discov) - MAX_INBAND_FRAME_SIZE; + + mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, 0, NULL, + changed); + + memcpy(buf + MT_TXD_SIZE, skb->data, skb->len); + + dev_kfree_skb(skb); + + return mt76_mcu_skb_send_msg(&dev->mt76, rskb, + MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true); +} + +static int mt7996_driver_own(struct mt7996_dev *dev, u8 band) +{ + mt76_wr(dev, MT_TOP_LPCR_HOST_BAND(band), MT_TOP_LPCR_HOST_DRV_OWN); + if (!mt76_poll_msec(dev, MT_TOP_LPCR_HOST_BAND(band), + MT_TOP_LPCR_HOST_FW_OWN_STAT, 0, 500)) { + dev_err(dev->mt76.dev, "Timeout for driver own\n"); + return -EIO; + } + + /* clear irq when the driver own success */ + mt76_wr(dev, MT_TOP_LPCR_HOST_BAND_IRQ_STAT(band), + MT_TOP_LPCR_HOST_BAND_STAT); + + return 0; +} + +static u32 mt7996_patch_sec_mode(u32 key_info) +{ + u32 sec = u32_get_bits(key_info, MT7996_PATCH_SEC), key = 0; + + if (key_info == GENMASK(31, 0) || sec == MT7996_SEC_MODE_PLAIN) + return 0; + + if (sec == MT7996_SEC_MODE_AES) + key = u32_get_bits(key_info, MT7996_PATCH_AES_KEY); + else + key = u32_get_bits(key_info, MT7996_PATCH_SCRAMBLE_KEY); + + return MT7996_SEC_ENCRYPT | MT7996_SEC_IV | + u32_encode_bits(key, MT7996_SEC_KEY_IDX); +} + +static int mt7996_load_patch(struct mt7996_dev *dev) +{ + const struct mt7996_patch_hdr *hdr; + const struct firmware *fw = NULL; + int i, ret, sem; + + sem = mt76_connac_mcu_patch_sem_ctrl(&dev->mt76, 1); + switch (sem) { + case PATCH_IS_DL: + return 0; + case PATCH_NOT_DL_SEM_SUCCESS: + break; + default: + dev_err(dev->mt76.dev, "Failed to get patch semaphore\n"); + return -EAGAIN; + } + + ret = request_firmware(&fw, MT7996_ROM_PATCH, dev->mt76.dev); + if (ret) + goto out; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) { + dev_err(dev->mt76.dev, "Invalid firmware\n"); + ret = -EINVAL; + goto out; + } + + hdr = (const struct mt7996_patch_hdr *)(fw->data); + + dev_info(dev->mt76.dev, "HW/SW Version: 0x%x, Build Time: %.16s\n", + be32_to_cpu(hdr->hw_sw_ver), hdr->build_date); + + for (i = 0; i < be32_to_cpu(hdr->desc.n_region); i++) { + struct mt7996_patch_sec *sec; + const u8 *dl; + u32 len, addr, sec_key_idx, mode = DL_MODE_NEED_RSP; + + sec = (struct mt7996_patch_sec *)(fw->data + sizeof(*hdr) + + i * sizeof(*sec)); + if ((be32_to_cpu(sec->type) & PATCH_SEC_TYPE_MASK) != + PATCH_SEC_TYPE_INFO) { + ret = -EINVAL; + goto out; + } + + addr = be32_to_cpu(sec->info.addr); + len = be32_to_cpu(sec->info.len); + sec_key_idx = be32_to_cpu(sec->info.sec_key_idx); + dl = fw->data + be32_to_cpu(sec->offs); + + mode |= mt7996_patch_sec_mode(sec_key_idx); + + ret = mt76_connac_mcu_init_download(&dev->mt76, addr, len, + mode); + if (ret) { + dev_err(dev->mt76.dev, "Download request failed\n"); + goto out; + } + + ret = __mt76_mcu_send_firmware(&dev->mt76, MCU_CMD(FW_SCATTER), + dl, len, 4096); + if (ret) { + dev_err(dev->mt76.dev, "Failed to send patch\n"); + goto out; + } + } + + ret = mt76_connac_mcu_start_patch(&dev->mt76); + if (ret) + dev_err(dev->mt76.dev, "Failed to start patch\n"); + +out: + sem = mt76_connac_mcu_patch_sem_ctrl(&dev->mt76, 0); + switch (sem) { + case PATCH_REL_SEM_SUCCESS: + break; + default: + ret = -EAGAIN; + dev_err(dev->mt76.dev, "Failed to release patch semaphore\n"); + break; + } + release_firmware(fw); + + return ret; +} + +static int +mt7996_mcu_send_ram_firmware(struct mt7996_dev *dev, + const struct mt7996_fw_trailer *hdr, + const u8 *data, bool is_wa) +{ + int i, offset = 0; + u32 override = 0, option = 0; + + for (i = 0; i < hdr->n_region; i++) { + const struct mt7996_fw_region *region; + int err; + u32 len, addr, mode; + + region = (const struct mt7996_fw_region *)((const u8 *)hdr - + (hdr->n_region - i) * sizeof(*region)); + mode = mt76_connac_mcu_gen_dl_mode(&dev->mt76, + region->feature_set, is_wa); + len = le32_to_cpu(region->len); + addr = le32_to_cpu(region->addr); + + if (region->feature_set & FW_FEATURE_OVERRIDE_ADDR) + override = addr; + + err = mt76_connac_mcu_init_download(&dev->mt76, addr, len, + mode); + if (err) { + dev_err(dev->mt76.dev, "Download request failed\n"); + return err; + } + + err = __mt76_mcu_send_firmware(&dev->mt76, MCU_CMD(FW_SCATTER), + data + offset, len, 4096); + if (err) { + dev_err(dev->mt76.dev, "Failed to send firmware.\n"); + return err; + } + + offset += len; + } + + if (override) + option |= FW_START_OVERRIDE; + + if (is_wa) + option |= FW_START_WORKING_PDA_CR4; + + return mt76_connac_mcu_start_firmware(&dev->mt76, override, option); +} + +static int mt7996_load_ram(struct mt7996_dev *dev) +{ + const struct mt7996_fw_trailer *hdr; + const struct firmware *fw; + int ret; + + ret = request_firmware(&fw, MT7996_FIRMWARE_WM, dev->mt76.dev); + if (ret) + return ret; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) { + dev_err(dev->mt76.dev, "Invalid firmware\n"); + ret = -EINVAL; + goto out; + } + + hdr = (const struct mt7996_fw_trailer *)(fw->data + fw->size - sizeof(*hdr)); + + dev_info(dev->mt76.dev, "WM Firmware Version: %.10s, Build Time: %.15s\n", + hdr->fw_ver, hdr->build_date); + + ret = mt7996_mcu_send_ram_firmware(dev, hdr, fw->data, false); + if (ret) { + dev_err(dev->mt76.dev, "Failed to start WM firmware\n"); + goto out; + } + + release_firmware(fw); + + ret = request_firmware(&fw, MT7996_FIRMWARE_WA, dev->mt76.dev); + if (ret) + return ret; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) { + dev_err(dev->mt76.dev, "Invalid firmware\n"); + ret = -EINVAL; + goto out; + } + + hdr = (const struct mt7996_fw_trailer *)(fw->data + fw->size - sizeof(*hdr)); + + dev_info(dev->mt76.dev, "WA Firmware Version: %.10s, Build Time: %.15s\n", + hdr->fw_ver, hdr->build_date); + + ret = mt7996_mcu_send_ram_firmware(dev, hdr, fw->data, true); + if (ret) { + dev_err(dev->mt76.dev, "Failed to start WA firmware\n"); + goto out; + } + + snprintf(dev->mt76.hw->wiphy->fw_version, + sizeof(dev->mt76.hw->wiphy->fw_version), + "%.10s-%.15s", hdr->fw_ver, hdr->build_date); + +out: + release_firmware(fw); + + return ret; +} + +static int +mt7996_firmware_state(struct mt7996_dev *dev, bool wa) +{ + u32 state = FIELD_PREP(MT_TOP_MISC_FW_STATE, + wa ? FW_STATE_RDY : FW_STATE_FW_DOWNLOAD); + + if (!mt76_poll_msec(dev, MT_TOP_MISC, MT_TOP_MISC_FW_STATE, + state, 1000)) { + dev_err(dev->mt76.dev, "Timeout for initializing firmware\n"); + return -EIO; + } + return 0; +} + +static int mt7996_load_firmware(struct mt7996_dev *dev) +{ + int ret; + + /* make sure fw is download state */ + if (mt7996_firmware_state(dev, false)) { + /* restart firmware once */ + __mt76_mcu_restart(&dev->mt76); + ret = mt7996_firmware_state(dev, false); + if (ret) { + dev_err(dev->mt76.dev, + "Firmware is not ready for download\n"); + return ret; + } + } + + ret = mt7996_load_patch(dev); + if (ret) + return ret; + + ret = mt7996_load_ram(dev); + if (ret) + return ret; + + ret = mt7996_firmware_state(dev, true); + if (ret) + return ret; + + mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_FWDL], false); + + dev_dbg(dev->mt76.dev, "Firmware init done\n"); + + return 0; +} + +int mt7996_mcu_fw_log_2_host(struct mt7996_dev *dev, u8 type, u8 ctrl) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + u8 ctrl; + u8 interval; + u8 _rsv2[2]; + } __packed data = { + .tag = cpu_to_le16(UNI_WSYS_CONFIG_FW_LOG_CTRL), + .len = cpu_to_le16(sizeof(data) - 4), + .ctrl = ctrl, + }; + + if (type == MCU_FW_LOG_WA) + return mt76_mcu_send_msg(&dev->mt76, MCU_WA_UNI_CMD(WSYS_CONFIG), + &data, sizeof(data), true); + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(WSYS_CONFIG), &data, + sizeof(data), true); +} + +int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + __le32 module_idx; + u8 level; + u8 _rsv2[3]; + } data = { + .tag = cpu_to_le16(UNI_WSYS_CONFIG_FW_DBG_CTRL), + .len = cpu_to_le16(sizeof(data) - 4), + .module_idx = cpu_to_le32(module), + .level = level, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(WSYS_CONFIG), &data, + sizeof(data), false); +} + +static int mt7996_mcu_set_mwds(struct mt7996_dev *dev, bool enabled) +{ + struct { + u8 enable; + u8 _rsv[3]; + } __packed req = { + .enable = enabled + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WA_EXT_CMD(MWDS_SUPPORT), &req, + sizeof(req), false); +} + +static void mt7996_add_rx_airtime_tlv(struct sk_buff *skb, u8 band_idx) +{ + struct vow_rx_airtime *req; + struct tlv *tlv; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_VOW_RX_AT_AIRTIME_CLR_EN, sizeof(*req)); + req = (struct vow_rx_airtime *)tlv; + req->enable = true; + req->band = band_idx; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_VOW_RX_AT_AIRTIME_EN, sizeof(*req)); + req = (struct vow_rx_airtime *)tlv; + req->enable = true; + req->band = band_idx; +} + +static int +mt7996_mcu_init_rx_airtime(struct mt7996_dev *dev) +{ + struct uni_header hdr = {}; + struct sk_buff *skb; + int len, num; + + num = 2 + 2 * (dev->dbdc_support + dev->tbtc_support); + len = sizeof(hdr) + num * sizeof(struct vow_rx_airtime); + skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, &hdr, sizeof(hdr)); + + mt7996_add_rx_airtime_tlv(skb, dev->mt76.phy.band_idx); + + if (dev->dbdc_support) + mt7996_add_rx_airtime_tlv(skb, MT_BAND1); + + if (dev->tbtc_support) + mt7996_add_rx_airtime_tlv(skb, MT_BAND2); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WM_UNI_CMD(VOW), true); +} + +static int +mt7996_mcu_restart(struct mt76_dev *dev) +{ + struct { + u8 __rsv1[4]; + + __le16 tag; + __le16 len; + u8 power_mode; + u8 __rsv2[3]; + } __packed req = { + .tag = cpu_to_le16(UNI_POWER_OFF), + .len = cpu_to_le16(sizeof(req) - 4), + .power_mode = 1, + }; + + return mt76_mcu_send_msg(dev, MCU_WM_UNI_CMD(POWER_CREL), &req, + sizeof(req), false); +} + +int mt7996_mcu_init(struct mt7996_dev *dev) +{ + static const struct mt76_mcu_ops mt7996_mcu_ops = { + .headroom = sizeof(struct mt76_connac2_mcu_txd), /* reuse */ + .mcu_skb_send_msg = mt7996_mcu_send_message, + .mcu_parse_response = mt7996_mcu_parse_response, + .mcu_restart = mt7996_mcu_restart, + }; + int ret; + + dev->mt76.mcu_ops = &mt7996_mcu_ops; + + /* force firmware operation mode into normal state, + * which should be set before firmware download stage. + */ + mt76_wr(dev, MT_SWDEF_MODE, MT_SWDEF_NORMAL_MODE); + + ret = mt7996_driver_own(dev, 0); + if (ret) + return ret; + /* set driver own for band1 when two hif exist */ + if (dev->hif2) { + ret = mt7996_driver_own(dev, 1); + if (ret) + return ret; + } + + ret = mt7996_load_firmware(dev); + if (ret) + return ret; + + set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); + ret = mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WM, 0); + if (ret) + return ret; + + ret = mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WA, 0); + if (ret) + return ret; + + ret = mt7996_mcu_set_mwds(dev, 1); + if (ret) + return ret; + + ret = mt7996_mcu_init_rx_airtime(dev); + if (ret) + return ret; + + return mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET), + MCU_WA_PARAM_RED, 0, 0); +} + +void mt7996_mcu_exit(struct mt7996_dev *dev) +{ + __mt76_mcu_restart(&dev->mt76); + if (mt7996_firmware_state(dev, false)) { + dev_err(dev->mt76.dev, "Failed to exit mcu\n"); + return; + } + + mt76_wr(dev, MT_TOP_LPCR_HOST_BAND(0), MT_TOP_LPCR_HOST_FW_OWN); + if (dev->hif2) + mt76_wr(dev, MT_TOP_LPCR_HOST_BAND(1), + MT_TOP_LPCR_HOST_FW_OWN); + skb_queue_purge(&dev->mt76.mcu.res_q); +} + +int mt7996_mcu_set_hdr_trans(struct mt7996_dev *dev, bool hdr_trans) +{ + struct { + u8 __rsv[4]; + } __packed hdr; + struct hdr_trans_blacklist *req_blacklist; + struct hdr_trans_en *req_en; + struct sk_buff *skb; + struct tlv *tlv; + int len = MT7996_HDR_TRANS_MAX_SIZE + sizeof(hdr); + + skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, &hdr, sizeof(hdr)); + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_HDR_TRANS_EN, sizeof(*req_en)); + req_en = (struct hdr_trans_en *)tlv; + req_en->enable = hdr_trans; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_HDR_TRANS_VLAN, + sizeof(struct hdr_trans_vlan)); + + if (hdr_trans) { + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_HDR_TRANS_BLACKLIST, + sizeof(*req_blacklist)); + req_blacklist = (struct hdr_trans_blacklist *)tlv; + req_blacklist->enable = 1; + req_blacklist->type = cpu_to_le16(ETH_P_PAE); + } + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WM_UNI_CMD(RX_HDR_TRANS), true); +} + +int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif) +{ +#define MCU_EDCA_AC_PARAM 0 +#define WMM_AIFS_SET BIT(0) +#define WMM_CW_MIN_SET BIT(1) +#define WMM_CW_MAX_SET BIT(2) +#define WMM_TXOP_SET BIT(3) +#define WMM_PARAM_SET (WMM_AIFS_SET | WMM_CW_MIN_SET | \ + WMM_CW_MAX_SET | WMM_TXOP_SET) + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct { + u8 bss_idx; + u8 __rsv[3]; + } __packed hdr = { + .bss_idx = mvif->mt76.idx, + }; + struct sk_buff *skb; + int len = sizeof(hdr) + IEEE80211_NUM_ACS * sizeof(struct edca); + int ac; + + skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, &hdr, sizeof(hdr)); + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + struct ieee80211_tx_queue_params *q = &mvif->queue_params[ac]; + struct edca *e; + struct tlv *tlv; + + tlv = mt7996_mcu_add_uni_tlv(skb, MCU_EDCA_AC_PARAM, sizeof(*e)); + + e = (struct edca *)tlv; + e->set = WMM_PARAM_SET; + e->queue = ac + mvif->mt76.wmm_idx * MT7996_MAX_WMM_SETS; + e->aifs = q->aifs; + e->txop = cpu_to_le16(q->txop); + + if (q->cw_min) + e->cw_min = fls(q->cw_min); + else + e->cw_min = 5; + + if (q->cw_max) + e->cw_max = fls(q->cw_max); + else + e->cw_max = 10; + } + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WM_UNI_CMD(EDCA_UPDATE), true); +} + +int mt7996_mcu_set_fcc5_lpn(struct mt7996_dev *dev, int val) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + + __le32 ctrl; + __le16 min_lpn; + u8 rsv[2]; + } __packed req = { + .tag = cpu_to_le16(UNI_RDD_CTRL_SET_TH), + .len = cpu_to_le16(sizeof(req) - 4), + + .ctrl = cpu_to_le32(0x1), + .min_lpn = cpu_to_le16(val), + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RDD_CTRL), + &req, sizeof(req), true); +} + +int mt7996_mcu_set_pulse_th(struct mt7996_dev *dev, + const struct mt7996_dfs_pulse *pulse) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + + __le32 ctrl; + + __le32 max_width; /* us */ + __le32 max_pwr; /* dbm */ + __le32 min_pwr; /* dbm */ + __le32 min_stgr_pri; /* us */ + __le32 max_stgr_pri; /* us */ + __le32 min_cr_pri; /* us */ + __le32 max_cr_pri; /* us */ + } __packed req = { + .tag = cpu_to_le16(UNI_RDD_CTRL_SET_TH), + .len = cpu_to_le16(sizeof(req) - 4), + + .ctrl = cpu_to_le32(0x3), + +#define __req_field(field) .field = cpu_to_le32(pulse->field) + __req_field(max_width), + __req_field(max_pwr), + __req_field(min_pwr), + __req_field(min_stgr_pri), + __req_field(max_stgr_pri), + __req_field(min_cr_pri), + __req_field(max_cr_pri), +#undef __req_field + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RDD_CTRL), + &req, sizeof(req), true); +} + +int mt7996_mcu_set_radar_th(struct mt7996_dev *dev, int index, + const struct mt7996_dfs_pattern *pattern) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + + __le32 ctrl; + __le16 radar_type; + + u8 enb; + u8 stgr; + u8 min_crpn; + u8 max_crpn; + u8 min_crpr; + u8 min_pw; + __le32 min_pri; + __le32 max_pri; + u8 max_pw; + u8 min_crbn; + u8 max_crbn; + u8 min_stgpn; + u8 max_stgpn; + u8 min_stgpr; + u8 rsv[2]; + __le32 min_stgpr_diff; + } __packed req = { + .tag = cpu_to_le16(UNI_RDD_CTRL_SET_TH), + .len = cpu_to_le16(sizeof(req) - 4), + + .ctrl = cpu_to_le32(0x2), + .radar_type = cpu_to_le16(index), + +#define __req_field_u8(field) .field = pattern->field +#define __req_field_u32(field) .field = cpu_to_le32(pattern->field) + __req_field_u8(enb), + __req_field_u8(stgr), + __req_field_u8(min_crpn), + __req_field_u8(max_crpn), + __req_field_u8(min_crpr), + __req_field_u8(min_pw), + __req_field_u32(min_pri), + __req_field_u32(max_pri), + __req_field_u8(max_pw), + __req_field_u8(min_crbn), + __req_field_u8(max_crbn), + __req_field_u8(min_stgpn), + __req_field_u8(max_stgpn), + __req_field_u8(min_stgpr), + __req_field_u32(min_stgpr_diff), +#undef __req_field_u8 +#undef __req_field_u32 + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RDD_CTRL), + &req, sizeof(req), true); +} + +static int +mt7996_mcu_background_chain_ctrl(struct mt7996_phy *phy, + struct cfg80211_chan_def *chandef, + int cmd) +{ + struct mt7996_dev *dev = phy->dev; + struct mt76_phy *mphy = phy->mt76; + struct ieee80211_channel *chan = mphy->chandef.chan; + int freq = mphy->chandef.center_freq1; + struct mt7996_mcu_background_chain_ctrl req = { + .tag = cpu_to_le16(0), + .len = cpu_to_le16(sizeof(req) - 4), + .monitor_scan_type = 2, /* simple rx */ + }; + + if (!chandef && cmd != CH_SWITCH_BACKGROUND_SCAN_STOP) + return -EINVAL; + + if (!cfg80211_chandef_valid(&mphy->chandef)) + return -EINVAL; + + switch (cmd) { + case CH_SWITCH_BACKGROUND_SCAN_START: { + req.chan = chan->hw_value; + req.central_chan = ieee80211_frequency_to_channel(freq); + req.bw = mt76_connac_chan_bw(&mphy->chandef); + req.monitor_chan = chandef->chan->hw_value; + req.monitor_central_chan = + ieee80211_frequency_to_channel(chandef->center_freq1); + req.monitor_bw = mt76_connac_chan_bw(chandef); + req.band_idx = phy->mt76->band_idx; + req.scan_mode = 1; + break; + } + case CH_SWITCH_BACKGROUND_SCAN_RUNNING: + req.monitor_chan = chandef->chan->hw_value; + req.monitor_central_chan = + ieee80211_frequency_to_channel(chandef->center_freq1); + req.band_idx = phy->mt76->band_idx; + req.scan_mode = 2; + break; + case CH_SWITCH_BACKGROUND_SCAN_STOP: + req.chan = chan->hw_value; + req.central_chan = ieee80211_frequency_to_channel(freq); + req.bw = mt76_connac_chan_bw(&mphy->chandef); + req.tx_stream = hweight8(mphy->antenna_mask); + req.rx_stream = mphy->antenna_mask; + break; + default: + return -EINVAL; + } + req.band = chandef ? chandef->chan->band == NL80211_BAND_5GHZ : 1; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(OFFCH_SCAN_CTRL), + &req, sizeof(req), false); +} + +int mt7996_mcu_rdd_background_enable(struct mt7996_phy *phy, + struct cfg80211_chan_def *chandef) +{ + struct mt7996_dev *dev = phy->dev; + int err, region; + + if (!chandef) { /* disable offchain */ + err = mt7996_mcu_rdd_cmd(dev, RDD_STOP, MT_RX_SEL2, + 0, 0); + if (err) + return err; + + return mt7996_mcu_background_chain_ctrl(phy, NULL, + CH_SWITCH_BACKGROUND_SCAN_STOP); + } + + err = mt7996_mcu_background_chain_ctrl(phy, chandef, + CH_SWITCH_BACKGROUND_SCAN_START); + if (err) + return err; + + switch (dev->mt76.region) { + case NL80211_DFS_ETSI: + region = 0; + break; + case NL80211_DFS_JP: + region = 2; + break; + case NL80211_DFS_FCC: + default: + region = 1; + break; + } + + return mt7996_mcu_rdd_cmd(dev, RDD_START, MT_RX_SEL2, + 0, region); +} + +int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag) +{ + static const u8 ch_band[] = { + [NL80211_BAND_2GHZ] = 0, + [NL80211_BAND_5GHZ] = 1, + [NL80211_BAND_6GHZ] = 2, + }; + struct mt7996_dev *dev = phy->dev; + struct cfg80211_chan_def *chandef = &phy->mt76->chandef; + int freq1 = chandef->center_freq1; + u8 band_idx = phy->mt76->band_idx; + struct { + /* fixed field */ + u8 __rsv[4]; + + __le16 tag; + __le16 len; + u8 control_ch; + u8 center_ch; + u8 bw; + u8 tx_path_num; + u8 rx_path; /* mask or num */ + u8 switch_reason; + u8 band_idx; + u8 center_ch2; /* for 80+80 only */ + __le16 cac_case; + u8 channel_band; + u8 rsv0; + __le32 outband_freq; + u8 txpower_drop; + u8 ap_bw; + u8 ap_center_ch; + u8 rsv1[53]; + } __packed req = { + .tag = cpu_to_le16(tag), + .len = cpu_to_le16(sizeof(req) - 4), + .control_ch = chandef->chan->hw_value, + .center_ch = ieee80211_frequency_to_channel(freq1), + .bw = mt76_connac_chan_bw(chandef), + .tx_path_num = hweight16(phy->mt76->chainmask), + .rx_path = phy->mt76->chainmask >> dev->chainshift[band_idx], + .band_idx = band_idx, + .channel_band = ch_band[chandef->chan->band], + }; + + if (tag == UNI_CHANNEL_RX_PATH || + dev->mt76.hw->conf.flags & IEEE80211_CONF_MONITOR) + req.switch_reason = CH_SWITCH_NORMAL; + else if (phy->mt76->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) + req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD; + else if (!cfg80211_reg_can_beacon(phy->mt76->hw->wiphy, chandef, + NL80211_IFTYPE_AP)) + req.switch_reason = CH_SWITCH_DFS; + else + req.switch_reason = CH_SWITCH_NORMAL; + + if (tag == UNI_CHANNEL_SWITCH) + req.rx_path = hweight8(req.rx_path); + + if (chandef->width == NL80211_CHAN_WIDTH_80P80) { + int freq2 = chandef->center_freq2; + + req.center_ch2 = ieee80211_frequency_to_channel(freq2); + } + + return mt76_mcu_send_msg(&dev->mt76, MCU_WMWA_UNI_CMD(CHANNEL_SWITCH), + &req, sizeof(req), true); +} + +static int mt7996_mcu_set_eeprom_flash(struct mt7996_dev *dev) +{ +#define MAX_PAGE_IDX_MASK GENMASK(7, 5) +#define PAGE_IDX_MASK GENMASK(4, 2) +#define PER_PAGE_SIZE 0x400 + struct mt7996_mcu_eeprom req = { + .tag = cpu_to_le16(UNI_EFUSE_BUFFER_MODE), + .buffer_mode = EE_MODE_BUFFER + }; + u16 eeprom_size = MT7996_EEPROM_SIZE; + u8 total = DIV_ROUND_UP(eeprom_size, PER_PAGE_SIZE); + u8 *eep = (u8 *)dev->mt76.eeprom.data; + int eep_len, i; + + for (i = 0; i < total; i++, eep += eep_len) { + struct sk_buff *skb; + int ret, msg_len; + + if (i == total - 1 && !!(eeprom_size % PER_PAGE_SIZE)) + eep_len = eeprom_size % PER_PAGE_SIZE; + else + eep_len = PER_PAGE_SIZE; + + msg_len = sizeof(req) + eep_len; + skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, msg_len); + if (!skb) + return -ENOMEM; + + req.len = cpu_to_le16(msg_len - 4); + req.format = FIELD_PREP(MAX_PAGE_IDX_MASK, total - 1) | + FIELD_PREP(PAGE_IDX_MASK, i) | EE_FORMAT_WHOLE; + req.buf_len = cpu_to_le16(eep_len); + + skb_put_data(skb, &req, sizeof(req)); + skb_put_data(skb, eep, eep_len); + + ret = mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WM_UNI_CMD(EFUSE_CTRL), true); + if (ret) + return ret; + } + + return 0; +} + +int mt7996_mcu_set_eeprom(struct mt7996_dev *dev) +{ + struct mt7996_mcu_eeprom req = { + .tag = cpu_to_le16(UNI_EFUSE_BUFFER_MODE), + .len = cpu_to_le16(sizeof(req) - 4), + .buffer_mode = EE_MODE_EFUSE, + .format = EE_FORMAT_WHOLE + }; + + if (dev->flash_mode) + return mt7996_mcu_set_eeprom_flash(dev); + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(EFUSE_CTRL), + &req, sizeof(req), true); +} + +int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + __le32 addr; + __le32 valid; + u8 data[16]; + } __packed req = { + .tag = cpu_to_le16(UNI_EFUSE_ACCESS), + .len = cpu_to_le16(sizeof(req) - 4), + .addr = cpu_to_le32(round_down(offset, + MT7996_EEPROM_BLOCK_SIZE)), + }; + struct sk_buff *skb; + bool valid; + int ret; + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(EFUSE_CTRL), &req, + sizeof(req), true, &skb); + if (ret) + return ret; + + valid = le32_to_cpu(*(__le32 *)(skb->data + 16)); + if (valid) { + u32 addr = le32_to_cpu(*(__le32 *)(skb->data + 12)); + u8 *buf = (u8 *)dev->mt76.eeprom.data + addr; + + skb_pull(skb, 64); + memcpy(buf, skb->data, MT7996_EEPROM_BLOCK_SIZE); + } + + dev_kfree_skb(skb); + + return 0; +} + +int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + u8 num; + u8 version; + u8 die_idx; + u8 _rsv2; + } __packed req = { + .tag = cpu_to_le16(UNI_EFUSE_FREE_BLOCK), + .len = cpu_to_le16(sizeof(req) - 4), + .version = 2, + }; + struct sk_buff *skb; + int ret; + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(EFUSE_CTRL), &req, + sizeof(req), true, &skb); + if (ret) + return ret; + + *block_num = *(u8 *)(skb->data + 8); + dev_kfree_skb(skb); + + return 0; +} + +int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch) +{ + struct { + struct { + u8 band; + u8 __rsv[3]; + } hdr; + struct { + __le16 tag; + __le16 len; + __le32 offs; + } data[4]; + } __packed req = { + .hdr.band = phy->mt76->band_idx, + }; + /* strict order */ + static const u32 offs[] = { + UNI_MIB_TX_TIME, + UNI_MIB_RX_TIME, + UNI_MIB_OBSS_AIRTIME, + UNI_MIB_NON_WIFI_TIME, + }; + struct mt76_channel_state *state = phy->mt76->chan_state; + struct mt76_channel_state *state_ts = &phy->state_ts; + struct mt7996_dev *dev = phy->dev; + struct mt7996_mcu_mib *res; + struct sk_buff *skb; + int i, ret; + + for (i = 0; i < 4; i++) { + req.data[i].tag = cpu_to_le16(UNI_CMD_MIB_DATA); + req.data[i].len = cpu_to_le16(sizeof(req.data[i])); + req.data[i].offs = cpu_to_le32(offs[i]); + } + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(GET_MIB_INFO), + &req, sizeof(req), true, &skb); + if (ret) + return ret; + + skb_pull(skb, sizeof(req.hdr)); + + res = (struct mt7996_mcu_mib *)(skb->data); + + if (chan_switch) + goto out; + +#define __res_u64(s) le64_to_cpu(res[s].data) + state->cc_tx += __res_u64(1) - state_ts->cc_tx; + state->cc_bss_rx += __res_u64(2) - state_ts->cc_bss_rx; + state->cc_rx += __res_u64(2) + __res_u64(3) - state_ts->cc_rx; + state->cc_busy += __res_u64(0) + __res_u64(1) + __res_u64(2) + __res_u64(3) - + state_ts->cc_busy; + +out: + state_ts->cc_tx = __res_u64(1); + state_ts->cc_bss_rx = __res_u64(2); + state_ts->cc_rx = __res_u64(2) + __res_u64(3); + state_ts->cc_busy = __res_u64(0) + __res_u64(1) + __res_u64(2) + __res_u64(3); +#undef __res_u64 + + dev_kfree_skb(skb); + + return 0; +} + +int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 val, u8 band) +{ + struct { + u8 rsv[4]; + + __le16 tag; + __le16 len; + + union { + struct { + __le32 mask; + } __packed set; + + struct { + u8 method; + u8 band; + u8 rsv2[2]; + } __packed trigger; + }; + } __packed req = { + .tag = cpu_to_le16(action), + .len = cpu_to_le16(sizeof(req) - 4), + }; + + switch (action) { + case UNI_CMD_SER_SET: + req.set.mask = cpu_to_le32(val); + break; + case UNI_CMD_SER_TRIGGER: + req.trigger.method = val; + req.trigger.band = band; + break; + default: + return -EINVAL; + } + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SER), + &req, sizeof(req), false); +} + +int mt7996_mcu_set_txbf(struct mt7996_dev *dev, u8 action) +{ +#define MT7996_BF_MAX_SIZE sizeof(union bf_tag_tlv) +#define BF_PROCESSING 4 + struct uni_header hdr; + struct sk_buff *skb; + struct tlv *tlv; + int len = sizeof(hdr) + MT7996_BF_MAX_SIZE; + + memset(&hdr, 0, sizeof(hdr)); + + skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, &hdr, sizeof(hdr)); + + switch (action) { + case BF_SOUNDING_ON: { + struct bf_sounding_on *req_snd_on; + + tlv = mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req_snd_on)); + req_snd_on = (struct bf_sounding_on *)tlv; + req_snd_on->snd_mode = BF_PROCESSING; + break; + } + case BF_HW_EN_UPDATE: { + struct bf_hw_en_status_update *req_hw_en; + + tlv = mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req_hw_en)); + req_hw_en = (struct bf_hw_en_status_update *)tlv; + req_hw_en->ebf = true; + req_hw_en->ibf = dev->ibf; + break; + } + case BF_MOD_EN_CTRL: { + struct bf_mod_en_ctrl *req_mod_en; + + tlv = mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req_mod_en)); + req_mod_en = (struct bf_mod_en_ctrl *)tlv; + req_mod_en->bf_num = 2; + req_mod_en->bf_bitmap = GENMASK(0, 0); + break; + } + default: + return -EINVAL; + } + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(BF), true); +} + +int mt7996_mcu_add_obss_spr(struct mt7996_dev *dev, struct ieee80211_vif *vif, + bool enable) +{ +#define MT_SPR_ENABLE 1 + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct { + u8 band_idx; + u8 __rsv[3]; + + __le16 tag; + __le16 len; + __le32 val; + } __packed req = { + .band_idx = mvif->mt76.band_idx, + .tag = cpu_to_le16(UNI_CMD_SR_ENABLE), + .len = cpu_to_le16(sizeof(req) - 4), + .val = cpu_to_le32(enable), + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SR), + &req, sizeof(req), true); +} + +int mt7996_mcu_update_bss_color(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct cfg80211_he_bss_color *he_bss_color) +{ + int len = sizeof(struct bss_req_hdr) + sizeof(struct bss_color_tlv); + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct bss_color_tlv *bss_color; + struct sk_buff *skb; + struct tlv *tlv; + + skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76, len); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_BSS_COLOR, + sizeof(*bss_color)); + bss_color = (struct bss_color_tlv *)tlv; + bss_color->enable = he_bss_color->enabled; + bss_color->color = he_bss_color->color; + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true); +} + +#define TWT_AGRT_TRIGGER BIT(0) +#define TWT_AGRT_ANNOUNCE BIT(1) +#define TWT_AGRT_PROTECT BIT(2) + +int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev, + struct mt7996_vif *mvif, + struct mt7996_twt_flow *flow, + int cmd) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + u8 tbl_idx; + u8 cmd; + u8 own_mac_idx; + u8 flowid; /* 0xff for group id */ + __le16 peer_id; /* specify the peer_id (msb=0) + * or group_id (msb=1) + */ + u8 duration; /* 256 us */ + u8 bss_idx; + __le64 start_tsf; + __le16 mantissa; + u8 exponent; + u8 is_ap; + u8 agrt_params; + u8 __rsv2[135]; + } __packed req = { + .tag = cpu_to_le16(UNI_CMD_TWT_ARGT_UPDATE), + .len = cpu_to_le16(sizeof(req) - 4), + .tbl_idx = flow->table_id, + .cmd = cmd, + .own_mac_idx = mvif->mt76.omac_idx, + .flowid = flow->id, + .peer_id = cpu_to_le16(flow->wcid), + .duration = flow->duration, + .bss_idx = mvif->mt76.idx, + .start_tsf = cpu_to_le64(flow->tsf), + .mantissa = flow->mantissa, + .exponent = flow->exp, + .is_ap = true, + }; + + if (flow->protection) + req.agrt_params |= TWT_AGRT_PROTECT; + if (!flow->flowtype) + req.agrt_params |= TWT_AGRT_ANNOUNCE; + if (flow->trigger) + req.agrt_params |= TWT_AGRT_TRIGGER; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TWT), + &req, sizeof(req), true); +} + +void mt7996_mcu_set_pm(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ +#define EXIT_PM_STATE 0 +#define ENTER_PM_STATE 1 + struct ieee80211_hw *hw = priv; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_hw_phy(hw); + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct bss_power_save *ps; + struct sk_buff *skb; + struct tlv *tlv; + bool running = test_bit(MT76_STATE_RUNNING, &phy->mt76->state); + + skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76, + MT7996_BSS_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_PS, sizeof(*ps)); + ps = (struct bss_power_save *)tlv; + ps->profile = running ? EXIT_PM_STATE : ENTER_PM_STATE; + + mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true); +} + +int mt7996_mcu_set_rts_thresh(struct mt7996_phy *phy, u32 val) +{ + struct { + u8 band_idx; + u8 _rsv[3]; + + __le16 tag; + __le16 len; + __le32 len_thresh; + __le32 pkt_thresh; + } __packed req = { + .band_idx = phy->mt76->band_idx, + .tag = cpu_to_le16(UNI_BAND_CONFIG_RTS_THRESHOLD), + .len = cpu_to_le16(sizeof(req) - 4), + .len_thresh = cpu_to_le32(val), + .pkt_thresh = cpu_to_le32(0x2), + }; + + return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG), + &req, sizeof(req), true); +} + +int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable) +{ + struct { + u8 band_idx; + u8 _rsv[3]; + + __le16 tag; + __le16 len; + u8 enable; + u8 _rsv2[3]; + } __packed req = { + .band_idx = phy->mt76->band_idx, + .tag = cpu_to_le16(UNI_BAND_CONFIG_RADIO_ENABLE), + .len = cpu_to_le16(sizeof(req) - 4), + .enable = enable, + }; + + return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG), + &req, sizeof(req), true); +} + +int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index, + u8 rx_sel, u8 val) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + + u8 ctrl; + u8 rdd_idx; + u8 rdd_rx_sel; + u8 val; + u8 rsv[4]; + } __packed req = { + .tag = cpu_to_le16(UNI_RDD_CTRL_PARM), + .len = cpu_to_le16(sizeof(req) - 4), + .ctrl = cmd, + .rdd_idx = index, + .rdd_rx_sel = rx_sel, + .val = val, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RDD_CTRL), + &req, sizeof(req), true); +} + +int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_sta *msta; + struct sk_buff *skb; + + msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->sta; + + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, + &msta->wcid, + MT7996_STA_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + /* starec hdr trans */ + mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, sta); + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); +} + +int mt7996_mcu_rf_regval(struct mt7996_dev *dev, u32 regidx, u32 *val, bool set) +{ + struct { + u8 __rsv1[4]; + + __le16 tag; + __le16 len; + __le16 idx; + u8 __rsv2[2]; + __le32 ofs; + __le32 data; + } __packed *res, req = { + .tag = cpu_to_le16(UNI_CMD_ACCESS_RF_REG_BASIC), + .len = cpu_to_le16(sizeof(req) - 4), + + .idx = cpu_to_le16(u32_get_bits(regidx, GENMASK(31, 24))), + .ofs = cpu_to_le32(u32_get_bits(regidx, GENMASK(23, 0))), + .data = set ? cpu_to_le32(*val) : 0, + }; + struct sk_buff *skb; + int ret; + + if (set) + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(REG_ACCESS), + &req, sizeof(req), true); + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, + MCU_WM_UNI_CMD_QUERY(REG_ACCESS), + &req, sizeof(req), true, &skb); + if (ret) + return ret; + + res = (void *)skb->data; + *val = le32_to_cpu(res->data); + dev_kfree_skb(skb); + + return 0; +} + +int mt7996_mcu_set_rro(struct mt7996_dev *dev, u16 tag, u8 val) +{ + struct { + u8 __rsv1[4]; + + __le16 tag; + __le16 len; + + union { + struct { + u8 type; + u8 __rsv2[3]; + } __packed platform_type; + struct { + u8 type; + u8 dest; + u8 __rsv2[2]; + } __packed bypass_mode; + struct { + u8 path; + u8 __rsv2[3]; + } __packed txfree_path; + }; + } __packed req = { + .tag = cpu_to_le16(tag), + .len = cpu_to_le16(sizeof(req) - 4), + }; + + switch (tag) { + case UNI_RRO_SET_PLATFORM_TYPE: + req.platform_type.type = val; + break; + case UNI_RRO_SET_BYPASS_MODE: + req.bypass_mode.type = val; + break; + case UNI_RRO_SET_TXFREE_PATH: + req.txfree_path.path = val; + break; + default: + return -EINVAL; + } + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RRO), &req, + sizeof(req), true); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h new file mode 100644 index 000000000000..ff12a7168bd8 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h @@ -0,0 +1,662 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#ifndef __MT7996_MCU_H +#define __MT7996_MCU_H + +#include "../mt76_connac_mcu.h" + +struct mt7996_mcu_rxd { + __le32 rxd[8]; + + __le16 len; + __le16 pkt_type_id; + + u8 eid; + u8 seq; + u8 option; + u8 __rsv; + + u8 ext_eid; + u8 __rsv1[2]; + u8 s2d_index; +}; + +struct mt7996_mcu_uni_event { + u8 cid; + u8 __rsv[3]; + __le32 status; /* 0: success, others: fail */ +} __packed; + +struct mt7996_mcu_csa_notify { + struct mt7996_mcu_rxd rxd; + + u8 omac_idx; + u8 csa_count; + u8 band_idx; + u8 rsv; +} __packed; + +struct mt7996_mcu_rdd_report { + struct mt7996_mcu_rxd rxd; + + u8 __rsv1[4]; + + __le16 tag; + __le16 len; + + u8 band_idx; + u8 long_detected; + u8 constant_prf_detected; + u8 staggered_prf_detected; + u8 radar_type_idx; + u8 periodic_pulse_num; + u8 long_pulse_num; + u8 hw_pulse_num; + + u8 out_lpn; + u8 out_spn; + u8 out_crpn; + u8 out_crpw; + u8 out_crbn; + u8 out_stgpn; + u8 out_stgpw; + + u8 __rsv2; + + __le32 out_pri_const; + __le32 out_pri_stg[3]; + __le32 out_pri_stg_dmin; + + struct { + __le32 start; + __le16 pulse_width; + __le16 pulse_power; + u8 mdrdy_flag; + u8 rsv[3]; + } long_pulse[32]; + + struct { + __le32 start; + __le16 pulse_width; + __le16 pulse_power; + u8 mdrdy_flag; + u8 rsv[3]; + } periodic_pulse[32]; + + struct { + __le32 start; + __le16 pulse_width; + __le16 pulse_power; + u8 sc_pass; + u8 sw_reset; + u8 mdrdy_flag; + u8 tx_active; + } hw_pulse[32]; +} __packed; + +struct mt7996_mcu_background_chain_ctrl { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + + u8 chan; /* primary channel */ + u8 central_chan; /* central channel */ + u8 bw; + u8 tx_stream; + u8 rx_stream; + + u8 monitor_chan; /* monitor channel */ + u8 monitor_central_chan;/* monitor central channel */ + u8 monitor_bw; + u8 monitor_tx_stream; + u8 monitor_rx_stream; + + u8 scan_mode; /* 0: ScanStop + * 1: ScanStart + * 2: ScanRunning + */ + u8 band_idx; /* DBDC */ + u8 monitor_scan_type; + u8 band; /* 0: 2.4GHz, 1: 5GHz */ + u8 rsv[2]; +} __packed; + +struct mt7996_mcu_eeprom { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + u8 buffer_mode; + u8 format; + __le16 buf_len; +} __packed; + +struct mt7996_mcu_phy_rx_info { + u8 category; + u8 rate; + u8 mode; + u8 nsts; + u8 gi; + u8 coding; + u8 stbc; + u8 bw; +}; + +struct mt7996_mcu_mib { + __le16 tag; + __le16 len; + __le32 offs; + __le64 data; +} __packed; + +enum mt7996_chan_mib_offs { + UNI_MIB_OBSS_AIRTIME = 26, + UNI_MIB_NON_WIFI_TIME = 27, + UNI_MIB_TX_TIME = 28, + UNI_MIB_RX_TIME = 29 +}; + +struct edca { + __le16 tag; + __le16 len; + + u8 queue; + u8 set; + u8 cw_min; + u8 cw_max; + __le16 txop; + u8 aifs; + u8 __rsv; +}; + +#define MCU_PQ_ID(p, q) (((p) << 15) | ((q) << 10)) +#define MCU_PKT_ID 0xa0 + +enum { + MCU_FW_LOG_WM, + MCU_FW_LOG_WA, + MCU_FW_LOG_TO_HOST, + MCU_FW_LOG_RELAY = 16 +}; + +enum { + MCU_TWT_AGRT_ADD, + MCU_TWT_AGRT_MODIFY, + MCU_TWT_AGRT_DELETE, + MCU_TWT_AGRT_TEARDOWN, + MCU_TWT_AGRT_GET_TSF, +}; + +enum { + MCU_WA_PARAM_CMD_QUERY, + MCU_WA_PARAM_CMD_SET, + MCU_WA_PARAM_CMD_CAPABILITY, + MCU_WA_PARAM_CMD_DEBUG, +}; + +enum { + MCU_WA_PARAM_PDMA_RX = 0x04, + MCU_WA_PARAM_CPU_UTIL = 0x0b, + MCU_WA_PARAM_RED = 0x0e, + MCU_WA_PARAM_HW_PATH_HIF_VER = 0x2f, +}; + +enum mcu_mmps_mode { + MCU_MMPS_STATIC, + MCU_MMPS_DYNAMIC, + MCU_MMPS_RSV, + MCU_MMPS_DISABLE, +}; + +struct bss_rate_tlv { + __le16 tag; + __le16 len; + u8 __rsv1[4]; + __le16 bc_trans; + __le16 mc_trans; + u8 short_preamble; + u8 bc_fixed_rate; + u8 mc_fixed_rate; + u8 __rsv2[1]; +} __packed; + +struct bss_ra_tlv { + __le16 tag; + __le16 len; + u8 short_preamble; + u8 force_sgi; + u8 force_gf; + u8 ht_mode; + u8 se_off; + u8 antenna_idx; + __le16 max_phyrate; + u8 force_tx_streams; + u8 __rsv[3]; +} __packed; + +struct bss_rlm_tlv { + __le16 tag; + __le16 len; + u8 control_channel; + u8 center_chan; + u8 center_chan2; + u8 bw; + u8 tx_streams; + u8 rx_streams; + u8 ht_op_info; + u8 sco; + u8 band; + u8 __rsv[3]; +} __packed; + +struct bss_color_tlv { + __le16 tag; + __le16 len; + u8 enable; + u8 color; + u8 rsv[2]; +} __packed; + +struct bss_inband_discovery_tlv { + __le16 tag; + __le16 len; + u8 tx_type; + u8 tx_mode; + u8 tx_interval; + u8 enable; + __le16 wcid; + __le16 prob_rsp_len; +#define MAX_INBAND_FRAME_SIZE 512 + u8 pkt[MAX_INBAND_FRAME_SIZE]; +} __packed; + +struct bss_bcn_content_tlv { + __le16 tag; + __le16 len; + __le16 tim_ie_pos; + __le16 csa_ie_pos; + __le16 bcc_ie_pos; + u8 enable; + u8 type; + __le16 pkt_len; +#define MAX_BEACON_SIZE 512 + u8 pkt[MAX_BEACON_SIZE]; +} __packed; + +struct bss_bcn_cntdwn_tlv { + __le16 tag; + __le16 len; + u8 cnt; + u8 rsv[3]; +} __packed; + +struct bss_bcn_mbss_tlv { + __le16 tag; + __le16 len; + __le32 bitmap; +#define MAX_BEACON_NUM 32 + __le16 offset[MAX_BEACON_NUM]; +} __packed __aligned(4); + +struct bss_txcmd_tlv { + __le16 tag; + __le16 len; + u8 txcmd_mode; + u8 __rsv[3]; +} __packed; + +struct bss_sec_tlv { + __le16 tag; + __le16 len; + u8 __rsv1[2]; + u8 cipher; + u8 __rsv2[1]; +} __packed; + +struct bss_power_save { + __le16 tag; + __le16 len; + u8 profile; + u8 _rsv[3]; +} __packed; + +struct bss_mld_tlv { + __le16 tag; + __le16 len; + u8 group_mld_id; + u8 own_mld_id; + u8 mac_addr[ETH_ALEN]; + u8 remap_idx; + u8 __rsv[3]; +} __packed; + +struct sta_rec_ba_uni { + __le16 tag; + __le16 len; + u8 tid; + u8 ba_type; + u8 amsdu; + u8 ba_en; + __le16 ssn; + __le16 winsize; + u8 ba_rdd_rro; + u8 __rsv[3]; +} __packed; + +struct sec_key_uni { + __le16 wlan_idx; + u8 mgmt_prot; + u8 cipher_id; + u8 cipher_len; + u8 key_id; + u8 key_len; + u8 need_resp; + u8 key[32]; +} __packed; + +struct sta_rec_sec_uni { + __le16 tag; + __le16 len; + u8 add; + u8 n_cipher; + u8 rsv[2]; + + struct sec_key_uni key[2]; +} __packed; + +struct sta_rec_hdrt { + __le16 tag; + __le16 len; + u8 hdrt_mode; + u8 rsv[3]; +} __packed; + +struct sta_rec_hdr_trans { + __le16 tag; + __le16 len; + u8 from_ds; + u8 to_ds; + u8 dis_rx_hdr_tran; + u8 rsv; +} __packed; + +struct hdr_trans_en { + __le16 tag; + __le16 len; + u8 enable; + u8 check_bssid; + u8 mode; + u8 __rsv; +} __packed; + +struct hdr_trans_vlan { + __le16 tag; + __le16 len; + u8 insert_vlan; + u8 remove_vlan; + u8 tid; + u8 __rsv; +} __packed; + +struct hdr_trans_blacklist { + __le16 tag; + __le16 len; + u8 idx; + u8 enable; + __le16 type; +} __packed; + +struct uni_header { + u8 __rsv[4]; +} __packed; + +struct vow_rx_airtime { + __le16 tag; + __le16 len; + + u8 enable; + u8 band; + u8 __rsv[2]; +} __packed; + +struct bf_sounding_on { + __le16 tag; + __le16 len; + + u8 snd_mode; + u8 sta_num; + u8 __rsv[2]; + __le16 wlan_id[4]; + __le32 snd_period; +} __packed; + +struct bf_hw_en_status_update { + __le16 tag; + __le16 len; + + bool ebf; + bool ibf; + u8 __rsv[2]; +} __packed; + +struct bf_mod_en_ctrl { + __le16 tag; + __le16 len; + + u8 bf_num; + u8 bf_bitmap; + u8 bf_sel[8]; + u8 __rsv[2]; +} __packed; + +union bf_tag_tlv { + struct bf_sounding_on bf_snd; + struct bf_hw_en_status_update bf_hw_en; + struct bf_mod_en_ctrl bf_mod_en; +}; + +struct ra_rate { + __le16 wlan_idx; + u8 mode; + u8 stbc; + __le16 gi; + u8 bw; + u8 ldpc; + u8 mcs; + u8 nss; + __le16 ltf; + u8 spe; + u8 preamble; + u8 __rsv[2]; +} __packed; + +struct ra_fixed_rate { + __le16 tag; + __le16 len; + + __le16 version; + struct ra_rate rate; +} __packed; + +enum { + UNI_RA_FIXED_RATE = 0xf, +}; + +#define MT7996_HDR_TRANS_MAX_SIZE (sizeof(struct hdr_trans_en) + \ + sizeof(struct hdr_trans_vlan) + \ + sizeof(struct hdr_trans_blacklist)) + +enum { + UNI_HDR_TRANS_EN, + UNI_HDR_TRANS_VLAN, + UNI_HDR_TRANS_BLACKLIST, +}; + +enum { + RATE_PARAM_FIXED = 3, + RATE_PARAM_MMPS_UPDATE = 5, + RATE_PARAM_FIXED_HE_LTF = 7, + RATE_PARAM_FIXED_MCS, + RATE_PARAM_FIXED_GI = 11, + RATE_PARAM_AUTO = 20, +}; + +enum { + BF_SOUNDING_ON = 1, + BF_HW_EN_UPDATE = 17, + BF_MOD_EN_CTRL = 20, +}; + +enum { + CMD_BAND_NONE, + CMD_BAND_24G, + CMD_BAND_5G, + CMD_BAND_6G, +}; + +struct bss_req_hdr { + u8 bss_idx; + u8 __rsv[3]; +} __packed; + +enum { + UNI_CHANNEL_SWITCH, + UNI_CHANNEL_RX_PATH, +}; + +#define MT7996_BSS_UPDATE_MAX_SIZE (sizeof(struct bss_req_hdr) + \ + sizeof(struct mt76_connac_bss_basic_tlv) + \ + sizeof(struct bss_rlm_tlv) + \ + sizeof(struct bss_ra_tlv) + \ + sizeof(struct bss_info_uni_he) + \ + sizeof(struct bss_rate_tlv) + \ + sizeof(struct bss_txcmd_tlv) + \ + sizeof(struct bss_power_save) + \ + sizeof(struct bss_sec_tlv) + \ + sizeof(struct bss_mld_tlv)) + +#define MT7996_STA_UPDATE_MAX_SIZE (sizeof(struct sta_req_hdr) + \ + sizeof(struct sta_rec_basic) + \ + sizeof(struct sta_rec_bf) + \ + sizeof(struct sta_rec_ht) + \ + sizeof(struct sta_rec_he_v2) + \ + sizeof(struct sta_rec_ba_uni) + \ + sizeof(struct sta_rec_vht) + \ + sizeof(struct sta_rec_uapsd) + \ + sizeof(struct sta_rec_amsdu) + \ + sizeof(struct sta_rec_bfee) + \ + sizeof(struct sta_rec_phy) + \ + sizeof(struct sta_rec_ra) + \ + sizeof(struct sta_rec_sec) + \ + sizeof(struct sta_rec_ra_fixed) + \ + sizeof(struct sta_rec_he_6g_capa) + \ + sizeof(struct sta_rec_hdrt) + \ + sizeof(struct sta_rec_hdr_trans) + \ + sizeof(struct tlv)) + +#define MT7996_BEACON_UPDATE_SIZE (sizeof(struct bss_req_hdr) + \ + sizeof(struct bss_bcn_content_tlv) + \ + sizeof(struct bss_bcn_cntdwn_tlv) + \ + sizeof(struct bss_bcn_mbss_tlv)) + +#define MT7996_INBAND_FRAME_SIZE (sizeof(struct bss_req_hdr) + \ + sizeof(struct bss_inband_discovery_tlv)) + +enum { + UNI_BAND_CONFIG_RADIO_ENABLE, + UNI_BAND_CONFIG_RTS_THRESHOLD = 0x08, +}; + +enum { + UNI_WSYS_CONFIG_FW_LOG_CTRL, + UNI_WSYS_CONFIG_FW_DBG_CTRL, +}; + +enum { + UNI_RDD_CTRL_PARM, + UNI_RDD_CTRL_SET_TH = 0x3, +}; + +enum { + UNI_EFUSE_ACCESS = 1, + UNI_EFUSE_BUFFER_MODE, + UNI_EFUSE_FREE_BLOCK, + UNI_EFUSE_BUFFER_RD, +}; + +enum { + UNI_VOW_DRR_CTRL, + UNI_VOW_RX_AT_AIRTIME_EN = 0x0b, + UNI_VOW_RX_AT_AIRTIME_CLR_EN = 0x0e, +}; + +enum { + UNI_CMD_MIB_DATA, +}; + +enum { + UNI_POWER_OFF, +}; + +enum { + UNI_CMD_TWT_ARGT_UPDATE = 0x0, + UNI_CMD_TWT_MGMT_OFFLOAD, +}; + +enum { + UNI_RRO_DEL_ENTRY = 0x1, + UNI_RRO_SET_PLATFORM_TYPE, + UNI_RRO_GET_BA_SESSION_TABLE, + UNI_RRO_SET_BYPASS_MODE, + UNI_RRO_SET_TXFREE_PATH, +}; + +enum{ + UNI_CMD_SR_ENABLE = 0x1, +}; + +enum { + UNI_CMD_ACCESS_REG_BASIC = 0x0, + UNI_CMD_ACCESS_RF_REG_BASIC, +}; + +enum { + UNI_CMD_SER_QUERY = 0x0, + UNI_CMD_SER_SET = 0x2, + UNI_CMD_SER_TRIGGER = 0x3, +}; + +enum { + SER_QUERY, + /* recovery */ + SER_SET_RECOVER_L1, + SER_SET_RECOVER_L2, + SER_SET_RECOVER_L3_RX_ABORT, + SER_SET_RECOVER_L3_TX_ABORT, + SER_SET_RECOVER_L3_TX_DISABLE, + SER_SET_RECOVER_L3_BF, + /* action */ + SER_ENABLE = 2, + SER_RECOVER +}; + +enum { + MT7996_SEC_MODE_PLAIN, + MT7996_SEC_MODE_AES, + MT7996_SEC_MODE_SCRAMBLE, + MT7996_SEC_MODE_MAX, +}; + +#define MT7996_PATCH_SEC GENMASK(31, 24) +#define MT7996_PATCH_SCRAMBLE_KEY GENMASK(15, 8) +#define MT7996_PATCH_AES_KEY GENMASK(7, 0) + +#define MT7996_SEC_ENCRYPT BIT(0) +#define MT7996_SEC_KEY_IDX GENMASK(2, 1) +#define MT7996_SEC_IV BIT(3) + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c new file mode 100644 index 000000000000..0d097cda4da7 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c @@ -0,0 +1,385 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#include +#include +#include + +#include "mt7996.h" +#include "mac.h" +#include "../trace.h" + +static const struct __base mt7996_reg_base[] = { + [WF_AGG_BASE] = { { 0x820e2000, 0x820f2000, 0x830e2000 } }, + [WF_MIB_BASE] = { { 0x820ed000, 0x820fd000, 0x830ed000 } }, + [WF_TMAC_BASE] = { { 0x820e4000, 0x820f4000, 0x830e4000 } }, + [WF_RMAC_BASE] = { { 0x820e5000, 0x820f5000, 0x830e5000 } }, + [WF_ARB_BASE] = { { 0x820e3000, 0x820f3000, 0x830e3000 } }, + [WF_LPON_BASE] = { { 0x820eb000, 0x820fb000, 0x830eb000 } }, + [WF_ETBF_BASE] = { { 0x820ea000, 0x820fa000, 0x830ea000 } }, + [WF_DMA_BASE] = { { 0x820e7000, 0x820f7000, 0x830e7000 } }, +}; + +static const struct __map mt7996_reg_map[] = { + { 0x54000000, 0x02000, 0x1000 }, /* WFDMA_0 (PCIE0 MCU DMA0) */ + { 0x55000000, 0x03000, 0x1000 }, /* WFDMA_1 (PCIE0 MCU DMA1) */ + { 0x56000000, 0x04000, 0x1000 }, /* WFDMA reserved */ + { 0x57000000, 0x05000, 0x1000 }, /* WFDMA MCU wrap CR */ + { 0x58000000, 0x06000, 0x1000 }, /* WFDMA PCIE1 MCU DMA0 (MEM_DMA) */ + { 0x59000000, 0x07000, 0x1000 }, /* WFDMA PCIE1 MCU DMA1 */ + { 0x820c0000, 0x08000, 0x4000 }, /* WF_UMAC_TOP (PLE) */ + { 0x820c8000, 0x0c000, 0x2000 }, /* WF_UMAC_TOP (PSE) */ + { 0x820cc000, 0x0e000, 0x1000 }, /* WF_UMAC_TOP (PP) */ + { 0x74030000, 0x10000, 0x1000 }, /* PCIe MAC */ + { 0x820e0000, 0x20000, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_CFG) */ + { 0x820e1000, 0x20400, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_TRB) */ + { 0x820e2000, 0x20800, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_AGG) */ + { 0x820e3000, 0x20c00, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_ARB) */ + { 0x820e4000, 0x21000, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_TMAC) */ + { 0x820e5000, 0x21400, 0x0800 }, /* WF_LMAC_TOP BN0 (WF_RMAC) */ + { 0x820ce000, 0x21c00, 0x0200 }, /* WF_LMAC_TOP (WF_SEC) */ + { 0x820e7000, 0x21e00, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_DMA) */ + { 0x820cf000, 0x22000, 0x1000 }, /* WF_LMAC_TOP (WF_PF) */ + { 0x820e9000, 0x23400, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_WTBLOFF) */ + { 0x820ea000, 0x24000, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_ETBF) */ + { 0x820eb000, 0x24200, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_LPON) */ + { 0x820ec000, 0x24600, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_INT) */ + { 0x820ed000, 0x24800, 0x0800 }, /* WF_LMAC_TOP BN0 (WF_MIB) */ + { 0x820ca000, 0x26000, 0x2000 }, /* WF_LMAC_TOP BN0 (WF_MUCOP) */ + { 0x820d0000, 0x30000, 0x10000 }, /* WF_LMAC_TOP (WF_WTBLON) */ + { 0x40000000, 0x70000, 0x10000 }, /* WF_UMAC_SYSRAM */ + { 0x00400000, 0x80000, 0x10000 }, /* WF_MCU_SYSRAM */ + { 0x00410000, 0x90000, 0x10000 }, /* WF_MCU_SYSRAM (configure register) */ + { 0x820f0000, 0xa0000, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_CFG) */ + { 0x820f1000, 0xa0600, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_TRB) */ + { 0x820f2000, 0xa0800, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_AGG) */ + { 0x820f3000, 0xa0c00, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_ARB) */ + { 0x820f4000, 0xa1000, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_TMAC) */ + { 0x820f5000, 0xa1400, 0x0800 }, /* WF_LMAC_TOP BN1 (WF_RMAC) */ + { 0x820f7000, 0xa1e00, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_DMA) */ + { 0x820f9000, 0xa3400, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_WTBLOFF) */ + { 0x820fa000, 0xa4000, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_ETBF) */ + { 0x820fb000, 0xa4200, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_LPON) */ + { 0x820fc000, 0xa4600, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_INT) */ + { 0x820fd000, 0xa4800, 0x0800 }, /* WF_LMAC_TOP BN1 (WF_MIB) */ + { 0x820cc000, 0xa5000, 0x2000 }, /* WF_LMAC_TOP BN1 (WF_MUCOP) */ + { 0x820c4000, 0xa8000, 0x4000 }, /* WF_LMAC_TOP BN1 (WF_MUCOP) */ + { 0x820b0000, 0xae000, 0x1000 }, /* [APB2] WFSYS_ON */ + { 0x80020000, 0xb0000, 0x10000 }, /* WF_TOP_MISC_OFF */ + { 0x81020000, 0xc0000, 0x10000 }, /* WF_TOP_MISC_ON */ + { 0x7c020000, 0xd0000, 0x10000 }, /* CONN_INFRA, wfdma */ + { 0x7c060000, 0xe0000, 0x10000 }, /* CONN_INFRA, conn_host_csr_top */ + { 0x7c000000, 0xf0000, 0x10000 }, /* CONN_INFRA */ + { 0x0, 0x0, 0x0 }, /* imply end of search */ +}; + +static u32 mt7996_reg_map_l1(struct mt7996_dev *dev, u32 addr) +{ + u32 offset = FIELD_GET(MT_HIF_REMAP_L1_OFFSET, addr); + u32 base = FIELD_GET(MT_HIF_REMAP_L1_BASE, addr); + + dev->reg_l1_backup = dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L1); + dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L1, + MT_HIF_REMAP_L1_MASK, + FIELD_PREP(MT_HIF_REMAP_L1_MASK, base)); + /* use read to push write */ + dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L1); + + return MT_HIF_REMAP_BASE_L1 + offset; +} + +static u32 mt7996_reg_map_l2(struct mt7996_dev *dev, u32 addr) +{ + u32 offset = FIELD_GET(MT_HIF_REMAP_L2_OFFSET, addr); + u32 base = FIELD_GET(MT_HIF_REMAP_L2_BASE, addr); + + dev->reg_l2_backup = dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L2); + dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L2, + MT_HIF_REMAP_L2_MASK, + FIELD_PREP(MT_HIF_REMAP_L2_MASK, base)); + /* use read to push write */ + dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L2); + + return MT_HIF_REMAP_BASE_L2 + offset; +} + +static void mt7996_reg_remap_restore(struct mt7996_dev *dev) +{ + /* remap to ori status */ + if (unlikely(dev->reg_l1_backup)) { + dev->bus_ops->wr(&dev->mt76, MT_HIF_REMAP_L1, dev->reg_l1_backup); + dev->reg_l1_backup = 0; + } + + if (dev->reg_l2_backup) { + dev->bus_ops->wr(&dev->mt76, MT_HIF_REMAP_L2, dev->reg_l2_backup); + dev->reg_l2_backup = 0; + } +} + +static u32 __mt7996_reg_addr(struct mt7996_dev *dev, u32 addr) +{ + int i; + + mt7996_reg_remap_restore(dev); + + if (addr < 0x100000) + return addr; + + for (i = 0; i < dev->reg.map_size; i++) { + u32 ofs; + + if (addr < dev->reg.map[i].phys) + continue; + + ofs = addr - dev->reg.map[i].phys; + if (ofs > dev->reg.map[i].size) + continue; + + return dev->reg.map[i].mapped + ofs; + } + + if ((addr >= MT_INFRA_BASE && addr < MT_WFSYS0_PHY_START) || + (addr >= MT_WFSYS0_PHY_START && addr < MT_WFSYS1_PHY_START) || + (addr >= MT_WFSYS1_PHY_START && addr <= MT_WFSYS1_PHY_END)) + return mt7996_reg_map_l1(dev, addr); + + if (dev_is_pci(dev->mt76.dev) && + ((addr >= MT_CBTOP1_PHY_START && addr <= MT_CBTOP1_PHY_END) || + (addr >= MT_CBTOP2_PHY_START && addr <= MT_CBTOP2_PHY_END))) + return mt7996_reg_map_l1(dev, addr); + + /* CONN_INFRA: covert to phyiscal addr and use layer 1 remap */ + if (addr >= MT_INFRA_MCU_START && addr <= MT_INFRA_MCU_END) { + addr = addr - MT_INFRA_MCU_START + MT_INFRA_BASE; + return mt7996_reg_map_l1(dev, addr); + } + + return mt7996_reg_map_l2(dev, addr); +} + +static u32 mt7996_rr(struct mt76_dev *mdev, u32 offset) +{ + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + + return dev->bus_ops->rr(mdev, __mt7996_reg_addr(dev, offset)); +} + +static void mt7996_wr(struct mt76_dev *mdev, u32 offset, u32 val) +{ + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + + dev->bus_ops->wr(mdev, __mt7996_reg_addr(dev, offset), val); +} + +static u32 mt7996_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val) +{ + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + + return dev->bus_ops->rmw(mdev, __mt7996_reg_addr(dev, offset), mask, val); +} + +static int mt7996_mmio_init(struct mt76_dev *mdev, + void __iomem *mem_base, + u32 device_id) +{ + struct mt76_bus_ops *bus_ops; + struct mt7996_dev *dev; + + dev = container_of(mdev, struct mt7996_dev, mt76); + mt76_mmio_init(&dev->mt76, mem_base); + + switch (device_id) { + case 0x7990: + dev->reg.base = mt7996_reg_base; + dev->reg.map = mt7996_reg_map; + dev->reg.map_size = ARRAY_SIZE(mt7996_reg_map); + break; + default: + return -EINVAL; + } + + dev->bus_ops = dev->mt76.bus; + bus_ops = devm_kmemdup(dev->mt76.dev, dev->bus_ops, sizeof(*bus_ops), + GFP_KERNEL); + if (!bus_ops) + return -ENOMEM; + + bus_ops->rr = mt7996_rr; + bus_ops->wr = mt7996_wr; + bus_ops->rmw = mt7996_rmw; + dev->mt76.bus = bus_ops; + + mdev->rev = (device_id << 16) | (mt76_rr(dev, MT_HW_REV) & 0xff); + + dev_dbg(mdev->dev, "ASIC revision: %04x\n", mdev->rev); + + return 0; +} + +void mt7996_dual_hif_set_irq_mask(struct mt7996_dev *dev, bool write_reg, + u32 clear, u32 set) +{ + struct mt76_dev *mdev = &dev->mt76; + unsigned long flags; + + spin_lock_irqsave(&mdev->mmio.irq_lock, flags); + + mdev->mmio.irqmask &= ~clear; + mdev->mmio.irqmask |= set; + + if (write_reg) { + mt76_wr(dev, MT_INT_MASK_CSR, mdev->mmio.irqmask); + mt76_wr(dev, MT_INT1_MASK_CSR, mdev->mmio.irqmask); + } + + spin_unlock_irqrestore(&mdev->mmio.irq_lock, flags); +} + +static void mt7996_rx_poll_complete(struct mt76_dev *mdev, + enum mt76_rxq_id q) +{ + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + + mt7996_irq_enable(dev, MT_INT_RX(q)); +} + +/* TODO: support 2/4/6/8 MSI-X vectors */ +static void mt7996_irq_tasklet(struct tasklet_struct *t) +{ + struct mt7996_dev *dev = from_tasklet(dev, t, irq_tasklet); + u32 i, intr, mask, intr1; + + mt76_wr(dev, MT_INT_MASK_CSR, 0); + if (dev->hif2) + mt76_wr(dev, MT_INT1_MASK_CSR, 0); + + intr = mt76_rr(dev, MT_INT_SOURCE_CSR); + intr &= dev->mt76.mmio.irqmask; + mt76_wr(dev, MT_INT_SOURCE_CSR, intr); + + if (dev->hif2) { + intr1 = mt76_rr(dev, MT_INT1_SOURCE_CSR); + intr1 &= dev->mt76.mmio.irqmask; + mt76_wr(dev, MT_INT1_SOURCE_CSR, intr1); + + intr |= intr1; + } + + trace_dev_irq(&dev->mt76, intr, dev->mt76.mmio.irqmask); + + mask = intr & MT_INT_RX_DONE_ALL; + if (intr & MT_INT_TX_DONE_MCU) + mask |= MT_INT_TX_DONE_MCU; + mt7996_irq_disable(dev, mask); + + if (intr & MT_INT_TX_DONE_MCU) + napi_schedule(&dev->mt76.tx_napi); + + for (i = 0; i < __MT_RXQ_MAX; i++) { + if ((intr & MT_INT_RX(i))) + napi_schedule(&dev->mt76.napi[i]); + } + + if (intr & MT_INT_MCU_CMD) { + u32 val = mt76_rr(dev, MT_MCU_CMD); + + mt76_wr(dev, MT_MCU_CMD, val); + if (val & MT_MCU_CMD_ERROR_MASK) { + dev->reset_state = val; + ieee80211_queue_work(mt76_hw(dev), &dev->reset_work); + wake_up(&dev->reset_wait); + } + } +} + +irqreturn_t mt7996_irq_handler(int irq, void *dev_instance) +{ + struct mt7996_dev *dev = dev_instance; + + mt76_wr(dev, MT_INT_MASK_CSR, 0); + if (dev->hif2) + mt76_wr(dev, MT_INT1_MASK_CSR, 0); + + if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state)) + return IRQ_NONE; + + tasklet_schedule(&dev->irq_tasklet); + + return IRQ_HANDLED; +} + +struct mt7996_dev *mt7996_mmio_probe(struct device *pdev, + void __iomem *mem_base, u32 device_id) +{ + static const struct mt76_driver_ops drv_ops = { + /* txwi_size = txd size + txp size */ + .txwi_size = MT_TXD_SIZE + sizeof(struct mt7996_txp), + .drv_flags = MT_DRV_TXWI_NO_FREE | + MT_DRV_HW_MGMT_TXQ, + .survey_flags = SURVEY_INFO_TIME_TX | + SURVEY_INFO_TIME_RX | + SURVEY_INFO_TIME_BSS_RX, + .token_size = MT7996_TOKEN_SIZE, + .tx_prepare_skb = mt7996_tx_prepare_skb, + .tx_complete_skb = mt7996_tx_complete_skb, + .rx_skb = mt7996_queue_rx_skb, + .rx_check = mt7996_rx_check, + .rx_poll_complete = mt7996_rx_poll_complete, + .sta_ps = mt7996_sta_ps, + .sta_add = mt7996_mac_sta_add, + .sta_remove = mt7996_mac_sta_remove, + .update_survey = mt7996_update_channel, + }; + struct mt7996_dev *dev; + struct mt76_dev *mdev; + int ret; + + mdev = mt76_alloc_device(pdev, sizeof(*dev), &mt7996_ops, &drv_ops); + if (!mdev) + return ERR_PTR(-ENOMEM); + + dev = container_of(mdev, struct mt7996_dev, mt76); + + ret = mt7996_mmio_init(mdev, mem_base, device_id); + if (ret) + goto error; + + tasklet_setup(&dev->irq_tasklet, mt7996_irq_tasklet); + + mt76_wr(dev, MT_INT_MASK_CSR, 0); + + return dev; + +error: + mt76_free_device(&dev->mt76); + + return ERR_PTR(ret); +} + +static int __init mt7996_init(void) +{ + int ret; + + ret = pci_register_driver(&mt7996_hif_driver); + if (ret) + return ret; + + ret = pci_register_driver(&mt7996_pci_driver); + if (ret) + pci_unregister_driver(&mt7996_hif_driver); + + return ret; +} + +static void __exit mt7996_exit(void) +{ + pci_unregister_driver(&mt7996_pci_driver); + pci_unregister_driver(&mt7996_hif_driver); +} + +module_init(mt7996_init); +module_exit(mt7996_exit); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h new file mode 100644 index 000000000000..167f615f099e --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -0,0 +1,516 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#ifndef __MT7996_H +#define __MT7996_H + +#include +#include +#include "../mt76_connac.h" +#include "regs.h" + +#define MT7996_MAX_INTERFACES 19 +#define MT7996_MAX_WMM_SETS 4 +#define MT7996_WTBL_SIZE 544 +#define MT7996_WTBL_RESERVED (MT7996_WTBL_SIZE - 1) +#define MT7996_WTBL_STA (MT7996_WTBL_RESERVED - \ + MT7996_MAX_INTERFACES) + +#define MT7996_WATCHDOG_TIME (HZ / 10) +#define MT7996_RESET_TIMEOUT (30 * HZ) + +#define MT7996_TX_RING_SIZE 2048 +#define MT7996_TX_MCU_RING_SIZE 256 +#define MT7996_TX_FWDL_RING_SIZE 128 + +#define MT7996_RX_RING_SIZE 1536 +#define MT7996_RX_MCU_RING_SIZE 512 + +#define MT7996_FIRMWARE_WA "mediatek/mt7996/mt7996_wa.bin" +#define MT7996_FIRMWARE_WM "mediatek/mt7996/mt7996_wm.bin" +#define MT7996_ROM_PATCH "mediatek/mt7996/mt7996_rom_patch.bin" + +#define MT7996_EEPROM_DEFAULT "mediatek/mt7996/mt7996_eeprom.bin" +#define MT7996_EEPROM_SIZE 7680 +#define MT7996_EEPROM_BLOCK_SIZE 16 +#define MT7996_TOKEN_SIZE 8192 + +#define MT7996_CFEND_RATE_DEFAULT 0x49 /* OFDM 24M */ +#define MT7996_CFEND_RATE_11B 0x03 /* 11B LP, 11M */ + +#define MT7996_MAX_TWT_AGRT 16 +#define MT7996_MAX_STA_TWT_AGRT 8 +#define MT7996_MAX_QUEUE (__MT_RXQ_MAX + __MT_MCUQ_MAX + 3) + +struct mt7996_vif; +struct mt7996_sta; +struct mt7996_dfs_pulse; +struct mt7996_dfs_pattern; + +enum mt7996_txq_id { + MT7996_TXQ_FWDL = 16, + MT7996_TXQ_MCU_WM, + MT7996_TXQ_BAND0, + MT7996_TXQ_BAND1, + MT7996_TXQ_MCU_WA, + MT7996_TXQ_BAND2, +}; + +enum mt7996_rxq_id { + MT7996_RXQ_MCU_WM = 0, + MT7996_RXQ_MCU_WA, + MT7996_RXQ_MCU_WA_MAIN = 2, + MT7996_RXQ_MCU_WA_EXT = 2,/* unused */ + MT7996_RXQ_MCU_WA_TRI = 3, + MT7996_RXQ_BAND0 = 4, + MT7996_RXQ_BAND1 = 4,/* unused */ + MT7996_RXQ_BAND2 = 5, +}; + +struct mt7996_twt_flow { + struct list_head list; + u64 start_tsf; + u64 tsf; + u32 duration; + u16 wcid; + __le16 mantissa; + u8 exp; + u8 table_id; + u8 id; + u8 protection:1; + u8 flowtype:1; + u8 trigger:1; + u8 sched:1; +}; + +struct mt7996_sta { + struct mt76_wcid wcid; /* must be first */ + + struct mt7996_vif *vif; + + struct list_head poll_list; + struct list_head rc_list; + u32 airtime_ac[8]; + + unsigned long changed; + unsigned long jiffies; + unsigned long ampdu_state; + + struct mt76_sta_stats stats; + + struct mt76_connac_sta_key_conf bip; + + struct { + u8 flowid_mask; + struct mt7996_twt_flow flow[MT7996_MAX_STA_TWT_AGRT]; + } twt; +}; + +struct mt7996_vif_cap { + bool ht_ldpc:1; + bool vht_ldpc:1; + bool he_ldpc:1; + bool vht_su_ebfer:1; + bool vht_su_ebfee:1; + bool vht_mu_ebfer:1; + bool vht_mu_ebfee:1; + bool he_su_ebfer:1; + bool he_su_ebfee:1; + bool he_mu_ebfer:1; +}; + +struct mt7996_vif { + struct mt76_vif mt76; /* must be first */ + + struct mt7996_vif_cap cap; + struct mt7996_sta sta; + struct mt7996_phy *phy; + + struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS]; + struct cfg80211_bitrate_mask bitrate_mask; +}; + +/* per-phy stats. */ +struct mib_stats { + u32 ack_fail_cnt; + u32 fcs_err_cnt; + u32 rts_cnt; + u32 rts_retries_cnt; + u32 ba_miss_cnt; + u32 tx_mu_bf_cnt; + u32 tx_mu_mpdu_cnt; + u32 tx_mu_acked_mpdu_cnt; + u32 tx_su_acked_mpdu_cnt; + u32 tx_bf_ibf_ppdu_cnt; + u32 tx_bf_ebf_ppdu_cnt; + + u32 tx_bf_rx_fb_all_cnt; + u32 tx_bf_rx_fb_eht_cnt; + u32 tx_bf_rx_fb_he_cnt; + u32 tx_bf_rx_fb_vht_cnt; + u32 tx_bf_rx_fb_ht_cnt; + + u32 tx_bf_rx_fb_bw; /* value of last sample, not cumulative */ + u32 tx_bf_rx_fb_nc_cnt; + u32 tx_bf_rx_fb_nr_cnt; + u32 tx_bf_fb_cpl_cnt; + u32 tx_bf_fb_trig_cnt; + + u32 tx_ampdu_cnt; + u32 tx_stop_q_empty_cnt; + u32 tx_mpdu_attempts_cnt; + u32 tx_mpdu_success_cnt; + /* BF counter is PPDU-based, so remove MPDU-based BF counter */ + + u32 tx_rwp_fail_cnt; + u32 tx_rwp_need_cnt; + + /* rx stats */ + u32 rx_fifo_full_cnt; + u32 channel_idle_cnt; + u32 rx_vector_mismatch_cnt; + u32 rx_delimiter_fail_cnt; + u32 rx_len_mismatch_cnt; + u32 rx_mpdu_cnt; + u32 rx_ampdu_cnt; + u32 rx_ampdu_bytes_cnt; + u32 rx_ampdu_valid_subframe_cnt; + u32 rx_ampdu_valid_subframe_bytes_cnt; + u32 rx_pfdrop_cnt; + u32 rx_vec_queue_overflow_drop_cnt; + u32 rx_ba_cnt; + + u32 tx_amsdu[8]; + u32 tx_amsdu_cnt; +}; + +struct mt7996_hif { + struct list_head list; + + struct device *dev; + void __iomem *regs; + int irq; +}; + +struct mt7996_phy { + struct mt76_phy *mt76; + struct mt7996_dev *dev; + + struct ieee80211_sband_iftype_data iftype[NUM_NL80211_BANDS][NUM_NL80211_IFTYPES]; + + struct ieee80211_vif *monitor_vif; + + u32 rxfilter; + u64 omac_mask; + + u16 noise; + + s16 coverage_class; + u8 slottime; + + u8 rdd_state; + + u32 rx_ampdu_ts; + u32 ampdu_ref; + + struct mib_stats mib; + struct mt76_channel_state state_ts; +}; + +struct mt7996_dev { + union { /* must be first */ + struct mt76_dev mt76; + struct mt76_phy mphy; + }; + + struct mt7996_hif *hif2; + struct mt7996_reg_desc reg; + u8 q_id[MT7996_MAX_QUEUE]; + u32 q_int_mask[MT7996_MAX_QUEUE]; + u32 q_wfdma_mask; + + const struct mt76_bus_ops *bus_ops; + struct tasklet_struct irq_tasklet; + struct mt7996_phy phy; + + /* monitor rx chain configured channel */ + struct cfg80211_chan_def rdd2_chandef; + struct mt7996_phy *rdd2_phy; + + u16 chainmask; + u8 chainshift[__MT_MAX_BAND]; + u32 hif_idx; + + struct work_struct init_work; + struct work_struct rc_work; + struct work_struct reset_work; + wait_queue_head_t reset_wait; + u32 reset_state; + + struct list_head sta_rc_list; + struct list_head sta_poll_list; + struct list_head twt_list; + spinlock_t sta_poll_lock; + + u32 hw_pattern; + + bool dbdc_support:1; + bool tbtc_support:1; + bool flash_mode:1; + + bool ibf; + u8 fw_debug_wm; + u8 fw_debug_wa; + u8 fw_debug_bin; + u16 fw_debug_seq; + + struct dentry *debugfs_dir; + struct rchan *relay_fwlog; + + struct { + u8 table_mask; + u8 n_agrt; + } twt; + + u32 reg_l1_backup; + u32 reg_l2_backup; +}; + +enum { + WFDMA0 = 0x0, + WFDMA1, + WFDMA_EXT, + __MT_WFDMA_MAX, +}; + +enum { + MT_CTX0, + MT_HIF0 = 0x0, + + MT_LMAC_AC00 = 0x0, + MT_LMAC_AC01, + MT_LMAC_AC02, + MT_LMAC_AC03, + MT_LMAC_ALTX0 = 0x10, + MT_LMAC_BMC0, + MT_LMAC_BCN0, + MT_LMAC_PSMP0, +}; + +enum { + MT_RX_SEL0, + MT_RX_SEL1, + MT_RX_SEL2, /* monitor chain */ +}; + +enum mt7996_rdd_cmd { + RDD_STOP, + RDD_START, + RDD_DET_MODE, + RDD_RADAR_EMULATE, + RDD_START_TXQ = 20, + RDD_CAC_START = 50, + RDD_CAC_END, + RDD_NORMAL_START, + RDD_DISABLE_DFS_CAL, + RDD_PULSE_DBG, + RDD_READ_PULSE, + RDD_RESUME_BF, + RDD_IRQ_OFF, +}; + +static inline struct mt7996_phy * +mt7996_hw_phy(struct ieee80211_hw *hw) +{ + struct mt76_phy *phy = hw->priv; + + return phy->priv; +} + +static inline struct mt7996_dev * +mt7996_hw_dev(struct ieee80211_hw *hw) +{ + struct mt76_phy *phy = hw->priv; + + return container_of(phy->dev, struct mt7996_dev, mt76); +} + +static inline struct mt7996_phy * +__mt7996_phy(struct mt7996_dev *dev, enum mt76_band_id band) +{ + struct mt76_phy *phy = dev->mt76.phys[band]; + + if (!phy) + return NULL; + + return phy->priv; +} + +static inline struct mt7996_phy * +mt7996_phy2(struct mt7996_dev *dev) +{ + return __mt7996_phy(dev, MT_BAND1); +} + +static inline struct mt7996_phy * +mt7996_phy3(struct mt7996_dev *dev) +{ + return __mt7996_phy(dev, MT_BAND2); +} + +extern const struct ieee80211_ops mt7996_ops; +extern struct pci_driver mt7996_pci_driver; +extern struct pci_driver mt7996_hif_driver; + +struct mt7996_dev *mt7996_mmio_probe(struct device *pdev, + void __iomem *mem_base, u32 device_id); +void mt7996_wfsys_reset(struct mt7996_dev *dev); +irqreturn_t mt7996_irq_handler(int irq, void *dev_instance); +u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif); +int mt7996_register_device(struct mt7996_dev *dev); +void mt7996_unregister_device(struct mt7996_dev *dev); +int mt7996_eeprom_init(struct mt7996_dev *dev); +int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy); +int mt7996_eeprom_get_target_power(struct mt7996_dev *dev, + struct ieee80211_channel *chan); +s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band); +int mt7996_dma_init(struct mt7996_dev *dev); +void mt7996_dma_prefetch(struct mt7996_dev *dev); +void mt7996_dma_cleanup(struct mt7996_dev *dev); +int mt7996_mcu_init(struct mt7996_dev *dev); +int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev, + struct mt7996_vif *mvif, + struct mt7996_twt_flow *flow, + int cmd); +int mt7996_mcu_add_dev_info(struct mt7996_phy *phy, + struct ieee80211_vif *vif, bool enable); +int mt7996_mcu_add_bss_info(struct mt7996_phy *phy, + struct ieee80211_vif *vif, int enable); +int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool enable); +int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev, + struct ieee80211_ampdu_params *params, + bool add); +int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev, + struct ieee80211_ampdu_params *params, + bool add); +int mt7996_mcu_update_bss_color(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct cfg80211_he_bss_color *he_bss_color); +int mt7996_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + int enable); +int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev, + struct ieee80211_vif *vif, u32 changed); +int mt7996_mcu_add_obss_spr(struct mt7996_dev *dev, struct ieee80211_vif *vif, + bool enable); +int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool changed); +int mt7996_set_channel(struct mt7996_phy *phy); +int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag); +int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif); +int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev, + void *data, u16 version); +int mt7996_mcu_set_eeprom(struct mt7996_dev *dev); +int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset); +int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num); +int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 set, u8 band); +int mt7996_mcu_set_txbf(struct mt7996_dev *dev, u8 action); +int mt7996_mcu_set_fcc5_lpn(struct mt7996_dev *dev, int val); +int mt7996_mcu_set_pulse_th(struct mt7996_dev *dev, + const struct mt7996_dfs_pulse *pulse); +int mt7996_mcu_set_radar_th(struct mt7996_dev *dev, int index, + const struct mt7996_dfs_pattern *pattern); +int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable); +void mt7996_mcu_set_pm(void *priv, u8 *mac, struct ieee80211_vif *vif); +int mt7996_mcu_set_rts_thresh(struct mt7996_phy *phy, u32 val); +int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch); +int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index, + u8 rx_sel, u8 val); +int mt7996_mcu_rdd_background_enable(struct mt7996_phy *phy, + struct cfg80211_chan_def *chandef); +int mt7996_mcu_rf_regval(struct mt7996_dev *dev, u32 regidx, u32 *val, bool set); +int mt7996_mcu_set_hdr_trans(struct mt7996_dev *dev, bool hdr_trans); +int mt7996_mcu_set_rro(struct mt7996_dev *dev, u16 tag, u8 val); +int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3); +int mt7996_mcu_fw_log_2_host(struct mt7996_dev *dev, u8 type, u8 ctrl); +int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level); +void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb); +void mt7996_mcu_exit(struct mt7996_dev *dev); + +void mt7996_dual_hif_set_irq_mask(struct mt7996_dev *dev, bool write_reg, + u32 clear, u32 set); + +static inline void mt7996_irq_enable(struct mt7996_dev *dev, u32 mask) +{ + if (dev->hif2) + mt7996_dual_hif_set_irq_mask(dev, false, 0, mask); + else + mt76_set_irq_mask(&dev->mt76, 0, 0, mask); + + tasklet_schedule(&dev->irq_tasklet); +} + +static inline void mt7996_irq_disable(struct mt7996_dev *dev, u32 mask) +{ + if (dev->hif2) + mt7996_dual_hif_set_irq_mask(dev, true, mask, 0); + else + mt76_set_irq_mask(&dev->mt76, MT_INT_MASK_CSR, mask, 0); +} + +u32 mt7996_mac_wtbl_lmac_addr(struct mt7996_dev *dev, u16 wcid, u8 dw); +bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask); +void mt7996_mac_reset_counters(struct mt7996_phy *phy); +void mt7996_mac_cca_stats_reset(struct mt7996_phy *phy); +void mt7996_mac_enable_nf(struct mt7996_dev *dev, u8 band); +void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, + struct sk_buff *skb, struct mt76_wcid *wcid, int pid, + struct ieee80211_key_conf *key, u32 changed); +void mt7996_mac_set_timing(struct mt7996_phy *phy); +int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void mt7996_mac_work(struct work_struct *work); +void mt7996_mac_reset_work(struct work_struct *work); +void mt7996_mac_sta_rc_work(struct work_struct *work); +void mt7996_mac_update_stats(struct mt7996_phy *phy); +void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev, + struct mt7996_sta *msta, + u8 flowid); +void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + struct ieee80211_twt_setup *twt); +int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info); +void mt7996_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e); +void mt7996_tx_token_put(struct mt7996_dev *dev); +void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, + struct sk_buff *skb, u32 *info); +bool mt7996_rx_check(struct mt76_dev *mdev, void *data, int len); +void mt7996_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps); +void mt7996_stats_work(struct work_struct *work); +int mt76_dfs_start_rdd(struct mt7996_dev *dev, bool force); +int mt7996_dfs_init_radar_detector(struct mt7996_phy *phy); +void mt7996_set_stream_he_caps(struct mt7996_phy *phy); +void mt7996_set_stream_vht_txbf_caps(struct mt7996_phy *phy); +void mt7996_update_channel(struct mt76_phy *mphy); +int mt7996_init_debugfs(struct mt7996_phy *phy); +void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int len); +bool mt7996_debugfs_rx_log(struct mt7996_dev *dev, const void *data, int len); +int mt7996_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif, + struct mt76_connac_sta_key_conf *sta_key_conf, + struct ieee80211_key_conf *key, int mcu_cmd, + struct mt76_wcid *wcid, enum set_key_cmd cmd); +int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +#ifdef CONFIG_MAC80211_DEBUGFS +void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, struct dentry *dir); +#endif + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/pci.c b/drivers/net/wireless/mediatek/mt76/mt7996/pci.c new file mode 100644 index 000000000000..64aee3fb5445 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/pci.c @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#include +#include +#include + +#include "mt7996.h" +#include "mac.h" +#include "../trace.h" + +static LIST_HEAD(hif_list); +static DEFINE_SPINLOCK(hif_lock); +static u32 hif_idx; + +static const struct pci_device_id mt7996_pci_device_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7990) }, + { }, +}; + +static const struct pci_device_id mt7996_hif_device_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7991) }, + { }, +}; + +static struct mt7996_hif *mt7996_pci_get_hif2(u32 idx) +{ + struct mt7996_hif *hif; + u32 val; + + spin_lock_bh(&hif_lock); + + list_for_each_entry(hif, &hif_list, list) { + val = readl(hif->regs + MT_PCIE_RECOG_ID); + val &= MT_PCIE_RECOG_ID_MASK; + if (val != idx) + continue; + + get_device(hif->dev); + goto out; + } + hif = NULL; + +out: + spin_unlock_bh(&hif_lock); + + return hif; +} + +static void mt7996_put_hif2(struct mt7996_hif *hif) +{ + if (!hif) + return; + + put_device(hif->dev); +} + +static struct mt7996_hif *mt7996_pci_init_hif2(struct pci_dev *pdev) +{ + hif_idx++; + if (!pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x7991, NULL)) + return NULL; + + writel(hif_idx | MT_PCIE_RECOG_ID_SEM, + pcim_iomap_table(pdev)[0] + MT_PCIE_RECOG_ID); + + return mt7996_pci_get_hif2(hif_idx); +} + +static int mt7996_pci_hif2_probe(struct pci_dev *pdev) +{ + struct mt7996_hif *hif; + + hif = devm_kzalloc(&pdev->dev, sizeof(*hif), GFP_KERNEL); + if (!hif) + return -ENOMEM; + + hif->dev = &pdev->dev; + hif->regs = pcim_iomap_table(pdev)[0]; + hif->irq = pdev->irq; + spin_lock_bh(&hif_lock); + list_add(&hif->list, &hif_list); + spin_unlock_bh(&hif_lock); + pci_set_drvdata(pdev, hif); + + return 0; +} + +static int mt7996_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct pci_dev *hif2_dev; + struct mt7996_dev *dev; + struct mt76_dev *mdev; + struct mt7996_hif *hif2; + int irq, ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev)); + if (ret) + return ret; + + pci_set_master(pdev); + + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + mt76_pci_disable_aspm(pdev); + + if (id->device == 0x7991) + return mt7996_pci_hif2_probe(pdev); + + dev = mt7996_mmio_probe(&pdev->dev, pcim_iomap_table(pdev)[0], + id->device); + if (IS_ERR(dev)) + return PTR_ERR(dev); + + mdev = &dev->mt76; + mt7996_wfsys_reset(dev); + hif2 = mt7996_pci_init_hif2(pdev); + + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); + if (ret < 0) + goto free_device; + + irq = pdev->irq; + ret = devm_request_irq(mdev->dev, irq, mt7996_irq_handler, + IRQF_SHARED, KBUILD_MODNAME, dev); + if (ret) + goto free_irq_vector; + + mt76_wr(dev, MT_INT_MASK_CSR, 0); + /* master switch of PCIe tnterrupt enable */ + mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); + + if (hif2) { + hif2_dev = container_of(hif2->dev, struct pci_dev, dev); + dev->hif2 = hif2; + + ret = pci_alloc_irq_vectors(hif2_dev, 1, 1, PCI_IRQ_ALL_TYPES); + if (ret < 0) + goto free_hif2; + + dev->hif2->irq = hif2_dev->irq; + ret = devm_request_irq(mdev->dev, dev->hif2->irq, + mt7996_irq_handler, IRQF_SHARED, + KBUILD_MODNAME "-hif", dev); + if (ret) + goto free_hif2_irq_vector; + + mt76_wr(dev, MT_INT1_MASK_CSR, 0); + /* master switch of PCIe tnterrupt enable */ + mt76_wr(dev, MT_PCIE1_MAC_INT_ENABLE, 0xff); + } + + ret = mt7996_register_device(dev); + if (ret) + goto free_hif2_irq; + + return 0; + +free_hif2_irq: + if (dev->hif2) + devm_free_irq(mdev->dev, dev->hif2->irq, dev); +free_hif2_irq_vector: + if (dev->hif2) + pci_free_irq_vectors(hif2_dev); +free_hif2: + if (dev->hif2) + put_device(dev->hif2->dev); + devm_free_irq(mdev->dev, irq, dev); +free_irq_vector: + pci_free_irq_vectors(pdev); +free_device: + mt76_free_device(&dev->mt76); + + return ret; +} + +static void mt7996_hif_remove(struct pci_dev *pdev) +{ + struct mt7996_hif *hif = pci_get_drvdata(pdev); + + list_del(&hif->list); +} + +static void mt7996_pci_remove(struct pci_dev *pdev) +{ + struct mt76_dev *mdev; + struct mt7996_dev *dev; + + mdev = pci_get_drvdata(pdev); + dev = container_of(mdev, struct mt7996_dev, mt76); + mt7996_put_hif2(dev->hif2); + mt7996_unregister_device(dev); +} + +struct pci_driver mt7996_hif_driver = { + .name = KBUILD_MODNAME "_hif", + .id_table = mt7996_hif_device_table, + .probe = mt7996_pci_probe, + .remove = mt7996_hif_remove, +}; + +struct pci_driver mt7996_pci_driver = { + .name = KBUILD_MODNAME, + .id_table = mt7996_pci_device_table, + .probe = mt7996_pci_probe, + .remove = mt7996_pci_remove, +}; + +MODULE_DEVICE_TABLE(pci, mt7996_pci_device_table); +MODULE_DEVICE_TABLE(pci, mt7996_hif_device_table); +MODULE_FIRMWARE(MT7996_FIRMWARE_WA); +MODULE_FIRMWARE(MT7996_FIRMWARE_WM); +MODULE_FIRMWARE(MT7996_ROM_PATCH); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/regs.h b/drivers/net/wireless/mediatek/mt76/mt7996/regs.h new file mode 100644 index 000000000000..1d53611da4d8 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/regs.h @@ -0,0 +1,533 @@ +/* SPDX-License-Identifier: ISC */ +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#ifndef __MT7996_REGS_H +#define __MT7996_REGS_H + +struct __map { + u32 phys; + u32 mapped; + u32 size; +}; + +struct __base { + u32 band_base[__MT_MAX_BAND]; +}; + +/* used to differentiate between generations */ +struct mt7996_reg_desc { + const struct __base *base; + const struct __map *map; + u32 map_size; +}; + +enum base_rev { + WF_AGG_BASE, + WF_MIB_BASE, + WF_TMAC_BASE, + WF_RMAC_BASE, + WF_ARB_BASE, + WF_LPON_BASE, + WF_ETBF_BASE, + WF_DMA_BASE, + __MT_REG_BASE_MAX, +}; + +#define __BASE(_id, _band) (dev->reg.base[(_id)].band_base[(_band)]) + +#define MT_MCU_INT_EVENT 0x2108 +#define MT_MCU_INT_EVENT_DMA_STOPPED BIT(0) +#define MT_MCU_INT_EVENT_DMA_INIT BIT(1) +#define MT_MCU_INT_EVENT_RESET_DONE BIT(3) + +/* PLE */ +#define MT_PLE_BASE 0x820c0000 +#define MT_PLE(ofs) (MT_PLE_BASE + (ofs)) + +#define MT_FL_Q_EMPTY MT_PLE(0x360) +#define MT_FL_Q0_CTRL MT_PLE(0x3e0) +#define MT_FL_Q2_CTRL MT_PLE(0x3e8) +#define MT_FL_Q3_CTRL MT_PLE(0x3ec) + +#define MT_PLE_FREEPG_CNT MT_PLE(0x380) +#define MT_PLE_FREEPG_HEAD_TAIL MT_PLE(0x384) +#define MT_PLE_PG_HIF_GROUP MT_PLE(0x00c) +#define MT_PLE_HIF_PG_INFO MT_PLE(0x388) + +#define MT_PLE_AC_QEMPTY(ac, n) MT_PLE(0x600 + 0x80 * (ac) + ((n) << 2)) +#define MT_PLE_AMSDU_PACK_MSDU_CNT(n) MT_PLE(0x10e0 + ((n) << 2)) + +/* WF MDP TOP */ +#define MT_MDP_BASE 0x820cc000 +#define MT_MDP(ofs) (MT_MDP_BASE + (ofs)) + +#define MT_MDP_DCR2 MT_MDP(0x8e8) +#define MT_MDP_DCR2_RX_TRANS_SHORT BIT(2) + +/* TMAC: band 0(0x820e4000), band 1(0x820f4000), band 2(0x830e4000) */ +#define MT_WF_TMAC_BASE(_band) __BASE(WF_TMAC_BASE, (_band)) +#define MT_WF_TMAC(_band, ofs) (MT_WF_TMAC_BASE(_band) + (ofs)) + +#define MT_TMAC_TCR0(_band) MT_WF_TMAC(_band, 0) +#define MT_TMAC_TCR0_TX_BLINK GENMASK(7, 6) + +#define MT_TMAC_CDTR(_band) MT_WF_TMAC(_band, 0x0c8) +#define MT_TMAC_ODTR(_band) MT_WF_TMAC(_band, 0x0cc) +#define MT_TIMEOUT_VAL_PLCP GENMASK(15, 0) +#define MT_TIMEOUT_VAL_CCA GENMASK(31, 16) + +#define MT_TMAC_ICR0(_band) MT_WF_TMAC(_band, 0x014) +#define MT_IFS_EIFS_OFDM GENMASK(8, 0) +#define MT_IFS_RIFS GENMASK(14, 10) +#define MT_IFS_SIFS GENMASK(22, 16) +#define MT_IFS_SLOT GENMASK(30, 24) + +#define MT_TMAC_ICR1(_band) MT_WF_TMAC(_band, 0x018) +#define MT_IFS_EIFS_CCK GENMASK(8, 0) + +/* WF DMA TOP: band 0(0x820e7000), band 1(0x820f7000), band 2(0x830e7000) */ +#define MT_WF_DMA_BASE(_band) __BASE(WF_DMA_BASE, (_band)) +#define MT_WF_DMA(_band, ofs) (MT_WF_DMA_BASE(_band) + (ofs)) + +#define MT_DMA_DCR0(_band) MT_WF_DMA(_band, 0x000) +#define MT_DMA_DCR0_RXD_G5_EN BIT(23) + +#define MT_DMA_TCRF1(_band) MT_WF_DMA(_band, 0x054) +#define MT_DMA_TCRF1_QIDX GENMASK(15, 13) + +/* ETBF: band 0(0x820ea000), band 1(0x820fa000), band 2(0x830ea000) */ +#define MT_WF_ETBF_BASE(_band) __BASE(WF_ETBF_BASE, (_band)) +#define MT_WF_ETBF(_band, ofs) (MT_WF_ETBF_BASE(_band) + (ofs)) + +#define MT_ETBF_RX_FB_CONT(_band) MT_WF_ETBF(_band, 0x100) +#define MT_ETBF_RX_FB_BW GENMASK(10, 8) +#define MT_ETBF_RX_FB_NC GENMASK(7, 4) +#define MT_ETBF_RX_FB_NR GENMASK(3, 0) + +/* LPON: band 0(0x820eb000), band 1(0x820fb000), band 2(0x830eb000) */ +#define MT_WF_LPON_BASE(_band) __BASE(WF_LPON_BASE, (_band)) +#define MT_WF_LPON(_band, ofs) (MT_WF_LPON_BASE(_band) + (ofs)) + +#define MT_LPON_UTTR0(_band) MT_WF_LPON(_band, 0x360) +#define MT_LPON_UTTR1(_band) MT_WF_LPON(_band, 0x364) +#define MT_LPON_FRCR(_band) MT_WF_LPON(_band, 0x37c) + +#define MT_LPON_TCR(_band, n) MT_WF_LPON(_band, 0x0a8 + (((n) * 4) << 4)) +#define MT_LPON_TCR_SW_MODE GENMASK(1, 0) +#define MT_LPON_TCR_SW_WRITE BIT(0) +#define MT_LPON_TCR_SW_ADJUST BIT(1) +#define MT_LPON_TCR_SW_READ GENMASK(1, 0) + +/* MIB: band 0(0x820ed000), band 1(0x820fd000), band 2(0x830ed000)*/ +/* These counters are (mostly?) clear-on-read. So, some should not + * be read at all in case firmware is already reading them. These + * are commented with 'DNR' below. The DNR stats will be read by querying + * the firmware API for the appropriate message. For counters the driver + * does read, the driver should accumulate the counters. + */ +#define MT_WF_MIB_BASE(_band) __BASE(WF_MIB_BASE, (_band)) +#define MT_WF_MIB(_band, ofs) (MT_WF_MIB_BASE(_band) + (ofs)) + +#define MT_MIB_BSCR0(_band) MT_WF_MIB(_band, 0x9cc) +#define MT_MIB_BSCR1(_band) MT_WF_MIB(_band, 0x9d0) +#define MT_MIB_BSCR2(_band) MT_WF_MIB(_band, 0x9d4) +#define MT_MIB_BSCR3(_band) MT_WF_MIB(_band, 0x9d8) +#define MT_MIB_BSCR4(_band) MT_WF_MIB(_band, 0x9dc) +#define MT_MIB_BSCR5(_band) MT_WF_MIB(_band, 0x9e0) +#define MT_MIB_BSCR6(_band) MT_WF_MIB(_band, 0x9e4) +#define MT_MIB_BSCR7(_band) MT_WF_MIB(_band, 0x9e8) +#define MT_MIB_BSCR17(_band) MT_WF_MIB(_band, 0xa10) + +#define MT_MIB_TSCR5(_band) MT_WF_MIB(_band, 0x6c4) +#define MT_MIB_TSCR6(_band) MT_WF_MIB(_band, 0x6c8) +#define MT_MIB_TSCR7(_band) MT_WF_MIB(_band, 0x6d0) + +#define MT_MIB_RSCR1(_band) MT_WF_MIB(_band, 0x7ac) +/* rx mpdu counter, full 32 bits */ +#define MT_MIB_RSCR31(_band) MT_WF_MIB(_band, 0x964) +#define MT_MIB_RSCR33(_band) MT_WF_MIB(_band, 0x96c) + +#define MT_MIB_SDR6(_band) MT_WF_MIB(_band, 0x020) +#define MT_MIB_SDR6_CHANNEL_IDL_CNT_MASK GENMASK(15, 0) + +#define MT_MIB_RVSR0(_band) MT_WF_MIB(_band, 0x720) + +#define MT_MIB_RSCR35(_band) MT_WF_MIB(_band, 0x974) +#define MT_MIB_RSCR36(_band) MT_WF_MIB(_band, 0x978) + +/* tx ampdu cnt, full 32 bits */ +#define MT_MIB_TSCR0(_band) MT_WF_MIB(_band, 0x6b0) +#define MT_MIB_TSCR2(_band) MT_WF_MIB(_band, 0x6b8) + +/* counts all mpdus in ampdu, regardless of success */ +#define MT_MIB_TSCR3(_band) MT_WF_MIB(_band, 0x6bc) + +/* counts all successfully tx'd mpdus in ampdu */ +#define MT_MIB_TSCR4(_band) MT_WF_MIB(_band, 0x6c0) + +/* rx ampdu count, 32-bit */ +#define MT_MIB_RSCR27(_band) MT_WF_MIB(_band, 0x954) + +/* rx ampdu bytes count, 32-bit */ +#define MT_MIB_RSCR28(_band) MT_WF_MIB(_band, 0x958) + +/* rx ampdu valid subframe count */ +#define MT_MIB_RSCR29(_band) MT_WF_MIB(_band, 0x95c) + +/* rx ampdu valid subframe bytes count, 32bits */ +#define MT_MIB_RSCR30(_band) MT_WF_MIB(_band, 0x960) + +/* remaining windows protected stats */ +#define MT_MIB_SDR27(_band) MT_WF_MIB(_band, 0x080) +#define MT_MIB_SDR27_TX_RWP_FAIL_CNT GENMASK(15, 0) + +#define MT_MIB_SDR28(_band) MT_WF_MIB(_band, 0x084) +#define MT_MIB_SDR28_TX_RWP_NEED_CNT GENMASK(15, 0) + +#define MT_MIB_RVSR1(_band) MT_WF_MIB(_band, 0x724) + +/* rx blockack count, 32 bits */ +#define MT_MIB_TSCR1(_band) MT_WF_MIB(_band, 0x6b4) + +#define MT_MIB_BTSCR0(_band) MT_WF_MIB(_band, 0x5e0) +#define MT_MIB_BTSCR5(_band) MT_WF_MIB(_band, 0x788) +#define MT_MIB_BTSCR6(_band) MT_WF_MIB(_band, 0x798) + +#define MT_MIB_BFTFCR(_band) MT_WF_MIB(_band, 0x5d0) + +#define MT_TX_AGG_CNT(_band, n) MT_WF_MIB(_band, 0xa28 + ((n) << 2)) +#define MT_MIB_ARNG(_band, n) MT_WF_MIB(_band, 0x0b0 + ((n) << 2)) +#define MT_MIB_ARNCR_RANGE(val, n) (((val) >> ((n) << 4)) & GENMASK(9, 0)) + +/* UMIB */ +#define MT_WF_UMIB_BASE 0x820cd000 +#define MT_WF_UMIB(ofs) (MT_WF_UMIB_BASE + (ofs)) + +#define MT_UMIB_RPDCR(_band) (MT_WF_UMIB(0x594) + (_band) * 0x164) + +/* WTBLON TOP */ +#define MT_WTBLON_TOP_BASE 0x820d4000 +#define MT_WTBLON_TOP(ofs) (MT_WTBLON_TOP_BASE + (ofs)) +#define MT_WTBLON_TOP_WDUCR MT_WTBLON_TOP(0x370) +#define MT_WTBLON_TOP_WDUCR_GROUP GENMASK(4, 0) + +#define MT_WTBL_UPDATE MT_WTBLON_TOP(0x380) +#define MT_WTBL_UPDATE_WLAN_IDX GENMASK(11, 0) +#define MT_WTBL_UPDATE_ADM_COUNT_CLEAR BIT(14) +#define MT_WTBL_UPDATE_BUSY BIT(31) + +/* WTBL */ +#define MT_WTBL_BASE 0x820d8000 +#define MT_WTBL_LMAC_ID GENMASK(14, 8) +#define MT_WTBL_LMAC_DW GENMASK(7, 2) +#define MT_WTBL_LMAC_OFFS(_id, _dw) (MT_WTBL_BASE | \ + FIELD_PREP(MT_WTBL_LMAC_ID, _id) | \ + FIELD_PREP(MT_WTBL_LMAC_DW, _dw)) + +/* AGG: band 0(0x820e2000), band 1(0x820f2000), band 2(0x830e2000) */ +#define MT_WF_AGG_BASE(_band) __BASE(WF_AGG_BASE, (_band)) +#define MT_WF_AGG(_band, ofs) (MT_WF_AGG_BASE(_band) + (ofs)) + +#define MT_AGG_ACR0(_band) MT_WF_AGG(_band, 0x054) +#define MT_AGG_ACR_CFEND_RATE GENMASK(13, 0) + +/* ARB: band 0(0x820e3000), band 1(0x820f3000), band 2(0x830e3000) */ +#define MT_WF_ARB_BASE(_band) __BASE(WF_ARB_BASE, (_band)) +#define MT_WF_ARB(_band, ofs) (MT_WF_ARB_BASE(_band) + (ofs)) + +#define MT_ARB_SCR(_band) MT_WF_ARB(_band, 0x000) +#define MT_ARB_SCR_TX_DISABLE BIT(8) +#define MT_ARB_SCR_RX_DISABLE BIT(9) + +/* RMAC: band 0(0x820e5000), band 1(0x820f5000), band 2(0x830e5000), */ +#define MT_WF_RMAC_BASE(_band) __BASE(WF_RMAC_BASE, (_band)) +#define MT_WF_RMAC(_band, ofs) (MT_WF_RMAC_BASE(_band) + (ofs)) + +#define MT_WF_RFCR(_band) MT_WF_RMAC(_band, 0x000) +#define MT_WF_RFCR_DROP_STBC_MULTI BIT(0) +#define MT_WF_RFCR_DROP_FCSFAIL BIT(1) +#define MT_WF_RFCR_DROP_PROBEREQ BIT(4) +#define MT_WF_RFCR_DROP_MCAST BIT(5) +#define MT_WF_RFCR_DROP_BCAST BIT(6) +#define MT_WF_RFCR_DROP_MCAST_FILTERED BIT(7) +#define MT_WF_RFCR_DROP_A3_MAC BIT(8) +#define MT_WF_RFCR_DROP_A3_BSSID BIT(9) +#define MT_WF_RFCR_DROP_A2_BSSID BIT(10) +#define MT_WF_RFCR_DROP_OTHER_BEACON BIT(11) +#define MT_WF_RFCR_DROP_FRAME_REPORT BIT(12) +#define MT_WF_RFCR_DROP_CTL_RSV BIT(13) +#define MT_WF_RFCR_DROP_CTS BIT(14) +#define MT_WF_RFCR_DROP_RTS BIT(15) +#define MT_WF_RFCR_DROP_DUPLICATE BIT(16) +#define MT_WF_RFCR_DROP_OTHER_BSS BIT(17) +#define MT_WF_RFCR_DROP_OTHER_UC BIT(18) +#define MT_WF_RFCR_DROP_OTHER_TIM BIT(19) +#define MT_WF_RFCR_DROP_NDPA BIT(20) +#define MT_WF_RFCR_DROP_UNWANTED_CTL BIT(21) + +#define MT_WF_RFCR1(_band) MT_WF_RMAC(_band, 0x004) +#define MT_WF_RFCR1_DROP_ACK BIT(4) +#define MT_WF_RFCR1_DROP_BF_POLL BIT(5) +#define MT_WF_RFCR1_DROP_BA BIT(6) +#define MT_WF_RFCR1_DROP_CFEND BIT(7) +#define MT_WF_RFCR1_DROP_CFACK BIT(8) + +#define MT_WF_RMAC_MIB_AIRTIME0(_band) MT_WF_RMAC(_band, 0x0380) +#define MT_WF_RMAC_MIB_RXTIME_CLR BIT(31) +#define MT_WF_RMAC_MIB_ED_OFFSET GENMASK(20, 16) +#define MT_WF_RMAC_MIB_OBSS_BACKOFF GENMASK(15, 0) + +#define MT_WF_RMAC_MIB_AIRTIME1(_band) MT_WF_RMAC(_band, 0x0384) +#define MT_WF_RMAC_MIB_NONQOSD_BACKOFF GENMASK(31, 16) + +#define MT_WF_RMAC_MIB_AIRTIME3(_band) MT_WF_RMAC(_band, 0x038c) +#define MT_WF_RMAC_MIB_QOS01_BACKOFF GENMASK(31, 0) + +#define MT_WF_RMAC_MIB_AIRTIME4(_band) MT_WF_RMAC(_band, 0x0390) +#define MT_WF_RMAC_MIB_QOS23_BACKOFF GENMASK(31, 0) + +#define MT_WF_RMAC_RSVD0(_band) MT_WF_RMAC(_band, 0x03e0) +#define MT_WF_RMAC_RSVD0_EIFS_CLR BIT(21) + +/* WFDMA0 */ +#define MT_WFDMA0_BASE 0xd4000 +#define MT_WFDMA0(ofs) (MT_WFDMA0_BASE + (ofs)) + +#define MT_WFDMA0_RST MT_WFDMA0(0x100) +#define MT_WFDMA0_RST_LOGIC_RST BIT(4) +#define MT_WFDMA0_RST_DMASHDL_ALL_RST BIT(5) + +#define MT_WFDMA0_BUSY_ENA MT_WFDMA0(0x13c) +#define MT_WFDMA0_BUSY_ENA_TX_FIFO0 BIT(0) +#define MT_WFDMA0_BUSY_ENA_TX_FIFO1 BIT(1) +#define MT_WFDMA0_BUSY_ENA_RX_FIFO BIT(2) + +#define MT_WFDMA0_RX_INT_PCIE_SEL MT_WFDMA0(0x154) +#define MT_WFDMA0_RX_INT_SEL_RING3 BIT(3) + +#define MT_WFDMA0_GLO_CFG MT_WFDMA0(0x208) +#define MT_WFDMA0_GLO_CFG_TX_DMA_EN BIT(0) +#define MT_WFDMA0_GLO_CFG_RX_DMA_EN BIT(2) +#define MT_WFDMA0_GLO_CFG_OMIT_TX_INFO BIT(28) +#define MT_WFDMA0_GLO_CFG_OMIT_RX_INFO BIT(27) +#define MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2 BIT(21) + +#define WF_WFDMA0_GLO_CFG_EXT0 MT_WFDMA0(0x2b0) +#define WF_WFDMA0_GLO_CFG_EXT0_RX_WB_RXD BIT(18) +#define WF_WFDMA0_GLO_CFG_EXT0_WED_MERGE_MODE BIT(14) + +#define WF_WFDMA0_GLO_CFG_EXT1 MT_WFDMA0(0x2b4) +#define WF_WFDMA0_GLO_CFG_EXT1_CALC_MODE BIT(31) +#define WF_WFDMA0_GLO_CFG_EXT1_TX_FCTRL_MODE BIT(28) + +#define MT_WFDMA0_RST_DTX_PTR MT_WFDMA0(0x20c) +#define MT_WFDMA0_PRI_DLY_INT_CFG0 MT_WFDMA0(0x2f0) +#define MT_WFDMA0_PRI_DLY_INT_CFG1 MT_WFDMA0(0x2f4) +#define MT_WFDMA0_PRI_DLY_INT_CFG2 MT_WFDMA0(0x2f8) + +/* WFDMA1 */ +#define MT_WFDMA1_BASE 0xd5000 + +/* WFDMA CSR */ +#define MT_WFDMA_EXT_CSR_BASE 0xd7000 +#define MT_WFDMA_EXT_CSR(ofs) (MT_WFDMA_EXT_CSR_BASE + (ofs)) + +#define MT_WFDMA_HOST_CONFIG MT_WFDMA_EXT_CSR(0x30) +#define MT_WFDMA_HOST_CONFIG_PDMA_BAND BIT(0) + +#define MT_WFDMA_EXT_CSR_HIF_MISC MT_WFDMA_EXT_CSR(0x44) +#define MT_WFDMA_EXT_CSR_HIF_MISC_BUSY BIT(0) + +#define MT_PCIE_RECOG_ID 0xd7090 +#define MT_PCIE_RECOG_ID_MASK GENMASK(30, 0) +#define MT_PCIE_RECOG_ID_SEM BIT(31) + +/* WFDMA0 PCIE1 */ +#define MT_WFDMA0_PCIE1_BASE 0xd8000 +#define MT_WFDMA0_PCIE1(ofs) (MT_WFDMA0_PCIE1_BASE + (ofs)) + +#define MT_WFDMA0_PCIE1_BUSY_ENA MT_WFDMA0_PCIE1(0x13c) +#define MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO0 BIT(0) +#define MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO1 BIT(1) +#define MT_WFDMA0_PCIE1_BUSY_ENA_RX_FIFO BIT(2) + +/* WFDMA COMMON */ +#define __RXQ(q) ((q) + __MT_MCUQ_MAX) +#define __TXQ(q) (__RXQ(q) + __MT_RXQ_MAX) + +#define MT_Q_ID(q) (dev->q_id[(q)]) +#define MT_Q_BASE(q) ((dev->q_wfdma_mask >> (q)) & 0x1 ? \ + MT_WFDMA1_BASE : MT_WFDMA0_BASE) + +#define MT_MCUQ_ID(q) MT_Q_ID(q) +#define MT_TXQ_ID(q) MT_Q_ID(__TXQ(q)) +#define MT_RXQ_ID(q) MT_Q_ID(__RXQ(q)) + +#define MT_MCUQ_RING_BASE(q) (MT_Q_BASE(q) + 0x300) +#define MT_TXQ_RING_BASE(q) (MT_Q_BASE(__TXQ(q)) + 0x300) +#define MT_RXQ_RING_BASE(q) (MT_Q_BASE(__RXQ(q)) + 0x500) + +#define MT_MCUQ_EXT_CTRL(q) (MT_Q_BASE(q) + 0x600 + \ + MT_MCUQ_ID(q) * 0x4) +#define MT_RXQ_BAND1_CTRL(q) (MT_Q_BASE(__RXQ(q)) + 0x680 + \ + MT_RXQ_ID(q) * 0x4) +#define MT_TXQ_EXT_CTRL(q) (MT_Q_BASE(__TXQ(q)) + 0x600 + \ + MT_TXQ_ID(q) * 0x4) + +#define MT_INT_SOURCE_CSR MT_WFDMA0(0x200) +#define MT_INT_MASK_CSR MT_WFDMA0(0x204) + +#define MT_INT1_SOURCE_CSR MT_WFDMA0_PCIE1(0x200) +#define MT_INT1_MASK_CSR MT_WFDMA0_PCIE1(0x204) + +#define MT_INT_RX_DONE_BAND0 BIT(12) +#define MT_INT_RX_DONE_BAND1 BIT(12) +#define MT_INT_RX_DONE_BAND2 BIT(13) +#define MT_INT_RX_DONE_WM BIT(0) +#define MT_INT_RX_DONE_WA BIT(1) +#define MT_INT_RX_DONE_WA_MAIN BIT(2) +#define MT_INT_RX_DONE_WA_EXT BIT(2) +#define MT_INT_RX_DONE_WA_TRI BIT(3) +#define MT_INT_RX_TXFREE_MAIN BIT(17) +#define MT_INT_RX_TXFREE_TRI BIT(15) +#define MT_INT_MCU_CMD BIT(29) + +#define MT_INT_RX(q) (dev->q_int_mask[__RXQ(q)]) +#define MT_INT_TX_MCU(q) (dev->q_int_mask[(q)]) + +#define MT_INT_RX_DONE_MCU (MT_INT_RX(MT_RXQ_MCU) | \ + MT_INT_RX(MT_RXQ_MCU_WA)) + +#define MT_INT_BAND0_RX_DONE (MT_INT_RX(MT_RXQ_MAIN) | \ + MT_INT_RX(MT_RXQ_MAIN_WA)) + +#define MT_INT_BAND1_RX_DONE (MT_INT_RX(MT_RXQ_BAND1) | \ + MT_INT_RX(MT_RXQ_BAND1_WA) | \ + MT_INT_RX(MT_RXQ_MAIN_WA)) + +#define MT_INT_BAND2_RX_DONE (MT_INT_RX(MT_RXQ_BAND2) | \ + MT_INT_RX(MT_RXQ_BAND2_WA) | \ + MT_INT_RX(MT_RXQ_MAIN_WA)) + +#define MT_INT_RX_DONE_ALL (MT_INT_RX_DONE_MCU | \ + MT_INT_BAND0_RX_DONE | \ + MT_INT_BAND1_RX_DONE | \ + MT_INT_BAND2_RX_DONE) + +#define MT_INT_TX_DONE_FWDL BIT(26) +#define MT_INT_TX_DONE_MCU_WM BIT(27) +#define MT_INT_TX_DONE_MCU_WA BIT(22) +#define MT_INT_TX_DONE_BAND0 BIT(30) +#define MT_INT_TX_DONE_BAND1 BIT(31) +#define MT_INT_TX_DONE_BAND2 BIT(15) + +#define MT_INT_TX_DONE_MCU (MT_INT_TX_MCU(MT_MCUQ_WA) | \ + MT_INT_TX_MCU(MT_MCUQ_WM) | \ + MT_INT_TX_MCU(MT_MCUQ_FWDL)) + +#define MT_MCU_CMD MT_WFDMA0(0x1f0) +#define MT_MCU_CMD_STOP_DMA BIT(2) +#define MT_MCU_CMD_RESET_DONE BIT(3) +#define MT_MCU_CMD_RECOVERY_DONE BIT(4) +#define MT_MCU_CMD_NORMAL_STATE BIT(5) +#define MT_MCU_CMD_ERROR_MASK GENMASK(5, 1) + +/* l1/l2 remap */ +#define MT_HIF_REMAP_L1 0x155024 +#define MT_HIF_REMAP_L1_MASK GENMASK(31, 16) +#define MT_HIF_REMAP_L1_OFFSET GENMASK(15, 0) +#define MT_HIF_REMAP_L1_BASE GENMASK(31, 16) +#define MT_HIF_REMAP_BASE_L1 0x130000 + +#define MT_HIF_REMAP_L2 0x1b4 +#define MT_HIF_REMAP_L2_MASK GENMASK(19, 0) +#define MT_HIF_REMAP_L2_OFFSET GENMASK(11, 0) +#define MT_HIF_REMAP_L2_BASE GENMASK(31, 12) +#define MT_HIF_REMAP_BASE_L2 0x1000 + +#define MT_INFRA_BASE 0x18000000 +#define MT_WFSYS0_PHY_START 0x18400000 +#define MT_WFSYS1_PHY_START 0x18800000 +#define MT_WFSYS1_PHY_END 0x18bfffff +#define MT_CBTOP1_PHY_START 0x70000000 +#define MT_CBTOP1_PHY_END 0x77ffffff +#define MT_CBTOP2_PHY_START 0xf0000000 +#define MT_CBTOP2_PHY_END 0xffffffff +#define MT_INFRA_MCU_START 0x7c000000 +#define MT_INFRA_MCU_END 0x7c3fffff + +/* FW MODE SYNC */ +#define MT_SWDEF_MODE 0x9143c +#define MT_SWDEF_NORMAL_MODE 0 + +/* LED */ +#define MT_LED_TOP_BASE 0x18013000 +#define MT_LED_PHYS(_n) (MT_LED_TOP_BASE + (_n)) + +#define MT_LED_CTRL(_n) MT_LED_PHYS(0x00 + ((_n) * 4)) +#define MT_LED_CTRL_KICK BIT(7) +#define MT_LED_CTRL_BLINK_MODE BIT(2) +#define MT_LED_CTRL_POLARITY BIT(1) + +#define MT_LED_TX_BLINK(_n) MT_LED_PHYS(0x10 + ((_n) * 4)) +#define MT_LED_TX_BLINK_ON_MASK GENMASK(7, 0) +#define MT_LED_TX_BLINK_OFF_MASK GENMASK(15, 8) + +#define MT_LED_EN(_n) MT_LED_PHYS(0x40 + ((_n) * 4)) + +#define MT_LED_GPIO_MUX2 0x70005058 /* GPIO 18 */ +#define MT_LED_GPIO_MUX3 0x7000505C /* GPIO 26 */ +#define MT_LED_GPIO_SEL_MASK GENMASK(11, 8) + +/* MT TOP */ +#define MT_TOP_BASE 0xe0000 +#define MT_TOP(ofs) (MT_TOP_BASE + (ofs)) + +#define MT_TOP_LPCR_HOST_BAND(_band) MT_TOP(0x10 + ((_band) * 0x10)) +#define MT_TOP_LPCR_HOST_FW_OWN BIT(0) +#define MT_TOP_LPCR_HOST_DRV_OWN BIT(1) +#define MT_TOP_LPCR_HOST_FW_OWN_STAT BIT(2) + +#define MT_TOP_LPCR_HOST_BAND_IRQ_STAT(_band) MT_TOP(0x14 + ((_band) * 0x10)) +#define MT_TOP_LPCR_HOST_BAND_STAT BIT(0) + +#define MT_TOP_MISC MT_TOP(0xf0) +#define MT_TOP_MISC_FW_STATE GENMASK(2, 0) + +#define MT_HW_REV 0x70010204 +#define MT_WF_SUBSYS_RST 0x70002600 + +/* PCIE MAC */ +#define MT_PCIE_MAC_BASE 0x74030000 +#define MT_PCIE_MAC(ofs) (MT_PCIE_MAC_BASE + (ofs)) +#define MT_PCIE_MAC_INT_ENABLE MT_PCIE_MAC(0x188) + +#define MT_PCIE1_MAC_BASE 0x74090000 +#define MT_PCIE1_MAC(ofs) (MT_PCIE1_MAC_BASE + (ofs)) + +#define MT_PCIE1_MAC_INT_ENABLE MT_PCIE1_MAC(0x188) + +/* PHYRX CTRL */ +#define MT_WF_PHYRX_BAND_BASE 0x83080000 +#define MT_WF_PHYRX_BAND(_band, ofs) (MT_WF_PHYRX_BAND_BASE + \ + ((_band) << 20) + (ofs)) + +#define MT_WF_PHYRX_BAND_RX_CTRL1(_band) MT_WF_PHYRX_BAND(_band, 0x2004) +#define MT_WF_PHYRX_BAND_RX_CTRL1_IPI_EN GENMASK(2, 0) +#define MT_WF_PHYRX_BAND_RX_CTRL1_STSCNT_EN GENMASK(11, 9) + +/* PHYRX CSD */ +#define MT_WF_PHYRX_CSD_BASE 0x83000000 +#define MT_WF_PHYRX_CSD(_band, _wf, ofs) (MT_WF_PHYRX_CSD_BASE + \ + ((_band) << 20) + \ + ((_wf) << 16) + (ofs)) +#define MT_WF_PHYRX_CSD_IRPI(_band, _wf) MT_WF_PHYRX_CSD(_band, _wf, 0x1000) + +/* PHYRX CSD BAND */ +#define MT_WF_PHYRX_CSD_BAND_RXTD12(_band) MT_WF_PHYRX_BAND(_band, 0x8230) +#define MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR_ONLY BIT(18) +#define MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR BIT(29) + +#endif From 03dd0d49de7db680a856fa566963bb8421f46368 Mon Sep 17 00:00:00 2001 From: Deren Wu Date: Sun, 27 Nov 2022 10:35:37 +0800 Subject: [PATCH 132/144] wifi: mt76: fix coverity overrun-call in mt76_get_txpower() Make sure the nss is valid for nss_delta array. Return zero if the index is invalid. Coverity message: Event overrun-call: Overrunning callee's array of size 4 by passing argument "n_chains" (which evaluates to 15) in call to "mt76_tx_power_nss_delta". int delta = mt76_tx_power_nss_delta(n_chains); Fixes: 07cda406308b ("mt76: fix rounding issues on converting per-chain and combined txpower") Signed-off-by: Deren Wu Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index 33f87e518d68..32a77a0ae9da 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -1110,8 +1110,9 @@ static inline bool mt76_is_skb_pktid(u8 pktid) static inline u8 mt76_tx_power_nss_delta(u8 nss) { static const u8 nss_delta[4] = { 0, 6, 9, 12 }; + u8 idx = nss - 1; - return nss_delta[nss - 1]; + return (idx < ARRAY_SIZE(nss_delta)) ? nss_delta[idx] : 0; } static inline bool mt76_testmode_enabled(struct mt76_phy *phy) From e5c6bc6f19d8c293f86b347cddab54d5a3830b38 Mon Sep 17 00:00:00 2001 From: Deren Wu Date: Mon, 28 Nov 2022 15:04:21 +0800 Subject: [PATCH 133/144] wifi: mt76: mt7921: Add missing __packed annotation of struct mt7921_clc Add __packed annotation to avoid potential CLC parsing error Fixes: 23bdc5d8cadf ("wifi: mt76: mt7921: introduce Country Location Control support") Signed-off-by: Deren Wu Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h index 6fc04ed34ec3..15d6b7fe1c6c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h @@ -224,7 +224,7 @@ struct mt7921_clc { u8 type; u8 rsv[8]; u8 data[]; -}; +} __packed; struct mt7921_phy { struct mt76_phy *mt76; From f37f76d43865c58cb96aa13c87164abb41f22d0b Mon Sep 17 00:00:00 2001 From: Deren Wu Date: Thu, 24 Nov 2022 22:20:38 +0800 Subject: [PATCH 134/144] wifi: mt76: do not send firmware FW_FEATURE_NON_DL region skip invalid section to avoid potential risks Fixes: 23bdc5d8cadf ("wifi: mt76: mt7921: introduce Country Location Control support") Signed-off-by: Deren Wu Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index 96d91238ad8d..5a047e630860 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -2906,6 +2906,9 @@ mt76_connac_mcu_send_ram_firmware(struct mt76_dev *dev, len = le32_to_cpu(region->len); addr = le32_to_cpu(region->addr); + if (region->feature_set & FW_FEATURE_NON_DL) + goto next; + if (region->feature_set & FW_FEATURE_OVERRIDE_ADDR) override = addr; @@ -2922,6 +2925,7 @@ mt76_connac_mcu_send_ram_firmware(struct mt76_dev *dev, return err; } +next: offset += len; } From 5938196cc188ba4323bc6357f5ac55127d715888 Mon Sep 17 00:00:00 2001 From: Xiongfeng Wang Date: Fri, 25 Nov 2022 10:58:31 +0800 Subject: [PATCH 135/144] mt76: mt7915: Fix PCI device refcount leak in mt7915_pci_init_hif2() As comment of pci_get_device() says, it returns a pci_device with its refcount increased. We need to call pci_dev_put() to decrease the refcount. Save the return value of pci_get_device() and call pci_dev_put() to decrease the refcount. Fixes: 9093cfff72e3 ("mt76: mt7915: add support for using a secondary PCIe link for gen1") Fixes: 2e30db0dde61 ("mt76: mt7915: add device id for mt7916") Signed-off-by: Xiongfeng Wang Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/pci.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/pci.c b/drivers/net/wireless/mediatek/mt76/mt7915/pci.c index 743f01f6eae9..39132894e8ea 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/pci.c @@ -62,10 +62,17 @@ static void mt7915_put_hif2(struct mt7915_hif *hif) static struct mt7915_hif *mt7915_pci_init_hif2(struct pci_dev *pdev) { + struct pci_dev *tmp_pdev; + hif_idx++; - if (!pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x7916, NULL) && - !pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x790a, NULL)) - return NULL; + + tmp_pdev = pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x7916, NULL); + if (!tmp_pdev) { + tmp_pdev = pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x790a, NULL); + if (!tmp_pdev) + return NULL; + } + pci_dev_put(tmp_pdev); writel(hif_idx | MT_PCIE_RECOG_ID_SEM, pcim_iomap_table(pdev)[0] + MT_PCIE_RECOG_ID); From 54dd1dc796f7b5ad12e8aa02945cc669bc870591 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Thu, 24 Nov 2022 03:59:10 +0800 Subject: [PATCH 136/144] wifi: mt76: mt7915: introduce mt7915_get_power_bound() Add a helper for common boundary check. This is a preliminary patch to add per bandwidth power control. Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/mcu.c | 12 +++++------- drivers/net/wireless/mediatek/mt76/mt7915/mcu.h | 12 ++++++++++++ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 718e52744994..b640ce9249c9 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -3144,14 +3144,14 @@ int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy, .txpower_max = DIV_ROUND_UP(mphy->txpower_cur, 2), .wcid = cpu_to_le16(msta->wcid.idx), }; - int ret, n_chains = hweight8(mphy->antenna_mask); + int ret; s8 txpower_sku[MT7915_SKU_RATE_NUM]; ret = mt7915_mcu_get_txpower_sku(phy, txpower_sku, sizeof(txpower_sku)); if (ret) return ret; - txpower = txpower * 2 - mt76_tx_power_nss_delta(n_chains); + txpower = mt7915_get_power_bound(phy, txpower); if (txpower > mphy->txpower_cur || txpower < 0) return -EINVAL; @@ -3203,12 +3203,10 @@ int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy) }; struct mt76_power_limits limits_array; s8 *la = (s8 *)&limits_array; - int i, idx, n_chains = hweight8(mphy->antenna_mask); - int tx_power = hw->conf.power_level * 2; + int i, idx; + int tx_power; - tx_power = mt76_get_sar_power(mphy, mphy->chandef.chan, - tx_power); - tx_power -= mt76_tx_power_nss_delta(n_chains); + tx_power = mt7915_get_power_bound(phy, hw->conf.power_level); tx_power = mt76_get_rate_power_limits(mphy, mphy->chandef.chan, &limits_array, tx_power); mphy->txpower_cur = tx_power; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h index 46c517e50ae4..382dcbda50bb 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h @@ -509,4 +509,16 @@ enum { sizeof(struct bss_info_bcn_cont) + \ sizeof(struct bss_info_inband_discovery)) +static inline s8 +mt7915_get_power_bound(struct mt7915_phy *phy, s8 txpower) +{ + struct mt76_phy *mphy = phy->mt76; + int n_chains = hweight8(mphy->antenna_mask); + + txpower = mt76_get_sar_power(mphy, mphy->chandef.chan, txpower * 2); + txpower -= mt76_tx_power_nss_delta(n_chains); + + return txpower; +} + #endif From e3296759f34752ea2562678706dbb5bf607af530 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Thu, 24 Nov 2022 03:59:11 +0800 Subject: [PATCH 137/144] wifi: mt76: mt7915: enable per bandwidth power limit support This power should override the per bandwidth max power that the device emits. Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt7915/debugfs.c | 204 +++++++++++++++--- .../net/wireless/mediatek/mt76/mt7915/mcu.c | 9 +- .../net/wireless/mediatek/mt76/mt7915/mcu.h | 7 + 3 files changed, 186 insertions(+), 34 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c index fef0ec83185b..8e586d101438 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c @@ -957,48 +957,198 @@ mt7915_xmit_queues_show(struct seq_file *file, void *data) DEFINE_SHOW_ATTRIBUTE(mt7915_xmit_queues); -static int -mt7915_rate_txpower_show(struct seq_file *file, void *data) -{ - static const char * const sku_group_name[] = { - "CCK", "OFDM", "HT20", "HT40", - "VHT20", "VHT40", "VHT80", "VHT160", - "RU26", "RU52", "RU106", "RU242/SU20", - "RU484/SU40", "RU996/SU80", "RU2x996/SU160" - }; - struct mt7915_phy *phy = file->private; - struct mt7915_dev *dev = phy->dev; - s8 txpower[MT7915_SKU_RATE_NUM], *buf; - u32 reg; - int i, ret; +#define mt7915_txpower_puts(prefix, rate) \ +({ \ + len += scnprintf(buf + len, sz - len, "%-16s:", #prefix " (tmac)"); \ + for (i = 0; i < mt7915_sku_group_len[rate]; i++, offs++) \ + len += scnprintf(buf + len, sz - len, " %6d", txpwr[offs]); \ + len += scnprintf(buf + len, sz - len, "\n"); \ +}) - ret = mt7915_mcu_get_txpower_sku(phy, txpower, sizeof(txpower)); +#define mt7915_txpower_sets(rate, pwr, flag) \ +({ \ + offs += len; \ + len = mt7915_sku_group_len[rate]; \ + if (mode == flag) { \ + for (i = 0; i < len; i++) \ + req.txpower_sku[offs + i] = pwr; \ + } \ +}) + +static ssize_t +mt7915_rate_txpower_get(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct mt7915_phy *phy = file->private_data; + struct mt7915_dev *dev = phy->dev; + s8 txpwr[MT7915_SKU_RATE_NUM]; + static const size_t sz = 2048; + int i, offs = 0, len = 0; + ssize_t ret; + char *buf; + u32 reg; + + buf = kzalloc(sz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = mt7915_mcu_get_txpower_sku(phy, txpwr, sizeof(txpwr)); if (ret) return ret; /* Txpower propagation path: TMAC -> TXV -> BBP */ - seq_printf(file, "\nPhy %d\n", phy != &dev->phy); + len += scnprintf(buf + len, sz - len, + "\nPhy%d Tx power table (channel %d)\n", + phy != &dev->phy, phy->mt76->chandef.chan->hw_value); + len += scnprintf(buf + len, sz - len, "%-16s %6s %6s %6s %6s\n", + " ", "1m", "2m", "5m", "11m"); + mt7915_txpower_puts(CCK, SKU_CCK); - for (i = 0, buf = txpower; i < ARRAY_SIZE(mt7915_sku_group_len); i++) { - u8 mcs_num = mt7915_sku_group_len[i]; + len += scnprintf(buf + len, sz - len, + "%-16s %6s %6s %6s %6s %6s %6s %6s %6s\n", + " ", "6m", "9m", "12m", "18m", "24m", "36m", "48m", + "54m"); + mt7915_txpower_puts(OFDM, SKU_OFDM); - if (i >= SKU_VHT_BW20 && i <= SKU_VHT_BW160) - mcs_num = 10; + len += scnprintf(buf + len, sz - len, + "%-16s %6s %6s %6s %6s %6s %6s %6s %6s\n", + " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", + "mcs5", "mcs6", "mcs7"); + mt7915_txpower_puts(HT20, SKU_HT_BW20); - mt76_seq_puts_array(file, sku_group_name[i], buf, mcs_num); - buf += mt7915_sku_group_len[i]; - } + len += scnprintf(buf + len, sz - len, + "%-16s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n", + " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5", + "mcs6", "mcs7", "mcs32"); + mt7915_txpower_puts(HT40, SKU_HT_BW40); + + len += scnprintf(buf + len, sz - len, + "%-16s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n", + " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5", + "mcs6", "mcs7", "mcs8", "mcs9", "mcs10", "mcs11"); + mt7915_txpower_puts(VHT20, SKU_VHT_BW20); + mt7915_txpower_puts(VHT40, SKU_VHT_BW40); + mt7915_txpower_puts(VHT80, SKU_VHT_BW80); + mt7915_txpower_puts(VHT160, SKU_VHT_BW160); + mt7915_txpower_puts(HE26, SKU_HE_RU26); + mt7915_txpower_puts(HE52, SKU_HE_RU52); + mt7915_txpower_puts(HE106, SKU_HE_RU106); + mt7915_txpower_puts(HE242, SKU_HE_RU242); + mt7915_txpower_puts(HE484, SKU_HE_RU484); + mt7915_txpower_puts(HE996, SKU_HE_RU996); + mt7915_txpower_puts(HE996x2, SKU_HE_RU2x996); reg = is_mt7915(&dev->mt76) ? MT_WF_PHY_TPC_CTRL_STAT(phy->band_idx) : MT_WF_PHY_TPC_CTRL_STAT_MT7916(phy->band_idx); - seq_printf(file, "\nBaseband transmit power %ld\n", - mt76_get_field(dev, reg, MT_WF_PHY_TPC_POWER)); + len += scnprintf(buf + len, sz - len, "\nTx power (bbp) : %6ld\n", + mt76_get_field(dev, reg, MT_WF_PHY_TPC_POWER)); - return 0; + ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + return ret; } -DEFINE_SHOW_ATTRIBUTE(mt7915_rate_txpower); +static ssize_t +mt7915_rate_txpower_set(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct mt7915_phy *phy = file->private_data; + struct mt7915_dev *dev = phy->dev; + struct mt76_phy *mphy = phy->mt76; + struct mt7915_mcu_txpower_sku req = { + .format_id = TX_POWER_LIMIT_TABLE, + .band_idx = phy->band_idx, + }; + char buf[100]; + int i, ret, pwr160 = 0, pwr80 = 0, pwr40 = 0, pwr20 = 0; + enum mac80211_rx_encoding mode; + u32 offs = 0, len = 0; + + if (count >= sizeof(buf)) + return -EINVAL; + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + if (count && buf[count - 1] == '\n') + buf[count - 1] = '\0'; + else + buf[count] = '\0'; + + if (sscanf(buf, "%u %u %u %u %u", + &mode, &pwr160, &pwr80, &pwr40, &pwr20) != 5) { + dev_warn(dev->mt76.dev, + "per bandwidth power limit: Mode BW160 BW80 BW40 BW20"); + return -EINVAL; + } + + if (mode > RX_ENC_HE) + return -EINVAL; + + if (pwr160) + pwr160 = mt7915_get_power_bound(phy, pwr160); + if (pwr80) + pwr80 = mt7915_get_power_bound(phy, pwr80); + if (pwr40) + pwr40 = mt7915_get_power_bound(phy, pwr40); + if (pwr20) + pwr20 = mt7915_get_power_bound(phy, pwr20); + + if (pwr160 < 0 || pwr80 < 0 || pwr40 < 0 || pwr20 < 0) + return -EINVAL; + + mutex_lock(&dev->mt76.mutex); + ret = mt7915_mcu_get_txpower_sku(phy, req.txpower_sku, + sizeof(req.txpower_sku)); + if (ret) + goto out; + + mt7915_txpower_sets(SKU_CCK, pwr20, RX_ENC_LEGACY); + mt7915_txpower_sets(SKU_OFDM, pwr20, RX_ENC_LEGACY); + if (mode == RX_ENC_LEGACY) + goto skip; + + mt7915_txpower_sets(SKU_HT_BW20, pwr20, RX_ENC_HT); + mt7915_txpower_sets(SKU_HT_BW40, pwr40, RX_ENC_HT); + if (mode == RX_ENC_HT) + goto skip; + + mt7915_txpower_sets(SKU_VHT_BW20, pwr20, RX_ENC_VHT); + mt7915_txpower_sets(SKU_VHT_BW40, pwr40, RX_ENC_VHT); + mt7915_txpower_sets(SKU_VHT_BW80, pwr80, RX_ENC_VHT); + mt7915_txpower_sets(SKU_VHT_BW160, pwr160, RX_ENC_VHT); + if (mode == RX_ENC_VHT) + goto skip; + + mt7915_txpower_sets(SKU_HE_RU26, pwr20, RX_ENC_HE + 1); + mt7915_txpower_sets(SKU_HE_RU52, pwr20, RX_ENC_HE + 1); + mt7915_txpower_sets(SKU_HE_RU106, pwr20, RX_ENC_HE + 1); + mt7915_txpower_sets(SKU_HE_RU242, pwr20, RX_ENC_HE); + mt7915_txpower_sets(SKU_HE_RU484, pwr40, RX_ENC_HE); + mt7915_txpower_sets(SKU_HE_RU996, pwr80, RX_ENC_HE); + mt7915_txpower_sets(SKU_HE_RU2x996, pwr160, RX_ENC_HE); +skip: + ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), + &req, sizeof(req), true); + if (ret) + goto out; + + mphy->txpower_cur = max(mphy->txpower_cur, + max(pwr160, max(pwr80, max(pwr40, pwr20)))); +out: + mutex_unlock(&dev->mt76.mutex); + + return ret ? ret : count; +} + +static const struct file_operations mt7915_rate_txpower_fops = { + .write = mt7915_rate_txpower_set, + .read = mt7915_rate_txpower_get, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; static int mt7915_twt_stats(struct seq_file *s, void *data) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index b640ce9249c9..765bf634db76 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -3192,12 +3192,7 @@ int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy) struct mt7915_dev *dev = phy->dev; struct mt76_phy *mphy = phy->mt76; struct ieee80211_hw *hw = mphy->hw; - struct mt7915_sku_val { - u8 format_id; - u8 limit_type; - u8 band_idx; - s8 val[MT7915_SKU_RATE_NUM]; - } __packed req = { + struct mt7915_mcu_txpower_sku req = { .format_id = TX_POWER_LIMIT_TABLE, .band_idx = phy->band_idx, }; @@ -3225,7 +3220,7 @@ int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy) } for (j = 0; j < min_t(u8, mcs_num, len); j++) - req.val[idx + j] = la[j]; + req.txpower_sku[idx + j] = la[j]; la += mcs_num; idx += len; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h index 382dcbda50bb..29b5434bfdb8 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h @@ -184,6 +184,13 @@ enum mt7915_chan_mib_offs { MIB_NON_WIFI_TIME_V2 }; +struct mt7915_mcu_txpower_sku { + u8 format_id; + u8 limit_type; + u8 band_idx; + s8 txpower_sku[MT7915_SKU_RATE_NUM]; +} __packed; + struct edca { u8 queue; u8 set; From 3eb50cc90534376484ee0836d966c75d4c778d5f Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Thu, 1 Dec 2022 11:44:41 +0800 Subject: [PATCH 138/144] wifi: mt76: mt7915: rely on band_idx of mt76_phy The commit dc44c45c8cd0 added band_idx into mt76_phy, so switching to rely on that. Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- .../wireless/mediatek/mt76/mt7915/debugfs.c | 16 +- .../net/wireless/mediatek/mt76/mt7915/dma.c | 12 +- .../wireless/mediatek/mt76/mt7915/eeprom.c | 14 +- .../net/wireless/mediatek/mt76/mt7915/init.c | 6 +- .../net/wireless/mediatek/mt76/mt7915/mac.c | 150 +++++++++--------- .../net/wireless/mediatek/mt76/mt7915/main.c | 46 +++--- .../net/wireless/mediatek/mt76/mt7915/mcu.c | 48 +++--- .../net/wireless/mediatek/mt76/mt7915/mmio.c | 8 +- .../wireless/mediatek/mt76/mt7915/mt7915.h | 1 - .../wireless/mediatek/mt76/mt7915/testmode.c | 49 +++--- 10 files changed, 184 insertions(+), 166 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c index 8e586d101438..fb46c2c1784f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c @@ -51,7 +51,7 @@ mt7915_sys_recovery_set(struct file *file, const char __user *user_buf, { struct mt7915_phy *phy = file->private_data; struct mt7915_dev *dev = phy->dev; - bool band = phy->band_idx; + bool band = phy->mt76->band_idx; char buf[16]; int ret = 0; u16 val; @@ -692,15 +692,16 @@ mt7915_ampdu_stat_read_phy(struct mt7915_phy *phy, struct mt7915_dev *dev = phy->dev; bool ext_phy = phy != &dev->phy; int bound[15], range[4], i; + u8 band = phy->mt76->band_idx; /* Tx ampdu stat */ for (i = 0; i < ARRAY_SIZE(range); i++) - range[i] = mt76_rr(dev, MT_MIB_ARNG(phy->band_idx, i)); + range[i] = mt76_rr(dev, MT_MIB_ARNG(band, i)); for (i = 0; i < ARRAY_SIZE(bound); i++) bound[i] = MT_MIB_ARNCR_RANGE(range[i / 4], i % 4) + 1; - seq_printf(file, "\nPhy %d, Phy band %d\n", ext_phy, phy->band_idx); + seq_printf(file, "\nPhy %d, Phy band %d\n", ext_phy, band); seq_printf(file, "Length: %8d | ", bound[0]); for (i = 0; i < ARRAY_SIZE(bound) - 1; i++) @@ -983,6 +984,7 @@ mt7915_rate_txpower_get(struct file *file, char __user *user_buf, struct mt7915_dev *dev = phy->dev; s8 txpwr[MT7915_SKU_RATE_NUM]; static const size_t sz = 2048; + u8 band = phy->mt76->band_idx; int i, offs = 0, len = 0; ssize_t ret; char *buf; @@ -1038,8 +1040,8 @@ mt7915_rate_txpower_get(struct file *file, char __user *user_buf, mt7915_txpower_puts(HE996, SKU_HE_RU996); mt7915_txpower_puts(HE996x2, SKU_HE_RU2x996); - reg = is_mt7915(&dev->mt76) ? MT_WF_PHY_TPC_CTRL_STAT(phy->band_idx) : - MT_WF_PHY_TPC_CTRL_STAT_MT7916(phy->band_idx); + reg = is_mt7915(&dev->mt76) ? MT_WF_PHY_TPC_CTRL_STAT(band) : + MT_WF_PHY_TPC_CTRL_STAT_MT7916(band); len += scnprintf(buf + len, sz - len, "\nTx power (bbp) : %6ld\n", mt76_get_field(dev, reg, MT_WF_PHY_TPC_POWER)); @@ -1058,7 +1060,7 @@ mt7915_rate_txpower_set(struct file *file, const char __user *user_buf, struct mt76_phy *mphy = phy->mt76; struct mt7915_mcu_txpower_sku req = { .format_id = TX_POWER_LIMIT_TABLE, - .band_idx = phy->band_idx, + .band_idx = phy->mt76->band_idx, }; char buf[100]; int i, ret, pwr160 = 0, pwr80 = 0, pwr40 = 0, pwr20 = 0; @@ -1241,7 +1243,7 @@ int mt7915_init_debugfs(struct mt7915_phy *phy) mt7915_twt_stats); debugfs_create_file("rf_regval", 0600, dir, dev, &fops_rf_regval); - if (!dev->dbdc_support || phy->band_idx) { + if (!dev->dbdc_support || phy->mt76->band_idx) { debugfs_create_u32("dfs_hw_pattern", 0400, dir, &dev->hw_pattern); debugfs_create_file("radar_trigger", 0200, dir, dev, diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c index ae5be28fdd9d..e3fa064918bf 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c @@ -353,10 +353,10 @@ static int mt7915_dma_enable(struct mt7915_dev *dev) MT_INT_TX_DONE_MCU | MT_INT_MCU_CMD; - if (!dev->phy.band_idx) + if (!dev->phy.mt76->band_idx) irq_mask |= MT_INT_BAND0_RX_DONE; - if (dev->dbdc_support || dev->phy.band_idx) + if (dev->dbdc_support || dev->phy.mt76->band_idx) irq_mask |= MT_INT_BAND1_RX_DONE; if (mtk_wed_device_active(&dev->mt76.mmio.wed)) { @@ -418,7 +418,7 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2) /* init tx queue */ ret = mt7915_init_tx_queues(&dev->phy, - MT_TXQ_ID(dev->phy.band_idx), + MT_TXQ_ID(dev->phy.mt76->band_idx), MT7915_TX_RING_SIZE, MT_TXQ_RING_BASE(0)); if (ret) @@ -426,7 +426,7 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2) if (phy2) { ret = mt7915_init_tx_queues(phy2, - MT_TXQ_ID(phy2->band_idx), + MT_TXQ_ID(phy2->mt76->band_idx), MT7915_TX_RING_SIZE, MT_TXQ_RING_BASE(1)); if (ret) @@ -482,7 +482,7 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2) return ret; /* rx data queue for band0 */ - if (!dev->phy.band_idx) { + if (!dev->phy.mt76->band_idx) { if (mtk_wed_device_active(&mdev->mmio.wed) && mtk_wed_get_rx_capa(&mdev->mmio.wed)) { dev->mt76.q_rx[MT_RXQ_MAIN].flags = @@ -519,7 +519,7 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2) return ret; } - if (dev->dbdc_support || dev->phy.band_idx) { + if (dev->dbdc_support || dev->phy.mt76->band_idx) { if (mtk_wed_device_active(&mdev->mmio.wed) && mtk_wed_get_rx_capa(&mdev->mmio.wed)) { dev->mt76.q_rx[MT_RXQ_BAND1].flags = diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c index 0bce0ce51be0..59069fb86414 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c @@ -131,9 +131,10 @@ static void mt7915_eeprom_parse_band_config(struct mt7915_phy *phy) { struct mt7915_dev *dev = phy->dev; u8 *eeprom = dev->mt76.eeprom.data; + u8 band = phy->mt76->band_idx; u32 val; - val = eeprom[MT_EE_WIFI_CONF + phy->band_idx]; + val = eeprom[MT_EE_WIFI_CONF + band]; val = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val); if (!is_mt7915(&dev->mt76)) { @@ -153,7 +154,7 @@ static void mt7915_eeprom_parse_band_config(struct mt7915_phy *phy) return; } } else if (val == MT_EE_BAND_SEL_DEFAULT && dev->dbdc_support) { - val = phy->band_idx ? MT_EE_BAND_SEL_5GHZ : MT_EE_BAND_SEL_2GHZ; + val = band ? MT_EE_BAND_SEL_5GHZ : MT_EE_BAND_SEL_2GHZ; } switch (val) { @@ -175,6 +176,7 @@ void mt7915_eeprom_parse_hw_cap(struct mt7915_dev *dev, { u8 path, nss, nss_max = 4, *eeprom = dev->mt76.eeprom.data; struct mt76_phy *mphy = phy->mt76; + u8 band = phy->mt76->band_idx; mt7915_eeprom_parse_band_config(phy); @@ -184,7 +186,7 @@ void mt7915_eeprom_parse_hw_cap(struct mt7915_dev *dev, eeprom[MT_EE_WIFI_CONF]); } else { path = FIELD_GET(MT_EE_WIFI_CONF0_TX_PATH, - eeprom[MT_EE_WIFI_CONF + phy->band_idx]); + eeprom[MT_EE_WIFI_CONF + band]); } if (!path || path > 4) @@ -197,12 +199,12 @@ void mt7915_eeprom_parse_hw_cap(struct mt7915_dev *dev, path = min_t(u8, path, 2); nss = FIELD_GET(MT_EE_WIFI_CONF3_TX_PATH_B0, eeprom[MT_EE_WIFI_CONF + 3]); - if (phy->band_idx) + if (band) nss = FIELD_GET(MT_EE_WIFI_CONF3_TX_PATH_B1, eeprom[MT_EE_WIFI_CONF + 3]); } else { nss = FIELD_GET(MT_EE_WIFI_CONF_STREAM_NUM, - eeprom[MT_EE_WIFI_CONF + 2 + phy->band_idx]); + eeprom[MT_EE_WIFI_CONF + 2 + band]); } if (!is_mt7986(&dev->mt76)) @@ -214,7 +216,7 @@ void mt7915_eeprom_parse_hw_cap(struct mt7915_dev *dev, nss = min_t(u8, min_t(u8, nss_max, nss), path); mphy->chainmask = BIT(path) - 1; - if (phy->band_idx) + if (band) mphy->chainmask <<= dev->chainshift; mphy->antenna_mask = BIT(nss) - 1; dev->chainmask |= mphy->chainmask; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c index 79bd7bf93f33..c810c31fbd6e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c @@ -540,7 +540,7 @@ mt7915_alloc_ext_phy(struct mt7915_dev *dev) phy->mt76 = mphy; /* Bind main phy to band0 and ext_phy to band1 for dbdc case */ - phy->band_idx = 1; + phy->mt76->band_idx = 1; return phy; } @@ -660,7 +660,7 @@ static bool mt7915_band_config(struct mt7915_dev *dev) { bool ret = true; - dev->phy.band_idx = 0; + dev->phy.mt76->band_idx = 0; if (is_mt7986(&dev->mt76)) { u32 sku = mt7915_check_adie(dev, true); @@ -671,7 +671,7 @@ static bool mt7915_band_config(struct mt7915_dev *dev) * dbdc is disabled. */ if (sku == MT7975_ONE_ADIE || sku == MT7976_ONE_ADIE) { - dev->phy.band_idx = 1; + dev->phy.mt76->band_idx = 1; ret = false; } } else { diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index eac3008225b0..f0d5a3603902 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -302,7 +302,7 @@ mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb, memset(status, 0, sizeof(*status)); - if ((rxd1 & MT_RXD1_NORMAL_BAND_IDX) && !phy->band_idx) { + if ((rxd1 & MT_RXD1_NORMAL_BAND_IDX) && !phy->mt76->band_idx) { mphy = dev->mt76.phys[MT_BAND1]; if (!mphy) return -EINVAL; @@ -575,7 +575,7 @@ mt7915_mac_fill_rx_vector(struct mt7915_dev *dev, struct sk_buff *skb) int i; band_idx = le32_get_bits(rxv_hdr[1], MT_RXV_HDR_BAND_IDX); - if (band_idx && !phy->band_idx) { + if (band_idx && !phy->mt76->band_idx) { phy = mt7915_ext_phy(dev); if (!phy) goto out; @@ -1175,7 +1175,7 @@ void mt7915_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, void mt7915_mac_cca_stats_reset(struct mt7915_phy *phy) { struct mt7915_dev *dev = phy->dev; - u32 reg = MT_WF_PHY_RX_CTRL1(phy->band_idx); + u32 reg = MT_WF_PHY_RX_CTRL1(phy->mt76->band_idx); mt76_clear(dev, reg, MT_WF_PHY_RX_CTRL1_STSCNT_EN); mt76_set(dev, reg, BIT(11) | BIT(9)); @@ -1187,15 +1187,15 @@ void mt7915_mac_reset_counters(struct mt7915_phy *phy) int i; for (i = 0; i < 4; i++) { - mt76_rr(dev, MT_TX_AGG_CNT(phy->band_idx, i)); - mt76_rr(dev, MT_TX_AGG_CNT2(phy->band_idx, i)); + mt76_rr(dev, MT_TX_AGG_CNT(phy->mt76->band_idx, i)); + mt76_rr(dev, MT_TX_AGG_CNT2(phy->mt76->band_idx, i)); } phy->mt76->survey_time = ktime_get_boottime(); memset(phy->mt76->aggr_stats, 0, sizeof(phy->mt76->aggr_stats)); /* reset airtime counters */ - mt76_set(dev, MT_WF_RMAC_MIB_AIRTIME0(phy->band_idx), + mt76_set(dev, MT_WF_RMAC_MIB_AIRTIME0(phy->mt76->band_idx), MT_WF_RMAC_MIB_RXTIME_CLR); mt7915_mcu_get_chan_mib_info(phy, true); @@ -1211,6 +1211,7 @@ void mt7915_mac_set_timing(struct mt7915_phy *phy) FIELD_PREP(MT_TIMEOUT_VAL_CCA, 48); u32 ofdm = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 60) | FIELD_PREP(MT_TIMEOUT_VAL_CCA, 28); + u8 band = phy->mt76->band_idx; int eifs_ofdm = 360, sifs = 10, offset; bool a_band = !(phy->mt76->chandef.chan->band == NL80211_BAND_2GHZ); @@ -1221,7 +1222,7 @@ void mt7915_mac_set_timing(struct mt7915_phy *phy) coverage_class = max_t(s16, dev->phy.coverage_class, ext_phy->coverage_class); - mt76_set(dev, MT_ARB_SCR(phy->band_idx), + mt76_set(dev, MT_ARB_SCR(band), MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE); udelay(1); @@ -1231,7 +1232,7 @@ void mt7915_mac_set_timing(struct mt7915_phy *phy) if (!is_mt7915(&dev->mt76)) { if (!a_band) { - mt76_wr(dev, MT_TMAC_ICR1(phy->band_idx), + mt76_wr(dev, MT_TMAC_ICR1(band), FIELD_PREP(MT_IFS_EIFS_CCK, 314)); eifs_ofdm = 78; } else { @@ -1241,9 +1242,9 @@ void mt7915_mac_set_timing(struct mt7915_phy *phy) sifs = 16; } - mt76_wr(dev, MT_TMAC_CDTR(phy->band_idx), cck + reg_offset); - mt76_wr(dev, MT_TMAC_ODTR(phy->band_idx), ofdm + reg_offset); - mt76_wr(dev, MT_TMAC_ICR0(phy->band_idx), + mt76_wr(dev, MT_TMAC_CDTR(band), cck + reg_offset); + mt76_wr(dev, MT_TMAC_ODTR(band), ofdm + reg_offset); + mt76_wr(dev, MT_TMAC_ICR0(band), FIELD_PREP(MT_IFS_EIFS_OFDM, eifs_ofdm) | FIELD_PREP(MT_IFS_RIFS, 2) | FIELD_PREP(MT_IFS_SIFS, sifs) | @@ -1254,8 +1255,8 @@ void mt7915_mac_set_timing(struct mt7915_phy *phy) else val = MT7915_CFEND_RATE_11B; - mt76_rmw_field(dev, MT_AGG_ACR0(phy->band_idx), MT_AGG_ACR_CFEND_RATE, val); - mt76_clear(dev, MT_ARB_SCR(phy->band_idx), + mt76_rmw_field(dev, MT_AGG_ACR0(band), MT_AGG_ACR_CFEND_RATE, val); + mt76_clear(dev, MT_ARB_SCR(band), MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE); } @@ -1308,7 +1309,7 @@ void mt7915_update_channel(struct mt76_phy *mphy) mt7915_mcu_get_chan_mib_info(phy, false); - nf = mt7915_phy_get_nf(phy, phy->band_idx); + nf = mt7915_phy_get_nf(phy, phy->mt76->band_idx); if (!phy->noise) phy->noise = nf << 4; else if (nf) @@ -1751,127 +1752,128 @@ void mt7915_mac_update_stats(struct mt7915_phy *phy) struct mt7915_dev *dev = phy->dev; struct mib_stats *mib = &phy->mib; int i, aggr0 = 0, aggr1, cnt; + u8 band = phy->mt76->band_idx; u32 val; - cnt = mt76_rr(dev, MT_MIB_SDR3(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR3(band)); mib->fcs_err_cnt += is_mt7915(&dev->mt76) ? FIELD_GET(MT_MIB_SDR3_FCS_ERR_MASK, cnt) : FIELD_GET(MT_MIB_SDR3_FCS_ERR_MASK_MT7916, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR4(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR4(band)); mib->rx_fifo_full_cnt += FIELD_GET(MT_MIB_SDR4_RX_FIFO_FULL_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR5(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR5(band)); mib->rx_mpdu_cnt += cnt; - cnt = mt76_rr(dev, MT_MIB_SDR6(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR6(band)); mib->channel_idle_cnt += FIELD_GET(MT_MIB_SDR6_CHANNEL_IDL_CNT_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR7(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR7(band)); mib->rx_vector_mismatch_cnt += FIELD_GET(MT_MIB_SDR7_RX_VECTOR_MISMATCH_CNT_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR8(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR8(band)); mib->rx_delimiter_fail_cnt += FIELD_GET(MT_MIB_SDR8_RX_DELIMITER_FAIL_CNT_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR10(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR10(band)); mib->rx_mrdy_cnt += is_mt7915(&dev->mt76) ? FIELD_GET(MT_MIB_SDR10_MRDY_COUNT_MASK, cnt) : FIELD_GET(MT_MIB_SDR10_MRDY_COUNT_MASK_MT7916, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR11(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR11(band)); mib->rx_len_mismatch_cnt += FIELD_GET(MT_MIB_SDR11_RX_LEN_MISMATCH_CNT_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR12(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR12(band)); mib->tx_ampdu_cnt += cnt; - cnt = mt76_rr(dev, MT_MIB_SDR13(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR13(band)); mib->tx_stop_q_empty_cnt += FIELD_GET(MT_MIB_SDR13_TX_STOP_Q_EMPTY_CNT_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR14(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR14(band)); mib->tx_mpdu_attempts_cnt += is_mt7915(&dev->mt76) ? FIELD_GET(MT_MIB_SDR14_TX_MPDU_ATTEMPTS_CNT_MASK, cnt) : FIELD_GET(MT_MIB_SDR14_TX_MPDU_ATTEMPTS_CNT_MASK_MT7916, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR15(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR15(band)); mib->tx_mpdu_success_cnt += is_mt7915(&dev->mt76) ? FIELD_GET(MT_MIB_SDR15_TX_MPDU_SUCCESS_CNT_MASK, cnt) : FIELD_GET(MT_MIB_SDR15_TX_MPDU_SUCCESS_CNT_MASK_MT7916, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR16(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR16(band)); mib->primary_cca_busy_time += FIELD_GET(MT_MIB_SDR16_PRIMARY_CCA_BUSY_TIME_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR17(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR17(band)); mib->secondary_cca_busy_time += FIELD_GET(MT_MIB_SDR17_SECONDARY_CCA_BUSY_TIME_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR18(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR18(band)); mib->primary_energy_detect_time += FIELD_GET(MT_MIB_SDR18_PRIMARY_ENERGY_DETECT_TIME_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR19(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR19(band)); mib->cck_mdrdy_time += FIELD_GET(MT_MIB_SDR19_CCK_MDRDY_TIME_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR20(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR20(band)); mib->ofdm_mdrdy_time += FIELD_GET(MT_MIB_SDR20_OFDM_VHT_MDRDY_TIME_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR21(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR21(band)); mib->green_mdrdy_time += FIELD_GET(MT_MIB_SDR21_GREEN_MDRDY_TIME_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR22(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR22(band)); mib->rx_ampdu_cnt += cnt; - cnt = mt76_rr(dev, MT_MIB_SDR23(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR23(band)); mib->rx_ampdu_bytes_cnt += cnt; - cnt = mt76_rr(dev, MT_MIB_SDR24(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR24(band)); mib->rx_ampdu_valid_subframe_cnt += is_mt7915(&dev->mt76) ? FIELD_GET(MT_MIB_SDR24_RX_AMPDU_SF_CNT_MASK, cnt) : FIELD_GET(MT_MIB_SDR24_RX_AMPDU_SF_CNT_MASK_MT7916, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR25(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR25(band)); mib->rx_ampdu_valid_subframe_bytes_cnt += cnt; - cnt = mt76_rr(dev, MT_MIB_SDR27(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR27(band)); mib->tx_rwp_fail_cnt += FIELD_GET(MT_MIB_SDR27_TX_RWP_FAIL_CNT_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR28(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR28(band)); mib->tx_rwp_need_cnt += FIELD_GET(MT_MIB_SDR28_TX_RWP_NEED_CNT_MASK, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR29(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR29(band)); mib->rx_pfdrop_cnt += is_mt7915(&dev->mt76) ? FIELD_GET(MT_MIB_SDR29_RX_PFDROP_CNT_MASK, cnt) : FIELD_GET(MT_MIB_SDR29_RX_PFDROP_CNT_MASK_MT7916, cnt); - cnt = mt76_rr(dev, MT_MIB_SDRVEC(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDRVEC(band)); mib->rx_vec_queue_overflow_drop_cnt += is_mt7915(&dev->mt76) ? FIELD_GET(MT_MIB_SDR30_RX_VEC_QUEUE_OVERFLOW_DROP_CNT_MASK, cnt) : FIELD_GET(MT_MIB_SDR30_RX_VEC_QUEUE_OVERFLOW_DROP_CNT_MASK_MT7916, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR31(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR31(band)); mib->rx_ba_cnt += cnt; - cnt = mt76_rr(dev, MT_MIB_SDRMUBF(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDRMUBF(band)); mib->tx_bf_cnt += FIELD_GET(MT_MIB_MU_BF_TX_CNT, cnt); - cnt = mt76_rr(dev, MT_MIB_DR8(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_DR8(band)); mib->tx_mu_mpdu_cnt += cnt; - cnt = mt76_rr(dev, MT_MIB_DR9(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_DR9(band)); mib->tx_mu_acked_mpdu_cnt += cnt; - cnt = mt76_rr(dev, MT_MIB_DR11(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_DR11(band)); mib->tx_su_acked_mpdu_cnt += cnt; - cnt = mt76_rr(dev, MT_ETBF_PAR_RPT0(phy->band_idx)); + cnt = mt76_rr(dev, MT_ETBF_PAR_RPT0(band)); mib->tx_bf_rx_fb_bw = FIELD_GET(MT_ETBF_PAR_RPT0_FB_BW, cnt); mib->tx_bf_rx_fb_nc_cnt += FIELD_GET(MT_ETBF_PAR_RPT0_FB_NC, cnt); mib->tx_bf_rx_fb_nr_cnt += FIELD_GET(MT_ETBF_PAR_RPT0_FB_NR, cnt); @@ -1884,41 +1886,41 @@ void mt7915_mac_update_stats(struct mt7915_phy *phy) if (is_mt7915(&dev->mt76)) { for (i = 0, aggr1 = aggr0 + 8; i < 4; i++) { - val = mt76_rr(dev, MT_MIB_MB_SDR1(phy->band_idx, (i << 4))); + val = mt76_rr(dev, MT_MIB_MB_SDR1(band, (i << 4))); mib->ba_miss_cnt += FIELD_GET(MT_MIB_BA_MISS_COUNT_MASK, val); mib->ack_fail_cnt += FIELD_GET(MT_MIB_ACK_FAIL_COUNT_MASK, val); - val = mt76_rr(dev, MT_MIB_MB_SDR0(phy->band_idx, (i << 4))); + val = mt76_rr(dev, MT_MIB_MB_SDR0(band, (i << 4))); mib->rts_cnt += FIELD_GET(MT_MIB_RTS_COUNT_MASK, val); mib->rts_retries_cnt += FIELD_GET(MT_MIB_RTS_RETRIES_COUNT_MASK, val); - val = mt76_rr(dev, MT_TX_AGG_CNT(phy->band_idx, i)); + val = mt76_rr(dev, MT_TX_AGG_CNT(band, i)); phy->mt76->aggr_stats[aggr0++] += val & 0xffff; phy->mt76->aggr_stats[aggr0++] += val >> 16; - val = mt76_rr(dev, MT_TX_AGG_CNT2(phy->band_idx, i)); + val = mt76_rr(dev, MT_TX_AGG_CNT2(band, i)); phy->mt76->aggr_stats[aggr1++] += val & 0xffff; phy->mt76->aggr_stats[aggr1++] += val >> 16; } - cnt = mt76_rr(dev, MT_MIB_SDR32(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR32(band)); mib->tx_pkt_ebf_cnt += FIELD_GET(MT_MIB_SDR32_TX_PKT_EBF_CNT, cnt); - cnt = mt76_rr(dev, MT_MIB_SDR33(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR33(band)); mib->tx_pkt_ibf_cnt += FIELD_GET(MT_MIB_SDR33_TX_PKT_IBF_CNT, cnt); - cnt = mt76_rr(dev, MT_ETBF_TX_APP_CNT(phy->band_idx)); + cnt = mt76_rr(dev, MT_ETBF_TX_APP_CNT(band)); mib->tx_bf_ibf_ppdu_cnt += FIELD_GET(MT_ETBF_TX_IBF_CNT, cnt); mib->tx_bf_ebf_ppdu_cnt += FIELD_GET(MT_ETBF_TX_EBF_CNT, cnt); - cnt = mt76_rr(dev, MT_ETBF_TX_NDP_BFRP(phy->band_idx)); + cnt = mt76_rr(dev, MT_ETBF_TX_NDP_BFRP(band)); mib->tx_bf_fb_cpl_cnt += FIELD_GET(MT_ETBF_TX_FB_CPL, cnt); mib->tx_bf_fb_trig_cnt += FIELD_GET(MT_ETBF_TX_FB_TRI, cnt); - cnt = mt76_rr(dev, MT_ETBF_RX_FB_CNT(phy->band_idx)); + cnt = mt76_rr(dev, MT_ETBF_RX_FB_CNT(band)); mib->tx_bf_rx_fb_all_cnt += FIELD_GET(MT_ETBF_RX_FB_ALL, cnt); mib->tx_bf_rx_fb_he_cnt += FIELD_GET(MT_ETBF_RX_FB_HE, cnt); mib->tx_bf_rx_fb_vht_cnt += FIELD_GET(MT_ETBF_RX_FB_VHT, cnt); @@ -1926,51 +1928,51 @@ void mt7915_mac_update_stats(struct mt7915_phy *phy) } else { for (i = 0; i < 2; i++) { /* rts count */ - val = mt76_rr(dev, MT_MIB_MB_SDR0(phy->band_idx, (i << 2))); + val = mt76_rr(dev, MT_MIB_MB_SDR0(band, (i << 2))); mib->rts_cnt += FIELD_GET(GENMASK(15, 0), val); mib->rts_cnt += FIELD_GET(GENMASK(31, 16), val); /* rts retry count */ - val = mt76_rr(dev, MT_MIB_MB_SDR1(phy->band_idx, (i << 2))); + val = mt76_rr(dev, MT_MIB_MB_SDR1(band, (i << 2))); mib->rts_retries_cnt += FIELD_GET(GENMASK(15, 0), val); mib->rts_retries_cnt += FIELD_GET(GENMASK(31, 16), val); /* ba miss count */ - val = mt76_rr(dev, MT_MIB_MB_SDR2(phy->band_idx, (i << 2))); + val = mt76_rr(dev, MT_MIB_MB_SDR2(band, (i << 2))); mib->ba_miss_cnt += FIELD_GET(GENMASK(15, 0), val); mib->ba_miss_cnt += FIELD_GET(GENMASK(31, 16), val); /* ack fail count */ - val = mt76_rr(dev, MT_MIB_MB_BFTF(phy->band_idx, (i << 2))); + val = mt76_rr(dev, MT_MIB_MB_BFTF(band, (i << 2))); mib->ack_fail_cnt += FIELD_GET(GENMASK(15, 0), val); mib->ack_fail_cnt += FIELD_GET(GENMASK(31, 16), val); } for (i = 0; i < 8; i++) { - val = mt76_rr(dev, MT_TX_AGG_CNT(phy->band_idx, i)); + val = mt76_rr(dev, MT_TX_AGG_CNT(band, i)); phy->mt76->aggr_stats[aggr0++] += FIELD_GET(GENMASK(15, 0), val); phy->mt76->aggr_stats[aggr0++] += FIELD_GET(GENMASK(31, 16), val); } - cnt = mt76_rr(dev, MT_MIB_SDR32(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR32(band)); mib->tx_pkt_ibf_cnt += FIELD_GET(MT_MIB_SDR32_TX_PKT_IBF_CNT, cnt); mib->tx_bf_ibf_ppdu_cnt += FIELD_GET(MT_MIB_SDR32_TX_PKT_IBF_CNT, cnt); mib->tx_pkt_ebf_cnt += FIELD_GET(MT_MIB_SDR32_TX_PKT_EBF_CNT, cnt); mib->tx_bf_ebf_ppdu_cnt += FIELD_GET(MT_MIB_SDR32_TX_PKT_EBF_CNT, cnt); - cnt = mt76_rr(dev, MT_MIB_BFCR7(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_BFCR7(band)); mib->tx_bf_fb_cpl_cnt += FIELD_GET(MT_MIB_BFCR7_BFEE_TX_FB_CPL, cnt); - cnt = mt76_rr(dev, MT_MIB_BFCR2(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_BFCR2(band)); mib->tx_bf_fb_trig_cnt += FIELD_GET(MT_MIB_BFCR2_BFEE_TX_FB_TRIG, cnt); - cnt = mt76_rr(dev, MT_MIB_BFCR0(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_BFCR0(band)); mib->tx_bf_rx_fb_vht_cnt += FIELD_GET(MT_MIB_BFCR0_RX_FB_VHT, cnt); mib->tx_bf_rx_fb_all_cnt += FIELD_GET(MT_MIB_BFCR0_RX_FB_VHT, cnt); mib->tx_bf_rx_fb_ht_cnt += FIELD_GET(MT_MIB_BFCR0_RX_FB_HT, cnt); mib->tx_bf_rx_fb_all_cnt += FIELD_GET(MT_MIB_BFCR0_RX_FB_HT, cnt); - cnt = mt76_rr(dev, MT_MIB_BFCR1(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_BFCR1(band)); mib->tx_bf_rx_fb_he_cnt += FIELD_GET(MT_MIB_BFCR1_RX_FB_HE, cnt); mib->tx_bf_rx_fb_all_cnt += FIELD_GET(MT_MIB_BFCR1_RX_FB_HE, cnt); } @@ -1988,7 +1990,7 @@ static void mt7915_mac_severe_check(struct mt7915_phy *phy) * stopping Rx, so check status periodically to see if TRB hardware * requires minimal recovery. */ - trb = mt76_rr(dev, MT_TRB_RXPSR0(phy->band_idx)); + trb = mt76_rr(dev, MT_TRB_RXPSR0(phy->mt76->band_idx)); if ((FIELD_GET(MT_TRB_RXPSR0_RX_RMAC_PTR, trb) != FIELD_GET(MT_TRB_RXPSR0_RX_WTBL_PTR, trb)) && @@ -1996,7 +1998,7 @@ static void mt7915_mac_severe_check(struct mt7915_phy *phy) FIELD_GET(MT_TRB_RXPSR0_RX_WTBL_PTR, phy->trb_ts)) && trb == phy->trb_ts) mt7915_mcu_set_ser(dev, SER_RECOVER, SER_SET_RECOVER_L3_RX_ABORT, - phy->band_idx); + phy->mt76->band_idx); phy->trb_ts = trb; } @@ -2116,16 +2118,16 @@ static int mt7915_dfs_start_radar_detector(struct mt7915_phy *phy) int err; /* start CAC */ - err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_CAC_START, phy->band_idx, - MT_RX_SEL0, 0); + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_CAC_START, + phy->mt76->band_idx, MT_RX_SEL0, 0); if (err < 0) return err; - err = mt7915_dfs_start_rdd(dev, phy->band_idx); + err = mt7915_dfs_start_rdd(dev, phy->mt76->band_idx); if (err < 0) return err; - phy->rdd_state |= BIT(phy->band_idx); + phy->rdd_state |= BIT(phy->mt76->band_idx); if (!is_mt7915(&dev->mt76)) return 0; @@ -2210,7 +2212,7 @@ int mt7915_dfs_init_radar_detector(struct mt7915_phy *phy) return 0; err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_CAC_END, - phy->band_idx, MT_RX_SEL0, 0); + phy->mt76->band_idx, MT_RX_SEL0, 0); if (err < 0) { phy->mt76->dfs_state = MT_DFS_STATE_UNKNOWN; return err; @@ -2221,13 +2223,13 @@ int mt7915_dfs_init_radar_detector(struct mt7915_phy *phy) stop: err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_NORMAL_START, - phy->band_idx, MT_RX_SEL0, 0); + phy->mt76->band_idx, MT_RX_SEL0, 0); if (err < 0) return err; if (is_mt7915(&dev->mt76)) { err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_SET_WF_ANT, - phy->band_idx, 0, + phy->mt76->band_idx, 0, dev->dbdc_support ? 2 : 0); if (err < 0) return err; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c index c40b6098f19a..0511d6a505b0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -30,31 +30,35 @@ int mt7915_run(struct ieee80211_hw *hw) running = mt7915_dev_running(dev); if (!running) { - ret = mt76_connac_mcu_set_pm(&dev->mt76, dev->phy.band_idx, 0); + ret = mt76_connac_mcu_set_pm(&dev->mt76, + dev->phy.mt76->band_idx, 0); if (ret) goto out; - ret = mt7915_mcu_set_mac(dev, dev->phy.band_idx, true, true); + ret = mt7915_mcu_set_mac(dev, dev->phy.mt76->band_idx, + true, true); if (ret) goto out; - mt7915_mac_enable_nf(dev, dev->phy.band_idx); + mt7915_mac_enable_nf(dev, dev->phy.mt76->band_idx); } if (phy != &dev->phy) { - ret = mt76_connac_mcu_set_pm(&dev->mt76, phy->band_idx, 0); + ret = mt76_connac_mcu_set_pm(&dev->mt76, + phy->mt76->band_idx, 0); if (ret) goto out; - ret = mt7915_mcu_set_mac(dev, phy->band_idx, true, true); + ret = mt7915_mcu_set_mac(dev, phy->mt76->band_idx, + true, true); if (ret) goto out; - mt7915_mac_enable_nf(dev, phy->band_idx); + mt7915_mac_enable_nf(dev, phy->mt76->band_idx); } ret = mt76_connac_mcu_set_rts_thresh(&dev->mt76, 0x92b, - phy->band_idx); + phy->mt76->band_idx); if (ret) goto out; @@ -107,13 +111,13 @@ static void mt7915_stop(struct ieee80211_hw *hw) clear_bit(MT76_STATE_RUNNING, &phy->mt76->state); if (phy != &dev->phy) { - mt76_connac_mcu_set_pm(&dev->mt76, phy->band_idx, 1); - mt7915_mcu_set_mac(dev, phy->band_idx, false, false); + mt76_connac_mcu_set_pm(&dev->mt76, phy->mt76->band_idx, 1); + mt7915_mcu_set_mac(dev, phy->mt76->band_idx, false, false); } if (!mt7915_dev_running(dev)) { - mt76_connac_mcu_set_pm(&dev->mt76, dev->phy.band_idx, 1); - mt7915_mcu_set_mac(dev, dev->phy.band_idx, false, false); + mt76_connac_mcu_set_pm(&dev->mt76, dev->phy.mt76->band_idx, 1); + mt7915_mcu_set_mac(dev, dev->phy.mt76->band_idx, false, false); } mutex_unlock(&dev->mt76.mutex); @@ -217,7 +221,7 @@ static int mt7915_add_interface(struct ieee80211_hw *hw, } mvif->mt76.omac_idx = idx; mvif->phy = phy; - mvif->mt76.band_idx = phy->band_idx; + mvif->mt76.band_idx = phy->mt76->band_idx; mvif->mt76.wmm_idx = vif->type != NL80211_IFTYPE_AP; if (ext_phy) @@ -467,7 +471,7 @@ static int mt7915_config(struct ieee80211_hw *hw, u32 changed) if (changed & IEEE80211_CONF_CHANGE_MONITOR) { bool enabled = !!(hw->conf.flags & IEEE80211_CONF_MONITOR); - bool band = phy->band_idx; + bool band = phy->mt76->band_idx; if (!enabled) phy->rxfilter |= MT_WF_RFCR_DROP_OTHER_UC; @@ -506,7 +510,7 @@ static void mt7915_configure_filter(struct ieee80211_hw *hw, { struct mt7915_dev *dev = mt7915_hw_dev(hw); struct mt7915_phy *phy = mt7915_hw_phy(hw); - bool band = phy->band_idx; + bool band = phy->mt76->band_idx; u32 ctl_flags = MT_WF_RFCR1_DROP_ACK | MT_WF_RFCR1_DROP_BF_POLL | MT_WF_RFCR1_DROP_BA | @@ -743,7 +747,8 @@ static int mt7915_set_rts_threshold(struct ieee80211_hw *hw, u32 val) int ret; mutex_lock(&dev->mt76.mutex); - ret = mt76_connac_mcu_set_rts_thresh(&dev->mt76, val, phy->band_idx); + ret = mt76_connac_mcu_set_rts_thresh(&dev->mt76, val, + phy->mt76->band_idx); mutex_unlock(&dev->mt76.mutex); return ret; @@ -846,7 +851,7 @@ u64 __mt7915_get_tsf(struct ieee80211_hw *hw, struct mt7915_vif *mvif) { struct mt7915_dev *dev = mt7915_hw_dev(hw); struct mt7915_phy *phy = mt7915_hw_phy(hw); - bool band = phy->band_idx; + bool band = phy->mt76->band_idx; union { u64 t64; u32 t32[2]; @@ -891,7 +896,7 @@ mt7915_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct mt7915_dev *dev = mt7915_hw_dev(hw); struct mt7915_phy *phy = mt7915_hw_phy(hw); - bool band = phy->band_idx; + bool band = phy->mt76->band_idx; union { u64 t64; u32 t32[2]; @@ -922,7 +927,7 @@ mt7915_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; struct mt7915_dev *dev = mt7915_hw_dev(hw); struct mt7915_phy *phy = mt7915_hw_phy(hw); - bool band = phy->band_idx; + bool band = phy->mt76->band_idx; union { u64 t64; u32 t32[2]; @@ -965,6 +970,7 @@ mt7915_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) struct mt7915_phy *phy = mt7915_hw_phy(hw); int max_nss = hweight8(hw->wiphy->available_antennas_tx); u8 chainshift = dev->chainshift; + u8 band = phy->mt76->band_idx; if (!tx_ant || tx_ant != rx_ant || ffs(tx_ant) > max_nss) return -EINVAL; @@ -974,10 +980,10 @@ mt7915_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) phy->mt76->antenna_mask = tx_ant; /* handle a variant of mt7916 which has 3T3R but nss2 on 5 GHz band */ - if (is_mt7916(&dev->mt76) && phy->band_idx && hweight8(tx_ant) == max_nss) + if (is_mt7916(&dev->mt76) && band && hweight8(tx_ant) == max_nss) phy->mt76->chainmask = (dev->chainmask >> chainshift) << chainshift; else - phy->mt76->chainmask = tx_ant << (chainshift * phy->band_idx); + phy->mt76->chainmask = tx_ant << (chainshift * band); mt76_set_stream_caps(phy->mt76, true); mt7915_set_stream_vht_txbf_caps(phy); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 765bf634db76..b2652de082ba 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -232,7 +232,8 @@ mt7915_mcu_rx_csa_notify(struct mt7915_dev *dev, struct sk_buff *skb) c = (struct mt7915_mcu_csa_notify *)skb->data; - if ((c->band_idx && !dev->phy.band_idx) && dev->mt76.phys[MT_BAND1]) + if ((c->band_idx && !dev->phy.mt76->band_idx) && + dev->mt76.phys[MT_BAND1]) mphy = dev->mt76.phys[MT_BAND1]; ieee80211_iterate_active_interfaces_atomic(mphy->hw, @@ -251,7 +252,8 @@ mt7915_mcu_rx_thermal_notify(struct mt7915_dev *dev, struct sk_buff *skb) if (t->ctrl.ctrl_id != THERMAL_PROTECT_ENABLE) return; - if ((t->ctrl.band_idx && !dev->phy.band_idx) && dev->mt76.phys[MT_BAND1]) + if ((t->ctrl.band_idx && !dev->phy.mt76->band_idx) && + dev->mt76.phys[MT_BAND1]) mphy = dev->mt76.phys[MT_BAND1]; phy = (struct mt7915_phy *)mphy->priv; @@ -266,7 +268,8 @@ mt7915_mcu_rx_radar_detected(struct mt7915_dev *dev, struct sk_buff *skb) r = (struct mt7915_mcu_rdd_report *)skb->data; - if ((r->band_idx && !dev->phy.band_idx) && dev->mt76.phys[MT_BAND1]) + if ((r->band_idx && !dev->phy.mt76->band_idx) && + dev->mt76.phys[MT_BAND1]) mphy = dev->mt76.phys[MT_BAND1]; if (r->band_idx == MT_RX_SEL2) @@ -323,7 +326,7 @@ mt7915_mcu_rx_bcc_notify(struct mt7915_dev *dev, struct sk_buff *skb) b = (struct mt7915_mcu_bcc_notify *)skb->data; - if ((b->band_idx && !dev->phy.band_idx) && dev->mt76.phys[MT_BAND1]) + if ((b->band_idx && !dev->phy.mt76->band_idx) && dev->mt76.phys[MT_BAND1]) mphy = dev->mt76.phys[MT_BAND1]; ieee80211_iterate_active_interfaces_atomic(mphy->hw, @@ -599,7 +602,7 @@ mt7915_mcu_muar_config(struct mt7915_phy *phy, struct ieee80211_vif *vif, .mode = !!mask || enable, .entry_count = 1, .write = 1, - .band = phy->band_idx, + .band = phy->mt76->band_idx, .index = idx * 2 + bssid, }; @@ -2192,7 +2195,7 @@ int mt7915_mcu_muru_debug_get(struct mt7915_phy *phy, void *ms) u8 band_idx; } req = { .cmd = cpu_to_le32(MURU_GET_TXC_TX_STATS), - .band_idx = phy->band_idx, + .band_idx = phy->mt76->band_idx, }; ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_EXT_CMD(MURU_CTRL), @@ -2585,7 +2588,7 @@ mt7915_mcu_background_chain_ctrl(struct mt7915_phy *phy, req.monitor_central_chan = ieee80211_frequency_to_channel(chandef->center_freq1); req.monitor_bw = mt76_connac_chan_bw(chandef); - req.band_idx = phy->band_idx; + req.band_idx = phy->mt76->band_idx; req.scan_mode = 1; break; } @@ -2593,7 +2596,7 @@ mt7915_mcu_background_chain_ctrl(struct mt7915_phy *phy, req.monitor_chan = chandef->chan->hw_value; req.monitor_central_chan = ieee80211_frequency_to_channel(chandef->center_freq1); - req.band_idx = phy->band_idx; + req.band_idx = phy->mt76->band_idx; req.scan_mode = 2; break; case CH_SWITCH_BACKGROUND_SCAN_STOP: @@ -2660,6 +2663,7 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd) struct mt7915_dev *dev = phy->dev; struct cfg80211_chan_def *chandef = &phy->mt76->chandef; int freq1 = chandef->center_freq1; + u8 band = phy->mt76->band_idx; struct { u8 control_ch; u8 center_ch; @@ -2682,8 +2686,8 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd) .center_ch = ieee80211_frequency_to_channel(freq1), .bw = mt76_connac_chan_bw(chandef), .tx_path_num = hweight16(phy->mt76->chainmask), - .rx_path = phy->mt76->chainmask >> (dev->chainshift * phy->band_idx), - .band_idx = phy->band_idx, + .rx_path = phy->mt76->chainmask >> (dev->chainshift * band), + .band_idx = band, .channel_band = ch_band[chandef->chan->band], }; @@ -2997,7 +3001,7 @@ int mt7915_mcu_get_chan_mib_info(struct mt7915_phy *phy, bool chan_switch) } for (i = 0; i < 5; i++) { - req[i].band = cpu_to_le32(phy->band_idx); + req[i].band = cpu_to_le32(phy->mt76->band_idx); req[i].offs = cpu_to_le32(offs[i + start]); if (!is_mt7915(&dev->mt76) && i == 3) @@ -3046,7 +3050,7 @@ int mt7915_mcu_get_temperature(struct mt7915_phy *phy) u8 rsv[5]; } req = { .ctrl_id = THERMAL_SENSOR_TEMP_QUERY, - .band_idx = phy->band_idx, + .band_idx = phy->mt76->band_idx, }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(THERMAL_CTRL), &req, @@ -3065,7 +3069,7 @@ int mt7915_mcu_set_thermal_throttling(struct mt7915_phy *phy, u8 state) u8 rsv[2]; } __packed req = { .ctrl = { - .band_idx = phy->band_idx, + .band_idx = phy->mt76->band_idx, }, }; int level; @@ -3115,7 +3119,7 @@ int mt7915_mcu_set_txpower_frame_min(struct mt7915_phy *phy, s8 txpower) s8 txpower_min; } __packed req = { .format_id = TX_POWER_LIMIT_FRAME_MIN, - .band_idx = phy->band_idx, + .band_idx = phy->mt76->band_idx, .txpower_min = txpower * 2, /* 0.5db */ }; @@ -3140,7 +3144,7 @@ int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy, s8 txpower_offs[48]; } __packed req = { .format_id = TX_POWER_LIMIT_FRAME, - .band_idx = phy->band_idx, + .band_idx = phy->mt76->band_idx, .txpower_max = DIV_ROUND_UP(mphy->txpower_cur, 2), .wcid = cpu_to_le16(msta->wcid.idx), }; @@ -3194,7 +3198,7 @@ int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy) struct ieee80211_hw *hw = mphy->hw; struct mt7915_mcu_txpower_sku req = { .format_id = TX_POWER_LIMIT_TABLE, - .band_idx = phy->band_idx, + .band_idx = phy->mt76->band_idx, }; struct mt76_power_limits limits_array; s8 *la = (s8 *)&limits_array; @@ -3243,7 +3247,7 @@ int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len) } __packed req = { .format_id = TX_POWER_LIMIT_INFO, .category = RATE_POWER_INFO, - .band_idx = phy->band_idx, + .band_idx = phy->mt76->band_idx, }; s8 txpower_sku[MT7915_SKU_RATE_NUM][2]; struct sk_buff *skb; @@ -3296,7 +3300,7 @@ int mt7915_mcu_set_sku_en(struct mt7915_phy *phy, bool enable) u8 rsv; } __packed req = { .format_id = TX_POWER_LIMIT_ENABLE, - .band_idx = phy->band_idx, + .band_idx = phy->mt76->band_idx, .sku_enable = enable, }; @@ -3378,7 +3382,7 @@ mt7915_mcu_enable_obss_spr(struct mt7915_phy *phy, u8 action, u8 val) struct mt7915_mcu_sr_ctrl req = { .action = action, .argnum = 1, - .band_idx = phy->band_idx, + .band_idx = phy->mt76->band_idx, .val = cpu_to_le32(val), }; @@ -3409,7 +3413,7 @@ mt7915_mcu_set_obss_spr_pd(struct mt7915_phy *phy, .ctrl = { .action = SPR_SET_PARAM, .argnum = 9, - .band_idx = phy->band_idx, + .band_idx = phy->mt76->band_idx, }, }; int ret; @@ -3458,7 +3462,7 @@ mt7915_mcu_set_obss_spr_siga(struct mt7915_phy *phy, struct ieee80211_vif *vif, .ctrl = { .action = SPR_SET_SIGA, .argnum = 1, - .band_idx = phy->band_idx, + .band_idx = phy->mt76->band_idx, }, .siga = { .omac = omac > HW_BSSID_MAX ? omac - 12 : omac, @@ -3497,7 +3501,7 @@ mt7915_mcu_set_obss_spr_bitmap(struct mt7915_phy *phy, .ctrl = { .action = SPR_SET_SRG_BITMAP, .argnum = 4, - .band_idx = phy->band_idx, + .band_idx = phy->mt76->band_idx, }, }; u32 bitmap; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c index 3b4ede3b85ea..5a4141682f04 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -558,11 +558,11 @@ static int mt7915_mmio_wed_offload_enable(struct mtk_wed_device *wed) return -EAGAIN; phy = &dev->phy; - mt76_set(dev, MT_AGG_ACR4(phy->band_idx), MT_AGG_ACR_PPDU_TXS2H); + mt76_set(dev, MT_AGG_ACR4(phy->mt76->band_idx), MT_AGG_ACR_PPDU_TXS2H); phy = dev->mt76.phys[MT_BAND1] ? dev->mt76.phys[MT_BAND1]->priv : NULL; if (phy) - mt76_set(dev, MT_AGG_ACR4(phy->band_idx), + mt76_set(dev, MT_AGG_ACR4(phy->mt76->band_idx), MT_AGG_ACR_PPDU_TXS2H); return 0; @@ -583,11 +583,11 @@ static void mt7915_mmio_wed_offload_disable(struct mtk_wed_device *wed) * MT_AGG_ACR_PPDU_TXS2H (PPDU format) even though ACR bit is set. */ phy = &dev->phy; - mt76_clear(dev, MT_AGG_ACR4(phy->band_idx), MT_AGG_ACR_PPDU_TXS2H); + mt76_clear(dev, MT_AGG_ACR4(phy->mt76->band_idx), MT_AGG_ACR_PPDU_TXS2H); phy = dev->mt76.phys[MT_BAND1] ? dev->mt76.phys[MT_BAND1]->priv : NULL; if (phy) - mt76_clear(dev, MT_AGG_ACR4(phy->band_idx), + mt76_clear(dev, MT_AGG_ACR4(phy->mt76->band_idx), MT_AGG_ACR_PPDU_TXS2H); } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index 42f213430758..6351feba6bdf 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -258,7 +258,6 @@ struct mt7915_phy { u32 rxfilter; u64 omac_mask; - u8 band_idx; u16 noise; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c index 7ace05e0b63b..0d76ae31b376 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c @@ -51,7 +51,7 @@ mt7915_tm_set_tx_power(struct mt7915_phy *phy) u8 rsv[3]; } __packed req = { .format_id = 0xf, - .band_idx = phy->band_idx, + .band_idx = phy->mt76->band_idx, .center_chan = ieee80211_frequency_to_channel(freq), }; u8 *tx_power = NULL; @@ -77,7 +77,7 @@ mt7915_tm_set_freq_offset(struct mt7915_phy *phy, bool en, u32 val) struct mt7915_tm_cmd req = { .testmode_en = en, .param_idx = MCU_ATE_SET_FREQ_OFFSET, - .param.freq.band = phy->band_idx, + .param.freq.band = phy->mt76->band_idx, .param.freq.freq_offset = cpu_to_le32(val), }; @@ -111,7 +111,7 @@ mt7915_tm_set_trx(struct mt7915_phy *phy, int type, bool en) .param_idx = MCU_ATE_SET_TRX, .param.trx.type = type, .param.trx.enable = en, - .param.trx.band = phy->band_idx, + .param.trx.band = phy->mt76->band_idx, }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req, @@ -126,7 +126,7 @@ mt7915_tm_clean_hwq(struct mt7915_phy *phy, u8 wcid) .testmode_en = 1, .param_idx = MCU_ATE_CLEAN_TXQUEUE, .param.clean.wcid = wcid, - .param.clean.band = phy->band_idx, + .param.clean.band = phy->mt76->band_idx, }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req, @@ -144,7 +144,7 @@ mt7915_tm_set_slot_time(struct mt7915_phy *phy, u8 slot_time, u8 sifs) .param.slot.sifs = sifs, .param.slot.rifs = 2, .param.slot.eifs = cpu_to_le16(60), - .param.slot.band = phy->band_idx, + .param.slot.band = phy->mt76->band_idx, }; return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req, @@ -198,6 +198,7 @@ mt7915_tm_set_ipg_params(struct mt7915_phy *phy, u32 ipg, u8 mode) u8 sig_ext = (mode == MT76_TM_TX_MODE_CCK) ? 0 : 6; u8 slot_time = 9, sifs = TM_DEFAULT_SIFS; u8 aifsn = TM_MIN_AIFSN; + u8 band = phy->mt76->band_idx; u32 i2t_time, tr2t_time, txv_time; u16 cw = 0; @@ -232,14 +233,14 @@ mt7915_tm_set_ipg_params(struct mt7915_phy *phy, u32 ipg, u8 mode) sifs = min_t(u32, ipg, TM_MAX_SIFS); } done: - txv_time = mt76_get_field(dev, MT_TMAC_ATCR(phy->band_idx), + txv_time = mt76_get_field(dev, MT_TMAC_ATCR(band), MT_TMAC_ATCR_TXV_TOUT); txv_time *= 50; /* normal clock time */ i2t_time = (slot_time * 1000 - txv_time - BBP_PROC_TIME) / 50; tr2t_time = (sifs * 1000 - txv_time - BBP_PROC_TIME) / 50; - mt76_set(dev, MT_TMAC_TRCR0(phy->band_idx), + mt76_set(dev, MT_TMAC_TRCR0(band), FIELD_PREP(MT_TMAC_TRCR0_TR2T_CHK, tr2t_time) | FIELD_PREP(MT_TMAC_TRCR0_I2T_CHK, i2t_time)); @@ -336,6 +337,7 @@ mt7915_tm_reg_backup_restore(struct mt7915_phy *phy) int n_regs = ARRAY_SIZE(reg_backup_list); struct mt7915_dev *dev = phy->dev; u32 *b = phy->test.reg_backup; + u8 band = phy->mt76->band_idx; int i; REG_BAND_IDX(reg_backup_list[0], AGG_PCR0, 0); @@ -358,7 +360,7 @@ mt7915_tm_reg_backup_restore(struct mt7915_phy *phy) if (phy->mt76->test.state == MT76_TM_STATE_OFF) { for (i = 0; i < n_regs; i++) - mt76_wr(dev, reg_backup_list[i].band[phy->band_idx], b[i]); + mt76_wr(dev, reg_backup_list[i].band[band], b[i]); return; } @@ -369,33 +371,33 @@ mt7915_tm_reg_backup_restore(struct mt7915_phy *phy) phy->test.reg_backup = b; for (i = 0; i < n_regs; i++) - b[i] = mt76_rr(dev, reg_backup_list[i].band[phy->band_idx]); + b[i] = mt76_rr(dev, reg_backup_list[i].band[band]); } - mt76_clear(dev, MT_AGG_PCR0(phy->band_idx, 0), MT_AGG_PCR0_MM_PROT | + mt76_clear(dev, MT_AGG_PCR0(band, 0), MT_AGG_PCR0_MM_PROT | MT_AGG_PCR0_GF_PROT | MT_AGG_PCR0_ERP_PROT | MT_AGG_PCR0_VHT_PROT | MT_AGG_PCR0_BW20_PROT | MT_AGG_PCR0_BW40_PROT | MT_AGG_PCR0_BW80_PROT); - mt76_set(dev, MT_AGG_PCR0(phy->band_idx, 0), MT_AGG_PCR0_PTA_WIN_DIS); + mt76_set(dev, MT_AGG_PCR0(band, 0), MT_AGG_PCR0_PTA_WIN_DIS); - mt76_wr(dev, MT_AGG_PCR0(phy->band_idx, 1), MT_AGG_PCR1_RTS0_NUM_THRES | + mt76_wr(dev, MT_AGG_PCR0(band, 1), MT_AGG_PCR1_RTS0_NUM_THRES | MT_AGG_PCR1_RTS0_LEN_THRES); - mt76_clear(dev, MT_AGG_MRCR(phy->band_idx), MT_AGG_MRCR_BAR_CNT_LIMIT | + mt76_clear(dev, MT_AGG_MRCR(band), MT_AGG_MRCR_BAR_CNT_LIMIT | MT_AGG_MRCR_LAST_RTS_CTS_RN | MT_AGG_MRCR_RTS_FAIL_LIMIT | MT_AGG_MRCR_TXCMD_RTS_FAIL_LIMIT); - mt76_rmw(dev, MT_AGG_MRCR(phy->band_idx), MT_AGG_MRCR_RTS_FAIL_LIMIT | + mt76_rmw(dev, MT_AGG_MRCR(band), MT_AGG_MRCR_RTS_FAIL_LIMIT | MT_AGG_MRCR_TXCMD_RTS_FAIL_LIMIT, FIELD_PREP(MT_AGG_MRCR_RTS_FAIL_LIMIT, 1) | FIELD_PREP(MT_AGG_MRCR_TXCMD_RTS_FAIL_LIMIT, 1)); - mt76_wr(dev, MT_TMAC_TFCR0(phy->band_idx), 0); - mt76_clear(dev, MT_TMAC_TCR0(phy->band_idx), MT_TMAC_TCR0_TBTT_STOP_CTRL); + mt76_wr(dev, MT_TMAC_TFCR0(band), 0); + mt76_clear(dev, MT_TMAC_TCR0(band), MT_TMAC_TCR0_TBTT_STOP_CTRL); /* config rx filter for testmode rx */ - mt76_wr(dev, MT_WF_RFCR(phy->band_idx), 0xcf70a); - mt76_wr(dev, MT_WF_RFCR1(phy->band_idx), 0); + mt76_wr(dev, MT_WF_RFCR(band), 0xcf70a); + mt76_wr(dev, MT_WF_RFCR1(band), 0); } static void @@ -488,7 +490,7 @@ mt7915_tm_set_rx_frames(struct mt7915_phy *phy, bool en) mt7915_tm_update_channel(phy); /* read-clear */ - mt76_rr(dev, MT_MIB_SDR3(phy->band_idx)); + mt76_rr(dev, MT_MIB_SDR3(phy->mt76->band_idx)); mt7915_tm_set_trx(phy, TM_MAC_RX_RXV, en); } } @@ -515,6 +517,7 @@ mt7915_tm_set_tx_cont(struct mt7915_phy *phy, bool en) struct mt76_testmode_data *td = &phy->mt76->test; u32 func_idx = en ? TX_CONT_START : TX_CONT_STOP; u8 rate_idx = td->tx_rate_idx, mode; + u8 band = phy->mt76->band_idx; u16 rateval; struct mt7915_tm_rf_test req = { .action = 1, @@ -526,7 +529,7 @@ mt7915_tm_set_tx_cont(struct mt7915_phy *phy, bool en) tx_cont->control_ch = chandef->chan->hw_value; tx_cont->center_ch = freq1; tx_cont->tx_ant = td->tx_antenna_mask; - tx_cont->band = phy->band_idx; + tx_cont->band = band; switch (chandef->width) { case NL80211_CHAN_WIDTH_40: @@ -558,7 +561,7 @@ mt7915_tm_set_tx_cont(struct mt7915_phy *phy, bool en) } if (!en) { - req.op.rf.param.func_data = cpu_to_le32(phy->band_idx); + req.op.rf.param.func_data = cpu_to_le32(band); goto out; } @@ -767,11 +770,11 @@ mt7915_tm_dump_stats(struct mt76_phy *mphy, struct sk_buff *msg) nla_nest_end(msg, rx); - cnt = mt76_rr(dev, MT_MIB_SDR3(phy->band_idx)); + cnt = mt76_rr(dev, MT_MIB_SDR3(phy->mt76->band_idx)); fcs_err = is_mt7915(&dev->mt76) ? FIELD_GET(MT_MIB_SDR3_FCS_ERR_MASK, cnt) : FIELD_GET(MT_MIB_SDR3_FCS_ERR_MASK_MT7916, cnt); - q = phy->band_idx ? MT_RXQ_BAND1 : MT_RXQ_MAIN; + q = phy->mt76->band_idx ? MT_RXQ_BAND1 : MT_RXQ_MAIN; mphy->test.rx_stats.packets[q] += fcs_err; mphy->test.rx_stats.fcs_error[q] += fcs_err; From d75e739bba5957464e0065cae52b3feb78ed9ff9 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Thu, 1 Dec 2022 11:44:42 +0800 Subject: [PATCH 139/144] wifi: mt76: mt7996: enable use_cts_prot support This adds selectable RTC/CTS enablement for each interface. Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 13 +++++++++++++ drivers/net/wireless/mediatek/mt76/mt7996/main.c | 3 +++ drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h | 2 ++ 3 files changed, 18 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 1c35f0f15452..9e6aa9c3e566 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -218,6 +218,19 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev) rcu_read_unlock(); } +void mt7996_mac_enable_rtscts(struct mt7996_dev *dev, + struct ieee80211_vif *vif, bool enable) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + u32 addr; + + addr = mt7996_mac_wtbl_lmac_addr(dev, mvif->sta.wcid.idx, 5); + if (enable) + mt76_set(dev, addr, BIT(5)); + else + mt76_clear(dev, addr, BIT(5)); +} + static void mt7996_mac_decode_he_radiotap_ru(struct mt76_rx_status *status, struct ieee80211_radiotap_he *he, diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 21dea3fa7dc1..d43530a9786b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -523,6 +523,9 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw, mt7996_mcu_add_obss_spr(dev, vif, info->he_obss_pd.enable); } + if (changed & BSS_CHANGED_ERP_CTS_PROT) + mt7996_mac_enable_rtscts(dev, vif, info->use_cts_prot); + if (changed & BSS_CHANGED_ERP_SLOT) { int slottime = info->use_short_slot ? 9 : 20; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index 167f615f099e..00c58878524e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -464,6 +464,8 @@ bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask); void mt7996_mac_reset_counters(struct mt7996_phy *phy); void mt7996_mac_cca_stats_reset(struct mt7996_phy *phy); void mt7996_mac_enable_nf(struct mt7996_dev *dev, u8 band); +void mt7996_mac_enable_rtscts(struct mt7996_dev *dev, + struct ieee80211_vif *vif, bool enable); void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, struct sk_buff *skb, struct mt76_wcid *wcid, int pid, struct ieee80211_key_conf *key, u32 changed); From ea5d99d07fbff328f2ed56733178608fa1add793 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Thu, 1 Dec 2022 11:44:43 +0800 Subject: [PATCH 140/144] wifi: mt76: mt7996: enable ack signal support This reports signal strength of ACK packets from the peer as measured at each interface. Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7996/init.c | 7 +++++++ .../net/wireless/mediatek/mt76/mt7996/mac.c | 19 ++++++++++++++++++- .../net/wireless/mediatek/mt76/mt7996/main.c | 8 ++++++++ .../net/wireless/mediatek/mt76/mt7996/mmio.c | 17 +++++++++-------- .../wireless/mediatek/mt76/mt7996/mt7996.h | 5 +++++ .../net/wireless/mediatek/mt76/mt7996/regs.h | 17 +++++++++++++---- 6 files changed, 60 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/init.c b/drivers/net/wireless/mediatek/mt76/mt7996/init.c index cd1657e3585d..46b290526092 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/init.c @@ -180,6 +180,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw) wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_HE); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_DISCOVERY); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT); if (!mdev->dev->of_node || !of_property_read_bool(mdev->dev->of_node, @@ -240,6 +241,12 @@ mt7996_mac_init_band(struct mt7996_dev *dev, u8 band) set = FIELD_PREP(MT_WF_RMAC_MIB_OBSS_BACKOFF, 0) | FIELD_PREP(MT_WF_RMAC_MIB_ED_OFFSET, 4); mt76_rmw(dev, MT_WF_RMAC_MIB_AIRTIME0(band), mask, set); + + /* filter out non-resp frames and get instanstaeous signal reporting */ + mask = MT_WTBLOFF_RSCR_RCPI_MODE | MT_WTBLOFF_RSCR_RCPI_PARAM; + set = FIELD_PREP(MT_WTBLOFF_RSCR_RCPI_MODE, 0) | + FIELD_PREP(MT_WTBLOFF_RSCR_RCPI_PARAM, 0x3); + mt76_rmw(dev, MT_WTBLOFF_RSCR(band), mask, set); } static void mt7996_mac_init(struct mt7996_dev *dev) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 9e6aa9c3e566..0b3e28748e76 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -10,7 +10,7 @@ #include "mac.h" #include "mcu.h" -#define to_rssi(field, rxv) ((FIELD_GET(field, rxv) - 220) / 2) +#define to_rssi(field, rcpi) ((FIELD_GET(field, rcpi) - 220) / 2) #define HE_BITS(f) cpu_to_le16(IEEE80211_RADIOTAP_HE_##f) #define HE_PREP(f, m, v) le16_encode_bits(le32_get_bits(v, MT_CRXV_HE_##m),\ @@ -124,6 +124,7 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev) bool clear = false; u32 addr, val; u16 idx; + s8 rssi[4]; u8 bw; spin_lock_bh(&dev->sta_poll_lock); @@ -137,6 +138,8 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev) spin_unlock_bh(&dev->sta_poll_lock); idx = msta->wcid.idx; + + /* refresh peer's airtime reporting */ addr = mt7996_mac_wtbl_lmac_addr(dev, idx, 20); for (i = 0; i < IEEE80211_NUM_ACS; i++) { @@ -213,6 +216,20 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev) else rate->flags &= ~RATE_INFO_FLAGS_SHORT_GI; } + + /* get signal strength of resp frames (CTS/BA/ACK) */ + addr = mt7996_mac_wtbl_lmac_addr(dev, idx, 34); + val = mt76_rr(dev, addr); + + rssi[0] = to_rssi(GENMASK(7, 0), val); + rssi[1] = to_rssi(GENMASK(15, 8), val); + rssi[2] = to_rssi(GENMASK(23, 16), val); + rssi[3] = to_rssi(GENMASK(31, 14), val); + + msta->ack_signal = + mt76_rx_signal(msta->vif->phy->mt76->antenna_mask, rssi); + + ewma_avg_signal_add(&msta->avg_ack_signal, -msta->ack_signal); } rcu_read_unlock(); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index d43530a9786b..f2129be25d99 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -595,6 +595,8 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, msta->wcid.tx_info |= MT_WCID_TX_INFO_SET; msta->jiffies = jiffies; + ewma_avg_signal_init(&msta->avg_ack_signal); + mt7996_mac_wtbl_update(dev, idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); @@ -917,6 +919,12 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw, } sinfo->txrate.flags = txrate->flags; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); + + sinfo->ack_signal = (s8)msta->ack_signal; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL); + + sinfo->avg_ack_signal = -(s8)ewma_avg_signal_read(&msta->avg_ack_signal); + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL_AVG); } static void mt7996_sta_rc_work(void *data, struct ieee80211_sta *sta) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c index 0d097cda4da7..521769eb6b0e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c @@ -12,14 +12,15 @@ #include "../trace.h" static const struct __base mt7996_reg_base[] = { - [WF_AGG_BASE] = { { 0x820e2000, 0x820f2000, 0x830e2000 } }, - [WF_MIB_BASE] = { { 0x820ed000, 0x820fd000, 0x830ed000 } }, - [WF_TMAC_BASE] = { { 0x820e4000, 0x820f4000, 0x830e4000 } }, - [WF_RMAC_BASE] = { { 0x820e5000, 0x820f5000, 0x830e5000 } }, - [WF_ARB_BASE] = { { 0x820e3000, 0x820f3000, 0x830e3000 } }, - [WF_LPON_BASE] = { { 0x820eb000, 0x820fb000, 0x830eb000 } }, - [WF_ETBF_BASE] = { { 0x820ea000, 0x820fa000, 0x830ea000 } }, - [WF_DMA_BASE] = { { 0x820e7000, 0x820f7000, 0x830e7000 } }, + [WF_AGG_BASE] = { { 0x820e2000, 0x820f2000, 0x830e2000 } }, + [WF_ARB_BASE] = { { 0x820e3000, 0x820f3000, 0x830e3000 } }, + [WF_TMAC_BASE] = { { 0x820e4000, 0x820f4000, 0x830e4000 } }, + [WF_RMAC_BASE] = { { 0x820e5000, 0x820f5000, 0x830e5000 } }, + [WF_DMA_BASE] = { { 0x820e7000, 0x820f7000, 0x830e7000 } }, + [WF_WTBLOFF_BASE] = { { 0x820e9000, 0x820f9000, 0x830e9000 } }, + [WF_ETBF_BASE] = { { 0x820ea000, 0x820fa000, 0x830ea000 } }, + [WF_LPON_BASE] = { { 0x820eb000, 0x820fb000, 0x830eb000 } }, + [WF_MIB_BASE] = { { 0x820ed000, 0x820fd000, 0x830ed000 } }, }; static const struct __map mt7996_reg_map[] = { diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index 00c58878524e..17dcd05d3459 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -85,6 +85,8 @@ struct mt7996_twt_flow { u8 sched:1; }; +DECLARE_EWMA(avg_signal, 10, 8) + struct mt7996_sta { struct mt76_wcid wcid; /* must be first */ @@ -94,6 +96,9 @@ struct mt7996_sta { struct list_head rc_list; u32 airtime_ac[8]; + int ack_signal; + struct ewma_avg_signal avg_ack_signal; + unsigned long changed; unsigned long jiffies; unsigned long ampdu_state; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/regs.h b/drivers/net/wireless/mediatek/mt76/mt7996/regs.h index 1d53611da4d8..794f61b93a46 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/regs.h @@ -25,13 +25,14 @@ struct mt7996_reg_desc { enum base_rev { WF_AGG_BASE, - WF_MIB_BASE, + WF_ARB_BASE, WF_TMAC_BASE, WF_RMAC_BASE, - WF_ARB_BASE, - WF_LPON_BASE, - WF_ETBF_BASE, WF_DMA_BASE, + WF_WTBLOFF_BASE, + WF_ETBF_BASE, + WF_LPON_BASE, + WF_MIB_BASE, __MT_REG_BASE_MAX, }; @@ -97,6 +98,14 @@ enum base_rev { #define MT_DMA_TCRF1(_band) MT_WF_DMA(_band, 0x054) #define MT_DMA_TCRF1_QIDX GENMASK(15, 13) +/* WTBLOFF TOP: band 0(0x820e9000), band 1(0x820f9000), band 2(0x830e9000) */ +#define MT_WTBLOFF_BASE(_band) __BASE(WF_WTBLOFF_BASE, (_band)) +#define MT_WTBLOFF(_band, ofs) (MT_WTBLOFF_BASE(_band) + (ofs)) + +#define MT_WTBLOFF_RSCR(_band) MT_WTBLOFF(_band, 0x008) +#define MT_WTBLOFF_RSCR_RCPI_MODE GENMASK(31, 30) +#define MT_WTBLOFF_RSCR_RCPI_PARAM GENMASK(25, 24) + /* ETBF: band 0(0x820ea000), band 1(0x820fa000), band 2(0x830ea000) */ #define MT_WF_ETBF_BASE(_band) __BASE(WF_ETBF_BASE, (_band)) #define MT_WF_ETBF(_band, ofs) (MT_WF_ETBF_BASE(_band) + (ofs)) From cf6dc2db17fecd0bbae498f12c1b515f0964763b Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Thu, 1 Dec 2022 16:03:32 +0800 Subject: [PATCH 141/144] wifi: mt76: mt7996: add support to configure spatial reuse parameter set The SPR parameter set comprises OBSS PD threshold for SRG and non SRG and Bitmap of BSS color and partial BSSID. This adds support to configure fields of SPR element to firmware. User can disable firmware SR algorithms by turning sr_scene_detect off. Signed-off-by: Ryder Lee Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7996/main.c | 6 +- .../net/wireless/mediatek/mt76/mt7996/mcu.c | 192 +++++++++++++++++- .../net/wireless/mediatek/mt76/mt7996/mcu.h | 7 + .../wireless/mediatek/mt76/mt7996/mt7996.h | 4 +- 4 files changed, 196 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index f2129be25d99..4421cd54311b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -518,10 +518,8 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw, mt7996_mcu_add_sta(dev, vif, NULL, join); } - if (changed & BSS_CHANGED_ASSOC) { + if (changed & BSS_CHANGED_ASSOC) mt7996_mcu_add_bss_info(phy, vif, vif->cfg.assoc); - mt7996_mcu_add_obss_spr(dev, vif, info->he_obss_pd.enable); - } if (changed & BSS_CHANGED_ERP_CTS_PROT) mt7996_mac_enable_rtscts(dev, vif, info->use_cts_prot); @@ -545,7 +543,7 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw, mt7996_mcu_set_tx(dev, vif); if (changed & BSS_CHANGED_HE_OBSS_PD) - mt7996_mcu_add_obss_spr(dev, vif, info->he_obss_pd.enable); + mt7996_mcu_add_obss_spr(phy, vif, &info->he_obss_pd); if (changed & BSS_CHANGED_HE_BSS_COLOR) mt7996_update_bss_color(hw, vif, &info->he_bss_color); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 123076db8021..04e1d10bbd21 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -71,6 +71,10 @@ struct mt7996_fw_region { #define HE_PHY(p, c) u8_get_bits(c, IEEE80211_HE_PHY_##p) #define HE_MAC(m, c) u8_get_bits(c, IEEE80211_HE_MAC_##m) +static bool sr_scene_detect = true; +module_param(sr_scene_detect, bool, 0644); +MODULE_PARM_DESC(sr_scene_detect, "Enable firmware scene detection algorithm"); + static u8 mt7996_mcu_get_sta_nss(u16 mcs_map) { @@ -3123,29 +3127,203 @@ int mt7996_mcu_set_txbf(struct mt7996_dev *dev, u8 action) return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(BF), true); } -int mt7996_mcu_add_obss_spr(struct mt7996_dev *dev, struct ieee80211_vif *vif, - bool enable) +static int +mt7996_mcu_enable_obss_spr(struct mt7996_phy *phy, u16 action, u8 val) { -#define MT_SPR_ENABLE 1 - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_dev *dev = phy->dev; struct { u8 band_idx; u8 __rsv[3]; __le16 tag; __le16 len; + __le32 val; } __packed req = { - .band_idx = mvif->mt76.band_idx, - .tag = cpu_to_le16(UNI_CMD_SR_ENABLE), + .band_idx = phy->mt76->band_idx, + .tag = cpu_to_le16(action), .len = cpu_to_le16(sizeof(req) - 4), - .val = cpu_to_le32(enable), + .val = cpu_to_le32(val), }; return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SR), &req, sizeof(req), true); } +static int +mt7996_mcu_set_obss_spr_pd(struct mt7996_phy *phy, + struct ieee80211_he_obss_pd *he_obss_pd) +{ + struct mt7996_dev *dev = phy->dev; + u8 max_th = 82, non_srg_max_th = 62; + struct { + u8 band_idx; + u8 __rsv[3]; + + __le16 tag; + __le16 len; + + u8 pd_th_non_srg; + u8 pd_th_srg; + u8 period_offs; + u8 rcpi_src; + __le16 obss_pd_min; + __le16 obss_pd_min_srg; + u8 resp_txpwr_mode; + u8 txpwr_restrict_mode; + u8 txpwr_ref; + u8 __rsv2[3]; + } __packed req = { + .band_idx = phy->mt76->band_idx, + .tag = cpu_to_le16(UNI_CMD_SR_SET_PARAM), + .len = cpu_to_le16(sizeof(req) - 4), + .obss_pd_min = cpu_to_le16(max_th), + .obss_pd_min_srg = cpu_to_le16(max_th), + .txpwr_restrict_mode = 2, + .txpwr_ref = 21 + }; + int ret; + + /* disable firmware dynamical PD asjustment */ + ret = mt7996_mcu_enable_obss_spr(phy, UNI_CMD_SR_ENABLE_DPD, false); + if (ret) + return ret; + + if (he_obss_pd->sr_ctrl & + IEEE80211_HE_SPR_NON_SRG_OBSS_PD_SR_DISALLOWED) + req.pd_th_non_srg = max_th; + else if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_NON_SRG_OFFSET_PRESENT) + req.pd_th_non_srg = max_th - he_obss_pd->non_srg_max_offset; + else + req.pd_th_non_srg = non_srg_max_th; + + if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_SRG_INFORMATION_PRESENT) + req.pd_th_srg = max_th - he_obss_pd->max_offset; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SR), + &req, sizeof(req), true); +} + +static int +mt7996_mcu_set_obss_spr_siga(struct mt7996_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_he_obss_pd *he_obss_pd) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_dev *dev = phy->dev; + u8 omac = mvif->mt76.omac_idx; + struct { + u8 band_idx; + u8 __rsv[3]; + + __le16 tag; + __le16 len; + + u8 omac; + u8 __rsv2[3]; + u8 flag[20]; + } __packed req = { + .band_idx = phy->mt76->band_idx, + .tag = cpu_to_le16(UNI_CMD_SR_SET_SIGA), + .len = cpu_to_le16(sizeof(req) - 4), + .omac = omac > HW_BSSID_MAX ? omac - 12 : omac, + }; + int ret; + + if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_HESIGA_SR_VAL15_ALLOWED) + req.flag[req.omac] = 0xf; + else + return 0; + + /* switch to normal AP mode */ + ret = mt7996_mcu_enable_obss_spr(phy, UNI_CMD_SR_ENABLE_MODE, 0); + if (ret) + return ret; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SR), + &req, sizeof(req), true); +} + +static int +mt7996_mcu_set_obss_spr_bitmap(struct mt7996_phy *phy, + struct ieee80211_he_obss_pd *he_obss_pd) +{ + struct mt7996_dev *dev = phy->dev; + struct { + u8 band_idx; + u8 __rsv[3]; + + __le16 tag; + __le16 len; + + __le32 color_l[2]; + __le32 color_h[2]; + __le32 bssid_l[2]; + __le32 bssid_h[2]; + } __packed req = { + .band_idx = phy->mt76->band_idx, + .tag = cpu_to_le16(UNI_CMD_SR_SET_SRG_BITMAP), + .len = cpu_to_le16(sizeof(req) - 4), + }; + u32 bitmap; + + memcpy(&bitmap, he_obss_pd->bss_color_bitmap, sizeof(bitmap)); + req.color_l[req.band_idx] = cpu_to_le32(bitmap); + + memcpy(&bitmap, he_obss_pd->bss_color_bitmap + 4, sizeof(bitmap)); + req.color_h[req.band_idx] = cpu_to_le32(bitmap); + + memcpy(&bitmap, he_obss_pd->partial_bssid_bitmap, sizeof(bitmap)); + req.bssid_l[req.band_idx] = cpu_to_le32(bitmap); + + memcpy(&bitmap, he_obss_pd->partial_bssid_bitmap + 4, sizeof(bitmap)); + req.bssid_h[req.band_idx] = cpu_to_le32(bitmap); + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SR), &req, + sizeof(req), true); +} + +int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_he_obss_pd *he_obss_pd) +{ + int ret; + + /* enable firmware scene detection algorithms */ + ret = mt7996_mcu_enable_obss_spr(phy, UNI_CMD_SR_ENABLE_SD, + sr_scene_detect); + if (ret) + return ret; + + /* firmware dynamically adjusts PD threshold so skip manual control */ + if (sr_scene_detect && !he_obss_pd->enable) + return 0; + + /* enable spatial reuse */ + ret = mt7996_mcu_enable_obss_spr(phy, UNI_CMD_SR_ENABLE, + he_obss_pd->enable); + if (ret) + return ret; + + if (sr_scene_detect || !he_obss_pd->enable) + return 0; + + ret = mt7996_mcu_enable_obss_spr(phy, UNI_CMD_SR_ENABLE_TX, true); + if (ret) + return ret; + + /* set SRG/non-SRG OBSS PD threshold */ + ret = mt7996_mcu_set_obss_spr_pd(phy, he_obss_pd); + if (ret) + return ret; + + /* Set SR prohibit */ + ret = mt7996_mcu_set_obss_spr_siga(phy, vif, he_obss_pd); + if (ret) + return ret; + + /* set SRG BSS color/BSSID bitmap */ + return mt7996_mcu_set_obss_spr_bitmap(phy, he_obss_pd); +} + int mt7996_mcu_update_bss_color(struct mt7996_dev *dev, struct ieee80211_vif *vif, struct cfg80211_he_bss_color *he_bss_color) { diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h index ff12a7168bd8..6084b2337598 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h @@ -617,6 +617,13 @@ enum { enum{ UNI_CMD_SR_ENABLE = 0x1, + UNI_CMD_SR_ENABLE_SD, + UNI_CMD_SR_ENABLE_MODE, + UNI_CMD_SR_ENABLE_DPD = 0x12, + UNI_CMD_SR_ENABLE_TX, + UNI_CMD_SR_SET_SRG_BITMAP = 0x80, + UNI_CMD_SR_SET_PARAM = 0xc1, + UNI_CMD_SR_SET_SIGA = 0xd0, }; enum { diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index 17dcd05d3459..725344791b4c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -407,8 +407,8 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int enable); int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev, struct ieee80211_vif *vif, u32 changed); -int mt7996_mcu_add_obss_spr(struct mt7996_dev *dev, struct ieee80211_vif *vif, - bool enable); +int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_he_obss_pd *he_obss_pd); int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta, bool changed); int mt7996_set_channel(struct mt7996_phy *phy); From 6a70a90dc1a74aa85598184f5a4c2f14316422b1 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Thu, 1 Dec 2022 09:51:55 +0100 Subject: [PATCH 142/144] wifi: mt76: mt7915: mmio: fix naming convention Rename mt7915_wed_release_rx_buf in mt7915_mmio_wed_release_rx_buf, mt7915_wed_init_rx_buf in mt7915_mmio_wed_init_rx_buf and mt7915_wed_release_rx_buf in mt7915_mmio_wed_release_rx_buf Signed-off-by: Lorenzo Bianconi Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7915/mmio.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c index 5a4141682f04..0a95c3da241b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -591,7 +591,7 @@ static void mt7915_mmio_wed_offload_disable(struct mtk_wed_device *wed) MT_AGG_ACR_PPDU_TXS2H); } -static void mt7915_wed_release_rx_buf(struct mtk_wed_device *wed) +static void mt7915_mmio_wed_release_rx_buf(struct mtk_wed_device *wed) { struct mt7915_dev *dev; struct page *page; @@ -621,7 +621,7 @@ static void mt7915_wed_release_rx_buf(struct mtk_wed_device *wed) memset(&wed->rx_buf_ring.rx_page, 0, sizeof(wed->rx_buf_ring.rx_page)); } -static u32 mt7915_wed_init_rx_buf(struct mtk_wed_device *wed, int size) +static u32 mt7915_mmio_wed_init_rx_buf(struct mtk_wed_device *wed, int size) { struct mtk_rxbm_desc *desc = wed->rx_buf_ring.desc; struct mt7915_dev *dev; @@ -661,7 +661,7 @@ static u32 mt7915_wed_init_rx_buf(struct mtk_wed_device *wed, int size) return 0; unmap: - mt7915_wed_release_rx_buf(wed); + mt7915_mmio_wed_release_rx_buf(wed); return -ENOMEM; } @@ -768,8 +768,8 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr, wed->wlan.init_buf = mt7915_wed_init_buf; wed->wlan.offload_enable = mt7915_mmio_wed_offload_enable; wed->wlan.offload_disable = mt7915_mmio_wed_offload_disable; - wed->wlan.init_rx_buf = mt7915_wed_init_rx_buf; - wed->wlan.release_rx_buf = mt7915_wed_release_rx_buf; + wed->wlan.init_rx_buf = mt7915_mmio_wed_init_rx_buf; + wed->wlan.release_rx_buf = mt7915_mmio_wed_release_rx_buf; wed->wlan.update_wo_rx_stats = mt7915_mmio_wed_update_rx_stats; dev->mt76.rx_token_size = wed->wlan.rx_npkt; From f23a0cea8bd62f4a348974340bd9e753dc9b2941 Mon Sep 17 00:00:00 2001 From: Leon Yen Date: Thu, 1 Dec 2022 18:38:42 +0800 Subject: [PATCH 143/144] wifi: mt76: mt7921e: add pci .shutdown() support Some combinations of hosts cannnot detect mt7921e after reboot. The interoperability issue is caused by the status mismatch between host and chip fw. In such cases, the driver should stop chip activities and reset chip to default state before reboot. Suggested-by: angelogioacchino.delregno@collabora.com Co-developed-by: Deren Wu Signed-off-by: Deren Wu Signed-off-by: Leon Yen Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt7921/pci.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c index 28342ec940f0..cb72ded37256 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c @@ -507,6 +507,21 @@ failed: return err; } +static void mt7921_pci_shutdown(struct pci_dev *pdev) +{ + struct mt76_dev *mdev = pci_get_drvdata(pdev); + struct mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76); + struct mt76_connac_pm *pm = &dev->pm; + + cancel_delayed_work_sync(&pm->ps_work); + cancel_work_sync(&pm->wake_work); + + /* chip cleanup before reboot */ + mt7921_mcu_drv_pmctrl(dev); + mt7921_dma_cleanup(dev); + mt7921_wfsys_reset(dev); +} + static DEFINE_SIMPLE_DEV_PM_OPS(mt7921_pm_ops, mt7921_pci_suspend, mt7921_pci_resume); static struct pci_driver mt7921_pci_driver = { @@ -514,6 +529,7 @@ static struct pci_driver mt7921_pci_driver = { .id_table = mt7921_pci_device_table, .probe = mt7921_pci_probe, .remove = mt7921_pci_remove, + .shutdown = mt7921_pci_shutdown, .driver.pm = pm_sleep_ptr(&mt7921_pm_ops), }; From d03407183d97554dfffea70f385b5bdd520f846c Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Fri, 2 Dec 2022 12:30:27 +0200 Subject: [PATCH 144/144] wifi: ath10k: fix QCOM_SMEM dependency Nathan noticed that when HWSPINLOCK is disabled there's a Kconfig warning: WARNING: unmet direct dependencies detected for QCOM_SMEM Depends on [n]: (ARCH_QCOM [=y] || COMPILE_TEST [=n]) && HWSPINLOCK [=n] Selected by [m]: - ATH10K_SNOC [=m] && NETDEVICES [=y] && WLAN [=y] && WLAN_VENDOR_ATH [=y] && ATH10K [=m] && (ARCH_QCOM [=y] || COMPILE_TEST [=n]) The problem here is that QCOM_SMEM depends on HWSPINLOCK so we cannot select QCOM_SMEM and instead we neeed to use 'depends on'. Reported-by: Nathan Chancellor Link: https://lore.kernel.org/all/Y4YsyaIW+CPdHWv3@dev-arch.thelio-3990X/ Fixes: 4d79f6f34bbb ("wifi: ath10k: Store WLAN firmware version in SMEM image table") Signed-off-by: Kalle Valo Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20221202103027.25974-1-kvalo@kernel.org --- drivers/net/wireless/ath/ath10k/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig index e0a51dad8e42..e6ea884cafc1 100644 --- a/drivers/net/wireless/ath/ath10k/Kconfig +++ b/drivers/net/wireless/ath/ath10k/Kconfig @@ -44,7 +44,7 @@ config ATH10K_SNOC tristate "Qualcomm ath10k SNOC support" depends on ATH10K depends on ARCH_QCOM || COMPILE_TEST - select QCOM_SMEM + depends on QCOM_SMEM select QCOM_SCM select QCOM_QMI_HELPERS help