Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/net/wireless/realtek/rtw88/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 113 kB image not shown  

Quelle  coex.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/* Copyright(c) 2018-2019  Realtek Corporation
 */


#include "main.h"
#include "coex.h"
#include "fw.h"
#include "ps.h"
#include "debug.h"
#include "reg.h"
#include "phy.h"

static u8 rtw_coex_next_rssi_state(struct rtw_dev *rtwdev, u8 pre_state,
       u8 rssi, u8 rssi_thresh)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 u8 tol = chip->rssi_tolerance;
 u8 next_state;

 if (pre_state == COEX_RSSI_STATE_LOW ||
     pre_state == COEX_RSSI_STATE_STAY_LOW) {
  if (rssi >= (rssi_thresh + tol))
   next_state = COEX_RSSI_STATE_HIGH;
  else
   next_state = COEX_RSSI_STATE_STAY_LOW;
 } else {
  if (rssi < rssi_thresh)
   next_state = COEX_RSSI_STATE_LOW;
  else
   next_state = COEX_RSSI_STATE_STAY_HIGH;
 }

 return next_state;
}

static void rtw_coex_limited_tx(struct rtw_dev *rtwdev,
    bool tx_limit_en, bool ampdu_limit_en)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 u8 num_of_active_port = 1;

 if (!chip->scbd_support)
  return;

 /* force max tx retry limit = 8 */
 if (coex_stat->wl_tx_limit_en == tx_limit_en &&
     coex_stat->wl_ampdu_limit_en == ampdu_limit_en)
  return;

 if (!coex_stat->wl_tx_limit_en) {
  coex_stat->darfrc = rtw_read32(rtwdev, REG_DARFRC);
  coex_stat->darfrch = rtw_read32(rtwdev, REG_DARFRCH);
  coex_stat->retry_limit = rtw_read16(rtwdev, REG_RETRY_LIMIT);
 }

 if (!coex_stat->wl_ampdu_limit_en)
  coex_stat->ampdu_max_time =
    rtw_read8(rtwdev, REG_AMPDU_MAX_TIME_V1);

 coex_stat->wl_tx_limit_en = tx_limit_en;
 coex_stat->wl_ampdu_limit_en = ampdu_limit_en;

 if (tx_limit_en) {
  /* set BT polluted packet on for tx rate adaptive,
 * not including tx retry broken by PTA
 */

  rtw_write8_set(rtwdev, REG_TX_HANG_CTRL, BIT_EN_GNT_BT_AWAKE);

  /* set queue life time to avoid can't reach tx retry limit
 * if tx is always broken by GNT_BT
 */

  if (num_of_active_port <= 1)
   rtw_write8_set(rtwdev, REG_LIFETIME_EN, 0xf);
  rtw_write16(rtwdev, REG_RETRY_LIMIT, 0x0808);

  /* auto rate fallback step within 8 retries */
  rtw_write32(rtwdev, REG_DARFRC, 0x1000000);
  rtw_write32(rtwdev, REG_DARFRCH, 0x4030201);
 } else {
  rtw_write8_clr(rtwdev, REG_TX_HANG_CTRL, BIT_EN_GNT_BT_AWAKE);
  rtw_write8_clr(rtwdev, REG_LIFETIME_EN, 0xf);

  rtw_write16(rtwdev, REG_RETRY_LIMIT, coex_stat->retry_limit);
  rtw_write32(rtwdev, REG_DARFRC, coex_stat->darfrc);
  rtw_write32(rtwdev, REG_DARFRCH, coex_stat->darfrch);
 }

 if (ampdu_limit_en)
  rtw_write8(rtwdev, REG_AMPDU_MAX_TIME_V1, 0x20);
 else
  rtw_write8(rtwdev, REG_AMPDU_MAX_TIME_V1,
      coex_stat->ampdu_max_time);
}

static void rtw_coex_limited_wl(struct rtw_dev *rtwdev)
{
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_dm *coex_dm = &coex->dm;
 bool tx_limit = false;
 bool tx_agg_ctrl = false;

 if (!coex->under_5g && coex_dm->bt_status != COEX_BTSTATUS_NCON_IDLE) {
  tx_limit = true;
  tx_agg_ctrl = true;
 }

 rtw_coex_limited_tx(rtwdev, tx_limit, tx_agg_ctrl);
}

static bool rtw_coex_freerun_check(struct rtw_dev *rtwdev)
{
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_dm *coex_dm = &coex->dm;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 struct rtw_efuse *efuse = &rtwdev->efuse;
 u8 bt_rssi;
 u8 ant_distance = 10;

 if (coex_stat->bt_disabled)
  return false;

 if (efuse->share_ant || ant_distance <= 5 || !coex_stat->wl_gl_busy)
  return false;

 if (ant_distance >= 40 || coex_stat->bt_hid_pair_num >= 2)
  return true;

 /* ant_distance = 5 ~ 40  */
 if (COEX_RSSI_HIGH(coex_dm->wl_rssi_state[1]) &&
     COEX_RSSI_HIGH(coex_dm->bt_rssi_state[0]))
  return true;

 if (coex_stat->wl_tput_dir == COEX_WL_TPUT_TX)
  bt_rssi = coex_dm->bt_rssi_state[0];
 else
  bt_rssi = coex_dm->bt_rssi_state[1];

 if (COEX_RSSI_HIGH(coex_dm->wl_rssi_state[3]) &&
     COEX_RSSI_HIGH(bt_rssi) &&
     coex_stat->cnt_wl[COEX_CNT_WL_SCANAP] <= 5)
  return true;

 return false;
}

static void rtw_coex_wl_slot_extend(struct rtw_dev *rtwdev, bool enable)
{
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 u8 para[6] = {0};

 para[0] = COEX_H2C69_WL_LEAKAP;
 para[1] = PARA1_H2C69_DIS_5MS;

 if (enable)
  para[1] = PARA1_H2C69_EN_5MS;
 else
  coex_stat->cnt_wl[COEX_CNT_WL_5MS_NOEXTEND] = 0;

 coex_stat->wl_slot_extend = enable;
 rtw_fw_bt_wifi_control(rtwdev, para[0], ¶[1]);
}

static void rtw_coex_wl_ccklock_action(struct rtw_dev *rtwdev)
{
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;

 if (coex->manual_control || coex->stop_dm)
  return;


 if (coex_stat->tdma_timer_base == 3 && coex_stat->wl_slot_extend) {
  rtw_dbg(rtwdev, RTW_DBG_COEX,
   "[BTCoex], set h2c 0x69 opcode 12 to turn off 5ms WL slot extend!!\n");
  rtw_coex_wl_slot_extend(rtwdev, false);
  return;
 }

 if (coex_stat->wl_slot_extend && coex_stat->wl_force_lps_ctrl &&
     !coex_stat->wl_cck_lock_ever) {
  if (coex_stat->wl_fw_dbg_info[7] <= 5)
   coex_stat->cnt_wl[COEX_CNT_WL_5MS_NOEXTEND]++;
  else
   coex_stat->cnt_wl[COEX_CNT_WL_5MS_NOEXTEND] = 0;

  rtw_dbg(rtwdev, RTW_DBG_COEX,
   "[BTCoex], 5ms WL slot extend cnt = %d!!\n",
   coex_stat->cnt_wl[COEX_CNT_WL_5MS_NOEXTEND]);

  if (coex_stat->cnt_wl[COEX_CNT_WL_5MS_NOEXTEND] == 7) {
   rtw_dbg(rtwdev, RTW_DBG_COEX,
    "[BTCoex], set h2c 0x69 opcode 12 to turn off 5ms WL slot extend!!\n");
   rtw_coex_wl_slot_extend(rtwdev, false);
  }
 } else if (!coex_stat->wl_slot_extend && coex_stat->wl_cck_lock) {
  rtw_dbg(rtwdev, RTW_DBG_COEX,
   "[BTCoex], set h2c 0x69 opcode 12 to turn on 5ms WL slot extend!!\n");

  rtw_coex_wl_slot_extend(rtwdev, true);
 }
}

static void rtw_coex_wl_ccklock_detect(struct rtw_dev *rtwdev)
{
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 struct rtw_coex_dm *coex_dm = &coex->dm;

 bool is_cck_lock_rate = false;

 if (coex_stat->wl_coex_mode != COEX_WLINK_2G1PORT &&
     coex_stat->wl_coex_mode != COEX_WLINK_2GFREE)
  return;

 if (coex_dm->bt_status == COEX_BTSTATUS_INQ_PAGE ||
     coex_stat->bt_setup_link) {
  coex_stat->wl_cck_lock = false;
  coex_stat->wl_cck_lock_pre = false;
  return;
 }

 if (coex_stat->wl_rx_rate <= COEX_CCK_2 ||
     coex_stat->wl_rts_rx_rate <= COEX_CCK_2)
  is_cck_lock_rate = true;

 if (coex_stat->wl_connected && coex_stat->wl_gl_busy &&
     COEX_RSSI_HIGH(coex_dm->wl_rssi_state[3]) &&
     (coex_dm->bt_status == COEX_BTSTATUS_ACL_BUSY ||
      coex_dm->bt_status == COEX_BTSTATUS_ACL_SCO_BUSY ||
      coex_dm->bt_status == COEX_BTSTATUS_SCO_BUSY)) {
  if (is_cck_lock_rate) {
   coex_stat->wl_cck_lock = true;

   rtw_dbg(rtwdev, RTW_DBG_COEX,
    "[BTCoex], cck locking...\n");

  } else {
   coex_stat->wl_cck_lock = false;

   rtw_dbg(rtwdev, RTW_DBG_COEX,
    "[BTCoex], cck unlock...\n");
  }
 } else {
  coex_stat->wl_cck_lock = false;
 }

 /* CCK lock identification */
 if (coex_stat->wl_cck_lock && !coex_stat->wl_cck_lock_pre)
  ieee80211_queue_delayed_work(rtwdev->hw, &coex->wl_ccklock_work,
          3 * HZ);

 coex_stat->wl_cck_lock_pre = coex_stat->wl_cck_lock;
}

static void rtw_coex_wl_noisy_detect(struct rtw_dev *rtwdev)
{
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 struct rtw_dm_info *dm_info = &rtwdev->dm_info;
 u32 cnt_cck;
 bool wl_cck_lock = false;

 /* wifi noisy environment identification */
 cnt_cck = dm_info->cck_ok_cnt + dm_info->cck_err_cnt;

 if (!coex_stat->wl_gl_busy && !wl_cck_lock) {
  if (cnt_cck > 250) {
   if (coex_stat->cnt_wl[COEX_CNT_WL_NOISY2] < 5)
    coex_stat->cnt_wl[COEX_CNT_WL_NOISY2]++;

   if (coex_stat->cnt_wl[COEX_CNT_WL_NOISY2] == 5) {
    coex_stat->cnt_wl[COEX_CNT_WL_NOISY0] = 0;
    coex_stat->cnt_wl[COEX_CNT_WL_NOISY1] = 0;
   }
  } else if (cnt_cck < 100) {
   if (coex_stat->cnt_wl[COEX_CNT_WL_NOISY0] < 5)
    coex_stat->cnt_wl[COEX_CNT_WL_NOISY0]++;

   if (coex_stat->cnt_wl[COEX_CNT_WL_NOISY0] == 5) {
    coex_stat->cnt_wl[COEX_CNT_WL_NOISY1] = 0;
    coex_stat->cnt_wl[COEX_CNT_WL_NOISY2] = 0;
   }
  } else {
   if (coex_stat->cnt_wl[COEX_CNT_WL_NOISY1] < 5)
    coex_stat->cnt_wl[COEX_CNT_WL_NOISY1]++;

   if (coex_stat->cnt_wl[COEX_CNT_WL_NOISY1] == 5) {
    coex_stat->cnt_wl[COEX_CNT_WL_NOISY0] = 0;
    coex_stat->cnt_wl[COEX_CNT_WL_NOISY2] = 0;
   }
  }

  if (coex_stat->cnt_wl[COEX_CNT_WL_NOISY2] == 5)
   coex_stat->wl_noisy_level = 2;
  else if (coex_stat->cnt_wl[COEX_CNT_WL_NOISY1] == 5)
   coex_stat->wl_noisy_level = 1;
  else
   coex_stat->wl_noisy_level = 0;

  rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], wl_noisy_level = %d\n",
   coex_stat->wl_noisy_level);
 }
}

static void rtw_coex_tdma_timer_base(struct rtw_dev *rtwdev, u8 type)
{
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 u8 para[6] = {};
 u8 times;
 u16 tbtt_interval = coex_stat->wl_beacon_interval;

 if (coex_stat->tdma_timer_base == type)
  return;

 coex_stat->tdma_timer_base = type;

 para[0] = COEX_H2C69_TDMA_SLOT;

 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], tbtt_interval = %d\n",
  tbtt_interval);

 if (type == TDMA_TIMER_TYPE_4SLOT && tbtt_interval < 120) {
  para[1] = PARA1_H2C69_TDMA_4SLOT; /* 4-slot */
 } else if (tbtt_interval < 80 && tbtt_interval > 0) {
  times = 100 / tbtt_interval;
  if (100 % tbtt_interval != 0)
   times++;

  para[1] = FIELD_PREP(PARA1_H2C69_TBTT_TIMES, times);
 } else if (tbtt_interval >= 180) {
  times = tbtt_interval / 100;
  if (tbtt_interval % 100 <= 80)
   times--;

  para[1] = FIELD_PREP(PARA1_H2C69_TBTT_TIMES, times) |
     FIELD_PREP(PARA1_H2C69_TBTT_DIV100, 1);
 } else {
  para[1] = PARA1_H2C69_TDMA_2SLOT;
 }

 rtw_fw_bt_wifi_control(rtwdev, para[0], ¶[1]);

 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s(): h2c_0x69 = 0x%x\n",
  __func__, para[1]);

 /* no 5ms_wl_slot_extend for 4-slot mode  */
 if (coex_stat->tdma_timer_base == 3)
  rtw_coex_wl_ccklock_action(rtwdev);
}

static void rtw_coex_set_wl_pri_mask(struct rtw_dev *rtwdev, u8 bitmap,
         u8 data)
{
 u32 addr;

 addr = REG_BT_COEX_TABLE_H + (bitmap / 8);
 bitmap = bitmap % 8;

 rtw_write8_mask(rtwdev, addr, BIT(bitmap), data);
}

void rtw_coex_write_scbd(struct rtw_dev *rtwdev, u16 bitpos, bool set)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 u16 val = 0x2;

 if (!chip->scbd_support)
  return;

 val |= coex_stat->score_board;

 /* for 8822b, scbd[10] is CQDDR on
 * for 8822c, scbd[10] is no fix 2M
 */

 if (!chip->new_scbd10_def && (bitpos & COEX_SCBD_FIX2M)) {
  if (set)
   val &= ~COEX_SCBD_FIX2M;
  else
   val |= COEX_SCBD_FIX2M;
 } else {
  if (set)
   val |= bitpos;
  else
   val &= ~bitpos;
 }

 if (val != coex_stat->score_board) {
  coex_stat->score_board = val;
  val |= BIT_BT_INT_EN;
  rtw_write16(rtwdev, REG_WIFI_BT_INFO, val);
 }
}
EXPORT_SYMBOL(rtw_coex_write_scbd);

static u16 rtw_coex_read_scbd(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;

 if (!chip->scbd_support)
  return 0;

 return (rtw_read16(rtwdev, REG_WIFI_BT_INFO)) & ~(BIT_BT_INT_EN);
}

static void rtw_coex_check_rfk(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 struct rtw_coex_rfe *coex_rfe = &coex->rfe;
 u8 cnt = 0;
 u32 wait_cnt;
 bool btk, wlk;

 if (coex_rfe->wlg_at_btg && chip->scbd_support &&
     coex_stat->bt_iqk_state != 0xff) {
  rtw_dbg(rtwdev, RTW_DBG_COEX,
   "[BTCoex], (Before Ant Setup) Delay by IQK\n");

  wait_cnt = COEX_RFK_TIMEOUT / COEX_MIN_DELAY;
  do {
   /* BT RFK */
   btk = !!(rtw_coex_read_scbd(rtwdev) & COEX_SCBD_BT_RFK);

   /* WL RFK */
   wlk = !!(rtw_read8(rtwdev, REG_ARFR4) & BIT_WL_RFK);

   if (!btk && !wlk)
    break;

   rtw_dbg(rtwdev, RTW_DBG_COEX,
    "[BTCoex], (Before Ant Setup) wlk = %d, btk = %d\n",
    wlk, btk);

   mdelay(COEX_MIN_DELAY);
  } while (++cnt < wait_cnt);

  if (cnt >= wait_cnt)
   coex_stat->bt_iqk_state = 0xff;
 }
}

void rtw_coex_query_bt_info(struct rtw_dev *rtwdev)
{
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;

 if (coex_stat->bt_disabled)
  return;

 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__);

 rtw_fw_query_bt_info(rtwdev);
}

static void rtw_coex_gnt_workaround(struct rtw_dev *rtwdev, bool force, u8 mode)
{
 rtw_coex_set_gnt_fix(rtwdev);
}

static void rtw_coex_monitor_bt_ctr(struct rtw_dev *rtwdev)
{
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 u32 tmp;

 tmp = rtw_read32(rtwdev, REG_BT_ACT_STATISTICS);
 coex_stat->hi_pri_tx = FIELD_GET(MASKLWORD, tmp);
 coex_stat->hi_pri_rx = FIELD_GET(MASKHWORD, tmp);

 tmp = rtw_read32(rtwdev, REG_BT_ACT_STATISTICS_1);
 coex_stat->lo_pri_tx = FIELD_GET(MASKLWORD, tmp);
 coex_stat->lo_pri_rx = FIELD_GET(MASKHWORD, tmp);

 rtw_write8(rtwdev, REG_BT_COEX_ENH_INTR_CTRL,
     BIT_R_GRANTALL_WLMASK | BIT_STATIS_BT_EN);

 rtw_dbg(rtwdev, RTW_DBG_COEX,
  "[BTCoex], Hi-Pri Rx/Tx: %d/%d, Lo-Pri Rx/Tx: %d/%d\n",
  coex_stat->hi_pri_rx, coex_stat->hi_pri_tx,
  coex_stat->lo_pri_rx, coex_stat->lo_pri_tx);
}

static void rtw_coex_monitor_bt_enable(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 struct rtw_coex_dm *coex_dm = &coex->dm;
 bool bt_disabled = false;
 bool bt_active = true;
 u16 score_board;

 if (chip->scbd_support) {
  score_board = rtw_coex_read_scbd(rtwdev);
  bt_disabled = !(score_board & COEX_SCBD_ONOFF);
 } else {
  if (coex_stat->hi_pri_tx == 0 && coex_stat->hi_pri_rx == 0 &&
      coex_stat->lo_pri_tx == 0 && coex_stat->lo_pri_rx == 0)
   bt_active = false;

  if (coex_stat->hi_pri_tx == 0xffff && coex_stat->hi_pri_rx == 0xffff &&
      coex_stat->lo_pri_tx == 0xffff && coex_stat->lo_pri_rx == 0xffff)
   bt_active = false;

  if (bt_active) {
   coex_stat->bt_disable_cnt = 0;
   bt_disabled = false;
  } else {
   coex_stat->bt_disable_cnt++;
   if (coex_stat->bt_disable_cnt >= 10)
    bt_disabled = true;
  }
 }

 if (coex_stat->bt_disabled != bt_disabled) {
  rtw_dbg(rtwdev, RTW_DBG_COEX,
   "[BTCoex], BT state changed (%d) -> (%d)\n",
   coex_stat->bt_disabled, bt_disabled);

  coex_stat->bt_disabled = bt_disabled;
  coex_stat->bt_ble_scan_type = 0;
  coex_dm->cur_bt_lna_lvl = 0;

  if (!coex_stat->bt_disabled) {
   coex_stat->bt_reenable = true;
   ieee80211_queue_delayed_work(rtwdev->hw,
           &coex->bt_reenable_work,
           15 * HZ);
  } else {
   coex_stat->bt_mailbox_reply = false;
   coex_stat->bt_reenable = false;
  }
 }
}

static void rtw_coex_update_wl_link_info(struct rtw_dev *rtwdev, u8 reason)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 struct rtw_coex_dm *coex_dm = &coex->dm;
 struct rtw_traffic_stats *stats = &rtwdev->stats;
 bool is_5G = false;
 bool wl_busy = false;
 bool scan = false, link = false;
 int i;
 u8 rssi_state;
 u8 rssi_step;
 u8 rssi;

 scan = test_bit(RTW_FLAG_SCANNING, rtwdev->flags);
 coex_stat->wl_connected = !!rtwdev->sta_cnt;

 wl_busy = test_bit(RTW_FLAG_BUSY_TRAFFIC, rtwdev->flags);
 if (wl_busy != coex_stat->wl_gl_busy) {
  if (wl_busy)
   coex_stat->wl_gl_busy = true;
  else
   ieee80211_queue_delayed_work(rtwdev->hw,
           &coex->wl_remain_work,
           12 * HZ);
 }

 if (stats->tx_throughput > stats->rx_throughput)
  coex_stat->wl_tput_dir = COEX_WL_TPUT_TX;
 else
  coex_stat->wl_tput_dir = COEX_WL_TPUT_RX;

 if (scan || link || reason == COEX_RSN_2GCONSTART ||
     reason == COEX_RSN_2GSCANSTART || reason == COEX_RSN_2GSWITCHBAND)
  coex_stat->wl_linkscan_proc = true;
 else
  coex_stat->wl_linkscan_proc = false;

 rtw_coex_wl_noisy_detect(rtwdev);

 for (i = 0; i < 4; i++) {
  rssi_state = coex_dm->wl_rssi_state[i];
  rssi_step = chip->wl_rssi_step[i];
  rssi = rtwdev->dm_info.min_rssi;
  rssi_state = rtw_coex_next_rssi_state(rtwdev, rssi_state,
            rssi, rssi_step);
  coex_dm->wl_rssi_state[i] = rssi_state;
 }

 if (coex_stat->wl_linkscan_proc || coex_stat->wl_hi_pri_task1 ||
     coex_stat->wl_hi_pri_task2 || coex_stat->wl_gl_busy)
  rtw_coex_write_scbd(rtwdev, COEX_SCBD_SCAN, true);
 else
  rtw_coex_write_scbd(rtwdev, COEX_SCBD_SCAN, false);

 switch (reason) {
 case COEX_RSN_5GSCANSTART:
 case COEX_RSN_5GSWITCHBAND:
 case COEX_RSN_5GCONSTART:

  is_5G = true;
  break;
 case COEX_RSN_2GSCANSTART:
 case COEX_RSN_2GSWITCHBAND:
 case COEX_RSN_2GCONSTART:

  is_5G = false;
  break;
 default:
  if (rtwdev->hal.current_band_type == RTW_BAND_5G)
   is_5G = true;
  else
   is_5G = false;
  break;
 }

 coex->under_5g = is_5G;
}

static inline u8 *get_payload_from_coex_resp(struct sk_buff *resp)
{
 struct rtw_c2h_cmd *c2h;
 u32 pkt_offset;

 pkt_offset = *((u32 *)resp->cb);
 c2h = (struct rtw_c2h_cmd *)(resp->data + pkt_offset);

 return c2h->payload;
}

void rtw_coex_info_response(struct rtw_dev *rtwdev, struct sk_buff *skb)
{
 struct rtw_coex *coex = &rtwdev->coex;
 u8 *payload = get_payload_from_coex_resp(skb);

 if (payload[0] != COEX_RESP_ACK_BY_WL_FW) {
  dev_kfree_skb_any(skb);
  return;
 }

 skb_queue_tail(&coex->queue, skb);
 wake_up(&coex->wait);
}

static struct sk_buff *rtw_coex_info_request(struct rtw_dev *rtwdev,
          struct rtw_coex_info_req *req)
{
 struct rtw_coex *coex = &rtwdev->coex;
 struct sk_buff *skb_resp = NULL;

 lockdep_assert_held(&rtwdev->mutex);

 rtw_fw_query_bt_mp_info(rtwdev, req);

 if (!wait_event_timeout(coex->wait, !skb_queue_empty(&coex->queue),
    COEX_REQUEST_TIMEOUT)) {
  rtw_err(rtwdev, "coex request time out\n");
  goto out;
 }

 skb_resp = skb_dequeue(&coex->queue);
 if (!skb_resp) {
  rtw_err(rtwdev, "failed to get coex info response\n");
  goto out;
 }

out:
 return skb_resp;
}

static bool rtw_coex_get_bt_scan_type(struct rtw_dev *rtwdev, u8 *scan_type)
{
 struct rtw_coex_info_req req = {0};
 struct sk_buff *skb;
 u8 *payload;

 req.op_code = BT_MP_INFO_OP_SCAN_TYPE;
 skb = rtw_coex_info_request(rtwdev, &req);
 if (!skb)
  return false;

 payload = get_payload_from_coex_resp(skb);
 *scan_type = GET_COEX_RESP_BT_SCAN_TYPE(payload);
 dev_kfree_skb_any(skb);
 return true;
}

static bool rtw_coex_set_lna_constrain_level(struct rtw_dev *rtwdev,
          u8 lna_constrain_level)
{
 struct rtw_coex_info_req req = {0};
 struct sk_buff *skb;

 req.op_code = BT_MP_INFO_OP_LNA_CONSTRAINT;
 req.para1 = lna_constrain_level;
 skb = rtw_coex_info_request(rtwdev, &req);
 if (!skb)
  return false;

 dev_kfree_skb_any(skb);
 return true;
}

#define case_BTSTATUS(src) \
 case COEX_BTSTATUS_##src: return #src

static const char *rtw_coex_get_bt_status_string(u8 bt_status)
{
 switch (bt_status) {
 case_BTSTATUS(NCON_IDLE);
 case_BTSTATUS(CON_IDLE);
 case_BTSTATUS(INQ_PAGE);
 case_BTSTATUS(ACL_BUSY);
 case_BTSTATUS(SCO_BUSY);
 case_BTSTATUS(ACL_SCO_BUSY);
 default:
  return "Unknown";
 }
}

static void rtw_coex_update_bt_link_info(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 struct rtw_coex_dm *coex_dm = &coex->dm;
 u8 i;
 u8 rssi_state;
 u8 rssi_step;
 u8 rssi;

 /* update wl/bt rssi by btinfo */
 for (i = 0; i < COEX_RSSI_STEP; i++) {
  rssi_state = coex_dm->bt_rssi_state[i];
  rssi_step = chip->bt_rssi_step[i];
  rssi = coex_stat->bt_rssi;
  rssi_state = rtw_coex_next_rssi_state(rtwdev, rssi_state, rssi,
            rssi_step);
  coex_dm->bt_rssi_state[i] = rssi_state;
 }

 if (coex_stat->bt_ble_scan_en &&
     coex_stat->cnt_bt[COEX_CNT_BT_INFOUPDATE] % 3 == 0) {
  u8 scan_type;

  if (rtw_coex_get_bt_scan_type(rtwdev, &scan_type)) {
   coex_stat->bt_ble_scan_type = scan_type;
   if ((coex_stat->bt_ble_scan_type & 0x1) == 0x1)
    coex_stat->bt_init_scan = true;
   else
    coex_stat->bt_init_scan = false;
  }
 }

 coex_stat->bt_profile_num = 0;

 /* set link exist status */
 if (!(coex_stat->bt_info_lb2 & COEX_INFO_CONNECTION)) {
  coex_stat->bt_link_exist = false;
  coex_stat->bt_pan_exist = false;
  coex_stat->bt_a2dp_exist = false;
  coex_stat->bt_hid_exist = false;
  coex_stat->bt_hfp_exist = false;
 } else {
  /* connection exists */
  coex_stat->bt_link_exist = true;
  if (coex_stat->bt_info_lb2 & COEX_INFO_FTP) {
   coex_stat->bt_pan_exist = true;
   coex_stat->bt_profile_num++;
  } else {
   coex_stat->bt_pan_exist = false;
  }

  if (coex_stat->bt_info_lb2 & COEX_INFO_A2DP) {
   coex_stat->bt_a2dp_exist = true;
   coex_stat->bt_profile_num++;
  } else {
   coex_stat->bt_a2dp_exist = false;
  }

  if (coex_stat->bt_info_lb2 & COEX_INFO_HID) {
   coex_stat->bt_hid_exist = true;
   coex_stat->bt_profile_num++;
  } else {
   coex_stat->bt_hid_exist = false;
  }

  if (coex_stat->bt_info_lb2 & COEX_INFO_SCO_ESCO) {
   coex_stat->bt_hfp_exist = true;
   coex_stat->bt_profile_num++;
  } else {
   coex_stat->bt_hfp_exist = false;
  }
 }

 if (coex_stat->bt_info_lb2 & COEX_INFO_INQ_PAGE) {
  coex_dm->bt_status = COEX_BTSTATUS_INQ_PAGE;
 } else if (!(coex_stat->bt_info_lb2 & COEX_INFO_CONNECTION)) {
  coex_dm->bt_status = COEX_BTSTATUS_NCON_IDLE;
  coex_stat->bt_multi_link_remain = false;
 } else if (coex_stat->bt_info_lb2 == COEX_INFO_CONNECTION) {
  coex_dm->bt_status = COEX_BTSTATUS_CON_IDLE;
 } else if ((coex_stat->bt_info_lb2 & COEX_INFO_SCO_ESCO) ||
     (coex_stat->bt_info_lb2 & COEX_INFO_SCO_BUSY)) {
  if (coex_stat->bt_info_lb2 & COEX_INFO_ACL_BUSY)
   coex_dm->bt_status = COEX_BTSTATUS_ACL_SCO_BUSY;
  else
   coex_dm->bt_status = COEX_BTSTATUS_SCO_BUSY;
 } else if (coex_stat->bt_info_lb2 & COEX_INFO_ACL_BUSY) {
  coex_dm->bt_status = COEX_BTSTATUS_ACL_BUSY;
 } else {
  coex_dm->bt_status = COEX_BTSTATUS_MAX;
 }

 coex_stat->cnt_bt[COEX_CNT_BT_INFOUPDATE]++;

 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s(), %s!!!\n", __func__,
  rtw_coex_get_bt_status_string(coex_dm->bt_status));
}

static void rtw_coex_update_wl_ch_info(struct rtw_dev *rtwdev, u8 type)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_efuse *efuse = &rtwdev->efuse;
 struct rtw_coex_dm *coex_dm = &rtwdev->coex.dm;
 struct rtw_coex_stat *coex_stat = &rtwdev->coex.stat;
 u8 link = 0;
 u8 center_chan = 0;
 u8 bw;
 int i;

 bw = rtwdev->hal.current_band_width;

 if (type != COEX_MEDIA_DISCONNECT)
  center_chan = rtwdev->hal.current_channel;

 if (center_chan == 0 ||
     (efuse->share_ant && center_chan <= 14 &&
      coex_stat->wl_coex_mode != COEX_WLINK_2GFREE)) {
  link = 0;
  center_chan = 0;
  bw = 0;
 } else if (center_chan <= 14) {
  link = 0x1;

  if (bw == RTW_CHANNEL_WIDTH_40)
   bw = chip->bt_afh_span_bw40;
  else
   bw = chip->bt_afh_span_bw20;
 } else if (chip->afh_5g_num > 1) {
  for (i = 0; i < chip->afh_5g_num; i++) {
   if (center_chan == chip->afh_5g[i].wl_5g_ch) {
    link = 0x3;
    center_chan = chip->afh_5g[i].bt_skip_ch;
    bw = chip->afh_5g[i].bt_skip_span;
    break;
   }
  }
 }

 coex_dm->wl_ch_info[0] = link;
 coex_dm->wl_ch_info[1] = center_chan;
 coex_dm->wl_ch_info[2] = bw;

 rtw_fw_wl_ch_info(rtwdev, link, center_chan, bw);
 rtw_dbg(rtwdev, RTW_DBG_COEX,
  "[BTCoex], %s: para[0:2] = 0x%x 0x%x 0x%x\n", __func__, link,
  center_chan, bw);
}

static void rtw_coex_set_bt_tx_power(struct rtw_dev *rtwdev, u8 bt_pwr_dec_lvl)
{
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_dm *coex_dm = &coex->dm;

 if (bt_pwr_dec_lvl == coex_dm->cur_bt_pwr_lvl)
  return;

 coex_dm->cur_bt_pwr_lvl = bt_pwr_dec_lvl;

 rtw_fw_force_bt_tx_power(rtwdev, bt_pwr_dec_lvl);
}

static void rtw_coex_set_bt_rx_gain(struct rtw_dev *rtwdev, u8 bt_lna_lvl)
{
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_dm *coex_dm = &coex->dm;

 if (bt_lna_lvl == coex_dm->cur_bt_lna_lvl)
  return;

 coex_dm->cur_bt_lna_lvl = bt_lna_lvl;

 /* notify BT rx gain table changed */
 if (bt_lna_lvl < 7) {
  rtw_coex_set_lna_constrain_level(rtwdev, bt_lna_lvl);
  rtw_coex_write_scbd(rtwdev, COEX_SCBD_RXGAIN, true);
 } else {
  rtw_coex_write_scbd(rtwdev, COEX_SCBD_RXGAIN, false);
 }
 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s(): bt_rx_LNA_level = %d\n",
  __func__, bt_lna_lvl);
}

static void rtw_coex_set_rf_para(struct rtw_dev *rtwdev,
     struct coex_rf_para para)
{
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 u8 offset = 0;

 if (coex->freerun && coex_stat->cnt_wl[COEX_CNT_WL_SCANAP] <= 5)
  offset = 3;

 rtw_coex_set_wl_tx_power(rtwdev, para.wl_pwr_dec_lvl);
 rtw_coex_set_bt_tx_power(rtwdev, para.bt_pwr_dec_lvl + offset);
 rtw_coex_set_wl_rx_gain(rtwdev, para.wl_low_gain_en);
 rtw_coex_set_bt_rx_gain(rtwdev, para.bt_lna_lvl);
}

u32 rtw_coex_read_indirect_reg(struct rtw_dev *rtwdev, u16 addr)
{
 u32 val;

 if (!ltecoex_read_reg(rtwdev, addr, &val)) {
  rtw_err(rtwdev, "failed to read indirect register\n");
  return 0;
 }

 return val;
}
EXPORT_SYMBOL(rtw_coex_read_indirect_reg);

void rtw_coex_write_indirect_reg(struct rtw_dev *rtwdev, u16 addr,
     u32 mask, u32 val)
{
 u32 shift = __ffs(mask);
 u32 tmp;

 tmp = rtw_coex_read_indirect_reg(rtwdev, addr);
 tmp = (tmp & (~mask)) | ((val << shift) & mask);

 if (!ltecoex_reg_write(rtwdev, addr, tmp))
  rtw_err(rtwdev, "failed to write indirect register\n");
}
EXPORT_SYMBOL(rtw_coex_write_indirect_reg);

static void rtw_coex_coex_ctrl_owner(struct rtw_dev *rtwdev, bool wifi_control)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 const struct rtw_hw_reg *btg_reg = chip->btg_reg;

 if (wifi_control) {
  rtw_write8_set(rtwdev, REG_SYS_SDIO_CTRL + 3,
          BIT_LTE_MUX_CTRL_PATH >> 24);
  if (btg_reg)
   rtw_write8_set(rtwdev, btg_reg->addr, btg_reg->mask);
 } else {
  rtw_write8_clr(rtwdev, REG_SYS_SDIO_CTRL + 3,
          BIT_LTE_MUX_CTRL_PATH >> 24);
  if (btg_reg)
   rtw_write8_clr(rtwdev, btg_reg->addr, btg_reg->mask);
 }
}

static void rtw_coex_set_gnt_bt(struct rtw_dev *rtwdev, u8 state)
{
 if (!rtwdev->chip->ltecoex_addr)
  return;

 rtw_coex_write_indirect_reg(rtwdev, LTE_COEX_CTRL, 0xc000, state);
 rtw_coex_write_indirect_reg(rtwdev, LTE_COEX_CTRL, 0x0c00, state);
}

static void rtw_coex_set_gnt_wl(struct rtw_dev *rtwdev, u8 state)
{
 if (!rtwdev->chip->ltecoex_addr)
  return;

 rtw_coex_write_indirect_reg(rtwdev, LTE_COEX_CTRL, 0x3000, state);
 rtw_coex_write_indirect_reg(rtwdev, LTE_COEX_CTRL, 0x0300, state);
}

static void rtw_coex_mimo_ps(struct rtw_dev *rtwdev, bool force, bool state)
{
 struct rtw_coex_stat *coex_stat = &rtwdev->coex.stat;

 if (!force && state == coex_stat->wl_mimo_ps)
  return;

 coex_stat->wl_mimo_ps = state;

 rtw_set_txrx_1ss(rtwdev, state);

 rtw_coex_update_wl_ch_info(rtwdev, (u8)coex_stat->wl_connected);

 rtw_dbg(rtwdev, RTW_DBG_COEX,
  "[BTCoex], %s(): state = %d\n", __func__, state);
}

static void rtw_btc_wltoggle_table_a(struct rtw_dev *rtwdev, bool force,
         u8 table_case)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_efuse *efuse = &rtwdev->efuse;
 u8 h2c_para[6] = {0};
 u32 table_wl = 0x5a5a5a5a;

 h2c_para[0] = COEX_H2C69_TOGGLE_TABLE_A;
 /* no definition */
 h2c_para[1] = 0x1;

 if (efuse->share_ant) {
  if (table_case < chip->table_sant_num)
   table_wl = chip->table_sant[table_case].wl;
 } else {
  if (table_case < chip->table_nsant_num)
   table_wl = chip->table_nsant[table_case].wl;
 }

 /* tell WL FW WL slot toggle table-A*/
 h2c_para[2] = (u8)u32_get_bits(table_wl, GENMASK(7, 0));
 h2c_para[3] = (u8)u32_get_bits(table_wl, GENMASK(15, 8));
 h2c_para[4] = (u8)u32_get_bits(table_wl, GENMASK(23, 16));
 h2c_para[5] = (u8)u32_get_bits(table_wl, GENMASK(31, 24));

 rtw_fw_bt_wifi_control(rtwdev, h2c_para[0], &h2c_para[1]);

 rtw_dbg(rtwdev, RTW_DBG_COEX,
  "[BTCoex], %s(): H2C = [%02x %02x %02x %02x %02x %02x]\n",
  __func__, h2c_para[0], h2c_para[1], h2c_para[2],
  h2c_para[3], h2c_para[4], h2c_para[5]);
}

#define COEX_WL_SLOT_TOGLLE 0x5a5a5aaa
static void rtw_btc_wltoggle_table_b(struct rtw_dev *rtwdev, bool force,
         u8 interval, u32 table)
{
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 u8 cur_h2c_para[6] = {0};
 u8 i;

 cur_h2c_para[0] = COEX_H2C69_TOGGLE_TABLE_B;
 cur_h2c_para[1] = interval;
 cur_h2c_para[2] = (u8)u32_get_bits(table, GENMASK(7, 0));
 cur_h2c_para[3] = (u8)u32_get_bits(table, GENMASK(15, 8));
 cur_h2c_para[4] = (u8)u32_get_bits(table, GENMASK(23, 16));
 cur_h2c_para[5] = (u8)u32_get_bits(table, GENMASK(31, 24));

 coex_stat->wl_toggle_interval = interval;

 for (i = 0; i <= 5; i++)
  coex_stat->wl_toggle_para[i] = cur_h2c_para[i];

 rtw_fw_bt_wifi_control(rtwdev, cur_h2c_para[0], &cur_h2c_para[1]);

 rtw_dbg(rtwdev, RTW_DBG_COEX,
  "[BTCoex], %s(): H2C = [%02x %02x %02x %02x %02x %02x]\n",
  __func__, cur_h2c_para[0], cur_h2c_para[1], cur_h2c_para[2],
  cur_h2c_para[3], cur_h2c_para[4], cur_h2c_para[5]);
}

static void rtw_coex_set_table(struct rtw_dev *rtwdev, bool force, u32 table0,
          u32 table1)
{
#define DEF_BRK_TABLE_VAL 0xf0ffffff
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_dm *coex_dm = &coex->dm;

 /* If last tdma is wl slot toggle, force write table*/
 if (!force && coex_dm->reason != COEX_RSN_LPS) {
  if (table0 == rtw_read32(rtwdev, REG_BT_COEX_TABLE0) &&
      table1 == rtw_read32(rtwdev, REG_BT_COEX_TABLE1))
   return;
 }
 rtw_write32(rtwdev, REG_BT_COEX_TABLE0, table0);
 rtw_write32(rtwdev, REG_BT_COEX_TABLE1, table1);
 rtw_write32(rtwdev, REG_BT_COEX_BRK_TABLE, DEF_BRK_TABLE_VAL);

 rtw_dbg(rtwdev, RTW_DBG_COEX,
  "[BTCoex], %s(): 0x6c0 = %x, 0x6c4 = %x\n", __func__, table0,
  table1);
}

static void rtw_coex_table(struct rtw_dev *rtwdev, bool force, u8 type)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_dm *coex_dm = &coex->dm;
 struct rtw_efuse *efuse = &rtwdev->efuse;
 struct rtw_coex_stat *coex_stat = &coex->stat;

 coex_dm->cur_table = type;

 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], Coex_Table - %d\n", type);

 if (efuse->share_ant) {
  if (type < chip->table_sant_num)
   rtw_coex_set_table(rtwdev, force,
        chip->table_sant[type].bt,
        chip->table_sant[type].wl);
 } else {
  type = type - 100;
  if (type < chip->table_nsant_num)
   rtw_coex_set_table(rtwdev, force,
        chip->table_nsant[type].bt,
        chip->table_nsant[type].wl);
 }
 if (coex_stat->wl_slot_toggle_change)
  rtw_btc_wltoggle_table_a(rtwdev, true, type);
}

static void rtw_coex_ignore_wlan_act(struct rtw_dev *rtwdev, bool enable)
{
 struct rtw_coex *coex = &rtwdev->coex;

 if (coex->manual_control || coex->stop_dm)
  return;

 rtw_fw_bt_ignore_wlan_action(rtwdev, enable);
}

static void rtw_coex_power_save_state(struct rtw_dev *rtwdev, u8 ps_type,
          u8 lps_val, u8 rpwm_val)
{
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 u8 lps_mode = 0x0;

 lps_mode = rtwdev->lps_conf.mode;

 switch (ps_type) {
 case COEX_PS_WIFI_NATIVE:
  /* recover to original 32k low power setting */
  coex_stat->wl_force_lps_ctrl = false;
  rtw_dbg(rtwdev, RTW_DBG_COEX,
   "[BTCoex], %s(): COEX_PS_WIFI_NATIVE\n", __func__);
  rtw_leave_lps(rtwdev);
  break;
 case COEX_PS_LPS_OFF:
  coex_stat->wl_force_lps_ctrl = true;
  if (lps_mode)
   rtw_fw_coex_tdma_type(rtwdev, 0, 0, 0, 0, 0);

  rtw_leave_lps(rtwdev);
  rtw_dbg(rtwdev, RTW_DBG_COEX,
   "[BTCoex], %s(): COEX_PS_LPS_OFF\n", __func__);
  break;
 default:
  break;
 }
}

static void rtw_coex_set_tdma(struct rtw_dev *rtwdev, u8 byte1, u8 byte2,
         u8 byte3, u8 byte4, u8 byte5)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_dm *coex_dm = &coex->dm;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 u8 ps_type = COEX_PS_WIFI_NATIVE;
 bool ap_enable = false;

 if (ap_enable && (byte1 & BIT(4) && !(byte1 & BIT(5)))) {
  rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s(): AP mode\n",
   __func__);

  byte1 &= ~BIT(4);
  byte1 |= BIT(5);

  byte5 |= BIT(5);
  byte5 &= ~BIT(6);

  ps_type = COEX_PS_WIFI_NATIVE;
  rtw_coex_power_save_state(rtwdev, ps_type, 0x0, 0x0);
 } else if ((byte1 & BIT(4) && !(byte1 & BIT(5))) ||
     coex_stat->wl_coex_mode == COEX_WLINK_2GFREE) {
  rtw_dbg(rtwdev, RTW_DBG_COEX,
   "[BTCoex], %s(): Force LPS (byte1 = 0x%x)\n", __func__,
   byte1);

  if (chip->pstdma_type == COEX_PSTDMA_FORCE_LPSOFF)
   ps_type = COEX_PS_LPS_OFF;
  else
   ps_type = COEX_PS_LPS_ON;
  rtw_coex_power_save_state(rtwdev, ps_type, 0x50, 0x4);
 } else {
  rtw_dbg(rtwdev, RTW_DBG_COEX,
   "[BTCoex], %s(): native power save (byte1 = 0x%x)\n",
   __func__, byte1);

  ps_type = COEX_PS_WIFI_NATIVE;
  rtw_coex_power_save_state(rtwdev, ps_type, 0x0, 0x0);
 }

 coex_dm->ps_tdma_para[0] = byte1;
 coex_dm->ps_tdma_para[1] = byte2;
 coex_dm->ps_tdma_para[2] = byte3;
 coex_dm->ps_tdma_para[3] = byte4;
 coex_dm->ps_tdma_para[4] = byte5;

 rtw_fw_coex_tdma_type(rtwdev, byte1, byte2, byte3, byte4, byte5);

 if (byte1 & BIT(2)) {
  coex_stat->wl_slot_toggle = true;
  coex_stat->wl_slot_toggle_change = false;
 } else {
  coex_stat->wl_slot_toggle_change = coex_stat->wl_slot_toggle;
  coex_stat->wl_slot_toggle = false;
 }
}

static void rtw_coex_tdma(struct rtw_dev *rtwdev, bool force, u32 tcase)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_dm *coex_dm = &coex->dm;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 struct rtw_efuse *efuse = &rtwdev->efuse;
 u8 n, type;
 bool turn_on;
 bool wl_busy = false;

 if (tcase & TDMA_4SLOT) /* 4-slot (50ms) mode */
  rtw_coex_tdma_timer_base(rtwdev, TDMA_TIMER_TYPE_4SLOT);
 else
  rtw_coex_tdma_timer_base(rtwdev, TDMA_TIMER_TYPE_2SLOT);

 type = (u8)(tcase & 0xff);

 turn_on = (type == 0 || type == 100) ? false : true;

 if (!force && turn_on == coex_dm->cur_ps_tdma_on &&
     type == coex_dm->cur_ps_tdma) {
  rtw_dbg(rtwdev, RTW_DBG_COEX,
   "[BTCoex], Skip TDMA because no change TDMA(%s, %d)\n",
   (coex_dm->cur_ps_tdma_on ? "on" : "off"),
   coex_dm->cur_ps_tdma);

  return;
 }
 wl_busy = test_bit(RTW_FLAG_BUSY_TRAFFIC, rtwdev->flags);

 if ((coex_stat->bt_a2dp_exist &&
      (coex_stat->bt_inq_remain || coex_stat->bt_multi_link)) ||
     !wl_busy)
  rtw_coex_write_scbd(rtwdev, COEX_SCBD_TDMA, false);
 else
  rtw_coex_write_scbd(rtwdev, COEX_SCBD_TDMA, true);

 /* update pre state */
 coex_dm->cur_ps_tdma_on = turn_on;
 coex_dm->cur_ps_tdma = type;

 if (efuse->share_ant) {
  if (type < chip->tdma_sant_num)
   rtw_coex_set_tdma(rtwdev,
       chip->tdma_sant[type].para[0],
       chip->tdma_sant[type].para[1],
       chip->tdma_sant[type].para[2],
       chip->tdma_sant[type].para[3],
       chip->tdma_sant[type].para[4]);
 } else {
  n = type - 100;
  if (n < chip->tdma_nsant_num)
   rtw_coex_set_tdma(rtwdev,
       chip->tdma_nsant[n].para[0],
       chip->tdma_nsant[n].para[1],
       chip->tdma_nsant[n].para[2],
       chip->tdma_nsant[n].para[3],
       chip->tdma_nsant[n].para[4]);
 }


 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], coex tdma type(%s, %d)\n",
  turn_on ? "on" : "off", type);
}

static void rtw_coex_set_ant_path(struct rtw_dev *rtwdev, bool force, u8 phase)
{
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 struct rtw_coex_rfe *coex_rfe = &coex->rfe;
 struct rtw_coex_dm *coex_dm = &coex->dm;
 u8 ctrl_type = COEX_SWITCH_CTRL_MAX;
 u8 pos_type = COEX_SWITCH_TO_MAX;

 if (!force && coex_dm->cur_ant_pos_type == phase)
  return;

 coex_dm->cur_ant_pos_type = phase;

 /* avoid switch coex_ctrl_owner during BT IQK */
 rtw_coex_check_rfk(rtwdev);

 rtw_dbg(rtwdev, RTW_DBG_COEX,
  "[BTCoex], coex_stat->bt_disabled = 0x%x\n",
  coex_stat->bt_disabled);

 switch (phase) {
 case COEX_SET_ANT_POWERON:
  rtw_dbg(rtwdev, RTW_DBG_COEX,
   "[BTCoex], %s() - PHASE_COEX_POWERON\n", __func__);
  /* set path control owner to BT at power-on */
  if (coex_stat->bt_disabled)
   rtw_coex_coex_ctrl_owner(rtwdev, true);
  else
   rtw_coex_coex_ctrl_owner(rtwdev, false);

  ctrl_type = COEX_SWITCH_CTRL_BY_BBSW;
  pos_type = COEX_SWITCH_TO_BT;
  break;
 case COEX_SET_ANT_INIT:
  rtw_dbg(rtwdev, RTW_DBG_COEX,
   "[BTCoex], %s() - PHASE_COEX_INIT\n", __func__);
  if (coex_stat->bt_disabled) {
   /* set GNT_BT to SW low */
   rtw_coex_set_gnt_bt(rtwdev, COEX_GNT_SET_SW_LOW);

   /* set GNT_WL to SW high */
   rtw_coex_set_gnt_wl(rtwdev, COEX_GNT_SET_SW_HIGH);
  } else {
   /* set GNT_BT to SW high */
   rtw_coex_set_gnt_bt(rtwdev, COEX_GNT_SET_SW_HIGH);

   /* set GNT_WL to SW low */
   rtw_coex_set_gnt_wl(rtwdev, COEX_GNT_SET_SW_LOW);
  }

  /* set path control owner to wl at initial step */
  rtw_coex_coex_ctrl_owner(rtwdev, true);

  ctrl_type = COEX_SWITCH_CTRL_BY_BBSW;
  pos_type = COEX_SWITCH_TO_BT;
  break;
 case COEX_SET_ANT_WONLY:
  rtw_dbg(rtwdev, RTW_DBG_COEX,
   "[BTCoex], %s() - PHASE_WLANONLY_INIT\n", __func__);
  /* set GNT_BT to SW Low */
  rtw_coex_set_gnt_bt(rtwdev, COEX_GNT_SET_SW_LOW);

  /* set GNT_WL to SW high */
  rtw_coex_set_gnt_wl(rtwdev, COEX_GNT_SET_SW_HIGH);

  /* set path control owner to wl at initial step */
  rtw_coex_coex_ctrl_owner(rtwdev, true);

  ctrl_type = COEX_SWITCH_CTRL_BY_BBSW;
  pos_type = COEX_SWITCH_TO_WLG;
  break;
 case COEX_SET_ANT_WOFF:
  rtw_dbg(rtwdev, RTW_DBG_COEX,
   "[BTCoex], %s() - PHASE_WLAN_OFF\n", __func__);
  /* set path control owner to BT */
  rtw_coex_coex_ctrl_owner(rtwdev, false);

  ctrl_type = COEX_SWITCH_CTRL_BY_BT;
  pos_type = COEX_SWITCH_TO_NOCARE;
  break;
 case COEX_SET_ANT_2G:
  rtw_dbg(rtwdev, RTW_DBG_COEX,
   "[BTCoex], %s() - PHASE_2G_RUNTIME\n", __func__);
  /* set GNT_BT to PTA */
  rtw_coex_set_gnt_bt(rtwdev, COEX_GNT_SET_HW_PTA);

  /* set GNT_WL to PTA */
  rtw_coex_set_gnt_wl(rtwdev, COEX_GNT_SET_HW_PTA);

  /* set path control owner to wl at runtime step */
  rtw_coex_coex_ctrl_owner(rtwdev, true);

  ctrl_type = COEX_SWITCH_CTRL_BY_PTA;
  pos_type = COEX_SWITCH_TO_NOCARE;
  break;
 case COEX_SET_ANT_5G:
  rtw_dbg(rtwdev, RTW_DBG_COEX,
   "[BTCoex], %s() - PHASE_5G_RUNTIME\n", __func__);

  /* set GNT_BT to HW PTA */
  rtw_coex_set_gnt_bt(rtwdev, COEX_GNT_SET_HW_PTA);

  /* set GNT_WL to SW high */
  rtw_coex_set_gnt_wl(rtwdev, COEX_GNT_SET_SW_HIGH);

  /* set path control owner to wl at runtime step */
  rtw_coex_coex_ctrl_owner(rtwdev, true);

  ctrl_type = COEX_SWITCH_CTRL_BY_BBSW;
  pos_type = COEX_SWITCH_TO_WLA;
  break;
 case COEX_SET_ANT_2G_FREERUN:
  rtw_dbg(rtwdev, RTW_DBG_COEX,
   "[BTCoex], %s() - PHASE_2G_FREERUN\n", __func__);

  /* set GNT_BT to HW PTA */
  rtw_coex_set_gnt_bt(rtwdev, COEX_GNT_SET_HW_PTA);

  /* Set GNT_WL to SW high */
  rtw_coex_set_gnt_wl(rtwdev, COEX_GNT_SET_SW_HIGH);

  /* set path control owner to wl at runtime step */
  rtw_coex_coex_ctrl_owner(rtwdev, true);

  ctrl_type = COEX_SWITCH_CTRL_BY_BBSW;
  pos_type = COEX_SWITCH_TO_WLG_BT;
  break;
 case COEX_SET_ANT_2G_WLBT:
  rtw_dbg(rtwdev, RTW_DBG_COEX,
   "[BTCoex], %s() - PHASE_2G_WLBT\n", __func__);
  /* set GNT_BT to HW PTA */
  rtw_coex_set_gnt_bt(rtwdev, COEX_GNT_SET_HW_PTA);

  /* Set GNT_WL to HW PTA */
  rtw_coex_set_gnt_wl(rtwdev, COEX_GNT_SET_HW_PTA);

  /* set path control owner to wl at runtime step */
  rtw_coex_coex_ctrl_owner(rtwdev, true);

  ctrl_type = COEX_SWITCH_CTRL_BY_BBSW;
  pos_type = COEX_SWITCH_TO_WLG_BT;
  break;
 default:
  WARN(1, "unknown phase when setting antenna path\n");
  return;
 }

 if (ctrl_type < COEX_SWITCH_CTRL_MAX && pos_type < COEX_SWITCH_TO_MAX &&
     coex_rfe->ant_switch_exist)
  rtw_coex_set_ant_switch(rtwdev, ctrl_type, pos_type);
}

#define case_ALGO(src) \
 case COEX_ALGO_##src: return #src

static const char *rtw_coex_get_algo_string(u8 algo)
{
 switch (algo) {
 case_ALGO(NOPROFILE);
 case_ALGO(HFP);
 case_ALGO(HID);
 case_ALGO(A2DP);
 case_ALGO(PAN);
 case_ALGO(A2DP_HID);
 case_ALGO(A2DP_PAN);
 case_ALGO(PAN_HID);
 case_ALGO(A2DP_PAN_HID);
 default:
  return "Unknown";
 }
}

#define case_BT_PROFILE(src) \
 case BPM_##src: return #src

static const char *rtw_coex_get_bt_profile_string(u8 bt_profile)
{
 switch (bt_profile) {
 case_BT_PROFILE(NOPROFILE);
 case_BT_PROFILE(HFP);
 case_BT_PROFILE(HID);
 case_BT_PROFILE(A2DP);
 case_BT_PROFILE(PAN);
 case_BT_PROFILE(HID_HFP);
 case_BT_PROFILE(A2DP_HFP);
 case_BT_PROFILE(A2DP_HID);
 case_BT_PROFILE(A2DP_HID_HFP);
 case_BT_PROFILE(PAN_HFP);
 case_BT_PROFILE(PAN_HID);
 case_BT_PROFILE(PAN_HID_HFP);
 case_BT_PROFILE(PAN_A2DP);
 case_BT_PROFILE(PAN_A2DP_HFP);
 case_BT_PROFILE(PAN_A2DP_HID);
 case_BT_PROFILE(PAN_A2DP_HID_HFP);
 default:
  return "Unknown";
 }
}

static u8 rtw_coex_algorithm(struct rtw_dev *rtwdev)
{
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 u8 algorithm = COEX_ALGO_NOPROFILE;
 u8 profile_map = 0;

 if (coex_stat->bt_hfp_exist)
  profile_map |= BPM_HFP;
 if (coex_stat->bt_hid_exist)
  profile_map |= BPM_HID;
 if (coex_stat->bt_a2dp_exist)
  profile_map |= BPM_A2DP;
 if (coex_stat->bt_pan_exist)
  profile_map |= BPM_PAN;

 switch (profile_map) {
 case BPM_HFP:
  algorithm = COEX_ALGO_HFP;
  break;
 case           BPM_HID:
 case BPM_HFP | BPM_HID:
  algorithm = COEX_ALGO_HID;
  break;
 case BPM_HFP           | BPM_A2DP:
 case           BPM_HID | BPM_A2DP:
 case BPM_HFP | BPM_HID | BPM_A2DP:
  algorithm = COEX_ALGO_A2DP_HID;
  break;
 case BPM_HFP                      | BPM_PAN:
 case           BPM_HID            | BPM_PAN:
 case BPM_HFP | BPM_HID            | BPM_PAN:
  algorithm = COEX_ALGO_PAN_HID;
  break;
 case BPM_HFP           | BPM_A2DP | BPM_PAN:
 case           BPM_HID | BPM_A2DP | BPM_PAN:
 case BPM_HFP | BPM_HID | BPM_A2DP | BPM_PAN:
  algorithm = COEX_ALGO_A2DP_PAN_HID;
  break;
 case                                BPM_PAN:
  algorithm = COEX_ALGO_PAN;
  break;
 case                     BPM_A2DP | BPM_PAN:
  algorithm = COEX_ALGO_A2DP_PAN;
  break;
 case                     BPM_A2DP:
  if (coex_stat->bt_multi_link) {
   if (coex_stat->bt_hid_pair_num > 0)
    algorithm = COEX_ALGO_A2DP_HID;
   else
    algorithm = COEX_ALGO_A2DP_PAN;
  } else {
   algorithm = COEX_ALGO_A2DP;
  }
  break;
 default:
  algorithm = COEX_ALGO_NOPROFILE;
  break;
 }

 rtw_dbg(rtwdev, RTW_DBG_COEX,
  "[BTCoex], BT Profile = %s => Algorithm = %s\n",
  rtw_coex_get_bt_profile_string(profile_map),
  rtw_coex_get_algo_string(algorithm));
 return algorithm;
}

static void rtw_coex_action_coex_all_off(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_efuse *efuse = &rtwdev->efuse;
 u8 table_case, tdma_case;

 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__);
 rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[0]);

 if (efuse->share_ant) {
  /* Shared-Ant */
  table_case = 2;
  tdma_case = 0;
 } else {
  /* Non-Shared-Ant */
  table_case = 100;
  tdma_case = 100;
 }

 rtw_coex_table(rtwdev, false, table_case);
 rtw_coex_tdma(rtwdev, false, tdma_case);
}

static void rtw_coex_action_freerun(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 struct rtw_coex_dm *coex_dm = &coex->dm;
 struct rtw_efuse *efuse = &rtwdev->efuse;
 u8 level = 0;
 bool bt_afh_loss = true;

 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__);

 if (efuse->share_ant)
  return;

 coex->freerun = true;

 if (bt_afh_loss)
  rtw_coex_update_wl_ch_info(rtwdev, COEX_MEDIA_CONNECT);

 rtw_coex_set_ant_path(rtwdev, false, COEX_SET_ANT_2G_FREERUN);

 rtw_coex_write_scbd(rtwdev, COEX_SCBD_FIX2M, false);

 if (COEX_RSSI_HIGH(coex_dm->wl_rssi_state[0]))
  level = 2;
 else if (COEX_RSSI_HIGH(coex_dm->wl_rssi_state[1]))
  level = 3;
 else if (COEX_RSSI_HIGH(coex_dm->wl_rssi_state[2]))
  level = 4;
 else
  level = 5;

 if (level > chip->wl_rf_para_num - 1)
  level = chip->wl_rf_para_num - 1;

 if (coex_stat->wl_tput_dir == COEX_WL_TPUT_TX)
  rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_tx[level]);
 else
  rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[level]);

 rtw_coex_table(rtwdev, false, 100);
 rtw_coex_tdma(rtwdev, false, 100);
}

static void rtw_coex_action_rf4ce(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_efuse *efuse = &rtwdev->efuse;
 u8 table_case, tdma_case;

 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__);

 rtw_coex_set_ant_path(rtwdev, false, COEX_SET_ANT_2G);
 rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[0]);

 if (efuse->share_ant) {
  /* Shared-Ant */
  table_case = 9;
  tdma_case = 16;
 } else {
  /* Non-Shared-Ant */
  table_case = 100;
  tdma_case = 100;
 }

 rtw_coex_table(rtwdev, false, table_case);
 rtw_coex_tdma(rtwdev, false, tdma_case);
}

static void rtw_coex_action_bt_whql_test(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_efuse *efuse = &rtwdev->efuse;
 u8 table_case, tdma_case;

 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__);

 rtw_coex_set_ant_path(rtwdev, false, COEX_SET_ANT_2G);
 rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[0]);

 if (efuse->share_ant) {
  /* Shared-Ant */
  table_case = 2;
  tdma_case = 0;
 } else {
  /* Non-Shared-Ant */
  table_case = 100;
  tdma_case = 100;
 }

 rtw_coex_table(rtwdev, false, table_case);
 rtw_coex_tdma(rtwdev, false, tdma_case);
}

static void rtw_coex_action_bt_relink(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 struct rtw_efuse *efuse = &rtwdev->efuse;
 u8 table_case, tdma_case;
 u32 slot_type = 0;

 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__);

 rtw_coex_set_ant_path(rtwdev, false, COEX_SET_ANT_2G);
 rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[0]);

 if (efuse->share_ant) { /* Shared-Ant */
  if (coex_stat->wl_gl_busy) {
   table_case = 26;
   if (coex_stat->bt_hid_exist &&
       coex_stat->bt_profile_num == 1) {
    slot_type = TDMA_4SLOT;
    tdma_case = 20;
   } else {
    tdma_case = 20;
   }
  } else {
   table_case = 1;
   tdma_case = 0;
  }
 } else { /* Non-Shared-Ant */
  if (coex_stat->wl_gl_busy)
   table_case = 115;
  else
   table_case = 100;
  tdma_case = 100;
 }

 rtw_coex_table(rtwdev, false, table_case);
 rtw_coex_tdma(rtwdev, false, tdma_case | slot_type);
}

static void rtw_coex_action_bt_idle(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 struct rtw_coex_dm *coex_dm = &coex->dm;
 struct rtw_efuse *efuse = &rtwdev->efuse;
 struct rtw_coex_rfe *coex_rfe = &coex->rfe;
 u8 table_case = 0xff, tdma_case = 0xff;

 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__);
 rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[0]);

 if (coex_rfe->ant_switch_with_bt &&
     coex_dm->bt_status == COEX_BTSTATUS_NCON_IDLE) {
  if (efuse->share_ant &&
      COEX_RSSI_HIGH(coex_dm->wl_rssi_state[3]) &&
      coex_stat->wl_gl_busy) {
   table_case = 0;
   tdma_case = 0;
  } else if (!efuse->share_ant) {
   table_case = 100;
   tdma_case = 100;
  }
 }

 if (table_case != 0xff && tdma_case != 0xff) {
  rtw_coex_set_ant_path(rtwdev, false, COEX_SET_ANT_2G_FREERUN);
  goto exit;
 }

 rtw_coex_set_ant_path(rtwdev, false, COEX_SET_ANT_2G);

 if (efuse->share_ant) {
  /* Shared-Ant */
  if (!coex_stat->wl_gl_busy) {
   table_case = 10;
   tdma_case = 3;
  } else if (coex_dm->bt_status == COEX_BTSTATUS_NCON_IDLE) {
   table_case = 11;

   if (coex_stat->lo_pri_rx + coex_stat->lo_pri_tx > 250)
    tdma_case = 17;
   else
    tdma_case = 7;
  } else {
   table_case = 12;
   tdma_case = 7;
  }
 } else {
  /* Non-Shared-Ant */
  if (!coex_stat->wl_gl_busy) {
   table_case = 112;
   tdma_case = 104;
  } else if ((coex_stat->bt_ble_scan_type & 0x2) &&
      coex_dm->bt_status == COEX_BTSTATUS_NCON_IDLE) {
   table_case = 114;
   tdma_case = 103;
  } else {
   table_case = 112;
   tdma_case = 103;
  }
 }

exit:
 rtw_coex_table(rtwdev, false, table_case);
 rtw_coex_tdma(rtwdev, false, tdma_case);
}

static void rtw_coex_action_bt_inquiry(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 struct rtw_efuse *efuse = &rtwdev->efuse;
 bool wl_hi_pri = false;
 u8 table_case, tdma_case;
 u32 slot_type = 0;

 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__);
 rtw_coex_set_ant_path(rtwdev, false, COEX_SET_ANT_2G);
 rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[0]);

 if (coex_stat->wl_linkscan_proc || coex_stat->wl_hi_pri_task1 ||
     coex_stat->wl_hi_pri_task2)
  wl_hi_pri = true;

 if (efuse->share_ant) {
  /* Shared-Ant */
  if (wl_hi_pri) {
   rtw_dbg(rtwdev, RTW_DBG_COEX,
    "[BTCoex], bt inq/page + wifi hi-pri task\n");
   table_case = 15;

   if (coex_stat->bt_profile_num > 0)
    tdma_case = 10;
   else if (coex_stat->wl_hi_pri_task1)
    tdma_case = 6;
   else if (!coex_stat->bt_page)
    tdma_case = 8;
   else
    tdma_case = 9;
  } else if (coex_stat->wl_gl_busy) {
   rtw_dbg(rtwdev, RTW_DBG_COEX,
    "[BTCoex], bt inq/page + wifi busy\n");
   if (coex_stat->bt_profile_num == 0) {
    table_case = 12;
    tdma_case = 18;
   } else if (coex_stat->bt_profile_num == 1 &&
       !coex_stat->bt_a2dp_exist) {
    slot_type = TDMA_4SLOT;
    table_case = 12;
    tdma_case = 20;
   } else {
    slot_type = TDMA_4SLOT;
    table_case = 12;
    tdma_case = 26;
   }
  } else if (coex_stat->wl_connected) {
   rtw_dbg(rtwdev, RTW_DBG_COEX,
    "[BTCoex], bt inq/page + wifi connected\n");
   table_case = 9;
   tdma_case = 27;
  } else {
   rtw_dbg(rtwdev, RTW_DBG_COEX,
    "[BTCoex], bt inq/page + wifi not-connected\n");
   table_case = 1;
   tdma_case = 0;
  }
 } else {
  /* Non_Shared-Ant */
  if (wl_hi_pri) {
   rtw_dbg(rtwdev, RTW_DBG_COEX,
    "[BTCoex], bt inq/page + wifi hi-pri task\n");
   table_case = 114;

   if (coex_stat->bt_profile_num > 0)
    tdma_case = 110;
   else if (coex_stat->wl_hi_pri_task1)
    tdma_case = 106;
   else if (!coex_stat->bt_page)
    tdma_case = 108;
   else
    tdma_case = 109;
  }  else if (coex_stat->wl_gl_busy) {
   rtw_dbg(rtwdev, RTW_DBG_COEX,
    "[BTCoex], bt inq/page + wifi busy\n");
   table_case = 114;
   tdma_case = 121;
  } else if (coex_stat->wl_connected) {
   rtw_dbg(rtwdev, RTW_DBG_COEX,
    "[BTCoex], bt inq/page + wifi connected\n");
   table_case = 101;
   tdma_case = 100;
  } else {
   rtw_dbg(rtwdev, RTW_DBG_COEX,
    "[BTCoex], bt inq/page + wifi not-connected\n");
   table_case = 101;
   tdma_case = 100;
  }
 }

 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], wifi hi(%d), bt page(%d)\n",
  wl_hi_pri, coex_stat->bt_page);

 rtw_coex_table(rtwdev, false, table_case);
 rtw_coex_tdma(rtwdev, false, tdma_case | slot_type);
}

static void rtw_coex_action_bt_game_hid(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 struct rtw_efuse *efuse = &rtwdev->efuse;
 struct rtw_coex_dm *coex_dm = &coex->dm;
 u8 table_case, tdma_case;

 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__);
 rtw_coex_set_ant_path(rtwdev, false, COEX_SET_ANT_2G);

 if (efuse->share_ant) {
  coex_stat->wl_coex_mode = COEX_WLINK_2GFREE;
  if (coex_stat->bt_whck_test)
   table_case = 2;
  else if (coex_stat->wl_linkscan_proc || coex_stat->bt_hid_exist)
   table_case = 33;
  else if (coex_stat->bt_setup_link || coex_stat->bt_inq_page)
   table_case = 0;
  else if (coex_stat->bt_a2dp_exist)
   table_case = 34;
  else
   table_case = 33;

  tdma_case = 0;
 } else {
  if (COEX_RSSI_HIGH(coex_dm->wl_rssi_state[1]))
   tdma_case = 112;
  else
   tdma_case = 113;

  table_case = 121;
 }

 if (coex_stat->wl_coex_mode == COEX_WLINK_2GFREE) {
  if (coex_stat->wl_tput_dir == COEX_WL_TPUT_TX)
   rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_tx[6]);
  else
   rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[5]);
 } else {
  rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[0]);
 }

 rtw_coex_table(rtwdev, false, table_case);
 rtw_coex_tdma(rtwdev, false, tdma_case);
}

static void rtw_coex_action_bt_hfp(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 struct rtw_efuse *efuse = &rtwdev->efuse;
 u8 table_case, tdma_case;

 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__);
 rtw_coex_set_ant_path(rtwdev, false, COEX_SET_ANT_2G);
 rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[0]);

 if (efuse->share_ant) {
  /* Shared-Ant */
  table_case = 10;
  tdma_case = 5;
 } else {
  /* Non-Shared-Ant */
  if (coex_stat->bt_multi_link) {
   table_case = 112;
   tdma_case = 117;
  } else {
   table_case = 105;
   tdma_case = 100;
  }
 }

 rtw_coex_table(rtwdev, false, table_case);
 rtw_coex_tdma(rtwdev, false, tdma_case);
}

static void rtw_coex_action_bt_hid(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 struct rtw_efuse *efuse = &rtwdev->efuse;
 u8 table_case, tdma_case;
 u32 slot_type = 0;
 bool bt_multi_link_remain = false, is_toggle_table = false;

 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__);
 rtw_coex_set_ant_path(rtwdev, false, COEX_SET_ANT_2G);
 rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[0]);

 if (efuse->share_ant) {
  /* Shared-Ant */
  if (coex_stat->bt_ble_exist) {
   /* RCU */
   if (coex_stat->cnt_wl[COEX_CNT_WL_SCANAP] > 5) {
    table_case = 26;
    tdma_case = 2;
   } else {
    table_case = 27;
    tdma_case = 9;
   }
  } else {
   /* Legacy HID  */
   if (coex_stat->bt_profile_num == 1 &&
       (coex_stat->bt_multi_link ||
       (coex_stat->lo_pri_rx +
        coex_stat->lo_pri_tx > 360) ||
        coex_stat->bt_slave ||
        bt_multi_link_remain)) {
    slot_type = TDMA_4SLOT;
    table_case = 12;
    tdma_case = 20;
   } else if (coex_stat->bt_a2dp_active) {
    table_case = 9;
    tdma_case = 18;
   } else if (coex_stat->bt_418_hid_exist &&
       coex_stat->wl_gl_busy) {
    is_toggle_table = true;
    slot_type = TDMA_4SLOT;
    table_case = 9;
    tdma_case = 24;
   } else if (coex_stat->bt_ble_hid_exist &&
       coex_stat->wl_gl_busy) {
    table_case = 32;
    tdma_case = 9;
   } else {
    table_case = 9;
    tdma_case = 9;
   }
  }
 } else {
  /* Non-Shared-Ant */
  if (coex_stat->bt_ble_exist) {
   /* BLE */
   if (coex_stat->cnt_wl[COEX_CNT_WL_SCANAP] > 5) {
    table_case = 121;
    tdma_case = 102;
   } else {
    table_case = 122;
    tdma_case = 109;
   }
  } else if (coex_stat->bt_a2dp_active) {
   table_case = 113;
   tdma_case = 118;
  } else {
   table_case = 113;
   tdma_case = 104;
  }
 }

 rtw_coex_table(rtwdev, false, table_case);
 if (is_toggle_table) {
  rtw_btc_wltoggle_table_a(rtwdev, true, table_case);
  rtw_btc_wltoggle_table_b(rtwdev, false, 1, COEX_WL_SLOT_TOGLLE);
 }

 rtw_coex_tdma(rtwdev, false, tdma_case | slot_type);
}

static void rtw_coex_action_bt_a2dp(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 struct rtw_coex_dm *coex_dm = &coex->dm;
 struct rtw_efuse *efuse = &rtwdev->efuse;
 u8 table_case, tdma_case;
 u32 slot_type = 0;

 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__);

 rtw_coex_set_ant_path(rtwdev, false, COEX_SET_ANT_2G);
 rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[0]);

 slot_type = TDMA_4SLOT;

 if (efuse->share_ant) {
  /* Shared-Ant */
  if (coex_stat->wl_gl_busy && coex_stat->wl_noisy_level == 0)
   table_case = 12;
  else
   table_case = 9;

  if (coex_stat->wl_connecting || !coex_stat->wl_gl_busy)
   tdma_case = 14;
  else
   tdma_case = 13;
 } else {
  /* Non-Shared-Ant */
  table_case = 112;

  if (COEX_RSSI_HIGH(coex_dm->wl_rssi_state[1]))
   tdma_case = 112;
  else
   tdma_case = 113;
 }

 rtw_coex_table(rtwdev, false, table_case);
 rtw_coex_tdma(rtwdev, false, tdma_case | slot_type);
}

static void rtw_coex_action_bt_a2dpsink(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 struct rtw_efuse *efuse = &rtwdev->efuse;
 u8 table_case, tdma_case;
 bool ap_enable = false;

 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__);

 rtw_coex_set_ant_path(rtwdev, false, COEX_SET_ANT_2G);
 rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[0]);

 if (efuse->share_ant) { /* Shared-Ant */
  if (ap_enable) {
   table_case = 2;
   tdma_case = 0;
  } else if (coex_stat->wl_gl_busy) {
   table_case = 28;
   tdma_case = 20;
  } else {
   table_case = 28;
   tdma_case = 26;
  }
 } else { /* Non-Shared-Ant */
  if (ap_enable) {
   table_case = 100;
   tdma_case = 100;
  } else {
   table_case = 119;
   tdma_case = 120;
  }
 }

 rtw_coex_table(rtwdev, false, table_case);
 rtw_coex_tdma(rtwdev, false, tdma_case);
}

static void rtw_coex_action_bt_pan(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 struct rtw_efuse *efuse = &rtwdev->efuse;
 u8 table_case, tdma_case;

 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__);
 rtw_coex_set_ant_path(rtwdev, false, COEX_SET_ANT_2G);
 rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[0]);

 if (efuse->share_ant) {
  /* Shared-Ant */
  if (coex_stat->wl_gl_busy && coex_stat->wl_noisy_level == 0)
   table_case = 14;
  else
   table_case = 10;

  if (coex_stat->wl_gl_busy)
   tdma_case = 17;
  else
   tdma_case = 20;
 } else {
  /* Non-Shared-Ant */
  table_case = 112;

  if (coex_stat->wl_gl_busy)
   tdma_case = 117;
  else
   tdma_case = 119;
 }

 rtw_coex_table(rtwdev, false, table_case);
 rtw_coex_tdma(rtwdev, false, tdma_case);
}

static void rtw_coex_action_bt_a2dp_hid(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 struct rtw_coex_dm *coex_dm = &coex->dm;
 struct rtw_efuse *efuse = &rtwdev->efuse;
 u8 table_case, tdma_case, interval = 0;
 u32 slot_type = 0;
 bool is_toggle_table = false;

 slot_type = TDMA_4SLOT;

 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__);
 rtw_coex_set_ant_path(rtwdev, false, COEX_SET_ANT_2G);
 rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[0]);

 if (efuse->share_ant) {
  /* Shared-Ant */
  if (coex_stat->bt_ble_exist) {
   table_case = 26; /* for RCU */
  } else if (coex_stat->bt_418_hid_exist) {
   table_case = 9;
   interval = 1;
  } else {
   table_case = 9;
  }

  if (coex_stat->wl_connecting || !coex_stat->wl_gl_busy) {
   tdma_case = 14;
  } else if (coex_stat->bt_418_hid_exist) {
   is_toggle_table = true;
   tdma_case = 23;
  } else {
   tdma_case = 13;
  }
 } else {
  /* Non-Shared-Ant */
  if (coex_stat->bt_ble_exist)
   table_case = 121;
  else
   table_case = 113;

  if (COEX_RSSI_HIGH(coex_dm->wl_rssi_state[1]))
   tdma_case = 112;
  else
   tdma_case = 113;
 }

 rtw_coex_table(rtwdev, false, table_case);
 if (is_toggle_table) {
  rtw_btc_wltoggle_table_a(rtwdev, true, table_case);
  rtw_btc_wltoggle_table_b(rtwdev, false, interval, COEX_WL_SLOT_TOGLLE);
 }
 rtw_coex_tdma(rtwdev, false, tdma_case | slot_type);
}

static void rtw_coex_action_bt_a2dp_pan(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 struct rtw_efuse *efuse = &rtwdev->efuse;
 u8 table_case, tdma_case;

 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__);

 rtw_coex_set_ant_path(rtwdev, false, COEX_SET_ANT_2G);
 rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[0]);
 if (efuse->share_ant) {
  /* Shared-Ant */
  if (coex_stat->wl_gl_busy &&
      coex_stat->wl_noisy_level == 0)
   table_case = 14;
  else
   table_case = 10;

  if (coex_stat->wl_gl_busy)
   tdma_case = 15;
  else
   tdma_case = 20;
 } else {
  /* Non-Shared-Ant */
  table_case = 112;

  if (coex_stat->wl_gl_busy)
   tdma_case = 115;
  else
   tdma_case = 120;
 }

 rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[0]);
 rtw_coex_table(rtwdev, false, table_case);
 rtw_coex_tdma(rtwdev, false, tdma_case);
}

static void rtw_coex_action_bt_pan_hid(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 struct rtw_efuse *efuse = &rtwdev->efuse;
 u8 table_case, tdma_case;

 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__);

 rtw_coex_set_ant_path(rtwdev, false, COEX_SET_ANT_2G);
 rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[0]);

 if (efuse->share_ant) {
  /* Shared-Ant */
  table_case = 9;

  if (coex_stat->wl_gl_busy)
   tdma_case = 18;
  else
   tdma_case = 19;
 } else {
  /* Non-Shared-Ant */
  table_case = 113;

  if (coex_stat->wl_gl_busy)
   tdma_case = 117;
  else
   tdma_case = 119;
 }

 rtw_coex_table(rtwdev, false, table_case);
 rtw_coex_tdma(rtwdev, false, tdma_case);
}

static void rtw_coex_action_bt_a2dp_pan_hid(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 struct rtw_efuse *efuse = &rtwdev->efuse;
 u8 table_case, tdma_case;

 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__);
 rtw_coex_set_ant_path(rtwdev, false, COEX_SET_ANT_2G);
 rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[0]);

 if (efuse->share_ant) {
  /* Shared-Ant */
  table_case = 10;

  if (coex_stat->wl_gl_busy)
   tdma_case = 15;
  else
   tdma_case = 20;
 } else {
  /* Non-Shared-Ant */
  table_case = 113;

  if (coex_stat->wl_gl_busy)
   tdma_case = 115;
  else
   tdma_case = 120;
 }

 rtw_coex_table(rtwdev, false, table_case);
 rtw_coex_tdma(rtwdev, false, tdma_case);
}

static void rtw_coex_action_wl_under5g(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_efuse *efuse = &rtwdev->efuse;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 u8 table_case, tdma_case;

 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__);

 rtw_coex_set_ant_path(rtwdev, true, COEX_SET_ANT_5G);
 rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[0]);

 rtw_coex_write_scbd(rtwdev, COEX_SCBD_FIX2M, false);

 if (coex_stat->bt_game_hid_exist && coex_stat->wl_linkscan_proc)
  coex_stat->wl_coex_mode = COEX_WLINK_2GFREE;

 if (efuse->share_ant) {
  /* Shared-Ant */
  table_case = 0;
  tdma_case = 0;
 } else {
  /* Non-Shared-Ant */
  table_case = 100;
  tdma_case = 100;
 }

 rtw_coex_table(rtwdev, false, table_case);
 rtw_coex_tdma(rtwdev, false, tdma_case);
}

static void rtw_coex_action_wl_only(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_efuse *efuse = &rtwdev->efuse;
 u8 table_case, tdma_case;

 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__);
 rtw_coex_set_ant_path(rtwdev, false, COEX_SET_ANT_2G);
 rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[0]);

 if (efuse->share_ant) {
  /* Shared-Ant */
  table_case = 2;
  tdma_case = 0;
 } else {
  /* Non-Shared-Ant */
  table_case = 100;
  tdma_case = 100;
 }

 rtw_coex_table(rtwdev, false, table_case);
 rtw_coex_tdma(rtwdev, false, tdma_case);
}

static void rtw_coex_action_wl_native_lps(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_efuse *efuse = &rtwdev->efuse;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 u8 table_case, tdma_case;

 if (coex->under_5g)
  return;

 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__);

 rtw_coex_set_ant_path(rtwdev, false, COEX_SET_ANT_2G);

 if (efuse->share_ant) {
  /* Shared-Ant */
  table_case = 28;
  tdma_case = 0;
 } else {
  /* Non-Shared-Ant */
  table_case = 100;
  tdma_case = 100;
 }

 if (coex_stat->bt_game_hid_exist) {
  coex_stat->wl_coex_mode = COEX_WLINK_2GFREE;
  if (coex_stat->wl_tput_dir == COEX_WL_TPUT_TX)
   rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_tx[6]);
  else
   rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[5]);
 } else {
  rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[0]);
 }

 rtw_coex_table(rtwdev, false, table_case);
 rtw_coex_tdma(rtwdev, false, tdma_case);
}

static void rtw_coex_action_wl_linkscan(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_coex *coex = &rtwdev->coex;
 struct rtw_coex_stat *coex_stat = &coex->stat;
 struct rtw_efuse *efuse = &rtwdev->efuse;
 u8 table_case, tdma_case;
 u32 slot_type = 0;

 rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__);
 rtw_coex_set_ant_path(rtwdev, false, COEX_SET_ANT_2G);
 rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[0]);

 if (efuse->share_ant) { /* Shared-Ant */
  if (coex_stat->bt_a2dp_exist) {
   slot_type = TDMA_4SLOT;
   tdma_case = 11;
   if (coex_stat->wl_gl_busy)
    table_case = 26;
   else
    table_case = 9;
  } else {
   table_case = 9;
   tdma_case = 7;
  }
 } else { /* Non-Shared-Ant */
  if (coex_stat->bt_a2dp_exist) {
   slot_type = TDMA_4SLOT;
   table_case = 112;
   tdma_case = 111;
  } else {
   table_case = 112;
   tdma_case = 107;
  }
 }

 rtw_coex_table(rtwdev, false, table_case);
 rtw_coex_tdma(rtwdev, false, tdma_case | slot_type);
}

static void rtw_coex_action_wl_not_connected(struct rtw_dev *rtwdev)
{
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=97 H=99 G=97

¤ Dauer der Verarbeitung: 0.30 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.