2021-06-18 06:50:10

by Pkshih

[permalink] [raw]
Subject: [PATCH 00/24] rtw89: add Realtek 802.11ax driver

This driver named rtw89, which is the next generation of rtw88, supports
Realtek 8852AE 802.11ax 2x2 chip whose new features are OFDMA, DBCC,
Spatial reuse, TWT and BSS coloring; now some of them aren't implemented
though.

The chip architecture is entirely different from the chips supported by
rtw88 like RTL8822CE 802.11ac chip. First of all, register address ranges
are totally redefined, so it's impossible to reuse register definition. To
communicate with firmware, new H2C/C2H format is proposed. In order to have
better utilization, TX DMA flow is changed to two stages DMA. To provide
rich RX status information, additional RX PPDU packets are added.

Since there are so many differences mentioned above, we decide to propose
a new driver. It has many authors, they are listed in alphabetic order:

Chin-Yen Lee <[email protected]>
Ping-Ke Shih <[email protected]>
Po Hao Huang <[email protected]>
Tzu-En Huang <[email protected]>
Vincent Fann <[email protected]>
Yan-Hsuan Chuang <[email protected]>
Zong-Zhe Yang <[email protected]>

v5:
- add OFDMA feature
- improve BT coexistence performance
- improve power save mode
- support SAR
- fine tune performance
- firmware: rtw89: 8852a: update fw to v0.13.24.0
https://github.com/pkshih/linux-firmware.git
pick the commit a5b79c4790da3eb3690e23554225ef8db464f2c6
v4:
- add basic BT coexistence features
- add power save mode, so an new patch (two files) is added
- fine tune performance
- add debugfs for debugging coex, bb, ...
v3:
- fix "networking block comments" reported by checkpatch
- Add MODULE_DEVICE_TABLE() generated by Thomas Backlund <[email protected]>
- Add missed BB settings
- error handle of RX BD and DESC length
- reduce debug level of C2H ACKs
- fix rekey failure due to wrong operator
v2:
- fix compiler warnings made by W=1
Reported-by: kernel test robot <[email protected]>
- sort header file alphabetically
- fix "networking block comments" reported by checkpatch

Ping-Ke Shih (24):
rtw89: add CAM files
rtw89: add BT coexistence files
rtw89: add core and trx files
rtw89: add debug files
rtw89: add efuse files
rtw89: add files to download and communicate with firmware
rtw89: add MAC files
rtw89: implement mac80211 ops
rtw89: add pci files
rtw89: add phy files
rtw89: define register names
rtw89: add regulatory support
rtw89: 8852a: add 8852a specific files
rtw89: 8852a: add 8852a RFK files
rtw89: 8852a: add 8852a RFK tables
rtw89: 8852a: add 8852a tables (1 of 5)
rtw89: 8852a: add 8852a tables (2 of 5)
rtw89: 8852a: add 8852a tables (3 of 5)
rtw89: 8852a: add 8852a tables (4 of 5)
rtw89: 8852a: add 8852a tables (5 of 5)
rtw89: add ser to recover error reported by firmware
rtw89: add PS files
rtw89: add SAR files
rtw89: add Kconfig and Makefile

drivers/net/wireless/realtek/Kconfig | 1 +
drivers/net/wireless/realtek/Makefile | 1 +
drivers/net/wireless/realtek/rtw89/Kconfig | 50 +
drivers/net/wireless/realtek/rtw89/Makefile | 26 +
drivers/net/wireless/realtek/rtw89/cam.c | 700 +
drivers/net/wireless/realtek/rtw89/cam.h | 165 +
drivers/net/wireless/realtek/rtw89/coex.c | 5620 ++
drivers/net/wireless/realtek/rtw89/coex.h | 152 +
drivers/net/wireless/realtek/rtw89/core.c | 2359 +
drivers/net/wireless/realtek/rtw89/core.h | 3336 ++
drivers/net/wireless/realtek/rtw89/debug.c | 2404 +
drivers/net/wireless/realtek/rtw89/debug.h | 77 +
drivers/net/wireless/realtek/rtw89/efuse.c | 188 +
drivers/net/wireless/realtek/rtw89/efuse.h | 13 +
drivers/net/wireless/realtek/rtw89/fw.c | 1599 +
drivers/net/wireless/realtek/rtw89/fw.h | 1378 +
drivers/net/wireless/realtek/rtw89/mac.c | 4070 ++
drivers/net/wireless/realtek/rtw89/mac.h | 905 +
drivers/net/wireless/realtek/rtw89/mac80211.c | 667 +
drivers/net/wireless/realtek/rtw89/pci.c | 3030 +
drivers/net/wireless/realtek/rtw89/pci.h | 601 +
drivers/net/wireless/realtek/rtw89/phy.c | 2736 +
drivers/net/wireless/realtek/rtw89/phy.h | 305 +
drivers/net/wireless/realtek/rtw89/ps.c | 149 +
drivers/net/wireless/realtek/rtw89/ps.h | 15 +
drivers/net/wireless/realtek/rtw89/reg.h | 2116 +
drivers/net/wireless/realtek/rtw89/regd.c | 353 +
drivers/net/wireless/realtek/rtw89/rtw8852a.c | 2083 +
drivers/net/wireless/realtek/rtw89/rtw8852a.h | 109 +
.../net/wireless/realtek/rtw89/rtw8852a_rfk.c | 3806 ++
.../net/wireless/realtek/rtw89/rtw8852a_rfk.h | 22 +
.../realtek/rtw89/rtw8852a_rfk_table.c | 1596 +
.../realtek/rtw89/rtw8852a_rfk_table.h | 132 +
.../wireless/realtek/rtw89/rtw8852a_table.c | 48725 ++++++++++++++++
.../wireless/realtek/rtw89/rtw8852a_table.h | 28 +
drivers/net/wireless/realtek/rtw89/sar.c | 190 +
drivers/net/wireless/realtek/rtw89/sar.h | 26 +
drivers/net/wireless/realtek/rtw89/ser.c | 491 +
drivers/net/wireless/realtek/rtw89/ser.h | 15 +
drivers/net/wireless/realtek/rtw89/txrx.h | 393 +
drivers/net/wireless/realtek/rtw89/util.c | 37 +
drivers/net/wireless/realtek/rtw89/util.h | 31 +
42 files changed, 90700 insertions(+)
create mode 100644 drivers/net/wireless/realtek/rtw89/Kconfig
create mode 100644 drivers/net/wireless/realtek/rtw89/Makefile
create mode 100644 drivers/net/wireless/realtek/rtw89/cam.c
create mode 100644 drivers/net/wireless/realtek/rtw89/cam.h
create mode 100644 drivers/net/wireless/realtek/rtw89/coex.c
create mode 100644 drivers/net/wireless/realtek/rtw89/coex.h
create mode 100644 drivers/net/wireless/realtek/rtw89/core.c
create mode 100644 drivers/net/wireless/realtek/rtw89/core.h
create mode 100644 drivers/net/wireless/realtek/rtw89/debug.c
create mode 100644 drivers/net/wireless/realtek/rtw89/debug.h
create mode 100644 drivers/net/wireless/realtek/rtw89/efuse.c
create mode 100644 drivers/net/wireless/realtek/rtw89/efuse.h
create mode 100644 drivers/net/wireless/realtek/rtw89/fw.c
create mode 100644 drivers/net/wireless/realtek/rtw89/fw.h
create mode 100644 drivers/net/wireless/realtek/rtw89/mac.c
create mode 100644 drivers/net/wireless/realtek/rtw89/mac.h
create mode 100644 drivers/net/wireless/realtek/rtw89/mac80211.c
create mode 100644 drivers/net/wireless/realtek/rtw89/pci.c
create mode 100644 drivers/net/wireless/realtek/rtw89/pci.h
create mode 100644 drivers/net/wireless/realtek/rtw89/phy.c
create mode 100644 drivers/net/wireless/realtek/rtw89/phy.h
create mode 100644 drivers/net/wireless/realtek/rtw89/ps.c
create mode 100644 drivers/net/wireless/realtek/rtw89/ps.h
create mode 100644 drivers/net/wireless/realtek/rtw89/reg.h
create mode 100644 drivers/net/wireless/realtek/rtw89/regd.c
create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8852a.c
create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8852a.h
create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.c
create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.h
create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8852a_rfk_table.c
create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8852a_rfk_table.h
create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8852a_table.c
create mode 100644 drivers/net/wireless/realtek/rtw89/rtw8852a_table.h
create mode 100644 drivers/net/wireless/realtek/rtw89/sar.c
create mode 100644 drivers/net/wireless/realtek/rtw89/sar.h
create mode 100644 drivers/net/wireless/realtek/rtw89/ser.c
create mode 100644 drivers/net/wireless/realtek/rtw89/ser.h
create mode 100644 drivers/net/wireless/realtek/rtw89/txrx.h
create mode 100644 drivers/net/wireless/realtek/rtw89/util.c
create mode 100644 drivers/net/wireless/realtek/rtw89/util.h

--
2.25.1


2021-06-18 06:50:10

by Pkshih

[permalink] [raw]
Subject: [PATCH 02/24] rtw89: add BT coexistence files

BT coexistence uses TDMA-based mechanism to coordinate with WiFi and BT.
Now, we implement basic coexistence features for wide use cases, such as
HID and A2DP. More will be supported later.

Signed-off-by: Ping-Ke Shih <[email protected]>
---
drivers/net/wireless/realtek/rtw89/coex.c | 5620 +++++++++++++++++++++
drivers/net/wireless/realtek/rtw89/coex.h | 152 +
2 files changed, 5772 insertions(+)
create mode 100644 drivers/net/wireless/realtek/rtw89/coex.c
create mode 100644 drivers/net/wireless/realtek/rtw89/coex.h

diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c
new file mode 100644
index 000000000000..868aa9d69622
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw89/coex.c
@@ -0,0 +1,5620 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Copyright(c) 2019-2020 Realtek Corporation
+ */
+
+#include "coex.h"
+#include "debug.h"
+#include "fw.h"
+#include "mac.h"
+#include "ps.h"
+#include "reg.h"
+
+#define FCXDEF_STEP 50 /* MUST <= FCXMAX_STEP and match with wl fw*/
+
+enum btc_fbtc_tdma_template {
+ CXTD_OFF = 0x0,
+ CXTD_OFF_B2,
+ CXTD_OFF_EXT,
+ CXTD_FIX,
+ CXTD_PFIX,
+ CXTD_AUTO,
+ CXTD_PAUTO,
+ CXTD_AUTO2,
+ CXTD_PAUTO2,
+ CXTD_MAX,
+};
+
+enum btc_fbtc_tdma_type {
+ CXTDMA_OFF = 0x0,
+ CXTDMA_FIX = 0x1,
+ CXTDMA_AUTO = 0x2,
+ CXTDMA_AUTO2 = 0x3,
+ CXTDMA_MAX
+};
+
+enum btc_fbtc_tdma_rx_flow_ctrl {
+ CXFLC_OFF = 0x0,
+ CXFLC_NULLP = 0x1,
+ CXFLC_QOSNULL = 0x2,
+ CXFLC_CTS = 0x3,
+ CXFLC_MAX
+};
+
+enum btc_fbtc_tdma_wlan_tx_pause {
+ CXTPS_OFF = 0x0, /* no wl tx pause*/
+ CXTPS_ON = 0x1,
+ CXTPS_MAX
+};
+
+enum btc_mlme_state {
+ MLME_NO_LINK,
+ MLME_LINKING,
+ MLME_LINKED,
+};
+
+#define FCXONESLOT_VER 1
+struct btc_fbtc_1slot {
+ u8 fver;
+ u8 sid; /* slot id */
+ struct rtw89_btc_fbtc_slot slot;
+} __packed;
+
+static const struct rtw89_btc_fbtc_tdma t_def[] = {
+ [CXTD_OFF] = { CXTDMA_OFF, CXFLC_OFF, CXTPS_OFF, 0, 0, 0, 0, 0},
+ [CXTD_OFF_B2] = { CXTDMA_OFF, CXFLC_OFF, CXTPS_OFF, 0, 0, 1, 0, 0},
+ [CXTD_OFF_EXT] = { CXTDMA_OFF, CXFLC_OFF, CXTPS_OFF, 0, 0, 3, 0, 0},
+ [CXTD_FIX] = { CXTDMA_FIX, CXFLC_OFF, CXTPS_OFF, 0, 0, 0, 0, 0},
+ [CXTD_PFIX] = { CXTDMA_FIX, CXFLC_NULLP, CXTPS_ON, 0, 5, 0, 0, 0},
+ [CXTD_AUTO] = { CXTDMA_AUTO, CXFLC_OFF, CXTPS_OFF, 0, 0, 0, 0, 0},
+ [CXTD_PAUTO] = { CXTDMA_AUTO, CXFLC_NULLP, CXTPS_ON, 0, 5, 0, 0, 0},
+ [CXTD_AUTO2] = {CXTDMA_AUTO2, CXFLC_OFF, CXTPS_OFF, 0, 0, 0, 0, 0},
+ [CXTD_PAUTO2] = {CXTDMA_AUTO2, CXFLC_NULLP, CXTPS_ON, 0, 5, 0, 0, 0}
+};
+
+#define __DEF_FBTC_SLOT(__dur, __cxtbl, __cxtype) \
+ { .dur = cpu_to_le16(__dur), .cxtbl = cpu_to_le32(__cxtbl), \
+ .cxtype = cpu_to_le16(__cxtype),}
+
+static const struct rtw89_btc_fbtc_slot s_def[] = {
+ [CXST_OFF] = __DEF_FBTC_SLOT(100, 0x55555555, SLOT_MIX),
+ [CXST_B2W] = __DEF_FBTC_SLOT(5, 0x5a5a5a5a, SLOT_ISO),
+ [CXST_W1] = __DEF_FBTC_SLOT(70, 0x5a5a5a5a, SLOT_ISO),
+ [CXST_W2] = __DEF_FBTC_SLOT(70, 0x5a5a5aaa, SLOT_ISO),
+ [CXST_W2B] = __DEF_FBTC_SLOT(15, 0x5a5a5a5a, SLOT_ISO),
+ [CXST_B1] = __DEF_FBTC_SLOT(100, 0x55555555, SLOT_MIX),
+ [CXST_B2] = __DEF_FBTC_SLOT(7, 0x6a5a5a5a, SLOT_MIX),
+ [CXST_B3] = __DEF_FBTC_SLOT(5, 0x55555555, SLOT_MIX),
+ [CXST_B4] = __DEF_FBTC_SLOT(50, 0x55555555, SLOT_MIX),
+ [CXST_LK] = __DEF_FBTC_SLOT(20, 0x5a5a5a5a, SLOT_ISO),
+ [CXST_BLK] = __DEF_FBTC_SLOT(250, 0x55555555, SLOT_MIX),
+ [CXST_E2G] = __DEF_FBTC_SLOT(20, 0x6a5a5a5a, SLOT_MIX),
+ [CXST_E5G] = __DEF_FBTC_SLOT(20, 0xffffffff, SLOT_MIX),
+ [CXST_EBT] = __DEF_FBTC_SLOT(20, 0x55555555, SLOT_MIX),
+ [CXST_ENULL] = __DEF_FBTC_SLOT(7, 0xaaaaaaaa, SLOT_ISO),
+ [CXST_WLK] = __DEF_FBTC_SLOT(250, 0x6a5a6a5a, SLOT_MIX),
+ [CXST_W1FDD] = __DEF_FBTC_SLOT(35, 0xfafafafa, SLOT_ISO),
+ [CXST_B1FDD] = __DEF_FBTC_SLOT(100, 0xffffffff, SLOT_MIX),
+};
+
+static const u32 cxtbl[] = {
+ 0xffffffff, /* 0 */
+ 0xaaaaaaaa, /* 1 */
+ 0x55555555, /* 2 */
+ 0x66555555, /* 3 */
+ 0x66556655, /* 4 */
+ 0x5a5a5a5a, /* 5 */
+ 0x5a5a5aaa, /* 6 */
+ 0xaa5a5a5a, /* 7 */
+ 0x6a5a5a5a, /* 8 */
+ 0x6a5a5aaa, /* 9 */
+ 0x6a5a6a5a, /* 10 */
+ 0x6a5a6aaa, /* 11 */
+ 0x6afa5afa, /* 12 */
+ 0xaaaa5aaa, /* 13 */
+ 0xaaffffaa, /* 14 */
+ 0xaa5555aa, /* 15 */
+ 0xfafafafa, /* 16 */
+ 0xffffddff, /* 17 */
+ 0xdaffdaff, /* 18 */
+ 0xfafadafa /* 19 */
+};
+
+struct rtw89_btc_btf_tlv {
+ u8 type;
+ u8 len;
+ u8 val[1];
+} __packed;
+
+enum btc_btf_set_report_en {
+ RPT_EN_TDMA = BIT(0),
+ RPT_EN_CYCLE = BIT(1),
+ RPT_EN_MREG = BIT(2),
+ RPT_EN_BT_VER_INFO = BIT(3),
+ RPT_EN_BT_SCAN_INFO = BIT(4),
+ RPT_EN_BT_AFH_MAP = BIT(5),
+ RPT_EN_BT_DEVICE_INFO = BIT(6),
+ RPT_EN_WL_ALL = GENMASK(2, 0),
+ RPT_EN_BT_ALL = GENMASK(6, 3),
+ RPT_EN_ALL = GENMASK(6, 0),
+};
+
+#define BTF_SET_REPORT_VER 1
+struct rtw89_btc_btf_set_report {
+ u8 fver;
+ __le32 enable;
+ __le32 para;
+} __packed;
+
+#define BTF_SET_SLOT_TABLE_VER 1
+struct rtw89_btc_btf_set_slot_table {
+ u8 fver;
+ u8 tbl_num;
+ u8 buf[];
+} __packed;
+
+#define BTF_SET_MON_REG_VER 1
+struct rtw89_btc_btf_set_mon_reg {
+ u8 fver;
+ u8 reg_num;
+ u8 buf[];
+} __packed;
+
+enum btc_btf_set_cx_policy {
+ CXPOLICY_TDMA = 0x0,
+ CXPOLICY_SLOT = 0x1,
+ CXPOLICY_TYPE = 0x2,
+ CXPOLICY_MAX,
+};
+
+enum btc_b2w_scoreboard {
+ BTC_BSCB_ACT = BIT(0),
+ BTC_BSCB_ON = BIT(1),
+ BTC_BSCB_WHQL = BIT(2),
+ BTC_BSCB_BT_S1 = BIT(3),
+ BTC_BSCB_A2DP_ACT = BIT(4),
+ BTC_BSCB_RFK_RUN = BIT(5),
+ BTC_BSCB_RFK_REQ = BIT(6),
+ BTC_BSCB_LPS = BIT(7),
+ BTC_BSCB_WLRFK = BIT(11),
+ BTC_BSCB_BT_HILNA = BIT(13),
+ BTC_BSCB_BT_CONNECT = BIT(16),
+ BTC_BSCB_PATCH_CODE = BIT(30),
+ BTC_BSCB_ALL = GENMASK(30, 0),
+};
+
+enum btc_phymap {
+ BTC_PHY_0 = BIT(0),
+ BTC_PHY_1 = BIT(1),
+ BTC_PHY_ALL = BIT(0) | BIT(1),
+};
+
+enum btc_cx_state_map {
+ BTC_WIDLE = 0,
+ BTC_WBUSY_BNOSCAN,
+ BTC_WBUSY_BSCAN,
+ BTC_WSCAN_BNOSCAN,
+ BTC_WSCAN_BSCAN,
+ BTC_WLINKING
+};
+
+enum btc_ant_phase {
+ BTC_ANT_WPOWERON = 0,
+ BTC_ANT_WINIT,
+ BTC_ANT_WONLY,
+ BTC_ANT_WOFF,
+ BTC_ANT_W2G,
+ BTC_ANT_W5G,
+ BTC_ANT_W25G,
+ BTC_ANT_FREERUN,
+ BTC_ANT_WRFK,
+ BTC_ANT_BRFK,
+ BTC_ANT_MAX
+};
+
+enum btc_plt {
+ BTC_PLT_NONE = 0,
+ BTC_PLT_LTE_RX = BIT(0),
+ BTC_PLT_GNT_BT_TX = BIT(1),
+ BTC_PLT_GNT_BT_RX = BIT(2),
+ BTC_PLT_GNT_WL = BIT(3),
+ BTC_PLT_BT = BIT(1) | BIT(2),
+ BTC_PLT_ALL = 0xf
+};
+
+enum btc_cx_poicy_main_type {
+ BTC_CXP_OFF = 0,
+ BTC_CXP_OFFB,
+ BTC_CXP_OFFE,
+ BTC_CXP_FIX,
+ BTC_CXP_PFIX,
+ BTC_CXP_AUTO,
+ BTC_CXP_PAUTO,
+ BTC_CXP_AUTO2,
+ BTC_CXP_PAUTO2,
+ BTC_CXP_MANUAL,
+ BTC_CXP_USERDEF0,
+ BTC_CXP_MAIN_MAX
+};
+
+enum btc_cx_poicy_type {
+ /* TDMA off + pri: BT > WL */
+ BTC_CXP_OFF_BT = (BTC_CXP_OFF << 8) | 0,
+
+ /* TDMA off + pri: WL > BT */
+ BTC_CXP_OFF_WL = (BTC_CXP_OFF << 8) | 1,
+
+ /* TDMA off + pri: BT = WL */
+ BTC_CXP_OFF_EQ0 = (BTC_CXP_OFF << 8) | 2,
+
+ /* TDMA off + pri: BT = WL > BT_Lo */
+ BTC_CXP_OFF_EQ1 = (BTC_CXP_OFF << 8) | 3,
+
+ /* TDMA off + pri: WL = BT, BT_Rx > WL_Lo_Tx */
+ BTC_CXP_OFF_EQ2 = (BTC_CXP_OFF << 8) | 4,
+
+ /* TDMA off + pri: WL_Rx = BT, BT_HI > WL_Tx > BT_Lo */
+ BTC_CXP_OFF_EQ3 = (BTC_CXP_OFF << 8) | 5,
+
+ /* TDMA off + pri: BT_Hi > WL > BT_Lo */
+ BTC_CXP_OFF_BWB0 = (BTC_CXP_OFF << 8) | 6,
+
+ /* TDMA off + pri: WL_Hi-Tx > BT_Hi_Rx, BT_Hi > WL > BT_Lo */
+ BTC_CXP_OFF_BWB1 = (BTC_CXP_OFF << 8) | 7,
+
+ /* TDMA off+Bcn-Protect + pri: WL_Hi-Tx > BT_Hi_Rx, BT_Hi > WL > BT_Lo*/
+ BTC_CXP_OFFB_BWB0 = (BTC_CXP_OFFB << 8) | 0,
+
+ /* TDMA off + Ext-Ctrl + pri: default */
+ BTC_CXP_OFFE_DEF = (BTC_CXP_OFFE << 8) | 0,
+
+ /* TDMA off + Ext-Ctrl + pri: E2G-slot block all BT */
+ BTC_CXP_OFFE_DEF2 = (BTC_CXP_OFFE << 8) | 1,
+
+ /* TDMA Fix slot-0: W1:B1 = 30:30 */
+ BTC_CXP_FIX_TD3030 = (BTC_CXP_FIX << 8) | 0,
+
+ /* TDMA Fix slot-1: W1:B1 = 50:50 */
+ BTC_CXP_FIX_TD5050 = (BTC_CXP_FIX << 8) | 1,
+
+ /* TDMA Fix slot-2: W1:B1 = 20:30 */
+ BTC_CXP_FIX_TD2030 = (BTC_CXP_FIX << 8) | 2,
+
+ /* TDMA Fix slot-3: W1:B1 = 40:10 */
+ BTC_CXP_FIX_TD4010 = (BTC_CXP_FIX << 8) | 3,
+
+ /* TDMA Fix slot-4: W1:B1 = 70:10 */
+ BTC_CXP_FIX_TD7010 = (BTC_CXP_FIX << 8) | 4,
+
+ /* TDMA Fix slot-5: W1:B1 = 20:60 */
+ BTC_CXP_FIX_TD2060 = (BTC_CXP_FIX << 8) | 5,
+
+ /* TDMA Fix slot-6: W1:B1 = 30:60 */
+ BTC_CXP_FIX_TD3060 = (BTC_CXP_FIX << 8) | 6,
+
+ /* TDMA Fix slot-7: W1:B1 = 20:80 */
+ BTC_CXP_FIX_TD2080 = (BTC_CXP_FIX << 8) | 7,
+
+ /* TDMA Fix slot-8: W1:B1 = user-define */
+ BTC_CXP_FIX_TDW1B1 = (BTC_CXP_FIX << 8) | 8,
+
+ /* TDMA Fix slot-9: W1:B1 = 40:20 */
+ BTC_CXP_FIX_TD4020 = (BTC_CXP_FIX << 8) | 9,
+
+ /* PS-TDMA Fix slot-0: W1:B1 = 30:30 */
+ BTC_CXP_PFIX_TD3030 = (BTC_CXP_PFIX << 8) | 0,
+
+ /* PS-TDMA Fix slot-1: W1:B1 = 50:50 */
+ BTC_CXP_PFIX_TD5050 = (BTC_CXP_PFIX << 8) | 1,
+
+ /* PS-TDMA Fix slot-2: W1:B1 = 20:30 */
+ BTC_CXP_PFIX_TD2030 = (BTC_CXP_PFIX << 8) | 2,
+
+ /* PS-TDMA Fix slot-3: W1:B1 = 20:60 */
+ BTC_CXP_PFIX_TD2060 = (BTC_CXP_PFIX << 8) | 3,
+
+ /* PS-TDMA Fix slot-4: W1:B1 = 30:70 */
+ BTC_CXP_PFIX_TD3070 = (BTC_CXP_PFIX << 8) | 4,
+
+ /* PS-TDMA Fix slot-5: W1:B1 = 20:80 */
+ BTC_CXP_PFIX_TD2080 = (BTC_CXP_PFIX << 8) | 5,
+
+ /* PS-TDMA Fix slot-6: W1:B1 = user-define */
+ BTC_CXP_PFIX_TDW1B1 = (BTC_CXP_PFIX << 8) | 6,
+
+ /* TDMA Auto slot-0: W1:B1 = 50:200 */
+ BTC_CXP_AUTO_TD50200 = (BTC_CXP_AUTO << 8) | 0,
+
+ /* TDMA Auto slot-1: W1:B1 = 60:200 */
+ BTC_CXP_AUTO_TD60200 = (BTC_CXP_AUTO << 8) | 1,
+
+ /* TDMA Auto slot-2: W1:B1 = 20:200 */
+ BTC_CXP_AUTO_TD20200 = (BTC_CXP_AUTO << 8) | 2,
+
+ /* TDMA Auto slot-3: W1:B1 = user-define */
+ BTC_CXP_AUTO_TDW1B1 = (BTC_CXP_AUTO << 8) | 3,
+
+ /* PS-TDMA Auto slot-0: W1:B1 = 50:200 */
+ BTC_CXP_PAUTO_TD50200 = (BTC_CXP_PAUTO << 8) | 0,
+
+ /* PS-TDMA Auto slot-1: W1:B1 = 60:200 */
+ BTC_CXP_PAUTO_TD60200 = (BTC_CXP_PAUTO << 8) | 1,
+
+ /* PS-TDMA Auto slot-2: W1:B1 = 20:200 */
+ BTC_CXP_PAUTO_TD20200 = (BTC_CXP_PAUTO << 8) | 2,
+
+ /* PS-TDMA Auto slot-3: W1:B1 = user-define */
+ BTC_CXP_PAUTO_TDW1B1 = (BTC_CXP_PAUTO << 8) | 3,
+
+ /* TDMA Auto slot2-0: W1:B4 = 30:50 */
+ BTC_CXP_AUTO2_TD3050 = (BTC_CXP_AUTO2 << 8) | 0,
+
+ /* TDMA Auto slot2-1: W1:B4 = 30:70 */
+ BTC_CXP_AUTO2_TD3070 = (BTC_CXP_AUTO2 << 8) | 1,
+
+ /* TDMA Auto slot2-2: W1:B4 = 50:50 */
+ BTC_CXP_AUTO2_TD5050 = (BTC_CXP_AUTO2 << 8) | 2,
+
+ /* TDMA Auto slot2-3: W1:B4 = 60:60 */
+ BTC_CXP_AUTO2_TD6060 = (BTC_CXP_AUTO2 << 8) | 3,
+
+ /* TDMA Auto slot2-4: W1:B4 = 20:80 */
+ BTC_CXP_AUTO2_TD2080 = (BTC_CXP_AUTO2 << 8) | 4,
+
+ /* TDMA Auto slot2-5: W1:B4 = user-define */
+ BTC_CXP_AUTO2_TDW1B4 = (BTC_CXP_AUTO2 << 8) | 5,
+
+ /* PS-TDMA Auto slot2-0: W1:B4 = 30:50 */
+ BTC_CXP_PAUTO2_TD3050 = (BTC_CXP_PAUTO2 << 8) | 0,
+
+ /* PS-TDMA Auto slot2-1: W1:B4 = 30:70 */
+ BTC_CXP_PAUTO2_TD3070 = (BTC_CXP_PAUTO2 << 8) | 1,
+
+ /* PS-TDMA Auto slot2-2: W1:B4 = 50:50 */
+ BTC_CXP_PAUTO2_TD5050 = (BTC_CXP_PAUTO2 << 8) | 2,
+
+ /* PS-TDMA Auto slot2-3: W1:B4 = 60:60 */
+ BTC_CXP_PAUTO2_TD6060 = (BTC_CXP_PAUTO2 << 8) | 3,
+
+ /* PS-TDMA Auto slot2-4: W1:B4 = 20:80 */
+ BTC_CXP_PAUTO2_TD2080 = (BTC_CXP_PAUTO2 << 8) | 4,
+
+ /* PS-TDMA Auto slot2-5: W1:B4 = user-define */
+ BTC_CXP_PAUTO2_TDW1B4 = (BTC_CXP_PAUTO2 << 8) | 5,
+
+ BTC_CXP_MAX = 0xffff
+};
+
+enum btc_wl_rfk_result {
+ BTC_WRFK_REJECT = 0,
+ BTC_WRFK_ALLOW = 1,
+};
+
+enum btc_coex_info_map_en {
+ BTC_COEX_INFO_CX = BIT(0),
+ BTC_COEX_INFO_WL = BIT(1),
+ BTC_COEX_INFO_BT = BIT(2),
+ BTC_COEX_INFO_DM = BIT(3),
+ BTC_COEX_INFO_MREG = BIT(4),
+ BTC_COEX_INFO_SUMMARY = BIT(5),
+ BTC_COEX_INFO_ALL = GENMASK(7, 0),
+};
+
+#define BTC_CXP_MASK GENMASK(15, 8)
+
+enum btc_w2b_scoreboard {
+ BTC_WSCB_ACTIVE = BIT(0),
+ BTC_WSCB_ON = BIT(1),
+ BTC_WSCB_SCAN = BIT(2),
+ BTC_WSCB_UNDERTEST = BIT(3),
+ BTC_WSCB_RXGAIN = BIT(4),
+ BTC_WSCB_WLBUSY = BIT(7),
+ BTC_WSCB_EXTFEM = BIT(8),
+ BTC_WSCB_TDMA = BIT(9),
+ BTC_WSCB_FIX2M = BIT(10),
+ BTC_WSCB_WLRFK = BIT(11),
+ BTC_WSCB_BTRFK_GNT = BIT(12), /* not used, use mailbox to inform BT */
+ BTC_WSCB_BT_HILNA = BIT(13),
+ BTC_WSCB_BTLOG = BIT(14),
+ BTC_WSCB_ALL = GENMASK(23, 0),
+};
+
+enum btc_wl_link_mode {
+ BTC_WLINK_NOLINK = 0x0,
+ BTC_WLINK_2G_STA,
+ BTC_WLINK_2G_AP,
+ BTC_WLINK_2G_GO,
+ BTC_WLINK_2G_GC,
+ BTC_WLINK_2G_SCC,
+ BTC_WLINK_2G_MCC,
+ BTC_WLINK_25G_MCC,
+ BTC_WLINK_25G_DBCC,
+ BTC_WLINK_5G,
+ BTC_WLINK_2G_NAN,
+ BTC_WLINK_OTHER,
+ BTC_WLINK_MAX
+};
+
+enum btc_bt_hid_type {
+ BTC_HID_218 = BIT(0),
+ BTC_HID_418 = BIT(1),
+ BTC_HID_BLE = BIT(2),
+ BTC_HID_RCU = BIT(3),
+ BTC_HID_RCU_VOICE = BIT(4),
+ BTC_HID_OTHER_LEGACY = BIT(5)
+};
+
+enum btc_reset_module {
+ BTC_RESET_CX = BIT(0),
+ BTC_RESET_DM = BIT(1),
+ BTC_RESET_CTRL = BIT(2),
+ BTC_RESET_CXDM = BIT(0) | BIT(1),
+ BTC_RESET_BTINFO = BIT(3),
+ BTC_RESET_MDINFO = BIT(4),
+ BTC_RESET_ALL = GENMASK(7, 0),
+};
+
+enum btc_gnt_state {
+ BTC_GNT_HW = 0,
+ BTC_GNT_SW_LO,
+ BTC_GNT_SW_HI,
+ BTC_GNT_MAX
+};
+
+enum btc_wl_max_tx_time {
+ BTC_MAX_TX_TIME_L1 = 500,
+ BTC_MAX_TX_TIME_L2 = 1000,
+ BTC_MAX_TX_TIME_L3 = 2000,
+ BTC_MAX_TX_TIME_DEF = 5280
+};
+
+enum btc_wl_max_tx_retry {
+ BTC_MAX_TX_RETRY_L1 = 7,
+ BTC_MAX_TX_RETRY_L2 = 15,
+ BTC_MAX_TX_RETRY_DEF = 31,
+};
+
+enum btc_reason_and_action {
+ BTC_RSN_NONE,
+ BTC_RSN_NTFY_INIT,
+ BTC_RSN_NTFY_SWBAND,
+ BTC_RSN_NTFY_WL_STA,
+ BTC_RSN_NTFY_RADIO_STATE,
+ BTC_RSN_UPDATE_BT_SCBD,
+ BTC_RSN_NTFY_WL_RFK,
+ BTC_RSN_UPDATE_BT_INFO,
+ BTC_RSN_NTFY_SCAN_START,
+ BTC_RSN_NTFY_SCAN_FINISH,
+ BTC_RSN_NTFY_SPECIFIC_PACKET,
+ BTC_RSN_NTFY_POWEROFF,
+ BTC_RSN_NTFY_ROLE_INFO,
+ BTC_RSN_CMD_SET_COEX,
+ BTC_RSN_NUM,
+ BTC_ACT_NONE = 100,
+ BTC_ACT_WL_ONLY,
+ BTC_ACT_WL_5G,
+ BTC_ACT_WL_OTHER,
+ BTC_ACT_WL_IDLE,
+ BTC_ACT_WL_NC,
+ BTC_ACT_WL_RFK,
+ BTC_ACT_WL_INIT,
+ BTC_ACT_WL_OFF,
+ BTC_ACT_FREERUN,
+ BTC_ACT_BT_WHQL,
+ BTC_ACT_BT_RFK,
+ BTC_ACT_BT_OFF,
+ BTC_ACT_BT_IDLE,
+ BTC_ACT_BT_HFP,
+ BTC_ACT_BT_HID,
+ BTC_ACT_BT_A2DP,
+ BTC_ACT_BT_A2DPSINK,
+ BTC_ACT_BT_PAN,
+ BTC_ACT_BT_A2DP_HID,
+ BTC_ACT_BT_A2DP_PAN,
+ BTC_ACT_BT_PAN_HID,
+ BTC_ACT_BT_A2DP_PAN_HID,
+ BTC_ACT_WL_25G_MCC,
+ BTC_ACT_WL_2G_MCC,
+ BTC_ACT_WL_2G_SCC,
+ BTC_ACT_WL_2G_AP,
+ BTC_ACT_WL_2G_GO,
+ BTC_ACT_WL_2G_GC,
+ BTC_ACT_WL_2G_NAN,
+ BTC_ACT_LAST,
+ BTC_ACT_NUM = BTC_ACT_LAST - BTC_ACT_NONE,
+ BTC_ACT_EXT_BIT = BIT(14),
+ BTC_POLICY_EXT_BIT = BIT(15),
+};
+
+#define BTC_FREERUN_ANTISO_MIN 30
+#define BTC_TDMA_BTHID_MAX 2
+#define BTC_BLINK_NOCONNECT 0
+
+static void _run_coex(struct rtw89_dev *rtwdev,
+ enum btc_reason_and_action reason);
+static void _write_scbd(struct rtw89_dev *rtwdev, u32 val, bool state);
+static void _update_bt_scbd(struct rtw89_dev *rtwdev, bool only_update);
+
+static void _send_fw_cmd(struct rtw89_dev *rtwdev, u8 h2c_class, u8 h2c_func,
+ void *param, u16 len)
+{
+ rtw89_fw_h2c_raw_with_hdr(rtwdev, h2c_class, h2c_func, param, len,
+ false, true);
+}
+
+static void _reset_btc_var(struct rtw89_dev *rtwdev, u8 type)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_cx *cx = &btc->cx;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+ struct rtw89_btc_bt_info *bt = &btc->cx.bt;
+ struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info;
+ struct rtw89_btc_wl_link_info *wl_linfo = wl->link_info;
+ u8 i;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC] %s\n", __func__);
+
+ if (type & BTC_RESET_CX)
+ memset(cx, 0, sizeof(*cx));
+ else if (type & BTC_RESET_BTINFO) /* only for BT enable */
+ memset(bt, 0, sizeof(*bt));
+
+ if (type & BTC_RESET_CTRL) {
+ memset(&btc->ctrl, 0, sizeof(btc->ctrl));
+ btc->ctrl.trace_step = FCXDEF_STEP;
+ }
+
+ /* Init Coex variables that are not zero */
+ if (type & BTC_RESET_DM) {
+ memset(&btc->dm, 0, sizeof(btc->dm));
+ memset(bt_linfo->rssi_state, 0, sizeof(bt_linfo->rssi_state));
+
+ for (i = 0; i < RTW89_MAX_HW_PORT_NUM; i++)
+ memset(wl_linfo[i].rssi_state, 0,
+ sizeof(wl_linfo[i].rssi_state));
+
+ /* set the slot_now table to original */
+ btc->dm.tdma_now = t_def[CXTD_OFF];
+ btc->dm.tdma = t_def[CXTD_OFF];
+ memcpy(&btc->dm.slot_now, s_def, sizeof(btc->dm.slot_now));
+ memcpy(&btc->dm.slot, s_def, sizeof(btc->dm.slot));
+
+ btc->policy_len = 0;
+ btc->bt_req_len = 0;
+
+ btc->dm.coex_info_map = BTC_COEX_INFO_ALL;
+ btc->dm.wl_tx_limit.tx_time = BTC_MAX_TX_TIME_DEF;
+ btc->dm.wl_tx_limit.tx_retry = BTC_MAX_TX_RETRY_DEF;
+ }
+
+ if (type & BTC_RESET_MDINFO)
+ memset(&btc->mdinfo, 0, sizeof(btc->mdinfo));
+}
+
+#define BTC_FWINFO_BUF 1024
+
+#define BTC_RPT_HDR_SIZE 3
+#define BTC_CHK_WLSLOT_DRIFT_MAX 15
+#define BTC_CHK_HANG_MAX 3
+
+static void _chk_btc_err(struct rtw89_dev *rtwdev, u8 type, u32 cnt)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_cx *cx = &btc->cx;
+ struct rtw89_btc_dm *dm = &btc->dm;
+ struct rtw89_btc_bt_info *bt = &cx->bt;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s type:%d cnt:%d\n",
+ __func__, type, cnt);
+
+ switch (type) {
+ case BTC_DCNT_RPT_FREEZE:
+ if (dm->cnt_dm[BTC_DCNT_RPT] == cnt && btc->fwinfo.rpt_en_map)
+ dm->cnt_dm[BTC_DCNT_RPT_FREEZE]++;
+ else
+ dm->cnt_dm[BTC_DCNT_RPT_FREEZE] = 0;
+
+ if (dm->cnt_dm[BTC_DCNT_RPT_FREEZE] >= BTC_CHK_HANG_MAX)
+ dm->error.map.wl_fw_hang = true;
+ else
+ dm->error.map.wl_fw_hang = false;
+
+ dm->cnt_dm[BTC_DCNT_RPT] = cnt;
+ break;
+ case BTC_DCNT_CYCLE_FREEZE:
+ if (dm->cnt_dm[BTC_DCNT_CYCLE] == cnt &&
+ (dm->tdma_now.type != CXTDMA_OFF ||
+ dm->tdma_now.ext_ctrl == CXECTL_EXT))
+ dm->cnt_dm[BTC_DCNT_CYCLE_FREEZE]++;
+ else
+ dm->cnt_dm[BTC_DCNT_CYCLE_FREEZE] = 0;
+
+ if (dm->cnt_dm[BTC_DCNT_CYCLE_FREEZE] >= BTC_CHK_HANG_MAX)
+ dm->error.map.cycle_hang = true;
+ else
+ dm->error.map.cycle_hang = false;
+
+ dm->cnt_dm[BTC_DCNT_CYCLE] = cnt;
+ break;
+ case BTC_DCNT_W1_FREEZE:
+ if (dm->cnt_dm[BTC_DCNT_W1] == cnt &&
+ dm->tdma_now.type != CXTDMA_OFF)
+ dm->cnt_dm[BTC_DCNT_W1_FREEZE]++;
+ else
+ dm->cnt_dm[BTC_DCNT_W1_FREEZE] = 0;
+
+ if (dm->cnt_dm[BTC_DCNT_W1_FREEZE] >= BTC_CHK_HANG_MAX)
+ dm->error.map.w1_hang = true;
+ else
+ dm->error.map.w1_hang = false;
+
+ dm->cnt_dm[BTC_DCNT_W1] = cnt;
+ break;
+ case BTC_DCNT_B1_FREEZE:
+ if (dm->cnt_dm[BTC_DCNT_B1] == cnt &&
+ dm->tdma_now.type != CXTDMA_OFF)
+ dm->cnt_dm[BTC_DCNT_B1_FREEZE]++;
+ else
+ dm->cnt_dm[BTC_DCNT_B1_FREEZE] = 0;
+
+ if (dm->cnt_dm[BTC_DCNT_B1_FREEZE] >= BTC_CHK_HANG_MAX)
+ dm->error.map.b1_hang = true;
+ else
+ dm->error.map.b1_hang = false;
+
+ dm->cnt_dm[BTC_DCNT_B1] = cnt;
+ break;
+ case BTC_DCNT_TDMA_NONSYNC:
+ if (cnt != 0) /* if tdma not sync between drv/fw */
+ dm->cnt_dm[BTC_DCNT_TDMA_NONSYNC]++;
+ else
+ dm->cnt_dm[BTC_DCNT_TDMA_NONSYNC] = 0;
+
+ if (dm->cnt_dm[BTC_DCNT_TDMA_NONSYNC] >= BTC_CHK_HANG_MAX)
+ dm->error.map.tdma_no_sync = true;
+ else
+ dm->error.map.tdma_no_sync = false;
+ break;
+ case BTC_DCNT_SLOT_NONSYNC:
+ if (cnt != 0) /* if slot not sync between drv/fw */
+ dm->cnt_dm[BTC_DCNT_SLOT_NONSYNC]++;
+ else
+ dm->cnt_dm[BTC_DCNT_SLOT_NONSYNC] = 0;
+
+ if (dm->cnt_dm[BTC_DCNT_SLOT_NONSYNC] >= BTC_CHK_HANG_MAX)
+ dm->error.map.tdma_no_sync = true;
+ else
+ dm->error.map.tdma_no_sync = false;
+ break;
+ case BTC_DCNT_BTCNT_FREEZE:
+ cnt = cx->cnt_bt[BTC_BCNT_HIPRI_RX] +
+ cx->cnt_bt[BTC_BCNT_HIPRI_TX] +
+ cx->cnt_bt[BTC_BCNT_LOPRI_RX] +
+ cx->cnt_bt[BTC_BCNT_LOPRI_TX];
+
+ if (cnt == 0)
+ dm->cnt_dm[BTC_DCNT_BTCNT_FREEZE]++;
+ else
+ dm->cnt_dm[BTC_DCNT_BTCNT_FREEZE] = 0;
+
+ if ((dm->cnt_dm[BTC_DCNT_BTCNT_FREEZE] >= BTC_CHK_HANG_MAX &&
+ bt->enable.now) || (!dm->cnt_dm[BTC_DCNT_BTCNT_FREEZE] &&
+ !bt->enable.now))
+ _update_bt_scbd(rtwdev, false);
+ break;
+ case BTC_DCNT_WL_SLOT_DRIFT:
+ if (cnt >= BTC_CHK_WLSLOT_DRIFT_MAX)
+ dm->cnt_dm[BTC_DCNT_WL_SLOT_DRIFT]++;
+ else
+ dm->cnt_dm[BTC_DCNT_WL_SLOT_DRIFT] = 0;
+
+ if (dm->cnt_dm[BTC_DCNT_WL_SLOT_DRIFT] >= BTC_CHK_HANG_MAX)
+ dm->error.map.wl_slot_drift = true;
+ else
+ dm->error.map.wl_slot_drift = false;
+ break;
+ }
+}
+
+static void _update_bt_report(struct rtw89_dev *rtwdev, u8 rpt_type, u8 *pfinfo)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_bt_info *bt = &btc->cx.bt;
+ struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info;
+ struct rtw89_btc_bt_a2dp_desc *a2dp = &bt_linfo->a2dp_desc;
+ struct rtw89_btc_fbtc_btver *pver = NULL;
+ struct rtw89_btc_fbtc_btscan *pscan = NULL;
+ struct rtw89_btc_fbtc_btafh *pafh = NULL;
+ struct rtw89_btc_fbtc_btdevinfo *pdev = NULL;
+
+ pver = (struct rtw89_btc_fbtc_btver *)pfinfo;
+ pscan = (struct rtw89_btc_fbtc_btscan *)pfinfo;
+ pafh = (struct rtw89_btc_fbtc_btafh *)pfinfo;
+ pdev = (struct rtw89_btc_fbtc_btdevinfo *)pfinfo;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s rpt_type:%d\n",
+ __func__, rpt_type);
+
+ switch (rpt_type) {
+ case BTC_RPT_TYPE_BT_VER:
+ bt->ver_info.fw = le32_to_cpu(pver->fw_ver);
+ bt->ver_info.fw_coex = le32_get_bits(pver->coex_ver, GENMASK(7, 0));
+ bt->feature = le32_to_cpu(pver->feature);
+ break;
+ case BTC_RPT_TYPE_BT_SCAN:
+ memcpy(bt->scan_info, pscan->scan, BTC_SCAN_MAX1);
+ break;
+ case BTC_RPT_TYPE_BT_AFH:
+ memcpy(&bt_linfo->afh_map[0], pafh->afh_l, 4);
+ memcpy(&bt_linfo->afh_map[4], pafh->afh_m, 4);
+ memcpy(&bt_linfo->afh_map[8], pafh->afh_h, 2);
+ break;
+ case BTC_RPT_TYPE_BT_DEVICE:
+ a2dp->device_name = le32_to_cpu(pdev->dev_name);
+ a2dp->vendor_id = le16_to_cpu(pdev->vendor_id);
+ a2dp->flush_time = le32_to_cpu(pdev->flush_time);
+ break;
+ default:
+ break;
+ }
+}
+
+struct rtw89_btc_fbtc_cysta_cpu {
+ u8 fver;
+ u8 rsvd;
+ u16 cycles;
+ u16 cycles_a2dp[CXT_FLCTRL_MAX];
+ u16 a2dpept;
+ u16 a2dpeptto;
+ u16 tavg_cycle[CXT_MAX];
+ u16 tmax_cycle[CXT_MAX];
+ u16 tmaxdiff_cycle[CXT_MAX];
+ u16 tavg_a2dp[CXT_FLCTRL_MAX];
+ u16 tmax_a2dp[CXT_FLCTRL_MAX];
+ u16 tavg_a2dpept;
+ u16 tmax_a2dpept;
+ u16 tavg_lk;
+ u16 tmax_lk;
+ u32 slot_cnt[CXST_MAX];
+ u32 bcn_cnt[CXBCN_MAX];
+ u32 leakrx_cnt;
+ u32 collision_cnt;
+ u32 skip_cnt;
+ u32 exception;
+ u32 except_cnt;
+#if (FCXCYSTA_VER > 1)
+ u16 tslot_cycle[BTC_CYCLE_SLOT_MAX];
+#endif
+};
+
+static void rtw89_btc_fbtc_cysta_to_cpu(const struct rtw89_btc_fbtc_cysta *src,
+ struct rtw89_btc_fbtc_cysta_cpu *dst)
+{
+ static_assert(sizeof(*src) == sizeof(*dst));
+
+#define __CPY_U8(_x) ({dst->_x = src->_x; })
+#define __CPY_LE16(_x) ({dst->_x = le16_to_cpu(src->_x); })
+#define __CPY_LE16S(_x) ({int _i; for (_i = 0; _i < ARRAY_SIZE(dst->_x); _i++) \
+ dst->_x[_i] = le16_to_cpu(src->_x[_i]); })
+#define __CPY_LE32(_x) ({dst->_x = le32_to_cpu(src->_x); })
+#define __CPY_LE32S(_x) ({int _i; for (_i = 0; _i < ARRAY_SIZE(dst->_x); _i++) \
+ dst->_x[_i] = le32_to_cpu(src->_x[_i]); })
+
+ __CPY_U8(fver);
+ __CPY_U8(rsvd);
+ __CPY_LE16(cycles);
+ __CPY_LE16S(cycles_a2dp);
+ __CPY_LE16(a2dpept);
+ __CPY_LE16(a2dpeptto);
+ __CPY_LE16S(tavg_cycle);
+ __CPY_LE16S(tmax_cycle);
+ __CPY_LE16S(tmaxdiff_cycle);
+ __CPY_LE16S(tavg_a2dp);
+ __CPY_LE16S(tmax_a2dp);
+ __CPY_LE16(tavg_a2dpept);
+ __CPY_LE16(tmax_a2dpept);
+ __CPY_LE16(tavg_lk);
+ __CPY_LE16(tmax_lk);
+ __CPY_LE32S(slot_cnt);
+ __CPY_LE32S(bcn_cnt);
+ __CPY_LE32(leakrx_cnt);
+ __CPY_LE32(collision_cnt);
+ __CPY_LE32(skip_cnt);
+ __CPY_LE32(exception);
+ __CPY_LE32(except_cnt);
+#if (FCXCYSTA_VER > 1)
+ __CPY_LE16S(tslot_cycle);
+#endif
+
+#undef __CPY_U8
+#undef __CPY_LE16
+#undef __CPY_LE16S
+#undef __CPY_LE32
+#undef __CPY_LE32S
+}
+
+#define BTC_LEAK_AP_TH 10
+#define BTC_CYSTA_CHK_PERIOD 100
+
+struct rtw89_btc_prpt {
+ u8 type;
+ __le16 len;
+ u8 content[];
+} __packed;
+
+static u32 _chk_btc_report(struct rtw89_dev *rtwdev,
+ struct rtw89_btc_btf_fwinfo *pfwinfo,
+ u8 *prptbuf, u32 index)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_dm *dm = &btc->dm;
+ struct rtw89_btc_rpt_cmn_info *pcinfo = NULL;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+ struct rtw89_btc_fbtc_rpt_ctrl *prpt = NULL;
+ struct rtw89_btc_fbtc_cysta *pcysta_le32 = NULL;
+ struct rtw89_btc_fbtc_cysta_cpu pcysta[1];
+ struct rtw89_btc_prpt *btc_prpt = NULL;
+ struct rtw89_btc_fbtc_slot *rtp_slot = NULL;
+ u8 rpt_type = 0, *rpt_content = NULL, *pfinfo = NULL;
+ u16 wl_slot_set = 0;
+ u32 trace_step = btc->ctrl.trace_step, rpt_len = 0, diff_t;
+ u8 i;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s index:%d\n",
+ __func__, index);
+
+ if (!prptbuf) {
+ pfwinfo->err[BTFRE_INVALID_INPUT]++;
+ return 0;
+ }
+
+ btc_prpt = (struct rtw89_btc_prpt *)&prptbuf[index];
+ rpt_type = btc_prpt->type;
+ rpt_len = le16_to_cpu(btc_prpt->len);
+ rpt_content = btc_prpt->content;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s rpt_type:%d\n",
+ __func__, rpt_type);
+
+ switch (rpt_type) {
+ case BTC_RPT_TYPE_CTRL:
+ pcinfo = &pfwinfo->rpt_ctrl.cinfo;
+ pfinfo = (u8 *)(&pfwinfo->rpt_ctrl.finfo);
+ pcinfo->req_len = sizeof(pfwinfo->rpt_ctrl.finfo);
+ pcinfo->req_fver = BTCRPT_VER;
+ pcinfo->rx_len = rpt_len;
+ pcinfo->rx_cnt++;
+ break;
+ case BTC_RPT_TYPE_TDMA:
+ pcinfo = &pfwinfo->rpt_fbtc_tdma.cinfo;
+ pfinfo = (u8 *)(&pfwinfo->rpt_fbtc_tdma.finfo);
+ pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_tdma.finfo);
+ pcinfo->req_fver = FCXTDMA_VER;
+ pcinfo->rx_len = rpt_len;
+ pcinfo->rx_cnt++;
+ break;
+ case BTC_RPT_TYPE_SLOT:
+ pcinfo = &pfwinfo->rpt_fbtc_slots.cinfo;
+ pfinfo = (u8 *)(&pfwinfo->rpt_fbtc_slots.finfo);
+ pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_slots.finfo);
+ pcinfo->req_fver = FCXSLOTS_VER;
+ pcinfo->rx_len = rpt_len;
+ pcinfo->rx_cnt++;
+ break;
+ case BTC_RPT_TYPE_CYSTA:
+ pcinfo = &pfwinfo->rpt_fbtc_cysta.cinfo;
+ pfinfo = (u8 *)(&pfwinfo->rpt_fbtc_cysta.finfo);
+ pcysta_le32 = &pfwinfo->rpt_fbtc_cysta.finfo;
+ rtw89_btc_fbtc_cysta_to_cpu(pcysta_le32, pcysta);
+ pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_cysta.finfo);
+ pcinfo->req_fver = FCXCYSTA_VER;
+ pcinfo->rx_len = rpt_len;
+ pcinfo->rx_cnt++;
+ break;
+ case BTC_RPT_TYPE_STEP:
+ pcinfo = &pfwinfo->rpt_fbtc_step.cinfo;
+ pfinfo = (u8 *)(&pfwinfo->rpt_fbtc_step.finfo);
+ pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_step.finfo.step[0]) *
+ trace_step + 8;
+ pcinfo->req_fver = FCXSTEP_VER;
+ pcinfo->rx_len = rpt_len;
+ pcinfo->rx_cnt++;
+ break;
+ case BTC_RPT_TYPE_NULLSTA:
+ pcinfo = &pfwinfo->rpt_fbtc_nullsta.cinfo;
+ pfinfo = (u8 *)(&pfwinfo->rpt_fbtc_nullsta.finfo);
+ pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_nullsta.finfo);
+ pcinfo->req_fver = FCXNULLSTA_VER;
+ pcinfo->rx_len = rpt_len;
+ pcinfo->rx_cnt++;
+ break;
+ case BTC_RPT_TYPE_MREG:
+ pcinfo = &pfwinfo->rpt_fbtc_mregval.cinfo;
+ pfinfo = (u8 *)(&pfwinfo->rpt_fbtc_mregval.finfo);
+ pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_mregval.finfo);
+ pcinfo->req_fver = FCXMREG_VER;
+ pcinfo->rx_len = rpt_len;
+ pcinfo->rx_cnt++;
+ break;
+ case BTC_RPT_TYPE_GPIO_DBG:
+ pcinfo = &pfwinfo->rpt_fbtc_gpio_dbg.cinfo;
+ pfinfo = (u8 *)(&pfwinfo->rpt_fbtc_gpio_dbg.finfo);
+ pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_gpio_dbg.finfo);
+ pcinfo->req_fver = FCXGPIODBG_VER;
+ pcinfo->rx_len = rpt_len;
+ pcinfo->rx_cnt++;
+ break;
+ case BTC_RPT_TYPE_BT_VER:
+ pcinfo = &pfwinfo->rpt_fbtc_btver.cinfo;
+ pfinfo = (u8 *)(&pfwinfo->rpt_fbtc_btver.finfo);
+ pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_btver.finfo);
+ pcinfo->req_fver = FCX_BTVER_VER;
+ pcinfo->rx_len = rpt_len;
+ pcinfo->rx_cnt++;
+ break;
+ case BTC_RPT_TYPE_BT_SCAN:
+ pcinfo = &pfwinfo->rpt_fbtc_btscan.cinfo;
+ pfinfo = (u8 *)(&pfwinfo->rpt_fbtc_btscan.finfo);
+ pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_btscan.finfo);
+ pcinfo->req_fver = FCX_BTSCAN_VER;
+ pcinfo->rx_len = rpt_len;
+ pcinfo->rx_cnt++;
+ break;
+ case BTC_RPT_TYPE_BT_AFH:
+ pcinfo = &pfwinfo->rpt_fbtc_btafh.cinfo;
+ pfinfo = (u8 *)(&pfwinfo->rpt_fbtc_btafh.finfo);
+ pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_btafh.finfo);
+ pcinfo->req_fver = FCX_BTAFH_VER;
+ pcinfo->rx_len = rpt_len;
+ pcinfo->rx_cnt++;
+ break;
+ case BTC_RPT_TYPE_BT_DEVICE:
+ pcinfo = &pfwinfo->rpt_fbtc_btdev.cinfo;
+ pfinfo = (u8 *)(&pfwinfo->rpt_fbtc_btdev.finfo);
+ pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_btdev.finfo);
+ pcinfo->req_fver = FCX_BTDEVINFO_VER;
+ pcinfo->rx_len = rpt_len;
+ pcinfo->rx_cnt++;
+ break;
+ default:
+ pfwinfo->err[BTFRE_UNDEF_TYPE]++;
+ return 0;
+ }
+
+ if (rpt_len != pcinfo->req_len) {
+ if (rpt_type < BTC_RPT_TYPE_MAX)
+ pfwinfo->len_mismch |= (0x1 << rpt_type);
+ else
+ pfwinfo->len_mismch |= BIT(31);
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s %d rpt_len:%d!=req_len:%d\n", __func__,
+ rpt_type, rpt_len, pcinfo->req_len);
+
+ pcinfo->valid = 0;
+ return 0;
+ } else if (!pfinfo || !rpt_content || !pcinfo->req_len) {
+ pfwinfo->err[BTFRE_EXCEPTION]++;
+ pcinfo->valid = 0;
+ return 0;
+ }
+
+ memcpy((void *)pfinfo, (void *)rpt_content, pcinfo->req_len);
+ pcinfo->valid = 1;
+
+ if (rpt_type == BTC_RPT_TYPE_TDMA) {
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s check %d %ld\n", __func__,
+ BTC_DCNT_TDMA_NONSYNC, sizeof(dm->tdma_now));
+
+ if (memcmp(&dm->tdma_now, &pfwinfo->rpt_fbtc_tdma.finfo,
+ sizeof(dm->tdma_now)) != 0) {
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s %d tdma_now %x %x %x %x %x %x %x %x\n",
+ __func__, BTC_DCNT_TDMA_NONSYNC,
+ dm->tdma_now.type, dm->tdma_now.rxflctrl,
+ dm->tdma_now.txpause, dm->tdma_now.wtgle_n,
+ dm->tdma_now.leak_n, dm->tdma_now.ext_ctrl,
+ dm->tdma_now.rsvd0, dm->tdma_now.rsvd1);
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s %d rpt_fbtc_tdma %x %x %x %x %x %x %x %x\n",
+ __func__, BTC_DCNT_TDMA_NONSYNC,
+ pfwinfo->rpt_fbtc_tdma.finfo.type,
+ pfwinfo->rpt_fbtc_tdma.finfo.rxflctrl,
+ pfwinfo->rpt_fbtc_tdma.finfo.txpause,
+ pfwinfo->rpt_fbtc_tdma.finfo.wtgle_n,
+ pfwinfo->rpt_fbtc_tdma.finfo.leak_n,
+ pfwinfo->rpt_fbtc_tdma.finfo.ext_ctrl,
+ pfwinfo->rpt_fbtc_tdma.finfo.rsvd0,
+ pfwinfo->rpt_fbtc_tdma.finfo.rsvd1);
+ }
+
+ _chk_btc_err(rtwdev, BTC_DCNT_TDMA_NONSYNC,
+ memcmp(&dm->tdma_now,
+ &pfwinfo->rpt_fbtc_tdma.finfo,
+ sizeof(dm->tdma_now)));
+ }
+
+ if (rpt_type == BTC_RPT_TYPE_SLOT) {
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s check %d %ld\n",
+ __func__, BTC_DCNT_SLOT_NONSYNC,
+ sizeof(dm->slot_now));
+
+ if (memcmp(dm->slot_now, pfwinfo->rpt_fbtc_slots.finfo.slot,
+ sizeof(dm->slot_now)) != 0) {
+ for (i = 0; i < CXST_MAX; i++) {
+ rtp_slot =
+ &pfwinfo->rpt_fbtc_slots.finfo.slot[i];
+ if (memcmp(&dm->slot_now[i], rtp_slot,
+ sizeof(dm->slot_now[i])) != 0) {
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s %d slot_now[%d] dur=0x%04x tbl=%08x type=0x%04x\n",
+ __func__,
+ BTC_DCNT_SLOT_NONSYNC, i,
+ dm->slot_now[i].dur,
+ dm->slot_now[i].cxtbl,
+ dm->slot_now[i].cxtype);
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s %d rpt_fbtc_slots[%d] dur=0x%04x tbl=%08x type=0x%04x\n",
+ __func__,
+ BTC_DCNT_SLOT_NONSYNC, i,
+ rtp_slot->dur,
+ rtp_slot->cxtbl,
+ rtp_slot->cxtype);
+ }
+ }
+ }
+ _chk_btc_err(rtwdev, BTC_DCNT_SLOT_NONSYNC,
+ memcmp(dm->slot_now,
+ pfwinfo->rpt_fbtc_slots.finfo.slot,
+ sizeof(dm->slot_now)));
+ }
+
+ if (rpt_type == BTC_RPT_TYPE_CYSTA &&
+ pcysta->cycles >= BTC_CYSTA_CHK_PERIOD) {
+ /* Check Leak-AP */
+ if (pcysta->slot_cnt[CXST_LK] != 0 &&
+ pcysta->leakrx_cnt != 0 && dm->tdma_now.rxflctrl) {
+ if (pcysta->slot_cnt[CXST_LK] <
+ BTC_LEAK_AP_TH * pcysta->leakrx_cnt)
+ dm->leak_ap = 1;
+ }
+
+ /* Check diff time between WL slot and W1/E2G slot */
+ if (dm->tdma_now.type == CXTDMA_OFF &&
+ dm->tdma_now.ext_ctrl == CXECTL_EXT)
+ wl_slot_set = le16_to_cpu(dm->slot_now[CXST_E2G].dur);
+ else
+ wl_slot_set = le16_to_cpu(dm->slot_now[CXST_W1].dur);
+
+ if (pcysta->tavg_cycle[CXT_WL] > wl_slot_set) {
+ diff_t = pcysta->tavg_cycle[CXT_WL] - wl_slot_set;
+ _chk_btc_err(rtwdev, BTC_DCNT_WL_SLOT_DRIFT, diff_t);
+ }
+ }
+
+ if (rpt_type == BTC_RPT_TYPE_CTRL) {
+ prpt = &pfwinfo->rpt_ctrl.finfo;
+ btc->fwinfo.rpt_en_map = prpt->rpt_enable;
+ wl->ver_info.fw_coex = prpt->wl_fw_coex_ver;
+ wl->ver_info.fw = prpt->wl_fw_ver;
+ dm->wl_fw_cx_offload = !!(prpt->wl_fw_cx_offload);
+ }
+
+ if (rpt_type >= BTC_RPT_TYPE_BT_VER &&
+ rpt_type <= BTC_RPT_TYPE_BT_DEVICE)
+ _update_bt_report(rtwdev, rpt_type, pfinfo);
+
+ return (rpt_len + BTC_RPT_HDR_SIZE);
+}
+
+static void _parse_btc_report(struct rtw89_dev *rtwdev,
+ struct rtw89_btc_btf_fwinfo *pfwinfo,
+ u8 *pbuf, u32 buf_len)
+{
+ struct rtw89_btc_prpt *btc_prpt = NULL;
+ u32 index = 0, rpt_len = 0;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s buf_len:%d\n",
+ __func__, buf_len);
+
+ while (pbuf) {
+ btc_prpt = (struct rtw89_btc_prpt *)&pbuf[index];
+ if (index + 2 >= BTC_FWINFO_BUF)
+ break;
+ /* At least 3 bytes: type(1) & len(2) */
+ rpt_len = le16_to_cpu(btc_prpt->len);
+ if ((index + rpt_len + BTC_RPT_HDR_SIZE) > buf_len)
+ break;
+
+ rpt_len = _chk_btc_report(rtwdev, pfwinfo, pbuf, index);
+ if (!rpt_len)
+ break;
+ index += rpt_len;
+ }
+}
+
+#define BTC_TLV_HDR_LEN 2
+
+static void _append_tdma(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_dm *dm = &btc->dm;
+ struct rtw89_btc_btf_tlv *tlv = NULL;
+ struct rtw89_btc_fbtc_tdma *v = NULL;
+ u16 len = btc->policy_len;
+
+ if (!btc->update_policy_force &&
+ !memcmp(&dm->tdma, &dm->tdma_now, sizeof(dm->tdma))) {
+ rtw89_debug(rtwdev,
+ RTW89_DBG_BTC, "[BTC], %s(): tdma no change!\n",
+ __func__);
+ return;
+ }
+
+ tlv = (struct rtw89_btc_btf_tlv *)&btc->policy[len];
+ v = (struct rtw89_btc_fbtc_tdma *)&tlv->val[0];
+ tlv->type = CXPOLICY_TDMA;
+ tlv->len = sizeof(*v);
+
+ memcpy(v, &dm->tdma, sizeof(*v));
+ btc->policy_len += BTC_TLV_HDR_LEN + sizeof(*v);
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s: type:%d, rxflctrl=%d, txpause=%d, wtgle_n=%d, leak_n=%d, ext_ctrl=%d\n",
+ __func__, dm->tdma.type, dm->tdma.rxflctrl,
+ dm->tdma.txpause, dm->tdma.wtgle_n, dm->tdma.leak_n,
+ dm->tdma.ext_ctrl);
+}
+
+static void _append_slot(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_dm *dm = &btc->dm;
+ struct rtw89_btc_btf_tlv *tlv = NULL;
+ struct btc_fbtc_1slot *v = NULL;
+ u16 len = 0;
+ u8 i, cnt = 0;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s: A:btc->policy_len = %d\n",
+ __func__, btc->policy_len);
+
+ for (i = 0; i < CXST_MAX; i++) {
+ if (!btc->update_policy_force &&
+ !memcmp(&dm->slot[i], &dm->slot_now[i],
+ sizeof(dm->slot[i])))
+ continue;
+
+ len = btc->policy_len;
+
+ tlv = (struct rtw89_btc_btf_tlv *)&btc->policy[len];
+ v = (struct btc_fbtc_1slot *)&tlv->val[0];
+ tlv->type = CXPOLICY_SLOT;
+ tlv->len = sizeof(*v);
+
+ v->fver = FCXONESLOT_VER;
+ v->sid = i;
+ v->slot = dm->slot[i];
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s: slot-%d: dur=%d, table=0x%08x, type=%d\n",
+ __func__, i, dm->slot[i].dur, dm->slot[i].cxtbl,
+ dm->slot[i].cxtype);
+ cnt++;
+
+ btc->policy_len += BTC_TLV_HDR_LEN + sizeof(*v);
+ }
+
+ if (cnt > 0)
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s: slot update (cnt=%d)!!\n",
+ __func__, cnt);
+}
+
+static void rtw89_btc_fw_en_rpt(struct rtw89_dev *rtwdev,
+ u32 rpt_map, bool rpt_state)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_btf_fwinfo *fwinfo = &btc->fwinfo;
+ struct rtw89_btc_btf_set_report r = {0};
+ u32 val = 0;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s: rpt_map=%x, rpt_state=%x\n",
+ __func__, rpt_map, rpt_state);
+
+ if (rpt_state)
+ val = fwinfo->rpt_en_map | rpt_map;
+ else
+ val = fwinfo->rpt_en_map & ~rpt_map;
+
+ if (val == fwinfo->rpt_en_map)
+ return;
+
+ fwinfo->rpt_en_map = val;
+
+ r.fver = BTF_SET_REPORT_VER;
+ r.enable = cpu_to_le32(val);
+ r.para = cpu_to_le32(rpt_state);
+
+ _send_fw_cmd(rtwdev, BTFC_SET, SET_REPORT_EN, &r, sizeof(r));
+}
+
+static void rtw89_btc_fw_set_slots(struct rtw89_dev *rtwdev, u8 num,
+ struct rtw89_btc_fbtc_slot *s)
+{
+ struct rtw89_btc_btf_set_slot_table *tbl = NULL;
+ u8 *ptr = NULL;
+ u16 n = 0;
+
+ n = sizeof(*s) * num + sizeof(*tbl);
+ tbl = kmalloc(n, GFP_KERNEL);
+ if (!tbl)
+ return;
+
+ tbl->fver = BTF_SET_SLOT_TABLE_VER;
+ tbl->tbl_num = num;
+ ptr = &tbl->buf[0];
+ memcpy(ptr, s, num * sizeof(*s));
+
+ _send_fw_cmd(rtwdev, BTFC_SET, SET_SLOT_TABLE, tbl, n);
+
+ kfree(tbl);
+}
+
+static void btc_fw_set_monreg(struct rtw89_dev *rtwdev)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_btc_btf_set_mon_reg *monreg = NULL;
+ u8 n, *ptr = NULL, ulen;
+ u16 sz = 0;
+
+ n = chip->mon_reg_num;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): mon_reg_num=%d\n", __func__, n);
+ if (n > CXMREG_MAX) {
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s: mon reg count %d > %d\n",
+ __func__, n, CXMREG_MAX);
+ return;
+ }
+
+ ulen = sizeof(struct rtw89_btc_fbtc_mreg);
+ sz = (ulen * n) + sizeof(*monreg);
+ monreg = kmalloc(sz, GFP_KERNEL);
+ if (!monreg)
+ return;
+
+ monreg->fver = BTF_SET_MON_REG_VER;
+ monreg->reg_num = n;
+ ptr = &monreg->buf[0];
+ memcpy((void *)ptr, chip->mon_reg, n * ulen);
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): sz=%d ulen=%d n=%d\n",
+ __func__, sz, ulen, n);
+
+ _send_fw_cmd(rtwdev, BTFC_SET, SET_MREG_TABLE, (u8 *)monreg, sz);
+ kfree(monreg);
+ rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_MREG, 1);
+}
+
+static void _update_dm_step(struct rtw89_dev *rtwdev,
+ enum btc_reason_and_action reason_or_action)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_dm *dm = &btc->dm;
+
+ /* use ring-structure to store dm step */
+ dm->dm_step.step[dm->dm_step.step_pos] = reason_or_action;
+ dm->dm_step.step_pos++;
+
+ if (dm->dm_step.step_pos >= ARRAY_SIZE(dm->dm_step.step)) {
+ dm->dm_step.step_pos = 0;
+ dm->dm_step.step_ov = true;
+ }
+}
+
+static void _fw_set_policy(struct rtw89_dev *rtwdev, u16 policy_type,
+ enum btc_reason_and_action action)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_dm *dm = &btc->dm;
+
+ dm->run_action = action;
+
+ _update_dm_step(rtwdev, action | BTC_ACT_EXT_BIT);
+ _update_dm_step(rtwdev, policy_type | BTC_POLICY_EXT_BIT);
+
+ btc->policy_len = 0;
+ btc->policy_type = policy_type;
+
+ _append_tdma(rtwdev);
+ _append_slot(rtwdev);
+
+ if (btc->policy_len == 0 || btc->policy_len > RTW89_BTC_POLICY_MAXLEN)
+ return;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): action = %d -> policy type/len: 0x%04x/%d\n",
+ __func__, action, policy_type, btc->policy_len);
+
+ if (dm->tdma.rxflctrl == CXFLC_NULLP ||
+ dm->tdma.rxflctrl == CXFLC_QOSNULL)
+ btc->lps = 1;
+ else
+ btc->lps = 0;
+
+ if (btc->lps == 1)
+ rtw89_set_coex_ctrl_lps(rtwdev, btc->lps);
+
+ _send_fw_cmd(rtwdev, BTFC_SET, SET_CX_POLICY,
+ btc->policy, btc->policy_len);
+
+ memcpy(&dm->tdma_now, &dm->tdma, sizeof(dm->tdma_now));
+ memcpy(&dm->slot_now, &dm->slot, sizeof(dm->slot_now));
+
+ if (btc->update_policy_force)
+ btc->update_policy_force = false;
+
+ if (btc->lps == 0)
+ rtw89_set_coex_ctrl_lps(rtwdev, btc->lps);
+}
+
+static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 type)
+{
+ switch (type) {
+ case CXDRVINFO_INIT:
+ rtw89_fw_h2c_cxdrv_init(rtwdev);
+ break;
+ case CXDRVINFO_ROLE:
+ rtw89_fw_h2c_cxdrv_role(rtwdev);
+ break;
+ case CXDRVINFO_CTRL:
+ rtw89_fw_h2c_cxdrv_ctrl(rtwdev);
+ break;
+ case CXDRVINFO_RFK:
+ rtw89_fw_h2c_cxdrv_rfk(rtwdev);
+ break;
+ default:
+ break;
+ }
+}
+
+static
+void btc_fw_event(struct rtw89_dev *rtwdev, u8 evt_id, void *data, u32 len)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s evt_id:%d len:%d\n",
+ __func__, evt_id, len);
+
+ if (!len || !data)
+ return;
+
+ switch (evt_id) {
+ case BTF_EVNT_RPT:
+ _parse_btc_report(rtwdev, pfwinfo, data, len);
+ break;
+ default:
+ break;
+ }
+}
+
+static void _set_gnt_wl(struct rtw89_dev *rtwdev, u8 phy_map, u8 state)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_dm *dm = &btc->dm;
+ struct rtw89_mac_ax_gnt *g = dm->gnt.band;
+ u8 i;
+
+ if (phy_map > BTC_PHY_ALL)
+ return;
+
+ for (i = 0; i < RTW89_PHY_MAX; i++) {
+ if (!(phy_map & BIT(i)))
+ continue;
+
+ switch (state) {
+ case BTC_GNT_HW:
+ g[i].gnt_wl_sw_en = 0;
+ g[i].gnt_wl = 0;
+ break;
+ case BTC_GNT_SW_LO:
+ g[i].gnt_wl_sw_en = 1;
+ g[i].gnt_wl = 0;
+ break;
+ case BTC_GNT_SW_HI:
+ g[i].gnt_wl_sw_en = 1;
+ g[i].gnt_wl = 1;
+ break;
+ }
+ }
+
+ rtw89_mac_cfg_gnt(rtwdev, &dm->gnt);
+}
+
+#define BTC_TDMA_WLROLE_MAX 2
+
+static void _set_bt_ignore_wlan_act(struct rtw89_dev *rtwdev, u8 enable)
+{
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): set bt %s wlan_act\n", __func__,
+ enable ? "ignore" : "do not ignore");
+
+ _send_fw_cmd(rtwdev, BTFC_SET, SET_BT_IGNORE_WLAN_ACT, &enable, 1);
+}
+
+#define WL_TX_POWER_NO_BTC_CTRL GENMASK(31, 0)
+#define WL_TX_POWER_ALL_TIME GENMASK(15, 0)
+#define WL_TX_POWER_WITH_BT GENMASK(31, 16)
+#define WL_TX_POWER_INT_PART GENMASK(8, 2)
+#define WL_TX_POWER_FRA_PART GENMASK(1, 0)
+#define B_BTC_WL_TX_POWER_SIGN BIT(7)
+#define B_TSSI_WL_TX_POWER_SIGN BIT(8)
+
+static void _set_wl_tx_power(struct rtw89_dev *rtwdev, u32 level)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+ u32 pwr_val;
+
+ if (wl->rf_para.tx_pwr_freerun == level)
+ return;
+
+ wl->rf_para.tx_pwr_freerun = level;
+ btc->dm.rf_trx_para.wl_tx_power = level;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): level = %d\n",
+ __func__, level);
+
+ if (level == RTW89_BTC_WL_DEF_TX_PWR) {
+ pwr_val = WL_TX_POWER_NO_BTC_CTRL;
+ } else { /* only apply "force tx power" */
+ pwr_val = FIELD_PREP(WL_TX_POWER_INT_PART, level);
+ if (pwr_val > RTW89_BTC_WL_DEF_TX_PWR)
+ pwr_val = RTW89_BTC_WL_DEF_TX_PWR;
+
+ if (level & B_BTC_WL_TX_POWER_SIGN)
+ pwr_val |= B_TSSI_WL_TX_POWER_SIGN;
+ pwr_val |= WL_TX_POWER_WITH_BT;
+ }
+
+ chip->ops->btc_set_wl_txpwr_ctrl(rtwdev, pwr_val);
+}
+
+#define rtw89_btc_wl_rx_gain(rtwdev, level) do {} while (0)
+
+static void _set_wl_rx_gain(struct rtw89_dev *rtwdev, u32 level)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+
+ if (wl->rf_para.rx_gain_freerun == level)
+ return;
+
+ wl->rf_para.rx_gain_freerun = level;
+ btc->dm.rf_trx_para.wl_rx_gain = level;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): level = %d\n",
+ __func__, level);
+
+ rtw89_btc_wl_rx_gain(rtwdev, level);
+}
+
+static void _set_bt_tx_power(struct rtw89_dev *rtwdev, u8 level)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_bt_info *bt = &btc->cx.bt;
+ u8 buf = 0;
+
+ if (bt->rf_para.tx_pwr_freerun == level)
+ return;
+
+ bt->rf_para.tx_pwr_freerun = level;
+ btc->dm.rf_trx_para.bt_tx_power = level;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): level = %d\n",
+ __func__, level);
+
+ buf = (s8)(-level);
+ _send_fw_cmd(rtwdev, BTFC_SET, SET_BT_TX_PWR, &buf, 1);
+}
+
+#define BTC_BT_RX_NORMAL_LVL 7
+
+static void _set_bt_rx_gain(struct rtw89_dev *rtwdev, u8 level)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_bt_info *bt = &btc->cx.bt;
+
+ if (bt->rf_para.rx_gain_freerun == level ||
+ level > BTC_BT_RX_NORMAL_LVL)
+ return;
+
+ bt->rf_para.rx_gain_freerun = level;
+ btc->dm.rf_trx_para.bt_rx_gain = level;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): level = %d\n",
+ __func__, level);
+
+ if (level == BTC_BT_RX_NORMAL_LVL)
+ _write_scbd(rtwdev, BTC_WSCB_RXGAIN, false);
+ else
+ _write_scbd(rtwdev, BTC_WSCB_RXGAIN, true);
+
+ _send_fw_cmd(rtwdev, BTFC_SET, SET_BT_LNA_CONSTRAIN, &level, 1);
+}
+
+static void _set_rf_trx_para(struct rtw89_dev *rtwdev)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_dm *dm = &btc->dm;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+ struct rtw89_btc_rf_trx_para para;
+ u8 level_id = 0;
+
+ if (!dm->freerun) {
+ dm->trx_para_level = 0;
+ chip->ops->btc_bt_aci_imp(rtwdev);
+ }
+
+ level_id = (u8)dm->trx_para_level;
+
+ if (level_id >= chip->rf_para_dlink_num ||
+ level_id >= chip->rf_para_ulink_num) {
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): invalid level_id: %d\n",
+ __func__, level_id);
+ return;
+ }
+
+ if (wl->status.map.traffic_dir & BIT(RTW89_TFC_UL))
+ para = chip->rf_para_ulink[level_id];
+ else
+ para = chip->rf_para_dlink[level_id];
+
+ if (para.wl_tx_power != 0)
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): wl_tx_power%d\n",
+ __func__, para.wl_tx_power);
+ _set_wl_tx_power(rtwdev, para.wl_tx_power);
+ _set_wl_rx_gain(rtwdev, para.wl_rx_gain);
+ _set_bt_tx_power(rtwdev, para.bt_tx_power);
+ _set_bt_rx_gain(rtwdev, para.bt_rx_gain);
+}
+
+static void _update_btc_state_map(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_cx *cx = &btc->cx;
+ struct rtw89_btc_wl_info *wl = &cx->wl;
+ struct rtw89_btc_bt_info *bt = &cx->bt;
+ struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info;
+
+ if (wl->status.map.connecting || wl->status.map._4way ||
+ wl->status.map.roaming) {
+ cx->state_map = BTC_WLINKING;
+ } else if (wl->status.map.scan) { /* wl scan */
+ if (bt_linfo->status.map.inq_pag)
+ cx->state_map = BTC_WSCAN_BSCAN;
+ else
+ cx->state_map = BTC_WSCAN_BNOSCAN;
+ } else if (wl->status.map.busy) { /* only busy */
+ if (bt_linfo->status.map.inq_pag)
+ cx->state_map = BTC_WBUSY_BSCAN;
+ else
+ cx->state_map = BTC_WBUSY_BNOSCAN;
+ } else { /* wl idle */
+ cx->state_map = BTC_WIDLE;
+ }
+}
+
+static void _set_bt_afh_info(struct rtw89_dev *rtwdev)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+ struct rtw89_btc_bt_info *bt = &btc->cx.bt;
+ struct rtw89_btc_bt_link_info *b = &bt->link_info;
+ struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info;
+ u8 en = 0, i, ch = 0, bw = 0;
+
+ if (btc->ctrl.manual || wl->status.map.scan)
+ return;
+
+ /* TODO if include module->ant.type == BTC_ANT_SHARED */
+ if (wl->status.map.rf_off || bt->whql_test ||
+ wl_rinfo->link_mode == BTC_WLINK_NOLINK ||
+ wl_rinfo->link_mode == BTC_WLINK_5G ||
+ wl_rinfo->connect_cnt > BTC_TDMA_WLROLE_MAX) {
+ en = false;
+ } else if (wl_rinfo->link_mode == BTC_WLINK_2G_MCC ||
+ wl_rinfo->link_mode == BTC_WLINK_2G_SCC) {
+ en = true;
+ /* get p2p channel */
+ for (i = 0; i < RTW89_MAX_HW_PORT_NUM; i++) {
+ if (wl_rinfo->active_role[i].role ==
+ RTW89_WIFI_ROLE_P2P_GO ||
+ wl_rinfo->active_role[i].role ==
+ RTW89_WIFI_ROLE_P2P_CLIENT) {
+ ch = wl_rinfo->active_role[i].ch;
+ bw = wl_rinfo->active_role[i].bw;
+ break;
+ }
+ }
+ } else {
+ en = true;
+ /* get 2g channel */
+ for (i = 0; i < RTW89_MAX_HW_PORT_NUM; i++) {
+ if (wl_rinfo->active_role[i].connected &&
+ wl_rinfo->active_role[i].band == RTW89_BAND_2G) {
+ ch = wl_rinfo->active_role[i].ch;
+ bw = wl_rinfo->active_role[i].bw;
+ break;
+ }
+ }
+ }
+
+ switch (bw) {
+ case RTW89_CHANNEL_WIDTH_20:
+#ifdef BTC_NON_SHARED_ANT_FREERUN
+ bw = 48;
+#else
+ bw = 20 + chip->afh_guard_ch * 2;
+#endif
+ break;
+ case RTW89_CHANNEL_WIDTH_40:
+ bw = 40 + chip->afh_guard_ch * 2;
+ break;
+ case RTW89_CHANNEL_WIDTH_5:
+ bw = 5 + chip->afh_guard_ch * 2;
+ break;
+ case RTW89_CHANNEL_WIDTH_10:
+ bw = 10 + chip->afh_guard_ch * 2;
+ break;
+ default:
+ bw = 0;
+ en = false; /* turn off AFH info if BW > 40 */
+ break;
+ }
+
+ if (wl->afh_info.en == en &&
+ wl->afh_info.ch == ch &&
+ wl->afh_info.bw == bw &&
+ b->profile_cnt.last == b->profile_cnt.now) {
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): return because no change!\n",
+ __func__);
+ return;
+ }
+
+ wl->afh_info.en = en;
+ wl->afh_info.ch = ch;
+ wl->afh_info.bw = bw;
+
+ _send_fw_cmd(rtwdev, BTFC_SET, SET_BT_WL_CH_INFO, &wl->afh_info, 3);
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): en=%d, ch=%d, bw=%d\n",
+ __func__, en, ch, bw);
+ btc->cx.cnt_wl[BTC_WCNT_CH_UPDATE]++;
+}
+
+static bool _check_freerun(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+ struct rtw89_btc_bt_info *bt = &btc->cx.bt;
+ struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info;
+ struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info;
+ struct rtw89_btc_bt_hid_desc *hid = &bt_linfo->hid_desc;
+
+ if (btc->mdinfo.ant.type == BTC_ANT_SHARED) {
+ btc->dm.trx_para_level = 0;
+ return false;
+ }
+
+ /* The below is dedicated antenna case */
+ if (wl_rinfo->connect_cnt > BTC_TDMA_WLROLE_MAX) {
+ btc->dm.trx_para_level = 5;
+ return true;
+ }
+
+ if (bt_linfo->profile_cnt.now == 0) {
+ btc->dm.trx_para_level = 5;
+ return true;
+ }
+
+ if (hid->pair_cnt > BTC_TDMA_BTHID_MAX) {
+ btc->dm.trx_para_level = 5;
+ return true;
+ }
+
+ /* TODO get isolation by BT psd */
+ if (btc->mdinfo.ant.isolation >= BTC_FREERUN_ANTISO_MIN) {
+ btc->dm.trx_para_level = 5;
+ return true;
+ }
+
+ if (!wl->status.map.busy) {/* wl idle -> freerun */
+ btc->dm.trx_para_level = 5;
+ return true;
+ } else if (wl->rssi_level > 1) {/* WL rssi < 50% (-60dBm) */
+ btc->dm.trx_para_level = 0;
+ return false;
+ } else if (wl->status.map.traffic_dir & BIT(RTW89_TFC_UL)) {
+ if (wl->rssi_level == 0 && bt_linfo->rssi > 31) {
+ btc->dm.trx_para_level = 6;
+ return true;
+ } else if (wl->rssi_level == 1 && bt_linfo->rssi > 36) {
+ btc->dm.trx_para_level = 7;
+ return true;
+ }
+ btc->dm.trx_para_level = 0;
+ return false;
+ } else if (wl->status.map.traffic_dir & BIT(RTW89_TFC_DL)) {
+ if (bt_linfo->rssi > 28) {
+ btc->dm.trx_para_level = 6;
+ return true;
+ }
+ }
+
+ btc->dm.trx_para_level = 0;
+ return false;
+}
+
+#define _tdma_set_flctrl(btc, flc) ({(btc)->dm.tdma.rxflctrl = flc; })
+#define _tdma_set_tog(btc, wtg) ({(btc)->dm.tdma.wtgle_n = wtg; })
+#define _tdma_set_lek(btc, lek) ({(btc)->dm.tdma.leak_n = lek; })
+
+#define _slot_set(btc, sid, dura, tbl, type) \
+ do { \
+ typeof(sid) _sid = (sid); \
+ typeof(btc) _btc = (btc); \
+ _btc->dm.slot[_sid].dur = cpu_to_le16(dura);\
+ _btc->dm.slot[_sid].cxtbl = cpu_to_le32(tbl); \
+ _btc->dm.slot[_sid].cxtype = cpu_to_le16(type); \
+ } while (0)
+
+#define _slot_set_dur(btc, sid, dura) (btc)->dm.slot[sid].dur = cpu_to_le16(dura)
+#define _slot_set_tbl(btc, sid, tbl) (btc)->dm.slot[sid].cxtbl = cpu_to_le32(tbl)
+#define _slot_set_type(btc, sid, type) (btc)->dm.slot[sid].cxtype = cpu_to_le16(type)
+
+struct btc_btinfo_lb2 {
+ u8 connect: 1;
+ u8 sco_busy: 1;
+ u8 inq_pag: 1;
+ u8 acl_busy: 1;
+ u8 hfp: 1;
+ u8 hid: 1;
+ u8 a2dp: 1;
+ u8 pan: 1;
+};
+
+struct btc_btinfo_lb3 {
+ u8 retry: 4;
+ u8 cqddr: 1;
+ u8 inq: 1;
+ u8 mesh_busy: 1;
+ u8 pag: 1;
+};
+
+struct btc_btinfo_hb0 {
+ s8 rssi;
+};
+
+struct btc_btinfo_hb1 {
+ u8 ble_connect: 1;
+ u8 reinit: 1;
+ u8 relink: 1;
+ u8 igno_wl: 1;
+ u8 voice: 1;
+ u8 ble_scan: 1;
+ u8 role_sw: 1;
+ u8 multi_link: 1;
+};
+
+struct btc_btinfo_hb2 {
+ u8 pan_active: 1;
+ u8 afh_update: 1;
+ u8 a2dp_active: 1;
+ u8 slave: 1;
+ u8 hid_slot: 2;
+ u8 hid_cnt: 2;
+};
+
+struct btc_btinfo_hb3 {
+ u8 a2dp_bitpool: 6;
+ u8 tx_3m: 1;
+ u8 a2dp_sink: 1;
+};
+
+union btc_btinfo {
+ u8 val;
+ struct btc_btinfo_lb2 lb2;
+ struct btc_btinfo_lb3 lb3;
+ struct btc_btinfo_hb0 hb0;
+ struct btc_btinfo_hb1 hb1;
+ struct btc_btinfo_hb2 hb2;
+ struct btc_btinfo_hb3 hb3;
+};
+
+static void _set_policy(struct rtw89_dev *rtwdev, u16 policy_type,
+ enum btc_reason_and_action action)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_dm *dm = &btc->dm;
+ struct rtw89_btc_fbtc_tdma *t = &dm->tdma;
+ struct rtw89_btc_fbtc_slot *s = dm->slot;
+ u8 type;
+ u32 tbl_w1, tbl_b1, tbl_b4;
+
+ if (btc->mdinfo.ant.type == BTC_ANT_SHARED) {
+ if (btc->cx.wl.status.map._4way)
+ tbl_w1 = cxtbl[1];
+ else
+ tbl_w1 = cxtbl[8];
+ tbl_b1 = cxtbl[3];
+ tbl_b4 = cxtbl[3];
+ } else {
+ tbl_w1 = cxtbl[16];
+ tbl_b1 = cxtbl[17];
+ tbl_b4 = cxtbl[17];
+ }
+
+ type = (u8)((policy_type & BTC_CXP_MASK) >> 8);
+ btc->bt_req_en = false;
+
+ switch (type) {
+ case BTC_CXP_USERDEF0:
+ *t = t_def[CXTD_OFF];
+ s[CXST_OFF] = s_def[CXST_OFF];
+ _slot_set_tbl(btc, CXST_OFF, cxtbl[2]);
+ btc->update_policy_force = true;
+ break;
+ case BTC_CXP_OFF: /* TDMA off */
+ _write_scbd(rtwdev, BTC_WSCB_TDMA, false);
+ *t = t_def[CXTD_OFF];
+ s[CXST_OFF] = s_def[CXST_OFF];
+
+ switch (policy_type) {
+ case BTC_CXP_OFF_BT:
+ _slot_set_tbl(btc, CXST_OFF, cxtbl[2]);
+ break;
+ case BTC_CXP_OFF_WL:
+ _slot_set_tbl(btc, CXST_OFF, cxtbl[1]);
+ break;
+ case BTC_CXP_OFF_EQ0:
+ _slot_set_tbl(btc, CXST_OFF, cxtbl[0]);
+ break;
+ case BTC_CXP_OFF_EQ1:
+ _slot_set_tbl(btc, CXST_OFF, cxtbl[16]);
+ break;
+ case BTC_CXP_OFF_EQ2:
+ _slot_set_tbl(btc, CXST_OFF, cxtbl[17]);
+ break;
+ case BTC_CXP_OFF_EQ3:
+ _slot_set_tbl(btc, CXST_OFF, cxtbl[18]);
+ break;
+ case BTC_CXP_OFF_BWB0:
+ _slot_set_tbl(btc, CXST_OFF, cxtbl[5]);
+ break;
+ case BTC_CXP_OFF_BWB1:
+ _slot_set_tbl(btc, CXST_OFF, cxtbl[8]);
+ break;
+ }
+ break;
+ case BTC_CXP_OFFB: /* TDMA off + beacon protect */
+ _write_scbd(rtwdev, BTC_WSCB_TDMA, false);
+ *t = t_def[CXTD_OFF_B2];
+ s[CXST_OFF] = s_def[CXST_OFF];
+ switch (policy_type) {
+ case BTC_CXP_OFFB_BWB0:
+ _slot_set_tbl(btc, CXST_OFF, cxtbl[8]);
+ break;
+ }
+ break;
+ case BTC_CXP_OFFE: /* TDMA off + beacon protect + Ext_control */
+ btc->bt_req_en = true;
+ _write_scbd(rtwdev, BTC_WSCB_TDMA, true);
+ *t = t_def[CXTD_OFF_EXT];
+ switch (policy_type) {
+ case BTC_CXP_OFFE_DEF:
+ s[CXST_E2G] = s_def[CXST_E2G];
+ s[CXST_E5G] = s_def[CXST_E5G];
+ s[CXST_EBT] = s_def[CXST_EBT];
+ s[CXST_ENULL] = s_def[CXST_ENULL];
+ break;
+ case BTC_CXP_OFFE_DEF2:
+ _slot_set(btc, CXST_E2G, 20, cxtbl[1], SLOT_ISO);
+ s[CXST_E5G] = s_def[CXST_E5G];
+ s[CXST_EBT] = s_def[CXST_EBT];
+ s[CXST_ENULL] = s_def[CXST_ENULL];
+ break;
+ }
+ break;
+ case BTC_CXP_FIX: /* TDMA Fix-Slot */
+ _write_scbd(rtwdev, BTC_WSCB_TDMA, true);
+ *t = t_def[CXTD_FIX];
+ switch (policy_type) {
+ case BTC_CXP_FIX_TD3030:
+ _slot_set(btc, CXST_W1, 30, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 30, tbl_b1, SLOT_MIX);
+ break;
+ case BTC_CXP_FIX_TD5050:
+ _slot_set(btc, CXST_W1, 50, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 50, tbl_b1, SLOT_MIX);
+ break;
+ case BTC_CXP_FIX_TD2030:
+ _slot_set(btc, CXST_W1, 20, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 30, tbl_b1, SLOT_MIX);
+ break;
+ case BTC_CXP_FIX_TD4010:
+ _slot_set(btc, CXST_W1, 40, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 10, tbl_b1, SLOT_MIX);
+ break;
+ case BTC_CXP_FIX_TD4020:
+ _slot_set(btc, CXST_W1, 40, cxtbl[1], SLOT_MIX);
+ _slot_set(btc, CXST_B1, 20, tbl_b1, SLOT_MIX);
+ break;
+ case BTC_CXP_FIX_TD7010:
+ _slot_set(btc, CXST_W1, 70, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 10, tbl_b1, SLOT_MIX);
+ break;
+ case BTC_CXP_FIX_TD2060:
+ _slot_set(btc, CXST_W1, 20, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 60, tbl_b1, SLOT_MIX);
+ break;
+ case BTC_CXP_FIX_TD3060:
+ _slot_set(btc, CXST_W1, 30, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 60, tbl_b1, SLOT_MIX);
+ break;
+ case BTC_CXP_FIX_TD2080:
+ _slot_set(btc, CXST_W1, 20, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 80, tbl_b1, SLOT_MIX);
+ break;
+ case BTC_CXP_FIX_TDW1B1: /* W1:B1 = user-define */
+ _slot_set(btc, CXST_W1, dm->slot_dur[CXST_W1],
+ tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, dm->slot_dur[CXST_B1],
+ tbl_b1, SLOT_MIX);
+ break;
+ }
+ break;
+ case BTC_CXP_PFIX: /* PS-TDMA Fix-Slot */
+ _write_scbd(rtwdev, BTC_WSCB_TDMA, true);
+ *t = t_def[CXTD_PFIX];
+ if (btc->cx.wl.role_info.role_map.role.ap)
+ _tdma_set_flctrl(btc, CXFLC_QOSNULL);
+
+ switch (policy_type) {
+ case BTC_CXP_PFIX_TD3030:
+ _slot_set(btc, CXST_W1, 30, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 30, tbl_b1, SLOT_MIX);
+ break;
+ case BTC_CXP_PFIX_TD5050:
+ _slot_set(btc, CXST_W1, 50, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 50, tbl_b1, SLOT_MIX);
+ break;
+ case BTC_CXP_PFIX_TD2030:
+ _slot_set(btc, CXST_W1, 20, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 30, tbl_b1, SLOT_MIX);
+ break;
+ case BTC_CXP_PFIX_TD2060:
+ _slot_set(btc, CXST_W1, 20, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 60, tbl_b1, SLOT_MIX);
+ break;
+ case BTC_CXP_PFIX_TD3070:
+ _slot_set(btc, CXST_W1, 30, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 60, tbl_b1, SLOT_MIX);
+ break;
+ case BTC_CXP_PFIX_TD2080:
+ _slot_set(btc, CXST_W1, 20, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 80, tbl_b1, SLOT_MIX);
+ break;
+ }
+ break;
+ case BTC_CXP_AUTO: /* TDMA Auto-Slot */
+ _write_scbd(rtwdev, BTC_WSCB_TDMA, true);
+ *t = t_def[CXTD_AUTO];
+ switch (policy_type) {
+ case BTC_CXP_AUTO_TD50200:
+ _slot_set(btc, CXST_W1, 50, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 200, tbl_b1, SLOT_MIX);
+ break;
+ case BTC_CXP_AUTO_TD60200:
+ _slot_set(btc, CXST_W1, 60, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 200, tbl_b1, SLOT_MIX);
+ break;
+ case BTC_CXP_AUTO_TD20200:
+ _slot_set(btc, CXST_W1, 20, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 200, tbl_b1, SLOT_MIX);
+ break;
+ case BTC_CXP_AUTO_TDW1B1: /* W1:B1 = user-define */
+ _slot_set(btc, CXST_W1, dm->slot_dur[CXST_W1],
+ tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, dm->slot_dur[CXST_B1],
+ tbl_b1, SLOT_MIX);
+ break;
+ }
+ break;
+ case BTC_CXP_PAUTO: /* PS-TDMA Auto-Slot */
+ _write_scbd(rtwdev, BTC_WSCB_TDMA, true);
+ *t = t_def[CXTD_PAUTO];
+ switch (policy_type) {
+ case BTC_CXP_PAUTO_TD50200:
+ _slot_set(btc, CXST_W1, 50, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 200, tbl_b1, SLOT_MIX);
+ break;
+ case BTC_CXP_PAUTO_TD60200:
+ _slot_set(btc, CXST_W1, 60, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 200, tbl_b1, SLOT_MIX);
+ break;
+ case BTC_CXP_PAUTO_TD20200:
+ _slot_set(btc, CXST_W1, 20, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 200, tbl_b1, SLOT_MIX);
+ break;
+ case BTC_CXP_PAUTO_TDW1B1:
+ _slot_set(btc, CXST_W1, dm->slot_dur[CXST_W1],
+ tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, dm->slot_dur[CXST_B1],
+ tbl_b1, SLOT_MIX);
+ break;
+ }
+ break;
+ case BTC_CXP_AUTO2: /* TDMA Auto-Slot2 */
+ _write_scbd(rtwdev, BTC_WSCB_TDMA, true);
+ *t = t_def[CXTD_AUTO2];
+ switch (policy_type) {
+ case BTC_CXP_AUTO2_TD3050:
+ _slot_set(btc, CXST_W1, 30, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 200, tbl_b1, SLOT_MIX);
+ _slot_set(btc, CXST_B4, 50, tbl_b4, SLOT_MIX);
+ break;
+ case BTC_CXP_AUTO2_TD3070:
+ _slot_set(btc, CXST_W1, 30, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 200, tbl_b1, SLOT_MIX);
+ _slot_set(btc, CXST_B4, 70, tbl_b4, SLOT_MIX);
+ break;
+ case BTC_CXP_AUTO2_TD5050:
+ _slot_set(btc, CXST_W1, 50, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 200, tbl_b1, SLOT_MIX);
+ _slot_set(btc, CXST_B4, 50, tbl_b4, SLOT_MIX);
+ break;
+ case BTC_CXP_AUTO2_TD6060:
+ _slot_set(btc, CXST_W1, 60, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 200, tbl_b1, SLOT_MIX);
+ _slot_set(btc, CXST_B4, 60, tbl_b4, SLOT_MIX);
+ break;
+ case BTC_CXP_AUTO2_TD2080:
+ _slot_set(btc, CXST_W1, 20, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 200, tbl_b1, SLOT_MIX);
+ _slot_set(btc, CXST_B4, 80, tbl_b4, SLOT_MIX);
+ break;
+ case BTC_CXP_AUTO2_TDW1B4: /* W1:B1 = user-define */
+ _slot_set(btc, CXST_W1, dm->slot_dur[CXST_W1],
+ tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B4, dm->slot_dur[CXST_B4],
+ tbl_b4, SLOT_MIX);
+ break;
+ }
+ break;
+ case BTC_CXP_PAUTO2: /* PS-TDMA Auto-Slot2 */
+ _write_scbd(rtwdev, BTC_WSCB_TDMA, true);
+ *t = t_def[CXTD_PAUTO2];
+ switch (policy_type) {
+ case BTC_CXP_PAUTO2_TD3050:
+ _slot_set(btc, CXST_W1, 30, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 200, tbl_b1, SLOT_MIX);
+ _slot_set(btc, CXST_B4, 50, tbl_b4, SLOT_MIX);
+ break;
+ case BTC_CXP_PAUTO2_TD3070:
+ _slot_set(btc, CXST_W1, 30, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 200, tbl_b1, SLOT_MIX);
+ _slot_set(btc, CXST_B4, 70, tbl_b4, SLOT_MIX);
+ break;
+ case BTC_CXP_PAUTO2_TD5050:
+ _slot_set(btc, CXST_W1, 50, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 200, tbl_b1, SLOT_MIX);
+ _slot_set(btc, CXST_B4, 50, tbl_b4, SLOT_MIX);
+ break;
+ case BTC_CXP_PAUTO2_TD6060:
+ _slot_set(btc, CXST_W1, 60, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 200, tbl_b1, SLOT_MIX);
+ _slot_set(btc, CXST_B4, 60, tbl_b4, SLOT_MIX);
+ break;
+ case BTC_CXP_PAUTO2_TD2080:
+ _slot_set(btc, CXST_W1, 20, tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B1, 200, tbl_b1, SLOT_MIX);
+ _slot_set(btc, CXST_B4, 80, tbl_b4, SLOT_MIX);
+ break;
+ case BTC_CXP_PAUTO2_TDW1B4: /* W1:B1 = user-define */
+ _slot_set(btc, CXST_W1, dm->slot_dur[CXST_W1],
+ tbl_w1, SLOT_ISO);
+ _slot_set(btc, CXST_B4, dm->slot_dur[CXST_B4],
+ tbl_b4, SLOT_MIX);
+ break;
+ }
+ break;
+ }
+
+ _fw_set_policy(rtwdev, policy_type, action);
+}
+
+static void _set_gnt_bt(struct rtw89_dev *rtwdev, u8 phy_map, u8 state)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_dm *dm = &btc->dm;
+ struct rtw89_mac_ax_gnt *g = dm->gnt.band;
+ u8 i;
+
+ if (phy_map > BTC_PHY_ALL)
+ return;
+
+ for (i = 0; i < RTW89_PHY_MAX; i++) {
+ if (!(phy_map & BIT(i)))
+ continue;
+
+ switch (state) {
+ case BTC_GNT_HW:
+ g[i].gnt_bt_sw_en = 0;
+ g[i].gnt_bt = 0;
+ break;
+ case BTC_GNT_SW_LO:
+ g[i].gnt_bt_sw_en = 1;
+ g[i].gnt_bt = 0;
+ break;
+ case BTC_GNT_SW_HI:
+ g[i].gnt_bt_sw_en = 1;
+ g[i].gnt_bt = 1;
+ break;
+ }
+ }
+
+ rtw89_mac_cfg_gnt(rtwdev, &dm->gnt);
+}
+
+static void _set_bt_plut(struct rtw89_dev *rtwdev, u8 phy_map,
+ u8 tx_val, u8 rx_val)
+{
+ struct rtw89_mac_ax_plt plt;
+
+ plt.band = RTW89_MAC_0;
+ plt.tx = tx_val;
+ plt.rx = rx_val;
+
+ if (phy_map & BTC_PHY_0)
+ rtw89_mac_cfg_plt(rtwdev, &plt);
+
+ if (!rtwdev->dbcc_en)
+ return;
+
+ plt.band = RTW89_MAC_1;
+ if (phy_map & BTC_PHY_1)
+ rtw89_mac_cfg_plt(rtwdev, &plt);
+}
+
+static void _set_ant(struct rtw89_dev *rtwdev, bool force_exec,
+ u8 phy_map, u8 type)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_dm *dm = &btc->dm;
+ struct rtw89_btc_cx *cx = &btc->cx;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+ struct rtw89_btc_bt_info *bt = &cx->bt;
+ struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info;
+ u8 gnt_wl_ctrl, gnt_bt_ctrl, plt_ctrl, i, b2g = 0;
+ u32 ant_path_type;
+
+ ant_path_type = ((phy_map << 8) + type);
+
+ if (btc->dm.run_reason == BTC_RSN_NTFY_POWEROFF ||
+ btc->dm.run_reason == BTC_RSN_NTFY_RADIO_STATE ||
+ btc->dm.run_reason == BTC_RSN_CMD_SET_COEX)
+ force_exec = FC_EXEC;
+
+ if (!force_exec && ant_path_type == dm->set_ant_path) {
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): return by no change!!\n",
+ __func__);
+ return;
+ } else if (bt->rfk_info.map.run) {
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): return by bt rfk!!\n", __func__);
+ return;
+ } else if (btc->dm.run_reason != BTC_RSN_NTFY_WL_RFK &&
+ wl->rfk_info.state != BTC_WRFK_STOP) {
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): return by wl rfk!!\n", __func__);
+ return;
+ }
+
+ dm->set_ant_path = ant_path_type;
+
+ rtw89_debug(rtwdev,
+ RTW89_DBG_BTC,
+ "[BTC], %s(): path=0x%x, set_type=0x%x\n",
+ __func__, phy_map, dm->set_ant_path & 0xff);
+
+ switch (type) {
+ case BTC_ANT_WPOWERON:
+ rtw89_mac_cfg_ctrl_path(rtwdev, false);
+ break;
+ case BTC_ANT_WINIT:
+ if (bt->enable.now) {
+ _set_gnt_wl(rtwdev, phy_map, BTC_GNT_SW_LO);
+ _set_gnt_bt(rtwdev, phy_map, BTC_GNT_SW_HI);
+ } else {
+ _set_gnt_wl(rtwdev, phy_map, BTC_GNT_SW_HI);
+ _set_gnt_bt(rtwdev, phy_map, BTC_GNT_SW_LO);
+ }
+ rtw89_mac_cfg_ctrl_path(rtwdev, true);
+ _set_bt_plut(rtwdev, BTC_PHY_ALL, BTC_PLT_BT, BTC_PLT_BT);
+ break;
+ case BTC_ANT_WONLY:
+ _set_gnt_wl(rtwdev, phy_map, BTC_GNT_SW_HI);
+ _set_gnt_bt(rtwdev, phy_map, BTC_GNT_SW_LO);
+ rtw89_mac_cfg_ctrl_path(rtwdev, true);
+ _set_bt_plut(rtwdev, BTC_PHY_ALL, BTC_PLT_NONE, BTC_PLT_NONE);
+ break;
+ case BTC_ANT_WOFF:
+ rtw89_mac_cfg_ctrl_path(rtwdev, false);
+ _set_bt_plut(rtwdev, BTC_PHY_ALL, BTC_PLT_NONE, BTC_PLT_NONE);
+ break;
+ case BTC_ANT_W2G:
+ rtw89_mac_cfg_ctrl_path(rtwdev, true);
+ if (rtwdev->dbcc_en) {
+ for (i = 0; i < RTW89_PHY_MAX; i++) {
+ b2g = (wl_dinfo->real_band[i] == RTW89_BAND_2G);
+
+ gnt_wl_ctrl = b2g ? BTC_GNT_HW : BTC_GNT_SW_HI;
+ _set_gnt_wl(rtwdev, BIT(i), gnt_wl_ctrl);
+
+ gnt_bt_ctrl = b2g ? BTC_GNT_HW : BTC_GNT_SW_HI;
+ /* BT should control by GNT_BT if WL_2G at S0 */
+ if (i == 1 &&
+ wl_dinfo->real_band[0] == RTW89_BAND_2G &&
+ wl_dinfo->real_band[1] == RTW89_BAND_5G)
+ gnt_bt_ctrl = BTC_GNT_HW;
+ _set_gnt_bt(rtwdev, BIT(i), gnt_bt_ctrl);
+
+ plt_ctrl = b2g ? BTC_PLT_BT : BTC_PLT_NONE;
+ _set_bt_plut(rtwdev, BIT(i),
+ plt_ctrl, plt_ctrl);
+ }
+ } else {
+ _set_gnt_wl(rtwdev, phy_map, BTC_GNT_HW);
+ _set_gnt_bt(rtwdev, phy_map, BTC_GNT_HW);
+ _set_bt_plut(rtwdev, BTC_PHY_ALL,
+ BTC_PLT_BT, BTC_PLT_BT);
+ }
+ break;
+ case BTC_ANT_W5G:
+ rtw89_mac_cfg_ctrl_path(rtwdev, true);
+ _set_gnt_wl(rtwdev, phy_map, BTC_GNT_SW_HI);
+ _set_gnt_bt(rtwdev, phy_map, BTC_GNT_HW);
+ _set_bt_plut(rtwdev, BTC_PHY_ALL, BTC_PLT_NONE, BTC_PLT_NONE);
+ break;
+ case BTC_ANT_W25G:
+ rtw89_mac_cfg_ctrl_path(rtwdev, true);
+ _set_gnt_wl(rtwdev, phy_map, BTC_GNT_HW);
+ _set_gnt_bt(rtwdev, phy_map, BTC_GNT_HW);
+ _set_bt_plut(rtwdev, BTC_PHY_ALL,
+ BTC_PLT_GNT_WL, BTC_PLT_GNT_WL);
+ break;
+ case BTC_ANT_FREERUN:
+ rtw89_mac_cfg_ctrl_path(rtwdev, true);
+ _set_gnt_wl(rtwdev, phy_map, BTC_GNT_SW_HI);
+ _set_gnt_bt(rtwdev, phy_map, BTC_GNT_SW_HI);
+ _set_bt_plut(rtwdev, BTC_PHY_ALL, BTC_PLT_NONE, BTC_PLT_NONE);
+ break;
+ case BTC_ANT_WRFK:
+ rtw89_mac_cfg_ctrl_path(rtwdev, true);
+ _set_gnt_wl(rtwdev, phy_map, BTC_GNT_SW_HI);
+ _set_gnt_bt(rtwdev, phy_map, BTC_GNT_SW_LO);
+ _set_bt_plut(rtwdev, phy_map, BTC_PLT_NONE, BTC_PLT_NONE);
+ break;
+ case BTC_ANT_BRFK:
+ rtw89_mac_cfg_ctrl_path(rtwdev, false);
+ _set_gnt_wl(rtwdev, phy_map, BTC_GNT_SW_LO);
+ _set_gnt_bt(rtwdev, phy_map, BTC_GNT_SW_HI);
+ _set_bt_plut(rtwdev, phy_map, BTC_PLT_NONE, BTC_PLT_NONE);
+ break;
+ default:
+ break;
+ }
+}
+
+static void _action_wl_only(struct rtw89_dev *rtwdev)
+{
+ _set_ant(rtwdev, FC_EXEC, BTC_PHY_ALL, BTC_ANT_WONLY);
+ _set_policy(rtwdev, BTC_CXP_OFF_BT, BTC_ACT_WL_ONLY);
+}
+
+static void _action_wl_init(struct rtw89_dev *rtwdev)
+{
+ rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s !!\n", __func__);
+
+ _set_ant(rtwdev, FC_EXEC, BTC_PHY_ALL, BTC_ANT_WINIT);
+ _set_policy(rtwdev, BTC_CXP_OFF_BT, BTC_ACT_WL_INIT);
+}
+
+static void _action_wl_off(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s !!\n", __func__);
+
+ if (wl->status.map.rf_off || btc->dm.bt_only)
+ _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_WOFF);
+
+ _set_policy(rtwdev, BTC_CXP_OFF_BT, BTC_ACT_WL_OFF);
+}
+
+static void _action_freerun(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s !!\n", __func__);
+
+ _set_ant(rtwdev, FC_EXEC, BTC_PHY_ALL, BTC_ANT_FREERUN);
+ _set_policy(rtwdev, BTC_CXP_OFF_BT, BTC_ACT_FREERUN);
+
+ btc->dm.freerun = true;
+}
+
+static void _action_bt_whql(struct rtw89_dev *rtwdev)
+{
+ rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s !!\n", __func__);
+
+ _set_ant(rtwdev, FC_EXEC, BTC_PHY_ALL, BTC_ANT_W2G);
+ _set_policy(rtwdev, BTC_CXP_OFF_BT, BTC_ACT_BT_WHQL);
+}
+
+static void _action_bt_off(struct rtw89_dev *rtwdev)
+{
+ rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s !!\n", __func__);
+
+ _set_ant(rtwdev, FC_EXEC, BTC_PHY_ALL, BTC_ANT_WONLY);
+ _set_policy(rtwdev, BTC_CXP_OFF_BT, BTC_ACT_BT_OFF);
+}
+
+static void _action_bt_idle(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_bt_link_info *b = &btc->cx.bt.link_info;
+
+ _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G);
+
+ if (btc->mdinfo.ant.type == BTC_ANT_SHARED) { /* shared-antenna */
+ switch (btc->cx.state_map) {
+ case BTC_WBUSY_BNOSCAN: /*wl-busy + bt idle*/
+ if (b->profile_cnt.now > 0)
+ _set_policy(rtwdev, BTC_CXP_FIX_TD4010,
+ BTC_ACT_BT_IDLE);
+ else
+ _set_policy(rtwdev, BTC_CXP_FIX_TD4020,
+ BTC_ACT_BT_IDLE);
+ break;
+ case BTC_WBUSY_BSCAN: /*wl-busy + bt-inq */
+ _set_policy(rtwdev, BTC_CXP_PFIX_TD5050,
+ BTC_ACT_BT_IDLE);
+ break;
+ case BTC_WSCAN_BNOSCAN: /* wl-scan + bt-idle */
+ if (b->profile_cnt.now > 0)
+ _set_policy(rtwdev, BTC_CXP_FIX_TD4010,
+ BTC_ACT_BT_IDLE);
+ else
+ _set_policy(rtwdev, BTC_CXP_FIX_TD4020,
+ BTC_ACT_BT_IDLE);
+ break;
+ case BTC_WSCAN_BSCAN: /* wl-scan + bt-inq */
+ _set_policy(rtwdev, BTC_CXP_FIX_TD5050,
+ BTC_ACT_BT_IDLE);
+ break;
+ case BTC_WLINKING: /* wl-connecting + bt-inq or bt-idle */
+ _set_policy(rtwdev, BTC_CXP_FIX_TD7010,
+ BTC_ACT_BT_IDLE);
+ break;
+ case BTC_WIDLE: /* wl-idle + bt-idle */
+ _set_policy(rtwdev, BTC_CXP_OFF_BWB1, BTC_ACT_BT_IDLE);
+ break;
+ }
+ } else { /* dedicated-antenna */
+ _set_policy(rtwdev, BTC_CXP_OFF_EQ0, BTC_ACT_BT_IDLE);
+ }
+}
+
+static void _action_bt_hfp(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+
+ _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G);
+
+ if (btc->mdinfo.ant.type == BTC_ANT_SHARED) {
+ if (btc->cx.wl.status.map._4way)
+ _set_policy(rtwdev, BTC_CXP_OFF_WL, BTC_ACT_BT_HFP);
+ else
+ _set_policy(rtwdev, BTC_CXP_OFF_BWB0, BTC_ACT_BT_HFP);
+ } else {
+ _set_policy(rtwdev, BTC_CXP_OFF_EQ2, BTC_ACT_BT_HFP);
+ }
+}
+
+static void _action_bt_hid(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+
+ _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G);
+
+ if (btc->mdinfo.ant.type == BTC_ANT_SHARED) /* shared-antenna */
+ if (btc->cx.wl.status.map._4way)
+ _set_policy(rtwdev, BTC_CXP_OFF_WL, BTC_ACT_BT_HID);
+ else
+ _set_policy(rtwdev, BTC_CXP_OFF_BWB0, BTC_ACT_BT_HID);
+ else /* dedicated-antenna */
+ _set_policy(rtwdev, BTC_CXP_OFF_EQ3, BTC_ACT_BT_HID);
+}
+
+static void _action_bt_a2dp(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt.link_info;
+ struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc;
+ struct rtw89_btc_dm *dm = &btc->dm;
+
+ _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G);
+
+ switch (btc->cx.state_map) {
+ case BTC_WBUSY_BNOSCAN: /* wl-busy + bt-A2DP */
+ if (a2dp.vendor_id == 0x4c || dm->leak_ap) {
+ dm->slot_dur[CXST_W1] = 40;
+ dm->slot_dur[CXST_B1] = 200;
+ _set_policy(rtwdev,
+ BTC_CXP_PAUTO_TDW1B1, BTC_ACT_BT_A2DP);
+ } else {
+ _set_policy(rtwdev,
+ BTC_CXP_PAUTO_TD50200, BTC_ACT_BT_A2DP);
+ }
+ break;
+ case BTC_WBUSY_BSCAN: /* wl-busy + bt-inq + bt-A2DP */
+ _set_policy(rtwdev, BTC_CXP_PAUTO2_TD3050, BTC_ACT_BT_A2DP);
+ break;
+ case BTC_WSCAN_BSCAN: /* wl-scan + bt-inq + bt-A2DP */
+ _set_policy(rtwdev, BTC_CXP_AUTO2_TD3050, BTC_ACT_BT_A2DP);
+ break;
+ case BTC_WSCAN_BNOSCAN: /* wl-scan + bt-A2DP */
+ case BTC_WLINKING: /* wl-connecting + bt-A2DP */
+ if (a2dp.vendor_id == 0x4c || dm->leak_ap) {
+ dm->slot_dur[CXST_W1] = 40;
+ dm->slot_dur[CXST_B1] = 200;
+ _set_policy(rtwdev, BTC_CXP_AUTO_TDW1B1,
+ BTC_ACT_BT_A2DP);
+ } else {
+ _set_policy(rtwdev, BTC_CXP_AUTO_TD50200,
+ BTC_ACT_BT_A2DP);
+ }
+ break;
+ case BTC_WIDLE: /* wl-idle + bt-A2DP */
+ _set_policy(rtwdev, BTC_CXP_AUTO_TD20200, BTC_ACT_BT_A2DP);
+ break;
+ }
+}
+
+static void _action_bt_a2dpsink(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+
+ _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G);
+
+ switch (btc->cx.state_map) {
+ case BTC_WBUSY_BNOSCAN: /* wl-busy + bt-A2dp_Sink */
+ _set_policy(rtwdev, BTC_CXP_PFIX_TD2030, BTC_ACT_BT_A2DPSINK);
+ break;
+ case BTC_WBUSY_BSCAN: /* wl-busy + bt-inq + bt-A2dp_Sink */
+ _set_policy(rtwdev, BTC_CXP_PFIX_TD2060, BTC_ACT_BT_A2DPSINK);
+ break;
+ case BTC_WSCAN_BNOSCAN: /* wl-scan + bt-A2dp_Sink */
+ _set_policy(rtwdev, BTC_CXP_FIX_TD2030, BTC_ACT_BT_A2DPSINK);
+ break;
+ case BTC_WSCAN_BSCAN: /* wl-scan + bt-inq + bt-A2dp_Sink */
+ _set_policy(rtwdev, BTC_CXP_FIX_TD2060, BTC_ACT_BT_A2DPSINK);
+ break;
+ case BTC_WLINKING: /* wl-connecting + bt-A2dp_Sink */
+ _set_policy(rtwdev, BTC_CXP_FIX_TD3030, BTC_ACT_BT_A2DPSINK);
+ break;
+ case BTC_WIDLE: /* wl-idle + bt-A2dp_Sink */
+ _set_policy(rtwdev, BTC_CXP_FIX_TD2080, BTC_ACT_BT_A2DPSINK);
+ break;
+ }
+}
+
+static void _action_bt_pan(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+
+ _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G);
+
+ switch (btc->cx.state_map) {
+ case BTC_WBUSY_BNOSCAN: /* wl-busy + bt-PAN */
+ _set_policy(rtwdev, BTC_CXP_PFIX_TD5050, BTC_ACT_BT_PAN);
+ break;
+ case BTC_WBUSY_BSCAN: /* wl-busy + bt-inq + bt-PAN */
+ _set_policy(rtwdev, BTC_CXP_PFIX_TD3070, BTC_ACT_BT_PAN);
+ break;
+ case BTC_WSCAN_BNOSCAN: /* wl-scan + bt-PAN */
+ _set_policy(rtwdev, BTC_CXP_FIX_TD3030, BTC_ACT_BT_PAN);
+ break;
+ case BTC_WSCAN_BSCAN: /* wl-scan + bt-inq + bt-PAN */
+ _set_policy(rtwdev, BTC_CXP_FIX_TD3060, BTC_ACT_BT_PAN);
+ break;
+ case BTC_WLINKING: /* wl-connecting + bt-PAN */
+ _set_policy(rtwdev, BTC_CXP_FIX_TD4020, BTC_ACT_BT_PAN);
+ break;
+ case BTC_WIDLE: /* wl-idle + bt-pan */
+ _set_policy(rtwdev, BTC_CXP_PFIX_TD2080, BTC_ACT_BT_PAN);
+ break;
+ }
+}
+
+static void _action_bt_a2dp_hid(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt.link_info;
+ struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc;
+ struct rtw89_btc_dm *dm = &btc->dm;
+
+ _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G);
+
+ switch (btc->cx.state_map) {
+ case BTC_WBUSY_BNOSCAN: /* wl-busy + bt-A2DP+HID */
+ case BTC_WIDLE: /* wl-idle + bt-A2DP */
+ if (a2dp.vendor_id == 0x4c || dm->leak_ap) {
+ dm->slot_dur[CXST_W1] = 40;
+ dm->slot_dur[CXST_B1] = 200;
+ _set_policy(rtwdev,
+ BTC_CXP_PAUTO_TDW1B1, BTC_ACT_BT_A2DP_HID);
+ } else {
+ _set_policy(rtwdev,
+ BTC_CXP_PAUTO_TD50200, BTC_ACT_BT_A2DP_HID);
+ }
+ break;
+ case BTC_WBUSY_BSCAN: /* wl-busy + bt-inq + bt-A2DP+HID */
+ _set_policy(rtwdev, BTC_CXP_PAUTO2_TD3050, BTC_ACT_BT_A2DP_HID);
+ break;
+
+ case BTC_WSCAN_BSCAN: /* wl-scan + bt-inq + bt-A2DP+HID */
+ _set_policy(rtwdev, BTC_CXP_AUTO2_TD3050, BTC_ACT_BT_A2DP_HID);
+ break;
+ case BTC_WSCAN_BNOSCAN: /* wl-scan + bt-A2DP+HID */
+ case BTC_WLINKING: /* wl-connecting + bt-A2DP+HID */
+ if (a2dp.vendor_id == 0x4c || dm->leak_ap) {
+ dm->slot_dur[CXST_W1] = 40;
+ dm->slot_dur[CXST_B1] = 200;
+ _set_policy(rtwdev, BTC_CXP_AUTO_TDW1B1,
+ BTC_ACT_BT_A2DP_HID);
+ } else {
+ _set_policy(rtwdev, BTC_CXP_AUTO_TD50200,
+ BTC_ACT_BT_A2DP_HID);
+ }
+ break;
+ }
+}
+
+static void _action_bt_a2dp_pan(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+
+ _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G);
+
+ switch (btc->cx.state_map) {
+ case BTC_WBUSY_BNOSCAN: /* wl-busy + bt-A2DP+PAN */
+ _set_policy(rtwdev, BTC_CXP_PAUTO2_TD3070, BTC_ACT_BT_A2DP_PAN);
+ break;
+ case BTC_WBUSY_BSCAN: /* wl-busy + bt-inq + bt-A2DP+PAN */
+ _set_policy(rtwdev, BTC_CXP_PAUTO2_TD3070, BTC_ACT_BT_A2DP_PAN);
+ break;
+ case BTC_WSCAN_BNOSCAN: /* wl-scan + bt-A2DP+PAN */
+ _set_policy(rtwdev, BTC_CXP_AUTO2_TD5050, BTC_ACT_BT_A2DP_PAN);
+ break;
+ case BTC_WSCAN_BSCAN: /* wl-scan + bt-inq + bt-A2DP+PAN */
+ _set_policy(rtwdev, BTC_CXP_AUTO2_TD3070, BTC_ACT_BT_A2DP_PAN);
+ break;
+ case BTC_WLINKING: /* wl-connecting + bt-A2DP+PAN */
+ _set_policy(rtwdev, BTC_CXP_AUTO2_TD3050, BTC_ACT_BT_A2DP_PAN);
+ break;
+ case BTC_WIDLE: /* wl-idle + bt-A2DP+PAN */
+ _set_policy(rtwdev, BTC_CXP_PAUTO2_TD2080, BTC_ACT_BT_A2DP_PAN);
+ break;
+ }
+}
+
+static void _action_bt_pan_hid(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+
+ _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G);
+
+ switch (btc->cx.state_map) {
+ case BTC_WBUSY_BNOSCAN: /* wl-busy + bt-PAN+HID */
+ _set_policy(rtwdev, BTC_CXP_PFIX_TD3030, BTC_ACT_BT_PAN_HID);
+ break;
+ case BTC_WBUSY_BSCAN: /* wl-busy + bt-inq + bt-PAN+HID */
+ _set_policy(rtwdev, BTC_CXP_PFIX_TD3070, BTC_ACT_BT_PAN_HID);
+ break;
+ case BTC_WSCAN_BNOSCAN: /* wl-scan + bt-PAN+HID */
+ _set_policy(rtwdev, BTC_CXP_FIX_TD3030, BTC_ACT_BT_PAN_HID);
+ break;
+ case BTC_WSCAN_BSCAN: /* wl-scan + bt-inq + bt-PAN+HID */
+ _set_policy(rtwdev, BTC_CXP_FIX_TD3060, BTC_ACT_BT_PAN_HID);
+ break;
+ case BTC_WLINKING: /* wl-connecting + bt-PAN+HID */
+ _set_policy(rtwdev, BTC_CXP_FIX_TD4010, BTC_ACT_BT_PAN_HID);
+ break;
+ case BTC_WIDLE: /* wl-idle + bt-PAN+HID */
+ _set_policy(rtwdev, BTC_CXP_PFIX_TD2080, BTC_ACT_BT_PAN_HID);
+ break;
+ }
+}
+
+static void _action_bt_a2dp_pan_hid(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+
+ _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G);
+
+ switch (btc->cx.state_map) {
+ case BTC_WBUSY_BNOSCAN: /* wl-busy + bt-A2DP+PAN+HID */
+ _set_policy(rtwdev, BTC_CXP_PAUTO2_TD3070,
+ BTC_ACT_BT_A2DP_PAN_HID);
+ break;
+ case BTC_WBUSY_BSCAN: /* wl-busy + bt-inq + bt-A2DP+PAN+HID */
+ _set_policy(rtwdev, BTC_CXP_PAUTO2_TD3070,
+ BTC_ACT_BT_A2DP_PAN_HID);
+ break;
+ case BTC_WSCAN_BSCAN: /* wl-scan + bt-inq + bt-A2DP+PAN+HID */
+ _set_policy(rtwdev, BTC_CXP_AUTO2_TD3070,
+ BTC_ACT_BT_A2DP_PAN_HID);
+ break;
+ case BTC_WSCAN_BNOSCAN: /* wl-scan + bt-A2DP+PAN+HID */
+ case BTC_WLINKING: /* wl-connecting + bt-A2DP+PAN+HID */
+ _set_policy(rtwdev, BTC_CXP_AUTO2_TD3050,
+ BTC_ACT_BT_A2DP_PAN_HID);
+ break;
+ case BTC_WIDLE: /* wl-idle + bt-A2DP+PAN+HID */
+ _set_policy(rtwdev, BTC_CXP_PAUTO2_TD2080,
+ BTC_ACT_BT_A2DP_PAN_HID);
+ break;
+ }
+}
+
+static void _action_wl_5g(struct rtw89_dev *rtwdev)
+{
+ _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W5G);
+ _set_policy(rtwdev, BTC_CXP_OFF_EQ0, BTC_ACT_WL_5G);
+}
+
+static void _action_wl_other(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+
+ _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G);
+
+ if (btc->mdinfo.ant.type == BTC_ANT_SHARED)
+ _set_policy(rtwdev, BTC_CXP_OFFB_BWB0, BTC_ACT_WL_OTHER);
+ else
+ _set_policy(rtwdev, BTC_CXP_OFF_EQ0, BTC_ACT_WL_OTHER);
+}
+
+static void _action_wl_nc(struct rtw89_dev *rtwdev)
+{
+ _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G);
+ _set_policy(rtwdev, BTC_CXP_OFF_BT, BTC_ACT_WL_NC);
+}
+
+static void _action_wl_rfk(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_wl_rfk_info rfk = btc->cx.wl.rfk_info;
+
+ if (rfk.state != BTC_WRFK_START)
+ return;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): band = %d\n",
+ __func__, rfk.band);
+
+ _set_ant(rtwdev, FC_EXEC, BTC_PHY_ALL, BTC_ANT_WRFK);
+ _set_policy(rtwdev, BTC_CXP_OFF_WL, BTC_ACT_WL_RFK);
+}
+
+static void _set_btg_ctrl(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+ struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info;
+ struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info;
+ bool is_btg = false;
+
+ if (btc->ctrl.manual)
+ return;
+
+ /* notify halbb ignore GNT_BT or not for WL BB Rx-AGC control */
+ if (btc->mdinfo.ant.type == BTC_ANT_DEDICATED)
+ is_btg = false;
+ else if (wl_rinfo->link_mode == BTC_WLINK_5G) /* always 0 if 5G */
+ is_btg = false;
+ else if (wl_rinfo->link_mode == BTC_WLINK_25G_DBCC &&
+ wl_dinfo->real_band[RTW89_PHY_1] != RTW89_BAND_2G)
+ is_btg = false;
+ else
+ is_btg = true;
+
+ if (btc->dm.run_reason != BTC_RSN_NTFY_INIT &&
+ is_btg == btc->dm.wl_btg_rx)
+ return;
+
+ btc->dm.wl_btg_rx = is_btg;
+
+ if (wl_rinfo->link_mode == BTC_WLINK_25G_MCC)
+ return;
+
+ rtw89_ctrl_btg(rtwdev, is_btg);
+}
+
+struct rtw89_txtime_data {
+ struct rtw89_dev *rtwdev;
+ int type;
+ u32 tx_time;
+ u8 tx_retry;
+ u16 enable;
+ bool reenable;
+};
+
+static void rtw89_tx_time_iter(void *data, struct ieee80211_sta *sta)
+{
+ struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv;
+ struct rtw89_txtime_data *iter_data =
+ (struct rtw89_txtime_data *)data;
+ struct rtw89_dev *rtwdev = iter_data->rtwdev;
+ struct rtw89_vif *rtwvif = rtwsta->rtwvif;
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_cx *cx = &btc->cx;
+ struct rtw89_btc_wl_info *wl = &cx->wl;
+ struct rtw89_btc_wl_link_info *plink = NULL;
+ u8 port = rtwvif->port;
+ u32 tx_time = iter_data->tx_time;
+ u8 tx_retry = iter_data->tx_retry;
+ u16 enable = iter_data->enable;
+ bool reenable = iter_data->reenable;
+
+ plink = &wl->link_info[port];
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): port = %d\n", __func__, port);
+
+ if (!plink->connected) {
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): connected = %d\n",
+ __func__, plink->connected);
+ return;
+ }
+
+ /* backup the original tx time before tx-limit on */
+ if (reenable) {
+ rtw89_mac_get_tx_time(rtwdev, rtwsta, &plink->tx_time);
+ rtw89_mac_get_tx_retry_limit(rtwdev, rtwsta, &plink->tx_retry);
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): reenable, tx_time=%d tx_retry= %d\n",
+ __func__, plink->tx_time, plink->tx_retry);
+ }
+
+ /* restore the original tx time if no tx-limit */
+ if (!enable) {
+ rtw89_mac_set_tx_time(rtwdev, rtwsta, true, plink->tx_time);
+ rtw89_mac_set_tx_retry_limit(rtwdev, rtwsta, true,
+ plink->tx_retry);
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): restore, tx_time=%d tx_retry= %d\n",
+ __func__, plink->tx_time, plink->tx_retry);
+
+ } else {
+ rtw89_mac_set_tx_time(rtwdev, rtwsta, false, tx_time);
+ rtw89_mac_set_tx_retry_limit(rtwdev, rtwsta, false, tx_retry);
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): set, tx_time=%d tx_retry= %d\n",
+ __func__, tx_time, tx_retry);
+ }
+}
+
+static void _set_wl_tx_limit(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_cx *cx = &btc->cx;
+ struct rtw89_btc_dm *dm = &btc->dm;
+ struct rtw89_btc_wl_info *wl = &cx->wl;
+ struct rtw89_btc_bt_info *bt = &cx->bt;
+ struct rtw89_btc_bt_link_info *b = &bt->link_info;
+ struct rtw89_btc_bt_hfp_desc *hfp = &b->hfp_desc;
+ struct rtw89_btc_bt_hid_desc *hid = &b->hid_desc;
+ struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info;
+ struct rtw89_txtime_data data = {.rtwdev = rtwdev};
+ u8 mode = wl_rinfo->link_mode;
+ u8 tx_retry = 0;
+ u32 tx_time = 0;
+ u16 enable = 0;
+ bool reenable = false;
+
+ if (btc->ctrl.manual)
+ return;
+
+ if (btc->dm.freerun || btc->ctrl.igno_bt || b->profile_cnt.now == 0 ||
+ mode == BTC_WLINK_5G || mode == BTC_WLINK_NOLINK) {
+ enable = 0;
+ tx_time = BTC_MAX_TX_TIME_DEF;
+ tx_retry = BTC_MAX_TX_RETRY_DEF;
+ } else if ((hfp->exist && hid->exist) || hid->pair_cnt > 1) {
+ enable = 1;
+ tx_time = BTC_MAX_TX_TIME_L2;
+ tx_retry = BTC_MAX_TX_RETRY_L1;
+ } else if (hfp->exist || hid->exist) {
+ enable = 1;
+ tx_time = BTC_MAX_TX_TIME_L3;
+ tx_retry = BTC_MAX_TX_RETRY_L1;
+ } else {
+ enable = 0;
+ tx_time = BTC_MAX_TX_TIME_DEF;
+ tx_retry = BTC_MAX_TX_RETRY_DEF;
+ }
+
+ if (dm->wl_tx_limit.enable == enable &&
+ dm->wl_tx_limit.tx_time == tx_time &&
+ dm->wl_tx_limit.tx_retry == tx_retry)
+ return;
+
+ if (!dm->wl_tx_limit.enable && enable)
+ reenable = true;
+
+ dm->wl_tx_limit.enable = enable;
+ dm->wl_tx_limit.tx_time = tx_time;
+ dm->wl_tx_limit.tx_retry = tx_retry;
+
+ data.enable = enable;
+ data.tx_time = tx_time;
+ data.tx_retry = tx_retry;
+ data.reenable = reenable;
+
+ ieee80211_iterate_stations_atomic(rtwdev->hw,
+ rtw89_tx_time_iter,
+ &data);
+}
+
+#define _set_bt_slot_req(rtwdev) do {} while (0)
+
+static void _set_bt_rx_agc(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+ struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info;
+ struct rtw89_btc_bt_info *bt = &btc->cx.bt;
+ bool bt_hi_lna_rx = false;
+
+ if (wl_rinfo->link_mode != BTC_WLINK_NOLINK && btc->dm.wl_btg_rx)
+ bt_hi_lna_rx = true;
+
+ if (bt_hi_lna_rx == bt->hi_lna_rx)
+ return;
+
+ _write_scbd(rtwdev, BTC_WSCB_BT_HILNA, bt_hi_lna_rx);
+}
+
+/* TODO add these functions */
+static void _action_common(struct rtw89_dev *rtwdev)
+{
+ _set_btg_ctrl(rtwdev);
+ _set_wl_tx_limit(rtwdev);
+ _set_bt_afh_info(rtwdev);
+ _set_bt_rx_agc(rtwdev);
+ _set_bt_slot_req(rtwdev);
+ _set_rf_trx_para(rtwdev);
+}
+
+static void _action_by_bt(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_bt_info *bt = &btc->cx.bt;
+ struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info;
+ struct rtw89_btc_bt_hid_desc hid = bt_linfo->hid_desc;
+ struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc;
+ struct rtw89_btc_bt_pan_desc pan = bt_linfo->pan_desc;
+ u8 profile_map = 0;
+
+ if (bt_linfo->hfp_desc.exist)
+ profile_map |= BTC_BT_HFP;
+
+ if (bt_linfo->hid_desc.exist)
+ profile_map |= BTC_BT_HID;
+
+ if (bt_linfo->a2dp_desc.exist)
+ profile_map |= BTC_BT_A2DP;
+
+ if (bt_linfo->pan_desc.exist)
+ profile_map |= BTC_BT_PAN;
+
+ switch (profile_map) {
+ case BTC_BT_NOPROFILE:
+ if (_check_freerun(rtwdev))
+ _action_freerun(rtwdev);
+ else if (a2dp.active || pan.active)
+ _action_bt_pan(rtwdev);
+ else
+ _action_bt_idle(rtwdev);
+ break;
+ case BTC_BT_HFP:
+ if (_check_freerun(rtwdev))
+ _action_freerun(rtwdev);
+ else
+ _action_bt_hfp(rtwdev);
+ break;
+ case BTC_BT_HFP | BTC_BT_HID:
+ case BTC_BT_HID:
+ if (_check_freerun(rtwdev))
+ _action_freerun(rtwdev);
+ else
+ _action_bt_hid(rtwdev);
+ break;
+ case BTC_BT_A2DP:
+ if (_check_freerun(rtwdev))
+ _action_freerun(rtwdev);
+ else if (a2dp.sink)
+ _action_bt_a2dpsink(rtwdev);
+ else if (bt_linfo->multi_link.now && !hid.pair_cnt)
+ _action_bt_a2dp_pan(rtwdev);
+ else
+ _action_bt_a2dp(rtwdev);
+ break;
+ case BTC_BT_PAN:
+ _action_bt_pan(rtwdev);
+ break;
+ case BTC_BT_A2DP | BTC_BT_HFP:
+ case BTC_BT_A2DP | BTC_BT_HID:
+ case BTC_BT_A2DP | BTC_BT_HFP | BTC_BT_HID:
+ if (_check_freerun(rtwdev))
+ _action_freerun(rtwdev);
+ else
+ _action_bt_a2dp_hid(rtwdev);
+ break;
+ case BTC_BT_A2DP | BTC_BT_PAN:
+ _action_bt_a2dp_pan(rtwdev);
+ break;
+ case BTC_BT_PAN | BTC_BT_HFP:
+ case BTC_BT_PAN | BTC_BT_HID:
+ case BTC_BT_PAN | BTC_BT_HFP | BTC_BT_HID:
+ _action_bt_pan_hid(rtwdev);
+ break;
+ case BTC_BT_A2DP | BTC_BT_PAN | BTC_BT_HID:
+ case BTC_BT_A2DP | BTC_BT_PAN | BTC_BT_HFP:
+ default:
+ _action_bt_a2dp_pan_hid(rtwdev);
+ break;
+ }
+}
+
+static void _action_wl_2g_sta(struct rtw89_dev *rtwdev)
+{
+ _action_by_bt(rtwdev);
+}
+
+static void _action_wl_scan(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+ struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info;
+
+ if (rtwdev->dbcc_en) {
+ if (wl_dinfo->real_band[RTW89_PHY_0] != RTW89_BAND_2G &&
+ wl_dinfo->real_band[RTW89_PHY_1] != RTW89_BAND_2G)
+ _action_wl_5g(rtwdev);
+ else
+ _action_by_bt(rtwdev);
+ } else {
+ if (wl->scan_info.band[RTW89_PHY_0] != RTW89_BAND_2G)
+ _action_wl_5g(rtwdev);
+ else
+ _action_by_bt(rtwdev);
+ }
+}
+
+static void _action_wl_25g_mcc(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+
+ _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W25G);
+
+ if (btc->mdinfo.ant.type == BTC_ANT_SHARED) {
+ if (btc->cx.bt.link_info.profile_cnt.now == 0)
+ _set_policy(rtwdev, BTC_CXP_OFFE_DEF2,
+ BTC_ACT_WL_25G_MCC);
+ else
+ _set_policy(rtwdev, BTC_CXP_OFFE_DEF,
+ BTC_ACT_WL_25G_MCC);
+ } else { /* dedicated-antenna */
+ _set_policy(rtwdev, BTC_CXP_OFF_EQ0, BTC_ACT_WL_25G_MCC);
+ }
+}
+
+static void _action_wl_2g_mcc(struct rtw89_dev *rtwdev)
+{ struct rtw89_btc *btc = &rtwdev->btc;
+
+ _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G);
+
+ if (btc->mdinfo.ant.type == BTC_ANT_SHARED) { /* shared-antenna */
+ if (btc->cx.bt.link_info.profile_cnt.now == 0)
+ _set_policy(rtwdev, BTC_CXP_OFFE_DEF2,
+ BTC_ACT_WL_2G_MCC);
+ else
+ _set_policy(rtwdev, BTC_CXP_OFFE_DEF,
+ BTC_ACT_WL_2G_MCC);
+ } else { /* dedicated-antenna */
+ _set_policy(rtwdev, BTC_CXP_OFF_EQ0, BTC_ACT_WL_2G_MCC);
+ }
+}
+
+static void _action_wl_2g_scc(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+
+ _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G);
+
+ if (btc->mdinfo.ant.type == BTC_ANT_SHARED) { /* shared-antenna */
+ if (btc->cx.bt.link_info.profile_cnt.now == 0)
+ _set_policy(rtwdev,
+ BTC_CXP_OFFE_DEF2, BTC_ACT_WL_2G_SCC);
+ else
+ _set_policy(rtwdev,
+ BTC_CXP_OFFE_DEF, BTC_ACT_WL_2G_SCC);
+ } else { /* dedicated-antenna */
+ _set_policy(rtwdev, BTC_CXP_OFF_EQ0, BTC_ACT_WL_2G_SCC);
+ }
+}
+
+static void _action_wl_2g_ap(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+
+ _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G);
+
+ if (btc->mdinfo.ant.type == BTC_ANT_SHARED) {
+ if (btc->cx.bt.link_info.profile_cnt.now == 0)
+ _set_policy(rtwdev, BTC_CXP_OFFE_DEF2,
+ BTC_ACT_WL_2G_AP);
+ else
+ _set_policy(rtwdev, BTC_CXP_OFFE_DEF, BTC_ACT_WL_2G_AP);
+ } else {/* dedicated-antenna */
+ _set_policy(rtwdev, BTC_CXP_OFF_EQ0, BTC_ACT_WL_2G_AP);
+ }
+}
+
+static void _action_wl_2g_go(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+
+ _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G);
+
+ if (btc->mdinfo.ant.type == BTC_ANT_SHARED) { /* shared-antenna */
+ if (btc->cx.bt.link_info.profile_cnt.now == 0)
+ _set_policy(rtwdev,
+ BTC_CXP_OFFE_DEF2, BTC_ACT_WL_2G_GO);
+ else
+ _set_policy(rtwdev,
+ BTC_CXP_OFFE_DEF, BTC_ACT_WL_2G_GO);
+ } else { /* dedicated-antenna */
+ _set_policy(rtwdev, BTC_CXP_OFF_EQ0, BTC_ACT_WL_2G_GO);
+ }
+}
+
+static void _action_wl_2g_gc(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+
+ _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G);
+
+ if (btc->mdinfo.ant.type == BTC_ANT_SHARED) { /* shared-antenna */
+ _action_by_bt(rtwdev);
+ } else {/* dedicated-antenna */
+ _set_policy(rtwdev, BTC_CXP_OFF_EQ0, BTC_ACT_WL_2G_GC);
+ }
+}
+
+static void _action_wl_2g_nan(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+
+ _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G);
+
+ if (btc->mdinfo.ant.type == BTC_ANT_SHARED) { /* shared-antenna */
+ if (btc->cx.bt.link_info.profile_cnt.now == 0)
+ _set_policy(rtwdev,
+ BTC_CXP_OFFE_DEF2, BTC_ACT_WL_2G_NAN);
+ else
+ _set_policy(rtwdev,
+ BTC_CXP_OFFE_DEF, BTC_ACT_WL_2G_NAN);
+ } else { /* dedicated-antenna */
+ _set_policy(rtwdev, BTC_CXP_OFF_EQ0, BTC_ACT_WL_2G_NAN);
+ }
+}
+
+static u32 _read_scbd(struct rtw89_dev *rtwdev)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_btc *btc = &rtwdev->btc;
+ u32 scbd_val = 0;
+
+ if (!chip->scbd)
+ return 0;
+
+ scbd_val = rtw89_mac_get_sb(rtwdev);
+ rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC] read scbd: 0x%08x\n",
+ scbd_val);
+
+ btc->cx.cnt_bt[BTC_BCNT_SCBDREAD]++;
+ return scbd_val;
+}
+
+static void _write_scbd(struct rtw89_dev *rtwdev, u32 val, bool state)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+ u32 scbd_val = 0;
+
+ if (!chip->scbd)
+ return;
+
+ scbd_val = state ? wl->scbd | val : wl->scbd & ~val;
+
+ if (scbd_val == wl->scbd)
+ return;
+ rtw89_mac_cfg_sb(rtwdev, scbd_val);
+ rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC] write scbd: 0x%08x\n",
+ scbd_val);
+ wl->scbd = scbd_val;
+
+ btc->cx.cnt_wl[BTC_WCNT_SCBDUPDATE]++;
+}
+
+static
+void _update_dbcc_band(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+
+ btc->cx.wl.dbcc_info.real_band[phy_idx] =
+ btc->cx.wl.scan_info.phy_map & BIT(phy_idx) ?
+ btc->cx.wl.dbcc_info.scan_band[phy_idx] :
+ btc->cx.wl.dbcc_info.op_band[phy_idx];
+}
+
+static void _update_wl_info(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+ struct rtw89_btc_wl_link_info *wl_linfo = wl->link_info;
+ struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info;
+ struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info;
+ u8 i, cnt_connect = 0, cnt_connecting = 0, cnt_active = 0;
+ u8 cnt_2g = 0, cnt_5g = 0, phy;
+ u32 wl_2g_ch[2] = {0}, wl_5g_ch[2] = {0};
+ bool b2g = false, b5g = false, client_joined = false;
+
+ memset(wl_rinfo, 0, sizeof(*wl_rinfo));
+
+ for (i = 0; i < RTW89_MAX_HW_PORT_NUM; i++) {
+ /* check if role active? */
+ if (!wl_linfo[i].active)
+ continue;
+
+ cnt_active++;
+ wl_rinfo->active_role[cnt_active - 1].role = wl_linfo[i].role;
+ wl_rinfo->active_role[cnt_active - 1].pid = wl_linfo[i].pid;
+ wl_rinfo->active_role[cnt_active - 1].phy = wl_linfo[i].phy;
+ wl_rinfo->active_role[cnt_active - 1].band = wl_linfo[i].band;
+ wl_rinfo->active_role[cnt_active - 1].noa = (u8)wl_linfo[i].noa;
+ wl_rinfo->active_role[cnt_active - 1].connected = 0;
+
+ wl->port_id[wl_linfo[i].role] = wl_linfo[i].pid;
+
+ phy = wl_linfo[i].phy;
+
+ /* check dbcc role */
+ if (rtwdev->dbcc_en && phy < RTW89_PHY_MAX) {
+ wl_dinfo->role[phy] = wl_linfo[i].role;
+ wl_dinfo->op_band[phy] = wl_linfo[i].band;
+ _update_dbcc_band(rtwdev, phy);
+ _fw_set_drv_info(rtwdev, CXDRVINFO_DBCC);
+ }
+
+ if (wl_linfo[i].connected == MLME_NO_LINK) {
+ continue;
+ } else if (wl_linfo[i].connected == MLME_LINKING) {
+ cnt_connecting++;
+ } else {
+ cnt_connect++;
+ if ((wl_linfo[i].role == RTW89_WIFI_ROLE_P2P_GO ||
+ wl_linfo[i].role == RTW89_WIFI_ROLE_AP) &&
+ wl_linfo[i].client_cnt > 1)
+ client_joined = true;
+ }
+
+ wl_rinfo->role_map.val |= BIT(wl_linfo[i].role);
+ wl_rinfo->active_role[cnt_active - 1].ch = wl_linfo[i].ch;
+ wl_rinfo->active_role[cnt_active - 1].bw = wl_linfo[i].bw;
+ wl_rinfo->active_role[cnt_active - 1].connected = 1;
+
+ /* only care 2 roles + BT coex */
+ if (wl_linfo[i].band != RTW89_BAND_2G) {
+ if (cnt_5g <= ARRAY_SIZE(wl_5g_ch) - 1)
+ wl_5g_ch[cnt_5g] = wl_linfo[i].ch;
+ cnt_5g++;
+ b5g = true;
+ } else {
+ if (cnt_2g <= ARRAY_SIZE(wl_2g_ch) - 1)
+ wl_2g_ch[cnt_2g] = wl_linfo[i].ch;
+ cnt_2g++;
+ b2g = true;
+ }
+ }
+
+ wl_rinfo->connect_cnt = cnt_connect;
+
+ /* Be careful to change the following sequence!! */
+ if (cnt_connect == 0) {
+ wl_rinfo->link_mode = BTC_WLINK_NOLINK;
+ } else if (!b2g && b5g) {
+ wl_rinfo->link_mode = BTC_WLINK_5G;
+ } else if (wl_rinfo->role_map.role.nan) {
+ wl_rinfo->link_mode = BTC_WLINK_2G_NAN;
+ } else if (cnt_connect > BTC_TDMA_WLROLE_MAX) {
+ wl_rinfo->link_mode = BTC_WLINK_OTHER;
+ } else if (b2g && b5g && cnt_connect == 2) {
+ if (rtwdev->dbcc_en) {
+ switch (wl_dinfo->role[RTW89_PHY_0]) {
+ case RTW89_WIFI_ROLE_STATION:
+ wl_rinfo->link_mode = BTC_WLINK_2G_STA;
+ break;
+ case RTW89_WIFI_ROLE_P2P_GO:
+ wl_rinfo->link_mode = BTC_WLINK_2G_GO;
+ break;
+ case RTW89_WIFI_ROLE_P2P_CLIENT:
+ wl_rinfo->link_mode = BTC_WLINK_2G_GC;
+ break;
+ case RTW89_WIFI_ROLE_AP:
+ wl_rinfo->link_mode = BTC_WLINK_2G_AP;
+ break;
+ default:
+ wl_rinfo->link_mode = BTC_WLINK_OTHER;
+ break;
+ }
+ } else {
+ wl_rinfo->link_mode = BTC_WLINK_25G_MCC;
+ }
+ } else if (!b5g && cnt_connect == 2) {
+ if (wl_rinfo->role_map.role.station &&
+ (wl_rinfo->role_map.role.p2p_go ||
+ wl_rinfo->role_map.role.p2p_gc ||
+ wl_rinfo->role_map.role.ap)) {
+ if (wl_2g_ch[0] == wl_2g_ch[1])
+ wl_rinfo->link_mode = BTC_WLINK_2G_SCC;
+ else
+ wl_rinfo->link_mode = BTC_WLINK_2G_MCC;
+ } else {
+ wl_rinfo->link_mode = BTC_WLINK_2G_MCC;
+ }
+ } else if (!b5g && cnt_connect == 1) {
+ if (wl_rinfo->role_map.role.station)
+ wl_rinfo->link_mode = BTC_WLINK_2G_STA;
+ else if (wl_rinfo->role_map.role.ap)
+ wl_rinfo->link_mode = BTC_WLINK_2G_AP;
+ else if (wl_rinfo->role_map.role.p2p_go)
+ wl_rinfo->link_mode = BTC_WLINK_2G_GO;
+ else if (wl_rinfo->role_map.role.p2p_gc)
+ wl_rinfo->link_mode = BTC_WLINK_2G_GC;
+ else
+ wl_rinfo->link_mode = BTC_WLINK_OTHER;
+ }
+
+ /* if no client_joined, don't care P2P-GO/AP role */
+ if (wl_rinfo->role_map.role.p2p_go || wl_rinfo->role_map.role.ap) {
+ if (!client_joined) {
+ if (wl_rinfo->link_mode == BTC_WLINK_2G_SCC ||
+ wl_rinfo->link_mode == BTC_WLINK_2G_MCC) {
+ wl_rinfo->link_mode = BTC_WLINK_2G_STA;
+ wl_rinfo->connect_cnt = 1;
+ } else if (wl_rinfo->link_mode == BTC_WLINK_2G_GO ||
+ wl_rinfo->link_mode == BTC_WLINK_2G_AP) {
+ wl_rinfo->link_mode = BTC_WLINK_NOLINK;
+ wl_rinfo->connect_cnt = 0;
+ }
+ }
+ }
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC] cnt_connect = %d, link_mode = %d\n",
+ cnt_connect, wl_rinfo->link_mode);
+
+ _fw_set_drv_info(rtwdev, CXDRVINFO_ROLE);
+}
+
+#define BTC_CHK_HANG_MAX 3
+#define BTC_SCB_INV_VALUE GENMASK(31, 0)
+
+void rtw89_coex_act1_work(struct work_struct *work)
+{
+ struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
+ coex_act1_work.work);
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_cx *cx = &btc->cx;
+ struct rtw89_btc_wl_info *wl = &cx->wl;
+
+ mutex_lock(&rtwdev->mutex);
+
+ if (wl->status.map._4way)
+ wl->status.map._4way = false;
+ if (wl->status.map.connecting)
+ wl->status.map.connecting = false;
+
+ mutex_unlock(&rtwdev->mutex);
+}
+
+static void _update_bt_scbd(struct rtw89_dev *rtwdev, bool only_update)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_cx *cx = &btc->cx;
+ struct rtw89_btc_bt_info *bt = &btc->cx.bt;
+ u32 val;
+ bool status_change = false;
+
+ if (!chip->scbd)
+ return;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC] %s\n", __func__);
+
+ val = _read_scbd(rtwdev);
+ if (val == BTC_SCB_INV_VALUE) {
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s return by invalid scbd value\n",
+ __func__);
+ return;
+ }
+
+ if (!(val & BTC_BSCB_ON) ||
+ btc->dm.cnt_dm[BTC_DCNT_BTCNT_FREEZE] >= BTC_CHK_HANG_MAX)
+ bt->enable.now = 0;
+ else
+ bt->enable.now = 1;
+
+ if (bt->enable.now != bt->enable.last)
+ status_change = true;
+
+ /* reset bt info if bt re-enable */
+ if (bt->enable.now && !bt->enable.last) {
+ _reset_btc_var(rtwdev, BTC_RESET_BTINFO);
+ cx->cnt_bt[BTC_BCNT_REENABLE]++;
+ bt->enable.now = 1;
+ }
+
+ bt->enable.last = bt->enable.now;
+ bt->scbd = val;
+ bt->mbx_avl = !!(val & BTC_BSCB_ACT);
+
+ if (bt->whql_test != !!(val & BTC_BSCB_WHQL))
+ status_change = true;
+
+ bt->whql_test = !!(val & BTC_BSCB_WHQL);
+ bt->btg_type = val & BTC_BSCB_BT_S1 ? BTC_BT_BTG : BTC_BT_ALONE;
+ bt->link_info.a2dp_desc.active = !!(val & BTC_BSCB_A2DP_ACT);
+
+ /* if rfk run 1->0 */
+ if (bt->rfk_info.map.run && !(val & BTC_BSCB_RFK_RUN))
+ status_change = true;
+
+ bt->rfk_info.map.run = !!(val & BTC_BSCB_RFK_RUN);
+ bt->rfk_info.map.req = !!(val & BTC_BSCB_RFK_REQ);
+ bt->hi_lna_rx = !!(val & BTC_BSCB_BT_HILNA);
+ bt->link_info.status.map.connect = !!(val & BTC_BSCB_BT_CONNECT);
+ bt->run_patch_code = !!(val & BTC_BSCB_PATCH_CODE);
+
+ if (!only_update && status_change)
+ _run_coex(rtwdev, BTC_RSN_UPDATE_BT_SCBD);
+}
+
+static bool _chk_wl_rfk_request(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_cx *cx = &btc->cx;
+ struct rtw89_btc_bt_info *bt = &cx->bt;
+
+ _update_bt_scbd(rtwdev, true);
+
+ cx->cnt_wl[BTC_WCNT_RFK_REQ]++;
+
+ if ((bt->rfk_info.map.run || bt->rfk_info.map.req) &&
+ !bt->rfk_info.map.timeout) {
+ cx->cnt_wl[BTC_WCNT_RFK_REJECT]++;
+ } else {
+ cx->cnt_wl[BTC_WCNT_RFK_GO]++;
+ return true;
+ }
+ return false;
+}
+
+#define _get_wl_nhm_dbm(rtwdev) do {} while (0)
+
+static
+void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_dm *dm = &rtwdev->btc.dm;
+ struct rtw89_btc_cx *cx = &btc->cx;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+ struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info;
+ u8 mode = wl_rinfo->link_mode;
+
+ lockdep_assert_held(&rtwdev->mutex);
+ rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC] %s reason=%d, mode=%d\n",
+ __func__, reason, mode);
+ rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC] %s wl_only=%d, bt_only=%d\n",
+ __func__, dm->wl_only, dm->bt_only);
+
+ dm->run_reason = reason;
+ _update_dm_step(rtwdev, reason);
+ _update_btc_state_map(rtwdev);
+ _get_wl_nhm_dbm(rtwdev);
+
+ /* Be careful to change the following function sequence!! */
+ if (btc->ctrl.manual) {
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): return for Manual CTRL!!\n",
+ __func__);
+ return;
+ }
+
+ if (btc->ctrl.igno_bt &&
+ (reason == BTC_RSN_UPDATE_BT_INFO ||
+ reason == BTC_RSN_UPDATE_BT_SCBD)) {
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): return for Stop Coex DM!!\n",
+ __func__);
+ return;
+ }
+
+ if (!wl->status.map.init_ok) {
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): return for WL init fail!!\n",
+ __func__);
+ return;
+ }
+
+ if (wl->status.map.rf_off_pre == wl->status.map.rf_off &&
+ wl->status.map.lps_pre == wl->status.map.lps &&
+ (reason == BTC_RSN_NTFY_POWEROFF ||
+ reason == BTC_RSN_NTFY_RADIO_STATE)) {
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): return for WL rf off state no change!!\n",
+ __func__);
+ return;
+ }
+
+ dm->cnt_dm[BTC_DCNT_RUN]++;
+
+ if (btc->ctrl.always_freerun) {
+ _action_freerun(rtwdev);
+ btc->ctrl.igno_bt = true;
+ goto exit;
+ }
+
+ if (dm->wl_only) {
+ _action_wl_only(rtwdev);
+ btc->ctrl.igno_bt = true;
+ goto exit;
+ }
+
+ if (wl->status.map.rf_off || wl->status.map.lps || dm->bt_only) {
+ _action_wl_off(rtwdev);
+ btc->ctrl.igno_bt = true;
+ goto exit;
+ }
+
+ btc->ctrl.igno_bt = false;
+ dm->freerun = false;
+
+ if (reason == BTC_RSN_NTFY_INIT) {
+ _action_wl_init(rtwdev);
+ goto exit;
+ }
+
+ if (!cx->bt.enable.now && !cx->other.type) {
+ _action_bt_off(rtwdev);
+ goto exit;
+ }
+
+ if (cx->bt.whql_test) {
+ _action_bt_whql(rtwdev);
+ goto exit;
+ }
+
+ if (wl->rfk_info.state != BTC_WRFK_STOP) {
+ _action_wl_rfk(rtwdev);
+ goto exit;
+ }
+
+ if (wl->status.map.scan || cx->state_map == BTC_WLINKING) {
+ _action_wl_scan(rtwdev);
+ goto exit;
+ }
+
+ switch (mode) {
+ case BTC_WLINK_NOLINK:
+ _action_wl_nc(rtwdev);
+ break;
+ case BTC_WLINK_2G_STA:
+ _action_wl_2g_sta(rtwdev);
+ break;
+ case BTC_WLINK_2G_AP:
+ _action_wl_2g_ap(rtwdev);
+ break;
+ case BTC_WLINK_2G_GO:
+ _action_wl_2g_go(rtwdev);
+ break;
+ case BTC_WLINK_2G_GC:
+ _action_wl_2g_gc(rtwdev);
+ break;
+ case BTC_WLINK_2G_SCC:
+ _action_wl_2g_scc(rtwdev);
+ break;
+ case BTC_WLINK_2G_MCC:
+ _action_wl_2g_mcc(rtwdev);
+ break;
+ case BTC_WLINK_25G_MCC:
+ _action_wl_25g_mcc(rtwdev);
+ break;
+ case BTC_WLINK_5G:
+ _action_wl_5g(rtwdev);
+ break;
+ case BTC_WLINK_2G_NAN:
+ _action_wl_2g_nan(rtwdev);
+ break;
+ default:
+ _action_wl_other(rtwdev);
+ break;
+ }
+
+exit:
+ rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC] %s exit\n", __func__);
+ _action_common(rtwdev);
+}
+
+void rtw89_btc_ntfy_poweron(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s !!\n", __func__);
+ btc->dm.cnt_notify[BTC_NCNT_POWER_ON]++;
+}
+
+void rtw89_btc_ntfy_poweroff(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s !!\n", __func__);
+ btc->dm.cnt_notify[BTC_NCNT_POWER_OFF]++;
+
+ btc->cx.wl.status.map.rf_off = 1;
+
+ _write_scbd(rtwdev, BTC_WSCB_ALL, false);
+ _run_coex(rtwdev, BTC_RSN_NTFY_POWEROFF);
+
+ rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_ALL, 0);
+
+ btc->cx.wl.status.map.rf_off_pre = btc->cx.wl.status.map.rf_off;
+}
+
+static void _set_init_info(struct rtw89_dev *rtwdev)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_dm *dm = &btc->dm;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+
+ dm->init_info.wl_only = (u8)dm->wl_only;
+ dm->init_info.bt_only = (u8)dm->bt_only;
+ dm->init_info.wl_init_ok = (u8)wl->status.map.init_ok;
+ dm->init_info.dbcc_en = rtwdev->dbcc_en;
+ dm->init_info.cx_other = btc->cx.other.type;
+ dm->init_info.wl_guard_ch = chip->afh_guard_ch;
+ dm->init_info.module = btc->mdinfo;
+}
+
+void rtw89_btc_ntfy_init(struct rtw89_dev *rtwdev, u8 mode)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_dm *dm = &rtwdev->btc.dm;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+
+ _reset_btc_var(rtwdev, BTC_RESET_ALL);
+ btc->dm.run_reason = BTC_RSN_NONE;
+ btc->dm.run_action = BTC_ACT_NONE;
+ btc->ctrl.igno_bt = true;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): mode=%d\n", __func__, mode);
+
+ dm->cnt_notify[BTC_NCNT_INIT_COEX]++;
+ dm->wl_only = mode == BTC_MODE_WL ? 1 : 0;
+ dm->bt_only = mode == BTC_MODE_BT ? 1 : 0;
+ wl->status.map.rf_off = mode == BTC_MODE_WLOFF ? 1 : 0;
+
+ chip->ops->btc_set_rfe(rtwdev);
+ chip->ops->btc_init_cfg(rtwdev);
+
+ if (!wl->status.map.init_ok) {
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): return for WL init fail!!\n",
+ __func__);
+ dm->error.map.init = true;
+ return;
+ }
+
+ _write_scbd(rtwdev,
+ BTC_WSCB_ACTIVE | BTC_WSCB_ON | BTC_WSCB_BTLOG, true);
+ _update_bt_scbd(rtwdev, true);
+ if (rtw89_mac_get_ctrl_path(rtwdev)) {
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): PTA owner warning!!\n",
+ __func__);
+ dm->error.map.pta_owner = true;
+ }
+
+ _set_init_info(rtwdev);
+ _set_wl_tx_power(rtwdev, RTW89_BTC_WL_DEF_TX_PWR);
+ rtw89_btc_fw_set_slots(rtwdev, CXST_MAX, dm->slot);
+ btc_fw_set_monreg(rtwdev);
+ _fw_set_drv_info(rtwdev, CXDRVINFO_INIT);
+ _fw_set_drv_info(rtwdev, CXDRVINFO_CTRL);
+
+ _run_coex(rtwdev, BTC_RSN_NTFY_INIT);
+}
+
+void rtw89_btc_ntfy_scan_start(struct rtw89_dev *rtwdev, u8 phy_idx, u8 band)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): phy_idx=%d, band=%d\n",
+ __func__, phy_idx, band);
+ btc->dm.cnt_notify[BTC_NCNT_SCAN_START]++;
+ wl->status.map.scan = true;
+ wl->scan_info.band[phy_idx] = band;
+ wl->scan_info.phy_map |= BIT(phy_idx);
+ _fw_set_drv_info(rtwdev, CXDRVINFO_SCAN);
+
+ if (rtwdev->dbcc_en) {
+ wl->dbcc_info.scan_band[phy_idx] = band;
+ _update_dbcc_band(rtwdev, phy_idx);
+ _fw_set_drv_info(rtwdev, CXDRVINFO_DBCC);
+ }
+
+ _run_coex(rtwdev, BTC_RSN_NTFY_SCAN_START);
+}
+
+void rtw89_btc_ntfy_scan_finish(struct rtw89_dev *rtwdev, u8 phy_idx)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): phy_idx=%d\n", __func__, phy_idx);
+ btc->dm.cnt_notify[BTC_NCNT_SCAN_FINISH]++;
+
+ wl->status.map.scan = false;
+ wl->scan_info.phy_map &= ~BIT(phy_idx);
+ _fw_set_drv_info(rtwdev, CXDRVINFO_SCAN);
+
+ if (rtwdev->dbcc_en) {
+ _update_dbcc_band(rtwdev, phy_idx);
+ _fw_set_drv_info(rtwdev, CXDRVINFO_DBCC);
+ }
+
+ _run_coex(rtwdev, BTC_RSN_NTFY_SCAN_FINISH);
+}
+
+void rtw89_btc_ntfy_switch_band(struct rtw89_dev *rtwdev, u8 phy_idx, u8 band)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): phy_idx=%d, band=%d\n",
+ __func__, phy_idx, band);
+ btc->dm.cnt_notify[BTC_NCNT_SWITCH_BAND]++;
+
+ wl->scan_info.band[phy_idx] = band;
+ wl->scan_info.phy_map |= BIT(phy_idx);
+ _fw_set_drv_info(rtwdev, CXDRVINFO_SCAN);
+
+ if (rtwdev->dbcc_en) {
+ wl->dbcc_info.scan_band[phy_idx] = band;
+ _update_dbcc_band(rtwdev, phy_idx);
+ _fw_set_drv_info(rtwdev, CXDRVINFO_DBCC);
+ }
+ _run_coex(rtwdev, BTC_RSN_NTFY_SWBAND);
+}
+
+void rtw89_btc_ntfy_specific_packet(struct rtw89_dev *rtwdev,
+ enum btc_pkt_type pkt_type)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_cx *cx = &btc->cx;
+ struct rtw89_btc_wl_info *wl = &cx->wl;
+ struct rtw89_btc_bt_link_info *b = &cx->bt.link_info;
+ struct rtw89_btc_bt_hfp_desc *hfp = &b->hfp_desc;
+ struct rtw89_btc_bt_hid_desc *hid = &b->hid_desc;
+ u32 cnt;
+ u32 delay = RTW89_COEX_ACT1_WORK_PERIOD;
+ bool delay_work = false;
+
+ switch (pkt_type) {
+ case PACKET_DHCP:
+ cnt = ++cx->cnt_wl[BTC_WCNT_DHCP];
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): DHCP cnt=%d\n", __func__, cnt);
+ wl->status.map.connecting = true;
+ delay_work = true;
+ break;
+ case PACKET_EAPOL:
+ cnt = ++cx->cnt_wl[BTC_WCNT_EAPOL];
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): EAPOL cnt=%d\n", __func__, cnt);
+ wl->status.map._4way = true;
+ delay_work = true;
+ if (hfp->exist || hid->exist)
+ delay /= 2;
+ break;
+ case PACKET_EAPOL_END:
+ cnt = ++cx->cnt_wl[BTC_WCNT_EAPOL];
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): EAPOL_End cnt=%d\n",
+ __func__, cnt);
+ wl->status.map._4way = false;
+ cancel_delayed_work(&rtwdev->coex_act1_work);
+ break;
+ case PACKET_ARP:
+ cnt = ++cx->cnt_wl[BTC_WCNT_ARP];
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): ARP cnt=%d\n", __func__, cnt);
+ return;
+ default:
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s(): unknown packet type %d\n",
+ __func__, pkt_type);
+ return;
+ }
+
+ if (delay_work) {
+ cancel_delayed_work(&rtwdev->coex_act1_work);
+ ieee80211_queue_delayed_work(rtwdev->hw,
+ &rtwdev->coex_act1_work, delay);
+ }
+
+ btc->dm.cnt_notify[BTC_NCNT_SPECIAL_PACKET]++;
+ _run_coex(rtwdev, BTC_RSN_NTFY_SPECIFIC_PACKET);
+}
+
+void rtw89_btc_ntfy_eapol_packet_work(struct work_struct *work)
+{
+ struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
+ btc.eapol_notify_work);
+
+ mutex_lock(&rtwdev->mutex);
+ rtw89_leave_ps_mode(rtwdev);
+ rtw89_btc_ntfy_specific_packet(rtwdev, PACKET_EAPOL);
+ mutex_unlock(&rtwdev->mutex);
+}
+
+void rtw89_btc_ntfy_arp_packet_work(struct work_struct *work)
+{
+ struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
+ btc.arp_notify_work);
+
+ mutex_lock(&rtwdev->mutex);
+ rtw89_leave_ps_mode(rtwdev);
+ rtw89_btc_ntfy_specific_packet(rtwdev, PACKET_ARP);
+ mutex_unlock(&rtwdev->mutex);
+}
+
+void rtw89_btc_ntfy_dhcp_packet_work(struct work_struct *work)
+{
+ struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
+ btc.dhcp_notify_work);
+
+ mutex_lock(&rtwdev->mutex);
+ rtw89_leave_ps_mode(rtwdev);
+ rtw89_btc_ntfy_specific_packet(rtwdev, PACKET_DHCP);
+ mutex_unlock(&rtwdev->mutex);
+}
+
+static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_cx *cx = &btc->cx;
+ struct rtw89_btc_bt_info *bt = &cx->bt;
+ struct rtw89_btc_bt_link_info *b = &bt->link_info;
+ struct rtw89_btc_bt_hfp_desc *hfp = &b->hfp_desc;
+ struct rtw89_btc_bt_hid_desc *hid = &b->hid_desc;
+ struct rtw89_btc_bt_a2dp_desc *a2dp = &b->a2dp_desc;
+ struct rtw89_btc_bt_pan_desc *pan = &b->pan_desc;
+ union btc_btinfo btinfo;
+
+ if (buf[BTC_BTINFO_L1] != 6)
+ return;
+
+ if (!memcmp(bt->raw_info, buf, BTC_BTINFO_MAX)) {
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s return by bt-info duplicate!!\n",
+ __func__);
+ cx->cnt_bt[BTC_BCNT_INFOSAME]++;
+ return;
+ }
+
+ memcpy(bt->raw_info, buf, BTC_BTINFO_MAX);
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s: bt_info[2]=0x%02x\n",
+ __func__, bt->raw_info[2]);
+
+ /* reset to mo-connect before update */
+ b->status.val = BTC_BLINK_NOCONNECT;
+ b->profile_cnt.last = b->profile_cnt.now;
+ b->relink.last = b->relink.now;
+ a2dp->exist_last = a2dp->exist;
+ b->multi_link.last = b->multi_link.now;
+ bt->inq_pag.last = bt->inq_pag.now;
+ b->profile_cnt.now = 0;
+ hid->type = 0;
+
+ /* ======= parse raw info low-Byte2 ======= */
+ btinfo.val = bt->raw_info[BTC_BTINFO_L2];
+ b->status.map.connect = btinfo.lb2.connect;
+ b->status.map.sco_busy = btinfo.lb2.sco_busy;
+ b->status.map.acl_busy = btinfo.lb2.acl_busy;
+ b->status.map.inq_pag = btinfo.lb2.inq_pag;
+ bt->inq_pag.now = btinfo.lb2.inq_pag;
+ cx->cnt_bt[BTC_BCNT_INQPAG] += !!(bt->inq_pag.now && !bt->inq_pag.last);
+
+ hfp->exist = btinfo.lb2.hfp;
+ b->profile_cnt.now += (u8)hfp->exist;
+ hid->exist = btinfo.lb2.hid;
+ b->profile_cnt.now += (u8)hid->exist;
+ a2dp->exist = btinfo.lb2.a2dp;
+ b->profile_cnt.now += (u8)a2dp->exist;
+ pan->exist = btinfo.lb2.pan;
+ b->profile_cnt.now += (u8)pan->exist;
+
+ /* ======= parse raw info low-Byte3 ======= */
+ btinfo.val = bt->raw_info[BTC_BTINFO_L3];
+ if (btinfo.lb3.retry != 0)
+ cx->cnt_bt[BTC_BCNT_RETRY]++;
+ b->cqddr = btinfo.lb3.cqddr;
+ cx->cnt_bt[BTC_BCNT_INQ] += !!(btinfo.lb3.inq && !bt->inq);
+ bt->inq = btinfo.lb3.inq;
+ cx->cnt_bt[BTC_BCNT_PAGE] += !!(btinfo.lb3.pag && !bt->pag);
+ bt->pag = btinfo.lb3.pag;
+
+ b->status.map.mesh_busy = btinfo.lb3.mesh_busy;
+ /* ======= parse raw info high-Byte0 ======= */
+ btinfo.val = bt->raw_info[BTC_BTINFO_H0];
+ /* raw val is dBm unit, translate from -100~ 0dBm to 0~100%*/
+ b->rssi = chip->ops->btc_get_bt_rssi(rtwdev, btinfo.hb0.rssi);
+
+ /* ======= parse raw info high-Byte1 ======= */
+ btinfo.val = bt->raw_info[BTC_BTINFO_H1];
+ b->status.map.ble_connect = btinfo.hb1.ble_connect;
+ if (btinfo.hb1.ble_connect)
+ hid->type |= (hid->exist ? BTC_HID_BLE : BTC_HID_RCU);
+
+ cx->cnt_bt[BTC_BCNT_REINIT] += !!(btinfo.hb1.reinit && !bt->reinit);
+ bt->reinit = btinfo.hb1.reinit;
+ cx->cnt_bt[BTC_BCNT_RELINK] += !!(btinfo.hb1.relink && !b->relink.now);
+ b->relink.now = btinfo.hb1.relink;
+ cx->cnt_bt[BTC_BCNT_IGNOWL] += !!(btinfo.hb1.igno_wl && !bt->igno_wl);
+ bt->igno_wl = btinfo.hb1.igno_wl;
+
+ if (bt->igno_wl && !cx->wl.status.map.rf_off)
+ _set_bt_ignore_wlan_act(rtwdev, false);
+
+ hid->type |= (btinfo.hb1.voice ? BTC_HID_RCU_VOICE : 0);
+ bt->ble_scan_en = btinfo.hb1.ble_scan;
+
+ cx->cnt_bt[BTC_BCNT_ROLESW] += !!(btinfo.hb1.role_sw && !b->role_sw);
+ b->role_sw = btinfo.hb1.role_sw;
+
+ b->multi_link.now = btinfo.hb1.multi_link;
+
+ /* ======= parse raw info high-Byte2 ======= */
+ btinfo.val = bt->raw_info[BTC_BTINFO_H2];
+ pan->active = !!btinfo.hb2.pan_active;
+
+ cx->cnt_bt[BTC_BCNT_AFH] += !!(btinfo.hb2.afh_update && !b->afh_update);
+ b->afh_update = btinfo.hb2.afh_update;
+ a2dp->active = btinfo.hb2.a2dp_active;
+ b->slave_role = btinfo.hb2.slave;
+ hid->slot_info = btinfo.hb2.hid_slot;
+ hid->pair_cnt = btinfo.hb2.hid_cnt;
+ hid->type |= (hid->slot_info == BTC_HID_218 ?
+ BTC_HID_218 : BTC_HID_418);
+ /* ======= parse raw info high-Byte3 ======= */
+ btinfo.val = bt->raw_info[BTC_BTINFO_H3];
+ a2dp->bitpool = btinfo.hb3.a2dp_bitpool;
+
+ if (b->tx_3m != (u32)btinfo.hb3.tx_3m)
+ cx->cnt_bt[BTC_BCNT_RATECHG]++;
+ b->tx_3m = (u32)btinfo.hb3.tx_3m;
+
+ a2dp->sink = btinfo.hb3.a2dp_sink;
+
+ if (b->profile_cnt.now || b->status.map.ble_connect)
+ rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_AFH_MAP, 1);
+ else
+ rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_AFH_MAP, 0);
+
+ if (!a2dp->exist_last && a2dp->exist) {
+ a2dp->vendor_id = 0;
+ a2dp->flush_time = 0;
+ a2dp->play_latency = 1;
+ }
+
+ if (a2dp->exist && (a2dp->flush_time == 0 || a2dp->vendor_id == 0 ||
+ a2dp->play_latency == 1))
+ rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_DEVICE_INFO, 1);
+ else
+ rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_DEVICE_INFO, 0);
+
+ _run_coex(rtwdev, BTC_RSN_UPDATE_BT_INFO);
+}
+
+enum btc_wl_mode {
+ BTC_WL_MODE_HT = 0,
+ BTC_WL_MODE_VHT = 1,
+ BTC_WL_MODE_HE = 2,
+ BTC_WL_MODE_NUM,
+};
+
+void rtw89_btc_ntfy_role_info(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,
+ struct rtw89_sta *rtwsta, enum btc_role_state state)
+{
+ struct rtw89_hal *hal = &rtwdev->hal;
+ struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif);
+ struct ieee80211_sta *sta = rtwsta_to_sta(rtwsta);
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+ struct rtw89_btc_wl_link_info r = {0};
+ struct rtw89_btc_wl_link_info *wlinfo = NULL;
+ u8 mode = 0;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC] state=%d\n", state);
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC] role is STA=%d\n",
+ vif->type == NL80211_IFTYPE_STATION);
+ rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC] port=%d\n", rtwvif->port);
+ rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC] band=%d ch=%d bw=%d\n",
+ hal->current_band_type, hal->current_channel,
+ hal->current_band_width);
+ rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC] associated=%d\n",
+ state == BTC_ROLE_MSTS_STA_CONN_END);
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC] bcn_period=%d dtim_period=%d\n",
+ vif->bss_conf.beacon_int, vif->bss_conf.dtim_period);
+
+ if (rtwsta) {
+ rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC] STA mac_id=%d\n",
+ rtwsta->mac_id);
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC] STA support HE=%d VHT=%d HT=%d\n",
+ sta->he_cap.has_he,
+ sta->vht_cap.vht_supported,
+ sta->ht_cap.ht_supported);
+ if (sta->he_cap.has_he)
+ mode |= BIT(BTC_WL_MODE_HE);
+ if (sta->vht_cap.vht_supported)
+ mode |= BIT(BTC_WL_MODE_VHT);
+ if (sta->ht_cap.ht_supported)
+ mode |= BIT(BTC_WL_MODE_HT);
+
+ r.mode = mode;
+ }
+
+ if (rtwvif->wifi_role >= RTW89_WIFI_ROLE_MLME_MAX)
+ return;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC] wifi_role=%d\n", rtwvif->wifi_role);
+
+ r.role = rtwvif->wifi_role;
+ r.phy = rtwvif->phy_idx;
+ r.pid = rtwvif->port;
+ r.active = true;
+ r.connected = MLME_LINKED;
+ r.bcn_period = vif->bss_conf.beacon_int;
+ r.dtim_period = vif->bss_conf.dtim_period;
+ r.band = hal->current_band_type;
+ r.ch = hal->current_channel;
+ r.bw = hal->current_band_width;
+ ether_addr_copy(r.mac_addr, rtwvif->mac_addr);
+
+ if (rtwsta && vif->type == NL80211_IFTYPE_STATION)
+ r.mac_id = rtwsta->mac_id;
+
+ btc->dm.cnt_notify[BTC_NCNT_ROLE_INFO]++;
+
+ wlinfo = &wl->link_info[r.pid];
+
+ memcpy(wlinfo, &r, sizeof(*wlinfo));
+ _update_wl_info(rtwdev);
+
+ if (wlinfo->role == RTW89_WIFI_ROLE_STATION &&
+ wlinfo->connected == MLME_NO_LINK)
+ btc->dm.leak_ap = 0;
+
+ if (state == BTC_ROLE_MSTS_STA_CONN_START)
+ wl->status.map.connecting = 1;
+ else
+ wl->status.map.connecting = 0;
+
+ if (state == BTC_ROLE_MSTS_STA_DIS_CONN)
+ wl->status.map._4way = false;
+
+ _run_coex(rtwdev, BTC_RSN_NTFY_ROLE_INFO);
+}
+
+void rtw89_btc_ntfy_radio_state(struct rtw89_dev *rtwdev, enum btc_rfctrl rf_state)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC] %s: rf_state = %d\n",
+ __func__, rf_state);
+ btc->dm.cnt_notify[BTC_NCNT_RADIO_STATE]++;
+
+ switch (rf_state) {
+ case BTC_RFCTRL_WL_OFF:
+ wl->status.map.rf_off = 1;
+ wl->status.map.lps = 0;
+ break;
+ case BTC_RFCTRL_FW_CTRL:
+ wl->status.map.rf_off = 0;
+ wl->status.map.lps = 1;
+ break;
+ case BTC_RFCTRL_WL_ON:
+ default:
+ wl->status.map.rf_off = 0;
+ wl->status.map.lps = 0;
+ break;
+ }
+
+ if (rf_state == BTC_RFCTRL_WL_ON) {
+ rtw89_btc_fw_en_rpt(rtwdev,
+ RPT_EN_MREG | RPT_EN_BT_VER_INFO, true);
+ _write_scbd(rtwdev, BTC_WSCB_ACTIVE, true);
+ _update_bt_scbd(rtwdev, true);
+ chip->ops->btc_init_cfg(rtwdev);
+ } else {
+ rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_ALL, false);
+ _write_scbd(rtwdev, BTC_WSCB_ACTIVE | BTC_WSCB_WLBUSY, false);
+ }
+
+ _run_coex(rtwdev, BTC_RSN_NTFY_RADIO_STATE);
+
+ wl->status.map.rf_off_pre = wl->status.map.rf_off;
+ wl->status.map.lps_pre = wl->status.map.lps;
+}
+
+static bool _ntfy_wl_rfk(struct rtw89_dev *rtwdev, u8 phy_path,
+ enum btc_wl_rfk_type type,
+ enum btc_wl_rfk_state state)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_cx *cx = &btc->cx;
+ struct rtw89_btc_wl_info *wl = &cx->wl;
+ bool result = BTC_WRFK_REJECT;
+
+ wl->rfk_info.type = type;
+ wl->rfk_info.path_map = FIELD_GET(BTC_RFK_PATH_MAP, phy_path);
+ wl->rfk_info.phy_map = FIELD_GET(BTC_RFK_PHY_MAP, phy_path);
+ wl->rfk_info.band = FIELD_GET(BTC_RFK_BAND_MAP, phy_path);
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s()_start: phy=0x%x, path=0x%x, type=%d, state=%d\n",
+ __func__, wl->rfk_info.phy_map, wl->rfk_info.path_map,
+ type, state);
+
+ switch (state) {
+ case BTC_WRFK_START:
+ result = _chk_wl_rfk_request(rtwdev);
+ wl->rfk_info.state = result ? BTC_WRFK_START : BTC_WRFK_STOP;
+
+ _write_scbd(rtwdev, BTC_WSCB_WLRFK, result);
+
+ btc->dm.cnt_notify[BTC_NCNT_WL_RFK]++;
+ break;
+ case BTC_WRFK_ONESHOT_START:
+ case BTC_WRFK_ONESHOT_STOP:
+ if (wl->rfk_info.state == BTC_WRFK_STOP) {
+ result = BTC_WRFK_REJECT;
+ } else {
+ result = BTC_WRFK_ALLOW;
+ wl->rfk_info.state = state;
+ }
+ break;
+ case BTC_WRFK_STOP:
+ result = BTC_WRFK_ALLOW;
+ wl->rfk_info.state = BTC_WRFK_STOP;
+
+ _write_scbd(rtwdev, BTC_WSCB_WLRFK, false);
+ break;
+ default:
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s() warning state=%d\n", __func__, state);
+ break;
+ }
+
+ if (result == BTC_WRFK_ALLOW) {
+ if (wl->rfk_info.state == BTC_WRFK_START ||
+ wl->rfk_info.state == BTC_WRFK_STOP)
+ _run_coex(rtwdev, BTC_RSN_NTFY_WL_RFK);
+ }
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s()_finish: rfk_cnt=%d, result=%d\n",
+ __func__, btc->dm.cnt_notify[BTC_NCNT_WL_RFK], result);
+
+ return result == BTC_WRFK_ALLOW;
+}
+
+void rtw89_btc_ntfy_wl_rfk(struct rtw89_dev *rtwdev, u8 phy_map,
+ enum btc_wl_rfk_type type,
+ enum btc_wl_rfk_state state)
+{
+ u8 band;
+ bool allow;
+ int ret;
+
+ band = FIELD_GET(BTC_RFK_BAND_MAP, phy_map);
+
+ rtw89_debug(rtwdev, RTW89_DBG_RFK,
+ "[RFK] RFK notify (%s / PHY%u / K_type = %u / path_idx = %lu / process = %s)\n",
+ band == RTW89_BAND_2G ? "2G" :
+ band == RTW89_BAND_5G ? "5G" : "6G",
+ !!(FIELD_GET(BTC_RFK_PHY_MAP, phy_map) & BIT(RTW89_PHY_1)),
+ type,
+ FIELD_GET(BTC_RFK_PATH_MAP, phy_map),
+ state == BTC_WRFK_STOP ? "RFK_STOP" :
+ state == BTC_WRFK_START ? "RFK_START" :
+ state == BTC_WRFK_ONESHOT_START ? "ONE-SHOT_START" :
+ "ONE-SHOT_STOP");
+
+ if (state != BTC_WRFK_START || rtwdev->is_bt_iqk_timeout) {
+ _ntfy_wl_rfk(rtwdev, phy_map, type, state);
+ return;
+ }
+
+ ret = read_poll_timeout(_ntfy_wl_rfk, allow, allow, 40, 100000, false,
+ rtwdev, phy_map, type, state);
+ if (ret) {
+ rtw89_warn(rtwdev, "RFK notify timeout\n");
+ rtwdev->is_bt_iqk_timeout = true;
+ }
+}
+
+struct rtw89_btc_wl_sta_iter_data {
+ struct rtw89_dev *rtwdev;
+ u8 busy_all;
+ u8 dir_all;
+ u8 rssi_map_all;
+ bool is_sta_change;
+ bool is_traffic_change;
+};
+
+static void rtw89_btc_ntfy_wl_sta_iter(void *data, struct ieee80211_sta *sta)
+{
+ struct rtw89_btc_wl_sta_iter_data *iter_data =
+ (struct rtw89_btc_wl_sta_iter_data *)data;
+ struct rtw89_dev *rtwdev = iter_data->rtwdev;
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+ struct rtw89_btc_wl_link_info *link_info = NULL;
+ struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv;
+ struct rtw89_traffic_stats *link_info_t = NULL;
+ struct rtw89_vif *rtwvif = rtwsta->rtwvif;
+ struct rtw89_traffic_stats *stats = &rtwvif->stats;
+ u32 last_tx_rate, last_rx_rate;
+ u16 last_tx_lvl, last_rx_lvl;
+ u8 port = rtwvif->port;
+ u8 rssi;
+ u8 busy = 0;
+ u8 dir = 0;
+ u8 rssi_map = 0;
+ bool is_sta_change = false, is_traffic_change = false;
+
+ rssi = ewma_rssi_read(&rtwsta->avg_rssi) >> RSSI_FACTOR;
+ rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC] rssi=%d\n", rssi);
+
+ link_info = &wl->link_info[port];
+ link_info->stat.traffic = rtwvif->stats;
+ link_info_t = &link_info->stat.traffic;
+
+ if (link_info->connected == MLME_NO_LINK) {
+ link_info->rx_rate_drop_cnt = 0;
+ iter_data->rssi_map_all |= rssi_map;
+ }
+
+ last_tx_rate = link_info_t->tx_rate;
+ last_rx_rate = link_info_t->rx_rate;
+ last_tx_lvl = (u16)link_info_t->tx_tfc_lv;
+ last_rx_lvl = (u16)link_info_t->rx_tfc_lv;
+
+ if (stats->tx_tfc_lv == RTW89_TFC_IDLE &&
+ stats->rx_tfc_lv == RTW89_TFC_IDLE)
+ return;
+
+ if (stats->tx_tfc_lv > stats->rx_tfc_lv)
+ dir = RTW89_TFC_UL;
+ else
+ dir = RTW89_TFC_DL;
+ busy = 1;
+
+ link_info = &wl->link_info[port];
+ if (link_info->busy != busy || link_info->dir != dir) {
+ is_sta_change = true;
+ link_info->busy = busy;
+ link_info->dir = dir;
+ }
+
+ iter_data->busy_all |= busy;
+ iter_data->dir_all |= BIT(dir);
+
+ if (rtwsta->rx_hw_rate <= RTW89_HW_RATE_CCK2 &&
+ last_rx_rate > RTW89_HW_RATE_CCK2 &&
+ link_info_t->rx_tfc_lv > RTW89_TFC_IDLE)
+ link_info->rx_rate_drop_cnt++;
+
+ if (last_tx_rate != rtwsta->ra_report.hw_rate ||
+ last_rx_rate != rtwsta->rx_hw_rate ||
+ last_tx_lvl != link_info_t->tx_tfc_lv ||
+ last_rx_lvl != link_info_t->rx_tfc_lv)
+ is_traffic_change = true;
+
+ link_info_t->tx_rate = rtwsta->ra_report.hw_rate;
+ link_info_t->rx_rate = rtwsta->rx_hw_rate;
+
+ wl->role_info.active_role[port].tx_lvl = (u16)stats->tx_tfc_lv;
+ wl->role_info.active_role[port].rx_lvl = (u16)stats->rx_tfc_lv;
+ wl->role_info.active_role[port].tx_rate = rtwsta->ra_report.hw_rate;
+ wl->role_info.active_role[port].rx_rate = rtwsta->rx_hw_rate;
+
+ if (is_sta_change)
+ iter_data->is_sta_change = true;
+
+ if (is_traffic_change)
+ iter_data->is_traffic_change = true;
+}
+
+#define BTC_NHM_CHK_INTVL 20
+
+static void _ntfy_wl_sta(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+ struct rtw89_btc_wl_sta_iter_data data = {.rtwdev = rtwdev};
+ u8 i;
+
+ ieee80211_iterate_stations_atomic(rtwdev->hw,
+ rtw89_btc_ntfy_wl_sta_iter,
+ &data);
+
+ wl->rssi_level = 0;
+ btc->dm.cnt_notify[BTC_NCNT_WL_STA]++;
+ for (i = BTC_WL_RSSI_THMAX; i > 0; i--) {
+ /* set RSSI level 4 ~ 0 if rssi bit map match */
+ if (data.rssi_map_all & BIT(i - 1)) {
+ wl->rssi_level = i;
+ break;
+ }
+ }
+
+ _write_scbd(rtwdev, BTC_WSCB_WLBUSY, (!!wl->status.map.busy));
+
+ if (data.is_traffic_change)
+ _fw_set_drv_info(rtwdev, CXDRVINFO_ROLE);
+ if (data.is_sta_change) {
+ wl->status.map.busy = data.busy_all;
+ wl->status.map.traffic_dir = data.dir_all;
+ _run_coex(rtwdev, BTC_RSN_NTFY_WL_STA);
+ } else if (btc->dm.cnt_notify[BTC_NCNT_WL_STA] >=
+ btc->dm.cnt_dm[BTC_DCNT_WL_STA_LAST] + BTC_NHM_CHK_INTVL) {
+ _get_wl_nhm_dbm(rtwdev);
+ btc->dm.cnt_dm[BTC_DCNT_WL_STA_LAST] =
+ btc->dm.cnt_notify[BTC_NCNT_WL_STA];
+ } else if (btc->dm.cnt_notify[BTC_NCNT_WL_STA] <
+ btc->dm.cnt_dm[BTC_DCNT_WL_STA_LAST]) {
+ btc->dm.cnt_dm[BTC_DCNT_WL_STA_LAST] =
+ btc->dm.cnt_notify[BTC_NCNT_WL_STA];
+ }
+}
+
+void rtw89_btc_ntfy_wl_sta_work(struct work_struct *work)
+{
+ struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
+ btc.wl_sta_notify_work);
+
+ mutex_lock(&rtwdev->mutex);
+ rtw89_leave_ps_mode(rtwdev);
+ _ntfy_wl_sta(rtwdev);
+ mutex_unlock(&rtwdev->mutex);
+}
+
+#define _update_bt_psd(rtwdev, buf, len) do {} while (0)
+#define _update_offload_runinfo(rtwdev, buf, len) do {} while (0)
+
+void rtw89_btc_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb,
+ u32 len, u8 class, u8 func)
+{
+ /* The below is just sample code. Don't use magic number in your release
+ * version.
+ */
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo;
+
+ /* note: 'len' includes header, so 'buf' length is 'len - 8' */
+ u8 *buf = &skb->data[8]; /* size of C2H header is 8 */
+
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s C2H BT len:%d class:%d fun:%d\n",
+ __func__, len, class, func);
+
+ if (class != 0x12)
+ return;
+
+ switch (func) {
+ case BTF_EVNT_RPT:
+ case BTF_EVNT_BUF_OVERFLOW:
+ pfwinfo->event[func]++;
+ /* Don't need rtw89_leave_ps_mode() */
+ btc_fw_event(rtwdev, func, buf, len);
+ break;
+ case BTF_EVNT_BT_INFO:
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], handle C2H BT INFO with data %8ph\n", buf);
+ btc->cx.cnt_bt[BTC_BCNT_INFOUPDATE]++;
+ rtw89_leave_ps_mode(rtwdev);
+ _update_bt_info(rtwdev, buf, len);
+ break;
+ case BTF_EVNT_BT_SCBD:
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], handle C2H BT SCBD with data %8ph\n", buf);
+ btc->cx.cnt_bt[BTC_BCNT_SCBDUPDATE]++;
+ rtw89_leave_ps_mode(rtwdev);
+ _update_bt_scbd(rtwdev, false);
+ break;
+ case BTF_EVNT_BT_PSD:
+ _update_bt_psd(rtwdev, buf, len);
+ break;
+ case BTF_EVNT_BT_REG:
+ btc->dbg.rb_done = true;
+ btc->dbg.rb_val = ((buf[3] << 24) | (buf[2] << 16) |
+ (buf[1] << 8) | (buf[0]));
+ break;
+ case BTF_EVNT_C2H_LOOPBACK:
+ btc->dbg.rb_done = true;
+ btc->dbg.rb_val = buf[0];
+ break;
+ case BTF_EVNT_CX_RUNINFO:
+ btc->dm.cnt_dm[BTC_DCNT_CX_RUNINFO]++;
+ _update_offload_runinfo(rtwdev, buf, len);
+ break; }
+}
+
+#define BTC_CX_FW_OFFLOAD 0
+
+static void _show_cx_info(struct rtw89_dev *rtwdev, struct seq_file *m)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_dm *dm = &btc->dm;
+ struct rtw89_btc_bt_info *bt = &btc->cx.bt;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+ u32 ver_main = 0, ver_sub = 0, ver_hotfix = 0, id_branch = 0;
+
+ if (!(dm->coex_info_map & BTC_COEX_INFO_CX))
+ return;
+
+ dm->cnt_notify[BTC_NCNT_SHOW_COEX_INFO]++;
+
+ seq_printf(m, "========== [BTC COEX INFO (%d)] ==========\n",
+ chip->chip_id);
+
+ ver_main = FIELD_GET(GENMASK(31, 24), chip->para_ver);
+ ver_sub = FIELD_GET(GENMASK(23, 16), chip->para_ver);
+ ver_hotfix = FIELD_GET(GENMASK(15, 8), chip->para_ver);
+ id_branch = FIELD_GET(GENMASK(7, 0), chip->para_ver);
+ seq_printf(m, " %-15s : Coex:%d.%d.%d(branch:%d), ",
+ "[coex_version]", ver_main, ver_sub, ver_hotfix, id_branch);
+
+ if (dm->wl_fw_cx_offload != BTC_CX_FW_OFFLOAD)
+ dm->error.map.offload_mismatch = true;
+ else
+ dm->error.map.offload_mismatch = false;
+
+ ver_main = FIELD_GET(GENMASK(31, 24), wl->ver_info.fw_coex);
+ ver_sub = FIELD_GET(GENMASK(23, 16), wl->ver_info.fw_coex);
+ ver_hotfix = FIELD_GET(GENMASK(15, 8), wl->ver_info.fw_coex);
+ id_branch = FIELD_GET(GENMASK(7, 0), wl->ver_info.fw_coex);
+ seq_printf(m, "WL_FW_coex:%d.%d.%d(branch:%d)",
+ ver_main, ver_sub, ver_hotfix, id_branch);
+
+ ver_main = FIELD_GET(GENMASK(31, 24), chip->wlcx_desired);
+ ver_sub = FIELD_GET(GENMASK(23, 16), chip->wlcx_desired);
+ ver_hotfix = FIELD_GET(GENMASK(15, 8), chip->wlcx_desired);
+ seq_printf(m, "(%s, desired:%d.%d.%d), ",
+ (wl->ver_info.fw_coex >= chip->wlcx_desired ?
+ "Match" : "Mis-Match"), ver_main, ver_sub, ver_hotfix);
+
+ seq_printf(m, "BT_FW_coex:%d(%s, desired:%d)\n",
+ bt->ver_info.fw_coex,
+ (bt->ver_info.fw_coex >= chip->btcx_desired ?
+ "Match" : "Mis-Match"), chip->btcx_desired);
+
+ if (bt->enable.now && bt->ver_info.fw == 0)
+ rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_VER_INFO, true);
+ else
+ rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_VER_INFO, false);
+
+ ver_main = FIELD_GET(GENMASK(31, 24), wl->ver_info.fw);
+ ver_sub = FIELD_GET(GENMASK(23, 16), wl->ver_info.fw);
+ ver_hotfix = FIELD_GET(GENMASK(15, 8), wl->ver_info.fw);
+ id_branch = FIELD_GET(GENMASK(7, 0), wl->ver_info.fw);
+ seq_printf(m, " %-15s : WL_FW:%d.%d.%d.%d, BT_FW:0x%x(%s)\n",
+ "[sub_module]",
+ ver_main, ver_sub, ver_hotfix, id_branch,
+ bt->ver_info.fw, bt->run_patch_code ? "patch" : "ROM");
+
+ seq_printf(m, " %-15s : cv:%x, rfe_type:0x%x, ant_iso:%d, ant_pg:%d, %s",
+ "[hw_info]", btc->mdinfo.cv, btc->mdinfo.rfe_type,
+ btc->mdinfo.ant.isolation, btc->mdinfo.ant.num,
+ (btc->mdinfo.ant.num > 1 ? "" : (btc->mdinfo.ant.single_pos ?
+ "1Ant_Pos:S1, " : "1Ant_Pos:S0, ")));
+
+ seq_printf(m, "3rd_coex:%d, dbcc:%d, tx_num:%d, rx_num:%d\n",
+ btc->cx.other.type, rtwdev->dbcc_en, chip->tx_nss,
+ chip->rx_nss);
+}
+
+static void _show_wl_role_info(struct rtw89_dev *rtwdev, struct seq_file *m)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_wl_link_info *plink = NULL;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+ struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info;
+ struct rtw89_traffic_stats *t;
+ u8 i;
+
+ if (rtwdev->dbcc_en) {
+ seq_printf(m,
+ " %-15s : PHY0_band(op:%d/scan:%d/real:%d), ",
+ "[dbcc_info]", wl_dinfo->op_band[RTW89_PHY_0],
+ wl_dinfo->scan_band[RTW89_PHY_0],
+ wl_dinfo->real_band[RTW89_PHY_0]);
+ seq_printf(m,
+ "PHY1_band(op:%d/scan:%d/real:%d)\n",
+ wl_dinfo->op_band[RTW89_PHY_1],
+ wl_dinfo->scan_band[RTW89_PHY_1],
+ wl_dinfo->real_band[RTW89_PHY_1]);
+ }
+
+ for (i = 0; i < RTW89_MAX_HW_PORT_NUM; i++) {
+ plink = &btc->cx.wl.link_info[i];
+
+ if (!plink->active)
+ continue;
+
+ seq_printf(m,
+ " [port_%d] : role=%d(phy-%d), connect=%d(client_cnt=%d), mode=%d, center_ch=%d, bw=%d",
+ plink->pid, (u32)plink->role, plink->phy,
+ (u32)plink->connected, plink->client_cnt - 1,
+ (u32)plink->mode, plink->ch, (u32)plink->bw);
+
+ if (plink->connected == MLME_NO_LINK)
+ continue;
+
+ seq_printf(m,
+ ", mac_id=%d, max_tx_time=%dus, max_tx_retry=%d\n",
+ plink->mac_id, plink->tx_time, plink->tx_retry);
+
+ seq_printf(m,
+ " [port_%d] : rssi=-%ddBm(%d), busy=%d, dir=%s, ",
+ plink->pid, 110 - plink->stat.rssi,
+ plink->stat.rssi, plink->busy,
+ plink->dir == RTW89_TFC_UL ? "UL" : "DL");
+
+ t = &plink->stat.traffic;
+
+ seq_printf(m,
+ "tx[rate:%d/busy_level:%d], ",
+ (u32)t->tx_rate, t->tx_tfc_lv);
+
+ seq_printf(m, "rx[rate:%d/busy_level:%d/drop:%d]\n",
+ (u32)t->rx_rate,
+ t->rx_tfc_lv, plink->rx_rate_drop_cnt);
+ }
+}
+
+static void _show_wl_info(struct rtw89_dev *rtwdev, struct seq_file *m)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_cx *cx = &btc->cx;
+ struct rtw89_btc_wl_info *wl = &cx->wl;
+ struct rtw89_btc_wl_role_info *wl_rinfo = &wl->role_info;
+
+ if (!(btc->dm.coex_info_map & BTC_COEX_INFO_WL))
+ return;
+
+ seq_puts(m, "========== [WL Status] ==========\n");
+
+ seq_printf(m, " %-15s : link_mode:%d, ",
+ "[status]", (u32)wl_rinfo->link_mode);
+
+ seq_printf(m,
+ "rf_off:%s, power_save:%s, scan:%s(band:%d/phy_map:0x%x), ",
+ wl->status.map.rf_off ? "Y" : "N",
+ wl->status.map.lps ? "Y" : "N",
+ wl->status.map.scan ? "Y" : "N",
+ wl->scan_info.band[RTW89_PHY_0], wl->scan_info.phy_map);
+
+ seq_printf(m,
+ "connecting:%s, roam:%s, 4way:%s, init_ok:%s\n",
+ wl->status.map.connecting ? "Y" : "N",
+ wl->status.map.roaming ? "Y" : "N",
+ wl->status.map._4way ? "Y" : "N",
+ wl->status.map.init_ok ? "Y" : "N");
+
+ _show_wl_role_info(rtwdev, m);
+}
+
+#define _get_bt_polt_cnt(rtwdev, phy, polt_cnt) do {} while (0)
+
+enum btc_bt_a2dp_type {
+ BTC_A2DP_LEGACY = 0,
+ BTC_A2DP_TWS_SNIFF = 1,
+ BTC_A2DP_TWS_RELAY = 2,
+};
+
+static void _show_bt_profile_info(struct rtw89_dev *rtwdev, struct seq_file *m)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt.link_info;
+ struct rtw89_btc_bt_hfp_desc hfp = bt_linfo->hfp_desc;
+ struct rtw89_btc_bt_hid_desc hid = bt_linfo->hid_desc;
+ struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc;
+ struct rtw89_btc_bt_pan_desc pan = bt_linfo->pan_desc;
+
+ if (hfp.exist) {
+ seq_printf(m, " %-15s : type:%s, sut_pwr:%d, golden-rx:%d",
+ "[HFP]", (hfp.type == 0 ? "SCO" : "eSCO"),
+ bt_linfo->sut_pwr_level[0],
+ bt_linfo->golden_rx_shift[0]);
+ }
+
+ if (hid.exist) {
+ seq_printf(m,
+ "\n\r %-15s : type:%s%s%s%s%s pair-cnt:%d, sut_pwr:%d, golden-rx:%d\n",
+ "[HID]",
+ hid.type & BTC_HID_218 ? "2/18," : "",
+ hid.type & BTC_HID_418 ? "4/18," : "",
+ hid.type & BTC_HID_BLE ? "BLE," : "",
+ hid.type & BTC_HID_RCU ? "RCU," : "",
+ hid.type & BTC_HID_RCU_VOICE ? "RCU-Voice," : "",
+ hid.pair_cnt, bt_linfo->sut_pwr_level[1],
+ bt_linfo->golden_rx_shift[1]);
+ }
+
+ if (a2dp.exist) {
+ seq_printf(m,
+ " %-15s : type:%s, bit-pool:%d, flush-time:%d, ",
+ "[A2DP]",
+ a2dp.type == BTC_A2DP_LEGACY ? "Legacy" : "TWS",
+ a2dp.bitpool, a2dp.flush_time);
+
+ seq_printf(m,
+ "vid:0x%x, Dev-name:0x%x, sut_pwr:%d, golden-rx:%d\n",
+ a2dp.vendor_id, a2dp.device_name,
+ bt_linfo->sut_pwr_level[2],
+ bt_linfo->golden_rx_shift[2]);
+ }
+
+ if (pan.exist) {
+ seq_printf(m, " %-15s : sut_pwr:%d, golden-rx:%d\n",
+ "[PAN]",
+ bt_linfo->sut_pwr_level[3],
+ bt_linfo->golden_rx_shift[3]);
+ }
+}
+
+static void _show_bt_info(struct rtw89_dev *rtwdev, struct seq_file *m)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_cx *cx = &btc->cx;
+ struct rtw89_btc_bt_info *bt = &cx->bt;
+ struct rtw89_btc_wl_info *wl = &cx->wl;
+ struct rtw89_btc_module *module = &btc->mdinfo;
+ struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info;
+ u8 *afh = bt_linfo->afh_map;
+ u16 polt_cnt = 0;
+
+ if (!(btc->dm.coex_info_map & BTC_COEX_INFO_BT))
+ return;
+
+ seq_puts(m, "========== [BT Status] ==========\n");
+
+ seq_printf(m, " %-15s : enable:%s, btg:%s%s, connect:%s, ",
+ "[status]", bt->enable.now ? "Y" : "N",
+ bt->btg_type ? "Y" : "N",
+ (bt->enable.now && (bt->btg_type != module->bt_pos) ?
+ "(efuse-mismatch!!)" : ""),
+ (bt_linfo->status.map.connect ? "Y" : "N"));
+
+ seq_printf(m, "igno_wl:%s, mailbox_avl:%s, rfk_state:0x%x\n",
+ bt->igno_wl ? "Y" : "N",
+ bt->mbx_avl ? "Y" : "N", bt->rfk_info.val);
+
+ seq_printf(m, " %-15s : profile:%s%s%s%s%s ",
+ "[profile]",
+ (bt_linfo->profile_cnt.now == 0) ? "None," : "",
+ bt_linfo->hfp_desc.exist ? "HFP," : "",
+ bt_linfo->hid_desc.exist ? "HID," : "",
+ bt_linfo->a2dp_desc.exist ?
+ (bt_linfo->a2dp_desc.sink ? "A2DP_sink," : "A2DP,") : "",
+ bt_linfo->pan_desc.exist ? "PAN," : "");
+
+ seq_printf(m,
+ "multi-link:%s, role:%s, ble-connect:%s, CQDDR:%s, A2DP_active:%s, PAN_active:%s\n",
+ bt_linfo->multi_link.now ? "Y" : "N",
+ bt_linfo->slave_role ? "Slave" : "Master",
+ bt_linfo->status.map.ble_connect ? "Y" : "N",
+ bt_linfo->cqddr ? "Y" : "N",
+ bt_linfo->a2dp_desc.active ? "Y" : "N",
+ bt_linfo->pan_desc.active ? "Y" : "N");
+
+ seq_printf(m,
+ " %-15s : rssi:%ddBm, tx_rate:%dM, %s%s%s",
+ "[link]", bt_linfo->rssi - 100,
+ bt_linfo->tx_3m ? 3 : 2,
+ bt_linfo->status.map.inq_pag ? " inq-page!!" : "",
+ bt_linfo->status.map.acl_busy ? " acl_busy!!" : "",
+ bt_linfo->status.map.mesh_busy ? " mesh_busy!!" : "");
+
+ seq_printf(m,
+ "%s afh_map[%02x%02x_%02x%02x_%02x%02x_%02x%02x_%02x%02x], ",
+ bt_linfo->relink.now ? " ReLink!!" : "",
+ afh[0], afh[1], afh[2], afh[3], afh[4],
+ afh[5], afh[6], afh[7], afh[8], afh[9]);
+
+ seq_printf(m, "wl_ch_map[en:%d/ch:%d/bw:%d]\n",
+ wl->afh_info.en, wl->afh_info.ch, wl->afh_info.bw);
+
+ seq_printf(m,
+ " %-15s : retry:%d, relink:%d, rate_chg:%d, reinit:%d, reenable:%d, ",
+ "[stat_cnt]", cx->cnt_bt[BTC_BCNT_RETRY],
+ cx->cnt_bt[BTC_BCNT_RELINK], cx->cnt_bt[BTC_BCNT_RATECHG],
+ cx->cnt_bt[BTC_BCNT_REINIT], cx->cnt_bt[BTC_BCNT_REENABLE]);
+
+ seq_printf(m,
+ "role-switch:%d, afh:%d, inq_page:%d(inq:%d/page:%d), igno_wl:%d\n",
+ cx->cnt_bt[BTC_BCNT_ROLESW], cx->cnt_bt[BTC_BCNT_AFH],
+ cx->cnt_bt[BTC_BCNT_INQPAG], cx->cnt_bt[BTC_BCNT_INQ],
+ cx->cnt_bt[BTC_BCNT_PAGE], cx->cnt_bt[BTC_BCNT_IGNOWL]);
+
+ _show_bt_profile_info(rtwdev, m);
+
+ seq_printf(m,
+ " %-15s : raw_data[%02x %02x %02x %02x %02x %02x] (type:%s/cnt:%d/same:%d)\n",
+ "[bt_info]", bt->raw_info[2], bt->raw_info[3],
+ bt->raw_info[4], bt->raw_info[5], bt->raw_info[6],
+ bt->raw_info[7],
+ bt->raw_info[0] == BTC_BTINFO_AUTO ? "auto" : "reply",
+ cx->cnt_bt[BTC_BCNT_INFOUPDATE],
+ cx->cnt_bt[BTC_BCNT_INFOSAME]);
+
+ if (wl->status.map.lps || wl->status.map.rf_off)
+ return;
+
+ chip->ops->btc_update_bt_cnt(rtwdev);
+ _chk_btc_err(rtwdev, BTC_DCNT_BTCNT_FREEZE, 0);
+ _get_bt_polt_cnt(rtwdev, RTW89_PHY_0, &polt_cnt);
+
+ seq_printf(m,
+ " %-15s : Hi-rx = %d, Hi-tx = %d, Lo-rx = %d, Lo-tx = %d (bt_polut_wl_tx = %d)\n",
+ "[trx_req_cnt]", cx->cnt_bt[BTC_BCNT_HIPRI_RX],
+ cx->cnt_bt[BTC_BCNT_HIPRI_TX], cx->cnt_bt[BTC_BCNT_LOPRI_RX],
+ cx->cnt_bt[BTC_BCNT_LOPRI_TX], polt_cnt);
+}
+
+#define CASE_BTC_RSN_STR(e) case BTC_RSN_ ## e: return #e
+#define CASE_BTC_ACT_STR(e) case BTC_ACT_ ## e | BTC_ACT_EXT_BIT: return #e
+#define CASE_BTC_POLICY_STR(e) \
+ case BTC_CXP_ ## e | BTC_POLICY_EXT_BIT: return #e
+
+static const char *steps_to_str(u16 step)
+{
+ switch (step) {
+ CASE_BTC_RSN_STR(NONE);
+ CASE_BTC_RSN_STR(NTFY_INIT);
+ CASE_BTC_RSN_STR(NTFY_SWBAND);
+ CASE_BTC_RSN_STR(NTFY_WL_STA);
+ CASE_BTC_RSN_STR(NTFY_RADIO_STATE);
+ CASE_BTC_RSN_STR(UPDATE_BT_SCBD);
+ CASE_BTC_RSN_STR(NTFY_WL_RFK);
+ CASE_BTC_RSN_STR(UPDATE_BT_INFO);
+ CASE_BTC_RSN_STR(NTFY_SCAN_START);
+ CASE_BTC_RSN_STR(NTFY_SCAN_FINISH);
+ CASE_BTC_RSN_STR(NTFY_SPECIFIC_PACKET);
+ CASE_BTC_RSN_STR(NTFY_POWEROFF);
+ CASE_BTC_RSN_STR(NTFY_ROLE_INFO);
+ CASE_BTC_RSN_STR(CMD_SET_COEX);
+
+ CASE_BTC_ACT_STR(NONE);
+ CASE_BTC_ACT_STR(WL_ONLY);
+ CASE_BTC_ACT_STR(WL_5G);
+ CASE_BTC_ACT_STR(WL_OTHER);
+ CASE_BTC_ACT_STR(WL_IDLE);
+ CASE_BTC_ACT_STR(WL_NC);
+ CASE_BTC_ACT_STR(WL_RFK);
+ CASE_BTC_ACT_STR(WL_INIT);
+ CASE_BTC_ACT_STR(WL_OFF);
+ CASE_BTC_ACT_STR(FREERUN);
+ CASE_BTC_ACT_STR(BT_WHQL);
+ CASE_BTC_ACT_STR(BT_RFK);
+ CASE_BTC_ACT_STR(BT_OFF);
+ CASE_BTC_ACT_STR(BT_IDLE);
+ CASE_BTC_ACT_STR(BT_HFP);
+ CASE_BTC_ACT_STR(BT_HID);
+ CASE_BTC_ACT_STR(BT_A2DP);
+ CASE_BTC_ACT_STR(BT_A2DPSINK);
+ CASE_BTC_ACT_STR(BT_PAN);
+ CASE_BTC_ACT_STR(BT_A2DP_HID);
+ CASE_BTC_ACT_STR(BT_A2DP_PAN);
+ CASE_BTC_ACT_STR(BT_PAN_HID);
+ CASE_BTC_ACT_STR(BT_A2DP_PAN_HID);
+ CASE_BTC_ACT_STR(WL_25G_MCC);
+ CASE_BTC_ACT_STR(WL_2G_MCC);
+ CASE_BTC_ACT_STR(WL_2G_SCC);
+ CASE_BTC_ACT_STR(WL_2G_AP);
+ CASE_BTC_ACT_STR(WL_2G_GO);
+ CASE_BTC_ACT_STR(WL_2G_GC);
+ CASE_BTC_ACT_STR(WL_2G_NAN);
+
+ CASE_BTC_POLICY_STR(OFF_BT);
+ CASE_BTC_POLICY_STR(OFF_WL);
+ CASE_BTC_POLICY_STR(OFF_EQ0);
+ CASE_BTC_POLICY_STR(OFF_EQ1);
+ CASE_BTC_POLICY_STR(OFF_EQ2);
+ CASE_BTC_POLICY_STR(OFF_EQ3);
+ CASE_BTC_POLICY_STR(OFF_BWB0);
+ CASE_BTC_POLICY_STR(OFF_BWB1);
+ CASE_BTC_POLICY_STR(OFFB_BWB0);
+ CASE_BTC_POLICY_STR(OFFE_DEF);
+ CASE_BTC_POLICY_STR(OFFE_DEF2);
+ CASE_BTC_POLICY_STR(FIX_TD3030);
+ CASE_BTC_POLICY_STR(FIX_TD5050);
+ CASE_BTC_POLICY_STR(FIX_TD2030);
+ CASE_BTC_POLICY_STR(FIX_TD4010);
+ CASE_BTC_POLICY_STR(FIX_TD7010);
+ CASE_BTC_POLICY_STR(FIX_TD2060);
+ CASE_BTC_POLICY_STR(FIX_TD3060);
+ CASE_BTC_POLICY_STR(FIX_TD2080);
+ CASE_BTC_POLICY_STR(FIX_TDW1B1);
+ CASE_BTC_POLICY_STR(FIX_TD4020);
+ CASE_BTC_POLICY_STR(PFIX_TD3030);
+ CASE_BTC_POLICY_STR(PFIX_TD5050);
+ CASE_BTC_POLICY_STR(PFIX_TD2030);
+ CASE_BTC_POLICY_STR(PFIX_TD2060);
+ CASE_BTC_POLICY_STR(PFIX_TD3070);
+ CASE_BTC_POLICY_STR(PFIX_TD2080);
+ CASE_BTC_POLICY_STR(PFIX_TDW1B1);
+ CASE_BTC_POLICY_STR(AUTO_TD50200);
+ CASE_BTC_POLICY_STR(AUTO_TD60200);
+ CASE_BTC_POLICY_STR(AUTO_TD20200);
+ CASE_BTC_POLICY_STR(AUTO_TDW1B1);
+ CASE_BTC_POLICY_STR(PAUTO_TD50200);
+ CASE_BTC_POLICY_STR(PAUTO_TD60200);
+ CASE_BTC_POLICY_STR(PAUTO_TD20200);
+ CASE_BTC_POLICY_STR(PAUTO_TDW1B1);
+ CASE_BTC_POLICY_STR(AUTO2_TD3050);
+ CASE_BTC_POLICY_STR(AUTO2_TD3070);
+ CASE_BTC_POLICY_STR(AUTO2_TD5050);
+ CASE_BTC_POLICY_STR(AUTO2_TD6060);
+ CASE_BTC_POLICY_STR(AUTO2_TD2080);
+ CASE_BTC_POLICY_STR(AUTO2_TDW1B4);
+ CASE_BTC_POLICY_STR(PAUTO2_TD3050);
+ CASE_BTC_POLICY_STR(PAUTO2_TD3070);
+ CASE_BTC_POLICY_STR(PAUTO2_TD5050);
+ CASE_BTC_POLICY_STR(PAUTO2_TD6060);
+ CASE_BTC_POLICY_STR(PAUTO2_TD2080);
+ CASE_BTC_POLICY_STR(PAUTO2_TDW1B4);
+ default:
+ return "unknown step";
+ }
+}
+
+static
+void seq_print_segment(struct seq_file *m, const char *prefix, u16 *data,
+ u8 len, u8 seg_len, u8 start_idx, u8 ring_len)
+{
+ u8 i;
+ u8 cur_index;
+
+ for (i = 0; i < len ; i++) {
+ if ((i % seg_len) == 0)
+ seq_printf(m, " %-15s : ", prefix);
+ cur_index = (start_idx + i) % ring_len;
+ if (i % 3 == 0)
+ seq_printf(m, "-> %-20s",
+ steps_to_str(*(data + cur_index)));
+ else if (i % 3 == 1)
+ seq_printf(m, "-> %-15s",
+ steps_to_str(*(data + cur_index)));
+ else
+ seq_printf(m, "-> %-13s",
+ steps_to_str(*(data + cur_index)));
+ if (i == (len - 1) || (i % seg_len) == (seg_len - 1))
+ seq_puts(m, "\n");
+ }
+}
+
+static void _show_dm_step(struct rtw89_dev *rtwdev, struct seq_file *m)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_dm *dm = &btc->dm;
+ u8 start_idx;
+ u8 len;
+
+ len = dm->dm_step.step_ov ? RTW89_BTC_DM_MAXSTEP : dm->dm_step.step_pos;
+ start_idx = dm->dm_step.step_ov ? dm->dm_step.step_pos : 0;
+
+ seq_print_segment(m, "[dm_steps]", dm->dm_step.step, len, 6, start_idx,
+ ARRAY_SIZE(dm->dm_step.step));
+}
+
+static void _show_dm_info(struct rtw89_dev *rtwdev, struct seq_file *m)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_module *module = &btc->mdinfo;
+ struct rtw89_btc_dm *dm = &btc->dm;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+ struct rtw89_btc_bt_info *bt = &btc->cx.bt;
+
+ if (!(dm->coex_info_map & BTC_COEX_INFO_DM))
+ return;
+
+ seq_printf(m, "========== [Mechanism Status %s] ==========\n",
+ (btc->ctrl.manual ? "(Manual)" : "(Auto)"));
+
+ seq_printf(m,
+ " %-15s : type:%s, reason:%s(), action:%s(), ant_path:%ld, run_cnt:%d\n",
+ "[status]",
+ module->ant.type == BTC_ANT_SHARED ? "shared" : "dedicated",
+ steps_to_str(dm->run_reason),
+ steps_to_str(dm->run_action | BTC_ACT_EXT_BIT),
+ FIELD_GET(GENMASK(7, 0), dm->set_ant_path),
+ dm->cnt_dm[BTC_DCNT_RUN]);
+
+ _show_dm_step(rtwdev, m);
+
+ seq_printf(m, " %-15s : wl_only:%d, bt_only:%d, igno_bt:%d, free_run:%d, wl_ps_ctrl:%d, wl_mimo_ps:%d, ",
+ "[dm_flag]", dm->wl_only, dm->bt_only, btc->ctrl.igno_bt,
+ dm->freerun, btc->lps, dm->wl_mimo_ps);
+
+ seq_printf(m, "leak_ap:%d, fw_offload:%s%s\n", dm->leak_ap,
+ (BTC_CX_FW_OFFLOAD ? "Y" : "N"),
+ (dm->wl_fw_cx_offload == BTC_CX_FW_OFFLOAD ?
+ "" : "(Mis-Match!!)"));
+
+ if (dm->rf_trx_para.wl_tx_power == 0xff)
+ seq_printf(m,
+ " %-15s : wl_rssi_lvl:%d, para_lvl:%d, wl_tx_pwr:orig, ",
+ "[trx_ctrl]", wl->rssi_level, dm->trx_para_level);
+
+ else
+ seq_printf(m,
+ " %-15s : wl_rssi_lvl:%d, para_lvl:%d, wl_tx_pwr:%d, ",
+ "[trx_ctrl]", wl->rssi_level, dm->trx_para_level,
+ dm->rf_trx_para.wl_tx_power);
+
+ seq_printf(m,
+ "wl_rx_lvl:%d, bt_tx_pwr_dec:%d, bt_rx_lna:%d(%s-tbl), wl_btg_rx:%d\n",
+ dm->rf_trx_para.wl_rx_gain, dm->rf_trx_para.bt_tx_power,
+ dm->rf_trx_para.bt_rx_gain,
+ (bt->hi_lna_rx ? "Hi" : "Ori"), dm->wl_btg_rx);
+
+ seq_printf(m,
+ " %-15s : wl_tx_limit[en:%d/max_t:%dus/max_retry:%d], bt_slot_reg:%d-TU\n",
+ "[dm_ctrl]", dm->wl_tx_limit.enable, dm->wl_tx_limit.tx_time,
+ dm->wl_tx_limit.tx_retry, btc->bt_req_len);
+}
+
+static void _show_error(struct rtw89_dev *rtwdev, struct seq_file *m)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo;
+ struct rtw89_btc_fbtc_cysta *pcysta = NULL;
+
+ pcysta = &pfwinfo->rpt_fbtc_cysta.finfo;
+
+ if (pfwinfo->event[BTF_EVNT_BUF_OVERFLOW] == 0 &&
+ pcysta->except_cnt == 0 &&
+ !pfwinfo->len_mismch && !pfwinfo->fver_mismch)
+ return;
+
+ seq_printf(m, " %-15s : ", "[error]");
+
+ if (pfwinfo->event[BTF_EVNT_BUF_OVERFLOW]) {
+ seq_printf(m,
+ "overflow-cnt: %d, ",
+ pfwinfo->event[BTF_EVNT_BUF_OVERFLOW]);
+ }
+
+ if (pfwinfo->len_mismch) {
+ seq_printf(m,
+ "len-mismatch: 0x%x, ",
+ pfwinfo->len_mismch);
+ }
+
+ if (pfwinfo->fver_mismch) {
+ seq_printf(m,
+ "fver-mismatch: 0x%x, ",
+ pfwinfo->fver_mismch);
+ }
+
+ /* cycle statistics exceptions */
+ if (pcysta->exception || pcysta->except_cnt) {
+ seq_printf(m,
+ "exception-type: 0x%x, exception-cnt = %d",
+ pcysta->exception, pcysta->except_cnt);
+ }
+ seq_puts(m, "\n");
+}
+
+static void _show_fbtc_tdma(struct rtw89_dev *rtwdev, struct seq_file *m)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo;
+ struct rtw89_btc_rpt_cmn_info *pcinfo = NULL;
+ struct rtw89_btc_fbtc_tdma *t = NULL;
+ struct rtw89_btc_fbtc_slot *s = NULL;
+ struct rtw89_btc_dm *dm = &btc->dm;
+ u8 i, cnt = 0;
+
+ pcinfo = &pfwinfo->rpt_fbtc_tdma.cinfo;
+ if (!pcinfo->valid)
+ return;
+
+ t = &pfwinfo->rpt_fbtc_tdma.finfo;
+
+ seq_printf(m,
+ " %-15s : ", "[tdma_policy]");
+ seq_printf(m,
+ "type:%d, rx_flow_ctrl:%d, tx_pause:%d, ",
+ (u32)t->type,
+ t->rxflctrl, t->txpause);
+
+ seq_printf(m,
+ "wl_toggle_n:%d, leak_n:%d, ext_ctrl:%d, ",
+ t->wtgle_n, t->leak_n, t->ext_ctrl);
+
+ seq_printf(m,
+ "policy_type:%d",
+ (u32)btc->policy_type);
+
+ s = pfwinfo->rpt_fbtc_slots.finfo.slot;
+
+ for (i = 0; i < CXST_MAX; i++) {
+ if (dm->update_slot_map == BIT(CXST_MAX) - 1)
+ break;
+
+ if (!(dm->update_slot_map & BIT(i)))
+ continue;
+
+ if (cnt % 6 == 0)
+ seq_printf(m,
+ " %-15s : %d[%d/0x%x/%d]",
+ "[slot_policy]",
+ (u32)i,
+ s[i].dur, s[i].cxtbl, s[i].cxtype);
+ else
+ seq_printf(m,
+ ", %d[%d/0x%x/%d]",
+ (u32)i,
+ s[i].dur, s[i].cxtbl, s[i].cxtype);
+ if (cnt % 6 == 5)
+ seq_puts(m, "\n");
+ cnt++;
+ }
+ seq_puts(m, "\n");
+}
+
+static void _show_fbtc_slots(struct rtw89_dev *rtwdev, struct seq_file *m)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo;
+ struct rtw89_btc_rpt_cmn_info *pcinfo = NULL;
+ struct rtw89_btc_fbtc_slots *pslots = NULL;
+ struct rtw89_btc_fbtc_slot s;
+ u8 i = 0;
+
+ pcinfo = &pfwinfo->rpt_fbtc_slots.cinfo;
+ if (!pcinfo->valid)
+ return;
+
+ pslots = &pfwinfo->rpt_fbtc_slots.finfo;
+
+ for (i = 0; i < CXST_MAX; i++) {
+ s = pslots->slot[i];
+ if (i % 6 == 0)
+ seq_printf(m,
+ " %-15s : %02d[%03d/0x%x/%d]",
+ "[slot_list]",
+ (u32)i,
+ s.dur, s.cxtbl, s.cxtype);
+ else
+ seq_printf(m,
+ ", %02d[%03d/0x%x/%d]",
+ (u32)i,
+ s.dur, s.cxtbl, s.cxtype);
+ if (i % 6 == 5)
+ seq_puts(m, "\n");
+ }
+}
+
+static void _show_fbtc_cysta(struct rtw89_dev *rtwdev, struct seq_file *m)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo;
+ struct rtw89_btc_dm *dm = &btc->dm;
+ struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt.link_info.a2dp_desc;
+ struct rtw89_btc_rpt_cmn_info *pcinfo = NULL;
+ struct rtw89_btc_fbtc_cysta *pcysta_le32 = NULL;
+ struct rtw89_btc_fbtc_cysta_cpu pcysta[1];
+ union rtw89_btc_fbtc_rxflct r;
+ u8 i, cnt = 0, slot_pair;
+ u16 cycle, c_begin, c_end, store_index;
+
+ pcinfo = &pfwinfo->rpt_fbtc_cysta.cinfo;
+ if (!pcinfo->valid)
+ return;
+
+ pcysta_le32 = &pfwinfo->rpt_fbtc_cysta.finfo;
+ rtw89_btc_fbtc_cysta_to_cpu(pcysta_le32, pcysta);
+ seq_printf(m,
+ " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]",
+ "[cycle_cnt]", pcysta->cycles, pcysta->bcn_cnt[CXBCN_ALL],
+ pcysta->bcn_cnt[CXBCN_ALL_OK],
+ pcysta->bcn_cnt[CXBCN_BT_SLOT],
+ pcysta->bcn_cnt[CXBCN_BT_OK]);
+
+ _chk_btc_err(rtwdev, BTC_DCNT_CYCLE_FREEZE, (u32)pcysta->cycles);
+
+ for (i = 0; i < CXST_MAX; i++) {
+ if (!pcysta->slot_cnt[i])
+ continue;
+ seq_printf(m,
+ ", %d:%d", (u32)i, pcysta->slot_cnt[i]);
+ }
+
+ if (dm->tdma_now.rxflctrl) {
+ seq_printf(m,
+ ", leak_rx:%d", pcysta->leakrx_cnt);
+ }
+
+ if (pcysta->collision_cnt) {
+ seq_printf(m,
+ ", collision:%d", pcysta->collision_cnt);
+ }
+
+ if (pcysta->skip_cnt) {
+ seq_printf(m,
+ ", skip:%d", pcysta->skip_cnt);
+ }
+ seq_puts(m, "\n");
+
+ _chk_btc_err(rtwdev, BTC_DCNT_W1_FREEZE, pcysta->slot_cnt[CXST_W1]);
+ _chk_btc_err(rtwdev, BTC_DCNT_B1_FREEZE, pcysta->slot_cnt[CXST_B1]);
+
+ seq_printf(m, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]",
+ "[cycle_time]",
+ pcysta->tavg_cycle[CXT_WL],
+ pcysta->tavg_cycle[CXT_BT],
+ pcysta->tavg_lk / 1000, pcysta->tavg_lk % 1000);
+ seq_printf(m,
+ ", max_t[wl:%d/bt:%d/lk:%d.%03d]",
+ pcysta->tmax_cycle[CXT_WL],
+ pcysta->tmax_cycle[CXT_BT],
+ pcysta->tmax_lk / 1000, pcysta->tmax_lk % 1000);
+ seq_printf(m,
+ ", maxdiff_t[wl:%d/bt:%d]\n",
+ pcysta->tmaxdiff_cycle[CXT_WL],
+ pcysta->tmaxdiff_cycle[CXT_BT]);
+
+ if (pcysta->cycles == 0)
+ return;
+
+ /* 1 cycle record 1 wl-slot and 1 bt-slot */
+ slot_pair = BTC_CYCLE_SLOT_MAX / 2;
+
+ if (pcysta->cycles <= slot_pair)
+ c_begin = 1;
+ else
+ c_begin = pcysta->cycles - slot_pair + 1;
+
+ c_end = pcysta->cycles;
+
+ for (cycle = c_begin; cycle <= c_end; cycle++) {
+ cnt++;
+ store_index = ((cycle - 1) % slot_pair) * 2;
+
+ if (cnt % (BTC_CYCLE_SLOT_MAX / 4) == 1)
+ seq_printf(m,
+ " %-15s : ->b%02d->w%02d", "[cycle_step]",
+ pcysta->tslot_cycle[store_index],
+ pcysta->tslot_cycle[store_index + 1]);
+ else
+ seq_printf(m,
+ "->b%02d->w%02d",
+ pcysta->tslot_cycle[store_index],
+ pcysta->tslot_cycle[store_index + 1]);
+ if (cnt % (BTC_CYCLE_SLOT_MAX / 4) == 0 || cnt == c_end)
+ seq_puts(m, "\n");
+ }
+
+ if (a2dp->exist) {
+ seq_printf(m,
+ " %-15s : a2dp_ept:%d, a2dp_late:%d",
+ "[a2dp_t_sta]",
+ pcysta->a2dpept, pcysta->a2dpeptto);
+
+ seq_printf(m,
+ ", avg_t:%d, max_t:%d",
+ pcysta->tavg_a2dpept, pcysta->tmax_a2dpept);
+ r.val = dm->tdma_now.rxflctrl;
+
+ if (r.type && r.tgln_n) {
+ seq_printf(m,
+ ", cycle[PSTDMA:%d/TDMA:%d], ",
+ pcysta->cycles_a2dp[CXT_FLCTRL_ON],
+ pcysta->cycles_a2dp[CXT_FLCTRL_OFF]);
+
+ seq_printf(m,
+ "avg_t[PSTDMA:%d/TDMA:%d], ",
+ pcysta->tavg_a2dp[CXT_FLCTRL_ON],
+ pcysta->tavg_a2dp[CXT_FLCTRL_OFF]);
+
+ seq_printf(m,
+ "max_t[PSTDMA:%d/TDMA:%d]",
+ pcysta->tmax_a2dp[CXT_FLCTRL_ON],
+ pcysta->tmax_a2dp[CXT_FLCTRL_OFF]);
+ }
+ seq_puts(m, "\n");
+ }
+}
+
+static void _show_fbtc_nullsta(struct rtw89_dev *rtwdev, struct seq_file *m)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo;
+ struct rtw89_btc_rpt_cmn_info *pcinfo = NULL;
+ struct rtw89_btc_fbtc_cynullsta *ns = NULL;
+ u8 i = 0;
+
+ if (!btc->dm.tdma_now.rxflctrl)
+ return;
+
+ pcinfo = &pfwinfo->rpt_fbtc_nullsta.cinfo;
+ if (!pcinfo->valid)
+ return;
+
+ ns = &pfwinfo->rpt_fbtc_nullsta.finfo;
+
+ seq_printf(m, " %-15s : ", "[null_sta]");
+
+ for (i = 0; i < 2; i++) {
+ if (i != 0)
+ seq_printf(m, ", null-%d", i);
+ else
+ seq_printf(m, "null-%d", i);
+ seq_printf(m, "[ok:%d/", le32_to_cpu(ns->result[i][1]));
+ seq_printf(m, "fail:%d/", le32_to_cpu(ns->result[i][0]));
+ seq_printf(m, "on_time:%d/", le32_to_cpu(ns->result[i][2]));
+ seq_printf(m, "retry:%d/", le32_to_cpu(ns->result[i][3]));
+ seq_printf(m, "avg_t:%d.%03d/",
+ le32_to_cpu(ns->avg_t[i]) / 1000,
+ le32_to_cpu(ns->avg_t[i]) % 1000);
+ seq_printf(m, "max_t:%d.%03d]",
+ le32_to_cpu(ns->max_t[i]) / 1000,
+ le32_to_cpu(ns->max_t[i]) % 1000);
+ }
+ seq_puts(m, "\n");
+}
+
+static void _show_fbtc_step(struct rtw89_dev *rtwdev, struct seq_file *m)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo;
+ struct rtw89_btc_rpt_cmn_info *pcinfo = NULL;
+ struct rtw89_btc_fbtc_steps *pstep = NULL;
+ u8 type, val, cnt = 0, state = 0;
+ bool outloop = false;
+ u16 i, diff_t, n_start = 0, n_stop = 0;
+ u16 pos_old, pos_new;
+
+ pcinfo = &pfwinfo->rpt_fbtc_step.cinfo;
+ if (!pcinfo->valid)
+ return;
+
+ pstep = &pfwinfo->rpt_fbtc_step.finfo;
+ pos_old = le16_to_cpu(pstep->pos_old);
+ pos_new = le16_to_cpu(pstep->pos_new);
+
+ if (pcinfo->req_fver != pstep->fver)
+ return;
+
+ /* store step info by using ring instead of FIFO*/
+ do {
+ switch (state) {
+ case 0:
+ n_start = pos_old;
+ if (pos_new >= pos_old)
+ n_stop = pos_new;
+ else
+ n_stop = btc->ctrl.trace_step - 1;
+
+ state = 1;
+ break;
+ case 1:
+ for (i = n_start; i <= n_stop; i++) {
+ type = pstep->step[i].type;
+ val = pstep->step[i].val;
+ diff_t = le16_to_cpu(pstep->step[i].difft);
+
+ if (type == CXSTEP_NONE || type >= CXSTEP_MAX)
+ continue;
+
+ if (cnt % 10 == 0)
+ seq_printf(m, " %-15s : ", "[steps]");
+
+ seq_printf(m, "-> %s(%02d)(%02d)",
+ (type == CXSTEP_SLOT ? "SLT" :
+ "EVT"), (u32)val, diff_t);
+ if (cnt % 10 == 9)
+ seq_puts(m, "\n");
+ cnt++;
+ }
+
+ state = 2;
+ break;
+ case 2:
+ if (pos_new < pos_old && n_start != 0) {
+ n_start = 0;
+ n_stop = pos_new;
+ state = 1;
+ } else {
+ outloop = true;
+ }
+ break;
+ }
+ } while (!outloop);
+}
+
+static void _show_fw_dm_msg(struct rtw89_dev *rtwdev, struct seq_file *m)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+
+ if (!(btc->dm.coex_info_map & BTC_COEX_INFO_DM))
+ return;
+
+ _show_error(rtwdev, m);
+ _show_fbtc_tdma(rtwdev, m);
+ _show_fbtc_slots(rtwdev, m);
+ _show_fbtc_cysta(rtwdev, m);
+ _show_fbtc_nullsta(rtwdev, m);
+ _show_fbtc_step(rtwdev, m);
+}
+
+static void _show_mreg(struct rtw89_dev *rtwdev, struct seq_file *m)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo;
+ struct rtw89_btc_rpt_cmn_info *pcinfo = NULL;
+ struct rtw89_btc_fbtc_mreg_val *pmreg = NULL;
+ struct rtw89_btc_fbtc_gpio_dbg *gdbg = NULL;
+ struct rtw89_btc_cx *cx = &btc->cx;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+ struct rtw89_btc_bt_info *bt = &btc->cx.bt;
+ struct rtw89_mac_ax_gnt gnt[2] = {0};
+ u8 i = 0, type = 0, cnt = 0;
+ u32 val, offset;
+
+ if (!(btc->dm.coex_info_map & BTC_COEX_INFO_MREG))
+ return;
+
+ seq_puts(m, "========== [HW Status] ==========\n");
+
+ seq_printf(m,
+ " %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)\n",
+ "[scoreboard]", wl->scbd, cx->cnt_wl[BTC_WCNT_SCBDUPDATE],
+ bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD],
+ cx->cnt_bt[BTC_BCNT_SCBDUPDATE]);
+
+ /* To avoid I/O if WL LPS or power-off */
+ if (!wl->status.map.lps && !wl->status.map.rf_off) {
+ rtw89_mac_read_lte(rtwdev, R_AX_LTE_SW_CFG_1, &val);
+ if (val & (B_AX_GNT_BT_RFC_S0_SW_VAL |
+ B_AX_GNT_BT_BB_S0_SW_VAL))
+ gnt[0].gnt_bt = true;
+ if (val & (B_AX_GNT_BT_RFC_S0_SW_CTRL |
+ B_AX_GNT_BT_BB_S0_SW_CTRL))
+ gnt[0].gnt_bt_sw_en = true;
+ if (val & (B_AX_GNT_WL_RFC_S0_SW_VAL |
+ B_AX_GNT_WL_BB_S0_SW_VAL))
+ gnt[0].gnt_wl = true;
+ if (val & (B_AX_GNT_WL_RFC_S0_SW_CTRL |
+ B_AX_GNT_WL_BB_S0_SW_CTRL))
+ gnt[0].gnt_wl_sw_en = true;
+
+ if (val & (B_AX_GNT_BT_RFC_S1_SW_VAL |
+ B_AX_GNT_BT_BB_S1_SW_VAL))
+ gnt[1].gnt_bt = true;
+ if (val & (B_AX_GNT_BT_RFC_S1_SW_CTRL |
+ B_AX_GNT_BT_BB_S1_SW_CTRL))
+ gnt[1].gnt_bt_sw_en = true;
+ if (val & (B_AX_GNT_WL_RFC_S1_SW_VAL |
+ B_AX_GNT_WL_BB_S1_SW_VAL))
+ gnt[1].gnt_wl = true;
+ if (val & (B_AX_GNT_WL_RFC_S1_SW_CTRL |
+ B_AX_GNT_WL_BB_S1_SW_CTRL))
+ gnt[1].gnt_wl_sw_en = true;
+
+ seq_printf(m,
+ " %-15s : pta_owner:%s, phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d], ",
+ "[gnt_status]",
+ (rtw89_mac_get_ctrl_path(rtwdev) ? "WL" : "BT"),
+ (gnt[0].gnt_wl_sw_en ? "SW" : "HW"), gnt[0].gnt_wl,
+ (gnt[0].gnt_bt_sw_en ? "SW" : "HW"), gnt[0].gnt_bt);
+
+ seq_printf(m, "phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]\n",
+ (gnt[1].gnt_wl_sw_en ? "SW" : "HW"), gnt[1].gnt_wl,
+ (gnt[1].gnt_bt_sw_en ? "SW" : "HW"), gnt[1].gnt_bt);
+ }
+
+ pcinfo = &pfwinfo->rpt_fbtc_mregval.cinfo;
+ if (!pcinfo->valid) {
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s stop due rpt_fbtc_mregval.cinfo\n",
+ __func__);
+ return;
+ }
+
+ pmreg = &pfwinfo->rpt_fbtc_mregval.finfo;
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s rpt_fbtc_mregval reg_num = %d\n",
+ __func__, pmreg->reg_num);
+
+ for (i = 0; i < pmreg->reg_num; i++) {
+ type = (u8)le16_to_cpu(chip->mon_reg[i].type);
+ offset = le32_to_cpu(chip->mon_reg[i].offset);
+ val = le32_to_cpu(pmreg->mreg_val[i]);
+
+ if (cnt % 6 == 0)
+ seq_printf(m, " %-15s : %d_0x%04x=0x%08x",
+ "[reg]", (u32)type, offset, val);
+ else
+ seq_printf(m, ", %d_0x%04x=0x%08x", (u32)type,
+ offset, val);
+ if (cnt % 6 == 5)
+ seq_puts(m, "\n");
+ cnt++;
+ }
+
+ pcinfo = &pfwinfo->rpt_fbtc_gpio_dbg.cinfo;
+ if (!pcinfo->valid) {
+ rtw89_debug(rtwdev, RTW89_DBG_BTC,
+ "[BTC], %s stop due rpt_fbtc_gpio_dbg.cinfo\n",
+ __func__);
+ return;
+ }
+
+ gdbg = &pfwinfo->rpt_fbtc_gpio_dbg.finfo;
+ if (!gdbg->en_map)
+ return;
+
+ seq_printf(m, " %-15s : enable_map:0x%08x",
+ "[gpio_dbg]", gdbg->en_map);
+
+ for (i = 0; i < BTC_DBG_MAX1; i++) {
+ if (!(gdbg->en_map & BIT(i)))
+ continue;
+ seq_printf(m, ", %d->GPIO%d", (u32)i, gdbg->gpio_map[i]);
+ }
+ seq_puts(m, "\n");
+}
+
+static void _show_summary(struct rtw89_dev *rtwdev, struct seq_file *m)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo;
+ struct rtw89_btc_rpt_cmn_info *pcinfo = NULL;
+ struct rtw89_btc_fbtc_rpt_ctrl *prptctrl = NULL;
+ struct rtw89_btc_cx *cx = &btc->cx;
+ struct rtw89_btc_dm *dm = &btc->dm;
+ struct rtw89_btc_wl_info *wl = &cx->wl;
+ struct rtw89_btc_bt_info *bt = &cx->bt;
+ u32 cnt_sum = 0, *cnt = btc->dm.cnt_notify;
+ u8 i;
+
+ if (!(dm->coex_info_map & BTC_COEX_INFO_SUMMARY))
+ return;
+
+ seq_puts(m, "========== [Statistics] ==========\n");
+
+ pcinfo = &pfwinfo->rpt_ctrl.cinfo;
+ if (pcinfo->valid && !wl->status.map.lps && !wl->status.map.rf_off) {
+ prptctrl = &pfwinfo->rpt_ctrl.finfo;
+
+ seq_printf(m,
+ " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d), ",
+ "[summary]", pfwinfo->cnt_h2c,
+ pfwinfo->cnt_h2c_fail, prptctrl->h2c_cnt,
+ pfwinfo->cnt_c2h, prptctrl->c2h_cnt);
+
+ seq_printf(m,
+ "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x, dm_error_map:0x%x",
+ pfwinfo->event[BTF_EVNT_RPT], prptctrl->rpt_cnt,
+ prptctrl->rpt_enable, dm->error.val);
+
+ _chk_btc_err(rtwdev, BTC_DCNT_RPT_FREEZE,
+ pfwinfo->event[BTF_EVNT_RPT]);
+
+ if (dm->error.map.wl_fw_hang)
+ seq_puts(m, " (WL FW Hang!!)");
+ seq_puts(m, "\n");
+ seq_printf(m,
+ " %-15s : send_ok:%d, send_fail:%d, recv:%d",
+ "[mailbox]", prptctrl->mb_send_ok_cnt,
+ prptctrl->mb_send_fail_cnt, prptctrl->mb_recv_cnt);
+
+ seq_printf(m,
+ "(A2DP_empty:%d, A2DP_flowstop:%d, A2DP_full:%d)\n",
+ prptctrl->mb_a2dp_empty_cnt,
+ prptctrl->mb_a2dp_flct_cnt,
+ prptctrl->mb_a2dp_full_cnt);
+
+ seq_printf(m,
+ " %-15s : wl_rfk[req:%d/go:%d/reject:%d/timeout:%d]",
+ "[RFK]", cx->cnt_wl[BTC_WCNT_RFK_REQ],
+ cx->cnt_wl[BTC_WCNT_RFK_GO],
+ cx->cnt_wl[BTC_WCNT_RFK_REJECT],
+ cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]);
+
+ seq_printf(m,
+ ", bt_rfk[req:%d/go:%d/reject:%d/timeout:%d/fail:%d]\n",
+ prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_REQ],
+ prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_GO],
+ prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_REJECT],
+ prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_TIMEOUT],
+ prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_FAIL]);
+
+ if (prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_TIMEOUT] > 0)
+ bt->rfk_info.map.timeout = 1;
+ else
+ bt->rfk_info.map.timeout = 0;
+
+ dm->error.map.wl_rfk_timeout = bt->rfk_info.map.timeout;
+ } else {
+ seq_printf(m,
+ " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d, rpt_cnt=%d, rpt_map=0x%x",
+ "[summary]", pfwinfo->cnt_h2c,
+ pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h,
+ pfwinfo->event[BTF_EVNT_RPT],
+ btc->fwinfo.rpt_en_map);
+ seq_puts(m, " (WL FW report invalid!!)\n");
+ }
+
+ for (i = 0; i < BTC_NCNT_NUM; i++)
+ cnt_sum += dm->cnt_notify[i];
+
+ seq_printf(m,
+ " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ",
+ "[notify_cnt]", cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO],
+ cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]);
+
+ seq_printf(m,
+ "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d\n",
+ cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE],
+ cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK],
+ cnt[BTC_NCNT_WL_STA]);
+
+ seq_printf(m,
+ " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ",
+ "[notify_cnt]", cnt[BTC_NCNT_SCAN_START],
+ cnt[BTC_NCNT_SCAN_FINISH], cnt[BTC_NCNT_SWITCH_BAND],
+ cnt[BTC_NCNT_SPECIAL_PACKET]);
+
+ seq_printf(m,
+ "timer=%d, control=%d, customerize=%d\n",
+ cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL],
+ cnt[BTC_NCNT_CUSTOMERIZE]);
+}
+
+void rtw89_btc_dump_info(struct rtw89_dev *rtwdev, struct seq_file *m)
+{
+ struct rtw89_fw_suit *fw_suit = &rtwdev->fw.normal;
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_cx *cx = &btc->cx;
+ struct rtw89_btc_bt_info *bt = &cx->bt;
+
+ seq_puts(m, "=========================================\n");
+ seq_printf(m, "WL FW / BT FW %d.%d.%d.%d / NA\n",
+ fw_suit->major_ver, fw_suit->minor_ver,
+ fw_suit->sub_ver, fw_suit->sub_idex);
+ seq_printf(m, "manual %d\n", btc->ctrl.manual);
+
+ seq_puts(m, "=========================================\n");
+
+ seq_printf(m, "\n\r %-15s : raw_data[%02x %02x %02x %02x %02x %02x] (type:%s/cnt:%d/same:%d)",
+ "[bt_info]",
+ bt->raw_info[2], bt->raw_info[3],
+ bt->raw_info[4], bt->raw_info[5],
+ bt->raw_info[6], bt->raw_info[7],
+ bt->raw_info[0] == BTC_BTINFO_AUTO ? "auto" : "reply",
+ cx->cnt_bt[BTC_BCNT_INFOUPDATE],
+ cx->cnt_bt[BTC_BCNT_INFOSAME]);
+
+ seq_puts(m, "\n=========================================\n");
+
+ _show_cx_info(rtwdev, m);
+ _show_wl_info(rtwdev, m);
+ _show_bt_info(rtwdev, m);
+ _show_dm_info(rtwdev, m);
+ _show_fw_dm_msg(rtwdev, m);
+ _show_mreg(rtwdev, m);
+ _show_summary(rtwdev, m);
+}
diff --git a/drivers/net/wireless/realtek/rtw89/coex.h b/drivers/net/wireless/realtek/rtw89/coex.h
new file mode 100644
index 000000000000..843f65e2dc4b
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw89/coex.h
@@ -0,0 +1,152 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/* Copyright(c) 2019-2020 Realtek Corporation
+ */
+
+#ifndef __RTW89_COEX_H__
+#define __RTW89_COEX_H__
+
+#include "core.h"
+
+enum btc_mode {
+ BTC_MODE_NORMAL,
+ BTC_MODE_WL,
+ BTC_MODE_BT,
+ BTC_MODE_WLOFF,
+ BTC_MODE_MAX
+};
+
+enum btc_wl_rfk_type {
+ BTC_WRFKT_IQK = 0,
+ BTC_WRFKT_LCK = 1,
+ BTC_WRFKT_DPK = 2,
+ BTC_WRFKT_TXGAPK = 3,
+ BTC_WRFKT_DACK = 4,
+ BTC_WRFKT_RXDCK = 5,
+ BTC_WRFKT_TSSI = 6,
+};
+
+#define NM_EXEC false
+#define FC_EXEC true
+
+#define RTW89_COEX_ACT1_WORK_PERIOD round_jiffies_relative(HZ * 4)
+#define BTC_RFK_PATH_MAP GENMASK(3, 0)
+#define BTC_RFK_PHY_MAP GENMASK(5, 4)
+#define BTC_RFK_BAND_MAP GENMASK(7, 6)
+
+enum btc_wl_rfk_state {
+ BTC_WRFK_STOP = 0,
+ BTC_WRFK_START = 1,
+ BTC_WRFK_ONESHOT_START = 2,
+ BTC_WRFK_ONESHOT_STOP = 3,
+};
+
+enum btc_pri {
+ BTC_PRI_MASK_RX_RESP = 0,
+ BTC_PRI_MASK_TX_RESP,
+ BTC_PRI_MASK_BEACON,
+ BTC_PRI_MASK_RX_CCK,
+ BTC_PRI_MASK_TX_MNGQ,
+ BTC_PRI_MASK_MAX,
+};
+
+enum btc_bt_trs {
+ BTC_BT_SS_GROUP = 0x0,
+ BTC_BT_TX_GROUP = 0x2,
+ BTC_BT_RX_GROUP = 0x3,
+ BTC_BT_MAX_GROUP,
+};
+
+enum btc_ant {
+ BTC_ANT_SHARED = 0,
+ BTC_ANT_DEDICATED,
+ BTC_ANTTYPE_MAX
+};
+
+enum btc_bt_btg {
+ BTC_BT_ALONE = 0,
+ BTC_BT_BTG
+};
+
+enum btc_switch {
+ BTC_SWITCH_INTERNAL = 0,
+ BTC_SWITCH_EXTERNAL
+};
+
+enum btc_pkt_type {
+ PACKET_DHCP,
+ PACKET_ARP,
+ PACKET_EAPOL,
+ PACKET_EAPOL_END,
+ PACKET_MAX
+};
+
+enum btc_bt_mailbox_id {
+ BTC_BTINFO_REPLY = 0x23,
+ BTC_BTINFO_AUTO = 0x27
+};
+
+enum btc_role_state {
+ BTC_ROLE_START,
+ BTC_ROLE_STOP,
+ BTC_ROLE_CHG_TYPE,
+ BTC_ROLE_MSTS_STA_CONN_START,
+ BTC_ROLE_MSTS_STA_CONN_END,
+ BTC_ROLE_MSTS_STA_DIS_CONN,
+ BTC_ROLE_MSTS_AP_START,
+ BTC_ROLE_MSTS_AP_STOP,
+ BTC_ROLE_STATE_UNKNOWN
+};
+
+enum btc_rfctrl {
+ BTC_RFCTRL_WL_OFF,
+ BTC_RFCTRL_WL_ON,
+ BTC_RFCTRL_FW_CTRL,
+ BTC_RFCTRL_MAX
+};
+
+void rtw89_btc_ntfy_poweron(struct rtw89_dev *rtwdev);
+void rtw89_btc_ntfy_poweroff(struct rtw89_dev *rtwdev);
+void rtw89_btc_ntfy_init(struct rtw89_dev *rtwdev, u8 mode);
+void rtw89_btc_ntfy_scan_start(struct rtw89_dev *rtwdev, u8 phy_idx, u8 band);
+void rtw89_btc_ntfy_scan_finish(struct rtw89_dev *rtwdev, u8 phy_idx);
+void rtw89_btc_ntfy_switch_band(struct rtw89_dev *rtwdev, u8 phy_idx, u8 band);
+void rtw89_btc_ntfy_specific_packet(struct rtw89_dev *rtwdev,
+ enum btc_pkt_type pkt_type);
+void rtw89_btc_ntfy_eapol_packet_work(struct work_struct *work);
+void rtw89_btc_ntfy_arp_packet_work(struct work_struct *work);
+void rtw89_btc_ntfy_dhcp_packet_work(struct work_struct *work);
+void rtw89_btc_ntfy_role_info(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,
+ struct rtw89_sta *rtwsta, enum btc_role_state state);
+void rtw89_btc_ntfy_radio_state(struct rtw89_dev *rtwdev, enum btc_rfctrl rf_state);
+void rtw89_btc_ntfy_wl_rfk(struct rtw89_dev *rtwdev, u8 phy_map,
+ enum btc_wl_rfk_type type,
+ enum btc_wl_rfk_state state);
+void rtw89_btc_ntfy_wl_sta_work(struct work_struct *work);
+void rtw89_btc_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb,
+ u32 len, u8 class, u8 func);
+void rtw89_btc_dump_info(struct rtw89_dev *rtwdev, struct seq_file *m);
+void rtw89_coex_act1_work(struct work_struct *work);
+void rtw89_coex_power_on(struct rtw89_dev *rtwdev);
+
+static inline u8 rtw89_btc_phymap(struct rtw89_dev *rtwdev,
+ enum rtw89_phy_idx phy_idx,
+ enum rtw89_rf_path_bit paths)
+{
+ struct rtw89_hal *hal = &rtwdev->hal;
+ u8 phy_map;
+
+ phy_map = FIELD_PREP(BTC_RFK_PATH_MAP, paths) |
+ FIELD_PREP(BTC_RFK_PHY_MAP, BIT(phy_idx)) |
+ FIELD_PREP(BTC_RFK_BAND_MAP, hal->current_band_type);
+
+ return phy_map;
+}
+
+static inline u8 rtw89_btc_path_phymap(struct rtw89_dev *rtwdev,
+ enum rtw89_phy_idx phy_idx,
+ enum rtw89_rf_path path)
+{
+ return rtw89_btc_phymap(rtwdev, phy_idx, BIT(path));
+}
+
+#endif
--
2.25.1

2021-06-18 06:50:13

by Pkshih

[permalink] [raw]
Subject: [PATCH 01/24] rtw89: add CAM files

Three kinds of CAM, security, address and bssid, are implemented in the
chip, and then we control them via H2C commands.

Every vif contains one address and one bssid CAM entries, and both are
allocated while vif is adding. Once any state is changed, driver notices
firmware. For example, add/remove vif, sta assoc/disassoc, and changed
security key.

When connecting to an AP with security, a security CAM entry is allocated
from security CAM pool and filled key values, and then attached to the
address CAM that contains seven keys entries for unicast, group and BIP
keys. Since the functions of these seven keys are predefined by firmware,
we should attach key to proper position depends on security entry mode and
key type.

Signed-off-by: Ping-Ke Shih <[email protected]>
---
drivers/net/wireless/realtek/rtw89/cam.c | 700 +++++++++++++++++++++++
drivers/net/wireless/realtek/rtw89/cam.h | 165 ++++++
2 files changed, 865 insertions(+)
create mode 100644 drivers/net/wireless/realtek/rtw89/cam.c
create mode 100644 drivers/net/wireless/realtek/rtw89/cam.h

diff --git a/drivers/net/wireless/realtek/rtw89/cam.c b/drivers/net/wireless/realtek/rtw89/cam.c
new file mode 100644
index 000000000000..fac5b6e99089
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw89/cam.c
@@ -0,0 +1,700 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Copyright(c) 2019-2020 Realtek Corporation
+ */
+
+#include "cam.h"
+#include "debug.h"
+#include "fw.h"
+#include "mac.h"
+
+static struct sk_buff *
+rtw89_cam_get_sec_key_cmd(struct rtw89_dev *rtwdev,
+ struct rtw89_sec_cam_entry *sec_cam,
+ bool ext_key)
+{
+ struct sk_buff *skb;
+ u32 cmd_len = H2C_SEC_CAM_LEN;
+ u32 key32[4];
+ u8 *cmd;
+ int i, j;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(cmd_len);
+ if (!skb)
+ return NULL;
+
+ skb_put_zero(skb, cmd_len);
+
+ for (i = 0; i < 4; i++) {
+ j = i * 4;
+ j += ext_key ? 16 : 0;
+ key32[i] = FIELD_PREP(GENMASK(7, 0), sec_cam->key[j + 0]) |
+ FIELD_PREP(GENMASK(15, 8), sec_cam->key[j + 1]) |
+ FIELD_PREP(GENMASK(23, 16), sec_cam->key[j + 2]) |
+ FIELD_PREP(GENMASK(31, 24), sec_cam->key[j + 3]);
+ }
+
+ cmd = skb->data;
+ RTW89_SET_FWCMD_SEC_IDX(cmd, sec_cam->sec_cam_idx + (ext_key ? 1 : 0));
+ RTW89_SET_FWCMD_SEC_OFFSET(cmd, sec_cam->offset);
+ RTW89_SET_FWCMD_SEC_LEN(cmd, sec_cam->len);
+ RTW89_SET_FWCMD_SEC_TYPE(cmd, sec_cam->type);
+ RTW89_SET_FWCMD_SEC_EXT_KEY(cmd, ext_key);
+ RTW89_SET_FWCMD_SEC_SPP_MODE(cmd, sec_cam->spp_mode);
+ RTW89_SET_FWCMD_SEC_KEY0(cmd, key32[0]);
+ RTW89_SET_FWCMD_SEC_KEY1(cmd, key32[1]);
+ RTW89_SET_FWCMD_SEC_KEY2(cmd, key32[2]);
+ RTW89_SET_FWCMD_SEC_KEY3(cmd, key32[3]);
+
+ return skb;
+}
+
+static int rtw89_cam_send_sec_key_cmd(struct rtw89_dev *rtwdev,
+ struct rtw89_sec_cam_entry *sec_cam)
+{
+ struct sk_buff *skb, *ext_skb;
+ int ret;
+
+ skb = rtw89_cam_get_sec_key_cmd(rtwdev, sec_cam, false);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to get sec key command\n");
+ return -ENOMEM;
+ }
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb,
+ FWCMD_TYPE_H2C,
+ H2C_CAT_MAC,
+ H2C_CL_MAC_SEC_CAM,
+ H2C_FUNC_MAC_SEC_UPD, 1, 0,
+ H2C_SEC_CAM_LEN);
+ ret = rtw89_h2c_tx(rtwdev, skb, false);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to send sec key h2c: %d\n", ret);
+ dev_kfree_skb(skb);
+ return ret;
+ }
+
+ if (!sec_cam->ext_key)
+ return 0;
+
+ ext_skb = rtw89_cam_get_sec_key_cmd(rtwdev, sec_cam, true);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to get ext sec key command\n");
+ return -ENOMEM;
+ }
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, ext_skb,
+ FWCMD_TYPE_H2C,
+ H2C_CAT_MAC,
+ H2C_CL_MAC_SEC_CAM,
+ H2C_FUNC_MAC_SEC_UPD,
+ 1, 0, H2C_SEC_CAM_LEN);
+ ret = rtw89_h2c_tx(rtwdev, ext_skb, false);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to send ext sec key h2c: %d\n", ret);
+ dev_kfree_skb(ext_skb);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rtw89_cam_get_avail_sec_cam(struct rtw89_dev *rtwdev,
+ u8 *sec_cam_idx, bool ext_key)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_cam_info *cam_info = &rtwdev->cam_info;
+ u8 sec_cam_num = chip->scam_num;
+ u8 idx = 0;
+
+ if (!ext_key) {
+ idx = find_first_zero_bit(cam_info->sec_cam_map, sec_cam_num);
+ if (idx >= sec_cam_num)
+ return -EBUSY;
+
+ set_bit(idx, cam_info->sec_cam_map);
+ *sec_cam_idx = idx;
+
+ return 0;
+ }
+
+again:
+ idx = find_next_zero_bit(cam_info->sec_cam_map, sec_cam_num, idx);
+ if (idx >= sec_cam_num - 1)
+ return -EBUSY;
+ /* ext keys need two cam entries for 256-bit key */
+ if (test_bit(idx + 1, cam_info->sec_cam_map)) {
+ idx++;
+ goto again;
+ }
+
+ set_bit(idx, cam_info->sec_cam_map);
+ set_bit(idx + 1, cam_info->sec_cam_map);
+ *sec_cam_idx = idx;
+
+ return 0;
+}
+
+static int rtw89_cam_get_addr_cam_key_idx(struct rtw89_addr_cam_entry *addr_cam,
+ struct rtw89_sec_cam_entry *sec_cam,
+ struct ieee80211_key_conf *key,
+ u8 *key_idx)
+{
+ u8 idx;
+
+ /* RTW89_ADDR_CAM_SEC_NONE : not enabled
+ * RTW89_ADDR_CAM_SEC_ALL_UNI : 0 - 6 unicast
+ * RTW89_ADDR_CAM_SEC_NORMAL : 0 - 1 unicast, 2 - 4 group, 5 - 6 BIP
+ * RTW89_ADDR_CAM_SEC_4GROUP : 0 - 1 unicast, 2 - 5 group, 6 BIP
+ */
+ switch (addr_cam->sec_ent_mode) {
+ case RTW89_ADDR_CAM_SEC_NONE:
+ return -EINVAL;
+ case RTW89_ADDR_CAM_SEC_ALL_UNI:
+ if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
+ return -EINVAL;
+ idx = find_first_zero_bit(addr_cam->sec_cam_map,
+ RTW89_SEC_CAM_IN_ADDR_CAM);
+ if (idx >= RTW89_SEC_CAM_IN_ADDR_CAM)
+ return -EBUSY;
+ *key_idx = idx;
+ break;
+ case RTW89_ADDR_CAM_SEC_NORMAL:
+ if (sec_cam->type == RTW89_SEC_KEY_TYPE_BIP_CCMP128) {
+ idx = find_next_zero_bit(addr_cam->sec_cam_map,
+ RTW89_SEC_CAM_IN_ADDR_CAM, 5);
+ if (idx > 6)
+ return -EBUSY;
+ *key_idx = idx;
+ break;
+ }
+
+ if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) {
+ idx = find_next_zero_bit(addr_cam->sec_cam_map,
+ RTW89_SEC_CAM_IN_ADDR_CAM, 0);
+ if (idx > 1)
+ return -EBUSY;
+ *key_idx = idx;
+ break;
+ }
+
+ /* Group keys */
+ idx = find_next_zero_bit(addr_cam->sec_cam_map,
+ RTW89_SEC_CAM_IN_ADDR_CAM, 2);
+ if (idx > 4)
+ return -EBUSY;
+ *key_idx = idx;
+ break;
+ case RTW89_ADDR_CAM_SEC_4GROUP:
+ if (sec_cam->type == RTW89_SEC_KEY_TYPE_BIP_CCMP128) {
+ if (test_bit(6, addr_cam->sec_cam_map))
+ return -EINVAL;
+ *key_idx = 6;
+ break;
+ }
+
+ if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) {
+ idx = find_next_zero_bit(addr_cam->sec_cam_map,
+ RTW89_SEC_CAM_IN_ADDR_CAM, 0);
+ if (idx > 1)
+ return -EBUSY;
+ *key_idx = idx;
+ break;
+ }
+
+ /* Group keys */
+ idx = find_next_zero_bit(addr_cam->sec_cam_map,
+ RTW89_SEC_CAM_IN_ADDR_CAM, 2);
+ if (idx > 5)
+ return -EBUSY;
+ *key_idx = idx;
+ break;
+ }
+
+ return 0;
+}
+
+static int rtw89_cam_attach_sec_cam(struct rtw89_dev *rtwdev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ struct rtw89_sec_cam_entry *sec_cam)
+{
+ struct rtw89_vif *rtwvif;
+ struct rtw89_addr_cam_entry *addr_cam;
+ u8 key_idx = 0;
+ int ret;
+
+ if (!vif) {
+ rtw89_err(rtwdev, "No iface for adding sec cam\n");
+ return -EINVAL;
+ }
+
+ rtwvif = (struct rtw89_vif *)vif->drv_priv;
+ addr_cam = &rtwvif->addr_cam;
+ ret = rtw89_cam_get_addr_cam_key_idx(addr_cam, sec_cam, key, &key_idx);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to get addr cam key idx %d, %d\n",
+ addr_cam->sec_ent_mode, sec_cam->type);
+ return ret;
+ }
+
+ key->hw_key_idx = key_idx;
+ addr_cam->sec_ent_keyid[key_idx] = key->keyidx;
+ addr_cam->sec_ent[key_idx] = sec_cam->sec_cam_idx;
+ addr_cam->sec_entries[key_idx] = sec_cam;
+ set_bit(key_idx, addr_cam->sec_cam_map);
+ ret = rtw89_fw_h2c_cam(rtwdev, rtwvif);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to update addr cam sec entry: %d\n",
+ ret);
+ clear_bit(key_idx, addr_cam->sec_cam_map);
+ addr_cam->sec_entries[key_idx] = NULL;
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rtw89_cam_sec_key_install(struct rtw89_dev *rtwdev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ u8 hw_key_type, bool ext_key)
+{
+ struct rtw89_sec_cam_entry *sec_cam = NULL;
+ struct rtw89_cam_info *cam_info = &rtwdev->cam_info;
+ u8 sec_cam_idx;
+ int ret;
+
+ /* maximum key length 256-bit */
+ if (key->keylen > 32) {
+ rtw89_err(rtwdev, "invalid sec key length %d\n", key->keylen);
+ return -EINVAL;
+ }
+
+ ret = rtw89_cam_get_avail_sec_cam(rtwdev, &sec_cam_idx, ext_key);
+ if (ret) {
+ rtw89_warn(rtwdev, "no available sec cam: %d ext: %d\n",
+ ret, ext_key);
+ return ret;
+ }
+
+ sec_cam = kzalloc(sizeof(*sec_cam), GFP_KERNEL);
+ if (!sec_cam) {
+ ret = -ENOMEM;
+ goto err_release_cam;
+ }
+
+ sec_cam->sec_cam_idx = sec_cam_idx;
+ sec_cam->type = hw_key_type;
+ sec_cam->len = RTW89_SEC_CAM_LEN;
+ sec_cam->ext_key = ext_key;
+ memcpy(sec_cam->key, key->key, key->keylen);
+ ret = rtw89_cam_send_sec_key_cmd(rtwdev, sec_cam);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to send sec key cmd: %d\n", ret);
+ goto err_release_cam;
+ }
+
+ /* associate with addr cam */
+ ret = rtw89_cam_attach_sec_cam(rtwdev, vif, sta, key, sec_cam);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to attach sec cam: %d\n", ret);
+ goto err_release_cam;
+ }
+
+ return 0;
+
+err_release_cam:
+ kfree(sec_cam);
+ clear_bit(sec_cam_idx, cam_info->sec_cam_map);
+ if (ext_key)
+ clear_bit(sec_cam_idx + 1, cam_info->sec_cam_map);
+
+ return ret;
+}
+
+int rtw89_cam_sec_key_add(struct rtw89_dev *rtwdev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ u8 hw_key_type;
+ bool ext_key = false;
+ int ret;
+
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ hw_key_type = RTW89_SEC_KEY_TYPE_WEP40;
+ break;
+ case WLAN_CIPHER_SUITE_WEP104:
+ hw_key_type = RTW89_SEC_KEY_TYPE_WEP104;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ hw_key_type = RTW89_SEC_KEY_TYPE_CCMP128;
+ key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ hw_key_type = RTW89_SEC_KEY_TYPE_CCMP256;
+ key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
+ ext_key = true;
+ break;
+ case WLAN_CIPHER_SUITE_GCMP:
+ hw_key_type = RTW89_SEC_KEY_TYPE_GCMP128;
+ key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
+ break;
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ hw_key_type = RTW89_SEC_KEY_TYPE_GCMP256;
+ key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
+ ext_key = true;
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+ case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+ /* suppress error messages */
+ return -EOPNOTSUPP;
+ default:
+ return -ENOTSUPP;
+ }
+
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+
+ ret = rtw89_cam_sec_key_install(rtwdev, vif, sta, key, hw_key_type,
+ ext_key);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to install key type %d ext %d: %d\n",
+ hw_key_type, ext_key, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+int rtw89_cam_sec_key_del(struct rtw89_dev *rtwdev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ bool inform_fw)
+{
+ struct rtw89_cam_info *cam_info = &rtwdev->cam_info;
+ struct rtw89_vif *rtwvif;
+ struct rtw89_addr_cam_entry *addr_cam;
+ struct rtw89_sec_cam_entry *sec_cam;
+ u8 key_idx = key->hw_key_idx;
+ u8 sec_cam_idx;
+ int ret = 0;
+
+ if (!vif) {
+ rtw89_err(rtwdev, "No iface for deleting sec cam\n");
+ return -EINVAL;
+ }
+
+ rtwvif = (struct rtw89_vif *)vif->drv_priv;
+ addr_cam = &rtwvif->addr_cam;
+ sec_cam = addr_cam->sec_entries[key_idx];
+
+ /* detach sec cam from addr cam */
+ clear_bit(key_idx, addr_cam->sec_cam_map);
+ addr_cam->sec_entries[key_idx] = NULL;
+ if (inform_fw) {
+ ret = rtw89_fw_h2c_cam(rtwdev, rtwvif);
+ if (ret)
+ rtw89_err(rtwdev, "failed to update cam del key: %d\n", ret);
+ }
+
+ /* clear valid bit in addr cam will disable sec cam,
+ * so we don't need to send H2C command again
+ */
+ sec_cam_idx = sec_cam->sec_cam_idx;
+ clear_bit(sec_cam_idx, cam_info->sec_cam_map);
+ if (sec_cam->ext_key)
+ clear_bit(sec_cam_idx + 1, cam_info->sec_cam_map);
+
+ kfree(sec_cam);
+
+ return ret;
+}
+
+static void rtw89_cam_reset_key_iter(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ void *data)
+{
+ struct rtw89_dev *rtwdev = (struct rtw89_dev *)data;
+ struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
+
+ rtw89_cam_sec_key_del(rtwdev, vif, sta, key, false);
+ rtw89_cam_deinit(rtwdev, rtwvif);
+}
+
+void rtw89_cam_deinit(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
+{
+ struct rtw89_cam_info *cam_info = &rtwdev->cam_info;
+ struct rtw89_addr_cam_entry *addr_cam = &rtwvif->addr_cam;
+ struct rtw89_bssid_cam_entry *bssid_cam = &rtwvif->bssid_cam;
+
+ addr_cam->valid = false;
+ bssid_cam->valid = false;
+ clear_bit(addr_cam->addr_cam_idx, cam_info->addr_cam_map);
+ clear_bit(bssid_cam->bssid_cam_idx, cam_info->bssid_cam_map);
+}
+
+void rtw89_cam_reset_keys(struct rtw89_dev *rtwdev)
+{
+ rcu_read_lock();
+ ieee80211_iter_keys_rcu(rtwdev->hw, NULL, rtw89_cam_reset_key_iter, rtwdev);
+ rcu_read_unlock();
+}
+
+static int rtw89_cam_get_avail_addr_cam(struct rtw89_dev *rtwdev,
+ u8 *addr_cam_idx)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_cam_info *cam_info = &rtwdev->cam_info;
+ u8 addr_cam_num = chip->acam_num;
+ u8 idx;
+
+ idx = find_first_zero_bit(cam_info->addr_cam_map, addr_cam_num);
+ if (idx >= addr_cam_num)
+ return -EBUSY;
+
+ set_bit(idx, cam_info->addr_cam_map);
+ *addr_cam_idx = idx;
+
+ return 0;
+}
+
+static int rtw89_cam_init_addr_cam(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif)
+{
+ struct rtw89_addr_cam_entry *addr_cam = &rtwvif->addr_cam;
+ u8 addr_cam_idx;
+ int i;
+ int ret;
+
+ ret = rtw89_cam_get_avail_addr_cam(rtwdev, &addr_cam_idx);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to get available addr cam\n");
+ return ret;
+ }
+
+ addr_cam->addr_cam_idx = addr_cam_idx;
+ addr_cam->len = ADDR_CAM_ENT_SIZE;
+ addr_cam->offset = 0;
+ addr_cam->valid = true;
+ addr_cam->addr_mask = 0;
+ addr_cam->mask_sel = RTW89_NO_MSK;
+ bitmap_zero(addr_cam->sec_cam_map, RTW89_SEC_CAM_IN_ADDR_CAM);
+ ether_addr_copy(addr_cam->sma, rtwvif->mac_addr);
+
+ for (i = 0; i < RTW89_SEC_CAM_IN_ADDR_CAM; i++) {
+ addr_cam->sec_ent_keyid[i] = 0;
+ addr_cam->sec_ent[i] = 0;
+ }
+
+ return 0;
+}
+
+static int rtw89_cam_get_avail_bssid_cam(struct rtw89_dev *rtwdev,
+ u8 *bssid_cam_idx)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_cam_info *cam_info = &rtwdev->cam_info;
+ u8 bssid_cam_num = chip->bcam_num;
+ u8 idx;
+
+ idx = find_first_zero_bit(cam_info->bssid_cam_map, bssid_cam_num);
+ if (idx >= bssid_cam_num)
+ return -EBUSY;
+
+ set_bit(idx, cam_info->bssid_cam_map);
+ *bssid_cam_idx = idx;
+
+ return 0;
+}
+
+static int rtw89_cam_init_bssid_cam(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif)
+{
+ struct rtw89_bssid_cam_entry *bssid_cam = &rtwvif->bssid_cam;
+ u8 bssid_cam_idx;
+ int ret;
+
+ ret = rtw89_cam_get_avail_bssid_cam(rtwdev, &bssid_cam_idx);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to get available bssid cam\n");
+ return ret;
+ }
+
+ bssid_cam->bssid_cam_idx = bssid_cam_idx;
+ bssid_cam->phy_idx = rtwvif->phy_idx;
+ bssid_cam->len = BSSID_CAM_ENT_SIZE;
+ bssid_cam->offset = 0;
+ bssid_cam->valid = true;
+ ether_addr_copy(bssid_cam->bssid, rtwvif->bssid);
+
+ return 0;
+}
+
+void rtw89_cam_bssid_changed(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
+{
+ struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif);
+ struct rtw89_addr_cam_entry *addr_cam = &rtwvif->addr_cam;
+ struct rtw89_bssid_cam_entry *bssid_cam = &rtwvif->bssid_cam;
+
+ if (vif->type == NL80211_IFTYPE_STATION)
+ ether_addr_copy(addr_cam->tma, rtwvif->bssid);
+ ether_addr_copy(bssid_cam->bssid, rtwvif->bssid);
+}
+
+int rtw89_cam_init(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
+{
+ struct rtw89_addr_cam_entry *addr_cam = &rtwvif->addr_cam;
+ struct rtw89_bssid_cam_entry *bssid_cam = &rtwvif->bssid_cam;
+ int ret;
+
+ ret = rtw89_cam_init_addr_cam(rtwdev, rtwvif);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to init addr cam\n");
+ return ret;
+ }
+
+ ret = rtw89_cam_init_bssid_cam(rtwdev, rtwvif);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to init bssid cam\n");
+ return ret;
+ }
+
+ /* associate addr cam with bssid cam */
+ addr_cam->bssid_cam_idx = bssid_cam->bssid_cam_idx;
+
+ return 0;
+}
+
+int rtw89_cam_fill_bssid_cam_info(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif, u8 *cmd)
+{
+ struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif);
+ struct rtw89_bssid_cam_entry *bssid_cam = &rtwvif->bssid_cam;
+ u8 bss_color = vif->bss_conf.he_bss_color.color;
+
+ FWCMD_SET_ADDR_BSSID_IDX(cmd, bssid_cam->bssid_cam_idx);
+ FWCMD_SET_ADDR_BSSID_OFFSET(cmd, bssid_cam->offset);
+ FWCMD_SET_ADDR_BSSID_LEN(cmd, bssid_cam->len);
+ FWCMD_SET_ADDR_BSSID_VALID(cmd, bssid_cam->valid);
+ FWCMD_SET_ADDR_BSSID_BB_SEL(cmd, bssid_cam->phy_idx);
+ FWCMD_SET_ADDR_BSSID_BSS_COLOR(cmd, bss_color);
+
+ FWCMD_SET_ADDR_BSSID_BSSID0(cmd, bssid_cam->bssid[0]);
+ FWCMD_SET_ADDR_BSSID_BSSID1(cmd, bssid_cam->bssid[1]);
+ FWCMD_SET_ADDR_BSSID_BSSID2(cmd, bssid_cam->bssid[2]);
+ FWCMD_SET_ADDR_BSSID_BSSID3(cmd, bssid_cam->bssid[3]);
+ FWCMD_SET_ADDR_BSSID_BSSID4(cmd, bssid_cam->bssid[4]);
+ FWCMD_SET_ADDR_BSSID_BSSID5(cmd, bssid_cam->bssid[5]);
+
+ return 0;
+}
+
+static u8 rtw89_cam_addr_hash(u8 start, u8 *addr)
+{
+ u8 hash = 0;
+ u8 i;
+
+ for (i = start; i < ETH_ALEN; i++)
+ hash ^= addr[i];
+
+ return hash;
+}
+
+void rtw89_cam_fill_addr_cam_info(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif,
+ u8 *cmd)
+{
+ struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif);
+ struct ieee80211_sta *sta;
+ struct rtw89_sta *rtwsta;
+ struct rtw89_addr_cam_entry *addr_cam = &rtwvif->addr_cam;
+ u8 sma_hash, tma_hash, addr_msk_start;
+ u8 sma_start = 0;
+ u8 tma_start = 0;
+
+ if (addr_cam->addr_mask != 0) {
+ addr_msk_start = __ffs(addr_cam->addr_mask);
+ if (addr_cam->mask_sel == RTW89_SMA)
+ sma_start = addr_msk_start;
+ else if (addr_cam->mask_sel == RTW89_TMA)
+ tma_start = addr_msk_start;
+ }
+ sma_hash = rtw89_cam_addr_hash(sma_start, rtwvif->mac_addr);
+ tma_hash = rtw89_cam_addr_hash(tma_start, addr_cam->tma);
+
+ FWCMD_SET_ADDR_IDX(cmd, addr_cam->addr_cam_idx);
+ FWCMD_SET_ADDR_OFFSET(cmd, addr_cam->offset);
+ FWCMD_SET_ADDR_LEN(cmd, addr_cam->len);
+
+ FWCMD_SET_ADDR_VALID(cmd, addr_cam->valid);
+ FWCMD_SET_ADDR_NET_TYPE(cmd, rtwvif->net_type);
+ FWCMD_SET_ADDR_BCN_HIT_COND(cmd, rtwvif->bcn_hit_cond);
+ FWCMD_SET_ADDR_HIT_RULE(cmd, rtwvif->hit_rule);
+ FWCMD_SET_ADDR_BB_SEL(cmd, rtwvif->phy_idx);
+ FWCMD_SET_ADDR_ADDR_MASK(cmd, addr_cam->addr_mask);
+ FWCMD_SET_ADDR_MASK_SEL(cmd, addr_cam->mask_sel);
+ FWCMD_SET_ADDR_SMA_HASH(cmd, sma_hash);
+ FWCMD_SET_ADDR_TMA_HASH(cmd, tma_hash);
+
+ FWCMD_SET_ADDR_BSSID_CAM_IDX(cmd, addr_cam->bssid_cam_idx);
+
+ FWCMD_SET_ADDR_SMA0(cmd, rtwvif->mac_addr[0]);
+ FWCMD_SET_ADDR_SMA1(cmd, rtwvif->mac_addr[1]);
+ FWCMD_SET_ADDR_SMA2(cmd, rtwvif->mac_addr[2]);
+ FWCMD_SET_ADDR_SMA3(cmd, rtwvif->mac_addr[3]);
+ FWCMD_SET_ADDR_SMA4(cmd, rtwvif->mac_addr[4]);
+ FWCMD_SET_ADDR_SMA5(cmd, rtwvif->mac_addr[5]);
+
+ FWCMD_SET_ADDR_TMA0(cmd, addr_cam->tma[0]);
+ FWCMD_SET_ADDR_TMA1(cmd, addr_cam->tma[1]);
+ FWCMD_SET_ADDR_TMA2(cmd, addr_cam->tma[2]);
+ FWCMD_SET_ADDR_TMA3(cmd, addr_cam->tma[3]);
+ FWCMD_SET_ADDR_TMA4(cmd, addr_cam->tma[4]);
+ FWCMD_SET_ADDR_TMA5(cmd, addr_cam->tma[5]);
+
+ FWCMD_SET_ADDR_PORT_INT(cmd, rtwvif->port);
+ FWCMD_SET_ADDR_TSF_SYNC(cmd, rtwvif->port);
+ FWCMD_SET_ADDR_TF_TRS(cmd, rtwvif->trigger);
+ FWCMD_SET_ADDR_LSIG_TXOP(cmd, rtwvif->lsig_txop);
+ FWCMD_SET_ADDR_TGT_IND(cmd, rtwvif->tgt_ind);
+ FWCMD_SET_ADDR_FRM_TGT_IND(cmd, rtwvif->frm_tgt_ind);
+
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ sta = rtwvif->mgd.ap;
+ if (sta) {
+ rtwsta = (struct rtw89_sta *)sta->drv_priv;
+ FWCMD_SET_ADDR_MACID(cmd, rtwsta->mac_id);
+ FWCMD_SET_ADDR_AID12(cmd, vif->bss_conf.aid & 0xfff);
+ }
+ }
+ FWCMD_SET_ADDR_WOL_PATTERN(cmd, rtwvif->wowlan_pattern);
+ FWCMD_SET_ADDR_WOL_UC(cmd, rtwvif->wowlan_uc);
+ FWCMD_SET_ADDR_WOL_MAGIC(cmd, rtwvif->wowlan_magic);
+ FWCMD_SET_ADDR_WAPI(cmd, addr_cam->wapi);
+ FWCMD_SET_ADDR_SEC_ENT_MODE(cmd, addr_cam->sec_ent_mode);
+ FWCMD_SET_ADDR_SEC_ENT0_KEYID(cmd, addr_cam->sec_ent_keyid[0]);
+ FWCMD_SET_ADDR_SEC_ENT1_KEYID(cmd, addr_cam->sec_ent_keyid[1]);
+ FWCMD_SET_ADDR_SEC_ENT2_KEYID(cmd, addr_cam->sec_ent_keyid[2]);
+ FWCMD_SET_ADDR_SEC_ENT3_KEYID(cmd, addr_cam->sec_ent_keyid[3]);
+ FWCMD_SET_ADDR_SEC_ENT4_KEYID(cmd, addr_cam->sec_ent_keyid[4]);
+ FWCMD_SET_ADDR_SEC_ENT5_KEYID(cmd, addr_cam->sec_ent_keyid[5]);
+ FWCMD_SET_ADDR_SEC_ENT6_KEYID(cmd, addr_cam->sec_ent_keyid[6]);
+
+ FWCMD_SET_ADDR_SEC_ENT_VALID(cmd, addr_cam->sec_cam_map[0] & 0xff);
+ FWCMD_SET_ADDR_SEC_ENT0(cmd, addr_cam->sec_ent[0]);
+ FWCMD_SET_ADDR_SEC_ENT1(cmd, addr_cam->sec_ent[1]);
+ FWCMD_SET_ADDR_SEC_ENT2(cmd, addr_cam->sec_ent[2]);
+ FWCMD_SET_ADDR_SEC_ENT3(cmd, addr_cam->sec_ent[3]);
+ FWCMD_SET_ADDR_SEC_ENT4(cmd, addr_cam->sec_ent[4]);
+ FWCMD_SET_ADDR_SEC_ENT5(cmd, addr_cam->sec_ent[5]);
+ FWCMD_SET_ADDR_SEC_ENT6(cmd, addr_cam->sec_ent[6]);
+}
diff --git a/drivers/net/wireless/realtek/rtw89/cam.h b/drivers/net/wireless/realtek/rtw89/cam.h
new file mode 100644
index 000000000000..90a20a5375c6
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw89/cam.h
@@ -0,0 +1,165 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/* Copyright(c) 2019-2020 Realtek Corporation
+ */
+
+#ifndef __RTW89_CAM_H__
+#define __RTW89_CAM_H__
+
+#include "core.h"
+
+#define RTW89_SEC_CAM_LEN 20
+
+#define FWCMD_SET_ADDR_IDX(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 1, value, GENMASK(7, 0))
+#define FWCMD_SET_ADDR_OFFSET(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 1, value, GENMASK(15, 8))
+#define FWCMD_SET_ADDR_LEN(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 1, value, GENMASK(23, 16))
+#define FWCMD_SET_ADDR_VALID(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 2, value, BIT(0))
+#define FWCMD_SET_ADDR_NET_TYPE(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 2, value, GENMASK(2, 1))
+#define FWCMD_SET_ADDR_BCN_HIT_COND(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 2, value, GENMASK(4, 3))
+#define FWCMD_SET_ADDR_HIT_RULE(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 2, value, GENMASK(6, 5))
+#define FWCMD_SET_ADDR_BB_SEL(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 2, value, BIT(7))
+#define FWCMD_SET_ADDR_ADDR_MASK(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 2, value, GENMASK(13, 8))
+#define FWCMD_SET_ADDR_MASK_SEL(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 2, value, GENMASK(15, 14))
+#define FWCMD_SET_ADDR_SMA_HASH(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 2, value, GENMASK(23, 16))
+#define FWCMD_SET_ADDR_TMA_HASH(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 2, value, GENMASK(31, 24))
+#define FWCMD_SET_ADDR_BSSID_CAM_IDX(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 3, value, GENMASK(5, 0))
+#define FWCMD_SET_ADDR_SMA0(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 4, value, GENMASK(7, 0))
+#define FWCMD_SET_ADDR_SMA1(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 4, value, GENMASK(15, 8))
+#define FWCMD_SET_ADDR_SMA2(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 4, value, GENMASK(23, 16))
+#define FWCMD_SET_ADDR_SMA3(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 4, value, GENMASK(31, 24))
+#define FWCMD_SET_ADDR_SMA4(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 5, value, GENMASK(7, 0))
+#define FWCMD_SET_ADDR_SMA5(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 5, value, GENMASK(15, 8))
+#define FWCMD_SET_ADDR_TMA0(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 5, value, GENMASK(23, 16))
+#define FWCMD_SET_ADDR_TMA1(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 5, value, GENMASK(31, 24))
+#define FWCMD_SET_ADDR_TMA2(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 6, value, GENMASK(7, 0))
+#define FWCMD_SET_ADDR_TMA3(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 6, value, GENMASK(15, 8))
+#define FWCMD_SET_ADDR_TMA4(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 6, value, GENMASK(23, 16))
+#define FWCMD_SET_ADDR_TMA5(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 6, value, GENMASK(31, 24))
+#define FWCMD_SET_ADDR_MACID(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 8, value, GENMASK(7, 0))
+#define FWCMD_SET_ADDR_PORT_INT(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 8, value, GENMASK(10, 8))
+#define FWCMD_SET_ADDR_TSF_SYNC(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 8, value, GENMASK(13, 11))
+#define FWCMD_SET_ADDR_TF_TRS(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 8, value, BIT(14))
+#define FWCMD_SET_ADDR_LSIG_TXOP(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 8, value, BIT(15))
+#define FWCMD_SET_ADDR_TGT_IND(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 8, value, GENMASK(26, 24))
+#define FWCMD_SET_ADDR_FRM_TGT_IND(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 8, value, GENMASK(29, 27))
+#define FWCMD_SET_ADDR_AID12(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(11, 0))
+#define FWCMD_SET_ADDR_AID12_0(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(7, 0))
+#define FWCMD_SET_ADDR_AID12_1(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(11, 8))
+#define FWCMD_SET_ADDR_WOL_PATTERN(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 9, value, BIT(12))
+#define FWCMD_SET_ADDR_WOL_UC(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 9, value, BIT(13))
+#define FWCMD_SET_ADDR_WOL_MAGIC(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 9, value, BIT(14))
+#define FWCMD_SET_ADDR_WAPI(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 9, value, BIT(15))
+#define FWCMD_SET_ADDR_SEC_ENT_MODE(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(17, 16))
+#define FWCMD_SET_ADDR_SEC_ENT0_KEYID(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(19, 18))
+#define FWCMD_SET_ADDR_SEC_ENT1_KEYID(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(21, 20))
+#define FWCMD_SET_ADDR_SEC_ENT2_KEYID(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(23, 22))
+#define FWCMD_SET_ADDR_SEC_ENT3_KEYID(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(25, 24))
+#define FWCMD_SET_ADDR_SEC_ENT4_KEYID(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(27, 26))
+#define FWCMD_SET_ADDR_SEC_ENT5_KEYID(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(29, 28))
+#define FWCMD_SET_ADDR_SEC_ENT6_KEYID(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(31, 30))
+#define FWCMD_SET_ADDR_SEC_ENT_VALID(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 10, value, GENMASK(7, 0))
+#define FWCMD_SET_ADDR_SEC_ENT0(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 10, value, GENMASK(15, 8))
+#define FWCMD_SET_ADDR_SEC_ENT1(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 10, value, GENMASK(23, 16))
+#define FWCMD_SET_ADDR_SEC_ENT2(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 10, value, GENMASK(31, 24))
+#define FWCMD_SET_ADDR_SEC_ENT3(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 11, value, GENMASK(7, 0))
+#define FWCMD_SET_ADDR_SEC_ENT4(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 11, value, GENMASK(15, 8))
+#define FWCMD_SET_ADDR_SEC_ENT5(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 11, value, GENMASK(23, 16))
+#define FWCMD_SET_ADDR_SEC_ENT6(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 11, value, GENMASK(31, 24))
+#define FWCMD_SET_ADDR_BSSID_IDX(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 12, value, GENMASK(7, 0))
+#define FWCMD_SET_ADDR_BSSID_OFFSET(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 12, value, GENMASK(15, 8))
+#define FWCMD_SET_ADDR_BSSID_LEN(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 12, value, GENMASK(23, 16))
+#define FWCMD_SET_ADDR_BSSID_VALID(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 13, value, BIT(0))
+#define FWCMD_SET_ADDR_BSSID_BB_SEL(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 13, value, BIT(1))
+#define FWCMD_SET_ADDR_BSSID_BSS_COLOR(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 13, value, GENMASK(13, 8))
+#define FWCMD_SET_ADDR_BSSID_BSSID0(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 13, value, GENMASK(23, 16))
+#define FWCMD_SET_ADDR_BSSID_BSSID1(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 13, value, GENMASK(31, 24))
+#define FWCMD_SET_ADDR_BSSID_BSSID2(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 14, value, GENMASK(7, 0))
+#define FWCMD_SET_ADDR_BSSID_BSSID3(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 14, value, GENMASK(15, 8))
+#define FWCMD_SET_ADDR_BSSID_BSSID4(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 14, value, GENMASK(23, 16))
+#define FWCMD_SET_ADDR_BSSID_BSSID5(cmd, value) \
+ le32p_replace_bits((__le32 *)(cmd) + 14, value, GENMASK(31, 24))
+
+int rtw89_cam_init(struct rtw89_dev *rtwdev, struct rtw89_vif *vif);
+void rtw89_cam_deinit(struct rtw89_dev *rtwdev, struct rtw89_vif *vif);
+void rtw89_cam_fill_addr_cam_info(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *vif, u8 *cmd);
+int rtw89_cam_fill_bssid_cam_info(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *vif, u8 *cmd);
+int rtw89_cam_sec_key_add(struct rtw89_dev *rtwdev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key);
+int rtw89_cam_sec_key_del(struct rtw89_dev *rtwdev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ bool inform_fw);
+void rtw89_cam_bssid_changed(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif);
+void rtw89_cam_reset_keys(struct rtw89_dev *rtwdev);
+#endif
--
2.25.1

2021-06-18 06:50:25

by Pkshih

[permalink] [raw]
Subject: [PATCH 03/24] rtw89: add core and trx files

Implement main flows that contains register/unregister mac80211 hw with
hardware capability, power on/off sequence, STA state actions, and
TX/RX path.

The chip info is read from efuse while probing PCI, and then it can be
used to induce supported channel, band, bitrate, ht/vht/he capability,
and etc. Then, we register hardware with these capabilities.

When network interface is up, driver does power-on sequence to enable MAC,
BB and RF function blocks. Oppositely, do power-off sequence when
interface is going to down.

To maintain STA state, five callbacks are implemented -- add, assoc,
disassoc, disconnect and remove. In which state, driver tells firmware STA
info via H2C.

TX flow:
When a SKB is going to be transmitted, we must know its type first. If
the type is mgmt or fwcmd made by driver, SKB is queued into corresponding
DMA channel and PCI ring. The other type is data frame that is more
complex, because it needs to establish BA session to have better throughput
with AMPDU and AMSDU.
In order to have better PCI DMA efficiency, we don't kick off DMA every
SKB. With wake TX queue, kick off DMA after a bunch of SKBs are written.
To achieve this, we have two HCI ops -- tx_write and tx_kick_off.

BA establishment work:
For data frames, we start to establish BA session if the STA is associated
with APMDU capability and the TID session isn't established, and then the
BA work is used to ask mac80211 to start AMPDU actions. Driver implements
AMPDU action callbacks to know the session is established, so that we can
set AGG_EN bit in TX descriptor to enable AMPDU.

RX flow:
When a RX SKB is delivered from PCI, rtw89_core_rx() process it depneds on
its type -- WIFI, C2H or PPDU. If type is C2H, it's queued into a C2H
queue, and wake a work to handle the C2H packet. If type is WIFI, it's a
normal RX packet. When mgmt or data frame is received, it is queued
into pending RX SKB queue to wait for corresponding PPDU packet (another
RX packet with PPDU type) to fill its rx_status, like RSSI. And, then
indicate this packet to mac80211. When control frame is received, indicate
it to mac80211 immediately.

Track work:
Use track work to monitor PHY status to know the changes of environment,
and then update RA status or do RFK accordingly.

Signed-off-by: Ping-Ke Shih <[email protected]>
---
drivers/net/wireless/realtek/rtw89/core.c | 2359 +++++++++++++++
drivers/net/wireless/realtek/rtw89/core.h | 3336 +++++++++++++++++++++
drivers/net/wireless/realtek/rtw89/txrx.h | 393 +++
drivers/net/wireless/realtek/rtw89/util.c | 37 +
drivers/net/wireless/realtek/rtw89/util.h | 31 +
5 files changed, 6156 insertions(+)
create mode 100644 drivers/net/wireless/realtek/rtw89/core.c
create mode 100644 drivers/net/wireless/realtek/rtw89/core.h
create mode 100644 drivers/net/wireless/realtek/rtw89/txrx.h
create mode 100644 drivers/net/wireless/realtek/rtw89/util.c
create mode 100644 drivers/net/wireless/realtek/rtw89/util.h

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
new file mode 100644
index 000000000000..3bd31e669aea
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -0,0 +1,2359 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Copyright(c) 2019-2020 Realtek Corporation
+ */
+
+#include "coex.h"
+#include "core.h"
+#include "efuse.h"
+#include "fw.h"
+#include "mac.h"
+#include "phy.h"
+#include "ps.h"
+#include "reg.h"
+#include "sar.h"
+#include "ser.h"
+#include "txrx.h"
+#include "util.h"
+
+static bool rtw89_disable_ps_mode;
+module_param_named(disable_ps_mode, rtw89_disable_ps_mode, bool, 0644);
+MODULE_PARM_DESC(disable_ps_mode, "Set Y to disable low power mode");
+
+static struct ieee80211_channel rtw89_channels_2ghz[] = {
+ { .center_freq = 2412, .hw_value = 1, },
+ { .center_freq = 2417, .hw_value = 2, },
+ { .center_freq = 2422, .hw_value = 3, },
+ { .center_freq = 2427, .hw_value = 4, },
+ { .center_freq = 2432, .hw_value = 5, },
+ { .center_freq = 2437, .hw_value = 6, },
+ { .center_freq = 2442, .hw_value = 7, },
+ { .center_freq = 2447, .hw_value = 8, },
+ { .center_freq = 2452, .hw_value = 9, },
+ { .center_freq = 2457, .hw_value = 10, },
+ { .center_freq = 2462, .hw_value = 11, },
+ { .center_freq = 2467, .hw_value = 12, },
+ { .center_freq = 2472, .hw_value = 13, },
+ { .center_freq = 2484, .hw_value = 14, },
+};
+
+static struct ieee80211_channel rtw89_channels_5ghz[] = {
+ {.center_freq = 5180, .hw_value = 36,},
+ {.center_freq = 5200, .hw_value = 40,},
+ {.center_freq = 5220, .hw_value = 44,},
+ {.center_freq = 5240, .hw_value = 48,},
+ {.center_freq = 5260, .hw_value = 52,},
+ {.center_freq = 5280, .hw_value = 56,},
+ {.center_freq = 5300, .hw_value = 60,},
+ {.center_freq = 5320, .hw_value = 64,},
+ {.center_freq = 5500, .hw_value = 100,},
+ {.center_freq = 5520, .hw_value = 104,},
+ {.center_freq = 5540, .hw_value = 108,},
+ {.center_freq = 5560, .hw_value = 112,},
+ {.center_freq = 5580, .hw_value = 116,},
+ {.center_freq = 5600, .hw_value = 120,},
+ {.center_freq = 5620, .hw_value = 124,},
+ {.center_freq = 5640, .hw_value = 128,},
+ {.center_freq = 5660, .hw_value = 132,},
+ {.center_freq = 5680, .hw_value = 136,},
+ {.center_freq = 5700, .hw_value = 140,},
+ {.center_freq = 5720, .hw_value = 144,},
+ {.center_freq = 5745, .hw_value = 149,},
+ {.center_freq = 5765, .hw_value = 153,},
+ {.center_freq = 5785, .hw_value = 157,},
+ {.center_freq = 5805, .hw_value = 161,},
+ {.center_freq = 5825, .hw_value = 165,
+ .flags = IEEE80211_CHAN_NO_HT40MINUS},
+};
+
+static struct ieee80211_rate rtw89_bitrates[] = {
+ { .bitrate = 10, .hw_value = 0x00, },
+ { .bitrate = 20, .hw_value = 0x01, },
+ { .bitrate = 55, .hw_value = 0x02, },
+ { .bitrate = 110, .hw_value = 0x03, },
+ { .bitrate = 60, .hw_value = 0x04, },
+ { .bitrate = 90, .hw_value = 0x05, },
+ { .bitrate = 120, .hw_value = 0x06, },
+ { .bitrate = 180, .hw_value = 0x07, },
+ { .bitrate = 240, .hw_value = 0x08, },
+ { .bitrate = 360, .hw_value = 0x09, },
+ { .bitrate = 480, .hw_value = 0x0a, },
+ { .bitrate = 540, .hw_value = 0x0b, },
+};
+
+u16 rtw89_ra_report_to_bitrate(struct rtw89_dev *rtwdev, u8 rpt_rate)
+{
+ struct ieee80211_rate rate;
+
+ if (unlikely(rpt_rate >= ARRAY_SIZE(rtw89_bitrates))) {
+ rtw89_info(rtwdev, "invalid rpt rate %d\n", rpt_rate);
+ return 0;
+ }
+
+ rate = rtw89_bitrates[rpt_rate];
+
+ return rate.bitrate;
+}
+
+static struct ieee80211_supported_band rtw89_sband_2ghz = {
+ .band = NL80211_BAND_2GHZ,
+ .channels = rtw89_channels_2ghz,
+ .n_channels = ARRAY_SIZE(rtw89_channels_2ghz),
+ .bitrates = rtw89_bitrates,
+ .n_bitrates = ARRAY_SIZE(rtw89_bitrates),
+ .ht_cap = {0},
+ .vht_cap = {0},
+};
+
+static struct ieee80211_supported_band rtw89_sband_5ghz = {
+ .band = NL80211_BAND_5GHZ,
+ .channels = rtw89_channels_5ghz,
+ .n_channels = ARRAY_SIZE(rtw89_channels_5ghz),
+
+ /* 5G has no CCK rates, 1M/2M/5.5M/11M */
+ .bitrates = rtw89_bitrates + 4,
+ .n_bitrates = ARRAY_SIZE(rtw89_bitrates) - 4,
+ .ht_cap = {0},
+ .vht_cap = {0},
+};
+
+static void rtw89_traffic_stats_accu(struct rtw89_dev *rtwdev,
+ struct rtw89_traffic_stats *stats,
+ struct sk_buff *skb, bool tx)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+
+ if (!ieee80211_is_data(hdr->frame_control))
+ return;
+
+ if (is_broadcast_ether_addr(hdr->addr1) ||
+ is_multicast_ether_addr(hdr->addr1))
+ return;
+
+ if (tx) {
+ stats->tx_cnt++;
+ stats->tx_unicast += skb->len;
+ } else {
+ stats->rx_cnt++;
+ stats->rx_unicast += skb->len;
+ }
+}
+
+static void rtw89_get_channel_params(struct cfg80211_chan_def *chandef,
+ struct rtw89_channel_params *chan_param)
+{
+ struct ieee80211_channel *channel = chandef->chan;
+ enum nl80211_chan_width width = chandef->width;
+ u8 *cch_by_bw = chan_param->cch_by_bw;
+ u32 primary_freq, center_freq;
+ u8 center_chan;
+ u8 bandwidth = RTW89_CHANNEL_WIDTH_20;
+ u8 primary_chan_idx = 0;
+ u8 i;
+
+ center_chan = channel->hw_value;
+ primary_freq = channel->center_freq;
+ center_freq = chandef->center_freq1;
+
+ /* assign the center channel used while 20M bw is selected */
+ cch_by_bw[RTW89_CHANNEL_WIDTH_20] = channel->hw_value;
+
+ switch (width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_20:
+ bandwidth = RTW89_CHANNEL_WIDTH_20;
+ primary_chan_idx = RTW89_SC_DONT_CARE;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ bandwidth = RTW89_CHANNEL_WIDTH_40;
+ if (primary_freq > center_freq) {
+ primary_chan_idx = RTW89_SC_20_UPPER;
+ center_chan -= 2;
+ } else {
+ primary_chan_idx = RTW89_SC_20_LOWER;
+ center_chan += 2;
+ }
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ bandwidth = RTW89_CHANNEL_WIDTH_80;
+ if (primary_freq > center_freq) {
+ if (primary_freq - center_freq == 10) {
+ primary_chan_idx = RTW89_SC_20_UPPER;
+ center_chan -= 2;
+ } else {
+ primary_chan_idx = RTW89_SC_20_UPMOST;
+ center_chan -= 6;
+ }
+ /* assign the center channel used
+ * while 40M bw is selected
+ */
+ cch_by_bw[RTW89_CHANNEL_WIDTH_40] = center_chan + 4;
+ } else {
+ if (center_freq - primary_freq == 10) {
+ primary_chan_idx = RTW89_SC_20_LOWER;
+ center_chan += 2;
+ } else {
+ primary_chan_idx = RTW89_SC_20_LOWEST;
+ center_chan += 6;
+ }
+ /* assign the center channel used
+ * while 40M bw is selected
+ */
+ cch_by_bw[RTW89_CHANNEL_WIDTH_40] = center_chan - 4;
+ }
+ break;
+ default:
+ center_chan = 0;
+ break;
+ }
+
+ chan_param->center_chan = center_chan;
+ chan_param->primary_chan = channel->hw_value;
+ chan_param->bandwidth = bandwidth;
+ chan_param->pri_ch_idx = primary_chan_idx;
+
+ /* assign the center channel used while current bw is selected */
+ cch_by_bw[bandwidth] = center_chan;
+
+ for (i = bandwidth + 1; i <= RTW89_MAX_CHANNEL_WIDTH; i++)
+ cch_by_bw[i] = 0;
+}
+
+void rtw89_set_channel(struct rtw89_dev *rtwdev)
+{
+ struct ieee80211_hw *hw = rtwdev->hw;
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_hal *hal = &rtwdev->hal;
+ struct rtw89_channel_params ch_param;
+ struct rtw89_channel_help_params bak;
+ u8 center_chan, bandwidth;
+ u8 band_type;
+ bool band_changed;
+ u8 i;
+
+ rtw89_get_channel_params(&hw->conf.chandef, &ch_param);
+ if (WARN(ch_param.center_chan == 0, "Invalid channel\n"))
+ return;
+
+ center_chan = ch_param.center_chan;
+ bandwidth = ch_param.bandwidth;
+ band_type = center_chan > 14 ? RTW89_BAND_5G : RTW89_BAND_2G;
+ band_changed = hal->current_band_type != band_type ||
+ hal->current_channel == 0;
+
+ hal->current_band_width = bandwidth;
+ hal->current_channel = center_chan;
+ hal->current_primary_channel = ch_param.primary_chan;
+ hal->current_band_type = band_type;
+
+ switch (center_chan) {
+ case 1 ... 14:
+ hal->current_subband = RTW89_CH_2G;
+ break;
+ case 36 ... 64:
+ hal->current_subband = RTW89_CH_5G_BAND_1;
+ break;
+ case 100 ... 144:
+ hal->current_subband = RTW89_CH_5G_BAND_3;
+ break;
+ case 149 ... 177:
+ hal->current_subband = RTW89_CH_5G_BAND_4;
+ break;
+ }
+
+ for (i = RTW89_CHANNEL_WIDTH_20; i <= RTW89_MAX_CHANNEL_WIDTH; i++)
+ hal->cch_by_bw[i] = ch_param.cch_by_bw[i];
+
+ rtw89_chip_set_channel_prepare(rtwdev, &bak);
+
+ chip->ops->set_channel(rtwdev, &ch_param);
+
+ rtw89_chip_set_txpwr(rtwdev);
+
+ rtw89_chip_set_channel_done(rtwdev, &bak);
+
+ if (band_changed) {
+ rtw89_btc_ntfy_switch_band(rtwdev, RTW89_PHY_0, hal->current_band_type);
+ rtw89_chip_rfk_band_changed(rtwdev);
+ }
+}
+
+static enum rtw89_core_tx_type
+rtw89_core_get_tx_type(struct rtw89_dev *rtwdev,
+ struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ __le16 fc = hdr->frame_control;
+
+ if (ieee80211_is_mgmt(fc) || ieee80211_is_nullfunc(fc))
+ return RTW89_CORE_TX_TYPE_MGMT;
+
+ return RTW89_CORE_TX_TYPE_DATA;
+}
+
+static void
+rtw89_core_tx_update_ampdu_info(struct rtw89_dev *rtwdev,
+ struct rtw89_core_tx_request *tx_req, u8 tid)
+{
+ struct ieee80211_sta *sta = tx_req->sta;
+ struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info;
+ struct rtw89_sta *rtwsta;
+ u8 ampdu_num;
+
+ if (!sta) {
+ rtw89_warn(rtwdev, "cannot set ampdu info without sta\n");
+ return;
+ }
+
+ rtwsta = (struct rtw89_sta *)sta->drv_priv;
+
+ ampdu_num = (u8)((rtwsta->ampdu_params[tid].agg_num ?
+ rtwsta->ampdu_params[tid].agg_num :
+ 4 << sta->ht_cap.ampdu_factor) - 1);
+
+ desc_info->agg_en = true;
+ desc_info->ampdu_density = sta->ht_cap.ampdu_density;
+ desc_info->ampdu_num = ampdu_num;
+}
+
+static void
+rtw89_core_tx_update_sec_key(struct rtw89_dev *rtwdev,
+ struct rtw89_core_tx_request *tx_req)
+{
+ struct ieee80211_vif *vif = tx_req->vif;
+ struct ieee80211_tx_info *info;
+ struct ieee80211_key_conf *key;
+ struct rtw89_vif *rtwvif;
+ struct rtw89_addr_cam_entry *addr_cam;
+ struct rtw89_sec_cam_entry *sec_cam;
+ struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info;
+ struct sk_buff *skb = tx_req->skb;
+ u8 sec_type = RTW89_SEC_KEY_TYPE_NONE;
+
+ if (!vif) {
+ rtw89_warn(rtwdev, "cannot set sec key without vif\n");
+ return;
+ }
+
+ rtwvif = (struct rtw89_vif *)vif->drv_priv;
+ addr_cam = &rtwvif->addr_cam;
+
+ info = IEEE80211_SKB_CB(skb);
+ key = info->control.hw_key;
+ sec_cam = addr_cam->sec_entries[key->hw_key_idx];
+ if (!sec_cam) {
+ rtw89_warn(rtwdev, "sec cam entry is empty\n");
+ return;
+ }
+
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ sec_type = RTW89_SEC_KEY_TYPE_WEP40;
+ break;
+ case WLAN_CIPHER_SUITE_WEP104:
+ sec_type = RTW89_SEC_KEY_TYPE_WEP104;
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ sec_type = RTW89_SEC_KEY_TYPE_TKIP;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ sec_type = RTW89_SEC_KEY_TYPE_CCMP128;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ sec_type = RTW89_SEC_KEY_TYPE_CCMP256;
+ break;
+ case WLAN_CIPHER_SUITE_GCMP:
+ sec_type = RTW89_SEC_KEY_TYPE_GCMP128;
+ break;
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ sec_type = RTW89_SEC_KEY_TYPE_GCMP256;
+ break;
+ default:
+ rtw89_warn(rtwdev, "key cipher not supported %d\n", key->cipher);
+ return;
+ }
+
+ desc_info->sec_en = true;
+ desc_info->sec_type = sec_type;
+ desc_info->sec_cam_idx = sec_cam->sec_cam_idx;
+}
+
+static void
+rtw89_core_tx_update_mgmt_info(struct rtw89_dev *rtwdev,
+ struct rtw89_core_tx_request *tx_req)
+{
+ struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info;
+ u8 qsel, ch_dma;
+
+ qsel = RTW89_TX_QSEL_B0_MGMT;
+ ch_dma = rtw89_core_get_ch_dma(rtwdev, qsel);
+
+ desc_info->qsel = RTW89_TX_QSEL_B0_MGMT;
+ desc_info->ch_dma = ch_dma;
+
+ /* fixed data rate for mgmt frames */
+ desc_info->en_wd_info = true;
+ desc_info->use_rate = true;
+ desc_info->dis_data_fb = true;
+ desc_info->data_rate = 0x00;
+}
+
+static void
+rtw89_core_tx_update_h2c_info(struct rtw89_dev *rtwdev,
+ struct rtw89_core_tx_request *tx_req)
+{
+ struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info;
+
+ desc_info->is_bmc = false;
+ desc_info->wd_page = false;
+ desc_info->ch_dma = RTW89_DMA_H2C;
+}
+
+static bool
+__rtw89_core_tx_check_he_qos_htc(struct rtw89_dev *rtwdev,
+ struct rtw89_core_tx_request *tx_req,
+ enum btc_pkt_type pkt_type)
+{
+ struct ieee80211_sta *sta = tx_req->sta;
+ struct sk_buff *skb = tx_req->skb;
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ __le16 fc = hdr->frame_control;
+
+ /* AP IOT issue with EAPoL, ARP and DHCP */
+ if (pkt_type < PACKET_MAX)
+ return false;
+
+ if (!sta || !sta->he_cap.has_he)
+ return false;
+
+ if (!ieee80211_is_data_qos(fc))
+ return false;
+
+ if (skb_headroom(skb) < IEEE80211_HT_CTL_LEN)
+ return false;
+
+ return true;
+}
+
+static void
+__rtw89_core_tx_adjust_he_qos_htc(struct rtw89_dev *rtwdev,
+ struct rtw89_core_tx_request *tx_req)
+{
+ struct sk_buff *skb = tx_req->skb;
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ __le16 fc = hdr->frame_control;
+ void *data;
+ __le32 *htc;
+ u8 *qc;
+ int hdr_len;
+
+ hdr_len = ieee80211_has_a4(fc) ? 32 : 26;
+ data = skb_push(skb, IEEE80211_HT_CTL_LEN);
+ memmove(data, data + IEEE80211_HT_CTL_LEN, hdr_len);
+
+ hdr = data;
+ htc = data + hdr_len;
+ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_ORDER);
+ memset(htc, 0, sizeof(*htc));
+ *htc = le32_encode_bits(RTW89_HTC_VARIANT_HE, RTW89_HTC_MASK_VARIANT) |
+ le32_encode_bits(RTW89_HTC_VARIANT_HE_CID_CAS, RTW89_HTC_MASK_CTL_ID);
+
+ qc = data + hdr_len - IEEE80211_QOS_CTL_LEN;
+ qc[0] |= IEEE80211_QOS_CTL_EOSP;
+}
+
+static void
+rtw89_core_tx_update_he_qos_htc(struct rtw89_dev *rtwdev,
+ struct rtw89_core_tx_request *tx_req,
+ enum btc_pkt_type pkt_type)
+{
+ struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info;
+ struct ieee80211_vif *vif = tx_req->vif;
+ struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
+
+ if (!__rtw89_core_tx_check_he_qos_htc(rtwdev, tx_req, pkt_type))
+ goto desc_bk;
+
+ __rtw89_core_tx_adjust_he_qos_htc(rtwdev, tx_req);
+
+ desc_info->pkt_size += IEEE80211_HT_CTL_LEN;
+ desc_info->a_ctrl_bsr = true;
+
+desc_bk:
+ if (!rtwvif || rtwvif->last_a_ctrl == desc_info->a_ctrl_bsr)
+ return;
+
+ rtwvif->last_a_ctrl = desc_info->a_ctrl_bsr;
+ desc_info->bk = true;
+}
+
+static void
+rtw89_core_tx_update_data_info(struct rtw89_dev *rtwdev,
+ struct rtw89_core_tx_request *tx_req)
+{
+ struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info;
+ struct sk_buff *skb = tx_req->skb;
+ u8 tid, tid_indicate;
+ u8 qsel, ch_dma;
+
+ tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
+ tid_indicate = rtw89_core_get_tid_indicate(rtwdev, tid);
+ qsel = rtw89_core_get_qsel(rtwdev, tid);
+ ch_dma = rtw89_core_get_ch_dma(rtwdev, qsel);
+
+ desc_info->ch_dma = ch_dma;
+ desc_info->tid_indicate = tid_indicate;
+ desc_info->qsel = qsel;
+
+ /* enable wd_info for AMPDU */
+ desc_info->en_wd_info = true;
+
+ if (IEEE80211_SKB_CB(skb)->flags & IEEE80211_TX_CTL_AMPDU)
+ rtw89_core_tx_update_ampdu_info(rtwdev, tx_req, tid);
+ if (IEEE80211_SKB_CB(skb)->control.hw_key)
+ rtw89_core_tx_update_sec_key(rtwdev, tx_req);
+}
+
+static enum btc_pkt_type
+rtw89_core_tx_btc_spec_pkt_notify(struct rtw89_dev *rtwdev,
+ struct rtw89_core_tx_request *tx_req)
+{
+ struct sk_buff *skb = tx_req->skb;
+ struct udphdr *udphdr;
+
+ if (IEEE80211_SKB_CB(skb)->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO) {
+ ieee80211_queue_work(rtwdev->hw, &rtwdev->btc.eapol_notify_work);
+ return PACKET_EAPOL;
+ }
+
+ if (skb->protocol == htons(ETH_P_ARP)) {
+ ieee80211_queue_work(rtwdev->hw, &rtwdev->btc.arp_notify_work);
+ return PACKET_ARP;
+ }
+
+ if (skb->protocol == htons(ETH_P_IP) &&
+ ip_hdr(skb)->protocol == IPPROTO_UDP) {
+ udphdr = udp_hdr(skb);
+ if (((udphdr->source == htons(67) && udphdr->dest == htons(68)) ||
+ (udphdr->source == htons(68) && udphdr->dest == htons(67))) &&
+ skb->len > 282) {
+ ieee80211_queue_work(rtwdev->hw, &rtwdev->btc.dhcp_notify_work);
+ return PACKET_DHCP;
+ }
+ }
+
+ return PACKET_MAX;
+}
+
+static void
+rtw89_core_tx_update_desc_info(struct rtw89_dev *rtwdev,
+ struct rtw89_core_tx_request *tx_req)
+{
+ struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info;
+ struct sk_buff *skb = tx_req->skb;
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ enum rtw89_core_tx_type tx_type;
+ enum btc_pkt_type pkt_type;
+ bool is_bmc;
+ u16 seq;
+
+ seq = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4;
+ if (tx_req->tx_type != RTW89_CORE_TX_TYPE_FWCMD) {
+ tx_type = rtw89_core_get_tx_type(rtwdev, skb);
+ tx_req->tx_type = tx_type;
+ }
+ is_bmc = (is_broadcast_ether_addr(hdr->addr1) ||
+ is_multicast_ether_addr(hdr->addr1));
+
+ desc_info->seq = seq;
+ desc_info->pkt_size = skb->len;
+ desc_info->is_bmc = is_bmc;
+ desc_info->wd_page = true;
+
+ switch (tx_req->tx_type) {
+ case RTW89_CORE_TX_TYPE_MGMT:
+ rtw89_core_tx_update_mgmt_info(rtwdev, tx_req);
+ break;
+ case RTW89_CORE_TX_TYPE_DATA:
+ rtw89_core_tx_update_data_info(rtwdev, tx_req);
+ pkt_type = rtw89_core_tx_btc_spec_pkt_notify(rtwdev, tx_req);
+ rtw89_core_tx_update_he_qos_htc(rtwdev, tx_req, pkt_type);
+ break;
+ case RTW89_CORE_TX_TYPE_FWCMD:
+ rtw89_core_tx_update_h2c_info(rtwdev, tx_req);
+ break;
+ }
+}
+
+void rtw89_core_tx_kick_off(struct rtw89_dev *rtwdev, u8 qsel)
+{
+ u8 ch_dma;
+
+ ch_dma = rtw89_core_get_ch_dma(rtwdev, qsel);
+
+ rtw89_hci_tx_kick_off(rtwdev, ch_dma);
+}
+
+int rtw89_h2c_tx(struct rtw89_dev *rtwdev,
+ struct sk_buff *skb, bool fwdl)
+{
+ struct rtw89_core_tx_request tx_req = {0};
+ int ret;
+
+ tx_req.skb = skb;
+ tx_req.tx_type = RTW89_CORE_TX_TYPE_FWCMD;
+ if (fwdl)
+ tx_req.desc_info.fw_dl = true;
+
+ rtw89_core_tx_update_desc_info(rtwdev, &tx_req);
+
+ if (!fwdl)
+ rtw89_hex_dump(rtwdev, RTW89_DBG_FW, "H2C: ", skb->data, skb->len);
+
+ ret = rtw89_hci_tx_write(rtwdev, &tx_req);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to transmit skb to HCI\n");
+ return ret;
+ }
+ rtw89_hci_tx_kick_off(rtwdev, RTW89_TXCH_CH12);
+
+ return 0;
+}
+
+int rtw89_core_tx_write(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, struct sk_buff *skb, int *qsel)
+{
+ struct rtw89_core_tx_request tx_req = {0};
+ struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
+ int ret;
+
+ tx_req.skb = skb;
+ tx_req.sta = sta;
+ tx_req.vif = vif;
+
+ rtw89_traffic_stats_accu(rtwdev, &rtwdev->stats, skb, true);
+ rtw89_traffic_stats_accu(rtwdev, &rtwvif->stats, skb, true);
+ rtw89_core_tx_update_desc_info(rtwdev, &tx_req);
+ ret = rtw89_hci_tx_write(rtwdev, &tx_req);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to transmit skb to HCI\n");
+ return ret;
+ }
+
+ if (qsel)
+ *qsel = tx_req.desc_info.qsel;
+
+ return 0;
+}
+
+void rtw89_core_fill_txdesc(struct rtw89_dev *rtwdev,
+ struct rtw89_tx_desc_info *desc_info,
+ void *txdesc)
+{
+ RTW89_SET_TXWD_BODY_WP_OFFSET(txdesc, desc_info->wp_offset);
+ RTW89_SET_TXWD_BODY_WD_INFO_EN(txdesc, desc_info->en_wd_info);
+ RTW89_SET_TXWD_BODY_CHANNEL_DMA(txdesc, desc_info->ch_dma);
+ RTW89_SET_TXWD_BODY_HDR_LLC_LEN(txdesc, desc_info->hdr_llc_len);
+ RTW89_SET_TXWD_BODY_WD_PAGE(txdesc, desc_info->wd_page);
+ RTW89_SET_TXWD_BODY_FW_DL(txdesc, desc_info->fw_dl);
+ RTW89_SET_TXWD_BODY_SW_SEQ(txdesc, desc_info->seq);
+
+ RTW89_SET_TXWD_BODY_TID_INDICATE(txdesc, desc_info->tid_indicate);
+ RTW89_SET_TXWD_BODY_QSEL(txdesc, desc_info->qsel);
+ RTW89_SET_TXWD_BODY_TXPKT_SIZE(txdesc, desc_info->pkt_size);
+ RTW89_SET_TXWD_BODY_AGG_EN(txdesc, desc_info->agg_en);
+ RTW89_SET_TXWD_BODY_BK(txdesc, desc_info->bk);
+
+ if (!desc_info->en_wd_info)
+ return;
+
+ RTW89_SET_TXWD_INFO_USE_RATE(txdesc, desc_info->use_rate);
+ RTW89_SET_TXWD_INFO_DATA_RATE(txdesc, desc_info->data_rate);
+ RTW89_SET_TXWD_INFO_DISDATAFB(txdesc, desc_info->dis_data_fb);
+ RTW89_SET_TXWD_INFO_MAX_AGGNUM(txdesc, desc_info->ampdu_num);
+ RTW89_SET_TXWD_INFO_AMPDU_DENSITY(txdesc, desc_info->ampdu_density);
+ RTW89_SET_TXWD_INFO_SEC_TYPE(txdesc, desc_info->sec_type);
+ RTW89_SET_TXWD_INFO_SEC_HW_ENC(txdesc, desc_info->sec_en);
+ RTW89_SET_TXWD_INFO_SEC_CAM_IDX(txdesc, desc_info->sec_cam_idx);
+ RTW89_SET_TXWD_INFO_RTS_EN(txdesc, 1);
+ RTW89_SET_TXWD_INFO_HW_RTS_EN(txdesc, 1);
+ RTW89_SET_TXWD_INFO_A_CTRL_BSR(txdesc, desc_info->a_ctrl_bsr);
+}
+EXPORT_SYMBOL(rtw89_core_fill_txdesc);
+
+static int rtw89_core_rx_process_mac_ppdu(struct rtw89_dev *rtwdev,
+ struct sk_buff *skb,
+ struct rtw89_rx_phy_ppdu *phy_ppdu)
+{
+ bool rx_cnt_valid = false;
+ u8 plcp_size = 0;
+ u8 usr_num = 0;
+ u8 *phy_sts;
+
+ rx_cnt_valid = RTW89_GET_RXINFO_RX_CNT_VLD(skb->data);
+ plcp_size = RTW89_GET_RXINFO_PLCP_LEN(skb->data) << 3;
+ usr_num = RTW89_GET_RXINFO_USR_NUM(skb->data);
+ if (usr_num > RTW89_PPDU_MAX_USR) {
+ rtw89_warn(rtwdev, "Invalid user number in mac info\n");
+ return -EINVAL;
+ }
+
+ phy_sts = skb->data + RTW89_PPDU_MAC_INFO_SIZE;
+ phy_sts += usr_num * RTW89_PPDU_MAC_INFO_USR_SIZE;
+ /* 8-byte alignment */
+ if (usr_num & BIT(0))
+ phy_sts += RTW89_PPDU_MAC_INFO_USR_SIZE;
+ if (rx_cnt_valid)
+ phy_sts += RTW89_PPDU_MAC_RX_CNT_SIZE;
+ phy_sts += plcp_size;
+
+ phy_ppdu->buf = phy_sts;
+ phy_ppdu->len = skb->data + skb->len - phy_sts;
+
+ return 0;
+}
+
+static void rtw89_core_rx_process_phy_ppdu_iter(void *data,
+ struct ieee80211_sta *sta)
+{
+ struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv;
+ struct rtw89_rx_phy_ppdu *phy_ppdu = (struct rtw89_rx_phy_ppdu *)data;
+
+ if (rtwsta->mac_id == phy_ppdu->mac_id && phy_ppdu->to_self)
+ ewma_rssi_add(&rtwsta->avg_rssi, phy_ppdu->rssi_avg);
+}
+
+#define VAR_LEN 0xff
+#define VAR_LEN_UNIT 8
+static u16 rtw89_core_get_phy_status_ie_len(struct rtw89_dev *rtwdev, u8 *addr)
+{
+ static const u8 physts_ie_len_tab[32] = {
+ 16, 32, 24, 24, 8, 8, 8, 8, VAR_LEN, 8, VAR_LEN, 176, VAR_LEN,
+ VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, 16, 24, VAR_LEN,
+ VAR_LEN, VAR_LEN, 0, 24, 24, 24, 24, 32, 32, 32, 32
+ };
+ u16 ie_len;
+ u8 ie;
+
+ ie = RTW89_GET_PHY_STS_IE_TYPE(addr);
+ if (physts_ie_len_tab[ie] != VAR_LEN)
+ ie_len = physts_ie_len_tab[ie];
+ else
+ ie_len = RTW89_GET_PHY_STS_IE_LEN(addr) * VAR_LEN_UNIT;
+
+ return ie_len;
+}
+
+static void rtw89_core_parse_phy_status_ie01(struct rtw89_dev *rtwdev, u8 *addr,
+ struct rtw89_rx_phy_ppdu *phy_ppdu)
+{
+ s16 cfo;
+
+ /* sign conversion for S(12,2) */
+ cfo = sign_extend32(RTW89_GET_PHY_STS_IE0_CFO(addr), 11);
+ rtw89_phy_cfo_parse(rtwdev, cfo, phy_ppdu);
+}
+
+static int rtw89_core_process_phy_status_ie(struct rtw89_dev *rtwdev, u8 *addr,
+ struct rtw89_rx_phy_ppdu *phy_ppdu)
+{
+ u8 ie;
+
+ ie = RTW89_GET_PHY_STS_IE_TYPE(addr);
+ switch (ie) {
+ case RTW89_PHYSTS_IE01_CMN_OFDM:
+ rtw89_core_parse_phy_status_ie01(rtwdev, addr, phy_ppdu);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void rtw89_core_update_phy_ppdu(struct rtw89_rx_phy_ppdu *phy_ppdu)
+{
+ s8 *rssi = phy_ppdu->rssi;
+ u8 *buf = phy_ppdu->buf;
+
+ phy_ppdu->rssi_avg = RTW89_GET_PHY_STS_RSSI_AVG(buf);
+ rssi[RF_PATH_A] = RTW89_RSSI_RAW_TO_DBM(RTW89_GET_PHY_STS_RSSI_A(buf));
+ rssi[RF_PATH_B] = RTW89_RSSI_RAW_TO_DBM(RTW89_GET_PHY_STS_RSSI_B(buf));
+ rssi[RF_PATH_C] = RTW89_RSSI_RAW_TO_DBM(RTW89_GET_PHY_STS_RSSI_C(buf));
+ rssi[RF_PATH_D] = RTW89_RSSI_RAW_TO_DBM(RTW89_GET_PHY_STS_RSSI_D(buf));
+}
+
+static int rtw89_core_rx_process_phy_ppdu(struct rtw89_dev *rtwdev,
+ struct rtw89_rx_phy_ppdu *phy_ppdu)
+{
+ if (RTW89_GET_PHY_STS_LEN(phy_ppdu->buf) << 3 != phy_ppdu->len) {
+ rtw89_warn(rtwdev, "phy ppdu len mismatch\n");
+ return -EINVAL;
+ }
+ rtw89_core_update_phy_ppdu(phy_ppdu);
+ ieee80211_iterate_stations_atomic(rtwdev->hw,
+ rtw89_core_rx_process_phy_ppdu_iter,
+ phy_ppdu);
+
+ return 0;
+}
+
+static int rtw89_core_rx_parse_phy_sts(struct rtw89_dev *rtwdev,
+ struct rtw89_rx_phy_ppdu *phy_ppdu)
+{
+ u16 ie_len;
+ u8 *pos, *end;
+
+ if (!phy_ppdu->to_self)
+ return 0;
+
+ pos = (u8 *)phy_ppdu->buf + PHY_STS_HDR_LEN;
+ end = (u8 *)phy_ppdu->buf + phy_ppdu->len;
+ while (pos < end) {
+ ie_len = rtw89_core_get_phy_status_ie_len(rtwdev, pos);
+ rtw89_core_process_phy_status_ie(rtwdev, pos, phy_ppdu);
+ pos += ie_len;
+ if (pos > end || ie_len == 0) {
+ rtw89_debug(rtwdev, RTW89_DBG_TXRX,
+ "phy status parse failed\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static void rtw89_core_rx_process_phy_sts(struct rtw89_dev *rtwdev,
+ struct rtw89_rx_phy_ppdu *phy_ppdu)
+{
+ int ret;
+
+ ret = rtw89_core_rx_parse_phy_sts(rtwdev, phy_ppdu);
+ if (ret)
+ rtw89_debug(rtwdev, RTW89_DBG_TXRX, "parse phy sts failed\n");
+ else
+ phy_ppdu->valid = true;
+}
+
+static u8 rtw89_rxdesc_to_nl_he_gi(struct rtw89_dev *rtwdev,
+ const struct rtw89_rx_desc_info *desc_info,
+ bool rx_status)
+{
+ switch (desc_info->gi_ltf) {
+ case RTW89_GILTF_SGI_4XHE08:
+ case RTW89_GILTF_2XHE08:
+ case RTW89_GILTF_1XHE08:
+ return NL80211_RATE_INFO_HE_GI_0_8;
+ case RTW89_GILTF_2XHE16:
+ case RTW89_GILTF_1XHE16:
+ return NL80211_RATE_INFO_HE_GI_1_6;
+ case RTW89_GILTF_LGI_4XHE32:
+ return NL80211_RATE_INFO_HE_GI_3_2;
+ default:
+ rtw89_warn(rtwdev, "invalid gi_ltf=%d", desc_info->gi_ltf);
+ return rx_status ? NL80211_RATE_INFO_HE_GI_3_2 : U8_MAX;
+ }
+}
+
+static bool rtw89_core_rx_ppdu_match(struct rtw89_dev *rtwdev,
+ struct rtw89_rx_desc_info *desc_info,
+ struct ieee80211_rx_status *status)
+{
+ u8 band = desc_info->bb_sel ? RTW89_PHY_1 : RTW89_PHY_0;
+ u8 data_rate_mode, bw, rate_idx = MASKBYTE0, gi_ltf;
+ u16 data_rate;
+ bool ret;
+
+ data_rate = desc_info->data_rate;
+ data_rate_mode = GET_DATA_RATE_MODE(data_rate);
+ if (data_rate_mode == DATA_RATE_MODE_NON_HT) {
+ rate_idx = GET_DATA_RATE_NOT_HT_IDX(data_rate);
+ /* No 4 CCK rates for 5G */
+ if (status->band == NL80211_BAND_5GHZ)
+ rate_idx -= 4;
+ } else if (data_rate_mode == DATA_RATE_MODE_HT) {
+ rate_idx = GET_DATA_RATE_HT_IDX(data_rate);
+ } else if (data_rate_mode == DATA_RATE_MODE_VHT) {
+ rate_idx = GET_DATA_RATE_VHT_HE_IDX(data_rate);
+ } else if (data_rate_mode == DATA_RATE_MODE_HE) {
+ rate_idx = GET_DATA_RATE_VHT_HE_IDX(data_rate);
+ } else {
+ rtw89_warn(rtwdev, "invalid RX rate mode %d\n", data_rate_mode);
+ }
+
+ if (desc_info->bw == RTW89_CHANNEL_WIDTH_80)
+ bw = RATE_INFO_BW_80;
+ else if (desc_info->bw == RTW89_CHANNEL_WIDTH_40)
+ bw = RATE_INFO_BW_40;
+ else
+ bw = RATE_INFO_BW_20;
+
+ gi_ltf = rtw89_rxdesc_to_nl_he_gi(rtwdev, desc_info, false);
+ ret = rtwdev->ppdu_sts.curr_rx_ppdu_cnt[band] == desc_info->ppdu_cnt &&
+ status->rate_idx == rate_idx &&
+ status->he_gi == gi_ltf &&
+ status->bw == bw;
+
+ return ret;
+}
+
+struct rtw89_vif_rx_stats_iter_data {
+ struct rtw89_dev *rtwdev;
+ struct rtw89_rx_phy_ppdu *phy_ppdu;
+ struct rtw89_rx_desc_info *desc_info;
+ struct sk_buff *skb;
+ const u8 *bssid;
+};
+
+static void rtw89_vif_rx_stats_iter(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
+ struct rtw89_vif_rx_stats_iter_data *iter_data = data;
+ struct rtw89_dev *rtwdev = iter_data->rtwdev;
+ struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.cur_pkt_stat;
+ struct rtw89_rx_desc_info *desc_info = iter_data->desc_info;
+ struct sk_buff *skb = iter_data->skb;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ const u8 *bssid = iter_data->bssid;
+
+ if (!ether_addr_equal(vif->bss_conf.bssid, bssid))
+ return;
+
+ if (ieee80211_is_beacon(hdr->frame_control))
+ pkt_stat->beacon_nr++;
+
+ if (!ether_addr_equal(vif->addr, hdr->addr1))
+ return;
+
+ if (desc_info->data_rate < RTW89_HW_RATE_NR)
+ pkt_stat->rx_rate_cnt[desc_info->data_rate]++;
+
+ rtw89_traffic_stats_accu(rtwdev, &rtwvif->stats, skb, false);
+}
+
+static void rtw89_core_rx_stats(struct rtw89_dev *rtwdev,
+ struct rtw89_rx_phy_ppdu *phy_ppdu,
+ struct rtw89_rx_desc_info *desc_info,
+ struct sk_buff *skb)
+{
+ struct rtw89_vif_rx_stats_iter_data iter_data;
+
+ rtw89_traffic_stats_accu(rtwdev, &rtwdev->stats, skb, false);
+
+ iter_data.rtwdev = rtwdev;
+ iter_data.phy_ppdu = phy_ppdu;
+ iter_data.desc_info = desc_info;
+ iter_data.skb = skb;
+ iter_data.bssid = get_hdr_bssid((struct ieee80211_hdr *)skb->data);
+ rtw89_iterate_vifs_bh(rtwdev, rtw89_vif_rx_stats_iter, &iter_data);
+}
+
+static void rtw89_core_rx_pending_skb(struct rtw89_dev *rtwdev,
+ struct rtw89_rx_phy_ppdu *phy_ppdu,
+ struct rtw89_rx_desc_info *desc_info,
+ struct sk_buff *skb)
+{
+ u8 band = desc_info->bb_sel ? RTW89_PHY_1 : RTW89_PHY_0;
+ int curr = rtwdev->ppdu_sts.curr_rx_ppdu_cnt[band];
+ struct sk_buff *skb_ppdu = NULL, *tmp;
+ struct ieee80211_rx_status *rx_status;
+
+ if (curr > RTW89_MAX_PPDU_CNT)
+ return;
+
+ skb_queue_walk_safe(&rtwdev->ppdu_sts.rx_queue[band], skb_ppdu, tmp) {
+ skb_unlink(skb_ppdu, &rtwdev->ppdu_sts.rx_queue[band]);
+ rx_status = IEEE80211_SKB_RXCB(skb_ppdu);
+ if (rtw89_core_rx_ppdu_match(rtwdev, desc_info, rx_status))
+ rtw89_chip_query_ppdu(rtwdev, phy_ppdu, rx_status);
+ rtw89_core_rx_stats(rtwdev, phy_ppdu, desc_info, skb_ppdu);
+ ieee80211_rx_napi(rtwdev->hw, NULL, skb_ppdu, &rtwdev->napi);
+ rtwdev->napi_budget_countdown--;
+ }
+}
+
+static void rtw89_core_rx_process_ppdu_sts(struct rtw89_dev *rtwdev,
+ struct rtw89_rx_desc_info *desc_info,
+ struct sk_buff *skb)
+{
+ struct rtw89_rx_phy_ppdu phy_ppdu = {.buf = skb->data, .valid = false,
+ .len = skb->len,
+ .to_self = desc_info->addr1_match,
+ .mac_id = desc_info->mac_id};
+ int ret;
+
+ if (desc_info->mac_info_valid)
+ rtw89_core_rx_process_mac_ppdu(rtwdev, skb, &phy_ppdu);
+ ret = rtw89_core_rx_process_phy_ppdu(rtwdev, &phy_ppdu);
+ if (ret)
+ rtw89_debug(rtwdev, RTW89_DBG_TXRX, "process ppdu failed\n");
+
+ rtw89_core_rx_process_phy_sts(rtwdev, &phy_ppdu);
+ rtw89_core_rx_pending_skb(rtwdev, &phy_ppdu, desc_info, skb);
+ dev_kfree_skb_any(skb);
+}
+
+static void rtw89_core_rx_process_report(struct rtw89_dev *rtwdev,
+ struct rtw89_rx_desc_info *desc_info,
+ struct sk_buff *skb)
+{
+ switch (desc_info->pkt_type) {
+ case RTW89_CORE_RX_TYPE_C2H:
+ rtw89_fw_c2h_irqsafe(rtwdev, skb);
+ break;
+ case RTW89_CORE_RX_TYPE_PPDU_STAT:
+ rtw89_core_rx_process_ppdu_sts(rtwdev, desc_info, skb);
+ break;
+ default:
+ rtw89_debug(rtwdev, RTW89_DBG_TXRX, "unhandled pkt_type=%d\n",
+ desc_info->pkt_type);
+ dev_kfree_skb_any(skb);
+ break;
+ }
+}
+
+void rtw89_core_query_rxdesc(struct rtw89_dev *rtwdev,
+ struct rtw89_rx_desc_info *desc_info,
+ u8 *data, u32 data_offset)
+{
+ struct rtw89_rxdesc_short *rxd_s;
+ struct rtw89_rxdesc_long *rxd_l;
+ u8 shift_len, drv_info_len;
+
+ rxd_s = (struct rtw89_rxdesc_short *)(data + data_offset);
+ desc_info->pkt_size = RTW89_GET_RXWD_PKT_SIZE(rxd_s);
+ desc_info->drv_info_size = RTW89_GET_RXWD_DRV_INFO_SIZE(rxd_s);
+ desc_info->long_rxdesc = RTW89_GET_RXWD_LONG_RXD(rxd_s);
+ desc_info->pkt_type = RTW89_GET_RXWD_RPKT_TYPE(rxd_s);
+ desc_info->mac_info_valid = RTW89_GET_RXWD_MAC_INFO_VALID(rxd_s);
+ desc_info->bw = RTW89_GET_RXWD_BW(rxd_s);
+ desc_info->data_rate = RTW89_GET_RXWD_DATA_RATE(rxd_s);
+ desc_info->gi_ltf = RTW89_GET_RXWD_GI_LTF(rxd_s);
+ desc_info->user_id = RTW89_GET_RXWD_USER_ID(rxd_s);
+ desc_info->sr_en = RTW89_GET_RXWD_SR_EN(rxd_s);
+ desc_info->ppdu_cnt = RTW89_GET_RXWD_PPDU_CNT(rxd_s);
+ desc_info->ppdu_type = RTW89_GET_RXWD_PPDU_TYPE(rxd_s);
+ desc_info->free_run_cnt = RTW89_GET_RXWD_FREE_RUN_CNT(rxd_s);
+ desc_info->icv_err = RTW89_GET_RXWD_ICV_ERR(rxd_s);
+ desc_info->crc32_err = RTW89_GET_RXWD_CRC32_ERR(rxd_s);
+ desc_info->hw_dec = RTW89_GET_RXWD_HW_DEC(rxd_s);
+ desc_info->sw_dec = RTW89_GET_RXWD_SW_DEC(rxd_s);
+ desc_info->addr1_match = RTW89_GET_RXWD_A1_MATCH(rxd_s);
+
+ shift_len = desc_info->shift << 1; /* 2-byte unit */
+ drv_info_len = desc_info->drv_info_size << 3; /* 8-byte unit */
+ desc_info->offset = data_offset + shift_len + drv_info_len;
+ desc_info->ready = true;
+
+ if (!desc_info->long_rxdesc)
+ return;
+
+ rxd_l = (struct rtw89_rxdesc_long *)(data + data_offset);
+ desc_info->frame_type = RTW89_GET_RXWD_TYPE(rxd_l);
+ desc_info->addr_cam_valid = RTW89_GET_RXWD_ADDR_CAM_VLD(rxd_l);
+ desc_info->addr_cam_id = RTW89_GET_RXWD_ADDR_CAM_ID(rxd_l);
+ desc_info->sec_cam_id = RTW89_GET_RXWD_SEC_CAM_ID(rxd_l);
+ desc_info->mac_id = RTW89_GET_RXWD_MAC_ID(rxd_l);
+ desc_info->rx_pl_id = RTW89_GET_RXWD_RX_PL_ID(rxd_l);
+}
+EXPORT_SYMBOL(rtw89_core_query_rxdesc);
+
+struct rtw89_core_iter_rx_status {
+ struct rtw89_dev *rtwdev;
+ struct ieee80211_rx_status *rx_status;
+ struct rtw89_rx_desc_info *desc_info;
+ u8 mac_id;
+};
+
+static
+void rtw89_core_stats_sta_rx_status_iter(void *data, struct ieee80211_sta *sta)
+{
+ struct rtw89_core_iter_rx_status *iter_data =
+ (struct rtw89_core_iter_rx_status *)data;
+ struct ieee80211_rx_status *rx_status = iter_data->rx_status;
+ struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv;
+ struct rtw89_rx_desc_info *desc_info = iter_data->desc_info;
+ u8 mac_id = iter_data->mac_id;
+
+ if (mac_id != rtwsta->mac_id)
+ return;
+
+ rtwsta->rx_status = *rx_status;
+ rtwsta->rx_hw_rate = desc_info->data_rate;
+}
+
+static void rtw89_core_stats_sta_rx_status(struct rtw89_dev *rtwdev,
+ struct rtw89_rx_desc_info *desc_info,
+ struct ieee80211_rx_status *rx_status)
+{
+ struct rtw89_core_iter_rx_status iter_data;
+
+ if (!desc_info->addr1_match || !desc_info->long_rxdesc)
+ return;
+
+ if (desc_info->frame_type != RTW89_RX_TYPE_DATA)
+ return;
+
+ iter_data.rtwdev = rtwdev;
+ iter_data.rx_status = rx_status;
+ iter_data.desc_info = desc_info;
+ iter_data.mac_id = desc_info->mac_id;
+ ieee80211_iterate_stations_atomic(rtwdev->hw,
+ rtw89_core_stats_sta_rx_status_iter,
+ &iter_data);
+}
+
+static void rtw89_core_update_rx_status(struct rtw89_dev *rtwdev,
+ struct rtw89_rx_desc_info *desc_info,
+ struct ieee80211_rx_status *rx_status)
+{
+ struct ieee80211_hw *hw = rtwdev->hw;
+ u16 data_rate;
+ u8 data_rate_mode;
+
+ /* currently using single PHY */
+ rx_status->freq = hw->conf.chandef.chan->center_freq;
+ rx_status->band = hw->conf.chandef.chan->band;
+
+ if (desc_info->icv_err || desc_info->crc32_err)
+ rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
+
+ if (desc_info->hw_dec &&
+ !(desc_info->sw_dec || desc_info->icv_err))
+ rx_status->flag |= RX_FLAG_DECRYPTED;
+
+ if (desc_info->bw == RTW89_CHANNEL_WIDTH_80)
+ rx_status->bw = RATE_INFO_BW_80;
+ else if (desc_info->bw == RTW89_CHANNEL_WIDTH_40)
+ rx_status->bw = RATE_INFO_BW_40;
+ else
+ rx_status->bw = RATE_INFO_BW_20;
+
+ data_rate = desc_info->data_rate;
+ data_rate_mode = GET_DATA_RATE_MODE(data_rate);
+ if (data_rate_mode == DATA_RATE_MODE_NON_HT) {
+ rx_status->encoding = RX_ENC_LEGACY;
+ rx_status->rate_idx = GET_DATA_RATE_NOT_HT_IDX(data_rate);
+ /* No 4 CCK rates for 5G */
+ if (rx_status->band == NL80211_BAND_5GHZ)
+ rx_status->rate_idx -= 4;
+ if (rtwdev->scanning)
+ rx_status->rate_idx = min_t(u8, rx_status->rate_idx,
+ ARRAY_SIZE(rtw89_bitrates) - 5);
+ } else if (data_rate_mode == DATA_RATE_MODE_HT) {
+ rx_status->encoding = RX_ENC_HT;
+ rx_status->rate_idx = GET_DATA_RATE_HT_IDX(data_rate);
+ if (desc_info->gi_ltf)
+ rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
+ } else if (data_rate_mode == DATA_RATE_MODE_VHT) {
+ rx_status->encoding = RX_ENC_VHT;
+ rx_status->rate_idx = GET_DATA_RATE_VHT_HE_IDX(data_rate);
+ rx_status->nss = GET_DATA_RATE_NSS(data_rate) + 1;
+ if (desc_info->gi_ltf)
+ rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
+ } else if (data_rate_mode == DATA_RATE_MODE_HE) {
+ rx_status->encoding = RX_ENC_HE;
+ rx_status->rate_idx = GET_DATA_RATE_VHT_HE_IDX(data_rate);
+ rx_status->nss = GET_DATA_RATE_NSS(data_rate) + 1;
+ } else {
+ rtw89_warn(rtwdev, "invalid RX rate mode %d\n", data_rate_mode);
+ }
+
+ /* he_gi is used to match ppdu, so we always fill it. */
+ rx_status->he_gi = rtw89_rxdesc_to_nl_he_gi(rtwdev, desc_info, true);
+ rx_status->flag |= RX_FLAG_MACTIME_START;
+ rx_status->mactime = desc_info->free_run_cnt;
+
+ rtw89_core_stats_sta_rx_status(rtwdev, desc_info, rx_status);
+}
+
+static enum rtw89_ps_mode rtw89_update_ps_mode(struct rtw89_dev *rtwdev)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+
+ if (rtw89_disable_ps_mode || !chip->ps_mode_supported)
+ return RTW89_PS_MODE_NONE;
+
+ if (chip->ps_mode_supported & BIT(RTW89_PS_MODE_PWR_GATED))
+ return RTW89_PS_MODE_PWR_GATED;
+
+ if (chip->ps_mode_supported & BIT(RTW89_PS_MODE_CLK_GATED))
+ return RTW89_PS_MODE_CLK_GATED;
+
+ if (chip->ps_mode_supported & BIT(RTW89_PS_MODE_RFOFF))
+ return RTW89_PS_MODE_RFOFF;
+
+ return RTW89_PS_MODE_NONE;
+}
+
+static void rtw89_core_flush_ppdu_rx_queue(struct rtw89_dev *rtwdev,
+ struct rtw89_rx_desc_info *desc_info)
+{
+ struct rtw89_ppdu_sts_info *ppdu_sts = &rtwdev->ppdu_sts;
+ u8 band = desc_info->bb_sel ? RTW89_PHY_1 : RTW89_PHY_0;
+ struct sk_buff *skb_ppdu, *tmp;
+
+ skb_queue_walk_safe(&ppdu_sts->rx_queue[band], skb_ppdu, tmp) {
+ skb_unlink(skb_ppdu, &ppdu_sts->rx_queue[band]);
+ rtw89_core_rx_stats(rtwdev, NULL, desc_info, skb_ppdu);
+ ieee80211_rx_napi(rtwdev->hw, NULL, skb_ppdu, &rtwdev->napi);
+ rtwdev->napi_budget_countdown--;
+ }
+}
+
+void rtw89_core_rx(struct rtw89_dev *rtwdev,
+ struct rtw89_rx_desc_info *desc_info,
+ struct sk_buff *skb)
+{
+ struct ieee80211_rx_status *rx_status;
+ struct rtw89_ppdu_sts_info *ppdu_sts = &rtwdev->ppdu_sts;
+ u8 ppdu_cnt = desc_info->ppdu_cnt;
+ u8 band = desc_info->bb_sel ? RTW89_PHY_1 : RTW89_PHY_0;
+
+ if (desc_info->pkt_type != RTW89_CORE_RX_TYPE_WIFI) {
+ rtw89_core_rx_process_report(rtwdev, desc_info, skb);
+ return;
+ }
+
+ if (ppdu_sts->curr_rx_ppdu_cnt[band] != ppdu_cnt) {
+ rtw89_core_flush_ppdu_rx_queue(rtwdev, desc_info);
+ ppdu_sts->curr_rx_ppdu_cnt[band] = ppdu_cnt;
+ }
+
+ rx_status = IEEE80211_SKB_RXCB(skb);
+ memset(rx_status, 0, sizeof(*rx_status));
+ rtw89_core_update_rx_status(rtwdev, desc_info, rx_status);
+ if (desc_info->long_rxdesc &&
+ BIT(desc_info->frame_type) & PPDU_FILTER_BITMAP) {
+ skb_queue_tail(&ppdu_sts->rx_queue[band], skb);
+ } else {
+ rtw89_core_rx_stats(rtwdev, NULL, desc_info, skb);
+ ieee80211_rx_napi(rtwdev->hw, NULL, skb, &rtwdev->napi);
+ rtwdev->napi_budget_countdown--;
+ }
+}
+EXPORT_SYMBOL(rtw89_core_rx);
+
+void rtw89_core_napi_start(struct rtw89_dev *rtwdev)
+{
+ if (test_and_set_bit(RTW89_FLAG_NAPI_RUNNING, rtwdev->flags))
+ return;
+
+ napi_enable(&rtwdev->napi);
+}
+EXPORT_SYMBOL(rtw89_core_napi_start);
+
+void rtw89_core_napi_stop(struct rtw89_dev *rtwdev)
+{
+ if (!test_and_clear_bit(RTW89_FLAG_NAPI_RUNNING, rtwdev->flags))
+ return;
+
+ napi_synchronize(&rtwdev->napi);
+ napi_disable(&rtwdev->napi);
+}
+EXPORT_SYMBOL(rtw89_core_napi_stop);
+
+void rtw89_core_napi_init(struct rtw89_dev *rtwdev)
+{
+ init_dummy_netdev(&rtwdev->netdev);
+ netif_napi_add(&rtwdev->netdev, &rtwdev->napi,
+ rtwdev->hci.ops->napi_poll, NAPI_POLL_WEIGHT);
+}
+EXPORT_SYMBOL(rtw89_core_napi_init);
+
+void rtw89_core_napi_deinit(struct rtw89_dev *rtwdev)
+{
+ rtw89_core_napi_stop(rtwdev);
+ netif_napi_del(&rtwdev->napi);
+}
+EXPORT_SYMBOL(rtw89_core_napi_deinit);
+
+static void rtw89_core_ba_work(struct work_struct *work)
+{
+ struct rtw89_dev *rtwdev =
+ container_of(work, struct rtw89_dev, ba_work);
+ struct rtw89_txq *rtwtxq, *tmp;
+ int ret;
+
+ spin_lock_bh(&rtwdev->ba_lock);
+ list_for_each_entry_safe(rtwtxq, tmp, &rtwdev->ba_list, list) {
+ struct ieee80211_txq *txq = rtw89_txq_to_txq(rtwtxq);
+ struct ieee80211_sta *sta = txq->sta;
+ struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv;
+ u8 tid = txq->tid;
+
+ if (!sta) {
+ rtw89_warn(rtwdev, "cannot start BA without sta\n");
+ goto skip_ba_work;
+ }
+
+ if (rtwsta->disassoc) {
+ rtw89_debug(rtwdev, RTW89_DBG_TXRX,
+ "cannot start BA with disassoc sta\n");
+ goto skip_ba_work;
+ }
+
+ ret = ieee80211_start_tx_ba_session(sta, tid, 0);
+ if (ret) {
+ rtw89_debug(rtwdev, RTW89_DBG_TXRX,
+ "failed to setup BA session for %pM:%2d: %d\n",
+ sta->addr, tid, ret);
+ if (ret == -EINVAL)
+ set_bit(RTW89_TXQ_F_BLOCK_BA, &rtwtxq->flags);
+ }
+skip_ba_work:
+ list_del_init(&rtwtxq->list);
+ }
+ spin_unlock_bh(&rtwdev->ba_lock);
+}
+
+static void rtw89_core_free_sta_pending_ba(struct rtw89_dev *rtwdev,
+ struct ieee80211_sta *sta)
+{
+ struct rtw89_txq *rtwtxq, *tmp;
+
+ spin_lock_bh(&rtwdev->ba_lock);
+ list_for_each_entry_safe(rtwtxq, tmp, &rtwdev->ba_list, list) {
+ struct ieee80211_txq *txq = rtw89_txq_to_txq(rtwtxq);
+
+ if (sta == txq->sta)
+ list_del_init(&rtwtxq->list);
+ }
+ spin_unlock_bh(&rtwdev->ba_lock);
+}
+
+static void rtw89_core_txq_check_agg(struct rtw89_dev *rtwdev,
+ struct rtw89_txq *rtwtxq,
+ struct sk_buff *skb)
+{
+ struct ieee80211_hw *hw = rtwdev->hw;
+ struct ieee80211_txq *txq = rtw89_txq_to_txq(rtwtxq);
+ struct ieee80211_sta *sta = txq->sta;
+ struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv;
+
+ if (unlikely(skb_get_queue_mapping(skb) == IEEE80211_AC_VO))
+ return;
+
+ if (unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE)))
+ return;
+
+ if (unlikely(!sta))
+ return;
+
+ if (unlikely(test_bit(RTW89_TXQ_F_BLOCK_BA, &rtwtxq->flags)))
+ return;
+
+ if (test_bit(RTW89_TXQ_F_AMPDU, &rtwtxq->flags)) {
+ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_AMPDU;
+ return;
+ }
+
+ spin_lock_bh(&rtwdev->ba_lock);
+ if (!rtwsta->disassoc && list_empty(&rtwtxq->list)) {
+ list_add_tail(&rtwtxq->list, &rtwdev->ba_list);
+ ieee80211_queue_work(hw, &rtwdev->ba_work);
+ }
+ spin_unlock_bh(&rtwdev->ba_lock);
+}
+
+static void rtw89_core_txq_push(struct rtw89_dev *rtwdev,
+ struct rtw89_txq *rtwtxq,
+ unsigned long frame_cnt,
+ unsigned long byte_cnt)
+{
+ struct ieee80211_txq *txq = rtw89_txq_to_txq(rtwtxq);
+ struct ieee80211_vif *vif = txq->vif;
+ struct ieee80211_sta *sta = txq->sta;
+ struct sk_buff *skb;
+ unsigned long i;
+ int ret;
+
+ for (i = 0; i < frame_cnt; i++) {
+ skb = ieee80211_tx_dequeue_ni(rtwdev->hw, txq);
+ if (!skb) {
+ rtw89_debug(rtwdev, RTW89_DBG_TXRX, "dequeue a NULL skb\n");
+ return;
+ }
+ rtw89_core_txq_check_agg(rtwdev, rtwtxq, skb);
+ ret = rtw89_core_tx_write(rtwdev, vif, sta, skb, NULL);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to push txq: %d\n", ret);
+ ieee80211_free_txskb(rtwdev->hw, skb);
+ break;
+ }
+ }
+}
+
+static u32 rtw89_check_and_reclaim_tx_resource(struct rtw89_dev *rtwdev, u8 tid)
+{
+ u8 qsel, ch_dma;
+
+ qsel = rtw89_core_get_qsel(rtwdev, tid);
+ ch_dma = rtw89_core_get_ch_dma(rtwdev, qsel);
+
+ return rtw89_hci_check_and_reclaim_tx_resource(rtwdev, ch_dma);
+}
+
+static bool rtw89_core_txq_agg_wait(struct rtw89_dev *rtwdev,
+ struct ieee80211_txq *txq,
+ unsigned long *frame_cnt,
+ bool *sched_txq, bool *reinvoke)
+{
+ struct rtw89_txq *rtwtxq = (struct rtw89_txq *)txq->drv_priv;
+ struct ieee80211_sta *sta = txq->sta;
+ struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv;
+
+ if (!sta || rtwsta->max_agg_wait <= 0)
+ return false;
+
+ if (rtwdev->stats.tx_tfc_lv <= RTW89_TFC_MID)
+ return false;
+
+ if (*frame_cnt > 1) {
+ *frame_cnt -= 1;
+ *sched_txq = true;
+ *reinvoke = true;
+ rtwtxq->wait_cnt = 1;
+ return false;
+ }
+
+ if (*frame_cnt == 1 && rtwtxq->wait_cnt < rtwsta->max_agg_wait) {
+ *reinvoke = true;
+ rtwtxq->wait_cnt++;
+ return true;
+ }
+
+ rtwtxq->wait_cnt = 0;
+ return false;
+}
+
+static void rtw89_core_txq_schedule(struct rtw89_dev *rtwdev, u8 ac, bool *reinvoke)
+{
+ struct ieee80211_hw *hw = rtwdev->hw;
+ struct ieee80211_txq *txq;
+ struct rtw89_txq *rtwtxq;
+ unsigned long frame_cnt;
+ unsigned long byte_cnt;
+ u32 tx_resource;
+ bool sched_txq;
+
+ ieee80211_txq_schedule_start(hw, ac);
+ while ((txq = ieee80211_next_txq(hw, ac))) {
+ rtwtxq = (struct rtw89_txq *)txq->drv_priv;
+ tx_resource = rtw89_check_and_reclaim_tx_resource(rtwdev, txq->tid);
+ sched_txq = false;
+
+ ieee80211_txq_get_depth(txq, &frame_cnt, &byte_cnt);
+ if (rtw89_core_txq_agg_wait(rtwdev, txq, &frame_cnt, &sched_txq, reinvoke)) {
+ ieee80211_return_txq(hw, txq, true);
+ continue;
+ }
+ frame_cnt = min_t(unsigned long, frame_cnt, tx_resource);
+ rtw89_core_txq_push(rtwdev, rtwtxq, frame_cnt, byte_cnt);
+ ieee80211_return_txq(hw, txq, sched_txq);
+ if (frame_cnt != 0)
+ rtw89_core_tx_kick_off(rtwdev, rtw89_core_get_qsel(rtwdev, txq->tid));
+ }
+ ieee80211_txq_schedule_end(hw, ac);
+}
+
+static void rtw89_core_txq_work(struct work_struct *w)
+{
+ struct rtw89_dev *rtwdev = container_of(w, struct rtw89_dev, txq_work);
+ bool reinvoke = false;
+ u8 ac;
+
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+ rtw89_core_txq_schedule(rtwdev, ac, &reinvoke);
+
+ if (reinvoke) {
+ /* reinvoke to process the last frame */
+ mod_delayed_work(rtwdev->txq_wq, &rtwdev->txq_reinvoke_work, 1);
+ }
+}
+
+static void rtw89_core_txq_reinvoke_work(struct work_struct *w)
+{
+ struct rtw89_dev *rtwdev = container_of(w, struct rtw89_dev,
+ txq_reinvoke_work.work);
+
+ queue_work(rtwdev->txq_wq, &rtwdev->txq_work);
+}
+
+static enum rtw89_tfc_lv rtw89_get_traffic_level(struct rtw89_dev *rtwdev,
+ u32 throughput, u64 cnt)
+{
+ if (cnt < 100)
+ return RTW89_TFC_IDLE;
+ if (throughput > 50)
+ return RTW89_TFC_HIGH;
+ if (throughput > 10)
+ return RTW89_TFC_MID;
+ if (throughput > 2)
+ return RTW89_TFC_LOW;
+ return RTW89_TFC_ULTRA_LOW;
+}
+
+static void rtw89_traffic_stats_calc(struct rtw89_dev *rtwdev,
+ struct rtw89_traffic_stats *stats)
+{
+ enum rtw89_tfc_lv tx_tfc_lv = stats->tx_tfc_lv;
+ enum rtw89_tfc_lv rx_tfc_lv = stats->rx_tfc_lv;
+
+ stats->tx_throughput_raw = (u32)(stats->tx_unicast >> RTW89_TP_SHIFT);
+ stats->rx_throughput_raw = (u32)(stats->rx_unicast >> RTW89_TP_SHIFT);
+
+ ewma_tp_add(&stats->tx_ewma_tp, stats->tx_throughput_raw);
+ ewma_tp_add(&stats->rx_ewma_tp, stats->rx_throughput_raw);
+
+ stats->tx_throughput = ewma_tp_read(&stats->tx_ewma_tp);
+ stats->rx_throughput = ewma_tp_read(&stats->rx_ewma_tp);
+ stats->tx_tfc_lv = rtw89_get_traffic_level(rtwdev, stats->tx_throughput,
+ stats->tx_cnt);
+ stats->rx_tfc_lv = rtw89_get_traffic_level(rtwdev, stats->rx_throughput,
+ stats->rx_cnt);
+ stats->tx_avg_len = (u32)(stats->tx_cnt ? stats->tx_unicast / stats->tx_cnt : 0);
+ stats->rx_avg_len = (u32)(stats->rx_cnt ? stats->rx_unicast / stats->rx_cnt : 0);
+
+ stats->tx_unicast = 0;
+ stats->rx_unicast = 0;
+ stats->tx_cnt = 0;
+ stats->rx_cnt = 0;
+
+ if (tx_tfc_lv != stats->tx_tfc_lv || rx_tfc_lv != stats->rx_tfc_lv)
+ ieee80211_queue_work(rtwdev->hw, &rtwdev->btc.wl_sta_notify_work);
+}
+
+static void rtw89_vif_traffic_stats_iter(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct rtw89_dev *rtwdev = data;
+ struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
+
+ rtw89_traffic_stats_calc(rtwdev, &rtwvif->stats);
+}
+
+static void rtw89_traffic_stats_track(struct rtw89_dev *rtwdev)
+{
+ rtw89_traffic_stats_calc(rtwdev, &rtwdev->stats);
+
+ rtw89_iterate_vifs(rtwdev, rtw89_vif_traffic_stats_iter, rtwdev, false);
+}
+
+static void rtw89_vif_enter_lps_iter(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct rtw89_dev *rtwdev = data;
+ struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
+
+ if (rtwvif->wifi_role != RTW89_WIFI_ROLE_STATION)
+ return;
+
+ if (rtwvif->stats.tx_tfc_lv == RTW89_TFC_IDLE &&
+ rtwvif->stats.rx_tfc_lv == RTW89_TFC_IDLE)
+ rtw89_enter_lps(rtwdev, rtwvif->mac_id);
+}
+
+static void rtw89_enter_lps_track(struct rtw89_dev *rtwdev)
+{
+ rtw89_iterate_vifs(rtwdev, rtw89_vif_enter_lps_iter, rtwdev, false);
+}
+
+void rtw89_traffic_stats_init(struct rtw89_dev *rtwdev,
+ struct rtw89_traffic_stats *stats)
+{
+ stats->tx_unicast = 0;
+ stats->rx_unicast = 0;
+ stats->tx_cnt = 0;
+ stats->rx_cnt = 0;
+ ewma_tp_init(&stats->tx_ewma_tp);
+ ewma_tp_init(&stats->rx_ewma_tp);
+}
+
+static void rtw89_track_work(struct work_struct *work)
+{
+ struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
+ track_work.work);
+
+ mutex_lock(&rtwdev->mutex);
+
+ if (!test_bit(RTW89_FLAG_RUNNING, rtwdev->flags))
+ goto out;
+
+ ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->track_work,
+ RTW89_TRACK_WORK_PERIOD);
+
+ rtw89_leave_lps(rtwdev, false);
+
+ rtw89_traffic_stats_track(rtwdev);
+ rtw89_mac_bf_monitor_track(rtwdev);
+ rtw89_phy_stat_track(rtwdev);
+ rtw89_phy_env_monitor_track(rtwdev);
+ rtw89_phy_dig(rtwdev);
+ rtw89_chip_rfk_track(rtwdev);
+ rtw89_phy_ra_update(rtwdev);
+ rtw89_phy_cfo_track(rtwdev);
+
+ if (rtwdev->lps_enabled && !rtwdev->btc.lps && !rtwdev->scanning)
+ rtw89_enter_lps_track(rtwdev);
+
+out:
+ mutex_unlock(&rtwdev->mutex);
+}
+
+int rtw89_core_power_on(struct rtw89_dev *rtwdev)
+{
+ int ret;
+
+ ret = rtw89_mac_pwr_on(rtwdev);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to start power sequence\n");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ return ret;
+}
+
+u8 rtw89_core_acquire_bit_map(unsigned long *addr, unsigned long size)
+{
+ unsigned long bit;
+
+ bit = find_first_zero_bit(addr, size);
+ if (bit < size)
+ set_bit(bit, addr);
+
+ return bit;
+}
+
+void rtw89_core_release_bit_map(unsigned long *addr, u8 bit)
+{
+ clear_bit(bit, addr);
+}
+
+void rtw89_core_release_all_bits_map(unsigned long *addr, unsigned int nbits)
+{
+ bitmap_zero(addr, nbits);
+}
+
+#define RTW89_TYPE_MAPPING(_type) \
+ case NL80211_IFTYPE_ ## _type: \
+ rtwvif->wifi_role = RTW89_WIFI_ROLE_ ## _type; \
+ break
+void rtw89_vif_type_mapping(struct ieee80211_vif *vif, bool assoc)
+{
+ struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
+
+ switch (vif->type) {
+ RTW89_TYPE_MAPPING(ADHOC);
+ RTW89_TYPE_MAPPING(STATION);
+ RTW89_TYPE_MAPPING(AP);
+ RTW89_TYPE_MAPPING(AP_VLAN);
+ RTW89_TYPE_MAPPING(MONITOR);
+ RTW89_TYPE_MAPPING(MESH_POINT);
+ RTW89_TYPE_MAPPING(P2P_CLIENT);
+ RTW89_TYPE_MAPPING(P2P_GO);
+ RTW89_TYPE_MAPPING(P2P_DEVICE);
+ RTW89_TYPE_MAPPING(NAN);
+ default:
+ WARN_ON(1);
+ break;
+ }
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_MESH_POINT:
+ rtwvif->net_type = RTW89_NET_TYPE_AP_MODE;
+ rtwvif->self_role = RTW89_SELF_ROLE_AP;
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ rtwvif->net_type = RTW89_NET_TYPE_AD_HOC;
+ break;
+ case NL80211_IFTYPE_STATION:
+ if (assoc) {
+ rtwvif->net_type = RTW89_NET_TYPE_INFRA;
+ rtwvif->trigger = vif->bss_conf.he_support;
+ } else {
+ rtwvif->net_type = RTW89_NET_TYPE_NO_LINK;
+ rtwvif->trigger = false;
+ }
+ rtwvif->self_role = RTW89_SELF_ROLE_CLIENT;
+ rtwvif->addr_cam.sec_ent_mode = RTW89_ADDR_CAM_SEC_NORMAL;
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
+}
+
+int rtw89_core_sta_add(struct rtw89_dev *rtwdev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
+ struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv;
+ int i;
+
+ rtwsta->rtwvif = rtwvif;
+ rtwsta->prev_rssi = 0;
+
+ for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
+ rtw89_core_txq_init(rtwdev, sta->txq[i]);
+
+ ewma_rssi_init(&rtwsta->avg_rssi);
+
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ rtwvif->mgd.ap = sta;
+ rtw89_btc_ntfy_role_info(rtwdev, rtwvif, rtwsta,
+ BTC_ROLE_MSTS_STA_CONN_START);
+ rtw89_chip_rfk_channel(rtwdev);
+ }
+
+ return 0;
+}
+
+int rtw89_core_sta_disassoc(struct rtw89_dev *rtwdev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv;
+
+ rtwdev->total_sta_assoc--;
+ rtwsta->disassoc = true;
+
+ return 0;
+}
+
+int rtw89_core_sta_disconnect(struct rtw89_dev *rtwdev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
+ int ret;
+
+ rtw89_mac_bf_monitor_calc(rtwdev, sta, true);
+ rtw89_mac_bf_disassoc(rtwdev, vif, sta);
+ rtw89_core_free_sta_pending_ba(rtwdev, sta);
+
+ rtw89_vif_type_mapping(vif, false);
+
+ ret = rtw89_fw_h2c_assoc_cmac_tbl(rtwdev, vif, sta);
+ if (ret) {
+ rtw89_warn(rtwdev, "failed to send h2c cmac table\n");
+ return ret;
+ }
+
+ ret = rtw89_fw_h2c_join_info(rtwdev, rtwvif, 1);
+ if (ret) {
+ rtw89_warn(rtwdev, "failed to send h2c join info\n");
+ return ret;
+ }
+
+ /* update cam aid mac_id net_type */
+ rtw89_fw_h2c_cam(rtwdev, rtwvif);
+ if (ret) {
+ rtw89_warn(rtwdev, "failed to send h2c cam\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+int rtw89_core_sta_assoc(struct rtw89_dev *rtwdev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
+ struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv;
+ int ret;
+
+ rtw89_vif_type_mapping(vif, true);
+
+ ret = rtw89_fw_h2c_assoc_cmac_tbl(rtwdev, vif, sta);
+ if (ret) {
+ rtw89_warn(rtwdev, "failed to send h2c cmac table\n");
+ return ret;
+ }
+
+ /* for station mode, assign the mac_id from itself */
+ if (vif->type == NL80211_IFTYPE_STATION)
+ rtwsta->mac_id = rtwvif->mac_id;
+
+ ret = rtw89_fw_h2c_join_info(rtwdev, rtwvif, 0);
+ if (ret) {
+ rtw89_warn(rtwdev, "failed to send h2c join info\n");
+ return ret;
+ }
+
+ /* update cam aid mac_id net_type */
+ rtw89_fw_h2c_cam(rtwdev, rtwvif);
+ if (ret) {
+ rtw89_warn(rtwdev, "failed to send h2c cam\n");
+ return ret;
+ }
+
+ ret = rtw89_fw_h2c_general_pkt(rtwdev, rtwsta->mac_id);
+ if (ret) {
+ rtw89_warn(rtwdev, "failed to send h2c general packet\n");
+ return ret;
+ }
+
+ rtwdev->total_sta_assoc++;
+ rtw89_phy_ra_assoc(rtwdev, sta);
+ rtw89_mac_bf_assoc(rtwdev, vif, sta);
+ rtw89_mac_bf_monitor_calc(rtwdev, sta, false);
+
+ if (vif->type == NL80211_IFTYPE_STATION)
+ rtw89_btc_ntfy_role_info(rtwdev, rtwvif, rtwsta,
+ BTC_ROLE_MSTS_STA_CONN_END);
+
+ return ret;
+}
+
+int rtw89_core_sta_remove(struct rtw89_dev *rtwdev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
+ struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv;
+
+ if (vif->type == NL80211_IFTYPE_STATION)
+ rtw89_btc_ntfy_role_info(rtwdev, rtwvif, rtwsta,
+ BTC_ROLE_MSTS_STA_DIS_CONN);
+
+ return 0;
+}
+
+static void rtw89_init_ht_cap(struct rtw89_dev *rtwdev,
+ struct ieee80211_sta_ht_cap *ht_cap)
+{
+ ht_cap->ht_supported = true;
+ ht_cap->cap = 0;
+ ht_cap->cap |= IEEE80211_HT_CAP_SGI_20 |
+ IEEE80211_HT_CAP_MAX_AMSDU |
+ IEEE80211_HT_CAP_TX_STBC |
+ (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
+
+ ht_cap->cap |= IEEE80211_HT_CAP_LDPC_CODING;
+
+ ht_cap->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+ IEEE80211_HT_CAP_DSSSCCK40 |
+ IEEE80211_HT_CAP_SGI_40;
+ ht_cap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
+ ht_cap->ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE;
+ ht_cap->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
+ ht_cap->mcs.rx_mask[0] = 0xFF;
+ ht_cap->mcs.rx_mask[1] = 0xFF;
+ ht_cap->mcs.rx_mask[4] = 0x01;
+ ht_cap->mcs.rx_highest = cpu_to_le16(300);
+}
+
+static void rtw89_init_vht_cap(struct rtw89_dev *rtwdev,
+ struct ieee80211_sta_vht_cap *vht_cap)
+{
+ u16 mcs_map;
+ __le16 highest;
+ u8 sts_cap = 3;
+
+ vht_cap->vht_supported = true;
+ vht_cap->cap = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 |
+ IEEE80211_VHT_CAP_SHORT_GI_80 |
+ IEEE80211_VHT_CAP_RXSTBC_1 |
+ IEEE80211_VHT_CAP_HTC_VHT |
+ IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK |
+ 0;
+ vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC;
+ vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC;
+ vht_cap->cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE |
+ IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+ vht_cap->cap |= sts_cap << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
+
+ mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 |
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 4 |
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 |
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 |
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 |
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 |
+ IEEE80211_VHT_MCS_NOT_SUPPORTED << 14;
+ highest = cpu_to_le16(867);
+ mcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << 2;
+ vht_cap->vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map);
+ vht_cap->vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map);
+ vht_cap->vht_mcs.rx_highest = highest;
+ vht_cap->vht_mcs.tx_highest = highest;
+}
+
+#define RTW89_SBAND_IFTYPES_NR 2
+
+static void rtw89_init_he_cap(struct rtw89_dev *rtwdev,
+ enum nl80211_band band,
+ struct ieee80211_supported_band *sband)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_hal *hal = &rtwdev->hal;
+ struct ieee80211_sband_iftype_data *iftype_data;
+ bool no_ng16 = (chip->chip_id == RTL8852A && hal->cv == CHIP_CBV) ||
+ (chip->chip_id == RTL8852B && hal->cv == CHIP_CAV);
+ u16 mcs_map = 0;
+ int i;
+ int nss = chip->rx_nss;
+ int idx = 0;
+
+ iftype_data = kcalloc(RTW89_SBAND_IFTYPES_NR, sizeof(*iftype_data), GFP_KERNEL);
+ if (!iftype_data)
+ return;
+
+ for (i = 0; i < 8; i++) {
+ if (i < nss)
+ mcs_map |= IEEE80211_HE_MCS_SUPPORT_0_11 << (i * 2);
+ else
+ mcs_map |= IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2);
+ }
+
+ for (i = 0; i < NUM_NL80211_IFTYPES; i++) {
+ struct ieee80211_sta_he_cap *he_cap;
+ u8 *mac_cap_info;
+ u8 *phy_cap_info;
+
+ switch (i) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_AP:
+ break;
+ default:
+ continue;
+ }
+
+ if (idx >= RTW89_SBAND_IFTYPES_NR) {
+ rtw89_warn(rtwdev, "run out of iftype_data\n");
+ break;
+ }
+
+ iftype_data[idx].types_mask = BIT(i);
+ he_cap = &iftype_data[idx].he_cap;
+ mac_cap_info = he_cap->he_cap_elem.mac_cap_info;
+ phy_cap_info = he_cap->he_cap_elem.phy_cap_info;
+
+ he_cap->has_he = true;
+ if (i == NL80211_IFTYPE_AP)
+ mac_cap_info[0] = IEEE80211_HE_MAC_CAP0_HTC_HE;
+ if (i == NL80211_IFTYPE_STATION)
+ mac_cap_info[1] = IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US;
+ mac_cap_info[2] = IEEE80211_HE_MAC_CAP2_ALL_ACK |
+ IEEE80211_HE_MAC_CAP2_BSR;
+ mac_cap_info[3] = IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_2;
+ if (i == NL80211_IFTYPE_AP)
+ mac_cap_info[3] |= IEEE80211_HE_MAC_CAP3_OMI_CONTROL;
+ mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_OPS |
+ IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU;
+ if (i == NL80211_IFTYPE_STATION)
+ mac_cap_info[5] = IEEE80211_HE_MAC_CAP5_HT_VHT_TRIG_FRAME_RX;
+ phy_cap_info[0] = IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
+ phy_cap_info[1] = IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A |
+ IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD |
+ IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US;
+ phy_cap_info[2] = IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
+ IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ |
+ IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ |
+ IEEE80211_HE_PHY_CAP2_DOPPLER_TX;
+ phy_cap_info[3] = IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM;
+ if (i == NL80211_IFTYPE_STATION)
+ phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_16_QAM |
+ IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_2;
+ if (i == NL80211_IFTYPE_AP)
+ phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_RX_PARTIAL_BW_SU_IN_20MHZ_MU;
+ phy_cap_info[4] = IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE |
+ IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4;
+ phy_cap_info[5] = no_ng16 ? 0 :
+ IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK |
+ IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK;
+ phy_cap_info[6] = IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU |
+ IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU |
+ IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB |
+ IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE;
+ phy_cap_info[7] = IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_SUPP |
+ IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI |
+ IEEE80211_HE_PHY_CAP7_MAX_NC_1;
+ phy_cap_info[8] = IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI |
+ IEEE80211_HE_PHY_CAP8_HE_ER_SU_1XLTF_AND_08_US_GI |
+ IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_996;
+ phy_cap_info[9] = IEEE80211_HE_PHY_CAP9_LONGER_THAN_16_SIGB_OFDM_SYM |
+ IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU |
+ IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB |
+ IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB |
+ IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_16US;
+ if (i == NL80211_IFTYPE_STATION)
+ phy_cap_info[9] |= IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU;
+ he_cap->he_mcs_nss_supp.rx_mcs_80 = cpu_to_le16(mcs_map);
+ he_cap->he_mcs_nss_supp.tx_mcs_80 = cpu_to_le16(mcs_map);
+
+ idx++;
+ }
+
+ sband->iftype_data = iftype_data;
+ sband->n_iftype_data = idx;
+}
+
+static int rtw89_core_set_supported_band(struct rtw89_dev *rtwdev)
+{
+ struct ieee80211_hw *hw = rtwdev->hw;
+ struct ieee80211_supported_band *sband_2ghz = NULL, *sband_5ghz = NULL;
+ u32 size = sizeof(struct ieee80211_supported_band);
+
+ sband_2ghz = kmemdup(&rtw89_sband_2ghz, size, GFP_KERNEL);
+ if (!sband_2ghz)
+ goto err;
+ rtw89_init_ht_cap(rtwdev, &sband_2ghz->ht_cap);
+ rtw89_init_he_cap(rtwdev, NL80211_BAND_2GHZ, sband_2ghz);
+ hw->wiphy->bands[NL80211_BAND_2GHZ] = sband_2ghz;
+
+ sband_5ghz = kmemdup(&rtw89_sband_5ghz, size, GFP_KERNEL);
+ if (!sband_5ghz)
+ goto err;
+ rtw89_init_ht_cap(rtwdev, &sband_5ghz->ht_cap);
+ rtw89_init_vht_cap(rtwdev, &sband_5ghz->vht_cap);
+ rtw89_init_he_cap(rtwdev, NL80211_BAND_5GHZ, sband_5ghz);
+ hw->wiphy->bands[NL80211_BAND_5GHZ] = sband_5ghz;
+
+ return 0;
+
+err:
+ hw->wiphy->bands[NL80211_BAND_2GHZ] = NULL;
+ hw->wiphy->bands[NL80211_BAND_5GHZ] = NULL;
+ if (sband_2ghz)
+ kfree(sband_2ghz->iftype_data);
+ if (sband_5ghz)
+ kfree(sband_5ghz->iftype_data);
+ kfree(sband_2ghz);
+ kfree(sband_5ghz);
+ return -ENOMEM;
+}
+
+static void rtw89_core_clr_supported_band(struct rtw89_dev *rtwdev)
+{
+ struct ieee80211_hw *hw = rtwdev->hw;
+
+ kfree(hw->wiphy->bands[NL80211_BAND_2GHZ]->iftype_data);
+ kfree(hw->wiphy->bands[NL80211_BAND_5GHZ]->iftype_data);
+ kfree(hw->wiphy->bands[NL80211_BAND_2GHZ]);
+ kfree(hw->wiphy->bands[NL80211_BAND_5GHZ]);
+ hw->wiphy->bands[NL80211_BAND_2GHZ] = NULL;
+ hw->wiphy->bands[NL80211_BAND_5GHZ] = NULL;
+}
+
+static void rtw89_core_ppdu_sts_init(struct rtw89_dev *rtwdev)
+{
+ int i;
+
+ for (i = 0; i < RTW89_PHY_MAX; i++)
+ skb_queue_head_init(&rtwdev->ppdu_sts.rx_queue[i]);
+ for (i = 0; i < RTW89_PHY_MAX; i++)
+ rtwdev->ppdu_sts.curr_rx_ppdu_cnt[i] = U8_MAX;
+}
+
+int rtw89_core_start(struct rtw89_dev *rtwdev)
+{
+ int ret;
+
+ rtwdev->mac.qta_mode = RTW89_QTA_SCC;
+ ret = rtw89_mac_init(rtwdev);
+ if (ret) {
+ rtw89_err(rtwdev, "mac init fail, ret:%d\n", ret);
+ return ret;
+ }
+
+ rtw89_btc_ntfy_poweron(rtwdev);
+
+ /* efuse process */
+
+ /* pre-config BB/RF, BB reset/RFC reset */
+ rtw89_mac_disable_bb_rf(rtwdev);
+ rtw89_mac_enable_bb_rf(rtwdev);
+ rtw89_phy_init_bb_reg(rtwdev);
+ rtw89_phy_init_rf_reg(rtwdev);
+
+ rtw89_btc_ntfy_init(rtwdev, BTC_MODE_NORMAL);
+
+ rtw89_phy_dm_init(rtwdev);
+
+ rtw89_mac_cfg_ppdu_status(rtwdev, RTW89_MAC_0, true);
+ rtw89_mac_update_rts_threshold(rtwdev, RTW89_MAC_0);
+
+ ret = rtw89_hci_start(rtwdev);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to start hci\n");
+ return ret;
+ }
+
+ ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->track_work,
+ RTW89_TRACK_WORK_PERIOD);
+
+ set_bit(RTW89_FLAG_RUNNING, rtwdev->flags);
+
+ rtw89_btc_ntfy_radio_state(rtwdev, BTC_RFCTRL_WL_ON);
+ rtw89_fw_h2c_fw_log(rtwdev, rtwdev->fw.fw_log_enable);
+
+ return 0;
+}
+
+void rtw89_core_stop(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+
+ /* Prvent to stop twice; enter_ips and ops_stop */
+ if (!test_bit(RTW89_FLAG_RUNNING, rtwdev->flags))
+ return;
+
+ rtw89_btc_ntfy_radio_state(rtwdev, BTC_RFCTRL_WL_OFF);
+
+ clear_bit(RTW89_FLAG_RUNNING, rtwdev->flags);
+
+ mutex_unlock(&rtwdev->mutex);
+
+ cancel_work_sync(&rtwdev->c2h_work);
+ cancel_work_sync(&btc->eapol_notify_work);
+ cancel_work_sync(&btc->arp_notify_work);
+ cancel_work_sync(&btc->dhcp_notify_work);
+ cancel_work_sync(&btc->wl_sta_notify_work);
+ cancel_delayed_work_sync(&rtwdev->txq_reinvoke_work);
+ cancel_delayed_work_sync(&rtwdev->track_work);
+ cancel_delayed_work_sync(&rtwdev->coex_act1_work);
+ cancel_delayed_work_sync(&rtwdev->cfo_track_work);
+
+ mutex_lock(&rtwdev->mutex);
+
+ rtw89_btc_ntfy_poweroff(rtwdev);
+ rtw89_hci_flush_queues(rtwdev, BIT(rtwdev->hw->queues) - 1, true);
+ rtw89_mac_flush_txq(rtwdev, BIT(rtwdev->hw->queues) - 1, true);
+ rtw89_hci_stop(rtwdev);
+ rtw89_hci_deinit(rtwdev);
+ rtw89_mac_pwr_off(rtwdev);
+ rtw89_hci_reset(rtwdev);
+}
+
+int rtw89_core_init(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ int ret;
+
+ INIT_LIST_HEAD(&rtwdev->ba_list);
+ INIT_WORK(&rtwdev->ba_work, rtw89_core_ba_work);
+ INIT_WORK(&rtwdev->txq_work, rtw89_core_txq_work);
+ INIT_DELAYED_WORK(&rtwdev->txq_reinvoke_work, rtw89_core_txq_reinvoke_work);
+ INIT_DELAYED_WORK(&rtwdev->track_work, rtw89_track_work);
+ INIT_DELAYED_WORK(&rtwdev->coex_act1_work, rtw89_coex_act1_work);
+ INIT_DELAYED_WORK(&rtwdev->cfo_track_work, rtw89_phy_cfo_track_work);
+ rtwdev->txq_wq = alloc_workqueue("rtw89_tx_wq", WQ_UNBOUND | WQ_HIGHPRI, 0);
+ spin_lock_init(&rtwdev->ba_lock);
+ mutex_init(&rtwdev->mutex);
+ mutex_init(&rtwdev->rf_mutex);
+ rtwdev->total_sta_assoc = 0;
+
+ INIT_WORK(&rtwdev->c2h_work, rtw89_fw_c2h_work);
+ skb_queue_head_init(&rtwdev->c2h_queue);
+ rtw89_core_ppdu_sts_init(rtwdev);
+ rtw89_traffic_stats_init(rtwdev, &rtwdev->stats);
+
+ rtwdev->ps_mode = rtw89_update_ps_mode(rtwdev);
+ rtwdev->hal.rx_fltr = DEFAULT_AX_RX_FLTR;
+
+ INIT_WORK(&btc->eapol_notify_work, rtw89_btc_ntfy_eapol_packet_work);
+ INIT_WORK(&btc->arp_notify_work, rtw89_btc_ntfy_arp_packet_work);
+ INIT_WORK(&btc->dhcp_notify_work, rtw89_btc_ntfy_dhcp_packet_work);
+ INIT_WORK(&btc->wl_sta_notify_work, rtw89_btc_ntfy_wl_sta_work);
+
+ ret = rtw89_load_firmware(rtwdev);
+ if (ret) {
+ rtw89_warn(rtwdev, "no firmware loaded\n");
+ return ret;
+ }
+ rtw89_ser_init(rtwdev);
+
+ return 0;
+}
+EXPORT_SYMBOL(rtw89_core_init);
+
+void rtw89_core_deinit(struct rtw89_dev *rtwdev)
+{
+ rtw89_ser_deinit(rtwdev);
+ rtw89_unload_firmware(rtwdev);
+
+ destroy_workqueue(rtwdev->txq_wq);
+ mutex_destroy(&rtwdev->rf_mutex);
+ mutex_destroy(&rtwdev->mutex);
+}
+EXPORT_SYMBOL(rtw89_core_deinit);
+
+static void rtw89_read_chip_ver(struct rtw89_dev *rtwdev)
+{
+ u8 cv;
+
+ cv = rtw89_read32_mask(rtwdev, R_AX_SYS_CFG1, B_AX_CHIP_VER_MSK);
+ if (cv <= CHIP_CBV) {
+ if (rtw89_read32(rtwdev, R_AX_GPIO0_7_FUNC_SEL) == RTW89_R32_DEAD)
+ cv = CHIP_CAV;
+ else
+ cv = CHIP_CBV;
+ }
+
+ rtwdev->hal.cv = cv;
+}
+
+static int rtw89_chip_efuse_info_setup(struct rtw89_dev *rtwdev)
+{
+ int ret;
+
+ ret = rtw89_mac_partial_init(rtwdev);
+ if (ret)
+ return ret;
+
+ ret = rtw89_parse_efuse_map(rtwdev);
+ if (ret)
+ return ret;
+
+ ret = rtw89_parse_phycap_map(rtwdev);
+ if (ret)
+ return ret;
+
+ rtw89_mac_pwr_off(rtwdev);
+
+ return 0;
+}
+
+static int rtw89_chip_board_info_setup(struct rtw89_dev *rtwdev)
+{
+ rtw89_chip_fem_setup(rtwdev);
+
+ return 0;
+}
+
+int rtw89_chip_info_setup(struct rtw89_dev *rtwdev)
+{
+ int ret;
+
+ rtw89_read_chip_ver(rtwdev);
+
+ ret = rtw89_wait_firmware_completion(rtwdev);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to wait firmware completion\n");
+ return ret;
+ }
+
+ ret = rtw89_fw_recognize(rtwdev);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to recognize firmware\n");
+ return ret;
+ }
+
+ ret = rtw89_chip_efuse_info_setup(rtwdev);
+ if (ret)
+ return ret;
+
+ ret = rtw89_chip_board_info_setup(rtwdev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL(rtw89_chip_info_setup);
+
+static int rtw89_core_register_hw(struct rtw89_dev *rtwdev)
+{
+ struct ieee80211_hw *hw = rtwdev->hw;
+ struct rtw89_efuse *efuse = &rtwdev->efuse;
+ int ret;
+ int tx_headroom = IEEE80211_HT_CTL_LEN;
+
+ hw->vif_data_size = sizeof(struct rtw89_vif);
+ hw->sta_data_size = sizeof(struct rtw89_sta);
+ hw->txq_data_size = sizeof(struct rtw89_txq);
+
+ SET_IEEE80211_PERM_ADDR(hw, efuse->addr);
+
+ hw->extra_tx_headroom = tx_headroom;
+ hw->queues = IEEE80211_NUM_ACS;
+ hw->max_rx_aggregation_subframes = RTW89_MAX_RX_AGG_NUM;
+ hw->max_tx_aggregation_subframes = RTW89_MAX_TX_AGG_NUM;
+
+ ieee80211_hw_set(hw, SIGNAL_DBM);
+ ieee80211_hw_set(hw, HAS_RATE_CONTROL);
+ ieee80211_hw_set(hw, MFP_CAPABLE);
+ ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
+ ieee80211_hw_set(hw, AMPDU_AGGREGATION);
+ ieee80211_hw_set(hw, RX_INCLUDES_FCS);
+ ieee80211_hw_set(hw, TX_AMSDU);
+ ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
+ ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
+ ieee80211_hw_set(hw, SUPPORTS_PS);
+ ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
+
+ hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+ hw->wiphy->available_antennas_tx = BIT(rtwdev->chip->rf_path_num) - 1;
+ hw->wiphy->available_antennas_rx = BIT(rtwdev->chip->rf_path_num) - 1;
+
+ hw->wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
+
+ wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0);
+
+ ret = rtw89_core_set_supported_band(rtwdev);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to set supported band\n");
+ return ret;
+ }
+
+ hw->wiphy->reg_notifier = rtw89_regd_notifier;
+ hw->wiphy->sar_capa = &rtw89_sar_capa;
+
+ ret = ieee80211_register_hw(hw);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to register hw\n");
+ goto err;
+ }
+
+ ret = rtw89_regd_init(rtwdev, rtw89_regd_notifier);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to init regd\n");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static void rtw89_core_unregister_hw(struct rtw89_dev *rtwdev)
+{
+ struct ieee80211_hw *hw = rtwdev->hw;
+
+ ieee80211_unregister_hw(hw);
+ rtw89_core_clr_supported_band(rtwdev);
+}
+
+int rtw89_core_register(struct rtw89_dev *rtwdev)
+{
+ int ret;
+
+ ret = rtw89_core_register_hw(rtwdev);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to register core hw\n");
+ return ret;
+ }
+
+ rtw89_debugfs_init(rtwdev);
+
+ return 0;
+}
+EXPORT_SYMBOL(rtw89_core_register);
+
+void rtw89_core_unregister(struct rtw89_dev *rtwdev)
+{
+ rtw89_core_unregister_hw(rtwdev);
+}
+EXPORT_SYMBOL(rtw89_core_unregister);
+
+MODULE_AUTHOR("Realtek Corporation");
+MODULE_DESCRIPTION("Realtek 802.11ax wireless core module");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
new file mode 100644
index 000000000000..d535b9293911
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -0,0 +1,3336 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/* Copyright(c) 2019-2020 Realtek Corporation
+ */
+
+#ifndef __RTW89_CORE_H__
+#define __RTW89_CORE_H__
+
+#include <linux/average.h>
+#include <linux/bitfield.h>
+#include <linux/firmware.h>
+#include <linux/iopoll.h>
+#include <linux/workqueue.h>
+#include <net/mac80211.h>
+
+struct rtw89_dev;
+
+extern const struct ieee80211_ops rtw89_ops;
+extern const struct rtw89_chip_info rtw8852a_chip_info;
+
+#define MASKBYTE0 0xff
+#define MASKBYTE1 0xff00
+#define MASKBYTE2 0xff0000
+#define MASKBYTE3 0xff000000
+#define MASKBYTE4 0xff00000000ULL
+#define MASKHWORD 0xffff0000
+#define MASKLWORD 0x0000ffff
+#define MASKDWORD 0xffffffff
+#define RFREG_MASK 0xfffff
+#define INV_RF_DATA 0xffffffff
+
+#define RTW89_TRACK_WORK_PERIOD round_jiffies_relative(HZ * 2)
+#define CFO_TRACK_MAX_USER 64
+#define MAX_RSSI 110
+#define RSSI_FACTOR 1
+#define RTW89_RSSI_RAW_TO_DBM(rssi) ((s8)((rssi) >> RSSI_FACTOR) - MAX_RSSI)
+#define RTW89_MAX_HW_PORT_NUM 5
+
+#define RTW89_HTC_MASK_VARIANT GENMASK(1, 0)
+#define RTW89_HTC_VARIANT_HE 3
+#define RTW89_HTC_MASK_CTL_ID GENMASK(5, 2)
+#define RTW89_HTC_VARIANT_HE_CID_CAS 6
+#define RTW89_HTC_MASK_CTL_INFO GENMASK(31, 6)
+
+enum rtw89_subband {
+ RTW89_CH_2G = 0,
+ RTW89_CH_5G_BAND_1 = 1,
+ /* RTW89_CH_5G_BAND_2 = 2, unused */
+ RTW89_CH_5G_BAND_3 = 3,
+ RTW89_CH_5G_BAND_4 = 4,
+
+ RTW89_SUBBAND_NR,
+};
+
+enum rtw89_hci_type {
+ RTW89_HCI_TYPE_PCIE,
+ RTW89_HCI_TYPE_USB,
+ RTW89_HCI_TYPE_SDIO,
+};
+
+enum rtw89_core_chip_id {
+ RTL8852A,
+ RTL8852B,
+ RTL8852C,
+};
+
+enum rtw89_cv {
+ CHIP_CAV,
+ CHIP_CBV,
+ CHIP_CCV,
+ CHIP_CDV,
+ CHIP_CEV,
+ CHIP_CFV,
+ CHIP_CV_MAX,
+ CHIP_CV_INVALID = CHIP_CV_MAX,
+};
+
+enum rtw89_core_tx_type {
+ RTW89_CORE_TX_TYPE_DATA,
+ RTW89_CORE_TX_TYPE_MGMT,
+ RTW89_CORE_TX_TYPE_FWCMD,
+};
+
+enum rtw89_core_rx_type {
+ RTW89_CORE_RX_TYPE_WIFI = 0,
+ RTW89_CORE_RX_TYPE_PPDU_STAT = 1,
+ RTW89_CORE_RX_TYPE_CHAN_INFO = 2,
+ RTW89_CORE_RX_TYPE_BB_SCOPE = 3,
+ RTW89_CORE_RX_TYPE_F2P_TXCMD = 4,
+ RTW89_CORE_RX_TYPE_SS2FW = 5,
+ RTW89_CORE_RX_TYPE_TX_REPORT = 6,
+ RTW89_CORE_RX_TYPE_TX_REL_HOST = 7,
+ RTW89_CORE_RX_TYPE_DFS_REPORT = 8,
+ RTW89_CORE_RX_TYPE_TX_REL_CPU = 9,
+ RTW89_CORE_RX_TYPE_C2H = 10,
+ RTW89_CORE_RX_TYPE_CSI = 11,
+ RTW89_CORE_RX_TYPE_CQI = 12,
+};
+
+enum rtw89_txq_flags {
+ RTW89_TXQ_F_AMPDU = 0,
+ RTW89_TXQ_F_BLOCK_BA = 1,
+};
+
+enum rtw89_net_type {
+ RTW89_NET_TYPE_NO_LINK = 0,
+ RTW89_NET_TYPE_AD_HOC = 1,
+ RTW89_NET_TYPE_INFRA = 2,
+ RTW89_NET_TYPE_AP_MODE = 3,
+};
+
+enum rtw89_wifi_role {
+ RTW89_WIFI_ROLE_NONE,
+ RTW89_WIFI_ROLE_STATION,
+ RTW89_WIFI_ROLE_AP,
+ RTW89_WIFI_ROLE_AP_VLAN,
+ RTW89_WIFI_ROLE_ADHOC,
+ RTW89_WIFI_ROLE_ADHOC_MASTER,
+ RTW89_WIFI_ROLE_MESH_POINT,
+ RTW89_WIFI_ROLE_MONITOR,
+ RTW89_WIFI_ROLE_P2P_DEVICE,
+ RTW89_WIFI_ROLE_P2P_CLIENT,
+ RTW89_WIFI_ROLE_P2P_GO,
+ RTW89_WIFI_ROLE_NAN,
+ RTW89_WIFI_ROLE_MLME_MAX
+};
+
+enum rtw89_upd_mode {
+ RTW89_VIF_CREATE,
+ RTW89_VIF_REMOVE,
+ RTW89_VIF_TYPE_CHANGE,
+ RTW89_VIF_INFO_CHANGE,
+ RTW89_VIF_CON_DISCONN
+};
+
+enum rtw89_self_role {
+ RTW89_SELF_ROLE_CLIENT,
+ RTW89_SELF_ROLE_AP,
+ RTW89_SELF_ROLE_AP_CLIENT
+};
+
+enum rtw89_msk_sO_el {
+ RTW89_NO_MSK,
+ RTW89_SMA,
+ RTW89_TMA,
+ RTW89_BSSID
+};
+
+enum rtw89_sch_tx_sel {
+ RTW89_SCH_TX_SEL_ALL,
+ RTW89_SCH_TX_SEL_HIQ,
+ RTW89_SCH_TX_SEL_MG0,
+ RTW89_SCH_TX_SEL_MACID,
+};
+
+/* RTW89_ADDR_CAM_SEC_NONE : not enabled
+ * RTW89_ADDR_CAM_SEC_ALL_UNI : 0 - 6 unicast
+ * RTW89_ADDR_CAM_SEC_NORMAL : 0 - 1 unicast, 2 - 4 group, 5 - 6 BIP
+ * RTW89_ADDR_CAM_SEC_4GROUP : 0 - 1 unicast, 2 - 5 group, 6 BIP
+ */
+enum rtw89_add_cam_sec_mode {
+ RTW89_ADDR_CAM_SEC_NONE = 0,
+ RTW89_ADDR_CAM_SEC_ALL_UNI = 1,
+ RTW89_ADDR_CAM_SEC_NORMAL = 2,
+ RTW89_ADDR_CAM_SEC_4GROUP = 3,
+};
+
+enum rtw89_sec_key_type {
+ RTW89_SEC_KEY_TYPE_NONE = 0,
+ RTW89_SEC_KEY_TYPE_WEP40 = 1,
+ RTW89_SEC_KEY_TYPE_WEP104 = 2,
+ RTW89_SEC_KEY_TYPE_TKIP = 3,
+ RTW89_SEC_KEY_TYPE_WAPI = 4,
+ RTW89_SEC_KEY_TYPE_GCMSMS4 = 5,
+ RTW89_SEC_KEY_TYPE_CCMP128 = 6,
+ RTW89_SEC_KEY_TYPE_CCMP256 = 7,
+ RTW89_SEC_KEY_TYPE_GCMP128 = 8,
+ RTW89_SEC_KEY_TYPE_GCMP256 = 9,
+ RTW89_SEC_KEY_TYPE_BIP_CCMP128 = 10,
+};
+
+enum rtw89_port {
+ RTW89_PORT_0 = 0,
+ RTW89_PORT_1 = 1,
+ RTW89_PORT_2 = 2,
+ RTW89_PORT_3 = 3,
+ RTW89_PORT_4 = 4,
+ RTW89_PORT_NUM
+};
+
+enum rtw89_band {
+ RTW89_BAND_2G = 0,
+ RTW89_BAND_5G = 1,
+ RTW89_BAND_MAX,
+};
+
+enum rtw89_hw_rate {
+ RTW89_HW_RATE_CCK1 = 0x0,
+ RTW89_HW_RATE_CCK2 = 0x1,
+ RTW89_HW_RATE_CCK5_5 = 0x2,
+ RTW89_HW_RATE_CCK11 = 0x3,
+ RTW89_HW_RATE_OFDM6 = 0x4,
+ RTW89_HW_RATE_OFDM9 = 0x5,
+ RTW89_HW_RATE_OFDM12 = 0x6,
+ RTW89_HW_RATE_OFDM18 = 0x7,
+ RTW89_HW_RATE_OFDM24 = 0x8,
+ RTW89_HW_RATE_OFDM36 = 0x9,
+ RTW89_HW_RATE_OFDM48 = 0xA,
+ RTW89_HW_RATE_OFDM54 = 0xB,
+ RTW89_HW_RATE_MCS0 = 0x80,
+ RTW89_HW_RATE_MCS1 = 0x81,
+ RTW89_HW_RATE_MCS2 = 0x82,
+ RTW89_HW_RATE_MCS3 = 0x83,
+ RTW89_HW_RATE_MCS4 = 0x84,
+ RTW89_HW_RATE_MCS5 = 0x85,
+ RTW89_HW_RATE_MCS6 = 0x86,
+ RTW89_HW_RATE_MCS7 = 0x87,
+ RTW89_HW_RATE_MCS8 = 0x88,
+ RTW89_HW_RATE_MCS9 = 0x89,
+ RTW89_HW_RATE_MCS10 = 0x8A,
+ RTW89_HW_RATE_MCS11 = 0x8B,
+ RTW89_HW_RATE_MCS12 = 0x8C,
+ RTW89_HW_RATE_MCS13 = 0x8D,
+ RTW89_HW_RATE_MCS14 = 0x8E,
+ RTW89_HW_RATE_MCS15 = 0x8F,
+ RTW89_HW_RATE_MCS16 = 0x90,
+ RTW89_HW_RATE_MCS17 = 0x91,
+ RTW89_HW_RATE_MCS18 = 0x92,
+ RTW89_HW_RATE_MCS19 = 0x93,
+ RTW89_HW_RATE_MCS20 = 0x94,
+ RTW89_HW_RATE_MCS21 = 0x95,
+ RTW89_HW_RATE_MCS22 = 0x96,
+ RTW89_HW_RATE_MCS23 = 0x97,
+ RTW89_HW_RATE_MCS24 = 0x98,
+ RTW89_HW_RATE_MCS25 = 0x99,
+ RTW89_HW_RATE_MCS26 = 0x9A,
+ RTW89_HW_RATE_MCS27 = 0x9B,
+ RTW89_HW_RATE_MCS28 = 0x9C,
+ RTW89_HW_RATE_MCS29 = 0x9D,
+ RTW89_HW_RATE_MCS30 = 0x9E,
+ RTW89_HW_RATE_MCS31 = 0x9F,
+ RTW89_HW_RATE_VHT_NSS1_MCS0 = 0x100,
+ RTW89_HW_RATE_VHT_NSS1_MCS1 = 0x101,
+ RTW89_HW_RATE_VHT_NSS1_MCS2 = 0x102,
+ RTW89_HW_RATE_VHT_NSS1_MCS3 = 0x103,
+ RTW89_HW_RATE_VHT_NSS1_MCS4 = 0x104,
+ RTW89_HW_RATE_VHT_NSS1_MCS5 = 0x105,
+ RTW89_HW_RATE_VHT_NSS1_MCS6 = 0x106,
+ RTW89_HW_RATE_VHT_NSS1_MCS7 = 0x107,
+ RTW89_HW_RATE_VHT_NSS1_MCS8 = 0x108,
+ RTW89_HW_RATE_VHT_NSS1_MCS9 = 0x109,
+ RTW89_HW_RATE_VHT_NSS2_MCS0 = 0x110,
+ RTW89_HW_RATE_VHT_NSS2_MCS1 = 0x111,
+ RTW89_HW_RATE_VHT_NSS2_MCS2 = 0x112,
+ RTW89_HW_RATE_VHT_NSS2_MCS3 = 0x113,
+ RTW89_HW_RATE_VHT_NSS2_MCS4 = 0x114,
+ RTW89_HW_RATE_VHT_NSS2_MCS5 = 0x115,
+ RTW89_HW_RATE_VHT_NSS2_MCS6 = 0x116,
+ RTW89_HW_RATE_VHT_NSS2_MCS7 = 0x117,
+ RTW89_HW_RATE_VHT_NSS2_MCS8 = 0x118,
+ RTW89_HW_RATE_VHT_NSS2_MCS9 = 0x119,
+ RTW89_HW_RATE_VHT_NSS3_MCS0 = 0x120,
+ RTW89_HW_RATE_VHT_NSS3_MCS1 = 0x121,
+ RTW89_HW_RATE_VHT_NSS3_MCS2 = 0x122,
+ RTW89_HW_RATE_VHT_NSS3_MCS3 = 0x123,
+ RTW89_HW_RATE_VHT_NSS3_MCS4 = 0x124,
+ RTW89_HW_RATE_VHT_NSS3_MCS5 = 0x125,
+ RTW89_HW_RATE_VHT_NSS3_MCS6 = 0x126,
+ RTW89_HW_RATE_VHT_NSS3_MCS7 = 0x127,
+ RTW89_HW_RATE_VHT_NSS3_MCS8 = 0x128,
+ RTW89_HW_RATE_VHT_NSS3_MCS9 = 0x129,
+ RTW89_HW_RATE_VHT_NSS4_MCS0 = 0x130,
+ RTW89_HW_RATE_VHT_NSS4_MCS1 = 0x131,
+ RTW89_HW_RATE_VHT_NSS4_MCS2 = 0x132,
+ RTW89_HW_RATE_VHT_NSS4_MCS3 = 0x133,
+ RTW89_HW_RATE_VHT_NSS4_MCS4 = 0x134,
+ RTW89_HW_RATE_VHT_NSS4_MCS5 = 0x135,
+ RTW89_HW_RATE_VHT_NSS4_MCS6 = 0x136,
+ RTW89_HW_RATE_VHT_NSS4_MCS7 = 0x137,
+ RTW89_HW_RATE_VHT_NSS4_MCS8 = 0x138,
+ RTW89_HW_RATE_VHT_NSS4_MCS9 = 0x139,
+ RTW89_HW_RATE_HE_NSS1_MCS0 = 0x180,
+ RTW89_HW_RATE_HE_NSS1_MCS1 = 0x181,
+ RTW89_HW_RATE_HE_NSS1_MCS2 = 0x182,
+ RTW89_HW_RATE_HE_NSS1_MCS3 = 0x183,
+ RTW89_HW_RATE_HE_NSS1_MCS4 = 0x184,
+ RTW89_HW_RATE_HE_NSS1_MCS5 = 0x185,
+ RTW89_HW_RATE_HE_NSS1_MCS6 = 0x186,
+ RTW89_HW_RATE_HE_NSS1_MCS7 = 0x187,
+ RTW89_HW_RATE_HE_NSS1_MCS8 = 0x188,
+ RTW89_HW_RATE_HE_NSS1_MCS9 = 0x189,
+ RTW89_HW_RATE_HE_NSS1_MCS10 = 0x18A,
+ RTW89_HW_RATE_HE_NSS1_MCS11 = 0x18B,
+ RTW89_HW_RATE_HE_NSS2_MCS0 = 0x190,
+ RTW89_HW_RATE_HE_NSS2_MCS1 = 0x191,
+ RTW89_HW_RATE_HE_NSS2_MCS2 = 0x192,
+ RTW89_HW_RATE_HE_NSS2_MCS3 = 0x193,
+ RTW89_HW_RATE_HE_NSS2_MCS4 = 0x194,
+ RTW89_HW_RATE_HE_NSS2_MCS5 = 0x195,
+ RTW89_HW_RATE_HE_NSS2_MCS6 = 0x196,
+ RTW89_HW_RATE_HE_NSS2_MCS7 = 0x197,
+ RTW89_HW_RATE_HE_NSS2_MCS8 = 0x198,
+ RTW89_HW_RATE_HE_NSS2_MCS9 = 0x199,
+ RTW89_HW_RATE_HE_NSS2_MCS10 = 0x19A,
+ RTW89_HW_RATE_HE_NSS2_MCS11 = 0x19B,
+ RTW89_HW_RATE_HE_NSS3_MCS0 = 0x1A0,
+ RTW89_HW_RATE_HE_NSS3_MCS1 = 0x1A1,
+ RTW89_HW_RATE_HE_NSS3_MCS2 = 0x1A2,
+ RTW89_HW_RATE_HE_NSS3_MCS3 = 0x1A3,
+ RTW89_HW_RATE_HE_NSS3_MCS4 = 0x1A4,
+ RTW89_HW_RATE_HE_NSS3_MCS5 = 0x1A5,
+ RTW89_HW_RATE_HE_NSS3_MCS6 = 0x1A6,
+ RTW89_HW_RATE_HE_NSS3_MCS7 = 0x1A7,
+ RTW89_HW_RATE_HE_NSS3_MCS8 = 0x1A8,
+ RTW89_HW_RATE_HE_NSS3_MCS9 = 0x1A9,
+ RTW89_HW_RATE_HE_NSS3_MCS10 = 0x1AA,
+ RTW89_HW_RATE_HE_NSS3_MCS11 = 0x1AB,
+ RTW89_HW_RATE_HE_NSS4_MCS0 = 0x1B0,
+ RTW89_HW_RATE_HE_NSS4_MCS1 = 0x1B1,
+ RTW89_HW_RATE_HE_NSS4_MCS2 = 0x1B2,
+ RTW89_HW_RATE_HE_NSS4_MCS3 = 0x1B3,
+ RTW89_HW_RATE_HE_NSS4_MCS4 = 0x1B4,
+ RTW89_HW_RATE_HE_NSS4_MCS5 = 0x1B5,
+ RTW89_HW_RATE_HE_NSS4_MCS6 = 0x1B6,
+ RTW89_HW_RATE_HE_NSS4_MCS7 = 0x1B7,
+ RTW89_HW_RATE_HE_NSS4_MCS8 = 0x1B8,
+ RTW89_HW_RATE_HE_NSS4_MCS9 = 0x1B9,
+ RTW89_HW_RATE_HE_NSS4_MCS10 = 0x1BA,
+ RTW89_HW_RATE_HE_NSS4_MCS11 = 0x1BB,
+ RTW89_HW_RATE_NR,
+
+ RTW89_HW_RATE_MASK_MOD = GENMASK(8, 7),
+ RTW89_HW_RATE_MASK_VAL = GENMASK(6, 0),
+};
+
+/* 2G channels,
+ * 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
+ */
+#define RTW89_2G_CH_NUM 14
+
+/* 5G channels,
+ * 36, 38, 40, 42, 44, 46, 48, 50,
+ * 52, 54, 56, 58, 60, 62, 64,
+ * 100, 102, 104, 106, 108, 110, 112, 114,
+ * 116, 118, 120, 122, 124, 126, 128, 130,
+ * 132, 134, 136, 138, 140, 142, 144,
+ * 149, 151, 153, 155, 157, 159, 161, 163,
+ * 165, 167, 169, 171, 173, 175, 177
+ */
+#define RTW89_5G_CH_NUM 53
+
+enum rtw89_rate_section {
+ RTW89_RS_CCK,
+ RTW89_RS_OFDM,
+ RTW89_RS_MCS, /* for HT/VHT/HE */
+ RTW89_RS_HEDCM,
+ RTW89_RS_OFFSET,
+ RTW89_RS_MAX,
+ RTW89_RS_LMT_NUM = RTW89_RS_MCS + 1,
+};
+
+enum rtw89_rate_max {
+ RTW89_RATE_CCK_MAX = 4,
+ RTW89_RATE_OFDM_MAX = 8,
+ RTW89_RATE_MCS_MAX = 12,
+ RTW89_RATE_HEDCM_MAX = 4, /* for HEDCM MCS0/1/3/4 */
+ RTW89_RATE_OFFSET_MAX = 5, /* for HE(HEDCM)/VHT/HT/OFDM/CCK offset */
+};
+
+enum rtw89_nss {
+ RTW89_NSS_1 = 0,
+ RTW89_NSS_2 = 1,
+ /* HE DCM only support 1ss and 2ss */
+ RTW89_NSS_HEDCM_MAX = RTW89_NSS_2 + 1,
+ RTW89_NSS_3 = 2,
+ RTW89_NSS_4 = 3,
+ RTW89_NSS_MAX,
+};
+
+enum rtw89_ntx {
+ RTW89_1TX = 0,
+ RTW89_2TX = 1,
+ RTW89_NTX_NUM,
+};
+
+enum rtw89_beamforming_type {
+ RTW89_NONBF = 0,
+ RTW89_BF = 1,
+ RTW89_BF_NUM,
+};
+
+enum rtw89_regulation_type {
+ RTW89_WW = 0,
+ RTW89_ETSI = 1,
+ RTW89_FCC = 2,
+ RTW89_MKK = 3,
+ RTW89_NA = 4,
+ RTW89_IC = 5,
+ RTW89_KCC = 6,
+ RTW89_NCC = 7,
+ RTW89_CHILE = 8,
+ RTW89_ACMA = 9,
+ RTW89_MEXICO = 10,
+ RTW89_UKRAINE = 11,
+ RTW89_CN = 12,
+ RTW89_REGD_NUM,
+};
+
+extern const u8 rtw89_rs_idx_max[RTW89_RS_MAX];
+extern const u8 rtw89_rs_nss_max[RTW89_RS_MAX];
+
+struct rtw89_txpwr_byrate {
+ s8 cck[RTW89_RATE_CCK_MAX];
+ s8 ofdm[RTW89_RATE_OFDM_MAX];
+ s8 mcs[RTW89_NSS_MAX][RTW89_RATE_MCS_MAX];
+ s8 hedcm[RTW89_NSS_HEDCM_MAX][RTW89_RATE_HEDCM_MAX];
+ s8 offset[RTW89_RATE_OFFSET_MAX];
+};
+
+enum rtw89_bandwidth_section_num {
+ RTW89_BW20_SEC_NUM = 8,
+ RTW89_BW40_SEC_NUM = 4,
+ RTW89_BW80_SEC_NUM = 2,
+};
+
+struct rtw89_txpwr_limit {
+ s8 cck_20m[RTW89_BF_NUM];
+ s8 cck_40m[RTW89_BF_NUM];
+ s8 ofdm[RTW89_BF_NUM];
+ s8 mcs_20m[RTW89_BW20_SEC_NUM][RTW89_BF_NUM];
+ s8 mcs_40m[RTW89_BW40_SEC_NUM][RTW89_BF_NUM];
+ s8 mcs_80m[RTW89_BW80_SEC_NUM][RTW89_BF_NUM];
+ s8 mcs_160m[RTW89_BF_NUM];
+ s8 mcs_40m_0p5[RTW89_BF_NUM];
+ s8 mcs_40m_2p5[RTW89_BF_NUM];
+};
+
+#define RTW89_RU_SEC_NUM 8
+
+struct rtw89_txpwr_limit_ru {
+ s8 ru26[RTW89_RU_SEC_NUM];
+ s8 ru52[RTW89_RU_SEC_NUM];
+ s8 ru106[RTW89_RU_SEC_NUM];
+};
+
+struct rtw89_rate_desc {
+ enum rtw89_nss nss;
+ enum rtw89_rate_section rs;
+ u8 idx;
+};
+
+#define PHY_STS_HDR_LEN 8
+#define RF_PATH_MAX 4
+#define RTW89_MAX_PPDU_CNT 8
+struct rtw89_rx_phy_ppdu {
+ u8 *buf;
+ u32 len;
+ u8 rssi_avg;
+ s8 rssi[RF_PATH_MAX];
+ u8 mac_id;
+ bool to_self;
+ bool valid;
+};
+
+enum rtw89_mac_idx {
+ RTW89_MAC_0 = 0,
+ RTW89_MAC_1 = 1,
+};
+
+enum rtw89_phy_idx {
+ RTW89_PHY_0 = 0,
+ RTW89_PHY_1 = 1,
+ RTW89_PHY_MAX
+};
+
+enum rtw89_rf_path {
+ RF_PATH_A = 0,
+ RF_PATH_B = 1,
+ RF_PATH_C = 2,
+ RF_PATH_D = 3,
+ RF_PATH_AB,
+ RF_PATH_AC,
+ RF_PATH_AD,
+ RF_PATH_BC,
+ RF_PATH_BD,
+ RF_PATH_CD,
+ RF_PATH_ABC,
+ RF_PATH_ABD,
+ RF_PATH_ACD,
+ RF_PATH_BCD,
+ RF_PATH_ABCD,
+};
+
+enum rtw89_rf_path_bit {
+ RF_A = BIT(0),
+ RF_B = BIT(1),
+ RF_C = BIT(2),
+ RF_D = BIT(3),
+
+ RF_AB = (RF_A | RF_B),
+ RF_AC = (RF_A | RF_C),
+ RF_AD = (RF_A | RF_D),
+ RF_BC = (RF_B | RF_C),
+ RF_BD = (RF_B | RF_D),
+ RF_CD = (RF_C | RF_D),
+
+ RF_ABC = (RF_A | RF_B | RF_C),
+ RF_ABD = (RF_A | RF_B | RF_D),
+ RF_ACD = (RF_A | RF_C | RF_D),
+ RF_BCD = (RF_B | RF_C | RF_D),
+
+ RF_ABCD = (RF_A | RF_B | RF_C | RF_D),
+};
+
+enum rtw89_bandwidth {
+ RTW89_CHANNEL_WIDTH_20 = 0,
+ RTW89_CHANNEL_WIDTH_40 = 1,
+ RTW89_CHANNEL_WIDTH_80 = 2,
+ RTW89_CHANNEL_WIDTH_160 = 3,
+ RTW89_CHANNEL_WIDTH_80_80 = 4,
+ RTW89_CHANNEL_WIDTH_5 = 5,
+ RTW89_CHANNEL_WIDTH_10 = 6,
+};
+
+enum rtw89_ps_mode {
+ RTW89_PS_MODE_NONE = 0,
+ RTW89_PS_MODE_RFOFF = 1,
+ RTW89_PS_MODE_CLK_GATED = 2,
+ RTW89_PS_MODE_PWR_GATED = 3,
+};
+
+#define RTW89_MAX_CHANNEL_WIDTH RTW89_CHANNEL_WIDTH_80
+#define RTW89_2G_BW_NUM (RTW89_CHANNEL_WIDTH_40 + 1)
+#define RTW89_5G_BW_NUM (RTW89_CHANNEL_WIDTH_80 + 1)
+#define RTW89_PPE_BW_NUM (RTW89_CHANNEL_WIDTH_80 + 1)
+
+enum rtw89_ru_bandwidth {
+ RTW89_RU26 = 0,
+ RTW89_RU52 = 1,
+ RTW89_RU106 = 2,
+ RTW89_RU_NUM,
+};
+
+enum rtw89_sc_offset {
+ RTW89_SC_DONT_CARE = 0,
+ RTW89_SC_20_UPPER = 1,
+ RTW89_SC_20_LOWER = 2,
+ RTW89_SC_20_UPMOST = 3,
+ RTW89_SC_20_LOWEST = 4,
+ RTW89_SC_40_UPPER = 9,
+ RTW89_SC_40_LOWER = 10,
+};
+
+struct rtw89_channel_params {
+ u8 center_chan;
+ u8 primary_chan;
+ u8 bandwidth;
+ u8 pri_ch_idx;
+ u8 cch_by_bw[RTW89_MAX_CHANNEL_WIDTH + 1];
+};
+
+struct rtw89_channel_help_params {
+ u16 tx_en;
+};
+
+struct rtw89_port_reg {
+ u32 port_cfg;
+ u32 tbtt_prohib;
+ u32 bcn_area;
+ u32 bcn_early;
+ u32 tbtt_early;
+ u32 tbtt_agg;
+ u32 bcn_space;
+ u32 bcn_forcetx;
+ u32 bcn_err_cnt;
+ u32 bcn_err_flag;
+ u32 dtim_ctrl;
+ u32 tbtt_shift;
+ u32 bcn_cnt_tmr;
+ u32 tsftr_l;
+ u32 tsftr_h;
+};
+
+struct rtw89_txwd_body {
+ __le32 dword0;
+ __le32 dword1;
+ __le32 dword2;
+ __le32 dword3;
+ __le32 dword4;
+ __le32 dword5;
+} __packed;
+
+struct rtw89_txwd_info {
+ __le32 dword0;
+ __le32 dword1;
+ __le32 dword2;
+ __le32 dword3;
+ __le32 dword4;
+ __le32 dword5;
+} __packed;
+
+struct rtw89_rx_desc_info {
+ u16 pkt_size;
+ u8 pkt_type;
+ u8 drv_info_size;
+ u8 shift;
+ u8 wl_hd_iv_len;
+ bool long_rxdesc;
+ bool bb_sel;
+ bool mac_info_valid;
+ u16 data_rate;
+ u8 gi_ltf;
+ u8 bw;
+ u32 free_run_cnt;
+ u8 user_id;
+ bool sr_en;
+ u8 ppdu_cnt;
+ u8 ppdu_type;
+ bool icv_err;
+ bool crc32_err;
+ bool hw_dec;
+ bool sw_dec;
+ bool addr1_match;
+ u8 frag;
+ u16 seq;
+ u8 frame_type;
+ u8 rx_pl_id;
+ bool addr_cam_valid;
+ u8 addr_cam_id;
+ u8 sec_cam_id;
+ u8 mac_id;
+ u16 offset;
+ bool ready;
+};
+
+struct rtw89_rxdesc_short {
+ __le32 dword0;
+ __le32 dword1;
+ __le32 dword2;
+ __le32 dword3;
+} __packed;
+
+struct rtw89_rxdesc_long {
+ __le32 dword0;
+ __le32 dword1;
+ __le32 dword2;
+ __le32 dword3;
+ __le32 dword4;
+ __le32 dword5;
+ __le32 dword6;
+ __le32 dword7;
+} __packed;
+
+struct rtw89_tx_desc_info {
+ u16 pkt_size;
+ u8 wp_offset;
+ u8 qsel;
+ u8 ch_dma;
+ u8 hdr_llc_len;
+ bool is_bmc;
+ bool en_wd_info;
+ bool wd_page;
+ bool use_rate;
+ bool dis_data_fb;
+ bool tid_indicate;
+ bool agg_en;
+ bool bk;
+ u8 ampdu_density;
+ u8 ampdu_num;
+ bool sec_en;
+ u8 sec_type;
+ u8 sec_cam_idx;
+ u16 data_rate;
+ bool fw_dl;
+ u16 seq;
+ bool a_ctrl_bsr;
+};
+
+struct rtw89_core_tx_request {
+ enum rtw89_core_tx_type tx_type;
+
+ struct sk_buff *skb;
+ struct ieee80211_vif *vif;
+ struct ieee80211_sta *sta;
+ struct rtw89_tx_desc_info desc_info;
+};
+
+struct rtw89_txq {
+ struct list_head list;
+ unsigned long flags;
+ int wait_cnt;
+};
+
+struct rtw89_mac_ax_gnt {
+ u8 gnt_bt_sw_en;
+ u8 gnt_bt;
+ u8 gnt_wl_sw_en;
+ u8 gnt_wl;
+};
+
+#define RTW89_MAC_AX_COEX_GNT_NR 2
+struct rtw89_mac_ax_coex_gnt {
+ struct rtw89_mac_ax_gnt band[RTW89_MAC_AX_COEX_GNT_NR];
+};
+
+enum rtw89_btc_ncnt {
+ BTC_NCNT_POWER_ON = 0x0,
+ BTC_NCNT_POWER_OFF,
+ BTC_NCNT_INIT_COEX,
+ BTC_NCNT_SCAN_START,
+ BTC_NCNT_SCAN_FINISH,
+ BTC_NCNT_SPECIAL_PACKET,
+ BTC_NCNT_SWITCH_BAND,
+ BTC_NCNT_RFK_TIMEOUT,
+ BTC_NCNT_SHOW_COEX_INFO,
+ BTC_NCNT_ROLE_INFO,
+ BTC_NCNT_CONTROL,
+ BTC_NCNT_RADIO_STATE,
+ BTC_NCNT_CUSTOMERIZE,
+ BTC_NCNT_WL_RFK,
+ BTC_NCNT_WL_STA,
+ BTC_NCNT_FWINFO,
+ BTC_NCNT_TIMER,
+ BTC_NCNT_NUM
+};
+
+enum rtw89_btc_btinfo {
+ BTC_BTINFO_L0 = 0,
+ BTC_BTINFO_L1,
+ BTC_BTINFO_L2,
+ BTC_BTINFO_L3,
+ BTC_BTINFO_H0,
+ BTC_BTINFO_H1,
+ BTC_BTINFO_H2,
+ BTC_BTINFO_H3,
+ BTC_BTINFO_MAX
+};
+
+enum rtw89_btc_dcnt {
+ BTC_DCNT_RUN = 0x0,
+ BTC_DCNT_CX_RUNINFO,
+ BTC_DCNT_RPT,
+ BTC_DCNT_RPT_FREEZE,
+ BTC_DCNT_CYCLE,
+ BTC_DCNT_CYCLE_FREEZE,
+ BTC_DCNT_W1,
+ BTC_DCNT_W1_FREEZE,
+ BTC_DCNT_B1,
+ BTC_DCNT_B1_FREEZE,
+ BTC_DCNT_TDMA_NONSYNC,
+ BTC_DCNT_SLOT_NONSYNC,
+ BTC_DCNT_BTCNT_FREEZE,
+ BTC_DCNT_WL_SLOT_DRIFT,
+ BTC_DCNT_WL_STA_LAST,
+ BTC_DCNT_NUM,
+};
+
+enum rtw89_btc_wl_state_cnt {
+ BTC_WCNT_SCANAP = 0x0,
+ BTC_WCNT_DHCP,
+ BTC_WCNT_EAPOL,
+ BTC_WCNT_ARP,
+ BTC_WCNT_SCBDUPDATE,
+ BTC_WCNT_RFK_REQ,
+ BTC_WCNT_RFK_GO,
+ BTC_WCNT_RFK_REJECT,
+ BTC_WCNT_RFK_TIMEOUT,
+ BTC_WCNT_CH_UPDATE,
+ BTC_WCNT_NUM
+};
+
+enum rtw89_btc_bt_state_cnt {
+ BTC_BCNT_RETRY = 0x0,
+ BTC_BCNT_REINIT,
+ BTC_BCNT_REENABLE,
+ BTC_BCNT_SCBDREAD,
+ BTC_BCNT_RELINK,
+ BTC_BCNT_IGNOWL,
+ BTC_BCNT_INQPAG,
+ BTC_BCNT_INQ,
+ BTC_BCNT_PAGE,
+ BTC_BCNT_ROLESW,
+ BTC_BCNT_AFH,
+ BTC_BCNT_INFOUPDATE,
+ BTC_BCNT_INFOSAME,
+ BTC_BCNT_SCBDUPDATE,
+ BTC_BCNT_HIPRI_TX,
+ BTC_BCNT_HIPRI_RX,
+ BTC_BCNT_LOPRI_TX,
+ BTC_BCNT_LOPRI_RX,
+ BTC_BCNT_RATECHG,
+ BTC_BCNT_NUM
+};
+
+enum rtw89_btc_bt_profile {
+ BTC_BT_NOPROFILE = 0,
+ BTC_BT_HFP = BIT(0),
+ BTC_BT_HID = BIT(1),
+ BTC_BT_A2DP = BIT(2),
+ BTC_BT_PAN = BIT(3),
+ BTC_PROFILE_MAX = 4,
+};
+
+struct rtw89_btc_ant_info {
+ u8 type; /* shared, dedicated */
+ u8 num;
+ u8 isolation;
+
+ u8 single_pos: 1;/* Single antenna at S0 or S1 */
+ u8 diversity: 1;
+};
+
+enum rtw89_tfc_dir {
+ RTW89_TFC_UL,
+ RTW89_TFC_DL,
+};
+
+struct rtw89_btc_wl_smap {
+ u32 busy: 1;
+ u32 scan: 1;
+ u32 connecting: 1;
+ u32 roaming: 1;
+ u32 _4way: 1;
+ u32 rf_off: 1;
+ u32 lps: 1;
+ u32 ips: 1;
+ u32 init_ok: 1;
+ u32 traffic_dir : 2;
+ u32 rf_off_pre: 1;
+ u32 lps_pre: 1;
+};
+
+enum rtw89_tfc_lv {
+ RTW89_TFC_IDLE,
+ RTW89_TFC_ULTRA_LOW,
+ RTW89_TFC_LOW,
+ RTW89_TFC_MID,
+ RTW89_TFC_HIGH,
+};
+
+#define RTW89_TP_SHIFT 18 /* bytes/2s --> Mbps */
+DECLARE_EWMA(tp, 10, 2);
+
+struct rtw89_traffic_stats {
+ /* units in bytes */
+ u64 tx_unicast;
+ u64 rx_unicast;
+ u32 tx_avg_len;
+ u32 rx_avg_len;
+
+ /* count for packets */
+ u64 tx_cnt;
+ u64 rx_cnt;
+
+ /* units in Mbps */
+ u32 tx_throughput;
+ u32 rx_throughput;
+ u32 tx_throughput_raw;
+ u32 rx_throughput_raw;
+ enum rtw89_tfc_lv tx_tfc_lv;
+ enum rtw89_tfc_lv rx_tfc_lv;
+ struct ewma_tp tx_ewma_tp;
+ struct ewma_tp rx_ewma_tp;
+
+ u16 tx_rate;
+ u16 rx_rate;
+};
+
+struct rtw89_btc_statistic {
+ u8 rssi; /* 0%~110% (dBm = rssi -110) */
+ struct rtw89_traffic_stats traffic;
+};
+
+#define BTC_WL_RSSI_THMAX 4
+
+struct rtw89_btc_wl_link_info {
+ struct rtw89_btc_statistic stat;
+ enum rtw89_tfc_dir dir;
+ u8 rssi_state[BTC_WL_RSSI_THMAX];
+ u8 mac_addr[ETH_ALEN];
+ u8 busy;
+ u8 ch;
+ u8 bw;
+ u8 band;
+ u8 role;
+ u8 pid;
+ u8 phy;
+ u8 dtim_period;
+ u8 mode;
+
+ u8 mac_id;
+ u8 tx_retry;
+
+ u32 bcn_period;
+ u32 busy_t;
+ u32 tx_time;
+ u32 client_cnt;
+ u32 rx_rate_drop_cnt;
+
+ u32 active: 1;
+ u32 noa: 1;
+ u32 client_ps: 1;
+ u32 connected: 2;
+};
+
+union rtw89_btc_wl_state_map {
+ u32 val;
+ struct rtw89_btc_wl_smap map;
+};
+
+struct rtw89_btc_bt_hfp_desc {
+ u32 exist: 1;
+ u32 type: 2;
+ u32 rsvd: 29;
+};
+
+struct rtw89_btc_bt_hid_desc {
+ u32 exist: 1;
+ u32 slot_info: 2;
+ u32 pair_cnt: 2;
+ u32 type: 8;
+ u32 rsvd: 19;
+};
+
+struct rtw89_btc_bt_a2dp_desc {
+ u8 exist: 1;
+ u8 exist_last: 1;
+ u8 play_latency: 1;
+ u8 type: 3;
+ u8 active: 1;
+ u8 sink: 1;
+
+ u8 bitpool;
+ u16 vendor_id;
+ u32 device_name;
+ u32 flush_time;
+};
+
+struct rtw89_btc_bt_pan_desc {
+ u32 exist: 1;
+ u32 type: 1;
+ u32 active: 1;
+ u32 rsvd: 29;
+};
+
+struct rtw89_btc_bt_rfk_info {
+ u32 run: 1;
+ u32 req: 1;
+ u32 timeout: 1;
+ u32 rsvd: 29;
+};
+
+union rtw89_btc_bt_rfk_info_map {
+ u32 val;
+ struct rtw89_btc_bt_rfk_info map;
+};
+
+struct rtw89_btc_bt_ver_info {
+ u32 fw_coex; /* match with which coex_ver */
+ u32 fw;
+};
+
+struct rtw89_btc_bool_sta_chg {
+ u32 now: 1;
+ u32 last: 1;
+ u32 remain: 1;
+ u32 srvd: 29;
+};
+
+struct rtw89_btc_u8_sta_chg {
+ u8 now;
+ u8 last;
+ u8 remain;
+ u8 rsvd;
+};
+
+struct rtw89_btc_wl_scan_info {
+ u8 band[RTW89_PHY_MAX];
+ u8 phy_map;
+ u8 rsvd;
+};
+
+struct rtw89_btc_wl_dbcc_info {
+ u8 op_band[RTW89_PHY_MAX]; /* op band in each phy */
+ u8 scan_band[RTW89_PHY_MAX]; /* scan band in each phy */
+ u8 real_band[RTW89_PHY_MAX];
+ u8 role[RTW89_PHY_MAX]; /* role in each phy */
+};
+
+struct rtw89_btc_wl_active_role {
+ u8 connected: 1;
+ u8 pid: 3;
+ u8 phy: 1;
+ u8 noa: 1;
+ u8 band: 2;
+
+ u8 client_ps: 1;
+ u8 bw: 7;
+
+ u8 role;
+ u8 ch;
+
+ u16 tx_lvl;
+ u16 rx_lvl;
+ u16 tx_rate;
+ u16 rx_rate;
+};
+
+struct rtw89_btc_wl_role_info_bpos {
+ u16 none: 1;
+ u16 station: 1;
+ u16 ap: 1;
+ u16 vap: 1;
+ u16 adhoc: 1;
+ u16 adhoc_master: 1;
+ u16 mesh: 1;
+ u16 moniter: 1;
+ u16 p2p_device: 1;
+ u16 p2p_gc: 1;
+ u16 p2p_go: 1;
+ u16 nan: 1;
+};
+
+union rtw89_btc_wl_role_info_map {
+ u16 val;
+ struct rtw89_btc_wl_role_info_bpos role;
+};
+
+struct rtw89_btc_wl_role_info { /* struct size must be n*4 bytes */
+ u8 connect_cnt;
+ u8 link_mode;
+ union rtw89_btc_wl_role_info_map role_map;
+ struct rtw89_btc_wl_active_role active_role[RTW89_MAX_HW_PORT_NUM];
+};
+
+struct rtw89_btc_wl_ver_info {
+ u32 fw_coex; /* match with which coex_ver */
+ u32 fw;
+ u32 mac;
+ u32 bb;
+ u32 rf;
+};
+
+struct rtw89_btc_wl_afh_info {
+ u8 en;
+ u8 ch;
+ u8 bw;
+ u8 rsvd;
+} __packed;
+
+struct rtw89_btc_wl_rfk_info {
+ u32 state: 2;
+ u32 path_map: 4;
+ u32 phy_map: 2;
+ u32 band: 2;
+ u32 type: 8;
+ u32 rsvd: 14;
+};
+
+struct rtw89_btc_bt_smap {
+ u32 connect: 1;
+ u32 ble_connect: 1;
+ u32 acl_busy: 1;
+ u32 sco_busy: 1;
+ u32 mesh_busy: 1;
+ u32 inq_pag: 1;
+};
+
+union rtw89_btc_bt_state_map {
+ u32 val;
+ struct rtw89_btc_bt_smap map;
+};
+
+#define BTC_BT_RSSI_THMAX 4
+#define BTC_BT_AFH_GROUP 12
+
+struct rtw89_btc_bt_link_info {
+ struct rtw89_btc_u8_sta_chg profile_cnt;
+ struct rtw89_btc_bool_sta_chg multi_link;
+ struct rtw89_btc_bool_sta_chg relink;
+ struct rtw89_btc_bt_hfp_desc hfp_desc;
+ struct rtw89_btc_bt_hid_desc hid_desc;
+ struct rtw89_btc_bt_a2dp_desc a2dp_desc;
+ struct rtw89_btc_bt_pan_desc pan_desc;
+ union rtw89_btc_bt_state_map status;
+
+ u8 sut_pwr_level[BTC_PROFILE_MAX];
+ u8 golden_rx_shift[BTC_PROFILE_MAX];
+ u8 rssi_state[BTC_BT_RSSI_THMAX];
+ u8 afh_map[BTC_BT_AFH_GROUP];
+
+ u32 role_sw: 1;
+ u32 slave_role: 1;
+ u32 afh_update: 1;
+ u32 cqddr: 1;
+ u32 rssi: 8;
+ u32 tx_3m: 1;
+ u32 rsvd: 19;
+};
+
+struct rtw89_btc_3rdcx_info {
+ u8 type; /* 0: none, 1:zigbee, 2:LTE */
+ u8 hw_coex;
+ u16 rsvd;
+};
+
+struct rtw89_btc_dm_emap {
+ u32 init: 1;
+ u32 pta_owner: 1;
+ u32 wl_rfk_timeout: 1;
+ u32 bt_rfk_timeout: 1;
+
+ u32 wl_fw_hang: 1;
+ u32 offload_mismatch: 1;
+ u32 cycle_hang: 1;
+ u32 w1_hang: 1;
+
+ u32 b1_hang: 1;
+ u32 tdma_no_sync: 1;
+ u32 wl_slot_drift: 1;
+};
+
+union rtw89_btc_dm_error_map {
+ u32 val;
+ struct rtw89_btc_dm_emap map;
+};
+
+struct rtw89_btc_rf_para {
+ u32 tx_pwr_freerun;
+ u32 rx_gain_freerun;
+ u32 tx_pwr_perpkt;
+ u32 rx_gain_perpkt;
+};
+
+struct rtw89_btc_wl_info {
+ struct rtw89_btc_wl_link_info link_info[RTW89_MAX_HW_PORT_NUM];
+ struct rtw89_btc_wl_rfk_info rfk_info;
+ struct rtw89_btc_wl_ver_info ver_info;
+ struct rtw89_btc_wl_afh_info afh_info;
+ struct rtw89_btc_wl_role_info role_info;
+ struct rtw89_btc_wl_scan_info scan_info;
+ struct rtw89_btc_wl_dbcc_info dbcc_info;
+ struct rtw89_btc_rf_para rf_para;
+ union rtw89_btc_wl_state_map status;
+
+ u8 port_id[RTW89_WIFI_ROLE_MLME_MAX];
+ u8 rssi_level;
+
+ u32 scbd;
+};
+
+struct rtw89_btc_module {
+ struct rtw89_btc_ant_info ant;
+ u8 rfe_type;
+ u8 cv;
+
+ u8 bt_solo: 1;
+ u8 bt_pos: 1;
+ u8 switch_type: 1;
+
+ u8 rsvd;
+};
+
+#define RTW89_BTC_DM_MAXSTEP 30
+#define RTW89_BTC_DM_CNT_MAX (RTW89_BTC_DM_MAXSTEP * 8)
+
+struct rtw89_btc_dm_step {
+ u16 step[RTW89_BTC_DM_MAXSTEP];
+ u8 step_pos;
+ bool step_ov;
+};
+
+struct rtw89_btc_init_info {
+ struct rtw89_btc_module module;
+ u8 wl_guard_ch;
+
+ u8 wl_only: 1;
+ u8 wl_init_ok: 1;
+ u8 dbcc_en: 1;
+ u8 cx_other: 1;
+ u8 bt_only: 1;
+
+ u16 rsvd;
+};
+
+struct rtw89_btc_wl_tx_limit_para {
+ u16 enable;
+ u32 tx_time; /* unit: us */
+ u16 tx_retry;
+};
+
+struct rtw89_btc_bt_scan_info {
+ u16 win;
+ u16 intvl;
+ u32 enable: 1;
+ u32 interlace: 1;
+ u32 rsvd: 30;
+};
+
+enum rtw89_btc_bt_scan_type {
+ BTC_SCAN_INQ = 0,
+ BTC_SCAN_PAGE,
+ BTC_SCAN_BLE,
+ BTC_SCAN_INIT,
+ BTC_SCAN_TV,
+ BTC_SCAN_ADV,
+ BTC_SCAN_MAX1,
+};
+
+struct rtw89_btc_bt_info {
+ struct rtw89_btc_bt_link_info link_info;
+ struct rtw89_btc_bt_scan_info scan_info[BTC_SCAN_MAX1];
+ struct rtw89_btc_bt_ver_info ver_info;
+ struct rtw89_btc_bool_sta_chg enable;
+ struct rtw89_btc_bool_sta_chg inq_pag;
+ struct rtw89_btc_rf_para rf_para;
+ union rtw89_btc_bt_rfk_info_map rfk_info;
+
+ u8 raw_info[BTC_BTINFO_MAX]; /* raw bt info from mailbox */
+
+ u32 scbd;
+ u32 feature;
+
+ u32 mbx_avl: 1;
+ u32 whql_test: 1;
+ u32 igno_wl: 1;
+ u32 reinit: 1;
+ u32 ble_scan_en: 1;
+ u32 btg_type: 1;
+ u32 inq: 1;
+ u32 pag: 1;
+ u32 run_patch_code: 1;
+ u32 hi_lna_rx: 1;
+ u32 rsvd: 22;
+};
+
+struct rtw89_btc_cx {
+ struct rtw89_btc_wl_info wl;
+ struct rtw89_btc_bt_info bt;
+ struct rtw89_btc_3rdcx_info other;
+ u32 state_map;
+ u32 cnt_bt[BTC_BCNT_NUM];
+ u32 cnt_wl[BTC_WCNT_NUM];
+};
+
+struct rtw89_btc_fbtc_tdma {
+ u8 type;
+ u8 rxflctrl;
+ u8 txpause;
+ u8 wtgle_n;
+ u8 leak_n;
+ u8 ext_ctrl;
+ u8 rsvd0;
+ u8 rsvd1;
+} __packed;
+
+#define CXMREG_MAX 30
+#define FCXMAX_STEP 255 /*STEP trace record cnt, Max:65535, default:255*/
+#define BTCRPT_VER 1
+#define BTC_CYCLE_SLOT_MAX 48 /* must be even number, non-zero */
+
+enum rtw89_btc_bt_rfk_counter {
+ BTC_BCNT_RFK_REQ = 0,
+ BTC_BCNT_RFK_GO = 1,
+ BTC_BCNT_RFK_REJECT = 2,
+ BTC_BCNT_RFK_FAIL = 3,
+ BTC_BCNT_RFK_TIMEOUT = 4,
+ BTC_BCNT_RFK_MAX
+};
+
+struct rtw89_btc_fbtc_rpt_ctrl {
+ u16 fver;
+ u16 rpt_cnt; /* tmr counters */
+ u32 wl_fw_coex_ver; /* match which driver's coex version */
+ u32 wl_fw_cx_offload;
+ u32 wl_fw_ver;
+ u32 rpt_enable;
+ u32 rpt_para; /* ms */
+ u32 mb_send_fail_cnt; /* fw send mailbox fail counter */
+ u32 mb_send_ok_cnt; /* fw send mailbox ok counter */
+ u32 mb_recv_cnt; /* fw recv mailbox counter */
+ u32 mb_a2dp_empty_cnt; /* a2dp empty count */
+ u32 mb_a2dp_flct_cnt; /* a2dp empty flow control counter */
+ u32 mb_a2dp_full_cnt; /* a2dp empty full counter */
+ u32 bt_rfk_cnt[BTC_BCNT_RFK_MAX];
+ u32 c2h_cnt; /* fw send c2h counter */
+ u32 h2c_cnt; /* fw recv h2c counter */
+} __packed;
+
+enum rtw89_fbtc_ext_ctrl_type {
+ CXECTL_OFF = 0x0, /* tdma off */
+ CXECTL_B2 = 0x1, /* allow B2 (beacon-early) */
+ CXECTL_EXT = 0x2,
+ CXECTL_MAX
+};
+
+union rtw89_btc_fbtc_rxflct {
+ u8 val;
+ u8 type: 3;
+ u8 tgln_n: 5;
+};
+
+enum rtw89_btc_cxst_state {
+ CXST_OFF = 0x0,
+ CXST_B2W = 0x1,
+ CXST_W1 = 0x2,
+ CXST_W2 = 0x3,
+ CXST_W2B = 0x4,
+ CXST_B1 = 0x5,
+ CXST_B2 = 0x6,
+ CXST_B3 = 0x7,
+ CXST_B4 = 0x8,
+ CXST_LK = 0x9,
+ CXST_BLK = 0xa,
+ CXST_E2G = 0xb,
+ CXST_E5G = 0xc,
+ CXST_EBT = 0xd,
+ CXST_ENULL = 0xe,
+ CXST_WLK = 0xf,
+ CXST_W1FDD = 0x10,
+ CXST_B1FDD = 0x11,
+ CXST_MAX = 0x12,
+};
+
+enum {
+ CXBCN_ALL = 0x0,
+ CXBCN_ALL_OK,
+ CXBCN_BT_SLOT,
+ CXBCN_BT_OK,
+ CXBCN_MAX
+};
+
+enum btc_slot_type {
+ SLOT_MIX = 0x0, /* accept BT Lower-Pri Tx/Rx request 0x778 = 1 */
+ SLOT_ISO = 0x1, /* no accept BT Lower-Pri Tx/Rx request 0x778 = d*/
+ CXSTYPE_NUM,
+};
+
+enum { /* TIME */
+ CXT_BT = 0x0,
+ CXT_WL = 0x1,
+ CXT_MAX
+};
+
+enum { /* TIME-A2DP */
+ CXT_FLCTRL_OFF = 0x0,
+ CXT_FLCTRL_ON = 0x1,
+ CXT_FLCTRL_MAX
+};
+
+enum { /* STEP TYPE */
+ CXSTEP_NONE = 0x0,
+ CXSTEP_EVNT = 0x1,
+ CXSTEP_SLOT = 0x2,
+ CXSTEP_MAX,
+};
+
+#define FCXGPIODBG_VER 1
+#define BTC_DBG_MAX1 32
+struct rtw89_btc_fbtc_gpio_dbg {
+ u8 fver;
+ u8 rsvd;
+ u16 rsvd2;
+ u32 en_map; /* which debug signal (see btc_wl_gpio_debug) is enable */
+ u32 pre_state; /* the debug signal is 1 or 0 */
+ u8 gpio_map[BTC_DBG_MAX1]; /*the debug signals to GPIO-Position */
+} __packed;
+
+#define FCXMREG_VER 1
+struct rtw89_btc_fbtc_mreg_val {
+ u8 fver;
+ u8 reg_num;
+ __le16 rsvd;
+ __le32 mreg_val[CXMREG_MAX];
+} __packed;
+
+#define RTW89_DEF_FBTC_MREG(__type, __bytes, __offset) \
+ { .type = cpu_to_le16(__type), .bytes = cpu_to_le16(__bytes), \
+ .offset = cpu_to_le32(__offset), }
+
+struct rtw89_btc_fbtc_mreg {
+ __le16 type;
+ __le16 bytes;
+ __le32 offset;
+} __packed;
+
+struct rtw89_btc_fbtc_slot {
+ __le16 dur;
+ __le32 cxtbl;
+ __le16 cxtype;
+} __packed;
+
+#define FCXSLOTS_VER 1
+struct rtw89_btc_fbtc_slots {
+ u8 fver;
+ u8 tbl_num;
+ __le16 rsvd;
+ __le32 update_map;
+ struct rtw89_btc_fbtc_slot slot[CXST_MAX];
+} __packed;
+
+#define FCXSTEP_VER 2
+struct rtw89_btc_fbtc_step {
+ u8 type;
+ u8 val;
+ __le16 difft;
+} __packed;
+
+struct rtw89_btc_fbtc_steps {
+ u8 fver;
+ u8 rsvd;
+ __le16 cnt;
+ __le16 pos_old;
+ __le16 pos_new;
+ struct rtw89_btc_fbtc_step step[FCXMAX_STEP];
+} __packed;
+
+#define FCXCYSTA_VER 2
+struct rtw89_btc_fbtc_cysta { /* statistics for cycles */
+ u8 fver;
+ u8 rsvd;
+ __le16 cycles; /* total cycle number */
+ __le16 cycles_a2dp[CXT_FLCTRL_MAX];
+ __le16 a2dpept; /* a2dp empty cnt */
+ __le16 a2dpeptto; /* a2dp empty timeout cnt*/
+ __le16 tavg_cycle[CXT_MAX]; /* avg wl/bt cycle time */
+ __le16 tmax_cycle[CXT_MAX]; /* max wl/bt cycle time */
+ __le16 tmaxdiff_cycle[CXT_MAX]; /* max wl-wl bt-bt cycle diff time */
+ __le16 tavg_a2dp[CXT_FLCTRL_MAX]; /* avg a2dp PSTDMA/TDMA time */
+ __le16 tmax_a2dp[CXT_FLCTRL_MAX]; /* max a2dp PSTDMA/TDMA time */
+ __le16 tavg_a2dpept; /* avg a2dp empty time */
+ __le16 tmax_a2dpept; /* max a2dp empty time */
+ __le16 tavg_lk; /* avg leak-slot time */
+ __le16 tmax_lk; /* max leak-slot time */
+ __le32 slot_cnt[CXST_MAX]; /* slot count */
+ __le32 bcn_cnt[CXBCN_MAX];
+ __le32 leakrx_cnt; /* the rximr occur at leak slot */
+ __le32 collision_cnt; /* counter for event/timer occur at same time */
+ __le32 skip_cnt;
+ __le32 exception;
+ __le32 except_cnt;
+#if (FCXCYSTA_VER > 1)
+ __le16 tslot_cycle[BTC_CYCLE_SLOT_MAX];
+#endif
+} __packed;
+
+#define FCXNULLSTA_VER 1
+struct rtw89_btc_fbtc_cynullsta { /* cycle null statistics */
+ u8 fver;
+ u8 rsvd;
+ __le16 rsvd2;
+ __le32 max_t[2]; /* max_t for 0:null0/1:null1 */
+ __le32 avg_t[2]; /* avg_t for 0:null0/1:null1 */
+ __le32 result[2][4]; /* 0:fail, 1:ok, 2:on_time, 3:retry */
+} __packed;
+
+#define FCX_BTVER_VER 1
+struct rtw89_btc_fbtc_btver {
+ u8 fver;
+ u8 rsvd;
+ __le16 rsvd2;
+ __le32 coex_ver; /*bit[15:8]->shared, bit[7:0]->non-shared */
+ __le32 fw_ver;
+ __le32 feature;
+} __packed;
+
+#define FCX_BTSCAN_VER 1
+struct rtw89_btc_fbtc_btscan {
+ u8 fver;
+ u8 rsvd;
+ __le16 rsvd2;
+ u8 scan[6];
+} __packed;
+
+#define FCX_BTAFH_VER 1
+struct rtw89_btc_fbtc_btafh {
+ u8 fver;
+ u8 rsvd;
+ __le16 rsvd2;
+ u8 afh_l[4]; /*bit0:2402, bit1: 2403.... bit31:2433 */
+ u8 afh_m[4]; /*bit0:2434, bit1: 2435.... bit31:2465 */
+ u8 afh_h[4]; /*bit0:2466, bit1:2467......bit14:2480 */
+} __packed;
+
+#define FCX_BTDEVINFO_VER 1
+struct rtw89_btc_fbtc_btdevinfo {
+ u8 fver;
+ u8 rsvd;
+ __le16 vendor_id;
+ __le32 dev_name; /* only 24 bits valid */
+ __le32 flush_time;
+} __packed;
+
+#define RTW89_BTC_WL_DEF_TX_PWR GENMASK(7, 0)
+struct rtw89_btc_rf_trx_para {
+ u32 wl_tx_power; /* absolute Tx power (dBm), 0xff-> no BTC control */
+ u32 wl_rx_gain; /* rx gain table index (TBD.) */
+ u8 bt_tx_power; /* decrease Tx power (dB) */
+ u8 bt_rx_gain; /* LNA constrain level */
+};
+
+struct rtw89_btc_dm {
+ struct rtw89_btc_fbtc_slot slot[CXST_MAX];
+ struct rtw89_btc_fbtc_slot slot_now[CXST_MAX];
+ struct rtw89_btc_fbtc_tdma tdma;
+ struct rtw89_btc_fbtc_tdma tdma_now;
+ struct rtw89_mac_ax_coex_gnt gnt;
+ struct rtw89_btc_init_info init_info; /* pass to wl_fw if offload */
+ struct rtw89_btc_rf_trx_para rf_trx_para;
+ struct rtw89_btc_wl_tx_limit_para wl_tx_limit;
+ struct rtw89_btc_dm_step dm_step;
+ union rtw89_btc_dm_error_map error;
+ u32 cnt_dm[BTC_DCNT_NUM];
+ u32 cnt_notify[BTC_NCNT_NUM];
+
+ u32 update_slot_map;
+ u32 set_ant_path;
+
+ u32 wl_only: 1;
+ u32 wl_fw_cx_offload: 1;
+ u32 freerun: 1;
+ u32 wl_ps_ctrl: 2;
+ u32 wl_mimo_ps: 1;
+ u32 leak_ap: 1;
+ u32 noisy_level: 3;
+ u32 coex_info_map: 8;
+ u32 bt_only: 1;
+ u32 wl_btg_rx: 1;
+ u32 trx_para_level: 8;
+ u32 rsvd: 4;
+
+ u16 slot_dur[CXST_MAX];
+
+ u8 run_reason;
+ u8 run_action;
+};
+
+struct rtw89_btc_ctrl {
+ u32 manual: 1;
+ u32 igno_bt: 1;
+ u32 always_freerun: 1;
+ u32 trace_step: 16;
+ u32 rsvd: 12;
+};
+
+struct rtw89_btc_dbg {
+ /* cmd "rb" */
+ bool rb_done;
+ u32 rb_val;
+};
+
+#define FCXTDMA_VER 1
+
+enum rtw89_btc_btf_fw_event {
+ BTF_EVNT_RPT = 0,
+ BTF_EVNT_BT_INFO = 1,
+ BTF_EVNT_BT_SCBD = 2,
+ BTF_EVNT_BT_REG = 3,
+ BTF_EVNT_CX_RUNINFO = 4,
+ BTF_EVNT_BT_PSD = 5,
+ BTF_EVNT_BUF_OVERFLOW,
+ BTF_EVNT_C2H_LOOPBACK,
+ BTF_EVNT_MAX,
+};
+
+enum btf_fw_event_report {
+ BTC_RPT_TYPE_CTRL = 0x0,
+ BTC_RPT_TYPE_TDMA,
+ BTC_RPT_TYPE_SLOT,
+ BTC_RPT_TYPE_CYSTA,
+ BTC_RPT_TYPE_STEP,
+ BTC_RPT_TYPE_NULLSTA,
+ BTC_RPT_TYPE_MREG,
+ BTC_RPT_TYPE_GPIO_DBG,
+ BTC_RPT_TYPE_BT_VER,
+ BTC_RPT_TYPE_BT_SCAN,
+ BTC_RPT_TYPE_BT_AFH,
+ BTC_RPT_TYPE_BT_DEVICE,
+ BTC_RPT_TYPE_TEST,
+ BTC_RPT_TYPE_MAX = 31
+};
+
+enum rtw_btc_btf_reg_type {
+ REG_MAC = 0x0,
+ REG_BB = 0x1,
+ REG_RF = 0x2,
+ REG_BT_RF = 0x3,
+ REG_BT_MODEM = 0x4,
+ REG_BT_BLUEWIZE = 0x5,
+ REG_BT_VENDOR = 0x6,
+ REG_BT_LE = 0x7,
+ REG_MAX_TYPE,
+};
+
+struct rtw89_btc_rpt_cmn_info {
+ u32 rx_cnt;
+ u32 rx_len;
+ u32 req_len; /* expected rsp len */
+ u8 req_fver; /* expected rsp fver */
+ u8 rsp_fver; /* fver from fw */
+ u8 valid;
+} __packed;
+
+struct rtw89_btc_report_ctrl_state {
+ struct rtw89_btc_rpt_cmn_info cinfo; /* common info, by driver */
+ struct rtw89_btc_fbtc_rpt_ctrl finfo; /* info from fw */
+};
+
+struct rtw89_btc_rpt_fbtc_tdma {
+ struct rtw89_btc_rpt_cmn_info cinfo; /* common info, by driver */
+ struct rtw89_btc_fbtc_tdma finfo; /* info from fw */
+};
+
+struct rtw89_btc_rpt_fbtc_slots {
+ struct rtw89_btc_rpt_cmn_info cinfo; /* common info, by driver */
+ struct rtw89_btc_fbtc_slots finfo; /* info from fw */
+};
+
+struct rtw89_btc_rpt_fbtc_cysta {
+ struct rtw89_btc_rpt_cmn_info cinfo; /* common info, by driver */
+ struct rtw89_btc_fbtc_cysta finfo; /* info from fw */
+};
+
+struct rtw89_btc_rpt_fbtc_step {
+ struct rtw89_btc_rpt_cmn_info cinfo; /* common info, by driver */
+ struct rtw89_btc_fbtc_steps finfo; /* info from fw */
+};
+
+struct rtw89_btc_rpt_fbtc_nullsta {
+ struct rtw89_btc_rpt_cmn_info cinfo; /* common info, by driver */
+ struct rtw89_btc_fbtc_cynullsta finfo; /* info from fw */
+};
+
+struct rtw89_btc_rpt_fbtc_mreg {
+ struct rtw89_btc_rpt_cmn_info cinfo; /* common info, by driver */
+ struct rtw89_btc_fbtc_mreg_val finfo; /* info from fw */
+};
+
+struct rtw89_btc_rpt_fbtc_gpio_dbg {
+ struct rtw89_btc_rpt_cmn_info cinfo; /* common info, by driver */
+ struct rtw89_btc_fbtc_gpio_dbg finfo; /* info from fw */
+};
+
+struct rtw89_btc_rpt_fbtc_btver {
+ struct rtw89_btc_rpt_cmn_info cinfo; /* common info, by driver */
+ struct rtw89_btc_fbtc_btver finfo; /* info from fw */
+};
+
+struct rtw89_btc_rpt_fbtc_btscan {
+ struct rtw89_btc_rpt_cmn_info cinfo; /* common info, by driver */
+ struct rtw89_btc_fbtc_btscan finfo; /* info from fw */
+};
+
+struct rtw89_btc_rpt_fbtc_btafh {
+ struct rtw89_btc_rpt_cmn_info cinfo; /* common info, by driver */
+ struct rtw89_btc_fbtc_btafh finfo; /* info from fw */
+};
+
+struct rtw89_btc_rpt_fbtc_btdev {
+ struct rtw89_btc_rpt_cmn_info cinfo; /* common info, by driver */
+ struct rtw89_btc_fbtc_btdevinfo finfo; /* info from fw */
+};
+
+enum rtw89_btc_btfre_type {
+ BTFRE_INVALID_INPUT = 0x0, /* invalid input parameters */
+ BTFRE_UNDEF_TYPE,
+ BTFRE_EXCEPTION,
+ BTFRE_MAX,
+};
+
+struct rtw89_btc_btf_fwinfo {
+ u32 cnt_c2h;
+ u32 cnt_h2c;
+ u32 cnt_h2c_fail;
+ u32 event[BTF_EVNT_MAX];
+
+ u32 err[BTFRE_MAX];
+ u32 len_mismch;
+ u32 fver_mismch;
+ u32 rpt_en_map;
+
+ struct rtw89_btc_report_ctrl_state rpt_ctrl;
+ struct rtw89_btc_rpt_fbtc_tdma rpt_fbtc_tdma;
+ struct rtw89_btc_rpt_fbtc_slots rpt_fbtc_slots;
+ struct rtw89_btc_rpt_fbtc_cysta rpt_fbtc_cysta;
+ struct rtw89_btc_rpt_fbtc_step rpt_fbtc_step;
+ struct rtw89_btc_rpt_fbtc_nullsta rpt_fbtc_nullsta;
+ struct rtw89_btc_rpt_fbtc_mreg rpt_fbtc_mregval;
+ struct rtw89_btc_rpt_fbtc_gpio_dbg rpt_fbtc_gpio_dbg;
+ struct rtw89_btc_rpt_fbtc_btver rpt_fbtc_btver;
+ struct rtw89_btc_rpt_fbtc_btscan rpt_fbtc_btscan;
+ struct rtw89_btc_rpt_fbtc_btafh rpt_fbtc_btafh;
+ struct rtw89_btc_rpt_fbtc_btdev rpt_fbtc_btdev;
+};
+
+#define RTW89_BTC_POLICY_MAXLEN 512
+
+struct rtw89_btc {
+ struct rtw89_btc_cx cx;
+ struct rtw89_btc_dm dm;
+ struct rtw89_btc_ctrl ctrl;
+ struct rtw89_btc_module mdinfo;
+ struct rtw89_btc_btf_fwinfo fwinfo;
+ struct rtw89_btc_dbg dbg;
+
+ struct work_struct eapol_notify_work;
+ struct work_struct arp_notify_work;
+ struct work_struct dhcp_notify_work;
+ struct work_struct wl_sta_notify_work;
+
+ u32 bt_req_len;
+
+ u8 policy[RTW89_BTC_POLICY_MAXLEN];
+ u16 policy_len;
+ u16 policy_type;
+ bool bt_req_en;
+ bool update_policy_force;
+ bool lps;
+};
+
+enum rtw89_ra_mode {
+ RTW89_RA_MODE_CCK = BIT(0),
+ RTW89_RA_MODE_OFDM = BIT(1),
+ RTW89_RA_MODE_HT = BIT(2),
+ RTW89_RA_MODE_VHT = BIT(3),
+ RTW89_RA_MODE_HE = BIT(4),
+};
+
+enum rtw89_ra_report_mode {
+ RTW89_RA_RPT_MODE_LEGACY,
+ RTW89_RA_RPT_MODE_HT,
+ RTW89_RA_RPT_MODE_VHT,
+ RTW89_RA_RPT_MODE_HE,
+};
+
+enum rtw89_dig_noisy_level {
+ RTW89_DIG_NOISY_LEVEL0 = -1,
+ RTW89_DIG_NOISY_LEVEL1 = 0,
+ RTW89_DIG_NOISY_LEVEL2 = 1,
+ RTW89_DIG_NOISY_LEVEL3 = 2,
+ RTW89_DIG_NOISY_LEVEL_MAX = 3,
+};
+
+enum rtw89_gi_ltf {
+ RTW89_GILTF_LGI_4XHE32 = 0,
+ RTW89_GILTF_SGI_4XHE08 = 1,
+ RTW89_GILTF_2XHE16 = 2,
+ RTW89_GILTF_2XHE08 = 3,
+ RTW89_GILTF_1XHE16 = 4,
+ RTW89_GILTF_1XHE08 = 5,
+ RTW89_GILTF_MAX
+};
+
+enum rtw89_rx_frame_type {
+ RTW89_RX_TYPE_MGNT = 0,
+ RTW89_RX_TYPE_CTRL = 1,
+ RTW89_RX_TYPE_DATA = 2,
+ RTW89_RX_TYPE_RSVD = 3,
+};
+
+struct rtw89_ra_info {
+ u8 is_dis_ra:1;
+ /* Bit0 : CCK
+ * Bit1 : OFDM
+ * Bit2 : HT
+ * Bit3 : VHT
+ * Bit4 : HE
+ */
+ u8 mode_ctrl:5;
+ u8 bw_cap:2;
+ u8 macid;
+ u8 dcm_cap:1;
+ u8 er_cap:1;
+ u8 init_rate_lv:2;
+ u8 upd_all:1;
+ u8 en_sgi:1;
+ u8 ldpc_cap:1;
+ u8 stbc_cap:1;
+ u8 ss_num:3;
+ u8 giltf:3;
+ u8 upd_bw_nss_mask:1;
+ u8 upd_mask:1;
+ u64 ra_mask; /* 63 bits ra_mask + 1 bit CSI ctrl */
+ /* BFee CSI */
+ u8 band_num;
+ u8 ra_csi_rate_en:1;
+ u8 fixed_csi_rate_en:1;
+ u8 cr_tbl_sel:1;
+ u8 rsvd2:5;
+ u8 csi_mcs_ss_idx;
+ u8 csi_mode:2;
+ u8 csi_gi_ltf:3;
+ u8 csi_bw:3;
+};
+
+#define RTW89_PPDU_MAX_USR 4
+#define RTW89_PPDU_MAC_INFO_USR_SIZE 4
+#define RTW89_PPDU_MAC_INFO_SIZE 8
+#define RTW89_PPDU_MAC_RX_CNT_SIZE 96
+
+#define RTW89_MAX_RX_AGG_NUM 64
+#define RTW89_MAX_TX_AGG_NUM 128
+
+struct rtw89_ampdu_params {
+ u16 agg_num;
+ bool amsdu;
+};
+
+struct rtw89_ra_report {
+ struct rate_info txrate;
+ u32 bit_rate;
+ u16 hw_rate;
+};
+
+DECLARE_EWMA(rssi, 10, 16);
+
+struct rtw89_sta {
+ u8 mac_id;
+ bool disassoc;
+ struct rtw89_vif *rtwvif;
+ struct rtw89_ra_info ra;
+ struct rtw89_ra_report ra_report;
+ int max_agg_wait;
+ u8 prev_rssi;
+ struct ewma_rssi avg_rssi;
+ struct rtw89_ampdu_params ampdu_params[IEEE80211_NUM_TIDS];
+ struct ieee80211_rx_status rx_status;
+ u16 rx_hw_rate;
+
+ bool use_cfg_mask;
+ struct cfg80211_bitrate_mask mask;
+
+ bool cctl_tx_time;
+ u32 ampdu_max_time:4;
+ bool cctl_tx_retry_limit;
+ u32 data_tx_cnt_lmt:6;
+};
+
+#define RTW89_MAX_ADDR_CAM_NUM 128
+#define RTW89_MAX_BSSID_CAM_NUM 20
+#define RTW89_MAX_SEC_CAM_NUM 128
+#define RTW89_SEC_CAM_IN_ADDR_CAM 7
+
+struct rtw89_addr_cam_entry {
+ u8 addr_cam_idx;
+ u8 offset;
+ u8 len;
+ u8 valid : 1;
+ u8 addr_mask : 6;
+ u8 wapi : 1;
+ u8 mask_sel : 2;
+ u8 bssid_cam_idx: 6;
+ u8 tma[ETH_ALEN];
+ u8 sma[ETH_ALEN];
+
+ u8 sec_ent_mode;
+ DECLARE_BITMAP(sec_cam_map, RTW89_SEC_CAM_IN_ADDR_CAM);
+ u8 sec_ent_keyid[RTW89_SEC_CAM_IN_ADDR_CAM];
+ u8 sec_ent[RTW89_SEC_CAM_IN_ADDR_CAM];
+ struct rtw89_sec_cam_entry *sec_entries[RTW89_SEC_CAM_IN_ADDR_CAM];
+};
+
+struct rtw89_bssid_cam_entry {
+ u8 bssid[ETH_ALEN];
+ u8 phy_idx;
+ u8 bssid_cam_idx;
+ u8 offset;
+ u8 len;
+ u8 valid : 1;
+ u8 num;
+};
+
+struct rtw89_sec_cam_entry {
+ u8 sec_cam_idx;
+ u8 offset;
+ u8 len;
+ u8 type : 4;
+ u8 ext_key : 1;
+ u8 spp_mode : 1;
+ /* 256 bits */
+ u8 key[32];
+};
+
+struct rtw89_efuse {
+ bool valid;
+ u8 xtal_cap;
+ u8 addr[ETH_ALEN];
+ u8 rfe_type;
+ char country_code[2];
+};
+
+struct rtw89_vif {
+ struct list_head list;
+ u8 mac_id;
+ u8 port;
+ u8 mac_addr[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ u8 phy_idx;
+ u8 mac_idx;
+ u8 net_type;
+ u8 wifi_role;
+ u8 self_role;
+ u8 wmm;
+ u8 bcn_hit_cond;
+ u8 hit_rule;
+ bool trigger;
+ bool lsig_txop;
+ u8 tgt_ind;
+ u8 frm_tgt_ind;
+ bool wowlan_pattern;
+ bool wowlan_uc;
+ bool wowlan_magic;
+ bool is_hesta;
+ bool last_a_ctrl;
+ union {
+ struct {
+ struct ieee80211_sta *ap;
+ } mgd;
+ struct {
+ struct list_head sta_list;
+ } ap;
+ };
+ struct rtw89_addr_cam_entry addr_cam;
+ struct rtw89_bssid_cam_entry bssid_cam;
+ struct ieee80211_tx_queue_params tx_params[IEEE80211_NUM_ACS];
+ struct rtw89_traffic_stats stats;
+};
+
+enum rtw89_lv1_rcvy_step {
+ RTW89_LV1_RCVY_STEP_1,
+ RTW89_LV1_RCVY_STEP_2,
+};
+
+struct rtw89_hci_ops {
+ int (*tx_write)(struct rtw89_dev *rtwdev, struct rtw89_core_tx_request *tx_req);
+ void (*tx_kick_off)(struct rtw89_dev *rtwdev, u8 txch);
+ void (*flush_queues)(struct rtw89_dev *rtwdev, u32 queues, bool drop);
+ void (*reset)(struct rtw89_dev *rtwdev);
+ int (*start)(struct rtw89_dev *rtwdev);
+ void (*stop)(struct rtw89_dev *rtwdev);
+ void (*link_ps)(struct rtw89_dev *rtwdev, bool enter);
+
+ u8 (*read8)(struct rtw89_dev *rtwdev, u32 addr);
+ u16 (*read16)(struct rtw89_dev *rtwdev, u32 addr);
+ u32 (*read32)(struct rtw89_dev *rtwdev, u32 addr);
+ void (*write8)(struct rtw89_dev *rtwdev, u32 addr, u8 data);
+ void (*write16)(struct rtw89_dev *rtwdev, u32 addr, u16 data);
+ void (*write32)(struct rtw89_dev *rtwdev, u32 addr, u32 data);
+
+ int (*mac_pre_init)(struct rtw89_dev *rtwdev);
+ int (*mac_post_init)(struct rtw89_dev *rtwdev);
+ int (*deinit)(struct rtw89_dev *rtwdev);
+
+ u32 (*check_and_reclaim_tx_resource)(struct rtw89_dev *rtwdev, u8 txch);
+ int (*mac_lv1_rcvy)(struct rtw89_dev *rtwdev, enum rtw89_lv1_rcvy_step step);
+ void (*dump_err_status)(struct rtw89_dev *rtwdev);
+ int (*napi_poll)(struct napi_struct *napi, int budget);
+};
+
+struct rtw89_hci_info {
+ const struct rtw89_hci_ops *ops;
+ enum rtw89_hci_type type;
+ u32 rpwm_addr;
+ u32 cpwm_addr;
+};
+
+struct rtw89_chip_ops {
+ void (*bb_reset)(struct rtw89_dev *rtwdev,
+ enum rtw89_phy_idx phy_idx);
+ void (*bb_sethw)(struct rtw89_dev *rtwdev);
+ u32 (*read_rf)(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path,
+ u32 addr, u32 mask);
+ bool (*write_rf)(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path,
+ u32 addr, u32 mask, u32 data);
+ void (*set_channel)(struct rtw89_dev *rtwdev,
+ struct rtw89_channel_params *param);
+ void (*set_channel_help)(struct rtw89_dev *rtwdev, bool enter,
+ struct rtw89_channel_help_params *p);
+ int (*read_efuse)(struct rtw89_dev *rtwdev, u8 *log_map);
+ int (*read_phycap)(struct rtw89_dev *rtwdev, u8 *phycap_map);
+ void (*fem_setup)(struct rtw89_dev *rtwdev);
+ void (*rfk_init)(struct rtw89_dev *rtwdev);
+ void (*rfk_channel)(struct rtw89_dev *rtwdev);
+ void (*rfk_band_changed)(struct rtw89_dev *rtwdev);
+ void (*rfk_track)(struct rtw89_dev *rtwdev);
+ void (*power_trim)(struct rtw89_dev *rtwdev);
+ void (*set_txpwr)(struct rtw89_dev *rtwdev);
+ void (*set_txpwr_ctrl)(struct rtw89_dev *rtwdev);
+ int (*init_txpwr_unit)(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx);
+ u8 (*get_thermal)(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path);
+ void (*ctrl_btg)(struct rtw89_dev *rtwdev, bool btg);
+ void (*query_ppdu)(struct rtw89_dev *rtwdev,
+ struct rtw89_rx_phy_ppdu *phy_ppdu,
+ struct ieee80211_rx_status *status);
+ void (*bb_ctrl_btc_preagc)(struct rtw89_dev *rtwdev, bool bt_en);
+ void (*set_txpwr_ul_tb_offset)(struct rtw89_dev *rtwdev,
+ s16 pw_ofst, enum rtw89_mac_idx mac_idx);
+
+ void (*btc_set_rfe)(struct rtw89_dev *rtwdev);
+ void (*btc_init_cfg)(struct rtw89_dev *rtwdev);
+ void (*btc_set_wl_pri)(struct rtw89_dev *rtwdev, u8 map, bool state);
+ void (*btc_set_wl_txpwr_ctrl)(struct rtw89_dev *rtwdev, u32 txpwr_val);
+ s8 (*btc_get_bt_rssi)(struct rtw89_dev *rtwdev, s8 val);
+ void (*btc_bt_aci_imp)(struct rtw89_dev *rtwdev);
+ void (*btc_update_bt_cnt)(struct rtw89_dev *rtwdev);
+};
+
+enum rtw89_dma_ch {
+ RTW89_DMA_ACH0 = 0,
+ RTW89_DMA_ACH1 = 1,
+ RTW89_DMA_ACH2 = 2,
+ RTW89_DMA_ACH3 = 3,
+ RTW89_DMA_ACH4 = 4,
+ RTW89_DMA_ACH5 = 5,
+ RTW89_DMA_ACH6 = 6,
+ RTW89_DMA_ACH7 = 7,
+ RTW89_DMA_B0MG = 8,
+ RTW89_DMA_B0HI = 9,
+ RTW89_DMA_B1MG = 10,
+ RTW89_DMA_B1HI = 11,
+ RTW89_DMA_H2C = 12,
+ RTW89_DMA_CH_NUM = 13
+};
+
+enum rtw89_qta_mode {
+ RTW89_QTA_SCC,
+ RTW89_QTA_DBCC,
+ RTW89_QTA_SCC_WD128,
+ RTW89_QTA_DBCC_WD128,
+ RTW89_QTA_SCC_STF,
+ RTW89_QTA_DBCC_STF,
+ RTW89_QTA_SU_TP,
+ RTW89_QTA_DLFW,
+ RTW89_QTA_BCN_TEST,
+ RTW89_QTA_LAMODE,
+
+ /* keep last */
+ RTW89_QTA_INVALID,
+};
+
+struct rtw89_hfc_ch_cfg {
+ u16 min;
+ u16 max;
+#define grp_0 0
+#define grp_1 1
+#define grp_num 2
+ u8 grp;
+};
+
+struct rtw89_hfc_ch_info {
+ u16 aval;
+ u16 used;
+};
+
+struct rtw89_hfc_pub_cfg {
+ u16 grp0;
+ u16 grp1;
+ u16 pub_max;
+ u16 wp_thrd;
+};
+
+struct rtw89_hfc_pub_info {
+ u16 g0_used;
+ u16 g1_used;
+ u16 g0_aval;
+ u16 g1_aval;
+ u16 pub_aval;
+ u16 wp_aval;
+};
+
+struct rtw89_hfc_prec_cfg {
+ u16 ch011_prec;
+ u16 h2c_prec;
+ u16 wp_ch07_prec;
+ u16 wp_ch811_prec;
+ u8 ch011_full_cond;
+ u8 h2c_full_cond;
+ u8 wp_ch07_full_cond;
+ u8 wp_ch811_full_cond;
+};
+
+struct rtw89_hfc_param {
+ bool en;
+ bool h2c_en;
+ u8 mode;
+ struct rtw89_hfc_ch_cfg *ch_cfg;
+ struct rtw89_hfc_ch_info ch_info[RTW89_DMA_CH_NUM];
+ struct rtw89_hfc_pub_cfg *pub_cfg;
+ struct rtw89_hfc_pub_info pub_info;
+ struct rtw89_hfc_prec_cfg *prec_cfg;
+};
+
+struct rtw89_hfc_param_ini {
+ struct rtw89_hfc_ch_cfg *ch_cfg;
+ struct rtw89_hfc_pub_cfg *pub_cfg;
+ struct rtw89_hfc_prec_cfg *prec_cfg;
+ u8 mode;
+};
+
+struct rtw89_dle_size {
+ u16 pge_size;
+ u16 lnk_pge_num;
+ u16 unlnk_pge_num;
+};
+
+struct rtw89_wde_quota {
+ u16 hif;
+ u16 wcpu;
+ u16 pkt_in;
+ u16 cpu_io;
+};
+
+struct rtw89_ple_quota {
+ u16 cma0_tx;
+ u16 cma1_tx;
+ u16 c2h;
+ u16 h2c;
+ u16 wcpu;
+ u16 mpdu_proc;
+ u16 cma0_dma;
+ u16 cma1_dma;
+ u16 bb_rpt;
+ u16 wd_rel;
+ u16 cpu_io;
+};
+
+struct rtw89_dle_mem {
+ enum rtw89_qta_mode mode;
+ struct rtw89_dle_size *wde_size;
+ struct rtw89_dle_size *ple_size;
+ struct rtw89_wde_quota *wde_min_qt;
+ struct rtw89_wde_quota *wde_max_qt;
+ struct rtw89_ple_quota *ple_min_qt;
+ struct rtw89_ple_quota *ple_max_qt;
+};
+
+struct rtw89_reg_def {
+ u32 addr;
+ u32 mask;
+};
+
+struct rtw89_reg2_def {
+ u32 addr;
+ u32 data;
+};
+
+struct rtw89_reg3_def {
+ u32 addr;
+ u32 mask;
+ u32 data;
+};
+
+struct rtw89_reg5_def {
+ u8 flag; /* recognized by parsers */
+ u8 path;
+ u32 addr;
+ u32 mask;
+ u32 data;
+};
+
+struct rtw89_phy_table {
+ const struct rtw89_reg2_def *regs;
+ u32 n_regs;
+ enum rtw89_rf_path rf_path;
+};
+
+struct rtw89_txpwr_table {
+ const void *data;
+ u32 size;
+ void (*load)(struct rtw89_dev *rtwdev,
+ const struct rtw89_txpwr_table *tbl);
+};
+
+struct rtw89_chip_info {
+ enum rtw89_core_chip_id chip_id;
+ const struct rtw89_chip_ops *ops;
+ const char *fw_name;
+ u32 fifo_size;
+ u32 dle_lamode_size;
+ u16 max_amsdu_limit;
+ struct rtw89_hfc_param_ini *hfc_param_ini;
+ struct rtw89_dle_mem *dle_mem;
+ u32 rf_base_addr[2];
+ u8 rf_path_num;
+ u8 tx_nss;
+ u8 rx_nss;
+ u8 acam_num;
+ u8 bcam_num;
+ u8 scam_num;
+
+ u8 sec_ctrl_efuse_size;
+ u32 physical_efuse_size;
+ u32 logical_efuse_size;
+ u32 limit_efuse_size;
+ u32 phycap_addr;
+ u32 phycap_size;
+
+ const struct rtw89_pwr_cfg * const *pwr_on_seq;
+ const struct rtw89_pwr_cfg * const *pwr_off_seq;
+ const struct rtw89_phy_table *bb_table;
+ const struct rtw89_phy_table *rf_table[RF_PATH_MAX];
+ const struct rtw89_phy_table *nctl_table;
+ const struct rtw89_txpwr_table *byr_table;
+ const struct rtw89_phy_dig_gain_table *dig_table;
+ const s8 (*txpwr_lmt_2g)[RTW89_2G_BW_NUM][RTW89_NTX_NUM]
+ [RTW89_RS_LMT_NUM][RTW89_BF_NUM]
+ [RTW89_REGD_NUM][RTW89_2G_CH_NUM];
+ const s8 (*txpwr_lmt_5g)[RTW89_5G_BW_NUM][RTW89_NTX_NUM]
+ [RTW89_RS_LMT_NUM][RTW89_BF_NUM]
+ [RTW89_REGD_NUM][RTW89_5G_CH_NUM];
+ const s8 (*txpwr_lmt_ru_2g)[RTW89_RU_NUM][RTW89_NTX_NUM]
+ [RTW89_REGD_NUM][RTW89_2G_CH_NUM];
+ const s8 (*txpwr_lmt_ru_5g)[RTW89_RU_NUM][RTW89_NTX_NUM]
+ [RTW89_REGD_NUM][RTW89_5G_CH_NUM];
+
+ u8 txpwr_factor_rf;
+ u8 txpwr_factor_mac;
+
+ u32 para_ver;
+ u32 wlcx_desired;
+ u8 btcx_desired;
+ u8 scbd;
+ u8 mailbox;
+
+ u8 afh_guard_ch;
+
+ u8 mon_reg_num;
+ const struct rtw89_btc_fbtc_mreg *mon_reg;
+ u8 rf_para_ulink_num;
+ 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;
+ u8 ps_mode_supported;
+};
+
+enum rtw89_hcifc_mode {
+ RTW89_HCIFC_POH = 0,
+ RTW89_HCIFC_STF = 1,
+ RTW89_HCIFC_SDIO = 2,
+
+ /* keep last */
+ RTW89_HCIFC_MODE_INVALID,
+};
+
+struct rtw89_dle_info {
+ enum rtw89_qta_mode qta_mode;
+ u16 wde_pg_size;
+ u16 ple_pg_size;
+ u16 c0_rx_qta;
+ u16 c1_rx_qta;
+};
+
+enum rtw89_host_rpr_mode {
+ RTW89_RPR_MODE_POH = 0,
+ RTW89_RPR_MODE_STF
+};
+
+struct rtw89_mac_info {
+ struct rtw89_dle_info dle_info;
+ struct rtw89_hfc_param hfc_param;
+ enum rtw89_qta_mode qta_mode;
+ u8 rpwm_seq_num;
+ u8 cpwm_seq_num;
+};
+
+enum rtw89_fw_type {
+ RTW89_FW_NORMAL = 1,
+ RTW89_FW_WOWLAN = 3,
+};
+
+struct rtw89_fw_suit {
+ const u8 *data;
+ u32 size;
+ u8 major_ver;
+ u8 minor_ver;
+ u8 sub_ver;
+ u8 sub_idex;
+ u16 build_year;
+ u16 build_mon;
+ u16 build_date;
+ u16 build_hour;
+ u16 build_min;
+ u8 cmd_ver;
+};
+
+struct rtw89_fw_info {
+ const struct firmware *firmware;
+ struct rtw89_dev *rtwdev;
+ struct completion completion;
+ u8 h2c_seq;
+ u8 rec_seq;
+ struct rtw89_fw_suit normal;
+ struct rtw89_fw_suit wowlan;
+ bool fw_log_enable;
+};
+
+struct rtw89_cam_info {
+ DECLARE_BITMAP(addr_cam_map, RTW89_MAX_ADDR_CAM_NUM);
+ DECLARE_BITMAP(bssid_cam_map, RTW89_MAX_BSSID_CAM_NUM);
+ DECLARE_BITMAP(sec_cam_map, RTW89_MAX_SEC_CAM_NUM);
+};
+
+enum rtw89_sar_sources {
+ RTW89_SAR_SOURCE_NONE,
+ RTW89_SAR_SOURCE_COMMON,
+
+ RTW89_SAR_SOURCE_NR,
+};
+
+struct rtw89_sar_cfg_common {
+ bool set[RTW89_SUBBAND_NR];
+ s32 cfg[RTW89_SUBBAND_NR];
+};
+
+struct rtw89_sar_info {
+ /* used to decide how to acces SAR cfg union */
+ enum rtw89_sar_sources src;
+
+ /* reserved for different knids of SAR cfg struct.
+ * supposed that a single cfg struct cannot handle various SAR sources.
+ */
+ union {
+ struct rtw89_sar_cfg_common cfg_common;
+ };
+};
+
+struct rtw89_hal {
+ u32 rx_fltr;
+ u8 cv;
+ u8 current_channel;
+ u8 current_primary_channel;
+ enum rtw89_subband current_subband;
+ u8 current_band_width;
+ u8 current_band_type;
+ /* center channel for different available bandwidth,
+ * val of (bw > current_band_width) is invalid
+ */
+ u8 cch_by_bw[RTW89_MAX_CHANNEL_WIDTH + 1];
+ u32 sw_amsdu_max_size;
+ u32 antenna_tx;
+ u32 antenna_rx;
+};
+
+#define RTW89_MAX_MAC_ID_NUM 128
+
+enum rtw89_flags {
+ RTW89_FLAG_POWERON,
+ RTW89_FLAG_FW_RDY,
+ RTW89_FLAG_RUNNING,
+ RTW89_FLAG_BFEE_MON,
+ RTW89_FLAG_BFEE_EN,
+ RTW89_FLAG_NAPI_RUNNING,
+ RTW89_FLAG_LEISURE_PS,
+ RTW89_FLAG_LOW_POWER_MODE,
+ RTW89_FLAG_INACTIVE_PS,
+
+ NUM_OF_RTW89_FLAGS,
+};
+
+struct rtw89_pkt_stat {
+ u16 beacon_nr;
+ u32 rx_rate_cnt[RTW89_HW_RATE_NR];
+};
+
+DECLARE_EWMA(thermal, 4, 4);
+
+struct rtw89_phy_stat {
+ struct ewma_thermal avg_thermal[RF_PATH_MAX];
+ struct rtw89_pkt_stat cur_pkt_stat;
+ struct rtw89_pkt_stat last_pkt_stat;
+};
+
+#define RTW89_DACK_PATH_NR 2
+#define RTW89_DACK_IDX_NR 2
+#define RTW89_DACK_MSBK_NR 16
+struct rtw89_dack_info {
+ bool dack_done;
+ u8 msbk_d[RTW89_DACK_PATH_NR][RTW89_DACK_IDX_NR][RTW89_DACK_MSBK_NR];
+ u8 dadck_d[RTW89_DACK_PATH_NR][RTW89_DACK_IDX_NR];
+ u16 addck_d[RTW89_DACK_PATH_NR][RTW89_DACK_IDX_NR];
+ u16 biask_d[RTW89_DACK_PATH_NR][RTW89_DACK_IDX_NR];
+ u32 dack_cnt;
+ bool addck_timeout[RTW89_DACK_PATH_NR];
+ bool dadck_timeout[RTW89_DACK_PATH_NR];
+ bool msbk_timeout[RTW89_DACK_PATH_NR];
+};
+
+#define RTW89_IQK_CHS_NR 2
+#define RTW89_IQK_PATH_NR 4
+struct rtw89_iqk_info {
+ bool lok_cor_fail[RTW89_IQK_CHS_NR][RTW89_IQK_PATH_NR];
+ bool lok_fin_fail[RTW89_IQK_CHS_NR][RTW89_IQK_PATH_NR];
+ bool iqk_tx_fail[RTW89_IQK_CHS_NR][RTW89_IQK_PATH_NR];
+ bool iqk_rx_fail[RTW89_IQK_CHS_NR][RTW89_IQK_PATH_NR];
+ u32 iqk_fail_cnt;
+ bool is_iqk_init;
+ u32 iqk_channel[RTW89_IQK_CHS_NR];
+ u8 iqk_band[RTW89_IQK_PATH_NR];
+ u8 iqk_ch[RTW89_IQK_PATH_NR];
+ u8 iqk_bw[RTW89_IQK_PATH_NR];
+ u8 kcount;
+ u8 iqk_times;
+ u8 version;
+ u32 nb_txcfir[RTW89_IQK_PATH_NR];
+ u32 nb_rxcfir[RTW89_IQK_PATH_NR];
+ u32 bp_txkresult[RTW89_IQK_PATH_NR];
+ u32 bp_rxkresult[RTW89_IQK_PATH_NR];
+ u32 bp_iqkenable[RTW89_IQK_PATH_NR];
+ bool is_wb_txiqk[RTW89_IQK_PATH_NR];
+ bool is_wb_rxiqk[RTW89_IQK_PATH_NR];
+ bool is_nbiqk;
+ bool iqk_fft_en;
+ bool iqk_xym_en;
+ bool iqk_sram_en;
+ bool iqk_cfir_en;
+ u8 thermal[RTW89_IQK_PATH_NR];
+ bool thermal_rek_en;
+ u32 syn1to2;
+ u8 iqk_mcc_ch[RTW89_IQK_CHS_NR][RTW89_IQK_PATH_NR];
+ u8 iqk_table_idx[RTW89_IQK_PATH_NR];
+};
+
+#define RTW89_DPK_RF_PATH 2
+#define RTW89_DPK_AVG_THERMAL_NUM 8
+#define RTW89_DPK_BKUP_NUM 2
+struct rtw89_dpk_bkup_para {
+ enum rtw89_band band;
+ enum rtw89_bandwidth bw;
+ u8 ch;
+ bool path_ok;
+ u8 txagc_dpk;
+ u8 ther_dpk;
+ u8 gs;
+ u16 pwsf;
+};
+
+struct rtw89_dpk_info {
+ bool is_dpk_enable;
+ bool is_dpk_reload_en;
+ u16 dc_i[RTW89_DPK_RF_PATH];
+ u16 dc_q[RTW89_DPK_RF_PATH];
+ u8 corr_val[RTW89_DPK_RF_PATH];
+ u8 corr_idx[RTW89_DPK_RF_PATH];
+ u8 cur_idx[RTW89_DPK_RF_PATH];
+ struct rtw89_dpk_bkup_para bp[RTW89_DPK_RF_PATH][RTW89_DPK_BKUP_NUM];
+};
+
+struct rtw89_fem_info {
+ bool elna_2g;
+ bool elna_5g;
+ bool epa_2g;
+ bool epa_5g;
+};
+
+struct rtw89_phy_ch_info {
+ u8 rssi_min;
+ u16 rssi_min_macid;
+ u8 pre_rssi_min;
+ u8 rssi_max;
+ u16 rssi_max_macid;
+ u8 rxsc_160;
+ u8 rxsc_80;
+ u8 rxsc_40;
+ u8 rxsc_20;
+ u8 rxsc_l;
+ u8 is_noisy;
+};
+
+struct rtw89_agc_gaincode_set {
+ u8 lna_idx;
+ u8 tia_idx;
+ u8 rxb_idx;
+};
+
+#define IGI_RSSI_TH_NUM 5
+#define FA_TH_NUM 4
+#define LNA_GAIN_NUM 7
+#define TIA_GAIN_NUM 2
+struct rtw89_dig_info {
+ struct rtw89_agc_gaincode_set cur_gaincode;
+ bool force_gaincode_idx_en;
+ struct rtw89_agc_gaincode_set force_gaincode;
+ u8 igi_rssi_th[IGI_RSSI_TH_NUM];
+ u16 fa_th[FA_TH_NUM];
+ u8 igi_rssi;
+ u8 igi_fa_rssi;
+ u8 fa_rssi_ofst;
+ u8 dyn_igi_max;
+ u8 dyn_igi_min;
+ bool dyn_pd_th_en;
+ u8 dyn_pd_th_max;
+ u8 pd_low_th_ofst;
+ u8 ib_pbk;
+ s8 ib_pkpwr;
+ s8 lna_gain_a[LNA_GAIN_NUM];
+ s8 lna_gain_g[LNA_GAIN_NUM];
+ s8 *lna_gain;
+ s8 tia_gain_a[TIA_GAIN_NUM];
+ s8 tia_gain_g[TIA_GAIN_NUM];
+ s8 *tia_gain;
+ bool is_linked_pre;
+};
+
+enum rtw89_multi_cfo_mode {
+ RTW89_PKT_BASED_AVG_MODE = 0,
+ RTW89_ENTRY_BASED_AVG_MODE = 1,
+ RTW89_TP_BASED_AVG_MODE = 2,
+};
+
+enum rtw89_phy_cfo_status {
+ RTW89_PHY_DCFO_STATE_NORMAL = 0,
+ RTW89_PHY_DCFO_STATE_ENHANCE = 1,
+ RTW89_PHY_DCFO_STATE_MAX
+};
+
+struct rtw89_cfo_tracking_info {
+ u16 cfo_timer_ms;
+ bool cfo_trig_by_timer_en;
+ enum rtw89_phy_cfo_status phy_cfo_status;
+ u8 phy_cfo_trk_cnt;
+ bool is_adjust;
+ enum rtw89_multi_cfo_mode rtw89_multi_cfo_mode;
+ bool apply_compensation;
+ u8 crystal_cap;
+ u8 crystal_cap_default;
+ u8 def_x_cap;
+ s8 x_cap_ofst;
+ u32 sta_cfo_tolerance;
+ s32 cfo_tail[CFO_TRACK_MAX_USER];
+ u16 cfo_cnt[CFO_TRACK_MAX_USER];
+ s32 cfo_avg_pre;
+ s32 cfo_avg[CFO_TRACK_MAX_USER];
+ s32 pre_cfo_avg[CFO_TRACK_MAX_USER];
+ u32 packet_count;
+ u32 packet_count_pre;
+ s32 residual_cfo_acc;
+ u8 phy_cfotrk_state;
+ u8 phy_cfotrk_cnt;
+};
+
+/* 2GL, 2GH, 5GL1, 5GH1, 5GM1, 5GM2, 5GH1, 5GH2 */
+#define TSSI_TRIM_CH_GROUP_NUM 8
+
+#define TSSI_CCK_CH_GROUP_NUM 6
+#define TSSI_MCS_2G_CH_GROUP_NUM 5
+#define TSSI_MCS_5G_CH_GROUP_NUM 14
+#define TSSI_MCS_CH_GROUP_NUM \
+ (TSSI_MCS_2G_CH_GROUP_NUM + TSSI_MCS_5G_CH_GROUP_NUM)
+
+struct rtw89_tssi_info {
+ u8 thermal[RF_PATH_MAX];
+ s8 tssi_trim[RF_PATH_MAX][TSSI_TRIM_CH_GROUP_NUM];
+ s8 tssi_cck[RF_PATH_MAX][TSSI_CCK_CH_GROUP_NUM];
+ s8 tssi_mcs[RF_PATH_MAX][TSSI_MCS_CH_GROUP_NUM];
+ s8 extra_ofst[RF_PATH_MAX];
+ bool tssi_tracking_check[RF_PATH_MAX];
+ u8 default_txagc_offset[RF_PATH_MAX];
+ u32 base_thermal[RF_PATH_MAX];
+};
+
+struct rtw89_power_trim_info {
+ bool pg_thermal_trim;
+ bool pg_pa_bias_trim;
+ u8 thermal_trim[RF_PATH_MAX];
+ u8 pa_bias_trim[RF_PATH_MAX];
+};
+
+struct rtw89_regulatory {
+ char alpha2[3];
+ u8 txpwr_regd[RTW89_BAND_MAX];
+};
+
+enum rtw89_ifs_clm_application {
+ RTW89_IFS_CLM_INIT = 0,
+ RTW89_IFS_CLM_BACKGROUND = 1,
+ RTW89_IFS_CLM_ACS = 2,
+ RTW89_IFS_CLM_DIG = 3,
+ RTW89_IFS_CLM_TDMA_DIG = 4,
+ RTW89_IFS_CLM_DBG = 5,
+ RTW89_IFS_CLM_DBG_MANUAL = 6
+};
+
+enum rtw89_env_racing_lv {
+ RTW89_RAC_RELEASE = 0,
+ RTW89_RAC_LV_1 = 1,
+ RTW89_RAC_LV_2 = 2,
+ RTW89_RAC_LV_3 = 3,
+ RTW89_RAC_LV_4 = 4,
+ RTW89_RAC_MAX_NUM = 5
+};
+
+struct rtw89_ccx_para_info {
+ enum rtw89_env_racing_lv rac_lv;
+ u16 mntr_time;
+ u8 nhm_manual_th_ofst;
+ u8 nhm_manual_th0;
+ enum rtw89_ifs_clm_application ifs_clm_app;
+ u32 ifs_clm_manual_th_times;
+ u32 ifs_clm_manual_th0;
+ u8 fahm_manual_th_ofst;
+ u8 fahm_manual_th0;
+ u8 fahm_numer_opt;
+ u8 fahm_denom_opt;
+};
+
+enum rtw89_ccx_edcca_opt_sc_idx {
+ RTW89_CCX_EDCCA_SEG0_P0 = 0,
+ RTW89_CCX_EDCCA_SEG0_S1 = 1,
+ RTW89_CCX_EDCCA_SEG0_S2 = 2,
+ RTW89_CCX_EDCCA_SEG0_S3 = 3,
+ RTW89_CCX_EDCCA_SEG1_P0 = 4,
+ RTW89_CCX_EDCCA_SEG1_S1 = 5,
+ RTW89_CCX_EDCCA_SEG1_S2 = 6,
+ RTW89_CCX_EDCCA_SEG1_S3 = 7
+};
+
+enum rtw89_ccx_edcca_opt_bw_idx {
+ RTW89_CCX_EDCCA_BW20_0 = 0,
+ RTW89_CCX_EDCCA_BW20_1 = 1,
+ RTW89_CCX_EDCCA_BW20_2 = 2,
+ RTW89_CCX_EDCCA_BW20_3 = 3,
+ RTW89_CCX_EDCCA_BW20_4 = 4,
+ RTW89_CCX_EDCCA_BW20_5 = 5,
+ RTW89_CCX_EDCCA_BW20_6 = 6,
+ RTW89_CCX_EDCCA_BW20_7 = 7
+};
+
+#define RTW89_NHM_TH_NUM 11
+#define RTW89_FAHM_TH_NUM 11
+#define RTW89_NHM_RPT_NUM 12
+#define RTW89_FAHM_RPT_NUM 12
+#define RTW89_IFS_CLM_NUM 4
+struct rtw89_env_monitor_info {
+ u32 ccx_trigger_time;
+ u64 start_time;
+ u8 ccx_rpt_stamp;
+ u8 ccx_watchdog_result;
+ bool ccx_ongoing;
+ u8 ccx_rac_lv;
+ bool ccx_manual_ctrl;
+ u8 ccx_pre_rssi;
+ u16 clm_mntr_time;
+ u16 nhm_mntr_time;
+ u16 ifs_clm_mntr_time;
+ enum rtw89_ifs_clm_application ifs_clm_app;
+ u16 fahm_mntr_time;
+ u16 edcca_clm_mntr_time;
+ u16 ccx_period;
+ u8 ccx_unit_idx;
+ enum rtw89_ccx_edcca_opt_bw_idx ccx_edcca_opt_bw_idx;
+ u8 nhm_th[RTW89_NHM_TH_NUM];
+ u16 ifs_clm_th_l[RTW89_IFS_CLM_NUM];
+ u16 ifs_clm_th_h[RTW89_IFS_CLM_NUM];
+ u8 fahm_numer_opt;
+ u8 fahm_denom_opt;
+ u8 fahm_th[RTW89_FAHM_TH_NUM];
+ u16 clm_result;
+ u16 nhm_result[RTW89_NHM_RPT_NUM];
+ u8 nhm_wgt[RTW89_NHM_RPT_NUM];
+ u16 nhm_tx_cnt;
+ u16 nhm_cca_cnt;
+ u16 nhm_idle_cnt;
+ u16 ifs_clm_tx;
+ u16 ifs_clm_edcca_excl_cca;
+ u16 ifs_clm_ofdmfa;
+ u16 ifs_clm_ofdmcca_excl_fa;
+ u16 ifs_clm_cckfa;
+ u16 ifs_clm_cckcca_excl_fa;
+ u16 ifs_clm_total_ifs;
+ u8 ifs_clm_his[RTW89_IFS_CLM_NUM];
+ u16 ifs_clm_avg[RTW89_IFS_CLM_NUM];
+ u16 ifs_clm_cca[RTW89_IFS_CLM_NUM];
+ u16 fahm_result[RTW89_FAHM_RPT_NUM];
+ u16 fahm_denom_result;
+ u16 edcca_clm_result;
+ u8 clm_ratio;
+ u8 nhm_rpt[RTW89_NHM_RPT_NUM];
+ u8 nhm_tx_ratio;
+ u8 nhm_cca_ratio;
+ u8 nhm_idle_ratio;
+ u8 nhm_ratio;
+ u16 nhm_result_sum;
+ u8 nhm_pwr;
+ u8 ifs_clm_tx_ratio;
+ u8 ifs_clm_edcca_excl_cca_ratio;
+ u8 ifs_clm_cck_fa_ratio;
+ u8 ifs_clm_ofdm_fa_ratio;
+ u8 ifs_clm_cck_cca_excl_fa_ratio;
+ u8 ifs_clm_ofdm_cca_excl_fa_ratio;
+ u16 ifs_clm_cck_fa_permil;
+ u16 ifs_clm_ofdm_fa_permil;
+ u32 ifs_clm_ifs_avg[RTW89_IFS_CLM_NUM];
+ u32 ifs_clm_cca_avg[RTW89_IFS_CLM_NUM];
+ u8 fahm_rpt[RTW89_FAHM_RPT_NUM];
+ u16 fahm_result_sum;
+ u8 fahm_ratio;
+ u8 fahm_denom_ratio;
+ u8 fahm_pwr;
+ u8 edcca_clm_ratio;
+};
+
+enum rtw89_ser_rcvy_step {
+ RTW89_SER_DRV_STOP_TX,
+ RTW89_SER_DRV_STOP_RX,
+ RTW89_SER_DRV_STOP_RUN,
+ RTW89_SER_HAL_STOP_DMA,
+ RTW89_NUM_OF_SER_FLAGS
+};
+
+struct rtw89_ser {
+ u8 state;
+ u8 alarm_event;
+
+ struct work_struct ser_hdl_work;
+ struct delayed_work ser_alarm_work;
+ struct state_ent *st_tbl;
+ struct event_ent *ev_tbl;
+ struct list_head msg_q;
+ spinlock_t msg_q_lock; /* lock when read/write ser msg */
+ DECLARE_BITMAP(flags, RTW89_NUM_OF_SER_FLAGS);
+};
+
+enum rtw89_mac_ax_ps_mode {
+ RTW89_MAC_AX_PS_MODE_ACTIVE = 0,
+ RTW89_MAC_AX_PS_MODE_LEGACY = 1,
+ RTW89_MAC_AX_PS_MODE_WMMPS = 2,
+ RTW89_MAC_AX_PS_MODE_MAX = 3,
+};
+
+enum rtw89_last_rpwm_mode {
+ RTW89_LAST_RPWM_PS = 0x0,
+ RTW89_LAST_RPWM_ACTIVE = 0x6,
+};
+
+struct rtw89_lps_parm {
+ u8 macid;
+ u8 psmode; /* enum rtw89_mac_ax_ps_mode */
+ u8 lastrpwm; /* enum rtw89_last_rpwm_mode */
+};
+
+struct rtw89_ppdu_sts_info {
+ struct sk_buff_head rx_queue[RTW89_PHY_MAX];
+ u8 curr_rx_ppdu_cnt[RTW89_PHY_MAX];
+};
+
+struct rtw89_dev {
+ struct ieee80211_hw *hw;
+ struct device *dev;
+
+ bool dbcc_en;
+ const struct rtw89_chip_info *chip;
+ struct rtw89_hal hal;
+ struct rtw89_mac_info mac;
+ struct rtw89_fw_info fw;
+ struct rtw89_hci_info hci;
+ struct rtw89_efuse efuse;
+ struct rtw89_traffic_stats stats;
+
+ /* ensures exclusive access from mac80211 callbacks */
+ struct mutex mutex;
+ /* 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;
+ /* used to protect ba_list */
+ spinlock_t ba_lock;
+ /* txqs to setup ba session */
+ struct list_head ba_list;
+ struct work_struct ba_work;
+
+ struct rtw89_cam_info cam_info;
+
+ struct sk_buff_head c2h_queue;
+ struct work_struct c2h_work;
+
+ struct rtw89_ser ser;
+
+ DECLARE_BITMAP(hw_port, RTW89_MAX_HW_PORT_NUM);
+ DECLARE_BITMAP(mac_id_map, RTW89_MAX_MAC_ID_NUM);
+ DECLARE_BITMAP(flags, NUM_OF_RTW89_FLAGS);
+
+ struct rtw89_phy_stat phystat;
+ struct rtw89_dack_info dack;
+ struct rtw89_iqk_info iqk;
+ struct rtw89_dpk_info dpk;
+ bool is_tssi_mode[RF_PATH_MAX];
+ bool is_bt_iqk_timeout;
+
+ struct rtw89_fem_info fem;
+ struct rtw89_txpwr_byrate byr[RTW89_BAND_MAX];
+ struct rtw89_tssi_info tssi;
+ struct rtw89_power_trim_info pwr_trim;
+
+ struct rtw89_cfo_tracking_info cfo_tracking;
+ struct rtw89_env_monitor_info env_monitor;
+ struct rtw89_dig_info dig;
+ struct rtw89_phy_ch_info ch_info;
+ struct delayed_work track_work;
+ struct delayed_work coex_act1_work;
+ struct delayed_work cfo_track_work;
+ struct rtw89_ppdu_sts_info ppdu_sts;
+ u8 total_sta_assoc;
+ bool scanning;
+
+ const struct rtw89_regulatory *regd;
+ struct rtw89_sar_info sar;
+
+ struct rtw89_btc btc;
+ enum rtw89_ps_mode ps_mode;
+ bool lps_enabled;
+
+ /* napi structure */
+ struct net_device netdev;
+ struct napi_struct napi;
+ int napi_budget_countdown;
+
+ /* HCI related data, keep last */
+ u8 priv[0] __aligned(sizeof(void *));
+};
+
+static inline int rtw89_hci_tx_write(struct rtw89_dev *rtwdev,
+ struct rtw89_core_tx_request *tx_req)
+{
+ return rtwdev->hci.ops->tx_write(rtwdev, tx_req);
+}
+
+static inline void rtw89_hci_reset(struct rtw89_dev *rtwdev)
+{
+ rtwdev->hci.ops->reset(rtwdev);
+}
+
+static inline int rtw89_hci_start(struct rtw89_dev *rtwdev)
+{
+ return rtwdev->hci.ops->start(rtwdev);
+}
+
+static inline void rtw89_hci_stop(struct rtw89_dev *rtwdev)
+{
+ rtwdev->hci.ops->stop(rtwdev);
+}
+
+static inline int rtw89_hci_deinit(struct rtw89_dev *rtwdev)
+{
+ return rtwdev->hci.ops->deinit(rtwdev);
+}
+
+static inline void rtw89_hci_link_ps(struct rtw89_dev *rtwdev, bool enter)
+{
+ rtwdev->hci.ops->link_ps(rtwdev, enter);
+}
+
+static inline u32 rtw89_hci_check_and_reclaim_tx_resource(struct rtw89_dev *rtwdev, u8 txch)
+{
+ return rtwdev->hci.ops->check_and_reclaim_tx_resource(rtwdev, txch);
+}
+
+static inline void rtw89_hci_tx_kick_off(struct rtw89_dev *rtwdev, u8 txch)
+{
+ return rtwdev->hci.ops->tx_kick_off(rtwdev, txch);
+}
+
+static inline void rtw89_hci_flush_queues(struct rtw89_dev *rtwdev, u32 queues,
+ bool drop)
+{
+ if (rtwdev->hci.ops->flush_queues)
+ return rtwdev->hci.ops->flush_queues(rtwdev, queues, drop);
+}
+
+static inline u8 rtw89_read8(struct rtw89_dev *rtwdev, u32 addr)
+{
+ return rtwdev->hci.ops->read8(rtwdev, addr);
+}
+
+static inline u16 rtw89_read16(struct rtw89_dev *rtwdev, u32 addr)
+{
+ return rtwdev->hci.ops->read16(rtwdev, addr);
+}
+
+static inline u32 rtw89_read32(struct rtw89_dev *rtwdev, u32 addr)
+{
+ return rtwdev->hci.ops->read32(rtwdev, addr);
+}
+
+static inline void rtw89_write8(struct rtw89_dev *rtwdev, u32 addr, u8 data)
+{
+ rtwdev->hci.ops->write8(rtwdev, addr, data);
+}
+
+static inline void rtw89_write16(struct rtw89_dev *rtwdev, u32 addr, u16 data)
+{
+ rtwdev->hci.ops->write16(rtwdev, addr, data);
+}
+
+static inline void rtw89_write32(struct rtw89_dev *rtwdev, u32 addr, u32 data)
+{
+ rtwdev->hci.ops->write32(rtwdev, addr, data);
+}
+
+static inline void
+rtw89_write8_set(struct rtw89_dev *rtwdev, u32 addr, u8 bit)
+{
+ u8 val;
+
+ val = rtw89_read8(rtwdev, addr);
+ rtw89_write8(rtwdev, addr, val | bit);
+}
+
+static inline void
+rtw89_write16_set(struct rtw89_dev *rtwdev, u32 addr, u16 bit)
+{
+ u16 val;
+
+ val = rtw89_read16(rtwdev, addr);
+ rtw89_write16(rtwdev, addr, val | bit);
+}
+
+static inline void
+rtw89_write32_set(struct rtw89_dev *rtwdev, u32 addr, u32 bit)
+{
+ u32 val;
+
+ val = rtw89_read32(rtwdev, addr);
+ rtw89_write32(rtwdev, addr, val | bit);
+}
+
+static inline void
+rtw89_write8_clr(struct rtw89_dev *rtwdev, u32 addr, u8 bit)
+{
+ u8 val;
+
+ val = rtw89_read8(rtwdev, addr);
+ rtw89_write8(rtwdev, addr, val & ~bit);
+}
+
+static inline void
+rtw89_write16_clr(struct rtw89_dev *rtwdev, u32 addr, u16 bit)
+{
+ u16 val;
+
+ val = rtw89_read16(rtwdev, addr);
+ rtw89_write16(rtwdev, addr, val & ~bit);
+}
+
+static inline void
+rtw89_write32_clr(struct rtw89_dev *rtwdev, u32 addr, u32 bit)
+{
+ u32 val;
+
+ val = rtw89_read32(rtwdev, addr);
+ rtw89_write32(rtwdev, addr, val & ~bit);
+}
+
+static inline u32
+rtw89_read32_mask(struct rtw89_dev *rtwdev, u32 addr, u32 mask)
+{
+ u32 shift = __ffs(mask);
+ u32 orig;
+ u32 ret;
+
+ orig = rtw89_read32(rtwdev, addr);
+ ret = (orig & mask) >> shift;
+
+ return ret;
+}
+
+static inline u16
+rtw89_read16_mask(struct rtw89_dev *rtwdev, u32 addr, u32 mask)
+{
+ u32 shift = __ffs(mask);
+ u32 orig;
+ u32 ret;
+
+ orig = rtw89_read16(rtwdev, addr);
+ ret = (orig & mask) >> shift;
+
+ return ret;
+}
+
+static inline u8
+rtw89_read8_mask(struct rtw89_dev *rtwdev, u32 addr, u32 mask)
+{
+ u32 shift = __ffs(mask);
+ u32 orig;
+ u32 ret;
+
+ orig = rtw89_read8(rtwdev, addr);
+ ret = (orig & mask) >> shift;
+
+ return ret;
+}
+
+static inline void
+rtw89_write32_mask(struct rtw89_dev *rtwdev, u32 addr, u32 mask, u32 data)
+{
+ u32 shift = __ffs(mask);
+ u32 orig;
+ u32 set;
+
+ WARN(addr & 0x3, "should be 4-byte aligned, addr = 0x%08x\n", addr);
+
+ orig = rtw89_read32(rtwdev, addr);
+ set = (orig & ~mask) | ((data << shift) & mask);
+ rtw89_write32(rtwdev, addr, set);
+}
+
+static inline void
+rtw89_write16_mask(struct rtw89_dev *rtwdev, u32 addr, u32 mask, u16 data)
+{
+ u32 shift;
+ u16 orig, set;
+
+ mask &= 0xffff;
+ shift = __ffs(mask);
+
+ orig = rtw89_read16(rtwdev, addr);
+ set = (orig & ~mask) | ((data << shift) & mask);
+ rtw89_write16(rtwdev, addr, set);
+}
+
+static inline void
+rtw89_write8_mask(struct rtw89_dev *rtwdev, u32 addr, u32 mask, u8 data)
+{
+ u32 shift;
+ u8 orig, set;
+
+ mask &= 0xff;
+ shift = __ffs(mask);
+
+ orig = rtw89_read8(rtwdev, addr);
+ set = (orig & ~mask) | ((data << shift) & mask);
+ rtw89_write8(rtwdev, addr, set);
+}
+
+static inline u32
+rtw89_read_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path,
+ u32 addr, u32 mask)
+{
+ u32 val;
+
+ mutex_lock(&rtwdev->rf_mutex);
+ val = rtwdev->chip->ops->read_rf(rtwdev, rf_path, addr, mask);
+ mutex_unlock(&rtwdev->rf_mutex);
+
+ return val;
+}
+
+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);
+ rtwdev->chip->ops->write_rf(rtwdev, rf_path, addr, mask, data);
+ mutex_unlock(&rtwdev->rf_mutex);
+}
+
+static inline struct ieee80211_txq *rtw89_txq_to_txq(struct rtw89_txq *rtwtxq)
+{
+ void *p = rtwtxq;
+
+ return container_of(p, struct ieee80211_txq, drv_priv);
+}
+
+static inline void rtw89_core_txq_init(struct rtw89_dev *rtwdev,
+ struct ieee80211_txq *txq)
+{
+ struct rtw89_txq *rtwtxq;
+
+ if (!txq)
+ return;
+
+ rtwtxq = (struct rtw89_txq *)txq->drv_priv;
+ INIT_LIST_HEAD(&rtwtxq->list);
+}
+
+static inline struct ieee80211_vif *rtwvif_to_vif(struct rtw89_vif *rtwvif)
+{
+ void *p = rtwvif;
+
+ return container_of(p, struct ieee80211_vif, drv_priv);
+}
+
+static inline struct ieee80211_sta *rtwsta_to_sta(struct rtw89_sta *rtwsta)
+{
+ void *p = rtwsta;
+
+ return container_of(p, struct ieee80211_sta, drv_priv);
+}
+
+static inline
+void rtw89_chip_set_channel_prepare(struct rtw89_dev *rtwdev,
+ struct rtw89_channel_help_params *p)
+{
+ rtwdev->chip->ops->set_channel_help(rtwdev, true, p);
+}
+
+static inline
+void rtw89_chip_set_channel_done(struct rtw89_dev *rtwdev,
+ struct rtw89_channel_help_params *p)
+{
+ rtwdev->chip->ops->set_channel_help(rtwdev, false, p);
+}
+
+static inline void rtw89_chip_fem_setup(struct rtw89_dev *rtwdev)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+
+ if (chip->ops->fem_setup)
+ chip->ops->fem_setup(rtwdev);
+}
+
+static inline void rtw89_chip_bb_sethw(struct rtw89_dev *rtwdev)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+
+ if (chip->ops->bb_sethw)
+ chip->ops->bb_sethw(rtwdev);
+}
+
+static inline void rtw89_chip_rfk_init(struct rtw89_dev *rtwdev)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+
+ if (chip->ops->rfk_init)
+ chip->ops->rfk_init(rtwdev);
+}
+
+static inline void rtw89_chip_rfk_channel(struct rtw89_dev *rtwdev)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+
+ if (chip->ops->rfk_channel)
+ chip->ops->rfk_channel(rtwdev);
+}
+
+static inline void rtw89_chip_rfk_band_changed(struct rtw89_dev *rtwdev)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+
+ if (chip->ops->rfk_band_changed)
+ chip->ops->rfk_band_changed(rtwdev);
+}
+
+static inline void rtw89_chip_rfk_track(struct rtw89_dev *rtwdev)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+
+ if (chip->ops->rfk_track)
+ chip->ops->rfk_track(rtwdev);
+}
+
+static inline void rtw89_chip_set_txpwr_ctrl(struct rtw89_dev *rtwdev)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+
+ if (chip->ops->set_txpwr_ctrl)
+ chip->ops->set_txpwr_ctrl(rtwdev);
+}
+
+static inline void rtw89_chip_set_txpwr(struct rtw89_dev *rtwdev)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ u8 ch = rtwdev->hal.current_channel;
+
+ if (!ch)
+ return;
+
+ if (chip->ops->set_txpwr)
+ chip->ops->set_txpwr(rtwdev);
+}
+
+static inline void rtw89_chip_power_trim(struct rtw89_dev *rtwdev)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+
+ if (chip->ops->power_trim)
+ chip->ops->power_trim(rtwdev);
+}
+
+static inline void rtw89_chip_init_txpwr_unit(struct rtw89_dev *rtwdev,
+ enum rtw89_phy_idx phy_idx)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+
+ if (chip->ops->init_txpwr_unit)
+ chip->ops->init_txpwr_unit(rtwdev, phy_idx);
+}
+
+static inline u8 rtw89_chip_get_thermal(struct rtw89_dev *rtwdev,
+ enum rtw89_rf_path rf_path)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+
+ if (!chip->ops->get_thermal)
+ return 0x10;
+
+ return chip->ops->get_thermal(rtwdev, rf_path);
+}
+
+static inline void rtw89_chip_query_ppdu(struct rtw89_dev *rtwdev,
+ struct rtw89_rx_phy_ppdu *phy_ppdu,
+ struct ieee80211_rx_status *status)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+
+ if (chip->ops->query_ppdu)
+ chip->ops->query_ppdu(rtwdev, phy_ppdu, status);
+}
+
+static inline void rtw89_chip_bb_ctrl_btc_preagc(struct rtw89_dev *rtwdev,
+ bool bt_en)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+
+ if (chip->ops->bb_ctrl_btc_preagc)
+ chip->ops->bb_ctrl_btc_preagc(rtwdev, bt_en);
+}
+
+static inline
+void rtw89_chip_cfg_txpwr_ul_tb_offset(struct rtw89_dev *rtwdev,
+ struct ieee80211_vif *vif)
+{
+ struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+
+ if (!vif->bss_conf.he_support || !vif->bss_conf.assoc)
+ return;
+
+ if (chip->ops->set_txpwr_ul_tb_offset)
+ chip->ops->set_txpwr_ul_tb_offset(rtwdev, 0, rtwvif->mac_idx);
+}
+
+static inline void rtw89_load_txpwr_table(struct rtw89_dev *rtwdev,
+ const struct rtw89_txpwr_table *tbl)
+{
+ tbl->load(rtwdev, tbl);
+}
+
+static inline u8 rtw89_regd_get(struct rtw89_dev *rtwdev, u8 band)
+{
+ return rtwdev->regd->txpwr_regd[band];
+}
+
+static inline void rtw89_ctrl_btg(struct rtw89_dev *rtwdev, bool btg)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+
+ if (chip->ops->ctrl_btg)
+ chip->ops->ctrl_btg(rtwdev, btg);
+}
+
+static inline u8 *get_hdr_bssid(struct ieee80211_hdr *hdr)
+{
+ __le16 fc = hdr->frame_control;
+
+ if (ieee80211_has_tods(fc))
+ return hdr->addr1;
+ else if (ieee80211_has_fromds(fc))
+ return hdr->addr2;
+ else
+ return hdr->addr3;
+}
+
+static inline bool rtw89_sta_has_beamformer_cap(struct ieee80211_sta *sta)
+{
+ if ((sta->vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE) ||
+ (sta->vht_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE) ||
+ (sta->he_cap.he_cap_elem.phy_cap_info[3] & IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER) ||
+ (sta->he_cap.he_cap_elem.phy_cap_info[4] & IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER))
+ return true;
+ return false;
+}
+
+static inline struct rtw89_fw_suit *rtw89_fw_suit_get(struct rtw89_dev *rtwdev,
+ enum rtw89_fw_type type)
+{
+ struct rtw89_fw_info *fw_info = &rtwdev->fw;
+
+ if (type == RTW89_FW_WOWLAN)
+ return &fw_info->wowlan;
+ return &fw_info->normal;
+}
+
+int rtw89_core_tx_write(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, struct sk_buff *skb, int *qsel);
+int rtw89_h2c_tx(struct rtw89_dev *rtwdev,
+ struct sk_buff *skb, bool fwdl);
+void rtw89_core_tx_kick_off(struct rtw89_dev *rtwdev, u8 qsel);
+void rtw89_core_fill_txdesc(struct rtw89_dev *rtwdev,
+ struct rtw89_tx_desc_info *desc_info,
+ void *txdesc);
+void rtw89_core_rx(struct rtw89_dev *rtwdev,
+ struct rtw89_rx_desc_info *desc_info,
+ struct sk_buff *skb);
+void rtw89_core_query_rxdesc(struct rtw89_dev *rtwdev,
+ struct rtw89_rx_desc_info *desc_info,
+ u8 *data, u32 data_offset);
+void rtw89_core_napi_start(struct rtw89_dev *rtwdev);
+void rtw89_core_napi_stop(struct rtw89_dev *rtwdev);
+void rtw89_core_napi_init(struct rtw89_dev *rtwdev);
+void rtw89_core_napi_deinit(struct rtw89_dev *rtwdev);
+int rtw89_core_power_on(struct rtw89_dev *rtwdev);
+int rtw89_core_sta_add(struct rtw89_dev *rtwdev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+int rtw89_core_sta_assoc(struct rtw89_dev *rtwdev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+int rtw89_core_sta_disassoc(struct rtw89_dev *rtwdev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+int rtw89_core_sta_disconnect(struct rtw89_dev *rtwdev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+int rtw89_core_sta_remove(struct rtw89_dev *rtwdev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+int rtw89_core_init(struct rtw89_dev *rtwdev);
+void rtw89_core_deinit(struct rtw89_dev *rtwdev);
+int rtw89_core_register(struct rtw89_dev *rtwdev);
+void rtw89_core_unregister(struct rtw89_dev *rtwdev);
+void rtw89_set_channel(struct rtw89_dev *rtwdev);
+u8 rtw89_core_acquire_bit_map(unsigned long *addr, unsigned long size);
+void rtw89_core_release_bit_map(unsigned long *addr, u8 bit);
+void rtw89_core_release_all_bits_map(unsigned long *addr, unsigned int nbits);
+void rtw89_vif_type_mapping(struct ieee80211_vif *vif, bool assoc);
+int rtw89_chip_info_setup(struct rtw89_dev *rtwdev);
+u16 rtw89_ra_report_to_bitrate(struct rtw89_dev *rtwdev, u8 rpt_rate);
+int rtw89_regd_init(struct rtw89_dev *rtwdev,
+ void (*reg_notifier)(struct wiphy *wiphy, struct regulatory_request *request));
+void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request);
+void rtw89_traffic_stats_init(struct rtw89_dev *rtwdev,
+ struct rtw89_traffic_stats *stats);
+int rtw89_core_start(struct rtw89_dev *rtwdev);
+void rtw89_core_stop(struct rtw89_dev *rtwdev);
+
+#endif
diff --git a/drivers/net/wireless/realtek/rtw89/txrx.h b/drivers/net/wireless/realtek/rtw89/txrx.h
new file mode 100644
index 000000000000..0dc99cbc6761
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw89/txrx.h
@@ -0,0 +1,393 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/* Copyright(c) 2020 Realtek Corporation
+ */
+
+#ifndef __RTW89_TXRX_H__
+#define __RTW89_TXRX_H__
+
+#include "debug.h"
+
+#define DATA_RATE_MODE_CTRL_MASK GENMASK(8, 7)
+#define DATA_RATE_NOT_HT_IDX_MASK GENMASK(3, 0)
+#define DATA_RATE_MODE_NON_HT 0x0
+#define DATA_RATE_HT_IDX_MASK GENMASK(4, 0)
+#define DATA_RATE_MODE_HT 0x1
+#define DATA_RATE_VHT_HE_NSS_MASK GENMASK(6, 4)
+#define DATA_RATE_VHT_HE_IDX_MASK GENMASK(3, 0)
+#define DATA_RATE_MODE_VHT 0x2
+#define DATA_RATE_MODE_HE 0x3
+#define GET_DATA_RATE_MODE(r) FIELD_GET(DATA_RATE_MODE_CTRL_MASK, r)
+#define GET_DATA_RATE_NOT_HT_IDX(r) FIELD_GET(DATA_RATE_NOT_HT_IDX_MASK, r)
+#define GET_DATA_RATE_HT_IDX(r) FIELD_GET(DATA_RATE_HT_IDX_MASK, r)
+#define GET_DATA_RATE_VHT_HE_IDX(r) FIELD_GET(DATA_RATE_VHT_HE_IDX_MASK, r)
+#define GET_DATA_RATE_NSS(r) FIELD_GET(DATA_RATE_VHT_HE_NSS_MASK, r)
+
+/* TX WD BODY DWORD 0 */
+#define RTW89_SET_TXWD_BODY_WP_OFFSET(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x00, GENMASK(31, 24))
+#define RTW89_SET_TXWD_BODY_MORE_DATA(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x00, BIT(23))
+#define RTW89_SET_TXWD_BODY_WD_INFO_EN(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x00, BIT(22))
+#define RTW89_SET_TXWD_BODY_FW_DL(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x00, BIT(20))
+#define RTW89_SET_TXWD_BODY_CHANNEL_DMA(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x00, GENMASK(19, 16))
+#define RTW89_SET_TXWD_BODY_HDR_LLC_LEN(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x00, GENMASK(15, 11))
+#define RTW89_SET_TXWD_BODY_WD_PAGE(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x00, BIT(7))
+#define RTW89_SET_TXWD_BODY_HW_AMSDU(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x00, BIT(5))
+
+/* TX WD BODY DWORD 1 */
+#define RTW89_SET_TXWD_BODY_PAYLOAD_ID(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x01, GENMASK(31, 16))
+
+/* TX WD BODY DWORD 2 */
+#define RTW89_SET_TXWD_BODY_MACID(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x02, GENMASK(30, 24))
+#define RTW89_SET_TXWD_BODY_TID_INDICATE(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x02, BIT(23))
+#define RTW89_SET_TXWD_BODY_QSEL(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x02, GENMASK(22, 17))
+#define RTW89_SET_TXWD_BODY_TXPKT_SIZE(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x02, GENMASK(13, 0))
+
+/* TX WD BODY DWORD 3 */
+#define RTW89_SET_TXWD_BODY_BK(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x03, BIT(13))
+#define RTW89_SET_TXWD_BODY_AGG_EN(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x03, BIT(12))
+#define RTW89_SET_TXWD_BODY_SW_SEQ(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x03, GENMASK(11, 0))
+
+/* TX WD BODY DWORD 4 */
+
+/* TX WD BODY DWORD 5 */
+
+/* TX WD INFO DWORD 0 */
+#define RTW89_SET_TXWD_INFO_USE_RATE(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x6, BIT(30))
+#define RTW89_SET_TXWD_INFO_DATA_BW(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x6, GENMASK(29, 28))
+#define RTW89_SET_TXWD_INFO_GI_LTF(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x6, GENMASK(27, 25))
+#define RTW89_SET_TXWD_INFO_DATA_RATE(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x6, GENMASK(24, 16))
+#define RTW89_SET_TXWD_INFO_DISDATAFB(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x6, BIT(10))
+
+/* TX WD INFO DWORD 1 */
+#define RTW89_SET_TXWD_INFO_A_CTRL_BSR(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x7, BIT(14))
+#define RTW89_SET_TXWD_INFO_MAX_AGGNUM(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x7, GENMASK(7, 0))
+
+/* TX WD INFO DWORD 2 */
+#define RTW89_SET_TXWD_INFO_AMPDU_DENSITY(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x8, GENMASK(20, 18))
+#define RTW89_SET_TXWD_INFO_SEC_TYPE(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x8, GENMASK(12, 9))
+#define RTW89_SET_TXWD_INFO_SEC_HW_ENC(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x8, BIT(8))
+#define RTW89_SET_TXWD_INFO_SEC_CAM_IDX(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0x8, GENMASK(7, 0))
+
+/* TX WD INFO DWORD 3 */
+
+/* TX WD INFO DWORD 4 */
+#define RTW89_SET_TXWD_INFO_RTS_EN(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0xA, BIT(27))
+#define RTW89_SET_TXWD_INFO_HW_RTS_EN(txdesc, val) \
+ RTW89_SET_TXWD(txdesc, val, 0xA, BIT(31))
+
+/* TX WD INFO DWORD 5 */
+
+/* RX DESC helpers */
+/* Short Descriptor */
+#define RTW89_GET_RXWD_LONG_RXD(rxdesc) \
+ le32_get_bits((rxdesc)->dword0, BIT(31))
+#define RTW89_GET_RXWD_DRV_INFO_SIZE(rxdesc) \
+ le32_get_bits((rxdesc)->dword0, GENMASK(30, 28))
+#define RTW89_GET_RXWD_RPKT_TYPE(rxdesc) \
+ le32_get_bits((rxdesc)->dword0, GENMASK(27, 24))
+#define RTW89_GET_RXWD_MAC_INFO_VALID(rxdesc) \
+ le32_get_bits((rxdesc)->dword0, BIT(23))
+#define RTW89_GET_RXWD_BB_SEL(rxdesc) \
+ le32_get_bits((rxdesc)->dword0, BIT(22))
+#define RTW89_GET_RXWD_HD_IV_LEN(rxdesc) \
+ le32_get_bits((rxdesc)->dword0, GENMASK(21, 16))
+#define RTW89_GET_RXWD_SHIFT(rxdesc) \
+ le32_get_bits((rxdesc)->dword0, GENMASK(15, 14))
+#define RTW89_GET_RXWD_PKT_SIZE(rxdesc) \
+ le32_get_bits((rxdesc)->dword0, GENMASK(13, 0))
+#define RTW89_GET_RXWD_BW(rxdesc) \
+ le32_get_bits((rxdesc)->dword1, GENMASK(31, 30))
+#define RTW89_GET_RXWD_GI_LTF(rxdesc) \
+ le32_get_bits((rxdesc)->dword1, GENMASK(27, 25))
+#define RTW89_GET_RXWD_DATA_RATE(rxdesc) \
+ le32_get_bits((rxdesc)->dword1, GENMASK(24, 16))
+#define RTW89_GET_RXWD_USER_ID(rxdesc) \
+ le32_get_bits((rxdesc)->dword1, GENMASK(15, 8))
+#define RTW89_GET_RXWD_SR_EN(rxdesc) \
+ le32_get_bits((rxdesc)->dword1, BIT(7))
+#define RTW89_GET_RXWD_PPDU_CNT(rxdesc) \
+ le32_get_bits((rxdesc)->dword1, GENMASK(6, 4))
+#define RTW89_GET_RXWD_PPDU_TYPE(rxdesc) \
+ le32_get_bits((rxdesc)->dword1, GENMASK(3, 0))
+#define RTW89_GET_RXWD_FREE_RUN_CNT(rxdesc) \
+ le32_get_bits((rxdesc)->dword2, GENMASK(31, 0))
+#define RTW89_GET_RXWD_ICV_ERR(rxdesc) \
+ le32_get_bits((rxdesc)->dword3, BIT(10))
+#define RTW89_GET_RXWD_CRC32_ERR(rxdesc) \
+ le32_get_bits((rxdesc)->dword3, BIT(9))
+#define RTW89_GET_RXWD_HW_DEC(rxdesc) \
+ le32_get_bits((rxdesc)->dword3, BIT(2))
+#define RTW89_GET_RXWD_SW_DEC(rxdesc) \
+ le32_get_bits((rxdesc)->dword3, BIT(1))
+#define RTW89_GET_RXWD_A1_MATCH(rxdesc) \
+ le32_get_bits((rxdesc)->dword3, BIT(0))
+
+/* Long Descriptor */
+#define RTW89_GET_RXWD_FRAG(rxdesc) \
+ le32_get_bits((rxdesc)->dword4, GENMASK(31, 28))
+#define RTW89_GET_RXWD_SEQ(rxdesc) \
+ le32_get_bits((rxdesc)->dword4, GENMASK(27, 16))
+#define RTW89_GET_RXWD_TYPE(rxdesc) \
+ le32_get_bits((rxdesc)->dword4, GENMASK(1, 0))
+#define RTW89_GET_RXWD_ADDR_CAM_VLD(rxdesc) \
+ le32_get_bits((rxdesc)->dword5, BIT(28))
+#define RTW89_GET_RXWD_RX_PL_ID(rxdesc) \
+ le32_get_bits((rxdesc)->dword5, GENMASK(27, 24))
+#define RTW89_GET_RXWD_MAC_ID(rxdesc) \
+ le32_get_bits((rxdesc)->dword5, GENMASK(23, 16))
+#define RTW89_GET_RXWD_ADDR_CAM_ID(rxdesc) \
+ le32_get_bits((rxdesc)->dword5, GENMASK(15, 8))
+#define RTW89_GET_RXWD_SEC_CAM_ID(rxdesc) \
+ le32_get_bits((rxdesc)->dword5, GENMASK(7, 0))
+
+#define RTW89_GET_RXINFO_USR_NUM(rpt) \
+ le32_get_bits(*((__le32 *)rpt), GENMASK(3, 0))
+#define RTW89_GET_RXINFO_FW_DEFINE(rpt) \
+ le32_get_bits(*((__le32 *)rpt), GENMASK(15, 8))
+#define RTW89_GET_RXINFO_LSIG_LEN(rpt) \
+ le32_get_bits(*((__le32 *)rpt), GENMASK(27, 16))
+#define RTW89_GET_RXINFO_IS_TO_SELF(rpt) \
+ le32_get_bits(*((__le32 *)rpt), BIT(28))
+#define RTW89_GET_RXINFO_RX_CNT_VLD(rpt) \
+ le32_get_bits(*((__le32 *)rpt), BIT(29))
+#define RTW89_GET_RXINFO_LONG_RXD(rpt) \
+ le32_get_bits(*((__le32 *)rpt), GENMASK(31, 30))
+#define RTW89_GET_RXINFO_SERVICE(rpt) \
+ le32_get_bits(*((__le32 *)(rpt) + 1), GENMASK(15, 0))
+#define RTW89_GET_RXINFO_PLCP_LEN(rpt) \
+ le32_get_bits(*((__le32 *)(rpt) + 1), GENMASK(23, 16))
+#define RTW89_GET_RXINFO_MAC_ID_VALID(rpt, usr) \
+ le32_get_bits(*((__le32 *)(rpt) + (usr) + 2), BIT(0))
+#define RTW89_GET_RXINFO_DATA(rpt, usr) \
+ le32_get_bits(*((__le32 *)(rpt) + (usr) + 2), BIT(1))
+#define RTW89_GET_RXINFO_CTRL(rpt, usr) \
+ le32_get_bits(*((__le32 *)(rpt) + (usr) + 2), BIT(2))
+#define RTW89_GET_RXINFO_MGMT(rpt, usr) \
+ le32_get_bits(*((__le32 *)(rpt) + (usr) + 2), BIT(3))
+#define RTW89_GET_RXINFO_BCM(rpt, usr) \
+ le32_get_bits(*((__le32 *)(rpt) + (usr) + 2), BIT(4))
+#define RTW89_GET_RXINFO_MACID(rpt, usr) \
+ le32_get_bits(*((__le32 *)(rpt) + (usr) + 2), GENMASK(15, 8))
+
+#define RTW89_GET_PHY_STS_RSSI_A(sts) \
+ le32_get_bits(*((__le32 *)(sts) + 1), GENMASK(7, 0))
+#define RTW89_GET_PHY_STS_RSSI_B(sts) \
+ le32_get_bits(*((__le32 *)(sts) + 1), GENMASK(15, 8))
+#define RTW89_GET_PHY_STS_RSSI_C(sts) \
+ le32_get_bits(*((__le32 *)(sts) + 1), GENMASK(23, 16))
+#define RTW89_GET_PHY_STS_RSSI_D(sts) \
+ le32_get_bits(*((__le32 *)(sts) + 1), GENMASK(31, 24))
+#define RTW89_GET_PHY_STS_LEN(sts) \
+ le32_get_bits(*((__le32 *)sts), GENMASK(15, 8))
+#define RTW89_GET_PHY_STS_RSSI_AVG(sts) \
+ le32_get_bits(*((__le32 *)sts), GENMASK(31, 24))
+#define RTW89_GET_PHY_STS_IE_TYPE(ie) \
+ le32_get_bits(*((__le32 *)ie), GENMASK(4, 0))
+#define RTW89_GET_PHY_STS_IE_LEN(ie) \
+ le32_get_bits(*((__le32 *)ie), GENMASK(11, 5))
+#define RTW89_GET_PHY_STS_IE0_CFO(ie) \
+ le32_get_bits(*((__le32 *)(ie) + 1), GENMASK(31, 20))
+
+enum rtw89_tx_channel {
+ RTW89_TXCH_ACH0 = 0,
+ RTW89_TXCH_ACH1 = 1,
+ RTW89_TXCH_ACH2 = 2,
+ RTW89_TXCH_ACH3 = 3,
+ RTW89_TXCH_ACH4 = 4,
+ RTW89_TXCH_ACH5 = 5,
+ RTW89_TXCH_ACH6 = 6,
+ RTW89_TXCH_ACH7 = 7,
+ RTW89_TXCH_CH8 = 8, /* MGMT Band 0 */
+ RTW89_TXCH_CH9 = 9, /* HI Band 0 */
+ RTW89_TXCH_CH10 = 10, /* MGMT Band 1 */
+ RTW89_TXCH_CH11 = 11, /* HI Band 1 */
+ RTW89_TXCH_CH12 = 12, /* FW CMD */
+
+ /* keep last */
+ RTW89_TXCH_NUM,
+ RTW89_TXCH_MAX = RTW89_TXCH_NUM - 1
+};
+
+enum rtw89_rx_channel {
+ RTW89_RXCH_RXQ = 0,
+ RTW89_RXCH_RPQ = 1,
+
+ /* keep last */
+ RTW89_RXCH_NUM,
+ RTW89_RXCH_MAX = RTW89_RXCH_NUM - 1
+};
+
+enum rtw89_tx_qsel {
+ RTW89_TX_QSEL_BE_0 = 0x00,
+ RTW89_TX_QSEL_BK_0 = 0x01,
+ RTW89_TX_QSEL_VI_0 = 0x02,
+ RTW89_TX_QSEL_VO_0 = 0x03,
+ RTW89_TX_QSEL_BE_1 = 0x04,
+ RTW89_TX_QSEL_BK_1 = 0x05,
+ RTW89_TX_QSEL_VI_1 = 0x06,
+ RTW89_TX_QSEL_VO_1 = 0x07,
+ RTW89_TX_QSEL_BE_2 = 0x08,
+ RTW89_TX_QSEL_BK_2 = 0x09,
+ RTW89_TX_QSEL_VI_2 = 0x0a,
+ RTW89_TX_QSEL_VO_2 = 0x0b,
+ RTW89_TX_QSEL_BE_3 = 0x0c,
+ RTW89_TX_QSEL_BK_3 = 0x0d,
+ RTW89_TX_QSEL_VI_3 = 0x0e,
+ RTW89_TX_QSEL_VO_3 = 0x0f,
+ RTW89_TX_QSEL_B0_BCN = 0x10,
+ RTW89_TX_QSEL_B0_HI = 0x11,
+ RTW89_TX_QSEL_B0_MGMT = 0x12,
+ RTW89_TX_QSEL_B0_NOPS = 0x13,
+ RTW89_TX_QSEL_B0_MGMT_FAST = 0x14,
+ /* reserved */
+ /* reserved */
+ /* reserved */
+ RTW89_TX_QSEL_B1_BCN = 0x18,
+ RTW89_TX_QSEL_B1_HI = 0x19,
+ RTW89_TX_QSEL_B1_MGMT = 0x1a,
+ RTW89_TX_QSEL_B1_NOPS = 0x1b,
+ RTW89_TX_QSEL_B1_MGMT_FAST = 0x1c,
+ /* reserved */
+ /* reserved */
+ /* reserved */
+};
+
+enum rtw89_phy_status_ie_type {
+ RTW89_PHYSTS_IE00_CMN_CCK = 0,
+ RTW89_PHYSTS_IE01_CMN_OFDM = 1,
+ RTW89_PHYSTS_IE02_CMN_EXT_AX = 2,
+ RTW89_PHYSTS_IE03_CMN_EXT_SEG_1 = 3,
+ RTW89_PHYSTS_IE04_CMN_EXT_PATH_A = 4,
+ RTW89_PHYSTS_IE05_CMN_EXT_PATH_B = 5,
+ RTW89_PHYSTS_IE06_CMN_EXT_PATH_C = 6,
+ RTW89_PHYSTS_IE07_CMN_EXT_PATH_D = 7,
+ RTW89_PHYSTS_IE08_FTR_CH = 8,
+ RTW89_PHYSTS_IE09_FTR_PLCP_0 = 9,
+ RTW89_PHYSTS_IE10_FTR_PLCP_EXT = 10,
+ RTW89_PHYSTS_IE11_FTR_PLCP_HISTOGRAM = 11,
+ RTW89_PHYSTS_IE12_MU_EIGEN_INFO = 12,
+ RTW89_PHYSTS_IE13_DL_MU_DEF = 13,
+ RTW89_PHYSTS_IE14_TB_UL_CQI = 14,
+ RTW89_PHYSTS_IE15_TB_UL_DEF = 15,
+ RTW89_PHYSTS_IE16_RSVD16 = 16,
+ RTW89_PHYSTS_IE17_TB_UL_CTRL = 17,
+ RTW89_PHYSTS_IE18_DBG_OFDM_FD_CMN = 18,
+ RTW89_PHYSTS_IE19_DBG_OFDM_TD_CMN = 19,
+ RTW89_PHYSTS_IE20_DBG_OFDM_FD_USER_SEG_0 = 20,
+ RTW89_PHYSTS_IE21_DBG_OFDM_FD_USER_SEG_1 = 21,
+ RTW89_PHYSTS_IE22_DBG_OFDM_FD_USER_AGC = 22,
+ RTW89_PHYSTS_IE23_RSVD23 = 23,
+ RTW89_PHYSTS_IE24_DBG_OFDM_TD_PATH_A = 24,
+ RTW89_PHYSTS_IE25_DBG_OFDM_TD_PATH_B = 25,
+ RTW89_PHYSTS_IE26_DBG_OFDM_TD_PATH_C = 26,
+ RTW89_PHYSTS_IE27_DBG_OFDM_TD_PATH_D = 27,
+ RTW89_PHYSTS_IE28_DBG_CCK_PATH_A = 28,
+ RTW89_PHYSTS_IE29_DBG_CCK_PATH_B = 29,
+ RTW89_PHYSTS_IE30_DBG_CCK_PATH_C = 30,
+ RTW89_PHYSTS_IE31_DBG_CCK_PATH_D = 31,
+
+ /* keep last */
+ RTW89_PHYSTS_IE_NUM,
+ RTW89_PHYSTS_IE_MAX = RTW89_PHYSTS_IE_NUM - 1
+};
+
+static inline u8 rtw89_core_get_qsel(struct rtw89_dev *rtwdev, u8 tid)
+{
+ switch (tid) {
+ default:
+ rtw89_warn(rtwdev, "Should use tag 1d: %d\n", tid);
+ fallthrough;
+ case 0:
+ case 3:
+ return RTW89_TX_QSEL_BE_0;
+ case 1:
+ case 2:
+ return RTW89_TX_QSEL_BK_0;
+ case 4:
+ case 5:
+ return RTW89_TX_QSEL_VI_0;
+ case 6:
+ case 7:
+ return RTW89_TX_QSEL_VO_0;
+ }
+}
+
+static inline u8 rtw89_core_get_ch_dma(struct rtw89_dev *rtwdev, u8 qsel)
+{
+ switch (qsel) {
+ default:
+ rtw89_warn(rtwdev, "Cannot map qsel to dma: %d\n", qsel);
+ fallthrough;
+ case RTW89_TX_QSEL_BE_0:
+ return RTW89_TXCH_ACH0;
+ case RTW89_TX_QSEL_BK_0:
+ return RTW89_TXCH_ACH1;
+ case RTW89_TX_QSEL_VI_0:
+ return RTW89_TXCH_ACH2;
+ case RTW89_TX_QSEL_VO_0:
+ return RTW89_TXCH_ACH3;
+ case RTW89_TX_QSEL_B0_MGMT:
+ return RTW89_TXCH_CH8;
+ case RTW89_TX_QSEL_B0_HI:
+ return RTW89_TXCH_CH9;
+ case RTW89_TX_QSEL_B1_MGMT:
+ return RTW89_TXCH_CH10;
+ case RTW89_TX_QSEL_B1_HI:
+ return RTW89_TXCH_CH11;
+ }
+}
+
+static inline u8 rtw89_core_get_tid_indicate(struct rtw89_dev *rtwdev, u8 tid)
+{
+ switch (tid) {
+ case 3:
+ case 2:
+ case 5:
+ case 7:
+ return 1;
+ default:
+ rtw89_warn(rtwdev, "Should use tag 1d: %d\n", tid);
+ fallthrough;
+ case 0:
+ case 1:
+ case 4:
+ case 6:
+ return 0;
+ }
+}
+
+static __always_inline void RTW89_SET_TXWD(u8 *txdesc, u32 val, u8 offset, u32 mask)
+{
+ u32 *txd32 = (u32 *)txdesc;
+
+ le32p_replace_bits((__le32 *)(txd32 + offset), val, mask);
+}
+
+#endif
diff --git a/drivers/net/wireless/realtek/rtw89/util.c b/drivers/net/wireless/realtek/rtw89/util.c
new file mode 100644
index 000000000000..a0650bb88d0c
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw89/util.c
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Copyright(c) 2019-2020 Realtek Corporation
+ */
+
+#include "util.h"
+
+static void
+__rtw89_vifs_collect_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+ struct list_head *vif_list = (struct list_head *)data;
+ struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
+
+ list_add_tail(&rtwvif->list, vif_list);
+}
+
+void __rtw89_iterate_vifs(struct rtw89_dev *rtwdev,
+ void (*iterator)(void *data, u8 *mac,
+ struct ieee80211_vif *vif),
+ void *data)
+{
+ struct ieee80211_vif *vif;
+ struct rtw89_vif *rtwvif;
+ LIST_HEAD(vif_list);
+
+ /* iflist_mtx & mutex are held */
+ lockdep_assert_held(&rtwdev->mutex);
+
+ /* Since iflist_mtx is held, we can use vif outside of iterator */
+ ieee80211_iterate_active_interfaces_atomic(rtwdev->hw,
+ IEEE80211_IFACE_ITER_NORMAL, __rtw89_vifs_collect_iter,
+ &vif_list);
+
+ list_for_each_entry(rtwvif, &vif_list, list) {
+ vif = rtwvif_to_vif(rtwvif);
+ iterator(data, vif->addr, vif);
+ }
+}
diff --git a/drivers/net/wireless/realtek/rtw89/util.h b/drivers/net/wireless/realtek/rtw89/util.h
new file mode 100644
index 000000000000..935ceecdbb75
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw89/util.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+ * Copyright(c) 2019-2020 Realtek Corporation
+ */
+#ifndef __RTW89_UTIL_H__
+#define __RTW89_UTIL_H__
+
+#include "core.h"
+
+#define rtw89_iterate_vifs_bh(rtwdev, iterator, data) \
+ ieee80211_iterate_active_interfaces_atomic((rtwdev)->hw, \
+ IEEE80211_IFACE_ITER_NORMAL, iterator, data)
+void __rtw89_iterate_vifs(struct rtw89_dev *rtwdev,
+ void (*iterator)(void *data, u8 *mac,
+ struct ieee80211_vif *vif),
+ void *data);
+static inline
+void rtw89_iterate_vifs(struct rtw89_dev *rtwdev,
+ void (*iterator)(void *data, u8 *mac,
+ struct ieee80211_vif *vif),
+ void *data, bool held_vifmtx)
+{
+ if (!held_vifmtx) {
+ ieee80211_iterate_active_interfaces((rtwdev)->hw,
+ IEEE80211_IFACE_ITER_NORMAL, iterator, data);
+ return;
+ }
+
+ __rtw89_iterate_vifs(rtwdev, iterator, data);
+}
+
+#endif
--
2.25.1

2021-06-18 06:50:27

by Pkshih

[permalink] [raw]
Subject: [PATCH 04/24] rtw89: add debug files

To recognize issues happened in field, two debug methods, debug message and
debugfs, are added.

The debug messages are written to kernel log, and four levels can be chosen
according to the cases -- debug, info, warn and err.

Debugfs is used to read and write registers and driver status.

Signed-off-by: Ping-Ke Shih <[email protected]>
---
drivers/net/wireless/realtek/rtw89/debug.c | 2404 ++++++++++++++++++++
drivers/net/wireless/realtek/rtw89/debug.h | 77 +
2 files changed, 2481 insertions(+)
create mode 100644 drivers/net/wireless/realtek/rtw89/debug.c
create mode 100644 drivers/net/wireless/realtek/rtw89/debug.h

diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c
new file mode 100644
index 000000000000..03d2d2eb813d
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw89/debug.c
@@ -0,0 +1,2404 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Copyright(c) 2019-2020 Realtek Corporation
+ */
+
+#include "coex.h"
+#include "debug.h"
+#include "fw.h"
+#include "mac.h"
+#include "ps.h"
+#include "reg.h"
+#include "sar.h"
+
+#ifdef CONFIG_RTW89_DEBUGMSG
+unsigned int rtw89_debug_mask;
+EXPORT_SYMBOL(rtw89_debug_mask);
+module_param_named(debug_mask, rtw89_debug_mask, uint, 0644);
+MODULE_PARM_DESC(debug_mask, "Debugging mask");
+#endif
+
+#ifdef CONFIG_RTW89_DEBUGFS
+struct rtw89_debugfs_priv {
+ struct rtw89_dev *rtwdev;
+ int (*cb_read)(struct seq_file *m, void *v);
+ ssize_t (*cb_write)(struct file *filp, const char __user *buffer,
+ size_t count, loff_t *loff);
+ union {
+ u32 cb_data;
+ struct {
+ u32 addr;
+ u8 len;
+ } read_reg;
+ struct {
+ u32 addr;
+ u32 mask;
+ u8 path;
+ } read_rf;
+ struct {
+ u8 ss_dbg:1;
+ u8 dle_dbg:1;
+ u8 dmac_dbg:1;
+ u8 cmac_dbg:1;
+ u8 dbg_port:1;
+ } dbgpkg_en;
+ struct {
+ u32 start;
+ u32 len;
+ u8 sel;
+ } mac_mem;
+ };
+};
+
+static int rtw89_debugfs_single_show(struct seq_file *m, void *v)
+{
+ struct rtw89_debugfs_priv *debugfs_priv = m->private;
+
+ return debugfs_priv->cb_read(m, v);
+}
+
+static ssize_t rtw89_debugfs_single_write(struct file *filp,
+ const char __user *buffer,
+ size_t count, loff_t *loff)
+{
+ struct rtw89_debugfs_priv *debugfs_priv = filp->private_data;
+
+ return debugfs_priv->cb_write(filp, buffer, count, loff);
+}
+
+static ssize_t rtw89_debugfs_seq_file_write(struct file *filp,
+ const char __user *buffer,
+ size_t count, loff_t *loff)
+{
+ struct seq_file *seqpriv = (struct seq_file *)filp->private_data;
+ struct rtw89_debugfs_priv *debugfs_priv = seqpriv->private;
+
+ return debugfs_priv->cb_write(filp, buffer, count, loff);
+}
+
+static int rtw89_debugfs_single_open(struct inode *inode, struct file *filp)
+{
+ return single_open(filp, rtw89_debugfs_single_show, inode->i_private);
+}
+
+static int rtw89_debugfs_close(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+static const struct file_operations file_ops_single_r = {
+ .owner = THIS_MODULE,
+ .open = rtw89_debugfs_single_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations file_ops_common_rw = {
+ .owner = THIS_MODULE,
+ .open = rtw89_debugfs_single_open,
+ .release = single_release,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .write = rtw89_debugfs_seq_file_write,
+};
+
+static const struct file_operations file_ops_single_w = {
+ .owner = THIS_MODULE,
+ .write = rtw89_debugfs_single_write,
+ .open = simple_open,
+ .release = rtw89_debugfs_close,
+};
+
+static ssize_t
+rtw89_debug_priv_read_reg_select(struct file *filp,
+ const char __user *user_buf,
+ size_t count, loff_t *loff)
+{
+ struct seq_file *m = (struct seq_file *)filp->private_data;
+ struct rtw89_debugfs_priv *debugfs_priv = m->private;
+ struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
+ char buf[32];
+ size_t buf_size;
+ u32 addr, len;
+ int num;
+
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ buf[buf_size] = '\0';
+ num = sscanf(buf, "%x %x", &addr, &len);
+ if (num != 2) {
+ rtw89_info(rtwdev, "invalid format: <addr> <len>\n");
+ return -EINVAL;
+ }
+
+ debugfs_priv->read_reg.addr = addr;
+ debugfs_priv->read_reg.len = len;
+
+ rtw89_info(rtwdev, "select read %d bytes from 0x%08x\n", len, addr);
+
+ return count;
+}
+
+static int rtw89_debug_priv_read_reg_get(struct seq_file *m, void *v)
+{
+ struct rtw89_debugfs_priv *debugfs_priv = m->private;
+ struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
+ u32 addr, data;
+ u8 len;
+
+ len = debugfs_priv->read_reg.len;
+ addr = debugfs_priv->read_reg.addr;
+
+ switch (len) {
+ case 1:
+ data = rtw89_read8(rtwdev, addr);
+ break;
+ case 2:
+ data = rtw89_read16(rtwdev, addr);
+ break;
+ case 4:
+ data = rtw89_read32(rtwdev, addr);
+ break;
+ default:
+ rtw89_info(rtwdev, "invalid read reg len %d\n", len);
+ return -EINVAL;
+ }
+
+ seq_printf(m, "get %d bytes at 0x%08x=0x%08x\n", len, addr, data);
+
+ return 0;
+}
+
+static ssize_t rtw89_debug_priv_write_reg_set(struct file *filp,
+ const char __user *user_buf,
+ size_t count, loff_t *loff)
+{
+ struct rtw89_debugfs_priv *debugfs_priv = filp->private_data;
+ struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
+ char buf[32];
+ size_t buf_size;
+ u32 addr, val, len;
+ int num;
+
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ buf[buf_size] = '\0';
+ num = sscanf(buf, "%x %x %x", &addr, &val, &len);
+ if (num != 3) {
+ rtw89_info(rtwdev, "invalid format: <addr> <val> <len>\n");
+ return -EINVAL;
+ }
+
+ switch (len) {
+ case 1:
+ rtw89_info(rtwdev, "reg write8 0x%08x: 0x%02x\n", addr, val);
+ rtw89_write8(rtwdev, addr, (u8)val);
+ break;
+ case 2:
+ rtw89_info(rtwdev, "reg write16 0x%08x: 0x%04x\n", addr, val);
+ rtw89_write16(rtwdev, addr, (u16)val);
+ break;
+ case 4:
+ rtw89_info(rtwdev, "reg write32 0x%08x: 0x%08x\n", addr, val);
+ rtw89_write32(rtwdev, addr, (u32)val);
+ break;
+ default:
+ rtw89_info(rtwdev, "invalid read write len %d\n", len);
+ break;
+ }
+
+ return count;
+}
+
+static ssize_t
+rtw89_debug_priv_read_rf_select(struct file *filp,
+ const char __user *user_buf,
+ size_t count, loff_t *loff)
+{
+ struct seq_file *m = (struct seq_file *)filp->private_data;
+ struct rtw89_debugfs_priv *debugfs_priv = m->private;
+ struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
+ char buf[32];
+ size_t buf_size;
+ u32 addr, mask;
+ u8 path;
+ int num;
+
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ buf[buf_size] = '\0';
+ num = sscanf(buf, "%hhd %x %x", &path, &addr, &mask);
+ if (num != 3) {
+ rtw89_info(rtwdev, "invalid format: <path> <addr> <mask>\n");
+ return -EINVAL;
+ }
+
+ if (path >= rtwdev->chip->rf_path_num) {
+ rtw89_info(rtwdev, "wrong rf path\n");
+ return -EINVAL;
+ }
+ debugfs_priv->read_rf.addr = addr;
+ debugfs_priv->read_rf.mask = mask;
+ debugfs_priv->read_rf.path = path;
+
+ rtw89_info(rtwdev, "select read rf path %d from 0x%08x\n", path, addr);
+
+ return count;
+}
+
+static int rtw89_debug_priv_read_rf_get(struct seq_file *m, void *v)
+{
+ struct rtw89_debugfs_priv *debugfs_priv = m->private;
+ struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
+ u32 addr, data, mask;
+ u8 path;
+
+ addr = debugfs_priv->read_rf.addr;
+ mask = debugfs_priv->read_rf.mask;
+ path = debugfs_priv->read_rf.path;
+
+ data = rtw89_read_rf(rtwdev, path, addr, mask);
+
+ seq_printf(m, "path %d, rf register 0x%08x=0x%08x\n", path, addr, data);
+
+ return 0;
+}
+
+static ssize_t rtw89_debug_priv_write_rf_set(struct file *filp,
+ const char __user *user_buf,
+ size_t count, loff_t *loff)
+{
+ struct rtw89_debugfs_priv *debugfs_priv = filp->private_data;
+ struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
+ char buf[32];
+ size_t buf_size;
+ u32 addr, val, mask;
+ u8 path;
+ int num;
+
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ buf[buf_size] = '\0';
+ num = sscanf(buf, "%hhd %x %x %x", &path, &addr, &mask, &val);
+ if (num != 4) {
+ rtw89_info(rtwdev, "invalid format: <path> <addr> <mask> <val>\n");
+ return -EINVAL;
+ }
+
+ if (path >= rtwdev->chip->rf_path_num) {
+ rtw89_info(rtwdev, "wrong rf path\n");
+ return -EINVAL;
+ }
+
+ rtw89_info(rtwdev, "path %d, rf register write 0x%08x=0x%08x (mask = 0x%08x)\n",
+ path, addr, val, mask);
+ rtw89_write_rf(rtwdev, path, addr, mask, val);
+
+ return count;
+}
+
+static int rtw89_debug_priv_rf_reg_dump_get(struct seq_file *m, void *v)
+{
+ struct rtw89_debugfs_priv *debugfs_priv = m->private;
+ struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ u32 addr, offset, data;
+ u8 path;
+
+ for (path = 0; path < chip->rf_path_num; path++) {
+ seq_printf(m, "RF path %d:\n\n", path);
+ for (addr = 0; addr < 0x100; addr += 4) {
+ seq_printf(m, "0x%08x: ", addr);
+ for (offset = 0; offset < 4; offset++) {
+ data = rtw89_read_rf(rtwdev, path,
+ addr + offset, RFREG_MASK);
+ seq_printf(m, "0x%05x ", data);
+ }
+ seq_puts(m, "\n");
+ }
+ seq_puts(m, "\n");
+ }
+
+ return 0;
+}
+
+struct txpwr_ent {
+ const char *txt;
+ u8 len;
+};
+
+struct txpwr_map {
+ const struct txpwr_ent *ent;
+ u8 size;
+ u32 addr_from;
+ u32 addr_to;
+};
+
+#define __GEN_TXPWR_ENT2(_t, _e0, _e1) \
+ { .len = 2, .txt = _t "\t- " _e0 " " _e1 }
+
+#define __GEN_TXPWR_ENT4(_t, _e0, _e1, _e2, _e3) \
+ { .len = 4, .txt = _t "\t- " _e0 " " _e1 " " _e2 " " _e3 }
+
+#define __GEN_TXPWR_ENT8(_t, _e0, _e1, _e2, _e3, _e4, _e5, _e6, _e7) \
+ { .len = 8, .txt = _t "\t- " \
+ _e0 " " _e1 " " _e2 " " _e3 " " \
+ _e4 " " _e5 " " _e6 " " _e7 }
+
+static const struct txpwr_ent __txpwr_ent_byr[] = {
+ __GEN_TXPWR_ENT4("CCK ", "1M ", "2M ", "5.5M ", "11M "),
+ __GEN_TXPWR_ENT4("LEGACY ", "6M ", "9M ", "12M ", "18M "),
+ __GEN_TXPWR_ENT4("LEGACY ", "24M ", "36M ", "48M ", "54M "),
+ /* 1NSS */
+ __GEN_TXPWR_ENT4("MCS_1NSS ", "MCS0 ", "MCS1 ", "MCS2 ", "MCS3 "),
+ __GEN_TXPWR_ENT4("MCS_1NSS ", "MCS4 ", "MCS5 ", "MCS6 ", "MCS7 "),
+ __GEN_TXPWR_ENT4("MCS_1NSS ", "MCS8 ", "MCS9 ", "MCS10", "MCS11"),
+ __GEN_TXPWR_ENT4("HEDCM_1NSS", "MCS0 ", "MCS1 ", "MCS3 ", "MCS4 "),
+ /* 2NSS */
+ __GEN_TXPWR_ENT4("MCS_2NSS ", "MCS0 ", "MCS1 ", "MCS2 ", "MCS3 "),
+ __GEN_TXPWR_ENT4("MCS_2NSS ", "MCS4 ", "MCS5 ", "MCS6 ", "MCS7 "),
+ __GEN_TXPWR_ENT4("MCS_2NSS ", "MCS8 ", "MCS9 ", "MCS10", "MCS11"),
+ __GEN_TXPWR_ENT4("HEDCM_2NSS", "MCS0 ", "MCS1 ", "MCS3 ", "MCS4 "),
+};
+
+static_assert((ARRAY_SIZE(__txpwr_ent_byr) * 4) ==
+ (R_AX_PWR_BY_RATE_MAX - R_AX_PWR_BY_RATE + 4));
+
+static const struct txpwr_map __txpwr_map_byr = {
+ .ent = __txpwr_ent_byr,
+ .size = ARRAY_SIZE(__txpwr_ent_byr),
+ .addr_from = R_AX_PWR_BY_RATE,
+ .addr_to = R_AX_PWR_BY_RATE_MAX,
+};
+
+static const struct txpwr_ent __txpwr_ent_lmt[] = {
+ /* 1TX */
+ __GEN_TXPWR_ENT2("CCK_1TX_20M ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("CCK_1TX_40M ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("OFDM_1TX ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_1TX_20M_0 ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_1TX_20M_1 ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_1TX_20M_2 ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_1TX_20M_3 ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_1TX_20M_4 ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_1TX_20M_5 ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_1TX_20M_6 ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_1TX_20M_7 ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_1TX_40M_0 ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_1TX_40M_1 ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_1TX_40M_2 ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_1TX_40M_3 ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_1TX_80M_0 ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_1TX_80M_1 ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_1TX_160M ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_1TX_40M_0p5", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_1TX_40M_2p5", "NON_BF", "BF"),
+ /* 2TX */
+ __GEN_TXPWR_ENT2("CCK_2TX_20M ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("CCK_2TX_40M ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("OFDM_2TX ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_2TX_20M_0 ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_2TX_20M_1 ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_2TX_20M_2 ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_2TX_20M_3 ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_2TX_20M_4 ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_2TX_20M_5 ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_2TX_20M_6 ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_2TX_20M_7 ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_2TX_40M_0 ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_2TX_40M_1 ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_2TX_40M_2 ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_2TX_40M_3 ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_2TX_80M_0 ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_2TX_80M_1 ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_2TX_160M ", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_2TX_40M_0p5", "NON_BF", "BF"),
+ __GEN_TXPWR_ENT2("MCS_2TX_40M_2p5", "NON_BF", "BF"),
+};
+
+static_assert((ARRAY_SIZE(__txpwr_ent_lmt) * 2) ==
+ (R_AX_PWR_LMT_MAX - R_AX_PWR_LMT + 4));
+
+static const struct txpwr_map __txpwr_map_lmt = {
+ .ent = __txpwr_ent_lmt,
+ .size = ARRAY_SIZE(__txpwr_ent_lmt),
+ .addr_from = R_AX_PWR_LMT,
+ .addr_to = R_AX_PWR_LMT_MAX,
+};
+
+static const struct txpwr_ent __txpwr_ent_lmt_ru[] = {
+ /* 1TX */
+ __GEN_TXPWR_ENT8("1TX", "RU26__0", "RU26__1", "RU26__2", "RU26__3",
+ "RU26__4", "RU26__5", "RU26__6", "RU26__7"),
+ __GEN_TXPWR_ENT8("1TX", "RU52__0", "RU52__1", "RU52__2", "RU52__3",
+ "RU52__4", "RU52__5", "RU52__6", "RU52__7"),
+ __GEN_TXPWR_ENT8("1TX", "RU106_0", "RU106_1", "RU106_2", "RU106_3",
+ "RU106_4", "RU106_5", "RU106_6", "RU106_7"),
+ /* 2TX */
+ __GEN_TXPWR_ENT8("2TX", "RU26__0", "RU26__1", "RU26__2", "RU26__3",
+ "RU26__4", "RU26__5", "RU26__6", "RU26__7"),
+ __GEN_TXPWR_ENT8("2TX", "RU52__0", "RU52__1", "RU52__2", "RU52__3",
+ "RU52__4", "RU52__5", "RU52__6", "RU52__7"),
+ __GEN_TXPWR_ENT8("2TX", "RU106_0", "RU106_1", "RU106_2", "RU106_3",
+ "RU106_4", "RU106_5", "RU106_6", "RU106_7"),
+};
+
+static_assert((ARRAY_SIZE(__txpwr_ent_lmt_ru) * 8) ==
+ (R_AX_PWR_RU_LMT_MAX - R_AX_PWR_RU_LMT + 4));
+
+static const struct txpwr_map __txpwr_map_lmt_ru = {
+ .ent = __txpwr_ent_lmt_ru,
+ .size = ARRAY_SIZE(__txpwr_ent_lmt_ru),
+ .addr_from = R_AX_PWR_RU_LMT,
+ .addr_to = R_AX_PWR_RU_LMT_MAX,
+};
+
+static u8 __print_txpwr_ent(struct seq_file *m, const struct txpwr_ent *ent,
+ const u8 *buf, const u8 cur)
+{
+ char *fmt;
+
+ switch (ent->len) {
+ case 2:
+ fmt = "%s\t| %3d, %3d,\tdBm\n";
+ seq_printf(m, fmt, ent->txt, buf[cur], buf[cur + 1]);
+ return 2;
+ case 4:
+ fmt = "%s\t| %3d, %3d, %3d, %3d,\tdBm\n";
+ seq_printf(m, fmt, ent->txt, buf[cur], buf[cur + 1],
+ buf[cur + 2], buf[cur + 3]);
+ return 4;
+ case 8:
+ fmt = "%s\t| %3d, %3d, %3d, %3d, %3d, %3d, %3d, %3d,\tdBm\n";
+ seq_printf(m, fmt, ent->txt, buf[cur], buf[cur + 1],
+ buf[cur + 2], buf[cur + 3], buf[cur + 4],
+ buf[cur + 5], buf[cur + 6], buf[cur + 7]);
+ return 8;
+ default:
+ return 0;
+ }
+}
+
+static int __print_txpwr_map(struct seq_file *m, struct rtw89_dev *rtwdev,
+ const struct txpwr_map *map)
+{
+ u8 fct = rtwdev->chip->txpwr_factor_mac;
+ u8 *buf, cur, i;
+ u32 val, addr;
+ int ret;
+
+ buf = vzalloc(map->addr_to - map->addr_from + 4);
+ if (!buf)
+ return -ENOMEM;
+
+ for (addr = map->addr_from; addr <= map->addr_to; addr += 4) {
+ ret = rtw89_mac_txpwr_read32(rtwdev, RTW89_PHY_0, addr, &val);
+ if (ret)
+ val = MASKDWORD;
+
+ cur = addr - map->addr_from;
+ for (i = 0; i < 4; i++, val >>= 8)
+ buf[cur + i] = FIELD_GET(MASKBYTE0, val) >> fct;
+ }
+
+ for (cur = 0, i = 0; i < map->size; i++)
+ cur += __print_txpwr_ent(m, &map->ent[i], buf, cur);
+
+ vfree(buf);
+ return 0;
+}
+
+#define case_REGD(_regd) \
+ case RTW89_ ## _regd: \
+ seq_puts(m, #_regd "\n"); \
+ break
+
+static void __print_regd(struct seq_file *m, struct rtw89_dev *rtwdev)
+{
+ u8 band = rtwdev->hal.current_band_type;
+ u8 regd = rtw89_regd_get(rtwdev, band);
+
+ switch (regd) {
+ default:
+ seq_printf(m, "UNKNOWN: %d\n", regd);
+ break;
+ case_REGD(WW);
+ case_REGD(ETSI);
+ case_REGD(FCC);
+ case_REGD(MKK);
+ case_REGD(NA);
+ case_REGD(IC);
+ case_REGD(KCC);
+ case_REGD(NCC);
+ case_REGD(CHILE);
+ case_REGD(ACMA);
+ case_REGD(MEXICO);
+ case_REGD(UKRAINE);
+ case_REGD(CN);
+ }
+}
+
+#undef case_REGD
+
+static int rtw89_debug_priv_txpwr_table_get(struct seq_file *m, void *v)
+{
+ struct rtw89_debugfs_priv *debugfs_priv = m->private;
+ struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
+ int ret = 0;
+
+ mutex_lock(&rtwdev->mutex);
+ rtw89_leave_ps_mode(rtwdev);
+
+ seq_puts(m, "[Regulatory] ");
+ __print_regd(m, rtwdev);
+
+ seq_puts(m, "[SAR]\n");
+ rtw89_print_sar(m, rtwdev);
+
+ seq_puts(m, "\n[TX power byrate]\n");
+ ret = __print_txpwr_map(m, rtwdev, &__txpwr_map_byr);
+ if (ret)
+ goto err;
+
+ seq_puts(m, "\n[TX power limit]\n");
+ ret = __print_txpwr_map(m, rtwdev, &__txpwr_map_lmt);
+ if (ret)
+ goto err;
+
+ seq_puts(m, "\n[TX power limit_ru]\n");
+ ret = __print_txpwr_map(m, rtwdev, &__txpwr_map_lmt_ru);
+ if (ret)
+ goto err;
+
+err:
+ mutex_unlock(&rtwdev->mutex);
+ return ret;
+}
+
+static ssize_t
+rtw89_debug_priv_mac_reg_dump_select(struct file *filp,
+ const char __user *user_buf,
+ size_t count, loff_t *loff)
+{
+ struct seq_file *m = (struct seq_file *)filp->private_data;
+ struct rtw89_debugfs_priv *debugfs_priv = m->private;
+ struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
+ char buf[32];
+ size_t buf_size;
+ int sel;
+ int ret;
+
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ buf[buf_size] = '\0';
+ ret = kstrtoint(buf, 0, &sel);
+ if (ret)
+ return ret;
+
+ if (sel < RTW89_DBG_SEL_MAC_00 || sel > RTW89_DBG_SEL_RFC) {
+ rtw89_info(rtwdev, "invalid args: %d\n", sel);
+ return -EINVAL;
+ }
+
+ debugfs_priv->cb_data = sel;
+ rtw89_info(rtwdev, "select mac page dump %d\n", debugfs_priv->cb_data);
+
+ return count;
+}
+
+#define RTW89_MAC_PAGE_SIZE 0x100
+
+static int rtw89_debug_priv_mac_reg_dump_get(struct seq_file *m, void *v)
+{
+ struct rtw89_debugfs_priv *debugfs_priv = m->private;
+ struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
+ enum rtw89_debug_mac_reg_sel reg_sel = debugfs_priv->cb_data;
+ u32 start, end;
+ u32 i, j, k, page;
+ u32 val;
+
+ switch (reg_sel) {
+ case RTW89_DBG_SEL_MAC_00:
+ seq_puts(m, "Debug selected MAC page 0x00\n");
+ start = 0x000;
+ end = 0x014;
+ break;
+ case RTW89_DBG_SEL_MAC_40:
+ seq_puts(m, "Debug selected MAC page 0x40\n");
+ start = 0x040;
+ end = 0x07f;
+ break;
+ case RTW89_DBG_SEL_MAC_80:
+ seq_puts(m, "Debug selected MAC page 0x80\n");
+ start = 0x080;
+ end = 0x09f;
+ break;
+ case RTW89_DBG_SEL_MAC_C0:
+ seq_puts(m, "Debug selected MAC page 0xc0\n");
+ start = 0x0c0;
+ end = 0x0df;
+ break;
+ case RTW89_DBG_SEL_MAC_E0:
+ seq_puts(m, "Debug selected MAC page 0xe0\n");
+ start = 0x0e0;
+ end = 0x0ff;
+ break;
+ case RTW89_DBG_SEL_BB:
+ seq_puts(m, "Debug selected BB register\n");
+ start = 0x100;
+ end = 0x17f;
+ break;
+ case RTW89_DBG_SEL_IQK:
+ seq_puts(m, "Debug selected IQK register\n");
+ start = 0x180;
+ end = 0x1bf;
+ break;
+ case RTW89_DBG_SEL_RFC:
+ seq_puts(m, "Debug selected RFC register\n");
+ start = 0x1c0;
+ end = 0x1ff;
+ break;
+ default:
+ seq_puts(m, "Selected invalid register page\n");
+ return -EINVAL;
+ }
+
+ for (i = start; i <= end; i++) {
+ page = i << 8;
+ for (j = page; j < page + RTW89_MAC_PAGE_SIZE; j += 16) {
+ seq_printf(m, "%08xh : ", 0x18600000 + j);
+ for (k = 0; k < 4; k++) {
+ val = rtw89_read32(rtwdev, j + (k << 2));
+ seq_printf(m, "%08x ", val);
+ }
+ seq_puts(m, "\n");
+ }
+ }
+
+ return 0;
+}
+
+static ssize_t
+rtw89_debug_priv_mac_mem_dump_select(struct file *filp,
+ const char __user *user_buf,
+ size_t count, loff_t *loff)
+{
+ struct seq_file *m = (struct seq_file *)filp->private_data;
+ struct rtw89_debugfs_priv *debugfs_priv = m->private;
+ struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
+ char buf[32];
+ size_t buf_size;
+ u32 sel, start_addr, len;
+ int num;
+
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ buf[buf_size] = '\0';
+ num = sscanf(buf, "%x %x %x", &sel, &start_addr, &len);
+ if (num != 3) {
+ rtw89_info(rtwdev, "invalid format: <sel> <start> <len>\n");
+ return -EINVAL;
+ }
+
+ debugfs_priv->mac_mem.sel = sel;
+ debugfs_priv->mac_mem.start = start_addr;
+ debugfs_priv->mac_mem.len = len;
+
+ rtw89_info(rtwdev, "select mem %d start %d len %d\n",
+ sel, start_addr, len);
+
+ return count;
+}
+
+static const u32 mac_mem_base_addr_table[RTW89_MAC_MEM_MAX] = {
+ [RTW89_MAC_MEM_SHARED_BUF] = SHARED_BUF_BASE_ADDR,
+ [RTW89_MAC_MEM_DMAC_TBL] = DMAC_TBL_BASE_ADDR,
+ [RTW89_MAC_MEM_SHCUT_MACHDR] = SHCUT_MACHDR_BASE_ADDR,
+ [RTW89_MAC_MEM_STA_SCHED] = STA_SCHED_BASE_ADDR,
+ [RTW89_MAC_MEM_RXPLD_FLTR_CAM] = RXPLD_FLTR_CAM_BASE_ADDR,
+ [RTW89_MAC_MEM_SECURITY_CAM] = SECURITY_CAM_BASE_ADDR,
+ [RTW89_MAC_MEM_WOW_CAM] = WOW_CAM_BASE_ADDR,
+ [RTW89_MAC_MEM_CMAC_TBL] = CMAC_TBL_BASE_ADDR,
+ [RTW89_MAC_MEM_ADDR_CAM] = ADDR_CAM_BASE_ADDR,
+ [RTW89_MAC_MEM_BA_CAM] = BA_CAM_BASE_ADDR,
+ [RTW89_MAC_MEM_BCN_IE_CAM0] = BCN_IE_CAM0_BASE_ADDR,
+ [RTW89_MAC_MEM_BCN_IE_CAM1] = BCN_IE_CAM1_BASE_ADDR,
+};
+
+static void rtw89_debug_dump_mac_mem(struct seq_file *m,
+ struct rtw89_dev *rtwdev,
+ u8 sel, u32 start_addr, u32 len)
+{
+ u32 base_addr, start_page, residue;
+ u32 i, j, p, pages;
+ u32 dump_len, remain;
+ u32 val;
+
+ remain = len;
+ pages = len / MAC_MEM_DUMP_PAGE_SIZE + 1;
+ start_page = start_addr / MAC_MEM_DUMP_PAGE_SIZE;
+ residue = start_addr % MAC_MEM_DUMP_PAGE_SIZE;
+ base_addr = mac_mem_base_addr_table[sel];
+ base_addr += start_page * MAC_MEM_DUMP_PAGE_SIZE;
+
+ for (p = 0; p < pages; p++) {
+ dump_len = min_t(u32, remain, MAC_MEM_DUMP_PAGE_SIZE);
+ rtw89_write32(rtwdev, R_AX_FILTER_MODEL_ADDR, base_addr);
+ for (i = R_AX_INDIR_ACCESS_ENTRY + residue;
+ i < R_AX_INDIR_ACCESS_ENTRY + dump_len;) {
+ seq_printf(m, "%08xh:", i);
+ for (j = 0;
+ j < 4 && i < R_AX_INDIR_ACCESS_ENTRY + dump_len;
+ j++, i += 4) {
+ val = rtw89_read32(rtwdev, i);
+ seq_printf(m, " %08x", val);
+ remain -= 4;
+ }
+ seq_puts(m, "\n");
+ }
+ base_addr += MAC_MEM_DUMP_PAGE_SIZE;
+ }
+}
+
+static int
+rtw89_debug_priv_mac_mem_dump_get(struct seq_file *m, void *v)
+{
+ struct rtw89_debugfs_priv *debugfs_priv = m->private;
+ struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
+
+ mutex_lock(&rtwdev->mutex);
+ rtw89_leave_ps_mode(rtwdev);
+ rtw89_debug_dump_mac_mem(m, rtwdev,
+ debugfs_priv->mac_mem.sel,
+ debugfs_priv->mac_mem.start,
+ debugfs_priv->mac_mem.len);
+ mutex_unlock(&rtwdev->mutex);
+
+ return 0;
+}
+
+static ssize_t
+rtw89_debug_priv_mac_dbg_port_dump_select(struct file *filp,
+ const char __user *user_buf,
+ size_t count, loff_t *loff)
+{
+ struct seq_file *m = (struct seq_file *)filp->private_data;
+ struct rtw89_debugfs_priv *debugfs_priv = m->private;
+ struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
+ char buf[32];
+ size_t buf_size;
+ int sel, set;
+ int num;
+ bool enable;
+
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ buf[buf_size] = '\0';
+ num = sscanf(buf, "%d %d", &sel, &set);
+ if (num != 2) {
+ rtw89_info(rtwdev, "invalid format: <sel> <set>\n");
+ return -EINVAL;
+ }
+
+ enable = set == 0 ? false : true;
+ switch (sel) {
+ case 0:
+ debugfs_priv->dbgpkg_en.ss_dbg = enable;
+ break;
+ case 1:
+ debugfs_priv->dbgpkg_en.dle_dbg = enable;
+ break;
+ case 2:
+ debugfs_priv->dbgpkg_en.dmac_dbg = enable;
+ break;
+ case 3:
+ debugfs_priv->dbgpkg_en.cmac_dbg = enable;
+ break;
+ case 4:
+ debugfs_priv->dbgpkg_en.dbg_port = enable;
+ break;
+ default:
+ rtw89_info(rtwdev, "invalid args: sel %d set %d\n", sel, set);
+ return -EINVAL;
+ }
+
+ rtw89_info(rtwdev, "%s debug port dump %d\n",
+ enable ? "Enable" : "Disable", sel);
+
+ return count;
+}
+
+static int rtw89_debug_mac_dump_ss_dbg(struct rtw89_dev *rtwdev,
+ struct seq_file *m)
+{
+ return 0;
+}
+
+static int rtw89_debug_mac_dump_dle_dbg(struct rtw89_dev *rtwdev,
+ struct seq_file *m)
+{
+#define DLE_DFI_DUMP(__type, __target, __sel) \
+({ \
+ u32 __ctrl; \
+ u32 __reg_ctrl = R_AX_##__type##_DBG_FUN_INTF_CTL; \
+ u32 __reg_data = R_AX_##__type##_DBG_FUN_INTF_DATA; \
+ u32 __data, __val32; \
+ int __ret; \
+ \
+ __ctrl = FIELD_PREP(B_AX_##__type##_DFI_TRGSEL, \
+ DLE_DFI_TYPE_##__target) | \
+ FIELD_PREP(B_AX_##__type##_DFI_ADDR, __sel) | \
+ B_AX_WDE_DFI_ACTIVE; \
+ rtw89_write32(rtwdev, __reg_ctrl, __ctrl); \
+ __ret = read_poll_timeout(rtw89_read32, __val32, \
+ !(__val32 & B_AX_##__type##_DFI_ACTIVE), \
+ 1000, 50000, false, \
+ rtwdev, __reg_ctrl); \
+ if (__ret) { \
+ rtw89_err(rtwdev, "failed to dump DLE %s %s %d\n", \
+ #__type, #__target, __sel); \
+ return __ret; \
+ } \
+ \
+ __data = rtw89_read32(rtwdev, __reg_data); \
+ __data; \
+})
+
+#define DLE_DFI_FREE_PAGE_DUMP(__m, __type) \
+({ \
+ u32 __freepg, __pubpg; \
+ u32 __freepg_head, __freepg_tail, __pubpg_num; \
+ \
+ __freepg = DLE_DFI_DUMP(__type, FREEPG, 0); \
+ __pubpg = DLE_DFI_DUMP(__type, FREEPG, 1); \
+ __freepg_head = FIELD_GET(B_AX_DLE_FREE_HEADPG, __freepg); \
+ __freepg_tail = FIELD_GET(B_AX_DLE_FREE_TAILPG, __freepg); \
+ __pubpg_num = FIELD_GET(B_AX_DLE_PUB_PGNUM, __pubpg); \
+ seq_printf(__m, "[%s] freepg head: %d\n", \
+ #__type, __freepg_head); \
+ seq_printf(__m, "[%s] freepg tail: %d\n", \
+ #__type, __freepg_tail); \
+ seq_printf(__m, "[%s] pubpg num : %d\n", \
+ #__type, __pubpg_num); \
+})
+
+#define case_QUOTA(__m, __type, __id) \
+ case __type##_QTAID_##__id: \
+ val32 = DLE_DFI_DUMP(__type, QUOTA, __type##_QTAID_##__id); \
+ rsv_pgnum = FIELD_GET(B_AX_DLE_RSV_PGNUM, val32); \
+ use_pgnum = FIELD_GET(B_AX_DLE_USE_PGNUM, val32); \
+ seq_printf(__m, "[%s][%s] rsv_pgnum: %d\n", \
+ #__type, #__id, rsv_pgnum); \
+ seq_printf(__m, "[%s][%s] use_pgnum: %d\n", \
+ #__type, #__id, use_pgnum); \
+ break
+ u32 quota_id;
+ u32 val32;
+ u16 rsv_pgnum, use_pgnum;
+ int ret;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, 0, RTW89_DMAC_SEL);
+ if (ret) {
+ seq_puts(m, "[DLE] : DMAC not enabled\n");
+ return ret;
+ }
+
+ DLE_DFI_FREE_PAGE_DUMP(m, WDE);
+ DLE_DFI_FREE_PAGE_DUMP(m, PLE);
+ for (quota_id = 0; quota_id <= WDE_QTAID_CPUIO; quota_id++) {
+ switch (quota_id) {
+ case_QUOTA(m, WDE, HOST_IF);
+ case_QUOTA(m, WDE, WLAN_CPU);
+ case_QUOTA(m, WDE, DATA_CPU);
+ case_QUOTA(m, WDE, PKTIN);
+ case_QUOTA(m, WDE, CPUIO);
+ }
+ }
+ for (quota_id = 0; quota_id <= PLE_QTAID_CPUIO; quota_id++) {
+ switch (quota_id) {
+ case_QUOTA(m, PLE, B0_TXPL);
+ case_QUOTA(m, PLE, B1_TXPL);
+ case_QUOTA(m, PLE, C2H);
+ case_QUOTA(m, PLE, H2C);
+ case_QUOTA(m, PLE, WLAN_CPU);
+ case_QUOTA(m, PLE, MPDU);
+ case_QUOTA(m, PLE, CMAC0_RX);
+ case_QUOTA(m, PLE, CMAC1_RX);
+ case_QUOTA(m, PLE, CMAC1_BBRPT);
+ case_QUOTA(m, PLE, WDRLS);
+ case_QUOTA(m, PLE, CPUIO);
+ }
+ }
+
+ return 0;
+
+#undef case_QUOTA
+#undef DLE_DFI_DUMP
+#undef DLE_DFI_FREE_PAGE_DUMP
+}
+
+static int rtw89_debug_mac_dump_dmac_dbg(struct rtw89_dev *rtwdev,
+ struct seq_file *m)
+{
+ int ret;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, 0, RTW89_DMAC_SEL);
+ if (ret) {
+ seq_puts(m, "[DMAC] : DMAC not enabled\n");
+ return ret;
+ }
+
+ seq_printf(m, "R_AX_DMAC_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_DMAC_ERR_ISR));
+ seq_printf(m, "[0]R_AX_WDRLS_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_WDRLS_ERR_ISR));
+ seq_printf(m, "[1]R_AX_SEC_ERR_IMR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_ERR_IMR_ISR));
+ seq_printf(m, "[2.1]R_AX_MPDU_TX_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_MPDU_TX_ERR_ISR));
+ seq_printf(m, "[2.2]R_AX_MPDU_RX_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_MPDU_RX_ERR_ISR));
+ seq_printf(m, "[3]R_AX_STA_SCHEDULER_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_STA_SCHEDULER_ERR_ISR));
+ seq_printf(m, "[4]R_AX_WDE_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_WDE_ERR_ISR));
+ seq_printf(m, "[5.1]R_AX_TXPKTCTL_ERR_IMR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR));
+ seq_printf(m, "[5.2]R_AX_TXPKTCTL_ERR_IMR_ISR_B1=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR_B1));
+ seq_printf(m, "[6]R_AX_PLE_ERR_FLAG_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_ISR));
+ seq_printf(m, "[7]R_AX_PKTIN_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PKTIN_ERR_ISR));
+ seq_printf(m, "[8.1]R_AX_OTHER_DISPATCHER_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_OTHER_DISPATCHER_ERR_ISR));
+ seq_printf(m, "[8.2]R_AX_HOST_DISPATCHER_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_HOST_DISPATCHER_ERR_ISR));
+ seq_printf(m, "[8.3]R_AX_CPU_DISPATCHER_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_CPU_DISPATCHER_ERR_ISR));
+ seq_printf(m, "[10]R_AX_CPUIO_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_CPUIO_ERR_ISR));
+ seq_printf(m, "[11.1]R_AX_BBRPT_COM_ERR_IMR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_IMR_ISR));
+ seq_printf(m, "[11.2]R_AX_BBRPT_CHINFO_ERR_IMR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_IMR_ISR));
+ seq_printf(m, "[11.3]R_AX_BBRPT_DFS_ERR_IMR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_IMR_ISR));
+ seq_printf(m, "[11.4]R_AX_LA_ERRFLAG=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_LA_ERRFLAG));
+
+ return 0;
+}
+
+static int rtw89_debug_mac_dump_cmac_dbg(struct rtw89_dev *rtwdev,
+ struct seq_file *m)
+{
+ int ret;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, 0, RTW89_CMAC_SEL);
+ if (ret) {
+ seq_puts(m, "[CMAC] : CMAC 0 not enabled\n");
+ return ret;
+ }
+
+ seq_printf(m, "R_AX_CMAC_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_CMAC_ERR_ISR));
+ seq_printf(m, "[0]R_AX_SCHEDULE_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SCHEDULE_ERR_ISR));
+ seq_printf(m, "[1]R_AX_PTCL_ISR0=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PTCL_ISR0));
+ seq_printf(m, "[3]R_AX_DLE_CTRL=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_DLE_CTRL));
+ seq_printf(m, "[4]R_AX_PHYINFO_ERR_ISR=0x%02x\n",
+ rtw89_read8(rtwdev, R_AX_PHYINFO_ERR_ISR));
+ seq_printf(m, "[5]R_AX_TXPWR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_TXPWR_ISR));
+ seq_printf(m, "[6]R_AX_RMAC_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_RMAC_ERR_ISR));
+ seq_printf(m, "[7]R_AX_TMAC_ERR_IMR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_TMAC_ERR_IMR_ISR));
+
+ ret = rtw89_mac_check_mac_en(rtwdev, 1, RTW89_CMAC_SEL);
+ if (ret) {
+ seq_puts(m, "[CMAC] : CMAC 1 not enabled\n");
+ return ret;
+ }
+
+ seq_printf(m, "R_AX_CMAC_ERR_ISR_C1=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_CMAC_ERR_ISR_C1));
+ seq_printf(m, "[0]R_AX_SCHEDULE_ERR_ISR_C1=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SCHEDULE_ERR_ISR_C1));
+ seq_printf(m, "[1]R_AX_PTCL_ISR0_C1=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PTCL_ISR0_C1));
+ seq_printf(m, "[3]R_AX_DLE_CTRL_C1=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_DLE_CTRL_C1));
+ seq_printf(m, "[4]R_AX_PHYINFO_ERR_ISR_C1=0x%02x\n",
+ rtw89_read8(rtwdev, R_AX_PHYINFO_ERR_ISR_C1));
+ seq_printf(m, "[5]R_AX_TXPWR_ISR_C1=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_TXPWR_ISR_C1));
+ seq_printf(m, "[6]R_AX_RMAC_ERR_ISR_C1=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_RMAC_ERR_ISR_C1));
+ seq_printf(m, "[7]R_AX_TMAC_ERR_IMR_ISR_C1=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_TMAC_ERR_IMR_ISR_C1));
+
+ return 0;
+}
+
+static const struct rtw89_mac_dbg_port_info dbg_port_ptcl_c0 = {
+ .sel_addr = R_AX_PTCL_DBG,
+ .sel_byte = 1,
+ .sel_msk = B_AX_PTCL_DBG_SEL,
+ .srt = 0x00,
+ .end = 0x3F,
+ .rd_addr = R_AX_PTCL_DBG_INFO,
+ .rd_byte = 4,
+ .rd_msk = B_AX_PTCL_DBG_INFO_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_ptcl_c1 = {
+ .sel_addr = R_AX_PTCL_DBG_C1,
+ .sel_byte = 1,
+ .sel_msk = B_AX_PTCL_DBG_SEL,
+ .srt = 0x00,
+ .end = 0x3F,
+ .rd_addr = R_AX_PTCL_DBG_INFO_C1,
+ .rd_byte = 4,
+ .rd_msk = B_AX_PTCL_DBG_INFO_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_sch_c0 = {
+ .sel_addr = R_AX_SCH_DBG_SEL,
+ .sel_byte = 1,
+ .sel_msk = B_AX_SCH_DBG_SEL_MSK,
+ .srt = 0x00,
+ .end = 0x2F,
+ .rd_addr = R_AX_SCH_DBG,
+ .rd_byte = 4,
+ .rd_msk = B_AX_SCHEDULER_DBG_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_sch_c1 = {
+ .sel_addr = R_AX_SCH_DBG_SEL_C1,
+ .sel_byte = 1,
+ .sel_msk = B_AX_SCH_DBG_SEL_MSK,
+ .srt = 0x00,
+ .end = 0x2F,
+ .rd_addr = R_AX_SCH_DBG_C1,
+ .rd_byte = 4,
+ .rd_msk = B_AX_SCHEDULER_DBG_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_tmac_c0 = {
+ .sel_addr = R_AX_MACTX_DBG_SEL_CNT,
+ .sel_byte = 1,
+ .sel_msk = B_AX_DBGSEL_MACTX,
+ .srt = 0x00,
+ .end = 0x19,
+ .rd_addr = R_AX_DBG_PORT_SEL,
+ .rd_byte = 4,
+ .rd_msk = B_AX_DEBUG_ST_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_tmac_c1 = {
+ .sel_addr = R_AX_MACTX_DBG_SEL_CNT_C1,
+ .sel_byte = 1,
+ .sel_msk = B_AX_DBGSEL_MACTX,
+ .srt = 0x00,
+ .end = 0x19,
+ .rd_addr = R_AX_DBG_PORT_SEL,
+ .rd_byte = 4,
+ .rd_msk = B_AX_DEBUG_ST_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_rmac_c0 = {
+ .sel_addr = R_AX_RX_DEBUG_SELECT,
+ .sel_byte = 1,
+ .sel_msk = B_AX_DEBUG_SEL,
+ .srt = 0x00,
+ .end = 0x58,
+ .rd_addr = R_AX_DBG_PORT_SEL,
+ .rd_byte = 4,
+ .rd_msk = B_AX_DEBUG_ST_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_rmac_c1 = {
+ .sel_addr = R_AX_RX_DEBUG_SELECT_C1,
+ .sel_byte = 1,
+ .sel_msk = B_AX_DEBUG_SEL,
+ .srt = 0x00,
+ .end = 0x58,
+ .rd_addr = R_AX_DBG_PORT_SEL,
+ .rd_byte = 4,
+ .rd_msk = B_AX_DEBUG_ST_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_rmacst_c0 = {
+ .sel_addr = R_AX_RX_STATE_MONITOR,
+ .sel_byte = 1,
+ .sel_msk = B_AX_STATE_SEL,
+ .srt = 0x00,
+ .end = 0x17,
+ .rd_addr = R_AX_RX_STATE_MONITOR,
+ .rd_byte = 4,
+ .rd_msk = R_AX_RX_STATE_MONITOR_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_rmacst_c1 = {
+ .sel_addr = R_AX_RX_STATE_MONITOR_C1,
+ .sel_byte = 1,
+ .sel_msk = B_AX_STATE_SEL,
+ .srt = 0x00,
+ .end = 0x17,
+ .rd_addr = R_AX_RX_STATE_MONITOR_C1,
+ .rd_byte = 4,
+ .rd_msk = R_AX_RX_STATE_MONITOR_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_rmac_plcp_c0 = {
+ .sel_addr = R_AX_RMAC_PLCP_MON,
+ .sel_byte = 4,
+ .sel_msk = B_AX_PCLP_MON_SEL,
+ .srt = 0x0,
+ .end = 0xF,
+ .rd_addr = R_AX_RMAC_PLCP_MON,
+ .rd_byte = 4,
+ .rd_msk = R_AX_RMAC_PLCP_MON_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_rmac_plcp_c1 = {
+ .sel_addr = R_AX_RMAC_PLCP_MON_C1,
+ .sel_byte = 4,
+ .sel_msk = B_AX_PCLP_MON_SEL,
+ .srt = 0x0,
+ .end = 0xF,
+ .rd_addr = R_AX_RMAC_PLCP_MON_C1,
+ .rd_byte = 4,
+ .rd_msk = R_AX_RMAC_PLCP_MON_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_trxptcl_c0 = {
+ .sel_addr = R_AX_DBGSEL_TRXPTCL,
+ .sel_byte = 1,
+ .sel_msk = B_AX_DBGSEL_TRXPTCL_MSK,
+ .srt = 0x08,
+ .end = 0x10,
+ .rd_addr = R_AX_DBG_PORT_SEL,
+ .rd_byte = 4,
+ .rd_msk = B_AX_DEBUG_ST_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_trxptcl_c1 = {
+ .sel_addr = R_AX_DBGSEL_TRXPTCL_C1,
+ .sel_byte = 1,
+ .sel_msk = B_AX_DBGSEL_TRXPTCL_MSK,
+ .srt = 0x08,
+ .end = 0x10,
+ .rd_addr = R_AX_DBG_PORT_SEL,
+ .rd_byte = 4,
+ .rd_msk = B_AX_DEBUG_ST_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_tx_infol_c0 = {
+ .sel_addr = R_AX_WMAC_TX_CTRL_DEBUG,
+ .sel_byte = 1,
+ .sel_msk = B_AX_TX_CTRL_DEBUG_SEL,
+ .srt = 0x00,
+ .end = 0x07,
+ .rd_addr = R_AX_WMAC_TX_INFO0_DEBUG,
+ .rd_byte = 4,
+ .rd_msk = B_AX_TX_CTRL_INFO_P0_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_tx_infoh_c0 = {
+ .sel_addr = R_AX_WMAC_TX_CTRL_DEBUG,
+ .sel_byte = 1,
+ .sel_msk = B_AX_TX_CTRL_DEBUG_SEL,
+ .srt = 0x00,
+ .end = 0x07,
+ .rd_addr = R_AX_WMAC_TX_INFO1_DEBUG,
+ .rd_byte = 4,
+ .rd_msk = B_AX_TX_CTRL_INFO_P1_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_tx_infol_c1 = {
+ .sel_addr = R_AX_WMAC_TX_CTRL_DEBUG_C1,
+ .sel_byte = 1,
+ .sel_msk = B_AX_TX_CTRL_DEBUG_SEL,
+ .srt = 0x00,
+ .end = 0x07,
+ .rd_addr = R_AX_WMAC_TX_INFO0_DEBUG_C1,
+ .rd_byte = 4,
+ .rd_msk = B_AX_TX_CTRL_INFO_P0_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_tx_infoh_c1 = {
+ .sel_addr = R_AX_WMAC_TX_CTRL_DEBUG_C1,
+ .sel_byte = 1,
+ .sel_msk = B_AX_TX_CTRL_DEBUG_SEL,
+ .srt = 0x00,
+ .end = 0x07,
+ .rd_addr = R_AX_WMAC_TX_INFO1_DEBUG_C1,
+ .rd_byte = 4,
+ .rd_msk = B_AX_TX_CTRL_INFO_P1_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_txtf_infol_c0 = {
+ .sel_addr = R_AX_WMAC_TX_TF_INFO_0,
+ .sel_byte = 1,
+ .sel_msk = B_AX_WMAC_TX_TF_INFO_SEL,
+ .srt = 0x00,
+ .end = 0x04,
+ .rd_addr = R_AX_WMAC_TX_TF_INFO_1,
+ .rd_byte = 4,
+ .rd_msk = B_AX_WMAC_TX_TF_INFO_P0
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_txtf_infoh_c0 = {
+ .sel_addr = R_AX_WMAC_TX_TF_INFO_0,
+ .sel_byte = 1,
+ .sel_msk = B_AX_WMAC_TX_TF_INFO_SEL,
+ .srt = 0x00,
+ .end = 0x04,
+ .rd_addr = R_AX_WMAC_TX_TF_INFO_2,
+ .rd_byte = 4,
+ .rd_msk = B_AX_WMAC_TX_TF_INFO_P1
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_txtf_infol_c1 = {
+ .sel_addr = R_AX_WMAC_TX_TF_INFO_0_C1,
+ .sel_byte = 1,
+ .sel_msk = B_AX_WMAC_TX_TF_INFO_SEL,
+ .srt = 0x00,
+ .end = 0x04,
+ .rd_addr = R_AX_WMAC_TX_TF_INFO_1_C1,
+ .rd_byte = 4,
+ .rd_msk = B_AX_WMAC_TX_TF_INFO_P0
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_txtf_infoh_c1 = {
+ .sel_addr = R_AX_WMAC_TX_TF_INFO_0_C1,
+ .sel_byte = 1,
+ .sel_msk = B_AX_WMAC_TX_TF_INFO_SEL,
+ .srt = 0x00,
+ .end = 0x04,
+ .rd_addr = R_AX_WMAC_TX_TF_INFO_2_C1,
+ .rd_byte = 4,
+ .rd_msk = B_AX_WMAC_TX_TF_INFO_P1
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_wde_bufmgn_freepg = {
+ .sel_addr = R_AX_WDE_DBG_FUN_INTF_CTL,
+ .sel_byte = 4,
+ .sel_msk = B_AX_WDE_DFI_DATA_MSK,
+ .srt = 0x80000000,
+ .end = 0x80000001,
+ .rd_addr = R_AX_WDE_DBG_FUN_INTF_DATA,
+ .rd_byte = 4,
+ .rd_msk = B_AX_WDE_DFI_DATA_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_wde_bufmgn_quota = {
+ .sel_addr = R_AX_WDE_DBG_FUN_INTF_CTL,
+ .sel_byte = 4,
+ .sel_msk = B_AX_WDE_DFI_DATA_MSK,
+ .srt = 0x80010000,
+ .end = 0x80010004,
+ .rd_addr = R_AX_WDE_DBG_FUN_INTF_DATA,
+ .rd_byte = 4,
+ .rd_msk = B_AX_WDE_DFI_DATA_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_wde_bufmgn_pagellt = {
+ .sel_addr = R_AX_WDE_DBG_FUN_INTF_CTL,
+ .sel_byte = 4,
+ .sel_msk = B_AX_WDE_DFI_DATA_MSK,
+ .srt = 0x80020000,
+ .end = 0x80020FFF,
+ .rd_addr = R_AX_WDE_DBG_FUN_INTF_DATA,
+ .rd_byte = 4,
+ .rd_msk = B_AX_WDE_DFI_DATA_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_wde_bufmgn_pktinfo = {
+ .sel_addr = R_AX_WDE_DBG_FUN_INTF_CTL,
+ .sel_byte = 4,
+ .sel_msk = B_AX_WDE_DFI_DATA_MSK,
+ .srt = 0x80030000,
+ .end = 0x80030FFF,
+ .rd_addr = R_AX_WDE_DBG_FUN_INTF_DATA,
+ .rd_byte = 4,
+ .rd_msk = B_AX_WDE_DFI_DATA_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_wde_quemgn_prepkt = {
+ .sel_addr = R_AX_WDE_DBG_FUN_INTF_CTL,
+ .sel_byte = 4,
+ .sel_msk = B_AX_WDE_DFI_DATA_MSK,
+ .srt = 0x80040000,
+ .end = 0x80040FFF,
+ .rd_addr = R_AX_WDE_DBG_FUN_INTF_DATA,
+ .rd_byte = 4,
+ .rd_msk = B_AX_WDE_DFI_DATA_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_wde_quemgn_nxtpkt = {
+ .sel_addr = R_AX_WDE_DBG_FUN_INTF_CTL,
+ .sel_byte = 4,
+ .sel_msk = B_AX_WDE_DFI_DATA_MSK,
+ .srt = 0x80050000,
+ .end = 0x80050FFF,
+ .rd_addr = R_AX_WDE_DBG_FUN_INTF_DATA,
+ .rd_byte = 4,
+ .rd_msk = B_AX_WDE_DFI_DATA_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_wde_quemgn_qlnktbl = {
+ .sel_addr = R_AX_WDE_DBG_FUN_INTF_CTL,
+ .sel_byte = 4,
+ .sel_msk = B_AX_WDE_DFI_DATA_MSK,
+ .srt = 0x80060000,
+ .end = 0x80060453,
+ .rd_addr = R_AX_WDE_DBG_FUN_INTF_DATA,
+ .rd_byte = 4,
+ .rd_msk = B_AX_WDE_DFI_DATA_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_wde_quemgn_qempty = {
+ .sel_addr = R_AX_WDE_DBG_FUN_INTF_CTL,
+ .sel_byte = 4,
+ .sel_msk = B_AX_WDE_DFI_DATA_MSK,
+ .srt = 0x80070000,
+ .end = 0x80070011,
+ .rd_addr = R_AX_WDE_DBG_FUN_INTF_DATA,
+ .rd_byte = 4,
+ .rd_msk = B_AX_WDE_DFI_DATA_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_ple_bufmgn_freepg = {
+ .sel_addr = R_AX_PLE_DBG_FUN_INTF_CTL,
+ .sel_byte = 4,
+ .sel_msk = B_AX_PLE_DFI_DATA_MSK,
+ .srt = 0x80000000,
+ .end = 0x80000001,
+ .rd_addr = R_AX_PLE_DBG_FUN_INTF_DATA,
+ .rd_byte = 4,
+ .rd_msk = B_AX_PLE_DFI_DATA_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_ple_bufmgn_quota = {
+ .sel_addr = R_AX_PLE_DBG_FUN_INTF_CTL,
+ .sel_byte = 4,
+ .sel_msk = B_AX_PLE_DFI_DATA_MSK,
+ .srt = 0x80010000,
+ .end = 0x8001000A,
+ .rd_addr = R_AX_PLE_DBG_FUN_INTF_DATA,
+ .rd_byte = 4,
+ .rd_msk = B_AX_PLE_DFI_DATA_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_ple_bufmgn_pagellt = {
+ .sel_addr = R_AX_PLE_DBG_FUN_INTF_CTL,
+ .sel_byte = 4,
+ .sel_msk = B_AX_PLE_DFI_DATA_MSK,
+ .srt = 0x80020000,
+ .end = 0x80020DBF,
+ .rd_addr = R_AX_PLE_DBG_FUN_INTF_DATA,
+ .rd_byte = 4,
+ .rd_msk = B_AX_PLE_DFI_DATA_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_ple_bufmgn_pktinfo = {
+ .sel_addr = R_AX_PLE_DBG_FUN_INTF_CTL,
+ .sel_byte = 4,
+ .sel_msk = B_AX_PLE_DFI_DATA_MSK,
+ .srt = 0x80030000,
+ .end = 0x80030DBF,
+ .rd_addr = R_AX_PLE_DBG_FUN_INTF_DATA,
+ .rd_byte = 4,
+ .rd_msk = B_AX_PLE_DFI_DATA_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_ple_quemgn_prepkt = {
+ .sel_addr = R_AX_PLE_DBG_FUN_INTF_CTL,
+ .sel_byte = 4,
+ .sel_msk = B_AX_PLE_DFI_DATA_MSK,
+ .srt = 0x80040000,
+ .end = 0x80040DBF,
+ .rd_addr = R_AX_PLE_DBG_FUN_INTF_DATA,
+ .rd_byte = 4,
+ .rd_msk = B_AX_PLE_DFI_DATA_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_ple_quemgn_nxtpkt = {
+ .sel_addr = R_AX_PLE_DBG_FUN_INTF_CTL,
+ .sel_byte = 4,
+ .sel_msk = B_AX_PLE_DFI_DATA_MSK,
+ .srt = 0x80050000,
+ .end = 0x80050DBF,
+ .rd_addr = R_AX_PLE_DBG_FUN_INTF_DATA,
+ .rd_byte = 4,
+ .rd_msk = B_AX_PLE_DFI_DATA_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_ple_quemgn_qlnktbl = {
+ .sel_addr = R_AX_PLE_DBG_FUN_INTF_CTL,
+ .sel_byte = 4,
+ .sel_msk = B_AX_PLE_DFI_DATA_MSK,
+ .srt = 0x80060000,
+ .end = 0x80060041,
+ .rd_addr = R_AX_PLE_DBG_FUN_INTF_DATA,
+ .rd_byte = 4,
+ .rd_msk = B_AX_PLE_DFI_DATA_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_ple_quemgn_qempty = {
+ .sel_addr = R_AX_PLE_DBG_FUN_INTF_CTL,
+ .sel_byte = 4,
+ .sel_msk = B_AX_PLE_DFI_DATA_MSK,
+ .srt = 0x80070000,
+ .end = 0x80070001,
+ .rd_addr = R_AX_PLE_DBG_FUN_INTF_DATA,
+ .rd_byte = 4,
+ .rd_msk = B_AX_PLE_DFI_DATA_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_pktinfo = {
+ .sel_addr = R_AX_DBG_FUN_INTF_CTL,
+ .sel_byte = 4,
+ .sel_msk = B_AX_DFI_DATA_MSK,
+ .srt = 0x80000000,
+ .end = 0x8000017f,
+ .rd_addr = R_AX_DBG_FUN_INTF_DATA,
+ .rd_byte = 4,
+ .rd_msk = B_AX_DFI_DATA_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_pcie_txdma = {
+ .sel_addr = R_AX_PCIE_DBG_CTRL,
+ .sel_byte = 2,
+ .sel_msk = B_AX_LOOPBACK_DBG_SEL,
+ .srt = 0x00,
+ .end = 0x03,
+ .rd_addr = R_AX_DBG_PORT_SEL,
+ .rd_byte = 4,
+ .rd_msk = B_AX_DEBUG_ST_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_pcie_rxdma = {
+ .sel_addr = R_AX_PCIE_DBG_CTRL,
+ .sel_byte = 2,
+ .sel_msk = B_AX_LOOPBACK_DBG_SEL,
+ .srt = 0x00,
+ .end = 0x04,
+ .rd_addr = R_AX_DBG_PORT_SEL,
+ .rd_byte = 4,
+ .rd_msk = B_AX_DEBUG_ST_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_pcie_cvt = {
+ .sel_addr = R_AX_PCIE_DBG_CTRL,
+ .sel_byte = 2,
+ .sel_msk = B_AX_LOOPBACK_DBG_SEL,
+ .srt = 0x00,
+ .end = 0x01,
+ .rd_addr = R_AX_DBG_PORT_SEL,
+ .rd_byte = 4,
+ .rd_msk = B_AX_DEBUG_ST_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_pcie_cxpl = {
+ .sel_addr = R_AX_PCIE_DBG_CTRL,
+ .sel_byte = 2,
+ .sel_msk = B_AX_LOOPBACK_DBG_SEL,
+ .srt = 0x00,
+ .end = 0x05,
+ .rd_addr = R_AX_DBG_PORT_SEL,
+ .rd_byte = 4,
+ .rd_msk = B_AX_DEBUG_ST_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_pcie_io = {
+ .sel_addr = R_AX_PCIE_DBG_CTRL,
+ .sel_byte = 2,
+ .sel_msk = B_AX_LOOPBACK_DBG_SEL,
+ .srt = 0x00,
+ .end = 0x05,
+ .rd_addr = R_AX_DBG_PORT_SEL,
+ .rd_byte = 4,
+ .rd_msk = B_AX_DEBUG_ST_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_pcie_misc = {
+ .sel_addr = R_AX_PCIE_DBG_CTRL,
+ .sel_byte = 2,
+ .sel_msk = B_AX_LOOPBACK_DBG_SEL,
+ .srt = 0x00,
+ .end = 0x06,
+ .rd_addr = R_AX_DBG_PORT_SEL,
+ .rd_byte = 4,
+ .rd_msk = B_AX_DEBUG_ST_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info dbg_port_pcie_misc2 = {
+ .sel_addr = R_AX_DBG_CTRL,
+ .sel_byte = 1,
+ .sel_msk = B_AX_DBG_SEL0,
+ .srt = 0x34,
+ .end = 0x3C,
+ .rd_addr = R_AX_DBG_PORT_SEL,
+ .rd_byte = 4,
+ .rd_msk = B_AX_DEBUG_ST_MSK
+};
+
+static const struct rtw89_mac_dbg_port_info *
+rtw89_debug_mac_dbg_port_sel(struct seq_file *m,
+ struct rtw89_dev *rtwdev, u32 sel)
+{
+ const struct rtw89_mac_dbg_port_info *info;
+ u32 val32;
+ u16 val16;
+ u8 val8;
+
+ switch (sel) {
+ case RTW89_DBG_PORT_SEL_PTCL_C0:
+ info = &dbg_port_ptcl_c0;
+ val16 = rtw89_read16(rtwdev, R_AX_PTCL_DBG);
+ val16 |= B_AX_PTCL_DBG_EN;
+ rtw89_write16(rtwdev, R_AX_PTCL_DBG, val16);
+ seq_puts(m, "Enable PTCL C0 dbgport.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_PTCL_C1:
+ info = &dbg_port_ptcl_c1;
+ val16 = rtw89_read16(rtwdev, R_AX_PTCL_DBG_C1);
+ val16 |= B_AX_PTCL_DBG_EN;
+ rtw89_write16(rtwdev, R_AX_PTCL_DBG_C1, val16);
+ seq_puts(m, "Enable PTCL C1 dbgport.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_SCH_C0:
+ info = &dbg_port_sch_c0;
+ val32 = rtw89_read32(rtwdev, R_AX_SCH_DBG_SEL);
+ val32 |= B_AX_SCH_DBG_EN;
+ rtw89_write32(rtwdev, R_AX_SCH_DBG_SEL, val32);
+ seq_puts(m, "Enable SCH C0 dbgport.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_SCH_C1:
+ info = &dbg_port_sch_c1;
+ val32 = rtw89_read32(rtwdev, R_AX_SCH_DBG_SEL_C1);
+ val32 |= B_AX_SCH_DBG_EN;
+ rtw89_write32(rtwdev, R_AX_SCH_DBG_SEL_C1, val32);
+ seq_puts(m, "Enable SCH C1 dbgport.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_TMAC_C0:
+ info = &dbg_port_tmac_c0;
+ val32 = rtw89_read32(rtwdev, R_AX_DBGSEL_TRXPTCL);
+ val32 = u32_replace_bits(val32, TRXPTRL_DBG_SEL_TMAC,
+ B_AX_DBGSEL_TRXPTCL_MSK);
+ rtw89_write32(rtwdev, R_AX_DBGSEL_TRXPTCL, val32);
+
+ val32 = rtw89_read32(rtwdev, R_AX_DBG_CTRL);
+ val32 = u32_replace_bits(val32, TMAC_DBG_SEL_C0, B_AX_DBG_SEL0);
+ val32 = u32_replace_bits(val32, TMAC_DBG_SEL_C0, B_AX_DBG_SEL1);
+ rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32);
+
+ val32 = rtw89_read32(rtwdev, R_AX_SYS_STATUS1);
+ val32 = u32_replace_bits(val32, MAC_DBG_SEL, B_AX_SEL_0XC0);
+ rtw89_write32(rtwdev, R_AX_SYS_STATUS1, val32);
+ seq_puts(m, "Enable TMAC C0 dbgport.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_TMAC_C1:
+ info = &dbg_port_tmac_c1;
+ val32 = rtw89_read32(rtwdev, R_AX_DBGSEL_TRXPTCL_C1);
+ val32 = u32_replace_bits(val32, TRXPTRL_DBG_SEL_TMAC,
+ B_AX_DBGSEL_TRXPTCL_MSK);
+ rtw89_write32(rtwdev, R_AX_DBGSEL_TRXPTCL_C1, val32);
+
+ val32 = rtw89_read32(rtwdev, R_AX_DBG_CTRL);
+ val32 = u32_replace_bits(val32, TMAC_DBG_SEL_C1, B_AX_DBG_SEL0);
+ val32 = u32_replace_bits(val32, TMAC_DBG_SEL_C1, B_AX_DBG_SEL1);
+ rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32);
+
+ val32 = rtw89_read32(rtwdev, R_AX_SYS_STATUS1);
+ val32 = u32_replace_bits(val32, MAC_DBG_SEL, B_AX_SEL_0XC0);
+ rtw89_write32(rtwdev, R_AX_SYS_STATUS1, val32);
+ seq_puts(m, "Enable TMAC C1 dbgport.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_RMAC_C0:
+ info = &dbg_port_rmac_c0;
+ val32 = rtw89_read32(rtwdev, R_AX_DBGSEL_TRXPTCL);
+ val32 = u32_replace_bits(val32, TRXPTRL_DBG_SEL_RMAC,
+ B_AX_DBGSEL_TRXPTCL_MSK);
+ rtw89_write32(rtwdev, R_AX_DBGSEL_TRXPTCL, val32);
+
+ val32 = rtw89_read32(rtwdev, R_AX_DBG_CTRL);
+ val32 = u32_replace_bits(val32, RMAC_DBG_SEL_C0, B_AX_DBG_SEL0);
+ val32 = u32_replace_bits(val32, RMAC_DBG_SEL_C0, B_AX_DBG_SEL1);
+ rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32);
+
+ val32 = rtw89_read32(rtwdev, R_AX_SYS_STATUS1);
+ val32 = u32_replace_bits(val32, MAC_DBG_SEL, B_AX_SEL_0XC0);
+ rtw89_write32(rtwdev, R_AX_SYS_STATUS1, val32);
+
+ val8 = rtw89_read8(rtwdev, R_AX_DBGSEL_TRXPTCL);
+ val8 = u8_replace_bits(val8, RMAC_CMAC_DBG_SEL,
+ B_AX_DBGSEL_TRXPTCL_MSK);
+ rtw89_write8(rtwdev, R_AX_DBGSEL_TRXPTCL, val8);
+ seq_puts(m, "Enable RMAC C0 dbgport.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_RMAC_C1:
+ info = &dbg_port_rmac_c1;
+ val32 = rtw89_read32(rtwdev, R_AX_DBGSEL_TRXPTCL_C1);
+ val32 = u32_replace_bits(val32, TRXPTRL_DBG_SEL_RMAC,
+ B_AX_DBGSEL_TRXPTCL_MSK);
+ rtw89_write32(rtwdev, R_AX_DBGSEL_TRXPTCL_C1, val32);
+
+ val32 = rtw89_read32(rtwdev, R_AX_DBG_CTRL);
+ val32 = u32_replace_bits(val32, RMAC_DBG_SEL_C1, B_AX_DBG_SEL0);
+ val32 = u32_replace_bits(val32, RMAC_DBG_SEL_C1, B_AX_DBG_SEL1);
+ rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32);
+
+ val32 = rtw89_read32(rtwdev, R_AX_SYS_STATUS1);
+ val32 = u32_replace_bits(val32, MAC_DBG_SEL, B_AX_SEL_0XC0);
+ rtw89_write32(rtwdev, R_AX_SYS_STATUS1, val32);
+
+ val8 = rtw89_read8(rtwdev, R_AX_DBGSEL_TRXPTCL_C1);
+ val8 = u8_replace_bits(val8, RMAC_CMAC_DBG_SEL,
+ B_AX_DBGSEL_TRXPTCL_MSK);
+ rtw89_write8(rtwdev, R_AX_DBGSEL_TRXPTCL_C1, val8);
+ seq_puts(m, "Enable RMAC C1 dbgport.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_RMACST_C0:
+ info = &dbg_port_rmacst_c0;
+ seq_puts(m, "Enable RMAC state C0 dbgport.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_RMACST_C1:
+ info = &dbg_port_rmacst_c1;
+ seq_puts(m, "Enable RMAC state C1 dbgport.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_RMAC_PLCP_C0:
+ info = &dbg_port_rmac_plcp_c0;
+ seq_puts(m, "Enable RMAC PLCP C0 dbgport.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_RMAC_PLCP_C1:
+ info = &dbg_port_rmac_plcp_c1;
+ seq_puts(m, "Enable RMAC PLCP C1 dbgport.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_TRXPTCL_C0:
+ info = &dbg_port_trxptcl_c0;
+ val32 = rtw89_read32(rtwdev, R_AX_DBG_CTRL);
+ val32 = u32_replace_bits(val32, TRXPTCL_DBG_SEL_C0, B_AX_DBG_SEL0);
+ val32 = u32_replace_bits(val32, TRXPTCL_DBG_SEL_C0, B_AX_DBG_SEL1);
+ rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32);
+
+ val32 = rtw89_read32(rtwdev, R_AX_SYS_STATUS1);
+ val32 = u32_replace_bits(val32, MAC_DBG_SEL, B_AX_SEL_0XC0);
+ rtw89_write32(rtwdev, R_AX_SYS_STATUS1, val32);
+ seq_puts(m, "Enable TRXPTCL C0 dbgport.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_TRXPTCL_C1:
+ info = &dbg_port_trxptcl_c1;
+ val32 = rtw89_read32(rtwdev, R_AX_DBG_CTRL);
+ val32 = u32_replace_bits(val32, TRXPTCL_DBG_SEL_C1, B_AX_DBG_SEL0);
+ val32 = u32_replace_bits(val32, TRXPTCL_DBG_SEL_C1, B_AX_DBG_SEL1);
+ rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32);
+
+ val32 = rtw89_read32(rtwdev, R_AX_SYS_STATUS1);
+ val32 = u32_replace_bits(val32, MAC_DBG_SEL, B_AX_SEL_0XC0);
+ rtw89_write32(rtwdev, R_AX_SYS_STATUS1, val32);
+ seq_puts(m, "Enable TRXPTCL C1 dbgport.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_TX_INFOL_C0:
+ info = &dbg_port_tx_infol_c0;
+ val32 = rtw89_read32(rtwdev, R_AX_TCR1);
+ val32 |= B_AX_TCR_FORCE_READ_TXDFIFO;
+ rtw89_write32(rtwdev, R_AX_TCR1, val32);
+ seq_puts(m, "Enable tx infol dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_TX_INFOH_C0:
+ info = &dbg_port_tx_infoh_c0;
+ val32 = rtw89_read32(rtwdev, R_AX_TCR1);
+ val32 |= B_AX_TCR_FORCE_READ_TXDFIFO;
+ rtw89_write32(rtwdev, R_AX_TCR1, val32);
+ seq_puts(m, "Enable tx infoh dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_TX_INFOL_C1:
+ info = &dbg_port_tx_infol_c1;
+ val32 = rtw89_read32(rtwdev, R_AX_TCR1_C1);
+ val32 |= B_AX_TCR_FORCE_READ_TXDFIFO;
+ rtw89_write32(rtwdev, R_AX_TCR1_C1, val32);
+ seq_puts(m, "Enable tx infol dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_TX_INFOH_C1:
+ info = &dbg_port_tx_infoh_c1;
+ val32 = rtw89_read32(rtwdev, R_AX_TCR1_C1);
+ val32 |= B_AX_TCR_FORCE_READ_TXDFIFO;
+ rtw89_write32(rtwdev, R_AX_TCR1_C1, val32);
+ seq_puts(m, "Enable tx infoh dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_TXTF_INFOL_C0:
+ info = &dbg_port_txtf_infol_c0;
+ val32 = rtw89_read32(rtwdev, R_AX_TCR1);
+ val32 |= B_AX_TCR_FORCE_READ_TXDFIFO;
+ rtw89_write32(rtwdev, R_AX_TCR1, val32);
+ seq_puts(m, "Enable tx tf infol dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_TXTF_INFOH_C0:
+ info = &dbg_port_txtf_infoh_c0;
+ val32 = rtw89_read32(rtwdev, R_AX_TCR1);
+ val32 |= B_AX_TCR_FORCE_READ_TXDFIFO;
+ rtw89_write32(rtwdev, R_AX_TCR1, val32);
+ seq_puts(m, "Enable tx tf infoh dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_TXTF_INFOL_C1:
+ info = &dbg_port_txtf_infol_c1;
+ val32 = rtw89_read32(rtwdev, R_AX_TCR1_C1);
+ val32 |= B_AX_TCR_FORCE_READ_TXDFIFO;
+ rtw89_write32(rtwdev, R_AX_TCR1_C1, val32);
+ seq_puts(m, "Enable tx tf infol dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_TXTF_INFOH_C1:
+ info = &dbg_port_txtf_infoh_c1;
+ val32 = rtw89_read32(rtwdev, R_AX_TCR1_C1);
+ val32 |= B_AX_TCR_FORCE_READ_TXDFIFO;
+ rtw89_write32(rtwdev, R_AX_TCR1_C1, val32);
+ seq_puts(m, "Enable tx tf infoh dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_WDE_BUFMGN_FREEPG:
+ info = &dbg_port_wde_bufmgn_freepg;
+ seq_puts(m, "Enable wde bufmgn freepg dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_WDE_BUFMGN_QUOTA:
+ info = &dbg_port_wde_bufmgn_quota;
+ seq_puts(m, "Enable wde bufmgn quota dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_WDE_BUFMGN_PAGELLT:
+ info = &dbg_port_wde_bufmgn_pagellt;
+ seq_puts(m, "Enable wde bufmgn pagellt dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_WDE_BUFMGN_PKTINFO:
+ info = &dbg_port_wde_bufmgn_pktinfo;
+ seq_puts(m, "Enable wde bufmgn pktinfo dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_WDE_QUEMGN_PREPKT:
+ info = &dbg_port_wde_quemgn_prepkt;
+ seq_puts(m, "Enable wde quemgn prepkt dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_WDE_QUEMGN_NXTPKT:
+ info = &dbg_port_wde_quemgn_nxtpkt;
+ seq_puts(m, "Enable wde quemgn nxtpkt dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_WDE_QUEMGN_QLNKTBL:
+ info = &dbg_port_wde_quemgn_qlnktbl;
+ seq_puts(m, "Enable wde quemgn qlnktbl dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_WDE_QUEMGN_QEMPTY:
+ info = &dbg_port_wde_quemgn_qempty;
+ seq_puts(m, "Enable wde quemgn qempty dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_PLE_BUFMGN_FREEPG:
+ info = &dbg_port_ple_bufmgn_freepg;
+ seq_puts(m, "Enable ple bufmgn freepg dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_PLE_BUFMGN_QUOTA:
+ info = &dbg_port_ple_bufmgn_quota;
+ seq_puts(m, "Enable ple bufmgn quota dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_PLE_BUFMGN_PAGELLT:
+ info = &dbg_port_ple_bufmgn_pagellt;
+ seq_puts(m, "Enable ple bufmgn pagellt dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_PLE_BUFMGN_PKTINFO:
+ info = &dbg_port_ple_bufmgn_pktinfo;
+ seq_puts(m, "Enable ple bufmgn pktinfo dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_PLE_QUEMGN_PREPKT:
+ info = &dbg_port_ple_quemgn_prepkt;
+ seq_puts(m, "Enable ple quemgn prepkt dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_PLE_QUEMGN_NXTPKT:
+ info = &dbg_port_ple_quemgn_nxtpkt;
+ seq_puts(m, "Enable ple quemgn nxtpkt dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_PLE_QUEMGN_QLNKTBL:
+ info = &dbg_port_ple_quemgn_qlnktbl;
+ seq_puts(m, "Enable ple quemgn qlnktbl dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_PLE_QUEMGN_QEMPTY:
+ info = &dbg_port_ple_quemgn_qempty;
+ seq_puts(m, "Enable ple quemgn qempty dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_PKTINFO:
+ info = &dbg_port_pktinfo;
+ seq_puts(m, "Enable pktinfo dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_PCIE_TXDMA:
+ info = &dbg_port_pcie_txdma;
+ val32 = rtw89_read32(rtwdev, R_AX_DBG_CTRL);
+ val32 = u32_replace_bits(val32, PCIE_TXDMA_DBG_SEL, B_AX_DBG_SEL0);
+ val32 = u32_replace_bits(val32, PCIE_TXDMA_DBG_SEL, B_AX_DBG_SEL1);
+ rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32);
+ seq_puts(m, "Enable pcie txdma dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_PCIE_RXDMA:
+ info = &dbg_port_pcie_rxdma;
+ val32 = rtw89_read32(rtwdev, R_AX_DBG_CTRL);
+ val32 = u32_replace_bits(val32, PCIE_RXDMA_DBG_SEL, B_AX_DBG_SEL0);
+ val32 = u32_replace_bits(val32, PCIE_RXDMA_DBG_SEL, B_AX_DBG_SEL1);
+ rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32);
+ seq_puts(m, "Enable pcie rxdma dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_PCIE_CVT:
+ info = &dbg_port_pcie_cvt;
+ val32 = rtw89_read32(rtwdev, R_AX_DBG_CTRL);
+ val32 = u32_replace_bits(val32, PCIE_CVT_DBG_SEL, B_AX_DBG_SEL0);
+ val32 = u32_replace_bits(val32, PCIE_CVT_DBG_SEL, B_AX_DBG_SEL1);
+ rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32);
+ seq_puts(m, "Enable pcie cvt dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_PCIE_CXPL:
+ info = &dbg_port_pcie_cxpl;
+ val32 = rtw89_read32(rtwdev, R_AX_DBG_CTRL);
+ val32 = u32_replace_bits(val32, PCIE_CXPL_DBG_SEL, B_AX_DBG_SEL0);
+ val32 = u32_replace_bits(val32, PCIE_CXPL_DBG_SEL, B_AX_DBG_SEL1);
+ rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32);
+ seq_puts(m, "Enable pcie cxpl dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_PCIE_IO:
+ info = &dbg_port_pcie_io;
+ val32 = rtw89_read32(rtwdev, R_AX_DBG_CTRL);
+ val32 = u32_replace_bits(val32, PCIE_IO_DBG_SEL, B_AX_DBG_SEL0);
+ val32 = u32_replace_bits(val32, PCIE_IO_DBG_SEL, B_AX_DBG_SEL1);
+ rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32);
+ seq_puts(m, "Enable pcie io dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_PCIE_MISC:
+ info = &dbg_port_pcie_misc;
+ val32 = rtw89_read32(rtwdev, R_AX_DBG_CTRL);
+ val32 = u32_replace_bits(val32, PCIE_MISC_DBG_SEL, B_AX_DBG_SEL0);
+ val32 = u32_replace_bits(val32, PCIE_MISC_DBG_SEL, B_AX_DBG_SEL1);
+ rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32);
+ seq_puts(m, "Enable pcie misc dump.\n");
+ break;
+ case RTW89_DBG_PORT_SEL_PCIE_MISC2:
+ info = &dbg_port_pcie_misc2;
+ val16 = rtw89_read16(rtwdev, R_AX_PCIE_DBG_CTRL);
+ val16 = u16_replace_bits(val16, PCIE_MISC2_DBG_SEL,
+ B_AX_LOOPBACK_DBG_SEL);
+ rtw89_write16(rtwdev, R_AX_PCIE_DBG_CTRL, val16);
+ seq_puts(m, "Enable pcie misc2 dump.\n");
+ break;
+ default:
+ seq_puts(m, "Dbg port select err\n");
+ return NULL;
+ }
+
+ return info;
+}
+
+static bool is_dbg_port_valid(struct rtw89_dev *rtwdev, u32 sel)
+{
+ if (rtwdev->hci.type != RTW89_HCI_TYPE_PCIE &&
+ sel >= RTW89_DBG_PORT_SEL_PCIE_TXDMA &&
+ sel <= RTW89_DBG_PORT_SEL_PCIE_MISC2)
+ return false;
+ if (rtwdev->chip->chip_id == RTL8852B &&
+ sel >= RTW89_DBG_PORT_SEL_PTCL_C1 &&
+ sel <= RTW89_DBG_PORT_SEL_TXTF_INFOH_C1)
+ return false;
+ if (rtw89_mac_check_mac_en(rtwdev, 0, RTW89_DMAC_SEL) &&
+ sel >= RTW89_DBG_PORT_SEL_WDE_BUFMGN_FREEPG &&
+ sel <= RTW89_DBG_PORT_SEL_PKTINFO)
+ return false;
+ if (rtw89_mac_check_mac_en(rtwdev, 0, RTW89_CMAC_SEL) &&
+ sel >= RTW89_DBG_PORT_SEL_PTCL_C0 &&
+ sel <= RTW89_DBG_PORT_SEL_TXTF_INFOH_C0)
+ return false;
+ if (rtw89_mac_check_mac_en(rtwdev, 1, RTW89_CMAC_SEL) &&
+ sel >= RTW89_DBG_PORT_SEL_PTCL_C1 &&
+ sel <= RTW89_DBG_PORT_SEL_TXTF_INFOH_C1)
+ return false;
+
+ return true;
+}
+
+static int rtw89_debug_mac_dbg_port_dump(struct rtw89_dev *rtwdev,
+ struct seq_file *m, u32 sel)
+{
+ const struct rtw89_mac_dbg_port_info *info;
+ u8 val8;
+ u16 val16;
+ u32 val32;
+ u32 i;
+
+ info = rtw89_debug_mac_dbg_port_sel(m, rtwdev, sel);
+ if (!info) {
+ rtw89_err(rtwdev, "failed to select debug port %d\n", sel);
+ return -EINVAL;
+ }
+
+#define case_DBG_SEL(__sel) \
+ case RTW89_DBG_PORT_SEL_##__sel: \
+ seq_puts(m, "Dump debug port " #__sel ":\n"); \
+ break
+
+ switch (sel) {
+ case_DBG_SEL(PTCL_C0);
+ case_DBG_SEL(PTCL_C1);
+ case_DBG_SEL(SCH_C0);
+ case_DBG_SEL(SCH_C1);
+ case_DBG_SEL(TMAC_C0);
+ case_DBG_SEL(TMAC_C1);
+ case_DBG_SEL(RMAC_C0);
+ case_DBG_SEL(RMAC_C1);
+ case_DBG_SEL(RMACST_C0);
+ case_DBG_SEL(RMACST_C1);
+ case_DBG_SEL(TRXPTCL_C0);
+ case_DBG_SEL(TRXPTCL_C1);
+ case_DBG_SEL(TX_INFOL_C0);
+ case_DBG_SEL(TX_INFOH_C0);
+ case_DBG_SEL(TX_INFOL_C1);
+ case_DBG_SEL(TX_INFOH_C1);
+ case_DBG_SEL(TXTF_INFOL_C0);
+ case_DBG_SEL(TXTF_INFOH_C0);
+ case_DBG_SEL(TXTF_INFOL_C1);
+ case_DBG_SEL(TXTF_INFOH_C1);
+ case_DBG_SEL(WDE_BUFMGN_FREEPG);
+ case_DBG_SEL(WDE_BUFMGN_QUOTA);
+ case_DBG_SEL(WDE_BUFMGN_PAGELLT);
+ case_DBG_SEL(WDE_BUFMGN_PKTINFO);
+ case_DBG_SEL(WDE_QUEMGN_PREPKT);
+ case_DBG_SEL(WDE_QUEMGN_NXTPKT);
+ case_DBG_SEL(WDE_QUEMGN_QLNKTBL);
+ case_DBG_SEL(WDE_QUEMGN_QEMPTY);
+ case_DBG_SEL(PLE_BUFMGN_FREEPG);
+ case_DBG_SEL(PLE_BUFMGN_QUOTA);
+ case_DBG_SEL(PLE_BUFMGN_PAGELLT);
+ case_DBG_SEL(PLE_BUFMGN_PKTINFO);
+ case_DBG_SEL(PLE_QUEMGN_PREPKT);
+ case_DBG_SEL(PLE_QUEMGN_NXTPKT);
+ case_DBG_SEL(PLE_QUEMGN_QLNKTBL);
+ case_DBG_SEL(PLE_QUEMGN_QEMPTY);
+ case_DBG_SEL(PKTINFO);
+ case_DBG_SEL(PCIE_TXDMA);
+ case_DBG_SEL(PCIE_RXDMA);
+ case_DBG_SEL(PCIE_CVT);
+ case_DBG_SEL(PCIE_CXPL);
+ case_DBG_SEL(PCIE_IO);
+ case_DBG_SEL(PCIE_MISC);
+ case_DBG_SEL(PCIE_MISC2);
+ }
+
+#undef case_DBG_SEL
+
+ seq_printf(m, "Sel addr = 0x%X\n", info->sel_addr);
+ seq_printf(m, "Read addr = 0x%X\n", info->rd_addr);
+
+ for (i = info->srt; i <= info->end; i++) {
+ switch (info->sel_byte) {
+ case 1:
+ default:
+ rtw89_write8_mask(rtwdev, info->sel_addr,
+ info->sel_msk, i);
+ seq_printf(m, "0x%02X: ", i);
+ break;
+ case 2:
+ rtw89_write16_mask(rtwdev, info->sel_addr,
+ info->sel_msk, i);
+ seq_printf(m, "0x%04X: ", i);
+ break;
+ case 4:
+ rtw89_write32_mask(rtwdev, info->sel_addr,
+ info->sel_msk, i);
+ seq_printf(m, "0x%04X: ", i);
+ break;
+ }
+
+ udelay(10);
+
+ switch (info->rd_byte) {
+ case 1:
+ default:
+ val8 = rtw89_read8_mask(rtwdev,
+ info->rd_addr, info->rd_msk);
+ seq_printf(m, "0x%02X\n", val8);
+ break;
+ case 2:
+ val16 = rtw89_read16_mask(rtwdev,
+ info->rd_addr, info->rd_msk);
+ seq_printf(m, "0x%04X\n", val16);
+ break;
+ case 4:
+ val32 = rtw89_read32_mask(rtwdev,
+ info->rd_addr, info->rd_msk);
+ seq_printf(m, "0x%08X\n", val32);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int rtw89_debug_mac_dump_dbg_port(struct rtw89_dev *rtwdev,
+ struct seq_file *m)
+{
+ u32 sel;
+ int ret = 0;
+
+ for (sel = RTW89_DBG_PORT_SEL_PTCL_C0;
+ sel < RTW89_DBG_PORT_SEL_LAST; sel++) {
+ if (!is_dbg_port_valid(rtwdev, sel))
+ continue;
+ ret = rtw89_debug_mac_dbg_port_dump(rtwdev, m, sel);
+ if (ret) {
+ rtw89_err(rtwdev,
+ "failed to dump debug port %d\n", sel);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int
+rtw89_debug_priv_mac_dbg_port_dump_get(struct seq_file *m, void *v)
+{
+ struct rtw89_debugfs_priv *debugfs_priv = m->private;
+ struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
+
+ if (debugfs_priv->dbgpkg_en.ss_dbg)
+ rtw89_debug_mac_dump_ss_dbg(rtwdev, m);
+ if (debugfs_priv->dbgpkg_en.dle_dbg)
+ rtw89_debug_mac_dump_dle_dbg(rtwdev, m);
+ if (debugfs_priv->dbgpkg_en.dmac_dbg)
+ rtw89_debug_mac_dump_dmac_dbg(rtwdev, m);
+ if (debugfs_priv->dbgpkg_en.cmac_dbg)
+ rtw89_debug_mac_dump_cmac_dbg(rtwdev, m);
+ if (debugfs_priv->dbgpkg_en.dbg_port)
+ rtw89_debug_mac_dump_dbg_port(rtwdev, m);
+
+ return 0;
+};
+
+static ssize_t rtw89_debug_priv_send_h2c_set(struct file *filp,
+ const char __user *user_buf,
+ size_t count, loff_t *loff)
+{
+ struct rtw89_debugfs_priv *debugfs_priv = filp->private_data;
+ struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
+ char buf[256];
+ size_t buf_size;
+ u8 h2c[128];
+ int num;
+
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ num = buf_size / 2;
+ if (hex2bin(h2c, buf, num)) {
+ rtw89_info(rtwdev, "invalid format: H1H2H3...\n");
+ return -EINVAL;
+ }
+
+ rtw89_fw_h2c_raw(rtwdev, h2c, num);
+
+ return count;
+}
+
+static int rtw89_debug_priv_btc_info_get(struct seq_file *m, void *v)
+{
+ struct rtw89_debugfs_priv *debugfs_priv = m->private;
+ struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
+
+ rtw89_btc_dump_info(rtwdev, m);
+
+ return 0;
+}
+
+static ssize_t rtw89_debug_priv_btc_manual_set(struct file *filp,
+ const char __user *user_buf,
+ size_t count, loff_t *loff)
+{
+ struct rtw89_debugfs_priv *debugfs_priv = filp->private_data;
+ struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
+ struct rtw89_btc *btc = &rtwdev->btc;
+ bool btc_manual;
+
+ if (kstrtobool_from_user(user_buf, count, &btc_manual))
+ goto out;
+
+ btc->ctrl.manual = btc_manual;
+out:
+ return count;
+}
+
+static ssize_t rtw89_debug_fw_log_btc_manual_set(struct file *filp,
+ const char __user *user_buf,
+ size_t count, loff_t *loff)
+{
+ struct rtw89_debugfs_priv *debugfs_priv = filp->private_data;
+ struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
+ struct rtw89_fw_info *fw_info = &rtwdev->fw;
+ bool fw_log_manual;
+
+ if (kstrtobool_from_user(user_buf, count, &fw_log_manual))
+ goto out;
+
+ mutex_lock(&rtwdev->mutex);
+ fw_info->fw_log_enable = fw_log_manual;
+ rtw89_fw_h2c_fw_log(rtwdev, fw_log_manual);
+ mutex_unlock(&rtwdev->mutex);
+out:
+ return count;
+}
+
+static void rtw89_sta_info_get_iter(void *data, struct ieee80211_sta *sta)
+{
+ static const char * const he_gi_str[] = {
+ [NL80211_RATE_INFO_HE_GI_0_8] = "0.8",
+ [NL80211_RATE_INFO_HE_GI_1_6] = "1.6",
+ [NL80211_RATE_INFO_HE_GI_3_2] = "3.2",
+ };
+ struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv;
+ struct rate_info *rate = &rtwsta->ra_report.txrate;
+ struct ieee80211_rx_status *status = &rtwsta->rx_status;
+ struct seq_file *m = (struct seq_file *)data;
+ u8 rssi;
+
+ seq_printf(m, "TX rate [%d]: ", rtwsta->mac_id);
+
+ if (rate->flags & RATE_INFO_FLAGS_MCS)
+ seq_printf(m, "HT MCS-%d%s", rate->mcs,
+ rate->flags & RATE_INFO_FLAGS_SHORT_GI ? " SGI" : "");
+ else if (rate->flags & RATE_INFO_FLAGS_VHT_MCS)
+ seq_printf(m, "VHT %dSS MCS-%d%s", rate->nss, rate->mcs,
+ rate->flags & RATE_INFO_FLAGS_SHORT_GI ? " SGI" : "");
+ else if (rate->flags & RATE_INFO_FLAGS_HE_MCS)
+ seq_printf(m, "HE %dSS MCS-%d GI:%s", rate->nss, rate->mcs,
+ rate->he_gi <= NL80211_RATE_INFO_HE_GI_3_2 ?
+ he_gi_str[rate->he_gi] : "N/A");
+ else
+ seq_printf(m, "Legacy %d", rate->legacy);
+ seq_printf(m, "\t(hw_rate=0x%x)", rtwsta->ra_report.hw_rate);
+ seq_printf(m, "\t==> agg_wait=%d (%d)\n", rtwsta->max_agg_wait,
+ sta->max_rc_amsdu_len);
+
+ seq_printf(m, "RX rate [%d]: ", rtwsta->mac_id);
+
+ switch (status->encoding) {
+ case RX_ENC_LEGACY:
+ seq_printf(m, "Legacy %d", status->rate_idx +
+ (status->band == NL80211_BAND_5GHZ ? 4 : 0));
+ break;
+ case RX_ENC_HT:
+ seq_printf(m, "HT MCS-%d%s", status->rate_idx,
+ status->enc_flags & RX_ENC_FLAG_SHORT_GI ? " SGI" : "");
+ break;
+ case RX_ENC_VHT:
+ seq_printf(m, "VHT %dSS MCS-%d%s", status->nss, status->rate_idx,
+ status->enc_flags & RX_ENC_FLAG_SHORT_GI ? " SGI" : "");
+ break;
+ case RX_ENC_HE:
+ seq_printf(m, "HE %dSS MCS-%d GI:%s", status->nss, status->rate_idx,
+ status->he_gi <= NL80211_RATE_INFO_HE_GI_3_2 ?
+ he_gi_str[rate->he_gi] : "N/A");
+ break;
+ }
+ seq_printf(m, "\t(hw_rate=0x%x)\n", rtwsta->rx_hw_rate);
+
+ rssi = ewma_rssi_read(&rtwsta->avg_rssi);
+ seq_printf(m, "RSSI: %d dBm (raw=%d, prev=%d)\n",
+ RTW89_RSSI_RAW_TO_DBM(rssi), rssi, rtwsta->prev_rssi);
+}
+
+static void
+rtw89_debug_append_rx_rate(struct seq_file *m, struct rtw89_pkt_stat *pkt_stat,
+ enum rtw89_hw_rate first_rate, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ seq_printf(m, "%s%u", i == 0 ? "" : ", ",
+ pkt_stat->rx_rate_cnt[first_rate + i]);
+}
+
+static const struct rtw89_rx_rate_cnt_info {
+ enum rtw89_hw_rate first_rate;
+ int len;
+ const char *rate_mode;
+} rtw89_rx_rate_cnt_infos[] = {
+ {RTW89_HW_RATE_CCK1, 4, "Legacy:"},
+ {RTW89_HW_RATE_OFDM6, 8, "OFDM:"},
+ {RTW89_HW_RATE_MCS0, 8, "HT 0:"},
+ {RTW89_HW_RATE_MCS8, 8, "HT 1:"},
+ {RTW89_HW_RATE_VHT_NSS1_MCS0, 10, "VHT 1SS:"},
+ {RTW89_HW_RATE_VHT_NSS2_MCS0, 10, "VHT 2SS:"},
+ {RTW89_HW_RATE_HE_NSS1_MCS0, 12, "HE 1SS:"},
+ {RTW89_HW_RATE_HE_NSS2_MCS0, 12, "HE 2ss:"},
+};
+
+static int rtw89_debug_priv_phy_info_get(struct seq_file *m, void *v)
+{
+ struct rtw89_debugfs_priv *debugfs_priv = m->private;
+ struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;
+ struct rtw89_traffic_stats *stats = &rtwdev->stats;
+ struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.last_pkt_stat;
+ const struct rtw89_rx_rate_cnt_info *info;
+ int i;
+
+ seq_printf(m, "TP TX: %u [%u] Mbps (lv: %d), RX: %u [%u] Mbps (lv: %d)\n",
+ stats->tx_throughput, stats->tx_throughput_raw, stats->tx_tfc_lv,
+ stats->rx_throughput, stats->rx_throughput_raw, stats->rx_tfc_lv);
+ seq_printf(m, "Beacon: %u\n", pkt_stat->beacon_nr);
+ seq_printf(m, "Avg packet length: TX=%u, RX=%u\n", stats->tx_avg_len,
+ stats->rx_avg_len);
+
+ seq_puts(m, "RX count:\n");
+ for (i = 0; i < ARRAY_SIZE(rtw89_rx_rate_cnt_infos); i++) {
+ info = &rtw89_rx_rate_cnt_infos[i];
+ seq_printf(m, "%10s [", info->rate_mode);
+ rtw89_debug_append_rx_rate(m, pkt_stat,
+ info->first_rate, info->len);
+ seq_puts(m, "]\n");
+ }
+
+ ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_sta_info_get_iter, m);
+
+ return 0;
+}
+
+static struct rtw89_debugfs_priv rtw89_debug_priv_read_reg = {
+ .cb_read = rtw89_debug_priv_read_reg_get,
+ .cb_write = rtw89_debug_priv_read_reg_select,
+};
+
+static struct rtw89_debugfs_priv rtw89_debug_priv_write_reg = {
+ .cb_write = rtw89_debug_priv_write_reg_set,
+};
+
+static struct rtw89_debugfs_priv rtw89_debug_priv_read_rf = {
+ .cb_read = rtw89_debug_priv_read_rf_get,
+ .cb_write = rtw89_debug_priv_read_rf_select,
+};
+
+static struct rtw89_debugfs_priv rtw89_debug_priv_write_rf = {
+ .cb_write = rtw89_debug_priv_write_rf_set,
+};
+
+static struct rtw89_debugfs_priv rtw89_debug_priv_rf_reg_dump = {
+ .cb_read = rtw89_debug_priv_rf_reg_dump_get,
+};
+
+static struct rtw89_debugfs_priv rtw89_debug_priv_txpwr_table = {
+ .cb_read = rtw89_debug_priv_txpwr_table_get,
+};
+
+static struct rtw89_debugfs_priv rtw89_debug_priv_mac_reg_dump = {
+ .cb_read = rtw89_debug_priv_mac_reg_dump_get,
+ .cb_write = rtw89_debug_priv_mac_reg_dump_select,
+};
+
+static struct rtw89_debugfs_priv rtw89_debug_priv_mac_mem_dump = {
+ .cb_read = rtw89_debug_priv_mac_mem_dump_get,
+ .cb_write = rtw89_debug_priv_mac_mem_dump_select,
+};
+
+static struct rtw89_debugfs_priv rtw89_debug_priv_mac_dbg_port_dump = {
+ .cb_read = rtw89_debug_priv_mac_dbg_port_dump_get,
+ .cb_write = rtw89_debug_priv_mac_dbg_port_dump_select,
+};
+
+static struct rtw89_debugfs_priv rtw89_debug_priv_send_h2c = {
+ .cb_write = rtw89_debug_priv_send_h2c_set,
+};
+
+static struct rtw89_debugfs_priv rtw89_debug_priv_btc_info = {
+ .cb_read = rtw89_debug_priv_btc_info_get,
+};
+
+static struct rtw89_debugfs_priv rtw89_debug_priv_btc_manual = {
+ .cb_write = rtw89_debug_priv_btc_manual_set,
+};
+
+static struct rtw89_debugfs_priv rtw89_debug_priv_fw_log_manual = {
+ .cb_write = rtw89_debug_fw_log_btc_manual_set,
+};
+
+static struct rtw89_debugfs_priv rtw89_debug_priv_phy_info = {
+ .cb_read = rtw89_debug_priv_phy_info_get,
+};
+
+#define rtw89_debugfs_add(name, mode, fopname, parent) \
+ do { \
+ rtw89_debug_priv_ ##name.rtwdev = rtwdev; \
+ if (!debugfs_create_file(#name, mode, \
+ parent, &rtw89_debug_priv_ ##name, \
+ &file_ops_ ##fopname)) \
+ pr_debug("Unable to initialize debugfs:%s\n", #name); \
+ } while (0)
+
+#define rtw89_debugfs_add_w(name) \
+ rtw89_debugfs_add(name, S_IFREG | 0222, single_w, debugfs_topdir)
+#define rtw89_debugfs_add_rw(name) \
+ rtw89_debugfs_add(name, S_IFREG | 0666, common_rw, debugfs_topdir)
+#define rtw89_debugfs_add_r(name) \
+ rtw89_debugfs_add(name, S_IFREG | 0444, single_r, debugfs_topdir)
+
+void rtw89_debugfs_init(struct rtw89_dev *rtwdev)
+{
+ struct dentry *debugfs_topdir;
+
+ debugfs_topdir = debugfs_create_dir("rtw89",
+ rtwdev->hw->wiphy->debugfsdir);
+
+ rtw89_debugfs_add_rw(read_reg);
+ rtw89_debugfs_add_w(write_reg);
+ rtw89_debugfs_add_rw(read_rf);
+ rtw89_debugfs_add_w(write_rf);
+ rtw89_debugfs_add_r(rf_reg_dump);
+ rtw89_debugfs_add_r(txpwr_table);
+ rtw89_debugfs_add_rw(mac_reg_dump);
+ rtw89_debugfs_add_rw(mac_mem_dump);
+ rtw89_debugfs_add_rw(mac_dbg_port_dump);
+ rtw89_debugfs_add_w(send_h2c);
+ rtw89_debugfs_add_r(btc_info);
+ rtw89_debugfs_add_w(btc_manual);
+ rtw89_debugfs_add_w(fw_log_manual);
+ rtw89_debugfs_add_r(phy_info);
+}
+#endif
+
+#ifdef CONFIG_RTW89_DEBUGMSG
+void __rtw89_debug(struct rtw89_dev *rtwdev,
+ enum rtw89_debug_mask mask,
+ const char *fmt, ...)
+{
+ struct va_format vaf = {
+ .fmt = fmt,
+ };
+
+ va_list args;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+
+ if (rtw89_debug_mask & mask)
+ dev_printk(KERN_DEBUG, rtwdev->dev, "%pV", &vaf);
+
+ va_end(args);
+}
+EXPORT_SYMBOL(__rtw89_debug);
+#endif
diff --git a/drivers/net/wireless/realtek/rtw89/debug.h b/drivers/net/wireless/realtek/rtw89/debug.h
new file mode 100644
index 000000000000..f14b726c1a9f
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw89/debug.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/* Copyright(c) 2019-2020 Realtek Corporation
+ */
+
+#ifndef __RTW89_DEBUG_H__
+#define __RTW89_DEBUG_H__
+
+#include "core.h"
+
+enum rtw89_debug_mask {
+ RTW89_DBG_TXRX = BIT(0),
+ RTW89_DBG_RFK = BIT(1),
+ RTW89_DBG_RFK_TRACK = BIT(2),
+ RTW89_DBG_CFO = BIT(3),
+ RTW89_DBG_TSSI = BIT(4),
+ RTW89_DBG_TXPWR = BIT(5),
+ RTW89_DBG_HCI = BIT(6),
+ RTW89_DBG_RA = BIT(7),
+ RTW89_DBG_REGD = BIT(8),
+ RTW89_DBG_PHY_TRACK = BIT(9),
+ RTW89_DBG_DIG = BIT(10),
+ RTW89_DBG_SER = BIT(11),
+ RTW89_DBG_FW = BIT(12),
+ RTW89_DBG_BTC = BIT(13),
+ RTW89_DBG_BF = BIT(14),
+};
+
+enum rtw89_debug_mac_reg_sel {
+ RTW89_DBG_SEL_MAC_00,
+ RTW89_DBG_SEL_MAC_40,
+ RTW89_DBG_SEL_MAC_80,
+ RTW89_DBG_SEL_MAC_C0,
+ RTW89_DBG_SEL_MAC_E0,
+ RTW89_DBG_SEL_BB,
+ RTW89_DBG_SEL_IQK,
+ RTW89_DBG_SEL_RFC,
+};
+
+#ifdef CONFIG_RTW89_DEBUGFS
+void rtw89_debugfs_init(struct rtw89_dev *rtwdev);
+#else
+static inline void rtw89_debugfs_init(struct rtw89_dev *rtwdev) {}
+#endif
+
+#define rtw89_info(rtwdev, a...) dev_info((rtwdev)->dev, ##a)
+#define rtw89_warn(rtwdev, a...) dev_warn((rtwdev)->dev, ##a)
+#define rtw89_err(rtwdev, a...) dev_err((rtwdev)->dev, ##a)
+
+#ifdef CONFIG_RTW89_DEBUGMSG
+extern unsigned int rtw89_debug_mask;
+#define rtw89_debug(rtwdev, a...) __rtw89_debug(rtwdev, ##a)
+
+__printf(3, 4)
+void __rtw89_debug(struct rtw89_dev *rtwdev,
+ enum rtw89_debug_mask mask,
+ const char *fmt, ...);
+static inline void rtw89_hex_dump(struct rtw89_dev *rtwdev,
+ enum rtw89_debug_mask mask,
+ const char *prefix_str,
+ const void *buf, size_t len)
+{
+ if (!(rtw89_debug_mask & mask))
+ return;
+
+ print_hex_dump_bytes(prefix_str, DUMP_PREFIX_OFFSET, buf, len);
+}
+#else
+static inline void rtw89_debug(struct rtw89_dev *rtwdev,
+ enum rtw89_debug_mask mask,
+ const char *fmt, ...) {}
+static inline void rtw89_hex_dump(struct rtw89_dev *rtwdev,
+ enum rtw89_debug_mask mask,
+ const char *prefix_str,
+ const void *buf, size_t len) {}
+#endif
+
+#endif
--
2.25.1

2021-06-18 06:50:40

by Pkshih

[permalink] [raw]
Subject: [PATCH 06/24] rtw89: add files to download and communicate with firmware

The firmware must be downloaded right after HCI basic initialization, and
then we can obtain hardware capabilities that are used to do mac80211
register hw.

To download firmware, we need to parse the header to know how many sections
the firmware has, and then download each section to proper location.

We introduce H2C and C2H to do bi-direction communication with firmware,
and both support packet-based and register-based methods.
Normally, we use packet-based H2C/C2H, because it has no number and size
limits. In contrast, register-based H2C/C2H has only one message and
fix-four-dword in size.

Header size of packet-based H2C/C2H is eight bytes (two dwords), which uses
a hierarchical IDs, containing type, category, class and function, to
classify a H2C or C2H command.

When a C2H is received in interrupt context, we don't process it right
there, but queue the skb and wake up a ieee80211 work to handle the skb.

Signed-off-by: Ping-Ke Shih <[email protected]>
---
drivers/net/wireless/realtek/rtw89/fw.c | 1599 +++++++++++++++++++++++
drivers/net/wireless/realtek/rtw89/fw.h | 1378 +++++++++++++++++++
2 files changed, 2977 insertions(+)
create mode 100644 drivers/net/wireless/realtek/rtw89/fw.c
create mode 100644 drivers/net/wireless/realtek/rtw89/fw.h

diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c
new file mode 100644
index 000000000000..7be5ce2c5d2c
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw89/fw.c
@@ -0,0 +1,1599 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Copyright(c) 2019-2020 Realtek Corporation
+ */
+
+#include "cam.h"
+#include "coex.h"
+#include "debug.h"
+#include "fw.h"
+#include "mac.h"
+#include "phy.h"
+#include "reg.h"
+
+static struct sk_buff *rtw89_fw_h2c_alloc_skb(u32 len, bool header)
+{
+ struct sk_buff *skb;
+ u32 header_len = 0;
+
+ if (header)
+ header_len = H2C_HEADER_LEN;
+
+ skb = dev_alloc_skb(len + header_len + 24);
+ if (!skb)
+ return NULL;
+ skb_reserve(skb, header_len + 24);
+ memset(skb->data, 0, len);
+
+ return skb;
+}
+
+struct sk_buff *rtw89_fw_h2c_alloc_skb_with_hdr(u32 len)
+{
+ return rtw89_fw_h2c_alloc_skb(len, true);
+}
+
+struct sk_buff *rtw89_fw_h2c_alloc_skb_no_hdr(u32 len)
+{
+ return rtw89_fw_h2c_alloc_skb(len, false);
+}
+
+static u8 _fw_get_rdy(struct rtw89_dev *rtwdev)
+{
+ u8 val = rtw89_read8(rtwdev, R_AX_WCPU_FW_CTRL);
+
+ return FIELD_GET(B_AX_WCPU_FWDL_STS_MASK, val);
+}
+
+#define FWDL_WAIT_CNT 400000
+int rtw89_fw_check_rdy(struct rtw89_dev *rtwdev)
+{
+ u8 val;
+ int ret;
+
+ ret = read_poll_timeout_atomic(_fw_get_rdy, val,
+ val == RTW89_FWDL_WCPU_FW_INIT_RDY,
+ 1, FWDL_WAIT_CNT, false, rtwdev);
+ if (ret) {
+ switch (val) {
+ case RTW89_FWDL_CHECKSUM_FAIL:
+ rtw89_err(rtwdev, "fw checksum fail\n");
+ return -EINVAL;
+
+ case RTW89_FWDL_SECURITY_FAIL:
+ rtw89_err(rtwdev, "fw security fail\n");
+ return -EINVAL;
+
+ case RTW89_FWDL_CV_NOT_MATCH:
+ rtw89_err(rtwdev, "fw cv not match\n");
+ return -EINVAL;
+
+ default:
+ return -EBUSY;
+ }
+ }
+
+ set_bit(RTW89_FLAG_FW_RDY, rtwdev->flags);
+
+ return 0;
+}
+
+static int rtw89_fw_hdr_parser(struct rtw89_dev *rtwdev, const u8 *fw, u32 len,
+ struct rtw89_fw_bin_info *info)
+{
+ struct rtw89_fw_hdr_section_info *section_info;
+ const u8 *fw_end = fw + len;
+ const u8 *bin;
+ u32 i;
+
+ if (!info)
+ return -EINVAL;
+
+ info->section_num = GET_FW_HDR_SEC_NUM(fw);
+ info->hdr_len = RTW89_FW_HDR_SIZE +
+ info->section_num * RTW89_FW_SECTION_HDR_SIZE;
+ SET_FW_HDR_PART_SIZE(fw, FWDL_SECTION_PER_PKT_LEN);
+
+ bin = fw + info->hdr_len;
+
+ /* jump to section header */
+ fw += RTW89_FW_HDR_SIZE;
+ section_info = info->section_info;
+ for (i = 0; i < info->section_num; i++) {
+ section_info->len = GET_FWSECTION_HDR_SEC_SIZE(fw);
+ if (GET_FWSECTION_HDR_CHECKSUM(fw))
+ section_info->len += FWDL_SECTION_CHKSUM_LEN;
+ section_info->redl = GET_FWSECTION_HDR_REDL(fw);
+ section_info->dladdr =
+ GET_FWSECTION_HDR_DL_ADDR(fw) & 0x1fffffff;
+ section_info->addr = bin;
+ bin += section_info->len;
+ fw += RTW89_FW_SECTION_HDR_SIZE;
+ section_info++;
+ }
+
+ if (fw_end != bin) {
+ rtw89_err(rtwdev, "[ERR]fw bin size\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static
+int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,
+ struct rtw89_fw_suit *fw_suit)
+{
+ struct rtw89_fw_info *fw_info = &rtwdev->fw;
+ const u8 *mfw = fw_info->firmware->data;
+ u32 mfw_len = fw_info->firmware->size;
+ const struct rtw89_mfw_hdr *mfw_hdr = (const struct rtw89_mfw_hdr *)mfw;
+ const struct rtw89_mfw_info *mfw_info;
+ int i;
+
+ if (mfw_hdr->sig != RTW89_MFW_SIG) {
+ rtw89_debug(rtwdev, RTW89_DBG_FW, "use legacy firmware\n");
+ /* legacy firmware support normal type only */
+ if (type != RTW89_FW_NORMAL)
+ return -EINVAL;
+ fw_suit->data = mfw;
+ fw_suit->size = mfw_len;
+ return 0;
+ }
+
+ for (i = 0; i < mfw_hdr->fw_nr; i++) {
+ mfw_info = &mfw_hdr->info[i];
+ if (mfw_info->cv != rtwdev->hal.cv ||
+ mfw_info->type != type ||
+ mfw_info->mp)
+ continue;
+
+ fw_suit->data = mfw + le32_to_cpu(mfw_info->shift);
+ fw_suit->size = le32_to_cpu(mfw_info->size);
+ return 0;
+ }
+
+ rtw89_err(rtwdev, "no suitable firmware found\n");
+ return -ENOENT;
+}
+
+static void rtw89_fw_update_ver(struct rtw89_dev *rtwdev,
+ enum rtw89_fw_type type,
+ struct rtw89_fw_suit *fw_suit)
+{
+ const u8 *hdr = fw_suit->data;
+
+ fw_suit->major_ver = GET_FW_HDR_MAJOR_VERSION(hdr);
+ fw_suit->minor_ver = GET_FW_HDR_MINOR_VERSION(hdr);
+ fw_suit->sub_ver = GET_FW_HDR_SUBVERSION(hdr);
+ fw_suit->sub_idex = GET_FW_HDR_SUBINDEX(hdr);
+ fw_suit->build_year = GET_FW_HDR_YEAR(hdr);
+ fw_suit->build_mon = GET_FW_HDR_MONTH(hdr);
+ fw_suit->build_date = GET_FW_HDR_DATE(hdr);
+ fw_suit->build_hour = GET_FW_HDR_HOUR(hdr);
+ fw_suit->build_min = GET_FW_HDR_MIN(hdr);
+ fw_suit->cmd_ver = GET_FW_HDR_CMD_VERSERION(hdr);
+
+ rtw89_info(rtwdev,
+ "Firmware version %u.%u.%u.%u, cmd version %u, type %u\n",
+ fw_suit->major_ver, fw_suit->minor_ver, fw_suit->sub_ver,
+ fw_suit->sub_idex, fw_suit->cmd_ver, type);
+}
+
+static
+int __rtw89_fw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type)
+{
+ struct rtw89_fw_suit *fw_suit = rtw89_fw_suit_get(rtwdev, type);
+ int ret;
+
+ ret = rtw89_mfw_recognize(rtwdev, type, fw_suit);
+ if (ret)
+ return ret;
+
+ rtw89_fw_update_ver(rtwdev, type, fw_suit);
+
+ return 0;
+}
+
+int rtw89_fw_recognize(struct rtw89_dev *rtwdev)
+{
+ int ret;
+
+ ret = __rtw89_fw_recognize(rtwdev, RTW89_FW_NORMAL);
+ if (ret)
+ return ret;
+
+ /* It still works if wowlan firmware isn't existing. */
+ __rtw89_fw_recognize(rtwdev, RTW89_FW_WOWLAN);
+
+ return 0;
+}
+
+void rtw89_h2c_pkt_set_hdr(struct rtw89_dev *rtwdev, struct sk_buff *skb,
+ u8 type, u8 cat, u8 class, u8 func,
+ bool rack, bool dack, u32 len)
+{
+ struct fwcmd_hdr *hdr;
+
+ hdr = (struct fwcmd_hdr *)skb_push(skb, 8);
+
+ if (!(rtwdev->fw.h2c_seq % 4))
+ rack = true;
+ hdr->hdr0 = cpu_to_le32(FIELD_PREP(H2C_HDR_DEL_TYPE, type) |
+ FIELD_PREP(H2C_HDR_CAT, cat) |
+ FIELD_PREP(H2C_HDR_CLASS, class) |
+ FIELD_PREP(H2C_HDR_FUNC, func) |
+ FIELD_PREP(H2C_HDR_H2C_SEQ, rtwdev->fw.h2c_seq));
+
+ hdr->hdr1 = cpu_to_le32(FIELD_PREP(H2C_HDR_TOTAL_LEN,
+ len + H2C_HEADER_LEN) |
+ (rack ? H2C_HDR_REC_ACK : 0) |
+ (dack ? H2C_HDR_DONE_ACK : 0));
+
+ rtwdev->fw.h2c_seq++;
+}
+
+static void rtw89_h2c_pkt_set_hdr_fwdl(struct rtw89_dev *rtwdev,
+ struct sk_buff *skb,
+ u8 type, u8 cat, u8 class, u8 func,
+ u32 len)
+{
+ struct fwcmd_hdr *hdr;
+
+ hdr = (struct fwcmd_hdr *)skb_push(skb, 8);
+
+ hdr->hdr0 = cpu_to_le32(FIELD_PREP(H2C_HDR_DEL_TYPE, type) |
+ FIELD_PREP(H2C_HDR_CAT, cat) |
+ FIELD_PREP(H2C_HDR_CLASS, class) |
+ FIELD_PREP(H2C_HDR_FUNC, func) |
+ FIELD_PREP(H2C_HDR_H2C_SEQ, rtwdev->fw.h2c_seq));
+
+ hdr->hdr1 = cpu_to_le32(FIELD_PREP(H2C_HDR_TOTAL_LEN,
+ len + H2C_HEADER_LEN));
+}
+
+static int __rtw89_fw_download_hdr(struct rtw89_dev *rtwdev, const u8 *fw, u32 len)
+{
+ struct sk_buff *skb;
+ u32 ret = 0;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(len);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for fw hdr dl\n");
+ return -ENOMEM;
+ }
+
+ skb_put_data(skb, fw, len);
+ rtw89_h2c_pkt_set_hdr_fwdl(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_MAC, H2C_CL_MAC_FWDL,
+ H2C_FUNC_MAC_FWHDR_DL, len);
+
+ ret = rtw89_h2c_tx(rtwdev, skb, false);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ ret = -1;
+ goto fail;
+ }
+
+ return 0;
+fail:
+ dev_kfree_skb_any(skb);
+
+ return ret;
+}
+
+static int rtw89_fw_download_hdr(struct rtw89_dev *rtwdev, const u8 *fw, u32 len)
+{
+ u8 val;
+ int ret;
+
+ ret = __rtw89_fw_download_hdr(rtwdev, fw, len);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]FW header download\n");
+ return ret;
+ }
+
+ ret = read_poll_timeout_atomic(rtw89_read8, val, val & B_AX_FWDL_PATH_RDY,
+ 1, FWDL_WAIT_CNT, false,
+ rtwdev, R_AX_WCPU_FW_CTRL);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]FWDL path ready\n");
+ return ret;
+ }
+
+ rtw89_write32(rtwdev, R_AX_HALT_H2C_CTRL, 0);
+ rtw89_write32(rtwdev, R_AX_HALT_C2H_CTRL, 0);
+
+ return 0;
+}
+
+static int __rtw89_fw_download_main(struct rtw89_dev *rtwdev,
+ struct rtw89_fw_hdr_section_info *info)
+{
+ struct sk_buff *skb;
+ const u8 *section = info->addr;
+ u32 residue_len = info->len;
+ u32 pkt_len;
+ int ret;
+
+ while (residue_len) {
+ if (residue_len >= FWDL_SECTION_PER_PKT_LEN)
+ pkt_len = FWDL_SECTION_PER_PKT_LEN;
+ else
+ pkt_len = residue_len;
+
+ skb = rtw89_fw_h2c_alloc_skb_no_hdr(pkt_len);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for fw dl\n");
+ return -ENOMEM;
+ }
+ skb_put_data(skb, section, pkt_len);
+
+ ret = rtw89_h2c_tx(rtwdev, skb, true);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ ret = -1;
+ goto fail;
+ }
+
+ section += pkt_len;
+ residue_len -= pkt_len;
+ }
+
+ return 0;
+fail:
+ dev_kfree_skb_any(skb);
+
+ return ret;
+}
+
+static int rtw89_fw_download_main(struct rtw89_dev *rtwdev, const u8 *fw,
+ struct rtw89_fw_bin_info *info)
+{
+ struct rtw89_fw_hdr_section_info *section_info = info->section_info;
+ u8 section_num = info->section_num;
+ int ret;
+
+ while (section_num--) {
+ ret = __rtw89_fw_download_main(rtwdev, section_info);
+ if (ret)
+ return ret;
+ section_info++;
+ }
+
+ mdelay(5);
+
+ ret = rtw89_fw_check_rdy(rtwdev);
+ if (ret) {
+ rtw89_warn(rtwdev, "download firmware fail\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void rtw89_fw_prog_cnt_dump(struct rtw89_dev *rtwdev)
+{
+ u32 val32;
+ u16 index;
+
+ rtw89_write32(rtwdev, R_AX_DBG_CTRL,
+ FIELD_PREP(B_AX_DBG_SEL0, FW_PROG_CNTR_DBG_SEL) |
+ FIELD_PREP(B_AX_DBG_SEL1, FW_PROG_CNTR_DBG_SEL));
+ rtw89_write32_mask(rtwdev, R_AX_SYS_STATUS1, B_AX_SEL_0XC0, MAC_DBG_SEL);
+
+ for (index = 0; index < 15; index++) {
+ val32 = rtw89_read32(rtwdev, R_AX_DBG_PORT_SEL);
+ rtw89_err(rtwdev, "[ERR]fw PC = 0x%x\n", val32);
+ fsleep(10);
+ }
+}
+
+static void rtw89_fw_dl_fail_dump(struct rtw89_dev *rtwdev)
+{
+ u32 val32;
+ u16 val16;
+
+ val32 = rtw89_read32(rtwdev, R_AX_WCPU_FW_CTRL);
+ rtw89_err(rtwdev, "[ERR]fwdl 0x1E0 = 0x%x\n", val32);
+
+ val16 = rtw89_read16(rtwdev, R_AX_BOOT_DBG + 2);
+ rtw89_err(rtwdev, "[ERR]fwdl 0x83F2 = 0x%x\n", val16);
+
+ rtw89_fw_prog_cnt_dump(rtwdev);
+}
+
+int rtw89_fw_download(struct rtw89_dev *rtwdev, enum rtw89_fw_type type)
+{
+ struct rtw89_fw_info *fw_info = &rtwdev->fw;
+ struct rtw89_fw_suit *fw_suit = rtw89_fw_suit_get(rtwdev, type);
+ struct rtw89_fw_bin_info info;
+ const u8 *fw = fw_suit->data;
+ u32 len = fw_suit->size;
+ u8 val;
+ int ret;
+
+ if (!fw || !len) {
+ rtw89_err(rtwdev, "fw type %d isn't recognized\n", type);
+ return -ENOENT;
+ }
+
+ ret = rtw89_fw_hdr_parser(rtwdev, fw, len, &info);
+ if (ret) {
+ rtw89_err(rtwdev, "parse fw header fail\n");
+ goto fwdl_err;
+ }
+
+ ret = read_poll_timeout_atomic(rtw89_read8, val, val & B_AX_H2C_PATH_RDY,
+ 1, FWDL_WAIT_CNT, false,
+ rtwdev, R_AX_WCPU_FW_CTRL);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]H2C path ready\n");
+ goto fwdl_err;
+ }
+
+ ret = rtw89_fw_download_hdr(rtwdev, fw, info.hdr_len);
+ if (ret) {
+ ret = -EBUSY;
+ goto fwdl_err;
+ }
+
+ ret = rtw89_fw_download_main(rtwdev, fw, &info);
+ if (ret) {
+ ret = -EBUSY;
+ goto fwdl_err;
+ }
+
+ fw_info->h2c_seq = 0;
+ fw_info->rec_seq = 0;
+ rtwdev->mac.rpwm_seq_num = RPWM_SEQ_NUM_MAX;
+ rtwdev->mac.cpwm_seq_num = CPWM_SEQ_NUM_MAX;
+
+ return ret;
+
+fwdl_err:
+ rtw89_fw_dl_fail_dump(rtwdev);
+ return ret;
+}
+
+int rtw89_wait_firmware_completion(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_fw_info *fw = &rtwdev->fw;
+
+ wait_for_completion(&fw->completion);
+ if (!fw->firmware)
+ return -EINVAL;
+
+ return 0;
+}
+
+static void rtw89_load_firmware_cb(const struct firmware *firmware, void *context)
+{
+ struct rtw89_fw_info *fw = context;
+ struct rtw89_dev *rtwdev = fw->rtwdev;
+
+ if (!firmware || !firmware->data) {
+ rtw89_err(rtwdev, "failed to request firmware\n");
+ complete_all(&fw->completion);
+ return;
+ }
+
+ fw->firmware = firmware;
+ complete_all(&fw->completion);
+}
+
+int rtw89_load_firmware(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_fw_info *fw = &rtwdev->fw;
+ const char *fw_name = rtwdev->chip->fw_name;
+ int ret;
+
+ fw->rtwdev = rtwdev;
+ init_completion(&fw->completion);
+
+ ret = request_firmware_nowait(THIS_MODULE, true, fw_name, rtwdev->dev,
+ GFP_KERNEL, fw, rtw89_load_firmware_cb);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to async firmware request\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+void rtw89_unload_firmware(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_fw_info *fw = &rtwdev->fw;
+
+ rtw89_wait_firmware_completion(rtwdev);
+
+ if (fw->firmware)
+ release_firmware(fw->firmware);
+}
+
+#define H2C_CAM_LEN 60
+int rtw89_fw_h2c_cam(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
+{
+ struct sk_buff *skb;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(H2C_CAM_LEN);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for fw dl\n");
+ return -ENOMEM;
+ }
+ skb_put(skb, H2C_CAM_LEN);
+ rtw89_cam_fill_addr_cam_info(rtwdev, rtwvif, skb->data);
+ rtw89_cam_fill_bssid_cam_info(rtwdev, rtwvif, skb->data);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_MAC,
+ H2C_CL_MAC_ADDR_CAM_UPDATE,
+ H2C_FUNC_MAC_ADDR_CAM_UPD, 0, 1,
+ H2C_CAM_LEN);
+
+ if (rtw89_h2c_tx(rtwdev, skb, false)) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ dev_kfree_skb_any(skb);
+
+ return -EBUSY;
+}
+
+#define H2C_BA_CAM_LEN 4
+int rtw89_fw_h2c_ba_cam(struct rtw89_dev *rtwdev, bool valid, u8 macid,
+ struct ieee80211_ampdu_params *params)
+{
+ struct sk_buff *skb;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(H2C_BA_CAM_LEN);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for h2c ba cam\n");
+ return -ENOMEM;
+ }
+ skb_put(skb, H2C_BA_CAM_LEN);
+ SET_BA_CAM_MACID(skb->data, macid);
+ if (!valid)
+ goto end;
+ SET_BA_CAM_VALID(skb->data, valid);
+ SET_BA_CAM_TID(skb->data, params->tid);
+ if (params->buf_size > 64)
+ SET_BA_CAM_BMAP_SIZE(skb->data, 4);
+ else
+ SET_BA_CAM_BMAP_SIZE(skb->data, 0);
+ /* If init req is set, hw will set the ssn */
+ SET_BA_CAM_INIT_REQ(skb->data, 0);
+ SET_BA_CAM_SSN(skb->data, params->ssn);
+
+end:
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_MAC,
+ H2C_CL_BA_CAM,
+ H2C_FUNC_MAC_BA_CAM, 0, 1,
+ H2C_BA_CAM_LEN);
+
+ if (rtw89_h2c_tx(rtwdev, skb, false)) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ dev_kfree_skb_any(skb);
+
+ return -EBUSY;
+}
+
+#define H2C_LOG_CFG_LEN 12
+int rtw89_fw_h2c_fw_log(struct rtw89_dev *rtwdev, bool enable)
+{
+ struct sk_buff *skb;
+ u32 comp = enable ? BIT(RTW89_FW_LOG_COMP_INIT) | BIT(RTW89_FW_LOG_COMP_TASK) |
+ BIT(RTW89_FW_LOG_COMP_PS) | BIT(RTW89_FW_LOG_COMP_ERROR) : 0;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(H2C_LOG_CFG_LEN);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for fw log cfg\n");
+ return -ENOMEM;
+ }
+
+ skb_put(skb, H2C_LOG_CFG_LEN);
+ SET_LOG_CFG_LEVEL(skb->data, RTW89_FW_LOG_LEVEL_SER);
+ SET_LOG_CFG_PATH(skb->data, BIT(RTW89_FW_LOG_LEVEL_C2H));
+ SET_LOG_CFG_COMP(skb->data, comp);
+ SET_LOG_CFG_COMP_EXT(skb->data, 0);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_MAC,
+ H2C_CL_FW_INFO,
+ H2C_FUNC_LOG_CFG, 0, 0,
+ H2C_LOG_CFG_LEN);
+
+ if (rtw89_h2c_tx(rtwdev, skb, false)) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ dev_kfree_skb_any(skb);
+
+ return -EBUSY;
+}
+
+#define H2C_GENERAL_PKT_LEN 6
+#define H2C_GENERAL_PKT_ID_UND 0xff
+int rtw89_fw_h2c_general_pkt(struct rtw89_dev *rtwdev, u8 macid)
+{
+ struct sk_buff *skb;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(H2C_GENERAL_PKT_LEN);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for fw dl\n");
+ return -ENOMEM;
+ }
+ skb_put(skb, H2C_GENERAL_PKT_LEN);
+ SET_GENERAL_PKT_MACID(skb->data, macid);
+ SET_GENERAL_PKT_PROBRSP_ID(skb->data, H2C_GENERAL_PKT_ID_UND);
+ SET_GENERAL_PKT_PSPOLL_ID(skb->data, H2C_GENERAL_PKT_ID_UND);
+ SET_GENERAL_PKT_NULL_ID(skb->data, H2C_GENERAL_PKT_ID_UND);
+ SET_GENERAL_PKT_QOS_NULL_ID(skb->data, H2C_GENERAL_PKT_ID_UND);
+ SET_GENERAL_PKT_CTS2SELF_ID(skb->data, H2C_GENERAL_PKT_ID_UND);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_MAC,
+ H2C_CL_FW_INFO,
+ H2C_FUNC_MAC_GENERAL_PKT, 0, 1,
+ H2C_GENERAL_PKT_LEN);
+
+ if (rtw89_h2c_tx(rtwdev, skb, false)) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ dev_kfree_skb_any(skb);
+
+ return -EBUSY;
+}
+
+#define H2C_LPS_PARM_LEN 8
+int rtw89_fw_h2c_lps_parm(struct rtw89_dev *rtwdev,
+ struct rtw89_lps_parm *lps_param)
+{
+ struct sk_buff *skb;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(H2C_LPS_PARM_LEN);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for fw dl\n");
+ return -ENOMEM;
+ }
+ skb_put(skb, H2C_LPS_PARM_LEN);
+
+ SET_LPS_PARM_MACID(skb->data, lps_param->macid);
+ SET_LPS_PARM_PSMODE(skb->data, lps_param->psmode);
+ SET_LPS_PARM_LASTRPWM(skb->data, lps_param->lastrpwm);
+ SET_LPS_PARM_RLBM(skb->data, 1);
+ SET_LPS_PARM_SMARTPS(skb->data, 1);
+ SET_LPS_PARM_AWAKEINTERVAL(skb->data, 1);
+ SET_LPS_PARM_VOUAPSD(skb->data, 0);
+ SET_LPS_PARM_VIUAPSD(skb->data, 0);
+ SET_LPS_PARM_BEUAPSD(skb->data, 0);
+ SET_LPS_PARM_BKUAPSD(skb->data, 0);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_MAC,
+ H2C_CL_MAC_PS,
+ H2C_FUNC_MAC_LPS_PARM, 0, 1,
+ H2C_LPS_PARM_LEN);
+
+ if (rtw89_h2c_tx(rtwdev, skb, false)) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ dev_kfree_skb_any(skb);
+
+ return -EBUSY;
+}
+
+#define H2C_CMC_TBL_LEN 68
+int rtw89_fw_h2c_default_cmac_tbl(struct rtw89_dev *rtwdev, u8 macid)
+{
+ struct rtw89_hal *hal = &rtwdev->hal;
+ struct sk_buff *skb;
+ u8 ntx_path = hal->antenna_tx ? hal->antenna_tx : RF_B;
+ u8 map_b = hal->antenna_tx == RF_AB ? 1 : 0;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(H2C_CMC_TBL_LEN);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for fw dl\n");
+ return -ENOMEM;
+ }
+ skb_put(skb, H2C_CMC_TBL_LEN);
+ SET_CTRL_INFO_MACID(skb->data, macid);
+ SET_CTRL_INFO_OPERATION(skb->data, 1);
+ SET_CMC_TBL_TXPWR_MODE(skb->data, 0);
+ SET_CMC_TBL_NTX_PATH_EN(skb->data, ntx_path);
+ SET_CMC_TBL_PATH_MAP_A(skb->data, 0);
+ SET_CMC_TBL_PATH_MAP_B(skb->data, map_b);
+ SET_CMC_TBL_PATH_MAP_C(skb->data, 0);
+ SET_CMC_TBL_PATH_MAP_D(skb->data, 0);
+ SET_CMC_TBL_ANTSEL_A(skb->data, 0);
+ SET_CMC_TBL_ANTSEL_B(skb->data, 0);
+ SET_CMC_TBL_ANTSEL_C(skb->data, 0);
+ SET_CMC_TBL_ANTSEL_D(skb->data, 0);
+ SET_CMC_TBL_DOPPLER_CTRL(skb->data, 0);
+ SET_CMC_TBL_TXPWR_TOLERENCE(skb->data, 0);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_MAC, H2C_CL_MAC_FR_EXCHG,
+ H2C_FUNC_MAC_CCTLINFO_UD, 0, 1,
+ H2C_CMC_TBL_LEN);
+
+ if (rtw89_h2c_tx(rtwdev, skb, false)) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ dev_kfree_skb_any(skb);
+
+ return -EBUSY;
+}
+
+static void __get_sta_he_pkt_padding(struct rtw89_dev *rtwdev,
+ struct ieee80211_sta *sta, u8 *pads)
+{
+ bool ppe_th;
+ u8 ppe16, ppe8;
+ u8 nss = min(sta->rx_nss, rtwdev->chip->tx_nss) - 1;
+ u8 ppe_thres_hdr = sta->he_cap.ppe_thres[0];
+ u8 ru_bitmap;
+ u8 n, idx, sh;
+ u16 ppe;
+ int i;
+
+ if (!sta->he_cap.has_he)
+ return;
+
+ ppe_th = FIELD_GET(IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT,
+ sta->he_cap.he_cap_elem.phy_cap_info[6]);
+ if (!ppe_th) {
+ u8 pad;
+
+ pad = FIELD_GET(IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_MASK,
+ sta->he_cap.he_cap_elem.phy_cap_info[9]);
+
+ for (i = 0; i < RTW89_PPE_BW_NUM; i++)
+ pads[i] = pad;
+ }
+
+ ru_bitmap = FIELD_GET(IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK, ppe_thres_hdr);
+ n = hweight8(ru_bitmap);
+ n = 7 + (n * IEEE80211_PPE_THRES_INFO_PPET_SIZE * 2) * nss;
+
+ for (i = 0; i < RTW89_PPE_BW_NUM; i++) {
+ if (!(ru_bitmap & BIT(i))) {
+ pads[i] = 1;
+ continue;
+ }
+
+ idx = n >> 3;
+ sh = n & 7;
+ n += IEEE80211_PPE_THRES_INFO_PPET_SIZE * 2;
+
+ ppe = le16_to_cpu(*((__le16 *)&sta->he_cap.ppe_thres[idx]));
+ ppe16 = (ppe >> sh) & IEEE80211_PPE_THRES_NSS_MASK;
+ sh += IEEE80211_PPE_THRES_INFO_PPET_SIZE;
+ ppe8 = (ppe >> sh) & IEEE80211_PPE_THRES_NSS_MASK;
+
+ if (ppe16 != 7 && ppe8 == 7)
+ pads[i] = 2;
+ else if (ppe8 != 7)
+ pads[i] = 1;
+ else
+ pads[i] = 0;
+ }
+}
+
+int rtw89_fw_h2c_assoc_cmac_tbl(struct rtw89_dev *rtwdev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct rtw89_sta *rtwsta = (struct rtw89_sta *)sta->drv_priv;
+ struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
+ struct sk_buff *skb;
+ u8 pads[RTW89_PPE_BW_NUM];
+
+ memset(pads, 0, sizeof(pads));
+ __get_sta_he_pkt_padding(rtwdev, sta, pads);
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(H2C_CMC_TBL_LEN);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for fw dl\n");
+ return -ENOMEM;
+ }
+ skb_put(skb, H2C_CMC_TBL_LEN);
+ SET_CTRL_INFO_MACID(skb->data, rtwsta->mac_id);
+ SET_CTRL_INFO_OPERATION(skb->data, 1);
+ SET_CMC_TBL_DISRTSFB(skb->data, 1);
+ SET_CMC_TBL_DISDATAFB(skb->data, 1);
+ SET_CMC_TBL_RTS_TXCNT_LMT_SEL(skb->data, 0);
+ SET_CMC_TBL_DATA_TXCNT_LMT_SEL(skb->data, 0);
+ if (vif->type == NL80211_IFTYPE_STATION)
+ SET_CMC_TBL_ULDL(skb->data, 1);
+ else
+ SET_CMC_TBL_ULDL(skb->data, 0);
+ SET_CMC_TBL_MULTI_PORT_ID(skb->data, rtwvif->port);
+ SET_CMC_TBL_NOMINAL_PKT_PADDING(skb->data, pads[RTW89_CHANNEL_WIDTH_20]);
+ SET_CMC_TBL_NOMINAL_PKT_PADDING40(skb->data, pads[RTW89_CHANNEL_WIDTH_40]);
+ SET_CMC_TBL_NOMINAL_PKT_PADDING80(skb->data, pads[RTW89_CHANNEL_WIDTH_80]);
+ SET_CMC_TBL_BSR_QUEUE_SIZE_FORMAT(skb->data, sta->he_cap.has_he);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_MAC, H2C_CL_MAC_FR_EXCHG,
+ H2C_FUNC_MAC_CCTLINFO_UD, 0, 1,
+ H2C_CMC_TBL_LEN);
+
+ if (rtw89_h2c_tx(rtwdev, skb, false)) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ dev_kfree_skb_any(skb);
+
+ return -EBUSY;
+}
+
+int rtw89_fw_h2c_txtime_cmac_tbl(struct rtw89_dev *rtwdev,
+ struct rtw89_sta *rtwsta)
+{
+ struct sk_buff *skb;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(H2C_CMC_TBL_LEN);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for fw dl\n");
+ return -ENOMEM;
+ }
+ skb_put(skb, H2C_CMC_TBL_LEN);
+ SET_CTRL_INFO_MACID(skb->data, rtwsta->mac_id);
+ SET_CTRL_INFO_OPERATION(skb->data, 1);
+ if (rtwsta->cctl_tx_time) {
+ SET_CMC_TBL_AMPDU_TIME_SEL(skb->data, 1);
+ SET_CMC_TBL_AMPDU_MAX_TIME(skb->data, rtwsta->ampdu_max_time);
+ }
+ if (rtwsta->cctl_tx_retry_limit) {
+ SET_CMC_TBL_DATA_TXCNT_LMT_SEL(skb->data, 1);
+ SET_CMC_TBL_DATA_TX_CNT_LMT(skb->data, rtwsta->data_tx_cnt_lmt);
+ }
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_MAC, H2C_CL_MAC_FR_EXCHG,
+ H2C_FUNC_MAC_CCTLINFO_UD, 0, 1,
+ H2C_CMC_TBL_LEN);
+
+ if (rtw89_h2c_tx(rtwdev, skb, false)) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ dev_kfree_skb_any(skb);
+
+ return -EBUSY;
+}
+
+#define H2C_VIF_MAINTAIN_LEN 4
+int rtw89_fw_h2c_vif_maintain(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif,
+ enum rtw89_upd_mode upd_mode)
+{
+ struct sk_buff *skb;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(H2C_VIF_MAINTAIN_LEN);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for h2c join\n");
+ return -ENOMEM;
+ }
+ skb_put(skb, H2C_VIF_MAINTAIN_LEN);
+ SET_FWROLE_MAINTAIN_MACID(skb->data, rtwvif->mac_id);
+ SET_FWROLE_MAINTAIN_SELF_ROLE(skb->data, rtwvif->self_role);
+ SET_FWROLE_MAINTAIN_UPD_MODE(skb->data, upd_mode);
+ SET_FWROLE_MAINTAIN_WIFI_ROLE(skb->data, rtwvif->wifi_role);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_MAC, H2C_CL_MAC_MEDIA_RPT,
+ H2C_FUNC_MAC_FWROLE_MAINTAIN, 0, 1,
+ H2C_VIF_MAINTAIN_LEN);
+
+ if (rtw89_h2c_tx(rtwdev, skb, false)) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ dev_kfree_skb_any(skb);
+
+ return -EBUSY;
+}
+
+#define H2C_JOIN_INFO_LEN 4
+int rtw89_fw_h2c_join_info(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,
+ u8 dis_conn)
+{
+ struct sk_buff *skb;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(H2C_JOIN_INFO_LEN);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for h2c join\n");
+ return -ENOMEM;
+ }
+ skb_put(skb, H2C_JOIN_INFO_LEN);
+ SET_JOININFO_MACID(skb->data, rtwvif->mac_id);
+ SET_JOININFO_OP(skb->data, dis_conn);
+ SET_JOININFO_BAND(skb->data, rtwvif->mac_idx);
+ SET_JOININFO_WMM(skb->data, rtwvif->wmm);
+ SET_JOININFO_TGR(skb->data, rtwvif->trigger);
+ SET_JOININFO_ISHESTA(skb->data, 0);
+ SET_JOININFO_DLBW(skb->data, 0);
+ SET_JOININFO_TF_MAC_PAD(skb->data, 0);
+ SET_JOININFO_DL_T_PE(skb->data, 0);
+ SET_JOININFO_PORT_ID(skb->data, rtwvif->port);
+ SET_JOININFO_NET_TYPE(skb->data, rtwvif->net_type);
+ SET_JOININFO_WIFI_ROLE(skb->data, rtwvif->wifi_role);
+ SET_JOININFO_SELF_ROLE(skb->data, rtwvif->self_role);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_MAC, H2C_CL_MAC_MEDIA_RPT,
+ H2C_FUNC_MAC_JOININFO, 0, 1,
+ H2C_JOIN_INFO_LEN);
+
+ if (rtw89_h2c_tx(rtwdev, skb, false)) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ dev_kfree_skb_any(skb);
+
+ return -EBUSY;
+}
+
+int rtw89_fw_h2c_macid_pause(struct rtw89_dev *rtwdev, u8 sh, u8 grp,
+ bool pause)
+{
+ struct rtw89_fw_macid_pause_grp h2c = {{0}};
+ u8 len = sizeof(struct rtw89_fw_macid_pause_grp);
+ struct sk_buff *skb;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(H2C_JOIN_INFO_LEN);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for h2c join\n");
+ return -ENOMEM;
+ }
+ h2c.mask_grp[grp] = cpu_to_le32(BIT(sh));
+ if (pause)
+ h2c.pause_grp[grp] = cpu_to_le32(BIT(sh));
+ skb_put_data(skb, &h2c, len);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_MAC, H2C_CL_MAC_FW_OFLD,
+ H2C_FUNC_MAC_MACID_PAUSE, 1, 0,
+ len);
+
+ if (rtw89_h2c_tx(rtwdev, skb, false)) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ dev_kfree_skb_any(skb);
+
+ return -EBUSY;
+}
+
+#define H2C_EDCA_LEN 12
+int rtw89_fw_h2c_set_edca(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,
+ u8 ac, u32 val)
+{
+ struct sk_buff *skb;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(H2C_EDCA_LEN);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for h2c edca\n");
+ return -ENOMEM;
+ }
+ skb_put(skb, H2C_EDCA_LEN);
+ RTW89_SET_EDCA_SEL(skb->data, 0);
+ RTW89_SET_EDCA_BAND(skb->data, rtwvif->mac_idx);
+ RTW89_SET_EDCA_WMM(skb->data, 0);
+ RTW89_SET_EDCA_AC(skb->data, ac);
+ RTW89_SET_EDCA_PARAM(skb->data, val);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_MAC, H2C_CL_MAC_FW_OFLD,
+ H2C_FUNC_USR_EDCA, 0, 1,
+ H2C_EDCA_LEN);
+
+ if (rtw89_h2c_tx(rtwdev, skb, false)) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ dev_kfree_skb_any(skb);
+
+ return -EBUSY;
+}
+
+#define H2C_OFLD_CFG_LEN 8
+int rtw89_fw_h2c_set_ofld_cfg(struct rtw89_dev *rtwdev)
+{
+ static const u8 cfg[] = {0x09, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x00};
+ struct sk_buff *skb;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(H2C_OFLD_CFG_LEN);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for h2c ofld\n");
+ return -ENOMEM;
+ }
+ skb_put_data(skb, cfg, H2C_OFLD_CFG_LEN);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_MAC, H2C_CL_MAC_FW_OFLD,
+ H2C_FUNC_OFLD_CFG, 0, 1,
+ H2C_OFLD_CFG_LEN);
+
+ if (rtw89_h2c_tx(rtwdev, skb, false)) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ dev_kfree_skb_any(skb);
+
+ return -EBUSY;
+}
+
+#define H2C_RA_LEN 16
+int rtw89_fw_h2c_ra(struct rtw89_dev *rtwdev, struct rtw89_ra_info *ra, bool csi)
+{
+ struct sk_buff *skb;
+ u8 *cmd;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(H2C_RA_LEN);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for h2c join\n");
+ return -ENOMEM;
+ }
+ skb_put(skb, H2C_RA_LEN);
+ cmd = skb->data;
+ rtw89_debug(rtwdev, RTW89_DBG_RA,
+ "ra cmd msk: %llx ", ra->ra_mask);
+
+ RTW89_SET_FWCMD_RA_MODE(cmd, ra->mode_ctrl);
+ RTW89_SET_FWCMD_RA_BW_CAP(cmd, ra->bw_cap);
+ RTW89_SET_FWCMD_RA_MACID(cmd, ra->macid);
+ RTW89_SET_FWCMD_RA_DCM(cmd, ra->dcm_cap);
+ RTW89_SET_FWCMD_RA_ER(cmd, ra->er_cap);
+ RTW89_SET_FWCMD_RA_INIT_RATE_LV(cmd, ra->init_rate_lv);
+ RTW89_SET_FWCMD_RA_UPD_ALL(cmd, ra->upd_all);
+ RTW89_SET_FWCMD_RA_SGI(cmd, ra->en_sgi);
+ RTW89_SET_FWCMD_RA_LDPC(cmd, ra->ldpc_cap);
+ RTW89_SET_FWCMD_RA_STBC(cmd, ra->stbc_cap);
+ RTW89_SET_FWCMD_RA_SS_NUM(cmd, ra->ss_num);
+ RTW89_SET_FWCMD_RA_GILTF(cmd, ra->giltf);
+ RTW89_SET_FWCMD_RA_UPD_BW_NSS_MASK(cmd, ra->upd_bw_nss_mask);
+ RTW89_SET_FWCMD_RA_UPD_MASK(cmd, ra->upd_mask);
+ RTW89_SET_FWCMD_RA_MASK_0(cmd, FIELD_GET(MASKBYTE0, ra->ra_mask));
+ RTW89_SET_FWCMD_RA_MASK_1(cmd, FIELD_GET(MASKBYTE1, ra->ra_mask));
+ RTW89_SET_FWCMD_RA_MASK_2(cmd, FIELD_GET(MASKBYTE2, ra->ra_mask));
+ RTW89_SET_FWCMD_RA_MASK_3(cmd, FIELD_GET(MASKBYTE3, ra->ra_mask));
+ RTW89_SET_FWCMD_RA_MASK_4(cmd, FIELD_GET(MASKBYTE4, ra->ra_mask));
+
+ if (csi) {
+ RTW89_SET_FWCMD_RA_BFEE_CSI_CTL(cmd, 1);
+ RTW89_SET_FWCMD_RA_BAND_NUM(cmd, ra->band_num);
+ RTW89_SET_FWCMD_RA_CR_TBL_SEL(cmd, ra->cr_tbl_sel);
+ RTW89_SET_FWCMD_RA_FIXED_CSI_RATE_EN(cmd, ra->fixed_csi_rate_en);
+ RTW89_SET_FWCMD_RA_RA_CSI_RATE_EN(cmd, ra->ra_csi_rate_en);
+ RTW89_SET_FWCMD_RA_FIXED_CSI_MCS_SS_IDX(cmd, ra->csi_mcs_ss_idx);
+ RTW89_SET_FWCMD_RA_FIXED_CSI_MODE(cmd, ra->csi_mode);
+ RTW89_SET_FWCMD_RA_FIXED_CSI_GI_LTF(cmd, ra->csi_gi_ltf);
+ RTW89_SET_FWCMD_RA_FIXED_CSI_BW(cmd, ra->csi_bw);
+ }
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_OUTSRC, H2C_CL_OUTSRC_RA,
+ H2C_FUNC_OUTSRC_RA_MACIDCFG, 0, 0,
+ H2C_RA_LEN);
+
+ if (rtw89_h2c_tx(rtwdev, skb, false)) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ dev_kfree_skb_any(skb);
+
+ return -EBUSY;
+}
+
+#define H2C_LEN_CXDRVHDR 2
+#define H2C_LEN_CXDRVINFO_INIT (12 + H2C_LEN_CXDRVHDR)
+int rtw89_fw_h2c_cxdrv_init(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_dm *dm = &btc->dm;
+ struct rtw89_btc_init_info *init_info = &dm->init_info;
+ struct rtw89_btc_module *module = &init_info->module;
+ struct rtw89_btc_ant_info *ant = &module->ant;
+ struct sk_buff *skb;
+ u8 *cmd;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(H2C_LEN_CXDRVINFO_INIT);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for h2c cxdrv_init\n");
+ return -ENOMEM;
+ }
+ skb_put(skb, H2C_LEN_CXDRVINFO_INIT);
+ cmd = skb->data;
+
+ RTW89_SET_FWCMD_CXHDR_TYPE(cmd, CXDRVINFO_INIT);
+ RTW89_SET_FWCMD_CXHDR_LEN(cmd, H2C_LEN_CXDRVINFO_INIT - H2C_LEN_CXDRVHDR);
+
+ RTW89_SET_FWCMD_CXINIT_ANT_TYPE(cmd, ant->type);
+ RTW89_SET_FWCMD_CXINIT_ANT_NUM(cmd, ant->num);
+ RTW89_SET_FWCMD_CXINIT_ANT_ISO(cmd, ant->isolation);
+ RTW89_SET_FWCMD_CXINIT_ANT_POS(cmd, ant->single_pos);
+ RTW89_SET_FWCMD_CXINIT_ANT_DIVERSITY(cmd, ant->diversity);
+
+ RTW89_SET_FWCMD_CXINIT_MOD_RFE(cmd, module->rfe_type);
+ RTW89_SET_FWCMD_CXINIT_MOD_CV(cmd, module->cv);
+ RTW89_SET_FWCMD_CXINIT_MOD_BT_SOLO(cmd, module->bt_solo);
+ RTW89_SET_FWCMD_CXINIT_MOD_BT_POS(cmd, module->bt_pos);
+ RTW89_SET_FWCMD_CXINIT_MOD_SW_TYPE(cmd, module->switch_type);
+
+ RTW89_SET_FWCMD_CXINIT_WL_GCH(cmd, init_info->wl_guard_ch);
+ RTW89_SET_FWCMD_CXINIT_WL_ONLY(cmd, init_info->wl_only);
+ RTW89_SET_FWCMD_CXINIT_WL_INITOK(cmd, init_info->wl_init_ok);
+ RTW89_SET_FWCMD_CXINIT_DBCC_EN(cmd, init_info->dbcc_en);
+ RTW89_SET_FWCMD_CXINIT_CX_OTHER(cmd, init_info->cx_other);
+ RTW89_SET_FWCMD_CXINIT_BT_ONLY(cmd, init_info->bt_only);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_OUTSRC, BTFC_SET,
+ SET_DRV_INFO, 0, 0,
+ H2C_LEN_CXDRVINFO_INIT);
+
+ if (rtw89_h2c_tx(rtwdev, skb, false)) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ dev_kfree_skb_any(skb);
+
+ return -EBUSY;
+}
+
+#define H2C_LEN_CXDRVINFO_ROLE (4 + 12 * RTW89_MAX_HW_PORT_NUM + H2C_LEN_CXDRVHDR)
+int rtw89_fw_h2c_cxdrv_role(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+ struct rtw89_btc_wl_role_info *role_info = &wl->role_info;
+ struct rtw89_btc_wl_role_info_bpos *bpos = &role_info->role_map.role;
+ struct rtw89_btc_wl_active_role *active = role_info->active_role;
+ struct sk_buff *skb;
+ u8 *cmd;
+ int i;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(H2C_LEN_CXDRVINFO_ROLE);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for h2c cxdrv_role\n");
+ return -ENOMEM;
+ }
+ skb_put(skb, H2C_LEN_CXDRVINFO_ROLE);
+ cmd = skb->data;
+
+ RTW89_SET_FWCMD_CXHDR_TYPE(cmd, CXDRVINFO_ROLE);
+ RTW89_SET_FWCMD_CXHDR_LEN(cmd, H2C_LEN_CXDRVINFO_ROLE - H2C_LEN_CXDRVHDR);
+
+ RTW89_SET_FWCMD_CXROLE_CONNECT_CNT(cmd, role_info->connect_cnt);
+ RTW89_SET_FWCMD_CXROLE_LINK_MODE(cmd, role_info->link_mode);
+
+ RTW89_SET_FWCMD_CXROLE_ROLE_NONE(cmd, bpos->none);
+ RTW89_SET_FWCMD_CXROLE_ROLE_STA(cmd, bpos->station);
+ RTW89_SET_FWCMD_CXROLE_ROLE_AP(cmd, bpos->ap);
+ RTW89_SET_FWCMD_CXROLE_ROLE_VAP(cmd, bpos->vap);
+ RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC(cmd, bpos->adhoc);
+ RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC_MASTER(cmd, bpos->adhoc_master);
+ RTW89_SET_FWCMD_CXROLE_ROLE_MESH(cmd, bpos->mesh);
+ RTW89_SET_FWCMD_CXROLE_ROLE_MONITOR(cmd, bpos->moniter);
+ RTW89_SET_FWCMD_CXROLE_ROLE_P2P_DEV(cmd, bpos->p2p_device);
+ RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GC(cmd, bpos->p2p_gc);
+ RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GO(cmd, bpos->p2p_go);
+ RTW89_SET_FWCMD_CXROLE_ROLE_NAN(cmd, bpos->nan);
+
+ for (i = 0; i < RTW89_MAX_HW_PORT_NUM; i++, active++) {
+ RTW89_SET_FWCMD_CXROLE_ACT_CONNECTED(cmd, active->connected, i);
+ RTW89_SET_FWCMD_CXROLE_ACT_PID(cmd, active->pid, i);
+ RTW89_SET_FWCMD_CXROLE_ACT_PHY(cmd, active->phy, i);
+ RTW89_SET_FWCMD_CXROLE_ACT_NOA(cmd, active->noa, i);
+ RTW89_SET_FWCMD_CXROLE_ACT_BAND(cmd, active->band, i);
+ RTW89_SET_FWCMD_CXROLE_ACT_CLIENT_PS(cmd, active->client_ps, i);
+ RTW89_SET_FWCMD_CXROLE_ACT_BW(cmd, active->bw, i);
+ RTW89_SET_FWCMD_CXROLE_ACT_ROLE(cmd, active->role, i);
+ RTW89_SET_FWCMD_CXROLE_ACT_CH(cmd, active->ch, i);
+ RTW89_SET_FWCMD_CXROLE_ACT_TX_LVL(cmd, active->tx_lvl, i);
+ RTW89_SET_FWCMD_CXROLE_ACT_RX_LVL(cmd, active->rx_lvl, i);
+ RTW89_SET_FWCMD_CXROLE_ACT_TX_RATE(cmd, active->tx_rate, i);
+ RTW89_SET_FWCMD_CXROLE_ACT_RX_RATE(cmd, active->rx_rate, i);
+ }
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_OUTSRC, BTFC_SET,
+ SET_DRV_INFO, 0, 0,
+ H2C_LEN_CXDRVINFO_ROLE);
+
+ if (rtw89_h2c_tx(rtwdev, skb, false)) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ dev_kfree_skb_any(skb);
+
+ return -EBUSY;
+}
+
+#define H2C_LEN_CXDRVINFO_CTRL (4 + H2C_LEN_CXDRVHDR)
+int rtw89_fw_h2c_cxdrv_ctrl(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_ctrl *ctrl = &btc->ctrl;
+ struct sk_buff *skb;
+ u8 *cmd;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(H2C_LEN_CXDRVINFO_CTRL);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for h2c cxdrv_ctrl\n");
+ return -ENOMEM;
+ }
+ skb_put(skb, H2C_LEN_CXDRVINFO_CTRL);
+ cmd = skb->data;
+
+ RTW89_SET_FWCMD_CXHDR_TYPE(cmd, CXDRVINFO_CTRL);
+ RTW89_SET_FWCMD_CXHDR_LEN(cmd, H2C_LEN_CXDRVINFO_CTRL - H2C_LEN_CXDRVHDR);
+
+ RTW89_SET_FWCMD_CXCTRL_MANUAL(cmd, ctrl->manual);
+ RTW89_SET_FWCMD_CXCTRL_IGNORE_BT(cmd, ctrl->igno_bt);
+ RTW89_SET_FWCMD_CXCTRL_ALWAYS_FREERUN(cmd, ctrl->always_freerun);
+ RTW89_SET_FWCMD_CXCTRL_TRACE_STEP(cmd, ctrl->trace_step);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_OUTSRC, BTFC_SET,
+ SET_DRV_INFO, 0, 0,
+ H2C_LEN_CXDRVINFO_CTRL);
+
+ if (rtw89_h2c_tx(rtwdev, skb, false)) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ dev_kfree_skb_any(skb);
+
+ return -EBUSY;
+}
+
+#define H2C_LEN_CXDRVINFO_RFK (4 + H2C_LEN_CXDRVHDR)
+int rtw89_fw_h2c_cxdrv_rfk(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_btc *btc = &rtwdev->btc;
+ struct rtw89_btc_wl_info *wl = &btc->cx.wl;
+ struct rtw89_btc_wl_rfk_info *rfk_info = &wl->rfk_info;
+ struct sk_buff *skb;
+ u8 *cmd;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(H2C_LEN_CXDRVINFO_RFK);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for h2c cxdrv_ctrl\n");
+ return -ENOMEM;
+ }
+ skb_put(skb, H2C_LEN_CXDRVINFO_RFK);
+ cmd = skb->data;
+
+ RTW89_SET_FWCMD_CXHDR_TYPE(cmd, CXDRVINFO_RFK);
+ RTW89_SET_FWCMD_CXHDR_LEN(cmd, H2C_LEN_CXDRVINFO_RFK - H2C_LEN_CXDRVHDR);
+
+ RTW89_SET_FWCMD_CXRFK_STATE(cmd, rfk_info->state);
+ RTW89_SET_FWCMD_CXRFK_PATH_MAP(cmd, rfk_info->path_map);
+ RTW89_SET_FWCMD_CXRFK_PHY_MAP(cmd, rfk_info->phy_map);
+ RTW89_SET_FWCMD_CXRFK_BAND(cmd, rfk_info->band);
+ RTW89_SET_FWCMD_CXRFK_TYPE(cmd, rfk_info->type);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_OUTSRC, BTFC_SET,
+ SET_DRV_INFO, 0, 0,
+ H2C_LEN_CXDRVINFO_RFK);
+
+ if (rtw89_h2c_tx(rtwdev, skb, false)) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ dev_kfree_skb_any(skb);
+
+ return -EBUSY;
+}
+
+int rtw89_fw_h2c_rf_reg(struct rtw89_dev *rtwdev,
+ struct rtw89_fw_h2c_rf_reg_info *info,
+ u16 len, u8 page)
+{
+ struct sk_buff *skb;
+ u8 class = info->rf_path == RF_PATH_A ?
+ H2C_CL_OUTSRC_RF_REG_A : H2C_CL_OUTSRC_RF_REG_B;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(len);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for h2c rf reg\n");
+ return -ENOMEM;
+ }
+ skb_put_data(skb, info->rtw89_phy_config_rf_h2c[page], len);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_OUTSRC, class, page, 0, 0,
+ len);
+
+ if (rtw89_h2c_tx(rtwdev, skb, false)) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ dev_kfree_skb_any(skb);
+
+ return -EBUSY;
+}
+
+int rtw89_fw_h2c_raw_with_hdr(struct rtw89_dev *rtwdev,
+ u8 h2c_class, u8 h2c_func, u8 *buf, u16 len,
+ bool rack, bool dack)
+{
+ struct sk_buff *skb;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(len);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for raw with hdr\n");
+ return -ENOMEM;
+ }
+ skb_put_data(skb, buf, len);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_OUTSRC, h2c_class, h2c_func, rack, dack,
+ len);
+
+ if (rtw89_h2c_tx(rtwdev, skb, false)) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ dev_kfree_skb_any(skb);
+
+ return -EBUSY;
+}
+
+int rtw89_fw_h2c_raw(struct rtw89_dev *rtwdev, const u8 *buf, u16 len)
+{
+ struct sk_buff *skb;
+
+ skb = rtw89_fw_h2c_alloc_skb_no_hdr(len);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to alloc skb for h2c raw\n");
+ return -ENOMEM;
+ }
+ skb_put_data(skb, buf, len);
+
+ if (rtw89_h2c_tx(rtwdev, skb, false)) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ dev_kfree_skb_any(skb);
+
+ return -EBUSY;
+}
+
+void rtw89_fw_c2h_irqsafe(struct rtw89_dev *rtwdev, struct sk_buff *c2h)
+{
+ skb_queue_tail(&rtwdev->c2h_queue, c2h);
+ ieee80211_queue_work(rtwdev->hw, &rtwdev->c2h_work);
+}
+
+static void rtw89_fw_c2h_cmd_handle(struct rtw89_dev *rtwdev,
+ struct sk_buff *skb)
+{
+ u8 category = RTW89_GET_C2H_CATEGORY(skb->data);
+ u8 class = RTW89_GET_C2H_CLASS(skb->data);
+ u8 func = RTW89_GET_C2H_FUNC(skb->data);
+ u16 len = RTW89_GET_C2H_LEN(skb->data);
+ bool dump = true;
+
+ if (!test_bit(RTW89_FLAG_RUNNING, rtwdev->flags))
+ return;
+
+ switch (category) {
+ case RTW89_C2H_CAT_TEST:
+ break;
+ case RTW89_C2H_CAT_MAC:
+ rtw89_mac_c2h_handle(rtwdev, skb, len, class, func);
+ if (class == RTW89_MAC_C2H_CLASS_INFO &&
+ func == RTW89_MAC_C2H_FUNC_C2H_LOG)
+ dump = false;
+ break;
+ case RTW89_C2H_CAT_OUTSRC:
+ if (class >= RTW89_PHY_C2H_CLASS_BTC_MIN &&
+ class <= RTW89_PHY_C2H_CLASS_BTC_MAX)
+ rtw89_btc_c2h_handle(rtwdev, skb, len, class, func);
+ else
+ rtw89_phy_c2h_handle(rtwdev, skb, len, class, func);
+ break;
+ }
+
+ if (dump)
+ rtw89_hex_dump(rtwdev, RTW89_DBG_FW, "C2H: ", skb->data, skb->len);
+}
+
+void rtw89_fw_c2h_work(struct work_struct *work)
+{
+ struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
+ c2h_work);
+ struct sk_buff *skb, *tmp;
+
+ skb_queue_walk_safe(&rtwdev->c2h_queue, skb, tmp) {
+ skb_unlink(skb, &rtwdev->c2h_queue);
+ mutex_lock(&rtwdev->mutex);
+ rtw89_fw_c2h_cmd_handle(rtwdev, skb);
+ mutex_unlock(&rtwdev->mutex);
+ dev_kfree_skb_any(skb);
+ }
+}
+
+static int rtw89_fw_write_h2c_reg(struct rtw89_dev *rtwdev,
+ struct rtw89_mac_h2c_info *info)
+{
+ static const u32 h2c_reg[RTW89_H2CREG_MAX] = {
+ R_AX_H2CREG_DATA0, R_AX_H2CREG_DATA1,
+ R_AX_H2CREG_DATA2, R_AX_H2CREG_DATA3
+ };
+ u8 i, val, len;
+ int ret;
+
+ ret = read_poll_timeout(rtw89_read8, val, val == 0, 1000, 5000, false,
+ rtwdev, R_AX_H2CREG_CTRL);
+ if (ret) {
+ rtw89_warn(rtwdev, "FW does not process h2c registers\n");
+ return ret;
+ }
+
+ len = DIV_ROUND_UP(info->content_len + RTW89_H2CREG_HDR_LEN,
+ sizeof(info->h2creg[0]));
+
+ RTW89_SET_H2CREG_HDR_FUNC(&info->h2creg[0], info->id);
+ RTW89_SET_H2CREG_HDR_LEN(&info->h2creg[0], len);
+ for (i = 0; i < RTW89_H2CREG_MAX; i++)
+ rtw89_write32(rtwdev, h2c_reg[i], info->h2creg[i]);
+
+ rtw89_write8(rtwdev, R_AX_H2CREG_CTRL, B_AX_H2CREG_TRIGGER);
+
+ return 0;
+}
+
+static int rtw89_fw_read_c2h_reg(struct rtw89_dev *rtwdev,
+ struct rtw89_mac_c2h_info *info)
+{
+ static const u32 c2h_reg[RTW89_C2HREG_MAX] = {
+ R_AX_C2HREG_DATA0, R_AX_C2HREG_DATA1,
+ R_AX_C2HREG_DATA2, R_AX_C2HREG_DATA3
+ };
+ u32 ret;
+ u8 i, val;
+
+ info->id = RTW89_FWCMD_C2HREG_FUNC_NULL;
+
+ ret = read_poll_timeout_atomic(rtw89_read8, val, val, 1,
+ RTW89_C2H_TIMEOUT, false, rtwdev,
+ R_AX_C2HREG_CTRL);
+ if (ret) {
+ rtw89_warn(rtwdev, "c2h reg timeout\n");
+ return ret;
+ }
+
+ for (i = 0; i < RTW89_C2HREG_MAX; i++)
+ info->c2hreg[i] = rtw89_read32(rtwdev, c2h_reg[i]);
+
+ rtw89_write8(rtwdev, R_AX_C2HREG_CTRL, 0);
+
+ info->id = RTW89_GET_C2H_HDR_FUNC(*info->c2hreg);
+ info->content_len = (RTW89_GET_C2H_HDR_LEN(*info->c2hreg) << 2) -
+ RTW89_C2HREG_HDR_LEN;
+
+ return 0;
+}
+
+int rtw89_fw_msg_reg(struct rtw89_dev *rtwdev,
+ struct rtw89_mac_h2c_info *h2c_info,
+ struct rtw89_mac_c2h_info *c2h_info)
+{
+ u32 ret;
+
+ lockdep_assert_held(&rtwdev->mutex);
+
+ if (!h2c_info && !c2h_info)
+ return -EINVAL;
+
+ if (!h2c_info)
+ goto recv_c2h;
+
+ ret = rtw89_fw_write_h2c_reg(rtwdev, h2c_info);
+ if (ret)
+ return ret;
+
+recv_c2h:
+ if (!c2h_info)
+ return 0;
+
+ ret = rtw89_fw_read_c2h_reg(rtwdev, c2h_info);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+void rtw89_fw_st_dbg_dump(struct rtw89_dev *rtwdev)
+{
+ if (!test_bit(RTW89_FLAG_POWERON, rtwdev->flags)) {
+ rtw89_err(rtwdev, "[ERR]pwr is off\n");
+ return;
+ }
+
+ rtw89_info(rtwdev, "FW status = 0x%x\n", rtw89_read32(rtwdev, R_AX_UDM0));
+ rtw89_info(rtwdev, "FW BADADDR = 0x%x\n", rtw89_read32(rtwdev, R_AX_UDM1));
+ rtw89_info(rtwdev, "FW EPC/RA = 0x%x\n", rtw89_read32(rtwdev, R_AX_UDM2));
+ rtw89_info(rtwdev, "FW MISC = 0x%x\n", rtw89_read32(rtwdev, R_AX_UDM3));
+ rtw89_info(rtwdev, "R_AX_HALT_C2H = 0x%x\n",
+ rtw89_read32(rtwdev, R_AX_HALT_C2H));
+ rtw89_info(rtwdev, "R_AX_SER_DBG_INFO = 0x%x\n",
+ rtw89_read32(rtwdev, R_AX_SER_DBG_INFO));
+
+ rtw89_fw_prog_cnt_dump(rtwdev);
+}
diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h
new file mode 100644
index 000000000000..9f7c452d5e79
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw89/fw.h
@@ -0,0 +1,1378 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/* Copyright(c) 2019-2020 Realtek Corporation
+ */
+
+#ifndef __RTW89_FW_H__
+#define __RTW89_FW_H__
+
+#include "core.h"
+
+enum rtw89_fw_dl_status {
+ RTW89_FWDL_INITIAL_STATE = 0,
+ RTW89_FWDL_FWDL_ONGOING = 1,
+ RTW89_FWDL_CHECKSUM_FAIL = 2,
+ RTW89_FWDL_SECURITY_FAIL = 3,
+ RTW89_FWDL_CV_NOT_MATCH = 4,
+ RTW89_FWDL_RSVD0 = 5,
+ RTW89_FWDL_WCPU_FWDL_RDY = 6,
+ RTW89_FWDL_WCPU_FW_INIT_RDY = 7
+};
+
+#define RTW89_GET_C2H_HDR_FUNC(info) \
+ u32_get_bits(info, GENMASK(6, 0))
+#define RTW89_GET_C2H_HDR_LEN(info) \
+ u32_get_bits(info, GENMASK(11, 8))
+
+#define RTW89_SET_H2CREG_HDR_FUNC(info, val) \
+ u32p_replace_bits(info, val, GENMASK(6, 0))
+#define RTW89_SET_H2CREG_HDR_LEN(info, val) \
+ u32p_replace_bits(info, val, GENMASK(11, 8))
+
+#define RTW89_H2CREG_MAX 4
+#define RTW89_C2HREG_MAX 4
+#define RTW89_C2HREG_HDR_LEN 2
+#define RTW89_H2CREG_HDR_LEN 2
+#define RTW89_C2H_TIMEOUT 1000000
+struct rtw89_mac_c2h_info {
+ u8 id;
+ u8 content_len;
+ u32 c2hreg[RTW89_C2HREG_MAX];
+};
+
+struct rtw89_mac_h2c_info {
+ u8 id;
+ u8 content_len;
+ u32 h2creg[RTW89_H2CREG_MAX];
+};
+
+enum rtw89_mac_h2c_type {
+ RTW89_FWCMD_H2CREG_FUNC_H2CREG_LB = 0,
+ RTW89_FWCMD_H2CREG_FUNC_CNSL_CMD,
+ RTW89_FWCMD_H2CREG_FUNC_FWERR,
+ RTW89_FWCMD_H2CREG_FUNC_GET_FEATURE,
+ RTW89_FWCMD_H2CREG_FUNC_GETPKT_INFORM,
+ RTW89_FWCMD_H2CREG_FUNC_SCH_TX_EN
+};
+
+enum rtw89_mac_c2h_type {
+ RTW89_FWCMD_C2HREG_FUNC_C2HREG_LB = 0,
+ RTW89_FWCMD_C2HREG_FUNC_ERR_RPT,
+ RTW89_FWCMD_C2HREG_FUNC_ERR_MSG,
+ RTW89_FWCMD_C2HREG_FUNC_PHY_CAP,
+ RTW89_FWCMD_C2HREG_FUNC_TX_PAUSE_RPT,
+ RTW89_FWCMD_C2HREG_FUNC_NULL = 0xFF
+};
+
+enum rtw89_fw_c2h_category {
+ RTW89_C2H_CAT_TEST,
+ RTW89_C2H_CAT_MAC,
+ RTW89_C2H_CAT_OUTSRC,
+};
+
+enum rtw89_fw_log_level {
+ RTW89_FW_LOG_LEVEL_OFF,
+ RTW89_FW_LOG_LEVEL_CRT,
+ RTW89_FW_LOG_LEVEL_SER,
+ RTW89_FW_LOG_LEVEL_WARN,
+ RTW89_FW_LOG_LEVEL_LOUD,
+ RTW89_FW_LOG_LEVEL_TR,
+};
+
+enum rtw89_fw_log_path {
+ RTW89_FW_LOG_LEVEL_UART,
+ RTW89_FW_LOG_LEVEL_C2H,
+ RTW89_FW_LOG_LEVEL_SNI,
+};
+
+enum rtw89_fw_log_comp {
+ RTW89_FW_LOG_COMP_VER,
+ RTW89_FW_LOG_COMP_INIT,
+ RTW89_FW_LOG_COMP_TASK,
+ RTW89_FW_LOG_COMP_CNS,
+ RTW89_FW_LOG_COMP_H2C,
+ RTW89_FW_LOG_COMP_C2H,
+ RTW89_FW_LOG_COMP_TX,
+ RTW89_FW_LOG_COMP_RX,
+ RTW89_FW_LOG_COMP_IPSEC,
+ RTW89_FW_LOG_COMP_TIMER,
+ RTW89_FW_LOG_COMP_DBGPKT,
+ RTW89_FW_LOG_COMP_PS,
+ RTW89_FW_LOG_COMP_ERROR,
+ RTW89_FW_LOG_COMP_WOWLAN,
+ RTW89_FW_LOG_COMP_SECURE_BOOT,
+ RTW89_FW_LOG_COMP_BTC,
+ RTW89_FW_LOG_COMP_BB,
+ RTW89_FW_LOG_COMP_TWT,
+ RTW89_FW_LOG_COMP_RF,
+ RTW89_FW_LOG_COMP_MCC = 20,
+};
+
+#define FWDL_SECTION_MAX_NUM 10
+#define FWDL_SECTION_CHKSUM_LEN 8
+#define FWDL_SECTION_PER_PKT_LEN 2020
+
+struct rtw89_fw_hdr_section_info {
+ u8 redl;
+ const u8 *addr;
+ u32 len;
+ u32 dladdr;
+};
+
+struct rtw89_fw_bin_info {
+ u8 section_num;
+ u32 hdr_len;
+ struct rtw89_fw_hdr_section_info section_info[FWDL_SECTION_MAX_NUM];
+};
+
+struct rtw89_fw_macid_pause_grp {
+ __le32 pause_grp[4];
+ __le32 mask_grp[4];
+} __packed;
+
+struct rtw89_h2creg_sch_tx_en {
+ u8 func:7;
+ u8 ack:1;
+ u8 total_len:4;
+ u8 seq_num:4;
+ u16 tx_en:16;
+ u16 mask:16;
+ u8 band:1;
+ u16 rsvd:15;
+} __packed;
+
+#define RTW89_SET_FWCMD_RA_IS_DIS(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x00, BIT(0))
+#define RTW89_SET_FWCMD_RA_MODE(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x00, GENMASK(5, 1))
+#define RTW89_SET_FWCMD_RA_BW_CAP(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x00, GENMASK(7, 6))
+#define RTW89_SET_FWCMD_RA_MACID(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x00, GENMASK(15, 8))
+#define RTW89_SET_FWCMD_RA_DCM(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x00, BIT(16))
+#define RTW89_SET_FWCMD_RA_ER(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x00, BIT(17))
+#define RTW89_SET_FWCMD_RA_INIT_RATE_LV(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x00, GENMASK(19, 18))
+#define RTW89_SET_FWCMD_RA_UPD_ALL(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x00, BIT(20))
+#define RTW89_SET_FWCMD_RA_SGI(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x00, BIT(21))
+#define RTW89_SET_FWCMD_RA_LDPC(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x00, BIT(22))
+#define RTW89_SET_FWCMD_RA_STBC(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x00, BIT(23))
+#define RTW89_SET_FWCMD_RA_SS_NUM(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x00, GENMASK(26, 24))
+#define RTW89_SET_FWCMD_RA_GILTF(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x00, GENMASK(29, 27))
+#define RTW89_SET_FWCMD_RA_UPD_BW_NSS_MASK(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x00, BIT(30))
+#define RTW89_SET_FWCMD_RA_UPD_MASK(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x00, BIT(31))
+#define RTW89_SET_FWCMD_RA_MASK_0(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x01, GENMASK(7, 0))
+#define RTW89_SET_FWCMD_RA_MASK_1(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x01, GENMASK(15, 8))
+#define RTW89_SET_FWCMD_RA_MASK_2(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x01, GENMASK(23, 16))
+#define RTW89_SET_FWCMD_RA_MASK_3(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x01, GENMASK(31, 24))
+#define RTW89_SET_FWCMD_RA_MASK_4(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x02, GENMASK(7, 0))
+#define RTW89_SET_FWCMD_RA_BFEE_CSI_CTL(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x02, BIT(31))
+#define RTW89_SET_FWCMD_RA_BAND_NUM(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x03, GENMASK(7, 0))
+#define RTW89_SET_FWCMD_RA_RA_CSI_RATE_EN(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x03, BIT(8))
+#define RTW89_SET_FWCMD_RA_FIXED_CSI_RATE_EN(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x03, BIT(9))
+#define RTW89_SET_FWCMD_RA_CR_TBL_SEL(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x03, BIT(10))
+#define RTW89_SET_FWCMD_RA_FIXED_CSI_MCS_SS_IDX(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x03, GENMASK(23, 16))
+#define RTW89_SET_FWCMD_RA_FIXED_CSI_MODE(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x03, GENMASK(25, 24))
+#define RTW89_SET_FWCMD_RA_FIXED_CSI_GI_LTF(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x03, GENMASK(28, 26))
+#define RTW89_SET_FWCMD_RA_FIXED_CSI_BW(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x03, GENMASK(31, 29))
+
+#define RTW89_SET_FWCMD_SEC_IDX(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x00, GENMASK(7, 0))
+#define RTW89_SET_FWCMD_SEC_OFFSET(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x00, GENMASK(15, 8))
+#define RTW89_SET_FWCMD_SEC_LEN(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x00, GENMASK(23, 16))
+#define RTW89_SET_FWCMD_SEC_TYPE(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x01, GENMASK(3, 0))
+#define RTW89_SET_FWCMD_SEC_EXT_KEY(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x01, BIT(4))
+#define RTW89_SET_FWCMD_SEC_SPP_MODE(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x01, BIT(5))
+#define RTW89_SET_FWCMD_SEC_KEY0(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x02, GENMASK(31, 0))
+#define RTW89_SET_FWCMD_SEC_KEY1(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x03, GENMASK(31, 0))
+#define RTW89_SET_FWCMD_SEC_KEY2(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x04, GENMASK(31, 0))
+#define RTW89_SET_FWCMD_SEC_KEY3(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x05, GENMASK(31, 0))
+
+#define RTW89_SET_EDCA_SEL(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x00, GENMASK(1, 0))
+#define RTW89_SET_EDCA_BAND(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x00, BIT(3))
+#define RTW89_SET_EDCA_WMM(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x00, BIT(4))
+#define RTW89_SET_EDCA_AC(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x00, GENMASK(6, 5))
+#define RTW89_SET_EDCA_PARAM(cmd, val) \
+ RTW89_SET_FWCMD(cmd, val, 0x01, GENMASK(31, 0))
+#define FW_EDCA_PARAM_TXOPLMT_MSK GENMASK(26, 16)
+#define FW_EDCA_PARAM_CWMAX_MSK GENMASK(15, 12)
+#define FW_EDCA_PARAM_CWMIN_MSK GENMASK(11, 8)
+#define FW_EDCA_PARAM_AIFS_MSK GENMASK(7, 0)
+
+#define GET_FWSECTION_HDR_SEC_SIZE(fwhdr) \
+ le32_get_bits(*((__le32 *)(fwhdr) + 1), GENMASK(23, 0))
+#define GET_FWSECTION_HDR_CHECKSUM(fwhdr) \
+ le32_get_bits(*((__le32 *)(fwhdr) + 1), BIT(28))
+#define GET_FWSECTION_HDR_REDL(fwhdr) \
+ le32_get_bits(*((__le32 *)(fwhdr) + 1), BIT(29))
+#define GET_FWSECTION_HDR_DL_ADDR(fwhdr) \
+ le32_get_bits(*((__le32 *)(fwhdr)), GENMASK(31, 0))
+
+#define GET_FW_HDR_MAJOR_VERSION(fwhdr) \
+ le32_get_bits(*((__le32 *)(fwhdr) + 1), GENMASK(7, 0))
+#define GET_FW_HDR_MINOR_VERSION(fwhdr) \
+ le32_get_bits(*((__le32 *)(fwhdr) + 1), GENMASK(15, 8))
+#define GET_FW_HDR_SUBVERSION(fwhdr) \
+ le32_get_bits(*((__le32 *)(fwhdr) + 1), GENMASK(23, 16))
+#define GET_FW_HDR_SUBINDEX(fwhdr) \
+ le32_get_bits(*((__le32 *)(fwhdr) + 1), GENMASK(31, 24))
+#define GET_FW_HDR_MONTH(fwhdr) \
+ le32_get_bits(*((__le32 *)(fwhdr) + 4), GENMASK(7, 0))
+#define GET_FW_HDR_DATE(fwhdr) \
+ le32_get_bits(*((__le32 *)(fwhdr) + 4), GENMASK(15, 8))
+#define GET_FW_HDR_HOUR(fwhdr) \
+ le32_get_bits(*((__le32 *)(fwhdr) + 4), GENMASK(23, 16))
+#define GET_FW_HDR_MIN(fwhdr) \
+ le32_get_bits(*((__le32 *)(fwhdr) + 4), GENMASK(31, 24))
+#define GET_FW_HDR_YEAR(fwhdr) \
+ le32_get_bits(*((__le32 *)(fwhdr) + 5), GENMASK(31, 0))
+#define GET_FW_HDR_SEC_NUM(fwhdr) \
+ le32_get_bits(*((__le32 *)(fwhdr) + 6), GENMASK(15, 8))
+#define GET_FW_HDR_CMD_VERSERION(fwhdr) \
+ le32_get_bits(*((__le32 *)(fwhdr) + 7), GENMASK(31, 24))
+#define SET_FW_HDR_PART_SIZE(fwhdr, val) \
+ le32p_replace_bits((__le32 *)(fwhdr) + 7, val, GENMASK(15, 0))
+
+#define SET_CTRL_INFO_MACID(table, val) \
+ le32p_replace_bits((__le32 *)(table) + 0, val, GENMASK(6, 0))
+#define SET_CTRL_INFO_OPERATION(table, val) \
+ le32p_replace_bits((__le32 *)(table) + 0, val, BIT(7))
+#define SET_CMC_TBL_MASK_DATARATE GENMASK(8, 0)
+#define SET_CMC_TBL_DATARATE(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 1, val, GENMASK(8, 0)); \
+ le32p_replace_bits((__le32 *)(table) + 9, SET_CMC_TBL_MASK_DATARATE, \
+ GENMASK(8, 0)); \
+} while (0)
+#define SET_CMC_TBL_MASK_FORCE_TXOP BIT(0)
+#define SET_CMC_TBL_FORCE_TXOP(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 1, val, BIT(9)); \
+ le32p_replace_bits((__le32 *)(table) + 9, SET_CMC_TBL_MASK_FORCE_TXOP, \
+ BIT(9)); \
+} while (0)
+#define SET_CMC_TBL_MASK_DATA_BW GENMASK(1, 0)
+#define SET_CMC_TBL_DATA_BW(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 1, val, GENMASK(11, 10)); \
+ le32p_replace_bits((__le32 *)(table) + 9, SET_CMC_TBL_MASK_DATA_BW, \
+ GENMASK(11, 10)); \
+} while (0)
+#define SET_CMC_TBL_MASK_DATA_GI_LTF GENMASK(2, 0)
+#define SET_CMC_TBL_DATA_GI_LTF(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 1, val, GENMASK(14, 12)); \
+ le32p_replace_bits((__le32 *)(table) + 9, SET_CMC_TBL_MASK_DATA_GI_LTF, \
+ GENMASK(14, 12)); \
+} while (0)
+#define SET_CMC_TBL_MASK_DARF_TC_INDEX BIT(0)
+#define SET_CMC_TBL_DARF_TC_INDEX(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 1, val, BIT(15)); \
+ le32p_replace_bits((__le32 *)(table) + 9, SET_CMC_TBL_MASK_DARF_TC_INDEX, \
+ BIT(15)); \
+} while (0)
+#define SET_CMC_TBL_MASK_ARFR_CTRL GENMASK(3, 0)
+#define SET_CMC_TBL_ARFR_CTRL(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 1, val, GENMASK(19, 16)); \
+ le32p_replace_bits((__le32 *)(table) + 9, SET_CMC_TBL_MASK_ARFR_CTRL, \
+ GENMASK(19, 16)); \
+} while (0)
+#define SET_CMC_TBL_MASK_ACQ_RPT_EN BIT(0)
+#define SET_CMC_TBL_ACQ_RPT_EN(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 1, val, BIT(20)); \
+ le32p_replace_bits((__le32 *)(table) + 9, SET_CMC_TBL_MASK_ACQ_RPT_EN, \
+ BIT(20)); \
+} while (0)
+#define SET_CMC_TBL_MASK_MGQ_RPT_EN BIT(0)
+#define SET_CMC_TBL_MGQ_RPT_EN(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 1, val, BIT(21)); \
+ le32p_replace_bits((__le32 *)(table) + 9, SET_CMC_TBL_MASK_MGQ_RPT_EN, \
+ BIT(21)); \
+} while (0)
+#define SET_CMC_TBL_MASK_ULQ_RPT_EN BIT(0)
+#define SET_CMC_TBL_ULQ_RPT_EN(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 1, val, BIT(22)); \
+ le32p_replace_bits((__le32 *)(table) + 9, SET_CMC_TBL_MASK_ULQ_RPT_EN, \
+ BIT(22)); \
+} while (0)
+#define SET_CMC_TBL_MASK_TWTQ_RPT_EN BIT(0)
+#define SET_CMC_TBL_TWTQ_RPT_EN(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 1, val, BIT(23)); \
+ le32p_replace_bits((__le32 *)(table) + 9, SET_CMC_TBL_MASK_TWTQ_RPT_EN, \
+ BIT(23)); \
+} while (0)
+#define SET_CMC_TBL_MASK_DISRTSFB BIT(0)
+#define SET_CMC_TBL_DISRTSFB(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 1, val, BIT(25)); \
+ le32p_replace_bits((__le32 *)(table) + 9, SET_CMC_TBL_MASK_DISRTSFB, \
+ BIT(25)); \
+} while (0)
+#define SET_CMC_TBL_MASK_DISDATAFB BIT(0)
+#define SET_CMC_TBL_DISDATAFB(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 1, val, BIT(26)); \
+ le32p_replace_bits((__le32 *)(table) + 9, SET_CMC_TBL_MASK_DISDATAFB, \
+ BIT(26)); \
+} while (0)
+#define SET_CMC_TBL_MASK_TRYRATE BIT(0)
+#define SET_CMC_TBL_TRYRATE(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 1, val, BIT(27)); \
+ le32p_replace_bits((__le32 *)(table) + 9, SET_CMC_TBL_MASK_TRYRATE, \
+ BIT(27)); \
+} while (0)
+#define SET_CMC_TBL_MASK_AMPDU_DENSITY GENMASK(3, 0)
+#define SET_CMC_TBL_AMPDU_DENSITY(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 1, val, GENMASK(31, 28)); \
+ le32p_replace_bits((__le32 *)(table) + 9, SET_CMC_TBL_MASK_AMPDU_DENSITY, \
+ GENMASK(31, 28)); \
+} while (0)
+#define SET_CMC_TBL_MASK_DATA_RTY_LOWEST_RATE GENMASK(8, 0)
+#define SET_CMC_TBL_DATA_RTY_LOWEST_RATE(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 2, val, GENMASK(8, 0)); \
+ le32p_replace_bits((__le32 *)(table) + 10, SET_CMC_TBL_MASK_DATA_RTY_LOWEST_RATE, \
+ GENMASK(8, 0)); \
+} while (0)
+#define SET_CMC_TBL_MASK_AMPDU_TIME_SEL BIT(0)
+#define SET_CMC_TBL_AMPDU_TIME_SEL(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 2, val, BIT(9)); \
+ le32p_replace_bits((__le32 *)(table) + 10, SET_CMC_TBL_MASK_AMPDU_TIME_SEL, \
+ BIT(9)); \
+} while (0)
+#define SET_CMC_TBL_MASK_AMPDU_LEN_SEL BIT(0)
+#define SET_CMC_TBL_AMPDU_LEN_SEL(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 2, val, BIT(10)); \
+ le32p_replace_bits((__le32 *)(table) + 10, SET_CMC_TBL_MASK_AMPDU_LEN_SEL, \
+ BIT(10)); \
+} while (0)
+#define SET_CMC_TBL_MASK_RTS_TXCNT_LMT_SEL BIT(0)
+#define SET_CMC_TBL_RTS_TXCNT_LMT_SEL(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 2, val, BIT(11)); \
+ le32p_replace_bits((__le32 *)(table) + 10, SET_CMC_TBL_MASK_RTS_TXCNT_LMT_SEL, \
+ BIT(11)); \
+} while (0)
+#define SET_CMC_TBL_MASK_RTS_TXCNT_LMT GENMASK(3, 0)
+#define SET_CMC_TBL_RTS_TXCNT_LMT(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 2, val, GENMASK(15, 12)); \
+ le32p_replace_bits((__le32 *)(table) + 10, SET_CMC_TBL_MASK_RTS_TXCNT_LMT, \
+ GENMASK(15, 12)); \
+} while (0)
+#define SET_CMC_TBL_MASK_RTSRATE GENMASK(8, 0)
+#define SET_CMC_TBL_RTSRATE(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 2, val, GENMASK(24, 16)); \
+ le32p_replace_bits((__le32 *)(table) + 10, SET_CMC_TBL_MASK_RTSRATE, \
+ GENMASK(24, 16)); \
+} while (0)
+#define SET_CMC_TBL_MASK_VCS_STBC BIT(0)
+#define SET_CMC_TBL_VCS_STBC(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 2, val, BIT(27)); \
+ le32p_replace_bits((__le32 *)(table) + 10, SET_CMC_TBL_MASK_VCS_STBC, \
+ BIT(27)); \
+} while (0)
+#define SET_CMC_TBL_MASK_RTS_RTY_LOWEST_RATE GENMASK(3, 0)
+#define SET_CMC_TBL_RTS_RTY_LOWEST_RATE(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 2, val, GENMASK(31, 28)); \
+ le32p_replace_bits((__le32 *)(table) + 10, SET_CMC_TBL_MASK_RTS_RTY_LOWEST_RATE, \
+ GENMASK(31, 28)); \
+} while (0)
+#define SET_CMC_TBL_MASK_DATA_TX_CNT_LMT GENMASK(5, 0)
+#define SET_CMC_TBL_DATA_TX_CNT_LMT(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 3, val, GENMASK(5, 0)); \
+ le32p_replace_bits((__le32 *)(table) + 11, SET_CMC_TBL_MASK_DATA_TX_CNT_LMT, \
+ GENMASK(5, 0)); \
+} while (0)
+#define SET_CMC_TBL_MASK_DATA_TXCNT_LMT_SEL BIT(0)
+#define SET_CMC_TBL_DATA_TXCNT_LMT_SEL(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 3, val, BIT(6)); \
+ le32p_replace_bits((__le32 *)(table) + 11, SET_CMC_TBL_MASK_DATA_TXCNT_LMT_SEL, \
+ BIT(6)); \
+} while (0)
+#define SET_CMC_TBL_MASK_MAX_AGG_NUM_SEL BIT(0)
+#define SET_CMC_TBL_MAX_AGG_NUM_SEL(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 3, val, BIT(7)); \
+ le32p_replace_bits((__le32 *)(table) + 11, SET_CMC_TBL_MASK_MAX_AGG_NUM_SEL, \
+ BIT(7)); \
+} while (0)
+#define SET_CMC_TBL_MASK_RTS_EN BIT(0)
+#define SET_CMC_TBL_RTS_EN(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 3, val, BIT(8)); \
+ le32p_replace_bits((__le32 *)(table) + 11, SET_CMC_TBL_MASK_RTS_EN, \
+ BIT(8)); \
+} while (0)
+#define SET_CMC_TBL_MASK_CTS2SELF_EN BIT(0)
+#define SET_CMC_TBL_CTS2SELF_EN(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 3, val, BIT(9)); \
+ le32p_replace_bits((__le32 *)(table) + 11, SET_CMC_TBL_MASK_CTS2SELF_EN, \
+ BIT(9)); \
+} while (0)
+#define SET_CMC_TBL_MASK_CCA_RTS GENMASK(1, 0)
+#define SET_CMC_TBL_CCA_RTS(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 3, val, GENMASK(11, 10)); \
+ le32p_replace_bits((__le32 *)(table) + 11, SET_CMC_TBL_MASK_CCA_RTS, \
+ GENMASK(11, 10)); \
+} while (0)
+#define SET_CMC_TBL_MASK_HW_RTS_EN BIT(0)
+#define SET_CMC_TBL_HW_RTS_EN(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 3, val, BIT(12)); \
+ le32p_replace_bits((__le32 *)(table) + 11, SET_CMC_TBL_MASK_HW_RTS_EN, \
+ BIT(12)); \
+} while (0)
+#define SET_CMC_TBL_MASK_RTS_DROP_DATA_MODE GENMASK(1, 0)
+#define SET_CMC_TBL_RTS_DROP_DATA_MODE(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 3, val, GENMASK(14, 13)); \
+ le32p_replace_bits((__le32 *)(table) + 11, SET_CMC_TBL_MASK_RTS_DROP_DATA_MODE, \
+ GENMASK(14, 13)); \
+} while (0)
+#define SET_CMC_TBL_MASK_AMPDU_MAX_LEN GENMASK(10, 0)
+#define SET_CMC_TBL_AMPDU_MAX_LEN(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 3, val, GENMASK(26, 16)); \
+ le32p_replace_bits((__le32 *)(table) + 11, SET_CMC_TBL_MASK_AMPDU_MAX_LEN, \
+ GENMASK(26, 16)); \
+} while (0)
+#define SET_CMC_TBL_MASK_UL_MU_DIS BIT(0)
+#define SET_CMC_TBL_UL_MU_DIS(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 3, val, BIT(27)); \
+ le32p_replace_bits((__le32 *)(table) + 11, SET_CMC_TBL_MASK_UL_MU_DIS, \
+ BIT(27)); \
+} while (0)
+#define SET_CMC_TBL_MASK_AMPDU_MAX_TIME GENMASK(3, 0)
+#define SET_CMC_TBL_AMPDU_MAX_TIME(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 3, val, GENMASK(31, 28)); \
+ le32p_replace_bits((__le32 *)(table) + 11, SET_CMC_TBL_MASK_AMPDU_MAX_TIME, \
+ GENMASK(31, 28)); \
+} while (0)
+#define SET_CMC_TBL_MASK_MAX_AGG_NUM GENMASK(7, 0)
+#define SET_CMC_TBL_MAX_AGG_NUM(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 4, val, GENMASK(7, 0)); \
+ le32p_replace_bits((__le32 *)(table) + 12, SET_CMC_TBL_MASK_MAX_AGG_NUM, \
+ GENMASK(7, 0)); \
+} while (0)
+#define SET_CMC_TBL_MASK_BA_BMAP GENMASK(1, 0)
+#define SET_CMC_TBL_BA_BMAP(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 4, val, GENMASK(9, 8)); \
+ le32p_replace_bits((__le32 *)(table) + 12, SET_CMC_TBL_MASK_BA_BMAP, \
+ GENMASK(9, 8)); \
+} while (0)
+#define SET_CMC_TBL_MASK_VO_LFTIME_SEL GENMASK(2, 0)
+#define SET_CMC_TBL_VO_LFTIME_SEL(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 4, val, GENMASK(18, 16)); \
+ le32p_replace_bits((__le32 *)(table) + 12, SET_CMC_TBL_MASK_VO_LFTIME_SEL, \
+ GENMASK(18, 16)); \
+} while (0)
+#define SET_CMC_TBL_MASK_VI_LFTIME_SEL GENMASK(2, 0)
+#define SET_CMC_TBL_VI_LFTIME_SEL(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 4, val, GENMASK(21, 19)); \
+ le32p_replace_bits((__le32 *)(table) + 12, SET_CMC_TBL_MASK_VI_LFTIME_SEL, \
+ GENMASK(21, 19)); \
+} while (0)
+#define SET_CMC_TBL_MASK_BE_LFTIME_SEL GENMASK(2, 0)
+#define SET_CMC_TBL_BE_LFTIME_SEL(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 4, val, GENMASK(24, 22)); \
+ le32p_replace_bits((__le32 *)(table) + 12, SET_CMC_TBL_MASK_BE_LFTIME_SEL, \
+ GENMASK(24, 22)); \
+} while (0)
+#define SET_CMC_TBL_MASK_BK_LFTIME_SEL GENMASK(2, 0)
+#define SET_CMC_TBL_BK_LFTIME_SEL(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 4, val, GENMASK(27, 25)); \
+ le32p_replace_bits((__le32 *)(table) + 12, SET_CMC_TBL_MASK_BK_LFTIME_SEL, \
+ GENMASK(27, 25)); \
+} while (0)
+#define SET_CMC_TBL_MASK_SECTYPE GENMASK(3, 0)
+#define SET_CMC_TBL_SECTYPE(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 4, val, GENMASK(31, 28)); \
+ le32p_replace_bits((__le32 *)(table) + 12, SET_CMC_TBL_MASK_SECTYPE, \
+ GENMASK(31, 28)); \
+} while (0)
+#define SET_CMC_TBL_MASK_MULTI_PORT_ID GENMASK(2, 0)
+#define SET_CMC_TBL_MULTI_PORT_ID(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 5, val, GENMASK(2, 0)); \
+ le32p_replace_bits((__le32 *)(table) + 13, SET_CMC_TBL_MASK_MULTI_PORT_ID, \
+ GENMASK(2, 0)); \
+} while (0)
+#define SET_CMC_TBL_MASK_BMC BIT(0)
+#define SET_CMC_TBL_BMC(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 5, val, BIT(3)); \
+ le32p_replace_bits((__le32 *)(table) + 13, SET_CMC_TBL_MASK_BMC, \
+ BIT(3)); \
+} while (0)
+#define SET_CMC_TBL_MASK_MBSSID GENMASK(3, 0)
+#define SET_CMC_TBL_MBSSID(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 5, val, GENMASK(7, 4)); \
+ le32p_replace_bits((__le32 *)(table) + 13, SET_CMC_TBL_MASK_MBSSID, \
+ GENMASK(7, 4)); \
+} while (0)
+#define SET_CMC_TBL_MASK_NAVUSEHDR BIT(0)
+#define SET_CMC_TBL_NAVUSEHDR(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 5, val, BIT(8)); \
+ le32p_replace_bits((__le32 *)(table) + 13, SET_CMC_TBL_MASK_NAVUSEHDR, \
+ BIT(8)); \
+} while (0)
+#define SET_CMC_TBL_MASK_TXPWR_MODE GENMASK(2, 0)
+#define SET_CMC_TBL_TXPWR_MODE(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 5, val, GENMASK(11, 9)); \
+ le32p_replace_bits((__le32 *)(table) + 13, SET_CMC_TBL_MASK_TXPWR_MODE, \
+ GENMASK(11, 9)); \
+} while (0)
+#define SET_CMC_TBL_MASK_DATA_DCM BIT(0)
+#define SET_CMC_TBL_DATA_DCM(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 5, val, BIT(12)); \
+ le32p_replace_bits((__le32 *)(table) + 13, SET_CMC_TBL_MASK_DATA_DCM, \
+ BIT(12)); \
+} while (0)
+#define SET_CMC_TBL_MASK_DATA_ER BIT(0)
+#define SET_CMC_TBL_DATA_ER(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 5, val, BIT(13)); \
+ le32p_replace_bits((__le32 *)(table) + 13, SET_CMC_TBL_MASK_DATA_ER, \
+ BIT(13)); \
+} while (0)
+#define SET_CMC_TBL_MASK_DATA_LDPC BIT(0)
+#define SET_CMC_TBL_DATA_LDPC(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 5, val, BIT(14)); \
+ le32p_replace_bits((__le32 *)(table) + 13, SET_CMC_TBL_MASK_DATA_LDPC, \
+ BIT(14)); \
+} while (0)
+#define SET_CMC_TBL_MASK_DATA_STBC BIT(0)
+#define SET_CMC_TBL_DATA_STBC(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 5, val, BIT(15)); \
+ le32p_replace_bits((__le32 *)(table) + 13, SET_CMC_TBL_MASK_DATA_STBC, \
+ BIT(15)); \
+} while (0)
+#define SET_CMC_TBL_MASK_A_CTRL_BQR BIT(0)
+#define SET_CMC_TBL_A_CTRL_BQR(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 5, val, BIT(16)); \
+ le32p_replace_bits((__le32 *)(table) + 13, SET_CMC_TBL_MASK_A_CTRL_BQR, \
+ BIT(16)); \
+} while (0)
+#define SET_CMC_TBL_MASK_A_CTRL_UPH BIT(0)
+#define SET_CMC_TBL_A_CTRL_UPH(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 5, val, BIT(17)); \
+ le32p_replace_bits((__le32 *)(table) + 13, SET_CMC_TBL_MASK_A_CTRL_UPH, \
+ BIT(17)); \
+} while (0)
+#define SET_CMC_TBL_MASK_A_CTRL_BSR BIT(0)
+#define SET_CMC_TBL_A_CTRL_BSR(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 5, val, BIT(18)); \
+ le32p_replace_bits((__le32 *)(table) + 13, SET_CMC_TBL_MASK_A_CTRL_BSR, \
+ BIT(18)); \
+} while (0)
+#define SET_CMC_TBL_MASK_A_CTRL_CAS BIT(0)
+#define SET_CMC_TBL_A_CTRL_CAS(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 5, val, BIT(19)); \
+ le32p_replace_bits((__le32 *)(table) + 13, SET_CMC_TBL_MASK_A_CTRL_CAS, \
+ BIT(19)); \
+} while (0)
+#define SET_CMC_TBL_MASK_DATA_BW_ER BIT(0)
+#define SET_CMC_TBL_DATA_BW_ER(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 5, val, BIT(20)); \
+ le32p_replace_bits((__le32 *)(table) + 13, SET_CMC_TBL_MASK_DATA_BW_ER, \
+ BIT(20)); \
+} while (0)
+#define SET_CMC_TBL_MASK_LSIG_TXOP_EN BIT(0)
+#define SET_CMC_TBL_LSIG_TXOP_EN(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 5, val, BIT(21)); \
+ le32p_replace_bits((__le32 *)(table) + 13, SET_CMC_TBL_MASK_LSIG_TXOP_EN, \
+ BIT(21)); \
+} while (0)
+#define SET_CMC_TBL_MASK_CTRL_CNT_VLD BIT(0)
+#define SET_CMC_TBL_CTRL_CNT_VLD(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 5, val, BIT(27)); \
+ le32p_replace_bits((__le32 *)(table) + 13, SET_CMC_TBL_MASK_CTRL_CNT_VLD, \
+ BIT(27)); \
+} while (0)
+#define SET_CMC_TBL_MASK_CTRL_CNT GENMASK(3, 0)
+#define SET_CMC_TBL_CTRL_CNT(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 5, val, GENMASK(31, 28)); \
+ le32p_replace_bits((__le32 *)(table) + 13, SET_CMC_TBL_MASK_CTRL_CNT, \
+ GENMASK(31, 28)); \
+} while (0)
+#define SET_CMC_TBL_MASK_RESP_REF_RATE GENMASK(8, 0)
+#define SET_CMC_TBL_RESP_REF_RATE(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 6, val, GENMASK(8, 0)); \
+ le32p_replace_bits((__le32 *)(table) + 14, SET_CMC_TBL_MASK_RESP_REF_RATE, \
+ GENMASK(8, 0)); \
+} while (0)
+#define SET_CMC_TBL_MASK_ALL_ACK_SUPPORT BIT(0)
+#define SET_CMC_TBL_ALL_ACK_SUPPORT(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 6, val, BIT(12)); \
+ le32p_replace_bits((__le32 *)(table) + 14, SET_CMC_TBL_MASK_ALL_ACK_SUPPORT, \
+ BIT(12)); \
+} while (0)
+#define SET_CMC_TBL_MASK_BSR_QUEUE_SIZE_FORMAT BIT(0)
+#define SET_CMC_TBL_BSR_QUEUE_SIZE_FORMAT(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 6, val, BIT(13)); \
+ le32p_replace_bits((__le32 *)(table) + 14, SET_CMC_TBL_MASK_BSR_QUEUE_SIZE_FORMAT, \
+ BIT(13)); \
+} while (0)
+#define SET_CMC_TBL_MASK_NTX_PATH_EN GENMASK(3, 0)
+#define SET_CMC_TBL_NTX_PATH_EN(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 6, val, GENMASK(19, 16)); \
+ le32p_replace_bits((__le32 *)(table) + 14, SET_CMC_TBL_MASK_NTX_PATH_EN, \
+ GENMASK(19, 16)); \
+} while (0)
+#define SET_CMC_TBL_MASK_PATH_MAP_A GENMASK(1, 0)
+#define SET_CMC_TBL_PATH_MAP_A(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 6, val, GENMASK(21, 20)); \
+ le32p_replace_bits((__le32 *)(table) + 14, SET_CMC_TBL_MASK_PATH_MAP_A, \
+ GENMASK(21, 20)); \
+} while (0)
+#define SET_CMC_TBL_MASK_PATH_MAP_B GENMASK(1, 0)
+#define SET_CMC_TBL_PATH_MAP_B(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 6, val, GENMASK(23, 22)); \
+ le32p_replace_bits((__le32 *)(table) + 14, SET_CMC_TBL_MASK_PATH_MAP_B, \
+ GENMASK(23, 22)); \
+} while (0)
+#define SET_CMC_TBL_MASK_PATH_MAP_C GENMASK(1, 0)
+#define SET_CMC_TBL_PATH_MAP_C(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 6, val, GENMASK(25, 24)); \
+ le32p_replace_bits((__le32 *)(table) + 14, SET_CMC_TBL_MASK_PATH_MAP_C, \
+ GENMASK(25, 24)); \
+} while (0)
+#define SET_CMC_TBL_MASK_PATH_MAP_D GENMASK(1, 0)
+#define SET_CMC_TBL_PATH_MAP_D(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 6, val, GENMASK(27, 26)); \
+ le32p_replace_bits((__le32 *)(table) + 14, SET_CMC_TBL_MASK_PATH_MAP_D, \
+ GENMASK(27, 26)); \
+} while (0)
+#define SET_CMC_TBL_MASK_ANTSEL_A BIT(0)
+#define SET_CMC_TBL_ANTSEL_A(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 6, val, BIT(28)); \
+ le32p_replace_bits((__le32 *)(table) + 14, SET_CMC_TBL_MASK_ANTSEL_A, \
+ BIT(28)); \
+} while (0)
+#define SET_CMC_TBL_MASK_ANTSEL_B BIT(0)
+#define SET_CMC_TBL_ANTSEL_B(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 6, val, BIT(29)); \
+ le32p_replace_bits((__le32 *)(table) + 14, SET_CMC_TBL_MASK_ANTSEL_B, \
+ BIT(29)); \
+} while (0)
+#define SET_CMC_TBL_MASK_ANTSEL_C BIT(0)
+#define SET_CMC_TBL_ANTSEL_C(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 6, val, BIT(30)); \
+ le32p_replace_bits((__le32 *)(table) + 14, SET_CMC_TBL_MASK_ANTSEL_C, \
+ BIT(30)); \
+} while (0)
+#define SET_CMC_TBL_MASK_ANTSEL_D BIT(0)
+#define SET_CMC_TBL_ANTSEL_D(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 6, val, BIT(31)); \
+ le32p_replace_bits((__le32 *)(table) + 14, SET_CMC_TBL_MASK_ANTSEL_D, \
+ BIT(31)); \
+} while (0)
+#define SET_CMC_TBL_MASK_ADDR_CAM_INDEX GENMASK(7, 0)
+#define SET_CMC_TBL_ADDR_CAM_INDEX(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 7, val, GENMASK(7, 0)); \
+ le32p_replace_bits((__le32 *)(table) + 15, SET_CMC_TBL_MASK_ADDR_CAM_INDEX, \
+ GENMASK(7, 0)); \
+} while (0)
+#define SET_CMC_TBL_MASK_PAID GENMASK(8, 0)
+#define SET_CMC_TBL_PAID(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 7, val, GENMASK(16, 8)); \
+ le32p_replace_bits((__le32 *)(table) + 15, SET_CMC_TBL_MASK_PAID, \
+ GENMASK(16, 8)); \
+} while (0)
+#define SET_CMC_TBL_MASK_ULDL BIT(0)
+#define SET_CMC_TBL_ULDL(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 7, val, BIT(17)); \
+ le32p_replace_bits((__le32 *)(table) + 15, SET_CMC_TBL_MASK_ULDL, \
+ BIT(17)); \
+} while (0)
+#define SET_CMC_TBL_MASK_DOPPLER_CTRL GENMASK(1, 0)
+#define SET_CMC_TBL_DOPPLER_CTRL(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 7, val, GENMASK(19, 18)); \
+ le32p_replace_bits((__le32 *)(table) + 15, SET_CMC_TBL_MASK_DOPPLER_CTRL, \
+ GENMASK(19, 18)); \
+} while (0)
+#define SET_CMC_TBL_MASK_NOMINAL_PKT_PADDING GENMASK(1, 0)
+#define SET_CMC_TBL_NOMINAL_PKT_PADDING(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 7, val, GENMASK(21, 20)); \
+ le32p_replace_bits((__le32 *)(table) + 15, SET_CMC_TBL_MASK_NOMINAL_PKT_PADDING, \
+ GENMASK(21, 20)); \
+} while (0)
+#define SET_CMC_TBL_NOMINAL_PKT_PADDING40(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 7, val, GENMASK(23, 22)); \
+ le32p_replace_bits((__le32 *)(table) + 15, SET_CMC_TBL_MASK_NOMINAL_PKT_PADDING, \
+ GENMASK(23, 22)); \
+} while (0)
+#define SET_CMC_TBL_MASK_TXPWR_TOLERENCE GENMASK(3, 0)
+#define SET_CMC_TBL_TXPWR_TOLERENCE(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 7, val, GENMASK(27, 24)); \
+ le32p_replace_bits((__le32 *)(table) + 15, SET_CMC_TBL_MASK_TXPWR_TOLERENCE, \
+ GENMASK(27, 24)); \
+} while (0)
+#define SET_CMC_TBL_NOMINAL_PKT_PADDING80(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 7, val, GENMASK(31, 30)); \
+ le32p_replace_bits((__le32 *)(table) + 15, SET_CMC_TBL_MASK_NOMINAL_PKT_PADDING, \
+ GENMASK(31, 30)); \
+} while (0)
+#define SET_CMC_TBL_MASK_NC GENMASK(2, 0)
+#define SET_CMC_TBL_NC(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 8, val, GENMASK(2, 0)); \
+ le32p_replace_bits((__le32 *)(table) + 16, SET_CMC_TBL_MASK_NC, \
+ GENMASK(2, 0)); \
+} while (0)
+#define SET_CMC_TBL_MASK_NR GENMASK(2, 0)
+#define SET_CMC_TBL_NR(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 8, val, GENMASK(5, 3)); \
+ le32p_replace_bits((__le32 *)(table) + 16, SET_CMC_TBL_MASK_NR, \
+ GENMASK(5, 3)); \
+} while (0)
+#define SET_CMC_TBL_MASK_NG GENMASK(1, 0)
+#define SET_CMC_TBL_NG(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 8, val, GENMASK(7, 6)); \
+ le32p_replace_bits((__le32 *)(table) + 16, SET_CMC_TBL_MASK_NG, \
+ GENMASK(7, 6)); \
+} while (0)
+#define SET_CMC_TBL_MASK_CB GENMASK(1, 0)
+#define SET_CMC_TBL_CB(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 8, val, GENMASK(9, 8)); \
+ le32p_replace_bits((__le32 *)(table) + 16, SET_CMC_TBL_MASK_CB, \
+ GENMASK(9, 8)); \
+} while (0)
+#define SET_CMC_TBL_MASK_CS GENMASK(1, 0)
+#define SET_CMC_TBL_CS(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 8, val, GENMASK(11, 10)); \
+ le32p_replace_bits((__le32 *)(table) + 16, SET_CMC_TBL_MASK_CS, \
+ GENMASK(11, 10)); \
+} while (0)
+#define SET_CMC_TBL_MASK_CSI_TXBF_EN BIT(0)
+#define SET_CMC_TBL_CSI_TXBF_EN(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 8, val, BIT(12)); \
+ le32p_replace_bits((__le32 *)(table) + 16, SET_CMC_TBL_MASK_CSI_TXBF_EN, \
+ BIT(12)); \
+} while (0)
+#define SET_CMC_TBL_MASK_CSI_STBC_EN BIT(0)
+#define SET_CMC_TBL_CSI_STBC_EN(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 8, val, BIT(13)); \
+ le32p_replace_bits((__le32 *)(table) + 16, SET_CMC_TBL_MASK_CSI_STBC_EN, \
+ BIT(13)); \
+} while (0)
+#define SET_CMC_TBL_MASK_CSI_LDPC_EN BIT(0)
+#define SET_CMC_TBL_CSI_LDPC_EN(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 8, val, BIT(14)); \
+ le32p_replace_bits((__le32 *)(table) + 16, SET_CMC_TBL_MASK_CSI_LDPC_EN, \
+ BIT(14)); \
+} while (0)
+#define SET_CMC_TBL_MASK_CSI_PARA_EN BIT(0)
+#define SET_CMC_TBL_CSI_PARA_EN(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 8, val, BIT(15)); \
+ le32p_replace_bits((__le32 *)(table) + 16, SET_CMC_TBL_MASK_CSI_PARA_EN, \
+ BIT(15)); \
+} while (0)
+#define SET_CMC_TBL_MASK_CSI_FIX_RATE GENMASK(8, 0)
+#define SET_CMC_TBL_CSI_FIX_RATE(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 8, val, GENMASK(24, 16)); \
+ le32p_replace_bits((__le32 *)(table) + 16, SET_CMC_TBL_MASK_CSI_FIX_RATE, \
+ GENMASK(24, 16)); \
+} while (0)
+#define SET_CMC_TBL_MASK_CSI_GI_LTF GENMASK(2, 0)
+#define SET_CMC_TBL_CSI_GI_LTF(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 8, val, GENMASK(27, 25)); \
+ le32p_replace_bits((__le32 *)(table) + 16, SET_CMC_TBL_MASK_CSI_GI_LTF, \
+ GENMASK(27, 25)); \
+} while (0)
+#define SET_CMC_TBL_MASK_CSI_GID_SEL BIT(0)
+#define SET_CMC_TBL_CSI_GID_SEL(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 8, val, BIT(29)); \
+ le32p_replace_bits((__le32 *)(table) + 16, SET_CMC_TBL_MASK_CSI_GID_SEL, \
+ BIT(29)); \
+} while (0)
+#define SET_CMC_TBL_MASK_CSI_BW GENMASK(1, 0)
+#define SET_CMC_TBL_CSI_BW(table, val) \
+do { \
+ le32p_replace_bits((__le32 *)(table) + 8, val, GENMASK(31, 30)); \
+ le32p_replace_bits((__le32 *)(table) + 16, SET_CMC_TBL_MASK_CSI_BW, \
+ GENMASK(31, 30)); \
+} while (0)
+
+#define SET_FWROLE_MAINTAIN_MACID(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(7, 0))
+#define SET_FWROLE_MAINTAIN_SELF_ROLE(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(9, 8))
+#define SET_FWROLE_MAINTAIN_UPD_MODE(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(12, 10))
+#define SET_FWROLE_MAINTAIN_WIFI_ROLE(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(16, 13))
+
+#define SET_JOININFO_MACID(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(7, 0))
+#define SET_JOININFO_OP(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, BIT(8))
+#define SET_JOININFO_BAND(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, BIT(9))
+#define SET_JOININFO_WMM(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(11, 10))
+#define SET_JOININFO_TGR(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, BIT(12))
+#define SET_JOININFO_ISHESTA(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, BIT(13))
+#define SET_JOININFO_DLBW(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(15, 14))
+#define SET_JOININFO_TF_MAC_PAD(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(17, 16))
+#define SET_JOININFO_DL_T_PE(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(20, 18))
+#define SET_JOININFO_PORT_ID(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(23, 21))
+#define SET_JOININFO_NET_TYPE(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(25, 24))
+#define SET_JOININFO_WIFI_ROLE(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(29, 26))
+#define SET_JOININFO_SELF_ROLE(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(31, 30))
+
+#define SET_GENERAL_PKT_MACID(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(7, 0))
+#define SET_GENERAL_PKT_PROBRSP_ID(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(15, 8))
+#define SET_GENERAL_PKT_PSPOLL_ID(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(23, 16))
+#define SET_GENERAL_PKT_NULL_ID(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(31, 24))
+#define SET_GENERAL_PKT_QOS_NULL_ID(h2c, val) \
+ le32p_replace_bits((__le32 *)(h2c) + 1, val, GENMASK(7, 0))
+#define SET_GENERAL_PKT_CTS2SELF_ID(h2c, val) \
+ le32p_replace_bits((__le32 *)(h2c) + 1, val, GENMASK(15, 8))
+
+#define SET_LOG_CFG_LEVEL(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(7, 0))
+#define SET_LOG_CFG_PATH(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(15, 8))
+#define SET_LOG_CFG_COMP(h2c, val) \
+ le32p_replace_bits((__le32 *)(h2c) + 1, val, GENMASK(31, 0))
+#define SET_LOG_CFG_COMP_EXT(h2c, val) \
+ le32p_replace_bits((__le32 *)(h2c) + 2, val, GENMASK(31, 0))
+
+#define SET_BA_CAM_VALID(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, BIT(0))
+#define SET_BA_CAM_INIT_REQ(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, BIT(1))
+#define SET_BA_CAM_ENTRY_IDX(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(3, 2))
+#define SET_BA_CAM_TID(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(7, 4))
+#define SET_BA_CAM_MACID(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(15, 8))
+#define SET_BA_CAM_BMAP_SIZE(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(19, 16))
+#define SET_BA_CAM_SSN(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(31, 20))
+
+#define SET_LPS_PARM_MACID(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(7, 0))
+#define SET_LPS_PARM_PSMODE(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(15, 8))
+#define SET_LPS_PARM_RLBM(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(19, 16))
+#define SET_LPS_PARM_SMARTPS(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(23, 20))
+#define SET_LPS_PARM_AWAKEINTERVAL(h2c, val) \
+ le32p_replace_bits((__le32 *)h2c, val, GENMASK(31, 24))
+#define SET_LPS_PARM_VOUAPSD(h2c, val) \
+ le32p_replace_bits((__le32 *)(h2c) + 1, val, BIT(0))
+#define SET_LPS_PARM_VIUAPSD(h2c, val) \
+ le32p_replace_bits((__le32 *)(h2c) + 1, val, BIT(1))
+#define SET_LPS_PARM_BEUAPSD(h2c, val) \
+ le32p_replace_bits((__le32 *)(h2c) + 1, val, BIT(2))
+#define SET_LPS_PARM_BKUAPSD(h2c, val) \
+ le32p_replace_bits((__le32 *)(h2c) + 1, val, BIT(3))
+#define SET_LPS_PARM_LASTRPWM(h2c, val) \
+ le32p_replace_bits((__le32 *)(h2c) + 1, val, GENMASK(15, 8))
+
+enum rtw89_btc_btf_h2c_class {
+ BTFC_SET = 0x10,
+ BTFC_GET = 0x11,
+ BTFC_FW_EVENT = 0x12,
+};
+
+enum rtw89_btc_btf_set {
+ SET_REPORT_EN = 0x0,
+ SET_SLOT_TABLE,
+ SET_MREG_TABLE,
+ SET_CX_POLICY,
+ SET_GPIO_DBG,
+ SET_DRV_INFO,
+ SET_DRV_EVENT,
+ SET_BT_WREG_ADDR,
+ SET_BT_WREG_VAL,
+ SET_BT_RREG_ADDR,
+ SET_BT_WL_CH_INFO,
+ SET_BT_INFO_REPORT,
+ SET_BT_IGNORE_WLAN_ACT,
+ SET_BT_TX_PWR,
+ SET_BT_LNA_CONSTRAIN,
+ SET_BT_GOLDEN_RX_RANGE,
+ SET_BT_PSD_REPORT,
+ SET_H2C_TEST,
+ SET_MAX1,
+};
+
+enum rtw89_btc_cxdrvinfo {
+ CXDRVINFO_INIT = 0,
+ CXDRVINFO_ROLE,
+ CXDRVINFO_DBCC,
+ CXDRVINFO_SMAP,
+ CXDRVINFO_RFK,
+ CXDRVINFO_RUN,
+ CXDRVINFO_CTRL,
+ CXDRVINFO_SCAN,
+ CXDRVINFO_MAX,
+};
+
+#define RTW89_SET_FWCMD_CXHDR_TYPE(cmd, val) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 0, GENMASK(7, 0))
+#define RTW89_SET_FWCMD_CXHDR_LEN(cmd, val) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 1, GENMASK(7, 0))
+
+#define RTW89_SET_FWCMD_CXINIT_ANT_TYPE(cmd, val) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 2, GENMASK(7, 0))
+#define RTW89_SET_FWCMD_CXINIT_ANT_NUM(cmd, val) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 3, GENMASK(7, 0))
+#define RTW89_SET_FWCMD_CXINIT_ANT_ISO(cmd, val) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 4, GENMASK(7, 0))
+#define RTW89_SET_FWCMD_CXINIT_ANT_POS(cmd, val) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 5, BIT(0))
+#define RTW89_SET_FWCMD_CXINIT_ANT_DIVERSITY(cmd, val) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 5, BIT(1))
+#define RTW89_SET_FWCMD_CXINIT_MOD_RFE(cmd, val) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 6, GENMASK(7, 0))
+#define RTW89_SET_FWCMD_CXINIT_MOD_CV(cmd, val) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 7, GENMASK(7, 0))
+#define RTW89_SET_FWCMD_CXINIT_MOD_BT_SOLO(cmd, val) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 8, BIT(0))
+#define RTW89_SET_FWCMD_CXINIT_MOD_BT_POS(cmd, val) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 8, BIT(1))
+#define RTW89_SET_FWCMD_CXINIT_MOD_SW_TYPE(cmd, val) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 8, BIT(2))
+#define RTW89_SET_FWCMD_CXINIT_WL_GCH(cmd, val) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 10, GENMASK(7, 0))
+#define RTW89_SET_FWCMD_CXINIT_WL_ONLY(cmd, val) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 11, BIT(0))
+#define RTW89_SET_FWCMD_CXINIT_WL_INITOK(cmd, val) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 11, BIT(1))
+#define RTW89_SET_FWCMD_CXINIT_DBCC_EN(cmd, val) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 11, BIT(2))
+#define RTW89_SET_FWCMD_CXINIT_CX_OTHER(cmd, val) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 11, BIT(3))
+#define RTW89_SET_FWCMD_CXINIT_BT_ONLY(cmd, val) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 11, BIT(4))
+
+#define RTW89_SET_FWCMD_CXROLE_CONNECT_CNT(cmd, val) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 2, GENMASK(7, 0))
+#define RTW89_SET_FWCMD_CXROLE_LINK_MODE(cmd, val) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 3, GENMASK(7, 0))
+#define RTW89_SET_FWCMD_CXROLE_ROLE_NONE(cmd, val) \
+ RTW89_SET_FWCMD_UA16(cmd, val, 4, BIT(0))
+#define RTW89_SET_FWCMD_CXROLE_ROLE_STA(cmd, val) \
+ RTW89_SET_FWCMD_UA16(cmd, val, 4, BIT(1))
+#define RTW89_SET_FWCMD_CXROLE_ROLE_AP(cmd, val) \
+ RTW89_SET_FWCMD_UA16(cmd, val, 4, BIT(2))
+#define RTW89_SET_FWCMD_CXROLE_ROLE_VAP(cmd, val) \
+ RTW89_SET_FWCMD_UA16(cmd, val, 4, BIT(3))
+#define RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC(cmd, val) \
+ RTW89_SET_FWCMD_UA16(cmd, val, 4, BIT(4))
+#define RTW89_SET_FWCMD_CXROLE_ROLE_ADHOC_MASTER(cmd, val) \
+ RTW89_SET_FWCMD_UA16(cmd, val, 4, BIT(5))
+#define RTW89_SET_FWCMD_CXROLE_ROLE_MESH(cmd, val) \
+ RTW89_SET_FWCMD_UA16(cmd, val, 4, BIT(6))
+#define RTW89_SET_FWCMD_CXROLE_ROLE_MONITOR(cmd, val) \
+ RTW89_SET_FWCMD_UA16(cmd, val, 4, BIT(7))
+#define RTW89_SET_FWCMD_CXROLE_ROLE_P2P_DEV(cmd, val) \
+ RTW89_SET_FWCMD_UA16(cmd, val, 4, BIT(8))
+#define RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GC(cmd, val) \
+ RTW89_SET_FWCMD_UA16(cmd, val, 4, BIT(9))
+#define RTW89_SET_FWCMD_CXROLE_ROLE_P2P_GO(cmd, val) \
+ RTW89_SET_FWCMD_UA16(cmd, val, 4, BIT(10))
+#define RTW89_SET_FWCMD_CXROLE_ROLE_NAN(cmd, val) \
+ RTW89_SET_FWCMD_UA16(cmd, val, 4, BIT(11))
+#define RTW89_SET_FWCMD_CXROLE_ACT_CONNECTED(cmd, val, n) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 6 + 12 * (n), BIT(0))
+#define RTW89_SET_FWCMD_CXROLE_ACT_PID(cmd, val, n) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 6 + 12 * (n), GENMASK(3, 1))
+#define RTW89_SET_FWCMD_CXROLE_ACT_PHY(cmd, val, n) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 6 + 12 * (n), BIT(4))
+#define RTW89_SET_FWCMD_CXROLE_ACT_NOA(cmd, val, n) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 6 + 12 * (n), BIT(5))
+#define RTW89_SET_FWCMD_CXROLE_ACT_BAND(cmd, val, n) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 6 + 12 * (n), GENMASK(7, 6))
+#define RTW89_SET_FWCMD_CXROLE_ACT_CLIENT_PS(cmd, val, n) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 7 + 12 * (n), BIT(0))
+#define RTW89_SET_FWCMD_CXROLE_ACT_BW(cmd, val, n) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 7 + 12 * (n), GENMASK(7, 1))
+#define RTW89_SET_FWCMD_CXROLE_ACT_ROLE(cmd, val, n) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 8 + 12 * (n), GENMASK(7, 0))
+#define RTW89_SET_FWCMD_CXROLE_ACT_CH(cmd, val, n) \
+ RTW89_SET_FWCMD_UA8(cmd, val, 9 + 12 * (n), GENMASK(7, 0))
+#define RTW89_SET_FWCMD_CXROLE_ACT_TX_LVL(cmd, val, n) \
+ RTW89_SET_FWCMD_UA16(cmd, val, 10 + 12 * (n), GENMASK(15, 0))
+#define RTW89_SET_FWCMD_CXROLE_ACT_RX_LVL(cmd, val, n) \
+ RTW89_SET_FWCMD_UA16(cmd, val, 12 + 12 * (n), GENMASK(15, 0))
+#define RTW89_SET_FWCMD_CXROLE_ACT_TX_RATE(cmd, val, n) \
+ RTW89_SET_FWCMD_UA16(cmd, val, 14 + 12 * (n), GENMASK(15, 0))
+#define RTW89_SET_FWCMD_CXROLE_ACT_RX_RATE(cmd, val, n) \
+ RTW89_SET_FWCMD_UA16(cmd, val, 16 + 12 * (n), GENMASK(15, 0))
+
+#define RTW89_SET_FWCMD_CXCTRL_MANUAL(cmd, val) \
+ RTW89_SET_FWCMD_UA32(cmd, val, 2, BIT(0))
+#define RTW89_SET_FWCMD_CXCTRL_IGNORE_BT(cmd, val) \
+ RTW89_SET_FWCMD_UA32(cmd, val, 2, BIT(1))
+#define RTW89_SET_FWCMD_CXCTRL_ALWAYS_FREERUN(cmd, val) \
+ RTW89_SET_FWCMD_UA32(cmd, val, 2, BIT(2))
+#define RTW89_SET_FWCMD_CXCTRL_TRACE_STEP(cmd, val) \
+ RTW89_SET_FWCMD_UA32(cmd, val, 2, GENMASK(18, 3))
+
+#define RTW89_SET_FWCMD_CXRFK_STATE(cmd, val) \
+ RTW89_SET_FWCMD_UA32(cmd, val, 2, GENMASK(1, 0))
+#define RTW89_SET_FWCMD_CXRFK_PATH_MAP(cmd, val) \
+ RTW89_SET_FWCMD_UA32(cmd, val, 2, GENMASK(5, 2))
+#define RTW89_SET_FWCMD_CXRFK_PHY_MAP(cmd, val) \
+ RTW89_SET_FWCMD_UA32(cmd, val, 2, GENMASK(7, 6))
+#define RTW89_SET_FWCMD_CXRFK_BAND(cmd, val) \
+ RTW89_SET_FWCMD_UA32(cmd, val, 2, GENMASK(9, 8))
+#define RTW89_SET_FWCMD_CXRFK_TYPE(cmd, val) \
+ RTW89_SET_FWCMD_UA32(cmd, val, 2, GENMASK(17, 10))
+
+#define RTW89_C2H_HEADER_LEN 8
+
+#define RTW89_GET_C2H_CATEGORY(c2h) \
+ le32_get_bits(*((__le32 *)c2h), GENMASK(1, 0))
+#define RTW89_GET_C2H_CLASS(c2h) \
+ le32_get_bits(*((__le32 *)c2h), GENMASK(7, 2))
+#define RTW89_GET_C2H_FUNC(c2h) \
+ le32_get_bits(*((__le32 *)c2h), GENMASK(15, 8))
+#define RTW89_GET_C2H_LEN(c2h) \
+ le32_get_bits(*((__le32 *)(c2h) + 1), GENMASK(13, 0))
+
+#define RTW89_GET_C2H_LOG_SRT_PRT(c2h) (char *)((__le32 *)(c2h) + 2)
+#define RTW89_GET_C2H_LOG_LEN(len) ((len) - RTW89_C2H_HEADER_LEN)
+
+#define RTW89_GET_MAC_C2H_DONE_ACK_CAT(c2h) \
+ le32_get_bits(*((__le32 *)(c2h) + 2), GENMASK(1, 0))
+#define RTW89_GET_MAC_C2H_DONE_ACK_CLASS(c2h) \
+ le32_get_bits(*((__le32 *)(c2h) + 2), GENMASK(7, 2))
+#define RTW89_GET_MAC_C2H_DONE_ACK_FUNC(c2h) \
+ le32_get_bits(*((__le32 *)(c2h) + 2), GENMASK(15, 8))
+#define RTW89_GET_MAC_C2H_DONE_ACK_H2C_RETURN(c2h) \
+ le32_get_bits(*((__le32 *)(c2h) + 2), GENMASK(23, 16))
+#define RTW89_GET_MAC_C2H_DONE_ACK_H2C_SEQ(c2h) \
+ le32_get_bits(*((__le32 *)(c2h) + 2), GENMASK(31, 24))
+
+#define RTW89_GET_MAC_C2H_REV_ACK_CAT(c2h) \
+ le32_get_bits(*((__le32 *)(c2h) + 2), GENMASK(1, 0))
+#define RTW89_GET_MAC_C2H_REV_ACK_CLASS(c2h) \
+ le32_get_bits(*((__le32 *)(c2h) + 2), GENMASK(7, 2))
+#define RTW89_GET_MAC_C2H_REV_ACK_FUNC(c2h) \
+ le32_get_bits(*((__le32 *)(c2h) + 2), GENMASK(15, 8))
+#define RTW89_GET_MAC_C2H_REV_ACK_H2C_SEQ(c2h) \
+ le32_get_bits(*((__le32 *)(c2h) + 2), GENMASK(23, 16))
+
+#define RTW89_GET_PHY_C2H_RA_RPT_MACID(c2h) \
+ le32_get_bits(*((__le32 *)(c2h) + 2), GENMASK(15, 0))
+#define RTW89_GET_PHY_C2H_RA_RPT_RETRY_RATIO(c2h) \
+ le32_get_bits(*((__le32 *)(c2h) + 2), GENMASK(23, 16))
+#define RTW89_GET_PHY_C2H_RA_RPT_MCSNSS(c2h) \
+ le32_get_bits(*((__le32 *)(c2h) + 3), GENMASK(6, 0))
+#define RTW89_GET_PHY_C2H_RA_RPT_MD_SEL(c2h) \
+ le32_get_bits(*((__le32 *)(c2h) + 3), GENMASK(9, 8))
+#define RTW89_GET_PHY_C2H_RA_RPT_GILTF(c2h) \
+ le32_get_bits(*((__le32 *)(c2h) + 3), GENMASK(12, 10))
+#define RTW89_GET_PHY_C2H_RA_RPT_BW(c2h) \
+ le32_get_bits(*((__le32 *)(c2h) + 3), GENMASK(14, 13))
+
+#define RTW89_RA_RATE_MASK_NSS GENMASK(6, 4)
+#define RTW89_RA_RATE_MASK_MCS GENMASK(3, 0)
+#define RTW89_MK_HT_RATE(nss, mcs) (FIELD_PREP(GENMASK(4, 3), nss) | \
+ FIELD_PREP(GENMASK(2, 0), mcs))
+
+#define RTW89_FW_HDR_SIZE 32
+#define RTW89_FW_SECTION_HDR_SIZE 16
+
+#define RTW89_MFW_SIG 0xFF
+
+struct rtw89_mfw_info {
+ u8 cv;
+ u8 type; /* enum rtw89_fw_type */
+ u8 mp;
+ u8 rsvd;
+ __le32 shift;
+ __le32 size;
+ u8 rsvd2[4];
+} __packed;
+
+struct rtw89_mfw_hdr {
+ u8 sig; /* RTW89_MFW_SIG */
+ u8 fw_nr;
+ u8 rsvd[14];
+ struct rtw89_mfw_info info[];
+} __packed;
+
+struct fwcmd_hdr {
+ __le32 hdr0;
+ __le32 hdr1;
+};
+
+#define RTW89_H2C_RF_PAGE_SIZE 500
+#define RTW89_H2C_RF_PAGE_NUM 3
+struct rtw89_fw_h2c_rf_reg_info {
+ enum rtw89_rf_path rf_path;
+ __le32 rtw89_phy_config_rf_h2c[RTW89_H2C_RF_PAGE_NUM][RTW89_H2C_RF_PAGE_SIZE];
+ u16 curr_idx;
+};
+
+#define H2C_SEC_CAM_LEN 24
+
+#define H2C_HEADER_LEN 8
+#define H2C_HDR_CAT GENMASK(1, 0)
+#define H2C_HDR_CLASS GENMASK(7, 2)
+#define H2C_HDR_FUNC GENMASK(15, 8)
+#define H2C_HDR_DEL_TYPE GENMASK(19, 16)
+#define H2C_HDR_H2C_SEQ GENMASK(31, 24)
+#define H2C_HDR_TOTAL_LEN GENMASK(13, 0)
+#define H2C_HDR_REC_ACK BIT(14)
+#define H2C_HDR_DONE_ACK BIT(15)
+
+#define FWCMD_TYPE_H2C 0
+
+#define H2C_CAT_MAC 0x1
+
+/* CLASS 0 - FW INFO */
+#define H2C_CL_FW_INFO 0x0
+#define H2C_FUNC_LOG_CFG 0x0
+#define H2C_FUNC_MAC_GENERAL_PKT 0x1
+
+/* CLASS 2 - PS */
+#define H2C_CL_MAC_PS 0x2
+#define H2C_FUNC_MAC_LPS_PARM 0x0
+
+/* CLASS 3 - FW download */
+#define H2C_CL_MAC_FWDL 0x3
+#define H2C_FUNC_MAC_FWHDR_DL 0x0
+
+/* CLASS 5 - Frame Exchange */
+#define H2C_CL_MAC_FR_EXCHG 0x5
+#define H2C_FUNC_MAC_CCTLINFO_UD 0x2
+
+/* CLASS 6 - Address CAM */
+#define H2C_CL_MAC_ADDR_CAM_UPDATE 0x6
+#define H2C_FUNC_MAC_ADDR_CAM_UPD 0x0
+
+/* CLASS 8 - Media Status Report */
+#define H2C_CL_MAC_MEDIA_RPT 0x8
+#define H2C_FUNC_MAC_JOININFO 0x0
+#define H2C_FUNC_MAC_FWROLE_MAINTAIN 0x4
+
+/* CLASS 9 - FW offload */
+#define H2C_CL_MAC_FW_OFLD 0x9
+#define H2C_FUNC_MAC_MACID_PAUSE 0x8
+#define H2C_FUNC_USR_EDCA 0xF
+#define H2C_FUNC_OFLD_CFG 0x14
+
+/* CLASS 10 - Security CAM */
+#define H2C_CL_MAC_SEC_CAM 0xa
+#define H2C_FUNC_MAC_SEC_UPD 0x1
+
+/* CLASS 12 - BA CAM */
+#define H2C_CL_BA_CAM 0xc
+#define H2C_FUNC_MAC_BA_CAM 0x0
+
+#define H2C_CAT_OUTSRC 0x2
+
+#define H2C_CL_OUTSRC_RA 0x1
+#define H2C_FUNC_OUTSRC_RA_MACIDCFG 0x0
+
+#define H2C_CL_OUTSRC_RF_REG_A 0x8
+#define H2C_CL_OUTSRC_RF_REG_B 0x9
+
+int rtw89_fw_check_rdy(struct rtw89_dev *rtwdev);
+int rtw89_fw_recognize(struct rtw89_dev *rtwdev);
+int rtw89_fw_download(struct rtw89_dev *rtwdev, enum rtw89_fw_type type);
+int rtw89_load_firmware(struct rtw89_dev *rtwdev);
+void rtw89_unload_firmware(struct rtw89_dev *rtwdev);
+int rtw89_wait_firmware_completion(struct rtw89_dev *rtwdev);
+void rtw89_h2c_pkt_set_hdr(struct rtw89_dev *rtwdev, struct sk_buff *skb,
+ u8 type, u8 cat, u8 class, u8 func,
+ bool rack, bool dack, u32 len);
+int rtw89_fw_h2c_default_cmac_tbl(struct rtw89_dev *rtwdev, u8 macid);
+int rtw89_fw_h2c_assoc_cmac_tbl(struct rtw89_dev *rtwdev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+int rtw89_fw_h2c_txtime_cmac_tbl(struct rtw89_dev *rtwdev,
+ struct rtw89_sta *rtwsta);
+int rtw89_fw_h2c_cam(struct rtw89_dev *rtwdev, struct rtw89_vif *vif);
+void rtw89_fw_c2h_irqsafe(struct rtw89_dev *rtwdev, struct sk_buff *c2h);
+void rtw89_fw_c2h_work(struct work_struct *work);
+int rtw89_fw_h2c_vif_maintain(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif,
+ enum rtw89_upd_mode upd_mode);
+int rtw89_fw_h2c_join_info(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,
+ u8 dis_conn);
+int rtw89_fw_h2c_macid_pause(struct rtw89_dev *rtwdev, u8 sh, u8 grp,
+ bool pause);
+int rtw89_fw_h2c_set_edca(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,
+ u8 ac, u32 val);
+int rtw89_fw_h2c_set_ofld_cfg(struct rtw89_dev *rtwdev);
+int rtw89_fw_h2c_ra(struct rtw89_dev *rtwdev, struct rtw89_ra_info *ra, bool csi);
+int rtw89_fw_h2c_cxdrv_init(struct rtw89_dev *rtwdev);
+int rtw89_fw_h2c_cxdrv_role(struct rtw89_dev *rtwdev);
+int rtw89_fw_h2c_cxdrv_ctrl(struct rtw89_dev *rtwdev);
+int rtw89_fw_h2c_cxdrv_rfk(struct rtw89_dev *rtwdev);
+int rtw89_fw_h2c_rf_reg(struct rtw89_dev *rtwdev,
+ struct rtw89_fw_h2c_rf_reg_info *info,
+ u16 len, u8 page);
+int rtw89_fw_h2c_raw_with_hdr(struct rtw89_dev *rtwdev,
+ u8 h2c_class, u8 h2c_func, u8 *buf, u16 len,
+ bool rack, bool dack);
+int rtw89_fw_h2c_raw(struct rtw89_dev *rtwdev, const u8 *buf, u16 len);
+int rtw89_fw_h2c_general_pkt(struct rtw89_dev *rtwdev, u8 macid);
+int rtw89_fw_h2c_ba_cam(struct rtw89_dev *rtwdev, bool valid, u8 macid,
+ struct ieee80211_ampdu_params *params);
+int rtw89_fw_h2c_lps_parm(struct rtw89_dev *rtwdev,
+ struct rtw89_lps_parm *lps_param);
+struct sk_buff *rtw89_fw_h2c_alloc_skb_with_hdr(u32 len);
+struct sk_buff *rtw89_fw_h2c_alloc_skb_no_hdr(u32 len);
+int rtw89_fw_msg_reg(struct rtw89_dev *rtwdev,
+ struct rtw89_mac_h2c_info *h2c_info,
+ struct rtw89_mac_c2h_info *c2h_info);
+int rtw89_fw_h2c_fw_log(struct rtw89_dev *rtwdev, bool enable);
+void rtw89_fw_st_dbg_dump(struct rtw89_dev *rtwdev);
+
+static __always_inline void RTW89_SET_FWCMD(u8 *cmd, u32 val, u8 offset, u32 mask)
+{
+ u32 *cmd32 = (u32 *)cmd;
+
+ le32p_replace_bits((__le32 *)(cmd32 + offset), val, mask);
+}
+
+static __always_inline void RTW89_SET_FWCMD_UA8(u8 *cmd, u8 val, u8 offset, u32 mask)
+{
+ u8p_replace_bits(cmd + offset, val, mask);
+}
+
+static __always_inline void RTW89_SET_FWCMD_UA16(u8 *cmd, u16 val, u8 offset, u32 mask)
+{
+ le16p_replace_bits((__le16 *)(cmd + offset), val, mask);
+}
+
+static __always_inline void RTW89_SET_FWCMD_UA32(u8 *cmd, u32 val, u8 offset, u32 mask)
+{
+ le32p_replace_bits((__le32 *)(cmd + offset), val, mask);
+}
+
+#endif
--
2.25.1

2021-06-18 06:50:43

by Pkshih

[permalink] [raw]
Subject: [PATCH 09/24] rtw89: add pci files

DMA data to/from device via TX/RX rings. There are totally 13 TX rings --
8 AC rings, 2 MGMT rings, 2 HI rings and one FW CMD ring. There are
totally 2 RX rings -- one is RX ring, and the other is report ring.

FW CMD TX ring is a special TX ring that is used to download firmware or
send H2C to firmware. The other TX rings are used to send normal data(skb),
and if the skb is sent, RX report ring will receive a report that tells
driver to free the skb. Normal RX packets, C2H and PPDU status are
received by RX rings accordingly.

One TX ring contains TX BD and TX WD. Both are allocated by
dma_alloc_coherent(), but their size can be different (#TX_BD <= #TX_WD).
When a skb is going to send, use a tx_req to hold skb and corresponding
description data, and then fill the request data to a free TX BD and a
free TX WD, finally kick off the TX BD. When TX BD DMA is done, read index
of register is increasing, and then TX BD can be freed, but free WD and skb
until report is received.

Since CMD TX ring doesn't send normal data, it doesn't need to wait for
sending data in air. Therefore, TX BD, TX WD and cmd skb can be freed, when
read index of register is increasing.

One entry of RX ring contains a RX BD and a skb. The skb is used to store
RX WD and frame. RX BD is allocated by dma_alloc_coherent(), and fill RX
size and point RX DMA address to skb->data. If size of RX packet exceeds
filled RX size, the packet will be divided into multiple RX BD with FS and
LS bit.

TX BD OK interrupt for normal AC ring is masked out, because it causes
frequent context switch. We defer to reclaim it until report is received.
The exception is FW CMD ring that doesn't wait for report, so we reclaim
it in interrupt.

With wake TX queue, we submit a bunch of TX skb in one go, and then kick
off DMA by writing write index of TX BD after the TX BD/WD/skb are filled.

Signed-off-by: Ping-Ke Shih <[email protected]>
---
drivers/net/wireless/realtek/rtw89/pci.c | 3030 ++++++++++++++++++++++
drivers/net/wireless/realtek/rtw89/pci.h | 601 +++++
2 files changed, 3631 insertions(+)
create mode 100644 drivers/net/wireless/realtek/rtw89/pci.c
create mode 100644 drivers/net/wireless/realtek/rtw89/pci.h

diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c
new file mode 100644
index 000000000000..155f463602da
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw89/pci.c
@@ -0,0 +1,3030 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Copyright(c) 2020 Realtek Corporation
+ */
+
+#include <linux/pci.h>
+
+#include "mac.h"
+#include "pci.h"
+#include "reg.h"
+#include "ser.h"
+
+static bool rtw89_pci_disable_clkreq;
+static bool rtw89_pci_disable_aspm_l1;
+static bool rtw89_pci_disable_l1ss;
+module_param_named(disable_clkreq, rtw89_pci_disable_clkreq, bool, 0644);
+module_param_named(disable_aspm_l1, rtw89_pci_disable_aspm_l1, bool, 0644);
+module_param_named(disable_aspm_l1ss, rtw89_pci_disable_l1ss, bool, 0644);
+MODULE_PARM_DESC(disable_clkreq, "Set Y to disable PCI clkreq support");
+MODULE_PARM_DESC(disable_aspm_l1, "Set Y to disable PCI ASPM L1 support");
+MODULE_PARM_DESC(disable_aspm_l1ss, "Set Y to disable PCI L1SS support");
+
+static int rtw89_pci_rst_bdram_pcie(struct rtw89_dev *rtwdev)
+{
+ u32 val;
+ int ret;
+
+ rtw89_write32(rtwdev, R_AX_PCIE_INIT_CFG1,
+ rtw89_read32(rtwdev, R_AX_PCIE_INIT_CFG1) | B_AX_RST_BDRAM);
+
+ ret = read_poll_timeout_atomic(rtw89_read32, val, !(val & B_AX_RST_BDRAM),
+ 1, RTW89_PCI_POLL_BDRAM_RST_CNT, false,
+ rtwdev, R_AX_PCIE_INIT_CFG1);
+
+ if (ret)
+ return -EBUSY;
+
+ return 0;
+}
+
+static u32 rtw89_pci_dma_recalc(struct rtw89_dev *rtwdev,
+ struct rtw89_pci_dma_ring *bd_ring,
+ u32 cur_idx, bool tx)
+{
+ u32 cnt, cur_rp, wp, rp, len;
+
+ rp = bd_ring->rp;
+ wp = bd_ring->wp;
+ len = bd_ring->len;
+
+ cur_rp = FIELD_GET(TXBD_HW_IDX_MASK, cur_idx);
+ if (tx)
+ cnt = cur_rp >= rp ? cur_rp - rp : len - (rp - cur_rp);
+ else
+ cnt = cur_rp >= wp ? cur_rp - wp : len - (wp - cur_rp);
+
+ bd_ring->rp = cur_rp;
+
+ return cnt;
+}
+
+static u32 rtw89_pci_txbd_recalc(struct rtw89_dev *rtwdev,
+ struct rtw89_pci_tx_ring *tx_ring)
+{
+ struct rtw89_pci_dma_ring *bd_ring = &tx_ring->bd_ring;
+ u32 addr_idx = bd_ring->addr_idx;
+ u32 cnt, idx;
+
+ idx = rtw89_read32(rtwdev, addr_idx);
+ cnt = rtw89_pci_dma_recalc(rtwdev, bd_ring, idx, true);
+
+ return cnt;
+}
+
+static void rtw89_pci_release_fwcmd(struct rtw89_dev *rtwdev,
+ struct rtw89_pci *rtwpci,
+ u32 cnt)
+{
+ struct rtw89_pci_tx_data *tx_data;
+ struct sk_buff *skb;
+
+ while (cnt--) {
+ skb = skb_dequeue(&rtwpci->h2c_queue);
+ if (!skb) {
+ rtw89_err(rtwdev, "failed to release fwcmd\n");
+ return;
+ }
+
+ tx_data = RTW89_PCI_TX_SKB_CB(skb);
+ dma_unmap_single(&rtwpci->pdev->dev, tx_data->dma, skb->len,
+ DMA_TO_DEVICE);
+ dev_kfree_skb_any(skb);
+ }
+}
+
+static void rtw89_pci_isr_txch_dma(struct rtw89_dev *rtwdev,
+ struct rtw89_pci *rtwpci,
+ enum rtw89_tx_channel txch)
+{
+ struct rtw89_pci_tx_ring *tx_ring = &rtwpci->tx_rings[txch];
+ u32 cnt;
+
+ if (txch != RTW89_TXCH_CH12)
+ return;
+
+ spin_lock_bh(&rtwpci->trx_lock);
+
+ cnt = rtw89_pci_txbd_recalc(rtwdev, tx_ring);
+ if (!cnt)
+ rtw89_debug(rtwdev, RTW89_DBG_TXRX,
+ "No fwcmd TXBD consumed after DMA kicked off\n");
+ rtw89_pci_release_fwcmd(rtwdev, rtwpci, cnt);
+
+ spin_unlock_bh(&rtwpci->trx_lock);
+}
+
+static u32 rtw89_pci_rxbd_recalc(struct rtw89_dev *rtwdev,
+ struct rtw89_pci_rx_ring *rx_ring)
+{
+ struct rtw89_pci_dma_ring *bd_ring = &rx_ring->bd_ring;
+ u32 addr_idx = bd_ring->addr_idx;
+ u32 cnt, idx;
+
+ idx = rtw89_read32(rtwdev, addr_idx);
+ cnt = rtw89_pci_dma_recalc(rtwdev, bd_ring, idx, false);
+
+ return cnt;
+}
+
+static void rtw89_pci_sync_skb_for_cpu(struct rtw89_dev *rtwdev,
+ struct sk_buff *skb)
+{
+ struct rtw89_pci_rx_info *rx_info;
+ dma_addr_t dma;
+
+ rx_info = RTW89_PCI_RX_SKB_CB(skb);
+ dma = rx_info->dma;
+ dma_sync_single_for_cpu(rtwdev->dev, dma, RTW89_PCI_RX_BUF_SIZE,
+ DMA_FROM_DEVICE);
+}
+
+static void rtw89_pci_sync_skb_for_device(struct rtw89_dev *rtwdev,
+ struct sk_buff *skb)
+{
+ struct rtw89_pci_rx_info *rx_info;
+ dma_addr_t dma;
+
+ rx_info = RTW89_PCI_RX_SKB_CB(skb);
+ dma = rx_info->dma;
+ dma_sync_single_for_device(rtwdev->dev, dma, RTW89_PCI_RX_BUF_SIZE,
+ DMA_FROM_DEVICE);
+}
+
+static int rtw89_pci_rxbd_info_update(struct rtw89_dev *rtwdev,
+ struct sk_buff *skb)
+{
+ struct rtw89_pci_rxbd_info *rxbd_info;
+ struct rtw89_pci_rx_info *rx_info = RTW89_PCI_RX_SKB_CB(skb);
+
+ rxbd_info = (struct rtw89_pci_rxbd_info *)skb->data;
+ rx_info->fs = le32_get_bits(rxbd_info->dword, RTW89_PCI_RXBD_FS);
+ rx_info->ls = le32_get_bits(rxbd_info->dword, RTW89_PCI_RXBD_LS);
+ rx_info->len = le32_get_bits(rxbd_info->dword, RTW89_PCI_RXBD_WRITE_SIZE);
+ rx_info->tag = le32_get_bits(rxbd_info->dword, RTW89_PCI_RXBD_TAG);
+
+ return 0;
+}
+
+static bool
+rtw89_skb_put_rx_data(struct rtw89_dev *rtwdev, bool fs, bool ls,
+ struct sk_buff *new,
+ const struct sk_buff *skb, u32 offset,
+ const struct rtw89_pci_rx_info *rx_info,
+ const struct rtw89_rx_desc_info *desc_info)
+{
+ u32 copy_len = rx_info->len - offset;
+
+ if (unlikely(skb_tailroom(new) < copy_len)) {
+ rtw89_debug(rtwdev, RTW89_DBG_TXRX,
+ "invalid rx data length bd_len=%d desc_len=%d offset=%d (fs=%d ls=%d)\n",
+ rx_info->len, desc_info->pkt_size, offset, fs, ls);
+ rtw89_hex_dump(rtwdev, RTW89_DBG_TXRX, "rx_data: ",
+ skb->data, rx_info->len);
+ /* length of a single segment skb is desc_info->pkt_size */
+ if (fs && ls) {
+ copy_len = desc_info->pkt_size;
+ } else {
+ rtw89_info(rtwdev, "drop rx data due to invalid length\n");
+ return false;
+ }
+ }
+
+ skb_put_data(new, skb->data + offset, copy_len);
+
+ return true;
+}
+
+static u32 rtw89_pci_rxbd_deliver_skbs(struct rtw89_dev *rtwdev,
+ struct rtw89_pci_rx_ring *rx_ring)
+{
+ struct rtw89_pci_dma_ring *bd_ring = &rx_ring->bd_ring;
+ struct rtw89_pci_rx_info *rx_info;
+ struct rtw89_rx_desc_info *desc_info = &rx_ring->diliver_desc;
+ struct sk_buff *new = rx_ring->diliver_skb;
+ struct sk_buff *skb;
+ u32 rxinfo_size = sizeof(struct rtw89_pci_rxbd_info);
+ u32 offset;
+ u32 cnt = 1;
+ bool fs, ls;
+ int ret;
+
+ skb = rx_ring->buf[bd_ring->wp];
+ rtw89_pci_sync_skb_for_cpu(rtwdev, skb);
+
+ ret = rtw89_pci_rxbd_info_update(rtwdev, skb);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to update %d RXBD info: %d\n",
+ bd_ring->wp, ret);
+ goto err_sync_device;
+ }
+
+ rx_info = RTW89_PCI_RX_SKB_CB(skb);
+ fs = rx_info->fs;
+ ls = rx_info->ls;
+
+ if (fs) {
+ if (new) {
+ rtw89_err(rtwdev, "skb should not be ready before first segment start\n");
+ goto err_sync_device;
+ }
+ if (desc_info->ready) {
+ rtw89_warn(rtwdev, "desc info should not be ready before first segment start\n");
+ goto err_sync_device;
+ }
+
+ rtw89_core_query_rxdesc(rtwdev, desc_info, skb->data, rxinfo_size);
+
+ new = dev_alloc_skb(desc_info->pkt_size);
+ if (!new)
+ goto err_sync_device;
+
+ rx_ring->diliver_skb = new;
+
+ /* first segment has RX desc */
+ offset = desc_info->offset;
+ offset += desc_info->long_rxdesc ? sizeof(struct rtw89_rxdesc_long) :
+ sizeof(struct rtw89_rxdesc_short);
+ } else {
+ offset = sizeof(struct rtw89_pci_rxbd_info);
+ if (!new) {
+ rtw89_warn(rtwdev, "no last skb\n");
+ goto err_sync_device;
+ }
+ }
+ if (!rtw89_skb_put_rx_data(rtwdev, fs, ls, new, skb, offset, rx_info, desc_info))
+ goto err_sync_device;
+ rtw89_pci_sync_skb_for_device(rtwdev, skb);
+ rtw89_pci_rxbd_increase(rx_ring, 1);
+
+ if (!desc_info->ready) {
+ rtw89_warn(rtwdev, "no rx desc information\n");
+ goto err_free_resource;
+ }
+ if (ls) {
+ rtw89_core_rx(rtwdev, desc_info, new);
+ rx_ring->diliver_skb = NULL;
+ desc_info->ready = false;
+ }
+
+ return cnt;
+
+err_sync_device:
+ rtw89_pci_sync_skb_for_device(rtwdev, skb);
+ rtw89_pci_rxbd_increase(rx_ring, 1);
+err_free_resource:
+ if (new)
+ dev_kfree_skb_any(new);
+ rx_ring->diliver_skb = NULL;
+ desc_info->ready = false;
+
+ return cnt;
+}
+
+static void rtw89_pci_rxbd_deliver(struct rtw89_dev *rtwdev,
+ struct rtw89_pci_rx_ring *rx_ring,
+ u32 cnt)
+{
+ struct rtw89_pci_dma_ring *bd_ring = &rx_ring->bd_ring;
+ u32 rx_cnt;
+
+ while (cnt && rtwdev->napi_budget_countdown > 0) {
+ rx_cnt = rtw89_pci_rxbd_deliver_skbs(rtwdev, rx_ring);
+ if (!rx_cnt) {
+ rtw89_err(rtwdev, "failed to deliver RXBD skb\n");
+
+ /* skip the rest RXBD bufs */
+ rtw89_pci_rxbd_increase(rx_ring, cnt);
+ break;
+ }
+
+ cnt -= rx_cnt;
+ }
+
+ rtw89_write16(rtwdev, bd_ring->addr_idx, bd_ring->wp);
+}
+
+static int rtw89_pci_poll_rxq_dma(struct rtw89_dev *rtwdev,
+ struct rtw89_pci *rtwpci, int budget)
+{
+ struct rtw89_pci_rx_ring *rx_ring;
+ u32 cnt;
+
+ rx_ring = &rtwpci->rx_rings[RTW89_RXCH_RXQ];
+
+ cnt = rtw89_pci_rxbd_recalc(rtwdev, rx_ring);
+ if (!cnt)
+ return 0;
+
+ rtwdev->napi_budget_countdown = budget;
+ cnt = min_t(u32, budget, cnt);
+
+ rtw89_pci_rxbd_deliver(rtwdev, rx_ring, cnt);
+
+ /* In case of flushing pending SKBs, the countdown may exceed. */
+ if (rtwdev->napi_budget_countdown <= 0)
+ return budget;
+
+ return budget - rtwdev->napi_budget_countdown;
+}
+
+static void rtw89_pci_tx_status(struct rtw89_dev *rtwdev,
+ struct rtw89_pci_tx_ring *tx_ring,
+ struct sk_buff *skb, u8 tx_status)
+{
+ struct ieee80211_tx_info *info;
+
+ info = IEEE80211_SKB_CB(skb);
+ ieee80211_tx_info_clear_status(info);
+
+ if (info->flags & IEEE80211_TX_CTL_NO_ACK)
+ info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED;
+ if (tx_status == RTW89_TX_DONE) {
+ info->flags |= IEEE80211_TX_STAT_ACK;
+ tx_ring->tx_acked++;
+ } else {
+ if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)
+ rtw89_debug(rtwdev, RTW89_DBG_FW,
+ "failed to TX of status %x\n", tx_status);
+ switch (tx_status) {
+ case RTW89_TX_RETRY_LIMIT:
+ tx_ring->tx_retry_lmt++;
+ break;
+ case RTW89_TX_LIFE_TIME:
+ tx_ring->tx_life_time++;
+ break;
+ case RTW89_TX_MACID_DROP:
+ tx_ring->tx_mac_id_drop++;
+ break;
+ default:
+ rtw89_warn(rtwdev, "invalid TX status %x\n", tx_status);
+ break;
+ }
+ }
+
+ ieee80211_tx_status_ni(rtwdev->hw, skb);
+}
+
+static void rtw89_pci_reclaim_txbd(struct rtw89_dev *rtwdev, struct rtw89_pci_tx_ring *tx_ring)
+{
+ struct rtw89_pci_tx_wd *txwd;
+ u32 cnt;
+
+ cnt = rtw89_pci_txbd_recalc(rtwdev, tx_ring);
+ while (cnt--) {
+ txwd = list_first_entry_or_null(&tx_ring->busy_pages, struct rtw89_pci_tx_wd, list);
+ if (!txwd) {
+ rtw89_warn(rtwdev, "No busy txwd pages available\n");
+ break;
+ }
+
+ list_del_init(&txwd->list);
+ }
+}
+
+static void rtw89_pci_release_busy_txwd(struct rtw89_dev *rtwdev,
+ struct rtw89_pci_tx_ring *tx_ring)
+{
+ struct rtw89_pci_tx_wd_ring *wd_ring = &tx_ring->wd_ring;
+ struct rtw89_pci_tx_wd *txwd;
+ int i;
+
+ for (i = 0; i < wd_ring->page_num; i++) {
+ txwd = list_first_entry_or_null(&tx_ring->busy_pages, struct rtw89_pci_tx_wd, list);
+ if (!txwd)
+ break;
+
+ list_del_init(&txwd->list);
+ }
+}
+
+static void rtw89_pci_release_txwd_skb(struct rtw89_dev *rtwdev,
+ struct rtw89_pci_tx_ring *tx_ring,
+ struct rtw89_pci_tx_wd *txwd, u16 seq,
+ u8 tx_status)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ struct rtw89_pci_tx_data *tx_data;
+ struct sk_buff *skb, *tmp;
+ u8 txch = tx_ring->txch;
+
+ if (!list_empty(&txwd->list)) {
+ rtw89_warn(rtwdev, "queue %d txwd %d is not idle\n",
+ txch, seq);
+ return;
+ }
+
+ /* currently, support for only one frame */
+ if (skb_queue_len(&txwd->queue) != 1) {
+ rtw89_warn(rtwdev, "empty pending queue %d page %d\n",
+ txch, seq);
+ return;
+ }
+
+ skb_queue_walk_safe(&txwd->queue, skb, tmp) {
+ skb_unlink(skb, &txwd->queue);
+
+ tx_data = RTW89_PCI_TX_SKB_CB(skb);
+ dma_unmap_single(&rtwpci->pdev->dev, tx_data->dma, skb->len,
+ DMA_TO_DEVICE);
+
+ rtw89_pci_tx_status(rtwdev, tx_ring, skb, tx_status);
+ }
+
+ rtw89_pci_enqueue_txwd(tx_ring, txwd);
+}
+
+static void rtw89_pci_release_rpp(struct rtw89_dev *rtwdev,
+ struct rtw89_pci_rpp_fmt *rpp)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ struct rtw89_pci_tx_ring *tx_ring;
+ struct rtw89_pci_tx_wd_ring *wd_ring;
+ struct rtw89_pci_tx_wd *txwd;
+ u16 seq;
+ u8 qsel, tx_status, txch;
+
+ seq = le32_get_bits(rpp->dword, RTW89_PCI_RPP_SEQ);
+ qsel = le32_get_bits(rpp->dword, RTW89_PCI_RPP_QSEL);
+ tx_status = le32_get_bits(rpp->dword, RTW89_PCI_RPP_TX_STATUS);
+ txch = rtw89_core_get_ch_dma(rtwdev, qsel);
+
+ if (txch == RTW89_TXCH_CH12) {
+ rtw89_warn(rtwdev, "should no fwcmd release report\n");
+ return;
+ }
+
+ tx_ring = &rtwpci->tx_rings[txch];
+ rtw89_pci_reclaim_txbd(rtwdev, tx_ring);
+ wd_ring = &tx_ring->wd_ring;
+ txwd = &wd_ring->pages[seq];
+
+ rtw89_pci_release_txwd_skb(rtwdev, tx_ring, txwd, seq, tx_status);
+}
+
+static void rtw89_pci_release_pending_txwd_skb(struct rtw89_dev *rtwdev,
+ struct rtw89_pci_tx_ring *tx_ring)
+{
+ struct rtw89_pci_tx_wd_ring *wd_ring = &tx_ring->wd_ring;
+ struct rtw89_pci_tx_wd *txwd;
+ int i;
+
+ for (i = 0; i < wd_ring->page_num; i++) {
+ txwd = &wd_ring->pages[i];
+
+ if (!list_empty(&txwd->list))
+ continue;
+
+ rtw89_pci_release_txwd_skb(rtwdev, tx_ring, txwd, i, RTW89_TX_MACID_DROP);
+ }
+}
+
+static u32 rtw89_pci_release_tx_skbs(struct rtw89_dev *rtwdev,
+ struct rtw89_pci_rx_ring *rx_ring,
+ u32 max_cnt)
+{
+ struct rtw89_pci_dma_ring *bd_ring = &rx_ring->bd_ring;
+ struct rtw89_pci_rx_info *rx_info;
+ struct rtw89_pci_rpp_fmt *rpp;
+ struct rtw89_rx_desc_info desc_info = {};
+ struct sk_buff *skb;
+ u32 cnt = 0;
+ u32 rpp_size = sizeof(struct rtw89_pci_rpp_fmt);
+ u32 rxinfo_size = sizeof(struct rtw89_pci_rxbd_info);
+ u32 offset;
+ int ret;
+
+ skb = rx_ring->buf[bd_ring->wp];
+ rtw89_pci_sync_skb_for_cpu(rtwdev, skb);
+
+ ret = rtw89_pci_rxbd_info_update(rtwdev, skb);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to update %d RXBD info: %d\n",
+ bd_ring->wp, ret);
+ goto err_sync_device;
+ }
+
+ rx_info = RTW89_PCI_RX_SKB_CB(skb);
+ if (!rx_info->fs || !rx_info->ls) {
+ rtw89_err(rtwdev, "cannot process RP frame not set FS/LS\n");
+ return cnt;
+ }
+
+ rtw89_core_query_rxdesc(rtwdev, &desc_info, skb->data, rxinfo_size);
+
+ /* first segment has RX desc */
+ offset = desc_info.offset;
+ offset += desc_info.long_rxdesc ? sizeof(struct rtw89_rxdesc_long) :
+ sizeof(struct rtw89_rxdesc_short);
+ for (; offset + rpp_size <= rx_info->len; offset += rpp_size) {
+ rpp = (struct rtw89_pci_rpp_fmt *)(skb->data + offset);
+ rtw89_pci_release_rpp(rtwdev, rpp);
+ }
+
+ rtw89_pci_sync_skb_for_device(rtwdev, skb);
+ rtw89_pci_rxbd_increase(rx_ring, 1);
+ cnt++;
+
+ return cnt;
+
+err_sync_device:
+ rtw89_pci_sync_skb_for_device(rtwdev, skb);
+ return 0;
+}
+
+static void rtw89_pci_release_tx(struct rtw89_dev *rtwdev,
+ struct rtw89_pci_rx_ring *rx_ring,
+ u32 cnt)
+{
+ struct rtw89_pci_dma_ring *bd_ring = &rx_ring->bd_ring;
+ u32 release_cnt;
+
+ while (cnt) {
+ release_cnt = rtw89_pci_release_tx_skbs(rtwdev, rx_ring, cnt);
+ if (!release_cnt) {
+ rtw89_err(rtwdev, "failed to release TX skbs\n");
+
+ /* skip the rest RXBD bufs */
+ rtw89_pci_rxbd_increase(rx_ring, cnt);
+ break;
+ }
+
+ cnt -= release_cnt;
+ }
+
+ rtw89_write16(rtwdev, bd_ring->addr_idx, bd_ring->wp);
+}
+
+static void rtw89_pci_isr_rpq_dma(struct rtw89_dev *rtwdev,
+ struct rtw89_pci *rtwpci)
+{
+ struct rtw89_pci_rx_ring *rx_ring;
+ u32 cnt;
+
+ rx_ring = &rtwpci->rx_rings[RTW89_RXCH_RPQ];
+
+ spin_lock_bh(&rtwpci->trx_lock);
+
+ cnt = rtw89_pci_rxbd_recalc(rtwdev, rx_ring);
+ if (cnt == 0)
+ goto out_unlock;
+
+ rtw89_pci_release_tx(rtwdev, rx_ring, cnt);
+
+out_unlock:
+ spin_unlock_bh(&rtwpci->trx_lock);
+}
+
+static void rtw89_pci_isr_rxd_unavail(struct rtw89_dev *rtwdev,
+ struct rtw89_pci *rtwpci)
+{
+ struct rtw89_pci_rx_ring *rx_ring;
+ struct rtw89_pci_dma_ring *bd_ring;
+ u32 reg_idx;
+ int i;
+
+ for (i = 0; i < RTW89_RXCH_NUM; i++) {
+ rx_ring = &rtwpci->rx_rings[i];
+ bd_ring = &rx_ring->bd_ring;
+
+ reg_idx = rtw89_read32(rtwdev, bd_ring->addr_idx);
+
+ rtw89_warn(rtwdev, "%d RXD unavailable, idx=0x%08x, len=%d\n",
+ i, reg_idx, bd_ring->len);
+ }
+}
+
+static void rtw89_pci_clear_intrs(struct rtw89_dev *rtwdev,
+ struct rtw89_pci *rtwpci)
+{
+ rtw89_write32(rtwdev, R_AX_HISR0, rtwpci->halt_c2h_isrs);
+ rtw89_write32(rtwdev, R_AX_PCIE_HISR00, rtwpci->isrs[0]);
+ rtw89_write32(rtwdev, R_AX_PCIE_HISR10, rtwpci->isrs[1]);
+}
+
+static void rtw89_pci_recognize_intrs(struct rtw89_dev *rtwdev,
+ struct rtw89_pci *rtwpci)
+{
+ rtwpci->halt_c2h_isrs = rtw89_read32(rtwdev, R_AX_HISR0) & rtwpci->halt_c2h_intrs;
+ rtwpci->isrs[0] = rtw89_read32(rtwdev, R_AX_PCIE_HISR00) & rtwpci->intrs[0];
+ rtwpci->isrs[1] = rtw89_read32(rtwdev, R_AX_PCIE_HISR10) & rtwpci->intrs[1];
+}
+
+static void rtw89_pci_enable_intr(struct rtw89_dev *rtwdev,
+ struct rtw89_pci *rtwpci)
+{
+ rtw89_write32(rtwdev, R_AX_HIMR0, rtwpci->halt_c2h_intrs);
+ rtw89_write32(rtwdev, R_AX_PCIE_HIMR00, rtwpci->intrs[0]);
+ rtw89_write32(rtwdev, R_AX_PCIE_HIMR10, rtwpci->intrs[1]);
+}
+
+static void rtw89_pci_enable_intr_unmask0(struct rtw89_dev *rtwdev,
+ struct rtw89_pci *rtwpci, u32 unmask0)
+{
+ rtwpci->intrs[0] &= ~unmask0;
+ rtw89_pci_enable_intr(rtwdev, rtwpci);
+}
+
+static void rtw89_pci_enable_intr_mask0(struct rtw89_dev *rtwdev,
+ struct rtw89_pci *rtwpci, u32 mask0)
+{
+ rtwpci->intrs[0] |= mask0;
+ rtw89_pci_enable_intr(rtwdev, rtwpci);
+}
+
+static void rtw89_pci_disable_intr(struct rtw89_dev *rtwdev,
+ struct rtw89_pci *rtwpci)
+{
+ rtw89_write32(rtwdev, R_AX_HIMR0, 0);
+ rtw89_write32(rtwdev, R_AX_PCIE_HIMR00, 0);
+ rtw89_write32(rtwdev, R_AX_PCIE_HIMR10, 0);
+}
+
+static void rtw89_pci_try_napi_schedule(struct rtw89_dev *rtwdev, u32 *unmask0_rx)
+{
+ if (*unmask0_rx && !napi_reschedule(&rtwdev->napi)) {
+ /* if can't invoke napi, RX_IMR must be off already */
+ *unmask0_rx = 0;
+ }
+}
+
+static irqreturn_t rtw89_pci_interrupt_threadfn(int irq, void *dev)
+{
+ struct rtw89_dev *rtwdev = dev;
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ u32 isrs[2];
+ unsigned long flags;
+ u32 unmask0_rx = 0;
+
+ isrs[0] = rtwpci->isrs[0];
+ isrs[1] = rtwpci->isrs[1];
+
+ /* TX ISR */
+ if (isrs[0] & B_AX_TXDMA_CH12_INT)
+ rtw89_pci_isr_txch_dma(rtwdev, rtwpci, RTW89_TXCH_CH12);
+
+ /* RX ISR */
+ if (isrs[0] & (B_AX_RXDMA_INT | B_AX_RXP1DMA_INT))
+ unmask0_rx = B_AX_RXDMA_INTS_MASK;
+ if (isrs[0] & B_AX_RPQDMA_INT)
+ rtw89_pci_isr_rpq_dma(rtwdev, rtwpci);
+ if (isrs[0] & B_AX_RDU_INT) {
+ rtw89_pci_isr_rxd_unavail(rtwdev, rtwpci);
+ unmask0_rx = B_AX_RXDMA_INTS_MASK;
+ }
+
+ if (rtwpci->halt_c2h_isrs & B_AX_HALT_C2H_INT_EN)
+ rtw89_ser_notify(rtwdev, rtw89_mac_get_err_status(rtwdev));
+
+ /* invoke napi and disable rx_imr within bh_disable, because we must
+ * ensure napi_poll re-enable rx_imr after this.
+ */
+ local_bh_disable();
+ spin_lock_irqsave(&rtwpci->irq_lock, flags);
+ rtw89_pci_try_napi_schedule(rtwdev, &unmask0_rx);
+ if (rtwpci->running) {
+ rtw89_pci_clear_intrs(rtwdev, rtwpci);
+ rtw89_pci_enable_intr_unmask0(rtwdev, rtwpci, unmask0_rx);
+ }
+ spin_unlock_irqrestore(&rtwpci->irq_lock, flags);
+ local_bh_enable();
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t rtw89_pci_interrupt_handler(int irq, void *dev)
+{
+ struct rtw89_dev *rtwdev = dev;
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ unsigned long flags;
+
+ /* If interrupt event is on the road, it is still trigger interrupt
+ * even we have done pci_stop() to turn off IMR.
+ */
+ if (!rtwpci->running)
+ return IRQ_HANDLED;
+
+ /* Disable interrupt here to avoid more interrupts being issued before
+ * the threadfn ends.
+ */
+ spin_lock_irqsave(&rtwpci->irq_lock, flags);
+ rtw89_pci_disable_intr(rtwdev, rtwpci);
+ rtw89_pci_recognize_intrs(rtwdev, rtwpci);
+ spin_unlock_irqrestore(&rtwpci->irq_lock, flags);
+
+ return IRQ_WAKE_THREAD;
+}
+
+#define case_TXCHADDRS(txch) \
+ case RTW89_TXCH_##txch: \
+ *addr_num = R_AX_##txch##_TXBD_NUM; \
+ *addr_idx = R_AX_##txch##_TXBD_IDX; \
+ *addr_bdram = R_AX_##txch##_BDRAM_CTRL; \
+ *addr_desa_l = R_AX_##txch##_TXBD_DESA_L; \
+ *addr_desa_h = R_AX_##txch##_TXBD_DESA_H; \
+ break
+
+static int rtw89_pci_get_txch_addrs(enum rtw89_tx_channel txch,
+ u32 *addr_num,
+ u32 *addr_idx,
+ u32 *addr_bdram,
+ u32 *addr_desa_l,
+ u32 *addr_desa_h)
+{
+ switch (txch) {
+ case_TXCHADDRS(ACH0);
+ case_TXCHADDRS(ACH1);
+ case_TXCHADDRS(ACH2);
+ case_TXCHADDRS(ACH3);
+ case_TXCHADDRS(ACH4);
+ case_TXCHADDRS(ACH5);
+ case_TXCHADDRS(ACH6);
+ case_TXCHADDRS(ACH7);
+ case_TXCHADDRS(CH8);
+ case_TXCHADDRS(CH9);
+ case_TXCHADDRS(CH10);
+ case_TXCHADDRS(CH11);
+ case_TXCHADDRS(CH12);
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#undef case_TXCHADDRS
+
+#define case_RXCHADDRS(rxch) \
+ case RTW89_RXCH_##rxch: \
+ *addr_num = R_AX_##rxch##_RXBD_NUM; \
+ *addr_idx = R_AX_##rxch##_RXBD_IDX; \
+ *addr_desa_l = R_AX_##rxch##_RXBD_DESA_L; \
+ *addr_desa_h = R_AX_##rxch##_RXBD_DESA_H; \
+ break
+
+static int rtw89_pci_get_rxch_addrs(enum rtw89_rx_channel rxch,
+ u32 *addr_num,
+ u32 *addr_idx,
+ u32 *addr_desa_l,
+ u32 *addr_desa_h)
+{
+ switch (rxch) {
+ case_RXCHADDRS(RXQ);
+ case_RXCHADDRS(RPQ);
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#undef case_RXCHADDRS
+
+static u32 rtw89_pci_get_avail_txbd_num(struct rtw89_pci_tx_ring *ring)
+{
+ struct rtw89_pci_dma_ring *bd_ring = &ring->bd_ring;
+
+ /* reserved 1 desc check ring is full or not */
+ if (bd_ring->rp > bd_ring->wp)
+ return bd_ring->rp - bd_ring->wp - 1;
+
+ return bd_ring->len - (bd_ring->wp - bd_ring->rp) - 1;
+}
+
+static u32 rtw89_pci_check_and_reclaim_tx_resource(struct rtw89_dev *rtwdev,
+ u8 txch)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ struct rtw89_pci_tx_ring *tx_ring = &rtwpci->tx_rings[txch];
+ struct rtw89_pci_tx_wd_ring *wd_ring = &tx_ring->wd_ring;
+ u32 bd_cnt, wd_cnt, min_cnt = 0;
+ struct rtw89_pci_rx_ring *rx_ring;
+ u32 cnt;
+
+ rx_ring = &rtwpci->rx_rings[RTW89_RXCH_RPQ];
+
+ spin_lock_bh(&rtwpci->trx_lock);
+ bd_cnt = rtw89_pci_get_avail_txbd_num(tx_ring);
+ wd_cnt = wd_ring->curr_num;
+
+ if (wd_cnt == 0 || bd_cnt == 0) {
+ cnt = rtw89_pci_rxbd_recalc(rtwdev, rx_ring);
+ if (!cnt)
+ goto out_unlock;
+ rtw89_pci_release_tx(rtwdev, rx_ring, cnt);
+ }
+
+ bd_cnt = rtw89_pci_get_avail_txbd_num(tx_ring);
+ wd_cnt = wd_ring->curr_num;
+ min_cnt = min(bd_cnt, wd_cnt);
+ if (min_cnt == 0)
+ rtw89_warn(rtwdev, "still no tx resource after reclaim\n");
+
+out_unlock:
+ spin_unlock_bh(&rtwpci->trx_lock);
+
+ return min_cnt;
+}
+
+static void __rtw89_pci_tx_kick_off(struct rtw89_dev *rtwdev, struct rtw89_pci_tx_ring *tx_ring)
+{
+ struct rtw89_pci_dma_ring *bd_ring = &tx_ring->bd_ring;
+ u32 host_idx, addr;
+
+ addr = bd_ring->addr_idx;
+ host_idx = bd_ring->wp;
+ rtw89_write16(rtwdev, addr, host_idx);
+}
+
+static void rtw89_pci_tx_bd_ring_update(struct rtw89_dev *rtwdev, struct rtw89_pci_tx_ring *tx_ring,
+ int n_txbd)
+{
+ struct rtw89_pci_dma_ring *bd_ring = &tx_ring->bd_ring;
+ u32 host_idx, len;
+
+ len = bd_ring->len;
+ host_idx = bd_ring->wp + n_txbd;
+ host_idx = host_idx < len ? host_idx : host_idx - len;
+
+ bd_ring->wp = host_idx;
+}
+
+static void rtw89_pci_ops_tx_kick_off(struct rtw89_dev *rtwdev, u8 txch)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ struct rtw89_pci_tx_ring *tx_ring = &rtwpci->tx_rings[txch];
+
+ spin_lock_bh(&rtwpci->trx_lock);
+ __rtw89_pci_tx_kick_off(rtwdev, tx_ring);
+ spin_unlock_bh(&rtwpci->trx_lock);
+}
+
+static void __pci_flush_txch(struct rtw89_dev *rtwdev, u8 txch, bool drop)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ struct rtw89_pci_tx_ring *tx_ring = &rtwpci->tx_rings[txch];
+ struct rtw89_pci_dma_ring *bd_ring = &tx_ring->bd_ring;
+ u32 cur_idx, cur_rp;
+ u8 i;
+
+ /* Because the time taked by the I/O is a bit dynamic, it's hard to
+ * define a reasonable fixed total timeout to use read_poll_timeout*
+ * helper. Instead, we can ensure a reasonable polling times, so we
+ * just use for loop with udelay here.
+ */
+ for (i = 0; i < 60; i++) {
+ cur_idx = rtw89_read32(rtwdev, bd_ring->addr_idx);
+ cur_rp = FIELD_GET(TXBD_HW_IDX_MASK, cur_idx);
+ if (cur_rp == bd_ring->wp)
+ return;
+
+ udelay(1);
+ }
+
+ if (!drop)
+ rtw89_info(rtwdev, "timed out to flush pci txch: %d\n", txch);
+}
+
+static void __rtw89_pci_ops_flush_txchs(struct rtw89_dev *rtwdev, u32 txchs,
+ bool drop)
+{
+ u8 i;
+
+ for (i = 0; i < RTW89_TXCH_NUM; i++) {
+ /* It may be unnecessary to flush FWCMD queue. */
+ if (i == RTW89_TXCH_CH12)
+ continue;
+
+ if (txchs & BIT(i))
+ __pci_flush_txch(rtwdev, i, drop);
+ }
+}
+
+static void rtw89_pci_ops_flush_queues(struct rtw89_dev *rtwdev, u32 queues,
+ bool drop)
+{
+ __rtw89_pci_ops_flush_txchs(rtwdev, BIT(RTW89_TXCH_NUM) - 1, drop);
+}
+
+static int rtw89_pci_txwd_submit(struct rtw89_dev *rtwdev,
+ struct rtw89_pci_tx_ring *tx_ring,
+ struct rtw89_pci_tx_wd *txwd,
+ struct rtw89_core_tx_request *tx_req)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info;
+ struct rtw89_txwd_body *txwd_body;
+ struct rtw89_txwd_info *txwd_info;
+ struct rtw89_pci_tx_wp_info *txwp_info;
+ struct rtw89_pci_tx_addr_info_32 *txaddr_info;
+ struct pci_dev *pdev = rtwpci->pdev;
+ struct sk_buff *skb = tx_req->skb;
+ struct rtw89_pci_tx_data *tx_data = RTW89_PCI_TX_SKB_CB(skb);
+ bool en_wd_info = desc_info->en_wd_info;
+ u32 txwd_len;
+ u32 txwp_len;
+ u32 txaddr_info_len;
+ dma_addr_t dma;
+ int ret;
+
+ rtw89_core_fill_txdesc(rtwdev, desc_info, txwd->vaddr);
+
+ dma = dma_map_single(&pdev->dev, skb->data, skb->len, DMA_TO_DEVICE);
+ if (dma_mapping_error(&pdev->dev, dma)) {
+ rtw89_err(rtwdev, "failed to map skb dma data\n");
+ ret = -EBUSY;
+ goto err;
+ }
+
+ tx_data->dma = dma;
+
+ txaddr_info_len = sizeof(*txaddr_info);
+ txwp_len = sizeof(*txwp_info);
+ txwd_len = sizeof(*txwd_body);
+ txwd_len += en_wd_info ? sizeof(*txwd_info) : 0;
+
+ txwp_info = txwd->vaddr + txwd_len;
+ txwp_info->seq0 = cpu_to_le16(txwd->seq | RTW89_PCI_TXWP_VALID);
+ txwp_info->seq1 = 0;
+ txwp_info->seq2 = 0;
+ txwp_info->seq3 = 0;
+
+ tx_ring->tx_cnt++;
+ txaddr_info = txwd->vaddr + txwd_len + txwp_len;
+ txaddr_info->length = cpu_to_le16(skb->len);
+ txaddr_info->option = cpu_to_le16(RTW89_PCI_ADDR_MSDU_LS |
+ RTW89_PCI_ADDR_NUM(1));
+ txaddr_info->dma = cpu_to_le32(dma);
+
+ txwd->len = txwd_len + txwp_len + txaddr_info_len;
+
+ skb_queue_tail(&txwd->queue, skb);
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static int rtw89_pci_fwcmd_submit(struct rtw89_dev *rtwdev,
+ struct rtw89_pci_tx_ring *tx_ring,
+ struct rtw89_pci_tx_bd_32 *txbd,
+ struct rtw89_core_tx_request *tx_req)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info;
+ struct rtw89_txwd_body *txwd_body;
+ struct pci_dev *pdev = rtwpci->pdev;
+ struct sk_buff *skb = tx_req->skb;
+ struct rtw89_pci_tx_data *tx_data = RTW89_PCI_TX_SKB_CB(skb);
+ dma_addr_t dma;
+
+ txwd_body = (struct rtw89_txwd_body *)skb_push(skb, sizeof(*txwd_body));
+ memset(txwd_body, 0, sizeof(*txwd_body));
+ rtw89_core_fill_txdesc(rtwdev, desc_info, txwd_body);
+
+ dma = dma_map_single(&pdev->dev, skb->data, skb->len, DMA_TO_DEVICE);
+ if (dma_mapping_error(&pdev->dev, dma)) {
+ rtw89_err(rtwdev, "failed to map fwcmd dma data\n");
+ return -EBUSY;
+ }
+
+ tx_data->dma = dma;
+ txbd->option = cpu_to_le16(RTW89_PCI_TXBD_OPTION_LS);
+ txbd->length = cpu_to_le16(skb->len);
+ txbd->dma = cpu_to_le32(tx_data->dma);
+ skb_queue_tail(&rtwpci->h2c_queue, skb);
+
+ rtw89_pci_tx_bd_ring_update(rtwdev, tx_ring, 1);
+
+ return 0;
+}
+
+static int rtw89_pci_txbd_submit(struct rtw89_dev *rtwdev,
+ struct rtw89_pci_tx_ring *tx_ring,
+ struct rtw89_pci_tx_bd_32 *txbd,
+ struct rtw89_core_tx_request *tx_req)
+{
+ struct rtw89_pci_tx_wd *txwd;
+ int ret;
+
+ /* FWCMD queue doesn't have wd pages. Instead, it submits the CMD
+ * buffer with WD BODY only. So here we don't need to check the free
+ * pages of the wd ring.
+ */
+ if (tx_ring->txch == RTW89_TXCH_CH12)
+ return rtw89_pci_fwcmd_submit(rtwdev, tx_ring, txbd, tx_req);
+
+ txwd = rtw89_pci_dequeue_txwd(tx_ring);
+ if (!txwd) {
+ rtw89_err(rtwdev, "no available TXWD\n");
+ ret = -ENOSPC;
+ goto err;
+ }
+
+ ret = rtw89_pci_txwd_submit(rtwdev, tx_ring, txwd, tx_req);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to submit TXWD %d\n", txwd->seq);
+ goto err_enqueue_wd;
+ }
+
+ list_add_tail(&txwd->list, &tx_ring->busy_pages);
+
+ txbd->option = cpu_to_le16(RTW89_PCI_TXBD_OPTION_LS);
+ txbd->length = cpu_to_le16(txwd->len);
+ txbd->dma = cpu_to_le32(txwd->paddr);
+
+ rtw89_pci_tx_bd_ring_update(rtwdev, tx_ring, 1);
+
+ return 0;
+
+err_enqueue_wd:
+ rtw89_pci_enqueue_txwd(tx_ring, txwd);
+err:
+ return ret;
+}
+
+static int rtw89_pci_tx_write(struct rtw89_dev *rtwdev, struct rtw89_core_tx_request *tx_req,
+ u8 txch)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ struct rtw89_pci_tx_ring *tx_ring;
+ struct rtw89_pci_tx_bd_32 *txbd;
+ u32 n_avail_txbd;
+ int ret = 0;
+
+ /* check the tx type and dma channel for fw cmd queue */
+ if ((txch == RTW89_TXCH_CH12 ||
+ tx_req->tx_type == RTW89_CORE_TX_TYPE_FWCMD) &&
+ (txch != RTW89_TXCH_CH12 ||
+ tx_req->tx_type != RTW89_CORE_TX_TYPE_FWCMD)) {
+ rtw89_err(rtwdev, "only fw cmd uses dma channel 12\n");
+ return -EINVAL;
+ }
+
+ tx_ring = &rtwpci->tx_rings[txch];
+ spin_lock_bh(&rtwpci->trx_lock);
+
+ n_avail_txbd = rtw89_pci_get_avail_txbd_num(tx_ring);
+ if (n_avail_txbd == 0) {
+ rtw89_err(rtwdev, "no available TXBD\n");
+ ret = -ENOSPC;
+ goto err_unlock;
+ }
+
+ txbd = rtw89_pci_get_next_txbd(tx_ring);
+ ret = rtw89_pci_txbd_submit(rtwdev, tx_ring, txbd, tx_req);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to submit TXBD\n");
+ goto err_unlock;
+ }
+
+ spin_unlock_bh(&rtwpci->trx_lock);
+ return 0;
+
+err_unlock:
+ spin_unlock_bh(&rtwpci->trx_lock);
+ return ret;
+}
+
+static int rtw89_pci_ops_tx_write(struct rtw89_dev *rtwdev, struct rtw89_core_tx_request *tx_req)
+{
+ struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info;
+ int ret;
+
+ ret = rtw89_pci_tx_write(rtwdev, tx_req, desc_info->ch_dma);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to TX Queue %d\n", desc_info->ch_dma);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct rtw89_pci_bd_ram bd_ram_table[RTW89_TXCH_NUM] = {
+ [RTW89_TXCH_ACH0] = {.start_idx = 0, .max_num = 5, .min_num = 2},
+ [RTW89_TXCH_ACH1] = {.start_idx = 5, .max_num = 5, .min_num = 2},
+ [RTW89_TXCH_ACH2] = {.start_idx = 10, .max_num = 5, .min_num = 2},
+ [RTW89_TXCH_ACH3] = {.start_idx = 15, .max_num = 5, .min_num = 2},
+ [RTW89_TXCH_ACH4] = {.start_idx = 20, .max_num = 5, .min_num = 2},
+ [RTW89_TXCH_ACH5] = {.start_idx = 25, .max_num = 5, .min_num = 2},
+ [RTW89_TXCH_ACH6] = {.start_idx = 30, .max_num = 5, .min_num = 2},
+ [RTW89_TXCH_ACH7] = {.start_idx = 35, .max_num = 5, .min_num = 2},
+ [RTW89_TXCH_CH8] = {.start_idx = 40, .max_num = 5, .min_num = 1},
+ [RTW89_TXCH_CH9] = {.start_idx = 45, .max_num = 5, .min_num = 1},
+ [RTW89_TXCH_CH10] = {.start_idx = 50, .max_num = 5, .min_num = 1},
+ [RTW89_TXCH_CH11] = {.start_idx = 55, .max_num = 5, .min_num = 1},
+ [RTW89_TXCH_CH12] = {.start_idx = 60, .max_num = 4, .min_num = 1},
+};
+
+static void rtw89_pci_reset_trx_rings(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ struct rtw89_pci_tx_ring *tx_ring;
+ struct rtw89_pci_rx_ring *rx_ring;
+ struct rtw89_pci_dma_ring *bd_ring;
+ const struct rtw89_pci_bd_ram *bd_ram;
+ u32 addr_num;
+ u32 addr_bdram;
+ u32 addr_desa_l;
+ u32 val32;
+ int i;
+
+ for (i = 0; i < RTW89_TXCH_NUM; i++) {
+ tx_ring = &rtwpci->tx_rings[i];
+ bd_ring = &tx_ring->bd_ring;
+ bd_ram = &bd_ram_table[i];
+ addr_num = bd_ring->addr_num;
+ addr_bdram = bd_ring->addr_bdram;
+ addr_desa_l = bd_ring->addr_desa_l;
+ bd_ring->wp = 0;
+ bd_ring->rp = 0;
+
+ val32 = FIELD_PREP(BDRAM_SIDX_MASK, bd_ram->start_idx) |
+ FIELD_PREP(BDRAM_MAX_MASK, bd_ram->max_num) |
+ FIELD_PREP(BDRAM_MIN_MASK, bd_ram->min_num);
+
+ rtw89_write16(rtwdev, addr_num, bd_ring->len);
+ rtw89_write32(rtwdev, addr_bdram, val32);
+ rtw89_write32(rtwdev, addr_desa_l, bd_ring->dma);
+ }
+
+ for (i = 0; i < RTW89_RXCH_NUM; i++) {
+ rx_ring = &rtwpci->rx_rings[i];
+ bd_ring = &rx_ring->bd_ring;
+ addr_num = bd_ring->addr_num;
+ addr_desa_l = bd_ring->addr_desa_l;
+ bd_ring->wp = 0;
+ bd_ring->rp = 0;
+ rx_ring->diliver_skb = NULL;
+ rx_ring->diliver_desc.ready = false;
+
+ rtw89_write16(rtwdev, addr_num, bd_ring->len);
+ rtw89_write32(rtwdev, addr_desa_l, bd_ring->dma);
+ }
+}
+
+static void rtw89_pci_release_tx_ring(struct rtw89_dev *rtwdev,
+ struct rtw89_pci_tx_ring *tx_ring)
+{
+ rtw89_pci_release_busy_txwd(rtwdev, tx_ring);
+ rtw89_pci_release_pending_txwd_skb(rtwdev, tx_ring);
+}
+
+static void rtw89_pci_ops_reset(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ int txch;
+
+ rtw89_pci_reset_trx_rings(rtwdev);
+
+ spin_lock_bh(&rtwpci->trx_lock);
+ for (txch = 0; txch < RTW89_TXCH_NUM; txch++) {
+ if (txch == RTW89_TXCH_CH12) {
+ rtw89_pci_release_fwcmd(rtwdev, rtwpci, rtwpci->h2c_queue.qlen);
+ continue;
+ }
+ rtw89_pci_release_tx_ring(rtwdev, &rtwpci->tx_rings[txch]);
+ }
+ spin_unlock_bh(&rtwpci->trx_lock);
+}
+
+static int rtw89_pci_ops_start(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ unsigned long flags;
+
+ rtw89_core_napi_start(rtwdev);
+
+ spin_lock_irqsave(&rtwpci->irq_lock, flags);
+ rtwpci->running = true;
+ rtw89_pci_enable_intr_mask0(rtwdev, rtwpci, B_AX_RXDMA_INTS_MASK);
+ spin_unlock_irqrestore(&rtwpci->irq_lock, flags);
+
+ return 0;
+}
+
+static void rtw89_pci_ops_stop(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ struct pci_dev *pdev = rtwpci->pdev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rtwpci->irq_lock, flags);
+ rtwpci->running = false;
+ rtw89_pci_disable_intr(rtwdev, rtwpci);
+ spin_unlock_irqrestore(&rtwpci->irq_lock, flags);
+
+ synchronize_irq(pdev->irq);
+ rtw89_core_napi_stop(rtwdev);
+}
+
+static void rtw89_pci_ops_write32(struct rtw89_dev *rtwdev, u32 addr, u32 data);
+
+static u32 rtw89_pci_ops_read32_cmac(struct rtw89_dev *rtwdev, u32 addr)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ u32 val = readl(rtwpci->mmap + addr);
+ int count;
+
+ for (count = 0; ; count++) {
+ if (val != RTW89_R32_DEAD)
+ return val;
+ if (count >= MAC_REG_POOL_COUNT) {
+ rtw89_warn(rtwdev, "addr %#x = %#x\n", addr, val);
+ return RTW89_R32_DEAD;
+ }
+ rtw89_pci_ops_write32(rtwdev, R_AX_CK_EN, B_AX_CMAC_ALLCKEN);
+ val = readl(rtwpci->mmap + addr);
+ }
+
+ return val;
+}
+
+static u8 rtw89_pci_ops_read8(struct rtw89_dev *rtwdev, u32 addr)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ u32 addr32, val32, shift;
+
+ if (!ACCESS_CMAC(addr))
+ return readb(rtwpci->mmap + addr);
+
+ addr32 = addr & ~0x3;
+ shift = (addr & 0x3) * 8;
+ val32 = rtw89_pci_ops_read32_cmac(rtwdev, addr32);
+ return val32 >> shift;
+}
+
+static u16 rtw89_pci_ops_read16(struct rtw89_dev *rtwdev, u32 addr)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ u32 addr32, val32, shift;
+
+ if (!ACCESS_CMAC(addr))
+ return readw(rtwpci->mmap + addr);
+
+ addr32 = addr & ~0x3;
+ shift = (addr & 0x3) * 8;
+ val32 = rtw89_pci_ops_read32_cmac(rtwdev, addr32);
+ return val32 >> shift;
+}
+
+static u32 rtw89_pci_ops_read32(struct rtw89_dev *rtwdev, u32 addr)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+
+ if (!ACCESS_CMAC(addr))
+ return readl(rtwpci->mmap + addr);
+
+ return rtw89_pci_ops_read32_cmac(rtwdev, addr);
+}
+
+static void rtw89_pci_ops_write8(struct rtw89_dev *rtwdev, u32 addr, u8 data)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+
+ writeb(data, rtwpci->mmap + addr);
+}
+
+static void rtw89_pci_ops_write16(struct rtw89_dev *rtwdev, u32 addr, u16 data)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+
+ writew(data, rtwpci->mmap + addr);
+}
+
+static void rtw89_pci_ops_write32(struct rtw89_dev *rtwdev, u32 addr, u32 data)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+
+ writel(data, rtwpci->mmap + addr);
+}
+
+static void rtw89_pci_ctrl_dma_all(struct rtw89_dev *rtwdev, bool enable)
+{
+ if (enable) {
+ rtw89_write32_set(rtwdev, R_AX_PCIE_INIT_CFG1,
+ B_AX_TXHCI_EN | B_AX_RXHCI_EN);
+ rtw89_write32_clr(rtwdev, R_AX_PCIE_DMA_STOP1,
+ B_AX_STOP_PCIEIO);
+ } else {
+ rtw89_write32_set(rtwdev, R_AX_PCIE_DMA_STOP1,
+ B_AX_STOP_PCIEIO);
+ rtw89_write32_clr(rtwdev, R_AX_PCIE_INIT_CFG1,
+ B_AX_TXHCI_EN | B_AX_RXHCI_EN);
+ }
+}
+
+static int rtw89_pci_check_mdio(struct rtw89_dev *rtwdev, u8 addr, u8 speed, u16 rw_bit)
+{
+ u16 val;
+
+ rtw89_write8(rtwdev, R_AX_MDIO_CFG, addr & 0x1F);
+
+ val = rtw89_read16(rtwdev, R_AX_MDIO_CFG);
+ switch (speed) {
+ case PCIE_PHY_GEN1:
+ if (addr < 0x20)
+ val = u16_replace_bits(val, MDIO_PG0_G1, B_AX_MDIO_PHY_ADDR_MASK);
+ else
+ val = u16_replace_bits(val, MDIO_PG1_G1, B_AX_MDIO_PHY_ADDR_MASK);
+ break;
+ case PCIE_PHY_GEN2:
+ if (addr < 0x20)
+ val = u16_replace_bits(val, MDIO_PG0_G2, B_AX_MDIO_PHY_ADDR_MASK);
+ else
+ val = u16_replace_bits(val, MDIO_PG1_G2, B_AX_MDIO_PHY_ADDR_MASK);
+ break;
+ default:
+ rtw89_err(rtwdev, "[ERR]Error Speed %d!\n", speed);
+ return -EINVAL;
+ };
+ rtw89_write16(rtwdev, R_AX_MDIO_CFG, val);
+ rtw89_write16_set(rtwdev, R_AX_MDIO_CFG, rw_bit);
+
+ return read_poll_timeout(rtw89_read16, val, !(val & rw_bit), 10, 2000,
+ false, rtwdev, R_AX_MDIO_CFG);
+}
+
+static int
+rtw89_read16_mdio(struct rtw89_dev *rtwdev, u8 addr, u8 speed, u16 *val)
+{
+ int ret;
+
+ ret = rtw89_pci_check_mdio(rtwdev, addr, speed, B_AX_MDIO_RFLAG);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]MDIO R16 0x%X fail ret=%d!\n", addr, ret);
+ return ret;
+ }
+ *val = rtw89_read16(rtwdev, R_AX_MDIO_RDATA);
+
+ return 0;
+}
+
+static int
+rtw89_write16_mdio(struct rtw89_dev *rtwdev, u8 addr, u16 data, u8 speed)
+{
+ int ret;
+
+ rtw89_write16(rtwdev, R_AX_MDIO_WDATA, data);
+ ret = rtw89_pci_check_mdio(rtwdev, addr, speed, B_AX_MDIO_WFLAG);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]MDIO W16 0x%X = %x fail ret=%d!\n", addr, data, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rtw89_write16_mdio_set(struct rtw89_dev *rtwdev, u8 addr, u16 mask, u8 speed)
+{
+ int ret;
+ u16 val;
+
+ ret = rtw89_read16_mdio(rtwdev, addr, speed, &val);
+ if (ret)
+ return ret;
+ ret = rtw89_write16_mdio(rtwdev, addr, val | mask, speed);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int rtw89_write16_mdio_clr(struct rtw89_dev *rtwdev, u8 addr, u16 mask, u8 speed)
+{
+ int ret;
+ u16 val;
+
+ ret = rtw89_read16_mdio(rtwdev, addr, speed, &val);
+ if (ret)
+ return ret;
+ ret = rtw89_write16_mdio(rtwdev, addr, val & ~mask, speed);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int rtw89_dbi_write8(struct rtw89_dev *rtwdev, u16 addr, u8 data)
+{
+ u16 write_addr;
+ u16 remainder = addr & ~(B_AX_DBI_ADDR_MSK | B_AX_DBI_WREN_MSK);
+ u8 flag;
+ int ret;
+
+ write_addr = addr & B_AX_DBI_ADDR_MSK;
+ write_addr |= u16_encode_bits(BIT(remainder), B_AX_DBI_WREN_MSK);
+ rtw89_write8(rtwdev, R_AX_DBI_WDATA + remainder, data);
+ rtw89_write16(rtwdev, R_AX_DBI_FLAG, write_addr);
+ rtw89_write8(rtwdev, R_AX_DBI_FLAG + 2, B_AX_DBI_WFLAG >> 16);
+
+ ret = read_poll_timeout_atomic(rtw89_read8, flag, !flag, 10,
+ 10 * RTW89_PCI_WR_RETRY_CNT, false,
+ rtwdev, R_AX_DBI_FLAG + 2);
+ if (ret)
+ WARN(flag, "failed to write to DBI register, addr=0x%04x\n",
+ addr);
+
+ return ret;
+}
+
+static int rtw89_dbi_read8(struct rtw89_dev *rtwdev, u16 addr, u8 *value)
+{
+ u16 read_addr = addr & B_AX_DBI_ADDR_MSK;
+ u8 flag;
+ int ret;
+
+ rtw89_write16(rtwdev, R_AX_DBI_FLAG, read_addr);
+ rtw89_write8(rtwdev, R_AX_DBI_FLAG + 2, B_AX_DBI_RFLAG >> 16);
+
+ ret = read_poll_timeout_atomic(rtw89_read8, flag, !flag, 10,
+ 10 * RTW89_PCI_WR_RETRY_CNT, false,
+ rtwdev, R_AX_DBI_FLAG + 2);
+
+ if (!ret) {
+ read_addr = R_AX_DBI_RDATA + (addr & 3);
+ *value = rtw89_read8(rtwdev, read_addr);
+ } else {
+ WARN(1, "failed to read DBI register, addr=0x%04x\n", addr);
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static int rtw89_dbi_write8_set(struct rtw89_dev *rtwdev, u16 addr, u8 bit)
+{
+ u8 value;
+ int ret;
+
+ ret = rtw89_dbi_read8(rtwdev, addr, &value);
+ if (ret)
+ return ret;
+
+ value |= bit;
+ ret = rtw89_dbi_write8(rtwdev, addr, value);
+
+ return ret;
+}
+
+static int rtw89_dbi_write8_clr(struct rtw89_dev *rtwdev, u16 addr, u8 bit)
+{
+ u8 value;
+ int ret;
+
+ ret = rtw89_dbi_read8(rtwdev, addr, &value);
+ if (ret)
+ return ret;
+
+ value &= ~bit;
+ ret = rtw89_dbi_write8(rtwdev, addr, value);
+
+ return ret;
+}
+
+static int
+__get_target(struct rtw89_dev *rtwdev, u16 *target, enum rtw89_pcie_phy phy_rate)
+{
+ u16 val, tar;
+ int ret;
+
+ /* Enable counter */
+ ret = rtw89_read16_mdio(rtwdev, RAC_CTRL_PPR_V1, phy_rate, &val);
+ if (ret)
+ return ret;
+ ret = rtw89_write16_mdio(rtwdev, RAC_CTRL_PPR_V1, val & ~B_AX_CLK_CALIB_EN,
+ phy_rate);
+ if (ret)
+ return ret;
+ ret = rtw89_write16_mdio(rtwdev, RAC_CTRL_PPR_V1, val | B_AX_CLK_CALIB_EN,
+ phy_rate);
+ if (ret)
+ return ret;
+
+ fsleep(300);
+
+ ret = rtw89_read16_mdio(rtwdev, RAC_CTRL_PPR_V1, phy_rate, &tar);
+ if (ret)
+ return ret;
+ ret = rtw89_write16_mdio(rtwdev, RAC_CTRL_PPR_V1, val & ~B_AX_CLK_CALIB_EN,
+ phy_rate);
+ if (ret)
+ return ret;
+
+ tar = tar & 0x0FFF;
+ if (tar == 0 || tar == 0x0FFF) {
+ rtw89_err(rtwdev, "[ERR]Get target failed.\n");
+ return -EINVAL;
+ }
+
+ *target = tar;
+
+ return 0;
+}
+
+static int rtw89_pci_auto_refclk_cal(struct rtw89_dev *rtwdev, bool autook_en)
+{
+ enum rtw89_pcie_phy phy_rate;
+ u16 val16, mgn_set, div_set, tar;
+ u8 val8, bdr_ori;
+ bool l1_flag = false;
+ int ret = 0;
+
+ if ((rtwdev->chip->chip_id == RTL8852A && rtwdev->hal.cv == CHIP_CBV) ||
+ rtwdev->chip->chip_id == RTL8852C)
+ return 0;
+
+ ret = rtw89_dbi_read8(rtwdev, RTW89_PCIE_PHY_RATE, &val8);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]dbi_r8_pcie %X\n", RTW89_PCIE_PHY_RATE);
+ return ret;
+ }
+
+ if (FIELD_GET(RTW89_PCIE_PHY_RATE_MASK, val8) == 0x1) {
+ phy_rate = PCIE_PHY_GEN1;
+ } else if (FIELD_GET(RTW89_PCIE_PHY_RATE_MASK, val8) == 0x2) {
+ phy_rate = PCIE_PHY_GEN2;
+ } else {
+ rtw89_err(rtwdev, "[ERR]PCIe PHY rate %#x not support\n", val8);
+ return -EOPNOTSUPP;
+ }
+ /* Disable L1BD */
+ ret = rtw89_dbi_read8(rtwdev, RTW89_PCIE_L1_CTRL, &bdr_ori);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]dbi_r8_pcie %X\n", RTW89_PCIE_L1_CTRL);
+ return ret;
+ }
+
+ if (bdr_ori & RTW89_PCIE_BIT_L1) {
+ ret = rtw89_dbi_write8(rtwdev, RTW89_PCIE_L1_CTRL,
+ bdr_ori & ~RTW89_PCIE_BIT_L1);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]dbi_w8_pcie %X\n", RTW89_PCIE_L1_CTRL);
+ return ret;
+ }
+ l1_flag = true;
+ }
+
+ ret = rtw89_read16_mdio(rtwdev, RAC_CTRL_PPR_V1, phy_rate, &val16);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]mdio_r16_pcie %X\n", RAC_CTRL_PPR_V1);
+ goto end;
+ }
+
+ if (val16 & B_AX_CALIB_EN) {
+ ret = rtw89_write16_mdio(rtwdev, RAC_CTRL_PPR_V1,
+ val16 & ~B_AX_CALIB_EN, phy_rate);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]mdio_w16_pcie %X\n", RAC_CTRL_PPR_V1);
+ goto end;
+ }
+ }
+
+ if (!autook_en)
+ goto end;
+ /* Set div */
+ ret = rtw89_write16_mdio_clr(rtwdev, RAC_CTRL_PPR_V1, B_AX_DIV, phy_rate);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]mdio_w16_pcie %X\n", RAC_CTRL_PPR_V1);
+ goto end;
+ }
+
+ /* Obtain div and margin */
+ ret = __get_target(rtwdev, &tar, phy_rate);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]1st get target fail %d\n", ret);
+ goto end;
+ }
+
+ mgn_set = tar * INTF_INTGRA_HOSTREF_V1 / INTF_INTGRA_MINREF_V1 - tar;
+
+ if (mgn_set >= 128) {
+ div_set = 0x0003;
+ mgn_set = 0x000F;
+ } else if (mgn_set >= 64) {
+ div_set = 0x0003;
+ mgn_set >>= 3;
+ } else if (mgn_set >= 32) {
+ div_set = 0x0002;
+ mgn_set >>= 2;
+ } else if (mgn_set >= 16) {
+ div_set = 0x0001;
+ mgn_set >>= 1;
+ } else if (mgn_set == 0) {
+ rtw89_err(rtwdev, "[ERR]cal mgn is 0,tar = %d\n", tar);
+ goto end;
+ } else {
+ div_set = 0x0000;
+ }
+
+ ret = rtw89_read16_mdio(rtwdev, RAC_CTRL_PPR_V1, phy_rate, &val16);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]mdio_r16_pcie %X\n", RAC_CTRL_PPR_V1);
+ goto end;
+ }
+
+ val16 |= u16_encode_bits(div_set, B_AX_DIV);
+
+ ret = rtw89_write16_mdio(rtwdev, RAC_CTRL_PPR_V1, val16, phy_rate);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]mdio_w16_pcie %X\n", RAC_CTRL_PPR_V1);
+ goto end;
+ }
+
+ ret = __get_target(rtwdev, &tar, phy_rate);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]2nd get target fail %d\n", ret);
+ goto end;
+ }
+
+ rtw89_debug(rtwdev, RTW89_DBG_HCI, "[TRACE]target = 0x%X, div = 0x%X, margin = 0x%X\n",
+ tar, div_set, mgn_set);
+ ret = rtw89_write16_mdio(rtwdev, RAC_SET_PPR_V1,
+ (tar & 0x0FFF) | (mgn_set << 12), phy_rate);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]mdio_w16_pcie %X\n", RAC_SET_PPR_V1);
+ goto end;
+ }
+
+ /* Enable function */
+ ret = rtw89_write16_mdio_set(rtwdev, RAC_CTRL_PPR_V1, B_AX_CALIB_EN, phy_rate);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]mdio_w16_pcie %X\n", RAC_CTRL_PPR_V1);
+ goto end;
+ }
+
+ /* CLK delay = 0 */
+ ret = rtw89_dbi_write8(rtwdev, RTW89_PCIE_CLK_CTRL, PCIE_CLKDLY_HW_0);
+
+end:
+ /* Set L1BD to ori */
+ if (l1_flag) {
+ ret = rtw89_dbi_write8(rtwdev, RTW89_PCIE_L1_CTRL, bdr_ori);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]dbi_w8_pcie %X\n", RTW89_PCIE_L1_CTRL);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int rtw89_pci_deglitch_setting(struct rtw89_dev *rtwdev)
+{
+ int ret;
+
+ if (rtwdev->chip->chip_id != RTL8852A)
+ return 0;
+
+ ret = rtw89_write16_mdio_clr(rtwdev, RAC_ANA24, B_AX_DEGLITCH,
+ PCIE_PHY_GEN1);
+ if (ret)
+ return ret;
+ ret = rtw89_write16_mdio_clr(rtwdev, RAC_ANA24, B_AX_DEGLITCH,
+ PCIE_PHY_GEN2);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void rtw89_pci_rxdma_prefth(struct rtw89_dev *rtwdev)
+{
+ rtw89_write32_set(rtwdev, R_AX_PCIE_INIT_CFG1, B_AX_DIS_RXDMA_PRE);
+}
+
+static void rtw89_pci_l1off_pwroff(struct rtw89_dev *rtwdev)
+{
+ if (rtwdev->chip->chip_id == RTL8852C)
+ return;
+
+ rtw89_write32_clr(rtwdev, R_AX_PCIE_PS_CTRL, B_AX_L1OFF_PWR_OFF_EN);
+}
+
+static u32 rtw89_pci_l2_rxen_lat(struct rtw89_dev *rtwdev)
+{
+ int ret;
+
+ if (rtwdev->chip->chip_id == RTL8852C)
+ return 0;
+
+ ret = rtw89_write16_mdio_clr(rtwdev, RAC_ANA26, B_AX_RXEN,
+ PCIE_PHY_GEN1);
+ if (ret)
+ return ret;
+
+ ret = rtw89_write16_mdio_clr(rtwdev, RAC_ANA26, B_AX_RXEN,
+ PCIE_PHY_GEN2);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void rtw89_pci_aphy_pwrcut(struct rtw89_dev *rtwdev)
+{
+ if (rtwdev->chip->chip_id != RTL8852A)
+ return;
+
+ rtw89_write32_clr(rtwdev, R_AX_SYS_PW_CTRL, B_AX_PSUS_OFF_CAPC_EN);
+}
+
+static void rtw89_pci_hci_ldo(struct rtw89_dev *rtwdev)
+{
+ if (rtwdev->chip->chip_id != RTL8852A)
+ return;
+
+ rtw89_write32_set(rtwdev, R_AX_SYS_SDIO_CTRL,
+ B_AX_PCIE_DIS_L2_CTRL_LDO_HCI);
+ rtw89_write32_clr(rtwdev, R_AX_SYS_SDIO_CTRL,
+ B_AX_PCIE_DIS_WLSUS_AFT_PDN);
+}
+
+static void rtw89_pci_set_sic(struct rtw89_dev *rtwdev)
+{
+ if (rtwdev->chip->chip_id == RTL8852C)
+ return;
+
+ rtw89_write32_clr(rtwdev, R_AX_PCIE_EXP_CTRL,
+ B_AX_SIC_EN_FORCE_CLKREQ);
+}
+
+static void rtw89_pci_set_dbg(struct rtw89_dev *rtwdev)
+{
+ if (rtwdev->chip->chip_id == RTL8852C)
+ return;
+
+ rtw89_write32_set(rtwdev, R_AX_PCIE_DBG_CTRL,
+ B_AX_ASFF_FULL_NO_STK | B_AX_EN_STUCK_DBG);
+
+ if (rtwdev->chip->chip_id == RTL8852A)
+ rtw89_write32_set(rtwdev, R_AX_PCIE_EXP_CTRL,
+ B_AX_EN_CHKDSC_NO_RX_STUCK);
+}
+
+static void rtw89_pci_clr_idx_all(struct rtw89_dev *rtwdev)
+{
+ u32 val = B_AX_CLR_ACH0_IDX | B_AX_CLR_ACH1_IDX | B_AX_CLR_ACH2_IDX |
+ B_AX_CLR_ACH3_IDX | B_AX_CLR_CH8_IDX | B_AX_CLR_CH9_IDX |
+ B_AX_CLR_CH12_IDX;
+
+ if (rtwdev->chip->chip_id == RTL8852A)
+ val |= B_AX_CLR_ACH4_IDX | B_AX_CLR_ACH5_IDX |
+ B_AX_CLR_ACH6_IDX | B_AX_CLR_ACH7_IDX;
+ /* clear DMA indexes */
+ rtw89_write32_set(rtwdev, R_AX_TXBD_RWPTR_CLR1, val);
+ if (rtwdev->chip->chip_id == RTL8852A)
+ rtw89_write32_set(rtwdev, R_AX_TXBD_RWPTR_CLR2,
+ B_AX_CLR_CH10_IDX | B_AX_CLR_CH11_IDX);
+ rtw89_write32_set(rtwdev, R_AX_RXBD_RWPTR_CLR,
+ B_AX_CLR_RXQ_IDX | B_AX_CLR_RPQ_IDX);
+}
+
+static int rtw89_pci_ops_deinit(struct rtw89_dev *rtwdev)
+{
+ if (rtwdev->chip->chip_id == RTL8852A) {
+ /* ltr sw trigger */
+ rtw89_write32_set(rtwdev, R_AX_LTR_CTRL_0, B_AX_APP_LTR_IDLE);
+ }
+ rtw89_pci_ctrl_dma_all(rtwdev, false);
+ rtw89_pci_clr_idx_all(rtwdev);
+
+ return 0;
+}
+
+static int rtw89_pci_ops_mac_pre_init(struct rtw89_dev *rtwdev)
+{
+ u32 dma_busy;
+ u32 check;
+ u32 lbc;
+ int ret;
+
+ rtw89_pci_rxdma_prefth(rtwdev);
+ rtw89_pci_l1off_pwroff(rtwdev);
+ rtw89_pci_deglitch_setting(rtwdev);
+ ret = rtw89_pci_l2_rxen_lat(rtwdev);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR] pcie l2 rxen lat %d\n", ret);
+ return ret;
+ }
+
+ rtw89_pci_aphy_pwrcut(rtwdev);
+ rtw89_pci_hci_ldo(rtwdev);
+
+ ret = rtw89_pci_auto_refclk_cal(rtwdev, false);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR] pcie autok fail %d\n", ret);
+ return ret;
+ }
+
+ rtw89_pci_set_sic(rtwdev);
+ rtw89_pci_set_dbg(rtwdev);
+
+ if (rtwdev->chip->chip_id == RTL8852A)
+ rtw89_write32_clr(rtwdev, R_AX_SYS_SDIO_CTRL,
+ B_AX_PCIE_AUXCLK_GATE);
+
+ lbc = rtw89_read32(rtwdev, R_AX_LBC_WATCHDOG);
+ lbc = u32_replace_bits(lbc, RTW89_MAC_LBC_TMR_128US, B_AX_LBC_TIMER);
+ lbc |= B_AX_LBC_FLAG | B_AX_LBC_EN;
+ rtw89_write32(rtwdev, R_AX_LBC_WATCHDOG, lbc);
+
+ rtw89_write32_set(rtwdev, R_AX_PCIE_INIT_CFG1,
+ B_AX_PCIE_TXRST_KEEP_REG | B_AX_PCIE_RXRST_KEEP_REG);
+ rtw89_write32_set(rtwdev, R_AX_PCIE_DMA_STOP1, B_AX_STOP_WPDMA);
+
+ /* stop DMA activities */
+ rtw89_pci_ctrl_dma_all(rtwdev, false);
+
+ /* check PCI at idle state */
+ check = B_AX_PCIEIO_BUSY | B_AX_PCIEIO_TX_BUSY | B_AX_PCIEIO_RX_BUSY;
+ ret = read_poll_timeout(rtw89_read32, dma_busy, (dma_busy & check) == 0,
+ 100, 3000, false, rtwdev, R_AX_PCIE_DMA_BUSY1);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to poll io busy\n");
+ return ret;
+ }
+
+ rtw89_pci_clr_idx_all(rtwdev);
+
+ /* configure TX/RX op modes */
+ rtw89_write32_set(rtwdev, R_AX_PCIE_INIT_CFG1, B_AX_TX_TRUNC_MODE |
+ B_AX_RX_TRUNC_MODE);
+ rtw89_write32_clr(rtwdev, R_AX_PCIE_INIT_CFG1, B_AX_RXBD_MODE);
+ rtw89_write32_mask(rtwdev, R_AX_PCIE_INIT_CFG1, B_AX_PCIE_MAX_TXDMA_MASK, 7);
+ rtw89_write32_mask(rtwdev, R_AX_PCIE_INIT_CFG1, B_AX_PCIE_MAX_RXDMA_MASK, 3);
+ /* multi-tag mode */
+ rtw89_write32_set(rtwdev, R_AX_PCIE_INIT_CFG1, B_AX_LATENCY_CONTROL);
+ rtw89_write32_mask(rtwdev, R_AX_PCIE_EXP_CTRL, B_AX_MAX_TAG_NUM,
+ RTW89_MAC_TAG_NUM_8);
+ rtw89_write32_mask(rtwdev, R_AX_PCIE_INIT_CFG2, B_AX_WD_ITVL_IDLE,
+ RTW89_MAC_WD_DMA_INTVL_256NS);
+ rtw89_write32_mask(rtwdev, R_AX_PCIE_INIT_CFG2, B_AX_WD_ITVL_ACT,
+ RTW89_MAC_WD_DMA_INTVL_256NS);
+
+ /* fill TRX BD indexes */
+ rtw89_pci_reset_trx_rings(rtwdev);
+
+ ret = rtw89_pci_rst_bdram_pcie(rtwdev);
+ if (ret) {
+ rtw89_warn(rtwdev, "reset bdram busy\n");
+ return ret;
+ }
+
+ /* enable FW CMD queue to download firmware */
+ rtw89_write32_set(rtwdev, R_AX_PCIE_DMA_STOP1, B_AX_TX_STOP1_ALL);
+ rtw89_write32_clr(rtwdev, R_AX_PCIE_DMA_STOP1, B_AX_STOP_CH12);
+ rtw89_write32_set(rtwdev, R_AX_PCIE_DMA_STOP2, B_AX_TX_STOP2_ALL);
+
+ /* start DMA activities */
+ rtw89_pci_ctrl_dma_all(rtwdev, true);
+
+ return 0;
+}
+
+static int rtw89_pci_ltr_set(struct rtw89_dev *rtwdev)
+{
+ u32 val;
+
+ val = rtw89_read32(rtwdev, R_AX_LTR_CTRL_0);
+ if (rtw89_pci_ltr_is_err_reg_val(val))
+ return -EINVAL;
+ val = rtw89_read32(rtwdev, R_AX_LTR_CTRL_1);
+ if (rtw89_pci_ltr_is_err_reg_val(val))
+ return -EINVAL;
+ val = rtw89_read32(rtwdev, R_AX_LTR_IDLE_LATENCY);
+ if (rtw89_pci_ltr_is_err_reg_val(val))
+ return -EINVAL;
+ val = rtw89_read32(rtwdev, R_AX_LTR_ACTIVE_LATENCY);
+ if (rtw89_pci_ltr_is_err_reg_val(val))
+ return -EINVAL;
+
+ rtw89_write32_clr(rtwdev, R_AX_LTR_CTRL_0, B_AX_LTR_HW_EN);
+ rtw89_write32_set(rtwdev, R_AX_LTR_CTRL_0, B_AX_LTR_EN);
+ rtw89_write32_mask(rtwdev, R_AX_LTR_CTRL_0, B_AX_LTR_SPACE_IDX_MASK,
+ PCI_LTR_SPC_500US);
+ rtw89_write32_mask(rtwdev, R_AX_LTR_CTRL_0, B_AX_LTR_IDLE_TIMER_IDX_MASK,
+ PCI_LTR_IDLE_TIMER_800US);
+ rtw89_write32_mask(rtwdev, R_AX_LTR_CTRL_1, B_AX_LTR_RX0_TH_MASK, 0x28);
+ rtw89_write32_mask(rtwdev, R_AX_LTR_CTRL_1, B_AX_LTR_RX1_TH_MASK, 0x28);
+ rtw89_write32(rtwdev, R_AX_LTR_IDLE_LATENCY, 0x88e088e0);
+ rtw89_write32(rtwdev, R_AX_LTR_ACTIVE_LATENCY, 0x880b880b);
+
+ return 0;
+}
+
+static int rtw89_pci_ops_mac_post_init(struct rtw89_dev *rtwdev)
+{
+ int ret;
+
+ ret = rtw89_pci_ltr_set(rtwdev);
+ if (ret) {
+ rtw89_err(rtwdev, "pci ltr set fail\n");
+ return ret;
+ }
+ if (rtwdev->chip->chip_id == RTL8852A) {
+ /* ltr sw trigger */
+ rtw89_write32_set(rtwdev, R_AX_LTR_CTRL_0, B_AX_APP_LTR_ACT);
+ }
+ /* ADDR info 8-byte mode */
+ rtw89_write32_set(rtwdev, R_AX_TX_ADDRESS_INFO_MODE_SETTING,
+ B_AX_HOST_ADDR_INFO_8B_SEL);
+ rtw89_write32_clr(rtwdev, R_AX_PKTIN_SETTING, B_AX_WD_ADDR_INFO_LENGTH);
+
+ /* enable DMA for all queues */
+ rtw89_write32_clr(rtwdev, R_AX_PCIE_DMA_STOP1, B_AX_TX_STOP1_ALL);
+ rtw89_write32_clr(rtwdev, R_AX_PCIE_DMA_STOP2, B_AX_TX_STOP2_ALL);
+
+ /* Release PCI IO */
+ rtw89_write32_clr(rtwdev, R_AX_PCIE_DMA_STOP1,
+ B_AX_STOP_WPDMA | B_AX_STOP_PCIEIO);
+
+ return 0;
+}
+
+static int rtw89_pci_claim_device(struct rtw89_dev *rtwdev,
+ struct pci_dev *pdev)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ int ret;
+
+ ret = pci_enable_device(pdev);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to enable pci device\n");
+ return ret;
+ }
+
+ pci_set_master(pdev);
+ pci_set_drvdata(pdev, rtwdev->hw);
+
+ rtwpci->pdev = pdev;
+
+ return 0;
+}
+
+static void rtw89_pci_declaim_device(struct rtw89_dev *rtwdev,
+ struct pci_dev *pdev)
+{
+ pci_clear_master(pdev);
+ pci_disable_device(pdev);
+}
+
+static int rtw89_pci_setup_mapping(struct rtw89_dev *rtwdev,
+ struct pci_dev *pdev)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ unsigned long resource_len;
+ u8 bar_id = 2;
+ int ret;
+
+ ret = pci_request_regions(pdev, KBUILD_MODNAME);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to request pci regions\n");
+ goto err;
+ }
+
+ ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret) {
+ rtw89_err(rtwdev, "failed to set dma mask to 32-bit\n");
+ goto err_release_regions;
+ }
+
+ ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret) {
+ rtw89_err(rtwdev, "failed to set consistent dma mask to 32-bit\n");
+ goto err_release_regions;
+ }
+
+ resource_len = pci_resource_len(pdev, bar_id);
+ rtwpci->mmap = pci_iomap(pdev, bar_id, resource_len);
+ if (!rtwpci->mmap) {
+ rtw89_err(rtwdev, "failed to map pci io\n");
+ ret = -EIO;
+ goto err_release_regions;
+ }
+
+ return 0;
+
+err_release_regions:
+ pci_release_regions(pdev);
+err:
+ return ret;
+}
+
+static void rtw89_pci_clear_mapping(struct rtw89_dev *rtwdev,
+ struct pci_dev *pdev)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+
+ if (rtwpci->mmap) {
+ pci_iounmap(pdev, rtwpci->mmap);
+ pci_release_regions(pdev);
+ }
+}
+
+static void rtw89_pci_free_tx_wd_ring(struct rtw89_dev *rtwdev,
+ struct pci_dev *pdev,
+ struct rtw89_pci_tx_ring *tx_ring)
+{
+ struct rtw89_pci_tx_wd_ring *wd_ring = &tx_ring->wd_ring;
+ u8 *head = wd_ring->head;
+ dma_addr_t dma = wd_ring->dma;
+ u32 page_size = wd_ring->page_size;
+ u32 page_num = wd_ring->page_num;
+ u32 ring_sz = page_size * page_num;
+
+ dma_free_coherent(&pdev->dev, ring_sz, head, dma);
+ wd_ring->head = NULL;
+}
+
+static void rtw89_pci_free_tx_ring(struct rtw89_dev *rtwdev,
+ struct pci_dev *pdev,
+ struct rtw89_pci_tx_ring *tx_ring)
+{
+ int ring_sz;
+ u8 *head;
+ dma_addr_t dma;
+
+ head = tx_ring->bd_ring.head;
+ dma = tx_ring->bd_ring.dma;
+ ring_sz = tx_ring->bd_ring.desc_size * tx_ring->bd_ring.len;
+ dma_free_coherent(&pdev->dev, ring_sz, head, dma);
+
+ tx_ring->bd_ring.head = NULL;
+}
+
+static void rtw89_pci_free_tx_rings(struct rtw89_dev *rtwdev,
+ struct pci_dev *pdev)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ struct rtw89_pci_tx_ring *tx_ring;
+ int i;
+
+ for (i = 0; i < RTW89_TXCH_NUM; i++) {
+ tx_ring = &rtwpci->tx_rings[i];
+ rtw89_pci_free_tx_wd_ring(rtwdev, pdev, tx_ring);
+ rtw89_pci_free_tx_ring(rtwdev, pdev, tx_ring);
+ }
+}
+
+static void rtw89_pci_free_rx_ring(struct rtw89_dev *rtwdev,
+ struct pci_dev *pdev,
+ struct rtw89_pci_rx_ring *rx_ring)
+{
+ struct rtw89_pci_rx_info *rx_info;
+ struct sk_buff *skb;
+ dma_addr_t dma;
+ u32 buf_sz;
+ u8 *head;
+ int ring_sz = rx_ring->bd_ring.desc_size * rx_ring->bd_ring.len;
+ int i;
+
+ buf_sz = rx_ring->buf_sz;
+ for (i = 0; i < rx_ring->bd_ring.len; i++) {
+ skb = rx_ring->buf[i];
+ if (!skb)
+ continue;
+
+ rx_info = RTW89_PCI_RX_SKB_CB(skb);
+ dma = rx_info->dma;
+ dma_unmap_single(&pdev->dev, dma, buf_sz, DMA_FROM_DEVICE);
+ dev_kfree_skb(skb);
+ rx_ring->buf[i] = NULL;
+ }
+
+ head = rx_ring->bd_ring.head;
+ dma = rx_ring->bd_ring.dma;
+ dma_free_coherent(&pdev->dev, ring_sz, head, dma);
+
+ rx_ring->bd_ring.head = NULL;
+}
+
+static void rtw89_pci_free_rx_rings(struct rtw89_dev *rtwdev,
+ struct pci_dev *pdev)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ struct rtw89_pci_rx_ring *rx_ring;
+ int i;
+
+ for (i = 0; i < RTW89_RXCH_NUM; i++) {
+ rx_ring = &rtwpci->rx_rings[i];
+ rtw89_pci_free_rx_ring(rtwdev, pdev, rx_ring);
+ }
+}
+
+static void rtw89_pci_free_trx_rings(struct rtw89_dev *rtwdev,
+ struct pci_dev *pdev)
+{
+ rtw89_pci_free_rx_rings(rtwdev, pdev);
+ rtw89_pci_free_tx_rings(rtwdev, pdev);
+}
+
+static int rtw89_pci_init_rx_bd(struct rtw89_dev *rtwdev, struct pci_dev *pdev,
+ struct rtw89_pci_rx_ring *rx_ring,
+ struct sk_buff *skb, int buf_sz, u32 idx)
+{
+ struct rtw89_pci_rx_info *rx_info;
+ struct rtw89_pci_rx_bd_32 *rx_bd;
+ dma_addr_t dma;
+
+ if (!skb)
+ return -EINVAL;
+
+ dma = dma_map_single(&pdev->dev, skb->data, buf_sz, DMA_FROM_DEVICE);
+ if (dma_mapping_error(&pdev->dev, dma))
+ return -EBUSY;
+
+ rx_info = RTW89_PCI_RX_SKB_CB(skb);
+ rx_bd = RTW89_PCI_RX_BD(rx_ring, idx);
+
+ memset(rx_bd, 0, sizeof(*rx_bd));
+ rx_bd->buf_size = cpu_to_le16(buf_sz);
+ rx_bd->dma = cpu_to_le32(dma);
+ rx_info->dma = dma;
+
+ return 0;
+}
+
+static int rtw89_pci_alloc_tx_wd_ring(struct rtw89_dev *rtwdev,
+ struct pci_dev *pdev,
+ struct rtw89_pci_tx_ring *tx_ring,
+ enum rtw89_tx_channel txch)
+{
+ struct rtw89_pci_tx_wd_ring *wd_ring = &tx_ring->wd_ring;
+ struct rtw89_pci_tx_wd *txwd;
+ dma_addr_t dma;
+ dma_addr_t cur_paddr;
+ u8 *head;
+ u8 *cur_vaddr;
+ u32 page_size = RTW89_PCI_TXWD_PAGE_SIZE;
+ u32 page_num = RTW89_PCI_TXWD_NUM_MAX;
+ u32 ring_sz = page_size * page_num;
+ u32 page_offset;
+ int i;
+
+ /* FWCMD queue doesn't use txwd as pages */
+ if (txch == RTW89_TXCH_CH12)
+ return 0;
+
+ head = dma_alloc_coherent(&pdev->dev, ring_sz, &dma, GFP_KERNEL);
+ if (!head)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&wd_ring->free_pages);
+ wd_ring->head = head;
+ wd_ring->dma = dma;
+ wd_ring->page_size = page_size;
+ wd_ring->page_num = page_num;
+
+ page_offset = 0;
+ for (i = 0; i < page_num; i++) {
+ txwd = &wd_ring->pages[i];
+ cur_paddr = dma + page_offset;
+ cur_vaddr = head + page_offset;
+
+ skb_queue_head_init(&txwd->queue);
+ INIT_LIST_HEAD(&txwd->list);
+ txwd->paddr = cur_paddr;
+ txwd->vaddr = cur_vaddr;
+ txwd->len = page_size;
+ txwd->seq = i;
+ rtw89_pci_enqueue_txwd(tx_ring, txwd);
+
+ page_offset += page_size;
+ }
+
+ return 0;
+}
+
+static int rtw89_pci_alloc_tx_ring(struct rtw89_dev *rtwdev,
+ struct pci_dev *pdev,
+ struct rtw89_pci_tx_ring *tx_ring,
+ u32 desc_size, u32 len,
+ enum rtw89_tx_channel txch)
+{
+ int ring_sz = desc_size * len;
+ u8 *head;
+ dma_addr_t dma;
+ u32 addr_num;
+ u32 addr_idx;
+ u32 addr_bdram;
+ u32 addr_desa_l;
+ u32 addr_desa_h;
+ int ret;
+
+ ret = rtw89_pci_alloc_tx_wd_ring(rtwdev, pdev, tx_ring, txch);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to alloc txwd ring of txch %d\n", txch);
+ goto err;
+ }
+
+ ret = rtw89_pci_get_txch_addrs(txch, &addr_num, &addr_idx, &addr_bdram,
+ &addr_desa_l, &addr_desa_h);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to get address of txch %d", txch);
+ goto err_free_wd_ring;
+ }
+
+ head = dma_alloc_coherent(&pdev->dev, ring_sz, &dma, GFP_KERNEL);
+ if (!head) {
+ ret = -ENOMEM;
+ goto err_free_wd_ring;
+ }
+
+ INIT_LIST_HEAD(&tx_ring->busy_pages);
+ tx_ring->bd_ring.head = head;
+ tx_ring->bd_ring.dma = dma;
+ tx_ring->bd_ring.len = len;
+ tx_ring->bd_ring.desc_size = desc_size;
+ tx_ring->bd_ring.addr_num = addr_num;
+ tx_ring->bd_ring.addr_idx = addr_idx;
+ tx_ring->bd_ring.addr_bdram = addr_bdram;
+ tx_ring->bd_ring.addr_desa_l = addr_desa_l;
+ tx_ring->bd_ring.addr_desa_h = addr_desa_h;
+ tx_ring->bd_ring.wp = 0;
+ tx_ring->bd_ring.rp = 0;
+ tx_ring->txch = txch;
+
+ return 0;
+
+err_free_wd_ring:
+ rtw89_pci_free_tx_wd_ring(rtwdev, pdev, tx_ring);
+err:
+ return ret;
+}
+
+static int rtw89_pci_alloc_tx_rings(struct rtw89_dev *rtwdev,
+ struct pci_dev *pdev)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ struct rtw89_pci_tx_ring *tx_ring;
+ u32 desc_size;
+ u32 len;
+ u32 i, tx_allocated;
+ int ret;
+
+ for (i = 0; i < RTW89_TXCH_NUM; i++) {
+ tx_ring = &rtwpci->tx_rings[i];
+ desc_size = sizeof(struct rtw89_pci_tx_bd_32);
+ len = RTW89_PCI_TXBD_NUM_MAX;
+ ret = rtw89_pci_alloc_tx_ring(rtwdev, pdev, tx_ring,
+ desc_size, len, i);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to alloc tx ring %d\n", i);
+ goto err_free;
+ }
+ }
+
+ return 0;
+
+err_free:
+ tx_allocated = i;
+ for (i = 0; i < tx_allocated; i++) {
+ tx_ring = &rtwpci->tx_rings[i];
+ rtw89_pci_free_tx_ring(rtwdev, pdev, tx_ring);
+ }
+
+ return ret;
+}
+
+static int rtw89_pci_alloc_rx_ring(struct rtw89_dev *rtwdev,
+ struct pci_dev *pdev,
+ struct rtw89_pci_rx_ring *rx_ring,
+ u32 desc_size, u32 len, u32 rxch)
+{
+ struct sk_buff *skb;
+ u8 *head;
+ dma_addr_t dma;
+ u32 addr_num;
+ u32 addr_idx;
+ u32 addr_desa_l;
+ u32 addr_desa_h;
+ int ring_sz = desc_size * len;
+ int buf_sz = RTW89_PCI_RX_BUF_SIZE;
+ int i, allocated;
+ int ret;
+
+ ret = rtw89_pci_get_rxch_addrs(rxch, &addr_num, &addr_idx,
+ &addr_desa_l, &addr_desa_h);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to get address of rxch %d", rxch);
+ return ret;
+ }
+
+ head = dma_alloc_coherent(&pdev->dev, ring_sz, &dma, GFP_KERNEL);
+ if (!head) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ rx_ring->bd_ring.head = head;
+ rx_ring->bd_ring.dma = dma;
+ rx_ring->bd_ring.len = len;
+ rx_ring->bd_ring.desc_size = desc_size;
+ rx_ring->bd_ring.addr_num = addr_num;
+ rx_ring->bd_ring.addr_idx = addr_idx;
+ rx_ring->bd_ring.addr_desa_l = addr_desa_l;
+ rx_ring->bd_ring.addr_desa_h = addr_desa_h;
+ rx_ring->bd_ring.wp = 0;
+ rx_ring->bd_ring.rp = 0;
+ rx_ring->buf_sz = buf_sz;
+ rx_ring->diliver_skb = NULL;
+ rx_ring->diliver_desc.ready = false;
+
+ for (i = 0; i < len; i++) {
+ skb = dev_alloc_skb(buf_sz);
+ if (!skb) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ memset(skb->data, 0, buf_sz);
+ rx_ring->buf[i] = skb;
+ ret = rtw89_pci_init_rx_bd(rtwdev, pdev, rx_ring, skb,
+ buf_sz, i);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to init rx buf %d\n", i);
+ dev_kfree_skb_any(skb);
+ rx_ring->buf[i] = NULL;
+ goto err_free;
+ }
+ }
+
+ return 0;
+
+err_free:
+ allocated = i;
+ for (i = 0; i < allocated; i++) {
+ skb = rx_ring->buf[i];
+ if (!skb)
+ continue;
+ dma = *((dma_addr_t *)skb->cb);
+ dma_unmap_single(&pdev->dev, dma, buf_sz, DMA_FROM_DEVICE);
+ dev_kfree_skb(skb);
+ rx_ring->buf[i] = NULL;
+ }
+
+ head = rx_ring->bd_ring.head;
+ dma = rx_ring->bd_ring.dma;
+ dma_free_coherent(&pdev->dev, ring_sz, head, dma);
+
+ rx_ring->bd_ring.head = NULL;
+err:
+ return ret;
+}
+
+static int rtw89_pci_alloc_rx_rings(struct rtw89_dev *rtwdev,
+ struct pci_dev *pdev)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ struct rtw89_pci_rx_ring *rx_ring;
+ u32 desc_size;
+ u32 len;
+ int i, rx_allocated;
+ int ret;
+
+ for (i = 0; i < RTW89_RXCH_NUM; i++) {
+ rx_ring = &rtwpci->rx_rings[i];
+ desc_size = sizeof(struct rtw89_pci_rx_bd_32);
+ len = RTW89_PCI_RXBD_NUM_MAX;
+ ret = rtw89_pci_alloc_rx_ring(rtwdev, pdev, rx_ring,
+ desc_size, len, i);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to alloc rx ring %d\n", i);
+ goto err_free;
+ }
+ }
+
+ return 0;
+
+err_free:
+ rx_allocated = i;
+ for (i = 0; i < rx_allocated; i++) {
+ rx_ring = &rtwpci->rx_rings[i];
+ rtw89_pci_free_rx_ring(rtwdev, pdev, rx_ring);
+ }
+
+ return ret;
+}
+
+static int rtw89_pci_alloc_trx_rings(struct rtw89_dev *rtwdev,
+ struct pci_dev *pdev)
+{
+ int ret;
+
+ ret = rtw89_pci_alloc_tx_rings(rtwdev, pdev);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to alloc dma tx rings\n");
+ goto err;
+ }
+
+ ret = rtw89_pci_alloc_rx_rings(rtwdev, pdev);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to alloc dma rx rings\n");
+ goto err_free_tx_rings;
+ }
+
+ return 0;
+
+err_free_tx_rings:
+ rtw89_pci_free_tx_rings(rtwdev, pdev);
+err:
+ return ret;
+}
+
+static void rtw89_pci_h2c_init(struct rtw89_dev *rtwdev,
+ struct rtw89_pci *rtwpci)
+{
+ skb_queue_head_init(&rtwpci->h2c_queue);
+}
+
+static int rtw89_pci_setup_resource(struct rtw89_dev *rtwdev,
+ struct pci_dev *pdev)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ int ret;
+
+ ret = rtw89_pci_setup_mapping(rtwdev, pdev);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to setup pci mapping\n");
+ goto err;
+ }
+
+ ret = rtw89_pci_alloc_trx_rings(rtwdev, pdev);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to alloc pci trx rings\n");
+ goto err_pci_unmap;
+ }
+
+ rtw89_pci_h2c_init(rtwdev, rtwpci);
+
+ spin_lock_init(&rtwpci->irq_lock);
+ spin_lock_init(&rtwpci->trx_lock);
+
+ return 0;
+
+err_pci_unmap:
+ rtw89_pci_clear_mapping(rtwdev, pdev);
+err:
+ return ret;
+}
+
+static void rtw89_pci_clear_resource(struct rtw89_dev *rtwdev,
+ struct pci_dev *pdev)
+{
+ rtw89_pci_free_trx_rings(rtwdev, pdev);
+ rtw89_pci_clear_mapping(rtwdev, pdev);
+}
+
+static void rtw89_pci_default_intr_mask(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+
+ rtwpci->halt_c2h_intrs = B_AX_HALT_C2H_INT_EN | 0;
+ rtwpci->intrs[0] = B_AX_TXDMA_CH12_INT_EN |
+ B_AX_TXDMA_STUCK_INT_EN |
+ B_AX_RXDMA_INT_EN |
+ B_AX_RXP1DMA_INT_EN |
+ B_AX_RPQDMA_INT_EN |
+ B_AX_RXDMA_STUCK_INT_EN |
+ B_AX_RDU_INT_EN |
+ B_AX_RPQBD_FULL_INT_EN |
+ B_AX_HS0ISR_IND_INT_EN;
+
+ rtwpci->intrs[1] = B_AX_HC10ISR_IND_INT_EN;
+}
+
+static int rtw89_pci_request_irq(struct rtw89_dev *rtwdev,
+ struct pci_dev *pdev)
+{
+ unsigned long flags = 0;
+ int ret;
+
+ flags |= PCI_IRQ_LEGACY | PCI_IRQ_MSI;
+ ret = pci_alloc_irq_vectors(pdev, 1, 1, flags);
+ if (ret < 0) {
+ rtw89_err(rtwdev, "failed to alloc irq vectors, ret %d\n", ret);
+ goto err;
+ }
+
+ ret = devm_request_threaded_irq(rtwdev->dev, pdev->irq,
+ rtw89_pci_interrupt_handler,
+ rtw89_pci_interrupt_threadfn,
+ IRQF_SHARED, KBUILD_MODNAME, rtwdev);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to request threaded irq\n");
+ goto err_free_vector;
+ }
+
+ rtw89_pci_default_intr_mask(rtwdev);
+
+ return 0;
+
+err_free_vector:
+ pci_free_irq_vectors(pdev);
+err:
+ return ret;
+}
+
+static void rtw89_pci_free_irq(struct rtw89_dev *rtwdev,
+ struct pci_dev *pdev)
+{
+ devm_free_irq(rtwdev->dev, pdev->irq, rtwdev);
+ pci_free_irq_vectors(pdev);
+}
+
+static void rtw89_pci_clkreq_set(struct rtw89_dev *rtwdev, bool enable)
+{
+ int ret;
+
+ if (rtw89_pci_disable_clkreq)
+ return;
+
+ if (enable)
+ ret = rtw89_dbi_write8_set(rtwdev, RTW89_PCIE_L1_CTRL,
+ RTW89_PCIE_BIT_CLK);
+ else
+ ret = rtw89_dbi_write8_clr(rtwdev, RTW89_PCIE_L1_CTRL,
+ RTW89_PCIE_BIT_CLK);
+ if (ret)
+ rtw89_err(rtwdev, "failed to %s CLKREQ_L1, ret=%d",
+ enable ? "set" : "unset", ret);
+}
+
+static void rtw89_pci_aspm_set(struct rtw89_dev *rtwdev, bool enable)
+{
+ int ret;
+
+ if (rtw89_pci_disable_aspm_l1)
+ return;
+
+ if (enable)
+ ret = rtw89_dbi_write8_set(rtwdev, RTW89_PCIE_L1_CTRL,
+ RTW89_PCIE_BIT_L1);
+ else
+ ret = rtw89_dbi_write8_clr(rtwdev, RTW89_PCIE_L1_CTRL,
+ RTW89_PCIE_BIT_L1);
+ if (ret)
+ rtw89_err(rtwdev, "failed to %s ASPM L1, ret=%d",
+ enable ? "set" : "unset", ret);
+}
+
+static void rtw89_pci_link_ps(struct rtw89_dev *rtwdev, bool enter)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+
+ /* Like CLKREQ, ASPM is also implemented by two HW modules, and can
+ * only be enabled when host supports it.
+ *
+ * And ASPM mechanism should be enabled when driver/firmware enters
+ * power save mode, without having heavy traffic. Because we've
+ * experienced some inter-operability issues that the link tends
+ * to enter L1 state on the fly even when driver is having high
+ * throughput. This is probably because the ASPM behavior slightly
+ * varies from different SOC.
+ */
+ if (rtwpci->link_ctrl & PCI_EXP_LNKCTL_ASPM_L1)
+ rtw89_pci_aspm_set(rtwdev, enter);
+}
+
+static void rtw89_pci_link_cfg(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ struct pci_dev *pdev = rtwpci->pdev;
+ u16 link_ctrl;
+ int ret;
+
+ /* Though there is standard PCIE configuration space to set the
+ * link control register, but by Realtek's design, driver should
+ * check if host supports CLKREQ/ASPM to enable the HW module.
+ *
+ * These functions are implemented by two HW modules associated,
+ * one is responsible to access PCIE configuration space to
+ * follow the host settings, and another is in charge of doing
+ * CLKREQ/ASPM mechanisms, it is default disabled. Because sometimes
+ * the host does not support it, and due to some reasons or wrong
+ * settings (ex. CLKREQ# not Bi-Direction), it could lead to device
+ * loss if HW misbehaves on the link.
+ *
+ * Hence it's designed that driver should first check the PCIE
+ * configuration space is sync'ed and enabled, then driver can turn
+ * on the other module that is actually working on the mechanism.
+ */
+ ret = pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &link_ctrl);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to read PCI cap, ret=%d\n", ret);
+ return;
+ }
+
+ if (link_ctrl & PCI_EXP_LNKCTL_CLKREQ_EN)
+ rtw89_pci_clkreq_set(rtwdev, true);
+
+ rtwpci->link_ctrl = link_ctrl;
+}
+
+static void rtw89_pci_l1ss_set(struct rtw89_dev *rtwdev, bool enable)
+{
+ int ret;
+
+ if (enable)
+ ret = rtw89_dbi_write8_set(rtwdev, RTW89_PCIE_TIMER_CTRL,
+ RTW89_PCIE_BIT_L1SUB);
+ else
+ ret = rtw89_dbi_write8_clr(rtwdev, RTW89_PCIE_TIMER_CTRL,
+ RTW89_PCIE_BIT_L1SUB);
+ if (ret)
+ rtw89_err(rtwdev, "failed to %s L1SS, ret=%d",
+ enable ? "set" : "unset", ret);
+}
+
+static void rtw89_pci_l1ss_cfg(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ struct pci_dev *pdev = rtwpci->pdev;
+ u32 l1ss_cap_ptr, l1ss_ctrl;
+
+ if (rtw89_pci_disable_l1ss)
+ return;
+
+ l1ss_cap_ptr = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_L1SS);
+ if (!l1ss_cap_ptr)
+ return;
+
+ pci_read_config_dword(pdev, l1ss_cap_ptr + PCI_L1SS_CTL1, &l1ss_ctrl);
+
+ if (l1ss_ctrl & PCI_L1SS_CTL1_L1SS_MASK)
+ rtw89_pci_l1ss_set(rtwdev, true);
+}
+
+static void rtw89_pci_ctrl_dma_all_pcie(struct rtw89_dev *rtwdev, u8 en)
+{
+ u32 val32;
+
+ if (en == MAC_AX_FUNC_EN) {
+ val32 = B_AX_STOP_PCIEIO;
+ rtw89_write32_clr(rtwdev, R_AX_PCIE_DMA_STOP1, val32);
+
+ val32 = B_AX_TXHCI_EN | B_AX_RXHCI_EN;
+ rtw89_write32_set(rtwdev, R_AX_PCIE_INIT_CFG1, val32);
+ } else {
+ val32 = B_AX_STOP_PCIEIO;
+ rtw89_write32_set(rtwdev, R_AX_PCIE_DMA_STOP1, val32);
+
+ val32 = B_AX_TXHCI_EN | B_AX_RXHCI_EN;
+ rtw89_write32_clr(rtwdev, R_AX_PCIE_INIT_CFG1, val32);
+ }
+}
+
+static int rtw89_pci_poll_io_idle(struct rtw89_dev *rtwdev)
+{
+ int ret = 0;
+ u32 sts;
+ u32 busy = B_AX_PCIEIO_BUSY | B_AX_PCIEIO_TX_BUSY | B_AX_PCIEIO_RX_BUSY;
+
+ ret = read_poll_timeout_atomic(rtw89_read32, sts, (sts & busy) == 0x0,
+ 10, 1000, false, rtwdev,
+ R_AX_PCIE_DMA_BUSY1);
+ if (ret) {
+ rtw89_err(rtwdev, "pci dmach busy1 0x%X\n",
+ rtw89_read32(rtwdev, R_AX_PCIE_DMA_BUSY1));
+ return -EINVAL;
+ }
+ return ret;
+}
+
+static int rtw89_pci_lv1rst_stop_dma(struct rtw89_dev *rtwdev)
+{
+ u32 val, dma_rst = 0;
+ int ret;
+
+ rtw89_pci_ctrl_dma_all_pcie(rtwdev, MAC_AX_FUNC_DIS);
+ ret = rtw89_pci_poll_io_idle(rtwdev);
+ if (ret) {
+ val = rtw89_read32(rtwdev, R_AX_DBG_ERR_FLAG);
+ rtw89_debug(rtwdev, RTW89_DBG_HCI,
+ "[PCIe] poll_io_idle fail, before 0x%08x: 0x%08x\n",
+ R_AX_DBG_ERR_FLAG, val);
+ if (val & B_AX_TX_STUCK || val & B_AX_PCIE_TXBD_LEN0)
+ dma_rst |= B_AX_HCI_TXDMA_EN;
+ if (val & B_AX_RX_STUCK)
+ dma_rst |= B_AX_HCI_RXDMA_EN;
+ val = rtw89_read32(rtwdev, R_AX_HCI_FUNC_EN);
+ rtw89_write32(rtwdev, R_AX_HCI_FUNC_EN, val & ~dma_rst);
+ rtw89_write32(rtwdev, R_AX_HCI_FUNC_EN, val | dma_rst);
+ ret = rtw89_pci_poll_io_idle(rtwdev);
+ val = rtw89_read32(rtwdev, R_AX_DBG_ERR_FLAG);
+ rtw89_debug(rtwdev, RTW89_DBG_HCI,
+ "[PCIe] poll_io_idle fail, after 0x%08x: 0x%08x\n",
+ R_AX_DBG_ERR_FLAG, val);
+ }
+
+ return ret;
+}
+
+static void rtw89_pci_ctrl_hci_dma_en(struct rtw89_dev *rtwdev, u8 en)
+{
+ u32 val32;
+
+ if (en == MAC_AX_FUNC_EN) {
+ val32 = B_AX_HCI_TXDMA_EN | B_AX_HCI_RXDMA_EN;
+ rtw89_write32_set(rtwdev, R_AX_HCI_FUNC_EN, val32);
+ } else {
+ val32 = B_AX_HCI_TXDMA_EN | B_AX_HCI_RXDMA_EN;
+ rtw89_write32_clr(rtwdev, R_AX_HCI_FUNC_EN, val32);
+ }
+}
+
+static int rtw89_pci_rst_bdram(struct rtw89_dev *rtwdev)
+{
+ int ret = 0;
+ u32 val32, sts;
+
+ val32 = B_AX_RST_BDRAM;
+ rtw89_write32_set(rtwdev, R_AX_PCIE_INIT_CFG1, val32);
+
+ ret = read_poll_timeout_atomic(rtw89_read32, sts,
+ (sts & B_AX_RST_BDRAM) == 0x0, 1, 100,
+ true, rtwdev, R_AX_PCIE_INIT_CFG1);
+ return ret;
+}
+
+static int rtw89_pci_lv1rst_start_dma(struct rtw89_dev *rtwdev)
+{
+ u32 ret;
+
+ rtw89_pci_ctrl_hci_dma_en(rtwdev, MAC_AX_FUNC_DIS);
+ rtw89_pci_ctrl_hci_dma_en(rtwdev, MAC_AX_FUNC_EN);
+ rtw89_pci_clr_idx_all(rtwdev);
+
+ ret = rtw89_pci_rst_bdram(rtwdev);
+ if (ret)
+ return ret;
+
+ rtw89_pci_ctrl_dma_all_pcie(rtwdev, MAC_AX_FUNC_EN);
+ return ret;
+}
+
+static int rtw89_pci_ops_mac_lv1_recovery(struct rtw89_dev *rtwdev,
+ enum rtw89_lv1_rcvy_step step)
+{
+ int ret;
+
+ switch (step) {
+ case RTW89_LV1_RCVY_STEP_1:
+ ret = rtw89_pci_lv1rst_stop_dma(rtwdev);
+ if (ret)
+ rtw89_err(rtwdev, "lv1 rcvy pci stop dma fail\n");
+
+ break;
+
+ case RTW89_LV1_RCVY_STEP_2:
+ ret = rtw89_pci_lv1rst_start_dma(rtwdev);
+ if (ret)
+ rtw89_err(rtwdev, "lv1 rcvy pci start dma fail\n");
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static void rtw89_pci_ops_dump_err_status(struct rtw89_dev *rtwdev)
+{
+ rtw89_info(rtwdev, "R_AX_RPQ_RXBD_IDX =0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_RPQ_RXBD_IDX));
+ rtw89_info(rtwdev, "R_AX_DBG_ERR_FLAG=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_DBG_ERR_FLAG));
+ rtw89_info(rtwdev, "R_AX_LBC_WATCHDOG=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_LBC_WATCHDOG));
+}
+
+static int rtw89_pci_napi_poll(struct napi_struct *napi, int budget)
+{
+ struct rtw89_dev *rtwdev = container_of(napi, struct rtw89_dev, napi);
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ unsigned long flags;
+ u32 cnt;
+ int ret;
+
+ ret = rtw89_pci_poll_rxq_dma(rtwdev, rtwpci, budget);
+ if (ret < budget) {
+ napi_complete_done(napi, ret);
+
+ cnt = rtw89_pci_rxbd_recalc(rtwdev, &rtwpci->rx_rings[RTW89_RXCH_RXQ]);
+ if (cnt && napi_reschedule(napi))
+ return ret;
+
+ spin_lock_irqsave(&rtwpci->irq_lock, flags);
+ if (rtwpci->running) {
+ rtw89_pci_clear_intrs(rtwdev, rtwpci);
+ rtw89_pci_enable_intr_mask0(rtwdev, rtwpci, B_AX_RXDMA_INTS_MASK);
+ }
+ spin_unlock_irqrestore(&rtwpci->irq_lock, flags);
+ }
+
+ return ret;
+}
+
+static int __maybe_unused rtw89_pci_suspend(struct device *dev)
+{
+ struct ieee80211_hw *hw = dev_get_drvdata(dev);
+ struct rtw89_dev *rtwdev = hw->priv;
+
+ rtw89_write32_clr(rtwdev, R_AX_SYS_SDIO_CTRL,
+ B_AX_PCIE_DIS_L2_CTRL_LDO_HCI);
+ rtw89_write32_set(rtwdev, R_AX_RSV_CTRL, B_AX_WLOCK_1C_B6);
+ rtw89_write32_set(rtwdev, R_AX_RSV_CTRL, B_AX_R_DIS_PRST);
+ rtw89_write32_clr(rtwdev, R_AX_RSV_CTRL, B_AX_WLOCK_1C_B6);
+ rtw89_write32_set(rtwdev, R_AX_PCIE_INIT_CFG1,
+ B_AX_PCIE_PERST_KEEP_REG | B_AX_PCIE_TRAIN_KEEP_REG);
+
+ return 0;
+}
+
+static void rtw89_pci_l2_hci_ldo(struct rtw89_dev *rtwdev)
+{
+ if (rtwdev->chip->chip_id == RTL8852C)
+ return;
+
+ /* Hardware need write the reg twice to ensure the setting work */
+ rtw89_dbi_write8_set(rtwdev, RTW89_PCIE_RST_MSTATE,
+ RTW89_PCIE_BIT_CFG_RST_MSTATE);
+ rtw89_dbi_write8_set(rtwdev, RTW89_PCIE_RST_MSTATE,
+ RTW89_PCIE_BIT_CFG_RST_MSTATE);
+}
+
+static int __maybe_unused rtw89_pci_resume(struct device *dev)
+{
+ struct ieee80211_hw *hw = dev_get_drvdata(dev);
+ struct rtw89_dev *rtwdev = hw->priv;
+
+ rtw89_write32_set(rtwdev, R_AX_SYS_SDIO_CTRL,
+ B_AX_PCIE_DIS_L2_CTRL_LDO_HCI);
+ rtw89_write32_set(rtwdev, R_AX_RSV_CTRL, B_AX_WLOCK_1C_B6);
+ rtw89_write32_clr(rtwdev, R_AX_RSV_CTRL, B_AX_R_DIS_PRST);
+ rtw89_write32_clr(rtwdev, R_AX_RSV_CTRL, B_AX_WLOCK_1C_B6);
+ rtw89_write32_clr(rtwdev, R_AX_PCIE_INIT_CFG1,
+ B_AX_PCIE_PERST_KEEP_REG | B_AX_PCIE_TRAIN_KEEP_REG);
+ rtw89_pci_l2_hci_ldo(rtwdev);
+ rtw89_pci_link_cfg(rtwdev);
+ rtw89_pci_l1ss_cfg(rtwdev);
+
+ return 0;
+}
+
+SIMPLE_DEV_PM_OPS(rtw89_pm_ops, rtw89_pci_suspend, rtw89_pci_resume);
+EXPORT_SYMBOL(rtw89_pm_ops);
+
+static const struct rtw89_hci_ops rtw89_pci_ops = {
+ .tx_write = rtw89_pci_ops_tx_write,
+ .tx_kick_off = rtw89_pci_ops_tx_kick_off,
+ .flush_queues = rtw89_pci_ops_flush_queues,
+ .reset = rtw89_pci_ops_reset,
+ .start = rtw89_pci_ops_start,
+ .stop = rtw89_pci_ops_stop,
+ .link_ps = rtw89_pci_link_ps,
+
+ .read8 = rtw89_pci_ops_read8,
+ .read16 = rtw89_pci_ops_read16,
+ .read32 = rtw89_pci_ops_read32,
+ .write8 = rtw89_pci_ops_write8,
+ .write16 = rtw89_pci_ops_write16,
+ .write32 = rtw89_pci_ops_write32,
+
+ .mac_pre_init = rtw89_pci_ops_mac_pre_init,
+ .mac_post_init = rtw89_pci_ops_mac_post_init,
+ .deinit = rtw89_pci_ops_deinit,
+
+ .check_and_reclaim_tx_resource = rtw89_pci_check_and_reclaim_tx_resource,
+ .mac_lv1_rcvy = rtw89_pci_ops_mac_lv1_recovery,
+ .dump_err_status = rtw89_pci_ops_dump_err_status,
+ .napi_poll = rtw89_pci_napi_poll,
+};
+
+static int rtw89_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct ieee80211_hw *hw;
+ struct rtw89_dev *rtwdev;
+ int driver_data_size;
+ int ret;
+
+ driver_data_size = sizeof(struct rtw89_dev) + sizeof(struct rtw89_pci);
+ hw = ieee80211_alloc_hw(driver_data_size, &rtw89_ops);
+ if (!hw) {
+ dev_err(&pdev->dev, "failed to allocate hw\n");
+ return -ENOMEM;
+ }
+
+ rtwdev = hw->priv;
+ rtwdev->hw = hw;
+ rtwdev->dev = &pdev->dev;
+ rtwdev->hci.ops = &rtw89_pci_ops;
+ rtwdev->hci.type = RTW89_HCI_TYPE_PCIE;
+ rtwdev->hci.rpwm_addr = R_AX_PCIE_HRPWM;
+ rtwdev->hci.cpwm_addr = R_AX_CPWM;
+
+ SET_IEEE80211_DEV(rtwdev->hw, &pdev->dev);
+
+ switch (id->driver_data) {
+ case RTL8852A:
+ rtwdev->chip = &rtw8852a_chip_info;
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ ret = rtw89_core_init(rtwdev);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to initialise core\n");
+ goto err_release_hw;
+ }
+
+ ret = rtw89_pci_claim_device(rtwdev, pdev);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to claim pci device\n");
+ goto err_core_deinit;
+ }
+
+ ret = rtw89_pci_setup_resource(rtwdev, pdev);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to setup pci resource\n");
+ goto err_declaim_pci;
+ }
+
+ ret = rtw89_chip_info_setup(rtwdev);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to setup chip information\n");
+ goto err_clear_resource;
+ }
+
+ rtw89_pci_link_cfg(rtwdev);
+ rtw89_pci_l1ss_cfg(rtwdev);
+
+ ret = rtw89_core_register(rtwdev);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to register core\n");
+ goto err_clear_resource;
+ }
+
+ rtw89_core_napi_init(rtwdev);
+
+ ret = rtw89_pci_request_irq(rtwdev, pdev);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to request pci irq\n");
+ goto err_unregister;
+ }
+
+ return 0;
+
+err_unregister:
+ rtw89_core_napi_deinit(rtwdev);
+ rtw89_core_unregister(rtwdev);
+err_clear_resource:
+ rtw89_pci_clear_resource(rtwdev, pdev);
+err_declaim_pci:
+ rtw89_pci_declaim_device(rtwdev, pdev);
+err_core_deinit:
+ rtw89_core_deinit(rtwdev);
+err_release_hw:
+ ieee80211_free_hw(hw);
+
+ return ret;
+}
+
+static void rtw89_pci_remove(struct pci_dev *pdev)
+{
+ struct ieee80211_hw *hw = pci_get_drvdata(pdev);
+ struct rtw89_dev *rtwdev;
+
+ rtwdev = hw->priv;
+
+ rtw89_pci_free_irq(rtwdev, pdev);
+ rtw89_core_napi_deinit(rtwdev);
+ rtw89_core_unregister(rtwdev);
+ rtw89_pci_clear_resource(rtwdev, pdev);
+ rtw89_pci_declaim_device(rtwdev, pdev);
+ rtw89_core_deinit(rtwdev);
+ ieee80211_free_hw(hw);
+}
+
+static const struct pci_device_id rtw89_pci_id_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8852), .driver_data = RTL8852A },
+ {},
+};
+MODULE_DEVICE_TABLE(pci, rtw89_pci_id_table);
+
+static struct pci_driver rtw89_pci_driver = {
+ .name = "rtw89_pci",
+ .id_table = rtw89_pci_id_table,
+ .probe = rtw89_pci_probe,
+ .remove = rtw89_pci_remove,
+ .driver.pm = &rtw89_pm_ops,
+};
+module_pci_driver(rtw89_pci_driver);
+
+MODULE_AUTHOR("Realtek Corporation");
+MODULE_DESCRIPTION("Realtek 802.11ax wireless PCI driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/realtek/rtw89/pci.h b/drivers/net/wireless/realtek/rtw89/pci.h
new file mode 100644
index 000000000000..353ecc64b1f3
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw89/pci.h
@@ -0,0 +1,601 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/* Copyright(c) 2020 Realtek Corporation
+ */
+
+#ifndef __RTW89_PCI_H__
+#define __RTW89_PCI_H__
+
+#include "txrx.h"
+
+#define MDIO_PG0_G1 0
+#define MDIO_PG1_G1 1
+#define MDIO_PG0_G2 2
+#define MDIO_PG1_G2 3
+#define RAC_ANA10 0x10
+#define RAC_ANA19 0x19
+#define RAC_ANA1F 0x1F
+#define RAC_ANA24 0x24
+#define B_AX_DEGLITCH GENMASK(11, 8)
+#define RAC_ANA26 0x26
+#define B_AX_RXEN GENMASK(15, 14)
+#define RAC_CTRL_PPR_V1 0x30
+#define B_AX_CLK_CALIB_EN BIT(12)
+#define B_AX_CALIB_EN BIT(13)
+#define B_AX_DIV GENMASK(15, 14)
+#define RAC_SET_PPR_V1 0x31
+
+#define R_AX_DBI_FLAG 0x1090
+#define B_AX_DBI_RFLAG BIT(17)
+#define B_AX_DBI_WFLAG BIT(16)
+#define B_AX_DBI_WREN_MSK GENMASK(15, 12)
+#define B_AX_DBI_ADDR_MSK GENMASK(11, 2)
+#define R_AX_DBI_WDATA 0x1094
+#define R_AX_DBI_RDATA 0x1098
+
+#define R_AX_MDIO_WDATA 0x10A4
+#define R_AX_MDIO_RDATA 0x10A6
+
+#define RTW89_PCI_WR_RETRY_CNT 20
+
+/* Interrupts */
+#define R_AX_HIMR0 0x01A0
+#define B_AX_HALT_C2H_INT_EN BIT(21)
+#define R_AX_HISR0 0x01A4
+
+#define R_AX_MDIO_CFG 0x10A0
+#define B_AX_MDIO_PHY_ADDR_MASK GENMASK(13, 12)
+#define B_AX_MDIO_RFLAG BIT(9)
+#define B_AX_MDIO_WFLAG BIT(8)
+#define B_AX_MDIO_ADDR_MASK GENMASK(4, 0)
+
+#define R_AX_PCIE_HIMR00 0x10B0
+#define B_AX_HC00ISR_IND_INT_EN BIT(27)
+#define B_AX_HD1ISR_IND_INT_EN BIT(26)
+#define B_AX_HD0ISR_IND_INT_EN BIT(25)
+#define B_AX_HS0ISR_IND_INT_EN BIT(24)
+#define B_AX_RETRAIN_INT_EN BIT(21)
+#define B_AX_RPQBD_FULL_INT_EN BIT(20)
+#define B_AX_RDU_INT_EN BIT(19)
+#define B_AX_RXDMA_STUCK_INT_EN BIT(18)
+#define B_AX_TXDMA_STUCK_INT_EN BIT(17)
+#define B_AX_PCIE_HOTRST_INT_EN BIT(16)
+#define B_AX_PCIE_FLR_INT_EN BIT(15)
+#define B_AX_PCIE_PERST_INT_EN BIT(14)
+#define B_AX_TXDMA_CH12_INT_EN BIT(13)
+#define B_AX_TXDMA_CH9_INT_EN BIT(12)
+#define B_AX_TXDMA_CH8_INT_EN BIT(11)
+#define B_AX_TXDMA_ACH7_INT_EN BIT(10)
+#define B_AX_TXDMA_ACH6_INT_EN BIT(9)
+#define B_AX_TXDMA_ACH5_INT_EN BIT(8)
+#define B_AX_TXDMA_ACH4_INT_EN BIT(7)
+#define B_AX_TXDMA_ACH3_INT_EN BIT(6)
+#define B_AX_TXDMA_ACH2_INT_EN BIT(5)
+#define B_AX_TXDMA_ACH1_INT_EN BIT(4)
+#define B_AX_TXDMA_ACH0_INT_EN BIT(3)
+#define B_AX_RPQDMA_INT_EN BIT(2)
+#define B_AX_RXP1DMA_INT_EN BIT(1)
+#define B_AX_RXDMA_INT_EN BIT(0)
+
+#define R_AX_PCIE_HISR00 0x10B4
+#define B_AX_HC00ISR_IND_INT BIT(27)
+#define B_AX_HD1ISR_IND_INT BIT(26)
+#define B_AX_HD0ISR_IND_INT BIT(25)
+#define B_AX_HS0ISR_IND_INT BIT(24)
+#define B_AX_RETRAIN_INT BIT(21)
+#define B_AX_RPQBD_FULL_INT BIT(20)
+#define B_AX_RDU_INT BIT(19)
+#define B_AX_RXDMA_STUCK_INT BIT(18)
+#define B_AX_TXDMA_STUCK_INT BIT(17)
+#define B_AX_PCIE_HOTRST_INT BIT(16)
+#define B_AX_PCIE_FLR_INT BIT(15)
+#define B_AX_PCIE_PERST_INT BIT(14)
+#define B_AX_TXDMA_CH12_INT BIT(13)
+#define B_AX_TXDMA_CH9_INT BIT(12)
+#define B_AX_TXDMA_CH8_INT BIT(11)
+#define B_AX_TXDMA_ACH7_INT BIT(10)
+#define B_AX_TXDMA_ACH6_INT BIT(9)
+#define B_AX_TXDMA_ACH5_INT BIT(8)
+#define B_AX_TXDMA_ACH4_INT BIT(7)
+#define B_AX_TXDMA_ACH3_INT BIT(6)
+#define B_AX_TXDMA_ACH2_INT BIT(5)
+#define B_AX_TXDMA_ACH1_INT BIT(4)
+#define B_AX_TXDMA_ACH0_INT BIT(3)
+#define B_AX_RPQDMA_INT BIT(2)
+#define B_AX_RXP1DMA_INT BIT(1)
+#define B_AX_RXDMA_INT BIT(0)
+#define B_AX_RXDMA_INTS_MASK (B_AX_RXDMA_INT | B_AX_RXP1DMA_INT | B_AX_RDU_INT)
+
+#define R_AX_PCIE_HIMR10 0x13B0
+#define B_AX_HC10ISR_IND_INT_EN BIT(28)
+#define B_AX_TXDMA_CH11_INT_EN BIT(12)
+#define B_AX_TXDMA_CH10_INT_EN BIT(11)
+
+#define R_AX_PCIE_HISR10 0x13B4
+#define B_AX_HC10ISR_IND_INT BIT(28)
+#define B_AX_TXDMA_CH11_INT BIT(12)
+#define B_AX_TXDMA_CH10_INT BIT(11)
+
+/* TX/RX */
+#define R_AX_RXQ_RXBD_IDX 0x1050
+#define R_AX_RPQ_RXBD_IDX 0x1054
+#define R_AX_ACH0_TXBD_IDX 0x1058
+#define R_AX_ACH1_TXBD_IDX 0x105C
+#define R_AX_ACH2_TXBD_IDX 0x1060
+#define R_AX_ACH3_TXBD_IDX 0x1064
+#define R_AX_ACH4_TXBD_IDX 0x1068
+#define R_AX_ACH5_TXBD_IDX 0x106C
+#define R_AX_ACH6_TXBD_IDX 0x1070
+#define R_AX_ACH7_TXBD_IDX 0x1074
+#define R_AX_CH8_TXBD_IDX 0x1078 /* Management Queue band 0 */
+#define R_AX_CH9_TXBD_IDX 0x107C /* HI Queue band 0 */
+#define R_AX_CH10_TXBD_IDX 0x137C /* Management Queue band 1 */
+#define R_AX_CH11_TXBD_IDX 0x1380 /* HI Queue band 1 */
+#define R_AX_CH12_TXBD_IDX 0x1080 /* FWCMD Queue */
+#define TXBD_HW_IDX_MASK GENMASK(27, 16)
+#define TXBD_HOST_IDX_MASK GENMASK(11, 0)
+
+#define R_AX_ACH0_TXBD_DESA_L 0x1110
+#define R_AX_ACH0_TXBD_DESA_H 0x1114
+#define R_AX_ACH1_TXBD_DESA_L 0x1118
+#define R_AX_ACH1_TXBD_DESA_H 0x111C
+#define R_AX_ACH2_TXBD_DESA_L 0x1120
+#define R_AX_ACH2_TXBD_DESA_H 0x1124
+#define R_AX_ACH3_TXBD_DESA_L 0x1128
+#define R_AX_ACH3_TXBD_DESA_H 0x112C
+#define R_AX_ACH4_TXBD_DESA_L 0x1130
+#define R_AX_ACH4_TXBD_DESA_H 0x1134
+#define R_AX_ACH5_TXBD_DESA_L 0x1138
+#define R_AX_ACH5_TXBD_DESA_H 0x113C
+#define R_AX_ACH6_TXBD_DESA_L 0x1140
+#define R_AX_ACH6_TXBD_DESA_H 0x1144
+#define R_AX_ACH7_TXBD_DESA_L 0x1148
+#define R_AX_ACH7_TXBD_DESA_H 0x114C
+#define R_AX_CH8_TXBD_DESA_L 0x1150
+#define R_AX_CH8_TXBD_DESA_H 0x1154
+#define R_AX_CH9_TXBD_DESA_L 0x1158
+#define R_AX_CH9_TXBD_DESA_H 0x115C
+#define R_AX_CH10_TXBD_DESA_L 0x1358
+#define R_AX_CH10_TXBD_DESA_H 0x135C
+#define R_AX_CH11_TXBD_DESA_L 0x1360
+#define R_AX_CH11_TXBD_DESA_H 0x1364
+#define R_AX_CH12_TXBD_DESA_L 0x1160
+#define R_AX_CH12_TXBD_DESA_H 0x1164
+#define R_AX_RXQ_RXBD_DESA_L 0x1100
+#define R_AX_RXQ_RXBD_DESA_H 0x1104
+#define R_AX_RPQ_RXBD_DESA_L 0x1108
+#define R_AX_RPQ_RXBD_DESA_H 0x110C
+#define B_AX_DESC_NUM_MSK GENMASK(11, 0)
+
+#define R_AX_RXQ_RXBD_NUM 0x1020
+#define R_AX_RPQ_RXBD_NUM 0x1022
+#define R_AX_ACH0_TXBD_NUM 0x1024
+#define R_AX_ACH1_TXBD_NUM 0x1026
+#define R_AX_ACH2_TXBD_NUM 0x1028
+#define R_AX_ACH3_TXBD_NUM 0x102A
+#define R_AX_ACH4_TXBD_NUM 0x102C
+#define R_AX_ACH5_TXBD_NUM 0x102E
+#define R_AX_ACH6_TXBD_NUM 0x1030
+#define R_AX_ACH7_TXBD_NUM 0x1032
+#define R_AX_CH8_TXBD_NUM 0x1034
+#define R_AX_CH9_TXBD_NUM 0x1036
+#define R_AX_CH10_TXBD_NUM 0x1338
+#define R_AX_CH11_TXBD_NUM 0x133A
+#define R_AX_CH12_TXBD_NUM 0x1038
+
+#define R_AX_ACH0_BDRAM_CTRL 0x1200
+#define R_AX_ACH1_BDRAM_CTRL 0x1204
+#define R_AX_ACH2_BDRAM_CTRL 0x1208
+#define R_AX_ACH3_BDRAM_CTRL 0x120C
+#define R_AX_ACH4_BDRAM_CTRL 0x1210
+#define R_AX_ACH5_BDRAM_CTRL 0x1214
+#define R_AX_ACH6_BDRAM_CTRL 0x1218
+#define R_AX_ACH7_BDRAM_CTRL 0x121C
+#define R_AX_CH8_BDRAM_CTRL 0x1220
+#define R_AX_CH9_BDRAM_CTRL 0x1224
+#define R_AX_CH10_BDRAM_CTRL 0x1320
+#define R_AX_CH11_BDRAM_CTRL 0x1324
+#define R_AX_CH12_BDRAM_CTRL 0x1228
+#define BDRAM_SIDX_MASK GENMASK(7, 0)
+#define BDRAM_MAX_MASK GENMASK(15, 8)
+#define BDRAM_MIN_MASK GENMASK(23, 16)
+
+#define R_AX_PCIE_INIT_CFG1 0x1000
+#define B_AX_PCIE_RXRST_KEEP_REG BIT(23)
+#define B_AX_PCIE_TXRST_KEEP_REG BIT(22)
+#define B_AX_PCIE_PERST_KEEP_REG BIT(21)
+#define B_AX_PCIE_FLR_KEEP_REG BIT(20)
+#define B_AX_PCIE_TRAIN_KEEP_REG BIT(19)
+#define B_AX_RXBD_MODE BIT(18)
+#define B_AX_PCIE_MAX_RXDMA_MASK GENMASK(16, 14)
+#define B_AX_RXHCI_EN BIT(13)
+#define B_AX_LATENCY_CONTROL BIT(12)
+#define B_AX_TXHCI_EN BIT(11)
+#define B_AX_PCIE_MAX_TXDMA_MASK GENMASK(10, 8)
+#define B_AX_TX_TRUNC_MODE BIT(5)
+#define B_AX_RX_TRUNC_MODE BIT(4)
+#define B_AX_RST_BDRAM BIT(3)
+#define B_AX_DIS_RXDMA_PRE BIT(2)
+
+#define R_AX_TXDMA_ADDR_H 0x10F0
+#define R_AX_RXDMA_ADDR_H 0x10F4
+
+#define R_AX_PCIE_DMA_STOP1 0x1010
+#define B_AX_STOP_PCIEIO BIT(20)
+#define B_AX_STOP_WPDMA BIT(19)
+#define B_AX_STOP_CH12 BIT(18)
+#define B_AX_STOP_CH9 BIT(17)
+#define B_AX_STOP_CH8 BIT(16)
+#define B_AX_STOP_ACH7 BIT(15)
+#define B_AX_STOP_ACH6 BIT(14)
+#define B_AX_STOP_ACH5 BIT(13)
+#define B_AX_STOP_ACH4 BIT(12)
+#define B_AX_STOP_ACH3 BIT(11)
+#define B_AX_STOP_ACH2 BIT(10)
+#define B_AX_STOP_ACH1 BIT(9)
+#define B_AX_STOP_ACH0 BIT(8)
+#define B_AX_STOP_RPQ BIT(1)
+#define B_AX_STOP_RXQ BIT(0)
+#define B_AX_TX_STOP1_ALL GENMASK(18, 8)
+
+#define R_AX_PCIE_DMA_STOP2 0x1310
+#define B_AX_STOP_CH11 BIT(1)
+#define B_AX_STOP_CH10 BIT(0)
+#define B_AX_TX_STOP2_ALL GENMASK(1, 0)
+
+#define R_AX_TXBD_RWPTR_CLR1 0x1014
+#define B_AX_CLR_CH12_IDX BIT(10)
+#define B_AX_CLR_CH9_IDX BIT(9)
+#define B_AX_CLR_CH8_IDX BIT(8)
+#define B_AX_CLR_ACH7_IDX BIT(7)
+#define B_AX_CLR_ACH6_IDX BIT(6)
+#define B_AX_CLR_ACH5_IDX BIT(5)
+#define B_AX_CLR_ACH4_IDX BIT(4)
+#define B_AX_CLR_ACH3_IDX BIT(3)
+#define B_AX_CLR_ACH2_IDX BIT(2)
+#define B_AX_CLR_ACH1_IDX BIT(1)
+#define B_AX_CLR_ACH0_IDX BIT(0)
+#define B_AX_TXBD_CLR1_ALL GENMASK(10, 0)
+
+#define R_AX_RXBD_RWPTR_CLR 0x1018
+#define B_AX_CLR_RPQ_IDX BIT(1)
+#define B_AX_CLR_RXQ_IDX BIT(0)
+#define B_AX_RXBD_CLR_ALL GENMASK(1, 0)
+
+#define R_AX_TXBD_RWPTR_CLR2 0x1314
+#define B_AX_CLR_CH11_IDX BIT(1)
+#define B_AX_CLR_CH10_IDX BIT(0)
+#define B_AX_TXBD_CLR2_ALL GENMASK(1, 0)
+
+#define R_AX_PCIE_DMA_BUSY1 0x101C
+#define B_AX_PCIEIO_RX_BUSY BIT(22)
+#define B_AX_PCIEIO_TX_BUSY BIT(21)
+#define B_AX_PCIEIO_BUSY BIT(20)
+#define B_AX_WPDMA_BUSY BIT(19)
+
+#define R_AX_PCIE_DMA_BUSY2 0x131C
+#define B_AX_CH11_BUSY BIT(1)
+#define B_AX_CH10_BUSY BIT(0)
+
+/* Configure */
+#define R_AX_PCIE_INIT_CFG1 0x1000
+#define B_AX_PCIE_RXRST_KEEP_REG BIT(23)
+#define B_AX_PCIE_TXRST_KEEP_REG BIT(22)
+#define B_AX_DIS_RXDMA_PRE BIT(2)
+
+#define R_AX_PCIE_INIT_CFG2 0x1004
+#define B_AX_WD_ITVL_IDLE GENMASK(27, 24)
+#define B_AX_WD_ITVL_ACT GENMASK(19, 16)
+
+#define R_AX_PCIE_PS_CTRL 0x1008
+#define B_AX_L1OFF_PWR_OFF_EN BIT(5)
+
+#define R_AX_DBG_ERR_FLAG 0x11C4
+#define B_AX_PCIE_RPQ_FULL BIT(29)
+#define B_AX_PCIE_RXQ_FULL BIT(28)
+#define B_AX_CPL_STATUS_MASK GENMASK(27, 25)
+#define B_AX_RX_STUCK BIT(22)
+#define B_AX_TX_STUCK BIT(21)
+#define B_AX_PCIEDBG_TXERR0 BIT(16)
+#define B_AX_PCIE_RXP1_ERR0 BIT(4)
+#define B_AX_PCIE_TXBD_LEN0 BIT(1)
+#define B_AX_PCIE_TXBD_4KBOUD_LENERR BIT(0)
+
+#define R_AX_LBC_WATCHDOG 0x11D8
+#define B_AX_LBC_TIMER GENMASK(7, 4)
+#define B_AX_LBC_FLAG BIT(1)
+#define B_AX_LBC_EN BIT(0)
+
+#define R_AX_PCIE_EXP_CTRL 0x13F0
+#define B_AX_EN_CHKDSC_NO_RX_STUCK BIT(20)
+#define B_AX_MAX_TAG_NUM GENMASK(18, 16)
+#define B_AX_SIC_EN_FORCE_CLKREQ BIT(4)
+
+#define R_AX_PCIE_RX_PREF_ADV 0x13F4
+#define B_AX_RXDMA_PREF_ADV_EN BIT(0)
+
+#define RTW89_PCI_TXBD_NUM_MAX 256
+#define RTW89_PCI_RXBD_NUM_MAX 256
+#define RTW89_PCI_TXWD_NUM_MAX 512
+#define RTW89_PCI_TXWD_PAGE_SIZE 128
+#define RTW89_PCI_ADDRINFO_MAX 4
+#define RTW89_PCI_RX_BUF_SIZE 11460
+
+#define RTW89_PCI_POLL_BDRAM_RST_CNT 100
+
+/* PCIE CFG register */
+#define RTW89_PCIE_TIMER_CTRL 0x0718
+#define RTW89_PCIE_BIT_L1SUB BIT(5)
+#define RTW89_PCIE_L1_CTRL 0x0719
+#define RTW89_PCIE_BIT_CLK BIT(4)
+#define RTW89_PCIE_BIT_L1 BIT(3)
+#define RTW89_PCIE_CLK_CTRL 0x0725
+#define RTW89_PCIE_RST_MSTATE 0x0B48
+#define RTW89_PCIE_BIT_CFG_RST_MSTATE BIT(0)
+#define RTW89_PCIE_PHY_RATE 0x82
+#define RTW89_PCIE_PHY_RATE_MASK GENMASK(1, 0)
+#define INTF_INTGRA_MINREF_V1 90
+#define INTF_INTGRA_HOSTREF_V1 100
+
+enum rtw89_pcie_phy {
+ PCIE_PHY_GEN1,
+ PCIE_PHY_GEN2,
+ PCIE_PHY_GEN1_UNDEFINE = 0x7F,
+};
+
+enum mac_ax_func_sw {
+ MAC_AX_FUNC_DIS,
+ MAC_AX_FUNC_EN,
+};
+
+enum rtw89_pcie_clkdly_hw {
+ PCIE_CLKDLY_HW_0 = 0,
+ PCIE_CLKDLY_HW_30US = 0x1,
+ PCIE_CLKDLY_HW_50US = 0x2,
+ PCIE_CLKDLY_HW_100US = 0x3,
+ PCIE_CLKDLY_HW_150US = 0x4,
+ PCIE_CLKDLY_HW_200US = 0x5
+};
+
+struct rtw89_pci_bd_ram {
+ u8 start_idx;
+ u8 max_num;
+ u8 min_num;
+};
+
+struct rtw89_pci_tx_data {
+ dma_addr_t dma;
+};
+
+struct rtw89_pci_rx_info {
+ dma_addr_t dma;
+ u32 fs:1, ls:1, tag:11, len:14;
+};
+
+#define RTW89_PCI_TXBD_OPTION_LS BIT(14)
+
+struct rtw89_pci_tx_bd_32 {
+ __le16 length;
+ __le16 option;
+ __le32 dma;
+} __packed;
+
+#define RTW89_PCI_TXWP_VALID BIT(15)
+
+struct rtw89_pci_tx_wp_info {
+ __le16 seq0;
+ __le16 seq1;
+ __le16 seq2;
+ __le16 seq3;
+} __packed;
+
+#define RTW89_PCI_ADDR_MSDU_LS BIT(15)
+#define RTW89_PCI_ADDR_LS BIT(14)
+#define RTW89_PCI_ADDR_HIGH(a) (((a) << 6) & GENMASK(13, 6))
+#define RTW89_PCI_ADDR_NUM(x) ((x) & GENMASK(5, 0))
+
+struct rtw89_pci_tx_addr_info_32 {
+ __le16 length;
+ __le16 option;
+ __le32 dma;
+} __packed;
+
+#define RTW89_PCI_RPP_POLLUTED BIT(31)
+#define RTW89_PCI_RPP_SEQ GENMASK(30, 16)
+#define RTW89_PCI_RPP_TX_STATUS GENMASK(15, 13)
+#define RTW89_TX_DONE 0x0
+#define RTW89_TX_RETRY_LIMIT 0x1
+#define RTW89_TX_LIFE_TIME 0x2
+#define RTW89_TX_MACID_DROP 0x3
+#define RTW89_PCI_RPP_QSEL GENMASK(12, 8)
+#define RTW89_PCI_RPP_MACID GENMASK(7, 0)
+
+struct rtw89_pci_rpp_fmt {
+ __le32 dword;
+} __packed;
+
+struct rtw89_pci_rx_bd_32 {
+ __le16 buf_size;
+ __le16 rsvd;
+ __le32 dma;
+} __packed;
+
+#define RTW89_PCI_RXBD_FS BIT(15)
+#define RTW89_PCI_RXBD_LS BIT(14)
+#define RTW89_PCI_RXBD_WRITE_SIZE GENMASK(13, 0)
+#define RTW89_PCI_RXBD_TAG GENMASK(28, 16)
+
+struct rtw89_pci_rxbd_info {
+ __le32 dword;
+};
+
+struct rtw89_pci_tx_wd {
+ struct list_head list;
+ struct sk_buff_head queue;
+
+ void *vaddr;
+ dma_addr_t paddr;
+ u32 len;
+ u32 seq;
+};
+
+struct rtw89_pci_dma_ring {
+ void *head;
+ u8 desc_size;
+ dma_addr_t dma;
+
+ u32 addr_num;
+ u32 addr_idx;
+ u32 addr_bdram;
+ u32 addr_desa_l;
+ u32 addr_desa_h;
+
+ u32 len;
+ u32 wp; /* host idx */
+ u32 rp; /* hw idx */
+};
+
+struct rtw89_pci_tx_wd_ring {
+ void *head;
+ dma_addr_t dma;
+
+ struct rtw89_pci_tx_wd pages[RTW89_PCI_TXWD_NUM_MAX];
+ struct list_head free_pages;
+
+ u32 page_size;
+ u32 page_num;
+ u32 curr_num;
+};
+
+#define RTW89_RX_TAG_MAX 0x1fff
+
+struct rtw89_pci_tx_ring {
+ struct rtw89_pci_tx_wd_ring wd_ring;
+ struct rtw89_pci_dma_ring bd_ring;
+ struct list_head busy_pages;
+ u8 txch;
+ bool dma_enabled;
+ u16 tag; /* range from 0x0001 ~ 0x1fff */
+
+ u64 tx_cnt;
+ u64 tx_acked;
+ u64 tx_retry_lmt;
+ u64 tx_life_time;
+ u64 tx_mac_id_drop;
+};
+
+struct rtw89_pci_rx_ring {
+ struct rtw89_pci_dma_ring bd_ring;
+ struct sk_buff *buf[RTW89_PCI_RXBD_NUM_MAX];
+ u32 buf_sz;
+ struct sk_buff *diliver_skb;
+ struct rtw89_rx_desc_info diliver_desc;
+};
+
+struct rtw89_pci {
+ struct pci_dev *pdev;
+
+ /* protect HW irq related registers */
+ spinlock_t irq_lock;
+ /* protect TRX resources (exclude RXQ) */
+ spinlock_t trx_lock;
+ bool running;
+ struct rtw89_pci_tx_ring tx_rings[RTW89_TXCH_NUM];
+ struct rtw89_pci_rx_ring rx_rings[RTW89_RXCH_NUM];
+ struct sk_buff_head h2c_queue;
+
+ u32 halt_c2h_isrs;
+ u32 halt_c2h_intrs;
+ u32 intrs[2];
+ u32 isrs[2];
+ u16 link_ctrl;
+ void __iomem *mmap;
+};
+
+static inline struct rtw89_pci_rx_info *RTW89_PCI_RX_SKB_CB(struct sk_buff *skb)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ BUILD_BUG_ON(sizeof(struct rtw89_pci_tx_data) >
+ sizeof(info->status.status_driver_data));
+
+ return (struct rtw89_pci_rx_info *)skb->cb;
+}
+
+static inline struct rtw89_pci_rx_bd_32 *
+RTW89_PCI_RX_BD(struct rtw89_pci_rx_ring *rx_ring, u32 idx)
+{
+ struct rtw89_pci_dma_ring *bd_ring = &rx_ring->bd_ring;
+ u8 *head = bd_ring->head;
+ u32 desc_size = bd_ring->desc_size;
+ u32 offset = idx * desc_size;
+
+ return (struct rtw89_pci_rx_bd_32 *)(head + offset);
+}
+
+static inline void
+rtw89_pci_rxbd_increase(struct rtw89_pci_rx_ring *rx_ring, u32 cnt)
+{
+ struct rtw89_pci_dma_ring *bd_ring = &rx_ring->bd_ring;
+
+ bd_ring->wp += cnt;
+
+ if (bd_ring->wp >= bd_ring->len)
+ bd_ring->wp -= bd_ring->len;
+}
+
+static inline struct rtw89_pci_tx_data *RTW89_PCI_TX_SKB_CB(struct sk_buff *skb)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ return (struct rtw89_pci_tx_data *)info->status.status_driver_data;
+}
+
+static inline struct rtw89_pci_tx_bd_32 *
+rtw89_pci_get_next_txbd(struct rtw89_pci_tx_ring *tx_ring)
+{
+ struct rtw89_pci_dma_ring *bd_ring = &tx_ring->bd_ring;
+ struct rtw89_pci_tx_bd_32 *tx_bd, *head;
+
+ head = bd_ring->head;
+ tx_bd = head + bd_ring->wp;
+
+ return tx_bd;
+}
+
+static inline struct rtw89_pci_tx_wd *
+rtw89_pci_dequeue_txwd(struct rtw89_pci_tx_ring *tx_ring)
+{
+ struct rtw89_pci_tx_wd_ring *wd_ring = &tx_ring->wd_ring;
+ struct rtw89_pci_tx_wd *txwd;
+
+ txwd = list_first_entry_or_null(&wd_ring->free_pages,
+ struct rtw89_pci_tx_wd, list);
+ if (!txwd)
+ return NULL;
+
+ list_del_init(&txwd->list);
+ txwd->len = 0;
+ wd_ring->curr_num--;
+
+ return txwd;
+}
+
+static inline void
+rtw89_pci_enqueue_txwd(struct rtw89_pci_tx_ring *tx_ring,
+ struct rtw89_pci_tx_wd *txwd)
+{
+ struct rtw89_pci_tx_wd_ring *wd_ring = &tx_ring->wd_ring;
+
+ memset(txwd->vaddr, 0, wd_ring->page_size);
+ list_add_tail(&txwd->list, &wd_ring->free_pages);
+ wd_ring->curr_num++;
+}
+
+static inline bool rtw89_pci_ltr_is_err_reg_val(u32 val)
+{
+ return val == 0xffffffff || val == 0xeaeaeaea;
+}
+
+extern const struct dev_pm_ops rtw89_pm_ops;
+
+#endif
--
2.25.1

2021-06-18 06:50:49

by Pkshih

[permalink] [raw]
Subject: [PATCH 05/24] rtw89: add efuse files

Efuse is divided into several parts, in which physical map and PHY cap
parts are read and parsed by driver.

Storing main data, physical map is translated into logic map, and then use
chip specific map to explain the logic map. Then, we can have MAC address,
country code, thermal tracking calibration values, and so on.

PHY cap part is used to store PHY data. We don't need to translate it,
because it's a direct map, like logic map.

Signed-off-by: Ping-Ke Shih <[email protected]>
---
drivers/net/wireless/realtek/rtw89/efuse.c | 188 +++++++++++++++++++++
drivers/net/wireless/realtek/rtw89/efuse.h | 13 ++
2 files changed, 201 insertions(+)
create mode 100644 drivers/net/wireless/realtek/rtw89/efuse.c
create mode 100644 drivers/net/wireless/realtek/rtw89/efuse.h

diff --git a/drivers/net/wireless/realtek/rtw89/efuse.c b/drivers/net/wireless/realtek/rtw89/efuse.c
new file mode 100644
index 000000000000..3bea7d813985
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw89/efuse.c
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Copyright(c) 2019-2020 Realtek Corporation
+ */
+
+#include "debug.h"
+#include "efuse.h"
+#include "reg.h"
+
+enum rtw89_efuse_bank {
+ RTW89_EFUSE_BANK_WIFI,
+ RTW89_EFUSE_BANK_BT,
+};
+
+static int rtw89_switch_efuse_bank(struct rtw89_dev *rtwdev,
+ enum rtw89_efuse_bank bank)
+{
+ u8 val;
+
+ val = rtw89_read32_mask(rtwdev, R_AX_EFUSE_CTRL_1,
+ B_AX_EF_CELL_SEL_MASK);
+ if (bank == val)
+ return 0;
+
+ rtw89_write32_mask(rtwdev, R_AX_EFUSE_CTRL_1, B_AX_EF_CELL_SEL_MASK,
+ bank);
+
+ val = rtw89_read32_mask(rtwdev, R_AX_EFUSE_CTRL_1,
+ B_AX_EF_CELL_SEL_MASK);
+ if (bank == val)
+ return 0;
+
+ return -EBUSY;
+}
+
+static int rtw89_dump_physical_efuse_map(struct rtw89_dev *rtwdev, u8 *map,
+ u32 dump_addr, u32 dump_size)
+{
+ u32 efuse_ctl;
+ u32 addr;
+ int ret;
+
+ rtw89_switch_efuse_bank(rtwdev, RTW89_EFUSE_BANK_WIFI);
+
+ for (addr = dump_addr; addr < dump_addr + dump_size; addr++) {
+ efuse_ctl = u32_encode_bits(addr, B_AX_EF_ADDR_MASK);
+ rtw89_write32(rtwdev, R_AX_EFUSE_CTRL, efuse_ctl & ~B_AX_EF_RDY);
+
+ ret = read_poll_timeout_atomic(rtw89_read32, efuse_ctl,
+ efuse_ctl & B_AX_EF_RDY, 1, 1000000,
+ true, rtwdev, R_AX_EFUSE_CTRL);
+ if (ret)
+ return -EBUSY;
+
+ *map++ = (u8)(efuse_ctl & 0xff);
+ }
+
+ return 0;
+}
+
+#define invalid_efuse_header(hdr1, hdr2) \
+ ((hdr1) == 0xff || (hdr2) == 0xff)
+#define invalid_efuse_content(word_en, i) \
+ (((word_en) & BIT(i)) != 0x0)
+#define get_efuse_blk_idx(hdr1, hdr2) \
+ ((((hdr2) & 0xf0) >> 4) | (((hdr1) & 0x0f) << 4))
+#define block_idx_to_logical_idx(blk_idx, i) \
+ (((blk_idx) << 3) + ((i) << 1))
+static int rtw89_dump_logical_efuse_map(struct rtw89_dev *rtwdev, u8 *phy_map,
+ u8 *log_map)
+{
+ u32 physical_size = rtwdev->chip->physical_efuse_size;
+ u32 logical_size = rtwdev->chip->logical_efuse_size;
+ u8 sec_ctrl_size = rtwdev->chip->sec_ctrl_efuse_size;
+ u32 phy_idx = sec_ctrl_size;
+ u32 log_idx;
+ u8 hdr1, hdr2;
+ u8 blk_idx;
+ u8 word_en;
+ int i;
+
+ while (phy_idx < physical_size - sec_ctrl_size) {
+ hdr1 = phy_map[phy_idx];
+ hdr2 = phy_map[phy_idx + 1];
+ if (invalid_efuse_header(hdr1, hdr2))
+ break;
+
+ blk_idx = get_efuse_blk_idx(hdr1, hdr2);
+ word_en = hdr2 & 0xf;
+ phy_idx += 2;
+
+ for (i = 0; i < 4; i++) {
+ if (invalid_efuse_content(word_en, i))
+ continue;
+
+ log_idx = block_idx_to_logical_idx(blk_idx, i);
+ if (phy_idx + 1 > physical_size - sec_ctrl_size - 1 ||
+ log_idx + 1 > logical_size)
+ return -EINVAL;
+
+ log_map[log_idx] = phy_map[phy_idx];
+ log_map[log_idx + 1] = phy_map[phy_idx + 1];
+ phy_idx += 2;
+ }
+ }
+ return 0;
+}
+
+int rtw89_parse_efuse_map(struct rtw89_dev *rtwdev)
+{
+ u32 phy_size = rtwdev->chip->physical_efuse_size;
+ u32 log_size = rtwdev->chip->logical_efuse_size;
+ u8 *phy_map = NULL;
+ u8 *log_map = NULL;
+ int ret;
+
+ if (rtw89_read16(rtwdev, R_AX_SYS_EEPROM_CTRL) & B_AX_AUTOLOAD_SUS)
+ rtwdev->efuse.valid = true;
+ else
+ rtw89_warn(rtwdev, "failed to check efuse autoload\n");
+
+ phy_map = kmalloc(phy_size, GFP_KERNEL);
+ log_map = kmalloc(log_size, GFP_KERNEL);
+
+ if (!phy_map || !log_map) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ ret = rtw89_dump_physical_efuse_map(rtwdev, phy_map, 0, phy_size);
+ if (ret) {
+ rtw89_warn(rtwdev, "failed to dump efuse physical map\n");
+ goto out_free;
+ }
+
+ memset(log_map, 0xff, log_size);
+ ret = rtw89_dump_logical_efuse_map(rtwdev, phy_map, log_map);
+ if (ret) {
+ rtw89_warn(rtwdev, "failed to dump efuse logical map\n");
+ goto out_free;
+ }
+
+ rtw89_hex_dump(rtwdev, RTW89_DBG_FW, "log_map: ", log_map, log_size);
+
+ ret = rtwdev->chip->ops->read_efuse(rtwdev, log_map);
+ if (ret) {
+ rtw89_warn(rtwdev, "failed to read efuse map\n");
+ goto out_free;
+ }
+
+out_free:
+ kfree(log_map);
+ kfree(phy_map);
+
+ return ret;
+}
+
+int rtw89_parse_phycap_map(struct rtw89_dev *rtwdev)
+{
+ u32 phycap_addr = rtwdev->chip->phycap_addr;
+ u32 phycap_size = rtwdev->chip->phycap_size;
+ u8 *phycap_map = NULL;
+ int ret = 0;
+
+ if (!phycap_size)
+ return 0;
+
+ phycap_map = kmalloc(phycap_size, GFP_KERNEL);
+ if (!phycap_map)
+ return -ENOMEM;
+
+ ret = rtw89_dump_physical_efuse_map(rtwdev, phycap_map,
+ phycap_addr, phycap_size);
+ if (ret) {
+ rtw89_warn(rtwdev, "failed to dump phycap map\n");
+ goto out_free;
+ }
+
+ ret = rtwdev->chip->ops->read_phycap(rtwdev, phycap_map);
+ if (ret) {
+ rtw89_warn(rtwdev, "failed to read phycap map\n");
+ goto out_free;
+ }
+
+out_free:
+ kfree(phycap_map);
+
+ return ret;
+}
diff --git a/drivers/net/wireless/realtek/rtw89/efuse.h b/drivers/net/wireless/realtek/rtw89/efuse.h
new file mode 100644
index 000000000000..622ff95e7476
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw89/efuse.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/* Copyright(c) 2019-2020 Realtek Corporation
+ */
+
+#ifndef __RTW89_EFUSE_H__
+#define __RTW89_EFUSE_H__
+
+#include "core.h"
+
+int rtw89_parse_efuse_map(struct rtw89_dev *rtwdev);
+int rtw89_parse_phycap_map(struct rtw89_dev *rtwdev);
+
+#endif
--
2.25.1

2021-06-18 06:50:46

by Pkshih

[permalink] [raw]
Subject: [PATCH 07/24] rtw89: add MAC files

Provide interfaces to access MAC function blocks including power-on/off
configuration.

MAC initialization does power-on, and then enable HCI to download firmware.
The BB, RF, DMAC and CMAC function blocks are enabled, and then configure
TXRX settings that contains DMAC, CMAC, interrupt mask, and RPQ parameters.
DMAC is short for data MAC, including data link engine (DLE), HCI function
control (HFC), MPDU processor, security engine and so on. CMAC is short for
control MAC, including scheduler, address CAM, RX filter, CCA control,
TMAC, RMAC, protocol component and so on.

The full MAC initialization is done for normal use case when user does
network interface up. When device is probing, driver does partial
initialization to do power-on and download firmware, because we need to
read hardware capabilities from efuse and firmware.

MAC supports five ports, so we can have five VIFs at most. To control MAC
port, we control a set of registers, and the most important one among them
is port_cfg register named R_AX_PORT_CFG_P0. We can turn on/off the port
function and configure network type (STA or AP mode) by this register.
The address and BSSID corresponding to this port are given by address
and BSSID CAM that is set by firmware via H2C command.

Since BT coexistence code needs to access coex registers, some help
functions are provided to make WiFi-only case work.

To access MAC registers, normally we use rtw89_writeXX/rtw89_readYY, but
we use rtw89_mac_txpwr_write32_ZZ to access TX power register with proper
address range checking.

Signed-off-by: Ping-Ke Shih <[email protected]>
---
drivers/net/wireless/realtek/rtw89/mac.c | 4070 ++++++++++++++++++++++
drivers/net/wireless/realtek/rtw89/mac.h | 905 +++++
2 files changed, 4975 insertions(+)
create mode 100644 drivers/net/wireless/realtek/rtw89/mac.c
create mode 100644 drivers/net/wireless/realtek/rtw89/mac.h

diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c
new file mode 100644
index 000000000000..e00652e11941
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw89/mac.c
@@ -0,0 +1,4070 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Copyright(c) 2019-2020 Realtek Corporation
+ */
+
+#include "cam.h"
+#include "debug.h"
+#include "fw.h"
+#include "mac.h"
+#include "ps.h"
+#include "reg.h"
+#include "util.h"
+
+int rtw89_mac_check_mac_en(struct rtw89_dev *rtwdev, u8 mac_idx,
+ enum rtw89_mac_hwmod_sel sel)
+{
+ u32 val, r_val;
+
+ if (sel == RTW89_DMAC_SEL) {
+ r_val = rtw89_read32(rtwdev, R_AX_DMAC_FUNC_EN);
+ val = (B_AX_MAC_FUNC_EN | B_AX_DMAC_FUNC_EN);
+ } else if (sel == RTW89_CMAC_SEL && mac_idx == 0) {
+ r_val = rtw89_read32(rtwdev, R_AX_CMAC_FUNC_EN);
+ val = B_AX_CMAC_EN;
+ } else if (sel == RTW89_CMAC_SEL && mac_idx == 1) {
+ r_val = rtw89_read32(rtwdev, R_AX_SYS_ISO_CTRL_EXTEND);
+ val = B_AX_CMAC1_FEN;
+ } else {
+ return -EINVAL;
+ }
+ if (r_val == RTW89_R32_EA || r_val == RTW89_R32_DEAD ||
+ (val & r_val) != val)
+ return -EFAULT;
+
+ return 0;
+}
+
+int rtw89_mac_write_lte(struct rtw89_dev *rtwdev, const u32 offset, u32 val)
+{
+ u8 lte_ctrl;
+ int ret;
+
+ ret = read_poll_timeout(rtw89_read8, lte_ctrl, (lte_ctrl & BIT(5)) != 0,
+ 50, 50000, false, rtwdev, R_AX_LTE_CTRL + 3);
+ if (ret)
+ rtw89_err(rtwdev, "[ERR]lte not ready(W)\n");
+
+ rtw89_write32(rtwdev, R_AX_LTE_WDATA, val);
+ rtw89_write32(rtwdev, R_AX_LTE_CTRL, 0xC00F0000 | offset);
+
+ return ret;
+}
+
+int rtw89_mac_read_lte(struct rtw89_dev *rtwdev, const u32 offset, u32 *val)
+{
+ u8 lte_ctrl;
+ int ret;
+
+ ret = read_poll_timeout(rtw89_read8, lte_ctrl, (lte_ctrl & BIT(5)) != 0,
+ 50, 50000, false, rtwdev, R_AX_LTE_CTRL + 3);
+ if (ret)
+ rtw89_err(rtwdev, "[ERR]lte not ready(W)\n");
+
+ rtw89_write32(rtwdev, R_AX_LTE_CTRL, 0x800F0000 | offset);
+ *val = rtw89_read32(rtwdev, R_AX_LTE_RDATA);
+
+ return ret;
+}
+
+static
+int dle_dfi_ctrl(struct rtw89_dev *rtwdev, struct rtw89_mac_dle_dfi_ctrl *ctrl)
+{
+ u32 ctrl_reg, data_reg, ctrl_data;
+ u32 val;
+ int ret;
+
+ switch (ctrl->type) {
+ case DLE_CTRL_TYPE_WDE:
+ ctrl_reg = R_AX_WDE_DBG_FUN_INTF_CTL;
+ data_reg = R_AX_WDE_DBG_FUN_INTF_DATA;
+ ctrl_data = FIELD_PREP(B_AX_WDE_DFI_TRGSEL, ctrl->target) |
+ FIELD_PREP(B_AX_WDE_DFI_ADDR, ctrl->addr) |
+ B_AX_WDE_DFI_ACTIVE;
+ break;
+ case DLE_CTRL_TYPE_PLE:
+ ctrl_reg = R_AX_PLE_DBG_FUN_INTF_CTL;
+ data_reg = R_AX_PLE_DBG_FUN_INTF_DATA;
+ ctrl_data = FIELD_PREP(B_AX_PLE_DFI_TRGSEL, ctrl->target) |
+ FIELD_PREP(B_AX_PLE_DFI_ADDR, ctrl->addr) |
+ B_AX_PLE_DFI_ACTIVE;
+ break;
+ default:
+ rtw89_warn(rtwdev, "[ERR] dfi ctrl type %d\n", ctrl->type);
+ return -EINVAL;
+ }
+
+ rtw89_write32(rtwdev, ctrl_reg, ctrl_data);
+
+ ret = read_poll_timeout_atomic(rtw89_read32, val, !(val & B_AX_WDE_DFI_ACTIVE),
+ 1, 1000, false, rtwdev, ctrl_reg);
+ if (ret) {
+ rtw89_warn(rtwdev, "[ERR] dle dfi ctrl 0x%X set 0x%X timeout\n",
+ ctrl_reg, ctrl_data);
+ return ret;
+ }
+
+ ctrl->out_data = rtw89_read32(rtwdev, data_reg);
+ return 0;
+}
+
+static int dle_dfi_quota(struct rtw89_dev *rtwdev,
+ struct rtw89_mac_dle_dfi_quota *quota)
+{
+ struct rtw89_mac_dle_dfi_ctrl ctrl;
+ int ret;
+
+ ctrl.type = quota->dle_type;
+ ctrl.target = DLE_DFI_TYPE_QUOTA;
+ ctrl.addr = quota->qtaid;
+ ret = dle_dfi_ctrl(rtwdev, &ctrl);
+ if (ret) {
+ rtw89_warn(rtwdev, "[ERR]dle_dfi_ctrl %d\n", ret);
+ return ret;
+ }
+
+ quota->rsv_pgnum = FIELD_GET(B_AX_DLE_RSV_PGNUM, ctrl.out_data);
+ quota->use_pgnum = FIELD_GET(B_AX_DLE_USE_PGNUM, ctrl.out_data);
+ return 0;
+}
+
+static int dle_dfi_qempty(struct rtw89_dev *rtwdev,
+ struct rtw89_mac_dle_dfi_qempty *qempty)
+{
+ struct rtw89_mac_dle_dfi_ctrl ctrl;
+ u32 ret;
+
+ ctrl.type = qempty->dle_type;
+ ctrl.target = DLE_DFI_TYPE_QEMPTY;
+ ctrl.addr = qempty->grpsel;
+ ret = dle_dfi_ctrl(rtwdev, &ctrl);
+ if (ret) {
+ rtw89_warn(rtwdev, "[ERR]dle_dfi_ctrl %d\n", ret);
+ return ret;
+ }
+
+ qempty->qempty = FIELD_GET(B_AX_DLE_QEMPTY_GRP, ctrl.out_data);
+ return 0;
+}
+
+static void dump_err_status_dispatcher(struct rtw89_dev *rtwdev)
+{
+ rtw89_info(rtwdev, "R_AX_HOST_DISPATCHER_ALWAYS_IMR=0x%08x ",
+ rtw89_read32(rtwdev, R_AX_HOST_DISPATCHER_ERR_IMR));
+ rtw89_info(rtwdev, "R_AX_HOST_DISPATCHER_ALWAYS_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_HOST_DISPATCHER_ERR_ISR));
+ rtw89_info(rtwdev, "R_AX_CPU_DISPATCHER_ALWAYS_IMR=0x%08x ",
+ rtw89_read32(rtwdev, R_AX_CPU_DISPATCHER_ERR_IMR));
+ rtw89_info(rtwdev, "R_AX_CPU_DISPATCHER_ALWAYS_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_CPU_DISPATCHER_ERR_ISR));
+ rtw89_info(rtwdev, "R_AX_OTHER_DISPATCHER_ALWAYS_IMR=0x%08x ",
+ rtw89_read32(rtwdev, R_AX_OTHER_DISPATCHER_ERR_IMR));
+ rtw89_info(rtwdev, "R_AX_OTHER_DISPATCHER_ALWAYS_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_OTHER_DISPATCHER_ERR_ISR));
+}
+
+static void rtw89_mac_dump_qta_lost(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_mac_dle_dfi_qempty qempty;
+ struct rtw89_mac_dle_dfi_quota quota;
+ struct rtw89_mac_dle_dfi_ctrl ctrl;
+ u32 val, not_empty, i;
+ int ret;
+
+ qempty.dle_type = DLE_CTRL_TYPE_PLE;
+ qempty.grpsel = 0;
+ ret = dle_dfi_qempty(rtwdev, &qempty);
+ if (ret)
+ rtw89_warn(rtwdev, "%s: query DLE fail\n", __func__);
+ else
+ rtw89_info(rtwdev, "DLE group0 empty: 0x%x\n", qempty.qempty);
+
+ for (not_empty = ~qempty.qempty, i = 0; not_empty != 0; not_empty >>= 1, i++) {
+ if (!(not_empty & BIT(0)))
+ continue;
+ ctrl.type = DLE_CTRL_TYPE_PLE;
+ ctrl.target = DLE_DFI_TYPE_QLNKTBL;
+ ctrl.addr = (QLNKTBL_ADDR_INFO_SEL_0 ? QLNKTBL_ADDR_INFO_SEL : 0) |
+ FIELD_PREP(QLNKTBL_ADDR_TBL_IDX_MASK, i);
+ ret = dle_dfi_ctrl(rtwdev, &ctrl);
+ if (ret)
+ rtw89_warn(rtwdev, "%s: query DLE fail\n", __func__);
+ else
+ rtw89_info(rtwdev, "qidx%d pktcnt = %ld\n", i,
+ FIELD_GET(QLNKTBL_DATA_SEL1_PKT_CNT_MASK,
+ ctrl.out_data));
+ }
+
+ quota.dle_type = DLE_CTRL_TYPE_PLE;
+ quota.qtaid = 6;
+ ret = dle_dfi_quota(rtwdev, &quota);
+ if (ret)
+ rtw89_warn(rtwdev, "%s: query DLE fail\n", __func__);
+ else
+ rtw89_info(rtwdev, "quota6 rsv/use: 0x%x/0x%x\n",
+ quota.rsv_pgnum, quota.use_pgnum);
+
+ val = rtw89_read32(rtwdev, R_AX_PLE_QTA6_CFG);
+ rtw89_info(rtwdev, "[PLE][CMAC0_RX]min_pgnum=0x%lx\n",
+ FIELD_GET(B_AX_PLE_Q6_MIN_SIZE_MASK, val));
+ rtw89_info(rtwdev, "[PLE][CMAC0_RX]max_pgnum=0x%lx\n",
+ FIELD_GET(B_AX_PLE_Q6_MAX_SIZE_MASK, val));
+
+ dump_err_status_dispatcher(rtwdev);
+}
+
+static void rtw89_mac_dump_l0_to_l1(struct rtw89_dev *rtwdev,
+ enum mac_ax_err_info err)
+{
+ u32 dbg, event;
+
+ dbg = rtw89_read32(rtwdev, R_AX_SER_DBG_INFO);
+ event = FIELD_GET(B_AX_L0_TO_L1_EVENT_MASK, dbg);
+
+ switch (event) {
+ case MAC_AX_L0_TO_L1_RX_QTA_LOST:
+ rtw89_info(rtwdev, "quota lost!\n");
+ rtw89_mac_dump_qta_lost(rtwdev);
+ break;
+ default:
+ break;
+ }
+}
+
+static void rtw89_mac_dump_err_status(struct rtw89_dev *rtwdev,
+ enum mac_ax_err_info err)
+{
+ u32 dmac_err, cmac_err;
+
+ if (err != MAC_AX_ERR_L1_ERR_DMAC &&
+ err != MAC_AX_ERR_L0_PROMOTE_TO_L1)
+ return;
+
+ rtw89_info(rtwdev, "--->\nerr=0x%x\n", err);
+ rtw89_info(rtwdev, "R_AX_SER_DBG_INFO =0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SER_DBG_INFO));
+
+ cmac_err = rtw89_read32(rtwdev, R_AX_CMAC_ERR_ISR);
+ rtw89_info(rtwdev, "R_AX_CMAC_ERR_ISR =0x%08x\n", cmac_err);
+ dmac_err = rtw89_read32(rtwdev, R_AX_DMAC_ERR_ISR);
+ rtw89_info(rtwdev, "R_AX_DMAC_ERR_ISR =0x%08x\n", dmac_err);
+
+ if (dmac_err) {
+ rtw89_info(rtwdev, "R_AX_WDE_ERR_FLAG_CFG =0x%08x ",
+ rtw89_read32(rtwdev, R_AX_WDE_ERR_FLAG_CFG));
+ rtw89_info(rtwdev, "R_AX_PLE_ERR_FLAG_CFG =0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_CFG));
+ }
+
+ if (dmac_err & B_AX_WDRLS_ERR_FLAG) {
+ rtw89_info(rtwdev, "R_AX_WDRLS_ERR_IMR =0x%08x ",
+ rtw89_read32(rtwdev, R_AX_WDRLS_ERR_IMR));
+ rtw89_info(rtwdev, "R_AX_WDRLS_ERR_ISR =0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_WDRLS_ERR_ISR));
+ }
+
+ if (dmac_err & B_AX_WSEC_ERR_FLAG) {
+ rtw89_info(rtwdev, "R_AX_SEC_ERR_IMR_ISR =0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_DEBUG));
+ rtw89_info(rtwdev, "SEC_local_Register 0x9D00 =0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_ENG_CTRL));
+ rtw89_info(rtwdev, "SEC_local_Register 0x9D04 =0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_MPDU_PROC));
+ rtw89_info(rtwdev, "SEC_local_Register 0x9D10 =0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_CAM_ACCESS));
+ rtw89_info(rtwdev, "SEC_local_Register 0x9D14 =0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_CAM_RDATA));
+ rtw89_info(rtwdev, "SEC_local_Register 0x9D18 =0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_CAM_WDATA));
+ rtw89_info(rtwdev, "SEC_local_Register 0x9D20 =0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_TX_DEBUG));
+ rtw89_info(rtwdev, "SEC_local_Register 0x9D24 =0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_RX_DEBUG));
+ rtw89_info(rtwdev, "SEC_local_Register 0x9D28 =0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_TRX_PKT_CNT));
+ rtw89_info(rtwdev, "SEC_local_Register 0x9D2C =0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_SEC_TRX_BLK_CNT));
+ }
+
+ if (dmac_err & B_AX_MPDU_ERR_FLAG) {
+ rtw89_info(rtwdev, "R_AX_MPDU_TX_ERR_IMR =0x%08x ",
+ rtw89_read32(rtwdev, R_AX_MPDU_TX_ERR_IMR));
+ rtw89_info(rtwdev, "R_AX_MPDU_TX_ERR_ISR =0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_MPDU_TX_ERR_ISR));
+ rtw89_info(rtwdev, "R_AX_MPDU_RX_ERR_IMR =0x%08x ",
+ rtw89_read32(rtwdev, R_AX_MPDU_RX_ERR_IMR));
+ rtw89_info(rtwdev, "R_AX_MPDU_RX_ERR_ISR =0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_MPDU_RX_ERR_ISR));
+ }
+
+ if (dmac_err & B_AX_STA_SCHEDULER_ERR_FLAG) {
+ rtw89_info(rtwdev, "R_AX_STA_SCHEDULER_ERR_IMR =0x%08x ",
+ rtw89_read32(rtwdev, R_AX_STA_SCHEDULER_ERR_IMR));
+ rtw89_info(rtwdev, "R_AX_STA_SCHEDULER_ERR_ISR= 0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_STA_SCHEDULER_ERR_ISR));
+ }
+
+ if (dmac_err & B_AX_WDE_DLE_ERR_FLAG) {
+ rtw89_info(rtwdev, "R_AX_WDE_ERR_IMR=0x%08x ",
+ rtw89_read32(rtwdev, R_AX_WDE_ERR_IMR));
+ rtw89_info(rtwdev, "R_AX_WDE_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_WDE_ERR_ISR));
+ rtw89_info(rtwdev, "R_AX_PLE_ERR_IMR=0x%08x ",
+ rtw89_read32(rtwdev, R_AX_PLE_ERR_IMR));
+ rtw89_info(rtwdev, "R_AX_PLE_ERR_FLAG_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_ISR));
+ dump_err_status_dispatcher(rtwdev);
+ }
+
+ if (dmac_err & B_AX_TXPKTCTRL_ERR_FLAG) {
+ rtw89_info(rtwdev, "R_AX_TXPKTCTL_ERR_IMR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR));
+ rtw89_info(rtwdev, "R_AX_TXPKTCTL_ERR_IMR_ISR_B1=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR_B1));
+ }
+
+ if (dmac_err & B_AX_PLE_DLE_ERR_FLAG) {
+ rtw89_info(rtwdev, "R_AX_WDE_ERR_IMR=0x%08x ",
+ rtw89_read32(rtwdev, R_AX_WDE_ERR_IMR));
+ rtw89_info(rtwdev, "R_AX_WDE_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_WDE_ERR_ISR));
+ rtw89_info(rtwdev, "R_AX_PLE_ERR_IMR=0x%08x ",
+ rtw89_read32(rtwdev, R_AX_PLE_ERR_IMR));
+ rtw89_info(rtwdev, "R_AX_PLE_ERR_FLAG_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_ISR));
+ rtw89_info(rtwdev, "R_AX_WD_CPUQ_OP_0=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_0));
+ rtw89_info(rtwdev, "R_AX_WD_CPUQ_OP_1=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_1));
+ rtw89_info(rtwdev, "R_AX_WD_CPUQ_OP_2=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_2));
+ rtw89_info(rtwdev, "R_AX_WD_CPUQ_OP_STATUS=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_STATUS));
+ rtw89_info(rtwdev, "R_AX_PL_CPUQ_OP_0=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_0));
+ rtw89_info(rtwdev, "R_AX_PL_CPUQ_OP_1=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_1));
+ rtw89_info(rtwdev, "R_AX_PL_CPUQ_OP_2=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_2));
+ rtw89_info(rtwdev, "R_AX_PL_CPUQ_OP_STATUS=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_STATUS));
+ rtw89_info(rtwdev, "R_AX_RXDMA_PKT_INFO_0=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_0));
+ rtw89_info(rtwdev, "R_AX_RXDMA_PKT_INFO_1=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_1));
+ rtw89_info(rtwdev, "R_AX_RXDMA_PKT_INFO_2=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_2));
+ dump_err_status_dispatcher(rtwdev);
+ }
+
+ if (dmac_err & B_AX_PKTIN_ERR_FLAG) {
+ rtw89_info(rtwdev, "R_AX_PKTIN_ERR_IMR =0x%08x ",
+ rtw89_read32(rtwdev, R_AX_PKTIN_ERR_IMR));
+ rtw89_info(rtwdev, "R_AX_PKTIN_ERR_ISR =0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PKTIN_ERR_ISR));
+ rtw89_info(rtwdev, "R_AX_PKTIN_ERR_IMR =0x%08x ",
+ rtw89_read32(rtwdev, R_AX_PKTIN_ERR_IMR));
+ rtw89_info(rtwdev, "R_AX_PKTIN_ERR_ISR =0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PKTIN_ERR_ISR));
+ }
+
+ if (dmac_err & B_AX_DISPATCH_ERR_FLAG)
+ dump_err_status_dispatcher(rtwdev);
+
+ if (dmac_err & B_AX_DLE_CPUIO_ERR_FLAG) {
+ rtw89_info(rtwdev, "R_AX_CPUIO_ERR_IMR=0x%08x ",
+ rtw89_read32(rtwdev, R_AX_CPUIO_ERR_IMR));
+ rtw89_info(rtwdev, "R_AX_CPUIO_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_CPUIO_ERR_ISR));
+ }
+
+ if (dmac_err & BIT(11)) {
+ rtw89_info(rtwdev, "R_AX_BBRPT_COM_ERR_IMR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_IMR_ISR));
+ }
+
+ if (cmac_err & B_AX_SCHEDULE_TOP_ERR_IND) {
+ rtw89_info(rtwdev, "R_AX_SCHEDULE_ERR_IMR=0x%08x ",
+ rtw89_read32(rtwdev, R_AX_SCHEDULE_ERR_IMR));
+ rtw89_info(rtwdev, "R_AX_SCHEDULE_ERR_ISR=0x%04x\n",
+ rtw89_read16(rtwdev, R_AX_SCHEDULE_ERR_ISR));
+ }
+
+ if (cmac_err & B_AX_PTCL_TOP_ERR_IND) {
+ rtw89_info(rtwdev, "R_AX_PTCL_IMR0=0x%08x ",
+ rtw89_read32(rtwdev, R_AX_PTCL_IMR0));
+ rtw89_info(rtwdev, "R_AX_PTCL_ISR0=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PTCL_ISR0));
+ }
+
+ if (cmac_err & B_AX_DMA_TOP_ERR_IND) {
+ rtw89_info(rtwdev, "R_AX_DLE_CTRL=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_DLE_CTRL));
+ }
+
+ if (cmac_err & B_AX_PHYINTF_ERR_IND) {
+ rtw89_info(rtwdev, "R_AX_PHYINFO_ERR_IMR=0x%04x\n",
+ rtw89_read16(rtwdev, R_AX_PHYINFO_ERR_IMR));
+ }
+
+ if (cmac_err & B_AX_TXPWR_CTRL_ERR_IND) {
+ rtw89_info(rtwdev, "R_AX_TXPWR_IMR=0x%08x ",
+ rtw89_read32(rtwdev, R_AX_TXPWR_IMR));
+ rtw89_info(rtwdev, "R_AX_TXPWR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_TXPWR_ISR));
+ }
+
+ if (cmac_err & B_AX_WMAC_RX_ERR_IND) {
+ rtw89_info(rtwdev, "R_AX_DBGSEL_TRXPTCL=0x%08x ",
+ rtw89_read32(rtwdev, R_AX_DBGSEL_TRXPTCL));
+ rtw89_info(rtwdev, "R_AX_PHYINFO_ERR_ISR=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_ISR));
+ }
+
+ if (cmac_err & B_AX_WMAC_TX_ERR_IND) {
+ rtw89_info(rtwdev, "R_AX_TMAC_ERR_IMR_ISR=0x%08x ",
+ rtw89_read32(rtwdev, R_AX_TMAC_ERR_IMR_ISR));
+ rtw89_info(rtwdev, "R_AX_DBGSEL_TRXPTCL=0x%08x\n",
+ rtw89_read32(rtwdev, R_AX_DBGSEL_TRXPTCL));
+ }
+
+ rtwdev->hci.ops->dump_err_status(rtwdev);
+
+ if (err == MAC_AX_ERR_L0_PROMOTE_TO_L1)
+ rtw89_mac_dump_l0_to_l1(rtwdev, err);
+
+ rtw89_info(rtwdev, "<---\n");
+}
+
+u32 rtw89_mac_get_err_status(struct rtw89_dev *rtwdev)
+{
+ u32 err;
+ int ret;
+
+ ret = read_poll_timeout(rtw89_read32, err, (err != 0), 1000, 100000,
+ false, rtwdev, R_AX_HALT_C2H_CTRL);
+ if (ret) {
+ rtw89_warn(rtwdev, "Polling FW err status fail\n");
+ return ret;
+ }
+
+ err = rtw89_read32(rtwdev, R_AX_HALT_C2H);
+ rtw89_write32(rtwdev, R_AX_HALT_C2H_CTRL, 0);
+
+ rtw89_fw_st_dbg_dump(rtwdev);
+ rtw89_mac_dump_err_status(rtwdev, err);
+
+ return err;
+}
+EXPORT_SYMBOL(rtw89_mac_get_err_status);
+
+int rtw89_mac_set_err_status(struct rtw89_dev *rtwdev, u32 err)
+{
+ u32 halt;
+ int ret = 0;
+
+ if (err > MAC_AX_SET_ERR_MAX) {
+ rtw89_err(rtwdev, "Bad set-err-status value 0x%08x\n", err);
+ return -EINVAL;
+ }
+
+ ret = read_poll_timeout(rtw89_read32, halt, (halt == 0x0), 1000,
+ 100000, false, rtwdev, R_AX_HALT_H2C_CTRL);
+ if (ret) {
+ rtw89_err(rtwdev, "FW doesn't receive previous msg\n");
+ return -EFAULT;
+ }
+
+ rtw89_write32(rtwdev, R_AX_HALT_H2C, err);
+ rtw89_write32(rtwdev, R_AX_HALT_H2C_CTRL, B_AX_HALT_H2C_TRIGGER);
+
+ return 0;
+}
+EXPORT_SYMBOL(rtw89_mac_set_err_status);
+
+struct rtw89_hfc_prec_cfg rtw_hfc_preccfg_pcie = {
+ 2, 40, 0, 0, 1, 0, 0, 0
+};
+
+struct rtw89_hfc_prec_cfg rtw_hfc_preccfg_pcie_wd128 = {
+ 2, 40, 0, 0, 1, 0, 0, 0
+};
+
+struct rtw89_hfc_prec_cfg rtw_hfc_preccfg_pcie_stf = {
+ 1, 40, 64, 64, 1, 0, 1, 1
+};
+
+static int hfc_reset_param(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param;
+ struct rtw89_hfc_param_ini param_ini = {NULL};
+ u8 qta_mode = rtwdev->mac.dle_info.qta_mode;
+
+ switch (rtwdev->hci.type) {
+ case RTW89_HCI_TYPE_PCIE:
+ param_ini = rtwdev->chip->hfc_param_ini[qta_mode];
+ param->en = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (param_ini.pub_cfg)
+ param->pub_cfg = param_ini.pub_cfg;
+
+ if (param_ini.prec_cfg) {
+ param->prec_cfg = param_ini.prec_cfg;
+ rtwdev->hal.sw_amsdu_max_size =
+ param->prec_cfg->wp_ch07_prec * HFC_PAGE_UNIT;
+ }
+
+ if (param_ini.ch_cfg)
+ param->ch_cfg = param_ini.ch_cfg;
+
+ memset(&param->ch_info, 0, sizeof(param->ch_info));
+ memset(&param->pub_info, 0, sizeof(param->pub_info));
+ param->mode = param_ini.mode;
+
+ return 0;
+}
+
+static int hfc_ch_cfg_chk(struct rtw89_dev *rtwdev, u8 ch)
+{
+ struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param;
+ struct rtw89_hfc_ch_cfg *ch_cfg = param->ch_cfg;
+ struct rtw89_hfc_pub_cfg *pub_cfg = param->pub_cfg;
+ struct rtw89_hfc_prec_cfg *prec_cfg = param->prec_cfg;
+
+ if (ch >= RTW89_DMA_CH_NUM)
+ return -EINVAL;
+
+ if ((ch_cfg[ch].min && ch_cfg[ch].min < prec_cfg->ch011_prec) ||
+ ch_cfg[ch].max > pub_cfg->pub_max)
+ return -EINVAL;
+ if (ch_cfg[ch].grp >= grp_num)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int hfc_pub_info_chk(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param;
+ struct rtw89_hfc_pub_cfg *cfg = param->pub_cfg;
+ struct rtw89_hfc_pub_info *info = &param->pub_info;
+
+ if (info->g0_used + info->g1_used + info->pub_aval != cfg->pub_max) {
+ if (rtwdev->chip->chip_id == RTL8852A)
+ return 0;
+ else
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int hfc_pub_cfg_chk(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param;
+ struct rtw89_hfc_pub_cfg *pub_cfg = param->pub_cfg;
+
+ if (pub_cfg->grp0 + pub_cfg->grp1 != pub_cfg->pub_max)
+ return 0;
+
+ return 0;
+}
+
+static int hfc_ch_ctrl(struct rtw89_dev *rtwdev, u8 ch)
+{
+ struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param;
+ struct rtw89_hfc_ch_cfg *cfg = param->ch_cfg;
+ int ret = 0;
+ u32 val = 0;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, RTW89_MAC_0, RTW89_DMAC_SEL);
+ if (ret)
+ return ret;
+
+ ret = hfc_ch_cfg_chk(rtwdev, ch);
+ if (ret)
+ return ret;
+
+ if (ch > RTW89_DMA_B1HI)
+ return -EINVAL;
+
+ val = u32_encode_bits(cfg[ch].min, B_AX_MIN_PG_MASK) |
+ u32_encode_bits(cfg[ch].max, B_AX_MAX_PG_MASK) |
+ (cfg[ch].grp ? B_AX_GRP : 0);
+ rtw89_write32(rtwdev, R_AX_ACH0_PAGE_CTRL + ch * 4, val);
+
+ return 0;
+}
+
+static int hfc_upd_ch_info(struct rtw89_dev *rtwdev, u8 ch)
+{
+ struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param;
+ struct rtw89_hfc_ch_info *info = param->ch_info;
+ struct rtw89_hfc_ch_cfg *cfg = param->ch_cfg;
+ u32 val;
+ u32 ret;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, RTW89_MAC_0, RTW89_DMAC_SEL);
+ if (ret)
+ return ret;
+
+ if (ch > RTW89_DMA_H2C)
+ return -EINVAL;
+
+ val = rtw89_read32(rtwdev, R_AX_ACH0_PAGE_INFO + ch * 4);
+ info[ch].aval = u32_get_bits(val, B_AX_AVAL_PG_MASK);
+ if (ch < RTW89_DMA_H2C)
+ info[ch].used = u32_get_bits(val, B_AX_USE_PG_MASK);
+ else
+ info[ch].used = cfg[ch].min - info[ch].aval;
+
+ return 0;
+}
+
+static int hfc_pub_ctrl(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_hfc_pub_cfg *cfg = rtwdev->mac.hfc_param.pub_cfg;
+ u32 val;
+ int ret;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, RTW89_MAC_0, RTW89_DMAC_SEL);
+ if (ret)
+ return ret;
+
+ ret = hfc_pub_cfg_chk(rtwdev);
+ if (ret)
+ return ret;
+
+ val = u32_encode_bits(cfg->grp0, B_AX_PUBPG_G0_MASK) |
+ u32_encode_bits(cfg->grp1, B_AX_PUBPG_G1_MASK);
+ rtw89_write32(rtwdev, R_AX_PUB_PAGE_CTRL1, val);
+
+ val = u32_encode_bits(cfg->wp_thrd, B_AX_WP_THRD_MASK);
+ rtw89_write32(rtwdev, R_AX_WP_PAGE_CTRL2, val);
+
+ return 0;
+}
+
+static int hfc_upd_mix_info(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param;
+ struct rtw89_hfc_pub_cfg *pub_cfg = param->pub_cfg;
+ struct rtw89_hfc_prec_cfg *prec_cfg = param->prec_cfg;
+ struct rtw89_hfc_pub_info *info = &param->pub_info;
+ u32 val;
+ int ret;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, RTW89_MAC_0, RTW89_DMAC_SEL);
+ if (ret)
+ return ret;
+
+ val = rtw89_read32(rtwdev, R_AX_PUB_PAGE_INFO1);
+ info->g0_used = u32_get_bits(val, B_AX_G0_USE_PG_MASK);
+ info->g1_used = u32_get_bits(val, B_AX_G1_USE_PG_MASK);
+ val = rtw89_read32(rtwdev, R_AX_PUB_PAGE_INFO3);
+ info->g0_aval = u32_get_bits(val, B_AX_G0_AVAL_PG_MASK);
+ info->g1_aval = u32_get_bits(val, B_AX_G1_AVAL_PG_MASK);
+ info->pub_aval =
+ u32_get_bits(rtw89_read32(rtwdev, R_AX_PUB_PAGE_INFO2),
+ B_AX_PUB_AVAL_PG_MASK);
+ info->wp_aval =
+ u32_get_bits(rtw89_read32(rtwdev, R_AX_WP_PAGE_INFO1),
+ B_AX_WP_AVAL_PG_MASK);
+
+ val = rtw89_read32(rtwdev, R_AX_HCI_FC_CTRL);
+ param->en = val & B_AX_HCI_FC_EN ? 1 : 0;
+ param->h2c_en = val & B_AX_HCI_FC_CH12_EN ? 1 : 0;
+ param->mode = u32_get_bits(val, B_AX_HCI_FC_MODE_MASK);
+ prec_cfg->ch011_full_cond =
+ u32_get_bits(val, B_AX_HCI_FC_WD_FULL_COND_MASK);
+ prec_cfg->h2c_full_cond =
+ u32_get_bits(val, B_AX_HCI_FC_CH12_FULL_COND_MASK);
+ prec_cfg->wp_ch07_full_cond =
+ u32_get_bits(val, B_AX_HCI_FC_WP_CH07_FULL_COND_MASK);
+ prec_cfg->wp_ch811_full_cond =
+ u32_get_bits(val, B_AX_HCI_FC_WP_CH811_FULL_COND_MASK);
+
+ val = rtw89_read32(rtwdev, R_AX_CH_PAGE_CTRL);
+ prec_cfg->ch011_prec = u32_get_bits(val, B_AX_PREC_PAGE_CH011_MASK);
+ prec_cfg->h2c_prec = u32_get_bits(val, B_AX_PREC_PAGE_CH12_MASK);
+
+ val = rtw89_read32(rtwdev, R_AX_PUB_PAGE_CTRL2);
+ pub_cfg->pub_max = u32_get_bits(val, B_AX_PUBPG_ALL_MASK);
+
+ val = rtw89_read32(rtwdev, R_AX_WP_PAGE_CTRL1);
+ prec_cfg->wp_ch07_prec = u32_get_bits(val, B_AX_PREC_PAGE_WP_CH07_MASK);
+ prec_cfg->wp_ch811_prec = u32_get_bits(val, B_AX_PREC_PAGE_WP_CH811_MASK);
+
+ val = rtw89_read32(rtwdev, R_AX_WP_PAGE_CTRL2);
+ pub_cfg->wp_thrd = u32_get_bits(val, B_AX_WP_THRD_MASK);
+
+ val = rtw89_read32(rtwdev, R_AX_PUB_PAGE_CTRL1);
+ pub_cfg->grp0 = u32_get_bits(val, B_AX_PUBPG_G0_MASK);
+ pub_cfg->grp1 = u32_get_bits(val, B_AX_PUBPG_G1_MASK);
+
+ ret = hfc_pub_info_chk(rtwdev);
+ if (param->en && ret)
+ return ret;
+
+ return 0;
+}
+
+static void hfc_h2c_cfg(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param;
+ struct rtw89_hfc_prec_cfg *prec_cfg = param->prec_cfg;
+ u32 val;
+
+ val = u32_encode_bits(prec_cfg->h2c_prec, B_AX_PREC_PAGE_CH12_MASK);
+ rtw89_write32(rtwdev, R_AX_CH_PAGE_CTRL, val);
+
+ rtw89_write32_mask(rtwdev, R_AX_HCI_FC_CTRL,
+ B_AX_HCI_FC_CH12_FULL_COND_MASK,
+ prec_cfg->h2c_full_cond);
+}
+
+static void hfc_mix_cfg(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param;
+ struct rtw89_hfc_pub_cfg *pub_cfg = param->pub_cfg;
+ struct rtw89_hfc_prec_cfg *prec_cfg = param->prec_cfg;
+ u32 val;
+
+ val = u32_encode_bits(prec_cfg->ch011_prec, B_AX_PREC_PAGE_CH011_MASK) |
+ u32_encode_bits(prec_cfg->h2c_prec, B_AX_PREC_PAGE_CH12_MASK);
+ rtw89_write32(rtwdev, R_AX_CH_PAGE_CTRL, val);
+
+ val = u32_encode_bits(pub_cfg->pub_max, B_AX_PUBPG_ALL_MASK);
+ rtw89_write32(rtwdev, R_AX_PUB_PAGE_CTRL2, val);
+
+ val = u32_encode_bits(prec_cfg->wp_ch07_prec,
+ B_AX_PREC_PAGE_WP_CH07_MASK) |
+ u32_encode_bits(prec_cfg->wp_ch811_prec,
+ B_AX_PREC_PAGE_WP_CH811_MASK);
+ rtw89_write32(rtwdev, R_AX_WP_PAGE_CTRL1, val);
+
+ val = u32_replace_bits(rtw89_read32(rtwdev, R_AX_HCI_FC_CTRL),
+ param->mode, B_AX_HCI_FC_MODE_MASK);
+ val = u32_replace_bits(val, prec_cfg->ch011_full_cond,
+ B_AX_HCI_FC_WD_FULL_COND_MASK);
+ val = u32_replace_bits(val, prec_cfg->h2c_full_cond,
+ B_AX_HCI_FC_CH12_FULL_COND_MASK);
+ val = u32_replace_bits(val, prec_cfg->wp_ch07_full_cond,
+ B_AX_HCI_FC_WP_CH07_FULL_COND_MASK);
+ val = u32_replace_bits(val, prec_cfg->wp_ch811_full_cond,
+ B_AX_HCI_FC_WP_CH811_FULL_COND_MASK);
+ rtw89_write32(rtwdev, R_AX_HCI_FC_CTRL, val);
+}
+
+static void hfc_func_en(struct rtw89_dev *rtwdev, bool en, bool h2c_en)
+{
+ struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param;
+ u32 val;
+
+ val = rtw89_read32(rtwdev, R_AX_HCI_FC_CTRL);
+ param->en = en;
+ param->h2c_en = h2c_en;
+ val = en ? (val | B_AX_HCI_FC_EN) : (val & ~B_AX_HCI_FC_EN);
+ val = h2c_en ? (val | B_AX_HCI_FC_CH12_EN) :
+ (val & ~B_AX_HCI_FC_CH12_EN);
+ rtw89_write32(rtwdev, R_AX_HCI_FC_CTRL, val);
+}
+
+static int hfc_init(struct rtw89_dev *rtwdev, bool reset, bool en, bool h2c_en)
+{
+ u8 ch;
+ u32 ret = 0;
+
+ if (reset)
+ ret = hfc_reset_param(rtwdev);
+ if (ret)
+ return ret;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, RTW89_MAC_0, RTW89_DMAC_SEL);
+ if (ret)
+ return ret;
+
+ hfc_func_en(rtwdev, false, false);
+
+ if (!en && h2c_en) {
+ hfc_h2c_cfg(rtwdev);
+ hfc_func_en(rtwdev, en, h2c_en);
+ return ret;
+ }
+
+ for (ch = RTW89_DMA_ACH0; ch < RTW89_DMA_H2C; ch++) {
+ ret = hfc_ch_ctrl(rtwdev, ch);
+ if (ret)
+ return ret;
+ }
+
+ ret = hfc_pub_ctrl(rtwdev);
+ if (ret)
+ return ret;
+
+ hfc_mix_cfg(rtwdev);
+ if (en || h2c_en) {
+ hfc_func_en(rtwdev, en, h2c_en);
+ udelay(10);
+ }
+ for (ch = RTW89_DMA_ACH0; ch < RTW89_DMA_H2C; ch++) {
+ ret = hfc_upd_ch_info(rtwdev, ch);
+ if (ret)
+ return ret;
+ }
+ ret = hfc_upd_mix_info(rtwdev);
+
+ return ret;
+}
+
+#define PWR_POLL_CNT 2000
+static int pwr_cmd_poll(struct rtw89_dev *rtwdev,
+ const struct rtw89_pwr_cfg *cfg)
+{
+ u8 val = 0;
+ int ret;
+ u32 addr = cfg->base == PWR_INTF_MSK_SDIO ?
+ cfg->addr | SDIO_LOCAL_BASE_ADDR : cfg->addr;
+
+ ret = read_poll_timeout(rtw89_read8, val, !((val ^ cfg->val) & cfg->msk),
+ 1000, 1000 * PWR_POLL_CNT, false, rtwdev, addr);
+
+ if (!ret)
+ return 0;
+
+ rtw89_warn(rtwdev, "[ERR] Polling timeout\n");
+ rtw89_warn(rtwdev, "[ERR] addr: %X, %X\n", addr, cfg->addr);
+ rtw89_warn(rtwdev, "[ERR] val: %X, %X\n", val, cfg->val);
+
+ return -EBUSY;
+}
+
+static int rtw89_mac_sub_pwr_seq(struct rtw89_dev *rtwdev, u8 cv_msk,
+ u8 intf_msk, const struct rtw89_pwr_cfg *cfg)
+{
+ const struct rtw89_pwr_cfg *cur_cfg;
+ u32 addr;
+ u8 val;
+
+ for (cur_cfg = cfg; cur_cfg->cmd != PWR_CMD_END; cur_cfg++) {
+ if (!(cur_cfg->intf_msk & intf_msk) ||
+ !(cur_cfg->cv_msk & cv_msk))
+ continue;
+
+ switch (cur_cfg->cmd) {
+ case PWR_CMD_WRITE:
+ addr = cur_cfg->addr;
+
+ if (cur_cfg->base == PWR_BASE_SDIO)
+ addr |= SDIO_LOCAL_BASE_ADDR;
+
+ val = rtw89_read8(rtwdev, addr);
+ val &= ~(cur_cfg->msk);
+ val |= (cur_cfg->val & cur_cfg->msk);
+
+ rtw89_write8(rtwdev, addr, val);
+ break;
+ case PWR_CMD_POLL:
+ if (pwr_cmd_poll(rtwdev, cur_cfg))
+ return -EBUSY;
+ break;
+ case PWR_CMD_DELAY:
+ if (cur_cfg->val == PWR_DELAY_US)
+ udelay(cur_cfg->addr);
+ else
+ fsleep(cur_cfg->addr * 1000);
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int rtw89_mac_pwr_seq(struct rtw89_dev *rtwdev,
+ const struct rtw89_pwr_cfg * const *cfg_seq)
+{
+ u32 idx = 0;
+ const struct rtw89_pwr_cfg *cfg;
+ int ret;
+
+ do {
+ cfg = cfg_seq[idx];
+ if (!cfg)
+ break;
+
+ ret = rtw89_mac_sub_pwr_seq(rtwdev, BIT(rtwdev->hal.cv),
+ PWR_INTF_MSK_PCIE, cfg);
+ if (ret)
+ return -EBUSY;
+
+ idx++;
+ } while (1);
+
+ return 0;
+}
+
+static enum rtw89_rpwm_req_pwr_state
+rtw89_mac_get_req_pwr_state(struct rtw89_dev *rtwdev)
+{
+ enum rtw89_rpwm_req_pwr_state state;
+
+ switch (rtwdev->ps_mode) {
+ case RTW89_PS_MODE_RFOFF:
+ state = RTW89_MAC_RPWM_REQ_PWR_STATE_BAND0_RFOFF;
+ break;
+ case RTW89_PS_MODE_CLK_GATED:
+ state = RTW89_MAC_RPWM_REQ_PWR_STATE_CLK_GATED;
+ break;
+ case RTW89_PS_MODE_PWR_GATED:
+ state = RTW89_MAC_RPWM_REQ_PWR_STATE_PWR_GATED;
+ break;
+ default:
+ state = RTW89_MAC_RPWM_REQ_PWR_STATE_ACTIVE;
+ break;
+ }
+ return state;
+}
+
+static void rtw89_mac_send_rpwm(struct rtw89_dev *rtwdev,
+ enum rtw89_rpwm_req_pwr_state req_pwr_state)
+{
+ u16 request;
+
+ request = rtw89_read16(rtwdev, R_AX_RPWM);
+ request ^= request | PS_RPWM_TOGGLE;
+
+ rtwdev->mac.rpwm_seq_num = (rtwdev->mac.rpwm_seq_num + 1) &
+ RPWM_SEQ_NUM_MAX;
+ request |= FIELD_PREP(PS_RPWM_SEQ_NUM, rtwdev->mac.rpwm_seq_num);
+
+ request |= req_pwr_state;
+
+ if (req_pwr_state < RTW89_MAC_RPWM_REQ_PWR_STATE_CLK_GATED)
+ request |= PS_RPWM_ACK;
+
+ rtw89_write16(rtwdev, rtwdev->hci.rpwm_addr, request);
+}
+
+static int rtw89_mac_check_cpwm_state(struct rtw89_dev *rtwdev,
+ enum rtw89_rpwm_req_pwr_state req_pwr_state)
+{
+ bool request_deep_mode;
+ bool in_deep_mode;
+ u8 rpwm_req_num;
+ u8 cpwm_rsp_seq;
+ u8 cpwm_seq;
+ u8 cpwm_status;
+
+ if (req_pwr_state >= RTW89_MAC_RPWM_REQ_PWR_STATE_CLK_GATED)
+ request_deep_mode = true;
+ else
+ request_deep_mode = false;
+
+ if (rtw89_read32_mask(rtwdev, R_AX_LDM, B_AX_LDM_32K_EN))
+ in_deep_mode = true;
+ else
+ in_deep_mode = false;
+
+ if (request_deep_mode != in_deep_mode)
+ return -EPERM;
+
+ if (request_deep_mode)
+ return 0;
+
+ rpwm_req_num = rtwdev->mac.rpwm_seq_num;
+ cpwm_rsp_seq = rtw89_read16_mask(rtwdev, R_AX_CPWM,
+ PS_CPWM_RSP_SEQ_NUM);
+
+ if (rpwm_req_num != cpwm_rsp_seq)
+ return -EPERM;
+
+ rtwdev->mac.cpwm_seq_num = (rtwdev->mac.cpwm_seq_num + 1) &
+ CPWM_SEQ_NUM_MAX;
+
+ cpwm_seq = rtw89_read16_mask(rtwdev, R_AX_CPWM, PS_CPWM_SEQ_NUM);
+ if (cpwm_seq != rtwdev->mac.cpwm_seq_num)
+ return -EPERM;
+
+ cpwm_status = rtw89_read16_mask(rtwdev, R_AX_CPWM, PS_CPWM_STATE);
+ if (cpwm_status != req_pwr_state)
+ return -EPERM;
+
+ return 0;
+}
+
+void rtw89_mac_power_mode_change(struct rtw89_dev *rtwdev, bool enter)
+{
+ enum rtw89_rpwm_req_pwr_state state;
+ int ret;
+
+ if (enter)
+ state = rtw89_mac_get_req_pwr_state(rtwdev);
+ else
+ state = RTW89_MAC_RPWM_REQ_PWR_STATE_ACTIVE;
+
+ rtw89_mac_send_rpwm(rtwdev, state);
+ ret = read_poll_timeout_atomic(rtw89_mac_check_cpwm_state, ret, !ret,
+ 1000, 15000, false, rtwdev, state);
+ if (ret)
+ rtw89_err(rtwdev, "firmware failed to ack for %s ps mode\n",
+ enter ? "entering" : "leaving");
+}
+
+static int rtw89_mac_power_switch(struct rtw89_dev *rtwdev, bool on)
+{
+#define PWR_ACT 1
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ const struct rtw89_pwr_cfg * const *cfg_seq;
+ struct rtw89_hal *hal = &rtwdev->hal;
+ int ret;
+ u8 val;
+
+ if (on)
+ cfg_seq = chip->pwr_on_seq;
+ else
+ cfg_seq = chip->pwr_off_seq;
+
+ if (test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags))
+ rtw89_leave_ps_mode(rtwdev);
+
+ val = rtw89_read8(rtwdev, 0x3F1) & 0x3;
+ if (on && val == PWR_ACT) {
+ rtw89_err(rtwdev, "MAC has already powered on\n");
+ return -EBUSY;
+ }
+
+ ret = rtw89_mac_pwr_seq(rtwdev, cfg_seq);
+ if (ret)
+ return ret;
+
+ if (on) {
+ set_bit(RTW89_FLAG_POWERON, rtwdev->flags);
+ rtw89_write8(rtwdev, R_AX_SCOREBOARD + 3, MAC_AX_NOTIFY_TP_MAJOR);
+ } else {
+ clear_bit(RTW89_FLAG_POWERON, rtwdev->flags);
+ clear_bit(RTW89_FLAG_FW_RDY, rtwdev->flags);
+ rtw89_write8(rtwdev, R_AX_SCOREBOARD + 3, MAC_AX_NOTIFY_PWR_MAJOR);
+ hal->current_channel = 0;
+ }
+
+ return 0;
+#undef PWR_ACT
+}
+
+int rtw89_mac_pwr_on(struct rtw89_dev *rtwdev)
+{
+ int ret;
+
+ ret = rtw89_mac_power_switch(rtwdev, true);
+ if (ret) {
+ rtw89_warn(rtwdev, "power on fail\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+void rtw89_mac_pwr_off(struct rtw89_dev *rtwdev)
+{
+ rtw89_mac_power_switch(rtwdev, false);
+}
+
+static int cmac_func_en(struct rtw89_dev *rtwdev, u8 mac_idx, bool en)
+{
+ u32 func_en = 0;
+ u32 ck_en = 0;
+ u32 c1pc_en = 0;
+ u32 addrl_func_en[] = {R_AX_CMAC_FUNC_EN, R_AX_CMAC_FUNC_EN_C1};
+ u32 addrl_ck_en[] = {R_AX_CK_EN, R_AX_CK_EN_C1};
+
+ func_en = B_AX_CMAC_EN | B_AX_CMAC_TXEN | B_AX_CMAC_RXEN |
+ B_AX_PHYINTF_EN | B_AX_CMAC_DMA_EN | B_AX_PTCLTOP_EN |
+ B_AX_SCHEDULER_EN | B_AX_TMAC_EN | B_AX_RMAC_EN;
+ ck_en = B_AX_CMAC_CKEN | B_AX_PHYINTF_CKEN | B_AX_CMAC_DMA_CKEN |
+ B_AX_PTCLTOP_CKEN | B_AX_SCHEDULER_CKEN | B_AX_TMAC_CKEN |
+ B_AX_RMAC_CKEN;
+ c1pc_en = B_AX_R_SYM_WLCMAC1_PC_EN |
+ B_AX_R_SYM_WLCMAC1_P1_PC_EN |
+ B_AX_R_SYM_WLCMAC1_P2_PC_EN |
+ B_AX_R_SYM_WLCMAC1_P3_PC_EN |
+ B_AX_R_SYM_WLCMAC1_P4_PC_EN;
+
+ if (en) {
+ if (mac_idx == RTW89_MAC_1) {
+ rtw89_write32_set(rtwdev, R_AX_AFE_CTRL1, c1pc_en);
+ rtw89_write32_clr(rtwdev, R_AX_SYS_ISO_CTRL_EXTEND,
+ B_AX_R_SYM_ISO_CMAC12PP);
+ rtw89_write32_set(rtwdev, R_AX_SYS_ISO_CTRL_EXTEND,
+ B_AX_CMAC1_FEN);
+ }
+ rtw89_write32_set(rtwdev, addrl_ck_en[mac_idx], ck_en);
+ rtw89_write32_set(rtwdev, addrl_func_en[mac_idx], func_en);
+ } else {
+ rtw89_write32_clr(rtwdev, addrl_func_en[mac_idx], func_en);
+ rtw89_write32_clr(rtwdev, addrl_ck_en[mac_idx], ck_en);
+ if (mac_idx == RTW89_MAC_1) {
+ rtw89_write32_clr(rtwdev, R_AX_SYS_ISO_CTRL_EXTEND,
+ B_AX_CMAC1_FEN);
+ rtw89_write32_set(rtwdev, R_AX_SYS_ISO_CTRL_EXTEND,
+ B_AX_R_SYM_ISO_CMAC12PP);
+ rtw89_write32_clr(rtwdev, R_AX_AFE_CTRL1, c1pc_en);
+ }
+ }
+
+ return 0;
+}
+
+static int dmac_func_en(struct rtw89_dev *rtwdev)
+{
+ u32 val32;
+ u32 ret = 0;
+
+ val32 = (B_AX_MAC_FUNC_EN | B_AX_DMAC_FUNC_EN | B_AX_MAC_SEC_EN |
+ B_AX_DISPATCHER_EN | B_AX_DLE_CPUIO_EN | B_AX_PKT_IN_EN |
+ B_AX_DMAC_TBL_EN | B_AX_PKT_BUF_EN | B_AX_STA_SCH_EN |
+ B_AX_TXPKT_CTRL_EN | B_AX_WD_RLS_EN | B_AX_MPDU_PROC_EN);
+ rtw89_write32(rtwdev, R_AX_DMAC_FUNC_EN, val32);
+
+ val32 = (B_AX_MAC_SEC_CLK_EN | B_AX_DISPATCHER_CLK_EN |
+ B_AX_DLE_CPUIO_CLK_EN | B_AX_PKT_IN_CLK_EN |
+ B_AX_STA_SCH_CLK_EN | B_AX_TXPKT_CTRL_CLK_EN |
+ B_AX_WD_RLS_CLK_EN);
+ rtw89_write32(rtwdev, R_AX_DMAC_CLK_EN, val32);
+
+ return ret;
+}
+
+static int chip_func_en(struct rtw89_dev *rtwdev)
+{
+ rtw89_write32_set(rtwdev, R_AX_SPSLDO_ON_CTRL0, B_AX_OCP_L1_MASK);
+
+ return 0;
+}
+
+static int rtw89_mac_sys_init(struct rtw89_dev *rtwdev)
+{
+ int ret;
+
+ ret = dmac_func_en(rtwdev);
+ if (ret)
+ return ret;
+
+ ret = cmac_func_en(rtwdev, 0, true);
+ if (ret)
+ return ret;
+
+ ret = chip_func_en(rtwdev);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+/* PCIE 64 */
+struct rtw89_dle_size wde_size0 = {
+ RTW89_WDE_PG_64, 4095, 1,
+};
+
+/* SDIO, PCIE STF, USB */
+struct rtw89_dle_size wde_size1 = {
+ RTW89_WDE_PG_64, 768, 0,
+};
+
+/* PCIE 128 */
+struct rtw89_dle_size wde_size2 = {
+ RTW89_WDE_PG_128, 2016, 32,
+};
+
+/* PCIE SU TP */
+struct rtw89_dle_size wde_size3 = {
+ RTW89_WDE_PG_64, 496, 3600,
+};
+
+/* DLFW */
+struct rtw89_dle_size wde_size4 = {
+ RTW89_WDE_PG_64, 0, 4096,
+};
+
+/* PCIE BCN TEST */
+struct rtw89_dle_size wde_size5 = {
+ RTW89_WDE_PG_64, 3904, 64,
+};
+
+/* PCIE 64 */
+struct rtw89_dle_size wde_size6 = {
+ RTW89_WDE_PG_64, 1024, 0,
+};
+
+/* PCIE 128 */
+struct rtw89_dle_size wde_size7 = {
+ RTW89_WDE_PG_128, 960, 0,
+};
+
+/* PCIE STF, USB */
+struct rtw89_dle_size wde_size8 = {
+ RTW89_WDE_PG_64, 256, 0,
+};
+
+/* DLFW */
+struct rtw89_dle_size wde_size9 = {
+ RTW89_WDE_PG_64, 0, 1024,
+};
+
+/* LA-PCIE */
+struct rtw89_dle_size wde_size10 = {
+ RTW89_WDE_PG_64, 1408, 0,
+};
+
+/* PCIE */
+struct rtw89_dle_size ple_size0 = {
+ RTW89_PLE_PG_128, 1520, 16,
+};
+
+/* PCIE STF */
+struct rtw89_dle_size ple_size2 = {
+ RTW89_PLE_PG_128, 3200, 0,
+};
+
+/* PCIE SU TP */
+struct rtw89_dle_size ple_size3 = {
+ RTW89_PLE_PG_128, 330, 1206,
+};
+
+/* DLFW */
+struct rtw89_dle_size ple_size4 = {
+ RTW89_PLE_PG_128, 64, 1472,
+};
+
+/* PCIE BCN TEST */
+struct rtw89_dle_size ple_size5 = {
+ RTW89_PLE_PG_128, 1520, 80,
+};
+
+/* PCIE 64 */
+struct rtw89_dle_size ple_size6 = {
+ RTW89_PLE_PG_128, 1008, 16,
+};
+
+/* PCIE STF, USB */
+struct rtw89_dle_size ple_size7 = {
+ RTW89_PLE_PG_128, 1408, 0,
+};
+
+/* DLFW */
+struct rtw89_dle_size ple_size8 = {
+ RTW89_PLE_PG_128, 64, 960,
+};
+
+/* PCIE 128 */
+struct rtw89_dle_size ple_size9 = {
+ RTW89_PLE_PG_128, 576, 0,
+};
+
+/* LA-PCIE */
+struct rtw89_dle_size ple_size10 = {
+ RTW89_PLE_PG_128, 832, 0,
+};
+
+/* PCIE 64 */
+struct rtw89_wde_quota wde_qt0 = {
+ 3792, 196, 0, 107,
+};
+
+/* SDIO, PCIE STF, USB */
+struct rtw89_wde_quota wde_qt1 = {
+ 512, 196, 0, 60,
+};
+
+/* PCIE 128 */
+struct rtw89_wde_quota wde_qt2 = {
+ 1896, 98, 0, 22,
+};
+
+/* PCIE SU TP */
+struct rtw89_wde_quota wde_qt3 = {
+ 256, 196, 0, 44,
+};
+
+/* DLFW */
+struct rtw89_wde_quota wde_qt4 = {
+ 0, 0, 0, 0,
+};
+
+/* PCIE BCN TEST */
+struct rtw89_wde_quota wde_qt5 = {
+ 3666, 196, 0, 44,
+};
+
+/* PCIE 64 */
+struct rtw89_wde_quota wde_qt6 = {
+ 968, 48, 0, 8,
+};
+
+/* PCIE 128 */
+struct rtw89_wde_quota wde_qt7 = {
+ 896, 56, 0, 8,
+};
+
+/* PCIE STF, USB */
+struct rtw89_wde_quota wde_qt8 = {
+ 236, 16, 0, 4,
+};
+
+/* LA-PCIE */
+struct rtw89_wde_quota wde_qt9 = {
+ 1392, 8, 0, 8,
+};
+
+/* PCIE DBCC */
+struct rtw89_ple_quota ple_qt0 = {
+ 264, 66, 16, 20, 26, 13, 356, 89, 32, 40, 8,
+};
+
+/* PCIE DBCC */
+struct rtw89_ple_quota ple_qt1 = {
+ 264, 66, 32, 20, 64, 13, 946, 679, 64, 128, 320,
+};
+
+/* PCIE SCC */
+struct rtw89_ple_quota ple_qt4 = {
+ 264, 0, 16, 20, 26, 13, 356, 0, 32, 40, 8,
+};
+
+/* PCIE SCC */
+struct rtw89_ple_quota ple_qt5 = {
+ 264, 0, 32, 20, 64, 13, 1101, 0, 64, 128, 120,
+};
+
+/* PCIE STF SCC */
+struct rtw89_ple_quota ple_qt8 = {
+ 1536, 0, 16, 20, 13, 13, 356, 0, 32, 40, 8,
+};
+
+/* PCIE STF SCC */
+struct rtw89_ple_quota ple_qt9 = {
+ 2702, 0, 32, 20, 64, 13, 1522, 0, 64, 128, 320,
+};
+
+/* PCIE STF DBCC */
+struct rtw89_ple_quota ple_qt10 = {
+ 2272, 0, 16, 20, 26, 13, 356, 89, 32, 40, 8
+};
+
+/* PCIE STF DBCC */
+struct rtw89_ple_quota ple_qt11 = {
+ 2600, 0, 32, 20, 64, 13, 684, 417, 64, 128, 320
+};
+
+/* PCIE SU TP */
+struct rtw89_ple_quota ple_qt12 = {
+ 50, 50, 16, 20, 26, 26, 25, 25, 32, 40, 20
+};
+
+/* DLFW */
+struct rtw89_ple_quota ple_qt13 = {
+ 0, 0, 16, 48, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* PCIE BCN TEST */
+struct rtw89_ple_quota ple_qt14 = {
+ 588, 147, 16, 20, 26, 26, 356, 89, 32, 40, 80,
+};
+
+/* PCIE BCN TEST */
+struct rtw89_ple_quota ple_qt15 = {
+ 688, 247, 116, 20, 64, 126, 456, 189, 64, 128, 80,
+};
+
+/* USB DBCC */
+struct rtw89_ple_quota ple_qt16 = {
+ 2048, 0, 16, 48, 26, 26, 360, 90, 32, 40, 1,
+};
+
+/* PCIE 64 */
+struct rtw89_ple_quota ple_qt18 = {
+ 66, 0, 16, 20, 13, 13, 178, 0, 32, 14, 8,
+};
+
+/* PCIE 64 */
+struct rtw89_ple_quota ple_qt19 = {
+ 66, 0, 32, 20, 661, 13, 826, 0, 680, 14, 64,
+};
+
+/* PCIE STF */
+struct rtw89_ple_quota ple_qt20 = {
+ 962, 0, 16, 20, 80, 13, 178, 0, 32, 14, 8,
+};
+
+/* PCIE STF */
+struct rtw89_ple_quota ple_qt21 = {
+ 1047, 0, 32, 20, 165, 13, 263, 0, 117, 14, 64,
+};
+
+/* PCIE 128 */
+struct rtw89_ple_quota ple_qt22 = {
+ 269, 0, 18, 20, 15, 28, 180, 0, 34, 14, 8,
+};
+
+/* LA-PCIE MIN*/
+struct rtw89_ple_quota ple_qt23 = {
+ 156, 39, 16, 20, 26, 26, 356, 89, 32, 40, 1,
+};
+
+/* LA-PCIE MAX*/
+struct rtw89_ple_quota ple_qt24 = {
+ 187, 70, 47, 20, 64, 57, 387, 120, 64, 128, 1,
+};
+
+static struct rtw89_dle_mem *get_dle_mem_cfg(struct rtw89_dev *rtwdev,
+ enum rtw89_qta_mode mode)
+{
+ struct rtw89_mac_info *mac = &rtwdev->mac;
+ struct rtw89_dle_mem *cfg;
+
+ cfg = &rtwdev->chip->dle_mem[mode];
+ if (!cfg)
+ return NULL;
+
+ if (cfg->mode != mode) {
+ rtw89_warn(rtwdev, "qta mode unmatch!\n");
+ return NULL;
+ }
+
+ mac->dle_info.wde_pg_size = cfg->wde_size->pge_size;
+ mac->dle_info.ple_pg_size = cfg->ple_size->pge_size;
+ mac->dle_info.qta_mode = mode;
+ mac->dle_info.c0_rx_qta = cfg->ple_min_qt->cma0_dma;
+ mac->dle_info.c1_rx_qta = cfg->ple_min_qt->cma1_dma;
+
+ return cfg;
+}
+
+static inline u32 dle_used_size(struct rtw89_dle_size *wde,
+ struct rtw89_dle_size *ple)
+{
+ return wde->pge_size * (wde->lnk_pge_num + wde->unlnk_pge_num) +
+ ple->pge_size * (ple->lnk_pge_num + ple->unlnk_pge_num);
+}
+
+static void dle_func_en(struct rtw89_dev *rtwdev, bool enable)
+{
+ if (enable)
+ rtw89_write32_set(rtwdev, R_AX_DMAC_FUNC_EN,
+ B_AX_DLE_WDE_EN | B_AX_DLE_PLE_EN);
+ else
+ rtw89_write32_clr(rtwdev, R_AX_DMAC_FUNC_EN,
+ B_AX_DLE_WDE_EN | B_AX_DLE_PLE_EN);
+}
+
+static void dle_clk_en(struct rtw89_dev *rtwdev, bool enable)
+{
+ if (enable)
+ rtw89_write32_set(rtwdev, R_AX_DMAC_CLK_EN,
+ B_AX_DLE_WDE_CLK_EN | B_AX_DLE_PLE_CLK_EN);
+ else
+ rtw89_write32_clr(rtwdev, R_AX_DMAC_CLK_EN,
+ B_AX_DLE_WDE_CLK_EN | B_AX_DLE_PLE_CLK_EN);
+}
+
+static int dle_mix_cfg(struct rtw89_dev *rtwdev, struct rtw89_dle_mem *cfg)
+{
+ struct rtw89_dle_size *size_cfg;
+ u32 val;
+ u8 bound = 0;
+
+ val = rtw89_read32(rtwdev, R_AX_WDE_PKTBUF_CFG);
+ size_cfg = cfg->wde_size;
+
+ switch (size_cfg->pge_size) {
+ default:
+ case RTW89_WDE_PG_64:
+ val = u32_replace_bits(val, S_AX_WDE_PAGE_SEL_64,
+ B_AX_WDE_PAGE_SEL_MASK);
+ break;
+ case RTW89_WDE_PG_128:
+ val = u32_replace_bits(val, S_AX_WDE_PAGE_SEL_128,
+ B_AX_WDE_PAGE_SEL_MASK);
+ break;
+ case RTW89_WDE_PG_256:
+ rtw89_err(rtwdev, "[ERR]WDE DLE doesn't support 256 byte!\n");
+ return -EINVAL;
+ }
+
+ val = u32_replace_bits(val, bound, B_AX_WDE_START_BOUND_MASK);
+ val = u32_replace_bits(val, size_cfg->lnk_pge_num,
+ B_AX_WDE_FREE_PAGE_NUM_MASK);
+ rtw89_write32(rtwdev, R_AX_WDE_PKTBUF_CFG, val);
+
+ val = rtw89_read32(rtwdev, R_AX_PLE_PKTBUF_CFG);
+ bound = (size_cfg->lnk_pge_num + size_cfg->unlnk_pge_num)
+ * size_cfg->pge_size / DLE_BOUND_UNIT;
+ size_cfg = cfg->ple_size;
+
+ switch (size_cfg->pge_size) {
+ default:
+ case RTW89_PLE_PG_64:
+ rtw89_err(rtwdev, "[ERR]PLE DLE doesn't support 64 byte!\n");
+ return -EINVAL;
+ case RTW89_PLE_PG_128:
+ val = u32_replace_bits(val, S_AX_PLE_PAGE_SEL_128,
+ B_AX_PLE_PAGE_SEL_MASK);
+ break;
+ case RTW89_PLE_PG_256:
+ val = u32_replace_bits(val, S_AX_PLE_PAGE_SEL_256,
+ B_AX_PLE_PAGE_SEL_MASK);
+ break;
+ }
+
+ val = u32_replace_bits(val, bound, B_AX_PLE_START_BOUND_MASK);
+ val = u32_replace_bits(val, size_cfg->lnk_pge_num,
+ B_AX_PLE_FREE_PAGE_NUM_MASK);
+ rtw89_write32(rtwdev, R_AX_PLE_PKTBUF_CFG, val);
+
+ return 0;
+}
+
+#define SET_QUOTA(_x, _module, _idx) \
+ do { \
+ val = (min_cfg->_x & \
+ B_AX_ ## _module ## _MIN_SIZE_MASK) | \
+ ((max_cfg->_x << 16) & \
+ B_AX_ ## _module ## _MAX_SIZE_MASK); \
+ rtw89_write32(rtwdev, \
+ R_AX_ ## _module ## _QTA ## _idx ## _CFG, \
+ val); \
+ } while (0)
+
+static void wde_quota_cfg(struct rtw89_dev *rtwdev,
+ struct rtw89_wde_quota *min_cfg,
+ struct rtw89_wde_quota *max_cfg)
+{
+ u32 val;
+
+ SET_QUOTA(hif, WDE, 0);
+ SET_QUOTA(wcpu, WDE, 1);
+ SET_QUOTA(pkt_in, WDE, 3);
+ SET_QUOTA(cpu_io, WDE, 4);
+}
+
+static void ple_quota_cfg(struct rtw89_dev *rtwdev,
+ struct rtw89_ple_quota *min_cfg,
+ struct rtw89_ple_quota *max_cfg)
+{
+ u32 val;
+
+ SET_QUOTA(cma0_tx, PLE, 0);
+ SET_QUOTA(cma1_tx, PLE, 1);
+ SET_QUOTA(c2h, PLE, 2);
+ SET_QUOTA(h2c, PLE, 3);
+ SET_QUOTA(wcpu, PLE, 4);
+ SET_QUOTA(mpdu_proc, PLE, 5);
+ SET_QUOTA(cma0_dma, PLE, 6);
+ SET_QUOTA(cma1_dma, PLE, 7);
+ SET_QUOTA(bb_rpt, PLE, 8);
+ SET_QUOTA(wd_rel, PLE, 9);
+ SET_QUOTA(cpu_io, PLE, 10);
+}
+
+#undef SET_QUOTA
+
+static void dle_quota_cfg(struct rtw89_dev *rtwdev, struct rtw89_dle_mem *cfg)
+{
+ wde_quota_cfg(rtwdev, cfg->wde_min_qt, cfg->wde_max_qt);
+ ple_quota_cfg(rtwdev, cfg->ple_min_qt, cfg->ple_max_qt);
+}
+
+static u32 dle_rsvd_size(struct rtw89_dev *rtwdev, enum rtw89_qta_mode mode)
+{
+ if (mode != RTW89_QTA_LAMODE)
+ return 0;
+
+ return rtwdev->chip->dle_lamode_size;
+}
+
+static int dle_init(struct rtw89_dev *rtwdev, enum rtw89_qta_mode mode,
+ enum rtw89_qta_mode ext_mode)
+{
+ struct rtw89_dle_mem *cfg, *ext_cfg;
+ int ret = 0;
+ u32 ini;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, RTW89_MAC_0, RTW89_DMAC_SEL);
+ if (ret)
+ return ret;
+
+ cfg = get_dle_mem_cfg(rtwdev, mode);
+ if (!cfg) {
+ rtw89_err(rtwdev, "[ERR]get_dle_mem_cfg\n");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (mode == RTW89_QTA_DLFW) {
+ ext_cfg = get_dle_mem_cfg(rtwdev, ext_mode);
+ if (!ext_cfg) {
+ rtw89_err(rtwdev, "[ERR]get_dle_ext_mem_cfg %d\n",
+ ext_mode);
+ ret = -EINVAL;
+ goto error;
+ }
+ cfg->wde_min_qt->wcpu = ext_cfg->wde_min_qt->wcpu;
+ }
+
+ if (dle_used_size(cfg->wde_size, cfg->ple_size) !=
+ (rtwdev->chip->fifo_size - dle_rsvd_size(rtwdev, mode))) {
+ rtw89_err(rtwdev, "[ERR]wd/dle mem cfg\n");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ dle_func_en(rtwdev, false);
+ dle_clk_en(rtwdev, true);
+
+ ret = dle_mix_cfg(rtwdev, cfg);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR] dle mix cfg\n");
+ goto error;
+ }
+ dle_quota_cfg(rtwdev, cfg);
+
+ dle_func_en(rtwdev, true);
+
+ ret = read_poll_timeout(rtw89_read32, ini,
+ (ini & WDE_MGN_INI_RDY) == WDE_MGN_INI_RDY, 1,
+ 2000, false, rtwdev, R_AX_WDE_INI_STATUS);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]WDE cfg ready\n");
+ return ret;
+ }
+
+ ret = read_poll_timeout(rtw89_read32, ini,
+ (ini & WDE_MGN_INI_RDY) == WDE_MGN_INI_RDY, 1,
+ 2000, false, rtwdev, R_AX_PLE_INI_STATUS);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]PLE cfg ready\n");
+ return ret;
+ }
+
+ return 0;
+error:
+ dle_func_en(rtwdev, false);
+ rtw89_err(rtwdev, "[ERR]trxcfg wde 0x8900 = %x\n",
+ rtw89_read32(rtwdev, R_AX_WDE_INI_STATUS));
+ rtw89_err(rtwdev, "[ERR]trxcfg ple 0x8D00 = %x\n",
+ rtw89_read32(rtwdev, R_AX_PLE_INI_STATUS));
+
+ return ret;
+}
+
+static bool dle_is_txq_empty(struct rtw89_dev *rtwdev)
+{
+ u32 msk32;
+ u32 val32;
+
+ msk32 = B_AX_WDE_EMPTY_QUE_CMAC0_ALL_AC | B_AX_WDE_EMPTY_QUE_CMAC0_MBH |
+ B_AX_WDE_EMPTY_QUE_CMAC1_MBH | B_AX_WDE_EMPTY_QUE_CMAC0_WMM0 |
+ B_AX_WDE_EMPTY_QUE_CMAC0_WMM1 | B_AX_WDE_EMPTY_QUE_OTHERS |
+ B_AX_PLE_EMPTY_QUE_DMAC_MPDU_TX | B_AX_PLE_EMPTY_QTA_DMAC_H2C |
+ B_AX_PLE_EMPTY_QUE_DMAC_SEC_TX | B_AX_WDE_EMPTY_QUE_DMAC_PKTIN |
+ B_AX_WDE_EMPTY_QTA_DMAC_HIF | B_AX_WDE_EMPTY_QTA_DMAC_WLAN_CPU |
+ B_AX_WDE_EMPTY_QTA_DMAC_PKTIN | B_AX_WDE_EMPTY_QTA_DMAC_CPUIO |
+ B_AX_PLE_EMPTY_QTA_DMAC_B0_TXPL |
+ B_AX_PLE_EMPTY_QTA_DMAC_B1_TXPL |
+ B_AX_PLE_EMPTY_QTA_DMAC_MPDU_TX |
+ B_AX_PLE_EMPTY_QTA_DMAC_CPUIO |
+ B_AX_WDE_EMPTY_QTA_DMAC_DATA_CPU |
+ B_AX_PLE_EMPTY_QTA_DMAC_WLAN_CPU;
+ val32 = rtw89_read32(rtwdev, R_AX_DLE_EMPTY0);
+
+ if ((val32 & msk32) == msk32)
+ return true;
+
+ return false;
+}
+
+static int sta_sch_init(struct rtw89_dev *rtwdev)
+{
+ u32 p_val;
+ u8 val;
+ int ret;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, RTW89_MAC_0, RTW89_DMAC_SEL);
+ if (ret)
+ return ret;
+
+ val = rtw89_read8(rtwdev, R_AX_SS_CTRL);
+ val |= B_AX_SS_EN;
+ rtw89_write8(rtwdev, R_AX_SS_CTRL, val);
+
+ ret = read_poll_timeout(rtw89_read32, p_val, p_val & B_AX_SS_INIT_DONE_1,
+ 1, TRXCFG_WAIT_CNT, false, rtwdev, R_AX_SS_CTRL);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]STA scheduler init\n");
+ return ret;
+ }
+
+ rtw89_write32_set(rtwdev, R_AX_SS_CTRL, B_AX_SS_WARM_INIT_FLG);
+
+ return 0;
+}
+
+static int mpdu_proc_init(struct rtw89_dev *rtwdev)
+{
+ int ret;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, RTW89_MAC_0, RTW89_DMAC_SEL);
+ if (ret)
+ return ret;
+
+ rtw89_write32(rtwdev, R_AX_ACTION_FWD0, TRXCFG_MPDU_PROC_ACT_FRWD);
+ rtw89_write32(rtwdev, R_AX_TF_FWD, TRXCFG_MPDU_PROC_TF_FRWD);
+ rtw89_write32_set(rtwdev, R_AX_MPDU_PROC,
+ B_AX_APPEND_FCS | B_AX_A_ICV_ERR);
+ rtw89_write32(rtwdev, R_AX_CUT_AMSDU_CTRL, TRXCFG_MPDU_PROC_CUT_CTRL);
+
+ return 0;
+}
+
+static int sec_eng_init(struct rtw89_dev *rtwdev)
+{
+ u32 val = 0;
+ int ret;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, RTW89_MAC_0, RTW89_DMAC_SEL);
+ if (ret)
+ return ret;
+
+ val = rtw89_read32(rtwdev, R_AX_SEC_ENG_CTRL);
+ /* init clock */
+ val |= (B_AX_CLK_EN_CGCMP | B_AX_CLK_EN_WAPI | B_AX_CLK_EN_WEP_TKIP);
+ /* init TX encryption */
+ val |= (B_AX_SEC_TX_ENC | B_AX_SEC_RX_DEC);
+ val |= (B_AX_MC_DEC | B_AX_BC_DEC);
+ val &= ~B_AX_TX_PARTIAL_MODE;
+ rtw89_write32(rtwdev, R_AX_SEC_ENG_CTRL, val);
+
+ /* init MIC ICV append */
+ val = rtw89_read32(rtwdev, R_AX_SEC_MPDU_PROC);
+ val |= (B_AX_APPEND_ICV | B_AX_APPEND_MIC);
+
+ /* option init */
+ rtw89_write32(rtwdev, R_AX_SEC_MPDU_PROC, val);
+
+ return 0;
+}
+
+static int dmac_init(struct rtw89_dev *rtwdev, u8 mac_idx)
+{
+ int ret;
+
+ ret = dle_init(rtwdev, rtwdev->mac.qta_mode, RTW89_QTA_INVALID);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]DLE init %d\n", ret);
+ return ret;
+ }
+
+ ret = hfc_init(rtwdev, true, true, true);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]HCI FC init %d\n", ret);
+ return ret;
+ }
+
+ ret = sta_sch_init(rtwdev);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]STA SCH init %d\n", ret);
+ return ret;
+ }
+
+ ret = mpdu_proc_init(rtwdev);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]MPDU Proc init %d\n", ret);
+ return ret;
+ }
+
+ ret = sec_eng_init(rtwdev);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]Security Engine init %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int addr_cam_init(struct rtw89_dev *rtwdev, u8 mac_idx)
+{
+ u32 val, reg;
+ u16 p_val;
+ int ret;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL);
+ if (ret)
+ return ret;
+
+ reg = rtw89_mac_reg_by_idx(R_AX_ADDR_CAM_CTRL, mac_idx);
+
+ val = rtw89_read32(rtwdev, reg);
+ val |= u32_encode_bits(0x7f, B_AX_ADDR_CAM_RANGE_MASK) |
+ B_AX_ADDR_CAM_CLR | B_AX_ADDR_CAM_EN;
+ rtw89_write32(rtwdev, reg, val);
+
+ ret = read_poll_timeout(rtw89_read16, p_val, !(p_val & B_AX_ADDR_CAM_CLR),
+ 1, TRXCFG_WAIT_CNT, false, rtwdev, B_AX_ADDR_CAM_CLR);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]ADDR_CAM reset\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int scheduler_init(struct rtw89_dev *rtwdev, u8 mac_idx)
+{
+ u32 ret;
+ u32 reg;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL);
+ if (ret)
+ return ret;
+
+ reg = rtw89_mac_reg_by_idx(R_AX_PREBKF_CFG_0, mac_idx);
+ rtw89_write32_mask(rtwdev, reg, B_AX_PREBKF_TIME_MASK, SCH_PREBKF_24US);
+
+ return 0;
+}
+
+static int rtw89_mac_typ_fltr_opt(struct rtw89_dev *rtwdev,
+ enum rtw89_machdr_frame_type type,
+ enum rtw89_mac_fwd_target fwd_target,
+ u8 mac_idx)
+{
+ u32 reg;
+ u32 val;
+
+ switch (fwd_target) {
+ case RTW89_FWD_DONT_CARE:
+ val = RX_FLTR_FRAME_DROP;
+ break;
+ case RTW89_FWD_TO_HOST:
+ val = RX_FLTR_FRAME_TO_HOST;
+ break;
+ case RTW89_FWD_TO_WLAN_CPU:
+ val = RX_FLTR_FRAME_TO_WLCPU;
+ break;
+ default:
+ rtw89_err(rtwdev, "[ERR]set rx filter fwd target err\n");
+ return -EINVAL;
+ }
+
+ switch (type) {
+ case RTW89_MGNT:
+ reg = rtw89_mac_reg_by_idx(R_AX_MGNT_FLTR, mac_idx);
+ break;
+ case RTW89_CTRL:
+ reg = rtw89_mac_reg_by_idx(R_AX_CTRL_FLTR, mac_idx);
+ break;
+ case RTW89_DATA:
+ reg = rtw89_mac_reg_by_idx(R_AX_DATA_FLTR, mac_idx);
+ break;
+ default:
+ rtw89_err(rtwdev, "[ERR]set rx filter type err\n");
+ return -EINVAL;
+ }
+ rtw89_write32(rtwdev, reg, val);
+
+ return 0;
+}
+
+static int rx_fltr_init(struct rtw89_dev *rtwdev, u8 mac_idx)
+{
+ int ret, i;
+ u32 mac_ftlr, plcp_ftlr;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL);
+ if (ret)
+ return ret;
+
+ for (i = RTW89_MGNT; i <= RTW89_DATA; i++) {
+ ret = rtw89_mac_typ_fltr_opt(rtwdev, i, RTW89_FWD_TO_HOST,
+ mac_idx);
+ if (ret)
+ return ret;
+ }
+ mac_ftlr = rtwdev->hal.rx_fltr;
+ plcp_ftlr = B_AX_CCK_CRC_CHK | B_AX_CCK_SIG_CHK |
+ B_AX_LSIG_PARITY_CHK | B_AX_SIGA_CRC_CHK |
+ B_AX_VHT_SU_SIGB_CRC_CHK | B_AX_VHT_MU_SIGB_CRC_CHK |
+ B_AX_HE_SIGB_CRC_CHK;
+ rtw89_write32(rtwdev, rtw89_mac_reg_by_idx(R_AX_RX_FLTR_OPT, mac_idx),
+ mac_ftlr);
+ rtw89_write16(rtwdev, rtw89_mac_reg_by_idx(R_AX_PLCP_HDR_FLTR, mac_idx),
+ plcp_ftlr);
+
+ return 0;
+}
+
+static int cca_ctrl_init(struct rtw89_dev *rtwdev, u8 mac_idx)
+{
+ u32 val, reg;
+ int ret;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL);
+ if (ret)
+ return ret;
+
+ reg = rtw89_mac_reg_by_idx(R_AX_CCA_CONTROL, mac_idx);
+ val = rtw89_read32(rtwdev, reg);
+ val |= (B_AX_TB_CHK_BASIC_NAV | B_AX_TB_CHK_BTCCA |
+ B_AX_TB_CHK_EDCCA | B_AX_TB_CHK_CCA_P20 |
+ B_AX_SIFS_CHK_BTCCA | B_AX_SIFS_CHK_CCA_P20 |
+ B_AX_CTN_CHK_TXNAV | B_AX_CTN_CHK_INTRA_NAV |
+ B_AX_CTN_CHK_BASIC_NAV | B_AX_CTN_CHK_BTCCA |
+ B_AX_CTN_CHK_EDCCA | B_AX_CTN_CHK_CCA_S80 |
+ B_AX_CTN_CHK_CCA_S40 | B_AX_CTN_CHK_CCA_S20 |
+ B_AX_CTN_CHK_CCA_P20);
+ val &= ~(B_AX_TB_CHK_TX_NAV | B_AX_TB_CHK_CCA_S80 |
+ B_AX_TB_CHK_CCA_S40 | B_AX_TB_CHK_CCA_S20 |
+ B_AX_SIFS_CHK_CCA_S80 | B_AX_SIFS_CHK_CCA_S40 |
+ B_AX_SIFS_CHK_CCA_S20 | B_AX_SIFS_CHK_EDCCA);
+ rtw89_write32(rtwdev, reg, val);
+
+ reg = rtw89_mac_reg_by_idx(R_AX_RSP_CHK_SIG, mac_idx);
+ val = rtw89_read32(rtwdev, reg);
+ val |= (B_AX_RSP_CHK_TX_NAV | B_AX_RSP_CHK_INTRA_NAV |
+ B_AX_RSP_CHK_BASIC_NAV | B_AX_RSP_CHK_SEC_CCA_80 |
+ B_AX_RSP_CHK_SEC_CCA_40 | B_AX_RSP_CHK_SEC_CCA_20 |
+ B_AX_RSP_CHK_BTCCA | B_AX_RSP_CHK_EDCCA |
+ B_AX_RSP_CHK_CCA);
+ rtw89_write32(rtwdev, reg, val);
+
+ return 0;
+}
+
+static int spatial_reuse_init(struct rtw89_dev *rtwdev, u8 mac_idx)
+{
+ u32 reg;
+ int ret;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL);
+ if (ret)
+ return ret;
+ reg = rtw89_mac_reg_by_idx(R_AX_RX_SR_CTRL, mac_idx);
+ rtw89_write8_clr(rtwdev, reg, B_AX_SR_EN);
+
+ return 0;
+}
+
+static int tmac_init(struct rtw89_dev *rtwdev, u8 mac_idx)
+{
+ u32 reg;
+ int ret;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL);
+ if (ret)
+ return ret;
+
+ reg = rtw89_mac_reg_by_idx(R_AX_MAC_LOOPBACK, mac_idx);
+ rtw89_write32_clr(rtwdev, reg, B_AX_MACLBK_EN);
+
+ return 0;
+}
+
+static int trxptcl_init(struct rtw89_dev *rtwdev, u8 mac_idx)
+{
+ u32 reg, val, sifs;
+ int ret;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL);
+ if (ret)
+ return ret;
+
+ reg = rtw89_mac_reg_by_idx(R_AX_TRXPTCL_RESP_0, mac_idx);
+ val = rtw89_read32(rtwdev, reg);
+ val &= ~B_AX_WMAC_SPEC_SIFS_CCK_MASK;
+ val |= FIELD_PREP(B_AX_WMAC_SPEC_SIFS_CCK_MASK, WMAC_SPEC_SIFS_CCK);
+
+ switch (rtwdev->chip->chip_id) {
+ case RTL8852A:
+ sifs = WMAC_SPEC_SIFS_OFDM_52A;
+ break;
+ case RTL8852B:
+ sifs = WMAC_SPEC_SIFS_OFDM_52B;
+ break;
+ default:
+ sifs = WMAC_SPEC_SIFS_OFDM_52C;
+ break;
+ }
+ val &= ~B_AX_WMAC_SPEC_SIFS_OFDM_MASK;
+ val |= FIELD_PREP(B_AX_WMAC_SPEC_SIFS_OFDM_MASK, sifs);
+ rtw89_write32(rtwdev, reg, val);
+
+ reg = rtw89_mac_reg_by_idx(R_AX_RXTRIG_TEST_USER_2, mac_idx);
+ rtw89_write32_set(rtwdev, reg, B_AX_RXTRIG_FCSCHK_EN);
+
+ return 0;
+}
+
+static int rmac_init(struct rtw89_dev *rtwdev, u8 mac_idx)
+{
+#define TRXCFG_RMAC_CCA_TO 32
+#define TRXCFG_RMAC_DATA_TO 15
+#define RX_MAX_LEN_UNIT 512
+#define PLD_RLS_MAX_PG 127
+ int ret;
+ u32 reg, rx_max_len, rx_qta;
+ u16 val;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL);
+ if (ret)
+ return ret;
+
+ reg = rtw89_mac_reg_by_idx(R_AX_RESPBA_CAM_CTRL, mac_idx);
+ rtw89_write8_set(rtwdev, reg, B_AX_SSN_SEL);
+
+ reg = rtw89_mac_reg_by_idx(R_AX_DLK_PROTECT_CTL, mac_idx);
+ val = rtw89_read16(rtwdev, reg);
+ val = u16_replace_bits(val, TRXCFG_RMAC_DATA_TO,
+ B_AX_RX_DLK_DATA_TIME_MASK);
+ val = u16_replace_bits(val, TRXCFG_RMAC_CCA_TO,
+ B_AX_RX_DLK_CCA_TIME_MASK);
+ rtw89_write16(rtwdev, reg, val);
+
+ reg = rtw89_mac_reg_by_idx(R_AX_RCR, mac_idx);
+ rtw89_write8_mask(rtwdev, reg, B_AX_CH_EN_MASK, 0x1);
+
+ reg = rtw89_mac_reg_by_idx(R_AX_RX_FLTR_OPT, mac_idx);
+ if (mac_idx == RTW89_MAC_0)
+ rx_qta = rtwdev->mac.dle_info.c0_rx_qta;
+ else
+ rx_qta = rtwdev->mac.dle_info.c1_rx_qta;
+ rx_qta = rx_qta > PLD_RLS_MAX_PG ? PLD_RLS_MAX_PG : rx_qta;
+ rx_max_len = (rx_qta - 1) * rtwdev->mac.dle_info.ple_pg_size /
+ RX_MAX_LEN_UNIT;
+ rx_max_len = rx_max_len > B_AX_RX_MPDU_MAX_LEN_SIZE ?
+ B_AX_RX_MPDU_MAX_LEN_SIZE : rx_max_len;
+ rtw89_write32_mask(rtwdev, reg, B_AX_RX_MPDU_MAX_LEN_MASK, rx_max_len);
+
+ if (rtwdev->chip->chip_id == RTL8852A &&
+ rtwdev->hal.cv == CHIP_CBV) {
+ rtw89_write16_mask(rtwdev,
+ rtw89_mac_reg_by_idx(R_AX_DLK_PROTECT_CTL, mac_idx),
+ B_AX_RX_DLK_CCA_TIME_MASK, 0);
+ rtw89_write16_set(rtwdev, rtw89_mac_reg_by_idx(R_AX_RCR, mac_idx),
+ BIT(12));
+ }
+
+ reg = rtw89_mac_reg_by_idx(R_AX_PLCP_HDR_FLTR, mac_idx);
+ rtw89_write8_clr(rtwdev, reg, B_AX_VHT_SU_SIGB_CRC_CHK);
+
+ return ret;
+}
+
+static int cmac_com_init(struct rtw89_dev *rtwdev, u8 mac_idx)
+{
+ u32 val, reg;
+ int ret;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL);
+ if (ret)
+ return ret;
+
+ reg = rtw89_mac_reg_by_idx(R_AX_TX_SUB_CARRIER_VALUE, mac_idx);
+ val = rtw89_read32(rtwdev, reg);
+ val = u32_replace_bits(val, 0, B_AX_TXSC_20M_MASK);
+ val = u32_replace_bits(val, 0, B_AX_TXSC_40M_MASK);
+ val = u32_replace_bits(val, 0, B_AX_TXSC_80M_MASK);
+ rtw89_write32(rtwdev, reg, val);
+
+ return 0;
+}
+
+static bool is_qta_dbcc(struct rtw89_dev *rtwdev, enum rtw89_qta_mode mode)
+{
+ struct rtw89_dle_mem *cfg;
+
+ cfg = get_dle_mem_cfg(rtwdev, mode);
+ if (!cfg) {
+ rtw89_err(rtwdev, "[ERR]get_dle_mem_cfg\n");
+ return false;
+ }
+
+ return (cfg->ple_min_qt->cma1_dma && cfg->ple_max_qt->cma1_dma);
+}
+
+static bool is_qta_poh(struct rtw89_dev *rtwdev, enum rtw89_qta_mode mode)
+{
+ return !(mode == RTW89_QTA_SCC_STF || mode == RTW89_QTA_DBCC_STF);
+}
+
+static int ptcl_init(struct rtw89_dev *rtwdev, u8 mac_idx)
+{
+ u32 val, reg;
+ int ret;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL);
+ if (ret)
+ return ret;
+
+ if (rtwdev->hci.type != RTW89_HCI_TYPE_PCIE)
+ goto end;
+
+ if (is_qta_poh(rtwdev, rtwdev->mac.qta_mode)) {
+ reg = rtw89_mac_reg_by_idx(R_AX_SIFS_SETTING, mac_idx);
+ val = rtw89_read32(rtwdev, reg);
+ val = u32_replace_bits(val, S_AX_CTS2S_TH_1K,
+ B_AX_HW_CTS2SELF_PKT_LEN_TH_MASK);
+ val |= B_AX_HW_CTS2SELF_EN;
+ rtw89_write32(rtwdev, reg, val);
+
+ reg = rtw89_mac_reg_by_idx(R_AX_PTCL_FSM_MON, mac_idx);
+ val = rtw89_read32(rtwdev, reg);
+ val = u32_replace_bits(val, S_AX_PTCL_TO_2MS, B_AX_PTCL_TX_ARB_TO_THR_MASK);
+ val &= ~B_AX_PTCL_TX_ARB_TO_MODE;
+ rtw89_write32(rtwdev, reg, val);
+ }
+
+end:
+ reg = rtw89_mac_reg_by_idx(R_AX_SIFS_SETTING, mac_idx);
+ val = rtw89_read32(rtwdev, reg);
+ val = u32_replace_bits(val, S_AX_CTS2S_TH_SEC_256B, B_AX_HW_CTS2SELF_PKT_LEN_TH_TWW_MASK);
+ val |= B_AX_HW_CTS2SELF_EN;
+ rtw89_write32(rtwdev, reg, val);
+
+ return 0;
+}
+
+static int cmac_init(struct rtw89_dev *rtwdev, u8 mac_idx)
+{
+ int ret;
+
+ ret = scheduler_init(rtwdev, mac_idx);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]CMAC%d SCH init %d\n", mac_idx, ret);
+ return ret;
+ }
+
+ ret = addr_cam_init(rtwdev, mac_idx);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]CMAC%d ADDR_CAM reset %d\n", mac_idx,
+ ret);
+ return ret;
+ }
+
+ ret = rx_fltr_init(rtwdev, mac_idx);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]CMAC%d RX filter init %d\n", mac_idx,
+ ret);
+ return ret;
+ }
+
+ ret = cca_ctrl_init(rtwdev, mac_idx);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]CMAC%d CCA CTRL init %d\n", mac_idx,
+ ret);
+ return ret;
+ }
+
+ ret = spatial_reuse_init(rtwdev, mac_idx);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]CMAC%d Spatial Reuse init %d\n",
+ mac_idx, ret);
+ return ret;
+ }
+
+ ret = tmac_init(rtwdev, mac_idx);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]CMAC%d TMAC init %d\n", mac_idx, ret);
+ return ret;
+ }
+
+ ret = trxptcl_init(rtwdev, mac_idx);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]CMAC%d TRXPTCL init %d\n", mac_idx, ret);
+ return ret;
+ }
+
+ ret = rmac_init(rtwdev, mac_idx);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]CMAC%d RMAC init %d\n", mac_idx, ret);
+ return ret;
+ }
+
+ ret = cmac_com_init(rtwdev, mac_idx);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]CMAC%d Com init %d\n", mac_idx, ret);
+ return ret;
+ }
+
+ ret = ptcl_init(rtwdev, mac_idx);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]CMAC%d PTCL init %d\n", mac_idx, ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int rtw89_hw_sch_tx_en_h2c(struct rtw89_dev *rtwdev, u8 band,
+ u16 tx_en_u16, u16 mask_u16)
+{
+ u32 ret;
+ struct rtw89_mac_c2h_info c2h_info = {0};
+ struct rtw89_mac_h2c_info h2c_info = {0};
+ struct rtw89_h2creg_sch_tx_en *h2creg =
+ (struct rtw89_h2creg_sch_tx_en *)h2c_info.h2creg;
+
+ h2c_info.id = RTW89_FWCMD_H2CREG_FUNC_SCH_TX_EN;
+ h2c_info.content_len = sizeof(*h2creg) - RTW89_H2CREG_HDR_LEN;
+ h2creg->tx_en = tx_en_u16;
+ h2creg->mask = mask_u16;
+ h2creg->band = band;
+
+ ret = rtw89_fw_msg_reg(rtwdev, &h2c_info, &c2h_info);
+ if (ret)
+ return ret;
+
+ if (c2h_info.id != RTW89_FWCMD_C2HREG_FUNC_TX_PAUSE_RPT)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int rtw89_set_hw_sch_tx_en(struct rtw89_dev *rtwdev, u8 mac_idx,
+ u16 tx_en, u16 tx_en_mask)
+{
+ u32 reg = rtw89_mac_reg_by_idx(R_AX_CTN_TXEN, mac_idx);
+ u16 val;
+ int ret;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL);
+ if (ret)
+ return ret;
+
+ if (test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags))
+ return rtw89_hw_sch_tx_en_h2c(rtwdev, mac_idx,
+ tx_en, tx_en_mask);
+
+ val = rtw89_read16(rtwdev, reg);
+ val = (val & ~tx_en_mask) | (tx_en & tx_en_mask);
+ rtw89_write16(rtwdev, reg, val);
+
+ return 0;
+}
+
+int rtw89_mac_stop_sch_tx(struct rtw89_dev *rtwdev, u8 mac_idx,
+ u16 *tx_en, enum rtw89_sch_tx_sel sel)
+{
+ int ret;
+
+ *tx_en = rtw89_read16(rtwdev,
+ rtw89_mac_reg_by_idx(R_AX_CTN_TXEN, mac_idx));
+
+ switch (sel) {
+ case RTW89_SCH_TX_SEL_ALL:
+ ret = rtw89_set_hw_sch_tx_en(rtwdev, mac_idx, 0, 0xffff);
+ if (ret)
+ return ret;
+ break;
+ case RTW89_SCH_TX_SEL_HIQ:
+ ret = rtw89_set_hw_sch_tx_en(rtwdev, mac_idx,
+ 0, B_AX_CTN_TXEN_HGQ);
+ if (ret)
+ return ret;
+ break;
+ case RTW89_SCH_TX_SEL_MG0:
+ ret = rtw89_set_hw_sch_tx_en(rtwdev, mac_idx,
+ 0, B_AX_CTN_TXEN_MGQ);
+ if (ret)
+ return ret;
+ break;
+ case RTW89_SCH_TX_SEL_MACID:
+ ret = rtw89_set_hw_sch_tx_en(rtwdev, mac_idx, 0, 0xffff);
+ if (ret)
+ return ret;
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+int rtw89_mac_resume_sch_tx(struct rtw89_dev *rtwdev, u8 mac_idx, u16 tx_en)
+{
+ int ret;
+
+ ret = rtw89_set_hw_sch_tx_en(rtwdev, mac_idx, tx_en, 0xffff);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static u16 rtw89_mac_dle_buf_req(struct rtw89_dev *rtwdev, u16 buf_len,
+ bool wd)
+{
+ u32 val, reg;
+ int ret;
+
+ reg = wd ? R_AX_WD_BUF_REQ : R_AX_PL_BUF_REQ;
+ val = buf_len;
+ val |= B_AX_BUF_REQ_EXEC;
+ rtw89_write32(rtwdev, reg, val);
+
+ reg = wd ? R_AX_WD_BUF_STATUS : R_AX_PL_BUF_STATUS;
+
+ ret = read_poll_timeout(rtw89_read32, val, val & B_AX_BUF_STAT_DONE,
+ 1, 2000, false, rtwdev, reg);
+ if (ret)
+ return 0xffff;
+
+ return FIELD_GET(B_AX_BUF_STAT_PKTID_MASK, val);
+}
+
+static int rtw89_mac_set_cpuio(struct rtw89_dev *rtwdev,
+ struct rtw89_cpuio_ctrl *ctrl_para,
+ bool wd)
+{
+ u32 val, cmd_type, reg;
+ int ret;
+
+ cmd_type = ctrl_para->cmd_type;
+
+ reg = wd ? R_AX_WD_CPUQ_OP_2 : R_AX_PL_CPUQ_OP_2;
+ val = 0;
+ val = u32_replace_bits(val, ctrl_para->start_pktid,
+ B_AX_CPUQ_OP_STRT_PKTID_MASK);
+ val = u32_replace_bits(val, ctrl_para->end_pktid,
+ B_AX_CPUQ_OP_END_PKTID_MASK);
+ rtw89_write32(rtwdev, reg, val);
+
+ reg = wd ? R_AX_WD_CPUQ_OP_1 : R_AX_PL_CPUQ_OP_1;
+ val = 0;
+ val = u32_replace_bits(val, ctrl_para->src_pid,
+ B_AX_CPUQ_OP_SRC_PID_MASK);
+ val = u32_replace_bits(val, ctrl_para->src_qid,
+ B_AX_CPUQ_OP_SRC_QID_MASK);
+ val = u32_replace_bits(val, ctrl_para->dst_pid,
+ B_AX_CPUQ_OP_DST_PID_MASK);
+ val = u32_replace_bits(val, ctrl_para->dst_qid,
+ B_AX_CPUQ_OP_DST_QID_MASK);
+ rtw89_write32(rtwdev, reg, val);
+
+ reg = wd ? R_AX_WD_CPUQ_OP_0 : R_AX_PL_CPUQ_OP_0;
+ val = 0;
+ val = u32_replace_bits(val, cmd_type,
+ B_AX_CPUQ_OP_CMD_TYPE_MASK);
+ val = u32_replace_bits(val, ctrl_para->macid,
+ B_AX_CPUQ_OP_MACID_MASK);
+ val = u32_replace_bits(val, ctrl_para->pkt_num,
+ B_AX_CPUQ_OP_PKTNUM_MASK);
+ val |= B_AX_CPUQ_OP_EXEC;
+ rtw89_write32(rtwdev, reg, val);
+
+ reg = wd ? R_AX_WD_CPUQ_OP_STATUS : R_AX_PL_CPUQ_OP_STATUS;
+
+ ret = read_poll_timeout(rtw89_read32, val, val & B_AX_CPUQ_OP_STAT_DONE,
+ 1, 2000, false, rtwdev, reg);
+ if (ret)
+ return ret;
+
+ if (cmd_type == CPUIO_OP_CMD_GET_1ST_PID ||
+ cmd_type == CPUIO_OP_CMD_GET_NEXT_PID)
+ ctrl_para->pktid = FIELD_GET(B_AX_CPUQ_OP_PKTID_MASK, val);
+
+ return 0;
+}
+
+static int dle_quota_change(struct rtw89_dev *rtwdev, enum rtw89_qta_mode mode)
+{
+ struct rtw89_dle_mem *cfg;
+ struct rtw89_cpuio_ctrl ctrl_para = {0};
+ u16 pkt_id;
+ int ret;
+
+ cfg = get_dle_mem_cfg(rtwdev, mode);
+ if (!cfg) {
+ rtw89_err(rtwdev, "[ERR]wd/dle mem cfg\n");
+ return -EINVAL;
+ }
+
+ if (dle_used_size(cfg->wde_size, cfg->ple_size) !=
+ (rtwdev->chip->fifo_size - dle_rsvd_size(rtwdev, mode))) {
+ rtw89_err(rtwdev, "[ERR]wd/dle mem cfg\n");
+ return -EINVAL;
+ }
+
+ dle_quota_cfg(rtwdev, cfg);
+
+ pkt_id = rtw89_mac_dle_buf_req(rtwdev, 0x20, true);
+ if (pkt_id == 0xffff) {
+ rtw89_err(rtwdev, "[ERR]WDE DLE buf req\n");
+ return -ENOMEM;
+ }
+
+ ctrl_para.cmd_type = CPUIO_OP_CMD_ENQ_TO_HEAD;
+ ctrl_para.start_pktid = pkt_id;
+ ctrl_para.end_pktid = pkt_id;
+ ctrl_para.pkt_num = 0;
+ ctrl_para.dst_pid = WDE_DLE_PORT_ID_WDRLS;
+ ctrl_para.dst_qid = WDE_DLE_QUEID_NO_REPORT;
+ ret = rtw89_mac_set_cpuio(rtwdev, &ctrl_para, true);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]WDE DLE enqueue to head\n");
+ return -EFAULT;
+ }
+
+ pkt_id = rtw89_mac_dle_buf_req(rtwdev, 0x20, false);
+ if (pkt_id == 0xffff) {
+ rtw89_err(rtwdev, "[ERR]PLE DLE buf req\n");
+ return -ENOMEM;
+ }
+
+ ctrl_para.cmd_type = CPUIO_OP_CMD_ENQ_TO_HEAD;
+ ctrl_para.start_pktid = pkt_id;
+ ctrl_para.end_pktid = pkt_id;
+ ctrl_para.pkt_num = 0;
+ ctrl_para.dst_pid = PLE_DLE_PORT_ID_PLRLS;
+ ctrl_para.dst_qid = PLE_DLE_QUEID_NO_REPORT;
+ ret = rtw89_mac_set_cpuio(rtwdev, &ctrl_para, false);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]PLE DLE enqueue to head\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int band_idle_ck_b(struct rtw89_dev *rtwdev, u8 mac_idx)
+{
+ int ret;
+ u32 reg;
+ u8 val;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL);
+ if (ret)
+ return ret;
+
+ reg = rtw89_mac_reg_by_idx(R_AX_PTCL_TX_CTN_SEL, mac_idx);
+
+ ret = read_poll_timeout(rtw89_read8, val,
+ (val & B_AX_PTCL_TX_ON_STAT) == 0,
+ SW_CVR_DUR_US,
+ SW_CVR_DUR_US * PTCL_IDLE_POLL_CNT,
+ false, rtwdev, reg);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int band1_enable(struct rtw89_dev *rtwdev)
+{
+ int ret, i;
+ u32 sleep_bak[4] = {0};
+ u32 pause_bak[4] = {0};
+ u16 tx_en;
+
+ ret = rtw89_mac_stop_sch_tx(rtwdev, 0, &tx_en, RTW89_SCH_TX_SEL_ALL);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]stop sch tx %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < 4; i++) {
+ sleep_bak[i] = rtw89_read32(rtwdev, R_AX_MACID_SLEEP_0 + i * 4);
+ pause_bak[i] = rtw89_read32(rtwdev, R_AX_SS_MACID_PAUSE_0 + i * 4);
+ rtw89_write32(rtwdev, R_AX_MACID_SLEEP_0 + i * 4, U32_MAX);
+ rtw89_write32(rtwdev, R_AX_SS_MACID_PAUSE_0 + i * 4, U32_MAX);
+ }
+
+ ret = band_idle_ck_b(rtwdev, 0);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]tx idle poll %d\n", ret);
+ return ret;
+ }
+
+ ret = dle_quota_change(rtwdev, rtwdev->mac.qta_mode);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]DLE quota change %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < 4; i++) {
+ rtw89_write32(rtwdev, R_AX_MACID_SLEEP_0 + i * 4, sleep_bak[i]);
+ rtw89_write32(rtwdev, R_AX_SS_MACID_PAUSE_0 + i * 4, pause_bak[i]);
+ }
+
+ ret = rtw89_mac_resume_sch_tx(rtwdev, 0, tx_en);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]CMAC1 resume sch tx %d\n", ret);
+ return ret;
+ }
+
+ ret = cmac_func_en(rtwdev, 1, true);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]CMAC1 func en %d\n", ret);
+ return ret;
+ }
+
+ ret = cmac_init(rtwdev, 1);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]CMAC1 init %d\n", ret);
+ return ret;
+ }
+
+ rtw89_write32_set(rtwdev, R_AX_SYS_ISO_CTRL_EXTEND,
+ B_AX_R_SYM_FEN_WLPHYFUN_1 | B_AX_R_SYM_FEN_WLPHYGLB_1);
+
+ return 0;
+}
+
+static int rtw89_mac_enable_imr(struct rtw89_dev *rtwdev, u8 mac_idx,
+ enum rtw89_mac_hwmod_sel sel)
+{
+ u32 reg, val;
+ int ret;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, sel);
+ if (ret) {
+ rtw89_err(rtwdev, "MAC%d mac_idx%d is not ready\n",
+ sel, mac_idx);
+ return ret;
+ }
+
+ if (sel == RTW89_DMAC_SEL) {
+ rtw89_write32_clr(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR,
+ B_AX_TXPKTCTL_USRCTL_RLSBMPLEN_ERR_INT_EN |
+ B_AX_TXPKTCTL_USRCTL_RDNRLSCMD_ERR_INT_EN |
+ B_AX_TXPKTCTL_CMDPSR_FRZTO_ERR_INT_EN);
+ rtw89_write32_clr(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR_B1,
+ B_AX_TXPKTCTL_USRCTL_RLSBMPLEN_ERR_INT_EN |
+ B_AX_TXPKTCTL_USRCTL_RDNRLSCMD_ERR_INT_EN);
+ rtw89_write32_clr(rtwdev, R_AX_HOST_DISPATCHER_ERR_IMR,
+ B_AX_HDT_PKT_FAIL_DBG_INT_EN |
+ B_AX_HDT_OFFSET_UNMATCH_INT_EN);
+ rtw89_write32_clr(rtwdev, R_AX_CPU_DISPATCHER_ERR_IMR,
+ B_AX_CPU_SHIFT_EN_ERR_INT_EN);
+ rtw89_write32_clr(rtwdev, R_AX_PLE_ERR_IMR,
+ B_AX_PLE_GETNPG_STRPG_ERR_INT_EN);
+ rtw89_write32_clr(rtwdev, R_AX_WDRLS_ERR_IMR,
+ B_AX_WDRLS_PLEBREQ_TO_ERR_INT_EN);
+ rtw89_write32_set(rtwdev, R_AX_HD0IMR, B_AX_WDT_PTFM_INT_EN);
+ rtw89_write32_clr(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR,
+ B_AX_TXPKTCTL_USRCTL_NOINIT_ERR_INT_EN);
+ } else if (sel == RTW89_CMAC_SEL) {
+ reg = rtw89_mac_reg_by_idx(R_AX_SCHEDULE_ERR_IMR, mac_idx);
+ rtw89_write32_clr(rtwdev, reg,
+ B_AX_SORT_NON_IDLE_ERR_INT_EN);
+
+ reg = rtw89_mac_reg_by_idx(R_AX_DLE_CTRL, mac_idx);
+ rtw89_write32_clr(rtwdev, reg,
+ B_AX_NO_RESERVE_PAGE_ERR_IMR |
+ B_AX_RXDATA_FSM_HANG_ERROR_IMR);
+
+ reg = rtw89_mac_reg_by_idx(R_AX_PTCL_IMR0, mac_idx);
+ val = B_AX_F2PCMD_USER_ALLC_ERR_INT_EN |
+ B_AX_TX_RECORD_PKTID_ERR_INT_EN |
+ B_AX_FSM_TIMEOUT_ERR_INT_EN;
+ rtw89_write32(rtwdev, reg, val);
+
+ reg = rtw89_mac_reg_by_idx(R_AX_PHYINFO_ERR_IMR, mac_idx);
+ rtw89_write16_set(rtwdev, reg,
+ B_AXC_PHY_TXON_TIMEOUT_INT_EN |
+ B_AX_CCK_CCA_TIMEOUT_INT_EN |
+ B_AX_OFDM_CCA_TIMEOUT_INT_EN |
+ B_AX_DATA_ON_TIMEOUT_INT_EN |
+ B_AX_STS_ON_TIMEOUT_INT_EN |
+ B_AX_CSI_ON_TIMEOUT_INT_EN);
+
+ reg = rtw89_mac_reg_by_idx(R_AX_RMAC_ERR_ISR, mac_idx);
+ val = rtw89_read32(rtwdev, reg);
+ val |= (B_AX_RMAC_RX_CSI_TIMEOUT_INT_EN |
+ B_AX_RMAC_RX_TIMEOUT_INT_EN |
+ B_AX_RMAC_CSI_TIMEOUT_INT_EN);
+ val &= ~(B_AX_RMAC_CCA_TO_IDLE_TIMEOUT_INT_EN |
+ B_AX_RMAC_DATA_ON_TO_IDLE_TIMEOUT_INT_EN |
+ B_AX_RMAC_CCA_TIMEOUT_INT_EN |
+ B_AX_RMAC_DATA_ON_TIMEOUT_INT_EN);
+ rtw89_write32(rtwdev, reg, val);
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rtw89_mac_dbcc_enable(struct rtw89_dev *rtwdev, bool enable)
+{
+ int ret = 0;
+
+ if (enable) {
+ ret = band1_enable(rtwdev);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR] band1_enable %d\n", ret);
+ return ret;
+ }
+
+ ret = rtw89_mac_enable_imr(rtwdev, RTW89_MAC_1, RTW89_CMAC_SEL);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR] enable CMAC1 IMR %d\n", ret);
+ return ret;
+ }
+ } else {
+ rtw89_err(rtwdev, "[ERR] disable dbcc is not implemented not\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int set_host_rpr(struct rtw89_dev *rtwdev)
+{
+ if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE &&
+ is_qta_poh(rtwdev, rtwdev->mac.qta_mode)) {
+ rtw89_write32_mask(rtwdev, R_AX_WDRLS_CFG,
+ B_AX_WDRLS_MODE_MASK, RTW89_RPR_MODE_POH);
+ rtw89_write32_set(rtwdev, R_AX_RLSRPT0_CFG0,
+ B_AX_RLSRPT0_FLTR_MAP_MASK);
+ } else {
+ rtw89_write32_mask(rtwdev, R_AX_WDRLS_CFG,
+ B_AX_WDRLS_MODE_MASK, RTW89_RPR_MODE_STF);
+ rtw89_write32_clr(rtwdev, R_AX_RLSRPT0_CFG0,
+ B_AX_RLSRPT0_FLTR_MAP_MASK);
+ }
+
+ rtw89_write32_mask(rtwdev, R_AX_RLSRPT0_CFG1, B_AX_RLSRPT0_AGGNUM_MASK, 30);
+ rtw89_write32_mask(rtwdev, R_AX_RLSRPT0_CFG1, B_AX_RLSRPT0_TO_MASK, 255);
+
+ return 0;
+}
+
+static int rtw89_mac_trx_init(struct rtw89_dev *rtwdev)
+{
+ enum rtw89_qta_mode qta_mode = rtwdev->mac.qta_mode;
+ int ret;
+
+ ret = dmac_init(rtwdev, 0);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]DMAC init %d\n", ret);
+ return ret;
+ }
+
+ ret = cmac_init(rtwdev, 0);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]CMAC%d init %d\n", 0, ret);
+ return ret;
+ }
+
+ if (is_qta_dbcc(rtwdev, qta_mode)) {
+ ret = rtw89_mac_dbcc_enable(rtwdev, true);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]dbcc_enable init %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = rtw89_mac_enable_imr(rtwdev, RTW89_MAC_0, RTW89_DMAC_SEL);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR] enable DMAC IMR %d\n", ret);
+ return ret;
+ }
+
+ ret = rtw89_mac_enable_imr(rtwdev, RTW89_MAC_0, RTW89_CMAC_SEL);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR] to enable CMAC0 IMR %d\n", ret);
+ return ret;
+ }
+
+ ret = set_host_rpr(rtwdev);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR] set host rpr %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void rtw89_mac_disable_cpu(struct rtw89_dev *rtwdev)
+{
+ clear_bit(RTW89_FLAG_FW_RDY, rtwdev->flags);
+
+ rtw89_write32_clr(rtwdev, R_AX_PLATFORM_ENABLE, B_AX_WCPU_EN);
+ rtw89_write32_clr(rtwdev, R_AX_SYS_CLK_CTRL, B_AX_CPU_CLK_EN);
+}
+
+static int rtw89_mac_enable_cpu(struct rtw89_dev *rtwdev, u8 boot_reason,
+ bool dlfw)
+{
+ u32 val;
+ int ret;
+
+ if (rtw89_read32(rtwdev, R_AX_PLATFORM_ENABLE) & B_AX_WCPU_EN)
+ return -EFAULT;
+
+ rtw89_write32(rtwdev, R_AX_HALT_H2C_CTRL, 0);
+ rtw89_write32(rtwdev, R_AX_HALT_C2H_CTRL, 0);
+
+ rtw89_write32_set(rtwdev, R_AX_SYS_CLK_CTRL, B_AX_CPU_CLK_EN);
+
+ val = rtw89_read32(rtwdev, R_AX_WCPU_FW_CTRL);
+ val &= ~(B_AX_WCPU_FWDL_EN | B_AX_H2C_PATH_RDY | B_AX_FWDL_PATH_RDY);
+ val = u32_replace_bits(val, RTW89_FWDL_INITIAL_STATE,
+ B_AX_WCPU_FWDL_STS_MASK);
+
+ if (dlfw)
+ val |= B_AX_WCPU_FWDL_EN;
+
+ rtw89_write32(rtwdev, R_AX_WCPU_FW_CTRL, val);
+ rtw89_write16_mask(rtwdev, R_AX_BOOT_REASON, B_AX_BOOT_REASON_MASK,
+ boot_reason);
+ rtw89_write32_set(rtwdev, R_AX_PLATFORM_ENABLE, B_AX_WCPU_EN);
+
+ if (!dlfw) {
+ mdelay(5);
+
+ ret = rtw89_fw_check_rdy(rtwdev);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rtw89_mac_fw_dl_pre_init(struct rtw89_dev *rtwdev)
+{
+ u32 val;
+ int ret;
+
+ val = B_AX_MAC_FUNC_EN | B_AX_DMAC_FUNC_EN | B_AX_DISPATCHER_EN |
+ B_AX_PKT_BUF_EN;
+ rtw89_write32(rtwdev, R_AX_DMAC_FUNC_EN, val);
+
+ val = B_AX_DISPATCHER_CLK_EN;
+ rtw89_write32(rtwdev, R_AX_DMAC_CLK_EN, val);
+
+ ret = dle_init(rtwdev, RTW89_QTA_DLFW, rtwdev->mac.qta_mode);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]DLE pre init %d\n", ret);
+ return ret;
+ }
+
+ ret = hfc_init(rtwdev, true, false, true);
+ if (ret) {
+ rtw89_err(rtwdev, "[ERR]HCI FC pre init %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static void rtw89_mac_hci_func_en(struct rtw89_dev *rtwdev)
+{
+ rtw89_write32_set(rtwdev, R_AX_HCI_FUNC_EN,
+ B_AX_HCI_TXDMA_EN | B_AX_HCI_RXDMA_EN);
+}
+
+void rtw89_mac_enable_bb_rf(struct rtw89_dev *rtwdev)
+{
+ rtw89_write8_set(rtwdev, R_AX_SYS_FUNC_EN,
+ B_AX_FEN_BBRSTB | B_AX_FEN_BB_GLB_RSTN);
+ rtw89_write32_set(rtwdev, R_AX_WLRF_CTRL,
+ B_AX_WLRF1_CTRL_7 | B_AX_WLRF1_CTRL_1 |
+ B_AX_WLRF_CTRL_7 | B_AX_WLRF_CTRL_1);
+ rtw89_write8_set(rtwdev, R_AX_PHYREG_SET, PHYREG_SET_ALL_CYCLE);
+}
+
+void rtw89_mac_disable_bb_rf(struct rtw89_dev *rtwdev)
+{
+ rtw89_write8_clr(rtwdev, R_AX_SYS_FUNC_EN,
+ B_AX_FEN_BBRSTB | B_AX_FEN_BB_GLB_RSTN);
+ rtw89_write32_clr(rtwdev, R_AX_WLRF_CTRL,
+ B_AX_WLRF1_CTRL_7 | B_AX_WLRF1_CTRL_1 |
+ B_AX_WLRF_CTRL_7 | B_AX_WLRF_CTRL_1);
+ rtw89_write8_clr(rtwdev, R_AX_PHYREG_SET, PHYREG_SET_ALL_CYCLE);
+}
+
+int rtw89_mac_partial_init(struct rtw89_dev *rtwdev)
+{
+ int ret;
+
+ ret = rtw89_mac_power_switch(rtwdev, true);
+ if (ret) {
+ rtw89_mac_power_switch(rtwdev, false);
+ ret = rtw89_mac_power_switch(rtwdev, true);
+ if (ret)
+ return ret;
+ }
+
+ rtw89_mac_hci_func_en(rtwdev);
+
+ if (rtwdev->hci.ops->mac_pre_init) {
+ ret = rtwdev->hci.ops->mac_pre_init(rtwdev);
+ if (ret)
+ return ret;
+ }
+
+ ret = rtw89_mac_fw_dl_pre_init(rtwdev);
+ if (ret)
+ return ret;
+
+ rtw89_mac_disable_cpu(rtwdev);
+ ret = rtw89_mac_enable_cpu(rtwdev, 0, true);
+ if (ret)
+ return ret;
+
+ ret = rtw89_fw_download(rtwdev, RTW89_FW_NORMAL);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int rtw89_mac_init(struct rtw89_dev *rtwdev)
+{
+ int ret;
+
+ ret = rtw89_mac_partial_init(rtwdev);
+ if (ret)
+ goto fail;
+
+ rtw89_mac_enable_bb_rf(rtwdev);
+ if (ret)
+ goto fail;
+
+ ret = rtw89_mac_sys_init(rtwdev);
+ if (ret)
+ goto fail;
+
+ ret = rtw89_mac_trx_init(rtwdev);
+ if (ret)
+ goto fail;
+
+ if (rtwdev->hci.ops->mac_post_init) {
+ ret = rtwdev->hci.ops->mac_post_init(rtwdev);
+ if (ret)
+ goto fail;
+ }
+
+ rtw89_fw_h2c_set_ofld_cfg(rtwdev);
+
+ return ret;
+fail:
+ rtw89_mac_power_switch(rtwdev, false);
+
+ return ret;
+}
+
+static void rtw89_mac_dmac_tbl_init(struct rtw89_dev *rtwdev, u8 macid)
+{
+ u8 i;
+
+ for (i = 0; i < 4; i++) {
+ rtw89_write32(rtwdev, R_AX_FILTER_MODEL_ADDR,
+ DMAC_TBL_BASE_ADDR + (macid << 4) + (i << 2));
+ rtw89_write32(rtwdev, R_AX_INDIR_ACCESS_ENTRY, 0);
+ }
+}
+
+static void rtw89_mac_cmac_tbl_init(struct rtw89_dev *rtwdev, u8 macid)
+{
+ rtw89_write32(rtwdev, R_AX_FILTER_MODEL_ADDR,
+ CMAC_TBL_BASE_ADDR + macid * CCTL_INFO_SIZE);
+ rtw89_write32(rtwdev, R_AX_INDIR_ACCESS_ENTRY, 0x4);
+ rtw89_write32(rtwdev, R_AX_INDIR_ACCESS_ENTRY + 4, 0x400A0004);
+ rtw89_write32(rtwdev, R_AX_INDIR_ACCESS_ENTRY + 8, 0);
+ rtw89_write32(rtwdev, R_AX_INDIR_ACCESS_ENTRY + 12, 0);
+ rtw89_write32(rtwdev, R_AX_INDIR_ACCESS_ENTRY + 16, 0);
+ rtw89_write32(rtwdev, R_AX_INDIR_ACCESS_ENTRY + 20, 0xE43000B);
+ rtw89_write32(rtwdev, R_AX_INDIR_ACCESS_ENTRY + 24, 0);
+ rtw89_write32(rtwdev, R_AX_INDIR_ACCESS_ENTRY + 28, 0xB8109);
+}
+
+static int rtw89_set_macid_pause(struct rtw89_dev *rtwdev, u8 macid, bool pause)
+{
+ u8 sh = FIELD_GET(GENMASK(4, 0), macid);
+ u8 grp = macid >> 5;
+ int ret;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, RTW89_MAC_0, RTW89_CMAC_SEL);
+ if (ret)
+ return ret;
+
+ rtw89_fw_h2c_macid_pause(rtwdev, sh, grp, pause);
+
+ return 0;
+}
+
+static const struct rtw89_port_reg rtw_port_base = {
+ .port_cfg = R_AX_PORT_CFG_P0,
+ .tbtt_prohib = R_AX_TBTT_PROHIB_P0,
+ .bcn_area = R_AX_BCN_AREA_P0,
+ .bcn_early = R_AX_BCNERLYINT_CFG_P0,
+ .tbtt_early = R_AX_TBTTERLYINT_CFG_P0,
+ .tbtt_agg = R_AX_TBTT_AGG_P0,
+ .bcn_space = R_AX_BCN_SPACE_CFG_P0,
+ .bcn_forcetx = R_AX_BCN_FORCETX_P0,
+ .bcn_err_cnt = R_AX_BCN_ERR_CNT_P0,
+ .bcn_err_flag = R_AX_BCN_ERR_FLAG_P0,
+ .dtim_ctrl = R_AX_DTIM_CTRL_P0,
+ .tbtt_shift = R_AX_TBTT_SHIFT_P0,
+ .bcn_cnt_tmr = R_AX_BCN_CNT_TMR_P0,
+ .tsftr_l = R_AX_TSFTR_LOW_P0,
+ .tsftr_h = R_AX_TSFTR_HIGH_P0
+};
+
+#define BCN_INTERVAL 100
+#define BCN_ERLY_DEF 160
+#define BCN_SETUP_DEF 2
+#define BCN_HOLD_DEF 200
+#define BCN_MASK_DEF 0
+#define TBTT_ERLY_DEF 5
+#define BCN_SET_UNIT 32
+#define BCN_ERLY_SET_DLY (10 * 2)
+
+static void rtw89_mac_port_cfg_func_sw(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif)
+{
+ struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif);
+ const struct rtw89_port_reg *p = &rtw_port_base;
+
+ if (!rtw89_read32_port_mask(rtwdev, rtwvif, p->port_cfg, B_AX_PORT_FUNC_EN))
+ return;
+
+ rtw89_write32_port_clr(rtwdev, rtwvif, p->tbtt_prohib, B_AX_TBTT_SETUP_MASK);
+ rtw89_write32_port_mask(rtwdev, rtwvif, p->tbtt_prohib, B_AX_TBTT_HOLD_MASK, 1);
+ rtw89_write16_port_clr(rtwdev, rtwvif, p->tbtt_early, B_AX_TBTTERLY_MASK);
+ rtw89_write16_port_clr(rtwdev, rtwvif, p->bcn_early, B_AX_BCNERLY_MASK);
+
+ msleep(vif->bss_conf.beacon_int + 1);
+
+ rtw89_write32_port_clr(rtwdev, rtwvif, p->port_cfg, B_AX_PORT_FUNC_EN |
+ B_AX_BRK_SETUP);
+ rtw89_write32_port_set(rtwdev, rtwvif, p->port_cfg, B_AX_TSFTR_RST);
+ rtw89_write32_port(rtwdev, rtwvif, p->bcn_cnt_tmr, 0);
+}
+
+static void rtw89_mac_port_cfg_tx_rpt(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif, bool en)
+{
+ const struct rtw89_port_reg *p = &rtw_port_base;
+
+ if (en)
+ rtw89_write32_port_set(rtwdev, rtwvif, p->port_cfg, B_AX_TXBCN_RPT_EN);
+ else
+ rtw89_write32_port_clr(rtwdev, rtwvif, p->port_cfg, B_AX_TXBCN_RPT_EN);
+}
+
+static void rtw89_mac_port_cfg_rx_rpt(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif, bool en)
+{
+ const struct rtw89_port_reg *p = &rtw_port_base;
+
+ if (en)
+ rtw89_write32_port_set(rtwdev, rtwvif, p->port_cfg, B_AX_RXBCN_RPT_EN);
+ else
+ rtw89_write32_port_clr(rtwdev, rtwvif, p->port_cfg, B_AX_RXBCN_RPT_EN);
+}
+
+static void rtw89_mac_port_cfg_net_type(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif)
+{
+ const struct rtw89_port_reg *p = &rtw_port_base;
+
+ rtw89_write32_port_mask(rtwdev, rtwvif, p->port_cfg, B_AX_NET_TYPE_MASK,
+ rtwvif->net_type);
+}
+
+static void rtw89_mac_port_cfg_bcn_prct(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif)
+{
+ const struct rtw89_port_reg *p = &rtw_port_base;
+ bool en = rtwvif->net_type != RTW89_NET_TYPE_NO_LINK;
+ u32 bits = B_AX_TBTT_PROHIB_EN | B_AX_BRK_SETUP;
+
+ if (en)
+ rtw89_write32_port_set(rtwdev, rtwvif, p->port_cfg, bits);
+ else
+ rtw89_write32_port_clr(rtwdev, rtwvif, p->port_cfg, bits);
+}
+
+static void rtw89_mac_port_cfg_rx_sw(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif)
+{
+ const struct rtw89_port_reg *p = &rtw_port_base;
+ bool en = rtwvif->net_type == RTW89_NET_TYPE_INFRA ||
+ rtwvif->net_type == RTW89_NET_TYPE_AD_HOC;
+ u32 bit = B_AX_RX_BSSID_FIT_EN;
+
+ if (en)
+ rtw89_write32_port_set(rtwdev, rtwvif, p->port_cfg, bit);
+ else
+ rtw89_write32_port_clr(rtwdev, rtwvif, p->port_cfg, bit);
+}
+
+static void rtw89_mac_port_cfg_rx_sync(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif)
+{
+ const struct rtw89_port_reg *p = &rtw_port_base;
+ bool en = rtwvif->net_type == RTW89_NET_TYPE_INFRA ||
+ rtwvif->net_type == RTW89_NET_TYPE_AD_HOC;
+
+ if (en)
+ rtw89_write32_port_set(rtwdev, rtwvif, p->port_cfg, B_AX_TSF_UDT_EN);
+ else
+ rtw89_write32_port_clr(rtwdev, rtwvif, p->port_cfg, B_AX_TSF_UDT_EN);
+}
+
+static void rtw89_mac_port_cfg_tx_sw(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif)
+{
+ const struct rtw89_port_reg *p = &rtw_port_base;
+ bool en = rtwvif->net_type == RTW89_NET_TYPE_AP_MODE ||
+ rtwvif->net_type == RTW89_NET_TYPE_AD_HOC;
+
+ if (en)
+ rtw89_write32_port_set(rtwdev, rtwvif, p->port_cfg, B_AX_BCNTX_EN);
+ else
+ rtw89_write32_port_clr(rtwdev, rtwvif, p->port_cfg, B_AX_BCNTX_EN);
+}
+
+static void rtw89_mac_port_cfg_bcn_intv(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif)
+{
+ struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif);
+ const struct rtw89_port_reg *p = &rtw_port_base;
+ u16 bcn_int = vif->bss_conf.beacon_int ? vif->bss_conf.beacon_int : BCN_INTERVAL;
+
+ rtw89_write32_port_mask(rtwdev, rtwvif, p->bcn_space, B_AX_BCN_SPACE_MASK,
+ bcn_int);
+}
+
+static void rtw89_mac_port_cfg_bcn_setup_time(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif)
+{
+ const struct rtw89_port_reg *p = &rtw_port_base;
+
+ rtw89_write32_port_mask(rtwdev, rtwvif, p->tbtt_prohib,
+ B_AX_TBTT_SETUP_MASK, BCN_SETUP_DEF);
+}
+
+static void rtw89_mac_port_cfg_bcn_hold_time(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif)
+{
+ const struct rtw89_port_reg *p = &rtw_port_base;
+
+ rtw89_write32_port_mask(rtwdev, rtwvif, p->tbtt_prohib,
+ B_AX_TBTT_HOLD_MASK, BCN_HOLD_DEF);
+}
+
+static void rtw89_mac_port_cfg_bcn_mask_area(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif)
+{
+ const struct rtw89_port_reg *p = &rtw_port_base;
+
+ rtw89_write32_port_mask(rtwdev, rtwvif, p->bcn_area,
+ B_AX_BCN_MSK_AREA_MASK, BCN_MASK_DEF);
+}
+
+static void rtw89_mac_port_cfg_tbtt_early(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif)
+{
+ const struct rtw89_port_reg *p = &rtw_port_base;
+
+ rtw89_write16_port_mask(rtwdev, rtwvif, p->tbtt_early,
+ B_AX_TBTTERLY_MASK, TBTT_ERLY_DEF);
+}
+
+static void rtw89_mac_port_cfg_bss_color(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif)
+{
+ struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif);
+ static const u32 masks[RTW89_PORT_NUM] = {
+ B_AX_BSS_COLOB_AX_PORT_0_MASK, B_AX_BSS_COLOB_AX_PORT_1_MASK,
+ B_AX_BSS_COLOB_AX_PORT_2_MASK, B_AX_BSS_COLOB_AX_PORT_3_MASK,
+ B_AX_BSS_COLOB_AX_PORT_4_MASK,
+ };
+ u8 port = rtwvif->port;
+ u32 reg_base;
+ u32 reg;
+ u8 bss_color;
+
+ bss_color = vif->bss_conf.he_bss_color.color;
+ reg_base = port >= 4 ? R_AX_PTCL_BSS_COLOR_1 : R_AX_PTCL_BSS_COLOR_0;
+ reg = rtw89_mac_reg_by_idx(reg_base, rtwvif->mac_idx);
+ rtw89_write32_mask(rtwdev, reg, masks[port], bss_color);
+}
+
+static void rtw89_mac_port_cfg_mbssid(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif)
+{
+ u8 port = rtwvif->port;
+ u32 reg;
+
+ if (rtwvif->net_type == RTW89_NET_TYPE_AP_MODE)
+ return;
+
+ if (port == 0) {
+ reg = rtw89_mac_reg_by_idx(R_AX_MBSSID_CTRL, rtwvif->mac_idx);
+ rtw89_write32_clr(rtwdev, reg, B_AX_P0MB_ALL_MASK);
+ }
+}
+
+static void rtw89_mac_port_cfg_hiq_drop(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif)
+{
+ u8 port = rtwvif->port;
+ u32 reg;
+ u32 val;
+
+ reg = rtw89_mac_reg_by_idx(R_AX_MBSSID_DROP_0, rtwvif->mac_idx);
+ val = rtw89_read32(rtwdev, reg);
+ val &= ~FIELD_PREP(B_AX_PORT_DROP_4_0_MSK, BIT(port));
+ if (port == 0)
+ val &= ~BIT(0);
+ rtw89_write32(rtwdev, reg, val);
+}
+
+static void rtw89_mac_port_cfg_func_en(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif)
+{
+ const struct rtw89_port_reg *p = &rtw_port_base;
+
+ rtw89_write32_port_set(rtwdev, rtwvif, p->port_cfg, B_AX_PORT_FUNC_EN);
+}
+
+static void rtw89_mac_port_cfg_bcn_early(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif)
+{
+ const struct rtw89_port_reg *p = &rtw_port_base;
+
+ rtw89_write32_port_mask(rtwdev, rtwvif, p->bcn_early, B_AX_BCNERLY_MASK,
+ BCN_ERLY_DEF);
+}
+
+static
+int rtw89_mac_vif_init(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
+{
+ int ret;
+
+ ret = rtw89_mac_port_update(rtwdev, rtwvif);
+ if (ret)
+ return ret;
+
+ rtw89_mac_dmac_tbl_init(rtwdev, rtwvif->mac_id);
+ rtw89_mac_cmac_tbl_init(rtwdev, rtwvif->mac_id);
+
+ ret = rtw89_set_macid_pause(rtwdev, rtwvif->mac_id, false);
+ if (ret)
+ return ret;
+
+ ret = rtw89_fw_h2c_vif_maintain(rtwdev, rtwvif, RTW89_VIF_CREATE);
+ if (ret)
+ return ret;
+
+ ret = rtw89_cam_init(rtwdev, rtwvif);
+ if (ret)
+ return ret;
+
+ ret = rtw89_fw_h2c_cam(rtwdev, rtwvif);
+ if (ret)
+ return ret;
+
+ ret = rtw89_fw_h2c_default_cmac_tbl(rtwdev, rtwvif->mac_id);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static
+int rtw89_mac_vif_deinit(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
+{
+ int ret;
+
+ ret = rtw89_fw_h2c_vif_maintain(rtwdev, rtwvif, RTW89_VIF_REMOVE);
+ if (ret)
+ return ret;
+
+ rtw89_cam_deinit(rtwdev, rtwvif);
+
+ ret = rtw89_fw_h2c_cam(rtwdev, rtwvif);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+void rtw89_restore_vif_cfg_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+ struct rtw89_dev *rtwdev = data;
+ struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
+
+ rtw89_mac_vif_init(rtwdev, rtwvif);
+}
+
+void rtw89_remove_vif_cfg_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+ struct rtw89_dev *rtwdev = data;
+ struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
+
+ rtw89_mac_vif_deinit(rtwdev, rtwvif);
+}
+
+int rtw89_mac_port_update(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
+{
+ u8 port = rtwvif->port;
+
+ if (port >= RTW89_PORT_NUM)
+ return -EINVAL;
+
+ rtw89_mac_port_cfg_func_sw(rtwdev, rtwvif);
+ rtw89_mac_port_cfg_tx_rpt(rtwdev, rtwvif, false);
+ rtw89_mac_port_cfg_rx_rpt(rtwdev, rtwvif, false);
+ rtw89_mac_port_cfg_net_type(rtwdev, rtwvif);
+ rtw89_mac_port_cfg_bcn_prct(rtwdev, rtwvif);
+ rtw89_mac_port_cfg_rx_sw(rtwdev, rtwvif);
+ rtw89_mac_port_cfg_rx_sync(rtwdev, rtwvif);
+ rtw89_mac_port_cfg_tx_sw(rtwdev, rtwvif);
+ rtw89_mac_port_cfg_bcn_intv(rtwdev, rtwvif);
+ rtw89_mac_port_cfg_bcn_setup_time(rtwdev, rtwvif);
+ rtw89_mac_port_cfg_bcn_hold_time(rtwdev, rtwvif);
+ rtw89_mac_port_cfg_bcn_mask_area(rtwdev, rtwvif);
+ rtw89_mac_port_cfg_tbtt_early(rtwdev, rtwvif);
+ rtw89_mac_port_cfg_bss_color(rtwdev, rtwvif);
+ rtw89_mac_port_cfg_mbssid(rtwdev, rtwvif);
+ rtw89_mac_port_cfg_hiq_drop(rtwdev, rtwvif);
+ rtw89_mac_port_cfg_func_en(rtwdev, rtwvif);
+ fsleep(BCN_ERLY_SET_DLY);
+ rtw89_mac_port_cfg_bcn_early(rtwdev, rtwvif);
+
+ return 0;
+}
+
+int rtw89_mac_add_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
+{
+ int ret;
+
+ rtwvif->mac_id = rtw89_core_acquire_bit_map(rtwdev->mac_id_map,
+ RTW89_MAX_MAC_ID_NUM);
+ if (rtwvif->mac_id == RTW89_MAX_MAC_ID_NUM)
+ return -ENOSPC;
+
+ ret = rtw89_mac_vif_init(rtwdev, rtwvif);
+ if (ret)
+ goto release_mac_id;
+
+ return 0;
+
+release_mac_id:
+ rtw89_core_release_bit_map(rtwdev->mac_id_map, rtwvif->mac_id);
+
+ return ret;
+}
+
+int rtw89_mac_remove_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
+{
+ int ret;
+
+ ret = rtw89_mac_vif_deinit(rtwdev, rtwvif);
+ rtw89_core_release_bit_map(rtwdev->mac_id_map, rtwvif->mac_id);
+
+ return ret;
+}
+
+static void
+rtw89_mac_c2h_macid_pause(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
+{
+}
+
+static void
+rtw89_mac_c2h_rec_ack(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
+{
+ rtw89_debug(rtwdev, RTW89_DBG_FW,
+ "C2H rev ack recv, cat: %d, class: %d, func: %d, seq : %d\n",
+ RTW89_GET_MAC_C2H_REV_ACK_CAT(c2h->data),
+ RTW89_GET_MAC_C2H_REV_ACK_CLASS(c2h->data),
+ RTW89_GET_MAC_C2H_REV_ACK_FUNC(c2h->data),
+ RTW89_GET_MAC_C2H_REV_ACK_H2C_SEQ(c2h->data));
+}
+
+static void
+rtw89_mac_c2h_done_ack(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
+{
+ rtw89_debug(rtwdev, RTW89_DBG_FW,
+ "C2H done ack recv, cat: %d, class: %d, func: %d, ret: %d, seq : %d\n",
+ RTW89_GET_MAC_C2H_DONE_ACK_CAT(c2h->data),
+ RTW89_GET_MAC_C2H_DONE_ACK_CLASS(c2h->data),
+ RTW89_GET_MAC_C2H_DONE_ACK_FUNC(c2h->data),
+ RTW89_GET_MAC_C2H_DONE_ACK_H2C_RETURN(c2h->data),
+ RTW89_GET_MAC_C2H_DONE_ACK_H2C_SEQ(c2h->data));
+}
+
+static void
+rtw89_mac_c2h_log(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
+{
+ rtw89_info(rtwdev, "%*s", RTW89_GET_C2H_LOG_LEN(len),
+ RTW89_GET_C2H_LOG_SRT_PRT(c2h->data));
+}
+
+static void (*rtw89_mac_c2h_ofld_handler[])(struct rtw89_dev *rtwdev,
+ struct sk_buff *c2h, u32 len) = {
+ [RTW89_MAC_C2H_FUNC_EFUSE_DUMP] = NULL,
+ [RTW89_MAC_C2H_FUNC_READ_RSP] = NULL,
+ [RTW89_MAC_C2H_FUNC_PKT_OFLD_RSP] = NULL,
+ [RTW89_MAC_C2H_FUNC_BCN_RESEND] = NULL,
+ [RTW89_MAC_C2H_FUNC_MACID_PAUSE] = rtw89_mac_c2h_macid_pause,
+};
+
+static void (*rtw89_mac_c2h_info_handler[])(struct rtw89_dev *rtwdev,
+ struct sk_buff *c2h, u32 len) = {
+ [RTW89_MAC_C2H_FUNC_REC_ACK] = rtw89_mac_c2h_rec_ack,
+ [RTW89_MAC_C2H_FUNC_DONE_ACK] = rtw89_mac_c2h_done_ack,
+ [RTW89_MAC_C2H_FUNC_C2H_LOG] = rtw89_mac_c2h_log,
+};
+
+void rtw89_mac_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb,
+ u32 len, u8 class, u8 func)
+{
+ void (*handler)(struct rtw89_dev *rtwdev,
+ struct sk_buff *c2h, u32 len) = NULL;
+
+ switch (class) {
+ case RTW89_MAC_C2H_CLASS_INFO:
+ if (func < RTW89_MAC_C2H_FUNC_INFO_MAX)
+ handler = rtw89_mac_c2h_info_handler[func];
+ break;
+ case RTW89_MAC_C2H_CLASS_OFLD:
+ if (func < RTW89_MAC_C2H_FUNC_OFLD_MAX)
+ handler = rtw89_mac_c2h_ofld_handler[func];
+ break;
+ case RTW89_MAC_C2H_CLASS_FWDBG:
+ return;
+ default:
+ rtw89_info(rtwdev, "c2h class %d not support\n", class);
+ return;
+ }
+ if (!handler) {
+ rtw89_info(rtwdev, "c2h class %d func %d not support\n", class,
+ func);
+ return;
+ }
+ handler(rtwdev, skb, len);
+}
+
+bool rtw89_mac_get_txpwr_cr(struct rtw89_dev *rtwdev,
+ enum rtw89_phy_idx phy_idx,
+ u32 reg_base, u32 *cr)
+{
+ struct rtw89_dle_mem *dle_mem = rtwdev->chip->dle_mem;
+ enum rtw89_qta_mode mode = dle_mem->mode;
+ u32 addr = rtw89_mac_reg_by_idx(reg_base, phy_idx);
+
+ if (addr < R_AX_PWR_RATE_CTRL || addr > CMAC1_END_ADDR) {
+ rtw89_err(rtwdev, "[TXPWR] addr=0x%x exceed txpwr cr\n",
+ addr);
+ goto error;
+ }
+
+ if (addr >= CMAC1_START_ADDR && addr <= CMAC1_END_ADDR)
+ if (mode == RTW89_QTA_SCC || mode == RTW89_QTA_SCC_STF) {
+ rtw89_err(rtwdev,
+ "[TXPWR] addr=0x%x but hw not enable\n",
+ addr);
+ goto error;
+ }
+
+ *cr = addr;
+ return true;
+
+error:
+ rtw89_err(rtwdev, "[TXPWR] check txpwr cr 0x%x(phy%d) fail\n",
+ addr, phy_idx);
+
+ return false;
+}
+
+int rtw89_mac_cfg_ppdu_status(struct rtw89_dev *rtwdev, u8 mac_idx, bool enable)
+{
+ u32 reg = rtw89_mac_reg_by_idx(R_AX_PPDU_STAT, mac_idx);
+ int ret = 0;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL);
+ if (ret)
+ return ret;
+
+ if (!enable) {
+ rtw89_write32_clr(rtwdev, reg, B_AX_PPDU_STAT_RPT_EN);
+ return ret;
+ }
+
+ rtw89_write32(rtwdev, reg, B_AX_PPDU_STAT_RPT_EN |
+ B_AX_APP_MAC_INFO_RPT |
+ B_AX_APP_RX_CNT_RPT | B_AX_APP_PLCP_HDR_RPT |
+ B_AX_PPDU_STAT_RPT_CRC32);
+ rtw89_write32_mask(rtwdev, R_AX_HW_RPT_FWD, B_AX_FWD_PPDU_STAT_MASK,
+ RTW89_PRPT_DEST_HOST);
+
+ return ret;
+}
+
+void rtw89_mac_update_rts_threshold(struct rtw89_dev *rtwdev, u8 mac_idx)
+{
+#define MAC_AX_TIME_TH_SH 5
+#define MAC_AX_LEN_TH_SH 4
+#define MAC_AX_TIME_TH_MAX 255
+#define MAC_AX_LEN_TH_MAX 255
+#define MAC_AX_TIME_TH_DEF 88
+#define MAC_AX_LEN_TH_DEF 4080
+ struct ieee80211_hw *hw = rtwdev->hw;
+ u32 rts_threshold = hw->wiphy->rts_threshold;
+ u32 time_th, len_th;
+ u32 reg;
+
+ if (rts_threshold == (u32)-1) {
+ time_th = MAC_AX_TIME_TH_DEF;
+ len_th = MAC_AX_LEN_TH_DEF;
+ } else {
+ time_th = MAC_AX_TIME_TH_MAX << MAC_AX_TIME_TH_SH;
+ len_th = rts_threshold;
+ }
+
+ time_th = min_t(u32, time_th >> MAC_AX_TIME_TH_SH, MAC_AX_TIME_TH_MAX);
+ len_th = min_t(u32, len_th >> MAC_AX_LEN_TH_SH, MAC_AX_LEN_TH_MAX);
+
+ reg = rtw89_mac_reg_by_idx(R_AX_AGG_LEN_HT_0, mac_idx);
+ rtw89_write16_mask(rtwdev, reg, B_AX_RTS_TXTIME_TH_MASK, time_th);
+ rtw89_write16_mask(rtwdev, reg, B_AX_RTS_LEN_TH_MASK, len_th);
+}
+
+void rtw89_mac_flush_txq(struct rtw89_dev *rtwdev, u32 queues, bool drop)
+{
+ bool empty;
+ int ret;
+
+ if (!test_bit(RTW89_FLAG_POWERON, rtwdev->flags))
+ return;
+
+ ret = read_poll_timeout(dle_is_txq_empty, empty, empty,
+ 10000, 200000, false, rtwdev);
+ if (ret && !drop && (rtwdev->total_sta_assoc || rtwdev->scanning))
+ rtw89_info(rtwdev, "timed out to flush queues\n");
+}
+
+int rtw89_mac_coex_init(struct rtw89_dev *rtwdev, const struct rtw89_mac_ax_coex *coex)
+{
+ u8 val;
+ u16 val16;
+ u32 val32;
+ int ret;
+
+ val = rtw89_read8(rtwdev, R_AX_GPIO_MUXCFG);
+ rtw89_write8(rtwdev, R_AX_GPIO_MUXCFG, val | B_AX_ENBT);
+
+ val = rtw89_read8(rtwdev, R_AX_BTC_FUNC_EN);
+ rtw89_write8(rtwdev, R_AX_BTC_FUNC_EN, val | B_AX_PTA_WL_TX_EN);
+
+ val = rtw89_read8(rtwdev, R_AX_BT_COEX_CFG_2 + 1);
+ rtw89_write8(rtwdev, R_AX_BT_COEX_CFG_2 + 1, val | BIT(0));
+
+ val = rtw89_read8(rtwdev, R_AX_CSR_MODE);
+ rtw89_write8(rtwdev, R_AX_CSR_MODE, val | B_AX_STATIS_BT_EN | B_AX_WL_ACT_MSK);
+
+ val = rtw89_read8(rtwdev, R_AX_CSR_MODE + 2);
+ rtw89_write8(rtwdev, R_AX_CSR_MODE + 2, val | BIT(0));
+
+ val = rtw89_read8(rtwdev, R_AX_TRXPTCL_RESP_0 + 3);
+ rtw89_write8(rtwdev, R_AX_TRXPTCL_RESP_0 + 3, val & ~BIT(1));
+
+ val16 = rtw89_read16(rtwdev, R_AX_CCA_CFG_0);
+ val16 = (val16 | B_AX_BTCCA_EN) & ~B_AX_BTCCA_BRK_TXOP_EN;
+ rtw89_write16(rtwdev, R_AX_CCA_CFG_0, val16);
+
+ ret = rtw89_mac_read_lte(rtwdev, R_AX_LTE_SW_CFG_2, &val32);
+ if (ret) {
+ rtw89_err(rtwdev, "Read R_AX_LTE_SW_CFG_2 fail!\n");
+ return ret;
+ }
+ val32 = val32 & B_AX_WL_RX_CTRL;
+ ret = rtw89_mac_write_lte(rtwdev, R_AX_LTE_SW_CFG_2, val32);
+ if (ret) {
+ rtw89_err(rtwdev, "Write R_AX_LTE_SW_CFG_2 fail!\n");
+ return ret;
+ }
+
+ switch (coex->pta_mode) {
+ case RTW89_MAC_AX_COEX_RTK_MODE:
+ val = rtw89_read8(rtwdev, R_AX_GPIO_MUXCFG);
+ val &= ~B_AX_BTMODE_MASK;
+ val |= FIELD_PREP(B_AX_BTMODE_MASK, MAC_AX_BT_MODE_0_3);
+ rtw89_write8(rtwdev, R_AX_GPIO_MUXCFG, val);
+
+ val = rtw89_read8(rtwdev, R_AX_TDMA_MODE);
+ rtw89_write8(rtwdev, R_AX_TDMA_MODE, val | B_AX_RTK_BT_ENABLE);
+
+ val = rtw89_read8(rtwdev, R_AX_BT_COEX_CFG_5);
+ val &= ~B_AX_BT_RPT_SAMPLE_RATE_MASK;
+ val |= FIELD_PREP(B_AX_BT_RPT_SAMPLE_RATE_MASK, MAC_AX_RTK_RATE);
+ rtw89_write8(rtwdev, R_AX_BT_COEX_CFG_5, val);
+ break;
+ case RTW89_MAC_AX_COEX_CSR_MODE:
+ val = rtw89_read8(rtwdev, R_AX_GPIO_MUXCFG);
+ val &= ~B_AX_BTMODE_MASK;
+ val |= FIELD_PREP(B_AX_BTMODE_MASK, MAC_AX_BT_MODE_2);
+ rtw89_write8(rtwdev, R_AX_GPIO_MUXCFG, val);
+
+ val16 = rtw89_read16(rtwdev, R_AX_CSR_MODE);
+ val16 &= ~B_AX_BT_PRI_DETECT_TO_MASK;
+ val16 |= FIELD_PREP(B_AX_BT_PRI_DETECT_TO_MASK, MAC_AX_CSR_PRI_TO);
+ val16 &= ~B_AX_BT_TRX_INIT_DETECT_MASK;
+ val16 |= FIELD_PREP(B_AX_BT_TRX_INIT_DETECT_MASK, MAC_AX_CSR_TRX_TO);
+ val16 &= ~B_AX_BT_STAT_DELAY_MASK;
+ val16 |= FIELD_PREP(B_AX_BT_STAT_DELAY_MASK, MAC_AX_CSR_DELAY);
+ val16 |= B_AX_ENHANCED_BT;
+ rtw89_write16(rtwdev, R_AX_CSR_MODE, val16);
+
+ rtw89_write8(rtwdev, R_AX_BT_COEX_CFG_2, MAC_AX_CSR_RATE);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (coex->direction) {
+ case RTW89_MAC_AX_COEX_INNER:
+ val = rtw89_read8(rtwdev, R_AX_GPIO_MUXCFG + 1);
+ val = (val & ~BIT(2)) | BIT(1);
+ rtw89_write8(rtwdev, R_AX_GPIO_MUXCFG + 1, val);
+ break;
+ case RTW89_MAC_AX_COEX_OUTPUT:
+ val = rtw89_read8(rtwdev, R_AX_GPIO_MUXCFG + 1);
+ val = val | BIT(1) | BIT(0);
+ rtw89_write8(rtwdev, R_AX_GPIO_MUXCFG + 1, val);
+ break;
+ case RTW89_MAC_AX_COEX_INPUT:
+ val = rtw89_read8(rtwdev, R_AX_GPIO_MUXCFG + 1);
+ val = val & ~(BIT(2) | BIT(1));
+ rtw89_write8(rtwdev, R_AX_GPIO_MUXCFG + 1, val);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int rtw89_mac_cfg_gnt(struct rtw89_dev *rtwdev,
+ const struct rtw89_mac_ax_coex_gnt *gnt_cfg)
+{
+ u32 val, ret;
+
+ ret = rtw89_mac_read_lte(rtwdev, R_AX_LTE_SW_CFG_1, &val);
+ if (ret) {
+ rtw89_err(rtwdev, "Read LTE fail!\n");
+ return ret;
+ }
+ val = (gnt_cfg->band[0].gnt_bt ?
+ B_AX_GNT_BT_RFC_S0_SW_VAL | B_AX_GNT_BT_BB_S0_SW_VAL : 0) |
+ (gnt_cfg->band[0].gnt_bt_sw_en ?
+ B_AX_GNT_BT_RFC_S0_SW_CTRL | B_AX_GNT_BT_BB_S0_SW_CTRL : 0) |
+ (gnt_cfg->band[0].gnt_wl ?
+ B_AX_GNT_WL_RFC_S0_SW_VAL | B_AX_GNT_WL_BB_S0_SW_VAL : 0) |
+ (gnt_cfg->band[0].gnt_wl_sw_en ?
+ B_AX_GNT_WL_RFC_S0_SW_CTRL | B_AX_GNT_WL_BB_S0_SW_CTRL : 0) |
+ (gnt_cfg->band[1].gnt_bt ?
+ B_AX_GNT_BT_RFC_S1_SW_VAL | B_AX_GNT_BT_BB_S1_SW_VAL : 0) |
+ (gnt_cfg->band[1].gnt_bt_sw_en ?
+ B_AX_GNT_BT_RFC_S1_SW_CTRL | B_AX_GNT_BT_BB_S1_SW_CTRL : 0) |
+ (gnt_cfg->band[1].gnt_wl ?
+ B_AX_GNT_WL_RFC_S1_SW_VAL | B_AX_GNT_WL_BB_S1_SW_VAL : 0) |
+ (gnt_cfg->band[1].gnt_wl_sw_en ?
+ B_AX_GNT_WL_RFC_S1_SW_CTRL | B_AX_GNT_WL_BB_S1_SW_CTRL : 0);
+ ret = rtw89_mac_write_lte(rtwdev, R_AX_LTE_SW_CFG_1, val);
+ if (ret) {
+ rtw89_err(rtwdev, "Write LTE fail!\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+int rtw89_mac_cfg_plt(struct rtw89_dev *rtwdev, struct rtw89_mac_ax_plt *plt)
+{
+ u32 reg;
+ u8 val;
+ int ret;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, plt->band, RTW89_CMAC_SEL);
+ if (ret)
+ return ret;
+
+ reg = rtw89_mac_reg_by_idx(R_AX_BT_PLT, plt->band);
+ val = (plt->tx & RTW89_MAC_AX_PLT_LTE_RX ? B_AX_TX_PLT_GNT_LTE_RX : 0) |
+ (plt->tx & RTW89_MAC_AX_PLT_GNT_BT_TX ? B_AX_TX_PLT_GNT_BT_TX : 0) |
+ (plt->tx & RTW89_MAC_AX_PLT_GNT_BT_RX ? B_AX_TX_PLT_GNT_BT_RX : 0) |
+ (plt->tx & RTW89_MAC_AX_PLT_GNT_WL ? B_AX_TX_PLT_GNT_WL : 0) |
+ (plt->rx & RTW89_MAC_AX_PLT_LTE_RX ? B_AX_RX_PLT_GNT_LTE_RX : 0) |
+ (plt->rx & RTW89_MAC_AX_PLT_GNT_BT_TX ? B_AX_RX_PLT_GNT_BT_TX : 0) |
+ (plt->rx & RTW89_MAC_AX_PLT_GNT_BT_RX ? B_AX_RX_PLT_GNT_BT_RX : 0) |
+ (plt->rx & RTW89_MAC_AX_PLT_GNT_WL ? B_AX_RX_PLT_GNT_WL : 0);
+ rtw89_write8(rtwdev, reg, val);
+
+ return 0;
+}
+
+u16 rtw89_mac_get_plt(struct rtw89_dev *rtwdev, u8 band)
+{
+ u32 reg;
+ u16 cnt;
+
+ reg = rtw89_mac_reg_by_idx(R_AX_BT_PLT, band);
+ cnt = rtw89_read32_mask(rtwdev, reg, B_AX_BT_PLT_PKT_CNT_MASK);
+ rtw89_write16_set(rtwdev, reg, B_AX_BT_PLT_RST);
+
+ return cnt;
+}
+
+void rtw89_mac_cfg_sb(struct rtw89_dev *rtwdev, u32 val)
+{
+ u32 fw_sb;
+
+ fw_sb = rtw89_read32(rtwdev, R_AX_SCOREBOARD);
+ fw_sb = FIELD_GET(B_MAC_AX_SB_FW_MASK, fw_sb);
+ fw_sb = fw_sb & ~B_MAC_AX_BTGS1_NOTIFY;
+ if (!test_bit(RTW89_FLAG_POWERON, rtwdev->flags))
+ fw_sb = fw_sb | MAC_AX_NOTIFY_PWR_MAJOR;
+ else
+ fw_sb = fw_sb | MAC_AX_NOTIFY_TP_MAJOR;
+ val = FIELD_GET(B_MAC_AX_SB_DRV_MASK, val);
+ val = B_AX_TOGGLE |
+ FIELD_PREP(B_MAC_AX_SB_DRV_MASK, val) |
+ FIELD_PREP(B_MAC_AX_SB_FW_MASK, fw_sb);
+ rtw89_write32(rtwdev, R_AX_SCOREBOARD, val);
+}
+
+u32 rtw89_mac_get_sb(struct rtw89_dev *rtwdev)
+{
+ return rtw89_read32(rtwdev, R_AX_SCOREBOARD);
+}
+
+int rtw89_mac_cfg_ctrl_path(struct rtw89_dev *rtwdev, bool wl)
+{
+ u8 val = rtw89_read8(rtwdev, R_AX_SYS_SDIO_CTRL + 3);
+
+ val = wl ? val | BIT(2) : val & ~BIT(2);
+ rtw89_write8(rtwdev, R_AX_SYS_SDIO_CTRL + 3, val);
+
+ return 0;
+}
+
+bool rtw89_mac_get_ctrl_path(struct rtw89_dev *rtwdev)
+{
+ u8 val = rtw89_read8(rtwdev, R_AX_SYS_SDIO_CTRL + 3);
+
+ return FIELD_GET(B_AX_PTA_MUX_CTRL_PATH >> 24, val);
+}
+
+static void rtw89_mac_bfee_ctrl(struct rtw89_dev *rtwdev, u8 mac_idx, bool en)
+{
+ u32 reg;
+ u32 mask = B_AX_BFMEE_HT_NDPA_EN | B_AX_BFMEE_VHT_NDPA_EN |
+ B_AX_BFMEE_HE_NDPA_EN;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BF, "set bfee ndpa_en to %d\n", en);
+ reg = rtw89_mac_reg_by_idx(R_AX_BFMEE_RESP_OPTION, mac_idx);
+ if (en) {
+ set_bit(RTW89_FLAG_BFEE_EN, rtwdev->flags);
+ rtw89_write32_set(rtwdev, reg, mask);
+ } else {
+ clear_bit(RTW89_FLAG_BFEE_EN, rtwdev->flags);
+ rtw89_write32_clr(rtwdev, reg, mask);
+ }
+}
+
+static int rtw89_mac_init_bfee(struct rtw89_dev *rtwdev, u8 mac_idx)
+{
+ u32 reg;
+ u32 val32;
+ int ret;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL);
+ if (ret)
+ return ret;
+
+ /* AP mode set tx gid to 63 */
+ /* STA mode set tx gid to 0(default) */
+ reg = rtw89_mac_reg_by_idx(R_AX_BFMER_CTRL_0, mac_idx);
+ rtw89_write32_set(rtwdev, reg, B_AX_BFMER_NDP_BFEN);
+
+ reg = rtw89_mac_reg_by_idx(R_AX_TRXPTCL_RESP_CSI_RRSC, mac_idx);
+ rtw89_write32(rtwdev, reg, CSI_RRSC_BMAP);
+
+ reg = rtw89_mac_reg_by_idx(R_AX_BFMEE_RESP_OPTION, mac_idx);
+ val32 = FIELD_PREP(B_AX_BFMEE_BFRP_RX_STANDBY_TIMER_MASK, BFRP_RX_STANDBY_TIMER);
+ val32 |= FIELD_PREP(B_AX_BFMEE_NDP_RX_STANDBY_TIMER_MASK, NDP_RX_STANDBY_TIMER);
+ rtw89_write32(rtwdev, reg, val32);
+ rtw89_mac_bfee_ctrl(rtwdev, mac_idx, true);
+
+ reg = rtw89_mac_reg_by_idx(R_AX_TRXPTCL_RESP_CSI_CTRL_0, mac_idx);
+ rtw89_write32_set(rtwdev, reg, B_AX_BFMEE_BFPARAM_SEL |
+ B_AX_BFMEE_USE_NSTS |
+ B_AX_BFMEE_CSI_GID_SEL |
+ B_AX_BFMEE_CSI_FORCE_RETE_EN);
+ reg = rtw89_mac_reg_by_idx(R_AX_TRXPTCL_RESP_CSI_RATE, mac_idx);
+ rtw89_write32(rtwdev, reg,
+ u32_encode_bits(CSI_INIT_RATE_HT, B_AX_BFMEE_HT_CSI_RATE_MASK) |
+ u32_encode_bits(CSI_INIT_RATE_VHT, B_AX_BFMEE_VHT_CSI_RATE_MASK) |
+ u32_encode_bits(CSI_INIT_RATE_HE, B_AX_BFMEE_HE_CSI_RATE_MASK));
+
+ return 0;
+}
+
+static int rtw89_mac_set_csi_para_reg(struct rtw89_dev *rtwdev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
+ u8 mac_idx = rtwvif->mac_idx;
+ u8 nc = 1, nr = 3, ng = 0, cb = 1, cs = 1, ldpc_en = 1, stbc_en = 1;
+ u8 port_sel = rtwvif->port;
+ u8 sound_dim = 3, t;
+ u8 *phy_cap = sta->he_cap.he_cap_elem.phy_cap_info;
+ u32 reg;
+ u16 val;
+ int ret;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL);
+ if (ret)
+ return ret;
+
+ if ((phy_cap[3] & IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER) ||
+ (phy_cap[4] & IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER)) {
+ ldpc_en &= !!(phy_cap[1] & IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD);
+ stbc_en &= !!(phy_cap[2] & IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ);
+ t = FIELD_GET(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK,
+ phy_cap[5]);
+ sound_dim = min(sound_dim, t);
+ }
+ if ((sta->vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE) ||
+ (sta->vht_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)) {
+ ldpc_en &= !!(sta->vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC);
+ stbc_en &= !!(sta->vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_MASK);
+ t = FIELD_GET(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK,
+ sta->vht_cap.cap);
+ sound_dim = min(sound_dim, t);
+ }
+ nc = min(nc, sound_dim);
+ nr = min(nr, sound_dim);
+
+ reg = rtw89_mac_reg_by_idx(R_AX_TRXPTCL_RESP_CSI_CTRL_0, mac_idx);
+ rtw89_write32_set(rtwdev, reg, B_AX_BFMEE_BFPARAM_SEL);
+
+ val = FIELD_PREP(B_AX_BFMEE_CSIINFO0_NC_MASK, nc) |
+ FIELD_PREP(B_AX_BFMEE_CSIINFO0_NR_MASK, nr) |
+ FIELD_PREP(B_AX_BFMEE_CSIINFO0_NG_MASK, ng) |
+ FIELD_PREP(B_AX_BFMEE_CSIINFO0_CB_MASK, cb) |
+ FIELD_PREP(B_AX_BFMEE_CSIINFO0_CS_MASK, cs) |
+ FIELD_PREP(B_AX_BFMEE_CSIINFO0_LDPC_EN, ldpc_en) |
+ FIELD_PREP(B_AX_BFMEE_CSIINFO0_STBC_EN, stbc_en);
+
+ if (port_sel == 0)
+ reg = rtw89_mac_reg_by_idx(R_AX_TRXPTCL_RESP_CSI_CTRL_0, mac_idx);
+ else
+ reg = rtw89_mac_reg_by_idx(R_AX_TRXPTCL_RESP_CSI_CTRL_1, mac_idx);
+
+ rtw89_write16(rtwdev, reg, val);
+
+ return 0;
+}
+
+static int rtw89_mac_csi_rrsc(struct rtw89_dev *rtwdev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
+ u32 rrsc = BIT(RTW89_MAC_BF_RRSC_6M) | BIT(RTW89_MAC_BF_RRSC_24M);
+ u32 reg;
+ u8 mac_idx = rtwvif->mac_idx;
+ int ret;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL);
+ if (ret)
+ return ret;
+
+ if (sta->he_cap.has_he) {
+ rrsc |= (BIT(RTW89_MAC_BF_RRSC_HE_MSC0) |
+ BIT(RTW89_MAC_BF_RRSC_HE_MSC3) |
+ BIT(RTW89_MAC_BF_RRSC_HE_MSC5));
+ }
+ if (sta->vht_cap.vht_supported) {
+ rrsc |= (BIT(RTW89_MAC_BF_RRSC_VHT_MSC0) |
+ BIT(RTW89_MAC_BF_RRSC_VHT_MSC3) |
+ BIT(RTW89_MAC_BF_RRSC_VHT_MSC5));
+ }
+ if (sta->ht_cap.ht_supported) {
+ rrsc |= (BIT(RTW89_MAC_BF_RRSC_HT_MSC0) |
+ BIT(RTW89_MAC_BF_RRSC_HT_MSC3) |
+ BIT(RTW89_MAC_BF_RRSC_HT_MSC5));
+ }
+ reg = rtw89_mac_reg_by_idx(R_AX_TRXPTCL_RESP_CSI_CTRL_0, mac_idx);
+ rtw89_write32_set(rtwdev, reg, B_AX_BFMEE_BFPARAM_SEL);
+ rtw89_write32_clr(rtwdev, reg, B_AX_BFMEE_CSI_FORCE_RETE_EN);
+ rtw89_write32(rtwdev,
+ rtw89_mac_reg_by_idx(R_AX_TRXPTCL_RESP_CSI_RRSC, mac_idx),
+ rrsc);
+
+ return 0;
+}
+
+void rtw89_mac_bf_assoc(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
+
+ if (rtw89_sta_has_beamformer_cap(sta)) {
+ rtw89_debug(rtwdev, RTW89_DBG_BF,
+ "initialize bfee for new association\n");
+ rtw89_mac_init_bfee(rtwdev, rtwvif->mac_idx);
+ rtw89_mac_set_csi_para_reg(rtwdev, vif, sta);
+ rtw89_mac_csi_rrsc(rtwdev, vif, sta);
+ }
+}
+
+void rtw89_mac_bf_disassoc(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
+
+ rtw89_mac_bfee_ctrl(rtwdev, rtwvif->mac_idx, false);
+}
+
+void rtw89_mac_bf_set_gid_table(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *conf)
+{
+ struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
+ u8 mac_idx = rtwvif->mac_idx;
+ __le32 *p;
+
+ rtw89_debug(rtwdev, RTW89_DBG_BF, "update bf GID table\n");
+
+ p = (__le32 *)conf->mu_group.membership;
+ rtw89_write32(rtwdev, rtw89_mac_reg_by_idx(R_AX_GID_POSITION_EN0, mac_idx),
+ le32_to_cpu(p[0]));
+ rtw89_write32(rtwdev, rtw89_mac_reg_by_idx(R_AX_GID_POSITION_EN1, mac_idx),
+ le32_to_cpu(p[1]));
+
+ p = (__le32 *)conf->mu_group.position;
+ rtw89_write32(rtwdev, rtw89_mac_reg_by_idx(R_AX_GID_POSITION0, mac_idx),
+ le32_to_cpu(p[0]));
+ rtw89_write32(rtwdev, rtw89_mac_reg_by_idx(R_AX_GID_POSITION1, mac_idx),
+ le32_to_cpu(p[1]));
+ rtw89_write32(rtwdev, rtw89_mac_reg_by_idx(R_AX_GID_POSITION2, mac_idx),
+ le32_to_cpu(p[2]));
+ rtw89_write32(rtwdev, rtw89_mac_reg_by_idx(R_AX_GID_POSITION3, mac_idx),
+ le32_to_cpu(p[3]));
+}
+
+struct rtw89_mac_bf_monitor_iter_data {
+ struct rtw89_dev *rtwdev;
+ struct ieee80211_sta *down_sta;
+ int count;
+};
+
+static
+void rtw89_mac_bf_monitor_calc_iter(void *data, struct ieee80211_sta *sta)
+{
+ struct rtw89_mac_bf_monitor_iter_data *iter_data =
+ (struct rtw89_mac_bf_monitor_iter_data *)data;
+ struct ieee80211_sta *down_sta = iter_data->down_sta;
+ int *count = &iter_data->count;
+
+ if (down_sta == sta)
+ return;
+
+ if (rtw89_sta_has_beamformer_cap(sta))
+ (*count)++;
+}
+
+void rtw89_mac_bf_monitor_calc(struct rtw89_dev *rtwdev,
+ struct ieee80211_sta *sta, bool disconnect)
+{
+ struct rtw89_mac_bf_monitor_iter_data data;
+
+ data.rtwdev = rtwdev;
+ data.down_sta = disconnect ? sta : NULL;
+ data.count = 0;
+ ieee80211_iterate_stations_atomic(rtwdev->hw,
+ rtw89_mac_bf_monitor_calc_iter,
+ &data);
+
+ rtw89_debug(rtwdev, RTW89_DBG_BF, "bfee STA count=%d\n", data.count);
+ if (data.count)
+ set_bit(RTW89_FLAG_BFEE_MON, rtwdev->flags);
+ else
+ clear_bit(RTW89_FLAG_BFEE_MON, rtwdev->flags);
+}
+
+struct rtw89_mac_bfee_ctrl_iter_data {
+ struct rtw89_dev *rtwdev;
+ bool en;
+};
+
+static void rtw89_mac_bfee_ctrl_iter(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
+ struct rtw89_mac_bfee_ctrl_iter_data *iter_data =
+ (struct rtw89_mac_bfee_ctrl_iter_data *)data;
+ struct rtw89_dev *rtwdev = iter_data->rtwdev;
+ bool en = iter_data->en;
+
+ rtw89_mac_bfee_ctrl(rtwdev, rtwvif->mac_idx, en);
+}
+
+void _rtw89_mac_bf_monitor_track(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_traffic_stats *stats = &rtwdev->stats;
+ struct rtw89_mac_bfee_ctrl_iter_data iter_data;
+ bool en = stats->tx_tfc_lv > stats->rx_tfc_lv ? false : true;
+ bool old = test_bit(RTW89_FLAG_BFEE_EN, rtwdev->flags);
+
+ if (en == old)
+ return;
+
+ iter_data.rtwdev = rtwdev;
+ iter_data.en = en;
+ rtw89_iterate_vifs(rtwdev, rtw89_mac_bfee_ctrl_iter, &iter_data, false);
+}
+
+static int
+__rtw89_mac_set_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta,
+ u32 tx_time)
+{
+#define MAC_AX_DFLT_TX_TIME 5280
+ u8 mac_idx = rtwsta->rtwvif->mac_idx;
+ u32 max_tx_time = tx_time == 0 ? MAC_AX_DFLT_TX_TIME : tx_time;
+ u32 reg;
+ int ret = 0;
+
+ if (rtwsta->cctl_tx_time) {
+ rtwsta->ampdu_max_time = (max_tx_time - 512) >> 9;
+ ret = rtw89_fw_h2c_txtime_cmac_tbl(rtwdev, rtwsta);
+ } else {
+ ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL);
+ if (ret) {
+ rtw89_warn(rtwdev, "failed to check cmac in set txtime\n");
+ return ret;
+ }
+
+ reg = rtw89_mac_reg_by_idx(R_AX_AMPDU_AGG_LIMIT, mac_idx);
+ rtw89_write32_mask(rtwdev, reg, B_AX_AMPDU_MAX_TIME_MASK,
+ max_tx_time >> 5);
+ }
+
+ return ret;
+}
+
+int rtw89_mac_set_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta,
+ bool resume, u32 tx_time)
+{
+ int ret = 0;
+
+ if (!resume) {
+ rtwsta->cctl_tx_time = true;
+ ret = __rtw89_mac_set_tx_time(rtwdev, rtwsta, tx_time);
+ } else {
+ ret = __rtw89_mac_set_tx_time(rtwdev, rtwsta, tx_time);
+ rtwsta->cctl_tx_time = false;
+ }
+
+ return ret;
+}
+
+int rtw89_mac_get_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta,
+ u32 *tx_time)
+{
+ u8 mac_idx = rtwsta->rtwvif->mac_idx;
+ u32 reg;
+ int ret = 0;
+
+ if (rtwsta->cctl_tx_time) {
+ *tx_time = (rtwsta->ampdu_max_time + 1) << 9;
+ } else {
+ ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL);
+ if (ret) {
+ rtw89_warn(rtwdev, "failed to check cmac in tx_time\n");
+ return ret;
+ }
+
+ reg = rtw89_mac_reg_by_idx(R_AX_AMPDU_AGG_LIMIT, mac_idx);
+ *tx_time = rtw89_read32_mask(rtwdev, reg, B_AX_AMPDU_MAX_TIME_MASK) << 5;
+ }
+
+ return ret;
+}
+
+int rtw89_mac_set_tx_retry_limit(struct rtw89_dev *rtwdev,
+ struct rtw89_sta *rtwsta,
+ bool resume, u8 tx_retry)
+{
+ int ret = 0;
+
+ rtwsta->data_tx_cnt_lmt = tx_retry;
+
+ if (!resume) {
+ rtwsta->cctl_tx_retry_limit = true;
+ ret = rtw89_fw_h2c_txtime_cmac_tbl(rtwdev, rtwsta);
+ } else {
+ ret = rtw89_fw_h2c_txtime_cmac_tbl(rtwdev, rtwsta);
+ rtwsta->cctl_tx_retry_limit = false;
+ }
+
+ return ret;
+}
+
+int rtw89_mac_get_tx_retry_limit(struct rtw89_dev *rtwdev,
+ struct rtw89_sta *rtwsta, u8 *tx_retry)
+{
+ u8 mac_idx = rtwsta->rtwvif->mac_idx;
+ u32 reg;
+ int ret = 0;
+
+ if (rtwsta->cctl_tx_retry_limit) {
+ *tx_retry = rtwsta->data_tx_cnt_lmt;
+ } else {
+ ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL);
+ if (ret) {
+ rtw89_warn(rtwdev, "failed to check cmac in rty_lmt\n");
+ return ret;
+ }
+
+ reg = rtw89_mac_reg_by_idx(R_AX_TXCNT, mac_idx);
+ *tx_retry = rtw89_read32_mask(rtwdev, reg, B_AX_L_TXCNT_LMT_MASK);
+ }
+
+ return ret;
+}
+
+int rtw89_mac_set_hw_muedca_ctrl(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif, bool en)
+{
+ u8 mac_idx = rtwvif->mac_idx;
+ u16 set = B_AX_MUEDCA_EN_0 | B_AX_SET_MUEDCATIMER_TF_0;
+ u32 reg;
+ u32 ret;
+
+ ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL);
+ if (ret)
+ return ret;
+
+ reg = rtw89_mac_reg_by_idx(R_AX_MUEDCA_EN, mac_idx);
+ if (en)
+ rtw89_write16_set(rtwdev, reg, set);
+ else
+ rtw89_write16_clr(rtwdev, reg, set);
+
+ return 0;
+}
diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h
new file mode 100644
index 000000000000..efff7cbb88fe
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw89/mac.h
@@ -0,0 +1,905 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/* Copyright(c) 2019-2020 Realtek Corporation
+ */
+
+#ifndef __RTW89_MAC_H__
+#define __RTW89_MAC_H__
+
+#include "core.h"
+
+#define MAC_MEM_DUMP_PAGE_SIZE 0x40000
+#define ADDR_CAM_ENT_SIZE 0x40
+#define BSSID_CAM_ENT_SIZE 0x08
+#define HFC_PAGE_UNIT 64
+
+enum rtw89_mac_hwmod_sel {
+ RTW89_DMAC_SEL = 0,
+ RTW89_CMAC_SEL = 1,
+
+ RTW89_MAC_INVALID,
+};
+
+enum rtw89_mac_fwd_target {
+ RTW89_FWD_DONT_CARE = 0,
+ RTW89_FWD_TO_HOST = 1,
+ RTW89_FWD_TO_WLAN_CPU = 2
+};
+
+enum rtw89_mac_wd_dma_intvl {
+ RTW89_MAC_WD_DMA_INTVL_0S,
+ RTW89_MAC_WD_DMA_INTVL_256NS,
+ RTW89_MAC_WD_DMA_INTVL_512NS,
+ RTW89_MAC_WD_DMA_INTVL_768NS,
+ RTW89_MAC_WD_DMA_INTVL_1US,
+ RTW89_MAC_WD_DMA_INTVL_1_5US,
+ RTW89_MAC_WD_DMA_INTVL_2US,
+ RTW89_MAC_WD_DMA_INTVL_4US,
+ RTW89_MAC_WD_DMA_INTVL_8US,
+ RTW89_MAC_WD_DMA_INTVL_16US,
+ RTW89_MAC_WD_DMA_INTVL_DEF = 0xFE
+};
+
+enum rtw89_mac_multi_tag_num {
+ RTW89_MAC_TAG_NUM_1,
+ RTW89_MAC_TAG_NUM_2,
+ RTW89_MAC_TAG_NUM_3,
+ RTW89_MAC_TAG_NUM_4,
+ RTW89_MAC_TAG_NUM_5,
+ RTW89_MAC_TAG_NUM_6,
+ RTW89_MAC_TAG_NUM_7,
+ RTW89_MAC_TAG_NUM_8,
+ RTW89_MAC_TAG_NUM_DEF = 0xFE
+};
+
+enum rtw89_mac_lbc_tmr {
+ RTW89_MAC_LBC_TMR_8US = 0,
+ RTW89_MAC_LBC_TMR_16US,
+ RTW89_MAC_LBC_TMR_32US,
+ RTW89_MAC_LBC_TMR_64US,
+ RTW89_MAC_LBC_TMR_128US,
+ RTW89_MAC_LBC_TMR_256US,
+ RTW89_MAC_LBC_TMR_512US,
+ RTW89_MAC_LBC_TMR_1MS,
+ RTW89_MAC_LBC_TMR_2MS,
+ RTW89_MAC_LBC_TMR_4MS,
+ RTW89_MAC_LBC_TMR_8MS,
+ RTW89_MAC_LBC_TMR_DEF = 0xFE
+};
+
+enum rtw89_mac_cpuio_op_cmd_type {
+ CPUIO_OP_CMD_GET_1ST_PID = 0,
+ CPUIO_OP_CMD_GET_NEXT_PID = 1,
+ CPUIO_OP_CMD_ENQ_TO_TAIL = 4,
+ CPUIO_OP_CMD_ENQ_TO_HEAD = 5,
+ CPUIO_OP_CMD_DEQ = 8,
+ CPUIO_OP_CMD_DEQ_ENQ_ALL = 9,
+ CPUIO_OP_CMD_DEQ_ENQ_TO_TAIL = 12
+};
+
+enum rtw89_mac_wde_dle_port_id {
+ WDE_DLE_PORT_ID_DISPATCH = 0,
+ WDE_DLE_PORT_ID_PKTIN = 1,
+ WDE_DLE_PORT_ID_CMAC0 = 3,
+ WDE_DLE_PORT_ID_CMAC1 = 4,
+ WDE_DLE_PORT_ID_CPU_IO = 6,
+ WDE_DLE_PORT_ID_WDRLS = 7,
+ WDE_DLE_PORT_ID_END = 8
+};
+
+enum rtw89_mac_wde_dle_queid_wdrls {
+ WDE_DLE_QUEID_TXOK = 0,
+ WDE_DLE_QUEID_DROP_RETRY_LIMIT = 1,
+ WDE_DLE_QUEID_DROP_LIFETIME_TO = 2,
+ WDE_DLE_QUEID_DROP_MACID_DROP = 3,
+ WDE_DLE_QUEID_NO_REPORT = 4
+};
+
+enum rtw89_mac_ple_dle_port_id {
+ PLE_DLE_PORT_ID_DISPATCH = 0,
+ PLE_DLE_PORT_ID_MPDU = 1,
+ PLE_DLE_PORT_ID_SEC = 2,
+ PLE_DLE_PORT_ID_CMAC0 = 3,
+ PLE_DLE_PORT_ID_CMAC1 = 4,
+ PLE_DLE_PORT_ID_WDRLS = 5,
+ PLE_DLE_PORT_ID_CPU_IO = 6,
+ PLE_DLE_PORT_ID_PLRLS = 7,
+ PLE_DLE_PORT_ID_END = 8
+};
+
+enum rtw89_mac_ple_dle_queid_plrls {
+ PLE_DLE_QUEID_NO_REPORT = 0x0
+};
+
+enum rtw89_machdr_frame_type {
+ RTW89_MGNT = 0,
+ RTW89_CTRL = 1,
+ RTW89_DATA = 2,
+};
+
+enum rtw89_mac_dle_dfi_type {
+ DLE_DFI_TYPE_FREEPG = 0,
+ DLE_DFI_TYPE_QUOTA = 1,
+ DLE_DFI_TYPE_PAGELLT = 2,
+ DLE_DFI_TYPE_PKTINFO = 3,
+ DLE_DFI_TYPE_PREPKTLLT = 4,
+ DLE_DFI_TYPE_NXTPKTLLT = 5,
+ DLE_DFI_TYPE_QLNKTBL = 6,
+ DLE_DFI_TYPE_QEMPTY = 7,
+};
+
+enum rtw89_mac_dle_wde_quota_id {
+ WDE_QTAID_HOST_IF = 0,
+ WDE_QTAID_WLAN_CPU = 1,
+ WDE_QTAID_DATA_CPU = 2,
+ WDE_QTAID_PKTIN = 3,
+ WDE_QTAID_CPUIO = 4,
+};
+
+enum rtw89_mac_dle_ple_quota_id {
+ PLE_QTAID_B0_TXPL = 0,
+ PLE_QTAID_B1_TXPL = 1,
+ PLE_QTAID_C2H = 2,
+ PLE_QTAID_H2C = 3,
+ PLE_QTAID_WLAN_CPU = 4,
+ PLE_QTAID_MPDU = 5,
+ PLE_QTAID_CMAC0_RX = 6,
+ PLE_QTAID_CMAC1_RX = 7,
+ PLE_QTAID_CMAC1_BBRPT = 8,
+ PLE_QTAID_WDRLS = 9,
+ PLE_QTAID_CPUIO = 10,
+};
+
+enum rtw89_mac_dle_ctrl_type {
+ DLE_CTRL_TYPE_WDE = 0,
+ DLE_CTRL_TYPE_PLE = 1,
+ DLE_CTRL_TYPE_NUM = 2,
+};
+
+enum rtw89_mac_ax_l0_to_l1_event {
+ MAC_AX_L0_TO_L1_CHIF_IDLE = 0,
+ MAC_AX_L0_TO_L1_CMAC_DMA_IDLE = 1,
+ MAC_AX_L0_TO_L1_RLS_PKID = 2,
+ MAC_AX_L0_TO_L1_PTCL_IDLE = 3,
+ MAC_AX_L0_TO_L1_RX_QTA_LOST = 4,
+ MAC_AX_L0_TO_L1_DLE_STAT_HANG = 5,
+ MAC_AX_L0_TO_L1_PCIE_STUCK = 6,
+ MAC_AX_L0_TO_L1_EVENT_MAX = 15,
+};
+
+enum rtw89_mac_dbg_port_sel {
+ /* CMAC 0 related */
+ RTW89_DBG_PORT_SEL_PTCL_C0 = 0,
+ RTW89_DBG_PORT_SEL_SCH_C0,
+ RTW89_DBG_PORT_SEL_TMAC_C0,
+ RTW89_DBG_PORT_SEL_RMAC_C0,
+ RTW89_DBG_PORT_SEL_RMACST_C0,
+ RTW89_DBG_PORT_SEL_RMAC_PLCP_C0,
+ RTW89_DBG_PORT_SEL_TRXPTCL_C0,
+ RTW89_DBG_PORT_SEL_TX_INFOL_C0,
+ RTW89_DBG_PORT_SEL_TX_INFOH_C0,
+ RTW89_DBG_PORT_SEL_TXTF_INFOL_C0,
+ RTW89_DBG_PORT_SEL_TXTF_INFOH_C0,
+ /* CMAC 1 related */
+ RTW89_DBG_PORT_SEL_PTCL_C1,
+ RTW89_DBG_PORT_SEL_SCH_C1,
+ RTW89_DBG_PORT_SEL_TMAC_C1,
+ RTW89_DBG_PORT_SEL_RMAC_C1,
+ RTW89_DBG_PORT_SEL_RMACST_C1,
+ RTW89_DBG_PORT_SEL_RMAC_PLCP_C1,
+ RTW89_DBG_PORT_SEL_TRXPTCL_C1,
+ RTW89_DBG_PORT_SEL_TX_INFOL_C1,
+ RTW89_DBG_PORT_SEL_TX_INFOH_C1,
+ RTW89_DBG_PORT_SEL_TXTF_INFOL_C1,
+ RTW89_DBG_PORT_SEL_TXTF_INFOH_C1,
+ /* DLE related */
+ RTW89_DBG_PORT_SEL_WDE_BUFMGN_FREEPG,
+ RTW89_DBG_PORT_SEL_WDE_BUFMGN_QUOTA,
+ RTW89_DBG_PORT_SEL_WDE_BUFMGN_PAGELLT,
+ RTW89_DBG_PORT_SEL_WDE_BUFMGN_PKTINFO,
+ RTW89_DBG_PORT_SEL_WDE_QUEMGN_PREPKT,
+ RTW89_DBG_PORT_SEL_WDE_QUEMGN_NXTPKT,
+ RTW89_DBG_PORT_SEL_WDE_QUEMGN_QLNKTBL,
+ RTW89_DBG_PORT_SEL_WDE_QUEMGN_QEMPTY,
+ RTW89_DBG_PORT_SEL_PLE_BUFMGN_FREEPG,
+ RTW89_DBG_PORT_SEL_PLE_BUFMGN_QUOTA,
+ RTW89_DBG_PORT_SEL_PLE_BUFMGN_PAGELLT,
+ RTW89_DBG_PORT_SEL_PLE_BUFMGN_PKTINFO,
+ RTW89_DBG_PORT_SEL_PLE_QUEMGN_PREPKT,
+ RTW89_DBG_PORT_SEL_PLE_QUEMGN_NXTPKT,
+ RTW89_DBG_PORT_SEL_PLE_QUEMGN_QLNKTBL,
+ RTW89_DBG_PORT_SEL_PLE_QUEMGN_QEMPTY,
+ RTW89_DBG_PORT_SEL_PKTINFO,
+ /* PCIE related */
+ RTW89_DBG_PORT_SEL_PCIE_TXDMA,
+ RTW89_DBG_PORT_SEL_PCIE_RXDMA,
+ RTW89_DBG_PORT_SEL_PCIE_CVT,
+ RTW89_DBG_PORT_SEL_PCIE_CXPL,
+ RTW89_DBG_PORT_SEL_PCIE_IO,
+ RTW89_DBG_PORT_SEL_PCIE_MISC,
+ RTW89_DBG_PORT_SEL_PCIE_MISC2,
+
+ /* keep last */
+ RTW89_DBG_PORT_SEL_LAST,
+ RTW89_DBG_PORT_SEL_MAX = RTW89_DBG_PORT_SEL_LAST,
+ RTW89_DBG_PORT_SEL_INVALID = RTW89_DBG_PORT_SEL_LAST,
+};
+
+/* SRAM mem dump */
+#define R_AX_INDIR_ACCESS_ENTRY 0x40000
+
+#define STA_SCHED_BASE_ADDR 0x18808000
+#define RXPLD_FLTR_CAM_BASE_ADDR 0x18813000
+#define SECURITY_CAM_BASE_ADDR 0x18814000
+#define WOW_CAM_BASE_ADDR 0x18815000
+#define CMAC_TBL_BASE_ADDR 0x18840000
+#define ADDR_CAM_BASE_ADDR 0x18850000
+#define BSSID_CAM_BASE_ADDR 0x18853000
+#define BA_CAM_BASE_ADDR 0x18854000
+#define BCN_IE_CAM0_BASE_ADDR 0x18855000
+#define SHARED_BUF_BASE_ADDR 0x18700000
+#define DMAC_TBL_BASE_ADDR 0x18800000
+#define SHCUT_MACHDR_BASE_ADDR 0x18800800
+#define BCN_IE_CAM1_BASE_ADDR 0x188A0000
+
+#define CCTL_INFO_SIZE 32
+
+enum rtw89_mac_mem_sel {
+ RTW89_MAC_MEM_SHARED_BUF,
+ RTW89_MAC_MEM_DMAC_TBL,
+ RTW89_MAC_MEM_SHCUT_MACHDR,
+ RTW89_MAC_MEM_STA_SCHED,
+ RTW89_MAC_MEM_RXPLD_FLTR_CAM,
+ RTW89_MAC_MEM_SECURITY_CAM,
+ RTW89_MAC_MEM_WOW_CAM,
+ RTW89_MAC_MEM_CMAC_TBL,
+ RTW89_MAC_MEM_ADDR_CAM,
+ RTW89_MAC_MEM_BA_CAM,
+ RTW89_MAC_MEM_BCN_IE_CAM0,
+ RTW89_MAC_MEM_BCN_IE_CAM1,
+
+ /* keep last */
+ RTW89_MAC_MEM_LAST,
+ RTW89_MAC_MEM_MAX = RTW89_MAC_MEM_LAST,
+ RTW89_MAC_MEM_INVALID = RTW89_MAC_MEM_LAST,
+};
+
+enum rtw89_rpwm_req_pwr_state {
+ RTW89_MAC_RPWM_REQ_PWR_STATE_ACTIVE = 0,
+ RTW89_MAC_RPWM_REQ_PWR_STATE_BAND0_RFON = 1,
+ RTW89_MAC_RPWM_REQ_PWR_STATE_BAND1_RFON = 2,
+ RTW89_MAC_RPWM_REQ_PWR_STATE_BAND0_RFOFF = 3,
+ RTW89_MAC_RPWM_REQ_PWR_STATE_BAND1_RFOFF = 4,
+ RTW89_MAC_RPWM_REQ_PWR_STATE_CLK_GATED = 5,
+ RTW89_MAC_RPWM_REQ_PWR_STATE_PWR_GATED = 6,
+ RTW89_MAC_RPWM_REQ_PWR_STATE_HIOE_PWR_GATED = 7,
+ RTW89_MAC_RPWM_REQ_PWR_STATE_MAX,
+};
+
+struct rtw89_pwr_cfg {
+ u16 addr;
+ u8 cv_msk;
+ u8 intf_msk;
+ u8 base:4;
+ u8 cmd:4;
+ u8 msk;
+ u8 val;
+};
+
+enum rtw89_mac_c2h_ofld_func {
+ RTW89_MAC_C2H_FUNC_EFUSE_DUMP,
+ RTW89_MAC_C2H_FUNC_READ_RSP,
+ RTW89_MAC_C2H_FUNC_PKT_OFLD_RSP,
+ RTW89_MAC_C2H_FUNC_BCN_RESEND,
+ RTW89_MAC_C2H_FUNC_MACID_PAUSE,
+ RTW89_MAC_C2H_FUNC_OFLD_MAX,
+};
+
+enum rtw89_mac_c2h_info_func {
+ RTW89_MAC_C2H_FUNC_REC_ACK,
+ RTW89_MAC_C2H_FUNC_DONE_ACK,
+ RTW89_MAC_C2H_FUNC_C2H_LOG,
+ RTW89_MAC_C2H_FUNC_INFO_MAX,
+};
+
+enum rtw89_mac_c2h_class {
+ RTW89_MAC_C2H_CLASS_INFO,
+ RTW89_MAC_C2H_CLASS_OFLD,
+ RTW89_MAC_C2H_CLASS_TWT,
+ RTW89_MAC_C2H_CLASS_WOW,
+ RTW89_MAC_C2H_CLASS_MCC,
+ RTW89_MAC_C2H_CLASS_FWDBG,
+ RTW89_MAC_C2H_CLASS_MAX,
+};
+
+struct rtw89_mac_ax_coex {
+#define RTW89_MAC_AX_COEX_RTK_MODE 0
+#define RTW89_MAC_AX_COEX_CSR_MODE 1
+ u8 pta_mode;
+#define RTW89_MAC_AX_COEX_INNER 0
+#define RTW89_MAC_AX_COEX_OUTPUT 1
+#define RTW89_MAC_AX_COEX_INPUT 2
+ u8 direction;
+};
+
+struct rtw89_mac_ax_plt {
+#define RTW89_MAC_AX_PLT_LTE_RX BIT(0)
+#define RTW89_MAC_AX_PLT_GNT_BT_TX BIT(1)
+#define RTW89_MAC_AX_PLT_GNT_BT_RX BIT(2)
+#define RTW89_MAC_AX_PLT_GNT_WL BIT(3)
+ u8 band;
+ u8 tx;
+ u8 rx;
+};
+
+enum rtw89_mac_bf_rrsc_rate {
+ RTW89_MAC_BF_RRSC_6M = 0,
+ RTW89_MAC_BF_RRSC_9M = 1,
+ RTW89_MAC_BF_RRSC_12M,
+ RTW89_MAC_BF_RRSC_18M,
+ RTW89_MAC_BF_RRSC_24M,
+ RTW89_MAC_BF_RRSC_36M,
+ RTW89_MAC_BF_RRSC_48M,
+ RTW89_MAC_BF_RRSC_54M,
+ RTW89_MAC_BF_RRSC_HT_MSC0,
+ RTW89_MAC_BF_RRSC_HT_MSC1,
+ RTW89_MAC_BF_RRSC_HT_MSC2,
+ RTW89_MAC_BF_RRSC_HT_MSC3,
+ RTW89_MAC_BF_RRSC_HT_MSC4,
+ RTW89_MAC_BF_RRSC_HT_MSC5,
+ RTW89_MAC_BF_RRSC_HT_MSC6,
+ RTW89_MAC_BF_RRSC_HT_MSC7,
+ RTW89_MAC_BF_RRSC_VHT_MSC0,
+ RTW89_MAC_BF_RRSC_VHT_MSC1,
+ RTW89_MAC_BF_RRSC_VHT_MSC2,
+ RTW89_MAC_BF_RRSC_VHT_MSC3,
+ RTW89_MAC_BF_RRSC_VHT_MSC4,
+ RTW89_MAC_BF_RRSC_VHT_MSC5,
+ RTW89_MAC_BF_RRSC_VHT_MSC6,
+ RTW89_MAC_BF_RRSC_VHT_MSC7,
+ RTW89_MAC_BF_RRSC_HE_MSC0,
+ RTW89_MAC_BF_RRSC_HE_MSC1,
+ RTW89_MAC_BF_RRSC_HE_MSC2,
+ RTW89_MAC_BF_RRSC_HE_MSC3,
+ RTW89_MAC_BF_RRSC_HE_MSC4,
+ RTW89_MAC_BF_RRSC_HE_MSC5,
+ RTW89_MAC_BF_RRSC_HE_MSC6,
+ RTW89_MAC_BF_RRSC_HE_MSC7 = 31,
+ RTW89_MAC_BF_RRSC_MAX = 32
+};
+
+#define RTW89_R32_EA 0xEAEAEAEA
+#define RTW89_R32_DEAD 0xDEADBEEF
+#define MAC_REG_POOL_COUNT 10
+#define ACCESS_CMAC(_addr) \
+ ({typeof(_addr) __addr = (_addr); \
+ __addr >= R_AX_CMAC_REG_START && __addr <= R_AX_CMAC_REG_END; })
+
+#define PTCL_IDLE_POLL_CNT 10000
+#define SW_CVR_DUR_US 8
+#define SW_CVR_CNT 8
+
+#define DLE_BOUND_UNIT (8 * 1024)
+#define DLE_WAIT_CNT 2000
+#define TRXCFG_WAIT_CNT 2000
+
+#define RTW89_WDE_PG_64 64
+#define RTW89_WDE_PG_128 128
+#define RTW89_WDE_PG_256 256
+
+#define S_AX_WDE_PAGE_SEL_64 0
+#define S_AX_WDE_PAGE_SEL_128 1
+#define S_AX_WDE_PAGE_SEL_256 2
+
+#define RTW89_PLE_PG_64 64
+#define RTW89_PLE_PG_128 128
+#define RTW89_PLE_PG_256 256
+
+#define S_AX_PLE_PAGE_SEL_64 0
+#define S_AX_PLE_PAGE_SEL_128 1
+#define S_AX_PLE_PAGE_SEL_256 2
+
+#define SDIO_LOCAL_BASE_ADDR 0x80000000
+
+#define PWR_CMD_WRITE 0
+#define PWR_CMD_POLL 1
+#define PWR_CMD_DELAY 2
+#define PWR_CMD_END 3
+
+#define PWR_INTF_MSK_SDIO BIT(0)
+#define PWR_INTF_MSK_USB BIT(1)
+#define PWR_INTF_MSK_PCIE BIT(2)
+#define PWR_INTF_MSK_ALL 0x7
+
+#define PWR_BASE_MAC 0
+#define PWR_BASE_USB 1
+#define PWR_BASE_PCIE 2
+#define PWR_BASE_SDIO 3
+
+#define PWR_CV_MSK_A BIT(0)
+#define PWR_CV_MSK_B BIT(1)
+#define PWR_CV_MSK_C BIT(2)
+#define PWR_CV_MSK_D BIT(3)
+#define PWR_CV_MSK_E BIT(4)
+#define PWR_CV_MSK_F BIT(5)
+#define PWR_CV_MSK_G BIT(6)
+#define PWR_CV_MSK_TEST BIT(7)
+#define PWR_CV_MSK_ALL 0xFF
+
+#define PWR_DELAY_US 0
+#define PWR_DELAY_MS 1
+
+/* STA scheduler */
+#define SS_MACID_SH 8
+#define SS_TX_LEN_MSK 0x1FFFFF
+#define SS_CTRL1_R_TX_LEN 5
+#define SS_CTRL1_R_NEXT_LINK 20
+#define SS_LINK_SIZE 256
+
+/* MAC debug port */
+#define TMAC_DBG_SEL_C0 0xA5
+#define RMAC_DBG_SEL_C0 0xA6
+#define TRXPTCL_DBG_SEL_C0 0xA7
+#define TMAC_DBG_SEL_C1 0xB5
+#define RMAC_DBG_SEL_C1 0xB6
+#define TRXPTCL_DBG_SEL_C1 0xB7
+#define FW_PROG_CNTR_DBG_SEL 0xF2
+#define PCIE_TXDMA_DBG_SEL 0x30
+#define PCIE_RXDMA_DBG_SEL 0x31
+#define PCIE_CVT_DBG_SEL 0x32
+#define PCIE_CXPL_DBG_SEL 0x33
+#define PCIE_IO_DBG_SEL 0x37
+#define PCIE_MISC_DBG_SEL 0x38
+#define PCIE_MISC2_DBG_SEL 0x00
+#define MAC_DBG_SEL 1
+#define RMAC_CMAC_DBG_SEL 1
+
+/* TRXPTCL dbg port sel */
+#define TRXPTRL_DBG_SEL_TMAC 0
+#define TRXPTRL_DBG_SEL_RMAC 1
+
+struct rtw89_cpuio_ctrl {
+ u16 pkt_num;
+ u16 start_pktid;
+ u16 end_pktid;
+ u8 cmd_type;
+ u8 macid;
+ u8 src_pid;
+ u8 src_qid;
+ u8 dst_pid;
+ u8 dst_qid;
+ u16 pktid;
+};
+
+struct rtw89_mac_dbg_port_info {
+ u32 sel_addr;
+ u8 sel_byte;
+ u32 sel_msk;
+ u32 srt;
+ u32 end;
+ u32 rd_addr;
+ u8 rd_byte;
+ u32 rd_msk;
+};
+
+#define QLNKTBL_ADDR_INFO_SEL BIT(0)
+#define QLNKTBL_ADDR_INFO_SEL_0 0
+#define QLNKTBL_ADDR_INFO_SEL_1 1
+#define QLNKTBL_ADDR_TBL_IDX_MASK GENMASK(10, 1)
+#define QLNKTBL_DATA_SEL1_PKT_CNT_MASK GENMASK(11, 0)
+
+struct rtw89_mac_dle_dfi_ctrl {
+ enum rtw89_mac_dle_ctrl_type type;
+ u32 target;
+ u32 addr;
+ u32 out_data;
+};
+
+struct rtw89_mac_dle_dfi_quota {
+ enum rtw89_mac_dle_ctrl_type dle_type;
+ u32 qtaid;
+ u16 rsv_pgnum;
+ u16 use_pgnum;
+};
+
+struct rtw89_mac_dle_dfi_qempty {
+ enum rtw89_mac_dle_ctrl_type dle_type;
+ u32 grpsel;
+ u32 qempty;
+};
+
+/* Define DBG and recovery enum */
+enum mac_ax_err_info {
+ /* Get error info */
+
+ /* L0 */
+ MAC_AX_ERR_L0_ERR_CMAC0 = 0x0001,
+ MAC_AX_ERR_L0_ERR_CMAC1 = 0x0002,
+ MAC_AX_ERR_L0_RESET_DONE = 0x0003,
+ MAC_AX_ERR_L0_PROMOTE_TO_L1 = 0x0010,
+
+ /* L1 */
+ MAC_AX_ERR_L1_ERR_DMAC = 0x1000,
+ MAC_AX_ERR_L1_RESET_DISABLE_DMAC_DONE = 0x1001,
+ MAC_AX_ERR_L1_RESET_RECOVERY_DONE = 0x1002,
+ MAC_AX_ERR_L1_PROMOTE_TO_L2 = 0x1010,
+ MAC_AX_ERR_L1_RCVY_STOP_DONE = 0x1011,
+
+ /* L2 */
+ /* address hole (master) */
+ MAC_AX_ERR_L2_ERR_AH_DMA = 0x2000,
+ MAC_AX_ERR_L2_ERR_AH_HCI = 0x2010,
+ MAC_AX_ERR_L2_ERR_AH_RLX4081 = 0x2020,
+ MAC_AX_ERR_L2_ERR_AH_IDDMA = 0x2030,
+ MAC_AX_ERR_L2_ERR_AH_HIOE = 0x2040,
+ MAC_AX_ERR_L2_ERR_AH_IPSEC = 0x2050,
+ MAC_AX_ERR_L2_ERR_AH_RX4281 = 0x2060,
+ MAC_AX_ERR_L2_ERR_AH_OTHERS = 0x2070,
+
+ /* AHB bridge timeout (master) */
+ MAC_AX_ERR_L2_ERR_AHB_TO_DMA = 0x2100,
+ MAC_AX_ERR_L2_ERR_AHB_TO_HCI = 0x2110,
+ MAC_AX_ERR_L2_ERR_AHB_TO_RLX4081 = 0x2120,
+ MAC_AX_ERR_L2_ERR_AHB_TO_IDDMA = 0x2130,
+ MAC_AX_ERR_L2_ERR_AHB_TO_HIOE = 0x2140,
+ MAC_AX_ERR_L2_ERR_AHB_TO_IPSEC = 0x2150,
+ MAC_AX_ERR_L2_ERR_AHB_TO_RX4281 = 0x2160,
+ MAC_AX_ERR_L2_ERR_AHB_TO_OTHERS = 0x2170,
+
+ /* APB_SA bridge timeout (master + slave) */
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_DMA_WVA = 0x2200,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_DMA_UART = 0x2201,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_DMA_CPULOCAL = 0x2202,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_DMA_AXIDMA = 0x2203,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_DMA_HIOE = 0x2204,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_DMA_IDDMA = 0x2205,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_DMA_IPSEC = 0x2206,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_DMA_WON = 0x2207,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_DMA_WDMAC = 0x2208,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_DMA_WCMAC = 0x2209,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_DMA_OTHERS = 0x220A,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_HCI_WVA = 0x2210,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_HCI_UART = 0x2211,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_HCI_CPULOCAL = 0x2212,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_HCI_AXIDMA = 0x2213,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_HCI_HIOE = 0x2214,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_HCI_IDDMA = 0x2215,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_HCI_IPSEC = 0x2216,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_HCI_WDMAC = 0x2218,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_HCI_WCMAC = 0x2219,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_HCI_OTHERS = 0x221A,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_RLX4081_WVA = 0x2220,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_RLX4081_UART = 0x2221,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_RLX4081_CPULOCAL = 0x2222,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_RLX4081_AXIDMA = 0x2223,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_RLX4081_HIOE = 0x2224,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_RLX4081_IDDMA = 0x2225,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_RLX4081_IPSEC = 0x2226,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_RLX4081_WON = 0x2227,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_RLX4081_WDMAC = 0x2228,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_RLX4081_WCMAC = 0x2229,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_RLX4081_OTHERS = 0x222A,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_IDDMA_WVA = 0x2230,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_IDDMA_UART = 0x2231,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_IDDMA_CPULOCAL = 0x2232,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_IDDMA_AXIDMA = 0x2233,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_IDDMA_HIOE = 0x2234,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_IDDMA_IDDMA = 0x2235,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_IDDMA_IPSEC = 0x2236,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_IDDMA_WON = 0x2237,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_IDDMA_WDMAC = 0x2238,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_IDDMA_WCMAC = 0x2239,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_IDDMA_OTHERS = 0x223A,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_HIOE_WVA = 0x2240,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_HIOE_UART = 0x2241,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_HIOE_CPULOCAL = 0x2242,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_HIOE_AXIDMA = 0x2243,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_HIOE_HIOE = 0x2244,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_HIOE_IDDMA = 0x2245,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_HIOE_IPSEC = 0x2246,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_HIOE_WON = 0x2247,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_HIOE_WDMAC = 0x2248,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_HIOE_WCMAC = 0x2249,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_HIOE_OTHERS = 0x224A,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_IPSEC_WVA = 0x2250,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_IPSEC_UART = 0x2251,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_IPSEC_CPULOCAL = 0x2252,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_IPSEC_AXIDMA = 0x2253,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_IPSEC_HIOE = 0x2254,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_IPSEC_IDDMA = 0x2255,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_IPSEC_IPSEC = 0x2256,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_IPSEC_WON = 0x2257,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_IPSEC_WDMAC = 0x2258,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_IPSEC_WCMAC = 0x2259,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_IPSEC_OTHERS = 0x225A,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_RX4281_WVA = 0x2260,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_RX4281_UART = 0x2261,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_RX4281_CPULOCAL = 0x2262,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_RX4281_AXIDMA = 0x2263,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_RX4281_HIOE = 0x2264,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_RX4281_IDDMA = 0x2265,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_RX4281_IPSEC = 0x2266,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_RX4281_WON = 0x2267,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_RX4281_WDMAC = 0x2268,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_RX4281_WCMAC = 0x2269,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_RX4281_OTHERS = 0x226A,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_OTHERS_WVA = 0x2270,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_OTHERS_UART = 0x2271,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_OTHERS_CPULOCAL = 0x2272,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_OTHERS_AXIDMA = 0x2273,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_OTHERS_HIOE = 0x2274,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_OTHERS_IDDMA = 0x2275,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_OTHERS_IPSEC = 0x2276,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_OTHERS_WON = 0x2277,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_OTHERS_WDMAC = 0x2278,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_OTHERS_WCMAC = 0x2279,
+ MAC_AX_ERR_L2_ERR_APB_SA_TO_OTHERS_OTHERS = 0x227A,
+
+ /* APB_BBRF bridge timeout (master) */
+ MAC_AX_ERR_L2_ERR_APB_BBRF_TO_DMA = 0x2300,
+ MAC_AX_ERR_L2_ERR_APB_BBRF_TO_HCI = 0x2310,
+ MAC_AX_ERR_L2_ERR_APB_BBRF_TO_RLX4081 = 0x2320,
+ MAC_AX_ERR_L2_ERR_APB_BBRF_TO_IDDMA = 0x2330,
+ MAC_AX_ERR_L2_ERR_APB_BBRF_TO_HIOE = 0x2340,
+ MAC_AX_ERR_L2_ERR_APB_BBRF_TO_IPSEC = 0x2350,
+ MAC_AX_ERR_L2_ERR_APB_BBRF_TO_RX4281 = 0x2360,
+ MAC_AX_ERR_L2_ERR_APB_BBRF_TO_OTHERS = 0x2370,
+ MAC_AX_ERR_L2_RESET_DONE = 0x2400,
+ MAC_AX_ERR_CPU_EXCEPTION = 0x3000,
+ MAC_AX_GET_ERR_MAX,
+ MAC_AX_DUMP_SHAREBUFF_INDICATOR = 0x80000000,
+
+ /* set error info */
+ MAC_AX_ERR_L1_DISABLE_EN = 0x0001,
+ MAC_AX_ERR_L1_RCVY_EN = 0x0002,
+ MAC_AX_ERR_L1_RCVY_STOP_REQ = 0x0003,
+ MAC_AX_ERR_L1_RCVY_START_REQ = 0x0004,
+ MAC_AX_ERR_L0_CFG_NOTIFY = 0x0010,
+ MAC_AX_ERR_L0_CFG_DIS_NOTIFY = 0x0011,
+ MAC_AX_ERR_L0_CFG_HANDSHAKE = 0x0012,
+ MAC_AX_ERR_L0_RCVY_EN = 0x0013,
+ MAC_AX_SET_ERR_MAX,
+};
+
+extern struct rtw89_hfc_prec_cfg rtw_hfc_preccfg_pcie;
+extern struct rtw89_hfc_prec_cfg rtw_hfc_preccfg_pcie_wd128;
+extern struct rtw89_hfc_prec_cfg rtw_hfc_preccfg_pcie_stf;
+extern struct rtw89_dle_size wde_size0;
+extern struct rtw89_dle_size wde_size1;
+extern struct rtw89_dle_size wde_size2;
+extern struct rtw89_dle_size wde_size3;
+extern struct rtw89_dle_size wde_size4;
+extern struct rtw89_dle_size wde_size5;
+extern struct rtw89_dle_size wde_size6;
+extern struct rtw89_dle_size wde_size7;
+extern struct rtw89_dle_size wde_size8;
+extern struct rtw89_dle_size wde_size9;
+extern struct rtw89_dle_size wde_size10;
+extern struct rtw89_dle_size ple_size0;
+extern struct rtw89_dle_size ple_size2;
+extern struct rtw89_dle_size ple_size3;
+extern struct rtw89_dle_size ple_size4;
+extern struct rtw89_dle_size ple_size5;
+extern struct rtw89_dle_size ple_size6;
+extern struct rtw89_dle_size ple_size7;
+extern struct rtw89_dle_size ple_size8;
+extern struct rtw89_dle_size ple_size9;
+extern struct rtw89_dle_size ple_size10;
+extern struct rtw89_wde_quota wde_qt0;
+extern struct rtw89_wde_quota wde_qt1;
+extern struct rtw89_wde_quota wde_qt2;
+extern struct rtw89_wde_quota wde_qt3;
+extern struct rtw89_wde_quota wde_qt4;
+extern struct rtw89_wde_quota wde_qt5;
+extern struct rtw89_wde_quota wde_qt6;
+extern struct rtw89_wde_quota wde_qt7;
+extern struct rtw89_wde_quota wde_qt8;
+extern struct rtw89_wde_quota wde_qt9;
+extern struct rtw89_ple_quota ple_qt0;
+extern struct rtw89_ple_quota ple_qt1;
+extern struct rtw89_ple_quota ple_qt4;
+extern struct rtw89_ple_quota ple_qt5;
+extern struct rtw89_ple_quota ple_qt8;
+extern struct rtw89_ple_quota ple_qt9;
+extern struct rtw89_ple_quota ple_qt10;
+extern struct rtw89_ple_quota ple_qt11;
+extern struct rtw89_ple_quota ple_qt12;
+extern struct rtw89_ple_quota ple_qt13;