From 2d85152020d576010a5396d10c864a66b4ce8cf3 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Fri, 6 Feb 2026 20:01:34 +0200 Subject: [PATCH 001/124] wifi: rtw89: Turbo mode for RTL8851BU/RTL8852BU RTL8851BU and RTL8852BU currently have much lower TX speed than expected, ~100 and ~300 Mbps, respectively. This is because part of the chip's memory is allocated for some unknown firmware features instead of the TX buffers. The vendor drivers have a module parameter called "rtw_quota_turbo_en", which can be used to choose between full TX speed or the unknown firmware features. It is on by default. Change the relevant chip parameters to implement the turbo mode. Do it only for USB because PCI is already fast even without the turbo mode. It's unclear if SDIO will need it or not. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/88cce107-ff52-496f-bb77-b930e4182b67@gmail.com --- drivers/net/wireless/realtek/rtw89/mac.c | 31 +++++++++--------- drivers/net/wireless/realtek/rtw89/mac.h | 16 +++++----- drivers/net/wireless/realtek/rtw89/rtw8851b.c | 32 +++++++++---------- drivers/net/wireless/realtek/rtw89/rtw8852b.c | 24 +++++++------- 4 files changed, 51 insertions(+), 52 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 8472f1a63951..c98ca2a82194 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -1729,8 +1729,8 @@ const struct rtw89_mac_size_set rtw89_mac_size = { /* 8852C PCIE SCC */ .wde_size19 = {RTW89_WDE_PG_64, 3328, 0,}, .wde_size23 = {RTW89_WDE_PG_64, 1022, 2,}, - /* 8852B USB2.0/USB3.0 SCC */ - .wde_size25 = {RTW89_WDE_PG_64, 162, 94,}, + /* 8852B USB2.0/USB3.0 SCC turbo */ + .wde_size30 = {RTW89_WDE_PG_64, 220, 36,}, /* 8852C USB2.0 */ .wde_size31 = {RTW89_WDE_PG_64, 384, 0,}, /* PCIE */ @@ -1754,10 +1754,10 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .ple_size19 = {RTW89_PLE_PG_128, 1904, 16,}, .ple_size20_v1 = {RTW89_PLE_PG_128, 2554, 182, 40960,}, .ple_size22_v1 = {RTW89_PLE_PG_128, 2736, 0, 40960,}, - /* 8852B USB2.0 SCC */ - .ple_size32 = {RTW89_PLE_PG_128, 620, 20,}, - /* 8852B USB3.0 SCC */ - .ple_size33 = {RTW89_PLE_PG_128, 632, 8,}, + /* 8851B USB2.0 SCC turbo */ + .ple_size27 = {RTW89_PLE_PG_128, 1396, 12,}, + /* 8852B USB3.0 SCC turbo */ + .ple_size31 = {RTW89_PLE_PG_128, 1392, 16,}, /* 8852C USB2.0 */ .ple_size34 = {RTW89_PLE_PG_128, 3374, 18,}, /* PCIE 64 */ @@ -1780,8 +1780,8 @@ const struct rtw89_mac_size_set rtw89_mac_size = { .wde_qt18 = {3228, 60, 0, 40,}, .wde_qt19_v1 = {613, 6, 0, 20,}, .wde_qt23 = {958, 48, 0, 16,}, - /* 8852B USB2.0/USB3.0 SCC */ - .wde_qt25 = {152, 2, 0, 8,}, + /* 8852B USB2.0/USB3.0 SCC turbo */ + .wde_qt30 = {210, 2, 0, 8,}, /* 8852C USB2.0 */ .wde_qt31 = {338, 6, 0, 40,}, .ple_qt0 = {320, 320, 32, 16, 13, 13, 292, 292, 64, 18, 1, 4, 0,}, @@ -1799,6 +1799,9 @@ const struct rtw89_mac_size_set rtw89_mac_size = { /* 8852A USB SCC */ .ple_qt25 = {1536, 0, 16, 48, 13, 13, 360, 0, 32, 40, 8, 0,}, .ple_qt26 = {2654, 0, 1134, 48, 64, 13, 1478, 0, 64, 128, 120, 0,}, + /* 8852B USB3.0 SCC turbo */ + .ple_qt27 = {1040, 0, 16, 48, 13, 13, 178, 0, 32, 14, 8, 0,}, + .ple_qt28 = {1040, 0, 32, 48, 43, 13, 208, 0, 62, 14, 24, 0,}, /* USB 52C USB3.0 */ .ple_qt42 = {1068, 0, 16, 48, 4, 13, 178, 0, 16, 1, 8, 16, 0,}, .ple_qt42_v2 = {91, 91, 32, 16, 19, 13, 91, 91, 44, 18, 1, 4, 0, 0,}, @@ -1817,13 +1820,9 @@ const struct rtw89_mac_size_set rtw89_mac_size = { /* PCIE 64 */ .ple_qt58 = {147, 0, 16, 20, 157, 13, 229, 0, 172, 14, 24, 0,}, .ple_qt59 = {147, 0, 32, 20, 1860, 13, 2025, 0, 1879, 14, 24, 0,}, - /* USB2.0 52B SCC */ - .ple_qt72 = {130, 0, 16, 48, 4, 13, 322, 0, 32, 14, 8, 0, 0,}, - /* USB2.0 52B 92K */ - .ple_qt73 = {130, 0, 32, 48, 37, 13, 355, 0, 65, 14, 24, 0, 0,}, - /* USB3.0 52B 92K */ - .ple_qt74 = {286, 0, 16, 48, 4, 13, 178, 0, 32, 14, 8, 0, 0,}, - .ple_qt75 = {286, 0, 32, 48, 37, 13, 211, 0, 65, 14, 24, 0, 0,}, + /* 8851B USB2.0 SCC turbo */ + .ple_qt61 = {858, 0, 16, 48, 4, 13, 370, 0, 32, 14, 8, 0, 0,}, + .ple_qt62 = {858, 0, 32, 48, 37, 13, 403, 0, 65, 14, 24, 0, 0,}, /* USB2.0 52C */ .ple_qt78 = {1560, 0, 16, 48, 13, 13, 390, 0, 32, 38, 8, 16, 0,}, /* USB2.0 52C */ @@ -2004,7 +2003,7 @@ static u32 dle_expected_used_size(struct rtw89_dev *rtwdev, { u32 size = rtwdev->chip->fifo_size; - if (mode == RTW89_QTA_SCC) + if (mode == RTW89_QTA_SCC && rtwdev->hci.type != RTW89_HCI_TYPE_USB) size -= rtwdev->chip->dle_scc_rsvd_size; return size; diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index e71a71648ab8..e6b715b95409 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -938,7 +938,7 @@ struct rtw89_mac_size_set { const struct rtw89_dle_size wde_size18_v1; const struct rtw89_dle_size wde_size19; const struct rtw89_dle_size wde_size23; - const struct rtw89_dle_size wde_size25; + const struct rtw89_dle_size wde_size30; const struct rtw89_dle_size wde_size31; const struct rtw89_dle_size ple_size0; const struct rtw89_dle_size ple_size1; @@ -953,8 +953,8 @@ struct rtw89_mac_size_set { const struct rtw89_dle_size ple_size19; const struct rtw89_dle_size ple_size20_v1; const struct rtw89_dle_size ple_size22_v1; - const struct rtw89_dle_size ple_size32; - const struct rtw89_dle_size ple_size33; + const struct rtw89_dle_size ple_size27; + const struct rtw89_dle_size ple_size31; const struct rtw89_dle_size ple_size34; const struct rtw89_wde_quota wde_qt0; const struct rtw89_wde_quota wde_qt1; @@ -968,7 +968,7 @@ struct rtw89_mac_size_set { const struct rtw89_wde_quota wde_qt18; const struct rtw89_wde_quota wde_qt19_v1; const struct rtw89_wde_quota wde_qt23; - const struct rtw89_wde_quota wde_qt25; + const struct rtw89_wde_quota wde_qt30; const struct rtw89_wde_quota wde_qt31; const struct rtw89_ple_quota ple_qt0; const struct rtw89_ple_quota ple_qt1; @@ -980,6 +980,8 @@ struct rtw89_mac_size_set { const struct rtw89_ple_quota ple_qt18; const struct rtw89_ple_quota ple_qt25; const struct rtw89_ple_quota ple_qt26; + const struct rtw89_ple_quota ple_qt27; + const struct rtw89_ple_quota ple_qt28; const struct rtw89_ple_quota ple_qt42; const struct rtw89_ple_quota ple_qt42_v2; const struct rtw89_ple_quota ple_qt43; @@ -991,10 +993,8 @@ struct rtw89_mac_size_set { const struct rtw89_ple_quota ple_qt57; const struct rtw89_ple_quota ple_qt58; const struct rtw89_ple_quota ple_qt59; - const struct rtw89_ple_quota ple_qt72; - const struct rtw89_ple_quota ple_qt73; - const struct rtw89_ple_quota ple_qt74; - const struct rtw89_ple_quota ple_qt75; + const struct rtw89_ple_quota ple_qt61; + const struct rtw89_ple_quota ple_qt62; const struct rtw89_ple_quota ple_qt78; const struct rtw89_ple_quota ple_qt79; const struct rtw89_ple_quota ple_qt_52a_wow; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index d6deb44a685b..b63c0e785209 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -52,25 +52,25 @@ static const struct rtw89_hfc_param_ini rtw8851b_hfc_param_ini_pcie[] = { }; static const struct rtw89_hfc_ch_cfg rtw8851b_hfc_chcfg_usb[] = { - {18, 152, grp_0}, /* ACH 0 */ - {18, 152, grp_0}, /* ACH 1 */ - {18, 152, grp_0}, /* ACH 2 */ - {18, 152, grp_0}, /* ACH 3 */ + {18, 210, grp_0}, /* ACH 0 */ + {18, 210, grp_0}, /* ACH 1 */ + {18, 210, grp_0}, /* ACH 2 */ + {18, 210, grp_0}, /* ACH 3 */ {0, 0, grp_0}, /* ACH 4 */ {0, 0, grp_0}, /* ACH 5 */ {0, 0, grp_0}, /* ACH 6 */ {0, 0, grp_0}, /* ACH 7 */ - {18, 152, grp_0}, /* B0MGQ */ - {18, 152, grp_0}, /* B0HIQ */ + {18, 210, grp_0}, /* B0MGQ */ + {18, 210, grp_0}, /* B0HIQ */ {0, 0, grp_0}, /* B1MGQ */ {0, 0, grp_0}, /* B1HIQ */ {0, 0, 0} /* FWCMDQ */ }; static const struct rtw89_hfc_pub_cfg rtw8851b_hfc_pubcfg_usb = { - 152, /* Group 0 */ + 210, /* Group 0 */ 0, /* Group 1 */ - 152, /* Public Max */ + 210, /* Public Max */ 0 /* WP threshold */ }; @@ -111,10 +111,10 @@ static const struct rtw89_dle_mem rtw8851b_dle_mem_pcie[] = { }; static const struct rtw89_dle_mem rtw8851b_dle_mem_usb2[] = { - [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size25, - &rtw89_mac_size.ple_size32, &rtw89_mac_size.wde_qt25, - &rtw89_mac_size.wde_qt25, &rtw89_mac_size.ple_qt72, - &rtw89_mac_size.ple_qt73}, + [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size30, + &rtw89_mac_size.ple_size27, &rtw89_mac_size.wde_qt30, + &rtw89_mac_size.wde_qt30, &rtw89_mac_size.ple_qt61, + &rtw89_mac_size.ple_qt62}, [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size9, &rtw89_mac_size.ple_size8, &rtw89_mac_size.wde_qt4, &rtw89_mac_size.wde_qt4, &rtw89_mac_size.ple_qt13, @@ -124,10 +124,10 @@ static const struct rtw89_dle_mem rtw8851b_dle_mem_usb2[] = { }; static const struct rtw89_dle_mem rtw8851b_dle_mem_usb3[] = { - [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size25, - &rtw89_mac_size.ple_size33, &rtw89_mac_size.wde_qt25, - &rtw89_mac_size.wde_qt25, &rtw89_mac_size.ple_qt74, - &rtw89_mac_size.ple_qt75}, + [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size30, + &rtw89_mac_size.ple_size31, &rtw89_mac_size.wde_qt30, + &rtw89_mac_size.wde_qt30, &rtw89_mac_size.ple_qt27, + &rtw89_mac_size.ple_qt28}, [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size9, &rtw89_mac_size.ple_size8, &rtw89_mac_size.wde_qt4, &rtw89_mac_size.wde_qt4, &rtw89_mac_size.ple_qt13, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 197e3f5fb21b..393986b297ea 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -50,25 +50,25 @@ static const struct rtw89_hfc_param_ini rtw8852b_hfc_param_ini_pcie[] = { }; static const struct rtw89_hfc_ch_cfg rtw8852b_hfc_chcfg_usb[] = { - {18, 152, grp_0}, /* ACH 0 */ - {18, 152, grp_0}, /* ACH 1 */ - {18, 152, grp_0}, /* ACH 2 */ - {18, 152, grp_0}, /* ACH 3 */ + {18, 210, grp_0}, /* ACH 0 */ + {18, 210, grp_0}, /* ACH 1 */ + {18, 210, grp_0}, /* ACH 2 */ + {18, 210, grp_0}, /* ACH 3 */ {0, 0, grp_0}, /* ACH 4 */ {0, 0, grp_0}, /* ACH 5 */ {0, 0, grp_0}, /* ACH 6 */ {0, 0, grp_0}, /* ACH 7 */ - {18, 152, grp_0}, /* B0MGQ */ - {18, 152, grp_0}, /* B0HIQ */ + {18, 210, grp_0}, /* B0MGQ */ + {18, 210, grp_0}, /* B0HIQ */ {0, 0, grp_0}, /* B1MGQ */ {0, 0, grp_0}, /* B1HIQ */ {0, 0, 0} /* FWCMDQ */ }; static const struct rtw89_hfc_pub_cfg rtw8852b_hfc_pubcfg_usb = { - 152, /* Group 0 */ + 210, /* Group 0 */ 0, /* Group 1 */ - 152, /* Public Max */ + 210, /* Public Max */ 0 /* WP threshold */ }; @@ -109,10 +109,10 @@ static const struct rtw89_dle_mem rtw8852b_dle_mem_pcie[] = { }; static const struct rtw89_dle_mem rtw8852b_dle_mem_usb3[] = { - [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size25, - &rtw89_mac_size.ple_size33, &rtw89_mac_size.wde_qt25, - &rtw89_mac_size.wde_qt25, &rtw89_mac_size.ple_qt74, - &rtw89_mac_size.ple_qt75}, + [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size30, + &rtw89_mac_size.ple_size31, &rtw89_mac_size.wde_qt30, + &rtw89_mac_size.wde_qt30, &rtw89_mac_size.ple_qt27, + &rtw89_mac_size.ple_qt28}, [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size9, &rtw89_mac_size.ple_size8, &rtw89_mac_size.wde_qt4, &rtw89_mac_size.wde_qt4, &rtw89_mac_size.ple_qt13, From c95323ea9dfba8ab2bafbaee9da96c53f893873f Mon Sep 17 00:00:00 2001 From: Ching-Te Ku Date: Fri, 13 Feb 2026 13:34:45 +0800 Subject: [PATCH 002/124] wifi: rtw88: coex: Solve LE-HID lag & update coex version to 26020420 When Wi-Fi enters power save, the register value can not be read correctly. If mechanism take the wrong information to make decision, it will run with wrong parameters. It leads Bluetooth low-energy HID lag. Add logic to isolate the wrong register state. BTCOEX Version: 26020420-2020 Desired_BT_Coex_Ver: 0x20 Desired_WL_FW_Ver: 9.9.X Signed-off-by: Ching-Te Ku Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260213053445.19384-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw88/coex.c | 44 ++++++++++++------- drivers/net/wireless/realtek/rtw88/main.h | 1 + drivers/net/wireless/realtek/rtw88/rtw8703b.c | 5 +++ drivers/net/wireless/realtek/rtw88/rtw8723d.c | 5 +++ drivers/net/wireless/realtek/rtw88/rtw8821a.c | 7 ++- drivers/net/wireless/realtek/rtw88/rtw8821c.c | 7 ++- drivers/net/wireless/realtek/rtw88/rtw8822b.c | 5 +++ drivers/net/wireless/realtek/rtw88/rtw8822c.c | 5 ++- 8 files changed, 61 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/coex.c b/drivers/net/wireless/realtek/rtw88/coex.c index b4dc6ff2c175..ee4007fe6c18 100644 --- a/drivers/net/wireless/realtek/rtw88/coex.c +++ b/drivers/net/wireless/realtek/rtw88/coex.c @@ -485,6 +485,13 @@ static void rtw_coex_monitor_bt_ctr(struct rtw_dev *rtwdev) "[BTCoex], Hi-Pri Rx/Tx: %d/%d, Lo-Pri Rx/Tx: %d/%d\n", coex_stat->hi_pri_rx, coex_stat->hi_pri_tx, coex_stat->lo_pri_rx, coex_stat->lo_pri_tx); + + if (coex_stat->wl_under_lps || coex_stat->wl_under_ips || + (coex_stat->hi_pri_rx > 60000 && coex_stat->hi_pri_tx == 60000 && + coex_stat->lo_pri_rx > 60000 && coex_stat->lo_pri_tx == 60000)) + coex_stat->bt_ctr_ok = false; + else + coex_stat->bt_ctr_ok = true; } static void rtw_coex_monitor_bt_enable(struct rtw_dev *rtwdev) @@ -1959,14 +1966,18 @@ static void rtw_coex_action_bt_hid(struct rtw_dev *rtwdev) struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_stat *coex_stat = &coex->stat; struct rtw_efuse *efuse = &rtwdev->efuse; + bool is_bt_ctr_hi = false, is_toggle_table = false; u8 table_case, tdma_case; u32 slot_type = 0; - bool bt_multi_link_remain = false, is_toggle_table = false; rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__); rtw_coex_set_ant_path(rtwdev, false, COEX_SET_ANT_2G); rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[0]); + if (coex_stat->bt_ctr_ok && + coex_stat->lo_pri_rx + coex_stat->lo_pri_tx > 360) + is_bt_ctr_hi = true; + if (efuse->share_ant) { /* Shared-Ant */ if (coex_stat->bt_ble_exist) { @@ -1980,28 +1991,31 @@ static void rtw_coex_action_bt_hid(struct rtw_dev *rtwdev) } } else { /* Legacy HID */ - if (coex_stat->bt_profile_num == 1 && - (coex_stat->bt_multi_link || - (coex_stat->lo_pri_rx + - coex_stat->lo_pri_tx > 360) || - coex_stat->bt_slave || - bt_multi_link_remain)) { - slot_type = TDMA_4SLOT; - table_case = 12; - tdma_case = 20; - } else if (coex_stat->bt_a2dp_active) { + if (coex_stat->bt_a2dp_active) { table_case = 9; tdma_case = 18; + } else if (coex_stat->bt_profile_num == 1 && + (coex_stat->bt_multi_link && + (is_bt_ctr_hi || coex_stat->bt_slave || + coex_stat->bt_multi_link_remain))) { + if (coex_stat->wl_gl_busy && + (coex_stat->wl_rx_rate <= 3 || + coex_stat->wl_rts_rx_rate <= 3)) + table_case = 13; + else + table_case = 12; + + tdma_case = 26; } else if (coex_stat->bt_418_hid_exist && coex_stat->wl_gl_busy) { is_toggle_table = true; slot_type = TDMA_4SLOT; - table_case = 9; - tdma_case = 24; + table_case = 32; + tdma_case = 27; } else if (coex_stat->bt_ble_hid_exist && coex_stat->wl_gl_busy) { - table_case = 32; - tdma_case = 9; + table_case = 36; + tdma_case = 0; } else { table_case = 9; tdma_case = 9; diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h index 1ab70214ce36..1179231a672d 100644 --- a/drivers/net/wireless/realtek/rtw88/main.h +++ b/drivers/net/wireless/realtek/rtw88/main.h @@ -1475,6 +1475,7 @@ struct rtw_coex_stat { bool bt_game_hid_exist; bool bt_hid_handle_cnt; bool bt_mailbox_reply; + bool bt_ctr_ok; bool wl_under_lps; bool wl_under_ips; diff --git a/drivers/net/wireless/realtek/rtw88/rtw8703b.c b/drivers/net/wireless/realtek/rtw88/rtw8703b.c index 821c28d9cb5d..b5e7ae7ebd95 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8703b.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8703b.c @@ -1794,6 +1794,11 @@ static const struct coex_table_para table_sant_8703b[] = { {0x66556aaa, 0x6a5a6aaa}, /* case-30 */ {0xffffffff, 0x5aaa5aaa}, {0x56555555, 0x5a5a5aaa}, + {0xdaffdaff, 0xdaffdaff}, + {0xddffddff, 0xddffddff}, + {0xe5555555, 0xe5555555}, /* case-35 */ + {0xea5a5a5a, 0xea5a5a5a}, + {0xea6a6a6a, 0xea6a6a6a}, }; /* Shared-Antenna TDMA */ diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723d.c b/drivers/net/wireless/realtek/rtw88/rtw8723d.c index 8715e0435f17..a2b3e7a2ad99 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8723d.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8723d.c @@ -1459,6 +1459,11 @@ static const struct coex_table_para table_sant_8723d[] = { {0x66556aaa, 0x6a5a6aaa}, /* case-30 */ {0xffffffff, 0x5aaa5aaa}, {0x56555555, 0x5a5a5aaa}, + {0xdaffdaff, 0xdaffdaff}, + {0xddffddff, 0xddffddff}, + {0xe5555555, 0xe5555555}, /* case-35 */ + {0xea5a5a5a, 0xea5a5a5a}, + {0xea6a6a6a, 0xea6a6a6a}, }; /* Non-Shared-Antenna Coex Table */ diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821a.c b/drivers/net/wireless/realtek/rtw88/rtw8821a.c index 414b77eef07c..cab85203b828 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8821a.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8821a.c @@ -998,7 +998,12 @@ static const struct coex_table_para table_sant_8821a[] = { {0x66556655, 0x66556655}, {0x66556aaa, 0x6a5a6aaa}, /* case-30 */ {0xffffffff, 0x5aaa5aaa}, - {0x56555555, 0x5a5a5aaa} + {0x56555555, 0x5a5a5aaa}, + {0xdaffdaff, 0xdaffdaff}, + {0xddffddff, 0xddffddff}, + {0xe5555555, 0xe5555555}, /* case-35 */ + {0xea5a5a5a, 0xea5a5a5a}, + {0xea6a6a6a, 0xea6a6a6a}, }; /* Non-Shared-Antenna Coex Table */ diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821c.c b/drivers/net/wireless/realtek/rtw88/rtw8821c.c index 2078b067562e..246046da4f13 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8821c.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8821c.c @@ -1727,7 +1727,12 @@ static const struct coex_table_para table_sant_8821c[] = { {0x66556655, 0x66556655}, {0x66556aaa, 0x6a5a6aaa}, /* case-30 */ {0xffffffff, 0x5aaa5aaa}, - {0x56555555, 0x5a5a5aaa} + {0x56555555, 0x5a5a5aaa}, + {0xdaffdaff, 0xdaffdaff}, + {0xddffddff, 0xddffddff}, + {0xe5555555, 0xe5555555}, /* case-35 */ + {0xea5a5a5a, 0xea5a5a5a}, + {0xea6a6a6a, 0xea6a6a6a}, }; /* Non-Shared-Antenna Coex Table */ diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822b.c b/drivers/net/wireless/realtek/rtw88/rtw8822b.c index 4d88cc2f4148..e9e8a7f3f382 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822b.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.c @@ -2217,6 +2217,11 @@ static const struct coex_table_para table_sant_8822b[] = { {0x66556aaa, 0x6a5a6aaa}, /* case-30 */ {0xffffffff, 0x5aaa5aaa}, {0x56555555, 0x5a5a5aaa}, + {0xdaffdaff, 0xdaffdaff}, + {0xddffddff, 0xddffddff}, + {0xe5555555, 0xe5555555}, /* case-35 */ + {0xea5a5a5a, 0xea5a5a5a}, + {0xea6a6a6a, 0xea6a6a6a}, }; /* Non-Shared-Antenna Coex Table */ diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c.c b/drivers/net/wireless/realtek/rtw88/rtw8822c.c index 28c121cf1e68..244c8026479c 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822c.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.c @@ -5035,6 +5035,9 @@ static const struct coex_table_para table_sant_8822c[] = { {0x56555555, 0x5a5a5aaa}, {0xdaffdaff, 0xdaffdaff}, {0xddffddff, 0xddffddff}, + {0xe5555555, 0xe5555555}, /* case-35 */ + {0xea5a5a5a, 0xea5a5a5a}, + {0xea6a6a6a, 0xea6a6a6a}, }; /* Non-Shared-Antenna Coex Table */ @@ -5401,7 +5404,7 @@ const struct rtw_chip_info rtw8822c_hw_spec = { .max_sched_scan_ssids = 4, #endif .max_scan_ie_len = (RTW_PROBE_PG_CNT - 1) * TX_PAGE_SIZE, - .coex_para_ver = 0x22020720, + .coex_para_ver = 0x26020420, .bt_desired_ver = 0x20, .scbd_support = true, .new_scbd10_def = true, From abaa59fb24cad1bfb4aeb3b2a152b5b4eb90a2d3 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 13 Feb 2026 14:15:41 +0800 Subject: [PATCH 003/124] wifi: rtw89: fw: add fw_def struct to put firmware name and format version The RTL8922DE has a RTL8922DE-VS variant which uses different firmware name and format version, and the rule to select firmware type will be needed to extend. Prepare for coming patches. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260213061552.29997-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 3 ++- drivers/net/wireless/realtek/rtw89/core.h | 25 +++++++++++++++++-- drivers/net/wireless/realtek/rtw89/fw.c | 10 +++++--- drivers/net/wireless/realtek/rtw89/fw.h | 1 + drivers/net/wireless/realtek/rtw89/rtw8851b.c | 6 +++-- drivers/net/wireless/realtek/rtw89/rtw8852a.c | 6 +++-- drivers/net/wireless/realtek/rtw89/rtw8852b.c | 6 +++-- .../net/wireless/realtek/rtw89/rtw8852bt.c | 6 +++-- drivers/net/wireless/realtek/rtw89/rtw8852c.c | 6 +++-- drivers/net/wireless/realtek/rtw89/rtw8922a.c | 7 ++++-- 10 files changed, 57 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 36e988277b2b..9d9b91570989 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -6753,7 +6753,8 @@ struct rtw89_dev *rtw89_alloc_ieee80211_hw(struct device *device, bool support_mlo; bool no_chanctx; - firmware = rtw89_early_fw_feature_recognize(device, chip, &early_fw, &fw_format); + firmware = rtw89_early_fw_feature_recognize(device, chip, variant, + &early_fw, &fw_format); ops = kmemdup(&rtw89_ops, sizeof(rtw89_ops), GFP_KERNEL); if (!ops) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 4778957d6b2d..5cb8aacf7644 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4152,6 +4152,11 @@ struct rtw89_reg_imr { u32 set; }; +struct rtw89_fw_def { + const char *fw_basename; + u8 fw_format_max; +}; + struct rtw89_phy_table { const struct rtw89_reg2_def *regs; u32 n_regs; @@ -4494,8 +4499,7 @@ struct rtw89_chip_info { const struct rtw89_chip_ops *ops; const struct rtw89_mac_gen_def *mac_def; const struct rtw89_phy_gen_def *phy_def; - const char *fw_basename; - u8 fw_format_max; + struct rtw89_fw_def fw_def; bool try_ce_fw; u8 bbmcu_nr; u32 needed_fw_elms; @@ -4633,6 +4637,7 @@ struct rtw89_chip_info { struct rtw89_chip_variant { bool no_mcs_12_13: 1; u32 fw_min_ver_code; + const struct rtw89_fw_def *fw_def_override; }; union rtw89_bus_info { @@ -7379,6 +7384,22 @@ void rtw89_chip_calc_rx_gain_normal(struct rtw89_dev *rtwdev, chip->ops->calc_rx_gain_normal(rtwdev, chan, path, phy_idx, calc); } +static inline const struct rtw89_fw_def * +__rtw89_chip_get_fw_def(const struct rtw89_chip_info *chip, + const struct rtw89_chip_variant *variant) +{ + if (variant && variant->fw_def_override) + return variant->fw_def_override; + + return &chip->fw_def; +} + +static inline +const struct rtw89_fw_def *rtw89_chip_get_fw_def(struct rtw89_dev *rtwdev) +{ + return __rtw89_chip_get_fw_def(rtwdev->chip, rtwdev->variant); +} + static inline void rtw89_load_txpwr_table(struct rtw89_dev *rtwdev, const struct rtw89_txpwr_table *tbl) { diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index c52f9e11a8b2..aa42e5c60eb7 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -965,18 +965,20 @@ static void rtw89_fw_recognize_features(struct rtw89_dev *rtwdev) const struct firmware * rtw89_early_fw_feature_recognize(struct device *device, const struct rtw89_chip_info *chip, + const struct rtw89_chip_variant *variant, struct rtw89_fw_info *early_fw, int *used_fw_format) { + const struct rtw89_fw_def *fw_def = __rtw89_chip_get_fw_def(chip, variant); const struct firmware *firmware; char fw_name[64]; int fw_format; u32 ver_code; int ret; - for (fw_format = chip->fw_format_max; fw_format >= 0; fw_format--) { + for (fw_format = fw_def->fw_format_max; fw_format >= 0; fw_format--) { rtw89_fw_get_filename(fw_name, sizeof(fw_name), - chip->fw_basename, fw_format); + fw_def->fw_basename, fw_format); ret = request_firmware(&firmware, fw_name, device); if (!ret) { @@ -2024,11 +2026,11 @@ void rtw89_load_firmware_work(struct work_struct *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, load_firmware_work); - const struct rtw89_chip_info *chip = rtwdev->chip; + const struct rtw89_fw_def *fw_def = rtw89_chip_get_fw_def(rtwdev); char fw_name[64]; rtw89_fw_get_filename(fw_name, sizeof(fw_name), - chip->fw_basename, rtwdev->fw.fw_format); + fw_def->fw_basename, rtwdev->fw.fw_format); rtw89_load_firmware_req(rtwdev, &rtwdev->fw.req, fw_name, false); } diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index d45b6ea6ea1b..c60d419616d6 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -5171,6 +5171,7 @@ int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev); const struct firmware * rtw89_early_fw_feature_recognize(struct device *device, const struct rtw89_chip_info *chip, + const struct rtw89_chip_variant *variant, struct rtw89_fw_info *early_fw, int *used_fw_format); int rtw89_fw_download(struct rtw89_dev *rtwdev, enum rtw89_fw_type type, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index b63c0e785209..fdb053fabae6 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -2580,8 +2580,10 @@ const struct rtw89_chip_info rtw8851b_chip_info = { .ops = &rtw8851b_chip_ops, .mac_def = &rtw89_mac_gen_ax, .phy_def = &rtw89_phy_gen_ax, - .fw_basename = RTW8851B_FW_BASENAME, - .fw_format_max = RTW8851B_FW_FORMAT_MAX, + .fw_def = { + .fw_basename = RTW8851B_FW_BASENAME, + .fw_format_max = RTW8851B_FW_FORMAT_MAX, + }, .try_ce_fw = true, .bbmcu_nr = 0, .needed_fw_elms = 0, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 5ea7a36ab5ab..36d66f4e2dd0 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2265,8 +2265,10 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .ops = &rtw8852a_chip_ops, .mac_def = &rtw89_mac_gen_ax, .phy_def = &rtw89_phy_gen_ax, - .fw_basename = RTW8852A_FW_BASENAME, - .fw_format_max = RTW8852A_FW_FORMAT_MAX, + .fw_def = { + .fw_basename = RTW8852A_FW_BASENAME, + .fw_format_max = RTW8852A_FW_FORMAT_MAX, + }, .try_ce_fw = false, .bbmcu_nr = 0, .needed_fw_elms = 0, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 393986b297ea..5a41ff1f18f4 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -911,8 +911,10 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .ops = &rtw8852b_chip_ops, .mac_def = &rtw89_mac_gen_ax, .phy_def = &rtw89_phy_gen_ax, - .fw_basename = RTW8852B_FW_BASENAME, - .fw_format_max = RTW8852B_FW_FORMAT_MAX, + .fw_def = { + .fw_basename = RTW8852B_FW_BASENAME, + .fw_format_max = RTW8852B_FW_FORMAT_MAX, + }, .try_ce_fw = true, .bbmcu_nr = 0, .needed_fw_elms = 0, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index 92bbd6e5d699..514861e48c25 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -757,8 +757,10 @@ const struct rtw89_chip_info rtw8852bt_chip_info = { .ops = &rtw8852bt_chip_ops, .mac_def = &rtw89_mac_gen_ax, .phy_def = &rtw89_phy_gen_ax, - .fw_basename = RTW8852BT_FW_BASENAME, - .fw_format_max = RTW8852BT_FW_FORMAT_MAX, + .fw_def = { + .fw_basename = RTW8852BT_FW_BASENAME, + .fw_format_max = RTW8852BT_FW_FORMAT_MAX, + }, .try_ce_fw = true, .bbmcu_nr = 0, .needed_fw_elms = RTW89_AX_GEN_DEF_NEEDED_FW_ELEMENTS_NO_6GHZ, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index de5d343f80a5..70ca8c3950d9 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -3106,8 +3106,10 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .ops = &rtw8852c_chip_ops, .mac_def = &rtw89_mac_gen_ax, .phy_def = &rtw89_phy_gen_ax, - .fw_basename = RTW8852C_FW_BASENAME, - .fw_format_max = RTW8852C_FW_FORMAT_MAX, + .fw_def = { + .fw_basename = RTW8852C_FW_BASENAME, + .fw_format_max = RTW8852C_FW_FORMAT_MAX, + }, .try_ce_fw = false, .bbmcu_nr = 0, .needed_fw_elms = 0, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index f41b66b362c4..fd1b171b106c 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -2916,8 +2916,10 @@ const struct rtw89_chip_info rtw8922a_chip_info = { .ops = &rtw8922a_chip_ops, .mac_def = &rtw89_mac_gen_be, .phy_def = &rtw89_phy_gen_be, - .fw_basename = RTW8922A_FW_BASENAME, - .fw_format_max = RTW8922A_FW_FORMAT_MAX, + .fw_def = { + .fw_basename = RTW8922A_FW_BASENAME, + .fw_format_max = RTW8922A_FW_FORMAT_MAX, + }, .try_ce_fw = false, .bbmcu_nr = 1, .needed_fw_elms = RTW89_BE_GEN_DEF_NEEDED_FW_ELEMENTS, @@ -3057,6 +3059,7 @@ EXPORT_SYMBOL(rtw8922a_chip_info); const struct rtw89_chip_variant rtw8922ae_vs_variant = { .no_mcs_12_13 = true, .fw_min_ver_code = RTW89_FW_VER_CODE(0, 35, 54, 0), + .fw_def_override = NULL, }; EXPORT_SYMBOL(rtw8922ae_vs_variant); From fdb626b79477d40c858faf7dad7628704f7b7c3c Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 13 Feb 2026 14:15:42 +0800 Subject: [PATCH 004/124] wifi: rtw89: fw: recognize firmware type B by AID Select a firmware suit including normal and WoWLAN firmware by chip AID from multiple firmware, because coming RTL8922D has variant hardware with different chip AID. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260213061552.29997-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 4 ++++ drivers/net/wireless/realtek/rtw89/fw.c | 13 +++++++++++-- drivers/net/wireless/realtek/rtw89/rtw8851b.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852a.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852b.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852bt.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852c.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8922a.c | 1 + 8 files changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 5cb8aacf7644..d1f58bf953a9 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4155,6 +4155,7 @@ struct rtw89_reg_imr { struct rtw89_fw_def { const char *fw_basename; u8 fw_format_max; + u16 fw_b_aid; }; struct rtw89_phy_table { @@ -4729,6 +4730,8 @@ enum rtw89_fw_type { RTW89_FW_NORMAL = 1, RTW89_FW_WOWLAN = 3, RTW89_FW_NORMAL_CE = 5, + RTW89_FW_NORMAL_B = 14, + RTW89_FW_WOWLAN_B = 15, RTW89_FW_BBMCU0 = 64, RTW89_FW_BBMCU1 = 65, RTW89_FW_LOGFMT = 255, @@ -7562,6 +7565,7 @@ static inline struct rtw89_fw_suit *rtw89_fw_suit_get(struct rtw89_dev *rtwdev, switch (type) { case RTW89_FW_WOWLAN: + case RTW89_FW_WOWLAN_B: return &fw_info->wowlan; case RTW89_FW_LOGFMT: return &fw_info->log.suit; diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index aa42e5c60eb7..130ebe33cef0 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -1027,16 +1027,25 @@ static int rtw89_fw_validate_ver_required(struct rtw89_dev *rtwdev) int rtw89_fw_recognize(struct rtw89_dev *rtwdev) { + const struct rtw89_fw_def *fw_def = rtw89_chip_get_fw_def(rtwdev); const struct rtw89_chip_info *chip = rtwdev->chip; + const struct rtw89_hal *hal = &rtwdev->hal; + enum rtw89_fw_type normal_fw_type = RTW89_FW_NORMAL; + enum rtw89_fw_type wowlan_fw_type = RTW89_FW_WOWLAN; int ret; + if (fw_def->fw_b_aid && fw_def->fw_b_aid == hal->aid) { + normal_fw_type = RTW89_FW_NORMAL_B; + wowlan_fw_type = RTW89_FW_WOWLAN_B; + } + if (chip->try_ce_fw) { ret = __rtw89_fw_recognize(rtwdev, RTW89_FW_NORMAL_CE, true); if (!ret) goto normal_done; } - ret = __rtw89_fw_recognize(rtwdev, RTW89_FW_NORMAL, false); + ret = __rtw89_fw_recognize(rtwdev, normal_fw_type, false); if (ret) return ret; @@ -1046,7 +1055,7 @@ normal_done: return ret; /* It still works if wowlan firmware isn't existing. */ - __rtw89_fw_recognize(rtwdev, RTW89_FW_WOWLAN, false); + __rtw89_fw_recognize(rtwdev, wowlan_fw_type, false); /* It still works if log format file isn't existing. */ __rtw89_fw_recognize(rtwdev, RTW89_FW_LOGFMT, true); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index fdb053fabae6..8352380b4be5 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -2583,6 +2583,7 @@ const struct rtw89_chip_info rtw8851b_chip_info = { .fw_def = { .fw_basename = RTW8851B_FW_BASENAME, .fw_format_max = RTW8851B_FW_FORMAT_MAX, + .fw_b_aid = 0, }, .try_ce_fw = true, .bbmcu_nr = 0, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 36d66f4e2dd0..32644c40ae28 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2268,6 +2268,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .fw_def = { .fw_basename = RTW8852A_FW_BASENAME, .fw_format_max = RTW8852A_FW_FORMAT_MAX, + .fw_b_aid = 0, }, .try_ce_fw = false, .bbmcu_nr = 0, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 5a41ff1f18f4..66cd46fbee4a 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -914,6 +914,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .fw_def = { .fw_basename = RTW8852B_FW_BASENAME, .fw_format_max = RTW8852B_FW_FORMAT_MAX, + .fw_b_aid = 0, }, .try_ce_fw = true, .bbmcu_nr = 0, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index 514861e48c25..0acdf64395f0 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -760,6 +760,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = { .fw_def = { .fw_basename = RTW8852BT_FW_BASENAME, .fw_format_max = RTW8852BT_FW_FORMAT_MAX, + .fw_b_aid = 0, }, .try_ce_fw = true, .bbmcu_nr = 0, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 70ca8c3950d9..9f0dd6a3956f 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -3109,6 +3109,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .fw_def = { .fw_basename = RTW8852C_FW_BASENAME, .fw_format_max = RTW8852C_FW_FORMAT_MAX, + .fw_b_aid = 0, }, .try_ce_fw = false, .bbmcu_nr = 0, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index fd1b171b106c..1c9db283cff9 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -2919,6 +2919,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = { .fw_def = { .fw_basename = RTW8922A_FW_BASENAME, .fw_format_max = RTW8922A_FW_FORMAT_MAX, + .fw_b_aid = 0, }, .try_ce_fw = false, .bbmcu_nr = 1, From 888226bd560a577be7f169a5f4a0b9e74f4cf039 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Fri, 13 Feb 2026 14:15:43 +0800 Subject: [PATCH 005/124] wifi: rtw89: add general way to generate module firmware string Module firmware string depends on max format of the firmware. When max format is 0, the module firmware string will be like XYZ.bin. However, when max format N > 0, the module firmware string will become XYZ-N.bin. Originally, when one chip updated its max firmware format from 0 to 1, the module firmware string also needed to be changed. However, did not need to do for other cases, e.g. from 1 to 2. It's a bit inconvenient to always remember that from 0 to 1 is a special case. So, add a general macro to generate the corresponding module firmware string based on max firmware format. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260213061552.29997-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 9 +++++++++ drivers/net/wireless/realtek/rtw89/rtw8851b.c | 2 +- drivers/net/wireless/realtek/rtw89/rtw8852a.c | 2 +- drivers/net/wireless/realtek/rtw89/rtw8852b.c | 2 +- drivers/net/wireless/realtek/rtw89/rtw8852bt.c | 2 +- drivers/net/wireless/realtek/rtw89/rtw8852c.c | 2 +- drivers/net/wireless/realtek/rtw89/rtw8922a.c | 2 +- 7 files changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index d1f58bf953a9..1ad1fd2a1f6f 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4152,6 +4152,15 @@ struct rtw89_reg_imr { u32 set; }; +#define RTW89_MODULE_FWNAME_PLACEHOLDER_0 0, +#define __RTW89_GEN_MODULE_FWNAME_FMT(placeholder_or_ignored, strfmt) \ + __take_second_arg(placeholder_or_ignored, strfmt) +#define RTW89_GEN_MODULE_FWNAME_FMT(maxfmt) \ + __RTW89_GEN_MODULE_FWNAME_FMT(RTW89_MODULE_FWNAME_PLACEHOLDER_ ## maxfmt, \ + "-" __stringify(maxfmt)) +#define RTW89_GEN_MODULE_FWNAME(basename, maxformat) \ + basename RTW89_GEN_MODULE_FWNAME_FMT(maxformat) ".bin" + struct rtw89_fw_def { const char *fw_basename; u8 fw_format_max; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index 8352380b4be5..19141ecad4d7 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -18,7 +18,7 @@ #define RTW8851B_FW_FORMAT_MAX 0 #define RTW8851B_FW_BASENAME "rtw89/rtw8851b_fw" #define RTW8851B_MODULE_FIRMWARE \ - RTW8851B_FW_BASENAME ".bin" + RTW89_GEN_MODULE_FWNAME(RTW8851B_FW_BASENAME, RTW8851B_FW_FORMAT_MAX) static const struct rtw89_hfc_ch_cfg rtw8851b_hfc_chcfg_pcie[] = { {5, 343, grp_0}, /* ACH 0 */ diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 32644c40ae28..0d5ec9755783 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -15,7 +15,7 @@ #define RTW8852A_FW_FORMAT_MAX 0 #define RTW8852A_FW_BASENAME "rtw89/rtw8852a_fw" #define RTW8852A_MODULE_FIRMWARE \ - RTW8852A_FW_BASENAME ".bin" + RTW89_GEN_MODULE_FWNAME(RTW8852A_FW_BASENAME, RTW8852A_FW_FORMAT_MAX) static const struct rtw89_hfc_ch_cfg rtw8852a_hfc_chcfg_pcie[] = { {128, 1896, grp_0}, /* ACH 0 */ diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 66cd46fbee4a..7aa1cf184051 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -16,7 +16,7 @@ #define RTW8852B_FW_FORMAT_MAX 1 #define RTW8852B_FW_BASENAME "rtw89/rtw8852b_fw" #define RTW8852B_MODULE_FIRMWARE \ - RTW8852B_FW_BASENAME "-" __stringify(RTW8852B_FW_FORMAT_MAX) ".bin" + RTW89_GEN_MODULE_FWNAME(RTW8852B_FW_BASENAME, RTW8852B_FW_FORMAT_MAX) static const struct rtw89_hfc_ch_cfg rtw8852b_hfc_chcfg_pcie[] = { {5, 341, grp_0}, /* ACH 0 */ diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index 0acdf64395f0..c86b995a7cb1 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -14,7 +14,7 @@ #define RTW8852BT_FW_FORMAT_MAX 0 #define RTW8852BT_FW_BASENAME "rtw89/rtw8852bt_fw" #define RTW8852BT_MODULE_FIRMWARE \ - RTW8852BT_FW_BASENAME ".bin" + RTW89_GEN_MODULE_FWNAME(RTW8852BT_FW_BASENAME, RTW8852BT_FW_FORMAT_MAX) static const struct rtw89_hfc_ch_cfg rtw8852bt_hfc_chcfg_pcie[] = { {16, 742, grp_0}, /* ACH 0 */ diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 9f0dd6a3956f..e62a7288c8aa 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -18,7 +18,7 @@ #define RTW8852C_FW_FORMAT_MAX 2 #define RTW8852C_FW_BASENAME "rtw89/rtw8852c_fw" #define RTW8852C_MODULE_FIRMWARE \ - RTW8852C_FW_BASENAME "-" __stringify(RTW8852C_FW_FORMAT_MAX) ".bin" + RTW89_GEN_MODULE_FWNAME(RTW8852C_FW_BASENAME, RTW8852C_FW_FORMAT_MAX) static const struct rtw89_hfc_ch_cfg rtw8852c_hfc_chcfg_pcie[] = { {13, 1614, grp_0}, /* ACH 0 */ diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 1c9db283cff9..36ef36110602 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -18,7 +18,7 @@ #define RTW8922A_FW_FORMAT_MAX 4 #define RTW8922A_FW_BASENAME "rtw89/rtw8922a_fw" #define RTW8922A_MODULE_FIRMWARE \ - RTW8922A_FW_BASENAME "-" __stringify(RTW8922A_FW_FORMAT_MAX) ".bin" + RTW89_GEN_MODULE_FWNAME(RTW8922A_FW_BASENAME, RTW8922A_FW_FORMAT_MAX) #define HE_N_USER_MAX_8922A 4 From 1d67f1f8e9a0d99ed1731601ea6bdf3c42570be9 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 13 Feb 2026 14:15:44 +0800 Subject: [PATCH 006/124] wifi: rtw89: 8852b: update supported firmware format to 2 After firmware version 0.29.29.15, more data are included in firmware file. Increase format to 2 to prevent old driver failed to load the new firmware. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260213061552.29997-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8852b.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 7aa1cf184051..19a9f07e8714 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -13,7 +13,7 @@ #include "rtw8852b_table.h" #include "txrx.h" -#define RTW8852B_FW_FORMAT_MAX 1 +#define RTW8852B_FW_FORMAT_MAX 2 #define RTW8852B_FW_BASENAME "rtw89/rtw8852b_fw" #define RTW8852B_MODULE_FIRMWARE \ RTW89_GEN_MODULE_FWNAME(RTW8852B_FW_BASENAME, RTW8852B_FW_FORMAT_MAX) From 5cfda90c638423aeab3f1e99f7606d6c6a2cf018 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Fri, 13 Feb 2026 14:15:45 +0800 Subject: [PATCH 007/124] wifi: rtw89: 8852a: move DIG tables to rtw8852a.c Now, most of PHY parameter tables in driver can be loaded via FW elements. Plan to generate the corresponding FW elements for 8852A PHY tables. Then, after FW elements work for a enough time, rtw8852a_table.c can be cleaned up. However, DIG (dynamic initial gain) tables are legacy for 8852A only, so FW element doesn't support. Their sizes are not very big, so move them to rtw8852a.c and keep rtw8852a_table.c for PHY tables which are supported by FW elements. No logic is changed. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260213061552.29997-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8852a.c | 51 +++++++++++++++++++ .../wireless/realtek/rtw89/rtw8852a_table.c | 51 ------------------- .../wireless/realtek/rtw89/rtw8852a_table.h | 1 - 3 files changed, 51 insertions(+), 52 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 0d5ec9755783..6089cf9e1d54 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2179,6 +2179,57 @@ static void rtw8852a_query_ppdu(struct rtw89_dev *rtwdev, rtw8852a_fill_freq_with_ppdu(rtwdev, phy_ppdu, status); } +#define DECLARE_DIG_TABLE(name) \ +static const struct rtw89_phy_dig_gain_cfg name##_table = { \ + .table = name, \ + .size = ARRAY_SIZE(name) \ +} + +static const struct rtw89_reg_def rtw89_8852a_lna_gain_g[] = { + {R_PATH0_LNA_ERR1, B_PATH0_LNA_ERR_G0_G_MSK}, + {R_PATH0_LNA_ERR2, B_PATH0_LNA_ERR_G1_G_MSK}, + {R_PATH0_LNA_ERR2, B_PATH0_LNA_ERR_G2_G_MSK}, + {R_PATH0_LNA_ERR3, B_PATH0_LNA_ERR_G3_G_MSK}, + {R_PATH0_LNA_ERR3, B_PATH0_LNA_ERR_G4_G_MSK}, + {R_PATH0_LNA_ERR4, B_PATH0_LNA_ERR_G5_G_MSK}, + {R_PATH0_LNA_ERR5, B_PATH0_LNA_ERR_G6_G_MSK}, +}; + +DECLARE_DIG_TABLE(rtw89_8852a_lna_gain_g); + +static const struct rtw89_reg_def rtw89_8852a_tia_gain_g[] = { + {R_PATH0_TIA_ERR_G0, B_PATH0_TIA_ERR_G0_G_MSK}, + {R_PATH0_TIA_ERR_G1, B_PATH0_TIA_ERR_G1_G_MSK}, +}; + +DECLARE_DIG_TABLE(rtw89_8852a_tia_gain_g); + +static const struct rtw89_reg_def rtw89_8852a_lna_gain_a[] = { + {R_PATH0_LNA_ERR1, B_PATH0_LNA_ERR_G0_A_MSK}, + {R_PATH0_LNA_ERR1, B_PATH0_LNA_ERR_G1_A_MSK}, + {R_PATH0_LNA_ERR2, B_PATH0_LNA_ERR_G2_A_MSK}, + {R_PATH0_LNA_ERR3, B_PATH0_LNA_ERR_G3_A_MSK}, + {R_PATH0_LNA_ERR3, B_PATH0_LNA_ERR_G4_A_MSK}, + {R_PATH0_LNA_ERR4, B_PATH0_LNA_ERR_G5_A_MSK}, + {R_PATH0_LNA_ERR4, B_PATH0_LNA_ERR_G6_A_MSK}, +}; + +DECLARE_DIG_TABLE(rtw89_8852a_lna_gain_a); + +static const struct rtw89_reg_def rtw89_8852a_tia_gain_a[] = { + {R_PATH0_TIA_ERR_G0, B_PATH0_TIA_ERR_G0_A_MSK}, + {R_PATH0_TIA_ERR_G1, B_PATH0_TIA_ERR_G1_A_MSK}, +}; + +DECLARE_DIG_TABLE(rtw89_8852a_tia_gain_a); + +static const struct rtw89_phy_dig_gain_table rtw89_8852a_phy_dig_table = { + .cfg_lna_g = &rtw89_8852a_lna_gain_g_table, + .cfg_tia_g = &rtw89_8852a_tia_gain_g_table, + .cfg_lna_a = &rtw89_8852a_lna_gain_a_table, + .cfg_tia_a = &rtw89_8852a_tia_gain_a_table +}; + #ifdef CONFIG_PM static const struct wiphy_wowlan_support rtw_wowlan_stub_8852a = { .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a_table.c b/drivers/net/wireless/realtek/rtw89/rtw8852a_table.c index 495890c180ef..ffdeb3801991 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a_table.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a_table.c @@ -50952,50 +50952,6 @@ const s8 rtw89_8852a_txpwr_lmt_ru_5g[RTW89_RU_NUM][RTW89_NTX_NUM] [2][1][RTW89_UK][46] = 32, }; -#define DECLARE_DIG_TABLE(name) \ -static const struct rtw89_phy_dig_gain_cfg name##_table = { \ - .table = name, \ - .size = ARRAY_SIZE(name) \ -} - -static const struct rtw89_reg_def rtw89_8852a_lna_gain_g[] = { - {R_PATH0_LNA_ERR1, B_PATH0_LNA_ERR_G0_G_MSK}, - {R_PATH0_LNA_ERR2, B_PATH0_LNA_ERR_G1_G_MSK}, - {R_PATH0_LNA_ERR2, B_PATH0_LNA_ERR_G2_G_MSK}, - {R_PATH0_LNA_ERR3, B_PATH0_LNA_ERR_G3_G_MSK}, - {R_PATH0_LNA_ERR3, B_PATH0_LNA_ERR_G4_G_MSK}, - {R_PATH0_LNA_ERR4, B_PATH0_LNA_ERR_G5_G_MSK}, - {R_PATH0_LNA_ERR5, B_PATH0_LNA_ERR_G6_G_MSK}, -}; - -DECLARE_DIG_TABLE(rtw89_8852a_lna_gain_g); - -static const struct rtw89_reg_def rtw89_8852a_tia_gain_g[] = { - {R_PATH0_TIA_ERR_G0, B_PATH0_TIA_ERR_G0_G_MSK}, - {R_PATH0_TIA_ERR_G1, B_PATH0_TIA_ERR_G1_G_MSK}, -}; - -DECLARE_DIG_TABLE(rtw89_8852a_tia_gain_g); - -static const struct rtw89_reg_def rtw89_8852a_lna_gain_a[] = { - {R_PATH0_LNA_ERR1, B_PATH0_LNA_ERR_G0_A_MSK}, - {R_PATH0_LNA_ERR1, B_PATH0_LNA_ERR_G1_A_MSK}, - {R_PATH0_LNA_ERR2, B_PATH0_LNA_ERR_G2_A_MSK}, - {R_PATH0_LNA_ERR3, B_PATH0_LNA_ERR_G3_A_MSK}, - {R_PATH0_LNA_ERR3, B_PATH0_LNA_ERR_G4_A_MSK}, - {R_PATH0_LNA_ERR4, B_PATH0_LNA_ERR_G5_A_MSK}, - {R_PATH0_LNA_ERR4, B_PATH0_LNA_ERR_G6_A_MSK}, -}; - -DECLARE_DIG_TABLE(rtw89_8852a_lna_gain_a); - -static const struct rtw89_reg_def rtw89_8852a_tia_gain_a[] = { - {R_PATH0_TIA_ERR_G0, B_PATH0_TIA_ERR_G0_A_MSK}, - {R_PATH0_TIA_ERR_G1, B_PATH0_TIA_ERR_G1_A_MSK}, -}; - -DECLARE_DIG_TABLE(rtw89_8852a_tia_gain_a); - const struct rtw89_phy_table rtw89_8852a_phy_bb_table = { .regs = rtw89_8852a_phy_bb_regs, .n_regs = ARRAY_SIZE(rtw89_8852a_phy_bb_regs), @@ -51042,13 +50998,6 @@ const struct rtw89_txpwr_track_cfg rtw89_8852a_trk_cfg = { .delta_swingidx_2g_cck_a_p = _txpwr_track_delta_swingidx_2g_cck_a_p, }; -const struct rtw89_phy_dig_gain_table rtw89_8852a_phy_dig_table = { - .cfg_lna_g = &rtw89_8852a_lna_gain_g_table, - .cfg_tia_g = &rtw89_8852a_tia_gain_g_table, - .cfg_lna_a = &rtw89_8852a_lna_gain_a_table, - .cfg_tia_a = &rtw89_8852a_tia_gain_a_table -}; - const struct rtw89_rfe_parms rtw89_8852a_dflt_parms = { .byr_tbl = &rtw89_8852a_byr_table, .rule_2ghz = { diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a_table.h b/drivers/net/wireless/realtek/rtw89/rtw8852a_table.h index 7463ae6ee3f9..58fe8575c1c9 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a_table.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a_table.h @@ -11,7 +11,6 @@ extern const struct rtw89_phy_table rtw89_8852a_phy_bb_table; extern const struct rtw89_phy_table rtw89_8852a_phy_radioa_table; extern const struct rtw89_phy_table rtw89_8852a_phy_radiob_table; extern const struct rtw89_phy_table rtw89_8852a_phy_nctl_table; -extern const struct rtw89_phy_dig_gain_table rtw89_8852a_phy_dig_table; extern const struct rtw89_txpwr_track_cfg rtw89_8852a_trk_cfg; extern const struct rtw89_rfe_parms rtw89_8852a_dflt_parms; From b60796c07a79178db32b63e5b75996551145fa5d Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Fri, 13 Feb 2026 14:15:46 +0800 Subject: [PATCH 008/124] wifi: rtw89: 8852a: update supported firmware format to 1 More data will be included in Firmware file and loaded via FW elements. Unlike other chips, for RF radio, RTL8852A uses rtw89_phy_config_rf_reg instead of v1, so update loading handling of corresponding FW element. And then, increase RTL8852A FW format to 1 to prevent old driver from misusing the data in FW elements. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260213061552.29997-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 5 ++++- drivers/net/wireless/realtek/rtw89/phy.c | 8 ++++---- drivers/net/wireless/realtek/rtw89/phy.h | 4 ++++ drivers/net/wireless/realtek/rtw89/rtw8852a.c | 2 +- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 130ebe33cef0..52f7e65fe6a5 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -1073,6 +1073,7 @@ int rtw89_build_phy_tbl_from_elm(struct rtw89_dev *rtwdev, const union rtw89_fw_element_arg arg) { struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info; + const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_hal *hal = &rtwdev->hal; struct rtw89_phy_table *tbl, **pp; struct rtw89_reg2_def *regs; @@ -1129,7 +1130,9 @@ int rtw89_build_phy_tbl_from_elm(struct rtw89_dev *rtwdev, if (radio) { tbl->rf_path = arg.rf_path; - tbl->config = rtw89_phy_config_rf_reg_v1; + tbl->config = chip->chip_id == RTL8852A ? + rtw89_phy_config_rf_reg : + rtw89_phy_config_rf_reg_v1; } *pp = tbl; diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index ee6ab2136b9a..74f5d5562848 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -1659,10 +1659,10 @@ static void rtw89_phy_config_rf_reg_noio(struct rtw89_dev *rtwdev, (struct rtw89_fw_h2c_rf_reg_info *)extra_data); } -static void rtw89_phy_config_rf_reg(struct rtw89_dev *rtwdev, - const struct rtw89_reg2_def *reg, - enum rtw89_rf_path rf_path, - void *extra_data) +void rtw89_phy_config_rf_reg(struct rtw89_dev *rtwdev, + const struct rtw89_reg2_def *reg, + enum rtw89_rf_path rf_path, + void *extra_data) { if (reg->addr == 0xfe) { mdelay(50); diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index ab263738d212..094c7e45f254 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -852,6 +852,10 @@ bool rtw89_phy_write_rf_v3(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, void rtw89_phy_init_bb_reg(struct rtw89_dev *rtwdev); void rtw89_phy_init_bb_afe(struct rtw89_dev *rtwdev); void rtw89_phy_init_rf_reg(struct rtw89_dev *rtwdev, bool noio); +void rtw89_phy_config_rf_reg(struct rtw89_dev *rtwdev, + const struct rtw89_reg2_def *reg, + enum rtw89_rf_path rf_path, + void *extra_data); void rtw89_phy_config_rf_reg_v1(struct rtw89_dev *rtwdev, const struct rtw89_reg2_def *reg, enum rtw89_rf_path rf_path, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 6089cf9e1d54..898c534a5762 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -12,7 +12,7 @@ #include "rtw8852a_table.h" #include "txrx.h" -#define RTW8852A_FW_FORMAT_MAX 0 +#define RTW8852A_FW_FORMAT_MAX 1 #define RTW8852A_FW_BASENAME "rtw89/rtw8852a_fw" #define RTW8852A_MODULE_FIRMWARE \ RTW89_GEN_MODULE_FWNAME(RTW8852A_FW_BASENAME, RTW8852A_FW_FORMAT_MAX) From 6b4fe140874b9e8a1765fda9e9b11b55b79de64e Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Fri, 13 Feb 2026 14:15:47 +0800 Subject: [PATCH 009/124] wifi: rtw89: 8851b: update supported firmware format to 1 More data will be included in Firmware file and loaded via FW elements. Increase RTL8851B FW format to 1 to prevent old driver from failing to recognize FW with FW elements. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260213061552.29997-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8851b.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index 19141ecad4d7..a3a5b6fbe46c 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -15,7 +15,7 @@ #include "txrx.h" #include "util.h" -#define RTW8851B_FW_FORMAT_MAX 0 +#define RTW8851B_FW_FORMAT_MAX 1 #define RTW8851B_FW_BASENAME "rtw89/rtw8851b_fw" #define RTW8851B_MODULE_FIRMWARE \ RTW89_GEN_MODULE_FWNAME(RTW8851B_FW_BASENAME, RTW8851B_FW_FORMAT_MAX) From 79b3702bbc02bc6e65e89195d761caae51fa20c8 Mon Sep 17 00:00:00 2001 From: Kuan-Chung Chen Date: Fri, 13 Feb 2026 14:15:48 +0800 Subject: [PATCH 010/124] wifi: rtw89: add H2C command to protect TX/RX for unused PHY For BE chips, the unused PHY should pause transmissions and receptions. This ensures that no unexpected packets are routed to an inactive PHY, which could otherwise trigger SER L0 and lead to TX hang. Signed-off-by: Kuan-Chung Chen Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260213061552.29997-9-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 87 +++++++++++++++++++ drivers/net/wireless/realtek/rtw89/fw.h | 51 +++++++++++ drivers/net/wireless/realtek/rtw89/mac.c | 3 + drivers/net/wireless/realtek/rtw89/mac80211.c | 5 ++ drivers/net/wireless/realtek/rtw89/ps.c | 2 + 5 files changed, 148 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 52f7e65fe6a5..ffbd75cb5533 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -6871,6 +6871,93 @@ flex_member: return 0; } +int rtw89_fw_h2c_trx_protect(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, bool enable) +{ + struct rtw89_wait_info *wait = &rtwdev->mac.fw_ofld_wait; + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_h2c_trx_protect *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + if (chip->chip_gen != RTW89_CHIP_BE) + return 0; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for h2c trx protect\n"); + return -ENOMEM; + } + + skb_put(skb, len); + h2c = (struct rtw89_h2c_trx_protect *)skb->data; + + h2c->c0 = le32_encode_bits(BIT(phy_idx), RTW89_H2C_TRX_PROTECT_C0_BAND_BITMAP) | + le32_encode_bits(0, RTW89_H2C_TRX_PROTECT_C0_OP_MODE); + h2c->c1 = le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_C1_RX_IN) | + le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_C1_PPDU_STS) | + le32_encode_bits(1, RTW89_H2C_TRX_PROTECT_C1_MSK_RX_IN) | + le32_encode_bits(1, RTW89_H2C_TRX_PROTECT_C1_MSK_PPDU_STS); + h2c->w0 = le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_BE0) | + le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_BK0) | + le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_VI0) | + le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_VO0) | + le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_BE1) | + le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_BK1) | + le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_VI1) | + le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_VO1) | + le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_MG0) | + le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_MG1) | + le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_MG2) | + le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_HI) | + le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_BCN) | + le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_UL) | + le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_TWT0) | + le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_TWT1) | + le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_TWT2) | + le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_TWT3) | + le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_SPEQ0) | + le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_SPEQ1); + h2c->m0 = cpu_to_le32(RTW89_H2C_TRX_PROTECT_W0_TXEN_BE0 | + RTW89_H2C_TRX_PROTECT_W0_TXEN_BK0 | + RTW89_H2C_TRX_PROTECT_W0_TXEN_VI0 | + RTW89_H2C_TRX_PROTECT_W0_TXEN_VO0 | + RTW89_H2C_TRX_PROTECT_W0_TXEN_BE1 | + RTW89_H2C_TRX_PROTECT_W0_TXEN_BK1 | + RTW89_H2C_TRX_PROTECT_W0_TXEN_VI1 | + RTW89_H2C_TRX_PROTECT_W0_TXEN_VO1 | + RTW89_H2C_TRX_PROTECT_W0_TXEN_MG0 | + RTW89_H2C_TRX_PROTECT_W0_TXEN_MG1 | + RTW89_H2C_TRX_PROTECT_W0_TXEN_MG2 | + RTW89_H2C_TRX_PROTECT_W0_TXEN_HI | + RTW89_H2C_TRX_PROTECT_W0_TXEN_BCN | + RTW89_H2C_TRX_PROTECT_W0_TXEN_UL | + RTW89_H2C_TRX_PROTECT_W0_TXEN_TWT0 | + RTW89_H2C_TRX_PROTECT_W0_TXEN_TWT1 | + RTW89_H2C_TRX_PROTECT_W0_TXEN_TWT2 | + RTW89_H2C_TRX_PROTECT_W0_TXEN_TWT3 | + RTW89_H2C_TRX_PROTECT_W0_TXEN_SPEQ0 | + RTW89_H2C_TRX_PROTECT_W0_TXEN_SPEQ1); + h2c->w1 = le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W1_CHINFO_EN) | + le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W1_DFS_EN); + h2c->m1 = cpu_to_le32(RTW89_H2C_TRX_PROTECT_W1_CHINFO_EN | + RTW89_H2C_TRX_PROTECT_W1_DFS_EN); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, H2C_CL_MAC_FW_OFLD, + H2C_FUNC_TRX_PROTECT, 0, 1, len); + + ret = rtw89_h2c_tx_and_wait(rtwdev, skb, wait, + RTW89_FW_OFLD_WAIT_COND_TRX_PROTECT); + if (ret) { + rtw89_debug(rtwdev, RTW89_DBG_FW, "failed to trx protect\n"); + return ret; + } + + return 0; +} + int rtw89_fw_h2c_rf_reg(struct rtw89_dev *rtwdev, struct rtw89_fw_h2c_rf_reg_info *info, u16 len, u8 page) diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index c60d419616d6..80d260eb08cd 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -3106,6 +3106,44 @@ struct rtw89_h2c_scanofld_be { #define RTW89_H2C_SCANOFLD_BE_W9_SIZE_MACC GENMASK(15, 8) #define RTW89_H2C_SCANOFLD_BE_W9_SIZE_OP GENMASK(23, 16) +struct rtw89_h2c_trx_protect { + __le32 c0; + __le32 c1; + __le32 w0; + __le32 m0; + __le32 w1; + __le32 m1; +} __packed; + +#define RTW89_H2C_TRX_PROTECT_C0_BAND_BITMAP GENMASK(2, 0) +#define RTW89_H2C_TRX_PROTECT_C0_OP_MODE GENMASK(4, 3) +#define RTW89_H2C_TRX_PROTECT_C1_RX_IN BIT(0) +#define RTW89_H2C_TRX_PROTECT_C1_PPDU_STS BIT(4) +#define RTW89_H2C_TRX_PROTECT_C1_MSK_RX_IN BIT(16) +#define RTW89_H2C_TRX_PROTECT_C1_MSK_PPDU_STS BIT(20) +#define RTW89_H2C_TRX_PROTECT_W0_TXEN_BE0 BIT(0) +#define RTW89_H2C_TRX_PROTECT_W0_TXEN_BK0 BIT(1) +#define RTW89_H2C_TRX_PROTECT_W0_TXEN_VI0 BIT(2) +#define RTW89_H2C_TRX_PROTECT_W0_TXEN_VO0 BIT(3) +#define RTW89_H2C_TRX_PROTECT_W0_TXEN_BE1 BIT(4) +#define RTW89_H2C_TRX_PROTECT_W0_TXEN_BK1 BIT(5) +#define RTW89_H2C_TRX_PROTECT_W0_TXEN_VI1 BIT(6) +#define RTW89_H2C_TRX_PROTECT_W0_TXEN_VO1 BIT(7) +#define RTW89_H2C_TRX_PROTECT_W0_TXEN_MG0 BIT(8) +#define RTW89_H2C_TRX_PROTECT_W0_TXEN_MG1 BIT(9) +#define RTW89_H2C_TRX_PROTECT_W0_TXEN_MG2 BIT(10) +#define RTW89_H2C_TRX_PROTECT_W0_TXEN_HI BIT(11) +#define RTW89_H2C_TRX_PROTECT_W0_TXEN_BCN BIT(12) +#define RTW89_H2C_TRX_PROTECT_W0_TXEN_UL BIT(13) +#define RTW89_H2C_TRX_PROTECT_W0_TXEN_TWT0 BIT(14) +#define RTW89_H2C_TRX_PROTECT_W0_TXEN_TWT1 BIT(15) +#define RTW89_H2C_TRX_PROTECT_W0_TXEN_TWT2 BIT(16) +#define RTW89_H2C_TRX_PROTECT_W0_TXEN_TWT3 BIT(17) +#define RTW89_H2C_TRX_PROTECT_W0_TXEN_SPEQ0 BIT(18) +#define RTW89_H2C_TRX_PROTECT_W0_TXEN_SPEQ1 BIT(19) +#define RTW89_H2C_TRX_PROTECT_W1_CHINFO_EN BIT(0) +#define RTW89_H2C_TRX_PROTECT_W1_DFS_EN BIT(1) + struct rtw89_h2c_fwips { __le32 w0; } __packed; @@ -4598,6 +4636,7 @@ enum rtw89_fw_ofld_h2c_func { H2C_FUNC_OFLD_TP = 0x20, H2C_FUNC_MAC_MACID_PAUSE_SLEEP = 0x28, H2C_FUNC_SCANOFLD_BE = 0x2c, + H2C_FUNC_TRX_PROTECT = 0x34, NUM_OF_RTW89_FW_OFLD_H2C_FUNC, }; @@ -4608,6 +4647,7 @@ enum rtw89_fw_ofld_h2c_func { #define RTW89_FW_OFLD_WAIT_COND_PKT_OFLD(pkt_id, pkt_op) \ RTW89_FW_OFLD_WAIT_COND(RTW89_PKT_OFLD_WAIT_TAG(pkt_id, pkt_op), \ H2C_FUNC_PACKET_OFLD) +#define RTW89_FW_OFLD_WAIT_COND_TRX_PROTECT RTW89_FW_OFLD_WAIT_COND(0, H2C_FUNC_TRX_PROTECT) #define RTW89_SCANOFLD_WAIT_COND_ADD_CH RTW89_FW_OFLD_WAIT_COND(0, H2C_FUNC_ADD_SCANOFLD_CH) @@ -5294,6 +5334,8 @@ int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev, struct rtw89_scan_option *opt, struct rtw89_vif_link *vif, bool wowlan); +int rtw89_fw_h2c_trx_protect(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, bool enable); int rtw89_fw_h2c_rf_reg(struct rtw89_dev *rtwdev, struct rtw89_fw_h2c_rf_reg_info *info, u16 len, u8 page); @@ -5469,6 +5511,15 @@ static inline void rtw89_fw_h2c_init_ba_cam(struct rtw89_dev *rtwdev) rtw89_fw_h2c_init_dynamic_ba_cam_v0_ext(rtwdev); } +static inline void rtw89_fw_h2c_init_trx_protect(struct rtw89_dev *rtwdev) +{ + u8 active_bands = rtw89_get_active_phy_bitmap(rtwdev); + int i; + + for (i = 0; i < RTW89_PHY_NUM; i++) + rtw89_fw_h2c_trx_protect(rtwdev, i, active_bands & BIT(i)); +} + static inline int rtw89_chip_h2c_default_cmac_tbl(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, struct rtw89_sta_link *rtwsta_link) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index c98ca2a82194..04ef4bae1852 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -5411,6 +5411,9 @@ rtw89_mac_c2h_done_ack(struct rtw89_dev *rtwdev, struct sk_buff *skb_c2h, u32 le cond = RTW89_SCANOFLD_BE_WAIT_COND_START; h2c_return &= RTW89_C2H_SCAN_DONE_ACK_RETURN; break; + case H2C_FUNC_TRX_PROTECT: + cond = RTW89_FW_OFLD_WAIT_COND_TRX_PROTECT; + break; } data.err = !!h2c_return; diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index 0ea33743853e..1ef73bfc40d1 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -528,6 +528,8 @@ static int __rtw89_ops_sta_add(struct rtw89_dev *rtwdev, if (vif->type == NL80211_IFTYPE_AP || sta->tdls) rtw89_queue_chanctx_change(rtwdev, RTW89_CHANCTX_REMOTE_STA_CHANGE); + rtw89_fw_h2c_init_trx_protect(rtwdev); + return 0; unset_link: @@ -1584,6 +1586,8 @@ static void __rtw89_ops_clr_vif_links(struct rtw89_dev *rtwdev, if (unlikely(!rtwvif_link)) continue; + rtw89_fw_h2c_trx_protect(rtwdev, rtwvif_link->phy_idx, false); + __rtw89_ops_remove_iface_link(rtwdev, rtwvif_link); rtw89_vif_unset_link(rtwvif, link_id); @@ -1609,6 +1613,7 @@ static int __rtw89_ops_set_vif_links(struct rtw89_dev *rtwdev, __func__, link_id); return ret; } + rtw89_fw_h2c_trx_protect(rtwdev, rtwvif_link->phy_idx, true); } return 0; diff --git a/drivers/net/wireless/realtek/rtw89/ps.c b/drivers/net/wireless/realtek/rtw89/ps.c index aad2ee7926d6..125cf14fa581 100644 --- a/drivers/net/wireless/realtek/rtw89/ps.c +++ b/drivers/net/wireless/realtek/rtw89/ps.c @@ -226,6 +226,8 @@ void rtw89_leave_lps(struct rtw89_dev *rtwdev) rtw89_for_each_rtwvif(rtwdev, rtwvif) rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id) rtw89_leave_lps_vif(rtwdev, rtwvif_link); + + rtw89_fw_h2c_init_trx_protect(rtwdev); } void rtw89_enter_ips(struct rtw89_dev *rtwdev) From dcd2326db84399adbb0a6768d04a09f3078394c5 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 13 Feb 2026 14:15:49 +0800 Subject: [PATCH 011/124] wifi: rtw89: rfk: add hardware version to rtw89_fw_h2c_rf_pre_ntfy_mcc for new WiFi 7 firmware The RF calibration in firmware needs proper hardware version to select corresponding logic, so add the field to H2C command accordingly. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260213061552.29997-10-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 1 + drivers/net/wireless/realtek/rtw89/fw.h | 2 ++ 2 files changed, 3 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index ffbd75cb5533..99084a6e10a5 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -7371,6 +7371,7 @@ v1: h2c = (struct rtw89_fw_h2c_rfk_pre_info_mcc *)skb->data; h2c->aid = cpu_to_le32(hal->aid); + h2c->acv = hal->acv; done: rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 80d260eb08cd..57e3b464c0a2 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -4881,6 +4881,8 @@ struct rtw89_fw_h2c_rfk_pre_info_mcc { struct rtw89_fw_h2c_rfk_pre_info_mcc_v1 base; u8 rsvd[2]; __le32 aid; + u8 acv; + u8 rsvd2[3]; } __packed; struct rtw89_h2c_rf_tssi { From a72de4d4599e8e31d43485f108bed477da139171 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Fri, 13 Feb 2026 14:15:50 +0800 Subject: [PATCH 012/124] wifi: rtw89: debug: add SER SW counters to count simulation Some chipsets, e.g. Wi-Fi 7, will not record SER (system error recovery), which are triggered by simulations, in the HW counters. Their HW counters only record the SER happen in field. However for verification, it's still needed to check if simulations are triggered. So, add SW counters to count any causes that SER happen. But, SW can only count L1 and L2. SW does not involve L0 SER, so SW cannot count it. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260213061552.29997-11-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 7 +++++++ drivers/net/wireless/realtek/rtw89/debug.c | 6 ++++++ drivers/net/wireless/realtek/rtw89/ser.c | 2 ++ 3 files changed, 15 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 1ad1fd2a1f6f..5d1cec20bc80 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5753,11 +5753,18 @@ enum rtw89_ser_rcvy_step { RTW89_NUM_OF_SER_FLAGS }; +struct rtw89_ser_count { + unsigned int l1; + unsigned int l2; +}; + struct rtw89_ser { u8 state; u8 alarm_event; bool prehandle_l1; + struct rtw89_ser_count sw_cnt; + struct work_struct ser_hdl_work; struct delayed_work ser_alarm_work; const struct state_ent *st_tbl; diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 012ead92f5f2..3e16ed2c4570 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -3781,6 +3781,7 @@ static ssize_t rtw89_debug_priv_ser_counters_get(struct rtw89_dev *rtwdev, struct rtw89_debugfs_priv *debugfs_priv, char *buf, size_t bufsz) { + const struct rtw89_ser_count *sw_cnt = &rtwdev->ser.sw_cnt; const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_dbg_ser_counters cnt = {}; char *p = buf, *end = buf + bufsz; @@ -3798,6 +3799,11 @@ static ssize_t rtw89_debug_priv_ser_counters_get(struct rtw89_dev *rtwdev, return -EOPNOTSUPP; } + p += scnprintf(p, end - p, "SER L1 SW Count: %u\n", sw_cnt->l1); + p += scnprintf(p, end - p, "SER L2 SW Count: %u\n", sw_cnt->l2); + + /* Some chipsets won't record SER simulation in HW cnt. */ + p += scnprintf(p, end - p, "---\n"); p += scnprintf(p, end - p, "SER L0 Count: %d\n", cnt.l0); p += scnprintf(p, end - p, "SER L1 Count: %d\n", cnt.l1); p += scnprintf(p, end - p, "SER L0 promote event: %d\n", cnt.l0_to_l1); diff --git a/drivers/net/wireless/realtek/rtw89/ser.c b/drivers/net/wireless/realtek/rtw89/ser.c index f91e66133b30..75220042a9a7 100644 --- a/drivers/net/wireless/realtek/rtw89/ser.c +++ b/drivers/net/wireless/realtek/rtw89/ser.c @@ -498,6 +498,7 @@ static void ser_reset_trx_st_hdl(struct rtw89_ser *ser, u8 evt) switch (evt) { case SER_EV_STATE_IN: wiphy_lock(wiphy); + ser->sw_cnt.l1++; wiphy_delayed_work_cancel(wiphy, &rtwdev->track_work); wiphy_delayed_work_cancel(wiphy, &rtwdev->track_ps_work); wiphy_unlock(wiphy); @@ -730,6 +731,7 @@ static void ser_l2_reset_st_hdl(struct rtw89_ser *ser, u8 evt) switch (evt) { case SER_EV_STATE_IN: wiphy_lock(rtwdev->hw->wiphy); + ser->sw_cnt.l2++; ser_l2_reset_st_pre_hdl(ser); wiphy_unlock(rtwdev->hw->wiphy); From 0cae26a78b14fe1292b0f50f28ebabe6801f3885 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Fri, 13 Feb 2026 14:15:51 +0800 Subject: [PATCH 013/124] wifi: rtw89: ser: Wi-Fi 7 reset HALT C2H after reading it When a SER (system error recovery) interrupt happens, driver reads HALT C2H register to get the error status via MAC. For Wi-Fi 7 chipset, driver needs to reset HALT C2H register after reading it to make FW aware that. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260213061552.29997-12-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 04ef4bae1852..e1cea0f88406 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -814,6 +814,7 @@ static bool rtw89_mac_suppress_log(struct rtw89_dev *rtwdev, u32 err) u32 rtw89_mac_get_err_status(struct rtw89_dev *rtwdev) { const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; + const struct rtw89_chip_info *chip = rtwdev->chip; u32 err, err_scnr; int ret; @@ -836,11 +837,15 @@ u32 rtw89_mac_get_err_status(struct rtw89_dev *rtwdev) err = MAC_AX_ERR_RXI300; if (rtw89_mac_suppress_log(rtwdev, err)) - return err; + goto bottom; rtw89_fw_st_dbg_dump(rtwdev); mac->dump_err_status(rtwdev, err); +bottom: + if (chip->chip_gen != RTW89_CHIP_AX) + rtw89_write32(rtwdev, R_AX_HALT_C2H, 0); + return err; } EXPORT_SYMBOL(rtw89_mac_get_err_status); From 3d90c421341ba6705f991ddaa350b585afefe078 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Fri, 13 Feb 2026 14:15:52 +0800 Subject: [PATCH 014/124] wifi: rtw89: ser: post-recover DMAC state to prevent LPS If entering LPS during SER (system error recovery), IMR might fail to be re-enabled after SER. Then, the next SER would not be noticed well. After FW v0.35.100.0, Wi-Fi 7 chipsets adjust the order in which SER recovers DMAC state to prevent LPS from being in the middle of SER. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260213061552.29997-13-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/fw.c | 1 + drivers/net/wireless/realtek/rtw89/mac.c | 7 ++++++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 5d1cec20bc80..cf0cc718f41c 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4796,6 +4796,7 @@ enum rtw89_fw_feature { RTW89_FW_FEATURE_SER_L1_BY_EVENT, RTW89_FW_FEATURE_SIM_SER_L0L1_BY_HALT_H2C, RTW89_FW_FEATURE_LPS_ML_INFO_V1, + RTW89_FW_FEATURE_SER_POST_RECOVER_DMAC, NUM_OF_RTW89_FW_FEATURES, }; diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 99084a6e10a5..45d8c5e70084 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -923,6 +923,7 @@ static const struct __fw_feat_cfg fw_feat_tbl[] = { __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 84, 0, RFK_PRE_NOTIFY_MCC_V1), __CFG_FW_FEAT(RTL8922A, lt, 0, 35, 84, 0, ADDR_CAM_V0), __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 97, 0, SIM_SER_L0L1_BY_HALT_H2C), + __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 100, 0, SER_POST_RECOVER_DMAC), }; static void rtw89_fw_iterate_feature_cfg(struct rtw89_fw_info *fw, diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index e1cea0f88406..234928613ef4 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -826,7 +826,9 @@ u32 rtw89_mac_get_err_status(struct rtw89_dev *rtwdev) } err = rtw89_read32(rtwdev, R_AX_HALT_C2H); - rtw89_write32(rtwdev, R_AX_HALT_C2H_CTRL, 0); + + if (!RTW89_CHK_FW_FEATURE(SER_POST_RECOVER_DMAC, &rtwdev->fw)) + rtw89_write32(rtwdev, R_AX_HALT_C2H_CTRL, 0); err_scnr = RTW89_ERROR_SCENARIO(err); if (err_scnr == RTW89_WCPU_CPU_EXCEPTION) @@ -846,6 +848,9 @@ bottom: if (chip->chip_gen != RTW89_CHIP_AX) rtw89_write32(rtwdev, R_AX_HALT_C2H, 0); + if (RTW89_CHK_FW_FEATURE(SER_POST_RECOVER_DMAC, &rtwdev->fw)) + rtw89_write32(rtwdev, R_AX_HALT_C2H_CTRL, 0); + return err; } EXPORT_SYMBOL(rtw89_mac_get_err_status); From eb101d2abdcccb514ca4fccd3b278dd8267374f6 Mon Sep 17 00:00:00 2001 From: Fedor Pchelkin Date: Fri, 20 Feb 2026 12:47:30 +0300 Subject: [PATCH 015/124] wifi: rtw88: check for PCI upstream bridge existence pci_upstream_bridge() returns NULL if the device is on a root bus. If 8821CE is installed in the system with such a PCI topology, the probing routine will crash. This has probably been unnoticed as 8821CE is mostly supplied in laptops where there is a PCI-to-PCI bridge located upstream from the device. However the card might be installed on a system with different configuration. Check if the bridge does exist for the specific workaround to be applied. Found by Linux Verification Center (linuxtesting.org) with Svace static analysis tool. Fixes: 24f5e38a13b5 ("rtw88: Disable PCIe ASPM while doing NAPI poll on 8821CE") Cc: stable@vger.kernel.org Signed-off-by: Fedor Pchelkin Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260220094730.49791-1-pchelkin@ispras.ru --- drivers/net/wireless/realtek/rtw88/pci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c index 56b16186d3aa..ec0a45bfb670 100644 --- a/drivers/net/wireless/realtek/rtw88/pci.c +++ b/drivers/net/wireless/realtek/rtw88/pci.c @@ -1804,7 +1804,8 @@ int rtw_pci_probe(struct pci_dev *pdev, } /* Disable PCIe ASPM L1 while doing NAPI poll for 8821CE */ - if (rtwdev->chip->id == RTW_CHIP_TYPE_8821C && bridge->vendor == PCI_VENDOR_ID_INTEL) + if (rtwdev->chip->id == RTW_CHIP_TYPE_8821C && + bridge && bridge->vendor == PCI_VENDOR_ID_INTEL) rtwpci->rx_no_aspm = true; rtw_pci_phy_cfg(rtwdev); From 039cd522dc70151da13329a5e3ae19b1736f468a Mon Sep 17 00:00:00 2001 From: Duoming Zhou Date: Mon, 23 Feb 2026 12:55:22 +0800 Subject: [PATCH 016/124] wifi: rtlwifi: pci: fix possible use-after-free caused by unfinished irq_prepare_bcn_tasklet The irq_prepare_bcn_tasklet is initialized in rtl_pci_init() and scheduled when RTL_IMR_BCNINT interrupt is triggered by hardware. But it is never killed in rtl_pci_deinit(). When the rtlwifi card probe fails or is being detached, the ieee80211_hw is deallocated. However, irq_prepare_bcn_tasklet may still be running or pending, leading to use-after-free when the freed ieee80211_hw is accessed in _rtl_pci_prepare_bcn_tasklet(). Similar to irq_tasklet, add tasklet_kill() in rtl_pci_deinit() to ensure that irq_prepare_bcn_tasklet is properly terminated before the ieee80211_hw is released. The issue was identified through static analysis. Fixes: 0c8173385e54 ("rtl8192ce: Add new driver") Signed-off-by: Duoming Zhou Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260223045522.48377-1-duoming@zju.edu.cn --- drivers/net/wireless/realtek/rtlwifi/pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c index d080469264cf..f0010336e78c 100644 --- a/drivers/net/wireless/realtek/rtlwifi/pci.c +++ b/drivers/net/wireless/realtek/rtlwifi/pci.c @@ -1674,6 +1674,7 @@ static void rtl_pci_deinit(struct ieee80211_hw *hw) synchronize_irq(rtlpci->pdev->irq); tasklet_kill(&rtlpriv->works.irq_tasklet); + tasklet_kill(&rtlpriv->works.irq_prepare_bcn_tasklet); cancel_work_sync(&rtlpriv->works.lps_change_work); } From 2a585b2efb48a86cd32a953ba84cf1557a655b40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Neusch=C3=A4fer?= Date: Tue, 3 Mar 2026 18:49:25 +0100 Subject: [PATCH 017/124] wifi: rtl8xxxu: Mark RTL8188ETV (0bda:0179) as tested MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This WiFi chip appears in the "TCU Fernsehfee 3.0" set-top box. I have tested that it works in WPA2-PSK-CCMP client mode. Cc: Jes.Sorensen@gmail.com Signed-off-by: J. Neuschäfer Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260303-rtl8xxx-tested-v1-1-291a38a6ff2f@posteo.net --- drivers/net/wireless/realtek/rtl8xxxu/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c index 794187d28caa..b4efc6f00a37 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c @@ -7703,6 +7703,7 @@ static int rtl8xxxu_probe(struct usb_interface *interface, switch (id->idVendor) { case USB_VENDOR_ID_REALTEK: switch(id->idProduct) { + case 0x0179: case 0x1724: case 0x8176: case 0x8178: From f8a2fc809bfeb49130709b31a4d357a049f28547 Mon Sep 17 00:00:00 2001 From: Yi Cong Date: Fri, 6 Mar 2026 15:16:27 +0800 Subject: [PATCH 018/124] wifi: rtl8xxxu: fix potential use of uninitialized value The local variables 'mcs' and 'nss' in rtl8xxxu_update_ra_report() are passed to rtl8xxxu_desc_to_mcsrate() as output parameters. If the helper function encounters an unhandled rate index, it may return without setting these values, leading to the use of uninitialized stack data. Remove the helper rtl8xxxu_desc_to_mcsrate() and inline the logic into rtl8xxxu_update_ra_report(). This fixes the use of uninitialized 'mcs' and 'nss' variables for legacy rates. The new implementation explicitly handles: - Legacy rates: Set bitrate only. - HT rates (MCS0-15): Set MCS flags, index, and NSS (1 or 2) directly. - Invalid rates: Return early. Fixes: 7de16123d9e2 ("wifi: rtl8xxxu: Introduce rtl8xxxu_update_ra_report") Cc: stable@vger.kernel.org Suggested-by: Ping-Ke Shih Signed-off-by: Yi Cong Link: https://lore.kernel.org/all/96e31963da0c42dcb52ce44f818963d7@realtek.com/ Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260306071627.56501-1-cong.yi@linux.dev --- drivers/net/wireless/realtek/rtl8xxxu/core.c | 28 ++++++-------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c index b4efc6f00a37..d1b1474cba67 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c @@ -4697,20 +4697,6 @@ static const struct ieee80211_rate rtl8xxxu_legacy_ratetable[] = { {.bitrate = 540, .hw_value = 0x0b,}, }; -static void rtl8xxxu_desc_to_mcsrate(u16 rate, u8 *mcs, u8 *nss) -{ - if (rate <= DESC_RATE_54M) - return; - - if (rate >= DESC_RATE_MCS0 && rate <= DESC_RATE_MCS15) { - if (rate < DESC_RATE_MCS8) - *nss = 1; - else - *nss = 2; - *mcs = rate - DESC_RATE_MCS0; - } -} - static void rtl8xxxu_set_basic_rates(struct rtl8xxxu_priv *priv, u32 rate_cfg) { struct ieee80211_hw *hw = priv->hw; @@ -4820,23 +4806,25 @@ static void rtl8xxxu_set_aifs(struct rtl8xxxu_priv *priv, u8 slot_time) void rtl8xxxu_update_ra_report(struct rtl8xxxu_ra_report *rarpt, u8 rate, u8 sgi, u8 bw) { - u8 mcs, nss; - rarpt->txrate.flags = 0; if (rate <= DESC_RATE_54M) { rarpt->txrate.legacy = rtl8xxxu_legacy_ratetable[rate].bitrate; - } else { - rtl8xxxu_desc_to_mcsrate(rate, &mcs, &nss); + } else if (rate >= DESC_RATE_MCS0 && rate <= DESC_RATE_MCS15) { rarpt->txrate.flags |= RATE_INFO_FLAGS_MCS; + if (rate < DESC_RATE_MCS8) + rarpt->txrate.nss = 1; + else + rarpt->txrate.nss = 2; - rarpt->txrate.mcs = mcs; - rarpt->txrate.nss = nss; + rarpt->txrate.mcs = rate - DESC_RATE_MCS0; if (sgi) rarpt->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; rarpt->txrate.bw = bw; + } else { + return; } rarpt->bit_rate = cfg80211_calculate_bitrate(&rarpt->txrate); From 68e7d359a5eeea75197626c800d082955032e30a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 6 Mar 2026 09:51:42 +0100 Subject: [PATCH 019/124] wifi: rtl818x: drop redundant device reference Driver core holds a reference to the USB interface and its parent USB device while the interface is bound to a driver and there is no need to take additional references unless the structures are needed after disconnect. Drop the redundant device reference to reduce cargo culting, make it easier to spot drivers where an extra reference is needed, and reduce the risk of memory leaks when drivers fail to release it. Signed-off-by: Johan Hovold Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260306085144.12064-17-johan@kernel.org --- drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c index f7e0f6573180..1d21c468a236 100644 --- a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c @@ -1475,8 +1475,6 @@ static int rtl8187_probe(struct usb_interface *intf, usb_set_intfdata(intf, dev); priv->udev = udev; - usb_get_dev(udev); - skb_queue_head_init(&priv->rx_queue); BUILD_BUG_ON(sizeof(priv->channels) != sizeof(rtl818x_channels)); @@ -1663,7 +1661,6 @@ static int rtl8187_probe(struct usb_interface *intf, err_free_dmabuf: kfree(priv->io_dmabuf); usb_set_intfdata(intf, NULL); - usb_put_dev(udev); err_free_dev: ieee80211_free_hw(dev); return err; @@ -1685,7 +1682,6 @@ static void rtl8187_disconnect(struct usb_interface *intf) priv = dev->priv; usb_reset_device(priv->udev); - usb_put_dev(interface_to_usbdev(intf)); kfree(priv->io_dmabuf); ieee80211_free_hw(dev); } From 711b8add39294393a086b5c0e51f0c8d79a89f4d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 6 Mar 2026 09:51:43 +0100 Subject: [PATCH 020/124] wifi: rtl8xxxu: drop redundant device reference Driver core holds a reference to the USB interface and its parent USB device while the interface is bound to a driver and there is no need to take additional references unless the structures are needed after disconnect. Drop the redundant device reference to reduce cargo culting, make it easier to spot drivers where an extra reference is needed, and reduce the risk of memory leaks when drivers fail to release it. Signed-off-by: Johan Hovold Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260306085144.12064-18-johan@kernel.org --- drivers/net/wireless/realtek/rtl8xxxu/core.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c index d1b1474cba67..cb6c8859a918 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c @@ -7686,7 +7686,7 @@ static int rtl8xxxu_probe(struct usb_interface *interface, int ret; int untested = 1; - udev = usb_get_dev(interface_to_usbdev(interface)); + udev = interface_to_usbdev(interface); switch (id->idVendor) { case USB_VENDOR_ID_REALTEK: @@ -7745,10 +7745,8 @@ static int rtl8xxxu_probe(struct usb_interface *interface, } hw = ieee80211_alloc_hw(sizeof(struct rtl8xxxu_priv), &rtl8xxxu_ops); - if (!hw) { - ret = -ENOMEM; - goto err_put_dev; - } + if (!hw) + return -ENOMEM; priv = hw->priv; priv->hw = hw; @@ -7890,8 +7888,6 @@ err_set_intfdata: mutex_destroy(&priv->h2c_mutex); ieee80211_free_hw(hw); -err_put_dev: - usb_put_dev(udev); return ret; } @@ -7924,7 +7920,6 @@ static void rtl8xxxu_disconnect(struct usb_interface *interface) "Device still attached, trying to reset\n"); usb_reset_device(priv->udev); } - usb_put_dev(priv->udev); ieee80211_free_hw(hw); } From bbb15e71156cd9f5e1869eee7207a06ea8e96c39 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 6 Mar 2026 09:51:44 +0100 Subject: [PATCH 021/124] wifi: rtw88: fix device leak on probe failure Driver core holds a reference to the USB interface and its parent USB device while the interface is bound to a driver and there is no need to take additional references unless the structures are needed after disconnect. This driver takes a reference to the USB device during probe but does not to release it on all probe errors (e.g. when descriptor parsing fails). Drop the redundant device reference to fix the leak, reduce cargo culting, make it easier to spot drivers where an extra reference is needed, and reduce the risk of further memory leaks. Fixes: a82dfd33d123 ("wifi: rtw88: Add common USB chip support") Reported-by: Greg Kroah-Hartman Link: https://lore.kernel.org/netdev/2026022319-turbofan-darkened-206d@gregkh/ Cc: stable@vger.kernel.org # 6.2 Cc: Sascha Hauer Signed-off-by: Johan Hovold Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260306085144.12064-19-johan@kernel.org --- drivers/net/wireless/realtek/rtw88/usb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c index 433b06c8d8a6..718940ebba31 100644 --- a/drivers/net/wireless/realtek/rtw88/usb.c +++ b/drivers/net/wireless/realtek/rtw88/usb.c @@ -1041,7 +1041,7 @@ static int rtw_usb_intf_init(struct rtw_dev *rtwdev, struct usb_interface *intf) { struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); - struct usb_device *udev = usb_get_dev(interface_to_usbdev(intf)); + struct usb_device *udev = interface_to_usbdev(intf); int ret; rtwusb->udev = udev; @@ -1067,7 +1067,6 @@ static void rtw_usb_intf_deinit(struct rtw_dev *rtwdev, { struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); - usb_put_dev(rtwusb->udev); kfree(rtwusb->usb_data); usb_set_intfdata(intf, NULL); } From c2a21f35cea43c3287a4f314b7aab22ea7234122 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 6 Mar 2026 10:32:06 +0100 Subject: [PATCH 022/124] wifi: rtw89: drop redundant device reference Driver core holds a reference to the USB interface and its parent USB device while the interface is bound to a driver and there is no need to take additional references unless the structures are needed after disconnect. Drop the redundant device reference to reduce cargo culting, make it easier to spot drivers where an extra reference is needed, and reduce the risk of memory leaks when drivers fail to release it. Signed-off-by: Johan Hovold Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260306093206.21081-1-johan@kernel.org --- drivers/net/wireless/realtek/rtw89/usb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index da1b7ce8089e..64f0c0ec5ed4 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -935,7 +935,7 @@ static int rtw89_usb_intf_init(struct rtw89_dev *rtwdev, if (!rtwusb->vendor_req_buf) return -ENOMEM; - rtwusb->udev = usb_get_dev(interface_to_usbdev(intf)); + rtwusb->udev = interface_to_usbdev(intf); usb_set_intfdata(intf, rtwdev->hw); @@ -949,7 +949,6 @@ static void rtw89_usb_intf_deinit(struct rtw89_dev *rtwdev, { struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); - usb_put_dev(rtwusb->udev); kfree(rtwusb->vendor_req_buf); usb_set_intfdata(intf, NULL); } From fc25b384fc0bb983fe383e8392b6c2f4aafec77d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 9 Mar 2026 09:33:36 +0100 Subject: [PATCH 023/124] wifi: rtlwifi: usb: drop redundant device reference Driver core holds a reference to the USB interface and its parent USB device while the interface is bound to a driver and there is no need to take additional references unless the structures are needed after disconnect. Drop the redundant device reference to reduce cargo culting, make it easier to spot drivers where an extra reference is needed, and reduce the risk of memory leaks when drivers fail to release it. Signed-off-by: Johan Hovold Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260309083336.16397-1-johan@kernel.org --- drivers/net/wireless/realtek/rtlwifi/usb.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/usb.c b/drivers/net/wireless/realtek/rtlwifi/usb.c index d35ed56d6db9..9a64df9eed39 100644 --- a/drivers/net/wireless/realtek/rtlwifi/usb.c +++ b/drivers/net/wireless/realtek/rtlwifi/usb.c @@ -986,7 +986,6 @@ int rtl_usb_probe(struct usb_interface *intf, init_completion(&rtlpriv->firmware_loading_complete); SET_IEEE80211_DEV(hw, &intf->dev); udev = interface_to_usbdev(intf); - usb_get_dev(udev); usb_priv = rtl_usbpriv(hw); memset(usb_priv, 0, sizeof(*usb_priv)); usb_priv->dev.intf = intf; @@ -1038,7 +1037,6 @@ error_out: rtl_deinit_core(hw); error_out2: _rtl_usb_io_handler_release(hw); - usb_put_dev(udev); kfree(rtlpriv->usb_data); ieee80211_free_hw(hw); return -ENODEV; @@ -1050,7 +1048,6 @@ void rtl_usb_disconnect(struct usb_interface *intf) struct ieee80211_hw *hw = usb_get_intfdata(intf); struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *rtlmac = rtl_mac(rtl_priv(hw)); - struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw)); if (unlikely(!rtlpriv)) return; @@ -1072,7 +1069,6 @@ void rtl_usb_disconnect(struct usb_interface *intf) kfree(rtlpriv->usb_data); rtlpriv->cfg->ops->deinit_sw_vars(hw); _rtl_usb_io_handler_release(hw); - usb_put_dev(rtlusb->udev); usb_set_intfdata(intf, NULL); ieee80211_free_hw(hw); } From e1b6b5af5d1725c9474baf3846f717a738f7e000 Mon Sep 17 00:00:00 2001 From: Jaime Saguillo Revilla Date: Sun, 8 Mar 2026 11:18:50 +0000 Subject: [PATCH 024/124] wifi: rtlwifi: rtl8192d: fix typo in H2C wait counter names Rename local variables in rtl92d_fill_h2c_cmd() from wait_writeh2c_limmit/wait_h2c_limmit to wait_writeh2c_limit/wait_h2c_limit. No functional change. Signed-off-by: Jaime Saguillo Revilla Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260308111850.20420-1-jaime.saguillo@gmail.com --- .../wireless/realtek/rtlwifi/rtl8192d/fw_common.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192d/fw_common.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192d/fw_common.c index aa54dbde6ea8..a255a5061d77 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192d/fw_common.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192d/fw_common.c @@ -212,9 +212,9 @@ void rtl92d_fill_h2c_cmd(struct ieee80211_hw *hw, struct rtl_priv *rtlpriv = rtl_priv(hw); u8 boxcontent[4], boxextcontent[2]; u16 box_reg = 0, box_extreg = 0; - u8 wait_writeh2c_limmit = 100; + u8 wait_writeh2c_limit = 100; bool bwrite_success = false; - u8 wait_h2c_limmit = 100; + u8 wait_h2c_limit = 100; u32 h2c_waitcounter = 0; bool isfw_read = false; unsigned long flag; @@ -261,8 +261,8 @@ void rtl92d_fill_h2c_cmd(struct ieee80211_hw *hw, } while (!bwrite_success) { - wait_writeh2c_limmit--; - if (wait_writeh2c_limmit == 0) { + wait_writeh2c_limit--; + if (wait_writeh2c_limit == 0) { pr_err("Write H2C fail because no trigger for FW INT!\n"); break; } @@ -278,8 +278,8 @@ void rtl92d_fill_h2c_cmd(struct ieee80211_hw *hw, isfw_read = _rtl92d_check_fw_read_last_h2c(hw, boxnum); while (!isfw_read) { - wait_h2c_limmit--; - if (wait_h2c_limmit == 0) { + wait_h2c_limit--; + if (wait_h2c_limit == 0) { rtl_dbg(rtlpriv, COMP_CMD, DBG_LOUD, "Waiting too long for FW read clear HMEBox(%d)!\n", boxnum); From 177520960c012cf223002279c6454726ea6fa73e Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 10 Mar 2026 16:01:35 +0800 Subject: [PATCH 025/124] wifi: rtw89: pci: update SER parameters for suspend/resume In suspend mode, SER timer unit is different from normal mode. Set proper value to prevent expected SER happened during suspend. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260310080146.31113-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/pci.h | 1 + drivers/net/wireless/realtek/rtw89/pci_be.c | 52 +++++++++++++++++++-- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/pci.h b/drivers/net/wireless/realtek/rtw89/pci.h index ccfa6d33623a..dc488d9a8884 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.h +++ b/drivers/net/wireless/realtek/rtw89/pci.h @@ -1016,6 +1016,7 @@ #define B_BE_PL1_IGNORE_HOT_RST BIT(30) #define B_BE_PL1_TIMER_UNIT_MASK GENMASK(19, 17) #define PCIE_SER_TIMER_UNIT 0x2 +#define PCIE_SER_WOW_TIMER_UNIT 0x4 #define B_BE_PL1_TIMER_CLEAR BIT(0) #define R_BE_REG_PL1_MASK 0x34B0 diff --git a/drivers/net/wireless/realtek/rtw89/pci_be.c b/drivers/net/wireless/realtek/rtw89/pci_be.c index 114f40c6c31b..dea01d9e57fd 100644 --- a/drivers/net/wireless/realtek/rtw89/pci_be.c +++ b/drivers/net/wireless/realtek/rtw89/pci_be.c @@ -721,12 +721,24 @@ static int __maybe_unused rtw89_pci_suspend_be(struct device *dev) { struct ieee80211_hw *hw = dev_get_drvdata(dev); struct rtw89_dev *rtwdev = hw->priv; + u32 val32; rtw89_write32_set(rtwdev, R_BE_RSV_CTRL, B_BE_WLOCK_1C_BIT6); rtw89_write32_set(rtwdev, R_BE_RSV_CTRL, B_BE_R_DIS_PRST); rtw89_write32_clr(rtwdev, R_BE_RSV_CTRL, B_BE_WLOCK_1C_BIT6); rtw89_write32_set(rtwdev, R_BE_PCIE_FRZ_CLK, B_BE_PCIE_FRZ_REG_RST); - rtw89_write32_clr(rtwdev, R_BE_REG_PL1_MASK, B_BE_SER_PM_MASTER_IMR); + + val32 = rtw89_read32(rtwdev, R_BE_SER_PL1_CTRL); + if (val32 & B_BE_PL1_SER_PL1_EN) { + val32 = u32_replace_bits(val32, PCIE_SER_WOW_TIMER_UNIT, + B_BE_PL1_TIMER_UNIT_MASK); + rtw89_write32(rtwdev, R_BE_SER_PL1_CTRL, val32); + + if (rtwdev->chip->chip_id == RTL8922A) + rtw89_write32_clr(rtwdev, R_BE_REG_PL1_MASK, + B_BE_SER_PM_MASTER_IMR); + } + return 0; } @@ -735,12 +747,19 @@ static int __maybe_unused rtw89_pci_resume_be(struct device *dev) struct ieee80211_hw *hw = dev_get_drvdata(dev); struct rtw89_dev *rtwdev = hw->priv; u32 polling; + u32 val32; + u16 val16; int ret; rtw89_write32_set(rtwdev, R_BE_RSV_CTRL, B_BE_WLOCK_1C_BIT6); rtw89_write32_clr(rtwdev, R_BE_RSV_CTRL, B_BE_R_DIS_PRST); rtw89_write32_clr(rtwdev, R_BE_RSV_CTRL, B_BE_WLOCK_1C_BIT6); rtw89_write32_clr(rtwdev, R_BE_PCIE_FRZ_CLK, B_BE_PCIE_FRZ_REG_RST); + + val32 = rtw89_read32(rtwdev, R_BE_SER_PL1_CTRL); + if (!(val32 & B_BE_PL1_SER_PL1_EN)) + goto clear_phy_isr; + rtw89_write32_clr(rtwdev, R_BE_SER_PL1_CTRL, B_BE_PL1_SER_PL1_EN); ret = read_poll_timeout_atomic(rtw89_read32, polling, !polling, 1, 1000, @@ -748,8 +767,35 @@ static int __maybe_unused rtw89_pci_resume_be(struct device *dev) if (ret) rtw89_warn(rtwdev, "[ERR] PCIE SER clear polling fail\n"); - rtw89_write32_set(rtwdev, R_BE_SER_PL1_CTRL, B_BE_PL1_SER_PL1_EN); - rtw89_write32_set(rtwdev, R_BE_REG_PL1_MASK, B_BE_SER_PM_MASTER_IMR); + if (rtwdev->chip->chip_id == RTL8922A) + rtw89_write32_set(rtwdev, R_BE_REG_PL1_MASK, + B_BE_SER_PM_MASTER_IMR | B_BE_SER_PCLKREQ_ACK_MASK); + + val32 = rtw89_read32(rtwdev, R_BE_SER_PL1_CTRL); + val32 = u32_replace_bits(val32, PCIE_SER_TIMER_UNIT, B_BE_PL1_TIMER_UNIT_MASK); + val32 |= B_BE_PL1_SER_PL1_EN; + rtw89_write32(rtwdev, R_BE_SER_PL1_CTRL, val32); + +clear_phy_isr: + if (rtwdev->chip->chip_id == RTL8922D) { + val16 = rtw89_read16(rtwdev, RAC_DIRECT_OFFESET_L0_G2 + + RAC_ANA41 * RAC_MULT); + if (val16 & PHY_ERR_FLAG_EN) { + rtw89_write16_clr(rtwdev, RAC_DIRECT_OFFESET_L0_G2 + + RAC_ANA41 * RAC_MULT, PHY_ERR_FLAG_EN); + rtw89_write16_set(rtwdev, RAC_DIRECT_OFFESET_L0_G2 + + RAC_ANA41 * RAC_MULT, PHY_ERR_FLAG_EN); + } + + val16 = rtw89_read16(rtwdev, RAC_DIRECT_OFFESET_L0_G1 + + RAC_ANA41 * RAC_MULT); + if (val16 & PHY_ERR_FLAG_EN) { + rtw89_write16_clr(rtwdev, RAC_DIRECT_OFFESET_L0_G1 + + RAC_ANA41 * RAC_MULT, PHY_ERR_FLAG_EN); + rtw89_write16_set(rtwdev, RAC_DIRECT_OFFESET_L0_G1 + + RAC_ANA41 * RAC_MULT, PHY_ERR_FLAG_EN); + } + } rtw89_pci_basic_cfg(rtwdev, true); From 9a38ef92aaa2d3c02ae1f6f1cacc3d3a8cf19db6 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 10 Mar 2026 16:01:36 +0800 Subject: [PATCH 026/124] wifi: rtw89: mac: remove A-die off setting for RTL8852C and RTL8922A Fix timing issue of A-die off followed by XTAL off. Otherwise, device might get lost potentially. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260310080146.31113-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8852c.c | 2 +- drivers/net/wireless/realtek/rtw89/rtw8922a.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index e62a7288c8aa..7ea0a8905282 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -463,7 +463,7 @@ static int rtw8852c_pwr_off_func(struct rtw89_dev *rtwdev) else if (rtwdev->hci.type == RTW89_HCI_TYPE_USB) rtw89_write32_clr(rtwdev, R_AX_SYS_PW_CTRL, B_AX_SOP_EDSWR); - rtw89_write32_set(rtwdev, R_AX_SYS_PW_CTRL, B_AX_XTAL_OFF_A_DIE); + rtw89_write32_clr(rtwdev, R_AX_SYS_PW_CTRL, B_AX_XTAL_OFF_A_DIE); rtw89_write32_set(rtwdev, R_AX_SYS_SWR_CTRL1, B_AX_SYM_CTRL_SPS_PWMFREQ); rtw89_write32_mask(rtwdev, R_AX_SPS_DIG_ON_CTRL0, B_AX_REG_ZCDC_H_MASK, 0x3); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 36ef36110602..a489aaf90f23 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -492,7 +492,7 @@ static int rtw8922a_pwr_off_func(struct rtw89_dev *rtwdev) return ret; rtw89_write32(rtwdev, R_BE_WLLPS_CTRL, 0x0000A1B2); - rtw89_write32_set(rtwdev, R_BE_SYS_PW_CTRL, B_BE_XTAL_OFF_A_DIE); + rtw89_write32_clr(rtwdev, R_BE_SYS_PW_CTRL, B_BE_XTAL_OFF_A_DIE); rtw89_write32_set(rtwdev, R_BE_SYS_PW_CTRL, B_BE_APFM_SWLPS); rtw89_write32(rtwdev, R_BE_UDM1, 0); From 84f5e0eaf84ec55edfc053e858d58af074e3fcb4 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 10 Mar 2026 16:01:37 +0800 Subject: [PATCH 027/124] wifi: rtw89: phy: limit AMPDU number for RA try rate When RA (Rate Adaptive) does try higher rate, a TRY bit is flagged, and hardware will reference registers configured by this patch as maximum number of AMPDU. To prevent aggregate too many MPDU over peer's capability causing loss in peer side, set the minimum values across all stations and TID since there is single one register per hardware band. Consider MLD case, a BA session can run across two hardware bands, so set the same value as well. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260310080146.31113-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac.c | 2 ++ drivers/net/wireless/realtek/rtw89/mac.h | 1 + drivers/net/wireless/realtek/rtw89/mac80211.c | 3 +++ drivers/net/wireless/realtek/rtw89/mac_be.c | 2 ++ drivers/net/wireless/realtek/rtw89/phy.c | 27 +++++++++++++++++++ drivers/net/wireless/realtek/rtw89/phy.h | 1 + 6 files changed, 36 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 234928613ef4..a292a14394b2 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -7319,6 +7319,8 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_ax = { }, .wow_ctrl = {.addr = R_AX_WOW_CTRL, .mask = B_AX_WOW_WOWEN,}, .agg_limit = {.addr = R_AX_AMPDU_AGG_LIMIT, .mask = B_AX_AMPDU_MAX_TIME_MASK,}, + .ra_agg_limit = {.addr = R_AX_AMPDU_AGG_LIMIT, + .mask = B_AX_RA_TRY_RATE_AGG_LMT_MASK,}, .txcnt_limit = {.addr = R_AX_TXCNT, .mask = B_AX_L_TXCNT_LMT_MASK,}, .check_mac_en = rtw89_mac_check_mac_en_ax, diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index e6b715b95409..9c77bfaa34ee 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -1037,6 +1037,7 @@ struct rtw89_mac_gen_def { struct rtw89_reg_def narrow_bw_ru_dis; struct rtw89_reg_def wow_ctrl; struct rtw89_reg_def agg_limit; + struct rtw89_reg_def ra_agg_limit; struct rtw89_reg_def txcnt_limit; int (*check_mac_en)(struct rtw89_dev *rtwdev, u8 band, diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index 1ef73bfc40d1..cd8e2c8de888 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -1005,6 +1005,8 @@ static int rtw89_ops_ampdu_action(struct ieee80211_hw *hw, clear_bit(tid, rtwsta->ampdu_map); rtw89_chip_h2c_ampdu_cmac_tbl(rtwdev, rtwvif, rtwsta); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + rtw89_leave_ps_mode(rtwdev); + rtw89_phy_ra_recalc_agg_limit(rtwdev); break; case IEEE80211_AMPDU_TX_OPERATIONAL: set_bit(RTW89_TXQ_F_AMPDU, &rtwtxq->flags); @@ -1013,6 +1015,7 @@ static int rtw89_ops_ampdu_action(struct ieee80211_hw *hw, set_bit(tid, rtwsta->ampdu_map); rtw89_leave_ps_mode(rtwdev); rtw89_chip_h2c_ampdu_cmac_tbl(rtwdev, rtwvif, rtwsta); + rtw89_phy_ra_recalc_agg_limit(rtwdev); break; case IEEE80211_AMPDU_RX_START: rtw89_chip_h2c_ba_cam(rtwdev, rtwsta, true, params); diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index dc66b1ee851a..39a28fd27412 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -3193,6 +3193,8 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_be = { }, .wow_ctrl = {.addr = R_BE_WOW_CTRL, .mask = B_BE_WOW_WOWEN,}, .agg_limit = {.addr = R_BE_AMPDU_AGG_LIMIT, .mask = B_BE_AMPDU_MAX_TIME_MASK,}, + .ra_agg_limit = {.addr = R_BE_AMPDU_AGG_LIMIT, + .mask = B_BE_RA_TRY_RATE_AGG_LMT_MASK,}, .txcnt_limit = {.addr = R_BE_TXCNT, .mask = B_BE_L_TXCNT_LMT_MASK,}, .check_mac_en = rtw89_mac_check_mac_en_be, diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 74f5d5562848..0fa4d8d791f1 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -775,6 +775,33 @@ void rtw89_phy_ra_assoc(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_ rtw89_fw_h2c_ra(rtwdev, ra, csi); } +void rtw89_phy_ra_recalc_agg_limit(struct rtw89_dev *rtwdev) +{ + const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; + const struct rtw89_reg_def *ra_limit = &mac->ra_agg_limit; + struct ieee80211_sta *sta; + struct rtw89_sta *rtwsta; + u16 agg_num = U16_MAX; + u8 tid; + + for_each_station(sta, rtwdev->hw) { + rtwsta = sta_to_rtwsta(sta); + + for_each_set_bit(tid, rtwsta->ampdu_map, IEEE80211_NUM_TIDS) + agg_num = min(agg_num, rtwsta->ampdu_params[tid].agg_num); + } + + if (agg_num == U16_MAX) + agg_num = 0x3F; + else + agg_num = clamp(agg_num, 1, 256) - 1; + + rtw89_write32_idx(rtwdev, ra_limit->addr, ra_limit->mask, agg_num, RTW89_MAC_0); + if (!rtwdev->dbcc_en) + return; + rtw89_write32_idx(rtwdev, ra_limit->addr, ra_limit->mask, agg_num, RTW89_MAC_1); +} + u8 rtw89_phy_get_txsc(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_bandwidth dbw) diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 094c7e45f254..bde419edf744 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -1006,6 +1006,7 @@ void rtw89_phy_ra_update_sta(struct rtw89_dev *rtwdev, struct ieee80211_sta *sta void rtw89_phy_ra_update_sta_link(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_link, u32 changed); +void rtw89_phy_ra_recalc_agg_limit(struct rtw89_dev *rtwdev); void rtw89_phy_rate_pattern_vif(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, const struct cfg80211_bitrate_mask *mask); From be28b2c4eed490c95c2ca2ebacbf7795912b3ec6 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 10 Mar 2026 16:01:38 +0800 Subject: [PATCH 028/124] wifi: rtw89: move disabling dynamic mechanism functions to core Some dynamic mechanism (DM) may need to be disabled during some normal processes rather than debugging. For example, should not do MLSR switch during SCAN/ROC or even MCC. So, move the disabling DM functions to core for impending uses. No logic changes. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260310080146.31113-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 29 ++++++++++++++++++ drivers/net/wireless/realtek/rtw89/core.h | 3 ++ drivers/net/wireless/realtek/rtw89/debug.c | 35 ++-------------------- 3 files changed, 35 insertions(+), 32 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 9d9b91570989..81004ef18168 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -4713,6 +4713,35 @@ static void rtw89_track_work(struct wiphy *wiphy, struct wiphy_work *work) rtw89_enter_lps_track(rtwdev); } +void rtw89_core_dm_disable_cfg(struct rtw89_dev *rtwdev, u32 new) +{ + struct rtw89_hal *hal = &rtwdev->hal; + u32 old = hal->disabled_dm_bitmap; + + if (new == old) + return; + + hal->disabled_dm_bitmap = new; + + rtw89_debug(rtwdev, RTW89_DBG_STATE, "Disable DM: 0x%x -> 0x%x\n", old, new); +} + +void rtw89_core_dm_disable_set(struct rtw89_dev *rtwdev, enum rtw89_dm_type type) +{ + struct rtw89_hal *hal = &rtwdev->hal; + u32 cur = hal->disabled_dm_bitmap; + + rtw89_core_dm_disable_cfg(rtwdev, cur | BIT(type)); +} + +void rtw89_core_dm_disable_clr(struct rtw89_dev *rtwdev, enum rtw89_dm_type type) +{ + struct rtw89_hal *hal = &rtwdev->hal; + u32 cur = hal->disabled_dm_bitmap; + + rtw89_core_dm_disable_cfg(rtwdev, cur & ~BIT(type)); +} + u8 rtw89_core_acquire_bit_map(unsigned long *addr, unsigned long size) { unsigned long bit; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index cf0cc718f41c..05f8ad6d3034 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -7862,5 +7862,8 @@ void rtw89_core_update_p2p_ps(struct rtw89_dev *rtwdev, void rtw89_core_ntfy_btc_event(struct rtw89_dev *rtwdev, enum rtw89_btc_hmsg event); int rtw89_core_mlsr_switch(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, unsigned int link_id); +void rtw89_core_dm_disable_cfg(struct rtw89_dev *rtwdev, u32 new); +void rtw89_core_dm_disable_set(struct rtw89_dev *rtwdev, enum rtw89_dm_type type); +void rtw89_core_dm_disable_clr(struct rtw89_dev *rtwdev, enum rtw89_dm_type type); #endif diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 3e16ed2c4570..aee0f25e036a 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -4333,35 +4333,6 @@ static ssize_t rtw89_debug_priv_stations_get(struct rtw89_dev *rtwdev, return p - buf; } -static void rtw89_debug_disable_dm_cfg_bmap(struct rtw89_dev *rtwdev, u32 new) -{ - struct rtw89_hal *hal = &rtwdev->hal; - u32 old = hal->disabled_dm_bitmap; - - if (new == old) - return; - - hal->disabled_dm_bitmap = new; - - rtw89_debug(rtwdev, RTW89_DBG_STATE, "Disable DM: 0x%x -> 0x%x\n", old, new); -} - -static void rtw89_debug_disable_dm_set_flag(struct rtw89_dev *rtwdev, u8 flag) -{ - struct rtw89_hal *hal = &rtwdev->hal; - u32 cur = hal->disabled_dm_bitmap; - - rtw89_debug_disable_dm_cfg_bmap(rtwdev, cur | BIT(flag)); -} - -static void rtw89_debug_disable_dm_clr_flag(struct rtw89_dev *rtwdev, u8 flag) -{ - struct rtw89_hal *hal = &rtwdev->hal; - u32 cur = hal->disabled_dm_bitmap; - - rtw89_debug_disable_dm_cfg_bmap(rtwdev, cur & ~BIT(flag)); -} - #define DM_INFO(type) {RTW89_DM_ ## type, #type} static const struct rtw89_disabled_dm_info { @@ -4412,7 +4383,7 @@ rtw89_debug_priv_disable_dm_set(struct rtw89_dev *rtwdev, if (ret) return -EINVAL; - rtw89_debug_disable_dm_cfg_bmap(rtwdev, conf); + rtw89_core_dm_disable_cfg(rtwdev, conf); return count; } @@ -4475,7 +4446,7 @@ rtw89_debug_priv_mlo_mode_set(struct rtw89_dev *rtwdev, if (num != 2) return -EINVAL; - rtw89_debug_disable_dm_set_flag(rtwdev, RTW89_DM_MLO); + rtw89_core_dm_disable_set(rtwdev, RTW89_DM_MLO); rtw89_debug(rtwdev, RTW89_DBG_STATE, "Set MLO mode to %x\n", mlo_mode); @@ -4485,7 +4456,7 @@ rtw89_debug_priv_mlo_mode_set(struct rtw89_dev *rtwdev, break; default: rtw89_debug(rtwdev, RTW89_DBG_STATE, "Unsupported MLO mode\n"); - rtw89_debug_disable_dm_clr_flag(rtwdev, RTW89_DM_MLO); + rtw89_core_dm_disable_clr(rtwdev, RTW89_DM_MLO); return -EOPNOTSUPP; } From 4516621686cb3c628c2094f38e622a94958b335e Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 10 Mar 2026 16:01:39 +0800 Subject: [PATCH 029/124] wifi: rtw89: tweak settings of TX power and channel for Wi-Fi 7 The support_mlo flag depends on FW features, so it's determined at runtime. Since Wi-Fi 7 chip now needs to initialize second HW band, if support_mlo is not allowed, second HW band might act without settings of TX power and channel. So, set that for Wi-Fi 7 chip. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260310080146.31113-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 81004ef18168..6ab99a3577f3 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -463,7 +463,7 @@ void rtw89_core_set_chip_txpwr(struct rtw89_dev *rtwdev) chan = rtw89_mgnt_chan_get(rtwdev, 0); __rtw89_core_set_chip_txpwr(rtwdev, chan, RTW89_PHY_0); - if (!rtwdev->support_mlo) + if (rtwdev->chip->chip_gen == RTW89_CHIP_AX) return; chan = rtw89_mgnt_chan_get(rtwdev, 1); @@ -558,7 +558,7 @@ int rtw89_set_channel(struct rtw89_dev *rtwdev) chan = rtw89_mgnt_chan_get(rtwdev, 0); __rtw89_set_channel(rtwdev, chan, RTW89_MAC_0, RTW89_PHY_0); - if (!rtwdev->support_mlo) + if (rtwdev->chip->chip_gen == RTW89_CHIP_AX) return 0; chan = rtw89_mgnt_chan_get(rtwdev, 1); From 2fed8de4eb98764e559189eb3bac9d3f2954cb95 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 10 Mar 2026 16:01:40 +0800 Subject: [PATCH 030/124] wifi: rtw89: chan: simplify link handling related to ROC The original channel is swapped out for the target channel during ROC. And, all vifs/links accessing the original channel will be marked with off-channel. So, it doesn't seem necessary for chan.c to determine which link instance it is. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260310080146.31113-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/chan.c | 11 ----------- drivers/net/wireless/realtek/rtw89/core.h | 1 - 2 files changed, 12 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c index 9b2f6f0a00fd..def9e4f3af59 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.c +++ b/drivers/net/wireless/realtek/rtw89/chan.c @@ -276,7 +276,6 @@ void rtw89_config_roc_chandef(struct rtw89_dev *rtwdev, } hal->roc_chandef = *chandef; - hal->roc_link_index = rtw89_vif_link_inst_get_index(rtwvif_link); } else { cur = atomic_cmpxchg(&hal->roc_chanctx_idx, idx, RTW89_CHANCTX_IDLE); @@ -389,7 +388,6 @@ const struct rtw89_chan *__rtw89_mgnt_chan_get(struct rtw89_dev *rtwdev, struct rtw89_hal *hal = &rtwdev->hal; struct rtw89_entity_mgnt *mgnt = &hal->entity_mgnt; enum rtw89_chanctx_idx chanctx_idx; - enum rtw89_chanctx_idx roc_idx; enum rtw89_entity_mode mode; u8 role_index; @@ -419,15 +417,6 @@ const struct rtw89_chan *__rtw89_mgnt_chan_get(struct rtw89_dev *rtwdev, if (chanctx_idx == RTW89_CHANCTX_IDLE) goto dflt; - roc_idx = atomic_read(&hal->roc_chanctx_idx); - if (roc_idx != RTW89_CHANCTX_IDLE) { - /* ROC is ongoing (given ROC runs on @hal->roc_link_index). - * If @link_index is the same, get the ongoing ROC chanctx. - */ - if (link_index == hal->roc_link_index) - chanctx_idx = roc_idx; - } - return rtw89_chan_get(rtwdev, chanctx_idx); dflt: diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 05f8ad6d3034..01573150ab3c 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5170,7 +5170,6 @@ struct rtw89_hal { bool no_eht; atomic_t roc_chanctx_idx; - u8 roc_link_index; DECLARE_BITMAP(changes, NUM_OF_RTW89_CHANCTX_CHANGES); DECLARE_BITMAP(entity_map, NUM_OF_RTW89_CHANCTX); From cf3cd3687d8a9ff2940d97dc649c53c62458d3e9 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 10 Mar 2026 16:01:41 +0800 Subject: [PATCH 031/124] wifi: rtw89: chan: recalc MLO DBCC mode based on current entity mode Since MLD vif can do MLSR switch, it may not always run on HW band 0. But when preparing MCC for MLD + P2P, P2P vif needs to use HW band 0 to handle connection, i.e. uses of HW bands may be different by vif. The current major role/vif can be indicated through entity mode. So, based on it, recalculate MLO DBCC mode to change use of HW band. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260310080146.31113-9-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/chan.c | 61 +++++++++++++++-------- 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c index def9e4f3af59..ceb399fc2b94 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.c +++ b/drivers/net/wireless/realtek/rtw89/chan.c @@ -381,6 +381,23 @@ static void rtw89_normalize_link_chanctx(struct rtw89_dev *rtwdev, rtw89_swap_chanctx(rtwdev, rtwvif_link->chanctx_idx, cur->chanctx_idx); } +static u8 rtw89_entity_role_get_index(struct rtw89_dev *rtwdev) +{ + enum rtw89_entity_mode mode; + + mode = rtw89_get_entity_mode(rtwdev); + switch (mode) { + default: + WARN(1, "Invalid ent mode: %d\n", mode); + fallthrough; + case RTW89_ENTITY_MODE_SCC_OR_SMLD: + case RTW89_ENTITY_MODE_MCC: + return 0; + case RTW89_ENTITY_MODE_MCC_PREPARE: + return 1; + } +} + const struct rtw89_chan *__rtw89_mgnt_chan_get(struct rtw89_dev *rtwdev, const char *caller_message, u8 link_index, bool nullchk) @@ -388,7 +405,6 @@ const struct rtw89_chan *__rtw89_mgnt_chan_get(struct rtw89_dev *rtwdev, struct rtw89_hal *hal = &rtwdev->hal; struct rtw89_entity_mgnt *mgnt = &hal->entity_mgnt; enum rtw89_chanctx_idx chanctx_idx; - enum rtw89_entity_mode mode; u8 role_index; lockdep_assert_wiphy(rtwdev->hw->wiphy); @@ -399,19 +415,7 @@ const struct rtw89_chan *__rtw89_mgnt_chan_get(struct rtw89_dev *rtwdev, goto dflt; } - mode = rtw89_get_entity_mode(rtwdev); - switch (mode) { - case RTW89_ENTITY_MODE_SCC_OR_SMLD: - case RTW89_ENTITY_MODE_MCC: - role_index = 0; - break; - case RTW89_ENTITY_MODE_MCC_PREPARE: - role_index = 1; - break; - default: - WARN(1, "Invalid ent mode: %d\n", mode); - goto dflt; - } + role_index = rtw89_entity_role_get_index(rtwdev); chanctx_idx = mgnt->chanctx_tbl[role_index][link_index]; if (chanctx_idx == RTW89_CHANCTX_IDLE) @@ -479,10 +483,28 @@ rtw89_entity_sel_mlo_dbcc_mode(struct rtw89_dev *rtwdev, u8 active_hws) } } -static -void rtw89_entity_recalc_mlo_dbcc_mode(struct rtw89_dev *rtwdev, u8 active_hws) +static void rtw89_entity_recalc_mlo_dbcc_mode(struct rtw89_dev *rtwdev) { + struct rtw89_entity_mgnt *mgnt = &rtwdev->hal.entity_mgnt; enum rtw89_mlo_dbcc_mode mode; + struct rtw89_vif *role; + u8 active_hws = 0; + u8 ridx; + + ridx = rtw89_entity_role_get_index(rtwdev); + role = mgnt->active_roles[ridx]; + if (role) { + struct rtw89_vif_link *link; + int i; + + for (i = 0; i < role->links_inst_valid_num; i++) { + link = rtw89_vif_get_link_inst(role, i); + if (!link || !link->chanctx_assigned) + continue; + + active_hws |= BIT(i); + } + } mode = rtw89_entity_sel_mlo_dbcc_mode(rtwdev, active_hws); rtwdev->mlo_dbcc_mode = mode; @@ -496,7 +518,6 @@ static void rtw89_entity_recalc_mgnt_roles(struct rtw89_dev *rtwdev) struct rtw89_entity_mgnt *mgnt = &hal->entity_mgnt; struct rtw89_vif_link *link; struct rtw89_vif *role; - u8 active_hws = 0; u8 pos = 0; int i, j; @@ -545,13 +566,10 @@ fill: continue; mgnt->chanctx_tbl[pos][i] = link->chanctx_idx; - active_hws |= BIT(i); } mgnt->active_roles[pos++] = role; } - - rtw89_entity_recalc_mlo_dbcc_mode(rtwdev, active_hws); } enum rtw89_entity_mode rtw89_entity_recalc(struct rtw89_dev *rtwdev) @@ -621,6 +639,9 @@ enum rtw89_entity_mode rtw89_entity_recalc(struct rtw89_dev *rtwdev) return rtw89_get_entity_mode(rtwdev); rtw89_set_entity_mode(rtwdev, mode); + + rtw89_entity_recalc_mlo_dbcc_mode(rtwdev); + return mode; } From 72dbc78594a19b78467d7a5ea67a0b557e505667 Mon Sep 17 00:00:00 2001 From: Chin-Yen Lee Date: Tue, 10 Mar 2026 16:01:42 +0800 Subject: [PATCH 032/124] wifi: rtw89: wow: add retry for ensuring packet are processed Before entering WoWLAN mode, the driver must ensure that all received packets have been processed to prevent packet loss. Consequently, a retry mechanism has been implemented to guarantee completion. Signed-off-by: Chin-Yen Lee Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260310080146.31113-10-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac.c | 15 ++++++++++++++- drivers/net/wireless/realtek/rtw89/mac.h | 1 + 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index a292a14394b2..35fd18fe6470 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -7183,7 +7183,7 @@ int rtw89_mac_ptk_drop_by_band_and_wait(struct rtw89_dev *rtwdev, return ret; } -int rtw89_mac_cpu_io_rx(struct rtw89_dev *rtwdev, bool wow_enable) +static int _rtw89_mac_cpu_io_rx(struct rtw89_dev *rtwdev, bool wow_enable) { struct rtw89_mac_h2c_info h2c_info = {}; struct rtw89_mac_c2h_info c2h_info = {}; @@ -7206,6 +7206,19 @@ int rtw89_mac_cpu_io_rx(struct rtw89_dev *rtwdev, bool wow_enable) return ret; } +int rtw89_mac_cpu_io_rx(struct rtw89_dev *rtwdev, bool wow_enable) +{ + int i, ret; + + for (i = 0; i < CPU_IO_RX_RETRY_CNT; i++) { + ret = _rtw89_mac_cpu_io_rx(rtwdev, wow_enable); + if (!ret) + return 0; + } + + return ret; +} + static int rtw89_wow_config_mac_ax(struct rtw89_dev *rtwdev, bool enable_wow) { const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 9c77bfaa34ee..88a877556cb3 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -17,6 +17,7 @@ #define BSSID_CAM_ENT_SIZE 0x08 #define HFC_PAGE_UNIT 64 #define RPWM_TRY_CNT 3 +#define CPU_IO_RX_RETRY_CNT 3 enum rtw89_mac_hwmod_sel { RTW89_DMAC_SEL = 0, From 829b89c2b08ff376eb4c5edc66363ea09ba99138 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 10 Mar 2026 16:01:43 +0800 Subject: [PATCH 033/124] wifi: rtw89: replace RF mutex with wiphy lock assertion Now, stack has introduced wiphy lock. And, the normal paths calling RF read/write should be under wiphy lock. So, replace RF mutex with wiphy lock assertion. Besides, in dbgfs paths, add the corresponding lock option. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260310080146.31113-11-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 2 -- drivers/net/wireless/realtek/rtw89/core.h | 14 ++++---------- drivers/net/wireless/realtek/rtw89/debug.c | 6 +++--- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 6ab99a3577f3..18dbf3664f0a 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -6147,7 +6147,6 @@ int rtw89_core_init(struct rtw89_dev *rtwdev) return -ENOMEM; spin_lock_init(&rtwdev->ba_lock); spin_lock_init(&rtwdev->rpwm_lock); - mutex_init(&rtwdev->rf_mutex); rtwdev->total_sta_assoc = 0; rtw89_init_wait(&rtwdev->mcc.wait); @@ -6206,7 +6205,6 @@ void rtw89_core_deinit(struct rtw89_dev *rtwdev) __rtw89_fw_free_all_early_h2c(rtwdev); destroy_workqueue(rtwdev->txq_wq); - mutex_destroy(&rtwdev->rf_mutex); } EXPORT_SYMBOL(rtw89_core_deinit); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 01573150ab3c..ce04ecaa3a5e 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -6182,8 +6182,6 @@ struct rtw89_dev { refcount_t refcount_ap_info; struct list_head rtwvifs_list; - /* used to protect rf read write */ - struct mutex rf_mutex; struct workqueue_struct *txq_wq; struct work_struct txq_work; struct delayed_work txq_reinvoke_work; @@ -6808,22 +6806,18 @@ static inline u32 rtw89_read_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, u32 addr, u32 mask) { - u32 val; + lockdep_assert_wiphy(rtwdev->hw->wiphy); - mutex_lock(&rtwdev->rf_mutex); - val = rtwdev->chip->ops->read_rf(rtwdev, rf_path, addr, mask); - mutex_unlock(&rtwdev->rf_mutex); - - return val; + return rtwdev->chip->ops->read_rf(rtwdev, rf_path, addr, mask); } static inline void rtw89_write_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path, u32 addr, u32 mask, u32 data) { - mutex_lock(&rtwdev->rf_mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); + rtwdev->chip->ops->write_rf(rtwdev, rf_path, addr, mask, data); - mutex_unlock(&rtwdev->rf_mutex); } static inline u32 rtw89_read32_pci_cfg(struct rtw89_dev *rtwdev, u32 addr) diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index aee0f25e036a..d461ffc6dc9e 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -4859,9 +4859,9 @@ rtw89_debug_priv_beacon_info_get(struct rtw89_dev *rtwdev, static const struct rtw89_debugfs rtw89_debugfs_templ = { .read_reg = rtw89_debug_priv_select_and_get(read_reg), .write_reg = rtw89_debug_priv_set(write_reg), - .read_rf = rtw89_debug_priv_select_and_get(read_rf), - .write_rf = rtw89_debug_priv_set(write_rf), - .rf_reg_dump = rtw89_debug_priv_get(rf_reg_dump, RSIZE_8K), + .read_rf = rtw89_debug_priv_select_and_get(read_rf, RLOCK), + .write_rf = rtw89_debug_priv_set(write_rf, WLOCK), + .rf_reg_dump = rtw89_debug_priv_get(rf_reg_dump, RSIZE_8K, RLOCK), .txpwr_table = rtw89_debug_priv_get(txpwr_table, RSIZE_20K, RLOCK), .mac_reg_dump = rtw89_debug_priv_select_and_get(mac_reg_dump, RSIZE_128K), .mac_mem_dump = rtw89_debug_priv_select_and_get(mac_mem_dump, RSIZE_16K, RLOCK), From bda294ed0ed05ada2a832b19a55dd4a6fa72b1a1 Mon Sep 17 00:00:00 2001 From: Po-Hao Huang Date: Tue, 10 Mar 2026 16:01:44 +0800 Subject: [PATCH 034/124] wifi: rtw89: Drop malformed AMPDU frames with abnormal PN Fix connection issue caused by AMPDU frames with abnormal PN patterns (out-of-order packets with correct MPDU sequence numbers but paired with abnormal PN values, which is next PN of previous in-order packet). This is causing packet drops, low throughput and disconnections. It is observed in fields with some specific AP firmwares. Do this workaround for better interoperability since some APs could never receive a proper FW update. Signed-off-by: Po-Hao Huang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260310080146.31113-12-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.c | 115 ++++++++++++++++++ drivers/net/wireless/realtek/rtw89/core.h | 11 ++ drivers/net/wireless/realtek/rtw89/mac80211.c | 3 + drivers/net/wireless/realtek/rtw89/util.h | 17 +++ drivers/net/wireless/realtek/rtw89/wow.c | 2 + drivers/net/wireless/realtek/rtw89/wow.h | 7 -- 6 files changed, 148 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 18dbf3664f0a..9d3f651798ff 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -3272,6 +3272,114 @@ out: rcu_read_unlock(); } +static void __rtw89_core_tid_rx_stats_reset(struct rtw89_tid_stats *tid_stats) +{ + tid_stats->last_pn = -1LL; + tid_stats->last_sn = IEEE80211_SN_MASK; +} + +void rtw89_core_tid_rx_stats_ctrl(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta, + struct ieee80211_ampdu_params *params, bool enable) +{ + struct rtw89_tid_stats *tid_stats; + u16 tid = params->tid; + + tid_stats = &rtwsta->tid_rx_stats[tid]; + + if (enable) { + __rtw89_core_tid_rx_stats_reset(tid_stats); + tid_stats->started = true; + } else { + tid_stats->started = false; + } +} + +void rtw89_core_tid_rx_stats_reset(struct rtw89_dev *rtwdev) +{ + struct rtw89_tid_stats *tid_stats; + struct ieee80211_sta *sta; + struct rtw89_sta *rtwsta; + u16 tid; + + for_each_station(sta, rtwdev->hw) { + rtwsta = sta_to_rtwsta(sta); + + for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) { + tid_stats = &rtwsta->tid_rx_stats[tid]; + + if (!tid_stats->started) + continue; + + __rtw89_core_tid_rx_stats_reset(tid_stats); + } + } +} + +static bool rtw89_core_skb_pn_valid(struct rtw89_dev *rtwdev, + struct rtw89_rx_desc_info *desc_info, + struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_sta_link *rtwsta_link; + struct rtw89_tid_stats *tid_stats; + struct rtw89_sta *rtwsta; + u8 tid, *ccmp_hdr_ptr; + s64 pn, last_pn; + u16 mpdu_sn; + int hdrlen; + + if (chip->chip_gen != RTW89_CHIP_AX) + return true; + + if (!ieee80211_is_data_qos(hdr->frame_control)) + return true; + + if (!desc_info->hw_dec || !desc_info->addr1_match) + return true; + + guard(rcu)(); + + rtwsta_link = rtw89_assoc_link_rcu_dereference(rtwdev, desc_info->mac_id); + if (!rtwsta_link) + return true; + + rtwsta = rtwsta_link->rtwsta; + tid = ieee80211_get_tid(hdr); + tid_stats = &rtwsta->tid_rx_stats[tid]; + + if (!tid_stats->started) + return true; + + switch (desc_info->sec_type) { + case RTW89_SEC_KEY_TYPE_CCMP128: + case RTW89_SEC_KEY_TYPE_CCMP256: + case RTW89_SEC_KEY_TYPE_GCMP128: + case RTW89_SEC_KEY_TYPE_GCMP256: + mpdu_sn = ieee80211_get_sn(hdr); + hdrlen = ieee80211_hdrlen(hdr->frame_control); + ccmp_hdr_ptr = skb->data + hdrlen; + ccmp_hdr2pn(&pn, ccmp_hdr_ptr); + last_pn = tid_stats->last_pn; + + if (pn > last_pn) { + if (ieee80211_sn_less(mpdu_sn, tid_stats->last_sn)) { + dev_kfree_skb_any(skb); + + return false; + } + + tid_stats->last_sn = mpdu_sn; + tid_stats->last_pn = pn; + } + break; + default: + break; + } + + return true; +} + 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, @@ -3421,6 +3529,7 @@ void rtw89_core_query_rxdesc(struct rtw89_dev *rtwdev, desc_info->sec_cam_id = le32_get_bits(rxd_l->dword5, AX_RXD_SEC_CAM_IDX_MASK); desc_info->mac_id = le32_get_bits(rxd_l->dword5, AX_RXD_MAC_ID_MASK); desc_info->rx_pl_id = le32_get_bits(rxd_l->dword5, AX_RXD_RX_PL_ID_MASK); + desc_info->sec_type = le32_get_bits(rxd_l->dword7, AX_RXD_SEC_TYPE_MASK); } EXPORT_SYMBOL(rtw89_core_query_rxdesc); @@ -3450,6 +3559,7 @@ void rtw89_core_query_rxdesc_v2(struct rtw89_dev *rtwdev, desc_info->mac_id = le32_get_bits(rxd_s->dword2, BE_RXD_MAC_ID_MASK); desc_info->addr_cam_valid = le32_get_bits(rxd_s->dword2, BE_RXD_ADDR_CAM_VLD); + desc_info->sec_type = le32_get_bits(rxd_s->dword3, BE_RXD_SEC_TYPE_MASK); desc_info->icv_err = le32_get_bits(rxd_s->dword3, BE_RXD_ICV_ERR); desc_info->crc32_err = le32_get_bits(rxd_s->dword3, BE_RXD_CRC32_ERR); desc_info->hw_dec = le32_get_bits(rxd_s->dword3, BE_RXD_HW_DEC); @@ -3523,6 +3633,7 @@ void rtw89_core_query_rxdesc_v3(struct rtw89_dev *rtwdev, desc_info->mac_id = le32_get_bits(rxd_s->dword2, BE_RXD_MAC_ID_V1); desc_info->addr_cam_valid = le32_get_bits(rxd_s->dword2, BE_RXD_ADDR_CAM_VLD); + desc_info->sec_type = le32_get_bits(rxd_s->dword3, BE_RXD_SEC_TYPE_MASK); desc_info->icv_err = le32_get_bits(rxd_s->dword3, BE_RXD_ICV_ERR); desc_info->crc32_err = le32_get_bits(rxd_s->dword3, BE_RXD_CRC32_ERR); desc_info->hw_dec = le32_get_bits(rxd_s->dword3, BE_RXD_HW_DEC); @@ -3802,6 +3913,10 @@ void rtw89_core_rx(struct rtw89_dev *rtwdev, memset(rx_status, 0, sizeof(*rx_status)); rtw89_core_update_rx_status(rtwdev, skb, desc_info, rx_status); rtw89_core_rx_pkt_hdl(rtwdev, skb, desc_info); + + if (!rtw89_core_skb_pn_valid(rtwdev, desc_info, skb)) + return; + if (desc_info->long_rxdesc && BIT(desc_info->frame_type) & PPDU_FILTER_BITMAP) skb_queue_tail(&ppdu_sts->rx_queue[band], skb); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index ce04ecaa3a5e..94e4faf70e12 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -1126,6 +1126,7 @@ struct rtw89_rx_desc_info { bool addr_cam_valid; u8 addr_cam_id; u8 sec_cam_id; + u8 sec_type; u8 mac_id; u16 offset; u16 rxd_len; @@ -6153,6 +6154,12 @@ struct rtw89_beacon_track_info { u32 tbtt_diff_th; }; +struct rtw89_tid_stats { + s64 last_pn; + u16 last_sn; + bool started; +}; + struct rtw89_dev { struct ieee80211_hw *hw; struct device *dev; @@ -6359,6 +6366,7 @@ struct rtw89_sta { struct sk_buff_head roc_queue; struct rtw89_ampdu_params ampdu_params[IEEE80211_NUM_TIDS]; + struct rtw89_tid_stats tid_rx_stats[IEEE80211_NUM_TIDS]; DECLARE_BITMAP(ampdu_map, IEEE80211_NUM_TIDS); DECLARE_BITMAP(pairwise_sec_cam_map, RTW89_MAX_SEC_CAM_NUM); @@ -7769,6 +7777,9 @@ int rtw89_core_sta_link_remove(struct rtw89_dev *rtwdev, void rtw89_core_set_tid_config(struct rtw89_dev *rtwdev, struct ieee80211_sta *sta, struct cfg80211_tid_config *tid_config); +void rtw89_core_tid_rx_stats_ctrl(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta, + struct ieee80211_ampdu_params *params, bool enable); +void rtw89_core_tid_rx_stats_reset(struct rtw89_dev *rtwdev); void rtw89_core_rfkill_poll(struct rtw89_dev *rtwdev, bool force); void rtw89_check_quirks(struct rtw89_dev *rtwdev, const struct dmi_system_id *quirks); int rtw89_core_init(struct rtw89_dev *rtwdev); diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index cd8e2c8de888..501c3af1da01 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -964,6 +964,7 @@ static int rtw89_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, rtw89_err(rtwdev, "failed to add key to sec cam\n"); return ret; } + rtw89_core_tid_rx_stats_reset(rtwdev); break; case DISABLE_KEY: flush_work(&rtwdev->txq_work); @@ -1018,9 +1019,11 @@ static int rtw89_ops_ampdu_action(struct ieee80211_hw *hw, rtw89_phy_ra_recalc_agg_limit(rtwdev); break; case IEEE80211_AMPDU_RX_START: + rtw89_core_tid_rx_stats_ctrl(rtwdev, rtwsta, params, true); rtw89_chip_h2c_ba_cam(rtwdev, rtwsta, true, params); break; case IEEE80211_AMPDU_RX_STOP: + rtw89_core_tid_rx_stats_ctrl(rtwdev, rtwsta, params, false); rtw89_chip_h2c_ba_cam(rtwdev, rtwsta, false, params); break; default: diff --git a/drivers/net/wireless/realtek/rtw89/util.h b/drivers/net/wireless/realtek/rtw89/util.h index bd08495301e4..c16e7a7f8bc9 100644 --- a/drivers/net/wireless/realtek/rtw89/util.h +++ b/drivers/net/wireless/realtek/rtw89/util.h @@ -6,6 +6,13 @@ #include "core.h" +#define RTW89_KEY_PN_0 GENMASK_ULL(7, 0) +#define RTW89_KEY_PN_1 GENMASK_ULL(15, 8) +#define RTW89_KEY_PN_2 GENMASK_ULL(23, 16) +#define RTW89_KEY_PN_3 GENMASK_ULL(31, 24) +#define RTW89_KEY_PN_4 GENMASK_ULL(39, 32) +#define RTW89_KEY_PN_5 GENMASK_ULL(47, 40) + #define rtw89_iterate_vifs_bh(rtwdev, iterator, data) \ ieee80211_iterate_active_interfaces_atomic((rtwdev)->hw, \ IEEE80211_IFACE_ITER_NORMAL, iterator, data) @@ -73,6 +80,16 @@ static inline void ether_addr_copy_mask(u8 *dst, const u8 *src, u8 mask) } } +static inline void ccmp_hdr2pn(s64 *pn, const u8 *hdr) +{ + *pn = u64_encode_bits(hdr[0], RTW89_KEY_PN_0) | + u64_encode_bits(hdr[1], RTW89_KEY_PN_1) | + u64_encode_bits(hdr[4], RTW89_KEY_PN_2) | + u64_encode_bits(hdr[5], RTW89_KEY_PN_3) | + u64_encode_bits(hdr[6], RTW89_KEY_PN_4) | + u64_encode_bits(hdr[7], RTW89_KEY_PN_5); +} + s32 rtw89_linear_to_db_quarter(u64 val); s32 rtw89_linear_to_db(u64 val); u64 rtw89_db_quarter_to_linear(s32 db); diff --git a/drivers/net/wireless/realtek/rtw89/wow.c b/drivers/net/wireless/realtek/rtw89/wow.c index 368e08826f1e..8dadd8df4fc6 100644 --- a/drivers/net/wireless/realtek/rtw89/wow.c +++ b/drivers/net/wireless/realtek/rtw89/wow.c @@ -1741,6 +1741,8 @@ static int rtw89_wow_disable(struct rtw89_dev *rtwdev) rtw89_wow_leave_ps(rtwdev, false); + rtw89_core_tid_rx_stats_reset(rtwdev); + ret = rtw89_wow_fw_stop(rtwdev); if (ret) { rtw89_err(rtwdev, "wow: failed to swap to normal fw\n"); diff --git a/drivers/net/wireless/realtek/rtw89/wow.h b/drivers/net/wireless/realtek/rtw89/wow.h index 71e07f482174..d7e67632efeb 100644 --- a/drivers/net/wireless/realtek/rtw89/wow.h +++ b/drivers/net/wireless/realtek/rtw89/wow.h @@ -8,13 +8,6 @@ #define RTW89_KEY_TKIP_PN_IV16 GENMASK_ULL(15, 0) #define RTW89_KEY_TKIP_PN_IV32 GENMASK_ULL(47, 16) -#define RTW89_KEY_PN_0 GENMASK_ULL(7, 0) -#define RTW89_KEY_PN_1 GENMASK_ULL(15, 8) -#define RTW89_KEY_PN_2 GENMASK_ULL(23, 16) -#define RTW89_KEY_PN_3 GENMASK_ULL(31, 24) -#define RTW89_KEY_PN_4 GENMASK_ULL(39, 32) -#define RTW89_KEY_PN_5 GENMASK_ULL(47, 40) - #define RTW89_IGTK_IPN_0 GENMASK_ULL(7, 0) #define RTW89_IGTK_IPN_1 GENMASK_ULL(15, 8) #define RTW89_IGTK_IPN_2 GENMASK_ULL(23, 16) From 45ba9226b1081fe3292c1087de9db6d592c38de8 Mon Sep 17 00:00:00 2001 From: Po-Hao Huang Date: Tue, 10 Mar 2026 16:01:45 +0800 Subject: [PATCH 035/124] wifi: rtw89: Recalculate station aggregates when AMSDU length changes for MLO links Currently, AMSDU length is updated per-link for MLO but not propagated to the station aggregates, causing suboptimal TX throughput. This change ensures station aggregates are recalculated when any link's AMSDU length changes. Signed-off-by: Po-Hao Huang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260310080146.31113-13-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 0fa4d8d791f1..3a241738ac06 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -3213,7 +3213,8 @@ struct rtw89_phy_iter_ra_data { static void __rtw89_phy_c2h_ra_rpt_iter(struct rtw89_sta_link *rtwsta_link, struct ieee80211_link_sta *link_sta, - struct rtw89_phy_iter_ra_data *ra_data) + struct rtw89_phy_iter_ra_data *ra_data, + bool *changed) { struct rtw89_dev *rtwdev = ra_data->rtwdev; const struct rtw89_c2h_ra_rpt *c2h = @@ -3222,7 +3223,7 @@ static void __rtw89_phy_c2h_ra_rpt_iter(struct rtw89_sta_link *rtwsta_link, const struct rtw89_chip_info *chip = rtwdev->chip; bool format_v1 = chip->chip_gen == RTW89_CHIP_BE; u8 mode, rate, bw, giltf, mac_id; - u16 legacy_bitrate; + u16 legacy_bitrate, amsdu_len; bool valid; u8 mcs = 0; u8 t; @@ -3319,7 +3320,13 @@ static void __rtw89_phy_c2h_ra_rpt_iter(struct rtw89_sta_link *rtwsta_link, u16_encode_bits(mode, RTW89_HW_RATE_MASK_MOD) | u16_encode_bits(rate, RTW89_HW_RATE_MASK_VAL); ra_report->might_fallback_legacy = mcs <= 2; - link_sta->agg.max_rc_amsdu_len = get_max_amsdu_len(rtwdev, ra_report); + + amsdu_len = get_max_amsdu_len(rtwdev, ra_report); + if (link_sta->agg.max_rc_amsdu_len != amsdu_len) { + link_sta->agg.max_rc_amsdu_len = amsdu_len; + *changed = true; + } + rtwsta_link->max_agg_wait = link_sta->agg.max_rc_amsdu_len / 1500 - 1; } @@ -3330,14 +3337,18 @@ static void rtw89_phy_c2h_ra_rpt_iter(void *data, struct ieee80211_sta *sta) struct rtw89_sta_link *rtwsta_link; struct ieee80211_link_sta *link_sta; unsigned int link_id; + bool changed = false; rcu_read_lock(); rtw89_sta_for_each_link(rtwsta, rtwsta_link, link_id) { link_sta = rtw89_sta_rcu_dereference_link(rtwsta_link, false); - __rtw89_phy_c2h_ra_rpt_iter(rtwsta_link, link_sta, ra_data); + __rtw89_phy_c2h_ra_rpt_iter(rtwsta_link, link_sta, ra_data, &changed); } + if (changed) + ieee80211_sta_recalc_aggregates(sta); + rcu_read_unlock(); } From a1488456f70655f7a95f846c8fbadbe61daf292c Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Tue, 10 Mar 2026 16:01:46 +0800 Subject: [PATCH 036/124] wifi: rtw89: debug: simulate Wi-Fi 7 SER L0/L1 without PS mode Current triggers of Wi-Fi 7 SER (system error recovery) L0/L1 simulation don't yet guarantee working with PS mode. So, leave PS mode first before triggering them for now. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260310080146.31113-14-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/debug.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index d461ffc6dc9e..82849d109cc3 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -3552,6 +3552,8 @@ static int rtw89_dbg_trigger_l1_error_by_halt_h2c_be(struct rtw89_dev *rtwdev) if (!test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags)) return -EBUSY; + rtw89_leave_ps_mode(rtwdev); + rtw89_write32_set(rtwdev, R_BE_FW_TRIGGER_IDCT_ISR, B_BE_DMAC_FW_TRIG_IDCT | B_BE_DMAC_FW_ERR_IDCT_IMR); @@ -3654,6 +3656,8 @@ static int rtw89_dbg_trigger_l0_error_by_halt_h2c_be(struct rtw89_dev *rtwdev) if (!test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags)) return -EBUSY; + rtw89_leave_ps_mode(rtwdev); + rtw89_write32_set(rtwdev, R_BE_CMAC_FW_TRIGGER_IDCT_ISR, B_BE_CMAC_FW_TRIG_IDCT | B_BE_CMAC_FW_ERR_IDCT_IMR); From eef6d4449e8a540fde792968a26d8aa514af8089 Mon Sep 17 00:00:00 2001 From: Shin-Yi Lin Date: Thu, 12 Mar 2026 13:57:24 +0800 Subject: [PATCH 037/124] wifi: rtw89: usb: Rx aggregation for RTL8832CU/RTL8851BU USB RX Aggregation is a performance optimization technique used in USB network devices to increase throughput. Instead of sending every received network packet to the host computer individually, the device hardware groups multiple smaller packets into a single, large USB Bulk Transfer. * toAP/toNB use iperf3 respectively. With Cisco BE6000 - iperf3 tcp 10 pair (to another NB) [6G 160Mhz]: RTL8832CU-USB3.0 before after TX 941 941 RX 847 919 RTL8832CU-USB2.0 before after TX 293 286 RX 342 356 [5G 80Mhz]: RTL8832CU-USB3.0 before after TX 864 877 RX 864 902 RTL8832CU-USB2.0 before after TX 279 271 RX 327 349 RTL8851BU before after TX 115 114 RX 295 306 Signed-off-by: Shin-Yi Lin Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260312055724.12177-1-pkshih@realtek.com --- .../net/wireless/realtek/rtw89/rtw8851bu.c | 1 + .../net/wireless/realtek/rtw89/rtw8852au.c | 1 + .../net/wireless/realtek/rtw89/rtw8852bu.c | 1 + .../net/wireless/realtek/rtw89/rtw8852cu.c | 1 + drivers/net/wireless/realtek/rtw89/usb.c | 84 ++++++++++++++++--- drivers/net/wireless/realtek/rtw89/usb.h | 12 +++ 6 files changed, 87 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851bu.c b/drivers/net/wireless/realtek/rtw89/rtw8851bu.c index 959d62aefdd8..6a8d31544314 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851bu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851bu.c @@ -15,6 +15,7 @@ static const struct rtw89_usb_info rtw8851b_usb_info = { .usb3_mac_npi_config_intf_0 = R_AX_USB3_MAC_NPI_CONFIG_INTF_0, .usb_endpoint_0 = R_AX_USB_ENDPOINT_0, .usb_endpoint_2 = R_AX_USB_ENDPOINT_2, + .rx_agg_alignment = 8, .bulkout_id = { [RTW89_DMA_ACH0] = 3, [RTW89_DMA_ACH1] = 4, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852au.c b/drivers/net/wireless/realtek/rtw89/rtw8852au.c index ccdbcc178c2a..4cced4619b7d 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852au.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852au.c @@ -15,6 +15,7 @@ static const struct rtw89_usb_info rtw8852a_usb_info = { .usb3_mac_npi_config_intf_0 = R_AX_USB3_MAC_NPI_CONFIG_INTF_0, .usb_endpoint_0 = R_AX_USB_ENDPOINT_0, .usb_endpoint_2 = R_AX_USB_ENDPOINT_2, + .rx_agg_alignment = 8, .bulkout_id = { [RTW89_DMA_ACH0] = 3, [RTW89_DMA_ACH2] = 5, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bu.c b/drivers/net/wireless/realtek/rtw89/rtw8852bu.c index 84cd3ec971f9..37111fed276f 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bu.c @@ -15,6 +15,7 @@ static const struct rtw89_usb_info rtw8852b_usb_info = { .usb3_mac_npi_config_intf_0 = R_AX_USB3_MAC_NPI_CONFIG_INTF_0, .usb_endpoint_0 = R_AX_USB_ENDPOINT_0, .usb_endpoint_2 = R_AX_USB_ENDPOINT_2, + .rx_agg_alignment = 8, .bulkout_id = { [RTW89_DMA_ACH0] = 3, [RTW89_DMA_ACH1] = 4, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c index 3b9825c92a0d..0c5aebaed873 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c @@ -15,6 +15,7 @@ static const struct rtw89_usb_info rtw8852c_usb_info = { .usb3_mac_npi_config_intf_0 = R_AX_USB3_MAC_NPI_CONFIG_INTF_0_V1, .usb_endpoint_0 = R_AX_USB_ENDPOINT_0_V1, .usb_endpoint_2 = R_AX_USB_ENDPOINT_2_V1, + .rx_agg_alignment = 8, .bulkout_id = { [RTW89_DMA_ACH0] = 3, [RTW89_DMA_ACH2] = 5, diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index 64f0c0ec5ed4..581b8c05f930 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -408,11 +408,14 @@ static int rtw89_usb_ops_tx_write(struct rtw89_dev *rtwdev, static void rtw89_usb_rx_handler(struct work_struct *work) { struct rtw89_usb *rtwusb = container_of(work, struct rtw89_usb, rx_work); + const struct rtw89_usb_info *info = rtwusb->info; struct rtw89_dev *rtwdev = rtwusb->rtwdev; struct rtw89_rx_desc_info desc_info; + s32 aligned_offset, remaining; struct sk_buff *rx_skb; struct sk_buff *skb; u32 pkt_offset; + u8 *pkt_ptr; int limit; for (limit = 0; limit < 200; limit++) { @@ -425,23 +428,38 @@ static void rtw89_usb_rx_handler(struct work_struct *work) goto free_or_reuse; } - memset(&desc_info, 0, sizeof(desc_info)); - rtw89_chip_query_rxdesc(rtwdev, &desc_info, rx_skb->data, 0); + pkt_ptr = rx_skb->data; + remaining = rx_skb->len; - skb = rtw89_alloc_skb_for_rx(rtwdev, desc_info.pkt_size); - if (!skb) { - rtw89_debug(rtwdev, RTW89_DBG_HCI, - "failed to allocate RX skb of size %u\n", - desc_info.pkt_size); - goto free_or_reuse; - } + do { + memset(&desc_info, 0, sizeof(desc_info)); + rtw89_chip_query_rxdesc(rtwdev, &desc_info, pkt_ptr, 0); - pkt_offset = desc_info.offset + desc_info.rxd_len; + pkt_offset = desc_info.offset + desc_info.rxd_len; + if (remaining < (pkt_offset + desc_info.pkt_size)) { + rtw89_debug(rtwdev, RTW89_DBG_HCI, + "Failed to get remaining RX pkt %u > %u\n", + pkt_offset + desc_info.pkt_size, remaining); + goto free_or_reuse; + } - skb_put_data(skb, rx_skb->data + pkt_offset, - desc_info.pkt_size); + skb = rtw89_alloc_skb_for_rx(rtwdev, desc_info.pkt_size); + if (!skb) { + rtw89_debug(rtwdev, RTW89_DBG_HCI, + "failed to allocate RX skb of size %u\n", + desc_info.pkt_size); + goto free_or_reuse; + } - rtw89_core_rx(rtwdev, &desc_info, skb); + skb_put_data(skb, pkt_ptr + pkt_offset, desc_info.pkt_size); + rtw89_core_rx(rtwdev, &desc_info, skb); + + /* next frame */ + pkt_offset += desc_info.pkt_size; + aligned_offset = ALIGN(pkt_offset, info->rx_agg_alignment); + pkt_ptr += aligned_offset; + remaining -= aligned_offset; + } while (remaining > 0); free_or_reuse: if (skb_queue_len(&rtwusb->rx_free_queue) >= RTW89_USB_RX_SKB_NUM) @@ -745,6 +763,44 @@ static int rtw89_usb_ops_mac_pre_deinit(struct rtw89_dev *rtwdev) return 0; /* Nothing to do. */ } +static void rtw89_usb_rx_agg_cfg_v1(struct rtw89_dev *rtwdev) +{ + const u32 rxagg_0 = FIELD_PREP_CONST(B_AX_RXAGG_0_EN, 1) | + FIELD_PREP_CONST(B_AX_RXAGG_0_NUM_TH, 0) | + FIELD_PREP_CONST(B_AX_RXAGG_0_TIME_32US_TH, 32) | + FIELD_PREP_CONST(B_AX_RXAGG_0_BUF_SZ_4K, 5); + + rtw89_write32(rtwdev, R_AX_RXAGG_0, rxagg_0); +} + +static void rtw89_usb_rx_agg_cfg_v2(struct rtw89_dev *rtwdev) +{ + const u32 rxagg_0 = FIELD_PREP_CONST(B_AX_RXAGG_0_EN, 1) | + FIELD_PREP_CONST(B_AX_RXAGG_0_NUM_TH, 255) | + FIELD_PREP_CONST(B_AX_RXAGG_0_TIME_32US_TH, 32) | + FIELD_PREP_CONST(B_AX_RXAGG_0_BUF_SZ_1K, 20); + + rtw89_write32(rtwdev, R_AX_RXAGG_0_V1, rxagg_0); + rtw89_write32(rtwdev, R_AX_RXAGG_1_V1, 0x1F); +} + +static void rtw89_usb_rx_agg_cfg(struct rtw89_dev *rtwdev) +{ + switch (rtwdev->chip->chip_id) { + case RTL8851B: + case RTL8852A: + case RTL8852B: + rtw89_usb_rx_agg_cfg_v1(rtwdev); + break; + case RTL8852C: + rtw89_usb_rx_agg_cfg_v2(rtwdev); + break; + default: + rtw89_warn(rtwdev, "%s: USB RX agg not support\n", __func__); + return; + } +} + static int rtw89_usb_ops_mac_post_init(struct rtw89_dev *rtwdev) { struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); @@ -773,6 +829,8 @@ static int rtw89_usb_ops_mac_post_init(struct rtw89_dev *rtwdev) rtw89_write8(rtwdev, info->usb_endpoint_2 + 1, NUMP); } + rtw89_usb_rx_agg_cfg(rtwdev); + return 0; } diff --git a/drivers/net/wireless/realtek/rtw89/usb.h b/drivers/net/wireless/realtek/rtw89/usb.h index 203ec8e993e9..3d17e514e346 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.h +++ b/drivers/net/wireless/realtek/rtw89/usb.h @@ -20,6 +20,17 @@ #define RTW89_MAX_ENDPOINT_NUM 9 #define RTW89_MAX_BULKOUT_NUM 7 +#define R_AX_RXAGG_0_V1 0x6000 +#define B_AX_RXAGG_0_EN BIT(31) +#define B_AX_RXAGG_0_NUM_TH GENMASK(23, 16) +#define B_AX_RXAGG_0_TIME_32US_TH GENMASK(15, 8) +#define B_AX_RXAGG_0_BUF_SZ_1K GENMASK(7, 0) + +#define R_AX_RXAGG_1_V1 0x6004 + +#define R_AX_RXAGG_0 0x8900 +#define B_AX_RXAGG_0_BUF_SZ_4K GENMASK(7, 0) + struct rtw89_usb_info { u32 usb_host_request_2; u32 usb_wlan0_1; @@ -27,6 +38,7 @@ struct rtw89_usb_info { u32 usb3_mac_npi_config_intf_0; u32 usb_endpoint_0; u32 usb_endpoint_2; + u8 rx_agg_alignment; u8 bulkout_id[RTW89_DMA_CH_NUM]; }; From b2bf9d61e14af4129362aeb9c10034229a6d8f08 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 16 Mar 2026 11:56:35 +0800 Subject: [PATCH 038/124] wifi: rtw88: add quirks to disable PCI ASPM and deep LPS for HP P3S95EA#ACB On an HP laptop (P3S95EA#ACB) equipped with a Realtek RTL8821CE 802.11ac PCIe adapter (PCI ID: 10ec:c821), the system experiences a hard lockup (complete freeze of the UI and kernel, sysrq doesn't work, requires holding the power button) when the WiFi adapter enters the power saving state. Disable PCI ASPM to avoid system freeze. In addition, driver throws messages periodically. Though this doesn't always cause unstable connection, missing H2C commands might cause unpredictable results. Disable deep LPS to avoid this as well. rtw88_8821ce 0000:13:00.0: firmware failed to leave lps state rtw88_8821ce 0000:13:00.0: failed to send h2c command rtw88_8821ce 0000:13:00.0: failed to send h2c command Tested on HP Notebook P3S95EA#ACB (kernel 6.19.7-1-cachyos): - No hard freeze observed during idle or active usage. - Zero h2c or lps errors in dmesg across idle (10 min), load stress (100MB download), and suspend/resume cycle. - Both quirk flags confirmed active via sysfs without any manual modprobe parameters. Reported-by: Oleksandr Havrylov Closes: https://lore.kernel.org/linux-wireless/CALdGYqSQ1Ko2TTBhUizMu_FvLMUAuQfFrVwS10n_C-LSQJQQkQ@mail.gmail.com/ Signed-off-by: Ping-Ke Shih Tested-by: Oleksandr Havrylov Link: https://patch.msgid.link/20260316035635.16550-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw88/main.h | 5 ++++ drivers/net/wireless/realtek/rtw88/pci.c | 31 +++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h index 1179231a672d..9c0b746540b0 100644 --- a/drivers/net/wireless/realtek/rtw88/main.h +++ b/drivers/net/wireless/realtek/rtw88/main.h @@ -432,6 +432,11 @@ enum rtw_wow_flags { RTW_WOW_FLAG_MAX, }; +enum rtw_quirk_dis_caps { + QUIRK_DIS_CAP_PCI_ASPM, + QUIRK_DIS_CAP_LPS_DEEP, +}; + /* the power index is represented by differences, which cck-1s & ht40-1s are * the base values, so for 1s's differences, there are only ht20 & ofdm */ diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c index ec0a45bfb670..bba370ad510c 100644 --- a/drivers/net/wireless/realtek/rtw88/pci.c +++ b/drivers/net/wireless/realtek/rtw88/pci.c @@ -2,6 +2,7 @@ /* Copyright(c) 2018-2019 Realtek Corporation */ +#include #include #include #include "main.h" @@ -1744,6 +1745,34 @@ const struct pci_error_handlers rtw_pci_err_handler = { }; EXPORT_SYMBOL(rtw_pci_err_handler); +static int rtw_pci_disable_caps(const struct dmi_system_id *dmi) +{ + uintptr_t dis_caps = (uintptr_t)dmi->driver_data; + + if (dis_caps & BIT(QUIRK_DIS_CAP_PCI_ASPM)) + rtw_pci_disable_aspm = true; + + if (dis_caps & BIT(QUIRK_DIS_CAP_LPS_DEEP)) + rtw_disable_lps_deep_mode = true; + + return 1; +} + +static const struct dmi_system_id rtw_pci_quirks[] = { + { + .callback = rtw_pci_disable_caps, + .ident = "HP Notebook - P3S95EA#ACB", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HP"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Notebook"), + DMI_MATCH(DMI_PRODUCT_SKU, "P3S95EA#ACB"), + }, + .driver_data = (void *)(BIT(QUIRK_DIS_CAP_PCI_ASPM) | + BIT(QUIRK_DIS_CAP_LPS_DEEP)), + }, + {} +}; + int rtw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -1771,6 +1800,8 @@ int rtw_pci_probe(struct pci_dev *pdev, rtwpci = (struct rtw_pci *)rtwdev->priv; atomic_set(&rtwpci->link_usage, 1); + dmi_check_system(rtw_pci_quirks); + ret = rtw_core_init(rtwdev); if (ret) goto err_release_hw; From 737e980e12983bb7420a2c00b981a1e607079a84 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Wed, 18 Mar 2026 19:45:13 +0200 Subject: [PATCH 039/124] wifi: rtw88: TX QOS Null data the same way as Null data When filling out the TX descriptor, Null data frames are treated like management frames, but QOS Null data frames are treated like normal data frames. Somehow this causes a problem for the firmware. When connected to a network in the 2.4 GHz band, wpa_supplicant (or NetworkManager?) triggers a scan every five minutes. During these scans mac80211 transmits many QOS Null frames in quick succession. Because these frames are marked with IEEE80211_TX_CTL_REQ_TX_STATUS, rtw88 asks the firmware to report the TX ACK status for each of these frames. Sometimes the firmware can't process the TX status requests quickly enough, they add up, it only processes some of them, and then marks every subsequent TX status report with the wrong number. The symptom is that after a while the warning "failed to get tx report from firmware" appears every five minutes. This problem apparently happens only with the older RTL8723D, RTL8821A, RTL8812A, and probably RTL8703B chips. Treat QOS Null data frames the same way as Null data frames. This seems to avoid the problem. Tested with RTL8821AU, RTL8723DU, RTL8811CU, and RTL8812BU. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/2b53fb0d-b1ed-47b6-8caa-2bb9ae2acb80@gmail.com --- drivers/net/wireless/realtek/rtw88/tx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw88/tx.c b/drivers/net/wireless/realtek/rtw88/tx.c index 2ab440cb2d67..3106edb84fb4 100644 --- a/drivers/net/wireless/realtek/rtw88/tx.c +++ b/drivers/net/wireless/realtek/rtw88/tx.c @@ -421,7 +421,7 @@ void rtw_tx_pkt_info_update(struct rtw_dev *rtwdev, pkt_info->mac_id = rtwvif->mac_id; } - if (ieee80211_is_mgmt(fc) || ieee80211_is_nullfunc(fc)) + if (ieee80211_is_mgmt(fc) || ieee80211_is_any_nullfunc(fc)) rtw_tx_mgmt_pkt_info_update(rtwdev, pkt_info, sta, skb); else if (ieee80211_is_data(fc)) rtw_tx_data_pkt_info_update(rtwdev, pkt_info, sta, skb); From d92f6ad6483e6d430c8273eeb7be97ce85244bd5 Mon Sep 17 00:00:00 2001 From: Christian Hewitt Date: Tue, 17 Mar 2026 11:21:55 +0000 Subject: [PATCH 040/124] wifi: rtw89: retry efuse physical map dump on transient failure On Radxa Rock 5B with a RTL8852BE combo WiFi/BT card, the efuse physical map dump intermittently fails with -EBUSY during probe. The failure occurs in rtw89_dump_physical_efuse_map_ddv() where read_poll_timeout_atomic() times out waiting for the B_AX_EF_RDY bit after 1 second. The root cause is a timing race during boot: the WiFi driver's chip initialization (firmware download via PCIe) overlaps with Bluetooth firmware download to the same combo chip via USB. This can leave the efuse controller temporarily unavailable when the WiFi driver attempts to read the efuse map. The firmware download path retries up to 5 times, but the efuse read that follows has no similar logic. Address this by adding retry loop logic (also up to 5 attempts) around physical efuse map dump. Signed-off-by: Christian Hewitt Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260317112155.1939569-1-christianshewitt@gmail.com --- drivers/net/wireless/realtek/rtw89/efuse.c | 23 ++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/efuse.c b/drivers/net/wireless/realtek/rtw89/efuse.c index a2757a88d55d..89d4b1b865f8 100644 --- a/drivers/net/wireless/realtek/rtw89/efuse.c +++ b/drivers/net/wireless/realtek/rtw89/efuse.c @@ -185,8 +185,8 @@ static int rtw89_dump_physical_efuse_map_dav(struct rtw89_dev *rtwdev, u8 *map, return 0; } -static int rtw89_dump_physical_efuse_map(struct rtw89_dev *rtwdev, u8 *map, - u32 dump_addr, u32 dump_size, bool dav) +static int __rtw89_dump_physical_efuse_map(struct rtw89_dev *rtwdev, u8 *map, + u32 dump_addr, u32 dump_size, bool dav) { int ret; @@ -208,6 +208,25 @@ static int rtw89_dump_physical_efuse_map(struct rtw89_dev *rtwdev, u8 *map, return 0; } +static int rtw89_dump_physical_efuse_map(struct rtw89_dev *rtwdev, u8 *map, + u32 dump_addr, u32 dump_size, bool dav) +{ + int retry; + int ret; + + for (retry = 0; retry < 5; retry++) { + ret = __rtw89_dump_physical_efuse_map(rtwdev, map, dump_addr, + dump_size, dav); + if (!ret) + return 0; + + rtw89_warn(rtwdev, "efuse dump (dav=%d) failed, retrying (%d)\n", + dav, retry); + } + + return ret; +} + #define invalid_efuse_header(hdr1, hdr2) \ ((hdr1) == 0xff || (hdr2) == 0xff) #define invalid_efuse_content(word_en, i) \ From 6678828eb78f3ae0bc6db90436068d5fd0387703 Mon Sep 17 00:00:00 2001 From: Zenm Chen Date: Fri, 20 Mar 2026 17:31:22 +0800 Subject: [PATCH 041/124] wifi: rtw89: Add support for TP-Link Archer TX50U Add the ID 37ad:0103 to the table to support an additional RTL8832CU adapter: TP-Link Archer TX50U. Link: https://github.com/morrownr/rtl8852cu-20251113/issues/2 Signed-off-by: Zenm Chen Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260320093122.6754-1-zenmchen@gmail.com --- drivers/net/wireless/realtek/rtw89/rtw8852cu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c index 0c5aebaed873..4755c483e1e6 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c @@ -55,6 +55,8 @@ static const struct usb_device_id rtw_8852cu_id_table[] = { .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, { USB_DEVICE_AND_INTERFACE_INFO(0x35bc, 0x0102, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x37ad, 0x0103, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, {}, }; MODULE_DEVICE_TABLE(usb, rtw_8852cu_id_table); From aefb20749074731c4f35444761e730991f1b8c77 Mon Sep 17 00:00:00 2001 From: Zenm Chen Date: Fri, 20 Mar 2026 23:41:36 +0800 Subject: [PATCH 042/124] wifi: rtw89: Add support for Buffalo WI-U3-2400XE2 Add the ID 0411:03a6 to the table to support an additional RTL8832CU adapter: Buffalo WI-U3-2400XE2. Link: https://github.com/morrownr/rtw89/commit/506d193b8cb7d6394509aebcf8de1531629f6100 Signed-off-by: Zenm Chen Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260320154136.5750-1-zenmchen@gmail.com --- drivers/net/wireless/realtek/rtw89/rtw8852cu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c index 4755c483e1e6..de3f8358bbe7 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c @@ -39,6 +39,8 @@ static const struct rtw89_driver_info rtw89_8852cu_info = { }; static const struct usb_device_id rtw_8852cu_id_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(0x0411, 0x03a6, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, { USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0xc832, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, { USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0xc85a, 0xff, 0xff, 0xff), From 047cddf88c611e616d49a00311d4722e46286234 Mon Sep 17 00:00:00 2001 From: Alexey Velichayshiy Date: Mon, 23 Mar 2026 17:05:53 +0300 Subject: [PATCH 043/124] wifi: rtw89: phy: fix uninitialized variable access in rtw89_phy_cfo_set_crystal_cap() In the rtw89_phy_cfo_set_crystal_cap() function, for chips other than RTL8852A/RTL8851B, the values read by rtw89_mac_read_xtal_si() are stored into the local variables sc_xi_val and sc_xo_val. If either read fails, these variables remain uninitialized, they are later used to update cfo->crystal_cap and in debug print statements. This can lead to undefined behavior. Fix the issue by initializing sc_xi_val and sc_xo_val to zero, like is implemented in vendor driver. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 8379fa611536 ("rtw89: 8852c: add write/read crystal function in CFO tracking") Signed-off-by: Alexey Velichayshiy Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260323140613.1615574-1-a.velichayshiy@ispras.ru --- 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 3a241738ac06..d205561f1f5c 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -4898,7 +4898,7 @@ static void rtw89_phy_cfo_set_crystal_cap(struct rtw89_dev *rtwdev, { struct rtw89_cfo_tracking_info *cfo = &rtwdev->cfo_tracking; const struct rtw89_chip_info *chip = rtwdev->chip; - u8 sc_xi_val, sc_xo_val; + u8 sc_xi_val = 0, sc_xo_val = 0; if (!force && cfo->crystal_cap == crystal_cap) return; From bf14367719fa86f7c6922c64d37a2df347954c66 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 24 Mar 2026 09:10:01 +0800 Subject: [PATCH 044/124] wifi: rtw88: validate RX rate to prevent out-of-bound The reported RX rate might be unexpected, causing kernel warns: Rate marked as a VHT rate but data is invalid: MCS: 0, NSS: 0 WARNING: net/mac80211/rx.c:5491 at ieee80211_rx_list+0x183/0x1020 [mac80211] As the RX rate can be index of an array under certain conditions, validate it to prevent accessing array out-of-bound potentially. Tested on HP Notebook P3S95EA#ACB (kernel 6.19.9-1-cachyos): - No WARNING: net/mac80211/rx.c:5491 observed after the v2 patch. The unexpected `NSS: 0, MCS: 0` VHT rate warnings are successfully mitigated. - The system remains fully stable through prolonged idle periods, high network load, active Bluetooth A2DP usage, and multiple deep suspend/resume cycles. - Zero h2c timeouts or firmware lps state errors observed in dmesg. Reported-by: Oleksandr Havrylov Closes: https://lore.kernel.org/linux-wireless/CALdGYqSMUPnPfW-_q1RgYr0_SjoXUejAaJJr-o+jpwCk1S7ndQ@mail.gmail.com/ Tested-by: Oleksandr Havrylov Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260324011001.5742-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw88/rx.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw88/rx.c b/drivers/net/wireless/realtek/rtw88/rx.c index 8b0afaaffaa0..d9e11343d498 100644 --- a/drivers/net/wireless/realtek/rtw88/rx.c +++ b/drivers/net/wireless/realtek/rtw88/rx.c @@ -295,6 +295,14 @@ void rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8, pkt_stat->tsf_low = le32_get_bits(rx_desc->w5, RTW_RX_DESC_W5_TSFL); + if (unlikely(pkt_stat->rate >= DESC_RATE_MAX)) { + rtw_dbg(rtwdev, RTW_DBG_UNEXP, + "unexpected RX rate=0x%x\n", pkt_stat->rate); + + pkt_stat->rate = DESC_RATE1M; + pkt_stat->bw = RTW_CHANNEL_WIDTH_20; + } + /* drv_info_sz is in unit of 8-bytes */ pkt_stat->drv_info_sz *= 8; From e51df91b894a583f03e35168d2082786f6dff1bd Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 24 Mar 2026 14:20:43 +0800 Subject: [PATCH 045/124] wifi: rtw89: 8922d: add definition of quota, registers and efuse block The quota is used to configure memory size for TX/RX, and the definition of registers includes H2C command, C2H event, WoWLAN reason, IMR of CMAC and DMAC, ACK rate selector, RF kill GPIO, and BB functions of dynamic initial gain and EDCCA. The layout of efuse block is to define logic map of efuse, such as MAC address and RF calibration values. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260324062049.52266-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/reg.h | 30 +++ drivers/net/wireless/realtek/rtw89/rtw8922d.c | 238 ++++++++++++++++++ drivers/net/wireless/realtek/rtw89/rtw8922d.h | 10 + 3 files changed, 278 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8922d.c create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8922d.h diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 9b605617c3f0..2cb35458a822 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -8737,6 +8737,7 @@ #define R_PHY_STS_BITMAP_EHT 0x0788 #define R_PHY_STS_BITMAP_EHT_BE4 0x20788 #define R_EDCCA_RPTREG_SEL_BE 0x078C +#define R_EDCCA_RPTREG_SEL_BE4 0x2078C #define B_EDCCA_RPTREG_SEL_BE_MSK GENMASK(22, 20) #define R_PMAC_GNT 0x0980 #define B_PMAC_GNT_TXEN BIT(0) @@ -8850,6 +8851,7 @@ #define R_UDP_COEEF 0x0CBC #define B_UDP_COEEF BIT(19) #define R_TX_COLLISION_T2R_ST_BE 0x0CC8 +#define R_TX_COLLISION_T2R_ST_BE4 0x20CC8 #define B_TX_COLLISION_T2R_ST_BE_M GENMASK(13, 8) #define R_RXHT_MCS_LIMIT 0x0D18 #define B_RXHT_MCS_LIMIT GENMASK(9, 8) @@ -9078,7 +9080,11 @@ #define R_P1_EN_SOUND_WO_NDP 0x2D7C #define B_P1_EN_SOUND_WO_NDP BIT(1) #define R_EDCCA_RPT_A_BE 0x2E38 +#define R_EDCCA_RPT_A_BE4 0x2EE30 +#define R_EDCCA_RPT_A_BE4_C1 0x2FE30 #define R_EDCCA_RPT_B_BE 0x2E3C +#define R_EDCCA_RPT_B_BE4 0x2EE34 +#define R_EDCCA_RPT_B_BE4_C1 0x2FE34 #define R_EDCCA_RPT_P1_A_BE 0x2E40 #define R_EDCCA_RPT_P1_B_BE 0x2E44 #define R_S1_HW_SI_DIS 0x3200 @@ -9262,11 +9268,13 @@ #define R_PATH0_P20_FOLLOW_BY_PAGCUGC_V1 0x4C24 #define R_PATH0_P20_FOLLOW_BY_PAGCUGC_V2 0x46E8 #define R_PATH0_P20_FOLLOW_BY_PAGCUGC_V3 0x41C8 +#define R_PATH0_P20_FOLLOW_BY_PAGCUGC_BE4 0x241C8 #define B_PATH0_P20_FOLLOW_BY_PAGCUGC_EN_MSK BIT(5) #define R_PATH0_S20_FOLLOW_BY_PAGCUGC 0x46A4 #define R_PATH0_S20_FOLLOW_BY_PAGCUGC_V1 0x4C28 #define R_PATH0_S20_FOLLOW_BY_PAGCUGC_V2 0x46EC #define R_PATH0_S20_FOLLOW_BY_PAGCUGC_V3 0x41CC +#define R_PATH0_S20_FOLLOW_BY_PAGCUGC_BE4 0x241CC #define B_PATH0_S20_FOLLOW_BY_PAGCUGC_EN_MSK BIT(5) #define R_PATH0_RXB_INIT_V1 0x46A8 #define B_PATH0_RXB_INIT_IDX_MSK_V1 GENMASK(14, 10) @@ -9313,11 +9321,13 @@ #define R_PATH1_P20_FOLLOW_BY_PAGCUGC_V1 0x4CE8 #define R_PATH1_P20_FOLLOW_BY_PAGCUGC_V2 0x47A8 #define R_PATH1_P20_FOLLOW_BY_PAGCUGC_V3 0x45C8 +#define R_PATH1_P20_FOLLOW_BY_PAGCUGC_BE4 0x245C8 #define B_PATH1_P20_FOLLOW_BY_PAGCUGC_EN_MSK BIT(5) #define R_PATH1_S20_FOLLOW_BY_PAGCUGC 0x4778 #define R_PATH1_S20_FOLLOW_BY_PAGCUGC_V1 0x4CEC #define R_PATH1_S20_FOLLOW_BY_PAGCUGC_V2 0x47AC #define R_PATH1_S20_FOLLOW_BY_PAGCUGC_V3 0x45CC +#define R_PATH1_S20_FOLLOW_BY_PAGCUGC_BE4 0x245CC #define B_PATH1_S20_FOLLOW_BY_PAGCUGC_EN_MSK BIT(5) #define R_PATH1_G_TIA0_LNA6_OP1DB_V1 0x4778 #define B_PATH1_G_TIA0_LNA6_OP1DB_V1 GENMASK(7, 0) @@ -9338,6 +9348,7 @@ #define R_SEG0R_PD 0x481C #define R_SEG0R_PD_V1 0x4860 #define R_SEG0R_PD_V2 0x6A74 +#define R_SEG0R_PD_BE4 0x26210 #define R_SEG0R_EDCCA_LVL 0x4840 #define R_SEG0R_EDCCA_LVL_V1 0x4884 #define B_EDCCA_LVL_MSK3 GENMASK(31, 24) @@ -9476,9 +9487,11 @@ #define B_DCFO_COMP_S0_V1_MSK GENMASK(13, 0) #define R_BMODE_PDTH_V1 0x4B64 #define R_BMODE_PDTH_V2 0x6708 +#define R_BMODE_PDTH_BE4 0x26040 #define B_BMODE_PDTH_LOWER_BOUND_MSK_V1 GENMASK(31, 24) #define R_BMODE_PDTH_EN_V1 0x4B74 #define R_BMODE_PDTH_EN_V2 0x6718 +#define R_BMODE_PDTH_EN_BE4 0x26050 #define B_BMODE_PDTH_LIMIT_EN_MSK_V1 BIT(30) #define R_BSS_CLR_VLD_V2 0x4EBC #define B_BSS_CLR_VLD0_V2 BIT(2) @@ -9653,7 +9666,9 @@ #define R_CCK_FC0INV 0x675c #define B_CCK_FC0INV GENMASK(18, 0) #define R_SEG0R_EDCCA_LVL_BE 0x69EC +#define R_SEG0R_EDCCA_LVL_BE4 0x2623C #define R_SEG0R_PPDU_LVL_BE 0x69F0 +#define R_SEG0R_PPDU_LVL_BE4 0x26240 #define R_SEGSND 0x6A14 #define B_SEGSND_EN BIT(31) #define R_DBCC 0x6B48 @@ -10380,6 +10395,9 @@ #define B_SW_SI_DATA_DAT_BE4 GENMASK(19, 0) #define R_SW_SI_READ_ADDR_BE4 0x20378 #define B_SW_SI_READ_ADDR_BE4 GENMASK(10, 0) +#define R_EDCCA_RPT_SEL_BE4 0x20780 +#define R_EDCCA_RPT_SEL_BE4_C1 0x21780 +#define B_EDCCA_RPT_SEL_BE4_MSK 0xE0000 #define R_IFS_T1_AVG_BE4 0x20EDC #define B_IFS_T1_AVG_BE4 GENMASK(15, 0) #define B_IFS_T2_AVG_BE4 GENMASK(31, 16) @@ -10402,6 +10420,18 @@ #define B_IFS_T3_HIS_BE4 GENMASK(15, 0) #define B_IFS_T4_HIS_BE4 GENMASK(31, 16) +#define R_PATH0_RXIDX_INIT_BE4 0x24108 +#define B_PATH0_RXIDX_INIT_BE4 GENMASK(29, 25) +#define R_PATH0_LNA_INIT_BE4 0x24158 +#define B_PATH0_LNA_INIT_IDX_BE4 GENMASK(14, 12) +#define R_PATH0_TIA_INIT_BE4 0x24168 +#define B_PATH0_TIA_INIT_IDX_BE4 BIT(18) +#define R_PATH1_RXIDX_INIT_BE4 0x24508 +#define B_PATH1_RXIDX_INIT_BE4 GENMASK(29, 25) +#define R_PATH1_LNA_INIT_BE4 0x24558 +#define B_PATH1_LNA_INIT_IDX_BE4 GENMASK(14, 12) +#define R_PATH1_TIA_INIT_BE4 0x24568 +#define B_PATH1_TIA_INIT_IDX_BE4 BIT(18) #define R_TX_CFR_MANUAL_EN_BE4 0x2483C #define B_TX_CFR_MANUAL_EN_BE4_M BIT(30) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c new file mode 100644 index 000000000000..6a90ded1b33e --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2026 Realtek Corporation + */ + +#include "efuse.h" +#include "mac.h" +#include "reg.h" +#include "rtw8922d.h" + +#define RTW8922D_FW_FORMAT_MAX 0 +#define RTW8922D_FW_BASENAME "rtw89/rtw8922d_fw" +#define RTW8922D_MODULE_FIRMWARE \ + RTW89_GEN_MODULE_FWNAME(RTW8922D_FW_BASENAME, RTW8922D_FW_FORMAT_MAX) + +#define RTW8922DS_FW_FORMAT_MAX 0 +#define RTW8922DS_FW_BASENAME "rtw89/rtw8922ds_fw" +#define RTW8922DS_MODULE_FIRMWARE \ + RTW89_GEN_MODULE_FWNAME(RTW8922DS_FW_BASENAME, RTW8922DS_FW_FORMAT_MAX) + +static const struct rtw89_hfc_ch_cfg rtw8922d_hfc_chcfg_pcie[] = { + {2, 603, 0}, /* ACH 0 */ + {0, 601, 0}, /* ACH 1 */ + {2, 603, 0}, /* ACH 2 */ + {0, 601, 0}, /* ACH 3 */ + {2, 603, 0}, /* ACH 4 */ + {0, 601, 0}, /* ACH 5 */ + {2, 603, 0}, /* ACH 6 */ + {0, 601, 0}, /* ACH 7 */ + {2, 603, 0}, /* B0MGQ */ + {0, 601, 0}, /* B0HIQ */ + {2, 603, 0}, /* B1MGQ */ + {0, 601, 0}, /* B1HIQ */ + {0, 0, 0}, /* FWCMDQ */ + {0, 0, 0}, /* BMC */ + {0, 0, 0}, /* H2D */ +}; + +static const struct rtw89_hfc_pub_cfg rtw8922d_hfc_pubcfg_pcie = { + 613, /* Group 0 */ + 0, /* Group 1 */ + 613, /* Public Max */ + 0, /* WP threshold */ +}; + +static const struct rtw89_hfc_param_ini rtw8922d_hfc_param_ini_pcie[] = { + [RTW89_QTA_SCC] = {rtw8922d_hfc_chcfg_pcie, &rtw8922d_hfc_pubcfg_pcie, + &rtw89_mac_size.hfc_prec_cfg_c0, RTW89_HCIFC_POH}, + [RTW89_QTA_DBCC] = {rtw8922d_hfc_chcfg_pcie, &rtw8922d_hfc_pubcfg_pcie, + &rtw89_mac_size.hfc_prec_cfg_c0, RTW89_HCIFC_POH}, + [RTW89_QTA_DLFW] = {NULL, NULL, &rtw89_mac_size.hfc_prec_cfg_c2, + RTW89_HCIFC_POH}, + [RTW89_QTA_INVALID] = {NULL}, +}; + +static const struct rtw89_dle_mem rtw8922d_dle_mem_pcie[] = { + [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size16_v1, + &rtw89_mac_size.ple_size20_v1, &rtw89_mac_size.wde_qt19_v1, + &rtw89_mac_size.wde_qt19_v1, &rtw89_mac_size.ple_qt42_v2, + &rtw89_mac_size.ple_qt43_v2, &rtw89_mac_size.ple_rsvd_qt9, + &rtw89_mac_size.rsvd0_size6, &rtw89_mac_size.rsvd1_size2, + &rtw89_mac_size.dle_input18}, + [RTW89_QTA_DBCC] = {RTW89_QTA_DBCC, &rtw89_mac_size.wde_size16_v1, + &rtw89_mac_size.ple_size20_v1, &rtw89_mac_size.wde_qt19_v1, + &rtw89_mac_size.wde_qt19_v1, &rtw89_mac_size.ple_qt42_v2, + &rtw89_mac_size.ple_qt43_v2, &rtw89_mac_size.ple_rsvd_qt9, + &rtw89_mac_size.rsvd0_size6, &rtw89_mac_size.rsvd1_size2, + &rtw89_mac_size.dle_input18}, + [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size18_v1, + &rtw89_mac_size.ple_size22_v1, &rtw89_mac_size.wde_qt3, + &rtw89_mac_size.wde_qt3, &rtw89_mac_size.ple_qt5_v2, + &rtw89_mac_size.ple_qt5_v2, &rtw89_mac_size.ple_rsvd_qt1, + &rtw89_mac_size.rsvd0_size6, &rtw89_mac_size.rsvd1_size2, + &rtw89_mac_size.dle_input3}, + [RTW89_QTA_INVALID] = {RTW89_QTA_INVALID, NULL, NULL, NULL, NULL, NULL, + NULL}, +}; + +static const u32 rtw8922d_h2c_regs[RTW89_H2CREG_MAX] = { + R_BE_H2CREG_DATA0, R_BE_H2CREG_DATA1, R_BE_H2CREG_DATA2, + R_BE_H2CREG_DATA3 +}; + +static const u32 rtw8922d_c2h_regs[RTW89_H2CREG_MAX] = { + R_BE_C2HREG_DATA0, R_BE_C2HREG_DATA1, R_BE_C2HREG_DATA2, + R_BE_C2HREG_DATA3 +}; + +static const u32 rtw8922d_wow_wakeup_regs[RTW89_WOW_REASON_NUM] = { + R_BE_DBG_WOW, R_BE_DBG_WOW, +}; + +static const struct rtw89_page_regs rtw8922d_page_regs = { + .hci_fc_ctrl = R_BE_HCI_FC_CTRL, + .ch_page_ctrl = R_BE_CH_PAGE_CTRL, + .ach_page_ctrl = R_BE_CH0_PAGE_CTRL, + .ach_page_info = R_BE_CH0_PAGE_INFO, + .pub_page_info3 = R_BE_PUB_PAGE_INFO3, + .pub_page_ctrl1 = R_BE_PUB_PAGE_CTRL1, + .pub_page_ctrl2 = R_BE_PUB_PAGE_CTRL2, + .pub_page_info1 = R_BE_PUB_PAGE_INFO1, + .pub_page_info2 = R_BE_PUB_PAGE_INFO2, + .wp_page_ctrl1 = R_BE_WP_PAGE_CTRL1, + .wp_page_ctrl2 = R_BE_WP_PAGE_CTRL2, + .wp_page_info1 = R_BE_WP_PAGE_INFO1, +}; + +static const struct rtw89_reg_imr rtw8922d_imr_dmac_regs[] = { + {R_BE_HCI_BUF_IMR, B_BE_HCI_BUF_IMR_CLR, B_BE_HCI_BUF_IMR_SET}, + {R_BE_DISP_HOST_IMR, B_BE_DISP_HOST_IMR_CLR_V1, B_BE_DISP_HOST_IMR_SET_V1}, + {R_BE_DISP_CPU_IMR, B_BE_DISP_CPU_IMR_CLR_V1, B_BE_DISP_CPU_IMR_SET_V1}, + {R_BE_DISP_OTHER_IMR, B_BE_DISP_OTHER_IMR_CLR_V1, B_BE_DISP_OTHER_IMR_SET_V1}, + {R_BE_PKTIN_ERR_IMR, B_BE_PKTIN_ERR_IMR_CLR, B_BE_PKTIN_ERR_IMR_SET}, + {R_BE_MLO_ERR_IDCT_IMR, B_BE_MLO_ERR_IDCT_IMR_CLR, B_BE_MLO_ERR_IDCT_IMR_SET}, + {R_BE_MPDU_TX_ERR_IMR, B_BE_MPDU_TX_ERR_IMR_CLR, B_BE_MPDU_TX_ERR_IMR_SET}, + {R_BE_MPDU_RX_ERR_IMR, B_BE_MPDU_RX_ERR_IMR_CLR, B_BE_MPDU_RX_ERR_IMR_SET}, + {R_BE_SEC_ERROR_IMR, B_BE_SEC_ERROR_IMR_CLR, B_BE_SEC_ERROR_IMR_SET}, + {R_BE_CPUIO_ERR_IMR, B_BE_CPUIO_ERR_IMR_CLR, B_BE_CPUIO_ERR_IMR_SET}, + {R_BE_WDE_ERR_IMR, B_BE_WDE_ERR_IMR_CLR, B_BE_WDE_ERR_IMR_SET}, + {R_BE_PLE_ERR_IMR, B_BE_PLE_ERR_IMR_CLR, B_BE_PLE_ERR_IMR_SET}, + {R_BE_WDRLS_ERR_IMR, B_BE_WDRLS_ERR_IMR_CLR, B_BE_WDRLS_ERR_IMR_SET}, + {R_BE_TXPKTCTL_B0_ERRFLAG_IMR, B_BE_TXPKTCTL_B0_ERRFLAG_IMR_CLR, + B_BE_TXPKTCTL_B0_ERRFLAG_IMR_SET}, + {R_BE_TXPKTCTL_B1_ERRFLAG_IMR, B_BE_TXPKTCTL_B1_ERRFLAG_IMR_CLR, + B_BE_TXPKTCTL_B1_ERRFLAG_IMR_SET}, + {R_BE_BBRPT_COM_ERR_IMR, B_BE_BBRPT_COM_ERR_IMR_CLR, B_BE_BBRPT_COM_ERR_IMR_SET}, + {R_BE_BBRPT_CHINFO_ERR_IMR, B_BE_BBRPT_CHINFO_ERR_IMR_CLR, + B_BE_BBRPT_CHINFO_ERR_IMR_SET}, + {R_BE_BBRPT_DFS_ERR_IMR, B_BE_BBRPT_DFS_ERR_IMR_CLR, B_BE_BBRPT_DFS_ERR_IMR_SET}, + {R_BE_LA_ERRFLAG_IMR, B_BE_LA_ERRFLAG_IMR_CLR, B_BE_LA_ERRFLAG_IMR_SET}, + {R_BE_CH_INFO_DBGFLAG_IMR, B_BE_CH_INFO_DBGFLAG_IMR_CLR, B_BE_CH_INFO_DBGFLAG_IMR_SET}, + {R_BE_PLRLS_ERR_IMR_V1, B_BE_PLRLS_ERR_IMR_V1_CLR, B_BE_PLRLS_ERR_IMR_V1_SET}, + {R_BE_HAXI_IDCT_MSK, B_BE_HAXI_IDCT_MSK_CLR, B_BE_HAXI_IDCT_MSK_SET}, +}; + +static const struct rtw89_imr_table rtw8922d_imr_dmac_table = { + .regs = rtw8922d_imr_dmac_regs, + .n_regs = ARRAY_SIZE(rtw8922d_imr_dmac_regs), +}; + +static const struct rtw89_reg_imr rtw8922d_imr_cmac_regs[] = { + {R_BE_RESP_IMR, B_BE_RESP_IMR_CLR_V1, B_BE_RESP_IMR_SET_V1}, + {R_BE_RESP_IMR1, B_BE_RESP_IMR1_CLR, B_BE_RESP_IMR1_SET}, + {R_BE_RX_ERROR_FLAG_IMR, B_BE_RX_ERROR_FLAG_IMR_CLR_V1, B_BE_RX_ERROR_FLAG_IMR_SET_V1}, + {R_BE_TX_ERROR_FLAG_IMR, B_BE_TX_ERROR_FLAG_IMR_CLR, B_BE_TX_ERROR_FLAG_IMR_SET}, + {R_BE_RX_ERROR_FLAG_IMR_1, B_BE_TX_ERROR_FLAG_IMR_1_CLR, B_BE_TX_ERROR_FLAG_IMR_1_SET}, + {R_BE_PTCL_IMR1, B_BE_PTCL_IMR1_CLR, B_BE_PTCL_IMR1_SET}, + {R_BE_PTCL_IMR0, B_BE_PTCL_IMR0_CLR, B_BE_PTCL_IMR0_SET}, + {R_BE_PTCL_IMR_2, B_BE_PTCL_IMR_2_CLR, B_BE_PTCL_IMR_2_SET}, + {R_BE_SCHEDULE_ERR_IMR, B_BE_SCHEDULE_ERR_IMR_CLR, B_BE_SCHEDULE_ERR_IMR_SET}, + {R_BE_C0_TXPWR_IMR, B_BE_C0_TXPWR_IMR_CLR, B_BE_C0_TXPWR_IMR_SET}, + {R_BE_TRXPTCL_ERROR_INDICA_MASK, B_BE_TRXPTCL_ERROR_INDICA_MASK_CLR, + B_BE_TRXPTCL_ERROR_INDICA_MASK_SET}, + {R_BE_RX_ERR_IMR, B_BE_RX_ERR_IMR_CLR, B_BE_RX_ERR_IMR_SET}, + {R_BE_PHYINFO_ERR_IMR_V1, B_BE_PHYINFO_ERR_IMR_V1_CLR, B_BE_PHYINFO_ERR_IMR_V1_SET}, +}; + +static const struct rtw89_imr_table rtw8922d_imr_cmac_table = { + .regs = rtw8922d_imr_cmac_regs, + .n_regs = ARRAY_SIZE(rtw8922d_imr_cmac_regs), +}; + +static const struct rtw89_rrsr_cfgs rtw8922d_rrsr_cfgs = { + .ref_rate = {R_BE_TRXPTCL_RESP_1, B_BE_WMAC_RESP_REF_RATE_SEL, 0}, + .rsc = {R_BE_PTCL_RRSR1, B_BE_RSC_MASK, 2}, +}; + +static const struct rtw89_rfkill_regs rtw8922d_rfkill_regs = { + .pinmux = {R_BE_GPIO8_15_FUNC_SEL, + B_BE_PINMUX_GPIO9_FUNC_SEL_MASK, + 0xf}, + .mode = {R_BE_GPIO_EXT_CTRL + 2, + (B_BE_GPIO_MOD_9 | B_BE_GPIO_IO_SEL_9) >> 16, + 0x0}, +}; + +static const struct rtw89_dig_regs rtw8922d_dig_regs = { + .seg0_pd_reg = R_SEG0R_PD_BE4, + .pd_lower_bound_mask = B_SEG0R_PD_LOWER_BOUND_MSK, + .pd_spatial_reuse_en = B_SEG0R_PD_SPATIAL_REUSE_EN_MSK_V1, + .bmode_pd_reg = R_BMODE_PDTH_EN_BE4, + .bmode_cca_rssi_limit_en = B_BMODE_PDTH_LIMIT_EN_MSK_V1, + .bmode_pd_lower_bound_reg = R_BMODE_PDTH_BE4, + .bmode_rssi_nocca_low_th_mask = B_BMODE_PDTH_LOWER_BOUND_MSK_V1, + .p0_lna_init = {R_PATH0_LNA_INIT_BE4, B_PATH0_LNA_INIT_IDX_BE4}, + .p1_lna_init = {R_PATH1_LNA_INIT_BE4, B_PATH1_LNA_INIT_IDX_BE4}, + .p0_tia_init = {R_PATH0_TIA_INIT_BE4, B_PATH0_TIA_INIT_IDX_BE4}, + .p1_tia_init = {R_PATH1_TIA_INIT_BE4, B_PATH1_TIA_INIT_IDX_BE4}, + .p0_rxb_init = {R_PATH0_RXIDX_INIT_BE4, B_PATH0_RXIDX_INIT_BE4}, + .p1_rxb_init = {R_PATH1_RXIDX_INIT_BE4, B_PATH1_RXIDX_INIT_BE4}, + .p0_p20_pagcugc_en = {R_PATH0_P20_FOLLOW_BY_PAGCUGC_BE4, + B_PATH0_P20_FOLLOW_BY_PAGCUGC_EN_MSK}, + .p0_s20_pagcugc_en = {R_PATH0_S20_FOLLOW_BY_PAGCUGC_BE4, + B_PATH0_S20_FOLLOW_BY_PAGCUGC_EN_MSK}, + .p1_p20_pagcugc_en = {R_PATH1_P20_FOLLOW_BY_PAGCUGC_BE4, + B_PATH1_P20_FOLLOW_BY_PAGCUGC_EN_MSK}, + .p1_s20_pagcugc_en = {R_PATH1_S20_FOLLOW_BY_PAGCUGC_BE4, + B_PATH1_S20_FOLLOW_BY_PAGCUGC_EN_MSK}, +}; + +static const struct rtw89_edcca_regs rtw8922d_edcca_regs = { + .edcca_level = R_SEG0R_EDCCA_LVL_BE4, + .edcca_mask = B_EDCCA_LVL_MSK0, + .edcca_p_mask = B_EDCCA_LVL_MSK1, + .ppdu_level = R_SEG0R_PPDU_LVL_BE4, + .ppdu_mask = B_EDCCA_LVL_MSK1, + .p = {{ + .rpt_a = R_EDCCA_RPT_A_BE4, + .rpt_b = R_EDCCA_RPT_B_BE4, + .rpt_sel = R_EDCCA_RPT_SEL_BE4, + .rpt_sel_mask = B_EDCCA_RPT_SEL_BE4_MSK, + }, { + .rpt_a = R_EDCCA_RPT_A_BE4_C1, + .rpt_b = R_EDCCA_RPT_A_BE4_C1, + .rpt_sel = R_EDCCA_RPT_SEL_BE4_C1, + .rpt_sel_mask = B_EDCCA_RPT_SEL_BE4_MSK, + }}, + .rpt_sel_be = R_EDCCA_RPTREG_SEL_BE4, + .rpt_sel_be_mask = B_EDCCA_RPTREG_SEL_BE_MSK, + .tx_collision_t2r_st = R_TX_COLLISION_T2R_ST_BE4, + .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_BE_M, +}; + +static const struct rtw89_efuse_block_cfg rtw8922d_efuse_blocks[] = { + [RTW89_EFUSE_BLOCK_SYS] = {.offset = 0x00000, .size = 0x310}, + [RTW89_EFUSE_BLOCK_RF] = {.offset = 0x10000, .size = 0x240}, + [RTW89_EFUSE_BLOCK_HCI_DIG_PCIE_SDIO] = {.offset = 0x20000, .size = 0x4800}, + [RTW89_EFUSE_BLOCK_HCI_DIG_USB] = {.offset = 0x30000, .size = 0x890}, + [RTW89_EFUSE_BLOCK_HCI_PHY_PCIE] = {.offset = 0x40000, .size = 0x400}, + [RTW89_EFUSE_BLOCK_HCI_PHY_USB3] = {.offset = 0x50000, .size = 0x80}, + [RTW89_EFUSE_BLOCK_HCI_PHY_USB2] = {.offset = 0x60000, .size = 0x50}, + [RTW89_EFUSE_BLOCK_ADIE] = {.offset = 0x70000, .size = 0x10}, +}; + +MODULE_FIRMWARE(RTW8922D_MODULE_FIRMWARE); +MODULE_FIRMWARE(RTW8922DS_MODULE_FIRMWARE); +MODULE_AUTHOR("Realtek Corporation"); +MODULE_DESCRIPTION("Realtek 802.11be wireless 8922D driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.h b/drivers/net/wireless/realtek/rtw89/rtw8922d.h new file mode 100644 index 000000000000..7ef3f263274e --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* Copyright(c) 2026 Realtek Corporation + */ + +#ifndef __RTW89_8922D_H__ +#define __RTW89_8922D_H__ + +#include "core.h" + +#endif From bbbde3d0fbe1005cffa45a7803c17589e2011107 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 24 Mar 2026 14:20:44 +0800 Subject: [PATCH 046/124] wifi: rtw89: 8922d: add power on/off functions The power on function is the first entry to power on hardware including all MAC/BB/RF circuits, and then it becomes possible to do high level operations, such as WiFi scan, connection. If connection becomes unavailable, device stays into idle mode, calling power off function to cut power. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260324062049.52266-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/reg.h | 63 +++++ drivers/net/wireless/realtek/rtw89/rtw8922d.c | 237 ++++++++++++++++++ 2 files changed, 300 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 2cb35458a822..b6fd7b434de9 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -4403,6 +4403,31 @@ #define B_BE_FS_GPIO17_INT_EN BIT(1) #define B_BE_FS_GPIO16_INT_EN BIT(0) +#define R_BE_FWS1ISR 0x019C +#define B_BE_FS_WL_HW_RADIO_OFF_INT BIT(28) +#define B_BE_SWRD_BOD_INT BIT(27) +#define B_BE_HCIDBG_INT BIT(25) +#define B_BE_FS_RPWM_INT_V1 BIT(24) +#define B_BE_PCIE_HOTRST BIT(22) +#define B_BE_PCIE_SER_TIMEOUT_INDIC BIT(21) +#define B_BE_PCIE_RXI300_SLVTOUT_INDIC BIT(20) +#define B_BE_AON_PCIE_FLR_INT BIT(19) +#define B_BE_PCIE_ERR_INDIC BIT(18) +#define B_BE_SDIO_ERR_INDIC BIT(17) +#define B_BE_USB_ERR_INDIC BIT(16) +#define B_BE_FS_GPIO27_INT BIT(11) +#define B_BE_FS_GPIO26_INT BIT(10) +#define B_BE_FS_GPIO25_INT BIT(9) +#define B_BE_FS_GPIO24_INT BIT(8) +#define B_BE_FS_GPIO23_INT BIT(7) +#define B_BE_FS_GPIO22_INT BIT(6) +#define B_BE_FS_GPIO21_INT BIT(5) +#define B_BE_FS_GPIO20_INT BIT(4) +#define B_BE_FS_GPIO19_INT BIT(3) +#define B_BE_FS_GPIO18_INT BIT(2) +#define B_BE_FS_GPIO17_INT BIT(1) +#define B_BE_FS_GPIO16_INT BIT(0) + #define R_BE_HIMR0 0x01A0 #define B_BE_WDT_DATACPU_TIMEOUT_INT_EN BIT(25) #define B_BE_HALT_D2H_INT_EN BIT(24) @@ -4503,6 +4528,44 @@ #define R_BE_UDM2 0x01F8 #define B_BE_UDM2_EPC_RA_MASK GENMASK(31, 0) +#define R_BE_SPS_DIG_ON_CTRL1 0x0204 +#define B_BE_SN_N_L_MASK GENMASK(31, 28) +#define B_BE_SP_N_L_MASK GENMASK(27, 24) +#define B_BE_SN_P_L_MASK GENMASK(23, 20) +#define B_BE_SP_P_L_MASK GENMASK(19, 16) +#define B_BE_VO_DISCHG_PWM_H BIT(15) +#define B_BE_REG_MODE_PREDRIVER BIT(14) +#define B_BE_VREFOCP_MASK GENMASK(13, 10) +#define B_BE_POWOCP_L1 BIT(9) +#define B_BE_PWM_FORCE BIT(8) +#define B_BE_PFM_PD_RST BIT(7) +#define B_BE_VC_PFM_RSTB BIT(6) +#define B_BE_PFM_IN_SEL BIT(5) +#define B_BE_VC_RSTB BIT(4) +#define B_BE_FPWMDELAY BIT(3) +#define B_BE_ENFPWMDELAY_H BIT(2) +#define B_BE_REG_MOS_HALF_L BIT(1) +#define B_BE_CURRENT_SENSE_MOS BIT(0) + +#define R_BE_SPS_ANA_ON_CTRL1 0x0224 +#define B_BE_SN_N_L_ANA_MASK GENMASK(31, 28) +#define B_BE_SP_N_L_ANA_MASK GENMASK(27, 24) +#define B_BE_SN_P_L_ANA_MASK GENMASK(23, 20) +#define B_BE_SP_P_L_ANA_MASK GENMASK(19, 16) +#define B_BE_VO_DISCHG_PWM_H_ANA BIT(15) +#define B_BE_REG_MODE_PREDRIVER_ANA BIT(14) +#define B_BE_VREFOCP_ANA_MASK GENMASK(13, 10) +#define B_BE_POWOCP_L1_ANA BIT(9) +#define B_BE_PWM_FORCE_ANA BIT(8) +#define B_BE_PFM_PD_RST_ANA BIT(7) +#define B_BE_VC_PFM_RSTB_ANA BIT(6) +#define B_BE_PFM_IN_SEL_ANA BIT(5) +#define B_BE_VC_RSTB_ANA BIT(4) +#define B_BE_FPWMDELAY_ANA BIT(3) +#define B_BE_ENFPWMDELAY_H_ANA BIT(2) +#define B_BE_REG_MOS_HALF_L_ANA BIT(1) +#define B_BE_CURRENT_SENSE_MOS_ANA BIT(0) + #define R_BE_AFE_ON_CTRL0 0x0240 #define B_BE_REG_LPF_R3_3_0_MASK GENMASK(31, 29) #define B_BE_REG_LPF_R2_MASK GENMASK(28, 24) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 6a90ded1b33e..194e2901232b 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -231,6 +231,243 @@ static const struct rtw89_efuse_block_cfg rtw8922d_efuse_blocks[] = { [RTW89_EFUSE_BLOCK_ADIE] = {.offset = 0x70000, .size = 0x10}, }; +static int rtw8922d_pwr_on_func(struct rtw89_dev *rtwdev) +{ + struct rtw89_hal *hal = &rtwdev->hal; + u32 val32; + int ret; + + if (hal->cid != RTL8922D_CID7025) + goto begin; + + switch (hal->cv) { + case CHIP_CAV: + case CHIP_CBV: + rtw89_write32_set(rtwdev, R_BE_SPS_DIG_ON_CTRL1, B_BE_PWM_FORCE); + rtw89_write32_set(rtwdev, R_BE_SPS_ANA_ON_CTRL1, B_BE_PWM_FORCE_ANA); + break; + default: + break; + } + +begin: + rtw89_write32_clr(rtwdev, R_BE_SYS_PW_CTRL, B_BE_AFSM_WLSUS_EN | + B_BE_AFSM_PCIE_SUS_EN); + rtw89_write32_set(rtwdev, R_BE_SYS_PW_CTRL, B_BE_DIS_WLBT_PDNSUSEN_SOPC); + rtw89_write32_set(rtwdev, R_BE_WLLPS_CTRL, B_BE_DIS_WLBT_LPSEN_LOPC); + if (hal->cid != RTL8922D_CID7090) + rtw89_write32_clr(rtwdev, R_BE_SYS_PW_CTRL, B_BE_APDM_HPDN); + rtw89_write32_clr(rtwdev, R_BE_FWS1ISR, B_BE_FS_WL_HW_RADIO_OFF_INT); + rtw89_write32_clr(rtwdev, R_BE_SYS_PW_CTRL, B_BE_APFM_SWLPS); + + ret = read_poll_timeout(rtw89_read32, val32, val32 & B_BE_RDY_SYSPWR, + 1000, 3000000, false, rtwdev, R_BE_SYS_PW_CTRL); + if (ret) + return ret; + + rtw89_write32_set(rtwdev, R_BE_SYS_PW_CTRL, B_BE_EN_WLON); + rtw89_write32_set(rtwdev, R_BE_WLRESUME_CTRL, B_BE_LPSROP_CMAC0 | + B_BE_LPSROP_CMAC1); + rtw89_write32_set(rtwdev, R_BE_SYS_PW_CTRL, B_BE_APFN_ONMAC); + + ret = read_poll_timeout(rtw89_read32, val32, !(val32 & B_BE_APFN_ONMAC), + 1000, 3000000, false, rtwdev, R_BE_SYS_PW_CTRL); + if (ret) + return ret; + + rtw89_write8_set(rtwdev, R_BE_PLATFORM_ENABLE, B_BE_PLATFORM_EN); + rtw89_write32_set(rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HAXIDMA_IO_EN); + + ret = read_poll_timeout(rtw89_read32, val32, val32 & B_BE_HAXIDMA_IO_ST, + 1000, 3000000, false, rtwdev, R_BE_HCI_OPT_CTRL); + if (ret) + return ret; + + ret = read_poll_timeout(rtw89_read32, val32, !(val32 & B_BE_HAXIDMA_BACKUP_RESTORE_ST), + 1000, 3000000, false, rtwdev, R_BE_HCI_OPT_CTRL); + if (ret) + return ret; + + rtw89_write32_set(rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HCI_WLAN_IO_EN); + + ret = read_poll_timeout(rtw89_read32, val32, val32 & B_BE_HCI_WLAN_IO_ST, + 1000, 3000000, false, rtwdev, R_BE_HCI_OPT_CTRL); + if (ret) + return ret; + + rtw89_write32_clr(rtwdev, R_BE_SYS_SDIO_CTRL, B_BE_PCIE_FORCE_IBX_EN); + + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_NORMAL_WRITE, 0x10, 0x10); + if (ret) + return ret; + + rtw89_write32_set(rtwdev, R_BE_SYS_ADIE_PAD_PWR_CTRL, B_BE_SYM_PADPDN_WL_RFC1_1P3); + + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0x40, 0x40); + if (ret) + return ret; + + rtw89_write32_set(rtwdev, R_BE_SYS_ADIE_PAD_PWR_CTRL, B_BE_SYM_PADPDN_WL_RFC0_1P3); + + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0x20, 0x20); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0x04, 0x04); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0x08, 0x08); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0, 0x10); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_WL_RFC_S0, 0xEB, 0xFF); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_WL_RFC_S1, 0xEB, 0xFF); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0x01, 0x01); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0x02, 0x02); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0, 0x80); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_XTAL_XMD_2, 0, 0x70); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_SRAM_CTRL, 0, 0x02); + if (ret) + return ret; + + rtw89_write32_set(rtwdev, R_BE_PMC_DBG_CTRL2, B_BE_SYSON_DIS_PMCR_BE_WRMSK); + rtw89_write32_set(rtwdev, R_BE_SYS_ISO_CTRL, B_BE_ISO_EB2CORE); + rtw89_write32_clr(rtwdev, R_BE_SYS_ISO_CTRL, B_BE_PWC_EV2EF_B); + + mdelay(1); + + rtw89_write32_clr(rtwdev, R_BE_SYS_ISO_CTRL, B_BE_PWC_EV2EF_S); + rtw89_write32_clr(rtwdev, R_BE_PMC_DBG_CTRL2, B_BE_SYSON_DIS_PMCR_BE_WRMSK); + + rtw89_write32_set(rtwdev, R_BE_DMAC_FUNC_EN, + B_BE_MAC_FUNC_EN | B_BE_DMAC_FUNC_EN | + B_BE_MPDU_PROC_EN | B_BE_WD_RLS_EN | + B_BE_DLE_WDE_EN | B_BE_TXPKT_CTRL_EN | + B_BE_STA_SCH_EN | B_BE_DLE_PLE_EN | + B_BE_PKT_BUF_EN | B_BE_DMAC_TBL_EN | + B_BE_PKT_IN_EN | B_BE_DLE_CPUIO_EN | + B_BE_DISPATCHER_EN | B_BE_BBRPT_EN | + B_BE_MAC_SEC_EN | B_BE_H_AXIDMA_EN | + B_BE_DMAC_MLO_EN | B_BE_PLRLS_EN | + B_BE_P_AXIDMA_EN | B_BE_DLE_DATACPUIO_EN | + B_BE_LTR_CTL_EN); + + set_bit(RTW89_FLAG_DMAC_FUNC, rtwdev->flags); + + rtw89_write32_set(rtwdev, R_BE_CMAC_SHARE_FUNC_EN, + B_BE_CMAC_SHARE_EN | B_BE_RESPBA_EN | + B_BE_ADDRSRCH_EN | B_BE_BTCOEX_EN); + + rtw89_write32_set(rtwdev, R_BE_CMAC_FUNC_EN, + B_BE_CMAC_EN | B_BE_CMAC_TXEN | + B_BE_CMAC_RXEN | B_BE_SIGB_EN | + B_BE_PHYINTF_EN | B_BE_CMAC_DMA_EN | + B_BE_PTCLTOP_EN | B_BE_SCHEDULER_EN | + B_BE_TMAC_EN | B_BE_RMAC_EN | + B_BE_TXTIME_EN | B_BE_RESP_PKTCTL_EN); + + set_bit(RTW89_FLAG_CMAC0_FUNC, rtwdev->flags); + + rtw89_write32_set(rtwdev, R_BE_FEN_RST_ENABLE, + B_BE_FEN_BB_IP_RSTN | B_BE_FEN_BBPLAT_RSTB); + + return 0; +} + +static int rtw8922d_pwr_off_func(struct rtw89_dev *rtwdev) +{ + u32 val32; + int ret; + + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0x10, 0x10); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0, 0x08); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0, 0x04); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_WL_RFC_S0, 0, 0x01); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_WL_RFC_S1, 0, 0x01); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0x80, 0x80); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0, 0x02); + if (ret) + return ret; + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0, 0x01); + if (ret) + return ret; + + rtw89_write32_set(rtwdev, R_BE_SYS_PW_CTRL, B_BE_EN_WLON); + rtw89_write8_clr(rtwdev, R_BE_FEN_RST_ENABLE, B_BE_FEN_BB_IP_RSTN | + B_BE_FEN_BBPLAT_RSTB); + rtw89_write32_clr(rtwdev, R_BE_SYS_ADIE_PAD_PWR_CTRL, + B_BE_SYM_PADPDN_WL_RFC0_1P3); + + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0, 0x20); + if (ret) + return ret; + + rtw89_write32_clr(rtwdev, R_BE_SYS_ADIE_PAD_PWR_CTRL, + B_BE_SYM_PADPDN_WL_RFC1_1P3); + + ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, 0, 0x40); + if (ret) + return ret; + + rtw89_write32_clr(rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HAXIDMA_IO_EN); + + ret = read_poll_timeout(rtw89_read32, val32, !(val32 & B_BE_HAXIDMA_IO_ST), + 1000, 3000000, false, rtwdev, R_BE_HCI_OPT_CTRL); + if (ret) + return ret; + ret = read_poll_timeout(rtw89_read32, val32, + !(val32 & B_BE_HAXIDMA_BACKUP_RESTORE_ST), + 1000, 3000000, false, rtwdev, R_BE_HCI_OPT_CTRL); + if (ret) + return ret; + + rtw89_write32_clr(rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HCI_WLAN_IO_EN); + + ret = read_poll_timeout(rtw89_read32, val32, !(val32 & B_BE_HCI_WLAN_IO_ST), + 1000, 3000000, false, rtwdev, R_BE_HCI_OPT_CTRL); + if (ret) + return ret; + + rtw89_write32_set(rtwdev, R_BE_SYS_PW_CTRL, B_BE_APFM_OFFMAC); + + ret = read_poll_timeout(rtw89_read32, val32, !(val32 & B_BE_APFM_OFFMAC), + 1000, 3000000, false, rtwdev, R_BE_SYS_PW_CTRL); + if (ret) + return ret; + + rtw89_write32(rtwdev, R_BE_WLLPS_CTRL, 0x00015002); + rtw89_write32_clr(rtwdev, R_BE_SYS_PW_CTRL, B_BE_XTAL_OFF_A_DIE); + rtw89_write32_set(rtwdev, R_BE_SYS_PW_CTRL, B_BE_APFM_SWLPS); + rtw89_write32(rtwdev, R_BE_UDM1, 0); + + return 0; +} + MODULE_FIRMWARE(RTW8922D_MODULE_FIRMWARE); MODULE_FIRMWARE(RTW8922DS_MODULE_FIRMWARE); MODULE_AUTHOR("Realtek Corporation"); From 199e4235d33f01d3205d40a056e506ac4964da28 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 24 Mar 2026 14:20:45 +0800 Subject: [PATCH 047/124] wifi: rtw89: 8922d: define efuse map and read necessary fields Define specific efuse map for RTL8922D, including TSSI, RX gain, MAC address, RFE type and etc. The additional fields comparing to existing chips are BT setting (define BT switch GPIO, antenna number and etc) and gain offset2 (define more fields like existing RX gain offset). Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260324062049.52266-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 3 + drivers/net/wireless/realtek/rtw89/rtw8922d.c | 162 ++++++++++++++++++ drivers/net/wireless/realtek/rtw89/rtw8922d.h | 70 ++++++++ 3 files changed, 235 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 94e4faf70e12..d0ae3e15253b 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -3562,6 +3562,8 @@ struct rtw89_efuse { u8 rfe_type; char country_code[2]; u8 adc_td; + u8 bt_setting_2; + u8 bt_setting_3; }; struct rtw89_phy_rate_pattern { @@ -5926,6 +5928,7 @@ struct rtw89_phy_efuse_gain { bool offset_valid; bool comp_valid; s8 offset[RF_PATH_MAX][RTW89_GAIN_OFFSET_NR]; /* S(8, 0) */ + s8 offset2[RF_PATH_MAX][RTW89_GAIN_OFFSET_NR]; /* S(8, 0) */ s8 offset_base[RTW89_PHY_NUM]; /* S(8, 4) */ s8 rssi_base[RTW89_PHY_NUM]; /* S(8, 4) */ s8 comp[RF_PATH_MAX][RTW89_SUBBAND_NR]; /* S(8, 0) */ diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 194e2901232b..0ae34a4f8d79 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -2,6 +2,7 @@ /* Copyright(c) 2026 Realtek Corporation */ +#include "debug.h" #include "efuse.h" #include "mac.h" #include "reg.h" @@ -468,6 +469,167 @@ static int rtw8922d_pwr_off_func(struct rtw89_dev *rtwdev) return 0; } +static void rtw8922d_efuse_parsing_tssi(struct rtw89_dev *rtwdev, + struct rtw8922d_efuse *map) +{ + const struct rtw8922d_tssi_offset_6g * const ofst_6g[] = { + &map->path_a_tssi_6g, + &map->path_b_tssi_6g, + }; + const struct rtw8922d_tssi_offset * const ofst[] = { + &map->path_a_tssi, + &map->path_b_tssi, + }; + struct rtw89_tssi_info *tssi = &rtwdev->tssi; + u8 i, j; + + tssi->thermal[RF_PATH_A] = map->path_a_therm; + tssi->thermal[RF_PATH_B] = map->path_b_therm; + + for (i = 0; i < RF_PATH_NUM_8922D; i++) { + memcpy(tssi->tssi_cck[i], ofst[i]->cck_tssi, TSSI_CCK_CH_GROUP_NUM); + + for (j = 0; j < TSSI_CCK_CH_GROUP_NUM; j++) + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][EFUSE] path=%d cck[%d]=0x%x\n", + i, j, tssi->tssi_cck[i][j]); + + memcpy(tssi->tssi_mcs[i], ofst[i]->bw40_tssi, + TSSI_MCS_2G_CH_GROUP_NUM); + memcpy(tssi->tssi_mcs[i] + TSSI_MCS_2G_CH_GROUP_NUM, + ofst[i]->bw40_1s_tssi_5g, TSSI_MCS_5G_CH_GROUP_NUM); + memcpy(tssi->tssi_6g_mcs[i], ofst_6g[i]->bw40_1s_tssi_6g, + TSSI_MCS_6G_CH_GROUP_NUM); + + for (j = 0; j < TSSI_MCS_CH_GROUP_NUM; j++) + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][EFUSE] path=%d mcs[%d]=0x%x\n", + i, j, tssi->tssi_mcs[i][j]); + + for (j = 0; j < TSSI_MCS_6G_CH_GROUP_NUM; j++) + rtw89_debug(rtwdev, RTW89_DBG_TSSI, + "[TSSI][EFUSE] path=%d mcs_6g[%d]=0x%x\n", + i, j, tssi->tssi_6g_mcs[i][j]); + } +} + +static void +__rtw8922d_efuse_parsing_gain_offset(struct rtw89_dev *rtwdev, + s8 offset[RTW89_GAIN_OFFSET_NR], + const s8 *offset_default, + const struct rtw8922d_rx_gain *rx_gain, + const struct rtw8922d_rx_gain_6g *rx_gain_6g) +{ + int i; + u8 t; + + offset[RTW89_GAIN_OFFSET_2G_CCK] = rx_gain->_2g_cck; + offset[RTW89_GAIN_OFFSET_2G_OFDM] = rx_gain->_2g_ofdm; + offset[RTW89_GAIN_OFFSET_5G_LOW] = rx_gain->_5g_low; + offset[RTW89_GAIN_OFFSET_5G_MID] = rx_gain->_5g_mid; + offset[RTW89_GAIN_OFFSET_5G_HIGH] = rx_gain->_5g_high; + offset[RTW89_GAIN_OFFSET_6G_L0] = rx_gain_6g->_6g_l0; + offset[RTW89_GAIN_OFFSET_6G_L1] = rx_gain_6g->_6g_l1; + offset[RTW89_GAIN_OFFSET_6G_M0] = rx_gain_6g->_6g_m0; + offset[RTW89_GAIN_OFFSET_6G_M1] = rx_gain_6g->_6g_m1; + offset[RTW89_GAIN_OFFSET_6G_H0] = rx_gain_6g->_6g_h0; + offset[RTW89_GAIN_OFFSET_6G_H1] = rx_gain_6g->_6g_h1; + offset[RTW89_GAIN_OFFSET_6G_UH0] = rx_gain_6g->_6g_uh0; + offset[RTW89_GAIN_OFFSET_6G_UH1] = rx_gain_6g->_6g_uh1; + + for (i = 0; i < RTW89_GAIN_OFFSET_NR; i++) { + t = offset[i]; + if (t == 0xff) { + if (offset_default) { + offset[i] = offset_default[i]; + continue; + } + t = 0; + } + + /* transform: sign-bit + U(7,2) to S(8,2) */ + if (t & 0x80) + offset[i] = (t ^ 0x7f) + 1; + else + offset[i] = t; + } +} + +static void rtw8922d_efuse_parsing_gain_offset(struct rtw89_dev *rtwdev, + struct rtw8922d_efuse *map) +{ + struct rtw89_phy_efuse_gain *gain = &rtwdev->efuse_gain; + + __rtw8922d_efuse_parsing_gain_offset(rtwdev, gain->offset[RF_PATH_A], + NULL, + &map->rx_gain_a, &map->rx_gain_6g_a); + __rtw8922d_efuse_parsing_gain_offset(rtwdev, gain->offset[RF_PATH_B], + NULL, + &map->rx_gain_b, &map->rx_gain_6g_b); + + __rtw8922d_efuse_parsing_gain_offset(rtwdev, gain->offset2[RF_PATH_A], + gain->offset[RF_PATH_A], + &map->rx_gain_a_2, &map->rx_gain_6g_a_2); + __rtw8922d_efuse_parsing_gain_offset(rtwdev, gain->offset2[RF_PATH_B], + gain->offset[RF_PATH_B], + &map->rx_gain_b_2, &map->rx_gain_6g_b_2); + + gain->offset_valid = true; +} + +static int rtw8922d_read_efuse_pci_sdio(struct rtw89_dev *rtwdev, u8 *log_map) +{ + struct rtw89_efuse *efuse = &rtwdev->efuse; + + if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) + ether_addr_copy(efuse->addr, log_map + 0x4104); + else + ether_addr_copy(efuse->addr, log_map + 0x001A); + + return 0; +} + +static int rtw8922d_read_efuse_usb(struct rtw89_dev *rtwdev, u8 *log_map) +{ + struct rtw89_efuse *efuse = &rtwdev->efuse; + + ether_addr_copy(efuse->addr, log_map + 0x0078); + + return 0; +} + +static int rtw8922d_read_efuse_rf(struct rtw89_dev *rtwdev, u8 *log_map) +{ + struct rtw8922d_efuse *map = (struct rtw8922d_efuse *)log_map; + struct rtw89_efuse *efuse = &rtwdev->efuse; + + efuse->rfe_type = map->rfe_type; + efuse->xtal_cap = map->xtal_k; + efuse->country_code[0] = map->country_code[0]; + efuse->country_code[1] = map->country_code[1]; + efuse->bt_setting_2 = map->bt_setting_2; + efuse->bt_setting_3 = map->bt_setting_3; + rtw8922d_efuse_parsing_tssi(rtwdev, map); + rtw8922d_efuse_parsing_gain_offset(rtwdev, map); + + return 0; +} + +static int rtw8922d_read_efuse(struct rtw89_dev *rtwdev, u8 *log_map, + enum rtw89_efuse_block block) +{ + switch (block) { + case RTW89_EFUSE_BLOCK_HCI_DIG_PCIE_SDIO: + return rtw8922d_read_efuse_pci_sdio(rtwdev, log_map); + case RTW89_EFUSE_BLOCK_HCI_DIG_USB: + return rtw8922d_read_efuse_usb(rtwdev, log_map); + case RTW89_EFUSE_BLOCK_RF: + return rtw8922d_read_efuse_rf(rtwdev, log_map); + default: + return 0; + } +} + MODULE_FIRMWARE(RTW8922D_MODULE_FIRMWARE); MODULE_FIRMWARE(RTW8922DS_MODULE_FIRMWARE); MODULE_AUTHOR("Realtek Corporation"); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.h b/drivers/net/wireless/realtek/rtw89/rtw8922d.h index 7ef3f263274e..a3b98ad6636c 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.h @@ -7,4 +7,74 @@ #include "core.h" +#define RF_PATH_NUM_8922D 2 +#define BB_PATH_NUM_8922D 2 + +struct rtw8922d_tssi_offset { + u8 cck_tssi[TSSI_CCK_CH_GROUP_NUM]; + u8 bw40_tssi[TSSI_MCS_2G_CH_GROUP_NUM]; + u8 rsvd[7]; + u8 bw40_1s_tssi_5g[TSSI_MCS_5G_CH_GROUP_NUM]; + u8 bw_diff_5g[10]; +} __packed; + +struct rtw8922d_tssi_offset_6g { + u8 bw40_1s_tssi_6g[TSSI_MCS_6G_CH_GROUP_NUM]; + u8 rsvd[0xa]; +} __packed; + +struct rtw8922d_rx_gain { + u8 _2g_ofdm; + u8 _2g_cck; + u8 _5g_low; + u8 _5g_mid; + u8 _5g_high; +} __packed; + +struct rtw8922d_rx_gain_6g { + u8 _6g_l0; + u8 _6g_l1; + u8 _6g_m0; + u8 _6g_m1; + u8 _6g_h0; + u8 _6g_h1; + u8 _6g_uh0; + u8 _6g_uh1; +} __packed; + +struct rtw8922d_efuse { + u8 country_code[2]; + u8 rsvd[0xe]; + struct rtw8922d_tssi_offset path_a_tssi; + struct rtw8922d_tssi_offset path_b_tssi; + u8 rsvd1[0x54]; + u8 channel_plan; + u8 xtal_k; + u8 rsvd2[0x7]; + u8 board_info; + u8 rsvd3[0x8]; + u8 rfe_type; + u8 rsvd4[2]; + u8 bt_setting_2; + u8 bt_setting_3; + u8 rsvd4_2; + u8 path_a_therm; + u8 path_b_therm; + u8 rsvd5[0x2]; + struct rtw8922d_rx_gain rx_gain_a; + struct rtw8922d_rx_gain rx_gain_b; + u8 rsvd6[0x18]; + struct rtw8922d_rx_gain rx_gain_a_2; + struct rtw8922d_rx_gain rx_gain_b_2; + struct rtw8922d_tssi_offset_6g path_a_tssi_6g; + struct rtw8922d_tssi_offset_6g path_b_tssi_6g; + struct rtw8922d_tssi_offset_6g path_c_tssi_6g; + struct rtw8922d_tssi_offset_6g path_d_tssi_6g; + struct rtw8922d_rx_gain_6g rx_gain_6g_a; + struct rtw8922d_rx_gain_6g rx_gain_6g_b; + u8 rsvd7[0x5a]; + struct rtw8922d_rx_gain_6g rx_gain_6g_a_2; + struct rtw8922d_rx_gain_6g rx_gain_6g_b_2; +} __packed; + #endif From 5b04b8049828d27b23ba5798f35bef835acc7fb1 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 24 Mar 2026 14:20:46 +0800 Subject: [PATCH 048/124] wifi: rtw89: 8922d: read and configure RF by calibration data from efuse physical map The calibration data is from physical map, including 1) thermal trim to align output thermal value across chips, and 2) PA bias to transmit expected power by controller. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260324062049.52266-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 2 + drivers/net/wireless/realtek/rtw89/reg.h | 2 + drivers/net/wireless/realtek/rtw89/rtw8922d.c | 205 ++++++++++++++++++ 3 files changed, 209 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index d0ae3e15253b..cde46ed21d32 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5595,9 +5595,11 @@ struct rtw89_tssi_info { struct rtw89_power_trim_info { bool pg_thermal_trim; bool pg_pa_bias_trim; + bool pg_vco_trim; u8 thermal_trim[RF_PATH_MAX]; u8 pa_bias_trim[RF_PATH_MAX]; u8 pad_bias_trim[RF_PATH_MAX]; + u8 vco_trim[RF_PATH_MAX]; }; enum rtw89_regd_func { diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index b6fd7b434de9..179006c8e499 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -8517,6 +8517,7 @@ #define RR_LUTWD0_LB GENMASK(5, 0) #define RR_TM 0x42 #define RR_TM_TRI BIT(19) +#define RR_TM_TRM GENMASK(17, 11) #define RR_TM_VAL_V1 GENMASK(7, 0) #define RR_TM_VAL GENMASK(6, 1) #define RR_TM2 0x43 @@ -8649,6 +8650,7 @@ #define RR_LDO 0xb1 #define RR_LDO_SEL GENMASK(8, 6) #define RR_VCO 0xb2 +#define RR_VCO_VAL GENMASK(18, 14) #define RR_VCO_SEL GENMASK(9, 8) #define RR_VCI 0xb3 #define RR_VCI_ON BIT(7) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 0ae34a4f8d79..cbe8e067ae55 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -630,6 +630,211 @@ static int rtw8922d_read_efuse(struct rtw89_dev *rtwdev, u8 *log_map, } } +static void rtw8922d_phycap_parsing_vco_trim(struct rtw89_dev *rtwdev, + u8 *phycap_map) +{ + static const u32 vco_trim_addr[RF_PATH_NUM_8922D] = {0x175E, 0x175F}; + struct rtw89_power_trim_info *info = &rtwdev->pwr_trim; + u32 addr = rtwdev->chip->phycap_addr; + const u32 vco_check_addr = 0x1700; + u8 val; + + val = phycap_map[vco_check_addr - addr]; + if (val & BIT(1)) + return; + + info->pg_vco_trim = true; + + info->vco_trim[0] = u8_get_bits(phycap_map[vco_trim_addr[0] - addr], GENMASK(4, 0)); + info->vco_trim[1] = u8_get_bits(phycap_map[vco_trim_addr[1] - addr], GENMASK(4, 0)); +} + +static void rtw8922d_vco_trim(struct rtw89_dev *rtwdev) +{ + struct rtw89_power_trim_info *info = &rtwdev->pwr_trim; + + if (!info->pg_vco_trim) + return; + + rtw89_write_rf(rtwdev, RF_PATH_A, RR_VCO, RR_VCO_VAL, info->vco_trim[0]); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_VCO, RR_VCO_VAL, info->vco_trim[1]); +} + +#define THM_TRIM_POSITIVE_MASK BIT(6) +#define THM_TRIM_MAGNITUDE_MASK GENMASK(5, 0) +#define THM_TRIM_MAX (15) +#define THM_TRIM_MIN (-15) + +static void rtw8922d_phycap_parsing_thermal_trim(struct rtw89_dev *rtwdev, + u8 *phycap_map) +{ + static const u32 thm_trim_addr[RF_PATH_NUM_8922D] = {0x1706, 0x1732}; + struct rtw89_power_trim_info *info = &rtwdev->pwr_trim; + u32 addr = rtwdev->chip->phycap_addr; + bool pg = true; + u8 pg_th; + s8 val; + u8 i; + + for (i = 0; i < RF_PATH_NUM_8922D; i++) { + pg_th = phycap_map[thm_trim_addr[i] - addr]; + if (pg_th == 0xff) { + memset(info->thermal_trim, 0, sizeof(info->thermal_trim)); + pg = false; + goto out; + } + + val = u8_get_bits(pg_th, THM_TRIM_MAGNITUDE_MASK); + + if (!(pg_th & THM_TRIM_POSITIVE_MASK)) + val *= -1; + + if (val <= THM_TRIM_MIN || val >= THM_TRIM_MAX) { + val = 0; + info->thermal_trim[i] = 0; + } else { + info->thermal_trim[i] = pg_th; + } + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[THERMAL][TRIM] path=%d thermal_trim=0x%x (%d)\n", + i, pg_th, val); + } + +out: + info->pg_thermal_trim = pg; +} + +static void rtw8922d_thermal_trim(struct rtw89_dev *rtwdev) +{ + struct rtw89_power_trim_info *info = &rtwdev->pwr_trim; + u8 thermal; + int i; + + for (i = 0; i < RF_PATH_NUM_8922D; i++) { + thermal = info->pg_thermal_trim ? info->thermal_trim[i] : 0; + rtw89_write_rf(rtwdev, i, RR_TM, RR_TM_TRM, thermal & 0x7f); + } +} + +static void rtw8922d_phycap_parsing_pa_bias_trim(struct rtw89_dev *rtwdev, + u8 *phycap_map) +{ + static const u32 pabias_trim_addr[RF_PATH_NUM_8922D] = {0x1707, 0x1733}; + static const u32 check_pa_pad_trim_addr = 0x1700; + struct rtw89_power_trim_info *info = &rtwdev->pwr_trim; + u32 addr = rtwdev->chip->phycap_addr; + bool pg = true; + u8 val; + u8 i; + + val = phycap_map[check_pa_pad_trim_addr - addr]; + if (val == 0xff) { + pg = false; + goto out; + } + + for (i = 0; i < RF_PATH_NUM_8922D; i++) { + info->pa_bias_trim[i] = phycap_map[pabias_trim_addr[i] - addr]; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[PA_BIAS][TRIM] path=%d pa_bias_trim=0x%x\n", + i, info->pa_bias_trim[i]); + } + +out: + info->pg_pa_bias_trim = pg; +} + +static void rtw8922d_pa_bias_trim(struct rtw89_dev *rtwdev) +{ + struct rtw89_power_trim_info *info = &rtwdev->pwr_trim; + u8 pabias_2g, pabias_5g; + u8 i; + + if (!info->pg_pa_bias_trim) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[PA_BIAS][TRIM] no PG, do nothing\n"); + + return; + } + + for (i = 0; i < RF_PATH_NUM_8922D; i++) { + pabias_2g = FIELD_GET(GENMASK(3, 0), info->pa_bias_trim[i]); + pabias_5g = FIELD_GET(GENMASK(7, 4), info->pa_bias_trim[i]); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[PA_BIAS][TRIM] path=%d 2G=0x%x 5G=0x%x\n", + i, pabias_2g, pabias_5g); + + rtw89_write_rf(rtwdev, i, RR_BIASA, RR_BIASA_TXG_V1, pabias_2g); + rtw89_write_rf(rtwdev, i, RR_BIASA, RR_BIASA_TXA_V1, pabias_5g); + } +} + +static void rtw8922d_phycap_parsing_pad_bias_trim(struct rtw89_dev *rtwdev, + u8 *phycap_map) +{ + static const u32 pad_bias_trim_addr[RF_PATH_NUM_8922D] = {0x1708, 0x1734}; + struct rtw89_power_trim_info *info = &rtwdev->pwr_trim; + u32 addr = rtwdev->chip->phycap_addr; + u8 i; + + if (!info->pg_pa_bias_trim) + return; + + for (i = 0; i < RF_PATH_NUM_8922D; i++) { + info->pad_bias_trim[i] = phycap_map[pad_bias_trim_addr[i] - addr]; + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[PAD_BIAS][TRIM] path=%d pad_bias_trim=0x%x\n", + i, info->pad_bias_trim[i]); + } +} + +static void rtw8922d_pad_bias_trim(struct rtw89_dev *rtwdev) +{ + struct rtw89_power_trim_info *info = &rtwdev->pwr_trim; + u8 pad_bias_2g, pad_bias_5g; + u8 i; + + if (!info->pg_pa_bias_trim) { + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[PAD_BIAS][TRIM] no PG, do nothing\n"); + return; + } + + for (i = 0; i < RF_PATH_NUM_8922D; i++) { + pad_bias_2g = u8_get_bits(info->pad_bias_trim[i], GENMASK(3, 0)); + pad_bias_5g = u8_get_bits(info->pad_bias_trim[i], GENMASK(7, 4)); + + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[PAD_BIAS][TRIM] path=%d 2G=0x%x 5G=0x%x\n", + i, pad_bias_2g, pad_bias_5g); + + rtw89_write_rf(rtwdev, i, RR_BIASA, RR_BIASD_TXG_V1, pad_bias_2g); + rtw89_write_rf(rtwdev, i, RR_BIASA, RR_BIASD_TXA_V1, pad_bias_5g); + } +} + +static int rtw8922d_read_phycap(struct rtw89_dev *rtwdev, u8 *phycap_map) +{ + rtw8922d_phycap_parsing_vco_trim(rtwdev, phycap_map); + rtw8922d_phycap_parsing_thermal_trim(rtwdev, phycap_map); + rtw8922d_phycap_parsing_pa_bias_trim(rtwdev, phycap_map); + rtw8922d_phycap_parsing_pad_bias_trim(rtwdev, phycap_map); + + return 0; +} + +static void rtw8922d_power_trim(struct rtw89_dev *rtwdev) +{ + rtw8922d_vco_trim(rtwdev); + rtw8922d_thermal_trim(rtwdev); + rtw8922d_pa_bias_trim(rtwdev); + rtw8922d_pad_bias_trim(rtwdev); +} + MODULE_FIRMWARE(RTW8922D_MODULE_FIRMWARE); MODULE_FIRMWARE(RTW8922DS_MODULE_FIRMWARE); MODULE_AUTHOR("Realtek Corporation"); From 56f48df95e7a0d2325ef1ead73228d413c713100 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 24 Mar 2026 14:20:47 +0800 Subject: [PATCH 049/124] wifi: rtw89: 8922d: add set channel of MAC part The set channel is a key function to switch to specific operating channel. For MAC part, configure hardware according to channel bandwidth, and enable CCK rate for 2GHz band only. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260324062049.52266-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/reg.h | 1 + drivers/net/wireless/realtek/rtw89/rtw8922d.c | 92 +++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 179006c8e499..2195f576facc 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -6801,6 +6801,7 @@ #define R_BE_MUEDCA_EN 0x10370 #define R_BE_MUEDCA_EN_C1 0x14370 #define B_BE_SIFS_TIMEOUT_TB_T2_MASK GENMASK(30, 24) +#define B_BE_SIFS_MACTXEN_TB_T1_DOT05US_MASK GENMASK(23, 16) #define B_BE_SIFS_MACTXEN_TB_T1_MASK GENMASK(22, 16) #define B_BE_MUEDCA_WMM_SEL BIT(8) #define B_BE_SET_MUEDCATIMER_TF_MASK GENMASK(5, 4) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index cbe8e067ae55..a341734ef54d 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -5,6 +5,7 @@ #include "debug.h" #include "efuse.h" #include "mac.h" +#include "phy.h" #include "reg.h" #include "rtw8922d.h" @@ -835,6 +836,97 @@ static void rtw8922d_power_trim(struct rtw89_dev *rtwdev) rtw8922d_pad_bias_trim(rtwdev); } +static void rtw8922d_set_channel_mac(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + u8 mac_idx) +{ + u32 sub_carr = rtw89_mac_reg_by_idx(rtwdev, R_BE_TX_SUB_BAND_VALUE, mac_idx); + u32 chk_rate = rtw89_mac_reg_by_idx(rtwdev, R_BE_TXRATE_CHK, mac_idx); + u32 rf_mod = rtw89_mac_reg_by_idx(rtwdev, R_BE_WMAC_RFMOD, mac_idx); + u8 txsb20 = 0, txsb40 = 0, txsb80 = 0; + u8 rf_mod_val, chk_rate_mask, sifs; + u32 txsb; + u32 reg; + + switch (chan->band_width) { + case RTW89_CHANNEL_WIDTH_160: + txsb80 = rtw89_phy_get_txsb(rtwdev, chan, RTW89_CHANNEL_WIDTH_80); + fallthrough; + case RTW89_CHANNEL_WIDTH_80: + txsb40 = rtw89_phy_get_txsb(rtwdev, chan, RTW89_CHANNEL_WIDTH_40); + fallthrough; + case RTW89_CHANNEL_WIDTH_40: + txsb20 = rtw89_phy_get_txsb(rtwdev, chan, RTW89_CHANNEL_WIDTH_20); + break; + default: + break; + } + + switch (chan->band_width) { + case RTW89_CHANNEL_WIDTH_160: + rf_mod_val = BE_WMAC_RFMOD_160M; + txsb = u32_encode_bits(txsb20, B_BE_TXSB_20M_MASK) | + u32_encode_bits(txsb40, B_BE_TXSB_40M_MASK) | + u32_encode_bits(txsb80, B_BE_TXSB_80M_MASK); + break; + case RTW89_CHANNEL_WIDTH_80: + rf_mod_val = BE_WMAC_RFMOD_80M; + txsb = u32_encode_bits(txsb20, B_BE_TXSB_20M_MASK) | + u32_encode_bits(txsb40, B_BE_TXSB_40M_MASK); + break; + case RTW89_CHANNEL_WIDTH_40: + rf_mod_val = BE_WMAC_RFMOD_40M; + txsb = u32_encode_bits(txsb20, B_BE_TXSB_20M_MASK); + break; + case RTW89_CHANNEL_WIDTH_20: + default: + rf_mod_val = BE_WMAC_RFMOD_20M; + txsb = 0; + break; + } + + if (txsb20 <= BE_PRI20_BITMAP_MAX) + txsb |= u32_encode_bits(BIT(txsb20), B_BE_PRI20_BITMAP_MASK); + + rtw89_write8_mask(rtwdev, rf_mod, B_BE_WMAC_RFMOD_MASK, rf_mod_val); + rtw89_write32(rtwdev, sub_carr, txsb); + + switch (chan->band_type) { + case RTW89_BAND_2G: + chk_rate_mask = B_BE_BAND_MODE; + break; + case RTW89_BAND_5G: + case RTW89_BAND_6G: + chk_rate_mask = B_BE_CHECK_CCK_EN | B_BE_RTS_LIMIT_IN_OFDM6; + break; + default: + rtw89_warn(rtwdev, "Invalid band_type:%d\n", chan->band_type); + return; + } + + rtw89_write8_clr(rtwdev, chk_rate, B_BE_BAND_MODE | B_BE_CHECK_CCK_EN | + B_BE_RTS_LIMIT_IN_OFDM6); + rtw89_write8_set(rtwdev, chk_rate, chk_rate_mask); + + switch (chan->band_width) { + case RTW89_CHANNEL_WIDTH_160: + sifs = 0x8C; + break; + case RTW89_CHANNEL_WIDTH_80: + sifs = 0x8A; + break; + case RTW89_CHANNEL_WIDTH_40: + sifs = 0x84; + break; + case RTW89_CHANNEL_WIDTH_20: + default: + sifs = 0x82; + } + + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_MUEDCA_EN, mac_idx); + rtw89_write32_mask(rtwdev, reg, B_BE_SIFS_MACTXEN_TB_T1_DOT05US_MASK, sifs); +} + MODULE_FIRMWARE(RTW8922D_MODULE_FIRMWARE); MODULE_FIRMWARE(RTW8922DS_MODULE_FIRMWARE); MODULE_AUTHOR("Realtek Corporation"); From 521f3a653dbe52b290607b5e0adf4730a3fcd9d6 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 24 Mar 2026 14:20:48 +0800 Subject: [PATCH 050/124] wifi: rtw89: 8922d: add set channel of BB part The set channel of BB part is the main part, which according to channel and bandwidth to configure CCK SCO, RX gain of LNA and TIA programmed in efuse for CCK and OFDM rate, and spur elimination of CSI and NBI tones. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260324062049.52266-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 2 + drivers/net/wireless/realtek/rtw89/reg.h | 62 ++ drivers/net/wireless/realtek/rtw89/rtw8922d.c | 820 ++++++++++++++++++ 3 files changed, 884 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index cde46ed21d32..696a1d92d62b 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -5933,6 +5933,8 @@ struct rtw89_phy_efuse_gain { s8 offset2[RF_PATH_MAX][RTW89_GAIN_OFFSET_NR]; /* S(8, 0) */ s8 offset_base[RTW89_PHY_NUM]; /* S(8, 4) */ s8 rssi_base[RTW89_PHY_NUM]; /* S(8, 4) */ + s8 ref_gain_base[RTW89_PHY_NUM]; /* S(8, 2) */ + s8 cck_rpl_base[RTW89_PHY_NUM]; /* S(8, 0) */ s8 comp[RF_PATH_MAX][RTW89_SUBBAND_NR]; /* S(8, 0) */ }; diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 2195f576facc..5d284f310069 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -8775,6 +8775,7 @@ #define B_P0_HW_ANTSW_DIS_BY_GNT_BT BIT(12) #define B_P0_TRSW_TX_EXTEND GENMASK(3, 0) #define R_MAC_PIN_SEL 0x0734 +#define R_MAC_PIN_SEL_BE4 0x20734 #define B_CH_IDX_SEG0 GENMASK(23, 16) #define R_PLCP_HISTOGRAM 0x0738 #define R_PLCP_HISTOGRAM_BE_V1 0x20738 @@ -10461,6 +10462,13 @@ #define B_SW_SI_DATA_DAT_BE4 GENMASK(19, 0) #define R_SW_SI_READ_ADDR_BE4 0x20378 #define B_SW_SI_READ_ADDR_BE4 GENMASK(10, 0) +#define R_RXBW67_BE4 0x2040C +#define B_RXBW6_BE4 GENMASK(22, 20) +#define B_RXBW7_BE4 GENMASK(25, 23) +#define R_RXBW_BE4 0x20410 +#define B_RXBW_BE4 GENMASK(29, 27) +#define R_ENABLE_CCK0_BE4 0x20700 +#define B_ENABLE_CCK0_BE4 BIT(5) #define R_EDCCA_RPT_SEL_BE4 0x20780 #define R_EDCCA_RPT_SEL_BE4_C1 0x21780 #define B_EDCCA_RPT_SEL_BE4_MSK 0xE0000 @@ -10486,21 +10494,75 @@ #define B_IFS_T3_HIS_BE4 GENMASK(15, 0) #define B_IFS_T4_HIS_BE4 GENMASK(31, 16) +#define R_TXPWR_RSTB0_BE4 0x2250C +#define B_TXPWR_RSTB0_BE4 BIT(16) +#define R_TXPWR_RSTB1_BE4 0x2260C +#define B_TXPWR_RSTB1_BE4 BIT(16) + +#define R_OFDM_OFST_P0_BE4 0x240C8 +#define B_OFDM_OFST_P0_BE4 GENMASK(31, 24) #define R_PATH0_RXIDX_INIT_BE4 0x24108 #define B_PATH0_RXIDX_INIT_BE4 GENMASK(29, 25) #define R_PATH0_LNA_INIT_BE4 0x24158 #define B_PATH0_LNA_INIT_IDX_BE4 GENMASK(14, 12) +#define R_BAND_SEL0_BE4 0x24160 +#define B_BAND_SEL0_BE4 BIT(26) #define R_PATH0_TIA_INIT_BE4 0x24168 #define B_PATH0_TIA_INIT_IDX_BE4 BIT(18) +#define R_OFDM_RPL_BIAS_P0_BE4 0x2420C +#define B_OFDM_RPL_BIAS_P0_BE4 GENMASK(11, 2) +#define R_OFDM_OFST_P1_BE4 0x244C8 +#define B_OFDM_OFST_P1_BE4 GENMASK(31, 24) #define R_PATH1_RXIDX_INIT_BE4 0x24508 #define B_PATH1_RXIDX_INIT_BE4 GENMASK(29, 25) #define R_PATH1_LNA_INIT_BE4 0x24558 #define B_PATH1_LNA_INIT_IDX_BE4 GENMASK(14, 12) +#define R_BAND_SEL1_BE4 0x24560 +#define B_BAND_SEL1_BE4 BIT(26) #define R_PATH1_TIA_INIT_BE4 0x24568 #define B_PATH1_TIA_INIT_IDX_BE4 BIT(18) +#define R_OFDM_RPL_BIAS_P1_BE4 0x2460C +#define B_OFDM_RPL_BIAS_P1_BE4 GENMASK(11, 2) #define R_TX_CFR_MANUAL_EN_BE4 0x2483C #define B_TX_CFR_MANUAL_EN_BE4_M BIT(30) +#define R_PCOEFF0_BE4 0x24880 +#define B_PCOEFF01_BE4 GENMASK(23, 0) +#define R_PCOEFF2_BE4 0x24884 +#define B_PCOEFF23_BE4 GENMASK(23, 0) +#define R_PCOEFF4_BE4 0x24888 +#define B_PCOEFF45_BE4 GENMASK(23, 0) +#define R_PCOEFF6_BE4 0x2488C +#define B_PCOEFF67_BE4 GENMASK(23, 0) +#define R_PCOEFF8_BE4 0x24890 +#define B_PCOEFF89_BE4 GENMASK(23, 0) +#define R_PCOEFF10_BE4 0x24894 +#define B_PCOEFF10_BE4 GENMASK(23, 0) +#define R_PCOEFF12_BE4 0x24898 +#define B_PCOEFF12_BE4 GENMASK(23, 0) +#define R_PCOEFF14_BE4 0x2489C +#define B_PCOEFF14_BE4 GENMASK(23, 0) +#define R_BW_BE4 0x24EE4 +#define B_BW_BE4 GENMASK(6, 4) +#define B_PRISB_BE4 GENMASK(3, 0) +#define R_FC0_BE4 0x24EE8 +#define B_FC0_BE4 GENMASK(12, 0) +#define R_FC0_INV_BE4 0x24EF4 +#define B_FC0_INV_BE4 GENMASK(15, 0) +#define R_CCK_RPL_OFST_BE4 0x26084 +#define B_CCK_RPL_OFST_BE4 GENMASK(7, 0) +#define R_BK_FC0_INV_BE4 0x2608C +#define B_BK_FC0_INV_BE4 GENMASK(18, 0) +#define R_CCK_FC0_INV_BE4 0x26090 +#define B_CCK_FC0_INV_BE4 GENMASK(18, 0) +#define R_GAIN_BIAS_BE4 0x260A0 +#define B_GAIN_BIAS_BW20_BE4 GENMASK(11, 6) +#define B_GAIN_BIAS_BW40_BE4 GENMASK(17, 12) +#define R_AWGN_DET_BE4 0x2668C +#define B_AWGN_DET_BE4 GENMASK(17, 9) +#define R_CSI_WGT_BE4 0x26770 +#define B_CSI_WGT_EN_BE4 BIT(0) +#define B_CSI_WGT_IDX_BE4 GENMASK(31, 20) #define R_CHINFO_OPT_BE4 0x267C8 #define B_CHINFO_OPT_BE4 GENMASK(14, 13) #define R_CHINFO_NX_BE4 0x267D0 diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index a341734ef54d..cae92e2abd85 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -8,6 +8,7 @@ #include "phy.h" #include "reg.h" #include "rtw8922d.h" +#include "util.h" #define RTW8922D_FW_FORMAT_MAX 0 #define RTW8922D_FW_BASENAME "rtw89/rtw8922d_fw" @@ -927,6 +928,825 @@ static void rtw8922d_set_channel_mac(struct rtw89_dev *rtwdev, rtw89_write32_mask(rtwdev, reg, B_BE_SIFS_MACTXEN_TB_T1_DOT05US_MASK, sifs); } +static const u32 rtw8922d_sco_barker_threshold[14] = { + 0x1fe4f, 0x1ff5e, 0x2006c, 0x2017b, 0x2028a, 0x20399, 0x204a8, 0x205b6, + 0x206c5, 0x207d4, 0x208e3, 0x209f2, 0x20b00, 0x20d8a +}; + +static const u32 rtw8922d_sco_cck_threshold[14] = { + 0x2bdac, 0x2bf21, 0x2c095, 0x2c209, 0x2c37e, 0x2c4f2, 0x2c666, 0x2c7db, + 0x2c94f, 0x2cac3, 0x2cc38, 0x2cdac, 0x2cf21, 0x2d29e +}; + +static int rtw8922d_ctrl_sco_cck(struct rtw89_dev *rtwdev, + u8 primary_ch, enum rtw89_bandwidth bw, + enum rtw89_phy_idx phy_idx) +{ + u8 ch_element; + + if (primary_ch >= 14) + return -EINVAL; + + ch_element = primary_ch - 1; + + rtw89_phy_write32_idx(rtwdev, R_BK_FC0_INV_BE4, B_BK_FC0_INV_BE4, + rtw8922d_sco_barker_threshold[ch_element], + phy_idx); + rtw89_phy_write32_idx(rtwdev, R_CCK_FC0_INV_BE4, B_CCK_FC0_INV_BE4, + rtw8922d_sco_cck_threshold[ch_element], + phy_idx); + + return 0; +} + +static void rtw8922d_ctrl_ch_core(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + u16 central_freq = chan->freq; + u16 sco; + + if (chan->band_type == RTW89_BAND_2G) { + rtw89_phy_write32_idx(rtwdev, R_BAND_SEL0_BE4, B_BAND_SEL0_BE4, + 1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BAND_SEL1_BE4, B_BAND_SEL1_BE4, + 1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_ENABLE_CCK0_BE4, B_ENABLE_CCK0_BE4, + 1, phy_idx); + } else { + rtw89_phy_write32_idx(rtwdev, R_BAND_SEL0_BE4, B_BAND_SEL0_BE4, + 0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BAND_SEL1_BE4, B_BAND_SEL1_BE4, + 0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_ENABLE_CCK0_BE4, B_ENABLE_CCK0_BE4, + 0, phy_idx); + } + + rtw89_phy_write32_idx(rtwdev, R_FC0_BE4, B_FC0_BE4, central_freq, phy_idx); + + sco = phy_div((BIT(0) << 27) + (central_freq / 2), central_freq); + rtw89_phy_write32_idx(rtwdev, R_FC0_INV_BE4, B_FC0_INV_BE4, sco, phy_idx); +} + +struct rtw8922d_bb_gain { + u32 gain_g[BB_PATH_NUM_8922D]; + u32 gain_a[BB_PATH_NUM_8922D]; + u32 gain_g_mask; + u32 gain_a_mask; +}; + +static const struct rtw89_reg_def rpl_comp_bw160[RTW89_BW20_SC_160M] = { + { .addr = 0x241E8, .mask = 0xFF00}, + { .addr = 0x241E8, .mask = 0xFF0000}, + { .addr = 0x241E8, .mask = 0xFF000000}, + { .addr = 0x241EC, .mask = 0xFF}, + { .addr = 0x241EC, .mask = 0xFF00}, + { .addr = 0x241EC, .mask = 0xFF0000}, + { .addr = 0x241EC, .mask = 0xFF000000}, + { .addr = 0x241F0, .mask = 0xFF} +}; + +static const struct rtw89_reg_def rpl_comp_bw80[RTW89_BW20_SC_80M] = { + { .addr = 0x241F4, .mask = 0xFF}, + { .addr = 0x241F4, .mask = 0xFF00}, + { .addr = 0x241F4, .mask = 0xFF0000}, + { .addr = 0x241F4, .mask = 0xFF000000} +}; + +static const struct rtw89_reg_def rpl_comp_bw40[RTW89_BW20_SC_40M] = { + { .addr = 0x241F0, .mask = 0xFF0000}, + { .addr = 0x241F0, .mask = 0xFF000000} +}; + +static const struct rtw89_reg_def rpl_comp_bw20[RTW89_BW20_SC_20M] = { + { .addr = 0x241F0, .mask = 0xFF00} +}; + +static const struct rtw8922d_bb_gain bb_gain_lna[LNA_GAIN_NUM] = { + { .gain_g = {0x2409C, 0x2449C}, .gain_a = {0x2406C, 0x2446C}, + .gain_g_mask = 0xFF00, .gain_a_mask = 0xFF}, + { .gain_g = {0x2409C, 0x2449C}, .gain_a = {0x2406C, 0x2446C}, + .gain_g_mask = 0xFF000000, .gain_a_mask = 0xFF0000}, + { .gain_g = {0x240A0, 0x244A0}, .gain_a = {0x24070, 0x24470}, + .gain_g_mask = 0xFF00, .gain_a_mask = 0xFF}, + { .gain_g = {0x240A0, 0x244A0}, .gain_a = {0x24070, 0x24470}, + .gain_g_mask = 0xFF000000, .gain_a_mask = 0xFF0000}, + { .gain_g = {0x240A4, 0x244A4}, .gain_a = {0x24074, 0x24474}, + .gain_g_mask = 0xFF00, .gain_a_mask = 0xFF}, + { .gain_g = {0x240A4, 0x244A4}, .gain_a = {0x24074, 0x24474}, + .gain_g_mask = 0xFF000000, .gain_a_mask = 0xFF0000}, + { .gain_g = {0x240A8, 0x244A8}, .gain_a = {0x24078, 0x24478}, + .gain_g_mask = 0xFF00, .gain_a_mask = 0xFF}, +}; + +static const struct rtw8922d_bb_gain bb_gain_tia[TIA_GAIN_NUM] = { + { .gain_g = {0x24054, 0x24454}, .gain_a = {0x24054, 0x24454}, + .gain_g_mask = 0x7FC0000, .gain_a_mask = 0x1FF}, + { .gain_g = {0x24058, 0x24458}, .gain_a = {0x24054, 0x24454}, + .gain_g_mask = 0x1FF, .gain_a_mask = 0x3FE00 }, +}; + +static const struct rtw8922d_bb_gain bb_op1db_lna[LNA_GAIN_NUM] = { + { .gain_g = {0x240AC, 0x244AC}, .gain_a = {0x24078, 0x24478}, + .gain_g_mask = 0xFF00, .gain_a_mask = 0xFF000000}, + { .gain_g = {0x240AC, 0x244AC}, .gain_a = {0x2407C, 0x2447C}, + .gain_g_mask = 0xFF0000, .gain_a_mask = 0xFF}, + { .gain_g = {0x240AC, 0x244AC}, .gain_a = {0x2407C, 0x2447C}, + .gain_g_mask = 0xFF000000, .gain_a_mask = 0xFF00}, + { .gain_g = {0x240B0, 0x244B0}, .gain_a = {0x2407C, 0x2447C}, + .gain_g_mask = 0xFF, .gain_a_mask = 0xFF0000}, + { .gain_g = {0x240B0, 0x244B0}, .gain_a = {0x2407C, 0x2447C}, + .gain_g_mask = 0xFF00, .gain_a_mask = 0xFF000000}, + { .gain_g = {0x240B0, 0x244B0}, .gain_a = {0x24080, 0x24480}, + .gain_g_mask = 0xFF0000, .gain_a_mask = 0xFF}, + { .gain_g = {0x240B0, 0x244B0}, .gain_a = {0x24080, 0x24480}, + .gain_g_mask = 0xFF000000, .gain_a_mask = 0xFF00}, +}; + +static const struct rtw8922d_bb_gain bb_op1db_tia_lna[TIA_LNA_OP1DB_NUM] = { + { .gain_g = {0x240B4, 0x244B4}, .gain_a = {0x24080, 0x24480}, + .gain_g_mask = 0xFF0000, .gain_a_mask = 0xFF000000}, + { .gain_g = {0x240B4, 0x244B4}, .gain_a = {0x24084, 0x24484}, + .gain_g_mask = 0xFF000000, .gain_a_mask = 0xFF}, + { .gain_g = {0x240B8, 0x244B8}, .gain_a = {0x24084, 0x24484}, + .gain_g_mask = 0xFF, .gain_a_mask = 0xFF00}, + { .gain_g = {0x240B8, 0x244B8}, .gain_a = {0x24084, 0x24484}, + .gain_g_mask = 0xFF00, .gain_a_mask = 0xFF0000}, + { .gain_g = {0x240B8, 0x244B8}, .gain_a = {0x24084, 0x24484}, + .gain_g_mask = 0xFF0000, .gain_a_mask = 0xFF000000}, + { .gain_g = {0x240B8, 0x244B8}, .gain_a = {0x24088, 0x24488}, + .gain_g_mask = 0xFF000000, .gain_a_mask = 0xFF}, + { .gain_g = {0x240BC, 0x244BC}, .gain_a = {0x24088, 0x24488}, + .gain_g_mask = 0xFF, .gain_a_mask = 0xFF00}, + { .gain_g = {0x240BC, 0x244BC}, .gain_a = {0x24088, 0x24488}, + .gain_g_mask = 0xFF00, .gain_a_mask = 0xFF0000}, +}; + +static void rtw8922d_set_rpl_gain(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_rf_path path, + enum rtw89_phy_idx phy_idx) +{ + const struct rtw89_phy_bb_gain_info_be *gain = &rtwdev->bb_gain.be; + u8 gain_band = rtw89_subband_to_gain_band_be(chan->subband_type); + u32 reg_path_ofst = 0; + u32 mask; + s32 val; + u32 reg; + int i; + + if (path == RF_PATH_B) + reg_path_ofst = 0x400; + + for (i = 0; i < RTW89_BW20_SC_160M; i++) { + reg = rpl_comp_bw160[i].addr | reg_path_ofst; + mask = rpl_comp_bw160[i].mask; + val = gain->rpl_ofst_160[gain_band][path][i]; + rtw89_phy_write32_idx(rtwdev, reg, mask, val, phy_idx); + } + + for (i = 0; i < RTW89_BW20_SC_80M; i++) { + reg = rpl_comp_bw80[i].addr | reg_path_ofst; + mask = rpl_comp_bw80[i].mask; + val = gain->rpl_ofst_80[gain_band][path][i]; + rtw89_phy_write32_idx(rtwdev, reg, mask, val, phy_idx); + } + + for (i = 0; i < RTW89_BW20_SC_40M; i++) { + reg = rpl_comp_bw40[i].addr | reg_path_ofst; + mask = rpl_comp_bw40[i].mask; + val = gain->rpl_ofst_40[gain_band][path][i]; + rtw89_phy_write32_idx(rtwdev, reg, mask, val, phy_idx); + } + + for (i = 0; i < RTW89_BW20_SC_20M; i++) { + reg = rpl_comp_bw20[i].addr | reg_path_ofst; + mask = rpl_comp_bw20[i].mask; + val = gain->rpl_ofst_20[gain_band][path][i]; + rtw89_phy_write32_idx(rtwdev, reg, mask, val, phy_idx); + } +} + +static void rtw8922d_set_lna_tia_gain(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_rf_path path, + enum rtw89_phy_idx phy_idx) +{ + const struct rtw89_phy_bb_gain_info_be *gain = &rtwdev->bb_gain.be; + u8 gain_band = rtw89_subband_to_gain_band_be(chan->subband_type); + enum rtw89_phy_bb_bw_be bw_type; + u32 mask; + s32 val; + u32 reg; + int i; + + bw_type = chan->band_width <= RTW89_CHANNEL_WIDTH_40 ? + RTW89_BB_BW_20_40 : RTW89_BB_BW_80_160_320; + + for (i = 0; i < LNA_GAIN_NUM; i++) { + if (chan->band_type == RTW89_BAND_2G) { + reg = bb_gain_lna[i].gain_g[path]; + mask = bb_gain_lna[i].gain_g_mask; + } else { + reg = bb_gain_lna[i].gain_a[path]; + mask = bb_gain_lna[i].gain_a_mask; + } + val = gain->lna_gain[gain_band][bw_type][path][i]; + rtw89_phy_write32_idx(rtwdev, reg, mask, val, phy_idx); + } + + for (i = 0; i < TIA_GAIN_NUM; i++) { + if (chan->band_type == RTW89_BAND_2G) { + reg = bb_gain_tia[i].gain_g[path]; + mask = bb_gain_tia[i].gain_g_mask; + } else { + reg = bb_gain_tia[i].gain_a[path]; + mask = bb_gain_tia[i].gain_a_mask; + } + val = gain->tia_gain[gain_band][bw_type][path][i]; + rtw89_phy_write32_idx(rtwdev, reg, mask, val, phy_idx); + } +} + +static void rtw8922d_set_op1db(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_rf_path path, + enum rtw89_phy_idx phy_idx) +{ + const struct rtw89_phy_bb_gain_info_be *gain = &rtwdev->bb_gain.be; + u8 gain_band = rtw89_subband_to_gain_band_be(chan->subband_type); + enum rtw89_phy_bb_bw_be bw_type; + u32 mask; + s32 val; + u32 reg; + int i; + + bw_type = chan->band_width <= RTW89_CHANNEL_WIDTH_40 ? + RTW89_BB_BW_20_40 : RTW89_BB_BW_80_160_320; + + for (i = 0; i < LNA_GAIN_NUM; i++) { + if (chan->band_type == RTW89_BAND_2G) { + reg = bb_op1db_lna[i].gain_g[path]; + mask = bb_op1db_lna[i].gain_g_mask; + } else { + reg = bb_op1db_lna[i].gain_a[path]; + mask = bb_op1db_lna[i].gain_a_mask; + } + val = gain->lna_op1db[gain_band][bw_type][path][i]; + rtw89_phy_write32_idx(rtwdev, reg, mask, val, phy_idx); + } + + for (i = 0; i < TIA_LNA_OP1DB_NUM; i++) { + if (chan->band_type == RTW89_BAND_2G) { + reg = bb_op1db_tia_lna[i].gain_g[path]; + mask = bb_op1db_tia_lna[i].gain_g_mask; + } else { + reg = bb_op1db_tia_lna[i].gain_a[path]; + mask = bb_op1db_tia_lna[i].gain_a_mask; + } + val = gain->tia_lna_op1db[gain_band][bw_type][path][i]; + rtw89_phy_write32_idx(rtwdev, reg, mask, val, phy_idx); + } +} + +static void rtw8922d_set_gain(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_rf_path path, + enum rtw89_phy_idx phy_idx) +{ + rtw8922d_set_rpl_gain(rtwdev, chan, path, phy_idx); + rtw8922d_set_lna_tia_gain(rtwdev, chan, path, phy_idx); + rtw8922d_set_op1db(rtwdev, chan, path, phy_idx); +} + +static s8 rtw8922d_get_rx_gain_by_chan(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_rf_path path, bool is_cck) +{ + struct rtw89_phy_efuse_gain *gain = &rtwdev->efuse_gain; + enum rtw89_gain_offset band; + u8 fc_ch = chan->channel; + s8 normal_efuse = 0; + + if (path > RF_PATH_B) + return 0; + + if (is_cck) { + if (fc_ch >= 1 && fc_ch <= 7) + return gain->offset[path][RTW89_GAIN_OFFSET_2G_CCK]; + else if (fc_ch >= 8 && fc_ch <= 14) + return gain->offset2[path][RTW89_GAIN_OFFSET_2G_CCK]; + + return 0; + } + + band = rtw89_subband_to_gain_offset_band_of_ofdm(chan->subband_type); + + if (band == RTW89_GAIN_OFFSET_2G_OFDM) { + if (fc_ch >= 1 && fc_ch <= 7) + normal_efuse = gain->offset[path][band]; + else if (fc_ch >= 8 && fc_ch <= 14) + normal_efuse = gain->offset2[path][band]; + } else if (band == RTW89_GAIN_OFFSET_5G_LOW) { + if (fc_ch == 50) + normal_efuse = (gain->offset[path][band] + gain->offset2[path][band]) >> 1; + else if (fc_ch >= 36 && fc_ch <= 48) + normal_efuse = gain->offset[path][band]; + else if (fc_ch >= 52 && fc_ch <= 64) + normal_efuse = gain->offset2[path][band]; + + } else if (band == RTW89_GAIN_OFFSET_5G_MID) { + if (fc_ch == 122) + normal_efuse = (gain->offset[path][band] + gain->offset2[path][band]) >> 1; + else if (fc_ch >= 100 && fc_ch <= 120) + normal_efuse = gain->offset[path][band]; + else if (fc_ch >= 124 && fc_ch <= 144) + normal_efuse = gain->offset2[path][band]; + } else if (band == RTW89_GAIN_OFFSET_5G_HIGH) { + if (fc_ch == 163) + normal_efuse = (gain->offset[path][band] + gain->offset2[path][band]) >> 1; + else if (fc_ch >= 149 && fc_ch <= 161) + normal_efuse = gain->offset[path][band]; + else if (fc_ch >= 165 && fc_ch <= 177) + normal_efuse = gain->offset2[path][band]; + } else if (band == RTW89_GAIN_OFFSET_6G_L0) { + if (fc_ch == 15) + normal_efuse = (gain->offset[path][band] + gain->offset2[path][band]) >> 1; + else if (fc_ch >= 1 && fc_ch <= 13) + normal_efuse = gain->offset[path][band]; + else if (fc_ch >= 17 && fc_ch <= 29) + normal_efuse = gain->offset2[path][band]; + } else if (band == RTW89_GAIN_OFFSET_6G_L1) { + if (fc_ch == 47) + normal_efuse = (gain->offset[path][band] + gain->offset2[path][band]) >> 1; + else if (fc_ch >= 33 && fc_ch <= 45) + normal_efuse = gain->offset[path][band]; + else if (fc_ch >= 49 && fc_ch <= 61) + normal_efuse = gain->offset2[path][band]; + } else if (band == RTW89_GAIN_OFFSET_6G_M0) { + if (fc_ch == 79) + normal_efuse = (gain->offset[path][band] + gain->offset2[path][band]) >> 1; + else if (fc_ch >= 65 && fc_ch <= 77) + normal_efuse = gain->offset[path][band]; + else if (fc_ch >= 81 && fc_ch <= 93) + normal_efuse = gain->offset2[path][band]; + } else if (band == RTW89_GAIN_OFFSET_6G_M1) { + if (fc_ch == 111) + normal_efuse = (gain->offset[path][band] + gain->offset2[path][band]) >> 1; + else if (fc_ch >= 97 && fc_ch <= 109) + normal_efuse = gain->offset[path][band]; + else if (fc_ch >= 113 && fc_ch <= 125) + normal_efuse = gain->offset2[path][band]; + } else if (band == RTW89_GAIN_OFFSET_6G_H0) { + if (fc_ch == 143) + normal_efuse = (gain->offset[path][band] + gain->offset2[path][band]) >> 1; + else if (fc_ch >= 129 && fc_ch <= 141) + normal_efuse = gain->offset[path][band]; + else if (fc_ch >= 145 && fc_ch <= 157) + normal_efuse = gain->offset2[path][band]; + } else if (band == RTW89_GAIN_OFFSET_6G_H1) { + if (fc_ch == 175) + normal_efuse = (gain->offset[path][band] + gain->offset2[path][band]) >> 1; + else if (fc_ch >= 161 && fc_ch <= 173) + normal_efuse = gain->offset[path][band]; + else if (fc_ch >= 177 && fc_ch <= 189) + normal_efuse = gain->offset2[path][band]; + } else if (band == RTW89_GAIN_OFFSET_6G_UH0) { + if (fc_ch == 207) + normal_efuse = (gain->offset[path][band] + gain->offset2[path][band]) >> 1; + else if (fc_ch >= 193 && fc_ch <= 205) + normal_efuse = gain->offset[path][band]; + else if (fc_ch >= 209 && fc_ch <= 221) + normal_efuse = gain->offset2[path][band]; + } else if (band == RTW89_GAIN_OFFSET_6G_UH1) { + if (fc_ch == 239) + normal_efuse = (gain->offset[path][band] + gain->offset2[path][band]) >> 1; + else if (fc_ch >= 225 && fc_ch <= 237) + normal_efuse = gain->offset[path][band]; + else if (fc_ch >= 241 && fc_ch <= 253) + normal_efuse = gain->offset2[path][band]; + } else { + normal_efuse = gain->offset[path][band]; + } + + return normal_efuse; +} + +static void rtw8922d_calc_rx_gain_normal_cck(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_rf_path path, + enum rtw89_phy_idx phy_idx, + struct rtw89_phy_calc_efuse_gain *calc) +{ + struct rtw89_phy_efuse_gain *gain = &rtwdev->efuse_gain; + s8 rx_gain_offset; + + rx_gain_offset = -rtw8922d_get_rx_gain_by_chan(rtwdev, chan, path, true); + + if (chan->band_width == RTW89_CHANNEL_WIDTH_40) + rx_gain_offset += (3 << 2); /* compensate RPL loss of 3dB */ + + calc->cck_mean_gain_bias = (rx_gain_offset & 0x3) << 1; + calc->cck_rpl_ofst = (rx_gain_offset >> 2) + gain->cck_rpl_base[phy_idx]; +} + +static void rtw8922d_set_rx_gain_normal_cck(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_rf_path path, + enum rtw89_phy_idx phy_idx) +{ + struct rtw89_phy_calc_efuse_gain calc = {}; + + rtw8922d_calc_rx_gain_normal_cck(rtwdev, chan, path, phy_idx, &calc); + + rtw89_phy_write32_idx(rtwdev, R_GAIN_BIAS_BE4, B_GAIN_BIAS_BW20_BE4, + calc.cck_mean_gain_bias, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_GAIN_BIAS_BE4, B_GAIN_BIAS_BW40_BE4, + calc.cck_mean_gain_bias, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_CCK_RPL_OFST_BE4, B_CCK_RPL_OFST_BE4, + calc.cck_rpl_ofst, phy_idx); +} + +static void rtw8922d_calc_rx_gain_normal_ofdm(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_rf_path path, + enum rtw89_phy_idx phy_idx, + struct rtw89_phy_calc_efuse_gain *calc) +{ + struct rtw89_phy_efuse_gain *gain = &rtwdev->efuse_gain; + s8 rx_gain_offset; + + rx_gain_offset = rtw8922d_get_rx_gain_by_chan(rtwdev, chan, path, false); + calc->rssi_ofst = (rx_gain_offset + gain->ref_gain_base[phy_idx]) & 0xff; +} + +static void rtw8922d_set_rx_gain_normal_ofdm(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_rf_path path, + enum rtw89_phy_idx phy_idx) +{ + static const u32 rssi_ofst_addr[2] = {R_OFDM_OFST_P0_BE4, R_OFDM_OFST_P1_BE4}; + static const u32 rssi_ofst_addr_m[2] = {B_OFDM_OFST_P0_BE4, B_OFDM_OFST_P1_BE4}; + static const u32 rpl_bias_comp[2] = {R_OFDM_RPL_BIAS_P0_BE4, R_OFDM_RPL_BIAS_P1_BE4}; + static const u32 rpl_bias_comp_m[2] = {B_OFDM_RPL_BIAS_P0_BE4, B_OFDM_RPL_BIAS_P1_BE4}; + struct rtw89_phy_calc_efuse_gain calc = {}; + + rtw8922d_calc_rx_gain_normal_ofdm(rtwdev, chan, path, phy_idx, &calc); + + rtw89_phy_write32_idx(rtwdev, rssi_ofst_addr[path], rssi_ofst_addr_m[path], + calc.rssi_ofst, phy_idx); + rtw89_phy_write32_idx(rtwdev, rpl_bias_comp[path], rpl_bias_comp_m[path], 0, phy_idx); +} + +static void rtw8922d_set_rx_gain_normal(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_rf_path path, + enum rtw89_phy_idx phy_idx) +{ + struct rtw89_phy_efuse_gain *gain = &rtwdev->efuse_gain; + + if (!gain->offset_valid) + return; + + if (chan->band_type == RTW89_BAND_2G) + rtw8922d_set_rx_gain_normal_cck(rtwdev, chan, path, phy_idx); + + rtw8922d_set_rx_gain_normal_ofdm(rtwdev, chan, path, phy_idx); +} + +static void rtw8922d_set_cck_parameters(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + u8 regd = rtw89_regd_get(rtwdev, chan->band_type); + u8 central_ch = chan->channel; + + if (central_ch == 14) { + rtw89_phy_write32_idx(rtwdev, R_PCOEFF0_BE4, B_PCOEFF01_BE4, 0x3b13ff, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF2_BE4, B_PCOEFF23_BE4, 0x1c42de, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF4_BE4, B_PCOEFF45_BE4, 0xfdb0ad, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF6_BE4, B_PCOEFF67_BE4, 0xf60f6e, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF8_BE4, B_PCOEFF89_BE4, 0xfd8f92, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF10_BE4, B_PCOEFF10_BE4, 0x2d011, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF12_BE4, B_PCOEFF12_BE4, 0x1c02c, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF14_BE4, B_PCOEFF14_BE4, 0xfff00a, phy_idx); + + return; + } + + if (regd == RTW89_FCC) { + rtw89_phy_write32_idx(rtwdev, R_PCOEFF0_BE4, B_PCOEFF01_BE4, 0x39A3BC, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF2_BE4, B_PCOEFF23_BE4, 0x2AA339, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF4_BE4, B_PCOEFF45_BE4, 0x15B202, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF6_BE4, B_PCOEFF67_BE4, 0x0550C7, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF8_BE4, B_PCOEFF89_BE4, 0xfe0009, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF10_BE4, B_PCOEFF10_BE4, 0xfd7fd3, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF12_BE4, B_PCOEFF12_BE4, 0xfeffe2, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF14_BE4, B_PCOEFF14_BE4, 0xffeff8, phy_idx); + } else { + rtw89_phy_write32_idx(rtwdev, R_PCOEFF0_BE4, B_PCOEFF01_BE4, 0x3d23ff, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF2_BE4, B_PCOEFF23_BE4, 0x29b354, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF4_BE4, B_PCOEFF45_BE4, 0xfc1c8, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF6_BE4, B_PCOEFF67_BE4, 0xfdb053, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF8_BE4, B_PCOEFF89_BE4, 0xf86f9a, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF10_BE4, B_PCOEFF10_BE4, 0xfaef92, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF12_BE4, B_PCOEFF12_BE4, 0xfe5fcc, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_PCOEFF14_BE4, B_PCOEFF14_BE4, 0xffdff5, phy_idx); + } +} + +static void rtw8922d_ctrl_ch(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + u16 central_freq = chan->freq; + u8 band = chan->band_type; + u8 chan_idx; + + if (!central_freq) { + rtw89_warn(rtwdev, "Invalid central_freq\n"); + return; + } + + rtw8922d_ctrl_ch_core(rtwdev, chan, phy_idx); + + chan_idx = rtw89_encode_chan_idx(rtwdev, chan->primary_channel, band); + rtw89_phy_write32_idx(rtwdev, R_MAC_PIN_SEL_BE4, B_CH_IDX_SEG0, chan_idx, phy_idx); + + rtw8922d_set_gain(rtwdev, chan, RF_PATH_A, phy_idx); + rtw8922d_set_gain(rtwdev, chan, RF_PATH_B, phy_idx); + + rtw8922d_set_rx_gain_normal(rtwdev, chan, RF_PATH_A, phy_idx); + rtw8922d_set_rx_gain_normal(rtwdev, chan, RF_PATH_B, phy_idx); + + if (band == RTW89_BAND_2G) + rtw8922d_set_cck_parameters(rtwdev, chan, phy_idx); +} + +static void rtw8922d_ctrl_bw(struct rtw89_dev *rtwdev, u8 pri_sb, u8 bw, + enum rtw89_phy_idx phy_idx) +{ + switch (bw) { + default: + case RTW89_CHANNEL_WIDTH_20: + rtw89_phy_write32_idx(rtwdev, R_BW_BE4, B_BW_BE4, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BW_BE4, B_PRISB_BE4, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RXBW_BE4, B_RXBW_BE4, 0x2, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RXBW67_BE4, B_RXBW6_BE4, 0x2, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RXBW67_BE4, B_RXBW7_BE4, 0x2, phy_idx); + break; + case RTW89_CHANNEL_WIDTH_40: + rtw89_phy_write32_idx(rtwdev, R_BW_BE4, B_BW_BE4, 0x1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BW_BE4, B_PRISB_BE4, pri_sb, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RXBW_BE4, B_RXBW_BE4, 0x3, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RXBW67_BE4, B_RXBW6_BE4, 0x3, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RXBW67_BE4, B_RXBW7_BE4, 0x3, phy_idx); + break; + case RTW89_CHANNEL_WIDTH_80: + rtw89_phy_write32_idx(rtwdev, R_BW_BE4, B_BW_BE4, 0x2, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BW_BE4, B_PRISB_BE4, pri_sb, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RXBW_BE4, B_RXBW_BE4, 0x4, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RXBW67_BE4, B_RXBW6_BE4, 0x4, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RXBW67_BE4, B_RXBW7_BE4, 0x4, phy_idx); + break; + case RTW89_CHANNEL_WIDTH_160: + rtw89_phy_write32_idx(rtwdev, R_BW_BE4, B_BW_BE4, 0x3, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BW_BE4, B_PRISB_BE4, pri_sb, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RXBW_BE4, B_RXBW_BE4, 0x5, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RXBW67_BE4, B_RXBW6_BE4, 0x5, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RXBW67_BE4, B_RXBW7_BE4, 0x5, phy_idx); + break; + } +} + +static const u16 spur_nbi_a[] = {6400}; +static const u16 spur_csi[] = {6400}; + +static u32 rtw8922d_spur_freq(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, + bool nbi_or_csi, enum rtw89_rf_path path) +{ + static const u16 cbw[RTW89_CHANNEL_WIDTH_ORDINARY_NUM] = { + 20, 40, 80, 160, 320, + }; + u16 freq_lower, freq_upper, freq; + const u16 *spur_freq; + int spur_freq_nr, i; + + if (rtwdev->hal.aid != RTL8922D_AID7060) + return 0; + + if (nbi_or_csi && path == RF_PATH_A) { + spur_freq = spur_nbi_a; + spur_freq_nr = ARRAY_SIZE(spur_nbi_a); + } else if (!nbi_or_csi) { + spur_freq = spur_csi; + spur_freq_nr = ARRAY_SIZE(spur_csi); + } else { + return 0; + } + + if (chan->band_width >= RTW89_CHANNEL_WIDTH_ORDINARY_NUM) + return 0; + + freq_lower = chan->freq - cbw[chan->band_width] / 2; + freq_upper = chan->freq + cbw[chan->band_width] / 2; + + for (i = 0; i < spur_freq_nr; i++) { + freq = spur_freq[i]; + + if (freq >= freq_lower && freq <= freq_upper) + return freq; + } + + return 0; +} + +#define CARRIER_SPACING_312_5 312500 /* 312.5 kHz */ +#define CARRIER_SPACING_78_125 78125 /* 78.125 kHz */ +#define MAX_TONE_NUM 2048 + +static void rtw8922d_set_csi_tone_idx(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + s32 freq_diff, csi_idx, csi_tone_idx; + u32 spur_freq; + + spur_freq = rtw8922d_spur_freq(rtwdev, chan, false, RF_PATH_AB); + if (spur_freq == 0) { + rtw89_phy_write32_idx(rtwdev, R_CSI_WGT_BE4, B_CSI_WGT_EN_BE4, + 0, phy_idx); + return; + } + + freq_diff = (spur_freq - chan->freq) * 1000000; + csi_idx = s32_div_u32_round_closest(freq_diff, CARRIER_SPACING_78_125); + s32_div_u32_round_down(csi_idx, MAX_TONE_NUM, &csi_tone_idx); + + rtw89_phy_write32_idx(rtwdev, R_CSI_WGT_BE4, B_CSI_WGT_IDX_BE4, + csi_tone_idx, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_CSI_WGT_BE4, B_CSI_WGT_EN_BE4, 1, phy_idx); +} + +static const struct rtw89_nbi_reg_def rtw8922d_nbi_reg_def[] = { + [RF_PATH_A] = { + .notch1_idx = {0x241A0, 0xFF}, + .notch1_frac_idx = {0x241A0, 0xC00}, + .notch1_en = {0x241A0, 0x1000}, + .notch2_idx = {0x241AC, 0xFF}, + .notch2_frac_idx = {0x241AC, 0xC00}, + .notch2_en = {0x241AC, 0x1000}, + }, + [RF_PATH_B] = { + .notch1_idx = {0x245A0, 0xFF}, + .notch1_frac_idx = {0x245A0, 0xC00}, + .notch1_en = {0x245A0, 0x1000}, + .notch2_idx = {0x245AC, 0xFF}, + .notch2_frac_idx = {0x245AC, 0xC00}, + .notch2_en = {0x245AC, 0x1000}, + }, +}; + +static void rtw8922d_set_nbi_tone_idx(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_rf_path path, + enum rtw89_phy_idx phy_idx) +{ + const struct rtw89_nbi_reg_def *nbi = &rtw8922d_nbi_reg_def[path]; + s32 nbi_frac_idx, nbi_frac_tone_idx; + s32 nbi_idx, nbi_tone_idx; + bool notch2_chk = false; + u32 spur_freq, fc; + s32 freq_diff; + + spur_freq = rtw8922d_spur_freq(rtwdev, chan, true, path); + if (spur_freq == 0) { + rtw89_phy_write32_idx(rtwdev, nbi->notch1_en.addr, + nbi->notch1_en.mask, 0, phy_idx); + rtw89_phy_write32_idx(rtwdev, nbi->notch2_en.addr, + nbi->notch2_en.mask, 0, phy_idx); + return; + } + + fc = chan->freq; + if (chan->band_width == RTW89_CHANNEL_WIDTH_160) { + fc = (spur_freq > fc) ? fc + 40 : fc - 40; + if ((fc > spur_freq && + chan->channel < chan->primary_channel) || + (fc < spur_freq && + chan->channel > chan->primary_channel)) + notch2_chk = true; + } + + freq_diff = (spur_freq - fc) * 1000000; + nbi_idx = s32_div_u32_round_down(freq_diff, CARRIER_SPACING_312_5, + &nbi_frac_idx); + + if (chan->band_width == RTW89_CHANNEL_WIDTH_20) { + s32_div_u32_round_down(nbi_idx + 32, 64, &nbi_tone_idx); + } else { + u16 tone_para = (chan->band_width == RTW89_CHANNEL_WIDTH_40) ? + 128 : 256; + + s32_div_u32_round_down(nbi_idx, tone_para, &nbi_tone_idx); + } + nbi_frac_tone_idx = + s32_div_u32_round_closest(nbi_frac_idx, CARRIER_SPACING_78_125); + + if (chan->band_width == RTW89_CHANNEL_WIDTH_160 && notch2_chk) { + rtw89_phy_write32_idx(rtwdev, nbi->notch2_idx.addr, + nbi->notch2_idx.mask, nbi_tone_idx, phy_idx); + rtw89_phy_write32_idx(rtwdev, nbi->notch2_frac_idx.addr, + nbi->notch2_frac_idx.mask, nbi_frac_tone_idx, + phy_idx); + rtw89_phy_write32_idx(rtwdev, nbi->notch2_en.addr, + nbi->notch2_en.mask, 0, phy_idx); + rtw89_phy_write32_idx(rtwdev, nbi->notch2_en.addr, + nbi->notch2_en.mask, 1, phy_idx); + rtw89_phy_write32_idx(rtwdev, nbi->notch1_en.addr, + nbi->notch1_en.mask, 0, phy_idx); + } else { + rtw89_phy_write32_idx(rtwdev, nbi->notch1_idx.addr, + nbi->notch1_idx.mask, nbi_tone_idx, phy_idx); + rtw89_phy_write32_idx(rtwdev, nbi->notch1_frac_idx.addr, + nbi->notch1_frac_idx.mask, nbi_frac_tone_idx, + phy_idx); + rtw89_phy_write32_idx(rtwdev, nbi->notch1_en.addr, + nbi->notch1_en.mask, 0, phy_idx); + rtw89_phy_write32_idx(rtwdev, nbi->notch1_en.addr, + nbi->notch1_en.mask, 1, phy_idx); + rtw89_phy_write32_idx(rtwdev, nbi->notch2_en.addr, + nbi->notch2_en.mask, 0, phy_idx); + } +} + +static void rtw8922d_spur_elimination(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + rtw8922d_set_csi_tone_idx(rtwdev, chan, phy_idx); + rtw8922d_set_nbi_tone_idx(rtwdev, chan, RF_PATH_A, phy_idx); + rtw8922d_set_nbi_tone_idx(rtwdev, chan, RF_PATH_B, phy_idx); +} + +static void rtw8922d_tssi_reset(struct rtw89_dev *rtwdev, + enum rtw89_rf_path path, + enum rtw89_phy_idx phy_idx) +{ + if (rtwdev->mlo_dbcc_mode == MLO_1_PLUS_1_1RF) { + if (phy_idx == RTW89_PHY_0) { + rtw89_phy_write32_mask(rtwdev, R_TXPWR_RSTB0_BE4, + B_TXPWR_RSTB0_BE4, 0x0); + rtw89_phy_write32_mask(rtwdev, R_TXPWR_RSTB0_BE4, + B_TXPWR_RSTB0_BE4, 0x1); + } else { + rtw89_phy_write32_mask(rtwdev, R_TXPWR_RSTB1_BE4, + B_TXPWR_RSTB1_BE4, 0x0); + rtw89_phy_write32_mask(rtwdev, R_TXPWR_RSTB1_BE4, + B_TXPWR_RSTB1_BE4, 0x1); + } + } else { + rtw89_phy_write32_mask(rtwdev, R_TXPWR_RSTB0_BE4, B_TXPWR_RSTB0_BE4, 0x0); + rtw89_phy_write32_mask(rtwdev, R_TXPWR_RSTB0_BE4, B_TXPWR_RSTB0_BE4, 0x1); + rtw89_phy_write32_mask(rtwdev, R_TXPWR_RSTB1_BE4, B_TXPWR_RSTB1_BE4, 0x0); + rtw89_phy_write32_mask(rtwdev, R_TXPWR_RSTB1_BE4, B_TXPWR_RSTB1_BE4, 0x1); + } +} + +static void rtw8922d_set_channel_bb(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + struct rtw89_hal *hal = &rtwdev->hal; + bool cck_en = chan->band_type == RTW89_BAND_2G; + u8 pri_sb = chan->pri_sb_idx; + u32 val; + + rtw89_phy_bb_wrap_set_rfsi_ct_opt(rtwdev, phy_idx); + rtw8922d_ctrl_ch(rtwdev, chan, phy_idx); + rtw8922d_ctrl_bw(rtwdev, pri_sb, chan->band_width, phy_idx); + rtw89_phy_bb_wrap_set_rfsi_bandedge_ch(rtwdev, chan, phy_idx); + + if (cck_en) + rtw8922d_ctrl_sco_cck(rtwdev, chan->primary_channel, + chan->band_width, phy_idx); + + rtw8922d_spur_elimination(rtwdev, chan, phy_idx); + + if (hal->cid == RTL8922D_CID7025) { + if (chan->band_width == RTW89_CHANNEL_WIDTH_160) + val = 0x1f9; + else if (chan->band_width == RTW89_CHANNEL_WIDTH_80) + val = 0x1f5; + else + val = 0x1e2; + + rtw89_phy_write32_idx(rtwdev, R_AWGN_DET_BE4, B_AWGN_DET_BE4, val, phy_idx); + } + + rtw8922d_tssi_reset(rtwdev, RF_PATH_AB, phy_idx); +} + MODULE_FIRMWARE(RTW8922D_MODULE_FIRMWARE); MODULE_FIRMWARE(RTW8922DS_MODULE_FIRMWARE); MODULE_AUTHOR("Realtek Corporation"); From 1b622535a577ab24e07de8bb68bd5dc20b50ccf0 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 24 Mar 2026 14:20:49 +0800 Subject: [PATCH 051/124] wifi: rtw89: 8922d: add set channel of RF part The set channel of RF part is to configure channel and bandwidth on a register. The function to encode channel and bandwidth into register value will be implemented by coming patch. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260324062049.52266-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 1 + .../net/wireless/realtek/rtw89/rtw8922d_rfk.c | 33 +++++++++++++++++++ .../net/wireless/realtek/rtw89/rtw8922d_rfk.h | 14 ++++++++ 3 files changed, 48 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index cae92e2abd85..1b5fc6c9ea85 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -8,6 +8,7 @@ #include "phy.h" #include "reg.h" #include "rtw8922d.h" +#include "rtw8922d_rfk.h" #include "util.h" #define RTW8922D_FW_FORMAT_MAX 0 diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c new file mode 100644 index 000000000000..6b35d196cb81 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2026 Realtek Corporation + */ + +#include "phy.h" +#include "reg.h" +#include "rtw8922d.h" +#include "rtw8922d_rfk.h" + +static +void rtw8922d_ctl_band_ch_bw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, + const struct rtw89_chan *chan) +{ + u8 synpath; + u32 rf18; + + synpath = rtw89_phy_get_syn_sel(rtwdev, phy); + rf18 = rtw89_chip_chan_to_rf18_val(rtwdev, chan); + + rtw89_write_rf(rtwdev, synpath, RR_RSV1, RFREG_MASK, 0x0); + rtw89_write_rf(rtwdev, synpath, RR_MOD, RFREG_MASK, 0x30000); + rtw89_write_rf(rtwdev, synpath, RR_CFGCH, RFREG_MASK, rf18); + fsleep(400); + rtw89_write_rf(rtwdev, synpath, RR_RSV1, RFREG_MASK, 0x1); + rtw89_write_rf(rtwdev, synpath, RR_CFGCH_V1, RFREG_MASK, rf18); +} + +void rtw8922d_set_channel_rf(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + rtw8922d_ctl_band_ch_bw(rtwdev, phy_idx, chan); +} diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h new file mode 100644 index 000000000000..03af1f0497ac --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* Copyright(c) 2026 Realtek Corporation + */ + +#ifndef __RTW89_8922D_RFK_H__ +#define __RTW89_8922D_RFK_H__ + +#include "core.h" + +void rtw8922d_set_channel_rf(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx); + +#endif From 86a4c63c01e4c0ca4f47952e371c7219c09bfc9e Mon Sep 17 00:00:00 2001 From: Chin-Yen Lee Date: Wed, 25 Mar 2026 15:21:23 +0800 Subject: [PATCH 052/124] wifi: rtw89: wow: use struct style to fill WOW wakeup control H2C command The WOW wakeup control command is used to tell firmware the content of wakeup feature. Use struct instead of macros to fill the data. Signed-off-by: Chin-Yen Lee Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260325072130.41751-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 21 ++++++---- drivers/net/wireless/realtek/rtw89/fw.h | 56 ++++++------------------- 2 files changed, 26 insertions(+), 51 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 45d8c5e70084..701cea9a771e 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -9705,38 +9705,43 @@ fail: return ret; } -#define H2C_WAKEUP_CTRL_LEN 4 int rtw89_fw_h2c_wow_wakeup_ctrl(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, bool enable) { struct rtw89_wow_param *rtw_wow = &rtwdev->wow; + struct rtw89_h2c_wow_wakeup_ctrl *h2c; struct sk_buff *skb; + u32 len = sizeof(*h2c); u8 macid = rtwvif_link->mac_id; int ret; - skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_WAKEUP_CTRL_LEN); + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); if (!skb) { rtw89_err(rtwdev, "failed to alloc skb for wakeup ctrl\n"); return -ENOMEM; } - skb_put(skb, H2C_WAKEUP_CTRL_LEN); + skb_put(skb, len); + h2c = (struct rtw89_h2c_wow_wakeup_ctrl *)skb->data; if (rtw_wow->pattern_cnt) - RTW89_SET_WOW_WAKEUP_CTRL_PATTERN_MATCH_ENABLE(skb->data, enable); + h2c->w0 |= le32_encode_bits(enable, + RTW89_H2C_WOW_WAKEUP_CTRL_W0_PATTERN_MATCH_ENABLE); if (test_bit(RTW89_WOW_FLAG_EN_MAGIC_PKT, rtw_wow->flags)) - RTW89_SET_WOW_WAKEUP_CTRL_MAGIC_ENABLE(skb->data, enable); + h2c->w0 |= le32_encode_bits(enable, + RTW89_H2C_WOW_WAKEUP_CTRL_W0_MAGIC_ENABLE); if (test_bit(RTW89_WOW_FLAG_EN_DISCONNECT, rtw_wow->flags)) - RTW89_SET_WOW_WAKEUP_CTRL_DEAUTH_ENABLE(skb->data, enable); + h2c->w0 |= le32_encode_bits(enable, + RTW89_H2C_WOW_WAKEUP_CTRL_W0_DEAUTH_ENABLE); - RTW89_SET_WOW_WAKEUP_CTRL_MAC_ID(skb->data, macid); + h2c->w0 |= le32_encode_bits(macid, RTW89_H2C_WOW_WAKEUP_CTRL_W0_MAC_ID); rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, H2C_CAT_MAC, H2C_CL_MAC_WOW, H2C_FUNC_WAKEUP_CTRL, 0, 1, - H2C_WAKEUP_CTRL_LEN); + len); ret = rtw89_h2c_tx(rtwdev, skb, false); if (ret) { diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 57e3b464c0a2..cf86fade84a2 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -2219,50 +2219,20 @@ struct rtw89_h2c_cfg_nlo { #define RTW89_H2C_NLO_W0_IGNORE_CIPHER BIT(2) #define RTW89_H2C_NLO_W0_MACID GENMASK(31, 24) -static inline void RTW89_SET_WOW_WAKEUP_CTRL_PATTERN_MATCH_ENABLE(void *h2c, u32 val) -{ - le32p_replace_bits((__le32 *)h2c, val, BIT(0)); -} +struct rtw89_h2c_wow_wakeup_ctrl { + __le32 w0; +} __packed; -static inline void RTW89_SET_WOW_WAKEUP_CTRL_MAGIC_ENABLE(void *h2c, u32 val) -{ - le32p_replace_bits((__le32 *)h2c, val, BIT(1)); -} - -static inline void RTW89_SET_WOW_WAKEUP_CTRL_HW_UNICAST_ENABLE(void *h2c, u32 val) -{ - le32p_replace_bits((__le32 *)h2c, val, BIT(2)); -} - -static inline void RTW89_SET_WOW_WAKEUP_CTRL_FW_UNICAST_ENABLE(void *h2c, u32 val) -{ - le32p_replace_bits((__le32 *)h2c, val, BIT(3)); -} - -static inline void RTW89_SET_WOW_WAKEUP_CTRL_DEAUTH_ENABLE(void *h2c, u32 val) -{ - le32p_replace_bits((__le32 *)h2c, val, BIT(4)); -} - -static inline void RTW89_SET_WOW_WAKEUP_CTRL_REKEYP_ENABLE(void *h2c, u32 val) -{ - le32p_replace_bits((__le32 *)h2c, val, BIT(5)); -} - -static inline void RTW89_SET_WOW_WAKEUP_CTRL_EAP_ENABLE(void *h2c, u32 val) -{ - le32p_replace_bits((__le32 *)h2c, val, BIT(6)); -} - -static inline void RTW89_SET_WOW_WAKEUP_CTRL_ALL_DATA_ENABLE(void *h2c, u32 val) -{ - le32p_replace_bits((__le32 *)h2c, val, BIT(7)); -} - -static inline void RTW89_SET_WOW_WAKEUP_CTRL_MAC_ID(void *h2c, u32 val) -{ - le32p_replace_bits((__le32 *)h2c, val, GENMASK(31, 24)); -} +#define RTW89_H2C_WOW_WAKEUP_CTRL_W0_PATTERN_MATCH_ENABLE BIT(0) +#define RTW89_H2C_WOW_WAKEUP_CTRL_W0_MAGIC_ENABLE BIT(1) +#define RTW89_H2C_WOW_WAKEUP_CTRL_W0_HW_UNICAST_ENABLE BIT(2) +#define RTW89_H2C_WOW_WAKEUP_CTRL_W0_FW_UNICAST_ENABLE BIT(3) +#define RTW89_H2C_WOW_WAKEUP_CTRL_W0_DEAUTH_ENABLE BIT(4) +#define RTW89_H2C_WOW_WAKEUP_CTRL_W0_REKEYP_ENABLE BIT(5) +#define RTW89_H2C_WOW_WAKEUP_CTRL_W0_EAP_ENABLE BIT(6) +#define RTW89_H2C_WOW_WAKEUP_CTRL_W0_ALL_DATA_ENABLE BIT(7) +#define RTW89_H2C_WOW_WAKEUP_CTRL_W0_MAC_ID_EXT GENMASK(23, 16) +#define RTW89_H2C_WOW_WAKEUP_CTRL_W0_MAC_ID GENMASK(31, 24) struct rtw89_h2c_wow_cam_update { __le32 w0; From 4e7a7f57718eaeb07be7d7fcec800e88338fb1ad Mon Sep 17 00:00:00 2001 From: Chin-Yen Lee Date: Wed, 25 Mar 2026 15:21:24 +0800 Subject: [PATCH 053/124] wifi: rtw89: wow: enable MLD address for Magic packet wakeup Under MLO connections, the original Magic Packet configuration only supported Link Addresses for wakeup. Update the setting to support both MLD Address and Link Addresses for wakeup process. Signed-off-by: Chin-Yen Lee Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260325072130.41751-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 8 +++++++- drivers/net/wireless/realtek/rtw89/fw.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 701cea9a771e..13391ec9627c 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -9709,6 +9709,7 @@ int rtw89_fw_h2c_wow_wakeup_ctrl(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, bool enable) { + struct ieee80211_vif *vif = rtwvif_link_to_vif(rtwvif_link); struct rtw89_wow_param *rtw_wow = &rtwdev->wow; struct rtw89_h2c_wow_wakeup_ctrl *h2c; struct sk_buff *skb; @@ -9728,9 +9729,14 @@ int rtw89_fw_h2c_wow_wakeup_ctrl(struct rtw89_dev *rtwdev, if (rtw_wow->pattern_cnt) h2c->w0 |= le32_encode_bits(enable, RTW89_H2C_WOW_WAKEUP_CTRL_W0_PATTERN_MATCH_ENABLE); - if (test_bit(RTW89_WOW_FLAG_EN_MAGIC_PKT, rtw_wow->flags)) + if (test_bit(RTW89_WOW_FLAG_EN_MAGIC_PKT, rtw_wow->flags)) { h2c->w0 |= le32_encode_bits(enable, RTW89_H2C_WOW_WAKEUP_CTRL_W0_MAGIC_ENABLE); + if (ieee80211_vif_is_mld(vif)) + h2c->w0 |= le32_encode_bits(enable, + RTW89_H2C_WOW_WAKEUP_CTRL_W0_MAGIC_MLD_ENABLE); + } + if (test_bit(RTW89_WOW_FLAG_EN_DISCONNECT, rtw_wow->flags)) h2c->w0 |= le32_encode_bits(enable, RTW89_H2C_WOW_WAKEUP_CTRL_W0_DEAUTH_ENABLE); diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index cf86fade84a2..4574a281d352 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -2231,6 +2231,7 @@ struct rtw89_h2c_wow_wakeup_ctrl { #define RTW89_H2C_WOW_WAKEUP_CTRL_W0_REKEYP_ENABLE BIT(5) #define RTW89_H2C_WOW_WAKEUP_CTRL_W0_EAP_ENABLE BIT(6) #define RTW89_H2C_WOW_WAKEUP_CTRL_W0_ALL_DATA_ENABLE BIT(7) +#define RTW89_H2C_WOW_WAKEUP_CTRL_W0_MAGIC_MLD_ENABLE BIT(8) #define RTW89_H2C_WOW_WAKEUP_CTRL_W0_MAC_ID_EXT GENMASK(23, 16) #define RTW89_H2C_WOW_WAKEUP_CTRL_W0_MAC_ID GENMASK(31, 24) From bdc607a67edfb35a671eb3405ce5a750a5ef28b2 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 25 Mar 2026 15:21:25 +0800 Subject: [PATCH 054/124] wifi: rtw89: pci: clear SER ISR when initial and leaving WoWLAN for WiFi 7 chips The PCIE SER is to diagnose PCIE becomes abnormal, relying on IMR settings to trigger interrupt when status is weird. Update settings to disable PHY error flag 9, and clear ISR when initial and leaving WoWLAN. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260325072130.41751-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/pci.h | 3 ++ drivers/net/wireless/realtek/rtw89/pci_be.c | 52 +++++++++++++++++---- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/pci.h b/drivers/net/wireless/realtek/rtw89/pci.h index dc488d9a8884..c7cd34e99349 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.h +++ b/drivers/net/wireless/realtek/rtw89/pci.h @@ -55,6 +55,8 @@ #define B_AX_CALIB_EN BIT(13) #define B_AX_DIV GENMASK(15, 14) #define RAC_SET_PPR_V1 0x31 +#define RAC_ANA40 0x40 +#define PHY_ERR_IMR_DIS (BIT(9) | BIT(0)) #define RAC_ANA41 0x41 #define PHY_ERR_FLAG_EN BIT(6) @@ -1029,6 +1031,7 @@ #define B_BE_SER_PMU_IMR BIT(0) #define R_BE_REG_PL1_ISR 0x34B4 +#define B_PCIE_SER_ALL_ISR 0x7F #define R_BE_RX_APPEND_MODE 0x8920 #define B_BE_APPEND_OFFSET_MASK GENMASK(23, 16) diff --git a/drivers/net/wireless/realtek/rtw89/pci_be.c b/drivers/net/wireless/realtek/rtw89/pci_be.c index dea01d9e57fd..dfffec1ff3c7 100644 --- a/drivers/net/wireless/realtek/rtw89/pci_be.c +++ b/drivers/net/wireless/realtek/rtw89/pci_be.c @@ -351,14 +351,41 @@ static void rtw89_pci_ser_setting_be(struct rtw89_dev *rtwdev) return; rtw89_write32(rtwdev, R_BE_PL1_DBG_INFO, 0x0); - rtw89_write32_set(rtwdev, R_BE_FWS1IMR, B_BE_PCIE_SER_TIMEOUT_INDIC_EN); - rtw89_write32_set(rtwdev, R_BE_SER_PL1_CTRL, B_BE_PL1_SER_PL1_EN); - rtw89_write32_mask(rtwdev, R_BE_SER_PL1_CTRL, B_BE_PL1_TIMER_UNIT_MASK, 1); - val32 = rtw89_read32(rtwdev, R_BE_REG_PL1_MASK); - val32 |= B_BE_SER_PMU_IMR | B_BE_SER_L1SUB_IMR | B_BE_SER_PM_MASTER_IMR | - B_BE_SER_LTSSM_IMR | B_BE_SER_PM_CLK_MASK | B_BE_SER_PCLKREQ_ACK_MASK; - rtw89_write32(rtwdev, R_BE_REG_PL1_MASK, val32); + switch (hal->cv) { + case CHIP_CAV: + case CHIP_CBV: + rtw89_write32_clr(rtwdev, R_BE_SER_PL1_CTRL, B_BE_PL1_SER_PL1_EN); + rtw89_write32_mask(rtwdev, R_BE_SER_PL1_CTRL, + B_BE_PL1_TIMER_UNIT_MASK, PCIE_SER_TIMER_UNIT); + + val32 = rtw89_read32(rtwdev, R_BE_REG_PL1_MASK); + val32 &= ~(B_BE_SER_PMU_IMR | B_BE_SER_L1SUB_IMR | + B_BE_SER_PM_MASTER_IMR | B_BE_SER_LTSSM_IMR | + B_BE_SER_PM_CLK_MASK | B_BE_SER_PCLKREQ_ACK_MASK); + rtw89_write32(rtwdev, R_BE_REG_PL1_MASK, val32); + break; + case CHIP_CCV: + default: + rtw89_write32_clr(rtwdev, R_BE_SER_PL1_CTRL, B_BE_PL1_SER_PL1_EN); + + ret = read_poll_timeout_atomic(rtw89_read32, val32, !val32, + 1, 1000, false, rtwdev, R_BE_REG_PL1_ISR); + if (ret) + rtw89_warn(rtwdev, "[ERR] PCIE SER clear poll fail\n"); + + rtw89_write32_mask(rtwdev, R_BE_SER_PL1_CTRL, + B_BE_PL1_TIMER_UNIT_MASK, PCIE_SER_TIMER_UNIT); + rtw89_write32_set(rtwdev, R_BE_SER_PL1_CTRL, B_BE_PL1_SER_PL1_EN); + + val32 = rtw89_read32(rtwdev, R_BE_REG_PL1_MASK); + val32 |= (B_BE_SER_PMU_IMR | B_BE_SER_PM_MASTER_IMR | + B_BE_SER_LTSSM_IMR | B_BE_SER_PM_CLK_MASK | + B_BE_SER_PCLKREQ_ACK_MASK); + val32 &= ~B_BE_SER_L1SUB_IMR; + rtw89_write32(rtwdev, R_BE_REG_PL1_MASK, val32); + break; + } return; @@ -366,6 +393,11 @@ be2_chips: rtw89_write32_clr(rtwdev, R_BE_PCIE_SER_DBG, B_BE_PCIE_SER_FLUSH_RSTB); rtw89_write32_set(rtwdev, R_BE_PCIE_SER_DBG, B_BE_PCIE_SER_FLUSH_RSTB); + rtw89_write16_clr(rtwdev, RAC_DIRECT_OFFESET_L0_G1 + + RAC_ANA40 * RAC_MULT, PHY_ERR_IMR_DIS); + rtw89_write16_clr(rtwdev, RAC_DIRECT_OFFESET_L0_G2 + + RAC_ANA40 * RAC_MULT, PHY_ERR_IMR_DIS); + rtw89_write16_clr(rtwdev, RAC_DIRECT_OFFESET_L0_G1 + RAC_ANA41 * RAC_MULT, PHY_ERR_FLAG_EN); rtw89_write16_clr(rtwdev, RAC_DIRECT_OFFESET_L0_G2 + @@ -378,6 +410,7 @@ be2_chips: val32 = rtw89_read32(rtwdev, R_BE_SER_PL1_CTRL); val32 &= ~B_BE_PL1_SER_PL1_EN; rtw89_write32(rtwdev, R_BE_SER_PL1_CTRL, val32); + rtw89_write32(rtwdev, R_BE_REG_PL1_ISR, B_PCIE_SER_ALL_ISR); ret = read_poll_timeout_atomic(rtw89_read32, val32, !val32, 1, 1000, false, rtwdev, R_BE_REG_PL1_ISR); @@ -385,9 +418,10 @@ be2_chips: rtw89_warn(rtwdev, "[ERR] PCIE SER clear poll fail\n"); val32 = rtw89_read32(rtwdev, R_BE_REG_PL1_MASK); - val32 |= B_BE_SER_PMU_IMR | B_BE_SER_L1SUB_IMR | B_BE_SER_PM_MASTER_IMR | + val32 |= B_BE_SER_PMU_IMR | B_BE_SER_PM_MASTER_IMR | B_BE_SER_LTSSM_IMR | B_BE_SER_PM_CLK_MASK | B_BE_SER_PCLKREQ_ACK_MASK | B_BE_SER_LTSSM_UNSTABLE_MASK; + val32 &= ~B_BE_SER_L1SUB_IMR; rtw89_write32(rtwdev, R_BE_REG_PL1_MASK, val32); rtw89_write32_mask(rtwdev, R_BE_SER_PL1_CTRL, B_BE_PL1_TIMER_UNIT_MASK, @@ -761,6 +795,8 @@ static int __maybe_unused rtw89_pci_resume_be(struct device *dev) goto clear_phy_isr; rtw89_write32_clr(rtwdev, R_BE_SER_PL1_CTRL, B_BE_PL1_SER_PL1_EN); + if (rtwdev->chip->chip_id == RTL8922D) + rtw89_write32(rtwdev, R_BE_REG_PL1_ISR, B_PCIE_SER_ALL_ISR); ret = read_poll_timeout_atomic(rtw89_read32, polling, !polling, 1, 1000, false, rtwdev, R_BE_REG_PL1_ISR); From ba42bb2813aa9574d67ba0f4612ff0acba972d67 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 25 Mar 2026 15:21:26 +0800 Subject: [PATCH 055/124] wifi: rtw89: mac: add specific case to dump mac memory for RTL8922D The RTL8922D can reuse most mac memory addresses, but only RTW89_MAC_MEM_SECURITY_CAM is different from existing one. Add a function to return the specific memory address for RTL8922D. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260325072130.41751-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/debug.c | 2 +- drivers/net/wireless/realtek/rtw89/mac.c | 4 ++-- drivers/net/wireless/realtek/rtw89/mac.h | 13 +++++++++++++ drivers/net/wireless/realtek/rtw89/ser.c | 2 +- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 82849d109cc3..7d8d22311018 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -1129,7 +1129,7 @@ static int rtw89_debug_dump_mac_mem(struct rtw89_dev *rtwdev, pages = len / mem_page_size + 1; start_page = start_addr / mem_page_size; residue = start_addr % mem_page_size; - base_addr = mac->mem_base_addrs[sel]; + base_addr = rtw89_mac_mem_base_addrs(rtwdev, sel); base_addr += start_page * mem_page_size; for (pp = 0; pp < pages; pp++) { diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index 35fd18fe6470..54aad37485d6 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -43,7 +43,7 @@ static void rtw89_mac_mem_write(struct rtw89_dev *rtwdev, u32 offset, u32 val, enum rtw89_mac_mem_sel sel) { const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; - u32 addr = mac->mem_base_addrs[sel] + offset; + u32 addr = rtw89_mac_mem_base_addrs(rtwdev, sel) + offset; rtw89_write32(rtwdev, mac->filter_model_addr, addr); rtw89_write32(rtwdev, mac->indir_access_addr, val); @@ -53,7 +53,7 @@ static u32 rtw89_mac_mem_read(struct rtw89_dev *rtwdev, u32 offset, enum rtw89_mac_mem_sel sel) { const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; - u32 addr = mac->mem_base_addrs[sel] + offset; + u32 addr = rtw89_mac_mem_base_addrs(rtwdev, sel) + offset; rtw89_write32(rtwdev, mac->filter_model_addr, addr); return rtw89_read32(rtwdev, mac->indir_access_addr); diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 88a877556cb3..93bedf056f17 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -334,6 +334,7 @@ enum rtw89_mac_dbg_port_sel { #define NAT25_CAM_BASE_ADDR_BE 0x18820000 #define RXPLD_FLTR_CAM_BASE_ADDR_BE 0x18823000 #define SEC_CAM_BASE_ADDR_BE 0x18824000 +#define SEC_CAM_BASE_ADDR_BE_8922D 0x1882C000 #define WOW_CAM_BASE_ADDR_BE 0x18828000 #define MLD_TBL_BASE_ADDR_BE 0x18829000 #define RX_CLSF_CAM_BASE_ADDR_BE 0x1882A000 @@ -1131,6 +1132,18 @@ struct rtw89_mac_gen_def { extern const struct rtw89_mac_gen_def rtw89_mac_gen_ax; extern const struct rtw89_mac_gen_def rtw89_mac_gen_be; +static inline +u32 rtw89_mac_mem_base_addrs(struct rtw89_dev *rtwdev, u8 sel) +{ + const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; + + if (rtwdev->chip->chip_id == RTL8922D && + sel == RTW89_MAC_MEM_SECURITY_CAM) + return SEC_CAM_BASE_ADDR_BE_8922D; + + return mac->mem_base_addrs[sel]; +} + static inline u32 rtw89_mac_reg_by_idx(struct rtw89_dev *rtwdev, u32 reg_base, u8 band) { diff --git a/drivers/net/wireless/realtek/rtw89/ser.c b/drivers/net/wireless/realtek/rtw89/ser.c index 75220042a9a7..a507ce1fcd63 100644 --- a/drivers/net/wireless/realtek/rtw89/ser.c +++ b/drivers/net/wireless/realtek/rtw89/ser.c @@ -589,7 +589,7 @@ static void ser_mac_mem_dump(struct rtw89_dev *rtwdev, u8 *buf, start_page = start_addr / mem_page_size; residue = start_addr % mem_page_size; - base_addr = mac->mem_base_addrs[sel]; + base_addr = rtw89_mac_mem_base_addrs(rtwdev, sel); base_addr += start_page * mem_page_size; while (cnt < len) { From ce945fb377ce8620272536d734c99064de91dfd0 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 25 Mar 2026 15:21:27 +0800 Subject: [PATCH 056/124] wifi: rtw89: mac: disable pre-load function for RTL8922DE The pre-load function is a MAC function to pre-load TX packets into WiFi device's memory, so it can enhance performance. However, RTL8922DE has not fully verified and fine tune this function, temporarily disable this function. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260325072130.41751-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/mac.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 93bedf056f17..9db9ae219cd6 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -1828,8 +1828,7 @@ static inline bool rtw89_mac_chk_preload_allow(struct rtw89_dev *rtwdev) if (rtwdev->hci.type != RTW89_HCI_TYPE_PCIE) return false; - if (rtwdev->chip->chip_id == RTL8922D && rtwdev->hal.cid == RTL8922D_CID7090) - return true; + /* The RTL8922DE will re-enable pre-load function after verification. */ return false; } From e0b88e052e4c6ca5789c61cef00a90caf08cfc08 Mon Sep 17 00:00:00 2001 From: Eric Huang Date: Wed, 25 Mar 2026 15:21:28 +0800 Subject: [PATCH 057/124] wifi: rtw89: phy: expand PHY page for RTL8922D PHY page range is to define offset from PHY0 to PHY1, and RTL8922D needs to expand page to 0x2E0. Signed-off-by: Eric Huang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260325072130.41751-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy_be.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index 08fd24a55d85..929fac1b10d2 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -199,7 +199,7 @@ static u32 rtw89_phy0_phy1_offset_be_v1(struct rtw89_dev *rtwdev, u32 addr) (phy_page >= 0x240 && phy_page <= 0x24f) || (phy_page >= 0x260 && phy_page <= 0x26f) || (phy_page >= 0x2C0 && phy_page <= 0x2C9) || - (phy_page >= 0x2E4 && phy_page <= 0x2E8) || + (phy_page >= 0x2E0 && phy_page <= 0x2E8) || phy_page == 0x2EE) ofst = 0x1000; else From 9c52ad439e6c50dc54175f180d177f9be73f3e98 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Wed, 25 Mar 2026 15:21:29 +0800 Subject: [PATCH 058/124] wifi: rtw89: phy: load RF parameters relying on ACV for RTL8922D RF parameters are conditional formats with RFE type and CV as arguments, but RTL8922D has many variants and use ACV as argument instead of CV. Add to select proper register values. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260325072130.41751-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index d205561f1f5c..e70d0e283987 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -1808,7 +1808,7 @@ static int rtw89_phy_sel_headline(struct rtw89_dev *rtwdev, } static void rtw89_phy_init_reg(struct rtw89_dev *rtwdev, - const struct rtw89_phy_table *table, + const struct rtw89_phy_table *table, bool by_acv, void (*config)(struct rtw89_dev *rtwdev, const struct rtw89_reg2_def *reg, enum rtw89_rf_path rf_path, @@ -1817,8 +1817,8 @@ static void rtw89_phy_init_reg(struct rtw89_dev *rtwdev, { const struct rtw89_reg2_def *reg; enum rtw89_rf_path rf_path = table->rf_path; + u8 cv = by_acv ? rtwdev->hal.acv : rtwdev->hal.cv; u8 rfe = rtwdev->efuse.rfe_type; - u8 cv = rtwdev->hal.cv; u32 i; u32 headline_size = 0, headline_idx = 0; u32 target = 0, cfg_target; @@ -1885,16 +1885,16 @@ void rtw89_phy_init_bb_reg(struct rtw89_dev *rtwdev) const struct rtw89_phy_table *bb_gain_table; bb_table = elm_info->bb_tbl ? elm_info->bb_tbl : chip->bb_table; - rtw89_phy_init_reg(rtwdev, bb_table, rtw89_phy_config_bb_reg, NULL); + rtw89_phy_init_reg(rtwdev, bb_table, false, rtw89_phy_config_bb_reg, NULL); if (rtwdev->dbcc_en) - rtw89_phy_init_reg(rtwdev, bb_table, rtw89_phy_config_bb_reg, + rtw89_phy_init_reg(rtwdev, bb_table, false, rtw89_phy_config_bb_reg, (void *)RTW89_PHY_1); rtw89_chip_init_txpwr_unit(rtwdev); bb_gain_table = elm_info->bb_gain ? elm_info->bb_gain : chip->bb_gain_table; if (bb_gain_table) - rtw89_phy_init_reg(rtwdev, bb_gain_table, + rtw89_phy_init_reg(rtwdev, bb_gain_table, false, chip->phy_def->config_bb_gain, NULL); rtw89_phy_bb_reset(rtwdev); @@ -2000,6 +2000,7 @@ void rtw89_phy_init_rf_reg(struct rtw89_dev *rtwdev, bool noio) const struct rtw89_chip_info *chip = rtwdev->chip; const struct rtw89_phy_table *rf_table; struct rtw89_fw_h2c_rf_reg_info *rf_reg_info; + bool by_acv = chip->chip_id == RTL8922D; u8 path; rf_reg_info = kzalloc_obj(*rf_reg_info); @@ -2015,7 +2016,7 @@ void rtw89_phy_init_rf_reg(struct rtw89_dev *rtwdev, bool noio) else config = rf_table->config ? rf_table->config : rtw89_phy_config_rf_reg; - rtw89_phy_init_reg(rtwdev, rf_table, config, (void *)rf_reg_info); + rtw89_phy_init_reg(rtwdev, rf_table, by_acv, config, (void *)rf_reg_info); if (rtw89_phy_config_rf_reg_fw(rtwdev, rf_reg_info)) rtw89_warn(rtwdev, "rf path %d reg h2c config failed\n", rf_reg_info->rf_path); @@ -2056,7 +2057,7 @@ static void rtw89_phy_init_rf_nctl(struct rtw89_dev *rtwdev) rtw89_phy_preinit_rf_nctl(rtwdev); nctl_table = elm_info->rf_nctl ? elm_info->rf_nctl : chip->nctl_table; - rtw89_phy_init_reg(rtwdev, nctl_table, rtw89_phy_config_bb_reg, NULL); + rtw89_phy_init_reg(rtwdev, nctl_table, false, rtw89_phy_config_bb_reg, NULL); if (chip->nctl_post_table) rtw89_rfk_parser(rtwdev, chip->nctl_post_table); From e0da9859cfbc9a8cf9a879b00931b89f590de0c1 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Wed, 25 Mar 2026 15:21:30 +0800 Subject: [PATCH 059/124] wifi: rtw89: fw: load TX power elements according to AID For different A-die, there will be different TX power parameters. In FW element header, the corresponding A-die ID will be described. So, compare runtime AID with that to load the target TX power parameters. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260325072130.41751-9-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 13391ec9627c..52db41c67265 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -1153,8 +1153,13 @@ int rtw89_fw_recognize_txpwr_from_elm(struct rtw89_dev *rtwdev, const struct __rtw89_fw_txpwr_element *txpwr_elm = &elm->u.txpwr; const unsigned long offset = arg.offset; struct rtw89_efuse *efuse = &rtwdev->efuse; + struct rtw89_hal *hal = &rtwdev->hal; + u16 aid = le16_to_cpu(elm->aid); struct rtw89_txpwr_conf *conf; + if (aid && aid != hal->aid) + return 1; + if (!rtwdev->rfe_data) { rtwdev->rfe_data = kzalloc_obj(*rtwdev->rfe_data); if (!rtwdev->rfe_data) From 658e3c836969e1624a7572c75684f54ec503c2ed Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Thu, 26 Mar 2026 00:27:15 +0200 Subject: [PATCH 060/124] wifi: rtw88: coex: Ignore BT info byte 5 from RTL8821A Sometimes while watching a Youtube video with Bluetooth headphones the audio has a lot of interruptions, because the 5th byte of the BT info sent by RTL8821AU has strange values, which result in coex_stat->bt_hid_pair_num being 2 or 3. When this happens rtw_coex_freerun_check() returns true, which causes rtw_coex_action_wl_connected() to call rtw_coex_action_freerun() instead of rtw_coex_action_bt_a2dp(). The RTL8821AU vendor driver doesn't do anything with the 5th byte of the BT info, so ignore it here as well. Signed-off-by: Bitterblue Smith Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/bbf06c83-d2ee-4205-8fbb-829e2347586f@gmail.com --- drivers/net/wireless/realtek/rtw88/coex.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw88/coex.c b/drivers/net/wireless/realtek/rtw88/coex.c index ee4007fe6c18..37c336def419 100644 --- a/drivers/net/wireless/realtek/rtw88/coex.c +++ b/drivers/net/wireless/realtek/rtw88/coex.c @@ -3109,6 +3109,9 @@ void rtw_coex_bt_info_notify(struct rtw_dev *rtwdev, u8 *buf, u8 length) for (i = 0; i < COEX_BTINFO_LENGTH; i++) coex_stat->bt_info_c2h[rsp_source][i] = buf[i]; + if (rtwdev->chip->id == RTW_CHIP_TYPE_8821A) + coex_stat->bt_info_c2h[rsp_source][5] = 0; + /* get the same info from bt, skip it */ if (coex_stat->bt_info_c2h[rsp_source][1] == coex_stat->bt_info_lb2 && coex_stat->bt_info_c2h[rsp_source][2] == coex_stat->bt_info_lb3 && From 249716daa9955f9fb8a9a80ed99b3b9071a838de Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Thu, 26 Mar 2026 17:21:19 +0200 Subject: [PATCH 061/124] wifi: rtw88: Fill fw_version member of struct wiphy Let userspace tools like lshw show the firmware version by filling the fw_version member of struct wiphy. Before: configuration: broadcast=yes driver=rtw88_8814au driverversion=6.19.6-arch1-1 firmware=N/A link=no multicast=yes wireless=IEEE 802.11 After: configuration: broadcast=yes driver=rtw88_8814au driverversion=6.19.6-arch1-1 firmware=33.6.0 link=no multicast=yes wireless=IEEE 802.11 Signed-off-by: Bitterblue Smith Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/3701cce1-42c4-4382-9120-cd21012c1b21@gmail.com --- drivers/net/wireless/realtek/rtw88/main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c index c4f9758b4e96..cd9254370fcc 100644 --- a/drivers/net/wireless/realtek/rtw88/main.c +++ b/drivers/net/wireless/realtek/rtw88/main.c @@ -1805,6 +1805,7 @@ static void rtw_load_firmware_cb(const struct firmware *firmware, void *context) { struct rtw_fw_state *fw = context; struct rtw_dev *rtwdev = fw->rtwdev; + struct wiphy *wiphy = rtwdev->hw->wiphy; if (!firmware || !firmware->data) { rtw_err(rtwdev, "failed to request firmware\n"); @@ -1819,6 +1820,11 @@ static void rtw_load_firmware_cb(const struct firmware *firmware, void *context) rtw_info(rtwdev, "%sFirmware version %u.%u.%u, H2C version %u\n", fw->type == RTW_WOWLAN_FW ? "WOW " : "", fw->version, fw->sub_version, fw->sub_index, fw->h2c_version); + + if (fw->type == RTW_NORMAL_FW) + snprintf(wiphy->fw_version, sizeof(wiphy->fw_version), + "%u.%u.%u", + fw->version, fw->sub_version, fw->sub_index); } static int rtw_load_firmware(struct rtw_dev *rtwdev, enum rtw_fw_type type) From c2ca7b9d27f8806772687b547ae08a1127fbe3f9 Mon Sep 17 00:00:00 2001 From: Bitterblue Smith Date: Thu, 26 Mar 2026 17:22:33 +0200 Subject: [PATCH 062/124] wifi: rtw89: Fill fw_version member of struct wiphy Let userspace tools like lshw show the firmware version by filling the fw_version member of struct wiphy. Before: configuration: broadcast=yes driver=rtw89_8852au driverversion=6.19.6-arch1-1 firmware=N/A link=no multicast=yes wireless=IEEE 802.11 After: configuration: broadcast=yes driver=rtw89_8852au driverversion=6.19.6-arch1-1 firmware=0.13.36.2 link=no multicast=yes wireless=IEEE 802.11 Signed-off-by: Bitterblue Smith Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/a46ed12c-387c-4063-849c-c6457bf97810@gmail.com --- drivers/net/wireless/realtek/rtw89/fw.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 52db41c67265..17704f054727 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -730,6 +730,7 @@ static int rtw89_fw_update_ver(struct rtw89_dev *rtwdev, { const struct rtw89_fw_hdr *v0 = (const struct rtw89_fw_hdr *)fw_suit->data; const struct rtw89_fw_hdr_v1 *v1 = (const struct rtw89_fw_hdr_v1 *)fw_suit->data; + struct wiphy *wiphy = rtwdev->hw->wiphy; if (type == RTW89_FW_LOGFMT) return 0; @@ -755,6 +756,13 @@ static int rtw89_fw_update_ver(struct rtw89_dev *rtwdev, fw_suit->major_ver, fw_suit->minor_ver, fw_suit->sub_ver, fw_suit->sub_idex, fw_suit->commitid, fw_suit->cmd_ver, type); + if (type == RTW89_FW_NORMAL || type == RTW89_FW_NORMAL_CE || + type == RTW89_FW_NORMAL_B) + snprintf(wiphy->fw_version, sizeof(wiphy->fw_version), + "%u.%u.%u.%u", + fw_suit->major_ver, fw_suit->minor_ver, + fw_suit->sub_ver, fw_suit->sub_idex); + return 0; } From 127ea8d0b0687509e3067b1e90a38067d2298f49 Mon Sep 17 00:00:00 2001 From: Christos Longros Date: Sun, 29 Mar 2026 09:45:50 +0200 Subject: [PATCH 063/124] wifi: rtw89: fix typo "frome" -> "from" in rx_freq_frome_ie The chip_info field rx_freq_frome_ie (RX frequency from Information Element) has a typo. The function that uses it is already spelled correctly: rtw89_core_update_rx_freq_from_ie. Rename the field to match. Signed-off-by: Christos Longros Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260329074550.114787-1-chris.longros@gmail.com --- drivers/net/wireless/realtek/rtw89/core.c | 2 +- drivers/net/wireless/realtek/rtw89/core.h | 2 +- drivers/net/wireless/realtek/rtw89/rtw8851b.c | 2 +- drivers/net/wireless/realtek/rtw89/rtw8852a.c | 2 +- drivers/net/wireless/realtek/rtw89/rtw8852b.c | 2 +- drivers/net/wireless/realtek/rtw89/rtw8852bt.c | 2 +- drivers/net/wireless/realtek/rtw89/rtw8852c.c | 2 +- drivers/net/wireless/realtek/rtw89/rtw8922a.c | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 9d3f651798ff..70feab97dccb 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -3203,7 +3203,7 @@ static void rtw89_core_update_rx_freq_from_ie(struct rtw89_dev *rtwdev, u8 *variable; int chan; - if (!rtwdev->chip->rx_freq_frome_ie) + if (!rtwdev->chip->rx_freq_from_ie) return; if (!rtwdev->scanning) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 696a1d92d62b..4b4d25d8ba98 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4546,7 +4546,7 @@ struct rtw89_chip_info { bool support_noise; bool ul_tb_waveform_ctrl; bool ul_tb_pwr_diff; - bool rx_freq_frome_ie; + bool rx_freq_from_ie; bool hw_sec_hdr; bool hw_mgmt_tx_encrypt; bool hw_tkip_crypto; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index a3a5b6fbe46c..2005934f64d2 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -2641,7 +2641,7 @@ const struct rtw89_chip_info rtw8851b_chip_info = { .support_noise = false, .ul_tb_waveform_ctrl = true, .ul_tb_pwr_diff = false, - .rx_freq_frome_ie = true, + .rx_freq_from_ie = true, .hw_sec_hdr = false, .hw_mgmt_tx_encrypt = false, .hw_tkip_crypto = false, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 898c534a5762..12ed35f548c1 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2378,7 +2378,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .support_noise = true, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = false, - .rx_freq_frome_ie = true, + .rx_freq_from_ie = true, .hw_sec_hdr = false, .hw_mgmt_tx_encrypt = false, .hw_tkip_crypto = false, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index 19a9f07e8714..dcb15f26c1b1 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -974,7 +974,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .support_noise = false, .ul_tb_waveform_ctrl = true, .ul_tb_pwr_diff = false, - .rx_freq_frome_ie = true, + .rx_freq_from_ie = true, .hw_sec_hdr = false, .hw_mgmt_tx_encrypt = false, .hw_tkip_crypto = false, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index c86b995a7cb1..2afc6908cc7e 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -813,7 +813,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = { .support_sar_by_ant = true, .ul_tb_waveform_ctrl = true, .ul_tb_pwr_diff = false, - .rx_freq_frome_ie = true, + .rx_freq_from_ie = true, .hw_sec_hdr = false, .hw_mgmt_tx_encrypt = false, .hw_tkip_crypto = true, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 7ea0a8905282..5f9da0f85bb5 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -3171,7 +3171,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .support_noise = false, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = true, - .rx_freq_frome_ie = false, + .rx_freq_from_ie = false, .hw_sec_hdr = true, .hw_mgmt_tx_encrypt = true, .hw_tkip_crypto = true, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index a489aaf90f23..5f83e71ed218 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -2975,7 +2975,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = { .support_noise = false, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = false, - .rx_freq_frome_ie = false, + .rx_freq_from_ie = false, .hw_sec_hdr = true, .hw_mgmt_tx_encrypt = true, .hw_tkip_crypto = true, From 347fbc602795f9595a226585968bc23f0073cf77 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 30 Mar 2026 14:58:39 +0800 Subject: [PATCH 064/124] wifi: rtw89: 8922d: BB hardware pre-/post-init, TX/RX path and power settings More settings related to BB pre-/post-initial settings, the TX/RX path settings, and digital power compensation. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260330065847.48946-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/reg.h | 64 ++++ drivers/net/wireless/realtek/rtw89/rtw8922d.c | 318 ++++++++++++++++++ 2 files changed, 382 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 5d284f310069..e64bd74db83a 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -8384,6 +8384,9 @@ #define B_BE_PWR_BT_VAL GENMASK(8, 0) #define B_BE_PWR_FORCE_COEX_ON GENMASK(29, 27) +#define R_PWR_BOOST_BE4 0x11A64 +#define B_PWR_BOOST_BE4 BIT(8) + #define R_BE_PWR_TH 0x11A78 #define R_BE_PWR_RSSI_TARGET_LMT 0x11A84 @@ -10248,6 +10251,8 @@ #define R_TSSI_K_P1 0xE7A0 #define B_TSSI_K_OFDM_P1 GENMASK(29, 20) +#define R_BBWRAP_ELMSR_BE4 0x11974 +#define B_BBWRAP_ELMSR_EN_BE4 GENMASK(29, 28) #define R_COMP_CIM3K_BE4 0x11998 #define B_COMP_CIM3K_OW_BE4 BIT(1) #define B_COMP_CIM3K_TH_BE4 BIT(2) @@ -10452,6 +10457,12 @@ #define R_BANDEDGE_DBWY_BE4 0x11AD0 #define B_BANDEDGE_DBW160_BE4 BIT(0) +#define R_SYS_DBCC_BE4 0x20000 +#define B_SYS_DBCC_BE4 BIT(0) +#define B_SYS_DBCC_24G_BAND_SEL_BE4 BIT(1) +#define R_EMLSR_SWITCH_BE4 0x20044 +#define B_EMLSR_SWITCH_BE4 GENMASK(27, 12) +#define B_EMLSR_BB_CLK_BE4 GENMASK(31, 30) #define R_CHINFO_SEG_BE4 0x200B4 #define B_CHINFO_SEG_LEN_BE4 GENMASK(12, 10) #define R_STS_HDR2_PARSING_BE4 0x2070C @@ -10467,11 +10478,25 @@ #define B_RXBW7_BE4 GENMASK(25, 23) #define R_RXBW_BE4 0x20410 #define B_RXBW_BE4 GENMASK(29, 27) +#define R_TXERRCT_EN_BE4 0x20518 +#define B_TXERRCT_EN_BE4 BIT(13) +#define R_TXERRCT1_EN_BE4 0x2051C +#define B_TXERRCT1_EN_BE4 BIT(31) #define R_ENABLE_CCK0_BE4 0x20700 #define B_ENABLE_CCK0_BE4 BIT(5) +#define R_RSTB_ASYNC_BE4 0x20704 +#define B_RSTB_ASYNC_BE4 BIT(1) #define R_EDCCA_RPT_SEL_BE4 0x20780 #define R_EDCCA_RPT_SEL_BE4_C1 0x21780 #define B_EDCCA_RPT_SEL_BE4_MSK 0xE0000 +#define R_IMR_TX_ERROR_BE4 0x20920 +#define B_IMR_TX_ERROR_BE4 BIT(30) +#define R_TXINFO_PATH_BE4 0x209A4 +#define B_TXINFO_PATH_EN_BE4 BIT(17) +#define B_TXINFO_PATH_MA_BE4 BIT(18) +#define B_TXINFO_PATH_MB_BE4 BIT(19) +#define R_SHAPER_COEFF_BE4 0x20CBC +#define B_SHAPER_COEFF_BE4 BIT(19) #define R_IFS_T1_AVG_BE4 0x20EDC #define B_IFS_T1_AVG_BE4 GENMASK(15, 0) #define B_IFS_T2_AVG_BE4 GENMASK(31, 16) @@ -10494,6 +10519,12 @@ #define B_IFS_T3_HIS_BE4 GENMASK(15, 0) #define B_IFS_T4_HIS_BE4 GENMASK(31, 16) +#define R_TX_ERROR_SEL_BE4 0x21254 +#define B_TX_ERROR_PSDU_BE4 BIT(11) +#define B_TX_ERROR_NSYM_BE4 BIT(10) +#define B_TX_ERROR_LSIG_BE4 BIT(9) +#define B_TX_ERROR_TXINFO_BE4 BIT(8) + #define R_TXPWR_RSTB0_BE4 0x2250C #define B_TXPWR_RSTB0_BE4 BIT(16) #define R_TXPWR_RSTB1_BE4 0x2260C @@ -10546,6 +10577,10 @@ #define B_PRISB_BE4 GENMASK(3, 0) #define R_FC0_BE4 0x24EE8 #define B_FC0_BE4 GENMASK(12, 0) +#define R_ANT_RX_1RCCA_BE4 0x24EEC +#define B_ANT_RX_1RCCA_BE4 GENMASK(17, 14) +#define R_ANT_RX_BE4 0x24EF0 +#define B_ANT_RX_BE4 GENMASK(3, 0) #define R_FC0_INV_BE4 0x24EF4 #define B_FC0_INV_BE4 GENMASK(15, 0) @@ -10569,6 +10604,32 @@ #define B_CHINFO_NX_BE4 GENMASK(16, 6) #define R_CHINFO_ALG_BE4 0x267C8 #define B_CHINFO_ALG_BE4 GENMASK(31, 30) +#define R_RX_AWGN02_BE4 0x2680C +#define B_RX_AWGN11_BE4 GENMASK(23, 18) +#define R_RX_AWGN00_BE4 0x26814 +#define B_RX_AWGN04_BE4 GENMASK(5, 0) +#define B_RX_AWGN07_BE4 GENMASK(23, 18) +#define R_RX_AWGN01_BE4 0x26818 +#define B_RX_AWGN09_BE4 GENMASK(5, 0) +#define R_RXCH_BCC0_BE4 0x26824 +#define B_RXCH_MCS4_BE4 GENMASK(29, 24) +#define R_RXCH_BCC1_BE4 0x26828 +#define B_RXCH_MCS5_BE4 GENMASK(5, 0) +#define B_RXCH_MCS6_BE4 GENMASK(11, 6) +#define B_RXCH_MCS7_BE4 GENMASK(17, 12) +#define B_RXCH_MCS8_BE4 GENMASK(23, 18) +#define B_RXCH_MCS9_BE4 GENMASK(29, 24) +#define R_RX_LDPC02_BE4 0x26834 +#define B_RX_LDPC10_BE4 GENMASK(17, 12) +#define B_RX_LDPC11_BE4 GENMASK(23, 18) +#define R_RX_LDPC00_BE4 0x2683C +#define B_RX_LDPC04_BE4 GENMASK(5, 0) +#define B_RX_LDPC05_BE4 GENMASK(11, 6) +#define B_RX_LDPC06_BE4 GENMASK(17, 12) +#define B_RX_LDPC07_BE4 GENMASK(23, 18) +#define B_RX_LDPC08_BE4 GENMASK(29, 24) +#define R_RX_LDPC01_BE4 0x26840 +#define B_RX_LDPC09_BE4 GENMASK(5, 0) #define R_SW_SI_DATA_BE4 0x2CF4C #define B_SW_SI_READ_DATA_BE4 GENMASK(19, 0) @@ -10576,6 +10637,9 @@ #define B_SW_SI_R_BUSY_BE4 BIT(25) #define B_SW_SI_READ_DATA_DONE_BE4 BIT(26) +#define R_RX_PATH0_TBL0_BE4 0x2E028 +#define R_RX_PATH1_TBL0_BE4 0x2E128 + /* WiFi CPU local domain */ #define R_AX_WDT_CTRL 0x0040 #define B_AX_WDT_EN BIT(31) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 1b5fc6c9ea85..51b025d898ff 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -2,6 +2,7 @@ /* Copyright(c) 2026 Realtek Corporation */ +#include "chan.h" #include "debug.h" #include "efuse.h" #include "mac.h" @@ -1690,6 +1691,94 @@ static void rtw8922d_spur_elimination(struct rtw89_dev *rtwdev, rtw8922d_set_nbi_tone_idx(rtwdev, chan, RF_PATH_B, phy_idx); } +static const u32 bbrst_mask[2] = {B_BE_FEN_BBPLAT_RSTB, B_BE_FEN_BB1PLAT_RSTB}; +static const u32 glbrst_mask[2] = {B_BE_FEN_BB_IP_RSTN, B_BE_FEN_BB1_IP_RSTN}; +static const u32 chip_top_bitmask[2] = {0xffff, 0xffff0000}; + +static void rtw8922d_bb_preinit(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) +{ + rtw89_write32_mask(rtwdev, R_BE_FEN_RST_ENABLE, glbrst_mask[phy_idx], 0x0); + rtw89_write32_mask(rtwdev, R_BE_FEN_RST_ENABLE, bbrst_mask[phy_idx], 0x0); + rtw89_write32_mask(rtwdev, R_BE_DMAC_SYS_CR32B, chip_top_bitmask[phy_idx], 0x74F9); + rtw89_write32_mask(rtwdev, R_BE_FEN_RST_ENABLE, glbrst_mask[phy_idx], 0x1); + rtw89_phy_write32_idx(rtwdev, R_RSTB_ASYNC_BE4, B_RSTB_ASYNC_BE4, 0, phy_idx); + rtw89_write32_mask(rtwdev, R_BE_FEN_RST_ENABLE, bbrst_mask[phy_idx], 0x1); +} + +static void rtw8922d_bb_postinit(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) +{ + rtw89_phy_write32_idx_clr(rtwdev, R_SHAPER_COEFF_BE4, B_SHAPER_COEFF_BE4, phy_idx); + rtw89_phy_write32_idx_set(rtwdev, R_SHAPER_COEFF_BE4, B_SHAPER_COEFF_BE4, phy_idx); +} + +static void rtw8922d_bb_reset_en(struct rtw89_dev *rtwdev, enum rtw89_band band, + bool en, enum rtw89_phy_idx phy_idx) +{ + if (en) + rtw89_phy_write32_idx(rtwdev, R_RSTB_ASYNC_BE4, B_RSTB_ASYNC_BE4, 1, phy_idx); + else + rtw89_phy_write32_idx(rtwdev, R_RSTB_ASYNC_BE4, B_RSTB_ASYNC_BE4, 0, phy_idx); +} + +static int rtw8922d_ctrl_tx_path_tmac(struct rtw89_dev *rtwdev, + enum rtw89_rf_path tx_path, + enum rtw89_phy_idx phy_idx) +{ + struct rtw89_reg2_def path_com_cr[] = { + {0x11A00, 0x21C86900}, + {0x11A04, 0x00E4E433}, + {0x11A08, 0x39390CC9}, + {0x11A10, 0x10CC0000}, + {0x11A14, 0x00240393}, + {0x11A18, 0x201C8600}, + {0x11B38, 0x39393FDB}, + {0x11B3C, 0x00E4E4FF}, + }; + int ret = 0; + u32 reg; + int i; + + rtw89_phy_write32_idx(rtwdev, R_TXINFO_PATH_BE4, B_TXINFO_PATH_EN_BE4, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_TXINFO_PATH_BE4, B_TXINFO_PATH_MA_BE4, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_TXINFO_PATH_BE4, B_TXINFO_PATH_MB_BE4, 0x0, phy_idx); + + if (phy_idx == RTW89_PHY_1 && !rtwdev->dbcc_en) + return 0; + + if (tx_path == RF_PATH_A) { + path_com_cr[1].data = 0x40031; + path_com_cr[2].data = 0x1000C48; + path_com_cr[5].data = 0x200; + path_com_cr[6].data = 0x1000C48; + path_com_cr[7].data = 0x40031; + } else if (tx_path == RF_PATH_B) { + path_com_cr[1].data = 0x40032; + path_com_cr[2].data = 0x1000C88; + path_com_cr[5].data = 0x400; + path_com_cr[6].data = 0x1000C88; + path_com_cr[7].data = 0x40032; + } else if (tx_path == RF_PATH_AB) { + path_com_cr[1].data = 0x00E4E433; + path_com_cr[2].data = 0x39390CC9; + path_com_cr[5].data = 0x201C8600; + path_com_cr[6].data = 0x1010CC9; + path_com_cr[7].data = 0x40433; + } else { + ret = -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(path_com_cr); i++) { + reg = rtw89_mac_reg_by_idx(rtwdev, path_com_cr[i].addr, phy_idx); + rtw89_write32(rtwdev, reg, path_com_cr[i].data); + } + + return ret; +} + +static void rtw8922d_bb_reset(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) +{ +} + static void rtw8922d_tssi_reset(struct rtw89_dev *rtwdev, enum rtw89_rf_path path, enum rtw89_phy_idx phy_idx) @@ -1714,6 +1803,235 @@ static void rtw8922d_tssi_reset(struct rtw89_dev *rtwdev, } } +static int rtw8922d_ctrl_rx_path_tmac(struct rtw89_dev *rtwdev, + enum rtw89_rf_path rx_path, + enum rtw89_phy_idx phy_idx) +{ + enum rtw89_rf_path_bit path; + + if (rx_path == RF_PATH_A) + path = RF_A; + else if (rx_path == RF_PATH_B) + path = RF_B; + else + path = RF_AB; + + rtw89_phy_write32_idx(rtwdev, R_ANT_RX_BE4, B_ANT_RX_BE4, path, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_ANT_RX_1RCCA_BE4, B_ANT_RX_1RCCA_BE4, + path, phy_idx); + + if (rx_path == RF_PATH_AB) { + rtw89_phy_write32_idx(rtwdev, R_RXCH_BCC0_BE4, B_RXCH_MCS4_BE4, 8, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RXCH_BCC1_BE4, B_RXCH_MCS5_BE4, 4, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RXCH_BCC1_BE4, B_RXCH_MCS6_BE4, 3, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RXCH_BCC1_BE4, B_RXCH_MCS7_BE4, 7, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RXCH_BCC1_BE4, B_RXCH_MCS8_BE4, 2, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RXCH_BCC1_BE4, B_RXCH_MCS9_BE4, 2, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_AWGN00_BE4, B_RX_AWGN04_BE4, 4, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_AWGN00_BE4, B_RX_AWGN07_BE4, 2, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_AWGN01_BE4, B_RX_AWGN09_BE4, 0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_AWGN02_BE4, B_RX_AWGN11_BE4, 1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC00_BE4, B_RX_LDPC04_BE4, 8, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC00_BE4, B_RX_LDPC05_BE4, 5, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC00_BE4, B_RX_LDPC06_BE4, 3, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC00_BE4, B_RX_LDPC07_BE4, 5, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC00_BE4, B_RX_LDPC08_BE4, 1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC01_BE4, B_RX_LDPC09_BE4, 2, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC02_BE4, B_RX_LDPC10_BE4, 4, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC02_BE4, B_RX_LDPC11_BE4, 2, phy_idx); + } else { + rtw89_phy_write32_idx(rtwdev, R_RXCH_BCC0_BE4, B_RXCH_MCS4_BE4, 13, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RXCH_BCC1_BE4, B_RXCH_MCS5_BE4, 15, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RXCH_BCC1_BE4, B_RXCH_MCS6_BE4, 6, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RXCH_BCC1_BE4, B_RXCH_MCS7_BE4, 15, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RXCH_BCC1_BE4, B_RXCH_MCS8_BE4, 4, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RXCH_BCC1_BE4, B_RXCH_MCS9_BE4, 15, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_AWGN00_BE4, B_RX_AWGN04_BE4, 9, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_AWGN00_BE4, B_RX_AWGN07_BE4, 3, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_AWGN01_BE4, B_RX_AWGN09_BE4, 1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_AWGN02_BE4, B_RX_AWGN11_BE4, 0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC00_BE4, B_RX_LDPC04_BE4, 9, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC00_BE4, B_RX_LDPC05_BE4, 8, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC00_BE4, B_RX_LDPC06_BE4, 6, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC00_BE4, B_RX_LDPC07_BE4, 16, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC00_BE4, B_RX_LDPC08_BE4, 4, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC01_BE4, B_RX_LDPC09_BE4, 9, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC02_BE4, B_RX_LDPC10_BE4, 9, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RX_LDPC02_BE4, B_RX_LDPC11_BE4, 7, phy_idx); + } + + return 0; +} + +static void rtw8922d_set_digital_pwr_comp(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, u8 nss, + enum rtw89_rf_path path, + enum rtw89_phy_idx phy_idx) +{ +#define DIGITAL_PWR_COMP_REG_NUM 22 + static const u32 pw_comp_cr[2] = {R_RX_PATH0_TBL0_BE4, R_RX_PATH1_TBL0_BE4}; + const __le32 (*pwr_comp_val)[2][RTW89_TX_COMP_BAND_NR] + [BB_PATH_NUM_8922D][DIGITAL_PWR_COMP_REG_NUM]; + struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info; + const struct rtw89_fw_element_hdr *txcomp_elm = elm_info->tx_comp; + const __le32 *digital_pwr_comp; + u32 addr, val; + u32 i; + + if (sizeof(*pwr_comp_val) != le32_to_cpu(txcomp_elm->size)) { + rtw89_debug(rtwdev, RTW89_DBG_UNEXP, + "incorrect power comp size %d\n", + le32_to_cpu(txcomp_elm->size)); + return; + } + + pwr_comp_val = (const void *)txcomp_elm->u.common.contents; + digital_pwr_comp = (*pwr_comp_val)[nss][chan->tx_comp_band][path]; + addr = pw_comp_cr[path]; + + for (i = 0; i < DIGITAL_PWR_COMP_REG_NUM; i++, addr += 4) { + val = le32_to_cpu(digital_pwr_comp[i]); + rtw89_phy_write32_idx(rtwdev, addr, MASKDWORD, val, phy_idx); + } +} + +static void rtw8922d_digital_pwr_comp(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx) +{ + const struct rtw89_chan *chan0 = rtw89_mgnt_chan_get(rtwdev, 0); + const struct rtw89_chan *chan1 = rtw89_mgnt_chan_get(rtwdev, 1); + + if (rtwdev->mlo_dbcc_mode == MLO_1_PLUS_1_1RF) { + rtw8922d_set_digital_pwr_comp(rtwdev, chan0, 0, RF_PATH_A, RTW89_PHY_0); + rtw8922d_set_digital_pwr_comp(rtwdev, chan1, 0, RF_PATH_B, RTW89_PHY_1); + } else { + rtw8922d_set_digital_pwr_comp(rtwdev, chan0, 1, RF_PATH_A, phy_idx); + rtw8922d_set_digital_pwr_comp(rtwdev, chan0, 1, RF_PATH_B, phy_idx); + } +} + +static int rtw8922d_ctrl_mlo(struct rtw89_dev *rtwdev, enum rtw89_mlo_dbcc_mode mode, + bool pwr_comp) +{ + const struct rtw89_chan *chan1; + u32 reg0, reg1; + u8 cck_phy_idx; + + if (mode == MLO_2_PLUS_0_1RF) { + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xBBBB); + udelay(1); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_BB_CLK_BE4, 0x3); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xAFFF); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xEBAD); + udelay(1); + + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_BB_CLK_BE4, 0x0); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xEAAD); + } else if (mode == MLO_0_PLUS_2_1RF) { + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xBBBB); + udelay(1); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_BB_CLK_BE4, 0x3); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xAFFF); + udelay(1); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xEFFF); + + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_BB_CLK_BE4, 0x0); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xEEFF); + } else if ((mode == MLO_1_PLUS_1_1RF) || (mode == DBCC_LEGACY)) { + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xBBBB); + udelay(1); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_BB_CLK_BE4, 0x3); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xAFFF); + udelay(1); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_BB_CLK_BE4, 0x0); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0x3AAB); + } else { + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0x6180); + udelay(1); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_BB_CLK_BE4, 0x3); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0x180); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_BB_CLK_BE4, 0x0); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0x0); + } + + if (pwr_comp) + rtw8922d_digital_pwr_comp(rtwdev, RTW89_PHY_0); + + reg0 = R_BBWRAP_ELMSR_BE4; + reg1 = rtw89_mac_reg_by_idx(rtwdev, reg0, 1); + + if (mode == MLO_2_PLUS_0_1RF) { + rtw89_phy_write32_mask(rtwdev, R_SYS_DBCC_BE4, + B_SYS_DBCC_24G_BAND_SEL_BE4, RTW89_PHY_0); + rtw89_write32_mask(rtwdev, reg0, B_BBWRAP_ELMSR_EN_BE4, 0); + rtw89_write32_mask(rtwdev, reg1, B_BBWRAP_ELMSR_EN_BE4, 0); + } else if (mode == MLO_0_PLUS_2_1RF) { + rtw89_phy_write32_mask(rtwdev, R_SYS_DBCC_BE4, + B_SYS_DBCC_24G_BAND_SEL_BE4, RTW89_PHY_0); + rtw89_write32_mask(rtwdev, reg0, B_BBWRAP_ELMSR_EN_BE4, 0); + rtw89_write32_mask(rtwdev, reg1, B_BBWRAP_ELMSR_EN_BE4, 0); + } else if ((mode == MLO_1_PLUS_1_1RF) || (mode == DBCC_LEGACY)) { + chan1 = rtw89_mgnt_chan_get(rtwdev, 1); + cck_phy_idx = chan1->band_type == RTW89_BAND_2G ? + RTW89_PHY_1 : RTW89_PHY_0; + + rtw89_phy_write32_mask(rtwdev, R_SYS_DBCC_BE4, + B_SYS_DBCC_24G_BAND_SEL_BE4, cck_phy_idx); + rtw89_write32_mask(rtwdev, reg0, B_BBWRAP_ELMSR_EN_BE4, 0x3); + rtw89_write32_mask(rtwdev, reg1, B_BBWRAP_ELMSR_EN_BE4, 0x3); + } else { + rtw89_phy_write32_mask(rtwdev, R_SYS_DBCC_BE4, + B_SYS_DBCC_24G_BAND_SEL_BE4, RTW89_PHY_0); + rtw89_write32_mask(rtwdev, reg0, B_BBWRAP_ELMSR_EN_BE4, 0); + rtw89_write32_mask(rtwdev, reg1, B_BBWRAP_ELMSR_EN_BE4, 0); + } + + udelay(1); + + return 0; +} + +static void rtw8922d_bb_sethw(struct rtw89_dev *rtwdev) +{ + struct rtw89_phy_efuse_gain *gain = &rtwdev->efuse_gain; + struct rtw89_hal *hal = &rtwdev->hal; + enum rtw89_phy_idx phy_idx; + u32 reg; + + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_BOOST, RTW89_PHY_0); + rtw89_write32_clr(rtwdev, reg, B_BE_PWR_CTRL_SEL); + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_BOOST, RTW89_PHY_1); + rtw89_write32_clr(rtwdev, reg, B_BE_PWR_CTRL_SEL); + + if (hal->cid == RTL8922D_CID7090) { + reg = rtw89_mac_reg_by_idx(rtwdev, R_PWR_BOOST_BE4, RTW89_PHY_0); + rtw89_write32_set(rtwdev, reg, B_PWR_BOOST_BE4); + reg = rtw89_mac_reg_by_idx(rtwdev, R_PWR_BOOST_BE4, RTW89_PHY_1); + rtw89_write32_set(rtwdev, reg, B_PWR_BOOST_BE4); + } + + rtw89_phy_write32_mask(rtwdev, R_TX_ERROR_SEL_BE4, B_TX_ERROR_PSDU_BE4, 0); + rtw89_phy_write32_mask(rtwdev, R_TX_ERROR_SEL_BE4, B_TX_ERROR_NSYM_BE4, 1); + rtw89_phy_write32_mask(rtwdev, R_TX_ERROR_SEL_BE4, B_TX_ERROR_LSIG_BE4, 1); + rtw89_phy_write32_mask(rtwdev, R_TX_ERROR_SEL_BE4, B_TX_ERROR_TXINFO_BE4, 1); + rtw89_phy_write32_mask(rtwdev, R_TXERRCT_EN_BE4, B_TXERRCT_EN_BE4, 0); + rtw89_phy_write32_mask(rtwdev, R_TXERRCT1_EN_BE4, B_TXERRCT1_EN_BE4, 0); + rtw89_phy_write32_idx(rtwdev, R_IMR_TX_ERROR_BE4, B_IMR_TX_ERROR_BE4, 1, RTW89_PHY_0); + rtw89_phy_write32_idx(rtwdev, R_IMR_TX_ERROR_BE4, B_IMR_TX_ERROR_BE4, 1, RTW89_PHY_1); + + rtw8922d_ctrl_mlo(rtwdev, rtwdev->mlo_dbcc_mode, false); + + /* read these registers after loading BB parameters */ + for (phy_idx = RTW89_PHY_0; phy_idx < RTW89_PHY_NUM; phy_idx++) { + gain->ref_gain_base[phy_idx] = + rtw89_phy_read32_idx(rtwdev, R_OFDM_OFST_P0_BE4, + B_OFDM_OFST_P0_BE4, phy_idx); + gain->cck_rpl_base[phy_idx] = + rtw89_phy_read32_idx(rtwdev, R_CCK_RPL_OFST_BE4, + B_CCK_RPL_OFST_BE4, phy_idx); + } +} + static void rtw8922d_set_channel_bb(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) From edf9f583c05cebe8933903dc3e96ffa2acc62c5b Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 30 Mar 2026 14:58:40 +0800 Subject: [PATCH 065/124] wifi: rtw89: 8922d: add set channel with pre-/post- helpers The main set channel function calls MAC/BB/RF ones, and pre-/post- helpers are called before/after the main function to backup/restore and stop/restart circuits, including TX scheduler, PPDU status, DACK and TSSI. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260330065847.48946-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/reg.h | 14 ++ drivers/net/wireless/realtek/rtw89/rtw8922d.c | 102 ++++++++ .../net/wireless/realtek/rtw89/rtw8922d_rfk.c | 230 ++++++++++++++++++ .../net/wireless/realtek/rtw89/rtw8922d_rfk.h | 4 + 4 files changed, 350 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index e64bd74db83a..195d4806c4ef 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -8445,6 +8445,8 @@ #define RR_MOD_M_RXBB GENMASK(9, 5) #define RR_MOD_LO_SEL BIT(1) #define RR_MODOPT 0x01 +#define RR_MODOPT_V1 0x10001 +#define RR_SW_SEL BIT(19) #define RR_TXG_SEL GENMASK(19, 17) #define RR_MODOPT_M_TXPWR GENMASK(5, 0) #define RR_WLSEL 0x02 @@ -10527,6 +10529,8 @@ #define R_TXPWR_RSTB0_BE4 0x2250C #define B_TXPWR_RSTB0_BE4 BIT(16) +#define R_TSSI_EN_P0_BE4 0x22510 +#define B_TSSI_EN_P0_BE4 GENMASK(3, 0) #define R_TXPWR_RSTB1_BE4 0x2260C #define B_TXPWR_RSTB1_BE4 BIT(16) @@ -10640,6 +10644,16 @@ #define R_RX_PATH0_TBL0_BE4 0x2E028 #define R_RX_PATH1_TBL0_BE4 0x2E128 +#define R_KTBL0A_BE4 0x38104 +#define R_KTBL0B_BE4 0x38204 +#define B_KTBL0_IDX0 GENMASK(1, 0) +#define B_KTBL0_IDX1 GENMASK(9, 8) +#define B_KTBL0_RST BIT(31) +#define R_KTBL1A_BE4 0x38154 +#define R_KTBL1B_BE4 0x38254 +#define B_KTBL1_TBL0 BIT(3) +#define B_KTBL1_TBL1 BIT(5) + /* WiFi CPU local domain */ #define R_AX_WDT_CTRL 0x0040 #define B_AX_WDT_EN BIT(31) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 51b025d898ff..a2dd504c99ed 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -2066,6 +2066,108 @@ static void rtw8922d_set_channel_bb(struct rtw89_dev *rtwdev, rtw8922d_tssi_reset(rtwdev, RF_PATH_AB, phy_idx); } +static void rtw8922d_pre_set_channel_bb(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx) +{ + if (!rtwdev->dbcc_en) + return; + + rtw89_phy_write32_mask(rtwdev, R_SYS_DBCC_BE4, B_SYS_DBCC_BE4, 0x0); + + if (phy_idx == RTW89_PHY_0) { + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xBBBB); + fsleep(1); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_BB_CLK_BE4, 0x3); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xAFFF); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xEBAD); + fsleep(1); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_BB_CLK_BE4, 0x0); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xEAAD); + } else { + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xBBBB); + fsleep(1); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_BB_CLK_BE4, 0x3); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xAFFF); + fsleep(1); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xEFFF); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_BB_CLK_BE4, 0x0); + rtw89_phy_write32_mask(rtwdev, R_EMLSR_SWITCH_BE4, B_EMLSR_SWITCH_BE4, 0xEEFF); + } + + fsleep(1); +} + +static void rtw8922d_post_set_channel_bb(struct rtw89_dev *rtwdev, + enum rtw89_mlo_dbcc_mode mode, + enum rtw89_phy_idx phy_idx) +{ + if (!rtwdev->dbcc_en) + return; + + rtw8922d_ctrl_mlo(rtwdev, mode, true); +} + +static void rtw8922d_set_channel(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_mac_idx mac_idx, + enum rtw89_phy_idx phy_idx) +{ + rtw8922d_set_channel_mac(rtwdev, chan, mac_idx); + rtw8922d_set_channel_bb(rtwdev, chan, phy_idx); + rtw8922d_set_channel_rf(rtwdev, chan, phy_idx); +} + +static void __rtw8922d_dack_reset(struct rtw89_dev *rtwdev, enum rtw89_rf_path path) +{ + rtw89_phy_write32_mask(rtwdev, 0x3c000 + (path << 8), BIT(17), 0x0); + rtw89_phy_write32_mask(rtwdev, 0x3c000 + (path << 8), BIT(17), 0x1); +} + +static void rtw8922d_dack_reset(struct rtw89_dev *rtwdev) +{ + __rtw8922d_dack_reset(rtwdev, RF_PATH_A); + __rtw8922d_dack_reset(rtwdev, RF_PATH_B); +} + +static +void rtw8922d_hal_reset(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, enum rtw89_mac_idx mac_idx, + enum rtw89_band band, u32 *tx_en, bool enter) +{ + if (enter) { + rtw89_chip_stop_sch_tx(rtwdev, mac_idx, tx_en, RTW89_SCH_TX_SEL_ALL); + rtw89_mac_cfg_ppdu_status(rtwdev, mac_idx, false); + rtw8922d_dack_reset(rtwdev); + rtw8922d_tssi_cont_en_phyidx(rtwdev, false, phy_idx); + fsleep(40); + rtw8922d_bb_reset_en(rtwdev, band, false, phy_idx); + } else { + rtw89_mac_cfg_ppdu_status(rtwdev, mac_idx, true); + rtw8922d_tssi_cont_en_phyidx(rtwdev, true, phy_idx); + rtw8922d_bb_reset_en(rtwdev, band, true, phy_idx); + rtw89_chip_resume_sch_tx(rtwdev, mac_idx, *tx_en); + } +} + +static void rtw8922d_set_channel_help(struct rtw89_dev *rtwdev, bool enter, + struct rtw89_channel_help_params *p, + const struct rtw89_chan *chan, + enum rtw89_mac_idx mac_idx, + enum rtw89_phy_idx phy_idx) +{ + if (enter) { + rtw8922d_pre_set_channel_bb(rtwdev, phy_idx); + rtw8922d_pre_set_channel_rf(rtwdev, phy_idx); + } + + rtw8922d_hal_reset(rtwdev, phy_idx, mac_idx, chan->band_type, &p->tx_en, enter); + + if (!enter) { + rtw8922d_post_set_channel_bb(rtwdev, rtwdev->mlo_dbcc_mode, phy_idx); + rtw8922d_post_set_channel_rf(rtwdev, phy_idx); + } +} + MODULE_FIRMWARE(RTW8922D_MODULE_FIRMWARE); MODULE_FIRMWARE(RTW8922DS_MODULE_FIRMWARE); MODULE_AUTHOR("Realtek Corporation"); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c index 6b35d196cb81..d1eda19a39a9 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c @@ -2,11 +2,40 @@ /* Copyright(c) 2026 Realtek Corporation */ +#include "chan.h" +#include "debug.h" #include "phy.h" #include "reg.h" #include "rtw8922d.h" #include "rtw8922d_rfk.h" +static void rtw8922d_tssi_cont_en(struct rtw89_dev *rtwdev, bool en, + enum rtw89_rf_path path, u8 phy_idx) +{ + static const u32 tssi_trk_man[2] = {R_TSSI_EN_P0_BE4, + R_TSSI_EN_P0_BE4 + 0x100}; + + if (en) + rtw89_phy_write32_idx(rtwdev, tssi_trk_man[path], + B_TSSI_CONT_EN, 0, phy_idx); + else + rtw89_phy_write32_idx(rtwdev, tssi_trk_man[path], + B_TSSI_CONT_EN, 1, phy_idx); +} + +void rtw8922d_tssi_cont_en_phyidx(struct rtw89_dev *rtwdev, bool en, u8 phy_idx) +{ + if (rtwdev->mlo_dbcc_mode == MLO_1_PLUS_1_1RF) { + if (phy_idx == RTW89_PHY_0) + rtw8922d_tssi_cont_en(rtwdev, en, RF_PATH_A, phy_idx); + else + rtw8922d_tssi_cont_en(rtwdev, en, RF_PATH_B, phy_idx); + } else { + rtw8922d_tssi_cont_en(rtwdev, en, RF_PATH_A, phy_idx); + rtw8922d_tssi_cont_en(rtwdev, en, RF_PATH_B, phy_idx); + } +} + static void rtw8922d_ctl_band_ch_bw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, const struct rtw89_chan *chan) @@ -31,3 +60,204 @@ void rtw8922d_set_channel_rf(struct rtw89_dev *rtwdev, { rtw8922d_ctl_band_ch_bw(rtwdev, phy_idx, chan); } + +enum _rf_syn_pow { + RF_SYN_ON_OFF, + RF_SYN_OFF_ON, + RF_SYN_ALLON, + RF_SYN_ALLOFF, +}; + +static void rtw8922d_set_syn01(struct rtw89_dev *rtwdev, enum _rf_syn_pow syn) +{ + rtw89_debug(rtwdev, RTW89_DBG_RFK, "SYN config=%d\n", syn); + + if (syn == RF_SYN_ALLON) { + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MOD, BIT(1), 0x0); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_MOD, BIT(1), 0x0); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_RSV1, MASKDWORD, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_RSV1, MASKDWORD, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_POW, RR_POW_SYN_V1, 0xf); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_POW, RR_POW_SYN_V1, 0xf); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_RSV1, MASKDWORD, 0x1); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_RSV1, MASKDWORD, 0x1); + } else if (syn == RF_SYN_ON_OFF) { + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MOD, BIT(1), 0x0); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_RSV1, MASKDWORD, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_POW, RR_POW_SYN_V1, 0xf); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_POW, RR_POW_SYN_V1, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_RSV1, MASKDWORD, 0x1); + } else if (syn == RF_SYN_OFF_ON) { + rtw89_write_rf(rtwdev, RF_PATH_B, RR_MOD, BIT(1), 0x0); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_RSV1, MASKDWORD, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_POW, RR_POW_SYN_V1, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_POW, RR_POW_SYN_V1, 0xf); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_RSV1, MASKDWORD, 0x1); + } else if (syn == RF_SYN_ALLOFF) { + rtw89_write_rf(rtwdev, RF_PATH_A, RR_POW, RR_POW_SYN_V1, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_POW, RR_POW_SYN_V1, 0x0); + } +} + +static void rtw8922d_chlk_ktbl_sel(struct rtw89_dev *rtwdev, u8 kpath, u8 idx) +{ + bool mlo_linking = false; + + if (idx > 2) { + rtw89_warn(rtwdev, "[DBCC][ERROR]indx is out of limit!! index(%d)", idx); + return; + } + + if (mlo_linking) { + if (kpath & RF_A) { + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT, RR_SW_SEL, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT_V1, RR_SW_SEL, 0x0); + } + + if (kpath & RF_B) { + rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT, RR_SW_SEL, 0x0); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT_V1, RR_SW_SEL, 0x0); + } + + return; + } + + if (kpath & RF_A) { + rtw89_phy_write32_mask(rtwdev, R_KTBL0A_BE4, B_KTBL0_RST, 0x1); + rtw89_phy_write32_mask(rtwdev, R_KTBL0A_BE4, B_KTBL0_IDX0, idx); + rtw89_phy_write32_mask(rtwdev, R_KTBL0A_BE4, B_KTBL0_IDX1, idx); + + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT, RR_TXG_SEL, 0x4 | idx); + rtw89_write_rf(rtwdev, RF_PATH_A, RR_MODOPT_V1, RR_TXG_SEL, 0x4 | idx); + + rtw89_phy_write32_mask(rtwdev, R_KTBL1A_BE4, B_KTBL1_TBL0, idx & BIT(0)); + rtw89_phy_write32_mask(rtwdev, R_KTBL1A_BE4, B_KTBL1_TBL1, (idx & BIT(1)) >> 1); + } + + if (kpath & RF_B) { + rtw89_phy_write32_mask(rtwdev, R_KTBL0B_BE4, B_KTBL0_RST, 0x1); + rtw89_phy_write32_mask(rtwdev, R_KTBL0B_BE4, B_KTBL0_IDX0, idx); + rtw89_phy_write32_mask(rtwdev, R_KTBL0B_BE4, B_KTBL0_IDX1, idx); + + rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT, RR_TXG_SEL, 0x4 | idx); + rtw89_write_rf(rtwdev, RF_PATH_B, RR_MODOPT_V1, RR_TXG_SEL, 0x4 | idx); + + rtw89_phy_write32_mask(rtwdev, R_KTBL1B_BE4, B_KTBL1_TBL0, idx & BIT(0)); + rtw89_phy_write32_mask(rtwdev, R_KTBL1B_BE4, B_KTBL1_TBL1, (idx & BIT(1)) >> 1); + } +} + +static u8 rtw8922d_chlk_reload_sel_tbl(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, u8 path) +{ + struct rtw89_rfk_mcc_info_data *rfk_mcc = rtwdev->rfk_mcc.data; + struct rtw89_rfk_chan_desc desc[__RTW89_RFK_CHS_NR_V1] = {}; + u8 tbl_sel; + + for (tbl_sel = 0; tbl_sel < ARRAY_SIZE(desc); tbl_sel++) { + struct rtw89_rfk_chan_desc *p = &desc[tbl_sel]; + + p->ch = rfk_mcc->ch[tbl_sel]; + + p->has_band = true; + p->band = rfk_mcc->band[tbl_sel]; + + p->has_bw = true; + p->bw = rfk_mcc->bw[tbl_sel]; + } + + tbl_sel = rtw89_rfk_chan_lookup(rtwdev, desc, ARRAY_SIZE(desc), chan); + + rfk_mcc->ch[tbl_sel] = chan->channel; + rfk_mcc->band[tbl_sel] = chan->band_type; + rfk_mcc->bw[tbl_sel] = chan->band_width; + rfk_mcc->rf18[tbl_sel] = rtw89_chip_chan_to_rf18_val(rtwdev, chan); + + /* shared table array, but tbl_sel can be independent by path */ + rfk_mcc[path].table_idx = tbl_sel; + + return tbl_sel; +} + +static void rtw8922d_chlk_reload(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chan *chan0, *chan1; + u8 s0_tbl, s1_tbl; + + switch (rtwdev->mlo_dbcc_mode) { + default: + case MLO_2_PLUS_0_1RF: + chan0 = rtw89_mgnt_chan_get(rtwdev, 0); + chan1 = chan0; + break; + case MLO_0_PLUS_2_1RF: + chan1 = rtw89_mgnt_chan_get(rtwdev, 1); + chan0 = chan1; + break; + case MLO_1_PLUS_1_1RF: + chan0 = rtw89_mgnt_chan_get(rtwdev, 0); + chan1 = rtw89_mgnt_chan_get(rtwdev, 1); + break; + } + + s0_tbl = rtw8922d_chlk_reload_sel_tbl(rtwdev, chan0, 0); + s1_tbl = rtw8922d_chlk_reload_sel_tbl(rtwdev, chan1, 1); + + rtw8922d_chlk_ktbl_sel(rtwdev, RF_A, s0_tbl); + rtw8922d_chlk_ktbl_sel(rtwdev, RF_B, s1_tbl); +} + +static enum _rf_syn_pow rtw8922d_get_syn_pow(struct rtw89_dev *rtwdev) +{ + switch (rtwdev->mlo_dbcc_mode) { + case MLO_0_PLUS_2_1RF: + return RF_SYN_OFF_ON; + case MLO_0_PLUS_2_2RF: + case MLO_1_PLUS_1_2RF: + case MLO_2_PLUS_0_1RF: + case MLO_2_PLUS_0_2RF: + case MLO_2_PLUS_2_2RF: + case MLO_DBCC_NOT_SUPPORT: + default: + return RF_SYN_ON_OFF; + case MLO_1_PLUS_1_1RF: + case DBCC_LEGACY: + return RF_SYN_ALLON; + } +} + +void rtw8922d_rfk_mlo_ctrl(struct rtw89_dev *rtwdev) +{ + enum _rf_syn_pow syn_pow = rtw8922d_get_syn_pow(rtwdev); + + if (!rtwdev->dbcc_en) + goto set_rfk_reload; + + rtw8922d_set_syn01(rtwdev, syn_pow); + +set_rfk_reload: + rtw8922d_chlk_reload(rtwdev); +} + +void rtw8922d_pre_set_channel_rf(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) +{ + bool mlo_1_1; + + if (!rtwdev->dbcc_en) + return; + + mlo_1_1 = rtw89_is_mlo_1_1(rtwdev); + if (mlo_1_1) + rtw8922d_set_syn01(rtwdev, RF_SYN_ALLON); + else if (phy_idx == RTW89_PHY_0) + rtw8922d_set_syn01(rtwdev, RF_SYN_ON_OFF); + else + rtw8922d_set_syn01(rtwdev, RF_SYN_OFF_ON); + + fsleep(1000); +} + +void rtw8922d_post_set_channel_rf(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) +{ + rtw8922d_rfk_mlo_ctrl(rtwdev); +} diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h index 03af1f0497ac..4c505ae24261 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h @@ -7,8 +7,12 @@ #include "core.h" +void rtw8922d_tssi_cont_en_phyidx(struct rtw89_dev *rtwdev, bool en, u8 phy_idx); void rtw8922d_set_channel_rf(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx); +void rtw8922d_rfk_mlo_ctrl(struct rtw89_dev *rtwdev); +void rtw8922d_pre_set_channel_rf(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); +void rtw8922d_post_set_channel_rf(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); #endif From 2ef4363f1388258f8073cd38d2ba059963f25fcf Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 30 Mar 2026 14:58:41 +0800 Subject: [PATCH 066/124] wifi: rtw89: 8922d: add RF calibration ops The chips ops related to RF calibration include init, init_late, channel, band_change, scan, and track. The init_late is similar to init, but HCI is ready, so receiving C2H event is possible. The ops channel is the main function that do all RF calibration on operating channel. The ops band_change and scan are to reset RF calibration because channel is switching at these moment, we need to reset RF state. The ops track is to monitor temperature to check if re-calibrate RF again. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260330065847.48946-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/reg.h | 4 + drivers/net/wireless/realtek/rtw89/rtw8922d.c | 154 ++++++++++++++++++ .../net/wireless/realtek/rtw89/rtw8922d_rfk.c | 85 ++++++++++ .../net/wireless/realtek/rtw89/rtw8922d_rfk.h | 1 + 4 files changed, 244 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 195d4806c4ef..1a5a5b30a28e 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -10531,6 +10531,10 @@ #define B_TXPWR_RSTB0_BE4 BIT(16) #define R_TSSI_EN_P0_BE4 0x22510 #define B_TSSI_EN_P0_BE4 GENMASK(3, 0) +#define R_USED_TSSI_TRK_ON_P0_BE4 0x22534 +#define B_USED_TSSI_TRK_ON_P0_BE4 BIT(22) +#define R_TSSI_DCK_MOV_AVG_LEN_P0_BE4 0x225CC +#define B_TSSI_DCK_MOV_AVG_LEN_P0_BE4 GENMASK(8, 6) #define R_TXPWR_RSTB1_BE4 0x2260C #define B_TXPWR_RSTB1_BE4 BIT(16) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index a2dd504c99ed..2e6f4504caeb 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -3,6 +3,7 @@ */ #include "chan.h" +#include "coex.h" #include "debug.h" #include "efuse.h" #include "mac.h" @@ -2168,6 +2169,159 @@ static void rtw8922d_set_channel_help(struct rtw89_dev *rtwdev, bool enter, } } +static void rtw8922d_rfk_init(struct rtw89_dev *rtwdev) +{ + struct rtw89_rfk_mcc_info *rfk_mcc = &rtwdev->rfk_mcc; + struct rtw89_lck_info *lck = &rtwdev->lck; + + rtwdev->is_tssi_mode[RF_PATH_A] = false; + rtwdev->is_tssi_mode[RF_PATH_B] = false; + memset(rfk_mcc, 0, sizeof(*rfk_mcc)); + memset(lck, 0, sizeof(*lck)); +} + +static void __rtw8922d_rfk_init_late(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, + const struct rtw89_chan *chan) +{ + rtw8922d_rfk_mlo_ctrl(rtwdev); + + rtw89_phy_rfk_pre_ntfy_and_wait(rtwdev, phy_idx, 5); + if (!test_bit(RTW89_FLAG_SER_HANDLING, rtwdev->flags)) + rtw89_phy_rfk_rxdck_and_wait(rtwdev, phy_idx, chan, false, 128); + if (phy_idx == RTW89_PHY_0) + rtw89_phy_rfk_dack_and_wait(rtwdev, phy_idx, chan, 58); +} + +static void rtw8922d_rfk_init_late(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); + + __rtw8922d_rfk_init_late(rtwdev, RTW89_PHY_0, chan); + if (rtwdev->dbcc_en) + __rtw8922d_rfk_init_late(rtwdev, RTW89_PHY_1, chan); +} + +static void _wait_rx_mode(struct rtw89_dev *rtwdev, u8 kpath) +{ + u32 rf_mode; + u8 path; + int ret; + + for (path = 0; path < RF_PATH_NUM_8922D; path++) { + if (!(kpath & BIT(path))) + continue; + + ret = read_poll_timeout_atomic(rtw89_read_rf, rf_mode, rf_mode != 2, + 2, 5000, false, rtwdev, path, 0x00, + RR_MOD_MASK); + rtw89_debug(rtwdev, RTW89_DBG_RFK, + "[RFK] Wait S%d to Rx mode!! (ret = %d)\n", + path, ret); + } +} + +static void __rtw8922d_tssi_enable(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) +{ + u8 path; + + for (path = RF_PATH_A; path <= RF_PATH_B; path++) { + u32 addr_ofst = (phy_idx << 12) + (path << 8); + + rtw89_phy_write32_mask(rtwdev, R_TSSI_DCK_MOV_AVG_LEN_P0_BE4 + addr_ofst, + B_TSSI_DCK_MOV_AVG_LEN_P0_BE4, 0x4); + rtw89_phy_write32_clr(rtwdev, R_USED_TSSI_TRK_ON_P0_BE4 + addr_ofst, + B_USED_TSSI_TRK_ON_P0_BE4); + rtw89_phy_write32_set(rtwdev, R_USED_TSSI_TRK_ON_P0_BE4 + addr_ofst, + B_USED_TSSI_TRK_ON_P0_BE4); + rtw89_phy_write32_clr(rtwdev, R_TSSI_EN_P0_BE4 + addr_ofst, + B_TSSI_EN_P0_BE4); + rtw89_phy_write32_mask(rtwdev, R_TSSI_EN_P0_BE4 + addr_ofst, + B_TSSI_EN_P0_BE4, 0x3); + } +} + +static void __rtw8922d_tssi_disable(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) +{ + u8 path; + + for (path = RF_PATH_A; path <= RF_PATH_B; path++) { + u32 addr_ofst = (phy_idx << 12) + (path << 8); + + rtw89_phy_write32_clr(rtwdev, R_TSSI_DCK_MOV_AVG_LEN_P0_BE4 + addr_ofst, + B_TSSI_DCK_MOV_AVG_LEN_P0_BE4); + rtw89_phy_write32_clr(rtwdev, R_USED_TSSI_TRK_ON_P0_BE4 + addr_ofst, + B_USED_TSSI_TRK_ON_P0_BE4); + rtw89_phy_write32_clr(rtwdev, R_TSSI_EN_P0_BE4 + addr_ofst, + B_TSSI_EN_P0_BE4); + } +} + +static void rtw8922d_rfk_tssi(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, + const struct rtw89_chan *chan, + enum rtw89_tssi_mode tssi_mode, + unsigned int ms) +{ + int ret; + + ret = rtw89_phy_rfk_tssi_and_wait(rtwdev, phy_idx, chan, tssi_mode, ms); + if (ret) { + rtwdev->is_tssi_mode[RF_PATH_A] = false; + rtwdev->is_tssi_mode[RF_PATH_B] = false; + } else { + rtwdev->is_tssi_mode[RF_PATH_A] = true; + rtwdev->is_tssi_mode[RF_PATH_B] = true; + } +} + +static void rtw8922d_rfk_channel(struct rtw89_dev *rtwdev, + struct rtw89_vif_link *rtwvif_link) +{ + enum rtw89_chanctx_idx chanctx_idx = rtwvif_link->chanctx_idx; + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, chanctx_idx); + enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx; + u8 phy_map = rtw89_btc_phymap(rtwdev, phy_idx, RF_AB, chanctx_idx); + u32 tx_en; + + rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_CHLK, BTC_WRFK_START); + rtw89_chip_stop_sch_tx(rtwdev, phy_idx, &tx_en, RTW89_SCH_TX_SEL_ALL); + _wait_rx_mode(rtwdev, RF_AB); + + rtw89_phy_rfk_pre_ntfy_and_wait(rtwdev, phy_idx, 5); + rtw89_phy_rfk_txgapk_and_wait(rtwdev, phy_idx, chan, 54); + rtw89_phy_rfk_txiqk_and_wait(rtwdev, phy_idx, chan, 45); + rtw89_phy_rfk_iqk_and_wait(rtwdev, phy_idx, chan, 84); + rtw8922d_rfk_tssi(rtwdev, phy_idx, chan, RTW89_TSSI_NORMAL, 20); + rtw89_phy_rfk_cim3k_and_wait(rtwdev, phy_idx, chan, 44); + rtw89_phy_rfk_dpk_and_wait(rtwdev, phy_idx, chan, 68); + rtw89_phy_rfk_rxdck_and_wait(rtwdev, RTW89_PHY_0, chan, true, 32); + + rtw89_chip_resume_sch_tx(rtwdev, phy_idx, tx_en); + rtw89_btc_ntfy_wl_rfk(rtwdev, phy_map, BTC_WRFKT_CHLK, BTC_WRFK_STOP); +} + +static void rtw8922d_rfk_band_changed(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx, + const struct rtw89_chan *chan) +{ +} + +static void rtw8922d_rfk_scan(struct rtw89_dev *rtwdev, + struct rtw89_vif_link *rtwvif_link, + bool start) +{ + if (start) + __rtw8922d_tssi_disable(rtwdev, rtwvif_link->phy_idx); + else + __rtw8922d_tssi_enable(rtwdev, rtwvif_link->phy_idx); +} + +static void rtw8922d_rfk_track(struct rtw89_dev *rtwdev) +{ + rtw8922d_lck_track(rtwdev); +} + MODULE_FIRMWARE(RTW8922D_MODULE_FIRMWARE); MODULE_FIRMWARE(RTW8922DS_MODULE_FIRMWARE); MODULE_AUTHOR("Realtek Corporation"); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c index d1eda19a39a9..147cf91d2cb0 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c @@ -261,3 +261,88 @@ void rtw8922d_post_set_channel_rf(struct rtw89_dev *rtwdev, enum rtw89_phy_idx p { rtw8922d_rfk_mlo_ctrl(rtwdev); } + +static u8 _get_thermal(struct rtw89_dev *rtwdev, enum rtw89_rf_path path) +{ + rtw89_write_rf(rtwdev, path, RR_TM, RR_TM_TRI, 0x1); + rtw89_write_rf(rtwdev, path, RR_TM, RR_TM_TRI, 0x0); + rtw89_write_rf(rtwdev, path, RR_TM, RR_TM_TRI, 0x1); + + fsleep(200); + + return rtw89_read_rf(rtwdev, path, RR_TM, RR_TM_VAL_V1); +} + +static void _lck_keep_thermal(struct rtw89_dev *rtwdev) +{ + struct rtw89_lck_info *lck = &rtwdev->lck; + int path; + + for (path = 0; path < rtwdev->chip->rf_path_num; path++) { + lck->thermal[path] = _get_thermal(rtwdev, path); + rtw89_debug(rtwdev, RTW89_DBG_RFK_TRACK, + "[LCK] path=%d thermal=0x%x", path, lck->thermal[path]); + } +} + +static void _lck(struct rtw89_dev *rtwdev) +{ + enum _rf_syn_pow syn_pow = rtw8922d_get_syn_pow(rtwdev); + u8 path_mask = 0; + u32 tmp18, tmp5; + int path; + + rtw89_debug(rtwdev, RTW89_DBG_RFK_TRACK, "[LCK] DO LCK\n"); + + if (syn_pow == RF_SYN_ALLON) + path_mask = BIT(RF_PATH_A) | BIT(RF_PATH_B); + else if (syn_pow == RF_SYN_ON_OFF) + path_mask = BIT(RF_PATH_A); + else if (syn_pow == RF_SYN_OFF_ON) + path_mask = BIT(RF_PATH_B); + else + return; + + for (path = 0; path < rtwdev->chip->rf_path_num; path++) { + if (!(path_mask & BIT(path))) + continue; + + tmp18 = rtw89_read_rf(rtwdev, path, RR_CFGCH, MASKDWORD); + tmp5 = rtw89_read_rf(rtwdev, path, RR_RSV1, MASKDWORD); + + rtw89_write_rf(rtwdev, path, RR_MOD, MASKDWORD, 0x10000); + rtw89_write_rf(rtwdev, path, RR_RSV1, MASKDWORD, 0x0); + rtw89_write_rf(rtwdev, path, RR_LCK_TRG, RR_LCK_TRGSEL, 0x1); + rtw89_write_rf(rtwdev, path, RR_CFGCH, MASKDWORD, tmp18); + rtw89_write_rf(rtwdev, path, RR_LCK_TRG, RR_LCK_TRGSEL, 0x0); + + fsleep(400); + + rtw89_write_rf(rtwdev, path, RR_RSV1, MASKDWORD, tmp5); + } + + _lck_keep_thermal(rtwdev); +} + +#define RTW8922D_LCK_TH 16 +void rtw8922d_lck_track(struct rtw89_dev *rtwdev) +{ + struct rtw89_lck_info *lck = &rtwdev->lck; + u8 cur_thermal; + int delta; + int path; + + for (path = 0; path < rtwdev->chip->rf_path_num; path++) { + cur_thermal = _get_thermal(rtwdev, path); + delta = abs((int)cur_thermal - lck->thermal[path]); + + rtw89_debug(rtwdev, RTW89_DBG_RFK_TRACK, + "[LCK] path=%d current thermal=0x%x delta=0x%x\n", + path, cur_thermal, delta); + + if (delta >= RTW8922D_LCK_TH) { + _lck(rtwdev); + return; + } + } +} diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h index 4c505ae24261..8a5f4b56b8ce 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h @@ -14,5 +14,6 @@ void rtw8922d_set_channel_rf(struct rtw89_dev *rtwdev, void rtw8922d_rfk_mlo_ctrl(struct rtw89_dev *rtwdev); void rtw8922d_pre_set_channel_rf(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); void rtw8922d_post_set_channel_rf(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); +void rtw8922d_lck_track(struct rtw89_dev *rtwdev); #endif From baa6ea4e5e034c59fd8899d200ead348e148ea06 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 30 Mar 2026 14:58:42 +0800 Subject: [PATCH 067/124] wifi: rtw89: 8922d: add set TX power callback Set TX power depends on operating channel. The Tx power factors are data rate, channel, bandwidth and etc. Also, consider SAR as a factor of TX power limit. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260330065847.48946-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/reg.h | 13 ++ drivers/net/wireless/realtek/rtw89/rtw8922d.c | 115 ++++++++++++++++++ 2 files changed, 128 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 1a5a5b30a28e..37de1c827814 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -10235,6 +10235,8 @@ #define B_TSSI_CONT_EN BIT(3) #define R_P0_TXPWRB_BE 0xE61C #define R_P1_TXPWRB_BE 0xE71C +#define R_P0_TXPWRB_BE4 0x2251C +#define R_P1_TXPWRB_BE4 0x2261C #define B_TXPWRB_MAX_BE GENMASK(20, 12) #define R_TSSI_MAP_OFST_P0 0xE620 #define R_TSSI_MAP_OFST_P1 0xE720 @@ -10531,13 +10533,24 @@ #define B_TXPWR_RSTB0_BE4 BIT(16) #define R_TSSI_EN_P0_BE4 0x22510 #define B_TSSI_EN_P0_BE4 GENMASK(3, 0) +#define R_TXAGC_REF_DBM_PATH0_TBL0_BE4 0x22528 +#define B_TXAGC_OFDM_REF_DBM_PATH0_TBL0_BE4 GENMASK(8, 0) +#define B_TXAGC_CCK_REF_DBM_PATH0_TBL0_BE4 GENMASK(17, 9) #define R_USED_TSSI_TRK_ON_P0_BE4 0x22534 #define B_USED_TSSI_TRK_ON_P0_BE4 BIT(22) +#define R_TSSI_K_OFDM_PATH0_TBL0_BE4 0x225A0 +#define B_TSSI_K_OFDM_PATH0_TBL0_BE4 GENMASK(29, 20) #define R_TSSI_DCK_MOV_AVG_LEN_P0_BE4 0x225CC #define B_TSSI_DCK_MOV_AVG_LEN_P0_BE4 GENMASK(8, 6) #define R_TXPWR_RSTB1_BE4 0x2260C #define B_TXPWR_RSTB1_BE4 BIT(16) +#define R_TXAGC_REF_DBM_PATH0_TBL1_BE4 0x23528 +#define B_TXAGC_OFDM_REF_DBM_PATH0_TBL1_BE4 GENMASK(8, 0) +#define B_TXAGC_CCK_REF_DBM_PATH0_TBL1_BE4 GENMASK(17, 9) +#define R_TSSI_K_OFDM_PATH0_TBL1_BE4 0x235A0 +#define B_TSSI_K_OFDM_PATH0_TBL1_BE4 GENMASK(29, 20) + #define R_OFDM_OFST_P0_BE4 0x240C8 #define B_OFDM_OFST_P0_BE4 GENMASK(31, 24) #define R_PATH0_RXIDX_INIT_BE4 0x24108 diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 2e6f4504caeb..9c62a5f12962 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -11,6 +11,7 @@ #include "reg.h" #include "rtw8922d.h" #include "rtw8922d_rfk.h" +#include "sar.h" #include "util.h" #define RTW8922D_FW_FORMAT_MAX 0 @@ -2322,6 +2323,120 @@ static void rtw8922d_rfk_track(struct rtw89_dev *rtwdev) rtw8922d_lck_track(rtwdev); } +static const struct rtw89_reg_def rtw8922d_txpwr_ref[][3] = { + {{ .addr = R_TXAGC_REF_DBM_PATH0_TBL0_BE4, + .mask = B_TXAGC_OFDM_REF_DBM_PATH0_TBL0_BE4 }, + { .addr = R_TXAGC_REF_DBM_PATH0_TBL0_BE4, + .mask = B_TXAGC_CCK_REF_DBM_PATH0_TBL0_BE4 }, + { .addr = R_TSSI_K_OFDM_PATH0_TBL0_BE4, + .mask = B_TSSI_K_OFDM_PATH0_TBL0_BE4 } + }, + {{ .addr = R_TXAGC_REF_DBM_PATH0_TBL1_BE4, + .mask = B_TXAGC_OFDM_REF_DBM_PATH0_TBL1_BE4 }, + { .addr = R_TXAGC_REF_DBM_PATH0_TBL1_BE4, + .mask = B_TXAGC_CCK_REF_DBM_PATH0_TBL1_BE4 }, + { .addr = R_TSSI_K_OFDM_PATH0_TBL1_BE4, + .mask = B_TSSI_K_OFDM_PATH0_TBL1_BE4 } + }, +}; + +static void rtw8922d_set_txpwr_diff(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + s16 pwr_ofst = rtw89_phy_ant_gain_pwr_offset(rtwdev, chan); + const struct rtw89_chip_info *chip = rtwdev->chip; + static const u32 path_ofst[] = {0x0, 0x100}; + const struct rtw89_reg_def *txpwr_ref; + s16 tssi_k_ofst = abs(pwr_ofst); + s16 ofst_dec[RF_PATH_NUM_8922D]; + s16 tssi_k[RF_PATH_NUM_8922D]; + s16 pwr_ref_ofst; + s16 pwr_ref = 16; + u8 i; + + pwr_ref <<= chip->txpwr_factor_rf; + pwr_ref_ofst = pwr_ref - rtw89_phy_txpwr_bb_to_rf(rtwdev, abs(pwr_ofst)); + + ofst_dec[RF_PATH_A] = pwr_ofst > 0 ? pwr_ref : pwr_ref_ofst; + ofst_dec[RF_PATH_B] = pwr_ofst > 0 ? pwr_ref_ofst : pwr_ref; + tssi_k[RF_PATH_A] = pwr_ofst > 0 ? 0 : tssi_k_ofst; + tssi_k[RF_PATH_B] = pwr_ofst > 0 ? tssi_k_ofst : 0; + + for (i = 0; i < RF_PATH_NUM_8922D; i++) { + txpwr_ref = rtw8922d_txpwr_ref[phy_idx]; + + rtw89_phy_write32_mask(rtwdev, txpwr_ref[0].addr + path_ofst[i], + txpwr_ref[0].mask, ofst_dec[i]); + rtw89_phy_write32_mask(rtwdev, txpwr_ref[1].addr + path_ofst[i], + txpwr_ref[1].mask, ofst_dec[i]); + rtw89_phy_write32_mask(rtwdev, txpwr_ref[2].addr + path_ofst[i], + txpwr_ref[2].mask, tssi_k[i]); + } +} + +static void rtw8922d_set_txpwr_ref(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + s16 ref_ofdm = 0; + s16 ref_cck = 0; + + rtw89_debug(rtwdev, RTW89_DBG_TXPWR, "[TXPWR] set txpwr reference\n"); + + rtw8922d_set_txpwr_diff(rtwdev, chan, phy_idx); + + rtw89_mac_txpwr_write32_mask(rtwdev, phy_idx, R_BE_PWR_REF_CTRL, + B_BE_PWR_REF_CTRL_OFDM, ref_ofdm); + rtw89_mac_txpwr_write32_mask(rtwdev, phy_idx, R_BE_PWR_REF_CTRL, + B_BE_PWR_REF_CTRL_CCK, ref_cck); +} + +static void rtw8922d_set_txpwr_sar_diff(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + struct rtw89_sar_parm sar_parm = { + .center_freq = chan->freq, + .force_path = true, + }; + s16 sar_rf; + s8 sar_mac; + + if (phy_idx != RTW89_PHY_0) + return; + + sar_parm.path = RF_PATH_A; + sar_mac = rtw89_query_sar(rtwdev, &sar_parm); + sar_rf = rtw89_phy_txpwr_mac_to_rf(rtwdev, sar_mac); + rtw89_phy_write32_mask(rtwdev, R_P0_TXPWRB_BE4, B_TXPWRB_MAX_BE, sar_rf); + + sar_parm.path = RF_PATH_B; + sar_mac = rtw89_query_sar(rtwdev, &sar_parm); + sar_rf = rtw89_phy_txpwr_mac_to_rf(rtwdev, sar_mac); + rtw89_phy_write32_mask(rtwdev, R_P1_TXPWRB_BE4, B_TXPWRB_MAX_BE, sar_rf); +} + +static void rtw8922d_set_txpwr(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + rtw89_phy_set_txpwr_byrate(rtwdev, chan, phy_idx); + rtw89_phy_set_txpwr_offset(rtwdev, chan, phy_idx); + rtw89_phy_set_txpwr_limit(rtwdev, chan, phy_idx); + rtw89_phy_set_txpwr_limit_ru(rtwdev, chan, phy_idx); + rtw8922d_set_txpwr_ref(rtwdev, chan, phy_idx); + rtw8922d_set_txpwr_sar_diff(rtwdev, chan, phy_idx); +} + +static void rtw8922d_set_txpwr_ctrl(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx) +{ + const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, phy_idx); + + rtw8922d_set_txpwr_ref(rtwdev, chan, phy_idx); +} + MODULE_FIRMWARE(RTW8922D_MODULE_FIRMWARE); MODULE_FIRMWARE(RTW8922DS_MODULE_FIRMWARE); MODULE_AUTHOR("Realtek Corporation"); From 2b19199952e6f2d6fbddd20c81d48180e98a3c3e Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 30 Mar 2026 14:58:43 +0800 Subject: [PATCH 068/124] wifi: rtw89: 8922d: configure TX/RX path assisting in BT coexistence The BT coexistence mechanism needs to control TX/RX path to co-work with BT well, and these helpers are provided by BB to configure path. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260330065847.48946-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/reg.h | 9 +- drivers/net/wireless/realtek/rtw89/rtw8922d.c | 125 ++++++++++++++++++ 2 files changed, 132 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 37de1c827814..78f2cf579fa6 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -10469,8 +10469,9 @@ #define B_EMLSR_BB_CLK_BE4 GENMASK(31, 30) #define R_CHINFO_SEG_BE4 0x200B4 #define B_CHINFO_SEG_LEN_BE4 GENMASK(12, 10) -#define R_STS_HDR2_PARSING_BE4 0x2070C -#define B_STS_HDR2_PARSING_BE4 BIT(10) +#define R_SEL_GNT_BT_RX_BE4 0x2010C +#define B_SEL_GNT_BT_RX_PATH0_BE4 GENMASK(3, 0) +#define B_SEL_GNT_BT_RX_PATH1_BE4 GENMASK(11, 8) #define R_SW_SI_WDATA_BE4 0x20370 #define B_SW_SI_DATA_PATH_BE4 GENMASK(31, 28) #define B_SW_SI_DATA_ADR_BE4 GENMASK(27, 20) @@ -10490,9 +10491,13 @@ #define B_ENABLE_CCK0_BE4 BIT(5) #define R_RSTB_ASYNC_BE4 0x20704 #define B_RSTB_ASYNC_BE4 BIT(1) +#define R_STS_HDR2_PARSING_BE4 0x2070C +#define B_STS_HDR2_PARSING_BE4 BIT(10) #define R_EDCCA_RPT_SEL_BE4 0x20780 #define R_EDCCA_RPT_SEL_BE4_C1 0x21780 #define B_EDCCA_RPT_SEL_BE4_MSK 0xE0000 +#define R_SEL_GNT_BT_RXPHY_BE4 0x2079C +#define B_SEL_GNT_BT_RXPHY_BE4 GENMASK(11, 8) #define R_IMR_TX_ERROR_BE4 0x20920 #define B_IMR_TX_ERROR_BE4 BIT(30) #define R_TXINFO_PATH_BE4 0x209A4 diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 9c62a5f12962..cfe9752abddc 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -238,6 +238,35 @@ static const struct rtw89_efuse_block_cfg rtw8922d_efuse_blocks[] = { [RTW89_EFUSE_BLOCK_ADIE] = {.offset = 0x70000, .size = 0x10}, }; +static void rtw8922d_sel_bt_rx_path(struct rtw89_dev *rtwdev, u8 val, + enum rtw89_rf_path rx_path) +{ + if (rx_path == RF_PATH_A) + rtw89_phy_write32_mask(rtwdev, R_SEL_GNT_BT_RX_BE4, + B_SEL_GNT_BT_RX_PATH0_BE4, val); + else if (rx_path == RF_PATH_B) + rtw89_phy_write32_mask(rtwdev, R_SEL_GNT_BT_RX_BE4, + B_SEL_GNT_BT_RX_PATH1_BE4, val); + else + rtw89_warn(rtwdev, "[%s] Not support path = %d\n", __func__, rx_path); +} + +static void rtw8922d_sel_bt_rx_phy(struct rtw89_dev *rtwdev, u8 val, + enum rtw89_phy_idx phy_idx) +{ + rtw89_phy_write32_idx(rtwdev, R_SEL_GNT_BT_RXPHY_BE4, + B_SEL_GNT_BT_RXPHY_BE4, val, phy_idx); +} + +static void rtw8922d_set_gbt_bt_rx_sel(struct rtw89_dev *rtwdev, bool en, + enum rtw89_phy_idx phy_idx) +{ + rtw8922d_sel_bt_rx_path(rtwdev, 0x3, RF_PATH_A); + rtw8922d_sel_bt_rx_phy(rtwdev, 0x0, RTW89_PHY_0); + rtw8922d_sel_bt_rx_path(rtwdev, 0x3, RF_PATH_B); + rtw8922d_sel_bt_rx_phy(rtwdev, 0x0, RTW89_PHY_1); +} + static int rtw8922d_pwr_on_func(struct rtw89_dev *rtwdev) { struct rtw89_hal *hal = &rtwdev->hal; @@ -2437,6 +2466,102 @@ static void rtw8922d_set_txpwr_ctrl(struct rtw89_dev *rtwdev, rtw8922d_set_txpwr_ref(rtwdev, chan, phy_idx); } +static void rtw8922d_ctrl_trx_path(struct rtw89_dev *rtwdev, + enum rtw89_rf_path tx_path, u8 tx_nss, + enum rtw89_rf_path rx_path, u8 rx_nss) +{ + enum rtw89_phy_idx phy_idx; + + for (phy_idx = RTW89_PHY_0; phy_idx <= RTW89_PHY_1; phy_idx++) { + rtw8922d_ctrl_tx_path_tmac(rtwdev, tx_path, phy_idx); + rtw8922d_ctrl_rx_path_tmac(rtwdev, rx_path, phy_idx); + + rtw8922d_tssi_reset(rtwdev, rx_path, phy_idx); + } +} + +static void rtw8922d_ctrl_nbtg_bt_tx(struct rtw89_dev *rtwdev, bool en, + enum rtw89_phy_idx phy_idx) +{ + if (en) { + rtw89_phy_write32_idx(rtwdev, R_FORCE_FIR_A, B_FORCE_FIR_A, 0x3, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RXBY_WBADC_A, B_RXBY_WBADC_A, + 0xf, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BT_RXBY_WBADC_A, B_BT_RXBY_WBADC_A, + 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BT_SHARE_A, B_BT_TRK_OFF_A, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_OP1DB_A, B_OP1DB_A, 0x80, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_OP1DB1_A, B_TIA10_A, 0x8080, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BACKOFF_A, B_LNA_IBADC_A, 0x34, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BKOFF_A, B_BKOFF_IBADC_A, 0x34, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_FORCE_FIR_B, B_FORCE_FIR_B, 0x3, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RXBY_WBADC_B, B_RXBY_WBADC_B, + 0xf, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BT_RXBY_WBADC_B, B_BT_RXBY_WBADC_B, + 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BT_SHARE_B, B_BT_TRK_OFF_B, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_LNA_TIA, B_TIA1_B, 0x80, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BACKOFF_B, B_LNA_IBADC_B, 0x34, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BKOFF_B, B_BKOFF_IBADC_B, 0x34, phy_idx); + } else { + rtw89_phy_write32_idx(rtwdev, R_FORCE_FIR_A, B_FORCE_FIR_A, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RXBY_WBADC_A, B_RXBY_WBADC_A, + 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BT_RXBY_WBADC_A, B_BT_RXBY_WBADC_A, + 0x1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BT_SHARE_A, B_BT_TRK_OFF_A, 0x1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_OP1DB_A, B_OP1DB_A, 0x1a, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_OP1DB1_A, B_TIA10_A, 0x2a2a, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BACKOFF_A, B_LNA_IBADC_A, 0x7a6, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BKOFF_A, B_BKOFF_IBADC_A, 0x26, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_FORCE_FIR_B, B_FORCE_FIR_B, 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_RXBY_WBADC_B, B_RXBY_WBADC_B, + 0x0, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BT_RXBY_WBADC_B, B_BT_RXBY_WBADC_B, + 0x1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BT_SHARE_B, B_BT_TRK_OFF_B, 0x1, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_LNA_TIA, B_TIA1_B, 0x2a, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BACKOFF_B, B_LNA_IBADC_B, 0x7a6, phy_idx); + rtw89_phy_write32_idx(rtwdev, R_BKOFF_B, B_BKOFF_IBADC_B, 0x26, phy_idx); + } +} + +static void rtw8922d_bb_cfg_txrx_path(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); + enum rtw89_band band = chan->band_type; + struct rtw89_hal *hal = &rtwdev->hal; + u8 ntx_path = RF_PATH_AB; + u8 nrx_path = RF_PATH_AB; + u32 tx_en0, tx_en1; + u8 rx_nss = 2; + + if (hal->antenna_tx == RF_A) + ntx_path = RF_PATH_A; + else if (hal->antenna_tx == RF_B) + ntx_path = RF_PATH_B; + + if (hal->antenna_rx == RF_A) + nrx_path = RF_PATH_A; + else if (hal->antenna_rx == RF_B) + nrx_path = RF_PATH_B; + + if (nrx_path != RF_PATH_AB) + rx_nss = 1; + + rtw8922d_hal_reset(rtwdev, RTW89_PHY_0, RTW89_MAC_0, band, &tx_en0, true); + if (rtwdev->dbcc_en) + rtw8922d_hal_reset(rtwdev, RTW89_PHY_1, RTW89_MAC_1, band, + &tx_en1, true); + + rtw8922d_ctrl_trx_path(rtwdev, ntx_path, 2, nrx_path, rx_nss); + + rtw8922d_hal_reset(rtwdev, RTW89_PHY_0, RTW89_MAC_0, band, &tx_en0, false); + if (rtwdev->dbcc_en) + rtw8922d_hal_reset(rtwdev, RTW89_PHY_1, RTW89_MAC_1, band, + &tx_en1, false); +} + MODULE_FIRMWARE(RTW8922D_MODULE_FIRMWARE); MODULE_FIRMWARE(RTW8922DS_MODULE_FIRMWARE); MODULE_AUTHOR("Realtek Corporation"); From 9c2f79b4d912dc96bbe5b4dadccd1099d430436d Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 30 Mar 2026 14:58:44 +0800 Subject: [PATCH 069/124] wifi: rtw89: 8922d: add RF ops of init hardware and get thermal One is to initialize hardware for RF circuit, and the ops of get thermal is used to monitor temperature to re-calibrate RF or reduce TX duty to prevent overheating. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260330065847.48946-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/reg.h | 6 ++ drivers/net/wireless/realtek/rtw89/rtw8922d.c | 57 +++++++++++++++++++ .../net/wireless/realtek/rtw89/rtw8922d_rfk.c | 24 ++++++++ .../net/wireless/realtek/rtw89/rtw8922d_rfk.h | 3 + 4 files changed, 90 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 78f2cf579fa6..e5e689f1bfa3 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -10676,6 +10676,12 @@ #define B_KTBL1_TBL0 BIT(3) #define B_KTBL1_TBL1 BIT(5) +#define R_TC_EN_BE4 0x3c200 +#define B_TC_EN_BE4 BIT(0) +#define B_TC_TRIG_BE4 BIT(1) +#define R_TC_VAL_BE4 0x3c208 +#define B_TC_VAL_BE4 GENMASK(7, 0) + /* WiFi CPU local domain */ #define R_AX_WDT_CTRL 0x0040 #define B_AX_WDT_EN BIT(31) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index cfe9752abddc..49b84d49ccac 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -2562,6 +2562,63 @@ static void rtw8922d_bb_cfg_txrx_path(struct rtw89_dev *rtwdev) &tx_en1, false); } +static u8 rtw8922d_get_thermal(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path) +{ + u8 val; + + rtw89_phy_write32_mask(rtwdev, R_TC_EN_BE4, B_TC_EN_BE4, 0x1); + rtw89_phy_write32_mask(rtwdev, R_TC_EN_BE4, B_TC_TRIG_BE4, 0x0); + rtw89_phy_write32_mask(rtwdev, R_TC_EN_BE4, B_TC_TRIG_BE4, 0x1); + + fsleep(100); + + val = rtw89_phy_read32_mask(rtwdev, R_TC_VAL_BE4, B_TC_VAL_BE4); + + return val; +} + +static u32 rtw8922d_chan_to_rf18_val(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan) +{ + u32 val = u32_encode_bits(chan->channel, RR_CFGCH_CH); + + switch (chan->band_type) { + case RTW89_BAND_2G: + default: + break; + case RTW89_BAND_5G: + val |= u32_encode_bits(CFGCH_BAND1_5G, RR_CFGCH_BAND1) | + u32_encode_bits(CFGCH_BAND0_5G, RR_CFGCH_BAND0); + break; + case RTW89_BAND_6G: + val |= u32_encode_bits(CFGCH_BAND1_6G, RR_CFGCH_BAND1) | + u32_encode_bits(CFGCH_BAND0_6G, RR_CFGCH_BAND0); + break; + } + + switch (chan->band_width) { + case RTW89_CHANNEL_WIDTH_5: + case RTW89_CHANNEL_WIDTH_10: + case RTW89_CHANNEL_WIDTH_20: + default: + break; + case RTW89_CHANNEL_WIDTH_40: + val |= u32_encode_bits(CFGCH_BW_V2_40M, RR_CFGCH_BW_V2); + break; + case RTW89_CHANNEL_WIDTH_80: + val |= u32_encode_bits(CFGCH_BW_V2_80M, RR_CFGCH_BW_V2); + break; + case RTW89_CHANNEL_WIDTH_160: + val |= u32_encode_bits(CFGCH_BW_V2_160M, RR_CFGCH_BW_V2); + break; + case RTW89_CHANNEL_WIDTH_320: + val |= u32_encode_bits(CFGCH_BW_V2_320M, RR_CFGCH_BW_V2); + break; + } + + return val; +} + MODULE_FIRMWARE(RTW8922D_MODULE_FIRMWARE); MODULE_FIRMWARE(RTW8922DS_MODULE_FIRMWARE); MODULE_AUTHOR("Realtek Corporation"); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c index 147cf91d2cb0..4e6a8e88a71e 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.c @@ -9,6 +9,12 @@ #include "rtw8922d.h" #include "rtw8922d_rfk.h" +static const struct rtw89_reg5_def rtw8922d_nctl_post_defs[] = { + RTW89_DECL_RFK_WM(0x20c7c, 0x00e00000, 0x1), +}; + +RTW89_DECLARE_RFK_TBL(rtw8922d_nctl_post_defs); + static void rtw8922d_tssi_cont_en(struct rtw89_dev *rtwdev, bool en, enum rtw89_rf_path path, u8 phy_idx) { @@ -239,6 +245,24 @@ set_rfk_reload: rtw8922d_chlk_reload(rtwdev); } +static void rtw8922d_x4k_setting(struct rtw89_dev *rtwdev) +{ + u32 val; + + val = rtw89_read_rf(rtwdev, RF_PATH_A, 0xB9, 0xF000); + rtw89_write_rf(rtwdev, RF_PATH_A, 0xB9, 0xF000, val); + val = rtw89_read_rf(rtwdev, RF_PATH_B, 0xB9, 0xF000); + rtw89_write_rf(rtwdev, RF_PATH_B, 0xB9, 0xF000, val); + + rtw89_write_rf(rtwdev, RF_PATH_A, 0xC2, BIT(19), 0x1); + rtw89_write_rf(rtwdev, RF_PATH_B, 0xC2, BIT(19), 0x1); +} + +void rtw8922d_rfk_hw_init(struct rtw89_dev *rtwdev) +{ + rtw8922d_x4k_setting(rtwdev); +} + void rtw8922d_pre_set_channel_rf(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) { bool mlo_1_1; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h index 8a5f4b56b8ce..c5bbe0eb972a 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d_rfk.h @@ -7,10 +7,13 @@ #include "core.h" +extern const struct rtw89_rfk_tbl rtw8922d_nctl_post_defs_tbl; + void rtw8922d_tssi_cont_en_phyidx(struct rtw89_dev *rtwdev, bool en, u8 phy_idx); void rtw8922d_set_channel_rf(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx); +void rtw8922d_rfk_hw_init(struct rtw89_dev *rtwdev); void rtw8922d_rfk_mlo_ctrl(struct rtw89_dev *rtwdev); void rtw8922d_pre_set_channel_rf(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); void rtw8922d_post_set_channel_rf(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); From 553fd44ce8ce3caccf580aa8286d8c211fea2a4a Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 30 Mar 2026 14:58:45 +0800 Subject: [PATCH 070/124] wifi: rtw89: 8922d: add ops related to BT coexistence mechanism The ops is used by shared BT coexistence mechanism to set WiFi TX power, get BT RSSI, and TX/RX parameters. The RTL8922D uses TX/RX parameter v9, so define it and fill NULL for other chips. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260330065847.48946-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 21 +++ drivers/net/wireless/realtek/rtw89/rtw8851b.c | 4 + drivers/net/wireless/realtek/rtw89/rtw8852a.c | 4 + drivers/net/wireless/realtek/rtw89/rtw8852b.c | 4 + .../net/wireless/realtek/rtw89/rtw8852bt.c | 4 + drivers/net/wireless/realtek/rtw89/rtw8852c.c | 4 + drivers/net/wireless/realtek/rtw89/rtw8922a.c | 4 + drivers/net/wireless/realtek/rtw89/rtw8922d.c | 123 ++++++++++++++++++ 8 files changed, 168 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 4b4d25d8ba98..fd29dbbb120d 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -873,6 +873,14 @@ enum rtw89_phy_idx { RTW89_PHY_NUM, }; +enum rtw89_fbtc_bt_index { + BTC_BT_1ST = 0x0, + BTC_BT_2ND = 0x1, + BTC_BT_EXT = 0x2, + BTC_ALL_BT = 0x2, + BTC_ALL_BT_EZL = 0x3 /* BT0+BT1+Ext-ZB(or Thread, or LTE) */ +}; + #define __RTW89_MLD_MAX_LINK_NUM 2 #define RTW89_MLD_NON_STA_LINK_NUM 1 @@ -2197,6 +2205,15 @@ struct rtw89_btc_bt_info { u32 rsvd: 17; }; +struct rtw89_btc_rf_trx_para_v9 { + u32 wl_tx_power[RTW89_PHY_NUM]; /* absolute Tx power (dBm), 1's complement -5->0x85 */ + u32 wl_rx_gain[RTW89_PHY_NUM]; /* rx gain table index (TBD.) */ + u32 bt_tx_power[BTC_ALL_BT]; /* decrease Tx power (dB) */ + u32 bt_rx_gain[BTC_ALL_BT]; /* LNA constrain level */ + u32 zb_tx_power[BTC_ALL_BT]; /* 15.4 devrease Tx power (dB) */ + u32 zb_rx_gain[BTC_ALL_BT]; /* 15.4 constrain level */ +}; + struct rtw89_btc_cx { struct rtw89_btc_wl_info wl; struct rtw89_btc_bt_info bt; @@ -4609,6 +4626,10 @@ struct rtw89_chip_info { const struct rtw89_btc_rf_trx_para *rf_para_ulink; u8 rf_para_dlink_num; const struct rtw89_btc_rf_trx_para *rf_para_dlink; + const struct rtw89_btc_rf_trx_para_v9 *rf_para_ulink_v9; + const struct rtw89_btc_rf_trx_para_v9 *rf_para_dlink_v9; + u8 rf_para_ulink_num_v9; + u8 rf_para_dlink_num_v9; u8 ps_mode_supported; u8 low_power_hci_modes; diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index 2005934f64d2..84bdd39b3ceb 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -2681,6 +2681,10 @@ const struct rtw89_chip_info rtw8851b_chip_info = { .rf_para_ulink = rtw89_btc_8851b_rf_ul, .rf_para_dlink_num = ARRAY_SIZE(rtw89_btc_8851b_rf_dl), .rf_para_dlink = rtw89_btc_8851b_rf_dl, + .rf_para_ulink_v9 = NULL, + .rf_para_dlink_v9 = NULL, + .rf_para_ulink_num_v9 = 0, + .rf_para_dlink_num_v9 = 0, .ps_mode_supported = BIT(RTW89_PS_MODE_RFOFF) | BIT(RTW89_PS_MODE_CLK_GATED), .low_power_hci_modes = 0, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 12ed35f548c1..1d4f1df524a1 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2418,6 +2418,10 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .rf_para_ulink = rtw89_btc_8852a_rf_ul, .rf_para_dlink_num = ARRAY_SIZE(rtw89_btc_8852a_rf_dl), .rf_para_dlink = rtw89_btc_8852a_rf_dl, + .rf_para_ulink_v9 = NULL, + .rf_para_dlink_v9 = NULL, + .rf_para_ulink_num_v9 = 0, + .rf_para_dlink_num_v9 = 0, .ps_mode_supported = BIT(RTW89_PS_MODE_RFOFF) | BIT(RTW89_PS_MODE_CLK_GATED) | BIT(RTW89_PS_MODE_PWR_GATED), diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index dcb15f26c1b1..5e8738bb2dc2 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -1014,6 +1014,10 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .rf_para_ulink = rtw89_btc_8852b_rf_ul, .rf_para_dlink_num = ARRAY_SIZE(rtw89_btc_8852b_rf_dl), .rf_para_dlink = rtw89_btc_8852b_rf_dl, + .rf_para_ulink_v9 = NULL, + .rf_para_dlink_v9 = NULL, + .rf_para_ulink_num_v9 = 0, + .rf_para_dlink_num_v9 = 0, .ps_mode_supported = BIT(RTW89_PS_MODE_RFOFF) | BIT(RTW89_PS_MODE_CLK_GATED) | BIT(RTW89_PS_MODE_PWR_GATED), diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index 2afc6908cc7e..ab4263bc8b9f 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -853,6 +853,10 @@ const struct rtw89_chip_info rtw8852bt_chip_info = { .rf_para_ulink = rtw89_btc_8852bt_rf_ul, .rf_para_dlink_num = ARRAY_SIZE(rtw89_btc_8852bt_rf_dl), .rf_para_dlink = rtw89_btc_8852bt_rf_dl, + .rf_para_ulink_v9 = NULL, + .rf_para_dlink_v9 = NULL, + .rf_para_ulink_num_v9 = 0, + .rf_para_dlink_num_v9 = 0, .ps_mode_supported = BIT(RTW89_PS_MODE_RFOFF) | BIT(RTW89_PS_MODE_CLK_GATED) | BIT(RTW89_PS_MODE_PWR_GATED), diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 5f9da0f85bb5..40db7e3c0d97 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -3211,6 +3211,10 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .rf_para_ulink = rtw89_btc_8852c_rf_ul, .rf_para_dlink_num = ARRAY_SIZE(rtw89_btc_8852c_rf_dl), .rf_para_dlink = rtw89_btc_8852c_rf_dl, + .rf_para_ulink_v9 = NULL, + .rf_para_dlink_v9 = NULL, + .rf_para_ulink_num_v9 = 0, + .rf_para_dlink_num_v9 = 0, .ps_mode_supported = BIT(RTW89_PS_MODE_RFOFF) | BIT(RTW89_PS_MODE_CLK_GATED) | BIT(RTW89_PS_MODE_PWR_GATED), diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 5f83e71ed218..8f6cf64271e8 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -3015,6 +3015,10 @@ const struct rtw89_chip_info rtw8922a_chip_info = { .rf_para_ulink = rtw89_btc_8922a_rf_ul, .rf_para_dlink_num = ARRAY_SIZE(rtw89_btc_8922a_rf_dl), .rf_para_dlink = rtw89_btc_8922a_rf_dl, + .rf_para_ulink_v9 = NULL, + .rf_para_dlink_v9 = NULL, + .rf_para_ulink_num_v9 = 0, + .rf_para_dlink_num_v9 = 0, .ps_mode_supported = BIT(RTW89_PS_MODE_RFOFF) | BIT(RTW89_PS_MODE_CLK_GATED) | BIT(RTW89_PS_MODE_PWR_GATED), diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index 49b84d49ccac..a9a7ffb5fb58 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -2619,6 +2619,129 @@ static u32 rtw8922d_chan_to_rf18_val(struct rtw89_dev *rtwdev, return val; } +static void rtw8922d_btc_set_rfe(struct rtw89_dev *rtwdev) +{ +} + +static void rtw8922d_btc_init_cfg(struct rtw89_dev *rtwdev) +{ + /* offload to firmware */ +} + +static void +rtw8922d_btc_set_wl_txpwr_ctrl(struct rtw89_dev *rtwdev, u32 txpwr_val) +{ + u16 ctrl_all_time = u32_get_bits(txpwr_val, GENMASK(15, 0)); + u16 ctrl_gnt_bt = u32_get_bits(txpwr_val, GENMASK(31, 16)); + + switch (ctrl_all_time) { + case 0xffff: + rtw89_mac_txpwr_write32_mask(rtwdev, RTW89_PHY_0, R_BE_PWR_RATE_CTRL, + B_BE_FORCE_PWR_BY_RATE_EN, 0x0); + rtw89_mac_txpwr_write32_mask(rtwdev, RTW89_PHY_0, R_BE_PWR_RATE_CTRL, + B_BE_FORCE_PWR_BY_RATE_VAL, 0x0); + break; + default: + rtw89_mac_txpwr_write32_mask(rtwdev, RTW89_PHY_0, R_BE_PWR_RATE_CTRL, + B_BE_FORCE_PWR_BY_RATE_VAL, ctrl_all_time); + rtw89_mac_txpwr_write32_mask(rtwdev, RTW89_PHY_0, R_BE_PWR_RATE_CTRL, + B_BE_FORCE_PWR_BY_RATE_EN, 0x1); + break; + } + + switch (ctrl_gnt_bt) { + case 0xffff: + rtw89_mac_txpwr_write32_mask(rtwdev, RTW89_PHY_0, R_BE_PWR_REG_CTRL, + B_BE_PWR_BT_EN, 0x0); + rtw89_mac_txpwr_write32_mask(rtwdev, RTW89_PHY_0, R_BE_PWR_COEX_CTRL, + B_BE_PWR_BT_VAL, 0x0); + break; + default: + rtw89_mac_txpwr_write32_mask(rtwdev, RTW89_PHY_0, R_BE_PWR_COEX_CTRL, + B_BE_PWR_BT_VAL, ctrl_gnt_bt); + rtw89_mac_txpwr_write32_mask(rtwdev, RTW89_PHY_0, R_BE_PWR_REG_CTRL, + B_BE_PWR_BT_EN, 0x1); + break; + } +} + +static +s8 rtw8922d_btc_get_bt_rssi(struct rtw89_dev *rtwdev, s8 val) +{ + return clamp_t(s8, val, -100, 0) + 100; +} + +static const struct rtw89_btc_rf_trx_para_v9 rtw89_btc_8922d_rf_ul_v9[] = { + /* + * 0 -> original + * 1 -> for BT-connected ACI issue && BTG co-rx + * 2 ~ 4 ->reserved for shared-antenna + * 5 ~ 8 ->for non-shared-antenna free-run + */ + {{15, 15}, {0, 0}, {0, 0}, {7, 7}, {0, 0}, {0, 0}}, + {{15, 15}, {2, 2}, {0, 0}, {7, 7}, {0, 0}, {0, 0}}, + {{15, 15}, {0, 0}, {0, 0}, {7, 7}, {0, 0}, {0, 0}}, + {{15, 15}, {0, 0}, {0, 0}, {7, 7}, {0, 0}, {0, 0}}, + {{15, 15}, {0, 0}, {0, 0}, {7, 7}, {0, 0}, {0, 0}}, + {{15, 15}, {1, 1}, {0, 0}, {7, 7}, {0, 0}, {0, 0}}, + {{ 6, 6}, {1, 1}, {0, 0}, {7, 7}, {0, 0}, {0, 0}}, + {{13, 13}, {1, 1}, {0, 0}, {7, 7}, {0, 0}, {0, 0}}, + {{13, 13}, {1, 1}, {0, 0}, {7, 7}, {0, 0}, {0, 0}}, +}; + +static const struct rtw89_btc_rf_trx_para_v9 rtw89_btc_8922d_rf_dl_v9[] = { + /* + * 0 -> original + * 1 -> for BT-connected ACI issue && BTG co-rx + * 2 ~ 4 ->reserved for shared-antenna + * 5 ~ 8 ->for non-shared-antenna free-run + */ + {{15, 15}, {0, 0}, {0, 0}, {7, 7}, {0, 0}, {0, 0}}, + {{15, 15}, {2, 2}, {0, 0}, {7, 7}, {0, 0}, {0, 0}}, + {{15, 15}, {0, 0}, {0, 0}, {7, 7}, {0, 0}, {0, 0}}, + {{15, 15}, {0, 0}, {0, 0}, {7, 7}, {0, 0}, {0, 0}}, + {{15, 15}, {0, 0}, {0, 0}, {7, 7}, {0, 0}, {0, 0}}, + {{15, 15}, {1, 1}, {0, 0}, {7, 7}, {0, 0}, {0, 0}}, + {{15, 15}, {1, 1}, {0, 0}, {7, 7}, {0, 0}, {0, 0}}, + {{15, 15}, {1, 1}, {0, 0}, {7, 7}, {0, 0}, {0, 0}}, + {{15, 15}, {1, 1}, {0, 0}, {7, 7}, {0, 0}, {0, 0}}, +}; + +static const u8 rtw89_btc_8922d_wl_rssi_thres[BTC_WL_RSSI_THMAX] = {60, 50, 40, 30}; +static const u8 rtw89_btc_8922d_bt_rssi_thres[BTC_BT_RSSI_THMAX] = {50, 40, 30, 20}; + +static const struct rtw89_btc_fbtc_mreg rtw89_btc_8922d_mon_reg[] = { + RTW89_DEF_FBTC_MREG(REG_MAC, 4, 0xe300), + RTW89_DEF_FBTC_MREG(REG_MAC, 4, 0xe330), + RTW89_DEF_FBTC_MREG(REG_MAC, 4, 0xe334), + RTW89_DEF_FBTC_MREG(REG_MAC, 4, 0xe338), + RTW89_DEF_FBTC_MREG(REG_MAC, 4, 0xe344), + RTW89_DEF_FBTC_MREG(REG_MAC, 4, 0xe348), + RTW89_DEF_FBTC_MREG(REG_MAC, 4, 0xe34c), + RTW89_DEF_FBTC_MREG(REG_MAC, 4, 0xe350), + RTW89_DEF_FBTC_MREG(REG_MAC, 4, 0xe354), + RTW89_DEF_FBTC_MREG(REG_MAC, 4, 0xe35c), + RTW89_DEF_FBTC_MREG(REG_MAC, 4, 0xe370), + RTW89_DEF_FBTC_MREG(REG_MAC, 4, 0xe380), +}; + +static +void rtw8922d_btc_update_bt_cnt(struct rtw89_dev *rtwdev) +{ + /* Feature move to firmware */ +} + +static +void rtw8922d_btc_wl_s1_standby(struct rtw89_dev *rtwdev, bool state) +{ + /* Feature move to firmware */ +} + +static void rtw8922d_btc_set_wl_rx_gain(struct rtw89_dev *rtwdev, u32 level) +{ + /* Feature move to firmware */ +} + MODULE_FIRMWARE(RTW8922D_MODULE_FIRMWARE); MODULE_FIRMWARE(RTW8922DS_MODULE_FIRMWARE); MODULE_AUTHOR("Realtek Corporation"); From 0737a803bae462e69ef030dc9d050a29c1d6d265 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 30 Mar 2026 14:58:46 +0800 Subject: [PATCH 071/124] wifi: rtw89: 8922d: add chip_info and chip_ops struct Add remaining functions including calculate RX gain for power saving, channel frequency and RSSI from PPDU status, and WoWLAN declaration. Then fill chip_info and chip_ops tables, which RTL8922D has two variants RTL8922D and RTL8922DS supporting 4096 and 1024 QAM respectively. Other features, such as support of 2/5/6 GHz and up to 160 MHz bandwidth, for variants are the same, Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260330065847.48946-9-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.h | 23 +- drivers/net/wireless/realtek/rtw89/reg.h | 17 + drivers/net/wireless/realtek/rtw89/rtw8922d.c | 344 ++++++++++++++++++ drivers/net/wireless/realtek/rtw89/rtw8922d.h | 3 + 4 files changed, 380 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 4574a281d352..db252d45e498 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -4298,13 +4298,22 @@ enum rtw89_fw_element_id { BIT(RTW89_FW_ELEMENT_ID_TXPWR_TRK) | \ BITS_OF_RTW89_TXPWR_FW_ELEMENTS_NO_6GHZ) -#define RTW89_BE_GEN_DEF_NEEDED_FW_ELEMENTS (BIT(RTW89_FW_ELEMENT_ID_BBMCU0) | \ - BIT(RTW89_FW_ELEMENT_ID_BB_REG) | \ - BIT(RTW89_FW_ELEMENT_ID_RADIO_A) | \ - BIT(RTW89_FW_ELEMENT_ID_RADIO_B) | \ - BIT(RTW89_FW_ELEMENT_ID_RF_NCTL) | \ - BIT(RTW89_FW_ELEMENT_ID_TXPWR_TRK) | \ - BITS_OF_RTW89_TXPWR_FW_ELEMENTS) +#define RTW89_BE_GEN_DEF_NEEDED_FW_ELEMENTS_BASE \ + (BIT(RTW89_FW_ELEMENT_ID_BB_REG) | \ + BIT(RTW89_FW_ELEMENT_ID_RADIO_A) | \ + BIT(RTW89_FW_ELEMENT_ID_RADIO_B) | \ + BIT(RTW89_FW_ELEMENT_ID_RF_NCTL) | \ + BIT(RTW89_FW_ELEMENT_ID_TXPWR_TRK) | \ + BITS_OF_RTW89_TXPWR_FW_ELEMENTS) + +#define RTW89_BE_GEN_DEF_NEEDED_FW_ELEMENTS \ + (RTW89_BE_GEN_DEF_NEEDED_FW_ELEMENTS_BASE | \ + BIT(RTW89_FW_ELEMENT_ID_BBMCU0)) + +#define RTW89_BE_GEN_DEF_NEEDED_FW_ELEMENTS_V1 \ + (RTW89_BE_GEN_DEF_NEEDED_FW_ELEMENTS_BASE | \ + BIT(RTW89_FW_ELEMENT_ID_AFE_PWR_SEQ) | \ + BIT(RTW89_FW_ELEMENT_ID_TX_COMP)) struct __rtw89_fw_txpwr_element { u8 rsvd0; diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index e5e689f1bfa3..42ffe83931a3 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -4291,6 +4291,20 @@ #define B_BE_VERIFY_ENV_MASK GENMASK(9, 8) #define B_BE_HW_ID_MASK GENMASK(7, 0) +#define R_BE_SCOREBOARD_0 0x0110 +#define B_BE_SB0_TOGGLE BIT(31) +#define B_BE_SB0_WL_DATA_LINE_MASK GENMASK(30, 0) + +#define R_BE_SCOREBOARD_0_BT_DATA 0x0114 +#define B_BE_SB0_BT_DATA_LINE_MASK GENMASK(30, 0) + +#define R_BE_SCOREBOARD_1 0x0118 +#define B_BE_SB1_TOGGLE BIT(31) +#define B_BE_SB1_WL_DATA_LINE_MASK GENMASK(30, 0) + +#define R_BE_SCOREBOARD_1_BT_DATA 0x011C +#define B_BE_SB1_BT_DATA_LINE_MASK GENMASK(30, 0) + #define R_BE_HALT_H2C_CTRL 0x0160 #define B_BE_HALT_H2C_TRIGGER BIT(0) @@ -10656,6 +10670,9 @@ #define B_RX_LDPC08_BE4 GENMASK(29, 24) #define R_RX_LDPC01_BE4 0x26840 #define B_RX_LDPC09_BE4 GENMASK(5, 0) +#define R_BSS_CLR_MAP_BE4 0x26914 +#define R_BSS_CLR_VLD_BE4 0x26920 +#define B_BSS_CLR_VLD_BE4 BIT(2) #define R_SW_SI_DATA_BE4 0x2CF4C #define B_SW_SI_READ_DATA_BE4 GENMASK(19, 0) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.c b/drivers/net/wireless/realtek/rtw89/rtw8922d.c index a9a7ffb5fb58..e3b77cd23514 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.c @@ -1447,6 +1447,20 @@ static void rtw8922d_set_rx_gain_normal(struct rtw89_dev *rtwdev, rtw8922d_set_rx_gain_normal_ofdm(rtwdev, chan, path, phy_idx); } +static void rtw8922d_calc_rx_gain_normal(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_rf_path path, + enum rtw89_phy_idx phy_idx, + struct rtw89_phy_calc_efuse_gain *calc) +{ + rtw8922d_calc_rx_gain_normal_ofdm(rtwdev, chan, path, phy_idx, calc); + + if (chan->band_type != RTW89_BAND_2G) + return; + + rtw8922d_calc_rx_gain_normal_cck(rtwdev, chan, path, phy_idx, calc); +} + static void rtw8922d_set_cck_parameters(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) @@ -2742,6 +2756,336 @@ static void rtw8922d_btc_set_wl_rx_gain(struct rtw89_dev *rtwdev, u32 level) /* Feature move to firmware */ } +static void rtw8922d_fill_freq_with_ppdu(struct rtw89_dev *rtwdev, + struct rtw89_rx_phy_ppdu *phy_ppdu, + struct ieee80211_rx_status *status) +{ + u8 chan_idx = phy_ppdu->chan_idx; + enum nl80211_band band; + u8 ch; + + if (chan_idx == 0) + return; + + rtw89_decode_chan_idx(rtwdev, chan_idx, &ch, &band); + status->freq = ieee80211_channel_to_frequency(ch, band); + status->band = band; +} + +static void rtw8922d_query_ppdu(struct rtw89_dev *rtwdev, + struct rtw89_rx_phy_ppdu *phy_ppdu, + struct ieee80211_rx_status *status) +{ + u8 path; + u8 *rx_power = phy_ppdu->rssi; + + if (!status->signal) + status->signal = RTW89_RSSI_RAW_TO_DBM(max(rx_power[RF_PATH_A], + rx_power[RF_PATH_B])); + + for (path = 0; path < rtwdev->chip->rf_path_num; path++) { + status->chains |= BIT(path); + status->chain_signal[path] = RTW89_RSSI_RAW_TO_DBM(rx_power[path]); + } + if (phy_ppdu->valid) + rtw8922d_fill_freq_with_ppdu(rtwdev, phy_ppdu, status); +} + +static void rtw8922d_convert_rpl_to_rssi(struct rtw89_dev *rtwdev, + struct rtw89_rx_phy_ppdu *phy_ppdu) +{ + /* Mapping to BW: 5, 10, 20, 40, 80, 160, 80_80 */ + static const u8 bw_compensate[] = {0, 0, 0, 6, 12, 18, 0}; + u8 *rssi = phy_ppdu->rssi; + u8 compensate = 0; + u8 i; + + if (phy_ppdu->bw_idx < ARRAY_SIZE(bw_compensate)) + compensate = bw_compensate[phy_ppdu->bw_idx]; + + for (i = 0; i < RF_PATH_NUM_8922D; i++) { + if (!(phy_ppdu->rx_path_en & BIT(i))) { + rssi[i] = 0; + phy_ppdu->rpl_path[i] = 0; + phy_ppdu->rpl_fd[i] = 0; + } + + if (phy_ppdu->ie != RTW89_CCK_PKT && rssi[i]) + rssi[i] += compensate; + + phy_ppdu->rpl_path[i] = rssi[i]; + } +} + +static void rtw8922d_phy_rpt_to_rssi(struct rtw89_dev *rtwdev, + struct rtw89_rx_desc_info *desc_info, + struct ieee80211_rx_status *rx_status) +{ + if (desc_info->rssi <= 0x1 || (desc_info->rssi >> 2) > MAX_RSSI) + return; + + rx_status->signal = (desc_info->rssi >> 2) - MAX_RSSI; +} + +static int rtw8922d_mac_enable_bb_rf(struct rtw89_dev *rtwdev) +{ + return 0; +} + +static int rtw8922d_mac_disable_bb_rf(struct rtw89_dev *rtwdev) +{ + return 0; +} + +static const struct rtw89_chanctx_listener rtw8922d_chanctx_listener = { + .callbacks[RTW89_CHANCTX_CALLBACK_TAS] = rtw89_tas_chanctx_cb, +}; + +#ifdef CONFIG_PM +static const struct wiphy_wowlan_support rtw_wowlan_stub_8922d = { + .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT | + WIPHY_WOWLAN_NET_DETECT, + .n_patterns = RTW89_MAX_PATTERN_NUM, + .pattern_max_len = RTW89_MAX_PATTERN_SIZE, + .pattern_min_len = 1, + .max_nd_match_sets = RTW89_SCANOFLD_MAX_SSID, +}; +#endif + +static const struct rtw89_chip_ops rtw8922d_chip_ops = { + .enable_bb_rf = rtw8922d_mac_enable_bb_rf, + .disable_bb_rf = rtw8922d_mac_disable_bb_rf, + .bb_preinit = rtw8922d_bb_preinit, + .bb_postinit = rtw8922d_bb_postinit, + .bb_reset = rtw8922d_bb_reset, + .bb_sethw = rtw8922d_bb_sethw, + .read_rf = rtw89_phy_read_rf_v3, + .write_rf = rtw89_phy_write_rf_v3, + .set_channel = rtw8922d_set_channel, + .set_channel_help = rtw8922d_set_channel_help, + .read_efuse = rtw8922d_read_efuse, + .read_phycap = rtw8922d_read_phycap, + .fem_setup = NULL, + .rfe_gpio = NULL, + .rfk_hw_init = rtw8922d_rfk_hw_init, + .rfk_init = rtw8922d_rfk_init, + .rfk_init_late = rtw8922d_rfk_init_late, + .rfk_channel = rtw8922d_rfk_channel, + .rfk_band_changed = rtw8922d_rfk_band_changed, + .rfk_scan = rtw8922d_rfk_scan, + .rfk_track = rtw8922d_rfk_track, + .power_trim = rtw8922d_power_trim, + .set_txpwr = rtw8922d_set_txpwr, + .set_txpwr_ctrl = rtw8922d_set_txpwr_ctrl, + .init_txpwr_unit = NULL, + .get_thermal = rtw8922d_get_thermal, + .chan_to_rf18_val = rtw8922d_chan_to_rf18_val, + .ctrl_btg_bt_rx = rtw8922d_set_gbt_bt_rx_sel, + .query_ppdu = rtw8922d_query_ppdu, + .convert_rpl_to_rssi = rtw8922d_convert_rpl_to_rssi, + .phy_rpt_to_rssi = rtw8922d_phy_rpt_to_rssi, + .ctrl_nbtg_bt_tx = rtw8922d_ctrl_nbtg_bt_tx, + .cfg_txrx_path = rtw8922d_bb_cfg_txrx_path, + .set_txpwr_ul_tb_offset = NULL, + .digital_pwr_comp = rtw8922d_digital_pwr_comp, + .calc_rx_gain_normal = rtw8922d_calc_rx_gain_normal, + .pwr_on_func = rtw8922d_pwr_on_func, + .pwr_off_func = rtw8922d_pwr_off_func, + .query_rxdesc = rtw89_core_query_rxdesc_v3, + .fill_txdesc = rtw89_core_fill_txdesc_v3, + .fill_txdesc_fwcmd = rtw89_core_fill_txdesc_fwcmd_v2, + .get_ch_dma = {rtw89_core_get_ch_dma_v1, + NULL, + NULL,}, + .cfg_ctrl_path = rtw89_mac_cfg_ctrl_path_v2, + .mac_cfg_gnt = rtw89_mac_cfg_gnt_v3, + .stop_sch_tx = rtw89_mac_stop_sch_tx_v2, + .resume_sch_tx = rtw89_mac_resume_sch_tx_v2, + .h2c_dctl_sec_cam = rtw89_fw_h2c_dctl_sec_cam_v3, + .h2c_default_cmac_tbl = rtw89_fw_h2c_default_cmac_tbl_be, + .h2c_assoc_cmac_tbl = rtw89_fw_h2c_assoc_cmac_tbl_be, + .h2c_ampdu_cmac_tbl = rtw89_fw_h2c_ampdu_cmac_tbl_be, + .h2c_txtime_cmac_tbl = rtw89_fw_h2c_txtime_cmac_tbl_be, + .h2c_punctured_cmac_tbl = rtw89_fw_h2c_punctured_cmac_tbl_be, + .h2c_default_dmac_tbl = rtw89_fw_h2c_default_dmac_tbl_v3, + .h2c_update_beacon = rtw89_fw_h2c_update_beacon_be, + .h2c_ba_cam = rtw89_fw_h2c_ba_cam_v1, + .h2c_wow_cam_update = rtw89_fw_h2c_wow_cam_update_v1, + + .btc_set_rfe = rtw8922d_btc_set_rfe, + .btc_init_cfg = rtw8922d_btc_init_cfg, + .btc_set_wl_pri = NULL, + .btc_set_wl_txpwr_ctrl = rtw8922d_btc_set_wl_txpwr_ctrl, + .btc_get_bt_rssi = rtw8922d_btc_get_bt_rssi, + .btc_update_bt_cnt = rtw8922d_btc_update_bt_cnt, + .btc_wl_s1_standby = rtw8922d_btc_wl_s1_standby, + .btc_set_wl_rx_gain = rtw8922d_btc_set_wl_rx_gain, + .btc_set_policy = rtw89_btc_set_policy_v1, +}; + +const struct rtw89_chip_info rtw8922d_chip_info = { + .chip_id = RTL8922D, + .chip_gen = RTW89_CHIP_BE, + .ops = &rtw8922d_chip_ops, + .mac_def = &rtw89_mac_gen_be, + .phy_def = &rtw89_phy_gen_be_v1, + .fw_def = { + .fw_basename = RTW8922D_FW_BASENAME, + .fw_format_max = RTW8922D_FW_FORMAT_MAX, + .fw_b_aid = RTL8922D_AID7102, + }, + .try_ce_fw = false, + .bbmcu_nr = 0, + .needed_fw_elms = RTW89_BE_GEN_DEF_NEEDED_FW_ELEMENTS_V1, + .fw_blacklist = &rtw89_fw_blacklist_default, + .fifo_size = 393216, + .small_fifo_size = false, + .dle_scc_rsvd_size = 0, + .max_amsdu_limit = 11000, + .max_vht_mpdu_cap = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991, + .max_eht_mpdu_cap = IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_7991, + .max_tx_agg_num = 128, + .max_rx_agg_num = 256, + .dis_2g_40m_ul_ofdma = false, + .rsvd_ple_ofst = 0x5f800, + .hfc_param_ini = {rtw8922d_hfc_param_ini_pcie, NULL, NULL}, + .dle_mem = {rtw8922d_dle_mem_pcie, NULL, NULL, NULL}, + .wde_qempty_acq_grpnum = 8, + .wde_qempty_mgq_grpsel = 8, + .rf_base_addr = {0x3e000, 0x3f000}, + .thermal_th = {0xac, 0xad}, + .pwr_on_seq = NULL, + .pwr_off_seq = NULL, + .bb_table = NULL, + .bb_gain_table = NULL, + .rf_table = {}, + .nctl_table = NULL, + .nctl_post_table = &rtw8922d_nctl_post_defs_tbl, + .dflt_parms = NULL, /* load parm from fw */ + .rfe_parms_conf = NULL, /* load parm from fw */ + .chanctx_listener = &rtw8922d_chanctx_listener, + .txpwr_factor_bb = 3, + .txpwr_factor_rf = 2, + .txpwr_factor_mac = 1, + .dig_table = NULL, + .dig_regs = &rtw8922d_dig_regs, + .tssi_dbw_table = NULL, + .support_macid_num = 64, + .support_link_num = 2, + .support_chanctx_num = 2, + .support_rnr = true, + .support_bands = BIT(NL80211_BAND_2GHZ) | + BIT(NL80211_BAND_5GHZ) | + BIT(NL80211_BAND_6GHZ), + .support_bandwidths = BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80) | + BIT(NL80211_CHAN_WIDTH_160), + .support_unii4 = true, + .support_ant_gain = false, + .support_tas = false, + .support_sar_by_ant = true, + .support_noise = false, + .ul_tb_waveform_ctrl = false, + .ul_tb_pwr_diff = false, + .rx_freq_frome_ie = false, + .hw_sec_hdr = true, + .hw_mgmt_tx_encrypt = true, + .hw_tkip_crypto = true, + .hw_mlo_bmc_crypto = true, + .rf_path_num = 2, + .tx_nss = 2, + .rx_nss = 2, + .acam_num = 128, + .bcam_num = 16, + .scam_num = 32, + .bacam_num = 24, + .bacam_dynamic_num = 8, + .bacam_ver = RTW89_BACAM_V1, + .addrcam_ver = 1, + .ppdu_max_usr = 16, + .sec_ctrl_efuse_size = 4, + .physical_efuse_size = 0x1300, + .logical_efuse_size = 0x70000, + .limit_efuse_size = 0x40000, + .dav_phy_efuse_size = 0, + .dav_log_efuse_size = 0, + .efuse_blocks = rtw8922d_efuse_blocks, + .phycap_addr = 0x1700, + .phycap_size = 0x60, + .para_ver = 0x3ff, + .wlcx_desired = 0x09150000, + .scbd = 0x1, + .mailbox = 0x1, + + .afh_guard_ch = 6, + .wl_rssi_thres = rtw89_btc_8922d_wl_rssi_thres, + .bt_rssi_thres = rtw89_btc_8922d_bt_rssi_thres, + .rssi_tol = 2, + .mon_reg_num = ARRAY_SIZE(rtw89_btc_8922d_mon_reg), + .mon_reg = rtw89_btc_8922d_mon_reg, + .rf_para_ulink_v9 = rtw89_btc_8922d_rf_ul_v9, + .rf_para_dlink_v9 = rtw89_btc_8922d_rf_dl_v9, + .rf_para_ulink_num_v9 = ARRAY_SIZE(rtw89_btc_8922d_rf_ul_v9), + .rf_para_dlink_num_v9 = ARRAY_SIZE(rtw89_btc_8922d_rf_dl_v9), + .ps_mode_supported = BIT(RTW89_PS_MODE_RFOFF) | + BIT(RTW89_PS_MODE_CLK_GATED) | + BIT(RTW89_PS_MODE_PWR_GATED), + .low_power_hci_modes = 0, + .h2c_cctl_func_id = H2C_FUNC_MAC_CCTLINFO_UD_G7, + .hci_func_en_addr = R_BE_HCI_FUNC_EN, + .h2c_desc_size = sizeof(struct rtw89_rxdesc_short_v3), + .txwd_body_size = sizeof(struct rtw89_txwd_body_v2), + .txwd_info_size = sizeof(struct rtw89_txwd_info_v2), + .h2c_ctrl_reg = R_BE_H2CREG_CTRL, + .h2c_counter_reg = {R_BE_UDM1 + 1, B_BE_UDM1_HALMAC_H2C_DEQ_CNT_MASK >> 8}, + .h2c_regs = rtw8922d_h2c_regs, + .c2h_ctrl_reg = R_BE_C2HREG_CTRL, + .c2h_counter_reg = {R_BE_UDM1 + 1, B_BE_UDM1_HALMAC_C2H_ENQ_CNT_MASK >> 8}, + .c2h_regs = rtw8922d_c2h_regs, + .page_regs = &rtw8922d_page_regs, + .wow_reason_reg = rtw8922d_wow_wakeup_regs, + .cfo_src_fd = true, + .cfo_hw_comp = true, + .dcfo_comp = NULL, + .dcfo_comp_sft = 0, + .nhm_report = NULL, + .nhm_th = NULL, + .imr_info = NULL, + .imr_dmac_table = &rtw8922d_imr_dmac_table, + .imr_cmac_table = &rtw8922d_imr_cmac_table, + .rrsr_cfgs = &rtw8922d_rrsr_cfgs, + .bss_clr_vld = {R_BSS_CLR_VLD_BE4, B_BSS_CLR_VLD_BE4}, + .bss_clr_map_reg = R_BSS_CLR_MAP_BE4, + .rfkill_init = &rtw8922d_rfkill_regs, + .rfkill_get = {R_BE_GPIO_EXT_CTRL, B_BE_GPIO_IN_9}, + .btc_sb = {{{R_BE_SCOREBOARD_0, R_BE_SCOREBOARD_0_BT_DATA}, + {R_BE_SCOREBOARD_1, R_BE_SCOREBOARD_1_BT_DATA}}}, + .dma_ch_mask = BIT(RTW89_DMA_ACH1) | BIT(RTW89_DMA_ACH3) | + BIT(RTW89_DMA_ACH5) | BIT(RTW89_DMA_ACH7) | + BIT(RTW89_DMA_B0HI) | BIT(RTW89_DMA_B1HI), + .edcca_regs = &rtw8922d_edcca_regs, +#ifdef CONFIG_PM + .wowlan_stub = &rtw_wowlan_stub_8922d, +#endif + .xtal_info = NULL, + .default_quirks = BIT(RTW89_QUIRK_THERMAL_PROT_120C), +}; +EXPORT_SYMBOL(rtw8922d_chip_info); + +static const struct rtw89_fw_def rtw8922de_vs_fw_def = { + .fw_basename = RTW8922DS_FW_BASENAME, + .fw_format_max = RTW8922DS_FW_FORMAT_MAX, + .fw_b_aid = RTL8922D_AID7060, +}; + +const struct rtw89_chip_variant rtw8922de_vs_variant = { + .no_mcs_12_13 = true, + .fw_min_ver_code = RTW89_FW_VER_CODE(0, 0, 0, 0), + .fw_def_override = &rtw8922de_vs_fw_def, +}; +EXPORT_SYMBOL(rtw8922de_vs_variant); + MODULE_FIRMWARE(RTW8922D_MODULE_FIRMWARE); MODULE_FIRMWARE(RTW8922DS_MODULE_FIRMWARE); MODULE_AUTHOR("Realtek Corporation"); diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922d.h b/drivers/net/wireless/realtek/rtw89/rtw8922d.h index a3b98ad6636c..22a7d1cc244f 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922d.h +++ b/drivers/net/wireless/realtek/rtw89/rtw8922d.h @@ -77,4 +77,7 @@ struct rtw8922d_efuse { struct rtw8922d_rx_gain_6g rx_gain_6g_b_2; } __packed; +extern const struct rtw89_chip_info rtw8922d_chip_info; +extern const struct rtw89_chip_variant rtw8922de_vs_variant; + #endif From a16ca7d91e4eb37a4faf0a2d2b44603489021725 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 30 Mar 2026 14:58:47 +0800 Subject: [PATCH 072/124] wifi: rtw89: 8922d: add PCI ID of RTL8922DE and RTL8922DE-VS Add PCI ID tables with RTL8922DE whose ID is 10EC:895D, and with RTL8922DE-VS whose ID are 10EC:892D and 10EC:882D. Also, add pci_info struct to describe the hardware capabilities and registers accordingly. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260330065847.48946-10-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/pci.h | 3 + .../net/wireless/realtek/rtw89/rtw8922de.c | 119 ++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8922de.c diff --git a/drivers/net/wireless/realtek/rtw89/pci.h b/drivers/net/wireless/realtek/rtw89/pci.h index c7cd34e99349..e7da37b9da7d 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.h +++ b/drivers/net/wireless/realtek/rtw89/pci.h @@ -1105,6 +1105,9 @@ B_BE_CH6_BUSY | B_BE_CH7_BUSY | B_BE_CH8_BUSY | \ B_BE_CH9_BUSY | B_BE_CH10_BUSY | B_BE_CH11_BUSY | \ B_BE_CH12_BUSY | B_BE_CH13_BUSY | B_BE_CH14_BUSY) +#define DMA_BUSY1_CHECK_BE_V1 (B_BE_CH0_BUSY | B_BE_CH2_BUSY | B_BE_CH4_BUSY | \ + B_BE_CH6_BUSY | B_BE_CH8_BUSY | B_BE_CH10_BUSY | \ + B_BE_CH12_BUSY) #define R_BE_HAXI_EXP_CTRL_V1 0xB020 #define B_BE_R_NO_SEC_ACCESS BIT(31) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922de.c b/drivers/net/wireless/realtek/rtw89/rtw8922de.c new file mode 100644 index 000000000000..f144e7fc76de --- /dev/null +++ b/drivers/net/wireless/realtek/rtw89/rtw8922de.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2026 Realtek Corporation + */ + +#include +#include + +#include "pci.h" +#include "reg.h" +#include "rtw8922d.h" + +static const struct rtw89_pci_info rtw8922d_pci_info = { + .gen_def = &rtw89_pci_gen_be, + .isr_def = &rtw89_pci_isr_be_v1, + .txbd_trunc_mode = MAC_AX_BD_TRUNC, + .rxbd_trunc_mode = MAC_AX_BD_TRUNC, + .rxbd_mode = MAC_AX_RXBD_PKT, + .tag_mode = MAC_AX_TAG_MULTI, + .tx_burst = MAC_AX_TX_BURST_V1_256B, + .rx_burst = MAC_AX_RX_BURST_V1_128B, + .wd_dma_idle_intvl = MAC_AX_WD_DMA_INTVL_256NS, + .wd_dma_act_intvl = MAC_AX_WD_DMA_INTVL_256NS, + .multi_tag_num = MAC_AX_TAG_NUM_8, + .lbc_en = MAC_AX_PCIE_ENABLE, + .lbc_tmr = MAC_AX_LBC_TMR_2MS, + .autok_en = MAC_AX_PCIE_DISABLE, + .io_rcy_en = MAC_AX_PCIE_ENABLE, + .io_rcy_tmr = MAC_AX_IO_RCY_ANA_TMR_DEF, + .rx_ring_eq_is_full = true, + .check_rx_tag = true, + .no_rxbd_fs = true, + .group_bd_addr = true, + .rpp_fmt_size = sizeof(struct rtw89_pci_rpp_fmt_v1), + + .init_cfg_reg = R_BE_HAXI_INIT_CFG1, + .txhci_en_bit = B_BE_TXDMA_EN, + .rxhci_en_bit = B_BE_RXDMA_EN, + .rxbd_mode_bit = B_BE_RXQ_RXBD_MODE_MASK, + .exp_ctrl_reg = R_BE_HAXI_EXP_CTRL_V1, + .max_tag_num_mask = B_BE_MAX_TAG_NUM_MASK, + .rxbd_rwptr_clr_reg = R_BE_RXBD_RWPTR_CLR1_V1, + .txbd_rwptr_clr2_reg = R_BE_TXBD_RWPTR_CLR1, + .dma_io_stop = {R_BE_HAXI_INIT_CFG1, B_BE_STOP_AXI_MST}, + .dma_stop1 = {R_BE_HAXI_DMA_STOP1, B_BE_TX_STOP1_MASK_V1}, + .dma_stop2 = {0}, + .dma_busy1 = {R_BE_HAXI_DMA_BUSY1, DMA_BUSY1_CHECK_BE_V1}, + .dma_busy2_reg = 0, + .dma_busy3_reg = R_BE_HAXI_DMA_BUSY1, + + .rpwm_addr = R_BE_PCIE_HRPWM, + .cpwm_addr = R_BE_PCIE_CRPWM, + .mit_addr = R_BE_PCIE_MIT_CH_EN, + .wp_sel_addr = R_BE_WP_ADDR_H_SEL0_3_V1, + .tx_dma_ch_mask = BIT(RTW89_TXCH_ACH1) | BIT(RTW89_TXCH_ACH3) | + BIT(RTW89_TXCH_ACH5) | BIT(RTW89_TXCH_ACH7) | + BIT(RTW89_TXCH_CH9) | BIT(RTW89_TXCH_CH11), + .bd_idx_addr_low_power = NULL, + .dma_addr_set = &rtw89_pci_ch_dma_addr_set_be_v1, + .bd_ram_table = NULL, + + .ltr_set = rtw89_pci_ltr_set_v2, + .fill_txaddr_info = rtw89_pci_fill_txaddr_info_v1, + .parse_rpp = rtw89_pci_parse_rpp_v1, + .config_intr_mask = rtw89_pci_config_intr_mask_v3, + .enable_intr = rtw89_pci_enable_intr_v3, + .disable_intr = rtw89_pci_disable_intr_v3, + .recognize_intrs = rtw89_pci_recognize_intrs_v3, + + .ssid_quirks = NULL, +}; + +static const struct rtw89_driver_info rtw89_8922de_vs_info = { + .chip = &rtw8922d_chip_info, + .variant = &rtw8922de_vs_variant, + .quirks = NULL, + .bus = { + .pci = &rtw8922d_pci_info, + }, +}; + +static const struct rtw89_driver_info rtw89_8922de_info = { + .chip = &rtw8922d_chip_info, + .variant = NULL, + .quirks = NULL, + .bus = { + .pci = &rtw8922d_pci_info, + }, +}; + +static const struct pci_device_id rtw89_8922de_id_table[] = { + { + PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x892D), + .driver_data = (kernel_ulong_t)&rtw89_8922de_vs_info, + }, + { + PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x882D), + .driver_data = (kernel_ulong_t)&rtw89_8922de_vs_info, + }, + { + PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x895D), + .driver_data = (kernel_ulong_t)&rtw89_8922de_info, + }, + {}, +}; +MODULE_DEVICE_TABLE(pci, rtw89_8922de_id_table); + +static struct pci_driver rtw89_8922de_driver = { + .name = "rtw89_8922de", + .id_table = rtw89_8922de_id_table, + .probe = rtw89_pci_probe, + .remove = rtw89_pci_remove, + .driver.pm = &rtw89_pm_ops_be, + .err_handler = &rtw89_pci_err_handler, +}; +module_pci_driver(rtw89_8922de_driver); + +MODULE_AUTHOR("Realtek Corporation"); +MODULE_DESCRIPTION("Realtek 802.11be wireless 8922DE/8922DE-VS driver"); +MODULE_LICENSE("Dual BSD/GPL"); From 4e4fc2149b0a983670fd99bbd549012839bda79e Mon Sep 17 00:00:00 2001 From: Zenm Chen Date: Mon, 30 Mar 2026 17:34:39 +0800 Subject: [PATCH 073/124] wifi: rtw89: Add support for Elecom WDC-XE2402TU3-B Add the ID 056e:4024 to the table to support an additional RTL8832CU adapter: Elecom WDC-XE2402TU3-B. Link: https://github.com/morrownr/rtw89/commit/55c059e2bd49acd5cf93edbc8eda7b9e042f4efd Signed-off-by: Zenm Chen Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260330093440.3615-1-zenmchen@gmail.com --- drivers/net/wireless/realtek/rtw89/rtw8852cu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c index de3f8358bbe7..092d2812a4d5 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c @@ -41,6 +41,8 @@ static const struct rtw89_driver_info rtw89_8852cu_info = { static const struct usb_device_id rtw_8852cu_id_table[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x0411, 0x03a6, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, + { USB_DEVICE_AND_INTERFACE_INFO(0x056e, 0x4024, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, { USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0xc832, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&rtw89_8852cu_info }, { USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0xc85a, 0xff, 0xff, 0xff), From 80119a77e5b03747b8886505df1b3cb26f49168d Mon Sep 17 00:00:00 2001 From: Lucid Duck Date: Wed, 1 Apr 2026 22:22:16 -0700 Subject: [PATCH 074/124] wifi: rtw89: usb: fix TX flow control by tracking in-flight URBs rtw89_usb_ops_check_and_reclaim_tx_resource() returns a hardcoded placeholder value (42) instead of actual TX resource availability. This violates mac80211's flow control contract, preventing backpressure and causing uncontrolled URB accumulation under sustained TX load. Fix by adding per-channel atomic counters (tx_inflight[]) that track in-flight URBs. Increment before usb_submit_urb() with rollback on failure, decrement in the completion callback, and return the remaining capacity to mac80211. The firmware command channel (CH12) always returns 1 since it has its own flow control. The pre-increment pattern prevents a race where USB core completes the URB on another CPU before the submitting code increments the counter. 128 URBs per channel provides headroom for RTL8832CU at 160 MHz bandwidth. Tested on RTL8852AU (USB3 80 MHz) where 64 and 128 showed equivalent throughput, and on RTL8832AU where 128 sustained full throughput under 8-stream parallel load. Tested on D-Link DWA-X1850 (RTL8832AU), kernel 6.19.8, Fedora 43: Unpatched -> Patched (128 URBs) USB3 5GHz UL: 844 -> 837 Mbps (no regression) USB3 5GHz retx: 3 -> 0 USB3 2.4GHz UL: 162 -> 164 Mbps (no regression) 4-stream UL: 858 -> 826 Mbps (within variance) 8-stream UL: 872 -> 826 Mbps (within variance) UDP flood: 0% loss (690K datagrams) 60-second soak: 855 Mbps, 0 retransmits Reported-by: morrownr Signed-off-by: Lucid Duck Acked-by: Ping-Ke Shih Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20260402052216.207858-1-lucid_duck@justthetip.ca --- drivers/net/wireless/realtek/rtw89/usb.c | 20 ++++++++++++++++++-- drivers/net/wireless/realtek/rtw89/usb.h | 3 +++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c index 581b8c05f930..767a95f759b1 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.c +++ b/drivers/net/wireless/realtek/rtw89/usb.c @@ -161,16 +161,24 @@ static u32 rtw89_usb_ops_check_and_reclaim_tx_resource(struct rtw89_dev *rtwdev, u8 txch) { + struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); + int inflight; + if (txch == RTW89_TXCH_CH12) return 1; - return 42; /* TODO some kind of calculation? */ + inflight = atomic_read(&rtwusb->tx_inflight[txch]); + if (inflight >= RTW89_USB_MAX_TX_URBS_PER_CH) + return 0; + + return RTW89_USB_MAX_TX_URBS_PER_CH - inflight; } static void rtw89_usb_write_port_complete(struct urb *urb) { struct rtw89_usb_tx_ctrl_block *txcb = urb->context; struct rtw89_dev *rtwdev = txcb->rtwdev; + struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); struct ieee80211_tx_info *info; struct rtw89_txwd_body *txdesc; struct sk_buff *skb; @@ -229,6 +237,8 @@ static void rtw89_usb_write_port_complete(struct urb *urb) break; } + atomic_dec(&rtwusb->tx_inflight[txcb->txch]); + kfree(txcb); } @@ -306,9 +316,13 @@ static void rtw89_usb_ops_tx_kick_off(struct rtw89_dev *rtwdev, u8 txch) skb_queue_tail(&txcb->tx_ack_queue, skb); + atomic_inc(&rtwusb->tx_inflight[txch]); + ret = rtw89_usb_write_port(rtwdev, txch, skb->data, skb->len, txcb); if (ret) { + atomic_dec(&rtwusb->tx_inflight[txch]); + if (ret != -ENODEV) rtw89_err(rtwdev, "write port txch %d failed: %d\n", txch, ret); @@ -684,8 +698,10 @@ static void rtw89_usb_init_tx(struct rtw89_dev *rtwdev) struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); int i; - for (i = 0; i < ARRAY_SIZE(rtwusb->tx_queue); i++) + for (i = 0; i < ARRAY_SIZE(rtwusb->tx_queue); i++) { skb_queue_head_init(&rtwusb->tx_queue[i]); + atomic_set(&rtwusb->tx_inflight[i], 0); + } } static void rtw89_usb_deinit_tx(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/usb.h b/drivers/net/wireless/realtek/rtw89/usb.h index 3d17e514e346..507f61f58ed9 100644 --- a/drivers/net/wireless/realtek/rtw89/usb.h +++ b/drivers/net/wireless/realtek/rtw89/usb.h @@ -31,6 +31,8 @@ #define R_AX_RXAGG_0 0x8900 #define B_AX_RXAGG_0_BUF_SZ_4K GENMASK(7, 0) +#define RTW89_USB_MAX_TX_URBS_PER_CH 128 + struct rtw89_usb_info { u32 usb_host_request_2; u32 usb_wlan0_1; @@ -75,6 +77,7 @@ struct rtw89_usb { struct usb_anchor tx_submitted; struct sk_buff_head tx_queue[RTW89_TXCH_NUM]; + atomic_t tx_inflight[RTW89_TXCH_NUM]; }; static inline struct rtw89_usb *rtw89_usb_priv(struct rtw89_dev *rtwdev) From 7f0de94ef44653764fa2fb8548b1253f0554f213 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 26 Mar 2026 12:14:31 +0200 Subject: [PATCH 075/124] wifi: mac80211: add a TXQ for management frames on NAN devices Currently there is no TXQ for non-data frames. Add a new txq_mgmt for this purpose and create one of these on NAN devices. On NAN devices, these frames may only be transmitted during the discovery window and it is therefore helpful to schedule them using a queue. Signed-off-by: Benjamin Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260326121156.32eddd986bd2.Iee95758287c276155fbd7779d3f263339308e083@changeid Signed-off-by: Johannes Berg --- include/net/mac80211.h | 2 ++ net/mac80211/iface.c | 28 ++++++++++++++++++++++++++-- net/mac80211/tx.c | 20 ++++++++++++++++---- net/mac80211/util.c | 34 +++++++++++++++++++++++----------- 4 files changed, 67 insertions(+), 17 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 72395895dc0e..b1c412eabf2b 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2074,6 +2074,7 @@ enum ieee80211_neg_ttlm_res { * @drv_priv: data area for driver use, will always be aligned to * sizeof(void \*). * @txq: the multicast data TX queue + * @txq_mgmt: the mgmt frame TX queue, currently only exists for NAN devices * @offload_flags: 802.3 -> 802.11 enapsulation offload flags, see * &enum ieee80211_offload_flags. */ @@ -2092,6 +2093,7 @@ struct ieee80211_vif { u8 hw_queue[IEEE80211_NUM_ACS]; struct ieee80211_txq *txq; + struct ieee80211_txq *txq_mgmt; netdev_features_t netdev_features; u32 driver_flags; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 125897717a4c..7518dcbcdf1c 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -682,6 +682,10 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do if (sdata->vif.txq) ieee80211_txq_purge(sdata->local, to_txq_info(sdata->vif.txq)); + if (sdata->vif.txq_mgmt) + ieee80211_txq_purge(sdata->local, + to_txq_info(sdata->vif.txq_mgmt)); + sdata->bss = NULL; if (local->open_count == 0) @@ -2223,10 +2227,16 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, lockdep_assert_wiphy(local->hw.wiphy); if (type == NL80211_IFTYPE_P2P_DEVICE || type == NL80211_IFTYPE_NAN) { + int size = ALIGN(sizeof(*sdata) + local->hw.vif_data_size, + sizeof(void *)); struct wireless_dev *wdev; + int txq_size = 0; - sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, - GFP_KERNEL); + if (type == NL80211_IFTYPE_NAN) + txq_size = sizeof(struct txq_info) + + local->hw.txq_data_size; + + sdata = kzalloc(size + txq_size, GFP_KERNEL); if (!sdata) return -ENOMEM; wdev = &sdata->wdev; @@ -2236,6 +2246,16 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, ieee80211_assign_perm_addr(local, wdev->address, type); memcpy(sdata->vif.addr, wdev->address, ETH_ALEN); ether_addr_copy(sdata->vif.bss_conf.addr, sdata->vif.addr); + + /* + * Add a management TXQ for NAN devices which includes frames + * that will only be transmitted during discovery windows (DWs) + */ + if (type == NL80211_IFTYPE_NAN) { + txqi = (struct txq_info *)((unsigned long)sdata + size); + ieee80211_txq_init(sdata, NULL, txqi, + IEEE80211_NUM_TIDS); + } } else { int size = ALIGN(sizeof(*sdata) + local->hw.vif_data_size, sizeof(void *)); @@ -2386,6 +2406,10 @@ void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata) if (sdata->vif.txq) ieee80211_txq_purge(sdata->local, to_txq_info(sdata->vif.txq)); + if (sdata->vif.txq_mgmt) + ieee80211_txq_purge(sdata->local, + to_txq_info(sdata->vif.txq_mgmt)); + synchronize_rcu(); cfg80211_unregister_wdev(&sdata->wdev); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index e0091a6196fc..f0f23e94db04 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1313,13 +1313,19 @@ static struct txq_info *ieee80211_get_txq(struct ieee80211_local *local, unlikely(!ieee80211_is_data_present(hdr->frame_control))) { if ((!ieee80211_is_mgmt(hdr->frame_control) || ieee80211_is_bufferable_mmpdu(skb) || - vif->type == NL80211_IFTYPE_STATION) && + vif->type == NL80211_IFTYPE_STATION || + vif->type == NL80211_IFTYPE_NAN || + vif->type == NL80211_IFTYPE_NAN_DATA) && sta && sta->uploaded) { /* * This will be NULL if the driver didn't set the * opt-in hardware flag. */ txq = sta->sta.txq[IEEE80211_NUM_TIDS]; + } else if ((!ieee80211_is_mgmt(hdr->frame_control) || + ieee80211_is_bufferable_mmpdu(skb)) && + !sta) { + txq = vif->txq_mgmt; } } else if (sta) { u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK; @@ -1512,9 +1518,15 @@ void ieee80211_txq_init(struct ieee80211_sub_if_data *sdata, txqi->txq.vif = &sdata->vif; if (!sta) { - sdata->vif.txq = &txqi->txq; - txqi->txq.tid = 0; - txqi->txq.ac = IEEE80211_AC_BE; + txqi->txq.tid = tid; + + if (tid == IEEE80211_NUM_TIDS) { + sdata->vif.txq_mgmt = &txqi->txq; + txqi->txq.ac = IEEE80211_AC_VO; + } else { + sdata->vif.txq = &txqi->txq; + txqi->txq.ac = IEEE80211_AC_BE; + } return; } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 8987a4504520..72e73f4f79c5 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -325,7 +325,7 @@ static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac) struct ieee80211_vif *vif = &sdata->vif; struct fq *fq = &local->fq; struct ps_data *ps = NULL; - struct txq_info *txqi; + struct txq_info *txqi = NULL; struct sta_info *sta; int i; @@ -344,37 +344,49 @@ static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac) for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { struct ieee80211_txq *txq = sta->sta.txq[i]; + struct txq_info *sta_txqi; if (!txq) continue; - txqi = to_txq_info(txq); + sta_txqi = to_txq_info(txq); if (ac != txq->ac) continue; if (!test_and_clear_bit(IEEE80211_TXQ_DIRTY, - &txqi->flags)) + &sta_txqi->flags)) continue; spin_unlock(&fq->lock); - drv_wake_tx_queue(local, txqi); + drv_wake_tx_queue(local, sta_txqi); spin_lock(&fq->lock); } } - if (!vif->txq) - goto out; + if (vif->txq) { + txqi = to_txq_info(vif->txq); - txqi = to_txq_info(vif->txq); + /* txq and txq_mgmt are mutually exclusive */ + WARN_ON_ONCE(vif->txq_mgmt); - if (!test_and_clear_bit(IEEE80211_TXQ_DIRTY, &txqi->flags) || - (ps && atomic_read(&ps->num_sta_ps)) || ac != vif->txq->ac) - goto out; + if (!test_and_clear_bit(IEEE80211_TXQ_DIRTY, &txqi->flags) || + (ps && atomic_read(&ps->num_sta_ps)) || + ac != vif->txq->ac) + txqi = NULL; + } else if (vif->txq_mgmt) { + txqi = to_txq_info(vif->txq_mgmt); + + if (!test_and_clear_bit(IEEE80211_TXQ_DIRTY, &txqi->flags) || + ac != vif->txq_mgmt->ac) + txqi = NULL; + } spin_unlock(&fq->lock); - drv_wake_tx_queue(local, txqi); + if (txqi) + drv_wake_tx_queue(local, txqi); + local_bh_enable(); return; out: From 8ea6b92faebe4bad0e271cb9a8d819b8955ed476 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 26 Mar 2026 12:14:32 +0200 Subject: [PATCH 076/124] wifi: ieee80211: add more NAN definitions These will be needed to implement NAN synchronization in mac80211_hwsim. Signed-off-by: Benjamin Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260326121156.ebb52db4c1eb.Ie8142cf92fc8c97c744a7c8b0a94ce3da6ff75ec@changeid Signed-off-by: Johannes Berg --- include/linux/ieee80211-nan.h | 37 +++++++++++++++++++++++++++++++++++ include/linux/ieee80211.h | 1 + 2 files changed, 38 insertions(+) diff --git a/include/linux/ieee80211-nan.h b/include/linux/ieee80211-nan.h index ebf28ea651f9..455033955e54 100644 --- a/include/linux/ieee80211-nan.h +++ b/include/linux/ieee80211-nan.h @@ -37,4 +37,41 @@ #define NAN_DEV_CAPA_NDPE_SUPPORTED 0x08 #define NAN_DEV_CAPA_S3_SUPPORTED 0x10 +/* NAN attributes, as defined in Wi-Fi Aware (TM) specification 4.0 Table 42 */ +#define NAN_ATTR_MASTER_INDICATION 0x00 +#define NAN_ATTR_CLUSTER_INFO 0x01 + +struct ieee80211_nan_attr { + u8 attr; + __le16 length; + u8 data[]; +} __packed; + +struct ieee80211_nan_master_indication { + u8 master_pref; + u8 random_factor; +} __packed; + +struct ieee80211_nan_anchor_master_info { + union { + __le64 master_rank; + struct { + u8 master_addr[ETH_ALEN]; + u8 random_factor; + u8 master_pref; + } __packed; + } __packed; + u8 hop_count; + __le32 ambtt; +} __packed; + +#define for_each_nan_attr(_attr, _data, _datalen) \ + for (_attr = (const struct ieee80211_nan_attr *)(_data); \ + (const u8 *)(_data) + (_datalen) - (const u8 *)_attr >= \ + (int)sizeof(*_attr) && \ + (const u8 *)(_data) + (_datalen) - (const u8 *)_attr >= \ + (int)sizeof(*_attr) + le16_to_cpu(_attr->length); \ + _attr = (const struct ieee80211_nan_attr *) \ + (_attr->data + le16_to_cpu(_attr->length))) + #endif /* LINUX_IEEE80211_NAN_H */ diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index b5d649db123f..ffa8f9f77efe 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -2240,6 +2240,7 @@ struct ieee80211_multiple_bssid_configuration { #define WLAN_OUI_WFA 0x506f9a #define WLAN_OUI_TYPE_WFA_P2P 9 +#define WLAN_OUI_TYPE_WFA_NAN 0x13 #define WLAN_OUI_TYPE_WFA_DPP 0x1A #define WLAN_OUI_MICROSOFT 0x0050f2 #define WLAN_OUI_TYPE_MICROSOFT_WPA 1 From b16df0dacb3a77d4c1ce95f21c58a2b99ae10213 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Thu, 26 Mar 2026 12:14:33 +0200 Subject: [PATCH 077/124] wifi: mac80211: export ieee80211_calculate_rx_timestamp The function is quite useful when handling beacon timestamps. Export it so that it can be used by mac80211_hwsim and others. Signed-off-by: Benjamin Berg Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260326121156.a1abc9c52f37.Ieabfe66768b1bf64c3076d62e73c50794faeacdc@changeid Signed-off-by: Johannes Berg --- include/net/mac80211.h | 18 ++++++++++++++++++ net/mac80211/ibss.c | 2 +- net/mac80211/ieee80211_i.h | 4 ---- net/mac80211/mesh_sync.c | 2 +- net/mac80211/rx.c | 2 +- net/mac80211/scan.c | 2 +- net/mac80211/util.c | 18 +++--------------- 7 files changed, 25 insertions(+), 23 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index b1c412eabf2b..8b513f777187 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -7393,6 +7393,24 @@ void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif); */ int ieee80211_ave_rssi(struct ieee80211_vif *vif, int link_id); +/** + * ieee80211_calculate_rx_timestamp - calculate timestamp in frame + * @hw: pointer as obtained from ieee80211_alloc_hw() + * @status: RX status + * @mpdu_len: total MPDU length (including FCS) + * @mpdu_offset: offset into MPDU to calculate timestamp at + * + * This function calculates the RX timestamp at the given MPDU offset, taking + * into account what the RX timestamp was. An offset of 0 will just normalize + * the timestamp to TSF at beginning of MPDU reception. + * + * Returns: the calculated timestamp + */ +u64 ieee80211_calculate_rx_timestamp(struct ieee80211_hw *hw, + struct ieee80211_rx_status *status, + unsigned int mpdu_len, + unsigned int mpdu_offset); + /** * ieee80211_report_wowlan_wakeup - report WoWLAN wakeup * @vif: virtual interface diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 1e1ab25d9d8d..97292ff51475 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -1127,7 +1127,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, if (ieee80211_have_rx_timestamp(rx_status)) { /* time when timestamp field was received */ rx_timestamp = - ieee80211_calculate_rx_timestamp(local, rx_status, + ieee80211_calculate_rx_timestamp(&local->hw, rx_status, len + FCS_LEN, 24); } else { /* diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index bacb49ad2817..53d783769642 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1922,10 +1922,6 @@ ieee80211_vif_get_num_mcast_if(struct ieee80211_sub_if_data *sdata) return -1; } -u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, - struct ieee80211_rx_status *status, - unsigned int mpdu_len, - unsigned int mpdu_offset); int ieee80211_hw_config(struct ieee80211_local *local, int radio_idx, u32 changed); int ieee80211_hw_conf_chan(struct ieee80211_local *local); diff --git a/net/mac80211/mesh_sync.c b/net/mac80211/mesh_sync.c index 3a66b4cefca7..24a68eef7db8 100644 --- a/net/mac80211/mesh_sync.c +++ b/net/mac80211/mesh_sync.c @@ -103,7 +103,7 @@ mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, u16 stype, * section. */ if (ieee80211_have_rx_timestamp(rx_status)) - t_r = ieee80211_calculate_rx_timestamp(local, rx_status, + t_r = ieee80211_calculate_rx_timestamp(&local->hw, rx_status, len + FCS_LEN, 24); else t_r = drv_get_tsf(local, sdata); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index d9a654ef082d..dbdd67c181d8 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -404,7 +404,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, while ((pos - (u8 *)rthdr) & 7) *pos++ = 0; put_unaligned_le64( - ieee80211_calculate_rx_timestamp(local, status, + ieee80211_calculate_rx_timestamp(&local->hw, status, mpdulen, 0), pos); rthdr->it_present |= cpu_to_le32(BIT(IEEE80211_RADIOTAP_TSFT)); diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 4823c8d45639..eeff230bd909 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -216,7 +216,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local, if (link_conf) { bss_meta.parent_tsf = - ieee80211_calculate_rx_timestamp(local, + ieee80211_calculate_rx_timestamp(&local->hw, rx_status, len + FCS_LEN, 24); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 72e73f4f79c5..38b0c42c4c13 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -3423,20 +3423,7 @@ u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs) return 1; } -/** - * ieee80211_calculate_rx_timestamp - calculate timestamp in frame - * @local: mac80211 hw info struct - * @status: RX status - * @mpdu_len: total MPDU length (including FCS) - * @mpdu_offset: offset into MPDU to calculate timestamp at - * - * This function calculates the RX timestamp at the given MPDU offset, taking - * into account what the RX timestamp was. An offset of 0 will just normalize - * the timestamp to TSF at beginning of MPDU reception. - * - * Returns: the calculated timestamp - */ -u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, +u64 ieee80211_calculate_rx_timestamp(struct ieee80211_hw *hw, struct ieee80211_rx_status *status, unsigned int mpdu_len, unsigned int mpdu_offset) @@ -3555,7 +3542,7 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, case RX_ENC_LEGACY: { struct ieee80211_supported_band *sband; - sband = local->hw.wiphy->bands[status->band]; + sband = hw->wiphy->bands[status->band]; ri.legacy = sband->bitrates[status->rate_idx].bitrate; if (mactime_plcp_start) { @@ -3587,6 +3574,7 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, return ts; } +EXPORT_SYMBOL_GPL(ieee80211_calculate_rx_timestamp); /* Cancel CAC for the interfaces under the specified @local. If @ctx is * also provided, only the interfaces using that ctx will be canceled. From 23eab70e301f8b88282be658ac9b34b5a5953479 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 26 Mar 2026 12:14:34 +0200 Subject: [PATCH 078/124] wifi: mac80211: run NAN DE code only when appropriate NAN DE (Discovery Engine) may be handled in the device or in user space. When handled in user space, all the NAN func management code should not run. Moreover, devices with user space DE should not provide the add/del_nan_func callbaks. For such devices, ieee80211_reconfig_nan will always fail. Make it clear what parts of ieee80211_if_nan are relevant to DE management, and touch those only when DE is offloaded. Add a check that makes sure that a driver doesn't register with add_del/nan_func callbacks if DE is in user space. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260326121156.6665f64865cd.Iee24bef3bae2e1d502216192e760c1e699d271c9@changeid Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 52 ++++++++++++++++++++++++-------------- net/mac80211/ieee80211_i.h | 13 ++++++---- net/mac80211/iface.c | 25 +++++++++++------- net/mac80211/main.c | 4 ++- net/mac80211/util.c | 10 +++++--- 5 files changed, 67 insertions(+), 37 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index b6163dcc7e92..3a6c5fed2420 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -502,12 +502,15 @@ static int ieee80211_add_nan_func(struct wiphy *wiphy, if (!ieee80211_sdata_running(sdata)) return -ENETDOWN; - spin_lock_bh(&sdata->u.nan.func_lock); + if (WARN_ON(wiphy->nan_capa.flags & WIPHY_NAN_FLAGS_USERSPACE_DE)) + return -EOPNOTSUPP; - ret = idr_alloc(&sdata->u.nan.function_inst_ids, + spin_lock_bh(&sdata->u.nan.de.func_lock); + + ret = idr_alloc(&sdata->u.nan.de.function_inst_ids, nan_func, 1, sdata->local->hw.max_nan_de_entries + 1, GFP_ATOMIC); - spin_unlock_bh(&sdata->u.nan.func_lock); + spin_unlock_bh(&sdata->u.nan.de.func_lock); if (ret < 0) return ret; @@ -518,10 +521,10 @@ static int ieee80211_add_nan_func(struct wiphy *wiphy, ret = drv_add_nan_func(sdata->local, sdata, nan_func); if (ret) { - spin_lock_bh(&sdata->u.nan.func_lock); - idr_remove(&sdata->u.nan.function_inst_ids, + spin_lock_bh(&sdata->u.nan.de.func_lock); + idr_remove(&sdata->u.nan.de.function_inst_ids, nan_func->instance_id); - spin_unlock_bh(&sdata->u.nan.func_lock); + spin_unlock_bh(&sdata->u.nan.de.func_lock); } return ret; @@ -534,9 +537,9 @@ ieee80211_find_nan_func_by_cookie(struct ieee80211_sub_if_data *sdata, struct cfg80211_nan_func *func; int id; - lockdep_assert_held(&sdata->u.nan.func_lock); + lockdep_assert_held(&sdata->u.nan.de.func_lock); - idr_for_each_entry(&sdata->u.nan.function_inst_ids, func, id) { + idr_for_each_entry(&sdata->u.nan.de.function_inst_ids, func, id) { if (func->cookie == cookie) return func; } @@ -555,13 +558,16 @@ static void ieee80211_del_nan_func(struct wiphy *wiphy, !ieee80211_sdata_running(sdata)) return; - spin_lock_bh(&sdata->u.nan.func_lock); + if (WARN_ON(wiphy->nan_capa.flags & WIPHY_NAN_FLAGS_USERSPACE_DE)) + return; + + spin_lock_bh(&sdata->u.nan.de.func_lock); func = ieee80211_find_nan_func_by_cookie(sdata, cookie); if (func) instance_id = func->instance_id; - spin_unlock_bh(&sdata->u.nan.func_lock); + spin_unlock_bh(&sdata->u.nan.de.func_lock); if (instance_id) drv_del_nan_func(sdata->local, sdata, instance_id); @@ -4888,18 +4894,22 @@ void ieee80211_nan_func_terminated(struct ieee80211_vif *vif, if (WARN_ON(vif->type != NL80211_IFTYPE_NAN)) return; - spin_lock_bh(&sdata->u.nan.func_lock); + if (WARN_ON(sdata->local->hw.wiphy->nan_capa.flags & + WIPHY_NAN_FLAGS_USERSPACE_DE)) + return; - func = idr_find(&sdata->u.nan.function_inst_ids, inst_id); + spin_lock_bh(&sdata->u.nan.de.func_lock); + + func = idr_find(&sdata->u.nan.de.function_inst_ids, inst_id); if (WARN_ON(!func)) { - spin_unlock_bh(&sdata->u.nan.func_lock); + spin_unlock_bh(&sdata->u.nan.de.func_lock); return; } cookie = func->cookie; - idr_remove(&sdata->u.nan.function_inst_ids, inst_id); + idr_remove(&sdata->u.nan.de.function_inst_ids, inst_id); - spin_unlock_bh(&sdata->u.nan.func_lock); + spin_unlock_bh(&sdata->u.nan.de.func_lock); cfg80211_free_nan_func(func); @@ -4918,16 +4928,20 @@ void ieee80211_nan_func_match(struct ieee80211_vif *vif, if (WARN_ON(vif->type != NL80211_IFTYPE_NAN)) return; - spin_lock_bh(&sdata->u.nan.func_lock); + if (WARN_ON(sdata->local->hw.wiphy->nan_capa.flags & + WIPHY_NAN_FLAGS_USERSPACE_DE)) + return; - func = idr_find(&sdata->u.nan.function_inst_ids, match->inst_id); + spin_lock_bh(&sdata->u.nan.de.func_lock); + + func = idr_find(&sdata->u.nan.de.function_inst_ids, match->inst_id); if (WARN_ON(!func)) { - spin_unlock_bh(&sdata->u.nan.func_lock); + spin_unlock_bh(&sdata->u.nan.de.func_lock); return; } match->cookie = func->cookie; - spin_unlock_bh(&sdata->u.nan.func_lock); + spin_unlock_bh(&sdata->u.nan.de.func_lock); cfg80211_nan_match(ieee80211_vif_to_wdev(vif), match, gfp); } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 53d783769642..8d5f9a725fdf 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -987,16 +987,19 @@ struct ieee80211_if_mntr { * * @conf: current NAN configuration * @started: true iff NAN is started - * @func_lock: lock for @func_inst_ids - * @function_inst_ids: a bitmap of available instance_id's + * @de: Discovery Engine state (only valid if !WIPHY_NAN_FLAGS_USERSPACE_DE) + * @de.func_lock: lock for @de.function_inst_ids + * @de.function_inst_ids: a bitmap of available instance_id's */ struct ieee80211_if_nan { struct cfg80211_nan_conf conf; bool started; - /* protects function_inst_ids */ - spinlock_t func_lock; - struct idr function_inst_ids; + struct { + /* protects function_inst_ids */ + spinlock_t func_lock; + struct idr function_inst_ids; + } de; }; struct ieee80211_link_data_managed { diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 7518dcbcdf1c..f0a5a675c5a5 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -622,15 +622,19 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do break; case NL80211_IFTYPE_NAN: /* clean all the functions */ - spin_lock_bh(&sdata->u.nan.func_lock); + if (!(local->hw.wiphy->nan_capa.flags & + WIPHY_NAN_FLAGS_USERSPACE_DE)) { + spin_lock_bh(&sdata->u.nan.de.func_lock); - idr_for_each_entry(&sdata->u.nan.function_inst_ids, func, i) { - idr_remove(&sdata->u.nan.function_inst_ids, i); - cfg80211_free_nan_func(func); + idr_for_each_entry(&sdata->u.nan.de.function_inst_ids, + func, i) { + idr_remove(&sdata->u.nan.de.function_inst_ids, i); + cfg80211_free_nan_func(func); + } + idr_destroy(&sdata->u.nan.de.function_inst_ids); + + spin_unlock_bh(&sdata->u.nan.de.func_lock); } - idr_destroy(&sdata->u.nan.function_inst_ids); - - spin_unlock_bh(&sdata->u.nan.func_lock); break; default: wiphy_work_cancel(sdata->local->hw.wiphy, &sdata->work); @@ -1942,8 +1946,11 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, MONITOR_FLAG_OTHER_BSS; break; case NL80211_IFTYPE_NAN: - idr_init(&sdata->u.nan.function_inst_ids); - spin_lock_init(&sdata->u.nan.func_lock); + if (!(sdata->local->hw.wiphy->nan_capa.flags & + WIPHY_NAN_FLAGS_USERSPACE_DE)) { + idr_init(&sdata->u.nan.de.function_inst_ids); + spin_lock_init(&sdata->u.nan.de.func_lock); + } sdata->vif.bss_conf.bssid = sdata->vif.addr; break; case NL80211_IFTYPE_AP_VLAN: diff --git a/net/mac80211/main.c b/net/mac80211/main.c index d1bb6353908d..f47dd58770ad 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1157,7 +1157,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if (WARN_ON(local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_NAN) && - (!local->ops->start_nan || !local->ops->stop_nan))) + ((!local->ops->start_nan || !local->ops->stop_nan) || + (local->hw.wiphy->nan_capa.flags & WIPHY_NAN_FLAGS_USERSPACE_DE && + (local->ops->add_nan_func || local->ops->del_nan_func))))) return -EINVAL; if (hw->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO) { diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 38b0c42c4c13..36795529ff82 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1754,6 +1754,10 @@ static int ieee80211_reconfig_nan(struct ieee80211_sub_if_data *sdata) if (WARN_ON(res)) return res; + if (sdata->local->hw.wiphy->nan_capa.flags & + WIPHY_NAN_FLAGS_USERSPACE_DE) + return 0; + funcs = kzalloc_objs(*funcs, sdata->local->hw.max_nan_de_entries + 1); if (!funcs) return -ENOMEM; @@ -1762,12 +1766,12 @@ static int ieee80211_reconfig_nan(struct ieee80211_sub_if_data *sdata) * This is a little bit ugly. We need to call a potentially sleeping * callback for each NAN function, so we can't hold the spinlock. */ - spin_lock_bh(&sdata->u.nan.func_lock); + spin_lock_bh(&sdata->u.nan.de.func_lock); - idr_for_each_entry(&sdata->u.nan.function_inst_ids, func, id) + idr_for_each_entry(&sdata->u.nan.de.function_inst_ids, func, id) funcs[i++] = func; - spin_unlock_bh(&sdata->u.nan.func_lock); + spin_unlock_bh(&sdata->u.nan.de.func_lock); for (i = 0; funcs[i]; i++) { res = drv_add_nan_func(sdata->local, sdata, funcs[i]); From 589c06e8fdeec592e018004cebe283370160e581 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 26 Mar 2026 12:14:35 +0200 Subject: [PATCH 079/124] wifi: mac80211: add NAN local schedule support A NAN local schedule consist of a list of NAN channels, and an array that maps time slots to the channel it is scheduled to (or NULL to indicate unscheduled). A NAN channel is the configuration of a channel which is used for NAN operations. It is a new type of chanctx user (before, the only user is a link). A NAN channel may not have a chanctx assigned if it is ULWed out. A NAN channel may or may not be scheduled (for example, user space may want to prepare the resources before the actual schedule is configured). Add management of the NAN local schedule. Since we introduce a new chanctx user, also adjust the different for_each_chanctx_user_* macros to visit also the NAN channels and take those into account. Co-developed-by: Avraham Stern Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260326121156.03350fd40630.Id158f815cfc9b5ab1ebdb8ee608bda426e4d7474@changeid Signed-off-by: Johannes Berg --- include/net/mac80211.h | 66 +++++++ net/mac80211/Makefile | 2 +- net/mac80211/cfg.c | 13 ++ net/mac80211/chan.c | 132 ++++++++++--- net/mac80211/ieee80211_i.h | 20 +- net/mac80211/iface.c | 8 + net/mac80211/nan.c | 378 +++++++++++++++++++++++++++++++++++++ net/mac80211/util.c | 28 ++- 8 files changed, 609 insertions(+), 38 deletions(-) create mode 100644 net/mac80211/nan.c diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 8b513f777187..cc23f99f8318 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -365,6 +365,7 @@ struct ieee80211_vif_chanctx_switch { * @BSS_CHANGED_MLD_VALID_LINKS: MLD valid links status changed. * @BSS_CHANGED_MLD_TTLM: negotiated TID to link mapping was changed * @BSS_CHANGED_TPE: transmit power envelope changed + * @BSS_CHANGED_NAN_LOCAL_SCHED: NAN local schedule changed (NAN mode only) */ enum ieee80211_bss_change { BSS_CHANGED_ASSOC = 1<<0, @@ -402,6 +403,7 @@ enum ieee80211_bss_change { BSS_CHANGED_MLD_VALID_LINKS = BIT_ULL(33), BSS_CHANGED_MLD_TTLM = BIT_ULL(34), BSS_CHANGED_TPE = BIT_ULL(35), + BSS_CHANGED_NAN_LOCAL_SCHED = BIT_ULL(36), /* when adding here, make sure to change ieee80211_reconfig */ }; @@ -866,6 +868,28 @@ struct ieee80211_bss_conf { u8 s1g_long_beacon_period; }; +#define IEEE80211_NAN_MAX_CHANNELS 3 + +/** + * struct ieee80211_nan_channel - NAN channel information + * + * @chanreq: channel request for this NAN channel. Even though this chanreq::ap + * is irrelevant for NAN, still store it for convenience - some functions + * require it as an argument. + * @needed_rx_chains: number of RX chains needed for this NAN channel + * @chanctx_conf: chanctx_conf assigned to this NAN channel. Will be %NULL + * if the channel is ULWed. + * @channel_entry: the Channel Entry blob as defined in Wi-Fi Aware + * (TM) 4.0 specification Table 100 (Channel Entry format for the NAN + * Availability attribute). + */ +struct ieee80211_nan_channel { + struct ieee80211_chan_req chanreq; + u8 needed_rx_chains; + struct ieee80211_chanctx_conf *chanctx_conf; + u8 channel_entry[6]; +}; + /** * enum mac80211_tx_info_flags - flags to describe transmission information/status * @@ -1917,6 +1941,8 @@ enum ieee80211_offload_flags { IEEE80211_OFFLOAD_DECAP_ENABLED = BIT(2), }; +#define IEEE80211_NAN_AVAIL_BLOB_MAX_LEN 54 + /** * struct ieee80211_eml_params - EHT Operating mode notification parameters * @@ -1942,6 +1968,32 @@ struct ieee80211_eml_params { u8 emlmr_mcs_map_bw[9]; }; +/** + * struct ieee80211_nan_sched_cfg - NAN schedule configuration + * @channels: array of NAN channels. A channel entry is in use if + * channels[i].chanreq.oper.chan is not NULL. + * @schedule: NAN local schedule - mapping of each 16TU time slot to + * the NAN channel on which the radio will operate. NULL if unscheduled. + * @avail_blob: NAN Availability attribute blob. + * @avail_blob_len: length of the @avail_blob in bytes. + * @deferred: indicates that the driver should notify peers before applying the + * new NAN schedule, and apply the new schedule the second NAN Slot + * boundary after it notified the peers, as defined in Wi-Fi Aware (TM) 4.0 + * specification, section 5.2.2. + * The driver must call ieee80211_nan_sched_update_done() after the + * schedule has been applied. + * If a HW restart happened while a deferred schedule update was pending, + * mac80211 will reconfigure the deferred schedule (and wait for the driver + * to notify that the schedule has been applied). + */ +struct ieee80211_nan_sched_cfg { + struct ieee80211_nan_channel channels[IEEE80211_NAN_MAX_CHANNELS]; + struct ieee80211_nan_channel *schedule[CFG80211_NAN_SCHED_NUM_TIME_SLOTS]; + u8 avail_blob[IEEE80211_NAN_AVAIL_BLOB_MAX_LEN]; + u16 avail_blob_len; + bool deferred; +}; + /** * struct ieee80211_vif_cfg - interface configuration * @assoc: association status @@ -1970,6 +2022,7 @@ struct ieee80211_eml_params { * your driver/device needs to do. * @ap_addr: AP MLD address, or BSSID for non-MLO connections * (station mode only) + * @nan_sched: NAN schedule parameters. &struct ieee80211_nan_sched_cfg */ struct ieee80211_vif_cfg { /* association related data */ @@ -1988,6 +2041,8 @@ struct ieee80211_vif_cfg { bool s1g; bool idle; u8 ap_addr[ETH_ALEN] __aligned(2); + /* Protected by the wiphy mutex */ + struct ieee80211_nan_sched_cfg nan_sched; }; #define IEEE80211_TTLM_NUM_TIDS 8 @@ -7756,6 +7811,17 @@ void ieee80211_nan_func_match(struct ieee80211_vif *vif, struct cfg80211_nan_match_params *match, gfp_t gfp); +/** + * ieee80211_nan_sched_update_done - notify that NAN schedule update is done + * + * This function is called by the driver to notify mac80211 that the NAN + * schedule update has been applied. + * Must be called with wiphy mutex held. May sleep. + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + */ +void ieee80211_nan_sched_update_done(struct ieee80211_vif *vif); + /** * ieee80211_calc_rx_airtime - calculate estimated transmission airtime for RX. * diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index b0e392eb7753..abf46c951299 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -36,7 +36,7 @@ mac80211-y := \ tdls.o \ ocb.o \ airtime.o \ - eht.o uhr.o + eht.o uhr.o nan.o mac80211-$(CONFIG_MAC80211_LEDS) += led.o mac80211-$(CONFIG_MAC80211_DEBUGFS) += \ diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 3a6c5fed2420..5bc867a56811 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -5594,6 +5594,18 @@ ieee80211_set_epcs(struct wiphy *wiphy, struct net_device *dev, bool enable) return ieee80211_mgd_set_epcs(sdata, enable); } +static int +ieee80211_set_local_nan_sched(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct cfg80211_nan_local_sched *sched) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); + + lockdep_assert_wiphy(wiphy); + + return ieee80211_nan_set_local_sched(sdata, sched); +} + const struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -5710,4 +5722,5 @@ const struct cfg80211_ops mac80211_config_ops = { .get_radio_mask = ieee80211_get_radio_mask, .assoc_ml_reconf = ieee80211_assoc_ml_reconf, .set_epcs = ieee80211_set_epcs, + .nan_set_local_sched = ieee80211_set_local_nan_sched, }; diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index dd99fdc1ea9d..50a06de928ba 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -16,6 +16,8 @@ struct ieee80211_chanctx_user_iter { struct ieee80211_chan_req *chanreq; struct ieee80211_sub_if_data *sdata; struct ieee80211_link_data *link; + struct ieee80211_nan_channel *nan_channel; + int nan_channel_next_idx; enum nl80211_iftype iftype; bool reserved, radar_required, done; enum { @@ -31,20 +33,38 @@ enum ieee80211_chanctx_iter_type { CHANCTX_ITER_ASSIGNED, }; -static void ieee80211_chanctx_user_iter_next(struct ieee80211_local *local, - struct ieee80211_chanctx *ctx, - struct ieee80211_chanctx_user_iter *iter, - enum ieee80211_chanctx_iter_type type, - bool start) +static bool +ieee80211_chanctx_user_iter_next_nan_channel(struct ieee80211_chanctx *ctx, + struct ieee80211_chanctx_user_iter *iter) { - lockdep_assert_wiphy(local->hw.wiphy); + /* Start from the next index after current position */ + for (int i = iter->nan_channel_next_idx; + i < ARRAY_SIZE(iter->sdata->vif.cfg.nan_sched.channels); i++) { + struct ieee80211_nan_channel *nan_channel = + &iter->sdata->vif.cfg.nan_sched.channels[i]; - if (start) { - memset(iter, 0, sizeof(*iter)); - goto next_interface; + if (!nan_channel->chanreq.oper.chan) + continue; + + if (nan_channel->chanctx_conf != &ctx->conf) + continue; + + iter->nan_channel = nan_channel; + iter->nan_channel_next_idx = i + 1; + iter->chanreq = &nan_channel->chanreq; + iter->link = NULL; + iter->reserved = false; + iter->radar_required = false; + return true; } + return false; +} -next_link: +static bool +ieee80211_chanctx_user_iter_next_link(struct ieee80211_chanctx *ctx, + struct ieee80211_chanctx_user_iter *iter, + enum ieee80211_chanctx_iter_type type) +{ for (int link_id = iter->link ? iter->link->link_id : 0; link_id < ARRAY_SIZE(iter->sdata->link); link_id++) { @@ -64,7 +84,7 @@ next_link: iter->reserved = false; iter->radar_required = link->radar_required; iter->chanreq = &link->conf->chanreq; - return; + return true; } fallthrough; case CHANCTX_ITER_POS_RESERVED: @@ -77,7 +97,7 @@ next_link: link->reserved_radar_required; iter->chanreq = &link->reserved; - return; + return true; } fallthrough; case CHANCTX_ITER_POS_DONE: @@ -85,6 +105,33 @@ next_link: continue; } } + return false; +} + +static void +ieee80211_chanctx_user_iter_next(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx, + struct ieee80211_chanctx_user_iter *iter, + enum ieee80211_chanctx_iter_type type, + bool start) +{ + bool found; + + lockdep_assert_wiphy(local->hw.wiphy); + + if (start) { + memset(iter, 0, sizeof(*iter)); + goto next_interface; + } + +next_user: + if (iter->iftype == NL80211_IFTYPE_NAN) + found = ieee80211_chanctx_user_iter_next_nan_channel(ctx, iter); + else + found = ieee80211_chanctx_user_iter_next_link(ctx, iter, type); + + if (found) + return; next_interface: /* next (or first) interface */ @@ -97,10 +144,18 @@ next_interface: if (iter->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) continue; + /* NAN channels don't reserve channel context */ + if (iter->sdata->vif.type == NL80211_IFTYPE_NAN && + type == CHANCTX_ITER_RESERVED) + continue; + + iter->nan_channel = NULL; iter->link = NULL; - iter->per_link = CHANCTX_ITER_POS_ASSIGNED; iter->iftype = iter->sdata->vif.type; - goto next_link; + iter->chanreq = NULL; + iter->per_link = CHANCTX_ITER_POS_ASSIGNED; + iter->nan_channel_next_idx = 0; + goto next_user; } iter->done = true; @@ -133,8 +188,8 @@ next_interface: CHANCTX_ITER_ALL, \ false)) -static int ieee80211_chanctx_num_assigned(struct ieee80211_local *local, - struct ieee80211_chanctx *ctx) +int ieee80211_chanctx_num_assigned(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx) { struct ieee80211_chanctx_user_iter iter; int num = 0; @@ -321,7 +376,7 @@ ieee80211_chanctx_non_reserved_chandef(struct ieee80211_local *local, lockdep_assert_wiphy(local->hw.wiphy); for_each_chanctx_user_assigned(local, ctx, &iter) { - if (iter.link->reserved_chanctx) + if (iter.link && iter.link->reserved_chanctx) continue; comp_def = ieee80211_chanreq_compatible(iter.chanreq, @@ -480,7 +535,6 @@ ieee80211_get_width_of_link(struct ieee80211_link_data *link) case NL80211_IFTYPE_AP_VLAN: return ieee80211_get_max_required_bw(link); case NL80211_IFTYPE_P2P_DEVICE: - case NL80211_IFTYPE_NAN: break; case NL80211_IFTYPE_MONITOR: WARN_ON_ONCE(!ieee80211_hw_check(&local->hw, @@ -495,6 +549,7 @@ ieee80211_get_width_of_link(struct ieee80211_link_data *link) case NUM_NL80211_IFTYPES: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_NAN: case NL80211_IFTYPE_NAN_DATA: WARN_ON_ONCE(1); break; @@ -504,6 +559,18 @@ ieee80211_get_width_of_link(struct ieee80211_link_data *link) return NL80211_CHAN_WIDTH_20_NOHT; } +static enum nl80211_chan_width +ieee80211_get_width_of_chanctx_user(struct ieee80211_chanctx_user_iter *iter) +{ + if (iter->link) + return ieee80211_get_width_of_link(iter->link); + + if (WARN_ON_ONCE(!iter->nan_channel || iter->reserved)) + return NL80211_CHAN_WIDTH_20_NOHT; + + return iter->nan_channel->chanreq.oper.width; +} + static enum nl80211_chan_width ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local, struct ieee80211_chanctx *ctx, @@ -521,7 +588,7 @@ ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local, /* When this is true we only care about the reserving links */ if (check_reserved) { for_each_chanctx_user_reserved(local, ctx, &iter) { - width = ieee80211_get_width_of_link(iter.link); + width = ieee80211_get_width_of_chanctx_user(&iter); max_bw = max(max_bw, width); } goto check_monitor; @@ -529,7 +596,7 @@ ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local, /* Consider all assigned links */ for_each_chanctx_user_assigned(local, ctx, &iter) { - width = ieee80211_get_width_of_link(iter.link); + width = ieee80211_get_width_of_chanctx_user(&iter); max_bw = max(max_bw, width); } @@ -943,7 +1010,10 @@ ieee80211_new_chanctx(struct ieee80211_local *local, kfree(ctx); return ERR_PTR(err); } - /* We ignored a driver error, see _ieee80211_set_active_links */ + /* + * We ignored a driver error, see _ieee80211_set_active_links and/or + * ieee80211_nan_set_local_sched + */ WARN_ON_ONCE(err && !local->in_reconfig); list_add_rcu(&ctx->list, &local->chanctx_list); @@ -964,9 +1034,9 @@ static void ieee80211_del_chanctx(struct ieee80211_local *local, ieee80211_remove_wbrf(local, &ctx->conf.def); } -static void ieee80211_free_chanctx(struct ieee80211_local *local, - struct ieee80211_chanctx *ctx, - bool skip_idle_recalc) +void ieee80211_free_chanctx(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx, + bool skip_idle_recalc) { lockdep_assert_wiphy(local->hw.wiphy); @@ -1161,6 +1231,7 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_OCB: + case NL80211_IFTYPE_NAN: break; default: continue; @@ -1171,6 +1242,15 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, break; } + if (iter.nan_channel) { + rx_chains_dynamic = rx_chains_static = + iter.nan_channel->needed_rx_chains; + break; + } + + if (!iter.link) + continue; + switch (iter.link->smps_mode) { default: WARN_ONCE(1, "Invalid SMPS mode %d\n", @@ -1779,7 +1859,7 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) for_each_chanctx_user_assigned(local, ctx->replace_ctx, &iter) { n_assigned++; - if (iter.link->reserved_chanctx) { + if (iter.link && iter.link->reserved_chanctx) { n_reserved++; if (iter.link->reserved_ready) n_ready++; @@ -2035,7 +2115,7 @@ void __ieee80211_link_release_channel(struct ieee80211_link_data *link, ieee80211_vif_use_reserved_switch(local); } -static struct ieee80211_chanctx * +struct ieee80211_chanctx * ieee80211_find_or_create_chanctx(struct ieee80211_sub_if_data *sdata, const struct ieee80211_chan_req *chanreq, enum ieee80211_chanctx_mode mode, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 8d5f9a725fdf..92ea8de8a6db 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -990,6 +990,8 @@ struct ieee80211_if_mntr { * @de: Discovery Engine state (only valid if !WIPHY_NAN_FLAGS_USERSPACE_DE) * @de.func_lock: lock for @de.function_inst_ids * @de.function_inst_ids: a bitmap of available instance_id's + * @removed_channels: bitmap of channels that should be removed from the NAN + * schedule once the deferred schedule update is completed. */ struct ieee80211_if_nan { struct cfg80211_nan_conf conf; @@ -1000,6 +1002,8 @@ struct ieee80211_if_nan { spinlock_t func_lock; struct idr function_inst_ids; } de; + + DECLARE_BITMAP(removed_channels, IEEE80211_NAN_MAX_CHANNELS); }; struct ieee80211_link_data_managed { @@ -2024,6 +2028,10 @@ int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata, int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata, u64 *changed); +/* NAN code */ +int ieee80211_nan_set_local_sched(struct ieee80211_sub_if_data *sdata, + struct cfg80211_nan_local_sched *sched); + /* scan/BSS handling */ void ieee80211_scan_work(struct wiphy *wiphy, struct wiphy_work *work); int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata, @@ -2812,7 +2820,17 @@ int ieee80211_max_num_channels(struct ieee80211_local *local, int radio_idx); u32 ieee80211_get_radio_mask(struct wiphy *wiphy, struct net_device *dev); void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, struct ieee80211_chanctx *ctx); - +struct ieee80211_chanctx * +ieee80211_find_or_create_chanctx(struct ieee80211_sub_if_data *sdata, + const struct ieee80211_chan_req *chanreq, + enum ieee80211_chanctx_mode mode, + bool assign_on_failure, + bool *reused_ctx); +void ieee80211_free_chanctx(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx, + bool skip_idle_recalc); +int ieee80211_chanctx_num_assigned(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx); /* TDLS */ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, int link_id, diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index f0a5a675c5a5..0f3e49cdbb39 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -886,6 +886,14 @@ static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata) ieee80211_vif_clear_links(sdata); ieee80211_link_stop(&sdata->deflink); + + if (sdata->vif.type == NL80211_IFTYPE_NAN) { + struct ieee80211_nan_sched_cfg *nan_sched = + &sdata->vif.cfg.nan_sched; + + for (int i = 0; i < ARRAY_SIZE(nan_sched->channels); i++) + WARN_ON(nan_sched->channels[i].chanreq.oper.chan); + } } static void ieee80211_uninit(struct net_device *dev) diff --git a/net/mac80211/nan.c b/net/mac80211/nan.c new file mode 100644 index 000000000000..2fa55e9a9dab --- /dev/null +++ b/net/mac80211/nan.c @@ -0,0 +1,378 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NAN mode implementation + * Copyright(c) 2025 Intel Corporation + */ +#include + +#include "ieee80211_i.h" +#include "driver-ops.h" + +static void +ieee80211_nan_init_channel(struct ieee80211_nan_channel *nan_channel, + struct cfg80211_nan_channel *cfg_nan_channel) +{ + memset(nan_channel, 0, sizeof(*nan_channel)); + + nan_channel->chanreq.oper = cfg_nan_channel->chandef; + memcpy(nan_channel->channel_entry, cfg_nan_channel->channel_entry, + sizeof(nan_channel->channel_entry)); + nan_channel->needed_rx_chains = cfg_nan_channel->rx_nss; +} + +static void +ieee80211_nan_update_channel(struct ieee80211_local *local, + struct ieee80211_nan_channel *nan_channel, + struct cfg80211_nan_channel *cfg_nan_channel, + bool deferred) +{ + struct ieee80211_chanctx_conf *conf; + bool reducing_nss; + + if (WARN_ON(!cfg80211_chandef_identical(&nan_channel->chanreq.oper, + &cfg_nan_channel->chandef))) + return; + + if (WARN_ON(memcmp(nan_channel->channel_entry, + cfg_nan_channel->channel_entry, + sizeof(nan_channel->channel_entry)))) + return; + + if (nan_channel->needed_rx_chains == cfg_nan_channel->rx_nss) + return; + + reducing_nss = nan_channel->needed_rx_chains > cfg_nan_channel->rx_nss; + nan_channel->needed_rx_chains = cfg_nan_channel->rx_nss; + + conf = nan_channel->chanctx_conf; + + /* + * If we are adding NSSs, we need to be ready before notifying the peer, + * if we are reducing NSSs, we need to wait until the peer is notified. + */ + if (!conf || (deferred && reducing_nss)) + return; + + ieee80211_recalc_smps_chanctx(local, container_of(conf, + struct ieee80211_chanctx, + conf)); +} + +static int +ieee80211_nan_use_chanctx(struct ieee80211_sub_if_data *sdata, + struct ieee80211_nan_channel *nan_channel, + bool assign_on_failure) +{ + struct ieee80211_chanctx *ctx; + bool reused_ctx; + + if (!nan_channel->chanreq.oper.chan) + return -EINVAL; + + if (ieee80211_check_combinations(sdata, &nan_channel->chanreq.oper, + IEEE80211_CHANCTX_SHARED, 0, -1)) + return -EBUSY; + + ctx = ieee80211_find_or_create_chanctx(sdata, &nan_channel->chanreq, + IEEE80211_CHANCTX_SHARED, + assign_on_failure, + &reused_ctx); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + + nan_channel->chanctx_conf = &ctx->conf; + + /* + * In case an existing channel context is being used, we marked it as + * will_be_used, now that it is assigned - clear this indication + */ + if (reused_ctx) { + WARN_ON(!ctx->will_be_used); + ctx->will_be_used = false; + } + ieee80211_recalc_chanctx_min_def(sdata->local, ctx); + ieee80211_recalc_smps_chanctx(sdata->local, ctx); + + return 0; +} + +static void +ieee80211_nan_remove_channel(struct ieee80211_sub_if_data *sdata, + struct ieee80211_nan_channel *nan_channel) +{ + struct ieee80211_chanctx_conf *conf; + struct ieee80211_chanctx *ctx; + struct ieee80211_nan_sched_cfg *sched_cfg = &sdata->vif.cfg.nan_sched; + + if (WARN_ON(!nan_channel)) + return; + + lockdep_assert_wiphy(sdata->local->hw.wiphy); + + if (!nan_channel->chanreq.oper.chan) + return; + + for (int slot = 0; slot < ARRAY_SIZE(sched_cfg->schedule); slot++) + if (sched_cfg->schedule[slot] == nan_channel) + sched_cfg->schedule[slot] = NULL; + + conf = nan_channel->chanctx_conf; + + memset(nan_channel, 0, sizeof(*nan_channel)); + + /* Update the driver before (possibly) releasing the channel context */ + drv_vif_cfg_changed(sdata->local, sdata, BSS_CHANGED_NAN_LOCAL_SCHED); + + /* Channel might not have a chanctx if it was ULWed */ + if (!conf) + return; + + ctx = container_of(conf, struct ieee80211_chanctx, conf); + + if (ieee80211_chanctx_num_assigned(sdata->local, ctx) > 0) { + ieee80211_recalc_chanctx_chantype(sdata->local, ctx); + ieee80211_recalc_smps_chanctx(sdata->local, ctx); + ieee80211_recalc_chanctx_min_def(sdata->local, ctx); + } + + if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) + ieee80211_free_chanctx(sdata->local, ctx, false); +} + +static struct ieee80211_nan_channel * +ieee80211_nan_find_free_channel(struct ieee80211_nan_sched_cfg *sched_cfg) +{ + for (int i = 0; i < ARRAY_SIZE(sched_cfg->channels); i++) { + if (!sched_cfg->channels[i].chanreq.oper.chan) + return &sched_cfg->channels[i]; + } + + return NULL; +} + +int ieee80211_nan_set_local_sched(struct ieee80211_sub_if_data *sdata, + struct cfg80211_nan_local_sched *sched) +{ + struct ieee80211_nan_channel *sched_idx_to_chan[IEEE80211_NAN_MAX_CHANNELS] = {}; + struct ieee80211_nan_sched_cfg *sched_cfg = &sdata->vif.cfg.nan_sched; + struct ieee80211_nan_sched_cfg backup_sched; + int ret; + + if (sched->n_channels > IEEE80211_NAN_MAX_CHANNELS) + return -EOPNOTSUPP; + + if (sched->nan_avail_blob_len > IEEE80211_NAN_AVAIL_BLOB_MAX_LEN) + return -EINVAL; + + /* + * If a deferred schedule update is pending completion, new updates are + * not allowed. Only allow to configure an empty schedule so NAN can be + * stopped in the middle of a deferred update. This is fine because + * empty schedule means the local NAN device will not be available for + * peers anymore so there is no need to update peers about a new + * schedule. + */ + if (WARN_ON(sched_cfg->deferred && sched->n_channels)) + return -EBUSY; + + bitmap_zero(sdata->u.nan.removed_channels, IEEE80211_NAN_MAX_CHANNELS); + + memcpy(backup_sched.schedule, sched_cfg->schedule, + sizeof(backup_sched.schedule)); + memcpy(backup_sched.channels, sched_cfg->channels, + sizeof(backup_sched.channels)); + memcpy(backup_sched.avail_blob, sched_cfg->avail_blob, + sizeof(backup_sched.avail_blob)); + backup_sched.avail_blob_len = sched_cfg->avail_blob_len; + + memcpy(sched_cfg->avail_blob, sched->nan_avail_blob, + sched->nan_avail_blob_len); + sched_cfg->avail_blob_len = sched->nan_avail_blob_len; + + /* + * Remove channels that are no longer in the new schedule to free up + * resources before adding new channels. For deferred schedule, channels + * will be removed when the schedule is applied. + * Create a mapping from sched index to sched_cfg channel + */ + for (int i = 0; i < ARRAY_SIZE(sched_cfg->channels); i++) { + bool still_needed = false; + + if (!sched_cfg->channels[i].chanreq.oper.chan) + continue; + + for (int j = 0; j < sched->n_channels; j++) { + if (cfg80211_chandef_identical(&sched_cfg->channels[i].chanreq.oper, + &sched->nan_channels[j].chandef)) { + sched_idx_to_chan[j] = + &sched_cfg->channels[i]; + still_needed = true; + break; + } + } + + if (!still_needed) { + __set_bit(i, sdata->u.nan.removed_channels); + if (!sched->deferred) + ieee80211_nan_remove_channel(sdata, + &sched_cfg->channels[i]); + } + } + + for (int i = 0; i < sched->n_channels; i++) { + struct ieee80211_nan_channel *chan = sched_idx_to_chan[i]; + + if (chan) { + ieee80211_nan_update_channel(sdata->local, chan, + &sched->nan_channels[i], + sched->deferred); + } else { + chan = ieee80211_nan_find_free_channel(sched_cfg); + if (WARN_ON(!chan)) { + ret = -EINVAL; + goto err; + } + + sched_idx_to_chan[i] = chan; + ieee80211_nan_init_channel(chan, + &sched->nan_channels[i]); + + ret = ieee80211_nan_use_chanctx(sdata, chan, false); + if (ret) { + memset(chan, 0, sizeof(*chan)); + goto err; + } + } + } + + for (int s = 0; s < ARRAY_SIZE(sched_cfg->schedule); s++) { + if (sched->schedule[s] < ARRAY_SIZE(sched_idx_to_chan)) + sched_cfg->schedule[s] = + sched_idx_to_chan[sched->schedule[s]]; + else + sched_cfg->schedule[s] = NULL; + } + + sched_cfg->deferred = sched->deferred; + + drv_vif_cfg_changed(sdata->local, sdata, BSS_CHANGED_NAN_LOCAL_SCHED); + + /* + * For deferred update, don't update NDI carriers yet as the new + * schedule is not yet applied so common slots don't change. The NDI + * carrier will be updated once the driver notifies the new schedule is + * applied. + */ + if (sched_cfg->deferred) + return 0; + + bitmap_zero(sdata->u.nan.removed_channels, IEEE80211_NAN_MAX_CHANNELS); + + return 0; +err: + /* Remove newly added channels */ + for (int i = 0; i < ARRAY_SIZE(sched_cfg->channels); i++) { + struct cfg80211_chan_def *chan_def = + &sched_cfg->channels[i].chanreq.oper; + + if (!chan_def->chan) + continue; + + if (!cfg80211_chandef_identical(&backup_sched.channels[i].chanreq.oper, + chan_def)) + ieee80211_nan_remove_channel(sdata, + &sched_cfg->channels[i]); + } + + /* Re-add all backed up channels */ + for (int i = 0; i < ARRAY_SIZE(backup_sched.channels); i++) { + struct ieee80211_nan_channel *chan = &sched_cfg->channels[i]; + + *chan = backup_sched.channels[i]; + + /* + * For deferred update, no channels were removed and the channel + * context didn't change, so nothing else to do. + */ + if (!chan->chanctx_conf || sched->deferred) + continue; + + if (test_bit(i, sdata->u.nan.removed_channels)) { + /* Clear the stale chanctx pointer */ + chan->chanctx_conf = NULL; + /* + * We removed the newly added channels so we don't lack + * resources. So the only reason that this would fail + * is a FW error which we ignore. Therefore, this + * should never fail. + */ + WARN_ON(ieee80211_nan_use_chanctx(sdata, chan, true)); + } else { + struct ieee80211_chanctx_conf *conf = chan->chanctx_conf; + + /* FIXME: detect no-op? */ + /* Channel was not removed but may have been updated */ + ieee80211_recalc_smps_chanctx(sdata->local, + container_of(conf, + struct ieee80211_chanctx, + conf)); + } + } + + memcpy(sched_cfg->schedule, backup_sched.schedule, + sizeof(backup_sched.schedule)); + memcpy(sched_cfg->avail_blob, backup_sched.avail_blob, + sizeof(backup_sched.avail_blob)); + sched_cfg->avail_blob_len = backup_sched.avail_blob_len; + sched_cfg->deferred = false; + bitmap_zero(sdata->u.nan.removed_channels, IEEE80211_NAN_MAX_CHANNELS); + + drv_vif_cfg_changed(sdata->local, sdata, BSS_CHANGED_NAN_LOCAL_SCHED); + return ret; +} + +void ieee80211_nan_sched_update_done(struct ieee80211_vif *vif) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_nan_sched_cfg *sched_cfg = &vif->cfg.nan_sched; + unsigned int i; + + lockdep_assert_wiphy(sdata->local->hw.wiphy); + + if (WARN_ON(!sched_cfg->deferred)) + return; + + /* + * Clear the deferred flag before removing channels. Removing channels + * will trigger another schedule update to the driver, and there is no + * need for this update to be deferred since removed channels are not + * part of the schedule anymore, so no need to notify peers about + * removing them. + */ + sched_cfg->deferred = false; + + for (i = 0; i < ARRAY_SIZE(sched_cfg->channels); i++) { + struct ieee80211_nan_channel *chan = &sched_cfg->channels[i]; + struct ieee80211_chanctx_conf *conf = chan->chanctx_conf; + + if (!chan->chanreq.oper.chan) + continue; + + if (test_bit(i, sdata->u.nan.removed_channels)) + ieee80211_nan_remove_channel(sdata, chan); + else if (conf) + /* + * We might have called this already for some channels, + * but this knows to handle a no-op. + */ + ieee80211_recalc_smps_chanctx(sdata->local, + container_of(conf, + struct ieee80211_chanctx, + conf)); + } + + bitmap_zero(sdata->u.nan.removed_channels, IEEE80211_NAN_MAX_CHANNELS); + cfg80211_nan_sched_update_done(ieee80211_vif_to_wdev(vif), true, + GFP_KERNEL); +} +EXPORT_SYMBOL(ieee80211_nan_sched_update_done); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 36795529ff82..19ac778b704d 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1744,20 +1744,12 @@ static void ieee80211_reconfig_stations(struct ieee80211_sub_if_data *sdata) } } -static int ieee80211_reconfig_nan(struct ieee80211_sub_if_data *sdata) +static int +ieee80211_reconfig_nan_offload_de(struct ieee80211_sub_if_data *sdata) { struct cfg80211_nan_func *func, **funcs; int res, id, i = 0; - res = drv_start_nan(sdata->local, sdata, - &sdata->u.nan.conf); - if (WARN_ON(res)) - return res; - - if (sdata->local->hw.wiphy->nan_capa.flags & - WIPHY_NAN_FLAGS_USERSPACE_DE) - return 0; - funcs = kzalloc_objs(*funcs, sdata->local->hw.max_nan_de_entries + 1); if (!funcs) return -ENOMEM; @@ -1783,6 +1775,22 @@ static int ieee80211_reconfig_nan(struct ieee80211_sub_if_data *sdata) } kfree(funcs); + return res; +} + +static int ieee80211_reconfig_nan(struct ieee80211_sub_if_data *sdata) +{ + int res; + + res = drv_start_nan(sdata->local, sdata, + &sdata->u.nan.conf); + if (WARN_ON(res)) + return res; + + if (!(sdata->local->hw.wiphy->nan_capa.flags & WIPHY_NAN_FLAGS_USERSPACE_DE)) + return ieee80211_reconfig_nan_offload_de(sdata); + + drv_vif_cfg_changed(sdata->local, sdata, BSS_CHANGED_NAN_LOCAL_SCHED); return 0; } From d6c470def51cd9ccc59c882e14ace478022b5a4a Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 26 Mar 2026 12:14:36 +0200 Subject: [PATCH 080/124] wifi: mac80211: support open and close for NAN_DATA interfaces Support opening and closing a NAN_DATA interface. Track the NAN (NMI) interface, for convenience. Allow opening an NAN_DATA interface only if the NAN interface is running (NAN has started). When closing the NAN interface, make sure all NAN_DATA interfaces are closed first, and warn if this is not the case. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260326121156.a19de68119e5.Ia6724dac6a0e17cb69989dd714d14f4df1c69bef@changeid Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 11 +++++++++++ net/mac80211/iface.c | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 92ea8de8a6db..e3a051beba6a 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1006,6 +1006,16 @@ struct ieee80211_if_nan { DECLARE_BITMAP(removed_channels, IEEE80211_NAN_MAX_CHANNELS); }; +/** + * struct ieee80211_if_nan_data - NAN data path state + * + * @nmi: pointer to the NAN management interface sdata. Used for data path, + * hence RCU. + */ +struct ieee80211_if_nan_data { + struct ieee80211_sub_if_data __rcu *nmi; +}; + struct ieee80211_link_data_managed { u8 bssid[ETH_ALEN] __aligned(2); @@ -1204,6 +1214,7 @@ struct ieee80211_sub_if_data { struct ieee80211_if_ocb ocb; struct ieee80211_if_mntr mntr; struct ieee80211_if_nan nan; + struct ieee80211_if_nan_data nan_data; } u; struct ieee80211_link_data deflink; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 0f3e49cdbb39..507c5e016ec8 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -361,6 +361,17 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata, nsdata->vif.type == NL80211_IFTYPE_OCB)) return -EBUSY; + /* + * A NAN DATA interface is correlated to the NAN + * (management) one + */ + if (iftype == NL80211_IFTYPE_NAN_DATA && + nsdata->vif.type == NL80211_IFTYPE_NAN) { + if (!nsdata->u.nan.started) + return -EINVAL; + rcu_assign_pointer(sdata->u.nan_data.nmi, nsdata); + } + /* * Allow only a single IBSS interface to be up at any * time. This is restricted because beacon distribution @@ -475,6 +486,7 @@ static int ieee80211_open(struct net_device *dev) static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_down) { struct ieee80211_local *local = sdata->local; + struct ieee80211_sub_if_data *iter; unsigned long flags; struct sk_buff_head freeq; struct sk_buff *skb, *tmp; @@ -621,6 +633,12 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do } break; case NL80211_IFTYPE_NAN: + /* Check if any open NAN_DATA interfaces */ + list_for_each_entry(iter, &local->interfaces, list) { + WARN_ON(iter->vif.type == NL80211_IFTYPE_NAN_DATA && + ieee80211_sdata_running(iter)); + } + /* clean all the functions */ if (!(local->hw.wiphy->nan_capa.flags & WIPHY_NAN_FLAGS_USERSPACE_DE)) { @@ -636,6 +654,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do spin_unlock_bh(&sdata->u.nan.de.func_lock); } break; + case NL80211_IFTYPE_NAN_DATA: + RCU_INIT_POINTER(sdata->u.nan_data.nmi, NULL); + fallthrough; default: wiphy_work_cancel(sdata->local->hw.wiphy, &sdata->work); /* @@ -1384,9 +1405,12 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) case NL80211_IFTYPE_P2P_DEVICE: case NL80211_IFTYPE_OCB: case NL80211_IFTYPE_NAN: - case NL80211_IFTYPE_NAN_DATA: /* no special treatment */ break; + case NL80211_IFTYPE_NAN_DATA: + if (WARN_ON(!rcu_access_pointer(sdata->u.nan_data.nmi))) + return -ENOLINK; + break; case NL80211_IFTYPE_UNSPECIFIED: case NUM_NL80211_IFTYPES: case NL80211_IFTYPE_P2P_CLIENT: @@ -1404,8 +1428,8 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) res = drv_start(local); if (res) { /* - * no need to worry about AP_VLAN cleanup since in that - * case we can't have open_count == 0 + * no need to worry about AP_VLAN/NAN_DATA cleanup since + * in that case we can't have open_count == 0 */ return res; } @@ -1524,6 +1548,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) case NL80211_IFTYPE_AP: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_OCB: + case NL80211_IFTYPE_NAN_DATA: netif_carrier_off(dev); break; case NL80211_IFTYPE_P2P_DEVICE: @@ -1570,6 +1595,8 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) err_stop: if (!local->open_count) drv_stop(local, false); + if (sdata->vif.type == NL80211_IFTYPE_NAN_DATA) + RCU_INIT_POINTER(sdata->u.nan_data.nmi, NULL); if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) list_del(&sdata->u.vlan.list); /* Might not be initialized yet, but it is harmless */ From e43ec602201c5477508855f9c159956df18d108f Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 26 Mar 2026 12:14:37 +0200 Subject: [PATCH 081/124] wifi: mac80211: handle reconfig for NAN DATA interfaces Make sure these interfaces are added to the driver only after the NAN one was, and after NAN operation was started. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260326121156.b14392ce99d6.I2ba3bfcd93e47e48d7f7c74007c70cca52d46896@changeid Signed-off-by: Johannes Berg --- net/mac80211/util.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 19ac778b704d..925a09246ad9 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1780,10 +1780,11 @@ ieee80211_reconfig_nan_offload_de(struct ieee80211_sub_if_data *sdata) static int ieee80211_reconfig_nan(struct ieee80211_sub_if_data *sdata) { + struct ieee80211_local *local = sdata->local; + struct ieee80211_sub_if_data *ndi_sdata; int res; - res = drv_start_nan(sdata->local, sdata, - &sdata->u.nan.conf); + res = drv_start_nan(local, sdata, &sdata->u.nan.conf); if (WARN_ON(res)) return res; @@ -1792,6 +1793,15 @@ static int ieee80211_reconfig_nan(struct ieee80211_sub_if_data *sdata) drv_vif_cfg_changed(sdata->local, sdata, BSS_CHANGED_NAN_LOCAL_SCHED); + /* Now we can add all the NDIs to the driver */ + list_for_each_entry(ndi_sdata, &local->interfaces, list) { + if (ndi_sdata->vif.type == NL80211_IFTYPE_NAN_DATA) { + res = drv_add_interface(local, ndi_sdata); + if (WARN_ON(res)) + return res; + } + } + return 0; } @@ -1945,6 +1955,9 @@ int ieee80211_reconfig(struct ieee80211_local *local) if (sdata->vif.type == NL80211_IFTYPE_MONITOR && !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) continue; + /* These vifs can't be added before NAN was started */ + if (sdata->vif.type == NL80211_IFTYPE_NAN_DATA) + continue; if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && ieee80211_sdata_running(sdata)) { res = drv_add_interface(local, sdata); @@ -1962,6 +1975,8 @@ int ieee80211_reconfig(struct ieee80211_local *local) if (sdata->vif.type == NL80211_IFTYPE_MONITOR && !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) continue; + if (sdata->vif.type == NL80211_IFTYPE_NAN_DATA) + continue; if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && ieee80211_sdata_running(sdata)) drv_remove_interface(local, sdata); From 27e9b326b67440b559517977e19682461a50da2c Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 26 Mar 2026 12:14:38 +0200 Subject: [PATCH 082/124] wifi: mac80211: support NAN stations Add support for both NMI and NDI stations. The NDI station will be linked to the NMI station of the NAN peer for which the NDI station is added. A peer can choose to reuse its NMI address as the NDI address. Since different keys might be in use for NAN management and for data frames, we will have 2 different stations, even if they'll have the same address. Even though there are no links in NAN, sta->deflink will still be used to store the one set of capabilities and SMPS mode. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260326121156.9fdd37b8e755.I7a7bd6e8e751cab49c329419485839afd209cfc6@changeid Signed-off-by: Johannes Berg --- include/net/mac80211.h | 13 ++++- net/mac80211/cfg.c | 103 ++++++++++++++++++++++++++++++++++++---- net/mac80211/he.c | 7 ++- net/mac80211/ht.c | 19 ++++++-- net/mac80211/iface.c | 10 ++-- net/mac80211/sta_info.c | 21 +++++++- net/mac80211/sta_info.h | 3 +- net/mac80211/util.c | 41 ++++++++++++++++ net/mac80211/vht.c | 16 ++++++- 9 files changed, 209 insertions(+), 24 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index cc23f99f8318..b190d9035182 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2534,11 +2534,15 @@ struct ieee80211_sta_aggregates { * @uhr_cap: UHR capabilities of this STA * @s1g_cap: S1G capabilities of this STA * @agg: per-link data for multi-link aggregation - * @bandwidth: current bandwidth the station can receive with + * @bandwidth: current bandwidth the station can receive with. + * This is the minimum between the peer's capabilities and our own + * operating channel width; Invalid for NAN since that is operating on + * multiple channels. * @rx_nss: in HT/VHT, the maximum number of spatial streams the * station can receive at the moment, changed by operating mode * notifications and capabilities. The value is only valid after - * the station moves to associated state. + * the station moves to associated state. Invalid for NAN since it + * operates on multiple configurations of rx_nss. * @txpwr: the station tx power configuration * */ @@ -2620,6 +2624,7 @@ struct ieee80211_link_sta { * @valid_links: bitmap of valid links, or 0 for non-MLO * @spp_amsdu: indicates whether the STA uses SPP A-MSDU or not. * @epp_peer: indicates that the peer is an EPP peer. + * @nmi: For NDI stations, pointer to the NMI station of the peer. */ struct ieee80211_sta { u8 addr[ETH_ALEN] __aligned(2); @@ -2648,6 +2653,8 @@ struct ieee80211_sta { struct ieee80211_link_sta deflink; struct ieee80211_link_sta __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS]; + struct ieee80211_sta __rcu *nmi; + /* must be last */ u8 drv_priv[] __aligned(sizeof(void *)); }; @@ -2881,6 +2888,8 @@ struct ieee80211_txq { * station has a unique address, i.e. each station entry can be identified * by just its MAC address; this prevents, for example, the same station * from connecting to two virtual AP interfaces at the same time. + * Note that this doesn't apply for NAN, in which the peer's NMI address + * can be equal to its NDI address. * * @IEEE80211_HW_SUPPORTS_REORDERING_BUFFER: Hardware (or driver) manages the * reordering buffer internally, guaranteeing mac80211 receives frames in diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 5bc867a56811..607f3bd3f8f8 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2077,7 +2077,7 @@ static int sta_link_apply_parameters(struct ieee80211_local *local, enum sta_link_apply_mode mode, struct link_station_parameters *params) { - struct ieee80211_supported_band *sband; + struct ieee80211_supported_band *sband = NULL; struct ieee80211_sub_if_data *sdata = sta->sdata; u32 link_id = params->link_id < 0 ? 0 : params->link_id; struct ieee80211_link_data *link = @@ -2085,6 +2085,9 @@ static int sta_link_apply_parameters(struct ieee80211_local *local, struct link_sta_info *link_sta = rcu_dereference_protected(sta->link[link_id], lockdep_is_held(&local->hw.wiphy->mtx)); + const struct ieee80211_sta_ht_cap *own_ht_cap; + const struct ieee80211_sta_vht_cap *own_vht_cap; + const struct ieee80211_sta_he_cap *own_he_cap; bool changes = params->link_mac || params->txpwr_set || params->supported_rates_len || @@ -2114,10 +2117,27 @@ static int sta_link_apply_parameters(struct ieee80211_local *local, if (!link || !link_sta) return -EINVAL; - sband = ieee80211_get_link_sband(link); - if (!sband) + /* + * We should not have any changes in NDI station, its capabilities are + * copied from the NMI sta + */ + if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_NAN_DATA)) return -EINVAL; + if (sdata->vif.type == NL80211_IFTYPE_NAN) { + own_ht_cap = &local->hw.wiphy->nan_capa.phy.ht; + own_vht_cap = &local->hw.wiphy->nan_capa.phy.vht; + own_he_cap = &local->hw.wiphy->nan_capa.phy.he; + } else { + sband = ieee80211_get_link_sband(link); + if (!sband) + return -EINVAL; + + own_ht_cap = &sband->ht_cap; + own_vht_cap = &sband->vht_cap; + own_he_cap = ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); + } + if (params->link_mac) { if (mode == STA_LINK_MODE_NEW) { memcpy(link_sta->addr, params->link_mac, ETH_ALEN); @@ -2139,6 +2159,27 @@ static int sta_link_apply_parameters(struct ieee80211_local *local, return ret; } + if (sdata->vif.type == NL80211_IFTYPE_NAN) { + static const u8 all_ofdm_rates[] = { + 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c + }; + + /* Set the same supported_rates for all bands */ + for (int i = 0; i < NUM_NL80211_BANDS; i++) { + struct ieee80211_supported_band *tmp = + sdata->local->hw.wiphy->bands[i]; + + if ((i != NL80211_BAND_2GHZ && i != NL80211_BAND_5GHZ) || + !tmp) + continue; + + if (!ieee80211_parse_bitrates(tmp, all_ofdm_rates, + sizeof(all_ofdm_rates), + &link_sta->pub->supp_rates[i])) + return -EINVAL; + } + } + if (params->supported_rates && params->supported_rates_len && !ieee80211_parse_bitrates(sband, params->supported_rates, @@ -2147,22 +2188,24 @@ static int sta_link_apply_parameters(struct ieee80211_local *local, return -EINVAL; if (params->ht_capa) - ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, &sband->ht_cap, + ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, own_ht_cap, params->ht_capa, link_sta); /* VHT can override some HT caps such as the A-MSDU max length */ if (params->vht_capa) ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, - &sband->vht_cap, + own_vht_cap, params->vht_capa, NULL, link_sta); if (params->he_capa) - ieee80211_he_cap_ie_to_sta_he_cap(sdata, sband, - (void *)params->he_capa, - params->he_capa_len, - (void *)params->he_6ghz_capa, - link_sta); + _ieee80211_he_cap_ie_to_sta_he_cap(sdata, + own_he_cap, + (void *)params->he_capa, + params->he_capa_len, + (sband && sband->band == NL80211_BAND_6GHZ) ? + (void *)params->he_6ghz_capa : NULL, + link_sta); if (params->he_capa && params->eht_capa) ieee80211_eht_cap_ie_to_sta_eht_cap(sdata, sband, @@ -2349,6 +2392,32 @@ static int sta_apply_parameters(struct ieee80211_local *local, if (params->airtime_weight) sta->airtime_weight = params->airtime_weight; + if (params->nmi_mac) { + struct ieee80211_sub_if_data *nmi = + rcu_dereference_wiphy(local->hw.wiphy, + sdata->u.nan_data.nmi); + struct sta_info *nmi_sta; + + if (WARN_ON(!nmi)) + return -EINVAL; + + nmi_sta = sta_info_get(nmi, params->nmi_mac); + if (!nmi_sta) + return -ENOENT; + rcu_assign_pointer(sta->sta.nmi, &nmi_sta->sta); + + /* For NAN_DATA stations, copy capabilities from the NMI station */ + if (!nmi_sta->deflink.pub->ht_cap.ht_supported) + return -EINVAL; + + sta->deflink.pub->ht_cap = nmi_sta->deflink.pub->ht_cap; + sta->deflink.pub->vht_cap = nmi_sta->deflink.pub->vht_cap; + sta->deflink.pub->he_cap = nmi_sta->deflink.pub->he_cap; + memcpy(&sta->deflink.pub->supp_rates, + &nmi_sta->deflink.pub->supp_rates, + sizeof(sta->deflink.pub->supp_rates)); + } + /* set the STA state after all sta info from usermode has been set */ if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) || set & BIT(NL80211_STA_FLAG_ASSOCIATED)) { @@ -2494,6 +2563,12 @@ static int ieee80211_change_station(struct wiphy *wiphy, else statype = CFG80211_STA_AP_CLIENT_UNASSOC; break; + case NL80211_IFTYPE_NAN: + statype = CFG80211_STA_NAN_MGMT; + break; + case NL80211_IFTYPE_NAN_DATA: + statype = CFG80211_STA_NAN_DATA; + break; default: return -EOPNOTSUPP; } @@ -2532,6 +2607,14 @@ static int ieee80211_change_station(struct wiphy *wiphy, } } + /* NAN capabilties should not change */ + if (statype == CFG80211_STA_NAN_DATA && + sta->deflink.pub->ht_cap.ht_supported && + (params->link_sta_params.ht_capa || + params->link_sta_params.vht_capa || + params->link_sta_params.he_capa)) + return -EINVAL; + err = sta_apply_parameters(local, sta, params); if (err) return err; diff --git a/net/mac80211/he.c b/net/mac80211/he.c index 93e0342cff4f..a3e16a5bec22 100644 --- a/net/mac80211/he.c +++ b/net/mac80211/he.c @@ -127,6 +127,10 @@ _ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata, if (!he_cap_ie || !own_he_cap_ptr || !own_he_cap_ptr->has_he) return; + /* NDI station are using the capabilities from the NMI station */ + if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_NAN_DATA)) + return; + own_he_cap = *own_he_cap_ptr; /* Make sure size is OK */ @@ -156,7 +160,8 @@ _ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata, he_cap->has_he = true; link_sta->cur_max_bandwidth = ieee80211_sta_cap_rx_bw(link_sta); - link_sta->pub->bandwidth = ieee80211_sta_cur_vht_bw(link_sta); + if (sdata->vif.type != NL80211_IFTYPE_NAN) + link_sta->pub->bandwidth = ieee80211_sta_cur_vht_bw(link_sta); if (he_6ghz_capa) ieee80211_update_from_he_6ghz_capa(he_6ghz_capa, link_sta); diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 410e2354f33a..97719298e038 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -154,6 +154,10 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, if (!ht_cap_ie || !own_cap_ptr->ht_supported) goto apply; + /* NDI station are using the capabilities from the NMI station */ + if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_NAN_DATA)) + return 0; + ht_cap.ht_supported = true; own_cap = *own_cap_ptr; @@ -254,10 +258,17 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, rcu_read_lock(); link_conf = rcu_dereference(sdata->vif.link_conf[link_sta->link_id]); - if (WARN_ON(!link_conf)) + if (WARN_ON(!link_conf)) { width = NL80211_CHAN_WIDTH_20_NOHT; - else + } else if (sdata->vif.type == NL80211_IFTYPE_NAN || + sdata->vif.type == NL80211_IFTYPE_NAN_DATA) { + /* In NAN, link_sta->bandwidth is invalid since NAN operates on + * multiple channels. Just take the maximum. + */ + width = NL80211_CHAN_WIDTH_320; + } else { width = link_conf->chanreq.oper.width; + } switch (width) { default: @@ -285,7 +296,9 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; if (sta->sdata->vif.type == NL80211_IFTYPE_AP || - sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { + sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN || + sta->sdata->vif.type == NL80211_IFTYPE_NAN || + sta->sdata->vif.type == NL80211_IFTYPE_NAN_DATA) { enum ieee80211_smps_mode smps_mode; switch ((ht_cap.cap & IEEE80211_HT_CAP_SM_PS) diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 507c5e016ec8..f1ab85ff326d 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -535,12 +535,14 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do * (because if we remove a STA after ops->remove_interface() * the driver will have removed the vif info already!) * - * For AP_VLANs stations may exist since there's nothing else that - * would have removed them, but in other modes there shouldn't - * be any stations. + * For AP_VLANs, NAN and NAN_DATA stations may exist since there's + * nothing else that would have removed them, but in other modes there + * shouldn't be any stations. */ flushed = sta_info_flush(sdata, -1); - WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP_VLAN && flushed > 0); + WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP_VLAN && + sdata->vif.type != NL80211_IFTYPE_NAN && + sdata->vif.type != NL80211_IFTYPE_NAN_DATA && flushed > 0); /* don't count this interface for allmulti while it is down */ if (sdata->flags & IEEE80211_SDATA_ALLMULTI) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 7923ee9eafab..580cd7a68beb 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -795,6 +795,7 @@ struct sta_info *sta_info_alloc_with_link(struct ieee80211_sub_if_data *sdata, static int sta_info_insert_check(struct sta_info *sta) { struct ieee80211_sub_if_data *sdata = sta->sdata; + struct ieee80211_sta *same_addr_sta; lockdep_assert_wiphy(sdata->local->hw.wiphy); @@ -810,13 +811,18 @@ static int sta_info_insert_check(struct sta_info *sta) !is_valid_ether_addr(sta->sta.addr))) return -EINVAL; + if (!ieee80211_hw_check(&sdata->local->hw, NEEDS_UNIQUE_STA_ADDR)) + return 0; + /* The RCU read lock is required by rhashtable due to * asynchronous resize/rehash. We also require the mutex * for correctness. */ rcu_read_lock(); - if (ieee80211_hw_check(&sdata->local->hw, NEEDS_UNIQUE_STA_ADDR) && - ieee80211_find_sta_by_ifaddr(&sdata->local->hw, sta->addr, NULL)) { + same_addr_sta = ieee80211_find_sta_by_ifaddr(&sdata->local->hw, + sta->addr, NULL); + /* For NAN, a peer can re-use */ + if (same_addr_sta && same_addr_sta != rcu_access_pointer(sta->sta.nmi)) { rcu_read_unlock(); return -ENOTUNIQ; } @@ -1294,6 +1300,17 @@ static int __must_check __sta_info_destroy_part1(struct sta_info *sta) lockdep_assert_wiphy(local->hw.wiphy); + if (sdata->vif.type == NL80211_IFTYPE_NAN) { + struct sta_info *sta_iter, *tmp; + + /* Remove all NDI stations associated with this NMI STA */ + list_for_each_entry_safe(sta_iter, tmp, &local->sta_list, list) { + if (rcu_access_pointer(sta_iter->sta.nmi) != &sta->sta) + continue; + sta_info_destroy_addr(sta_iter->sdata, sta_iter->addr); + } + } + /* * Before removing the station from the driver and * rate control, it might still start new aggregation diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 58ccbea7f6f6..3e5d003bd31f 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -505,7 +505,8 @@ struct ieee80211_fragment_cache { * @status_stats.ack_signal_filled: last ACK signal validity * @status_stats.avg_ack_signal: average ACK signal * @cur_max_bandwidth: maximum bandwidth to use for TX to the station, - * taken from HT/VHT capabilities or VHT operating mode notification + * taken from HT/VHT capabilities or VHT operating mode notification. + * Invalid for NAN since that is operating on multiple bands. * @rx_omi_bw_rx: RX OMI bandwidth restriction to apply for RX * @rx_omi_bw_tx: RX OMI bandwidth restriction to apply for TX * @rx_omi_bw_staging: RX OMI bandwidth restriction to apply later diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 925a09246ad9..a352f73a7ec4 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1782,6 +1782,7 @@ static int ieee80211_reconfig_nan(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; struct ieee80211_sub_if_data *ndi_sdata; + struct sta_info *sta; int res; res = drv_start_nan(local, sdata, &sdata->u.nan.conf); @@ -1802,6 +1803,42 @@ static int ieee80211_reconfig_nan(struct ieee80211_sub_if_data *sdata) } } + /* Add NMI stations (stations on the NAN interface) */ + list_for_each_entry(sta, &local->sta_list, list) { + enum ieee80211_sta_state state; + + if (!sta->uploaded || sta->sdata != sdata) + continue; + + for (state = IEEE80211_STA_NOTEXIST; state < sta->sta_state; + state++) { + res = drv_sta_state(local, sdata, sta, state, + state + 1); + if (WARN_ON(res)) + return res; + } + } + + /* Add NDI stations (stations on NAN_DATA interfaces) */ + list_for_each_entry(sta, &local->sta_list, list) { + enum ieee80211_sta_state state; + + if (!sta->uploaded || + sta->sdata->vif.type != NL80211_IFTYPE_NAN_DATA) + continue; + + if (WARN_ON(!sta->sta.nmi)) + continue; + + for (state = IEEE80211_STA_NOTEXIST; state < sta->sta_state; + state++) { + res = drv_sta_state(local, sta->sdata, sta, state, + state + 1); + if (WARN_ON(res)) + return res; + } + } + return 0; } @@ -2060,6 +2097,10 @@ int ieee80211_reconfig(struct ieee80211_local *local) case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_MONITOR: break; + case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_NAN_DATA: + /* NAN stations are handled later */ + break; case NL80211_IFTYPE_ADHOC: if (sdata->vif.cfg.ibss_joined) WARN_ON(drv_join_ibss(local, sdata)); diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index a6570781740a..f3bb5a561a38 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -133,6 +133,10 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, if (!vht_cap_ie || !own_vht_cap->vht_supported) return; + /* NDI station are using the capabilities from the NMI station */ + if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_NAN_DATA)) + return; + if (sband) { /* Allow VHT if at least one channel on the sband supports 80 MHz */ bool have_80mhz = false; @@ -320,7 +324,8 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, IEEE80211_STA_RX_BW_160; } - link_sta->pub->bandwidth = ieee80211_sta_cur_vht_bw(link_sta); + if (sdata->vif.type != NL80211_IFTYPE_NAN) + link_sta->pub->bandwidth = ieee80211_sta_cur_vht_bw(link_sta); /* * Work around the Cisco 9115 FW 17.3 bug by taking the min of @@ -373,6 +378,10 @@ __ieee80211_sta_cap_rx_bw(struct link_sta_info *link_sta, } else { struct ieee80211_bss_conf *link_conf; + if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_NAN_DATA || + sdata->vif.type == NL80211_IFTYPE_NAN)) + return IEEE80211_STA_RX_BW_20; + rcu_read_lock(); link_conf = rcu_dereference(sdata->vif.link_conf[link_id]); band = link_conf->chanreq.oper.chan->band; @@ -518,6 +527,11 @@ _ieee80211_sta_cur_vht_bw(struct link_sta_info *link_sta, } else { struct ieee80211_bss_conf *link_conf; + /* NAN operates on multiple channels so a chandef must be given */ + if (WARN_ON_ONCE(sta->sdata->vif.type == NL80211_IFTYPE_NAN || + sta->sdata->vif.type == NL80211_IFTYPE_NAN_DATA)) + return IEEE80211_STA_RX_BW_20; + rcu_read_lock(); link_conf = rcu_dereference(sta->sdata->vif.link_conf[link_sta->link_id]); if (WARN_ON_ONCE(!link_conf)) { From 840492bf333bf3b69503a573a7ad71147a7ab67e Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 26 Mar 2026 12:14:39 +0200 Subject: [PATCH 083/124] wifi: mac80211: add NAN peer schedule support Peer schedules specify which channels the peer is available on and when. Add support for configuring peer NAN schedules: - build and store the schedule and maps - for each channel, make sure that it fits into the capabilities, and take the minimum between it and the local compatible nan channel. - configure the driver Note that the removal of a peer schedule should be done by the driver upon NMI station removal. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260326121156.185ff2283fa6.I0345eb665be8ccf4a77eb1aca9a421eb8d2432e2@changeid Signed-off-by: Johannes Berg --- include/net/mac80211.h | 62 +++++++++- net/mac80211/cfg.c | 13 +++ net/mac80211/driver-ops.h | 21 ++++ net/mac80211/ieee80211_i.h | 3 + net/mac80211/nan.c | 226 ++++++++++++++++++++++++++++++++++++- net/mac80211/sta_info.c | 4 + net/mac80211/trace.h | 31 +++++ net/mac80211/util.c | 8 ++ 8 files changed, 365 insertions(+), 3 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index b190d9035182..40cb20d9309c 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -877,8 +877,11 @@ struct ieee80211_bss_conf { * is irrelevant for NAN, still store it for convenience - some functions * require it as an argument. * @needed_rx_chains: number of RX chains needed for this NAN channel - * @chanctx_conf: chanctx_conf assigned to this NAN channel. Will be %NULL - * if the channel is ULWed. + * @chanctx_conf: chanctx_conf assigned to this NAN channel. + * If a local channel is being ULWed (because we needed this chanctx for + * something else), the local NAN channel that used this chanctx, + * will have this pointer set to %NULL. + * A peer NAN channel should never have this pointer set to %NULL. * @channel_entry: the Channel Entry blob as defined in Wi-Fi Aware * (TM) 4.0 specification Table 100 (Channel Entry format for the NAN * Availability attribute). @@ -890,6 +893,49 @@ struct ieee80211_nan_channel { u8 channel_entry[6]; }; +/** + * struct ieee80211_nan_peer_map - NAN peer schedule map + * + * This stores a single map from a peer's schedule. Each peer can have + * multiple maps. + * + * @map_id: the map ID from the peer schedule, %CFG80211_NAN_INVALID_MAP_ID + * if unused + * @slots: mapping of time slots to channel configurations in the schedule's + * channels array + */ +struct ieee80211_nan_peer_map { + u8 map_id; + struct ieee80211_nan_channel *slots[CFG80211_NAN_SCHED_NUM_TIME_SLOTS]; +}; + +/** + * struct ieee80211_nan_peer_sched - NAN peer schedule + * + * This stores the complete schedule from a peer. Contains peer-level + * parameters and an array of schedule maps. + * + * @seq_id: the sequence ID from the peer schedule + * @committed_dw: committed DW as published by the peer + * @max_chan_switch: maximum channel switch time in microseconds + * @init_ulw: initial ULWs as published by the peer (copied) + * @ulw_size: number of bytes in @init_ulw + * @maps: array of peer schedule maps. Invalid slots have map_id set to + * %CFG80211_NAN_INVALID_MAP_ID. + * @n_channels: number of valid channel entries in @channels + * @channels: flexible array of negotiated peer channels for this schedule + */ +struct ieee80211_nan_peer_sched { + u8 seq_id; + u16 committed_dw; + u16 max_chan_switch; + const u8 *init_ulw; + u16 ulw_size; + struct ieee80211_nan_peer_map maps[CFG80211_NAN_MAX_PEER_MAPS]; + u8 n_channels; + struct ieee80211_nan_channel channels[] __counted_by(n_channels); +}; + /** * enum mac80211_tx_info_flags - flags to describe transmission information/status * @@ -2625,6 +2671,7 @@ struct ieee80211_link_sta { * @spp_amsdu: indicates whether the STA uses SPP A-MSDU or not. * @epp_peer: indicates that the peer is an EPP peer. * @nmi: For NDI stations, pointer to the NMI station of the peer. + * @nan_sched: NAN peer schedule for this station. Valid only for NMI stations. */ struct ieee80211_sta { u8 addr[ETH_ALEN] __aligned(2); @@ -2655,6 +2702,9 @@ struct ieee80211_sta { struct ieee80211_sta __rcu *nmi; + /* should only be accessed with the wiphy mutex held */ + struct ieee80211_nan_peer_sched *nan_sched; + /* must be last */ u8 drv_priv[] __aligned(sizeof(void *)); }; @@ -4556,6 +4606,12 @@ struct ieee80211_prep_tx_info { * @del_nan_func: Remove a NAN function. The driver must call * ieee80211_nan_func_terminated() with * NL80211_NAN_FUNC_TERM_REASON_USER_REQUEST reason code upon removal. + * @nan_peer_sched_changed: Notifies the driver that the peer NAN schedule + * has changed. The new schedule is available via sta->nan_sched. + * Note that the channel_entry blob might not match the actual chandef + * since the bandwidth of the chandef is the minimum of the local and peer + * bandwidth. It is the driver responsibility to remove the peer schedule + * when the NMI station is removed. * @can_aggregate_in_amsdu: Called in order to determine if HW supports * aggregating two specific frames in the same A-MSDU. The relation * between the skbs should be symmetric and transitive. Note that while @@ -4961,6 +5017,8 @@ struct ieee80211_ops { void (*del_nan_func)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 instance_id); + int (*nan_peer_sched_changed)(struct ieee80211_hw *hw, + struct ieee80211_sta *sta); bool (*can_aggregate_in_amsdu)(struct ieee80211_hw *hw, struct sk_buff *head, struct sk_buff *skb); diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 607f3bd3f8f8..0c1439d31c93 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -5689,6 +5689,18 @@ ieee80211_set_local_nan_sched(struct wiphy *wiphy, return ieee80211_nan_set_local_sched(sdata, sched); } +static int +ieee80211_set_peer_nan_sched(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct cfg80211_nan_peer_sched *sched) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); + + lockdep_assert_wiphy(sdata->local->hw.wiphy); + + return ieee80211_nan_set_peer_sched(sdata, sched); +} + const struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -5806,4 +5818,5 @@ const struct cfg80211_ops mac80211_config_ops = { .assoc_ml_reconf = ieee80211_assoc_ml_reconf, .set_epcs = ieee80211_set_epcs, .nan_set_local_sched = ieee80211_set_local_nan_sched, + .nan_set_peer_sched = ieee80211_set_peer_nan_sched, }; diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 51bf3c7822a7..f1c0b87fddd5 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -1793,4 +1793,25 @@ static inline int drv_set_eml_op_mode(struct ieee80211_sub_if_data *sdata, return ret; } +static inline int +drv_nan_peer_sched_changed(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct sta_info *sta) +{ + int ret; + + might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); + check_sdata_in_driver(sdata); + + if (!local->ops->nan_peer_sched_changed) + return -EOPNOTSUPP; + + trace_drv_nan_peer_sched_changed(local, sdata, &sta->sta); + ret = local->ops->nan_peer_sched_changed(&local->hw, &sta->sta); + trace_drv_return_int(local, ret); + + return ret; +} + #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index e3a051beba6a..23bf00472915 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2042,6 +2042,9 @@ int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata, /* NAN code */ int ieee80211_nan_set_local_sched(struct ieee80211_sub_if_data *sdata, struct cfg80211_nan_local_sched *sched); +int ieee80211_nan_set_peer_sched(struct ieee80211_sub_if_data *sdata, + struct cfg80211_nan_peer_sched *sched); +void ieee80211_nan_free_peer_sched(struct ieee80211_nan_peer_sched *sched); /* scan/BSS handling */ void ieee80211_scan_work(struct wiphy *wiphy, struct wiphy_work *work); diff --git a/net/mac80211/nan.c b/net/mac80211/nan.c index 2fa55e9a9dab..5e1f9bb7c49d 100644 --- a/net/mac80211/nan.c +++ b/net/mac80211/nan.c @@ -1,12 +1,13 @@ // SPDX-License-Identifier: GPL-2.0-only /* * NAN mode implementation - * Copyright(c) 2025 Intel Corporation + * Copyright(c) 2025-2026 Intel Corporation */ #include #include "ieee80211_i.h" #include "driver-ops.h" +#include "sta_info.h" static void ieee80211_nan_init_channel(struct ieee80211_nan_channel *nan_channel, @@ -96,6 +97,82 @@ ieee80211_nan_use_chanctx(struct ieee80211_sub_if_data *sdata, return 0; } +static void +ieee80211_nan_update_peer_channels(struct ieee80211_sub_if_data *sdata, + struct ieee80211_chanctx_conf *removed_conf) +{ + struct ieee80211_local *local = sdata->local; + struct sta_info *sta; + + lockdep_assert_wiphy(local->hw.wiphy); + + list_for_each_entry(sta, &local->sta_list, list) { + struct ieee80211_nan_peer_sched *peer_sched; + int write_idx = 0; + bool updated = false; + + if (sta->sdata != sdata) + continue; + + peer_sched = sta->sta.nan_sched; + if (!peer_sched) + continue; + + /* NULL out map slots for channels being removed */ + for (int i = 0; i < peer_sched->n_channels; i++) { + if (peer_sched->channels[i].chanctx_conf != removed_conf) + continue; + + for (int m = 0; m < CFG80211_NAN_MAX_PEER_MAPS; m++) { + struct ieee80211_nan_peer_map *map = + &peer_sched->maps[m]; + + if (map->map_id == CFG80211_NAN_INVALID_MAP_ID) + continue; + + for (int s = 0; s < ARRAY_SIZE(map->slots); s++) + if (map->slots[s] == &peer_sched->channels[i]) + map->slots[s] = NULL; + } + } + + /* Compact channels array, removing those with removed_conf */ + for (int i = 0; i < peer_sched->n_channels; i++) { + if (peer_sched->channels[i].chanctx_conf == removed_conf) { + updated = true; + continue; + } + + if (write_idx != i) { + /* Update map pointers before moving */ + for (int m = 0; m < CFG80211_NAN_MAX_PEER_MAPS; m++) { + struct ieee80211_nan_peer_map *map = + &peer_sched->maps[m]; + + if (map->map_id == CFG80211_NAN_INVALID_MAP_ID) + continue; + + for (int s = 0; s < ARRAY_SIZE(map->slots); s++) + if (map->slots[s] == &peer_sched->channels[i]) + map->slots[s] = &peer_sched->channels[write_idx]; + } + + peer_sched->channels[write_idx] = peer_sched->channels[i]; + } + write_idx++; + } + + /* Clear any remaining entries at the end */ + for (int i = write_idx; i < peer_sched->n_channels; i++) + memset(&peer_sched->channels[i], 0, sizeof(peer_sched->channels[i])); + + peer_sched->n_channels = write_idx; + + if (updated) + drv_nan_peer_sched_changed(local, sdata, sta); + } +} + static void ieee80211_nan_remove_channel(struct ieee80211_sub_if_data *sdata, struct ieee80211_nan_channel *nan_channel) @@ -118,6 +195,10 @@ ieee80211_nan_remove_channel(struct ieee80211_sub_if_data *sdata, conf = nan_channel->chanctx_conf; + /* If any peer nan schedule uses this chanctx, update them */ + if (conf) + ieee80211_nan_update_peer_channels(sdata, conf); + memset(nan_channel, 0, sizeof(*nan_channel)); /* Update the driver before (possibly) releasing the channel context */ @@ -376,3 +457,146 @@ void ieee80211_nan_sched_update_done(struct ieee80211_vif *vif) GFP_KERNEL); } EXPORT_SYMBOL(ieee80211_nan_sched_update_done); + +void ieee80211_nan_free_peer_sched(struct ieee80211_nan_peer_sched *sched) +{ + if (!sched) + return; + + kfree(sched->init_ulw); + kfree(sched); +} + +static int +ieee80211_nan_init_peer_channel(struct ieee80211_sub_if_data *sdata, + const struct sta_info *sta, + const struct cfg80211_nan_channel *cfg_chan, + struct ieee80211_nan_channel *new_chan) +{ + struct ieee80211_nan_sched_cfg *sched_cfg = &sdata->vif.cfg.nan_sched; + + /* Find compatible local channel */ + for (int j = 0; j < ARRAY_SIZE(sched_cfg->channels); j++) { + struct ieee80211_nan_channel *local_chan = + &sched_cfg->channels[j]; + const struct cfg80211_chan_def *compat; + + if (!local_chan->chanreq.oper.chan) + continue; + + compat = cfg80211_chandef_compatible(&local_chan->chanreq.oper, + &cfg_chan->chandef); + if (!compat) + continue; + + /* compat is the wider chandef, and we want the narrower one */ + new_chan->chanreq.oper = compat == &local_chan->chanreq.oper ? + cfg_chan->chandef : local_chan->chanreq.oper; + new_chan->needed_rx_chains = min(local_chan->needed_rx_chains, + cfg_chan->rx_nss); + new_chan->chanctx_conf = local_chan->chanctx_conf; + + break; + } + + /* + * nl80211 already validated that each peer channel is compatible + * with at least one local channel, so this should never happen. + */ + if (WARN_ON(!new_chan->chanreq.oper.chan)) + return -EINVAL; + + memcpy(new_chan->channel_entry, cfg_chan->channel_entry, + sizeof(new_chan->channel_entry)); + + return 0; +} + +static void +ieee80211_nan_init_peer_map(struct ieee80211_nan_peer_sched *peer_sched, + const struct cfg80211_nan_peer_map *cfg_map, + struct ieee80211_nan_peer_map *new_map) +{ + new_map->map_id = cfg_map->map_id; + + if (new_map->map_id == CFG80211_NAN_INVALID_MAP_ID) + return; + + /* Set up the slots array */ + for (int slot = 0; slot < ARRAY_SIZE(new_map->slots); slot++) { + u8 chan_idx = cfg_map->schedule[slot]; + + if (chan_idx < peer_sched->n_channels) + new_map->slots[slot] = &peer_sched->channels[chan_idx]; + } +} + +int ieee80211_nan_set_peer_sched(struct ieee80211_sub_if_data *sdata, + struct cfg80211_nan_peer_sched *sched) +{ + struct ieee80211_nan_peer_sched *new_sched, *old_sched, *to_free; + struct sta_info *sta; + int ret; + + lockdep_assert_wiphy(sdata->local->hw.wiphy); + + if (!sdata->u.nan.started) + return -EINVAL; + + sta = sta_info_get(sdata, sched->peer_addr); + if (!sta) + return -ENOENT; + + new_sched = kzalloc(struct_size(new_sched, channels, sched->n_channels), + GFP_KERNEL); + if (!new_sched) + return -ENOMEM; + + to_free = new_sched; + + new_sched->seq_id = sched->seq_id; + new_sched->committed_dw = sched->committed_dw; + new_sched->max_chan_switch = sched->max_chan_switch; + new_sched->n_channels = sched->n_channels; + + if (sched->ulw_size && sched->init_ulw) { + new_sched->init_ulw = kmemdup(sched->init_ulw, sched->ulw_size, + GFP_KERNEL); + if (!new_sched->init_ulw) { + ret = -ENOMEM; + goto out; + } + new_sched->ulw_size = sched->ulw_size; + } + + for (int i = 0; i < sched->n_channels; i++) { + ret = ieee80211_nan_init_peer_channel(sdata, sta, + &sched->nan_channels[i], + &new_sched->channels[i]); + if (ret) + goto out; + } + + for (int m = 0; m < ARRAY_SIZE(sched->maps); m++) + ieee80211_nan_init_peer_map(new_sched, &sched->maps[m], + &new_sched->maps[m]); + + /* Install the new schedule before calling the driver */ + old_sched = sta->sta.nan_sched; + sta->sta.nan_sched = new_sched; + + ret = drv_nan_peer_sched_changed(sdata->local, sdata, sta); + if (ret) { + /* Revert to old schedule */ + sta->sta.nan_sched = old_sched; + goto out; + } + + /* Success - free old schedule */ + to_free = old_sched; + ret = 0; + +out: + ieee80211_nan_free_peer_sched(to_free); + return ret; +} diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 580cd7a68beb..fb0cfd4d16d3 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1309,6 +1309,10 @@ static int __must_check __sta_info_destroy_part1(struct sta_info *sta) continue; sta_info_destroy_addr(sta_iter->sdata, sta_iter->addr); } + + /* Free and clear the local peer schedule */ + ieee80211_nan_free_peer_sched(sta->sta.nan_sched); + sta->sta.nan_sched = NULL; } /* diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index e5968d754f8b..71cf88039bd4 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -3366,6 +3366,37 @@ TRACE_EVENT(drv_set_eml_op_mode, ) ); +TRACE_EVENT(drv_nan_peer_sched_changed, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_sta *sta), + + TP_ARGS(local, sdata, sta), + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + STA_ENTRY + __array(u8, map_ids, CFG80211_NAN_MAX_PEER_MAPS) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + STA_ASSIGN; + for (int i = 0; i < CFG80211_NAN_MAX_PEER_MAPS; i++) + __entry->map_ids[i] = sta->nan_sched ? + sta->nan_sched->maps[i].map_id : + 0xff; + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT + " map_ids=[%u, %u]", + LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, + __entry->map_ids[0], __entry->map_ids[1] + ) +); + #endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */ #undef TRACE_INCLUDE_PATH diff --git a/net/mac80211/util.c b/net/mac80211/util.c index a352f73a7ec4..b093bc203c81 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1817,6 +1817,14 @@ static int ieee80211_reconfig_nan(struct ieee80211_sub_if_data *sdata) if (WARN_ON(res)) return res; } + + /* Add peer schedules for NMI stations that have them */ + if (!sta->sta.nan_sched) + continue; + + res = drv_nan_peer_sched_changed(local, sdata, sta); + if (WARN_ON(res)) + return res; } /* Add NDI stations (stations on NAN_DATA interfaces) */ From e1d5c95456a433b8898fff48c17a6150e69e9af9 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 26 Mar 2026 12:14:40 +0200 Subject: [PATCH 084/124] wifi: mac80211: update NAN data path state on schedule changes A carrier of an NDI interface is turned on when there is at least one NDI station that: (1) correlates to this interface (2) is authorized (3) the NAN peer to which this station belongs has at least one common slot with the local schedule. Otherwise, it is turned off. (common slots are slots where both schedules are active on compatible channels.) Implement the calculation of the carrier state and trigger it when needed. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260326121156.98ff4115406f.Ie796487ab9eb23cda819b0afac57e7267b134911@changeid Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 10 +++- net/mac80211/ieee80211_i.h | 1 + net/mac80211/nan.c | 108 +++++++++++++++++++++++++++++++++++++ net/mac80211/sta_info.c | 4 ++ 4 files changed, 122 insertions(+), 1 deletion(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 0c1439d31c93..e47197fe6d1b 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2502,7 +2502,15 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct wireless_dev *wdev, test_sta_flag(sta, WLAN_STA_ASSOC)) rate_control_rate_init_all_links(sta); - return sta_info_insert(sta); + err = sta_info_insert(sta); + + /* + * ieee80211_nan_update_ndi_carrier was called from sta_apply_parameters, + * but then we did not have the STA in the list. + */ + if (!err && sdata->vif.type == NL80211_IFTYPE_NAN_DATA) + ieee80211_nan_update_ndi_carrier(sta->sdata); + return err; } static int ieee80211_del_station(struct wiphy *wiphy, struct wireless_dev *wdev, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 23bf00472915..2a693406294b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2045,6 +2045,7 @@ int ieee80211_nan_set_local_sched(struct ieee80211_sub_if_data *sdata, int ieee80211_nan_set_peer_sched(struct ieee80211_sub_if_data *sdata, struct cfg80211_nan_peer_sched *sched); void ieee80211_nan_free_peer_sched(struct ieee80211_nan_peer_sched *sched); +void ieee80211_nan_update_ndi_carrier(struct ieee80211_sub_if_data *ndi_sdata); /* scan/BSS handling */ void ieee80211_scan_work(struct wiphy *wiphy, struct wiphy_work *work); diff --git a/net/mac80211/nan.c b/net/mac80211/nan.c index 5e1f9bb7c49d..4e262b624521 100644 --- a/net/mac80211/nan.c +++ b/net/mac80211/nan.c @@ -220,6 +220,23 @@ ieee80211_nan_remove_channel(struct ieee80211_sub_if_data *sdata, ieee80211_free_chanctx(sdata->local, ctx, false); } +static void +ieee80211_nan_update_all_ndi_carriers(struct ieee80211_local *local) +{ + struct ieee80211_sub_if_data *sdata; + + lockdep_assert_wiphy(local->hw.wiphy); + + /* Iterate all interfaces and update carrier for NDI interfaces */ + list_for_each_entry(sdata, &local->interfaces, list) { + if (!ieee80211_sdata_running(sdata) || + sdata->vif.type != NL80211_IFTYPE_NAN_DATA) + continue; + + ieee80211_nan_update_ndi_carrier(sdata); + } +} + static struct ieee80211_nan_channel * ieee80211_nan_find_free_channel(struct ieee80211_nan_sched_cfg *sched_cfg) { @@ -347,6 +364,7 @@ int ieee80211_nan_set_local_sched(struct ieee80211_sub_if_data *sdata, if (sched_cfg->deferred) return 0; + ieee80211_nan_update_all_ndi_carriers(sdata->local); bitmap_zero(sdata->u.nan.removed_channels, IEEE80211_NAN_MAX_CHANNELS); return 0; @@ -409,6 +427,7 @@ err: bitmap_zero(sdata->u.nan.removed_channels, IEEE80211_NAN_MAX_CHANNELS); drv_vif_cfg_changed(sdata->local, sdata, BSS_CHANGED_NAN_LOCAL_SCHED); + ieee80211_nan_update_all_ndi_carriers(sdata->local); return ret; } @@ -423,6 +442,8 @@ void ieee80211_nan_sched_update_done(struct ieee80211_vif *vif) if (WARN_ON(!sched_cfg->deferred)) return; + ieee80211_nan_update_all_ndi_carriers(sdata->local); + /* * Clear the deferred flag before removing channels. Removing channels * will trigger another schedule update to the driver, and there is no @@ -531,6 +552,91 @@ ieee80211_nan_init_peer_map(struct ieee80211_nan_peer_sched *peer_sched, } } +/* + * Check if the local schedule and a peer schedule have at least one common + * slot - a slot where both schedules are active on compatible channels. + */ +static bool +ieee80211_nan_has_common_slots(struct ieee80211_sub_if_data *sdata, + struct ieee80211_nan_peer_sched *peer_sched) +{ + for (int slot = 0; slot < CFG80211_NAN_SCHED_NUM_TIME_SLOTS; slot++) { + struct ieee80211_nan_channel *local_chan = + sdata->vif.cfg.nan_sched.schedule[slot]; + + if (!local_chan || !local_chan->chanctx_conf) + continue; + + /* Check all peer maps for this slot */ + for (int m = 0; m < CFG80211_NAN_MAX_PEER_MAPS; m++) { + struct ieee80211_nan_peer_map *map = &peer_sched->maps[m]; + struct ieee80211_nan_channel *peer_chan; + + if (map->map_id == CFG80211_NAN_INVALID_MAP_ID) + continue; + + peer_chan = map->slots[slot]; + if (!peer_chan) + continue; + + if (local_chan->chanctx_conf == peer_chan->chanctx_conf) + return true; + } + } + + return false; +} + +void ieee80211_nan_update_ndi_carrier(struct ieee80211_sub_if_data *ndi_sdata) +{ + struct ieee80211_local *local = ndi_sdata->local; + struct ieee80211_sub_if_data *nmi_sdata; + struct sta_info *sta; + + lockdep_assert_wiphy(local->hw.wiphy); + + if (WARN_ON(ndi_sdata->vif.type != NL80211_IFTYPE_NAN_DATA || + !ndi_sdata->dev) || !ieee80211_sdata_running(ndi_sdata)) + return; + + nmi_sdata = wiphy_dereference(local->hw.wiphy, ndi_sdata->u.nan_data.nmi); + if (WARN_ON(!nmi_sdata)) + return; + + list_for_each_entry(sta, &local->sta_list, list) { + struct ieee80211_sta *nmi_sta; + + if (sta->sdata != ndi_sdata || + !test_sta_flag(sta, WLAN_STA_AUTHORIZED)) + continue; + + nmi_sta = wiphy_dereference(local->hw.wiphy, sta->sta.nmi); + if (WARN_ON(!nmi_sta) || !nmi_sta->nan_sched) + continue; + + if (ieee80211_nan_has_common_slots(nmi_sdata, nmi_sta->nan_sched)) { + netif_carrier_on(ndi_sdata->dev); + return; + } + } + + netif_carrier_off(ndi_sdata->dev); +} + +static void +ieee80211_nan_update_peer_ndis_carrier(struct ieee80211_local *local, + struct sta_info *nmi_sta) +{ + struct sta_info *sta; + + lockdep_assert_wiphy(local->hw.wiphy); + + list_for_each_entry(sta, &local->sta_list, list) { + if (rcu_access_pointer(sta->sta.nmi) == &nmi_sta->sta) + ieee80211_nan_update_ndi_carrier(sta->sdata); + } +} + int ieee80211_nan_set_peer_sched(struct ieee80211_sub_if_data *sdata, struct cfg80211_nan_peer_sched *sched) { @@ -592,6 +698,8 @@ int ieee80211_nan_set_peer_sched(struct ieee80211_sub_if_data *sdata, goto out; } + ieee80211_nan_update_peer_ndis_carrier(sdata->local, sta); + /* Success - free old schedule */ to_free = old_sched; ret = 0; diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index fb0cfd4d16d3..4c31ef8817ce 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1454,6 +1454,8 @@ static int _sta_info_move_state(struct sta_info *sta, } else if (sta->sta_state == IEEE80211_STA_AUTHORIZED) { ieee80211_vif_dec_num_mcast(sta->sdata); clear_bit(WLAN_STA_AUTHORIZED, &sta->_flags); + if (sta->sdata->vif.type == NL80211_IFTYPE_NAN_DATA) + ieee80211_nan_update_ndi_carrier(sta->sdata); /* * If we have encryption offload, flush (station) queues @@ -1482,6 +1484,8 @@ static int _sta_info_move_state(struct sta_info *sta, set_bit(WLAN_STA_AUTHORIZED, &sta->_flags); ieee80211_check_fast_xmit(sta); ieee80211_check_fast_rx(sta); + if (sta->sdata->vif.type == NL80211_IFTYPE_NAN_DATA) + ieee80211_nan_update_ndi_carrier(sta->sdata); } if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN || sta->sdata->vif.type == NL80211_IFTYPE_AP) From b5e4adbd01d1c4f864941162133840a370008e2c Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 26 Mar 2026 12:14:41 +0200 Subject: [PATCH 085/124] wifi: mac80211: add support for TX over NAN_DATA interfaces Add support for TXing frames over NAN_DATA interfaces: - find the NDI station - populoate the addresses fields - use NUM_NL80211_BANDS for the band, similar to NAN interfaces. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260326121156.b1d248947158.I04b27d9727f7a553fa80520cf6e532683ac03690@changeid Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index f0f23e94db04..b487d2330f25 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2543,6 +2543,13 @@ int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata, if (!sta) return -ENOLINK; break; + case NL80211_IFTYPE_NAN_DATA: + if (is_multicast_ether_addr(skb->data)) { + *sta_out = ERR_PTR(-ENOENT); + return 0; + } + sta = sta_info_get(sdata, skb->data); + break; default: return -EINVAL; } @@ -2836,18 +2843,37 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, memcpy(hdr.addr3, sdata->u.ibss.bssid, ETH_ALEN); hdrlen = 24; break; + case NL80211_IFTYPE_NAN_DATA: { + struct ieee80211_sub_if_data *nmi; + + /* DA SA Cluster ID */ + memcpy(hdr.addr1, skb->data, ETH_ALEN); + memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); + nmi = rcu_dereference(sdata->u.nan_data.nmi); + if (!nmi) { + ret = -ENOTCONN; + goto free; + } + memcpy(hdr.addr3, nmi->wdev.u.nan.cluster_id, ETH_ALEN); + hdrlen = 24; + break; + } default: ret = -EINVAL; goto free; } if (!chanctx_conf) { - if (!ieee80211_vif_is_mld(&sdata->vif)) { + if (sdata->vif.type == NL80211_IFTYPE_NAN_DATA) { + /* NAN operates on multiple bands */ + band = NUM_NL80211_BANDS; + } else if (!ieee80211_vif_is_mld(&sdata->vif)) { ret = -ENOTCONN; goto free; + } else { + /* MLD transmissions must not rely on the band */ + band = 0; } - /* MLD transmissions must not rely on the band */ - band = 0; } else { band = chanctx_conf->def.chan->band; } From 61408403e2b4a3e90b2bc6eda9a57837c4fa8ece Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 26 Mar 2026 12:14:42 +0200 Subject: [PATCH 086/124] wifi: mac80211: Accept frames on NAN DATA interfaces Accept frames there were received on NAN DATA interfaces: - Data frames, both multicast or unicast - Non-Public action frames, both multicast or unicast - Unicast secure management frames - FromDS and ToDS are 0. While at it, check FromDS/ToDS also for NAN management frames. Accept only data frames from devices that are part of the NAN cluster. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260326121156.0e6f37d4a40c.Iaa84cc3d063392f0150fcdf2bf610bdb41062f70@changeid Signed-off-by: Johannes Berg --- net/mac80211/rx.c | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index dbdd67c181d8..a00b73420929 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -4469,6 +4469,9 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx) u8 *bssid = ieee80211_get_bssid(hdr, skb->len, sdata->vif.type); bool multicast = is_multicast_ether_addr(hdr->addr1) || ieee80211_is_s1g_beacon(hdr->frame_control); + static const u8 nan_network_id[ETH_ALEN] __aligned(2) = { + 0x51, 0x6F, 0x9A, 0x01, 0x00, 0x00 + }; switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: @@ -4597,6 +4600,10 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx) (ieee80211_is_auth(hdr->frame_control) && ether_addr_equal(sdata->vif.addr, hdr->addr1)); case NL80211_IFTYPE_NAN: + if (ieee80211_has_tods(hdr->frame_control) || + ieee80211_has_fromds(hdr->frame_control)) + return false; + /* Accept only frames that are addressed to the NAN cluster * (based on the Cluster ID). From these frames, accept only * action frames or authentication frames that are addressed to @@ -4608,7 +4615,35 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx) (ieee80211_is_auth(hdr->frame_control) && ether_addr_equal(sdata->vif.addr, hdr->addr1))); case NL80211_IFTYPE_NAN_DATA: - return false; + if (ieee80211_has_tods(hdr->frame_control) || + ieee80211_has_fromds(hdr->frame_control)) + return false; + + if (ieee80211_is_data(hdr->frame_control)) { + struct ieee80211_sub_if_data *nmi; + + nmi = rcu_dereference(sdata->u.nan_data.nmi); + if (!nmi) + return false; + + if (!ether_addr_equal(nmi->wdev.u.nan.cluster_id, + hdr->addr3)) + return false; + + return multicast || + ether_addr_equal(sdata->vif.addr, hdr->addr1); + } + + /* Non-public action frames (unicast or multicast) */ + if (ieee80211_is_action(hdr->frame_control) && + !ieee80211_is_public_action(hdr, skb->len) && + (ether_addr_equal(nan_network_id, hdr->addr1) || + ether_addr_equal(sdata->vif.addr, hdr->addr1))) + return true; + + /* Unicast secure management frames */ + return ether_addr_equal(sdata->vif.addr, hdr->addr1) && + ieee80211_is_unicast_robust_mgmt_frame(skb); default: break; } From 779df4461440b34278d8558c8c977f8ff1d6c18d Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 26 Mar 2026 12:14:43 +0200 Subject: [PATCH 087/124] wifi: mac80211: allow block ack agreements in NAN Data Allow receiving and sending Add Block Ack action frames for NAN Data Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260326121156.fe51df9d65f4.I104435f2af65e032cba168b1d842cb9610720041@changeid Signed-off-by: Johannes Berg --- net/mac80211/agg-tx.c | 3 ++- net/mac80211/rx.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 01d927b88264..4833b46770b6 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -641,7 +641,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, sdata->vif.type != NL80211_IFTYPE_MESH_POINT && sdata->vif.type != NL80211_IFTYPE_AP_VLAN && sdata->vif.type != NL80211_IFTYPE_AP && - sdata->vif.type != NL80211_IFTYPE_ADHOC) + sdata->vif.type != NL80211_IFTYPE_ADHOC && + sdata->vif.type != NL80211_IFTYPE_NAN_DATA) return -EINVAL; if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) { diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index a00b73420929..979ac26d1173 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -3748,7 +3748,8 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) sdata->vif.type != NL80211_IFTYPE_MESH_POINT && sdata->vif.type != NL80211_IFTYPE_AP_VLAN && sdata->vif.type != NL80211_IFTYPE_AP && - sdata->vif.type != NL80211_IFTYPE_ADHOC) + sdata->vif.type != NL80211_IFTYPE_ADHOC && + sdata->vif.type != NL80211_IFTYPE_NAN_DATA) break; /* verify action_code is present */ From 5f6fba9a1e7b03a6369a1078d3921440fff95200 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Thu, 26 Mar 2026 12:14:44 +0200 Subject: [PATCH 088/124] wifi: mac80211: report and drop spurious NAN Data frames According to Wi-Fi Aware (TM) 4.0 specification 6.2.5, in case a frame is recevied from an address that doesn't belong to any active NDP, the frame should be dropped and a NAN Data Path Termination should be sent to the transmitter. Do it by dropping the frame and calling cfg80211_rx_spurious_frame in that case. Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260326121156.721b3a61c580.I19e3572508beeba143871682c80e9a56b6c1046a@changeid Signed-off-by: Johannes Berg --- net/mac80211/rx.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 979ac26d1173..3e5d1c47a5b0 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1589,6 +1589,25 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx) if (ieee80211_vif_is_mesh(&rx->sdata->vif)) return ieee80211_rx_mesh_check(rx); + /* + * Wi-Fi Aware (TM) 4.0 specification 6.2.5: + * For NAN_DATA, unicast data frames must have A2 (source) + * assigned to an active NDP. If not the frame must be dropped + * and NAN Data Path termination frame should be sent. Notify + * user space so it can do so. + */ + if (rx->sdata->vif.type == NL80211_IFTYPE_NAN_DATA) { + if (ieee80211_is_data(hdr->frame_control) && + !is_multicast_ether_addr(hdr->addr1) && + (!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_ASSOC))) { + if (cfg80211_rx_spurious_frame(rx->sdata->dev, hdr->addr2, + rx->link_id, GFP_ATOMIC)) + return RX_DROP_U_SPURIOUS_NOTIF; + return RX_DROP_U_SPURIOUS; + } + return RX_CONTINUE; + } + if (unlikely((ieee80211_is_data(hdr->frame_control) || ieee80211_is_pspoll(hdr->frame_control)) && rx->sdata->vif.type != NL80211_IFTYPE_ADHOC && From 014eec318fc00f0b497bffa04f1a45255ba7ff5e Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Thu, 26 Mar 2026 12:14:45 +0200 Subject: [PATCH 089/124] wifi: mac80211: allow add_key on NAN interfaces Keys may be added to the NAN interfaces to protect NAN management frames and data, allow that. Signed-off-by: Avraham Stern Reviewed-by: Ilan Peer Signed-off-by: Miri Korenblit Link: https://patch.msgid.link/20260326121156.34961ba9a0c4.I5b7c646c456d4112e5ab8663026153ace9b6b7d6@changeid Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index e47197fe6d1b..5a908eaddbf2 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -702,6 +702,8 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct wireless_dev *wdev, break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_NAN_DATA: /* Keys without a station are used for TX only */ if (sta && test_sta_flag(sta, WLAN_STA_MFP)) key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT; @@ -718,13 +720,11 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct wireless_dev *wdev, case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_P2P_DEVICE: - case NL80211_IFTYPE_NAN: case NL80211_IFTYPE_UNSPECIFIED: case NUM_NL80211_IFTYPES: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_OCB: - case NL80211_IFTYPE_NAN_DATA: /* shouldn't happen */ WARN_ON_ONCE(1); break; From dd8592fc6007a451c3e4b9025de365e39de8178a Mon Sep 17 00:00:00 2001 From: Ethan Tidmore Date: Mon, 16 Feb 2026 20:30:43 -0600 Subject: [PATCH 090/124] wifi: brcmfmac: Fix error pointer dereference The function brcmf_chip_add_core() can return an error pointer and is not checked. Add checks for error pointer. Detected by Smatch: drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c:1010 brcmf_chip_recognition() error: 'core' dereferencing possible ERR_PTR() drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c:1013 brcmf_chip_recognition() error: 'core' dereferencing possible ERR_PTR() drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c:1016 brcmf_chip_recognition() error: 'core' dereferencing possible ERR_PTR() drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c:1019 brcmf_chip_recognition() error: 'core' dereferencing possible ERR_PTR() drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c:1022 brcmf_chip_recognition() error: 'core' dereferencing possible ERR_PTR() Fixes: cb7cf7be9eba7 ("brcmfmac: make chip related functions host interface independent") Signed-off-by: Ethan Tidmore Acked-by: Arend van Spriel Link: https://patch.msgid.link/20260217023043.73631-1-ethantidmore06@gmail.com [add missing wifi: prefix] Signed-off-by: Johannes Berg --- .../wireless/broadcom/brcm80211/brcmfmac/chip.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c index a790f1693b82..4adc0d0e4251 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c @@ -1007,18 +1007,33 @@ static int brcmf_chip_recognition(struct brcmf_chip_priv *ci) core = brcmf_chip_add_core(ci, BCMA_CORE_CHIPCOMMON, SI_ENUM_BASE_DEFAULT, 0); + if (IS_ERR(core)) + return PTR_ERR(core); + brcmf_chip_sb_corerev(ci, core); core = brcmf_chip_add_core(ci, BCMA_CORE_SDIO_DEV, BCM4329_CORE_BUS_BASE, 0); + if (IS_ERR(core)) + return PTR_ERR(core); + brcmf_chip_sb_corerev(ci, core); core = brcmf_chip_add_core(ci, BCMA_CORE_INTERNAL_MEM, BCM4329_CORE_SOCRAM_BASE, 0); + if (IS_ERR(core)) + return PTR_ERR(core); + brcmf_chip_sb_corerev(ci, core); core = brcmf_chip_add_core(ci, BCMA_CORE_ARM_CM3, BCM4329_CORE_ARM_BASE, 0); + if (IS_ERR(core)) + return PTR_ERR(core); + brcmf_chip_sb_corerev(ci, core); core = brcmf_chip_add_core(ci, BCMA_CORE_80211, 0x18001000, 0); + if (IS_ERR(core)) + return PTR_ERR(core); + brcmf_chip_sb_corerev(ci, core); } else if (socitype == SOCI_AI) { ci->iscoreup = brcmf_chip_ai_iscoreup; From 084863593243c5dce0f2eef44e23de8c53ebf4a2 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 19 Feb 2026 18:27:39 -0800 Subject: [PATCH 091/124] wifi: brcmfmac: of: defer probe for MAC address of_get_mac_address can return EPROBE_DEFER if the specific nvmem driver has not been loaded yet. Signed-off-by: Rosen Penev Acked-by: Arend van Spriel Link: https://patch.msgid.link/20260220022739.41755-1-rosenp@gmail.com Signed-off-by: Johannes Berg --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c index 1681ad00f82e..03efae36a0b2 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c @@ -128,7 +128,9 @@ int brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type, if (err) brcmf_err("failed to get OF country code map (err=%d)\n", err); - of_get_mac_address(np, settings->mac); + err = of_get_mac_address(np, settings->mac); + if (err == -EPROBE_DEFER) + return err; if (bus_type != BRCMF_BUSTYPE_SDIO) return 0; From 49152949deeabd24db87f284f993ab89d4b8ac38 Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Thu, 26 Mar 2026 12:31:58 +0000 Subject: [PATCH 092/124] wifi: iwlegacy: Fixup allocation failure log Fix 2 issues spotted by AI[0]: 1. Missing space after the full stop. 2. Wrong GFP flags are printed. And also switch to %pGg for the GFP flags. This produces nice readable output and decouples the format string from the size of gfp_t. [0] https://sashiko.dev/#/patchset/20260319-gfp64-v1-0-2c73b8d42b7f%40google.com Signed-off-by: Brendan Jackman Acked-by: Stanislaw Gruszka Link: https://patch.msgid.link/20260326-gfp64-v2-2-d916021cecdf@google.com [add missing wifi: prefix] Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlegacy/3945-mac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlegacy/3945-mac.c b/drivers/net/wireless/intel/iwlegacy/3945-mac.c index c148654aa953..88b31e0b9568 100644 --- a/drivers/net/wireless/intel/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/intel/iwlegacy/3945-mac.c @@ -1002,9 +1002,9 @@ il3945_rx_allocate(struct il_priv *il, gfp_t priority) D_INFO("Failed to allocate SKB buffer.\n"); if (rxq->free_count <= RX_LOW_WATERMARK && net_ratelimit()) - IL_ERR("Failed to allocate SKB buffer with %0x." + IL_ERR("Failed to allocate SKB buffer with %pGg. " "Only %u free buffers remaining.\n", - priority, rxq->free_count); + &gfp_mask, rxq->free_count); /* We don't reschedule replenish work here -- we will * call the restock method and if it still needs * more buffers it will schedule replenish */ From d278bf868604af9895f4b8b4f7f4bb82e317740c Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 26 Mar 2026 20:06:16 -0700 Subject: [PATCH 093/124] wifi: wilc1000: use kzalloc_flex Because key is a flexible array member, kzalloc_flex can be used to handle the math properly and simplify the code slightly. Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260327030616.8774-1-rosenp@gmail.com Signed-off-by: Johannes Berg --- drivers/net/wireless/microchip/wilc1000/hif.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/microchip/wilc1000/hif.c b/drivers/net/wireless/microchip/wilc1000/hif.c index 944b2a812b63..009c4770a6f9 100644 --- a/drivers/net/wireless/microchip/wilc1000/hif.c +++ b/drivers/net/wireless/microchip/wilc1000/hif.c @@ -1123,7 +1123,7 @@ int wilc_add_ptk(struct wilc_vif *vif, const u8 *ptk, u8 ptk_key_len, wid_list[0].size = sizeof(char); wid_list[0].val = (s8 *)&cipher_mode; - key_buf = kzalloc(sizeof(*key_buf) + t_key_len, GFP_KERNEL); + key_buf = kzalloc_flex(*key_buf, key, t_key_len); if (!key_buf) return -ENOMEM; @@ -1151,7 +1151,7 @@ int wilc_add_ptk(struct wilc_vif *vif, const u8 *ptk, u8 ptk_key_len, struct wid wid; struct wilc_sta_wpa_ptk *key_buf; - key_buf = kzalloc(sizeof(*key_buf) + t_key_len, GFP_KERNEL); + key_buf = kzalloc_flex(*key_buf, key, t_key_len); if (!key_buf) return -ENOMEM; @@ -1186,7 +1186,7 @@ int wilc_add_igtk(struct wilc_vif *vif, const u8 *igtk, u8 igtk_key_len, struct wid wid; struct wilc_wpa_igtk *key_buf; - key_buf = kzalloc(sizeof(*key_buf) + t_key_len, GFP_KERNEL); + key_buf = kzalloc_flex(*key_buf, key, t_key_len); if (!key_buf) return -ENOMEM; @@ -1217,7 +1217,7 @@ int wilc_add_rx_gtk(struct wilc_vif *vif, const u8 *rx_gtk, u8 gtk_key_len, struct wilc_gtk_key *gtk_key; int t_key_len = gtk_key_len + WILC_RX_MIC_KEY_LEN + WILC_TX_MIC_KEY_LEN; - gtk_key = kzalloc(sizeof(*gtk_key) + t_key_len, GFP_KERNEL); + gtk_key = kzalloc_flex(*gtk_key, key, t_key_len); if (!gtk_key) return -ENOMEM; From 72b18625ba8e5c28acbc923822a8b9ca7c5b3931 Mon Sep 17 00:00:00 2001 From: Ronald Claveau Date: Fri, 27 Mar 2026 10:36:26 +0100 Subject: [PATCH 094/124] dt-bindings: net: wireless: brcm: Add compatible for bcm43752 Add bcm43752 compatible with its bcm4329 compatible fallback. Acked-by: Conor Dooley Signed-off-by: Ronald Claveau Acked-by: Arend van Spriel Link: https://patch.msgid.link/20260327-add-bcm43752-compatible-v2-1-5b28e6637101@aliel.fr Signed-off-by: Johannes Berg --- .../devicetree/bindings/net/wireless/brcm,bcm4329-fmac.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/net/wireless/brcm,bcm4329-fmac.yaml b/Documentation/devicetree/bindings/net/wireless/brcm,bcm4329-fmac.yaml index 3be757678764..81fd3e37452a 100644 --- a/Documentation/devicetree/bindings/net/wireless/brcm,bcm4329-fmac.yaml +++ b/Documentation/devicetree/bindings/net/wireless/brcm,bcm4329-fmac.yaml @@ -42,6 +42,7 @@ properties: - brcm,bcm4356-fmac - brcm,bcm4359-fmac - brcm,bcm4366-fmac + - brcm,bcm43752-fmac - cypress,cyw4373-fmac - cypress,cyw43012-fmac - infineon,cyw43439-fmac From 1c161ca67e9bbd39b5c2adc8e067affcab10e8a5 Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Fri, 27 Mar 2026 12:30:07 +0000 Subject: [PATCH 095/124] wifi: iwlegacy: Fix GFP flags in allocation loop Do not latch these flags, they should be re-evaluated for each iteration of the loop. Concretely, rxq->free_count is incremented during the loop so the __GFP_NOWARN decision may be stale. There may be other reasons to require the re-evaluation too. Suggested-by: Stanislaw Gruszka Link: https://lore.kernel.org/all/20260327115739.GB16800@wp.pl/ Signed-off-by: Brendan Jackman Acked-by: Stanislaw Gruszka Link: https://patch.msgid.link/20260327-iwlegacy-gfp-fix-v1-1-b83e4db0bd66@google.com Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlegacy/3945-mac.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlegacy/3945-mac.c b/drivers/net/wireless/intel/iwlegacy/3945-mac.c index 88b31e0b9568..cbaf250626c5 100644 --- a/drivers/net/wireless/intel/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/intel/iwlegacy/3945-mac.c @@ -979,9 +979,10 @@ il3945_rx_allocate(struct il_priv *il, gfp_t priority) struct page *page; dma_addr_t page_dma; unsigned long flags; - gfp_t gfp_mask = priority; while (1) { + gfp_t gfp_mask = priority; + spin_lock_irqsave(&rxq->lock, flags); if (list_empty(&rxq->rx_used)) { spin_unlock_irqrestore(&rxq->lock, flags); From 368f5098ed0b2eb5d06dbbe692c163c85f240a4d Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Sat, 28 Mar 2026 15:01:21 +0100 Subject: [PATCH 096/124] wifi: brcmfmac: silence warning for non-existent, optional firmware The driver tries to load optional firmware files, specific to the actual board compatible. These might not exist resulting in a warning like this: brcmfmac mmc2:0001:1: Direct firmware load for brcm/brcmfmac4373-sdio.tq,imx93-tqma9352-mba93xxla-mini.bin failed with error -2 Silence this by using firmware_request_nowait_nowarn() for all firmware loads which use brcmf_fw_request_done_alt_path() as callback. This one handles optional firmware files. Signed-off-by: Alexander Stein Tested-by: Christian Hewitt [arend: use nowarn api for optional firmware files] Signed-off-by: Arend van Spriel Link: https://patch.msgid.link/20260328140121.2583606-1-arend.vanspriel@broadcom.com [clean up code a bit] Signed-off-by: Johannes Berg --- .../broadcom/brcm80211/brcmfmac/firmware.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c index 4bacd83db052..22ff326f1924 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c @@ -670,6 +670,9 @@ static int brcmf_fw_request_firmware(const struct firmware **fw, } fallback: + if (cur->flags & BRCMF_FW_REQF_OPTIONAL) + return firmware_request_nowarn(fw, cur->path, fwctx->dev); + return request_firmware(fw, cur->path, fwctx->dev); } @@ -714,9 +717,10 @@ static void brcmf_fw_request_done_alt_path(const struct firmware *fw, void *ctx) if (!alt_path) goto fallback; - ret = request_firmware_nowait(THIS_MODULE, true, alt_path, - fwctx->dev, GFP_KERNEL, fwctx, - brcmf_fw_request_done_alt_path); + ret = firmware_request_nowait_nowarn(THIS_MODULE, + alt_path, fwctx->dev, + GFP_KERNEL, fwctx, + brcmf_fw_request_done_alt_path); kfree(alt_path); if (ret < 0) @@ -779,9 +783,10 @@ int brcmf_fw_get_firmwares(struct device *dev, struct brcmf_fw_request *req, fwctx->req->board_types[0]); if (alt_path) { fwctx->board_index++; - ret = request_firmware_nowait(THIS_MODULE, true, alt_path, - fwctx->dev, GFP_KERNEL, fwctx, - brcmf_fw_request_done_alt_path); + ret = firmware_request_nowait_nowarn(THIS_MODULE, + alt_path, fwctx->dev, + GFP_KERNEL, fwctx, + brcmf_fw_request_done_alt_path); kfree(alt_path); } else { ret = request_firmware_nowait(THIS_MODULE, true, first->path, From c4ed2c3f4f5e34156e607f2ea92cefad638af50e Mon Sep 17 00:00:00 2001 From: Jiajia Liu Date: Tue, 7 Apr 2026 14:32:05 +0800 Subject: [PATCH 097/124] wifi: mac80211: remove unused variables in minstrel_ht_alloc_sta Remove the unused variable max_rates and related code. Also remove the variable mi and pass type to kzalloc_obj instead. Signed-off-by: Jiajia Liu Link: https://patch.msgid.link/20260407063205.68471-1-liujiajia@kylinos.cn Signed-off-by: Johannes Berg --- net/mac80211/rc80211_minstrel_ht.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index 62745ca00e06..b73ef3adfcc5 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -1849,20 +1849,7 @@ minstrel_ht_rate_update(void *priv, struct ieee80211_supported_band *sband, static void * minstrel_ht_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp) { - struct ieee80211_supported_band *sband; - struct minstrel_ht_sta *mi; - struct minstrel_priv *mp = priv; - struct ieee80211_hw *hw = mp->hw; - int max_rates = 0; - int i; - - for (i = 0; i < NUM_NL80211_BANDS; i++) { - sband = hw->wiphy->bands[i]; - if (sband && sband->n_bitrates > max_rates) - max_rates = sband->n_bitrates; - } - - return kzalloc_obj(*mi, gfp); + return kzalloc_obj(struct minstrel_ht_sta, gfp); } static void From b5b8e295973083abf823fb66647a7c702a8db8a7 Mon Sep 17 00:00:00 2001 From: Nicolas Escande Date: Fri, 27 Mar 2026 11:02:56 +0100 Subject: [PATCH 098/124] wifi: mac80211: handle VHT EXT NSS in ieee80211_determine_our_sta_mode() A station which has a NSS ratio on the number of streams it is capable of in 160MHz VHT operation is supposed to use the 'Extended NSS BW Support' as defined by section '9.4.2.156.2 VHT Capabilities Information field'. This was missing in ieee80211_determine_our_sta_mode() and so we would wrongfully downgrade our bandwidth when connecting to an AP that supported 160MHz with messages such as: [ 37.638346] wlan1: AP XX:XX:XX:XX:XX:XX changed bandwidth in assoc response, new used config is 5280.000 MHz, width 3 (5290.000/0 MHz) Fixes: 310c8387c638 ("wifi: mac80211: clean up connection process") Signed-off-by: Nicolas Escande Link: https://patch.msgid.link/20260327100256.3101348-1-nico.escande@gmail.com Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 7fc5616cb244..285caaa2997e 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -6085,7 +6085,8 @@ ieee80211_determine_our_sta_mode(struct ieee80211_sub_if_data *sdata, if (is_5ghz && !(vht_cap.cap & (IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ | - IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) { + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ | + IEEE80211_VHT_CAP_EXT_NSS_BW_MASK))) { conn->bw_limit = IEEE80211_CONN_BW_LIMIT_80; mlme_link_id_dbg(sdata, link_id, "no VHT 160 MHz capability on 5 GHz, limiting to 80 MHz"); From 469d5d5a3b7a133837004a18f34c899625fd5941 Mon Sep 17 00:00:00 2001 From: Tamizh Chelvam Raja Date: Thu, 26 Mar 2026 22:17:21 +0530 Subject: [PATCH 099/124] wifi: mac80211: synchronize valid links for WDS AP_VLAN interfaces The current code does not provide any link-configuration support for 4-address mode WDS AP_VLAN interfaces in MLO setups, preventing MLD stations from being added correctly. Add the required handling to enable proper integration of 4-address WDS stations into an MLO environment. When a 4-address station associates with an MLO AP, compute the intersection of valid links between the master AP interface and the station's advertised capabilities. Configure the AP_VLAN interface with only these common links to ensure correct data-path operation. This update ensures AP_VLAN interfaces correctly track link-state transitions and maintain consistent addressing across all active MLO links. Co-developed-by: Muna Sinada Signed-off-by: Muna Sinada Signed-off-by: Tamizh Chelvam Raja Link: https://patch.msgid.link/20260326164723.553927-2-tamizh.raja@oss.qualcomm.com Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 67 +++++++++++++++++++++++++++++++++++++++++---- net/mac80211/chan.c | 8 ++++++ net/mac80211/link.c | 45 ++++++++++++++++++++---------- 3 files changed, 101 insertions(+), 19 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 5a908eaddbf2..6a5bf403fd85 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2527,6 +2527,65 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct wireless_dev *wdev, return 0; } +static int ieee80211_set_sta_4addr(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct sta_info *sta) +{ + struct ieee80211_vif *vif = &sdata->vif; + struct wiphy *wiphy = local->hw.wiphy; + struct ieee80211_sub_if_data *master; + struct ieee80211_bss_conf *link_conf; + struct wireless_dev *wdev; + unsigned long master_iter; + int link_id; + int err; + + lockdep_assert_wiphy(local->hw.wiphy); + + if (sdata->u.vlan.sta) + return -EBUSY; + + wdev = &sdata->wdev; + master = container_of(sdata->bss, + struct ieee80211_sub_if_data, + u.ap); + + if (sta->sta.valid_links) { + u16 sta_links = sta->sta.valid_links; + u16 new_links = master->vif.valid_links & sta_links; + u16 orig_links = wdev->valid_links; + + wdev->valid_links = new_links; + + err = ieee80211_vif_set_links(sdata, new_links, 0); + if (err) { + wdev->valid_links = orig_links; + return err; + } + + master_iter = master->vif.valid_links; + + for_each_set_bit(link_id, &master_iter, + IEEE80211_MLD_MAX_NUM_LINKS) { + if (!(sta_links & BIT(link_id))) { + eth_zero_addr(wdev->links[link_id].addr); + } else { + link_conf = wiphy_dereference(wiphy, + vif->link_conf[link_id]); + + ether_addr_copy(wdev->links[link_id].addr, + link_conf->bssid); + } + } + } + + rcu_assign_pointer(sdata->u.vlan.sta, sta); + __ieee80211_check_fast_rx_iface(sdata); + drv_sta_set_4addr(local, sta->sdata, &sta->sta, true); + + return 0; +} + static int ieee80211_change_station(struct wiphy *wiphy, struct wireless_dev *wdev, const u8 *mac, struct station_parameters *params) @@ -2589,12 +2648,10 @@ static int ieee80211_change_station(struct wiphy *wiphy, vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); if (params->vlan->ieee80211_ptr->use_4addr) { - if (vlansdata->u.vlan.sta) - return -EBUSY; + err = ieee80211_set_sta_4addr(local, vlansdata, sta); + if (err) + return err; - rcu_assign_pointer(vlansdata->u.vlan.sta, sta); - __ieee80211_check_fast_rx_iface(vlansdata); - drv_sta_set_4addr(local, sta->sdata, &sta->sta, true); } if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 50a06de928ba..fda692316f08 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -1321,6 +1321,10 @@ __ieee80211_link_copy_chanctx_to_vlans(struct ieee80211_link_data *link, list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) { struct ieee80211_bss_conf *vlan_conf; + if (vlan->vif.valid_links && + !(vlan->vif.valid_links & BIT(link_id))) + continue; + vlan_conf = wiphy_dereference(local->hw.wiphy, vlan->vif.link_conf[link_id]); if (WARN_ON(!vlan_conf)) @@ -1564,6 +1568,10 @@ ieee80211_link_update_chanreq(struct ieee80211_link_data *link, list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) { struct ieee80211_bss_conf *vlan_conf; + if (vlan->vif.valid_links && + !(vlan->vif.valid_links & BIT(link_id))) + continue; + vlan_conf = wiphy_dereference(sdata->local->hw.wiphy, vlan->vif.link_conf[link_id]); if (WARN_ON(!vlan_conf)) diff --git a/net/mac80211/link.c b/net/mac80211/link.c index 03bfca27d205..93e290dd783f 100644 --- a/net/mac80211/link.c +++ b/net/mac80211/link.c @@ -14,27 +14,39 @@ static void ieee80211_update_apvlan_links(struct ieee80211_sub_if_data *sdata) { + unsigned long rem = ~sdata->vif.valid_links & + GENMASK(IEEE80211_MLD_MAX_NUM_LINKS - 1, 0); + struct ieee80211_local *local = sdata->local; + unsigned long add = sdata->vif.valid_links; + struct wiphy *wiphy = local->hw.wiphy; struct ieee80211_sub_if_data *vlan; struct ieee80211_link_data *link; - u16 ap_bss_links = sdata->vif.valid_links; - u16 new_links, vlan_links; - unsigned long add; + struct sta_info *sta; list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) { int link_id; - /* No support for 4addr with MLO yet */ - if (vlan->wdev.use_4addr) - return; + if (vlan->wdev.use_4addr) { + sta = wiphy_dereference(wiphy, + vlan->u.vlan.sta); + if (sta) + add = add & sta->sta.valid_links; + } - vlan_links = vlan->vif.valid_links; - - new_links = ap_bss_links; - - add = new_links & ~vlan_links; - if (!add) + if (add == vlan->vif.valid_links) continue; + for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) { + vlan->wdev.valid_links |= BIT(link_id); + ether_addr_copy(vlan->wdev.links[link_id].addr, + sdata->wdev.links[link_id].addr); + } + + for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) { + vlan->wdev.valid_links &= ~BIT(link_id); + eth_zero_addr(vlan->wdev.links[link_id].addr); + } + ieee80211_vif_set_links(vlan, add, 0); for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) { @@ -96,8 +108,13 @@ void ieee80211_link_init(struct ieee80211_sub_if_data *sdata, ap_bss = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); - ap_bss_conf = sdata_dereference(ap_bss->vif.link_conf[link_id], - ap_bss); + + if (deflink) + ap_bss_conf = &ap_bss->vif.bss_conf; + else + ap_bss_conf = sdata_dereference(ap_bss->vif.link_conf[link_id], + ap_bss); + memcpy(link_conf, ap_bss_conf, sizeof(*link_conf)); } From 594be50a3f0a6b7389f40f7acbf0dd731beb5204 Mon Sep 17 00:00:00 2001 From: Tamizh Chelvam Raja Date: Thu, 26 Mar 2026 22:17:22 +0530 Subject: [PATCH 100/124] wifi: mac80211: use ap_addr for 4-address NULL frame destination Currently ieee80211_send_4addr_nullfunc() uses deflink.u.mgd.bssid for addr1 and addr3 fields. In MLO configurations, deflink.u.mgd.bssid represents link 0's BSSID and is not updated when link 0 is not an assoc link. This causes 4-address NULL frames to be sent to the wrong address, preventing WDS AP_VLAN interface creation on the peer AP. To fix this use sdata->vif.cfg.ap_addr instead, which contains the AP's MLD address populated during authentication/association and remains valid regardless of which links are active. This ensures 4-address NULL frames reach the correct AP, allowing proper WDS operation over MLO connections. Co-developed-by: Sathishkumar Muruganandam Signed-off-by: Sathishkumar Muruganandam Signed-off-by: Tamizh Chelvam Raja Link: https://patch.msgid.link/20260326164723.553927-3-tamizh.raja@oss.qualcomm.com Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 285caaa2997e..4a34547b39d5 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2514,9 +2514,9 @@ void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local, fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC | IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); nullfunc->frame_control = fc; - memcpy(nullfunc->addr1, sdata->deflink.u.mgd.bssid, ETH_ALEN); + memcpy(nullfunc->addr1, sdata->vif.cfg.ap_addr, ETH_ALEN); memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN); - memcpy(nullfunc->addr3, sdata->deflink.u.mgd.bssid, ETH_ALEN); + memcpy(nullfunc->addr3, sdata->vif.cfg.ap_addr, ETH_ALEN); memcpy(nullfunc->addr4, sdata->vif.addr, ETH_ALEN); IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; From 915c1d23e2e3a94f432bda6fb64f47c06f840ca1 Mon Sep 17 00:00:00 2001 From: Tamizh Chelvam Raja Date: Thu, 26 Mar 2026 22:17:23 +0530 Subject: [PATCH 101/124] wifi: mac80211: enable MLO support for 4-address mode interfaces The current code does not support establishing MLO connections for interfaces operating in 4-address AP_VLAN mode. MLO bringup is blocked by sanity checks in cfg.c, iface.c, and mlme.c, which prevent MLD initialization when use_4addr is enabled. Remove these restrictions to allow 4-address AP_VLAN interfaces to initialize as part of an MLD and successfully participate in MLO connections. This patch series also adds the necessary changes to support WDS operation in MLO, making these modifications valid. Allow 4-address mode interfaces to: - Proceed with MLD initialization during interface setup - Add MLO links dynamically via ieee80211_add_intf_link() - Establish associations with MLO-capable access points - Support AP_VLAN interfaces with MLO parent APs Signed-off-by: Tamizh Chelvam Raja Link: https://patch.msgid.link/20260326164723.553927-4-tamizh.raja@oss.qualcomm.com Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 7 ------- net/mac80211/iface.c | 7 ------- net/mac80211/mlme.c | 4 ---- 3 files changed, 18 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 6a5bf403fd85..7b77d57c9f96 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -281,10 +281,6 @@ static int ieee80211_change_iface(struct wiphy *wiphy, if (params->use_4addr == ifmgd->use_4addr) return 0; - /* FIXME: no support for 4-addr MLO yet */ - if (ieee80211_vif_is_mld(&sdata->vif)) - return -EOPNOTSUPP; - sdata->u.mgd.use_4addr = params->use_4addr; if (!ifmgd->associated) return 0; @@ -5585,9 +5581,6 @@ static int ieee80211_add_intf_link(struct wiphy *wiphy, lockdep_assert_wiphy(sdata->local->hw.wiphy); - if (wdev->use_4addr) - return -EOPNOTSUPP; - return ieee80211_vif_set_links(sdata, wdev->valid_links, 0); } diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index f1ab85ff326d..95b779c4d627 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -409,13 +409,6 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata, nsdata->vif.type)) return -ENOTUNIQ; - /* No support for VLAN with MLO yet */ - if (iftype == NL80211_IFTYPE_AP_VLAN && - sdata->wdev.use_4addr && - nsdata->vif.type == NL80211_IFTYPE_AP && - nsdata->vif.valid_links) - return -EOPNOTSUPP; - /* * can only add VLANs to enabled APs */ diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 4a34547b39d5..160ae65a5c64 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -9859,10 +9859,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) size += req->links[i].elems_len; - /* FIXME: no support for 4-addr MLO yet */ - if (sdata->u.mgd.use_4addr && req->link_id >= 0) - return -EOPNOTSUPP; - assoc_data = kzalloc(size, GFP_KERNEL); if (!assoc_data) return -ENOMEM; From ba563287beaa99c18144b2e39f63b89412abfd18 Mon Sep 17 00:00:00 2001 From: Roopni Devanathan Date: Tue, 31 Mar 2026 10:28:34 +0530 Subject: [PATCH 102/124] wifi: ath12k: Rename hw_link_id to radio_idx in ath12k_ah_to_ar() ath12k_ah_to_ar() is returning radio from the given hardware based on the radio index passed. But, the variable that radio index is received at is wrongly named 'hw_link_id', which points to the hardware link index that comes from the firmware. This affects readability. Resolve this by renaming 'hw_link_id' to 'radio_idx'. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ-1 Signed-off-by: Roopni Devanathan Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260331045834.1181924-1-roopni.devanathan@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/core.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 59c193b24764..5eff86827e9c 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -1366,13 +1366,13 @@ static inline struct ath12k_hw *ath12k_hw_to_ah(struct ieee80211_hw *hw) return hw->priv; } -static inline struct ath12k *ath12k_ah_to_ar(struct ath12k_hw *ah, u8 hw_link_id) +static inline struct ath12k *ath12k_ah_to_ar(struct ath12k_hw *ah, u8 radio_idx) { - if (WARN(hw_link_id >= ah->num_radio, - "bad hw link id %d, so switch to default link\n", hw_link_id)) - hw_link_id = 0; + if (WARN(radio_idx >= ah->num_radio, + "bad radio index %d, use default radio\n", radio_idx)) + radio_idx = 0; - return &ah->radio[hw_link_id]; + return &ah->radio[radio_idx]; } static inline struct ath12k_hw *ath12k_ar_to_ah(struct ath12k *ar) From af5708ed67fc562bc45fafbd0f95789c464c0105 Mon Sep 17 00:00:00 2001 From: Harish Rachakonda Date: Thu, 26 Mar 2026 10:36:41 +0530 Subject: [PATCH 103/124] wifi: ath12k: Support channel change stats Add support to request channel change stats from the firmware through HTT stats type 76. These stats give channel switch details like the channel that the radio changed to, its center frequency, time taken for the switch, chainmask details, etc. Sample output: echo 76 > /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/htt_stats_type cat /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/htt_stats Channel Change Timings: |PRIMARY CHANNEL FREQ|BANDWIDTH CENTER FREQ|PHYMODE|TX_CHAINMASK|RX_CHAINMASK|SWITCH TIME(us)|INI(us)|TPC+CTL(us)|CAL(us)|MISC(us)|CTL(us)|SW PROFILE| | 5200| 5200| 24| 15| 15| 448850| 2410| 10546| 434593| 1071| 1100| 4| | 5240| 5240| 24| 15| 15| 450730| 4106| 10524| 434528| 1306| 1150| 4| | 5180| 5210| 26| 15| 15| 467894| 4764| 10438| 451101| 1337| 1508| 4| | 5200| 5200| 0| 15| 15| 13838| 2692| 1736| 8558| 686| 802| 6| | 5180| 5180| 0| 15| 15| 13465| 3207| 855| 8579| 578| 760| 6| | 5200| 5200| 24| 15| 15| 570321| 2441| 10439| 555661| 1574| 949| 4| Note: QCC2072 and WCN7850 firmware does not support HTT stats type 76. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01181-QCAHKSWPL_SILICONZ-1 Signed-off-by: Harish Rachakonda Signed-off-by: Roopni Devanathan Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260326050641.3066562-1-roopni.devanathan@oss.qualcomm.com Signed-off-by: Jeff Johnson --- .../wireless/ath/ath12k/debugfs_htt_stats.c | 72 +++++++++++++++++++ .../wireless/ath/ath12k/debugfs_htt_stats.h | 26 +++++++ 2 files changed, 98 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c index 7f6ca07fb335..b772181a496e 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c @@ -5722,6 +5722,75 @@ ath12k_htt_print_tx_hwq_stats_cmn_tlv(const void *tag_buf, u16 tag_len, stats_req->buf_len = len; } +static void +ath12k_htt_print_chan_switch_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_chan_switch_stats_tlv *sbuf = tag_buf; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 switch_freq, switch_profile; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + u8 i; + + if (tag_len < sizeof(*sbuf)) + return; + + i = min(le32_to_cpu(sbuf->switch_count), ATH12K_HTT_CHAN_SWITCH_STATS_BUF_LEN); + if (!i) + return; + + len += scnprintf(buf + len, buf_len - len, "Channel Change Timings:\n"); + len += scnprintf(buf + len, buf_len - len, + "|%-20s|%-21s|%-7s|%-12s|%-12s|%-15s|", + "PRIMARY CHANNEL FREQ", "BANDWIDTH CENTER FREQ", "PHYMODE", + "TX_CHAINMASK", "RX_CHAINMASK", "SWITCH TIME(us)"); + len += scnprintf(buf + len, buf_len - len, + "%-7s|%-11s|%-7s|%-8s|%-7s|%-10s|\n", + "INI(us)", "TPC+CTL(us)", "CAL(us)", "MISC(us)", "CTL(us)", + "SW PROFILE"); + + /* + * sbuf->switch_count has the number of successful channel changes. The firmware + * sends the record of channel change in such a way that sbuf->chan_stats[0] will + * point to the channel change that occurred first and the recent channel change + * records will be stored in sbuf->chan_stats[9]. As and when new channel change + * occurs, sbuf->chan_stats[0] will be replaced by records from the next index, + * sbuf->chan_stats[1]. While printing the records, reverse chronological order + * is followed, i.e., the most recent channel change records are printed first + * and the oldest one, last. + */ + while (i--) { + switch_freq = le32_to_cpu(sbuf->chan_stats[i].chan_switch_freq); + switch_profile = le32_to_cpu(sbuf->chan_stats[i].chan_switch_profile); + + len += scnprintf(buf + len, buf_len - len, + "|%20u|%21u|%7u|%12u|%12u|%15u|", + u32_get_bits(switch_freq, + ATH12K_HTT_STATS_CHAN_SWITCH_BW_MHZ), + u32_get_bits(switch_freq, + ATH12K_HTT_STATS_CHAN_SWITCH_BAND_FREQ), + u32_get_bits(switch_profile, + ATH12K_HTT_STATS_CHAN_SWITCH_PHY_MODE), + u32_get_bits(switch_profile, + ATH12K_HTT_STATS_CHAN_SWITCH_TX_CHAINMASK), + u32_get_bits(switch_profile, + ATH12K_HTT_STATS_CHAN_SWITCH_RX_CHAINMASK), + le32_to_cpu(sbuf->chan_stats[i].chan_switch_time)); + len += scnprintf(buf + len, buf_len - len, + "%7u|%11u|%7u|%8u|%7u|%10u|\n", + le32_to_cpu(sbuf->chan_stats[i].ini_module_time), + le32_to_cpu(sbuf->chan_stats[i].tpc_module_time), + le32_to_cpu(sbuf->chan_stats[i].cal_module_time), + le32_to_cpu(sbuf->chan_stats[i].misc_module_time), + le32_to_cpu(sbuf->chan_stats[i].ctl_module_time), + u32_get_bits(switch_profile, + ATH12K_HTT_STATS_CHAN_SWITCH_SW_PROFILE)); + } + + stats_req->buf_len = len; +} + static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab, u16 tag, u16 len, const void *tag_buf, void *user_data) @@ -6024,6 +6093,9 @@ static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab, case HTT_STATS_TX_HWQ_CMN_TAG: ath12k_htt_print_tx_hwq_stats_cmn_tlv(tag_buf, len, stats_req); break; + case HTT_STATS_CHAN_SWITCH_STATS_TAG: + ath12k_htt_print_chan_switch_stats_tlv(tag_buf, len, stats_req); + break; default: break; } diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h index bfabe6500d44..82ab7b9e4db9 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h @@ -164,6 +164,7 @@ enum ath12k_dbg_htt_ext_stats_type { ATH12K_DBG_HTT_PDEV_MLO_IPC_STATS = 64, ATH12K_DBG_HTT_EXT_PDEV_RTT_RESP_STATS = 65, ATH12K_DBG_HTT_EXT_PDEV_RTT_INITIATOR_STATS = 66, + ATH12K_DBG_HTT_EXT_CHAN_SWITCH_STATS = 76, /* keep this last */ ATH12K_DBG_HTT_NUM_EXT_STATS, @@ -267,6 +268,7 @@ enum ath12k_dbg_htt_tlv_tag { HTT_STATS_PDEV_RTT_HW_STATS_TAG = 196, HTT_STATS_PDEV_RTT_TBR_SELFGEN_QUEUED_STATS_TAG = 197, HTT_STATS_PDEV_RTT_TBR_CMD_RESULT_STATS_TAG = 198, + HTT_STATS_CHAN_SWITCH_STATS_TAG = 213, HTT_STATS_MAX_TAG, }; @@ -2156,4 +2158,28 @@ struct htt_tx_hwq_stats_cmn_tlv { __le32 txq_timeout; } __packed; +#define ATH12K_HTT_CHAN_SWITCH_STATS_BUF_LEN 10 + +#define ATH12K_HTT_STATS_CHAN_SWITCH_BW_MHZ GENMASK(15, 0) +#define ATH12K_HTT_STATS_CHAN_SWITCH_BAND_FREQ GENMASK(31, 16) +#define ATH12K_HTT_STATS_CHAN_SWITCH_PHY_MODE GENMASK(7, 0) +#define ATH12K_HTT_STATS_CHAN_SWITCH_TX_CHAINMASK GENMASK(15, 8) +#define ATH12K_HTT_STATS_CHAN_SWITCH_RX_CHAINMASK GENMASK(23, 16) +#define ATH12K_HTT_STATS_CHAN_SWITCH_SW_PROFILE GENMASK(31, 24) + +struct ath12k_htt_chan_switch_stats_tlv { + struct { + __le32 chan_switch_freq; + __le32 chan_switch_profile; + __le32 chan_switch_time; + __le32 cal_module_time; + __le32 ini_module_time; + __le32 tpc_module_time; + __le32 misc_module_time; + __le32 ctl_module_time; + __le32 reserved; + } chan_stats[ATH12K_HTT_CHAN_SWITCH_STATS_BUF_LEN]; + __le32 switch_count; /* shows how many channel changes have occurred */ +} __packed; + #endif From 7d7dc26f72abb7a76abb4a68ebad75d5ab7b375e Mon Sep 17 00:00:00 2001 From: Avula Sri Charan Date: Mon, 30 Mar 2026 09:37:32 +0530 Subject: [PATCH 104/124] wifi: ath12k: Skip adding inactive partner vdev info Currently, a vdev that is created is considered active for partner link population. In case of an MLD station, non-associated link vdevs can be created but not started. Yet, they are added as partner links. This leads to the creation of stale FW partner entries which accumulate and cause assertions. To resolve this issue, check if the vdev is started and operating on a chosen frequency, i.e., arvif->is_started, instead of checking if the vdev is created, i.e., arvif->is_created. This determines if the vdev is active or not and skips adding it as a partner link if it's inactive. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01181-QCAHKSWPL_SILICONZ-1 Signed-off-by: Avula Sri Charan Signed-off-by: Roopni Devanathan Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260330040732.1847263-1-roopni.devanathan@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/mac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 553ec28b6aaa..c1a1b220f4dd 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -11131,7 +11131,7 @@ ath12k_mac_mlo_get_vdev_args(struct ath12k_link_vif *arvif, if (arvif == arvif_p) continue; - if (!arvif_p->is_created) + if (!arvif_p->is_started) continue; link_conf = wiphy_dereference(ahvif->ah->hw->wiphy, From 0ec4b904be72f78ba6ce6bb9a8aaf2eb6b9b1004 Mon Sep 17 00:00:00 2001 From: Roopni Devanathan Date: Thu, 2 Apr 2026 10:44:02 +0530 Subject: [PATCH 105/124] wifi: ath12k: Create symlink for each radio in a wiphy In single-wiphy design, when more than one radio is registered as a single-wiphy in the mac80211 layer, the following warnings are seen: 1. debugfs: File 'ath12k' in directory 'phy0' already present! 2. debugfs: File 'simulate_fw_crash' in directory 'pci-0000:57:00.0' already present! debugfs: File 'device_dp_stats' in directory 'pci-01777777777777777777777:57:00.0' already present! When more than one radio is registered as a single-wiphy, symlinks for all the radios are created in the same debugfs directory: /sys/kernel/debug/ieee80211/phyX/ath12k, resulting in warning 1. When a symlink is created for the first radio, since the 'ath12k' directory is not present, it will be created and no warning will be thrown. But when symlink is created for more than one radio, since the 'ath12k' directory was already created for symlink for radio 1, a warning is thrown complaining that 'ath12k' directory is already present. To resolve warning 1, create symlink for each radio in separate debugfs directories. For the first radio, the symlink will always be the 'ath12k' directory. This ensures that the existing directory structure is retained for single-wiphy and multi-wiphy architectures. In single-wiphy architecture with multiple radios, create symlink in separate debugfs directories introduced by mac80211. Existing debugfs directory in single-wiphy architecture: /sys/kernel/debug/ieee80211/phyX/ath12k is a symlink to /sys/kernel/debug/ath12k/pci-0001:01:00.0/macY Proposed debugfs directory in single-wiphy architecture with one radio: /sys/kernel/debug/ieee80211/phyX/ath12k is a symlink to /sys/kernel/debug/ath12k/pci-0001:01:00.0/mac0 Proposed debugfs directory in single-wiphy architecture with more than one radio: /sys/kernel/debug/ieee80211/phyX/radio0/ath12k is a symlink to /sys/kernel/debug/ath12k/pci-0001:01:00.0/mac0 and /sys/kernel/debug/ieee80211/phyX/radioY/ath12k is a symlink to /sys/kernel/debug/ath12k/pci-0001:01:00.0/macY Where X is phy index and Y is radio index, seen in 'iw phyX info | grep Idx'. Two symlinks for the first radio are to ensure compatibility with the existing design. Add radio_idx inside ar, to track the radio index in probing order. API ath12k_debugfs_pdev_create() that creates SoC entries is called more than once when hardware group starts up, resulting in warning 2. To resolve this warning, remove all other calls to this API and add one inside the ath12k_core_pdev_create(). This API carries all pdev-specific initializations and can conveniently hold a call to ath12k_debugfs_pdev_create(). Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Co-developed-by: Harshitha Prem Signed-off-by: Harshitha Prem Signed-off-by: Roopni Devanathan Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260402051402.3903795-1-roopni.devanathan@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/core.c | 4 ++-- drivers/net/wireless/ath/ath12k/core.h | 2 ++ drivers/net/wireless/ath/ath12k/debugfs.c | 29 +++++++++++++++++++---- drivers/net/wireless/ath/ath12k/mac.c | 2 +- 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index c31c47fb5a73..2519e2400d58 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -835,8 +835,6 @@ static int ath12k_core_soc_create(struct ath12k_base *ab) goto err_qmi_deinit; } - ath12k_debugfs_pdev_create(ab); - return 0; err_qmi_deinit: @@ -869,6 +867,8 @@ static int ath12k_core_pdev_create(struct ath12k_base *ab) goto err_dp_pdev_free; } + ath12k_debugfs_pdev_create(ab); + return 0; err_dp_pdev_free: diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 5eff86827e9c..46cef973db6f 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -588,6 +588,7 @@ struct ath12k_dbg_htt_stats { struct ath12k_debug { struct dentry *debugfs_pdev; struct dentry *debugfs_pdev_symlink; + struct dentry *debugfs_pdev_symlink_default; struct ath12k_dbg_htt_stats htt_stats; enum wmi_halphy_ctrl_path_stats_id tpc_stats_type; bool tpc_request; @@ -673,6 +674,7 @@ struct ath12k { u8 pdev_idx; u8 lmac_id; u8 hw_link_id; + u8 radio_idx; struct completion peer_assoc_done; struct completion peer_delete_done; diff --git a/drivers/net/wireless/ath/ath12k/debugfs.c b/drivers/net/wireless/ath/ath12k/debugfs.c index 358031fa14eb..8c81a1c22449 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.c +++ b/drivers/net/wireless/ath/ath12k/debugfs.c @@ -1473,18 +1473,35 @@ void ath12k_debugfs_register(struct ath12k *ar) { struct ath12k_base *ab = ar->ab; struct ieee80211_hw *hw = ar->ah->hw; - char pdev_name[5]; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct dentry *ath12k_fs; char buf[100] = {}; + char pdev_name[5]; scnprintf(pdev_name, sizeof(pdev_name), "%s%d", "mac", ar->pdev_idx); ar->debug.debugfs_pdev = debugfs_create_dir(pdev_name, ab->debugfs_soc); /* Create a symlink under ieee80211/phy* */ - scnprintf(buf, sizeof(buf), "../../ath12k/%pd2", ar->debug.debugfs_pdev); - ar->debug.debugfs_pdev_symlink = debugfs_create_symlink("ath12k", - hw->wiphy->debugfsdir, - buf); + if (ar->radio_idx == 0) { + scnprintf(buf, sizeof(buf), "../../ath12k/%pd2", + ar->debug.debugfs_pdev); + ath12k_fs = hw->wiphy->debugfsdir; + + /* symbolic link for compatibility */ + ar->debug.debugfs_pdev_symlink_default = debugfs_create_symlink("ath12k", + ath12k_fs, + buf); + } + + if (ah->num_radio > 1) { + scnprintf(buf, sizeof(buf), "../../../ath12k/%pd2", + ar->debug.debugfs_pdev); + ath12k_fs = hw->wiphy->radio_cfg[ar->radio_idx].radio_debugfsdir; + ar->debug.debugfs_pdev_symlink = debugfs_create_symlink("ath12k", + ath12k_fs, + buf); + } if (ar->mac.sbands[NL80211_BAND_5GHZ].channels) { debugfs_create_file("dfs_simulate_radar", 0200, @@ -1513,7 +1530,9 @@ void ath12k_debugfs_unregister(struct ath12k *ar) /* Remove symlink under ieee80211/phy* */ debugfs_remove(ar->debug.debugfs_pdev_symlink); + debugfs_remove(ar->debug.debugfs_pdev_symlink_default); debugfs_remove_recursive(ar->debug.debugfs_pdev); ar->debug.debugfs_pdev_symlink = NULL; + ar->debug.debugfs_pdev_symlink_default = NULL; ar->debug.debugfs_pdev = NULL; } diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index c1a1b220f4dd..fbdfe6424fd7 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -15065,6 +15065,7 @@ static struct ath12k_hw *ath12k_mac_hw_allocate(struct ath12k_hw_group *ag, ar->hw_link_id = pdev->hw_link_id; ar->pdev = pdev; ar->pdev_idx = pdev_idx; + ar->radio_idx = i; pdev->ar = ar; ag->hw_links[ar->hw_link_id].device_id = ab->device_id; @@ -15132,7 +15133,6 @@ int ath12k_mac_allocate(struct ath12k_hw_group *ag) if (!ab) continue; - ath12k_debugfs_pdev_create(ab); ath12k_mac_set_device_defaults(ab); total_radio += ab->num_radios; } From 9a34a59c6086ae731a06b3e61b0951feef758648 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Wed, 25 Mar 2026 11:05:01 +0800 Subject: [PATCH 106/124] wifi: ath10k: fix station lookup failure during disconnect Recent commit [1] moved station statistics collection to an earlier stage of the disconnect flow. With this change in place, ath10k fails to resolve the station entry when handling a peer stats event triggered during disconnect, resulting in log messages such as: wlp58s0: deauthenticating from 74:1a:e0:e7:b4:c8 by local choice (Reason: 3=DEAUTH_LEAVING) ath10k_pci 0000:3a:00.0: not found station for peer stats ath10k_pci 0000:3a:00.0: failed to parse stats info tlv: -22 The failure occurs because ath10k relies on ieee80211_find_sta_by_ifaddr() for station lookup. That function uses local->sta_hash, but by the time the peer stats request is triggered during disconnect, mac80211 has already removed the station from that hash table, leading to lookup failure. Before commit [1], this issue was not visible because the transition from IEEE80211_STA_NONE to IEEE80211_STA_NOTEXIST prevented ath10k from sending a peer stats request at all: ath10k_mac_sta_get_peer_stats_info() would fail early to find the peer and skip requesting statistics. Fix this by switching the lookup path to ath10k_peer_find(), which queries ath10k's internal peer table. At the point where the firmware emits the peer stats event, the peer entry is still present in the driver's list, ensuring lookup succeeds. Tested-on: QCA6174 hw3.2 PCI WLAN.RM.4.4.1-00309-QCARMSWPZ-1 Fixes: a203dbeeca15 ("wifi: mac80211: collect station statistics earlier when disconnect") # [1] Reported-by: Paul Menzel Closes: https://lore.kernel.org/ath10k/57671b89-ec9f-4e6c-992c-45eb8e75929c@molgen.mpg.de Signed-off-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Reviewed-by: Paul Menzel Tested-by: Paul Menzel Link: https://patch.msgid.link/20260325-ath10k-station-lookup-failure-v1-1-2e0c970f25d5@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath10k/wmi-tlv.c | 26 +++++++++++++---------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index ec8e91707f84..01f2d1fa9d7d 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -3,7 +3,7 @@ * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. - * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include "core.h" #include "debug.h" @@ -14,6 +14,7 @@ #include "wmi-tlv.h" #include "p2p.h" #include "testmode.h" +#include "txrx.h" #include /***************/ @@ -224,8 +225,9 @@ static int ath10k_wmi_tlv_parse_peer_stats_info(struct ath10k *ar, u16 tag, u16 const void *ptr, void *data) { const struct wmi_tlv_peer_stats_info *stat = ptr; - struct ieee80211_sta *sta; + u32 vdev_id = *(u32 *)data; struct ath10k_sta *arsta; + struct ath10k_peer *peer; if (tag != WMI_TLV_TAG_STRUCT_PEER_STATS_INFO) return -EPROTO; @@ -241,20 +243,20 @@ static int ath10k_wmi_tlv_parse_peer_stats_info(struct ath10k *ar, u16 tag, u16 __le32_to_cpu(stat->last_tx_rate_code), __le32_to_cpu(stat->last_tx_bitrate_kbps)); - rcu_read_lock(); - sta = ieee80211_find_sta_by_ifaddr(ar->hw, stat->peer_macaddr.addr, NULL); - if (!sta) { - rcu_read_unlock(); - ath10k_warn(ar, "not found station for peer stats\n"); + guard(spinlock_bh)(&ar->data_lock); + + peer = ath10k_peer_find(ar, vdev_id, stat->peer_macaddr.addr); + if (!peer || !peer->sta) { + ath10k_warn(ar, "not found %s with vdev id %u mac addr %pM for peer stats\n", + peer ? "sta" : "peer", vdev_id, stat->peer_macaddr.addr); return -EINVAL; } - arsta = (struct ath10k_sta *)sta->drv_priv; + arsta = (struct ath10k_sta *)peer->sta->drv_priv; arsta->rx_rate_code = __le32_to_cpu(stat->last_rx_rate_code); arsta->rx_bitrate_kbps = __le32_to_cpu(stat->last_rx_bitrate_kbps); arsta->tx_rate_code = __le32_to_cpu(stat->last_tx_rate_code); arsta->tx_bitrate_kbps = __le32_to_cpu(stat->last_tx_bitrate_kbps); - rcu_read_unlock(); return 0; } @@ -266,6 +268,7 @@ static int ath10k_wmi_tlv_op_pull_peer_stats_info(struct ath10k *ar, const struct wmi_tlv_peer_stats_info_ev *ev; const void *data; u32 num_peer_stats; + u32 vdev_id; int ret; tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC); @@ -284,15 +287,16 @@ static int ath10k_wmi_tlv_op_pull_peer_stats_info(struct ath10k *ar, } num_peer_stats = __le32_to_cpu(ev->num_peers); + vdev_id = __le32_to_cpu(ev->vdev_id); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv peer stats info update peer vdev id %d peers %i more data %d\n", - __le32_to_cpu(ev->vdev_id), + vdev_id, num_peer_stats, __le32_to_cpu(ev->more_data)); ret = ath10k_wmi_tlv_iter(ar, data, ath10k_wmi_tlv_len(data), - ath10k_wmi_tlv_parse_peer_stats_info, NULL); + ath10k_wmi_tlv_parse_peer_stats_info, &vdev_id); if (ret) ath10k_warn(ar, "failed to parse stats info tlv: %d\n", ret); From 3ebaf730b5832319726e12ebe634a7679eaf2e9b Mon Sep 17 00:00:00 2001 From: Raj Kumar Bhagat Date: Tue, 7 Apr 2026 10:56:28 +0530 Subject: [PATCH 107/124] dt-bindings: net: wireless: add ath12k wifi device IPQ5424 Add the device-tree bindings for the ATH12K AHB wifi device IPQ5424. Signed-off-by: Raj Kumar Bhagat Acked-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260407-ath12k-ipq5424-v5-1-8e96aa660ec4@oss.qualcomm.com Signed-off-by: Jeff Johnson --- .../devicetree/bindings/net/wireless/qcom,ipq5332-wifi.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,ipq5332-wifi.yaml b/Documentation/devicetree/bindings/net/wireless/qcom,ipq5332-wifi.yaml index 363a0ecb6ad9..37d8a0da7780 100644 --- a/Documentation/devicetree/bindings/net/wireless/qcom,ipq5332-wifi.yaml +++ b/Documentation/devicetree/bindings/net/wireless/qcom,ipq5332-wifi.yaml @@ -17,6 +17,7 @@ properties: compatible: enum: - qcom,ipq5332-wifi + - qcom,ipq5424-wifi reg: maxItems: 1 From b1ad1a052beda2ac0400d6d4cc05dd2e549a6936 Mon Sep 17 00:00:00 2001 From: Saravanakumar Duraisamy Date: Tue, 7 Apr 2026 10:56:29 +0530 Subject: [PATCH 108/124] wifi: ath12k: Add ath12k_hw_params for IPQ5424 Add ath12k_hw_params for the ath12k AHB-based WiFi 7 device IPQ5424. The WiFi device IPQ5424 is similar to IPQ5332. Most of the hardware parameters like hw_ops, wmi_init, ring_mask, etc., are the same between IPQ5424 and IPQ5332, hence use these same parameters for IPQ5424. Some parameters are specific to IPQ5424; initially set these to 0 or NULL, and populate them in subsequent patches. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Tested-on: IPQ5332 hw1.0 AHB WLAN.WBE.1.6-01275-QCAHKSWPL_SILICONZ-1 Tested-on: IPQ5424 hw1.0 AHB WLAN.WBE.1.6-01275-QCAHKSWPL_SILICONZ-1 Signed-off-by: Saravanakumar Duraisamy Signed-off-by: Raj Kumar Bhagat Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260407-ath12k-ipq5424-v5-2-8e96aa660ec4@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/core.h | 1 + drivers/net/wireless/ath/ath12k/wifi7/hw.c | 79 ++++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 46cef973db6f..8be435535a4e 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -157,6 +157,7 @@ enum ath12k_hw_rev { ATH12K_HW_WCN7850_HW20, ATH12K_HW_IPQ5332_HW10, ATH12K_HW_QCC2072_HW10, + ATH12K_HW_IPQ5424_HW10, }; enum ath12k_firmware_mode { diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hw.c b/drivers/net/wireless/ath/ath12k/wifi7/hw.c index ec6dba96640b..2b5d1f7e9e04 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hw.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hw.c @@ -753,6 +753,85 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .dp_primary_link_only = false, }, + { + .name = "ipq5424 hw1.0", + .hw_rev = ATH12K_HW_IPQ5424_HW10, + .fw = { + .dir = "IPQ5424/hw1.0", + .board_size = 256 * 1024, + .cal_offset = 128 * 1024, + .m3_loader = ath12k_m3_fw_loader_remoteproc, + .download_aux_ucode = false, + }, + .max_radios = 1, + .single_pdev_only = false, + .qmi_service_ins_id = ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_IPQ5332, + .internal_sleep_clock = false, + + .hw_ops = &qcn9274_ops, + .ring_mask = &ath12k_wifi7_hw_ring_mask_ipq5332, + + .host_ce_config = ath12k_wifi7_host_ce_config_ipq5332, + .ce_count = 12, + .target_ce_config = ath12k_wifi7_target_ce_config_wlan_ipq5332, + .target_ce_count = 12, + .svc_to_ce_map = + ath12k_wifi7_target_service_to_ce_map_wlan_ipq5332, + .svc_to_ce_map_len = 18, + + .rxdma1_enable = true, + .num_rxdma_per_pdev = 1, + .num_rxdma_dst_ring = 0, + .rx_mac_buf_ring = false, + .vdev_start_delay = false, + + .interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MESH_POINT), + .supports_monitor = true, + + .idle_ps = false, + .download_calib = true, + .supports_suspend = false, + .tcl_ring_retry = true, + .reoq_lut_support = false, + .supports_shadow_regs = false, + + .num_tcl_banks = 48, + .max_tx_ring = 4, + + .mhi_config = NULL, + + .wmi_init = &ath12k_wifi7_wmi_init_qcn9274, + + .qmi_cnss_feature_bitmap = BIT(CNSS_QDSS_CFG_MISS_V01), + + .rfkill_pin = 0, + .rfkill_cfg = 0, + .rfkill_on_level = 0, + + .rddm_size = 0, + + .def_num_link = 0, + .max_mlo_peer = 256, + + .otp_board_id_register = 0, + + .supports_sta_ps = false, + + .acpi_guid = NULL, + .supports_dynamic_smps_6ghz = false, + .iova_mask = 0, + .supports_aspm = false, + + .ce_ie_addr = NULL, + .ce_remap = NULL, + .bdf_addr_offset = 0x940000, + + .current_cc_support = false, + + .dp_primary_link_only = true, + }, }; /* Note: called under rcu_read_lock() */ From 74f5a619b1a6a06cc5e6246d326da5b6f2b0fcbd Mon Sep 17 00:00:00 2001 From: Raj Kumar Bhagat Date: Tue, 7 Apr 2026 10:56:30 +0530 Subject: [PATCH 109/124] wifi: ath12k: add ath12k_hw_version_map entry for IPQ5424 Add a new ath12k_hw_version_map entry for the AHB based WiFi 7 device IPQ5424. Reuse most of the ath12k_hw_version_map fields such as hal_ops, hal_desc_sz, tcl_to_wbm_rbm_map, and hal_params from IPQ5332. The register addresses differ on IPQ5424, hence set hw_regs temporarily to NULL and populated it in a subsequent patch. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Tested-on: IPQ5332 hw1.0 AHB WLAN.WBE.1.6-01275-QCAHKSWPL_SILICONZ-1 Tested-on: IPQ5424 hw1.0 AHB WLAN.WBE.1.6-01275-QCAHKSWPL_SILICONZ-1 Signed-off-by: Raj Kumar Bhagat Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260407-ath12k-ipq5424-v5-3-8e96aa660ec4@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/wifi7/hal.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal.c b/drivers/net/wireless/ath/ath12k/wifi7/hal.c index bd1753ca0db6..c2cc99a83f09 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal.c @@ -50,6 +50,13 @@ static const struct ath12k_hw_version_map ath12k_wifi7_hw_ver_map[] = { .hal_params = &ath12k_hw_hal_params_wcn7850, .hw_regs = &qcc2072_regs, }, + [ATH12K_HW_IPQ5424_HW10] = { + .hal_ops = &hal_qcn9274_ops, + .hal_desc_sz = sizeof(struct hal_rx_desc_qcn9274_compact), + .tcl_to_wbm_rbm_map = ath12k_hal_tcl_to_wbm_rbm_map_qcn9274, + .hal_params = &ath12k_hw_hal_params_ipq5332, + .hw_regs = NULL, + }, }; int ath12k_wifi7_hal_init(struct ath12k_base *ab) From 7e2131ba332f5ae62b6302eb889feeeea56a1691 Mon Sep 17 00:00:00 2001 From: Saravanakumar Duraisamy Date: Tue, 7 Apr 2026 10:56:31 +0530 Subject: [PATCH 110/124] wifi: ath12k: add ath12k_hw_regs for IPQ5424 Add register addresses (ath12k_hw_regs) for ath12k AHB based WiFi 7 device IPQ5424. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Tested-on: IPQ5332 hw1.0 AHB WLAN.WBE.1.6-01275-QCAHKSWPL_SILICONZ-1 Tested-on: IPQ5424 hw1.0 AHB WLAN.WBE.1.6-01275-QCAHKSWPL_SILICONZ-1 Signed-off-by: Saravanakumar Duraisamy Signed-off-by: Raj Kumar Bhagat Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260407-ath12k-ipq5424-v5-4-8e96aa660ec4@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/wifi7/hal.c | 2 +- drivers/net/wireless/ath/ath12k/wifi7/hal.h | 3 + .../wireless/ath/ath12k/wifi7/hal_qcn9274.c | 88 +++++++++++++++++++ .../wireless/ath/ath12k/wifi7/hal_qcn9274.h | 1 + 4 files changed, 93 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal.c b/drivers/net/wireless/ath/ath12k/wifi7/hal.c index c2cc99a83f09..a0a1902fb491 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal.c @@ -55,7 +55,7 @@ static const struct ath12k_hw_version_map ath12k_wifi7_hw_ver_map[] = { .hal_desc_sz = sizeof(struct hal_rx_desc_qcn9274_compact), .tcl_to_wbm_rbm_map = ath12k_hal_tcl_to_wbm_rbm_map_qcn9274, .hal_params = &ath12k_hw_hal_params_ipq5332, - .hw_regs = NULL, + .hw_regs = &ipq5424_regs, }, }; diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal.h b/drivers/net/wireless/ath/ath12k/wifi7/hal.h index 9337225a5253..3d9386198893 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal.h +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal.h @@ -364,6 +364,9 @@ #define HAL_IPQ5332_CE_WFSS_REG_BASE 0x740000 #define HAL_IPQ5332_CE_SIZE 0x100000 +#define HAL_IPQ5424_CE_WFSS_REG_BASE 0x200000 +#define HAL_IPQ5424_CE_SIZE 0x100000 + #define HAL_RX_MAX_BA_WINDOW 256 #define HAL_DEFAULT_BE_BK_VI_REO_TIMEOUT_USEC (100 * 1000) diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.c b/drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.c index 41c918eb1767..ba9ce1e718e8 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.c @@ -484,6 +484,94 @@ const struct ath12k_hw_regs ipq5332_regs = { HAL_IPQ5332_CE_WFSS_REG_BASE, }; +const struct ath12k_hw_regs ipq5424_regs = { + /* SW2TCL(x) R0 ring configuration address */ + .tcl1_ring_id = 0x00000918, + .tcl1_ring_misc = 0x00000920, + .tcl1_ring_tp_addr_lsb = 0x0000092c, + .tcl1_ring_tp_addr_msb = 0x00000930, + .tcl1_ring_consumer_int_setup_ix0 = 0x00000940, + .tcl1_ring_consumer_int_setup_ix1 = 0x00000944, + .tcl1_ring_msi1_base_lsb = 0x00000958, + .tcl1_ring_msi1_base_msb = 0x0000095c, + .tcl1_ring_base_lsb = 0x00000910, + .tcl1_ring_base_msb = 0x00000914, + .tcl1_ring_msi1_data = 0x00000960, + .tcl2_ring_base_lsb = 0x00000988, + .tcl_ring_base_lsb = 0x00000b68, + + /* TCL STATUS ring address */ + .tcl_status_ring_base_lsb = 0x00000d48, + + /* REO DEST ring address */ + .reo2_ring_base = 0x00000578, + .reo1_misc_ctrl_addr = 0x00000b9c, + .reo1_sw_cookie_cfg0 = 0x0000006c, + .reo1_sw_cookie_cfg1 = 0x00000070, + .reo1_qdesc_lut_base0 = 0x00000074, + .reo1_qdesc_lut_base1 = 0x00000078, + .reo1_ring_base_lsb = 0x00000500, + .reo1_ring_base_msb = 0x00000504, + .reo1_ring_id = 0x00000508, + .reo1_ring_misc = 0x00000510, + .reo1_ring_hp_addr_lsb = 0x00000514, + .reo1_ring_hp_addr_msb = 0x00000518, + .reo1_ring_producer_int_setup = 0x00000524, + .reo1_ring_msi1_base_lsb = 0x00000548, + .reo1_ring_msi1_base_msb = 0x0000054C, + .reo1_ring_msi1_data = 0x00000550, + .reo1_aging_thres_ix0 = 0x00000B28, + .reo1_aging_thres_ix1 = 0x00000B2C, + .reo1_aging_thres_ix2 = 0x00000B30, + .reo1_aging_thres_ix3 = 0x00000B34, + + /* REO Exception ring address */ + .reo2_sw0_ring_base = 0x000008c0, + + /* REO Reinject ring address */ + .sw2reo_ring_base = 0x00000320, + .sw2reo1_ring_base = 0x00000398, + + /* REO cmd ring address */ + .reo_cmd_ring_base = 0x000002A8, + + /* REO status ring address */ + .reo_status_ring_base = 0x00000aa0, + + /* WBM idle link ring address */ + .wbm_idle_ring_base_lsb = 0x00000d3c, + .wbm_idle_ring_misc_addr = 0x00000d4c, + .wbm_r0_idle_list_cntl_addr = 0x00000240, + .wbm_r0_idle_list_size_addr = 0x00000244, + .wbm_scattered_ring_base_lsb = 0x00000250, + .wbm_scattered_ring_base_msb = 0x00000254, + .wbm_scattered_desc_head_info_ix0 = 0x00000260, + .wbm_scattered_desc_head_info_ix1 = 0x00000264, + .wbm_scattered_desc_tail_info_ix0 = 0x00000270, + .wbm_scattered_desc_tail_info_ix1 = 0x00000274, + .wbm_scattered_desc_ptr_hp_addr = 0x0000027c, + + /* SW2WBM release ring address */ + .wbm_sw_release_ring_base_lsb = 0x0000037c, + + /* WBM2SW release ring address */ + .wbm0_release_ring_base_lsb = 0x00000e08, + .wbm1_release_ring_base_lsb = 0x00000e80, + + /* PPE release ring address */ + .ppe_rel_ring_base = 0x0000046c, + + /* CE address */ + .umac_ce0_src_reg_base = 0x00200000 - + HAL_IPQ5424_CE_WFSS_REG_BASE, + .umac_ce0_dest_reg_base = 0x00201000 - + HAL_IPQ5424_CE_WFSS_REG_BASE, + .umac_ce1_src_reg_base = 0x00202000 - + HAL_IPQ5424_CE_WFSS_REG_BASE, + .umac_ce1_dest_reg_base = 0x00203000 - + HAL_IPQ5424_CE_WFSS_REG_BASE, +}; + static inline bool ath12k_hal_rx_desc_get_first_msdu_qcn9274(struct hal_rx_desc *desc) { diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.h b/drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.h index 08c0a0469474..03cf3792d523 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.h +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_qcn9274.h @@ -17,6 +17,7 @@ extern const struct hal_ops hal_qcn9274_ops; extern const struct ath12k_hw_regs qcn9274_v1_regs; extern const struct ath12k_hw_regs qcn9274_v2_regs; extern const struct ath12k_hw_regs ipq5332_regs; +extern const struct ath12k_hw_regs ipq5424_regs; extern const struct ath12k_hal_tcl_to_wbm_rbm_map ath12k_hal_tcl_to_wbm_rbm_map_qcn9274[DP_TCL_NUM_RING_MAX]; extern const struct ath12k_hw_hal_params ath12k_hw_hal_params_qcn9274; From 38cff745fa7c0b006f95565a2e5de9f0cac13702 Mon Sep 17 00:00:00 2001 From: Saravanakumar Duraisamy Date: Tue, 7 Apr 2026 10:56:32 +0530 Subject: [PATCH 111/124] wifi: ath12k: Add CE remap hardware parameters for IPQ5424 Add CE remap hardware parameters for Ath12k AHB device IPQ5424. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Tested-on: IPQ5332 hw1.0 AHB WLAN.WBE.1.6-01275-QCAHKSWPL_SILICONZ-1 Tested-on: IPQ5424 hw1.0 AHB WLAN.WBE.1.6-01275-QCAHKSWPL_SILICONZ-1 Signed-off-by: Saravanakumar Duraisamy Signed-off-by: Raj Kumar Bhagat Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260407-ath12k-ipq5424-v5-5-8e96aa660ec4@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/ce.h | 13 +++++++++---- drivers/net/wireless/ath/ath12k/wifi7/hw.c | 22 +++++++++++++++++----- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/ce.h b/drivers/net/wireless/ath/ath12k/ce.h index df4f2a4f8480..009cddf2d68d 100644 --- a/drivers/net/wireless/ath/ath12k/ce.h +++ b/drivers/net/wireless/ath/ath12k/ce.h @@ -38,10 +38,15 @@ #define PIPEDIR_INOUT 3 /* bidirectional */ #define PIPEDIR_INOUT_H2H 4 /* bidirectional, host to host */ -/* CE address/mask */ -#define CE_HOST_IE_ADDRESS 0x75804C -#define CE_HOST_IE_2_ADDRESS 0x758050 -#define CE_HOST_IE_3_ADDRESS CE_HOST_IE_ADDRESS +/* IPQ5332 CE address/mask */ +#define CE_HOST_IPQ5332_IE_ADDRESS 0x75804C +#define CE_HOST_IPQ5332_IE_2_ADDRESS 0x758050 +#define CE_HOST_IPQ5332_IE_3_ADDRESS CE_HOST_IPQ5332_IE_ADDRESS + +/* IPQ5424 CE address/mask */ +#define CE_HOST_IPQ5424_IE_ADDRESS 0x21804C +#define CE_HOST_IPQ5424_IE_2_ADDRESS 0x218050 +#define CE_HOST_IPQ5424_IE_3_ADDRESS CE_HOST_IPQ5424_IE_ADDRESS #define CE_HOST_IE_3_SHIFT 0xC diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hw.c b/drivers/net/wireless/ath/ath12k/wifi7/hw.c index 2b5d1f7e9e04..cb3185850439 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hw.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hw.c @@ -329,9 +329,15 @@ static const struct ath12k_hw_ring_mask ath12k_wifi7_hw_ring_mask_wcn7850 = { }; static const struct ce_ie_addr ath12k_wifi7_ce_ie_addr_ipq5332 = { - .ie1_reg_addr = CE_HOST_IE_ADDRESS - HAL_IPQ5332_CE_WFSS_REG_BASE, - .ie2_reg_addr = CE_HOST_IE_2_ADDRESS - HAL_IPQ5332_CE_WFSS_REG_BASE, - .ie3_reg_addr = CE_HOST_IE_3_ADDRESS - HAL_IPQ5332_CE_WFSS_REG_BASE, + .ie1_reg_addr = CE_HOST_IPQ5332_IE_ADDRESS - HAL_IPQ5332_CE_WFSS_REG_BASE, + .ie2_reg_addr = CE_HOST_IPQ5332_IE_2_ADDRESS - HAL_IPQ5332_CE_WFSS_REG_BASE, + .ie3_reg_addr = CE_HOST_IPQ5332_IE_3_ADDRESS - HAL_IPQ5332_CE_WFSS_REG_BASE, +}; + +static const struct ce_ie_addr ath12k_wifi7_ce_ie_addr_ipq5424 = { + .ie1_reg_addr = CE_HOST_IPQ5424_IE_ADDRESS - HAL_IPQ5424_CE_WFSS_REG_BASE, + .ie2_reg_addr = CE_HOST_IPQ5424_IE_2_ADDRESS - HAL_IPQ5424_CE_WFSS_REG_BASE, + .ie3_reg_addr = CE_HOST_IPQ5424_IE_3_ADDRESS - HAL_IPQ5424_CE_WFSS_REG_BASE, }; static const struct ce_remap ath12k_wifi7_ce_remap_ipq5332 = { @@ -340,6 +346,12 @@ static const struct ce_remap ath12k_wifi7_ce_remap_ipq5332 = { .cmem_offset = HAL_SEQ_WCSS_CMEM_OFFSET, }; +static const struct ce_remap ath12k_wifi7_ce_remap_ipq5424 = { + .base = HAL_IPQ5424_CE_WFSS_REG_BASE, + .size = HAL_IPQ5424_CE_SIZE, + .cmem_offset = HAL_SEQ_WCSS_CMEM_OFFSET, +}; + static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { { .name = "qcn9274 hw1.0", @@ -824,8 +836,8 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .iova_mask = 0, .supports_aspm = false, - .ce_ie_addr = NULL, - .ce_remap = NULL, + .ce_ie_addr = &ath12k_wifi7_ce_ie_addr_ipq5424, + .ce_remap = &ath12k_wifi7_ce_remap_ipq5424, .bdf_addr_offset = 0x940000, .current_cc_support = false, From 8fb66931fe31094aa2e1b2a5c015050b8b4cb2ec Mon Sep 17 00:00:00 2001 From: Sowmiya Sree Elavalagan Date: Tue, 7 Apr 2026 10:56:33 +0530 Subject: [PATCH 112/124] wifi: ath12k: Enable IPQ5424 WiFi device support Currently, ath12k AHB (in IPQ5332) uses SCM calls to authenticate the firmware image to bring up userpd. From IPQ5424 onwards, Q6 firmware can directly communicate with the Trusted Management Engine - Lite (TME-L), eliminating the need for SCM calls for userpd bring-up. Hence, to enable IPQ5424 device support, use qcom_mdt_load_no_init() and skip the SCM call as Q6 will directly authenticate the userpd firmware. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 Tested-on: IPQ5332 hw1.0 AHB WLAN.WBE.1.6-01275-QCAHKSWPL_SILICONZ-1 Tested-on: IPQ5424 hw1.0 AHB WLAN.WBE.1.6-01275-QCAHKSWPL_SILICONZ-1 Signed-off-by: Sowmiya Sree Elavalagan Co-developed-by: Saravanakumar Duraisamy Signed-off-by: Saravanakumar Duraisamy Co-developed-by: Raj Kumar Bhagat Signed-off-by: Raj Kumar Bhagat Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260407-ath12k-ipq5424-v5-6-8e96aa660ec4@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/ahb.c | 36 +++++++++++++-------- drivers/net/wireless/ath/ath12k/ahb.h | 1 + drivers/net/wireless/ath/ath12k/wifi7/ahb.c | 8 +++++ 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/ahb.c b/drivers/net/wireless/ath/ath12k/ahb.c index 9a4d34e49104..2dcf0a52e4c1 100644 --- a/drivers/net/wireless/ath/ath12k/ahb.c +++ b/drivers/net/wireless/ath/ath12k/ahb.c @@ -382,8 +382,12 @@ static int ath12k_ahb_power_up(struct ath12k_base *ab) ATH12K_AHB_UPD_SWID; /* Load FW image to a reserved memory location */ - ret = qcom_mdt_load(dev, fw, fw_name, pasid, mem_region, mem_phys, mem_size, - &mem_phys); + if (ab_ahb->scm_auth_enabled) + ret = qcom_mdt_load(dev, fw, fw_name, pasid, mem_region, + mem_phys, mem_size, &mem_phys); + else + ret = qcom_mdt_load_no_init(dev, fw, fw_name, mem_region, + mem_phys, mem_size, &mem_phys); if (ret) { ath12k_err(ab, "Failed to load MDT segments: %d\n", ret); goto err_fw; @@ -414,11 +418,13 @@ static int ath12k_ahb_power_up(struct ath12k_base *ab) goto err_fw2; } - /* Authenticate FW image using peripheral ID */ - ret = qcom_scm_pas_auth_and_reset(pasid); - if (ret) { - ath12k_err(ab, "failed to boot the remote processor %d\n", ret); - goto err_fw2; + if (ab_ahb->scm_auth_enabled) { + /* Authenticate FW image using peripheral ID */ + ret = qcom_scm_pas_auth_and_reset(pasid); + if (ret) { + ath12k_err(ab, "failed to boot the remote processor %d\n", ret); + goto err_fw2; + } } /* Instruct Q6 to spawn userPD thread */ @@ -475,13 +481,15 @@ static void ath12k_ahb_power_down(struct ath12k_base *ab, bool is_suspend) qcom_smem_state_update_bits(ab_ahb->stop_state, BIT(ab_ahb->stop_bit), 0); - pasid = (u32_encode_bits(ab_ahb->userpd_id, ATH12K_USERPD_ID_MASK)) | - ATH12K_AHB_UPD_SWID; - /* Release the firmware */ - ret = qcom_scm_pas_shutdown(pasid); - if (ret) - ath12k_err(ab, "scm pas shutdown failed for userPD%d: %d\n", - ab_ahb->userpd_id, ret); + if (ab_ahb->scm_auth_enabled) { + pasid = (u32_encode_bits(ab_ahb->userpd_id, ATH12K_USERPD_ID_MASK)) | + ATH12K_AHB_UPD_SWID; + /* Release the firmware */ + ret = qcom_scm_pas_shutdown(pasid); + if (ret) + ath12k_err(ab, "scm pas shutdown failed for userPD%d\n", + ab_ahb->userpd_id); + } } static void ath12k_ahb_init_qmi_ce_config(struct ath12k_base *ab) diff --git a/drivers/net/wireless/ath/ath12k/ahb.h b/drivers/net/wireless/ath/ath12k/ahb.h index be9e31b3682d..0fa15daaa3e6 100644 --- a/drivers/net/wireless/ath/ath12k/ahb.h +++ b/drivers/net/wireless/ath/ath12k/ahb.h @@ -68,6 +68,7 @@ struct ath12k_ahb { int userpd_irq_num[ATH12K_USERPD_MAX_IRQ]; const struct ath12k_ahb_ops *ahb_ops; const struct ath12k_ahb_device_family_ops *device_family_ops; + bool scm_auth_enabled; }; struct ath12k_ahb_driver { diff --git a/drivers/net/wireless/ath/ath12k/wifi7/ahb.c b/drivers/net/wireless/ath/ath12k/wifi7/ahb.c index a6c5f7689edd..6a8b8b2a56f9 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/ahb.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/ahb.c @@ -19,6 +19,9 @@ static const struct of_device_id ath12k_wifi7_ahb_of_match[] = { { .compatible = "qcom,ipq5332-wifi", .data = (void *)ATH12K_HW_IPQ5332_HW10, }, + { .compatible = "qcom,ipq5424-wifi", + .data = (void *)ATH12K_HW_IPQ5424_HW10, + }, { } }; @@ -38,6 +41,11 @@ static int ath12k_wifi7_ahb_probe(struct platform_device *pdev) switch (hw_rev) { case ATH12K_HW_IPQ5332_HW10: ab_ahb->userpd_id = ATH12K_IPQ5332_USERPD_ID; + ab_ahb->scm_auth_enabled = true; + break; + case ATH12K_HW_IPQ5424_HW10: + ab_ahb->userpd_id = ATH12K_IPQ5332_USERPD_ID; + ab_ahb->scm_auth_enabled = false; break; default: return -EOPNOTSUPP; From 607363a10ee60cc46ae0168349f1f8c59045c371 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 7 Apr 2026 17:11:09 +0200 Subject: [PATCH 113/124] wifi: at76c50x: refactor endpoint lookup Use the common USB helper for looking up bulk and interrupt endpoints instead of open coding. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260407151111.3187826-2-johan@kernel.org Signed-off-by: Johannes Berg --- drivers/net/wireless/atmel/at76c50x-usb.c | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/atmel/at76c50x-usb.c b/drivers/net/wireless/atmel/at76c50x-usb.c index 44b04ea3cc8b..32e3e09e7680 100644 --- a/drivers/net/wireless/atmel/at76c50x-usb.c +++ b/drivers/net/wireless/atmel/at76c50x-usb.c @@ -2226,34 +2226,20 @@ static struct at76_priv *at76_alloc_new_device(struct usb_device *udev) static int at76_alloc_urbs(struct at76_priv *priv, struct usb_interface *interface) { - struct usb_endpoint_descriptor *endpoint, *ep_in, *ep_out; - int i; + struct usb_endpoint_descriptor *ep_in, *ep_out; int buffer_size; struct usb_host_interface *iface_desc; + int ret; at76_dbg(DBG_PROC_ENTRY, "%s: ENTER", __func__); at76_dbg(DBG_URB, "%s: NumEndpoints %d ", __func__, interface->cur_altsetting->desc.bNumEndpoints); - ep_in = NULL; - ep_out = NULL; iface_desc = interface->cur_altsetting; - for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { - endpoint = &iface_desc->endpoint[i].desc; - at76_dbg(DBG_URB, "%s: %d. endpoint: addr 0x%x attr 0x%x", - __func__, i, endpoint->bEndpointAddress, - endpoint->bmAttributes); - - if (!ep_in && usb_endpoint_is_bulk_in(endpoint)) - ep_in = endpoint; - - if (!ep_out && usb_endpoint_is_bulk_out(endpoint)) - ep_out = endpoint; - } - - if (!ep_in || !ep_out) { + ret = usb_find_common_endpoints(iface_desc, &ep_in, &ep_out, NULL, NULL); + if (ret) { dev_err(&interface->dev, "bulk endpoints missing\n"); return -ENXIO; } From c885e392aadb43227d3c677eab2941a2c31355ff Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 7 Apr 2026 17:11:10 +0200 Subject: [PATCH 114/124] wifi: libertas: refactor endpoint lookup Use the common USB helpers for looking up bulk and interrupt endpoints (and determining max packet size) instead of open coding. Note that the driver has an implicit max packet size check which is kept. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260407151111.3187826-3-johan@kernel.org Signed-off-by: Johannes Berg --- .../net/wireless/marvell/libertas/if_usb.c | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/marvell/libertas/if_usb.c b/drivers/net/wireless/marvell/libertas/if_usb.c index 176f2106bab6..4fae0e335136 100644 --- a/drivers/net/wireless/marvell/libertas/if_usb.c +++ b/drivers/net/wireless/marvell/libertas/if_usb.c @@ -193,13 +193,12 @@ static void if_usb_reset_olpc_card(struct lbs_private *priv) static int if_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { + struct usb_endpoint_descriptor *ep_in, *ep_out; struct usb_device *udev; struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; struct lbs_private *priv; struct if_usb_card *cardp; int r = -ENOMEM; - int i; udev = interface_to_usbdev(intf); @@ -224,27 +223,27 @@ static int if_usb_probe(struct usb_interface *intf, init_usb_anchor(&cardp->rx_submitted); init_usb_anchor(&cardp->tx_submitted); - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - if (usb_endpoint_is_bulk_in(endpoint)) { - cardp->ep_in_size = le16_to_cpu(endpoint->wMaxPacketSize); - cardp->ep_in = usb_endpoint_num(endpoint); - - lbs_deb_usbd(&udev->dev, "in_endpoint = %d\n", cardp->ep_in); - lbs_deb_usbd(&udev->dev, "Bulk in size is %d\n", cardp->ep_in_size); - - } else if (usb_endpoint_is_bulk_out(endpoint)) { - cardp->ep_out_size = le16_to_cpu(endpoint->wMaxPacketSize); - cardp->ep_out = usb_endpoint_num(endpoint); - - lbs_deb_usbd(&udev->dev, "out_endpoint = %d\n", cardp->ep_out); - lbs_deb_usbd(&udev->dev, "Bulk out size is %d\n", cardp->ep_out_size); - } - } - if (!cardp->ep_out_size || !cardp->ep_in_size) { + if (usb_find_common_endpoints_reverse(iface_desc, &ep_in, &ep_out, NULL, NULL)) { lbs_deb_usbd(&udev->dev, "Endpoints not found\n"); goto dealloc; } + + cardp->ep_in_size = usb_endpoint_maxp(ep_in); + cardp->ep_in = usb_endpoint_num(ep_in); + + lbs_deb_usbd(&udev->dev, "in_endpoint = %d\n", cardp->ep_in); + lbs_deb_usbd(&udev->dev, "Bulk in size is %d\n", cardp->ep_in_size); + + cardp->ep_out_size = usb_endpoint_maxp(ep_out); + cardp->ep_out = usb_endpoint_num(ep_out); + + lbs_deb_usbd(&udev->dev, "out_endpoint = %d\n", cardp->ep_out); + lbs_deb_usbd(&udev->dev, "Bulk out size is %d\n", cardp->ep_out_size); + + if (!cardp->ep_out_size || !cardp->ep_in_size) { + lbs_deb_usbd(&udev->dev, "Endpoints not valid\n"); + goto dealloc; + } if (!(cardp->rx_urb = usb_alloc_urb(0, GFP_KERNEL))) { lbs_deb_usbd(&udev->dev, "Rx URB allocation failed\n"); goto dealloc; From e801194b604cd9fdb24c2958892fd79e1aa3b651 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 7 Apr 2026 17:11:11 +0200 Subject: [PATCH 115/124] wifi: libertas_tf: refactor endpoint lookup Use the common USB helpers for looking up bulk and interrupt endpoints (and determining max packet size) instead of open coding. Note that the driver has an implicit max packet size check which is kept. Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260407151111.3187826-4-johan@kernel.org Signed-off-by: Johannes Berg --- .../net/wireless/marvell/libertas_tf/if_usb.c | 48 +++++++++---------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/drivers/net/wireless/marvell/libertas_tf/if_usb.c b/drivers/net/wireless/marvell/libertas_tf/if_usb.c index 07b38f2b8f58..b85c6d783bf7 100644 --- a/drivers/net/wireless/marvell/libertas_tf/if_usb.c +++ b/drivers/net/wireless/marvell/libertas_tf/if_usb.c @@ -144,12 +144,12 @@ static const struct lbtf_ops if_usb_ops = { static int if_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { + struct usb_endpoint_descriptor *ep_in, *ep_out; struct usb_device *udev; struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; struct lbtf_private *priv; struct if_usb_card *cardp; - int i; + int ret; lbtf_deb_enter(LBTF_DEB_USB); udev = interface_to_usbdev(intf); @@ -171,31 +171,27 @@ static int if_usb_probe(struct usb_interface *intf, udev->descriptor.bDeviceSubClass, udev->descriptor.bDeviceProtocol); - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - if (usb_endpoint_is_bulk_in(endpoint)) { - cardp->ep_in_size = - le16_to_cpu(endpoint->wMaxPacketSize); - cardp->ep_in = usb_endpoint_num(endpoint); - - lbtf_deb_usbd(&udev->dev, "in_endpoint = %d\n", - cardp->ep_in); - lbtf_deb_usbd(&udev->dev, "Bulk in size is %d\n", - cardp->ep_in_size); - } else if (usb_endpoint_is_bulk_out(endpoint)) { - cardp->ep_out_size = - le16_to_cpu(endpoint->wMaxPacketSize); - cardp->ep_out = usb_endpoint_num(endpoint); - - lbtf_deb_usbd(&udev->dev, "out_endpoint = %d\n", - cardp->ep_out); - lbtf_deb_usbd(&udev->dev, "Bulk out size is %d\n", - cardp->ep_out_size); - } - } - if (!cardp->ep_out_size || !cardp->ep_in_size) { + ret = usb_find_common_endpoints_reverse(iface_desc, &ep_in, &ep_out, + NULL, NULL); + if (ret) { lbtf_deb_usbd(&udev->dev, "Endpoints not found\n"); - /* Endpoints not found */ + goto dealloc; + } + + cardp->ep_in_size = usb_endpoint_maxp(ep_in); + cardp->ep_in = usb_endpoint_num(ep_in); + + lbtf_deb_usbd(&udev->dev, "in_endpoint = %d\n", cardp->ep_in); + lbtf_deb_usbd(&udev->dev, "Bulk in size is %d\n", cardp->ep_in_size); + + cardp->ep_out_size = usb_endpoint_maxp(ep_out); + cardp->ep_out = usb_endpoint_num(ep_out); + + lbtf_deb_usbd(&udev->dev, "out_endpoint = %d\n", cardp->ep_out); + lbtf_deb_usbd(&udev->dev, "Bulk out size is %d\n", cardp->ep_out_size); + + if (!cardp->ep_out_size || !cardp->ep_in_size) { + lbtf_deb_usbd(&udev->dev, "Endpoints not valid\n"); goto dealloc; } From ea06baf59bd4b83c2cb13698411909e5e6be001e Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 7 Apr 2026 20:06:46 -0700 Subject: [PATCH 116/124] wifi: ipw2x00: Rename michael_mic() to libipw_michael_mic() Rename the driver-local michael_mic() function to libipw_michael_mic() to prevent a name conflict with the common michael_mic() function. Note that this code will be superseded later when libipw starts using the common michael_mic(). This commit just prevents a bisection hazard. Signed-off-by: Eric Biggers Link: https://patch.msgid.link/20260408030651.80336-2-ebiggers@kernel.org Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/ipw2x00/libipw_crypto_tkip.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/ipw2x00/libipw_crypto_tkip.c b/drivers/net/wireless/intel/ipw2x00/libipw_crypto_tkip.c index c6b0de8d91ae..c2cd6808fd0f 100644 --- a/drivers/net/wireless/intel/ipw2x00/libipw_crypto_tkip.c +++ b/drivers/net/wireless/intel/ipw2x00/libipw_crypto_tkip.c @@ -464,7 +464,7 @@ static int libipw_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv) return keyidx; } -static int michael_mic(struct crypto_shash *tfm_michael, u8 *key, u8 *hdr, +static int libipw_michael_mic(struct crypto_shash *tfm_michael, u8 *key, u8 *hdr, u8 *data, size_t data_len, u8 *mic) { SHASH_DESC_ON_STACK(desc, tfm_michael); @@ -546,7 +546,7 @@ static int libipw_michael_mic_add(struct sk_buff *skb, int hdr_len, michael_mic_hdr(skb, tkey->tx_hdr); pos = skb_put(skb, 8); - if (michael_mic(tkey->tx_tfm_michael, &tkey->key[16], tkey->tx_hdr, + if (libipw_michael_mic(tkey->tx_tfm_michael, &tkey->key[16], tkey->tx_hdr, skb->data + hdr_len, skb->len - 8 - hdr_len, pos)) return -1; @@ -584,7 +584,7 @@ static int libipw_michael_mic_verify(struct sk_buff *skb, int keyidx, return -1; michael_mic_hdr(skb, tkey->rx_hdr); - if (michael_mic(tkey->rx_tfm_michael, &tkey->key[24], tkey->rx_hdr, + if (libipw_michael_mic(tkey->rx_tfm_michael, &tkey->key[24], tkey->rx_hdr, skb->data + hdr_len, skb->len - 8 - hdr_len, mic)) return -1; if (memcmp(mic, skb->data + skb->len - 8, 8) != 0) { From 613c83766884503f0f6bfdc45964c84b5286091c Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 7 Apr 2026 20:06:47 -0700 Subject: [PATCH 117/124] wifi: mac80211, cfg80211: Export michael_mic() and move it to cfg80211 Export michael_mic() so that the ath11k and ath12k drivers can call it. In addition, move it from mac80211 to cfg80211 so that the ipw2x00 drivers, which depend on cfg80211 but not mac80211, can also call it. Currently these drivers have their own local implementations of michael_mic() based on crypto_shash, which is redundant and inefficient. By consolidating all the Michael MIC code into cfg80211, we'll be able to remove the duplicate Michael MIC code in the crypto/ directory. Signed-off-by: Eric Biggers Link: https://patch.msgid.link/20260408030651.80336-3-ebiggers@kernel.org Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 5 +++++ net/mac80211/Makefile | 1 - net/mac80211/michael.h | 22 ------------------- net/mac80211/wpa.c | 1 - net/wireless/Makefile | 2 +- .../michael.c => wireless/michael-mic.c} | 5 ++++- 6 files changed, 10 insertions(+), 26 deletions(-) delete mode 100644 net/mac80211/michael.h rename net/{mac80211/michael.c => wireless/michael-mic.c} (96%) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index ffa8f9f77efe..23f9df9be837 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1921,6 +1921,11 @@ enum ieee80211_radio_measurement_actioncode { #define PMK_MAX_LEN 64 #define SAE_PASSWORD_MAX_LEN 128 +#define MICHAEL_MIC_LEN 8 + +void michael_mic(const u8 *key, struct ieee80211_hdr *hdr, + const u8 *data, size_t data_len, u8 *mic); + /* Public action codes (IEEE Std 802.11-2016, 9.6.8.1, Table 9-307) */ enum ieee80211_pub_actioncode { WLAN_PUB_ACTION_20_40_BSS_COEX = 0, diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index abf46c951299..20c3135b73ea 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -18,7 +18,6 @@ mac80211-y := \ iface.o \ link.o \ rate.o \ - michael.o \ tkip.o \ aes_cmac.o \ aes_gmac.o \ diff --git a/net/mac80211/michael.h b/net/mac80211/michael.h deleted file mode 100644 index a7fdb8e84615..000000000000 --- a/net/mac80211/michael.h +++ /dev/null @@ -1,22 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Michael MIC implementation - optimized for TKIP MIC operations - * Copyright 2002-2003, Instant802 Networks, Inc. - */ - -#ifndef MICHAEL_H -#define MICHAEL_H - -#include -#include - -#define MICHAEL_MIC_LEN 8 - -struct michael_mic_ctx { - u32 l, r; -}; - -void michael_mic(const u8 *key, struct ieee80211_hdr *hdr, - const u8 *data, size_t data_len, u8 *mic); - -#endif /* MICHAEL_H */ diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index 64a57475ce50..724ec831a885 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -18,7 +18,6 @@ #include #include "ieee80211_i.h" -#include "michael.h" #include "tkip.h" #include "aes_ccm.h" #include "aes_cmac.h" diff --git a/net/wireless/Makefile b/net/wireless/Makefile index 62a83faf0e07..a77fd5ba6368 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -8,7 +8,7 @@ obj-$(CONFIG_WEXT_PRIV) += wext-priv.o cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o ocb.o -cfg80211-y += pmsr.o +cfg80211-y += michael-mic.o pmsr.o cfg80211-$(CONFIG_OF) += of.o cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o diff --git a/net/mac80211/michael.c b/net/wireless/michael-mic.c similarity index 96% rename from net/mac80211/michael.c rename to net/wireless/michael-mic.c index 8a1afc93e749..50cdb67f0503 100644 --- a/net/mac80211/michael.c +++ b/net/wireless/michael-mic.c @@ -8,7 +8,9 @@ #include #include -#include "michael.h" +struct michael_mic_ctx { + u32 l, r; +}; static void michael_block(struct michael_mic_ctx *mctx, u32 val) { @@ -81,3 +83,4 @@ void michael_mic(const u8 *key, struct ieee80211_hdr *hdr, put_unaligned_le32(mctx.l, mic); put_unaligned_le32(mctx.r, mic + 4); } +EXPORT_SYMBOL_GPL(michael_mic); From 295e476b8217345ba25ff69d8e78c842771c31a8 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 7 Apr 2026 20:06:48 -0700 Subject: [PATCH 118/124] wifi: ath11k: Use michael_mic() from cfg80211 Just use the michael_mic() function from cfg80211 instead of a local implementation of it using the crypto_shash API. Note: when the kernel is booted with fips=1, crypto_alloc_shash("michael_mic", 0, 0) always returned ERR_PTR(-ENOENT), because Michael MIC is not a "FIPS allowed" algorithm. For now, just preserve that behavior exactly, to ensure that TKIP is not allowed to be used in FIPS mode. This logic actually seems to disable the entire driver in FIPS mode and not just TKIP, but that was the existing behavior. Supporting this driver in FIPS mode, if anyone actually needs it there, should be a separate commit. Signed-off-by: Eric Biggers Link: https://patch.msgid.link/20260408030651.80336-4-ebiggers@kernel.org Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath11k/Kconfig | 1 - drivers/net/wireless/ath/ath11k/dp.c | 2 - drivers/net/wireless/ath/ath11k/dp_rx.c | 60 +++---------------------- drivers/net/wireless/ath/ath11k/peer.h | 1 - 4 files changed, 7 insertions(+), 57 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/Kconfig b/drivers/net/wireless/ath/ath11k/Kconfig index 47dfd39caa89..385513cfdc30 100644 --- a/drivers/net/wireless/ath/ath11k/Kconfig +++ b/drivers/net/wireless/ath/ath11k/Kconfig @@ -2,7 +2,6 @@ config ATH11K tristate "Qualcomm Technologies 802.11ax chipset support" depends on MAC80211 && HAS_DMA - select CRYPTO_MICHAEL_MIC select ATH_COMMON select QCOM_QMI_HELPERS help diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c index c940de285276..bbb86f165141 100644 --- a/drivers/net/wireless/ath/ath11k/dp.c +++ b/drivers/net/wireless/ath/ath11k/dp.c @@ -5,7 +5,6 @@ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ -#include #include #include "core.h" #include "dp_tx.h" @@ -39,7 +38,6 @@ void ath11k_dp_peer_cleanup(struct ath11k *ar, int vdev_id, const u8 *addr) ath11k_peer_rx_tid_cleanup(ar, peer); peer->dp_setup_done = false; - crypto_free_shash(peer->tfm_mmic); spin_unlock_bh(&ab->base_lock); } diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 85defe11750d..fe79109adc70 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -4,10 +4,10 @@ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ +#include #include #include #include -#include #include "core.h" #include "debug.h" #include "debugfs_htt_stats.h" @@ -3182,16 +3182,13 @@ static void ath11k_dp_rx_frag_timer(struct timer_list *timer) int ath11k_peer_rx_frag_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id) { struct ath11k_base *ab = ar->ab; - struct crypto_shash *tfm; struct ath11k_peer *peer; struct dp_rx_tid *rx_tid; int i; - tfm = crypto_alloc_shash("michael_mic", 0, 0); - if (IS_ERR(tfm)) { - ath11k_warn(ab, "failed to allocate michael_mic shash: %ld\n", - PTR_ERR(tfm)); - return PTR_ERR(tfm); + if (fips_enabled) { + ath11k_warn(ab, "This driver is disabled due to FIPS\n"); + return -ENOENT; } spin_lock_bh(&ab->base_lock); @@ -3200,7 +3197,6 @@ int ath11k_peer_rx_frag_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id if (!peer) { ath11k_warn(ab, "failed to find the peer to set up fragment info\n"); spin_unlock_bh(&ab->base_lock); - crypto_free_shash(tfm); return -ENOENT; } @@ -3211,54 +3207,12 @@ int ath11k_peer_rx_frag_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id skb_queue_head_init(&rx_tid->rx_frags); } - peer->tfm_mmic = tfm; peer->dp_setup_done = true; spin_unlock_bh(&ab->base_lock); return 0; } -static int ath11k_dp_rx_h_michael_mic(struct crypto_shash *tfm, u8 *key, - struct ieee80211_hdr *hdr, u8 *data, - size_t data_len, u8 *mic) -{ - SHASH_DESC_ON_STACK(desc, tfm); - u8 mic_hdr[16] = {}; - u8 tid = 0; - int ret; - - if (!tfm) - return -EINVAL; - - desc->tfm = tfm; - - ret = crypto_shash_setkey(tfm, key, 8); - if (ret) - goto out; - - ret = crypto_shash_init(desc); - if (ret) - goto out; - - /* TKIP MIC header */ - memcpy(mic_hdr, ieee80211_get_DA(hdr), ETH_ALEN); - memcpy(mic_hdr + ETH_ALEN, ieee80211_get_SA(hdr), ETH_ALEN); - if (ieee80211_is_data_qos(hdr->frame_control)) - tid = ieee80211_get_tid(hdr); - mic_hdr[12] = tid; - - ret = crypto_shash_update(desc, mic_hdr, 16); - if (ret) - goto out; - ret = crypto_shash_update(desc, data, data_len); - if (ret) - goto out; - ret = crypto_shash_final(desc, mic); -out: - shash_desc_zero(desc); - return ret; -} - static int ath11k_dp_rx_h_verify_tkip_mic(struct ath11k *ar, struct ath11k_peer *peer, struct sk_buff *msdu) { @@ -3267,7 +3221,7 @@ static int ath11k_dp_rx_h_verify_tkip_mic(struct ath11k *ar, struct ath11k_peer struct ieee80211_key_conf *key_conf; struct ieee80211_hdr *hdr; u8 mic[IEEE80211_CCMP_MIC_LEN]; - int head_len, tail_len, ret; + int head_len, tail_len; size_t data_len; u32 hdr_len, hal_rx_desc_sz = ar->ab->hw_params.hal_desc_sz; u8 *key, *data; @@ -3293,8 +3247,8 @@ static int ath11k_dp_rx_h_verify_tkip_mic(struct ath11k *ar, struct ath11k_peer data_len = msdu->len - head_len - tail_len; key = &key_conf->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY]; - ret = ath11k_dp_rx_h_michael_mic(peer->tfm_mmic, key, hdr, data, data_len, mic); - if (ret || memcmp(mic, data + data_len, IEEE80211_CCMP_MIC_LEN)) + michael_mic(key, hdr, data, data_len, mic); + if (memcmp(mic, data + data_len, IEEE80211_CCMP_MIC_LEN)) goto mic_fail; return 0; diff --git a/drivers/net/wireless/ath/ath11k/peer.h b/drivers/net/wireless/ath/ath11k/peer.h index 3ad2f3355b14..f5ef1a27f8f2 100644 --- a/drivers/net/wireless/ath/ath11k/peer.h +++ b/drivers/net/wireless/ath/ath11k/peer.h @@ -29,7 +29,6 @@ struct ath11k_peer { /* Info used in MMIC verification of * RX fragments */ - struct crypto_shash *tfm_mmic; u8 mcast_keyidx; u8 ucast_keyidx; u16 sec_type; From 65abaa9e722ef29ce79dbc6034195961aa33954c Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 7 Apr 2026 20:06:49 -0700 Subject: [PATCH 119/124] wifi: ath12k: Use michael_mic() from cfg80211 Just use the michael_mic() function from cfg80211 instead of a local implementation of it using the crypto_shash API. Note: when the kernel is booted with fips=1, crypto_alloc_shash("michael_mic", 0, 0) always returned ERR_PTR(-ENOENT), because Michael MIC is not a "FIPS allowed" algorithm. For now, just preserve that behavior exactly, to ensure that TKIP is not allowed to be used in FIPS mode. This logic actually seems to disable the entire driver in FIPS mode and not just TKIP, but that was the existing behavior. Supporting this driver in FIPS mode, if anyone actually needs it there, should be a separate commit. Signed-off-by: Eric Biggers Link: https://patch.msgid.link/20260408030651.80336-5-ebiggers@kernel.org Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath12k/Kconfig | 1 - drivers/net/wireless/ath/ath12k/dp.c | 2 - drivers/net/wireless/ath/ath12k/dp_peer.h | 1 - drivers/net/wireless/ath/ath12k/dp_rx.c | 55 ++----------------- drivers/net/wireless/ath/ath12k/dp_rx.h | 4 -- drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c | 7 +-- 6 files changed, 8 insertions(+), 62 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/Kconfig b/drivers/net/wireless/ath/ath12k/Kconfig index 1ea1af1b8f6c..d39c075758bd 100644 --- a/drivers/net/wireless/ath/ath12k/Kconfig +++ b/drivers/net/wireless/ath/ath12k/Kconfig @@ -2,7 +2,6 @@ config ATH12K tristate "Qualcomm Technologies Wi-Fi 7 support (ath12k)" depends on MAC80211 && HAS_DMA && PCI - select CRYPTO_MICHAEL_MIC select QCOM_QMI_HELPERS select MHI_BUS select QRTR diff --git a/drivers/net/wireless/ath/ath12k/dp.c b/drivers/net/wireless/ath/ath12k/dp.c index 1c82d927d27b..90802ed1aa59 100644 --- a/drivers/net/wireless/ath/ath12k/dp.c +++ b/drivers/net/wireless/ath/ath12k/dp.c @@ -4,7 +4,6 @@ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ -#include #include "core.h" #include "dp_tx.h" #include "hif.h" @@ -41,7 +40,6 @@ void ath12k_dp_peer_cleanup(struct ath12k *ar, int vdev_id, const u8 *addr) } ath12k_dp_rx_peer_tid_cleanup(ar, peer); - crypto_free_shash(peer->dp_peer->tfm_mmic); peer->dp_peer->dp_setup_done = false; spin_unlock_bh(&dp->dp_lock); } diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.h b/drivers/net/wireless/ath/ath12k/dp_peer.h index 20294ff09513..113b8040010f 100644 --- a/drivers/net/wireless/ath/ath12k/dp_peer.h +++ b/drivers/net/wireless/ath/ath12k/dp_peer.h @@ -139,7 +139,6 @@ struct ath12k_dp_peer { u16 sec_type; /* Info used in MMIC verification of * RX fragments */ - struct crypto_shash *tfm_mmic; struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1]; struct ath12k_dp_link_peer __rcu *link_peers[ATH12K_NUM_MAX_LINKS]; struct ath12k_reoq_buf reoq_bufs[IEEE80211_NUM_TIDS + 1]; diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index 59088ab407d0..250459facff3 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -4,10 +4,10 @@ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ +#include #include #include #include -#include #include "core.h" #include "debug.h" #include "hw.h" @@ -1433,29 +1433,27 @@ static void ath12k_dp_rx_frag_timer(struct timer_list *timer) int ath12k_dp_rx_peer_frag_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_id) { struct ath12k_base *ab = ar->ab; - struct crypto_shash *tfm; struct ath12k_dp_link_peer *peer; struct ath12k_dp_rx_tid *rx_tid; int i; struct ath12k_dp *dp = ath12k_ab_to_dp(ab); - tfm = crypto_alloc_shash("michael_mic", 0, 0); - if (IS_ERR(tfm)) - return PTR_ERR(tfm); + if (fips_enabled) { + ath12k_warn(ab, "This driver is disabled due to FIPS\n"); + return -ENOENT; + } spin_lock_bh(&dp->dp_lock); peer = ath12k_dp_link_peer_find_by_vdev_and_addr(dp, vdev_id, peer_mac); if (!peer || !peer->dp_peer) { spin_unlock_bh(&dp->dp_lock); - crypto_free_shash(tfm); ath12k_warn(ab, "failed to find the peer to set up fragment info\n"); return -ENOENT; } if (!peer->primary_link) { spin_unlock_bh(&dp->dp_lock); - crypto_free_shash(tfm); return 0; } @@ -1466,55 +1464,12 @@ int ath12k_dp_rx_peer_frag_setup(struct ath12k *ar, const u8 *peer_mac, int vdev skb_queue_head_init(&rx_tid->rx_frags); } - peer->dp_peer->tfm_mmic = tfm; peer->dp_peer->dp_setup_done = true; spin_unlock_bh(&dp->dp_lock); return 0; } -int ath12k_dp_rx_h_michael_mic(struct crypto_shash *tfm, u8 *key, - struct ieee80211_hdr *hdr, u8 *data, - size_t data_len, u8 *mic) -{ - SHASH_DESC_ON_STACK(desc, tfm); - u8 mic_hdr[16] = {}; - u8 tid = 0; - int ret; - - if (!tfm) - return -EINVAL; - - desc->tfm = tfm; - - ret = crypto_shash_setkey(tfm, key, 8); - if (ret) - goto out; - - ret = crypto_shash_init(desc); - if (ret) - goto out; - - /* TKIP MIC header */ - memcpy(mic_hdr, ieee80211_get_DA(hdr), ETH_ALEN); - memcpy(mic_hdr + ETH_ALEN, ieee80211_get_SA(hdr), ETH_ALEN); - if (ieee80211_is_data_qos(hdr->frame_control)) - tid = ieee80211_get_tid(hdr); - mic_hdr[12] = tid; - - ret = crypto_shash_update(desc, mic_hdr, 16); - if (ret) - goto out; - ret = crypto_shash_update(desc, data, data_len); - if (ret) - goto out; - ret = crypto_shash_final(desc, mic); -out: - shash_desc_zero(desc); - return ret; -} -EXPORT_SYMBOL(ath12k_dp_rx_h_michael_mic); - void ath12k_dp_rx_h_undecap_frag(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu, enum hal_encrypt_type enctype, u32 flags) { diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.h b/drivers/net/wireless/ath/ath12k/dp_rx.h index bd62af0c80d4..55a31e669b3b 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.h +++ b/drivers/net/wireless/ath/ath12k/dp_rx.h @@ -6,7 +6,6 @@ #ifndef ATH12K_DP_RX_H #define ATH12K_DP_RX_H -#include #include "core.h" #include "debug.h" @@ -204,9 +203,6 @@ void ath12k_dp_rx_h_sort_frags(struct ath12k_hal *hal, struct sk_buff *cur_frag); void ath12k_dp_rx_h_undecap_frag(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu, enum hal_encrypt_type enctype, u32 flags); -int ath12k_dp_rx_h_michael_mic(struct crypto_shash *tfm, u8 *key, - struct ieee80211_hdr *hdr, u8 *data, - size_t data_len, u8 *mic); int ath12k_dp_rx_ampdu_start(struct ath12k *ar, struct ieee80211_ampdu_params *params, u8 link_id); diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c index e6a934d74e85..945680b3ebdf 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c @@ -983,7 +983,7 @@ static int ath12k_wifi7_dp_rx_h_verify_tkip_mic(struct ath12k_pdev_dp *dp_pdev, struct ieee80211_key_conf *key_conf; struct ieee80211_hdr *hdr; u8 mic[IEEE80211_CCMP_MIC_LEN]; - int head_len, tail_len, ret; + int head_len, tail_len; size_t data_len; u32 hdr_len, hal_rx_desc_sz = hal->hal_desc_sz; u8 *key, *data; @@ -1011,9 +1011,8 @@ static int ath12k_wifi7_dp_rx_h_verify_tkip_mic(struct ath12k_pdev_dp *dp_pdev, data_len = msdu->len - head_len - tail_len; key = &key_conf->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY]; - ret = ath12k_dp_rx_h_michael_mic(peer->tfm_mmic, key, hdr, data, - data_len, mic); - if (ret || memcmp(mic, data + data_len, IEEE80211_CCMP_MIC_LEN)) + michael_mic(key, hdr, data, data_len, mic); + if (memcmp(mic, data + data_len, IEEE80211_CCMP_MIC_LEN)) goto mic_fail; return 0; From 32a0e1c63cdfaa9a6f1405b552b5f9eb2be61c59 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 7 Apr 2026 20:06:50 -0700 Subject: [PATCH 120/124] wifi: ipw2x00: Use michael_mic() from cfg80211 Just use the michael_mic() function from cfg80211 instead of a local implementation of it using the crypto_shash API. Signed-off-by: Eric Biggers Link: https://patch.msgid.link/20260408030651.80336-6-ebiggers@kernel.org Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/ipw2x00/Kconfig | 1 - .../intel/ipw2x00/libipw_crypto_tkip.c | 120 +----------------- 2 files changed, 5 insertions(+), 116 deletions(-) diff --git a/drivers/net/wireless/intel/ipw2x00/Kconfig b/drivers/net/wireless/intel/ipw2x00/Kconfig index b92df91adb3a..b508f14542d5 100644 --- a/drivers/net/wireless/intel/ipw2x00/Kconfig +++ b/drivers/net/wireless/intel/ipw2x00/Kconfig @@ -154,7 +154,6 @@ config LIBIPW depends on PCI && CFG80211 select WIRELESS_EXT select CRYPTO - select CRYPTO_MICHAEL_MIC select CRYPTO_LIB_ARC4 select CRC32 help diff --git a/drivers/net/wireless/intel/ipw2x00/libipw_crypto_tkip.c b/drivers/net/wireless/intel/ipw2x00/libipw_crypto_tkip.c index c2cd6808fd0f..24bb28ab7a49 100644 --- a/drivers/net/wireless/intel/ipw2x00/libipw_crypto_tkip.c +++ b/drivers/net/wireless/intel/ipw2x00/libipw_crypto_tkip.c @@ -25,8 +25,6 @@ #include #include #include -#include -#include #include #include "libipw.h" @@ -57,11 +55,6 @@ struct libipw_tkip_data { struct arc4_ctx rx_ctx_arc4; struct arc4_ctx tx_ctx_arc4; - struct crypto_shash *rx_tfm_michael; - struct crypto_shash *tx_tfm_michael; - - /* scratch buffers for virt_to_page() (crypto API) */ - u8 rx_hdr[16], tx_hdr[16]; unsigned long flags; }; @@ -89,41 +82,14 @@ static void *libipw_tkip_init(int key_idx) priv = kzalloc_obj(*priv, GFP_ATOMIC); if (priv == NULL) - goto fail; + return priv; priv->key_idx = key_idx; - - priv->tx_tfm_michael = crypto_alloc_shash("michael_mic", 0, 0); - if (IS_ERR(priv->tx_tfm_michael)) { - priv->tx_tfm_michael = NULL; - goto fail; - } - - priv->rx_tfm_michael = crypto_alloc_shash("michael_mic", 0, 0); - if (IS_ERR(priv->rx_tfm_michael)) { - priv->rx_tfm_michael = NULL; - goto fail; - } - return priv; - - fail: - if (priv) { - crypto_free_shash(priv->tx_tfm_michael); - crypto_free_shash(priv->rx_tfm_michael); - kfree(priv); - } - - return NULL; } static void libipw_tkip_deinit(void *priv) { - struct libipw_tkip_data *_priv = priv; - if (_priv) { - crypto_free_shash(_priv->tx_tfm_michael); - crypto_free_shash(_priv->rx_tfm_michael); - } kfree_sensitive(priv); } @@ -464,73 +430,6 @@ static int libipw_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv) return keyidx; } -static int libipw_michael_mic(struct crypto_shash *tfm_michael, u8 *key, u8 *hdr, - u8 *data, size_t data_len, u8 *mic) -{ - SHASH_DESC_ON_STACK(desc, tfm_michael); - int err; - - if (tfm_michael == NULL) { - pr_warn("%s(): tfm_michael == NULL\n", __func__); - return -1; - } - - desc->tfm = tfm_michael; - - if (crypto_shash_setkey(tfm_michael, key, 8)) - return -1; - - err = crypto_shash_init(desc); - if (err) - goto out; - err = crypto_shash_update(desc, hdr, 16); - if (err) - goto out; - err = crypto_shash_update(desc, data, data_len); - if (err) - goto out; - err = crypto_shash_final(desc, mic); - -out: - shash_desc_zero(desc); - return err; -} - -static void michael_mic_hdr(struct sk_buff *skb, u8 * hdr) -{ - struct ieee80211_hdr *hdr11; - - hdr11 = (struct ieee80211_hdr *)skb->data; - - switch (le16_to_cpu(hdr11->frame_control) & - (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) { - case IEEE80211_FCTL_TODS: - memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */ - memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */ - break; - case IEEE80211_FCTL_FROMDS: - memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */ - memcpy(hdr + ETH_ALEN, hdr11->addr3, ETH_ALEN); /* SA */ - break; - case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS: - memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */ - memcpy(hdr + ETH_ALEN, hdr11->addr4, ETH_ALEN); /* SA */ - break; - default: - memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */ - memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */ - break; - } - - if (ieee80211_is_data_qos(hdr11->frame_control)) { - hdr[12] = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(hdr11))) - & IEEE80211_QOS_CTL_TID_MASK; - } else - hdr[12] = 0; /* priority */ - - hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */ -} - static int libipw_michael_mic_add(struct sk_buff *skb, int hdr_len, void *priv) { @@ -544,12 +443,9 @@ static int libipw_michael_mic_add(struct sk_buff *skb, int hdr_len, return -1; } - michael_mic_hdr(skb, tkey->tx_hdr); pos = skb_put(skb, 8); - if (libipw_michael_mic(tkey->tx_tfm_michael, &tkey->key[16], tkey->tx_hdr, - skb->data + hdr_len, skb->len - 8 - hdr_len, pos)) - return -1; - + michael_mic(&tkey->key[16], (struct ieee80211_hdr *)skb->data, + skb->data + hdr_len, skb->len - 8 - hdr_len, pos); return 0; } @@ -583,10 +479,8 @@ static int libipw_michael_mic_verify(struct sk_buff *skb, int keyidx, if (!tkey->key_set) return -1; - michael_mic_hdr(skb, tkey->rx_hdr); - if (libipw_michael_mic(tkey->rx_tfm_michael, &tkey->key[24], tkey->rx_hdr, - skb->data + hdr_len, skb->len - 8 - hdr_len, mic)) - return -1; + michael_mic(&tkey->key[24], (struct ieee80211_hdr *)skb->data, + skb->data + hdr_len, skb->len - 8 - hdr_len, mic); if (memcmp(mic, skb->data + skb->len - 8, 8) != 0) { struct ieee80211_hdr *hdr; hdr = (struct ieee80211_hdr *)skb->data; @@ -614,17 +508,13 @@ static int libipw_tkip_set_key(void *key, int len, u8 * seq, void *priv) { struct libipw_tkip_data *tkey = priv; int keyidx; - struct crypto_shash *tfm = tkey->tx_tfm_michael; struct arc4_ctx *tfm2 = &tkey->tx_ctx_arc4; - struct crypto_shash *tfm3 = tkey->rx_tfm_michael; struct arc4_ctx *tfm4 = &tkey->rx_ctx_arc4; keyidx = tkey->key_idx; memset(tkey, 0, sizeof(*tkey)); tkey->key_idx = keyidx; - tkey->tx_tfm_michael = tfm; tkey->tx_ctx_arc4 = *tfm2; - tkey->rx_tfm_michael = tfm3; tkey->rx_ctx_arc4 = *tfm4; if (len == TKIP_KEY_LEN) { memcpy(tkey->key, key, TKIP_KEY_LEN); From 8c6d03b7a249ffe85ba2bda09a2a7614c0ff03db Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 7 Apr 2026 20:06:51 -0700 Subject: [PATCH 121/124] crypto: Remove michael_mic from crypto_shash API Remove the "michael_mic" crypto_shash algorithm, since it's no longer used. Its only users were wireless drivers, which have now been converted to use the michael_mic() function instead. It makes sense that no other users ever appeared: Michael MIC is an insecure algorithm that is specific to WPA TKIP, which itself was an interim security solution to replace the broken WEP standard. Acked-by: Geert Uytterhoeven Signed-off-by: Eric Biggers Acked-by: Herbert Xu Link: https://patch.msgid.link/20260408030651.80336-7-ebiggers@kernel.org Signed-off-by: Johannes Berg --- arch/arm/configs/omap2plus_defconfig | 1 - arch/arm/configs/spitz_defconfig | 1 - arch/arm64/configs/defconfig | 1 - arch/m68k/configs/amiga_defconfig | 1 - arch/m68k/configs/apollo_defconfig | 1 - arch/m68k/configs/atari_defconfig | 1 - arch/m68k/configs/bvme6000_defconfig | 1 - arch/m68k/configs/hp300_defconfig | 1 - arch/m68k/configs/mac_defconfig | 1 - arch/m68k/configs/multi_defconfig | 1 - arch/m68k/configs/mvme147_defconfig | 1 - arch/m68k/configs/mvme16x_defconfig | 1 - arch/m68k/configs/q40_defconfig | 1 - arch/m68k/configs/sun3_defconfig | 1 - arch/m68k/configs/sun3x_defconfig | 1 - arch/mips/configs/bigsur_defconfig | 1 - arch/mips/configs/decstation_64_defconfig | 1 - arch/mips/configs/decstation_defconfig | 1 - arch/mips/configs/decstation_r4k_defconfig | 1 - arch/mips/configs/gpr_defconfig | 1 - arch/mips/configs/ip32_defconfig | 1 - arch/mips/configs/lemote2f_defconfig | 1 - arch/mips/configs/malta_qemu_32r6_defconfig | 1 - arch/mips/configs/maltaaprp_defconfig | 1 - arch/mips/configs/maltasmvp_defconfig | 1 - arch/mips/configs/maltasmvp_eva_defconfig | 1 - arch/mips/configs/maltaup_defconfig | 1 - arch/mips/configs/mtx1_defconfig | 1 - arch/mips/configs/rm200_defconfig | 1 - arch/mips/configs/sb1250_swarm_defconfig | 1 - arch/parisc/configs/generic-32bit_defconfig | 1 - arch/parisc/configs/generic-64bit_defconfig | 1 - arch/powerpc/configs/g5_defconfig | 1 - arch/powerpc/configs/linkstation_defconfig | 1 - arch/powerpc/configs/mvme5100_defconfig | 1 - arch/powerpc/configs/powernv_defconfig | 1 - arch/powerpc/configs/ppc64_defconfig | 1 - arch/powerpc/configs/ppc64e_defconfig | 1 - arch/powerpc/configs/ppc6xx_defconfig | 1 - arch/powerpc/configs/ps3_defconfig | 1 - arch/s390/configs/debug_defconfig | 1 - arch/s390/configs/defconfig | 1 - arch/sh/configs/sh2007_defconfig | 1 - arch/sh/configs/titan_defconfig | 1 - arch/sh/configs/ul2_defconfig | 1 - arch/sparc/configs/sparc32_defconfig | 1 - arch/sparc/configs/sparc64_defconfig | 1 - crypto/Kconfig | 12 -- crypto/Makefile | 1 - crypto/michael_mic.c | 176 -------------------- crypto/tcrypt.c | 4 - crypto/testmgr.c | 6 - crypto/testmgr.h | 50 ------ 53 files changed, 296 deletions(-) delete mode 100644 crypto/michael_mic.c diff --git a/arch/arm/configs/omap2plus_defconfig b/arch/arm/configs/omap2plus_defconfig index 0464f6552169..ae2883d3ff0e 100644 --- a/arch/arm/configs/omap2plus_defconfig +++ b/arch/arm/configs/omap2plus_defconfig @@ -704,7 +704,6 @@ CONFIG_ROOT_NFS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ISO8859_1=y CONFIG_SECURITY=y -CONFIG_CRYPTO_MICHAEL_MIC=y CONFIG_CRYPTO_GHASH_ARM_CE=m CONFIG_CRYPTO_AES=m CONFIG_CRYPTO_AES_ARM_BS=m diff --git a/arch/arm/configs/spitz_defconfig b/arch/arm/configs/spitz_defconfig index c130af6d44d4..f116a01c3f5f 100644 --- a/arch/arm/configs/spitz_defconfig +++ b/arch/arm/configs/spitz_defconfig @@ -230,7 +230,6 @@ CONFIG_CRYPTO_TEA=m CONFIG_CRYPTO_TWOFISH=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_MD4=m -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_SHA512=m CONFIG_CRYPTO_WP512=m CONFIG_FONTS=y diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 0651a771f5c1..8b2343048465 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -1914,7 +1914,6 @@ CONFIG_CRYPTO_USER=y CONFIG_CRYPTO_CHACHA20=m CONFIG_CRYPTO_BENCHMARK=m CONFIG_CRYPTO_ECHAINIV=y -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_SHA3=m CONFIG_CRYPTO_USER_API_RNG=m CONFIG_CRYPTO_GHASH_ARM64_CE=y diff --git a/arch/m68k/configs/amiga_defconfig b/arch/m68k/configs/amiga_defconfig index c8b936bb702f..510e16f25318 100644 --- a/arch/m68k/configs/amiga_defconfig +++ b/arch/m68k/configs/amiga_defconfig @@ -538,7 +538,6 @@ CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m CONFIG_CRYPTO_MD4=m -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m CONFIG_CRYPTO_SHA1=m CONFIG_CRYPTO_SM3_GENERIC=m diff --git a/arch/m68k/configs/apollo_defconfig b/arch/m68k/configs/apollo_defconfig index fc1792495bbc..d1d8f02b4d96 100644 --- a/arch/m68k/configs/apollo_defconfig +++ b/arch/m68k/configs/apollo_defconfig @@ -495,7 +495,6 @@ CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m CONFIG_CRYPTO_MD4=m -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m CONFIG_CRYPTO_SHA1=m CONFIG_CRYPTO_SM3_GENERIC=m diff --git a/arch/m68k/configs/atari_defconfig b/arch/m68k/configs/atari_defconfig index e440c596e60b..2e207af4add2 100644 --- a/arch/m68k/configs/atari_defconfig +++ b/arch/m68k/configs/atari_defconfig @@ -515,7 +515,6 @@ CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m CONFIG_CRYPTO_MD4=m -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m CONFIG_CRYPTO_SHA1=m CONFIG_CRYPTO_SM3_GENERIC=m diff --git a/arch/m68k/configs/bvme6000_defconfig b/arch/m68k/configs/bvme6000_defconfig index 7aa352d14363..d75465289898 100644 --- a/arch/m68k/configs/bvme6000_defconfig +++ b/arch/m68k/configs/bvme6000_defconfig @@ -487,7 +487,6 @@ CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m CONFIG_CRYPTO_MD4=m -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m CONFIG_CRYPTO_SHA1=m CONFIG_CRYPTO_SM3_GENERIC=m diff --git a/arch/m68k/configs/hp300_defconfig b/arch/m68k/configs/hp300_defconfig index 0baaf2a82c61..c2011a749e10 100644 --- a/arch/m68k/configs/hp300_defconfig +++ b/arch/m68k/configs/hp300_defconfig @@ -497,7 +497,6 @@ CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m CONFIG_CRYPTO_MD4=m -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m CONFIG_CRYPTO_SHA1=m CONFIG_CRYPTO_SM3_GENERIC=m diff --git a/arch/m68k/configs/mac_defconfig b/arch/m68k/configs/mac_defconfig index 0cbbfe5aeaec..e377443c56d4 100644 --- a/arch/m68k/configs/mac_defconfig +++ b/arch/m68k/configs/mac_defconfig @@ -514,7 +514,6 @@ CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m CONFIG_CRYPTO_MD4=m -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m CONFIG_CRYPTO_SHA1=m CONFIG_CRYPTO_SM3_GENERIC=m diff --git a/arch/m68k/configs/multi_defconfig b/arch/m68k/configs/multi_defconfig index 2b96f90e1a4d..cdd0449f7141 100644 --- a/arch/m68k/configs/multi_defconfig +++ b/arch/m68k/configs/multi_defconfig @@ -601,7 +601,6 @@ CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m CONFIG_CRYPTO_MD4=m -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m CONFIG_CRYPTO_SHA1=m CONFIG_CRYPTO_SM3_GENERIC=m diff --git a/arch/m68k/configs/mvme147_defconfig b/arch/m68k/configs/mvme147_defconfig index b49264cec911..4e78ff8d793e 100644 --- a/arch/m68k/configs/mvme147_defconfig +++ b/arch/m68k/configs/mvme147_defconfig @@ -487,7 +487,6 @@ CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m CONFIG_CRYPTO_MD4=m -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m CONFIG_CRYPTO_SHA1=m CONFIG_CRYPTO_SM3_GENERIC=m diff --git a/arch/m68k/configs/mvme16x_defconfig b/arch/m68k/configs/mvme16x_defconfig index 96a974b0a766..9fc30a3236e1 100644 --- a/arch/m68k/configs/mvme16x_defconfig +++ b/arch/m68k/configs/mvme16x_defconfig @@ -488,7 +488,6 @@ CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m CONFIG_CRYPTO_MD4=m -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m CONFIG_CRYPTO_SHA1=m CONFIG_CRYPTO_SM3_GENERIC=m diff --git a/arch/m68k/configs/q40_defconfig b/arch/m68k/configs/q40_defconfig index e53361584393..c3b5fdabfe16 100644 --- a/arch/m68k/configs/q40_defconfig +++ b/arch/m68k/configs/q40_defconfig @@ -504,7 +504,6 @@ CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m CONFIG_CRYPTO_MD4=m -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m CONFIG_CRYPTO_SHA1=m CONFIG_CRYPTO_SM3_GENERIC=m diff --git a/arch/m68k/configs/sun3_defconfig b/arch/m68k/configs/sun3_defconfig index af89287c1093..ea40f990eb22 100644 --- a/arch/m68k/configs/sun3_defconfig +++ b/arch/m68k/configs/sun3_defconfig @@ -485,7 +485,6 @@ CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m CONFIG_CRYPTO_MD4=m -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m CONFIG_CRYPTO_SHA1=m CONFIG_CRYPTO_SM3_GENERIC=m diff --git a/arch/m68k/configs/sun3x_defconfig b/arch/m68k/configs/sun3x_defconfig index af210e8b77f9..e20b6a9d26cd 100644 --- a/arch/m68k/configs/sun3x_defconfig +++ b/arch/m68k/configs/sun3x_defconfig @@ -485,7 +485,6 @@ CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_AEGIS128=m CONFIG_CRYPTO_MD4=m -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m CONFIG_CRYPTO_SHA1=m CONFIG_CRYPTO_SM3_GENERIC=m diff --git a/arch/mips/configs/bigsur_defconfig b/arch/mips/configs/bigsur_defconfig index 349e9e0b4f54..3b64e151e187 100644 --- a/arch/mips/configs/bigsur_defconfig +++ b/arch/mips/configs/bigsur_defconfig @@ -222,7 +222,6 @@ CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_XCBC=m CONFIG_CRYPTO_MD4=m -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m CONFIG_CRYPTO_SHA512=m CONFIG_CRYPTO_WP512=m diff --git a/arch/mips/configs/decstation_64_defconfig b/arch/mips/configs/decstation_64_defconfig index dad98c575292..7c43352fac6b 100644 --- a/arch/mips/configs/decstation_64_defconfig +++ b/arch/mips/configs/decstation_64_defconfig @@ -180,7 +180,6 @@ CONFIG_CRYPTO_CMAC=m CONFIG_CRYPTO_XCBC=m CONFIG_CRYPTO_CRC32=m CONFIG_CRYPTO_MD4=m -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m CONFIG_CRYPTO_SHA512=m CONFIG_CRYPTO_WP512=m diff --git a/arch/mips/configs/decstation_defconfig b/arch/mips/configs/decstation_defconfig index 4e1b51a4ad90..aee10274f048 100644 --- a/arch/mips/configs/decstation_defconfig +++ b/arch/mips/configs/decstation_defconfig @@ -175,7 +175,6 @@ CONFIG_CRYPTO_CMAC=m CONFIG_CRYPTO_XCBC=m CONFIG_CRYPTO_CRC32=m CONFIG_CRYPTO_MD4=m -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m CONFIG_CRYPTO_SHA512=m CONFIG_CRYPTO_WP512=m diff --git a/arch/mips/configs/decstation_r4k_defconfig b/arch/mips/configs/decstation_r4k_defconfig index 4e550dffc23d..a1698049aa7a 100644 --- a/arch/mips/configs/decstation_r4k_defconfig +++ b/arch/mips/configs/decstation_r4k_defconfig @@ -175,7 +175,6 @@ CONFIG_CRYPTO_CMAC=m CONFIG_CRYPTO_XCBC=m CONFIG_CRYPTO_CRC32=m CONFIG_CRYPTO_MD4=m -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m CONFIG_CRYPTO_SHA512=m CONFIG_CRYPTO_WP512=m diff --git a/arch/mips/configs/gpr_defconfig b/arch/mips/configs/gpr_defconfig index 437ef6dc0b4c..fdd28a89e336 100644 --- a/arch/mips/configs/gpr_defconfig +++ b/arch/mips/configs/gpr_defconfig @@ -275,7 +275,6 @@ CONFIG_CRYPTO_AUTHENC=m CONFIG_CRYPTO_BENCHMARK=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_MD4=m -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_SHA512=m CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_ANUBIS=m diff --git a/arch/mips/configs/ip32_defconfig b/arch/mips/configs/ip32_defconfig index 7568838eb08b..68558d0d3f52 100644 --- a/arch/mips/configs/ip32_defconfig +++ b/arch/mips/configs/ip32_defconfig @@ -159,7 +159,6 @@ CONFIG_CRYPTO_PCBC=y CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_XCBC=y CONFIG_CRYPTO_MD4=y -CONFIG_CRYPTO_MICHAEL_MIC=y CONFIG_CRYPTO_SHA1=y CONFIG_CRYPTO_SHA256=y CONFIG_CRYPTO_SHA512=y diff --git a/arch/mips/configs/lemote2f_defconfig b/arch/mips/configs/lemote2f_defconfig index 8d3f20ed19b5..eb3565a3f292 100644 --- a/arch/mips/configs/lemote2f_defconfig +++ b/arch/mips/configs/lemote2f_defconfig @@ -308,7 +308,6 @@ CONFIG_CRYPTO_TWOFISH=m CONFIG_CRYPTO_LRW=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m CONFIG_CRYPTO_SHA1=m CONFIG_CRYPTO_WP512=m diff --git a/arch/mips/configs/malta_qemu_32r6_defconfig b/arch/mips/configs/malta_qemu_32r6_defconfig index accb471a1d93..46a69e8984c5 100644 --- a/arch/mips/configs/malta_qemu_32r6_defconfig +++ b/arch/mips/configs/malta_qemu_32r6_defconfig @@ -167,7 +167,6 @@ CONFIG_NLS_CODEPAGE_437=m CONFIG_NLS_ISO8859_1=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_ANUBIS=m CONFIG_CRYPTO_BLOWFISH=m diff --git a/arch/mips/configs/maltaaprp_defconfig b/arch/mips/configs/maltaaprp_defconfig index 6bda67c5f68f..74a0e5f6a886 100644 --- a/arch/mips/configs/maltaaprp_defconfig +++ b/arch/mips/configs/maltaaprp_defconfig @@ -168,7 +168,6 @@ CONFIG_NLS_CODEPAGE_437=m CONFIG_NLS_ISO8859_1=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_ANUBIS=m CONFIG_CRYPTO_BLOWFISH=m diff --git a/arch/mips/configs/maltasmvp_defconfig b/arch/mips/configs/maltasmvp_defconfig index e4082537f80f..873bfc59623b 100644 --- a/arch/mips/configs/maltasmvp_defconfig +++ b/arch/mips/configs/maltasmvp_defconfig @@ -169,7 +169,6 @@ CONFIG_NLS_CODEPAGE_437=m CONFIG_NLS_ISO8859_1=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_ANUBIS=m CONFIG_CRYPTO_BLOWFISH=m diff --git a/arch/mips/configs/maltasmvp_eva_defconfig b/arch/mips/configs/maltasmvp_eva_defconfig index 58f5af45fa98..c9230b2c4ea8 100644 --- a/arch/mips/configs/maltasmvp_eva_defconfig +++ b/arch/mips/configs/maltasmvp_eva_defconfig @@ -171,7 +171,6 @@ CONFIG_NLS_CODEPAGE_437=m CONFIG_NLS_ISO8859_1=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_ANUBIS=m CONFIG_CRYPTO_BLOWFISH=m diff --git a/arch/mips/configs/maltaup_defconfig b/arch/mips/configs/maltaup_defconfig index 9bfef7de0d1c..79fd3ccab339 100644 --- a/arch/mips/configs/maltaup_defconfig +++ b/arch/mips/configs/maltaup_defconfig @@ -167,7 +167,6 @@ CONFIG_NLS_CODEPAGE_437=m CONFIG_NLS_ISO8859_1=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_ANUBIS=m CONFIG_CRYPTO_BLOWFISH=m diff --git a/arch/mips/configs/mtx1_defconfig b/arch/mips/configs/mtx1_defconfig index 77050ae3945f..930c0178cc67 100644 --- a/arch/mips/configs/mtx1_defconfig +++ b/arch/mips/configs/mtx1_defconfig @@ -663,7 +663,6 @@ CONFIG_CRYPTO_BENCHMARK=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_MD5=y -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_ANUBIS=m CONFIG_CRYPTO_BLOWFISH=m diff --git a/arch/mips/configs/rm200_defconfig b/arch/mips/configs/rm200_defconfig index b507dc4dddd4..b1e67ff0c4f0 100644 --- a/arch/mips/configs/rm200_defconfig +++ b/arch/mips/configs/rm200_defconfig @@ -382,7 +382,6 @@ CONFIG_CRYPTO_LRW=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_XCBC=m -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_ANUBIS=m CONFIG_CRYPTO_BLOWFISH=m diff --git a/arch/mips/configs/sb1250_swarm_defconfig b/arch/mips/configs/sb1250_swarm_defconfig index ae2afff00e01..4a25b8d3e507 100644 --- a/arch/mips/configs/sb1250_swarm_defconfig +++ b/arch/mips/configs/sb1250_swarm_defconfig @@ -85,7 +85,6 @@ CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_XCBC=m CONFIG_CRYPTO_MD4=m -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_SHA512=m CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_BLOWFISH=m diff --git a/arch/parisc/configs/generic-32bit_defconfig b/arch/parisc/configs/generic-32bit_defconfig index 5444ce6405f3..a2cb2a7a02db 100644 --- a/arch/parisc/configs/generic-32bit_defconfig +++ b/arch/parisc/configs/generic-32bit_defconfig @@ -259,7 +259,6 @@ CONFIG_CRYPTO_SERPENT=m CONFIG_CRYPTO_TWOFISH=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_MD5=y -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_SHA1=y CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_DEFLATE=y diff --git a/arch/parisc/configs/generic-64bit_defconfig b/arch/parisc/configs/generic-64bit_defconfig index ce91f9d1fdbf..4dd6cf6a2cb9 100644 --- a/arch/parisc/configs/generic-64bit_defconfig +++ b/arch/parisc/configs/generic-64bit_defconfig @@ -287,7 +287,6 @@ CONFIG_CRYPTO_ECB=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_MD4=m CONFIG_CRYPTO_MD5=y -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_DEFLATE=m # CONFIG_CRYPTO_HW is not set CONFIG_PRINTK_TIME=y diff --git a/arch/powerpc/configs/g5_defconfig b/arch/powerpc/configs/g5_defconfig index 428f17b45513..466f196ee8b2 100644 --- a/arch/powerpc/configs/g5_defconfig +++ b/arch/powerpc/configs/g5_defconfig @@ -238,7 +238,6 @@ CONFIG_BOOTX_TEXT=y CONFIG_CRYPTO_BENCHMARK=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_SHA512=m CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_ANUBIS=m diff --git a/arch/powerpc/configs/linkstation_defconfig b/arch/powerpc/configs/linkstation_defconfig index b564f9e33a0d..31f84d08b6ef 100644 --- a/arch/powerpc/configs/linkstation_defconfig +++ b/arch/powerpc/configs/linkstation_defconfig @@ -129,7 +129,6 @@ CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_KERNEL=y CONFIG_DETECT_HUNG_TASK=y CONFIG_CRYPTO_PCBC=m -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_SHA1=m CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_SERPENT=m diff --git a/arch/powerpc/configs/mvme5100_defconfig b/arch/powerpc/configs/mvme5100_defconfig index fa2b3b9c5945..c82754c14e15 100644 --- a/arch/powerpc/configs/mvme5100_defconfig +++ b/arch/powerpc/configs/mvme5100_defconfig @@ -115,7 +115,6 @@ CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=20 CONFIG_CRYPTO_CBC=y CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_MD5=y -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_SHA1=m CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_DES=y diff --git a/arch/powerpc/configs/powernv_defconfig b/arch/powerpc/configs/powernv_defconfig index 9ac746cfb4be..675462b78320 100644 --- a/arch/powerpc/configs/powernv_defconfig +++ b/arch/powerpc/configs/powernv_defconfig @@ -319,7 +319,6 @@ CONFIG_XMON=y CONFIG_CRYPTO_BENCHMARK=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_SHA256=y CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_ANUBIS=m diff --git a/arch/powerpc/configs/ppc64_defconfig b/arch/powerpc/configs/ppc64_defconfig index 2b0720f2753b..e3e1cad668d9 100644 --- a/arch/powerpc/configs/ppc64_defconfig +++ b/arch/powerpc/configs/ppc64_defconfig @@ -382,7 +382,6 @@ CONFIG_CRYPTO_CAST6=m CONFIG_CRYPTO_SERPENT=m CONFIG_CRYPTO_TWOFISH=m CONFIG_CRYPTO_PCBC=m -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_SHA256=y CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_LZO=m diff --git a/arch/powerpc/configs/ppc64e_defconfig b/arch/powerpc/configs/ppc64e_defconfig index 90247b2a0ab0..e877598fe356 100644 --- a/arch/powerpc/configs/ppc64e_defconfig +++ b/arch/powerpc/configs/ppc64e_defconfig @@ -225,7 +225,6 @@ CONFIG_CRYPTO_CCM=m CONFIG_CRYPTO_GCM=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_SHA512=m CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_ANUBIS=m diff --git a/arch/powerpc/configs/ppc6xx_defconfig b/arch/powerpc/configs/ppc6xx_defconfig index 3c08f46f3d41..27d4350e8fdb 100644 --- a/arch/powerpc/configs/ppc6xx_defconfig +++ b/arch/powerpc/configs/ppc6xx_defconfig @@ -1077,7 +1077,6 @@ CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_XCBC=m -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m CONFIG_CRYPTO_SHA1=y CONFIG_CRYPTO_SHA512=m diff --git a/arch/powerpc/configs/ps3_defconfig b/arch/powerpc/configs/ps3_defconfig index 0b48d2b776c4..7cfae0b7b2f3 100644 --- a/arch/powerpc/configs/ps3_defconfig +++ b/arch/powerpc/configs/ps3_defconfig @@ -146,7 +146,6 @@ CONFIG_NLS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ISO8859_1=y CONFIG_CRYPTO_PCBC=m -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_LZO=m CONFIG_PRINTK_TIME=y CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y diff --git a/arch/s390/configs/debug_defconfig b/arch/s390/configs/debug_defconfig index 98fd0a2f51c6..b74f96eec465 100644 --- a/arch/s390/configs/debug_defconfig +++ b/arch/s390/configs/debug_defconfig @@ -794,7 +794,6 @@ CONFIG_CRYPTO_GCM=y CONFIG_CRYPTO_SEQIV=y CONFIG_CRYPTO_MD4=m CONFIG_CRYPTO_MD5=y -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m CONFIG_CRYPTO_SHA3=m CONFIG_CRYPTO_SM3_GENERIC=m diff --git a/arch/s390/configs/defconfig b/arch/s390/configs/defconfig index 0f4cedcab3ce..0c831481e43f 100644 --- a/arch/s390/configs/defconfig +++ b/arch/s390/configs/defconfig @@ -778,7 +778,6 @@ CONFIG_CRYPTO_GCM=y CONFIG_CRYPTO_SEQIV=y CONFIG_CRYPTO_MD4=m CONFIG_CRYPTO_MD5=y -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m CONFIG_CRYPTO_SHA3=m CONFIG_CRYPTO_SM3_GENERIC=m diff --git a/arch/sh/configs/sh2007_defconfig b/arch/sh/configs/sh2007_defconfig index e32d2ce72699..5d9080499485 100644 --- a/arch/sh/configs/sh2007_defconfig +++ b/arch/sh/configs/sh2007_defconfig @@ -170,7 +170,6 @@ CONFIG_CRYPTO_XTS=y CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_XCBC=y CONFIG_CRYPTO_MD4=y -CONFIG_CRYPTO_MICHAEL_MIC=y CONFIG_CRYPTO_SHA1=y CONFIG_CRYPTO_SHA256=y CONFIG_CRYPTO_SHA512=y diff --git a/arch/sh/configs/titan_defconfig b/arch/sh/configs/titan_defconfig index 896e980d04e1..00863ecb228e 100644 --- a/arch/sh/configs/titan_defconfig +++ b/arch/sh/configs/titan_defconfig @@ -246,7 +246,6 @@ CONFIG_DEBUG_KERNEL=y CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_ECB=y CONFIG_CRYPTO_MD4=m -CONFIG_CRYPTO_MICHAEL_MIC=y CONFIG_CRYPTO_SHA256=m CONFIG_CRYPTO_SHA512=m CONFIG_CRYPTO_TGR192=m diff --git a/arch/sh/configs/ul2_defconfig b/arch/sh/configs/ul2_defconfig index 0d1c858754db..00a37944b043 100644 --- a/arch/sh/configs/ul2_defconfig +++ b/arch/sh/configs/ul2_defconfig @@ -79,4 +79,3 @@ CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_CODEPAGE_932=y CONFIG_NLS_ISO8859_1=y # CONFIG_ENABLE_MUST_CHECK is not set -CONFIG_CRYPTO_MICHAEL_MIC=y diff --git a/arch/sparc/configs/sparc32_defconfig b/arch/sparc/configs/sparc32_defconfig index e021ecfb5a77..48d834acafb4 100644 --- a/arch/sparc/configs/sparc32_defconfig +++ b/arch/sparc/configs/sparc32_defconfig @@ -82,7 +82,6 @@ CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_ECB=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_MD4=y -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_SHA256=m CONFIG_CRYPTO_SHA512=m CONFIG_CRYPTO_AES=m diff --git a/arch/sparc/configs/sparc64_defconfig b/arch/sparc/configs/sparc64_defconfig index 9f3f41246ae6..632081a262ba 100644 --- a/arch/sparc/configs/sparc64_defconfig +++ b/arch/sparc/configs/sparc64_defconfig @@ -210,7 +210,6 @@ CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_XCBC=y CONFIG_CRYPTO_MD4=y -CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_SHA256=m CONFIG_CRYPTO_SHA512=m CONFIG_CRYPTO_TGR192=m diff --git a/crypto/Kconfig b/crypto/Kconfig index b4bb85e8e226..769aef52a785 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -916,18 +916,6 @@ config CRYPTO_MD5 help MD5 message digest algorithm (RFC1321), including HMAC support. -config CRYPTO_MICHAEL_MIC - tristate "Michael MIC" - select CRYPTO_HASH - help - Michael MIC (Message Integrity Code) (IEEE 802.11i) - - Defined by the IEEE 802.11i TKIP (Temporal Key Integrity Protocol), - known as WPA (Wif-Fi Protected Access). - - This algorithm is required for TKIP, but it should not be used for - other purposes because of the weakness of the algorithm. - config CRYPTO_RMD160 tristate "RIPEMD-160" select CRYPTO_HASH diff --git a/crypto/Makefile b/crypto/Makefile index 04e269117589..aa35ba03222f 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -150,7 +150,6 @@ obj-$(CONFIG_CRYPTO_ARIA) += aria_generic.o obj-$(CONFIG_CRYPTO_CHACHA20) += chacha.o CFLAGS_chacha.o += -DARCH=$(ARCH) obj-$(CONFIG_CRYPTO_DEFLATE) += deflate.o -obj-$(CONFIG_CRYPTO_MICHAEL_MIC) += michael_mic.o obj-$(CONFIG_CRYPTO_CRC32C) += crc32c-cryptoapi.o crc32c-cryptoapi-y := crc32c.o obj-$(CONFIG_CRYPTO_CRC32) += crc32-cryptoapi.o diff --git a/crypto/michael_mic.c b/crypto/michael_mic.c deleted file mode 100644 index 69ad35f524d7..000000000000 --- a/crypto/michael_mic.c +++ /dev/null @@ -1,176 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Cryptographic API - * - * Michael MIC (IEEE 802.11i/TKIP) keyed digest - * - * Copyright (c) 2004 Jouni Malinen - */ -#include -#include -#include -#include -#include -#include - - -struct michael_mic_ctx { - u32 l, r; -}; - -struct michael_mic_desc_ctx { - __le32 pending; - size_t pending_len; - - u32 l, r; -}; - -static inline u32 xswap(u32 val) -{ - return ((val & 0x00ff00ff) << 8) | ((val & 0xff00ff00) >> 8); -} - - -#define michael_block(l, r) \ -do { \ - r ^= rol32(l, 17); \ - l += r; \ - r ^= xswap(l); \ - l += r; \ - r ^= rol32(l, 3); \ - l += r; \ - r ^= ror32(l, 2); \ - l += r; \ -} while (0) - - -static int michael_init(struct shash_desc *desc) -{ - struct michael_mic_desc_ctx *mctx = shash_desc_ctx(desc); - struct michael_mic_ctx *ctx = crypto_shash_ctx(desc->tfm); - mctx->pending_len = 0; - mctx->l = ctx->l; - mctx->r = ctx->r; - - return 0; -} - - -static int michael_update(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - struct michael_mic_desc_ctx *mctx = shash_desc_ctx(desc); - - if (mctx->pending_len) { - int flen = 4 - mctx->pending_len; - if (flen > len) - flen = len; - memcpy((u8 *)&mctx->pending + mctx->pending_len, data, flen); - mctx->pending_len += flen; - data += flen; - len -= flen; - - if (mctx->pending_len < 4) - return 0; - - mctx->l ^= le32_to_cpu(mctx->pending); - michael_block(mctx->l, mctx->r); - mctx->pending_len = 0; - } - - while (len >= 4) { - mctx->l ^= get_unaligned_le32(data); - michael_block(mctx->l, mctx->r); - data += 4; - len -= 4; - } - - if (len > 0) { - mctx->pending_len = len; - memcpy(&mctx->pending, data, len); - } - - return 0; -} - - -static int michael_final(struct shash_desc *desc, u8 *out) -{ - struct michael_mic_desc_ctx *mctx = shash_desc_ctx(desc); - u8 *data = (u8 *)&mctx->pending; - - /* Last block and padding (0x5a, 4..7 x 0) */ - switch (mctx->pending_len) { - case 0: - mctx->l ^= 0x5a; - break; - case 1: - mctx->l ^= data[0] | 0x5a00; - break; - case 2: - mctx->l ^= data[0] | (data[1] << 8) | 0x5a0000; - break; - case 3: - mctx->l ^= data[0] | (data[1] << 8) | (data[2] << 16) | - 0x5a000000; - break; - } - michael_block(mctx->l, mctx->r); - /* l ^= 0; */ - michael_block(mctx->l, mctx->r); - - put_unaligned_le32(mctx->l, out); - put_unaligned_le32(mctx->r, out + 4); - - return 0; -} - - -static int michael_setkey(struct crypto_shash *tfm, const u8 *key, - unsigned int keylen) -{ - struct michael_mic_ctx *mctx = crypto_shash_ctx(tfm); - - if (keylen != 8) - return -EINVAL; - - mctx->l = get_unaligned_le32(key); - mctx->r = get_unaligned_le32(key + 4); - return 0; -} - -static struct shash_alg alg = { - .digestsize = 8, - .setkey = michael_setkey, - .init = michael_init, - .update = michael_update, - .final = michael_final, - .descsize = sizeof(struct michael_mic_desc_ctx), - .base = { - .cra_name = "michael_mic", - .cra_driver_name = "michael_mic-generic", - .cra_blocksize = 8, - .cra_ctxsize = sizeof(struct michael_mic_ctx), - .cra_module = THIS_MODULE, - } -}; - -static int __init michael_mic_init(void) -{ - return crypto_register_shash(&alg); -} - - -static void __exit michael_mic_exit(void) -{ - crypto_unregister_shash(&alg); -} - - -module_init(michael_mic_init); -module_exit(michael_mic_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Michael MIC"); -MODULE_AUTHOR("Jouni Malinen "); -MODULE_ALIAS_CRYPTO("michael_mic"); diff --git a/crypto/tcrypt.c b/crypto/tcrypt.c index aded37546137..24f0ccc76796 100644 --- a/crypto/tcrypt.c +++ b/crypto/tcrypt.c @@ -1557,10 +1557,6 @@ static int do_test(const char *alg, u32 type, u32 mask, int m, u32 num_mb) ret = min(ret, tcrypt_test("ecb(arc4)")); break; - case 17: - ret = min(ret, tcrypt_test("michael_mic")); - break; - case 18: ret = min(ret, tcrypt_test("crc32c")); break; diff --git a/crypto/testmgr.c b/crypto/testmgr.c index 4985411dedae..d5c38683bf46 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -5197,12 +5197,6 @@ static const struct alg_test_desc alg_test_descs[] = { .suite = { .hash = __VECS(md5_tv_template) } - }, { - .alg = "michael_mic", - .test = alg_test_hash, - .suite = { - .hash = __VECS(michael_mic_tv_template) - } }, { .alg = "p1363(ecdsa-nist-p192)", .test = alg_test_null, diff --git a/crypto/testmgr.h b/crypto/testmgr.h index 1c69c11c0cdb..11911bff5f79 100644 --- a/crypto/testmgr.h +++ b/crypto/testmgr.h @@ -32808,56 +32808,6 @@ static const struct comp_testvec lzorle_decomp_tv_template[] = { }, }; -/* - * Michael MIC test vectors from IEEE 802.11i - */ -#define MICHAEL_MIC_TEST_VECTORS 6 - -static const struct hash_testvec michael_mic_tv_template[] = { - { - .key = "\x00\x00\x00\x00\x00\x00\x00\x00", - .ksize = 8, - .plaintext = zeroed_string, - .psize = 0, - .digest = "\x82\x92\x5c\x1c\xa1\xd1\x30\xb8", - }, - { - .key = "\x82\x92\x5c\x1c\xa1\xd1\x30\xb8", - .ksize = 8, - .plaintext = "M", - .psize = 1, - .digest = "\x43\x47\x21\xca\x40\x63\x9b\x3f", - }, - { - .key = "\x43\x47\x21\xca\x40\x63\x9b\x3f", - .ksize = 8, - .plaintext = "Mi", - .psize = 2, - .digest = "\xe8\xf9\xbe\xca\xe9\x7e\x5d\x29", - }, - { - .key = "\xe8\xf9\xbe\xca\xe9\x7e\x5d\x29", - .ksize = 8, - .plaintext = "Mic", - .psize = 3, - .digest = "\x90\x03\x8f\xc6\xcf\x13\xc1\xdb", - }, - { - .key = "\x90\x03\x8f\xc6\xcf\x13\xc1\xdb", - .ksize = 8, - .plaintext = "Mich", - .psize = 4, - .digest = "\xd5\x5e\x10\x05\x10\x12\x89\x86", - }, - { - .key = "\xd5\x5e\x10\x05\x10\x12\x89\x86", - .ksize = 8, - .plaintext = "Michael", - .psize = 7, - .digest = "\x0a\x94\x2b\x12\x4e\xca\xa5\x46", - } -}; - /* * CRC32 test vectors */ From 3d7640b6c371a1795e6d9580695d20caf16be9a4 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 7 Apr 2026 08:43:54 +0200 Subject: [PATCH 122/124] dt-bindings: wireless: ath10k: Add quirk to skip host cap QMI requests Some firmware versions do not support the host-capability QMI request. Since this request occurs before firmware and board files are loaded, the quirk cannot be expressed in the firmware itself and must be described in the device tree. Signed-off-by: Amit Pundir Co-developed-by: David Heidelberg Signed-off-by: David Heidelberg Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260407-skip-host-cam-qmi-req-v5-1-dfa8a05c6538@ixit.cz Signed-off-by: Jeff Johnson --- .../devicetree/bindings/net/wireless/qcom,ath10k.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml b/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml index f2440d39b7eb..c21d66c7cd55 100644 --- a/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml +++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath10k.yaml @@ -171,6 +171,12 @@ properties: Quirk specifying that the firmware expects the 8bit version of the host capability QMI request + qcom,snoc-host-cap-skip-quirk: + type: boolean + description: + Quirk specifying that the firmware wants to skip the host + capability QMI request + qcom,xo-cal-data: $ref: /schemas/types.yaml#/definitions/uint32 description: @@ -292,6 +298,11 @@ allOf: required: - interrupts + - not: + required: + - qcom,snoc-host-cap-8bit-quirk + - qcom,snoc-host-cap-skip-quirk + examples: # SNoC - | From 6a7693873b20680a3c33bae0c9f9cb3185f64ade Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 7 Apr 2026 08:43:55 +0200 Subject: [PATCH 123/124] wifi: ath10k: Add device-tree quirk to skip host cap QMI requests Some firmware versions do not support the host capability QMI request. Since this request occurs before firmware-N.bin and board-M.bin are loaded, the quirk cannot be expressed in the firmware itself. The root cause is unclear, but there appears to be a generation of firmware that lacks host capability support. Without this quirk, ath10k_qmi_host_cap_send_sync() returns QMI_ERR_MALFORMED_MSG_V01 before loading the firmware. This error is not fatal - Wi-Fi services still come up successfully if the request is simply skipped. Add a device-tree quirk to skip the host capability QMI request on devices whose firmware does not support it. For example, firmware build "QC_IMAGE_VERSION_STRING=WLAN.HL.2.0.c3-00257-QCAHLSWMTPLZ-1" on Xiaomi Poco F1 phone requires this quirk. Suggested-by: Bjorn Andersson Signed-off-by: Amit Pundir Tested-by: Paul Sajna Reviewed-by: Baochen Qiang Reviewed-by: Vasanthakumar Thiagarajan Acked-by: Dmitry Baryshkov Signed-off-by: David Heidelberg Link: https://patch.msgid.link/20260407-skip-host-cam-qmi-req-v5-2-dfa8a05c6538@ixit.cz Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath10k/qmi.c | 13 ++++++++++--- drivers/net/wireless/ath/ath10k/snoc.c | 3 +++ drivers/net/wireless/ath/ath10k/snoc.h | 1 + 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c index eebd78e7ff6b..e7f90fd9e9b8 100644 --- a/drivers/net/wireless/ath/ath10k/qmi.c +++ b/drivers/net/wireless/ath/ath10k/qmi.c @@ -808,6 +808,7 @@ out: static void ath10k_qmi_event_server_arrive(struct ath10k_qmi *qmi) { struct ath10k *ar = qmi->ar; + struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); int ret; ret = ath10k_qmi_ind_register_send_sync_msg(qmi); @@ -819,9 +820,15 @@ static void ath10k_qmi_event_server_arrive(struct ath10k_qmi *qmi) return; } - ret = ath10k_qmi_host_cap_send_sync(qmi); - if (ret) - return; + /* + * Skip the host capability request for the firmware versions which + * do not support this feature. + */ + if (!test_bit(ATH10K_SNOC_FLAG_SKIP_HOST_CAP_QUIRK, &ar_snoc->flags)) { + ret = ath10k_qmi_host_cap_send_sync(qmi); + if (ret) + return; + } ret = ath10k_qmi_msa_mem_info_send_sync_msg(qmi); if (ret) diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index f72f236fb9eb..310650227578 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -1362,6 +1362,9 @@ static void ath10k_snoc_quirks_init(struct ath10k *ar) if (of_property_read_bool(dev->of_node, "qcom,snoc-host-cap-8bit-quirk")) set_bit(ATH10K_SNOC_FLAG_8BIT_HOST_CAP_QUIRK, &ar_snoc->flags); + + if (of_property_read_bool(dev->of_node, "qcom,snoc-host-cap-skip-quirk")) + set_bit(ATH10K_SNOC_FLAG_SKIP_HOST_CAP_QUIRK, &ar_snoc->flags); } int ath10k_snoc_fw_indication(struct ath10k *ar, u64 type) diff --git a/drivers/net/wireless/ath/ath10k/snoc.h b/drivers/net/wireless/ath/ath10k/snoc.h index 1ecae34687c2..46574fd8f84e 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.h +++ b/drivers/net/wireless/ath/ath10k/snoc.h @@ -51,6 +51,7 @@ enum ath10k_snoc_flags { ATH10K_SNOC_FLAG_MODEM_STOPPED, ATH10K_SNOC_FLAG_RECOVERY, ATH10K_SNOC_FLAG_8BIT_HOST_CAP_QUIRK, + ATH10K_SNOC_FLAG_SKIP_HOST_CAP_QUIRK, }; struct clk_bulk_data; From fa489a77e3267e05df95db96ba98e141ec07cbd9 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 8 Apr 2026 20:03:33 -0700 Subject: [PATCH 124/124] wifi: cfg80211: Explicitly include in michael-mic.c This happened to be included transitively via a long chain starting with , but it's less fragile to include it explicitly. Signed-off-by: Eric Biggers Link: https://patch.msgid.link/20260409030333.13024-1-ebiggers@kernel.org Signed-off-by: Johannes Berg --- net/wireless/michael-mic.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/wireless/michael-mic.c b/net/wireless/michael-mic.c index 50cdb67f0503..ec5164756e0a 100644 --- a/net/wireless/michael-mic.c +++ b/net/wireless/michael-mic.c @@ -5,6 +5,7 @@ */ #include #include +#include #include #include