blob: abe4b6549ab228444fd5029401232b9762037370 [file] [log] [blame]
// 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_ACT1_WORK,
BTC_RSN_BT_DEVINFO_WORK,
BTC_RSN_RFK_CHK_WORK,
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;
u16 tslot_cycle[BTC_CYCLE_SLOT_MAX];
};
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);
__CPY_LE16S(tslot_cycle);
#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(pfinfo, 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 %zu\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 %zu\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(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);
}
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);
}
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;
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_bt_info *bt = &btc->cx.bt;
struct rtw89_btc_rf_trx_para para;
u32 wl_stb_chg = 0;
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 != RTW89_BTC_WL_DEF_TX_PWR)
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);
if (bt->enable.now == 0 || wl->status.map.rf_off == 1 ||
wl->status.map.lps == 1)
wl_stb_chg = 0;
else
wl_stb_chg = 1;
if (wl_stb_chg != dm->wl_stb_chg) {
rtw89_debug(rtwdev, RTW89_DBG_BTC,
"[BTC], %s(): wl_stb_chg=%d\n",
__func__, wl_stb_chg);
dm->wl_stb_chg = wl_stb_chg;
chip->ops->btc_wl_s1_standby(rtwdev, dm->wl_stb_chg);
}
}
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:
bw = 20 + chip->afh_guard_ch * 2;
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 (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