diff --git a/sys/contrib/dev/rtw88/Makefile b/sys/contrib/dev/rtw88/Makefile new file mode 100644 index 000000000000..fd212c09d88a --- /dev/null +++ b/sys/contrib/dev/rtw88/Makefile @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause + +obj-$(CONFIG_RTW88_CORE) += rtw88_core.o +rtw88_core-y += main.o \ + mac80211.o \ + util.o \ + debug.o \ + tx.o \ + rx.o \ + mac.o \ + phy.o \ + coex.o \ + efuse.o \ + fw.o \ + ps.o \ + sec.o \ + bf.o \ + sar.o \ + regd.o + +rtw88_core-$(CONFIG_PM) += wow.o + +obj-$(CONFIG_RTW88_8822B) += rtw88_8822b.o +rtw88_8822b-objs := rtw8822b.o rtw8822b_table.o + +obj-$(CONFIG_RTW88_8822BE) += rtw88_8822be.o +rtw88_8822be-objs := rtw8822be.o + +obj-$(CONFIG_RTW88_8822BS) += rtw88_8822bs.o +rtw88_8822bs-objs := rtw8822bs.o + +obj-$(CONFIG_RTW88_8822BU) += rtw88_8822bu.o +rtw88_8822bu-objs := rtw8822bu.o + +obj-$(CONFIG_RTW88_8822C) += rtw88_8822c.o +rtw88_8822c-objs := rtw8822c.o rtw8822c_table.o + +obj-$(CONFIG_RTW88_8822CE) += rtw88_8822ce.o +rtw88_8822ce-objs := rtw8822ce.o + +obj-$(CONFIG_RTW88_8822CS) += rtw88_8822cs.o +rtw88_8822cs-objs := rtw8822cs.o + +obj-$(CONFIG_RTW88_8822CU) += rtw88_8822cu.o +rtw88_8822cu-objs := rtw8822cu.o + +obj-$(CONFIG_RTW88_8723D) += rtw88_8723d.o +rtw88_8723d-objs := rtw8723d.o rtw8723d_table.o + +obj-$(CONFIG_RTW88_8723DE) += rtw88_8723de.o +rtw88_8723de-objs := rtw8723de.o + +obj-$(CONFIG_RTW88_8723DS) += rtw88_8723ds.o +rtw88_8723ds-objs := rtw8723ds.o + +obj-$(CONFIG_RTW88_8723DU) += rtw88_8723du.o +rtw88_8723du-objs := rtw8723du.o + +obj-$(CONFIG_RTW88_8821C) += rtw88_8821c.o +rtw88_8821c-objs := rtw8821c.o rtw8821c_table.o + +obj-$(CONFIG_RTW88_8821CE) += rtw88_8821ce.o +rtw88_8821ce-objs := rtw8821ce.o + +obj-$(CONFIG_RTW88_8821CS) += rtw88_8821cs.o +rtw88_8821cs-objs := rtw8821cs.o + +obj-$(CONFIG_RTW88_8821CU) += rtw88_8821cu.o +rtw88_8821cu-objs := rtw8821cu.o + +obj-$(CONFIG_RTW88_PCI) += rtw88_pci.o +rtw88_pci-objs := pci.o + +obj-$(CONFIG_RTW88_SDIO) += rtw88_sdio.o +rtw88_sdio-objs := sdio.o + +obj-$(CONFIG_RTW88_USB) += rtw88_usb.o +rtw88_usb-objs := usb.o diff --git a/sys/contrib/dev/rtw88/bf.c b/sys/contrib/dev/rtw88/bf.c index d207fa5c122f..16c6a1d972e5 100644 --- a/sys/contrib/dev/rtw88/bf.c +++ b/sys/contrib/dev/rtw88/bf.c @@ -1,414 +1,415 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* Copyright(c) 2018-2019 Realtek Corporation. */ #include "main.h" #include "reg.h" #include "bf.h" #include "debug.h" void rtw_bf_disassoc(struct rtw_dev *rtwdev, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf) { struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; struct rtw_bfee *bfee = &rtwvif->bfee; struct rtw_bf_info *bfinfo = &rtwdev->bf_info; if (bfee->role == RTW_BFEE_NONE) return; if (bfee->role == RTW_BFEE_MU) bfinfo->bfer_mu_cnt--; else if (bfee->role == RTW_BFEE_SU) bfinfo->bfer_su_cnt--; rtw_chip_config_bfee(rtwdev, rtwvif, bfee, false); bfee->role = RTW_BFEE_NONE; } void rtw_bf_assoc(struct rtw_dev *rtwdev, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf) { + const struct rtw_chip_info *chip = rtwdev->chip; struct ieee80211_hw *hw = rtwdev->hw; struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; struct rtw_bfee *bfee = &rtwvif->bfee; struct rtw_bf_info *bfinfo = &rtwdev->bf_info; - struct rtw_chip_info *chip = rtwdev->chip; struct ieee80211_sta *sta; struct ieee80211_sta_vht_cap *vht_cap; struct ieee80211_sta_vht_cap *ic_vht_cap; const u8 *bssid = bss_conf->bssid; u32 sound_dim; u8 i; if (!(chip->band & RTW_BAND_5G)) return; rcu_read_lock(); sta = ieee80211_find_sta(vif, bssid); if (!sta) { + rcu_read_unlock(); + #if defined(__linux__) rtw_warn(rtwdev, "failed to find station entry for bss %pM\n", bssid); #elif defined(__FreeBSD__) rtw_warn(rtwdev, "failed to find station entry for bss %6D\n", bssid, ":"); #endif - goto out_unlock; + return; } ic_vht_cap = &hw->wiphy->bands[NL80211_BAND_5GHZ]->vht_cap; vht_cap = &sta->deflink.vht_cap; + rcu_read_unlock(); + if ((ic_vht_cap->cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE) && (vht_cap->cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)) { if (bfinfo->bfer_mu_cnt >= chip->bfer_mu_max_num) { rtw_dbg(rtwdev, RTW_DBG_BF, "mu bfer number over limit\n"); - goto out_unlock; + return; } ether_addr_copy(bfee->mac_addr, bssid); bfee->role = RTW_BFEE_MU; bfee->p_aid = (bssid[5] << 1) | (bssid[4] >> 7); - bfee->aid = bss_conf->aid; + bfee->aid = vif->cfg.aid; bfinfo->bfer_mu_cnt++; rtw_chip_config_bfee(rtwdev, rtwvif, bfee, true); } else if ((ic_vht_cap->cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE) && (vht_cap->cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)) { if (bfinfo->bfer_su_cnt >= chip->bfer_su_max_num) { rtw_dbg(rtwdev, RTW_DBG_BF, "su bfer number over limit\n"); - goto out_unlock; + return; } sound_dim = vht_cap->cap & IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK; sound_dim >>= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT; ether_addr_copy(bfee->mac_addr, bssid); bfee->role = RTW_BFEE_SU; bfee->sound_dim = (u8)sound_dim; bfee->g_id = 0; bfee->p_aid = (bssid[5] << 1) | (bssid[4] >> 7); bfinfo->bfer_su_cnt++; for (i = 0; i < chip->bfer_su_max_num; i++) { if (!test_bit(i, bfinfo->bfer_su_reg_maping)) { set_bit(i, bfinfo->bfer_su_reg_maping); bfee->su_reg_index = i; break; } } rtw_chip_config_bfee(rtwdev, rtwvif, bfee, true); } - -out_unlock: - rcu_read_unlock(); } void rtw_bf_init_bfer_entry_mu(struct rtw_dev *rtwdev, struct mu_bfer_init_para *param) { u16 mu_bf_ctl = 0; u8 *addr = param->bfer_address; int i; for (i = 0; i < ETH_ALEN; i++) rtw_write8(rtwdev, REG_ASSOCIATED_BFMER0_INFO + i, addr[i]); rtw_write16(rtwdev, REG_ASSOCIATED_BFMER0_INFO + 6, param->paid); rtw_write16(rtwdev, REG_TX_CSI_RPT_PARAM_BW20, param->csi_para); mu_bf_ctl = rtw_read16(rtwdev, REG_WMAC_MU_BF_CTL) & 0xC000; mu_bf_ctl |= param->my_aid | (param->csi_length_sel << 12); rtw_write16(rtwdev, REG_WMAC_MU_BF_CTL, mu_bf_ctl); } void rtw_bf_cfg_sounding(struct rtw_dev *rtwdev, struct rtw_vif *vif, enum rtw_trx_desc_rate rate) { u32 psf_ctl = 0; u8 csi_rsc = 0x1; psf_ctl = rtw_read32(rtwdev, REG_BBPSF_CTRL) | BIT_WMAC_USE_NDPARATE | (csi_rsc << 13); rtw_write8_mask(rtwdev, REG_SND_PTCL_CTRL, BIT_MASK_BEAMFORM, RTW_SND_CTRL_SOUNDING); rtw_write8(rtwdev, REG_SND_PTCL_CTRL + 3, 0x26); rtw_write8_clr(rtwdev, REG_RXFLTMAP1, BIT_RXFLTMAP1_BF_REPORT_POLL); rtw_write8_clr(rtwdev, REG_RXFLTMAP4, BIT_RXFLTMAP4_BF_REPORT_POLL); if (vif->net_type == RTW_NET_AP_MODE) rtw_write32(rtwdev, REG_BBPSF_CTRL, psf_ctl | BIT(12)); else rtw_write32(rtwdev, REG_BBPSF_CTRL, psf_ctl & ~BIT(12)); } void rtw_bf_cfg_mu_bfee(struct rtw_dev *rtwdev, struct cfg_mumimo_para *param) { u8 mu_tbl_sel; u8 mu_valid; mu_valid = rtw_read8(rtwdev, REG_MU_TX_CTL) & ~BIT_MASK_R_MU_TABLE_VALID; rtw_write8(rtwdev, REG_MU_TX_CTL, (mu_valid | BIT(0) | BIT(1)) & ~(BIT(7))); mu_tbl_sel = rtw_read8(rtwdev, REG_MU_TX_CTL + 1) & 0xF8; rtw_write8(rtwdev, REG_MU_TX_CTL + 1, mu_tbl_sel); rtw_write32(rtwdev, REG_MU_STA_GID_VLD, param->given_gid_tab[0]); rtw_write32(rtwdev, REG_MU_STA_USER_POS_INFO, param->given_user_pos[0]); rtw_write32(rtwdev, REG_MU_STA_USER_POS_INFO + 4, param->given_user_pos[1]); rtw_write8(rtwdev, REG_MU_TX_CTL + 1, mu_tbl_sel | 1); rtw_write32(rtwdev, REG_MU_STA_GID_VLD, param->given_gid_tab[1]); rtw_write32(rtwdev, REG_MU_STA_USER_POS_INFO, param->given_user_pos[2]); rtw_write32(rtwdev, REG_MU_STA_USER_POS_INFO + 4, param->given_user_pos[3]); } void rtw_bf_del_bfer_entry_mu(struct rtw_dev *rtwdev) { rtw_write32(rtwdev, REG_ASSOCIATED_BFMER0_INFO, 0); rtw_write32(rtwdev, REG_ASSOCIATED_BFMER0_INFO + 4, 0); rtw_write16(rtwdev, REG_WMAC_MU_BF_CTL, 0); rtw_write8(rtwdev, REG_MU_TX_CTL, 0); } void rtw_bf_del_sounding(struct rtw_dev *rtwdev) { rtw_write8_mask(rtwdev, REG_SND_PTCL_CTRL, BIT_MASK_BEAMFORM, 0); } void rtw_bf_enable_bfee_su(struct rtw_dev *rtwdev, struct rtw_vif *vif, struct rtw_bfee *bfee) { u8 nc_index = hweight8(rtwdev->hal.antenna_rx) - 1; u8 nr_index = bfee->sound_dim; u8 grouping = 0, codebookinfo = 1, coefficientsize = 3; u32 addr_bfer_info, addr_csi_rpt, csi_param; u8 i; rtw_dbg(rtwdev, RTW_DBG_BF, "config as an su bfee\n"); switch (bfee->su_reg_index) { case 1: addr_bfer_info = REG_ASSOCIATED_BFMER1_INFO; addr_csi_rpt = REG_TX_CSI_RPT_PARAM_BW20 + 2; break; case 0: default: addr_bfer_info = REG_ASSOCIATED_BFMER0_INFO; addr_csi_rpt = REG_TX_CSI_RPT_PARAM_BW20; break; } /* Sounding protocol control */ rtw_write8_mask(rtwdev, REG_SND_PTCL_CTRL, BIT_MASK_BEAMFORM, RTW_SND_CTRL_SOUNDING); /* MAC address/Partial AID of Beamformer */ for (i = 0; i < ETH_ALEN; i++) rtw_write8(rtwdev, addr_bfer_info + i, bfee->mac_addr[i]); csi_param = (u16)((coefficientsize << 10) | (codebookinfo << 8) | (grouping << 6) | (nr_index << 3) | nc_index); rtw_write16(rtwdev, addr_csi_rpt, csi_param); /* ndp rx standby timer */ rtw_write8(rtwdev, REG_SND_PTCL_CTRL + 3, RTW_NDP_RX_STANDBY_TIME); } EXPORT_SYMBOL(rtw_bf_enable_bfee_su); /* nc index: 1 2T2R 0 1T1R * nr index: 1 use Nsts 0 use reg setting * codebookinfo: 1 802.11ac 3 802.11n */ void rtw_bf_enable_bfee_mu(struct rtw_dev *rtwdev, struct rtw_vif *vif, struct rtw_bfee *bfee) { struct rtw_bf_info *bf_info = &rtwdev->bf_info; struct mu_bfer_init_para param; u8 nc_index = hweight8(rtwdev->hal.antenna_rx) - 1; u8 nr_index = 1; u8 grouping = 0, codebookinfo = 1, coefficientsize = 0; u32 csi_param; rtw_dbg(rtwdev, RTW_DBG_BF, "config as an mu bfee\n"); csi_param = (u16)((coefficientsize << 10) | (codebookinfo << 8) | (grouping << 6) | (nr_index << 3) | nc_index); rtw_dbg(rtwdev, RTW_DBG_BF, "nc=%d nr=%d group=%d codebookinfo=%d coefficientsize=%d\n", nc_index, nr_index, grouping, codebookinfo, coefficientsize); param.paid = bfee->p_aid; param.csi_para = csi_param; param.my_aid = bfee->aid & 0xfff; param.csi_length_sel = HAL_CSI_SEG_4K; ether_addr_copy(param.bfer_address, bfee->mac_addr); rtw_bf_init_bfer_entry_mu(rtwdev, ¶m); bf_info->cur_csi_rpt_rate = DESC_RATE6M; rtw_bf_cfg_sounding(rtwdev, vif, DESC_RATE6M); /* accept action_no_ack */ rtw_write16_set(rtwdev, REG_RXFLTMAP0, BIT_RXFLTMAP0_ACTIONNOACK); /* accept NDPA and BF report poll */ rtw_write16_set(rtwdev, REG_RXFLTMAP1, BIT_RXFLTMAP1_BF); } EXPORT_SYMBOL(rtw_bf_enable_bfee_mu); void rtw_bf_remove_bfee_su(struct rtw_dev *rtwdev, struct rtw_bfee *bfee) { struct rtw_bf_info *bfinfo = &rtwdev->bf_info; rtw_dbg(rtwdev, RTW_DBG_BF, "remove as a su bfee\n"); rtw_write8_mask(rtwdev, REG_SND_PTCL_CTRL, BIT_MASK_BEAMFORM, RTW_SND_CTRL_REMOVE); switch (bfee->su_reg_index) { case 0: rtw_write32(rtwdev, REG_ASSOCIATED_BFMER0_INFO, 0); rtw_write16(rtwdev, REG_ASSOCIATED_BFMER0_INFO + 4, 0); rtw_write16(rtwdev, REG_TX_CSI_RPT_PARAM_BW20, 0); break; case 1: rtw_write32(rtwdev, REG_ASSOCIATED_BFMER1_INFO, 0); rtw_write16(rtwdev, REG_ASSOCIATED_BFMER1_INFO + 4, 0); rtw_write16(rtwdev, REG_TX_CSI_RPT_PARAM_BW20 + 2, 0); break; } clear_bit(bfee->su_reg_index, bfinfo->bfer_su_reg_maping); bfee->su_reg_index = 0xFF; } EXPORT_SYMBOL(rtw_bf_remove_bfee_su); void rtw_bf_remove_bfee_mu(struct rtw_dev *rtwdev, struct rtw_bfee *bfee) { struct rtw_bf_info *bfinfo = &rtwdev->bf_info; rtw_write8_mask(rtwdev, REG_SND_PTCL_CTRL, BIT_MASK_BEAMFORM, RTW_SND_CTRL_REMOVE); rtw_bf_del_bfer_entry_mu(rtwdev); if (bfinfo->bfer_su_cnt == 0 && bfinfo->bfer_mu_cnt == 0) rtw_bf_del_sounding(rtwdev); } EXPORT_SYMBOL(rtw_bf_remove_bfee_mu); void rtw_bf_set_gid_table(struct rtw_dev *rtwdev, struct ieee80211_vif *vif, struct ieee80211_bss_conf *conf) { struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; struct rtw_bfee *bfee = &rtwvif->bfee; struct cfg_mumimo_para param; if (bfee->role != RTW_BFEE_MU) { rtw_dbg(rtwdev, RTW_DBG_BF, "this vif is not mu bfee\n"); return; } param.grouping_bitmap = 0; param.mu_tx_en = 0; memset(param.sounding_sts, 0, 6); memcpy(param.given_gid_tab, conf->mu_group.membership, 8); memcpy(param.given_user_pos, conf->mu_group.position, 16); rtw_dbg(rtwdev, RTW_DBG_BF, "STA0: gid_valid=0x%x, user_position_l=0x%x, user_position_h=0x%x\n", param.given_gid_tab[0], param.given_user_pos[0], param.given_user_pos[1]); rtw_dbg(rtwdev, RTW_DBG_BF, "STA1: gid_valid=0x%x, user_position_l=0x%x, user_position_h=0x%x\n", param.given_gid_tab[1], param.given_user_pos[2], param.given_user_pos[3]); rtw_bf_cfg_mu_bfee(rtwdev, ¶m); } EXPORT_SYMBOL(rtw_bf_set_gid_table); void rtw_bf_phy_init(struct rtw_dev *rtwdev) { u8 tmp8; u32 tmp32; u8 retry_limit = 0xA; u8 ndpa_rate = 0x10; u8 ack_policy = 3; tmp32 = rtw_read32(rtwdev, REG_MU_TX_CTL); /* Enable P1 aggr new packet according to P0 transfer time */ tmp32 |= BIT_MU_P1_WAIT_STATE_EN; /* MU Retry Limit */ tmp32 &= ~BIT_MASK_R_MU_RL; tmp32 |= (retry_limit << BIT_SHIFT_R_MU_RL) & BIT_MASK_R_MU_RL; /* Disable Tx MU-MIMO until sounding done */ tmp32 &= ~BIT_EN_MU_MIMO; /* Clear validity of MU STAs */ tmp32 &= ~BIT_MASK_R_MU_TABLE_VALID; rtw_write32(rtwdev, REG_MU_TX_CTL, tmp32); /* MU-MIMO Option as default value */ tmp8 = ack_policy << BIT_SHIFT_WMAC_TXMU_ACKPOLICY; tmp8 |= BIT_WMAC_TXMU_ACKPOLICY_EN; rtw_write8(rtwdev, REG_WMAC_MU_BF_OPTION, tmp8); /* MU-MIMO Control as default value */ rtw_write16(rtwdev, REG_WMAC_MU_BF_CTL, 0); /* Set MU NDPA rate & BW source */ rtw_write32_set(rtwdev, REG_TXBF_CTRL, BIT_USE_NDPA_PARAMETER); /* Set NDPA Rate */ rtw_write8(rtwdev, REG_NDPA_OPT_CTRL, ndpa_rate); rtw_write32_mask(rtwdev, REG_BBPSF_CTRL, BIT_MASK_CSI_RATE, DESC_RATE6M); } EXPORT_SYMBOL(rtw_bf_phy_init); void rtw_bf_cfg_csi_rate(struct rtw_dev *rtwdev, u8 rssi, u8 cur_rate, u8 fixrate_en, u8 *new_rate) { u32 csi_cfg; u16 cur_rrsr; csi_cfg = rtw_read32(rtwdev, REG_BBPSF_CTRL) & ~BIT_MASK_CSI_RATE; cur_rrsr = rtw_read16(rtwdev, REG_RRSR); if (rssi >= 40) { if (cur_rate != DESC_RATE54M) { cur_rrsr |= BIT(DESC_RATE54M); csi_cfg |= (DESC_RATE54M & BIT_MASK_CSI_RATE_VAL) << BIT_SHIFT_CSI_RATE; rtw_write16(rtwdev, REG_RRSR, cur_rrsr); rtw_write32(rtwdev, REG_BBPSF_CTRL, csi_cfg); } *new_rate = DESC_RATE54M; } else { if (cur_rate != DESC_RATE24M) { cur_rrsr &= ~BIT(DESC_RATE54M); csi_cfg |= (DESC_RATE54M & BIT_MASK_CSI_RATE_VAL) << BIT_SHIFT_CSI_RATE; rtw_write16(rtwdev, REG_RRSR, cur_rrsr); rtw_write32(rtwdev, REG_BBPSF_CTRL, csi_cfg); } *new_rate = DESC_RATE24M; } } EXPORT_SYMBOL(rtw_bf_cfg_csi_rate); diff --git a/sys/contrib/dev/rtw88/coex.c b/sys/contrib/dev/rtw88/coex.c index cac053f485c3..86467d2f8888 100644 --- a/sys/contrib/dev/rtw88/coex.c +++ b/sys/contrib/dev/rtw88/coex.c @@ -1,4176 +1,4175 @@ // 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) { - struct rtw_chip_info *chip = rtwdev->chip; + 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) { - struct rtw_chip_info *chip = rtwdev->chip; + 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[2] = {0}; 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) { - struct rtw_chip_info *chip = rtwdev->chip; + 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) { - struct rtw_chip_info *chip = rtwdev->chip; + 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) { - struct rtw_chip_info *chip = rtwdev->chip; + 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; } } static 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) { - struct rtw_chip_info *chip = rtwdev->chip; + 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; u16 score_board; if (chip->scbd_support) { score_board = rtw_coex_read_scbd(rtwdev); bt_disabled = !(score_board & COEX_SCBD_ONOFF); } 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_chip_info *chip = rtwdev->chip; 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; - mutex_lock(&coex->mutex); + 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: - mutex_unlock(&coex->mutex); 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; - struct rtw_chip_info *chip = rtwdev->chip; 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) { - struct rtw_chip_info *chip = rtwdev->chip; + 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) { - struct rtw_chip_info *chip = rtwdev->chip; + 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) { 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) { 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) { - struct rtw_chip_info *chip = rtwdev->chip; + 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_chip_info *chip = rtwdev->chip; 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_chip_info *chip = rtwdev->chip; 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_chip_info *chip = rtwdev->chip; 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; - struct rtw_chip_info *chip = rtwdev->chip; 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; - struct rtw_chip_info *chip = rtwdev->chip; 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; - struct rtw_chip_info *chip = rtwdev->chip; 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; - struct rtw_chip_info *chip = rtwdev->chip; 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; - struct rtw_chip_info *chip = rtwdev->chip; 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_chip_info *chip = rtwdev->chip; 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; - struct rtw_chip_info *chip = rtwdev->chip; 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; - struct rtw_chip_info *chip = rtwdev->chip; 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; - struct rtw_chip_info *chip = rtwdev->chip; 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; - struct rtw_chip_info *chip = rtwdev->chip; 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; - struct rtw_chip_info *chip = rtwdev->chip; 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; - struct rtw_chip_info *chip = rtwdev->chip; 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; - struct rtw_chip_info *chip = rtwdev->chip; 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; - struct rtw_chip_info *chip = rtwdev->chip; 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; - struct rtw_chip_info *chip = rtwdev->chip; u8 table_case, tdma_case; bool wl_cpt_test = false, bt_cpt_test = 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 (wl_cpt_test) { if (coex_stat->wl_gl_busy) { table_case = 20; tdma_case = 17; } else { table_case = 10; tdma_case = 15; } } else if (bt_cpt_test) { table_case = 26; tdma_case = 26; } else { 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; } if (wl_cpt_test) rtw_coex_set_rf_para(rtwdev, chip->wl_rf_para_rx[1]); 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_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; - struct rtw_chip_info *chip = rtwdev->chip; 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; - struct rtw_chip_info *chip = rtwdev->chip; 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_chip_info *chip = rtwdev->chip; 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; - struct rtw_chip_info *chip = rtwdev->chip; 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_chip_info *chip = rtwdev->chip; 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; - struct rtw_chip_info *chip = rtwdev->chip; 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) { + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_efuse *efuse = &rtwdev->efuse; - struct rtw_chip_info *chip = rtwdev->chip; 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 = 1; 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_connected(struct rtw_dev *rtwdev) { struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_stat *coex_stat = &coex->stat; u8 algorithm; rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__); algorithm = rtw_coex_algorithm(rtwdev); switch (algorithm) { case COEX_ALGO_HFP: rtw_coex_action_bt_hfp(rtwdev); break; case COEX_ALGO_HID: if (rtw_coex_freerun_check(rtwdev)) rtw_coex_action_freerun(rtwdev); else rtw_coex_action_bt_hid(rtwdev); break; case COEX_ALGO_A2DP: if (rtw_coex_freerun_check(rtwdev)) rtw_coex_action_freerun(rtwdev); else if (coex_stat->bt_a2dp_sink) rtw_coex_action_bt_a2dpsink(rtwdev); else rtw_coex_action_bt_a2dp(rtwdev); break; case COEX_ALGO_PAN: rtw_coex_action_bt_pan(rtwdev); break; case COEX_ALGO_A2DP_HID: if (rtw_coex_freerun_check(rtwdev)) rtw_coex_action_freerun(rtwdev); else rtw_coex_action_bt_a2dp_hid(rtwdev); break; case COEX_ALGO_A2DP_PAN: rtw_coex_action_bt_a2dp_pan(rtwdev); break; case COEX_ALGO_PAN_HID: rtw_coex_action_bt_pan_hid(rtwdev); break; case COEX_ALGO_A2DP_PAN_HID: rtw_coex_action_bt_a2dp_pan_hid(rtwdev); break; default: case COEX_ALGO_NOPROFILE: rtw_coex_action_bt_idle(rtwdev); break; } } static void rtw_coex_run_coex(struct rtw_dev *rtwdev, u8 reason) { + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_coex *coex = &rtwdev->coex; - struct rtw_chip_info *chip = rtwdev->chip; struct rtw_coex_dm *coex_dm = &coex->dm; struct rtw_coex_stat *coex_stat = &coex->stat; bool rf4ce_en = false; lockdep_assert_held(&rtwdev->mutex); if (!test_bit(RTW_FLAG_RUNNING, rtwdev->flags)) return; coex_dm->reason = reason; rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s(): reason = %d\n", __func__, reason); /* update wifi_link_info_ext variable */ rtw_coex_update_wl_link_info(rtwdev, reason); rtw_coex_monitor_bt_enable(rtwdev); if (coex->manual_control) { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], return for Manual CTRL!!\n"); return; } if (coex->stop_dm) { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], return for Stop Coex DM!!\n"); return; } if (coex_stat->wl_under_ips) { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], return for wifi is under IPS!!\n"); return; } if (coex->freeze && coex_dm->reason == COEX_RSN_BTINFO && !coex_stat->bt_setup_link) { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], return for coex_freeze!!\n"); return; } coex_stat->cnt_wl[COEX_CNT_WL_COEXRUN]++; coex->freerun = false; /* Pure-5G Coex Process */ if (coex->under_5g) { coex_stat->wl_coex_mode = COEX_WLINK_5G; rtw_coex_action_wl_under5g(rtwdev); goto exit; } rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], WiFi is single-port 2G!!\n"); coex_stat->wl_coex_mode = COEX_WLINK_2G1PORT; if (coex_stat->bt_disabled) { if (coex_stat->wl_connected && rf4ce_en) rtw_coex_action_rf4ce(rtwdev); else if (!coex_stat->wl_connected) rtw_coex_action_wl_not_connected(rtwdev); else rtw_coex_action_wl_only(rtwdev); goto exit; } if (coex_stat->wl_under_lps && !coex_stat->wl_force_lps_ctrl) { rtw_coex_action_wl_native_lps(rtwdev); goto exit; } if (coex_stat->bt_game_hid_exist && coex_stat->wl_connected) { rtw_coex_action_bt_game_hid(rtwdev); goto exit; } if (coex_stat->bt_whck_test) { rtw_coex_action_bt_whql_test(rtwdev); goto exit; } if (coex_stat->bt_setup_link) { rtw_coex_action_bt_relink(rtwdev); goto exit; } if (coex_stat->bt_inq_page) { rtw_coex_action_bt_inquiry(rtwdev); goto exit; } if ((coex_dm->bt_status == COEX_BTSTATUS_NCON_IDLE || coex_dm->bt_status == COEX_BTSTATUS_CON_IDLE) && coex_stat->wl_connected) { rtw_coex_action_bt_idle(rtwdev); goto exit; } if (coex_stat->wl_linkscan_proc && !coex->freerun) { rtw_coex_action_wl_linkscan(rtwdev); goto exit; } if (coex_stat->wl_connected) { rtw_coex_action_wl_connected(rtwdev); goto exit; } else { rtw_coex_action_wl_not_connected(rtwdev); goto exit; } exit: if (chip->wl_mimo_ps_support) { if (coex_stat->wl_coex_mode == COEX_WLINK_2GFREE) { if (coex_dm->reason == COEX_RSN_2GMEDIA) rtw_coex_mimo_ps(rtwdev, true, true); else rtw_coex_mimo_ps(rtwdev, false, true); } else { rtw_coex_mimo_ps(rtwdev, false, false); } } rtw_coex_gnt_workaround(rtwdev, false, coex_stat->wl_coex_mode); rtw_coex_limited_wl(rtwdev); } static void rtw_coex_init_coex_var(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; u8 i; memset(coex_dm, 0, sizeof(*coex_dm)); memset(coex_stat, 0, sizeof(*coex_stat)); for (i = 0; i < COEX_CNT_WL_MAX; i++) coex_stat->cnt_wl[i] = 0; for (i = 0; i < COEX_CNT_BT_MAX; i++) coex_stat->cnt_bt[i] = 0; for (i = 0; i < ARRAY_SIZE(coex_dm->bt_rssi_state); i++) coex_dm->bt_rssi_state[i] = COEX_RSSI_STATE_LOW; for (i = 0; i < ARRAY_SIZE(coex_dm->wl_rssi_state); i++) coex_dm->wl_rssi_state[i] = COEX_RSSI_STATE_LOW; coex_stat->wl_coex_mode = COEX_WLINK_MAX; coex_stat->wl_rx_rate = DESC_RATE5_5M; coex_stat->wl_rts_rx_rate = DESC_RATE5_5M; } static void __rtw_coex_init_hw_config(struct rtw_dev *rtwdev, bool wifi_only) { struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_stat *coex_stat = &coex->stat; rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__); rtw_coex_init_coex_var(rtwdev); coex_stat->kt_ver = u8_get_bits(rtw_read8(rtwdev, 0xf1), GENMASK(7, 4)); rtw_coex_monitor_bt_enable(rtwdev); rtw_coex_wl_slot_extend(rtwdev, coex_stat->wl_slot_extend); rtw_write8_set(rtwdev, REG_BCN_CTRL, BIT_EN_BCN_FUNCTION); rtw_coex_set_rfe_type(rtwdev); rtw_coex_set_init(rtwdev); /* set Tx response = Hi-Pri (ex: Transmitting ACK,BA,CTS) */ rtw_coex_set_wl_pri_mask(rtwdev, COEX_WLPRI_TX_RSP, 1); /* set Tx beacon = Hi-Pri */ rtw_coex_set_wl_pri_mask(rtwdev, COEX_WLPRI_TX_BEACON, 1); /* set Tx beacon queue = Hi-Pri */ rtw_coex_set_wl_pri_mask(rtwdev, COEX_WLPRI_TX_BEACONQ, 1); /* antenna config */ if (coex->wl_rf_off) { rtw_coex_set_ant_path(rtwdev, true, COEX_SET_ANT_WOFF); rtw_coex_write_scbd(rtwdev, COEX_SCBD_ALL, false); coex->stop_dm = true; rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s(): RF Off\n", __func__); } else if (wifi_only) { rtw_coex_set_ant_path(rtwdev, true, COEX_SET_ANT_WONLY); rtw_coex_write_scbd(rtwdev, COEX_SCBD_ACTIVE | COEX_SCBD_ONOFF, true); coex->stop_dm = true; } else { rtw_coex_set_ant_path(rtwdev, true, COEX_SET_ANT_INIT); rtw_coex_write_scbd(rtwdev, COEX_SCBD_ACTIVE | COEX_SCBD_ONOFF, true); coex->stop_dm = false; coex->freeze = true; } /* PTA parameter */ rtw_coex_table(rtwdev, true, 1); rtw_coex_tdma(rtwdev, true, 0); rtw_coex_query_bt_info(rtwdev); } void rtw_coex_power_on_setting(struct rtw_dev *rtwdev) { struct rtw_coex *coex = &rtwdev->coex; u8 table_case = 1; rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__); coex->stop_dm = true; coex->wl_rf_off = false; /* enable BB, we can write 0x948 */ rtw_write8_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_BB_GLB_RST | BIT_FEN_BB_RSTB); rtw_coex_monitor_bt_enable(rtwdev); rtw_coex_set_rfe_type(rtwdev); /* set antenna path to BT */ rtw_coex_set_ant_path(rtwdev, true, COEX_SET_ANT_POWERON); rtw_coex_table(rtwdev, true, table_case); /* red x issue */ rtw_write8(rtwdev, 0xff1a, 0x0); rtw_coex_set_gnt_debug(rtwdev); } void rtw_coex_power_off_setting(struct rtw_dev *rtwdev) { rtw_write16(rtwdev, REG_WIFI_BT_INFO, BIT_BT_INT_EN); } void rtw_coex_init_hw_config(struct rtw_dev *rtwdev, bool wifi_only) { __rtw_coex_init_hw_config(rtwdev, wifi_only); } void rtw_coex_ips_notify(struct rtw_dev *rtwdev, u8 type) { struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_stat *coex_stat = &coex->stat; if (coex->manual_control || coex->stop_dm) return; if (type == COEX_IPS_ENTER) { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], IPS ENTER notify\n"); coex_stat->wl_under_ips = true; /* for lps off */ rtw_coex_write_scbd(rtwdev, COEX_SCBD_ALL, false); rtw_coex_set_ant_path(rtwdev, true, COEX_SET_ANT_WOFF); rtw_coex_action_coex_all_off(rtwdev); } else if (type == COEX_IPS_LEAVE) { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], IPS LEAVE notify\n"); rtw_coex_write_scbd(rtwdev, COEX_SCBD_ACTIVE | COEX_SCBD_ONOFF, true); /* run init hw config (exclude wifi only) */ __rtw_coex_init_hw_config(rtwdev, false); coex_stat->wl_under_ips = false; } } void rtw_coex_lps_notify(struct rtw_dev *rtwdev, u8 type) { struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_stat *coex_stat = &coex->stat; if (coex->manual_control || coex->stop_dm) return; if (type == COEX_LPS_ENABLE) { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], LPS ENABLE notify\n"); coex_stat->wl_under_lps = true; if (coex_stat->wl_force_lps_ctrl) { /* for ps-tdma */ rtw_coex_write_scbd(rtwdev, COEX_SCBD_ACTIVE, true); } else { /* for native ps */ rtw_coex_write_scbd(rtwdev, COEX_SCBD_ACTIVE, false); rtw_coex_write_scbd(rtwdev, COEX_SCBD_WLBUSY, false); rtw_coex_run_coex(rtwdev, COEX_RSN_LPS); } } else if (type == COEX_LPS_DISABLE) { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], LPS DISABLE notify\n"); coex_stat->wl_under_lps = false; /* for lps off */ rtw_coex_write_scbd(rtwdev, COEX_SCBD_ACTIVE, true); if (!coex_stat->wl_force_lps_ctrl) rtw_coex_query_bt_info(rtwdev); rtw_coex_run_coex(rtwdev, COEX_RSN_LPS); } } void rtw_coex_scan_notify(struct rtw_dev *rtwdev, u8 type) { struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_stat *coex_stat = &coex->stat; if (coex->manual_control || coex->stop_dm) return; coex->freeze = false; rtw_coex_write_scbd(rtwdev, COEX_SCBD_ACTIVE | COEX_SCBD_ONOFF, true); if (type == COEX_SCAN_START_5G) { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], SCAN START notify (5G)\n"); rtw_coex_set_ant_path(rtwdev, true, COEX_SET_ANT_5G); rtw_coex_run_coex(rtwdev, COEX_RSN_5GSCANSTART); } else if ((type == COEX_SCAN_START_2G) || (type == COEX_SCAN_START)) { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], SCAN START notify (2G)\n"); coex_stat->wl_hi_pri_task2 = true; /* Force antenna setup for no scan result issue */ rtw_coex_set_ant_path(rtwdev, true, COEX_SET_ANT_2G); rtw_coex_run_coex(rtwdev, COEX_RSN_2GSCANSTART); } else { coex_stat->cnt_wl[COEX_CNT_WL_SCANAP] = 30; /* To do */ rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], SCAN FINISH notify (Scan-AP = %d)\n", coex_stat->cnt_wl[COEX_CNT_WL_SCANAP]); coex_stat->wl_hi_pri_task2 = false; rtw_coex_run_coex(rtwdev, COEX_RSN_SCANFINISH); } } void rtw_coex_switchband_notify(struct rtw_dev *rtwdev, u8 type) { struct rtw_coex *coex = &rtwdev->coex; if (coex->manual_control || coex->stop_dm) return; if (type == COEX_SWITCH_TO_5G) { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s(): TO_5G\n", __func__); } else if (type == COEX_SWITCH_TO_24G_NOFORSCAN) { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s(): TO_24G_NOFORSCAN\n", __func__); } else { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s(): TO_2G\n", __func__); } if (type == COEX_SWITCH_TO_5G) rtw_coex_run_coex(rtwdev, COEX_RSN_5GSWITCHBAND); else if (type == COEX_SWITCH_TO_24G_NOFORSCAN) rtw_coex_run_coex(rtwdev, COEX_RSN_2GSWITCHBAND); else rtw_coex_scan_notify(rtwdev, COEX_SCAN_START_2G); } void rtw_coex_connect_notify(struct rtw_dev *rtwdev, u8 type) { struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_stat *coex_stat = &coex->stat; if (coex->manual_control || coex->stop_dm) return; rtw_coex_write_scbd(rtwdev, COEX_SCBD_ACTIVE | COEX_SCBD_ONOFF, true); if (type == COEX_ASSOCIATE_5G_START) { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s(): 5G start\n", __func__); rtw_coex_set_ant_path(rtwdev, true, COEX_SET_ANT_5G); rtw_coex_run_coex(rtwdev, COEX_RSN_5GCONSTART); } else if (type == COEX_ASSOCIATE_5G_FINISH) { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s(): 5G finish\n", __func__); rtw_coex_set_ant_path(rtwdev, true, COEX_SET_ANT_5G); rtw_coex_run_coex(rtwdev, COEX_RSN_5GCONFINISH); } else if (type == COEX_ASSOCIATE_START) { coex_stat->wl_hi_pri_task1 = true; coex_stat->wl_connecting = true; coex_stat->cnt_wl[COEX_CNT_WL_CONNPKT] = 2; coex_stat->wl_connecting = true; ieee80211_queue_delayed_work(rtwdev->hw, &coex->wl_connecting_work, 2 * HZ); rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s(): 2G start\n", __func__); /* Force antenna setup for no scan result issue */ rtw_coex_set_ant_path(rtwdev, true, COEX_SET_ANT_2G); rtw_coex_run_coex(rtwdev, COEX_RSN_2GCONSTART); /* To keep TDMA case during connect process, * to avoid changed by Btinfo and runcoexmechanism */ coex->freeze = true; ieee80211_queue_delayed_work(rtwdev->hw, &coex->defreeze_work, 5 * HZ); } else { coex_stat->wl_hi_pri_task1 = false; coex->freeze = false; coex_stat->wl_connecting = false; rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s(): 2G finish\n", __func__); rtw_coex_run_coex(rtwdev, COEX_RSN_2GCONFINISH); } } void rtw_coex_media_status_notify(struct rtw_dev *rtwdev, u8 type) { struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_stat *coex_stat = &coex->stat; if (coex->manual_control || coex->stop_dm) return; if (type == COEX_MEDIA_CONNECT_5G) { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s(): 5G\n", __func__); rtw_coex_write_scbd(rtwdev, COEX_SCBD_ACTIVE, true); rtw_coex_set_ant_path(rtwdev, true, COEX_SET_ANT_5G); rtw_coex_run_coex(rtwdev, COEX_RSN_5GMEDIA); } else if (type == COEX_MEDIA_CONNECT) { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s(): 2G\n", __func__); coex_stat->wl_connecting = false; rtw_coex_write_scbd(rtwdev, COEX_SCBD_ACTIVE, true); /* Force antenna setup for no scan result issue */ rtw_coex_set_ant_path(rtwdev, true, COEX_SET_ANT_2G); /* Set CCK Rx high Pri */ rtw_coex_set_wl_pri_mask(rtwdev, COEX_WLPRI_RX_CCK, 1); rtw_coex_run_coex(rtwdev, COEX_RSN_2GMEDIA); } else { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s(): disconnect!!\n", __func__); rtw_coex_set_wl_pri_mask(rtwdev, COEX_WLPRI_RX_CCK, 0); rtw_coex_run_coex(rtwdev, COEX_RSN_MEDIADISCON); } rtw_coex_update_wl_ch_info(rtwdev, type); } void rtw_coex_bt_info_notify(struct rtw_dev *rtwdev, u8 *buf, u8 length) { + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_stat *coex_stat = &coex->stat; - struct rtw_chip_info *chip = rtwdev->chip; struct rtw_coex_dm *coex_dm = &coex->dm; u32 bt_relink_time; u8 i, rsp_source = 0, type; bool inq_page = false; rsp_source = buf[0] & 0xf; if (rsp_source >= COEX_BTINFO_SRC_MAX) return; coex_stat->cnt_bt_info_c2h[rsp_source]++; if (rsp_source == COEX_BTINFO_SRC_BT_IQK) { coex_stat->bt_iqk_state = buf[1]; if (coex_stat->bt_iqk_state == 0) coex_stat->cnt_bt[COEX_CNT_BT_IQK]++; else if (coex_stat->bt_iqk_state == 2) coex_stat->cnt_bt[COEX_CNT_BT_IQKFAIL]++; rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], BT IQK by bt_info, data0 = 0x%02x\n", buf[1]); return; } if (rsp_source == COEX_BTINFO_SRC_BT_SCBD) { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], BT Scoreboard change notify by WL FW c2h, 0xaa = 0x%02x, 0xab = 0x%02x\n", buf[1], buf[2]); rtw_coex_monitor_bt_enable(rtwdev); if (coex_stat->bt_disabled != coex_stat->bt_disabled_pre) { coex_stat->bt_disabled_pre = coex_stat->bt_disabled; rtw_coex_run_coex(rtwdev, COEX_RSN_BTINFO); } return; } if (rsp_source == COEX_BTINFO_SRC_H2C60) { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], H2C 0x60 content replied by WL FW: H2C_0x60 = [%02x %02x %02x %02x %02x]\n", buf[1], buf[2], buf[3], buf[4], buf[5]); for (i = 1; i <= COEX_WL_TDMA_PARA_LENGTH; i++) coex_dm->fw_tdma_para[i - 1] = buf[i]; return; } if (rsp_source == COEX_BTINFO_SRC_WL_FW) { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], bt_info reply by WL FW\n"); rtw_coex_update_bt_link_info(rtwdev); return; } if (rsp_source == COEX_BTINFO_SRC_BT_RSP || rsp_source == COEX_BTINFO_SRC_BT_ACT) { if (coex_stat->bt_disabled) { coex_stat->bt_disabled = false; coex_stat->bt_reenable = true; ieee80211_queue_delayed_work(rtwdev->hw, &coex->bt_reenable_work, 15 * HZ); rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], BT enable detected by bt_info\n"); } } if (length != COEX_BTINFO_LENGTH) { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], Bt_info length = %d invalid!!\n", length); return; } rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], Bt_info[%d], len=%d, data=[%02x %02x %02x %02x %02x %02x]\n", buf[0], length, buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); for (i = 0; i < COEX_BTINFO_LENGTH; i++) coex_stat->bt_info_c2h[rsp_source][i] = buf[i]; /* get the same info from bt, skip it */ if (coex_stat->bt_info_c2h[rsp_source][1] == coex_stat->bt_info_lb2 && coex_stat->bt_info_c2h[rsp_source][2] == coex_stat->bt_info_lb3 && coex_stat->bt_info_c2h[rsp_source][3] == coex_stat->bt_info_hb0 && coex_stat->bt_info_c2h[rsp_source][4] == coex_stat->bt_info_hb1 && coex_stat->bt_info_c2h[rsp_source][5] == coex_stat->bt_info_hb2 && coex_stat->bt_info_c2h[rsp_source][6] == coex_stat->bt_info_hb3) { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], Return because Btinfo duplicate!!\n"); return; } coex_stat->bt_info_lb2 = coex_stat->bt_info_c2h[rsp_source][1]; coex_stat->bt_info_lb3 = coex_stat->bt_info_c2h[rsp_source][2]; coex_stat->bt_info_hb0 = coex_stat->bt_info_c2h[rsp_source][3]; coex_stat->bt_info_hb1 = coex_stat->bt_info_c2h[rsp_source][4]; coex_stat->bt_info_hb2 = coex_stat->bt_info_c2h[rsp_source][5]; coex_stat->bt_info_hb3 = coex_stat->bt_info_c2h[rsp_source][6]; /* 0xff means BT is under WHCK test */ coex_stat->bt_whck_test = (coex_stat->bt_info_lb2 == 0xff); inq_page = ((coex_stat->bt_info_lb2 & BIT(2)) == BIT(2)); if (inq_page != coex_stat->bt_inq_page) { cancel_delayed_work_sync(&coex->bt_remain_work); coex_stat->bt_inq_page = inq_page; if (inq_page) coex_stat->bt_inq_remain = true; else ieee80211_queue_delayed_work(rtwdev->hw, &coex->bt_remain_work, 4 * HZ); } coex_stat->bt_acl_busy = ((coex_stat->bt_info_lb2 & BIT(3)) == BIT(3)); if (chip->ble_hid_profile_support) { if (coex_stat->bt_info_lb2 & BIT(5)) { if (coex_stat->bt_info_hb1 & BIT(0)) { /*BLE HID*/ coex_stat->bt_ble_hid_exist = true; } else { coex_stat->bt_ble_hid_exist = false; } coex_stat->bt_ble_exist = false; } else if (coex_stat->bt_info_hb1 & BIT(0)) { /*RCU*/ coex_stat->bt_ble_hid_exist = false; coex_stat->bt_ble_exist = true; } else { coex_stat->bt_ble_hid_exist = false; coex_stat->bt_ble_exist = false; } } else { if (coex_stat->bt_info_hb1 & BIT(0)) { if (coex_stat->bt_hid_slot == 1 && coex_stat->hi_pri_rx + 100 < coex_stat->hi_pri_tx && coex_stat->hi_pri_rx < 100) { coex_stat->bt_ble_hid_exist = true; coex_stat->bt_ble_exist = false; } else { coex_stat->bt_ble_hid_exist = false; coex_stat->bt_ble_exist = true; } } else { coex_stat->bt_ble_hid_exist = false; coex_stat->bt_ble_exist = false; } } coex_stat->cnt_bt[COEX_CNT_BT_RETRY] = coex_stat->bt_info_lb3 & 0xf; if (coex_stat->cnt_bt[COEX_CNT_BT_RETRY] >= 1) coex_stat->cnt_bt[COEX_CNT_BT_POPEVENT]++; coex_stat->bt_fix_2M = ((coex_stat->bt_info_lb3 & BIT(4)) == BIT(4)); coex_stat->bt_inq = ((coex_stat->bt_info_lb3 & BIT(5)) == BIT(5)); if (coex_stat->bt_inq) coex_stat->cnt_bt[COEX_CNT_BT_INQ]++; coex_stat->bt_page = ((coex_stat->bt_info_lb3 & BIT(7)) == BIT(7)); if (coex_stat->bt_page) coex_stat->cnt_bt[COEX_CNT_BT_PAGE]++; /* unit: % (value-100 to translate to unit: dBm in coex info) */ if (chip->bt_rssi_type == COEX_BTRSSI_RATIO) { coex_stat->bt_rssi = coex_stat->bt_info_hb0 * 2 + 10; } else { if (coex_stat->bt_info_hb0 <= 127) coex_stat->bt_rssi = 100; else if (256 - coex_stat->bt_info_hb0 <= 100) coex_stat->bt_rssi = 100 - (256 - coex_stat->bt_info_hb0); else coex_stat->bt_rssi = 0; } if (coex_stat->bt_info_hb1 & BIT(1)) coex_stat->cnt_bt[COEX_CNT_BT_REINIT]++; if (coex_stat->bt_info_hb1 & BIT(2)) { coex_stat->cnt_bt[COEX_CNT_BT_SETUPLINK]++; coex_stat->bt_setup_link = true; if (coex_stat->bt_reenable) bt_relink_time = 6 * HZ; else bt_relink_time = 1 * HZ; ieee80211_queue_delayed_work(rtwdev->hw, &coex->bt_relink_work, bt_relink_time); rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], Re-Link start in BT info!!\n"); } if (coex_stat->bt_info_hb1 & BIT(3)) coex_stat->cnt_bt[COEX_CNT_BT_IGNWLANACT]++; coex_stat->bt_ble_voice = ((coex_stat->bt_info_hb1 & BIT(4)) == BIT(4)); coex_stat->bt_ble_scan_en = ((coex_stat->bt_info_hb1 & BIT(5)) == BIT(5)); if (coex_stat->bt_info_hb1 & BIT(6)) coex_stat->cnt_bt[COEX_CNT_BT_ROLESWITCH]++; coex_stat->bt_multi_link = ((coex_stat->bt_info_hb1 & BIT(7)) == BIT(7)); /* for multi_link = 0 but bt pkt remain exist */ /* Use PS-TDMA to protect WL RX */ if (!coex_stat->bt_multi_link && coex_stat->bt_multi_link_pre) { coex_stat->bt_multi_link_remain = true; ieee80211_queue_delayed_work(rtwdev->hw, &coex->bt_multi_link_remain_work, 3 * HZ); } coex_stat->bt_multi_link_pre = coex_stat->bt_multi_link; /* resend wifi info to bt, it is reset and lost the info */ if (coex_stat->bt_info_hb1 & BIT(1)) { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], BT Re-init, send wifi BW & Chnl to BT!!\n"); if (coex_stat->wl_connected) type = COEX_MEDIA_CONNECT; else type = COEX_MEDIA_DISCONNECT; rtw_coex_update_wl_ch_info(rtwdev, type); } /* if ignore_wlan_act && not set_up_link */ if ((coex_stat->bt_info_hb1 & BIT(3)) && (!(coex_stat->bt_info_hb1 & BIT(2)))) { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], BT ext info bit3 check, set BT NOT to ignore Wlan active!!\n"); rtw_coex_ignore_wlan_act(rtwdev, false); } coex_stat->bt_opp_exist = ((coex_stat->bt_info_hb2 & BIT(0)) == BIT(0)); if (coex_stat->bt_info_hb2 & BIT(1)) coex_stat->cnt_bt[COEX_CNT_BT_AFHUPDATE]++; coex_stat->bt_a2dp_active = (coex_stat->bt_info_hb2 & BIT(2)) == BIT(2); coex_stat->bt_slave = ((coex_stat->bt_info_hb2 & BIT(3)) == BIT(3)); coex_stat->bt_hid_slot = (coex_stat->bt_info_hb2 & 0x30) >> 4; coex_stat->bt_hid_pair_num = (coex_stat->bt_info_hb2 & 0xc0) >> 6; if (coex_stat->bt_hid_pair_num > 0 && coex_stat->bt_hid_slot >= 2) coex_stat->bt_418_hid_exist = true; else if (coex_stat->bt_hid_pair_num == 0 || coex_stat->bt_hid_slot == 1) coex_stat->bt_418_hid_exist = false; if ((coex_stat->bt_info_lb2 & 0x49) == 0x49) coex_stat->bt_a2dp_bitpool = (coex_stat->bt_info_hb3 & 0x7f); else coex_stat->bt_a2dp_bitpool = 0; coex_stat->bt_a2dp_sink = ((coex_stat->bt_info_hb3 & BIT(7)) == BIT(7)); rtw_coex_update_bt_link_info(rtwdev); rtw_coex_run_coex(rtwdev, COEX_RSN_BTINFO); } #define COEX_BT_HIDINFO_MTK 0x46 static const u8 coex_bt_hidinfo_ps[] = {0x57, 0x69, 0x72}; static const u8 coex_bt_hidinfo_xb[] = {0x58, 0x62, 0x6f}; void rtw_coex_bt_hid_info_notify(struct rtw_dev *rtwdev, u8 *buf, u8 length) { + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_coex *coex = &rtwdev->coex; - struct rtw_chip_info *chip = rtwdev->chip; struct rtw_coex_stat *coex_stat = &coex->stat; struct rtw_coex_hid *hidinfo; struct rtw_coex_hid_info_a *hida; struct rtw_coex_hid_handle_list *hl, *bhl; u8 sub_id = buf[2], gamehid_cnt = 0, handle, i; bool cur_game_hid_exist, complete; if (!chip->wl_mimo_ps_support && (sub_id == COEX_BT_HIDINFO_LIST || sub_id == COEX_BT_HIDINFO_A)) return; rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], HID info notify, sub_id = 0x%x\n", sub_id); switch (sub_id) { case COEX_BT_HIDINFO_LIST: hl = &coex_stat->hid_handle_list; bhl = (struct rtw_coex_hid_handle_list *)buf; if (!memcmp(hl, bhl, sizeof(*hl))) return; coex_stat->hid_handle_list = *bhl; memset(&coex_stat->hid_info, 0, sizeof(coex_stat->hid_info)); for (i = 0; i < COEX_BT_HIDINFO_HANDLE_NUM; i++) { hidinfo = &coex_stat->hid_info[i]; if (hl->handle[i] != COEX_BT_HIDINFO_NOTCON && hl->handle[i] != 0) hidinfo->hid_handle = hl->handle[i]; } break; case COEX_BT_HIDINFO_A: hida = (struct rtw_coex_hid_info_a *)buf; handle = hida->handle; for (i = 0; i < COEX_BT_HIDINFO_HANDLE_NUM; i++) { hidinfo = &coex_stat->hid_info[i]; if (hidinfo->hid_handle == handle) { hidinfo->hid_vendor = hida->vendor; memcpy(hidinfo->hid_name, hida->name, sizeof(hidinfo->hid_name)); hidinfo->hid_info_completed = true; break; } } break; } for (i = 0; i < COEX_BT_HIDINFO_HANDLE_NUM; i++) { hidinfo = &coex_stat->hid_info[i]; complete = hidinfo->hid_info_completed; handle = hidinfo->hid_handle; if (!complete || handle == COEX_BT_HIDINFO_NOTCON || handle == 0 || handle >= COEX_BT_BLE_HANDLE_THRS) { hidinfo->is_game_hid = false; continue; } if (hidinfo->hid_vendor == COEX_BT_HIDINFO_MTK) { if ((memcmp(hidinfo->hid_name, coex_bt_hidinfo_ps, COEX_BT_HIDINFO_NAME)) == 0) hidinfo->is_game_hid = true; else if ((memcmp(hidinfo->hid_name, coex_bt_hidinfo_xb, COEX_BT_HIDINFO_NAME)) == 0) hidinfo->is_game_hid = true; else hidinfo->is_game_hid = false; } else { hidinfo->is_game_hid = false; } if (hidinfo->is_game_hid) gamehid_cnt++; } if (gamehid_cnt > 0) cur_game_hid_exist = true; else cur_game_hid_exist = false; if (cur_game_hid_exist != coex_stat->bt_game_hid_exist) { coex_stat->bt_game_hid_exist = cur_game_hid_exist; rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], HID info changed!bt_game_hid_exist = %d!\n", coex_stat->bt_game_hid_exist); rtw_coex_run_coex(rtwdev, COEX_RSN_BTSTATUS); } } void rtw_coex_query_bt_hid_list(struct rtw_dev *rtwdev) { + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_coex *coex = &rtwdev->coex; - struct rtw_chip_info *chip = rtwdev->chip; struct rtw_coex_stat *coex_stat = &coex->stat; struct rtw_coex_hid *hidinfo; u8 i, handle; bool complete; if (!chip->wl_mimo_ps_support || coex_stat->wl_under_ips || (coex_stat->wl_under_lps && !coex_stat->wl_force_lps_ctrl)) return; if (!coex_stat->bt_hid_exist && !((coex_stat->bt_info_lb2 & COEX_INFO_CONNECTION) && (coex_stat->hi_pri_tx + coex_stat->hi_pri_rx > COEX_BT_GAMEHID_CNT))) return; rtw_fw_coex_query_hid_info(rtwdev, COEX_BT_HIDINFO_LIST, 0); for (i = 0; i < COEX_BT_HIDINFO_HANDLE_NUM; i++) { hidinfo = &coex_stat->hid_info[i]; complete = hidinfo->hid_info_completed; handle = hidinfo->hid_handle; if (handle == 0 || handle == COEX_BT_HIDINFO_NOTCON || handle >= COEX_BT_BLE_HANDLE_THRS || complete) continue; rtw_fw_coex_query_hid_info(rtwdev, COEX_BT_HIDINFO_A, handle); } } void rtw_coex_wl_fwdbginfo_notify(struct rtw_dev *rtwdev, u8 *buf, u8 length) { struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_stat *coex_stat = &coex->stat; u8 val; int i; rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], WiFi Fw Dbg info = %8ph (len = %d)\n", buf, length); if (WARN(length < 8, "invalid wl info c2h length\n")) return; if (buf[0] != 0x08) return; for (i = 1; i < 8; i++) { val = coex_stat->wl_fw_dbg_info_pre[i]; if (buf[i] >= val) coex_stat->wl_fw_dbg_info[i] = buf[i] - val; else coex_stat->wl_fw_dbg_info[i] = 255 - val + buf[i]; coex_stat->wl_fw_dbg_info_pre[i] = buf[i]; } coex_stat->cnt_wl[COEX_CNT_WL_FW_NOTIFY]++; rtw_coex_wl_ccklock_action(rtwdev); rtw_coex_wl_ccklock_detect(rtwdev); } void rtw_coex_wl_status_change_notify(struct rtw_dev *rtwdev, u32 type) { rtw_coex_run_coex(rtwdev, COEX_RSN_WLSTATUS); } void rtw_coex_wl_status_check(struct rtw_dev *rtwdev) { struct rtw_coex_stat *coex_stat = &rtwdev->coex.stat; if ((coex_stat->wl_under_lps && !coex_stat->wl_force_lps_ctrl) || coex_stat->wl_under_ips) return; rtw_coex_monitor_bt_ctr(rtwdev); } void rtw_coex_bt_relink_work(struct work_struct *work) { struct rtw_dev *rtwdev = container_of(work, struct rtw_dev, coex.bt_relink_work.work); struct rtw_coex_stat *coex_stat = &rtwdev->coex.stat; mutex_lock(&rtwdev->mutex); coex_stat->bt_setup_link = false; rtw_coex_run_coex(rtwdev, COEX_RSN_WLSTATUS); mutex_unlock(&rtwdev->mutex); } void rtw_coex_bt_reenable_work(struct work_struct *work) { struct rtw_dev *rtwdev = container_of(work, struct rtw_dev, coex.bt_reenable_work.work); struct rtw_coex_stat *coex_stat = &rtwdev->coex.stat; mutex_lock(&rtwdev->mutex); coex_stat->bt_reenable = false; mutex_unlock(&rtwdev->mutex); } void rtw_coex_defreeze_work(struct work_struct *work) { struct rtw_dev *rtwdev = container_of(work, struct rtw_dev, coex.defreeze_work.work); struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_stat *coex_stat = &rtwdev->coex.stat; mutex_lock(&rtwdev->mutex); coex->freeze = false; coex_stat->wl_hi_pri_task1 = false; rtw_coex_run_coex(rtwdev, COEX_RSN_WLSTATUS); mutex_unlock(&rtwdev->mutex); } void rtw_coex_wl_remain_work(struct work_struct *work) { struct rtw_dev *rtwdev = container_of(work, struct rtw_dev, coex.wl_remain_work.work); struct rtw_coex_stat *coex_stat = &rtwdev->coex.stat; mutex_lock(&rtwdev->mutex); coex_stat->wl_gl_busy = test_bit(RTW_FLAG_BUSY_TRAFFIC, rtwdev->flags); rtw_coex_run_coex(rtwdev, COEX_RSN_WLSTATUS); mutex_unlock(&rtwdev->mutex); } void rtw_coex_bt_remain_work(struct work_struct *work) { struct rtw_dev *rtwdev = container_of(work, struct rtw_dev, coex.bt_remain_work.work); struct rtw_coex_stat *coex_stat = &rtwdev->coex.stat; mutex_lock(&rtwdev->mutex); coex_stat->bt_inq_remain = coex_stat->bt_inq_page; rtw_coex_run_coex(rtwdev, COEX_RSN_BTSTATUS); mutex_unlock(&rtwdev->mutex); } void rtw_coex_wl_connecting_work(struct work_struct *work) { struct rtw_dev *rtwdev = container_of(work, struct rtw_dev, coex.wl_connecting_work.work); struct rtw_coex_stat *coex_stat = &rtwdev->coex.stat; mutex_lock(&rtwdev->mutex); coex_stat->wl_connecting = false; rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], WL connecting stop!!\n"); rtw_coex_run_coex(rtwdev, COEX_RSN_WLSTATUS); mutex_unlock(&rtwdev->mutex); } void rtw_coex_bt_multi_link_remain_work(struct work_struct *work) { struct rtw_dev *rtwdev = container_of(work, struct rtw_dev, coex.bt_multi_link_remain_work.work); struct rtw_coex_stat *coex_stat = &rtwdev->coex.stat; mutex_lock(&rtwdev->mutex); coex_stat->bt_multi_link_remain = false; mutex_unlock(&rtwdev->mutex); } void rtw_coex_wl_ccklock_work(struct work_struct *work) { struct rtw_dev *rtwdev = container_of(work, struct rtw_dev, coex.wl_ccklock_work.work); struct rtw_coex_stat *coex_stat = &rtwdev->coex.stat; mutex_lock(&rtwdev->mutex); coex_stat->wl_cck_lock = false; mutex_unlock(&rtwdev->mutex); } #ifdef CONFIG_RTW88_DEBUGFS #define INFO_SIZE 80 #define case_BTINFO(src) \ case COEX_BTINFO_SRC_##src: return #src static const char *rtw_coex_get_bt_info_src_string(u8 bt_info_src) { switch (bt_info_src) { case_BTINFO(WL_FW); case_BTINFO(BT_RSP); case_BTINFO(BT_ACT); default: return "Unknown"; } } #define case_RSN(src) \ case COEX_RSN_##src: return #src static const char *rtw_coex_get_reason_string(u8 reason) { switch (reason) { case_RSN(2GSCANSTART); case_RSN(5GSCANSTART); case_RSN(SCANFINISH); case_RSN(2GSWITCHBAND); case_RSN(5GSWITCHBAND); case_RSN(2GCONSTART); case_RSN(5GCONSTART); case_RSN(2GCONFINISH); case_RSN(5GCONFINISH); case_RSN(2GMEDIA); case_RSN(5GMEDIA); case_RSN(MEDIADISCON); case_RSN(BTINFO); case_RSN(LPS); case_RSN(WLSTATUS); default: return "Unknown"; } } static u8 rtw_coex_get_table_index(struct rtw_dev *rtwdev, u32 wl_reg_6c0, u32 wl_reg_6c4) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_efuse *efuse = &rtwdev->efuse; u8 ans = 0xFF; u8 n, i; u32 load_bt_val; u32 load_wl_val; bool share_ant = efuse->share_ant; if (share_ant) n = chip->table_sant_num; else n = chip->table_nsant_num; for (i = 0; i < n; i++) { if (share_ant) { load_bt_val = chip->table_sant[i].bt; load_wl_val = chip->table_sant[i].wl; } else { load_bt_val = chip->table_nsant[i].bt; load_wl_val = chip->table_nsant[i].wl; } if (wl_reg_6c0 == load_bt_val && wl_reg_6c4 == load_wl_val) { ans = i; if (!share_ant) ans += 100; break; } } return ans; } static u8 rtw_coex_get_tdma_index(struct rtw_dev *rtwdev, u8 *tdma_para) { + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_efuse *efuse = &rtwdev->efuse; - struct rtw_chip_info *chip = rtwdev->chip; u8 ans = 0xFF; u8 n, i, j; u8 load_cur_tab_val; bool valid = false; bool share_ant = efuse->share_ant; if (share_ant) n = chip->tdma_sant_num; else n = chip->tdma_nsant_num; for (i = 0; i < n; i++) { valid = false; for (j = 0; j < 5; j++) { if (share_ant) load_cur_tab_val = chip->tdma_sant[i].para[j]; else load_cur_tab_val = chip->tdma_nsant[i].para[j]; if (*(tdma_para + j) != load_cur_tab_val) break; if (j == 4) valid = true; } if (valid) { ans = i; break; } } return ans; } static int rtw_coex_addr_info(struct rtw_dev *rtwdev, const struct rtw_reg_domain *reg, char addr_info[], int n) { const char *rf_prefix = ""; const char *sep = n == 0 ? "" : "/ "; int ffs, fls; int max_fls; if (INFO_SIZE - n <= 0) return 0; switch (reg->domain) { case RTW_REG_DOMAIN_MAC32: max_fls = 31; break; case RTW_REG_DOMAIN_MAC16: max_fls = 15; break; case RTW_REG_DOMAIN_MAC8: max_fls = 7; break; case RTW_REG_DOMAIN_RF_A: case RTW_REG_DOMAIN_RF_B: rf_prefix = "RF_"; max_fls = 19; break; default: return 0; } ffs = __ffs(reg->mask); fls = __fls(reg->mask); if (ffs == 0 && fls == max_fls) return scnprintf(addr_info + n, INFO_SIZE - n, "%s%s%x", sep, rf_prefix, reg->addr); else if (ffs == fls) return scnprintf(addr_info + n, INFO_SIZE - n, "%s%s%x[%d]", sep, rf_prefix, reg->addr, ffs); else return scnprintf(addr_info + n, INFO_SIZE - n, "%s%s%x[%d:%d]", sep, rf_prefix, reg->addr, fls, ffs); } static int rtw_coex_val_info(struct rtw_dev *rtwdev, const struct rtw_reg_domain *reg, char val_info[], int n) { const char *sep = n == 0 ? "" : "/ "; u8 rf_path; if (INFO_SIZE - n <= 0) return 0; switch (reg->domain) { case RTW_REG_DOMAIN_MAC32: return scnprintf(val_info + n, INFO_SIZE - n, "%s0x%x", sep, rtw_read32_mask(rtwdev, reg->addr, reg->mask)); case RTW_REG_DOMAIN_MAC16: return scnprintf(val_info + n, INFO_SIZE - n, "%s0x%x", sep, rtw_read16_mask(rtwdev, reg->addr, reg->mask)); case RTW_REG_DOMAIN_MAC8: return scnprintf(val_info + n, INFO_SIZE - n, "%s0x%x", sep, rtw_read8_mask(rtwdev, reg->addr, reg->mask)); case RTW_REG_DOMAIN_RF_A: rf_path = RF_PATH_A; break; case RTW_REG_DOMAIN_RF_B: rf_path = RF_PATH_B; break; default: return 0; } /* only RF go through here */ return scnprintf(val_info + n, INFO_SIZE - n, "%s0x%x", sep, rtw_read_rf(rtwdev, rf_path, reg->addr, reg->mask)); } static void rtw_coex_set_coexinfo_hw(struct rtw_dev *rtwdev, struct seq_file *m) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; const struct rtw_reg_domain *reg; char addr_info[INFO_SIZE]; int n_addr = 0; char val_info[INFO_SIZE]; int n_val = 0; int i; for (i = 0; i < chip->coex_info_hw_regs_num; i++) { reg = &chip->coex_info_hw_regs[i]; n_addr += rtw_coex_addr_info(rtwdev, reg, addr_info, n_addr); n_val += rtw_coex_val_info(rtwdev, reg, val_info, n_val); if (reg->domain == RTW_REG_DOMAIN_NL) { seq_printf(m, "%-40s = %s\n", addr_info, val_info); n_addr = 0; n_val = 0; } } if (n_addr != 0 && n_val != 0) seq_printf(m, "%-40s = %s\n", addr_info, val_info); } static bool rtw_coex_get_bt_reg(struct rtw_dev *rtwdev, u8 type, u16 addr, u16 *val) { struct rtw_coex_info_req req = {0}; struct sk_buff *skb; __le16 le_addr; u8 *payload; le_addr = cpu_to_le16(addr); req.op_code = BT_MP_INFO_OP_READ_REG; req.para1 = type; req.para2 = le16_get_bits(le_addr, GENMASK(7, 0)); req.para3 = le16_get_bits(le_addr, GENMASK(15, 8)); skb = rtw_coex_info_request(rtwdev, &req); if (!skb) { *val = 0xeaea; return false; } payload = get_payload_from_coex_resp(skb); *val = GET_COEX_RESP_BT_REG_VAL(payload); dev_kfree_skb_any(skb); return true; } static bool rtw_coex_get_bt_patch_version(struct rtw_dev *rtwdev, u32 *patch_version) { struct rtw_coex_info_req req = {0}; struct sk_buff *skb; u8 *payload; req.op_code = BT_MP_INFO_OP_PATCH_VER; skb = rtw_coex_info_request(rtwdev, &req); if (!skb) return false; payload = get_payload_from_coex_resp(skb); *patch_version = GET_COEX_RESP_BT_PATCH_VER(payload); dev_kfree_skb_any(skb); return true; } static bool rtw_coex_get_bt_supported_version(struct rtw_dev *rtwdev, u32 *supported_version) { struct rtw_coex_info_req req = {0}; struct sk_buff *skb; u8 *payload; req.op_code = BT_MP_INFO_OP_SUPP_VER; skb = rtw_coex_info_request(rtwdev, &req); if (!skb) return false; payload = get_payload_from_coex_resp(skb); *supported_version = GET_COEX_RESP_BT_SUPP_VER(payload); dev_kfree_skb_any(skb); return true; } static bool rtw_coex_get_bt_supported_feature(struct rtw_dev *rtwdev, u32 *supported_feature) { struct rtw_coex_info_req req = {0}; struct sk_buff *skb; u8 *payload; req.op_code = BT_MP_INFO_OP_SUPP_FEAT; skb = rtw_coex_info_request(rtwdev, &req); if (!skb) return false; payload = get_payload_from_coex_resp(skb); *supported_feature = GET_COEX_RESP_BT_SUPP_FEAT(payload); dev_kfree_skb_any(skb); return true; } struct rtw_coex_sta_stat_iter_data { struct rtw_vif *rtwvif; struct seq_file *file; }; static void rtw_coex_sta_stat_iter(void *data, struct ieee80211_sta *sta) { struct rtw_coex_sta_stat_iter_data *sta_iter_data = data; struct rtw_vif *rtwvif = sta_iter_data->rtwvif; struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv; struct seq_file *m = sta_iter_data->file; struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif); u8 rssi; if (si->vif != vif) return; rssi = ewma_rssi_read(&si->avg_rssi); seq_printf(m, "\tPeer %3d\n", si->mac_id); seq_printf(m, "\t\t%-24s = %d\n", "RSSI", rssi); seq_printf(m, "\t\t%-24s = %d\n", "BW mode", si->bw_mode); } struct rtw_coex_vif_stat_iter_data { struct rtw_dev *rtwdev; struct seq_file *file; }; static void rtw_coex_vif_stat_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { struct rtw_coex_vif_stat_iter_data *vif_iter_data = data; struct rtw_coex_sta_stat_iter_data sta_iter_data; struct rtw_dev *rtwdev = vif_iter_data->rtwdev; struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; struct seq_file *m = vif_iter_data->file; struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; seq_printf(m, "Iface on Port (%d)\n", rtwvif->port); seq_printf(m, "\t%-32s = %d\n", "Beacon interval", bss_conf->beacon_int); seq_printf(m, "\t%-32s = %d\n", "Network Type", rtwvif->net_type); sta_iter_data.rtwvif = rtwvif; sta_iter_data.file = m; rtw_iterate_stas_atomic(rtwdev, rtw_coex_sta_stat_iter, &sta_iter_data); } #define case_WLINK(src) \ case COEX_WLINK_##src: return #src static const char *rtw_coex_get_wl_coex_mode(u8 coex_wl_link_mode) { switch (coex_wl_link_mode) { case_WLINK(2G1PORT); case_WLINK(5G); case_WLINK(2GFREE); default: return "Unknown"; } } void rtw_coex_display_coex_info(struct rtw_dev *rtwdev, struct seq_file *m) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_dm_info *dm_info = &rtwdev->dm_info; struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_stat *coex_stat = &coex->stat; struct rtw_coex_dm *coex_dm = &coex->dm; struct rtw_hal *hal = &rtwdev->hal; struct rtw_efuse *efuse = &rtwdev->efuse; struct rtw_fw_state *fw = &rtwdev->fw; struct rtw_coex_vif_stat_iter_data vif_iter_data; u8 reason = coex_dm->reason; u8 sys_lte; u16 score_board_WB, score_board_BW; u32 wl_reg_6c0, wl_reg_6c4, wl_reg_6c8, wl_reg_778, wl_reg_6cc; u32 lte_coex, bt_coex; int i; score_board_BW = rtw_coex_read_scbd(rtwdev); score_board_WB = coex_stat->score_board; wl_reg_6c0 = rtw_read32(rtwdev, REG_BT_COEX_TABLE0); wl_reg_6c4 = rtw_read32(rtwdev, REG_BT_COEX_TABLE1); wl_reg_6c8 = rtw_read32(rtwdev, REG_BT_COEX_BRK_TABLE); wl_reg_6cc = rtw_read32(rtwdev, REG_BT_COEX_TABLE_H); wl_reg_778 = rtw_read8(rtwdev, REG_BT_STAT_CTRL); sys_lte = rtw_read8(rtwdev, 0x73); lte_coex = rtw_coex_read_indirect_reg(rtwdev, 0x38); bt_coex = rtw_coex_read_indirect_reg(rtwdev, 0x54); if (!coex_stat->bt_disabled && !coex_stat->bt_mailbox_reply) { rtw_coex_get_bt_supported_version(rtwdev, &coex_stat->bt_supported_version); rtw_coex_get_bt_patch_version(rtwdev, &coex_stat->patch_ver); rtw_coex_get_bt_supported_feature(rtwdev, &coex_stat->bt_supported_feature); rtw_coex_get_bt_reg(rtwdev, 3, 0xae, &coex_stat->bt_reg_vendor_ae); rtw_coex_get_bt_reg(rtwdev, 3, 0xac, &coex_stat->bt_reg_vendor_ac); if (coex_stat->patch_ver != 0) coex_stat->bt_mailbox_reply = true; } rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], %s()\n", __func__); seq_printf(m, "**********************************************\n"); seq_printf(m, "\t\tBT Coexist info %x\n", chip->id); seq_printf(m, "**********************************************\n"); if (coex->manual_control) { seq_puts(m, "============[Under Manual Control]============\n"); seq_puts(m, "==========================================\n"); } else if (coex->stop_dm) { seq_puts(m, "============[Coex is STOPPED]============\n"); seq_puts(m, "==========================================\n"); } else if (coex->freeze) { seq_puts(m, "============[coex_freeze]============\n"); seq_puts(m, "==========================================\n"); } seq_printf(m, "%-40s = %s/ %d\n", "Mech/ RFE", efuse->share_ant ? "Shared" : "Non-Shared", efuse->rfe_option); seq_printf(m, "%-40s = %08x/ 0x%02x/ 0x%08x %s\n", "Coex Ver/ BT Dez/ BT Rpt", chip->coex_para_ver, chip->bt_desired_ver, coex_stat->bt_supported_version, coex_stat->bt_disabled ? "(BT disabled)" : coex_stat->bt_supported_version >= chip->bt_desired_ver ? "(Match)" : "(Mismatch)"); seq_printf(m, "%-40s = %s/ %u/ %d\n", "Role/ RoleSwCnt/ IgnWL/ Feature", coex_stat->bt_slave ? "Slave" : "Master", coex_stat->cnt_bt[COEX_CNT_BT_ROLESWITCH], coex_dm->ignore_wl_act); seq_printf(m, "%-40s = %u.%u/ 0x%x/ 0x%x/ %c\n", "WL FW/ BT FW/ BT FW Desired/ KT", fw->version, fw->sub_version, coex_stat->patch_ver, chip->wl_fw_desired_ver, coex_stat->kt_ver + 65); seq_printf(m, "%-40s = %u/ %u/ %u/ ch-(%u)\n", "AFH Map", coex_dm->wl_ch_info[0], coex_dm->wl_ch_info[1], coex_dm->wl_ch_info[2], hal->current_channel); rtw_debugfs_get_simple_phy_info(m); seq_printf(m, "**********************************************\n"); seq_printf(m, "\t\tBT Status\n"); seq_printf(m, "**********************************************\n"); seq_printf(m, "%-40s = %s/ %ddBm/ %u/ %u\n", "BT status/ rssi/ retry/ pop", coex_dm->bt_status == COEX_BTSTATUS_NCON_IDLE ? "non-conn" : coex_dm->bt_status == COEX_BTSTATUS_CON_IDLE ? "conn-idle" : "busy", coex_stat->bt_rssi - 100, coex_stat->cnt_bt[COEX_CNT_BT_RETRY], coex_stat->cnt_bt[COEX_CNT_BT_POPEVENT]); seq_printf(m, "%-40s = %s%s%s%s%s (multi-link %d)\n", "Profiles", coex_stat->bt_a2dp_exist ? (coex_stat->bt_a2dp_sink ? "A2DP sink," : "A2DP,") : "", coex_stat->bt_hfp_exist ? "HFP," : "", coex_stat->bt_hid_exist ? (coex_stat->bt_ble_exist ? "HID(RCU)," : coex_stat->bt_hid_slot >= 2 ? "HID(4/18)" : coex_stat->bt_ble_hid_exist ? "HID(BLE)" : "HID(2/18),") : "", coex_stat->bt_pan_exist ? coex_stat->bt_opp_exist ? "OPP," : "PAN," : "", coex_stat->bt_ble_voice ? "Voice," : "", coex_stat->bt_multi_link); seq_printf(m, "%-40s = %u/ %u/ %u/ 0x%08x\n", "Reinit/ Relink/ IgnWl/ Feature", coex_stat->cnt_bt[COEX_CNT_BT_REINIT], coex_stat->cnt_bt[COEX_CNT_BT_SETUPLINK], coex_stat->cnt_bt[COEX_CNT_BT_IGNWLANACT], coex_stat->bt_supported_feature); seq_printf(m, "%-40s = %u/ %u/ %u/ %u\n", "Page/ Inq/ iqk/ iqk fail", coex_stat->cnt_bt[COEX_CNT_BT_PAGE], coex_stat->cnt_bt[COEX_CNT_BT_INQ], coex_stat->cnt_bt[COEX_CNT_BT_IQK], coex_stat->cnt_bt[COEX_CNT_BT_IQKFAIL]); seq_printf(m, "%-40s = 0x%04x/ 0x%04x/ 0x%04x/ 0x%04x\n", "0xae/ 0xac/ score board (W->B)/ (B->W)", coex_stat->bt_reg_vendor_ae, coex_stat->bt_reg_vendor_ac, score_board_WB, score_board_BW); seq_printf(m, "%-40s = %u/%u, %u/%u\n", "Hi-Pri TX/RX, Lo-Pri TX/RX", coex_stat->hi_pri_tx, coex_stat->hi_pri_rx, coex_stat->lo_pri_tx, coex_stat->lo_pri_rx); for (i = 0; i < COEX_BTINFO_SRC_BT_IQK; i++) seq_printf(m, "%-40s = %7ph\n", rtw_coex_get_bt_info_src_string(i), coex_stat->bt_info_c2h[i]); seq_printf(m, "**********************************************\n"); seq_printf(m, "\t\tWiFi Status\n"); seq_printf(m, "**********************************************\n"); seq_printf(m, "%-40s = %d\n", "Scanning", test_bit(RTW_FLAG_SCANNING, rtwdev->flags)); seq_printf(m, "%-40s = %u/ TX %d Mbps/ RX %d Mbps\n", "G_busy/ TX/ RX", coex_stat->wl_gl_busy, rtwdev->stats.tx_throughput, rtwdev->stats.rx_throughput); seq_printf(m, "%-40s = %u/ %u/ %u\n", "IPS/ Low Power/ PS mode", - test_bit(RTW_FLAG_INACTIVE_PS, rtwdev->flags), + !test_bit(RTW_FLAG_POWERON, rtwdev->flags), test_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags), rtwdev->lps_conf.mode); vif_iter_data.rtwdev = rtwdev; vif_iter_data.file = m; rtw_iterate_vifs_atomic(rtwdev, rtw_coex_vif_stat_iter, &vif_iter_data); if (coex->manual_control) { seq_printf(m, "**********************************************\n"); seq_printf(m, "\t\tMechanism (Under Manual)\n"); seq_printf(m, "**********************************************\n"); seq_printf(m, "%-40s = %5ph (%d)\n", "TDMA Now", coex_dm->fw_tdma_para, rtw_coex_get_tdma_index(rtwdev, &coex_dm->fw_tdma_para[0])); } else { seq_printf(m, "**********************************************\n"); seq_printf(m, "\t\tMechanism\n"); seq_printf(m, "**********************************************\n"); seq_printf(m, "%-40s = %5ph (case-%d)\n", "TDMA", coex_dm->ps_tdma_para, coex_dm->cur_ps_tdma); } seq_printf(m, "%-40s = %s/ %s/ %d\n", "Coex Mode/Free Run/Timer base", rtw_coex_get_wl_coex_mode(coex_stat->wl_coex_mode), coex->freerun ? "Yes" : "No", coex_stat->tdma_timer_base); seq_printf(m, "%-40s = %d(%d)/ 0x%08x/ 0x%08x/ 0x%08x\n", "Table/ 0x6c0/ 0x6c4/ 0x6c8", coex_dm->cur_table, rtw_coex_get_table_index(rtwdev, wl_reg_6c0, wl_reg_6c4), wl_reg_6c0, wl_reg_6c4, wl_reg_6c8); seq_printf(m, "%-40s = 0x%08x/ 0x%08x/ %d/ reason (%s)\n", "0x778/ 0x6cc/ Run Count/ Reason", wl_reg_778, wl_reg_6cc, coex_stat->cnt_wl[COEX_CNT_WL_COEXRUN], rtw_coex_get_reason_string(reason)); seq_printf(m, "%-40s = %3ph\n", "AFH Map to BT", coex_dm->wl_ch_info); seq_printf(m, "%-40s = %s/ %d\n", "AntDiv/ BtCtrlLPS/ g_busy", coex_stat->wl_force_lps_ctrl ? "On" : "Off", coex_stat->wl_gl_busy); seq_printf(m, "%-40s = %u/ %u/ %u/ %u/ %u\n", "Null All/ Retry/ Ack/ BT Empty/ BT Late", coex_stat->wl_fw_dbg_info[1], coex_stat->wl_fw_dbg_info[2], coex_stat->wl_fw_dbg_info[3], coex_stat->wl_fw_dbg_info[4], coex_stat->wl_fw_dbg_info[5]); seq_printf(m, "%-40s = %u/ %u/ %s/ %u\n", "Cnt TDMA Toggle/ Lk 5ms/ Lk 5ms on/ FW", coex_stat->wl_fw_dbg_info[6], coex_stat->wl_fw_dbg_info[7], coex_stat->wl_slot_extend ? "Yes" : "No", coex_stat->cnt_wl[COEX_CNT_WL_FW_NOTIFY]); seq_printf(m, "%-40s = %d/ %d/ %s/ %d\n", "WL_TxPw/ BT_TxPw/ WL_Rx/ BT_LNA_Lvl", coex_dm->cur_wl_pwr_lvl, coex_dm->cur_bt_pwr_lvl, coex_dm->cur_wl_rx_low_gain_en ? "On" : "Off", coex_dm->cur_bt_lna_lvl); seq_printf(m, "**********************************************\n"); seq_printf(m, "\t\tHW setting\n"); seq_printf(m, "**********************************************\n"); seq_printf(m, "%-40s = %s/ %s\n", "LTE Coex/ Path Owner", lte_coex & BIT(7) ? "ON" : "OFF", sys_lte & BIT(2) ? "WL" : "BT"); seq_printf(m, "%-40s = RF:%s_BB:%s/ RF:%s_BB:%s/ %s\n", "GNT_WL_CTRL/ GNT_BT_CTRL/ Dbg", lte_coex & BIT(12) ? "SW" : "HW", lte_coex & BIT(8) ? "SW" : "HW", lte_coex & BIT(14) ? "SW" : "HW", lte_coex & BIT(10) ? "SW" : "HW", sys_lte & BIT(3) ? "On" : "Off"); seq_printf(m, "%-40s = %lu/ %lu\n", "GNT_WL/ GNT_BT", (bt_coex & BIT(2)) >> 2, (bt_coex & BIT(3)) >> 3); seq_printf(m, "%-40s = %u/ %u/ %u/ %u\n", "CRC OK CCK/ OFDM/ HT/ VHT", dm_info->cck_ok_cnt, dm_info->ofdm_ok_cnt, dm_info->ht_ok_cnt, dm_info->vht_ok_cnt); seq_printf(m, "%-40s = %u/ %u/ %u/ %u\n", "CRC ERR CCK/ OFDM/ HT/ VHT", dm_info->cck_err_cnt, dm_info->ofdm_err_cnt, dm_info->ht_err_cnt, dm_info->vht_err_cnt); seq_printf(m, "%-40s = %s/ %s/ %s/ %u\n", "HiPr/ Locking/ Locked/ Noisy", coex_stat->wl_hi_pri_task1 ? "Y" : "N", coex_stat->wl_cck_lock ? "Y" : "N", coex_stat->wl_cck_lock_ever ? "Y" : "N", coex_stat->wl_noisy_level); rtw_coex_set_coexinfo_hw(rtwdev, m); seq_printf(m, "%-40s = %d/ %d/ %d/ %d\n", "EVM A/ EVM B/ SNR A/ SNR B", -dm_info->rx_evm_dbm[RF_PATH_A], -dm_info->rx_evm_dbm[RF_PATH_B], -dm_info->rx_snr[RF_PATH_A], -dm_info->rx_snr[RF_PATH_B]); seq_printf(m, "%-40s = %d/ %d/ %d/ %d\n", "CCK-CCA/CCK-FA/OFDM-CCA/OFDM-FA", dm_info->cck_cca_cnt, dm_info->cck_fa_cnt, dm_info->ofdm_cca_cnt, dm_info->ofdm_fa_cnt); seq_printf(m, "%-40s = %d/ %d/ %d/ %d\n", "CRC OK CCK/11g/11n/11ac", dm_info->cck_ok_cnt, dm_info->ofdm_ok_cnt, dm_info->ht_ok_cnt, dm_info->vht_ok_cnt); seq_printf(m, "%-40s = %d/ %d/ %d/ %d\n", "CRC Err CCK/11g/11n/11ac", dm_info->cck_err_cnt, dm_info->ofdm_err_cnt, dm_info->ht_err_cnt, dm_info->vht_err_cnt); } #endif /* CONFIG_RTW88_DEBUGFS */ diff --git a/sys/contrib/dev/rtw88/coex.h b/sys/contrib/dev/rtw88/coex.h index 07fa7aa34d4b..57cf29da9ea4 100644 --- a/sys/contrib/dev/rtw88/coex.h +++ b/sys/contrib/dev/rtw88/coex.h @@ -1,422 +1,422 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* Copyright(c) 2018-2019 Realtek Corporation */ #ifndef __RTW_COEX_H__ #define __RTW_COEX_H__ #define COEX_CCK_2 0x1 #define COEX_RESP_ACK_BY_WL_FW 0x1 #define COEX_REQUEST_TIMEOUT msecs_to_jiffies(10) #define COEX_MIN_DELAY 10 /* delay unit in ms */ #define COEX_RFK_TIMEOUT 600 /* RFK timeout in ms */ #define COEX_BT_GAMEHID_CNT 800 #define COEX_RF_OFF 0x0 #define COEX_RF_ON 0x1 #define COEX_H2C69_WL_LEAKAP 0xc #define PARA1_H2C69_DIS_5MS 0x1 #define PARA1_H2C69_EN_5MS 0x0 #define COEX_H2C69_TDMA_SLOT 0xb #define PARA1_H2C69_TDMA_4SLOT 0xc1 #define PARA1_H2C69_TDMA_2SLOT 0x1 #define PARA1_H2C69_TBTT_TIMES GENMASK(5, 0) #define PARA1_H2C69_TBTT_DIV100 BIT(7) #define COEX_H2C69_TOGGLE_TABLE_A 0xd #define COEX_H2C69_TOGGLE_TABLE_B 0x7 #define TDMA_4SLOT BIT(8) #define TDMA_TIMER_TYPE_2SLOT 0 #define TDMA_TIMER_TYPE_4SLOT 3 #define COEX_RSSI_STEP 4 #define COEX_RSSI_HIGH(rssi) \ ({ typeof(rssi) __rssi__ = rssi; \ (__rssi__ == COEX_RSSI_STATE_HIGH || \ __rssi__ == COEX_RSSI_STATE_STAY_HIGH ? true : false); }) #define COEX_RSSI_MEDIUM(rssi) \ ({ typeof(rssi) __rssi__ = rssi; \ (__rssi__ == COEX_RSSI_STATE_MEDIUM || \ __rssi__ == COEX_RSSI_STATE_STAY_MEDIUM ? true : false); }) #define COEX_RSSI_LOW(rssi) \ ({ typeof(rssi) __rssi__ = rssi; \ (__rssi__ == COEX_RSSI_STATE_LOW || \ __rssi__ == COEX_RSSI_STATE_STAY_LOW ? true : false); }) #define GET_COEX_RESP_BT_SUPP_VER(payload) \ le64_get_bits(*((__le64 *)(payload)), GENMASK_ULL(39, 32)) #define GET_COEX_RESP_BT_SUPP_FEAT(payload) \ le64_get_bits(*((__le64 *)(payload)), GENMASK_ULL(39, 24)) #define GET_COEX_RESP_BT_PATCH_VER(payload) \ le64_get_bits(*((__le64 *)(payload)), GENMASK_ULL(55, 24)) #define GET_COEX_RESP_BT_REG_VAL(payload) \ le64_get_bits(*((__le64 *)(payload)), GENMASK_ULL(39, 24)) #define GET_COEX_RESP_BT_SCAN_TYPE(payload) \ le64_get_bits(*((__le64 *)(payload)), GENMASK(31, 24)) enum coex_mp_info_op { BT_MP_INFO_OP_PATCH_VER = 0x00, BT_MP_INFO_OP_READ_REG = 0x11, BT_MP_INFO_OP_SUPP_FEAT = 0x2a, BT_MP_INFO_OP_SUPP_VER = 0x2b, BT_MP_INFO_OP_SCAN_TYPE = 0x2d, BT_MP_INFO_OP_LNA_CONSTRAINT = 0x32, }; enum coex_set_ant_phase { COEX_SET_ANT_INIT, COEX_SET_ANT_WONLY, COEX_SET_ANT_WOFF, COEX_SET_ANT_2G, COEX_SET_ANT_5G, COEX_SET_ANT_POWERON, COEX_SET_ANT_2G_WLBT, COEX_SET_ANT_2G_FREERUN, COEX_SET_ANT_MAX }; enum coex_runreason { COEX_RSN_2GSCANSTART = 0, COEX_RSN_5GSCANSTART = 1, COEX_RSN_SCANFINISH = 2, COEX_RSN_2GSWITCHBAND = 3, COEX_RSN_5GSWITCHBAND = 4, COEX_RSN_2GCONSTART = 5, COEX_RSN_5GCONSTART = 6, COEX_RSN_2GCONFINISH = 7, COEX_RSN_5GCONFINISH = 8, COEX_RSN_2GMEDIA = 9, COEX_RSN_5GMEDIA = 10, COEX_RSN_MEDIADISCON = 11, COEX_RSN_BTINFO = 12, COEX_RSN_LPS = 13, COEX_RSN_WLSTATUS = 14, COEX_RSN_BTSTATUS = 15, COEX_RSN_MAX }; enum coex_lte_coex_table_type { COEX_CTT_WL_VS_LTE, COEX_CTT_BT_VS_LTE, }; enum coex_gnt_setup_state { COEX_GNT_SET_HW_PTA = 0x0, COEX_GNT_SET_SW_LOW = 0x1, COEX_GNT_SET_SW_HIGH = 0x3, }; enum coex_ext_ant_switch_pos_type { COEX_SWITCH_TO_BT, COEX_SWITCH_TO_WLG, COEX_SWITCH_TO_WLA, COEX_SWITCH_TO_NOCARE, COEX_SWITCH_TO_WLG_BT, COEX_SWITCH_TO_MAX }; enum coex_ext_ant_switch_ctrl_type { COEX_SWITCH_CTRL_BY_BBSW, COEX_SWITCH_CTRL_BY_PTA, COEX_SWITCH_CTRL_BY_ANTDIV, COEX_SWITCH_CTRL_BY_MAC, COEX_SWITCH_CTRL_BY_BT, COEX_SWITCH_CTRL_BY_FW, COEX_SWITCH_CTRL_MAX }; enum coex_algorithm { COEX_ALGO_NOPROFILE = 0, COEX_ALGO_HFP = 1, COEX_ALGO_HID = 2, COEX_ALGO_A2DP = 3, COEX_ALGO_PAN = 4, COEX_ALGO_A2DP_HID = 5, COEX_ALGO_A2DP_PAN = 6, COEX_ALGO_PAN_HID = 7, COEX_ALGO_A2DP_PAN_HID = 8, COEX_ALGO_MAX }; enum coex_bt_profile { BPM_NOPROFILE = 0, BPM_HFP = BIT(0), BPM_HID = BIT(1), BPM_A2DP = BIT(2), BPM_PAN = BIT(3), BPM_HID_HFP = BPM_HID | BPM_HFP, BPM_A2DP_HFP = BPM_A2DP | BPM_HFP, BPM_A2DP_HID = BPM_A2DP | BPM_HID, BPM_A2DP_HID_HFP = BPM_A2DP | BPM_HID | BPM_HFP, BPM_PAN_HFP = BPM_PAN | BPM_HFP, BPM_PAN_HID = BPM_PAN | BPM_HID, BPM_PAN_HID_HFP = BPM_PAN | BPM_HID | BPM_HFP, BPM_PAN_A2DP = BPM_PAN | BPM_A2DP, BPM_PAN_A2DP_HFP = BPM_PAN | BPM_A2DP | BPM_HFP, BPM_PAN_A2DP_HID = BPM_PAN | BPM_A2DP | BPM_HID, BPM_PAN_A2DP_HID_HFP = BPM_PAN | BPM_A2DP | BPM_HID | BPM_HFP, }; enum coex_wl_link_mode { COEX_WLINK_2G1PORT = 0x0, COEX_WLINK_5G = 0x3, COEX_WLINK_2GFREE = 0x7, COEX_WLINK_MAX }; enum coex_wl2bt_scoreboard { COEX_SCBD_ACTIVE = BIT(0), COEX_SCBD_ONOFF = BIT(1), COEX_SCBD_SCAN = BIT(2), COEX_SCBD_UNDERTEST = BIT(3), COEX_SCBD_RXGAIN = BIT(4), COEX_SCBD_BT_RFK = BIT(5), COEX_SCBD_WLBUSY = BIT(6), COEX_SCBD_EXTFEM = BIT(8), COEX_SCBD_TDMA = BIT(9), COEX_SCBD_FIX2M = BIT(10), COEX_SCBD_ALL = GENMASK(15, 0), }; enum coex_power_save_type { COEX_PS_WIFI_NATIVE = 0, COEX_PS_LPS_ON = 1, COEX_PS_LPS_OFF = 2, }; enum coex_rssi_state { COEX_RSSI_STATE_HIGH, COEX_RSSI_STATE_MEDIUM, COEX_RSSI_STATE_LOW, COEX_RSSI_STATE_STAY_HIGH, COEX_RSSI_STATE_STAY_MEDIUM, COEX_RSSI_STATE_STAY_LOW, }; enum coex_notify_type_ips { COEX_IPS_LEAVE = 0x0, COEX_IPS_ENTER = 0x1, }; enum coex_notify_type_lps { COEX_LPS_DISABLE = 0x0, COEX_LPS_ENABLE = 0x1, }; enum coex_notify_type_scan { COEX_SCAN_FINISH, COEX_SCAN_START, COEX_SCAN_START_2G, COEX_SCAN_START_5G, }; enum coex_notify_type_switchband { COEX_NOT_SWITCH, COEX_SWITCH_TO_24G, COEX_SWITCH_TO_5G, COEX_SWITCH_TO_24G_NOFORSCAN, }; enum coex_notify_type_associate { COEX_ASSOCIATE_FINISH, COEX_ASSOCIATE_START, COEX_ASSOCIATE_5G_FINISH, COEX_ASSOCIATE_5G_START, }; enum coex_notify_type_media_status { COEX_MEDIA_DISCONNECT, COEX_MEDIA_CONNECT, COEX_MEDIA_CONNECT_5G, }; enum coex_bt_status { COEX_BTSTATUS_NCON_IDLE = 0, COEX_BTSTATUS_CON_IDLE = 1, COEX_BTSTATUS_INQ_PAGE = 2, COEX_BTSTATUS_ACL_BUSY = 3, COEX_BTSTATUS_SCO_BUSY = 4, COEX_BTSTATUS_ACL_SCO_BUSY = 5, COEX_BTSTATUS_MAX }; enum coex_wl_tput_dir { COEX_WL_TPUT_TX = 0x0, COEX_WL_TPUT_RX = 0x1, COEX_WL_TPUT_MAX }; enum coex_wl_priority_mask { COEX_WLPRI_RX_RSP = 2, COEX_WLPRI_TX_RSP = 3, COEX_WLPRI_TX_BEACON = 4, COEX_WLPRI_TX_OFDM = 11, COEX_WLPRI_TX_CCK = 12, COEX_WLPRI_TX_BEACONQ = 27, COEX_WLPRI_RX_CCK = 28, COEX_WLPRI_RX_OFDM = 29, COEX_WLPRI_MAX }; enum coex_commom_chip_setup { COEX_CSETUP_INIT_HW = 0x0, COEX_CSETUP_ANT_SWITCH = 0x1, COEX_CSETUP_GNT_FIX = 0x2, COEX_CSETUP_GNT_DEBUG = 0x3, COEX_CSETUP_RFE_TYPE = 0x4, COEX_CSETUP_COEXINFO_HW = 0x5, COEX_CSETUP_WL_TX_POWER = 0x6, COEX_CSETUP_WL_RX_GAIN = 0x7, COEX_CSETUP_WLAN_ACT_IPS = 0x8, COEX_CSETUP_MAX }; enum coex_indirect_reg_type { COEX_INDIRECT_1700 = 0x0, COEX_INDIRECT_7C0 = 0x1, COEX_INDIRECT_MAX }; enum coex_pstdma_type { COEX_PSTDMA_FORCE_LPSOFF = 0x0, COEX_PSTDMA_FORCE_LPSON = 0x1, COEX_PSTDMA_MAX }; enum coex_btrssi_type { COEX_BTRSSI_RATIO = 0x0, COEX_BTRSSI_DBM = 0x1, COEX_BTRSSI_MAX }; struct coex_table_para { u32 bt; u32 wl; }; struct coex_tdma_para { u8 para[5]; }; struct coex_5g_afh_map { u32 wl_5g_ch; u8 bt_skip_ch; u8 bt_skip_span; }; struct coex_rf_para { u8 wl_pwr_dec_lvl; u8 bt_pwr_dec_lvl; bool wl_low_gain_en; u8 bt_lna_lvl; }; static inline void rtw_coex_set_init(struct rtw_dev *rtwdev) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; chip->ops->coex_set_init(rtwdev); } static inline void rtw_coex_set_ant_switch(struct rtw_dev *rtwdev, u8 ctrl_type, u8 pos_type) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; if (!chip->ops->coex_set_ant_switch) return; chip->ops->coex_set_ant_switch(rtwdev, ctrl_type, pos_type); } static inline void rtw_coex_set_gnt_fix(struct rtw_dev *rtwdev) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; chip->ops->coex_set_gnt_fix(rtwdev); } static inline void rtw_coex_set_gnt_debug(struct rtw_dev *rtwdev) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; chip->ops->coex_set_gnt_debug(rtwdev); } static inline void rtw_coex_set_rfe_type(struct rtw_dev *rtwdev) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; chip->ops->coex_set_rfe_type(rtwdev); } static inline void rtw_coex_set_wl_tx_power(struct rtw_dev *rtwdev, u8 wl_pwr) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; chip->ops->coex_set_wl_tx_power(rtwdev, wl_pwr); } static inline void rtw_coex_set_wl_rx_gain(struct rtw_dev *rtwdev, bool low_gain) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; chip->ops->coex_set_wl_rx_gain(rtwdev, low_gain); } void rtw_coex_info_response(struct rtw_dev *rtwdev, struct sk_buff *skb); u32 rtw_coex_read_indirect_reg(struct rtw_dev *rtwdev, u16 addr); void rtw_coex_write_indirect_reg(struct rtw_dev *rtwdev, u16 addr, u32 mask, u32 val); void rtw_coex_write_scbd(struct rtw_dev *rtwdev, u16 bitpos, bool set); void rtw_coex_bt_relink_work(struct work_struct *work); void rtw_coex_bt_reenable_work(struct work_struct *work); void rtw_coex_defreeze_work(struct work_struct *work); void rtw_coex_wl_remain_work(struct work_struct *work); void rtw_coex_bt_remain_work(struct work_struct *work); void rtw_coex_wl_connecting_work(struct work_struct *work); void rtw_coex_bt_multi_link_remain_work(struct work_struct *work); void rtw_coex_wl_ccklock_work(struct work_struct *work); void rtw_coex_power_on_setting(struct rtw_dev *rtwdev); void rtw_coex_power_off_setting(struct rtw_dev *rtwdev); void rtw_coex_init_hw_config(struct rtw_dev *rtwdev, bool wifi_only); void rtw_coex_ips_notify(struct rtw_dev *rtwdev, u8 type); void rtw_coex_lps_notify(struct rtw_dev *rtwdev, u8 type); void rtw_coex_scan_notify(struct rtw_dev *rtwdev, u8 type); void rtw_coex_connect_notify(struct rtw_dev *rtwdev, u8 type); void rtw_coex_media_status_notify(struct rtw_dev *rtwdev, u8 type); void rtw_coex_bt_info_notify(struct rtw_dev *rtwdev, u8 *buf, u8 length); void rtw_coex_bt_hid_info_notify(struct rtw_dev *rtwdev, u8 *buf, u8 length); void rtw_coex_wl_fwdbginfo_notify(struct rtw_dev *rtwdev, u8 *buf, u8 length); void rtw_coex_switchband_notify(struct rtw_dev *rtwdev, u8 type); void rtw_coex_wl_status_change_notify(struct rtw_dev *rtwdev, u32 type); void rtw_coex_wl_status_check(struct rtw_dev *rtwdev); void rtw_coex_query_bt_hid_list(struct rtw_dev *rtwdev); void rtw_coex_display_coex_info(struct rtw_dev *rtwdev, struct seq_file *m); static inline bool rtw_coex_disabled(struct rtw_dev *rtwdev) { struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_stat *coex_stat = &coex->stat; return coex_stat->bt_disabled; } #endif diff --git a/sys/contrib/dev/rtw88/debug.c b/sys/contrib/dev/rtw88/debug.c index fbbd11be5eef..d745c774aa6f 100644 --- a/sys/contrib/dev/rtw88/debug.c +++ b/sys/contrib/dev/rtw88/debug.c @@ -1,1294 +1,1339 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* Copyright(c) 2018-2019 Realtek Corporation */ #include #include #include "main.h" #include "coex.h" #include "sec.h" #include "fw.h" #include "debug.h" #include "phy.h" #include "reg.h" #include "ps.h" #include "regd.h" #ifdef CONFIG_RTW88_DEBUGFS struct rtw_debugfs_priv { struct rtw_dev *rtwdev; int (*cb_read)(struct seq_file *m, void *v); ssize_t (*cb_write)(struct file *filp, const char __user *buffer, size_t count, loff_t *loff); union { u32 cb_data; u8 *buf; struct { u32 page_offset; u32 page_num; } rsvd_page; struct { u8 rf_path; u32 rf_addr; u32 rf_mask; }; struct { u32 addr; u32 len; } read_reg; struct { u8 bit; } dm_cap; }; }; static const char * const rtw_dm_cap_strs[] = { [RTW_DM_CAP_NA] = "NA", [RTW_DM_CAP_TXGAPK] = "TXGAPK", }; static int rtw_debugfs_single_show(struct seq_file *m, void *v) { struct rtw_debugfs_priv *debugfs_priv = m->private; return debugfs_priv->cb_read(m, v); } static ssize_t rtw_debugfs_common_write(struct file *filp, const char __user *buffer, size_t count, loff_t *loff) { struct rtw_debugfs_priv *debugfs_priv = filp->private_data; return debugfs_priv->cb_write(filp, buffer, count, loff); } static ssize_t rtw_debugfs_single_write(struct file *filp, const char __user *buffer, size_t count, loff_t *loff) { struct seq_file *seqpriv = (struct seq_file *)filp->private_data; struct rtw_debugfs_priv *debugfs_priv = seqpriv->private; return debugfs_priv->cb_write(filp, buffer, count, loff); } static int rtw_debugfs_single_open_rw(struct inode *inode, struct file *filp) { return single_open(filp, rtw_debugfs_single_show, inode->i_private); } static int rtw_debugfs_close(struct inode *inode, struct file *filp) { return 0; } static const struct file_operations file_ops_single_r = { .owner = THIS_MODULE, .open = rtw_debugfs_single_open_rw, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static const struct file_operations file_ops_single_rw = { .owner = THIS_MODULE, .open = rtw_debugfs_single_open_rw, .release = single_release, .read = seq_read, .llseek = seq_lseek, .write = rtw_debugfs_single_write, }; static const struct file_operations file_ops_common_write = { .owner = THIS_MODULE, .write = rtw_debugfs_common_write, .open = simple_open, .release = rtw_debugfs_close, }; static int rtw_debugfs_get_read_reg(struct seq_file *m, void *v) { struct rtw_debugfs_priv *debugfs_priv = m->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; u32 val, len, addr; len = debugfs_priv->read_reg.len; addr = debugfs_priv->read_reg.addr; switch (len) { case 1: val = rtw_read8(rtwdev, addr); seq_printf(m, "reg 0x%03x: 0x%02x\n", addr, val); break; case 2: val = rtw_read16(rtwdev, addr); seq_printf(m, "reg 0x%03x: 0x%04x\n", addr, val); break; case 4: val = rtw_read32(rtwdev, addr); seq_printf(m, "reg 0x%03x: 0x%08x\n", addr, val); break; } return 0; } static int rtw_debugfs_get_rf_read(struct seq_file *m, void *v) { struct rtw_debugfs_priv *debugfs_priv = m->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; u32 val, addr, mask; u8 path; path = debugfs_priv->rf_path; addr = debugfs_priv->rf_addr; mask = debugfs_priv->rf_mask; + mutex_lock(&rtwdev->mutex); val = rtw_read_rf(rtwdev, path, addr, mask); + mutex_unlock(&rtwdev->mutex); seq_printf(m, "rf_read path:%d addr:0x%08x mask:0x%08x val=0x%08x\n", path, addr, mask, val); return 0; } static int rtw_debugfs_get_fix_rate(struct seq_file *m, void *v) { struct rtw_debugfs_priv *debugfs_priv = m->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 fix_rate = dm_info->fix_rate; if (fix_rate >= DESC_RATE_MAX) { seq_printf(m, "Fix rate disabled, fix_rate = %u\n", fix_rate); return 0; } seq_printf(m, "Data frames fixed at desc rate %u\n", fix_rate); return 0; } static int rtw_debugfs_copy_from_user(char tmp[], int size, const char __user *buffer, size_t count, int num) { int tmp_len; memset(tmp, 0, size); if (count < num) return -EFAULT; tmp_len = (count > size - 1 ? size - 1 : count); - if (!buffer || copy_from_user(tmp, buffer, tmp_len)) - return count; + if (copy_from_user(tmp, buffer, tmp_len)) + return -EFAULT; tmp[tmp_len] = '\0'; return 0; } static ssize_t rtw_debugfs_set_read_reg(struct file *filp, const char __user *buffer, size_t count, loff_t *loff) { struct seq_file *seqpriv = (struct seq_file *)filp->private_data; struct rtw_debugfs_priv *debugfs_priv = seqpriv->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; char tmp[32 + 1]; u32 addr, len; int num; + int ret; - rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 2); + ret = rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 2); + if (ret) + return ret; num = sscanf(tmp, "%x %x", &addr, &len); if (num != 2) - return count; + return -EINVAL; if (len != 1 && len != 2 && len != 4) { rtw_warn(rtwdev, "read reg setting wrong len\n"); return -EINVAL; } debugfs_priv->read_reg.addr = addr; debugfs_priv->read_reg.len = len; return count; } static int rtw_debugfs_get_dump_cam(struct seq_file *m, void *v) { struct rtw_debugfs_priv *debugfs_priv = m->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; u32 val, command; u32 hw_key_idx = debugfs_priv->cb_data << RTW_SEC_CAM_ENTRY_SHIFT; u32 read_cmd = RTW_SEC_CMD_POLLING; int i; seq_printf(m, "cam entry%d\n", debugfs_priv->cb_data); seq_puts(m, "0x0 0x1 0x2 0x3 "); seq_puts(m, "0x4 0x5\n"); mutex_lock(&rtwdev->mutex); for (i = 0; i <= 5; i++) { command = read_cmd | (hw_key_idx + i); rtw_write32(rtwdev, RTW_SEC_CMD_REG, command); val = rtw_read32(rtwdev, RTW_SEC_READ_REG); seq_printf(m, "%8.8x", val); if (i < 2) seq_puts(m, " "); } seq_puts(m, "\n"); mutex_unlock(&rtwdev->mutex); return 0; } static int rtw_debugfs_get_rsvd_page(struct seq_file *m, void *v) { struct rtw_debugfs_priv *debugfs_priv = m->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; u8 page_size = rtwdev->chip->page_size; u32 buf_size = debugfs_priv->rsvd_page.page_num * page_size; u32 offset = debugfs_priv->rsvd_page.page_offset * page_size; u8 *buf; int i; int ret; buf = vzalloc(buf_size); if (!buf) return -ENOMEM; ret = rtw_fw_dump_fifo(rtwdev, RTW_FW_FIFO_SEL_RSVD_PAGE, offset, buf_size, (u32 *)buf); if (ret) { rtw_err(rtwdev, "failed to dump rsvd page\n"); vfree(buf); return ret; } for (i = 0 ; i < buf_size ; i += 8) { if (i % page_size == 0) seq_printf(m, "PAGE %d\n", (i + offset) / page_size); seq_printf(m, "%8ph\n", buf + i); } vfree(buf); return 0; } static ssize_t rtw_debugfs_set_rsvd_page(struct file *filp, const char __user *buffer, size_t count, loff_t *loff) { struct seq_file *seqpriv = (struct seq_file *)filp->private_data; struct rtw_debugfs_priv *debugfs_priv = seqpriv->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; char tmp[32 + 1]; u32 offset, page_num; int num; + int ret; - rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 2); + ret = rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 2); + if (ret) + return ret; num = sscanf(tmp, "%d %d", &offset, &page_num); if (num != 2) { rtw_warn(rtwdev, "invalid arguments\n"); return -EINVAL; } debugfs_priv->rsvd_page.page_offset = offset; debugfs_priv->rsvd_page.page_num = page_num; return count; } static ssize_t rtw_debugfs_set_single_input(struct file *filp, const char __user *buffer, size_t count, loff_t *loff) { struct seq_file *seqpriv = (struct seq_file *)filp->private_data; struct rtw_debugfs_priv *debugfs_priv = seqpriv->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; char tmp[32 + 1]; u32 input; int num; + int ret; - rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 1); + ret = rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 1); + if (ret) + return ret; num = kstrtoint(tmp, 0, &input); if (num) { rtw_warn(rtwdev, "kstrtoint failed\n"); return num; } debugfs_priv->cb_data = input; return count; } static ssize_t rtw_debugfs_set_write_reg(struct file *filp, const char __user *buffer, size_t count, loff_t *loff) { struct rtw_debugfs_priv *debugfs_priv = filp->private_data; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; char tmp[32 + 1]; u32 addr, val, len; int num; + int ret; - rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 3); + ret = rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 3); + if (ret) + return ret; /* write BB/MAC register */ num = sscanf(tmp, "%x %x %x", &addr, &val, &len); if (num != 3) - return count; + return -EINVAL; switch (len) { case 1: rtw_dbg(rtwdev, RTW_DBG_DEBUGFS, "reg write8 0x%03x: 0x%08x\n", addr, val); rtw_write8(rtwdev, addr, (u8)val); break; case 2: rtw_dbg(rtwdev, RTW_DBG_DEBUGFS, "reg write16 0x%03x: 0x%08x\n", addr, val); rtw_write16(rtwdev, addr, (u16)val); break; case 4: rtw_dbg(rtwdev, RTW_DBG_DEBUGFS, "reg write32 0x%03x: 0x%08x\n", addr, val); rtw_write32(rtwdev, addr, (u32)val); break; default: rtw_dbg(rtwdev, RTW_DBG_DEBUGFS, "error write length = %d\n", len); break; } return count; } static ssize_t rtw_debugfs_set_h2c(struct file *filp, const char __user *buffer, size_t count, loff_t *loff) { struct rtw_debugfs_priv *debugfs_priv = filp->private_data; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; char tmp[32 + 1]; u8 param[8]; int num; + int ret; - rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 3); + ret = rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 3); + if (ret) + return ret; num = sscanf(tmp, "%hhx,%hhx,%hhx,%hhx,%hhx,%hhx,%hhx,%hhx", ¶m[0], ¶m[1], ¶m[2], ¶m[3], ¶m[4], ¶m[5], ¶m[6], ¶m[7]); if (num != 8) { rtw_warn(rtwdev, "invalid H2C command format for debug\n"); return -EINVAL; } + mutex_lock(&rtwdev->mutex); rtw_fw_h2c_cmd_dbg(rtwdev, param); + mutex_unlock(&rtwdev->mutex); return count; } static ssize_t rtw_debugfs_set_rf_write(struct file *filp, const char __user *buffer, size_t count, loff_t *loff) { struct rtw_debugfs_priv *debugfs_priv = filp->private_data; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; char tmp[32 + 1]; u32 path, addr, mask, val; int num; + int ret; - rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 4); + ret = rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 4); + if (ret) + return ret; num = sscanf(tmp, "%x %x %x %x", &path, &addr, &mask, &val); if (num != 4) { rtw_warn(rtwdev, "invalid args, [path] [addr] [mask] [val]\n"); - return count; + return -EINVAL; } + mutex_lock(&rtwdev->mutex); rtw_write_rf(rtwdev, path, addr, mask, val); + mutex_unlock(&rtwdev->mutex); rtw_dbg(rtwdev, RTW_DBG_DEBUGFS, "write_rf path:%d addr:0x%08x mask:0x%08x, val:0x%08x\n", path, addr, mask, val); return count; } static ssize_t rtw_debugfs_set_rf_read(struct file *filp, const char __user *buffer, size_t count, loff_t *loff) { struct seq_file *seqpriv = (struct seq_file *)filp->private_data; struct rtw_debugfs_priv *debugfs_priv = seqpriv->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; char tmp[32 + 1]; u32 path, addr, mask; int num; + int ret; - rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 3); + ret = rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 3); + if (ret) + return ret; num = sscanf(tmp, "%x %x %x", &path, &addr, &mask); if (num != 3) { rtw_warn(rtwdev, "invalid args, [path] [addr] [mask] [val]\n"); - return count; + return -EINVAL; } debugfs_priv->rf_path = path; debugfs_priv->rf_addr = addr; debugfs_priv->rf_mask = mask; return count; } static ssize_t rtw_debugfs_set_fix_rate(struct file *filp, const char __user *buffer, size_t count, loff_t *loff) { struct seq_file *seqpriv = (struct seq_file *)filp->private_data; struct rtw_debugfs_priv *debugfs_priv = seqpriv->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 fix_rate; char tmp[32 + 1]; int ret; - rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 1); + ret = rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 1); + if (ret) + return ret; ret = kstrtou8(tmp, 0, &fix_rate); if (ret) { rtw_warn(rtwdev, "invalid args, [rate]\n"); return ret; } dm_info->fix_rate = fix_rate; return count; } static int rtw_debug_get_mac_page(struct seq_file *m, void *v) { struct rtw_debugfs_priv *debugfs_priv = m->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; u32 page = debugfs_priv->cb_data; int i, n; int max = 0xff; rtw_read32(rtwdev, debugfs_priv->cb_data); for (n = 0; n <= max; ) { seq_printf(m, "\n%8.8x ", n + page); for (i = 0; i < 4 && n <= max; i++, n += 4) seq_printf(m, "%8.8x ", rtw_read32(rtwdev, (page | n))); } seq_puts(m, "\n"); return 0; } static int rtw_debug_get_bb_page(struct seq_file *m, void *v) { struct rtw_debugfs_priv *debugfs_priv = m->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; u32 page = debugfs_priv->cb_data; int i, n; int max = 0xff; rtw_read32(rtwdev, debugfs_priv->cb_data); for (n = 0; n <= max; ) { seq_printf(m, "\n%8.8x ", n + page); for (i = 0; i < 4 && n <= max; i++, n += 4) seq_printf(m, "%8.8x ", rtw_read32(rtwdev, (page | n))); } seq_puts(m, "\n"); return 0; } static int rtw_debug_get_rf_dump(struct seq_file *m, void *v) { struct rtw_debugfs_priv *debugfs_priv = m->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; u32 addr, offset, data; u8 path; + mutex_lock(&rtwdev->mutex); + for (path = 0; path < rtwdev->hal.rf_path_num; path++) { seq_printf(m, "RF path:%d\n", path); for (addr = 0; addr < 0x100; addr += 4) { seq_printf(m, "%8.8x ", addr); for (offset = 0; offset < 4; offset++) { data = rtw_read_rf(rtwdev, path, addr + offset, 0xffffffff); seq_printf(m, "%8.8x ", data); } seq_puts(m, "\n"); } seq_puts(m, "\n"); } + mutex_unlock(&rtwdev->mutex); + return 0; } static void rtw_print_cck_rate_txt(struct seq_file *m, u8 rate) { static const char * const cck_rate[] = {"1M", "2M", "5.5M", "11M"}; u8 idx = rate - DESC_RATE1M; seq_printf(m, " CCK_%-5s", cck_rate[idx]); } static void rtw_print_ofdm_rate_txt(struct seq_file *m, u8 rate) { static const char * const ofdm_rate[] = {"6M", "9M", "12M", "18M", "24M", "36M", "48M", "54M"}; u8 idx = rate - DESC_RATE6M; seq_printf(m, " OFDM_%-4s", ofdm_rate[idx]); } static void rtw_print_ht_rate_txt(struct seq_file *m, u8 rate) { u8 mcs_n = rate - DESC_RATEMCS0; seq_printf(m, " MCS%-6u", mcs_n); } static void rtw_print_vht_rate_txt(struct seq_file *m, u8 rate) { u8 idx = rate - DESC_RATEVHT1SS_MCS0; u8 n_ss, mcs_n; /* n spatial stream */ n_ss = 1 + idx / 10; /* MCS n */ mcs_n = idx % 10; seq_printf(m, " VHT%uSMCS%u", n_ss, mcs_n); } static void rtw_print_rate(struct seq_file *m, u8 rate) { switch (rate) { case DESC_RATE1M...DESC_RATE11M: rtw_print_cck_rate_txt(m, rate); break; case DESC_RATE6M...DESC_RATE54M: rtw_print_ofdm_rate_txt(m, rate); break; case DESC_RATEMCS0...DESC_RATEMCS15: rtw_print_ht_rate_txt(m, rate); break; case DESC_RATEVHT1SS_MCS0...DESC_RATEVHT2SS_MCS9: rtw_print_vht_rate_txt(m, rate); break; default: seq_printf(m, " Unknown rate=0x%x\n", rate); break; } } #define case_REGD(src) \ case RTW_REGD_##src: return #src static const char *rtw_get_regd_string(u8 regd) { switch (regd) { case_REGD(FCC); case_REGD(MKK); case_REGD(ETSI); case_REGD(IC); case_REGD(KCC); case_REGD(ACMA); case_REGD(CHILE); case_REGD(UKRAINE); case_REGD(MEXICO); case_REGD(CN); case_REGD(WW); default: return "Unknown"; } } static int rtw_debugfs_get_tx_pwr_tbl(struct seq_file *m, void *v) { struct rtw_debugfs_priv *debugfs_priv = m->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; struct rtw_hal *hal = &rtwdev->hal; - u8 path, rate; + u8 path, rate, bw, ch, regd; struct rtw_power_params pwr_param = {0}; - u8 bw = hal->current_band_width; - u8 ch = hal->current_channel; - u8 regd = rtw_regd_get(rtwdev); + + mutex_lock(&rtwdev->mutex); + bw = hal->current_band_width; + ch = hal->current_channel; + regd = rtw_regd_get(rtwdev); seq_printf(m, "channel: %u\n", ch); seq_printf(m, "bandwidth: %u\n", bw); seq_printf(m, "regulatory: %s\n", rtw_get_regd_string(regd)); seq_printf(m, "%-4s %-10s %-9s %-9s (%-4s %-4s %-4s) %-4s\n", "path", "rate", "pwr", "base", "byr", "lmt", "sar", "rem"); mutex_lock(&hal->tx_power_mutex); for (path = RF_PATH_A; path <= RF_PATH_B; path++) { /* there is no CCK rates used in 5G */ if (hal->current_band_type == RTW_BAND_5G) rate = DESC_RATE6M; else rate = DESC_RATE1M; /* now, not support vht 3ss and vht 4ss*/ for (; rate <= DESC_RATEVHT2SS_MCS9; rate++) { /* now, not support ht 3ss and ht 4ss*/ if (rate > DESC_RATEMCS15 && rate < DESC_RATEVHT1SS_MCS0) continue; rtw_get_tx_power_params(rtwdev, path, rate, bw, ch, regd, &pwr_param); seq_printf(m, "%4c ", path + 'A'); rtw_print_rate(m, rate); seq_printf(m, " %3u(0x%02x) %4u %4d (%4d %4d %4d) %4d\n", hal->tx_pwr_tbl[path][rate], hal->tx_pwr_tbl[path][rate], pwr_param.pwr_base, min3(pwr_param.pwr_offset, pwr_param.pwr_limit, pwr_param.pwr_sar), pwr_param.pwr_offset, pwr_param.pwr_limit, pwr_param.pwr_sar, pwr_param.pwr_remnant); } } mutex_unlock(&hal->tx_power_mutex); + mutex_unlock(&rtwdev->mutex); return 0; } void rtw_debugfs_get_simple_phy_info(struct seq_file *m) { struct rtw_debugfs_priv *debugfs_priv = m->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; struct rtw_hal *hal = &rtwdev->hal; struct rtw_dm_info *dm_info = &rtwdev->dm_info; struct rtw_traffic_stats *stats = &rtwdev->stats; seq_printf(m, "%-40s = %ddBm/ %d\n", "RSSI/ STA Channel", dm_info->rssi[RF_PATH_A] - 100, hal->current_channel); seq_printf(m, "TP {Tx, Rx} = {%u, %u}Mbps\n", stats->tx_throughput, stats->rx_throughput); seq_puts(m, "[Tx Rate] = "); rtw_print_rate(m, dm_info->tx_rate); seq_printf(m, "(0x%x)\n", dm_info->tx_rate); seq_puts(m, "[Rx Rate] = "); rtw_print_rate(m, dm_info->curr_rx_rate); seq_printf(m, "(0x%x)\n", dm_info->curr_rx_rate); } static int rtw_debugfs_get_phy_info(struct seq_file *m, void *v) { struct rtw_debugfs_priv *debugfs_priv = m->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; struct rtw_dm_info *dm_info = &rtwdev->dm_info; struct rtw_traffic_stats *stats = &rtwdev->stats; struct rtw_pkt_count *last_cnt = &dm_info->last_pkt_count; struct rtw_efuse *efuse = &rtwdev->efuse; struct ewma_evm *ewma_evm = dm_info->ewma_evm; struct ewma_snr *ewma_snr = dm_info->ewma_snr; u8 ss, rate_id; seq_puts(m, "==========[Common Info]========\n"); seq_printf(m, "Is link = %c\n", rtw_is_assoc(rtwdev) ? 'Y' : 'N'); seq_printf(m, "Current CH(fc) = %u\n", rtwdev->hal.current_channel); seq_printf(m, "Current BW = %u\n", rtwdev->hal.current_band_width); seq_printf(m, "Current IGI = 0x%x\n", dm_info->igi_history[0]); seq_printf(m, "TP {Tx, Rx} = {%u, %u}Mbps\n", stats->tx_throughput, stats->rx_throughput); seq_printf(m, "1SS for TX and RX = %c\n\n", rtwdev->hal.txrx_1ss ? 'Y' : 'N'); seq_puts(m, "==========[Tx Phy Info]========\n"); seq_puts(m, "[Tx Rate] = "); rtw_print_rate(m, dm_info->tx_rate); seq_printf(m, "(0x%x)\n\n", dm_info->tx_rate); seq_puts(m, "==========[Rx Phy Info]========\n"); seq_printf(m, "[Rx Beacon Count] = %u\n", last_cnt->num_bcn_pkt); seq_puts(m, "[Rx Rate] = "); rtw_print_rate(m, dm_info->curr_rx_rate); seq_printf(m, "(0x%x)\n", dm_info->curr_rx_rate); seq_puts(m, "[Rx Rate Count]:\n"); seq_printf(m, " * CCK = {%u, %u, %u, %u}\n", last_cnt->num_qry_pkt[DESC_RATE1M], last_cnt->num_qry_pkt[DESC_RATE2M], last_cnt->num_qry_pkt[DESC_RATE5_5M], last_cnt->num_qry_pkt[DESC_RATE11M]); seq_printf(m, " * OFDM = {%u, %u, %u, %u, %u, %u, %u, %u}\n", last_cnt->num_qry_pkt[DESC_RATE6M], last_cnt->num_qry_pkt[DESC_RATE9M], last_cnt->num_qry_pkt[DESC_RATE12M], last_cnt->num_qry_pkt[DESC_RATE18M], last_cnt->num_qry_pkt[DESC_RATE24M], last_cnt->num_qry_pkt[DESC_RATE36M], last_cnt->num_qry_pkt[DESC_RATE48M], last_cnt->num_qry_pkt[DESC_RATE54M]); for (ss = 0; ss < efuse->hw_cap.nss; ss++) { rate_id = DESC_RATEMCS0 + ss * 8; seq_printf(m, " * HT_MCS[%u:%u] = {%u, %u, %u, %u, %u, %u, %u, %u}\n", ss * 8, ss * 8 + 7, last_cnt->num_qry_pkt[rate_id], last_cnt->num_qry_pkt[rate_id + 1], last_cnt->num_qry_pkt[rate_id + 2], last_cnt->num_qry_pkt[rate_id + 3], last_cnt->num_qry_pkt[rate_id + 4], last_cnt->num_qry_pkt[rate_id + 5], last_cnt->num_qry_pkt[rate_id + 6], last_cnt->num_qry_pkt[rate_id + 7]); } for (ss = 0; ss < efuse->hw_cap.nss; ss++) { rate_id = DESC_RATEVHT1SS_MCS0 + ss * 10; seq_printf(m, " * VHT_MCS-%uss MCS[0:9] = {%u, %u, %u, %u, %u, %u, %u, %u, %u, %u}\n", ss + 1, last_cnt->num_qry_pkt[rate_id], last_cnt->num_qry_pkt[rate_id + 1], last_cnt->num_qry_pkt[rate_id + 2], last_cnt->num_qry_pkt[rate_id + 3], last_cnt->num_qry_pkt[rate_id + 4], last_cnt->num_qry_pkt[rate_id + 5], last_cnt->num_qry_pkt[rate_id + 6], last_cnt->num_qry_pkt[rate_id + 7], last_cnt->num_qry_pkt[rate_id + 8], last_cnt->num_qry_pkt[rate_id + 9]); } seq_printf(m, "[RSSI(dBm)] = {%d, %d}\n", dm_info->rssi[RF_PATH_A] - 100, dm_info->rssi[RF_PATH_B] - 100); seq_printf(m, "[Rx EVM(dB)] = {-%d, -%d}\n", dm_info->rx_evm_dbm[RF_PATH_A], dm_info->rx_evm_dbm[RF_PATH_B]); seq_printf(m, "[Rx SNR] = {%d, %d}\n", dm_info->rx_snr[RF_PATH_A], dm_info->rx_snr[RF_PATH_B]); seq_printf(m, "[CFO_tail(KHz)] = {%d, %d}\n", dm_info->cfo_tail[RF_PATH_A], dm_info->cfo_tail[RF_PATH_B]); if (dm_info->curr_rx_rate >= DESC_RATE11M) { seq_puts(m, "[Rx Average Status]:\n"); seq_printf(m, " * OFDM, EVM: {-%d}, SNR: {%d}\n", (u8)ewma_evm_read(&ewma_evm[RTW_EVM_OFDM]), (u8)ewma_snr_read(&ewma_snr[RTW_SNR_OFDM_A])); seq_printf(m, " * 1SS, EVM: {-%d}, SNR: {%d}\n", (u8)ewma_evm_read(&ewma_evm[RTW_EVM_1SS]), (u8)ewma_snr_read(&ewma_snr[RTW_SNR_1SS_A])); seq_printf(m, " * 2SS, EVM: {-%d, -%d}, SNR: {%d, %d}\n", (u8)ewma_evm_read(&ewma_evm[RTW_EVM_2SS_A]), (u8)ewma_evm_read(&ewma_evm[RTW_EVM_2SS_B]), (u8)ewma_snr_read(&ewma_snr[RTW_SNR_2SS_A]), (u8)ewma_snr_read(&ewma_snr[RTW_SNR_2SS_B])); } seq_puts(m, "[Rx Counter]:\n"); seq_printf(m, " * CCA (CCK, OFDM, Total) = (%u, %u, %u)\n", dm_info->cck_cca_cnt, dm_info->ofdm_cca_cnt, dm_info->total_cca_cnt); seq_printf(m, " * False Alarm (CCK, OFDM, Total) = (%u, %u, %u)\n", dm_info->cck_fa_cnt, dm_info->ofdm_fa_cnt, dm_info->total_fa_cnt); seq_printf(m, " * CCK cnt (ok, err) = (%u, %u)\n", dm_info->cck_ok_cnt, dm_info->cck_err_cnt); seq_printf(m, " * OFDM cnt (ok, err) = (%u, %u)\n", dm_info->ofdm_ok_cnt, dm_info->ofdm_err_cnt); seq_printf(m, " * HT cnt (ok, err) = (%u, %u)\n", dm_info->ht_ok_cnt, dm_info->ht_err_cnt); seq_printf(m, " * VHT cnt (ok, err) = (%u, %u)\n", dm_info->vht_ok_cnt, dm_info->vht_err_cnt); return 0; } static int rtw_debugfs_get_coex_info(struct seq_file *m, void *v) { struct rtw_debugfs_priv *debugfs_priv = m->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; + mutex_lock(&rtwdev->mutex); rtw_coex_display_coex_info(rtwdev, m); + mutex_unlock(&rtwdev->mutex); return 0; } static ssize_t rtw_debugfs_set_coex_enable(struct file *filp, const char __user *buffer, size_t count, loff_t *loff) { struct seq_file *seqpriv = (struct seq_file *)filp->private_data; struct rtw_debugfs_priv *debugfs_priv = seqpriv->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; struct rtw_coex *coex = &rtwdev->coex; char tmp[32 + 1]; bool enable; int ret; - rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 1); + ret = rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 1); + if (ret) + return ret; ret = kstrtobool(tmp, &enable); if (ret) { rtw_warn(rtwdev, "invalid arguments\n"); return ret; } mutex_lock(&rtwdev->mutex); coex->manual_control = !enable; mutex_unlock(&rtwdev->mutex); return count; } static int rtw_debugfs_get_coex_enable(struct seq_file *m, void *v) { struct rtw_debugfs_priv *debugfs_priv = m->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; struct rtw_coex *coex = &rtwdev->coex; seq_printf(m, "coex mechanism %s\n", coex->manual_control ? "disabled" : "enabled"); return 0; } static ssize_t rtw_debugfs_set_edcca_enable(struct file *filp, const char __user *buffer, size_t count, loff_t *loff) { struct seq_file *seqpriv = (struct seq_file *)filp->private_data; struct rtw_debugfs_priv *debugfs_priv = seqpriv->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; bool input; int err; err = kstrtobool_from_user(buffer, count, &input); if (err) return err; rtw_edcca_enabled = input; rtw_phy_adaptivity_set_mode(rtwdev); return count; } static int rtw_debugfs_get_edcca_enable(struct seq_file *m, void *v) { struct rtw_debugfs_priv *debugfs_priv = m->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; struct rtw_dm_info *dm_info = &rtwdev->dm_info; seq_printf(m, "EDCCA %s: EDCCA mode %d\n", rtw_edcca_enabled ? "enabled" : "disabled", dm_info->edcca_mode); return 0; } static ssize_t rtw_debugfs_set_fw_crash(struct file *filp, const char __user *buffer, size_t count, loff_t *loff) { struct seq_file *seqpriv = (struct seq_file *)filp->private_data; struct rtw_debugfs_priv *debugfs_priv = seqpriv->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; char tmp[32 + 1]; bool input; int ret; - rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 1); + ret = rtw_debugfs_copy_from_user(tmp, sizeof(tmp), buffer, count, 1); + if (ret) + return ret; ret = kstrtobool(tmp, &input); if (ret) return -EINVAL; if (!input) return -EINVAL; if (test_bit(RTW_FLAG_RESTARTING, rtwdev->flags)) return -EINPROGRESS; mutex_lock(&rtwdev->mutex); rtw_leave_lps_deep(rtwdev); set_bit(RTW_FLAG_RESTART_TRIGGERING, rtwdev->flags); rtw_write8(rtwdev, REG_HRCV_MSG, 1); mutex_unlock(&rtwdev->mutex); return count; } static int rtw_debugfs_get_fw_crash(struct seq_file *m, void *v) { struct rtw_debugfs_priv *debugfs_priv = m->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; seq_printf(m, "%d\n", test_bit(RTW_FLAG_RESTART_TRIGGERING, rtwdev->flags) || test_bit(RTW_FLAG_RESTARTING, rtwdev->flags)); return 0; } static ssize_t rtw_debugfs_set_force_lowest_basic_rate(struct file *filp, const char __user *buffer, size_t count, loff_t *loff) { struct seq_file *seqpriv = (struct seq_file *)filp->private_data; struct rtw_debugfs_priv *debugfs_priv = seqpriv->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; bool input; int err; err = kstrtobool_from_user(buffer, count, &input); if (err) return err; if (input) set_bit(RTW_FLAG_FORCE_LOWEST_RATE, rtwdev->flags); else clear_bit(RTW_FLAG_FORCE_LOWEST_RATE, rtwdev->flags); return count; } static int rtw_debugfs_get_force_lowest_basic_rate(struct seq_file *m, void *v) { struct rtw_debugfs_priv *debugfs_priv = m->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; seq_printf(m, "force lowest basic rate: %d\n", test_bit(RTW_FLAG_FORCE_LOWEST_RATE, rtwdev->flags)); return 0; } static ssize_t rtw_debugfs_set_dm_cap(struct file *filp, const char __user *buffer, size_t count, loff_t *loff) { struct seq_file *seqpriv = (struct seq_file *)filp->private_data; struct rtw_debugfs_priv *debugfs_priv = seqpriv->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; struct rtw_dm_info *dm_info = &rtwdev->dm_info; int bit; bool en; if (kstrtoint_from_user(buffer, count, 10, &bit)) return -EINVAL; en = bit > 0; bit = abs(bit); if (bit >= RTW_DM_CAP_NUM) { rtw_warn(rtwdev, "unknown DM CAP %d\n", bit); return -EINVAL; } if (en) dm_info->dm_flags &= ~BIT(bit); else dm_info->dm_flags |= BIT(bit); debugfs_priv->dm_cap.bit = bit; return count; } static void dump_gapk_status(struct rtw_dev *rtwdev, struct seq_file *m) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; struct rtw_gapk_info *txgapk = &rtwdev->dm_info.gapk; int i, path; u32 val; seq_printf(m, "\n(%2d) %c%s\n\n", RTW_DM_CAP_TXGAPK, dm_info->dm_flags & BIT(RTW_DM_CAP_TXGAPK) ? '-' : '+', rtw_dm_cap_strs[RTW_DM_CAP_TXGAPK]); + mutex_lock(&rtwdev->mutex); + for (path = 0; path < rtwdev->hal.rf_path_num; path++) { val = rtw_read_rf(rtwdev, path, RF_GAINTX, RFREG_MASK); seq_printf(m, "path %d:\n0x%x = 0x%x\n", path, RF_GAINTX, val); for (i = 0; i < RF_HW_OFFSET_NUM; i++) seq_printf(m, "[TXGAPK] offset %d %d\n", txgapk->rf3f_fs[path][i], i); seq_puts(m, "\n"); } + mutex_unlock(&rtwdev->mutex); } static int rtw_debugfs_get_dm_cap(struct seq_file *m, void *v) { struct rtw_debugfs_priv *debugfs_priv = m->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; struct rtw_dm_info *dm_info = &rtwdev->dm_info; int i; switch (debugfs_priv->dm_cap.bit) { case RTW_DM_CAP_TXGAPK: dump_gapk_status(rtwdev, m); break; default: for (i = 1; i < RTW_DM_CAP_NUM; i++) { seq_printf(m, "(%2d) %c%s\n", i, dm_info->dm_flags & BIT(i) ? '-' : '+', rtw_dm_cap_strs[i]); } break; } debugfs_priv->dm_cap.bit = RTW_DM_CAP_NA; return 0; } #define rtw_debug_impl_mac(page, addr) \ static struct rtw_debugfs_priv rtw_debug_priv_mac_ ##page = { \ .cb_read = rtw_debug_get_mac_page, \ .cb_data = addr, \ } rtw_debug_impl_mac(0, 0x0000); rtw_debug_impl_mac(1, 0x0100); rtw_debug_impl_mac(2, 0x0200); rtw_debug_impl_mac(3, 0x0300); rtw_debug_impl_mac(4, 0x0400); rtw_debug_impl_mac(5, 0x0500); rtw_debug_impl_mac(6, 0x0600); rtw_debug_impl_mac(7, 0x0700); rtw_debug_impl_mac(10, 0x1000); rtw_debug_impl_mac(11, 0x1100); rtw_debug_impl_mac(12, 0x1200); rtw_debug_impl_mac(13, 0x1300); rtw_debug_impl_mac(14, 0x1400); rtw_debug_impl_mac(15, 0x1500); rtw_debug_impl_mac(16, 0x1600); rtw_debug_impl_mac(17, 0x1700); #define rtw_debug_impl_bb(page, addr) \ static struct rtw_debugfs_priv rtw_debug_priv_bb_ ##page = { \ .cb_read = rtw_debug_get_bb_page, \ .cb_data = addr, \ } rtw_debug_impl_bb(8, 0x0800); rtw_debug_impl_bb(9, 0x0900); rtw_debug_impl_bb(a, 0x0a00); rtw_debug_impl_bb(b, 0x0b00); rtw_debug_impl_bb(c, 0x0c00); rtw_debug_impl_bb(d, 0x0d00); rtw_debug_impl_bb(e, 0x0e00); rtw_debug_impl_bb(f, 0x0f00); rtw_debug_impl_bb(18, 0x1800); rtw_debug_impl_bb(19, 0x1900); rtw_debug_impl_bb(1a, 0x1a00); rtw_debug_impl_bb(1b, 0x1b00); rtw_debug_impl_bb(1c, 0x1c00); rtw_debug_impl_bb(1d, 0x1d00); rtw_debug_impl_bb(1e, 0x1e00); rtw_debug_impl_bb(1f, 0x1f00); rtw_debug_impl_bb(2c, 0x2c00); rtw_debug_impl_bb(2d, 0x2d00); rtw_debug_impl_bb(40, 0x4000); rtw_debug_impl_bb(41, 0x4100); static struct rtw_debugfs_priv rtw_debug_priv_rf_dump = { .cb_read = rtw_debug_get_rf_dump, }; static struct rtw_debugfs_priv rtw_debug_priv_tx_pwr_tbl = { .cb_read = rtw_debugfs_get_tx_pwr_tbl, }; static struct rtw_debugfs_priv rtw_debug_priv_write_reg = { .cb_write = rtw_debugfs_set_write_reg, }; static struct rtw_debugfs_priv rtw_debug_priv_h2c = { .cb_write = rtw_debugfs_set_h2c, }; static struct rtw_debugfs_priv rtw_debug_priv_rf_write = { .cb_write = rtw_debugfs_set_rf_write, }; static struct rtw_debugfs_priv rtw_debug_priv_rf_read = { .cb_write = rtw_debugfs_set_rf_read, .cb_read = rtw_debugfs_get_rf_read, }; static struct rtw_debugfs_priv rtw_debug_priv_read_reg = { .cb_write = rtw_debugfs_set_read_reg, .cb_read = rtw_debugfs_get_read_reg, }; static struct rtw_debugfs_priv rtw_debug_priv_fix_rate = { .cb_write = rtw_debugfs_set_fix_rate, .cb_read = rtw_debugfs_get_fix_rate, }; static struct rtw_debugfs_priv rtw_debug_priv_dump_cam = { .cb_write = rtw_debugfs_set_single_input, .cb_read = rtw_debugfs_get_dump_cam, }; static struct rtw_debugfs_priv rtw_debug_priv_rsvd_page = { .cb_write = rtw_debugfs_set_rsvd_page, .cb_read = rtw_debugfs_get_rsvd_page, }; static struct rtw_debugfs_priv rtw_debug_priv_phy_info = { .cb_read = rtw_debugfs_get_phy_info, }; static struct rtw_debugfs_priv rtw_debug_priv_coex_enable = { .cb_write = rtw_debugfs_set_coex_enable, .cb_read = rtw_debugfs_get_coex_enable, }; static struct rtw_debugfs_priv rtw_debug_priv_coex_info = { .cb_read = rtw_debugfs_get_coex_info, }; static struct rtw_debugfs_priv rtw_debug_priv_edcca_enable = { .cb_write = rtw_debugfs_set_edcca_enable, .cb_read = rtw_debugfs_get_edcca_enable, }; static struct rtw_debugfs_priv rtw_debug_priv_fw_crash = { .cb_write = rtw_debugfs_set_fw_crash, .cb_read = rtw_debugfs_get_fw_crash, }; static struct rtw_debugfs_priv rtw_debug_priv_force_lowest_basic_rate = { .cb_write = rtw_debugfs_set_force_lowest_basic_rate, .cb_read = rtw_debugfs_get_force_lowest_basic_rate, }; static struct rtw_debugfs_priv rtw_debug_priv_dm_cap = { .cb_write = rtw_debugfs_set_dm_cap, .cb_read = rtw_debugfs_get_dm_cap, }; #define rtw_debugfs_add_core(name, mode, fopname, parent) \ do { \ rtw_debug_priv_ ##name.rtwdev = rtwdev; \ if (!debugfs_create_file(#name, mode, \ parent, &rtw_debug_priv_ ##name,\ &file_ops_ ##fopname)) \ pr_debug("Unable to initialize debugfs:%s\n", \ #name); \ } while (0) #define rtw_debugfs_add_w(name) \ rtw_debugfs_add_core(name, S_IFREG | 0222, common_write, debugfs_topdir) #define rtw_debugfs_add_rw(name) \ rtw_debugfs_add_core(name, S_IFREG | 0666, single_rw, debugfs_topdir) #define rtw_debugfs_add_r(name) \ rtw_debugfs_add_core(name, S_IFREG | 0444, single_r, debugfs_topdir) void rtw_debugfs_init(struct rtw_dev *rtwdev) { struct dentry *debugfs_topdir; debugfs_topdir = debugfs_create_dir("rtw88", rtwdev->hw->wiphy->debugfsdir); rtw_debugfs_add_w(write_reg); rtw_debugfs_add_rw(read_reg); rtw_debugfs_add_w(rf_write); rtw_debugfs_add_rw(rf_read); rtw_debugfs_add_rw(fix_rate); rtw_debugfs_add_rw(dump_cam); rtw_debugfs_add_rw(rsvd_page); rtw_debugfs_add_r(phy_info); rtw_debugfs_add_r(coex_info); rtw_debugfs_add_rw(coex_enable); rtw_debugfs_add_w(h2c); rtw_debugfs_add_r(mac_0); rtw_debugfs_add_r(mac_1); rtw_debugfs_add_r(mac_2); rtw_debugfs_add_r(mac_3); rtw_debugfs_add_r(mac_4); rtw_debugfs_add_r(mac_5); rtw_debugfs_add_r(mac_6); rtw_debugfs_add_r(mac_7); rtw_debugfs_add_r(bb_8); rtw_debugfs_add_r(bb_9); rtw_debugfs_add_r(bb_a); rtw_debugfs_add_r(bb_b); rtw_debugfs_add_r(bb_c); rtw_debugfs_add_r(bb_d); rtw_debugfs_add_r(bb_e); rtw_debugfs_add_r(bb_f); rtw_debugfs_add_r(mac_10); rtw_debugfs_add_r(mac_11); rtw_debugfs_add_r(mac_12); rtw_debugfs_add_r(mac_13); rtw_debugfs_add_r(mac_14); rtw_debugfs_add_r(mac_15); rtw_debugfs_add_r(mac_16); rtw_debugfs_add_r(mac_17); rtw_debugfs_add_r(bb_18); rtw_debugfs_add_r(bb_19); rtw_debugfs_add_r(bb_1a); rtw_debugfs_add_r(bb_1b); rtw_debugfs_add_r(bb_1c); rtw_debugfs_add_r(bb_1d); rtw_debugfs_add_r(bb_1e); rtw_debugfs_add_r(bb_1f); if (rtwdev->chip->id == RTW_CHIP_TYPE_8822C) { rtw_debugfs_add_r(bb_2c); rtw_debugfs_add_r(bb_2d); rtw_debugfs_add_r(bb_40); rtw_debugfs_add_r(bb_41); } rtw_debugfs_add_r(rf_dump); rtw_debugfs_add_r(tx_pwr_tbl); rtw_debugfs_add_rw(edcca_enable); rtw_debugfs_add_rw(fw_crash); rtw_debugfs_add_rw(force_lowest_basic_rate); rtw_debugfs_add_rw(dm_cap); } #endif /* CONFIG_RTW88_DEBUGFS */ #ifdef CONFIG_RTW88_DEBUG void __rtw_dbg(struct rtw_dev *rtwdev, enum rtw_debug_mask mask, const char *fmt, ...) { struct va_format vaf = { .fmt = fmt, }; va_list args; va_start(args, fmt); vaf.va = &args; if (rtw_debug_mask & mask) #if defined(__linux__) dev_printk(KERN_DEBUG, rtwdev->dev, "%pV", &vaf); #elif defined(__FreeBSD__) vlog(LOG_DEBUG, vaf.fmt, args); #endif va_end(args); } EXPORT_SYMBOL(__rtw_dbg); #endif /* CONFIG_RTW88_DEBUG */ diff --git a/sys/contrib/dev/rtw88/debug.h b/sys/contrib/dev/rtw88/debug.h index 7bf59d3dfd5a..e826102083cf 100644 --- a/sys/contrib/dev/rtw88/debug.h +++ b/sys/contrib/dev/rtw88/debug.h @@ -1,68 +1,69 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* Copyright(c) 2018-2019 Realtek Corporation */ #ifndef __RTW_DEBUG_H #define __RTW_DEBUG_H enum rtw_debug_mask { RTW_DBG_PCI = 0x00000001, RTW_DBG_TX = 0x00000002, RTW_DBG_RX = 0x00000004, RTW_DBG_PHY = 0x00000008, RTW_DBG_FW = 0x00000010, RTW_DBG_EFUSE = 0x00000020, RTW_DBG_COEX = 0x00000040, RTW_DBG_RFK = 0x00000080, RTW_DBG_REGD = 0x00000100, RTW_DBG_DEBUGFS = 0x00000200, RTW_DBG_PS = 0x00000400, RTW_DBG_BF = 0x00000800, RTW_DBG_WOW = 0x00001000, RTW_DBG_CFO = 0x00002000, RTW_DBG_PATH_DIV = 0x00004000, RTW_DBG_ADAPTIVITY = 0x00008000, RTW_DBG_HW_SCAN = 0x00010000, RTW_DBG_STATE = 0x00020000, + RTW_DBG_SDIO = 0x00040000, #if defined(__FreeBSD__) RTW_DBG_IO_RW = 0x80000000, #endif RTW_DBG_ALL = 0xffffffff }; #ifdef CONFIG_RTW88_DEBUGFS void rtw_debugfs_init(struct rtw_dev *rtwdev); void rtw_debugfs_get_simple_phy_info(struct seq_file *m); #else static inline void rtw_debugfs_init(struct rtw_dev *rtwdev) {} #endif /* CONFIG_RTW88_DEBUGFS */ #ifdef CONFIG_RTW88_DEBUG __printf(3, 4) void __rtw_dbg(struct rtw_dev *rtwdev, enum rtw_debug_mask mask, const char *fmt, ...); #if defined(__linux__) #define rtw_dbg(rtwdev, a...) __rtw_dbg(rtwdev, ##a) #elif defined(__FreeBSD__) #define rtw_dbg(rtwdev, ...) __rtw_dbg(rtwdev, __VA_ARGS__) #endif #else static inline void rtw_dbg(struct rtw_dev *rtwdev, enum rtw_debug_mask mask, const char *fmt, ...) {} #endif /* CONFIG_RTW88_DEBUG */ #define rtw_info(rtwdev, a...) dev_info(rtwdev->dev, ##a) #define rtw_warn(rtwdev, a...) dev_warn(rtwdev->dev, ##a) #define rtw_err(rtwdev, a...) dev_err(rtwdev->dev, ##a) #endif diff --git a/sys/contrib/dev/rtw88/efuse.c b/sys/contrib/dev/rtw88/efuse.c index c266c84ef233..b85075cd68d0 100644 --- a/sys/contrib/dev/rtw88/efuse.c +++ b/sys/contrib/dev/rtw88/efuse.c @@ -1,187 +1,187 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* Copyright(c) 2018-2019 Realtek Corporation */ #include #include "main.h" #include "efuse.h" #include "reg.h" #include "debug.h" #define RTW_EFUSE_BANK_WIFI 0x0 static void switch_efuse_bank(struct rtw_dev *rtwdev) { rtw_write32_mask(rtwdev, REG_LDO_EFUSE_CTRL, BIT_MASK_EFUSE_BANK_SEL, RTW_EFUSE_BANK_WIFI); } #define invalid_efuse_header(hdr1, hdr2) \ ((hdr1) == 0xff || (((hdr1) & 0x1f) == 0xf && (hdr2) == 0xff)) #define invalid_efuse_content(word_en, i) \ (((word_en) & BIT(i)) != 0x0) #define get_efuse_blk_idx_2_byte(hdr1, hdr2) \ ((((hdr2) & 0xf0) >> 1) | (((hdr1) >> 5) & 0x07)) #define get_efuse_blk_idx_1_byte(hdr1) \ (((hdr1) & 0xf0) >> 4) #define block_idx_to_logical_idx(blk_idx, i) \ (((blk_idx) << 3) + ((i) << 1)) /* efuse header format * * | 7 5 4 0 | 7 4 3 0 | 15 8 7 0 | * block[2:0] 0 1111 block[6:3] word_en[3:0] byte0 byte1 * | header 1 (optional) | header 2 | word N | * * word_en: 4 bits each word. 0 -> write; 1 -> not write * N: 1~4, depends on word_en */ static int rtw_dump_logical_efuse_map(struct rtw_dev *rtwdev, u8 *phy_map, u8 *log_map) { u32 physical_size = rtwdev->efuse.physical_size; u32 protect_size = rtwdev->efuse.protect_size; u32 logical_size = rtwdev->efuse.logical_size; u32 phy_idx, log_idx; u8 hdr1, hdr2; u8 blk_idx; u8 word_en; int i; for (phy_idx = 0; phy_idx < physical_size - protect_size;) { hdr1 = phy_map[phy_idx]; hdr2 = phy_map[phy_idx + 1]; if (invalid_efuse_header(hdr1, hdr2)) break; if ((hdr1 & 0x1f) == 0xf) { /* 2-byte header format */ blk_idx = get_efuse_blk_idx_2_byte(hdr1, hdr2); word_en = hdr2 & 0xf; phy_idx += 2; } else { /* 1-byte header format */ blk_idx = get_efuse_blk_idx_1_byte(hdr1); word_en = hdr1 & 0xf; phy_idx += 1; } for (i = 0; i < 4; i++) { if (invalid_efuse_content(word_en, i)) continue; log_idx = block_idx_to_logical_idx(blk_idx, i); if (phy_idx + 1 > physical_size - protect_size || log_idx + 1 > logical_size) return -EINVAL; log_map[log_idx] = phy_map[phy_idx]; log_map[log_idx + 1] = phy_map[phy_idx + 1]; phy_idx += 2; } } return 0; } static int rtw_dump_physical_efuse_map(struct rtw_dev *rtwdev, u8 *map) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; u32 size = rtwdev->efuse.physical_size; u32 efuse_ctl; u32 addr; u32 cnt; rtw_chip_efuse_grant_on(rtwdev); switch_efuse_bank(rtwdev); /* disable 2.5V LDO */ chip->ops->cfg_ldo25(rtwdev, false); efuse_ctl = rtw_read32(rtwdev, REG_EFUSE_CTRL); for (addr = 0; addr < size; addr++) { efuse_ctl &= ~(BIT_MASK_EF_DATA | BITS_EF_ADDR); efuse_ctl |= (addr & BIT_MASK_EF_ADDR) << BIT_SHIFT_EF_ADDR; rtw_write32(rtwdev, REG_EFUSE_CTRL, efuse_ctl & (~BIT_EF_FLAG)); cnt = 1000000; do { udelay(1); efuse_ctl = rtw_read32(rtwdev, REG_EFUSE_CTRL); if (--cnt == 0) return -EBUSY; } while (!(efuse_ctl & BIT_EF_FLAG)); *(map + addr) = (u8)(efuse_ctl & BIT_MASK_EF_DATA); } rtw_chip_efuse_grant_off(rtwdev); return 0; } int rtw_read8_physical_efuse(struct rtw_dev *rtwdev, u16 addr, u8 *data) { u32 efuse_ctl; int ret; rtw_write32_mask(rtwdev, REG_EFUSE_CTRL, 0x3ff00, addr); rtw_write32_clr(rtwdev, REG_EFUSE_CTRL, BIT_EF_FLAG); ret = read_poll_timeout(rtw_read32, efuse_ctl, efuse_ctl & BIT_EF_FLAG, 1000, 100000, false, rtwdev, REG_EFUSE_CTRL); if (ret) { *data = EFUSE_READ_FAIL; return ret; } *data = rtw_read8(rtwdev, REG_EFUSE_CTRL); return 0; } EXPORT_SYMBOL(rtw_read8_physical_efuse); int rtw_parse_efuse_map(struct rtw_dev *rtwdev) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_efuse *efuse = &rtwdev->efuse; u32 phy_size = efuse->physical_size; u32 log_size = efuse->logical_size; u8 *phy_map = NULL; u8 *log_map = NULL; int ret = 0; phy_map = kmalloc(phy_size, GFP_KERNEL); log_map = kmalloc(log_size, GFP_KERNEL); if (!phy_map || !log_map) { ret = -ENOMEM; goto out_free; } ret = rtw_dump_physical_efuse_map(rtwdev, phy_map); if (ret) { rtw_err(rtwdev, "failed to dump efuse physical map\n"); goto out_free; } memset(log_map, 0xff, log_size); ret = rtw_dump_logical_efuse_map(rtwdev, phy_map, log_map); if (ret) { rtw_err(rtwdev, "failed to dump efuse logical map\n"); goto out_free; } ret = chip->ops->read_efuse(rtwdev, log_map); if (ret) { rtw_err(rtwdev, "failed to read efuse map\n"); goto out_free; } out_free: kfree(log_map); kfree(phy_map); return ret; } diff --git a/sys/contrib/dev/rtw88/fw.c b/sys/contrib/dev/rtw88/fw.c index 9bc419f71ed6..f7fecc48fd39 100644 --- a/sys/contrib/dev/rtw88/fw.c +++ b/sys/contrib/dev/rtw88/fw.c @@ -1,2232 +1,2358 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* Copyright(c) 2018-2019 Realtek Corporation */ #include #include "main.h" #include "coex.h" #include "fw.h" #include "tx.h" #include "reg.h" #include "sec.h" #include "debug.h" #include "util.h" #include "wow.h" #include "ps.h" +#include "phy.h" +#include "mac.h" static void rtw_fw_c2h_cmd_handle_ext(struct rtw_dev *rtwdev, struct sk_buff *skb) { struct rtw_c2h_cmd *c2h; u8 sub_cmd_id; c2h = get_c2h_from_skb(skb); sub_cmd_id = c2h->payload[0]; switch (sub_cmd_id) { case C2H_CCX_RPT: rtw_tx_report_handle(rtwdev, skb, C2H_CCX_RPT); break; case C2H_SCAN_STATUS_RPT: rtw_hw_scan_status_report(rtwdev, skb); break; case C2H_CHAN_SWITCH: rtw_hw_scan_chan_switch(rtwdev, skb); break; default: break; } } static u16 get_max_amsdu_len(u32 bit_rate) { /* lower than ofdm, do not aggregate */ if (bit_rate < 550) return 1; /* lower than 20M 2ss mcs8, make it small */ if (bit_rate < 1800) return 1200; /* lower than 40M 2ss mcs9, make it medium */ if (bit_rate < 4000) return 2600; /* not yet 80M 2ss mcs8/9, make it twice regular packet size */ if (bit_rate < 7000) return 3500; /* unlimited */ return 0; } struct rtw_fw_iter_ra_data { struct rtw_dev *rtwdev; u8 *payload; }; static void rtw_fw_ra_report_iter(void *data, struct ieee80211_sta *sta) { struct rtw_fw_iter_ra_data *ra_data = data; struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv; u8 mac_id, rate, sgi, bw; u8 mcs, nss; u32 bit_rate; mac_id = GET_RA_REPORT_MACID(ra_data->payload); if (si->mac_id != mac_id) return; si->ra_report.txrate.flags = 0; rate = GET_RA_REPORT_RATE(ra_data->payload); sgi = GET_RA_REPORT_SGI(ra_data->payload); bw = GET_RA_REPORT_BW(ra_data->payload); if (rate < DESC_RATEMCS0) { si->ra_report.txrate.legacy = rtw_desc_to_bitrate(rate); goto legacy; } rtw_desc_to_mcsrate(rate, &mcs, &nss); if (rate >= DESC_RATEVHT1SS_MCS0) si->ra_report.txrate.flags |= RATE_INFO_FLAGS_VHT_MCS; else if (rate >= DESC_RATEMCS0) si->ra_report.txrate.flags |= RATE_INFO_FLAGS_MCS; if (rate >= DESC_RATEMCS0) { si->ra_report.txrate.mcs = mcs; si->ra_report.txrate.nss = nss; } if (sgi) si->ra_report.txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; if (bw == RTW_CHANNEL_WIDTH_80) si->ra_report.txrate.bw = RATE_INFO_BW_80; else if (bw == RTW_CHANNEL_WIDTH_40) si->ra_report.txrate.bw = RATE_INFO_BW_40; else si->ra_report.txrate.bw = RATE_INFO_BW_20; legacy: bit_rate = cfg80211_calculate_bitrate(&si->ra_report.txrate); si->ra_report.desc_rate = rate; si->ra_report.bit_rate = bit_rate; - sta->max_rc_amsdu_len = get_max_amsdu_len(bit_rate); + sta->deflink.agg.max_rc_amsdu_len = get_max_amsdu_len(bit_rate); } static void rtw_fw_ra_report_handle(struct rtw_dev *rtwdev, u8 *payload, u8 length) { struct rtw_fw_iter_ra_data ra_data; if (WARN(length < 7, "invalid ra report c2h length\n")) return; rtwdev->dm_info.tx_rate = GET_RA_REPORT_RATE(payload); ra_data.rtwdev = rtwdev; ra_data.payload = payload; rtw_iterate_stas_atomic(rtwdev, rtw_fw_ra_report_iter, &ra_data); } struct rtw_beacon_filter_iter_data { struct rtw_dev *rtwdev; u8 *payload; }; -static void rtw_fw_bcn_filter_notify_vif_iter(void *data, u8 *mac, +static void rtw_fw_bcn_filter_notify_vif_iter(void *data, struct ieee80211_vif *vif) { struct rtw_beacon_filter_iter_data *iter_data = data; struct rtw_dev *rtwdev = iter_data->rtwdev; u8 *payload = iter_data->payload; u8 type = GET_BCN_FILTER_NOTIFY_TYPE(payload); u8 event = GET_BCN_FILTER_NOTIFY_EVENT(payload); s8 sig = (s8)GET_BCN_FILTER_NOTIFY_RSSI(payload); switch (type) { case BCN_FILTER_NOTIFY_SIGNAL_CHANGE: event = event ? NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH : NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW; ieee80211_cqm_rssi_notify(vif, event, sig, GFP_KERNEL); break; case BCN_FILTER_CONNECTION_LOSS: ieee80211_connection_loss(vif); break; case BCN_FILTER_CONNECTED: rtwdev->beacon_loss = false; break; case BCN_FILTER_NOTIFY_BEACON_LOSS: rtwdev->beacon_loss = true; rtw_leave_lps(rtwdev); break; } } static void rtw_fw_bcn_filter_notify(struct rtw_dev *rtwdev, u8 *payload, u8 length) { struct rtw_beacon_filter_iter_data dev_iter_data; dev_iter_data.rtwdev = rtwdev; dev_iter_data.payload = payload; rtw_iterate_vifs(rtwdev, rtw_fw_bcn_filter_notify_vif_iter, &dev_iter_data); } static void rtw_fw_scan_result(struct rtw_dev *rtwdev, u8 *payload, u8 length) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; dm_info->scan_density = payload[0]; rtw_dbg(rtwdev, RTW_DBG_FW, "scan.density = %x\n", dm_info->scan_density); } static void rtw_fw_adaptivity_result(struct rtw_dev *rtwdev, u8 *payload, u8 length) { struct rtw_hw_reg_offset *edcca_th = rtwdev->chip->edcca_th; struct rtw_c2h_adaptivity *result = (struct rtw_c2h_adaptivity *)payload; rtw_dbg(rtwdev, RTW_DBG_ADAPTIVITY, "Adaptivity: density %x igi %x l2h_th_init %x l2h %x h2l %x option %x\n", result->density, result->igi, result->l2h_th_init, result->l2h, result->h2l, result->option); rtw_dbg(rtwdev, RTW_DBG_ADAPTIVITY, "Reg Setting: L2H %x H2L %x\n", rtw_read32_mask(rtwdev, edcca_th[EDCCA_TH_L2H_IDX].hw_reg.addr, edcca_th[EDCCA_TH_L2H_IDX].hw_reg.mask), rtw_read32_mask(rtwdev, edcca_th[EDCCA_TH_H2L_IDX].hw_reg.addr, edcca_th[EDCCA_TH_H2L_IDX].hw_reg.mask)); rtw_dbg(rtwdev, RTW_DBG_ADAPTIVITY, "EDCCA Flag %s\n", rtw_read32_mask(rtwdev, REG_EDCCA_REPORT, BIT_EDCCA_FLAG) ? "Set" : "Unset"); } void rtw_fw_c2h_cmd_handle(struct rtw_dev *rtwdev, struct sk_buff *skb) { struct rtw_c2h_cmd *c2h; u32 pkt_offset; u8 len; pkt_offset = *((u32 *)skb->cb); c2h = (struct rtw_c2h_cmd *)(skb->data + pkt_offset); len = skb->len - pkt_offset - 2; mutex_lock(&rtwdev->mutex); if (!test_bit(RTW_FLAG_RUNNING, rtwdev->flags)) goto unlock; switch (c2h->id) { case C2H_CCX_TX_RPT: rtw_tx_report_handle(rtwdev, skb, C2H_CCX_TX_RPT); break; case C2H_BT_INFO: rtw_coex_bt_info_notify(rtwdev, c2h->payload, len); break; case C2H_BT_HID_INFO: rtw_coex_bt_hid_info_notify(rtwdev, c2h->payload, len); break; case C2H_WLAN_INFO: rtw_coex_wl_fwdbginfo_notify(rtwdev, c2h->payload, len); break; case C2H_BCN_FILTER_NOTIFY: rtw_fw_bcn_filter_notify(rtwdev, c2h->payload, len); break; case C2H_HALMAC: rtw_fw_c2h_cmd_handle_ext(rtwdev, skb); break; case C2H_RA_RPT: rtw_fw_ra_report_handle(rtwdev, c2h->payload, len); break; default: rtw_dbg(rtwdev, RTW_DBG_FW, "C2H 0x%x isn't handled\n", c2h->id); break; } unlock: mutex_unlock(&rtwdev->mutex); } void rtw_fw_c2h_cmd_rx_irqsafe(struct rtw_dev *rtwdev, u32 pkt_offset, struct sk_buff *skb) { struct rtw_c2h_cmd *c2h; u8 len; c2h = (struct rtw_c2h_cmd *)(skb->data + pkt_offset); len = skb->len - pkt_offset - 2; *((u32 *)skb->cb) = pkt_offset; rtw_dbg(rtwdev, RTW_DBG_FW, "recv C2H, id=0x%02x, seq=0x%02x, len=%d\n", c2h->id, c2h->seq, len); switch (c2h->id) { case C2H_BT_MP_INFO: rtw_coex_info_response(rtwdev, skb); break; case C2H_WLAN_RFON: complete(&rtwdev->lps_leave_check); dev_kfree_skb_any(skb); break; case C2H_SCAN_RESULT: complete(&rtwdev->fw_scan_density); rtw_fw_scan_result(rtwdev, c2h->payload, len); dev_kfree_skb_any(skb); break; case C2H_ADAPTIVITY: rtw_fw_adaptivity_result(rtwdev, c2h->payload, len); dev_kfree_skb_any(skb); break; default: /* pass offset for further operation */ *((u32 *)skb->cb) = pkt_offset; skb_queue_tail(&rtwdev->c2h_queue, skb); ieee80211_queue_work(rtwdev->hw, &rtwdev->c2h_work); break; } } EXPORT_SYMBOL(rtw_fw_c2h_cmd_rx_irqsafe); void rtw_fw_c2h_cmd_isr(struct rtw_dev *rtwdev) { if (rtw_read8(rtwdev, REG_MCU_TST_CFG) == VAL_FW_TRIGGER) rtw_fw_recovery(rtwdev); else rtw_warn(rtwdev, "unhandled firmware c2h interrupt\n"); } EXPORT_SYMBOL(rtw_fw_c2h_cmd_isr); +static void rtw_fw_send_h2c_command_register(struct rtw_dev *rtwdev, + struct rtw_h2c_register *h2c) +{ + u32 box_reg, box_ex_reg; + u8 box_state, box; + int ret; + + rtw_dbg(rtwdev, RTW_DBG_FW, "send H2C content %08x %08x\n", h2c->w0, + h2c->w1); + + lockdep_assert_held(&rtwdev->mutex); + + box = rtwdev->h2c.last_box_num; + switch (box) { + case 0: + box_reg = REG_HMEBOX0; + box_ex_reg = REG_HMEBOX0_EX; + break; + case 1: + box_reg = REG_HMEBOX1; + box_ex_reg = REG_HMEBOX1_EX; + break; + case 2: + box_reg = REG_HMEBOX2; + box_ex_reg = REG_HMEBOX2_EX; + break; + case 3: + box_reg = REG_HMEBOX3; + box_ex_reg = REG_HMEBOX3_EX; + break; + default: + WARN(1, "invalid h2c mail box number\n"); + return; + } + + ret = read_poll_timeout_atomic(rtw_read8, box_state, + !((box_state >> box) & 0x1), 100, 3000, + false, rtwdev, REG_HMETFR); + + if (ret) { + rtw_err(rtwdev, "failed to send h2c command\n"); + return; + } + + rtw_write32(rtwdev, box_ex_reg, h2c->w1); + rtw_write32(rtwdev, box_reg, h2c->w0); + + if (++rtwdev->h2c.last_box_num >= 4) + rtwdev->h2c.last_box_num = 0; +} + static void rtw_fw_send_h2c_command(struct rtw_dev *rtwdev, u8 *h2c) { + struct rtw_h2c_cmd *h2c_cmd = (struct rtw_h2c_cmd *)h2c; u8 box; u8 box_state; u32 box_reg, box_ex_reg; - int idx; int ret; rtw_dbg(rtwdev, RTW_DBG_FW, "send H2C content %02x%02x%02x%02x %02x%02x%02x%02x\n", h2c[3], h2c[2], h2c[1], h2c[0], h2c[7], h2c[6], h2c[5], h2c[4]); - spin_lock(&rtwdev->h2c.lock); + lockdep_assert_held(&rtwdev->mutex); box = rtwdev->h2c.last_box_num; switch (box) { case 0: box_reg = REG_HMEBOX0; box_ex_reg = REG_HMEBOX0_EX; break; case 1: box_reg = REG_HMEBOX1; box_ex_reg = REG_HMEBOX1_EX; break; case 2: box_reg = REG_HMEBOX2; box_ex_reg = REG_HMEBOX2_EX; break; case 3: box_reg = REG_HMEBOX3; box_ex_reg = REG_HMEBOX3_EX; break; default: WARN(1, "invalid h2c mail box number\n"); - goto out; + return; } ret = read_poll_timeout_atomic(rtw_read8, box_state, !((box_state >> box) & 0x1), 100, 3000, false, rtwdev, REG_HMETFR); if (ret) { rtw_err(rtwdev, "failed to send h2c command\n"); - goto out; + return; } - for (idx = 0; idx < 4; idx++) - rtw_write8(rtwdev, box_reg + idx, h2c[idx]); - for (idx = 0; idx < 4; idx++) - rtw_write8(rtwdev, box_ex_reg + idx, h2c[idx + 4]); + rtw_write32(rtwdev, box_ex_reg, le32_to_cpu(h2c_cmd->msg_ext)); + rtw_write32(rtwdev, box_reg, le32_to_cpu(h2c_cmd->msg)); if (++rtwdev->h2c.last_box_num >= 4) rtwdev->h2c.last_box_num = 0; - -out: - spin_unlock(&rtwdev->h2c.lock); } void rtw_fw_h2c_cmd_dbg(struct rtw_dev *rtwdev, u8 *h2c) { rtw_fw_send_h2c_command(rtwdev, h2c); } static void rtw_fw_send_h2c_packet(struct rtw_dev *rtwdev, u8 *h2c_pkt) { int ret; - spin_lock(&rtwdev->h2c.lock); + lockdep_assert_held(&rtwdev->mutex); FW_OFFLOAD_H2C_SET_SEQ_NUM(h2c_pkt, rtwdev->h2c.seq); ret = rtw_hci_write_data_h2c(rtwdev, h2c_pkt, H2C_PKT_SIZE); if (ret) rtw_err(rtwdev, "failed to send h2c packet\n"); rtwdev->h2c.seq++; - - spin_unlock(&rtwdev->h2c.lock); } void rtw_fw_send_general_info(struct rtw_dev *rtwdev) { struct rtw_fifo_conf *fifo = &rtwdev->fifo; u8 h2c_pkt[H2C_PKT_SIZE] = {0}; u16 total_size = H2C_PKT_HDR_SIZE + 4; if (rtw_chip_wcpu_11n(rtwdev)) return; rtw_h2c_pkt_set_header(h2c_pkt, H2C_PKT_GENERAL_INFO); SET_PKT_H2C_TOTAL_LEN(h2c_pkt, total_size); GENERAL_INFO_SET_FW_TX_BOUNDARY(h2c_pkt, fifo->rsvd_fw_txbuf_addr - fifo->rsvd_boundary); rtw_fw_send_h2c_packet(rtwdev, h2c_pkt); } void rtw_fw_send_phydm_info(struct rtw_dev *rtwdev) { struct rtw_hal *hal = &rtwdev->hal; struct rtw_efuse *efuse = &rtwdev->efuse; u8 h2c_pkt[H2C_PKT_SIZE] = {0}; u16 total_size = H2C_PKT_HDR_SIZE + 8; u8 fw_rf_type = 0; if (rtw_chip_wcpu_11n(rtwdev)) return; if (hal->rf_type == RF_1T1R) fw_rf_type = FW_RF_1T1R; else if (hal->rf_type == RF_2T2R) fw_rf_type = FW_RF_2T2R; rtw_h2c_pkt_set_header(h2c_pkt, H2C_PKT_PHYDM_INFO); SET_PKT_H2C_TOTAL_LEN(h2c_pkt, total_size); PHYDM_INFO_SET_REF_TYPE(h2c_pkt, efuse->rfe_option); PHYDM_INFO_SET_RF_TYPE(h2c_pkt, fw_rf_type); PHYDM_INFO_SET_CUT_VER(h2c_pkt, hal->cut_version); PHYDM_INFO_SET_RX_ANT_STATUS(h2c_pkt, hal->antenna_tx); PHYDM_INFO_SET_TX_ANT_STATUS(h2c_pkt, hal->antenna_rx); rtw_fw_send_h2c_packet(rtwdev, h2c_pkt); } void rtw_fw_do_iqk(struct rtw_dev *rtwdev, struct rtw_iqk_para *para) { u8 h2c_pkt[H2C_PKT_SIZE] = {0}; u16 total_size = H2C_PKT_HDR_SIZE + 1; rtw_h2c_pkt_set_header(h2c_pkt, H2C_PKT_IQK); SET_PKT_H2C_TOTAL_LEN(h2c_pkt, total_size); IQK_SET_CLEAR(h2c_pkt, para->clear); IQK_SET_SEGMENT_IQK(h2c_pkt, para->segment_iqk); rtw_fw_send_h2c_packet(rtwdev, h2c_pkt); } EXPORT_SYMBOL(rtw_fw_do_iqk); void rtw_fw_inform_rfk_status(struct rtw_dev *rtwdev, bool start) { u8 h2c_pkt[H2C_PKT_SIZE] = {0}; SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_WIFI_CALIBRATION); RFK_SET_INFORM_START(h2c_pkt, start); rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } EXPORT_SYMBOL(rtw_fw_inform_rfk_status); void rtw_fw_query_bt_info(struct rtw_dev *rtwdev) { u8 h2c_pkt[H2C_PKT_SIZE] = {0}; SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_QUERY_BT_INFO); SET_QUERY_BT_INFO(h2c_pkt, true); rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } +void rtw_fw_default_port(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif) +{ + struct rtw_h2c_register h2c = {}; + + if (rtwvif->net_type != RTW_NET_MGD_LINKED) + return; + + /* Leave LPS before default port H2C so FW timer is correct */ + rtw_leave_lps(rtwdev); + + h2c.w0 = u32_encode_bits(H2C_CMD_DEFAULT_PORT, RTW_H2C_W0_CMDID) | + u32_encode_bits(rtwvif->port, RTW_H2C_DEFAULT_PORT_W0_PORTID) | + u32_encode_bits(rtwvif->mac_id, RTW_H2C_DEFAULT_PORT_W0_MACID); + + rtw_fw_send_h2c_command_register(rtwdev, &h2c); +} + void rtw_fw_wl_ch_info(struct rtw_dev *rtwdev, u8 link, u8 ch, u8 bw) { u8 h2c_pkt[H2C_PKT_SIZE] = {0}; SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_WL_CH_INFO); SET_WL_CH_INFO_LINK(h2c_pkt, link); SET_WL_CH_INFO_CHNL(h2c_pkt, ch); SET_WL_CH_INFO_BW(h2c_pkt, bw); rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } void rtw_fw_query_bt_mp_info(struct rtw_dev *rtwdev, struct rtw_coex_info_req *req) { u8 h2c_pkt[H2C_PKT_SIZE] = {0}; SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_QUERY_BT_MP_INFO); SET_BT_MP_INFO_SEQ(h2c_pkt, req->seq); SET_BT_MP_INFO_OP_CODE(h2c_pkt, req->op_code); SET_BT_MP_INFO_PARA1(h2c_pkt, req->para1); SET_BT_MP_INFO_PARA2(h2c_pkt, req->para2); SET_BT_MP_INFO_PARA3(h2c_pkt, req->para3); rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } void rtw_fw_force_bt_tx_power(struct rtw_dev *rtwdev, u8 bt_pwr_dec_lvl) { u8 h2c_pkt[H2C_PKT_SIZE] = {0}; u8 index = 0 - bt_pwr_dec_lvl; SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_FORCE_BT_TX_POWER); SET_BT_TX_POWER_INDEX(h2c_pkt, index); rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } void rtw_fw_bt_ignore_wlan_action(struct rtw_dev *rtwdev, bool enable) { u8 h2c_pkt[H2C_PKT_SIZE] = {0}; SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_IGNORE_WLAN_ACTION); SET_IGNORE_WLAN_ACTION_EN(h2c_pkt, enable); rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } void rtw_fw_coex_tdma_type(struct rtw_dev *rtwdev, u8 para1, u8 para2, u8 para3, u8 para4, u8 para5) { u8 h2c_pkt[H2C_PKT_SIZE] = {0}; SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_COEX_TDMA_TYPE); SET_COEX_TDMA_TYPE_PARA1(h2c_pkt, para1); SET_COEX_TDMA_TYPE_PARA2(h2c_pkt, para2); SET_COEX_TDMA_TYPE_PARA3(h2c_pkt, para3); SET_COEX_TDMA_TYPE_PARA4(h2c_pkt, para4); SET_COEX_TDMA_TYPE_PARA5(h2c_pkt, para5); rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } void rtw_fw_coex_query_hid_info(struct rtw_dev *rtwdev, u8 sub_id, u8 data) { u8 h2c_pkt[H2C_PKT_SIZE] = {0}; SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_QUERY_BT_HID_INFO); SET_COEX_QUERY_HID_INFO_SUBID(h2c_pkt, sub_id); SET_COEX_QUERY_HID_INFO_DATA1(h2c_pkt, data); rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } void rtw_fw_bt_wifi_control(struct rtw_dev *rtwdev, u8 op_code, u8 *data) { u8 h2c_pkt[H2C_PKT_SIZE] = {0}; SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_BT_WIFI_CONTROL); SET_BT_WIFI_CONTROL_OP_CODE(h2c_pkt, op_code); SET_BT_WIFI_CONTROL_DATA1(h2c_pkt, *data); SET_BT_WIFI_CONTROL_DATA2(h2c_pkt, *(data + 1)); SET_BT_WIFI_CONTROL_DATA3(h2c_pkt, *(data + 2)); SET_BT_WIFI_CONTROL_DATA4(h2c_pkt, *(data + 3)); SET_BT_WIFI_CONTROL_DATA5(h2c_pkt, *(data + 4)); rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } void rtw_fw_send_rssi_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si) { u8 h2c_pkt[H2C_PKT_SIZE] = {0}; u8 rssi = ewma_rssi_read(&si->avg_rssi); bool stbc_en = si->stbc_en ? true : false; SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_RSSI_MONITOR); SET_RSSI_INFO_MACID(h2c_pkt, si->mac_id); SET_RSSI_INFO_RSSI(h2c_pkt, rssi); SET_RSSI_INFO_STBC(h2c_pkt, stbc_en); rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } void rtw_fw_send_ra_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si, bool reset_ra_mask) { u8 h2c_pkt[H2C_PKT_SIZE] = {0}; bool disable_pt = true; SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_RA_INFO); SET_RA_INFO_MACID(h2c_pkt, si->mac_id); SET_RA_INFO_RATE_ID(h2c_pkt, si->rate_id); SET_RA_INFO_INIT_RA_LVL(h2c_pkt, si->init_ra_lv); SET_RA_INFO_SGI_EN(h2c_pkt, si->sgi_enable); SET_RA_INFO_BW_MODE(h2c_pkt, si->bw_mode); SET_RA_INFO_LDPC(h2c_pkt, !!si->ldpc_en); SET_RA_INFO_NO_UPDATE(h2c_pkt, !reset_ra_mask); SET_RA_INFO_VHT_EN(h2c_pkt, si->vht_enable); SET_RA_INFO_DIS_PT(h2c_pkt, disable_pt); SET_RA_INFO_RA_MASK0(h2c_pkt, (si->ra_mask & 0xff)); SET_RA_INFO_RA_MASK1(h2c_pkt, (si->ra_mask & 0xff00) >> 8); SET_RA_INFO_RA_MASK2(h2c_pkt, (si->ra_mask & 0xff0000) >> 16); SET_RA_INFO_RA_MASK3(h2c_pkt, (si->ra_mask & 0xff000000) >> 24); si->init_ra_lv = 0; rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } void rtw_fw_media_status_report(struct rtw_dev *rtwdev, u8 mac_id, bool connect) { u8 h2c_pkt[H2C_PKT_SIZE] = {0}; SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_MEDIA_STATUS_RPT); MEDIA_STATUS_RPT_SET_OP_MODE(h2c_pkt, connect); MEDIA_STATUS_RPT_SET_MACID(h2c_pkt, mac_id); rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } void rtw_fw_update_wl_phy_info(struct rtw_dev *rtwdev) { struct rtw_traffic_stats *stats = &rtwdev->stats; struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 h2c_pkt[H2C_PKT_SIZE] = {0}; SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_WL_PHY_INFO); SET_WL_PHY_INFO_TX_TP(h2c_pkt, stats->tx_throughput); SET_WL_PHY_INFO_RX_TP(h2c_pkt, stats->rx_throughput); SET_WL_PHY_INFO_TX_RATE_DESC(h2c_pkt, dm_info->tx_rate); SET_WL_PHY_INFO_RX_RATE_DESC(h2c_pkt, dm_info->curr_rx_rate); SET_WL_PHY_INFO_RX_EVM(h2c_pkt, dm_info->rx_evm_dbm[RF_PATH_A]); rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } void rtw_fw_beacon_filter_config(struct rtw_dev *rtwdev, bool connect, struct ieee80211_vif *vif) { struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; struct ieee80211_sta *sta = ieee80211_find_sta(vif, bss_conf->bssid); static const u8 rssi_min = 0, rssi_max = 100, rssi_offset = 100; struct rtw_sta_info *si = sta ? (struct rtw_sta_info *)sta->drv_priv : NULL; s32 threshold = bss_conf->cqm_rssi_thold + rssi_offset; u8 h2c_pkt[H2C_PKT_SIZE] = {0}; if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_BCN_FILTER)) return; if (!connect) { SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_BCN_FILTER_OFFLOAD_P1); SET_BCN_FILTER_OFFLOAD_P1_ENABLE(h2c_pkt, connect); rtw_fw_send_h2c_command(rtwdev, h2c_pkt); return; } if (!si) return; SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_BCN_FILTER_OFFLOAD_P0); ether_addr_copy(&h2c_pkt[1], bss_conf->bssid); rtw_fw_send_h2c_command(rtwdev, h2c_pkt); memset(h2c_pkt, 0, sizeof(h2c_pkt)); threshold = clamp_t(s32, threshold, rssi_min, rssi_max); SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_BCN_FILTER_OFFLOAD_P1); SET_BCN_FILTER_OFFLOAD_P1_ENABLE(h2c_pkt, connect); SET_BCN_FILTER_OFFLOAD_P1_OFFLOAD_MODE(h2c_pkt, BCN_FILTER_OFFLOAD_MODE_DEFAULT); SET_BCN_FILTER_OFFLOAD_P1_THRESHOLD(h2c_pkt, (u8)threshold); SET_BCN_FILTER_OFFLOAD_P1_BCN_LOSS_CNT(h2c_pkt, BCN_LOSS_CNT); SET_BCN_FILTER_OFFLOAD_P1_MACID(h2c_pkt, si->mac_id); SET_BCN_FILTER_OFFLOAD_P1_HYST(h2c_pkt, bss_conf->cqm_rssi_hyst); SET_BCN_FILTER_OFFLOAD_P1_BCN_INTERVAL(h2c_pkt, bss_conf->beacon_int); rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } void rtw_fw_set_pwr_mode(struct rtw_dev *rtwdev) { struct rtw_lps_conf *conf = &rtwdev->lps_conf; u8 h2c_pkt[H2C_PKT_SIZE] = {0}; SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_SET_PWR_MODE); SET_PWR_MODE_SET_MODE(h2c_pkt, conf->mode); SET_PWR_MODE_SET_RLBM(h2c_pkt, conf->rlbm); SET_PWR_MODE_SET_SMART_PS(h2c_pkt, conf->smart_ps); SET_PWR_MODE_SET_AWAKE_INTERVAL(h2c_pkt, conf->awake_interval); SET_PWR_MODE_SET_PORT_ID(h2c_pkt, conf->port_id); SET_PWR_MODE_SET_PWR_STATE(h2c_pkt, conf->state); rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } void rtw_fw_set_keep_alive_cmd(struct rtw_dev *rtwdev, bool enable) { u8 h2c_pkt[H2C_PKT_SIZE] = {0}; struct rtw_fw_wow_keep_alive_para mode = { .adopt = true, .pkt_type = KEEP_ALIVE_NULL_PKT, .period = 5, }; SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_KEEP_ALIVE); SET_KEEP_ALIVE_ENABLE(h2c_pkt, enable); SET_KEEP_ALIVE_ADOPT(h2c_pkt, mode.adopt); SET_KEEP_ALIVE_PKT_TYPE(h2c_pkt, mode.pkt_type); SET_KEEP_ALIVE_CHECK_PERIOD(h2c_pkt, mode.period); rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } void rtw_fw_set_disconnect_decision_cmd(struct rtw_dev *rtwdev, bool enable) { struct rtw_wow_param *rtw_wow = &rtwdev->wow; u8 h2c_pkt[H2C_PKT_SIZE] = {0}; struct rtw_fw_wow_disconnect_para mode = { .adopt = true, .period = 30, .retry_count = 5, }; SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_DISCONNECT_DECISION); if (test_bit(RTW_WOW_FLAG_EN_DISCONNECT, rtw_wow->flags)) { SET_DISCONNECT_DECISION_ENABLE(h2c_pkt, enable); SET_DISCONNECT_DECISION_ADOPT(h2c_pkt, mode.adopt); SET_DISCONNECT_DECISION_CHECK_PERIOD(h2c_pkt, mode.period); SET_DISCONNECT_DECISION_TRY_PKT_NUM(h2c_pkt, mode.retry_count); } rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } void rtw_fw_set_wowlan_ctrl_cmd(struct rtw_dev *rtwdev, bool enable) { struct rtw_wow_param *rtw_wow = &rtwdev->wow; u8 h2c_pkt[H2C_PKT_SIZE] = {0}; SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_WOWLAN); SET_WOWLAN_FUNC_ENABLE(h2c_pkt, enable); if (rtw_wow_mgd_linked(rtwdev)) { if (test_bit(RTW_WOW_FLAG_EN_MAGIC_PKT, rtw_wow->flags)) SET_WOWLAN_MAGIC_PKT_ENABLE(h2c_pkt, enable); if (test_bit(RTW_WOW_FLAG_EN_DISCONNECT, rtw_wow->flags)) SET_WOWLAN_DEAUTH_WAKEUP_ENABLE(h2c_pkt, enable); if (test_bit(RTW_WOW_FLAG_EN_REKEY_PKT, rtw_wow->flags)) SET_WOWLAN_REKEY_WAKEUP_ENABLE(h2c_pkt, enable); if (rtw_wow->pattern_cnt) SET_WOWLAN_PATTERN_MATCH_ENABLE(h2c_pkt, enable); } rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } void rtw_fw_set_aoac_global_info_cmd(struct rtw_dev *rtwdev, u8 pairwise_key_enc, u8 group_key_enc) { u8 h2c_pkt[H2C_PKT_SIZE] = {0}; SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_AOAC_GLOBAL_INFO); SET_AOAC_GLOBAL_INFO_PAIRWISE_ENC_ALG(h2c_pkt, pairwise_key_enc); SET_AOAC_GLOBAL_INFO_GROUP_ENC_ALG(h2c_pkt, group_key_enc); rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } void rtw_fw_set_remote_wake_ctrl_cmd(struct rtw_dev *rtwdev, bool enable) { u8 h2c_pkt[H2C_PKT_SIZE] = {0}; SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_REMOTE_WAKE_CTRL); SET_REMOTE_WAKECTRL_ENABLE(h2c_pkt, enable); if (rtw_wow_no_link(rtwdev)) SET_REMOTE_WAKE_CTRL_NLO_OFFLOAD_EN(h2c_pkt, enable); rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } static u8 rtw_get_rsvd_page_location(struct rtw_dev *rtwdev, enum rtw_rsvd_packet_type type) { struct rtw_rsvd_page *rsvd_pkt; u8 location = 0; list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, build_list) { if (type == rsvd_pkt->type) location = rsvd_pkt->page; } return location; } void rtw_fw_set_nlo_info(struct rtw_dev *rtwdev, bool enable) { u8 h2c_pkt[H2C_PKT_SIZE] = {0}; u8 loc_nlo; loc_nlo = rtw_get_rsvd_page_location(rtwdev, RSVD_NLO_INFO); SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_NLO_INFO); SET_NLO_FUN_EN(h2c_pkt, enable); if (enable) { if (rtw_get_lps_deep_mode(rtwdev) != LPS_DEEP_MODE_NONE) SET_NLO_PS_32K(h2c_pkt, enable); SET_NLO_IGNORE_SECURITY(h2c_pkt, enable); SET_NLO_LOC_NLO_INFO(h2c_pkt, loc_nlo); } rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } +void rtw_fw_set_recover_bt_device(struct rtw_dev *rtwdev) +{ + u8 h2c_pkt[H2C_PKT_SIZE] = {0}; + + SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_RECOVER_BT_DEV); + SET_RECOVER_BT_DEV_EN(h2c_pkt, 1); + + rtw_fw_send_h2c_command(rtwdev, h2c_pkt); +} + void rtw_fw_set_pg_info(struct rtw_dev *rtwdev) { struct rtw_lps_conf *conf = &rtwdev->lps_conf; u8 h2c_pkt[H2C_PKT_SIZE] = {0}; u8 loc_pg, loc_dpk; loc_pg = rtw_get_rsvd_page_location(rtwdev, RSVD_LPS_PG_INFO); loc_dpk = rtw_get_rsvd_page_location(rtwdev, RSVD_LPS_PG_DPK); SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_LPS_PG_INFO); LPS_PG_INFO_LOC(h2c_pkt, loc_pg); LPS_PG_DPK_LOC(h2c_pkt, loc_dpk); LPS_PG_SEC_CAM_EN(h2c_pkt, conf->sec_cam_backup); LPS_PG_PATTERN_CAM_EN(h2c_pkt, conf->pattern_cam_backup); rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } static u8 rtw_get_rsvd_page_probe_req_location(struct rtw_dev *rtwdev, struct cfg80211_ssid *ssid) { struct rtw_rsvd_page *rsvd_pkt; u8 location = 0; list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, build_list) { if (rsvd_pkt->type != RSVD_PROBE_REQ) continue; if ((!ssid && !rsvd_pkt->ssid) || rtw_ssid_equal(rsvd_pkt->ssid, ssid)) location = rsvd_pkt->page; } return location; } static u16 rtw_get_rsvd_page_probe_req_size(struct rtw_dev *rtwdev, struct cfg80211_ssid *ssid) { struct rtw_rsvd_page *rsvd_pkt; u16 size = 0; list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, build_list) { if (rsvd_pkt->type != RSVD_PROBE_REQ) continue; if ((!ssid && !rsvd_pkt->ssid) || rtw_ssid_equal(rsvd_pkt->ssid, ssid)) size = rsvd_pkt->probe_req_size; } return size; } void rtw_send_rsvd_page_h2c(struct rtw_dev *rtwdev) { u8 h2c_pkt[H2C_PKT_SIZE] = {0}; u8 location = 0; SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_RSVD_PAGE); location = rtw_get_rsvd_page_location(rtwdev, RSVD_PROBE_RESP); *(h2c_pkt + 1) = location; rtw_dbg(rtwdev, RTW_DBG_FW, "RSVD_PROBE_RESP loc: %d\n", location); location = rtw_get_rsvd_page_location(rtwdev, RSVD_PS_POLL); *(h2c_pkt + 2) = location; rtw_dbg(rtwdev, RTW_DBG_FW, "RSVD_PS_POLL loc: %d\n", location); location = rtw_get_rsvd_page_location(rtwdev, RSVD_NULL); *(h2c_pkt + 3) = location; rtw_dbg(rtwdev, RTW_DBG_FW, "RSVD_NULL loc: %d\n", location); location = rtw_get_rsvd_page_location(rtwdev, RSVD_QOS_NULL); *(h2c_pkt + 4) = location; rtw_dbg(rtwdev, RTW_DBG_FW, "RSVD_QOS_NULL loc: %d\n", location); rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } static struct sk_buff *rtw_nlo_info_get(struct ieee80211_hw *hw) { struct rtw_dev *rtwdev = hw->priv; - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_pno_request *pno_req = &rtwdev->wow.pno_req; struct rtw_nlo_info_hdr *nlo_hdr; struct cfg80211_ssid *ssid; struct sk_buff *skb; u8 *pos, loc; u32 size; int i; if (!pno_req->inited || !pno_req->match_set_cnt) return NULL; size = sizeof(struct rtw_nlo_info_hdr) + pno_req->match_set_cnt * IEEE80211_MAX_SSID_LEN + chip->tx_pkt_desc_sz; skb = alloc_skb(size, GFP_KERNEL); if (!skb) return NULL; skb_reserve(skb, chip->tx_pkt_desc_sz); nlo_hdr = skb_put_zero(skb, sizeof(struct rtw_nlo_info_hdr)); nlo_hdr->nlo_count = pno_req->match_set_cnt; nlo_hdr->hidden_ap_count = pno_req->match_set_cnt; /* pattern check for firmware */ memset(nlo_hdr->pattern_check, 0xA5, FW_NLO_INFO_CHECK_SIZE); for (i = 0; i < pno_req->match_set_cnt; i++) nlo_hdr->ssid_len[i] = pno_req->match_sets[i].ssid.ssid_len; for (i = 0; i < pno_req->match_set_cnt; i++) { ssid = &pno_req->match_sets[i].ssid; loc = rtw_get_rsvd_page_probe_req_location(rtwdev, ssid); if (!loc) { rtw_err(rtwdev, "failed to get probe req rsvd loc\n"); kfree_skb(skb); return NULL; } nlo_hdr->location[i] = loc; } for (i = 0; i < pno_req->match_set_cnt; i++) { pos = skb_put_zero(skb, IEEE80211_MAX_SSID_LEN); memcpy(pos, pno_req->match_sets[i].ssid.ssid, pno_req->match_sets[i].ssid.ssid_len); } return skb; } static struct sk_buff *rtw_cs_channel_info_get(struct ieee80211_hw *hw) { struct rtw_dev *rtwdev = hw->priv; - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_pno_request *pno_req = &rtwdev->wow.pno_req; struct ieee80211_channel *channels = pno_req->channels; struct sk_buff *skb; int count = pno_req->channel_cnt; u8 *pos; int i = 0; skb = alloc_skb(4 * count + chip->tx_pkt_desc_sz, GFP_KERNEL); if (!skb) return NULL; skb_reserve(skb, chip->tx_pkt_desc_sz); for (i = 0; i < count; i++) { pos = skb_put_zero(skb, 4); CHSW_INFO_SET_CH(pos, channels[i].hw_value); if (channels[i].flags & IEEE80211_CHAN_RADAR) CHSW_INFO_SET_ACTION_ID(pos, 0); else CHSW_INFO_SET_ACTION_ID(pos, 1); CHSW_INFO_SET_TIMEOUT(pos, 1); CHSW_INFO_SET_PRI_CH_IDX(pos, 1); CHSW_INFO_SET_BW(pos, 0); } return skb; } static struct sk_buff *rtw_lps_pg_dpk_get(struct ieee80211_hw *hw) { struct rtw_dev *rtwdev = hw->priv; - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_dpk_info *dpk_info = &rtwdev->dm_info.dpk_info; struct rtw_lps_pg_dpk_hdr *dpk_hdr; struct sk_buff *skb; u32 size; size = chip->tx_pkt_desc_sz + sizeof(*dpk_hdr); skb = alloc_skb(size, GFP_KERNEL); if (!skb) return NULL; skb_reserve(skb, chip->tx_pkt_desc_sz); dpk_hdr = skb_put_zero(skb, sizeof(*dpk_hdr)); dpk_hdr->dpk_ch = dpk_info->dpk_ch; dpk_hdr->dpk_path_ok = dpk_info->dpk_path_ok[0]; memcpy(dpk_hdr->dpk_txagc, dpk_info->dpk_txagc, 2); memcpy(dpk_hdr->dpk_gs, dpk_info->dpk_gs, 4); memcpy(dpk_hdr->coef, dpk_info->coef, 160); return skb; } static struct sk_buff *rtw_lps_pg_info_get(struct ieee80211_hw *hw) { struct rtw_dev *rtwdev = hw->priv; - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_lps_conf *conf = &rtwdev->lps_conf; struct rtw_lps_pg_info_hdr *pg_info_hdr; struct rtw_wow_param *rtw_wow = &rtwdev->wow; struct sk_buff *skb; u32 size; size = chip->tx_pkt_desc_sz + sizeof(*pg_info_hdr); skb = alloc_skb(size, GFP_KERNEL); if (!skb) return NULL; skb_reserve(skb, chip->tx_pkt_desc_sz); pg_info_hdr = skb_put_zero(skb, sizeof(*pg_info_hdr)); pg_info_hdr->tx_bu_page_count = rtwdev->fifo.rsvd_drv_pg_num; pg_info_hdr->macid = find_first_bit(rtwdev->mac_id_map, RTW_MAX_MAC_ID_NUM); pg_info_hdr->sec_cam_count = rtw_sec_cam_pg_backup(rtwdev, pg_info_hdr->sec_cam); pg_info_hdr->pattern_count = rtw_wow->pattern_cnt; conf->sec_cam_backup = pg_info_hdr->sec_cam_count != 0; conf->pattern_cam_backup = rtw_wow->pattern_cnt != 0; return skb; } static struct sk_buff *rtw_get_rsvd_page_skb(struct ieee80211_hw *hw, struct rtw_rsvd_page *rsvd_pkt) { struct ieee80211_vif *vif; struct rtw_vif *rtwvif; struct sk_buff *skb_new; struct cfg80211_ssid *ssid; u16 tim_offset = 0; if (rsvd_pkt->type == RSVD_DUMMY) { skb_new = alloc_skb(1, GFP_KERNEL); if (!skb_new) return NULL; skb_put(skb_new, 1); return skb_new; } rtwvif = rsvd_pkt->rtwvif; if (!rtwvif) return NULL; vif = rtwvif_to_vif(rtwvif); switch (rsvd_pkt->type) { case RSVD_BEACON: skb_new = ieee80211_beacon_get_tim(hw, vif, &tim_offset, NULL, 0); rsvd_pkt->tim_offset = tim_offset; break; case RSVD_PS_POLL: skb_new = ieee80211_pspoll_get(hw, vif); break; case RSVD_PROBE_RESP: skb_new = ieee80211_proberesp_get(hw, vif); break; case RSVD_NULL: - skb_new = ieee80211_nullfunc_get(hw, vif, false); + skb_new = ieee80211_nullfunc_get(hw, vif, -1, false); break; case RSVD_QOS_NULL: - skb_new = ieee80211_nullfunc_get(hw, vif, true); + skb_new = ieee80211_nullfunc_get(hw, vif, -1, true); break; case RSVD_LPS_PG_DPK: skb_new = rtw_lps_pg_dpk_get(hw); break; case RSVD_LPS_PG_INFO: skb_new = rtw_lps_pg_info_get(hw); break; case RSVD_PROBE_REQ: ssid = (struct cfg80211_ssid *)rsvd_pkt->ssid; if (ssid) skb_new = ieee80211_probereq_get(hw, vif->addr, ssid->ssid, ssid->ssid_len, 0); else skb_new = ieee80211_probereq_get(hw, vif->addr, NULL, 0, 0); if (skb_new) rsvd_pkt->probe_req_size = (u16)skb_new->len; break; case RSVD_NLO_INFO: skb_new = rtw_nlo_info_get(hw); break; case RSVD_CH_INFO: skb_new = rtw_cs_channel_info_get(hw); break; default: return NULL; } if (!skb_new) return NULL; return skb_new; } static void rtw_fill_rsvd_page_desc(struct rtw_dev *rtwdev, struct sk_buff *skb, enum rtw_rsvd_packet_type type) { struct rtw_tx_pkt_info pkt_info = {0}; - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; u8 *pkt_desc; rtw_tx_rsvd_page_pkt_info_update(rtwdev, &pkt_info, skb, type); pkt_desc = skb_push(skb, chip->tx_pkt_desc_sz); memset(pkt_desc, 0, chip->tx_pkt_desc_sz); rtw_tx_fill_tx_desc(&pkt_info, skb); } static inline u8 rtw_len_to_page(unsigned int len, u8 page_size) { return DIV_ROUND_UP(len, page_size); } static void rtw_rsvd_page_list_to_buf(struct rtw_dev *rtwdev, u8 page_size, u8 page_margin, u32 page, u8 *buf, struct rtw_rsvd_page *rsvd_pkt) { struct sk_buff *skb = rsvd_pkt->skb; if (page >= 1) memcpy(buf + page_margin + page_size * (page - 1), skb->data, skb->len); else memcpy(buf, skb->data, skb->len); } static struct rtw_rsvd_page *rtw_alloc_rsvd_page(struct rtw_dev *rtwdev, enum rtw_rsvd_packet_type type, bool txdesc) { struct rtw_rsvd_page *rsvd_pkt = NULL; rsvd_pkt = kzalloc(sizeof(*rsvd_pkt), GFP_KERNEL); if (!rsvd_pkt) return NULL; INIT_LIST_HEAD(&rsvd_pkt->vif_list); INIT_LIST_HEAD(&rsvd_pkt->build_list); rsvd_pkt->type = type; rsvd_pkt->add_txdesc = txdesc; return rsvd_pkt; } static void rtw_insert_rsvd_page(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif, struct rtw_rsvd_page *rsvd_pkt) { lockdep_assert_held(&rtwdev->mutex); list_add_tail(&rsvd_pkt->vif_list, &rtwvif->rsvd_page_list); } static void rtw_add_rsvd_page(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif, enum rtw_rsvd_packet_type type, bool txdesc) { struct rtw_rsvd_page *rsvd_pkt; rsvd_pkt = rtw_alloc_rsvd_page(rtwdev, type, txdesc); if (!rsvd_pkt) { rtw_err(rtwdev, "failed to alloc rsvd page %d\n", type); return; } rsvd_pkt->rtwvif = rtwvif; rtw_insert_rsvd_page(rtwdev, rtwvif, rsvd_pkt); } static void rtw_add_rsvd_page_probe_req(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif, struct cfg80211_ssid *ssid) { struct rtw_rsvd_page *rsvd_pkt; rsvd_pkt = rtw_alloc_rsvd_page(rtwdev, RSVD_PROBE_REQ, true); if (!rsvd_pkt) { rtw_err(rtwdev, "failed to alloc probe req rsvd page\n"); return; } rsvd_pkt->rtwvif = rtwvif; rsvd_pkt->ssid = ssid; rtw_insert_rsvd_page(rtwdev, rtwvif, rsvd_pkt); } void rtw_remove_rsvd_page(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif) { struct rtw_rsvd_page *rsvd_pkt, *tmp; lockdep_assert_held(&rtwdev->mutex); /* remove all of the rsvd pages for vif */ list_for_each_entry_safe(rsvd_pkt, tmp, &rtwvif->rsvd_page_list, vif_list) { list_del(&rsvd_pkt->vif_list); if (!list_empty(&rsvd_pkt->build_list)) list_del(&rsvd_pkt->build_list); kfree(rsvd_pkt); } } void rtw_add_rsvd_page_bcn(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif) { struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif); if (vif->type != NL80211_IFTYPE_AP && vif->type != NL80211_IFTYPE_ADHOC && vif->type != NL80211_IFTYPE_MESH_POINT) { rtw_warn(rtwdev, "Cannot add beacon rsvd page for %d\n", vif->type); return; } rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_BEACON, false); } void rtw_add_rsvd_page_pno(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif) { struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif); struct rtw_wow_param *rtw_wow = &rtwdev->wow; struct rtw_pno_request *rtw_pno_req = &rtw_wow->pno_req; struct cfg80211_ssid *ssid; int i; if (vif->type != NL80211_IFTYPE_STATION) { rtw_warn(rtwdev, "Cannot add PNO rsvd page for %d\n", vif->type); return; } for (i = 0 ; i < rtw_pno_req->match_set_cnt; i++) { ssid = &rtw_pno_req->match_sets[i].ssid; rtw_add_rsvd_page_probe_req(rtwdev, rtwvif, ssid); } rtw_add_rsvd_page_probe_req(rtwdev, rtwvif, NULL); rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_NLO_INFO, false); rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_CH_INFO, true); } void rtw_add_rsvd_page_sta(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif) { struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif); if (vif->type != NL80211_IFTYPE_STATION) { rtw_warn(rtwdev, "Cannot add sta rsvd page for %d\n", vif->type); return; } rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_PS_POLL, true); rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_QOS_NULL, true); rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_NULL, true); rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_LPS_PG_DPK, true); rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_LPS_PG_INFO, true); } int rtw_fw_write_data_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr, u8 *buf, u32 size) { u8 bckp[2]; u8 val; u16 rsvd_pg_head; u32 bcn_valid_addr; u32 bcn_valid_mask; int ret; lockdep_assert_held(&rtwdev->mutex); if (!size) return -EINVAL; if (rtw_chip_wcpu_11n(rtwdev)) { rtw_write32_set(rtwdev, REG_DWBCN0_CTRL, BIT_BCN_VALID); } else { pg_addr &= BIT_MASK_BCN_HEAD_1_V1; pg_addr |= BIT_BCN_VALID_V1; rtw_write16(rtwdev, REG_FIFOPAGE_CTRL_2, pg_addr); } val = rtw_read8(rtwdev, REG_CR + 1); bckp[0] = val; val |= BIT_ENSWBCN >> 8; rtw_write8(rtwdev, REG_CR + 1, val); val = rtw_read8(rtwdev, REG_FWHW_TXQ_CTRL + 2); bckp[1] = val; val &= ~(BIT_EN_BCNQ_DL >> 16); rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL + 2, val); ret = rtw_hci_write_data_rsvd_page(rtwdev, buf, size); if (ret) { rtw_err(rtwdev, "failed to write data to rsvd page\n"); goto restore; } if (rtw_chip_wcpu_11n(rtwdev)) { bcn_valid_addr = REG_DWBCN0_CTRL; bcn_valid_mask = BIT_BCN_VALID; } else { bcn_valid_addr = REG_FIFOPAGE_CTRL_2; bcn_valid_mask = BIT_BCN_VALID_V1; } if (!check_hw_ready(rtwdev, bcn_valid_addr, bcn_valid_mask, 1)) { rtw_err(rtwdev, "error beacon valid\n"); ret = -EBUSY; } restore: rsvd_pg_head = rtwdev->fifo.rsvd_boundary; rtw_write16(rtwdev, REG_FIFOPAGE_CTRL_2, rsvd_pg_head | BIT_BCN_VALID_V1); rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL + 2, bckp[1]); rtw_write8(rtwdev, REG_CR + 1, bckp[0]); return ret; } static int rtw_download_drv_rsvd_page(struct rtw_dev *rtwdev, u8 *buf, u32 size) { u32 pg_size; u32 pg_num = 0; u16 pg_addr = 0; pg_size = rtwdev->chip->page_size; pg_num = size / pg_size + ((size & (pg_size - 1)) ? 1 : 0); if (pg_num > rtwdev->fifo.rsvd_drv_pg_num) return -ENOMEM; pg_addr = rtwdev->fifo.rsvd_drv_addr; return rtw_fw_write_data_rsvd_page(rtwdev, pg_addr, buf, size); } static void __rtw_build_rsvd_page_reset(struct rtw_dev *rtwdev) { struct rtw_rsvd_page *rsvd_pkt, *tmp; list_for_each_entry_safe(rsvd_pkt, tmp, &rtwdev->rsvd_page_list, build_list) { list_del_init(&rsvd_pkt->build_list); /* Don't free except for the dummy rsvd page, * others will be freed when removing vif */ if (rsvd_pkt->type == RSVD_DUMMY) kfree(rsvd_pkt); } } static void rtw_build_rsvd_page_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { struct rtw_dev *rtwdev = data; struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; struct rtw_rsvd_page *rsvd_pkt; + /* AP not yet started, don't gather its rsvd pages */ + if (vif->type == NL80211_IFTYPE_AP && !rtwdev->ap_active) + return; + list_for_each_entry(rsvd_pkt, &rtwvif->rsvd_page_list, vif_list) { if (rsvd_pkt->type == RSVD_BEACON) list_add(&rsvd_pkt->build_list, &rtwdev->rsvd_page_list); else list_add_tail(&rsvd_pkt->build_list, &rtwdev->rsvd_page_list); } } static int __rtw_build_rsvd_page_from_vifs(struct rtw_dev *rtwdev) { struct rtw_rsvd_page *rsvd_pkt; __rtw_build_rsvd_page_reset(rtwdev); /* gather rsvd page from vifs */ rtw_iterate_vifs_atomic(rtwdev, rtw_build_rsvd_page_iter, rtwdev); rsvd_pkt = list_first_entry_or_null(&rtwdev->rsvd_page_list, struct rtw_rsvd_page, build_list); if (!rsvd_pkt) { WARN(1, "Should not have an empty reserved page\n"); return -EINVAL; } /* the first rsvd should be beacon, otherwise add a dummy one */ if (rsvd_pkt->type != RSVD_BEACON) { struct rtw_rsvd_page *dummy_pkt; dummy_pkt = rtw_alloc_rsvd_page(rtwdev, RSVD_DUMMY, false); if (!dummy_pkt) { rtw_err(rtwdev, "failed to alloc dummy rsvd page\n"); return -ENOMEM; } list_add(&dummy_pkt->build_list, &rtwdev->rsvd_page_list); } return 0; } static u8 *rtw_build_rsvd_page(struct rtw_dev *rtwdev, u32 *size) { struct ieee80211_hw *hw = rtwdev->hw; - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct sk_buff *iter; struct rtw_rsvd_page *rsvd_pkt; u32 page = 0; u8 total_page = 0; u8 page_size, page_margin, tx_desc_sz; u8 *buf; int ret; page_size = chip->page_size; tx_desc_sz = chip->tx_pkt_desc_sz; page_margin = page_size - tx_desc_sz; ret = __rtw_build_rsvd_page_from_vifs(rtwdev); if (ret) { rtw_err(rtwdev, "failed to build rsvd page from vifs, ret %d\n", ret); return NULL; } list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, build_list) { iter = rtw_get_rsvd_page_skb(hw, rsvd_pkt); if (!iter) { rtw_err(rtwdev, "failed to build rsvd packet\n"); goto release_skb; } /* Fill the tx_desc for the rsvd pkt that requires one. * And iter->len will be added with size of tx_desc_sz. */ if (rsvd_pkt->add_txdesc) rtw_fill_rsvd_page_desc(rtwdev, iter, rsvd_pkt->type); rsvd_pkt->skb = iter; rsvd_pkt->page = total_page; /* Reserved page is downloaded via TX path, and TX path will * generate a tx_desc at the header to describe length of * the buffer. If we are not counting page numbers with the * size of tx_desc added at the first rsvd_pkt (usually a * beacon, firmware default refer to the first page as the * content of beacon), we could generate a buffer which size * is smaller than the actual size of the whole rsvd_page */ if (total_page == 0) { if (rsvd_pkt->type != RSVD_BEACON && rsvd_pkt->type != RSVD_DUMMY) { rtw_err(rtwdev, "first page should be a beacon\n"); goto release_skb; } total_page += rtw_len_to_page(iter->len + tx_desc_sz, page_size); } else { total_page += rtw_len_to_page(iter->len, page_size); } } if (total_page > rtwdev->fifo.rsvd_drv_pg_num) { rtw_err(rtwdev, "rsvd page over size: %d\n", total_page); goto release_skb; } *size = (total_page - 1) * page_size + page_margin; buf = kzalloc(*size, GFP_KERNEL); if (!buf) goto release_skb; /* Copy the content of each rsvd_pkt to the buf, and they should * be aligned to the pages. * * Note that the first rsvd_pkt is a beacon no matter what vif->type. * And that rsvd_pkt does not require tx_desc because when it goes * through TX path, the TX path will generate one for it. */ list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, build_list) { rtw_rsvd_page_list_to_buf(rtwdev, page_size, page_margin, page, buf, rsvd_pkt); if (page == 0) page += rtw_len_to_page(rsvd_pkt->skb->len + tx_desc_sz, page_size); else page += rtw_len_to_page(rsvd_pkt->skb->len, page_size); kfree_skb(rsvd_pkt->skb); rsvd_pkt->skb = NULL; } return buf; release_skb: list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, build_list) { kfree_skb(rsvd_pkt->skb); rsvd_pkt->skb = NULL; } return NULL; } static int rtw_download_beacon(struct rtw_dev *rtwdev) { struct ieee80211_hw *hw = rtwdev->hw; struct rtw_rsvd_page *rsvd_pkt; struct sk_buff *skb; int ret = 0; rsvd_pkt = list_first_entry_or_null(&rtwdev->rsvd_page_list, struct rtw_rsvd_page, build_list); if (!rsvd_pkt) { rtw_err(rtwdev, "failed to get rsvd page from build list\n"); return -ENOENT; } if (rsvd_pkt->type != RSVD_BEACON && rsvd_pkt->type != RSVD_DUMMY) { rtw_err(rtwdev, "invalid rsvd page type %d, should be beacon or dummy\n", rsvd_pkt->type); return -EINVAL; } skb = rtw_get_rsvd_page_skb(hw, rsvd_pkt); if (!skb) { rtw_err(rtwdev, "failed to get beacon skb\n"); return -ENOMEM; } ret = rtw_download_drv_rsvd_page(rtwdev, skb->data, skb->len); if (ret) rtw_err(rtwdev, "failed to download drv rsvd page\n"); dev_kfree_skb(skb); return ret; } int rtw_fw_download_rsvd_page(struct rtw_dev *rtwdev) { u8 *buf; u32 size; int ret; buf = rtw_build_rsvd_page(rtwdev, &size); if (!buf) { rtw_err(rtwdev, "failed to build rsvd page pkt\n"); return -ENOMEM; } ret = rtw_download_drv_rsvd_page(rtwdev, buf, size); if (ret) { rtw_err(rtwdev, "failed to download drv rsvd page\n"); goto free; } /* The last thing is to download the *ONLY* beacon again, because * the previous tx_desc is to describe the total rsvd page. Download * the beacon again to replace the TX desc header, and we will get * a correct tx_desc for the beacon in the rsvd page. */ ret = rtw_download_beacon(rtwdev); if (ret) { rtw_err(rtwdev, "failed to download beacon\n"); goto free; } free: kfree(buf); return ret; } void rtw_fw_update_beacon_work(struct work_struct *work) { struct rtw_dev *rtwdev = container_of(work, struct rtw_dev, update_beacon_work); mutex_lock(&rtwdev->mutex); rtw_fw_download_rsvd_page(rtwdev); + rtw_send_rsvd_page_h2c(rtwdev); mutex_unlock(&rtwdev->mutex); } static void rtw_fw_read_fifo_page(struct rtw_dev *rtwdev, u32 offset, u32 size, u32 *buf, u32 residue, u16 start_pg) { u32 i; u16 idx = 0; u16 ctl; ctl = rtw_read16(rtwdev, REG_PKTBUF_DBG_CTRL) & 0xf000; /* disable rx clock gate */ rtw_write32_set(rtwdev, REG_RCR, BIT_DISGCLK); do { rtw_write16(rtwdev, REG_PKTBUF_DBG_CTRL, start_pg | ctl); for (i = FIFO_DUMP_ADDR + residue; i < FIFO_DUMP_ADDR + FIFO_PAGE_SIZE; i += 4) { buf[idx++] = rtw_read32(rtwdev, i); size -= 4; if (size == 0) goto out; } residue = 0; start_pg++; } while (size); out: rtw_write16(rtwdev, REG_PKTBUF_DBG_CTRL, ctl); /* restore rx clock gate */ rtw_write32_clr(rtwdev, REG_RCR, BIT_DISGCLK); } static void rtw_fw_read_fifo(struct rtw_dev *rtwdev, enum rtw_fw_fifo_sel sel, u32 offset, u32 size, u32 *buf) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; u32 start_pg, residue; if (sel >= RTW_FW_FIFO_MAX) { rtw_dbg(rtwdev, RTW_DBG_FW, "wrong fw fifo sel\n"); return; } if (sel == RTW_FW_FIFO_SEL_RSVD_PAGE) offset += rtwdev->fifo.rsvd_boundary << TX_PAGE_SIZE_SHIFT; residue = offset & (FIFO_PAGE_SIZE - 1); start_pg = (offset >> FIFO_PAGE_SIZE_SHIFT) + chip->fw_fifo_addr[sel]; rtw_fw_read_fifo_page(rtwdev, offset, size, buf, residue, start_pg); } static bool rtw_fw_dump_check_size(struct rtw_dev *rtwdev, enum rtw_fw_fifo_sel sel, u32 start_addr, u32 size) { switch (sel) { case RTW_FW_FIFO_SEL_TX: case RTW_FW_FIFO_SEL_RX: if ((start_addr + size) > rtwdev->chip->fw_fifo_addr[sel]) return false; fallthrough; default: return true; } } int rtw_fw_dump_fifo(struct rtw_dev *rtwdev, u8 fifo_sel, u32 addr, u32 size, u32 *buffer) { if (!rtwdev->chip->fw_fifo_addr[0]) { rtw_dbg(rtwdev, RTW_DBG_FW, "chip not support dump fw fifo\n"); return -ENOTSUPP; } if (size == 0 || !buffer) return -EINVAL; if (size & 0x3) { rtw_dbg(rtwdev, RTW_DBG_FW, "not 4byte alignment\n"); return -EINVAL; } if (!rtw_fw_dump_check_size(rtwdev, fifo_sel, addr, size)) { rtw_dbg(rtwdev, RTW_DBG_FW, "fw fifo dump size overflow\n"); return -EINVAL; } rtw_fw_read_fifo(rtwdev, fifo_sel, addr, size, buffer); return 0; } static void __rtw_fw_update_pkt(struct rtw_dev *rtwdev, u8 pkt_id, u16 size, u8 location) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; u8 h2c_pkt[H2C_PKT_SIZE] = {0}; u16 total_size = H2C_PKT_HDR_SIZE + H2C_PKT_UPDATE_PKT_LEN; rtw_h2c_pkt_set_header(h2c_pkt, H2C_PKT_UPDATE_PKT); SET_PKT_H2C_TOTAL_LEN(h2c_pkt, total_size); UPDATE_PKT_SET_PKT_ID(h2c_pkt, pkt_id); UPDATE_PKT_SET_LOCATION(h2c_pkt, location); /* include txdesc size */ size += chip->tx_pkt_desc_sz; UPDATE_PKT_SET_SIZE(h2c_pkt, size); rtw_fw_send_h2c_packet(rtwdev, h2c_pkt); } void rtw_fw_update_pkt_probe_req(struct rtw_dev *rtwdev, struct cfg80211_ssid *ssid) { u8 loc; u16 size; loc = rtw_get_rsvd_page_probe_req_location(rtwdev, ssid); if (!loc) { rtw_err(rtwdev, "failed to get probe_req rsvd loc\n"); return; } size = rtw_get_rsvd_page_probe_req_size(rtwdev, ssid); if (!size) { rtw_err(rtwdev, "failed to get probe_req rsvd size\n"); return; } __rtw_fw_update_pkt(rtwdev, RTW_PACKET_PROBE_REQ, size, loc); } void rtw_fw_channel_switch(struct rtw_dev *rtwdev, bool enable) { struct rtw_pno_request *rtw_pno_req = &rtwdev->wow.pno_req; u8 h2c_pkt[H2C_PKT_SIZE] = {0}; u16 total_size = H2C_PKT_HDR_SIZE + H2C_PKT_CH_SWITCH_LEN; u8 loc_ch_info; const struct rtw_ch_switch_option cs_option = { .dest_ch_en = 1, .dest_ch = 1, .periodic_option = 2, .normal_period = 5, .normal_period_sel = 0, .normal_cycle = 10, .slow_period = 1, .slow_period_sel = 1, }; rtw_h2c_pkt_set_header(h2c_pkt, H2C_PKT_CH_SWITCH); SET_PKT_H2C_TOTAL_LEN(h2c_pkt, total_size); CH_SWITCH_SET_START(h2c_pkt, enable); CH_SWITCH_SET_DEST_CH_EN(h2c_pkt, cs_option.dest_ch_en); CH_SWITCH_SET_DEST_CH(h2c_pkt, cs_option.dest_ch); CH_SWITCH_SET_NORMAL_PERIOD(h2c_pkt, cs_option.normal_period); CH_SWITCH_SET_NORMAL_PERIOD_SEL(h2c_pkt, cs_option.normal_period_sel); CH_SWITCH_SET_SLOW_PERIOD(h2c_pkt, cs_option.slow_period); CH_SWITCH_SET_SLOW_PERIOD_SEL(h2c_pkt, cs_option.slow_period_sel); CH_SWITCH_SET_NORMAL_CYCLE(h2c_pkt, cs_option.normal_cycle); CH_SWITCH_SET_PERIODIC_OPT(h2c_pkt, cs_option.periodic_option); CH_SWITCH_SET_CH_NUM(h2c_pkt, rtw_pno_req->channel_cnt); CH_SWITCH_SET_INFO_SIZE(h2c_pkt, rtw_pno_req->channel_cnt * 4); loc_ch_info = rtw_get_rsvd_page_location(rtwdev, RSVD_CH_INFO); CH_SWITCH_SET_INFO_LOC(h2c_pkt, loc_ch_info); rtw_fw_send_h2c_packet(rtwdev, h2c_pkt); } void rtw_fw_adaptivity(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 h2c_pkt[H2C_PKT_SIZE] = {0}; if (!rtw_edcca_enabled) { dm_info->edcca_mode = RTW_EDCCA_NORMAL; rtw_dbg(rtwdev, RTW_DBG_ADAPTIVITY, "EDCCA disabled by debugfs\n"); } SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_ADAPTIVITY); SET_ADAPTIVITY_MODE(h2c_pkt, dm_info->edcca_mode); SET_ADAPTIVITY_OPTION(h2c_pkt, 1); SET_ADAPTIVITY_IGI(h2c_pkt, dm_info->igi_history[0]); SET_ADAPTIVITY_L2H(h2c_pkt, dm_info->l2h_th_ini); SET_ADAPTIVITY_DENSITY(h2c_pkt, dm_info->scan_density); rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } void rtw_fw_scan_notify(struct rtw_dev *rtwdev, bool start) { u8 h2c_pkt[H2C_PKT_SIZE] = {0}; SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_SCAN); SET_SCAN_START(h2c_pkt, start); rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } static int rtw_append_probe_req_ie(struct rtw_dev *rtwdev, struct sk_buff *skb, struct sk_buff_head *list, u8 *bands, struct rtw_vif *rtwvif) { + const struct rtw_chip_info *chip = rtwdev->chip; struct ieee80211_scan_ies *ies = rtwvif->scan_ies; - struct rtw_chip_info *chip = rtwdev->chip; struct sk_buff *new; u8 idx; for (idx = NL80211_BAND_2GHZ; idx < NUM_NL80211_BANDS; idx++) { if (!(BIT(idx) & chip->band)) continue; new = skb_copy(skb, GFP_KERNEL); if (!new) return -ENOMEM; skb_put_data(new, ies->ies[idx], ies->len[idx]); skb_put_data(new, ies->common_ies, ies->common_ie_len); skb_queue_tail(list, new); (*bands)++; } return 0; } static int _rtw_hw_scan_update_probe_req(struct rtw_dev *rtwdev, u8 num_probes, struct sk_buff_head *probe_req_list) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct sk_buff *skb, *tmp; u8 page_offset = 1, *buf, page_size = chip->page_size; - u8 pages = page_offset + num_probes * RTW_PROBE_PG_CNT; u16 pg_addr = rtwdev->fifo.rsvd_h2c_info_addr, loc; u16 buf_offset = page_size * page_offset; u8 tx_desc_sz = chip->tx_pkt_desc_sz; + u8 page_cnt, pages; unsigned int pkt_len; int ret; + if (rtw_fw_feature_ext_check(&rtwdev->fw, FW_FEATURE_EXT_OLD_PAGE_NUM)) + page_cnt = RTW_OLD_PROBE_PG_CNT; + else + page_cnt = RTW_PROBE_PG_CNT; + + pages = page_offset + num_probes * page_cnt; + buf = kzalloc(page_size * pages, GFP_KERNEL); if (!buf) return -ENOMEM; buf_offset -= tx_desc_sz; skb_queue_walk_safe(probe_req_list, skb, tmp) { skb_unlink(skb, probe_req_list); rtw_fill_rsvd_page_desc(rtwdev, skb, RSVD_PROBE_REQ); - if (skb->len > page_size * RTW_PROBE_PG_CNT) { + if (skb->len > page_size * page_cnt) { #if defined(__FreeBSD__) kfree_skb(skb); #endif ret = -EINVAL; goto out; } memcpy(buf + buf_offset, skb->data, skb->len); pkt_len = skb->len - tx_desc_sz; loc = pg_addr - rtwdev->fifo.rsvd_boundary + page_offset; __rtw_fw_update_pkt(rtwdev, RTW_PACKET_PROBE_REQ, pkt_len, loc); - buf_offset += RTW_PROBE_PG_CNT * page_size; - page_offset += RTW_PROBE_PG_CNT; + buf_offset += page_cnt * page_size; + page_offset += page_cnt; kfree_skb(skb); } ret = rtw_fw_write_data_rsvd_page(rtwdev, pg_addr, buf, buf_offset); if (ret) { rtw_err(rtwdev, "Download probe request to firmware failed\n"); goto out; } rtwdev->scan_info.probe_pg_size = page_offset; out: kfree(buf); skb_queue_walk_safe(probe_req_list, skb, tmp) kfree_skb(skb); return ret; } static int rtw_hw_scan_update_probe_req(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif) { struct cfg80211_scan_request *req = rtwvif->scan_req; struct sk_buff_head list; struct sk_buff *skb, *tmp; u8 num = req->n_ssids, i, bands = 0; int ret; skb_queue_head_init(&list); for (i = 0; i < num; i++) { skb = ieee80211_probereq_get(rtwdev->hw, rtwvif->mac_addr, req->ssids[i].ssid, req->ssids[i].ssid_len, req->ie_len); if (!skb) { ret = -ENOMEM; goto out; } ret = rtw_append_probe_req_ie(rtwdev, skb, &list, &bands, rtwvif); if (ret) goto out; kfree_skb(skb); } return _rtw_hw_scan_update_probe_req(rtwdev, num * bands, &list); out: skb_queue_walk_safe(&list, skb, tmp) kfree_skb(skb); return ret; } static int rtw_add_chan_info(struct rtw_dev *rtwdev, struct rtw_chan_info *info, struct rtw_chan_list *list, u8 *buf) { u8 *chan = &buf[list->size]; u8 info_size = RTW_CH_INFO_SIZE; if (list->size > list->buf_size) return -ENOMEM; CH_INFO_SET_CH(chan, info->channel); CH_INFO_SET_PRI_CH_IDX(chan, info->pri_ch_idx); CH_INFO_SET_BW(chan, info->bw); CH_INFO_SET_TIMEOUT(chan, info->timeout); CH_INFO_SET_ACTION_ID(chan, info->action_id); CH_INFO_SET_EXTRA_INFO(chan, info->extra_info); if (info->extra_info) { EXTRA_CH_INFO_SET_ID(chan, RTW_SCAN_EXTRA_ID_DFS); EXTRA_CH_INFO_SET_INFO(chan, RTW_SCAN_EXTRA_ACTION_SCAN); EXTRA_CH_INFO_SET_SIZE(chan, RTW_EX_CH_INFO_SIZE - RTW_EX_CH_INFO_HDR_SIZE); EXTRA_CH_INFO_SET_DFS_EXT_TIME(chan, RTW_DFS_CHAN_TIME); info_size += RTW_EX_CH_INFO_SIZE; } list->size += info_size; list->ch_num++; return 0; } static int rtw_add_chan_list(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif, struct rtw_chan_list *list, u8 *buf) { struct cfg80211_scan_request *req = rtwvif->scan_req; struct rtw_fifo_conf *fifo = &rtwdev->fifo; struct ieee80211_channel *channel; int i, ret = 0; for (i = 0; i < req->n_channels; i++) { struct rtw_chan_info ch_info = {0}; channel = req->channels[i]; ch_info.channel = channel->hw_value; ch_info.bw = RTW_SCAN_WIDTH; ch_info.pri_ch_idx = RTW_PRI_CH_IDX; ch_info.timeout = req->duration_mandatory ? req->duration : RTW_CHANNEL_TIME; if (channel->flags & (IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IR)) { ch_info.action_id = RTW_CHANNEL_RADAR; ch_info.extra_info = 1; /* Overwrite duration for passive scans if necessary */ ch_info.timeout = ch_info.timeout > RTW_PASS_CHAN_TIME ? ch_info.timeout : RTW_PASS_CHAN_TIME; } else { ch_info.action_id = RTW_CHANNEL_ACTIVE; } ret = rtw_add_chan_info(rtwdev, &ch_info, list, buf); if (ret) return ret; } if (list->size > fifo->rsvd_pg_num << TX_PAGE_SIZE_SHIFT) { rtw_err(rtwdev, "List exceeds rsvd page total size\n"); return -EINVAL; } list->addr = fifo->rsvd_h2c_info_addr + rtwdev->scan_info.probe_pg_size; ret = rtw_fw_write_data_rsvd_page(rtwdev, list->addr, buf, list->size); if (ret) rtw_err(rtwdev, "Download channel list failed\n"); return ret; } static void rtw_fw_set_scan_offload(struct rtw_dev *rtwdev, struct rtw_ch_switch_option *opt, struct rtw_vif *rtwvif, struct rtw_chan_list *list) { struct rtw_hw_scan_info *scan_info = &rtwdev->scan_info; struct cfg80211_scan_request *req = rtwvif->scan_req; struct rtw_fifo_conf *fifo = &rtwdev->fifo; /* reserve one dummy page at the beginning for tx descriptor */ u8 pkt_loc = fifo->rsvd_h2c_info_addr - fifo->rsvd_boundary + 1; bool random_seq = req->flags & NL80211_SCAN_FLAG_RANDOM_SN; u8 h2c_pkt[H2C_PKT_SIZE] = {0}; rtw_h2c_pkt_set_header(h2c_pkt, H2C_PKT_SCAN_OFFLOAD); SET_PKT_H2C_TOTAL_LEN(h2c_pkt, H2C_PKT_CH_SWITCH_LEN); SCAN_OFFLOAD_SET_START(h2c_pkt, opt->switch_en); SCAN_OFFLOAD_SET_BACK_OP_EN(h2c_pkt, opt->back_op_en); SCAN_OFFLOAD_SET_RANDOM_SEQ_EN(h2c_pkt, random_seq); SCAN_OFFLOAD_SET_NO_CCK_EN(h2c_pkt, req->no_cck); SCAN_OFFLOAD_SET_CH_NUM(h2c_pkt, list->ch_num); SCAN_OFFLOAD_SET_CH_INFO_SIZE(h2c_pkt, list->size); SCAN_OFFLOAD_SET_CH_INFO_LOC(h2c_pkt, list->addr - fifo->rsvd_boundary); SCAN_OFFLOAD_SET_OP_CH(h2c_pkt, scan_info->op_chan); SCAN_OFFLOAD_SET_OP_PRI_CH_IDX(h2c_pkt, scan_info->op_pri_ch_idx); SCAN_OFFLOAD_SET_OP_BW(h2c_pkt, scan_info->op_bw); SCAN_OFFLOAD_SET_OP_PORT_ID(h2c_pkt, rtwvif->port); SCAN_OFFLOAD_SET_OP_DWELL_TIME(h2c_pkt, req->duration_mandatory ? req->duration : RTW_CHANNEL_TIME); SCAN_OFFLOAD_SET_OP_GAP_TIME(h2c_pkt, RTW_OFF_CHAN_TIME); SCAN_OFFLOAD_SET_SSID_NUM(h2c_pkt, req->n_ssids); SCAN_OFFLOAD_SET_PKT_LOC(h2c_pkt, pkt_loc); rtw_fw_send_h2c_packet(rtwdev, h2c_pkt); } void rtw_hw_scan_start(struct rtw_dev *rtwdev, struct ieee80211_vif *vif, struct ieee80211_scan_request *scan_req) { struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; struct cfg80211_scan_request *req = &scan_req->req; u8 mac_addr[ETH_ALEN]; rtwdev->scan_info.scanning_vif = vif; rtwvif->scan_ies = &scan_req->ies; rtwvif->scan_req = req; ieee80211_stop_queues(rtwdev->hw); + rtw_leave_lps_deep(rtwdev); + rtw_hci_flush_all_queues(rtwdev, false); + rtw_mac_flush_all_queues(rtwdev, false); if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) get_random_mask_addr(mac_addr, req->mac_addr, req->mac_addr_mask); else ether_addr_copy(mac_addr, vif->addr); rtw_core_scan_start(rtwdev, rtwvif, mac_addr, true); rtwdev->hal.rcr &= ~BIT_CBSSID_BCN; rtw_write32(rtwdev, REG_RCR, rtwdev->hal.rcr); } void rtw_hw_scan_complete(struct rtw_dev *rtwdev, struct ieee80211_vif *vif, bool aborted) { struct cfg80211_scan_info info = { .aborted = aborted, }; struct rtw_hw_scan_info *scan_info = &rtwdev->scan_info; struct rtw_hal *hal = &rtwdev->hal; struct rtw_vif *rtwvif; u8 chan = scan_info->op_chan; if (!vif) return; rtwdev->hal.rcr |= BIT_CBSSID_BCN; rtw_write32(rtwdev, REG_RCR, rtwdev->hal.rcr); rtw_core_scan_complete(rtwdev, vif, true); rtwvif = (struct rtw_vif *)vif->drv_priv; - if (rtwvif->net_type == RTW_NET_MGD_LINKED) { - hal->current_channel = chan; - hal->current_band_type = chan > 14 ? RTW_BAND_5G : RTW_BAND_2G; - } + if (chan) + rtw_store_op_chan(rtwdev, false); + rtw_phy_set_tx_power_level(rtwdev, hal->current_channel); ieee80211_wake_queues(rtwdev->hw); ieee80211_scan_completed(rtwdev->hw, &info); rtwvif->scan_req = NULL; rtwvif->scan_ies = NULL; rtwdev->scan_info.scanning_vif = NULL; } static int rtw_hw_scan_prehandle(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif, struct rtw_chan_list *list) { struct cfg80211_scan_request *req = rtwvif->scan_req; int size = req->n_channels * (RTW_CH_INFO_SIZE + RTW_EX_CH_INFO_SIZE); u8 *buf; int ret; buf = kmalloc(size, GFP_KERNEL); if (!buf) return -ENOMEM; ret = rtw_hw_scan_update_probe_req(rtwdev, rtwvif); if (ret) { rtw_err(rtwdev, "Update probe request failed\n"); goto out; } list->buf_size = size; list->size = 0; list->ch_num = 0; ret = rtw_add_chan_list(rtwdev, rtwvif, list, buf); out: kfree(buf); return ret; } int rtw_hw_scan_offload(struct rtw_dev *rtwdev, struct ieee80211_vif *vif, bool enable) { struct rtw_vif *rtwvif = vif ? (struct rtw_vif *)vif->drv_priv : NULL; + struct rtw_hw_scan_info *scan_info = &rtwdev->scan_info; struct rtw_ch_switch_option cs_option = {0}; struct rtw_chan_list chan_list = {0}; int ret = 0; if (!rtwvif) return -EINVAL; cs_option.switch_en = enable; - cs_option.back_op_en = rtwvif->net_type == RTW_NET_MGD_LINKED; + cs_option.back_op_en = scan_info->op_chan != 0; if (enable) { ret = rtw_hw_scan_prehandle(rtwdev, rtwvif, &chan_list); if (ret) goto out; } rtw_fw_set_scan_offload(rtwdev, &cs_option, rtwvif, &chan_list); out: + if (rtwdev->ap_active) { + ret = rtw_download_beacon(rtwdev); + if (ret) + rtw_err(rtwdev, "HW scan download beacon failed\n"); + } + return ret; } -void rtw_hw_scan_abort(struct rtw_dev *rtwdev, struct ieee80211_vif *vif) +void rtw_hw_scan_abort(struct rtw_dev *rtwdev) { + struct ieee80211_vif *vif = rtwdev->scan_info.scanning_vif; + if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_SCAN_OFFLOAD)) return; rtw_hw_scan_offload(rtwdev, vif, false); rtw_hw_scan_complete(rtwdev, vif, true); } void rtw_hw_scan_status_report(struct rtw_dev *rtwdev, struct sk_buff *skb) { struct ieee80211_vif *vif = rtwdev->scan_info.scanning_vif; struct rtw_c2h_cmd *c2h; bool aborted; u8 rc; if (!test_bit(RTW_FLAG_SCANNING, rtwdev->flags)) return; c2h = get_c2h_from_skb(skb); rc = GET_SCAN_REPORT_RETURN_CODE(c2h->payload); aborted = rc != RTW_SCAN_REPORT_SUCCESS; rtw_hw_scan_complete(rtwdev, vif, aborted); if (aborted) rtw_dbg(rtwdev, RTW_DBG_HW_SCAN, "HW scan aborted with code: %d\n", rc); } -void rtw_store_op_chan(struct rtw_dev *rtwdev) +void rtw_store_op_chan(struct rtw_dev *rtwdev, bool backup) { struct rtw_hw_scan_info *scan_info = &rtwdev->scan_info; struct rtw_hal *hal = &rtwdev->hal; + u8 band; - scan_info->op_chan = hal->current_channel; - scan_info->op_bw = hal->current_band_width; - scan_info->op_pri_ch_idx = hal->current_primary_channel_index; + if (backup) { + scan_info->op_chan = hal->current_channel; + scan_info->op_bw = hal->current_band_width; + scan_info->op_pri_ch_idx = hal->current_primary_channel_index; + scan_info->op_pri_ch = hal->primary_channel; + } else { + band = scan_info->op_chan > 14 ? RTW_BAND_5G : RTW_BAND_2G; + rtw_update_channel(rtwdev, scan_info->op_chan, + scan_info->op_pri_ch, + band, scan_info->op_bw); + } +} + +void rtw_clear_op_chan(struct rtw_dev *rtwdev) +{ + struct rtw_hw_scan_info *scan_info = &rtwdev->scan_info; + + scan_info->op_chan = 0; + scan_info->op_bw = 0; + scan_info->op_pri_ch_idx = 0; + scan_info->op_pri_ch = 0; } static bool rtw_is_op_chan(struct rtw_dev *rtwdev, u8 channel) { struct rtw_hw_scan_info *scan_info = &rtwdev->scan_info; return channel == scan_info->op_chan; } void rtw_hw_scan_chan_switch(struct rtw_dev *rtwdev, struct sk_buff *skb) { struct rtw_hal *hal = &rtwdev->hal; struct rtw_c2h_cmd *c2h; enum rtw_scan_notify_id id; - u8 chan, status; + u8 chan, band, status; if (!test_bit(RTW_FLAG_SCANNING, rtwdev->flags)) return; c2h = get_c2h_from_skb(skb); chan = GET_CHAN_SWITCH_CENTRAL_CH(c2h->payload); id = GET_CHAN_SWITCH_ID(c2h->payload); status = GET_CHAN_SWITCH_STATUS(c2h->payload); if (id == RTW_SCAN_NOTIFY_ID_POSTSWITCH) { - if (rtw_is_op_chan(rtwdev, chan)) + band = chan > 14 ? RTW_BAND_5G : RTW_BAND_2G; + rtw_update_channel(rtwdev, chan, chan, band, + RTW_CHANNEL_WIDTH_20); + if (rtw_is_op_chan(rtwdev, chan)) { + rtw_store_op_chan(rtwdev, false); ieee80211_wake_queues(rtwdev->hw); - hal->current_channel = chan; - hal->current_band_type = chan > 14 ? RTW_BAND_5G : RTW_BAND_2G; + rtw_core_enable_beacon(rtwdev, true); + } } else if (id == RTW_SCAN_NOTIFY_ID_PRESWITCH) { if (IS_CH_5G_BAND(chan)) { rtw_coex_switchband_notify(rtwdev, COEX_SWITCH_TO_5G); } else if (IS_CH_2G_BAND(chan)) { u8 chan_type; if (test_bit(RTW_FLAG_SCANNING, rtwdev->flags)) chan_type = COEX_SWITCH_TO_24G; else chan_type = COEX_SWITCH_TO_24G_NOFORSCAN; rtw_coex_switchband_notify(rtwdev, chan_type); } - if (rtw_is_op_chan(rtwdev, chan)) + /* The channel of C2H RTW_SCAN_NOTIFY_ID_PRESWITCH is next + * channel that hardware will switch. We need to stop queue + * if next channel is non-op channel. + */ + if (!rtw_is_op_chan(rtwdev, chan) && + rtw_is_op_chan(rtwdev, hal->current_channel)) { + rtw_core_enable_beacon(rtwdev, false); ieee80211_stop_queues(rtwdev->hw); + } } rtw_dbg(rtwdev, RTW_DBG_HW_SCAN, "Chan switch: %x, id: %x, status: %x\n", chan, id, status); } diff --git a/sys/contrib/dev/rtw88/fw.h b/sys/contrib/dev/rtw88/fw.h index 7a37675c61e8..43ccdf9965ac 100644 --- a/sys/contrib/dev/rtw88/fw.h +++ b/sys/contrib/dev/rtw88/fw.h @@ -1,844 +1,885 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* Copyright(c) 2018-2019 Realtek Corporation */ #ifndef __RTW_FW_H_ #define __RTW_FW_H_ #define H2C_PKT_SIZE 32 #define H2C_PKT_HDR_SIZE 8 /* FW bin information */ #define FW_HDR_SIZE 64 #define FW_HDR_CHKSUM_SIZE 8 #define FW_NLO_INFO_CHECK_SIZE 4 #define FIFO_PAGE_SIZE_SHIFT 12 #define FIFO_PAGE_SIZE 4096 #define FIFO_DUMP_ADDR 0x8000 #define DLFW_PAGE_SIZE_SHIFT_LEGACY 12 #define DLFW_PAGE_SIZE_LEGACY 0x1000 #define DLFW_BLK_SIZE_SHIFT_LEGACY 2 #define DLFW_BLK_SIZE_LEGACY 4 #define FW_START_ADDR_LEGACY 0x1000 #define BCN_LOSS_CNT 10 #define BCN_FILTER_NOTIFY_SIGNAL_CHANGE 0 #define BCN_FILTER_CONNECTION_LOSS 1 #define BCN_FILTER_CONNECTED 2 #define BCN_FILTER_NOTIFY_BEACON_LOSS 3 #define SCAN_NOTIFY_TIMEOUT msecs_to_jiffies(10) #define RTW_CHANNEL_TIME 45 #define RTW_OFF_CHAN_TIME 100 #define RTW_PASS_CHAN_TIME 105 #define RTW_DFS_CHAN_TIME 20 #define RTW_CH_INFO_SIZE 4 #define RTW_EX_CH_INFO_SIZE 3 #define RTW_EX_CH_INFO_HDR_SIZE 2 #define RTW_SCAN_WIDTH 0 #define RTW_PRI_CH_IDX 1 -#define RTW_PROBE_PG_CNT 2 +#define RTW_OLD_PROBE_PG_CNT 2 +#define RTW_PROBE_PG_CNT 4 enum rtw_c2h_cmd_id { C2H_CCX_TX_RPT = 0x03, C2H_BT_INFO = 0x09, C2H_BT_MP_INFO = 0x0b, C2H_BT_HID_INFO = 0x45, C2H_RA_RPT = 0x0c, C2H_HW_FEATURE_REPORT = 0x19, C2H_WLAN_INFO = 0x27, C2H_WLAN_RFON = 0x32, C2H_BCN_FILTER_NOTIFY = 0x36, C2H_ADAPTIVITY = 0x37, C2H_SCAN_RESULT = 0x38, C2H_HW_FEATURE_DUMP = 0xfd, C2H_HALMAC = 0xff, }; enum rtw_c2h_cmd_id_ext { C2H_SCAN_STATUS_RPT = 0x3, C2H_CCX_RPT = 0x0f, C2H_CHAN_SWITCH = 0x22, }; struct rtw_c2h_cmd { u8 id; u8 seq; u8 payload[]; } __packed; struct rtw_c2h_adaptivity { u8 density; u8 igi; u8 l2h_th_init; u8 l2h; u8 h2l; u8 option; } __packed; +struct rtw_h2c_register { + u32 w0; + u32 w1; +} __packed; + +#define RTW_H2C_W0_CMDID GENMASK(7, 0) + +/* H2C_CMD_DEFAULT_PORT command */ +#define RTW_H2C_DEFAULT_PORT_W0_PORTID GENMASK(15, 8) +#define RTW_H2C_DEFAULT_PORT_W0_MACID GENMASK(23, 16) + +struct rtw_h2c_cmd { + __le32 msg; + __le32 msg_ext; +} __packed; + enum rtw_rsvd_packet_type { RSVD_BEACON, RSVD_DUMMY, RSVD_PS_POLL, RSVD_PROBE_RESP, RSVD_NULL, RSVD_QOS_NULL, RSVD_LPS_PG_DPK, RSVD_LPS_PG_INFO, RSVD_PROBE_REQ, RSVD_NLO_INFO, RSVD_CH_INFO, }; enum rtw_fw_rf_type { FW_RF_1T2R = 0, FW_RF_2T4R = 1, FW_RF_2T2R = 2, FW_RF_2T3R = 3, FW_RF_1T1R = 4, FW_RF_2T2R_GREEN = 5, FW_RF_3T3R = 6, FW_RF_3T4R = 7, FW_RF_4T4R = 8, FW_RF_MAX_TYPE = 0xF, }; enum rtw_fw_feature { FW_FEATURE_SIG = BIT(0), FW_FEATURE_LPS_C2H = BIT(1), FW_FEATURE_LCLK = BIT(2), FW_FEATURE_PG = BIT(3), FW_FEATURE_TX_WAKE = BIT(4), FW_FEATURE_BCN_FILTER = BIT(5), FW_FEATURE_NOTIFY_SCAN = BIT(6), FW_FEATURE_ADAPTIVITY = BIT(7), FW_FEATURE_SCAN_OFFLOAD = BIT(8), FW_FEATURE_MAX = BIT(31), }; +enum rtw_fw_feature_ext { + FW_FEATURE_EXT_OLD_PAGE_NUM = BIT(0), +}; + enum rtw_beacon_filter_offload_mode { BCN_FILTER_OFFLOAD_MODE_0 = 0, BCN_FILTER_OFFLOAD_MODE_1, BCN_FILTER_OFFLOAD_MODE_2, BCN_FILTER_OFFLOAD_MODE_3, BCN_FILTER_OFFLOAD_MODE_DEFAULT = BCN_FILTER_OFFLOAD_MODE_0, }; struct rtw_coex_info_req { u8 seq; u8 op_code; u8 para1; u8 para2; u8 para3; }; struct rtw_iqk_para { u8 clear; u8 segment_iqk; }; struct rtw_lps_pg_dpk_hdr { u16 dpk_path_ok; u8 dpk_txagc[2]; u16 dpk_gs[2]; u32 coef[2][20]; u8 dpk_ch; } __packed; struct rtw_lps_pg_info_hdr { u8 macid; u8 mbssid; u8 pattern_count; u8 mu_tab_group_id; u8 sec_cam_count; u8 tx_bu_page_count; u16 rsvd; u8 sec_cam[MAX_PG_CAM_BACKUP_NUM]; } __packed; struct rtw_rsvd_page { /* associated with each vif */ struct list_head vif_list; struct rtw_vif *rtwvif; /* associated when build rsvd page */ struct list_head build_list; struct sk_buff *skb; enum rtw_rsvd_packet_type type; u8 page; u16 tim_offset; bool add_txdesc; struct cfg80211_ssid *ssid; u16 probe_req_size; }; enum rtw_keep_alive_pkt_type { KEEP_ALIVE_NULL_PKT = 0, KEEP_ALIVE_ARP_RSP = 1, }; struct rtw_nlo_info_hdr { u8 nlo_count; u8 hidden_ap_count; u8 rsvd1[2]; u8 pattern_check[FW_NLO_INFO_CHECK_SIZE]; u8 rsvd2[8]; u8 ssid_len[16]; u8 chiper[16]; u8 rsvd3[16]; u8 location[8]; } __packed; enum rtw_packet_type { RTW_PACKET_PROBE_REQ = 0x00, RTW_PACKET_UNDEFINE = 0x7FFFFFFF, }; struct rtw_fw_wow_keep_alive_para { bool adopt; u8 pkt_type; u8 period; /* unit: sec */ }; struct rtw_fw_wow_disconnect_para { bool adopt; u8 period; /* unit: sec */ u8 retry_count; }; enum rtw_channel_type { RTW_CHANNEL_PASSIVE, RTW_CHANNEL_ACTIVE, RTW_CHANNEL_RADAR, }; enum rtw_scan_extra_id { RTW_SCAN_EXTRA_ID_DFS, }; enum rtw_scan_extra_info { RTW_SCAN_EXTRA_ACTION_SCAN, }; enum rtw_scan_report_code { RTW_SCAN_REPORT_SUCCESS = 0x00, RTW_SCAN_REPORT_ERR_PHYDM = 0x01, RTW_SCAN_REPORT_ERR_ID = 0x02, RTW_SCAN_REPORT_ERR_TX = 0x03, RTW_SCAN_REPORT_CANCELED = 0x10, RTW_SCAN_REPORT_CANCELED_EXT = 0x11, RTW_SCAN_REPORT_FW_DISABLED = 0xF0, }; enum rtw_scan_notify_id { RTW_SCAN_NOTIFY_ID_PRESWITCH = 0x00, RTW_SCAN_NOTIFY_ID_POSTSWITCH = 0x01, RTW_SCAN_NOTIFY_ID_PROBE_PRETX = 0x02, RTW_SCAN_NOTIFY_ID_PROBE_ISSUETX = 0x03, RTW_SCAN_NOTIFY_ID_NULL0_PRETX = 0x04, RTW_SCAN_NOTIFY_ID_NULL0_ISSUETX = 0x05, RTW_SCAN_NOTIFY_ID_NULL0_POSTTX = 0x06, RTW_SCAN_NOTIFY_ID_NULL1_PRETX = 0x07, RTW_SCAN_NOTIFY_ID_NULL1_ISSUETX = 0x08, RTW_SCAN_NOTIFY_ID_NULL1_POSTTX = 0x09, RTW_SCAN_NOTIFY_ID_DWELLEXT = 0x0A, }; enum rtw_scan_notify_status { RTW_SCAN_NOTIFY_STATUS_SUCCESS = 0x00, RTW_SCAN_NOTIFY_STATUS_FAILURE = 0x01, RTW_SCAN_NOTIFY_STATUS_RESOURCE = 0x02, RTW_SCAN_NOTIFY_STATUS_TIMEOUT = 0x03, }; struct rtw_ch_switch_option { u8 periodic_option; u32 tsf_high; u32 tsf_low; u8 dest_ch_en; u8 absolute_time_en; u8 dest_ch; u8 normal_period; u8 normal_period_sel; u8 normal_cycle; u8 slow_period; u8 slow_period_sel; u8 nlo_en; bool switch_en; bool back_op_en; }; struct rtw_fw_hdr { __le16 signature; u8 category; u8 function; __le16 version; /* 0x04 */ u8 subversion; u8 subindex; __le32 rsvd; /* 0x08 */ __le32 feature; /* 0x0C */ u8 month; /* 0x10 */ u8 day; u8 hour; u8 min; __le16 year; /* 0x14 */ __le16 rsvd3; u8 mem_usage; /* 0x18 */ u8 rsvd4[3]; __le16 h2c_fmt_ver; /* 0x1C */ __le16 rsvd5; __le32 dmem_addr; /* 0x20 */ __le32 dmem_size; __le32 rsvd6; __le32 rsvd7; __le32 imem_size; /* 0x30 */ __le32 emem_size; __le32 emem_addr; __le32 imem_addr; } __packed; struct rtw_fw_hdr_legacy { __le16 signature; u8 category; u8 function; __le16 version; /* 0x04 */ u8 subversion1; u8 subversion2; u8 month; /* 0x08 */ u8 day; u8 hour; u8 minute; __le16 size; __le16 rsvd2; __le32 idx; /* 0x10 */ __le32 rsvd3; __le32 rsvd4; /* 0x18 */ __le32 rsvd5; } __packed; +#define RTW_FW_VER_CODE(ver, sub_ver, idx) \ + (((ver) << 16) | ((sub_ver) << 8) | (idx)) +#define RTW_FW_SUIT_VER_CODE(s) \ + RTW_FW_VER_CODE((s).version, (s).sub_version, (s).sub_index) + /* C2H */ #define GET_CCX_REPORT_SEQNUM_V0(c2h_payload) (c2h_payload[6] & 0xfc) #define GET_CCX_REPORT_STATUS_V0(c2h_payload) (c2h_payload[0] & 0xc0) #define GET_CCX_REPORT_SEQNUM_V1(c2h_payload) (c2h_payload[8] & 0xfc) #define GET_CCX_REPORT_STATUS_V1(c2h_payload) (c2h_payload[9] & 0xc0) #define GET_SCAN_REPORT_RETURN_CODE(c2h_payload) (c2h_payload[2] & 0xff) #define GET_CHAN_SWITCH_CENTRAL_CH(c2h_payload) (c2h_payload[2]) #define GET_CHAN_SWITCH_ID(c2h_payload) (c2h_payload[3]) #define GET_CHAN_SWITCH_STATUS(c2h_payload) (c2h_payload[4]) #define GET_RA_REPORT_RATE(c2h_payload) (c2h_payload[0] & 0x7f) #define GET_RA_REPORT_SGI(c2h_payload) ((c2h_payload[0] & 0x80) >> 7) #define GET_RA_REPORT_BW(c2h_payload) (c2h_payload[6]) #define GET_RA_REPORT_MACID(c2h_payload) (c2h_payload[1]) #define GET_BCN_FILTER_NOTIFY_TYPE(c2h_payload) (c2h_payload[1] & 0xf) #define GET_BCN_FILTER_NOTIFY_EVENT(c2h_payload) (c2h_payload[1] & 0x10) #define GET_BCN_FILTER_NOTIFY_RSSI(c2h_payload) (c2h_payload[2] - 100) /* PKT H2C */ #define H2C_PKT_CMD_ID 0xFF #define H2C_PKT_CATEGORY 0x01 #define H2C_PKT_GENERAL_INFO 0x0D #define H2C_PKT_PHYDM_INFO 0x11 #define H2C_PKT_IQK 0x0E #define H2C_PKT_CH_SWITCH 0x02 #define H2C_PKT_UPDATE_PKT 0x0C #define H2C_PKT_SCAN_OFFLOAD 0x19 #define H2C_PKT_CH_SWITCH_LEN 0x20 #define H2C_PKT_UPDATE_PKT_LEN 0x4 #define SET_PKT_H2C_CATEGORY(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(6, 0)) #define SET_PKT_H2C_CMD_ID(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(15, 8)) #define SET_PKT_H2C_SUB_CMD_ID(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(31, 16)) #define SET_PKT_H2C_TOTAL_LEN(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x01, value, GENMASK(15, 0)) static inline void rtw_h2c_pkt_set_header(u8 *h2c_pkt, u8 sub_id) { SET_PKT_H2C_CATEGORY(h2c_pkt, H2C_PKT_CATEGORY); SET_PKT_H2C_CMD_ID(h2c_pkt, H2C_PKT_CMD_ID); SET_PKT_H2C_SUB_CMD_ID(h2c_pkt, sub_id); } #define FW_OFFLOAD_H2C_SET_SEQ_NUM(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x01, value, GENMASK(31, 16)) #define GENERAL_INFO_SET_FW_TX_BOUNDARY(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, GENMASK(23, 16)) #define PHYDM_INFO_SET_REF_TYPE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, GENMASK(7, 0)) #define PHYDM_INFO_SET_RF_TYPE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, GENMASK(15, 8)) #define PHYDM_INFO_SET_CUT_VER(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, GENMASK(23, 16)) #define PHYDM_INFO_SET_RX_ANT_STATUS(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, GENMASK(27, 24)) #define PHYDM_INFO_SET_TX_ANT_STATUS(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, GENMASK(31, 28)) #define IQK_SET_CLEAR(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, BIT(0)) #define IQK_SET_SEGMENT_IQK(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, BIT(1)) #define CHSW_INFO_SET_CH(pkt, value) \ le32p_replace_bits((__le32 *)(pkt) + 0x00, value, GENMASK(7, 0)) #define CHSW_INFO_SET_PRI_CH_IDX(pkt, value) \ le32p_replace_bits((__le32 *)(pkt) + 0x00, value, GENMASK(11, 8)) #define CHSW_INFO_SET_BW(pkt, value) \ le32p_replace_bits((__le32 *)(pkt) + 0x00, value, GENMASK(15, 12)) #define CHSW_INFO_SET_TIMEOUT(pkt, value) \ le32p_replace_bits((__le32 *)(pkt) + 0x00, value, GENMASK(23, 16)) #define CHSW_INFO_SET_ACTION_ID(pkt, value) \ le32p_replace_bits((__le32 *)(pkt) + 0x00, value, GENMASK(30, 24)) #define CHSW_INFO_SET_EXTRA_INFO(pkt, value) \ le32p_replace_bits((__le32 *)(pkt) + 0x00, value, BIT(31)) #define CH_INFO_SET_CH(pkt, value) \ u8p_replace_bits((u8 *)(pkt) + 0x00, value, GENMASK(7, 0)) #define CH_INFO_SET_PRI_CH_IDX(pkt, value) \ u8p_replace_bits((u8 *)(pkt) + 0x01, value, GENMASK(3, 0)) #define CH_INFO_SET_BW(pkt, value) \ u8p_replace_bits((u8 *)(pkt) + 0x01, value, GENMASK(7, 4)) #define CH_INFO_SET_TIMEOUT(pkt, value) \ u8p_replace_bits((u8 *)(pkt) + 0x02, value, GENMASK(7, 0)) #define CH_INFO_SET_ACTION_ID(pkt, value) \ u8p_replace_bits((u8 *)(pkt) + 0x03, value, GENMASK(6, 0)) #define CH_INFO_SET_EXTRA_INFO(pkt, value) \ u8p_replace_bits((u8 *)(pkt) + 0x03, value, BIT(7)) #define EXTRA_CH_INFO_SET_ID(pkt, value) \ u8p_replace_bits((u8 *)(pkt) + 0x04, value, GENMASK(6, 0)) #define EXTRA_CH_INFO_SET_INFO(pkt, value) \ u8p_replace_bits((u8 *)(pkt) + 0x04, value, BIT(7)) #define EXTRA_CH_INFO_SET_SIZE(pkt, value) \ u8p_replace_bits((u8 *)(pkt) + 0x05, value, GENMASK(7, 0)) #define EXTRA_CH_INFO_SET_DFS_EXT_TIME(pkt, value) \ u8p_replace_bits((u8 *)(pkt) + 0x06, value, GENMASK(7, 0)) #define UPDATE_PKT_SET_SIZE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, GENMASK(15, 0)) #define UPDATE_PKT_SET_PKT_ID(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, GENMASK(23, 16)) #define UPDATE_PKT_SET_LOCATION(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, GENMASK(31, 24)) #define CH_SWITCH_SET_START(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, BIT(0)) #define CH_SWITCH_SET_DEST_CH_EN(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, BIT(1)) #define CH_SWITCH_SET_ABSOLUTE_TIME(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, BIT(2)) #define CH_SWITCH_SET_PERIODIC_OPT(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, GENMASK(4, 3)) #define CH_SWITCH_SET_SCAN_MODE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, BIT(5)) #define CH_SWITCH_SET_BACK_OP_EN(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, BIT(6)) #define CH_SWITCH_SET_INFO_LOC(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, GENMASK(15, 8)) #define CH_SWITCH_SET_CH_NUM(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, GENMASK(23, 16)) #define CH_SWITCH_SET_PRI_CH_IDX(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, GENMASK(27, 24)) #define CH_SWITCH_SET_DEST_BW(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, GENMASK(31, 28)) #define CH_SWITCH_SET_DEST_CH(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x03, value, GENMASK(7, 0)) #define CH_SWITCH_SET_NORMAL_PERIOD(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x03, value, GENMASK(13, 8)) #define CH_SWITCH_SET_NORMAL_PERIOD_SEL(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x03, value, GENMASK(15, 14)) #define CH_SWITCH_SET_SLOW_PERIOD(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x03, value, GENMASK(21, 16)) #define CH_SWITCH_SET_SLOW_PERIOD_SEL(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x03, value, GENMASK(23, 22)) #define CH_SWITCH_SET_NORMAL_CYCLE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x03, value, GENMASK(31, 24)) #define CH_SWITCH_SET_TSF_HIGH(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x04, value, GENMASK(31, 0)) #define CH_SWITCH_SET_TSF_LOW(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x05, value, GENMASK(31, 0)) #define CH_SWITCH_SET_INFO_SIZE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x06, value, GENMASK(15, 0)) #define SCAN_OFFLOAD_SET_START(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, BIT(0)) #define SCAN_OFFLOAD_SET_BACK_OP_EN(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, BIT(1)) #define SCAN_OFFLOAD_SET_RANDOM_SEQ_EN(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, BIT(2)) #define SCAN_OFFLOAD_SET_NO_CCK_EN(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, BIT(3)) #define SCAN_OFFLOAD_SET_VERBOSE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, BIT(4)) #define SCAN_OFFLOAD_SET_CH_NUM(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, GENMASK(15, 8)) #define SCAN_OFFLOAD_SET_CH_INFO_SIZE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x02, value, GENMASK(31, 16)) #define SCAN_OFFLOAD_SET_CH_INFO_LOC(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x03, value, GENMASK(7, 0)) #define SCAN_OFFLOAD_SET_OP_CH(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x03, value, GENMASK(15, 8)) #define SCAN_OFFLOAD_SET_OP_PRI_CH_IDX(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x03, value, GENMASK(19, 16)) #define SCAN_OFFLOAD_SET_OP_BW(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x03, value, GENMASK(23, 20)) #define SCAN_OFFLOAD_SET_OP_PORT_ID(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x03, value, GENMASK(26, 24)) #define SCAN_OFFLOAD_SET_OP_DWELL_TIME(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x04, value, GENMASK(15, 0)) #define SCAN_OFFLOAD_SET_OP_GAP_TIME(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x04, value, GENMASK(31, 16)) #define SCAN_OFFLOAD_SET_MODE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x05, value, GENMASK(3, 0)) #define SCAN_OFFLOAD_SET_SSID_NUM(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x05, value, GENMASK(7, 4)) #define SCAN_OFFLOAD_SET_PKT_LOC(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x05, value, GENMASK(15, 8)) /* Command H2C */ #define H2C_CMD_RSVD_PAGE 0x0 #define H2C_CMD_MEDIA_STATUS_RPT 0x01 #define H2C_CMD_SET_PWR_MODE 0x20 #define H2C_CMD_LPS_PG_INFO 0x2b +#define H2C_CMD_DEFAULT_PORT 0x2c #define H2C_CMD_RA_INFO 0x40 #define H2C_CMD_RSSI_MONITOR 0x42 #define H2C_CMD_BCN_FILTER_OFFLOAD_P0 0x56 #define H2C_CMD_BCN_FILTER_OFFLOAD_P1 0x57 #define H2C_CMD_WL_PHY_INFO 0x58 #define H2C_CMD_SCAN 0x59 #define H2C_CMD_ADAPTIVITY 0x5A #define H2C_CMD_COEX_TDMA_TYPE 0x60 #define H2C_CMD_QUERY_BT_INFO 0x61 #define H2C_CMD_FORCE_BT_TX_POWER 0x62 #define H2C_CMD_IGNORE_WLAN_ACTION 0x63 #define H2C_CMD_WL_CH_INFO 0x66 #define H2C_CMD_QUERY_BT_MP_INFO 0x67 #define H2C_CMD_BT_WIFI_CONTROL 0x69 #define H2C_CMD_WIFI_CALIBRATION 0x6d #define H2C_CMD_QUERY_BT_HID_INFO 0x73 #define H2C_CMD_KEEP_ALIVE 0x03 #define H2C_CMD_DISCONNECT_DECISION 0x04 #define H2C_CMD_WOWLAN 0x80 #define H2C_CMD_REMOTE_WAKE_CTRL 0x81 #define H2C_CMD_AOAC_GLOBAL_INFO 0x82 #define H2C_CMD_NLO_INFO 0x8C +#define H2C_CMD_RECOVER_BT_DEV 0xD1 + #define SET_H2C_CMD_ID_CLASS(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(7, 0)) #define MEDIA_STATUS_RPT_SET_OP_MODE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(8)) #define MEDIA_STATUS_RPT_SET_MACID(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(23, 16)) #define SET_WL_PHY_INFO_TX_TP(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(17, 8)) #define SET_WL_PHY_INFO_RX_TP(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(27, 18)) #define SET_WL_PHY_INFO_TX_RATE_DESC(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x01, value, GENMASK(7, 0)) #define SET_WL_PHY_INFO_RX_RATE_DESC(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x01, value, GENMASK(15, 8)) #define SET_WL_PHY_INFO_RX_EVM(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x01, value, GENMASK(23, 16)) #define SET_BCN_FILTER_OFFLOAD_P1_MACID(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(15, 8)) #define SET_BCN_FILTER_OFFLOAD_P1_ENABLE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(16)) #define SET_BCN_FILTER_OFFLOAD_P1_HYST(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(20, 17)) #define SET_BCN_FILTER_OFFLOAD_P1_OFFLOAD_MODE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(23, 21)) #define SET_BCN_FILTER_OFFLOAD_P1_THRESHOLD(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(31, 24)) #define SET_BCN_FILTER_OFFLOAD_P1_BCN_LOSS_CNT(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x01, value, GENMASK(3, 0)) #define SET_BCN_FILTER_OFFLOAD_P1_BCN_INTERVAL(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x01, value, GENMASK(13, 4)) #define SET_SCAN_START(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(8)) #define SET_ADAPTIVITY_MODE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(11, 8)) #define SET_ADAPTIVITY_OPTION(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(15, 12)) #define SET_ADAPTIVITY_IGI(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(23, 16)) #define SET_ADAPTIVITY_L2H(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(31, 24)) #define SET_ADAPTIVITY_DENSITY(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x01, value, GENMASK(7, 0)) #define SET_PWR_MODE_SET_MODE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(14, 8)) #define SET_PWR_MODE_SET_RLBM(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(19, 16)) #define SET_PWR_MODE_SET_SMART_PS(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(23, 20)) #define SET_PWR_MODE_SET_AWAKE_INTERVAL(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(31, 24)) #define SET_PWR_MODE_SET_PORT_ID(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x01, value, GENMASK(7, 5)) #define SET_PWR_MODE_SET_PWR_STATE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x01, value, GENMASK(15, 8)) #define LPS_PG_INFO_LOC(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(23, 16)) #define LPS_PG_DPK_LOC(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(31, 24)) #define LPS_PG_SEC_CAM_EN(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(8)) #define LPS_PG_PATTERN_CAM_EN(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(10)) #define SET_RSSI_INFO_MACID(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(15, 8)) #define SET_RSSI_INFO_RSSI(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(31, 24)) #define SET_RSSI_INFO_STBC(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x01, value, BIT(1)) #define SET_RA_INFO_MACID(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(15, 8)) #define SET_RA_INFO_RATE_ID(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(20, 16)) #define SET_RA_INFO_INIT_RA_LVL(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(22, 21)) #define SET_RA_INFO_SGI_EN(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(23)) #define SET_RA_INFO_BW_MODE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(25, 24)) #define SET_RA_INFO_LDPC(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(26)) #define SET_RA_INFO_NO_UPDATE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(27)) #define SET_RA_INFO_VHT_EN(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(29, 28)) #define SET_RA_INFO_DIS_PT(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(30)) #define SET_RA_INFO_RA_MASK0(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x01, value, GENMASK(7, 0)) #define SET_RA_INFO_RA_MASK1(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x01, value, GENMASK(15, 8)) #define SET_RA_INFO_RA_MASK2(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x01, value, GENMASK(23, 16)) #define SET_RA_INFO_RA_MASK3(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x01, value, GENMASK(31, 24)) #define SET_QUERY_BT_INFO(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(8)) #define SET_WL_CH_INFO_LINK(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(15, 8)) #define SET_WL_CH_INFO_CHNL(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(23, 16)) #define SET_WL_CH_INFO_BW(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(31, 24)) #define SET_BT_MP_INFO_SEQ(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(15, 12)) #define SET_BT_MP_INFO_OP_CODE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(23, 16)) #define SET_BT_MP_INFO_PARA1(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(31, 24)) #define SET_BT_MP_INFO_PARA2(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x01, value, GENMASK(7, 0)) #define SET_BT_MP_INFO_PARA3(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x01, value, GENMASK(15, 8)) #define SET_BT_TX_POWER_INDEX(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(15, 8)) #define SET_IGNORE_WLAN_ACTION_EN(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(8)) #define SET_COEX_TDMA_TYPE_PARA1(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(15, 8)) #define SET_COEX_TDMA_TYPE_PARA2(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(23, 16)) #define SET_COEX_TDMA_TYPE_PARA3(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(31, 24)) #define SET_COEX_TDMA_TYPE_PARA4(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x01, value, GENMASK(7, 0)) #define SET_COEX_TDMA_TYPE_PARA5(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x01, value, GENMASK(15, 8)) #define SET_BT_WIFI_CONTROL_OP_CODE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(15, 8)) #define SET_BT_WIFI_CONTROL_DATA1(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(23, 16)) #define SET_BT_WIFI_CONTROL_DATA2(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(31, 24)) #define SET_BT_WIFI_CONTROL_DATA3(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x01, value, GENMASK(7, 0)) #define SET_BT_WIFI_CONTROL_DATA4(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x01, value, GENMASK(15, 8)) #define SET_BT_WIFI_CONTROL_DATA5(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x01, value, GENMASK(23, 16)) #define SET_COEX_QUERY_HID_INFO_SUBID(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(15, 8)) #define SET_COEX_QUERY_HID_INFO_DATA1(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(23, 16)) #define SET_KEEP_ALIVE_ENABLE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(8)) #define SET_KEEP_ALIVE_ADOPT(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(9)) #define SET_KEEP_ALIVE_PKT_TYPE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(10)) #define SET_KEEP_ALIVE_CHECK_PERIOD(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(23, 16)) #define SET_DISCONNECT_DECISION_ENABLE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(8)) #define SET_DISCONNECT_DECISION_ADOPT(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(9)) #define SET_DISCONNECT_DECISION_CHECK_PERIOD(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(23, 16)) #define SET_DISCONNECT_DECISION_TRY_PKT_NUM(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(31, 24)) #define SET_WOWLAN_FUNC_ENABLE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(8)) #define SET_WOWLAN_PATTERN_MATCH_ENABLE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(9)) #define SET_WOWLAN_MAGIC_PKT_ENABLE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(10)) #define SET_WOWLAN_UNICAST_PKT_ENABLE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(11)) #define SET_WOWLAN_REKEY_WAKEUP_ENABLE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(14)) #define SET_WOWLAN_DEAUTH_WAKEUP_ENABLE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(15)) #define SET_REMOTE_WAKECTRL_ENABLE(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(8)) #define SET_REMOTE_WAKE_CTRL_NLO_OFFLOAD_EN(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(12)) #define SET_AOAC_GLOBAL_INFO_PAIRWISE_ENC_ALG(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(15, 8)) #define SET_AOAC_GLOBAL_INFO_GROUP_ENC_ALG(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(23, 16)) #define SET_NLO_FUN_EN(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(8)) #define SET_NLO_PS_32K(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(9)) #define SET_NLO_IGNORE_SECURITY(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(10)) #define SET_NLO_LOC_NLO_INFO(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(23, 16)) +#define SET_RECOVER_BT_DEV_EN(h2c_pkt, value) \ + le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(8)) + #define GET_FW_DUMP_LEN(_header) \ le32_get_bits(*((__le32 *)(_header) + 0x00), GENMASK(15, 0)) #define GET_FW_DUMP_SEQ(_header) \ le32_get_bits(*((__le32 *)(_header) + 0x00), GENMASK(22, 16)) #define GET_FW_DUMP_MORE(_header) \ le32_get_bits(*((__le32 *)(_header) + 0x00), BIT(23)) #define GET_FW_DUMP_VERSION(_header) \ le32_get_bits(*((__le32 *)(_header) + 0x00), GENMASK(31, 24)) #define GET_FW_DUMP_TLV_TYPE(_header) \ le32_get_bits(*((__le32 *)(_header) + 0x01), GENMASK(15, 0)) #define GET_FW_DUMP_TLV_LEN(_header) \ le32_get_bits(*((__le32 *)(_header) + 0x01), GENMASK(31, 16)) #define GET_FW_DUMP_TLV_VAL(_header) \ le32_get_bits(*((__le32 *)(_header) + 0x02), GENMASK(31, 0)) #define RFK_SET_INFORM_START(h2c_pkt, value) \ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(8)) static inline struct rtw_c2h_cmd *get_c2h_from_skb(struct sk_buff *skb) { u32 pkt_offset; pkt_offset = *((u32 *)skb->cb); return (struct rtw_c2h_cmd *)(skb->data + pkt_offset); } static inline bool rtw_fw_feature_check(struct rtw_fw_state *fw, enum rtw_fw_feature feature) { return !!(fw->feature & feature); } +static inline bool rtw_fw_feature_ext_check(struct rtw_fw_state *fw, + enum rtw_fw_feature_ext feature) +{ + return !!(fw->feature_ext & feature); +} + void rtw_fw_c2h_cmd_rx_irqsafe(struct rtw_dev *rtwdev, u32 pkt_offset, struct sk_buff *skb); void rtw_fw_c2h_cmd_handle(struct rtw_dev *rtwdev, struct sk_buff *skb); void rtw_fw_send_general_info(struct rtw_dev *rtwdev); void rtw_fw_send_phydm_info(struct rtw_dev *rtwdev); +void rtw_fw_default_port(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif); void rtw_fw_do_iqk(struct rtw_dev *rtwdev, struct rtw_iqk_para *para); void rtw_fw_inform_rfk_status(struct rtw_dev *rtwdev, bool start); void rtw_fw_set_pwr_mode(struct rtw_dev *rtwdev); void rtw_fw_set_pg_info(struct rtw_dev *rtwdev); void rtw_fw_query_bt_info(struct rtw_dev *rtwdev); void rtw_fw_wl_ch_info(struct rtw_dev *rtwdev, u8 link, u8 ch, u8 bw); void rtw_fw_query_bt_mp_info(struct rtw_dev *rtwdev, struct rtw_coex_info_req *req); void rtw_fw_force_bt_tx_power(struct rtw_dev *rtwdev, u8 bt_pwr_dec_lvl); void rtw_fw_bt_ignore_wlan_action(struct rtw_dev *rtwdev, bool enable); void rtw_fw_coex_tdma_type(struct rtw_dev *rtwdev, u8 para1, u8 para2, u8 para3, u8 para4, u8 para5); void rtw_fw_coex_query_hid_info(struct rtw_dev *rtwdev, u8 sub_id, u8 data); void rtw_fw_bt_wifi_control(struct rtw_dev *rtwdev, u8 op_code, u8 *data); void rtw_fw_send_rssi_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si); void rtw_fw_send_ra_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si, bool reset_ra_mask); void rtw_fw_media_status_report(struct rtw_dev *rtwdev, u8 mac_id, bool conn); void rtw_fw_update_wl_phy_info(struct rtw_dev *rtwdev); void rtw_fw_beacon_filter_config(struct rtw_dev *rtwdev, bool connect, struct ieee80211_vif *vif); int rtw_fw_write_data_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr, u8 *buf, u32 size); void rtw_remove_rsvd_page(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif); void rtw_add_rsvd_page_bcn(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif); void rtw_add_rsvd_page_pno(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif); void rtw_add_rsvd_page_sta(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif); int rtw_fw_download_rsvd_page(struct rtw_dev *rtwdev); void rtw_fw_update_beacon_work(struct work_struct *work); void rtw_send_rsvd_page_h2c(struct rtw_dev *rtwdev); int rtw_dump_drv_rsvd_page(struct rtw_dev *rtwdev, u32 offset, u32 size, u32 *buf); void rtw_fw_set_remote_wake_ctrl_cmd(struct rtw_dev *rtwdev, bool enable); void rtw_fw_set_wowlan_ctrl_cmd(struct rtw_dev *rtwdev, bool enable); void rtw_fw_set_keep_alive_cmd(struct rtw_dev *rtwdev, bool enable); void rtw_fw_set_disconnect_decision_cmd(struct rtw_dev *rtwdev, bool enable); void rtw_fw_set_aoac_global_info_cmd(struct rtw_dev *rtwdev, u8 pairwise_key_enc, u8 group_key_enc); void rtw_fw_set_nlo_info(struct rtw_dev *rtwdev, bool enable); +void rtw_fw_set_recover_bt_device(struct rtw_dev *rtwdev); void rtw_fw_update_pkt_probe_req(struct rtw_dev *rtwdev, struct cfg80211_ssid *ssid); void rtw_fw_channel_switch(struct rtw_dev *rtwdev, bool enable); void rtw_fw_h2c_cmd_dbg(struct rtw_dev *rtwdev, u8 *h2c); void rtw_fw_c2h_cmd_isr(struct rtw_dev *rtwdev); int rtw_fw_dump_fifo(struct rtw_dev *rtwdev, u8 fifo_sel, u32 addr, u32 size, u32 *buffer); void rtw_fw_scan_notify(struct rtw_dev *rtwdev, bool start); void rtw_fw_adaptivity(struct rtw_dev *rtwdev); -void rtw_store_op_chan(struct rtw_dev *rtwdev); +void rtw_store_op_chan(struct rtw_dev *rtwdev, bool backup); +void rtw_clear_op_chan(struct rtw_dev *rtwdev); void rtw_hw_scan_start(struct rtw_dev *rtwdev, struct ieee80211_vif *vif, struct ieee80211_scan_request *req); void rtw_hw_scan_complete(struct rtw_dev *rtwdev, struct ieee80211_vif *vif, bool aborted); int rtw_hw_scan_offload(struct rtw_dev *rtwdev, struct ieee80211_vif *vif, bool enable); void rtw_hw_scan_status_report(struct rtw_dev *rtwdev, struct sk_buff *skb); void rtw_hw_scan_chan_switch(struct rtw_dev *rtwdev, struct sk_buff *skb); -void rtw_hw_scan_abort(struct rtw_dev *rtwdev, struct ieee80211_vif *vif); +void rtw_hw_scan_abort(struct rtw_dev *rtwdev); #endif diff --git a/sys/contrib/dev/rtw88/hci.h b/sys/contrib/dev/rtw88/hci.h index 4c6fc6fb3f83..830d7532f2a3 100644 --- a/sys/contrib/dev/rtw88/hci.h +++ b/sys/contrib/dev/rtw88/hci.h @@ -1,277 +1,274 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* Copyright(c) 2018-2019 Realtek Corporation */ #ifndef __RTW_HCI_H__ #define __RTW_HCI_H__ /* ops for PCI, USB and SDIO */ struct rtw_hci_ops { int (*tx_write)(struct rtw_dev *rtwdev, struct rtw_tx_pkt_info *pkt_info, struct sk_buff *skb); void (*tx_kick_off)(struct rtw_dev *rtwdev); void (*flush_queues)(struct rtw_dev *rtwdev, u32 queues, bool drop); int (*setup)(struct rtw_dev *rtwdev); int (*start)(struct rtw_dev *rtwdev); void (*stop)(struct rtw_dev *rtwdev); void (*deep_ps)(struct rtw_dev *rtwdev, bool enter); void (*link_ps)(struct rtw_dev *rtwdev, bool enter); void (*interface_cfg)(struct rtw_dev *rtwdev); int (*write_data_rsvd_page)(struct rtw_dev *rtwdev, u8 *buf, u32 size); int (*write_data_h2c)(struct rtw_dev *rtwdev, u8 *buf, u32 size); u8 (*read8)(struct rtw_dev *rtwdev, u32 addr); u16 (*read16)(struct rtw_dev *rtwdev, u32 addr); u32 (*read32)(struct rtw_dev *rtwdev, u32 addr); void (*write8)(struct rtw_dev *rtwdev, u32 addr, u8 val); void (*write16)(struct rtw_dev *rtwdev, u32 addr, u16 val); void (*write32)(struct rtw_dev *rtwdev, u32 addr, u32 val); }; static inline int rtw_hci_tx_write(struct rtw_dev *rtwdev, struct rtw_tx_pkt_info *pkt_info, struct sk_buff *skb) { return rtwdev->hci.ops->tx_write(rtwdev, pkt_info, skb); } static inline void rtw_hci_tx_kick_off(struct rtw_dev *rtwdev) { return rtwdev->hci.ops->tx_kick_off(rtwdev); } static inline int rtw_hci_setup(struct rtw_dev *rtwdev) { return rtwdev->hci.ops->setup(rtwdev); } static inline int rtw_hci_start(struct rtw_dev *rtwdev) { return rtwdev->hci.ops->start(rtwdev); } static inline void rtw_hci_stop(struct rtw_dev *rtwdev) { rtwdev->hci.ops->stop(rtwdev); } static inline void rtw_hci_deep_ps(struct rtw_dev *rtwdev, bool enter) { rtwdev->hci.ops->deep_ps(rtwdev, enter); } static inline void rtw_hci_link_ps(struct rtw_dev *rtwdev, bool enter) { rtwdev->hci.ops->link_ps(rtwdev, enter); } static inline void rtw_hci_interface_cfg(struct rtw_dev *rtwdev) { rtwdev->hci.ops->interface_cfg(rtwdev); } static inline int rtw_hci_write_data_rsvd_page(struct rtw_dev *rtwdev, u8 *buf, u32 size) { return rtwdev->hci.ops->write_data_rsvd_page(rtwdev, buf, size); } static inline int rtw_hci_write_data_h2c(struct rtw_dev *rtwdev, u8 *buf, u32 size) { return rtwdev->hci.ops->write_data_h2c(rtwdev, buf, size); } static inline u8 rtw_read8(struct rtw_dev *rtwdev, u32 addr) { return rtwdev->hci.ops->read8(rtwdev, addr); } static inline u16 rtw_read16(struct rtw_dev *rtwdev, u32 addr) { return rtwdev->hci.ops->read16(rtwdev, addr); } static inline u32 rtw_read32(struct rtw_dev *rtwdev, u32 addr) { return rtwdev->hci.ops->read32(rtwdev, addr); } static inline void rtw_write8(struct rtw_dev *rtwdev, u32 addr, u8 val) { rtwdev->hci.ops->write8(rtwdev, addr, val); } static inline void rtw_write16(struct rtw_dev *rtwdev, u32 addr, u16 val) { rtwdev->hci.ops->write16(rtwdev, addr, val); } static inline void rtw_write32(struct rtw_dev *rtwdev, u32 addr, u32 val) { rtwdev->hci.ops->write32(rtwdev, addr, val); } static inline void rtw_write8_set(struct rtw_dev *rtwdev, u32 addr, u8 bit) { u8 val; val = rtw_read8(rtwdev, addr); rtw_write8(rtwdev, addr, val | bit); } static inline void rtw_write16_set(struct rtw_dev *rtwdev, u32 addr, u16 bit) { u16 val; val = rtw_read16(rtwdev, addr); rtw_write16(rtwdev, addr, val | bit); } static inline void rtw_write32_set(struct rtw_dev *rtwdev, u32 addr, u32 bit) { u32 val; val = rtw_read32(rtwdev, addr); rtw_write32(rtwdev, addr, val | bit); } static inline void rtw_write8_clr(struct rtw_dev *rtwdev, u32 addr, u8 bit) { u8 val; val = rtw_read8(rtwdev, addr); rtw_write8(rtwdev, addr, val & ~bit); } static inline void rtw_write16_clr(struct rtw_dev *rtwdev, u32 addr, u16 bit) { u16 val; val = rtw_read16(rtwdev, addr); rtw_write16(rtwdev, addr, val & ~bit); } static inline void rtw_write32_clr(struct rtw_dev *rtwdev, u32 addr, u32 bit) { u32 val; val = rtw_read32(rtwdev, addr); rtw_write32(rtwdev, addr, val & ~bit); } static inline u32 rtw_read_rf(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, u32 addr, u32 mask) { - unsigned long flags; u32 val; - spin_lock_irqsave(&rtwdev->rf_lock, flags); + lockdep_assert_held(&rtwdev->mutex); + val = rtwdev->chip->ops->read_rf(rtwdev, rf_path, addr, mask); - spin_unlock_irqrestore(&rtwdev->rf_lock, flags); return val; } static inline void rtw_write_rf(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, u32 addr, u32 mask, u32 data) { - unsigned long flags; + lockdep_assert_held(&rtwdev->mutex); - spin_lock_irqsave(&rtwdev->rf_lock, flags); rtwdev->chip->ops->write_rf(rtwdev, rf_path, addr, mask, data); - spin_unlock_irqrestore(&rtwdev->rf_lock, flags); } static inline u32 rtw_read32_mask(struct rtw_dev *rtwdev, u32 addr, u32 mask) { u32 shift = __ffs(mask); u32 orig; u32 ret; orig = rtw_read32(rtwdev, addr); ret = (orig & mask) >> shift; return ret; } static inline u16 rtw_read16_mask(struct rtw_dev *rtwdev, u32 addr, u32 mask) { u32 shift = __ffs(mask); u32 orig; u32 ret; orig = rtw_read16(rtwdev, addr); ret = (orig & mask) >> shift; return ret; } static inline u8 rtw_read8_mask(struct rtw_dev *rtwdev, u32 addr, u32 mask) { u32 shift = __ffs(mask); u32 orig; u32 ret; orig = rtw_read8(rtwdev, addr); ret = (orig & mask) >> shift; return ret; } static inline void rtw_write32_mask(struct rtw_dev *rtwdev, u32 addr, u32 mask, u32 data) { u32 shift = __ffs(mask); u32 orig; u32 set; WARN(addr & 0x3, "should be 4-byte aligned, addr = 0x%08x\n", addr); orig = rtw_read32(rtwdev, addr); set = (orig & ~mask) | ((data << shift) & mask); rtw_write32(rtwdev, addr, set); } static inline void rtw_write8_mask(struct rtw_dev *rtwdev, u32 addr, u32 mask, u8 data) { u32 shift; u8 orig, set; mask &= 0xff; shift = __ffs(mask); orig = rtw_read8(rtwdev, addr); set = (orig & ~mask) | ((data << shift) & mask); rtw_write8(rtwdev, addr, set); } static inline enum rtw_hci_type rtw_hci_type(struct rtw_dev *rtwdev) { return rtwdev->hci.type; } static inline void rtw_hci_flush_queues(struct rtw_dev *rtwdev, u32 queues, bool drop) { if (rtwdev->hci.ops->flush_queues) rtwdev->hci.ops->flush_queues(rtwdev, queues, drop); } static inline void rtw_hci_flush_all_queues(struct rtw_dev *rtwdev, bool drop) { if (rtwdev->hci.ops->flush_queues) rtwdev->hci.ops->flush_queues(rtwdev, BIT(rtwdev->hw->queues) - 1, drop); } #endif diff --git a/sys/contrib/dev/rtw88/mac.c b/sys/contrib/dev/rtw88/mac.c index caf2603da2d6..298663b03580 100644 --- a/sys/contrib/dev/rtw88/mac.c +++ b/sys/contrib/dev/rtw88/mac.c @@ -1,1298 +1,1379 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* Copyright(c) 2018-2019 Realtek Corporation */ #include "main.h" #include "mac.h" #include "reg.h" #include "fw.h" #include "debug.h" +#include "sdio.h" void rtw_set_channel_mac(struct rtw_dev *rtwdev, u8 channel, u8 bw, u8 primary_ch_idx) { u8 txsc40 = 0, txsc20 = 0; u32 value32; u8 value8; txsc20 = primary_ch_idx; if (bw == RTW_CHANNEL_WIDTH_80) { if (txsc20 == RTW_SC_20_UPPER || txsc20 == RTW_SC_20_UPMOST) txsc40 = RTW_SC_40_UPPER; else txsc40 = RTW_SC_40_LOWER; } rtw_write8(rtwdev, REG_DATA_SC, BIT_TXSC_20M(txsc20) | BIT_TXSC_40M(txsc40)); value32 = rtw_read32(rtwdev, REG_WMAC_TRXPTCL_CTL); value32 &= ~BIT_RFMOD; switch (bw) { case RTW_CHANNEL_WIDTH_80: value32 |= BIT_RFMOD_80M; break; case RTW_CHANNEL_WIDTH_40: value32 |= BIT_RFMOD_40M; break; case RTW_CHANNEL_WIDTH_20: default: break; } rtw_write32(rtwdev, REG_WMAC_TRXPTCL_CTL, value32); if (rtw_chip_wcpu_11n(rtwdev)) return; value32 = rtw_read32(rtwdev, REG_AFE_CTRL1) & ~(BIT_MAC_CLK_SEL); value32 |= (MAC_CLK_HW_DEF_80M << BIT_SHIFT_MAC_CLK_SEL); rtw_write32(rtwdev, REG_AFE_CTRL1, value32); rtw_write8(rtwdev, REG_USTIME_TSF, MAC_CLK_SPEED); rtw_write8(rtwdev, REG_USTIME_EDCA, MAC_CLK_SPEED); value8 = rtw_read8(rtwdev, REG_CCK_CHECK); value8 = value8 & ~BIT_CHECK_CCK_EN; if (IS_CH_5G_BAND(channel)) value8 |= BIT_CHECK_CCK_EN; rtw_write8(rtwdev, REG_CCK_CHECK, value8); } EXPORT_SYMBOL(rtw_set_channel_mac); static int rtw_mac_pre_system_cfg(struct rtw_dev *rtwdev) { + unsigned int retry; u32 value32; u8 value8; rtw_write8(rtwdev, REG_RSV_CTRL, 0); if (rtw_chip_wcpu_11n(rtwdev)) { if (rtw_read32(rtwdev, REG_SYS_CFG1) & BIT_LDO) rtw_write8(rtwdev, REG_LDO_SWR_CTRL, LDO_SEL); else rtw_write8(rtwdev, REG_LDO_SWR_CTRL, SPS_SEL); return 0; } switch (rtw_hci_type(rtwdev)) { case RTW_HCI_TYPE_PCIE: rtw_write32_set(rtwdev, REG_HCI_OPT_CTRL, BIT_USB_SUS_DIS); break; + case RTW_HCI_TYPE_SDIO: + rtw_write8_clr(rtwdev, REG_SDIO_HSUS_CTRL, BIT_HCI_SUS_REQ); + + for (retry = 0; retry < RTW_PWR_POLLING_CNT; retry++) { + if (rtw_read8(rtwdev, REG_SDIO_HSUS_CTRL) & BIT_HCI_RESUME_RDY) + break; + + usleep_range(10, 50); + } + + if (retry == RTW_PWR_POLLING_CNT) { + rtw_err(rtwdev, "failed to poll REG_SDIO_HSUS_CTRL[1]"); + return -ETIMEDOUT; + } + + if (rtw_sdio_is_sdio30_supported(rtwdev)) + rtw_write8_set(rtwdev, REG_HCI_OPT_CTRL + 2, + BIT_SDIO_PAD_E5 >> 16); + else + rtw_write8_clr(rtwdev, REG_HCI_OPT_CTRL + 2, + BIT_SDIO_PAD_E5 >> 16); + break; case RTW_HCI_TYPE_USB: break; default: return -EINVAL; } /* config PIN Mux */ value32 = rtw_read32(rtwdev, REG_PAD_CTRL1); value32 |= BIT_PAPE_WLBT_SEL | BIT_LNAON_WLBT_SEL; rtw_write32(rtwdev, REG_PAD_CTRL1, value32); value32 = rtw_read32(rtwdev, REG_LED_CFG); value32 &= ~(BIT_PAPE_SEL_EN | BIT_LNAON_SEL_EN); rtw_write32(rtwdev, REG_LED_CFG, value32); value32 = rtw_read32(rtwdev, REG_GPIO_MUXCFG); value32 |= BIT_WLRFE_4_5_EN; rtw_write32(rtwdev, REG_GPIO_MUXCFG, value32); /* disable BB/RF */ value8 = rtw_read8(rtwdev, REG_SYS_FUNC_EN); value8 &= ~(BIT_FEN_BB_RSTB | BIT_FEN_BB_GLB_RST); rtw_write8(rtwdev, REG_SYS_FUNC_EN, value8); value8 = rtw_read8(rtwdev, REG_RF_CTRL); value8 &= ~(BIT_RF_SDM_RSTB | BIT_RF_RSTB | BIT_RF_EN); rtw_write8(rtwdev, REG_RF_CTRL, value8); value32 = rtw_read32(rtwdev, REG_WLRF1); value32 &= ~BIT_WLRF1_BBRF_EN; rtw_write32(rtwdev, REG_WLRF1, value32); return 0; } static bool do_pwr_poll_cmd(struct rtw_dev *rtwdev, u32 addr, u32 mask, u32 target) { u32 val; target &= mask; return read_poll_timeout_atomic(rtw_read8, val, (val & mask) == target, 50, 50 * RTW_PWR_POLLING_CNT, false, rtwdev, addr) == 0; } static int rtw_pwr_cmd_polling(struct rtw_dev *rtwdev, const struct rtw_pwr_seq_cmd *cmd) { u8 value; u32 offset; if (cmd->base == RTW_PWR_ADDR_SDIO) offset = cmd->offset | SDIO_LOCAL_OFFSET; else offset = cmd->offset; if (do_pwr_poll_cmd(rtwdev, offset, cmd->mask, cmd->value)) return 0; if (rtw_hci_type(rtwdev) != RTW_HCI_TYPE_PCIE) goto err; /* if PCIE, toggle BIT_PFM_WOWL and try again */ value = rtw_read8(rtwdev, REG_SYS_PW_CTRL); if (rtwdev->chip->id == RTW_CHIP_TYPE_8723D) rtw_write8(rtwdev, REG_SYS_PW_CTRL, value & ~BIT_PFM_WOWL); rtw_write8(rtwdev, REG_SYS_PW_CTRL, value | BIT_PFM_WOWL); rtw_write8(rtwdev, REG_SYS_PW_CTRL, value & ~BIT_PFM_WOWL); if (rtwdev->chip->id == RTW_CHIP_TYPE_8723D) rtw_write8(rtwdev, REG_SYS_PW_CTRL, value | BIT_PFM_WOWL); if (do_pwr_poll_cmd(rtwdev, offset, cmd->mask, cmd->value)) return 0; err: rtw_err(rtwdev, "failed to poll offset=0x%x mask=0x%x value=0x%x\n", offset, cmd->mask, cmd->value); return -EBUSY; } static int rtw_sub_pwr_seq_parser(struct rtw_dev *rtwdev, u8 intf_mask, u8 cut_mask, const struct rtw_pwr_seq_cmd *cmd) { const struct rtw_pwr_seq_cmd *cur_cmd; u32 offset; u8 value; for (cur_cmd = cmd; cur_cmd->cmd != RTW_PWR_CMD_END; cur_cmd++) { if (!(cur_cmd->intf_mask & intf_mask) || !(cur_cmd->cut_mask & cut_mask)) continue; switch (cur_cmd->cmd) { case RTW_PWR_CMD_WRITE: offset = cur_cmd->offset; if (cur_cmd->base == RTW_PWR_ADDR_SDIO) offset |= SDIO_LOCAL_OFFSET; value = rtw_read8(rtwdev, offset); value &= ~cur_cmd->mask; value |= (cur_cmd->value & cur_cmd->mask); rtw_write8(rtwdev, offset, value); break; case RTW_PWR_CMD_POLLING: if (rtw_pwr_cmd_polling(rtwdev, cur_cmd)) return -EBUSY; break; case RTW_PWR_CMD_DELAY: if (cur_cmd->value == RTW_PWR_DELAY_US) udelay(cur_cmd->offset); else mdelay(cur_cmd->offset); break; case RTW_PWR_CMD_READ: break; default: return -EINVAL; } } return 0; } static int rtw_pwr_seq_parser(struct rtw_dev *rtwdev, const struct rtw_pwr_seq_cmd **cmd_seq) { u8 cut_mask; u8 intf_mask; u8 cut; u32 idx = 0; const struct rtw_pwr_seq_cmd *cmd; int ret; cut = rtwdev->hal.cut_version; cut_mask = cut_version_to_mask(cut); switch (rtw_hci_type(rtwdev)) { case RTW_HCI_TYPE_PCIE: - intf_mask = BIT(2); + intf_mask = RTW_PWR_INTF_PCI_MSK; break; case RTW_HCI_TYPE_USB: - intf_mask = BIT(1); + intf_mask = RTW_PWR_INTF_USB_MSK; + break; + case RTW_HCI_TYPE_SDIO: + intf_mask = RTW_PWR_INTF_SDIO_MSK; break; default: return -EINVAL; } do { cmd = cmd_seq[idx]; if (!cmd) break; ret = rtw_sub_pwr_seq_parser(rtwdev, intf_mask, cut_mask, cmd); if (ret) - return -EBUSY; + return ret; idx++; } while (1); return 0; } static int rtw_mac_power_switch(struct rtw_dev *rtwdev, bool pwr_on) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; const struct rtw_pwr_seq_cmd **pwr_seq; + u32 imr = 0; u8 rpwm; bool cur_pwr; + int ret; if (rtw_chip_wcpu_11ac(rtwdev)) { rpwm = rtw_read8(rtwdev, rtwdev->hci.rpwm_addr); /* Check FW still exist or not */ if (rtw_read16(rtwdev, REG_MCUFW_CTRL) == 0xC078) { rpwm = (rpwm ^ BIT_RPWM_TOGGLE) & BIT_RPWM_TOGGLE; rtw_write8(rtwdev, rtwdev->hci.rpwm_addr, rpwm); } } if (rtw_read8(rtwdev, REG_CR) == 0xea) cur_pwr = false; else if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB && (rtw_read8(rtwdev, REG_SYS_STATUS1 + 1) & BIT(0))) cur_pwr = false; else cur_pwr = true; if (pwr_on == cur_pwr) return -EALREADY; + if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_SDIO) { + imr = rtw_read32(rtwdev, REG_SDIO_HIMR); + rtw_write32(rtwdev, REG_SDIO_HIMR, 0); + } + + if (!pwr_on) + clear_bit(RTW_FLAG_POWERON, rtwdev->flags); + pwr_seq = pwr_on ? chip->pwr_on_seq : chip->pwr_off_seq; - if (rtw_pwr_seq_parser(rtwdev, pwr_seq)) - return -EINVAL; + ret = rtw_pwr_seq_parser(rtwdev, pwr_seq); - return 0; + if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_SDIO) + rtw_write32(rtwdev, REG_SDIO_HIMR, imr); + + if (!ret && pwr_on) + set_bit(RTW_FLAG_POWERON, rtwdev->flags); + + return ret; } static int __rtw_mac_init_system_cfg(struct rtw_dev *rtwdev) { u8 sys_func_en = rtwdev->chip->sys_func_en; u8 value8; u32 value, tmp; value = rtw_read32(rtwdev, REG_CPU_DMEM_CON); value |= BIT_WL_PLATFORM_RST | BIT_DDMA_EN; rtw_write32(rtwdev, REG_CPU_DMEM_CON, value); rtw_write8_set(rtwdev, REG_SYS_FUNC_EN + 1, sys_func_en); value8 = (rtw_read8(rtwdev, REG_CR_EXT + 3) & 0xF0) | 0x0C; rtw_write8(rtwdev, REG_CR_EXT + 3, value8); /* disable boot-from-flash for driver's DL FW */ tmp = rtw_read32(rtwdev, REG_MCUFW_CTRL); if (tmp & BIT_BOOT_FSPI_EN) { rtw_write32(rtwdev, REG_MCUFW_CTRL, tmp & (~BIT_BOOT_FSPI_EN)); value = rtw_read32(rtwdev, REG_GPIO_MUXCFG) & (~BIT_FSPI_EN); rtw_write32(rtwdev, REG_GPIO_MUXCFG, value); } return 0; } static int __rtw_mac_init_system_cfg_legacy(struct rtw_dev *rtwdev) { rtw_write8(rtwdev, REG_CR, 0xff); mdelay(2); rtw_write8(rtwdev, REG_HWSEQ_CTRL, 0x7f); mdelay(2); rtw_write8_set(rtwdev, REG_SYS_CLKR, BIT_WAKEPAD_EN); rtw_write16_clr(rtwdev, REG_GPIO_MUXCFG, BIT_EN_SIC); rtw_write16(rtwdev, REG_CR, 0x2ff); return 0; } static int rtw_mac_init_system_cfg(struct rtw_dev *rtwdev) { if (rtw_chip_wcpu_11n(rtwdev)) return __rtw_mac_init_system_cfg_legacy(rtwdev); return __rtw_mac_init_system_cfg(rtwdev); } int rtw_mac_power_on(struct rtw_dev *rtwdev) { int ret = 0; ret = rtw_mac_pre_system_cfg(rtwdev); if (ret) goto err; ret = rtw_mac_power_switch(rtwdev, true); if (ret == -EALREADY) { rtw_mac_power_switch(rtwdev, false); + + ret = rtw_mac_pre_system_cfg(rtwdev); + if (ret) + goto err; + ret = rtw_mac_power_switch(rtwdev, true); if (ret) goto err; } else if (ret) { goto err; } ret = rtw_mac_init_system_cfg(rtwdev); if (ret) goto err; return 0; err: rtw_err(rtwdev, "mac power on failed"); return ret; } void rtw_mac_power_off(struct rtw_dev *rtwdev) { rtw_mac_power_switch(rtwdev, false); } static bool check_firmware_size(const u8 *data, u32 size) { const struct rtw_fw_hdr *fw_hdr = (const struct rtw_fw_hdr *)data; u32 dmem_size; u32 imem_size; u32 emem_size; u32 real_size; dmem_size = le32_to_cpu(fw_hdr->dmem_size); imem_size = le32_to_cpu(fw_hdr->imem_size); emem_size = (fw_hdr->mem_usage & BIT(4)) ? le32_to_cpu(fw_hdr->emem_size) : 0; dmem_size += FW_HDR_CHKSUM_SIZE; imem_size += FW_HDR_CHKSUM_SIZE; emem_size += emem_size ? FW_HDR_CHKSUM_SIZE : 0; real_size = FW_HDR_SIZE + dmem_size + imem_size + emem_size; if (real_size != size) return false; return true; } static void wlan_cpu_enable(struct rtw_dev *rtwdev, bool enable) { if (enable) { /* cpu io interface enable */ rtw_write8_set(rtwdev, REG_RSV_CTRL + 1, BIT_WLMCU_IOIF); /* cpu enable */ rtw_write8_set(rtwdev, REG_SYS_FUNC_EN + 1, BIT_FEN_CPUEN); } else { /* cpu io interface disable */ rtw_write8_clr(rtwdev, REG_SYS_FUNC_EN + 1, BIT_FEN_CPUEN); /* cpu disable */ rtw_write8_clr(rtwdev, REG_RSV_CTRL + 1, BIT_WLMCU_IOIF); } } #define DLFW_RESTORE_REG_NUM 6 static void download_firmware_reg_backup(struct rtw_dev *rtwdev, struct rtw_backup_info *bckp) { u8 tmp; u8 bckp_idx = 0; /* set HIQ to hi priority */ bckp[bckp_idx].len = 1; bckp[bckp_idx].reg = REG_TXDMA_PQ_MAP + 1; bckp[bckp_idx].val = rtw_read8(rtwdev, REG_TXDMA_PQ_MAP + 1); bckp_idx++; tmp = RTW_DMA_MAPPING_HIGH << 6; rtw_write8(rtwdev, REG_TXDMA_PQ_MAP + 1, tmp); /* DLFW only use HIQ, map HIQ to hi priority */ bckp[bckp_idx].len = 1; bckp[bckp_idx].reg = REG_CR; bckp[bckp_idx].val = rtw_read8(rtwdev, REG_CR); bckp_idx++; bckp[bckp_idx].len = 4; bckp[bckp_idx].reg = REG_H2CQ_CSR; bckp[bckp_idx].val = BIT_H2CQ_FULL; bckp_idx++; tmp = BIT_HCI_TXDMA_EN | BIT_TXDMA_EN; rtw_write8(rtwdev, REG_CR, tmp); rtw_write32(rtwdev, REG_H2CQ_CSR, BIT_H2CQ_FULL); /* Config hi priority queue and public priority queue page number */ bckp[bckp_idx].len = 2; bckp[bckp_idx].reg = REG_FIFOPAGE_INFO_1; bckp[bckp_idx].val = rtw_read16(rtwdev, REG_FIFOPAGE_INFO_1); bckp_idx++; bckp[bckp_idx].len = 4; bckp[bckp_idx].reg = REG_RQPN_CTRL_2; bckp[bckp_idx].val = rtw_read32(rtwdev, REG_RQPN_CTRL_2) | BIT_LD_RQPN; bckp_idx++; rtw_write16(rtwdev, REG_FIFOPAGE_INFO_1, 0x200); rtw_write32(rtwdev, REG_RQPN_CTRL_2, bckp[bckp_idx - 1].val); + if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_SDIO) + rtw_read32(rtwdev, REG_SDIO_FREE_TXPG); + /* Disable beacon related functions */ tmp = rtw_read8(rtwdev, REG_BCN_CTRL); bckp[bckp_idx].len = 1; bckp[bckp_idx].reg = REG_BCN_CTRL; bckp[bckp_idx].val = tmp; bckp_idx++; tmp = (u8)((tmp & (~BIT_EN_BCN_FUNCTION)) | BIT_DIS_TSF_UDT); rtw_write8(rtwdev, REG_BCN_CTRL, tmp); WARN(bckp_idx != DLFW_RESTORE_REG_NUM, "wrong backup number\n"); } static void download_firmware_reset_platform(struct rtw_dev *rtwdev) { rtw_write8_clr(rtwdev, REG_CPU_DMEM_CON + 2, BIT_WL_PLATFORM_RST >> 16); rtw_write8_clr(rtwdev, REG_SYS_CLK_CTRL + 1, BIT_CPU_CLK_EN >> 8); rtw_write8_set(rtwdev, REG_CPU_DMEM_CON + 2, BIT_WL_PLATFORM_RST >> 16); rtw_write8_set(rtwdev, REG_SYS_CLK_CTRL + 1, BIT_CPU_CLK_EN >> 8); } static void download_firmware_reg_restore(struct rtw_dev *rtwdev, struct rtw_backup_info *bckp, u8 bckp_num) { rtw_restore_reg(rtwdev, bckp, bckp_num); } #define TX_DESC_SIZE 48 static int send_firmware_pkt_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr, const u8 *data, u32 size) { u8 *buf; int ret; buf = kmemdup(data, size, GFP_KERNEL); if (!buf) return -ENOMEM; ret = rtw_fw_write_data_rsvd_page(rtwdev, pg_addr, buf, size); kfree(buf); return ret; } static int send_firmware_pkt(struct rtw_dev *rtwdev, u16 pg_addr, const u8 *data, u32 size) { int ret; if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB && !((size + TX_DESC_SIZE) & (512 - 1))) size += 1; ret = send_firmware_pkt_rsvd_page(rtwdev, pg_addr, data, size); if (ret) rtw_err(rtwdev, "failed to download rsvd page\n"); return ret; } static int iddma_enable(struct rtw_dev *rtwdev, u32 src, u32 dst, u32 ctrl) { rtw_write32(rtwdev, REG_DDMA_CH0SA, src); rtw_write32(rtwdev, REG_DDMA_CH0DA, dst); rtw_write32(rtwdev, REG_DDMA_CH0CTRL, ctrl); if (!check_hw_ready(rtwdev, REG_DDMA_CH0CTRL, BIT_DDMACH0_OWN, 0)) return -EBUSY; return 0; } static int iddma_download_firmware(struct rtw_dev *rtwdev, u32 src, u32 dst, u32 len, u8 first) { u32 ch0_ctrl = BIT_DDMACH0_CHKSUM_EN | BIT_DDMACH0_OWN; if (!check_hw_ready(rtwdev, REG_DDMA_CH0CTRL, BIT_DDMACH0_OWN, 0)) return -EBUSY; ch0_ctrl |= len & BIT_MASK_DDMACH0_DLEN; if (!first) ch0_ctrl |= BIT_DDMACH0_CHKSUM_CONT; if (iddma_enable(rtwdev, src, dst, ch0_ctrl)) return -EBUSY; return 0; } int rtw_ddma_to_fw_fifo(struct rtw_dev *rtwdev, u32 ocp_src, u32 size) { u32 ch0_ctrl = BIT_DDMACH0_OWN | BIT_DDMACH0_DDMA_MODE; if (!check_hw_ready(rtwdev, REG_DDMA_CH0CTRL, BIT_DDMACH0_OWN, 0)) { rtw_dbg(rtwdev, RTW_DBG_FW, "busy to start ddma\n"); return -EBUSY; } ch0_ctrl |= size & BIT_MASK_DDMACH0_DLEN; if (iddma_enable(rtwdev, ocp_src, OCPBASE_RXBUF_FW_88XX, ch0_ctrl)) { rtw_dbg(rtwdev, RTW_DBG_FW, "busy to complete ddma\n"); return -EBUSY; } return 0; } static bool check_fw_checksum(struct rtw_dev *rtwdev, u32 addr) { u8 fw_ctrl; fw_ctrl = rtw_read8(rtwdev, REG_MCUFW_CTRL); if (rtw_read32(rtwdev, REG_DDMA_CH0CTRL) & BIT_DDMACH0_CHKSUM_STS) { if (addr < OCPBASE_DMEM_88XX) { fw_ctrl |= BIT_IMEM_DW_OK; fw_ctrl &= ~BIT_IMEM_CHKSUM_OK; rtw_write8(rtwdev, REG_MCUFW_CTRL, fw_ctrl); } else { fw_ctrl |= BIT_DMEM_DW_OK; fw_ctrl &= ~BIT_DMEM_CHKSUM_OK; rtw_write8(rtwdev, REG_MCUFW_CTRL, fw_ctrl); } rtw_err(rtwdev, "invalid fw checksum\n"); return false; } if (addr < OCPBASE_DMEM_88XX) { fw_ctrl |= (BIT_IMEM_DW_OK | BIT_IMEM_CHKSUM_OK); rtw_write8(rtwdev, REG_MCUFW_CTRL, fw_ctrl); } else { fw_ctrl |= (BIT_DMEM_DW_OK | BIT_DMEM_CHKSUM_OK); rtw_write8(rtwdev, REG_MCUFW_CTRL, fw_ctrl); } return true; } static int download_firmware_to_mem(struct rtw_dev *rtwdev, const u8 *data, u32 src, u32 dst, u32 size) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; u32 desc_size = chip->tx_pkt_desc_sz; u8 first_part; u32 mem_offset; u32 residue_size; u32 pkt_size; u32 max_size = 0x1000; u32 val; int ret; mem_offset = 0; first_part = 1; residue_size = size; val = rtw_read32(rtwdev, REG_DDMA_CH0CTRL); val |= BIT_DDMACH0_RESET_CHKSUM_STS; rtw_write32(rtwdev, REG_DDMA_CH0CTRL, val); while (residue_size) { if (residue_size >= max_size) pkt_size = max_size; else pkt_size = residue_size; ret = send_firmware_pkt(rtwdev, (u16)(src >> 7), data + mem_offset, pkt_size); if (ret) return ret; ret = iddma_download_firmware(rtwdev, OCPBASE_TXBUF_88XX + src + desc_size, dst + mem_offset, pkt_size, first_part); if (ret) return ret; first_part = 0; mem_offset += pkt_size; residue_size -= pkt_size; } if (!check_fw_checksum(rtwdev, dst)) return -EINVAL; return 0; } static int start_download_firmware(struct rtw_dev *rtwdev, const u8 *data, u32 size) { const struct rtw_fw_hdr *fw_hdr = (const struct rtw_fw_hdr *)data; const u8 *cur_fw; u16 val; u32 imem_size; u32 dmem_size; u32 emem_size; u32 addr; int ret; dmem_size = le32_to_cpu(fw_hdr->dmem_size); imem_size = le32_to_cpu(fw_hdr->imem_size); emem_size = (fw_hdr->mem_usage & BIT(4)) ? le32_to_cpu(fw_hdr->emem_size) : 0; dmem_size += FW_HDR_CHKSUM_SIZE; imem_size += FW_HDR_CHKSUM_SIZE; emem_size += emem_size ? FW_HDR_CHKSUM_SIZE : 0; val = (u16)(rtw_read16(rtwdev, REG_MCUFW_CTRL) & 0x3800); val |= BIT_MCUFWDL_EN; rtw_write16(rtwdev, REG_MCUFW_CTRL, val); cur_fw = data + FW_HDR_SIZE; addr = le32_to_cpu(fw_hdr->dmem_addr); addr &= ~BIT(31); ret = download_firmware_to_mem(rtwdev, cur_fw, 0, addr, dmem_size); if (ret) return ret; cur_fw = data + FW_HDR_SIZE + dmem_size; addr = le32_to_cpu(fw_hdr->imem_addr); addr &= ~BIT(31); ret = download_firmware_to_mem(rtwdev, cur_fw, 0, addr, imem_size); if (ret) return ret; if (emem_size) { cur_fw = data + FW_HDR_SIZE + dmem_size + imem_size; addr = le32_to_cpu(fw_hdr->emem_addr); addr &= ~BIT(31); ret = download_firmware_to_mem(rtwdev, cur_fw, 0, addr, emem_size); if (ret) return ret; } return 0; } static int download_firmware_validate(struct rtw_dev *rtwdev) { u32 fw_key; if (!check_hw_ready(rtwdev, REG_MCUFW_CTRL, FW_READY_MASK, FW_READY)) { fw_key = rtw_read32(rtwdev, REG_FW_DBG7) & FW_KEY_MASK; if (fw_key == ILLEGAL_KEY_GROUP) rtw_err(rtwdev, "invalid fw key\n"); return -EINVAL; } return 0; } static void download_firmware_end_flow(struct rtw_dev *rtwdev) { u16 fw_ctrl; rtw_write32(rtwdev, REG_TXDMA_STATUS, BTI_PAGE_OVF); /* Check IMEM & DMEM checksum is OK or not */ fw_ctrl = rtw_read16(rtwdev, REG_MCUFW_CTRL); if ((fw_ctrl & BIT_CHECK_SUM_OK) != BIT_CHECK_SUM_OK) return; fw_ctrl = (fw_ctrl | BIT_FW_DW_RDY) & ~BIT_MCUFWDL_EN; rtw_write16(rtwdev, REG_MCUFW_CTRL, fw_ctrl); } static int __rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw) { struct rtw_backup_info bckp[DLFW_RESTORE_REG_NUM]; const u8 *data = fw->firmware->data; u32 size = fw->firmware->size; u32 ltecoex_bckp; int ret; if (!check_firmware_size(data, size)) return -EINVAL; if (!ltecoex_read_reg(rtwdev, 0x38, <ecoex_bckp)) return -EBUSY; wlan_cpu_enable(rtwdev, false); download_firmware_reg_backup(rtwdev, bckp); download_firmware_reset_platform(rtwdev); ret = start_download_firmware(rtwdev, data, size); if (ret) goto dlfw_fail; download_firmware_reg_restore(rtwdev, bckp, DLFW_RESTORE_REG_NUM); download_firmware_end_flow(rtwdev); wlan_cpu_enable(rtwdev, true); - if (!ltecoex_reg_write(rtwdev, 0x38, ltecoex_bckp)) - return -EBUSY; + if (!ltecoex_reg_write(rtwdev, 0x38, ltecoex_bckp)) { + ret = -EBUSY; + goto dlfw_fail; + } ret = download_firmware_validate(rtwdev); if (ret) goto dlfw_fail; /* reset desc and index */ rtw_hci_setup(rtwdev); rtwdev->h2c.last_box_num = 0; rtwdev->h2c.seq = 0; set_bit(RTW_FLAG_FW_RUNNING, rtwdev->flags); return 0; dlfw_fail: /* Disable FWDL_EN */ rtw_write8_clr(rtwdev, REG_MCUFW_CTRL, BIT_MCUFWDL_EN); rtw_write8_set(rtwdev, REG_SYS_FUNC_EN + 1, BIT_FEN_CPUEN); return ret; } static void en_download_firmware_legacy(struct rtw_dev *rtwdev, bool en) { int try; if (en) { wlan_cpu_enable(rtwdev, false); wlan_cpu_enable(rtwdev, true); rtw_write8_set(rtwdev, REG_MCUFW_CTRL, BIT_MCUFWDL_EN); for (try = 0; try < 10; try++) { if (rtw_read8(rtwdev, REG_MCUFW_CTRL) & BIT_MCUFWDL_EN) goto fwdl_ready; rtw_write8_set(rtwdev, REG_MCUFW_CTRL, BIT_MCUFWDL_EN); msleep(20); } rtw_err(rtwdev, "failed to check fw download ready\n"); fwdl_ready: rtw_write32_clr(rtwdev, REG_MCUFW_CTRL, BIT_ROM_DLEN); } else { rtw_write8_clr(rtwdev, REG_MCUFW_CTRL, BIT_MCUFWDL_EN); } } static void write_firmware_page(struct rtw_dev *rtwdev, u32 page, const u8 *data, u32 size) { u32 val32; u32 block_nr; u32 remain_size; u32 write_addr = FW_START_ADDR_LEGACY; const __le32 *ptr = (const __le32 *)data; u32 block; __le32 remain_data = 0; block_nr = size >> DLFW_BLK_SIZE_SHIFT_LEGACY; remain_size = size & (DLFW_BLK_SIZE_LEGACY - 1); val32 = rtw_read32(rtwdev, REG_MCUFW_CTRL); val32 &= ~BIT_ROM_PGE; val32 |= (page << BIT_SHIFT_ROM_PGE) & BIT_ROM_PGE; rtw_write32(rtwdev, REG_MCUFW_CTRL, val32); for (block = 0; block < block_nr; block++) { rtw_write32(rtwdev, write_addr, le32_to_cpu(*ptr)); write_addr += DLFW_BLK_SIZE_LEGACY; ptr++; } if (remain_size) { memcpy(&remain_data, ptr, remain_size); rtw_write32(rtwdev, write_addr, le32_to_cpu(remain_data)); } } static int download_firmware_legacy(struct rtw_dev *rtwdev, const u8 *data, u32 size) { u32 page; u32 total_page; u32 last_page_size; data += sizeof(struct rtw_fw_hdr_legacy); size -= sizeof(struct rtw_fw_hdr_legacy); total_page = size >> DLFW_PAGE_SIZE_SHIFT_LEGACY; last_page_size = size & (DLFW_PAGE_SIZE_LEGACY - 1); rtw_write8_set(rtwdev, REG_MCUFW_CTRL, BIT_FWDL_CHK_RPT); for (page = 0; page < total_page; page++) { write_firmware_page(rtwdev, page, data, DLFW_PAGE_SIZE_LEGACY); data += DLFW_PAGE_SIZE_LEGACY; } if (last_page_size) write_firmware_page(rtwdev, page, data, last_page_size); if (!check_hw_ready(rtwdev, REG_MCUFW_CTRL, BIT_FWDL_CHK_RPT, 1)) { rtw_err(rtwdev, "failed to check download firmware report\n"); return -EINVAL; } return 0; } static int download_firmware_validate_legacy(struct rtw_dev *rtwdev) { u32 val32; int try; val32 = rtw_read32(rtwdev, REG_MCUFW_CTRL); val32 |= BIT_MCUFWDL_RDY; val32 &= ~BIT_WINTINI_RDY; rtw_write32(rtwdev, REG_MCUFW_CTRL, val32); wlan_cpu_enable(rtwdev, false); wlan_cpu_enable(rtwdev, true); for (try = 0; try < 10; try++) { val32 = rtw_read32(rtwdev, REG_MCUFW_CTRL); if ((val32 & FW_READY_LEGACY) == FW_READY_LEGACY) return 0; msleep(20); } rtw_err(rtwdev, "failed to validate firmware\n"); return -EINVAL; } static int __rtw_download_firmware_legacy(struct rtw_dev *rtwdev, struct rtw_fw_state *fw) { int ret = 0; en_download_firmware_legacy(rtwdev, true); ret = download_firmware_legacy(rtwdev, fw->firmware->data, fw->firmware->size); en_download_firmware_legacy(rtwdev, false); if (ret) goto out; ret = download_firmware_validate_legacy(rtwdev); if (ret) goto out; /* reset desc and index */ rtw_hci_setup(rtwdev); rtwdev->h2c.last_box_num = 0; rtwdev->h2c.seq = 0; set_bit(RTW_FLAG_FW_RUNNING, rtwdev->flags); out: return ret; } -int rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw) +static +int _rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw) { if (rtw_chip_wcpu_11n(rtwdev)) return __rtw_download_firmware_legacy(rtwdev, fw); return __rtw_download_firmware(rtwdev, fw); } +int rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw) +{ + int ret; + + ret = _rtw_download_firmware(rtwdev, fw); + if (ret) + return ret; + + if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE && + rtwdev->chip->id == RTW_CHIP_TYPE_8821C) + rtw_fw_set_recover_bt_device(rtwdev); + + return 0; +} + static u32 get_priority_queues(struct rtw_dev *rtwdev, u32 queues) { const struct rtw_rqpn *rqpn = rtwdev->fifo.rqpn; u32 prio_queues = 0; if (queues & BIT(IEEE80211_AC_VO)) prio_queues |= BIT(rqpn->dma_map_vo); if (queues & BIT(IEEE80211_AC_VI)) prio_queues |= BIT(rqpn->dma_map_vi); if (queues & BIT(IEEE80211_AC_BE)) prio_queues |= BIT(rqpn->dma_map_be); if (queues & BIT(IEEE80211_AC_BK)) prio_queues |= BIT(rqpn->dma_map_bk); return prio_queues; } static void __rtw_mac_flush_prio_queue(struct rtw_dev *rtwdev, u32 prio_queue, bool drop) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; const struct rtw_prioq_addr *addr; bool wsize; u16 avail_page, rsvd_page; int i; if (prio_queue >= RTW_DMA_MAPPING_MAX) return; addr = &chip->prioq_addrs->prio[prio_queue]; wsize = chip->prioq_addrs->wsize; /* check if all of the reserved pages are available for 100 msecs */ for (i = 0; i < 5; i++) { rsvd_page = wsize ? rtw_read16(rtwdev, addr->rsvd) : rtw_read8(rtwdev, addr->rsvd); avail_page = wsize ? rtw_read16(rtwdev, addr->avail) : rtw_read8(rtwdev, addr->avail); if (rsvd_page == avail_page) return; msleep(20); } /* priority queue is still not empty, throw a warning, * * Note that if we want to flush the tx queue when having a lot of * traffic (ex, 100Mbps up), some of the packets could be dropped. * And it requires like ~2secs to flush the full priority queue. */ if (!drop) rtw_warn(rtwdev, "timed out to flush queue %d\n", prio_queue); } static void rtw_mac_flush_prio_queues(struct rtw_dev *rtwdev, u32 prio_queues, bool drop) { u32 q; for (q = 0; q < RTW_DMA_MAPPING_MAX; q++) if (prio_queues & BIT(q)) __rtw_mac_flush_prio_queue(rtwdev, q, drop); } void rtw_mac_flush_queues(struct rtw_dev *rtwdev, u32 queues, bool drop) { u32 prio_queues = 0; /* If all of the hardware queues are requested to flush, * or the priority queues are not mapped yet, * flush all of the priority queues */ if (queues == BIT(rtwdev->hw->queues) - 1 || !rtwdev->fifo.rqpn) prio_queues = BIT(RTW_DMA_MAPPING_MAX) - 1; else prio_queues = get_priority_queues(rtwdev, queues); rtw_mac_flush_prio_queues(rtwdev, prio_queues, drop); } static int txdma_queue_mapping(struct rtw_dev *rtwdev) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; const struct rtw_rqpn *rqpn = NULL; u16 txdma_pq_map = 0; switch (rtw_hci_type(rtwdev)) { case RTW_HCI_TYPE_PCIE: rqpn = &chip->rqpn_table[1]; break; case RTW_HCI_TYPE_USB: if (rtwdev->hci.bulkout_num == 2) rqpn = &chip->rqpn_table[2]; else if (rtwdev->hci.bulkout_num == 3) rqpn = &chip->rqpn_table[3]; else if (rtwdev->hci.bulkout_num == 4) rqpn = &chip->rqpn_table[4]; else return -EINVAL; break; + case RTW_HCI_TYPE_SDIO: + rqpn = &chip->rqpn_table[0]; + break; default: return -EINVAL; } rtwdev->fifo.rqpn = rqpn; txdma_pq_map |= BIT_TXDMA_HIQ_MAP(rqpn->dma_map_hi); txdma_pq_map |= BIT_TXDMA_MGQ_MAP(rqpn->dma_map_mg); txdma_pq_map |= BIT_TXDMA_BKQ_MAP(rqpn->dma_map_bk); txdma_pq_map |= BIT_TXDMA_BEQ_MAP(rqpn->dma_map_be); txdma_pq_map |= BIT_TXDMA_VIQ_MAP(rqpn->dma_map_vi); txdma_pq_map |= BIT_TXDMA_VOQ_MAP(rqpn->dma_map_vo); rtw_write16(rtwdev, REG_TXDMA_PQ_MAP, txdma_pq_map); rtw_write8(rtwdev, REG_CR, 0); rtw_write8(rtwdev, REG_CR, MAC_TRX_ENABLE); if (rtw_chip_wcpu_11ac(rtwdev)) rtw_write32(rtwdev, REG_H2CQ_CSR, BIT_H2CQ_FULL); + if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_SDIO) { + rtw_read32(rtwdev, REG_SDIO_FREE_TXPG); + rtw_write32(rtwdev, REG_SDIO_TX_CTRL, 0); + } else if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB) { + rtw_write8_set(rtwdev, REG_TXDMA_PQ_MAP, BIT_RXDMA_ARBBW_EN); + } + return 0; } static int set_trx_fifo_info(struct rtw_dev *rtwdev) { + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_fifo_conf *fifo = &rtwdev->fifo; - struct rtw_chip_info *chip = rtwdev->chip; u16 cur_pg_addr; u8 csi_buf_pg_num = chip->csi_buf_pg_num; /* config rsvd page num */ - fifo->rsvd_drv_pg_num = 8; + fifo->rsvd_drv_pg_num = chip->rsvd_drv_pg_num; fifo->txff_pg_num = chip->txff_size >> 7; if (rtw_chip_wcpu_11n(rtwdev)) fifo->rsvd_pg_num = fifo->rsvd_drv_pg_num; else fifo->rsvd_pg_num = fifo->rsvd_drv_pg_num + RSVD_PG_H2C_EXTRAINFO_NUM + RSVD_PG_H2C_STATICINFO_NUM + RSVD_PG_H2CQ_NUM + RSVD_PG_CPU_INSTRUCTION_NUM + RSVD_PG_FW_TXBUF_NUM + csi_buf_pg_num; if (fifo->rsvd_pg_num > fifo->txff_pg_num) return -ENOMEM; fifo->acq_pg_num = fifo->txff_pg_num - fifo->rsvd_pg_num; fifo->rsvd_boundary = fifo->txff_pg_num - fifo->rsvd_pg_num; cur_pg_addr = fifo->txff_pg_num; if (rtw_chip_wcpu_11ac(rtwdev)) { cur_pg_addr -= csi_buf_pg_num; fifo->rsvd_csibuf_addr = cur_pg_addr; cur_pg_addr -= RSVD_PG_FW_TXBUF_NUM; fifo->rsvd_fw_txbuf_addr = cur_pg_addr; cur_pg_addr -= RSVD_PG_CPU_INSTRUCTION_NUM; fifo->rsvd_cpu_instr_addr = cur_pg_addr; cur_pg_addr -= RSVD_PG_H2CQ_NUM; fifo->rsvd_h2cq_addr = cur_pg_addr; cur_pg_addr -= RSVD_PG_H2C_STATICINFO_NUM; fifo->rsvd_h2c_sta_info_addr = cur_pg_addr; cur_pg_addr -= RSVD_PG_H2C_EXTRAINFO_NUM; fifo->rsvd_h2c_info_addr = cur_pg_addr; } cur_pg_addr -= fifo->rsvd_drv_pg_num; fifo->rsvd_drv_addr = cur_pg_addr; if (fifo->rsvd_boundary != fifo->rsvd_drv_addr) { rtw_err(rtwdev, "wrong rsvd driver address\n"); return -EINVAL; } return 0; } static int __priority_queue_cfg(struct rtw_dev *rtwdev, const struct rtw_page_table *pg_tbl, u16 pubq_num) { + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_fifo_conf *fifo = &rtwdev->fifo; - struct rtw_chip_info *chip = rtwdev->chip; rtw_write16(rtwdev, REG_FIFOPAGE_INFO_1, pg_tbl->hq_num); rtw_write16(rtwdev, REG_FIFOPAGE_INFO_2, pg_tbl->lq_num); rtw_write16(rtwdev, REG_FIFOPAGE_INFO_3, pg_tbl->nq_num); rtw_write16(rtwdev, REG_FIFOPAGE_INFO_4, pg_tbl->exq_num); rtw_write16(rtwdev, REG_FIFOPAGE_INFO_5, pubq_num); rtw_write32_set(rtwdev, REG_RQPN_CTRL_2, BIT_LD_RQPN); rtw_write16(rtwdev, REG_FIFOPAGE_CTRL_2, fifo->rsvd_boundary); rtw_write8_set(rtwdev, REG_FWHW_TXQ_CTRL + 2, BIT_EN_WR_FREE_TAIL >> 16); rtw_write16(rtwdev, REG_BCNQ_BDNY_V1, fifo->rsvd_boundary); rtw_write16(rtwdev, REG_FIFOPAGE_CTRL_2 + 2, fifo->rsvd_boundary); rtw_write16(rtwdev, REG_BCNQ1_BDNY_V1, fifo->rsvd_boundary); rtw_write32(rtwdev, REG_RXFF_BNDY, chip->rxff_size - C2H_PKT_BUF - 1); rtw_write8_set(rtwdev, REG_AUTO_LLT_V1, BIT_AUTO_INIT_LLT_V1); if (!check_hw_ready(rtwdev, REG_AUTO_LLT_V1, BIT_AUTO_INIT_LLT_V1, 0)) return -EBUSY; rtw_write8(rtwdev, REG_CR + 3, 0); return 0; } static int __priority_queue_cfg_legacy(struct rtw_dev *rtwdev, const struct rtw_page_table *pg_tbl, u16 pubq_num) { + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_fifo_conf *fifo = &rtwdev->fifo; - struct rtw_chip_info *chip = rtwdev->chip; u32 val32; val32 = BIT_RQPN_NE(pg_tbl->nq_num, pg_tbl->exq_num); rtw_write32(rtwdev, REG_RQPN_NPQ, val32); val32 = BIT_RQPN_HLP(pg_tbl->hq_num, pg_tbl->lq_num, pubq_num); rtw_write32(rtwdev, REG_RQPN, val32); rtw_write8(rtwdev, REG_TRXFF_BNDY, fifo->rsvd_boundary); rtw_write16(rtwdev, REG_TRXFF_BNDY + 2, chip->rxff_size - REPORT_BUF - 1); rtw_write8(rtwdev, REG_DWBCN0_CTRL + 1, fifo->rsvd_boundary); rtw_write8(rtwdev, REG_BCNQ_BDNY, fifo->rsvd_boundary); rtw_write8(rtwdev, REG_MGQ_BDNY, fifo->rsvd_boundary); rtw_write8(rtwdev, REG_WMAC_LBK_BF_HD, fifo->rsvd_boundary); rtw_write32_set(rtwdev, REG_AUTO_LLT, BIT_AUTO_INIT_LLT); if (!check_hw_ready(rtwdev, REG_AUTO_LLT, BIT_AUTO_INIT_LLT, 0)) return -EBUSY; return 0; } static int priority_queue_cfg(struct rtw_dev *rtwdev) { + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_fifo_conf *fifo = &rtwdev->fifo; - struct rtw_chip_info *chip = rtwdev->chip; const struct rtw_page_table *pg_tbl = NULL; u16 pubq_num; int ret; ret = set_trx_fifo_info(rtwdev); if (ret) return ret; switch (rtw_hci_type(rtwdev)) { case RTW_HCI_TYPE_PCIE: pg_tbl = &chip->page_table[1]; break; case RTW_HCI_TYPE_USB: if (rtwdev->hci.bulkout_num == 2) pg_tbl = &chip->page_table[2]; else if (rtwdev->hci.bulkout_num == 3) pg_tbl = &chip->page_table[3]; else if (rtwdev->hci.bulkout_num == 4) pg_tbl = &chip->page_table[4]; else return -EINVAL; break; + case RTW_HCI_TYPE_SDIO: + pg_tbl = &chip->page_table[0]; + break; default: return -EINVAL; } pubq_num = fifo->acq_pg_num - pg_tbl->hq_num - pg_tbl->lq_num - pg_tbl->nq_num - pg_tbl->exq_num - pg_tbl->gapq_num; if (rtw_chip_wcpu_11n(rtwdev)) return __priority_queue_cfg_legacy(rtwdev, pg_tbl, pubq_num); else return __priority_queue_cfg(rtwdev, pg_tbl, pubq_num); } static int init_h2c(struct rtw_dev *rtwdev) { struct rtw_fifo_conf *fifo = &rtwdev->fifo; u8 value8; u32 value32; u32 h2cq_addr; u32 h2cq_size; u32 h2cq_free; u32 wp, rp; if (rtw_chip_wcpu_11n(rtwdev)) return 0; h2cq_addr = fifo->rsvd_h2cq_addr << TX_PAGE_SIZE_SHIFT; h2cq_size = RSVD_PG_H2CQ_NUM << TX_PAGE_SIZE_SHIFT; value32 = rtw_read32(rtwdev, REG_H2C_HEAD); value32 = (value32 & 0xFFFC0000) | h2cq_addr; rtw_write32(rtwdev, REG_H2C_HEAD, value32); value32 = rtw_read32(rtwdev, REG_H2C_READ_ADDR); value32 = (value32 & 0xFFFC0000) | h2cq_addr; rtw_write32(rtwdev, REG_H2C_READ_ADDR, value32); value32 = rtw_read32(rtwdev, REG_H2C_TAIL); value32 &= 0xFFFC0000; value32 |= (h2cq_addr + h2cq_size); rtw_write32(rtwdev, REG_H2C_TAIL, value32); value8 = rtw_read8(rtwdev, REG_H2C_INFO); value8 = (u8)((value8 & 0xFC) | 0x01); rtw_write8(rtwdev, REG_H2C_INFO, value8); value8 = rtw_read8(rtwdev, REG_H2C_INFO); value8 = (u8)((value8 & 0xFB) | 0x04); rtw_write8(rtwdev, REG_H2C_INFO, value8); value8 = rtw_read8(rtwdev, REG_TXDMA_OFFSET_CHK + 1); value8 = (u8)((value8 & 0x7f) | 0x80); rtw_write8(rtwdev, REG_TXDMA_OFFSET_CHK + 1, value8); wp = rtw_read32(rtwdev, REG_H2C_PKT_WRITEADDR) & 0x3FFFF; rp = rtw_read32(rtwdev, REG_H2C_PKT_READADDR) & 0x3FFFF; h2cq_free = wp >= rp ? h2cq_size - (wp - rp) : rp - wp; if (h2cq_size != h2cq_free) { rtw_err(rtwdev, "H2C queue mismatch\n"); return -EINVAL; } return 0; } static int rtw_init_trx_cfg(struct rtw_dev *rtwdev) { int ret; ret = txdma_queue_mapping(rtwdev); if (ret) return ret; ret = priority_queue_cfg(rtwdev); if (ret) return ret; ret = init_h2c(rtwdev); if (ret) return ret; return 0; } static int rtw_drv_info_cfg(struct rtw_dev *rtwdev) { u8 value8; rtw_write8(rtwdev, REG_RX_DRVINFO_SZ, PHY_STATUS_SIZE); if (rtw_chip_wcpu_11ac(rtwdev)) { value8 = rtw_read8(rtwdev, REG_TRXFF_BNDY + 1); value8 &= 0xF0; /* For rxdesc len = 0 issue */ value8 |= 0xF; rtw_write8(rtwdev, REG_TRXFF_BNDY + 1, value8); } rtw_write32_set(rtwdev, REG_RCR, BIT_APP_PHYSTS); rtw_write32_clr(rtwdev, REG_WMAC_OPTION_FUNCTION + 4, BIT(8) | BIT(9)); return 0; } int rtw_mac_init(struct rtw_dev *rtwdev) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; int ret; ret = rtw_init_trx_cfg(rtwdev); if (ret) return ret; ret = chip->ops->mac_init(rtwdev); if (ret) return ret; ret = rtw_drv_info_cfg(rtwdev); if (ret) return ret; rtw_hci_interface_cfg(rtwdev); return 0; } diff --git a/sys/contrib/dev/rtw88/mac.h b/sys/contrib/dev/rtw88/mac.h index 3172aa5ac4de..58c3dccc14bb 100644 --- a/sys/contrib/dev/rtw88/mac.h +++ b/sys/contrib/dev/rtw88/mac.h @@ -1,46 +1,45 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* Copyright(c) 2018-2019 Realtek Corporation */ #ifndef __RTW_MAC_H__ #define __RTW_MAC_H__ #define RTW_HW_PORT_NUM 5 #define cut_version_to_mask(cut) (0x1 << ((cut) + 1)) -#define SDIO_LOCAL_OFFSET 0x10250000 #define DDMA_POLLING_COUNT 1000 #define C2H_PKT_BUF 256 #define REPORT_BUF 128 #define PHY_STATUS_SIZE 4 #define ILLEGAL_KEY_GROUP 0xFAAAAA00 /* HW memory address */ #define OCPBASE_RXBUF_FW_88XX 0x18680000 #define OCPBASE_TXBUF_88XX 0x18780000 #define OCPBASE_ROM_88XX 0x00000000 #define OCPBASE_IMEM_88XX 0x00030000 #define OCPBASE_DMEM_88XX 0x00200000 #define OCPBASE_EMEM_88XX 0x00100000 #define RSVD_PG_DRV_NUM 16 #define RSVD_PG_H2C_EXTRAINFO_NUM 24 #define RSVD_PG_H2C_STATICINFO_NUM 8 #define RSVD_PG_H2CQ_NUM 8 #define RSVD_PG_CPU_INSTRUCTION_NUM 0 #define RSVD_PG_FW_TXBUF_NUM 4 void rtw_set_channel_mac(struct rtw_dev *rtwdev, u8 channel, u8 bw, u8 primary_ch_idx); int rtw_mac_power_on(struct rtw_dev *rtwdev); void rtw_mac_power_off(struct rtw_dev *rtwdev); int rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw); int rtw_mac_init(struct rtw_dev *rtwdev); void rtw_mac_flush_queues(struct rtw_dev *rtwdev, u32 queues, bool drop); int rtw_ddma_to_fw_fifo(struct rtw_dev *rtwdev, u32 ocp_src, u32 size); static inline void rtw_mac_flush_all_queues(struct rtw_dev *rtwdev, bool drop) { rtw_mac_flush_queues(rtwdev, BIT(rtwdev->hw->queues) - 1, drop); } #endif diff --git a/sys/contrib/dev/rtw88/mac80211.c b/sys/contrib/dev/rtw88/mac80211.c index 643ce2900d0a..d01c5268191f 100644 --- a/sys/contrib/dev/rtw88/mac80211.c +++ b/sys/contrib/dev/rtw88/mac80211.c @@ -1,943 +1,982 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* Copyright(c) 2018-2019 Realtek Corporation */ #include "main.h" #include "sec.h" #include "tx.h" #include "fw.h" #include "mac.h" #include "coex.h" #include "ps.h" #include "reg.h" #include "bf.h" #include "debug.h" #include "wow.h" #include "sar.h" static void rtw_ops_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) { struct rtw_dev *rtwdev = hw->priv; if (!test_bit(RTW_FLAG_RUNNING, rtwdev->flags)) { ieee80211_free_txskb(hw, skb); return; } rtw_tx(rtwdev, control, skb); } static void rtw_ops_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq) { struct rtw_dev *rtwdev = hw->priv; struct rtw_txq *rtwtxq = (struct rtw_txq *)txq->drv_priv; if (!test_bit(RTW_FLAG_RUNNING, rtwdev->flags)) return; spin_lock_bh(&rtwdev->txq_lock); if (list_empty(&rtwtxq->list)) list_add_tail(&rtwtxq->list, &rtwdev->txqs); spin_unlock_bh(&rtwdev->txq_lock); - queue_work(rtwdev->tx_wq, &rtwdev->tx_work); + /* ensure to dequeue EAPOL (4/4) at the right time */ + if (txq->ac == IEEE80211_AC_VO) + __rtw_tx_work(rtwdev); + else + queue_work(rtwdev->tx_wq, &rtwdev->tx_work); } static int rtw_ops_start(struct ieee80211_hw *hw) { struct rtw_dev *rtwdev = hw->priv; int ret; mutex_lock(&rtwdev->mutex); ret = rtw_core_start(rtwdev); mutex_unlock(&rtwdev->mutex); return ret; } static void rtw_ops_stop(struct ieee80211_hw *hw) { struct rtw_dev *rtwdev = hw->priv; mutex_lock(&rtwdev->mutex); rtw_core_stop(rtwdev); mutex_unlock(&rtwdev->mutex); } static int rtw_ops_config(struct ieee80211_hw *hw, u32 changed) { struct rtw_dev *rtwdev = hw->priv; int ret = 0; /* let previous ips work finish to ensure we don't leave ips twice */ cancel_work_sync(&rtwdev->ips_work); mutex_lock(&rtwdev->mutex); rtw_leave_lps_deep(rtwdev); if ((changed & IEEE80211_CONF_CHANGE_IDLE) && !(hw->conf.flags & IEEE80211_CONF_IDLE)) { ret = rtw_leave_ips(rtwdev); if (ret) { rtw_err(rtwdev, "failed to leave idle state\n"); goto out; } } - if (changed & IEEE80211_CONF_CHANGE_PS) { - if (hw->conf.flags & IEEE80211_CONF_PS) { - rtwdev->ps_enabled = true; - } else { - rtwdev->ps_enabled = false; - rtw_leave_lps(rtwdev); - } - } - if (changed & IEEE80211_CONF_CHANGE_CHANNEL) rtw_set_channel(rtwdev); if ((changed & IEEE80211_CONF_CHANGE_IDLE) && - (hw->conf.flags & IEEE80211_CONF_IDLE)) + (hw->conf.flags & IEEE80211_CONF_IDLE) && + !test_bit(RTW_FLAG_SCANNING, rtwdev->flags)) rtw_enter_ips(rtwdev); out: mutex_unlock(&rtwdev->mutex); return ret; } static const struct rtw_vif_port rtw_vif_port[] = { [0] = { .mac_addr = {.addr = 0x0610}, .bssid = {.addr = 0x0618}, .net_type = {.addr = 0x0100, .mask = 0x30000}, .aid = {.addr = 0x06a8, .mask = 0x7ff}, .bcn_ctrl = {.addr = 0x0550, .mask = 0xff}, }, [1] = { .mac_addr = {.addr = 0x0700}, .bssid = {.addr = 0x0708}, .net_type = {.addr = 0x0100, .mask = 0xc0000}, .aid = {.addr = 0x0710, .mask = 0x7ff}, .bcn_ctrl = {.addr = 0x0551, .mask = 0xff}, }, [2] = { .mac_addr = {.addr = 0x1620}, .bssid = {.addr = 0x1628}, .net_type = {.addr = 0x1100, .mask = 0x3}, .aid = {.addr = 0x1600, .mask = 0x7ff}, .bcn_ctrl = {.addr = 0x0578, .mask = 0xff}, }, [3] = { .mac_addr = {.addr = 0x1630}, .bssid = {.addr = 0x1638}, .net_type = {.addr = 0x1100, .mask = 0xc}, .aid = {.addr = 0x1604, .mask = 0x7ff}, .bcn_ctrl = {.addr = 0x0579, .mask = 0xff}, }, [4] = { .mac_addr = {.addr = 0x1640}, .bssid = {.addr = 0x1648}, .net_type = {.addr = 0x1100, .mask = 0x30}, .aid = {.addr = 0x1608, .mask = 0x7ff}, .bcn_ctrl = {.addr = 0x057a, .mask = 0xff}, }, }; static int rtw_ops_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct rtw_dev *rtwdev = hw->priv; struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; enum rtw_net_type net_type; u32 config = 0; - u8 port = 0; + u8 port; u8 bcn_ctrl = 0; if (rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_BCN_FILTER)) vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | IEEE80211_VIF_SUPPORTS_CQM_RSSI; - rtwvif->port = port; rtwvif->stats.tx_unicast = 0; rtwvif->stats.rx_unicast = 0; rtwvif->stats.tx_cnt = 0; rtwvif->stats.rx_cnt = 0; rtwvif->scan_req = NULL; memset(&rtwvif->bfee, 0, sizeof(struct rtw_bfee)); - rtwvif->conf = &rtw_vif_port[port]; rtw_txq_init(rtwdev, vif->txq); INIT_LIST_HEAD(&rtwvif->rsvd_page_list); mutex_lock(&rtwdev->mutex); + port = find_first_zero_bit(rtwdev->hw_port, RTW_PORT_NUM); + if (port >= RTW_PORT_NUM) { + mutex_unlock(&rtwdev->mutex); + return -EINVAL; + } + set_bit(port, rtwdev->hw_port); + + rtwvif->port = port; + rtwvif->conf = &rtw_vif_port[port]; rtw_leave_lps_deep(rtwdev); switch (vif->type) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_MESH_POINT: rtw_add_rsvd_page_bcn(rtwdev, rtwvif); net_type = RTW_NET_AP_MODE; bcn_ctrl = BIT_EN_BCN_FUNCTION | BIT_DIS_TSF_UDT; break; case NL80211_IFTYPE_ADHOC: rtw_add_rsvd_page_bcn(rtwdev, rtwvif); net_type = RTW_NET_AD_HOC; bcn_ctrl = BIT_EN_BCN_FUNCTION | BIT_DIS_TSF_UDT; break; case NL80211_IFTYPE_STATION: rtw_add_rsvd_page_sta(rtwdev, rtwvif); net_type = RTW_NET_NO_LINK; bcn_ctrl = BIT_EN_BCN_FUNCTION; break; default: WARN_ON(1); + clear_bit(rtwvif->port, rtwdev->hw_port); mutex_unlock(&rtwdev->mutex); return -EINVAL; } ether_addr_copy(rtwvif->mac_addr, vif->addr); config |= PORT_SET_MAC_ADDR; rtwvif->net_type = net_type; config |= PORT_SET_NET_TYPE; rtwvif->bcn_ctrl = bcn_ctrl; config |= PORT_SET_BCN_CTRL; rtw_vif_port_config(rtwdev, rtwvif, config); + rtw_core_port_switch(rtwdev, vif); + rtw_recalc_lps(rtwdev, vif); mutex_unlock(&rtwdev->mutex); #if defined(__linux__) rtw_dbg(rtwdev, RTW_DBG_STATE, "start vif %pM on port %d\n", vif->addr, rtwvif->port); #elif defined(__FreeBSD__) rtw_dbg(rtwdev, RTW_DBG_STATE, "start vif %6D on port %d\n", vif->addr, ":", rtwvif->port); #endif return 0; } static void rtw_ops_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct rtw_dev *rtwdev = hw->priv; struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; u32 config = 0; #if defined(__linux__) rtw_dbg(rtwdev, RTW_DBG_STATE, "stop vif %pM on port %d\n", vif->addr, rtwvif->port); #elif defined(__FreeBSD__) rtw_dbg(rtwdev, RTW_DBG_STATE, "stop vif %6D on port %d\n", vif->addr, ":", rtwvif->port); #endif mutex_lock(&rtwdev->mutex); rtw_leave_lps_deep(rtwdev); rtw_txq_cleanup(rtwdev, vif->txq); rtw_remove_rsvd_page(rtwdev, rtwvif); eth_zero_addr(rtwvif->mac_addr); config |= PORT_SET_MAC_ADDR; rtwvif->net_type = RTW_NET_NO_LINK; config |= PORT_SET_NET_TYPE; rtwvif->bcn_ctrl = 0; config |= PORT_SET_BCN_CTRL; rtw_vif_port_config(rtwdev, rtwvif, config); + clear_bit(rtwvif->port, rtwdev->hw_port); + rtw_recalc_lps(rtwdev, NULL); mutex_unlock(&rtwdev->mutex); } static int rtw_ops_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum nl80211_iftype type, bool p2p) { struct rtw_dev *rtwdev = hw->priv; #if defined(__linux__) rtw_dbg(rtwdev, RTW_DBG_STATE, "change vif %pM (%d)->(%d), p2p (%d)->(%d)\n", vif->addr, vif->type, type, vif->p2p, p2p); #elif defined(__FreeBSD__) rtw_dbg(rtwdev, RTW_DBG_STATE, "change vif %6D (%d)->(%d), p2p (%d)->(%d)\n", vif->addr, ":", vif->type, type, vif->p2p, p2p); #endif rtw_ops_remove_interface(hw, vif); vif->type = type; vif->p2p = p2p; return rtw_ops_add_interface(hw, vif); } static void rtw_ops_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *new_flags, u64 multicast) { struct rtw_dev *rtwdev = hw->priv; *new_flags &= FIF_ALLMULTI | FIF_OTHER_BSS | FIF_FCSFAIL | FIF_BCN_PRBRESP_PROMISC; mutex_lock(&rtwdev->mutex); rtw_leave_lps_deep(rtwdev); if (changed_flags & FIF_ALLMULTI) { if (*new_flags & FIF_ALLMULTI) rtwdev->hal.rcr |= BIT_AM | BIT_AB; else rtwdev->hal.rcr &= ~(BIT_AM | BIT_AB); } if (changed_flags & FIF_FCSFAIL) { if (*new_flags & FIF_FCSFAIL) rtwdev->hal.rcr |= BIT_ACRC32; else rtwdev->hal.rcr &= ~(BIT_ACRC32); } if (changed_flags & FIF_OTHER_BSS) { if (*new_flags & FIF_OTHER_BSS) rtwdev->hal.rcr |= BIT_AAP; else rtwdev->hal.rcr &= ~(BIT_AAP); } if (changed_flags & FIF_BCN_PRBRESP_PROMISC) { if (*new_flags & FIF_BCN_PRBRESP_PROMISC) rtwdev->hal.rcr &= ~(BIT_CBSSID_BCN | BIT_CBSSID_DATA); else rtwdev->hal.rcr |= BIT_CBSSID_BCN; } rtw_dbg(rtwdev, RTW_DBG_RX, "config rx filter, changed=0x%08x, new=0x%08x, rcr=0x%08x\n", changed_flags, *new_flags, rtwdev->hal.rcr); rtw_write32(rtwdev, REG_RCR, rtwdev->hal.rcr); mutex_unlock(&rtwdev->mutex); } /* Only have one group of EDCA parameters now */ static const u32 ac_to_edca_param[IEEE80211_NUM_ACS] = { [IEEE80211_AC_VO] = REG_EDCA_VO_PARAM, [IEEE80211_AC_VI] = REG_EDCA_VI_PARAM, [IEEE80211_AC_BE] = REG_EDCA_BE_PARAM, [IEEE80211_AC_BK] = REG_EDCA_BK_PARAM, }; static u8 rtw_aifsn_to_aifs(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif, u8 aifsn) { struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif); u8 slot_time; u8 sifs; slot_time = vif->bss_conf.use_short_slot ? 9 : 20; sifs = rtwdev->hal.current_band_type == RTW_BAND_5G ? 16 : 10; return aifsn * slot_time + sifs; } static void __rtw_conf_tx(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif, u16 ac) { struct ieee80211_tx_queue_params *params = &rtwvif->tx_params[ac]; u32 edca_param = ac_to_edca_param[ac]; u8 ecw_max, ecw_min; u8 aifs; /* 2^ecw - 1 = cw; ecw = log2(cw + 1) */ ecw_max = ilog2(params->cw_max + 1); ecw_min = ilog2(params->cw_min + 1); aifs = rtw_aifsn_to_aifs(rtwdev, rtwvif, params->aifs); rtw_write32_mask(rtwdev, edca_param, BIT_MASK_TXOP_LMT, params->txop); rtw_write32_mask(rtwdev, edca_param, BIT_MASK_CWMAX, ecw_max); rtw_write32_mask(rtwdev, edca_param, BIT_MASK_CWMIN, ecw_min); rtw_write32_mask(rtwdev, edca_param, BIT_MASK_AIFS, aifs); } static void rtw_conf_tx(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif) { u16 ac; for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) __rtw_conf_tx(rtwdev, rtwvif, ac); } static void rtw_ops_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *conf, u64 changed) { struct rtw_dev *rtwdev = hw->priv; struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_stat *coex_stat = &coex->stat; u32 config = 0; mutex_lock(&rtwdev->mutex); rtw_leave_lps_deep(rtwdev); if (changed & BSS_CHANGED_ASSOC) { rtw_vif_assoc_changed(rtwvif, conf); - if (conf->assoc) { + if (vif->cfg.assoc) { rtw_coex_connect_notify(rtwdev, COEX_ASSOCIATE_FINISH); rtw_fw_download_rsvd_page(rtwdev); rtw_send_rsvd_page_h2c(rtwdev); - rtw_coex_media_status_notify(rtwdev, conf->assoc); + rtw_fw_default_port(rtwdev, rtwvif); + rtw_coex_media_status_notify(rtwdev, vif->cfg.assoc); if (rtw_bf_support) rtw_bf_assoc(rtwdev, vif, conf); - rtw_store_op_chan(rtwdev); } else { rtw_leave_lps(rtwdev); rtw_bf_disassoc(rtwdev, vif, conf); /* Abort ongoing scan if cancel_scan isn't issued * when disconnected by peer */ if (test_bit(RTW_FLAG_SCANNING, rtwdev->flags)) - rtw_hw_scan_abort(rtwdev, vif); + rtw_hw_scan_abort(rtwdev); + } config |= PORT_SET_NET_TYPE; config |= PORT_SET_AID; } if (changed & BSS_CHANGED_BSSID) { ether_addr_copy(rtwvif->bssid, conf->bssid); config |= PORT_SET_BSSID; + if (!rtw_core_check_sta_active(rtwdev)) + rtw_clear_op_chan(rtwdev); + else + rtw_store_op_chan(rtwdev, true); } if (changed & BSS_CHANGED_BEACON_INT) { if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_STATION) coex_stat->wl_beacon_interval = conf->beacon_int; } if (changed & BSS_CHANGED_BEACON) { rtw_set_dtim_period(rtwdev, conf->dtim_period); rtw_fw_download_rsvd_page(rtwdev); + rtw_send_rsvd_page_h2c(rtwdev); } if (changed & BSS_CHANGED_BEACON_ENABLED) { if (conf->enable_beacon) rtw_write32_set(rtwdev, REG_FWHW_TXQ_CTRL, BIT_EN_BCNQ_DL); else rtw_write32_clr(rtwdev, REG_FWHW_TXQ_CTRL, BIT_EN_BCNQ_DL); } if (changed & BSS_CHANGED_CQM) rtw_fw_beacon_filter_config(rtwdev, true, vif); if (changed & BSS_CHANGED_MU_GROUPS) rtw_chip_set_gid_table(rtwdev, vif, conf); if (changed & BSS_CHANGED_ERP_SLOT) rtw_conf_tx(rtwdev, rtwvif); + if (changed & BSS_CHANGED_PS) + rtw_recalc_lps(rtwdev, NULL); + rtw_vif_port_config(rtwdev, rtwvif, config); mutex_unlock(&rtwdev->mutex); } static int rtw_ops_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf) { struct rtw_dev *rtwdev = hw->priv; - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; mutex_lock(&rtwdev->mutex); + rtw_write32_set(rtwdev, REG_TCR, BIT_TCR_UPDATE_HGQMD); + rtwdev->ap_active = true; + rtw_store_op_chan(rtwdev, true); chip->ops->phy_calibration(rtwdev); mutex_unlock(&rtwdev->mutex); return 0; } +static void rtw_ops_stop_ap(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf) +{ + struct rtw_dev *rtwdev = hw->priv; + + mutex_lock(&rtwdev->mutex); + rtw_write32_clr(rtwdev, REG_TCR, BIT_TCR_UPDATE_HGQMD); + rtwdev->ap_active = false; + if (!rtw_core_check_sta_active(rtwdev)) + rtw_clear_op_chan(rtwdev); + mutex_unlock(&rtwdev->mutex); +} + static int rtw_ops_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, unsigned int link_id, u16 ac, const struct ieee80211_tx_queue_params *params) { struct rtw_dev *rtwdev = hw->priv; struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; mutex_lock(&rtwdev->mutex); rtw_leave_lps_deep(rtwdev); rtwvif->tx_params[ac] = *params; __rtw_conf_tx(rtwdev, rtwvif, ac); mutex_unlock(&rtwdev->mutex); return 0; } static int rtw_ops_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct rtw_dev *rtwdev = hw->priv; int ret = 0; mutex_lock(&rtwdev->mutex); ret = rtw_sta_add(rtwdev, sta, vif); mutex_unlock(&rtwdev->mutex); return ret; } static int rtw_ops_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct rtw_dev *rtwdev = hw->priv; - rtw_fw_beacon_filter_config(rtwdev, false, vif); mutex_lock(&rtwdev->mutex); + rtw_fw_beacon_filter_config(rtwdev, false, vif); rtw_sta_remove(rtwdev, sta, true); mutex_unlock(&rtwdev->mutex); return 0; } static int rtw_ops_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set) { struct rtw_dev *rtwdev = hw->priv; ieee80211_queue_work(hw, &rtwdev->update_beacon_work); return 0; } static int rtw_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { struct rtw_dev *rtwdev = hw->priv; struct rtw_sec_desc *sec = &rtwdev->sec; u8 hw_key_type; u8 hw_key_idx; int ret = 0; switch (key->cipher) { case WLAN_CIPHER_SUITE_WEP40: hw_key_type = RTW_CAM_WEP40; break; case WLAN_CIPHER_SUITE_WEP104: hw_key_type = RTW_CAM_WEP104; break; case WLAN_CIPHER_SUITE_TKIP: hw_key_type = RTW_CAM_TKIP; key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; break; case WLAN_CIPHER_SUITE_CCMP: hw_key_type = RTW_CAM_AES; key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX; break; case WLAN_CIPHER_SUITE_AES_CMAC: case WLAN_CIPHER_SUITE_BIP_CMAC_256: case WLAN_CIPHER_SUITE_BIP_GMAC_128: case WLAN_CIPHER_SUITE_BIP_GMAC_256: case WLAN_CIPHER_SUITE_CCMP_256: case WLAN_CIPHER_SUITE_GCMP: case WLAN_CIPHER_SUITE_GCMP_256: /* suppress error messages */ return -EOPNOTSUPP; default: return -ENOTSUPP; } mutex_lock(&rtwdev->mutex); rtw_leave_lps_deep(rtwdev); if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) { hw_key_idx = rtw_sec_get_free_cam(sec); } else { /* multiple interfaces? */ hw_key_idx = key->keyidx; } if (hw_key_idx > sec->total_cam_num) { ret = -ENOSPC; goto out; } switch (cmd) { case SET_KEY: /* need sw generated IV */ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; key->hw_key_idx = hw_key_idx; rtw_sec_write_cam(rtwdev, sec, sta, key, hw_key_type, hw_key_idx); break; case DISABLE_KEY: rtw_hci_flush_all_queues(rtwdev, false); rtw_mac_flush_all_queues(rtwdev, false); rtw_sec_clear_cam(rtwdev, sec, key->hw_key_idx); break; } /* download new cam settings for PG to backup */ if (rtw_get_lps_deep_mode(rtwdev) == LPS_DEEP_MODE_PG) rtw_fw_download_rsvd_page(rtwdev); out: mutex_unlock(&rtwdev->mutex); return ret; } static int rtw_ops_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_ampdu_params *params) { struct ieee80211_sta *sta = params->sta; u16 tid = params->tid; struct ieee80211_txq *txq = sta->txq[tid]; struct rtw_txq *rtwtxq = (struct rtw_txq *)txq->drv_priv; switch (params->action) { case IEEE80211_AMPDU_TX_START: return IEEE80211_AMPDU_TX_START_IMMEDIATE; case IEEE80211_AMPDU_TX_STOP_CONT: case IEEE80211_AMPDU_TX_STOP_FLUSH: case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: clear_bit(RTW_TXQ_AMPDU, &rtwtxq->flags); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: set_bit(RTW_TXQ_AMPDU, &rtwtxq->flags); break; case IEEE80211_AMPDU_RX_START: case IEEE80211_AMPDU_RX_STOP: break; default: WARN_ON(1); return -ENOTSUPP; } return 0; } static bool rtw_ops_can_aggregate_in_amsdu(struct ieee80211_hw *hw, struct sk_buff *head, struct sk_buff *skb) { struct rtw_dev *rtwdev = hw->priv; struct rtw_hal *hal = &rtwdev->hal; /* we don't want to enable TX AMSDU on 2.4G */ if (hal->current_band_type == RTW_BAND_2G) return false; return true; } static void rtw_ops_sw_scan_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const u8 *mac_addr) { struct rtw_dev *rtwdev = hw->priv; struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; mutex_lock(&rtwdev->mutex); rtw_core_scan_start(rtwdev, rtwvif, mac_addr, false); mutex_unlock(&rtwdev->mutex); } static void rtw_ops_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct rtw_dev *rtwdev = hw->priv; mutex_lock(&rtwdev->mutex); rtw_core_scan_complete(rtwdev, vif, false); mutex_unlock(&rtwdev->mutex); } static void rtw_ops_mgd_prepare_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_prep_tx_info *info) { struct rtw_dev *rtwdev = hw->priv; mutex_lock(&rtwdev->mutex); rtw_leave_lps_deep(rtwdev); rtw_coex_connect_notify(rtwdev, COEX_ASSOCIATE_START); rtw_chip_prepare_tx(rtwdev); mutex_unlock(&rtwdev->mutex); } static int rtw_ops_set_rts_threshold(struct ieee80211_hw *hw, u32 value) { struct rtw_dev *rtwdev = hw->priv; mutex_lock(&rtwdev->mutex); rtwdev->rts_threshold = value; mutex_unlock(&rtwdev->mutex); return 0; } static void rtw_ops_sta_statistics(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct station_info *sinfo) { struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv; sinfo->txrate = si->ra_report.txrate; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); } static void rtw_ops_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 queues, bool drop) { struct rtw_dev *rtwdev = hw->priv; mutex_lock(&rtwdev->mutex); rtw_leave_lps_deep(rtwdev); rtw_hci_flush_queues(rtwdev, queues, drop); rtw_mac_flush_queues(rtwdev, queues, drop); mutex_unlock(&rtwdev->mutex); } struct rtw_iter_bitrate_mask_data { struct rtw_dev *rtwdev; struct ieee80211_vif *vif; const struct cfg80211_bitrate_mask *mask; }; static void rtw_ra_mask_info_update_iter(void *data, struct ieee80211_sta *sta) { struct rtw_iter_bitrate_mask_data *br_data = data; struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv; if (si->vif != br_data->vif) return; /* free previous mask setting */ kfree(si->mask); si->mask = kmemdup(br_data->mask, sizeof(struct cfg80211_bitrate_mask), GFP_ATOMIC); if (!si->mask) { si->use_cfg_mask = false; return; } si->use_cfg_mask = true; rtw_update_sta_info(br_data->rtwdev, si, true); } static void rtw_ra_mask_info_update(struct rtw_dev *rtwdev, struct ieee80211_vif *vif, const struct cfg80211_bitrate_mask *mask) { struct rtw_iter_bitrate_mask_data br_data; br_data.rtwdev = rtwdev; br_data.vif = vif; br_data.mask = mask; - rtw_iterate_stas_atomic(rtwdev, rtw_ra_mask_info_update_iter, &br_data); + rtw_iterate_stas(rtwdev, rtw_ra_mask_info_update_iter, &br_data); } static int rtw_ops_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const struct cfg80211_bitrate_mask *mask) { struct rtw_dev *rtwdev = hw->priv; + mutex_lock(&rtwdev->mutex); rtw_ra_mask_info_update(rtwdev, vif, mask); + mutex_unlock(&rtwdev->mutex); return 0; } static int rtw_ops_set_antenna(struct ieee80211_hw *hw, u32 tx_antenna, u32 rx_antenna) { struct rtw_dev *rtwdev = hw->priv; - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; int ret; if (!chip->ops->set_antenna) return -EOPNOTSUPP; mutex_lock(&rtwdev->mutex); ret = chip->ops->set_antenna(rtwdev, tx_antenna, rx_antenna); mutex_unlock(&rtwdev->mutex); return ret; } static int rtw_ops_get_antenna(struct ieee80211_hw *hw, u32 *tx_antenna, u32 *rx_antenna) { struct rtw_dev *rtwdev = hw->priv; struct rtw_hal *hal = &rtwdev->hal; *tx_antenna = hal->antenna_tx; *rx_antenna = hal->antenna_rx; return 0; } #ifdef CONFIG_PM static int rtw_ops_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) { struct rtw_dev *rtwdev = hw->priv; int ret; mutex_lock(&rtwdev->mutex); ret = rtw_wow_suspend(rtwdev, wowlan); if (ret) rtw_err(rtwdev, "failed to suspend for wow %d\n", ret); mutex_unlock(&rtwdev->mutex); return ret ? 1 : 0; } static int rtw_ops_resume(struct ieee80211_hw *hw) { struct rtw_dev *rtwdev = hw->priv; int ret; mutex_lock(&rtwdev->mutex); ret = rtw_wow_resume(rtwdev); if (ret) rtw_err(rtwdev, "failed to resume for wow %d\n", ret); mutex_unlock(&rtwdev->mutex); return ret ? 1 : 0; } static void rtw_ops_set_wakeup(struct ieee80211_hw *hw, bool enabled) { struct rtw_dev *rtwdev = hw->priv; device_set_wakeup_enable(rtwdev->dev, enabled); } #endif static void rtw_reconfig_complete(struct ieee80211_hw *hw, enum ieee80211_reconfig_type reconfig_type) { struct rtw_dev *rtwdev = hw->priv; mutex_lock(&rtwdev->mutex); if (reconfig_type == IEEE80211_RECONFIG_TYPE_RESTART) clear_bit(RTW_FLAG_RESTARTING, rtwdev->flags); mutex_unlock(&rtwdev->mutex); } static int rtw_ops_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_scan_request *req) { struct rtw_dev *rtwdev = hw->priv; int ret; if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_SCAN_OFFLOAD)) return 1; if (test_bit(RTW_FLAG_SCANNING, rtwdev->flags)) return -EBUSY; mutex_lock(&rtwdev->mutex); rtw_hw_scan_start(rtwdev, vif, req); ret = rtw_hw_scan_offload(rtwdev, vif, true); if (ret) { - rtw_hw_scan_abort(rtwdev, vif); + rtw_hw_scan_abort(rtwdev); rtw_err(rtwdev, "HW scan failed with status: %d\n", ret); } mutex_unlock(&rtwdev->mutex); return ret; } static void rtw_ops_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct rtw_dev *rtwdev = hw->priv; if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_SCAN_OFFLOAD)) return; if (!test_bit(RTW_FLAG_SCANNING, rtwdev->flags)) return; mutex_lock(&rtwdev->mutex); - rtw_hw_scan_abort(rtwdev, vif); + rtw_hw_scan_abort(rtwdev); mutex_unlock(&rtwdev->mutex); } static int rtw_ops_set_sar_specs(struct ieee80211_hw *hw, const struct cfg80211_sar_specs *sar) { struct rtw_dev *rtwdev = hw->priv; + mutex_lock(&rtwdev->mutex); rtw_set_sar_specs(rtwdev, sar); + mutex_unlock(&rtwdev->mutex); return 0; } static void rtw_ops_sta_rc_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u32 changed) { struct rtw_dev *rtwdev = hw->priv; struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv; if (changed & IEEE80211_RC_BW_CHANGED) - rtw_update_sta_info(rtwdev, si, true); + ieee80211_queue_work(rtwdev->hw, &si->rc_work); } const struct ieee80211_ops rtw_ops = { .tx = rtw_ops_tx, .wake_tx_queue = rtw_ops_wake_tx_queue, .start = rtw_ops_start, .stop = rtw_ops_stop, .config = rtw_ops_config, .add_interface = rtw_ops_add_interface, .remove_interface = rtw_ops_remove_interface, .change_interface = rtw_ops_change_interface, .configure_filter = rtw_ops_configure_filter, .bss_info_changed = rtw_ops_bss_info_changed, .start_ap = rtw_ops_start_ap, + .stop_ap = rtw_ops_stop_ap, .conf_tx = rtw_ops_conf_tx, .sta_add = rtw_ops_sta_add, .sta_remove = rtw_ops_sta_remove, .set_tim = rtw_ops_set_tim, .set_key = rtw_ops_set_key, .ampdu_action = rtw_ops_ampdu_action, .can_aggregate_in_amsdu = rtw_ops_can_aggregate_in_amsdu, .sw_scan_start = rtw_ops_sw_scan_start, .sw_scan_complete = rtw_ops_sw_scan_complete, .mgd_prepare_tx = rtw_ops_mgd_prepare_tx, .set_rts_threshold = rtw_ops_set_rts_threshold, .sta_statistics = rtw_ops_sta_statistics, .flush = rtw_ops_flush, .set_bitrate_mask = rtw_ops_set_bitrate_mask, .set_antenna = rtw_ops_set_antenna, .get_antenna = rtw_ops_get_antenna, .reconfig_complete = rtw_reconfig_complete, .hw_scan = rtw_ops_hw_scan, .cancel_hw_scan = rtw_ops_cancel_hw_scan, .sta_rc_update = rtw_ops_sta_rc_update, .set_sar_specs = rtw_ops_set_sar_specs, #ifdef CONFIG_PM .suspend = rtw_ops_suspend, .resume = rtw_ops_resume, .set_wakeup = rtw_ops_set_wakeup, #endif }; EXPORT_SYMBOL(rtw_ops); diff --git a/sys/contrib/dev/rtw88/main.c b/sys/contrib/dev/rtw88/main.c index deac3564d847..cb4e0f92ae12 100644 --- a/sys/contrib/dev/rtw88/main.c +++ b/sys/contrib/dev/rtw88/main.c @@ -1,2209 +1,2450 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* Copyright(c) 2018-2019 Realtek Corporation */ #if defined(__FreeBSD__) #define LINUXKPI_PARAM_PREFIX rtw88_ #endif #include #include "main.h" #include "regd.h" #include "fw.h" #include "ps.h" #include "sec.h" #include "mac.h" #include "coex.h" #include "phy.h" #include "reg.h" #include "efuse.h" #include "tx.h" #include "debug.h" #include "bf.h" #include "sar.h" +#include "sdio.h" bool rtw_disable_lps_deep_mode; EXPORT_SYMBOL(rtw_disable_lps_deep_mode); bool rtw_bf_support = true; unsigned int rtw_debug_mask; EXPORT_SYMBOL(rtw_debug_mask); /* EDCCA is enabled during normal behavior. For debugging purpose in * a noisy environment, it can be disabled via edcca debugfs. Because * all rtw88 devices will probably be affected if environment is noisy, * rtw_edcca_enabled is just declared by driver instead of by device. * So, turning it off will take effect for all rtw88 devices before * there is a tough reason to maintain rtw_edcca_enabled by device. */ bool rtw_edcca_enabled = true; module_param_named(disable_lps_deep, rtw_disable_lps_deep_mode, bool, 0644); module_param_named(support_bf, rtw_bf_support, bool, 0644); module_param_named(debug_mask, rtw_debug_mask, uint, 0644); MODULE_PARM_DESC(disable_lps_deep, "Set Y to disable Deep PS"); MODULE_PARM_DESC(support_bf, "Set Y to enable beamformee support"); MODULE_PARM_DESC(debug_mask, "Debugging mask"); static struct ieee80211_channel rtw_channeltable_2g[] = { {.center_freq = 2412, .hw_value = 1,}, {.center_freq = 2417, .hw_value = 2,}, {.center_freq = 2422, .hw_value = 3,}, {.center_freq = 2427, .hw_value = 4,}, {.center_freq = 2432, .hw_value = 5,}, {.center_freq = 2437, .hw_value = 6,}, {.center_freq = 2442, .hw_value = 7,}, {.center_freq = 2447, .hw_value = 8,}, {.center_freq = 2452, .hw_value = 9,}, {.center_freq = 2457, .hw_value = 10,}, {.center_freq = 2462, .hw_value = 11,}, {.center_freq = 2467, .hw_value = 12,}, {.center_freq = 2472, .hw_value = 13,}, {.center_freq = 2484, .hw_value = 14,}, }; static struct ieee80211_channel rtw_channeltable_5g[] = { {.center_freq = 5180, .hw_value = 36,}, {.center_freq = 5200, .hw_value = 40,}, {.center_freq = 5220, .hw_value = 44,}, {.center_freq = 5240, .hw_value = 48,}, {.center_freq = 5260, .hw_value = 52,}, {.center_freq = 5280, .hw_value = 56,}, {.center_freq = 5300, .hw_value = 60,}, {.center_freq = 5320, .hw_value = 64,}, {.center_freq = 5500, .hw_value = 100,}, {.center_freq = 5520, .hw_value = 104,}, {.center_freq = 5540, .hw_value = 108,}, {.center_freq = 5560, .hw_value = 112,}, {.center_freq = 5580, .hw_value = 116,}, {.center_freq = 5600, .hw_value = 120,}, {.center_freq = 5620, .hw_value = 124,}, {.center_freq = 5640, .hw_value = 128,}, {.center_freq = 5660, .hw_value = 132,}, {.center_freq = 5680, .hw_value = 136,}, {.center_freq = 5700, .hw_value = 140,}, {.center_freq = 5720, .hw_value = 144,}, {.center_freq = 5745, .hw_value = 149,}, {.center_freq = 5765, .hw_value = 153,}, {.center_freq = 5785, .hw_value = 157,}, {.center_freq = 5805, .hw_value = 161,}, {.center_freq = 5825, .hw_value = 165, .flags = IEEE80211_CHAN_NO_HT40MINUS}, }; static struct ieee80211_rate rtw_ratetable[] = { {.bitrate = 10, .hw_value = 0x00,}, {.bitrate = 20, .hw_value = 0x01,}, {.bitrate = 55, .hw_value = 0x02,}, {.bitrate = 110, .hw_value = 0x03,}, {.bitrate = 60, .hw_value = 0x04,}, {.bitrate = 90, .hw_value = 0x05,}, {.bitrate = 120, .hw_value = 0x06,}, {.bitrate = 180, .hw_value = 0x07,}, {.bitrate = 240, .hw_value = 0x08,}, {.bitrate = 360, .hw_value = 0x09,}, {.bitrate = 480, .hw_value = 0x0a,}, {.bitrate = 540, .hw_value = 0x0b,}, }; +static const struct ieee80211_iface_limit rtw_iface_limits[] = { + { + .max = 1, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_AP), + } +}; + +static const struct ieee80211_iface_combination rtw_iface_combs[] = { + { + .limits = rtw_iface_limits, + .n_limits = ARRAY_SIZE(rtw_iface_limits), + .max_interfaces = 2, + .num_different_channels = 1, + } +}; + u16 rtw_desc_to_bitrate(u8 desc_rate) { struct ieee80211_rate rate; if (WARN(desc_rate >= ARRAY_SIZE(rtw_ratetable), "invalid desc rate\n")) return 0; rate = rtw_ratetable[desc_rate]; return rate.bitrate; } static struct ieee80211_supported_band rtw_band_2ghz = { .band = NL80211_BAND_2GHZ, .channels = rtw_channeltable_2g, .n_channels = ARRAY_SIZE(rtw_channeltable_2g), .bitrates = rtw_ratetable, .n_bitrates = ARRAY_SIZE(rtw_ratetable), .ht_cap = {0}, .vht_cap = {0}, }; static struct ieee80211_supported_band rtw_band_5ghz = { .band = NL80211_BAND_5GHZ, .channels = rtw_channeltable_5g, .n_channels = ARRAY_SIZE(rtw_channeltable_5g), /* 5G has no CCK rates */ .bitrates = rtw_ratetable + 4, .n_bitrates = ARRAY_SIZE(rtw_ratetable) - 4, .ht_cap = {0}, .vht_cap = {0}, }; struct rtw_watch_dog_iter_data { struct rtw_dev *rtwdev; struct rtw_vif *rtwvif; }; static void rtw_dynamic_csi_rate(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif) { struct rtw_bf_info *bf_info = &rtwdev->bf_info; u8 fix_rate_enable = 0; u8 new_csi_rate_idx; if (rtwvif->bfee.role != RTW_BFEE_SU && rtwvif->bfee.role != RTW_BFEE_MU) return; rtw_chip_cfg_csi_rate(rtwdev, rtwdev->dm_info.min_rssi, bf_info->cur_csi_rpt_rate, fix_rate_enable, &new_csi_rate_idx); if (new_csi_rate_idx != bf_info->cur_csi_rpt_rate) bf_info->cur_csi_rpt_rate = new_csi_rate_idx; } -static void rtw_vif_watch_dog_iter(void *data, u8 *mac, - struct ieee80211_vif *vif) +static void rtw_vif_watch_dog_iter(void *data, struct ieee80211_vif *vif) { struct rtw_watch_dog_iter_data *iter_data = data; struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; if (vif->type == NL80211_IFTYPE_STATION) - if (vif->bss_conf.assoc) + if (vif->cfg.assoc) iter_data->rtwvif = rtwvif; rtw_dynamic_csi_rate(iter_data->rtwdev, rtwvif); rtwvif->stats.tx_unicast = 0; rtwvif->stats.rx_unicast = 0; rtwvif->stats.tx_cnt = 0; rtwvif->stats.rx_cnt = 0; } /* process TX/RX statistics periodically for hardware, * the information helps hardware to enhance performance */ static void rtw_watch_dog_work(struct work_struct *work) { struct rtw_dev *rtwdev = container_of(work, struct rtw_dev, watch_dog_work.work); struct rtw_traffic_stats *stats = &rtwdev->stats; struct rtw_watch_dog_iter_data data = {}; bool busy_traffic = test_bit(RTW_FLAG_BUSY_TRAFFIC, rtwdev->flags); bool ps_active; mutex_lock(&rtwdev->mutex); if (!test_bit(RTW_FLAG_RUNNING, rtwdev->flags)) goto unlock; ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->watch_dog_work, RTW_WATCH_DOG_DELAY_TIME); if (rtwdev->stats.tx_cnt > 100 || rtwdev->stats.rx_cnt > 100) set_bit(RTW_FLAG_BUSY_TRAFFIC, rtwdev->flags); else clear_bit(RTW_FLAG_BUSY_TRAFFIC, rtwdev->flags); rtw_coex_wl_status_check(rtwdev); rtw_coex_query_bt_hid_list(rtwdev); if (busy_traffic != test_bit(RTW_FLAG_BUSY_TRAFFIC, rtwdev->flags)) rtw_coex_wl_status_change_notify(rtwdev, 0); if (stats->tx_cnt > RTW_LPS_THRESHOLD || stats->rx_cnt > RTW_LPS_THRESHOLD) ps_active = true; else ps_active = false; ewma_tp_add(&stats->tx_ewma_tp, (u32)(stats->tx_unicast >> RTW_TP_SHIFT)); ewma_tp_add(&stats->rx_ewma_tp, (u32)(stats->rx_unicast >> RTW_TP_SHIFT)); stats->tx_throughput = ewma_tp_read(&stats->tx_ewma_tp); stats->rx_throughput = ewma_tp_read(&stats->rx_ewma_tp); /* reset tx/rx statictics */ stats->tx_unicast = 0; stats->rx_unicast = 0; stats->tx_cnt = 0; stats->rx_cnt = 0; if (test_bit(RTW_FLAG_SCANNING, rtwdev->flags)) goto unlock; /* make sure BB/RF is working for dynamic mech */ rtw_leave_lps(rtwdev); rtw_phy_dynamic_mechanism(rtwdev); data.rtwdev = rtwdev; - /* use atomic version to avoid taking local->iflist_mtx mutex */ - rtw_iterate_vifs_atomic(rtwdev, rtw_vif_watch_dog_iter, &data); + /* rtw_iterate_vifs internally uses an atomic iterator which is needed + * to avoid taking local->iflist_mtx mutex + */ + rtw_iterate_vifs(rtwdev, rtw_vif_watch_dog_iter, &data); /* fw supports only one station associated to enter lps, if there are * more than two stations associated to the AP, then we can not enter * lps, because fw does not handle the overlapped beacon interval * - * mac80211 should iterate vifs and determine if driver can enter - * ps by passing IEEE80211_CONF_PS to us, all we need to do is to + * rtw_recalc_lps() iterate vifs and determine if driver can enter + * ps by vif->type and vif->cfg.ps, all we need to do here is to * get that vif and check if device is having traffic more than the * threshold. */ if (rtwdev->ps_enabled && data.rtwvif && !ps_active && - !rtwdev->beacon_loss) + !rtwdev->beacon_loss && !rtwdev->ap_active) rtw_enter_lps(rtwdev, data.rtwvif->port); rtwdev->watch_dog_cnt++; unlock: mutex_unlock(&rtwdev->mutex); } static void rtw_c2h_work(struct work_struct *work) { struct rtw_dev *rtwdev = container_of(work, struct rtw_dev, c2h_work); struct sk_buff *skb, *tmp; skb_queue_walk_safe(&rtwdev->c2h_queue, skb, tmp) { skb_unlink(skb, &rtwdev->c2h_queue); rtw_fw_c2h_cmd_handle(rtwdev, skb); dev_kfree_skb_any(skb); } } static void rtw_ips_work(struct work_struct *work) { struct rtw_dev *rtwdev = container_of(work, struct rtw_dev, ips_work); mutex_lock(&rtwdev->mutex); if (rtwdev->hw->conf.flags & IEEE80211_CONF_IDLE) rtw_enter_ips(rtwdev); mutex_unlock(&rtwdev->mutex); } static u8 rtw_acquire_macid(struct rtw_dev *rtwdev) { unsigned long mac_id; mac_id = find_first_zero_bit(rtwdev->mac_id_map, RTW_MAX_MAC_ID_NUM); if (mac_id < RTW_MAX_MAC_ID_NUM) set_bit(mac_id, rtwdev->mac_id_map); return mac_id; } +static void rtw_sta_rc_work(struct work_struct *work) +{ + struct rtw_sta_info *si = container_of(work, struct rtw_sta_info, + rc_work); + struct rtw_dev *rtwdev = si->rtwdev; + + mutex_lock(&rtwdev->mutex); + rtw_update_sta_info(rtwdev, si, true); + mutex_unlock(&rtwdev->mutex); +} + int rtw_sta_add(struct rtw_dev *rtwdev, struct ieee80211_sta *sta, struct ieee80211_vif *vif) { struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv; + struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; int i; si->mac_id = rtw_acquire_macid(rtwdev); if (si->mac_id >= RTW_MAX_MAC_ID_NUM) return -ENOSPC; + if (vif->type == NL80211_IFTYPE_STATION && vif->cfg.assoc == 0) + rtwvif->mac_id = si->mac_id; + si->rtwdev = rtwdev; si->sta = sta; si->vif = vif; si->init_ra_lv = 1; ewma_rssi_init(&si->avg_rssi); for (i = 0; i < ARRAY_SIZE(sta->txq); i++) rtw_txq_init(rtwdev, sta->txq[i]); + INIT_WORK(&si->rc_work, rtw_sta_rc_work); rtw_update_sta_info(rtwdev, si, true); rtw_fw_media_status_report(rtwdev, si->mac_id, true); rtwdev->sta_cnt++; rtwdev->beacon_loss = false; #if defined(__linux__) rtw_dbg(rtwdev, RTW_DBG_STATE, "sta %pM joined with macid %d\n", sta->addr, si->mac_id); #elif defined(__FreeBSD__) rtw_dbg(rtwdev, RTW_DBG_STATE, "sta %6D joined with macid %d\n", sta->addr, ":", si->mac_id); #endif return 0; } void rtw_sta_remove(struct rtw_dev *rtwdev, struct ieee80211_sta *sta, bool fw_exist) { struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv; int i; + cancel_work_sync(&si->rc_work); + rtw_release_macid(rtwdev, si->mac_id); if (fw_exist) rtw_fw_media_status_report(rtwdev, si->mac_id, false); for (i = 0; i < ARRAY_SIZE(sta->txq); i++) rtw_txq_cleanup(rtwdev, sta->txq[i]); kfree(si->mask); rtwdev->sta_cnt--; #if defined(__linux__) rtw_dbg(rtwdev, RTW_DBG_STATE, "sta %pM with macid %d left\n", sta->addr, si->mac_id); #elif defined(__FreeBSD__) rtw_dbg(rtwdev, RTW_DBG_STATE, "sta %6D with macid %d left\n", sta->addr, ":", si->mac_id); #endif } struct rtw_fwcd_hdr { u32 item; u32 size; u32 padding1; u32 padding2; } __packed; static int rtw_fwcd_prep(struct rtw_dev *rtwdev) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_fwcd_desc *desc = &rtwdev->fw.fwcd_desc; const struct rtw_fwcd_segs *segs = chip->fwcd_segs; u32 prep_size = chip->fw_rxff_size + sizeof(struct rtw_fwcd_hdr); u8 i; if (segs) { prep_size += segs->num * sizeof(struct rtw_fwcd_hdr); for (i = 0; i < segs->num; i++) prep_size += segs->segs[i]; } desc->data = vmalloc(prep_size); if (!desc->data) return -ENOMEM; desc->size = prep_size; desc->next = desc->data; return 0; } static u8 *rtw_fwcd_next(struct rtw_dev *rtwdev, u32 item, u32 size) { struct rtw_fwcd_desc *desc = &rtwdev->fw.fwcd_desc; struct rtw_fwcd_hdr *hdr; u8 *next; if (!desc->data) { rtw_dbg(rtwdev, RTW_DBG_FW, "fwcd isn't prepared successfully\n"); return NULL; } next = desc->next + sizeof(struct rtw_fwcd_hdr); if (next - desc->data + size > desc->size) { rtw_dbg(rtwdev, RTW_DBG_FW, "fwcd isn't prepared enough\n"); return NULL; } hdr = (struct rtw_fwcd_hdr *)(desc->next); hdr->item = item; hdr->size = size; hdr->padding1 = 0x01234567; hdr->padding2 = 0x89abcdef; desc->next = next + size; return next; } static void rtw_fwcd_dump(struct rtw_dev *rtwdev) { struct rtw_fwcd_desc *desc = &rtwdev->fw.fwcd_desc; rtw_dbg(rtwdev, RTW_DBG_FW, "dump fwcd\n"); /* Data will be freed after lifetime of device coredump. After calling * dev_coredump, data is supposed to be handled by the device coredump * framework. Note that a new dump will be discarded if a previous one * hasn't been released yet. */ dev_coredumpv(rtwdev->dev, desc->data, desc->size, GFP_KERNEL); } static void rtw_fwcd_free(struct rtw_dev *rtwdev, bool free_self) { struct rtw_fwcd_desc *desc = &rtwdev->fw.fwcd_desc; if (free_self) { rtw_dbg(rtwdev, RTW_DBG_FW, "free fwcd by self\n"); vfree(desc->data); } desc->data = NULL; desc->next = NULL; } static int rtw_fw_dump_crash_log(struct rtw_dev *rtwdev) { u32 size = rtwdev->chip->fw_rxff_size; u32 *buf; u8 seq; buf = (u32 *)rtw_fwcd_next(rtwdev, RTW_FWCD_TLV, size); if (!buf) return -ENOMEM; if (rtw_fw_dump_fifo(rtwdev, RTW_FW_FIFO_SEL_RXBUF_FW, 0, size, buf)) { rtw_dbg(rtwdev, RTW_DBG_FW, "dump fw fifo fail\n"); return -EINVAL; } if (GET_FW_DUMP_LEN(buf) == 0) { rtw_dbg(rtwdev, RTW_DBG_FW, "fw crash dump's length is 0\n"); return -EINVAL; } seq = GET_FW_DUMP_SEQ(buf); if (seq > 0) { rtw_dbg(rtwdev, RTW_DBG_FW, "fw crash dump's seq is wrong: %d\n", seq); return -EINVAL; } return 0; } int rtw_dump_fw(struct rtw_dev *rtwdev, const u32 ocp_src, u32 size, u32 fwcd_item) { u32 rxff = rtwdev->chip->fw_rxff_size; u32 dump_size, done_size = 0; u8 *buf; int ret; buf = rtw_fwcd_next(rtwdev, fwcd_item, size); if (!buf) return -ENOMEM; while (size) { dump_size = size > rxff ? rxff : size; ret = rtw_ddma_to_fw_fifo(rtwdev, ocp_src + done_size, dump_size); if (ret) { rtw_err(rtwdev, "ddma fw 0x%x [+0x%x] to fw fifo fail\n", ocp_src, done_size); return ret; } ret = rtw_fw_dump_fifo(rtwdev, RTW_FW_FIFO_SEL_RXBUF_FW, 0, dump_size, (u32 *)(buf + done_size)); if (ret) { rtw_err(rtwdev, "dump fw 0x%x [+0x%x] from fw fifo fail\n", ocp_src, done_size); return ret; } size -= dump_size; done_size += dump_size; } return 0; } EXPORT_SYMBOL(rtw_dump_fw); int rtw_dump_reg(struct rtw_dev *rtwdev, const u32 addr, const u32 size) { u8 *buf; u32 i; if (addr & 0x3) { WARN(1, "should be 4-byte aligned, addr = 0x%08x\n", addr); return -EINVAL; } buf = rtw_fwcd_next(rtwdev, RTW_FWCD_REG, size); if (!buf) return -ENOMEM; for (i = 0; i < size; i += 4) *(u32 *)(buf + i) = rtw_read32(rtwdev, addr + i); return 0; } EXPORT_SYMBOL(rtw_dump_reg); void rtw_vif_assoc_changed(struct rtw_vif *rtwvif, struct ieee80211_bss_conf *conf) { - if (conf && conf->assoc) { - rtwvif->aid = conf->aid; + struct ieee80211_vif *vif = NULL; + + if (conf) + vif = container_of(conf, struct ieee80211_vif, bss_conf); + + if (conf && vif->cfg.assoc) { + rtwvif->aid = vif->cfg.aid; rtwvif->net_type = RTW_NET_MGD_LINKED; } else { rtwvif->aid = 0; rtwvif->net_type = RTW_NET_NO_LINK; } } static void rtw_reset_key_iter(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key, void *data) { struct rtw_dev *rtwdev = (struct rtw_dev *)data; struct rtw_sec_desc *sec = &rtwdev->sec; rtw_sec_clear_cam(rtwdev, sec, key->hw_key_idx); } static void rtw_reset_sta_iter(void *data, struct ieee80211_sta *sta) { struct rtw_dev *rtwdev = (struct rtw_dev *)data; if (rtwdev->sta_cnt == 0) { rtw_warn(rtwdev, "sta count before reset should not be 0\n"); return; } rtw_sta_remove(rtwdev, sta, false); } static void rtw_reset_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { struct rtw_dev *rtwdev = (struct rtw_dev *)data; struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; rtw_bf_disassoc(rtwdev, vif, NULL); rtw_vif_assoc_changed(rtwvif, NULL); rtw_txq_cleanup(rtwdev, vif->txq); } void rtw_fw_recovery(struct rtw_dev *rtwdev) { if (!test_bit(RTW_FLAG_RESTARTING, rtwdev->flags)) ieee80211_queue_work(rtwdev->hw, &rtwdev->fw_recovery_work); } static void __fw_recovery_work(struct rtw_dev *rtwdev) { int ret = 0; set_bit(RTW_FLAG_RESTARTING, rtwdev->flags); clear_bit(RTW_FLAG_RESTART_TRIGGERING, rtwdev->flags); ret = rtw_fwcd_prep(rtwdev); if (ret) goto free; ret = rtw_fw_dump_crash_log(rtwdev); if (ret) goto free; ret = rtw_chip_dump_fw_crash(rtwdev); if (ret) goto free; rtw_fwcd_dump(rtwdev); free: rtw_fwcd_free(rtwdev, !!ret); rtw_write8(rtwdev, REG_MCU_TST_CFG, 0); WARN(1, "firmware crash, start reset and recover\n"); rcu_read_lock(); rtw_iterate_keys_rcu(rtwdev, NULL, rtw_reset_key_iter, rtwdev); rcu_read_unlock(); rtw_iterate_stas_atomic(rtwdev, rtw_reset_sta_iter, rtwdev); rtw_iterate_vifs_atomic(rtwdev, rtw_reset_vif_iter, rtwdev); + bitmap_zero(rtwdev->hw_port, RTW_PORT_NUM); rtw_enter_ips(rtwdev); } static void rtw_fw_recovery_work(struct work_struct *work) { struct rtw_dev *rtwdev = container_of(work, struct rtw_dev, fw_recovery_work); mutex_lock(&rtwdev->mutex); __fw_recovery_work(rtwdev); mutex_unlock(&rtwdev->mutex); ieee80211_restart_hw(rtwdev->hw); } struct rtw_txq_ba_iter_data { }; static void rtw_txq_ba_iter(void *data, struct ieee80211_sta *sta) { struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv; int ret; u8 tid; tid = find_first_bit(si->tid_ba, IEEE80211_NUM_TIDS); while (tid != IEEE80211_NUM_TIDS) { clear_bit(tid, si->tid_ba); ret = ieee80211_start_tx_ba_session(sta, tid, 0); if (ret == -EINVAL) { struct ieee80211_txq *txq; struct rtw_txq *rtwtxq; txq = sta->txq[tid]; rtwtxq = (struct rtw_txq *)txq->drv_priv; set_bit(RTW_TXQ_BLOCK_BA, &rtwtxq->flags); } tid = find_first_bit(si->tid_ba, IEEE80211_NUM_TIDS); } } static void rtw_txq_ba_work(struct work_struct *work) { struct rtw_dev *rtwdev = container_of(work, struct rtw_dev, ba_work); struct rtw_txq_ba_iter_data data; rtw_iterate_stas_atomic(rtwdev, rtw_txq_ba_iter, &data); } void rtw_set_rx_freq_band(struct rtw_rx_pkt_stat *pkt_stat, u8 channel) { if (IS_CH_2G_BAND(channel)) pkt_stat->band = NL80211_BAND_2GHZ; else if (IS_CH_5G_BAND(channel)) pkt_stat->band = NL80211_BAND_5GHZ; else return; pkt_stat->freq = ieee80211_channel_to_frequency(channel, pkt_stat->band); } EXPORT_SYMBOL(rtw_set_rx_freq_band); void rtw_set_dtim_period(struct rtw_dev *rtwdev, int dtim_period) { rtw_write32_set(rtwdev, REG_TCR, BIT_TCR_UPDATE_TIMIE); rtw_write8(rtwdev, REG_DTIM_COUNTER_ROOT, dtim_period - 1); } +void rtw_update_channel(struct rtw_dev *rtwdev, u8 center_channel, + u8 primary_channel, enum rtw_supported_band band, + enum rtw_bandwidth bandwidth) +{ + enum nl80211_band nl_band = rtw_hw_to_nl80211_band(band); + struct rtw_hal *hal = &rtwdev->hal; + u8 *cch_by_bw = hal->cch_by_bw; + u32 center_freq, primary_freq; + enum rtw_sar_bands sar_band; + u8 primary_channel_idx; + + center_freq = ieee80211_channel_to_frequency(center_channel, nl_band); + primary_freq = ieee80211_channel_to_frequency(primary_channel, nl_band); + + /* assign the center channel used while 20M bw is selected */ + cch_by_bw[RTW_CHANNEL_WIDTH_20] = primary_channel; + + /* assign the center channel used while current bw is selected */ + cch_by_bw[bandwidth] = center_channel; + + switch (bandwidth) { + case RTW_CHANNEL_WIDTH_20: + default: + primary_channel_idx = RTW_SC_DONT_CARE; + break; + case RTW_CHANNEL_WIDTH_40: + if (primary_freq > center_freq) + primary_channel_idx = RTW_SC_20_UPPER; + else + primary_channel_idx = RTW_SC_20_LOWER; + break; + case RTW_CHANNEL_WIDTH_80: + if (primary_freq > center_freq) { + if (primary_freq - center_freq == 10) + primary_channel_idx = RTW_SC_20_UPPER; + else + primary_channel_idx = RTW_SC_20_UPMOST; + + /* assign the center channel used + * while 40M bw is selected + */ + cch_by_bw[RTW_CHANNEL_WIDTH_40] = center_channel + 4; + } else { + if (center_freq - primary_freq == 10) + primary_channel_idx = RTW_SC_20_LOWER; + else + primary_channel_idx = RTW_SC_20_LOWEST; + + /* assign the center channel used + * while 40M bw is selected + */ + cch_by_bw[RTW_CHANNEL_WIDTH_40] = center_channel - 4; + } + break; + } + + switch (center_channel) { + case 1 ... 14: + sar_band = RTW_SAR_BAND_0; + break; + case 36 ... 64: + sar_band = RTW_SAR_BAND_1; + break; + case 100 ... 144: + sar_band = RTW_SAR_BAND_3; + break; + case 149 ... 177: + sar_band = RTW_SAR_BAND_4; + break; + default: + WARN(1, "unknown ch(%u) to SAR band\n", center_channel); + sar_band = RTW_SAR_BAND_0; + break; + } + + hal->current_primary_channel_index = primary_channel_idx; + hal->current_band_width = bandwidth; + hal->primary_channel = primary_channel; + hal->current_channel = center_channel; + hal->current_band_type = band; + hal->sar_band = sar_band; +} + void rtw_get_channel_params(struct cfg80211_chan_def *chandef, struct rtw_channel_params *chan_params) { struct ieee80211_channel *channel = chandef->chan; enum nl80211_chan_width width = chandef->width; - u8 *cch_by_bw = chan_params->cch_by_bw; u32 primary_freq, center_freq; u8 center_chan; u8 bandwidth = RTW_CHANNEL_WIDTH_20; - u8 primary_chan_idx = 0; - u8 i; center_chan = channel->hw_value; primary_freq = channel->center_freq; center_freq = chandef->center_freq1; - /* assign the center channel used while 20M bw is selected */ - cch_by_bw[RTW_CHANNEL_WIDTH_20] = channel->hw_value; - switch (width) { case NL80211_CHAN_WIDTH_20_NOHT: case NL80211_CHAN_WIDTH_20: bandwidth = RTW_CHANNEL_WIDTH_20; - primary_chan_idx = RTW_SC_DONT_CARE; break; case NL80211_CHAN_WIDTH_40: bandwidth = RTW_CHANNEL_WIDTH_40; - if (primary_freq > center_freq) { - primary_chan_idx = RTW_SC_20_UPPER; + if (primary_freq > center_freq) center_chan -= 2; - } else { - primary_chan_idx = RTW_SC_20_LOWER; + else center_chan += 2; - } break; case NL80211_CHAN_WIDTH_80: bandwidth = RTW_CHANNEL_WIDTH_80; if (primary_freq > center_freq) { - if (primary_freq - center_freq == 10) { - primary_chan_idx = RTW_SC_20_UPPER; + if (primary_freq - center_freq == 10) center_chan -= 2; - } else { - primary_chan_idx = RTW_SC_20_UPMOST; + else center_chan -= 6; - } - /* assign the center channel used - * while 40M bw is selected - */ - cch_by_bw[RTW_CHANNEL_WIDTH_40] = center_chan + 4; } else { - if (center_freq - primary_freq == 10) { - primary_chan_idx = RTW_SC_20_LOWER; + if (center_freq - primary_freq == 10) center_chan += 2; - } else { - primary_chan_idx = RTW_SC_20_LOWEST; + else center_chan += 6; - } - /* assign the center channel used - * while 40M bw is selected - */ - cch_by_bw[RTW_CHANNEL_WIDTH_40] = center_chan - 4; } break; default: center_chan = 0; break; } chan_params->center_chan = center_chan; chan_params->bandwidth = bandwidth; - chan_params->primary_chan_idx = primary_chan_idx; - - /* assign the center channel used while current bw is selected */ - cch_by_bw[bandwidth] = center_chan; - - for (i = bandwidth + 1; i <= RTW_MAX_CHANNEL_WIDTH; i++) - cch_by_bw[i] = 0; + chan_params->primary_chan = channel->hw_value; } void rtw_set_channel(struct rtw_dev *rtwdev) { + const struct rtw_chip_info *chip = rtwdev->chip; struct ieee80211_hw *hw = rtwdev->hw; struct rtw_hal *hal = &rtwdev->hal; - struct rtw_chip_info *chip = rtwdev->chip; struct rtw_channel_params ch_param; - u8 center_chan, bandwidth, primary_chan_idx; - u8 i; + u8 center_chan, primary_chan, bandwidth, band; rtw_get_channel_params(&hw->conf.chandef, &ch_param); if (WARN(ch_param.center_chan == 0, "Invalid channel\n")) return; center_chan = ch_param.center_chan; + primary_chan = ch_param.primary_chan; bandwidth = ch_param.bandwidth; - primary_chan_idx = ch_param.primary_chan_idx; - - hal->current_band_width = bandwidth; - hal->current_channel = center_chan; - hal->current_primary_channel_index = primary_chan_idx; - hal->current_band_type = center_chan > 14 ? RTW_BAND_5G : RTW_BAND_2G; + band = ch_param.center_chan > 14 ? RTW_BAND_5G : RTW_BAND_2G; - switch (center_chan) { - case 1 ... 14: - hal->sar_band = RTW_SAR_BAND_0; - break; - case 36 ... 64: - hal->sar_band = RTW_SAR_BAND_1; - break; - case 100 ... 144: - hal->sar_band = RTW_SAR_BAND_3; - break; - case 149 ... 177: - hal->sar_band = RTW_SAR_BAND_4; - break; - default: - WARN(1, "unknown ch(%u) to SAR band\n", center_chan); - hal->sar_band = RTW_SAR_BAND_0; - break; - } + rtw_update_channel(rtwdev, center_chan, primary_chan, band, bandwidth); - for (i = RTW_CHANNEL_WIDTH_20; i <= RTW_MAX_CHANNEL_WIDTH; i++) - hal->cch_by_bw[i] = ch_param.cch_by_bw[i]; + if (rtwdev->scan_info.op_chan) + rtw_store_op_chan(rtwdev, true); - chip->ops->set_channel(rtwdev, center_chan, bandwidth, primary_chan_idx); + chip->ops->set_channel(rtwdev, center_chan, bandwidth, + hal->current_primary_channel_index); if (hal->current_band_type == RTW_BAND_5G) { rtw_coex_switchband_notify(rtwdev, COEX_SWITCH_TO_5G); } else { if (test_bit(RTW_FLAG_SCANNING, rtwdev->flags)) rtw_coex_switchband_notify(rtwdev, COEX_SWITCH_TO_24G); else rtw_coex_switchband_notify(rtwdev, COEX_SWITCH_TO_24G_NOFORSCAN); } rtw_phy_set_tx_power_level(rtwdev, center_chan); /* if the channel isn't set for scanning, we will do RF calibration * in ieee80211_ops::mgd_prepare_tx(). Performing the calibration * during scanning on each channel takes too long. */ if (!test_bit(RTW_FLAG_SCANNING, rtwdev->flags)) rtwdev->need_rfk = true; } void rtw_chip_prepare_tx(struct rtw_dev *rtwdev) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; if (rtwdev->need_rfk) { rtwdev->need_rfk = false; chip->ops->phy_calibration(rtwdev); } } static void rtw_vif_write_addr(struct rtw_dev *rtwdev, u32 start, u8 *addr) { int i; for (i = 0; i < ETH_ALEN; i++) rtw_write8(rtwdev, start + i, addr[i]); } void rtw_vif_port_config(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif, u32 config) { u32 addr, mask; if (config & PORT_SET_MAC_ADDR) { addr = rtwvif->conf->mac_addr.addr; rtw_vif_write_addr(rtwdev, addr, rtwvif->mac_addr); } if (config & PORT_SET_BSSID) { addr = rtwvif->conf->bssid.addr; rtw_vif_write_addr(rtwdev, addr, rtwvif->bssid); } if (config & PORT_SET_NET_TYPE) { addr = rtwvif->conf->net_type.addr; mask = rtwvif->conf->net_type.mask; rtw_write32_mask(rtwdev, addr, mask, rtwvif->net_type); } if (config & PORT_SET_AID) { addr = rtwvif->conf->aid.addr; mask = rtwvif->conf->aid.mask; rtw_write32_mask(rtwdev, addr, mask, rtwvif->aid); } if (config & PORT_SET_BCN_CTRL) { addr = rtwvif->conf->bcn_ctrl.addr; mask = rtwvif->conf->bcn_ctrl.mask; rtw_write8_mask(rtwdev, addr, mask, rtwvif->bcn_ctrl); } } static u8 hw_bw_cap_to_bitamp(u8 bw_cap) { u8 bw = 0; switch (bw_cap) { case EFUSE_HW_CAP_IGNORE: case EFUSE_HW_CAP_SUPP_BW80: bw |= BIT(RTW_CHANNEL_WIDTH_80); fallthrough; case EFUSE_HW_CAP_SUPP_BW40: bw |= BIT(RTW_CHANNEL_WIDTH_40); fallthrough; default: bw |= BIT(RTW_CHANNEL_WIDTH_20); break; } return bw; } static void rtw_hw_config_rf_ant_num(struct rtw_dev *rtwdev, u8 hw_ant_num) { + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_hal *hal = &rtwdev->hal; - struct rtw_chip_info *chip = rtwdev->chip; if (hw_ant_num == EFUSE_HW_CAP_IGNORE || hw_ant_num >= hal->rf_path_num) return; switch (hw_ant_num) { case 1: hal->rf_type = RF_1T1R; hal->rf_path_num = 1; if (!chip->fix_rf_phy_num) hal->rf_phy_num = hal->rf_path_num; hal->antenna_tx = BB_PATH_A; hal->antenna_rx = BB_PATH_A; break; default: WARN(1, "invalid hw configuration from efuse\n"); break; } } static u64 get_vht_ra_mask(struct ieee80211_sta *sta) { u64 ra_mask = 0; u16 mcs_map = le16_to_cpu(sta->deflink.vht_cap.vht_mcs.rx_mcs_map); u8 vht_mcs_cap; int i, nss; /* 4SS, every two bits for MCS7/8/9 */ for (i = 0, nss = 12; i < 4; i++, mcs_map >>= 2, nss += 10) { vht_mcs_cap = mcs_map & 0x3; switch (vht_mcs_cap) { case 2: /* MCS9 */ ra_mask |= 0x3ffULL << nss; break; case 1: /* MCS8 */ ra_mask |= 0x1ffULL << nss; break; case 0: /* MCS7 */ ra_mask |= 0x0ffULL << nss; break; default: break; } } return ra_mask; } static u8 get_rate_id(u8 wireless_set, enum rtw_bandwidth bw_mode, u8 tx_num) { u8 rate_id = 0; switch (wireless_set) { case WIRELESS_CCK: rate_id = RTW_RATEID_B_20M; break; case WIRELESS_OFDM: rate_id = RTW_RATEID_G; break; case WIRELESS_CCK | WIRELESS_OFDM: rate_id = RTW_RATEID_BG; break; case WIRELESS_OFDM | WIRELESS_HT: if (tx_num == 1) rate_id = RTW_RATEID_GN_N1SS; else if (tx_num == 2) rate_id = RTW_RATEID_GN_N2SS; else if (tx_num == 3) rate_id = RTW_RATEID_ARFR5_N_3SS; break; case WIRELESS_CCK | WIRELESS_OFDM | WIRELESS_HT: if (bw_mode == RTW_CHANNEL_WIDTH_40) { if (tx_num == 1) rate_id = RTW_RATEID_BGN_40M_1SS; else if (tx_num == 2) rate_id = RTW_RATEID_BGN_40M_2SS; else if (tx_num == 3) rate_id = RTW_RATEID_ARFR5_N_3SS; else if (tx_num == 4) rate_id = RTW_RATEID_ARFR7_N_4SS; } else { if (tx_num == 1) rate_id = RTW_RATEID_BGN_20M_1SS; else if (tx_num == 2) rate_id = RTW_RATEID_BGN_20M_2SS; else if (tx_num == 3) rate_id = RTW_RATEID_ARFR5_N_3SS; else if (tx_num == 4) rate_id = RTW_RATEID_ARFR7_N_4SS; } break; case WIRELESS_OFDM | WIRELESS_VHT: if (tx_num == 1) rate_id = RTW_RATEID_ARFR1_AC_1SS; else if (tx_num == 2) rate_id = RTW_RATEID_ARFR0_AC_2SS; else if (tx_num == 3) rate_id = RTW_RATEID_ARFR4_AC_3SS; else if (tx_num == 4) rate_id = RTW_RATEID_ARFR6_AC_4SS; break; case WIRELESS_CCK | WIRELESS_OFDM | WIRELESS_VHT: if (bw_mode >= RTW_CHANNEL_WIDTH_80) { if (tx_num == 1) rate_id = RTW_RATEID_ARFR1_AC_1SS; else if (tx_num == 2) rate_id = RTW_RATEID_ARFR0_AC_2SS; else if (tx_num == 3) rate_id = RTW_RATEID_ARFR4_AC_3SS; else if (tx_num == 4) rate_id = RTW_RATEID_ARFR6_AC_4SS; } else { if (tx_num == 1) rate_id = RTW_RATEID_ARFR2_AC_2G_1SS; else if (tx_num == 2) rate_id = RTW_RATEID_ARFR3_AC_2G_2SS; else if (tx_num == 3) rate_id = RTW_RATEID_ARFR4_AC_3SS; else if (tx_num == 4) rate_id = RTW_RATEID_ARFR6_AC_4SS; } break; default: break; } return rate_id; } #define RA_MASK_CCK_RATES 0x0000f #define RA_MASK_OFDM_RATES 0x00ff0 #define RA_MASK_HT_RATES_1SS (0xff000ULL << 0) #define RA_MASK_HT_RATES_2SS (0xff000ULL << 8) #define RA_MASK_HT_RATES_3SS (0xff000ULL << 16) #define RA_MASK_HT_RATES (RA_MASK_HT_RATES_1SS | \ RA_MASK_HT_RATES_2SS | \ RA_MASK_HT_RATES_3SS) #define RA_MASK_VHT_RATES_1SS (0x3ff000ULL << 0) #define RA_MASK_VHT_RATES_2SS (0x3ff000ULL << 10) #define RA_MASK_VHT_RATES_3SS (0x3ff000ULL << 20) #define RA_MASK_VHT_RATES (RA_MASK_VHT_RATES_1SS | \ RA_MASK_VHT_RATES_2SS | \ RA_MASK_VHT_RATES_3SS) #define RA_MASK_CCK_IN_BG 0x00005 #define RA_MASK_CCK_IN_HT 0x00005 #define RA_MASK_CCK_IN_VHT 0x00005 #define RA_MASK_OFDM_IN_VHT 0x00010 #define RA_MASK_OFDM_IN_HT_2G 0x00010 #define RA_MASK_OFDM_IN_HT_5G 0x00030 static u64 rtw_rate_mask_rssi(struct rtw_sta_info *si, u8 wireless_set) { u8 rssi_level = si->rssi_level; if (wireless_set == WIRELESS_CCK) return 0xffffffffffffffffULL; if (rssi_level == 0) return 0xffffffffffffffffULL; else if (rssi_level == 1) return 0xfffffffffffffff0ULL; else if (rssi_level == 2) return 0xffffffffffffefe0ULL; else if (rssi_level == 3) return 0xffffffffffffcfc0ULL; else if (rssi_level == 4) return 0xffffffffffff8f80ULL; else return 0xffffffffffff0f00ULL; } static u64 rtw_rate_mask_recover(u64 ra_mask, u64 ra_mask_bak) { if ((ra_mask & ~(RA_MASK_CCK_RATES | RA_MASK_OFDM_RATES)) == 0) ra_mask |= (ra_mask_bak & ~(RA_MASK_CCK_RATES | RA_MASK_OFDM_RATES)); if (ra_mask == 0) ra_mask |= (ra_mask_bak & (RA_MASK_CCK_RATES | RA_MASK_OFDM_RATES)); return ra_mask; } static u64 rtw_rate_mask_cfg(struct rtw_dev *rtwdev, struct rtw_sta_info *si, u64 ra_mask, bool is_vht_enable) { struct rtw_hal *hal = &rtwdev->hal; const struct cfg80211_bitrate_mask *mask = si->mask; u64 cfg_mask = GENMASK_ULL(63, 0); u8 band; if (!si->use_cfg_mask) return ra_mask; band = hal->current_band_type; if (band == RTW_BAND_2G) { band = NL80211_BAND_2GHZ; cfg_mask = mask->control[band].legacy; } else if (band == RTW_BAND_5G) { band = NL80211_BAND_5GHZ; cfg_mask = u64_encode_bits(mask->control[band].legacy, RA_MASK_OFDM_RATES); } if (!is_vht_enable) { if (ra_mask & RA_MASK_HT_RATES_1SS) cfg_mask |= u64_encode_bits(mask->control[band].ht_mcs[0], RA_MASK_HT_RATES_1SS); if (ra_mask & RA_MASK_HT_RATES_2SS) cfg_mask |= u64_encode_bits(mask->control[band].ht_mcs[1], RA_MASK_HT_RATES_2SS); } else { if (ra_mask & RA_MASK_VHT_RATES_1SS) cfg_mask |= u64_encode_bits(mask->control[band].vht_mcs[0], RA_MASK_VHT_RATES_1SS); if (ra_mask & RA_MASK_VHT_RATES_2SS) cfg_mask |= u64_encode_bits(mask->control[band].vht_mcs[1], RA_MASK_VHT_RATES_2SS); } ra_mask &= cfg_mask; return ra_mask; } void rtw_update_sta_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si, bool reset_ra_mask) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; struct ieee80211_sta *sta = si->sta; struct rtw_efuse *efuse = &rtwdev->efuse; struct rtw_hal *hal = &rtwdev->hal; u8 wireless_set; u8 bw_mode; u8 rate_id; u8 rf_type = RF_1T1R; u8 stbc_en = 0; u8 ldpc_en = 0; u8 tx_num = 1; u64 ra_mask = 0; u64 ra_mask_bak = 0; bool is_vht_enable = false; bool is_support_sgi = false; if (sta->deflink.vht_cap.vht_supported) { is_vht_enable = true; ra_mask |= get_vht_ra_mask(sta); if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_MASK) stbc_en = VHT_STBC_EN; if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC) ldpc_en = VHT_LDPC_EN; } else if (sta->deflink.ht_cap.ht_supported) { ra_mask |= (sta->deflink.ht_cap.mcs.rx_mask[1] << 20) | (sta->deflink.ht_cap.mcs.rx_mask[0] << 12); if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_RX_STBC) stbc_en = HT_STBC_EN; if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING) ldpc_en = HT_LDPC_EN; } if (efuse->hw_cap.nss == 1 || rtwdev->hal.txrx_1ss) ra_mask &= RA_MASK_VHT_RATES_1SS | RA_MASK_HT_RATES_1SS; if (hal->current_band_type == RTW_BAND_5G) { ra_mask |= (u64)sta->deflink.supp_rates[NL80211_BAND_5GHZ] << 4; ra_mask_bak = ra_mask; if (sta->deflink.vht_cap.vht_supported) { ra_mask &= RA_MASK_VHT_RATES | RA_MASK_OFDM_IN_VHT; wireless_set = WIRELESS_OFDM | WIRELESS_VHT; } else if (sta->deflink.ht_cap.ht_supported) { ra_mask &= RA_MASK_HT_RATES | RA_MASK_OFDM_IN_HT_5G; wireless_set = WIRELESS_OFDM | WIRELESS_HT; } else { wireless_set = WIRELESS_OFDM; } dm_info->rrsr_val_init = RRSR_INIT_5G; } else if (hal->current_band_type == RTW_BAND_2G) { ra_mask |= sta->deflink.supp_rates[NL80211_BAND_2GHZ]; ra_mask_bak = ra_mask; if (sta->deflink.vht_cap.vht_supported) { ra_mask &= RA_MASK_VHT_RATES | RA_MASK_CCK_IN_VHT | RA_MASK_OFDM_IN_VHT; wireless_set = WIRELESS_CCK | WIRELESS_OFDM | WIRELESS_HT | WIRELESS_VHT; } else if (sta->deflink.ht_cap.ht_supported) { ra_mask &= RA_MASK_HT_RATES | RA_MASK_CCK_IN_HT | RA_MASK_OFDM_IN_HT_2G; wireless_set = WIRELESS_CCK | WIRELESS_OFDM | WIRELESS_HT; #if defined(__linux__) } else if (sta->deflink.supp_rates[0] <= 0xf) { #elif defined(__FreeBSD__) } else if (sta->deflink.supp_rates[NL80211_BAND_2GHZ] <= 0xf) { #endif wireless_set = WIRELESS_CCK; } else { ra_mask &= RA_MASK_OFDM_RATES | RA_MASK_CCK_IN_BG; wireless_set = WIRELESS_CCK | WIRELESS_OFDM; } dm_info->rrsr_val_init = RRSR_INIT_2G; } else { rtw_err(rtwdev, "Unknown band type\n"); ra_mask_bak = ra_mask; wireless_set = 0; } switch (sta->deflink.bandwidth) { case IEEE80211_STA_RX_BW_80: bw_mode = RTW_CHANNEL_WIDTH_80; is_support_sgi = sta->deflink.vht_cap.vht_supported && (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80); break; case IEEE80211_STA_RX_BW_40: bw_mode = RTW_CHANNEL_WIDTH_40; is_support_sgi = sta->deflink.ht_cap.ht_supported && (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_40); break; default: bw_mode = RTW_CHANNEL_WIDTH_20; is_support_sgi = sta->deflink.ht_cap.ht_supported && (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_20); break; } if (sta->deflink.vht_cap.vht_supported && ra_mask & 0xffc00000) { tx_num = 2; rf_type = RF_2T2R; } else if (sta->deflink.ht_cap.ht_supported && ra_mask & 0xfff00000) { tx_num = 2; rf_type = RF_2T2R; } rate_id = get_rate_id(wireless_set, bw_mode, tx_num); ra_mask &= rtw_rate_mask_rssi(si, wireless_set); ra_mask = rtw_rate_mask_recover(ra_mask, ra_mask_bak); ra_mask = rtw_rate_mask_cfg(rtwdev, si, ra_mask, is_vht_enable); si->bw_mode = bw_mode; si->stbc_en = stbc_en; si->ldpc_en = ldpc_en; si->rf_type = rf_type; - si->wireless_set = wireless_set; si->sgi_enable = is_support_sgi; si->vht_enable = is_vht_enable; si->ra_mask = ra_mask; si->rate_id = rate_id; rtw_fw_send_ra_info(rtwdev, si, reset_ra_mask); } static int rtw_wait_firmware_completion(struct rtw_dev *rtwdev) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_fw_state *fw; fw = &rtwdev->fw; wait_for_completion(&fw->completion); if (!fw->firmware) return -EINVAL; if (chip->wow_fw_name) { fw = &rtwdev->wow_fw; wait_for_completion(&fw->completion); if (!fw->firmware) return -EINVAL; } return 0; } static enum rtw_lps_deep_mode rtw_update_lps_deep_mode(struct rtw_dev *rtwdev, struct rtw_fw_state *fw) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; if (rtw_disable_lps_deep_mode || !chip->lps_deep_mode_supported || !fw->feature) return LPS_DEEP_MODE_NONE; if ((chip->lps_deep_mode_supported & BIT(LPS_DEEP_MODE_PG)) && rtw_fw_feature_check(fw, FW_FEATURE_PG)) return LPS_DEEP_MODE_PG; if ((chip->lps_deep_mode_supported & BIT(LPS_DEEP_MODE_LCLK)) && rtw_fw_feature_check(fw, FW_FEATURE_LCLK)) return LPS_DEEP_MODE_LCLK; return LPS_DEEP_MODE_NONE; } static int rtw_power_on(struct rtw_dev *rtwdev) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_fw_state *fw = &rtwdev->fw; bool wifi_only; int ret; ret = rtw_hci_setup(rtwdev); if (ret) { rtw_err(rtwdev, "failed to setup hci\n"); goto err; } /* power on MAC before firmware downloaded */ ret = rtw_mac_power_on(rtwdev); if (ret) { rtw_err(rtwdev, "failed to power on mac\n"); goto err; } ret = rtw_wait_firmware_completion(rtwdev); if (ret) { rtw_err(rtwdev, "failed to wait firmware completion\n"); goto err_off; } ret = rtw_download_firmware(rtwdev, fw); if (ret) { rtw_err(rtwdev, "failed to download firmware\n"); goto err_off; } /* config mac after firmware downloaded */ ret = rtw_mac_init(rtwdev); if (ret) { rtw_err(rtwdev, "failed to configure mac\n"); goto err_off; } chip->ops->phy_set_param(rtwdev); ret = rtw_hci_start(rtwdev); if (ret) { rtw_err(rtwdev, "failed to start hci\n"); goto err_off; } /* send H2C after HCI has started */ rtw_fw_send_general_info(rtwdev); rtw_fw_send_phydm_info(rtwdev); wifi_only = !rtwdev->efuse.btcoex; rtw_coex_power_on_setting(rtwdev); rtw_coex_init_hw_config(rtwdev, wifi_only); return 0; err_off: rtw_mac_power_off(rtwdev); err: return ret; } void rtw_core_fw_scan_notify(struct rtw_dev *rtwdev, bool start) { if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_NOTIFY_SCAN)) return; if (start) { rtw_fw_scan_notify(rtwdev, true); } else { reinit_completion(&rtwdev->fw_scan_density); rtw_fw_scan_notify(rtwdev, false); if (!wait_for_completion_timeout(&rtwdev->fw_scan_density, SCAN_NOTIFY_TIMEOUT)) rtw_warn(rtwdev, "firmware failed to report density after scan\n"); } } void rtw_core_scan_start(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif, const u8 *mac_addr, bool hw_scan) { u32 config = 0; int ret = 0; rtw_leave_lps(rtwdev); if (hw_scan && (rtwdev->hw->conf.flags & IEEE80211_CONF_IDLE)) { ret = rtw_leave_ips(rtwdev); if (ret) { rtw_err(rtwdev, "failed to leave idle state\n"); return; } } ether_addr_copy(rtwvif->mac_addr, mac_addr); config |= PORT_SET_MAC_ADDR; rtw_vif_port_config(rtwdev, rtwvif, config); rtw_coex_scan_notify(rtwdev, COEX_SCAN_START); rtw_core_fw_scan_notify(rtwdev, true); set_bit(RTW_FLAG_DIG_DISABLE, rtwdev->flags); set_bit(RTW_FLAG_SCANNING, rtwdev->flags); } void rtw_core_scan_complete(struct rtw_dev *rtwdev, struct ieee80211_vif *vif, bool hw_scan) { struct rtw_vif *rtwvif = vif ? (struct rtw_vif *)vif->drv_priv : NULL; u32 config = 0; if (!rtwvif) return; clear_bit(RTW_FLAG_SCANNING, rtwdev->flags); clear_bit(RTW_FLAG_DIG_DISABLE, rtwdev->flags); rtw_core_fw_scan_notify(rtwdev, false); ether_addr_copy(rtwvif->mac_addr, vif->addr); config |= PORT_SET_MAC_ADDR; rtw_vif_port_config(rtwdev, rtwvif, config); rtw_coex_scan_notify(rtwdev, COEX_SCAN_FINISH); if (hw_scan && (rtwdev->hw->conf.flags & IEEE80211_CONF_IDLE)) ieee80211_queue_work(rtwdev->hw, &rtwdev->ips_work); } int rtw_core_start(struct rtw_dev *rtwdev) { int ret; ret = rtw_power_on(rtwdev); if (ret) return ret; rtw_sec_enable_sec_engine(rtwdev); rtwdev->lps_conf.deep_mode = rtw_update_lps_deep_mode(rtwdev, &rtwdev->fw); rtwdev->lps_conf.wow_deep_mode = rtw_update_lps_deep_mode(rtwdev, &rtwdev->wow_fw); /* rcr reset after powered on */ rtw_write32(rtwdev, REG_RCR, rtwdev->hal.rcr); ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->watch_dog_work, RTW_WATCH_DOG_DELAY_TIME); set_bit(RTW_FLAG_RUNNING, rtwdev->flags); return 0; } static void rtw_power_off(struct rtw_dev *rtwdev) { rtw_hci_stop(rtwdev); rtw_coex_power_off_setting(rtwdev); rtw_mac_power_off(rtwdev); } void rtw_core_stop(struct rtw_dev *rtwdev) { struct rtw_coex *coex = &rtwdev->coex; clear_bit(RTW_FLAG_RUNNING, rtwdev->flags); clear_bit(RTW_FLAG_FW_RUNNING, rtwdev->flags); mutex_unlock(&rtwdev->mutex); cancel_work_sync(&rtwdev->c2h_work); cancel_work_sync(&rtwdev->update_beacon_work); cancel_delayed_work_sync(&rtwdev->watch_dog_work); cancel_delayed_work_sync(&coex->bt_relink_work); cancel_delayed_work_sync(&coex->bt_reenable_work); cancel_delayed_work_sync(&coex->defreeze_work); cancel_delayed_work_sync(&coex->wl_remain_work); cancel_delayed_work_sync(&coex->bt_remain_work); cancel_delayed_work_sync(&coex->wl_connecting_work); cancel_delayed_work_sync(&coex->bt_multi_link_remain_work); cancel_delayed_work_sync(&coex->wl_ccklock_work); mutex_lock(&rtwdev->mutex); rtw_power_off(rtwdev); } static void rtw_init_ht_cap(struct rtw_dev *rtwdev, struct ieee80211_sta_ht_cap *ht_cap) { + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_efuse *efuse = &rtwdev->efuse; - struct rtw_chip_info *chip = rtwdev->chip; ht_cap->ht_supported = true; ht_cap->cap = 0; ht_cap->cap |= IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_MAX_AMSDU | (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT); if (rtw_chip_has_rx_ldpc(rtwdev)) ht_cap->cap |= IEEE80211_HT_CAP_LDPC_CODING; if (rtw_chip_has_tx_stbc(rtwdev)) ht_cap->cap |= IEEE80211_HT_CAP_TX_STBC; if (efuse->hw_cap.bw & BIT(RTW_CHANNEL_WIDTH_40)) ht_cap->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_DSSSCCK40 | IEEE80211_HT_CAP_SGI_40; ht_cap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; ht_cap->ampdu_density = chip->ampdu_density; ht_cap->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; if (efuse->hw_cap.nss > 1) { ht_cap->mcs.rx_mask[0] = 0xFF; ht_cap->mcs.rx_mask[1] = 0xFF; ht_cap->mcs.rx_mask[4] = 0x01; ht_cap->mcs.rx_highest = cpu_to_le16(300); } else { ht_cap->mcs.rx_mask[0] = 0xFF; ht_cap->mcs.rx_mask[1] = 0x00; ht_cap->mcs.rx_mask[4] = 0x01; ht_cap->mcs.rx_highest = cpu_to_le16(150); } } static void rtw_init_vht_cap(struct rtw_dev *rtwdev, struct ieee80211_sta_vht_cap *vht_cap) { struct rtw_efuse *efuse = &rtwdev->efuse; u16 mcs_map; __le16 highest; if (efuse->hw_cap.ptcl != EFUSE_HW_CAP_IGNORE && efuse->hw_cap.ptcl != EFUSE_HW_CAP_PTCL_VHT) return; vht_cap->vht_supported = true; vht_cap->cap = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | IEEE80211_VHT_CAP_SHORT_GI_80 | IEEE80211_VHT_CAP_RXSTBC_1 | IEEE80211_VHT_CAP_HTC_VHT | IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK | 0; if (rtwdev->hal.rf_path_num > 1) vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC; vht_cap->cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE | IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE; vht_cap->cap |= (rtwdev->hal.bfee_sts_cap << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT); if (rtw_chip_has_rx_ldpc(rtwdev)) vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC; mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | IEEE80211_VHT_MCS_NOT_SUPPORTED << 4 | IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 | IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 | IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 | IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 | IEEE80211_VHT_MCS_NOT_SUPPORTED << 14; if (efuse->hw_cap.nss > 1) { highest = cpu_to_le16(780); mcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << 2; } else { highest = cpu_to_le16(390); mcs_map |= IEEE80211_VHT_MCS_NOT_SUPPORTED << 2; } vht_cap->vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map); vht_cap->vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map); vht_cap->vht_mcs.rx_highest = highest; vht_cap->vht_mcs.tx_highest = highest; } +static u16 rtw_get_max_scan_ie_len(struct rtw_dev *rtwdev) +{ + u16 len; + + len = rtwdev->chip->max_scan_ie_len; + + if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_SCAN_OFFLOAD) && + rtwdev->chip->id == RTW_CHIP_TYPE_8822C) + len = IEEE80211_MAX_DATA_LEN; + else if (rtw_fw_feature_ext_check(&rtwdev->fw, FW_FEATURE_EXT_OLD_PAGE_NUM)) + len -= RTW_OLD_PROBE_PG_CNT * TX_PAGE_SIZE; + + return len; +} + static void rtw_set_supported_band(struct ieee80211_hw *hw, - struct rtw_chip_info *chip) + const struct rtw_chip_info *chip) { struct rtw_dev *rtwdev = hw->priv; struct ieee80211_supported_band *sband; if (chip->band & RTW_BAND_2G) { sband = kmemdup(&rtw_band_2ghz, sizeof(*sband), GFP_KERNEL); if (!sband) goto err_out; if (chip->ht_supported) rtw_init_ht_cap(rtwdev, &sband->ht_cap); hw->wiphy->bands[NL80211_BAND_2GHZ] = sband; } if (chip->band & RTW_BAND_5G) { sband = kmemdup(&rtw_band_5ghz, sizeof(*sband), GFP_KERNEL); if (!sband) goto err_out; if (chip->ht_supported) rtw_init_ht_cap(rtwdev, &sband->ht_cap); if (chip->vht_supported) rtw_init_vht_cap(rtwdev, &sband->vht_cap); hw->wiphy->bands[NL80211_BAND_5GHZ] = sband; } return; err_out: rtw_err(rtwdev, "failed to set supported band\n"); } static void rtw_unset_supported_band(struct ieee80211_hw *hw, - struct rtw_chip_info *chip) + const struct rtw_chip_info *chip) { kfree(hw->wiphy->bands[NL80211_BAND_2GHZ]); kfree(hw->wiphy->bands[NL80211_BAND_5GHZ]); } static void rtw_vif_smps_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { struct rtw_dev *rtwdev = (struct rtw_dev *)data; - if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc) + if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc) return; if (rtwdev->hal.txrx_1ss) - ieee80211_request_smps(vif, IEEE80211_SMPS_STATIC); + ieee80211_request_smps(vif, 0, IEEE80211_SMPS_STATIC); else - ieee80211_request_smps(vif, IEEE80211_SMPS_OFF); + ieee80211_request_smps(vif, 0, IEEE80211_SMPS_OFF); } void rtw_set_txrx_1ss(struct rtw_dev *rtwdev, bool txrx_1ss) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_hal *hal = &rtwdev->hal; if (!chip->ops->config_txrx_mode || rtwdev->hal.txrx_1ss == txrx_1ss) return; rtwdev->hal.txrx_1ss = txrx_1ss; if (txrx_1ss) chip->ops->config_txrx_mode(rtwdev, BB_PATH_A, BB_PATH_A, false); else chip->ops->config_txrx_mode(rtwdev, hal->antenna_tx, hal->antenna_rx, false); rtw_iterate_vifs_atomic(rtwdev, rtw_vif_smps_iter, rtwdev); } static void __update_firmware_feature(struct rtw_dev *rtwdev, struct rtw_fw_state *fw) { u32 feature; const struct rtw_fw_hdr *fw_hdr = (const struct rtw_fw_hdr *)fw->firmware->data; feature = le32_to_cpu(fw_hdr->feature); fw->feature = feature & FW_FEATURE_SIG ? feature : 0; + + if (rtwdev->chip->id == RTW_CHIP_TYPE_8822C && + RTW_FW_SUIT_VER_CODE(rtwdev->fw) < RTW_FW_VER_CODE(9, 9, 13)) + fw->feature_ext |= FW_FEATURE_EXT_OLD_PAGE_NUM; } static void __update_firmware_info(struct rtw_dev *rtwdev, struct rtw_fw_state *fw) { const struct rtw_fw_hdr *fw_hdr = (const struct rtw_fw_hdr *)fw->firmware->data; fw->h2c_version = le16_to_cpu(fw_hdr->h2c_fmt_ver); fw->version = le16_to_cpu(fw_hdr->version); fw->sub_version = fw_hdr->subversion; fw->sub_index = fw_hdr->subindex; __update_firmware_feature(rtwdev, fw); } static void __update_firmware_info_legacy(struct rtw_dev *rtwdev, struct rtw_fw_state *fw) { struct rtw_fw_hdr_legacy *legacy = #if defined(__linux__) (struct rtw_fw_hdr_legacy *)fw->firmware->data; #elif defined(__FreeBSD__) __DECONST(struct rtw_fw_hdr_legacy *, fw->firmware->data); #endif fw->h2c_version = 0; fw->version = le16_to_cpu(legacy->version); fw->sub_version = legacy->subversion1; fw->sub_index = legacy->subversion2; } static void update_firmware_info(struct rtw_dev *rtwdev, struct rtw_fw_state *fw) { if (rtw_chip_wcpu_11n(rtwdev)) __update_firmware_info_legacy(rtwdev, fw); else __update_firmware_info(rtwdev, fw); } static void rtw_load_firmware_cb(const struct firmware *firmware, void *context) { struct rtw_fw_state *fw = context; struct rtw_dev *rtwdev = fw->rtwdev; if (!firmware || !firmware->data) { rtw_err(rtwdev, "failed to request firmware\n"); complete_all(&fw->completion); return; } fw->firmware = firmware; update_firmware_info(rtwdev, fw); complete_all(&fw->completion); - rtw_info(rtwdev, "Firmware version %u.%u.%u, H2C version %u\n", + rtw_info(rtwdev, "%sFirmware version %u.%u.%u, H2C version %u\n", + fw->type == RTW_WOWLAN_FW ? "WOW " : "", fw->version, fw->sub_version, fw->sub_index, fw->h2c_version); } static int rtw_load_firmware(struct rtw_dev *rtwdev, enum rtw_fw_type type) { const char *fw_name; struct rtw_fw_state *fw; int ret; switch (type) { case RTW_WOWLAN_FW: fw = &rtwdev->wow_fw; fw_name = rtwdev->chip->wow_fw_name; break; case RTW_NORMAL_FW: fw = &rtwdev->fw; fw_name = rtwdev->chip->fw_name; break; default: rtw_warn(rtwdev, "unsupported firmware type\n"); return -ENOENT; } + fw->type = type; fw->rtwdev = rtwdev; init_completion(&fw->completion); ret = request_firmware_nowait(THIS_MODULE, true, fw_name, rtwdev->dev, GFP_KERNEL, fw, rtw_load_firmware_cb); if (ret) { rtw_err(rtwdev, "failed to async firmware request\n"); return ret; } return 0; } static int rtw_chip_parameter_setup(struct rtw_dev *rtwdev) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_hal *hal = &rtwdev->hal; struct rtw_efuse *efuse = &rtwdev->efuse; switch (rtw_hci_type(rtwdev)) { case RTW_HCI_TYPE_PCIE: rtwdev->hci.rpwm_addr = 0x03d9; rtwdev->hci.cpwm_addr = 0x03da; break; + case RTW_HCI_TYPE_SDIO: + rtwdev->hci.rpwm_addr = REG_SDIO_HRPWM1; + rtwdev->hci.cpwm_addr = REG_SDIO_HCPWM1_V2; + break; + case RTW_HCI_TYPE_USB: + rtwdev->hci.rpwm_addr = 0xfe58; + rtwdev->hci.cpwm_addr = 0xfe57; + break; default: rtw_err(rtwdev, "unsupported hci type\n"); return -EINVAL; } hal->chip_version = rtw_read32(rtwdev, REG_SYS_CFG1); hal->cut_version = BIT_GET_CHIP_VER(hal->chip_version); hal->mp_chip = (hal->chip_version & BIT_RTL_ID) ? 0 : 1; if (hal->chip_version & BIT_RF_TYPE_ID) { hal->rf_type = RF_2T2R; hal->rf_path_num = 2; hal->antenna_tx = BB_PATH_AB; hal->antenna_rx = BB_PATH_AB; } else { hal->rf_type = RF_1T1R; hal->rf_path_num = 1; hal->antenna_tx = BB_PATH_A; hal->antenna_rx = BB_PATH_A; } hal->rf_phy_num = chip->fix_rf_phy_num ? chip->fix_rf_phy_num : hal->rf_path_num; efuse->physical_size = chip->phy_efuse_size; efuse->logical_size = chip->log_efuse_size; efuse->protect_size = chip->ptct_efuse_size; /* default use ack */ rtwdev->hal.rcr |= BIT_VHT_DACK; hal->bfee_sts_cap = 3; return 0; } static int rtw_chip_efuse_enable(struct rtw_dev *rtwdev) { struct rtw_fw_state *fw = &rtwdev->fw; int ret; ret = rtw_hci_setup(rtwdev); if (ret) { rtw_err(rtwdev, "failed to setup hci\n"); goto err; } ret = rtw_mac_power_on(rtwdev); if (ret) { rtw_err(rtwdev, "failed to power on mac\n"); goto err; } rtw_write8(rtwdev, REG_C2HEVT, C2H_HW_FEATURE_DUMP); wait_for_completion(&fw->completion); if (!fw->firmware) { ret = -EINVAL; rtw_err(rtwdev, "failed to load firmware\n"); goto err; } ret = rtw_download_firmware(rtwdev, fw); if (ret) { rtw_err(rtwdev, "failed to download firmware\n"); goto err_off; } return 0; err_off: rtw_mac_power_off(rtwdev); err: return ret; } static int rtw_dump_hw_feature(struct rtw_dev *rtwdev) { struct rtw_efuse *efuse = &rtwdev->efuse; u8 hw_feature[HW_FEATURE_LEN]; u8 id; u8 bw; int i; id = rtw_read8(rtwdev, REG_C2HEVT); if (id != C2H_HW_FEATURE_REPORT) { rtw_err(rtwdev, "failed to read hw feature report\n"); return -EBUSY; } for (i = 0; i < HW_FEATURE_LEN; i++) hw_feature[i] = rtw_read8(rtwdev, REG_C2HEVT + 2 + i); rtw_write8(rtwdev, REG_C2HEVT, 0); bw = GET_EFUSE_HW_CAP_BW(hw_feature); efuse->hw_cap.bw = hw_bw_cap_to_bitamp(bw); efuse->hw_cap.hci = GET_EFUSE_HW_CAP_HCI(hw_feature); efuse->hw_cap.nss = GET_EFUSE_HW_CAP_NSS(hw_feature); efuse->hw_cap.ptcl = GET_EFUSE_HW_CAP_PTCL(hw_feature); efuse->hw_cap.ant_num = GET_EFUSE_HW_CAP_ANT_NUM(hw_feature); rtw_hw_config_rf_ant_num(rtwdev, efuse->hw_cap.ant_num); if (efuse->hw_cap.nss == EFUSE_HW_CAP_IGNORE || efuse->hw_cap.nss > rtwdev->hal.rf_path_num) efuse->hw_cap.nss = rtwdev->hal.rf_path_num; rtw_dbg(rtwdev, RTW_DBG_EFUSE, "hw cap: hci=0x%02x, bw=0x%02x, ptcl=0x%02x, ant_num=%d, nss=%d\n", efuse->hw_cap.hci, efuse->hw_cap.bw, efuse->hw_cap.ptcl, efuse->hw_cap.ant_num, efuse->hw_cap.nss); return 0; } static void rtw_chip_efuse_disable(struct rtw_dev *rtwdev) { rtw_hci_stop(rtwdev); rtw_mac_power_off(rtwdev); } static int rtw_chip_efuse_info_setup(struct rtw_dev *rtwdev) { struct rtw_efuse *efuse = &rtwdev->efuse; int ret; mutex_lock(&rtwdev->mutex); /* power on mac to read efuse */ ret = rtw_chip_efuse_enable(rtwdev); if (ret) goto out_unlock; ret = rtw_parse_efuse_map(rtwdev); if (ret) goto out_disable; ret = rtw_dump_hw_feature(rtwdev); if (ret) goto out_disable; ret = rtw_check_supported_rfe(rtwdev); if (ret) goto out_disable; if (efuse->crystal_cap == 0xff) efuse->crystal_cap = 0; if (efuse->pa_type_2g == 0xff) efuse->pa_type_2g = 0; if (efuse->pa_type_5g == 0xff) efuse->pa_type_5g = 0; if (efuse->lna_type_2g == 0xff) efuse->lna_type_2g = 0; if (efuse->lna_type_5g == 0xff) efuse->lna_type_5g = 0; if (efuse->channel_plan == 0xff) efuse->channel_plan = 0x7f; if (efuse->rf_board_option == 0xff) efuse->rf_board_option = 0; if (efuse->bt_setting & BIT(0)) efuse->share_ant = true; if (efuse->regd == 0xff) efuse->regd = 0; if (efuse->tx_bb_swing_setting_2g == 0xff) efuse->tx_bb_swing_setting_2g = 0; if (efuse->tx_bb_swing_setting_5g == 0xff) efuse->tx_bb_swing_setting_5g = 0; efuse->btcoex = (efuse->rf_board_option & 0xe0) == 0x20; efuse->ext_pa_2g = efuse->pa_type_2g & BIT(4) ? 1 : 0; efuse->ext_lna_2g = efuse->lna_type_2g & BIT(3) ? 1 : 0; efuse->ext_pa_5g = efuse->pa_type_5g & BIT(0) ? 1 : 0; efuse->ext_lna_2g = efuse->lna_type_5g & BIT(3) ? 1 : 0; out_disable: rtw_chip_efuse_disable(rtwdev); out_unlock: mutex_unlock(&rtwdev->mutex); return ret; } static int rtw_chip_board_info_setup(struct rtw_dev *rtwdev) { struct rtw_hal *hal = &rtwdev->hal; const struct rtw_rfe_def *rfe_def = rtw_get_rfe_def(rtwdev); if (!rfe_def) return -ENODEV; - rtw_phy_setup_phy_cond(rtwdev, 0); + rtw_phy_setup_phy_cond(rtwdev, hal->pkg_type); rtw_phy_init_tx_power(rtwdev); if (rfe_def->agc_btg_tbl) rtw_load_table(rtwdev, rfe_def->agc_btg_tbl); rtw_load_table(rtwdev, rfe_def->phy_pg_tbl); rtw_load_table(rtwdev, rfe_def->txpwr_lmt_tbl); rtw_phy_tx_power_by_rate_config(hal); rtw_phy_tx_power_limit_config(hal); return 0; } int rtw_chip_info_setup(struct rtw_dev *rtwdev) { int ret; ret = rtw_chip_parameter_setup(rtwdev); if (ret) { rtw_err(rtwdev, "failed to setup chip parameters\n"); goto err_out; } ret = rtw_chip_efuse_info_setup(rtwdev); if (ret) { rtw_err(rtwdev, "failed to setup chip efuse info\n"); goto err_out; } ret = rtw_chip_board_info_setup(rtwdev); if (ret) { rtw_err(rtwdev, "failed to setup chip board info\n"); goto err_out; } return 0; err_out: return ret; } EXPORT_SYMBOL(rtw_chip_info_setup); static void rtw_stats_init(struct rtw_dev *rtwdev) { struct rtw_traffic_stats *stats = &rtwdev->stats; struct rtw_dm_info *dm_info = &rtwdev->dm_info; int i; ewma_tp_init(&stats->tx_ewma_tp); ewma_tp_init(&stats->rx_ewma_tp); for (i = 0; i < RTW_EVM_NUM; i++) ewma_evm_init(&dm_info->ewma_evm[i]); for (i = 0; i < RTW_SNR_NUM; i++) ewma_snr_init(&dm_info->ewma_snr[i]); } int rtw_core_init(struct rtw_dev *rtwdev) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_coex *coex = &rtwdev->coex; int ret; INIT_LIST_HEAD(&rtwdev->rsvd_page_list); INIT_LIST_HEAD(&rtwdev->txqs); timer_setup(&rtwdev->tx_report.purge_timer, rtw_tx_report_purge_timer, 0); rtwdev->tx_wq = alloc_workqueue("rtw_tx_wq", WQ_UNBOUND | WQ_HIGHPRI, 0); + if (!rtwdev->tx_wq) { + rtw_warn(rtwdev, "alloc_workqueue rtw_tx_wq failed\n"); + return -ENOMEM; + } INIT_DELAYED_WORK(&rtwdev->watch_dog_work, rtw_watch_dog_work); INIT_DELAYED_WORK(&coex->bt_relink_work, rtw_coex_bt_relink_work); INIT_DELAYED_WORK(&coex->bt_reenable_work, rtw_coex_bt_reenable_work); INIT_DELAYED_WORK(&coex->defreeze_work, rtw_coex_defreeze_work); INIT_DELAYED_WORK(&coex->wl_remain_work, rtw_coex_wl_remain_work); INIT_DELAYED_WORK(&coex->bt_remain_work, rtw_coex_bt_remain_work); INIT_DELAYED_WORK(&coex->wl_connecting_work, rtw_coex_wl_connecting_work); INIT_DELAYED_WORK(&coex->bt_multi_link_remain_work, rtw_coex_bt_multi_link_remain_work); INIT_DELAYED_WORK(&coex->wl_ccklock_work, rtw_coex_wl_ccklock_work); INIT_WORK(&rtwdev->tx_work, rtw_tx_work); INIT_WORK(&rtwdev->c2h_work, rtw_c2h_work); INIT_WORK(&rtwdev->ips_work, rtw_ips_work); INIT_WORK(&rtwdev->fw_recovery_work, rtw_fw_recovery_work); INIT_WORK(&rtwdev->update_beacon_work, rtw_fw_update_beacon_work); INIT_WORK(&rtwdev->ba_work, rtw_txq_ba_work); skb_queue_head_init(&rtwdev->c2h_queue); skb_queue_head_init(&rtwdev->coex.queue); skb_queue_head_init(&rtwdev->tx_report.queue); - spin_lock_init(&rtwdev->rf_lock); - spin_lock_init(&rtwdev->h2c.lock); spin_lock_init(&rtwdev->txq_lock); spin_lock_init(&rtwdev->tx_report.q_lock); mutex_init(&rtwdev->mutex); - mutex_init(&rtwdev->coex.mutex); mutex_init(&rtwdev->hal.tx_power_mutex); init_waitqueue_head(&rtwdev->coex.wait); init_completion(&rtwdev->lps_leave_check); init_completion(&rtwdev->fw_scan_density); rtwdev->sec.total_cam_num = 32; rtwdev->hal.current_channel = 1; rtwdev->dm_info.fix_rate = U8_MAX; set_bit(RTW_BC_MC_MACID, rtwdev->mac_id_map); rtw_stats_init(rtwdev); /* default rx filter setting */ rtwdev->hal.rcr = BIT_APP_FCS | BIT_APP_MIC | BIT_APP_ICV | BIT_PKTCTL_DLEN | BIT_HTC_LOC_CTRL | BIT_APP_PHYSTS | BIT_AB | BIT_AM | BIT_APM; ret = rtw_load_firmware(rtwdev, RTW_NORMAL_FW); if (ret) { rtw_warn(rtwdev, "no firmware loaded\n"); - return ret; + goto out; } if (chip->wow_fw_name) { ret = rtw_load_firmware(rtwdev, RTW_WOWLAN_FW); if (ret) { rtw_warn(rtwdev, "no wow firmware loaded\n"); wait_for_completion(&rtwdev->fw.completion); if (rtwdev->fw.firmware) release_firmware(rtwdev->fw.firmware); - return ret; + goto out; } } #if defined(__FreeBSD__) rtw_wait_firmware_completion(rtwdev); #endif return 0; + +out: + destroy_workqueue(rtwdev->tx_wq); + return ret; } EXPORT_SYMBOL(rtw_core_init); void rtw_core_deinit(struct rtw_dev *rtwdev) { struct rtw_fw_state *fw = &rtwdev->fw; struct rtw_fw_state *wow_fw = &rtwdev->wow_fw; struct rtw_rsvd_page *rsvd_pkt, *tmp; unsigned long flags; rtw_wait_firmware_completion(rtwdev); if (fw->firmware) release_firmware(fw->firmware); if (wow_fw->firmware) release_firmware(wow_fw->firmware); destroy_workqueue(rtwdev->tx_wq); + timer_delete_sync(&rtwdev->tx_report.purge_timer); spin_lock_irqsave(&rtwdev->tx_report.q_lock, flags); skb_queue_purge(&rtwdev->tx_report.queue); - skb_queue_purge(&rtwdev->coex.queue); spin_unlock_irqrestore(&rtwdev->tx_report.q_lock, flags); + skb_queue_purge(&rtwdev->coex.queue); + skb_queue_purge(&rtwdev->c2h_queue); list_for_each_entry_safe(rsvd_pkt, tmp, &rtwdev->rsvd_page_list, build_list) { list_del(&rsvd_pkt->build_list); kfree(rsvd_pkt); } mutex_destroy(&rtwdev->mutex); - mutex_destroy(&rtwdev->coex.mutex); mutex_destroy(&rtwdev->hal.tx_power_mutex); } EXPORT_SYMBOL(rtw_core_deinit); int rtw_register_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw) { struct rtw_hal *hal = &rtwdev->hal; int max_tx_headroom = 0; int ret; - /* TODO: USB & SDIO may need extra room? */ max_tx_headroom = rtwdev->chip->tx_pkt_desc_sz; + if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_SDIO) + max_tx_headroom += RTW_SDIO_DATA_PTR_ALIGN; + hw->extra_tx_headroom = max_tx_headroom; hw->queues = IEEE80211_NUM_ACS; hw->txq_data_size = sizeof(struct rtw_txq); hw->sta_data_size = sizeof(struct rtw_sta_info); hw->vif_data_size = sizeof(struct rtw_vif); ieee80211_hw_set(hw, SIGNAL_DBM); ieee80211_hw_set(hw, RX_INCLUDES_FCS); ieee80211_hw_set(hw, AMPDU_AGGREGATION); ieee80211_hw_set(hw, MFP_CAPABLE); ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); ieee80211_hw_set(hw, SUPPORTS_PS); ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); ieee80211_hw_set(hw, SUPPORT_FAST_XMIT); ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU); ieee80211_hw_set(hw, HAS_RATE_CONTROL); ieee80211_hw_set(hw, TX_AMSDU); ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS); hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_MESH_POINT); hw->wiphy->available_antennas_tx = hal->antenna_tx; hw->wiphy->available_antennas_rx = hal->antenna_rx; hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | WIPHY_FLAG_TDLS_EXTERNAL_SETUP; hw->wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR; hw->wiphy->max_scan_ssids = RTW_SCAN_MAX_SSIDS; - hw->wiphy->max_scan_ie_len = RTW_SCAN_MAX_IE_LEN; + hw->wiphy->max_scan_ie_len = rtw_get_max_scan_ie_len(rtwdev); + + if (rtwdev->chip->id == RTW_CHIP_TYPE_8822C) { + hw->wiphy->iface_combinations = rtw_iface_combs; + hw->wiphy->n_iface_combinations = ARRAY_SIZE(rtw_iface_combs); + } wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0); wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_SCAN_RANDOM_SN); wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_SET_SCAN_DWELL); #ifdef CONFIG_PM hw->wiphy->wowlan = rtwdev->chip->wowlan_stub; hw->wiphy->max_sched_scan_ssids = rtwdev->chip->max_sched_scan_ssids; #endif rtw_set_supported_band(hw, rtwdev->chip); SET_IEEE80211_PERM_ADDR(hw, rtwdev->efuse.addr); hw->wiphy->sar_capa = &rtw_sar_capa; ret = rtw_regd_init(rtwdev); if (ret) { rtw_err(rtwdev, "failed to init regd\n"); return ret; } ret = ieee80211_register_hw(hw); if (ret) { rtw_err(rtwdev, "failed to register hw\n"); return ret; } ret = rtw_regd_hint(rtwdev); if (ret) { rtw_err(rtwdev, "failed to hint regd\n"); return ret; } rtw_debugfs_init(rtwdev); rtwdev->bf_info.bfer_mu_cnt = 0; rtwdev->bf_info.bfer_su_cnt = 0; return 0; } EXPORT_SYMBOL(rtw_register_hw); void rtw_unregister_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; ieee80211_unregister_hw(hw); rtw_unset_supported_band(hw, chip); } EXPORT_SYMBOL(rtw_unregister_hw); +static +void rtw_swap_reg_nbytes(struct rtw_dev *rtwdev, const struct rtw_hw_reg *reg1, + const struct rtw_hw_reg *reg2, u8 nbytes) +{ + u8 i; + + for (i = 0; i < nbytes; i++) { + u8 v1 = rtw_read8(rtwdev, reg1->addr + i); + u8 v2 = rtw_read8(rtwdev, reg2->addr + i); + + rtw_write8(rtwdev, reg1->addr + i, v2); + rtw_write8(rtwdev, reg2->addr + i, v1); + } +} + +static +void rtw_swap_reg_mask(struct rtw_dev *rtwdev, const struct rtw_hw_reg *reg1, + const struct rtw_hw_reg *reg2) +{ + u32 v1, v2; + + v1 = rtw_read32_mask(rtwdev, reg1->addr, reg1->mask); + v2 = rtw_read32_mask(rtwdev, reg2->addr, reg2->mask); + rtw_write32_mask(rtwdev, reg2->addr, reg2->mask, v1); + rtw_write32_mask(rtwdev, reg1->addr, reg1->mask, v2); +} + +struct rtw_iter_port_switch_data { + struct rtw_dev *rtwdev; + struct rtw_vif *rtwvif_ap; +}; + +static void rtw_port_switch_iter(void *data, struct ieee80211_vif *vif) +{ + struct rtw_iter_port_switch_data *iter_data = data; + struct rtw_dev *rtwdev = iter_data->rtwdev; + struct rtw_vif *rtwvif_target = (struct rtw_vif *)vif->drv_priv; + struct rtw_vif *rtwvif_ap = iter_data->rtwvif_ap; + const struct rtw_hw_reg *reg1, *reg2; + + if (rtwvif_target->port != RTW_PORT_0) + return; + + rtw_dbg(rtwdev, RTW_DBG_STATE, "AP port switch from %d -> %d\n", + rtwvif_ap->port, rtwvif_target->port); + + /* Leave LPS so the value swapped are not in PS mode */ + rtw_leave_lps(rtwdev); + + reg1 = &rtwvif_ap->conf->net_type; + reg2 = &rtwvif_target->conf->net_type; + rtw_swap_reg_mask(rtwdev, reg1, reg2); + + reg1 = &rtwvif_ap->conf->mac_addr; + reg2 = &rtwvif_target->conf->mac_addr; + rtw_swap_reg_nbytes(rtwdev, reg1, reg2, ETH_ALEN); + + reg1 = &rtwvif_ap->conf->bssid; + reg2 = &rtwvif_target->conf->bssid; + rtw_swap_reg_nbytes(rtwdev, reg1, reg2, ETH_ALEN); + + reg1 = &rtwvif_ap->conf->bcn_ctrl; + reg2 = &rtwvif_target->conf->bcn_ctrl; + rtw_swap_reg_nbytes(rtwdev, reg1, reg2, 1); + + swap(rtwvif_target->port, rtwvif_ap->port); + swap(rtwvif_target->conf, rtwvif_ap->conf); + + rtw_fw_default_port(rtwdev, rtwvif_target); +} + +void rtw_core_port_switch(struct rtw_dev *rtwdev, struct ieee80211_vif *vif) +{ + struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; + struct rtw_iter_port_switch_data iter_data; + + if (vif->type != NL80211_IFTYPE_AP || rtwvif->port == RTW_PORT_0) + return; + + iter_data.rtwdev = rtwdev; + iter_data.rtwvif_ap = rtwvif; + rtw_iterate_vifs(rtwdev, rtw_port_switch_iter, &iter_data); +} + +static void rtw_check_sta_active_iter(void *data, struct ieee80211_vif *vif) +{ + struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; + bool *active = data; + + if (*active) + return; + + if (vif->type != NL80211_IFTYPE_STATION) + return; + + if (vif->cfg.assoc || !is_zero_ether_addr(rtwvif->bssid)) + *active = true; +} + +bool rtw_core_check_sta_active(struct rtw_dev *rtwdev) +{ + bool sta_active = false; + + rtw_iterate_vifs(rtwdev, rtw_check_sta_active_iter, &sta_active); + + return rtwdev->ap_active || sta_active; +} + +void rtw_core_enable_beacon(struct rtw_dev *rtwdev, bool enable) +{ + if (!rtwdev->ap_active) + return; + + if (enable) { + rtw_write32_set(rtwdev, REG_BCN_CTRL, BIT_EN_BCN_FUNCTION); + rtw_write32_clr(rtwdev, REG_TXPAUSE, BIT_HIGH_QUEUE); + } else { + rtw_write32_clr(rtwdev, REG_BCN_CTRL, BIT_EN_BCN_FUNCTION); + rtw_write32_set(rtwdev, REG_TXPAUSE, BIT_HIGH_QUEUE); + } +} + MODULE_AUTHOR("Realtek Corporation"); MODULE_DESCRIPTION("Realtek 802.11ac wireless core module"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sys/contrib/dev/rtw88/main.h b/sys/contrib/dev/rtw88/main.h index 4daa75ed8c66..e1d903bb88ee 100644 --- a/sys/contrib/dev/rtw88/main.h +++ b/sys/contrib/dev/rtw88/main.h @@ -1,2190 +1,2217 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* Copyright(c) 2018-2019 Realtek Corporation */ #ifndef __RTK_MAIN_H_ #define __RTK_MAIN_H_ #include #include #include #include #include #include #include #include #include #if defined(__FreeBSD__) #include #include #include #include #include #include #endif #include "util.h" #define RTW_MAX_MAC_ID_NUM 32 #define RTW_MAX_SEC_CAM_NUM 32 #define MAX_PG_CAM_BACKUP_NUM 8 #define RTW_SCAN_MAX_SSIDS 4 -#define RTW_SCAN_MAX_IE_LEN 128 #define RTW_MAX_PATTERN_NUM 12 #define RTW_MAX_PATTERN_MASK_SIZE 16 #define RTW_MAX_PATTERN_SIZE 128 #define RTW_WATCH_DOG_DELAY_TIME round_jiffies_relative(HZ * 2) #define RFREG_MASK 0xfffff #define INV_RF_DATA 0xffffffff #define TX_PAGE_SIZE_SHIFT 7 +#define TX_PAGE_SIZE (1 << TX_PAGE_SIZE_SHIFT) #define RTW_CHANNEL_WIDTH_MAX 3 #define RTW_RF_PATH_MAX 4 #define HW_FEATURE_LEN 13 #define RTW_TP_SHIFT 18 /* bytes/2s --> Mbps */ extern bool rtw_bf_support; extern bool rtw_disable_lps_deep_mode; extern unsigned int rtw_debug_mask; extern bool rtw_edcca_enabled; extern const struct ieee80211_ops rtw_ops; #define RTW_MAX_CHANNEL_NUM_2G 14 #define RTW_MAX_CHANNEL_NUM_5G 49 struct rtw_dev; enum rtw_hci_type { RTW_HCI_TYPE_PCIE, RTW_HCI_TYPE_USB, RTW_HCI_TYPE_SDIO, RTW_HCI_TYPE_UNDEFINE, }; struct rtw_hci { struct rtw_hci_ops *ops; enum rtw_hci_type type; u32 rpwm_addr; u32 cpwm_addr; u8 bulkout_num; }; #define IS_CH_5G_BAND_1(channel) ((channel) >= 36 && (channel <= 48)) #define IS_CH_5G_BAND_2(channel) ((channel) >= 52 && (channel <= 64)) #define IS_CH_5G_BAND_3(channel) ((channel) >= 100 && (channel <= 144)) #define IS_CH_5G_BAND_4(channel) ((channel) >= 149 && (channel <= 177)) #define IS_CH_5G_BAND_MID(channel) \ (IS_CH_5G_BAND_2(channel) || IS_CH_5G_BAND_3(channel)) #define IS_CH_2G_BAND(channel) ((channel) <= 14) #define IS_CH_5G_BAND(channel) \ (IS_CH_5G_BAND_1(channel) || IS_CH_5G_BAND_2(channel) || \ IS_CH_5G_BAND_3(channel) || IS_CH_5G_BAND_4(channel)) enum rtw_supported_band { RTW_BAND_2G = BIT(NL80211_BAND_2GHZ), RTW_BAND_5G = BIT(NL80211_BAND_5GHZ), RTW_BAND_60G = BIT(NL80211_BAND_60GHZ), }; -/* now, support upto 80M bw */ +/* now, support up to 80M bw */ #define RTW_MAX_CHANNEL_WIDTH RTW_CHANNEL_WIDTH_80 enum rtw_bandwidth { RTW_CHANNEL_WIDTH_20 = 0, RTW_CHANNEL_WIDTH_40 = 1, RTW_CHANNEL_WIDTH_80 = 2, RTW_CHANNEL_WIDTH_160 = 3, RTW_CHANNEL_WIDTH_80_80 = 4, RTW_CHANNEL_WIDTH_5 = 5, RTW_CHANNEL_WIDTH_10 = 6, }; enum rtw_sc_offset { RTW_SC_DONT_CARE = 0, RTW_SC_20_UPPER = 1, RTW_SC_20_LOWER = 2, RTW_SC_20_UPMOST = 3, RTW_SC_20_LOWEST = 4, RTW_SC_40_UPPER = 9, RTW_SC_40_LOWER = 10, }; enum rtw_net_type { RTW_NET_NO_LINK = 0, RTW_NET_AD_HOC = 1, RTW_NET_MGD_LINKED = 2, RTW_NET_AP_MODE = 3, }; enum rtw_rf_type { RF_1T1R = 0, RF_1T2R = 1, RF_2T2R = 2, RF_2T3R = 3, RF_2T4R = 4, RF_3T3R = 5, RF_3T4R = 6, RF_4T4R = 7, RF_TYPE_MAX, }; enum rtw_rf_path { RF_PATH_A = 0, RF_PATH_B = 1, RF_PATH_C = 2, RF_PATH_D = 3, }; enum rtw_bb_path { BB_PATH_A = BIT(0), BB_PATH_B = BIT(1), BB_PATH_C = BIT(2), BB_PATH_D = BIT(3), BB_PATH_AB = (BB_PATH_A | BB_PATH_B), BB_PATH_AC = (BB_PATH_A | BB_PATH_C), BB_PATH_AD = (BB_PATH_A | BB_PATH_D), BB_PATH_BC = (BB_PATH_B | BB_PATH_C), BB_PATH_BD = (BB_PATH_B | BB_PATH_D), BB_PATH_CD = (BB_PATH_C | BB_PATH_D), BB_PATH_ABC = (BB_PATH_A | BB_PATH_B | BB_PATH_C), BB_PATH_ABD = (BB_PATH_A | BB_PATH_B | BB_PATH_D), BB_PATH_ACD = (BB_PATH_A | BB_PATH_C | BB_PATH_D), BB_PATH_BCD = (BB_PATH_B | BB_PATH_C | BB_PATH_D), BB_PATH_ABCD = (BB_PATH_A | BB_PATH_B | BB_PATH_C | BB_PATH_D), }; enum rtw_rate_section { RTW_RATE_SECTION_CCK = 0, RTW_RATE_SECTION_OFDM, RTW_RATE_SECTION_HT_1S, RTW_RATE_SECTION_HT_2S, RTW_RATE_SECTION_VHT_1S, RTW_RATE_SECTION_VHT_2S, /* keep last */ RTW_RATE_SECTION_MAX, }; enum rtw_wireless_set { WIRELESS_CCK = 0x00000001, WIRELESS_OFDM = 0x00000002, WIRELESS_HT = 0x00000004, WIRELESS_VHT = 0x00000008, }; #define HT_STBC_EN BIT(0) #define VHT_STBC_EN BIT(1) #define HT_LDPC_EN BIT(0) #define VHT_LDPC_EN BIT(1) enum rtw_chip_type { RTW_CHIP_TYPE_8822B, RTW_CHIP_TYPE_8822C, RTW_CHIP_TYPE_8723D, RTW_CHIP_TYPE_8821C, }; enum rtw_tx_queue_type { /* the order of AC queues matters */ RTW_TX_QUEUE_BK = 0x0, RTW_TX_QUEUE_BE = 0x1, RTW_TX_QUEUE_VI = 0x2, RTW_TX_QUEUE_VO = 0x3, RTW_TX_QUEUE_BCN = 0x4, RTW_TX_QUEUE_MGMT = 0x5, RTW_TX_QUEUE_HI0 = 0x6, RTW_TX_QUEUE_H2C = 0x7, /* keep it last */ RTK_MAX_TX_QUEUE_NUM }; enum rtw_rx_queue_type { RTW_RX_QUEUE_MPDU = 0x0, RTW_RX_QUEUE_C2H = 0x1, /* keep it last */ RTK_MAX_RX_QUEUE_NUM }; enum rtw_fw_type { RTW_NORMAL_FW = 0x0, RTW_WOWLAN_FW = 0x1, }; enum rtw_rate_index { RTW_RATEID_BGN_40M_2SS = 0, RTW_RATEID_BGN_40M_1SS = 1, RTW_RATEID_BGN_20M_2SS = 2, RTW_RATEID_BGN_20M_1SS = 3, RTW_RATEID_GN_N2SS = 4, RTW_RATEID_GN_N1SS = 5, RTW_RATEID_BG = 6, RTW_RATEID_G = 7, RTW_RATEID_B_20M = 8, RTW_RATEID_ARFR0_AC_2SS = 9, RTW_RATEID_ARFR1_AC_1SS = 10, RTW_RATEID_ARFR2_AC_2G_1SS = 11, RTW_RATEID_ARFR3_AC_2G_2SS = 12, RTW_RATEID_ARFR4_AC_3SS = 13, RTW_RATEID_ARFR5_N_3SS = 14, RTW_RATEID_ARFR7_N_4SS = 15, RTW_RATEID_ARFR6_AC_4SS = 16 }; enum rtw_trx_desc_rate { DESC_RATE1M = 0x00, DESC_RATE2M = 0x01, DESC_RATE5_5M = 0x02, DESC_RATE11M = 0x03, DESC_RATE6M = 0x04, DESC_RATE9M = 0x05, DESC_RATE12M = 0x06, DESC_RATE18M = 0x07, DESC_RATE24M = 0x08, DESC_RATE36M = 0x09, DESC_RATE48M = 0x0a, DESC_RATE54M = 0x0b, DESC_RATEMCS0 = 0x0c, DESC_RATEMCS1 = 0x0d, DESC_RATEMCS2 = 0x0e, DESC_RATEMCS3 = 0x0f, DESC_RATEMCS4 = 0x10, DESC_RATEMCS5 = 0x11, DESC_RATEMCS6 = 0x12, DESC_RATEMCS7 = 0x13, DESC_RATEMCS8 = 0x14, DESC_RATEMCS9 = 0x15, DESC_RATEMCS10 = 0x16, DESC_RATEMCS11 = 0x17, DESC_RATEMCS12 = 0x18, DESC_RATEMCS13 = 0x19, DESC_RATEMCS14 = 0x1a, DESC_RATEMCS15 = 0x1b, DESC_RATEMCS16 = 0x1c, DESC_RATEMCS17 = 0x1d, DESC_RATEMCS18 = 0x1e, DESC_RATEMCS19 = 0x1f, DESC_RATEMCS20 = 0x20, DESC_RATEMCS21 = 0x21, DESC_RATEMCS22 = 0x22, DESC_RATEMCS23 = 0x23, DESC_RATEMCS24 = 0x24, DESC_RATEMCS25 = 0x25, DESC_RATEMCS26 = 0x26, DESC_RATEMCS27 = 0x27, DESC_RATEMCS28 = 0x28, DESC_RATEMCS29 = 0x29, DESC_RATEMCS30 = 0x2a, DESC_RATEMCS31 = 0x2b, DESC_RATEVHT1SS_MCS0 = 0x2c, DESC_RATEVHT1SS_MCS1 = 0x2d, DESC_RATEVHT1SS_MCS2 = 0x2e, DESC_RATEVHT1SS_MCS3 = 0x2f, DESC_RATEVHT1SS_MCS4 = 0x30, DESC_RATEVHT1SS_MCS5 = 0x31, DESC_RATEVHT1SS_MCS6 = 0x32, DESC_RATEVHT1SS_MCS7 = 0x33, DESC_RATEVHT1SS_MCS8 = 0x34, DESC_RATEVHT1SS_MCS9 = 0x35, DESC_RATEVHT2SS_MCS0 = 0x36, DESC_RATEVHT2SS_MCS1 = 0x37, DESC_RATEVHT2SS_MCS2 = 0x38, DESC_RATEVHT2SS_MCS3 = 0x39, DESC_RATEVHT2SS_MCS4 = 0x3a, DESC_RATEVHT2SS_MCS5 = 0x3b, DESC_RATEVHT2SS_MCS6 = 0x3c, DESC_RATEVHT2SS_MCS7 = 0x3d, DESC_RATEVHT2SS_MCS8 = 0x3e, DESC_RATEVHT2SS_MCS9 = 0x3f, DESC_RATEVHT3SS_MCS0 = 0x40, DESC_RATEVHT3SS_MCS1 = 0x41, DESC_RATEVHT3SS_MCS2 = 0x42, DESC_RATEVHT3SS_MCS3 = 0x43, DESC_RATEVHT3SS_MCS4 = 0x44, DESC_RATEVHT3SS_MCS5 = 0x45, DESC_RATEVHT3SS_MCS6 = 0x46, DESC_RATEVHT3SS_MCS7 = 0x47, DESC_RATEVHT3SS_MCS8 = 0x48, DESC_RATEVHT3SS_MCS9 = 0x49, DESC_RATEVHT4SS_MCS0 = 0x4a, DESC_RATEVHT4SS_MCS1 = 0x4b, DESC_RATEVHT4SS_MCS2 = 0x4c, DESC_RATEVHT4SS_MCS3 = 0x4d, DESC_RATEVHT4SS_MCS4 = 0x4e, DESC_RATEVHT4SS_MCS5 = 0x4f, DESC_RATEVHT4SS_MCS6 = 0x50, DESC_RATEVHT4SS_MCS7 = 0x51, DESC_RATEVHT4SS_MCS8 = 0x52, DESC_RATEVHT4SS_MCS9 = 0x53, DESC_RATE_MAX, }; enum rtw_regulatory_domains { RTW_REGD_FCC = 0, RTW_REGD_MKK = 1, RTW_REGD_ETSI = 2, RTW_REGD_IC = 3, RTW_REGD_KCC = 4, RTW_REGD_ACMA = 5, RTW_REGD_CHILE = 6, RTW_REGD_UKRAINE = 7, RTW_REGD_MEXICO = 8, RTW_REGD_CN = 9, RTW_REGD_WW, RTW_REGD_MAX }; enum rtw_txq_flags { RTW_TXQ_AMPDU, RTW_TXQ_BLOCK_BA, }; enum rtw_flags { RTW_FLAG_RUNNING, RTW_FLAG_FW_RUNNING, RTW_FLAG_SCANNING, - RTW_FLAG_INACTIVE_PS, + RTW_FLAG_POWERON, RTW_FLAG_LEISURE_PS, RTW_FLAG_LEISURE_PS_DEEP, RTW_FLAG_DIG_DISABLE, RTW_FLAG_BUSY_TRAFFIC, RTW_FLAG_WOWLAN, RTW_FLAG_RESTARTING, RTW_FLAG_RESTART_TRIGGERING, RTW_FLAG_FORCE_LOWEST_RATE, NUM_OF_RTW_FLAGS, }; enum rtw_evm { RTW_EVM_OFDM = 0, RTW_EVM_1SS, RTW_EVM_2SS_A, RTW_EVM_2SS_B, /* keep it last */ RTW_EVM_NUM }; enum rtw_snr { RTW_SNR_OFDM_A = 0, RTW_SNR_OFDM_B, RTW_SNR_OFDM_C, RTW_SNR_OFDM_D, RTW_SNR_1SS_A, RTW_SNR_1SS_B, RTW_SNR_1SS_C, RTW_SNR_1SS_D, RTW_SNR_2SS_A, RTW_SNR_2SS_B, RTW_SNR_2SS_C, RTW_SNR_2SS_D, /* keep it last */ RTW_SNR_NUM }; +enum rtw_port { + RTW_PORT_0 = 0, + RTW_PORT_1 = 1, + RTW_PORT_2 = 2, + RTW_PORT_3 = 3, + RTW_PORT_4 = 4, + RTW_PORT_NUM +}; + enum rtw_wow_flags { RTW_WOW_FLAG_EN_MAGIC_PKT, RTW_WOW_FLAG_EN_REKEY_PKT, RTW_WOW_FLAG_EN_DISCONNECT, /* keep it last */ RTW_WOW_FLAG_MAX, }; /* the power index is represented by differences, which cck-1s & ht40-1s are * the base values, so for 1s's differences, there are only ht20 & ofdm */ struct rtw_2g_1s_pwr_idx_diff { #ifdef __LITTLE_ENDIAN s8 ofdm:4; s8 bw20:4; #else s8 bw20:4; s8 ofdm:4; #endif } __packed; struct rtw_2g_ns_pwr_idx_diff { #ifdef __LITTLE_ENDIAN s8 bw20:4; s8 bw40:4; s8 cck:4; s8 ofdm:4; #else s8 ofdm:4; s8 cck:4; s8 bw40:4; s8 bw20:4; #endif } __packed; struct rtw_2g_txpwr_idx { u8 cck_base[6]; u8 bw40_base[5]; struct rtw_2g_1s_pwr_idx_diff ht_1s_diff; struct rtw_2g_ns_pwr_idx_diff ht_2s_diff; struct rtw_2g_ns_pwr_idx_diff ht_3s_diff; struct rtw_2g_ns_pwr_idx_diff ht_4s_diff; }; struct rtw_5g_ht_1s_pwr_idx_diff { #ifdef __LITTLE_ENDIAN s8 ofdm:4; s8 bw20:4; #else s8 bw20:4; s8 ofdm:4; #endif } __packed; struct rtw_5g_ht_ns_pwr_idx_diff { #ifdef __LITTLE_ENDIAN s8 bw20:4; s8 bw40:4; #else s8 bw40:4; s8 bw20:4; #endif } __packed; struct rtw_5g_ofdm_ns_pwr_idx_diff { #ifdef __LITTLE_ENDIAN s8 ofdm_3s:4; s8 ofdm_2s:4; s8 ofdm_4s:4; s8 res:4; #else s8 res:4; s8 ofdm_4s:4; s8 ofdm_2s:4; s8 ofdm_3s:4; #endif } __packed; struct rtw_5g_vht_ns_pwr_idx_diff { #ifdef __LITTLE_ENDIAN s8 bw160:4; s8 bw80:4; #else s8 bw80:4; s8 bw160:4; #endif } __packed; struct rtw_5g_txpwr_idx { u8 bw40_base[14]; struct rtw_5g_ht_1s_pwr_idx_diff ht_1s_diff; struct rtw_5g_ht_ns_pwr_idx_diff ht_2s_diff; struct rtw_5g_ht_ns_pwr_idx_diff ht_3s_diff; struct rtw_5g_ht_ns_pwr_idx_diff ht_4s_diff; struct rtw_5g_ofdm_ns_pwr_idx_diff ofdm_diff; struct rtw_5g_vht_ns_pwr_idx_diff vht_1s_diff; struct rtw_5g_vht_ns_pwr_idx_diff vht_2s_diff; struct rtw_5g_vht_ns_pwr_idx_diff vht_3s_diff; struct rtw_5g_vht_ns_pwr_idx_diff vht_4s_diff; }; struct rtw_txpwr_idx { struct rtw_2g_txpwr_idx pwr_idx_2g; struct rtw_5g_txpwr_idx pwr_idx_5g; }; -struct rtw_timer_list { - struct timer_list timer; - void (*function)(void *data); - void *args; -}; - struct rtw_channel_params { u8 center_chan; + u8 primary_chan; u8 bandwidth; - u8 primary_chan_idx; - /* center channel by different available bandwidth, - * val of (bw > current bandwidth) is invalid - */ - u8 cch_by_bw[RTW_MAX_CHANNEL_WIDTH + 1]; }; struct rtw_hw_reg { u32 addr; u32 mask; }; struct rtw_ltecoex_addr { u32 ctrl; u32 wdata; u32 rdata; }; struct rtw_reg_domain { u32 addr; u32 mask; #define RTW_REG_DOMAIN_MAC32 0 #define RTW_REG_DOMAIN_MAC16 1 #define RTW_REG_DOMAIN_MAC8 2 #define RTW_REG_DOMAIN_RF_A 3 #define RTW_REG_DOMAIN_RF_B 4 #define RTW_REG_DOMAIN_NL 0xFF u8 domain; }; struct rtw_rf_sipi_addr { u32 hssi_1; u32 hssi_2; u32 lssi_read; u32 lssi_read_pi; }; struct rtw_hw_reg_offset { struct rtw_hw_reg hw_reg; u8 offset; }; struct rtw_backup_info { u8 len; u32 reg; u32 val; }; enum rtw_vif_port_set { PORT_SET_MAC_ADDR = BIT(0), PORT_SET_BSSID = BIT(1), PORT_SET_NET_TYPE = BIT(2), PORT_SET_AID = BIT(3), PORT_SET_BCN_CTRL = BIT(4), }; struct rtw_vif_port { struct rtw_hw_reg mac_addr; struct rtw_hw_reg bssid; struct rtw_hw_reg net_type; struct rtw_hw_reg aid; struct rtw_hw_reg bcn_ctrl; }; struct rtw_tx_pkt_info { u32 tx_pkt_size; u8 offset; u8 pkt_offset; u8 tim_offset; u8 mac_id; u8 rate_id; u8 rate; u8 qsel; u8 bw; u8 sec_type; u8 sn; bool ampdu_en; u8 ampdu_factor; u8 ampdu_density; u16 seq; bool stbc; bool ldpc; bool dis_rate_fallback; bool bmc; bool use_rate; bool ls; bool fs; bool short_gi; bool report; bool rts; bool dis_qselseq; bool en_hwseq; u8 hw_ssn_sel; bool nav_use_hdr; bool bt_null; }; struct rtw_rx_pkt_stat { bool phy_status; bool icv_err; bool crc_err; bool decrypted; bool is_c2h; s32 signal_power; u16 pkt_len; u8 bw; u8 drv_info_sz; u8 shift; u8 rate; u8 mac_id; u8 cam_id; u8 ppdu_cnt; u32 tsf_low; s8 rx_power[RTW_RF_PATH_MAX]; u8 rssi; u8 rxsc; s8 rx_snr[RTW_RF_PATH_MAX]; u8 rx_evm[RTW_RF_PATH_MAX]; s8 cfo_tail[RTW_RF_PATH_MAX]; u16 freq; u8 band; struct rtw_sta_info *si; struct ieee80211_vif *vif; struct ieee80211_hdr *hdr; }; DECLARE_EWMA(tp, 10, 2); struct rtw_traffic_stats { /* units in bytes */ u64 tx_unicast; u64 rx_unicast; /* count for packets */ u64 tx_cnt; u64 rx_cnt; /* units in Mbps */ u32 tx_throughput; u32 rx_throughput; struct ewma_tp tx_ewma_tp; struct ewma_tp rx_ewma_tp; }; enum rtw_lps_mode { RTW_MODE_ACTIVE = 0, RTW_MODE_LPS = 1, RTW_MODE_WMM_PS = 2, }; enum rtw_lps_deep_mode { LPS_DEEP_MODE_NONE = 0, LPS_DEEP_MODE_LCLK = 1, LPS_DEEP_MODE_PG = 2, }; enum rtw_pwr_state { RTW_RF_OFF = 0x0, RTW_RF_ON = 0x4, RTW_ALL_ON = 0xc, }; struct rtw_lps_conf { enum rtw_lps_mode mode; enum rtw_lps_deep_mode deep_mode; enum rtw_lps_deep_mode wow_deep_mode; enum rtw_pwr_state state; u8 awake_interval; u8 rlbm; u8 smart_ps; u8 port_id; bool sec_cam_backup; bool pattern_cam_backup; }; enum rtw_hw_key_type { RTW_CAM_NONE = 0, RTW_CAM_WEP40 = 1, RTW_CAM_TKIP = 2, RTW_CAM_AES = 4, RTW_CAM_WEP104 = 5, }; struct rtw_cam_entry { bool valid; bool group; u8 addr[ETH_ALEN]; u8 hw_key_type; struct ieee80211_key_conf *key; }; struct rtw_sec_desc { /* search strategy */ bool default_key_search; u32 total_cam_num; struct rtw_cam_entry cam_table[RTW_MAX_SEC_CAM_NUM]; DECLARE_BITMAP(cam_map, RTW_MAX_SEC_CAM_NUM); }; struct rtw_tx_report { /* protect the tx report queue */ spinlock_t q_lock; struct sk_buff_head queue; atomic_t sn; struct timer_list purge_timer; }; struct rtw_ra_report { struct rate_info txrate; u32 bit_rate; u8 desc_rate; }; struct rtw_txq { struct list_head list; - unsigned long flags; - unsigned long last_push; }; #define RTW_BC_MC_MACID 1 DECLARE_EWMA(rssi, 10, 16); struct rtw_sta_info { + struct rtw_dev *rtwdev; struct ieee80211_sta *sta; struct ieee80211_vif *vif; struct ewma_rssi avg_rssi; u8 rssi_level; u8 mac_id; u8 rate_id; enum rtw_bandwidth bw_mode; enum rtw_rf_type rf_type; - enum rtw_wireless_set wireless_set; u8 stbc_en:2; u8 ldpc_en:2; bool sgi_enable; bool vht_enable; u8 init_ra_lv; u64 ra_mask; DECLARE_BITMAP(tid_ba, IEEE80211_NUM_TIDS); struct rtw_ra_report ra_report; bool use_cfg_mask; struct cfg80211_bitrate_mask *mask; + + struct work_struct rc_work; }; enum rtw_bfee_role { RTW_BFEE_NONE, RTW_BFEE_SU, RTW_BFEE_MU }; struct rtw_bfee { enum rtw_bfee_role role; u16 p_aid; u8 g_id; u8 mac_addr[ETH_ALEN]; u8 sound_dim; /* SU-MIMO */ u8 su_reg_index; /* MU-MIMO */ u16 aid; }; struct rtw_bf_info { u8 bfer_mu_cnt; u8 bfer_su_cnt; DECLARE_BITMAP(bfer_su_reg_maping, 2); u8 cur_csi_rpt_rate; }; struct rtw_vif { enum rtw_net_type net_type; u16 aid; + u8 mac_id; /* for STA mode only */ u8 mac_addr[ETH_ALEN]; u8 bssid[ETH_ALEN]; u8 port; u8 bcn_ctrl; struct list_head rsvd_page_list; struct ieee80211_tx_queue_params tx_params[IEEE80211_NUM_ACS]; const struct rtw_vif_port *conf; struct cfg80211_scan_request *scan_req; struct ieee80211_scan_ies *scan_ies; struct rtw_traffic_stats stats; struct rtw_bfee bfee; }; struct rtw_regulatory { char alpha2[2]; u8 txpwr_regd_2g; u8 txpwr_regd_5g; }; enum rtw_regd_state { RTW_REGD_STATE_WORLDWIDE, RTW_REGD_STATE_PROGRAMMED, RTW_REGD_STATE_SETTING, RTW_REGD_STATE_NR, }; struct rtw_regd { enum rtw_regd_state state; const struct rtw_regulatory *regulatory; enum nl80211_dfs_regions dfs_region; }; struct rtw_chip_ops { int (*mac_init)(struct rtw_dev *rtwdev); int (*dump_fw_crash)(struct rtw_dev *rtwdev); void (*shutdown)(struct rtw_dev *rtwdev); int (*read_efuse)(struct rtw_dev *rtwdev, u8 *map); void (*phy_set_param)(struct rtw_dev *rtwdev); void (*set_channel)(struct rtw_dev *rtwdev, u8 channel, u8 bandwidth, u8 primary_chan_idx); void (*query_rx_desc)(struct rtw_dev *rtwdev, u8 *rx_desc, struct rtw_rx_pkt_stat *pkt_stat, struct ieee80211_rx_status *rx_status); u32 (*read_rf)(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, u32 addr, u32 mask); bool (*write_rf)(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, u32 addr, u32 mask, u32 data); void (*set_tx_power_index)(struct rtw_dev *rtwdev); int (*rsvd_page_dump)(struct rtw_dev *rtwdev, u8 *buf, u32 offset, u32 size); int (*set_antenna)(struct rtw_dev *rtwdev, u32 antenna_tx, u32 antenna_rx); void (*cfg_ldo25)(struct rtw_dev *rtwdev, bool enable); void (*efuse_grant)(struct rtw_dev *rtwdev, bool enable); void (*false_alarm_statistics)(struct rtw_dev *rtwdev); void (*phy_calibration)(struct rtw_dev *rtwdev); void (*dpk_track)(struct rtw_dev *rtwdev); void (*cck_pd_set)(struct rtw_dev *rtwdev, u8 level); void (*pwr_track)(struct rtw_dev *rtwdev); void (*config_bfee)(struct rtw_dev *rtwdev, struct rtw_vif *vif, struct rtw_bfee *bfee, bool enable); void (*set_gid_table)(struct rtw_dev *rtwdev, struct ieee80211_vif *vif, struct ieee80211_bss_conf *conf); void (*cfg_csi_rate)(struct rtw_dev *rtwdev, u8 rssi, u8 cur_rate, u8 fixrate_en, u8 *new_rate); void (*adaptivity_init)(struct rtw_dev *rtwdev); void (*adaptivity)(struct rtw_dev *rtwdev); void (*cfo_init)(struct rtw_dev *rtwdev); void (*cfo_track)(struct rtw_dev *rtwdev); void (*config_tx_path)(struct rtw_dev *rtwdev, u8 tx_path, enum rtw_bb_path tx_path_1ss, enum rtw_bb_path tx_path_cck, bool is_tx2_path); void (*config_txrx_mode)(struct rtw_dev *rtwdev, u8 tx_path, u8 rx_path, bool is_tx2_path); + /* for USB/SDIO only */ + void (*fill_txdesc_checksum)(struct rtw_dev *rtwdev, + struct rtw_tx_pkt_info *pkt_info, + u8 *txdesc); /* for coex */ void (*coex_set_init)(struct rtw_dev *rtwdev); void (*coex_set_ant_switch)(struct rtw_dev *rtwdev, u8 ctrl_type, u8 pos_type); void (*coex_set_gnt_fix)(struct rtw_dev *rtwdev); void (*coex_set_gnt_debug)(struct rtw_dev *rtwdev); void (*coex_set_rfe_type)(struct rtw_dev *rtwdev); void (*coex_set_wl_tx_power)(struct rtw_dev *rtwdev, u8 wl_pwr); void (*coex_set_wl_rx_gain)(struct rtw_dev *rtwdev, bool low_gain); }; #define RTW_PWR_POLLING_CNT 20000 #define RTW_PWR_CMD_READ 0x00 #define RTW_PWR_CMD_WRITE 0x01 #define RTW_PWR_CMD_POLLING 0x02 #define RTW_PWR_CMD_DELAY 0x03 #define RTW_PWR_CMD_END 0x04 /* define the base address of each block */ #define RTW_PWR_ADDR_MAC 0x00 #define RTW_PWR_ADDR_USB 0x01 #define RTW_PWR_ADDR_PCIE 0x02 #define RTW_PWR_ADDR_SDIO 0x03 #define RTW_PWR_INTF_SDIO_MSK BIT(0) #define RTW_PWR_INTF_USB_MSK BIT(1) #define RTW_PWR_INTF_PCI_MSK BIT(2) #define RTW_PWR_INTF_ALL_MSK (BIT(0) | BIT(1) | BIT(2) | BIT(3)) #define RTW_PWR_CUT_TEST_MSK BIT(0) #define RTW_PWR_CUT_A_MSK BIT(1) #define RTW_PWR_CUT_B_MSK BIT(2) #define RTW_PWR_CUT_C_MSK BIT(3) #define RTW_PWR_CUT_D_MSK BIT(4) #define RTW_PWR_CUT_E_MSK BIT(5) #define RTW_PWR_CUT_F_MSK BIT(6) #define RTW_PWR_CUT_G_MSK BIT(7) #define RTW_PWR_CUT_ALL_MSK 0xFF enum rtw_pwr_seq_cmd_delay_unit { RTW_PWR_DELAY_US, RTW_PWR_DELAY_MS, }; struct rtw_pwr_seq_cmd { u16 offset; u8 cut_mask; u8 intf_mask; u8 base:4; u8 cmd:4; u8 mask; u8 value; }; enum rtw_chip_ver { RTW_CHIP_VER_CUT_A = 0x00, RTW_CHIP_VER_CUT_B = 0x01, RTW_CHIP_VER_CUT_C = 0x02, RTW_CHIP_VER_CUT_D = 0x03, RTW_CHIP_VER_CUT_E = 0x04, RTW_CHIP_VER_CUT_F = 0x05, RTW_CHIP_VER_CUT_G = 0x06, }; #define RTW_INTF_PHY_PLATFORM_ALL 0 enum rtw_intf_phy_cut { RTW_INTF_PHY_CUT_A = BIT(0), RTW_INTF_PHY_CUT_B = BIT(1), RTW_INTF_PHY_CUT_C = BIT(2), RTW_INTF_PHY_CUT_D = BIT(3), RTW_INTF_PHY_CUT_E = BIT(4), RTW_INTF_PHY_CUT_F = BIT(5), RTW_INTF_PHY_CUT_G = BIT(6), RTW_INTF_PHY_CUT_ALL = 0xFFFF, }; enum rtw_ip_sel { RTW_IP_SEL_PHY = 0, RTW_IP_SEL_MAC = 1, RTW_IP_SEL_DBI = 2, RTW_IP_SEL_UNDEF = 0xFFFF }; enum rtw_pq_map_id { RTW_PQ_MAP_VO = 0x0, RTW_PQ_MAP_VI = 0x1, RTW_PQ_MAP_BE = 0x2, RTW_PQ_MAP_BK = 0x3, RTW_PQ_MAP_MG = 0x4, RTW_PQ_MAP_HI = 0x5, RTW_PQ_MAP_NUM = 0x6, RTW_PQ_MAP_UNDEF, }; enum rtw_dma_mapping { RTW_DMA_MAPPING_EXTRA = 0, RTW_DMA_MAPPING_LOW = 1, RTW_DMA_MAPPING_NORMAL = 2, RTW_DMA_MAPPING_HIGH = 3, RTW_DMA_MAPPING_MAX, RTW_DMA_MAPPING_UNDEF, }; struct rtw_rqpn { enum rtw_dma_mapping dma_map_vo; enum rtw_dma_mapping dma_map_vi; enum rtw_dma_mapping dma_map_be; enum rtw_dma_mapping dma_map_bk; enum rtw_dma_mapping dma_map_mg; enum rtw_dma_mapping dma_map_hi; }; struct rtw_prioq_addr { u32 rsvd; u32 avail; }; struct rtw_prioq_addrs { struct rtw_prioq_addr prio[RTW_DMA_MAPPING_MAX]; bool wsize; }; struct rtw_page_table { u16 hq_num; u16 nq_num; u16 lq_num; u16 exq_num; u16 gapq_num; }; struct rtw_intf_phy_para { u16 offset; u16 value; u16 ip_sel; u16 cut_mask; u16 platform; }; struct rtw_wow_pattern { u16 crc; u8 type; u8 valid; u8 mask[RTW_MAX_PATTERN_MASK_SIZE]; }; struct rtw_pno_request { bool inited; u32 match_set_cnt; struct cfg80211_match_set *match_sets; u8 channel_cnt; struct ieee80211_channel *channels; struct cfg80211_sched_scan_plan scan_plan; }; struct rtw_wow_param { struct ieee80211_vif *wow_vif; DECLARE_BITMAP(flags, RTW_WOW_FLAG_MAX); u8 txpause; u8 pattern_cnt; struct rtw_wow_pattern patterns[RTW_MAX_PATTERN_NUM]; bool ips_enabled; struct rtw_pno_request pno_req; }; struct rtw_intf_phy_para_table { const struct rtw_intf_phy_para *usb2_para; const struct rtw_intf_phy_para *usb3_para; const struct rtw_intf_phy_para *gen1_para; const struct rtw_intf_phy_para *gen2_para; u8 n_usb2_para; u8 n_usb3_para; u8 n_gen1_para; u8 n_gen2_para; }; struct rtw_table { const void *data; const u32 size; void (*parse)(struct rtw_dev *rtwdev, const struct rtw_table *tbl); void (*do_cfg)(struct rtw_dev *rtwdev, const struct rtw_table *tbl, u32 addr, u32 data); enum rtw_rf_path rf_path; }; static inline void rtw_load_table(struct rtw_dev *rtwdev, const struct rtw_table *tbl) { (*tbl->parse)(rtwdev, tbl); } enum rtw_rfe_fem { RTW_RFE_IFEM, RTW_RFE_EFEM, RTW_RFE_IFEM2G_EFEM5G, RTW_RFE_NUM, }; struct rtw_rfe_def { const struct rtw_table *phy_pg_tbl; const struct rtw_table *txpwr_lmt_tbl; const struct rtw_table *agc_btg_tbl; }; #define RTW_DEF_RFE(chip, bb_pg, pwrlmt) { \ .phy_pg_tbl = &rtw ## chip ## _bb_pg_type ## bb_pg ## _tbl, \ .txpwr_lmt_tbl = &rtw ## chip ## _txpwr_lmt_type ## pwrlmt ## _tbl, \ } #define RTW_DEF_RFE_EXT(chip, bb_pg, pwrlmt, btg) { \ .phy_pg_tbl = &rtw ## chip ## _bb_pg_type ## bb_pg ## _tbl, \ .txpwr_lmt_tbl = &rtw ## chip ## _txpwr_lmt_type ## pwrlmt ## _tbl, \ .agc_btg_tbl = &rtw ## chip ## _agc_btg_type ## btg ## _tbl, \ } #define RTW_PWR_TRK_5G_1 0 #define RTW_PWR_TRK_5G_2 1 #define RTW_PWR_TRK_5G_3 2 #define RTW_PWR_TRK_5G_NUM 3 #define RTW_PWR_TRK_TBL_SZ 30 /* This table stores the values of TX power that will be adjusted by power * tracking. * * For 5G bands, there are 3 different settings. * For 2G there are cck rate and ofdm rate with different settings. */ struct rtw_pwr_track_tbl { const u8 *pwrtrk_5gb_n[RTW_PWR_TRK_5G_NUM]; const u8 *pwrtrk_5gb_p[RTW_PWR_TRK_5G_NUM]; const u8 *pwrtrk_5ga_n[RTW_PWR_TRK_5G_NUM]; const u8 *pwrtrk_5ga_p[RTW_PWR_TRK_5G_NUM]; const u8 *pwrtrk_2gb_n; const u8 *pwrtrk_2gb_p; const u8 *pwrtrk_2ga_n; const u8 *pwrtrk_2ga_p; const u8 *pwrtrk_2g_cckb_n; const u8 *pwrtrk_2g_cckb_p; const u8 *pwrtrk_2g_ccka_n; const u8 *pwrtrk_2g_ccka_p; const s8 *pwrtrk_xtal_n; const s8 *pwrtrk_xtal_p; }; enum rtw_wlan_cpu { RTW_WCPU_11AC, RTW_WCPU_11N, }; enum rtw_fw_fifo_sel { RTW_FW_FIFO_SEL_TX, RTW_FW_FIFO_SEL_RX, RTW_FW_FIFO_SEL_RSVD_PAGE, RTW_FW_FIFO_SEL_REPORT, RTW_FW_FIFO_SEL_LLT, RTW_FW_FIFO_SEL_RXBUF_FW, RTW_FW_FIFO_MAX, }; enum rtw_fwcd_item { RTW_FWCD_TLV, RTW_FWCD_REG, RTW_FWCD_ROM, RTW_FWCD_IMEM, RTW_FWCD_DMEM, RTW_FWCD_EMEM, }; /* hardware configuration for each IC */ struct rtw_chip_info { struct rtw_chip_ops *ops; u8 id; const char *fw_name; enum rtw_wlan_cpu wlan_cpu; u8 tx_pkt_desc_sz; u8 tx_buf_desc_sz; u8 rx_pkt_desc_sz; u8 rx_buf_desc_sz; u32 phy_efuse_size; u32 log_efuse_size; u32 ptct_efuse_size; u32 txff_size; u32 rxff_size; u32 fw_rxff_size; + u16 rsvd_drv_pg_num; u8 band; u8 page_size; u8 csi_buf_pg_num; u8 dig_max; u8 dig_min; u8 txgi_factor; bool is_pwr_by_rate_dec; bool rx_ldpc; bool tx_stbc; u8 max_power_index; u8 ampdu_density; u16 fw_fifo_addr[RTW_FW_FIFO_MAX]; const struct rtw_fwcd_segs *fwcd_segs; u8 default_1ss_tx_path; bool path_div_supported; bool ht_supported; bool vht_supported; u8 lps_deep_mode_supported; /* init values */ u8 sys_func_en; const struct rtw_pwr_seq_cmd **pwr_on_seq; const struct rtw_pwr_seq_cmd **pwr_off_seq; const struct rtw_rqpn *rqpn_table; const struct rtw_prioq_addrs *prioq_addrs; const struct rtw_page_table *page_table; const struct rtw_intf_phy_para_table *intf_table; const struct rtw_hw_reg *dig; const struct rtw_hw_reg *dig_cck; u32 rf_base_addr[2]; u32 rf_sipi_addr[2]; const struct rtw_rf_sipi_addr *rf_sipi_read_addr; u8 fix_rf_phy_num; const struct rtw_ltecoex_addr *ltecoex_addr; const struct rtw_table *mac_tbl; const struct rtw_table *agc_tbl; const struct rtw_table *bb_tbl; const struct rtw_table *rf_tbl[RTW_RF_PATH_MAX]; const struct rtw_table *rfk_init_tbl; const struct rtw_rfe_def *rfe_defs; u32 rfe_defs_size; bool en_dis_dpd; u16 dpd_ratemask; u8 iqk_threshold; u8 lck_threshold; const struct rtw_pwr_track_tbl *pwr_track_tbl; u8 bfer_su_max_num; u8 bfer_mu_max_num; struct rtw_hw_reg_offset *edcca_th; s8 l2h_th_ini_cs; s8 l2h_th_ini_ad; const char *wow_fw_name; const struct wiphy_wowlan_support *wowlan_stub; const u8 max_sched_scan_ssids; + const u16 max_scan_ie_len; /* coex paras */ u32 coex_para_ver; u8 bt_desired_ver; bool scbd_support; bool new_scbd10_def; /* true: fix 2M(8822c) */ bool ble_hid_profile_support; bool wl_mimo_ps_support; u8 pstdma_type; /* 0: LPSoff, 1:LPSon */ u8 bt_rssi_type; u8 ant_isolation; u8 rssi_tolerance; u8 table_sant_num; u8 table_nsant_num; u8 tdma_sant_num; u8 tdma_nsant_num; u8 bt_afh_span_bw20; u8 bt_afh_span_bw40; u8 afh_5g_num; u8 wl_rf_para_num; u8 coex_info_hw_regs_num; const u8 *bt_rssi_step; const u8 *wl_rssi_step; const struct coex_table_para *table_nsant; const struct coex_table_para *table_sant; const struct coex_tdma_para *tdma_sant; const struct coex_tdma_para *tdma_nsant; const struct coex_rf_para *wl_rf_para_tx; const struct coex_rf_para *wl_rf_para_rx; const struct coex_5g_afh_map *afh_5g; const struct rtw_hw_reg *btg_reg; const struct rtw_reg_domain *coex_info_hw_regs; u32 wl_fw_desired_ver; }; enum rtw_coex_bt_state_cnt { COEX_CNT_BT_RETRY, COEX_CNT_BT_REINIT, COEX_CNT_BT_REENABLE, COEX_CNT_BT_POPEVENT, COEX_CNT_BT_SETUPLINK, COEX_CNT_BT_IGNWLANACT, COEX_CNT_BT_INQ, COEX_CNT_BT_PAGE, COEX_CNT_BT_ROLESWITCH, COEX_CNT_BT_AFHUPDATE, COEX_CNT_BT_INFOUPDATE, COEX_CNT_BT_IQK, COEX_CNT_BT_IQKFAIL, COEX_CNT_BT_MAX }; enum rtw_coex_wl_state_cnt { COEX_CNT_WL_SCANAP, COEX_CNT_WL_CONNPKT, COEX_CNT_WL_COEXRUN, COEX_CNT_WL_NOISY0, COEX_CNT_WL_NOISY1, COEX_CNT_WL_NOISY2, COEX_CNT_WL_5MS_NOEXTEND, COEX_CNT_WL_FW_NOTIFY, COEX_CNT_WL_MAX }; struct rtw_coex_rfe { bool ant_switch_exist; bool ant_switch_diversity; bool ant_switch_with_bt; u8 rfe_module_type; u8 ant_switch_polarity; /* true if WLG at BTG, else at WLAG */ bool wlg_at_btg; }; #define COEX_WL_TDMA_PARA_LENGTH 5 struct rtw_coex_dm { bool cur_ps_tdma_on; bool cur_wl_rx_low_gain_en; bool ignore_wl_act; u8 reason; u8 bt_rssi_state[4]; u8 wl_rssi_state[4]; u8 wl_ch_info[3]; u8 cur_ps_tdma; u8 cur_table; u8 ps_tdma_para[5]; u8 cur_bt_pwr_lvl; u8 cur_bt_lna_lvl; u8 cur_wl_pwr_lvl; u8 bt_status; u32 cur_ant_pos_type; u32 cur_switch_status; u32 setting_tdma; u8 fw_tdma_para[COEX_WL_TDMA_PARA_LENGTH]; }; #define COEX_BTINFO_SRC_WL_FW 0x0 #define COEX_BTINFO_SRC_BT_RSP 0x1 #define COEX_BTINFO_SRC_BT_ACT 0x2 #define COEX_BTINFO_SRC_BT_IQK 0x3 #define COEX_BTINFO_SRC_BT_SCBD 0x4 #define COEX_BTINFO_SRC_H2C60 0x5 #define COEX_BTINFO_SRC_MAX 0x6 #define COEX_INFO_FTP BIT(7) #define COEX_INFO_A2DP BIT(6) #define COEX_INFO_HID BIT(5) #define COEX_INFO_SCO_BUSY BIT(4) #define COEX_INFO_ACL_BUSY BIT(3) #define COEX_INFO_INQ_PAGE BIT(2) #define COEX_INFO_SCO_ESCO BIT(1) #define COEX_INFO_CONNECTION BIT(0) #define COEX_BTINFO_LENGTH_MAX 10 #define COEX_BTINFO_LENGTH 7 #define COEX_BT_HIDINFO_LIST 0x0 #define COEX_BT_HIDINFO_A 0x1 #define COEX_BT_HIDINFO_NAME 3 #define COEX_BT_HIDINFO_LENGTH 6 #define COEX_BT_HIDINFO_HANDLE_NUM 4 #define COEX_BT_HIDINFO_C2H_HANDLE 0 #define COEX_BT_HIDINFO_C2H_VENDOR 1 #define COEX_BT_BLE_HANDLE_THRS 0x10 #define COEX_BT_HIDINFO_NOTCON 0xff struct rtw_coex_hid { u8 hid_handle; u8 hid_vendor; u8 hid_name[COEX_BT_HIDINFO_NAME]; bool hid_info_completed; bool is_game_hid; }; struct rtw_coex_hid_handle_list { u8 cmd_id; u8 len; u8 subid; u8 handle_cnt; u8 handle[COEX_BT_HIDINFO_HANDLE_NUM]; } __packed; struct rtw_coex_hid_info_a { u8 cmd_id; u8 len; u8 subid; u8 handle; u8 vendor; u8 name[COEX_BT_HIDINFO_NAME]; } __packed; struct rtw_coex_stat { bool bt_disabled; bool bt_disabled_pre; bool bt_link_exist; bool bt_whck_test; bool bt_inq_page; bool bt_inq_remain; bool bt_inq; bool bt_page; bool bt_ble_voice; bool bt_ble_exist; bool bt_hfp_exist; bool bt_a2dp_exist; bool bt_hid_exist; bool bt_pan_exist; /* PAN or OPP */ bool bt_opp_exist; /* OPP only */ bool bt_acl_busy; bool bt_fix_2M; bool bt_setup_link; bool bt_multi_link; bool bt_multi_link_pre; bool bt_multi_link_remain; bool bt_a2dp_sink; bool bt_a2dp_active; bool bt_reenable; bool bt_ble_scan_en; bool bt_init_scan; bool bt_slave; bool bt_418_hid_exist; bool bt_ble_hid_exist; bool bt_game_hid_exist; bool bt_hid_handle_cnt; bool bt_mailbox_reply; bool wl_under_lps; bool wl_under_ips; bool wl_hi_pri_task1; bool wl_hi_pri_task2; bool wl_force_lps_ctrl; bool wl_gl_busy; bool wl_linkscan_proc; bool wl_ps_state_fail; bool wl_tx_limit_en; bool wl_ampdu_limit_en; bool wl_connected; bool wl_slot_extend; bool wl_cck_lock; bool wl_cck_lock_pre; bool wl_cck_lock_ever; bool wl_connecting; bool wl_slot_toggle; bool wl_slot_toggle_change; /* if toggle to no-toggle */ bool wl_mimo_ps; u32 bt_supported_version; u32 bt_supported_feature; u32 hi_pri_tx; u32 hi_pri_rx; u32 lo_pri_tx; u32 lo_pri_rx; u32 patch_ver; u16 bt_reg_vendor_ae; u16 bt_reg_vendor_ac; s8 bt_rssi; u8 kt_ver; u8 gnt_workaround_state; u8 tdma_timer_base; u8 bt_profile_num; u8 bt_info_c2h[COEX_BTINFO_SRC_MAX][COEX_BTINFO_LENGTH_MAX]; u8 bt_info_lb2; u8 bt_info_lb3; u8 bt_info_hb0; u8 bt_info_hb1; u8 bt_info_hb2; u8 bt_info_hb3; u8 bt_ble_scan_type; u8 bt_hid_pair_num; u8 bt_hid_slot; u8 bt_a2dp_bitpool; u8 bt_iqk_state; u16 wl_beacon_interval; u8 wl_noisy_level; u8 wl_fw_dbg_info[10]; u8 wl_fw_dbg_info_pre[10]; u8 wl_rx_rate; u8 wl_tx_rate; u8 wl_rts_rx_rate; u8 wl_coex_mode; u8 wl_iot_peer; u8 ampdu_max_time; u8 wl_tput_dir; u8 wl_toggle_para[6]; u8 wl_toggle_interval; u16 score_board; u16 retry_limit; /* counters to record bt states */ u32 cnt_bt[COEX_CNT_BT_MAX]; /* counters to record wifi states */ u32 cnt_wl[COEX_CNT_WL_MAX]; /* counters to record bt c2h data */ u32 cnt_bt_info_c2h[COEX_BTINFO_SRC_MAX]; u32 darfrc; u32 darfrch; struct rtw_coex_hid hid_info[COEX_BT_HIDINFO_HANDLE_NUM]; struct rtw_coex_hid_handle_list hid_handle_list; }; struct rtw_coex { - /* protects coex info request section */ - struct mutex mutex; struct sk_buff_head queue; wait_queue_head_t wait; bool under_5g; bool stop_dm; bool freeze; bool freerun; bool wl_rf_off; bool manual_control; struct rtw_coex_stat stat; struct rtw_coex_dm dm; struct rtw_coex_rfe rfe; struct delayed_work bt_relink_work; struct delayed_work bt_reenable_work; struct delayed_work defreeze_work; struct delayed_work wl_remain_work; struct delayed_work bt_remain_work; struct delayed_work wl_connecting_work; struct delayed_work bt_multi_link_remain_work; struct delayed_work wl_ccklock_work; }; #define DPK_RF_REG_NUM 7 #define DPK_RF_PATH_NUM 2 #define DPK_BB_REG_NUM 18 #define DPK_CHANNEL_WIDTH_80 1 DECLARE_EWMA(thermal, 10, 4); struct rtw_dpk_info { bool is_dpk_pwr_on; bool is_reload; DECLARE_BITMAP(dpk_path_ok, DPK_RF_PATH_NUM); u8 thermal_dpk[DPK_RF_PATH_NUM]; struct ewma_thermal avg_thermal[DPK_RF_PATH_NUM]; u32 gnt_control; u32 gnt_value; u8 result[RTW_RF_PATH_MAX]; u8 dpk_txagc[RTW_RF_PATH_MAX]; u32 coef[RTW_RF_PATH_MAX][20]; u16 dpk_gs[RTW_RF_PATH_MAX]; u8 thermal_dpk_delta[RTW_RF_PATH_MAX]; u8 pre_pwsf[RTW_RF_PATH_MAX]; u8 dpk_band; u8 dpk_ch; u8 dpk_bw; }; struct rtw_phy_cck_pd_reg { u32 reg_pd; u32 mask_pd; u32 reg_cs; u32 mask_cs; }; #define DACK_MSBK_BACKUP_NUM 0xf #define DACK_DCK_BACKUP_NUM 0x2 struct rtw_swing_table { const u8 *p[RTW_RF_PATH_MAX]; const u8 *n[RTW_RF_PATH_MAX]; }; struct rtw_pkt_count { u16 num_bcn_pkt; u16 num_qry_pkt[DESC_RATE_MAX]; }; DECLARE_EWMA(evm, 10, 4); DECLARE_EWMA(snr, 10, 4); struct rtw_iqk_info { bool done; struct { u32 s1_x; u32 s1_y; u32 s0_x; u32 s0_y; } result; }; enum rtw_rf_band { RF_BAND_2G_CCK, RF_BAND_2G_OFDM, RF_BAND_5G_L, RF_BAND_5G_M, RF_BAND_5G_H, RF_BAND_MAX }; #define RF_GAIN_NUM 11 #define RF_HW_OFFSET_NUM 10 struct rtw_gapk_info { u32 rf3f_bp[RF_BAND_MAX][RF_GAIN_NUM][RTW_RF_PATH_MAX]; u32 rf3f_fs[RTW_RF_PATH_MAX][RF_GAIN_NUM]; bool txgapk_bp_done; s8 offset[RF_GAIN_NUM][RTW_RF_PATH_MAX]; s8 fianl_offset[RF_GAIN_NUM][RTW_RF_PATH_MAX]; u8 read_txgain; u8 channel; }; #define EDCCA_TH_L2H_IDX 0 #define EDCCA_TH_H2L_IDX 1 #define EDCCA_TH_L2H_LB 48 #define EDCCA_ADC_BACKOFF 12 #define EDCCA_IGI_BASE 50 #define EDCCA_IGI_L2H_DIFF 8 #define EDCCA_L2H_H2L_DIFF 7 #define EDCCA_L2H_H2L_DIFF_NORMAL 8 enum rtw_edcca_mode { RTW_EDCCA_NORMAL = 0, RTW_EDCCA_ADAPTIVITY = 1, }; struct rtw_cfo_track { bool is_adjust; u8 crystal_cap; s32 cfo_tail[RTW_RF_PATH_MAX]; s32 cfo_cnt[RTW_RF_PATH_MAX]; u32 packet_count; u32 packet_count_pre; }; #define RRSR_INIT_2G 0x15f #define RRSR_INIT_5G 0x150 enum rtw_dm_cap { RTW_DM_CAP_NA, RTW_DM_CAP_TXGAPK, RTW_DM_CAP_NUM }; struct rtw_dm_info { u32 cck_fa_cnt; u32 ofdm_fa_cnt; u32 total_fa_cnt; u32 cck_cca_cnt; u32 ofdm_cca_cnt; u32 total_cca_cnt; u32 cck_ok_cnt; u32 cck_err_cnt; u32 ofdm_ok_cnt; u32 ofdm_err_cnt; u32 ht_ok_cnt; u32 ht_err_cnt; u32 vht_ok_cnt; u32 vht_err_cnt; u8 min_rssi; u8 pre_min_rssi; u16 fa_history[4]; u8 igi_history[4]; u8 igi_bitmap; bool damping; u8 damping_cnt; u8 damping_rssi; u8 cck_gi_u_bnd; u8 cck_gi_l_bnd; u8 fix_rate; u8 tx_rate; u32 rrsr_val_init; u32 rrsr_mask_min; u8 thermal_avg[RTW_RF_PATH_MAX]; u8 thermal_meter_k; u8 thermal_meter_lck; s8 delta_power_index[RTW_RF_PATH_MAX]; s8 delta_power_index_last[RTW_RF_PATH_MAX]; u8 default_ofdm_index; bool pwr_trk_triggered; bool pwr_trk_init_trigger; struct ewma_thermal avg_thermal[RTW_RF_PATH_MAX]; s8 txagc_remnant_cck; s8 txagc_remnant_ofdm; /* backup dack results for each path and I/Q */ u32 dack_adck[RTW_RF_PATH_MAX]; u16 dack_msbk[RTW_RF_PATH_MAX][2][DACK_MSBK_BACKUP_NUM]; u8 dack_dck[RTW_RF_PATH_MAX][2][DACK_DCK_BACKUP_NUM]; struct rtw_dpk_info dpk_info; struct rtw_cfo_track cfo_track; /* [bandwidth 0:20M/1:40M][number of path] */ u8 cck_pd_lv[2][RTW_RF_PATH_MAX]; u32 cck_fa_avg; u8 cck_pd_default; /* save the last rx phy status for debug */ s8 rx_snr[RTW_RF_PATH_MAX]; u8 rx_evm_dbm[RTW_RF_PATH_MAX]; s16 cfo_tail[RTW_RF_PATH_MAX]; u8 rssi[RTW_RF_PATH_MAX]; u8 curr_rx_rate; struct rtw_pkt_count cur_pkt_count; struct rtw_pkt_count last_pkt_count; struct ewma_evm ewma_evm[RTW_EVM_NUM]; struct ewma_snr ewma_snr[RTW_SNR_NUM]; u32 dm_flags; /* enum rtw_dm_cap */ struct rtw_iqk_info iqk; struct rtw_gapk_info gapk; bool is_bt_iqk_timeout; s8 l2h_th_ini; enum rtw_edcca_mode edcca_mode; u8 scan_density; }; struct rtw_efuse { u32 size; u32 physical_size; u32 logical_size; u32 protect_size; u8 addr[ETH_ALEN]; u8 channel_plan; u8 country_code[2]; u8 rf_board_option; u8 rfe_option; u8 power_track_type; u8 thermal_meter[RTW_RF_PATH_MAX]; u8 thermal_meter_k; u8 crystal_cap; u8 ant_div_cfg; u8 ant_div_type; u8 regd; u8 afe; u8 lna_type_2g; u8 lna_type_5g; u8 glna_type; u8 alna_type; bool ext_lna_2g; bool ext_lna_5g; u8 pa_type_2g; u8 pa_type_5g; u8 gpa_type; u8 apa_type; bool ext_pa_2g; bool ext_pa_5g; u8 tx_bb_swing_setting_2g; u8 tx_bb_swing_setting_5g; bool btcoex; /* bt share antenna with wifi */ bool share_ant; u8 bt_setting; struct { u8 hci; u8 bw; u8 ptcl; u8 nss; u8 ant_num; } hw_cap; struct rtw_txpwr_idx txpwr_idx_table[4]; }; struct rtw_phy_cond { #ifdef __LITTLE_ENDIAN u32 rfe:8; u32 intf:4; u32 pkg:4; u32 plat:4; u32 intf_rsvd:4; u32 cut:4; u32 branch:2; u32 neg:1; u32 pos:1; #else u32 pos:1; u32 neg:1; u32 branch:2; u32 cut:4; u32 intf_rsvd:4; u32 plat:4; u32 pkg:4; u32 intf:4; u32 rfe:8; #endif /* for intf:4 */ #define INTF_PCIE BIT(0) #define INTF_USB BIT(1) #define INTF_SDIO BIT(2) /* for branch:2 */ #define BRANCH_IF 0 #define BRANCH_ELIF 1 #define BRANCH_ELSE 2 #define BRANCH_ENDIF 3 }; struct rtw_fifo_conf { /* tx fifo information */ u16 rsvd_boundary; u16 rsvd_pg_num; u16 rsvd_drv_pg_num; u16 txff_pg_num; u16 acq_pg_num; u16 rsvd_drv_addr; u16 rsvd_h2c_info_addr; u16 rsvd_h2c_sta_info_addr; u16 rsvd_h2cq_addr; u16 rsvd_cpu_instr_addr; u16 rsvd_fw_txbuf_addr; u16 rsvd_csibuf_addr; const struct rtw_rqpn *rqpn; }; struct rtw_fwcd_desc { u32 size; u8 *next; u8 *data; }; struct rtw_fwcd_segs { const u32 *segs; u8 num; }; #define FW_CD_TYPE 0xffff #define FW_CD_LEN 4 #define FW_CD_VAL 0xaabbccdd struct rtw_fw_state { const struct firmware *firmware; struct rtw_dev *rtwdev; struct completion completion; struct rtw_fwcd_desc fwcd_desc; u16 version; u8 sub_version; u8 sub_index; u16 h2c_version; u32 feature; + u32 feature_ext; + enum rtw_fw_type type; }; enum rtw_sar_sources { RTW_SAR_SOURCE_NONE, RTW_SAR_SOURCE_COMMON, }; enum rtw_sar_bands { RTW_SAR_BAND_0, RTW_SAR_BAND_1, /* RTW_SAR_BAND_2, not used now */ RTW_SAR_BAND_3, RTW_SAR_BAND_4, RTW_SAR_BAND_NR, }; -/* the union is reserved for other knids of SAR sources +/* the union is reserved for other kinds of SAR sources * which might not re-use same format with array common. */ union rtw_sar_cfg { s8 common[RTW_SAR_BAND_NR]; }; struct rtw_sar { enum rtw_sar_sources src; union rtw_sar_cfg cfg[RTW_RF_PATH_MAX][RTW_RATE_SECTION_MAX]; }; struct rtw_hal { u32 rcr; u32 chip_version; u8 cut_version; u8 mp_chip; u8 oem_id; + u8 pkg_type; struct rtw_phy_cond phy_cond; + bool rfe_btg; u8 ps_mode; u8 current_channel; u8 current_primary_channel_index; u8 current_band_width; u8 current_band_type; + u8 primary_channel; /* center channel for different available bandwidth, * val of (bw > current_band_width) is invalid */ u8 cch_by_bw[RTW_MAX_CHANNEL_WIDTH + 1]; u8 sec_ch_offset; u8 rf_type; u8 rf_path_num; u8 rf_phy_num; u32 antenna_tx; u32 antenna_rx; u8 bfee_sts_cap; bool txrx_1ss; /* protect tx power section */ struct mutex tx_power_mutex; s8 tx_pwr_by_rate_offset_2g[RTW_RF_PATH_MAX] [DESC_RATE_MAX]; s8 tx_pwr_by_rate_offset_5g[RTW_RF_PATH_MAX] [DESC_RATE_MAX]; s8 tx_pwr_by_rate_base_2g[RTW_RF_PATH_MAX] [RTW_RATE_SECTION_MAX]; s8 tx_pwr_by_rate_base_5g[RTW_RF_PATH_MAX] [RTW_RATE_SECTION_MAX]; s8 tx_pwr_limit_2g[RTW_REGD_MAX] [RTW_CHANNEL_WIDTH_MAX] [RTW_RATE_SECTION_MAX] [RTW_MAX_CHANNEL_NUM_2G]; s8 tx_pwr_limit_5g[RTW_REGD_MAX] [RTW_CHANNEL_WIDTH_MAX] [RTW_RATE_SECTION_MAX] [RTW_MAX_CHANNEL_NUM_5G]; s8 tx_pwr_tbl[RTW_RF_PATH_MAX] [DESC_RATE_MAX]; enum rtw_sar_bands sar_band; struct rtw_sar sar; /* for 8821c set channel */ u32 ch_param[3]; }; struct rtw_path_div { enum rtw_bb_path current_tx_path; u32 path_a_sum; u32 path_b_sum; u16 path_a_cnt; u16 path_b_cnt; }; struct rtw_chan_info { int pri_ch_idx; int action_id; int bw; u8 extra_info; u8 channel; u16 timeout; }; struct rtw_chan_list { u32 buf_size; u32 ch_num; u32 size; u16 addr; }; struct rtw_hw_scan_info { struct ieee80211_vif *scanning_vif; u8 probe_pg_size; u8 op_pri_ch_idx; + u8 op_pri_ch; u8 op_chan; u8 op_bw; }; struct rtw_dev { struct ieee80211_hw *hw; struct device *dev; struct rtw_hci hci; struct rtw_hw_scan_info scan_info; - struct rtw_chip_info *chip; + const struct rtw_chip_info *chip; struct rtw_hal hal; struct rtw_fifo_conf fifo; struct rtw_fw_state fw; struct rtw_efuse efuse; struct rtw_sec_desc sec; struct rtw_traffic_stats stats; struct rtw_regd regd; struct rtw_bf_info bf_info; struct rtw_dm_info dm_info; struct rtw_coex coex; /* ensures exclusive access from mac80211 callbacks */ struct mutex mutex; - /* read/write rf register */ - spinlock_t rf_lock; - /* watch dog every 2 sec */ struct delayed_work watch_dog_work; u32 watch_dog_cnt; struct list_head rsvd_page_list; /* c2h cmd queue & handler work */ struct sk_buff_head c2h_queue; struct work_struct c2h_work; struct work_struct ips_work; struct work_struct fw_recovery_work; struct work_struct update_beacon_work; /* used to protect txqs list */ spinlock_t txq_lock; struct list_head txqs; struct workqueue_struct *tx_wq; struct work_struct tx_work; struct work_struct ba_work; struct rtw_tx_report tx_report; struct { - /* incicate the mail box to use with fw */ + /* indicate the mail box to use with fw */ u8 last_box_num; - /* protect to send h2c to fw */ - spinlock_t lock; u32 seq; } h2c; /* lps power state & handler work */ struct rtw_lps_conf lps_conf; bool ps_enabled; bool beacon_loss; struct completion lps_leave_check; struct dentry *debugfs; u8 sta_cnt; u32 rts_threshold; + DECLARE_BITMAP(hw_port, RTW_PORT_NUM); DECLARE_BITMAP(mac_id_map, RTW_MAX_MAC_ID_NUM); DECLARE_BITMAP(flags, NUM_OF_RTW_FLAGS); u8 mp_mode; struct rtw_path_div dm_path_div; struct rtw_fw_state wow_fw; struct rtw_wow_param wow; bool need_rfk; struct completion fw_scan_density; + bool ap_active; /* hci related data, must be last */ u8 priv[] __aligned(sizeof(void *)); }; #include "hci.h" static inline bool rtw_is_assoc(struct rtw_dev *rtwdev) { return !!rtwdev->sta_cnt; } static inline struct ieee80211_txq *rtwtxq_to_txq(struct rtw_txq *rtwtxq) { void *p = rtwtxq; return container_of(p, struct ieee80211_txq, drv_priv); } static inline struct ieee80211_vif *rtwvif_to_vif(struct rtw_vif *rtwvif) { void *p = rtwvif; return container_of(p, struct ieee80211_vif, drv_priv); } static inline bool rtw_ssid_equal(struct cfg80211_ssid *a, struct cfg80211_ssid *b) { if (!a || !b || a->ssid_len != b->ssid_len) return false; if (memcmp(a->ssid, b->ssid, a->ssid_len)) return false; return true; } static inline void rtw_chip_efuse_grant_on(struct rtw_dev *rtwdev) { if (rtwdev->chip->ops->efuse_grant) rtwdev->chip->ops->efuse_grant(rtwdev, true); } static inline void rtw_chip_efuse_grant_off(struct rtw_dev *rtwdev) { if (rtwdev->chip->ops->efuse_grant) rtwdev->chip->ops->efuse_grant(rtwdev, false); } static inline bool rtw_chip_wcpu_11n(struct rtw_dev *rtwdev) { return rtwdev->chip->wlan_cpu == RTW_WCPU_11N; } static inline bool rtw_chip_wcpu_11ac(struct rtw_dev *rtwdev) { return rtwdev->chip->wlan_cpu == RTW_WCPU_11AC; } static inline bool rtw_chip_has_rx_ldpc(struct rtw_dev *rtwdev) { return rtwdev->chip->rx_ldpc; } static inline bool rtw_chip_has_tx_stbc(struct rtw_dev *rtwdev) { return rtwdev->chip->tx_stbc; } static inline void rtw_release_macid(struct rtw_dev *rtwdev, u8 mac_id) { clear_bit(mac_id, rtwdev->mac_id_map); } static inline int rtw_chip_dump_fw_crash(struct rtw_dev *rtwdev) { if (rtwdev->chip->ops->dump_fw_crash) return rtwdev->chip->ops->dump_fw_crash(rtwdev); return 0; } +static inline +enum nl80211_band rtw_hw_to_nl80211_band(enum rtw_supported_band hw_band) +{ + switch (hw_band) { + default: + case RTW_BAND_2G: + return NL80211_BAND_2GHZ; + case RTW_BAND_5G: + return NL80211_BAND_5GHZ; + case RTW_BAND_60G: + return NL80211_BAND_60GHZ; + } +} + void rtw_set_rx_freq_band(struct rtw_rx_pkt_stat *pkt_stat, u8 channel); void rtw_set_dtim_period(struct rtw_dev *rtwdev, int dtim_period); void rtw_get_channel_params(struct cfg80211_chan_def *chandef, struct rtw_channel_params *ch_param); bool check_hw_ready(struct rtw_dev *rtwdev, u32 addr, u32 mask, u32 target); bool ltecoex_read_reg(struct rtw_dev *rtwdev, u16 offset, u32 *val); bool ltecoex_reg_write(struct rtw_dev *rtwdev, u16 offset, u32 value); void rtw_restore_reg(struct rtw_dev *rtwdev, struct rtw_backup_info *bckp, u32 num); void rtw_desc_to_mcsrate(u16 rate, u8 *mcs, u8 *nss); void rtw_set_channel(struct rtw_dev *rtwdev); void rtw_chip_prepare_tx(struct rtw_dev *rtwdev); void rtw_vif_port_config(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif, u32 config); void rtw_tx_report_purge_timer(struct timer_list *t); void rtw_update_sta_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si, bool reset_ra_mask); void rtw_core_scan_start(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif, const u8 *mac_addr, bool hw_scan); void rtw_core_scan_complete(struct rtw_dev *rtwdev, struct ieee80211_vif *vif, bool hw_scan); int rtw_core_start(struct rtw_dev *rtwdev); void rtw_core_stop(struct rtw_dev *rtwdev); int rtw_chip_info_setup(struct rtw_dev *rtwdev); int rtw_core_init(struct rtw_dev *rtwdev); void rtw_core_deinit(struct rtw_dev *rtwdev); int rtw_register_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw); void rtw_unregister_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw); u16 rtw_desc_to_bitrate(u8 desc_rate); void rtw_vif_assoc_changed(struct rtw_vif *rtwvif, struct ieee80211_bss_conf *conf); int rtw_sta_add(struct rtw_dev *rtwdev, struct ieee80211_sta *sta, struct ieee80211_vif *vif); void rtw_sta_remove(struct rtw_dev *rtwdev, struct ieee80211_sta *sta, bool fw_exist); void rtw_fw_recovery(struct rtw_dev *rtwdev); void rtw_core_fw_scan_notify(struct rtw_dev *rtwdev, bool start); int rtw_dump_fw(struct rtw_dev *rtwdev, const u32 ocp_src, u32 size, u32 fwcd_item); int rtw_dump_reg(struct rtw_dev *rtwdev, const u32 addr, const u32 size); void rtw_set_txrx_1ss(struct rtw_dev *rtwdev, bool config_1ss); +void rtw_update_channel(struct rtw_dev *rtwdev, u8 center_channel, + u8 primary_channel, enum rtw_supported_band band, + enum rtw_bandwidth bandwidth); +void rtw_core_port_switch(struct rtw_dev *rtwdev, struct ieee80211_vif *vif); +bool rtw_core_check_sta_active(struct rtw_dev *rtwdev); +void rtw_core_enable_beacon(struct rtw_dev *rtwdev, bool enable); #if defined(__linux__) #define rtw88_static_assert(_x) static_assert(_x) #elif defined(__FreeBSD__) #define rtw88_static_assert(_x) _Static_assert(_x, "bad array size") #endif #endif diff --git a/sys/contrib/dev/rtw88/pci.c b/sys/contrib/dev/rtw88/pci.c index 151400f6f892..d3980e0e97c4 100644 --- a/sys/contrib/dev/rtw88/pci.c +++ b/sys/contrib/dev/rtw88/pci.c @@ -1,1959 +1,1918 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* Copyright(c) 2018-2019 Realtek Corporation */ #if defined(__FreeBSD__) #define LINUXKPI_PARAM_PREFIX rtw88_pci_ #endif #include #include #include "main.h" #include "pci.h" #include "reg.h" #include "tx.h" #include "rx.h" #include "fw.h" #include "ps.h" #include "debug.h" #if defined(__FreeBSD__) #include #endif static bool rtw_disable_msi; static bool rtw_pci_disable_aspm; module_param_named(disable_msi, rtw_disable_msi, bool, 0644); module_param_named(disable_aspm, rtw_pci_disable_aspm, bool, 0644); MODULE_PARM_DESC(disable_msi, "Set Y to disable MSI interrupt support"); MODULE_PARM_DESC(disable_aspm, "Set Y to disable PCI ASPM support"); static u32 rtw_pci_tx_queue_idx_addr[] = { [RTW_TX_QUEUE_BK] = RTK_PCI_TXBD_IDX_BKQ, [RTW_TX_QUEUE_BE] = RTK_PCI_TXBD_IDX_BEQ, [RTW_TX_QUEUE_VI] = RTK_PCI_TXBD_IDX_VIQ, [RTW_TX_QUEUE_VO] = RTK_PCI_TXBD_IDX_VOQ, [RTW_TX_QUEUE_MGMT] = RTK_PCI_TXBD_IDX_MGMTQ, [RTW_TX_QUEUE_HI0] = RTK_PCI_TXBD_IDX_HI0Q, [RTW_TX_QUEUE_H2C] = RTK_PCI_TXBD_IDX_H2CQ, }; -static u8 rtw_pci_get_tx_qsel(struct sk_buff *skb, u8 queue) +static u8 rtw_pci_get_tx_qsel(struct sk_buff *skb, + enum rtw_tx_queue_type queue) { switch (queue) { case RTW_TX_QUEUE_BCN: return TX_DESC_QSEL_BEACON; case RTW_TX_QUEUE_H2C: return TX_DESC_QSEL_H2C; case RTW_TX_QUEUE_MGMT: return TX_DESC_QSEL_MGMT; case RTW_TX_QUEUE_HI0: return TX_DESC_QSEL_HIGH; default: return skb->priority; } }; static u8 rtw_pci_read8(struct rtw_dev *rtwdev, u32 addr) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; #if defined(__linux__) return readb(rtwpci->mmap + addr); #elif defined(__FreeBSD__) u8 val; val = bus_read_1((struct resource *)rtwpci->mmap, addr); rtw_dbg(rtwdev, RTW_DBG_IO_RW, "R08 (%#010x) -> %#04x\n", addr, val); return (val); #endif } static u16 rtw_pci_read16(struct rtw_dev *rtwdev, u32 addr) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; #if defined(__linux__) return readw(rtwpci->mmap + addr); #elif defined(__FreeBSD__) u16 val; val = bus_read_2((struct resource *)rtwpci->mmap, addr); rtw_dbg(rtwdev, RTW_DBG_IO_RW, "R16 (%#010x) -> %#06x\n", addr, val); return (val); #endif } static u32 rtw_pci_read32(struct rtw_dev *rtwdev, u32 addr) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; #if defined(__linux__) return readl(rtwpci->mmap + addr); #elif defined(__FreeBSD__) u32 val; val = bus_read_4((struct resource *)rtwpci->mmap, addr); rtw_dbg(rtwdev, RTW_DBG_IO_RW, "R32 (%#010x) -> %#010x\n", addr, val); return (val); #endif } static void rtw_pci_write8(struct rtw_dev *rtwdev, u32 addr, u8 val) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; #if defined(__linux__) writeb(val, rtwpci->mmap + addr); #elif defined(__FreeBSD__) rtw_dbg(rtwdev, RTW_DBG_IO_RW, "W08 (%#010x) <- %#04x\n", addr, val); return (bus_write_1((struct resource *)rtwpci->mmap, addr, val)); #endif } static void rtw_pci_write16(struct rtw_dev *rtwdev, u32 addr, u16 val) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; #if defined(__linux__) writew(val, rtwpci->mmap + addr); #elif defined(__FreeBSD__) rtw_dbg(rtwdev, RTW_DBG_IO_RW, "W16 (%#010x) <- %#06x\n", addr, val); return (bus_write_2((struct resource *)rtwpci->mmap, addr, val)); #endif } static void rtw_pci_write32(struct rtw_dev *rtwdev, u32 addr, u32 val) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; #if defined(__linux__) writel(val, rtwpci->mmap + addr); #elif defined(__FreeBSD__) rtw_dbg(rtwdev, RTW_DBG_IO_RW, "W32 (%#010x) <- %#010x\n", addr, val); return (bus_write_4((struct resource *)rtwpci->mmap, addr, val)); #endif } -#if defined(__linux__) && 0 -static inline void *rtw_pci_get_tx_desc(struct rtw_pci_tx_ring *tx_ring, u8 idx) -{ - int offset = tx_ring->r.desc_size * idx; - - return tx_ring->r.head + offset; -} -#endif - static void rtw_pci_free_tx_ring_skbs(struct rtw_dev *rtwdev, struct rtw_pci_tx_ring *tx_ring) { struct pci_dev *pdev = to_pci_dev(rtwdev->dev); struct rtw_pci_tx_data *tx_data; struct sk_buff *skb, *tmp; dma_addr_t dma; /* free every skb remained in tx list */ skb_queue_walk_safe(&tx_ring->queue, skb, tmp) { __skb_unlink(skb, &tx_ring->queue); tx_data = rtw_pci_get_tx_data(skb); dma = tx_data->dma; dma_unmap_single(&pdev->dev, dma, skb->len, DMA_TO_DEVICE); dev_kfree_skb_any(skb); } } static void rtw_pci_free_tx_ring(struct rtw_dev *rtwdev, struct rtw_pci_tx_ring *tx_ring) { struct pci_dev *pdev = to_pci_dev(rtwdev->dev); u8 *head = tx_ring->r.head; u32 len = tx_ring->r.len; int ring_sz = len * tx_ring->r.desc_size; rtw_pci_free_tx_ring_skbs(rtwdev, tx_ring); /* free the ring itself */ dma_free_coherent(&pdev->dev, ring_sz, head, tx_ring->r.dma); tx_ring->r.head = NULL; } static void rtw_pci_free_rx_ring_skbs(struct rtw_dev *rtwdev, struct rtw_pci_rx_ring *rx_ring) { struct pci_dev *pdev = to_pci_dev(rtwdev->dev); struct sk_buff *skb; int buf_sz = RTK_PCI_RX_BUF_SIZE; dma_addr_t dma; int i; for (i = 0; i < rx_ring->r.len; i++) { skb = rx_ring->buf[i]; if (!skb) continue; dma = *((dma_addr_t *)skb->cb); dma_unmap_single(&pdev->dev, dma, buf_sz, DMA_FROM_DEVICE); dev_kfree_skb(skb); rx_ring->buf[i] = NULL; } } static void rtw_pci_free_rx_ring(struct rtw_dev *rtwdev, struct rtw_pci_rx_ring *rx_ring) { struct pci_dev *pdev = to_pci_dev(rtwdev->dev); u8 *head = rx_ring->r.head; int ring_sz = rx_ring->r.desc_size * rx_ring->r.len; rtw_pci_free_rx_ring_skbs(rtwdev, rx_ring); dma_free_coherent(&pdev->dev, ring_sz, head, rx_ring->r.dma); } static void rtw_pci_free_trx_ring(struct rtw_dev *rtwdev) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; struct rtw_pci_tx_ring *tx_ring; struct rtw_pci_rx_ring *rx_ring; int i; for (i = 0; i < RTK_MAX_TX_QUEUE_NUM; i++) { tx_ring = &rtwpci->tx_rings[i]; rtw_pci_free_tx_ring(rtwdev, tx_ring); } for (i = 0; i < RTK_MAX_RX_QUEUE_NUM; i++) { rx_ring = &rtwpci->rx_rings[i]; rtw_pci_free_rx_ring(rtwdev, rx_ring); } } static int rtw_pci_init_tx_ring(struct rtw_dev *rtwdev, struct rtw_pci_tx_ring *tx_ring, u8 desc_size, u32 len) { struct pci_dev *pdev = to_pci_dev(rtwdev->dev); int ring_sz = desc_size * len; dma_addr_t dma; u8 *head; if (len > TRX_BD_IDX_MASK) { rtw_err(rtwdev, "len %d exceeds maximum TX entries\n", len); return -EINVAL; } head = dma_alloc_coherent(&pdev->dev, ring_sz, &dma, GFP_KERNEL); if (!head) { rtw_err(rtwdev, "failed to allocate tx ring\n"); return -ENOMEM; } skb_queue_head_init(&tx_ring->queue); tx_ring->r.head = head; tx_ring->r.dma = dma; tx_ring->r.len = len; tx_ring->r.desc_size = desc_size; tx_ring->r.wp = 0; tx_ring->r.rp = 0; return 0; } static int rtw_pci_reset_rx_desc(struct rtw_dev *rtwdev, struct sk_buff *skb, struct rtw_pci_rx_ring *rx_ring, u32 idx, u32 desc_sz) { struct pci_dev *pdev = to_pci_dev(rtwdev->dev); struct rtw_pci_rx_buffer_desc *buf_desc; int buf_sz = RTK_PCI_RX_BUF_SIZE; dma_addr_t dma; if (!skb) return -EINVAL; dma = dma_map_single(&pdev->dev, skb->data, buf_sz, DMA_FROM_DEVICE); if (dma_mapping_error(&pdev->dev, dma)) return -EBUSY; *((dma_addr_t *)skb->cb) = dma; buf_desc = (struct rtw_pci_rx_buffer_desc *)(rx_ring->r.head + idx * desc_sz); memset(buf_desc, 0, sizeof(*buf_desc)); buf_desc->buf_size = cpu_to_le16(RTK_PCI_RX_BUF_SIZE); buf_desc->dma = cpu_to_le32(dma); return 0; } static void rtw_pci_sync_rx_desc_device(struct rtw_dev *rtwdev, dma_addr_t dma, struct rtw_pci_rx_ring *rx_ring, u32 idx, u32 desc_sz) { struct device *dev = rtwdev->dev; struct rtw_pci_rx_buffer_desc *buf_desc; int buf_sz = RTK_PCI_RX_BUF_SIZE; dma_sync_single_for_device(dev, dma, buf_sz, DMA_FROM_DEVICE); buf_desc = (struct rtw_pci_rx_buffer_desc *)(rx_ring->r.head + idx * desc_sz); memset(buf_desc, 0, sizeof(*buf_desc)); buf_desc->buf_size = cpu_to_le16(RTK_PCI_RX_BUF_SIZE); buf_desc->dma = cpu_to_le32(dma); } static int rtw_pci_init_rx_ring(struct rtw_dev *rtwdev, struct rtw_pci_rx_ring *rx_ring, u8 desc_size, u32 len) { struct pci_dev *pdev = to_pci_dev(rtwdev->dev); struct sk_buff *skb = NULL; dma_addr_t dma; u8 *head; int ring_sz = desc_size * len; int buf_sz = RTK_PCI_RX_BUF_SIZE; int i, allocated; int ret = 0; head = dma_alloc_coherent(&pdev->dev, ring_sz, &dma, GFP_KERNEL); if (!head) { rtw_err(rtwdev, "failed to allocate rx ring\n"); return -ENOMEM; } rx_ring->r.head = head; for (i = 0; i < len; i++) { skb = dev_alloc_skb(buf_sz); if (!skb) { allocated = i; ret = -ENOMEM; goto err_out; } memset(skb->data, 0, buf_sz); rx_ring->buf[i] = skb; ret = rtw_pci_reset_rx_desc(rtwdev, skb, rx_ring, i, desc_size); if (ret) { allocated = i; dev_kfree_skb_any(skb); goto err_out; } } rx_ring->r.dma = dma; rx_ring->r.len = len; rx_ring->r.desc_size = desc_size; rx_ring->r.wp = 0; rx_ring->r.rp = 0; return 0; err_out: for (i = 0; i < allocated; i++) { skb = rx_ring->buf[i]; if (!skb) continue; dma = *((dma_addr_t *)skb->cb); dma_unmap_single(&pdev->dev, dma, buf_sz, DMA_FROM_DEVICE); dev_kfree_skb_any(skb); rx_ring->buf[i] = NULL; } dma_free_coherent(&pdev->dev, ring_sz, head, dma); rtw_err(rtwdev, "failed to init rx buffer\n"); return ret; } static int rtw_pci_init_trx_ring(struct rtw_dev *rtwdev) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; struct rtw_pci_tx_ring *tx_ring; struct rtw_pci_rx_ring *rx_ring; - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; int i = 0, j = 0, tx_alloced = 0, rx_alloced = 0; int tx_desc_size, rx_desc_size; u32 len; int ret; tx_desc_size = chip->tx_buf_desc_sz; for (i = 0; i < RTK_MAX_TX_QUEUE_NUM; i++) { tx_ring = &rtwpci->tx_rings[i]; len = max_num_of_tx_queue(i); ret = rtw_pci_init_tx_ring(rtwdev, tx_ring, tx_desc_size, len); if (ret) goto out; } rx_desc_size = chip->rx_buf_desc_sz; for (j = 0; j < RTK_MAX_RX_QUEUE_NUM; j++) { rx_ring = &rtwpci->rx_rings[j]; ret = rtw_pci_init_rx_ring(rtwdev, rx_ring, rx_desc_size, RTK_MAX_RX_DESC_NUM); if (ret) goto out; } return 0; out: tx_alloced = i; for (i = 0; i < tx_alloced; i++) { tx_ring = &rtwpci->tx_rings[i]; rtw_pci_free_tx_ring(rtwdev, tx_ring); } rx_alloced = j; for (j = 0; j < rx_alloced; j++) { rx_ring = &rtwpci->rx_rings[j]; rtw_pci_free_rx_ring(rtwdev, rx_ring); } return ret; } static void rtw_pci_deinit(struct rtw_dev *rtwdev) { rtw_pci_free_trx_ring(rtwdev); } static int rtw_pci_init(struct rtw_dev *rtwdev) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; int ret = 0; rtwpci->irq_mask[0] = IMR_HIGHDOK | IMR_MGNTDOK | IMR_BKDOK | IMR_BEDOK | IMR_VIDOK | IMR_VODOK | IMR_ROK | IMR_BCNDMAINT_E | IMR_C2HCMD | 0; rtwpci->irq_mask[1] = IMR_TXFOVW | 0; rtwpci->irq_mask[3] = IMR_H2CDOK | 0; spin_lock_init(&rtwpci->irq_lock); spin_lock_init(&rtwpci->hwirq_lock); ret = rtw_pci_init_trx_ring(rtwdev); return ret; } static void rtw_pci_reset_buf_desc(struct rtw_dev *rtwdev) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; u32 len; u8 tmp; dma_addr_t dma; tmp = rtw_read8(rtwdev, RTK_PCI_CTRL + 3); rtw_write8(rtwdev, RTK_PCI_CTRL + 3, tmp | 0xf7); dma = rtwpci->tx_rings[RTW_TX_QUEUE_BCN].r.dma; rtw_write32(rtwdev, RTK_PCI_TXBD_DESA_BCNQ, dma); if (!rtw_chip_wcpu_11n(rtwdev)) { len = rtwpci->tx_rings[RTW_TX_QUEUE_H2C].r.len; dma = rtwpci->tx_rings[RTW_TX_QUEUE_H2C].r.dma; rtwpci->tx_rings[RTW_TX_QUEUE_H2C].r.rp = 0; rtwpci->tx_rings[RTW_TX_QUEUE_H2C].r.wp = 0; rtw_write16(rtwdev, RTK_PCI_TXBD_NUM_H2CQ, len & TRX_BD_IDX_MASK); rtw_write32(rtwdev, RTK_PCI_TXBD_DESA_H2CQ, dma); } len = rtwpci->tx_rings[RTW_TX_QUEUE_BK].r.len; dma = rtwpci->tx_rings[RTW_TX_QUEUE_BK].r.dma; rtwpci->tx_rings[RTW_TX_QUEUE_BK].r.rp = 0; rtwpci->tx_rings[RTW_TX_QUEUE_BK].r.wp = 0; rtw_write16(rtwdev, RTK_PCI_TXBD_NUM_BKQ, len & TRX_BD_IDX_MASK); rtw_write32(rtwdev, RTK_PCI_TXBD_DESA_BKQ, dma); len = rtwpci->tx_rings[RTW_TX_QUEUE_BE].r.len; dma = rtwpci->tx_rings[RTW_TX_QUEUE_BE].r.dma; rtwpci->tx_rings[RTW_TX_QUEUE_BE].r.rp = 0; rtwpci->tx_rings[RTW_TX_QUEUE_BE].r.wp = 0; rtw_write16(rtwdev, RTK_PCI_TXBD_NUM_BEQ, len & TRX_BD_IDX_MASK); rtw_write32(rtwdev, RTK_PCI_TXBD_DESA_BEQ, dma); len = rtwpci->tx_rings[RTW_TX_QUEUE_VO].r.len; dma = rtwpci->tx_rings[RTW_TX_QUEUE_VO].r.dma; rtwpci->tx_rings[RTW_TX_QUEUE_VO].r.rp = 0; rtwpci->tx_rings[RTW_TX_QUEUE_VO].r.wp = 0; rtw_write16(rtwdev, RTK_PCI_TXBD_NUM_VOQ, len & TRX_BD_IDX_MASK); rtw_write32(rtwdev, RTK_PCI_TXBD_DESA_VOQ, dma); len = rtwpci->tx_rings[RTW_TX_QUEUE_VI].r.len; dma = rtwpci->tx_rings[RTW_TX_QUEUE_VI].r.dma; rtwpci->tx_rings[RTW_TX_QUEUE_VI].r.rp = 0; rtwpci->tx_rings[RTW_TX_QUEUE_VI].r.wp = 0; rtw_write16(rtwdev, RTK_PCI_TXBD_NUM_VIQ, len & TRX_BD_IDX_MASK); rtw_write32(rtwdev, RTK_PCI_TXBD_DESA_VIQ, dma); len = rtwpci->tx_rings[RTW_TX_QUEUE_MGMT].r.len; dma = rtwpci->tx_rings[RTW_TX_QUEUE_MGMT].r.dma; rtwpci->tx_rings[RTW_TX_QUEUE_MGMT].r.rp = 0; rtwpci->tx_rings[RTW_TX_QUEUE_MGMT].r.wp = 0; rtw_write16(rtwdev, RTK_PCI_TXBD_NUM_MGMTQ, len & TRX_BD_IDX_MASK); rtw_write32(rtwdev, RTK_PCI_TXBD_DESA_MGMTQ, dma); len = rtwpci->tx_rings[RTW_TX_QUEUE_HI0].r.len; dma = rtwpci->tx_rings[RTW_TX_QUEUE_HI0].r.dma; rtwpci->tx_rings[RTW_TX_QUEUE_HI0].r.rp = 0; rtwpci->tx_rings[RTW_TX_QUEUE_HI0].r.wp = 0; rtw_write16(rtwdev, RTK_PCI_TXBD_NUM_HI0Q, len & TRX_BD_IDX_MASK); rtw_write32(rtwdev, RTK_PCI_TXBD_DESA_HI0Q, dma); len = rtwpci->rx_rings[RTW_RX_QUEUE_MPDU].r.len; dma = rtwpci->rx_rings[RTW_RX_QUEUE_MPDU].r.dma; rtwpci->rx_rings[RTW_RX_QUEUE_MPDU].r.rp = 0; rtwpci->rx_rings[RTW_RX_QUEUE_MPDU].r.wp = 0; rtw_write16(rtwdev, RTK_PCI_RXBD_NUM_MPDUQ, len & TRX_BD_IDX_MASK); rtw_write32(rtwdev, RTK_PCI_RXBD_DESA_MPDUQ, dma); /* reset read/write point */ rtw_write32(rtwdev, RTK_PCI_TXBD_RWPTR_CLR, 0xffffffff); /* reset H2C Queue index in a single write */ if (rtw_chip_wcpu_11ac(rtwdev)) rtw_write32_set(rtwdev, RTK_PCI_TXBD_H2CQ_CSR, BIT_CLR_H2CQ_HOST_IDX | BIT_CLR_H2CQ_HW_IDX); } static void rtw_pci_reset_trx_ring(struct rtw_dev *rtwdev) { rtw_pci_reset_buf_desc(rtwdev); } static void rtw_pci_enable_interrupt(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci, bool exclude_rx) { unsigned long flags; u32 imr0_unmask = exclude_rx ? IMR_ROK : 0; spin_lock_irqsave(&rtwpci->hwirq_lock, flags); rtw_write32(rtwdev, RTK_PCI_HIMR0, rtwpci->irq_mask[0] & ~imr0_unmask); rtw_write32(rtwdev, RTK_PCI_HIMR1, rtwpci->irq_mask[1]); if (rtw_chip_wcpu_11ac(rtwdev)) rtw_write32(rtwdev, RTK_PCI_HIMR3, rtwpci->irq_mask[3]); rtwpci->irq_enabled = true; spin_unlock_irqrestore(&rtwpci->hwirq_lock, flags); } static void rtw_pci_disable_interrupt(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci) { unsigned long flags; spin_lock_irqsave(&rtwpci->hwirq_lock, flags); if (!rtwpci->irq_enabled) goto out; rtw_write32(rtwdev, RTK_PCI_HIMR0, 0); rtw_write32(rtwdev, RTK_PCI_HIMR1, 0); if (rtw_chip_wcpu_11ac(rtwdev)) rtw_write32(rtwdev, RTK_PCI_HIMR3, 0); rtwpci->irq_enabled = false; out: spin_unlock_irqrestore(&rtwpci->hwirq_lock, flags); } static void rtw_pci_dma_reset(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci) { /* reset dma and rx tag */ rtw_write32_set(rtwdev, RTK_PCI_CTRL, BIT_RST_TRXDMA_INTF | BIT_RX_TAG_EN); rtwpci->rx_tag = 0; } static int rtw_pci_setup(struct rtw_dev *rtwdev) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; rtw_pci_reset_trx_ring(rtwdev); rtw_pci_dma_reset(rtwdev, rtwpci); return 0; } static void rtw_pci_dma_release(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci) { struct rtw_pci_tx_ring *tx_ring; - u8 queue; + enum rtw_tx_queue_type queue; rtw_pci_reset_trx_ring(rtwdev); for (queue = 0; queue < RTK_MAX_TX_QUEUE_NUM; queue++) { tx_ring = &rtwpci->tx_rings[queue]; rtw_pci_free_tx_ring_skbs(rtwdev, tx_ring); } } static void rtw_pci_napi_start(struct rtw_dev *rtwdev) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; if (test_and_set_bit(RTW_PCI_FLAG_NAPI_RUNNING, rtwpci->flags)) return; napi_enable(&rtwpci->napi); } static void rtw_pci_napi_stop(struct rtw_dev *rtwdev) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; if (!test_and_clear_bit(RTW_PCI_FLAG_NAPI_RUNNING, rtwpci->flags)) return; napi_synchronize(&rtwpci->napi); napi_disable(&rtwpci->napi); } static int rtw_pci_start(struct rtw_dev *rtwdev) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; rtw_pci_napi_start(rtwdev); spin_lock_bh(&rtwpci->irq_lock); rtwpci->running = true; rtw_pci_enable_interrupt(rtwdev, rtwpci, false); spin_unlock_bh(&rtwpci->irq_lock); return 0; } static void rtw_pci_stop(struct rtw_dev *rtwdev) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; struct pci_dev *pdev = rtwpci->pdev; spin_lock_bh(&rtwpci->irq_lock); rtwpci->running = false; rtw_pci_disable_interrupt(rtwdev, rtwpci); spin_unlock_bh(&rtwpci->irq_lock); synchronize_irq(pdev->irq); rtw_pci_napi_stop(rtwdev); spin_lock_bh(&rtwpci->irq_lock); rtw_pci_dma_release(rtwdev, rtwpci); spin_unlock_bh(&rtwpci->irq_lock); } static void rtw_pci_deep_ps_enter(struct rtw_dev *rtwdev) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; struct rtw_pci_tx_ring *tx_ring; + enum rtw_tx_queue_type queue; bool tx_empty = true; - u8 queue; if (rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_TX_WAKE)) goto enter_deep_ps; lockdep_assert_held(&rtwpci->irq_lock); /* Deep PS state is not allowed to TX-DMA */ for (queue = 0; queue < RTK_MAX_TX_QUEUE_NUM; queue++) { /* BCN queue is rsvd page, does not have DMA interrupt * H2C queue is managed by firmware */ if (queue == RTW_TX_QUEUE_BCN || queue == RTW_TX_QUEUE_H2C) continue; tx_ring = &rtwpci->tx_rings[queue]; /* check if there is any skb DMAing */ if (skb_queue_len(&tx_ring->queue)) { tx_empty = false; break; } } if (!tx_empty) { rtw_dbg(rtwdev, RTW_DBG_PS, "TX path not empty, cannot enter deep power save state\n"); return; } enter_deep_ps: set_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags); rtw_power_mode_change(rtwdev, true); } static void rtw_pci_deep_ps_leave(struct rtw_dev *rtwdev) { #if defined(__linux__) struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; lockdep_assert_held(&rtwpci->irq_lock); #elif defined(__FreeBSD__) lockdep_assert_held(&((struct rtw_pci *)rtwdev->priv)->irq_lock); #endif if (test_and_clear_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags)) rtw_power_mode_change(rtwdev, false); } static void rtw_pci_deep_ps(struct rtw_dev *rtwdev, bool enter) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; spin_lock_bh(&rtwpci->irq_lock); if (enter && !test_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags)) rtw_pci_deep_ps_enter(rtwdev); if (!enter && test_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags)) rtw_pci_deep_ps_leave(rtwdev); spin_unlock_bh(&rtwpci->irq_lock); } -static u8 ac_to_hwq[] = { - [IEEE80211_AC_VO] = RTW_TX_QUEUE_VO, - [IEEE80211_AC_VI] = RTW_TX_QUEUE_VI, - [IEEE80211_AC_BE] = RTW_TX_QUEUE_BE, - [IEEE80211_AC_BK] = RTW_TX_QUEUE_BK, -}; - -#if defined(__linux__) -static_assert(ARRAY_SIZE(ac_to_hwq) == IEEE80211_NUM_ACS); -#elif defined(__FreeBSD__) -rtw88_static_assert(ARRAY_SIZE(ac_to_hwq) == IEEE80211_NUM_ACS); -#endif - -static u8 rtw_hw_queue_mapping(struct sk_buff *skb) -{ - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - __le16 fc = hdr->frame_control; - u8 q_mapping = skb_get_queue_mapping(skb); - u8 queue; - - if (unlikely(ieee80211_is_beacon(fc))) - queue = RTW_TX_QUEUE_BCN; - else if (unlikely(ieee80211_is_mgmt(fc) || ieee80211_is_ctl(fc))) - queue = RTW_TX_QUEUE_MGMT; - else if (is_broadcast_ether_addr(hdr->addr1) || - is_multicast_ether_addr(hdr->addr1)) - queue = RTW_TX_QUEUE_HI0; - else if (WARN_ON_ONCE(q_mapping >= ARRAY_SIZE(ac_to_hwq))) - queue = ac_to_hwq[IEEE80211_AC_BE]; - else - queue = ac_to_hwq[q_mapping]; - - return queue; -} - static void rtw_pci_release_rsvd_page(struct rtw_pci *rtwpci, struct rtw_pci_tx_ring *ring) { struct sk_buff *prev = skb_dequeue(&ring->queue); struct rtw_pci_tx_data *tx_data; dma_addr_t dma; if (!prev) return; tx_data = rtw_pci_get_tx_data(prev); dma = tx_data->dma; dma_unmap_single(&rtwpci->pdev->dev, dma, prev->len, DMA_TO_DEVICE); dev_kfree_skb_any(prev); } static void rtw_pci_dma_check(struct rtw_dev *rtwdev, struct rtw_pci_rx_ring *rx_ring, u32 idx) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_pci_rx_buffer_desc *buf_desc; u32 desc_sz = chip->rx_buf_desc_sz; u16 total_pkt_size; buf_desc = (struct rtw_pci_rx_buffer_desc *)(rx_ring->r.head + idx * desc_sz); total_pkt_size = le16_to_cpu(buf_desc->total_pkt_size); /* rx tag mismatch, throw a warning */ if (total_pkt_size != rtwpci->rx_tag) rtw_warn(rtwdev, "pci bus timeout, check dma status\n"); rtwpci->rx_tag = (rtwpci->rx_tag + 1) % RX_TAG_MAX; } static u32 __pci_get_hw_tx_ring_rp(struct rtw_dev *rtwdev, u8 pci_q) { u32 bd_idx_addr = rtw_pci_tx_queue_idx_addr[pci_q]; u32 bd_idx = rtw_read16(rtwdev, bd_idx_addr + 2); return FIELD_GET(TRX_BD_IDX_MASK, bd_idx); } static void __pci_flush_queue(struct rtw_dev *rtwdev, u8 pci_q, bool drop) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; struct rtw_pci_tx_ring *ring = &rtwpci->tx_rings[pci_q]; u32 cur_rp; u8 i; /* Because the time taked by the I/O in __pci_get_hw_tx_ring_rp is a * bit dynamic, it's hard to define a reasonable fixed total timeout to * use read_poll_timeout* helper. Instead, we can ensure a reasonable * polling times, so we just use for loop with udelay here. */ for (i = 0; i < 30; i++) { cur_rp = __pci_get_hw_tx_ring_rp(rtwdev, pci_q); if (cur_rp == ring->r.wp) return; udelay(1); } if (!drop) rtw_warn(rtwdev, "timed out to flush pci tx ring[%d]\n", pci_q); } static void __rtw_pci_flush_queues(struct rtw_dev *rtwdev, u32 pci_queues, bool drop) { u8 q; for (q = 0; q < RTK_MAX_TX_QUEUE_NUM; q++) { - /* It may be not necessary to flush BCN and H2C tx queues. */ - if (q == RTW_TX_QUEUE_BCN || q == RTW_TX_QUEUE_H2C) + /* Unnecessary to flush BCN, H2C and HI tx queues. */ + if (q == RTW_TX_QUEUE_BCN || q == RTW_TX_QUEUE_H2C || + q == RTW_TX_QUEUE_HI0) continue; if (pci_queues & BIT(q)) __pci_flush_queue(rtwdev, q, drop); } } static void rtw_pci_flush_queues(struct rtw_dev *rtwdev, u32 queues, bool drop) { u32 pci_queues = 0; u8 i; /* If all of the hardware queues are requested to flush, * flush all of the pci queues. */ if (queues == BIT(rtwdev->hw->queues) - 1) { pci_queues = BIT(RTK_MAX_TX_QUEUE_NUM) - 1; } else { for (i = 0; i < rtwdev->hw->queues; i++) if (queues & BIT(i)) - pci_queues |= BIT(ac_to_hwq[i]); + pci_queues |= BIT(rtw_tx_ac_to_hwq(i)); } __rtw_pci_flush_queues(rtwdev, pci_queues, drop); } -static void rtw_pci_tx_kick_off_queue(struct rtw_dev *rtwdev, u8 queue) +static void rtw_pci_tx_kick_off_queue(struct rtw_dev *rtwdev, + enum rtw_tx_queue_type queue) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; struct rtw_pci_tx_ring *ring; u32 bd_idx; ring = &rtwpci->tx_rings[queue]; bd_idx = rtw_pci_tx_queue_idx_addr[queue]; spin_lock_bh(&rtwpci->irq_lock); if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_TX_WAKE)) rtw_pci_deep_ps_leave(rtwdev); rtw_write16(rtwdev, bd_idx, ring->r.wp & TRX_BD_IDX_MASK); spin_unlock_bh(&rtwpci->irq_lock); } static void rtw_pci_tx_kick_off(struct rtw_dev *rtwdev) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; - u8 queue; + enum rtw_tx_queue_type queue; for (queue = 0; queue < RTK_MAX_TX_QUEUE_NUM; queue++) if (test_and_clear_bit(queue, rtwpci->tx_queued)) rtw_pci_tx_kick_off_queue(rtwdev, queue); } static int rtw_pci_tx_write_data(struct rtw_dev *rtwdev, struct rtw_tx_pkt_info *pkt_info, - struct sk_buff *skb, u8 queue) + struct sk_buff *skb, + enum rtw_tx_queue_type queue) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_pci_tx_ring *ring; struct rtw_pci_tx_data *tx_data; dma_addr_t dma; u32 tx_pkt_desc_sz = chip->tx_pkt_desc_sz; u32 tx_buf_desc_sz = chip->tx_buf_desc_sz; u32 size; u32 psb_len; u8 *pkt_desc; struct rtw_pci_tx_buffer_desc *buf_desc; ring = &rtwpci->tx_rings[queue]; size = skb->len; if (queue == RTW_TX_QUEUE_BCN) rtw_pci_release_rsvd_page(rtwpci, ring); else if (!avail_desc(ring->r.wp, ring->r.rp, ring->r.len)) return -ENOSPC; pkt_desc = skb_push(skb, chip->tx_pkt_desc_sz); memset(pkt_desc, 0, tx_pkt_desc_sz); pkt_info->qsel = rtw_pci_get_tx_qsel(skb, queue); rtw_tx_fill_tx_desc(pkt_info, skb); dma = dma_map_single(&rtwpci->pdev->dev, skb->data, skb->len, DMA_TO_DEVICE); if (dma_mapping_error(&rtwpci->pdev->dev, dma)) return -EBUSY; /* after this we got dma mapped, there is no way back */ buf_desc = get_tx_buffer_desc(ring, tx_buf_desc_sz); memset(buf_desc, 0, tx_buf_desc_sz); psb_len = (skb->len - 1) / 128 + 1; if (queue == RTW_TX_QUEUE_BCN) psb_len |= 1 << RTK_PCI_TXBD_OWN_OFFSET; buf_desc[0].psb_len = cpu_to_le16(psb_len); buf_desc[0].buf_size = cpu_to_le16(tx_pkt_desc_sz); buf_desc[0].dma = cpu_to_le32(dma); buf_desc[1].buf_size = cpu_to_le16(size); buf_desc[1].dma = cpu_to_le32(dma + tx_pkt_desc_sz); tx_data = rtw_pci_get_tx_data(skb); tx_data->dma = dma; tx_data->sn = pkt_info->sn; spin_lock_bh(&rtwpci->irq_lock); skb_queue_tail(&ring->queue, skb); if (queue == RTW_TX_QUEUE_BCN) goto out_unlock; /* update write-index, and kick it off later */ set_bit(queue, rtwpci->tx_queued); if (++ring->r.wp >= ring->r.len) ring->r.wp = 0; out_unlock: spin_unlock_bh(&rtwpci->irq_lock); return 0; } static int rtw_pci_write_data_rsvd_page(struct rtw_dev *rtwdev, u8 *buf, u32 size) { struct sk_buff *skb; struct rtw_tx_pkt_info pkt_info = {0}; u8 reg_bcn_work; int ret; skb = rtw_tx_write_data_rsvd_page_get(rtwdev, &pkt_info, buf, size); if (!skb) return -ENOMEM; ret = rtw_pci_tx_write_data(rtwdev, &pkt_info, skb, RTW_TX_QUEUE_BCN); if (ret) { #if defined(__FreeBSD__) dev_kfree_skb_any(skb); #endif rtw_err(rtwdev, "failed to write rsvd page data\n"); return ret; } /* reserved pages go through beacon queue */ reg_bcn_work = rtw_read8(rtwdev, RTK_PCI_TXBD_BCN_WORK); reg_bcn_work |= BIT_PCI_BCNQ_FLAG; rtw_write8(rtwdev, RTK_PCI_TXBD_BCN_WORK, reg_bcn_work); return 0; } static int rtw_pci_write_data_h2c(struct rtw_dev *rtwdev, u8 *buf, u32 size) { struct sk_buff *skb; struct rtw_tx_pkt_info pkt_info = {0}; int ret; skb = rtw_tx_write_data_h2c_get(rtwdev, &pkt_info, buf, size); if (!skb) return -ENOMEM; ret = rtw_pci_tx_write_data(rtwdev, &pkt_info, skb, RTW_TX_QUEUE_H2C); if (ret) { #if defined(__FreeBSD__) dev_kfree_skb_any(skb); #endif rtw_err(rtwdev, "failed to write h2c data\n"); return ret; } rtw_pci_tx_kick_off_queue(rtwdev, RTW_TX_QUEUE_H2C); return 0; } static int rtw_pci_tx_write(struct rtw_dev *rtwdev, struct rtw_tx_pkt_info *pkt_info, struct sk_buff *skb) { + enum rtw_tx_queue_type queue = rtw_tx_queue_mapping(skb); struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; struct rtw_pci_tx_ring *ring; - u8 queue = rtw_hw_queue_mapping(skb); int ret; ret = rtw_pci_tx_write_data(rtwdev, pkt_info, skb, queue); if (ret) return ret; ring = &rtwpci->tx_rings[queue]; spin_lock_bh(&rtwpci->irq_lock); if (avail_desc(ring->r.wp, ring->r.rp, ring->r.len) < 2) { ieee80211_stop_queue(rtwdev->hw, skb_get_queue_mapping(skb)); ring->queue_stopped = true; } spin_unlock_bh(&rtwpci->irq_lock); return 0; } static void rtw_pci_tx_isr(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci, u8 hw_queue) { struct ieee80211_hw *hw = rtwdev->hw; struct ieee80211_tx_info *info; struct rtw_pci_tx_ring *ring; struct rtw_pci_tx_data *tx_data; struct sk_buff *skb; u32 count; u32 bd_idx_addr; u32 bd_idx, cur_rp, rp_idx; u16 q_map; ring = &rtwpci->tx_rings[hw_queue]; bd_idx_addr = rtw_pci_tx_queue_idx_addr[hw_queue]; bd_idx = rtw_read32(rtwdev, bd_idx_addr); cur_rp = bd_idx >> 16; cur_rp &= TRX_BD_IDX_MASK; rp_idx = ring->r.rp; if (cur_rp >= ring->r.rp) count = cur_rp - ring->r.rp; else count = ring->r.len - (ring->r.rp - cur_rp); while (count--) { skb = skb_dequeue(&ring->queue); if (!skb) { rtw_err(rtwdev, "failed to dequeue %d skb TX queue %d, BD=0x%08x, rp %d -> %d\n", count, hw_queue, bd_idx, ring->r.rp, cur_rp); break; } tx_data = rtw_pci_get_tx_data(skb); dma_unmap_single(&rtwpci->pdev->dev, tx_data->dma, skb->len, DMA_TO_DEVICE); /* just free command packets from host to card */ if (hw_queue == RTW_TX_QUEUE_H2C) { dev_kfree_skb_irq(skb); continue; } if (ring->queue_stopped && avail_desc(ring->r.wp, rp_idx, ring->r.len) > 4) { q_map = skb_get_queue_mapping(skb); ieee80211_wake_queue(hw, q_map); ring->queue_stopped = false; } if (++rp_idx >= ring->r.len) rp_idx = 0; skb_pull(skb, rtwdev->chip->tx_pkt_desc_sz); info = IEEE80211_SKB_CB(skb); /* enqueue to wait for tx report */ if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) { rtw_tx_report_enqueue(rtwdev, skb, tx_data->sn); continue; } /* always ACK for others, then they won't be marked as drop */ if (info->flags & IEEE80211_TX_CTL_NO_ACK) info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; else info->flags |= IEEE80211_TX_STAT_ACK; ieee80211_tx_info_clear_status(info); ieee80211_tx_status_irqsafe(hw, skb); } ring->r.rp = cur_rp; } static void rtw_pci_rx_isr(struct rtw_dev *rtwdev) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; struct napi_struct *napi = &rtwpci->napi; napi_schedule(napi); } static int rtw_pci_get_hw_rx_ring_nr(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci) { struct rtw_pci_rx_ring *ring; int count = 0; u32 tmp, cur_wp; ring = &rtwpci->rx_rings[RTW_RX_QUEUE_MPDU]; tmp = rtw_read32(rtwdev, RTK_PCI_RXBD_IDX_MPDUQ); cur_wp = u32_get_bits(tmp, TRX_BD_HW_IDX_MASK); if (cur_wp >= ring->r.wp) count = cur_wp - ring->r.wp; else count = ring->r.len - (ring->r.wp - cur_wp); return count; } static u32 rtw_pci_rx_napi(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci, u8 hw_queue, u32 limit) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct napi_struct *napi = &rtwpci->napi; struct rtw_pci_rx_ring *ring = &rtwpci->rx_rings[RTW_RX_QUEUE_MPDU]; struct rtw_rx_pkt_stat pkt_stat; struct ieee80211_rx_status rx_status; struct sk_buff *skb, *new; u32 cur_rp = ring->r.rp; u32 count, rx_done = 0; u32 pkt_offset; u32 pkt_desc_sz = chip->rx_pkt_desc_sz; u32 buf_desc_sz = chip->rx_buf_desc_sz; u32 new_len; u8 *rx_desc; dma_addr_t dma; count = rtw_pci_get_hw_rx_ring_nr(rtwdev, rtwpci); count = min(count, limit); while (count--) { rtw_pci_dma_check(rtwdev, ring, cur_rp); skb = ring->buf[cur_rp]; dma = *((dma_addr_t *)skb->cb); dma_sync_single_for_cpu(rtwdev->dev, dma, RTK_PCI_RX_BUF_SIZE, DMA_FROM_DEVICE); rx_desc = skb->data; chip->ops->query_rx_desc(rtwdev, rx_desc, &pkt_stat, &rx_status); /* offset from rx_desc to payload */ pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz + pkt_stat.shift; /* allocate a new skb for this frame, * discard the frame if none available */ new_len = pkt_stat.pkt_len + pkt_offset; new = dev_alloc_skb(new_len); if (WARN_ONCE(!new, "rx routine starvation\n")) goto next_rp; /* put the DMA data including rx_desc from phy to new skb */ skb_put_data(new, skb->data, new_len); if (pkt_stat.is_c2h) { rtw_fw_c2h_cmd_rx_irqsafe(rtwdev, pkt_offset, new); } else { /* remove rx_desc */ skb_pull(new, pkt_offset); rtw_rx_stats(rtwdev, pkt_stat.vif, new); memcpy(new->cb, &rx_status, sizeof(rx_status)); ieee80211_rx_napi(rtwdev->hw, NULL, new, napi); rx_done++; } next_rp: /* new skb delivered to mac80211, re-enable original skb DMA */ rtw_pci_sync_rx_desc_device(rtwdev, dma, ring, cur_rp, buf_desc_sz); /* host read next element in ring */ if (++cur_rp >= ring->r.len) cur_rp = 0; } ring->r.rp = cur_rp; /* 'rp', the last position we have read, is seen as previous posistion * of 'wp' that is used to calculate 'count' next time. */ ring->r.wp = cur_rp; rtw_write16(rtwdev, RTK_PCI_RXBD_IDX_MPDUQ, ring->r.rp); return rx_done; } static void rtw_pci_irq_recognized(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci, u32 *irq_status) { unsigned long flags; spin_lock_irqsave(&rtwpci->hwirq_lock, flags); irq_status[0] = rtw_read32(rtwdev, RTK_PCI_HISR0); irq_status[1] = rtw_read32(rtwdev, RTK_PCI_HISR1); if (rtw_chip_wcpu_11ac(rtwdev)) irq_status[3] = rtw_read32(rtwdev, RTK_PCI_HISR3); else irq_status[3] = 0; irq_status[0] &= rtwpci->irq_mask[0]; irq_status[1] &= rtwpci->irq_mask[1]; irq_status[3] &= rtwpci->irq_mask[3]; rtw_write32(rtwdev, RTK_PCI_HISR0, irq_status[0]); rtw_write32(rtwdev, RTK_PCI_HISR1, irq_status[1]); if (rtw_chip_wcpu_11ac(rtwdev)) rtw_write32(rtwdev, RTK_PCI_HISR3, irq_status[3]); spin_unlock_irqrestore(&rtwpci->hwirq_lock, flags); } static irqreturn_t rtw_pci_interrupt_handler(int irq, void *dev) { struct rtw_dev *rtwdev = dev; struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; /* disable RTW PCI interrupt to avoid more interrupts before the end of * thread function * * disable HIMR here to also avoid new HISR flag being raised before * the HISRs have been Write-1-cleared for MSI. If not all of the HISRs * are cleared, the edge-triggered interrupt will not be generated when * a new HISR flag is set. */ rtw_pci_disable_interrupt(rtwdev, rtwpci); return IRQ_WAKE_THREAD; } static irqreturn_t rtw_pci_interrupt_threadfn(int irq, void *dev) { struct rtw_dev *rtwdev = dev; struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; u32 irq_status[4]; bool rx = false; spin_lock_bh(&rtwpci->irq_lock); rtw_pci_irq_recognized(rtwdev, rtwpci, irq_status); if (irq_status[0] & IMR_MGNTDOK) rtw_pci_tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_MGMT); if (irq_status[0] & IMR_HIGHDOK) rtw_pci_tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_HI0); if (irq_status[0] & IMR_BEDOK) rtw_pci_tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_BE); if (irq_status[0] & IMR_BKDOK) rtw_pci_tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_BK); if (irq_status[0] & IMR_VODOK) rtw_pci_tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_VO); if (irq_status[0] & IMR_VIDOK) rtw_pci_tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_VI); if (irq_status[3] & IMR_H2CDOK) rtw_pci_tx_isr(rtwdev, rtwpci, RTW_TX_QUEUE_H2C); if (irq_status[0] & IMR_ROK) { rtw_pci_rx_isr(rtwdev); rx = true; } if (unlikely(irq_status[0] & IMR_C2HCMD)) rtw_fw_c2h_cmd_isr(rtwdev); /* all of the jobs for this interrupt have been done */ if (rtwpci->running) rtw_pci_enable_interrupt(rtwdev, rtwpci, rx); spin_unlock_bh(&rtwpci->irq_lock); return IRQ_HANDLED; } static int rtw_pci_io_mapping(struct rtw_dev *rtwdev, struct pci_dev *pdev) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; unsigned long len; u8 bar_id = 2; int ret; ret = pci_request_regions(pdev, KBUILD_MODNAME); if (ret) { rtw_err(rtwdev, "failed to request pci regions\n"); return ret; } #if defined(__FreeBSD__) ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); if (ret) { rtw_err(rtwdev, "failed to set dma mask to 32-bit\n"); goto err_release_regions; } ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); if (ret) { rtw_err(rtwdev, "failed to set consistent dma mask to 32-bit\n"); goto err_release_regions; } #endif len = pci_resource_len(pdev, bar_id); #if defined(__FreeBSD__) linuxkpi_pcim_want_to_use_bus_functions(pdev); #endif rtwpci->mmap = pci_iomap(pdev, bar_id, len); if (!rtwpci->mmap) { pci_release_regions(pdev); rtw_err(rtwdev, "failed to map pci memory\n"); return -ENOMEM; } return 0; #if defined(__FreeBSD__) err_release_regions: pci_release_regions(pdev); return ret; #endif } static void rtw_pci_io_unmapping(struct rtw_dev *rtwdev, struct pci_dev *pdev) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; if (rtwpci->mmap) { pci_iounmap(pdev, rtwpci->mmap); pci_release_regions(pdev); } } static void rtw_dbi_write8(struct rtw_dev *rtwdev, u16 addr, u8 data) { u16 write_addr; u16 remainder = addr & ~(BITS_DBI_WREN | BITS_DBI_ADDR_MASK); u8 flag; u8 cnt; write_addr = addr & BITS_DBI_ADDR_MASK; write_addr |= u16_encode_bits(BIT(remainder), BITS_DBI_WREN); rtw_write8(rtwdev, REG_DBI_WDATA_V1 + remainder, data); rtw_write16(rtwdev, REG_DBI_FLAG_V1, write_addr); rtw_write8(rtwdev, REG_DBI_FLAG_V1 + 2, BIT_DBI_WFLAG >> 16); for (cnt = 0; cnt < RTW_PCI_WR_RETRY_CNT; cnt++) { flag = rtw_read8(rtwdev, REG_DBI_FLAG_V1 + 2); if (flag == 0) return; udelay(10); } WARN(flag, "failed to write to DBI register, addr=0x%04x\n", addr); } static int rtw_dbi_read8(struct rtw_dev *rtwdev, u16 addr, u8 *value) { u16 read_addr = addr & BITS_DBI_ADDR_MASK; u8 flag; u8 cnt; rtw_write16(rtwdev, REG_DBI_FLAG_V1, read_addr); rtw_write8(rtwdev, REG_DBI_FLAG_V1 + 2, BIT_DBI_RFLAG >> 16); for (cnt = 0; cnt < RTW_PCI_WR_RETRY_CNT; cnt++) { flag = rtw_read8(rtwdev, REG_DBI_FLAG_V1 + 2); if (flag == 0) { read_addr = REG_DBI_RDATA_V1 + (addr & 3); *value = rtw_read8(rtwdev, read_addr); return 0; } udelay(10); } WARN(1, "failed to read DBI register, addr=0x%04x\n", addr); return -EIO; } static void rtw_mdio_write(struct rtw_dev *rtwdev, u8 addr, u16 data, bool g1) { u8 page; u8 wflag; u8 cnt; rtw_write16(rtwdev, REG_MDIO_V1, data); page = addr < RTW_PCI_MDIO_PG_SZ ? 0 : 1; page += g1 ? RTW_PCI_MDIO_PG_OFFS_G1 : RTW_PCI_MDIO_PG_OFFS_G2; rtw_write8(rtwdev, REG_PCIE_MIX_CFG, addr & BITS_MDIO_ADDR_MASK); rtw_write8(rtwdev, REG_PCIE_MIX_CFG + 3, page); rtw_write32_mask(rtwdev, REG_PCIE_MIX_CFG, BIT_MDIO_WFLAG_V1, 1); for (cnt = 0; cnt < RTW_PCI_WR_RETRY_CNT; cnt++) { wflag = rtw_read32_mask(rtwdev, REG_PCIE_MIX_CFG, BIT_MDIO_WFLAG_V1); if (wflag == 0) return; udelay(10); } WARN(wflag, "failed to write to MDIO register, addr=0x%02x\n", addr); } static void rtw_pci_clkreq_set(struct rtw_dev *rtwdev, bool enable) { u8 value; int ret; if (rtw_pci_disable_aspm) return; ret = rtw_dbi_read8(rtwdev, RTK_PCIE_LINK_CFG, &value); if (ret) { rtw_err(rtwdev, "failed to read CLKREQ_L1, ret=%d", ret); return; } if (enable) value |= BIT_CLKREQ_SW_EN; else value &= ~BIT_CLKREQ_SW_EN; rtw_dbi_write8(rtwdev, RTK_PCIE_LINK_CFG, value); } static void rtw_pci_clkreq_pad_low(struct rtw_dev *rtwdev, bool enable) { u8 value; int ret; ret = rtw_dbi_read8(rtwdev, RTK_PCIE_LINK_CFG, &value); if (ret) { rtw_err(rtwdev, "failed to read CLKREQ_L1, ret=%d", ret); return; } if (enable) value &= ~BIT_CLKREQ_N_PAD; else value |= BIT_CLKREQ_N_PAD; rtw_dbi_write8(rtwdev, RTK_PCIE_LINK_CFG, value); } static void rtw_pci_aspm_set(struct rtw_dev *rtwdev, bool enable) { u8 value; int ret; if (rtw_pci_disable_aspm) return; ret = rtw_dbi_read8(rtwdev, RTK_PCIE_LINK_CFG, &value); if (ret) { rtw_err(rtwdev, "failed to read ASPM, ret=%d", ret); return; } if (enable) value |= BIT_L1_SW_EN; else value &= ~BIT_L1_SW_EN; rtw_dbi_write8(rtwdev, RTK_PCIE_LINK_CFG, value); } static void rtw_pci_link_ps(struct rtw_dev *rtwdev, bool enter) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; /* Like CLKREQ, ASPM is also implemented by two HW modules, and can * only be enabled when host supports it. * * And ASPM mechanism should be enabled when driver/firmware enters * power save mode, without having heavy traffic. Because we've * experienced some inter-operability issues that the link tends * to enter L1 state on the fly even when driver is having high * throughput. This is probably because the ASPM behavior slightly * varies from different SOC. */ if (!(rtwpci->link_ctrl & PCI_EXP_LNKCTL_ASPM_L1)) return; if ((enter && atomic_dec_if_positive(&rtwpci->link_usage) == 0) || (!enter && atomic_inc_return(&rtwpci->link_usage) == 1)) rtw_pci_aspm_set(rtwdev, enter); } static void rtw_pci_link_cfg(struct rtw_dev *rtwdev) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; struct pci_dev *pdev = rtwpci->pdev; u16 link_ctrl; int ret; /* RTL8822CE has enabled REFCLK auto calibration, it does not need * to add clock delay to cover the REFCLK timing gap. */ if (chip->id == RTW_CHIP_TYPE_8822C) rtw_dbi_write8(rtwdev, RTK_PCIE_CLKDLY_CTRL, 0); /* Though there is standard PCIE configuration space to set the * link control register, but by Realtek's design, driver should * check if host supports CLKREQ/ASPM to enable the HW module. * * These functions are implemented by two HW modules associated, * one is responsible to access PCIE configuration space to * follow the host settings, and another is in charge of doing * CLKREQ/ASPM mechanisms, it is default disabled. Because sometimes * the host does not support it, and due to some reasons or wrong * settings (ex. CLKREQ# not Bi-Direction), it could lead to device * loss if HW misbehaves on the link. * * Hence it's designed that driver should first check the PCIE * configuration space is sync'ed and enabled, then driver can turn * on the other module that is actually working on the mechanism. */ ret = pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &link_ctrl); if (ret) { rtw_err(rtwdev, "failed to read PCI cap, ret=%d\n", ret); return; } if (link_ctrl & PCI_EXP_LNKCTL_CLKREQ_EN) rtw_pci_clkreq_set(rtwdev, true); rtwpci->link_ctrl = link_ctrl; } static void rtw_pci_interface_cfg(struct rtw_dev *rtwdev) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; switch (chip->id) { case RTW_CHIP_TYPE_8822C: if (rtwdev->hal.cut_version >= RTW_CHIP_VER_CUT_D) rtw_write32_mask(rtwdev, REG_HCI_MIX_CFG, BIT_PCIE_EMAC_PDN_AUX_TO_FAST_CLK, 1); break; default: break; } } static void rtw_pci_phy_cfg(struct rtw_dev *rtwdev) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct pci_dev *pdev = rtwpci->pdev; const struct rtw_intf_phy_para *para; u16 cut; u16 value; u16 offset; int i; int ret; cut = BIT(0) << rtwdev->hal.cut_version; for (i = 0; i < chip->intf_table->n_gen1_para; i++) { para = &chip->intf_table->gen1_para[i]; if (!(para->cut_mask & cut)) continue; if (para->offset == 0xffff) break; offset = para->offset; value = para->value; if (para->ip_sel == RTW_IP_SEL_PHY) rtw_mdio_write(rtwdev, offset, value, true); else rtw_dbi_write8(rtwdev, offset, value); } for (i = 0; i < chip->intf_table->n_gen2_para; i++) { para = &chip->intf_table->gen2_para[i]; if (!(para->cut_mask & cut)) continue; if (para->offset == 0xffff) break; offset = para->offset; value = para->value; if (para->ip_sel == RTW_IP_SEL_PHY) rtw_mdio_write(rtwdev, offset, value, false); else rtw_dbi_write8(rtwdev, offset, value); } rtw_pci_link_cfg(rtwdev); /* Disable 8821ce completion timeout by default */ if (chip->id == RTW_CHIP_TYPE_8821C) { ret = pcie_capability_set_word(pdev, PCI_EXP_DEVCTL2, PCI_EXP_DEVCTL2_COMP_TMOUT_DIS); if (ret) rtw_err(rtwdev, "failed to set PCI cap, ret = %d\n", ret); } } static int __maybe_unused rtw_pci_suspend(struct device *dev) { struct ieee80211_hw *hw = dev_get_drvdata(dev); struct rtw_dev *rtwdev = hw->priv; - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_efuse *efuse = &rtwdev->efuse; if (chip->id == RTW_CHIP_TYPE_8822C && efuse->rfe_option == 6) rtw_pci_clkreq_pad_low(rtwdev, true); return 0; } static int __maybe_unused rtw_pci_resume(struct device *dev) { struct ieee80211_hw *hw = dev_get_drvdata(dev); struct rtw_dev *rtwdev = hw->priv; - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_efuse *efuse = &rtwdev->efuse; if (chip->id == RTW_CHIP_TYPE_8822C && efuse->rfe_option == 6) rtw_pci_clkreq_pad_low(rtwdev, false); return 0; } SIMPLE_DEV_PM_OPS(rtw_pm_ops, rtw_pci_suspend, rtw_pci_resume); EXPORT_SYMBOL(rtw_pm_ops); static int rtw_pci_claim(struct rtw_dev *rtwdev, struct pci_dev *pdev) { int ret; ret = pci_enable_device(pdev); if (ret) { rtw_err(rtwdev, "failed to enable pci device\n"); return ret; } pci_set_master(pdev); pci_set_drvdata(pdev, rtwdev->hw); SET_IEEE80211_DEV(rtwdev->hw, &pdev->dev); return 0; } static void rtw_pci_declaim(struct rtw_dev *rtwdev, struct pci_dev *pdev) { - pci_clear_master(pdev); pci_disable_device(pdev); } static int rtw_pci_setup_resource(struct rtw_dev *rtwdev, struct pci_dev *pdev) { struct rtw_pci *rtwpci; int ret; rtwpci = (struct rtw_pci *)rtwdev->priv; rtwpci->pdev = pdev; /* after this driver can access to hw registers */ ret = rtw_pci_io_mapping(rtwdev, pdev); if (ret) { rtw_err(rtwdev, "failed to request pci io region\n"); goto err_out; } ret = rtw_pci_init(rtwdev); if (ret) { rtw_err(rtwdev, "failed to allocate pci resources\n"); goto err_io_unmap; } return 0; err_io_unmap: rtw_pci_io_unmapping(rtwdev, pdev); err_out: return ret; } static void rtw_pci_destroy(struct rtw_dev *rtwdev, struct pci_dev *pdev) { rtw_pci_deinit(rtwdev); rtw_pci_io_unmapping(rtwdev, pdev); } static struct rtw_hci_ops rtw_pci_ops = { .tx_write = rtw_pci_tx_write, .tx_kick_off = rtw_pci_tx_kick_off, .flush_queues = rtw_pci_flush_queues, .setup = rtw_pci_setup, .start = rtw_pci_start, .stop = rtw_pci_stop, .deep_ps = rtw_pci_deep_ps, .link_ps = rtw_pci_link_ps, .interface_cfg = rtw_pci_interface_cfg, .read8 = rtw_pci_read8, .read16 = rtw_pci_read16, .read32 = rtw_pci_read32, .write8 = rtw_pci_write8, .write16 = rtw_pci_write16, .write32 = rtw_pci_write32, .write_data_rsvd_page = rtw_pci_write_data_rsvd_page, .write_data_h2c = rtw_pci_write_data_h2c, }; static int rtw_pci_request_irq(struct rtw_dev *rtwdev, struct pci_dev *pdev) { unsigned int flags = PCI_IRQ_LEGACY; int ret; if (!rtw_disable_msi) flags |= PCI_IRQ_MSI; ret = pci_alloc_irq_vectors(pdev, 1, 1, flags); if (ret < 0) { rtw_err(rtwdev, "failed to alloc PCI irq vectors\n"); return ret; } ret = devm_request_threaded_irq(rtwdev->dev, pdev->irq, rtw_pci_interrupt_handler, rtw_pci_interrupt_threadfn, IRQF_SHARED, KBUILD_MODNAME, rtwdev); if (ret) { rtw_err(rtwdev, "failed to request irq %d\n", ret); pci_free_irq_vectors(pdev); } return ret; } static void rtw_pci_free_irq(struct rtw_dev *rtwdev, struct pci_dev *pdev) { devm_free_irq(rtwdev->dev, pdev->irq, rtwdev); pci_free_irq_vectors(pdev); } static int rtw_pci_napi_poll(struct napi_struct *napi, int budget) { struct rtw_pci *rtwpci = container_of(napi, struct rtw_pci, napi); struct rtw_dev *rtwdev = container_of((void *)rtwpci, struct rtw_dev, priv); int work_done = 0; if (rtwpci->rx_no_aspm) rtw_pci_link_ps(rtwdev, false); while (work_done < budget) { u32 work_done_once; work_done_once = rtw_pci_rx_napi(rtwdev, rtwpci, RTW_RX_QUEUE_MPDU, budget - work_done); if (work_done_once == 0) break; work_done += work_done_once; } if (work_done < budget) { napi_complete_done(napi, work_done); spin_lock_bh(&rtwpci->irq_lock); if (rtwpci->running) rtw_pci_enable_interrupt(rtwdev, rtwpci, false); spin_unlock_bh(&rtwpci->irq_lock); /* When ISR happens during polling and before napi_complete * while no further data is received. Data on the dma_ring will * not be processed immediately. Check whether dma ring is * empty and perform napi_schedule accordingly. */ if (rtw_pci_get_hw_rx_ring_nr(rtwdev, rtwpci)) napi_schedule(napi); } if (rtwpci->rx_no_aspm) rtw_pci_link_ps(rtwdev, true); return work_done; } static void rtw_pci_napi_init(struct rtw_dev *rtwdev) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; init_dummy_netdev(&rtwpci->netdev); netif_napi_add(&rtwpci->netdev, &rtwpci->napi, rtw_pci_napi_poll); } static void rtw_pci_napi_deinit(struct rtw_dev *rtwdev) { struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv; rtw_pci_napi_stop(rtwdev); netif_napi_del(&rtwpci->napi); } int rtw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct pci_dev *bridge = pci_upstream_bridge(pdev); struct ieee80211_hw *hw; struct rtw_dev *rtwdev; struct rtw_pci *rtwpci; int drv_data_size; int ret; drv_data_size = sizeof(struct rtw_dev) + sizeof(struct rtw_pci); hw = ieee80211_alloc_hw(drv_data_size, &rtw_ops); if (!hw) { dev_err(&pdev->dev, "failed to allocate hw\n"); return -ENOMEM; } rtwdev = hw->priv; rtwdev->hw = hw; rtwdev->dev = &pdev->dev; rtwdev->chip = (struct rtw_chip_info *)id->driver_data; rtwdev->hci.ops = &rtw_pci_ops; rtwdev->hci.type = RTW_HCI_TYPE_PCIE; rtwpci = (struct rtw_pci *)rtwdev->priv; atomic_set(&rtwpci->link_usage, 1); ret = rtw_core_init(rtwdev); if (ret) goto err_release_hw; rtw_dbg(rtwdev, RTW_DBG_PCI, "rtw88 pci probe: vendor=0x%4.04X device=0x%4.04X rev=%d\n", pdev->vendor, pdev->device, pdev->revision); ret = rtw_pci_claim(rtwdev, pdev); if (ret) { rtw_err(rtwdev, "failed to claim pci device\n"); goto err_deinit_core; } ret = rtw_pci_setup_resource(rtwdev, pdev); if (ret) { rtw_err(rtwdev, "failed to setup pci resources\n"); goto err_pci_declaim; } rtw_pci_napi_init(rtwdev); ret = rtw_chip_info_setup(rtwdev); if (ret) { rtw_err(rtwdev, "failed to setup chip information\n"); goto err_destroy_pci; } /* Disable PCIe ASPM L1 while doing NAPI poll for 8821CE */ if (rtwdev->chip->id == RTW_CHIP_TYPE_8821C && bridge->vendor == PCI_VENDOR_ID_INTEL) rtwpci->rx_no_aspm = true; rtw_pci_phy_cfg(rtwdev); ret = rtw_register_hw(rtwdev, hw); if (ret) { rtw_err(rtwdev, "failed to register hw\n"); goto err_destroy_pci; } ret = rtw_pci_request_irq(rtwdev, pdev); if (ret) { ieee80211_unregister_hw(hw); goto err_destroy_pci; } return 0; err_destroy_pci: rtw_pci_napi_deinit(rtwdev); rtw_pci_destroy(rtwdev, pdev); err_pci_declaim: rtw_pci_declaim(rtwdev, pdev); err_deinit_core: rtw_core_deinit(rtwdev); err_release_hw: ieee80211_free_hw(hw); return ret; } EXPORT_SYMBOL(rtw_pci_probe); void rtw_pci_remove(struct pci_dev *pdev) { struct ieee80211_hw *hw = pci_get_drvdata(pdev); struct rtw_dev *rtwdev; struct rtw_pci *rtwpci; if (!hw) return; rtwdev = hw->priv; rtwpci = (struct rtw_pci *)rtwdev->priv; rtw_unregister_hw(rtwdev, hw); rtw_pci_disable_interrupt(rtwdev, rtwpci); rtw_pci_napi_deinit(rtwdev); rtw_pci_destroy(rtwdev, pdev); rtw_pci_declaim(rtwdev, pdev); rtw_pci_free_irq(rtwdev, pdev); rtw_core_deinit(rtwdev); ieee80211_free_hw(hw); } EXPORT_SYMBOL(rtw_pci_remove); void rtw_pci_shutdown(struct pci_dev *pdev) { struct ieee80211_hw *hw = pci_get_drvdata(pdev); struct rtw_dev *rtwdev; - struct rtw_chip_info *chip; + const struct rtw_chip_info *chip; if (!hw) return; rtwdev = hw->priv; chip = rtwdev->chip; if (chip->ops->shutdown) chip->ops->shutdown(rtwdev); pci_set_power_state(pdev, PCI_D3hot); } EXPORT_SYMBOL(rtw_pci_shutdown); MODULE_AUTHOR("Realtek Corporation"); MODULE_DESCRIPTION("Realtek 802.11ac wireless PCI driver"); MODULE_LICENSE("Dual BSD/GPL"); #if defined(__FreeBSD__) MODULE_VERSION(rtw_pci, 1); MODULE_DEPEND(rtw_pci, linuxkpi, 1, 1, 1); MODULE_DEPEND(rtw_pci, linuxkpi_wlan, 1, 1, 1); #ifdef CONFIG_RTW88_DEBUGFS MODULE_DEPEND(rtw_pci, lindebugfs, 1, 1, 1); #endif #endif diff --git a/sys/contrib/dev/rtw88/phy.c b/sys/contrib/dev/rtw88/phy.c index 8982e0c98dac..128e75a81bf3 100644 --- a/sys/contrib/dev/rtw88/phy.c +++ b/sys/contrib/dev/rtw88/phy.c @@ -1,2548 +1,2543 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* Copyright(c) 2018-2019 Realtek Corporation */ #include #include "main.h" #include "reg.h" #include "fw.h" #include "phy.h" #include "debug.h" #include "regd.h" #include "sar.h" struct phy_cfg_pair { u32 addr; u32 data; }; union phy_table_tile { struct rtw_phy_cond cond; struct phy_cfg_pair cfg; }; static const u32 db_invert_table[12][8] = { {10, 13, 16, 20, 25, 32, 40, 50}, {64, 80, 101, 128, 160, 201, 256, 318}, {401, 505, 635, 800, 1007, 1268, 1596, 2010}, {316, 398, 501, 631, 794, 1000, 1259, 1585}, {1995, 2512, 3162, 3981, 5012, 6310, 7943, 10000}, {12589, 15849, 19953, 25119, 31623, 39811, 50119, 63098}, {79433, 100000, 125893, 158489, 199526, 251189, 316228, 398107}, {501187, 630957, 794328, 1000000, 1258925, 1584893, 1995262, 2511886}, {3162278, 3981072, 5011872, 6309573, 7943282, 1000000, 12589254, 15848932}, {19952623, 25118864, 31622777, 39810717, 50118723, 63095734, 79432823, 100000000}, {125892541, 158489319, 199526232, 251188643, 316227766, 398107171, 501187234, 630957345}, {794328235, 1000000000, 1258925412, 1584893192, 1995262315, 2511886432U, 3162277660U, 3981071706U} }; u8 rtw_cck_rates[] = { DESC_RATE1M, DESC_RATE2M, DESC_RATE5_5M, DESC_RATE11M }; u8 rtw_ofdm_rates[] = { DESC_RATE6M, DESC_RATE9M, DESC_RATE12M, DESC_RATE18M, DESC_RATE24M, DESC_RATE36M, DESC_RATE48M, DESC_RATE54M }; u8 rtw_ht_1s_rates[] = { DESC_RATEMCS0, DESC_RATEMCS1, DESC_RATEMCS2, DESC_RATEMCS3, DESC_RATEMCS4, DESC_RATEMCS5, DESC_RATEMCS6, DESC_RATEMCS7 }; u8 rtw_ht_2s_rates[] = { DESC_RATEMCS8, DESC_RATEMCS9, DESC_RATEMCS10, DESC_RATEMCS11, DESC_RATEMCS12, DESC_RATEMCS13, DESC_RATEMCS14, DESC_RATEMCS15 }; u8 rtw_vht_1s_rates[] = { DESC_RATEVHT1SS_MCS0, DESC_RATEVHT1SS_MCS1, DESC_RATEVHT1SS_MCS2, DESC_RATEVHT1SS_MCS3, DESC_RATEVHT1SS_MCS4, DESC_RATEVHT1SS_MCS5, DESC_RATEVHT1SS_MCS6, DESC_RATEVHT1SS_MCS7, DESC_RATEVHT1SS_MCS8, DESC_RATEVHT1SS_MCS9 }; u8 rtw_vht_2s_rates[] = { DESC_RATEVHT2SS_MCS0, DESC_RATEVHT2SS_MCS1, DESC_RATEVHT2SS_MCS2, DESC_RATEVHT2SS_MCS3, DESC_RATEVHT2SS_MCS4, DESC_RATEVHT2SS_MCS5, DESC_RATEVHT2SS_MCS6, DESC_RATEVHT2SS_MCS7, DESC_RATEVHT2SS_MCS8, DESC_RATEVHT2SS_MCS9 }; u8 *rtw_rate_section[RTW_RATE_SECTION_MAX] = { rtw_cck_rates, rtw_ofdm_rates, rtw_ht_1s_rates, rtw_ht_2s_rates, rtw_vht_1s_rates, rtw_vht_2s_rates }; EXPORT_SYMBOL(rtw_rate_section); u8 rtw_rate_size[RTW_RATE_SECTION_MAX] = { ARRAY_SIZE(rtw_cck_rates), ARRAY_SIZE(rtw_ofdm_rates), ARRAY_SIZE(rtw_ht_1s_rates), ARRAY_SIZE(rtw_ht_2s_rates), ARRAY_SIZE(rtw_vht_1s_rates), ARRAY_SIZE(rtw_vht_2s_rates) }; EXPORT_SYMBOL(rtw_rate_size); static const u8 rtw_cck_size = ARRAY_SIZE(rtw_cck_rates); static const u8 rtw_ofdm_size = ARRAY_SIZE(rtw_ofdm_rates); static const u8 rtw_ht_1s_size = ARRAY_SIZE(rtw_ht_1s_rates); static const u8 rtw_ht_2s_size = ARRAY_SIZE(rtw_ht_2s_rates); static const u8 rtw_vht_1s_size = ARRAY_SIZE(rtw_vht_1s_rates); static const u8 rtw_vht_2s_size = ARRAY_SIZE(rtw_vht_2s_rates); enum rtw_phy_band_type { PHY_BAND_2G = 0, PHY_BAND_5G = 1, }; static void rtw_phy_cck_pd_init(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 i, j; for (i = 0; i <= RTW_CHANNEL_WIDTH_40; i++) { for (j = 0; j < RTW_RF_PATH_MAX; j++) dm_info->cck_pd_lv[i][j] = CCK_PD_LV0; } dm_info->cck_fa_avg = CCK_FA_AVG_RESET; } void rtw_phy_set_edcca_th(struct rtw_dev *rtwdev, u8 l2h, u8 h2l) { struct rtw_hw_reg_offset *edcca_th = rtwdev->chip->edcca_th; rtw_write32_mask(rtwdev, edcca_th[EDCCA_TH_L2H_IDX].hw_reg.addr, edcca_th[EDCCA_TH_L2H_IDX].hw_reg.mask, l2h + edcca_th[EDCCA_TH_L2H_IDX].offset); rtw_write32_mask(rtwdev, edcca_th[EDCCA_TH_H2L_IDX].hw_reg.addr, edcca_th[EDCCA_TH_H2L_IDX].hw_reg.mask, h2l + edcca_th[EDCCA_TH_H2L_IDX].offset); } EXPORT_SYMBOL(rtw_phy_set_edcca_th); void rtw_phy_adaptivity_set_mode(struct rtw_dev *rtwdev) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_dm_info *dm_info = &rtwdev->dm_info; /* turn off in debugfs for debug usage */ if (!rtw_edcca_enabled) { dm_info->edcca_mode = RTW_EDCCA_NORMAL; rtw_dbg(rtwdev, RTW_DBG_PHY, "EDCCA disabled, cannot be set\n"); return; } switch (rtwdev->regd.dfs_region) { case NL80211_DFS_ETSI: dm_info->edcca_mode = RTW_EDCCA_ADAPTIVITY; dm_info->l2h_th_ini = chip->l2h_th_ini_ad; break; case NL80211_DFS_JP: dm_info->edcca_mode = RTW_EDCCA_ADAPTIVITY; dm_info->l2h_th_ini = chip->l2h_th_ini_cs; break; default: dm_info->edcca_mode = RTW_EDCCA_NORMAL; break; } } static void rtw_phy_adaptivity_init(struct rtw_dev *rtwdev) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; rtw_phy_adaptivity_set_mode(rtwdev); if (chip->ops->adaptivity_init) chip->ops->adaptivity_init(rtwdev); } static void rtw_phy_adaptivity(struct rtw_dev *rtwdev) { if (rtwdev->chip->ops->adaptivity) rtwdev->chip->ops->adaptivity(rtwdev); } static void rtw_phy_cfo_init(struct rtw_dev *rtwdev) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; if (chip->ops->cfo_init) chip->ops->cfo_init(rtwdev); } static void rtw_phy_tx_path_div_init(struct rtw_dev *rtwdev) { struct rtw_path_div *path_div = &rtwdev->dm_path_div; path_div->current_tx_path = rtwdev->chip->default_1ss_tx_path; path_div->path_a_cnt = 0; path_div->path_a_sum = 0; path_div->path_b_cnt = 0; path_div->path_b_sum = 0; } void rtw_phy_init(struct rtw_dev *rtwdev) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_dm_info *dm_info = &rtwdev->dm_info; u32 addr, mask; dm_info->fa_history[3] = 0; dm_info->fa_history[2] = 0; dm_info->fa_history[1] = 0; dm_info->fa_history[0] = 0; dm_info->igi_bitmap = 0; dm_info->igi_history[3] = 0; dm_info->igi_history[2] = 0; dm_info->igi_history[1] = 0; addr = chip->dig[0].addr; mask = chip->dig[0].mask; dm_info->igi_history[0] = rtw_read32_mask(rtwdev, addr, mask); rtw_phy_cck_pd_init(rtwdev); dm_info->iqk.done = false; rtw_phy_adaptivity_init(rtwdev); rtw_phy_cfo_init(rtwdev); rtw_phy_tx_path_div_init(rtwdev); } EXPORT_SYMBOL(rtw_phy_init); void rtw_phy_dig_write(struct rtw_dev *rtwdev, u8 igi) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_hal *hal = &rtwdev->hal; u32 addr, mask; u8 path; if (chip->dig_cck) { const struct rtw_hw_reg *dig_cck = &chip->dig_cck[0]; rtw_write32_mask(rtwdev, dig_cck->addr, dig_cck->mask, igi >> 1); } for (path = 0; path < hal->rf_path_num; path++) { addr = chip->dig[path].addr; mask = chip->dig[path].mask; rtw_write32_mask(rtwdev, addr, mask, igi); } } static void rtw_phy_stat_false_alarm(struct rtw_dev *rtwdev) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; chip->ops->false_alarm_statistics(rtwdev); } #define RA_FLOOR_TABLE_SIZE 7 #define RA_FLOOR_UP_GAP 3 static u8 rtw_phy_get_rssi_level(u8 old_level, u8 rssi) { u8 table[RA_FLOOR_TABLE_SIZE] = {20, 34, 38, 42, 46, 50, 100}; u8 new_level = 0; int i; for (i = 0; i < RA_FLOOR_TABLE_SIZE; i++) if (i >= old_level) table[i] += RA_FLOOR_UP_GAP; for (i = 0; i < RA_FLOOR_TABLE_SIZE; i++) { if (rssi < table[i]) { new_level = i; break; } } return new_level; } struct rtw_phy_stat_iter_data { struct rtw_dev *rtwdev; u8 min_rssi; }; static void rtw_phy_stat_rssi_iter(void *data, struct ieee80211_sta *sta) { struct rtw_phy_stat_iter_data *iter_data = data; struct rtw_dev *rtwdev = iter_data->rtwdev; struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv; u8 rssi; rssi = ewma_rssi_read(&si->avg_rssi); si->rssi_level = rtw_phy_get_rssi_level(si->rssi_level, rssi); rtw_fw_send_rssi_info(rtwdev, si); iter_data->min_rssi = min_t(u8, rssi, iter_data->min_rssi); } static void rtw_phy_stat_rssi(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; struct rtw_phy_stat_iter_data data = {}; data.rtwdev = rtwdev; data.min_rssi = U8_MAX; - rtw_iterate_stas_atomic(rtwdev, rtw_phy_stat_rssi_iter, &data); + rtw_iterate_stas(rtwdev, rtw_phy_stat_rssi_iter, &data); dm_info->pre_min_rssi = dm_info->min_rssi; dm_info->min_rssi = data.min_rssi; } static void rtw_phy_stat_rate_cnt(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; dm_info->last_pkt_count = dm_info->cur_pkt_count; memset(&dm_info->cur_pkt_count, 0, sizeof(dm_info->cur_pkt_count)); } static void rtw_phy_statistics(struct rtw_dev *rtwdev) { rtw_phy_stat_rssi(rtwdev); rtw_phy_stat_false_alarm(rtwdev); rtw_phy_stat_rate_cnt(rtwdev); } #define DIG_PERF_FA_TH_LOW 250 #define DIG_PERF_FA_TH_HIGH 500 #define DIG_PERF_FA_TH_EXTRA_HIGH 750 #define DIG_PERF_MAX 0x5a #define DIG_PERF_MID 0x40 #define DIG_CVRG_FA_TH_LOW 2000 #define DIG_CVRG_FA_TH_HIGH 4000 #define DIG_CVRG_FA_TH_EXTRA_HIGH 5000 #define DIG_CVRG_MAX 0x2a #define DIG_CVRG_MID 0x26 #define DIG_CVRG_MIN 0x1c #define DIG_RSSI_GAIN_OFFSET 15 static bool rtw_phy_dig_check_damping(struct rtw_dm_info *dm_info) { u16 fa_lo = DIG_PERF_FA_TH_LOW; u16 fa_hi = DIG_PERF_FA_TH_HIGH; u16 *fa_history; u8 *igi_history; u8 damping_rssi; u8 min_rssi; u8 diff; u8 igi_bitmap; bool damping = false; min_rssi = dm_info->min_rssi; if (dm_info->damping) { damping_rssi = dm_info->damping_rssi; diff = min_rssi > damping_rssi ? min_rssi - damping_rssi : damping_rssi - min_rssi; if (diff > 3 || dm_info->damping_cnt++ > 20) { dm_info->damping = false; return false; } return true; } igi_history = dm_info->igi_history; fa_history = dm_info->fa_history; igi_bitmap = dm_info->igi_bitmap & 0xf; switch (igi_bitmap) { case 5: /* down -> up -> down -> up */ if (igi_history[0] > igi_history[1] && igi_history[2] > igi_history[3] && igi_history[0] - igi_history[1] >= 2 && igi_history[2] - igi_history[3] >= 2 && fa_history[0] > fa_hi && fa_history[1] < fa_lo && fa_history[2] > fa_hi && fa_history[3] < fa_lo) damping = true; break; case 9: /* up -> down -> down -> up */ if (igi_history[0] > igi_history[1] && igi_history[3] > igi_history[2] && igi_history[0] - igi_history[1] >= 4 && igi_history[3] - igi_history[2] >= 2 && fa_history[0] > fa_hi && fa_history[1] < fa_lo && fa_history[2] < fa_lo && fa_history[3] > fa_hi) damping = true; break; default: return false; } if (damping) { dm_info->damping = true; dm_info->damping_cnt = 0; dm_info->damping_rssi = min_rssi; } return damping; } static void rtw_phy_dig_get_boundary(struct rtw_dev *rtwdev, struct rtw_dm_info *dm_info, u8 *upper, u8 *lower, bool linked) { u8 dig_max, dig_min, dig_mid; u8 min_rssi; if (linked) { dig_max = DIG_PERF_MAX; dig_mid = DIG_PERF_MID; dig_min = rtwdev->chip->dig_min; min_rssi = max_t(u8, dm_info->min_rssi, dig_min); } else { dig_max = DIG_CVRG_MAX; dig_mid = DIG_CVRG_MID; dig_min = DIG_CVRG_MIN; min_rssi = dig_min; } /* DIG MAX should be bounded by minimum RSSI with offset +15 */ dig_max = min_t(u8, dig_max, min_rssi + DIG_RSSI_GAIN_OFFSET); *lower = clamp_t(u8, min_rssi, dig_min, dig_mid); *upper = clamp_t(u8, *lower + DIG_RSSI_GAIN_OFFSET, dig_min, dig_max); } static void rtw_phy_dig_get_threshold(struct rtw_dm_info *dm_info, u16 *fa_th, u8 *step, bool linked) { u8 min_rssi, pre_min_rssi; min_rssi = dm_info->min_rssi; pre_min_rssi = dm_info->pre_min_rssi; step[0] = 4; step[1] = 3; step[2] = 2; if (linked) { fa_th[0] = DIG_PERF_FA_TH_EXTRA_HIGH; fa_th[1] = DIG_PERF_FA_TH_HIGH; fa_th[2] = DIG_PERF_FA_TH_LOW; if (pre_min_rssi > min_rssi) { step[0] = 6; step[1] = 4; step[2] = 2; } } else { fa_th[0] = DIG_CVRG_FA_TH_EXTRA_HIGH; fa_th[1] = DIG_CVRG_FA_TH_HIGH; fa_th[2] = DIG_CVRG_FA_TH_LOW; } } static void rtw_phy_dig_recorder(struct rtw_dm_info *dm_info, u8 igi, u16 fa) { u8 *igi_history; u16 *fa_history; u8 igi_bitmap; bool up; igi_bitmap = dm_info->igi_bitmap << 1 & 0xfe; igi_history = dm_info->igi_history; fa_history = dm_info->fa_history; up = igi > igi_history[0]; igi_bitmap |= up; igi_history[3] = igi_history[2]; igi_history[2] = igi_history[1]; igi_history[1] = igi_history[0]; igi_history[0] = igi; fa_history[3] = fa_history[2]; fa_history[2] = fa_history[1]; fa_history[1] = fa_history[0]; fa_history[0] = fa; dm_info->igi_bitmap = igi_bitmap; } static void rtw_phy_dig(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 upper_bound, lower_bound; u8 pre_igi, cur_igi; u16 fa_th[3], fa_cnt; u8 level; u8 step[3]; bool linked; if (test_bit(RTW_FLAG_DIG_DISABLE, rtwdev->flags)) return; if (rtw_phy_dig_check_damping(dm_info)) return; linked = !!rtwdev->sta_cnt; fa_cnt = dm_info->total_fa_cnt; pre_igi = dm_info->igi_history[0]; rtw_phy_dig_get_threshold(dm_info, fa_th, step, linked); /* test the false alarm count from the highest threshold level first, * and increase it by corresponding step size * * note that the step size is offset by -2, compensate it afterall */ cur_igi = pre_igi; for (level = 0; level < 3; level++) { if (fa_cnt > fa_th[level]) { cur_igi += step[level]; break; } } cur_igi -= 2; /* calculate the upper/lower bound by the minimum rssi we have among * the peers connected with us, meanwhile make sure the igi value does * not beyond the hardware limitation */ rtw_phy_dig_get_boundary(rtwdev, dm_info, &upper_bound, &lower_bound, linked); cur_igi = clamp_t(u8, cur_igi, lower_bound, upper_bound); /* record current igi value and false alarm statistics for further * damping checks, and record the trend of igi values */ rtw_phy_dig_recorder(dm_info, cur_igi, fa_cnt); if (cur_igi != pre_igi) rtw_phy_dig_write(rtwdev, cur_igi); } static void rtw_phy_ra_info_update_iter(void *data, struct ieee80211_sta *sta) { struct rtw_dev *rtwdev = data; struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv; rtw_update_sta_info(rtwdev, si, false); } static void rtw_phy_ra_info_update(struct rtw_dev *rtwdev) { if (rtwdev->watch_dog_cnt & 0x3) return; - rtw_iterate_stas_atomic(rtwdev, rtw_phy_ra_info_update_iter, rtwdev); + rtw_iterate_stas(rtwdev, rtw_phy_ra_info_update_iter, rtwdev); } static u32 rtw_phy_get_rrsr_mask(struct rtw_dev *rtwdev, u8 rate_idx) { u8 rate_order; rate_order = rate_idx; if (rate_idx >= DESC_RATEVHT4SS_MCS0) rate_order -= DESC_RATEVHT4SS_MCS0; else if (rate_idx >= DESC_RATEVHT3SS_MCS0) rate_order -= DESC_RATEVHT3SS_MCS0; else if (rate_idx >= DESC_RATEVHT2SS_MCS0) rate_order -= DESC_RATEVHT2SS_MCS0; else if (rate_idx >= DESC_RATEVHT1SS_MCS0) rate_order -= DESC_RATEVHT1SS_MCS0; else if (rate_idx >= DESC_RATEMCS24) rate_order -= DESC_RATEMCS24; else if (rate_idx >= DESC_RATEMCS16) rate_order -= DESC_RATEMCS16; else if (rate_idx >= DESC_RATEMCS8) rate_order -= DESC_RATEMCS8; else if (rate_idx >= DESC_RATEMCS0) rate_order -= DESC_RATEMCS0; else if (rate_idx >= DESC_RATE6M) rate_order -= DESC_RATE6M; else rate_order -= DESC_RATE1M; if (rate_idx >= DESC_RATEMCS0 || rate_order == 0) rate_order++; return GENMASK(rate_order + RRSR_RATE_ORDER_CCK_LEN - 1, 0); } static void rtw_phy_rrsr_mask_min_iter(void *data, struct ieee80211_sta *sta) { struct rtw_dev *rtwdev = (struct rtw_dev *)data; struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv; struct rtw_dm_info *dm_info = &rtwdev->dm_info; u32 mask = 0; mask = rtw_phy_get_rrsr_mask(rtwdev, si->ra_report.desc_rate); if (mask < dm_info->rrsr_mask_min) dm_info->rrsr_mask_min = mask; } static void rtw_phy_rrsr_update(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; dm_info->rrsr_mask_min = RRSR_RATE_ORDER_MAX; - rtw_iterate_stas_atomic(rtwdev, rtw_phy_rrsr_mask_min_iter, rtwdev); + rtw_iterate_stas(rtwdev, rtw_phy_rrsr_mask_min_iter, rtwdev); rtw_write32(rtwdev, REG_RRSR, dm_info->rrsr_val_init & dm_info->rrsr_mask_min); } static void rtw_phy_dpk_track(struct rtw_dev *rtwdev) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; if (chip->ops->dpk_track) chip->ops->dpk_track(rtwdev); } struct rtw_rx_addr_match_data { struct rtw_dev *rtwdev; struct ieee80211_hdr *hdr; struct rtw_rx_pkt_stat *pkt_stat; u8 *bssid; }; static void rtw_phy_parsing_cfo_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { struct rtw_rx_addr_match_data *iter_data = data; struct rtw_dev *rtwdev = iter_data->rtwdev; struct rtw_rx_pkt_stat *pkt_stat = iter_data->pkt_stat; struct rtw_dm_info *dm_info = &rtwdev->dm_info; struct rtw_cfo_track *cfo = &dm_info->cfo_track; u8 *bssid = iter_data->bssid; u8 i; if (!ether_addr_equal(vif->bss_conf.bssid, bssid)) return; for (i = 0; i < rtwdev->hal.rf_path_num; i++) { cfo->cfo_tail[i] += pkt_stat->cfo_tail[i]; cfo->cfo_cnt[i]++; } cfo->packet_count++; } void rtw_phy_parsing_cfo(struct rtw_dev *rtwdev, struct rtw_rx_pkt_stat *pkt_stat) { struct ieee80211_hdr *hdr = pkt_stat->hdr; struct rtw_rx_addr_match_data data = {}; if (pkt_stat->crc_err || pkt_stat->icv_err || !pkt_stat->phy_status || ieee80211_is_ctl(hdr->frame_control)) return; data.rtwdev = rtwdev; data.hdr = hdr; data.pkt_stat = pkt_stat; data.bssid = get_hdr_bssid(hdr); rtw_iterate_vifs_atomic(rtwdev, rtw_phy_parsing_cfo_iter, &data); } EXPORT_SYMBOL(rtw_phy_parsing_cfo); static void rtw_phy_cfo_track(struct rtw_dev *rtwdev) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; if (chip->ops->cfo_track) chip->ops->cfo_track(rtwdev); } #define CCK_PD_FA_LV1_MIN 1000 #define CCK_PD_FA_LV0_MAX 500 static u8 rtw_phy_cck_pd_lv_unlink(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u32 cck_fa_avg = dm_info->cck_fa_avg; if (cck_fa_avg > CCK_PD_FA_LV1_MIN) return CCK_PD_LV1; if (cck_fa_avg < CCK_PD_FA_LV0_MAX) return CCK_PD_LV0; return CCK_PD_LV_MAX; } #define CCK_PD_IGI_LV4_VAL 0x38 #define CCK_PD_IGI_LV3_VAL 0x2a #define CCK_PD_IGI_LV2_VAL 0x24 #define CCK_PD_RSSI_LV4_VAL 32 #define CCK_PD_RSSI_LV3_VAL 32 #define CCK_PD_RSSI_LV2_VAL 24 static u8 rtw_phy_cck_pd_lv_link(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 igi = dm_info->igi_history[0]; u8 rssi = dm_info->min_rssi; u32 cck_fa_avg = dm_info->cck_fa_avg; if (igi > CCK_PD_IGI_LV4_VAL && rssi > CCK_PD_RSSI_LV4_VAL) return CCK_PD_LV4; if (igi > CCK_PD_IGI_LV3_VAL && rssi > CCK_PD_RSSI_LV3_VAL) return CCK_PD_LV3; if (igi > CCK_PD_IGI_LV2_VAL || rssi > CCK_PD_RSSI_LV2_VAL) return CCK_PD_LV2; if (cck_fa_avg > CCK_PD_FA_LV1_MIN) return CCK_PD_LV1; if (cck_fa_avg < CCK_PD_FA_LV0_MAX) return CCK_PD_LV0; return CCK_PD_LV_MAX; } static u8 rtw_phy_cck_pd_lv(struct rtw_dev *rtwdev) { if (!rtw_is_assoc(rtwdev)) return rtw_phy_cck_pd_lv_unlink(rtwdev); else return rtw_phy_cck_pd_lv_link(rtwdev); } static void rtw_phy_cck_pd(struct rtw_dev *rtwdev) { + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_dm_info *dm_info = &rtwdev->dm_info; - struct rtw_chip_info *chip = rtwdev->chip; u32 cck_fa = dm_info->cck_fa_cnt; u8 level; if (rtwdev->hal.current_band_type != RTW_BAND_2G) return; if (dm_info->cck_fa_avg == CCK_FA_AVG_RESET) dm_info->cck_fa_avg = cck_fa; else dm_info->cck_fa_avg = (dm_info->cck_fa_avg * 3 + cck_fa) >> 2; rtw_dbg(rtwdev, RTW_DBG_PHY, "IGI=0x%x, rssi_min=%d, cck_fa=%d\n", dm_info->igi_history[0], dm_info->min_rssi, dm_info->fa_history[0]); rtw_dbg(rtwdev, RTW_DBG_PHY, "cck_fa_avg=%d, cck_pd_default=%d\n", dm_info->cck_fa_avg, dm_info->cck_pd_default); level = rtw_phy_cck_pd_lv(rtwdev); if (level >= CCK_PD_LV_MAX) return; if (chip->ops->cck_pd_set) chip->ops->cck_pd_set(rtwdev, level); } static void rtw_phy_pwr_track(struct rtw_dev *rtwdev) { rtwdev->chip->ops->pwr_track(rtwdev); } static void rtw_phy_ra_track(struct rtw_dev *rtwdev) { rtw_fw_update_wl_phy_info(rtwdev); rtw_phy_ra_info_update(rtwdev); rtw_phy_rrsr_update(rtwdev); } void rtw_phy_dynamic_mechanism(struct rtw_dev *rtwdev) { /* for further calculation */ rtw_phy_statistics(rtwdev); rtw_phy_dig(rtwdev); rtw_phy_cck_pd(rtwdev); rtw_phy_ra_track(rtwdev); rtw_phy_tx_path_diversity(rtwdev); rtw_phy_cfo_track(rtwdev); rtw_phy_dpk_track(rtwdev); rtw_phy_pwr_track(rtwdev); if (rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_ADAPTIVITY)) rtw_fw_adaptivity(rtwdev); else rtw_phy_adaptivity(rtwdev); } #define FRAC_BITS 3 static u8 rtw_phy_power_2_db(s8 power) { if (power <= -100 || power >= 20) return 0; else if (power >= 0) return 100; else return 100 + power; } static u64 rtw_phy_db_2_linear(u8 power_db) { u8 i, j; u64 linear; if (power_db > 96) power_db = 96; else if (power_db < 1) return 1; /* 1dB ~ 96dB */ i = (power_db - 1) >> 3; j = (power_db - 1) - (i << 3); linear = db_invert_table[i][j]; linear = i > 2 ? linear << FRAC_BITS : linear; return linear; } static u8 rtw_phy_linear_2_db(u64 linear) { u8 i; u8 j; u32 dB; - if (linear >= db_invert_table[11][7]) - return 96; /* maximum 96 dB */ - for (i = 0; i < 12; i++) { - if (i <= 2 && (linear << FRAC_BITS) <= db_invert_table[i][7]) - break; - else if (i > 2 && linear <= db_invert_table[i][7]) - break; + for (j = 0; j < 8; j++) { + if (i <= 2 && (linear << FRAC_BITS) <= db_invert_table[i][j]) + goto cnt; + else if (i > 2 && linear <= db_invert_table[i][j]) + goto cnt; + } } - for (j = 0; j < 8; j++) { - if (i <= 2 && (linear << FRAC_BITS) <= db_invert_table[i][j]) - break; - else if (i > 2 && linear <= db_invert_table[i][j]) - break; - } + return 96; /* maximum 96 dB */ +cnt: if (j == 0 && i == 0) goto end; if (j == 0) { if (i != 3) { if (db_invert_table[i][0] - linear > linear - db_invert_table[i - 1][7]) { i = i - 1; j = 7; } } else { if (db_invert_table[3][0] - linear > linear - db_invert_table[2][7]) { i = 2; j = 7; } } } else { if (db_invert_table[i][j] - linear > linear - db_invert_table[i][j - 1]) { j = j - 1; } } end: dB = (i << 3) + j + 1; return dB; } u8 rtw_phy_rf_power_2_rssi(s8 *rf_power, u8 path_num) { s8 power; u8 power_db; u64 linear; u64 sum = 0; u8 path; for (path = 0; path < path_num; path++) { power = rf_power[path]; power_db = rtw_phy_power_2_db(power); linear = rtw_phy_db_2_linear(power_db); sum += linear; } sum = (sum + (1 << (FRAC_BITS - 1))) >> FRAC_BITS; switch (path_num) { case 2: sum >>= 1; break; case 3: sum = ((sum) + ((sum) << 1) + ((sum) << 3)) >> 5; break; case 4: sum >>= 2; break; default: break; } return rtw_phy_linear_2_db(sum); } EXPORT_SYMBOL(rtw_phy_rf_power_2_rssi); u32 rtw_phy_read_rf(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, u32 addr, u32 mask) { struct rtw_hal *hal = &rtwdev->hal; - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; const u32 *base_addr = chip->rf_base_addr; u32 val, direct_addr; if (rf_path >= hal->rf_phy_num) { rtw_err(rtwdev, "unsupported rf path (%d)\n", rf_path); return INV_RF_DATA; } addr &= 0xff; direct_addr = base_addr[rf_path] + (addr << 2); mask &= RFREG_MASK; val = rtw_read32_mask(rtwdev, direct_addr, mask); return val; } EXPORT_SYMBOL(rtw_phy_read_rf); u32 rtw_phy_read_rf_sipi(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, u32 addr, u32 mask) { struct rtw_hal *hal = &rtwdev->hal; - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; const struct rtw_rf_sipi_addr *rf_sipi_addr; const struct rtw_rf_sipi_addr *rf_sipi_addr_a; u32 val32; u32 en_pi; u32 r_addr; u32 shift; if (rf_path >= hal->rf_phy_num) { rtw_err(rtwdev, "unsupported rf path (%d)\n", rf_path); return INV_RF_DATA; } if (!chip->rf_sipi_read_addr) { rtw_err(rtwdev, "rf_sipi_read_addr isn't defined\n"); return INV_RF_DATA; } rf_sipi_addr = &chip->rf_sipi_read_addr[rf_path]; rf_sipi_addr_a = &chip->rf_sipi_read_addr[RF_PATH_A]; addr &= 0xff; val32 = rtw_read32(rtwdev, rf_sipi_addr->hssi_2); val32 = (val32 & ~LSSI_READ_ADDR_MASK) | (addr << 23); rtw_write32(rtwdev, rf_sipi_addr->hssi_2, val32); /* toggle read edge of path A */ val32 = rtw_read32(rtwdev, rf_sipi_addr_a->hssi_2); rtw_write32(rtwdev, rf_sipi_addr_a->hssi_2, val32 & ~LSSI_READ_EDGE_MASK); rtw_write32(rtwdev, rf_sipi_addr_a->hssi_2, val32 | LSSI_READ_EDGE_MASK); udelay(120); en_pi = rtw_read32_mask(rtwdev, rf_sipi_addr->hssi_1, BIT(8)); r_addr = en_pi ? rf_sipi_addr->lssi_read_pi : rf_sipi_addr->lssi_read; val32 = rtw_read32_mask(rtwdev, r_addr, LSSI_READ_DATA_MASK); shift = __ffs(mask); return (val32 & mask) >> shift; } EXPORT_SYMBOL(rtw_phy_read_rf_sipi); bool rtw_phy_write_rf_reg_sipi(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, u32 addr, u32 mask, u32 data) { struct rtw_hal *hal = &rtwdev->hal; - struct rtw_chip_info *chip = rtwdev->chip; - u32 *sipi_addr = chip->rf_sipi_addr; + const struct rtw_chip_info *chip = rtwdev->chip; + const u32 *sipi_addr = chip->rf_sipi_addr; u32 data_and_addr; u32 old_data = 0; u32 shift; if (rf_path >= hal->rf_phy_num) { rtw_err(rtwdev, "unsupported rf path (%d)\n", rf_path); return false; } addr &= 0xff; mask &= RFREG_MASK; if (mask != RFREG_MASK) { old_data = chip->ops->read_rf(rtwdev, rf_path, addr, RFREG_MASK); if (old_data == INV_RF_DATA) { rtw_err(rtwdev, "Write fail, rf is disabled\n"); return false; } shift = __ffs(mask); data = ((old_data) & (~mask)) | (data << shift); } data_and_addr = ((addr << 20) | (data & 0x000fffff)) & 0x0fffffff; rtw_write32(rtwdev, sipi_addr[rf_path], data_and_addr); udelay(13); return true; } EXPORT_SYMBOL(rtw_phy_write_rf_reg_sipi); bool rtw_phy_write_rf_reg(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, u32 addr, u32 mask, u32 data) { struct rtw_hal *hal = &rtwdev->hal; - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; const u32 *base_addr = chip->rf_base_addr; u32 direct_addr; if (rf_path >= hal->rf_phy_num) { rtw_err(rtwdev, "unsupported rf path (%d)\n", rf_path); return false; } addr &= 0xff; direct_addr = base_addr[rf_path] + (addr << 2); mask &= RFREG_MASK; rtw_write32_mask(rtwdev, direct_addr, mask, data); udelay(1); return true; } bool rtw_phy_write_rf_reg_mix(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, u32 addr, u32 mask, u32 data) { if (addr != 0x00) return rtw_phy_write_rf_reg(rtwdev, rf_path, addr, mask, data); return rtw_phy_write_rf_reg_sipi(rtwdev, rf_path, addr, mask, data); } EXPORT_SYMBOL(rtw_phy_write_rf_reg_mix); void rtw_phy_setup_phy_cond(struct rtw_dev *rtwdev, u32 pkg) { struct rtw_hal *hal = &rtwdev->hal; struct rtw_efuse *efuse = &rtwdev->efuse; struct rtw_phy_cond cond = {0}; cond.cut = hal->cut_version ? hal->cut_version : 15; cond.pkg = pkg ? pkg : 15; cond.plat = 0x04; cond.rfe = efuse->rfe_option; switch (rtw_hci_type(rtwdev)) { case RTW_HCI_TYPE_USB: cond.intf = INTF_USB; break; case RTW_HCI_TYPE_SDIO: cond.intf = INTF_SDIO; break; case RTW_HCI_TYPE_PCIE: default: cond.intf = INTF_PCIE; break; } hal->phy_cond = cond; rtw_dbg(rtwdev, RTW_DBG_PHY, "phy cond=0x%08x\n", *((u32 *)&hal->phy_cond)); } static bool check_positive(struct rtw_dev *rtwdev, struct rtw_phy_cond cond) { struct rtw_hal *hal = &rtwdev->hal; struct rtw_phy_cond drv_cond = hal->phy_cond; if (cond.cut && cond.cut != drv_cond.cut) return false; if (cond.pkg && cond.pkg != drv_cond.pkg) return false; if (cond.intf && cond.intf != drv_cond.intf) return false; if (cond.rfe != drv_cond.rfe) return false; return true; } void rtw_parse_tbl_phy_cond(struct rtw_dev *rtwdev, const struct rtw_table *tbl) { const union phy_table_tile *p = tbl->data; const union phy_table_tile *end = p + tbl->size / 2; struct rtw_phy_cond pos_cond = {0}; bool is_matched = true, is_skipped = false; BUILD_BUG_ON(sizeof(union phy_table_tile) != sizeof(struct phy_cfg_pair)); for (; p < end; p++) { if (p->cond.pos) { switch (p->cond.branch) { case BRANCH_ENDIF: is_matched = true; is_skipped = false; break; case BRANCH_ELSE: is_matched = is_skipped ? false : true; break; case BRANCH_IF: case BRANCH_ELIF: default: pos_cond = p->cond; break; } } else if (p->cond.neg) { if (!is_skipped) { if (check_positive(rtwdev, pos_cond)) { is_matched = true; is_skipped = true; } else { is_matched = false; is_skipped = false; } } else { is_matched = false; } } else if (is_matched) { (*tbl->do_cfg)(rtwdev, tbl, p->cfg.addr, p->cfg.data); } } } EXPORT_SYMBOL(rtw_parse_tbl_phy_cond); #define bcd_to_dec_pwr_by_rate(val, i) bcd2bin(val >> (i * 8)) static u8 tbl_to_dec_pwr_by_rate(struct rtw_dev *rtwdev, u32 hex, u8 i) { if (rtwdev->chip->is_pwr_by_rate_dec) return bcd_to_dec_pwr_by_rate(hex, i); return (hex >> (i * 8)) & 0xFF; } static void rtw_phy_get_rate_values_of_txpwr_by_rate(struct rtw_dev *rtwdev, u32 addr, u32 mask, u32 val, u8 *rate, u8 *pwr_by_rate, u8 *rate_num) { int i; switch (addr) { case 0xE00: case 0x830: rate[0] = DESC_RATE6M; rate[1] = DESC_RATE9M; rate[2] = DESC_RATE12M; rate[3] = DESC_RATE18M; for (i = 0; i < 4; ++i) pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); *rate_num = 4; break; case 0xE04: case 0x834: rate[0] = DESC_RATE24M; rate[1] = DESC_RATE36M; rate[2] = DESC_RATE48M; rate[3] = DESC_RATE54M; for (i = 0; i < 4; ++i) pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); *rate_num = 4; break; case 0xE08: rate[0] = DESC_RATE1M; pwr_by_rate[0] = bcd_to_dec_pwr_by_rate(val, 1); *rate_num = 1; break; case 0x86C: if (mask == 0xffffff00) { rate[0] = DESC_RATE2M; rate[1] = DESC_RATE5_5M; rate[2] = DESC_RATE11M; for (i = 1; i < 4; ++i) pwr_by_rate[i - 1] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); *rate_num = 3; } else if (mask == 0x000000ff) { rate[0] = DESC_RATE11M; pwr_by_rate[0] = bcd_to_dec_pwr_by_rate(val, 0); *rate_num = 1; } break; case 0xE10: case 0x83C: rate[0] = DESC_RATEMCS0; rate[1] = DESC_RATEMCS1; rate[2] = DESC_RATEMCS2; rate[3] = DESC_RATEMCS3; for (i = 0; i < 4; ++i) pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); *rate_num = 4; break; case 0xE14: case 0x848: rate[0] = DESC_RATEMCS4; rate[1] = DESC_RATEMCS5; rate[2] = DESC_RATEMCS6; rate[3] = DESC_RATEMCS7; for (i = 0; i < 4; ++i) pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); *rate_num = 4; break; case 0xE18: case 0x84C: rate[0] = DESC_RATEMCS8; rate[1] = DESC_RATEMCS9; rate[2] = DESC_RATEMCS10; rate[3] = DESC_RATEMCS11; for (i = 0; i < 4; ++i) pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); *rate_num = 4; break; case 0xE1C: case 0x868: rate[0] = DESC_RATEMCS12; rate[1] = DESC_RATEMCS13; rate[2] = DESC_RATEMCS14; rate[3] = DESC_RATEMCS15; for (i = 0; i < 4; ++i) pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); *rate_num = 4; break; case 0x838: rate[0] = DESC_RATE1M; rate[1] = DESC_RATE2M; rate[2] = DESC_RATE5_5M; for (i = 1; i < 4; ++i) pwr_by_rate[i - 1] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); *rate_num = 3; break; case 0xC20: case 0xE20: case 0x1820: case 0x1A20: rate[0] = DESC_RATE1M; rate[1] = DESC_RATE2M; rate[2] = DESC_RATE5_5M; rate[3] = DESC_RATE11M; for (i = 0; i < 4; ++i) pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); *rate_num = 4; break; case 0xC24: case 0xE24: case 0x1824: case 0x1A24: rate[0] = DESC_RATE6M; rate[1] = DESC_RATE9M; rate[2] = DESC_RATE12M; rate[3] = DESC_RATE18M; for (i = 0; i < 4; ++i) pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); *rate_num = 4; break; case 0xC28: case 0xE28: case 0x1828: case 0x1A28: rate[0] = DESC_RATE24M; rate[1] = DESC_RATE36M; rate[2] = DESC_RATE48M; rate[3] = DESC_RATE54M; for (i = 0; i < 4; ++i) pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); *rate_num = 4; break; case 0xC2C: case 0xE2C: case 0x182C: case 0x1A2C: rate[0] = DESC_RATEMCS0; rate[1] = DESC_RATEMCS1; rate[2] = DESC_RATEMCS2; rate[3] = DESC_RATEMCS3; for (i = 0; i < 4; ++i) pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); *rate_num = 4; break; case 0xC30: case 0xE30: case 0x1830: case 0x1A30: rate[0] = DESC_RATEMCS4; rate[1] = DESC_RATEMCS5; rate[2] = DESC_RATEMCS6; rate[3] = DESC_RATEMCS7; for (i = 0; i < 4; ++i) pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); *rate_num = 4; break; case 0xC34: case 0xE34: case 0x1834: case 0x1A34: rate[0] = DESC_RATEMCS8; rate[1] = DESC_RATEMCS9; rate[2] = DESC_RATEMCS10; rate[3] = DESC_RATEMCS11; for (i = 0; i < 4; ++i) pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); *rate_num = 4; break; case 0xC38: case 0xE38: case 0x1838: case 0x1A38: rate[0] = DESC_RATEMCS12; rate[1] = DESC_RATEMCS13; rate[2] = DESC_RATEMCS14; rate[3] = DESC_RATEMCS15; for (i = 0; i < 4; ++i) pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); *rate_num = 4; break; case 0xC3C: case 0xE3C: case 0x183C: case 0x1A3C: rate[0] = DESC_RATEVHT1SS_MCS0; rate[1] = DESC_RATEVHT1SS_MCS1; rate[2] = DESC_RATEVHT1SS_MCS2; rate[3] = DESC_RATEVHT1SS_MCS3; for (i = 0; i < 4; ++i) pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); *rate_num = 4; break; case 0xC40: case 0xE40: case 0x1840: case 0x1A40: rate[0] = DESC_RATEVHT1SS_MCS4; rate[1] = DESC_RATEVHT1SS_MCS5; rate[2] = DESC_RATEVHT1SS_MCS6; rate[3] = DESC_RATEVHT1SS_MCS7; for (i = 0; i < 4; ++i) pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); *rate_num = 4; break; case 0xC44: case 0xE44: case 0x1844: case 0x1A44: rate[0] = DESC_RATEVHT1SS_MCS8; rate[1] = DESC_RATEVHT1SS_MCS9; rate[2] = DESC_RATEVHT2SS_MCS0; rate[3] = DESC_RATEVHT2SS_MCS1; for (i = 0; i < 4; ++i) pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); *rate_num = 4; break; case 0xC48: case 0xE48: case 0x1848: case 0x1A48: rate[0] = DESC_RATEVHT2SS_MCS2; rate[1] = DESC_RATEVHT2SS_MCS3; rate[2] = DESC_RATEVHT2SS_MCS4; rate[3] = DESC_RATEVHT2SS_MCS5; for (i = 0; i < 4; ++i) pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); *rate_num = 4; break; case 0xC4C: case 0xE4C: case 0x184C: case 0x1A4C: rate[0] = DESC_RATEVHT2SS_MCS6; rate[1] = DESC_RATEVHT2SS_MCS7; rate[2] = DESC_RATEVHT2SS_MCS8; rate[3] = DESC_RATEVHT2SS_MCS9; for (i = 0; i < 4; ++i) pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); *rate_num = 4; break; case 0xCD8: case 0xED8: case 0x18D8: case 0x1AD8: rate[0] = DESC_RATEMCS16; rate[1] = DESC_RATEMCS17; rate[2] = DESC_RATEMCS18; rate[3] = DESC_RATEMCS19; for (i = 0; i < 4; ++i) pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); *rate_num = 4; break; case 0xCDC: case 0xEDC: case 0x18DC: case 0x1ADC: rate[0] = DESC_RATEMCS20; rate[1] = DESC_RATEMCS21; rate[2] = DESC_RATEMCS22; rate[3] = DESC_RATEMCS23; for (i = 0; i < 4; ++i) pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); *rate_num = 4; break; case 0xCE0: case 0xEE0: case 0x18E0: case 0x1AE0: rate[0] = DESC_RATEVHT3SS_MCS0; rate[1] = DESC_RATEVHT3SS_MCS1; rate[2] = DESC_RATEVHT3SS_MCS2; rate[3] = DESC_RATEVHT3SS_MCS3; for (i = 0; i < 4; ++i) pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); *rate_num = 4; break; case 0xCE4: case 0xEE4: case 0x18E4: case 0x1AE4: rate[0] = DESC_RATEVHT3SS_MCS4; rate[1] = DESC_RATEVHT3SS_MCS5; rate[2] = DESC_RATEVHT3SS_MCS6; rate[3] = DESC_RATEVHT3SS_MCS7; for (i = 0; i < 4; ++i) pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); *rate_num = 4; break; case 0xCE8: case 0xEE8: case 0x18E8: case 0x1AE8: rate[0] = DESC_RATEVHT3SS_MCS8; rate[1] = DESC_RATEVHT3SS_MCS9; for (i = 0; i < 2; ++i) pwr_by_rate[i] = tbl_to_dec_pwr_by_rate(rtwdev, val, i); *rate_num = 2; break; default: rtw_warn(rtwdev, "invalid tx power index addr 0x%08x\n", addr); break; } } static void rtw_phy_store_tx_power_by_rate(struct rtw_dev *rtwdev, u32 band, u32 rfpath, u32 txnum, u32 regaddr, u32 bitmask, u32 data) { struct rtw_hal *hal = &rtwdev->hal; u8 rate_num = 0; u8 rate; u8 rates[RTW_RF_PATH_MAX] = {0}; s8 offset; s8 pwr_by_rate[RTW_RF_PATH_MAX] = {0}; int i; rtw_phy_get_rate_values_of_txpwr_by_rate(rtwdev, regaddr, bitmask, data, rates, pwr_by_rate, &rate_num); if (WARN_ON(rfpath >= RTW_RF_PATH_MAX || (band != PHY_BAND_2G && band != PHY_BAND_5G) || rate_num > RTW_RF_PATH_MAX)) return; for (i = 0; i < rate_num; i++) { offset = pwr_by_rate[i]; rate = rates[i]; if (band == PHY_BAND_2G) hal->tx_pwr_by_rate_offset_2g[rfpath][rate] = offset; else if (band == PHY_BAND_5G) hal->tx_pwr_by_rate_offset_5g[rfpath][rate] = offset; else continue; } } void rtw_parse_tbl_bb_pg(struct rtw_dev *rtwdev, const struct rtw_table *tbl) { const struct rtw_phy_pg_cfg_pair *p = tbl->data; const struct rtw_phy_pg_cfg_pair *end = p + tbl->size; for (; p < end; p++) { if (p->addr == 0xfe || p->addr == 0xffe) { msleep(50); continue; } rtw_phy_store_tx_power_by_rate(rtwdev, p->band, p->rf_path, p->tx_num, p->addr, p->bitmask, p->data); } } EXPORT_SYMBOL(rtw_parse_tbl_bb_pg); static const u8 rtw_channel_idx_5g[RTW_MAX_CHANNEL_NUM_5G] = { 36, 38, 40, 42, 44, 46, 48, /* Band 1 */ 52, 54, 56, 58, 60, 62, 64, /* Band 2 */ 100, 102, 104, 106, 108, 110, 112, /* Band 3 */ 116, 118, 120, 122, 124, 126, 128, /* Band 3 */ 132, 134, 136, 138, 140, 142, 144, /* Band 3 */ 149, 151, 153, 155, 157, 159, 161, /* Band 4 */ 165, 167, 169, 171, 173, 175, 177}; /* Band 4 */ static int rtw_channel_to_idx(u8 band, u8 channel) { int ch_idx; u8 n_channel; if (band == PHY_BAND_2G) { ch_idx = channel - 1; n_channel = RTW_MAX_CHANNEL_NUM_2G; } else if (band == PHY_BAND_5G) { n_channel = RTW_MAX_CHANNEL_NUM_5G; for (ch_idx = 0; ch_idx < n_channel; ch_idx++) if (rtw_channel_idx_5g[ch_idx] == channel) break; } else { return -1; } if (ch_idx >= n_channel) return -1; return ch_idx; } static void rtw_phy_set_tx_power_limit(struct rtw_dev *rtwdev, u8 regd, u8 band, u8 bw, u8 rs, u8 ch, s8 pwr_limit) { struct rtw_hal *hal = &rtwdev->hal; u8 max_power_index = rtwdev->chip->max_power_index; s8 ww; int ch_idx; pwr_limit = clamp_t(s8, pwr_limit, -max_power_index, max_power_index); ch_idx = rtw_channel_to_idx(band, ch); if (regd >= RTW_REGD_MAX || bw >= RTW_CHANNEL_WIDTH_MAX || rs >= RTW_RATE_SECTION_MAX || ch_idx < 0) { WARN(1, "wrong txpwr_lmt regd=%u, band=%u bw=%u, rs=%u, ch_idx=%u, pwr_limit=%d\n", regd, band, bw, rs, ch_idx, pwr_limit); return; } if (band == PHY_BAND_2G) { hal->tx_pwr_limit_2g[regd][bw][rs][ch_idx] = pwr_limit; ww = hal->tx_pwr_limit_2g[RTW_REGD_WW][bw][rs][ch_idx]; ww = min_t(s8, ww, pwr_limit); hal->tx_pwr_limit_2g[RTW_REGD_WW][bw][rs][ch_idx] = ww; } else if (band == PHY_BAND_5G) { hal->tx_pwr_limit_5g[regd][bw][rs][ch_idx] = pwr_limit; ww = hal->tx_pwr_limit_5g[RTW_REGD_WW][bw][rs][ch_idx]; ww = min_t(s8, ww, pwr_limit); hal->tx_pwr_limit_5g[RTW_REGD_WW][bw][rs][ch_idx] = ww; } } /* cross-reference 5G power limits if values are not assigned */ static void rtw_xref_5g_txpwr_lmt(struct rtw_dev *rtwdev, u8 regd, u8 bw, u8 ch_idx, u8 rs_ht, u8 rs_vht) { struct rtw_hal *hal = &rtwdev->hal; u8 max_power_index = rtwdev->chip->max_power_index; s8 lmt_ht = hal->tx_pwr_limit_5g[regd][bw][rs_ht][ch_idx]; s8 lmt_vht = hal->tx_pwr_limit_5g[regd][bw][rs_vht][ch_idx]; if (lmt_ht == lmt_vht) return; if (lmt_ht == max_power_index) hal->tx_pwr_limit_5g[regd][bw][rs_ht][ch_idx] = lmt_vht; else if (lmt_vht == max_power_index) hal->tx_pwr_limit_5g[regd][bw][rs_vht][ch_idx] = lmt_ht; } /* cross-reference power limits for ht and vht */ static void rtw_xref_txpwr_lmt_by_rs(struct rtw_dev *rtwdev, u8 regd, u8 bw, u8 ch_idx) { u8 rs_idx, rs_ht, rs_vht; u8 rs_cmp[2][2] = {{RTW_RATE_SECTION_HT_1S, RTW_RATE_SECTION_VHT_1S}, {RTW_RATE_SECTION_HT_2S, RTW_RATE_SECTION_VHT_2S} }; for (rs_idx = 0; rs_idx < 2; rs_idx++) { rs_ht = rs_cmp[rs_idx][0]; rs_vht = rs_cmp[rs_idx][1]; rtw_xref_5g_txpwr_lmt(rtwdev, regd, bw, ch_idx, rs_ht, rs_vht); } } /* cross-reference power limits for 5G channels */ static void rtw_xref_5g_txpwr_lmt_by_ch(struct rtw_dev *rtwdev, u8 regd, u8 bw) { u8 ch_idx; for (ch_idx = 0; ch_idx < RTW_MAX_CHANNEL_NUM_5G; ch_idx++) rtw_xref_txpwr_lmt_by_rs(rtwdev, regd, bw, ch_idx); } /* cross-reference power limits for 20/40M bandwidth */ static void rtw_xref_txpwr_lmt_by_bw(struct rtw_dev *rtwdev, u8 regd) { u8 bw; for (bw = RTW_CHANNEL_WIDTH_20; bw <= RTW_CHANNEL_WIDTH_40; bw++) rtw_xref_5g_txpwr_lmt_by_ch(rtwdev, regd, bw); } /* cross-reference power limits */ static void rtw_xref_txpwr_lmt(struct rtw_dev *rtwdev) { u8 regd; for (regd = 0; regd < RTW_REGD_MAX; regd++) rtw_xref_txpwr_lmt_by_bw(rtwdev, regd); } static void __cfg_txpwr_lmt_by_alt(struct rtw_hal *hal, u8 regd, u8 regd_alt, u8 bw, u8 rs) { u8 ch; for (ch = 0; ch < RTW_MAX_CHANNEL_NUM_2G; ch++) hal->tx_pwr_limit_2g[regd][bw][rs][ch] = hal->tx_pwr_limit_2g[regd_alt][bw][rs][ch]; for (ch = 0; ch < RTW_MAX_CHANNEL_NUM_5G; ch++) hal->tx_pwr_limit_5g[regd][bw][rs][ch] = hal->tx_pwr_limit_5g[regd_alt][bw][rs][ch]; } static void rtw_cfg_txpwr_lmt_by_alt(struct rtw_dev *rtwdev, u8 regd, u8 regd_alt) { u8 bw, rs; for (bw = 0; bw < RTW_CHANNEL_WIDTH_MAX; bw++) for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++) __cfg_txpwr_lmt_by_alt(&rtwdev->hal, regd, regd_alt, bw, rs); } void rtw_parse_tbl_txpwr_lmt(struct rtw_dev *rtwdev, const struct rtw_table *tbl) { const struct rtw_txpwr_lmt_cfg_pair *p = tbl->data; const struct rtw_txpwr_lmt_cfg_pair *end = p + tbl->size; u32 regd_cfg_flag = 0; u8 regd_alt; u8 i; for (; p < end; p++) { regd_cfg_flag |= BIT(p->regd); rtw_phy_set_tx_power_limit(rtwdev, p->regd, p->band, p->bw, p->rs, p->ch, p->txpwr_lmt); } for (i = 0; i < RTW_REGD_MAX; i++) { if (i == RTW_REGD_WW) continue; if (regd_cfg_flag & BIT(i)) continue; rtw_dbg(rtwdev, RTW_DBG_REGD, "txpwr regd %d does not be configured\n", i); if (rtw_regd_has_alt(i, ®d_alt) && regd_cfg_flag & BIT(regd_alt)) { rtw_dbg(rtwdev, RTW_DBG_REGD, "cfg txpwr regd %d by regd %d as alternative\n", i, regd_alt); rtw_cfg_txpwr_lmt_by_alt(rtwdev, i, regd_alt); continue; } rtw_dbg(rtwdev, RTW_DBG_REGD, "cfg txpwr regd %d by WW\n", i); rtw_cfg_txpwr_lmt_by_alt(rtwdev, i, RTW_REGD_WW); } rtw_xref_txpwr_lmt(rtwdev); } EXPORT_SYMBOL(rtw_parse_tbl_txpwr_lmt); void rtw_phy_cfg_mac(struct rtw_dev *rtwdev, const struct rtw_table *tbl, u32 addr, u32 data) { rtw_write8(rtwdev, addr, data); } EXPORT_SYMBOL(rtw_phy_cfg_mac); void rtw_phy_cfg_agc(struct rtw_dev *rtwdev, const struct rtw_table *tbl, u32 addr, u32 data) { rtw_write32(rtwdev, addr, data); } EXPORT_SYMBOL(rtw_phy_cfg_agc); void rtw_phy_cfg_bb(struct rtw_dev *rtwdev, const struct rtw_table *tbl, u32 addr, u32 data) { if (addr == 0xfe) msleep(50); else if (addr == 0xfd) mdelay(5); else if (addr == 0xfc) mdelay(1); else if (addr == 0xfb) usleep_range(50, 60); else if (addr == 0xfa) udelay(5); else if (addr == 0xf9) udelay(1); else rtw_write32(rtwdev, addr, data); } EXPORT_SYMBOL(rtw_phy_cfg_bb); void rtw_phy_cfg_rf(struct rtw_dev *rtwdev, const struct rtw_table *tbl, u32 addr, u32 data) { if (addr == 0xffe) { msleep(50); } else if (addr == 0xfe) { usleep_range(100, 110); } else { rtw_write_rf(rtwdev, tbl->rf_path, addr, RFREG_MASK, data); udelay(1); } } EXPORT_SYMBOL(rtw_phy_cfg_rf); static void rtw_load_rfk_table(struct rtw_dev *rtwdev) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_dpk_info *dpk_info = &rtwdev->dm_info.dpk_info; if (!chip->rfk_init_tbl) return; rtw_write32_mask(rtwdev, 0x1e24, BIT(17), 0x1); rtw_write32_mask(rtwdev, 0x1cd0, BIT(28), 0x1); rtw_write32_mask(rtwdev, 0x1cd0, BIT(29), 0x1); rtw_write32_mask(rtwdev, 0x1cd0, BIT(30), 0x1); rtw_write32_mask(rtwdev, 0x1cd0, BIT(31), 0x0); rtw_load_table(rtwdev, chip->rfk_init_tbl); dpk_info->is_dpk_pwr_on = true; } void rtw_phy_load_tables(struct rtw_dev *rtwdev) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; u8 rf_path; rtw_load_table(rtwdev, chip->mac_tbl); rtw_load_table(rtwdev, chip->bb_tbl); rtw_load_table(rtwdev, chip->agc_tbl); rtw_load_rfk_table(rtwdev); for (rf_path = 0; rf_path < rtwdev->hal.rf_path_num; rf_path++) { const struct rtw_table *tbl; tbl = chip->rf_tbl[rf_path]; rtw_load_table(rtwdev, tbl); } } EXPORT_SYMBOL(rtw_phy_load_tables); static u8 rtw_get_channel_group(u8 channel, u8 rate) { switch (channel) { default: WARN_ON(1); fallthrough; case 1: case 2: case 36: case 38: case 40: case 42: return 0; case 3: case 4: case 5: case 44: case 46: case 48: case 50: return 1; case 6: case 7: case 8: case 52: case 54: case 56: case 58: return 2; case 9: case 10: case 11: case 60: case 62: case 64: return 3; case 12: case 13: case 100: case 102: case 104: case 106: return 4; case 14: return rate <= DESC_RATE11M ? 5 : 4; case 108: case 110: case 112: case 114: return 5; case 116: case 118: case 120: case 122: return 6; case 124: case 126: case 128: case 130: return 7; case 132: case 134: case 136: case 138: return 8; case 140: case 142: case 144: return 9; case 149: case 151: case 153: case 155: return 10; case 157: case 159: case 161: return 11; case 165: case 167: case 169: case 171: return 12; case 173: case 175: case 177: return 13; } } static s8 rtw_phy_get_dis_dpd_by_rate_diff(struct rtw_dev *rtwdev, u16 rate) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; s8 dpd_diff = 0; if (!chip->en_dis_dpd) return 0; #define RTW_DPD_RATE_CHECK(_rate) \ case DESC_RATE ## _rate: \ if (DIS_DPD_RATE ## _rate & chip->dpd_ratemask) \ dpd_diff = -6 * chip->txgi_factor; \ break switch (rate) { RTW_DPD_RATE_CHECK(6M); RTW_DPD_RATE_CHECK(9M); RTW_DPD_RATE_CHECK(MCS0); RTW_DPD_RATE_CHECK(MCS1); RTW_DPD_RATE_CHECK(MCS8); RTW_DPD_RATE_CHECK(MCS9); RTW_DPD_RATE_CHECK(VHT1SS_MCS0); RTW_DPD_RATE_CHECK(VHT1SS_MCS1); RTW_DPD_RATE_CHECK(VHT2SS_MCS0); RTW_DPD_RATE_CHECK(VHT2SS_MCS1); } #undef RTW_DPD_RATE_CHECK return dpd_diff; } static u8 rtw_phy_get_2g_tx_power_index(struct rtw_dev *rtwdev, struct rtw_2g_txpwr_idx *pwr_idx_2g, enum rtw_bandwidth bandwidth, u8 rate, u8 group) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; u8 tx_power; bool mcs_rate; bool above_2ss; u8 factor = chip->txgi_factor; if (rate <= DESC_RATE11M) tx_power = pwr_idx_2g->cck_base[group]; else tx_power = pwr_idx_2g->bw40_base[group]; if (rate >= DESC_RATE6M && rate <= DESC_RATE54M) tx_power += pwr_idx_2g->ht_1s_diff.ofdm * factor; mcs_rate = (rate >= DESC_RATEMCS0 && rate <= DESC_RATEMCS15) || (rate >= DESC_RATEVHT1SS_MCS0 && rate <= DESC_RATEVHT2SS_MCS9); above_2ss = (rate >= DESC_RATEMCS8 && rate <= DESC_RATEMCS15) || (rate >= DESC_RATEVHT2SS_MCS0); if (!mcs_rate) return tx_power; switch (bandwidth) { default: WARN_ON(1); fallthrough; case RTW_CHANNEL_WIDTH_20: tx_power += pwr_idx_2g->ht_1s_diff.bw20 * factor; if (above_2ss) tx_power += pwr_idx_2g->ht_2s_diff.bw20 * factor; break; case RTW_CHANNEL_WIDTH_40: /* bw40 is the base power */ if (above_2ss) tx_power += pwr_idx_2g->ht_2s_diff.bw40 * factor; break; } return tx_power; } static u8 rtw_phy_get_5g_tx_power_index(struct rtw_dev *rtwdev, struct rtw_5g_txpwr_idx *pwr_idx_5g, enum rtw_bandwidth bandwidth, u8 rate, u8 group) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; u8 tx_power; u8 upper, lower; bool mcs_rate; bool above_2ss; u8 factor = chip->txgi_factor; tx_power = pwr_idx_5g->bw40_base[group]; mcs_rate = (rate >= DESC_RATEMCS0 && rate <= DESC_RATEMCS15) || (rate >= DESC_RATEVHT1SS_MCS0 && rate <= DESC_RATEVHT2SS_MCS9); above_2ss = (rate >= DESC_RATEMCS8 && rate <= DESC_RATEMCS15) || (rate >= DESC_RATEVHT2SS_MCS0); if (!mcs_rate) { tx_power += pwr_idx_5g->ht_1s_diff.ofdm * factor; return tx_power; } switch (bandwidth) { default: WARN_ON(1); fallthrough; case RTW_CHANNEL_WIDTH_20: tx_power += pwr_idx_5g->ht_1s_diff.bw20 * factor; if (above_2ss) tx_power += pwr_idx_5g->ht_2s_diff.bw20 * factor; break; case RTW_CHANNEL_WIDTH_40: /* bw40 is the base power */ if (above_2ss) tx_power += pwr_idx_5g->ht_2s_diff.bw40 * factor; break; case RTW_CHANNEL_WIDTH_80: /* the base idx of bw80 is the average of bw40+/bw40- */ lower = pwr_idx_5g->bw40_base[group]; upper = pwr_idx_5g->bw40_base[group + 1]; tx_power = (lower + upper) / 2; tx_power += pwr_idx_5g->vht_1s_diff.bw80 * factor; if (above_2ss) tx_power += pwr_idx_5g->vht_2s_diff.bw80 * factor; break; } return tx_power; } /* return RTW_RATE_SECTION_MAX to indicate rate is invalid */ static u8 rtw_phy_rate_to_rate_section(u8 rate) { if (rate >= DESC_RATE1M && rate <= DESC_RATE11M) return RTW_RATE_SECTION_CCK; else if (rate >= DESC_RATE6M && rate <= DESC_RATE54M) return RTW_RATE_SECTION_OFDM; else if (rate >= DESC_RATEMCS0 && rate <= DESC_RATEMCS7) return RTW_RATE_SECTION_HT_1S; else if (rate >= DESC_RATEMCS8 && rate <= DESC_RATEMCS15) return RTW_RATE_SECTION_HT_2S; else if (rate >= DESC_RATEVHT1SS_MCS0 && rate <= DESC_RATEVHT1SS_MCS9) return RTW_RATE_SECTION_VHT_1S; else if (rate >= DESC_RATEVHT2SS_MCS0 && rate <= DESC_RATEVHT2SS_MCS9) return RTW_RATE_SECTION_VHT_2S; else return RTW_RATE_SECTION_MAX; } static s8 rtw_phy_get_tx_power_limit(struct rtw_dev *rtwdev, u8 band, enum rtw_bandwidth bw, u8 rf_path, u8 rate, u8 channel, u8 regd) { struct rtw_hal *hal = &rtwdev->hal; u8 *cch_by_bw = hal->cch_by_bw; s8 power_limit = (s8)rtwdev->chip->max_power_index; u8 rs = rtw_phy_rate_to_rate_section(rate); int ch_idx; u8 cur_bw, cur_ch; s8 cur_lmt; if (regd > RTW_REGD_WW) return power_limit; if (rs == RTW_RATE_SECTION_MAX) goto err; /* only 20M BW with cck and ofdm */ if (rs == RTW_RATE_SECTION_CCK || rs == RTW_RATE_SECTION_OFDM) bw = RTW_CHANNEL_WIDTH_20; /* only 20/40M BW with ht */ if (rs == RTW_RATE_SECTION_HT_1S || rs == RTW_RATE_SECTION_HT_2S) bw = min_t(u8, bw, RTW_CHANNEL_WIDTH_40); /* select min power limit among [20M BW ~ current BW] */ for (cur_bw = RTW_CHANNEL_WIDTH_20; cur_bw <= bw; cur_bw++) { cur_ch = cch_by_bw[cur_bw]; ch_idx = rtw_channel_to_idx(band, cur_ch); if (ch_idx < 0) goto err; cur_lmt = cur_ch <= RTW_MAX_CHANNEL_NUM_2G ? hal->tx_pwr_limit_2g[regd][cur_bw][rs][ch_idx] : hal->tx_pwr_limit_5g[regd][cur_bw][rs][ch_idx]; power_limit = min_t(s8, cur_lmt, power_limit); } return power_limit; err: WARN(1, "invalid arguments, band=%d, bw=%d, path=%d, rate=%d, ch=%d\n", band, bw, rf_path, rate, channel); return (s8)rtwdev->chip->max_power_index; } static s8 rtw_phy_get_tx_power_sar(struct rtw_dev *rtwdev, u8 sar_band, u8 rf_path, u8 rate) { u8 rs = rtw_phy_rate_to_rate_section(rate); struct rtw_sar_arg arg = { .sar_band = sar_band, .path = rf_path, .rs = rs, }; if (rs == RTW_RATE_SECTION_MAX) goto err; return rtw_query_sar(rtwdev, &arg); err: WARN(1, "invalid arguments, sar_band=%d, path=%d, rate=%d\n", sar_band, rf_path, rate); return (s8)rtwdev->chip->max_power_index; } void rtw_get_tx_power_params(struct rtw_dev *rtwdev, u8 path, u8 rate, u8 bw, u8 ch, u8 regd, struct rtw_power_params *pwr_param) { struct rtw_hal *hal = &rtwdev->hal; struct rtw_dm_info *dm_info = &rtwdev->dm_info; struct rtw_txpwr_idx *pwr_idx; u8 group, band; u8 *base = &pwr_param->pwr_base; s8 *offset = &pwr_param->pwr_offset; s8 *limit = &pwr_param->pwr_limit; s8 *remnant = &pwr_param->pwr_remnant; s8 *sar = &pwr_param->pwr_sar; pwr_idx = &rtwdev->efuse.txpwr_idx_table[path]; group = rtw_get_channel_group(ch, rate); /* base power index for 2.4G/5G */ if (IS_CH_2G_BAND(ch)) { band = PHY_BAND_2G; *base = rtw_phy_get_2g_tx_power_index(rtwdev, &pwr_idx->pwr_idx_2g, bw, rate, group); *offset = hal->tx_pwr_by_rate_offset_2g[path][rate]; } else { band = PHY_BAND_5G; *base = rtw_phy_get_5g_tx_power_index(rtwdev, &pwr_idx->pwr_idx_5g, bw, rate, group); *offset = hal->tx_pwr_by_rate_offset_5g[path][rate]; } *limit = rtw_phy_get_tx_power_limit(rtwdev, band, bw, path, rate, ch, regd); *remnant = (rate <= DESC_RATE11M ? dm_info->txagc_remnant_cck : dm_info->txagc_remnant_ofdm); *sar = rtw_phy_get_tx_power_sar(rtwdev, hal->sar_band, path, rate); } u8 rtw_phy_get_tx_power_index(struct rtw_dev *rtwdev, u8 rf_path, u8 rate, enum rtw_bandwidth bandwidth, u8 channel, u8 regd) { struct rtw_power_params pwr_param = {0}; u8 tx_power; s8 offset; rtw_get_tx_power_params(rtwdev, rf_path, rate, bandwidth, channel, regd, &pwr_param); tx_power = pwr_param.pwr_base; offset = min3(pwr_param.pwr_offset, pwr_param.pwr_limit, pwr_param.pwr_sar); if (rtwdev->chip->en_dis_dpd) offset += rtw_phy_get_dis_dpd_by_rate_diff(rtwdev, rate); tx_power += offset + pwr_param.pwr_remnant; if (tx_power > rtwdev->chip->max_power_index) tx_power = rtwdev->chip->max_power_index; return tx_power; } EXPORT_SYMBOL(rtw_phy_get_tx_power_index); static void rtw_phy_set_tx_power_index_by_rs(struct rtw_dev *rtwdev, u8 ch, u8 path, u8 rs) { struct rtw_hal *hal = &rtwdev->hal; u8 regd = rtw_regd_get(rtwdev); u8 *rates; u8 size; u8 rate; u8 pwr_idx; u8 bw; int i; if (rs >= RTW_RATE_SECTION_MAX) return; rates = rtw_rate_section[rs]; size = rtw_rate_size[rs]; bw = hal->current_band_width; for (i = 0; i < size; i++) { rate = rates[i]; pwr_idx = rtw_phy_get_tx_power_index(rtwdev, path, rate, bw, ch, regd); hal->tx_pwr_tbl[path][rate] = pwr_idx; } } /* set tx power level by path for each rates, note that the order of the rates * are *very* important, bacause 8822B/8821C combines every four bytes of tx * power index into a four-byte power index register, and calls set_tx_agc to * write these values into hardware */ static void rtw_phy_set_tx_power_level_by_path(struct rtw_dev *rtwdev, u8 ch, u8 path) { struct rtw_hal *hal = &rtwdev->hal; u8 rs; /* do not need cck rates if we are not in 2.4G */ if (hal->current_band_type == RTW_BAND_2G) rs = RTW_RATE_SECTION_CCK; else rs = RTW_RATE_SECTION_OFDM; for (; rs < RTW_RATE_SECTION_MAX; rs++) rtw_phy_set_tx_power_index_by_rs(rtwdev, ch, path, rs); } void rtw_phy_set_tx_power_level(struct rtw_dev *rtwdev, u8 channel) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_hal *hal = &rtwdev->hal; u8 path; mutex_lock(&hal->tx_power_mutex); for (path = 0; path < hal->rf_path_num; path++) rtw_phy_set_tx_power_level_by_path(rtwdev, channel, path); chip->ops->set_tx_power_index(rtwdev); mutex_unlock(&hal->tx_power_mutex); } EXPORT_SYMBOL(rtw_phy_set_tx_power_level); static void rtw_phy_tx_power_by_rate_config_by_path(struct rtw_hal *hal, u8 path, u8 rs, u8 size, u8 *rates) { u8 rate; u8 base_idx, rate_idx; s8 base_2g, base_5g; if (rs >= RTW_RATE_SECTION_VHT_1S) base_idx = rates[size - 3]; else base_idx = rates[size - 1]; base_2g = hal->tx_pwr_by_rate_offset_2g[path][base_idx]; base_5g = hal->tx_pwr_by_rate_offset_5g[path][base_idx]; hal->tx_pwr_by_rate_base_2g[path][rs] = base_2g; hal->tx_pwr_by_rate_base_5g[path][rs] = base_5g; for (rate = 0; rate < size; rate++) { rate_idx = rates[rate]; hal->tx_pwr_by_rate_offset_2g[path][rate_idx] -= base_2g; hal->tx_pwr_by_rate_offset_5g[path][rate_idx] -= base_5g; } } void rtw_phy_tx_power_by_rate_config(struct rtw_hal *hal) { u8 path; for (path = 0; path < RTW_RF_PATH_MAX; path++) { rtw_phy_tx_power_by_rate_config_by_path(hal, path, RTW_RATE_SECTION_CCK, rtw_cck_size, rtw_cck_rates); rtw_phy_tx_power_by_rate_config_by_path(hal, path, RTW_RATE_SECTION_OFDM, rtw_ofdm_size, rtw_ofdm_rates); rtw_phy_tx_power_by_rate_config_by_path(hal, path, RTW_RATE_SECTION_HT_1S, rtw_ht_1s_size, rtw_ht_1s_rates); rtw_phy_tx_power_by_rate_config_by_path(hal, path, RTW_RATE_SECTION_HT_2S, rtw_ht_2s_size, rtw_ht_2s_rates); rtw_phy_tx_power_by_rate_config_by_path(hal, path, RTW_RATE_SECTION_VHT_1S, rtw_vht_1s_size, rtw_vht_1s_rates); rtw_phy_tx_power_by_rate_config_by_path(hal, path, RTW_RATE_SECTION_VHT_2S, rtw_vht_2s_size, rtw_vht_2s_rates); } } static void __rtw_phy_tx_power_limit_config(struct rtw_hal *hal, u8 regd, u8 bw, u8 rs) { s8 base; u8 ch; for (ch = 0; ch < RTW_MAX_CHANNEL_NUM_2G; ch++) { base = hal->tx_pwr_by_rate_base_2g[0][rs]; hal->tx_pwr_limit_2g[regd][bw][rs][ch] -= base; } for (ch = 0; ch < RTW_MAX_CHANNEL_NUM_5G; ch++) { base = hal->tx_pwr_by_rate_base_5g[0][rs]; hal->tx_pwr_limit_5g[regd][bw][rs][ch] -= base; } } void rtw_phy_tx_power_limit_config(struct rtw_hal *hal) { u8 regd, bw, rs; /* default at channel 1 */ hal->cch_by_bw[RTW_CHANNEL_WIDTH_20] = 1; for (regd = 0; regd < RTW_REGD_MAX; regd++) for (bw = 0; bw < RTW_CHANNEL_WIDTH_MAX; bw++) for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++) __rtw_phy_tx_power_limit_config(hal, regd, bw, rs); } static void rtw_phy_init_tx_power_limit(struct rtw_dev *rtwdev, u8 regd, u8 bw, u8 rs) { struct rtw_hal *hal = &rtwdev->hal; s8 max_power_index = (s8)rtwdev->chip->max_power_index; u8 ch; /* 2.4G channels */ for (ch = 0; ch < RTW_MAX_CHANNEL_NUM_2G; ch++) hal->tx_pwr_limit_2g[regd][bw][rs][ch] = max_power_index; /* 5G channels */ for (ch = 0; ch < RTW_MAX_CHANNEL_NUM_5G; ch++) hal->tx_pwr_limit_5g[regd][bw][rs][ch] = max_power_index; } void rtw_phy_init_tx_power(struct rtw_dev *rtwdev) { struct rtw_hal *hal = &rtwdev->hal; u8 regd, path, rate, rs, bw; /* init tx power by rate offset */ for (path = 0; path < RTW_RF_PATH_MAX; path++) { for (rate = 0; rate < DESC_RATE_MAX; rate++) { hal->tx_pwr_by_rate_offset_2g[path][rate] = 0; hal->tx_pwr_by_rate_offset_5g[path][rate] = 0; } } /* init tx power limit */ for (regd = 0; regd < RTW_REGD_MAX; regd++) for (bw = 0; bw < RTW_CHANNEL_WIDTH_MAX; bw++) for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++) rtw_phy_init_tx_power_limit(rtwdev, regd, bw, rs); } void rtw_phy_config_swing_table(struct rtw_dev *rtwdev, struct rtw_swing_table *swing_table) { const struct rtw_pwr_track_tbl *tbl = rtwdev->chip->pwr_track_tbl; u8 channel = rtwdev->hal.current_channel; if (IS_CH_2G_BAND(channel)) { if (rtwdev->dm_info.tx_rate <= DESC_RATE11M) { swing_table->p[RF_PATH_A] = tbl->pwrtrk_2g_ccka_p; swing_table->n[RF_PATH_A] = tbl->pwrtrk_2g_ccka_n; swing_table->p[RF_PATH_B] = tbl->pwrtrk_2g_cckb_p; swing_table->n[RF_PATH_B] = tbl->pwrtrk_2g_cckb_n; } else { swing_table->p[RF_PATH_A] = tbl->pwrtrk_2ga_p; swing_table->n[RF_PATH_A] = tbl->pwrtrk_2ga_n; swing_table->p[RF_PATH_B] = tbl->pwrtrk_2gb_p; swing_table->n[RF_PATH_B] = tbl->pwrtrk_2gb_n; } } else if (IS_CH_5G_BAND_1(channel) || IS_CH_5G_BAND_2(channel)) { swing_table->p[RF_PATH_A] = tbl->pwrtrk_5ga_p[RTW_PWR_TRK_5G_1]; swing_table->n[RF_PATH_A] = tbl->pwrtrk_5ga_n[RTW_PWR_TRK_5G_1]; swing_table->p[RF_PATH_B] = tbl->pwrtrk_5gb_p[RTW_PWR_TRK_5G_1]; swing_table->n[RF_PATH_B] = tbl->pwrtrk_5gb_n[RTW_PWR_TRK_5G_1]; } else if (IS_CH_5G_BAND_3(channel)) { swing_table->p[RF_PATH_A] = tbl->pwrtrk_5ga_p[RTW_PWR_TRK_5G_2]; swing_table->n[RF_PATH_A] = tbl->pwrtrk_5ga_n[RTW_PWR_TRK_5G_2]; swing_table->p[RF_PATH_B] = tbl->pwrtrk_5gb_p[RTW_PWR_TRK_5G_2]; swing_table->n[RF_PATH_B] = tbl->pwrtrk_5gb_n[RTW_PWR_TRK_5G_2]; } else if (IS_CH_5G_BAND_4(channel)) { swing_table->p[RF_PATH_A] = tbl->pwrtrk_5ga_p[RTW_PWR_TRK_5G_3]; swing_table->n[RF_PATH_A] = tbl->pwrtrk_5ga_n[RTW_PWR_TRK_5G_3]; swing_table->p[RF_PATH_B] = tbl->pwrtrk_5gb_p[RTW_PWR_TRK_5G_3]; swing_table->n[RF_PATH_B] = tbl->pwrtrk_5gb_n[RTW_PWR_TRK_5G_3]; } else { swing_table->p[RF_PATH_A] = tbl->pwrtrk_2ga_p; swing_table->n[RF_PATH_A] = tbl->pwrtrk_2ga_n; swing_table->p[RF_PATH_B] = tbl->pwrtrk_2gb_p; swing_table->n[RF_PATH_B] = tbl->pwrtrk_2gb_n; } } EXPORT_SYMBOL(rtw_phy_config_swing_table); void rtw_phy_pwrtrack_avg(struct rtw_dev *rtwdev, u8 thermal, u8 path) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; ewma_thermal_add(&dm_info->avg_thermal[path], thermal); dm_info->thermal_avg[path] = ewma_thermal_read(&dm_info->avg_thermal[path]); } EXPORT_SYMBOL(rtw_phy_pwrtrack_avg); bool rtw_phy_pwrtrack_thermal_changed(struct rtw_dev *rtwdev, u8 thermal, u8 path) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 avg = ewma_thermal_read(&dm_info->avg_thermal[path]); if (avg == thermal) return false; return true; } EXPORT_SYMBOL(rtw_phy_pwrtrack_thermal_changed); u8 rtw_phy_pwrtrack_get_delta(struct rtw_dev *rtwdev, u8 path) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 therm_avg, therm_efuse, therm_delta; therm_avg = dm_info->thermal_avg[path]; therm_efuse = rtwdev->efuse.thermal_meter[path]; therm_delta = abs(therm_avg - therm_efuse); return min_t(u8, therm_delta, RTW_PWR_TRK_TBL_SZ - 1); } EXPORT_SYMBOL(rtw_phy_pwrtrack_get_delta); s8 rtw_phy_pwrtrack_get_pwridx(struct rtw_dev *rtwdev, struct rtw_swing_table *swing_table, u8 tbl_path, u8 therm_path, u8 delta) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; const u8 *delta_swing_table_idx_pos; const u8 *delta_swing_table_idx_neg; if (delta >= RTW_PWR_TRK_TBL_SZ) { rtw_warn(rtwdev, "power track table overflow\n"); return 0; } if (!swing_table) { rtw_warn(rtwdev, "swing table not configured\n"); return 0; } delta_swing_table_idx_pos = swing_table->p[tbl_path]; delta_swing_table_idx_neg = swing_table->n[tbl_path]; if (!delta_swing_table_idx_pos || !delta_swing_table_idx_neg) { rtw_warn(rtwdev, "invalid swing table index\n"); return 0; } if (dm_info->thermal_avg[therm_path] > rtwdev->efuse.thermal_meter[therm_path]) return delta_swing_table_idx_pos[delta]; else return -delta_swing_table_idx_neg[delta]; } EXPORT_SYMBOL(rtw_phy_pwrtrack_get_pwridx); bool rtw_phy_pwrtrack_need_lck(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 delta_lck; delta_lck = abs(dm_info->thermal_avg[0] - dm_info->thermal_meter_lck); if (delta_lck >= rtwdev->chip->lck_threshold) { dm_info->thermal_meter_lck = dm_info->thermal_avg[0]; return true; } return false; } EXPORT_SYMBOL(rtw_phy_pwrtrack_need_lck); bool rtw_phy_pwrtrack_need_iqk(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 delta_iqk; delta_iqk = abs(dm_info->thermal_avg[0] - dm_info->thermal_meter_k); if (delta_iqk >= rtwdev->chip->iqk_threshold) { dm_info->thermal_meter_k = dm_info->thermal_avg[0]; return true; } return false; } EXPORT_SYMBOL(rtw_phy_pwrtrack_need_iqk); static void rtw_phy_set_tx_path_by_reg(struct rtw_dev *rtwdev, enum rtw_bb_path tx_path_sel_1ss) { struct rtw_path_div *path_div = &rtwdev->dm_path_div; enum rtw_bb_path tx_path_sel_cck = tx_path_sel_1ss; - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; if (tx_path_sel_1ss == path_div->current_tx_path) return; path_div->current_tx_path = tx_path_sel_1ss; rtw_dbg(rtwdev, RTW_DBG_PATH_DIV, "Switch TX path=%s\n", tx_path_sel_1ss == BB_PATH_A ? "A" : "B"); chip->ops->config_tx_path(rtwdev, rtwdev->hal.antenna_tx, tx_path_sel_1ss, tx_path_sel_cck, false); } static void rtw_phy_tx_path_div_select(struct rtw_dev *rtwdev) { struct rtw_path_div *path_div = &rtwdev->dm_path_div; enum rtw_bb_path path = path_div->current_tx_path; s32 rssi_a = 0, rssi_b = 0; if (path_div->path_a_cnt) rssi_a = path_div->path_a_sum / path_div->path_a_cnt; else rssi_a = 0; if (path_div->path_b_cnt) rssi_b = path_div->path_b_sum / path_div->path_b_cnt; else rssi_b = 0; if (rssi_a != rssi_b) path = (rssi_a > rssi_b) ? BB_PATH_A : BB_PATH_B; path_div->path_a_cnt = 0; path_div->path_a_sum = 0; path_div->path_b_cnt = 0; path_div->path_b_sum = 0; rtw_phy_set_tx_path_by_reg(rtwdev, path); } static void rtw_phy_tx_path_diversity_2ss(struct rtw_dev *rtwdev) { if (rtwdev->hal.antenna_rx != BB_PATH_AB) { rtw_dbg(rtwdev, RTW_DBG_PATH_DIV, "[Return] tx_Path_en=%d, rx_Path_en=%d\n", rtwdev->hal.antenna_tx, rtwdev->hal.antenna_rx); return; } if (rtwdev->sta_cnt == 0) { rtw_dbg(rtwdev, RTW_DBG_PATH_DIV, "No Link\n"); return; } rtw_phy_tx_path_div_select(rtwdev); } void rtw_phy_tx_path_diversity(struct rtw_dev *rtwdev) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; if (!chip->path_div_supported) return; rtw_phy_tx_path_diversity_2ss(rtwdev); } diff --git a/sys/contrib/dev/rtw88/phy.h b/sys/contrib/dev/rtw88/phy.h index b6c5ae60a462..ccfcbd3ced03 100644 --- a/sys/contrib/dev/rtw88/phy.h +++ b/sys/contrib/dev/rtw88/phy.h @@ -1,198 +1,198 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* Copyright(c) 2018-2019 Realtek Corporation */ #ifndef __RTW_PHY_H_ #define __RTW_PHY_H_ #include "debug.h" extern u8 rtw_cck_rates[]; extern u8 rtw_ofdm_rates[]; extern u8 rtw_ht_1s_rates[]; extern u8 rtw_ht_2s_rates[]; extern u8 rtw_vht_1s_rates[]; extern u8 rtw_vht_2s_rates[]; extern u8 *rtw_rate_section[]; extern u8 rtw_rate_size[]; void rtw_phy_init(struct rtw_dev *rtwdev); void rtw_phy_dynamic_mechanism(struct rtw_dev *rtwdev); u8 rtw_phy_rf_power_2_rssi(s8 *rf_power, u8 path_num); u32 rtw_phy_read_rf(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, u32 addr, u32 mask); u32 rtw_phy_read_rf_sipi(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, u32 addr, u32 mask); bool rtw_phy_write_rf_reg_sipi(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, u32 addr, u32 mask, u32 data); bool rtw_phy_write_rf_reg(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, u32 addr, u32 mask, u32 data); bool rtw_phy_write_rf_reg_mix(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, u32 addr, u32 mask, u32 data); void rtw_phy_setup_phy_cond(struct rtw_dev *rtwdev, u32 pkg); void rtw_parse_tbl_phy_cond(struct rtw_dev *rtwdev, const struct rtw_table *tbl); void rtw_parse_tbl_bb_pg(struct rtw_dev *rtwdev, const struct rtw_table *tbl); void rtw_parse_tbl_txpwr_lmt(struct rtw_dev *rtwdev, const struct rtw_table *tbl); void rtw_phy_cfg_mac(struct rtw_dev *rtwdev, const struct rtw_table *tbl, u32 addr, u32 data); void rtw_phy_cfg_agc(struct rtw_dev *rtwdev, const struct rtw_table *tbl, u32 addr, u32 data); void rtw_phy_cfg_bb(struct rtw_dev *rtwdev, const struct rtw_table *tbl, u32 addr, u32 data); void rtw_phy_cfg_rf(struct rtw_dev *rtwdev, const struct rtw_table *tbl, u32 addr, u32 data); void rtw_phy_init_tx_power(struct rtw_dev *rtwdev); void rtw_phy_load_tables(struct rtw_dev *rtwdev); u8 rtw_phy_get_tx_power_index(struct rtw_dev *rtwdev, u8 rf_path, u8 rate, enum rtw_bandwidth bw, u8 channel, u8 regd); void rtw_phy_set_tx_power_level(struct rtw_dev *rtwdev, u8 channel); void rtw_phy_tx_power_by_rate_config(struct rtw_hal *hal); void rtw_phy_tx_power_limit_config(struct rtw_hal *hal); void rtw_phy_pwrtrack_avg(struct rtw_dev *rtwdev, u8 thermal, u8 path); bool rtw_phy_pwrtrack_thermal_changed(struct rtw_dev *rtwdev, u8 thermal, u8 path); u8 rtw_phy_pwrtrack_get_delta(struct rtw_dev *rtwdev, u8 path); s8 rtw_phy_pwrtrack_get_pwridx(struct rtw_dev *rtwdev, struct rtw_swing_table *swing_table, u8 tbl_path, u8 therm_path, u8 delta); bool rtw_phy_pwrtrack_need_lck(struct rtw_dev *rtwdev); bool rtw_phy_pwrtrack_need_iqk(struct rtw_dev *rtwdev); void rtw_phy_config_swing_table(struct rtw_dev *rtwdev, struct rtw_swing_table *swing_table); void rtw_phy_set_edcca_th(struct rtw_dev *rtwdev, u8 l2h, u8 h2l); void rtw_phy_adaptivity_set_mode(struct rtw_dev *rtwdev); void rtw_phy_parsing_cfo(struct rtw_dev *rtwdev, struct rtw_rx_pkt_stat *pkt_stat); void rtw_phy_tx_path_diversity(struct rtw_dev *rtwdev); struct rtw_txpwr_lmt_cfg_pair { u8 regd; u8 band; u8 bw; u8 rs; u8 ch; s8 txpwr_lmt; }; struct rtw_phy_pg_cfg_pair { u32 band; u32 rf_path; u32 tx_num; u32 addr; u32 bitmask; u32 data; }; #define RTW_DECL_TABLE_PHY_COND_CORE(name, cfg, path) \ const struct rtw_table name ## _tbl = { \ .data = name, \ .size = ARRAY_SIZE(name), \ .parse = rtw_parse_tbl_phy_cond, \ .do_cfg = cfg, \ .rf_path = path, \ } #define RTW_DECL_TABLE_PHY_COND(name, cfg) \ RTW_DECL_TABLE_PHY_COND_CORE(name, cfg, 0) #define RTW_DECL_TABLE_RF_RADIO(name, path) \ RTW_DECL_TABLE_PHY_COND_CORE(name, rtw_phy_cfg_rf, RF_PATH_ ## path) #define RTW_DECL_TABLE_BB_PG(name) \ const struct rtw_table name ## _tbl = { \ .data = name, \ .size = ARRAY_SIZE(name), \ .parse = rtw_parse_tbl_bb_pg, \ } #define RTW_DECL_TABLE_TXPWR_LMT(name) \ const struct rtw_table name ## _tbl = { \ .data = name, \ .size = ARRAY_SIZE(name), \ .parse = rtw_parse_tbl_txpwr_lmt, \ } static inline const struct rtw_rfe_def *rtw_get_rfe_def(struct rtw_dev *rtwdev) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_efuse *efuse = &rtwdev->efuse; const struct rtw_rfe_def *rfe_def = NULL; if (chip->rfe_defs_size == 0) return NULL; if (efuse->rfe_option < chip->rfe_defs_size) rfe_def = &chip->rfe_defs[efuse->rfe_option]; rtw_dbg(rtwdev, RTW_DBG_PHY, "use rfe_def[%d]\n", efuse->rfe_option); return rfe_def; } static inline int rtw_check_supported_rfe(struct rtw_dev *rtwdev) { const struct rtw_rfe_def *rfe_def = rtw_get_rfe_def(rtwdev); if (!rfe_def || !rfe_def->phy_pg_tbl || !rfe_def->txpwr_lmt_tbl) { rtw_err(rtwdev, "rfe %d isn't supported\n", rtwdev->efuse.rfe_option); return -ENODEV; } return 0; } void rtw_phy_dig_write(struct rtw_dev *rtwdev, u8 igi); struct rtw_power_params { u8 pwr_base; s8 pwr_offset; s8 pwr_limit; s8 pwr_remnant; s8 pwr_sar; }; void rtw_get_tx_power_params(struct rtw_dev *rtwdev, u8 path, u8 rate, u8 bw, u8 ch, u8 regd, struct rtw_power_params *pwr_param); enum rtw_phy_cck_pd_lv { CCK_PD_LV0, CCK_PD_LV1, CCK_PD_LV2, CCK_PD_LV3, CCK_PD_LV4, CCK_PD_LV_MAX, }; #define MASKBYTE0 0xff #define MASKBYTE1 0xff00 #define MASKBYTE2 0xff0000 #define MASKBYTE3 0xff000000 #define MASKHWORD 0xffff0000 #define MASKLWORD 0x0000ffff #define MASKDWORD 0xffffffff #define RFREG_MASK 0xfffff #define MASK7BITS 0x7f #define MASK12BITS 0xfff #define MASKH4BITS 0xf0000000 #define MASK20BITS 0xfffff #define MASK24BITS 0xffffff #define MASKH3BYTES 0xffffff00 #define MASKL3BYTES 0x00ffffff #define MASKBYTE2HIGHNIBBLE 0x00f00000 #define MASKBYTE3LOWNIBBLE 0x0f000000 #define MASKL3BYTES 0x00ffffff #define CCK_FA_AVG_RESET 0xffffffff #define LSSI_READ_ADDR_MASK 0x7f800000 #define LSSI_READ_EDGE_MASK 0x80000000 #define LSSI_READ_DATA_MASK 0xfffff #define RRSR_RATE_ORDER_MAX 0xfffff #define RRSR_RATE_ORDER_CCK_LEN 4 #endif diff --git a/sys/contrib/dev/rtw88/ps.c b/sys/contrib/dev/rtw88/ps.c index bfa64c038f5f..07e8cbd436cd 100644 --- a/sys/contrib/dev/rtw88/ps.c +++ b/sys/contrib/dev/rtw88/ps.c @@ -1,298 +1,341 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* Copyright(c) 2018-2019 Realtek Corporation */ #include "main.h" #include "reg.h" #include "fw.h" #include "ps.h" #include "mac.h" #include "coex.h" #include "debug.h" static int rtw_ips_pwr_up(struct rtw_dev *rtwdev) { int ret; ret = rtw_core_start(rtwdev); if (ret) rtw_err(rtwdev, "leave idle state failed\n"); + rtw_coex_ips_notify(rtwdev, COEX_IPS_LEAVE); rtw_set_channel(rtwdev); - clear_bit(RTW_FLAG_INACTIVE_PS, rtwdev->flags); return ret; } int rtw_enter_ips(struct rtw_dev *rtwdev) { - set_bit(RTW_FLAG_INACTIVE_PS, rtwdev->flags); + if (!test_bit(RTW_FLAG_POWERON, rtwdev->flags)) + return 0; rtw_coex_ips_notify(rtwdev, COEX_IPS_ENTER); rtw_core_stop(rtwdev); rtw_hci_link_ps(rtwdev, true); return 0; } -static void rtw_restore_port_cfg_iter(void *data, u8 *mac, - struct ieee80211_vif *vif) +static void rtw_restore_port_cfg_iter(void *data, struct ieee80211_vif *vif) { struct rtw_dev *rtwdev = data; struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; u32 config = ~0; rtw_vif_port_config(rtwdev, rtwvif, config); } int rtw_leave_ips(struct rtw_dev *rtwdev) { int ret; + if (test_bit(RTW_FLAG_POWERON, rtwdev->flags)) + return 0; + rtw_hci_link_ps(rtwdev, false); ret = rtw_ips_pwr_up(rtwdev); if (ret) { rtw_err(rtwdev, "failed to leave ips state\n"); return ret; } - rtw_iterate_vifs_atomic(rtwdev, rtw_restore_port_cfg_iter, rtwdev); - - rtw_coex_ips_notify(rtwdev, COEX_IPS_LEAVE); + rtw_iterate_vifs(rtwdev, rtw_restore_port_cfg_iter, rtwdev); return 0; } void rtw_power_mode_change(struct rtw_dev *rtwdev, bool enter) { u8 request, confirm, polling; int ret; request = rtw_read8(rtwdev, rtwdev->hci.rpwm_addr); confirm = rtw_read8(rtwdev, rtwdev->hci.cpwm_addr); /* toggle to request power mode, others remain 0 */ request ^= request | BIT_RPWM_TOGGLE; if (enter) { request |= POWER_MODE_LCLK; if (rtw_get_lps_deep_mode(rtwdev) == LPS_DEEP_MODE_PG) request |= POWER_MODE_PG; } /* Each request require an ack from firmware */ request |= POWER_MODE_ACK; if (rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_TX_WAKE)) request |= POWER_TX_WAKE; rtw_write8(rtwdev, rtwdev->hci.rpwm_addr, request); /* Check firmware get the power requset and ack via cpwm register */ ret = read_poll_timeout_atomic(rtw_read8, polling, (polling ^ confirm) & BIT_RPWM_TOGGLE, 100, 15000, true, rtwdev, rtwdev->hci.cpwm_addr); if (ret) { /* Hit here means that driver failed to get an ack from firmware. * The reason could be that hardware is locked at Deep sleep, * so most of the hardware circuits are not working, even * register read/write; or firmware is locked in some state and * cannot get the request. It should be treated as fatal error * and requires an entire analysis about the firmware/hardware. */ WARN(1, "firmware failed to ack driver for %s Deep Power mode\n", enter ? "entering" : "leaving"); } } EXPORT_SYMBOL(rtw_power_mode_change); static void __rtw_leave_lps_deep(struct rtw_dev *rtwdev) { rtw_hci_deep_ps(rtwdev, false); } static int __rtw_fw_leave_lps_check_reg(struct rtw_dev *rtwdev) { int i; /* Driver needs to wait for firmware to leave LPS state * successfully. Firmware will send null packet to inform AP, * and see if AP sends an ACK back, then firmware will restore * the REG_TCR register. * * If driver does not wait for firmware, null packet with * PS bit could be sent due to incorrect REG_TCR setting. * * In our test, 100ms should be enough for firmware to finish * the flow. If REG_TCR Register is still incorrect after 100ms, * just modify it directly, and throw a warn message. */ for (i = 0 ; i < LEAVE_LPS_TRY_CNT; i++) { if (rtw_read32_mask(rtwdev, REG_TCR, BIT_PWRMGT_HWDATA_EN) == 0) return 0; msleep(20); } return -EBUSY; } static int __rtw_fw_leave_lps_check_c2h(struct rtw_dev *rtwdev) { if (wait_for_completion_timeout(&rtwdev->lps_leave_check, LEAVE_LPS_TIMEOUT)) return 0; return -EBUSY; } static void rtw_fw_leave_lps_check(struct rtw_dev *rtwdev) { bool ret = false; struct rtw_fw_state *fw; if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags)) fw = &rtwdev->wow_fw; else fw = &rtwdev->fw; if (rtw_fw_feature_check(fw, FW_FEATURE_LPS_C2H)) ret = __rtw_fw_leave_lps_check_c2h(rtwdev); else ret = __rtw_fw_leave_lps_check_reg(rtwdev); if (ret) { rtw_write32_clr(rtwdev, REG_TCR, BIT_PWRMGT_HWDATA_EN); rtw_warn(rtwdev, "firmware failed to leave lps state\n"); } } static void rtw_fw_leave_lps_check_prepare(struct rtw_dev *rtwdev) { struct rtw_fw_state *fw; if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags)) fw = &rtwdev->wow_fw; else fw = &rtwdev->fw; if (rtw_fw_feature_check(fw, FW_FEATURE_LPS_C2H)) reinit_completion(&rtwdev->lps_leave_check); } static void rtw_leave_lps_core(struct rtw_dev *rtwdev) { struct rtw_lps_conf *conf = &rtwdev->lps_conf; conf->state = RTW_ALL_ON; conf->awake_interval = 1; conf->rlbm = 0; conf->smart_ps = 0; rtw_hci_link_ps(rtwdev, false); rtw_fw_leave_lps_check_prepare(rtwdev); rtw_fw_set_pwr_mode(rtwdev); rtw_fw_leave_lps_check(rtwdev); clear_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags); rtw_coex_lps_notify(rtwdev, COEX_LPS_DISABLE); } enum rtw_lps_deep_mode rtw_get_lps_deep_mode(struct rtw_dev *rtwdev) { if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags)) return rtwdev->lps_conf.wow_deep_mode; else return rtwdev->lps_conf.deep_mode; } static void __rtw_enter_lps_deep(struct rtw_dev *rtwdev) { if (rtw_get_lps_deep_mode(rtwdev) == LPS_DEEP_MODE_NONE) return; if (!test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags)) { rtw_dbg(rtwdev, RTW_DBG_PS, "Should enter LPS before entering deep PS\n"); return; } if (rtw_get_lps_deep_mode(rtwdev) == LPS_DEEP_MODE_PG) rtw_fw_set_pg_info(rtwdev); rtw_hci_deep_ps(rtwdev, true); } static void rtw_enter_lps_core(struct rtw_dev *rtwdev) { struct rtw_lps_conf *conf = &rtwdev->lps_conf; conf->state = RTW_RF_OFF; conf->awake_interval = 1; conf->rlbm = 1; conf->smart_ps = 2; rtw_coex_lps_notify(rtwdev, COEX_LPS_ENABLE); rtw_fw_set_pwr_mode(rtwdev); rtw_hci_link_ps(rtwdev, true); set_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags); } static void __rtw_enter_lps(struct rtw_dev *rtwdev, u8 port_id) { struct rtw_lps_conf *conf = &rtwdev->lps_conf; if (test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags)) return; conf->mode = RTW_MODE_LPS; conf->port_id = port_id; rtw_enter_lps_core(rtwdev); } static void __rtw_leave_lps(struct rtw_dev *rtwdev) { struct rtw_lps_conf *conf = &rtwdev->lps_conf; if (test_and_clear_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags)) { rtw_dbg(rtwdev, RTW_DBG_PS, "Should leave deep PS before leaving LPS\n"); __rtw_leave_lps_deep(rtwdev); } if (!test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags)) return; conf->mode = RTW_MODE_ACTIVE; rtw_leave_lps_core(rtwdev); } void rtw_enter_lps(struct rtw_dev *rtwdev, u8 port_id) { lockdep_assert_held(&rtwdev->mutex); if (rtwdev->coex.stat.wl_force_lps_ctrl) return; __rtw_enter_lps(rtwdev, port_id); __rtw_enter_lps_deep(rtwdev); } void rtw_leave_lps(struct rtw_dev *rtwdev) { lockdep_assert_held(&rtwdev->mutex); __rtw_leave_lps_deep(rtwdev); __rtw_leave_lps(rtwdev); } void rtw_leave_lps_deep(struct rtw_dev *rtwdev) { lockdep_assert_held(&rtwdev->mutex); __rtw_leave_lps_deep(rtwdev); } + +struct rtw_vif_recalc_lps_iter_data { + struct rtw_dev *rtwdev; + struct ieee80211_vif *found_vif; + int count; +}; + +static void __rtw_vif_recalc_lps(struct rtw_vif_recalc_lps_iter_data *data, + struct ieee80211_vif *vif) +{ + if (data->count < 0) + return; + + if (vif->type != NL80211_IFTYPE_STATION) { + data->count = -1; + return; + } + + data->count++; + data->found_vif = vif; +} + +static void rtw_vif_recalc_lps_iter(void *data, struct ieee80211_vif *vif) +{ + __rtw_vif_recalc_lps(data, vif); +} + +void rtw_recalc_lps(struct rtw_dev *rtwdev, struct ieee80211_vif *new_vif) +{ + struct rtw_vif_recalc_lps_iter_data data = { .rtwdev = rtwdev }; + + if (new_vif) + __rtw_vif_recalc_lps(&data, new_vif); + rtw_iterate_vifs(rtwdev, rtw_vif_recalc_lps_iter, &data); + + if (data.count == 1 && data.found_vif->cfg.ps) { + rtwdev->ps_enabled = true; + } else { + rtwdev->ps_enabled = false; + rtw_leave_lps(rtwdev); + } +} diff --git a/sys/contrib/dev/rtw88/ps.h b/sys/contrib/dev/rtw88/ps.h index c194386f6db5..5ae83d2526cf 100644 --- a/sys/contrib/dev/rtw88/ps.h +++ b/sys/contrib/dev/rtw88/ps.h @@ -1,26 +1,28 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* Copyright(c) 2018-2019 Realtek Corporation */ #ifndef __RTW_PS_H_ #define __RTW_PS_H_ #define RTW_LPS_THRESHOLD 50 #define POWER_MODE_ACK BIT(6) #define POWER_MODE_PG BIT(4) #define POWER_TX_WAKE BIT(1) #define POWER_MODE_LCLK BIT(0) #define LEAVE_LPS_TRY_CNT 5 #define LEAVE_LPS_TIMEOUT msecs_to_jiffies(100) int rtw_enter_ips(struct rtw_dev *rtwdev); int rtw_leave_ips(struct rtw_dev *rtwdev); void rtw_power_mode_change(struct rtw_dev *rtwdev, bool enter); void rtw_enter_lps(struct rtw_dev *rtwdev, u8 port_id); void rtw_leave_lps(struct rtw_dev *rtwdev); void rtw_leave_lps_deep(struct rtw_dev *rtwdev); enum rtw_lps_deep_mode rtw_get_lps_deep_mode(struct rtw_dev *rtwdev); +void rtw_recalc_lps(struct rtw_dev *rtwdev, struct ieee80211_vif *new_vif); + #endif diff --git a/sys/contrib/dev/rtw88/reg.h b/sys/contrib/dev/rtw88/reg.h index 03bd8dc53f72..7c6c11d50ff3 100644 --- a/sys/contrib/dev/rtw88/reg.h +++ b/sys/contrib/dev/rtw88/reg.h @@ -1,690 +1,705 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* Copyright(c) 2018-2019 Realtek Corporation */ #ifndef __RTW_REG_DEF_H__ #define __RTW_REG_DEF_H__ #define REG_SYS_FUNC_EN 0x0002 #define BIT_FEN_EN_25_1 BIT(13) #define BIT_FEN_ELDR BIT(12) #define BIT_FEN_CPUEN BIT(2) #define BIT_FEN_BB_GLB_RST BIT(1) #define BIT_FEN_BB_RSTB BIT(0) #define BIT_R_DIS_PRST BIT(6) #define BIT_WLOCK_1C_B6 BIT(5) #define REG_SYS_PW_CTRL 0x0004 #define BIT_PFM_WOWL BIT(3) #define REG_SYS_CLK_CTRL 0x0008 #define BIT_CPU_CLK_EN BIT(14) #define REG_SYS_CLKR 0x0008 #define BIT_ANA8M BIT(1) #define BIT_WAKEPAD_EN BIT(3) #define BIT_LOADER_CLK_EN BIT(5) #define REG_RSV_CTRL 0x001C #define DISABLE_PI 0x3 #define ENABLE_PI 0x2 #define BITS_RFC_DIRECT (BIT(31) | BIT(30)) #define BIT_WLMCU_IOIF BIT(0) #define REG_RF_CTRL 0x001F #define BIT_RF_SDM_RSTB BIT(2) #define BIT_RF_RSTB BIT(1) #define BIT_RF_EN BIT(0) #define REG_AFE_CTRL1 0x0024 #define BIT_MAC_CLK_SEL (BIT(20) | BIT(21)) #define REG_EFUSE_CTRL 0x0030 #define BIT_EF_FLAG BIT(31) #define BIT_SHIFT_EF_ADDR 8 #define BIT_MASK_EF_ADDR 0x3ff #define BIT_MASK_EF_DATA 0xff #define BITS_EF_ADDR (BIT_MASK_EF_ADDR << BIT_SHIFT_EF_ADDR) #define BITS_PLL 0xf0 #define REG_AFE_XTAL_CTRL 0x24 #define REG_AFE_PLL_CTRL 0x28 #define REG_AFE_CTRL3 0x2c #define BIT_MASK_XTAL 0x00FFF000 #define BIT_XTAL_GMP_BIT4 BIT(28) #define REG_LDO_EFUSE_CTRL 0x0034 #define BIT_MASK_EFUSE_BANK_SEL (BIT(8) | BIT(9)) #define BIT_LDO25_VOLTAGE_V25 0x03 #define BIT_MASK_LDO25_VOLTAGE GENMASK(6, 4) #define BIT_SHIFT_LDO25_VOLTAGE 4 #define BIT_LDO25_EN BIT(7) #define REG_GPIO_MUXCFG 0x0040 #define BIT_FSPI_EN BIT(19) #define BIT_EN_SIC BIT(12) #define BIT_PO_BT_PTA_PINS BIT(9) #define BIT_BT_PTA_EN BIT(5) #define BIT_WLRFE_4_5_EN BIT(2) #define REG_LED_CFG 0x004C #define BIT_LNAON_SEL_EN BIT(26) #define BIT_PAPE_SEL_EN BIT(25) #define BIT_DPDT_WL_SEL BIT(24) #define BIT_DPDT_SEL_EN BIT(23) #define REG_LEDCFG2 0x004E #define REG_PAD_CTRL1 0x0064 #define BIT_BT_BTG_SEL BIT(31) #define BIT_PAPE_WLBT_SEL BIT(29) #define BIT_LNAON_WLBT_SEL BIT(28) #define BIT_BTGP_JTAG_EN BIT(24) #define BIT_BTGP_SPI_EN BIT(20) #define BIT_LED1DIS BIT(15) #define BIT_SW_DPDT_SEL_DATA BIT(0) #define REG_WL_BT_PWR_CTRL 0x0068 #define BIT_BT_FUNC_EN BIT(18) #define BIT_BT_DIG_CLK_EN BIT(8) #define REG_SYS_SDIO_CTRL 0x0070 #define BIT_DBG_GNT_WL_BT BIT(27) #define BIT_LTE_MUX_CTRL_PATH BIT(26) #define REG_HCI_OPT_CTRL 0x0074 #define BIT_USB_SUS_DIS BIT(8) +#define BIT_SDIO_PAD_E5 BIT(18) #define REG_AFE_CTRL_4 0x0078 #define BIT_CK320M_AFE_EN BIT(4) #define BIT_EN_SYN BIT(15) #define REG_LDO_SWR_CTRL 0x007C #define LDO_SEL 0xC3 #define SPS_SEL 0x83 #define BIT_XTA1 BIT(29) #define BIT_XTA0 BIT(28) #define REG_MCUFW_CTRL 0x0080 #define BIT_ANA_PORT_EN BIT(22) #define BIT_MAC_PORT_EN BIT(21) #define BIT_BOOT_FSPI_EN BIT(20) #define BIT_ROM_DLEN BIT(19) #define BIT_ROM_PGE GENMASK(18, 16) /* legacy only */ #define BIT_SHIFT_ROM_PGE 16 #define BIT_FW_INIT_RDY BIT(15) #define BIT_FW_DW_RDY BIT(14) #define BIT_RPWM_TOGGLE BIT(7) #define BIT_RAM_DL_SEL BIT(7) /* legacy only */ #define BIT_DMEM_CHKSUM_OK BIT(6) #define BIT_WINTINI_RDY BIT(6) /* legacy only */ #define BIT_DMEM_DW_OK BIT(5) #define BIT_IMEM_CHKSUM_OK BIT(4) #define BIT_IMEM_DW_OK BIT(3) #define BIT_IMEM_BOOT_LOAD_CHECKSUM_OK BIT(2) #define BIT_FWDL_CHK_RPT BIT(2) /* legacy only */ #define BIT_MCUFWDL_RDY BIT(1) /* legacy only */ #define BIT_MCUFWDL_EN BIT(0) #define BIT_CHECK_SUM_OK (BIT(4) | BIT(6)) #define FW_READY (BIT_FW_INIT_RDY | BIT_FW_DW_RDY | \ BIT_IMEM_DW_OK | BIT_DMEM_DW_OK | \ BIT_CHECK_SUM_OK) #define FW_READY_LEGACY (BIT_MCUFWDL_RDY | BIT_FWDL_CHK_RPT | \ BIT_WINTINI_RDY | BIT_RAM_DL_SEL) #define FW_READY_MASK 0xffff #define REG_MCU_TST_CFG 0x84 #define VAL_FW_TRIGGER 0x1 #define REG_PMC_DBG_CTRL1 0xa8 #define BITS_PMC_BT_IQK_STS GENMASK(22, 21) #define REG_EFUSE_ACCESS 0x00CF #define EFUSE_ACCESS_ON 0x69 #define EFUSE_ACCESS_OFF 0x00 #define REG_WLRF1 0x00EC #define REG_WIFI_BT_INFO 0x00AA #define BIT_BT_INT_EN BIT(15) #define REG_SYS_CFG1 0x00F0 #define BIT_RTL_ID BIT(23) #define BIT_LDO BIT(24) #define BIT_RF_TYPE_ID BIT(27) #define BIT_SHIFT_VENDOR_ID 16 #define BIT_MASK_VENDOR_ID 0xf #define BIT_VENDOR_ID(x) (((x) & BIT_MASK_VENDOR_ID) << BIT_SHIFT_VENDOR_ID) #define BITS_VENDOR_ID (BIT_MASK_VENDOR_ID << BIT_SHIFT_VENDOR_ID) #define BIT_CLEAR_VENDOR_ID(x) ((x) & (~BITS_VENDOR_ID)) #define BIT_GET_VENDOR_ID(x) (((x) >> BIT_SHIFT_VENDOR_ID) & BIT_MASK_VENDOR_ID) #define BIT_SHIFT_CHIP_VER 12 #define BIT_MASK_CHIP_VER 0xf #define BIT_CHIP_VER(x) (((x) & BIT_MASK_CHIP_VER) << BIT_SHIFT_CHIP_VER) #define BITS_CHIP_VER (BIT_MASK_CHIP_VER << BIT_SHIFT_CHIP_VER) #define BIT_CLEAR_CHIP_VER(x) ((x) & (~BITS_CHIP_VER)) #define BIT_GET_CHIP_VER(x) (((x) >> BIT_SHIFT_CHIP_VER) & BIT_MASK_CHIP_VER) #define REG_SYS_STATUS1 0x00F4 #define REG_SYS_STATUS2 0x00F8 #define REG_SYS_CFG2 0x00FC #define REG_WLRF1 0x00EC #define BIT_WLRF1_BBRF_EN (BIT(24) | BIT(25) | BIT(26)) #define REG_CR 0x0100 #define BIT_32K_CAL_TMR_EN BIT(10) #define BIT_MAC_SEC_EN BIT(9) #define BIT_ENSWBCN BIT(8) #define BIT_MACRXEN BIT(7) #define BIT_MACTXEN BIT(6) #define BIT_SCHEDULE_EN BIT(5) #define BIT_PROTOCOL_EN BIT(4) #define BIT_RXDMA_EN BIT(3) #define BIT_TXDMA_EN BIT(2) #define BIT_HCI_RXDMA_EN BIT(1) #define BIT_HCI_TXDMA_EN BIT(0) #define MAC_TRX_ENABLE (BIT_HCI_TXDMA_EN | BIT_HCI_RXDMA_EN | BIT_TXDMA_EN | \ BIT_RXDMA_EN | BIT_PROTOCOL_EN | BIT_SCHEDULE_EN | \ BIT_MACTXEN | BIT_MACRXEN) #define BIT_SHIFT_TXDMA_VOQ_MAP 4 #define BIT_MASK_TXDMA_VOQ_MAP 0x3 #define BIT_TXDMA_VOQ_MAP(x) \ (((x) & BIT_MASK_TXDMA_VOQ_MAP) << BIT_SHIFT_TXDMA_VOQ_MAP) #define BIT_SHIFT_TXDMA_VIQ_MAP 6 #define BIT_MASK_TXDMA_VIQ_MAP 0x3 #define BIT_TXDMA_VIQ_MAP(x) \ (((x) & BIT_MASK_TXDMA_VIQ_MAP) << BIT_SHIFT_TXDMA_VIQ_MAP) #define REG_TXDMA_PQ_MAP 0x010C +#define BIT_RXDMA_ARBBW_EN BIT(0) +#define BIT_RXSHFT_EN BIT(1) +#define BIT_RXDMA_AGG_EN BIT(2) +#define BIT_TXDMA_BW_EN BIT(3) #define BIT_SHIFT_TXDMA_BEQ_MAP 8 #define BIT_MASK_TXDMA_BEQ_MAP 0x3 #define BIT_TXDMA_BEQ_MAP(x) \ (((x) & BIT_MASK_TXDMA_BEQ_MAP) << BIT_SHIFT_TXDMA_BEQ_MAP) #define BIT_SHIFT_TXDMA_BKQ_MAP 10 #define BIT_MASK_TXDMA_BKQ_MAP 0x3 #define BIT_TXDMA_BKQ_MAP(x) \ (((x) & BIT_MASK_TXDMA_BKQ_MAP) << BIT_SHIFT_TXDMA_BKQ_MAP) #define BIT_SHIFT_TXDMA_MGQ_MAP 12 #define BIT_MASK_TXDMA_MGQ_MAP 0x3 #define BIT_TXDMA_MGQ_MAP(x) \ (((x) & BIT_MASK_TXDMA_MGQ_MAP) << BIT_SHIFT_TXDMA_MGQ_MAP) #define BIT_SHIFT_TXDMA_HIQ_MAP 14 #define BIT_MASK_TXDMA_HIQ_MAP 0x3 #define BIT_TXDMA_HIQ_MAP(x) \ (((x) & BIT_MASK_TXDMA_HIQ_MAP) << BIT_SHIFT_TXDMA_HIQ_MAP) #define BIT_SHIFT_TXSC_40M 4 #define BIT_MASK_TXSC_40M 0xf #define BIT_TXSC_40M(x) \ (((x) & BIT_MASK_TXSC_40M) << BIT_SHIFT_TXSC_40M) #define BIT_SHIFT_TXSC_20M 0 #define BIT_MASK_TXSC_20M 0xf #define BIT_TXSC_20M(x) \ (((x) & BIT_MASK_TXSC_20M) << BIT_SHIFT_TXSC_20M) #define BIT_SHIFT_MAC_CLK_SEL 20 #define MAC_CLK_HW_DEF_80M 0 #define MAC_CLK_HW_DEF_40M 1 #define MAC_CLK_HW_DEF_20M 2 #define MAC_CLK_SPEED 80 #define REG_CR 0x0100 #define REG_TRXFF_BNDY 0x0114 #define REG_RXFF_BNDY 0x011C #define REG_FE1IMR 0x0120 #define BIT_FS_RXDONE BIT(16) #define REG_PKTBUF_DBG_CTRL 0x0140 #define REG_C2HEVT 0x01A0 #define REG_MCUTST_1 0x01C0 #define REG_MCUTST_II 0x01C4 #define REG_WOWLAN_WAKE_REASON 0x01C7 #define REG_HMETFR 0x01CC #define REG_HMEBOX0 0x01D0 #define REG_HMEBOX1 0x01D4 #define REG_HMEBOX2 0x01D8 #define REG_HMEBOX3 0x01DC #define REG_HMEBOX0_EX 0x01F0 #define REG_HMEBOX1_EX 0x01F4 #define REG_HMEBOX2_EX 0x01F8 #define REG_HMEBOX3_EX 0x01FC #define REG_RQPN 0x0200 #define BIT_MASK_HPQ 0xff #define BIT_SHIFT_HPQ 0 #define BIT_RQPN_HPQ(x) (((x) & BIT_MASK_HPQ) << BIT_SHIFT_HPQ) #define BIT_MASK_LPQ 0xff #define BIT_SHIFT_LPQ 8 #define BIT_RQPN_LPQ(x) (((x) & BIT_MASK_LPQ) << BIT_SHIFT_LPQ) #define BIT_MASK_PUBQ 0xff #define BIT_SHIFT_PUBQ 16 #define BIT_RQPN_PUBQ(x) (((x) & BIT_MASK_PUBQ) << BIT_SHIFT_PUBQ) #define BIT_RQPN_HLP(h, l, p) (BIT_LD_RQPN | BIT_RQPN_HPQ(h) | \ BIT_RQPN_LPQ(l) | BIT_RQPN_PUBQ(p)) #define REG_FIFOPAGE_CTRL_2 0x0204 #define BIT_BCN_VALID_V1 BIT(15) #define BIT_MASK_BCN_HEAD_1_V1 0xfff #define REG_AUTO_LLT_V1 0x0208 #define BIT_AUTO_INIT_LLT_V1 BIT(0) #define REG_DWBCN0_CTRL 0x0208 #define BIT_BCN_VALID BIT(16) #define REG_TXDMA_OFFSET_CHK 0x020C #define BIT_DROP_DATA_EN BIT(9) #define REG_TXDMA_STATUS 0x0210 #define BTI_PAGE_OVF BIT(2) #define REG_RQPN_NPQ 0x0214 #define BIT_MASK_NPQ 0xff #define BIT_SHIFT_NPQ 0 #define BIT_MASK_EPQ 0xff #define BIT_SHIFT_EPQ 16 #define BIT_RQPN_NPQ(x) (((x) & BIT_MASK_NPQ) << BIT_SHIFT_NPQ) #define BIT_RQPN_EPQ(x) (((x) & BIT_MASK_EPQ) << BIT_SHIFT_EPQ) #define BIT_RQPN_NE(n, e) (BIT_RQPN_NPQ(n) | BIT_RQPN_EPQ(e)) #define REG_AUTO_LLT 0x0224 #define BIT_AUTO_INIT_LLT BIT(16) #define REG_RQPN_CTRL_1 0x0228 #define REG_RQPN_CTRL_2 0x022C #define BIT_LD_RQPN BIT(31) #define REG_FIFOPAGE_INFO_1 0x0230 #define REG_FIFOPAGE_INFO_2 0x0234 #define REG_FIFOPAGE_INFO_3 0x0238 #define REG_FIFOPAGE_INFO_4 0x023C #define REG_FIFOPAGE_INFO_5 0x0240 #define REG_H2C_HEAD 0x0244 #define REG_H2C_TAIL 0x0248 #define REG_H2C_READ_ADDR 0x024C #define REG_H2C_INFO 0x0254 +#define REG_RXDMA_AGG_PG_TH 0x0280 +#define BIT_RXDMA_AGG_PG_TH GENMASK(7, 0) +#define BIT_DMA_AGG_TO_V1 GENMASK(15, 8) +#define BIT_EN_PRE_CALC BIT(29) #define REG_RXPKT_NUM 0x0284 #define BIT_RXDMA_REQ BIT(19) #define BIT_RW_RELEASE BIT(18) #define BIT_RXDMA_IDLE BIT(17) +#define REG_RXDMA_STATUS 0x0288 +#define REG_RXDMA_DPR 0x028C +#define REG_RXDMA_MODE 0x0290 +#define BIT_DMA_MODE BIT(1) #define REG_RXPKTNUM 0x02B0 #define REG_INT_MIG 0x0304 #define REG_HCI_MIX_CFG 0x03FC #define BIT_PCIE_EMAC_PDN_AUX_TO_FAST_CLK BIT(26) #define REG_BCNQ_INFO 0x0418 #define BIT_MGQ_CPU_EMPTY BIT(24) #define REG_FWHW_TXQ_CTRL 0x0420 #define BIT_EN_BCNQ_DL BIT(22) #define BIT_EN_WR_FREE_TAIL BIT(20) #define REG_HWSEQ_CTRL 0x0423 #define REG_BCNQ_BDNY_V1 0x0424 #define REG_BCNQ_BDNY 0x0424 #define REG_MGQ_BDNY 0x0425 #define REG_LIFETIME_EN 0x0426 #define BIT_BA_PARSER_EN BIT(5) #define REG_SPEC_SIFS 0x0428 #define REG_RETRY_LIMIT 0x042a #define REG_DARFRC 0x0430 #define REG_DARFRCH 0x0434 #define REG_RARFRCH 0x043C #define REG_RRSR 0x0440 #define BITS_RRSR_RSC GENMASK(22, 21) #define REG_ARFR0 0x0444 #define REG_ARFRH0 0x0448 #define REG_ARFR1_V1 0x044C #define REG_ARFRH1_V1 0x0450 #define REG_CCK_CHECK 0x0454 #define BIT_CHECK_CCK_EN BIT(7) #define REG_AMPDU_MAX_TIME_V1 0x0455 #define REG_BCNQ1_BDNY_V1 0x0456 #define REG_AMPDU_MAX_TIME 0x0456 #define REG_WMAC_LBK_BF_HD 0x045D #define REG_TX_HANG_CTRL 0x045E #define BIT_EN_GNT_BT_AWAKE BIT(3) #define BIT_EN_EOF_V1 BIT(2) #define REG_DATA_SC 0x0483 #define REG_ARFR4 0x049C #define BIT_WL_RFK BIT(0) #define REG_ARFRH4 0x04A0 #define REG_ARFR5 0x04A4 #define REG_ARFRH5 0x04A8 #define REG_SW_AMPDU_BURST_MODE_CTRL 0x04BC #define BIT_PRE_TX_CMD BIT(6) #define REG_QUEUE_CTRL 0x04C6 #define BIT_PTA_WL_TX_EN BIT(4) #define BIT_PTA_EDCCA_EN BIT(5) #define REG_SINGLE_AMPDU_CTRL 0x04C7 #define BIT_EN_SINGLE_APMDU BIT(7) #define REG_PROT_MODE_CTRL 0x04C8 #define REG_MAX_AGGR_NUM 0x04CA #define REG_BAR_MODE_CTRL 0x04CC #define REG_PRECNT_CTRL 0x04E5 #define BIT_BTCCA_CTRL (BIT(0) | BIT(1)) #define BIT_EN_PRECNT BIT(11) #define REG_DUMMY_PAGE4_V1 0x04FC #define REG_EDCA_VO_PARAM 0x0500 #define REG_EDCA_VI_PARAM 0x0504 #define REG_EDCA_BE_PARAM 0x0508 #define REG_EDCA_BK_PARAM 0x050C #define BIT_MASK_TXOP_LMT GENMASK(26, 16) #define BIT_MASK_CWMAX GENMASK(15, 12) #define BIT_MASK_CWMIN GENMASK(11, 8) #define BIT_MASK_AIFS GENMASK(7, 0) #define REG_PIFS 0x0512 #define REG_SIFS 0x0514 #define BIT_SHIFT_SIFS_OFDM_CTX 8 #define BIT_SHIFT_SIFS_CCK_TRX 16 #define BIT_SHIFT_SIFS_OFDM_TRX 24 #define REG_AGGR_BREAK_TIME 0x051A #define REG_SLOT 0x051B #define REG_TX_PTCL_CTRL 0x0520 #define BIT_DIS_EDCCA BIT(15) #define BIT_SIFS_BK_EN BIT(12) #define REG_TXPAUSE 0x0522 #define BIT_AC_QUEUE GENMASK(7, 0) +#define BIT_HIGH_QUEUE BIT(5) #define REG_RD_CTRL 0x0524 #define BIT_EDCCA_MSK_CNTDOWN_EN BIT(11) #define BIT_DIS_TXOP_CFE BIT(10) #define BIT_DIS_LSIG_CFE BIT(9) #define BIT_DIS_STBC_CFE BIT(8) #define REG_TBTT_PROHIBIT 0x0540 #define BIT_SHIFT_TBTT_HOLD_TIME_AP 8 #define REG_RD_NAV_NXT 0x0544 #define REG_NAV_PROT_LEN 0x0546 #define REG_BCN_CTRL 0x0550 #define BIT_DIS_TSF_UDT BIT(4) #define BIT_EN_BCN_FUNCTION BIT(3) #define BIT_EN_TXBCN_RPT BIT(2) #define REG_BCN_CTRL_CLINT0 0x0551 #define REG_DRVERLYINT 0x0558 #define REG_BCNDMATIM 0x0559 #define REG_ATIMWND 0x055A #define REG_USTIME_TSF 0x055C #define REG_BCN_MAX_ERR 0x055D #define REG_RXTSF_OFFSET_CCK 0x055E #define REG_MISC_CTRL 0x0577 #define BIT_EN_FREE_CNT BIT(3) #define BIT_DIS_SECOND_CCA (BIT(0) | BIT(1)) #define REG_HIQ_NO_LMT_EN 0x5A7 #define REG_DTIM_COUNTER_ROOT 0x5A8 #define BIT_HIQ_NO_LMT_EN_ROOT BIT(0) #define REG_TIMER0_SRC_SEL 0x05B4 #define BIT_TSFT_SEL_TIMER0 (BIT(4) | BIT(5) | BIT(6)) #define REG_TCR 0x0604 #define BIT_PWRMGT_HWDATA_EN BIT(7) #define BIT_TCR_UPDATE_TIMIE BIT(5) +#define BIT_TCR_UPDATE_HGQMD BIT(4) #define REG_RCR 0x0608 #define BIT_APP_FCS BIT(31) #define BIT_APP_MIC BIT(30) #define BIT_APP_ICV BIT(29) #define BIT_APP_PHYSTS BIT(28) #define BIT_APP_BASSN BIT(27) #define BIT_VHT_DACK BIT(26) #define BIT_TCPOFLD_EN BIT(25) #define BIT_ENMBID BIT(24) #define BIT_LSIGEN BIT(23) #define BIT_MFBEN BIT(22) #define BIT_DISCHKPPDLLEN BIT(21) #define BIT_PKTCTL_DLEN BIT(20) #define BIT_DISGCLK BIT(19) #define BIT_TIM_PARSER_EN BIT(18) #define BIT_BC_MD_EN BIT(17) #define BIT_UC_MD_EN BIT(16) #define BIT_RXSK_PERPKT BIT(15) #define BIT_HTC_LOC_CTRL BIT(14) #define BIT_RPFM_CAM_ENABLE BIT(12) #define BIT_TA_BCN BIT(11) #define BIT_RCR_ADF BIT(11) #define BIT_DISDECMYPKT BIT(10) #define BIT_AICV BIT(9) #define BIT_ACRC32 BIT(8) #define BIT_CBSSID_BCN BIT(7) #define BIT_CBSSID_DATA BIT(6) #define BIT_APWRMGT BIT(5) #define BIT_ADD3 BIT(4) #define BIT_AB BIT(3) #define BIT_AM BIT(2) #define BIT_APM BIT(1) #define BIT_AAP BIT(0) #define REG_RX_PKT_LIMIT 0x060C #define REG_RX_DRVINFO_SZ 0x060F #define BIT_APP_PHYSTS BIT(28) #define REG_MAR 0x0620 #define REG_USTIME_EDCA 0x0638 #define REG_ACKTO_CCK 0x0639 #define REG_MAC_SPEC_SIFS 0x063A #define REG_RESP_SIFS_CCK 0x063C #define REG_RESP_SIFS_OFDM 0x063E #define REG_ACKTO 0x0640 #define REG_EIFS 0x0642 #define REG_NAV_CTRL 0x0650 #define REG_WMAC_TRXPTCL_CTL 0x0668 #define BIT_RFMOD (BIT(7) | BIT(8)) #define BIT_RFMOD_80M BIT(8) #define BIT_RFMOD_40M BIT(7) #define REG_WMAC_TRXPTCL_CTL_H 0x066C #define REG_WKFMCAM_CMD 0x0698 #define BIT_WKFCAM_POLLING_V1 BIT(31) #define BIT_WKFCAM_CLR_V1 BIT(30) #define BIT_WKFCAM_WE BIT(16) #define BIT_SHIFT_WKFCAM_ADDR_V2 8 #define BIT_MASK_WKFCAM_ADDR_V2 0xff #define BIT_WKFCAM_ADDR_V2(x) \ (((x) & BIT_MASK_WKFCAM_ADDR_V2) << BIT_SHIFT_WKFCAM_ADDR_V2) #define REG_WKFMCAM_RWD 0x069C #define BIT_WKFMCAM_VALID BIT(31) #define BIT_WKFMCAM_BC BIT(26) #define BIT_WKFMCAM_MC BIT(25) #define BIT_WKFMCAM_UC BIT(24) #define REG_RXFLTMAP0 0x06A0 #define REG_RXFLTMAP1 0x06A2 #define REG_RXFLTMAP2 0x06A4 #define REG_RXFLTMAP4 0x068A #define REG_BT_COEX_TABLE0 0x06C0 #define REG_BT_COEX_TABLE1 0x06C4 #define REG_BT_COEX_BRK_TABLE 0x06C8 #define REG_BT_COEX_TABLE_H 0x06CC #define REG_BT_COEX_TABLE_H1 0x06CD #define REG_BT_COEX_TABLE_H2 0x06CE #define REG_BT_COEX_TABLE_H3 0x06CF #define REG_BBPSF_CTRL 0x06DC #define REG_BT_COEX_V2 0x0762 #define BIT_GNT_BT_POLARITY BIT(12) #define BIT_LTE_COEX_EN BIT(7) #define REG_BT_COEX_ENH_INTR_CTRL 0x76E #define BIT_R_GRANTALL_WLMASK BIT(3) #define BIT_STATIS_BT_EN BIT(2) #define REG_BT_ACT_STATISTICS 0x0770 #define REG_BT_ACT_STATISTICS_1 0x0774 #define REG_BT_STAT_CTRL 0x0778 #define REG_BT_TDMA_TIME 0x0790 #define BIT_MASK_SAMPLE_RATE GENMASK(5, 0) #define REG_LTR_IDLE_LATENCY 0x0798 #define REG_LTR_ACTIVE_LATENCY 0x079C #define REG_LTR_CTRL_BASIC 0x07A4 #define REG_WMAC_OPTION_FUNCTION 0x07D0 #define REG_WMAC_OPTION_FUNCTION_1 0x07D4 #define REG_FPGA0_RFMOD 0x0800 #define BIT_CCKEN BIT(24) #define BIT_OFDMEN BIT(25) #define REG_RX_GAIN_EN 0x081c #define REG_RFE_CTRL_E 0x0974 #define REG_2ND_CCA_CTRL 0x0976 #define REG_CCK0_FAREPORT 0xa2c #define BIT_CCK0_2RX BIT(18) #define BIT_CCK0_MRC BIT(22) #define REG_DIS_DPD 0x0a70 #define DIS_DPD_MASK GENMASK(9, 0) #define DIS_DPD_RATE6M BIT(0) #define DIS_DPD_RATE9M BIT(1) #define DIS_DPD_RATEMCS0 BIT(2) #define DIS_DPD_RATEMCS1 BIT(3) #define DIS_DPD_RATEMCS8 BIT(4) #define DIS_DPD_RATEMCS9 BIT(5) #define DIS_DPD_RATEVHT1SS_MCS0 BIT(6) #define DIS_DPD_RATEVHT1SS_MCS1 BIT(7) #define DIS_DPD_RATEVHT2SS_MCS0 BIT(8) #define DIS_DPD_RATEVHT2SS_MCS1 BIT(9) #define DIS_DPD_RATEALL GENMASK(9, 0) #define REG_RFE_CTRL8 0x0cb4 #define BIT_MASK_RFE_SEL89 GENMASK(7, 0) #define REG_RFE_INV8 0x0cbd #define BIT_MASK_RFE_INV89 GENMASK(1, 0) #define REG_RFE_INV16 0x0cbe #define BIT_RFE_BUF_EN BIT(3) #define REG_ANAPAR_XTAL_0 0x1040 #define BIT_XCAP_0 GENMASK(23, 10) #define REG_CPU_DMEM_CON 0x1080 #define BIT_WL_PLATFORM_RST BIT(16) #define BIT_WL_SECURITY_CLK BIT(15) #define BIT_DDMA_EN BIT(8) #define REG_H2C_PKT_READADDR 0x10D0 #define REG_H2C_PKT_WRITEADDR 0x10D4 #define REG_FW_DBG7 0x10FC #define FW_KEY_MASK 0xffffff00 #define REG_CR_EXT 0x1100 #define REG_DDMA_CH0SA 0x1200 #define REG_DDMA_CH0DA 0x1204 #define REG_DDMA_CH0CTRL 0x1208 #define BIT_DDMACH0_OWN BIT(31) #define BIT_DDMACH0_CHKSUM_EN BIT(29) #define BIT_DDMACH0_CHKSUM_STS BIT(27) #define BIT_DDMACH0_DDMA_MODE BIT(26) #define BIT_DDMACH0_RESET_CHKSUM_STS BIT(25) #define BIT_DDMACH0_CHKSUM_CONT BIT(24) #define BIT_MASK_DDMACH0_DLEN 0x3ffff #define REG_H2CQ_CSR 0x1330 #define BIT_H2CQ_FULL BIT(31) #define REG_FAST_EDCA_VOVI_SETTING 0x1448 #define REG_FAST_EDCA_BEBK_SETTING 0x144C #define REG_RXPSF_CTRL 0x1610 #define BIT_RXGCK_FIFOTHR_EN BIT(28) #define BIT_SHIFT_RXGCK_VHT_FIFOTHR 26 #define BIT_MASK_RXGCK_VHT_FIFOTHR 0x3 #define BIT_RXGCK_VHT_FIFOTHR(x) \ (((x) & BIT_MASK_RXGCK_VHT_FIFOTHR) << BIT_SHIFT_RXGCK_VHT_FIFOTHR) #define BITS_RXGCK_VHT_FIFOTHR \ (BIT_MASK_RXGCK_VHT_FIFOTHR << BIT_SHIFT_RXGCK_VHT_FIFOTHR) #define BIT_SHIFT_RXGCK_HT_FIFOTHR 24 #define BIT_MASK_RXGCK_HT_FIFOTHR 0x3 #define BIT_RXGCK_HT_FIFOTHR(x) \ (((x) & BIT_MASK_RXGCK_HT_FIFOTHR) << BIT_SHIFT_RXGCK_HT_FIFOTHR) #define BITS_RXGCK_HT_FIFOTHR \ (BIT_MASK_RXGCK_HT_FIFOTHR << BIT_SHIFT_RXGCK_HT_FIFOTHR) #define BIT_SHIFT_RXGCK_OFDM_FIFOTHR 22 #define BIT_MASK_RXGCK_OFDM_FIFOTHR 0x3 #define BIT_RXGCK_OFDM_FIFOTHR(x) \ (((x) & BIT_MASK_RXGCK_OFDM_FIFOTHR) << BIT_SHIFT_RXGCK_OFDM_FIFOTHR) #define BITS_RXGCK_OFDM_FIFOTHR \ (BIT_MASK_RXGCK_OFDM_FIFOTHR << BIT_SHIFT_RXGCK_OFDM_FIFOTHR) #define BIT_SHIFT_RXGCK_CCK_FIFOTHR 20 #define BIT_MASK_RXGCK_CCK_FIFOTHR 0x3 #define BIT_RXGCK_CCK_FIFOTHR(x) \ (((x) & BIT_MASK_RXGCK_CCK_FIFOTHR) << BIT_SHIFT_RXGCK_CCK_FIFOTHR) #define BITS_RXGCK_CCK_FIFOTHR \ (BIT_MASK_RXGCK_CCK_FIFOTHR << BIT_SHIFT_RXGCK_CCK_FIFOTHR) #define BIT_RXGCK_OFDMCCA_EN BIT(16) #define BIT_SHIFT_RXPSF_PKTLENTHR 13 #define BIT_MASK_RXPSF_PKTLENTHR 0x7 #define BIT_RXPSF_PKTLENTHR(x) \ (((x) & BIT_MASK_RXPSF_PKTLENTHR) << BIT_SHIFT_RXPSF_PKTLENTHR) #define BITS_RXPSF_PKTLENTHR \ (BIT_MASK_RXPSF_PKTLENTHR << BIT_SHIFT_RXPSF_PKTLENTHR) #define BIT_CLEAR_RXPSF_PKTLENTHR(x) ((x) & (~BITS_RXPSF_PKTLENTHR)) #define BIT_SET_RXPSF_PKTLENTHR(x, v) \ (BIT_CLEAR_RXPSF_PKTLENTHR(x) | BIT_RXPSF_PKTLENTHR(v)) #define BIT_RXPSF_CTRLEN BIT(12) #define BIT_RXPSF_VHTCHKEN BIT(11) #define BIT_RXPSF_HTCHKEN BIT(10) #define BIT_RXPSF_OFDMCHKEN BIT(9) #define BIT_RXPSF_CCKCHKEN BIT(8) #define BIT_RXPSF_OFDMRST BIT(7) #define BIT_RXPSF_CCKRST BIT(6) #define BIT_RXPSF_MHCHKEN BIT(5) #define BIT_RXPSF_CONT_ERRCHKEN BIT(4) #define BIT_RXPSF_ALL_ERRCHKEN BIT(3) #define BIT_SHIFT_RXPSF_ERRTHR 0 #define BIT_MASK_RXPSF_ERRTHR 0x7 #define BIT_RXPSF_ERRTHR(x) \ (((x) & BIT_MASK_RXPSF_ERRTHR) << BIT_SHIFT_RXPSF_ERRTHR) #define BITS_RXPSF_ERRTHR (BIT_MASK_RXPSF_ERRTHR << BIT_SHIFT_RXPSF_ERRTHR) #define BIT_CLEAR_RXPSF_ERRTHR(x) ((x) & (~BITS_RXPSF_ERRTHR)) #define BIT_GET_RXPSF_ERRTHR(x) \ (((x) >> BIT_SHIFT_RXPSF_ERRTHR) & BIT_MASK_RXPSF_ERRTHR) #define BIT_SET_RXPSF_ERRTHR(x, v) \ (BIT_CLEAR_RXPSF_ERRTHR(x) | BIT_RXPSF_ERRTHR(v)) #define REG_RXPSF_TYPE_CTRL 0x1614 #define REG_GENERAL_OPTION 0x1664 #define BIT_DUMMY_FCS_READY_MASK_EN BIT(9) #define REG_WL2LTECOEX_INDIRECT_ACCESS_CTRL_V1 0x1700 #define REG_WL2LTECOEX_INDIRECT_ACCESS_WRITE_DATA_V1 0x1704 #define REG_WL2LTECOEX_INDIRECT_ACCESS_READ_DATA_V1 0x1708 #define LTECOEX_READY BIT(29) #define LTECOEX_ACCESS_CTRL REG_WL2LTECOEX_INDIRECT_ACCESS_CTRL_V1 #define LTECOEX_WRITE_DATA REG_WL2LTECOEX_INDIRECT_ACCESS_WRITE_DATA_V1 #define LTECOEX_READ_DATA REG_WL2LTECOEX_INDIRECT_ACCESS_READ_DATA_V1 #define REG_IGN_GNT_BT1 0x1860 #define REG_RFESEL_CTRL 0x1990 #define REG_NOMASK_TXBT 0x1ca7 #define REG_ANAPAR 0x1c30 #define BIT_ANAPAR_BTPS BIT(22) #define REG_RSTB_SEL 0x1c38 #define BIT_DAC_OFF_ENABLE BIT(4) #define BIT_PI_IGNORE_GNT_BT BIT(3) #define BIT_NOMASK_TXBT_ENABLE BIT(3) #define REG_HRCV_MSG 0x1cf #define REG_EDCCA_REPORT 0x2d38 #define BIT_EDCCA_FLAG BIT(24) #define REG_IGN_GNTBT4 0x4160 #define RF_MODE 0x00 #define RF_MODOPT 0x01 #define RF_WLINT 0x01 #define RF_WLSEL 0x02 #define RF_DTXLOK 0x08 #define RF_CFGCH 0x18 #define BIT_BAND GENMASK(18, 16) #define RF_RCK 0x1d #define RF_LUTWA 0x33 #define RF_LUTWD1 0x3e #define RF_LUTWD0 0x3f #define BIT_GAIN_EXT BIT(12) #define BIT_DATA_L GENMASK(11, 0) #define RF_T_METER 0x42 #define RF_BSPAD 0x54 #define RF_GAINTX 0x56 #define RF_TXATANK 0x64 #define RF_TRXIQ 0x66 #define RF_RXIQGEN 0x8d #define RF_SYN_PFD 0xb0 #define RF_XTALX2 0xb8 #define RF_SYN_CTRL 0xbb #define RF_MALSEL 0xbe #define RF_SYN_AAC 0xc9 #define RF_AAC_CTRL 0xca #define RF_FAST_LCK 0xcc #define RF_RCKD 0xde #define RF_TXADBG 0xde #define RF_LUTDBG 0xdf #define BIT_TXA_TANK BIT(4) #define RF_LUTWE2 0xee #define RF_LUTWE 0xef #define LTE_COEX_CTRL 0x38 #define LTE_WL_TRX_CTRL 0xa0 #define LTE_BT_TRX_CTRL 0xa4 #endif diff --git a/sys/contrib/dev/rtw88/regd.c b/sys/contrib/dev/rtw88/regd.c index 315c2b193e92..2f547cbcf6da 100644 --- a/sys/contrib/dev/rtw88/regd.c +++ b/sys/contrib/dev/rtw88/regd.c @@ -1,529 +1,531 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* Copyright(c) 2018-2019 Realtek Corporation */ #include "main.h" #include "regd.h" #include "debug.h" #include "phy.h" #define COUNTRY_REGD_ENT(_alpha2, _regd_2g, _regd_5g) \ {.alpha2 = (_alpha2), \ .txpwr_regd_2g = (_regd_2g), \ .txpwr_regd_5g = (_regd_5g), \ } #define rtw_dbg_regd_dump(_dev, _msg, _args...) \ do { \ struct rtw_dev *__d = (_dev); \ const struct rtw_regd *__r = &__d->regd; \ rtw_dbg(__d, RTW_DBG_REGD, _msg \ "apply alpha2 %c%c, regd {%d, %d}, dfs_region %d\n",\ ##_args, \ __r->regulatory->alpha2[0], \ __r->regulatory->alpha2[1], \ __r->regulatory->txpwr_regd_2g, \ __r->regulatory->txpwr_regd_5g, \ __r->dfs_region); \ } while (0) /* If country code is not correctly defined in efuse, * use worldwide country code and txpwr regd. */ static const struct rtw_regulatory rtw_reg_ww = COUNTRY_REGD_ENT("00", RTW_REGD_WW, RTW_REGD_WW); static const struct rtw_regulatory rtw_reg_map[] = { COUNTRY_REGD_ENT("AD", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("AE", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("AF", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("AG", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("AI", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("AL", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("AM", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("AN", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("AO", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("AQ", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("AR", RTW_REGD_MEXICO, RTW_REGD_MEXICO), COUNTRY_REGD_ENT("AS", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("AT", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("AU", RTW_REGD_ACMA, RTW_REGD_ACMA), COUNTRY_REGD_ENT("AW", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("AZ", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("BA", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("BB", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("BD", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("BE", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("BF", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("BG", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("BH", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("BI", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("BJ", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("BM", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("BN", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("BO", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("BR", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("BS", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("BT", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("BV", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("BW", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("BY", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("BZ", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("CA", RTW_REGD_IC, RTW_REGD_IC), COUNTRY_REGD_ENT("CC", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("CD", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("CF", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("CG", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("CH", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("CI", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("CK", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("CL", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("CM", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("CN", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("CO", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("CR", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("CV", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("CX", RTW_REGD_ACMA, RTW_REGD_ACMA), COUNTRY_REGD_ENT("CY", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("CZ", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("DE", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("DJ", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("DK", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("DM", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("DO", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("DZ", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("EC", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("EE", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("EG", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("EH", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("ER", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("ES", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("ET", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("FI", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("FJ", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("FK", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("FM", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("FO", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("FR", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("GA", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("GB", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("GD", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("GE", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("GF", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("GG", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("GH", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("GI", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("GL", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("GM", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("GN", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("GP", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("GQ", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("GR", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("GS", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("GT", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("GU", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("GW", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("GY", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("HK", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("HM", RTW_REGD_ACMA, RTW_REGD_ACMA), COUNTRY_REGD_ENT("HN", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("HR", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("HT", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("HU", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("ID", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("IE", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("IL", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("IM", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("IN", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("IO", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("IQ", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("IR", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("IS", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("IT", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("JE", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("JM", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("JO", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("JP", RTW_REGD_MKK, RTW_REGD_MKK), COUNTRY_REGD_ENT("KE", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("KG", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("KH", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("KI", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("KM", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("KN", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("KR", RTW_REGD_KCC, RTW_REGD_KCC), COUNTRY_REGD_ENT("KW", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("KY", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("KZ", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("LA", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("LB", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("LC", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("LI", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("LK", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("LR", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("LS", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("LT", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("LU", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("LV", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("LY", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("MA", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("MC", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("MD", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("ME", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("MF", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("MG", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("MH", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("MK", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("ML", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("MM", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("MN", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("MO", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("MP", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("MQ", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("MR", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("MS", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("MT", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("MU", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("MV", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("MW", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("MX", RTW_REGD_MEXICO, RTW_REGD_MEXICO), COUNTRY_REGD_ENT("MY", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("MZ", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("NA", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("NC", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("NE", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("NF", RTW_REGD_ACMA, RTW_REGD_ACMA), COUNTRY_REGD_ENT("NG", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("NI", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("NL", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("NO", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("NP", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("NR", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("NU", RTW_REGD_ACMA, RTW_REGD_ACMA), COUNTRY_REGD_ENT("NZ", RTW_REGD_ACMA, RTW_REGD_ACMA), COUNTRY_REGD_ENT("OM", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("PA", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("PE", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("PF", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("PG", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("PH", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("PK", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("PL", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("PM", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("PR", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("PS", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("PT", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("PW", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("PY", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("QA", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("RE", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("RO", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("RS", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("RU", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("RW", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("SA", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("SB", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("SC", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("SE", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("SG", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("SH", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("SI", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("SJ", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("SK", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("SL", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("SM", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("SN", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("SO", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("SR", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("ST", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("SV", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("SX", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("SZ", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("TC", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("TD", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("TF", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("TG", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("TH", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("TJ", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("TK", RTW_REGD_ACMA, RTW_REGD_ACMA), COUNTRY_REGD_ENT("TM", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("TN", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("TO", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("TR", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("TT", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("TV", RTW_REGD_ETSI, RTW_REGD_WW), COUNTRY_REGD_ENT("TW", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("TZ", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("UA", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("UG", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("US", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("UY", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("UZ", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("VA", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("VC", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("VE", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("VG", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("VI", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("VN", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("VU", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("WF", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("WS", RTW_REGD_FCC, RTW_REGD_FCC), COUNTRY_REGD_ENT("XK", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("YE", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("YT", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("ZA", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("ZM", RTW_REGD_ETSI, RTW_REGD_ETSI), COUNTRY_REGD_ENT("ZW", RTW_REGD_ETSI, RTW_REGD_ETSI), }; static void rtw_regd_apply_hw_cap_flags(struct wiphy *wiphy) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct ieee80211_supported_band *sband; struct ieee80211_channel *ch; struct rtw_dev *rtwdev = hw->priv; struct rtw_efuse *efuse = &rtwdev->efuse; int i; if (efuse->hw_cap.bw & BIT(RTW_CHANNEL_WIDTH_80)) return; sband = wiphy->bands[NL80211_BAND_2GHZ]; if (!sband) goto out_5g; for (i = 0; i < sband->n_channels; i++) { ch = &sband->channels[i]; ch->flags |= IEEE80211_CHAN_NO_80MHZ; } out_5g: sband = wiphy->bands[NL80211_BAND_5GHZ]; if (!sband) return; for (i = 0; i < sband->n_channels; i++) { ch = &sband->channels[i]; ch->flags |= IEEE80211_CHAN_NO_80MHZ; } } static bool rtw_reg_is_ww(const struct rtw_regulatory *reg) { return reg == &rtw_reg_ww; } static bool rtw_reg_match(const struct rtw_regulatory *reg, const char *alpha2) { return memcmp(reg->alpha2, alpha2, 2) == 0; } static const struct rtw_regulatory *rtw_reg_find_by_name(const char *alpha2) { unsigned int i; for (i = 0; i < ARRAY_SIZE(rtw_reg_map); i++) { if (rtw_reg_match(&rtw_reg_map[i], alpha2)) return &rtw_reg_map[i]; } return &rtw_reg_ww; } static void rtw_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request); /* call this before ieee80211_register_hw() */ int rtw_regd_init(struct rtw_dev *rtwdev) { struct wiphy *wiphy = rtwdev->hw->wiphy; const struct rtw_regulatory *chip_reg; if (!wiphy) return -EINVAL; wiphy->reg_notifier = rtw_regd_notifier; chip_reg = rtw_reg_find_by_name(rtwdev->efuse.country_code); if (!rtw_reg_is_ww(chip_reg)) { rtwdev->regd.state = RTW_REGD_STATE_PROGRAMMED; /* Set REGULATORY_STRICT_REG before ieee80211_register_hw(), * stack will wait for regulatory_hint() and consider it * as the superset for our regulatory rule. */ wiphy->regulatory_flags |= REGULATORY_STRICT_REG; wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE; } else { rtwdev->regd.state = RTW_REGD_STATE_WORLDWIDE; } rtwdev->regd.regulatory = &rtw_reg_ww; rtwdev->regd.dfs_region = NL80211_DFS_UNSET; rtw_dbg_regd_dump(rtwdev, "regd init state %d: ", rtwdev->regd.state); rtw_regd_apply_hw_cap_flags(wiphy); return 0; } /* call this after ieee80211_register_hw() */ int rtw_regd_hint(struct rtw_dev *rtwdev) { struct wiphy *wiphy = rtwdev->hw->wiphy; int ret; if (!wiphy) return -EINVAL; if (rtwdev->regd.state == RTW_REGD_STATE_PROGRAMMED) { rtw_dbg(rtwdev, RTW_DBG_REGD, "country domain %c%c is PGed on efuse", rtwdev->efuse.country_code[0], rtwdev->efuse.country_code[1]); ret = regulatory_hint(wiphy, rtwdev->efuse.country_code); if (ret) { rtw_warn(rtwdev, "failed to hint regulatory: %d\n", ret); return ret; } } return 0; } static bool rtw_regd_mgmt_worldwide(struct rtw_dev *rtwdev, struct rtw_regd *next_regd, struct regulatory_request *request) { struct wiphy *wiphy = rtwdev->hw->wiphy; next_regd->state = RTW_REGD_STATE_WORLDWIDE; if (request->initiator == NL80211_REGDOM_SET_BY_USER && !rtw_reg_is_ww(next_regd->regulatory)) { next_regd->state = RTW_REGD_STATE_SETTING; wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE; } return true; } static bool rtw_regd_mgmt_programmed(struct rtw_dev *rtwdev, struct rtw_regd *next_regd, struct regulatory_request *request) { if (request->initiator == NL80211_REGDOM_SET_BY_DRIVER && rtw_reg_match(next_regd->regulatory, rtwdev->efuse.country_code)) { next_regd->state = RTW_REGD_STATE_PROGRAMMED; return true; } return false; } static bool rtw_regd_mgmt_setting(struct rtw_dev *rtwdev, struct rtw_regd *next_regd, struct regulatory_request *request) { struct wiphy *wiphy = rtwdev->hw->wiphy; if (request->initiator != NL80211_REGDOM_SET_BY_USER) return false; next_regd->state = RTW_REGD_STATE_SETTING; if (rtw_reg_is_ww(next_regd->regulatory)) { next_regd->state = RTW_REGD_STATE_WORLDWIDE; wiphy->regulatory_flags &= ~REGULATORY_COUNTRY_IE_IGNORE; } return true; } static bool (*const rtw_regd_handler[RTW_REGD_STATE_NR]) (struct rtw_dev *, struct rtw_regd *, struct regulatory_request *) = { [RTW_REGD_STATE_WORLDWIDE] = rtw_regd_mgmt_worldwide, [RTW_REGD_STATE_PROGRAMMED] = rtw_regd_mgmt_programmed, [RTW_REGD_STATE_SETTING] = rtw_regd_mgmt_setting, }; static bool rtw_regd_state_hdl(struct rtw_dev *rtwdev, struct rtw_regd *next_regd, struct regulatory_request *request) { next_regd->regulatory = rtw_reg_find_by_name(request->alpha2); next_regd->dfs_region = request->dfs_region; return rtw_regd_handler[rtwdev->regd.state](rtwdev, next_regd, request); } static void rtw_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct rtw_dev *rtwdev = hw->priv; struct rtw_hal *hal = &rtwdev->hal; struct rtw_regd next_regd = {0}; bool hdl; hdl = rtw_regd_state_hdl(rtwdev, &next_regd, request); if (!hdl) { rtw_dbg(rtwdev, RTW_DBG_REGD, "regd state %d: ignore request %c%c of initiator %d\n", rtwdev->regd.state, request->alpha2[0], request->alpha2[1], request->initiator); return; } rtw_dbg(rtwdev, RTW_DBG_REGD, "regd state: %d -> %d\n", rtwdev->regd.state, next_regd.state); + mutex_lock(&rtwdev->mutex); rtwdev->regd = next_regd; rtw_dbg_regd_dump(rtwdev, "get alpha2 %c%c from initiator %d: ", request->alpha2[0], request->alpha2[1], request->initiator); rtw_phy_adaptivity_set_mode(rtwdev); rtw_phy_set_tx_power_level(rtwdev, hal->current_channel); + mutex_unlock(&rtwdev->mutex); } u8 rtw_regd_get(struct rtw_dev *rtwdev) { struct rtw_hal *hal = &rtwdev->hal; u8 band = hal->current_band_type; return band == RTW_BAND_2G ? rtwdev->regd.regulatory->txpwr_regd_2g : rtwdev->regd.regulatory->txpwr_regd_5g; } EXPORT_SYMBOL(rtw_regd_get); struct rtw_regd_alternative_t { bool set; u8 alt; }; #define DECL_REGD_ALT(_regd, _regd_alt) \ [(_regd)] = {.set = true, .alt = (_regd_alt)} static const struct rtw_regd_alternative_t rtw_regd_alt[RTW_REGD_MAX] = { DECL_REGD_ALT(RTW_REGD_IC, RTW_REGD_FCC), DECL_REGD_ALT(RTW_REGD_KCC, RTW_REGD_ETSI), DECL_REGD_ALT(RTW_REGD_ACMA, RTW_REGD_ETSI), DECL_REGD_ALT(RTW_REGD_CHILE, RTW_REGD_FCC), DECL_REGD_ALT(RTW_REGD_UKRAINE, RTW_REGD_ETSI), DECL_REGD_ALT(RTW_REGD_MEXICO, RTW_REGD_FCC), DECL_REGD_ALT(RTW_REGD_CN, RTW_REGD_ETSI), }; bool rtw_regd_has_alt(u8 regd, u8 *regd_alt) { if (!rtw_regd_alt[regd].set) return false; *regd_alt = rtw_regd_alt[regd].alt; return true; } diff --git a/sys/contrib/dev/rtw88/rtw8723d.c b/sys/contrib/dev/rtw88/rtw8723d.c index 993bd6b1d723..c575476a0020 100644 --- a/sys/contrib/dev/rtw88/rtw8723d.c +++ b/sys/contrib/dev/rtw88/rtw8723d.c @@ -1,2790 +1,2831 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* Copyright(c) 2018-2019 Realtek Corporation */ #include #include "main.h" #include "coex.h" #include "fw.h" #include "tx.h" #include "rx.h" #include "phy.h" #include "rtw8723d.h" #include "rtw8723d_table.h" #include "mac.h" #include "reg.h" #include "debug.h" static const struct rtw_hw_reg rtw8723d_txagc[] = { [DESC_RATE1M] = { .addr = 0xe08, .mask = 0x0000ff00 }, [DESC_RATE2M] = { .addr = 0x86c, .mask = 0x0000ff00 }, [DESC_RATE5_5M] = { .addr = 0x86c, .mask = 0x00ff0000 }, [DESC_RATE11M] = { .addr = 0x86c, .mask = 0xff000000 }, [DESC_RATE6M] = { .addr = 0xe00, .mask = 0x000000ff }, [DESC_RATE9M] = { .addr = 0xe00, .mask = 0x0000ff00 }, [DESC_RATE12M] = { .addr = 0xe00, .mask = 0x00ff0000 }, [DESC_RATE18M] = { .addr = 0xe00, .mask = 0xff000000 }, [DESC_RATE24M] = { .addr = 0xe04, .mask = 0x000000ff }, [DESC_RATE36M] = { .addr = 0xe04, .mask = 0x0000ff00 }, [DESC_RATE48M] = { .addr = 0xe04, .mask = 0x00ff0000 }, [DESC_RATE54M] = { .addr = 0xe04, .mask = 0xff000000 }, [DESC_RATEMCS0] = { .addr = 0xe10, .mask = 0x000000ff }, [DESC_RATEMCS1] = { .addr = 0xe10, .mask = 0x0000ff00 }, [DESC_RATEMCS2] = { .addr = 0xe10, .mask = 0x00ff0000 }, [DESC_RATEMCS3] = { .addr = 0xe10, .mask = 0xff000000 }, [DESC_RATEMCS4] = { .addr = 0xe14, .mask = 0x000000ff }, [DESC_RATEMCS5] = { .addr = 0xe14, .mask = 0x0000ff00 }, [DESC_RATEMCS6] = { .addr = 0xe14, .mask = 0x00ff0000 }, [DESC_RATEMCS7] = { .addr = 0xe14, .mask = 0xff000000 }, }; #define WLAN_TXQ_RPT_EN 0x1F #define WLAN_SLOT_TIME 0x09 #define WLAN_RL_VAL 0x3030 #define WLAN_BAR_VAL 0x0201ffff #define BIT_MASK_TBTT_HOLD 0x00000fff #define BIT_SHIFT_TBTT_HOLD 8 #define BIT_MASK_TBTT_SETUP 0x000000ff #define BIT_SHIFT_TBTT_SETUP 0 #define BIT_MASK_TBTT_MASK ((BIT_MASK_TBTT_HOLD << BIT_SHIFT_TBTT_HOLD) | \ (BIT_MASK_TBTT_SETUP << BIT_SHIFT_TBTT_SETUP)) #define TBTT_TIME(s, h)((((s) & BIT_MASK_TBTT_SETUP) << BIT_SHIFT_TBTT_SETUP) |\ (((h) & BIT_MASK_TBTT_HOLD) << BIT_SHIFT_TBTT_HOLD)) #define WLAN_TBTT_TIME_NORMAL TBTT_TIME(0x04, 0x80) #define WLAN_TBTT_TIME_STOP_BCN TBTT_TIME(0x04, 0x64) #define WLAN_PIFS_VAL 0 #define WLAN_AGG_BRK_TIME 0x16 #define WLAN_NAV_PROT_LEN 0x0040 #define WLAN_SPEC_SIFS 0x100a #define WLAN_RX_PKT_LIMIT 0x17 #define WLAN_MAX_AGG_NR 0x0A #define WLAN_AMPDU_MAX_TIME 0x1C #define WLAN_ANT_SEL 0x82 #define WLAN_LTR_IDLE_LAT 0x90039003 #define WLAN_LTR_ACT_LAT 0x883c883c #define WLAN_LTR_CTRL1 0xCB004010 #define WLAN_LTR_CTRL2 0x01233425 static void rtw8723d_lck(struct rtw_dev *rtwdev) { u32 lc_cal; u8 val_ctx, rf_val; int ret; val_ctx = rtw_read8(rtwdev, REG_CTX); if ((val_ctx & BIT_MASK_CTX_TYPE) != 0) rtw_write8(rtwdev, REG_CTX, val_ctx & ~BIT_MASK_CTX_TYPE); else rtw_write8(rtwdev, REG_TXPAUSE, 0xFF); lc_cal = rtw_read_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK); rtw_write_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK, lc_cal | BIT_LCK); ret = read_poll_timeout(rtw_read_rf, rf_val, rf_val != 0x1, 10000, 1000000, false, rtwdev, RF_PATH_A, RF_CFGCH, BIT_LCK); if (ret) rtw_warn(rtwdev, "failed to poll LCK status bit\n"); rtw_write_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK, lc_cal); if ((val_ctx & BIT_MASK_CTX_TYPE) != 0) rtw_write8(rtwdev, REG_CTX, val_ctx); else rtw_write8(rtwdev, REG_TXPAUSE, 0x00); } static const u32 rtw8723d_ofdm_swing_table[] = { 0x0b40002d, 0x0c000030, 0x0cc00033, 0x0d800036, 0x0e400039, 0x0f00003c, 0x10000040, 0x11000044, 0x12000048, 0x1300004c, 0x14400051, 0x15800056, 0x16c0005b, 0x18000060, 0x19800066, 0x1b00006c, 0x1c800072, 0x1e400079, 0x20000080, 0x22000088, 0x24000090, 0x26000098, 0x288000a2, 0x2ac000ab, 0x2d4000b5, 0x300000c0, 0x32c000cb, 0x35c000d7, 0x390000e4, 0x3c8000f2, 0x40000100, 0x43c0010f, 0x47c0011f, 0x4c000130, 0x50800142, 0x55400155, 0x5a400169, 0x5fc0017f, 0x65400195, 0x6b8001ae, 0x71c001c7, 0x788001e2, 0x7f8001fe, }; static const u32 rtw8723d_cck_swing_table[] = { 0x0CD, 0x0D9, 0x0E6, 0x0F3, 0x102, 0x111, 0x121, 0x132, 0x144, 0x158, 0x16C, 0x182, 0x198, 0x1B1, 0x1CA, 0x1E5, 0x202, 0x221, 0x241, 0x263, 0x287, 0x2AE, 0x2D6, 0x301, 0x32F, 0x35F, 0x392, 0x3C9, 0x402, 0x43F, 0x47F, 0x4C3, 0x50C, 0x558, 0x5A9, 0x5FF, 0x65A, 0x6BA, 0x720, 0x78C, 0x7FF, }; #define RTW_OFDM_SWING_TABLE_SIZE ARRAY_SIZE(rtw8723d_ofdm_swing_table) #define RTW_CCK_SWING_TABLE_SIZE ARRAY_SIZE(rtw8723d_cck_swing_table) static void rtw8723d_pwrtrack_init(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 path; dm_info->default_ofdm_index = RTW_DEF_OFDM_SWING_INDEX; for (path = RF_PATH_A; path < rtwdev->hal.rf_path_num; path++) { ewma_thermal_init(&dm_info->avg_thermal[path]); dm_info->delta_power_index[path] = 0; } dm_info->pwr_trk_triggered = false; dm_info->pwr_trk_init_trigger = true; dm_info->thermal_meter_k = rtwdev->efuse.thermal_meter_k; dm_info->txagc_remnant_cck = 0; dm_info->txagc_remnant_ofdm = 0; } static void rtw8723d_phy_set_param(struct rtw_dev *rtwdev) { u8 xtal_cap; u32 val32; /* power on BB/RF domain */ rtw_write16_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_EN_25_1 | BIT_FEN_BB_GLB_RST | BIT_FEN_BB_RSTB); rtw_write8_set(rtwdev, REG_RF_CTRL, BIT_RF_EN | BIT_RF_RSTB | BIT_RF_SDM_RSTB); rtw_write8(rtwdev, REG_AFE_CTRL1 + 1, 0x80); rtw_phy_load_tables(rtwdev); /* post init after header files config */ rtw_write32_clr(rtwdev, REG_RCR, BIT_RCR_ADF); rtw_write8_set(rtwdev, REG_HIQ_NO_LMT_EN, BIT_HIQ_NO_LMT_EN_ROOT); rtw_write16_set(rtwdev, REG_AFE_CTRL_4, BIT_CK320M_AFE_EN | BIT_EN_SYN); xtal_cap = rtwdev->efuse.crystal_cap & 0x3F; rtw_write32_mask(rtwdev, REG_AFE_CTRL3, BIT_MASK_XTAL, xtal_cap | (xtal_cap << 6)); rtw_write32_set(rtwdev, REG_FPGA0_RFMOD, BIT_CCKEN | BIT_OFDMEN); if ((rtwdev->efuse.afe >> 4) == 14) { rtw_write32_set(rtwdev, REG_AFE_CTRL3, BIT_XTAL_GMP_BIT4); rtw_write32_clr(rtwdev, REG_AFE_CTRL1, BITS_PLL); rtw_write32_set(rtwdev, REG_LDO_SWR_CTRL, BIT_XTA1); rtw_write32_clr(rtwdev, REG_LDO_SWR_CTRL, BIT_XTA0); } rtw_write8(rtwdev, REG_SLOT, WLAN_SLOT_TIME); rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL + 1, WLAN_TXQ_RPT_EN); rtw_write16(rtwdev, REG_RETRY_LIMIT, WLAN_RL_VAL); rtw_write32(rtwdev, REG_BAR_MODE_CTRL, WLAN_BAR_VAL); rtw_write8(rtwdev, REG_ATIMWND, 0x2); rtw_write8(rtwdev, REG_BCN_CTRL, BIT_DIS_TSF_UDT | BIT_EN_BCN_FUNCTION | BIT_EN_TXBCN_RPT); val32 = rtw_read32(rtwdev, REG_TBTT_PROHIBIT); val32 &= ~BIT_MASK_TBTT_MASK; val32 |= WLAN_TBTT_TIME_STOP_BCN; rtw_write8(rtwdev, REG_TBTT_PROHIBIT, val32); rtw_write8(rtwdev, REG_PIFS, WLAN_PIFS_VAL); rtw_write8(rtwdev, REG_AGGR_BREAK_TIME, WLAN_AGG_BRK_TIME); rtw_write16(rtwdev, REG_NAV_PROT_LEN, WLAN_NAV_PROT_LEN); rtw_write16(rtwdev, REG_MAC_SPEC_SIFS, WLAN_SPEC_SIFS); rtw_write16(rtwdev, REG_SIFS, WLAN_SPEC_SIFS); rtw_write16(rtwdev, REG_SIFS + 2, WLAN_SPEC_SIFS); rtw_write8(rtwdev, REG_SINGLE_AMPDU_CTRL, BIT_EN_SINGLE_APMDU); rtw_write8(rtwdev, REG_RX_PKT_LIMIT, WLAN_RX_PKT_LIMIT); rtw_write8(rtwdev, REG_MAX_AGGR_NUM, WLAN_MAX_AGG_NR); rtw_write8(rtwdev, REG_AMPDU_MAX_TIME, WLAN_AMPDU_MAX_TIME); rtw_write8(rtwdev, REG_LEDCFG2, WLAN_ANT_SEL); rtw_write32(rtwdev, REG_LTR_IDLE_LATENCY, WLAN_LTR_IDLE_LAT); rtw_write32(rtwdev, REG_LTR_ACTIVE_LATENCY, WLAN_LTR_ACT_LAT); rtw_write32(rtwdev, REG_LTR_CTRL_BASIC, WLAN_LTR_CTRL1); rtw_write32(rtwdev, REG_LTR_CTRL_BASIC + 4, WLAN_LTR_CTRL2); rtw_phy_init(rtwdev); rtwdev->dm_info.cck_pd_default = rtw_read8(rtwdev, REG_CSRATIO) & 0x1f; rtw_write16_set(rtwdev, REG_TXDMA_OFFSET_CHK, BIT_DROP_DATA_EN); rtw8723d_lck(rtwdev); rtw_write32_mask(rtwdev, REG_OFDM0_XAAGC1, MASKBYTE0, 0x50); rtw_write32_mask(rtwdev, REG_OFDM0_XAAGC1, MASKBYTE0, 0x20); rtw8723d_pwrtrack_init(rtwdev); } static void rtw8723de_efuse_parsing(struct rtw_efuse *efuse, struct rtw8723d_efuse *map) { ether_addr_copy(efuse->addr, map->e.mac_addr); } +static void rtw8723du_efuse_parsing(struct rtw_efuse *efuse, + struct rtw8723d_efuse *map) +{ + ether_addr_copy(efuse->addr, map->u.mac_addr); +} + +static void rtw8723ds_efuse_parsing(struct rtw_efuse *efuse, + struct rtw8723d_efuse *map) +{ + ether_addr_copy(efuse->addr, map->s.mac_addr); +} + static int rtw8723d_read_efuse(struct rtw_dev *rtwdev, u8 *log_map) { struct rtw_efuse *efuse = &rtwdev->efuse; struct rtw8723d_efuse *map; int i; map = (struct rtw8723d_efuse *)log_map; efuse->rfe_option = 0; efuse->rf_board_option = map->rf_board_option; efuse->crystal_cap = map->xtal_k; efuse->pa_type_2g = map->pa_type; efuse->lna_type_2g = map->lna_type_2g[0]; efuse->channel_plan = map->channel_plan; efuse->country_code[0] = map->country_code[0]; efuse->country_code[1] = map->country_code[1]; efuse->bt_setting = map->rf_bt_setting; efuse->regd = map->rf_board_option & 0x7; efuse->thermal_meter[0] = map->thermal_meter; efuse->thermal_meter_k = map->thermal_meter; efuse->afe = map->afe; for (i = 0; i < 4; i++) efuse->txpwr_idx_table[i] = map->txpwr_idx_table[i]; switch (rtw_hci_type(rtwdev)) { case RTW_HCI_TYPE_PCIE: rtw8723de_efuse_parsing(efuse, map); break; + case RTW_HCI_TYPE_USB: + rtw8723du_efuse_parsing(efuse, map); + break; + case RTW_HCI_TYPE_SDIO: + rtw8723ds_efuse_parsing(efuse, map); + break; default: /* unsupported now */ return -ENOTSUPP; } return 0; } static void query_phy_status_page0(struct rtw_dev *rtwdev, u8 *phy_status, struct rtw_rx_pkt_stat *pkt_stat) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; s8 min_rx_power = -120; u8 pwdb = GET_PHY_STAT_P0_PWDB(phy_status); pkt_stat->rx_power[RF_PATH_A] = pwdb - 97; pkt_stat->rssi = rtw_phy_rf_power_2_rssi(pkt_stat->rx_power, 1); pkt_stat->bw = RTW_CHANNEL_WIDTH_20; pkt_stat->signal_power = max(pkt_stat->rx_power[RF_PATH_A], min_rx_power); dm_info->rssi[RF_PATH_A] = pkt_stat->rssi; } static void query_phy_status_page1(struct rtw_dev *rtwdev, u8 *phy_status, struct rtw_rx_pkt_stat *pkt_stat) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 rxsc, bw; s8 min_rx_power = -120; s8 rx_evm; if (pkt_stat->rate > DESC_RATE11M && pkt_stat->rate < DESC_RATEMCS0) rxsc = GET_PHY_STAT_P1_L_RXSC(phy_status); else rxsc = GET_PHY_STAT_P1_HT_RXSC(phy_status); if (GET_PHY_STAT_P1_RF_MODE(phy_status) == 0) bw = RTW_CHANNEL_WIDTH_20; else if ((rxsc == 1) || (rxsc == 2)) bw = RTW_CHANNEL_WIDTH_20; else bw = RTW_CHANNEL_WIDTH_40; pkt_stat->rx_power[RF_PATH_A] = GET_PHY_STAT_P1_PWDB_A(phy_status) - 110; pkt_stat->rssi = rtw_phy_rf_power_2_rssi(pkt_stat->rx_power, 1); pkt_stat->bw = bw; pkt_stat->signal_power = max(pkt_stat->rx_power[RF_PATH_A], min_rx_power); pkt_stat->rx_evm[RF_PATH_A] = GET_PHY_STAT_P1_RXEVM_A(phy_status); pkt_stat->rx_snr[RF_PATH_A] = GET_PHY_STAT_P1_RXSNR_A(phy_status); pkt_stat->cfo_tail[RF_PATH_A] = GET_PHY_STAT_P1_CFO_TAIL_A(phy_status); dm_info->curr_rx_rate = pkt_stat->rate; dm_info->rssi[RF_PATH_A] = pkt_stat->rssi; dm_info->rx_snr[RF_PATH_A] = pkt_stat->rx_snr[RF_PATH_A] >> 1; dm_info->cfo_tail[RF_PATH_A] = (pkt_stat->cfo_tail[RF_PATH_A] * 5) >> 1; rx_evm = clamp_t(s8, -pkt_stat->rx_evm[RF_PATH_A] >> 1, 0, 64); rx_evm &= 0x3F; /* 64->0: second path of 1SS rate is 64 */ dm_info->rx_evm_dbm[RF_PATH_A] = rx_evm; } static void query_phy_status(struct rtw_dev *rtwdev, u8 *phy_status, struct rtw_rx_pkt_stat *pkt_stat) { u8 page; page = *phy_status & 0xf; switch (page) { case 0: query_phy_status_page0(rtwdev, phy_status, pkt_stat); break; case 1: query_phy_status_page1(rtwdev, phy_status, pkt_stat); break; default: rtw_warn(rtwdev, "unused phy status page (%d)\n", page); return; } } static void rtw8723d_query_rx_desc(struct rtw_dev *rtwdev, u8 *rx_desc, struct rtw_rx_pkt_stat *pkt_stat, struct ieee80211_rx_status *rx_status) { struct ieee80211_hdr *hdr; u32 desc_sz = rtwdev->chip->rx_pkt_desc_sz; u8 *phy_status = NULL; memset(pkt_stat, 0, sizeof(*pkt_stat)); pkt_stat->phy_status = GET_RX_DESC_PHYST(rx_desc); pkt_stat->icv_err = GET_RX_DESC_ICV_ERR(rx_desc); pkt_stat->crc_err = GET_RX_DESC_CRC32(rx_desc); pkt_stat->decrypted = !GET_RX_DESC_SWDEC(rx_desc) && GET_RX_DESC_ENC_TYPE(rx_desc) != RX_DESC_ENC_NONE; pkt_stat->is_c2h = GET_RX_DESC_C2H(rx_desc); pkt_stat->pkt_len = GET_RX_DESC_PKT_LEN(rx_desc); pkt_stat->drv_info_sz = GET_RX_DESC_DRV_INFO_SIZE(rx_desc); pkt_stat->shift = GET_RX_DESC_SHIFT(rx_desc); pkt_stat->rate = GET_RX_DESC_RX_RATE(rx_desc); pkt_stat->cam_id = GET_RX_DESC_MACID(rx_desc); pkt_stat->ppdu_cnt = 0; pkt_stat->tsf_low = GET_RX_DESC_TSFL(rx_desc); /* drv_info_sz is in unit of 8-bytes */ pkt_stat->drv_info_sz *= 8; /* c2h cmd pkt's rx/phy status is not interested */ if (pkt_stat->is_c2h) return; hdr = (struct ieee80211_hdr *)(rx_desc + desc_sz + pkt_stat->shift + pkt_stat->drv_info_sz); if (pkt_stat->phy_status) { phy_status = rx_desc + desc_sz + pkt_stat->shift; query_phy_status(rtwdev, phy_status, pkt_stat); } rtw_rx_fill_rx_status(rtwdev, pkt_stat, hdr, rx_status, phy_status); } static bool rtw8723d_check_spur_ov_thres(struct rtw_dev *rtwdev, u8 channel, u32 thres) { u32 freq; bool ret = false; if (channel == 13) freq = FREQ_CH13; else if (channel == 14) freq = FREQ_CH14; else return false; rtw_write32(rtwdev, REG_ANALOG_P4, DIS_3WIRE); rtw_write32(rtwdev, REG_PSDFN, freq); rtw_write32(rtwdev, REG_PSDFN, START_PSD | freq); msleep(30); if (rtw_read32(rtwdev, REG_PSDRPT) >= thres) ret = true; rtw_write32(rtwdev, REG_PSDFN, freq); rtw_write32(rtwdev, REG_ANALOG_P4, EN_3WIRE); return ret; } static void rtw8723d_cfg_notch(struct rtw_dev *rtwdev, u8 channel, bool notch) { if (!notch) { rtw_write32_mask(rtwdev, REG_OFDM0_RXDSP, BIT_MASK_RXDSP, 0x1f); rtw_write32_mask(rtwdev, REG_OFDM0_RXDSP, BIT_EN_RXDSP, 0x0); rtw_write32(rtwdev, REG_OFDM1_CSI1, 0x00000000); rtw_write32(rtwdev, REG_OFDM1_CSI2, 0x00000000); rtw_write32(rtwdev, REG_OFDM1_CSI3, 0x00000000); rtw_write32(rtwdev, REG_OFDM1_CSI4, 0x00000000); rtw_write32_mask(rtwdev, REG_OFDM1_CFOTRK, BIT_EN_CFOTRK, 0x0); return; } switch (channel) { case 13: rtw_write32_mask(rtwdev, REG_OFDM0_RXDSP, BIT_MASK_RXDSP, 0xb); rtw_write32_mask(rtwdev, REG_OFDM0_RXDSP, BIT_EN_RXDSP, 0x1); rtw_write32(rtwdev, REG_OFDM1_CSI1, 0x04000000); rtw_write32(rtwdev, REG_OFDM1_CSI2, 0x00000000); rtw_write32(rtwdev, REG_OFDM1_CSI3, 0x00000000); rtw_write32(rtwdev, REG_OFDM1_CSI4, 0x00000000); rtw_write32_mask(rtwdev, REG_OFDM1_CFOTRK, BIT_EN_CFOTRK, 0x1); break; case 14: rtw_write32_mask(rtwdev, REG_OFDM0_RXDSP, BIT_MASK_RXDSP, 0x5); rtw_write32_mask(rtwdev, REG_OFDM0_RXDSP, BIT_EN_RXDSP, 0x1); rtw_write32(rtwdev, REG_OFDM1_CSI1, 0x00000000); rtw_write32(rtwdev, REG_OFDM1_CSI2, 0x00000000); rtw_write32(rtwdev, REG_OFDM1_CSI3, 0x00000000); rtw_write32(rtwdev, REG_OFDM1_CSI4, 0x00080000); rtw_write32_mask(rtwdev, REG_OFDM1_CFOTRK, BIT_EN_CFOTRK, 0x1); break; default: rtw_write32_mask(rtwdev, REG_OFDM0_RXDSP, BIT_EN_RXDSP, 0x0); rtw_write32_mask(rtwdev, REG_OFDM1_CFOTRK, BIT_EN_CFOTRK, 0x0); break; } } static void rtw8723d_spur_cal(struct rtw_dev *rtwdev, u8 channel) { bool notch; if (channel < 13) { rtw8723d_cfg_notch(rtwdev, channel, false); return; } notch = rtw8723d_check_spur_ov_thres(rtwdev, channel, SPUR_THRES); rtw8723d_cfg_notch(rtwdev, channel, notch); } static void rtw8723d_set_channel_rf(struct rtw_dev *rtwdev, u8 channel, u8 bw) { u32 rf_cfgch_a, rf_cfgch_b; rf_cfgch_a = rtw_read_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK); rf_cfgch_b = rtw_read_rf(rtwdev, RF_PATH_B, RF_CFGCH, RFREG_MASK); rf_cfgch_a &= ~RFCFGCH_CHANNEL_MASK; rf_cfgch_b &= ~RFCFGCH_CHANNEL_MASK; rf_cfgch_a |= (channel & RFCFGCH_CHANNEL_MASK); rf_cfgch_b |= (channel & RFCFGCH_CHANNEL_MASK); rf_cfgch_a &= ~RFCFGCH_BW_MASK; switch (bw) { case RTW_CHANNEL_WIDTH_20: rf_cfgch_a |= RFCFGCH_BW_20M; break; case RTW_CHANNEL_WIDTH_40: rf_cfgch_a |= RFCFGCH_BW_40M; break; default: break; } rtw_write_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK, rf_cfgch_a); rtw_write_rf(rtwdev, RF_PATH_B, RF_CFGCH, RFREG_MASK, rf_cfgch_b); rtw8723d_spur_cal(rtwdev, channel); } static const struct rtw_backup_info cck_dfir_cfg[][CCK_DFIR_NR] = { [0] = { { .len = 4, .reg = 0xA24, .val = 0x64B80C1C }, { .len = 4, .reg = 0xA28, .val = 0x00008810 }, { .len = 4, .reg = 0xAAC, .val = 0x01235667 }, }, [1] = { { .len = 4, .reg = 0xA24, .val = 0x0000B81C }, { .len = 4, .reg = 0xA28, .val = 0x00000000 }, { .len = 4, .reg = 0xAAC, .val = 0x00003667 }, }, }; static void rtw8723d_set_channel_bb(struct rtw_dev *rtwdev, u8 channel, u8 bw, u8 primary_ch_idx) { const struct rtw_backup_info *cck_dfir; int i; cck_dfir = channel <= 13 ? cck_dfir_cfg[0] : cck_dfir_cfg[1]; for (i = 0; i < CCK_DFIR_NR; i++, cck_dfir++) rtw_write32(rtwdev, cck_dfir->reg, cck_dfir->val); switch (bw) { case RTW_CHANNEL_WIDTH_20: rtw_write32_mask(rtwdev, REG_FPGA0_RFMOD, BIT_MASK_RFMOD, 0x0); rtw_write32_mask(rtwdev, REG_FPGA1_RFMOD, BIT_MASK_RFMOD, 0x0); rtw_write32_mask(rtwdev, REG_BBRX_DFIR, BIT_RXBB_DFIR_EN, 1); rtw_write32_mask(rtwdev, REG_BBRX_DFIR, BIT_MASK_RXBB_DFIR, 0xa); break; case RTW_CHANNEL_WIDTH_40: rtw_write32_mask(rtwdev, REG_FPGA0_RFMOD, BIT_MASK_RFMOD, 0x1); rtw_write32_mask(rtwdev, REG_FPGA1_RFMOD, BIT_MASK_RFMOD, 0x1); rtw_write32_mask(rtwdev, REG_BBRX_DFIR, BIT_RXBB_DFIR_EN, 0); rtw_write32_mask(rtwdev, REG_CCK0_SYS, BIT_CCK_SIDE_BAND, (primary_ch_idx == RTW_SC_20_UPPER ? 1 : 0)); break; default: break; } } static void rtw8723d_set_channel(struct rtw_dev *rtwdev, u8 channel, u8 bw, u8 primary_chan_idx) { rtw8723d_set_channel_rf(rtwdev, channel, bw); rtw_set_channel_mac(rtwdev, channel, bw, primary_chan_idx); rtw8723d_set_channel_bb(rtwdev, channel, bw, primary_chan_idx); } #define BIT_CFENDFORM BIT(9) #define BIT_WMAC_TCR_ERR0 BIT(12) #define BIT_WMAC_TCR_ERR1 BIT(13) #define BIT_TCR_CFG (BIT_CFENDFORM | BIT_WMAC_TCR_ERR0 | \ BIT_WMAC_TCR_ERR1) #define WLAN_RX_FILTER0 0xFFFF #define WLAN_RX_FILTER1 0x400 #define WLAN_RX_FILTER2 0xFFFF #define WLAN_RCR_CFG 0x700060CE static int rtw8723d_mac_init(struct rtw_dev *rtwdev) { rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL + 1, WLAN_TXQ_RPT_EN); rtw_write32(rtwdev, REG_TCR, BIT_TCR_CFG); rtw_write16(rtwdev, REG_RXFLTMAP0, WLAN_RX_FILTER0); rtw_write16(rtwdev, REG_RXFLTMAP1, WLAN_RX_FILTER1); rtw_write16(rtwdev, REG_RXFLTMAP2, WLAN_RX_FILTER2); rtw_write32(rtwdev, REG_RCR, WLAN_RCR_CFG); rtw_write32(rtwdev, REG_INT_MIG, 0); rtw_write32(rtwdev, REG_MCUTST_1, 0x0); rtw_write8(rtwdev, REG_MISC_CTRL, BIT_DIS_SECOND_CCA); rtw_write8(rtwdev, REG_2ND_CCA_CTRL, 0); return 0; } static void rtw8723d_shutdown(struct rtw_dev *rtwdev) { rtw_write16_set(rtwdev, REG_HCI_OPT_CTRL, BIT_USB_SUS_DIS); } static void rtw8723d_cfg_ldo25(struct rtw_dev *rtwdev, bool enable) { u8 ldo_pwr; ldo_pwr = rtw_read8(rtwdev, REG_LDO_EFUSE_CTRL + 3); if (enable) { ldo_pwr &= ~BIT_MASK_LDO25_VOLTAGE; ldo_pwr |= (BIT_LDO25_VOLTAGE_V25 << 4) | BIT_LDO25_EN; } else { ldo_pwr &= ~BIT_LDO25_EN; } rtw_write8(rtwdev, REG_LDO_EFUSE_CTRL + 3, ldo_pwr); } static void rtw8723d_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, u8 rs) { struct rtw_hal *hal = &rtwdev->hal; const struct rtw_hw_reg *txagc; u8 rate, pwr_index; int j; for (j = 0; j < rtw_rate_size[rs]; j++) { rate = rtw_rate_section[rs][j]; pwr_index = hal->tx_pwr_tbl[path][rate]; if (rate >= ARRAY_SIZE(rtw8723d_txagc)) { rtw_warn(rtwdev, "rate 0x%x isn't supported\n", rate); continue; } txagc = &rtw8723d_txagc[rate]; if (!txagc->addr) { rtw_warn(rtwdev, "rate 0x%x isn't defined\n", rate); continue; } rtw_write32_mask(rtwdev, txagc->addr, txagc->mask, pwr_index); } } static void rtw8723d_set_tx_power_index(struct rtw_dev *rtwdev) { struct rtw_hal *hal = &rtwdev->hal; int rs, path; for (path = 0; path < hal->rf_path_num; path++) { for (rs = 0; rs <= RTW_RATE_SECTION_HT_1S; rs++) rtw8723d_set_tx_power_index_by_rate(rtwdev, path, rs); } } static void rtw8723d_efuse_grant(struct rtw_dev *rtwdev, bool on) { if (on) { rtw_write8(rtwdev, REG_EFUSE_ACCESS, EFUSE_ACCESS_ON); rtw_write16_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_ELDR); rtw_write16_set(rtwdev, REG_SYS_CLKR, BIT_LOADER_CLK_EN | BIT_ANA8M); } else { rtw_write8(rtwdev, REG_EFUSE_ACCESS, EFUSE_ACCESS_OFF); } } static void rtw8723d_false_alarm_statistics(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u32 cck_fa_cnt; u32 ofdm_fa_cnt; u32 crc32_cnt; u32 val32; /* hold counter */ rtw_write32_mask(rtwdev, REG_OFDM_FA_HOLDC_11N, BIT_MASK_OFDM_FA_KEEP, 1); rtw_write32_mask(rtwdev, REG_OFDM_FA_RSTD_11N, BIT_MASK_OFDM_FA_KEEP1, 1); rtw_write32_mask(rtwdev, REG_CCK_FA_RST_11N, BIT_MASK_CCK_CNT_KEEP, 1); rtw_write32_mask(rtwdev, REG_CCK_FA_RST_11N, BIT_MASK_CCK_FA_KEEP, 1); cck_fa_cnt = rtw_read32_mask(rtwdev, REG_CCK_FA_LSB_11N, MASKBYTE0); cck_fa_cnt += rtw_read32_mask(rtwdev, REG_CCK_FA_MSB_11N, MASKBYTE3) << 8; val32 = rtw_read32(rtwdev, REG_OFDM_FA_TYPE1_11N); ofdm_fa_cnt = u32_get_bits(val32, BIT_MASK_OFDM_FF_CNT); ofdm_fa_cnt += u32_get_bits(val32, BIT_MASK_OFDM_SF_CNT); val32 = rtw_read32(rtwdev, REG_OFDM_FA_TYPE2_11N); dm_info->ofdm_cca_cnt = u32_get_bits(val32, BIT_MASK_OFDM_CCA_CNT); ofdm_fa_cnt += u32_get_bits(val32, BIT_MASK_OFDM_PF_CNT); val32 = rtw_read32(rtwdev, REG_OFDM_FA_TYPE3_11N); ofdm_fa_cnt += u32_get_bits(val32, BIT_MASK_OFDM_RI_CNT); ofdm_fa_cnt += u32_get_bits(val32, BIT_MASK_OFDM_CRC_CNT); val32 = rtw_read32(rtwdev, REG_OFDM_FA_TYPE4_11N); ofdm_fa_cnt += u32_get_bits(val32, BIT_MASK_OFDM_MNS_CNT); dm_info->cck_fa_cnt = cck_fa_cnt; dm_info->ofdm_fa_cnt = ofdm_fa_cnt; dm_info->total_fa_cnt = cck_fa_cnt + ofdm_fa_cnt; dm_info->cck_err_cnt = rtw_read32(rtwdev, REG_IGI_C_11N); dm_info->cck_ok_cnt = rtw_read32(rtwdev, REG_IGI_D_11N); crc32_cnt = rtw_read32(rtwdev, REG_OFDM_CRC32_CNT_11N); dm_info->ofdm_err_cnt = u32_get_bits(crc32_cnt, BIT_MASK_OFDM_LCRC_ERR); dm_info->ofdm_ok_cnt = u32_get_bits(crc32_cnt, BIT_MASK_OFDM_LCRC_OK); crc32_cnt = rtw_read32(rtwdev, REG_HT_CRC32_CNT_11N); dm_info->ht_err_cnt = u32_get_bits(crc32_cnt, BIT_MASK_HT_CRC_ERR); dm_info->ht_ok_cnt = u32_get_bits(crc32_cnt, BIT_MASK_HT_CRC_OK); dm_info->vht_err_cnt = 0; dm_info->vht_ok_cnt = 0; val32 = rtw_read32(rtwdev, REG_CCK_CCA_CNT_11N); dm_info->cck_cca_cnt = (u32_get_bits(val32, BIT_MASK_CCK_FA_MSB) << 8) | u32_get_bits(val32, BIT_MASK_CCK_FA_LSB); dm_info->total_cca_cnt = dm_info->cck_cca_cnt + dm_info->ofdm_cca_cnt; /* reset counter */ rtw_write32_mask(rtwdev, REG_OFDM_FA_RSTC_11N, BIT_MASK_OFDM_FA_RST, 1); rtw_write32_mask(rtwdev, REG_OFDM_FA_RSTC_11N, BIT_MASK_OFDM_FA_RST, 0); rtw_write32_mask(rtwdev, REG_OFDM_FA_RSTD_11N, BIT_MASK_OFDM_FA_RST1, 1); rtw_write32_mask(rtwdev, REG_OFDM_FA_RSTD_11N, BIT_MASK_OFDM_FA_RST1, 0); rtw_write32_mask(rtwdev, REG_OFDM_FA_HOLDC_11N, BIT_MASK_OFDM_FA_KEEP, 0); rtw_write32_mask(rtwdev, REG_OFDM_FA_RSTD_11N, BIT_MASK_OFDM_FA_KEEP1, 0); rtw_write32_mask(rtwdev, REG_CCK_FA_RST_11N, BIT_MASK_CCK_CNT_KPEN, 0); rtw_write32_mask(rtwdev, REG_CCK_FA_RST_11N, BIT_MASK_CCK_CNT_KPEN, 2); rtw_write32_mask(rtwdev, REG_CCK_FA_RST_11N, BIT_MASK_CCK_FA_KPEN, 0); rtw_write32_mask(rtwdev, REG_CCK_FA_RST_11N, BIT_MASK_CCK_FA_KPEN, 2); rtw_write32_mask(rtwdev, REG_PAGE_F_RST_11N, BIT_MASK_F_RST_ALL, 1); rtw_write32_mask(rtwdev, REG_PAGE_F_RST_11N, BIT_MASK_F_RST_ALL, 0); } static const u32 iqk_adda_regs[] = { 0x85c, 0xe6c, 0xe70, 0xe74, 0xe78, 0xe7c, 0xe80, 0xe84, 0xe88, 0xe8c, 0xed0, 0xed4, 0xed8, 0xedc, 0xee0, 0xeec }; static const u32 iqk_mac8_regs[] = {0x522, 0x550, 0x551}; static const u32 iqk_mac32_regs[] = {0x40}; static const u32 iqk_bb_regs[] = { 0xc04, 0xc08, 0x874, 0xb68, 0xb6c, 0x870, 0x860, 0x864, 0xa04 }; #define IQK_ADDA_REG_NUM ARRAY_SIZE(iqk_adda_regs) #define IQK_MAC8_REG_NUM ARRAY_SIZE(iqk_mac8_regs) #define IQK_MAC32_REG_NUM ARRAY_SIZE(iqk_mac32_regs) #define IQK_BB_REG_NUM ARRAY_SIZE(iqk_bb_regs) struct iqk_backup_regs { u32 adda[IQK_ADDA_REG_NUM]; u8 mac8[IQK_MAC8_REG_NUM]; u32 mac32[IQK_MAC32_REG_NUM]; u32 bb[IQK_BB_REG_NUM]; u32 lte_path; u32 lte_gnt; u32 bb_sel_btg; u8 btg_sel; u8 igia; u8 igib; }; static void rtw8723d_iqk_backup_regs(struct rtw_dev *rtwdev, struct iqk_backup_regs *backup) { int i; for (i = 0; i < IQK_ADDA_REG_NUM; i++) backup->adda[i] = rtw_read32(rtwdev, iqk_adda_regs[i]); for (i = 0; i < IQK_MAC8_REG_NUM; i++) backup->mac8[i] = rtw_read8(rtwdev, iqk_mac8_regs[i]); for (i = 0; i < IQK_MAC32_REG_NUM; i++) backup->mac32[i] = rtw_read32(rtwdev, iqk_mac32_regs[i]); for (i = 0; i < IQK_BB_REG_NUM; i++) backup->bb[i] = rtw_read32(rtwdev, iqk_bb_regs[i]); backup->igia = rtw_read32_mask(rtwdev, REG_OFDM0_XAAGC1, MASKBYTE0); backup->igib = rtw_read32_mask(rtwdev, REG_OFDM0_XBAGC1, MASKBYTE0); backup->bb_sel_btg = rtw_read32(rtwdev, REG_BB_SEL_BTG); } static void rtw8723d_iqk_restore_regs(struct rtw_dev *rtwdev, const struct iqk_backup_regs *backup) { int i; for (i = 0; i < IQK_ADDA_REG_NUM; i++) rtw_write32(rtwdev, iqk_adda_regs[i], backup->adda[i]); for (i = 0; i < IQK_MAC8_REG_NUM; i++) rtw_write8(rtwdev, iqk_mac8_regs[i], backup->mac8[i]); for (i = 0; i < IQK_MAC32_REG_NUM; i++) rtw_write32(rtwdev, iqk_mac32_regs[i], backup->mac32[i]); for (i = 0; i < IQK_BB_REG_NUM; i++) rtw_write32(rtwdev, iqk_bb_regs[i], backup->bb[i]); rtw_write32_mask(rtwdev, REG_OFDM0_XAAGC1, MASKBYTE0, 0x50); rtw_write32_mask(rtwdev, REG_OFDM0_XAAGC1, MASKBYTE0, backup->igia); rtw_write32_mask(rtwdev, REG_OFDM0_XBAGC1, MASKBYTE0, 0x50); rtw_write32_mask(rtwdev, REG_OFDM0_XBAGC1, MASKBYTE0, backup->igib); rtw_write32(rtwdev, REG_TXIQK_TONE_A_11N, 0x01008c00); rtw_write32(rtwdev, REG_RXIQK_TONE_A_11N, 0x01008c00); } static void rtw8723d_iqk_backup_path_ctrl(struct rtw_dev *rtwdev, struct iqk_backup_regs *backup) { backup->btg_sel = rtw_read8(rtwdev, REG_BTG_SEL); rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] original 0x67 = 0x%x\n", backup->btg_sel); } static void rtw8723d_iqk_config_path_ctrl(struct rtw_dev *rtwdev) { rtw_write32_mask(rtwdev, REG_PAD_CTRL1, BIT_BT_BTG_SEL, 0x1); rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] set 0x67 = 0x%x\n", rtw_read32_mask(rtwdev, REG_PAD_CTRL1, MASKBYTE3)); } static void rtw8723d_iqk_restore_path_ctrl(struct rtw_dev *rtwdev, const struct iqk_backup_regs *backup) { rtw_write8(rtwdev, REG_BTG_SEL, backup->btg_sel); rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] restore 0x67 = 0x%x\n", rtw_read32_mask(rtwdev, REG_PAD_CTRL1, MASKBYTE3)); } static void rtw8723d_iqk_backup_lte_path_gnt(struct rtw_dev *rtwdev, struct iqk_backup_regs *backup) { backup->lte_path = rtw_read32(rtwdev, REG_LTECOEX_PATH_CONTROL); rtw_write32(rtwdev, REG_LTECOEX_CTRL, 0x800f0038); mdelay(1); backup->lte_gnt = rtw_read32(rtwdev, REG_LTECOEX_READ_DATA); rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] OriginalGNT = 0x%x\n", backup->lte_gnt); } static void rtw8723d_iqk_config_lte_path_gnt(struct rtw_dev *rtwdev) { rtw_write32(rtwdev, REG_LTECOEX_WRITE_DATA, 0x0000ff00); rtw_write32(rtwdev, REG_LTECOEX_CTRL, 0xc0020038); rtw_write32_mask(rtwdev, REG_LTECOEX_PATH_CONTROL, BIT_LTE_MUX_CTRL_PATH, 0x1); } static void rtw8723d_iqk_restore_lte_path_gnt(struct rtw_dev *rtwdev, const struct iqk_backup_regs *bak) { rtw_write32(rtwdev, REG_LTECOEX_WRITE_DATA, bak->lte_gnt); rtw_write32(rtwdev, REG_LTECOEX_CTRL, 0xc00f0038); rtw_write32(rtwdev, REG_LTECOEX_PATH_CONTROL, bak->lte_path); } struct rtw_8723d_iqk_cfg { const char *name; u32 val_bb_sel_btg; u32 reg_lutwe; u32 val_txiqk_pi; u32 reg_padlut; u32 reg_gaintx; u32 reg_bspad; u32 val_wlint; u32 val_wlsel; u32 val_iqkpts; }; static const struct rtw_8723d_iqk_cfg iqk_tx_cfg[PATH_NR] = { [PATH_S1] = { .name = "S1", .val_bb_sel_btg = 0x99000000, .reg_lutwe = RF_LUTWE, .val_txiqk_pi = 0x8214019f, .reg_padlut = RF_LUTDBG, .reg_gaintx = RF_GAINTX, .reg_bspad = RF_BSPAD, .val_wlint = 0xe0d, .val_wlsel = 0x60d, .val_iqkpts = 0xfa000000, }, [PATH_S0] = { .name = "S0", .val_bb_sel_btg = 0x99000280, .reg_lutwe = RF_LUTWE2, .val_txiqk_pi = 0x8214018a, .reg_padlut = RF_TXADBG, .reg_gaintx = RF_TRXIQ, .reg_bspad = RF_TXATANK, .val_wlint = 0xe6d, .val_wlsel = 0x66d, .val_iqkpts = 0xf9000000, }, }; static u8 rtw8723d_iqk_check_tx_failed(struct rtw_dev *rtwdev, const struct rtw_8723d_iqk_cfg *iqk_cfg) { s32 tx_x, tx_y; u32 tx_fail; rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] 0xeac = 0x%x\n", rtw_read32(rtwdev, REG_IQK_RES_RY)); rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] 0xe94 = 0x%x, 0xe9c = 0x%x\n", rtw_read32(rtwdev, REG_IQK_RES_TX), rtw_read32(rtwdev, REG_IQK_RES_TY)); rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] 0xe90(before IQK)= 0x%x, 0xe98(afer IQK) = 0x%x\n", rtw_read32(rtwdev, 0xe90), rtw_read32(rtwdev, 0xe98)); tx_fail = rtw_read32_mask(rtwdev, REG_IQK_RES_RY, BIT_IQK_TX_FAIL); tx_x = rtw_read32_mask(rtwdev, REG_IQK_RES_TX, BIT_MASK_RES_TX); tx_y = rtw_read32_mask(rtwdev, REG_IQK_RES_TY, BIT_MASK_RES_TY); if (!tx_fail && tx_x != IQK_TX_X_ERR && tx_y != IQK_TX_Y_ERR) return IQK_TX_OK; rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] %s TXIQK is failed\n", iqk_cfg->name); return 0; } static u8 rtw8723d_iqk_check_rx_failed(struct rtw_dev *rtwdev, const struct rtw_8723d_iqk_cfg *iqk_cfg) { s32 rx_x, rx_y; u32 rx_fail; rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] 0xea4 = 0x%x, 0xeac = 0x%x\n", rtw_read32(rtwdev, REG_IQK_RES_RX), rtw_read32(rtwdev, REG_IQK_RES_RY)); rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] 0xea0(before IQK)= 0x%x, 0xea8(afer IQK) = 0x%x\n", rtw_read32(rtwdev, 0xea0), rtw_read32(rtwdev, 0xea8)); rx_fail = rtw_read32_mask(rtwdev, REG_IQK_RES_RY, BIT_IQK_RX_FAIL); rx_x = rtw_read32_mask(rtwdev, REG_IQK_RES_RX, BIT_MASK_RES_RX); rx_y = rtw_read32_mask(rtwdev, REG_IQK_RES_RY, BIT_MASK_RES_RY); rx_y = abs(iqkxy_to_s32(rx_y)); if (!rx_fail && rx_x < IQK_RX_X_UPPER && rx_x > IQK_RX_X_LOWER && rx_y < IQK_RX_Y_LMT) return IQK_RX_OK; rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] %s RXIQK STEP2 is failed\n", iqk_cfg->name); return 0; } static void rtw8723d_iqk_one_shot(struct rtw_dev *rtwdev, bool tx, const struct rtw_8723d_iqk_cfg *iqk_cfg) { u32 pts = (tx ? iqk_cfg->val_iqkpts : 0xf9000000); /* enter IQK mode */ rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, BIT_MASK_IQK_MOD, EN_IQK); rtw8723d_iqk_config_lte_path_gnt(rtwdev); rtw_write32(rtwdev, REG_LTECOEX_CTRL, 0x800f0054); mdelay(1); rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] GNT_BT @%s %sIQK1 = 0x%x\n", iqk_cfg->name, tx ? "TX" : "RX", rtw_read32(rtwdev, REG_LTECOEX_READ_DATA)); rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] 0x948 @%s %sIQK1 = 0x%x\n", iqk_cfg->name, tx ? "TX" : "RX", rtw_read32(rtwdev, REG_BB_SEL_BTG)); /* One shot, LOK & IQK */ rtw_write32(rtwdev, REG_IQK_AGC_PTS_11N, pts); rtw_write32(rtwdev, REG_IQK_AGC_PTS_11N, 0xf8000000); if (!check_hw_ready(rtwdev, REG_IQK_RES_RY, BIT_IQK_DONE, 1)) rtw_warn(rtwdev, "%s %s IQK isn't done\n", iqk_cfg->name, tx ? "TX" : "RX"); } static void rtw8723d_iqk_txrx_path_post(struct rtw_dev *rtwdev, const struct rtw_8723d_iqk_cfg *iqk_cfg, const struct iqk_backup_regs *backup) { rtw8723d_iqk_restore_lte_path_gnt(rtwdev, backup); rtw_write32(rtwdev, REG_BB_SEL_BTG, backup->bb_sel_btg); /* leave IQK mode */ rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, BIT_MASK_IQK_MOD, RST_IQK); mdelay(1); rtw_write_rf(rtwdev, RF_PATH_A, iqk_cfg->reg_padlut, 0x800, 0x0); rtw_write_rf(rtwdev, RF_PATH_A, RF_WLINT, BIT(0), 0x0); rtw_write_rf(rtwdev, RF_PATH_A, RF_WLSEL, BIT(0), 0x0); } static u8 rtw8723d_iqk_tx_path(struct rtw_dev *rtwdev, const struct rtw_8723d_iqk_cfg *iqk_cfg, const struct iqk_backup_regs *backup) { u8 status; rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path %s TXIQK!!\n", iqk_cfg->name); rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] 0x67 @%s TXIQK = 0x%x\n", iqk_cfg->name, rtw_read32_mask(rtwdev, REG_PAD_CTRL1, MASKBYTE3)); rtw_write32(rtwdev, REG_BB_SEL_BTG, iqk_cfg->val_bb_sel_btg); rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, BIT_MASK_IQK_MOD, RST_IQK); mdelay(1); rtw_write_rf(rtwdev, RF_PATH_A, iqk_cfg->reg_lutwe, RFREG_MASK, 0x80000); rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWA, RFREG_MASK, 0x00004); rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWD1, RFREG_MASK, 0x0005d); rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWD0, RFREG_MASK, 0xBFFE0); rtw_write_rf(rtwdev, RF_PATH_A, iqk_cfg->reg_lutwe, RFREG_MASK, 0x00000); /* IQK setting */ rtw_write32(rtwdev, REG_TXIQK_TONE_A_11N, 0x08008c0c); rtw_write32(rtwdev, REG_RXIQK_TONE_A_11N, 0x38008c1c); rtw_write32(rtwdev, REG_TXIQK_PI_A_11N, iqk_cfg->val_txiqk_pi); rtw_write32(rtwdev, REG_RXIQK_PI_A_11N, 0x28160200); rtw_write32(rtwdev, REG_TXIQK_11N, 0x01007c00); rtw_write32(rtwdev, REG_RXIQK_11N, 0x01004800); /* LOK setting */ rtw_write32(rtwdev, REG_IQK_AGC_RSP_11N, 0x00462911); /* PA, PAD setting */ rtw_write_rf(rtwdev, RF_PATH_A, iqk_cfg->reg_padlut, 0x800, 0x1); rtw_write_rf(rtwdev, RF_PATH_A, iqk_cfg->reg_gaintx, 0x600, 0x0); rtw_write_rf(rtwdev, RF_PATH_A, iqk_cfg->reg_gaintx, 0x1E0, 0x3); rtw_write_rf(rtwdev, RF_PATH_A, RF_RXIQGEN, 0x1F, 0xf); /* LOK setting for 8723D */ rtw_write_rf(rtwdev, RF_PATH_A, iqk_cfg->reg_lutwe, 0x10, 0x1); rtw_write_rf(rtwdev, RF_PATH_A, iqk_cfg->reg_bspad, 0x1, 0x1); rtw_write_rf(rtwdev, RF_PATH_A, RF_WLINT, RFREG_MASK, iqk_cfg->val_wlint); rtw_write_rf(rtwdev, RF_PATH_A, RF_WLSEL, RFREG_MASK, iqk_cfg->val_wlsel); rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] RF0x1 @%s TXIQK = 0x%x\n", iqk_cfg->name, rtw_read_rf(rtwdev, RF_PATH_A, RF_WLINT, RFREG_MASK)); rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] RF0x2 @%s TXIQK = 0x%x\n", iqk_cfg->name, rtw_read_rf(rtwdev, RF_PATH_A, RF_WLSEL, RFREG_MASK)); rtw8723d_iqk_one_shot(rtwdev, true, iqk_cfg); status = rtw8723d_iqk_check_tx_failed(rtwdev, iqk_cfg); rtw8723d_iqk_txrx_path_post(rtwdev, iqk_cfg, backup); return status; } static u8 rtw8723d_iqk_rx_path(struct rtw_dev *rtwdev, const struct rtw_8723d_iqk_cfg *iqk_cfg, const struct iqk_backup_regs *backup) { u32 tx_x, tx_y; u8 status; rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path %s RXIQK Step1!!\n", iqk_cfg->name); rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] 0x67 @%s RXIQK1 = 0x%x\n", iqk_cfg->name, rtw_read32_mask(rtwdev, REG_PAD_CTRL1, MASKBYTE3)); rtw_write32(rtwdev, REG_BB_SEL_BTG, iqk_cfg->val_bb_sel_btg); rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, BIT_MASK_IQK_MOD, RST_IQK); /* IQK setting */ rtw_write32(rtwdev, REG_TXIQK_11N, 0x01007c00); rtw_write32(rtwdev, REG_RXIQK_11N, 0x01004800); /* path IQK setting */ rtw_write32(rtwdev, REG_TXIQK_TONE_A_11N, 0x18008c1c); rtw_write32(rtwdev, REG_RXIQK_TONE_A_11N, 0x38008c1c); rtw_write32(rtwdev, REG_TX_IQK_TONE_B, 0x38008c1c); rtw_write32(rtwdev, REG_RX_IQK_TONE_B, 0x38008c1c); rtw_write32(rtwdev, REG_TXIQK_PI_A_11N, 0x82160000); rtw_write32(rtwdev, REG_RXIQK_PI_A_11N, 0x28160000); /* LOK setting */ rtw_write32(rtwdev, REG_IQK_AGC_RSP_11N, 0x0046a911); /* RXIQK mode */ rtw_write_rf(rtwdev, RF_PATH_A, iqk_cfg->reg_lutwe, RFREG_MASK, 0x80000); rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWA, RFREG_MASK, 0x00006); rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWD1, RFREG_MASK, 0x0005f); rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWD0, RFREG_MASK, 0xa7ffb); rtw_write_rf(rtwdev, RF_PATH_A, iqk_cfg->reg_lutwe, RFREG_MASK, 0x00000); /* PA/PAD=0 */ rtw_write_rf(rtwdev, RF_PATH_A, iqk_cfg->reg_padlut, 0x800, 0x1); rtw_write_rf(rtwdev, RF_PATH_A, iqk_cfg->reg_gaintx, 0x600, 0x0); rtw_write_rf(rtwdev, RF_PATH_A, RF_WLINT, RFREG_MASK, iqk_cfg->val_wlint); rtw_write_rf(rtwdev, RF_PATH_A, RF_WLSEL, RFREG_MASK, iqk_cfg->val_wlsel); rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] RF0x1@ path %s RXIQK1 = 0x%x\n", iqk_cfg->name, rtw_read_rf(rtwdev, RF_PATH_A, RF_WLINT, RFREG_MASK)); rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] RF0x2@ path %s RXIQK1 = 0x%x\n", iqk_cfg->name, rtw_read_rf(rtwdev, RF_PATH_A, RF_WLSEL, RFREG_MASK)); rtw8723d_iqk_one_shot(rtwdev, false, iqk_cfg); status = rtw8723d_iqk_check_tx_failed(rtwdev, iqk_cfg); if (!status) goto restore; /* second round */ tx_x = rtw_read32_mask(rtwdev, REG_IQK_RES_TX, BIT_MASK_RES_TX); tx_y = rtw_read32_mask(rtwdev, REG_IQK_RES_TY, BIT_MASK_RES_TY); rtw_write32(rtwdev, REG_TXIQK_11N, BIT_SET_TXIQK_11N(tx_x, tx_y)); rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] 0xe40 = 0x%x u4tmp = 0x%x\n", rtw_read32(rtwdev, REG_TXIQK_11N), BIT_SET_TXIQK_11N(tx_x, tx_y)); rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path %s RXIQK STEP2!!\n", iqk_cfg->name); rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] 0x67 @%s RXIQK2 = 0x%x\n", iqk_cfg->name, rtw_read32_mask(rtwdev, REG_PAD_CTRL1, MASKBYTE3)); rtw_write32(rtwdev, REG_RXIQK_11N, 0x01004800); rtw_write32(rtwdev, REG_TXIQK_TONE_A_11N, 0x38008c1c); rtw_write32(rtwdev, REG_RXIQK_TONE_A_11N, 0x18008c1c); rtw_write32(rtwdev, REG_TX_IQK_TONE_B, 0x38008c1c); rtw_write32(rtwdev, REG_RX_IQK_TONE_B, 0x38008c1c); rtw_write32(rtwdev, REG_TXIQK_PI_A_11N, 0x82170000); rtw_write32(rtwdev, REG_RXIQK_PI_A_11N, 0x28171400); /* LOK setting */ rtw_write32(rtwdev, REG_IQK_AGC_RSP_11N, 0x0046a8d1); /* RXIQK mode */ rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, BIT_MASK_IQK_MOD, RST_IQK); mdelay(1); rtw_write_rf(rtwdev, RF_PATH_A, iqk_cfg->reg_lutwe, 0x80000, 0x1); rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWA, RFREG_MASK, 0x00007); rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWD1, RFREG_MASK, 0x0005f); rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWD0, RFREG_MASK, 0xb3fdb); rtw_write_rf(rtwdev, RF_PATH_A, iqk_cfg->reg_lutwe, RFREG_MASK, 0x00000); rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] RF0x1 @%s RXIQK2 = 0x%x\n", iqk_cfg->name, rtw_read_rf(rtwdev, RF_PATH_A, RF_WLINT, RFREG_MASK)); rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] RF0x2 @%s RXIQK2 = 0x%x\n", iqk_cfg->name, rtw_read_rf(rtwdev, RF_PATH_A, RF_WLSEL, RFREG_MASK)); rtw8723d_iqk_one_shot(rtwdev, false, iqk_cfg); status |= rtw8723d_iqk_check_rx_failed(rtwdev, iqk_cfg); restore: rtw8723d_iqk_txrx_path_post(rtwdev, iqk_cfg, backup); return status; } static void rtw8723d_iqk_fill_s1_matrix(struct rtw_dev *rtwdev, const s32 result[]) { s32 oldval_1; s32 x, y; s32 tx1_a, tx1_a_ext; s32 tx1_c, tx1_c_ext; if (result[IQK_S1_TX_X] == 0) return; oldval_1 = rtw_read32_mask(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE, BIT_MASK_TXIQ_ELM_D); x = iqkxy_to_s32(result[IQK_S1_TX_X]); tx1_a = iqk_mult(x, oldval_1, &tx1_a_ext); rtw_write32_mask(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE, BIT_MASK_TXIQ_ELM_A, tx1_a); rtw_write32_mask(rtwdev, REG_OFDM_0_ECCA_THRESHOLD, BIT_MASK_OFDM0_EXT_A, tx1_a_ext); y = iqkxy_to_s32(result[IQK_S1_TX_Y]); tx1_c = iqk_mult(y, oldval_1, &tx1_c_ext); rtw_write32_mask(rtwdev, REG_TXIQK_MATRIXA_LSB2_11N, MASKH4BITS, BIT_SET_TXIQ_ELM_C1(tx1_c)); rtw_write32_mask(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE, BIT_MASK_TXIQ_ELM_C, BIT_SET_TXIQ_ELM_C2(tx1_c)); rtw_write32_mask(rtwdev, REG_OFDM_0_ECCA_THRESHOLD, BIT_MASK_OFDM0_EXT_C, tx1_c_ext); rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] X = 0x%x, TX1_A = 0x%x, oldval_1 0x%x\n", x, tx1_a, oldval_1); rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] Y = 0x%x, TX1_C = 0x%x\n", y, tx1_c); if (result[IQK_S1_RX_X] == 0) return; rtw_write32_mask(rtwdev, REG_A_RXIQI, BIT_MASK_RXIQ_S1_X, result[IQK_S1_RX_X]); rtw_write32_mask(rtwdev, REG_A_RXIQI, BIT_MASK_RXIQ_S1_Y1, BIT_SET_RXIQ_S1_Y1(result[IQK_S1_RX_Y])); rtw_write32_mask(rtwdev, REG_RXIQK_MATRIX_LSB_11N, BIT_MASK_RXIQ_S1_Y2, BIT_SET_RXIQ_S1_Y2(result[IQK_S1_RX_Y])); } static void rtw8723d_iqk_fill_s0_matrix(struct rtw_dev *rtwdev, const s32 result[]) { s32 oldval_0; s32 x, y; s32 tx0_a, tx0_a_ext; s32 tx0_c, tx0_c_ext; if (result[IQK_S0_TX_X] == 0) return; oldval_0 = rtw_read32_mask(rtwdev, REG_TXIQ_CD_S0, BIT_MASK_TXIQ_D_S0); x = iqkxy_to_s32(result[IQK_S0_TX_X]); tx0_a = iqk_mult(x, oldval_0, &tx0_a_ext); rtw_write32_mask(rtwdev, REG_TXIQ_AB_S0, BIT_MASK_TXIQ_A_S0, tx0_a); rtw_write32_mask(rtwdev, REG_TXIQ_AB_S0, BIT_MASK_TXIQ_A_EXT_S0, tx0_a_ext); y = iqkxy_to_s32(result[IQK_S0_TX_Y]); tx0_c = iqk_mult(y, oldval_0, &tx0_c_ext); rtw_write32_mask(rtwdev, REG_TXIQ_CD_S0, BIT_MASK_TXIQ_C_S0, tx0_c); rtw_write32_mask(rtwdev, REG_TXIQ_CD_S0, BIT_MASK_TXIQ_C_EXT_S0, tx0_c_ext); if (result[IQK_S0_RX_X] == 0) return; rtw_write32_mask(rtwdev, REG_RXIQ_AB_S0, BIT_MASK_RXIQ_X_S0, result[IQK_S0_RX_X]); rtw_write32_mask(rtwdev, REG_RXIQ_AB_S0, BIT_MASK_RXIQ_Y_S0, result[IQK_S0_RX_Y]); } static void rtw8723d_iqk_path_adda_on(struct rtw_dev *rtwdev) { int i; for (i = 0; i < IQK_ADDA_REG_NUM; i++) rtw_write32(rtwdev, iqk_adda_regs[i], 0x03c00016); } static void rtw8723d_iqk_config_mac(struct rtw_dev *rtwdev) { rtw_write8(rtwdev, REG_TXPAUSE, 0xff); } static void rtw8723d_iqk_rf_standby(struct rtw_dev *rtwdev, enum rtw_rf_path path) { rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path-%s standby mode!\n", path == RF_PATH_A ? "S1" : "S0"); rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, BIT_MASK_IQK_MOD, RST_IQK); mdelay(1); rtw_write_rf(rtwdev, path, RF_MODE, RFREG_MASK, 0x10000); rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, BIT_MASK_IQK_MOD, EN_IQK); } static bool rtw8723d_iqk_similarity_cmp(struct rtw_dev *rtwdev, s32 result[][IQK_NR], u8 c1, u8 c2) { u32 i, j, diff; u32 bitmap = 0; u8 candidate[PATH_NR] = {IQK_ROUND_INVALID, IQK_ROUND_INVALID}; bool ret = true; s32 tmp1, tmp2; for (i = 0; i < IQK_NR; i++) { tmp1 = iqkxy_to_s32(result[c1][i]); tmp2 = iqkxy_to_s32(result[c2][i]); diff = abs(tmp1 - tmp2); if (diff <= MAX_TOLERANCE) continue; if ((i == IQK_S1_RX_X || i == IQK_S0_RX_X) && !bitmap) { if (result[c1][i] + result[c1][i + 1] == 0) candidate[i / IQK_SX_NR] = c2; else if (result[c2][i] + result[c2][i + 1] == 0) candidate[i / IQK_SX_NR] = c1; else bitmap |= BIT(i); } else { bitmap |= BIT(i); } } if (bitmap != 0) goto check_sim; for (i = 0; i < PATH_NR; i++) { if (candidate[i] == IQK_ROUND_INVALID) continue; for (j = i * IQK_SX_NR; j < i * IQK_SX_NR + 2; j++) result[IQK_ROUND_HYBRID][j] = result[candidate[i]][j]; ret = false; } return ret; check_sim: for (i = 0; i < IQK_NR; i++) { j = i & ~1; /* 2 bits are a pair for IQ[X, Y] */ if (bitmap & GENMASK(j + 1, j)) continue; result[IQK_ROUND_HYBRID][i] = result[c1][i]; } return false; } static void rtw8723d_iqk_precfg_path(struct rtw_dev *rtwdev, enum rtw8723d_path path) { if (path == PATH_S0) { rtw8723d_iqk_rf_standby(rtwdev, RF_PATH_A); rtw8723d_iqk_path_adda_on(rtwdev); } rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, BIT_MASK_IQK_MOD, EN_IQK); rtw_write32(rtwdev, REG_TXIQK_11N, 0x01007c00); rtw_write32(rtwdev, REG_RXIQK_11N, 0x01004800); if (path == PATH_S1) { rtw8723d_iqk_rf_standby(rtwdev, RF_PATH_B); rtw8723d_iqk_path_adda_on(rtwdev); } } static void rtw8723d_iqk_one_round(struct rtw_dev *rtwdev, s32 result[][IQK_NR], u8 t, const struct iqk_backup_regs *backup) { u32 i; u8 s1_ok, s0_ok; rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] IQ Calibration for 1T1R_S0/S1 for %d times\n", t); rtw8723d_iqk_path_adda_on(rtwdev); rtw8723d_iqk_config_mac(rtwdev); rtw_write32_mask(rtwdev, REG_CCK_ANT_SEL_11N, 0x0f000000, 0xf); rtw_write32(rtwdev, REG_BB_RX_PATH_11N, 0x03a05611); rtw_write32(rtwdev, REG_TRMUX_11N, 0x000800e4); rtw_write32(rtwdev, REG_BB_PWR_SAV1_11N, 0x25204200); rtw8723d_iqk_precfg_path(rtwdev, PATH_S1); for (i = 0; i < PATH_IQK_RETRY; i++) { s1_ok = rtw8723d_iqk_tx_path(rtwdev, &iqk_tx_cfg[PATH_S1], backup); if (s1_ok == IQK_TX_OK) { rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path S1 Tx IQK Success!!\n"); result[t][IQK_S1_TX_X] = rtw_read32_mask(rtwdev, REG_IQK_RES_TX, BIT_MASK_RES_TX); result[t][IQK_S1_TX_Y] = rtw_read32_mask(rtwdev, REG_IQK_RES_TY, BIT_MASK_RES_TY); break; } rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path S1 Tx IQK Fail!!\n"); result[t][IQK_S1_TX_X] = 0x100; result[t][IQK_S1_TX_Y] = 0x0; } for (i = 0; i < PATH_IQK_RETRY; i++) { s1_ok = rtw8723d_iqk_rx_path(rtwdev, &iqk_tx_cfg[PATH_S1], backup); if (s1_ok == (IQK_TX_OK | IQK_RX_OK)) { rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path S1 Rx IQK Success!!\n"); result[t][IQK_S1_RX_X] = rtw_read32_mask(rtwdev, REG_IQK_RES_RX, BIT_MASK_RES_RX); result[t][IQK_S1_RX_Y] = rtw_read32_mask(rtwdev, REG_IQK_RES_RY, BIT_MASK_RES_RY); break; } rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path S1 Rx IQK Fail!!\n"); result[t][IQK_S1_RX_X] = 0x100; result[t][IQK_S1_RX_Y] = 0x0; } if (s1_ok == 0x0) rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path S1 IQK is failed!!\n"); rtw8723d_iqk_precfg_path(rtwdev, PATH_S0); for (i = 0; i < PATH_IQK_RETRY; i++) { s0_ok = rtw8723d_iqk_tx_path(rtwdev, &iqk_tx_cfg[PATH_S0], backup); if (s0_ok == IQK_TX_OK) { rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path S0 Tx IQK Success!!\n"); result[t][IQK_S0_TX_X] = rtw_read32_mask(rtwdev, REG_IQK_RES_TX, BIT_MASK_RES_TX); result[t][IQK_S0_TX_Y] = rtw_read32_mask(rtwdev, REG_IQK_RES_TY, BIT_MASK_RES_TY); break; } rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path S0 Tx IQK Fail!!\n"); result[t][IQK_S0_TX_X] = 0x100; result[t][IQK_S0_TX_Y] = 0x0; } for (i = 0; i < PATH_IQK_RETRY; i++) { s0_ok = rtw8723d_iqk_rx_path(rtwdev, &iqk_tx_cfg[PATH_S0], backup); if (s0_ok == (IQK_TX_OK | IQK_RX_OK)) { rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path S0 Rx IQK Success!!\n"); result[t][IQK_S0_RX_X] = rtw_read32_mask(rtwdev, REG_IQK_RES_RX, BIT_MASK_RES_RX); result[t][IQK_S0_RX_Y] = rtw_read32_mask(rtwdev, REG_IQK_RES_RY, BIT_MASK_RES_RY); break; } rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path S0 Rx IQK Fail!!\n"); result[t][IQK_S0_RX_X] = 0x100; result[t][IQK_S0_RX_Y] = 0x0; } if (s0_ok == 0x0) rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] path S0 IQK is failed!!\n"); rtw_write32_mask(rtwdev, REG_FPGA0_IQK_11N, BIT_MASK_IQK_MOD, RST_IQK); mdelay(1); rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] back to BB mode, load original value!\n"); } static void rtw8723d_phy_calibration(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; s32 result[IQK_ROUND_SIZE][IQK_NR]; struct iqk_backup_regs backup; u8 i, j; u8 final_candidate = IQK_ROUND_INVALID; bool good; rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] Start!!!\n"); memset(result, 0, sizeof(result)); rtw8723d_iqk_backup_path_ctrl(rtwdev, &backup); rtw8723d_iqk_backup_lte_path_gnt(rtwdev, &backup); rtw8723d_iqk_backup_regs(rtwdev, &backup); for (i = IQK_ROUND_0; i <= IQK_ROUND_2; i++) { rtw8723d_iqk_config_path_ctrl(rtwdev); rtw8723d_iqk_config_lte_path_gnt(rtwdev); rtw8723d_iqk_one_round(rtwdev, result, i, &backup); if (i > IQK_ROUND_0) rtw8723d_iqk_restore_regs(rtwdev, &backup); rtw8723d_iqk_restore_lte_path_gnt(rtwdev, &backup); rtw8723d_iqk_restore_path_ctrl(rtwdev, &backup); for (j = IQK_ROUND_0; j < i; j++) { good = rtw8723d_iqk_similarity_cmp(rtwdev, result, j, i); if (good) { final_candidate = j; rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] cmp %d:%d final_candidate is %x\n", j, i, final_candidate); goto iqk_done; } } } if (final_candidate == IQK_ROUND_INVALID) { s32 reg_tmp = 0; for (i = 0; i < IQK_NR; i++) reg_tmp += result[IQK_ROUND_HYBRID][i]; if (reg_tmp != 0) { final_candidate = IQK_ROUND_HYBRID; } else { WARN(1, "IQK is failed\n"); goto out; } } iqk_done: rtw8723d_iqk_fill_s1_matrix(rtwdev, result[final_candidate]); rtw8723d_iqk_fill_s0_matrix(rtwdev, result[final_candidate]); dm_info->iqk.result.s1_x = result[final_candidate][IQK_S1_TX_X]; dm_info->iqk.result.s1_y = result[final_candidate][IQK_S1_TX_Y]; dm_info->iqk.result.s0_x = result[final_candidate][IQK_S0_TX_X]; dm_info->iqk.result.s0_y = result[final_candidate][IQK_S0_TX_Y]; dm_info->iqk.done = true; out: rtw_write32(rtwdev, REG_BB_SEL_BTG, backup.bb_sel_btg); rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] final_candidate is %x\n", final_candidate); for (i = IQK_ROUND_0; i < IQK_ROUND_SIZE; i++) rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] Result %u: rege94_s1=%x rege9c_s1=%x regea4_s1=%x regeac_s1=%x rege94_s0=%x rege9c_s0=%x regea4_s0=%x regeac_s0=%x %s\n", i, result[i][0], result[i][1], result[i][2], result[i][3], result[i][4], result[i][5], result[i][6], result[i][7], final_candidate == i ? "(final candidate)" : ""); rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK]0xc80 = 0x%x 0xc94 = 0x%x 0xc14 = 0x%x 0xca0 = 0x%x\n", rtw_read32(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE), rtw_read32(rtwdev, REG_TXIQK_MATRIXA_LSB2_11N), rtw_read32(rtwdev, REG_A_RXIQI), rtw_read32(rtwdev, REG_RXIQK_MATRIX_LSB_11N)); rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK]0xcd0 = 0x%x 0xcd4 = 0x%x 0xcd8 = 0x%x\n", rtw_read32(rtwdev, REG_TXIQ_AB_S0), rtw_read32(rtwdev, REG_TXIQ_CD_S0), rtw_read32(rtwdev, REG_RXIQ_AB_S0)); rtw_dbg(rtwdev, RTW_DBG_RFK, "[IQK] finished\n"); } static void rtw8723d_phy_cck_pd_set(struct rtw_dev *rtwdev, u8 new_lvl) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 pd[CCK_PD_LV_MAX] = {3, 7, 13, 13, 13}; u8 cck_n_rx; rtw_dbg(rtwdev, RTW_DBG_PHY, "lv: (%d) -> (%d)\n", dm_info->cck_pd_lv[RTW_CHANNEL_WIDTH_20][RF_PATH_A], new_lvl); if (dm_info->cck_pd_lv[RTW_CHANNEL_WIDTH_20][RF_PATH_A] == new_lvl) return; cck_n_rx = (rtw_read8_mask(rtwdev, REG_CCK0_FAREPORT, BIT_CCK0_2RX) && rtw_read8_mask(rtwdev, REG_CCK0_FAREPORT, BIT_CCK0_MRC)) ? 2 : 1; rtw_dbg(rtwdev, RTW_DBG_PHY, "is_linked=%d, lv=%d, n_rx=%d, cs_ratio=0x%x, pd_th=0x%x, cck_fa_avg=%d\n", rtw_is_assoc(rtwdev), new_lvl, cck_n_rx, dm_info->cck_pd_default + new_lvl * 2, pd[new_lvl], dm_info->cck_fa_avg); dm_info->cck_fa_avg = CCK_FA_AVG_RESET; dm_info->cck_pd_lv[RTW_CHANNEL_WIDTH_20][RF_PATH_A] = new_lvl; rtw_write32_mask(rtwdev, REG_PWRTH, 0x3f0000, pd[new_lvl]); rtw_write32_mask(rtwdev, REG_PWRTH2, 0x1f0000, dm_info->cck_pd_default + new_lvl * 2); } /* for coex */ static void rtw8723d_coex_cfg_init(struct rtw_dev *rtwdev) { /* enable TBTT nterrupt */ rtw_write8_set(rtwdev, REG_BCN_CTRL, BIT_EN_BCN_FUNCTION); /* BT report packet sample rate */ /* 0x790[5:0]=0x5 */ rtw_write8_mask(rtwdev, REG_BT_TDMA_TIME, BIT_MASK_SAMPLE_RATE, 0x5); /* enable BT counter statistics */ rtw_write8(rtwdev, REG_BT_STAT_CTRL, 0x1); /* enable PTA (3-wire function form BT side) */ rtw_write32_set(rtwdev, REG_GPIO_MUXCFG, BIT_BT_PTA_EN); rtw_write32_set(rtwdev, REG_GPIO_MUXCFG, BIT_PO_BT_PTA_PINS); /* enable PTA (tx/rx signal form WiFi side) */ rtw_write8_set(rtwdev, REG_QUEUE_CTRL, BIT_PTA_WL_TX_EN); } static void rtw8723d_coex_cfg_gnt_fix(struct rtw_dev *rtwdev) { } static void rtw8723d_coex_cfg_gnt_debug(struct rtw_dev *rtwdev) { rtw_write8_mask(rtwdev, REG_LEDCFG2, BIT(6), 0); rtw_write8_mask(rtwdev, REG_PAD_CTRL1 + 3, BIT(0), 0); rtw_write8_mask(rtwdev, REG_GPIO_INTM + 2, BIT(4), 0); rtw_write8_mask(rtwdev, REG_GPIO_MUXCFG + 2, BIT(1), 0); rtw_write8_mask(rtwdev, REG_PAD_CTRL1 + 3, BIT(1), 0); rtw_write8_mask(rtwdev, REG_PAD_CTRL1 + 2, BIT(7), 0); rtw_write8_mask(rtwdev, REG_SYS_CLKR + 1, BIT(1), 0); rtw_write8_mask(rtwdev, REG_SYS_SDIO_CTRL + 3, BIT(3), 0); } static void rtw8723d_coex_cfg_rfe_type(struct rtw_dev *rtwdev) { struct rtw_efuse *efuse = &rtwdev->efuse; struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_rfe *coex_rfe = &coex->rfe; bool aux = efuse->bt_setting & BIT(6); coex_rfe->rfe_module_type = rtwdev->efuse.rfe_option; coex_rfe->ant_switch_polarity = 0; coex_rfe->ant_switch_exist = false; coex_rfe->ant_switch_with_bt = false; coex_rfe->ant_switch_diversity = false; coex_rfe->wlg_at_btg = true; /* decide antenna at main or aux */ if (efuse->share_ant) { if (aux) rtw_write16(rtwdev, REG_BB_SEL_BTG, 0x80); else rtw_write16(rtwdev, REG_BB_SEL_BTG, 0x200); } else { if (aux) rtw_write16(rtwdev, REG_BB_SEL_BTG, 0x280); else rtw_write16(rtwdev, REG_BB_SEL_BTG, 0x0); } /* disable LTE coex in wifi side */ rtw_coex_write_indirect_reg(rtwdev, LTE_COEX_CTRL, BIT_LTE_COEX_EN, 0x0); rtw_coex_write_indirect_reg(rtwdev, LTE_WL_TRX_CTRL, MASKLWORD, 0xffff); rtw_coex_write_indirect_reg(rtwdev, LTE_BT_TRX_CTRL, MASKLWORD, 0xffff); } static void rtw8723d_coex_cfg_wl_tx_power(struct rtw_dev *rtwdev, u8 wl_pwr) { struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_dm *coex_dm = &coex->dm; static const u8 wl_tx_power[] = {0xb2, 0x90}; u8 pwr; if (wl_pwr == coex_dm->cur_wl_pwr_lvl) return; coex_dm->cur_wl_pwr_lvl = wl_pwr; if (coex_dm->cur_wl_pwr_lvl >= ARRAY_SIZE(wl_tx_power)) coex_dm->cur_wl_pwr_lvl = ARRAY_SIZE(wl_tx_power) - 1; pwr = wl_tx_power[coex_dm->cur_wl_pwr_lvl]; rtw_write8(rtwdev, REG_ANA_PARAM1 + 3, pwr); } static void rtw8723d_coex_cfg_wl_rx_gain(struct rtw_dev *rtwdev, bool low_gain) { struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_dm *coex_dm = &coex->dm; /* WL Rx Low gain on */ static const u32 wl_rx_low_gain_on[] = { 0xec120101, 0xeb130101, 0xce140101, 0xcd150101, 0xcc160101, 0xcb170101, 0xca180101, 0x8d190101, 0x8c1a0101, 0x8b1b0101, 0x4f1c0101, 0x4e1d0101, 0x4d1e0101, 0x4c1f0101, 0x0e200101, 0x0d210101, 0x0c220101, 0x0b230101, 0xcf240001, 0xce250001, 0xcd260001, 0xcc270001, 0x8f280001 }; /* WL Rx Low gain off */ static const u32 wl_rx_low_gain_off[] = { 0xec120101, 0xeb130101, 0xea140101, 0xe9150101, 0xe8160101, 0xe7170101, 0xe6180101, 0xe5190101, 0xe41a0101, 0xe31b0101, 0xe21c0101, 0xe11d0101, 0xe01e0101, 0x861f0101, 0x85200101, 0x84210101, 0x83220101, 0x82230101, 0x81240101, 0x80250101, 0x44260101, 0x43270101, 0x42280101 }; u8 i; if (low_gain == coex_dm->cur_wl_rx_low_gain_en) return; coex_dm->cur_wl_rx_low_gain_en = low_gain; if (coex_dm->cur_wl_rx_low_gain_en) { for (i = 0; i < ARRAY_SIZE(wl_rx_low_gain_on); i++) rtw_write32(rtwdev, REG_AGCRSSI, wl_rx_low_gain_on[i]); } else { for (i = 0; i < ARRAY_SIZE(wl_rx_low_gain_off); i++) rtw_write32(rtwdev, REG_AGCRSSI, wl_rx_low_gain_off[i]); } } static u8 rtw8723d_pwrtrack_get_limit_ofdm(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 tx_rate = dm_info->tx_rate; u8 limit_ofdm = 30; switch (tx_rate) { case DESC_RATE1M...DESC_RATE5_5M: case DESC_RATE11M: break; case DESC_RATE6M...DESC_RATE48M: limit_ofdm = 36; break; case DESC_RATE54M: limit_ofdm = 34; break; case DESC_RATEMCS0...DESC_RATEMCS2: limit_ofdm = 38; break; case DESC_RATEMCS3...DESC_RATEMCS4: limit_ofdm = 36; break; case DESC_RATEMCS5...DESC_RATEMCS7: limit_ofdm = 34; break; default: rtw_warn(rtwdev, "pwrtrack unhandled tx_rate 0x%x\n", tx_rate); break; } return limit_ofdm; } static void rtw8723d_set_iqk_matrix_by_result(struct rtw_dev *rtwdev, u32 ofdm_swing, u8 rf_path) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; s32 ele_A, ele_D, ele_C; s32 ele_A_ext, ele_C_ext, ele_D_ext; s32 iqk_result_x; s32 iqk_result_y; s32 value32; switch (rf_path) { default: case RF_PATH_A: iqk_result_x = dm_info->iqk.result.s1_x; iqk_result_y = dm_info->iqk.result.s1_y; break; case RF_PATH_B: iqk_result_x = dm_info->iqk.result.s0_x; iqk_result_y = dm_info->iqk.result.s0_y; break; } /* new element D */ ele_D = OFDM_SWING_D(ofdm_swing); iqk_mult(iqk_result_x, ele_D, &ele_D_ext); /* new element A */ iqk_result_x = iqkxy_to_s32(iqk_result_x); ele_A = iqk_mult(iqk_result_x, ele_D, &ele_A_ext); /* new element C */ iqk_result_y = iqkxy_to_s32(iqk_result_y); ele_C = iqk_mult(iqk_result_y, ele_D, &ele_C_ext); switch (rf_path) { case RF_PATH_A: default: /* write new elements A, C, D, and element B is always 0 */ value32 = BIT_SET_TXIQ_ELM_ACD(ele_A, ele_C, ele_D); rtw_write32(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE, value32); value32 = BIT_SET_TXIQ_ELM_C1(ele_C); rtw_write32_mask(rtwdev, REG_TXIQK_MATRIXA_LSB2_11N, MASKH4BITS, value32); value32 = rtw_read32(rtwdev, REG_OFDM_0_ECCA_THRESHOLD); value32 &= ~BIT_MASK_OFDM0_EXTS; value32 |= BIT_SET_OFDM0_EXTS(ele_A_ext, ele_C_ext, ele_D_ext); rtw_write32(rtwdev, REG_OFDM_0_ECCA_THRESHOLD, value32); break; case RF_PATH_B: /* write new elements A, C, D, and element B is always 0 */ rtw_write32_mask(rtwdev, REG_TXIQ_CD_S0, BIT_MASK_TXIQ_D_S0, ele_D); rtw_write32_mask(rtwdev, REG_TXIQ_CD_S0, BIT_MASK_TXIQ_C_S0, ele_C); rtw_write32_mask(rtwdev, REG_TXIQ_AB_S0, BIT_MASK_TXIQ_A_S0, ele_A); rtw_write32_mask(rtwdev, REG_TXIQ_CD_S0, BIT_MASK_TXIQ_D_EXT_S0, ele_D_ext); rtw_write32_mask(rtwdev, REG_TXIQ_AB_S0, BIT_MASK_TXIQ_A_EXT_S0, ele_A_ext); rtw_write32_mask(rtwdev, REG_TXIQ_CD_S0, BIT_MASK_TXIQ_C_EXT_S0, ele_C_ext); break; } } static void rtw8723d_set_iqk_matrix(struct rtw_dev *rtwdev, s8 ofdm_index, u8 rf_path) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; s32 value32; u32 ofdm_swing; if (ofdm_index >= RTW_OFDM_SWING_TABLE_SIZE) ofdm_index = RTW_OFDM_SWING_TABLE_SIZE - 1; else if (ofdm_index < 0) ofdm_index = 0; ofdm_swing = rtw8723d_ofdm_swing_table[ofdm_index]; if (dm_info->iqk.done) { rtw8723d_set_iqk_matrix_by_result(rtwdev, ofdm_swing, rf_path); return; } switch (rf_path) { case RF_PATH_A: default: rtw_write32(rtwdev, REG_OFDM_0_XA_TX_IQ_IMBALANCE, ofdm_swing); rtw_write32_mask(rtwdev, REG_TXIQK_MATRIXA_LSB2_11N, MASKH4BITS, 0x00); value32 = rtw_read32(rtwdev, REG_OFDM_0_ECCA_THRESHOLD); value32 &= ~BIT_MASK_OFDM0_EXTS; rtw_write32(rtwdev, REG_OFDM_0_ECCA_THRESHOLD, value32); break; case RF_PATH_B: /* image S1:c80 to S0:Cd0 and Cd4 */ rtw_write32_mask(rtwdev, REG_TXIQ_AB_S0, BIT_MASK_TXIQ_A_S0, OFDM_SWING_A(ofdm_swing)); rtw_write32_mask(rtwdev, REG_TXIQ_AB_S0, BIT_MASK_TXIQ_B_S0, OFDM_SWING_B(ofdm_swing)); rtw_write32_mask(rtwdev, REG_TXIQ_CD_S0, BIT_MASK_TXIQ_C_S0, OFDM_SWING_C(ofdm_swing)); rtw_write32_mask(rtwdev, REG_TXIQ_CD_S0, BIT_MASK_TXIQ_D_S0, OFDM_SWING_D(ofdm_swing)); rtw_write32_mask(rtwdev, REG_TXIQ_CD_S0, BIT_MASK_TXIQ_D_EXT_S0, 0x0); rtw_write32_mask(rtwdev, REG_TXIQ_CD_S0, BIT_MASK_TXIQ_C_EXT_S0, 0x0); rtw_write32_mask(rtwdev, REG_TXIQ_AB_S0, BIT_MASK_TXIQ_A_EXT_S0, 0x0); break; } } static void rtw8723d_pwrtrack_set_ofdm_pwr(struct rtw_dev *rtwdev, s8 swing_idx, s8 txagc_idx) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; dm_info->txagc_remnant_ofdm = txagc_idx; rtw8723d_set_iqk_matrix(rtwdev, swing_idx, RF_PATH_A); rtw8723d_set_iqk_matrix(rtwdev, swing_idx, RF_PATH_B); } static void rtw8723d_pwrtrack_set_cck_pwr(struct rtw_dev *rtwdev, s8 swing_idx, s8 txagc_idx) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; dm_info->txagc_remnant_cck = txagc_idx; rtw_write32_mask(rtwdev, 0xab4, 0x000007FF, rtw8723d_cck_swing_table[swing_idx]); } static void rtw8723d_pwrtrack_set(struct rtw_dev *rtwdev, u8 path) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; struct rtw_hal *hal = &rtwdev->hal; u8 limit_ofdm; u8 limit_cck = 40; s8 final_ofdm_swing_index; s8 final_cck_swing_index; limit_ofdm = rtw8723d_pwrtrack_get_limit_ofdm(rtwdev); final_ofdm_swing_index = RTW_DEF_OFDM_SWING_INDEX + dm_info->delta_power_index[path]; final_cck_swing_index = RTW_DEF_CCK_SWING_INDEX + dm_info->delta_power_index[path]; if (final_ofdm_swing_index > limit_ofdm) rtw8723d_pwrtrack_set_ofdm_pwr(rtwdev, limit_ofdm, final_ofdm_swing_index - limit_ofdm); else if (final_ofdm_swing_index < 0) rtw8723d_pwrtrack_set_ofdm_pwr(rtwdev, 0, final_ofdm_swing_index); else rtw8723d_pwrtrack_set_ofdm_pwr(rtwdev, final_ofdm_swing_index, 0); if (final_cck_swing_index > limit_cck) rtw8723d_pwrtrack_set_cck_pwr(rtwdev, limit_cck, final_cck_swing_index - limit_cck); else if (final_cck_swing_index < 0) rtw8723d_pwrtrack_set_cck_pwr(rtwdev, 0, final_cck_swing_index); else rtw8723d_pwrtrack_set_cck_pwr(rtwdev, final_cck_swing_index, 0); rtw_phy_set_tx_power_level(rtwdev, hal->current_channel); } static void rtw8723d_pwrtrack_set_xtal(struct rtw_dev *rtwdev, u8 therm_path, u8 delta) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; const struct rtw_pwr_track_tbl *tbl = rtwdev->chip->pwr_track_tbl; const s8 *pwrtrk_xtal; s8 xtal_cap; if (dm_info->thermal_avg[therm_path] > rtwdev->efuse.thermal_meter[therm_path]) pwrtrk_xtal = tbl->pwrtrk_xtal_p; else pwrtrk_xtal = tbl->pwrtrk_xtal_n; xtal_cap = rtwdev->efuse.crystal_cap & 0x3F; xtal_cap = clamp_t(s8, xtal_cap + pwrtrk_xtal[delta], 0, 0x3F); rtw_write32_mask(rtwdev, REG_AFE_CTRL3, BIT_MASK_XTAL, xtal_cap | (xtal_cap << 6)); } static void rtw8723d_phy_pwrtrack(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; struct rtw_swing_table swing_table; u8 thermal_value, delta, path; bool do_iqk = false; rtw_phy_config_swing_table(rtwdev, &swing_table); if (rtwdev->efuse.thermal_meter[0] == 0xff) return; thermal_value = rtw_read_rf(rtwdev, RF_PATH_A, RF_T_METER, 0xfc00); rtw_phy_pwrtrack_avg(rtwdev, thermal_value, RF_PATH_A); do_iqk = rtw_phy_pwrtrack_need_iqk(rtwdev); if (do_iqk) rtw8723d_lck(rtwdev); if (dm_info->pwr_trk_init_trigger) dm_info->pwr_trk_init_trigger = false; else if (!rtw_phy_pwrtrack_thermal_changed(rtwdev, thermal_value, RF_PATH_A)) goto iqk; delta = rtw_phy_pwrtrack_get_delta(rtwdev, RF_PATH_A); delta = min_t(u8, delta, RTW_PWR_TRK_TBL_SZ - 1); for (path = 0; path < rtwdev->hal.rf_path_num; path++) { s8 delta_cur, delta_last; delta_last = dm_info->delta_power_index[path]; delta_cur = rtw_phy_pwrtrack_get_pwridx(rtwdev, &swing_table, path, RF_PATH_A, delta); if (delta_last == delta_cur) continue; dm_info->delta_power_index[path] = delta_cur; rtw8723d_pwrtrack_set(rtwdev, path); } rtw8723d_pwrtrack_set_xtal(rtwdev, RF_PATH_A, delta); iqk: if (do_iqk) rtw8723d_phy_calibration(rtwdev); } static void rtw8723d_pwr_track(struct rtw_dev *rtwdev) { struct rtw_efuse *efuse = &rtwdev->efuse; struct rtw_dm_info *dm_info = &rtwdev->dm_info; if (efuse->power_track_type != 0) return; if (!dm_info->pwr_trk_triggered) { rtw_write_rf(rtwdev, RF_PATH_A, RF_T_METER, GENMASK(17, 16), 0x03); dm_info->pwr_trk_triggered = true; return; } rtw8723d_phy_pwrtrack(rtwdev); dm_info->pwr_trk_triggered = false; } +static void rtw8723d_fill_txdesc_checksum(struct rtw_dev *rtwdev, + struct rtw_tx_pkt_info *pkt_info, + u8 *txdesc) +{ + size_t words = 32 / 2; /* calculate the first 32 bytes (16 words) */ + __le16 chksum = 0; + __le16 *data = (__le16 *)(txdesc); + struct rtw_tx_desc *tx_desc = (struct rtw_tx_desc *)txdesc; + + le32p_replace_bits(&tx_desc->w7, 0, RTW_TX_DESC_W7_TXDESC_CHECKSUM); + + while (words--) + chksum ^= *data++; + + chksum = ~chksum; + + le32p_replace_bits(&tx_desc->w7, __le16_to_cpu(chksum), + RTW_TX_DESC_W7_TXDESC_CHECKSUM); +} + static struct rtw_chip_ops rtw8723d_ops = { .phy_set_param = rtw8723d_phy_set_param, .read_efuse = rtw8723d_read_efuse, .query_rx_desc = rtw8723d_query_rx_desc, .set_channel = rtw8723d_set_channel, .mac_init = rtw8723d_mac_init, .shutdown = rtw8723d_shutdown, .read_rf = rtw_phy_read_rf_sipi, .write_rf = rtw_phy_write_rf_reg_sipi, .set_tx_power_index = rtw8723d_set_tx_power_index, .set_antenna = NULL, .cfg_ldo25 = rtw8723d_cfg_ldo25, .efuse_grant = rtw8723d_efuse_grant, .false_alarm_statistics = rtw8723d_false_alarm_statistics, .phy_calibration = rtw8723d_phy_calibration, .cck_pd_set = rtw8723d_phy_cck_pd_set, .pwr_track = rtw8723d_pwr_track, .config_bfee = NULL, .set_gid_table = NULL, .cfg_csi_rate = NULL, + .fill_txdesc_checksum = rtw8723d_fill_txdesc_checksum, .coex_set_init = rtw8723d_coex_cfg_init, .coex_set_ant_switch = NULL, .coex_set_gnt_fix = rtw8723d_coex_cfg_gnt_fix, .coex_set_gnt_debug = rtw8723d_coex_cfg_gnt_debug, .coex_set_rfe_type = rtw8723d_coex_cfg_rfe_type, .coex_set_wl_tx_power = rtw8723d_coex_cfg_wl_tx_power, .coex_set_wl_rx_gain = rtw8723d_coex_cfg_wl_rx_gain, }; /* Shared-Antenna Coex Table */ static const struct coex_table_para table_sant_8723d[] = { {0xffffffff, 0xffffffff}, /* case-0 */ {0x55555555, 0x55555555}, {0x66555555, 0x66555555}, {0xaaaaaaaa, 0xaaaaaaaa}, {0x5a5a5a5a, 0x5a5a5a5a}, {0xfafafafa, 0xfafafafa}, /* case-5 */ {0x6a5a5555, 0xaaaaaaaa}, {0x6a5a56aa, 0x6a5a56aa}, {0x6a5a5a5a, 0x6a5a5a5a}, {0x66555555, 0x5a5a5a5a}, {0x66555555, 0x6a5a5a5a}, /* case-10 */ {0x66555555, 0x6a5a5aaa}, {0x66555555, 0x5a5a5aaa}, {0x66555555, 0x6aaa5aaa}, {0x66555555, 0xaaaa5aaa}, {0x66555555, 0xaaaaaaaa}, /* case-15 */ {0xffff55ff, 0xfafafafa}, {0xffff55ff, 0x6afa5afa}, {0xaaffffaa, 0xfafafafa}, {0xaa5555aa, 0x5a5a5a5a}, {0xaa5555aa, 0x6a5a5a5a}, /* case-20 */ {0xaa5555aa, 0xaaaaaaaa}, {0xffffffff, 0x5a5a5a5a}, {0xffffffff, 0x5a5a5a5a}, {0xffffffff, 0x55555555}, {0xffffffff, 0x5a5a5aaa}, /* case-25 */ {0x55555555, 0x5a5a5a5a}, {0x55555555, 0xaaaaaaaa}, {0x55555555, 0x6a5a6a5a}, {0x66556655, 0x66556655}, {0x66556aaa, 0x6a5a6aaa}, /* case-30 */ {0xffffffff, 0x5aaa5aaa}, {0x56555555, 0x5a5a5aaa}, }; /* Non-Shared-Antenna Coex Table */ static const struct coex_table_para table_nsant_8723d[] = { {0xffffffff, 0xffffffff}, /* case-100 */ {0x55555555, 0x55555555}, {0x66555555, 0x66555555}, {0xaaaaaaaa, 0xaaaaaaaa}, {0x5a5a5a5a, 0x5a5a5a5a}, {0xfafafafa, 0xfafafafa}, /* case-105 */ {0x5afa5afa, 0x5afa5afa}, {0x55555555, 0xfafafafa}, {0x66555555, 0xfafafafa}, {0x66555555, 0x5a5a5a5a}, {0x66555555, 0x6a5a5a5a}, /* case-110 */ {0x66555555, 0xaaaaaaaa}, {0xffff55ff, 0xfafafafa}, {0xffff55ff, 0x5afa5afa}, {0xffff55ff, 0xaaaaaaaa}, {0xffff55ff, 0xffff55ff}, /* case-115 */ {0xaaffffaa, 0x5afa5afa}, {0xaaffffaa, 0xaaaaaaaa}, {0xffffffff, 0xfafafafa}, {0xffffffff, 0x5afa5afa}, {0xffffffff, 0xaaaaaaaa}, /* case-120 */ {0x55ff55ff, 0x5afa5afa}, {0x55ff55ff, 0xaaaaaaaa}, {0x55ff55ff, 0x55ff55ff} }; /* Shared-Antenna TDMA */ static const struct coex_tdma_para tdma_sant_8723d[] = { { {0x00, 0x00, 0x00, 0x00, 0x00} }, /* case-0 */ { {0x61, 0x45, 0x03, 0x11, 0x11} }, /* case-1 */ { {0x61, 0x3a, 0x03, 0x11, 0x11} }, { {0x61, 0x30, 0x03, 0x11, 0x11} }, { {0x61, 0x20, 0x03, 0x11, 0x11} }, { {0x61, 0x10, 0x03, 0x11, 0x11} }, /* case-5 */ { {0x61, 0x45, 0x03, 0x11, 0x10} }, { {0x61, 0x3a, 0x03, 0x11, 0x10} }, { {0x61, 0x30, 0x03, 0x11, 0x10} }, { {0x61, 0x20, 0x03, 0x11, 0x10} }, { {0x61, 0x10, 0x03, 0x11, 0x10} }, /* case-10 */ { {0x61, 0x08, 0x03, 0x11, 0x14} }, { {0x61, 0x08, 0x03, 0x10, 0x14} }, { {0x51, 0x08, 0x03, 0x10, 0x54} }, { {0x51, 0x08, 0x03, 0x10, 0x55} }, { {0x51, 0x08, 0x07, 0x10, 0x54} }, /* case-15 */ { {0x51, 0x45, 0x03, 0x10, 0x50} }, { {0x51, 0x3a, 0x03, 0x10, 0x50} }, { {0x51, 0x30, 0x03, 0x10, 0x50} }, { {0x51, 0x20, 0x03, 0x10, 0x50} }, { {0x51, 0x10, 0x03, 0x10, 0x50} }, /* case-20 */ { {0x51, 0x4a, 0x03, 0x10, 0x50} }, { {0x51, 0x0c, 0x03, 0x10, 0x54} }, { {0x55, 0x08, 0x03, 0x10, 0x54} }, { {0x65, 0x10, 0x03, 0x11, 0x10} }, { {0x51, 0x10, 0x03, 0x10, 0x51} }, /* case-25 */ { {0x51, 0x08, 0x03, 0x10, 0x50} }, { {0x61, 0x08, 0x03, 0x11, 0x11} } }; /* Non-Shared-Antenna TDMA */ static const struct coex_tdma_para tdma_nsant_8723d[] = { { {0x00, 0x00, 0x00, 0x00, 0x01} }, /* case-100 */ { {0x61, 0x45, 0x03, 0x11, 0x11} }, /* case-101 */ { {0x61, 0x3a, 0x03, 0x11, 0x11} }, { {0x61, 0x30, 0x03, 0x11, 0x11} }, { {0x61, 0x20, 0x03, 0x11, 0x11} }, { {0x61, 0x10, 0x03, 0x11, 0x11} }, /* case-105 */ { {0x61, 0x45, 0x03, 0x11, 0x10} }, { {0x61, 0x3a, 0x03, 0x11, 0x10} }, { {0x61, 0x30, 0x03, 0x11, 0x10} }, { {0x61, 0x20, 0x03, 0x11, 0x10} }, { {0x61, 0x10, 0x03, 0x11, 0x10} }, /* case-110 */ { {0x61, 0x08, 0x03, 0x11, 0x14} }, { {0x61, 0x08, 0x03, 0x10, 0x14} }, { {0x51, 0x08, 0x03, 0x10, 0x54} }, { {0x51, 0x08, 0x03, 0x10, 0x55} }, { {0x51, 0x08, 0x07, 0x10, 0x54} }, /* case-115 */ { {0x51, 0x45, 0x03, 0x10, 0x50} }, { {0x51, 0x3a, 0x03, 0x10, 0x50} }, { {0x51, 0x30, 0x03, 0x10, 0x50} }, { {0x51, 0x20, 0x03, 0x10, 0x50} }, { {0x51, 0x10, 0x03, 0x10, 0x50} }, /* case-120 */ { {0x51, 0x08, 0x03, 0x10, 0x50} } }; /* rssi in percentage % (dbm = % - 100) */ static const u8 wl_rssi_step_8723d[] = {60, 50, 44, 30}; static const u8 bt_rssi_step_8723d[] = {30, 30, 30, 30}; static const struct coex_5g_afh_map afh_5g_8723d[] = { {0, 0, 0} }; static const struct rtw_hw_reg btg_reg_8723d = { .addr = REG_BTG_SEL, .mask = BIT_MASK_BTG_WL, }; /* wl_tx_dec_power, bt_tx_dec_power, wl_rx_gain, bt_rx_lna_constrain */ static const struct coex_rf_para rf_para_tx_8723d[] = { {0, 0, false, 7}, /* for normal */ {0, 10, false, 7}, /* for WL-CPT */ {1, 0, true, 4}, {1, 2, true, 4}, {1, 10, true, 4}, {1, 15, true, 4} }; static const struct coex_rf_para rf_para_rx_8723d[] = { {0, 0, false, 7}, /* for normal */ {0, 10, false, 7}, /* for WL-CPT */ {1, 0, true, 5}, {1, 2, true, 5}, {1, 10, true, 5}, {1, 15, true, 5} }; static const struct rtw_pwr_seq_cmd trans_carddis_to_cardemu_8723d[] = { {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(3) | BIT(7), 0}, {0x0086, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_SDIO, RTW_PWR_CMD_WRITE, BIT(0), 0}, {0x0086, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_SDIO, RTW_PWR_CMD_POLLING, BIT(1), BIT(1)}, {0x004A, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), 0}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(3) | BIT(4), 0}, {0x0023, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(4), 0}, {0x0301, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0}, {0xFFFF, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, 0, RTW_PWR_CMD_END, 0, 0}, }; static const struct rtw_pwr_seq_cmd trans_cardemu_to_act_8723d[] = { {0x0020, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, {0x0001, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_DELAY, 1, RTW_PWR_DELAY_MS}, {0x0000, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(5), 0}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, (BIT(4) | BIT(3) | BIT(2)), 0}, {0x0075, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, {0x0006, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_POLLING, BIT(1), BIT(1)}, {0x0075, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), 0}, {0x0006, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_POLLING, (BIT(1) | BIT(0)), 0}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(7), 0}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, (BIT(4) | BIT(3)), 0}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_POLLING, BIT(0), 0}, {0x0010, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(6), BIT(6)}, {0x0049, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(1), BIT(1)}, {0x0063, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(1), BIT(1)}, {0x0062, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(1), 0}, {0x0058, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, {0x005A, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(1), BIT(1)}, {0x0068, RTW_PWR_CUT_TEST_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(3), BIT(3)}, {0x0069, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(6), BIT(6)}, {0x001f, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0x00}, {0x0077, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0x00}, {0x001f, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0x07}, {0x0077, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0x07}, {0xFFFF, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, 0, RTW_PWR_CMD_END, 0, 0}, }; static const struct rtw_pwr_seq_cmd *card_enable_flow_8723d[] = { trans_carddis_to_cardemu_8723d, trans_cardemu_to_act_8723d, NULL }; static const struct rtw_pwr_seq_cmd trans_act_to_lps_8723d[] = { {0x0301, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0xFF}, {0x0522, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0xFF}, {0x05F8, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_POLLING, 0xFF, 0}, {0x05F9, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_POLLING, 0xFF, 0}, {0x05FA, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_POLLING, 0xFF, 0}, {0x05FB, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_POLLING, 0xFF, 0}, {0x0002, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), 0}, {0x0002, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_DELAY, 0, RTW_PWR_DELAY_US}, {0x0002, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(1), 0}, {0x0100, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0x03}, {0x0101, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(1), 0}, {0x0093, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0x00}, {0x0553, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(5), BIT(5)}, {0xFFFF, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, 0, RTW_PWR_CMD_END, 0, 0}, }; static const struct rtw_pwr_seq_cmd trans_act_to_pre_carddis_8723d[] = { {0x0003, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(2), 0}, {0x0080, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0}, {0xFFFF, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, 0, RTW_PWR_CMD_END, 0, 0}, }; static const struct rtw_pwr_seq_cmd trans_act_to_cardemu_8723d[] = { {0x0002, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), 0}, {0x0049, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(1), 0}, {0x0006, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(1), BIT(1)}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_POLLING, BIT(1), 0}, {0x0010, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(6), 0}, {0x0000, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(5), BIT(5)}, {0x0020, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), 0}, {0xFFFF, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, 0, RTW_PWR_CMD_END, 0, 0}, }; static const struct rtw_pwr_seq_cmd trans_cardemu_to_carddis_8723d[] = { {0x0007, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0x20}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(3) | BIT(4), BIT(3)}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(2), BIT(2)}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(3) | BIT(4), BIT(3) | BIT(4)}, {0x004A, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), 1}, {0x0023, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(4), BIT(4)}, {0x0086, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_SDIO, RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, {0x0086, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_SDIO, RTW_PWR_CMD_POLLING, BIT(1), 0}, {0xFFFF, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, 0, RTW_PWR_CMD_END, 0, 0}, }; static const struct rtw_pwr_seq_cmd trans_act_to_post_carddis_8723d[] = { {0x001D, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), 0}, {0x001D, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, {0x001C, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0x0E}, {0xFFFF, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, 0, RTW_PWR_CMD_END, 0, 0}, }; static const struct rtw_pwr_seq_cmd *card_disable_flow_8723d[] = { trans_act_to_lps_8723d, trans_act_to_pre_carddis_8723d, trans_act_to_cardemu_8723d, trans_cardemu_to_carddis_8723d, trans_act_to_post_carddis_8723d, NULL }; static const struct rtw_page_table page_table_8723d[] = { {12, 2, 2, 0, 1}, {12, 2, 2, 0, 1}, {12, 2, 2, 0, 1}, {12, 2, 2, 0, 1}, {12, 2, 2, 0, 1}, }; static const struct rtw_rqpn rqpn_table_8723d[] = { {RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_EXTRA, RTW_DMA_MAPPING_HIGH}, {RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_EXTRA, RTW_DMA_MAPPING_HIGH}, {RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH}, {RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH}, {RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_EXTRA, RTW_DMA_MAPPING_HIGH}, }; static const struct rtw_prioq_addrs prioq_addrs_8723d = { .prio[RTW_DMA_MAPPING_EXTRA] = { .rsvd = REG_RQPN_NPQ + 2, .avail = REG_RQPN_NPQ + 3, }, .prio[RTW_DMA_MAPPING_LOW] = { .rsvd = REG_RQPN + 1, .avail = REG_FIFOPAGE_CTRL_2 + 1, }, .prio[RTW_DMA_MAPPING_NORMAL] = { .rsvd = REG_RQPN_NPQ, .avail = REG_RQPN_NPQ + 1, }, .prio[RTW_DMA_MAPPING_HIGH] = { .rsvd = REG_RQPN, .avail = REG_FIFOPAGE_CTRL_2, }, .wsize = false, }; static const struct rtw_intf_phy_para pcie_gen1_param_8723d[] = { {0x0008, 0x4a22, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_ALL, RTW_INTF_PHY_PLATFORM_ALL}, {0x0009, 0x1000, RTW_IP_SEL_PHY, ~(RTW_INTF_PHY_CUT_A | RTW_INTF_PHY_CUT_B), RTW_INTF_PHY_PLATFORM_ALL}, {0xFFFF, 0x0000, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_ALL, RTW_INTF_PHY_PLATFORM_ALL}, }; static const struct rtw_intf_phy_para_table phy_para_table_8723d = { .gen1_para = pcie_gen1_param_8723d, .n_gen1_para = ARRAY_SIZE(pcie_gen1_param_8723d), }; static const struct rtw_hw_reg rtw8723d_dig[] = { [0] = { .addr = 0xc50, .mask = 0x7f }, [1] = { .addr = 0xc50, .mask = 0x7f }, }; static const struct rtw_hw_reg rtw8723d_dig_cck[] = { [0] = { .addr = 0xa0c, .mask = 0x3f00 }, }; static const struct rtw_rf_sipi_addr rtw8723d_rf_sipi_addr[] = { [RF_PATH_A] = { .hssi_1 = 0x820, .lssi_read = 0x8a0, .hssi_2 = 0x824, .lssi_read_pi = 0x8b8}, [RF_PATH_B] = { .hssi_1 = 0x828, .lssi_read = 0x8a4, .hssi_2 = 0x82c, .lssi_read_pi = 0x8bc}, }; static const struct rtw_ltecoex_addr rtw8723d_ltecoex_addr = { .ctrl = REG_LTECOEX_CTRL, .wdata = REG_LTECOEX_WRITE_DATA, .rdata = REG_LTECOEX_READ_DATA, }; static const struct rtw_rfe_def rtw8723d_rfe_defs[] = { [0] = { .phy_pg_tbl = &rtw8723d_bb_pg_tbl, .txpwr_lmt_tbl = &rtw8723d_txpwr_lmt_tbl,}, }; static const u8 rtw8723d_pwrtrk_2gb_n[] = { 0, 0, 1, 1, 1, 2, 2, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 10, 10 }; static const u8 rtw8723d_pwrtrk_2gb_p[] = { 0, 0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10 }; static const u8 rtw8723d_pwrtrk_2ga_n[] = { 0, 0, 1, 1, 1, 2, 2, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 10, 10 }; static const u8 rtw8723d_pwrtrk_2ga_p[] = { 0, 0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10 }; static const u8 rtw8723d_pwrtrk_2g_cck_b_n[] = { 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 11, 11, 11 }; static const u8 rtw8723d_pwrtrk_2g_cck_b_p[] = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 9, 9, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11 }; static const u8 rtw8723d_pwrtrk_2g_cck_a_n[] = { 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 11, 11, 11 }; static const u8 rtw8723d_pwrtrk_2g_cck_a_p[] = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 9, 9, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11 }; static const s8 rtw8723d_pwrtrk_xtal_n[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static const s8 rtw8723d_pwrtrk_xtal_p[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -10, -12, -14, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, -16 }; static const struct rtw_pwr_track_tbl rtw8723d_rtw_pwr_track_tbl = { .pwrtrk_2gb_n = rtw8723d_pwrtrk_2gb_n, .pwrtrk_2gb_p = rtw8723d_pwrtrk_2gb_p, .pwrtrk_2ga_n = rtw8723d_pwrtrk_2ga_n, .pwrtrk_2ga_p = rtw8723d_pwrtrk_2ga_p, .pwrtrk_2g_cckb_n = rtw8723d_pwrtrk_2g_cck_b_n, .pwrtrk_2g_cckb_p = rtw8723d_pwrtrk_2g_cck_b_p, .pwrtrk_2g_ccka_n = rtw8723d_pwrtrk_2g_cck_a_n, .pwrtrk_2g_ccka_p = rtw8723d_pwrtrk_2g_cck_a_p, .pwrtrk_xtal_p = rtw8723d_pwrtrk_xtal_p, .pwrtrk_xtal_n = rtw8723d_pwrtrk_xtal_n, }; static const struct rtw_reg_domain coex_info_hw_regs_8723d[] = { {0x948, MASKDWORD, RTW_REG_DOMAIN_MAC32}, {0x67, BIT(7), RTW_REG_DOMAIN_MAC8}, {0, 0, RTW_REG_DOMAIN_NL}, {0x964, BIT(1), RTW_REG_DOMAIN_MAC8}, {0x864, BIT(0), RTW_REG_DOMAIN_MAC8}, {0xab7, BIT(5), RTW_REG_DOMAIN_MAC8}, {0xa01, BIT(7), RTW_REG_DOMAIN_MAC8}, {0, 0, RTW_REG_DOMAIN_NL}, {0x430, MASKDWORD, RTW_REG_DOMAIN_MAC32}, {0x434, MASKDWORD, RTW_REG_DOMAIN_MAC32}, {0x42a, MASKLWORD, RTW_REG_DOMAIN_MAC16}, {0x426, MASKBYTE0, RTW_REG_DOMAIN_MAC8}, {0x45e, BIT(3), RTW_REG_DOMAIN_MAC8}, {0, 0, RTW_REG_DOMAIN_NL}, {0x4c6, BIT(4), RTW_REG_DOMAIN_MAC8}, {0x40, BIT(5), RTW_REG_DOMAIN_MAC8}, {0x550, MASKDWORD, RTW_REG_DOMAIN_MAC32}, {0x522, MASKBYTE0, RTW_REG_DOMAIN_MAC8}, {0x953, BIT(1), RTW_REG_DOMAIN_MAC8}, }; const struct rtw_chip_info rtw8723d_hw_spec = { .ops = &rtw8723d_ops, .id = RTW_CHIP_TYPE_8723D, .fw_name = "rtw88/rtw8723d_fw.bin", .wlan_cpu = RTW_WCPU_11N, .tx_pkt_desc_sz = 40, .tx_buf_desc_sz = 16, .rx_pkt_desc_sz = 24, .rx_buf_desc_sz = 8, .phy_efuse_size = 512, .log_efuse_size = 512, .ptct_efuse_size = 96 + 1, .txff_size = 32768, .rxff_size = 16384, + .rsvd_drv_pg_num = 8, .txgi_factor = 1, .is_pwr_by_rate_dec = true, .max_power_index = 0x3f, .csi_buf_pg_num = 0, .band = RTW_BAND_2G, - .page_size = 128, + .page_size = TX_PAGE_SIZE, .dig_min = 0x20, .ht_supported = true, .vht_supported = false, .lps_deep_mode_supported = 0, .sys_func_en = 0xFD, .pwr_on_seq = card_enable_flow_8723d, .pwr_off_seq = card_disable_flow_8723d, .page_table = page_table_8723d, .rqpn_table = rqpn_table_8723d, .prioq_addrs = &prioq_addrs_8723d, .intf_table = &phy_para_table_8723d, .dig = rtw8723d_dig, .dig_cck = rtw8723d_dig_cck, .rf_sipi_addr = {0x840, 0x844}, .rf_sipi_read_addr = rtw8723d_rf_sipi_addr, .fix_rf_phy_num = 2, .ltecoex_addr = &rtw8723d_ltecoex_addr, .mac_tbl = &rtw8723d_mac_tbl, .agc_tbl = &rtw8723d_agc_tbl, .bb_tbl = &rtw8723d_bb_tbl, .rf_tbl = {&rtw8723d_rf_a_tbl}, .rfe_defs = rtw8723d_rfe_defs, .rfe_defs_size = ARRAY_SIZE(rtw8723d_rfe_defs), .rx_ldpc = false, .pwr_track_tbl = &rtw8723d_rtw_pwr_track_tbl, .iqk_threshold = 8, .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, + .max_scan_ie_len = IEEE80211_MAX_DATA_LEN, .coex_para_ver = 0x2007022f, .bt_desired_ver = 0x2f, .scbd_support = true, .new_scbd10_def = true, .ble_hid_profile_support = false, .wl_mimo_ps_support = false, .pstdma_type = COEX_PSTDMA_FORCE_LPSOFF, .bt_rssi_type = COEX_BTRSSI_RATIO, .ant_isolation = 15, .rssi_tolerance = 2, .wl_rssi_step = wl_rssi_step_8723d, .bt_rssi_step = bt_rssi_step_8723d, .table_sant_num = ARRAY_SIZE(table_sant_8723d), .table_sant = table_sant_8723d, .table_nsant_num = ARRAY_SIZE(table_nsant_8723d), .table_nsant = table_nsant_8723d, .tdma_sant_num = ARRAY_SIZE(tdma_sant_8723d), .tdma_sant = tdma_sant_8723d, .tdma_nsant_num = ARRAY_SIZE(tdma_nsant_8723d), .tdma_nsant = tdma_nsant_8723d, .wl_rf_para_num = ARRAY_SIZE(rf_para_tx_8723d), .wl_rf_para_tx = rf_para_tx_8723d, .wl_rf_para_rx = rf_para_rx_8723d, .bt_afh_span_bw20 = 0x20, .bt_afh_span_bw40 = 0x30, .afh_5g_num = ARRAY_SIZE(afh_5g_8723d), .afh_5g = afh_5g_8723d, .btg_reg = &btg_reg_8723d, .coex_info_hw_regs_num = ARRAY_SIZE(coex_info_hw_regs_8723d), .coex_info_hw_regs = coex_info_hw_regs_8723d, }; EXPORT_SYMBOL(rtw8723d_hw_spec); MODULE_FIRMWARE("rtw88/rtw8723d_fw.bin"); MODULE_AUTHOR("Realtek Corporation"); MODULE_DESCRIPTION("Realtek 802.11n wireless 8723d driver"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sys/contrib/dev/rtw88/rtw8723d.h b/sys/contrib/dev/rtw88/rtw8723d.h index 4641f6e047b4..3642a2c7f80c 100644 --- a/sys/contrib/dev/rtw88/rtw8723d.h +++ b/sys/contrib/dev/rtw88/rtw8723d.h @@ -1,288 +1,305 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* Copyright(c) 2018-2019 Realtek Corporation */ #ifndef __RTW8723D_H__ #define __RTW8723D_H__ enum rtw8723d_path { PATH_S1, PATH_S0, PATH_NR, }; enum rtw8723d_iqk_round { IQK_ROUND_0, IQK_ROUND_1, IQK_ROUND_2, IQK_ROUND_HYBRID, IQK_ROUND_SIZE, IQK_ROUND_INVALID = 0xff, }; enum rtw8723d_iqk_result { IQK_S1_TX_X, IQK_S1_TX_Y, IQK_S1_RX_X, IQK_S1_RX_Y, IQK_S0_TX_X, IQK_S0_TX_Y, IQK_S0_RX_X, IQK_S0_RX_Y, IQK_NR, IQK_SX_NR = IQK_NR / PATH_NR, }; struct rtw8723de_efuse { u8 mac_addr[ETH_ALEN]; /* 0xd0 */ u8 vender_id[2]; u8 device_id[2]; u8 sub_vender_id[2]; u8 sub_device_id[2]; }; +struct rtw8723du_efuse { + u8 res4[48]; /* 0xd0 */ + u8 vender_id[2]; /* 0x100 */ + u8 product_id[2]; /* 0x102 */ + u8 usb_option; /* 0x104 */ + u8 mac_addr[ETH_ALEN]; /* 0x107 */ +}; + +struct rtw8723ds_efuse { + u8 res4[0x4a]; /* 0xd0 */ + u8 mac_addr[ETH_ALEN]; /* 0x11a */ +}; + struct rtw8723d_efuse { __le16 rtl_id; u8 rsvd[2]; u8 afe; u8 rsvd1[11]; /* power index for four RF paths */ struct rtw_txpwr_idx txpwr_idx_table[4]; u8 channel_plan; /* 0xb8 */ u8 xtal_k; u8 thermal_meter; u8 iqk_lck; u8 pa_type; /* 0xbc */ u8 lna_type_2g[2]; /* 0xbd */ u8 lna_type_5g[2]; u8 rf_board_option; u8 rf_feature_option; u8 rf_bt_setting; u8 eeprom_version; u8 eeprom_customer_id; u8 tx_bb_swing_setting_2g; u8 res_c7; u8 tx_pwr_calibrate_rate; u8 rf_antenna_option; /* 0xc9 */ u8 rfe_option; u8 country_code[2]; u8 res[3]; - struct rtw8723de_efuse e; + union { + struct rtw8723de_efuse e; + struct rtw8723du_efuse u; + struct rtw8723ds_efuse s; + }; }; extern const struct rtw_chip_info rtw8723d_hw_spec; /* phy status page0 */ #define GET_PHY_STAT_P0_PWDB(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x00), GENMASK(15, 8)) /* phy status page1 */ #define GET_PHY_STAT_P1_PWDB_A(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x00), GENMASK(15, 8)) #define GET_PHY_STAT_P1_PWDB_B(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x00), GENMASK(23, 16)) #define GET_PHY_STAT_P1_RF_MODE(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x03), GENMASK(29, 28)) #define GET_PHY_STAT_P1_L_RXSC(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x01), GENMASK(11, 8)) #define GET_PHY_STAT_P1_HT_RXSC(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x01), GENMASK(15, 12)) #define GET_PHY_STAT_P1_RXEVM_A(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x04), GENMASK(7, 0)) #define GET_PHY_STAT_P1_CFO_TAIL_A(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x05), GENMASK(7, 0)) #define GET_PHY_STAT_P1_RXSNR_A(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x06), GENMASK(7, 0)) static inline s32 iqkxy_to_s32(s32 val) { /* val is Q10.8 */ return sign_extend32(val, 9); } static inline s32 iqk_mult(s32 x, s32 y, s32 *ext) { /* x, y and return value are Q10.8 */ s32 t; t = x * y; if (ext) *ext = (t >> 7) & 0x1; /* Q.16 --> Q.9; get LSB of Q.9 */ return (t >> 8); /* Q.16 --> Q.8 */ } #define OFDM_SWING_A(swing) FIELD_GET(GENMASK(9, 0), swing) #define OFDM_SWING_B(swing) FIELD_GET(GENMASK(15, 10), swing) #define OFDM_SWING_C(swing) FIELD_GET(GENMASK(21, 16), swing) #define OFDM_SWING_D(swing) FIELD_GET(GENMASK(31, 22), swing) #define RTW_DEF_OFDM_SWING_INDEX 28 #define RTW_DEF_CCK_SWING_INDEX 28 #define MAX_TOLERANCE 5 #define IQK_TX_X_ERR 0x142 #define IQK_TX_Y_ERR 0x42 #define IQK_RX_X_UPPER 0x11a #define IQK_RX_X_LOWER 0xe6 #define IQK_RX_Y_LMT 0x1a #define IQK_TX_OK BIT(0) #define IQK_RX_OK BIT(1) #define PATH_IQK_RETRY 2 #define SPUR_THRES 0x16 #define CCK_DFIR_NR 3 #define DIS_3WIRE 0xccf000c0 #define EN_3WIRE 0xccc000c0 #define START_PSD 0x400000 #define FREQ_CH13 0xfccd #define FREQ_CH14 0xff9a #define RFCFGCH_CHANNEL_MASK GENMASK(7, 0) #define RFCFGCH_BW_MASK (BIT(11) | BIT(10)) #define RFCFGCH_BW_20M (BIT(11) | BIT(10)) #define RFCFGCH_BW_40M BIT(10) #define BIT_MASK_RFMOD BIT(0) #define BIT_LCK BIT(15) #define REG_GPIO_INTM 0x0048 #define REG_BTG_SEL 0x0067 #define BIT_MASK_BTG_WL BIT(7) #define REG_LTECOEX_PATH_CONTROL 0x0070 #define REG_LTECOEX_CTRL 0x07c0 #define REG_LTECOEX_WRITE_DATA 0x07c4 #define REG_LTECOEX_READ_DATA 0x07c8 #define REG_PSDFN 0x0808 #define REG_BB_PWR_SAV1_11N 0x0874 #define REG_ANA_PARAM1 0x0880 #define REG_ANALOG_P4 0x088c #define REG_PSDRPT 0x08b4 #define REG_FPGA1_RFMOD 0x0900 #define REG_BB_SEL_BTG 0x0948 #define REG_BBRX_DFIR 0x0954 #define BIT_MASK_RXBB_DFIR GENMASK(27, 24) #define BIT_RXBB_DFIR_EN BIT(19) #define REG_CCK0_SYS 0x0a00 #define BIT_CCK_SIDE_BAND BIT(4) #define REG_CCK_ANT_SEL_11N 0x0a04 #define REG_PWRTH 0x0a08 #define REG_CCK_FA_RST_11N 0x0a2c #define BIT_MASK_CCK_CNT_KEEP BIT(12) #define BIT_MASK_CCK_CNT_EN BIT(13) #define BIT_MASK_CCK_CNT_KPEN (BIT_MASK_CCK_CNT_KEEP | BIT_MASK_CCK_CNT_EN) #define BIT_MASK_CCK_FA_KEEP BIT(14) #define BIT_MASK_CCK_FA_EN BIT(15) #define BIT_MASK_CCK_FA_KPEN (BIT_MASK_CCK_FA_KEEP | BIT_MASK_CCK_FA_EN) #define REG_CCK_FA_LSB_11N 0x0a5c #define REG_CCK_FA_MSB_11N 0x0a58 #define REG_CCK_CCA_CNT_11N 0x0a60 #define BIT_MASK_CCK_FA_MSB GENMASK(7, 0) #define BIT_MASK_CCK_FA_LSB GENMASK(15, 8) #define REG_PWRTH2 0x0aa8 #define REG_CSRATIO 0x0aaa #define REG_OFDM_FA_HOLDC_11N 0x0c00 #define BIT_MASK_OFDM_FA_KEEP BIT(31) #define REG_BB_RX_PATH_11N 0x0c04 #define REG_TRMUX_11N 0x0c08 #define REG_OFDM_FA_RSTC_11N 0x0c0c #define BIT_MASK_OFDM_FA_RST BIT(31) #define REG_A_RXIQI 0x0c14 #define BIT_MASK_RXIQ_S1_X 0x000003FF #define BIT_MASK_RXIQ_S1_Y1 0x0000FC00 #define BIT_SET_RXIQ_S1_Y1(y) ((y) & 0x3F) #define REG_OFDM0_RXDSP 0x0c40 #define BIT_MASK_RXDSP GENMASK(28, 24) #define BIT_EN_RXDSP BIT(9) #define REG_OFDM_0_ECCA_THRESHOLD 0x0c4c #define BIT_MASK_OFDM0_EXT_A BIT(31) #define BIT_MASK_OFDM0_EXT_C BIT(29) #define BIT_MASK_OFDM0_EXTS (BIT(31) | BIT(29) | BIT(28)) #define BIT_SET_OFDM0_EXTS(a, c, d) (((a) << 31) | ((c) << 29) | ((d) << 28)) #define REG_OFDM0_XAAGC1 0x0c50 #define REG_OFDM0_XBAGC1 0x0c58 #define REG_AGCRSSI 0x0c78 #define REG_OFDM_0_XA_TX_IQ_IMBALANCE 0x0c80 #define BIT_MASK_TXIQ_ELM_A 0x03ff #define BIT_SET_TXIQ_ELM_ACD(a, c, d) (((d) << 22) | (((c) & 0x3F) << 16) | \ ((a) & 0x03ff)) #define BIT_MASK_TXIQ_ELM_C GENMASK(21, 16) #define BIT_SET_TXIQ_ELM_C2(c) ((c) & 0x3F) #define BIT_MASK_TXIQ_ELM_D GENMASK(31, 22) #define REG_TXIQK_MATRIXA_LSB2_11N 0x0c94 #define BIT_SET_TXIQ_ELM_C1(c) (((c) & 0x000003C0) >> 6) #define REG_RXIQK_MATRIX_LSB_11N 0x0ca0 #define BIT_MASK_RXIQ_S1_Y2 0xF0000000 #define BIT_SET_RXIQ_S1_Y2(y) (((y) >> 6) & 0xF) #define REG_TXIQ_AB_S0 0x0cd0 #define BIT_MASK_TXIQ_A_S0 0x000007FE #define BIT_MASK_TXIQ_A_EXT_S0 BIT(0) #define BIT_MASK_TXIQ_B_S0 0x0007E000 #define REG_TXIQ_CD_S0 0x0cd4 #define BIT_MASK_TXIQ_C_S0 0x000007FE #define BIT_MASK_TXIQ_C_EXT_S0 BIT(0) #define BIT_MASK_TXIQ_D_S0 GENMASK(22, 13) #define BIT_MASK_TXIQ_D_EXT_S0 BIT(12) #define REG_RXIQ_AB_S0 0x0cd8 #define BIT_MASK_RXIQ_X_S0 0x000003FF #define BIT_MASK_RXIQ_Y_S0 0x003FF000 #define REG_OFDM_FA_TYPE1_11N 0x0cf0 #define BIT_MASK_OFDM_FF_CNT GENMASK(15, 0) #define BIT_MASK_OFDM_SF_CNT GENMASK(31, 16) #define REG_OFDM_FA_RSTD_11N 0x0d00 #define BIT_MASK_OFDM_FA_RST1 BIT(27) #define BIT_MASK_OFDM_FA_KEEP1 BIT(31) #define REG_CTX 0x0d03 #define BIT_MASK_CTX_TYPE GENMASK(6, 4) #define REG_OFDM1_CFOTRK 0x0d2c #define BIT_EN_CFOTRK BIT(28) #define REG_OFDM1_CSI1 0x0d40 #define REG_OFDM1_CSI2 0x0d44 #define REG_OFDM1_CSI3 0x0d48 #define REG_OFDM1_CSI4 0x0d4c #define REG_OFDM_FA_TYPE2_11N 0x0da0 #define BIT_MASK_OFDM_CCA_CNT GENMASK(15, 0) #define BIT_MASK_OFDM_PF_CNT GENMASK(31, 16) #define REG_OFDM_FA_TYPE3_11N 0x0da4 #define BIT_MASK_OFDM_RI_CNT GENMASK(15, 0) #define BIT_MASK_OFDM_CRC_CNT GENMASK(31, 16) #define REG_OFDM_FA_TYPE4_11N 0x0da8 #define BIT_MASK_OFDM_MNS_CNT GENMASK(15, 0) #define REG_FPGA0_IQK_11N 0x0e28 #define BIT_MASK_IQK_MOD 0xffffff00 #define EN_IQK 0x808000 #define RST_IQK 0x000000 #define REG_TXIQK_TONE_A_11N 0x0e30 #define REG_RXIQK_TONE_A_11N 0x0e34 #define REG_TXIQK_PI_A_11N 0x0e38 #define REG_RXIQK_PI_A_11N 0x0e3c #define REG_TXIQK_11N 0x0e40 #define BIT_SET_TXIQK_11N(x, y) (0x80007C00 | ((x) << 16) | (y)) #define REG_RXIQK_11N 0x0e44 #define REG_IQK_AGC_PTS_11N 0x0e48 #define REG_IQK_AGC_RSP_11N 0x0e4c #define REG_TX_IQK_TONE_B 0x0e50 #define REG_RX_IQK_TONE_B 0x0e54 #define REG_IQK_RES_TX 0x0e94 #define BIT_MASK_RES_TX GENMASK(25, 16) #define REG_IQK_RES_TY 0x0e9c #define BIT_MASK_RES_TY GENMASK(25, 16) #define REG_IQK_RES_RX 0x0ea4 #define BIT_MASK_RES_RX GENMASK(25, 16) #define REG_IQK_RES_RY 0x0eac #define BIT_IQK_TX_FAIL BIT(28) #define BIT_IQK_RX_FAIL BIT(27) #define BIT_IQK_DONE BIT(26) #define BIT_MASK_RES_RY GENMASK(25, 16) #define REG_PAGE_F_RST_11N 0x0f14 #define BIT_MASK_F_RST_ALL BIT(16) #define REG_IGI_C_11N 0x0f84 #define REG_IGI_D_11N 0x0f88 #define REG_HT_CRC32_CNT_11N 0x0f90 #define BIT_MASK_HT_CRC_OK GENMASK(15, 0) #define BIT_MASK_HT_CRC_ERR GENMASK(31, 16) #define REG_OFDM_CRC32_CNT_11N 0x0f94 #define BIT_MASK_OFDM_LCRC_OK GENMASK(15, 0) #define BIT_MASK_OFDM_LCRC_ERR GENMASK(31, 16) #define REG_HT_CRC32_CNT_11N_AGG 0x0fb8 #endif diff --git a/sys/contrib/dev/rtw88/rtw8723ds.c b/sys/contrib/dev/rtw88/rtw8723ds.c new file mode 100644 index 000000000000..e5b6960ba0a0 --- /dev/null +++ b/sys/contrib/dev/rtw88/rtw8723ds.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) Martin Blumenstingl + */ + +#include +#include +#include +#include "main.h" +#include "rtw8723d.h" +#include "sdio.h" + +static const struct sdio_device_id rtw_8723ds_id_table[] = { + { + SDIO_DEVICE(SDIO_VENDOR_ID_REALTEK, + SDIO_DEVICE_ID_REALTEK_RTW8723DS_1ANT), + .driver_data = (kernel_ulong_t)&rtw8723d_hw_spec, + }, + { + SDIO_DEVICE(SDIO_VENDOR_ID_REALTEK, + SDIO_DEVICE_ID_REALTEK_RTW8723DS_2ANT), + .driver_data = (kernel_ulong_t)&rtw8723d_hw_spec, + }, + {} +}; +MODULE_DEVICE_TABLE(sdio, rtw_8723ds_id_table); + +static struct sdio_driver rtw_8723ds_driver = { + .name = "rtw_8723ds", + .probe = rtw_sdio_probe, + .remove = rtw_sdio_remove, + .id_table = rtw_8723ds_id_table, + .drv = { + .pm = &rtw_sdio_pm_ops, + .shutdown = rtw_sdio_shutdown, + } +}; +module_sdio_driver(rtw_8723ds_driver); + +MODULE_AUTHOR("Martin Blumenstingl "); +MODULE_DESCRIPTION("Realtek 802.11n wireless 8723ds driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sys/contrib/dev/rtw88/rtw8723du.c b/sys/contrib/dev/rtw88/rtw8723du.c new file mode 100644 index 000000000000..eebe8026c6c3 --- /dev/null +++ b/sys/contrib/dev/rtw88/rtw8723du.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2018-2019 Realtek Corporation + */ + +#include +#include +#include "main.h" +#include "rtw8723d.h" +#include "usb.h" + +static const struct usb_device_id rtw_8723du_id_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xd723, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8723d_hw_spec) }, /* 8723DU 1*1 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0xd611, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8723d_hw_spec) }, /* Edimax EW-7611ULB V2 */ + { }, +}; +MODULE_DEVICE_TABLE(usb, rtw_8723du_id_table); + +static int rtw8723du_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return rtw_usb_probe(intf, id); +} + +static struct usb_driver rtw_8723du_driver = { + .name = "rtw_8723du", + .id_table = rtw_8723du_id_table, + .probe = rtw8723du_probe, + .disconnect = rtw_usb_disconnect, +#if defined(__FreeBSD__) && defined(__notyet__) + .bsddriver.name = KBUILD_MODNAME, +#endif +}; +module_usb_driver(rtw_8723du_driver); + +MODULE_AUTHOR("Hans Ulli Kroll "); +MODULE_DESCRIPTION("Realtek 802.11n wireless 8723du driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sys/contrib/dev/rtw88/rtw8821c.c b/sys/contrib/dev/rtw88/rtw8821c.c index 6275c9ddc7d9..01aa0d1e90dc 100644 --- a/sys/contrib/dev/rtw88/rtw8821c.c +++ b/sys/contrib/dev/rtw88/rtw8821c.c @@ -1,1971 +1,2013 @@ // 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 "tx.h" #include "rx.h" #include "phy.h" #include "rtw8821c.h" #include "rtw8821c_table.h" #include "mac.h" #include "reg.h" #include "debug.h" #include "bf.h" #include "regd.h" static const s8 lna_gain_table_0[8] = {22, 8, -6, -22, -31, -40, -46, -52}; static const s8 lna_gain_table_1[16] = {10, 6, 2, -2, -6, -10, -14, -17, -20, -24, -28, -31, -34, -37, -40, -44}; static void rtw8821ce_efuse_parsing(struct rtw_efuse *efuse, struct rtw8821c_efuse *map) { ether_addr_copy(efuse->addr, map->e.mac_addr); } +static void rtw8821cu_efuse_parsing(struct rtw_efuse *efuse, + struct rtw8821c_efuse *map) +{ + ether_addr_copy(efuse->addr, map->u.mac_addr); +} + +static void rtw8821cs_efuse_parsing(struct rtw_efuse *efuse, + struct rtw8821c_efuse *map) +{ + ether_addr_copy(efuse->addr, map->s.mac_addr); +} + enum rtw8821ce_rf_set { SWITCH_TO_BTG, SWITCH_TO_WLG, SWITCH_TO_WLA, SWITCH_TO_BT, }; static int rtw8821c_read_efuse(struct rtw_dev *rtwdev, u8 *log_map) { + struct rtw_hal *hal = &rtwdev->hal; struct rtw_efuse *efuse = &rtwdev->efuse; struct rtw8821c_efuse *map; int i; map = (struct rtw8821c_efuse *)log_map; - efuse->rfe_option = map->rfe_option; + efuse->rfe_option = map->rfe_option & 0x1f; efuse->rf_board_option = map->rf_board_option; efuse->crystal_cap = map->xtal_k; efuse->pa_type_2g = map->pa_type; efuse->pa_type_5g = map->pa_type; efuse->lna_type_2g = map->lna_type_2g[0]; efuse->lna_type_5g = map->lna_type_5g[0]; efuse->channel_plan = map->channel_plan; efuse->country_code[0] = map->country_code[0]; efuse->country_code[1] = map->country_code[1]; efuse->bt_setting = map->rf_bt_setting; efuse->regd = map->rf_board_option & 0x7; efuse->thermal_meter[0] = map->thermal_meter; efuse->thermal_meter_k = map->thermal_meter; efuse->tx_bb_swing_setting_2g = map->tx_bb_swing_setting_2g; efuse->tx_bb_swing_setting_5g = map->tx_bb_swing_setting_5g; + hal->pkg_type = map->rfe_option & BIT(5) ? 1 : 0; + + switch (efuse->rfe_option) { + case 0x2: + case 0x4: + case 0x7: + case 0xa: + case 0xc: + case 0xf: + hal->rfe_btg = true; + break; + } + for (i = 0; i < 4; i++) efuse->txpwr_idx_table[i] = map->txpwr_idx_table[i]; if (rtwdev->efuse.rfe_option == 2 || rtwdev->efuse.rfe_option == 4) efuse->txpwr_idx_table[0].pwr_idx_2g = map->txpwr_idx_table[1].pwr_idx_2g; switch (rtw_hci_type(rtwdev)) { case RTW_HCI_TYPE_PCIE: rtw8821ce_efuse_parsing(efuse, map); break; + case RTW_HCI_TYPE_USB: + rtw8821cu_efuse_parsing(efuse, map); + break; + case RTW_HCI_TYPE_SDIO: + rtw8821cs_efuse_parsing(efuse, map); + break; default: /* unsupported now */ return -ENOTSUPP; } return 0; } static const u32 rtw8821c_txscale_tbl[] = { 0x081, 0x088, 0x090, 0x099, 0x0a2, 0x0ac, 0x0b6, 0x0c0, 0x0cc, 0x0d8, 0x0e5, 0x0f2, 0x101, 0x110, 0x120, 0x131, 0x143, 0x156, 0x16a, 0x180, 0x197, 0x1af, 0x1c8, 0x1e3, 0x200, 0x21e, 0x23e, 0x261, 0x285, 0x2ab, 0x2d3, 0x2fe, 0x32b, 0x35c, 0x38e, 0x3c4, 0x3fe }; static u8 rtw8821c_get_swing_index(struct rtw_dev *rtwdev) { u8 i = 0; u32 swing, table_value; swing = rtw_read32_mask(rtwdev, REG_TXSCALE_A, 0xffe00000); for (i = 0; i < ARRAY_SIZE(rtw8821c_txscale_tbl); i++) { table_value = rtw8821c_txscale_tbl[i]; if (swing == table_value) break; } return i; } static void rtw8821c_pwrtrack_init(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 swing_idx = rtw8821c_get_swing_index(rtwdev); if (swing_idx >= ARRAY_SIZE(rtw8821c_txscale_tbl)) dm_info->default_ofdm_index = 24; else dm_info->default_ofdm_index = swing_idx; ewma_thermal_init(&dm_info->avg_thermal[RF_PATH_A]); dm_info->delta_power_index[RF_PATH_A] = 0; dm_info->delta_power_index_last[RF_PATH_A] = 0; dm_info->pwr_trk_triggered = false; dm_info->pwr_trk_init_trigger = true; dm_info->thermal_meter_k = rtwdev->efuse.thermal_meter_k; } static void rtw8821c_phy_bf_init(struct rtw_dev *rtwdev) { rtw_bf_phy_init(rtwdev); /* Grouping bitmap parameters */ rtw_write32(rtwdev, 0x1C94, 0xAFFFAFFF); } static void rtw8821c_phy_set_param(struct rtw_dev *rtwdev) { struct rtw_hal *hal = &rtwdev->hal; u8 crystal_cap, val; /* power on BB/RF domain */ val = rtw_read8(rtwdev, REG_SYS_FUNC_EN); val |= BIT_FEN_PCIEA; rtw_write8(rtwdev, REG_SYS_FUNC_EN, val); /* toggle BB reset */ val |= BIT_FEN_BB_RSTB | BIT_FEN_BB_GLB_RST; rtw_write8(rtwdev, REG_SYS_FUNC_EN, val); val &= ~(BIT_FEN_BB_RSTB | BIT_FEN_BB_GLB_RST); rtw_write8(rtwdev, REG_SYS_FUNC_EN, val); val |= BIT_FEN_BB_RSTB | BIT_FEN_BB_GLB_RST; rtw_write8(rtwdev, REG_SYS_FUNC_EN, val); rtw_write8(rtwdev, REG_RF_CTRL, BIT_RF_EN | BIT_RF_RSTB | BIT_RF_SDM_RSTB); usleep_range(10, 11); rtw_write8(rtwdev, REG_WLRF1 + 3, BIT_RF_EN | BIT_RF_RSTB | BIT_RF_SDM_RSTB); usleep_range(10, 11); /* pre init before header files config */ rtw_write32_clr(rtwdev, REG_RXPSEL, BIT_RX_PSEL_RST); rtw_phy_load_tables(rtwdev); crystal_cap = rtwdev->efuse.crystal_cap & 0x3F; rtw_write32_mask(rtwdev, REG_AFE_XTAL_CTRL, 0x7e000000, crystal_cap); rtw_write32_mask(rtwdev, REG_AFE_PLL_CTRL, 0x7e, crystal_cap); rtw_write32_mask(rtwdev, REG_CCK0_FAREPORT, BIT(18) | BIT(22), 0); /* post init after header files config */ rtw_write32_set(rtwdev, REG_RXPSEL, BIT_RX_PSEL_RST); hal->ch_param[0] = rtw_read32_mask(rtwdev, REG_TXSF2, MASKDWORD); hal->ch_param[1] = rtw_read32_mask(rtwdev, REG_TXSF6, MASKDWORD); hal->ch_param[2] = rtw_read32_mask(rtwdev, REG_TXFILTER, MASKDWORD); rtw_phy_init(rtwdev); rtwdev->dm_info.cck_pd_default = rtw_read8(rtwdev, REG_CSRATIO) & 0x1f; rtw8821c_pwrtrack_init(rtwdev); rtw8821c_phy_bf_init(rtwdev); } static int rtw8821c_mac_init(struct rtw_dev *rtwdev) { u32 value32; u16 pre_txcnt; /* protocol configuration */ rtw_write8(rtwdev, REG_AMPDU_MAX_TIME_V1, WLAN_AMPDU_MAX_TIME); rtw_write8_set(rtwdev, REG_TX_HANG_CTRL, BIT_EN_EOF_V1); pre_txcnt = WLAN_PRE_TXCNT_TIME_TH | BIT_EN_PRECNT; rtw_write8(rtwdev, REG_PRECNT_CTRL, (u8)(pre_txcnt & 0xFF)); rtw_write8(rtwdev, REG_PRECNT_CTRL + 1, (u8)(pre_txcnt >> 8)); value32 = WLAN_RTS_LEN_TH | (WLAN_RTS_TX_TIME_TH << 8) | (WLAN_MAX_AGG_PKT_LIMIT << 16) | (WLAN_RTS_MAX_AGG_PKT_LIMIT << 24); rtw_write32(rtwdev, REG_PROT_MODE_CTRL, value32); rtw_write16(rtwdev, REG_BAR_MODE_CTRL + 2, WLAN_BAR_RETRY_LIMIT | WLAN_RA_TRY_RATE_AGG_LIMIT << 8); rtw_write8(rtwdev, REG_FAST_EDCA_VOVI_SETTING, FAST_EDCA_VO_TH); rtw_write8(rtwdev, REG_FAST_EDCA_VOVI_SETTING + 2, FAST_EDCA_VI_TH); rtw_write8(rtwdev, REG_FAST_EDCA_BEBK_SETTING, FAST_EDCA_BE_TH); rtw_write8(rtwdev, REG_FAST_EDCA_BEBK_SETTING + 2, FAST_EDCA_BK_TH); rtw_write8_set(rtwdev, REG_INIRTS_RATE_SEL, BIT(5)); /* EDCA configuration */ rtw_write8_clr(rtwdev, REG_TIMER0_SRC_SEL, BIT_TSFT_SEL_TIMER0); rtw_write16(rtwdev, REG_TXPAUSE, 0); rtw_write8(rtwdev, REG_SLOT, WLAN_SLOT_TIME); rtw_write8(rtwdev, REG_PIFS, WLAN_PIFS_TIME); rtw_write32(rtwdev, REG_SIFS, WLAN_SIFS_CFG); rtw_write16(rtwdev, REG_EDCA_VO_PARAM + 2, WLAN_VO_TXOP_LIMIT); rtw_write16(rtwdev, REG_EDCA_VI_PARAM + 2, WLAN_VI_TXOP_LIMIT); rtw_write32(rtwdev, REG_RD_NAV_NXT, WLAN_NAV_CFG); rtw_write16(rtwdev, REG_RXTSF_OFFSET_CCK, WLAN_RX_TSF_CFG); /* Set beacon cotnrol - enable TSF and other related functions */ rtw_write8_set(rtwdev, REG_BCN_CTRL, BIT_EN_BCN_FUNCTION); /* Set send beacon related registers */ rtw_write32(rtwdev, REG_TBTT_PROHIBIT, WLAN_TBTT_TIME); rtw_write8(rtwdev, REG_DRVERLYINT, WLAN_DRV_EARLY_INT); rtw_write8(rtwdev, REG_BCNDMATIM, WLAN_BCN_DMA_TIME); rtw_write8_clr(rtwdev, REG_TX_PTCL_CTRL + 1, BIT_SIFS_BK_EN >> 8); /* WMAC configuration */ rtw_write32(rtwdev, REG_RXFLTMAP0, WLAN_RX_FILTER0); rtw_write16(rtwdev, REG_RXFLTMAP2, WLAN_RX_FILTER2); rtw_write32(rtwdev, REG_RCR, WLAN_RCR_CFG); rtw_write8(rtwdev, REG_RX_PKT_LIMIT, WLAN_RXPKT_MAX_SZ_512); rtw_write8(rtwdev, REG_TCR + 2, WLAN_TX_FUNC_CFG2); rtw_write8(rtwdev, REG_TCR + 1, WLAN_TX_FUNC_CFG1); rtw_write8(rtwdev, REG_ACKTO_CCK, 0x40); rtw_write8_set(rtwdev, REG_WMAC_TRXPTCL_CTL_H, BIT(1)); rtw_write8_set(rtwdev, REG_SND_PTCL_CTRL, BIT_DIS_CHK_VHTSIGB_CRC); rtw_write32(rtwdev, REG_WMAC_OPTION_FUNCTION + 8, WLAN_MAC_OPT_FUNC2); rtw_write8(rtwdev, REG_WMAC_OPTION_FUNCTION + 4, WLAN_MAC_OPT_NORM_FUNC1); return 0; } static void rtw8821c_cfg_ldo25(struct rtw_dev *rtwdev, bool enable) { u8 ldo_pwr; ldo_pwr = rtw_read8(rtwdev, REG_LDO_EFUSE_CTRL + 3); ldo_pwr = enable ? ldo_pwr | BIT(7) : ldo_pwr & ~BIT(7); rtw_write8(rtwdev, REG_LDO_EFUSE_CTRL + 3, ldo_pwr); } static void rtw8821c_switch_rf_set(struct rtw_dev *rtwdev, u8 rf_set) { u32 reg; rtw_write32_set(rtwdev, REG_DMEM_CTRL, BIT_WL_RST); rtw_write32_set(rtwdev, REG_SYS_CTRL, BIT_FEN_EN); reg = rtw_read32(rtwdev, REG_RFECTL); switch (rf_set) { case SWITCH_TO_BTG: reg |= B_BTG_SWITCH; reg &= ~(B_CTRL_SWITCH | B_WL_SWITCH | B_WLG_SWITCH | B_WLA_SWITCH); rtw_write32_mask(rtwdev, REG_ENRXCCA, MASKBYTE2, BTG_CCA); rtw_write32_mask(rtwdev, REG_ENTXCCK, MASKLWORD, BTG_LNA); break; case SWITCH_TO_WLG: reg |= B_WL_SWITCH | B_WLG_SWITCH; reg &= ~(B_BTG_SWITCH | B_CTRL_SWITCH | B_WLA_SWITCH); rtw_write32_mask(rtwdev, REG_ENRXCCA, MASKBYTE2, WLG_CCA); rtw_write32_mask(rtwdev, REG_ENTXCCK, MASKLWORD, WLG_LNA); break; case SWITCH_TO_WLA: reg |= B_WL_SWITCH | B_WLA_SWITCH; reg &= ~(B_BTG_SWITCH | B_CTRL_SWITCH | B_WLG_SWITCH); break; case SWITCH_TO_BT: default: break; } rtw_write32(rtwdev, REG_RFECTL, reg); } static void rtw8821c_set_channel_rf(struct rtw_dev *rtwdev, u8 channel, u8 bw) { + struct rtw_hal *hal = &rtwdev->hal; u32 rf_reg18; rf_reg18 = rtw_read_rf(rtwdev, RF_PATH_A, 0x18, RFREG_MASK); rf_reg18 &= ~(RF18_BAND_MASK | RF18_CHANNEL_MASK | RF18_RFSI_MASK | RF18_BW_MASK); rf_reg18 |= (channel <= 14 ? RF18_BAND_2G : RF18_BAND_5G); rf_reg18 |= (channel & RF18_CHANNEL_MASK); if (channel >= 100 && channel <= 140) rf_reg18 |= RF18_RFSI_GE; else if (channel > 140) rf_reg18 |= RF18_RFSI_GT; switch (bw) { case RTW_CHANNEL_WIDTH_5: case RTW_CHANNEL_WIDTH_10: case RTW_CHANNEL_WIDTH_20: default: rf_reg18 |= RF18_BW_20M; break; case RTW_CHANNEL_WIDTH_40: rf_reg18 |= RF18_BW_40M; break; case RTW_CHANNEL_WIDTH_80: rf_reg18 |= RF18_BW_80M; break; } if (channel <= 14) { - if (rtwdev->efuse.rfe_option == 0) - rtw8821c_switch_rf_set(rtwdev, SWITCH_TO_WLG); - else if (rtwdev->efuse.rfe_option == 2 || - rtwdev->efuse.rfe_option == 4) + if (hal->rfe_btg) rtw8821c_switch_rf_set(rtwdev, SWITCH_TO_BTG); + else + rtw8821c_switch_rf_set(rtwdev, SWITCH_TO_WLG); rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTDBG, BIT(6), 0x1); rtw_write_rf(rtwdev, RF_PATH_A, 0x64, 0xf, 0xf); } else { rtw8821c_switch_rf_set(rtwdev, SWITCH_TO_WLA); rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTDBG, BIT(6), 0x0); } rtw_write_rf(rtwdev, RF_PATH_A, 0x18, RFREG_MASK, rf_reg18); rtw_write_rf(rtwdev, RF_PATH_A, RF_XTALX2, BIT(19), 0); rtw_write_rf(rtwdev, RF_PATH_A, RF_XTALX2, BIT(19), 1); } static void rtw8821c_set_channel_rxdfir(struct rtw_dev *rtwdev, u8 bw) { if (bw == RTW_CHANNEL_WIDTH_40) { /* RX DFIR for BW40 */ rtw_write32_mask(rtwdev, REG_ACBB0, BIT(29) | BIT(28), 0x2); rtw_write32_mask(rtwdev, REG_ACBBRXFIR, BIT(29) | BIT(28), 0x2); rtw_write32_mask(rtwdev, REG_TXDFIR, BIT(31), 0x0); rtw_write32_mask(rtwdev, REG_CHFIR, BIT(31), 0x0); } else if (bw == RTW_CHANNEL_WIDTH_80) { /* RX DFIR for BW80 */ rtw_write32_mask(rtwdev, REG_ACBB0, BIT(29) | BIT(28), 0x2); rtw_write32_mask(rtwdev, REG_ACBBRXFIR, BIT(29) | BIT(28), 0x1); rtw_write32_mask(rtwdev, REG_TXDFIR, BIT(31), 0x0); rtw_write32_mask(rtwdev, REG_CHFIR, BIT(31), 0x1); } else { /* RX DFIR for BW20, BW10 and BW5 */ rtw_write32_mask(rtwdev, REG_ACBB0, BIT(29) | BIT(28), 0x2); rtw_write32_mask(rtwdev, REG_ACBBRXFIR, BIT(29) | BIT(28), 0x2); rtw_write32_mask(rtwdev, REG_TXDFIR, BIT(31), 0x1); rtw_write32_mask(rtwdev, REG_CHFIR, BIT(31), 0x0); } } static void rtw8821c_set_channel_bb(struct rtw_dev *rtwdev, u8 channel, u8 bw, u8 primary_ch_idx) { struct rtw_hal *hal = &rtwdev->hal; u32 val32; if (channel <= 14) { rtw_write32_mask(rtwdev, REG_RXPSEL, BIT(28), 0x1); rtw_write32_mask(rtwdev, REG_CCK_CHECK, BIT(7), 0x0); rtw_write32_mask(rtwdev, REG_ENTXCCK, BIT(18), 0x0); rtw_write32_mask(rtwdev, REG_RXCCAMSK, 0x0000FC00, 15); rtw_write32_mask(rtwdev, REG_TXSCALE_A, 0xf00, 0x0); rtw_write32_mask(rtwdev, REG_CLKTRK, 0x1ffe0000, 0x96a); if (channel == 14) { rtw_write32_mask(rtwdev, REG_TXSF2, MASKDWORD, 0x0000b81c); rtw_write32_mask(rtwdev, REG_TXSF6, MASKLWORD, 0x0000); rtw_write32_mask(rtwdev, REG_TXFILTER, MASKDWORD, 0x00003667); } else { rtw_write32_mask(rtwdev, REG_TXSF2, MASKDWORD, hal->ch_param[0]); rtw_write32_mask(rtwdev, REG_TXSF6, MASKLWORD, hal->ch_param[1] & MASKLWORD); rtw_write32_mask(rtwdev, REG_TXFILTER, MASKDWORD, hal->ch_param[2]); } } else if (channel > 35) { rtw_write32_mask(rtwdev, REG_ENTXCCK, BIT(18), 0x1); rtw_write32_mask(rtwdev, REG_CCK_CHECK, BIT(7), 0x1); rtw_write32_mask(rtwdev, REG_RXPSEL, BIT(28), 0x0); rtw_write32_mask(rtwdev, REG_RXCCAMSK, 0x0000FC00, 15); if (channel >= 36 && channel <= 64) rtw_write32_mask(rtwdev, REG_TXSCALE_A, 0xf00, 0x1); else if (channel >= 100 && channel <= 144) rtw_write32_mask(rtwdev, REG_TXSCALE_A, 0xf00, 0x2); else if (channel >= 149) rtw_write32_mask(rtwdev, REG_TXSCALE_A, 0xf00, 0x3); if (channel >= 36 && channel <= 48) rtw_write32_mask(rtwdev, REG_CLKTRK, 0x1ffe0000, 0x494); else if (channel >= 52 && channel <= 64) rtw_write32_mask(rtwdev, REG_CLKTRK, 0x1ffe0000, 0x453); else if (channel >= 100 && channel <= 116) rtw_write32_mask(rtwdev, REG_CLKTRK, 0x1ffe0000, 0x452); else if (channel >= 118 && channel <= 177) rtw_write32_mask(rtwdev, REG_CLKTRK, 0x1ffe0000, 0x412); } switch (bw) { case RTW_CHANNEL_WIDTH_20: default: val32 = rtw_read32_mask(rtwdev, REG_ADCCLK, MASKDWORD); val32 &= 0xffcffc00; val32 |= 0x10010000; rtw_write32_mask(rtwdev, REG_ADCCLK, MASKDWORD, val32); rtw_write32_mask(rtwdev, REG_ADC160, BIT(30), 0x1); break; case RTW_CHANNEL_WIDTH_40: if (primary_ch_idx == 1) rtw_write32_set(rtwdev, REG_RXSB, BIT(4)); else rtw_write32_clr(rtwdev, REG_RXSB, BIT(4)); val32 = rtw_read32_mask(rtwdev, REG_ADCCLK, MASKDWORD); val32 &= 0xff3ff300; val32 |= 0x20020000 | ((primary_ch_idx & 0xf) << 2) | RTW_CHANNEL_WIDTH_40; rtw_write32_mask(rtwdev, REG_ADCCLK, MASKDWORD, val32); rtw_write32_mask(rtwdev, REG_ADC160, BIT(30), 0x1); break; case RTW_CHANNEL_WIDTH_80: val32 = rtw_read32_mask(rtwdev, REG_ADCCLK, MASKDWORD); val32 &= 0xfcffcf00; val32 |= 0x40040000 | ((primary_ch_idx & 0xf) << 2) | RTW_CHANNEL_WIDTH_80; rtw_write32_mask(rtwdev, REG_ADCCLK, MASKDWORD, val32); rtw_write32_mask(rtwdev, REG_ADC160, BIT(30), 0x1); break; case RTW_CHANNEL_WIDTH_5: val32 = rtw_read32_mask(rtwdev, REG_ADCCLK, MASKDWORD); val32 &= 0xefcefc00; val32 |= 0x200240; rtw_write32_mask(rtwdev, REG_ADCCLK, MASKDWORD, val32); rtw_write32_mask(rtwdev, REG_ADC160, BIT(30), 0x0); rtw_write32_mask(rtwdev, REG_ADC40, BIT(31), 0x1); break; case RTW_CHANNEL_WIDTH_10: val32 = rtw_read32_mask(rtwdev, REG_ADCCLK, MASKDWORD); val32 &= 0xefcefc00; val32 |= 0x300380; rtw_write32_mask(rtwdev, REG_ADCCLK, MASKDWORD, val32); rtw_write32_mask(rtwdev, REG_ADC160, BIT(30), 0x0); rtw_write32_mask(rtwdev, REG_ADC40, BIT(31), 0x1); break; } } static u32 rtw8821c_get_bb_swing(struct rtw_dev *rtwdev, u8 channel) { struct rtw_efuse efuse = rtwdev->efuse; u8 tx_bb_swing; u32 swing2setting[4] = {0x200, 0x16a, 0x101, 0x0b6}; tx_bb_swing = channel <= 14 ? efuse.tx_bb_swing_setting_2g : efuse.tx_bb_swing_setting_5g; if (tx_bb_swing > 9) tx_bb_swing = 0; return swing2setting[(tx_bb_swing / 3)]; } static void rtw8821c_set_channel_bb_swing(struct rtw_dev *rtwdev, u8 channel, u8 bw, u8 primary_ch_idx) { rtw_write32_mask(rtwdev, REG_TXSCALE_A, GENMASK(31, 21), rtw8821c_get_bb_swing(rtwdev, channel)); rtw8821c_pwrtrack_init(rtwdev); } static void rtw8821c_set_channel(struct rtw_dev *rtwdev, u8 channel, u8 bw, u8 primary_chan_idx) { rtw8821c_set_channel_bb(rtwdev, channel, bw, primary_chan_idx); rtw8821c_set_channel_bb_swing(rtwdev, channel, bw, primary_chan_idx); rtw_set_channel_mac(rtwdev, channel, bw, primary_chan_idx); rtw8821c_set_channel_rf(rtwdev, channel, bw); rtw8821c_set_channel_rxdfir(rtwdev, bw); } static s8 get_cck_rx_pwr(struct rtw_dev *rtwdev, u8 lna_idx, u8 vga_idx) { struct rtw_efuse *efuse = &rtwdev->efuse; const s8 *lna_gain_table; int lna_gain_table_size; s8 rx_pwr_all = 0; s8 lna_gain = 0; if (efuse->rfe_option == 0) { lna_gain_table = lna_gain_table_0; lna_gain_table_size = ARRAY_SIZE(lna_gain_table_0); } else { lna_gain_table = lna_gain_table_1; lna_gain_table_size = ARRAY_SIZE(lna_gain_table_1); } if (lna_idx >= lna_gain_table_size) { rtw_warn(rtwdev, "incorrect lna index (%d)\n", lna_idx); return -120; } lna_gain = lna_gain_table[lna_idx]; rx_pwr_all = lna_gain - 2 * vga_idx; return rx_pwr_all; } static void query_phy_status_page0(struct rtw_dev *rtwdev, u8 *phy_status, struct rtw_rx_pkt_stat *pkt_stat) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; s8 rx_power; u8 lna_idx = 0; u8 vga_idx = 0; vga_idx = GET_PHY_STAT_P0_VGA(phy_status); lna_idx = FIELD_PREP(BIT_LNA_H_MASK, GET_PHY_STAT_P0_LNA_H(phy_status)) | FIELD_PREP(BIT_LNA_L_MASK, GET_PHY_STAT_P0_LNA_L(phy_status)); rx_power = get_cck_rx_pwr(rtwdev, lna_idx, vga_idx); pkt_stat->rx_power[RF_PATH_A] = rx_power; pkt_stat->rssi = rtw_phy_rf_power_2_rssi(pkt_stat->rx_power, 1); dm_info->rssi[RF_PATH_A] = pkt_stat->rssi; pkt_stat->bw = RTW_CHANNEL_WIDTH_20; pkt_stat->signal_power = rx_power; } static void query_phy_status_page1(struct rtw_dev *rtwdev, u8 *phy_status, struct rtw_rx_pkt_stat *pkt_stat) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 rxsc, bw; s8 min_rx_power = -120; if (pkt_stat->rate > DESC_RATE11M && pkt_stat->rate < DESC_RATEMCS0) rxsc = GET_PHY_STAT_P1_L_RXSC(phy_status); else rxsc = GET_PHY_STAT_P1_HT_RXSC(phy_status); if (rxsc >= 1 && rxsc <= 8) bw = RTW_CHANNEL_WIDTH_20; else if (rxsc >= 9 && rxsc <= 12) bw = RTW_CHANNEL_WIDTH_40; else if (rxsc >= 13) bw = RTW_CHANNEL_WIDTH_80; else bw = GET_PHY_STAT_P1_RF_MODE(phy_status); pkt_stat->rx_power[RF_PATH_A] = GET_PHY_STAT_P1_PWDB_A(phy_status) - 110; pkt_stat->rssi = rtw_phy_rf_power_2_rssi(pkt_stat->rx_power, 1); dm_info->rssi[RF_PATH_A] = pkt_stat->rssi; pkt_stat->bw = bw; pkt_stat->signal_power = max(pkt_stat->rx_power[RF_PATH_A], min_rx_power); } static void query_phy_status(struct rtw_dev *rtwdev, u8 *phy_status, struct rtw_rx_pkt_stat *pkt_stat) { u8 page; page = *phy_status & 0xf; switch (page) { case 0: query_phy_status_page0(rtwdev, phy_status, pkt_stat); break; case 1: query_phy_status_page1(rtwdev, phy_status, pkt_stat); break; default: rtw_warn(rtwdev, "unused phy status page (%d)\n", page); return; } } static void rtw8821c_query_rx_desc(struct rtw_dev *rtwdev, u8 *rx_desc, struct rtw_rx_pkt_stat *pkt_stat, struct ieee80211_rx_status *rx_status) { struct ieee80211_hdr *hdr; u32 desc_sz = rtwdev->chip->rx_pkt_desc_sz; u8 *phy_status = NULL; memset(pkt_stat, 0, sizeof(*pkt_stat)); pkt_stat->phy_status = GET_RX_DESC_PHYST(rx_desc); pkt_stat->icv_err = GET_RX_DESC_ICV_ERR(rx_desc); pkt_stat->crc_err = GET_RX_DESC_CRC32(rx_desc); pkt_stat->decrypted = !GET_RX_DESC_SWDEC(rx_desc) && GET_RX_DESC_ENC_TYPE(rx_desc) != RX_DESC_ENC_NONE; pkt_stat->is_c2h = GET_RX_DESC_C2H(rx_desc); pkt_stat->pkt_len = GET_RX_DESC_PKT_LEN(rx_desc); pkt_stat->drv_info_sz = GET_RX_DESC_DRV_INFO_SIZE(rx_desc); pkt_stat->shift = GET_RX_DESC_SHIFT(rx_desc); pkt_stat->rate = GET_RX_DESC_RX_RATE(rx_desc); pkt_stat->cam_id = GET_RX_DESC_MACID(rx_desc); pkt_stat->ppdu_cnt = GET_RX_DESC_PPDU_CNT(rx_desc); pkt_stat->tsf_low = GET_RX_DESC_TSFL(rx_desc); /* drv_info_sz is in unit of 8-bytes */ pkt_stat->drv_info_sz *= 8; /* c2h cmd pkt's rx/phy status is not interested */ if (pkt_stat->is_c2h) return; hdr = (struct ieee80211_hdr *)(rx_desc + desc_sz + pkt_stat->shift + pkt_stat->drv_info_sz); if (pkt_stat->phy_status) { phy_status = rx_desc + desc_sz + pkt_stat->shift; query_phy_status(rtwdev, phy_status, pkt_stat); } rtw_rx_fill_rx_status(rtwdev, pkt_stat, hdr, rx_status, phy_status); } static void rtw8821c_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, u8 rs) { struct rtw_hal *hal = &rtwdev->hal; static const u32 offset_txagc[2] = {0x1d00, 0x1d80}; static u32 phy_pwr_idx; u8 rate, rate_idx, pwr_index, shift; int j; for (j = 0; j < rtw_rate_size[rs]; j++) { rate = rtw_rate_section[rs][j]; pwr_index = hal->tx_pwr_tbl[path][rate]; shift = rate & 0x3; phy_pwr_idx |= ((u32)pwr_index << (shift * 8)); if (shift == 0x3 || rate == DESC_RATEVHT1SS_MCS9) { rate_idx = rate & 0xfc; rtw_write32(rtwdev, offset_txagc[path] + rate_idx, phy_pwr_idx); phy_pwr_idx = 0; } } } static void rtw8821c_set_tx_power_index(struct rtw_dev *rtwdev) { struct rtw_hal *hal = &rtwdev->hal; int rs, path; for (path = 0; path < hal->rf_path_num; path++) { for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++) { if (rs == RTW_RATE_SECTION_HT_2S || rs == RTW_RATE_SECTION_VHT_2S) continue; rtw8821c_set_tx_power_index_by_rate(rtwdev, path, rs); } } } static void rtw8821c_false_alarm_statistics(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u32 cck_enable; u32 cck_fa_cnt; u32 ofdm_fa_cnt; u32 crc32_cnt; u32 cca32_cnt; cck_enable = rtw_read32(rtwdev, REG_RXPSEL) & BIT(28); cck_fa_cnt = rtw_read16(rtwdev, REG_FA_CCK); ofdm_fa_cnt = rtw_read16(rtwdev, REG_FA_OFDM); dm_info->cck_fa_cnt = cck_fa_cnt; dm_info->ofdm_fa_cnt = ofdm_fa_cnt; if (cck_enable) dm_info->total_fa_cnt += cck_fa_cnt; dm_info->total_fa_cnt = ofdm_fa_cnt; crc32_cnt = rtw_read32(rtwdev, REG_CRC_CCK); dm_info->cck_ok_cnt = FIELD_GET(GENMASK(15, 0), crc32_cnt); dm_info->cck_err_cnt = FIELD_GET(GENMASK(31, 16), crc32_cnt); crc32_cnt = rtw_read32(rtwdev, REG_CRC_OFDM); dm_info->ofdm_ok_cnt = FIELD_GET(GENMASK(15, 0), crc32_cnt); dm_info->ofdm_err_cnt = FIELD_GET(GENMASK(31, 16), crc32_cnt); crc32_cnt = rtw_read32(rtwdev, REG_CRC_HT); dm_info->ht_ok_cnt = FIELD_GET(GENMASK(15, 0), crc32_cnt); dm_info->ht_err_cnt = FIELD_GET(GENMASK(31, 16), crc32_cnt); crc32_cnt = rtw_read32(rtwdev, REG_CRC_VHT); dm_info->vht_ok_cnt = FIELD_GET(GENMASK(15, 0), crc32_cnt); dm_info->vht_err_cnt = FIELD_GET(GENMASK(31, 16), crc32_cnt); cca32_cnt = rtw_read32(rtwdev, REG_CCA_OFDM); dm_info->ofdm_cca_cnt = FIELD_GET(GENMASK(31, 16), cca32_cnt); dm_info->total_cca_cnt = dm_info->ofdm_cca_cnt; if (cck_enable) { cca32_cnt = rtw_read32(rtwdev, REG_CCA_CCK); dm_info->cck_cca_cnt = FIELD_GET(GENMASK(15, 0), cca32_cnt); dm_info->total_cca_cnt += dm_info->cck_cca_cnt; } rtw_write32_set(rtwdev, REG_FAS, BIT(17)); rtw_write32_clr(rtwdev, REG_FAS, BIT(17)); rtw_write32_clr(rtwdev, REG_RXDESC, BIT(15)); rtw_write32_set(rtwdev, REG_RXDESC, BIT(15)); rtw_write32_set(rtwdev, REG_CNTRST, BIT(0)); rtw_write32_clr(rtwdev, REG_CNTRST, BIT(0)); } static void rtw8821c_do_iqk(struct rtw_dev *rtwdev) { static int do_iqk_cnt; struct rtw_iqk_para para = {.clear = 0, .segment_iqk = 0}; u32 rf_reg, iqk_fail_mask; int counter; bool reload; if (rtw_is_assoc(rtwdev)) para.segment_iqk = 1; rtw_fw_do_iqk(rtwdev, ¶); for (counter = 0; counter < 300; counter++) { rf_reg = rtw_read_rf(rtwdev, RF_PATH_A, RF_DTXLOK, RFREG_MASK); if (rf_reg == 0xabcde) break; msleep(20); } rtw_write_rf(rtwdev, RF_PATH_A, RF_DTXLOK, RFREG_MASK, 0x0); reload = !!rtw_read32_mask(rtwdev, REG_IQKFAILMSK, BIT(16)); iqk_fail_mask = rtw_read32_mask(rtwdev, REG_IQKFAILMSK, GENMASK(7, 0)); rtw_dbg(rtwdev, RTW_DBG_PHY, "iqk counter=%d reload=%d do_iqk_cnt=%d n_iqk_fail(mask)=0x%02x\n", counter, reload, ++do_iqk_cnt, iqk_fail_mask); } static void rtw8821c_phy_calibration(struct rtw_dev *rtwdev) { rtw8821c_do_iqk(rtwdev); } /* for coex */ static void rtw8821c_coex_cfg_init(struct rtw_dev *rtwdev) { /* enable TBTT nterrupt */ rtw_write8_set(rtwdev, REG_BCN_CTRL, BIT_EN_BCN_FUNCTION); /* BT report packet sample rate */ rtw_write8_mask(rtwdev, REG_BT_TDMA_TIME, BIT_MASK_SAMPLE_RATE, 0x5); /* enable BT counter statistics */ rtw_write8(rtwdev, REG_BT_STAT_CTRL, BT_CNT_ENABLE); /* enable PTA (3-wire function form BT side) */ rtw_write32_set(rtwdev, REG_GPIO_MUXCFG, BIT_BT_PTA_EN); rtw_write32_set(rtwdev, REG_GPIO_MUXCFG, BIT_PO_BT_PTA_PINS); /* enable PTA (tx/rx signal form WiFi side) */ rtw_write8_set(rtwdev, REG_QUEUE_CTRL, BIT_PTA_WL_TX_EN); /* wl tx signal to PTA not case EDCCA */ rtw_write8_clr(rtwdev, REG_QUEUE_CTRL, BIT_PTA_EDCCA_EN); /* GNT_BT=1 while select both */ rtw_write16_set(rtwdev, REG_BT_COEX_V2, BIT_GNT_BT_POLARITY); /* beacon queue always hi-pri */ rtw_write8_mask(rtwdev, REG_BT_COEX_TABLE_H + 3, BIT_BCN_QUEUE, BCN_PRI_EN); } static void rtw8821c_coex_cfg_ant_switch(struct rtw_dev *rtwdev, u8 ctrl_type, u8 pos_type) { struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_dm *coex_dm = &coex->dm; struct rtw_coex_rfe *coex_rfe = &coex->rfe; u32 switch_status = FIELD_PREP(CTRL_TYPE_MASK, ctrl_type) | pos_type; bool polarity_inverse; u8 regval = 0; if (switch_status == coex_dm->cur_switch_status) return; if (coex_rfe->wlg_at_btg) { ctrl_type = COEX_SWITCH_CTRL_BY_BBSW; if (coex_rfe->ant_switch_polarity) pos_type = COEX_SWITCH_TO_WLA; else pos_type = COEX_SWITCH_TO_WLG_BT; } coex_dm->cur_switch_status = switch_status; if (coex_rfe->ant_switch_diversity && ctrl_type == COEX_SWITCH_CTRL_BY_BBSW) ctrl_type = COEX_SWITCH_CTRL_BY_ANTDIV; polarity_inverse = (coex_rfe->ant_switch_polarity == 1); switch (ctrl_type) { default: case COEX_SWITCH_CTRL_BY_BBSW: rtw_write32_clr(rtwdev, REG_LED_CFG, BIT_DPDT_SEL_EN); rtw_write32_set(rtwdev, REG_LED_CFG, BIT_DPDT_WL_SEL); /* BB SW, DPDT use RFE_ctrl8 and RFE_ctrl9 as ctrl pin */ rtw_write8_mask(rtwdev, REG_RFE_CTRL8, BIT_MASK_RFE_SEL89, DPDT_CTRL_PIN); if (pos_type == COEX_SWITCH_TO_WLG_BT) { if (coex_rfe->rfe_module_type != 0x4 && coex_rfe->rfe_module_type != 0x2) regval = 0x3; else regval = (!polarity_inverse ? 0x2 : 0x1); } else if (pos_type == COEX_SWITCH_TO_WLG) { regval = (!polarity_inverse ? 0x2 : 0x1); } else { regval = (!polarity_inverse ? 0x1 : 0x2); } rtw_write32_mask(rtwdev, REG_RFE_CTRL8, BIT_MASK_R_RFE_SEL_15, regval); break; case COEX_SWITCH_CTRL_BY_PTA: rtw_write32_clr(rtwdev, REG_LED_CFG, BIT_DPDT_SEL_EN); rtw_write32_set(rtwdev, REG_LED_CFG, BIT_DPDT_WL_SEL); /* PTA, DPDT use RFE_ctrl8 and RFE_ctrl9 as ctrl pin */ rtw_write8_mask(rtwdev, REG_RFE_CTRL8, BIT_MASK_RFE_SEL89, PTA_CTRL_PIN); regval = (!polarity_inverse ? 0x2 : 0x1); rtw_write32_mask(rtwdev, REG_RFE_CTRL8, BIT_MASK_R_RFE_SEL_15, regval); break; case COEX_SWITCH_CTRL_BY_ANTDIV: rtw_write32_clr(rtwdev, REG_LED_CFG, BIT_DPDT_SEL_EN); rtw_write32_set(rtwdev, REG_LED_CFG, BIT_DPDT_WL_SEL); rtw_write8_mask(rtwdev, REG_RFE_CTRL8, BIT_MASK_RFE_SEL89, ANTDIC_CTRL_PIN); break; case COEX_SWITCH_CTRL_BY_MAC: rtw_write32_set(rtwdev, REG_LED_CFG, BIT_DPDT_SEL_EN); regval = (!polarity_inverse ? 0x0 : 0x1); rtw_write8_mask(rtwdev, REG_PAD_CTRL1, BIT_SW_DPDT_SEL_DATA, regval); break; case COEX_SWITCH_CTRL_BY_FW: rtw_write32_clr(rtwdev, REG_LED_CFG, BIT_DPDT_SEL_EN); rtw_write32_set(rtwdev, REG_LED_CFG, BIT_DPDT_WL_SEL); break; case COEX_SWITCH_CTRL_BY_BT: rtw_write32_clr(rtwdev, REG_LED_CFG, BIT_DPDT_SEL_EN); rtw_write32_clr(rtwdev, REG_LED_CFG, BIT_DPDT_WL_SEL); break; } if (ctrl_type == COEX_SWITCH_CTRL_BY_BT) { rtw_write8_clr(rtwdev, REG_CTRL_TYPE, BIT_CTRL_TYPE1); rtw_write8_clr(rtwdev, REG_CTRL_TYPE, BIT_CTRL_TYPE2); } else { rtw_write8_set(rtwdev, REG_CTRL_TYPE, BIT_CTRL_TYPE1); rtw_write8_set(rtwdev, REG_CTRL_TYPE, BIT_CTRL_TYPE2); } } static void rtw8821c_coex_cfg_gnt_fix(struct rtw_dev *rtwdev) {} static void rtw8821c_coex_cfg_gnt_debug(struct rtw_dev *rtwdev) { rtw_write32_clr(rtwdev, REG_PAD_CTRL1, BIT_BTGP_SPI_EN); rtw_write32_clr(rtwdev, REG_PAD_CTRL1, BIT_BTGP_JTAG_EN); rtw_write32_clr(rtwdev, REG_GPIO_MUXCFG, BIT_FSPI_EN); rtw_write32_clr(rtwdev, REG_PAD_CTRL1, BIT_LED1DIS); rtw_write32_clr(rtwdev, REG_SYS_SDIO_CTRL, BIT_SDIO_INT); rtw_write32_clr(rtwdev, REG_SYS_SDIO_CTRL, BIT_DBG_GNT_WL_BT); } static void rtw8821c_coex_cfg_rfe_type(struct rtw_dev *rtwdev) { struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_rfe *coex_rfe = &coex->rfe; struct rtw_efuse *efuse = &rtwdev->efuse; coex_rfe->rfe_module_type = efuse->rfe_option; coex_rfe->ant_switch_polarity = 0; coex_rfe->ant_switch_exist = true; coex_rfe->wlg_at_btg = false; switch (coex_rfe->rfe_module_type) { case 0: case 8: case 1: case 9: /* 1-Ant, Main, WLG */ default: /* 2-Ant, DPDT, WLG */ break; case 2: case 10: /* 1-Ant, Main, BTG */ case 7: case 15: /* 2-Ant, DPDT, BTG */ coex_rfe->wlg_at_btg = true; break; case 3: case 11: /* 1-Ant, Aux, WLG */ coex_rfe->ant_switch_polarity = 1; break; case 4: case 12: /* 1-Ant, Aux, BTG */ coex_rfe->wlg_at_btg = true; coex_rfe->ant_switch_polarity = 1; break; case 5: case 13: /* 2-Ant, no switch, WLG */ case 6: case 14: /* 2-Ant, no antenna switch, WLG */ coex_rfe->ant_switch_exist = false; break; } } static void rtw8821c_coex_cfg_wl_tx_power(struct rtw_dev *rtwdev, u8 wl_pwr) { struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_dm *coex_dm = &coex->dm; struct rtw_efuse *efuse = &rtwdev->efuse; bool share_ant = efuse->share_ant; if (share_ant) return; if (wl_pwr == coex_dm->cur_wl_pwr_lvl) return; coex_dm->cur_wl_pwr_lvl = wl_pwr; } static void rtw8821c_coex_cfg_wl_rx_gain(struct rtw_dev *rtwdev, bool low_gain) {} static void rtw8821c_txagc_swing_offset(struct rtw_dev *rtwdev, u8 pwr_idx_offset, s8 pwr_idx_offset_lower, s8 *txagc_idx, u8 *swing_idx) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; s8 delta_pwr_idx = dm_info->delta_power_index[RF_PATH_A]; u8 swing_upper_bound = dm_info->default_ofdm_index + 10; u8 swing_lower_bound = 0; u8 max_pwr_idx_offset = 0xf; s8 agc_index = 0; u8 swing_index = dm_info->default_ofdm_index; pwr_idx_offset = min_t(u8, pwr_idx_offset, max_pwr_idx_offset); pwr_idx_offset_lower = max_t(s8, pwr_idx_offset_lower, -15); if (delta_pwr_idx >= 0) { if (delta_pwr_idx <= pwr_idx_offset) { agc_index = delta_pwr_idx; swing_index = dm_info->default_ofdm_index; } else if (delta_pwr_idx > pwr_idx_offset) { agc_index = pwr_idx_offset; swing_index = dm_info->default_ofdm_index + delta_pwr_idx - pwr_idx_offset; swing_index = min_t(u8, swing_index, swing_upper_bound); } } else if (delta_pwr_idx < 0) { if (delta_pwr_idx >= pwr_idx_offset_lower) { agc_index = delta_pwr_idx; swing_index = dm_info->default_ofdm_index; } else if (delta_pwr_idx < pwr_idx_offset_lower) { if (dm_info->default_ofdm_index > (pwr_idx_offset_lower - delta_pwr_idx)) swing_index = dm_info->default_ofdm_index + delta_pwr_idx - pwr_idx_offset_lower; else swing_index = swing_lower_bound; agc_index = pwr_idx_offset_lower; } } if (swing_index >= ARRAY_SIZE(rtw8821c_txscale_tbl)) { rtw_warn(rtwdev, "swing index overflow\n"); swing_index = ARRAY_SIZE(rtw8821c_txscale_tbl) - 1; } *txagc_idx = agc_index; *swing_idx = swing_index; } static void rtw8821c_pwrtrack_set_pwr(struct rtw_dev *rtwdev, u8 pwr_idx_offset, s8 pwr_idx_offset_lower) { s8 txagc_idx; u8 swing_idx; rtw8821c_txagc_swing_offset(rtwdev, pwr_idx_offset, pwr_idx_offset_lower, &txagc_idx, &swing_idx); rtw_write32_mask(rtwdev, REG_TXAGCIDX, GENMASK(6, 1), txagc_idx); rtw_write32_mask(rtwdev, REG_TXSCALE_A, GENMASK(31, 21), rtw8821c_txscale_tbl[swing_idx]); } static void rtw8821c_pwrtrack_set(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 pwr_idx_offset, tx_pwr_idx; s8 pwr_idx_offset_lower; u8 channel = rtwdev->hal.current_channel; u8 band_width = rtwdev->hal.current_band_width; u8 regd = rtw_regd_get(rtwdev); u8 tx_rate = dm_info->tx_rate; u8 max_pwr_idx = rtwdev->chip->max_power_index; tx_pwr_idx = rtw_phy_get_tx_power_index(rtwdev, RF_PATH_A, tx_rate, band_width, channel, regd); tx_pwr_idx = min_t(u8, tx_pwr_idx, max_pwr_idx); pwr_idx_offset = max_pwr_idx - tx_pwr_idx; pwr_idx_offset_lower = 0 - tx_pwr_idx; rtw8821c_pwrtrack_set_pwr(rtwdev, pwr_idx_offset, pwr_idx_offset_lower); } static void rtw8821c_phy_pwrtrack(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; struct rtw_swing_table swing_table; u8 thermal_value, delta; rtw_phy_config_swing_table(rtwdev, &swing_table); if (rtwdev->efuse.thermal_meter[0] == 0xff) return; thermal_value = rtw_read_rf(rtwdev, RF_PATH_A, RF_T_METER, 0xfc00); rtw_phy_pwrtrack_avg(rtwdev, thermal_value, RF_PATH_A); if (dm_info->pwr_trk_init_trigger) dm_info->pwr_trk_init_trigger = false; else if (!rtw_phy_pwrtrack_thermal_changed(rtwdev, thermal_value, RF_PATH_A)) goto iqk; delta = rtw_phy_pwrtrack_get_delta(rtwdev, RF_PATH_A); delta = min_t(u8, delta, RTW_PWR_TRK_TBL_SZ - 1); dm_info->delta_power_index[RF_PATH_A] = rtw_phy_pwrtrack_get_pwridx(rtwdev, &swing_table, RF_PATH_A, RF_PATH_A, delta); if (dm_info->delta_power_index[RF_PATH_A] == dm_info->delta_power_index_last[RF_PATH_A]) goto iqk; else dm_info->delta_power_index_last[RF_PATH_A] = dm_info->delta_power_index[RF_PATH_A]; rtw8821c_pwrtrack_set(rtwdev); iqk: if (rtw_phy_pwrtrack_need_iqk(rtwdev)) rtw8821c_do_iqk(rtwdev); } static void rtw8821c_pwr_track(struct rtw_dev *rtwdev) { struct rtw_efuse *efuse = &rtwdev->efuse; struct rtw_dm_info *dm_info = &rtwdev->dm_info; if (efuse->power_track_type != 0) return; if (!dm_info->pwr_trk_triggered) { rtw_write_rf(rtwdev, RF_PATH_A, RF_T_METER, GENMASK(17, 16), 0x03); dm_info->pwr_trk_triggered = true; return; } rtw8821c_phy_pwrtrack(rtwdev); dm_info->pwr_trk_triggered = false; } static void rtw8821c_bf_config_bfee_su(struct rtw_dev *rtwdev, struct rtw_vif *vif, struct rtw_bfee *bfee, bool enable) { if (enable) rtw_bf_enable_bfee_su(rtwdev, vif, bfee); else rtw_bf_remove_bfee_su(rtwdev, bfee); } static void rtw8821c_bf_config_bfee_mu(struct rtw_dev *rtwdev, struct rtw_vif *vif, struct rtw_bfee *bfee, bool enable) { if (enable) rtw_bf_enable_bfee_mu(rtwdev, vif, bfee); else rtw_bf_remove_bfee_mu(rtwdev, bfee); } static void rtw8821c_bf_config_bfee(struct rtw_dev *rtwdev, struct rtw_vif *vif, struct rtw_bfee *bfee, bool enable) { if (bfee->role == RTW_BFEE_SU) rtw8821c_bf_config_bfee_su(rtwdev, vif, bfee, enable); else if (bfee->role == RTW_BFEE_MU) rtw8821c_bf_config_bfee_mu(rtwdev, vif, bfee, enable); else rtw_warn(rtwdev, "wrong bfee role\n"); } static void rtw8821c_phy_cck_pd_set(struct rtw_dev *rtwdev, u8 new_lvl) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 pd[CCK_PD_LV_MAX] = {3, 7, 13, 13, 13}; u8 cck_n_rx; rtw_dbg(rtwdev, RTW_DBG_PHY, "lv: (%d) -> (%d)\n", dm_info->cck_pd_lv[RTW_CHANNEL_WIDTH_20][RF_PATH_A], new_lvl); if (dm_info->cck_pd_lv[RTW_CHANNEL_WIDTH_20][RF_PATH_A] == new_lvl) return; cck_n_rx = (rtw_read8_mask(rtwdev, REG_CCK0_FAREPORT, BIT_CCK0_2RX) && rtw_read8_mask(rtwdev, REG_CCK0_FAREPORT, BIT_CCK0_MRC)) ? 2 : 1; rtw_dbg(rtwdev, RTW_DBG_PHY, "is_linked=%d, lv=%d, n_rx=%d, cs_ratio=0x%x, pd_th=0x%x, cck_fa_avg=%d\n", rtw_is_assoc(rtwdev), new_lvl, cck_n_rx, dm_info->cck_pd_default + new_lvl * 2, pd[new_lvl], dm_info->cck_fa_avg); dm_info->cck_fa_avg = CCK_FA_AVG_RESET; dm_info->cck_pd_lv[RTW_CHANNEL_WIDTH_20][RF_PATH_A] = new_lvl; rtw_write32_mask(rtwdev, REG_PWRTH, 0x3f0000, pd[new_lvl]); rtw_write32_mask(rtwdev, REG_PWRTH2, 0x1f0000, dm_info->cck_pd_default + new_lvl * 2); } +static void rtw8821c_fill_txdesc_checksum(struct rtw_dev *rtwdev, + struct rtw_tx_pkt_info *pkt_info, + u8 *txdesc) +{ + fill_txdesc_checksum_common(txdesc, 16); +} + static struct rtw_pwr_seq_cmd trans_carddis_to_cardemu_8821c[] = { {0x0086, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_SDIO, RTW_PWR_CMD_WRITE, BIT(0), 0}, {0x0086, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_SDIO, RTW_PWR_CMD_POLLING, BIT(1), BIT(1)}, {0x004A, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), 0}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(3) | BIT(4) | BIT(7), 0}, {0x0300, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0}, {0x0301, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0}, {0xFFFF, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, 0, RTW_PWR_CMD_END, 0, 0}, }; static struct rtw_pwr_seq_cmd trans_cardemu_to_act_8821c[] = { {0x0020, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, {0x0001, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_DELAY, 1, RTW_PWR_DELAY_MS}, {0x0000, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(5), 0}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, (BIT(4) | BIT(3) | BIT(2)), 0}, {0x0075, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, {0x0006, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_POLLING, BIT(1), BIT(1)}, {0x0075, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), 0}, {0x0006, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(7), 0}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, (BIT(4) | BIT(3)), 0}, {0x10C3, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_POLLING, BIT(0), 0}, {0x0020, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(3), BIT(3)}, {0x0074, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(5), BIT(5)}, {0x0022, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(1), 0}, {0x0062, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, (BIT(7) | BIT(6) | BIT(5)), (BIT(7) | BIT(6) | BIT(5))}, {0x0061, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, (BIT(7) | BIT(6) | BIT(5)), 0}, {0x007C, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(1), 0}, {0xFFFF, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, 0, RTW_PWR_CMD_END, 0, 0}, }; static struct rtw_pwr_seq_cmd trans_act_to_cardemu_8821c[] = { {0x0093, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(3), 0}, {0x001F, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0}, {0x0049, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(1), 0}, {0x0006, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, {0x0002, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(1), 0}, {0x10C3, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), 0}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(1), BIT(1)}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_POLLING, BIT(1), 0}, {0x0020, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(3), 0}, {0x0000, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(5), BIT(5)}, {0xFFFF, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, 0, RTW_PWR_CMD_END, 0, 0}, }; static struct rtw_pwr_seq_cmd trans_cardemu_to_carddis_8821c[] = { {0x0007, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0x20}, {0x0067, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(5), 0}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(2), BIT(2)}, {0x004A, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), 0}, {0x0067, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(5), 0}, {0x0067, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(4), 0}, {0x004F, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), 0}, {0x0067, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(1), 0}, {0x0046, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(6), BIT(6)}, {0x0067, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(2), 0}, {0x0046, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(7), BIT(7)}, {0x0062, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(4), BIT(4)}, {0x0081, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(7) | BIT(6), 0}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(3) | BIT(4), BIT(3)}, {0x0086, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_SDIO, RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, {0x0086, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_SDIO, RTW_PWR_CMD_POLLING, BIT(1), 0}, {0x0090, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(1), 0}, {0x0044, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_SDIO, RTW_PWR_CMD_WRITE, 0xFF, 0}, {0x0040, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_SDIO, RTW_PWR_CMD_WRITE, 0xFF, 0x90}, {0x0041, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_SDIO, RTW_PWR_CMD_WRITE, 0xFF, 0x00}, {0x0042, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_SDIO, RTW_PWR_CMD_WRITE, 0xFF, 0x04}, {0xFFFF, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, 0, RTW_PWR_CMD_END, 0, 0}, }; static const struct rtw_pwr_seq_cmd *card_enable_flow_8821c[] = { trans_carddis_to_cardemu_8821c, trans_cardemu_to_act_8821c, NULL }; static const struct rtw_pwr_seq_cmd *card_disable_flow_8821c[] = { trans_act_to_cardemu_8821c, trans_cardemu_to_carddis_8821c, NULL }; static const struct rtw_intf_phy_para usb2_param_8821c[] = { {0xFFFF, 0x00, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_ALL, RTW_INTF_PHY_PLATFORM_ALL}, }; static const struct rtw_intf_phy_para usb3_param_8821c[] = { {0xFFFF, 0x0000, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_ALL, RTW_INTF_PHY_PLATFORM_ALL}, }; static const struct rtw_intf_phy_para pcie_gen1_param_8821c[] = { {0x0009, 0x6380, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_ALL, RTW_INTF_PHY_PLATFORM_ALL}, {0xFFFF, 0x0000, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_ALL, RTW_INTF_PHY_PLATFORM_ALL}, }; static const struct rtw_intf_phy_para pcie_gen2_param_8821c[] = { {0xFFFF, 0x0000, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_ALL, RTW_INTF_PHY_PLATFORM_ALL}, }; static const struct rtw_intf_phy_para_table phy_para_table_8821c = { .usb2_para = usb2_param_8821c, .usb3_para = usb3_param_8821c, .gen1_para = pcie_gen1_param_8821c, .gen2_para = pcie_gen2_param_8821c, .n_usb2_para = ARRAY_SIZE(usb2_param_8821c), .n_usb3_para = ARRAY_SIZE(usb2_param_8821c), .n_gen1_para = ARRAY_SIZE(pcie_gen1_param_8821c), .n_gen2_para = ARRAY_SIZE(pcie_gen2_param_8821c), }; static const struct rtw_rfe_def rtw8821c_rfe_defs[] = { [0] = RTW_DEF_RFE(8821c, 0, 0), [2] = RTW_DEF_RFE_EXT(8821c, 0, 0, 2), [4] = RTW_DEF_RFE_EXT(8821c, 0, 0, 2), [6] = RTW_DEF_RFE(8821c, 0, 0), }; static struct rtw_hw_reg rtw8821c_dig[] = { [0] = { .addr = 0xc50, .mask = 0x7f }, }; static const struct rtw_ltecoex_addr rtw8821c_ltecoex_addr = { .ctrl = LTECOEX_ACCESS_CTRL, .wdata = LTECOEX_WRITE_DATA, .rdata = LTECOEX_READ_DATA, }; static struct rtw_page_table page_table_8821c[] = { /* not sure what [0] stands for */ {16, 16, 16, 14, 1}, {16, 16, 16, 14, 1}, {16, 16, 0, 0, 1}, {16, 16, 16, 0, 1}, {16, 16, 16, 14, 1}, }; static struct rtw_rqpn rqpn_table_8821c[] = { /* not sure what [0] stands for */ {RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_EXTRA, RTW_DMA_MAPPING_HIGH}, {RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_EXTRA, RTW_DMA_MAPPING_HIGH}, {RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH}, {RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH}, {RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_EXTRA, RTW_DMA_MAPPING_HIGH}, }; static struct rtw_prioq_addrs prioq_addrs_8821c = { .prio[RTW_DMA_MAPPING_EXTRA] = { .rsvd = REG_FIFOPAGE_INFO_4, .avail = REG_FIFOPAGE_INFO_4 + 2, }, .prio[RTW_DMA_MAPPING_LOW] = { .rsvd = REG_FIFOPAGE_INFO_2, .avail = REG_FIFOPAGE_INFO_2 + 2, }, .prio[RTW_DMA_MAPPING_NORMAL] = { .rsvd = REG_FIFOPAGE_INFO_3, .avail = REG_FIFOPAGE_INFO_3 + 2, }, .prio[RTW_DMA_MAPPING_HIGH] = { .rsvd = REG_FIFOPAGE_INFO_1, .avail = REG_FIFOPAGE_INFO_1 + 2, }, .wsize = true, }; static struct rtw_chip_ops rtw8821c_ops = { .phy_set_param = rtw8821c_phy_set_param, .read_efuse = rtw8821c_read_efuse, .query_rx_desc = rtw8821c_query_rx_desc, .set_channel = rtw8821c_set_channel, .mac_init = rtw8821c_mac_init, .read_rf = rtw_phy_read_rf, .write_rf = rtw_phy_write_rf_reg_sipi, .set_antenna = NULL, .set_tx_power_index = rtw8821c_set_tx_power_index, .cfg_ldo25 = rtw8821c_cfg_ldo25, .false_alarm_statistics = rtw8821c_false_alarm_statistics, .phy_calibration = rtw8821c_phy_calibration, .cck_pd_set = rtw8821c_phy_cck_pd_set, .pwr_track = rtw8821c_pwr_track, .config_bfee = rtw8821c_bf_config_bfee, .set_gid_table = rtw_bf_set_gid_table, .cfg_csi_rate = rtw_bf_cfg_csi_rate, + .fill_txdesc_checksum = rtw8821c_fill_txdesc_checksum, .coex_set_init = rtw8821c_coex_cfg_init, .coex_set_ant_switch = rtw8821c_coex_cfg_ant_switch, .coex_set_gnt_fix = rtw8821c_coex_cfg_gnt_fix, .coex_set_gnt_debug = rtw8821c_coex_cfg_gnt_debug, .coex_set_rfe_type = rtw8821c_coex_cfg_rfe_type, .coex_set_wl_tx_power = rtw8821c_coex_cfg_wl_tx_power, .coex_set_wl_rx_gain = rtw8821c_coex_cfg_wl_rx_gain, }; /* rssi in percentage % (dbm = % - 100) */ static const u8 wl_rssi_step_8821c[] = {101, 45, 101, 40}; static const u8 bt_rssi_step_8821c[] = {101, 101, 101, 101}; /* Shared-Antenna Coex Table */ static const struct coex_table_para table_sant_8821c[] = { {0x55555555, 0x55555555}, /* case-0 */ {0x55555555, 0x55555555}, {0x66555555, 0x66555555}, {0xaaaaaaaa, 0xaaaaaaaa}, {0x5a5a5a5a, 0x5a5a5a5a}, {0xfafafafa, 0xfafafafa}, /* case-5 */ {0x6a5a5555, 0xaaaaaaaa}, {0x6a5a56aa, 0x6a5a56aa}, {0x6a5a5a5a, 0x6a5a5a5a}, {0x66555555, 0x5a5a5a5a}, {0x66555555, 0x6a5a5a5a}, /* case-10 */ {0x66555555, 0xaaaaaaaa}, {0x66555555, 0x6a5a5aaa}, {0x66555555, 0x6aaa6aaa}, {0x66555555, 0x6a5a5aaa}, {0x66555555, 0xaaaaaaaa}, /* case-15 */ {0xffff55ff, 0xfafafafa}, {0xffff55ff, 0x6afa5afa}, {0xaaffffaa, 0xfafafafa}, {0xaa5555aa, 0x5a5a5a5a}, {0xaa5555aa, 0x6a5a5a5a}, /* case-20 */ {0xaa5555aa, 0xaaaaaaaa}, {0xffffffff, 0x55555555}, {0xffffffff, 0x5a5a5a5a}, {0xffffffff, 0x5a5a5a5a}, {0xffffffff, 0x5a5a5aaa}, /* case-25 */ {0x55555555, 0x5a5a5a5a}, {0x55555555, 0xaaaaaaaa}, {0x66555555, 0x6a5a6a5a}, {0x66556655, 0x66556655}, {0x66556aaa, 0x6a5a6aaa}, /* case-30 */ {0xffffffff, 0x5aaa5aaa}, {0x56555555, 0x5a5a5aaa} }; /* Non-Shared-Antenna Coex Table */ static const struct coex_table_para table_nsant_8821c[] = { {0xffffffff, 0xffffffff}, /* case-100 */ {0xffff55ff, 0xfafafafa}, {0x66555555, 0x66555555}, {0xaaaaaaaa, 0xaaaaaaaa}, {0x5a5a5a5a, 0x5a5a5a5a}, {0xffffffff, 0xffffffff}, /* case-105 */ {0x5afa5afa, 0x5afa5afa}, {0x55555555, 0xfafafafa}, {0x66555555, 0xfafafafa}, {0x66555555, 0x5a5a5a5a}, {0x66555555, 0x6a5a5a5a}, /* case-110 */ {0x66555555, 0xaaaaaaaa}, {0xffff55ff, 0xfafafafa}, {0xffff55ff, 0x5afa5afa}, {0xffff55ff, 0xaaaaaaaa}, {0xffff55ff, 0xffff55ff}, /* case-115 */ {0xaaffffaa, 0x5afa5afa}, {0xaaffffaa, 0xaaaaaaaa}, {0xffffffff, 0xfafafafa}, {0xffff55ff, 0xfafafafa}, {0xffffffff, 0xaaaaaaaa}, /* case-120 */ {0xffff55ff, 0x5afa5afa}, {0xffff55ff, 0x5afa5afa}, {0x55ff55ff, 0x55ff55ff} }; /* Shared-Antenna TDMA */ static const struct coex_tdma_para tdma_sant_8821c[] = { { {0x00, 0x00, 0x00, 0x00, 0x00} }, /* case-0 */ { {0x61, 0x45, 0x03, 0x11, 0x11} }, /* case-1 */ { {0x61, 0x3a, 0x03, 0x11, 0x11} }, { {0x61, 0x35, 0x03, 0x11, 0x11} }, { {0x61, 0x20, 0x03, 0x11, 0x11} }, { {0x61, 0x3a, 0x03, 0x11, 0x11} }, /* case-5 */ { {0x61, 0x45, 0x03, 0x11, 0x10} }, { {0x61, 0x35, 0x03, 0x11, 0x10} }, { {0x61, 0x30, 0x03, 0x11, 0x10} }, { {0x61, 0x20, 0x03, 0x11, 0x10} }, { {0x61, 0x10, 0x03, 0x11, 0x10} }, /* case-10 */ { {0x61, 0x08, 0x03, 0x11, 0x15} }, { {0x61, 0x08, 0x03, 0x10, 0x14} }, { {0x51, 0x08, 0x03, 0x10, 0x54} }, { {0x51, 0x08, 0x03, 0x10, 0x55} }, { {0x51, 0x08, 0x07, 0x10, 0x54} }, /* case-15 */ { {0x51, 0x45, 0x03, 0x10, 0x50} }, { {0x51, 0x3a, 0x03, 0x11, 0x50} }, { {0x51, 0x30, 0x03, 0x10, 0x50} }, { {0x51, 0x21, 0x03, 0x10, 0x50} }, { {0x51, 0x10, 0x03, 0x10, 0x50} }, /* case-20 */ { {0x51, 0x4a, 0x03, 0x10, 0x50} }, { {0x51, 0x08, 0x03, 0x30, 0x54} }, { {0x55, 0x08, 0x03, 0x10, 0x54} }, { {0x65, 0x10, 0x03, 0x11, 0x10} }, { {0x51, 0x10, 0x03, 0x10, 0x51} }, /* case-25 */ { {0x51, 0x21, 0x03, 0x10, 0x50} }, { {0x61, 0x08, 0x03, 0x11, 0x11} } }; /* Non-Shared-Antenna TDMA */ static const struct coex_tdma_para tdma_nsant_8821c[] = { { {0x00, 0x00, 0x00, 0x40, 0x00} }, /* case-100 */ { {0x61, 0x45, 0x03, 0x11, 0x11} }, { {0x61, 0x25, 0x03, 0x11, 0x11} }, { {0x61, 0x35, 0x03, 0x11, 0x11} }, { {0x61, 0x20, 0x03, 0x11, 0x11} }, { {0x61, 0x10, 0x03, 0x11, 0x11} }, /* case-105 */ { {0x61, 0x45, 0x03, 0x11, 0x10} }, { {0x61, 0x30, 0x03, 0x11, 0x10} }, { {0x61, 0x30, 0x03, 0x11, 0x10} }, { {0x61, 0x20, 0x03, 0x11, 0x10} }, { {0x61, 0x10, 0x03, 0x11, 0x10} }, /* case-110 */ { {0x61, 0x10, 0x03, 0x11, 0x11} }, { {0x61, 0x08, 0x03, 0x10, 0x14} }, { {0x51, 0x08, 0x03, 0x10, 0x54} }, { {0x51, 0x08, 0x03, 0x10, 0x55} }, { {0x51, 0x08, 0x07, 0x10, 0x54} }, /* case-115 */ { {0x51, 0x45, 0x03, 0x10, 0x50} }, { {0x51, 0x3a, 0x03, 0x10, 0x50} }, { {0x51, 0x30, 0x03, 0x10, 0x50} }, { {0x51, 0x21, 0x03, 0x10, 0x50} }, { {0x51, 0x21, 0x03, 0x10, 0x50} }, /* case-120 */ { {0x51, 0x10, 0x03, 0x10, 0x50} } }; static const struct coex_5g_afh_map afh_5g_8821c[] = { {0, 0, 0} }; /* wl_tx_dec_power, bt_tx_dec_power, wl_rx_gain, bt_rx_lna_constrain */ static const struct coex_rf_para rf_para_tx_8821c[] = { {0, 0, false, 7}, /* for normal */ {0, 20, false, 7}, /* for WL-CPT */ {8, 17, true, 4}, {7, 18, true, 4}, {6, 19, true, 4}, {5, 20, true, 4} }; static const struct coex_rf_para rf_para_rx_8821c[] = { {0, 0, false, 7}, /* for normal */ {0, 20, false, 7}, /* for WL-CPT */ {3, 24, true, 5}, {2, 26, true, 5}, {1, 27, true, 5}, {0, 28, true, 5} }; #if defined(__linux__) static_assert(ARRAY_SIZE(rf_para_tx_8821c) == ARRAY_SIZE(rf_para_rx_8821c)); #elif defined(__FreeBSD__) rtw88_static_assert(ARRAY_SIZE(rf_para_tx_8821c) == ARRAY_SIZE(rf_para_rx_8821c)); #endif static const u8 rtw8821c_pwrtrk_5gb_n[][RTW_PWR_TRK_TBL_SZ] = { {0, 1, 1, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, 8, 8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, 12}, {0, 1, 1, 1, 2, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, 12, 12, 12}, {0, 1, 2, 2, 3, 4, 4, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, 12, 12}, }; static const u8 rtw8821c_pwrtrk_5gb_p[][RTW_PWR_TRK_TBL_SZ] = { {0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 11, 11, 12, 12, 12, 12, 12, 12, 12}, {0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, 8, 8, 9, 10, 10, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12}, {0, 1, 1, 1, 2, 3, 3, 3, 4, 4, 4, 5, 6, 6, 7, 7, 8, 8, 9, 10, 10, 11, 11, 12, 12, 12, 12, 12, 12, 12}, }; static const u8 rtw8821c_pwrtrk_5ga_n[][RTW_PWR_TRK_TBL_SZ] = { {0, 1, 1, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, 8, 8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, 12}, {0, 1, 1, 1, 2, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, 12, 12, 12}, {0, 1, 2, 2, 3, 4, 4, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, 12, 12}, }; static const u8 rtw8821c_pwrtrk_5ga_p[][RTW_PWR_TRK_TBL_SZ] = { {0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 11, 11, 12, 12, 12, 12, 12, 12, 12}, {0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, 8, 8, 9, 10, 10, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12}, {0, 1, 1, 1, 2, 3, 3, 3, 4, 4, 4, 5, 6, 6, 7, 7, 8, 8, 9, 10, 10, 11, 11, 12, 12, 12, 12, 12, 12, 12}, }; static const u8 rtw8821c_pwrtrk_2gb_n[] = { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 8, 8, 9 }; static const u8 rtw8821c_pwrtrk_2gb_p[] = { 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 9, 9, 9, 9 }; static const u8 rtw8821c_pwrtrk_2ga_n[] = { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 8, 8, 9 }; static const u8 rtw8821c_pwrtrk_2ga_p[] = { 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 9, 9, 9, 9 }; static const u8 rtw8821c_pwrtrk_2g_cck_b_n[] = { 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 9 }; static const u8 rtw8821c_pwrtrk_2g_cck_b_p[] = { 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 9, 9, 9, 9, 9, 9 }; static const u8 rtw8821c_pwrtrk_2g_cck_a_n[] = { 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 9 }; static const u8 rtw8821c_pwrtrk_2g_cck_a_p[] = { 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 9, 9, 9, 9, 9, 9 }; static const struct rtw_pwr_track_tbl rtw8821c_rtw_pwr_track_tbl = { .pwrtrk_5gb_n[0] = rtw8821c_pwrtrk_5gb_n[0], .pwrtrk_5gb_n[1] = rtw8821c_pwrtrk_5gb_n[1], .pwrtrk_5gb_n[2] = rtw8821c_pwrtrk_5gb_n[2], .pwrtrk_5gb_p[0] = rtw8821c_pwrtrk_5gb_p[0], .pwrtrk_5gb_p[1] = rtw8821c_pwrtrk_5gb_p[1], .pwrtrk_5gb_p[2] = rtw8821c_pwrtrk_5gb_p[2], .pwrtrk_5ga_n[0] = rtw8821c_pwrtrk_5ga_n[0], .pwrtrk_5ga_n[1] = rtw8821c_pwrtrk_5ga_n[1], .pwrtrk_5ga_n[2] = rtw8821c_pwrtrk_5ga_n[2], .pwrtrk_5ga_p[0] = rtw8821c_pwrtrk_5ga_p[0], .pwrtrk_5ga_p[1] = rtw8821c_pwrtrk_5ga_p[1], .pwrtrk_5ga_p[2] = rtw8821c_pwrtrk_5ga_p[2], .pwrtrk_2gb_n = rtw8821c_pwrtrk_2gb_n, .pwrtrk_2gb_p = rtw8821c_pwrtrk_2gb_p, .pwrtrk_2ga_n = rtw8821c_pwrtrk_2ga_n, .pwrtrk_2ga_p = rtw8821c_pwrtrk_2ga_p, .pwrtrk_2g_cckb_n = rtw8821c_pwrtrk_2g_cck_b_n, .pwrtrk_2g_cckb_p = rtw8821c_pwrtrk_2g_cck_b_p, .pwrtrk_2g_ccka_n = rtw8821c_pwrtrk_2g_cck_a_n, .pwrtrk_2g_ccka_p = rtw8821c_pwrtrk_2g_cck_a_p, }; static const struct rtw_reg_domain coex_info_hw_regs_8821c[] = { {0xCB0, MASKDWORD, RTW_REG_DOMAIN_MAC32}, {0xCB4, MASKDWORD, RTW_REG_DOMAIN_MAC32}, {0xCBA, MASKBYTE0, RTW_REG_DOMAIN_MAC8}, {0, 0, RTW_REG_DOMAIN_NL}, {0x430, MASKDWORD, RTW_REG_DOMAIN_MAC32}, {0x434, MASKDWORD, RTW_REG_DOMAIN_MAC32}, {0x42a, MASKLWORD, RTW_REG_DOMAIN_MAC16}, {0x426, MASKBYTE0, RTW_REG_DOMAIN_MAC8}, {0x45e, BIT(3), RTW_REG_DOMAIN_MAC8}, {0x454, MASKLWORD, RTW_REG_DOMAIN_MAC16}, {0, 0, RTW_REG_DOMAIN_NL}, {0x4c, BIT(24) | BIT(23), RTW_REG_DOMAIN_MAC32}, {0x64, BIT(0), RTW_REG_DOMAIN_MAC8}, {0x4c6, BIT(4), RTW_REG_DOMAIN_MAC8}, {0x40, BIT(5), RTW_REG_DOMAIN_MAC8}, {0x1, RFREG_MASK, RTW_REG_DOMAIN_RF_A}, {0, 0, RTW_REG_DOMAIN_NL}, {0x550, MASKDWORD, RTW_REG_DOMAIN_MAC32}, {0x522, MASKBYTE0, RTW_REG_DOMAIN_MAC8}, {0x953, BIT(1), RTW_REG_DOMAIN_MAC8}, {0xc50, MASKBYTE0, RTW_REG_DOMAIN_MAC8}, {0x60A, MASKBYTE0, RTW_REG_DOMAIN_MAC8}, }; const struct rtw_chip_info rtw8821c_hw_spec = { .ops = &rtw8821c_ops, .id = RTW_CHIP_TYPE_8821C, .fw_name = "rtw88/rtw8821c_fw.bin", .wlan_cpu = RTW_WCPU_11AC, .tx_pkt_desc_sz = 48, .tx_buf_desc_sz = 16, .rx_pkt_desc_sz = 24, .rx_buf_desc_sz = 8, .phy_efuse_size = 512, .log_efuse_size = 512, .ptct_efuse_size = 96, .txff_size = 65536, .rxff_size = 16384, + .rsvd_drv_pg_num = 8, .txgi_factor = 1, .is_pwr_by_rate_dec = true, .max_power_index = 0x3f, .csi_buf_pg_num = 0, .band = RTW_BAND_2G | RTW_BAND_5G, - .page_size = 128, + .page_size = TX_PAGE_SIZE, .dig_min = 0x1c, .ht_supported = true, .vht_supported = true, .lps_deep_mode_supported = BIT(LPS_DEEP_MODE_LCLK), .sys_func_en = 0xD8, .pwr_on_seq = card_enable_flow_8821c, .pwr_off_seq = card_disable_flow_8821c, .page_table = page_table_8821c, .rqpn_table = rqpn_table_8821c, .prioq_addrs = &prioq_addrs_8821c, .intf_table = &phy_para_table_8821c, .dig = rtw8821c_dig, .rf_base_addr = {0x2800, 0x2c00}, .rf_sipi_addr = {0xc90, 0xe90}, .ltecoex_addr = &rtw8821c_ltecoex_addr, .mac_tbl = &rtw8821c_mac_tbl, .agc_tbl = &rtw8821c_agc_tbl, .bb_tbl = &rtw8821c_bb_tbl, .rf_tbl = {&rtw8821c_rf_a_tbl}, .rfe_defs = rtw8821c_rfe_defs, .rfe_defs_size = ARRAY_SIZE(rtw8821c_rfe_defs), .rx_ldpc = false, .pwr_track_tbl = &rtw8821c_rtw_pwr_track_tbl, .iqk_threshold = 8, .bfer_su_max_num = 2, .bfer_mu_max_num = 1, .ampdu_density = IEEE80211_HT_MPDU_DENSITY_2, + .max_scan_ie_len = IEEE80211_MAX_DATA_LEN, .coex_para_ver = 0x19092746, .bt_desired_ver = 0x46, .scbd_support = true, .new_scbd10_def = false, .ble_hid_profile_support = false, .wl_mimo_ps_support = false, .pstdma_type = COEX_PSTDMA_FORCE_LPSOFF, .bt_rssi_type = COEX_BTRSSI_RATIO, .ant_isolation = 15, .rssi_tolerance = 2, .wl_rssi_step = wl_rssi_step_8821c, .bt_rssi_step = bt_rssi_step_8821c, .table_sant_num = ARRAY_SIZE(table_sant_8821c), .table_sant = table_sant_8821c, .table_nsant_num = ARRAY_SIZE(table_nsant_8821c), .table_nsant = table_nsant_8821c, .tdma_sant_num = ARRAY_SIZE(tdma_sant_8821c), .tdma_sant = tdma_sant_8821c, .tdma_nsant_num = ARRAY_SIZE(tdma_nsant_8821c), .tdma_nsant = tdma_nsant_8821c, .wl_rf_para_num = ARRAY_SIZE(rf_para_tx_8821c), .wl_rf_para_tx = rf_para_tx_8821c, .wl_rf_para_rx = rf_para_rx_8821c, .bt_afh_span_bw20 = 0x24, .bt_afh_span_bw40 = 0x36, .afh_5g_num = ARRAY_SIZE(afh_5g_8821c), .afh_5g = afh_5g_8821c, .coex_info_hw_regs_num = ARRAY_SIZE(coex_info_hw_regs_8821c), .coex_info_hw_regs = coex_info_hw_regs_8821c, }; EXPORT_SYMBOL(rtw8821c_hw_spec); MODULE_FIRMWARE("rtw88/rtw8821c_fw.bin"); MODULE_AUTHOR("Realtek Corporation"); MODULE_DESCRIPTION("Realtek 802.11ac wireless 8821c driver"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sys/contrib/dev/rtw88/rtw8821c.h b/sys/contrib/dev/rtw88/rtw8821c.h index 2698801fc35d..fcff31688c45 100644 --- a/sys/contrib/dev/rtw88/rtw8821c.h +++ b/sys/contrib/dev/rtw88/rtw8821c.h @@ -1,281 +1,308 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* Copyright(c) 2018-2019 Realtek Corporation */ #ifndef __RTW8821C_H__ #define __RTW8821C_H__ #include #define RCR_VHT_ACK BIT(26) +struct rtw8821cu_efuse { + u8 res4[4]; /* 0xd0 */ + u8 usb_optional_function; + u8 res5[0x1e]; + u8 res6[2]; + u8 serial[0x0b]; /* 0xf5 */ + u8 vid; /* 0x100 */ + u8 res7; + u8 pid; + u8 res8[4]; + u8 mac_addr[ETH_ALEN]; /* 0x107 */ + u8 res9[2]; + u8 vendor_name[0x07]; + u8 res10[2]; + u8 device_name[0x14]; + u8 res11[0xcf]; + u8 package_type; /* 0x1fb */ + u8 res12[0x4]; +}; + struct rtw8821ce_efuse { u8 mac_addr[ETH_ALEN]; /* 0xd0 */ u8 vender_id[2]; u8 device_id[2]; u8 sub_vender_id[2]; u8 sub_device_id[2]; u8 pmc[2]; u8 exp_device_cap[2]; u8 msi_cap; u8 ltr_cap; /* 0xe3 */ u8 exp_link_control[2]; u8 link_cap[4]; u8 link_control[2]; u8 serial_number[8]; u8 res0:2; /* 0xf4 */ u8 ltr_en:1; u8 res1:2; u8 obff:2; u8 res2:3; u8 obff_cap:2; u8 res3:4; u8 res4[3]; u8 class_code[3]; u8 pci_pm_L1_2_supp:1; u8 pci_pm_L1_1_supp:1; u8 aspm_pm_L1_2_supp:1; u8 aspm_pm_L1_1_supp:1; u8 L1_pm_substates_supp:1; u8 res5:3; u8 port_common_mode_restore_time; u8 port_t_power_on_scale:2; u8 res6:1; u8 port_t_power_on_value:5; u8 res7; }; +struct rtw8821cs_efuse { + u8 res4[0x4a]; /* 0xd0 */ + u8 mac_addr[ETH_ALEN]; /* 0x11a */ +} __packed; + struct rtw8821c_efuse { __le16 rtl_id; u8 res0[0x0e]; /* power index for four RF paths */ struct rtw_txpwr_idx txpwr_idx_table[4]; u8 channel_plan; /* 0xb8 */ u8 xtal_k; u8 thermal_meter; u8 iqk_lck; u8 pa_type; /* 0xbc */ u8 lna_type_2g[2]; /* 0xbd */ u8 lna_type_5g[2]; u8 rf_board_option; u8 rf_feature_option; u8 rf_bt_setting; u8 eeprom_version; u8 eeprom_customer_id; u8 tx_bb_swing_setting_2g; u8 tx_bb_swing_setting_5g; u8 tx_pwr_calibrate_rate; u8 rf_antenna_option; /* 0xc9 */ u8 rfe_option; u8 country_code[2]; u8 res[3]; union { struct rtw8821ce_efuse e; + struct rtw8821cu_efuse u; + struct rtw8821cs_efuse s; }; }; static inline void _rtw_write32s_mask(struct rtw_dev *rtwdev, u32 addr, u32 mask, u32 data) { /* 0xC00-0xCFF and 0xE00-0xEFF have the same layout */ rtw_write32_mask(rtwdev, addr, mask, data); rtw_write32_mask(rtwdev, addr + 0x200, mask, data); } extern const struct rtw_chip_info rtw8821c_hw_spec; #define rtw_write32s_mask(rtwdev, addr, mask, data) \ do { \ BUILD_BUG_ON((addr) < 0xC00 || (addr) >= 0xD00); \ \ _rtw_write32s_mask(rtwdev, addr, mask, data); \ } while (0) #define BIT_FEN_PCIEA BIT(6) #define WLAN_SLOT_TIME 0x09 #define WLAN_PIFS_TIME 0x19 #define WLAN_SIFS_CCK_CONT_TX 0xA #define WLAN_SIFS_OFDM_CONT_TX 0xE #define WLAN_SIFS_CCK_TRX 0x10 #define WLAN_SIFS_OFDM_TRX 0x10 #define WLAN_VO_TXOP_LIMIT 0x186 #define WLAN_VI_TXOP_LIMIT 0x3BC #define WLAN_RDG_NAV 0x05 #define WLAN_TXOP_NAV 0x1B #define WLAN_CCK_RX_TSF 0x30 #define WLAN_OFDM_RX_TSF 0x30 #define WLAN_TBTT_PROHIBIT 0x04 #define WLAN_TBTT_HOLD_TIME 0x064 #define WLAN_DRV_EARLY_INT 0x04 #define WLAN_BCN_DMA_TIME 0x02 #define WLAN_RX_FILTER0 0x0FFFFFFF #define WLAN_RX_FILTER2 0xFFFF #define WLAN_RCR_CFG 0xE400220E #define WLAN_RXPKT_MAX_SZ 12288 #define WLAN_RXPKT_MAX_SZ_512 (WLAN_RXPKT_MAX_SZ >> 9) #define WLAN_AMPDU_MAX_TIME 0x70 #define WLAN_RTS_LEN_TH 0xFF #define WLAN_RTS_TX_TIME_TH 0x08 #define WLAN_MAX_AGG_PKT_LIMIT 0x20 #define WLAN_RTS_MAX_AGG_PKT_LIMIT 0x20 #define FAST_EDCA_VO_TH 0x06 #define FAST_EDCA_VI_TH 0x06 #define FAST_EDCA_BE_TH 0x06 #define FAST_EDCA_BK_TH 0x06 #define WLAN_BAR_RETRY_LIMIT 0x01 #define WLAN_RA_TRY_RATE_AGG_LIMIT 0x08 #define WLAN_TX_FUNC_CFG1 0x30 #define WLAN_TX_FUNC_CFG2 0x30 #define WLAN_MAC_OPT_NORM_FUNC1 0x98 #define WLAN_MAC_OPT_LB_FUNC1 0x80 #define WLAN_MAC_OPT_FUNC2 0xb0810041 #define WLAN_SIFS_CFG (WLAN_SIFS_CCK_CONT_TX | \ (WLAN_SIFS_OFDM_CONT_TX << BIT_SHIFT_SIFS_OFDM_CTX) | \ (WLAN_SIFS_CCK_TRX << BIT_SHIFT_SIFS_CCK_TRX) | \ (WLAN_SIFS_OFDM_TRX << BIT_SHIFT_SIFS_OFDM_TRX)) #define WLAN_TBTT_TIME (WLAN_TBTT_PROHIBIT |\ (WLAN_TBTT_HOLD_TIME << BIT_SHIFT_TBTT_HOLD_TIME_AP)) #define WLAN_NAV_CFG (WLAN_RDG_NAV | (WLAN_TXOP_NAV << 16)) #define WLAN_RX_TSF_CFG (WLAN_CCK_RX_TSF | (WLAN_OFDM_RX_TSF) << 8) #define WLAN_PRE_TXCNT_TIME_TH 0x1E4 /* phy status page0 */ #define GET_PHY_STAT_P0_PWDB(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x00), GENMASK(15, 8)) #define GET_PHY_STAT_P0_VGA(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x03), GENMASK(12, 8)) #define GET_PHY_STAT_P0_LNA_L(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x03), GENMASK(15, 13)) #define GET_PHY_STAT_P0_LNA_H(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x03), BIT(23)) #define BIT_LNA_H_MASK BIT(3) #define BIT_LNA_L_MASK GENMASK(2, 0) /* phy status page1 */ #define GET_PHY_STAT_P1_PWDB_A(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x00), GENMASK(15, 8)) #define GET_PHY_STAT_P1_PWDB_B(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x00), GENMASK(23, 16)) #define GET_PHY_STAT_P1_RF_MODE(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x03), GENMASK(29, 28)) #define GET_PHY_STAT_P1_L_RXSC(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x01), GENMASK(11, 8)) #define GET_PHY_STAT_P1_HT_RXSC(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x01), GENMASK(15, 12)) #define GET_PHY_STAT_P1_RXEVM_A(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x04), GENMASK(7, 0)) #define GET_PHY_STAT_P1_RXEVM_B(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x04), GENMASK(15, 8)) #define GET_PHY_STAT_P1_CFO_TAIL_A(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x05), GENMASK(7, 0)) #define GET_PHY_STAT_P1_CFO_TAIL_B(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x05), GENMASK(15, 8)) #define GET_PHY_STAT_P1_RXSNR_A(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x06), GENMASK(7, 0)) #define GET_PHY_STAT_P1_RXSNR_B(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x06), GENMASK(15, 8)) #define REG_SYS_CTRL 0x000 #define BIT_FEN_EN BIT(26) #define REG_INIRTS_RATE_SEL 0x0480 #define REG_HTSTFWT 0x800 #define REG_RXPSEL 0x808 #define BIT_RX_PSEL_RST (BIT(28) | BIT(29)) #define REG_TXPSEL 0x80c #define REG_RXCCAMSK 0x814 #define REG_CCASEL 0x82c #define REG_PDMFTH 0x830 #define REG_CCA2ND 0x838 #define REG_L1WT 0x83c #define REG_L1PKWT 0x840 #define REG_MRC 0x850 #define REG_CLKTRK 0x860 #define REG_ADCCLK 0x8ac #define REG_ADC160 0x8c4 #define REG_ADC40 0x8c8 #define REG_CHFIR 0x8f0 #define REG_CDDTXP 0x93c #define REG_TXPSEL1 0x940 #define REG_ACBB0 0x948 #define REG_ACBBRXFIR 0x94c #define REG_ACGG2TBL 0x958 #define REG_FAS 0x9a4 #define REG_RXSB 0xa00 #define REG_ADCINI 0xa04 #define REG_PWRTH 0xa08 #define REG_TXSF2 0xa24 #define REG_TXSF6 0xa28 #define REG_FA_CCK 0xa5c #define REG_RXDESC 0xa2c #define REG_ENTXCCK 0xa80 #define BTG_LNA 0xfc84 #define WLG_LNA 0x7532 #define REG_ENRXCCA 0xa84 #define BTG_CCA 0x0e #define WLG_CCA 0x12 #define REG_PWRTH2 0xaa8 #define REG_CSRATIO 0xaaa #define REG_TXFILTER 0xaac #define REG_CNTRST 0xb58 #define REG_AGCTR_A 0xc08 #define REG_TXSCALE_A 0xc1c #define REG_TXDFIR 0xc20 #define REG_RXIGI_A 0xc50 #define REG_TXAGCIDX 0xc94 #define REG_TRSW 0xca0 #define REG_RFESEL0 0xcb0 #define REG_RFESEL8 0xcb4 #define REG_RFECTL 0xcb8 #define B_BTG_SWITCH BIT(16) #define B_CTRL_SWITCH BIT(18) #define B_WL_SWITCH (BIT(20) | BIT(22)) #define B_WLG_SWITCH BIT(21) #define B_WLA_SWITCH BIT(23) #define REG_RFEINV 0xcbc #define REG_AGCTR_B 0xe08 #define REG_RXIGI_B 0xe50 #define REG_CRC_CCK 0xf04 #define REG_CRC_OFDM 0xf14 #define REG_CRC_HT 0xf10 #define REG_CRC_VHT 0xf0c #define REG_CCA_OFDM 0xf08 #define REG_FA_OFDM 0xf48 #define REG_CCA_CCK 0xfcc #define REG_DMEM_CTRL 0x1080 #define BIT_WL_RST BIT(16) #define REG_ANTWT 0x1904 #define REG_IQKFAILMSK 0x1bf0 #define BIT_MASK_R_RFE_SEL_15 GENMASK(31, 28) #define BIT_SDIO_INT BIT(18) #define BT_CNT_ENABLE 0x1 #define BIT_BCN_QUEUE BIT(3) #define BCN_PRI_EN 0x1 #define PTA_CTRL_PIN 0x66 #define DPDT_CTRL_PIN 0x77 #define ANTDIC_CTRL_PIN 0x88 #define REG_CTRL_TYPE 0x67 #define BIT_CTRL_TYPE1 BIT(5) #define BIT_CTRL_TYPE2 BIT(4) #define CTRL_TYPE_MASK GENMASK(15, 8) #define RF18_BAND_MASK (BIT(16) | BIT(9) | BIT(8)) #define RF18_BAND_2G (0) #define RF18_BAND_5G (BIT(16) | BIT(8)) #define RF18_CHANNEL_MASK (MASKBYTE0) #define RF18_RFSI_MASK (BIT(18) | BIT(17)) #define RF18_RFSI_GE (BIT(17)) #define RF18_RFSI_GT (BIT(18)) #define RF18_BW_MASK (BIT(11) | BIT(10)) #define RF18_BW_20M (BIT(11) | BIT(10)) #define RF18_BW_40M (BIT(11)) #define RF18_BW_80M (BIT(10)) #endif diff --git a/sys/contrib/dev/rtw88/rtw8821cs.c b/sys/contrib/dev/rtw88/rtw8821cs.c new file mode 100644 index 000000000000..a359413369a4 --- /dev/null +++ b/sys/contrib/dev/rtw88/rtw8821cs.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) Martin Blumenstingl + */ + +#include +#include +#include +#include "main.h" +#include "rtw8821c.h" +#include "sdio.h" + +static const struct sdio_device_id rtw_8821cs_id_table[] = { + { + SDIO_DEVICE(SDIO_VENDOR_ID_REALTEK, + SDIO_DEVICE_ID_REALTEK_RTW8821CS), + .driver_data = (kernel_ulong_t)&rtw8821c_hw_spec, + }, + {} +}; +MODULE_DEVICE_TABLE(sdio, rtw_8821cs_id_table); + +static struct sdio_driver rtw_8821cs_driver = { + .name = "rtw_8821cs", + .probe = rtw_sdio_probe, + .remove = rtw_sdio_remove, + .id_table = rtw_8821cs_id_table, + .drv = { + .pm = &rtw_sdio_pm_ops, + .shutdown = rtw_sdio_shutdown, + } +}; +module_sdio_driver(rtw_8821cs_driver); + +MODULE_AUTHOR("Martin Blumenstingl "); +MODULE_DESCRIPTION("Realtek 802.11ac wireless 8821cs driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sys/contrib/dev/rtw88/rtw8821cu.c b/sys/contrib/dev/rtw88/rtw8821cu.c new file mode 100644 index 000000000000..35d29abbd17f --- /dev/null +++ b/sys/contrib/dev/rtw88/rtw8821cu.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2018-2019 Realtek Corporation + */ + +#include +#include +#include "main.h" +#include "rtw8821c.h" +#include "usb.h" + +static const struct usb_device_id rtw_8821cu_id_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xb82b, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8821c_hw_spec) }, /* 8821CU */ + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xb820, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8821c_hw_spec) }, /* 8821CU */ + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xc821, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8821c_hw_spec) }, /* 8821CU */ + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xc820, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8821c_hw_spec) }, /* 8821CU */ + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xc82a, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8821c_hw_spec) }, /* 8821CU */ + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xc82b, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8821c_hw_spec) }, /* 8821CU */ + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xc811, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8821c_hw_spec) }, /* 8811CU */ + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0x8811, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8821c_hw_spec) }, /* 8811CU */ + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0x2006, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8821c_hw_spec) }, /* TOTOLINK A650UA v3 */ + {}, +}; +MODULE_DEVICE_TABLE(usb, rtw_8821cu_id_table); + +static int rtw_8821cu_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return rtw_usb_probe(intf, id); +} + +static struct usb_driver rtw_8821cu_driver = { + .name = "rtw_8821cu", + .id_table = rtw_8821cu_id_table, + .probe = rtw_8821cu_probe, + .disconnect = rtw_usb_disconnect, +#if defined(__FreeBSD__) && defined(__notyet__) + .bsddriver.name = KBUILD_MODNAME, +#endif +}; +module_usb_driver(rtw_8821cu_driver); + +MODULE_AUTHOR("Hans Ulli Kroll "); +MODULE_DESCRIPTION("Realtek 802.11ac wireless 8821cu driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sys/contrib/dev/rtw88/rtw8822b.c b/sys/contrib/dev/rtw88/rtw8822b.c index d4ec559d599c..c09ebd674af7 100644 --- a/sys/contrib/dev/rtw88/rtw8822b.c +++ b/sys/contrib/dev/rtw88/rtw8822b.c @@ -1,2596 +1,2626 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* Copyright(c) 2018-2019 Realtek Corporation */ #include #include "main.h" #include "coex.h" #include "fw.h" #include "tx.h" #include "rx.h" #include "phy.h" #include "rtw8822b.h" #include "rtw8822b_table.h" #include "mac.h" #include "reg.h" #include "debug.h" #include "bf.h" #include "regd.h" static void rtw8822b_config_trx_mode(struct rtw_dev *rtwdev, u8 tx_path, u8 rx_path, bool is_tx2_path); static void rtw8822be_efuse_parsing(struct rtw_efuse *efuse, struct rtw8822b_efuse *map) { ether_addr_copy(efuse->addr, map->e.mac_addr); } +static void rtw8822bu_efuse_parsing(struct rtw_efuse *efuse, + struct rtw8822b_efuse *map) +{ + ether_addr_copy(efuse->addr, map->u.mac_addr); +} + +static void rtw8822bs_efuse_parsing(struct rtw_efuse *efuse, + struct rtw8822b_efuse *map) +{ + ether_addr_copy(efuse->addr, map->s.mac_addr); +} + static int rtw8822b_read_efuse(struct rtw_dev *rtwdev, u8 *log_map) { struct rtw_efuse *efuse = &rtwdev->efuse; struct rtw8822b_efuse *map; int i; map = (struct rtw8822b_efuse *)log_map; efuse->rfe_option = map->rfe_option; efuse->rf_board_option = map->rf_board_option; efuse->crystal_cap = map->xtal_k; efuse->pa_type_2g = map->pa_type; efuse->pa_type_5g = map->pa_type; efuse->lna_type_2g = map->lna_type_2g[0]; efuse->lna_type_5g = map->lna_type_5g[0]; efuse->channel_plan = map->channel_plan; efuse->country_code[0] = map->country_code[0]; efuse->country_code[1] = map->country_code[1]; efuse->bt_setting = map->rf_bt_setting; efuse->regd = map->rf_board_option & 0x7; efuse->thermal_meter[RF_PATH_A] = map->thermal_meter; efuse->thermal_meter_k = map->thermal_meter; for (i = 0; i < 4; i++) efuse->txpwr_idx_table[i] = map->txpwr_idx_table[i]; switch (rtw_hci_type(rtwdev)) { case RTW_HCI_TYPE_PCIE: rtw8822be_efuse_parsing(efuse, map); break; + case RTW_HCI_TYPE_USB: + rtw8822bu_efuse_parsing(efuse, map); + break; + case RTW_HCI_TYPE_SDIO: + rtw8822bs_efuse_parsing(efuse, map); + break; default: /* unsupported now */ return -ENOTSUPP; } return 0; } static void rtw8822b_phy_rfe_init(struct rtw_dev *rtwdev) { /* chip top mux */ rtw_write32_mask(rtwdev, 0x64, BIT(29) | BIT(28), 0x3); rtw_write32_mask(rtwdev, 0x4c, BIT(26) | BIT(25), 0x0); rtw_write32_mask(rtwdev, 0x40, BIT(2), 0x1); /* from s0 or s1 */ rtw_write32_mask(rtwdev, 0x1990, 0x3f, 0x30); rtw_write32_mask(rtwdev, 0x1990, (BIT(11) | BIT(10)), 0x3); /* input or output */ rtw_write32_mask(rtwdev, 0x974, 0x3f, 0x3f); rtw_write32_mask(rtwdev, 0x974, (BIT(11) | BIT(10)), 0x3); } #define RTW_TXSCALE_SIZE 37 static const u32 rtw8822b_txscale_tbl[RTW_TXSCALE_SIZE] = { 0x081, 0x088, 0x090, 0x099, 0x0a2, 0x0ac, 0x0b6, 0x0c0, 0x0cc, 0x0d8, 0x0e5, 0x0f2, 0x101, 0x110, 0x120, 0x131, 0x143, 0x156, 0x16a, 0x180, 0x197, 0x1af, 0x1c8, 0x1e3, 0x200, 0x21e, 0x23e, 0x261, 0x285, 0x2ab, 0x2d3, 0x2fe, 0x32b, 0x35c, 0x38e, 0x3c4, 0x3fe }; static u8 rtw8822b_get_swing_index(struct rtw_dev *rtwdev) { u8 i = 0; u32 swing, table_value; swing = rtw_read32_mask(rtwdev, 0xc1c, 0xffe00000); for (i = 0; i < RTW_TXSCALE_SIZE; i++) { table_value = rtw8822b_txscale_tbl[i]; if (swing == table_value) break; } return i; } static void rtw8822b_pwrtrack_init(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 swing_idx = rtw8822b_get_swing_index(rtwdev); u8 path; if (swing_idx >= RTW_TXSCALE_SIZE) dm_info->default_ofdm_index = 24; else dm_info->default_ofdm_index = swing_idx; for (path = RF_PATH_A; path < rtwdev->hal.rf_path_num; path++) { ewma_thermal_init(&dm_info->avg_thermal[path]); dm_info->delta_power_index[path] = 0; } dm_info->pwr_trk_triggered = false; dm_info->pwr_trk_init_trigger = true; dm_info->thermal_meter_k = rtwdev->efuse.thermal_meter_k; } static void rtw8822b_phy_bf_init(struct rtw_dev *rtwdev) { rtw_bf_phy_init(rtwdev); /* Grouping bitmap parameters */ rtw_write32(rtwdev, 0x1C94, 0xAFFFAFFF); } static void rtw8822b_phy_set_param(struct rtw_dev *rtwdev) { struct rtw_hal *hal = &rtwdev->hal; u8 crystal_cap; bool is_tx2_path; /* power on BB/RF domain */ rtw_write8_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_BB_RSTB | BIT_FEN_BB_GLB_RST); rtw_write8_set(rtwdev, REG_RF_CTRL, BIT_RF_EN | BIT_RF_RSTB | BIT_RF_SDM_RSTB); rtw_write32_set(rtwdev, REG_WLRF1, BIT_WLRF1_BBRF_EN); /* pre init before header files config */ rtw_write32_clr(rtwdev, REG_RXPSEL, BIT_RX_PSEL_RST); rtw_phy_load_tables(rtwdev); crystal_cap = rtwdev->efuse.crystal_cap & 0x3F; rtw_write32_mask(rtwdev, 0x24, 0x7e000000, crystal_cap); rtw_write32_mask(rtwdev, 0x28, 0x7e, crystal_cap); /* post init after header files config */ rtw_write32_set(rtwdev, REG_RXPSEL, BIT_RX_PSEL_RST); is_tx2_path = false; rtw8822b_config_trx_mode(rtwdev, hal->antenna_tx, hal->antenna_rx, is_tx2_path); rtw_phy_init(rtwdev); rtw8822b_phy_rfe_init(rtwdev); rtw8822b_pwrtrack_init(rtwdev); rtw8822b_phy_bf_init(rtwdev); } #define WLAN_SLOT_TIME 0x09 #define WLAN_PIFS_TIME 0x19 #define WLAN_SIFS_CCK_CONT_TX 0xA #define WLAN_SIFS_OFDM_CONT_TX 0xE #define WLAN_SIFS_CCK_TRX 0x10 #define WLAN_SIFS_OFDM_TRX 0x10 #define WLAN_VO_TXOP_LIMIT 0x186 /* unit : 32us */ #define WLAN_VI_TXOP_LIMIT 0x3BC /* unit : 32us */ #define WLAN_RDG_NAV 0x05 #define WLAN_TXOP_NAV 0x1B #define WLAN_CCK_RX_TSF 0x30 #define WLAN_OFDM_RX_TSF 0x30 #define WLAN_TBTT_PROHIBIT 0x04 /* unit : 32us */ #define WLAN_TBTT_HOLD_TIME 0x064 /* unit : 32us */ #define WLAN_DRV_EARLY_INT 0x04 #define WLAN_BCN_DMA_TIME 0x02 #define WLAN_RX_FILTER0 0x0FFFFFFF #define WLAN_RX_FILTER2 0xFFFF #define WLAN_RCR_CFG 0xE400220E #define WLAN_RXPKT_MAX_SZ 12288 #define WLAN_RXPKT_MAX_SZ_512 (WLAN_RXPKT_MAX_SZ >> 9) #define WLAN_AMPDU_MAX_TIME 0x70 #define WLAN_RTS_LEN_TH 0xFF #define WLAN_RTS_TX_TIME_TH 0x08 #define WLAN_MAX_AGG_PKT_LIMIT 0x20 #define WLAN_RTS_MAX_AGG_PKT_LIMIT 0x20 #define FAST_EDCA_VO_TH 0x06 #define FAST_EDCA_VI_TH 0x06 #define FAST_EDCA_BE_TH 0x06 #define FAST_EDCA_BK_TH 0x06 #define WLAN_BAR_RETRY_LIMIT 0x01 #define WLAN_RA_TRY_RATE_AGG_LIMIT 0x08 #define WLAN_TX_FUNC_CFG1 0x30 #define WLAN_TX_FUNC_CFG2 0x30 #define WLAN_MAC_OPT_NORM_FUNC1 0x98 #define WLAN_MAC_OPT_LB_FUNC1 0x80 #define WLAN_MAC_OPT_FUNC2 0xb0810041 #define WLAN_SIFS_CFG (WLAN_SIFS_CCK_CONT_TX | \ (WLAN_SIFS_OFDM_CONT_TX << BIT_SHIFT_SIFS_OFDM_CTX) | \ (WLAN_SIFS_CCK_TRX << BIT_SHIFT_SIFS_CCK_TRX) | \ (WLAN_SIFS_OFDM_TRX << BIT_SHIFT_SIFS_OFDM_TRX)) #define WLAN_TBTT_TIME (WLAN_TBTT_PROHIBIT |\ (WLAN_TBTT_HOLD_TIME << BIT_SHIFT_TBTT_HOLD_TIME_AP)) #define WLAN_NAV_CFG (WLAN_RDG_NAV | (WLAN_TXOP_NAV << 16)) #define WLAN_RX_TSF_CFG (WLAN_CCK_RX_TSF | (WLAN_OFDM_RX_TSF) << 8) static int rtw8822b_mac_init(struct rtw_dev *rtwdev) { u32 value32; /* protocol configuration */ rtw_write8_clr(rtwdev, REG_SW_AMPDU_BURST_MODE_CTRL, BIT_PRE_TX_CMD); rtw_write8(rtwdev, REG_AMPDU_MAX_TIME_V1, WLAN_AMPDU_MAX_TIME); rtw_write8_set(rtwdev, REG_TX_HANG_CTRL, BIT_EN_EOF_V1); value32 = WLAN_RTS_LEN_TH | (WLAN_RTS_TX_TIME_TH << 8) | (WLAN_MAX_AGG_PKT_LIMIT << 16) | (WLAN_RTS_MAX_AGG_PKT_LIMIT << 24); rtw_write32(rtwdev, REG_PROT_MODE_CTRL, value32); rtw_write16(rtwdev, REG_BAR_MODE_CTRL + 2, WLAN_BAR_RETRY_LIMIT | WLAN_RA_TRY_RATE_AGG_LIMIT << 8); rtw_write8(rtwdev, REG_FAST_EDCA_VOVI_SETTING, FAST_EDCA_VO_TH); rtw_write8(rtwdev, REG_FAST_EDCA_VOVI_SETTING + 2, FAST_EDCA_VI_TH); rtw_write8(rtwdev, REG_FAST_EDCA_BEBK_SETTING, FAST_EDCA_BE_TH); rtw_write8(rtwdev, REG_FAST_EDCA_BEBK_SETTING + 2, FAST_EDCA_BK_TH); /* EDCA configuration */ rtw_write8_clr(rtwdev, REG_TIMER0_SRC_SEL, BIT_TSFT_SEL_TIMER0); rtw_write16(rtwdev, REG_TXPAUSE, 0x0000); rtw_write8(rtwdev, REG_SLOT, WLAN_SLOT_TIME); rtw_write8(rtwdev, REG_PIFS, WLAN_PIFS_TIME); rtw_write32(rtwdev, REG_SIFS, WLAN_SIFS_CFG); rtw_write16(rtwdev, REG_EDCA_VO_PARAM + 2, WLAN_VO_TXOP_LIMIT); rtw_write16(rtwdev, REG_EDCA_VI_PARAM + 2, WLAN_VI_TXOP_LIMIT); rtw_write32(rtwdev, REG_RD_NAV_NXT, WLAN_NAV_CFG); rtw_write16(rtwdev, REG_RXTSF_OFFSET_CCK, WLAN_RX_TSF_CFG); /* Set beacon cotnrol - enable TSF and other related functions */ rtw_write8_set(rtwdev, REG_BCN_CTRL, BIT_EN_BCN_FUNCTION); /* Set send beacon related registers */ rtw_write32(rtwdev, REG_TBTT_PROHIBIT, WLAN_TBTT_TIME); rtw_write8(rtwdev, REG_DRVERLYINT, WLAN_DRV_EARLY_INT); rtw_write8(rtwdev, REG_BCNDMATIM, WLAN_BCN_DMA_TIME); rtw_write8_clr(rtwdev, REG_TX_PTCL_CTRL + 1, BIT_SIFS_BK_EN >> 8); /* WMAC configuration */ rtw_write32(rtwdev, REG_RXFLTMAP0, WLAN_RX_FILTER0); rtw_write16(rtwdev, REG_RXFLTMAP2, WLAN_RX_FILTER2); rtw_write32(rtwdev, REG_RCR, WLAN_RCR_CFG); rtw_write8(rtwdev, REG_RX_PKT_LIMIT, WLAN_RXPKT_MAX_SZ_512); rtw_write8(rtwdev, REG_TCR + 2, WLAN_TX_FUNC_CFG2); rtw_write8(rtwdev, REG_TCR + 1, WLAN_TX_FUNC_CFG1); rtw_write32(rtwdev, REG_WMAC_OPTION_FUNCTION + 8, WLAN_MAC_OPT_FUNC2); rtw_write8(rtwdev, REG_WMAC_OPTION_FUNCTION + 4, WLAN_MAC_OPT_NORM_FUNC1); rtw_write8_set(rtwdev, REG_SND_PTCL_CTRL, BIT_DIS_CHK_VHTSIGB_CRC); return 0; } static void rtw8822b_set_channel_rfe_efem(struct rtw_dev *rtwdev, u8 channel) { struct rtw_hal *hal = &rtwdev->hal; if (IS_CH_2G_BAND(channel)) { rtw_write32s_mask(rtwdev, REG_RFESEL0, 0xffffff, 0x705770); rtw_write32s_mask(rtwdev, REG_RFESEL8, MASKBYTE1, 0x57); rtw_write32s_mask(rtwdev, REG_RFECTL, BIT(4), 0); } else { rtw_write32s_mask(rtwdev, REG_RFESEL0, 0xffffff, 0x177517); rtw_write32s_mask(rtwdev, REG_RFESEL8, MASKBYTE1, 0x75); rtw_write32s_mask(rtwdev, REG_RFECTL, BIT(5), 0); } rtw_write32s_mask(rtwdev, REG_RFEINV, BIT(11) | BIT(10) | 0x3f, 0x0); if (hal->antenna_rx == BB_PATH_AB || hal->antenna_tx == BB_PATH_AB) { /* 2TX or 2RX */ rtw_write32s_mask(rtwdev, REG_TRSW, MASKLWORD, 0xa501); } else if (hal->antenna_rx == hal->antenna_tx) { /* TXA+RXA or TXB+RXB */ rtw_write32s_mask(rtwdev, REG_TRSW, MASKLWORD, 0xa500); } else { /* TXB+RXA or TXA+RXB */ rtw_write32s_mask(rtwdev, REG_TRSW, MASKLWORD, 0xa005); } } static void rtw8822b_set_channel_rfe_ifem(struct rtw_dev *rtwdev, u8 channel) { struct rtw_hal *hal = &rtwdev->hal; if (IS_CH_2G_BAND(channel)) { /* signal source */ rtw_write32s_mask(rtwdev, REG_RFESEL0, 0xffffff, 0x745774); rtw_write32s_mask(rtwdev, REG_RFESEL8, MASKBYTE1, 0x57); } else { /* signal source */ rtw_write32s_mask(rtwdev, REG_RFESEL0, 0xffffff, 0x477547); rtw_write32s_mask(rtwdev, REG_RFESEL8, MASKBYTE1, 0x75); } rtw_write32s_mask(rtwdev, REG_RFEINV, BIT(11) | BIT(10) | 0x3f, 0x0); if (IS_CH_2G_BAND(channel)) { if (hal->antenna_rx == BB_PATH_AB || hal->antenna_tx == BB_PATH_AB) { /* 2TX or 2RX */ rtw_write32s_mask(rtwdev, REG_TRSW, MASKLWORD, 0xa501); } else if (hal->antenna_rx == hal->antenna_tx) { /* TXA+RXA or TXB+RXB */ rtw_write32s_mask(rtwdev, REG_TRSW, MASKLWORD, 0xa500); } else { /* TXB+RXA or TXA+RXB */ rtw_write32s_mask(rtwdev, REG_TRSW, MASKLWORD, 0xa005); } } else { rtw_write32s_mask(rtwdev, REG_TRSW, MASKLWORD, 0xa5a5); } } enum { CCUT_IDX_1R_2G, CCUT_IDX_2R_2G, CCUT_IDX_1R_5G, CCUT_IDX_2R_5G, CCUT_IDX_NR, }; struct cca_ccut { u32 reg82c[CCUT_IDX_NR]; u32 reg830[CCUT_IDX_NR]; u32 reg838[CCUT_IDX_NR]; }; static const struct cca_ccut cca_ifem_ccut = { {0x75C97010, 0x75C97010, 0x75C97010, 0x75C97010}, /*Reg82C*/ {0x79a0eaaa, 0x79A0EAAC, 0x79a0eaaa, 0x79a0eaaa}, /*Reg830*/ {0x87765541, 0x87746341, 0x87765541, 0x87746341}, /*Reg838*/ }; static const struct cca_ccut cca_efem_ccut = { {0x75B86010, 0x75B76010, 0x75B86010, 0x75B76010}, /*Reg82C*/ {0x79A0EAA8, 0x79A0EAAC, 0x79A0EAA8, 0x79a0eaaa}, /*Reg830*/ {0x87766451, 0x87766431, 0x87766451, 0x87766431}, /*Reg838*/ }; static const struct cca_ccut cca_ifem_ccut_ext = { {0x75da8010, 0x75da8010, 0x75da8010, 0x75da8010}, /*Reg82C*/ {0x79a0eaaa, 0x97A0EAAC, 0x79a0eaaa, 0x79a0eaaa}, /*Reg830*/ {0x87765541, 0x86666341, 0x87765561, 0x86666361}, /*Reg838*/ }; static void rtw8822b_get_cca_val(const struct cca_ccut *cca_ccut, u8 col, u32 *reg82c, u32 *reg830, u32 *reg838) { *reg82c = cca_ccut->reg82c[col]; *reg830 = cca_ccut->reg830[col]; *reg838 = cca_ccut->reg838[col]; } struct rtw8822b_rfe_info { const struct cca_ccut *cca_ccut_2g; const struct cca_ccut *cca_ccut_5g; enum rtw_rfe_fem fem; bool ifem_ext; void (*rtw_set_channel_rfe)(struct rtw_dev *rtwdev, u8 channel); }; #define I2GE5G_CCUT(set_ch) { \ .cca_ccut_2g = &cca_ifem_ccut, \ .cca_ccut_5g = &cca_efem_ccut, \ .fem = RTW_RFE_IFEM2G_EFEM5G, \ .ifem_ext = false, \ .rtw_set_channel_rfe = &rtw8822b_set_channel_rfe_ ## set_ch, \ } #define IFEM_EXT_CCUT(set_ch) { \ .cca_ccut_2g = &cca_ifem_ccut_ext, \ .cca_ccut_5g = &cca_ifem_ccut_ext, \ .fem = RTW_RFE_IFEM, \ .ifem_ext = true, \ .rtw_set_channel_rfe = &rtw8822b_set_channel_rfe_ ## set_ch, \ } static const struct rtw8822b_rfe_info rtw8822b_rfe_info[] = { [2] = I2GE5G_CCUT(efem), [3] = IFEM_EXT_CCUT(ifem), [5] = IFEM_EXT_CCUT(ifem), }; static void rtw8822b_set_channel_cca(struct rtw_dev *rtwdev, u8 channel, u8 bw, const struct rtw8822b_rfe_info *rfe_info) { struct rtw_hal *hal = &rtwdev->hal; struct rtw_efuse *efuse = &rtwdev->efuse; const struct cca_ccut *cca_ccut; u8 col; u32 reg82c, reg830, reg838; bool is_efem_cca = false, is_ifem_cca = false, is_rfe_type = false; if (IS_CH_2G_BAND(channel)) { cca_ccut = rfe_info->cca_ccut_2g; if (hal->antenna_rx == BB_PATH_A || hal->antenna_rx == BB_PATH_B) col = CCUT_IDX_1R_2G; else col = CCUT_IDX_2R_2G; } else { cca_ccut = rfe_info->cca_ccut_5g; if (hal->antenna_rx == BB_PATH_A || hal->antenna_rx == BB_PATH_B) col = CCUT_IDX_1R_5G; else col = CCUT_IDX_2R_5G; } rtw8822b_get_cca_val(cca_ccut, col, ®82c, ®830, ®838); switch (rfe_info->fem) { case RTW_RFE_IFEM: default: is_ifem_cca = true; if (rfe_info->ifem_ext) is_rfe_type = true; break; case RTW_RFE_EFEM: is_efem_cca = true; break; case RTW_RFE_IFEM2G_EFEM5G: if (IS_CH_2G_BAND(channel)) is_ifem_cca = true; else is_efem_cca = true; break; } if (is_ifem_cca) { if ((hal->cut_version == RTW_CHIP_VER_CUT_B && (col == CCUT_IDX_2R_2G || col == CCUT_IDX_2R_5G) && bw == RTW_CHANNEL_WIDTH_40) || (!is_rfe_type && col == CCUT_IDX_2R_5G && bw == RTW_CHANNEL_WIDTH_40) || (efuse->rfe_option == 5 && col == CCUT_IDX_2R_5G)) reg830 = 0x79a0ea28; } rtw_write32_mask(rtwdev, REG_CCASEL, MASKDWORD, reg82c); rtw_write32_mask(rtwdev, REG_PDMFTH, MASKDWORD, reg830); rtw_write32_mask(rtwdev, REG_CCA2ND, MASKDWORD, reg838); if (is_efem_cca && !(hal->cut_version == RTW_CHIP_VER_CUT_B)) rtw_write32_mask(rtwdev, REG_L1WT, MASKDWORD, 0x9194b2b9); if (bw == RTW_CHANNEL_WIDTH_20 && IS_CH_5G_BAND_MID(channel)) rtw_write32_mask(rtwdev, REG_CCA2ND, 0xf0, 0x4); } static const u8 low_band[15] = {0x7, 0x6, 0x6, 0x5, 0x0, 0x0, 0x7, 0xff, 0x6, 0x5, 0x0, 0x0, 0x7, 0x6, 0x6}; static const u8 middle_band[23] = {0x6, 0x5, 0x0, 0x0, 0x7, 0x6, 0x6, 0xff, 0x0, 0x0, 0x7, 0x6, 0x6, 0x5, 0x0, 0xff, 0x7, 0x6, 0x6, 0x5, 0x0, 0x0, 0x7}; static const u8 high_band[15] = {0x5, 0x5, 0x0, 0x7, 0x7, 0x6, 0x5, 0xff, 0x0, 0x7, 0x7, 0x6, 0x5, 0x5, 0x0}; static void rtw8822b_set_channel_rf(struct rtw_dev *rtwdev, u8 channel, u8 bw) { #define RF18_BAND_MASK (BIT(16) | BIT(9) | BIT(8)) #define RF18_BAND_2G (0) #define RF18_BAND_5G (BIT(16) | BIT(8)) #define RF18_CHANNEL_MASK (MASKBYTE0) #define RF18_RFSI_MASK (BIT(18) | BIT(17)) #define RF18_RFSI_GE_CH80 (BIT(17)) #define RF18_RFSI_GT_CH144 (BIT(18)) #define RF18_BW_MASK (BIT(11) | BIT(10)) #define RF18_BW_20M (BIT(11) | BIT(10)) #define RF18_BW_40M (BIT(11)) #define RF18_BW_80M (BIT(10)) #define RFBE_MASK (BIT(17) | BIT(16) | BIT(15)) struct rtw_hal *hal = &rtwdev->hal; u32 rf_reg18, rf_reg_be; rf_reg18 = rtw_read_rf(rtwdev, RF_PATH_A, 0x18, RFREG_MASK); rf_reg18 &= ~(RF18_BAND_MASK | RF18_CHANNEL_MASK | RF18_RFSI_MASK | RF18_BW_MASK); rf_reg18 |= (IS_CH_2G_BAND(channel) ? RF18_BAND_2G : RF18_BAND_5G); rf_reg18 |= (channel & RF18_CHANNEL_MASK); if (channel > 144) rf_reg18 |= RF18_RFSI_GT_CH144; else if (channel >= 80) rf_reg18 |= RF18_RFSI_GE_CH80; switch (bw) { case RTW_CHANNEL_WIDTH_5: case RTW_CHANNEL_WIDTH_10: case RTW_CHANNEL_WIDTH_20: default: rf_reg18 |= RF18_BW_20M; break; case RTW_CHANNEL_WIDTH_40: rf_reg18 |= RF18_BW_40M; break; case RTW_CHANNEL_WIDTH_80: rf_reg18 |= RF18_BW_80M; break; } if (IS_CH_2G_BAND(channel)) rf_reg_be = 0x0; else if (IS_CH_5G_BAND_1(channel) || IS_CH_5G_BAND_2(channel)) rf_reg_be = low_band[(channel - 36) >> 1]; else if (IS_CH_5G_BAND_3(channel)) rf_reg_be = middle_band[(channel - 100) >> 1]; else if (IS_CH_5G_BAND_4(channel)) rf_reg_be = high_band[(channel - 149) >> 1]; else goto err; rtw_write_rf(rtwdev, RF_PATH_A, RF_MALSEL, RFBE_MASK, rf_reg_be); /* need to set 0xdf[18]=1 before writing RF18 when channel 144 */ if (channel == 144) rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTDBG, BIT(18), 0x1); else rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTDBG, BIT(18), 0x0); rtw_write_rf(rtwdev, RF_PATH_A, 0x18, RFREG_MASK, rf_reg18); if (hal->rf_type > RF_1T1R) rtw_write_rf(rtwdev, RF_PATH_B, 0x18, RFREG_MASK, rf_reg18); rtw_write_rf(rtwdev, RF_PATH_A, RF_XTALX2, BIT(19), 0); rtw_write_rf(rtwdev, RF_PATH_A, RF_XTALX2, BIT(19), 1); return; err: WARN_ON(1); } static void rtw8822b_toggle_igi(struct rtw_dev *rtwdev) { struct rtw_hal *hal = &rtwdev->hal; u32 igi; igi = rtw_read32_mask(rtwdev, REG_RXIGI_A, 0x7f); rtw_write32_mask(rtwdev, REG_RXIGI_A, 0x7f, igi - 2); rtw_write32_mask(rtwdev, REG_RXIGI_A, 0x7f, igi); rtw_write32_mask(rtwdev, REG_RXIGI_B, 0x7f, igi - 2); rtw_write32_mask(rtwdev, REG_RXIGI_B, 0x7f, igi); rtw_write32_mask(rtwdev, REG_RXPSEL, MASKBYTE0, 0x0); rtw_write32_mask(rtwdev, REG_RXPSEL, MASKBYTE0, hal->antenna_rx | (hal->antenna_rx << 4)); } static void rtw8822b_set_channel_rxdfir(struct rtw_dev *rtwdev, u8 bw) { if (bw == RTW_CHANNEL_WIDTH_40) { /* RX DFIR for BW40 */ rtw_write32_mask(rtwdev, REG_ACBB0, BIT(29) | BIT(28), 0x1); rtw_write32_mask(rtwdev, REG_ACBBRXFIR, BIT(29) | BIT(28), 0x0); rtw_write32s_mask(rtwdev, REG_TXDFIR, BIT(31), 0x0); } else if (bw == RTW_CHANNEL_WIDTH_80) { /* RX DFIR for BW80 */ rtw_write32_mask(rtwdev, REG_ACBB0, BIT(29) | BIT(28), 0x2); rtw_write32_mask(rtwdev, REG_ACBBRXFIR, BIT(29) | BIT(28), 0x1); rtw_write32s_mask(rtwdev, REG_TXDFIR, BIT(31), 0x0); } else { /* RX DFIR for BW20, BW10 and BW5*/ rtw_write32_mask(rtwdev, REG_ACBB0, BIT(29) | BIT(28), 0x2); rtw_write32_mask(rtwdev, REG_ACBBRXFIR, BIT(29) | BIT(28), 0x2); rtw_write32s_mask(rtwdev, REG_TXDFIR, BIT(31), 0x1); } } static void rtw8822b_set_channel_bb(struct rtw_dev *rtwdev, u8 channel, u8 bw, u8 primary_ch_idx) { struct rtw_efuse *efuse = &rtwdev->efuse; u8 rfe_option = efuse->rfe_option; u32 val32; if (IS_CH_2G_BAND(channel)) { rtw_write32_mask(rtwdev, REG_RXPSEL, BIT(28), 0x1); rtw_write32_mask(rtwdev, REG_CCK_CHECK, BIT(7), 0x0); rtw_write32_mask(rtwdev, REG_ENTXCCK, BIT(18), 0x0); rtw_write32_mask(rtwdev, REG_RXCCAMSK, 0x0000FC00, 15); rtw_write32_mask(rtwdev, REG_ACGG2TBL, 0x1f, 0x0); rtw_write32_mask(rtwdev, REG_CLKTRK, 0x1ffe0000, 0x96a); if (channel == 14) { rtw_write32_mask(rtwdev, REG_TXSF2, MASKDWORD, 0x00006577); rtw_write32_mask(rtwdev, REG_TXSF6, MASKLWORD, 0x0000); } else { rtw_write32_mask(rtwdev, REG_TXSF2, MASKDWORD, 0x384f6577); rtw_write32_mask(rtwdev, REG_TXSF6, MASKLWORD, 0x1525); } rtw_write32_mask(rtwdev, REG_RFEINV, 0x300, 0x2); } else if (IS_CH_5G_BAND(channel)) { rtw_write32_mask(rtwdev, REG_ENTXCCK, BIT(18), 0x1); rtw_write32_mask(rtwdev, REG_CCK_CHECK, BIT(7), 0x1); rtw_write32_mask(rtwdev, REG_RXPSEL, BIT(28), 0x0); rtw_write32_mask(rtwdev, REG_RXCCAMSK, 0x0000FC00, 34); if (IS_CH_5G_BAND_1(channel) || IS_CH_5G_BAND_2(channel)) rtw_write32_mask(rtwdev, REG_ACGG2TBL, 0x1f, 0x1); else if (IS_CH_5G_BAND_3(channel)) rtw_write32_mask(rtwdev, REG_ACGG2TBL, 0x1f, 0x2); else if (IS_CH_5G_BAND_4(channel)) rtw_write32_mask(rtwdev, REG_ACGG2TBL, 0x1f, 0x3); if (IS_CH_5G_BAND_1(channel)) rtw_write32_mask(rtwdev, REG_CLKTRK, 0x1ffe0000, 0x494); else if (IS_CH_5G_BAND_2(channel)) rtw_write32_mask(rtwdev, REG_CLKTRK, 0x1ffe0000, 0x453); else if (channel >= 100 && channel <= 116) rtw_write32_mask(rtwdev, REG_CLKTRK, 0x1ffe0000, 0x452); else if (channel >= 118 && channel <= 177) rtw_write32_mask(rtwdev, REG_CLKTRK, 0x1ffe0000, 0x412); rtw_write32_mask(rtwdev, 0xcbc, 0x300, 0x1); } switch (bw) { case RTW_CHANNEL_WIDTH_20: default: val32 = rtw_read32_mask(rtwdev, REG_ADCCLK, MASKDWORD); val32 &= 0xFFCFFC00; val32 |= (RTW_CHANNEL_WIDTH_20); rtw_write32_mask(rtwdev, REG_ADCCLK, MASKDWORD, val32); rtw_write32_mask(rtwdev, REG_ADC160, BIT(30), 0x1); break; case RTW_CHANNEL_WIDTH_40: if (primary_ch_idx == RTW_SC_20_UPPER) rtw_write32_set(rtwdev, REG_RXSB, BIT(4)); else rtw_write32_clr(rtwdev, REG_RXSB, BIT(4)); val32 = rtw_read32_mask(rtwdev, REG_ADCCLK, MASKDWORD); val32 &= 0xFF3FF300; val32 |= (((primary_ch_idx & 0xf) << 2) | RTW_CHANNEL_WIDTH_40); rtw_write32_mask(rtwdev, REG_ADCCLK, MASKDWORD, val32); rtw_write32_mask(rtwdev, REG_ADC160, BIT(30), 0x1); break; case RTW_CHANNEL_WIDTH_80: val32 = rtw_read32_mask(rtwdev, REG_ADCCLK, MASKDWORD); val32 &= 0xFCEFCF00; val32 |= (((primary_ch_idx & 0xf) << 2) | RTW_CHANNEL_WIDTH_80); rtw_write32_mask(rtwdev, REG_ADCCLK, MASKDWORD, val32); rtw_write32_mask(rtwdev, REG_ADC160, BIT(30), 0x1); if (rfe_option == 2 || rfe_option == 3) { rtw_write32_mask(rtwdev, REG_L1PKWT, 0x0000f000, 0x6); rtw_write32_mask(rtwdev, REG_ADC40, BIT(10), 0x1); } break; case RTW_CHANNEL_WIDTH_5: val32 = rtw_read32_mask(rtwdev, REG_ADCCLK, MASKDWORD); val32 &= 0xEFEEFE00; val32 |= ((BIT(6) | RTW_CHANNEL_WIDTH_20)); rtw_write32_mask(rtwdev, REG_ADCCLK, MASKDWORD, val32); rtw_write32_mask(rtwdev, REG_ADC160, BIT(30), 0x0); rtw_write32_mask(rtwdev, REG_ADC40, BIT(31), 0x1); break; case RTW_CHANNEL_WIDTH_10: val32 = rtw_read32_mask(rtwdev, REG_ADCCLK, MASKDWORD); val32 &= 0xEFFEFF00; val32 |= ((BIT(7) | RTW_CHANNEL_WIDTH_20)); rtw_write32_mask(rtwdev, REG_ADCCLK, MASKDWORD, val32); rtw_write32_mask(rtwdev, REG_ADC160, BIT(30), 0x0); rtw_write32_mask(rtwdev, REG_ADC40, BIT(31), 0x1); break; } } static void rtw8822b_set_channel(struct rtw_dev *rtwdev, u8 channel, u8 bw, u8 primary_chan_idx) { struct rtw_efuse *efuse = &rtwdev->efuse; const struct rtw8822b_rfe_info *rfe_info; if (WARN(efuse->rfe_option >= ARRAY_SIZE(rtw8822b_rfe_info), "rfe_option %d is out of boundary\n", efuse->rfe_option)) return; rfe_info = &rtw8822b_rfe_info[efuse->rfe_option]; rtw8822b_set_channel_bb(rtwdev, channel, bw, primary_chan_idx); rtw_set_channel_mac(rtwdev, channel, bw, primary_chan_idx); rtw8822b_set_channel_rf(rtwdev, channel, bw); rtw8822b_set_channel_rxdfir(rtwdev, bw); rtw8822b_toggle_igi(rtwdev); rtw8822b_set_channel_cca(rtwdev, channel, bw, rfe_info); (*rfe_info->rtw_set_channel_rfe)(rtwdev, channel); } static void rtw8822b_config_trx_mode(struct rtw_dev *rtwdev, u8 tx_path, u8 rx_path, bool is_tx2_path) { struct rtw_efuse *efuse = &rtwdev->efuse; const struct rtw8822b_rfe_info *rfe_info; u8 ch = rtwdev->hal.current_channel; u8 tx_path_sel, rx_path_sel; int counter; if (WARN(efuse->rfe_option >= ARRAY_SIZE(rtw8822b_rfe_info), "rfe_option %d is out of boundary\n", efuse->rfe_option)) return; rfe_info = &rtw8822b_rfe_info[efuse->rfe_option]; if ((tx_path | rx_path) & BB_PATH_A) rtw_write32_mask(rtwdev, REG_AGCTR_A, MASKLWORD, 0x3231); else rtw_write32_mask(rtwdev, REG_AGCTR_A, MASKLWORD, 0x1111); if ((tx_path | rx_path) & BB_PATH_B) rtw_write32_mask(rtwdev, REG_AGCTR_B, MASKLWORD, 0x3231); else rtw_write32_mask(rtwdev, REG_AGCTR_B, MASKLWORD, 0x1111); rtw_write32_mask(rtwdev, REG_CDDTXP, (BIT(19) | BIT(18)), 0x3); rtw_write32_mask(rtwdev, REG_TXPSEL, (BIT(29) | BIT(28)), 0x1); rtw_write32_mask(rtwdev, REG_TXPSEL, BIT(30), 0x1); if (tx_path & BB_PATH_A) { rtw_write32_mask(rtwdev, REG_CDDTXP, 0xfff00000, 0x001); rtw_write32_mask(rtwdev, REG_ADCINI, 0xf0000000, 0x8); } else if (tx_path & BB_PATH_B) { rtw_write32_mask(rtwdev, REG_CDDTXP, 0xfff00000, 0x002); rtw_write32_mask(rtwdev, REG_ADCINI, 0xf0000000, 0x4); } if (tx_path == BB_PATH_A || tx_path == BB_PATH_B) rtw_write32_mask(rtwdev, REG_TXPSEL1, 0xfff0, 0x01); else rtw_write32_mask(rtwdev, REG_TXPSEL1, 0xfff0, 0x43); tx_path_sel = (tx_path << 4) | tx_path; rtw_write32_mask(rtwdev, REG_TXPSEL, MASKBYTE0, tx_path_sel); if (tx_path != BB_PATH_A && tx_path != BB_PATH_B) { if (is_tx2_path || rtwdev->mp_mode) { rtw_write32_mask(rtwdev, REG_CDDTXP, 0xfff00000, 0x043); rtw_write32_mask(rtwdev, REG_ADCINI, 0xf0000000, 0xc); } } rtw_write32_mask(rtwdev, REG_RXDESC, BIT(22), 0x0); rtw_write32_mask(rtwdev, REG_RXDESC, BIT(18), 0x0); if (rx_path & BB_PATH_A) rtw_write32_mask(rtwdev, REG_ADCINI, 0x0f000000, 0x0); else if (rx_path & BB_PATH_B) rtw_write32_mask(rtwdev, REG_ADCINI, 0x0f000000, 0x5); rx_path_sel = (rx_path << 4) | rx_path; rtw_write32_mask(rtwdev, REG_RXPSEL, MASKBYTE0, rx_path_sel); if (rx_path == BB_PATH_A || rx_path == BB_PATH_B) { rtw_write32_mask(rtwdev, REG_ANTWT, BIT(16), 0x0); rtw_write32_mask(rtwdev, REG_HTSTFWT, BIT(28), 0x0); rtw_write32_mask(rtwdev, REG_MRC, BIT(23), 0x0); } else { rtw_write32_mask(rtwdev, REG_ANTWT, BIT(16), 0x1); rtw_write32_mask(rtwdev, REG_HTSTFWT, BIT(28), 0x1); rtw_write32_mask(rtwdev, REG_MRC, BIT(23), 0x1); } for (counter = 100; counter > 0; counter--) { u32 rf_reg33; rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, RFREG_MASK, 0x80000); rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWA, RFREG_MASK, 0x00001); udelay(2); rf_reg33 = rtw_read_rf(rtwdev, RF_PATH_A, 0x33, RFREG_MASK); if (rf_reg33 == 0x00001) break; } if (WARN(counter <= 0, "write RF mode table fail\n")) return; rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, RFREG_MASK, 0x80000); rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWA, RFREG_MASK, 0x00001); rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWD1, RFREG_MASK, 0x00034); rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWD0, RFREG_MASK, 0x4080c); rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, RFREG_MASK, 0x00000); rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, RFREG_MASK, 0x00000); rtw8822b_toggle_igi(rtwdev); rtw8822b_set_channel_cca(rtwdev, 1, RTW_CHANNEL_WIDTH_20, rfe_info); (*rfe_info->rtw_set_channel_rfe)(rtwdev, ch); } static void query_phy_status_page0(struct rtw_dev *rtwdev, u8 *phy_status, struct rtw_rx_pkt_stat *pkt_stat) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; s8 min_rx_power = -120; u8 pwdb = GET_PHY_STAT_P0_PWDB(phy_status); /* 8822B uses only 1 antenna to RX CCK rates */ pkt_stat->rx_power[RF_PATH_A] = pwdb - 110; pkt_stat->rssi = rtw_phy_rf_power_2_rssi(pkt_stat->rx_power, 1); pkt_stat->bw = RTW_CHANNEL_WIDTH_20; pkt_stat->signal_power = max(pkt_stat->rx_power[RF_PATH_A], min_rx_power); dm_info->rssi[RF_PATH_A] = pkt_stat->rssi; } static void query_phy_status_page1(struct rtw_dev *rtwdev, u8 *phy_status, struct rtw_rx_pkt_stat *pkt_stat) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 rxsc, bw; s8 min_rx_power = -120; s8 rx_evm; u8 evm_dbm = 0; u8 rssi; int path; if (pkt_stat->rate > DESC_RATE11M && pkt_stat->rate < DESC_RATEMCS0) rxsc = GET_PHY_STAT_P1_L_RXSC(phy_status); else rxsc = GET_PHY_STAT_P1_HT_RXSC(phy_status); if (rxsc >= 1 && rxsc <= 8) bw = RTW_CHANNEL_WIDTH_20; else if (rxsc >= 9 && rxsc <= 12) bw = RTW_CHANNEL_WIDTH_40; else if (rxsc >= 13) bw = RTW_CHANNEL_WIDTH_80; else bw = GET_PHY_STAT_P1_RF_MODE(phy_status); pkt_stat->rx_power[RF_PATH_A] = GET_PHY_STAT_P1_PWDB_A(phy_status) - 110; pkt_stat->rx_power[RF_PATH_B] = GET_PHY_STAT_P1_PWDB_B(phy_status) - 110; pkt_stat->rssi = rtw_phy_rf_power_2_rssi(pkt_stat->rx_power, 2); pkt_stat->bw = bw; pkt_stat->signal_power = max3(pkt_stat->rx_power[RF_PATH_A], pkt_stat->rx_power[RF_PATH_B], min_rx_power); dm_info->curr_rx_rate = pkt_stat->rate; pkt_stat->rx_evm[RF_PATH_A] = GET_PHY_STAT_P1_RXEVM_A(phy_status); pkt_stat->rx_evm[RF_PATH_B] = GET_PHY_STAT_P1_RXEVM_B(phy_status); pkt_stat->rx_snr[RF_PATH_A] = GET_PHY_STAT_P1_RXSNR_A(phy_status); pkt_stat->rx_snr[RF_PATH_B] = GET_PHY_STAT_P1_RXSNR_B(phy_status); pkt_stat->cfo_tail[RF_PATH_A] = GET_PHY_STAT_P1_CFO_TAIL_A(phy_status); pkt_stat->cfo_tail[RF_PATH_B] = GET_PHY_STAT_P1_CFO_TAIL_B(phy_status); for (path = 0; path <= rtwdev->hal.rf_path_num; path++) { rssi = rtw_phy_rf_power_2_rssi(&pkt_stat->rx_power[path], 1); dm_info->rssi[path] = rssi; dm_info->rx_snr[path] = pkt_stat->rx_snr[path] >> 1; dm_info->cfo_tail[path] = (pkt_stat->cfo_tail[path] * 5) >> 1; rx_evm = pkt_stat->rx_evm[path]; if (rx_evm < 0) { if (rx_evm == S8_MIN) evm_dbm = 0; else evm_dbm = ((u8)-rx_evm >> 1); } dm_info->rx_evm_dbm[path] = evm_dbm; } } static void query_phy_status(struct rtw_dev *rtwdev, u8 *phy_status, struct rtw_rx_pkt_stat *pkt_stat) { u8 page; page = *phy_status & 0xf; switch (page) { case 0: query_phy_status_page0(rtwdev, phy_status, pkt_stat); break; case 1: query_phy_status_page1(rtwdev, phy_status, pkt_stat); break; default: rtw_warn(rtwdev, "unused phy status page (%d)\n", page); return; } } static void rtw8822b_query_rx_desc(struct rtw_dev *rtwdev, u8 *rx_desc, struct rtw_rx_pkt_stat *pkt_stat, struct ieee80211_rx_status *rx_status) { struct ieee80211_hdr *hdr; u32 desc_sz = rtwdev->chip->rx_pkt_desc_sz; u8 *phy_status = NULL; memset(pkt_stat, 0, sizeof(*pkt_stat)); pkt_stat->phy_status = GET_RX_DESC_PHYST(rx_desc); pkt_stat->icv_err = GET_RX_DESC_ICV_ERR(rx_desc); pkt_stat->crc_err = GET_RX_DESC_CRC32(rx_desc); pkt_stat->decrypted = !GET_RX_DESC_SWDEC(rx_desc) && GET_RX_DESC_ENC_TYPE(rx_desc) != RX_DESC_ENC_NONE; pkt_stat->is_c2h = GET_RX_DESC_C2H(rx_desc); pkt_stat->pkt_len = GET_RX_DESC_PKT_LEN(rx_desc); pkt_stat->drv_info_sz = GET_RX_DESC_DRV_INFO_SIZE(rx_desc); pkt_stat->shift = GET_RX_DESC_SHIFT(rx_desc); pkt_stat->rate = GET_RX_DESC_RX_RATE(rx_desc); pkt_stat->cam_id = GET_RX_DESC_MACID(rx_desc); pkt_stat->ppdu_cnt = GET_RX_DESC_PPDU_CNT(rx_desc); pkt_stat->tsf_low = GET_RX_DESC_TSFL(rx_desc); /* drv_info_sz is in unit of 8-bytes */ pkt_stat->drv_info_sz *= 8; /* c2h cmd pkt's rx/phy status is not interested */ if (pkt_stat->is_c2h) return; hdr = (struct ieee80211_hdr *)(rx_desc + desc_sz + pkt_stat->shift + pkt_stat->drv_info_sz); if (pkt_stat->phy_status) { phy_status = rx_desc + desc_sz + pkt_stat->shift; query_phy_status(rtwdev, phy_status, pkt_stat); } rtw_rx_fill_rx_status(rtwdev, pkt_stat, hdr, rx_status, phy_status); } static void rtw8822b_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, u8 rs) { struct rtw_hal *hal = &rtwdev->hal; static const u32 offset_txagc[2] = {0x1d00, 0x1d80}; static u32 phy_pwr_idx; u8 rate, rate_idx, pwr_index, shift; int j; for (j = 0; j < rtw_rate_size[rs]; j++) { rate = rtw_rate_section[rs][j]; pwr_index = hal->tx_pwr_tbl[path][rate]; shift = rate & 0x3; phy_pwr_idx |= ((u32)pwr_index << (shift * 8)); if (shift == 0x3) { rate_idx = rate & 0xfc; rtw_write32(rtwdev, offset_txagc[path] + rate_idx, phy_pwr_idx); phy_pwr_idx = 0; } } } static void rtw8822b_set_tx_power_index(struct rtw_dev *rtwdev) { struct rtw_hal *hal = &rtwdev->hal; int rs, path; for (path = 0; path < hal->rf_path_num; path++) { for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++) rtw8822b_set_tx_power_index_by_rate(rtwdev, path, rs); } } static bool rtw8822b_check_rf_path(u8 antenna) { switch (antenna) { case BB_PATH_A: case BB_PATH_B: case BB_PATH_AB: return true; default: return false; } } static int rtw8822b_set_antenna(struct rtw_dev *rtwdev, u32 antenna_tx, u32 antenna_rx) { struct rtw_hal *hal = &rtwdev->hal; rtw_dbg(rtwdev, RTW_DBG_PHY, "config RF path, tx=0x%x rx=0x%x\n", antenna_tx, antenna_rx); if (!rtw8822b_check_rf_path(antenna_tx)) { rtw_warn(rtwdev, "unsupported tx path 0x%x\n", antenna_tx); return -EINVAL; } if (!rtw8822b_check_rf_path(antenna_rx)) { rtw_warn(rtwdev, "unsupported rx path 0x%x\n", antenna_rx); return -EINVAL; } hal->antenna_tx = antenna_tx; hal->antenna_rx = antenna_rx; rtw8822b_config_trx_mode(rtwdev, antenna_tx, antenna_rx, false); return 0; } static void rtw8822b_cfg_ldo25(struct rtw_dev *rtwdev, bool enable) { u8 ldo_pwr; ldo_pwr = rtw_read8(rtwdev, REG_LDO_EFUSE_CTRL + 3); ldo_pwr = enable ? ldo_pwr | BIT_LDO25_EN : ldo_pwr & ~BIT_LDO25_EN; rtw_write8(rtwdev, REG_LDO_EFUSE_CTRL + 3, ldo_pwr); } static void rtw8822b_false_alarm_statistics(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u32 cck_enable; u32 cck_fa_cnt; u32 ofdm_fa_cnt; u32 crc32_cnt; u32 cca32_cnt; cck_enable = rtw_read32(rtwdev, 0x808) & BIT(28); cck_fa_cnt = rtw_read16(rtwdev, 0xa5c); ofdm_fa_cnt = rtw_read16(rtwdev, 0xf48); dm_info->cck_fa_cnt = cck_fa_cnt; dm_info->ofdm_fa_cnt = ofdm_fa_cnt; dm_info->total_fa_cnt = ofdm_fa_cnt; dm_info->total_fa_cnt += cck_enable ? cck_fa_cnt : 0; crc32_cnt = rtw_read32(rtwdev, 0xf04); dm_info->cck_ok_cnt = crc32_cnt & 0xffff; dm_info->cck_err_cnt = (crc32_cnt & 0xffff0000) >> 16; crc32_cnt = rtw_read32(rtwdev, 0xf14); dm_info->ofdm_ok_cnt = crc32_cnt & 0xffff; dm_info->ofdm_err_cnt = (crc32_cnt & 0xffff0000) >> 16; crc32_cnt = rtw_read32(rtwdev, 0xf10); dm_info->ht_ok_cnt = crc32_cnt & 0xffff; dm_info->ht_err_cnt = (crc32_cnt & 0xffff0000) >> 16; crc32_cnt = rtw_read32(rtwdev, 0xf0c); dm_info->vht_ok_cnt = crc32_cnt & 0xffff; dm_info->vht_err_cnt = (crc32_cnt & 0xffff0000) >> 16; cca32_cnt = rtw_read32(rtwdev, 0xf08); dm_info->ofdm_cca_cnt = ((cca32_cnt & 0xffff0000) >> 16); dm_info->total_cca_cnt = dm_info->ofdm_cca_cnt; if (cck_enable) { cca32_cnt = rtw_read32(rtwdev, 0xfcc); dm_info->cck_cca_cnt = cca32_cnt & 0xffff; dm_info->total_cca_cnt += dm_info->cck_cca_cnt; } rtw_write32_set(rtwdev, 0x9a4, BIT(17)); rtw_write32_clr(rtwdev, 0x9a4, BIT(17)); rtw_write32_clr(rtwdev, 0xa2c, BIT(15)); rtw_write32_set(rtwdev, 0xa2c, BIT(15)); rtw_write32_set(rtwdev, 0xb58, BIT(0)); rtw_write32_clr(rtwdev, 0xb58, BIT(0)); } static void rtw8822b_do_iqk(struct rtw_dev *rtwdev) { static int do_iqk_cnt; struct rtw_iqk_para para = {.clear = 0, .segment_iqk = 0}; u32 rf_reg, iqk_fail_mask; int counter; bool reload; rtw_fw_do_iqk(rtwdev, ¶); for (counter = 0; counter < 300; counter++) { rf_reg = rtw_read_rf(rtwdev, RF_PATH_A, RF_DTXLOK, RFREG_MASK); if (rf_reg == 0xabcde) break; msleep(20); } rtw_write_rf(rtwdev, RF_PATH_A, RF_DTXLOK, RFREG_MASK, 0x0); reload = !!rtw_read32_mask(rtwdev, REG_IQKFAILMSK, BIT(16)); iqk_fail_mask = rtw_read32_mask(rtwdev, REG_IQKFAILMSK, GENMASK(7, 0)); rtw_dbg(rtwdev, RTW_DBG_PHY, "iqk counter=%d reload=%d do_iqk_cnt=%d n_iqk_fail(mask)=0x%02x\n", counter, reload, ++do_iqk_cnt, iqk_fail_mask); } static void rtw8822b_phy_calibration(struct rtw_dev *rtwdev) { rtw8822b_do_iqk(rtwdev); } static void rtw8822b_coex_cfg_init(struct rtw_dev *rtwdev) { /* enable TBTT nterrupt */ rtw_write8_set(rtwdev, REG_BCN_CTRL, BIT_EN_BCN_FUNCTION); /* BT report packet sample rate */ /* 0x790[5:0]=0x5 */ rtw_write8_mask(rtwdev, REG_BT_TDMA_TIME, BIT_MASK_SAMPLE_RATE, 0x5); /* enable BT counter statistics */ rtw_write8(rtwdev, REG_BT_STAT_CTRL, 0x1); /* enable PTA (3-wire function form BT side) */ rtw_write32_set(rtwdev, REG_GPIO_MUXCFG, BIT_BT_PTA_EN); rtw_write32_set(rtwdev, REG_GPIO_MUXCFG, BIT_PO_BT_PTA_PINS); /* enable PTA (tx/rx signal form WiFi side) */ rtw_write8_set(rtwdev, REG_QUEUE_CTRL, BIT_PTA_WL_TX_EN); /* wl tx signal to PTA not case EDCCA */ rtw_write8_clr(rtwdev, REG_QUEUE_CTRL, BIT_PTA_EDCCA_EN); /* GNT_BT=1 while select both */ rtw_write16_set(rtwdev, REG_BT_COEX_V2, BIT_GNT_BT_POLARITY); } static void rtw8822b_coex_cfg_ant_switch(struct rtw_dev *rtwdev, u8 ctrl_type, u8 pos_type) { struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_dm *coex_dm = &coex->dm; struct rtw_coex_rfe *coex_rfe = &coex->rfe; bool polarity_inverse; u8 regval = 0; if (((ctrl_type << 8) + pos_type) == coex_dm->cur_switch_status) return; coex_dm->cur_switch_status = (ctrl_type << 8) + pos_type; if (coex_rfe->ant_switch_diversity && ctrl_type == COEX_SWITCH_CTRL_BY_BBSW) ctrl_type = COEX_SWITCH_CTRL_BY_ANTDIV; polarity_inverse = (coex_rfe->ant_switch_polarity == 1); switch (ctrl_type) { default: case COEX_SWITCH_CTRL_BY_BBSW: /* 0x4c[23] = 0 */ rtw_write8_mask(rtwdev, REG_LED_CFG + 2, BIT_DPDT_SEL_EN >> 16, 0x0); /* 0x4c[24] = 1 */ rtw_write8_mask(rtwdev, REG_LED_CFG + 3, BIT_DPDT_WL_SEL >> 24, 0x1); /* BB SW, DPDT use RFE_ctrl8 and RFE_ctrl9 as ctrl pin */ rtw_write8_mask(rtwdev, REG_RFE_CTRL8, BIT_MASK_RFE_SEL89, 0x77); if (pos_type == COEX_SWITCH_TO_WLG_BT) { if (coex_rfe->rfe_module_type != 0x4 && coex_rfe->rfe_module_type != 0x2) regval = 0x3; else regval = (!polarity_inverse ? 0x2 : 0x1); } else if (pos_type == COEX_SWITCH_TO_WLG) { regval = (!polarity_inverse ? 0x2 : 0x1); } else { regval = (!polarity_inverse ? 0x1 : 0x2); } rtw_write8_mask(rtwdev, REG_RFE_INV8, BIT_MASK_RFE_INV89, regval); break; case COEX_SWITCH_CTRL_BY_PTA: /* 0x4c[23] = 0 */ rtw_write8_mask(rtwdev, REG_LED_CFG + 2, BIT_DPDT_SEL_EN >> 16, 0x0); /* 0x4c[24] = 1 */ rtw_write8_mask(rtwdev, REG_LED_CFG + 3, BIT_DPDT_WL_SEL >> 24, 0x1); /* PTA, DPDT use RFE_ctrl8 and RFE_ctrl9 as ctrl pin */ rtw_write8_mask(rtwdev, REG_RFE_CTRL8, BIT_MASK_RFE_SEL89, 0x66); regval = (!polarity_inverse ? 0x2 : 0x1); rtw_write8_mask(rtwdev, REG_RFE_INV8, BIT_MASK_RFE_INV89, regval); break; case COEX_SWITCH_CTRL_BY_ANTDIV: /* 0x4c[23] = 0 */ rtw_write8_mask(rtwdev, REG_LED_CFG + 2, BIT_DPDT_SEL_EN >> 16, 0x0); /* 0x4c[24] = 1 */ rtw_write8_mask(rtwdev, REG_LED_CFG + 3, BIT_DPDT_WL_SEL >> 24, 0x1); rtw_write8_mask(rtwdev, REG_RFE_CTRL8, BIT_MASK_RFE_SEL89, 0x88); break; case COEX_SWITCH_CTRL_BY_MAC: /* 0x4c[23] = 1 */ rtw_write8_mask(rtwdev, REG_LED_CFG + 2, BIT_DPDT_SEL_EN >> 16, 0x1); regval = (!polarity_inverse ? 0x0 : 0x1); rtw_write8_mask(rtwdev, REG_PAD_CTRL1, BIT_SW_DPDT_SEL_DATA, regval); break; case COEX_SWITCH_CTRL_BY_FW: /* 0x4c[23] = 0 */ rtw_write8_mask(rtwdev, REG_LED_CFG + 2, BIT_DPDT_SEL_EN >> 16, 0x0); /* 0x4c[24] = 1 */ rtw_write8_mask(rtwdev, REG_LED_CFG + 3, BIT_DPDT_WL_SEL >> 24, 0x1); break; case COEX_SWITCH_CTRL_BY_BT: /* 0x4c[23] = 0 */ rtw_write8_mask(rtwdev, REG_LED_CFG + 2, BIT_DPDT_SEL_EN >> 16, 0x0); /* 0x4c[24] = 0 */ rtw_write8_mask(rtwdev, REG_LED_CFG + 3, BIT_DPDT_WL_SEL >> 24, 0x0); break; } } static void rtw8822b_coex_cfg_gnt_fix(struct rtw_dev *rtwdev) { } static void rtw8822b_coex_cfg_gnt_debug(struct rtw_dev *rtwdev) { rtw_write8_mask(rtwdev, REG_PAD_CTRL1 + 2, BIT_BTGP_SPI_EN >> 16, 0); rtw_write8_mask(rtwdev, REG_PAD_CTRL1 + 3, BIT_BTGP_JTAG_EN >> 24, 0); rtw_write8_mask(rtwdev, REG_GPIO_MUXCFG + 2, BIT_FSPI_EN >> 16, 0); rtw_write8_mask(rtwdev, REG_PAD_CTRL1 + 1, BIT_LED1DIS >> 8, 0); rtw_write8_mask(rtwdev, REG_SYS_SDIO_CTRL + 3, BIT_DBG_GNT_WL_BT >> 24, 0); } static void rtw8822b_coex_cfg_rfe_type(struct rtw_dev *rtwdev) { struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_rfe *coex_rfe = &coex->rfe; struct rtw_efuse *efuse = &rtwdev->efuse; bool is_ext_fem = false; coex_rfe->rfe_module_type = rtwdev->efuse.rfe_option; coex_rfe->ant_switch_polarity = 0; coex_rfe->ant_switch_diversity = false; if (coex_rfe->rfe_module_type == 0x12 || coex_rfe->rfe_module_type == 0x15 || coex_rfe->rfe_module_type == 0x16) coex_rfe->ant_switch_exist = false; else coex_rfe->ant_switch_exist = true; if (coex_rfe->rfe_module_type == 2 || coex_rfe->rfe_module_type == 4) { rtw_coex_write_scbd(rtwdev, COEX_SCBD_EXTFEM, true); is_ext_fem = true; } else { rtw_coex_write_scbd(rtwdev, COEX_SCBD_EXTFEM, false); } coex_rfe->wlg_at_btg = false; if (efuse->share_ant && coex_rfe->ant_switch_exist && !is_ext_fem) coex_rfe->ant_switch_with_bt = true; else coex_rfe->ant_switch_with_bt = false; /* Ext switch buffer mux */ rtw_write8(rtwdev, REG_RFE_CTRL_E, 0xff); rtw_write8_mask(rtwdev, REG_RFESEL_CTRL + 1, 0x3, 0x0); rtw_write8_mask(rtwdev, REG_RFE_INV16, BIT_RFE_BUF_EN, 0x0); /* Disable LTE Coex Function in WiFi side */ rtw_coex_write_indirect_reg(rtwdev, LTE_COEX_CTRL, BIT_LTE_COEX_EN, 0); /* BTC_CTT_WL_VS_LTE */ rtw_coex_write_indirect_reg(rtwdev, LTE_WL_TRX_CTRL, MASKLWORD, 0xffff); /* BTC_CTT_BT_VS_LTE */ rtw_coex_write_indirect_reg(rtwdev, LTE_BT_TRX_CTRL, MASKLWORD, 0xffff); } static void rtw8822b_coex_cfg_wl_tx_power(struct rtw_dev *rtwdev, u8 wl_pwr) { struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_dm *coex_dm = &coex->dm; static const u16 reg_addr[] = {0xc58, 0xe58}; static const u8 wl_tx_power[] = {0xd8, 0xd4, 0xd0, 0xcc, 0xc8}; u8 i, pwr; if (wl_pwr == coex_dm->cur_wl_pwr_lvl) return; coex_dm->cur_wl_pwr_lvl = wl_pwr; if (coex_dm->cur_wl_pwr_lvl >= ARRAY_SIZE(wl_tx_power)) coex_dm->cur_wl_pwr_lvl = ARRAY_SIZE(wl_tx_power) - 1; pwr = wl_tx_power[coex_dm->cur_wl_pwr_lvl]; for (i = 0; i < ARRAY_SIZE(reg_addr); i++) rtw_write8_mask(rtwdev, reg_addr[i], 0xff, pwr); } static void rtw8822b_coex_cfg_wl_rx_gain(struct rtw_dev *rtwdev, bool low_gain) { struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_dm *coex_dm = &coex->dm; /* WL Rx Low gain on */ static const u32 wl_rx_low_gain_on[] = { 0xff000003, 0xbd120003, 0xbe100003, 0xbf080003, 0xbf060003, 0xbf050003, 0xbc140003, 0xbb160003, 0xba180003, 0xb91a0003, 0xb81c0003, 0xb71e0003, 0xb4200003, 0xb5220003, 0xb4240003, 0xb3260003, 0xb2280003, 0xb12a0003, 0xb02c0003, 0xaf2e0003, 0xae300003, 0xad320003, 0xac340003, 0xab360003, 0x8d380003, 0x8c3a0003, 0x8b3c0003, 0x8a3e0003, 0x6e400003, 0x6d420003, 0x6c440003, 0x6b460003, 0x6a480003, 0x694a0003, 0x684c0003, 0x674e0003, 0x66500003, 0x65520003, 0x64540003, 0x64560003, 0x007e0403 }; /* WL Rx Low gain off */ static const u32 wl_rx_low_gain_off[] = { 0xff000003, 0xf4120003, 0xf5100003, 0xf60e0003, 0xf70c0003, 0xf80a0003, 0xf3140003, 0xf2160003, 0xf1180003, 0xf01a0003, 0xef1c0003, 0xee1e0003, 0xed200003, 0xec220003, 0xeb240003, 0xea260003, 0xe9280003, 0xe82a0003, 0xe72c0003, 0xe62e0003, 0xe5300003, 0xc8320003, 0xc7340003, 0xc6360003, 0xc5380003, 0xc43a0003, 0xc33c0003, 0xc23e0003, 0xc1400003, 0xc0420003, 0xa5440003, 0xa4460003, 0xa3480003, 0xa24a0003, 0xa14c0003, 0x834e0003, 0x82500003, 0x81520003, 0x80540003, 0x65560003, 0x007e0403 }; u8 i; if (low_gain == coex_dm->cur_wl_rx_low_gain_en) return; coex_dm->cur_wl_rx_low_gain_en = low_gain; if (coex_dm->cur_wl_rx_low_gain_en) { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], Hi-Li Table On!\n"); for (i = 0; i < ARRAY_SIZE(wl_rx_low_gain_on); i++) rtw_write32(rtwdev, REG_RX_GAIN_EN, wl_rx_low_gain_on[i]); /* set Rx filter corner RCK offset */ rtw_write_rf(rtwdev, RF_PATH_A, RF_RCKD, 0x2, 0x1); rtw_write_rf(rtwdev, RF_PATH_A, RF_RCK, 0x3f, 0x3f); rtw_write_rf(rtwdev, RF_PATH_B, RF_RCKD, 0x2, 0x1); rtw_write_rf(rtwdev, RF_PATH_B, RF_RCK, 0x3f, 0x3f); } else { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], Hi-Li Table Off!\n"); for (i = 0; i < ARRAY_SIZE(wl_rx_low_gain_off); i++) rtw_write32(rtwdev, 0x81c, wl_rx_low_gain_off[i]); /* set Rx filter corner RCK offset */ rtw_write_rf(rtwdev, RF_PATH_A, RF_RCK, 0x3f, 0x4); rtw_write_rf(rtwdev, RF_PATH_A, RF_RCKD, 0x2, 0x0); rtw_write_rf(rtwdev, RF_PATH_B, RF_RCK, 0x3f, 0x4); rtw_write_rf(rtwdev, RF_PATH_B, RF_RCKD, 0x2, 0x0); } } static void rtw8822b_txagc_swing_offset(struct rtw_dev *rtwdev, u8 path, u8 tx_pwr_idx_offset, s8 *txagc_idx, u8 *swing_idx) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; s8 delta_pwr_idx = dm_info->delta_power_index[path]; u8 swing_upper_bound = dm_info->default_ofdm_index + 10; u8 swing_lower_bound = 0; u8 max_tx_pwr_idx_offset = 0xf; s8 agc_index = 0; u8 swing_index = dm_info->default_ofdm_index; tx_pwr_idx_offset = min_t(u8, tx_pwr_idx_offset, max_tx_pwr_idx_offset); if (delta_pwr_idx >= 0) { if (delta_pwr_idx <= tx_pwr_idx_offset) { agc_index = delta_pwr_idx; swing_index = dm_info->default_ofdm_index; } else if (delta_pwr_idx > tx_pwr_idx_offset) { agc_index = tx_pwr_idx_offset; swing_index = dm_info->default_ofdm_index + delta_pwr_idx - tx_pwr_idx_offset; swing_index = min_t(u8, swing_index, swing_upper_bound); } } else { if (dm_info->default_ofdm_index > abs(delta_pwr_idx)) swing_index = dm_info->default_ofdm_index + delta_pwr_idx; else swing_index = swing_lower_bound; swing_index = max_t(u8, swing_index, swing_lower_bound); agc_index = 0; } if (swing_index >= RTW_TXSCALE_SIZE) { rtw_warn(rtwdev, "swing index overflow\n"); swing_index = RTW_TXSCALE_SIZE - 1; } *txagc_idx = agc_index; *swing_idx = swing_index; } static void rtw8822b_pwrtrack_set_pwr(struct rtw_dev *rtwdev, u8 path, u8 pwr_idx_offset) { s8 txagc_idx; u8 swing_idx; u32 reg1, reg2; if (path == RF_PATH_A) { reg1 = 0xc94; reg2 = 0xc1c; } else if (path == RF_PATH_B) { reg1 = 0xe94; reg2 = 0xe1c; } else { return; } rtw8822b_txagc_swing_offset(rtwdev, path, pwr_idx_offset, &txagc_idx, &swing_idx); rtw_write32_mask(rtwdev, reg1, GENMASK(29, 25), txagc_idx); rtw_write32_mask(rtwdev, reg2, GENMASK(31, 21), rtw8822b_txscale_tbl[swing_idx]); } static void rtw8822b_pwrtrack_set(struct rtw_dev *rtwdev, u8 path) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 pwr_idx_offset, tx_pwr_idx; u8 channel = rtwdev->hal.current_channel; u8 band_width = rtwdev->hal.current_band_width; u8 regd = rtw_regd_get(rtwdev); u8 tx_rate = dm_info->tx_rate; u8 max_pwr_idx = rtwdev->chip->max_power_index; tx_pwr_idx = rtw_phy_get_tx_power_index(rtwdev, path, tx_rate, band_width, channel, regd); tx_pwr_idx = min_t(u8, tx_pwr_idx, max_pwr_idx); pwr_idx_offset = max_pwr_idx - tx_pwr_idx; rtw8822b_pwrtrack_set_pwr(rtwdev, path, pwr_idx_offset); } static void rtw8822b_phy_pwrtrack_path(struct rtw_dev *rtwdev, struct rtw_swing_table *swing_table, u8 path) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 power_idx_cur, power_idx_last; u8 delta; /* 8822B only has one thermal meter at PATH A */ delta = rtw_phy_pwrtrack_get_delta(rtwdev, RF_PATH_A); power_idx_last = dm_info->delta_power_index[path]; power_idx_cur = rtw_phy_pwrtrack_get_pwridx(rtwdev, swing_table, path, RF_PATH_A, delta); /* if delta of power indexes are the same, just skip */ if (power_idx_cur == power_idx_last) return; dm_info->delta_power_index[path] = power_idx_cur; rtw8822b_pwrtrack_set(rtwdev, path); } static void rtw8822b_phy_pwrtrack(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; struct rtw_swing_table swing_table; u8 thermal_value, path; rtw_phy_config_swing_table(rtwdev, &swing_table); if (rtwdev->efuse.thermal_meter[RF_PATH_A] == 0xff) return; thermal_value = rtw_read_rf(rtwdev, RF_PATH_A, RF_T_METER, 0xfc00); rtw_phy_pwrtrack_avg(rtwdev, thermal_value, RF_PATH_A); if (dm_info->pwr_trk_init_trigger) dm_info->pwr_trk_init_trigger = false; else if (!rtw_phy_pwrtrack_thermal_changed(rtwdev, thermal_value, RF_PATH_A)) goto iqk; for (path = 0; path < rtwdev->hal.rf_path_num; path++) rtw8822b_phy_pwrtrack_path(rtwdev, &swing_table, path); iqk: if (rtw_phy_pwrtrack_need_iqk(rtwdev)) rtw8822b_do_iqk(rtwdev); } static void rtw8822b_pwr_track(struct rtw_dev *rtwdev) { struct rtw_efuse *efuse = &rtwdev->efuse; struct rtw_dm_info *dm_info = &rtwdev->dm_info; if (efuse->power_track_type != 0) return; if (!dm_info->pwr_trk_triggered) { rtw_write_rf(rtwdev, RF_PATH_A, RF_T_METER, GENMASK(17, 16), 0x03); dm_info->pwr_trk_triggered = true; return; } rtw8822b_phy_pwrtrack(rtwdev); dm_info->pwr_trk_triggered = false; } static void rtw8822b_bf_config_bfee_su(struct rtw_dev *rtwdev, struct rtw_vif *vif, struct rtw_bfee *bfee, bool enable) { if (enable) rtw_bf_enable_bfee_su(rtwdev, vif, bfee); else rtw_bf_remove_bfee_su(rtwdev, bfee); } static void rtw8822b_bf_config_bfee_mu(struct rtw_dev *rtwdev, struct rtw_vif *vif, struct rtw_bfee *bfee, bool enable) { if (enable) rtw_bf_enable_bfee_mu(rtwdev, vif, bfee); else rtw_bf_remove_bfee_mu(rtwdev, bfee); } static void rtw8822b_bf_config_bfee(struct rtw_dev *rtwdev, struct rtw_vif *vif, struct rtw_bfee *bfee, bool enable) { if (bfee->role == RTW_BFEE_SU) rtw8822b_bf_config_bfee_su(rtwdev, vif, bfee, enable); else if (bfee->role == RTW_BFEE_MU) rtw8822b_bf_config_bfee_mu(rtwdev, vif, bfee, enable); else rtw_warn(rtwdev, "wrong bfee role\n"); } static void rtw8822b_adaptivity_init(struct rtw_dev *rtwdev) { rtw_phy_set_edcca_th(rtwdev, RTW8822B_EDCCA_MAX, RTW8822B_EDCCA_MAX); /* mac edcca state setting */ rtw_write32_clr(rtwdev, REG_TX_PTCL_CTRL, BIT_DIS_EDCCA); rtw_write32_set(rtwdev, REG_RD_CTRL, BIT_EDCCA_MSK_CNTDOWN_EN); rtw_write32_mask(rtwdev, REG_EDCCA_SOURCE, BIT_SOURCE_OPTION, RTW8822B_EDCCA_SRC_DEF); rtw_write32_mask(rtwdev, REG_EDCCA_POW_MA, BIT_MA_LEVEL, 0); /* edcca decision opt */ rtw_write32_set(rtwdev, REG_EDCCA_DECISION, BIT_EDCCA_OPTION); } static void rtw8822b_adaptivity(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; s8 l2h, h2l; u8 igi; igi = dm_info->igi_history[0]; if (dm_info->edcca_mode == RTW_EDCCA_NORMAL) { l2h = max_t(s8, igi + EDCCA_IGI_L2H_DIFF, EDCCA_TH_L2H_LB); h2l = l2h - EDCCA_L2H_H2L_DIFF_NORMAL; } else { l2h = min_t(s8, igi, dm_info->l2h_th_ini); h2l = l2h - EDCCA_L2H_H2L_DIFF; } rtw_phy_set_edcca_th(rtwdev, l2h, h2l); } +static void rtw8822b_fill_txdesc_checksum(struct rtw_dev *rtwdev, + struct rtw_tx_pkt_info *pkt_info, + u8 *txdesc) +{ + size_t words = 32 / 2; /* calculate the first 32 bytes (16 words) */ + + fill_txdesc_checksum_common(txdesc, words); +} + static const struct rtw_pwr_seq_cmd trans_carddis_to_cardemu_8822b[] = { {0x0086, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_SDIO, RTW_PWR_CMD_WRITE, BIT(0), 0}, {0x0086, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_SDIO, RTW_PWR_CMD_POLLING, BIT(1), BIT(1)}, {0x004A, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), 0}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(3) | BIT(4) | BIT(7), 0}, {0x0300, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0}, {0x0301, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0}, {0xFFFF, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, 0, RTW_PWR_CMD_END, 0, 0}, }; static const struct rtw_pwr_seq_cmd trans_cardemu_to_act_8822b[] = { {0x0012, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(1), 0}, {0x0012, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, {0x0020, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, {0x0001, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_DELAY, 1, RTW_PWR_DELAY_MS}, {0x0000, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(5), 0}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, (BIT(4) | BIT(3) | BIT(2)), 0}, {0x0075, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, {0x0006, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_POLLING, BIT(1), BIT(1)}, {0x0075, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), 0}, {0xFF1A, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0}, {0x0006, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(7), 0}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, (BIT(4) | BIT(3)), 0}, {0x10C3, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_POLLING, BIT(0), 0}, {0x0020, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(3), BIT(3)}, {0x10A8, RTW_PWR_CUT_C_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0}, {0x10A9, RTW_PWR_CUT_C_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0xef}, {0x10AA, RTW_PWR_CUT_C_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0x0c}, {0x0068, RTW_PWR_CUT_C_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(4), BIT(4)}, {0x0029, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0xF9}, {0x0024, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(2), 0}, {0x0074, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(5), BIT(5)}, {0x00AF, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(5), BIT(5)}, {0xFFFF, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, 0, RTW_PWR_CMD_END, 0, 0}, }; static const struct rtw_pwr_seq_cmd trans_act_to_cardemu_8822b[] = { {0x0003, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(2), 0}, {0x0093, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(3), 0}, {0x001F, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0}, {0x00EF, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0}, {0xFF1A, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0x30}, {0x0049, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(1), 0}, {0x0006, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, {0x0002, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(1), 0}, {0x10C3, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), 0}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(1), BIT(1)}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_POLLING, BIT(1), 0}, {0x0020, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(3), 0}, {0x0000, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(5), BIT(5)}, {0xFFFF, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, 0, RTW_PWR_CMD_END, 0, 0}, }; static const struct rtw_pwr_seq_cmd trans_cardemu_to_carddis_8822b[] = { {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(7), BIT(7)}, {0x0007, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0x20}, {0x0067, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(5), 0}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(2), BIT(2)}, {0x004A, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), 0}, {0x0067, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(5), 0}, {0x0067, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(4), 0}, {0x004F, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), 0}, {0x0067, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(1), 0}, {0x0046, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(6), BIT(6)}, {0x0067, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(2), 0}, {0x0046, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(7), BIT(7)}, {0x0062, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(4), BIT(4)}, {0x0081, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(7) | BIT(6), 0}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(3) | BIT(4), BIT(3)}, {0x0086, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_SDIO, RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, {0x0086, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_SDIO, RTW_PWR_CMD_POLLING, BIT(1), 0}, {0x0090, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(1), 0}, {0x0044, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_SDIO, RTW_PWR_CMD_WRITE, 0xFF, 0}, {0x0040, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_SDIO, RTW_PWR_CMD_WRITE, 0xFF, 0x90}, {0x0041, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_SDIO, RTW_PWR_CMD_WRITE, 0xFF, 0x00}, {0x0042, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_SDIO, RTW_PWR_CMD_WRITE, 0xFF, 0x04}, {0xFFFF, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, 0, RTW_PWR_CMD_END, 0, 0}, }; static const struct rtw_pwr_seq_cmd *card_enable_flow_8822b[] = { trans_carddis_to_cardemu_8822b, trans_cardemu_to_act_8822b, NULL }; static const struct rtw_pwr_seq_cmd *card_disable_flow_8822b[] = { trans_act_to_cardemu_8822b, trans_cardemu_to_carddis_8822b, NULL }; static const struct rtw_intf_phy_para usb2_param_8822b[] = { {0xFFFF, 0x00, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_ALL, RTW_INTF_PHY_PLATFORM_ALL}, }; static const struct rtw_intf_phy_para usb3_param_8822b[] = { {0x0001, 0xA841, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_D, RTW_INTF_PHY_PLATFORM_ALL}, {0xFFFF, 0x0000, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_ALL, RTW_INTF_PHY_PLATFORM_ALL}, }; static const struct rtw_intf_phy_para pcie_gen1_param_8822b[] = { {0x0001, 0xA841, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_C, RTW_INTF_PHY_PLATFORM_ALL}, {0x0002, 0x60C6, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_C, RTW_INTF_PHY_PLATFORM_ALL}, {0x0008, 0x3596, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_C, RTW_INTF_PHY_PLATFORM_ALL}, {0x0009, 0x321C, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_C, RTW_INTF_PHY_PLATFORM_ALL}, {0x000A, 0x9623, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_C, RTW_INTF_PHY_PLATFORM_ALL}, {0x0020, 0x94FF, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_C, RTW_INTF_PHY_PLATFORM_ALL}, {0x0021, 0xFFCF, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_C, RTW_INTF_PHY_PLATFORM_ALL}, {0x0026, 0xC006, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_C, RTW_INTF_PHY_PLATFORM_ALL}, {0x0029, 0xFF0E, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_C, RTW_INTF_PHY_PLATFORM_ALL}, {0x002A, 0x1840, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_C, RTW_INTF_PHY_PLATFORM_ALL}, {0xFFFF, 0x0000, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_ALL, RTW_INTF_PHY_PLATFORM_ALL}, }; static const struct rtw_intf_phy_para pcie_gen2_param_8822b[] = { {0x0001, 0xA841, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_C, RTW_INTF_PHY_PLATFORM_ALL}, {0x0002, 0x60C6, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_C, RTW_INTF_PHY_PLATFORM_ALL}, {0x0008, 0x3597, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_C, RTW_INTF_PHY_PLATFORM_ALL}, {0x0009, 0x321C, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_C, RTW_INTF_PHY_PLATFORM_ALL}, {0x000A, 0x9623, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_C, RTW_INTF_PHY_PLATFORM_ALL}, {0x0020, 0x94FF, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_C, RTW_INTF_PHY_PLATFORM_ALL}, {0x0021, 0xFFCF, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_C, RTW_INTF_PHY_PLATFORM_ALL}, {0x0026, 0xC006, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_C, RTW_INTF_PHY_PLATFORM_ALL}, {0x0029, 0xFF0E, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_C, RTW_INTF_PHY_PLATFORM_ALL}, {0x002A, 0x3040, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_C, RTW_INTF_PHY_PLATFORM_ALL}, {0xFFFF, 0x0000, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_ALL, RTW_INTF_PHY_PLATFORM_ALL}, }; static const struct rtw_intf_phy_para_table phy_para_table_8822b = { .usb2_para = usb2_param_8822b, .usb3_para = usb3_param_8822b, .gen1_para = pcie_gen1_param_8822b, .gen2_para = pcie_gen2_param_8822b, .n_usb2_para = ARRAY_SIZE(usb2_param_8822b), .n_usb3_para = ARRAY_SIZE(usb2_param_8822b), .n_gen1_para = ARRAY_SIZE(pcie_gen1_param_8822b), .n_gen2_para = ARRAY_SIZE(pcie_gen2_param_8822b), }; static const struct rtw_rfe_def rtw8822b_rfe_defs[] = { [2] = RTW_DEF_RFE(8822b, 2, 2), [3] = RTW_DEF_RFE(8822b, 3, 0), [5] = RTW_DEF_RFE(8822b, 5, 5), }; static const struct rtw_hw_reg rtw8822b_dig[] = { [0] = { .addr = 0xc50, .mask = 0x7f }, [1] = { .addr = 0xe50, .mask = 0x7f }, }; static const struct rtw_ltecoex_addr rtw8822b_ltecoex_addr = { .ctrl = LTECOEX_ACCESS_CTRL, .wdata = LTECOEX_WRITE_DATA, .rdata = LTECOEX_READ_DATA, }; static const struct rtw_page_table page_table_8822b[] = { {64, 64, 64, 64, 1}, {64, 64, 64, 64, 1}, {64, 64, 0, 0, 1}, {64, 64, 64, 0, 1}, {64, 64, 64, 64, 1}, }; static const struct rtw_rqpn rqpn_table_8822b[] = { {RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_EXTRA, RTW_DMA_MAPPING_HIGH}, {RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_EXTRA, RTW_DMA_MAPPING_HIGH}, {RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH}, {RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH}, {RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_EXTRA, RTW_DMA_MAPPING_HIGH}, }; static struct rtw_prioq_addrs prioq_addrs_8822b = { .prio[RTW_DMA_MAPPING_EXTRA] = { .rsvd = REG_FIFOPAGE_INFO_4, .avail = REG_FIFOPAGE_INFO_4 + 2, }, .prio[RTW_DMA_MAPPING_LOW] = { .rsvd = REG_FIFOPAGE_INFO_2, .avail = REG_FIFOPAGE_INFO_2 + 2, }, .prio[RTW_DMA_MAPPING_NORMAL] = { .rsvd = REG_FIFOPAGE_INFO_3, .avail = REG_FIFOPAGE_INFO_3 + 2, }, .prio[RTW_DMA_MAPPING_HIGH] = { .rsvd = REG_FIFOPAGE_INFO_1, .avail = REG_FIFOPAGE_INFO_1 + 2, }, .wsize = true, }; static struct rtw_chip_ops rtw8822b_ops = { .phy_set_param = rtw8822b_phy_set_param, .read_efuse = rtw8822b_read_efuse, .query_rx_desc = rtw8822b_query_rx_desc, .set_channel = rtw8822b_set_channel, .mac_init = rtw8822b_mac_init, .read_rf = rtw_phy_read_rf, .write_rf = rtw_phy_write_rf_reg_sipi, .set_tx_power_index = rtw8822b_set_tx_power_index, .set_antenna = rtw8822b_set_antenna, .cfg_ldo25 = rtw8822b_cfg_ldo25, .false_alarm_statistics = rtw8822b_false_alarm_statistics, .phy_calibration = rtw8822b_phy_calibration, .pwr_track = rtw8822b_pwr_track, .config_bfee = rtw8822b_bf_config_bfee, .set_gid_table = rtw_bf_set_gid_table, .cfg_csi_rate = rtw_bf_cfg_csi_rate, .adaptivity_init = rtw8822b_adaptivity_init, .adaptivity = rtw8822b_adaptivity, + .fill_txdesc_checksum = rtw8822b_fill_txdesc_checksum, .coex_set_init = rtw8822b_coex_cfg_init, .coex_set_ant_switch = rtw8822b_coex_cfg_ant_switch, .coex_set_gnt_fix = rtw8822b_coex_cfg_gnt_fix, .coex_set_gnt_debug = rtw8822b_coex_cfg_gnt_debug, .coex_set_rfe_type = rtw8822b_coex_cfg_rfe_type, .coex_set_wl_tx_power = rtw8822b_coex_cfg_wl_tx_power, .coex_set_wl_rx_gain = rtw8822b_coex_cfg_wl_rx_gain, }; /* Shared-Antenna Coex Table */ static const struct coex_table_para table_sant_8822b[] = { {0xffffffff, 0xffffffff}, /* case-0 */ {0x55555555, 0x55555555}, {0x66555555, 0x66555555}, {0xaaaaaaaa, 0xaaaaaaaa}, {0x5a5a5a5a, 0x5a5a5a5a}, {0xfafafafa, 0xfafafafa}, /* case-5 */ {0x6a5a5555, 0xaaaaaaaa}, {0x6a5a56aa, 0x6a5a56aa}, {0x6a5a5a5a, 0x6a5a5a5a}, {0x66555555, 0x5a5a5a5a}, {0x66555555, 0x6a5a5a5a}, /* case-10 */ {0x66555555, 0xfafafafa}, {0x66555555, 0x5a5a5aaa}, {0x66555555, 0x6aaa5aaa}, {0x66555555, 0xaaaa5aaa}, {0x66555555, 0xaaaaaaaa}, /* case-15 */ {0xffff55ff, 0xfafafafa}, {0xffff55ff, 0x6afa5afa}, {0xaaffffaa, 0xfafafafa}, {0xaa5555aa, 0x5a5a5a5a}, {0xaa5555aa, 0x6a5a5a5a}, /* case-20 */ {0xaa5555aa, 0xaaaaaaaa}, {0xffffffff, 0x5a5a5a5a}, {0xffffffff, 0x5a5a5a5a}, {0xffffffff, 0x55555555}, {0xffffffff, 0x6a5a5aaa}, /* case-25 */ {0x55555555, 0x5a5a5a5a}, {0x55555555, 0xaaaaaaaa}, {0x55555555, 0x6a5a6a5a}, {0x66556655, 0x66556655}, {0x66556aaa, 0x6a5a6aaa}, /* case-30 */ {0xffffffff, 0x5aaa5aaa}, {0x56555555, 0x5a5a5aaa}, }; /* Non-Shared-Antenna Coex Table */ static const struct coex_table_para table_nsant_8822b[] = { {0xffffffff, 0xffffffff}, /* case-100 */ {0x55555555, 0x55555555}, {0x66555555, 0x66555555}, {0xaaaaaaaa, 0xaaaaaaaa}, {0x5a5a5a5a, 0x5a5a5a5a}, {0xfafafafa, 0xfafafafa}, /* case-105 */ {0x5afa5afa, 0x5afa5afa}, {0x55555555, 0xfafafafa}, {0x66555555, 0xfafafafa}, {0x66555555, 0x5a5a5a5a}, {0x66555555, 0x6a5a5a5a}, /* case-110 */ {0x66555555, 0xaaaaaaaa}, {0xffff55ff, 0xfafafafa}, {0xffff55ff, 0x5afa5afa}, {0xffff55ff, 0xaaaaaaaa}, {0xffff55ff, 0xffff55ff}, /* case-115 */ {0xaaffffaa, 0x5afa5afa}, {0xaaffffaa, 0xaaaaaaaa}, {0xffffffff, 0xfafafafa}, {0xffffffff, 0x5afa5afa}, {0xffffffff, 0xaaaaaaaa}, /* case-120 */ {0x55ff55ff, 0x5afa5afa}, {0x55ff55ff, 0xaaaaaaaa}, {0x55ff55ff, 0x55ff55ff} }; /* Shared-Antenna TDMA */ static const struct coex_tdma_para tdma_sant_8822b[] = { { {0x00, 0x00, 0x00, 0x00, 0x00} }, /* case-0 */ { {0x61, 0x45, 0x03, 0x11, 0x11} }, { {0x61, 0x3a, 0x03, 0x11, 0x11} }, { {0x61, 0x30, 0x03, 0x11, 0x11} }, { {0x61, 0x20, 0x03, 0x11, 0x11} }, { {0x61, 0x10, 0x03, 0x11, 0x11} }, /* case-5 */ { {0x61, 0x45, 0x03, 0x11, 0x10} }, { {0x61, 0x3a, 0x03, 0x11, 0x10} }, { {0x61, 0x30, 0x03, 0x11, 0x10} }, { {0x61, 0x20, 0x03, 0x11, 0x10} }, { {0x61, 0x10, 0x03, 0x11, 0x10} }, /* case-10 */ { {0x61, 0x08, 0x03, 0x11, 0x14} }, { {0x61, 0x08, 0x03, 0x10, 0x14} }, { {0x51, 0x08, 0x03, 0x10, 0x54} }, { {0x51, 0x08, 0x03, 0x10, 0x55} }, { {0x51, 0x08, 0x07, 0x10, 0x54} }, /* case-15 */ { {0x51, 0x45, 0x03, 0x10, 0x50} }, { {0x51, 0x3a, 0x03, 0x10, 0x50} }, { {0x51, 0x30, 0x03, 0x10, 0x50} }, { {0x51, 0x20, 0x03, 0x10, 0x50} }, { {0x51, 0x10, 0x03, 0x10, 0x50} }, /* case-20 */ { {0x51, 0x4a, 0x03, 0x10, 0x50} }, { {0x51, 0x0c, 0x03, 0x10, 0x54} }, { {0x55, 0x08, 0x03, 0x10, 0x54} }, { {0x65, 0x10, 0x03, 0x11, 0x10} }, { {0x51, 0x10, 0x03, 0x10, 0x51} }, /* case-25 */ { {0x51, 0x08, 0x03, 0x10, 0x50} }, { {0x61, 0x08, 0x03, 0x11, 0x11} } }; /* Non-Shared-Antenna TDMA */ static const struct coex_tdma_para tdma_nsant_8822b[] = { { {0x00, 0x00, 0x00, 0x00, 0x00} }, /* case-100 */ { {0x61, 0x45, 0x03, 0x11, 0x11} }, /* case-101 */ { {0x61, 0x3a, 0x03, 0x11, 0x11} }, { {0x61, 0x30, 0x03, 0x11, 0x11} }, { {0x61, 0x20, 0x03, 0x11, 0x11} }, { {0x61, 0x10, 0x03, 0x11, 0x11} }, /* case-105 */ { {0x61, 0x45, 0x03, 0x11, 0x10} }, { {0x61, 0x3a, 0x03, 0x11, 0x10} }, { {0x61, 0x30, 0x03, 0x11, 0x10} }, { {0x61, 0x20, 0x03, 0x11, 0x10} }, { {0x61, 0x10, 0x03, 0x11, 0x10} }, /* case-110 */ { {0x61, 0x08, 0x03, 0x11, 0x14} }, { {0x61, 0x08, 0x03, 0x10, 0x14} }, { {0x51, 0x08, 0x03, 0x10, 0x54} }, { {0x51, 0x08, 0x03, 0x10, 0x55} }, { {0x51, 0x08, 0x07, 0x10, 0x54} }, /* case-115 */ { {0x51, 0x45, 0x03, 0x10, 0x50} }, { {0x51, 0x3a, 0x03, 0x10, 0x50} }, { {0x51, 0x30, 0x03, 0x10, 0x50} }, { {0x51, 0x20, 0x03, 0x10, 0x50} }, { {0x51, 0x10, 0x03, 0x10, 0x50} }, /* case-120 */ { {0x51, 0x08, 0x03, 0x10, 0x50} } }; /* rssi in percentage % (dbm = % - 100) */ static const u8 wl_rssi_step_8822b[] = {60, 50, 44, 30}; static const u8 bt_rssi_step_8822b[] = {30, 30, 30, 30}; /* wl_tx_dec_power, bt_tx_dec_power, wl_rx_gain, bt_rx_lna_constrain */ static const struct coex_rf_para rf_para_tx_8822b[] = { {0, 0, false, 7}, /* for normal */ {0, 16, false, 7}, /* for WL-CPT */ {4, 0, true, 1}, {3, 6, true, 1}, {2, 9, true, 1}, {1, 13, true, 1} }; static const struct coex_rf_para rf_para_rx_8822b[] = { {0, 0, false, 7}, /* for normal */ {0, 16, false, 7}, /* for WL-CPT */ {4, 0, true, 1}, {3, 6, true, 1}, {2, 9, true, 1}, {1, 13, true, 1} }; static const struct coex_5g_afh_map afh_5g_8822b[] = { {120, 2, 4}, {124, 8, 8}, {128, 17, 8}, {132, 26, 10}, {136, 34, 8}, {140, 42, 10}, {144, 51, 8}, {149, 62, 8}, {153, 71, 10}, {157, 77, 4}, {118, 2, 4}, {126, 12, 16}, {134, 29, 16}, {142, 46, 16}, {151, 66, 16}, {159, 76, 4}, {122, 10, 20}, {138, 37, 34}, {155, 68, 20} }; #if defined(__linux__) static_assert(ARRAY_SIZE(rf_para_tx_8822b) == ARRAY_SIZE(rf_para_rx_8822b)); #elif defined(__FreeBSD__) rtw88_static_assert(ARRAY_SIZE(rf_para_tx_8822b) == ARRAY_SIZE(rf_para_rx_8822b)); #endif static const u8 rtw8822b_pwrtrk_5gb_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { { 0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 8, 9, 10, 11, 11, 12, 13, 14, 14, 15, 16, 17, 17, 18, 19, 20, 20, 21, 22 }, { 0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 8, 9, 10, 11, 11, 12, 13, 14, 14, 15, 16, 17, 17, 18, 19, 20, 20, 21, 22 }, { 0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 8, 9, 10, 11, 11, 12, 13, 14, 14, 15, 16, 17, 17, 18, 19, 20, 20, 21, 22 }, }; static const u8 rtw8822b_pwrtrk_5gb_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { { 0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10, 11, 12, 13, 14, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 22, 23 }, { 0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10, 11, 12, 13, 14, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 22, 23 }, { 0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10, 11, 12, 13, 14, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 22, 23 }, }; static const u8 rtw8822b_pwrtrk_5ga_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { { 0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 8, 9, 10, 11, 11, 12, 13, 14, 14, 15, 16, 17, 17, 18, 19, 20, 20, 21, 22 }, { 0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 8, 9, 10, 11, 11, 12, 13, 14, 14, 15, 16, 17, 17, 18, 19, 20, 20, 21, 22 }, { 0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 8, 9, 10, 11, 11, 12, 13, 14, 14, 15, 16, 17, 17, 18, 19, 20, 20, 21, 22 }, }; static const u8 rtw8822b_pwrtrk_5ga_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { { 0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10, 11, 12, 13, 14, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 22, 23}, { 0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10, 11, 12, 13, 14, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 22, 23}, { 0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10, 11, 12, 13, 14, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 22, 23}, }; static const u8 rtw8822b_pwrtrk_2gb_n[RTW_PWR_TRK_TBL_SZ] = { 0, 1, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 9, 9, 9, 10, 10, 11, 11, 11, 12 }; static const u8 rtw8822b_pwrtrk_2gb_p[RTW_PWR_TRK_TBL_SZ] = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 13, 13 }; static const u8 rtw8822b_pwrtrk_2ga_n[RTW_PWR_TRK_TBL_SZ] = { 0, 1, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 9, 9, 9, 10, 10, 11, 11, 11, 12 }; static const u8 rtw8822b_pwrtrk_2ga_p[RTW_PWR_TRK_TBL_SZ] = { 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15 }; static const u8 rtw8822b_pwrtrk_2g_cck_b_n[RTW_PWR_TRK_TBL_SZ] = { 0, 1, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 9, 9, 9, 10, 10, 11, 11, 11, 12 }; static const u8 rtw8822b_pwrtrk_2g_cck_b_p[RTW_PWR_TRK_TBL_SZ] = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 13, 13 }; static const u8 rtw8822b_pwrtrk_2g_cck_a_n[RTW_PWR_TRK_TBL_SZ] = { 0, 1, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 9, 9, 9, 10, 10, 11, 11, 11, 12 }; static const u8 rtw8822b_pwrtrk_2g_cck_a_p[RTW_PWR_TRK_TBL_SZ] = { 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15 }; static const struct rtw_pwr_track_tbl rtw8822b_rtw_pwr_track_tbl = { .pwrtrk_5gb_n[RTW_PWR_TRK_5G_1] = rtw8822b_pwrtrk_5gb_n[RTW_PWR_TRK_5G_1], .pwrtrk_5gb_n[RTW_PWR_TRK_5G_2] = rtw8822b_pwrtrk_5gb_n[RTW_PWR_TRK_5G_2], .pwrtrk_5gb_n[RTW_PWR_TRK_5G_3] = rtw8822b_pwrtrk_5gb_n[RTW_PWR_TRK_5G_3], .pwrtrk_5gb_p[RTW_PWR_TRK_5G_1] = rtw8822b_pwrtrk_5gb_p[RTW_PWR_TRK_5G_1], .pwrtrk_5gb_p[RTW_PWR_TRK_5G_2] = rtw8822b_pwrtrk_5gb_p[RTW_PWR_TRK_5G_2], .pwrtrk_5gb_p[RTW_PWR_TRK_5G_3] = rtw8822b_pwrtrk_5gb_p[RTW_PWR_TRK_5G_3], .pwrtrk_5ga_n[RTW_PWR_TRK_5G_1] = rtw8822b_pwrtrk_5ga_n[RTW_PWR_TRK_5G_1], .pwrtrk_5ga_n[RTW_PWR_TRK_5G_2] = rtw8822b_pwrtrk_5ga_n[RTW_PWR_TRK_5G_2], .pwrtrk_5ga_n[RTW_PWR_TRK_5G_3] = rtw8822b_pwrtrk_5ga_n[RTW_PWR_TRK_5G_3], .pwrtrk_5ga_p[RTW_PWR_TRK_5G_1] = rtw8822b_pwrtrk_5ga_p[RTW_PWR_TRK_5G_1], .pwrtrk_5ga_p[RTW_PWR_TRK_5G_2] = rtw8822b_pwrtrk_5ga_p[RTW_PWR_TRK_5G_2], .pwrtrk_5ga_p[RTW_PWR_TRK_5G_3] = rtw8822b_pwrtrk_5ga_p[RTW_PWR_TRK_5G_3], .pwrtrk_2gb_n = rtw8822b_pwrtrk_2gb_n, .pwrtrk_2gb_p = rtw8822b_pwrtrk_2gb_p, .pwrtrk_2ga_n = rtw8822b_pwrtrk_2ga_n, .pwrtrk_2ga_p = rtw8822b_pwrtrk_2ga_p, .pwrtrk_2g_cckb_n = rtw8822b_pwrtrk_2g_cck_b_n, .pwrtrk_2g_cckb_p = rtw8822b_pwrtrk_2g_cck_b_p, .pwrtrk_2g_ccka_n = rtw8822b_pwrtrk_2g_cck_a_n, .pwrtrk_2g_ccka_p = rtw8822b_pwrtrk_2g_cck_a_p, }; static const struct rtw_reg_domain coex_info_hw_regs_8822b[] = { {0xcb0, MASKDWORD, RTW_REG_DOMAIN_MAC32}, {0xcb4, MASKDWORD, RTW_REG_DOMAIN_MAC32}, {0xcba, MASKBYTE0, RTW_REG_DOMAIN_MAC8}, {0xcbd, MASKBYTE0, RTW_REG_DOMAIN_MAC8}, {0xc58, MASKBYTE0, RTW_REG_DOMAIN_MAC8}, {0xcbd, BIT(0), RTW_REG_DOMAIN_MAC8}, {0, 0, RTW_REG_DOMAIN_NL}, {0x430, MASKDWORD, RTW_REG_DOMAIN_MAC32}, {0x434, MASKDWORD, RTW_REG_DOMAIN_MAC32}, {0x42a, MASKLWORD, RTW_REG_DOMAIN_MAC16}, {0x426, MASKBYTE0, RTW_REG_DOMAIN_MAC8}, {0x45e, BIT(3), RTW_REG_DOMAIN_MAC8}, {0x454, MASKLWORD, RTW_REG_DOMAIN_MAC16}, {0, 0, RTW_REG_DOMAIN_NL}, {0x4c, BIT(24) | BIT(23), RTW_REG_DOMAIN_MAC32}, {0x64, BIT(0), RTW_REG_DOMAIN_MAC8}, {0x4c6, BIT(4), RTW_REG_DOMAIN_MAC8}, {0x40, BIT(5), RTW_REG_DOMAIN_MAC8}, {0x1, RFREG_MASK, RTW_REG_DOMAIN_RF_B}, {0, 0, RTW_REG_DOMAIN_NL}, {0x550, MASKDWORD, RTW_REG_DOMAIN_MAC32}, {0x522, MASKBYTE0, RTW_REG_DOMAIN_MAC8}, {0x953, BIT(1), RTW_REG_DOMAIN_MAC8}, {0xc50, MASKBYTE0, RTW_REG_DOMAIN_MAC8}, }; static struct rtw_hw_reg_offset rtw8822b_edcca_th[] = { [EDCCA_TH_L2H_IDX] = {{.addr = 0x8a4, .mask = MASKBYTE0}, .offset = 0}, [EDCCA_TH_H2L_IDX] = {{.addr = 0x8a4, .mask = MASKBYTE1}, .offset = 0}, }; const struct rtw_chip_info rtw8822b_hw_spec = { .ops = &rtw8822b_ops, .id = RTW_CHIP_TYPE_8822B, .fw_name = "rtw88/rtw8822b_fw.bin", .wlan_cpu = RTW_WCPU_11AC, .tx_pkt_desc_sz = 48, .tx_buf_desc_sz = 16, .rx_pkt_desc_sz = 24, .rx_buf_desc_sz = 8, .phy_efuse_size = 1024, .log_efuse_size = 768, .ptct_efuse_size = 96, .txff_size = 262144, .rxff_size = 24576, .fw_rxff_size = 12288, + .rsvd_drv_pg_num = 8, .txgi_factor = 1, .is_pwr_by_rate_dec = true, .max_power_index = 0x3f, .csi_buf_pg_num = 0, .band = RTW_BAND_2G | RTW_BAND_5G, - .page_size = 128, + .page_size = TX_PAGE_SIZE, .dig_min = 0x1c, .ht_supported = true, .vht_supported = true, .lps_deep_mode_supported = BIT(LPS_DEEP_MODE_LCLK), .sys_func_en = 0xDC, .pwr_on_seq = card_enable_flow_8822b, .pwr_off_seq = card_disable_flow_8822b, .page_table = page_table_8822b, .rqpn_table = rqpn_table_8822b, .prioq_addrs = &prioq_addrs_8822b, .intf_table = &phy_para_table_8822b, .dig = rtw8822b_dig, .dig_cck = NULL, .rf_base_addr = {0x2800, 0x2c00}, .rf_sipi_addr = {0xc90, 0xe90}, .ltecoex_addr = &rtw8822b_ltecoex_addr, .mac_tbl = &rtw8822b_mac_tbl, .agc_tbl = &rtw8822b_agc_tbl, .bb_tbl = &rtw8822b_bb_tbl, .rf_tbl = {&rtw8822b_rf_a_tbl, &rtw8822b_rf_b_tbl}, .rfe_defs = rtw8822b_rfe_defs, .rfe_defs_size = ARRAY_SIZE(rtw8822b_rfe_defs), .pwr_track_tbl = &rtw8822b_rtw_pwr_track_tbl, .iqk_threshold = 8, .bfer_su_max_num = 2, .bfer_mu_max_num = 1, .rx_ldpc = true, .edcca_th = rtw8822b_edcca_th, .l2h_th_ini_cs = 10 + EDCCA_IGI_BASE, .l2h_th_ini_ad = -14 + EDCCA_IGI_BASE, .ampdu_density = IEEE80211_HT_MPDU_DENSITY_2, + .max_scan_ie_len = IEEE80211_MAX_DATA_LEN, .coex_para_ver = 0x20070206, .bt_desired_ver = 0x6, .scbd_support = true, .new_scbd10_def = false, .ble_hid_profile_support = false, .wl_mimo_ps_support = false, .pstdma_type = COEX_PSTDMA_FORCE_LPSOFF, .bt_rssi_type = COEX_BTRSSI_RATIO, .ant_isolation = 15, .rssi_tolerance = 2, .wl_rssi_step = wl_rssi_step_8822b, .bt_rssi_step = bt_rssi_step_8822b, .table_sant_num = ARRAY_SIZE(table_sant_8822b), .table_sant = table_sant_8822b, .table_nsant_num = ARRAY_SIZE(table_nsant_8822b), .table_nsant = table_nsant_8822b, .tdma_sant_num = ARRAY_SIZE(tdma_sant_8822b), .tdma_sant = tdma_sant_8822b, .tdma_nsant_num = ARRAY_SIZE(tdma_nsant_8822b), .tdma_nsant = tdma_nsant_8822b, .wl_rf_para_num = ARRAY_SIZE(rf_para_tx_8822b), .wl_rf_para_tx = rf_para_tx_8822b, .wl_rf_para_rx = rf_para_rx_8822b, .bt_afh_span_bw20 = 0x24, .bt_afh_span_bw40 = 0x36, .afh_5g_num = ARRAY_SIZE(afh_5g_8822b), .afh_5g = afh_5g_8822b, .coex_info_hw_regs_num = ARRAY_SIZE(coex_info_hw_regs_8822b), .coex_info_hw_regs = coex_info_hw_regs_8822b, .fw_fifo_addr = {0x780, 0x700, 0x780, 0x660, 0x650, 0x680}, }; EXPORT_SYMBOL(rtw8822b_hw_spec); MODULE_FIRMWARE("rtw88/rtw8822b_fw.bin"); MODULE_AUTHOR("Realtek Corporation"); MODULE_DESCRIPTION("Realtek 802.11ac wireless 8822b driver"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sys/contrib/dev/rtw88/rtw8822b.h b/sys/contrib/dev/rtw88/rtw8822b.h index 01d3644e0c94..2dc3a6660f06 100644 --- a/sys/contrib/dev/rtw88/rtw8822b.h +++ b/sys/contrib/dev/rtw88/rtw8822b.h @@ -1,192 +1,198 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* Copyright(c) 2018-2019 Realtek Corporation */ #ifndef __RTW8822B_H__ #define __RTW8822B_H__ #include #define RCR_VHT_ACK BIT(26) struct rtw8822bu_efuse { u8 res4[4]; /* 0xd0 */ u8 usb_optional_function; u8 res5[0x1e]; u8 res6[2]; u8 serial[0x0b]; /* 0xf5 */ u8 vid; /* 0x100 */ u8 res7; u8 pid; u8 res8[4]; u8 mac_addr[ETH_ALEN]; /* 0x107 */ u8 res9[2]; u8 vendor_name[0x07]; u8 res10[2]; u8 device_name[0x14]; u8 res11[0xcf]; u8 package_type; /* 0x1fb */ u8 res12[0x4]; }; struct rtw8822be_efuse { u8 mac_addr[ETH_ALEN]; /* 0xd0 */ u8 vender_id[2]; u8 device_id[2]; u8 sub_vender_id[2]; u8 sub_device_id[2]; u8 pmc[2]; u8 exp_device_cap[2]; u8 msi_cap; u8 ltr_cap; /* 0xe3 */ u8 exp_link_control[2]; u8 link_cap[4]; u8 link_control[2]; u8 serial_number[8]; u8 res0:2; /* 0xf4 */ u8 ltr_en:1; u8 res1:2; u8 obff:2; u8 res2:3; u8 obff_cap:2; u8 res3:4; u8 res4[3]; u8 class_code[3]; u8 pci_pm_L1_2_supp:1; u8 pci_pm_L1_1_supp:1; u8 aspm_pm_L1_2_supp:1; u8 aspm_pm_L1_1_supp:1; u8 L1_pm_substates_supp:1; u8 res5:3; u8 port_common_mode_restore_time; u8 port_t_power_on_scale:2; u8 res6:1; u8 port_t_power_on_value:5; u8 res7; }; +struct rtw8822bs_efuse { + u8 res4[0x4a]; /* 0xd0 */ + u8 mac_addr[ETH_ALEN]; /* 0x11a */ +} __packed; + struct rtw8822b_efuse { __le16 rtl_id; u8 res0[0x0e]; /* power index for four RF paths */ struct rtw_txpwr_idx txpwr_idx_table[4]; u8 channel_plan; /* 0xb8 */ u8 xtal_k; u8 thermal_meter; u8 iqk_lck; u8 pa_type; /* 0xbc */ u8 lna_type_2g[2]; /* 0xbd */ u8 lna_type_5g[2]; u8 rf_board_option; u8 rf_feature_option; u8 rf_bt_setting; u8 eeprom_version; u8 eeprom_customer_id; u8 tx_bb_swing_setting_2g; u8 tx_bb_swing_setting_5g; u8 tx_pwr_calibrate_rate; u8 rf_antenna_option; /* 0xc9 */ u8 rfe_option; u8 country_code[2]; u8 res[3]; union { - struct rtw8822bu_efuse u; struct rtw8822be_efuse e; + struct rtw8822bu_efuse u; + struct rtw8822bs_efuse s; }; }; static inline void _rtw_write32s_mask(struct rtw_dev *rtwdev, u32 addr, u32 mask, u32 data) { /* 0xC00-0xCFF and 0xE00-0xEFF have the same layout */ rtw_write32_mask(rtwdev, addr, mask, data); rtw_write32_mask(rtwdev, addr + 0x200, mask, data); } #define rtw_write32s_mask(rtwdev, addr, mask, data) \ do { \ BUILD_BUG_ON((addr) < 0xC00 || (addr) >= 0xD00); \ \ _rtw_write32s_mask(rtwdev, addr, mask, data); \ } while (0) /* phy status page0 */ #define GET_PHY_STAT_P0_PWDB(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x00), GENMASK(15, 8)) /* phy status page1 */ #define GET_PHY_STAT_P1_PWDB_A(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x00), GENMASK(15, 8)) #define GET_PHY_STAT_P1_PWDB_B(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x00), GENMASK(23, 16)) #define GET_PHY_STAT_P1_RF_MODE(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x03), GENMASK(29, 28)) #define GET_PHY_STAT_P1_L_RXSC(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x01), GENMASK(11, 8)) #define GET_PHY_STAT_P1_HT_RXSC(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x01), GENMASK(15, 12)) #define GET_PHY_STAT_P1_RXEVM_A(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x04), GENMASK(7, 0)) #define GET_PHY_STAT_P1_RXEVM_B(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x04), GENMASK(15, 8)) #define GET_PHY_STAT_P1_CFO_TAIL_A(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x05), GENMASK(7, 0)) #define GET_PHY_STAT_P1_CFO_TAIL_B(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x05), GENMASK(15, 8)) #define GET_PHY_STAT_P1_RXSNR_A(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x06), GENMASK(7, 0)) #define GET_PHY_STAT_P1_RXSNR_B(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x06), GENMASK(15, 8)) #define RTW8822B_EDCCA_MAX 0x7f #define RTW8822B_EDCCA_SRC_DEF 1 #define REG_HTSTFWT 0x800 #define REG_RXPSEL 0x808 #define BIT_RX_PSEL_RST (BIT(28) | BIT(29)) #define REG_TXPSEL 0x80c #define REG_RXCCAMSK 0x814 #define REG_CCASEL 0x82c #define REG_PDMFTH 0x830 #define REG_CCA2ND 0x838 #define REG_L1WT 0x83c #define REG_L1PKWT 0x840 #define REG_MRC 0x850 #define REG_CLKTRK 0x860 #define REG_EDCCA_POW_MA 0x8a0 #define BIT_MA_LEVEL GENMASK(1, 0) #define REG_ADCCLK 0x8ac #define REG_ADC160 0x8c4 #define REG_ADC40 0x8c8 #define REG_EDCCA_DECISION 0x8dc #define BIT_EDCCA_OPTION BIT(5) #define REG_CDDTXP 0x93c #define REG_TXPSEL1 0x940 #define REG_EDCCA_SOURCE 0x944 #define BIT_SOURCE_OPTION GENMASK(29, 28) #define REG_ACBB0 0x948 #define REG_ACBBRXFIR 0x94c #define REG_ACGG2TBL 0x958 #define REG_RXSB 0xa00 #define REG_ADCINI 0xa04 #define REG_TXSF2 0xa24 #define REG_TXSF6 0xa28 #define REG_RXDESC 0xa2c #define REG_ENTXCCK 0xa80 #define REG_AGCTR_A 0xc08 #define REG_TXDFIR 0xc20 #define REG_RXIGI_A 0xc50 #define REG_TRSW 0xca0 #define REG_RFESEL0 0xcb0 #define REG_RFESEL8 0xcb4 #define REG_RFECTL 0xcb8 #define REG_RFEINV 0xcbc #define REG_AGCTR_B 0xe08 #define REG_RXIGI_B 0xe50 #define REG_ANTWT 0x1904 #define REG_IQKFAILMSK 0x1bf0 extern const struct rtw_chip_info rtw8822b_hw_spec; #endif diff --git a/sys/contrib/dev/rtw88/rtw8822bs.c b/sys/contrib/dev/rtw88/rtw8822bs.c new file mode 100644 index 000000000000..31d8645f83bd --- /dev/null +++ b/sys/contrib/dev/rtw88/rtw8822bs.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) Jernej Skrabec + */ + +#include +#include +#include +#include "main.h" +#include "rtw8822b.h" +#include "sdio.h" + +static const struct sdio_device_id rtw_8822bs_id_table[] = { + { + SDIO_DEVICE(SDIO_VENDOR_ID_REALTEK, + SDIO_DEVICE_ID_REALTEK_RTW8822BS), + .driver_data = (kernel_ulong_t)&rtw8822b_hw_spec, + }, + {} +}; +MODULE_DEVICE_TABLE(sdio, rtw_8822bs_id_table); + +static struct sdio_driver rtw_8822bs_driver = { + .name = "rtw_8822bs", + .probe = rtw_sdio_probe, + .remove = rtw_sdio_remove, + .id_table = rtw_8822bs_id_table, + .drv = { + .pm = &rtw_sdio_pm_ops, + .shutdown = rtw_sdio_shutdown, + } +}; +module_sdio_driver(rtw_8822bs_driver); + +MODULE_AUTHOR("Jernej Skrabec "); +MODULE_DESCRIPTION("Realtek 802.11ac wireless 8822bs driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sys/contrib/dev/rtw88/rtw8822bu.c b/sys/contrib/dev/rtw88/rtw8822bu.c new file mode 100644 index 000000000000..37c6d939e703 --- /dev/null +++ b/sys/contrib/dev/rtw88/rtw8822bu.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2018-2019 Realtek Corporation + */ + +#include +#include +#include "main.h" +#include "rtw8822b.h" +#include "usb.h" + +static const struct usb_device_id rtw_8822bu_id_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xb812, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xb82c, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0x2102, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* CCNC */ + { USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0xb822, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Edimax EW-7822ULC */ + { USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0xc822, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Edimax EW-7822UTC */ + { USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0xd822, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Edimax */ + { USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0xe822, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Edimax */ + { USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0xf822, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Edimax EW-7822UAD */ + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xb81a, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Default ID */ + { USB_DEVICE_AND_INTERFACE_INFO(0x0b05, 0x1841, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* ASUS AC1300 USB-AC55 B1 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x0b05, 0x184c, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* ASUS U2 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x0B05, 0x19aa, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* ASUS - USB-AC58 rev A1 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x0B05, 0x1870, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* ASUS */ + { USB_DEVICE_AND_INTERFACE_INFO(0x0B05, 0x1874, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* ASUS */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x331e, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Dlink - DWA-181 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x331c, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Dlink - DWA-182 - D1 */ + {USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x331f, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec)}, /* Dlink - DWA-183 D Ver */ + { USB_DEVICE_AND_INTERFACE_INFO(0x13b1, 0x0043, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Linksys WUSB6400M */ + { USB_DEVICE_AND_INTERFACE_INFO(0x13b1, 0x0045, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Linksys WUSB3600 v2 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2357, 0x012d, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* TP-Link Archer T3U v1 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2357, 0x0138, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* TP-Link Archer T3U Plus v1 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2357, 0x0115, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* TP-Link Archer T4U V3 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2357, 0x012e, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* TP-LINK */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2357, 0x0116, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* TP-LINK */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2357, 0x0117, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* TP-LINK */ + { USB_DEVICE_AND_INTERFACE_INFO(0x0846, 0x9055, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Netgear A6150 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x0e66, 0x0025, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Hawking HW12ACU */ + { USB_DEVICE_AND_INTERFACE_INFO(0x04ca, 0x8602, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* LiteOn */ + { USB_DEVICE_AND_INTERFACE_INFO(0x20f4, 0x808a, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* TRENDnet TEW-808UBM */ + {}, +}; +MODULE_DEVICE_TABLE(usb, rtw_8822bu_id_table); + +static int rtw8822bu_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return rtw_usb_probe(intf, id); +} + +static struct usb_driver rtw_8822bu_driver = { + .name = "rtw_8822bu", + .id_table = rtw_8822bu_id_table, + .probe = rtw8822bu_probe, + .disconnect = rtw_usb_disconnect, +#if defined(__FreeBSD__) && defined(__notyet__) + .bsddriver.name = KBUILD_MODNAME, +#endif +}; +module_usb_driver(rtw_8822bu_driver); + +MODULE_AUTHOR("Realtek Corporation"); +MODULE_DESCRIPTION("Realtek 802.11ac wireless 8822bu driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sys/contrib/dev/rtw88/rtw8822c.c b/sys/contrib/dev/rtw88/rtw8822c.c index 47e84b5c2fc5..a2fda1b36e6c 100644 --- a/sys/contrib/dev/rtw88/rtw8822c.c +++ b/sys/contrib/dev/rtw88/rtw8822c.c @@ -1,5423 +1,5470 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* Copyright(c) 2018-2019 Realtek Corporation */ #include #include "main.h" #include "coex.h" #include "fw.h" #include "tx.h" #include "rx.h" #include "phy.h" #include "rtw8822c.h" #include "rtw8822c_table.h" #include "mac.h" #include "reg.h" #include "debug.h" #include "util.h" #include "bf.h" #include "efuse.h" #define IQK_DONE_8822C 0xaa static void rtw8822c_config_trx_mode(struct rtw_dev *rtwdev, u8 tx_path, u8 rx_path, bool is_tx2_path); static void rtw8822ce_efuse_parsing(struct rtw_efuse *efuse, struct rtw8822c_efuse *map) { ether_addr_copy(efuse->addr, map->e.mac_addr); } +static void rtw8822cu_efuse_parsing(struct rtw_efuse *efuse, + struct rtw8822c_efuse *map) +{ + ether_addr_copy(efuse->addr, map->u.mac_addr); +} + +static void rtw8822cs_efuse_parsing(struct rtw_efuse *efuse, + struct rtw8822c_efuse *map) +{ + ether_addr_copy(efuse->addr, map->s.mac_addr); +} + static int rtw8822c_read_efuse(struct rtw_dev *rtwdev, u8 *log_map) { struct rtw_efuse *efuse = &rtwdev->efuse; struct rtw8822c_efuse *map; int i; map = (struct rtw8822c_efuse *)log_map; efuse->rfe_option = map->rfe_option; efuse->rf_board_option = map->rf_board_option; efuse->crystal_cap = map->xtal_k & XCAP_MASK; efuse->channel_plan = map->channel_plan; efuse->country_code[0] = map->country_code[0]; efuse->country_code[1] = map->country_code[1]; efuse->bt_setting = map->rf_bt_setting; efuse->regd = map->rf_board_option & 0x7; efuse->thermal_meter[RF_PATH_A] = map->path_a_thermal; efuse->thermal_meter[RF_PATH_B] = map->path_b_thermal; efuse->thermal_meter_k = (map->path_a_thermal + map->path_b_thermal) >> 1; efuse->power_track_type = (map->tx_pwr_calibrate_rate >> 4) & 0xf; for (i = 0; i < 4; i++) efuse->txpwr_idx_table[i] = map->txpwr_idx_table[i]; switch (rtw_hci_type(rtwdev)) { case RTW_HCI_TYPE_PCIE: rtw8822ce_efuse_parsing(efuse, map); break; + case RTW_HCI_TYPE_USB: + rtw8822cu_efuse_parsing(efuse, map); + break; + case RTW_HCI_TYPE_SDIO: + rtw8822cs_efuse_parsing(efuse, map); + break; default: /* unsupported now */ return -ENOTSUPP; } return 0; } static void rtw8822c_header_file_init(struct rtw_dev *rtwdev, bool pre) { rtw_write32_set(rtwdev, REG_3WIRE, BIT_3WIRE_TX_EN | BIT_3WIRE_RX_EN); rtw_write32_set(rtwdev, REG_3WIRE, BIT_3WIRE_PI_ON); rtw_write32_set(rtwdev, REG_3WIRE2, BIT_3WIRE_TX_EN | BIT_3WIRE_RX_EN); rtw_write32_set(rtwdev, REG_3WIRE2, BIT_3WIRE_PI_ON); if (pre) rtw_write32_clr(rtwdev, REG_ENCCK, BIT_CCK_OFDM_BLK_EN); else rtw_write32_set(rtwdev, REG_ENCCK, BIT_CCK_OFDM_BLK_EN); } static void rtw8822c_bb_reset(struct rtw_dev *rtwdev) { rtw_write16_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_BB_RSTB); rtw_write16_clr(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_BB_RSTB); rtw_write16_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_BB_RSTB); } static void rtw8822c_dac_backup_reg(struct rtw_dev *rtwdev, struct rtw_backup_info *backup, struct rtw_backup_info *backup_rf) { u32 path, i; u32 val; u32 reg; u32 rf_addr[DACK_RF_8822C] = {0x8f}; u32 addrs[DACK_REG_8822C] = {0x180c, 0x1810, 0x410c, 0x4110, 0x1c3c, 0x1c24, 0x1d70, 0x9b4, 0x1a00, 0x1a14, 0x1d58, 0x1c38, 0x1e24, 0x1e28, 0x1860, 0x4160}; for (i = 0; i < DACK_REG_8822C; i++) { backup[i].len = 4; backup[i].reg = addrs[i]; backup[i].val = rtw_read32(rtwdev, addrs[i]); } for (path = 0; path < DACK_PATH_8822C; path++) { for (i = 0; i < DACK_RF_8822C; i++) { reg = rf_addr[i]; val = rtw_read_rf(rtwdev, path, reg, RFREG_MASK); backup_rf[path * i + i].reg = reg; backup_rf[path * i + i].val = val; } } } static void rtw8822c_dac_restore_reg(struct rtw_dev *rtwdev, struct rtw_backup_info *backup, struct rtw_backup_info *backup_rf) { u32 path, i; u32 val; u32 reg; rtw_restore_reg(rtwdev, backup, DACK_REG_8822C); for (path = 0; path < DACK_PATH_8822C; path++) { for (i = 0; i < DACK_RF_8822C; i++) { val = backup_rf[path * i + i].val; reg = backup_rf[path * i + i].reg; rtw_write_rf(rtwdev, path, reg, RFREG_MASK, val); } } } static void rtw8822c_rf_minmax_cmp(struct rtw_dev *rtwdev, u32 value, u32 *min, u32 *max) { if (value >= 0x200) { if (*min >= 0x200) { if (*min > value) *min = value; } else { *min = value; } if (*max >= 0x200) { if (*max < value) *max = value; } } else { if (*min < 0x200) { if (*min > value) *min = value; } if (*max >= 0x200) { *max = value; } else { if (*max < value) *max = value; } } } static void __rtw8822c_dac_iq_sort(struct rtw_dev *rtwdev, u32 *v1, u32 *v2) { if (*v1 >= 0x200 && *v2 >= 0x200) { if (*v1 > *v2) swap(*v1, *v2); } else if (*v1 < 0x200 && *v2 < 0x200) { if (*v1 > *v2) swap(*v1, *v2); } else if (*v1 < 0x200 && *v2 >= 0x200) { swap(*v1, *v2); } } static void rtw8822c_dac_iq_sort(struct rtw_dev *rtwdev, u32 *iv, u32 *qv) { u32 i, j; for (i = 0; i < DACK_SN_8822C - 1; i++) { for (j = 0; j < (DACK_SN_8822C - 1 - i) ; j++) { __rtw8822c_dac_iq_sort(rtwdev, &iv[j], &iv[j + 1]); __rtw8822c_dac_iq_sort(rtwdev, &qv[j], &qv[j + 1]); } } } static void rtw8822c_dac_iq_offset(struct rtw_dev *rtwdev, u32 *vec, u32 *val) { u32 p, m, t, i; m = 0; p = 0; for (i = 10; i < DACK_SN_8822C - 10; i++) { if (vec[i] > 0x200) m = (0x400 - vec[i]) + m; else p = vec[i] + p; } if (p > m) { t = p - m; t = t / (DACK_SN_8822C - 20); } else { t = m - p; t = t / (DACK_SN_8822C - 20); if (t != 0x0) t = 0x400 - t; } *val = t; } static u32 rtw8822c_get_path_write_addr(u8 path) { u32 base_addr; switch (path) { case RF_PATH_A: base_addr = 0x1800; break; case RF_PATH_B: base_addr = 0x4100; break; default: WARN_ON(1); return -1; } return base_addr; } static u32 rtw8822c_get_path_read_addr(u8 path) { u32 base_addr; switch (path) { case RF_PATH_A: base_addr = 0x2800; break; case RF_PATH_B: base_addr = 0x4500; break; default: WARN_ON(1); return -1; } return base_addr; } static bool rtw8822c_dac_iq_check(struct rtw_dev *rtwdev, u32 value) { bool ret = true; if ((value >= 0x200 && (0x400 - value) > 0x64) || (value < 0x200 && value > 0x64)) { ret = false; rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] Error overflow\n"); } return ret; } static void rtw8822c_dac_cal_iq_sample(struct rtw_dev *rtwdev, u32 *iv, u32 *qv) { u32 temp; int i = 0, cnt = 0; while (i < DACK_SN_8822C && cnt < 10000) { cnt++; temp = rtw_read32_mask(rtwdev, 0x2dbc, 0x3fffff); iv[i] = (temp & 0x3ff000) >> 12; qv[i] = temp & 0x3ff; if (rtw8822c_dac_iq_check(rtwdev, iv[i]) && rtw8822c_dac_iq_check(rtwdev, qv[i])) i++; } } static void rtw8822c_dac_cal_iq_search(struct rtw_dev *rtwdev, u32 *iv, u32 *qv, u32 *i_value, u32 *q_value) { u32 i_max = 0, q_max = 0, i_min = 0, q_min = 0; u32 i_delta, q_delta; u32 temp; int i, cnt = 0; do { i_min = iv[0]; i_max = iv[0]; q_min = qv[0]; q_max = qv[0]; for (i = 0; i < DACK_SN_8822C; i++) { rtw8822c_rf_minmax_cmp(rtwdev, iv[i], &i_min, &i_max); rtw8822c_rf_minmax_cmp(rtwdev, qv[i], &q_min, &q_max); } if (i_max < 0x200 && i_min < 0x200) i_delta = i_max - i_min; else if (i_max >= 0x200 && i_min >= 0x200) i_delta = i_max - i_min; else i_delta = i_max + (0x400 - i_min); if (q_max < 0x200 && q_min < 0x200) q_delta = q_max - q_min; else if (q_max >= 0x200 && q_min >= 0x200) q_delta = q_max - q_min; else q_delta = q_max + (0x400 - q_min); rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] i: min=0x%08x, max=0x%08x, delta=0x%08x\n", i_min, i_max, i_delta); rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] q: min=0x%08x, max=0x%08x, delta=0x%08x\n", q_min, q_max, q_delta); rtw8822c_dac_iq_sort(rtwdev, iv, qv); if (i_delta > 5 || q_delta > 5) { temp = rtw_read32_mask(rtwdev, 0x2dbc, 0x3fffff); iv[0] = (temp & 0x3ff000) >> 12; qv[0] = temp & 0x3ff; temp = rtw_read32_mask(rtwdev, 0x2dbc, 0x3fffff); iv[DACK_SN_8822C - 1] = (temp & 0x3ff000) >> 12; qv[DACK_SN_8822C - 1] = temp & 0x3ff; } else { break; } } while (cnt++ < 100); rtw8822c_dac_iq_offset(rtwdev, iv, i_value); rtw8822c_dac_iq_offset(rtwdev, qv, q_value); } static void rtw8822c_dac_cal_rf_mode(struct rtw_dev *rtwdev, u32 *i_value, u32 *q_value) { u32 iv[DACK_SN_8822C], qv[DACK_SN_8822C]; u32 rf_a, rf_b; rf_a = rtw_read_rf(rtwdev, RF_PATH_A, 0x0, RFREG_MASK); rf_b = rtw_read_rf(rtwdev, RF_PATH_B, 0x0, RFREG_MASK); rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] RF path-A=0x%05x\n", rf_a); rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] RF path-B=0x%05x\n", rf_b); rtw8822c_dac_cal_iq_sample(rtwdev, iv, qv); rtw8822c_dac_cal_iq_search(rtwdev, iv, qv, i_value, q_value); } static void rtw8822c_dac_bb_setting(struct rtw_dev *rtwdev) { rtw_write32_mask(rtwdev, 0x1d58, 0xff8, 0x1ff); rtw_write32_mask(rtwdev, 0x1a00, 0x3, 0x2); rtw_write32_mask(rtwdev, 0x1a14, 0x300, 0x3); rtw_write32(rtwdev, 0x1d70, 0x7e7e7e7e); rtw_write32_mask(rtwdev, 0x180c, 0x3, 0x0); rtw_write32_mask(rtwdev, 0x410c, 0x3, 0x0); rtw_write32(rtwdev, 0x1b00, 0x00000008); rtw_write8(rtwdev, 0x1bcc, 0x3f); rtw_write32(rtwdev, 0x1b00, 0x0000000a); rtw_write8(rtwdev, 0x1bcc, 0x3f); rtw_write32_mask(rtwdev, 0x1e24, BIT(31), 0x0); rtw_write32_mask(rtwdev, 0x1e28, 0xf, 0x3); } static void rtw8822c_dac_cal_adc(struct rtw_dev *rtwdev, u8 path, u32 *adc_ic, u32 *adc_qc) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u32 ic = 0, qc = 0, temp = 0; u32 base_addr; u32 path_sel; int i; rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] ADCK path(%d)\n", path); base_addr = rtw8822c_get_path_write_addr(path); switch (path) { case RF_PATH_A: path_sel = 0xa0000; break; case RF_PATH_B: path_sel = 0x80000; break; default: WARN_ON(1); return; } /* ADCK step1 */ rtw_write32_mask(rtwdev, base_addr + 0x30, BIT(30), 0x0); if (path == RF_PATH_B) rtw_write32(rtwdev, base_addr + 0x30, 0x30db8041); rtw_write32(rtwdev, base_addr + 0x60, 0xf0040ff0); rtw_write32(rtwdev, base_addr + 0x0c, 0xdff00220); rtw_write32(rtwdev, base_addr + 0x10, 0x02dd08c4); rtw_write32(rtwdev, base_addr + 0x0c, 0x10000260); rtw_write_rf(rtwdev, RF_PATH_A, 0x0, RFREG_MASK, 0x10000); rtw_write_rf(rtwdev, RF_PATH_B, 0x0, RFREG_MASK, 0x10000); for (i = 0; i < 10; i++) { rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] ADCK count=%d\n", i); rtw_write32(rtwdev, 0x1c3c, path_sel + 0x8003); rtw_write32(rtwdev, 0x1c24, 0x00010002); rtw8822c_dac_cal_rf_mode(rtwdev, &ic, &qc); rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] before: i=0x%x, q=0x%x\n", ic, qc); /* compensation value */ if (ic != 0x0) { ic = 0x400 - ic; *adc_ic = ic; } if (qc != 0x0) { qc = 0x400 - qc; *adc_qc = qc; } temp = (ic & 0x3ff) | ((qc & 0x3ff) << 10); rtw_write32(rtwdev, base_addr + 0x68, temp); dm_info->dack_adck[path] = temp; rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] ADCK 0x%08x=0x08%x\n", base_addr + 0x68, temp); /* check ADC DC offset */ rtw_write32(rtwdev, 0x1c3c, path_sel + 0x8103); rtw8822c_dac_cal_rf_mode(rtwdev, &ic, &qc); rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] after: i=0x%08x, q=0x%08x\n", ic, qc); if (ic >= 0x200) ic = 0x400 - ic; if (qc >= 0x200) qc = 0x400 - qc; if (ic < 5 && qc < 5) break; } /* ADCK step2 */ rtw_write32(rtwdev, 0x1c3c, 0x00000003); rtw_write32(rtwdev, base_addr + 0x0c, 0x10000260); rtw_write32(rtwdev, base_addr + 0x10, 0x02d508c4); /* release pull low switch on IQ path */ rtw_write_rf(rtwdev, path, 0x8f, BIT(13), 0x1); } static void rtw8822c_dac_cal_step1(struct rtw_dev *rtwdev, u8 path) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u32 base_addr; u32 read_addr; base_addr = rtw8822c_get_path_write_addr(path); read_addr = rtw8822c_get_path_read_addr(path); rtw_write32(rtwdev, base_addr + 0x68, dm_info->dack_adck[path]); rtw_write32(rtwdev, base_addr + 0x0c, 0xdff00220); if (path == RF_PATH_A) { rtw_write32(rtwdev, base_addr + 0x60, 0xf0040ff0); rtw_write32(rtwdev, 0x1c38, 0xffffffff); } rtw_write32(rtwdev, base_addr + 0x10, 0x02d508c5); rtw_write32(rtwdev, 0x9b4, 0xdb66db00); rtw_write32(rtwdev, base_addr + 0xb0, 0x0a11fb88); rtw_write32(rtwdev, base_addr + 0xbc, 0x0008ff81); rtw_write32(rtwdev, base_addr + 0xc0, 0x0003d208); rtw_write32(rtwdev, base_addr + 0xcc, 0x0a11fb88); rtw_write32(rtwdev, base_addr + 0xd8, 0x0008ff81); rtw_write32(rtwdev, base_addr + 0xdc, 0x0003d208); rtw_write32(rtwdev, base_addr + 0xb8, 0x60000000); mdelay(2); rtw_write32(rtwdev, base_addr + 0xbc, 0x000aff8d); mdelay(2); rtw_write32(rtwdev, base_addr + 0xb0, 0x0a11fb89); rtw_write32(rtwdev, base_addr + 0xcc, 0x0a11fb89); mdelay(1); rtw_write32(rtwdev, base_addr + 0xb8, 0x62000000); rtw_write32(rtwdev, base_addr + 0xd4, 0x62000000); mdelay(20); if (!check_hw_ready(rtwdev, read_addr + 0x08, 0x7fff80, 0xffff) || !check_hw_ready(rtwdev, read_addr + 0x34, 0x7fff80, 0xffff)) rtw_err(rtwdev, "failed to wait for dack ready\n"); rtw_write32(rtwdev, base_addr + 0xb8, 0x02000000); mdelay(1); rtw_write32(rtwdev, base_addr + 0xbc, 0x0008ff87); rtw_write32(rtwdev, 0x9b4, 0xdb6db600); rtw_write32(rtwdev, base_addr + 0x10, 0x02d508c5); rtw_write32(rtwdev, base_addr + 0xbc, 0x0008ff87); rtw_write32(rtwdev, base_addr + 0x60, 0xf0000000); } static void rtw8822c_dac_cal_step2(struct rtw_dev *rtwdev, u8 path, u32 *ic_out, u32 *qc_out) { u32 base_addr; u32 ic, qc, ic_in, qc_in; base_addr = rtw8822c_get_path_write_addr(path); rtw_write32_mask(rtwdev, base_addr + 0xbc, 0xf0000000, 0x0); rtw_write32_mask(rtwdev, base_addr + 0xc0, 0xf, 0x8); rtw_write32_mask(rtwdev, base_addr + 0xd8, 0xf0000000, 0x0); rtw_write32_mask(rtwdev, base_addr + 0xdc, 0xf, 0x8); rtw_write32(rtwdev, 0x1b00, 0x00000008); rtw_write8(rtwdev, 0x1bcc, 0x03f); rtw_write32(rtwdev, base_addr + 0x0c, 0xdff00220); rtw_write32(rtwdev, base_addr + 0x10, 0x02d508c5); rtw_write32(rtwdev, 0x1c3c, 0x00088103); rtw8822c_dac_cal_rf_mode(rtwdev, &ic_in, &qc_in); ic = ic_in; qc = qc_in; /* compensation value */ if (ic != 0x0) ic = 0x400 - ic; if (qc != 0x0) qc = 0x400 - qc; if (ic < 0x300) { ic = ic * 2 * 6 / 5; ic = ic + 0x80; } else { ic = (0x400 - ic) * 2 * 6 / 5; ic = 0x7f - ic; } if (qc < 0x300) { qc = qc * 2 * 6 / 5; qc = qc + 0x80; } else { qc = (0x400 - qc) * 2 * 6 / 5; qc = 0x7f - qc; } *ic_out = ic; *qc_out = qc; rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] before i=0x%x, q=0x%x\n", ic_in, qc_in); rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] after i=0x%x, q=0x%x\n", ic, qc); } static void rtw8822c_dac_cal_step3(struct rtw_dev *rtwdev, u8 path, u32 adc_ic, u32 adc_qc, u32 *ic_in, u32 *qc_in, u32 *i_out, u32 *q_out) { u32 base_addr; u32 read_addr; u32 ic, qc; u32 temp; base_addr = rtw8822c_get_path_write_addr(path); read_addr = rtw8822c_get_path_read_addr(path); ic = *ic_in; qc = *qc_in; rtw_write32(rtwdev, base_addr + 0x0c, 0xdff00220); rtw_write32(rtwdev, base_addr + 0x10, 0x02d508c5); rtw_write32(rtwdev, 0x9b4, 0xdb66db00); rtw_write32(rtwdev, base_addr + 0xb0, 0x0a11fb88); rtw_write32(rtwdev, base_addr + 0xbc, 0xc008ff81); rtw_write32(rtwdev, base_addr + 0xc0, 0x0003d208); rtw_write32_mask(rtwdev, base_addr + 0xbc, 0xf0000000, ic & 0xf); rtw_write32_mask(rtwdev, base_addr + 0xc0, 0xf, (ic & 0xf0) >> 4); rtw_write32(rtwdev, base_addr + 0xcc, 0x0a11fb88); rtw_write32(rtwdev, base_addr + 0xd8, 0xe008ff81); rtw_write32(rtwdev, base_addr + 0xdc, 0x0003d208); rtw_write32_mask(rtwdev, base_addr + 0xd8, 0xf0000000, qc & 0xf); rtw_write32_mask(rtwdev, base_addr + 0xdc, 0xf, (qc & 0xf0) >> 4); rtw_write32(rtwdev, base_addr + 0xb8, 0x60000000); mdelay(2); rtw_write32_mask(rtwdev, base_addr + 0xbc, 0xe, 0x6); mdelay(2); rtw_write32(rtwdev, base_addr + 0xb0, 0x0a11fb89); rtw_write32(rtwdev, base_addr + 0xcc, 0x0a11fb89); mdelay(1); rtw_write32(rtwdev, base_addr + 0xb8, 0x62000000); rtw_write32(rtwdev, base_addr + 0xd4, 0x62000000); mdelay(20); if (!check_hw_ready(rtwdev, read_addr + 0x24, 0x07f80000, ic) || !check_hw_ready(rtwdev, read_addr + 0x50, 0x07f80000, qc)) rtw_err(rtwdev, "failed to write IQ vector to hardware\n"); rtw_write32(rtwdev, base_addr + 0xb8, 0x02000000); mdelay(1); rtw_write32_mask(rtwdev, base_addr + 0xbc, 0xe, 0x3); rtw_write32(rtwdev, 0x9b4, 0xdb6db600); /* check DAC DC offset */ temp = ((adc_ic + 0x10) & 0x3ff) | (((adc_qc + 0x10) & 0x3ff) << 10); rtw_write32(rtwdev, base_addr + 0x68, temp); rtw_write32(rtwdev, base_addr + 0x10, 0x02d508c5); rtw_write32(rtwdev, base_addr + 0x60, 0xf0000000); rtw8822c_dac_cal_rf_mode(rtwdev, &ic, &qc); if (ic >= 0x10) ic = ic - 0x10; else ic = 0x400 - (0x10 - ic); if (qc >= 0x10) qc = qc - 0x10; else qc = 0x400 - (0x10 - qc); *i_out = ic; *q_out = qc; if (ic >= 0x200) ic = 0x400 - ic; if (qc >= 0x200) qc = 0x400 - qc; *ic_in = ic; *qc_in = qc; rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] after DACK i=0x%x, q=0x%x\n", *i_out, *q_out); } static void rtw8822c_dac_cal_step4(struct rtw_dev *rtwdev, u8 path) { u32 base_addr = rtw8822c_get_path_write_addr(path); rtw_write32(rtwdev, base_addr + 0x68, 0x0); rtw_write32(rtwdev, base_addr + 0x10, 0x02d508c4); rtw_write32_mask(rtwdev, base_addr + 0xbc, 0x1, 0x0); rtw_write32_mask(rtwdev, base_addr + 0x30, BIT(30), 0x1); } static void rtw8822c_dac_cal_backup_vec(struct rtw_dev *rtwdev, u8 path, u8 vec, u32 w_addr, u32 r_addr) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u16 val; u32 i; if (WARN_ON(vec >= 2)) return; for (i = 0; i < DACK_MSBK_BACKUP_NUM; i++) { rtw_write32_mask(rtwdev, w_addr, 0xf0000000, i); val = (u16)rtw_read32_mask(rtwdev, r_addr, 0x7fc0000); dm_info->dack_msbk[path][vec][i] = val; } } static void rtw8822c_dac_cal_backup_path(struct rtw_dev *rtwdev, u8 path) { u32 w_off = 0x1c; u32 r_off = 0x2c; u32 w_addr, r_addr; if (WARN_ON(path >= 2)) return; /* backup I vector */ w_addr = rtw8822c_get_path_write_addr(path) + 0xb0; r_addr = rtw8822c_get_path_read_addr(path) + 0x10; rtw8822c_dac_cal_backup_vec(rtwdev, path, 0, w_addr, r_addr); /* backup Q vector */ w_addr = rtw8822c_get_path_write_addr(path) + 0xb0 + w_off; r_addr = rtw8822c_get_path_read_addr(path) + 0x10 + r_off; rtw8822c_dac_cal_backup_vec(rtwdev, path, 1, w_addr, r_addr); } static void rtw8822c_dac_cal_backup_dck(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 val; val = (u8)rtw_read32_mask(rtwdev, REG_DCKA_I_0, 0xf0000000); dm_info->dack_dck[RF_PATH_A][0][0] = val; val = (u8)rtw_read32_mask(rtwdev, REG_DCKA_I_1, 0xf); dm_info->dack_dck[RF_PATH_A][0][1] = val; val = (u8)rtw_read32_mask(rtwdev, REG_DCKA_Q_0, 0xf0000000); dm_info->dack_dck[RF_PATH_A][1][0] = val; val = (u8)rtw_read32_mask(rtwdev, REG_DCKA_Q_1, 0xf); dm_info->dack_dck[RF_PATH_A][1][1] = val; val = (u8)rtw_read32_mask(rtwdev, REG_DCKB_I_0, 0xf0000000); dm_info->dack_dck[RF_PATH_B][0][0] = val; val = (u8)rtw_read32_mask(rtwdev, REG_DCKB_I_1, 0xf); dm_info->dack_dck[RF_PATH_B][1][0] = val; val = (u8)rtw_read32_mask(rtwdev, REG_DCKB_Q_0, 0xf0000000); dm_info->dack_dck[RF_PATH_B][0][1] = val; val = (u8)rtw_read32_mask(rtwdev, REG_DCKB_Q_1, 0xf); dm_info->dack_dck[RF_PATH_B][1][1] = val; } static void rtw8822c_dac_cal_backup(struct rtw_dev *rtwdev) { u32 temp[3]; temp[0] = rtw_read32(rtwdev, 0x1860); temp[1] = rtw_read32(rtwdev, 0x4160); temp[2] = rtw_read32(rtwdev, 0x9b4); /* set clock */ rtw_write32(rtwdev, 0x9b4, 0xdb66db00); /* backup path-A I/Q */ rtw_write32_clr(rtwdev, 0x1830, BIT(30)); rtw_write32_mask(rtwdev, 0x1860, 0xfc000000, 0x3c); rtw8822c_dac_cal_backup_path(rtwdev, RF_PATH_A); /* backup path-B I/Q */ rtw_write32_clr(rtwdev, 0x4130, BIT(30)); rtw_write32_mask(rtwdev, 0x4160, 0xfc000000, 0x3c); rtw8822c_dac_cal_backup_path(rtwdev, RF_PATH_B); rtw8822c_dac_cal_backup_dck(rtwdev); rtw_write32_set(rtwdev, 0x1830, BIT(30)); rtw_write32_set(rtwdev, 0x4130, BIT(30)); rtw_write32(rtwdev, 0x1860, temp[0]); rtw_write32(rtwdev, 0x4160, temp[1]); rtw_write32(rtwdev, 0x9b4, temp[2]); } static void rtw8822c_dac_cal_restore_dck(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 val; rtw_write32_set(rtwdev, REG_DCKA_I_0, BIT(19)); val = dm_info->dack_dck[RF_PATH_A][0][0]; rtw_write32_mask(rtwdev, REG_DCKA_I_0, 0xf0000000, val); val = dm_info->dack_dck[RF_PATH_A][0][1]; rtw_write32_mask(rtwdev, REG_DCKA_I_1, 0xf, val); rtw_write32_set(rtwdev, REG_DCKA_Q_0, BIT(19)); val = dm_info->dack_dck[RF_PATH_A][1][0]; rtw_write32_mask(rtwdev, REG_DCKA_Q_0, 0xf0000000, val); val = dm_info->dack_dck[RF_PATH_A][1][1]; rtw_write32_mask(rtwdev, REG_DCKA_Q_1, 0xf, val); rtw_write32_set(rtwdev, REG_DCKB_I_0, BIT(19)); val = dm_info->dack_dck[RF_PATH_B][0][0]; rtw_write32_mask(rtwdev, REG_DCKB_I_0, 0xf0000000, val); val = dm_info->dack_dck[RF_PATH_B][0][1]; rtw_write32_mask(rtwdev, REG_DCKB_I_1, 0xf, val); rtw_write32_set(rtwdev, REG_DCKB_Q_0, BIT(19)); val = dm_info->dack_dck[RF_PATH_B][1][0]; rtw_write32_mask(rtwdev, REG_DCKB_Q_0, 0xf0000000, val); val = dm_info->dack_dck[RF_PATH_B][1][1]; rtw_write32_mask(rtwdev, REG_DCKB_Q_1, 0xf, val); } static void rtw8822c_dac_cal_restore_prepare(struct rtw_dev *rtwdev) { rtw_write32(rtwdev, 0x9b4, 0xdb66db00); rtw_write32_mask(rtwdev, 0x18b0, BIT(27), 0x0); rtw_write32_mask(rtwdev, 0x18cc, BIT(27), 0x0); rtw_write32_mask(rtwdev, 0x41b0, BIT(27), 0x0); rtw_write32_mask(rtwdev, 0x41cc, BIT(27), 0x0); rtw_write32_mask(rtwdev, 0x1830, BIT(30), 0x0); rtw_write32_mask(rtwdev, 0x1860, 0xfc000000, 0x3c); rtw_write32_mask(rtwdev, 0x18b4, BIT(0), 0x1); rtw_write32_mask(rtwdev, 0x18d0, BIT(0), 0x1); rtw_write32_mask(rtwdev, 0x4130, BIT(30), 0x0); rtw_write32_mask(rtwdev, 0x4160, 0xfc000000, 0x3c); rtw_write32_mask(rtwdev, 0x41b4, BIT(0), 0x1); rtw_write32_mask(rtwdev, 0x41d0, BIT(0), 0x1); rtw_write32_mask(rtwdev, 0x18b0, 0xf00, 0x0); rtw_write32_mask(rtwdev, 0x18c0, BIT(14), 0x0); rtw_write32_mask(rtwdev, 0x18cc, 0xf00, 0x0); rtw_write32_mask(rtwdev, 0x18dc, BIT(14), 0x0); rtw_write32_mask(rtwdev, 0x18b0, BIT(0), 0x0); rtw_write32_mask(rtwdev, 0x18cc, BIT(0), 0x0); rtw_write32_mask(rtwdev, 0x18b0, BIT(0), 0x1); rtw_write32_mask(rtwdev, 0x18cc, BIT(0), 0x1); rtw8822c_dac_cal_restore_dck(rtwdev); rtw_write32_mask(rtwdev, 0x18c0, 0x38000, 0x7); rtw_write32_mask(rtwdev, 0x18dc, 0x38000, 0x7); rtw_write32_mask(rtwdev, 0x41c0, 0x38000, 0x7); rtw_write32_mask(rtwdev, 0x41dc, 0x38000, 0x7); rtw_write32_mask(rtwdev, 0x18b8, BIT(26) | BIT(25), 0x1); rtw_write32_mask(rtwdev, 0x18d4, BIT(26) | BIT(25), 0x1); rtw_write32_mask(rtwdev, 0x41b0, 0xf00, 0x0); rtw_write32_mask(rtwdev, 0x41c0, BIT(14), 0x0); rtw_write32_mask(rtwdev, 0x41cc, 0xf00, 0x0); rtw_write32_mask(rtwdev, 0x41dc, BIT(14), 0x0); rtw_write32_mask(rtwdev, 0x41b0, BIT(0), 0x0); rtw_write32_mask(rtwdev, 0x41cc, BIT(0), 0x0); rtw_write32_mask(rtwdev, 0x41b0, BIT(0), 0x1); rtw_write32_mask(rtwdev, 0x41cc, BIT(0), 0x1); rtw_write32_mask(rtwdev, 0x41b8, BIT(26) | BIT(25), 0x1); rtw_write32_mask(rtwdev, 0x41d4, BIT(26) | BIT(25), 0x1); } static bool rtw8822c_dac_cal_restore_wait(struct rtw_dev *rtwdev, u32 target_addr, u32 toggle_addr) { u32 cnt = 0; do { rtw_write32_mask(rtwdev, toggle_addr, BIT(26) | BIT(25), 0x0); rtw_write32_mask(rtwdev, toggle_addr, BIT(26) | BIT(25), 0x2); if (rtw_read32_mask(rtwdev, target_addr, 0xf) == 0x6) return true; } while (cnt++ < 100); return false; } static bool rtw8822c_dac_cal_restore_path(struct rtw_dev *rtwdev, u8 path) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u32 w_off = 0x1c; u32 r_off = 0x2c; u32 w_i, r_i, w_q, r_q; u32 value; u32 i; w_i = rtw8822c_get_path_write_addr(path) + 0xb0; r_i = rtw8822c_get_path_read_addr(path) + 0x08; w_q = rtw8822c_get_path_write_addr(path) + 0xb0 + w_off; r_q = rtw8822c_get_path_read_addr(path) + 0x08 + r_off; if (!rtw8822c_dac_cal_restore_wait(rtwdev, r_i, w_i + 0x8)) return false; for (i = 0; i < DACK_MSBK_BACKUP_NUM; i++) { rtw_write32_mask(rtwdev, w_i + 0x4, BIT(2), 0x0); value = dm_info->dack_msbk[path][0][i]; rtw_write32_mask(rtwdev, w_i + 0x4, 0xff8, value); rtw_write32_mask(rtwdev, w_i, 0xf0000000, i); rtw_write32_mask(rtwdev, w_i + 0x4, BIT(2), 0x1); } rtw_write32_mask(rtwdev, w_i + 0x4, BIT(2), 0x0); if (!rtw8822c_dac_cal_restore_wait(rtwdev, r_q, w_q + 0x8)) return false; for (i = 0; i < DACK_MSBK_BACKUP_NUM; i++) { rtw_write32_mask(rtwdev, w_q + 0x4, BIT(2), 0x0); value = dm_info->dack_msbk[path][1][i]; rtw_write32_mask(rtwdev, w_q + 0x4, 0xff8, value); rtw_write32_mask(rtwdev, w_q, 0xf0000000, i); rtw_write32_mask(rtwdev, w_q + 0x4, BIT(2), 0x1); } rtw_write32_mask(rtwdev, w_q + 0x4, BIT(2), 0x0); rtw_write32_mask(rtwdev, w_i + 0x8, BIT(26) | BIT(25), 0x0); rtw_write32_mask(rtwdev, w_q + 0x8, BIT(26) | BIT(25), 0x0); rtw_write32_mask(rtwdev, w_i + 0x4, BIT(0), 0x0); rtw_write32_mask(rtwdev, w_q + 0x4, BIT(0), 0x0); return true; } static bool __rtw8822c_dac_cal_restore(struct rtw_dev *rtwdev) { if (!rtw8822c_dac_cal_restore_path(rtwdev, RF_PATH_A)) return false; if (!rtw8822c_dac_cal_restore_path(rtwdev, RF_PATH_B)) return false; return true; } static bool rtw8822c_dac_cal_restore(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u32 temp[3]; /* sample the first element for both path's IQ vector */ if (dm_info->dack_msbk[RF_PATH_A][0][0] == 0 && dm_info->dack_msbk[RF_PATH_A][1][0] == 0 && dm_info->dack_msbk[RF_PATH_B][0][0] == 0 && dm_info->dack_msbk[RF_PATH_B][1][0] == 0) return false; temp[0] = rtw_read32(rtwdev, 0x1860); temp[1] = rtw_read32(rtwdev, 0x4160); temp[2] = rtw_read32(rtwdev, 0x9b4); rtw8822c_dac_cal_restore_prepare(rtwdev); if (!check_hw_ready(rtwdev, 0x2808, 0x7fff80, 0xffff) || !check_hw_ready(rtwdev, 0x2834, 0x7fff80, 0xffff) || !check_hw_ready(rtwdev, 0x4508, 0x7fff80, 0xffff) || !check_hw_ready(rtwdev, 0x4534, 0x7fff80, 0xffff)) return false; if (!__rtw8822c_dac_cal_restore(rtwdev)) { rtw_err(rtwdev, "failed to restore dack vectors\n"); return false; } rtw_write32_mask(rtwdev, 0x1830, BIT(30), 0x1); rtw_write32_mask(rtwdev, 0x4130, BIT(30), 0x1); rtw_write32(rtwdev, 0x1860, temp[0]); rtw_write32(rtwdev, 0x4160, temp[1]); rtw_write32_mask(rtwdev, 0x18b0, BIT(27), 0x1); rtw_write32_mask(rtwdev, 0x18cc, BIT(27), 0x1); rtw_write32_mask(rtwdev, 0x41b0, BIT(27), 0x1); rtw_write32_mask(rtwdev, 0x41cc, BIT(27), 0x1); rtw_write32(rtwdev, 0x9b4, temp[2]); return true; } static void rtw8822c_rf_dac_cal(struct rtw_dev *rtwdev) { struct rtw_backup_info backup_rf[DACK_RF_8822C * DACK_PATH_8822C]; struct rtw_backup_info backup[DACK_REG_8822C]; u32 ic = 0, qc = 0, i; u32 i_a = 0x0, q_a = 0x0, i_b = 0x0, q_b = 0x0; u32 ic_a = 0x0, qc_a = 0x0, ic_b = 0x0, qc_b = 0x0; u32 adc_ic_a = 0x0, adc_qc_a = 0x0, adc_ic_b = 0x0, adc_qc_b = 0x0; if (rtw8822c_dac_cal_restore(rtwdev)) return; /* not able to restore, do it */ rtw8822c_dac_backup_reg(rtwdev, backup, backup_rf); rtw8822c_dac_bb_setting(rtwdev); /* path-A */ rtw8822c_dac_cal_adc(rtwdev, RF_PATH_A, &adc_ic_a, &adc_qc_a); for (i = 0; i < 10; i++) { rtw8822c_dac_cal_step1(rtwdev, RF_PATH_A); rtw8822c_dac_cal_step2(rtwdev, RF_PATH_A, &ic, &qc); ic_a = ic; qc_a = qc; rtw8822c_dac_cal_step3(rtwdev, RF_PATH_A, adc_ic_a, adc_qc_a, &ic, &qc, &i_a, &q_a); if (ic < 5 && qc < 5) break; } rtw8822c_dac_cal_step4(rtwdev, RF_PATH_A); /* path-B */ rtw8822c_dac_cal_adc(rtwdev, RF_PATH_B, &adc_ic_b, &adc_qc_b); for (i = 0; i < 10; i++) { rtw8822c_dac_cal_step1(rtwdev, RF_PATH_B); rtw8822c_dac_cal_step2(rtwdev, RF_PATH_B, &ic, &qc); ic_b = ic; qc_b = qc; rtw8822c_dac_cal_step3(rtwdev, RF_PATH_B, adc_ic_b, adc_qc_b, &ic, &qc, &i_b, &q_b); if (ic < 5 && qc < 5) break; } rtw8822c_dac_cal_step4(rtwdev, RF_PATH_B); rtw_write32(rtwdev, 0x1b00, 0x00000008); rtw_write32_mask(rtwdev, 0x4130, BIT(30), 0x1); rtw_write8(rtwdev, 0x1bcc, 0x0); rtw_write32(rtwdev, 0x1b00, 0x0000000a); rtw_write8(rtwdev, 0x1bcc, 0x0); rtw8822c_dac_restore_reg(rtwdev, backup, backup_rf); /* backup results to restore, saving a lot of time */ rtw8822c_dac_cal_backup(rtwdev); rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] path A: ic=0x%x, qc=0x%x\n", ic_a, qc_a); rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] path B: ic=0x%x, qc=0x%x\n", ic_b, qc_b); rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] path A: i=0x%x, q=0x%x\n", i_a, q_a); rtw_dbg(rtwdev, RTW_DBG_RFK, "[DACK] path B: i=0x%x, q=0x%x\n", i_b, q_b); } static void rtw8822c_rf_x2_check(struct rtw_dev *rtwdev) { u8 x2k_busy; mdelay(1); x2k_busy = rtw_read_rf(rtwdev, RF_PATH_A, 0xb8, BIT(15)); if (x2k_busy == 1) { rtw_write_rf(rtwdev, RF_PATH_A, 0xb8, RFREG_MASK, 0xC4440); rtw_write_rf(rtwdev, RF_PATH_A, 0xba, RFREG_MASK, 0x6840D); rtw_write_rf(rtwdev, RF_PATH_A, 0xb8, RFREG_MASK, 0x80440); mdelay(1); } } static void rtw8822c_set_power_trim(struct rtw_dev *rtwdev, s8 bb_gain[2][8]) { #define RF_SET_POWER_TRIM(_path, _seq, _idx) \ do { \ rtw_write_rf(rtwdev, _path, 0x33, RFREG_MASK, _seq); \ rtw_write_rf(rtwdev, _path, 0x3f, RFREG_MASK, \ bb_gain[_path][_idx]); \ } while (0) u8 path; for (path = 0; path < rtwdev->hal.rf_path_num; path++) { rtw_write_rf(rtwdev, path, 0xee, BIT(19), 1); RF_SET_POWER_TRIM(path, 0x0, 0); RF_SET_POWER_TRIM(path, 0x1, 1); RF_SET_POWER_TRIM(path, 0x2, 2); RF_SET_POWER_TRIM(path, 0x3, 2); RF_SET_POWER_TRIM(path, 0x4, 3); RF_SET_POWER_TRIM(path, 0x5, 4); RF_SET_POWER_TRIM(path, 0x6, 5); RF_SET_POWER_TRIM(path, 0x7, 6); RF_SET_POWER_TRIM(path, 0x8, 7); RF_SET_POWER_TRIM(path, 0x9, 3); RF_SET_POWER_TRIM(path, 0xa, 4); RF_SET_POWER_TRIM(path, 0xb, 5); RF_SET_POWER_TRIM(path, 0xc, 6); RF_SET_POWER_TRIM(path, 0xd, 7); RF_SET_POWER_TRIM(path, 0xe, 7); rtw_write_rf(rtwdev, path, 0xee, BIT(19), 0); } #undef RF_SET_POWER_TRIM } static void rtw8822c_power_trim(struct rtw_dev *rtwdev) { u8 pg_pwr = 0xff, i, path, idx; s8 bb_gain[2][8] = {}; u16 rf_efuse_2g[3] = {PPG_2GL_TXAB, PPG_2GM_TXAB, PPG_2GH_TXAB}; u16 rf_efuse_5g[2][5] = {{PPG_5GL1_TXA, PPG_5GL2_TXA, PPG_5GM1_TXA, PPG_5GM2_TXA, PPG_5GH1_TXA}, {PPG_5GL1_TXB, PPG_5GL2_TXB, PPG_5GM1_TXB, PPG_5GM2_TXB, PPG_5GH1_TXB} }; bool set = false; for (i = 0; i < ARRAY_SIZE(rf_efuse_2g); i++) { rtw_read8_physical_efuse(rtwdev, rf_efuse_2g[i], &pg_pwr); if (pg_pwr == EFUSE_READ_FAIL) continue; set = true; bb_gain[RF_PATH_A][i] = FIELD_GET(PPG_2G_A_MASK, pg_pwr); bb_gain[RF_PATH_B][i] = FIELD_GET(PPG_2G_B_MASK, pg_pwr); } for (i = 0; i < ARRAY_SIZE(rf_efuse_5g[0]); i++) { for (path = 0; path < rtwdev->hal.rf_path_num; path++) { rtw_read8_physical_efuse(rtwdev, rf_efuse_5g[path][i], &pg_pwr); if (pg_pwr == EFUSE_READ_FAIL) continue; set = true; idx = i + ARRAY_SIZE(rf_efuse_2g); bb_gain[path][idx] = FIELD_GET(PPG_5G_MASK, pg_pwr); } } if (set) rtw8822c_set_power_trim(rtwdev, bb_gain); rtw_write32_mask(rtwdev, REG_DIS_DPD, DIS_DPD_MASK, DIS_DPD_RATEALL); } static void rtw8822c_thermal_trim(struct rtw_dev *rtwdev) { u16 rf_efuse[2] = {PPG_THERMAL_A, PPG_THERMAL_B}; u8 pg_therm = 0xff, thermal[2] = {0}, path; for (path = 0; path < rtwdev->hal.rf_path_num; path++) { rtw_read8_physical_efuse(rtwdev, rf_efuse[path], &pg_therm); if (pg_therm == EFUSE_READ_FAIL) return; /* Efuse value of BIT(0) shall be move to BIT(3), and the value * of BIT(1) to BIT(3) should be right shifted 1 bit. */ thermal[path] = FIELD_GET(GENMASK(3, 1), pg_therm); thermal[path] |= FIELD_PREP(BIT(3), pg_therm & BIT(0)); rtw_write_rf(rtwdev, path, 0x43, RF_THEMAL_MASK, thermal[path]); } } static void rtw8822c_pa_bias(struct rtw_dev *rtwdev) { u16 rf_efuse_2g[2] = {PPG_PABIAS_2GA, PPG_PABIAS_2GB}; u16 rf_efuse_5g[2] = {PPG_PABIAS_5GA, PPG_PABIAS_5GB}; u8 pg_pa_bias = 0xff, path; for (path = 0; path < rtwdev->hal.rf_path_num; path++) { rtw_read8_physical_efuse(rtwdev, rf_efuse_2g[path], &pg_pa_bias); if (pg_pa_bias == EFUSE_READ_FAIL) return; pg_pa_bias = FIELD_GET(PPG_PABIAS_MASK, pg_pa_bias); rtw_write_rf(rtwdev, path, RF_PA, RF_PABIAS_2G_MASK, pg_pa_bias); } for (path = 0; path < rtwdev->hal.rf_path_num; path++) { rtw_read8_physical_efuse(rtwdev, rf_efuse_5g[path], &pg_pa_bias); pg_pa_bias = FIELD_GET(PPG_PABIAS_MASK, pg_pa_bias); rtw_write_rf(rtwdev, path, RF_PA, RF_PABIAS_5G_MASK, pg_pa_bias); } } static void rtw8822c_rfk_handshake(struct rtw_dev *rtwdev, bool is_before_k) { struct rtw_dm_info *dm = &rtwdev->dm_info; u8 u1b_tmp; u8 u4b_tmp; int ret; if (is_before_k) { rtw_dbg(rtwdev, RTW_DBG_RFK, "[RFK] WiFi / BT RFK handshake start!!\n"); if (!dm->is_bt_iqk_timeout) { ret = read_poll_timeout(rtw_read32_mask, u4b_tmp, u4b_tmp == 0, 20, 600000, false, rtwdev, REG_PMC_DBG_CTRL1, BITS_PMC_BT_IQK_STS); if (ret) { rtw_dbg(rtwdev, RTW_DBG_RFK, "[RFK] Wait BT IQK finish timeout!!\n"); dm->is_bt_iqk_timeout = true; } } rtw_fw_inform_rfk_status(rtwdev, true); ret = read_poll_timeout(rtw_read8_mask, u1b_tmp, u1b_tmp == 1, 20, 100000, false, rtwdev, REG_ARFR4, BIT_WL_RFK); if (ret) rtw_dbg(rtwdev, RTW_DBG_RFK, "[RFK] Send WiFi RFK start H2C cmd FAIL!!\n"); } else { rtw_fw_inform_rfk_status(rtwdev, false); ret = read_poll_timeout(rtw_read8_mask, u1b_tmp, u1b_tmp == 1, 20, 100000, false, rtwdev, REG_ARFR4, BIT_WL_RFK); if (ret) rtw_dbg(rtwdev, RTW_DBG_RFK, "[RFK] Send WiFi RFK finish H2C cmd FAIL!!\n"); rtw_dbg(rtwdev, RTW_DBG_RFK, "[RFK] WiFi / BT RFK handshake finish!!\n"); } } static void rtw8822c_rfk_power_save(struct rtw_dev *rtwdev, bool is_power_save) { u8 path; for (path = 0; path < rtwdev->hal.rf_path_num; path++) { rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SEL_PATH, path); rtw_write32_mask(rtwdev, REG_DPD_CTL1_S0, BIT_PS_EN, is_power_save ? 0 : 1); } } static void rtw8822c_txgapk_backup_bb_reg(struct rtw_dev *rtwdev, const u32 reg[], u32 reg_backup[], u32 reg_num) { u32 i; for (i = 0; i < reg_num; i++) { reg_backup[i] = rtw_read32(rtwdev, reg[i]); rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] Backup BB 0x%x = 0x%x\n", reg[i], reg_backup[i]); } } static void rtw8822c_txgapk_reload_bb_reg(struct rtw_dev *rtwdev, const u32 reg[], u32 reg_backup[], u32 reg_num) { u32 i; for (i = 0; i < reg_num; i++) { rtw_write32(rtwdev, reg[i], reg_backup[i]); rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] Reload BB 0x%x = 0x%x\n", reg[i], reg_backup[i]); } } static bool check_rf_status(struct rtw_dev *rtwdev, u8 status) { u8 reg_rf0_a, reg_rf0_b; reg_rf0_a = (u8)rtw_read_rf(rtwdev, RF_PATH_A, RF_MODE_TRXAGC, BIT_RF_MODE); reg_rf0_b = (u8)rtw_read_rf(rtwdev, RF_PATH_B, RF_MODE_TRXAGC, BIT_RF_MODE); if (reg_rf0_a == status || reg_rf0_b == status) return false; return true; } static void rtw8822c_txgapk_tx_pause(struct rtw_dev *rtwdev) { bool status; int ret; rtw_write8(rtwdev, REG_TXPAUSE, BIT_AC_QUEUE); rtw_write32_mask(rtwdev, REG_TX_FIFO, BIT_STOP_TX, 0x2); ret = read_poll_timeout_atomic(check_rf_status, status, status, 2, 5000, false, rtwdev, 2); if (ret) rtw_warn(rtwdev, "failed to pause TX\n"); rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] Tx pause!!\n"); } static void rtw8822c_txgapk_bb_dpk(struct rtw_dev *rtwdev, u8 path) { rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] ======>%s\n", __func__); rtw_write32_mask(rtwdev, REG_ENFN, BIT_IQK_DPK_EN, 0x1); rtw_write32_mask(rtwdev, REG_CH_DELAY_EXTR2, BIT_IQK_DPK_CLOCK_SRC, 0x1); rtw_write32_mask(rtwdev, REG_CH_DELAY_EXTR2, BIT_IQK_DPK_RESET_SRC, 0x1); rtw_write32_mask(rtwdev, REG_CH_DELAY_EXTR2, BIT_EN_IOQ_IQK_DPK, 0x1); rtw_write32_mask(rtwdev, REG_CH_DELAY_EXTR2, BIT_TST_IQK2SET_SRC, 0x0); rtw_write32_mask(rtwdev, REG_CCA_OFF, BIT_CCA_ON_BY_PW, 0x1ff); if (path == RF_PATH_A) { rtw_write32_mask(rtwdev, REG_RFTXEN_GCK_A, BIT_RFTXEN_GCK_FORCE_ON, 0x1); rtw_write32_mask(rtwdev, REG_3WIRE, BIT_DIS_SHARERX_TXGAT, 0x1); rtw_write32_mask(rtwdev, REG_DIS_SHARE_RX_A, BIT_TX_SCALE_0DB, 0x1); rtw_write32_mask(rtwdev, REG_3WIRE, BIT_3WIRE_EN, 0x0); } else if (path == RF_PATH_B) { rtw_write32_mask(rtwdev, REG_RFTXEN_GCK_B, BIT_RFTXEN_GCK_FORCE_ON, 0x1); rtw_write32_mask(rtwdev, REG_3WIRE2, BIT_DIS_SHARERX_TXGAT, 0x1); rtw_write32_mask(rtwdev, REG_DIS_SHARE_RX_B, BIT_TX_SCALE_0DB, 0x1); rtw_write32_mask(rtwdev, REG_3WIRE2, BIT_3WIRE_EN, 0x0); } rtw_write32_mask(rtwdev, REG_CCKSB, BIT_BBMODE, 0x2); } static void rtw8822c_txgapk_afe_dpk(struct rtw_dev *rtwdev, u8 path) { u32 reg; rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] ======>%s\n", __func__); if (path == RF_PATH_A) { reg = REG_ANAPAR_A; } else if (path == RF_PATH_B) { reg = REG_ANAPAR_B; } else { rtw_err(rtwdev, "[TXGAPK] unknown path %d!!\n", path); return; } rtw_write32_mask(rtwdev, REG_IQK_CTRL, MASKDWORD, MASKDWORD); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x700f0001); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x700f0001); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x701f0001); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x702f0001); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x703f0001); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x704f0001); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x705f0001); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x706f0001); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x707f0001); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x708f0001); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x709f0001); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70af0001); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70bf0001); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70cf0001); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70df0001); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70ef0001); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70ff0001); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70ff0001); } static void rtw8822c_txgapk_afe_dpk_restore(struct rtw_dev *rtwdev, u8 path) { u32 reg; rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] ======>%s\n", __func__); if (path == RF_PATH_A) { reg = REG_ANAPAR_A; } else if (path == RF_PATH_B) { reg = REG_ANAPAR_B; } else { rtw_err(rtwdev, "[TXGAPK] unknown path %d!!\n", path); return; } rtw_write32_mask(rtwdev, REG_IQK_CTRL, MASKDWORD, 0xffa1005e); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x700b8041); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70144041); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70244041); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70344041); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70444041); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x705b8041); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70644041); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x707b8041); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x708b8041); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x709b8041); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70ab8041); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70bb8041); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70cb8041); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70db8041); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70eb8041); rtw_write32_mask(rtwdev, reg, MASKDWORD, 0x70fb8041); } static void rtw8822c_txgapk_bb_dpk_restore(struct rtw_dev *rtwdev, u8 path) { rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] ======>%s\n", __func__); rtw_write_rf(rtwdev, path, RF_DEBUG, BIT_DE_TX_GAIN, 0x0); rtw_write_rf(rtwdev, path, RF_DIS_BYPASS_TXBB, BIT_TIA_BYPASS, 0x0); rtw_write_rf(rtwdev, path, RF_DIS_BYPASS_TXBB, BIT_TXBB, 0x0); rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SEL_PATH, 0x0); rtw_write32_mask(rtwdev, REG_IQK_CTL1, BIT_TX_CFIR, 0x0); rtw_write32_mask(rtwdev, REG_SINGLE_TONE_SW, BIT_IRQ_TEST_MODE, 0x0); rtw_write32_mask(rtwdev, REG_R_CONFIG, MASKBYTE0, 0x00); rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SEL_PATH, 0x1); rtw_write32_mask(rtwdev, REG_IQK_CTL1, BIT_TX_CFIR, 0x0); rtw_write32_mask(rtwdev, REG_SINGLE_TONE_SW, BIT_IRQ_TEST_MODE, 0x0); rtw_write32_mask(rtwdev, REG_R_CONFIG, MASKBYTE0, 0x00); rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SEL_PATH, 0x0); rtw_write32_mask(rtwdev, REG_CCA_OFF, BIT_CCA_ON_BY_PW, 0x0); if (path == RF_PATH_A) { rtw_write32_mask(rtwdev, REG_RFTXEN_GCK_A, BIT_RFTXEN_GCK_FORCE_ON, 0x0); rtw_write32_mask(rtwdev, REG_3WIRE, BIT_DIS_SHARERX_TXGAT, 0x0); rtw_write32_mask(rtwdev, REG_DIS_SHARE_RX_A, BIT_TX_SCALE_0DB, 0x0); rtw_write32_mask(rtwdev, REG_3WIRE, BIT_3WIRE_EN, 0x3); } else if (path == RF_PATH_B) { rtw_write32_mask(rtwdev, REG_RFTXEN_GCK_B, BIT_RFTXEN_GCK_FORCE_ON, 0x0); rtw_write32_mask(rtwdev, REG_3WIRE2, BIT_DIS_SHARERX_TXGAT, 0x0); rtw_write32_mask(rtwdev, REG_DIS_SHARE_RX_B, BIT_TX_SCALE_0DB, 0x0); rtw_write32_mask(rtwdev, REG_3WIRE2, BIT_3WIRE_EN, 0x3); } rtw_write32_mask(rtwdev, REG_CCKSB, BIT_BBMODE, 0x0); rtw_write32_mask(rtwdev, REG_IQK_CTL1, BIT_CFIR_EN, 0x5); } static bool _rtw8822c_txgapk_gain_valid(struct rtw_dev *rtwdev, u32 gain) { if ((FIELD_GET(BIT_GAIN_TX_PAD_H, gain) >= 0xc) && (FIELD_GET(BIT_GAIN_TX_PAD_L, gain) >= 0xe)) return true; return false; } static void _rtw8822c_txgapk_write_gain_bb_table(struct rtw_dev *rtwdev, u8 band, u8 path) { struct rtw_gapk_info *txgapk = &rtwdev->dm_info.gapk; u32 v, tmp_3f = 0; u8 gain, check_txgain; rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SEL_PATH, path); switch (band) { case RF_BAND_2G_OFDM: rtw_write32_mask(rtwdev, REG_TABLE_SEL, BIT_Q_GAIN_SEL, 0x0); break; case RF_BAND_5G_L: rtw_write32_mask(rtwdev, REG_TABLE_SEL, BIT_Q_GAIN_SEL, 0x2); break; case RF_BAND_5G_M: rtw_write32_mask(rtwdev, REG_TABLE_SEL, BIT_Q_GAIN_SEL, 0x3); break; case RF_BAND_5G_H: rtw_write32_mask(rtwdev, REG_TABLE_SEL, BIT_Q_GAIN_SEL, 0x4); break; default: break; } rtw_write32_mask(rtwdev, REG_TX_GAIN_SET, MASKBYTE0, 0x88); check_txgain = 0; for (gain = 0; gain < RF_GAIN_NUM; gain++) { v = txgapk->rf3f_bp[band][gain][path]; if (_rtw8822c_txgapk_gain_valid(rtwdev, v)) { if (!check_txgain) { tmp_3f = txgapk->rf3f_bp[band][gain][path]; check_txgain = 1; } rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] tx_gain=0x%03X >= 0xCEX\n", txgapk->rf3f_bp[band][gain][path]); } else { tmp_3f = txgapk->rf3f_bp[band][gain][path]; } rtw_write32_mask(rtwdev, REG_TABLE_SEL, BIT_Q_GAIN, tmp_3f); rtw_write32_mask(rtwdev, REG_TABLE_SEL, BIT_I_GAIN, gain); rtw_write32_mask(rtwdev, REG_TABLE_SEL, BIT_GAIN_RST, 0x1); rtw_write32_mask(rtwdev, REG_TABLE_SEL, BIT_GAIN_RST, 0x0); rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] Band=%d 0x1b98[11:0]=0x%03X path=%d\n", band, tmp_3f, path); } } static void rtw8822c_txgapk_write_gain_bb_table(struct rtw_dev *rtwdev) { u8 path, band; rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] ======>%s channel=%d\n", __func__, rtwdev->dm_info.gapk.channel); for (band = 0; band < RF_BAND_MAX; band++) { for (path = 0; path < rtwdev->hal.rf_path_num; path++) { _rtw8822c_txgapk_write_gain_bb_table(rtwdev, band, path); } } } static void rtw8822c_txgapk_read_offset(struct rtw_dev *rtwdev, u8 path) { static const u32 cfg1_1b00[2] = {0x00000d18, 0x00000d2a}; static const u32 cfg2_1b00[2] = {0x00000d19, 0x00000d2b}; static const u32 set_pi[2] = {REG_RSV_CTRL, REG_WLRF1}; static const u32 path_setting[2] = {REG_ORITXCODE, REG_ORITXCODE2}; struct rtw_gapk_info *txgapk = &rtwdev->dm_info.gapk; u8 channel = txgapk->channel; u32 val; int i; if (path >= ARRAY_SIZE(cfg1_1b00) || path >= ARRAY_SIZE(cfg2_1b00) || path >= ARRAY_SIZE(set_pi) || path >= ARRAY_SIZE(path_setting)) { rtw_warn(rtwdev, "[TXGAPK] wrong path %d\n", path); return; } rtw_write32_mask(rtwdev, REG_ANTMAP0, BIT_ANT_PATH, path + 1); rtw_write32_mask(rtwdev, REG_TXLGMAP, MASKDWORD, 0xe4e40000); rtw_write32_mask(rtwdev, REG_TXANTSEG, BIT_ANTSEG, 0x3); rtw_write32_mask(rtwdev, path_setting[path], MASK20BITS, 0x33312); rtw_write32_mask(rtwdev, path_setting[path], BIT_PATH_EN, 0x1); rtw_write32_mask(rtwdev, set_pi[path], BITS_RFC_DIRECT, 0x0); rtw_write_rf(rtwdev, path, RF_LUTDBG, BIT_TXA_TANK, 0x1); rtw_write_rf(rtwdev, path, RF_IDAC, BIT_TX_MODE, 0x820); rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SEL_PATH, path); rtw_write32_mask(rtwdev, REG_IQKSTAT, MASKBYTE0, 0x0); rtw_write32_mask(rtwdev, REG_TX_TONE_IDX, MASKBYTE0, 0x018); fsleep(1000); if (channel >= 1 && channel <= 14) rtw_write32_mask(rtwdev, REG_R_CONFIG, MASKBYTE0, BIT_2G_SWING); else rtw_write32_mask(rtwdev, REG_R_CONFIG, MASKBYTE0, BIT_5G_SWING); fsleep(1000); rtw_write32_mask(rtwdev, REG_NCTL0, MASKDWORD, cfg1_1b00[path]); rtw_write32_mask(rtwdev, REG_NCTL0, MASKDWORD, cfg2_1b00[path]); read_poll_timeout(rtw_read32_mask, val, val == 0x55, 1000, 100000, false, rtwdev, REG_RPT_CIP, BIT_RPT_CIP_STATUS); rtw_write32_mask(rtwdev, set_pi[path], BITS_RFC_DIRECT, 0x2); rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SEL_PATH, path); rtw_write32_mask(rtwdev, REG_RXSRAM_CTL, BIT_RPT_EN, 0x1); rtw_write32_mask(rtwdev, REG_RXSRAM_CTL, BIT_RPT_SEL, 0x12); rtw_write32_mask(rtwdev, REG_TX_GAIN_SET, BIT_GAPK_RPT_IDX, 0x3); val = rtw_read32(rtwdev, REG_STAT_RPT); txgapk->offset[0][path] = (s8)FIELD_GET(BIT_GAPK_RPT0, val); txgapk->offset[1][path] = (s8)FIELD_GET(BIT_GAPK_RPT1, val); txgapk->offset[2][path] = (s8)FIELD_GET(BIT_GAPK_RPT2, val); txgapk->offset[3][path] = (s8)FIELD_GET(BIT_GAPK_RPT3, val); txgapk->offset[4][path] = (s8)FIELD_GET(BIT_GAPK_RPT4, val); txgapk->offset[5][path] = (s8)FIELD_GET(BIT_GAPK_RPT5, val); txgapk->offset[6][path] = (s8)FIELD_GET(BIT_GAPK_RPT6, val); txgapk->offset[7][path] = (s8)FIELD_GET(BIT_GAPK_RPT7, val); rtw_write32_mask(rtwdev, REG_TX_GAIN_SET, BIT_GAPK_RPT_IDX, 0x4); val = rtw_read32(rtwdev, REG_STAT_RPT); txgapk->offset[8][path] = (s8)FIELD_GET(BIT_GAPK_RPT0, val); txgapk->offset[9][path] = (s8)FIELD_GET(BIT_GAPK_RPT1, val); for (i = 0; i < RF_HW_OFFSET_NUM; i++) if (txgapk->offset[i][path] & BIT(3)) txgapk->offset[i][path] = txgapk->offset[i][path] | 0xf0; for (i = 0; i < RF_HW_OFFSET_NUM; i++) rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] offset %d %d path=%d\n", txgapk->offset[i][path], i, path); } static void rtw8822c_txgapk_calculate_offset(struct rtw_dev *rtwdev, u8 path) { static const u32 bb_reg[] = {REG_ANTMAP0, REG_TXLGMAP, REG_TXANTSEG, REG_ORITXCODE, REG_ORITXCODE2}; struct rtw_gapk_info *txgapk = &rtwdev->dm_info.gapk; u8 channel = txgapk->channel; u32 reg_backup[ARRAY_SIZE(bb_reg)] = {0}; rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] ======>%s channel=%d\n", __func__, channel); rtw8822c_txgapk_backup_bb_reg(rtwdev, bb_reg, reg_backup, ARRAY_SIZE(bb_reg)); if (channel >= 1 && channel <= 14) { rtw_write32_mask(rtwdev, REG_SINGLE_TONE_SW, BIT_IRQ_TEST_MODE, 0x0); rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SEL_PATH, path); rtw_write32_mask(rtwdev, REG_R_CONFIG, BIT_IQ_SWITCH, 0x3f); rtw_write32_mask(rtwdev, REG_IQK_CTL1, BIT_TX_CFIR, 0x0); rtw_write_rf(rtwdev, path, RF_DEBUG, BIT_DE_TX_GAIN, 0x1); rtw_write_rf(rtwdev, path, RF_MODE_TRXAGC, RFREG_MASK, 0x5000f); rtw_write_rf(rtwdev, path, RF_TX_GAIN_OFFSET, BIT_RF_GAIN, 0x0); rtw_write_rf(rtwdev, path, RF_RXG_GAIN, BIT_RXG_GAIN, 0x1); rtw_write_rf(rtwdev, path, RF_MODE_TRXAGC, BIT_RXAGC, 0x0f); rtw_write_rf(rtwdev, path, RF_DEBUG, BIT_DE_TRXBW, 0x1); rtw_write_rf(rtwdev, path, RF_BW_TRXBB, BIT_BW_TXBB, 0x1); rtw_write_rf(rtwdev, path, RF_BW_TRXBB, BIT_BW_RXBB, 0x0); rtw_write_rf(rtwdev, path, RF_EXT_TIA_BW, BIT_PW_EXT_TIA, 0x1); rtw_write32_mask(rtwdev, REG_IQKSTAT, MASKBYTE0, 0x00); rtw_write32_mask(rtwdev, REG_TABLE_SEL, BIT_Q_GAIN_SEL, 0x0); rtw8822c_txgapk_read_offset(rtwdev, path); rtw_dbg(rtwdev, RTW_DBG_RFK, "=============================\n"); } else { rtw_write32_mask(rtwdev, REG_SINGLE_TONE_SW, BIT_IRQ_TEST_MODE, 0x0); rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SEL_PATH, path); rtw_write32_mask(rtwdev, REG_R_CONFIG, BIT_IQ_SWITCH, 0x3f); rtw_write32_mask(rtwdev, REG_IQK_CTL1, BIT_TX_CFIR, 0x0); rtw_write_rf(rtwdev, path, RF_DEBUG, BIT_DE_TX_GAIN, 0x1); rtw_write_rf(rtwdev, path, RF_MODE_TRXAGC, RFREG_MASK, 0x50011); rtw_write_rf(rtwdev, path, RF_TXA_LB_SW, BIT_TXA_LB_ATT, 0x3); rtw_write_rf(rtwdev, path, RF_TXA_LB_SW, BIT_LB_ATT, 0x3); rtw_write_rf(rtwdev, path, RF_TXA_LB_SW, BIT_LB_SW, 0x1); rtw_write_rf(rtwdev, path, RF_RXA_MIX_GAIN, BIT_RXA_MIX_GAIN, 0x2); rtw_write_rf(rtwdev, path, RF_MODE_TRXAGC, BIT_RXAGC, 0x12); rtw_write_rf(rtwdev, path, RF_DEBUG, BIT_DE_TRXBW, 0x1); rtw_write_rf(rtwdev, path, RF_BW_TRXBB, BIT_BW_RXBB, 0x0); rtw_write_rf(rtwdev, path, RF_EXT_TIA_BW, BIT_PW_EXT_TIA, 0x1); rtw_write_rf(rtwdev, path, RF_MODE_TRXAGC, BIT_RF_MODE, 0x5); rtw_write32_mask(rtwdev, REG_IQKSTAT, MASKBYTE0, 0x0); if (channel >= 36 && channel <= 64) rtw_write32_mask(rtwdev, REG_TABLE_SEL, BIT_Q_GAIN_SEL, 0x2); else if (channel >= 100 && channel <= 144) rtw_write32_mask(rtwdev, REG_TABLE_SEL, BIT_Q_GAIN_SEL, 0x3); else if (channel >= 149 && channel <= 177) rtw_write32_mask(rtwdev, REG_TABLE_SEL, BIT_Q_GAIN_SEL, 0x4); rtw8822c_txgapk_read_offset(rtwdev, path); rtw_dbg(rtwdev, RTW_DBG_RFK, "=============================\n"); } rtw8822c_txgapk_reload_bb_reg(rtwdev, bb_reg, reg_backup, ARRAY_SIZE(bb_reg)); } static void rtw8822c_txgapk_rf_restore(struct rtw_dev *rtwdev, u8 path) { rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] ======>%s\n", __func__); if (path >= rtwdev->hal.rf_path_num) return; rtw_write_rf(rtwdev, path, RF_MODE_TRXAGC, BIT_RF_MODE, 0x3); rtw_write_rf(rtwdev, path, RF_DEBUG, BIT_DE_TRXBW, 0x0); rtw_write_rf(rtwdev, path, RF_EXT_TIA_BW, BIT_PW_EXT_TIA, 0x0); } static u32 rtw8822c_txgapk_cal_gain(struct rtw_dev *rtwdev, u32 gain, s8 offset) { u32 gain_x2, new_gain; rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] ======>%s\n", __func__); if (_rtw8822c_txgapk_gain_valid(rtwdev, gain)) { new_gain = gain; rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] gain=0x%03X(>=0xCEX) offset=%d new_gain=0x%03X\n", gain, offset, new_gain); return new_gain; } gain_x2 = (gain << 1) + offset; new_gain = (gain_x2 >> 1) | (gain_x2 & BIT(0) ? BIT_GAIN_EXT : 0); rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] gain=0x%X offset=%d new_gain=0x%X\n", gain, offset, new_gain); return new_gain; } static void rtw8822c_txgapk_write_tx_gain(struct rtw_dev *rtwdev) { struct rtw_gapk_info *txgapk = &rtwdev->dm_info.gapk; u32 i, j, tmp = 0x20, tmp_3f, v; s8 offset_tmp[RF_GAIN_NUM] = {0}; u8 path, band = RF_BAND_2G_OFDM, channel = txgapk->channel; rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] ======>%s\n", __func__); if (channel >= 1 && channel <= 14) { tmp = 0x20; band = RF_BAND_2G_OFDM; } else if (channel >= 36 && channel <= 64) { tmp = 0x200; band = RF_BAND_5G_L; } else if (channel >= 100 && channel <= 144) { tmp = 0x280; band = RF_BAND_5G_M; } else if (channel >= 149 && channel <= 177) { tmp = 0x300; band = RF_BAND_5G_H; } else { rtw_err(rtwdev, "[TXGAPK] unknown channel %d!!\n", channel); return; } for (path = 0; path < rtwdev->hal.rf_path_num; path++) { for (i = 0; i < RF_GAIN_NUM; i++) { offset_tmp[i] = 0; for (j = i; j < RF_GAIN_NUM; j++) { v = txgapk->rf3f_bp[band][j][path]; if (_rtw8822c_txgapk_gain_valid(rtwdev, v)) continue; offset_tmp[i] += txgapk->offset[j][path]; txgapk->fianl_offset[i][path] = offset_tmp[i]; } v = txgapk->rf3f_bp[band][i][path]; if (_rtw8822c_txgapk_gain_valid(rtwdev, v)) { rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] tx_gain=0x%03X >= 0xCEX\n", txgapk->rf3f_bp[band][i][path]); } else { txgapk->rf3f_fs[path][i] = offset_tmp[i]; rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] offset %d %d\n", offset_tmp[i], i); } } rtw_write_rf(rtwdev, path, RF_LUTWE2, RFREG_MASK, 0x10000); for (i = 0; i < RF_GAIN_NUM; i++) { rtw_write_rf(rtwdev, path, RF_LUTWA, RFREG_MASK, tmp + i); tmp_3f = rtw8822c_txgapk_cal_gain(rtwdev, txgapk->rf3f_bp[band][i][path], offset_tmp[i]); rtw_write_rf(rtwdev, path, RF_LUTWD0, BIT_GAIN_EXT | BIT_DATA_L, tmp_3f); rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] 0x33=0x%05X 0x3f=0x%04X\n", tmp + i, tmp_3f); } rtw_write_rf(rtwdev, path, RF_LUTWE2, RFREG_MASK, 0x0); } } static void rtw8822c_txgapk_save_all_tx_gain_table(struct rtw_dev *rtwdev) { struct rtw_gapk_info *txgapk = &rtwdev->dm_info.gapk; static const u32 three_wire[2] = {REG_3WIRE, REG_3WIRE2}; static const u8 ch_num[RF_BAND_MAX] = {1, 1, 36, 100, 149}; static const u8 band_num[RF_BAND_MAX] = {0x0, 0x0, 0x1, 0x3, 0x5}; static const u8 cck[RF_BAND_MAX] = {0x1, 0x0, 0x0, 0x0, 0x0}; u8 path, band, gain, rf0_idx; u32 rf18, v; if (rtwdev->dm_info.dm_flags & BIT(RTW_DM_CAP_TXGAPK)) return; rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] ======>%s\n", __func__); if (txgapk->read_txgain == 1) { rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] Already Read txgapk->read_txgain return!!!\n"); rtw8822c_txgapk_write_gain_bb_table(rtwdev); return; } for (band = 0; band < RF_BAND_MAX; band++) { for (path = 0; path < rtwdev->hal.rf_path_num; path++) { rf18 = rtw_read_rf(rtwdev, path, RF_CFGCH, RFREG_MASK); rtw_write32_mask(rtwdev, three_wire[path], BIT_3WIRE_EN, 0x0); rtw_write_rf(rtwdev, path, RF_CFGCH, MASKBYTE0, ch_num[band]); rtw_write_rf(rtwdev, path, RF_CFGCH, BIT_BAND, band_num[band]); rtw_write_rf(rtwdev, path, RF_BW_TRXBB, BIT_DBG_CCK_CCA, cck[band]); rtw_write_rf(rtwdev, path, RF_BW_TRXBB, BIT_TX_CCK_IND, cck[band]); gain = 0; for (rf0_idx = 1; rf0_idx < 32; rf0_idx += 3) { rtw_write_rf(rtwdev, path, RF_MODE_TRXAGC, MASKBYTE0, rf0_idx); v = rtw_read_rf(rtwdev, path, RF_TX_RESULT, RFREG_MASK); txgapk->rf3f_bp[band][gain][path] = v & BIT_DATA_L; rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] 0x5f=0x%03X band=%d path=%d\n", txgapk->rf3f_bp[band][gain][path], band, path); gain++; } rtw_write_rf(rtwdev, path, RF_CFGCH, RFREG_MASK, rf18); rtw_write32_mask(rtwdev, three_wire[path], BIT_3WIRE_EN, 0x3); } } rtw8822c_txgapk_write_gain_bb_table(rtwdev); txgapk->read_txgain = 1; } static void rtw8822c_txgapk(struct rtw_dev *rtwdev) { static const u32 bb_reg[2] = {REG_TX_PTCL_CTRL, REG_TX_FIFO}; struct rtw_gapk_info *txgapk = &rtwdev->dm_info.gapk; u32 bb_reg_backup[2]; u8 path; rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] ======>%s\n", __func__); rtw8822c_txgapk_save_all_tx_gain_table(rtwdev); if (txgapk->read_txgain == 0) { rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] txgapk->read_txgain == 0 return!!!\n"); return; } if (rtwdev->efuse.power_track_type >= 4 && rtwdev->efuse.power_track_type <= 7) { rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] Normal Mode in TSSI mode. return!!!\n"); return; } rtw8822c_txgapk_backup_bb_reg(rtwdev, bb_reg, bb_reg_backup, ARRAY_SIZE(bb_reg)); rtw8822c_txgapk_tx_pause(rtwdev); for (path = 0; path < rtwdev->hal.rf_path_num; path++) { txgapk->channel = rtw_read_rf(rtwdev, path, RF_CFGCH, RFREG_MASK) & MASKBYTE0; rtw8822c_txgapk_bb_dpk(rtwdev, path); rtw8822c_txgapk_afe_dpk(rtwdev, path); rtw8822c_txgapk_calculate_offset(rtwdev, path); rtw8822c_txgapk_rf_restore(rtwdev, path); rtw8822c_txgapk_afe_dpk_restore(rtwdev, path); rtw8822c_txgapk_bb_dpk_restore(rtwdev, path); } rtw8822c_txgapk_write_tx_gain(rtwdev); rtw8822c_txgapk_reload_bb_reg(rtwdev, bb_reg, bb_reg_backup, ARRAY_SIZE(bb_reg)); } static void rtw8822c_do_gapk(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm = &rtwdev->dm_info; if (dm->dm_flags & BIT(RTW_DM_CAP_TXGAPK)) { rtw_dbg(rtwdev, RTW_DBG_RFK, "[TXGAPK] feature disable!!!\n"); return; } rtw8822c_rfk_handshake(rtwdev, true); rtw8822c_txgapk(rtwdev); rtw8822c_rfk_handshake(rtwdev, false); } static void rtw8822c_rf_init(struct rtw_dev *rtwdev) { rtw8822c_rf_dac_cal(rtwdev); rtw8822c_rf_x2_check(rtwdev); rtw8822c_thermal_trim(rtwdev); rtw8822c_power_trim(rtwdev); rtw8822c_pa_bias(rtwdev); } static void rtw8822c_pwrtrack_init(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 path; for (path = RF_PATH_A; path < RTW_RF_PATH_MAX; path++) { dm_info->delta_power_index[path] = 0; ewma_thermal_init(&dm_info->avg_thermal[path]); dm_info->thermal_avg[path] = 0xff; } dm_info->pwr_trk_triggered = false; dm_info->thermal_meter_k = rtwdev->efuse.thermal_meter_k; dm_info->thermal_meter_lck = rtwdev->efuse.thermal_meter_k; } static void rtw8822c_phy_set_param(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; struct rtw_hal *hal = &rtwdev->hal; u8 crystal_cap; u8 cck_gi_u_bnd_msb = 0; u8 cck_gi_u_bnd_lsb = 0; u8 cck_gi_l_bnd_msb = 0; u8 cck_gi_l_bnd_lsb = 0; bool is_tx2_path; /* power on BB/RF domain */ rtw_write8_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_BB_GLB_RST | BIT_FEN_BB_RSTB); rtw_write8_set(rtwdev, REG_RF_CTRL, BIT_RF_EN | BIT_RF_RSTB | BIT_RF_SDM_RSTB); rtw_write32_set(rtwdev, REG_WLRF1, BIT_WLRF1_BBRF_EN); /* disable low rate DPD */ rtw_write32_mask(rtwdev, REG_DIS_DPD, DIS_DPD_MASK, DIS_DPD_RATEALL); /* pre init before header files config */ rtw8822c_header_file_init(rtwdev, true); rtw_phy_load_tables(rtwdev); crystal_cap = rtwdev->efuse.crystal_cap & 0x7f; rtw_write32_mask(rtwdev, REG_ANAPAR_XTAL_0, 0xfffc00, crystal_cap | (crystal_cap << 7)); /* post init after header files config */ rtw8822c_header_file_init(rtwdev, false); is_tx2_path = false; rtw8822c_config_trx_mode(rtwdev, hal->antenna_tx, hal->antenna_rx, is_tx2_path); rtw_phy_init(rtwdev); cck_gi_u_bnd_msb = (u8)rtw_read32_mask(rtwdev, 0x1a98, 0xc000); cck_gi_u_bnd_lsb = (u8)rtw_read32_mask(rtwdev, 0x1aa8, 0xf0000); cck_gi_l_bnd_msb = (u8)rtw_read32_mask(rtwdev, 0x1a98, 0xc0); cck_gi_l_bnd_lsb = (u8)rtw_read32_mask(rtwdev, 0x1a70, 0x0f000000); dm_info->cck_gi_u_bnd = ((cck_gi_u_bnd_msb << 4) | (cck_gi_u_bnd_lsb)); dm_info->cck_gi_l_bnd = ((cck_gi_l_bnd_msb << 4) | (cck_gi_l_bnd_lsb)); rtw8822c_rf_init(rtwdev); rtw8822c_pwrtrack_init(rtwdev); rtw_bf_phy_init(rtwdev); } #define WLAN_TXQ_RPT_EN 0x1F #define WLAN_SLOT_TIME 0x09 #define WLAN_PIFS_TIME 0x1C #define WLAN_SIFS_CCK_CONT_TX 0x0A #define WLAN_SIFS_OFDM_CONT_TX 0x0E #define WLAN_SIFS_CCK_TRX 0x0A #define WLAN_SIFS_OFDM_TRX 0x10 #define WLAN_NAV_MAX 0xC8 #define WLAN_RDG_NAV 0x05 #define WLAN_TXOP_NAV 0x1B #define WLAN_CCK_RX_TSF 0x30 #define WLAN_OFDM_RX_TSF 0x30 #define WLAN_TBTT_PROHIBIT 0x04 /* unit : 32us */ #define WLAN_TBTT_HOLD_TIME 0x064 /* unit : 32us */ #define WLAN_DRV_EARLY_INT 0x04 #define WLAN_BCN_CTRL_CLT0 0x10 #define WLAN_BCN_DMA_TIME 0x02 #define WLAN_BCN_MAX_ERR 0xFF #define WLAN_SIFS_CCK_DUR_TUNE 0x0A #define WLAN_SIFS_OFDM_DUR_TUNE 0x10 #define WLAN_SIFS_CCK_CTX 0x0A #define WLAN_SIFS_CCK_IRX 0x0A #define WLAN_SIFS_OFDM_CTX 0x0E #define WLAN_SIFS_OFDM_IRX 0x0E #define WLAN_EIFS_DUR_TUNE 0x40 #define WLAN_EDCA_VO_PARAM 0x002FA226 #define WLAN_EDCA_VI_PARAM 0x005EA328 #define WLAN_EDCA_BE_PARAM 0x005EA42B #define WLAN_EDCA_BK_PARAM 0x0000A44F #define WLAN_RX_FILTER0 0xFFFFFFFF #define WLAN_RX_FILTER2 0xFFFF #define WLAN_RCR_CFG 0xE400220E #define WLAN_RXPKT_MAX_SZ 12288 #define WLAN_RXPKT_MAX_SZ_512 (WLAN_RXPKT_MAX_SZ >> 9) #define WLAN_AMPDU_MAX_TIME 0x70 #define WLAN_RTS_LEN_TH 0xFF #define WLAN_RTS_TX_TIME_TH 0x08 #define WLAN_MAX_AGG_PKT_LIMIT 0x3f #define WLAN_RTS_MAX_AGG_PKT_LIMIT 0x3f #define WLAN_PRE_TXCNT_TIME_TH 0x1E0 #define FAST_EDCA_VO_TH 0x06 #define FAST_EDCA_VI_TH 0x06 #define FAST_EDCA_BE_TH 0x06 #define FAST_EDCA_BK_TH 0x06 #define WLAN_BAR_RETRY_LIMIT 0x01 #define WLAN_BAR_ACK_TYPE 0x05 #define WLAN_RA_TRY_RATE_AGG_LIMIT 0x08 #define WLAN_RESP_TXRATE 0x84 #define WLAN_ACK_TO 0x21 #define WLAN_ACK_TO_CCK 0x6A #define WLAN_DATA_RATE_FB_CNT_1_4 0x01000000 #define WLAN_DATA_RATE_FB_CNT_5_8 0x08070504 #define WLAN_RTS_RATE_FB_CNT_5_8 0x08070504 #define WLAN_DATA_RATE_FB_RATE0 0xFE01F010 #define WLAN_DATA_RATE_FB_RATE0_H 0x40000000 #define WLAN_RTS_RATE_FB_RATE1 0x003FF010 #define WLAN_RTS_RATE_FB_RATE1_H 0x40000000 #define WLAN_RTS_RATE_FB_RATE4 0x0600F010 #define WLAN_RTS_RATE_FB_RATE4_H 0x400003E0 #define WLAN_RTS_RATE_FB_RATE5 0x0600F015 #define WLAN_RTS_RATE_FB_RATE5_H 0x000000E0 #define WLAN_MULTI_ADDR 0xFFFFFFFF #define WLAN_TX_FUNC_CFG1 0x30 #define WLAN_TX_FUNC_CFG2 0x30 #define WLAN_MAC_OPT_NORM_FUNC1 0x98 #define WLAN_MAC_OPT_LB_FUNC1 0x80 #define WLAN_MAC_OPT_FUNC2 0xb0810041 #define WLAN_MAC_INT_MIG_CFG 0x33330000 #define WLAN_SIFS_CFG (WLAN_SIFS_CCK_CONT_TX | \ (WLAN_SIFS_OFDM_CONT_TX << BIT_SHIFT_SIFS_OFDM_CTX) | \ (WLAN_SIFS_CCK_TRX << BIT_SHIFT_SIFS_CCK_TRX) | \ (WLAN_SIFS_OFDM_TRX << BIT_SHIFT_SIFS_OFDM_TRX)) #define WLAN_SIFS_DUR_TUNE (WLAN_SIFS_CCK_DUR_TUNE | \ (WLAN_SIFS_OFDM_DUR_TUNE << 8)) #define WLAN_TBTT_TIME (WLAN_TBTT_PROHIBIT |\ (WLAN_TBTT_HOLD_TIME << BIT_SHIFT_TBTT_HOLD_TIME_AP)) #define WLAN_NAV_CFG (WLAN_RDG_NAV | (WLAN_TXOP_NAV << 16)) #define WLAN_RX_TSF_CFG (WLAN_CCK_RX_TSF | (WLAN_OFDM_RX_TSF) << 8) #define MAC_CLK_SPEED 80 /* 80M */ #define EFUSE_PCB_INFO_OFFSET 0xCA static int rtw8822c_mac_init(struct rtw_dev *rtwdev) { u8 value8; u16 value16; u32 value32; u16 pre_txcnt; /* txq control */ value8 = rtw_read8(rtwdev, REG_FWHW_TXQ_CTRL); value8 |= (BIT(7) & ~BIT(1) & ~BIT(2)); rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL, value8); rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL + 1, WLAN_TXQ_RPT_EN); /* sifs control */ rtw_write16(rtwdev, REG_SPEC_SIFS, WLAN_SIFS_DUR_TUNE); rtw_write32(rtwdev, REG_SIFS, WLAN_SIFS_CFG); rtw_write16(rtwdev, REG_RESP_SIFS_CCK, WLAN_SIFS_CCK_CTX | WLAN_SIFS_CCK_IRX << 8); rtw_write16(rtwdev, REG_RESP_SIFS_OFDM, WLAN_SIFS_OFDM_CTX | WLAN_SIFS_OFDM_IRX << 8); /* rate fallback control */ rtw_write32(rtwdev, REG_DARFRC, WLAN_DATA_RATE_FB_CNT_1_4); rtw_write32(rtwdev, REG_DARFRCH, WLAN_DATA_RATE_FB_CNT_5_8); rtw_write32(rtwdev, REG_RARFRCH, WLAN_RTS_RATE_FB_CNT_5_8); rtw_write32(rtwdev, REG_ARFR0, WLAN_DATA_RATE_FB_RATE0); rtw_write32(rtwdev, REG_ARFRH0, WLAN_DATA_RATE_FB_RATE0_H); rtw_write32(rtwdev, REG_ARFR1_V1, WLAN_RTS_RATE_FB_RATE1); rtw_write32(rtwdev, REG_ARFRH1_V1, WLAN_RTS_RATE_FB_RATE1_H); rtw_write32(rtwdev, REG_ARFR4, WLAN_RTS_RATE_FB_RATE4); rtw_write32(rtwdev, REG_ARFRH4, WLAN_RTS_RATE_FB_RATE4_H); rtw_write32(rtwdev, REG_ARFR5, WLAN_RTS_RATE_FB_RATE5); rtw_write32(rtwdev, REG_ARFRH5, WLAN_RTS_RATE_FB_RATE5_H); /* protocol configuration */ rtw_write8(rtwdev, REG_AMPDU_MAX_TIME_V1, WLAN_AMPDU_MAX_TIME); rtw_write8_set(rtwdev, REG_TX_HANG_CTRL, BIT_EN_EOF_V1); pre_txcnt = WLAN_PRE_TXCNT_TIME_TH | BIT_EN_PRECNT; rtw_write8(rtwdev, REG_PRECNT_CTRL, (u8)(pre_txcnt & 0xFF)); rtw_write8(rtwdev, REG_PRECNT_CTRL + 1, (u8)(pre_txcnt >> 8)); value32 = WLAN_RTS_LEN_TH | (WLAN_RTS_TX_TIME_TH << 8) | (WLAN_MAX_AGG_PKT_LIMIT << 16) | (WLAN_RTS_MAX_AGG_PKT_LIMIT << 24); rtw_write32(rtwdev, REG_PROT_MODE_CTRL, value32); rtw_write16(rtwdev, REG_BAR_MODE_CTRL + 2, WLAN_BAR_RETRY_LIMIT | WLAN_RA_TRY_RATE_AGG_LIMIT << 8); rtw_write8(rtwdev, REG_FAST_EDCA_VOVI_SETTING, FAST_EDCA_VO_TH); rtw_write8(rtwdev, REG_FAST_EDCA_VOVI_SETTING + 2, FAST_EDCA_VI_TH); rtw_write8(rtwdev, REG_FAST_EDCA_BEBK_SETTING, FAST_EDCA_BE_TH); rtw_write8(rtwdev, REG_FAST_EDCA_BEBK_SETTING + 2, FAST_EDCA_BK_TH); /* close BA parser */ rtw_write8_clr(rtwdev, REG_LIFETIME_EN, BIT_BA_PARSER_EN); rtw_write32_clr(rtwdev, REG_RRSR, BITS_RRSR_RSC); /* EDCA configuration */ rtw_write32(rtwdev, REG_EDCA_VO_PARAM, WLAN_EDCA_VO_PARAM); rtw_write32(rtwdev, REG_EDCA_VI_PARAM, WLAN_EDCA_VI_PARAM); rtw_write32(rtwdev, REG_EDCA_BE_PARAM, WLAN_EDCA_BE_PARAM); rtw_write32(rtwdev, REG_EDCA_BK_PARAM, WLAN_EDCA_BK_PARAM); rtw_write8(rtwdev, REG_PIFS, WLAN_PIFS_TIME); rtw_write8_clr(rtwdev, REG_TX_PTCL_CTRL + 1, BIT_SIFS_BK_EN >> 8); rtw_write8_set(rtwdev, REG_RD_CTRL + 1, (BIT_DIS_TXOP_CFE | BIT_DIS_LSIG_CFE | BIT_DIS_STBC_CFE) >> 8); /* MAC clock configuration */ rtw_write32_clr(rtwdev, REG_AFE_CTRL1, BIT_MAC_CLK_SEL); rtw_write8(rtwdev, REG_USTIME_TSF, MAC_CLK_SPEED); rtw_write8(rtwdev, REG_USTIME_EDCA, MAC_CLK_SPEED); rtw_write8_set(rtwdev, REG_MISC_CTRL, BIT_EN_FREE_CNT | BIT_DIS_SECOND_CCA); rtw_write8_clr(rtwdev, REG_TIMER0_SRC_SEL, BIT_TSFT_SEL_TIMER0); rtw_write16(rtwdev, REG_TXPAUSE, 0x0000); rtw_write8(rtwdev, REG_SLOT, WLAN_SLOT_TIME); rtw_write32(rtwdev, REG_RD_NAV_NXT, WLAN_NAV_CFG); rtw_write16(rtwdev, REG_RXTSF_OFFSET_CCK, WLAN_RX_TSF_CFG); /* Set beacon cotnrol - enable TSF and other related functions */ rtw_write8_set(rtwdev, REG_BCN_CTRL, BIT_EN_BCN_FUNCTION); /* Set send beacon related registers */ rtw_write32(rtwdev, REG_TBTT_PROHIBIT, WLAN_TBTT_TIME); rtw_write8(rtwdev, REG_DRVERLYINT, WLAN_DRV_EARLY_INT); rtw_write8(rtwdev, REG_BCN_CTRL_CLINT0, WLAN_BCN_CTRL_CLT0); rtw_write8(rtwdev, REG_BCNDMATIM, WLAN_BCN_DMA_TIME); rtw_write8(rtwdev, REG_BCN_MAX_ERR, WLAN_BCN_MAX_ERR); /* WMAC configuration */ rtw_write32(rtwdev, REG_MAR, WLAN_MULTI_ADDR); rtw_write32(rtwdev, REG_MAR + 4, WLAN_MULTI_ADDR); rtw_write8(rtwdev, REG_BBPSF_CTRL + 2, WLAN_RESP_TXRATE); rtw_write8(rtwdev, REG_ACKTO, WLAN_ACK_TO); rtw_write8(rtwdev, REG_ACKTO_CCK, WLAN_ACK_TO_CCK); rtw_write16(rtwdev, REG_EIFS, WLAN_EIFS_DUR_TUNE); rtw_write8(rtwdev, REG_NAV_CTRL + 2, WLAN_NAV_MAX); rtw_write8(rtwdev, REG_WMAC_TRXPTCL_CTL_H + 2, WLAN_BAR_ACK_TYPE); rtw_write32(rtwdev, REG_RXFLTMAP0, WLAN_RX_FILTER0); rtw_write16(rtwdev, REG_RXFLTMAP2, WLAN_RX_FILTER2); rtw_write32(rtwdev, REG_RCR, WLAN_RCR_CFG); rtw_write8(rtwdev, REG_RX_PKT_LIMIT, WLAN_RXPKT_MAX_SZ_512); rtw_write8(rtwdev, REG_TCR + 2, WLAN_TX_FUNC_CFG2); rtw_write8(rtwdev, REG_TCR + 1, WLAN_TX_FUNC_CFG1); rtw_write32_set(rtwdev, REG_GENERAL_OPTION, BIT_DUMMY_FCS_READY_MASK_EN); rtw_write32(rtwdev, REG_WMAC_OPTION_FUNCTION + 8, WLAN_MAC_OPT_FUNC2); rtw_write8(rtwdev, REG_WMAC_OPTION_FUNCTION_1, WLAN_MAC_OPT_NORM_FUNC1); /* init low power */ value16 = rtw_read16(rtwdev, REG_RXPSF_CTRL + 2) & 0xF00F; value16 |= (BIT_RXGCK_VHT_FIFOTHR(1) | BIT_RXGCK_HT_FIFOTHR(1) | BIT_RXGCK_OFDM_FIFOTHR(1) | BIT_RXGCK_CCK_FIFOTHR(1)) >> 16; rtw_write16(rtwdev, REG_RXPSF_CTRL + 2, value16); value16 = 0; value16 = BIT_SET_RXPSF_PKTLENTHR(value16, 1); value16 |= BIT_RXPSF_CTRLEN | BIT_RXPSF_VHTCHKEN | BIT_RXPSF_HTCHKEN | BIT_RXPSF_OFDMCHKEN | BIT_RXPSF_CCKCHKEN | BIT_RXPSF_OFDMRST; rtw_write16(rtwdev, REG_RXPSF_CTRL, value16); rtw_write32(rtwdev, REG_RXPSF_TYPE_CTRL, 0xFFFFFFFF); /* rx ignore configuration */ value16 = rtw_read16(rtwdev, REG_RXPSF_CTRL); value16 &= ~(BIT_RXPSF_MHCHKEN | BIT_RXPSF_CCKRST | BIT_RXPSF_CONT_ERRCHKEN); value16 = BIT_SET_RXPSF_ERRTHR(value16, 0x07); rtw_write16(rtwdev, REG_RXPSF_CTRL, value16); rtw_write8_set(rtwdev, REG_SND_PTCL_CTRL, BIT_DIS_CHK_VHTSIGB_CRC); /* Interrupt migration configuration */ rtw_write32(rtwdev, REG_INT_MIG, WLAN_MAC_INT_MIG_CFG); return 0; } #define FWCD_SIZE_REG_8822C 0x2000 #define FWCD_SIZE_DMEM_8822C 0x10000 #define FWCD_SIZE_IMEM_8822C 0x10000 #define FWCD_SIZE_EMEM_8822C 0x20000 #define FWCD_SIZE_ROM_8822C 0x10000 static const u32 __fwcd_segs_8822c[] = { FWCD_SIZE_REG_8822C, FWCD_SIZE_DMEM_8822C, FWCD_SIZE_IMEM_8822C, FWCD_SIZE_EMEM_8822C, FWCD_SIZE_ROM_8822C, }; static const struct rtw_fwcd_segs rtw8822c_fwcd_segs = { .segs = __fwcd_segs_8822c, .num = ARRAY_SIZE(__fwcd_segs_8822c), }; static int rtw8822c_dump_fw_crash(struct rtw_dev *rtwdev) { #define __dump_fw_8822c(_dev, _mem) \ rtw_dump_fw(_dev, OCPBASE_ ## _mem ## _88XX, \ FWCD_SIZE_ ## _mem ## _8822C, RTW_FWCD_ ## _mem) int ret; ret = rtw_dump_reg(rtwdev, 0x0, FWCD_SIZE_REG_8822C); if (ret) return ret; ret = __dump_fw_8822c(rtwdev, DMEM); if (ret) return ret; ret = __dump_fw_8822c(rtwdev, IMEM); if (ret) return ret; ret = __dump_fw_8822c(rtwdev, EMEM); if (ret) return ret; ret = __dump_fw_8822c(rtwdev, ROM); if (ret) return ret; return 0; #undef __dump_fw_8822c } static void rtw8822c_rstb_3wire(struct rtw_dev *rtwdev, bool enable) { if (enable) { rtw_write32_mask(rtwdev, REG_RSTB, BIT_RSTB_3WIRE, 0x1); rtw_write32_mask(rtwdev, REG_ANAPAR_A, BIT_ANAPAR_UPDATE, 0x1); rtw_write32_mask(rtwdev, REG_ANAPAR_B, BIT_ANAPAR_UPDATE, 0x1); } else { rtw_write32_mask(rtwdev, REG_RSTB, BIT_RSTB_3WIRE, 0x0); } } static void rtw8822c_set_channel_rf(struct rtw_dev *rtwdev, u8 channel, u8 bw) { #define RF18_BAND_MASK (BIT(16) | BIT(9) | BIT(8)) #define RF18_BAND_2G (0) #define RF18_BAND_5G (BIT(16) | BIT(8)) #define RF18_CHANNEL_MASK (MASKBYTE0) #define RF18_RFSI_MASK (BIT(18) | BIT(17)) #define RF18_RFSI_GE_CH80 (BIT(17)) #define RF18_RFSI_GT_CH140 (BIT(18)) #define RF18_BW_MASK (BIT(13) | BIT(12)) #define RF18_BW_20M (BIT(13) | BIT(12)) #define RF18_BW_40M (BIT(13)) #define RF18_BW_80M (BIT(12)) u32 rf_reg18 = 0; u32 rf_rxbb = 0; +#if defined(__linux__) rf_reg18 = rtw_read_rf(rtwdev, RF_PATH_A, 0x18, RFREG_MASK); +#elif defined(__FreeBSD__) + rf_reg18 = rtw_read_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK); +#endif rf_reg18 &= ~(RF18_BAND_MASK | RF18_CHANNEL_MASK | RF18_RFSI_MASK | RF18_BW_MASK); rf_reg18 |= (IS_CH_2G_BAND(channel) ? RF18_BAND_2G : RF18_BAND_5G); rf_reg18 |= (channel & RF18_CHANNEL_MASK); if (IS_CH_5G_BAND_4(channel)) rf_reg18 |= RF18_RFSI_GT_CH140; else if (IS_CH_5G_BAND_3(channel)) rf_reg18 |= RF18_RFSI_GE_CH80; switch (bw) { case RTW_CHANNEL_WIDTH_5: case RTW_CHANNEL_WIDTH_10: case RTW_CHANNEL_WIDTH_20: default: rf_reg18 |= RF18_BW_20M; rf_rxbb = 0x18; break; case RTW_CHANNEL_WIDTH_40: /* RF bandwidth */ rf_reg18 |= RF18_BW_40M; rf_rxbb = 0x10; break; case RTW_CHANNEL_WIDTH_80: rf_reg18 |= RF18_BW_80M; rf_rxbb = 0x8; break; } rtw8822c_rstb_3wire(rtwdev, false); rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE2, 0x04, 0x01); rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWA, 0x1f, 0x12); rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWD0, 0xfffff, rf_rxbb); rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE2, 0x04, 0x00); rtw_write_rf(rtwdev, RF_PATH_B, RF_LUTWE2, 0x04, 0x01); rtw_write_rf(rtwdev, RF_PATH_B, RF_LUTWA, 0x1f, 0x12); rtw_write_rf(rtwdev, RF_PATH_B, RF_LUTWD0, 0xfffff, rf_rxbb); rtw_write_rf(rtwdev, RF_PATH_B, RF_LUTWE2, 0x04, 0x00); rtw_write_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK, rf_reg18); rtw_write_rf(rtwdev, RF_PATH_B, RF_CFGCH, RFREG_MASK, rf_reg18); rtw8822c_rstb_3wire(rtwdev, true); } static void rtw8822c_toggle_igi(struct rtw_dev *rtwdev) { u32 igi; igi = rtw_read32_mask(rtwdev, REG_RXIGI, 0x7f); rtw_write32_mask(rtwdev, REG_RXIGI, 0x7f, igi - 2); rtw_write32_mask(rtwdev, REG_RXIGI, 0x7f00, igi - 2); rtw_write32_mask(rtwdev, REG_RXIGI, 0x7f, igi); rtw_write32_mask(rtwdev, REG_RXIGI, 0x7f00, igi); } static void rtw8822c_set_channel_bb(struct rtw_dev *rtwdev, u8 channel, u8 bw, u8 primary_ch_idx) { if (IS_CH_2G_BAND(channel)) { rtw_write32_clr(rtwdev, REG_BGCTRL, BITS_RX_IQ_WEIGHT); rtw_write32_set(rtwdev, REG_TXF4, BIT(20)); rtw_write32_clr(rtwdev, REG_CCK_CHECK, BIT_CHECK_CCK_EN); rtw_write32_clr(rtwdev, REG_CCKTXONLY, BIT_BB_CCK_CHECK_EN); rtw_write32_mask(rtwdev, REG_CCAMSK, 0x3F000000, 0xF); switch (bw) { case RTW_CHANNEL_WIDTH_20: rtw_write32_mask(rtwdev, REG_RXAGCCTL0, BITS_RXAGC_CCK, 0x5); rtw_write32_mask(rtwdev, REG_RXAGCCTL, BITS_RXAGC_CCK, 0x5); rtw_write32_mask(rtwdev, REG_RXAGCCTL0, BITS_RXAGC_OFDM, 0x6); rtw_write32_mask(rtwdev, REG_RXAGCCTL, BITS_RXAGC_OFDM, 0x6); break; case RTW_CHANNEL_WIDTH_40: rtw_write32_mask(rtwdev, REG_RXAGCCTL0, BITS_RXAGC_CCK, 0x4); rtw_write32_mask(rtwdev, REG_RXAGCCTL, BITS_RXAGC_CCK, 0x4); rtw_write32_mask(rtwdev, REG_RXAGCCTL0, BITS_RXAGC_OFDM, 0x0); rtw_write32_mask(rtwdev, REG_RXAGCCTL, BITS_RXAGC_OFDM, 0x0); break; } if (channel == 13 || channel == 14) rtw_write32_mask(rtwdev, REG_SCOTRK, 0xfff, 0x969); else if (channel == 11 || channel == 12) rtw_write32_mask(rtwdev, REG_SCOTRK, 0xfff, 0x96a); else rtw_write32_mask(rtwdev, REG_SCOTRK, 0xfff, 0x9aa); if (channel == 14) { rtw_write32_mask(rtwdev, REG_TXF0, MASKHWORD, 0x3da0); rtw_write32_mask(rtwdev, REG_TXF1, MASKDWORD, 0x4962c931); rtw_write32_mask(rtwdev, REG_TXF2, MASKLWORD, 0x6aa3); rtw_write32_mask(rtwdev, REG_TXF3, MASKHWORD, 0xaa7b); rtw_write32_mask(rtwdev, REG_TXF4, MASKLWORD, 0xf3d7); rtw_write32_mask(rtwdev, REG_TXF5, MASKDWORD, 0x0); rtw_write32_mask(rtwdev, REG_TXF6, MASKDWORD, 0xff012455); rtw_write32_mask(rtwdev, REG_TXF7, MASKDWORD, 0xffff); } else { rtw_write32_mask(rtwdev, REG_TXF0, MASKHWORD, 0x5284); rtw_write32_mask(rtwdev, REG_TXF1, MASKDWORD, 0x3e18fec8); rtw_write32_mask(rtwdev, REG_TXF2, MASKLWORD, 0x0a88); rtw_write32_mask(rtwdev, REG_TXF3, MASKHWORD, 0xacc4); rtw_write32_mask(rtwdev, REG_TXF4, MASKLWORD, 0xc8b2); rtw_write32_mask(rtwdev, REG_TXF5, MASKDWORD, 0x00faf0de); rtw_write32_mask(rtwdev, REG_TXF6, MASKDWORD, 0x00122344); rtw_write32_mask(rtwdev, REG_TXF7, MASKDWORD, 0x0fffffff); } if (channel == 13) rtw_write32_mask(rtwdev, REG_TXDFIR0, 0x70, 0x3); else rtw_write32_mask(rtwdev, REG_TXDFIR0, 0x70, 0x1); } else if (IS_CH_5G_BAND(channel)) { rtw_write32_set(rtwdev, REG_CCKTXONLY, BIT_BB_CCK_CHECK_EN); rtw_write32_set(rtwdev, REG_CCK_CHECK, BIT_CHECK_CCK_EN); rtw_write32_set(rtwdev, REG_BGCTRL, BITS_RX_IQ_WEIGHT); rtw_write32_clr(rtwdev, REG_TXF4, BIT(20)); rtw_write32_mask(rtwdev, REG_CCAMSK, 0x3F000000, 0x22); rtw_write32_mask(rtwdev, REG_TXDFIR0, 0x70, 0x3); if (IS_CH_5G_BAND_1(channel) || IS_CH_5G_BAND_2(channel)) { rtw_write32_mask(rtwdev, REG_RXAGCCTL0, BITS_RXAGC_OFDM, 0x1); rtw_write32_mask(rtwdev, REG_RXAGCCTL, BITS_RXAGC_OFDM, 0x1); } else if (IS_CH_5G_BAND_3(channel)) { rtw_write32_mask(rtwdev, REG_RXAGCCTL0, BITS_RXAGC_OFDM, 0x2); rtw_write32_mask(rtwdev, REG_RXAGCCTL, BITS_RXAGC_OFDM, 0x2); } else if (IS_CH_5G_BAND_4(channel)) { rtw_write32_mask(rtwdev, REG_RXAGCCTL0, BITS_RXAGC_OFDM, 0x3); rtw_write32_mask(rtwdev, REG_RXAGCCTL, BITS_RXAGC_OFDM, 0x3); } if (channel >= 36 && channel <= 51) rtw_write32_mask(rtwdev, REG_SCOTRK, 0xfff, 0x494); else if (channel >= 52 && channel <= 55) rtw_write32_mask(rtwdev, REG_SCOTRK, 0xfff, 0x493); else if (channel >= 56 && channel <= 111) rtw_write32_mask(rtwdev, REG_SCOTRK, 0xfff, 0x453); else if (channel >= 112 && channel <= 119) rtw_write32_mask(rtwdev, REG_SCOTRK, 0xfff, 0x452); else if (channel >= 120 && channel <= 172) rtw_write32_mask(rtwdev, REG_SCOTRK, 0xfff, 0x412); else if (channel >= 173 && channel <= 177) rtw_write32_mask(rtwdev, REG_SCOTRK, 0xfff, 0x411); } switch (bw) { case RTW_CHANNEL_WIDTH_20: rtw_write32_mask(rtwdev, REG_DFIRBW, 0x3FF0, 0x19B); rtw_write32_mask(rtwdev, REG_TXBWCTL, 0xf, 0x0); rtw_write32_mask(rtwdev, REG_TXBWCTL, 0xffc0, 0x0); rtw_write32_mask(rtwdev, REG_TXCLK, 0x700, 0x7); rtw_write32_mask(rtwdev, REG_TXCLK, 0x700000, 0x6); rtw_write32_mask(rtwdev, REG_CCK_SOURCE, BIT_NBI_EN, 0x0); rtw_write32_mask(rtwdev, REG_SBD, BITS_SUBTUNE, 0x1); rtw_write32_mask(rtwdev, REG_PT_CHSMO, BIT_PT_OPT, 0x0); break; case RTW_CHANNEL_WIDTH_40: rtw_write32_mask(rtwdev, REG_CCKSB, BIT(4), (primary_ch_idx == RTW_SC_20_UPPER ? 1 : 0)); rtw_write32_mask(rtwdev, REG_TXBWCTL, 0xf, 0x5); rtw_write32_mask(rtwdev, REG_TXBWCTL, 0xc0, 0x0); rtw_write32_mask(rtwdev, REG_TXBWCTL, 0xff00, (primary_ch_idx | (primary_ch_idx << 4))); rtw_write32_mask(rtwdev, REG_CCK_SOURCE, BIT_NBI_EN, 0x1); rtw_write32_mask(rtwdev, REG_SBD, BITS_SUBTUNE, 0x1); rtw_write32_mask(rtwdev, REG_PT_CHSMO, BIT_PT_OPT, 0x1); break; case RTW_CHANNEL_WIDTH_80: rtw_write32_mask(rtwdev, REG_TXBWCTL, 0xf, 0xa); rtw_write32_mask(rtwdev, REG_TXBWCTL, 0xc0, 0x0); rtw_write32_mask(rtwdev, REG_TXBWCTL, 0xff00, (primary_ch_idx | (primary_ch_idx << 4))); rtw_write32_mask(rtwdev, REG_SBD, BITS_SUBTUNE, 0x6); rtw_write32_mask(rtwdev, REG_PT_CHSMO, BIT_PT_OPT, 0x1); break; case RTW_CHANNEL_WIDTH_5: rtw_write32_mask(rtwdev, REG_DFIRBW, 0x3FF0, 0x2AB); rtw_write32_mask(rtwdev, REG_TXBWCTL, 0xf, 0x0); rtw_write32_mask(rtwdev, REG_TXBWCTL, 0xffc0, 0x1); rtw_write32_mask(rtwdev, REG_TXCLK, 0x700, 0x4); rtw_write32_mask(rtwdev, REG_TXCLK, 0x700000, 0x4); rtw_write32_mask(rtwdev, REG_CCK_SOURCE, BIT_NBI_EN, 0x0); rtw_write32_mask(rtwdev, REG_SBD, BITS_SUBTUNE, 0x1); rtw_write32_mask(rtwdev, REG_PT_CHSMO, BIT_PT_OPT, 0x0); break; case RTW_CHANNEL_WIDTH_10: rtw_write32_mask(rtwdev, REG_DFIRBW, 0x3FF0, 0x2AB); rtw_write32_mask(rtwdev, REG_TXBWCTL, 0xf, 0x0); rtw_write32_mask(rtwdev, REG_TXBWCTL, 0xffc0, 0x2); rtw_write32_mask(rtwdev, REG_TXCLK, 0x700, 0x6); rtw_write32_mask(rtwdev, REG_TXCLK, 0x700000, 0x5); rtw_write32_mask(rtwdev, REG_CCK_SOURCE, BIT_NBI_EN, 0x0); rtw_write32_mask(rtwdev, REG_SBD, BITS_SUBTUNE, 0x1); rtw_write32_mask(rtwdev, REG_PT_CHSMO, BIT_PT_OPT, 0x0); break; } } static void rtw8822c_set_channel(struct rtw_dev *rtwdev, u8 channel, u8 bw, u8 primary_chan_idx) { rtw8822c_set_channel_bb(rtwdev, channel, bw, primary_chan_idx); rtw_set_channel_mac(rtwdev, channel, bw, primary_chan_idx); rtw8822c_set_channel_rf(rtwdev, channel, bw); rtw8822c_toggle_igi(rtwdev); } static void rtw8822c_config_cck_rx_path(struct rtw_dev *rtwdev, u8 rx_path) { if (rx_path == BB_PATH_A || rx_path == BB_PATH_B) { rtw_write32_mask(rtwdev, REG_CCANRX, 0x00060000, 0x0); rtw_write32_mask(rtwdev, REG_CCANRX, 0x00600000, 0x0); } else if (rx_path == BB_PATH_AB) { rtw_write32_mask(rtwdev, REG_CCANRX, 0x00600000, 0x1); rtw_write32_mask(rtwdev, REG_CCANRX, 0x00060000, 0x1); } if (rx_path == BB_PATH_A) rtw_write32_mask(rtwdev, REG_RXCCKSEL, 0x0f000000, 0x0); else if (rx_path == BB_PATH_B) rtw_write32_mask(rtwdev, REG_RXCCKSEL, 0x0f000000, 0x5); else if (rx_path == BB_PATH_AB) rtw_write32_mask(rtwdev, REG_RXCCKSEL, 0x0f000000, 0x1); } static void rtw8822c_config_ofdm_rx_path(struct rtw_dev *rtwdev, u8 rx_path) { if (rx_path == BB_PATH_A || rx_path == BB_PATH_B) { rtw_write32_mask(rtwdev, REG_RXFNCTL, 0x300, 0x0); rtw_write32_mask(rtwdev, REG_RXFNCTL, 0x600000, 0x0); rtw_write32_mask(rtwdev, REG_AGCSWSH, BIT(17), 0x0); rtw_write32_mask(rtwdev, REG_ANTWTPD, BIT(20), 0x0); rtw_write32_mask(rtwdev, REG_MRCM, BIT(24), 0x0); } else if (rx_path == BB_PATH_AB) { rtw_write32_mask(rtwdev, REG_RXFNCTL, 0x300, 0x1); rtw_write32_mask(rtwdev, REG_RXFNCTL, 0x600000, 0x1); rtw_write32_mask(rtwdev, REG_AGCSWSH, BIT(17), 0x1); rtw_write32_mask(rtwdev, REG_ANTWTPD, BIT(20), 0x1); rtw_write32_mask(rtwdev, REG_MRCM, BIT(24), 0x1); } rtw_write32_mask(rtwdev, 0x824, 0x0f000000, rx_path); rtw_write32_mask(rtwdev, 0x824, 0x000f0000, rx_path); } static void rtw8822c_config_rx_path(struct rtw_dev *rtwdev, u8 rx_path) { rtw8822c_config_cck_rx_path(rtwdev, rx_path); rtw8822c_config_ofdm_rx_path(rtwdev, rx_path); } static void rtw8822c_config_cck_tx_path(struct rtw_dev *rtwdev, u8 tx_path, bool is_tx2_path) { if (tx_path == BB_PATH_A) { rtw_write32_mask(rtwdev, REG_RXCCKSEL, 0xf0000000, 0x8); } else if (tx_path == BB_PATH_B) { rtw_write32_mask(rtwdev, REG_RXCCKSEL, 0xf0000000, 0x4); } else { if (is_tx2_path) rtw_write32_mask(rtwdev, REG_RXCCKSEL, 0xf0000000, 0xc); else rtw_write32_mask(rtwdev, REG_RXCCKSEL, 0xf0000000, 0x8); } rtw8822c_bb_reset(rtwdev); } static void rtw8822c_config_ofdm_tx_path(struct rtw_dev *rtwdev, u8 tx_path, enum rtw_bb_path tx_path_sel_1ss) { if (tx_path == BB_PATH_A) { rtw_write32_mask(rtwdev, REG_ANTMAP0, 0xff, 0x11); rtw_write32_mask(rtwdev, REG_TXLGMAP, 0xff, 0x0); } else if (tx_path == BB_PATH_B) { rtw_write32_mask(rtwdev, REG_ANTMAP0, 0xff, 0x12); rtw_write32_mask(rtwdev, REG_TXLGMAP, 0xff, 0x0); } else { if (tx_path_sel_1ss == BB_PATH_AB) { rtw_write32_mask(rtwdev, REG_ANTMAP0, 0xff, 0x33); rtw_write32_mask(rtwdev, REG_TXLGMAP, 0xffff, 0x0404); } else if (tx_path_sel_1ss == BB_PATH_B) { rtw_write32_mask(rtwdev, REG_ANTMAP0, 0xff, 0x32); rtw_write32_mask(rtwdev, REG_TXLGMAP, 0xffff, 0x0400); } else if (tx_path_sel_1ss == BB_PATH_A) { rtw_write32_mask(rtwdev, REG_ANTMAP0, 0xff, 0x31); rtw_write32_mask(rtwdev, REG_TXLGMAP, 0xffff, 0x0400); } } rtw8822c_bb_reset(rtwdev); } static void rtw8822c_config_tx_path(struct rtw_dev *rtwdev, u8 tx_path, enum rtw_bb_path tx_path_sel_1ss, enum rtw_bb_path tx_path_cck, bool is_tx2_path) { rtw8822c_config_cck_tx_path(rtwdev, tx_path_cck, is_tx2_path); rtw8822c_config_ofdm_tx_path(rtwdev, tx_path, tx_path_sel_1ss); rtw8822c_bb_reset(rtwdev); } static void rtw8822c_config_trx_mode(struct rtw_dev *rtwdev, u8 tx_path, u8 rx_path, bool is_tx2_path) { if ((tx_path | rx_path) & BB_PATH_A) rtw_write32_mask(rtwdev, REG_ORITXCODE, MASK20BITS, 0x33312); else rtw_write32_mask(rtwdev, REG_ORITXCODE, MASK20BITS, 0x11111); if ((tx_path | rx_path) & BB_PATH_B) rtw_write32_mask(rtwdev, REG_ORITXCODE2, MASK20BITS, 0x33312); else rtw_write32_mask(rtwdev, REG_ORITXCODE2, MASK20BITS, 0x11111); rtw8822c_config_rx_path(rtwdev, rx_path); rtw8822c_config_tx_path(rtwdev, tx_path, BB_PATH_A, BB_PATH_A, is_tx2_path); rtw8822c_toggle_igi(rtwdev); } static void query_phy_status_page0(struct rtw_dev *rtwdev, u8 *phy_status, struct rtw_rx_pkt_stat *pkt_stat) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 l_bnd, u_bnd; u8 gain_a, gain_b; s8 rx_power[RTW_RF_PATH_MAX]; s8 min_rx_power = -120; u8 rssi; u8 channel; int path; rx_power[RF_PATH_A] = GET_PHY_STAT_P0_PWDB_A(phy_status); rx_power[RF_PATH_B] = GET_PHY_STAT_P0_PWDB_B(phy_status); l_bnd = dm_info->cck_gi_l_bnd; u_bnd = dm_info->cck_gi_u_bnd; gain_a = GET_PHY_STAT_P0_GAIN_A(phy_status); gain_b = GET_PHY_STAT_P0_GAIN_B(phy_status); if (gain_a < l_bnd) rx_power[RF_PATH_A] += (l_bnd - gain_a) << 1; else if (gain_a > u_bnd) rx_power[RF_PATH_A] -= (gain_a - u_bnd) << 1; if (gain_b < l_bnd) rx_power[RF_PATH_B] += (l_bnd - gain_b) << 1; else if (gain_b > u_bnd) rx_power[RF_PATH_B] -= (gain_b - u_bnd) << 1; rx_power[RF_PATH_A] -= 110; rx_power[RF_PATH_B] -= 110; channel = GET_PHY_STAT_P0_CHANNEL(phy_status); if (channel == 0) channel = rtwdev->hal.current_channel; rtw_set_rx_freq_band(pkt_stat, channel); pkt_stat->rx_power[RF_PATH_A] = rx_power[RF_PATH_A]; pkt_stat->rx_power[RF_PATH_B] = rx_power[RF_PATH_B]; for (path = 0; path <= rtwdev->hal.rf_path_num; path++) { rssi = rtw_phy_rf_power_2_rssi(&pkt_stat->rx_power[path], 1); dm_info->rssi[path] = rssi; } pkt_stat->rssi = rtw_phy_rf_power_2_rssi(pkt_stat->rx_power, 1); pkt_stat->bw = RTW_CHANNEL_WIDTH_20; pkt_stat->signal_power = max(pkt_stat->rx_power[RF_PATH_A], min_rx_power); } static void query_phy_status_page1(struct rtw_dev *rtwdev, u8 *phy_status, struct rtw_rx_pkt_stat *pkt_stat) { struct rtw_path_div *p_div = &rtwdev->dm_path_div; struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 rxsc, bw; s8 min_rx_power = -120; s8 rx_evm; u8 evm_dbm = 0; u8 rssi; int path; u8 channel; if (pkt_stat->rate > DESC_RATE11M && pkt_stat->rate < DESC_RATEMCS0) rxsc = GET_PHY_STAT_P1_L_RXSC(phy_status); else rxsc = GET_PHY_STAT_P1_HT_RXSC(phy_status); if (rxsc >= 9 && rxsc <= 12) bw = RTW_CHANNEL_WIDTH_40; else if (rxsc >= 13) bw = RTW_CHANNEL_WIDTH_80; else bw = RTW_CHANNEL_WIDTH_20; channel = GET_PHY_STAT_P1_CHANNEL(phy_status); rtw_set_rx_freq_band(pkt_stat, channel); pkt_stat->rx_power[RF_PATH_A] = GET_PHY_STAT_P1_PWDB_A(phy_status) - 110; pkt_stat->rx_power[RF_PATH_B] = GET_PHY_STAT_P1_PWDB_B(phy_status) - 110; pkt_stat->rssi = rtw_phy_rf_power_2_rssi(pkt_stat->rx_power, 2); pkt_stat->bw = bw; pkt_stat->signal_power = max3(pkt_stat->rx_power[RF_PATH_A], pkt_stat->rx_power[RF_PATH_B], min_rx_power); dm_info->curr_rx_rate = pkt_stat->rate; pkt_stat->rx_evm[RF_PATH_A] = GET_PHY_STAT_P1_RXEVM_A(phy_status); pkt_stat->rx_evm[RF_PATH_B] = GET_PHY_STAT_P1_RXEVM_B(phy_status); pkt_stat->rx_snr[RF_PATH_A] = GET_PHY_STAT_P1_RXSNR_A(phy_status); pkt_stat->rx_snr[RF_PATH_B] = GET_PHY_STAT_P1_RXSNR_B(phy_status); pkt_stat->cfo_tail[RF_PATH_A] = GET_PHY_STAT_P1_CFO_TAIL_A(phy_status); pkt_stat->cfo_tail[RF_PATH_B] = GET_PHY_STAT_P1_CFO_TAIL_B(phy_status); for (path = 0; path <= rtwdev->hal.rf_path_num; path++) { rssi = rtw_phy_rf_power_2_rssi(&pkt_stat->rx_power[path], 1); dm_info->rssi[path] = rssi; if (path == RF_PATH_A) { p_div->path_a_sum += rssi; p_div->path_a_cnt++; } else if (path == RF_PATH_B) { p_div->path_b_sum += rssi; p_div->path_b_cnt++; } dm_info->rx_snr[path] = pkt_stat->rx_snr[path] >> 1; dm_info->cfo_tail[path] = (pkt_stat->cfo_tail[path] * 5) >> 1; rx_evm = pkt_stat->rx_evm[path]; if (rx_evm < 0) { if (rx_evm == S8_MIN) evm_dbm = 0; else evm_dbm = ((u8)-rx_evm >> 1); } dm_info->rx_evm_dbm[path] = evm_dbm; } rtw_phy_parsing_cfo(rtwdev, pkt_stat); } static void query_phy_status(struct rtw_dev *rtwdev, u8 *phy_status, struct rtw_rx_pkt_stat *pkt_stat) { u8 page; page = *phy_status & 0xf; switch (page) { case 0: query_phy_status_page0(rtwdev, phy_status, pkt_stat); break; case 1: query_phy_status_page1(rtwdev, phy_status, pkt_stat); break; default: rtw_warn(rtwdev, "unused phy status page (%d)\n", page); return; } } static void rtw8822c_query_rx_desc(struct rtw_dev *rtwdev, u8 *rx_desc, struct rtw_rx_pkt_stat *pkt_stat, struct ieee80211_rx_status *rx_status) { struct ieee80211_hdr *hdr; u32 desc_sz = rtwdev->chip->rx_pkt_desc_sz; u8 *phy_status = NULL; memset(pkt_stat, 0, sizeof(*pkt_stat)); pkt_stat->phy_status = GET_RX_DESC_PHYST(rx_desc); pkt_stat->icv_err = GET_RX_DESC_ICV_ERR(rx_desc); pkt_stat->crc_err = GET_RX_DESC_CRC32(rx_desc); pkt_stat->decrypted = !GET_RX_DESC_SWDEC(rx_desc) && GET_RX_DESC_ENC_TYPE(rx_desc) != RX_DESC_ENC_NONE; pkt_stat->is_c2h = GET_RX_DESC_C2H(rx_desc); pkt_stat->pkt_len = GET_RX_DESC_PKT_LEN(rx_desc); pkt_stat->drv_info_sz = GET_RX_DESC_DRV_INFO_SIZE(rx_desc); pkt_stat->shift = GET_RX_DESC_SHIFT(rx_desc); pkt_stat->rate = GET_RX_DESC_RX_RATE(rx_desc); pkt_stat->cam_id = GET_RX_DESC_MACID(rx_desc); pkt_stat->ppdu_cnt = GET_RX_DESC_PPDU_CNT(rx_desc); pkt_stat->tsf_low = GET_RX_DESC_TSFL(rx_desc); /* drv_info_sz is in unit of 8-bytes */ pkt_stat->drv_info_sz *= 8; /* c2h cmd pkt's rx/phy status is not interested */ if (pkt_stat->is_c2h) return; hdr = (struct ieee80211_hdr *)(rx_desc + desc_sz + pkt_stat->shift + pkt_stat->drv_info_sz); pkt_stat->hdr = hdr; if (pkt_stat->phy_status) { phy_status = rx_desc + desc_sz + pkt_stat->shift; query_phy_status(rtwdev, phy_status, pkt_stat); } rtw_rx_fill_rx_status(rtwdev, pkt_stat, hdr, rx_status, phy_status); } static void rtw8822c_set_write_tx_power_ref(struct rtw_dev *rtwdev, u8 *tx_pwr_ref_cck, u8 *tx_pwr_ref_ofdm) { struct rtw_hal *hal = &rtwdev->hal; u32 txref_cck[2] = {0x18a0, 0x41a0}; u32 txref_ofdm[2] = {0x18e8, 0x41e8}; u8 path; for (path = 0; path < hal->rf_path_num; path++) { rtw_write32_mask(rtwdev, 0x1c90, BIT(15), 0); rtw_write32_mask(rtwdev, txref_cck[path], 0x7f0000, tx_pwr_ref_cck[path]); } for (path = 0; path < hal->rf_path_num; path++) { rtw_write32_mask(rtwdev, 0x1c90, BIT(15), 0); rtw_write32_mask(rtwdev, txref_ofdm[path], 0x1fc00, tx_pwr_ref_ofdm[path]); } } static void rtw8822c_set_tx_power_diff(struct rtw_dev *rtwdev, u8 rate, s8 *diff_idx) { u32 offset_txagc = 0x3a00; u8 rate_idx = rate & 0xfc; u8 pwr_idx[4]; u32 phy_pwr_idx; int i; for (i = 0; i < 4; i++) pwr_idx[i] = diff_idx[i] & 0x7f; phy_pwr_idx = pwr_idx[0] | (pwr_idx[1] << 8) | (pwr_idx[2] << 16) | (pwr_idx[3] << 24); rtw_write32_mask(rtwdev, 0x1c90, BIT(15), 0x0); rtw_write32_mask(rtwdev, offset_txagc + rate_idx, MASKDWORD, phy_pwr_idx); } static void rtw8822c_set_tx_power_index(struct rtw_dev *rtwdev) { struct rtw_hal *hal = &rtwdev->hal; u8 rs, rate, j; u8 pwr_ref_cck[2] = {hal->tx_pwr_tbl[RF_PATH_A][DESC_RATE11M], hal->tx_pwr_tbl[RF_PATH_B][DESC_RATE11M]}; u8 pwr_ref_ofdm[2] = {hal->tx_pwr_tbl[RF_PATH_A][DESC_RATEMCS7], hal->tx_pwr_tbl[RF_PATH_B][DESC_RATEMCS7]}; s8 diff_a, diff_b; u8 pwr_a, pwr_b; s8 diff_idx[4]; rtw8822c_set_write_tx_power_ref(rtwdev, pwr_ref_cck, pwr_ref_ofdm); for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++) { for (j = 0; j < rtw_rate_size[rs]; j++) { rate = rtw_rate_section[rs][j]; pwr_a = hal->tx_pwr_tbl[RF_PATH_A][rate]; pwr_b = hal->tx_pwr_tbl[RF_PATH_B][rate]; if (rs == 0) { diff_a = (s8)pwr_a - (s8)pwr_ref_cck[0]; diff_b = (s8)pwr_b - (s8)pwr_ref_cck[1]; } else { diff_a = (s8)pwr_a - (s8)pwr_ref_ofdm[0]; diff_b = (s8)pwr_b - (s8)pwr_ref_ofdm[1]; } diff_idx[rate % 4] = min(diff_a, diff_b); if (rate % 4 == 3) rtw8822c_set_tx_power_diff(rtwdev, rate - 3, diff_idx); } } } static int rtw8822c_set_antenna(struct rtw_dev *rtwdev, u32 antenna_tx, u32 antenna_rx) { struct rtw_hal *hal = &rtwdev->hal; switch (antenna_tx) { case BB_PATH_A: case BB_PATH_B: case BB_PATH_AB: break; default: rtw_warn(rtwdev, "unsupported tx path 0x%x\n", antenna_tx); return -EINVAL; } /* path B only is not available for RX */ switch (antenna_rx) { case BB_PATH_A: case BB_PATH_AB: break; default: rtw_warn(rtwdev, "unsupported rx path 0x%x\n", antenna_rx); return -EINVAL; } hal->antenna_tx = antenna_tx; hal->antenna_rx = antenna_rx; rtw8822c_config_trx_mode(rtwdev, antenna_tx, antenna_rx, false); return 0; } static void rtw8822c_cfg_ldo25(struct rtw_dev *rtwdev, bool enable) { u8 ldo_pwr; ldo_pwr = rtw_read8(rtwdev, REG_ANAPARLDO_POW_MAC); ldo_pwr = enable ? ldo_pwr | BIT_LDOE25_PON : ldo_pwr & ~BIT_LDOE25_PON; rtw_write8(rtwdev, REG_ANAPARLDO_POW_MAC, ldo_pwr); } static void rtw8822c_false_alarm_statistics(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u32 cck_enable; u32 cck_fa_cnt; u32 crc32_cnt; u32 cca32_cnt; u32 ofdm_fa_cnt; u32 ofdm_fa_cnt1, ofdm_fa_cnt2, ofdm_fa_cnt3, ofdm_fa_cnt4, ofdm_fa_cnt5; u16 parity_fail, rate_illegal, crc8_fail, mcs_fail, sb_search_fail, fast_fsync, crc8_fail_vhta, mcs_fail_vht; cck_enable = rtw_read32(rtwdev, REG_ENCCK) & BIT_CCK_BLK_EN; cck_fa_cnt = rtw_read16(rtwdev, REG_CCK_FACNT); ofdm_fa_cnt1 = rtw_read32(rtwdev, REG_OFDM_FACNT1); ofdm_fa_cnt2 = rtw_read32(rtwdev, REG_OFDM_FACNT2); ofdm_fa_cnt3 = rtw_read32(rtwdev, REG_OFDM_FACNT3); ofdm_fa_cnt4 = rtw_read32(rtwdev, REG_OFDM_FACNT4); ofdm_fa_cnt5 = rtw_read32(rtwdev, REG_OFDM_FACNT5); parity_fail = FIELD_GET(GENMASK(31, 16), ofdm_fa_cnt1); rate_illegal = FIELD_GET(GENMASK(15, 0), ofdm_fa_cnt2); crc8_fail = FIELD_GET(GENMASK(31, 16), ofdm_fa_cnt2); crc8_fail_vhta = FIELD_GET(GENMASK(15, 0), ofdm_fa_cnt3); mcs_fail = FIELD_GET(GENMASK(15, 0), ofdm_fa_cnt4); mcs_fail_vht = FIELD_GET(GENMASK(31, 16), ofdm_fa_cnt4); fast_fsync = FIELD_GET(GENMASK(15, 0), ofdm_fa_cnt5); sb_search_fail = FIELD_GET(GENMASK(31, 16), ofdm_fa_cnt5); ofdm_fa_cnt = parity_fail + rate_illegal + crc8_fail + crc8_fail_vhta + mcs_fail + mcs_fail_vht + fast_fsync + sb_search_fail; dm_info->cck_fa_cnt = cck_fa_cnt; dm_info->ofdm_fa_cnt = ofdm_fa_cnt; dm_info->total_fa_cnt = ofdm_fa_cnt; dm_info->total_fa_cnt += cck_enable ? cck_fa_cnt : 0; crc32_cnt = rtw_read32(rtwdev, 0x2c04); dm_info->cck_ok_cnt = crc32_cnt & 0xffff; dm_info->cck_err_cnt = (crc32_cnt & 0xffff0000) >> 16; crc32_cnt = rtw_read32(rtwdev, 0x2c14); dm_info->ofdm_ok_cnt = crc32_cnt & 0xffff; dm_info->ofdm_err_cnt = (crc32_cnt & 0xffff0000) >> 16; crc32_cnt = rtw_read32(rtwdev, 0x2c10); dm_info->ht_ok_cnt = crc32_cnt & 0xffff; dm_info->ht_err_cnt = (crc32_cnt & 0xffff0000) >> 16; crc32_cnt = rtw_read32(rtwdev, 0x2c0c); dm_info->vht_ok_cnt = crc32_cnt & 0xffff; dm_info->vht_err_cnt = (crc32_cnt & 0xffff0000) >> 16; cca32_cnt = rtw_read32(rtwdev, 0x2c08); dm_info->ofdm_cca_cnt = ((cca32_cnt & 0xffff0000) >> 16); dm_info->cck_cca_cnt = cca32_cnt & 0xffff; dm_info->total_cca_cnt = dm_info->ofdm_cca_cnt; if (cck_enable) dm_info->total_cca_cnt += dm_info->cck_cca_cnt; rtw_write32_mask(rtwdev, REG_CCANRX, BIT_CCK_FA_RST, 0); rtw_write32_mask(rtwdev, REG_CCANRX, BIT_CCK_FA_RST, 2); rtw_write32_mask(rtwdev, REG_CCANRX, BIT_OFDM_FA_RST, 0); rtw_write32_mask(rtwdev, REG_CCANRX, BIT_OFDM_FA_RST, 2); /* disable rx clk gating to reset counters */ rtw_write32_clr(rtwdev, REG_RX_BREAK, BIT_COM_RX_GCK_EN); rtw_write32_set(rtwdev, REG_CNT_CTRL, BIT_ALL_CNT_RST); rtw_write32_clr(rtwdev, REG_CNT_CTRL, BIT_ALL_CNT_RST); rtw_write32_set(rtwdev, REG_RX_BREAK, BIT_COM_RX_GCK_EN); } static void rtw8822c_do_lck(struct rtw_dev *rtwdev) { u32 val; rtw_write_rf(rtwdev, RF_PATH_A, RF_SYN_CTRL, RFREG_MASK, 0x80010); rtw_write_rf(rtwdev, RF_PATH_A, RF_SYN_PFD, RFREG_MASK, 0x1F0FA); fsleep(1); rtw_write_rf(rtwdev, RF_PATH_A, RF_AAC_CTRL, RFREG_MASK, 0x80000); rtw_write_rf(rtwdev, RF_PATH_A, RF_SYN_AAC, RFREG_MASK, 0x80001); read_poll_timeout(rtw_read_rf, val, val != 0x1, 1000, 100000, true, rtwdev, RF_PATH_A, RF_AAC_CTRL, 0x1000); rtw_write_rf(rtwdev, RF_PATH_A, RF_SYN_PFD, RFREG_MASK, 0x1F0F8); rtw_write_rf(rtwdev, RF_PATH_B, RF_SYN_CTRL, RFREG_MASK, 0x80010); rtw_write_rf(rtwdev, RF_PATH_A, RF_FAST_LCK, RFREG_MASK, 0x0f000); rtw_write_rf(rtwdev, RF_PATH_A, RF_FAST_LCK, RFREG_MASK, 0x4f000); fsleep(1); rtw_write_rf(rtwdev, RF_PATH_A, RF_FAST_LCK, RFREG_MASK, 0x0f000); } static void rtw8822c_do_iqk(struct rtw_dev *rtwdev) { struct rtw_iqk_para para = {0}; u8 iqk_chk; int ret; para.clear = 1; rtw_fw_do_iqk(rtwdev, ¶); ret = read_poll_timeout(rtw_read8, iqk_chk, iqk_chk == IQK_DONE_8822C, 20000, 300000, false, rtwdev, REG_RPT_CIP); if (ret) rtw_warn(rtwdev, "failed to poll iqk status bit\n"); rtw_write8(rtwdev, REG_IQKSTAT, 0x0); } /* for coex */ static void rtw8822c_coex_cfg_init(struct rtw_dev *rtwdev) { /* enable TBTT nterrupt */ rtw_write8_set(rtwdev, REG_BCN_CTRL, BIT_EN_BCN_FUNCTION); /* BT report packet sample rate */ /* 0x790[5:0]=0x5 */ rtw_write8_mask(rtwdev, REG_BT_TDMA_TIME, BIT_MASK_SAMPLE_RATE, 0x5); /* enable BT counter statistics */ rtw_write8(rtwdev, REG_BT_STAT_CTRL, 0x1); /* enable PTA (3-wire function form BT side) */ rtw_write32_set(rtwdev, REG_GPIO_MUXCFG, BIT_BT_PTA_EN); rtw_write32_set(rtwdev, REG_GPIO_MUXCFG, BIT_PO_BT_PTA_PINS); /* enable PTA (tx/rx signal form WiFi side) */ rtw_write8_set(rtwdev, REG_QUEUE_CTRL, BIT_PTA_WL_TX_EN); /* wl tx signal to PTA not case EDCCA */ rtw_write8_clr(rtwdev, REG_QUEUE_CTRL, BIT_PTA_EDCCA_EN); /* GNT_BT=1 while select both */ rtw_write16_set(rtwdev, REG_BT_COEX_V2, BIT_GNT_BT_POLARITY); /* BT_CCA = ~GNT_WL_BB, not or GNT_BT_BB, LTE_Rx */ rtw_write8_clr(rtwdev, REG_DUMMY_PAGE4_V1, BIT_BTCCA_CTRL); /* to avoid RF parameter error */ rtw_write_rf(rtwdev, RF_PATH_B, RF_MODOPT, 0xfffff, 0x40000); } static void rtw8822c_coex_cfg_gnt_fix(struct rtw_dev *rtwdev) { struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_stat *coex_stat = &coex->stat; struct rtw_efuse *efuse = &rtwdev->efuse; u32 rf_0x1; if (coex_stat->gnt_workaround_state == coex_stat->wl_coex_mode) return; coex_stat->gnt_workaround_state = coex_stat->wl_coex_mode; if ((coex_stat->kt_ver == 0 && coex->under_5g) || coex->freerun) rf_0x1 = 0x40021; else rf_0x1 = 0x40000; /* BT at S1 for Shared-Ant */ if (efuse->share_ant) rf_0x1 |= BIT(13); rtw_write_rf(rtwdev, RF_PATH_B, 0x1, 0xfffff, rf_0x1); /* WL-S0 2G RF TRX cannot be masked by GNT_BT * enable "WLS0 BB chage RF mode if GNT_BT = 1" for shared-antenna type * disable:0x1860[3] = 1, enable:0x1860[3] = 0 * * enable "DAC off if GNT_WL = 0" for non-shared-antenna * disable 0x1c30[22] = 0, * enable: 0x1c30[22] = 1, 0x1c38[12] = 0, 0x1c38[28] = 1 */ if (coex_stat->wl_coex_mode == COEX_WLINK_2GFREE) { rtw_write8_mask(rtwdev, REG_ANAPAR + 2, BIT_ANAPAR_BTPS >> 16, 0); } else { rtw_write8_mask(rtwdev, REG_ANAPAR + 2, BIT_ANAPAR_BTPS >> 16, 1); rtw_write8_mask(rtwdev, REG_RSTB_SEL + 1, BIT_DAC_OFF_ENABLE, 0); rtw_write8_mask(rtwdev, REG_RSTB_SEL + 3, BIT_DAC_OFF_ENABLE, 1); } /* disable WL-S1 BB chage RF mode if GNT_BT * since RF TRx mask can do it */ rtw_write8_mask(rtwdev, REG_IGN_GNTBT4, BIT_PI_IGNORE_GNT_BT, 1); /* disable WL-S0 BB chage RF mode if wifi is at 5G, * or antenna path is separated */ if (coex_stat->wl_coex_mode == COEX_WLINK_2GFREE) { rtw_write8_mask(rtwdev, REG_IGN_GNT_BT1, BIT_PI_IGNORE_GNT_BT, 1); rtw_write8_mask(rtwdev, REG_NOMASK_TXBT, BIT_NOMASK_TXBT_ENABLE, 1); } else if (coex_stat->wl_coex_mode == COEX_WLINK_5G || coex->under_5g || !efuse->share_ant) { if (coex_stat->kt_ver >= 3) { rtw_write8_mask(rtwdev, REG_IGN_GNT_BT1, BIT_PI_IGNORE_GNT_BT, 0); rtw_write8_mask(rtwdev, REG_NOMASK_TXBT, BIT_NOMASK_TXBT_ENABLE, 1); } else { rtw_write8_mask(rtwdev, REG_IGN_GNT_BT1, BIT_PI_IGNORE_GNT_BT, 1); } } else { /* shared-antenna */ rtw_write8_mask(rtwdev, REG_IGN_GNT_BT1, BIT_PI_IGNORE_GNT_BT, 0); if (coex_stat->kt_ver >= 3) { rtw_write8_mask(rtwdev, REG_NOMASK_TXBT, BIT_NOMASK_TXBT_ENABLE, 0); } } } static void rtw8822c_coex_cfg_gnt_debug(struct rtw_dev *rtwdev) { rtw_write8_mask(rtwdev, REG_PAD_CTRL1 + 2, BIT_BTGP_SPI_EN >> 16, 0); rtw_write8_mask(rtwdev, REG_PAD_CTRL1 + 3, BIT_BTGP_JTAG_EN >> 24, 0); rtw_write8_mask(rtwdev, REG_GPIO_MUXCFG + 2, BIT_FSPI_EN >> 16, 0); rtw_write8_mask(rtwdev, REG_PAD_CTRL1 + 1, BIT_LED1DIS >> 8, 0); rtw_write8_mask(rtwdev, REG_SYS_SDIO_CTRL + 3, BIT_DBG_GNT_WL_BT >> 24, 0); } static void rtw8822c_coex_cfg_rfe_type(struct rtw_dev *rtwdev) { struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_rfe *coex_rfe = &coex->rfe; struct rtw_efuse *efuse = &rtwdev->efuse; coex_rfe->rfe_module_type = rtwdev->efuse.rfe_option; coex_rfe->ant_switch_polarity = 0; coex_rfe->ant_switch_exist = false; coex_rfe->ant_switch_with_bt = false; coex_rfe->ant_switch_diversity = false; if (efuse->share_ant) coex_rfe->wlg_at_btg = true; else coex_rfe->wlg_at_btg = false; /* disable LTE coex in wifi side */ rtw_coex_write_indirect_reg(rtwdev, LTE_COEX_CTRL, BIT_LTE_COEX_EN, 0x0); rtw_coex_write_indirect_reg(rtwdev, LTE_WL_TRX_CTRL, MASKLWORD, 0xffff); rtw_coex_write_indirect_reg(rtwdev, LTE_BT_TRX_CTRL, MASKLWORD, 0xffff); } static void rtw8822c_coex_cfg_wl_tx_power(struct rtw_dev *rtwdev, u8 wl_pwr) { struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_dm *coex_dm = &coex->dm; if (wl_pwr == coex_dm->cur_wl_pwr_lvl) return; coex_dm->cur_wl_pwr_lvl = wl_pwr; } static void rtw8822c_coex_cfg_wl_rx_gain(struct rtw_dev *rtwdev, bool low_gain) { struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_dm *coex_dm = &coex->dm; if (low_gain == coex_dm->cur_wl_rx_low_gain_en) return; coex_dm->cur_wl_rx_low_gain_en = low_gain; if (coex_dm->cur_wl_rx_low_gain_en) { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], Hi-Li Table On!\n"); /* set Rx filter corner RCK offset */ rtw_write_rf(rtwdev, RF_PATH_A, RF_RCKD, RFREG_MASK, 0x22); rtw_write_rf(rtwdev, RF_PATH_A, RF_RCK, RFREG_MASK, 0x36); rtw_write_rf(rtwdev, RF_PATH_B, RF_RCKD, RFREG_MASK, 0x22); rtw_write_rf(rtwdev, RF_PATH_B, RF_RCK, RFREG_MASK, 0x36); } else { rtw_dbg(rtwdev, RTW_DBG_COEX, "[BTCoex], Hi-Li Table Off!\n"); /* set Rx filter corner RCK offset */ rtw_write_rf(rtwdev, RF_PATH_A, RF_RCKD, RFREG_MASK, 0x20); rtw_write_rf(rtwdev, RF_PATH_A, RF_RCK, RFREG_MASK, 0x0); rtw_write_rf(rtwdev, RF_PATH_B, RF_RCKD, RFREG_MASK, 0x20); rtw_write_rf(rtwdev, RF_PATH_B, RF_RCK, RFREG_MASK, 0x0); } } static void rtw8822c_bf_enable_bfee_su(struct rtw_dev *rtwdev, struct rtw_vif *vif, struct rtw_bfee *bfee) { u8 csi_rsc = 0; u32 tmp6dc; rtw_bf_enable_bfee_su(rtwdev, vif, bfee); tmp6dc = rtw_read32(rtwdev, REG_BBPSF_CTRL) | BIT_WMAC_USE_NDPARATE | (csi_rsc << 13); if (vif->net_type == RTW_NET_AP_MODE) rtw_write32(rtwdev, REG_BBPSF_CTRL, tmp6dc | BIT(12)); else rtw_write32(rtwdev, REG_BBPSF_CTRL, tmp6dc & ~BIT(12)); rtw_write32(rtwdev, REG_CSI_RRSR, 0x550); } static void rtw8822c_bf_config_bfee_su(struct rtw_dev *rtwdev, struct rtw_vif *vif, struct rtw_bfee *bfee, bool enable) { if (enable) rtw8822c_bf_enable_bfee_su(rtwdev, vif, bfee); else rtw_bf_remove_bfee_su(rtwdev, bfee); } static void rtw8822c_bf_config_bfee_mu(struct rtw_dev *rtwdev, struct rtw_vif *vif, struct rtw_bfee *bfee, bool enable) { if (enable) rtw_bf_enable_bfee_mu(rtwdev, vif, bfee); else rtw_bf_remove_bfee_mu(rtwdev, bfee); } static void rtw8822c_bf_config_bfee(struct rtw_dev *rtwdev, struct rtw_vif *vif, struct rtw_bfee *bfee, bool enable) { if (bfee->role == RTW_BFEE_SU) rtw8822c_bf_config_bfee_su(rtwdev, vif, bfee, enable); else if (bfee->role == RTW_BFEE_MU) rtw8822c_bf_config_bfee_mu(rtwdev, vif, bfee, enable); else rtw_warn(rtwdev, "wrong bfee role\n"); } struct dpk_cfg_pair { u32 addr; u32 bitmask; u32 data; }; void rtw8822c_parse_tbl_dpk(struct rtw_dev *rtwdev, const struct rtw_table *tbl) { const struct dpk_cfg_pair *p = tbl->data; const struct dpk_cfg_pair *end = p + tbl->size / 3; BUILD_BUG_ON(sizeof(struct dpk_cfg_pair) != sizeof(u32) * 3); for (; p < end; p++) rtw_write32_mask(rtwdev, p->addr, p->bitmask, p->data); } static void rtw8822c_dpk_set_gnt_wl(struct rtw_dev *rtwdev, bool is_before_k) { struct rtw_dpk_info *dpk_info = &rtwdev->dm_info.dpk_info; if (is_before_k) { dpk_info->gnt_control = rtw_read32(rtwdev, 0x70); dpk_info->gnt_value = rtw_coex_read_indirect_reg(rtwdev, 0x38); rtw_write32_mask(rtwdev, 0x70, BIT(26), 0x1); rtw_coex_write_indirect_reg(rtwdev, 0x38, MASKBYTE1, 0x77); } else { rtw_coex_write_indirect_reg(rtwdev, 0x38, MASKDWORD, dpk_info->gnt_value); rtw_write32(rtwdev, 0x70, dpk_info->gnt_control); } } static void rtw8822c_dpk_restore_registers(struct rtw_dev *rtwdev, u32 reg_num, struct rtw_backup_info *bckp) { rtw_restore_reg(rtwdev, bckp, reg_num); rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SUBPAGE, 0xc); rtw_write32_mask(rtwdev, REG_RXSRAM_CTL, BIT_DPD_CLK, 0x4); } static void rtw8822c_dpk_backup_registers(struct rtw_dev *rtwdev, u32 *reg, u32 reg_num, struct rtw_backup_info *bckp) { u32 i; for (i = 0; i < reg_num; i++) { bckp[i].len = 4; bckp[i].reg = reg[i]; bckp[i].val = rtw_read32(rtwdev, reg[i]); } } static void rtw8822c_dpk_backup_rf_registers(struct rtw_dev *rtwdev, u32 *rf_reg, u32 rf_reg_bak[][2]) { u32 i; for (i = 0; i < DPK_RF_REG_NUM; i++) { rf_reg_bak[i][RF_PATH_A] = rtw_read_rf(rtwdev, RF_PATH_A, rf_reg[i], RFREG_MASK); rf_reg_bak[i][RF_PATH_B] = rtw_read_rf(rtwdev, RF_PATH_B, rf_reg[i], RFREG_MASK); } } static void rtw8822c_dpk_reload_rf_registers(struct rtw_dev *rtwdev, u32 *rf_reg, u32 rf_reg_bak[][2]) { u32 i; for (i = 0; i < DPK_RF_REG_NUM; i++) { rtw_write_rf(rtwdev, RF_PATH_A, rf_reg[i], RFREG_MASK, rf_reg_bak[i][RF_PATH_A]); rtw_write_rf(rtwdev, RF_PATH_B, rf_reg[i], RFREG_MASK, rf_reg_bak[i][RF_PATH_B]); } } static void rtw8822c_dpk_information(struct rtw_dev *rtwdev) { struct rtw_dpk_info *dpk_info = &rtwdev->dm_info.dpk_info; u32 reg; u8 band_shift; +#if defined(__linux__) reg = rtw_read_rf(rtwdev, RF_PATH_A, 0x18, RFREG_MASK); +#elif defined(__FreeBSD__) + reg = rtw_read_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK); +#endif band_shift = FIELD_GET(BIT(16), reg); dpk_info->dpk_band = 1 << band_shift; dpk_info->dpk_ch = FIELD_GET(0xff, reg); dpk_info->dpk_bw = FIELD_GET(0x3000, reg); } static void rtw8822c_dpk_rxbb_dc_cal(struct rtw_dev *rtwdev, u8 path) { rtw_write_rf(rtwdev, path, 0x92, RFREG_MASK, 0x84800); udelay(5); rtw_write_rf(rtwdev, path, 0x92, RFREG_MASK, 0x84801); usleep_range(600, 610); rtw_write_rf(rtwdev, path, 0x92, RFREG_MASK, 0x84800); } static u8 rtw8822c_dpk_dc_corr_check(struct rtw_dev *rtwdev, u8 path) { u16 dc_i, dc_q; u8 corr_idx; rtw_write32(rtwdev, REG_RXSRAM_CTL, 0x000900f0); dc_i = (u16)rtw_read32_mask(rtwdev, REG_STAT_RPT, GENMASK(27, 16)); dc_q = (u16)rtw_read32_mask(rtwdev, REG_STAT_RPT, GENMASK(11, 0)); if (dc_i & BIT(11)) dc_i = 0x1000 - dc_i; if (dc_q & BIT(11)) dc_q = 0x1000 - dc_q; rtw_write32(rtwdev, REG_RXSRAM_CTL, 0x000000f0); corr_idx = (u8)rtw_read32_mask(rtwdev, REG_STAT_RPT, GENMASK(7, 0)); rtw_read32_mask(rtwdev, REG_STAT_RPT, GENMASK(15, 8)); if (dc_i > 200 || dc_q > 200 || corr_idx < 40 || corr_idx > 65) return 1; else return 0; } static void rtw8822c_dpk_tx_pause(struct rtw_dev *rtwdev) { u8 reg_a, reg_b; u16 count = 0; rtw_write8(rtwdev, 0x522, 0xff); rtw_write32_mask(rtwdev, 0x1e70, 0xf, 0x2); do { reg_a = (u8)rtw_read_rf(rtwdev, RF_PATH_A, 0x00, 0xf0000); reg_b = (u8)rtw_read_rf(rtwdev, RF_PATH_B, 0x00, 0xf0000); udelay(2); count++; } while ((reg_a == 2 || reg_b == 2) && count < 2500); } static void rtw8822c_dpk_mac_bb_setting(struct rtw_dev *rtwdev) { rtw8822c_dpk_tx_pause(rtwdev); rtw_load_table(rtwdev, &rtw8822c_dpk_mac_bb_tbl); } static void rtw8822c_dpk_afe_setting(struct rtw_dev *rtwdev, bool is_do_dpk) { if (is_do_dpk) rtw_load_table(rtwdev, &rtw8822c_dpk_afe_is_dpk_tbl); else rtw_load_table(rtwdev, &rtw8822c_dpk_afe_no_dpk_tbl); } static void rtw8822c_dpk_pre_setting(struct rtw_dev *rtwdev) { u8 path; for (path = 0; path < rtwdev->hal.rf_path_num; path++) { rtw_write_rf(rtwdev, path, RF_RXAGC_OFFSET, RFREG_MASK, 0x0); rtw_write32(rtwdev, REG_NCTL0, 0x8 | (path << 1)); if (rtwdev->dm_info.dpk_info.dpk_band == RTW_BAND_2G) rtw_write32(rtwdev, REG_DPD_CTL1_S1, 0x1f100000); else rtw_write32(rtwdev, REG_DPD_CTL1_S1, 0x1f0d0000); rtw_write32_mask(rtwdev, REG_DPD_LUT0, BIT_GLOSS_DB, 0x4); rtw_write32_mask(rtwdev, REG_IQK_CTL1, BIT_TX_CFIR, 0x3); } rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SUBPAGE, 0xc); rtw_write32(rtwdev, REG_DPD_CTL11, 0x3b23170b); rtw_write32(rtwdev, REG_DPD_CTL12, 0x775f5347); } static u32 rtw8822c_dpk_rf_setting(struct rtw_dev *rtwdev, u8 path) { u32 ori_txbb; rtw_write_rf(rtwdev, path, RF_MODE_TRXAGC, RFREG_MASK, 0x50017); ori_txbb = rtw_read_rf(rtwdev, path, RF_TX_GAIN, RFREG_MASK); rtw_write_rf(rtwdev, path, RF_DEBUG, BIT_DE_TX_GAIN, 0x1); rtw_write_rf(rtwdev, path, RF_DEBUG, BIT_DE_PWR_TRIM, 0x1); rtw_write_rf(rtwdev, path, RF_TX_GAIN_OFFSET, BIT_BB_GAIN, 0x0); rtw_write_rf(rtwdev, path, RF_TX_GAIN, RFREG_MASK, ori_txbb); if (rtwdev->dm_info.dpk_info.dpk_band == RTW_BAND_2G) { rtw_write_rf(rtwdev, path, RF_TX_GAIN_OFFSET, BIT_RF_GAIN, 0x1); rtw_write_rf(rtwdev, path, RF_RXG_GAIN, BIT_RXG_GAIN, 0x0); } else { rtw_write_rf(rtwdev, path, RF_TXA_LB_SW, BIT_TXA_LB_ATT, 0x0); rtw_write_rf(rtwdev, path, RF_TXA_LB_SW, BIT_LB_ATT, 0x6); rtw_write_rf(rtwdev, path, RF_TXA_LB_SW, BIT_LB_SW, 0x1); rtw_write_rf(rtwdev, path, RF_RXA_MIX_GAIN, BIT_RXA_MIX_GAIN, 0); } rtw_write_rf(rtwdev, path, RF_MODE_TRXAGC, BIT_RXAGC, 0xf); rtw_write_rf(rtwdev, path, RF_DEBUG, BIT_DE_TRXBW, 0x1); rtw_write_rf(rtwdev, path, RF_BW_TRXBB, BIT_BW_RXBB, 0x0); if (rtwdev->dm_info.dpk_info.dpk_bw == DPK_CHANNEL_WIDTH_80) rtw_write_rf(rtwdev, path, RF_BW_TRXBB, BIT_BW_TXBB, 0x2); else rtw_write_rf(rtwdev, path, RF_BW_TRXBB, BIT_BW_TXBB, 0x1); rtw_write_rf(rtwdev, path, RF_EXT_TIA_BW, BIT(1), 0x1); usleep_range(100, 110); return ori_txbb & 0x1f; } static u16 rtw8822c_dpk_get_cmd(struct rtw_dev *rtwdev, u8 action, u8 path) { u16 cmd; u8 bw = rtwdev->dm_info.dpk_info.dpk_bw == DPK_CHANNEL_WIDTH_80 ? 2 : 0; switch (action) { case RTW_DPK_GAIN_LOSS: cmd = 0x14 + path; break; case RTW_DPK_DO_DPK: cmd = 0x16 + path + bw; break; case RTW_DPK_DPK_ON: cmd = 0x1a + path; break; case RTW_DPK_DAGC: cmd = 0x1c + path + bw; break; default: return 0; } return (cmd << 8) | 0x48; } static u8 rtw8822c_dpk_one_shot(struct rtw_dev *rtwdev, u8 path, u8 action) { u16 dpk_cmd; u8 result = 0; rtw8822c_dpk_set_gnt_wl(rtwdev, true); if (action == RTW_DPK_CAL_PWR) { rtw_write32_mask(rtwdev, REG_DPD_CTL0, BIT(12), 0x1); rtw_write32_mask(rtwdev, REG_DPD_CTL0, BIT(12), 0x0); rtw_write32_mask(rtwdev, REG_RXSRAM_CTL, BIT_RPT_SEL, 0x0); msleep(10); if (!check_hw_ready(rtwdev, REG_STAT_RPT, BIT(31), 0x1)) { result = 1; rtw_dbg(rtwdev, RTW_DBG_RFK, "[DPK] one-shot over 20ms\n"); } } else { rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SUBPAGE, 0x8 | (path << 1)); rtw_write32_mask(rtwdev, REG_R_CONFIG, BIT_IQ_SWITCH, 0x9); dpk_cmd = rtw8822c_dpk_get_cmd(rtwdev, action, path); rtw_write32(rtwdev, REG_NCTL0, dpk_cmd); rtw_write32(rtwdev, REG_NCTL0, dpk_cmd + 1); msleep(10); if (!check_hw_ready(rtwdev, 0x2d9c, 0xff, 0x55)) { result = 1; rtw_dbg(rtwdev, RTW_DBG_RFK, "[DPK] one-shot over 20ms\n"); } rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SUBPAGE, 0x8 | (path << 1)); rtw_write32_mask(rtwdev, REG_R_CONFIG, BIT_IQ_SWITCH, 0x0); } rtw8822c_dpk_set_gnt_wl(rtwdev, false); rtw_write8(rtwdev, 0x1b10, 0x0); return result; } static u16 rtw8822c_dpk_dgain_read(struct rtw_dev *rtwdev, u8 path) { u16 dgain; rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SUBPAGE, 0xc); rtw_write32_mask(rtwdev, REG_RXSRAM_CTL, 0x00ff0000, 0x0); dgain = (u16)rtw_read32_mask(rtwdev, REG_STAT_RPT, GENMASK(27, 16)); return dgain; } static u8 rtw8822c_dpk_thermal_read(struct rtw_dev *rtwdev, u8 path) { rtw_write_rf(rtwdev, path, RF_T_METER, BIT(19), 0x1); rtw_write_rf(rtwdev, path, RF_T_METER, BIT(19), 0x0); rtw_write_rf(rtwdev, path, RF_T_METER, BIT(19), 0x1); udelay(15); return (u8)rtw_read_rf(rtwdev, path, RF_T_METER, 0x0007e); } static u32 rtw8822c_dpk_pas_read(struct rtw_dev *rtwdev, u8 path) { u32 i_val, q_val; rtw_write32(rtwdev, REG_NCTL0, 0x8 | (path << 1)); rtw_write32_mask(rtwdev, 0x1b48, BIT(14), 0x0); rtw_write32(rtwdev, REG_RXSRAM_CTL, 0x00060001); rtw_write32(rtwdev, 0x1b4c, 0x00000000); rtw_write32(rtwdev, 0x1b4c, 0x00080000); q_val = rtw_read32_mask(rtwdev, REG_STAT_RPT, MASKHWORD); i_val = rtw_read32_mask(rtwdev, REG_STAT_RPT, MASKLWORD); if (i_val & BIT(15)) i_val = 0x10000 - i_val; if (q_val & BIT(15)) q_val = 0x10000 - q_val; rtw_write32(rtwdev, 0x1b4c, 0x00000000); return i_val * i_val + q_val * q_val; } static u32 rtw8822c_psd_log2base(u32 val) { u32 tmp, val_integerd_b, tindex; u32 result, val_fractiond_b; u32 table_fraction[21] = {0, 432, 332, 274, 232, 200, 174, 151, 132, 115, 100, 86, 74, 62, 51, 42, 32, 23, 15, 7, 0}; if (val == 0) return 0; val_integerd_b = __fls(val) + 1; tmp = (val * 100) / (1 << val_integerd_b); tindex = tmp / 5; if (tindex >= ARRAY_SIZE(table_fraction)) tindex = ARRAY_SIZE(table_fraction) - 1; val_fractiond_b = table_fraction[tindex]; result = val_integerd_b * 100 - val_fractiond_b; return result; } static u8 rtw8822c_dpk_gainloss_result(struct rtw_dev *rtwdev, u8 path) { u8 result; rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SUBPAGE, 0x8 | (path << 1)); rtw_write32_mask(rtwdev, 0x1b48, BIT(14), 0x1); rtw_write32(rtwdev, REG_RXSRAM_CTL, 0x00060000); result = (u8)rtw_read32_mask(rtwdev, REG_STAT_RPT, 0x000000f0); rtw_write32_mask(rtwdev, 0x1b48, BIT(14), 0x0); return result; } static u8 rtw8822c_dpk_agc_gain_chk(struct rtw_dev *rtwdev, u8 path, u8 limited_pga) { u8 result = 0; u16 dgain; rtw8822c_dpk_one_shot(rtwdev, path, RTW_DPK_DAGC); dgain = rtw8822c_dpk_dgain_read(rtwdev, path); if (dgain > 1535 && !limited_pga) return RTW_DPK_GAIN_LESS; else if (dgain < 768 && !limited_pga) return RTW_DPK_GAIN_LARGE; else return result; } static u8 rtw8822c_dpk_agc_loss_chk(struct rtw_dev *rtwdev, u8 path) { u32 loss, loss_db; loss = rtw8822c_dpk_pas_read(rtwdev, path); if (loss < 0x4000000) return RTW_DPK_GL_LESS; loss_db = 3 * rtw8822c_psd_log2base(loss >> 13) - 3870; if (loss_db > 1000) return RTW_DPK_GL_LARGE; else if (loss_db < 250) return RTW_DPK_GL_LESS; else return RTW_DPK_AGC_OUT; } struct rtw8822c_dpk_data { u8 txbb; u8 pga; u8 limited_pga; u8 agc_cnt; bool loss_only; bool gain_only; u8 path; }; static u8 rtw8822c_gain_check_state(struct rtw_dev *rtwdev, struct rtw8822c_dpk_data *data) { u8 state; data->txbb = (u8)rtw_read_rf(rtwdev, data->path, RF_TX_GAIN, BIT_GAIN_TXBB); data->pga = (u8)rtw_read_rf(rtwdev, data->path, RF_MODE_TRXAGC, BIT_RXAGC); if (data->loss_only) { state = RTW_DPK_LOSS_CHECK; goto check_end; } state = rtw8822c_dpk_agc_gain_chk(rtwdev, data->path, data->limited_pga); if (state == RTW_DPK_GAIN_CHECK && data->gain_only) state = RTW_DPK_AGC_OUT; else if (state == RTW_DPK_GAIN_CHECK) state = RTW_DPK_LOSS_CHECK; check_end: data->agc_cnt++; if (data->agc_cnt >= 6) state = RTW_DPK_AGC_OUT; return state; } static u8 rtw8822c_gain_large_state(struct rtw_dev *rtwdev, struct rtw8822c_dpk_data *data) { u8 pga = data->pga; if (pga > 0xe) rtw_write_rf(rtwdev, data->path, RF_MODE_TRXAGC, BIT_RXAGC, 0xc); else if (pga > 0xb && pga < 0xf) rtw_write_rf(rtwdev, data->path, RF_MODE_TRXAGC, BIT_RXAGC, 0x0); else if (pga < 0xc) data->limited_pga = 1; return RTW_DPK_GAIN_CHECK; } static u8 rtw8822c_gain_less_state(struct rtw_dev *rtwdev, struct rtw8822c_dpk_data *data) { u8 pga = data->pga; if (pga < 0xc) rtw_write_rf(rtwdev, data->path, RF_MODE_TRXAGC, BIT_RXAGC, 0xc); else if (pga > 0xb && pga < 0xf) rtw_write_rf(rtwdev, data->path, RF_MODE_TRXAGC, BIT_RXAGC, 0xf); else if (pga > 0xe) data->limited_pga = 1; return RTW_DPK_GAIN_CHECK; } static u8 rtw8822c_gl_state(struct rtw_dev *rtwdev, struct rtw8822c_dpk_data *data, u8 is_large) { u8 txbb_bound[] = {0x1f, 0}; if (data->txbb == txbb_bound[is_large]) return RTW_DPK_AGC_OUT; if (is_large == 1) data->txbb -= 2; else data->txbb += 3; rtw_write_rf(rtwdev, data->path, RF_TX_GAIN, BIT_GAIN_TXBB, data->txbb); data->limited_pga = 0; return RTW_DPK_GAIN_CHECK; } static u8 rtw8822c_gl_large_state(struct rtw_dev *rtwdev, struct rtw8822c_dpk_data *data) { return rtw8822c_gl_state(rtwdev, data, 1); } static u8 rtw8822c_gl_less_state(struct rtw_dev *rtwdev, struct rtw8822c_dpk_data *data) { return rtw8822c_gl_state(rtwdev, data, 0); } static u8 rtw8822c_loss_check_state(struct rtw_dev *rtwdev, struct rtw8822c_dpk_data *data) { u8 path = data->path; u8 state; rtw8822c_dpk_one_shot(rtwdev, path, RTW_DPK_GAIN_LOSS); state = rtw8822c_dpk_agc_loss_chk(rtwdev, path); return state; } static u8 (*dpk_state[])(struct rtw_dev *rtwdev, struct rtw8822c_dpk_data *data) = { rtw8822c_gain_check_state, rtw8822c_gain_large_state, rtw8822c_gain_less_state, rtw8822c_gl_large_state, rtw8822c_gl_less_state, rtw8822c_loss_check_state }; static u8 rtw8822c_dpk_pas_agc(struct rtw_dev *rtwdev, u8 path, bool gain_only, bool loss_only) { struct rtw8822c_dpk_data data = {0}; u8 (*func)(struct rtw_dev *rtwdev, struct rtw8822c_dpk_data *data); u8 state = RTW_DPK_GAIN_CHECK; data.loss_only = loss_only; data.gain_only = gain_only; data.path = path; for (;;) { func = dpk_state[state]; state = func(rtwdev, &data); if (state == RTW_DPK_AGC_OUT) break; } return data.txbb; } static bool rtw8822c_dpk_coef_iq_check(struct rtw_dev *rtwdev, u16 coef_i, u16 coef_q) { if (coef_i == 0x1000 || coef_i == 0x0fff || coef_q == 0x1000 || coef_q == 0x0fff) return true; return false; } static u32 rtw8822c_dpk_coef_transfer(struct rtw_dev *rtwdev) { u32 reg = 0; u16 coef_i = 0, coef_q = 0; reg = rtw_read32(rtwdev, REG_STAT_RPT); coef_i = (u16)rtw_read32_mask(rtwdev, REG_STAT_RPT, MASKHWORD) & 0x1fff; coef_q = (u16)rtw_read32_mask(rtwdev, REG_STAT_RPT, MASKLWORD) & 0x1fff; coef_q = ((0x2000 - coef_q) & 0x1fff) - 1; reg = (coef_i << 16) | coef_q; return reg; } static const u32 rtw8822c_dpk_get_coef_tbl[] = { 0x000400f0, 0x040400f0, 0x080400f0, 0x010400f0, 0x050400f0, 0x090400f0, 0x020400f0, 0x060400f0, 0x0a0400f0, 0x030400f0, 0x070400f0, 0x0b0400f0, 0x0c0400f0, 0x100400f0, 0x0d0400f0, 0x110400f0, 0x0e0400f0, 0x120400f0, 0x0f0400f0, 0x130400f0, }; static void rtw8822c_dpk_coef_tbl_apply(struct rtw_dev *rtwdev, u8 path) { struct rtw_dpk_info *dpk_info = &rtwdev->dm_info.dpk_info; int i; for (i = 0; i < 20; i++) { rtw_write32(rtwdev, REG_RXSRAM_CTL, rtw8822c_dpk_get_coef_tbl[i]); dpk_info->coef[path][i] = rtw8822c_dpk_coef_transfer(rtwdev); } } static void rtw8822c_dpk_get_coef(struct rtw_dev *rtwdev, u8 path) { rtw_write32(rtwdev, REG_NCTL0, 0x0000000c); if (path == RF_PATH_A) { rtw_write32_mask(rtwdev, REG_DPD_CTL0, BIT(24), 0x0); rtw_write32(rtwdev, REG_DPD_CTL0_S0, 0x30000080); } else if (path == RF_PATH_B) { rtw_write32_mask(rtwdev, REG_DPD_CTL0, BIT(24), 0x1); rtw_write32(rtwdev, REG_DPD_CTL0_S1, 0x30000080); } rtw8822c_dpk_coef_tbl_apply(rtwdev, path); } static u8 rtw8822c_dpk_coef_read(struct rtw_dev *rtwdev, u8 path) { struct rtw_dpk_info *dpk_info = &rtwdev->dm_info.dpk_info; u8 addr, result = 1; u16 coef_i, coef_q; for (addr = 0; addr < 20; addr++) { coef_i = FIELD_GET(0x1fff0000, dpk_info->coef[path][addr]); coef_q = FIELD_GET(0x1fff, dpk_info->coef[path][addr]); if (rtw8822c_dpk_coef_iq_check(rtwdev, coef_i, coef_q)) { result = 0; break; } } return result; } static void rtw8822c_dpk_coef_write(struct rtw_dev *rtwdev, u8 path, u8 result) { struct rtw_dpk_info *dpk_info = &rtwdev->dm_info.dpk_info; u16 reg[DPK_RF_PATH_NUM] = {0x1b0c, 0x1b64}; u32 coef; u8 addr; rtw_write32(rtwdev, REG_NCTL0, 0x0000000c); rtw_write32(rtwdev, REG_RXSRAM_CTL, 0x000000f0); for (addr = 0; addr < 20; addr++) { if (result == 0) { if (addr == 3) coef = 0x04001fff; else coef = 0x00001fff; } else { coef = dpk_info->coef[path][addr]; } rtw_write32(rtwdev, reg[path] + addr * 4, coef); } } static void rtw8822c_dpk_fill_result(struct rtw_dev *rtwdev, u32 dpk_txagc, u8 path, u8 result) { struct rtw_dpk_info *dpk_info = &rtwdev->dm_info.dpk_info; rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SUBPAGE, 0x8 | (path << 1)); if (result) rtw_write8(rtwdev, REG_DPD_AGC, (u8)(dpk_txagc - 6)); else rtw_write8(rtwdev, REG_DPD_AGC, 0x00); dpk_info->result[path] = result; dpk_info->dpk_txagc[path] = rtw_read8(rtwdev, REG_DPD_AGC); rtw8822c_dpk_coef_write(rtwdev, path, result); } static u32 rtw8822c_dpk_gainloss(struct rtw_dev *rtwdev, u8 path) { struct rtw_dpk_info *dpk_info = &rtwdev->dm_info.dpk_info; u8 tx_agc, tx_bb, ori_txbb, ori_txagc, tx_agc_search, t1, t2; ori_txbb = rtw8822c_dpk_rf_setting(rtwdev, path); ori_txagc = (u8)rtw_read_rf(rtwdev, path, RF_MODE_TRXAGC, BIT_TXAGC); rtw8822c_dpk_rxbb_dc_cal(rtwdev, path); rtw8822c_dpk_one_shot(rtwdev, path, RTW_DPK_DAGC); rtw8822c_dpk_dgain_read(rtwdev, path); if (rtw8822c_dpk_dc_corr_check(rtwdev, path)) { rtw8822c_dpk_rxbb_dc_cal(rtwdev, path); rtw8822c_dpk_one_shot(rtwdev, path, RTW_DPK_DAGC); rtw8822c_dpk_dc_corr_check(rtwdev, path); } t1 = rtw8822c_dpk_thermal_read(rtwdev, path); tx_bb = rtw8822c_dpk_pas_agc(rtwdev, path, false, true); tx_agc_search = rtw8822c_dpk_gainloss_result(rtwdev, path); if (tx_bb < tx_agc_search) tx_bb = 0; else tx_bb = tx_bb - tx_agc_search; rtw_write_rf(rtwdev, path, RF_TX_GAIN, BIT_GAIN_TXBB, tx_bb); tx_agc = ori_txagc - (ori_txbb - tx_bb); t2 = rtw8822c_dpk_thermal_read(rtwdev, path); dpk_info->thermal_dpk_delta[path] = abs(t2 - t1); return tx_agc; } static u8 rtw8822c_dpk_by_path(struct rtw_dev *rtwdev, u32 tx_agc, u8 path) { u8 result; result = rtw8822c_dpk_one_shot(rtwdev, path, RTW_DPK_DO_DPK); rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SUBPAGE, 0x8 | (path << 1)); result = result | (u8)rtw_read32_mask(rtwdev, REG_DPD_CTL1_S0, BIT(26)); rtw_write_rf(rtwdev, path, RF_MODE_TRXAGC, RFREG_MASK, 0x33e14); rtw8822c_dpk_get_coef(rtwdev, path); return result; } static void rtw8822c_dpk_cal_gs(struct rtw_dev *rtwdev, u8 path) { struct rtw_dpk_info *dpk_info = &rtwdev->dm_info.dpk_info; u32 tmp_gs = 0; rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SUBPAGE, 0x8 | (path << 1)); rtw_write32_mask(rtwdev, REG_IQK_CTL1, BIT_BYPASS_DPD, 0x0); rtw_write32_mask(rtwdev, REG_IQK_CTL1, BIT_TX_CFIR, 0x0); rtw_write32_mask(rtwdev, REG_R_CONFIG, BIT_IQ_SWITCH, 0x9); rtw_write32_mask(rtwdev, REG_R_CONFIG, BIT_INNER_LB, 0x1); rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SUBPAGE, 0xc); rtw_write32_mask(rtwdev, REG_RXSRAM_CTL, BIT_DPD_CLK, 0xf); if (path == RF_PATH_A) { rtw_write32_mask(rtwdev, REG_DPD_CTL0_S0, BIT_GS_PWSF, 0x1066680); rtw_write32_mask(rtwdev, REG_DPD_CTL1_S0, BIT_DPD_EN, 0x1); } else { rtw_write32_mask(rtwdev, REG_DPD_CTL0_S1, BIT_GS_PWSF, 0x1066680); rtw_write32_mask(rtwdev, REG_DPD_CTL1_S1, BIT_DPD_EN, 0x1); } if (dpk_info->dpk_bw == DPK_CHANNEL_WIDTH_80) { rtw_write32(rtwdev, REG_DPD_CTL16, 0x80001310); rtw_write32(rtwdev, REG_DPD_CTL16, 0x00001310); rtw_write32(rtwdev, REG_DPD_CTL16, 0x810000db); rtw_write32(rtwdev, REG_DPD_CTL16, 0x010000db); rtw_write32(rtwdev, REG_DPD_CTL16, 0x0000b428); rtw_write32(rtwdev, REG_DPD_CTL15, 0x05020000 | (BIT(path) << 28)); } else { rtw_write32(rtwdev, REG_DPD_CTL16, 0x8200190c); rtw_write32(rtwdev, REG_DPD_CTL16, 0x0200190c); rtw_write32(rtwdev, REG_DPD_CTL16, 0x8301ee14); rtw_write32(rtwdev, REG_DPD_CTL16, 0x0301ee14); rtw_write32(rtwdev, REG_DPD_CTL16, 0x0000b428); rtw_write32(rtwdev, REG_DPD_CTL15, 0x05020008 | (BIT(path) << 28)); } rtw_write32_mask(rtwdev, REG_DPD_CTL0, MASKBYTE3, 0x8 | path); rtw8822c_dpk_one_shot(rtwdev, path, RTW_DPK_CAL_PWR); rtw_write32_mask(rtwdev, REG_DPD_CTL15, MASKBYTE3, 0x0); rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SUBPAGE, 0x8 | (path << 1)); rtw_write32_mask(rtwdev, REG_R_CONFIG, BIT_IQ_SWITCH, 0x0); rtw_write32_mask(rtwdev, REG_R_CONFIG, BIT_INNER_LB, 0x0); rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SUBPAGE, 0xc); if (path == RF_PATH_A) rtw_write32_mask(rtwdev, REG_DPD_CTL0_S0, BIT_GS_PWSF, 0x5b); else rtw_write32_mask(rtwdev, REG_DPD_CTL0_S1, BIT_GS_PWSF, 0x5b); rtw_write32_mask(rtwdev, REG_RXSRAM_CTL, BIT_RPT_SEL, 0x0); tmp_gs = (u16)rtw_read32_mask(rtwdev, REG_STAT_RPT, BIT_RPT_DGAIN); tmp_gs = (tmp_gs * 910) >> 10; tmp_gs = DIV_ROUND_CLOSEST(tmp_gs, 10); if (path == RF_PATH_A) rtw_write32_mask(rtwdev, REG_DPD_CTL0_S0, BIT_GS_PWSF, tmp_gs); else rtw_write32_mask(rtwdev, REG_DPD_CTL0_S1, BIT_GS_PWSF, tmp_gs); dpk_info->dpk_gs[path] = tmp_gs; } static void rtw8822c_dpk_cal_coef1(struct rtw_dev *rtwdev) { struct rtw_dpk_info *dpk_info = &rtwdev->dm_info.dpk_info; u32 offset[DPK_RF_PATH_NUM] = {0, 0x58}; u32 i_scaling; u8 path; rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SUBPAGE, 0x0000000c); rtw_write32(rtwdev, REG_RXSRAM_CTL, 0x000000f0); rtw_write32(rtwdev, REG_NCTL0, 0x00001148); rtw_write32(rtwdev, REG_NCTL0, 0x00001149); check_hw_ready(rtwdev, 0x2d9c, MASKBYTE0, 0x55); rtw_write8(rtwdev, 0x1b10, 0x0); rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SUBPAGE, 0x0000000c); for (path = 0; path < rtwdev->hal.rf_path_num; path++) { i_scaling = 0x16c00 / dpk_info->dpk_gs[path]; rtw_write32_mask(rtwdev, 0x1b18 + offset[path], MASKHWORD, i_scaling); rtw_write32_mask(rtwdev, REG_DPD_CTL0_S0 + offset[path], GENMASK(31, 28), 0x9); rtw_write32_mask(rtwdev, REG_DPD_CTL0_S0 + offset[path], GENMASK(31, 28), 0x1); rtw_write32_mask(rtwdev, REG_DPD_CTL0_S0 + offset[path], GENMASK(31, 28), 0x0); rtw_write32_mask(rtwdev, REG_DPD_CTL1_S0 + offset[path], BIT(14), 0x0); } } static void rtw8822c_dpk_on(struct rtw_dev *rtwdev, u8 path) { struct rtw_dpk_info *dpk_info = &rtwdev->dm_info.dpk_info; rtw8822c_dpk_one_shot(rtwdev, path, RTW_DPK_DPK_ON); rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SUBPAGE, 0x8 | (path << 1)); rtw_write32_mask(rtwdev, REG_IQK_CTL1, BIT_TX_CFIR, 0x0); if (test_bit(path, dpk_info->dpk_path_ok)) rtw8822c_dpk_cal_gs(rtwdev, path); } static bool rtw8822c_dpk_check_pass(struct rtw_dev *rtwdev, bool is_fail, u32 dpk_txagc, u8 path) { bool result; if (!is_fail) { if (rtw8822c_dpk_coef_read(rtwdev, path)) result = true; else result = false; } else { result = false; } rtw8822c_dpk_fill_result(rtwdev, dpk_txagc, path, result); return result; } static void rtw8822c_dpk_result_reset(struct rtw_dev *rtwdev) { struct rtw_dpk_info *dpk_info = &rtwdev->dm_info.dpk_info; u8 path; for (path = 0; path < rtwdev->hal.rf_path_num; path++) { clear_bit(path, dpk_info->dpk_path_ok); rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SUBPAGE, 0x8 | (path << 1)); rtw_write32_mask(rtwdev, 0x1b58, 0x0000007f, 0x0); dpk_info->dpk_txagc[path] = 0; dpk_info->result[path] = 0; dpk_info->dpk_gs[path] = 0x5b; dpk_info->pre_pwsf[path] = 0; dpk_info->thermal_dpk[path] = rtw8822c_dpk_thermal_read(rtwdev, path); } } static void rtw8822c_dpk_calibrate(struct rtw_dev *rtwdev, u8 path) { struct rtw_dpk_info *dpk_info = &rtwdev->dm_info.dpk_info; u32 dpk_txagc; u8 dpk_fail; rtw_dbg(rtwdev, RTW_DBG_RFK, "[DPK] s%d dpk start\n", path); dpk_txagc = rtw8822c_dpk_gainloss(rtwdev, path); dpk_fail = rtw8822c_dpk_by_path(rtwdev, dpk_txagc, path); if (!rtw8822c_dpk_check_pass(rtwdev, dpk_fail, dpk_txagc, path)) rtw_err(rtwdev, "failed to do dpk calibration\n"); rtw_dbg(rtwdev, RTW_DBG_RFK, "[DPK] s%d dpk finish\n", path); if (dpk_info->result[path]) set_bit(path, dpk_info->dpk_path_ok); } static void rtw8822c_dpk_path_select(struct rtw_dev *rtwdev) { rtw8822c_dpk_calibrate(rtwdev, RF_PATH_A); rtw8822c_dpk_calibrate(rtwdev, RF_PATH_B); rtw8822c_dpk_on(rtwdev, RF_PATH_A); rtw8822c_dpk_on(rtwdev, RF_PATH_B); rtw8822c_dpk_cal_coef1(rtwdev); } static void rtw8822c_dpk_enable_disable(struct rtw_dev *rtwdev) { struct rtw_dpk_info *dpk_info = &rtwdev->dm_info.dpk_info; u32 mask = BIT(15) | BIT(14); rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SUBPAGE, 0xc); rtw_write32_mask(rtwdev, REG_DPD_CTL1_S0, BIT_DPD_EN, dpk_info->is_dpk_pwr_on); rtw_write32_mask(rtwdev, REG_DPD_CTL1_S1, BIT_DPD_EN, dpk_info->is_dpk_pwr_on); if (test_bit(RF_PATH_A, dpk_info->dpk_path_ok)) { rtw_write32_mask(rtwdev, REG_DPD_CTL1_S0, mask, 0x0); rtw_write8(rtwdev, REG_DPD_CTL0_S0, dpk_info->dpk_gs[RF_PATH_A]); } if (test_bit(RF_PATH_B, dpk_info->dpk_path_ok)) { rtw_write32_mask(rtwdev, REG_DPD_CTL1_S1, mask, 0x0); rtw_write8(rtwdev, REG_DPD_CTL0_S1, dpk_info->dpk_gs[RF_PATH_B]); } } static void rtw8822c_dpk_reload_data(struct rtw_dev *rtwdev) { struct rtw_dpk_info *dpk_info = &rtwdev->dm_info.dpk_info; u8 path; if (!test_bit(RF_PATH_A, dpk_info->dpk_path_ok) && !test_bit(RF_PATH_B, dpk_info->dpk_path_ok) && dpk_info->dpk_ch == 0) return; for (path = 0; path < rtwdev->hal.rf_path_num; path++) { rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SUBPAGE, 0x8 | (path << 1)); if (dpk_info->dpk_band == RTW_BAND_2G) rtw_write32(rtwdev, REG_DPD_CTL1_S1, 0x1f100000); else rtw_write32(rtwdev, REG_DPD_CTL1_S1, 0x1f0d0000); rtw_write8(rtwdev, REG_DPD_AGC, dpk_info->dpk_txagc[path]); rtw8822c_dpk_coef_write(rtwdev, path, test_bit(path, dpk_info->dpk_path_ok)); rtw8822c_dpk_one_shot(rtwdev, path, RTW_DPK_DPK_ON); rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SUBPAGE, 0xc); if (path == RF_PATH_A) rtw_write32_mask(rtwdev, REG_DPD_CTL0_S0, BIT_GS_PWSF, dpk_info->dpk_gs[path]); else rtw_write32_mask(rtwdev, REG_DPD_CTL0_S1, BIT_GS_PWSF, dpk_info->dpk_gs[path]); } rtw8822c_dpk_cal_coef1(rtwdev); } static bool rtw8822c_dpk_reload(struct rtw_dev *rtwdev) { struct rtw_dpk_info *dpk_info = &rtwdev->dm_info.dpk_info; u8 channel; dpk_info->is_reload = false; +#if defined(__linux__) channel = (u8)(rtw_read_rf(rtwdev, RF_PATH_A, 0x18, RFREG_MASK) & 0xff); +#elif defined(__FreeBSD__) + channel = (u8)(rtw_read_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK) & 0xff); +#endif if (channel == dpk_info->dpk_ch) { rtw_dbg(rtwdev, RTW_DBG_RFK, "[DPK] DPK reload for CH%d!!\n", dpk_info->dpk_ch); rtw8822c_dpk_reload_data(rtwdev); dpk_info->is_reload = true; } return dpk_info->is_reload; } static void rtw8822c_do_dpk(struct rtw_dev *rtwdev) { struct rtw_dpk_info *dpk_info = &rtwdev->dm_info.dpk_info; struct rtw_backup_info bckp[DPK_BB_REG_NUM]; u32 rf_reg_backup[DPK_RF_REG_NUM][DPK_RF_PATH_NUM]; u32 bb_reg[DPK_BB_REG_NUM] = { 0x520, 0x820, 0x824, 0x1c3c, 0x1d58, 0x1864, 0x4164, 0x180c, 0x410c, 0x186c, 0x416c, 0x1a14, 0x1e70, 0x80c, 0x1d70, 0x1e7c, 0x18a4, 0x41a4}; u32 rf_reg[DPK_RF_REG_NUM] = { 0x0, 0x1a, 0x55, 0x63, 0x87, 0x8f, 0xde}; u8 path; if (!dpk_info->is_dpk_pwr_on) { rtw_dbg(rtwdev, RTW_DBG_RFK, "[DPK] Skip DPK due to DPD PWR off\n"); return; } else if (rtw8822c_dpk_reload(rtwdev)) { return; } for (path = RF_PATH_A; path < DPK_RF_PATH_NUM; path++) ewma_thermal_init(&dpk_info->avg_thermal[path]); rtw8822c_dpk_information(rtwdev); rtw8822c_dpk_backup_registers(rtwdev, bb_reg, DPK_BB_REG_NUM, bckp); rtw8822c_dpk_backup_rf_registers(rtwdev, rf_reg, rf_reg_backup); rtw8822c_dpk_mac_bb_setting(rtwdev); rtw8822c_dpk_afe_setting(rtwdev, true); rtw8822c_dpk_pre_setting(rtwdev); rtw8822c_dpk_result_reset(rtwdev); rtw8822c_dpk_path_select(rtwdev); rtw8822c_dpk_afe_setting(rtwdev, false); rtw8822c_dpk_enable_disable(rtwdev); rtw8822c_dpk_reload_rf_registers(rtwdev, rf_reg, rf_reg_backup); for (path = 0; path < rtwdev->hal.rf_path_num; path++) rtw8822c_dpk_rxbb_dc_cal(rtwdev, path); rtw8822c_dpk_restore_registers(rtwdev, DPK_BB_REG_NUM, bckp); } static void rtw8822c_phy_calibration(struct rtw_dev *rtwdev) { rtw8822c_rfk_power_save(rtwdev, false); rtw8822c_do_gapk(rtwdev); rtw8822c_do_iqk(rtwdev); rtw8822c_do_dpk(rtwdev); rtw8822c_rfk_power_save(rtwdev, true); } static void rtw8822c_dpk_track(struct rtw_dev *rtwdev) { struct rtw_dpk_info *dpk_info = &rtwdev->dm_info.dpk_info; u8 path; u8 thermal_value[DPK_RF_PATH_NUM] = {0}; s8 offset[DPK_RF_PATH_NUM], delta_dpk[DPK_RF_PATH_NUM]; if (dpk_info->thermal_dpk[0] == 0 && dpk_info->thermal_dpk[1] == 0) return; for (path = 0; path < DPK_RF_PATH_NUM; path++) { thermal_value[path] = rtw8822c_dpk_thermal_read(rtwdev, path); ewma_thermal_add(&dpk_info->avg_thermal[path], thermal_value[path]); thermal_value[path] = ewma_thermal_read(&dpk_info->avg_thermal[path]); delta_dpk[path] = dpk_info->thermal_dpk[path] - thermal_value[path]; offset[path] = delta_dpk[path] - dpk_info->thermal_dpk_delta[path]; offset[path] &= 0x7f; if (offset[path] != dpk_info->pre_pwsf[path]) { rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SUBPAGE, 0x8 | (path << 1)); rtw_write32_mask(rtwdev, 0x1b58, GENMASK(6, 0), offset[path]); dpk_info->pre_pwsf[path] = offset[path]; } } } #define XCAP_EXTEND(val) ({typeof(val) _v = (val); _v | _v << 7; }) static void rtw8822c_set_crystal_cap_reg(struct rtw_dev *rtwdev, u8 crystal_cap) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; struct rtw_cfo_track *cfo = &dm_info->cfo_track; u32 val = 0; val = XCAP_EXTEND(crystal_cap); cfo->crystal_cap = crystal_cap; rtw_write32_mask(rtwdev, REG_ANAPAR_XTAL_0, BIT_XCAP_0, val); } static void rtw8822c_set_crystal_cap(struct rtw_dev *rtwdev, u8 crystal_cap) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; struct rtw_cfo_track *cfo = &dm_info->cfo_track; if (cfo->crystal_cap == crystal_cap) return; rtw8822c_set_crystal_cap_reg(rtwdev, crystal_cap); } static void rtw8822c_cfo_tracking_reset(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; struct rtw_cfo_track *cfo = &dm_info->cfo_track; cfo->is_adjust = true; if (cfo->crystal_cap > rtwdev->efuse.crystal_cap) rtw8822c_set_crystal_cap(rtwdev, cfo->crystal_cap - 1); else if (cfo->crystal_cap < rtwdev->efuse.crystal_cap) rtw8822c_set_crystal_cap(rtwdev, cfo->crystal_cap + 1); } static void rtw8822c_cfo_init(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; struct rtw_cfo_track *cfo = &dm_info->cfo_track; cfo->crystal_cap = rtwdev->efuse.crystal_cap; cfo->is_adjust = true; } #define REPORT_TO_KHZ(val) ({typeof(val) _v = (val); (_v << 1) + (_v >> 1); }) static s32 rtw8822c_cfo_calc_avg(struct rtw_dev *rtwdev, u8 path_num) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; struct rtw_cfo_track *cfo = &dm_info->cfo_track; s32 cfo_avg, cfo_path_sum = 0, cfo_rpt_sum; u8 i; for (i = 0; i < path_num; i++) { cfo_rpt_sum = REPORT_TO_KHZ(cfo->cfo_tail[i]); if (cfo->cfo_cnt[i]) cfo_avg = cfo_rpt_sum / cfo->cfo_cnt[i]; else cfo_avg = 0; cfo_path_sum += cfo_avg; } for (i = 0; i < path_num; i++) { cfo->cfo_tail[i] = 0; cfo->cfo_cnt[i] = 0; } return cfo_path_sum / path_num; } static void rtw8822c_cfo_need_adjust(struct rtw_dev *rtwdev, s32 cfo_avg) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; struct rtw_cfo_track *cfo = &dm_info->cfo_track; if (!cfo->is_adjust) { if (abs(cfo_avg) > CFO_TRK_ENABLE_TH) cfo->is_adjust = true; } else { if (abs(cfo_avg) <= CFO_TRK_STOP_TH) cfo->is_adjust = false; } if (!rtw_coex_disabled(rtwdev)) { cfo->is_adjust = false; rtw8822c_set_crystal_cap(rtwdev, rtwdev->efuse.crystal_cap); } } static void rtw8822c_cfo_track(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; struct rtw_cfo_track *cfo = &dm_info->cfo_track; u8 path_num = rtwdev->hal.rf_path_num; s8 crystal_cap = cfo->crystal_cap; s32 cfo_avg = 0; if (rtwdev->sta_cnt != 1) { rtw8822c_cfo_tracking_reset(rtwdev); return; } if (cfo->packet_count == cfo->packet_count_pre) return; cfo->packet_count_pre = cfo->packet_count; cfo_avg = rtw8822c_cfo_calc_avg(rtwdev, path_num); rtw8822c_cfo_need_adjust(rtwdev, cfo_avg); if (cfo->is_adjust) { if (cfo_avg > CFO_TRK_ADJ_TH) crystal_cap++; else if (cfo_avg < -CFO_TRK_ADJ_TH) crystal_cap--; crystal_cap = clamp_t(s8, crystal_cap, 0, XCAP_MASK); rtw8822c_set_crystal_cap(rtwdev, (u8)crystal_cap); } } static const struct rtw_phy_cck_pd_reg rtw8822c_cck_pd_reg[RTW_CHANNEL_WIDTH_40 + 1][RTW_RF_PATH_MAX] = { { {0x1ac8, 0x00ff, 0x1ad0, 0x01f}, {0x1ac8, 0xff00, 0x1ad0, 0x3e0} }, { {0x1acc, 0x00ff, 0x1ad0, 0x01F00000}, {0x1acc, 0xff00, 0x1ad0, 0x3E000000} }, }; #define RTW_CCK_PD_MAX 255 #define RTW_CCK_CS_MAX 31 #define RTW_CCK_CS_ERR1 27 #define RTW_CCK_CS_ERR2 29 static void rtw8822c_phy_cck_pd_set_reg(struct rtw_dev *rtwdev, s8 pd_diff, s8 cs_diff, u8 bw, u8 nrx) { u32 pd, cs; if (WARN_ON(bw > RTW_CHANNEL_WIDTH_40 || nrx >= RTW_RF_PATH_MAX)) return; pd = rtw_read32_mask(rtwdev, rtw8822c_cck_pd_reg[bw][nrx].reg_pd, rtw8822c_cck_pd_reg[bw][nrx].mask_pd); cs = rtw_read32_mask(rtwdev, rtw8822c_cck_pd_reg[bw][nrx].reg_cs, rtw8822c_cck_pd_reg[bw][nrx].mask_cs); pd += pd_diff; cs += cs_diff; if (pd > RTW_CCK_PD_MAX) pd = RTW_CCK_PD_MAX; if (cs == RTW_CCK_CS_ERR1 || cs == RTW_CCK_CS_ERR2) cs++; else if (cs > RTW_CCK_CS_MAX) cs = RTW_CCK_CS_MAX; rtw_write32_mask(rtwdev, rtw8822c_cck_pd_reg[bw][nrx].reg_pd, rtw8822c_cck_pd_reg[bw][nrx].mask_pd, pd); rtw_write32_mask(rtwdev, rtw8822c_cck_pd_reg[bw][nrx].reg_cs, rtw8822c_cck_pd_reg[bw][nrx].mask_cs, cs); rtw_dbg(rtwdev, RTW_DBG_PHY, "is_linked=%d, bw=%d, nrx=%d, cs_ratio=0x%x, pd_th=0x%x\n", rtw_is_assoc(rtwdev), bw, nrx, cs, pd); } static void rtw8822c_phy_cck_pd_set(struct rtw_dev *rtwdev, u8 new_lvl) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; s8 pd_lvl[CCK_PD_LV_MAX] = {0, 2, 4, 6, 8}; s8 cs_lvl[CCK_PD_LV_MAX] = {0, 2, 2, 2, 4}; u8 cur_lvl; u8 nrx, bw; nrx = (u8)rtw_read32_mask(rtwdev, 0x1a2c, 0x60000); bw = (u8)rtw_read32_mask(rtwdev, 0x9b0, 0xc); rtw_dbg(rtwdev, RTW_DBG_PHY, "lv: (%d) -> (%d) bw=%d nr=%d cck_fa_avg=%d\n", dm_info->cck_pd_lv[bw][nrx], new_lvl, bw, nrx, dm_info->cck_fa_avg); if (dm_info->cck_pd_lv[bw][nrx] == new_lvl) return; cur_lvl = dm_info->cck_pd_lv[bw][nrx]; /* update cck pd info */ dm_info->cck_fa_avg = CCK_FA_AVG_RESET; rtw8822c_phy_cck_pd_set_reg(rtwdev, pd_lvl[new_lvl] - pd_lvl[cur_lvl], cs_lvl[new_lvl] - cs_lvl[cur_lvl], bw, nrx); dm_info->cck_pd_lv[bw][nrx] = new_lvl; } #define PWR_TRACK_MASK 0x7f static void rtw8822c_pwrtrack_set(struct rtw_dev *rtwdev, u8 rf_path) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; switch (rf_path) { case RF_PATH_A: rtw_write32_mask(rtwdev, 0x18a0, PWR_TRACK_MASK, dm_info->delta_power_index[rf_path]); break; case RF_PATH_B: rtw_write32_mask(rtwdev, 0x41a0, PWR_TRACK_MASK, dm_info->delta_power_index[rf_path]); break; default: break; } } static void rtw8822c_pwr_track_stats(struct rtw_dev *rtwdev, u8 path) { u8 thermal_value; if (rtwdev->efuse.thermal_meter[path] == 0xff) return; thermal_value = rtw_read_rf(rtwdev, path, RF_T_METER, 0x7e); rtw_phy_pwrtrack_avg(rtwdev, thermal_value, path); } static void rtw8822c_pwr_track_path(struct rtw_dev *rtwdev, struct rtw_swing_table *swing_table, u8 path) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; u8 delta; delta = rtw_phy_pwrtrack_get_delta(rtwdev, path); dm_info->delta_power_index[path] = rtw_phy_pwrtrack_get_pwridx(rtwdev, swing_table, path, path, delta); rtw8822c_pwrtrack_set(rtwdev, path); } static void __rtw8822c_pwr_track(struct rtw_dev *rtwdev) { struct rtw_swing_table swing_table; u8 i; rtw_phy_config_swing_table(rtwdev, &swing_table); for (i = 0; i < rtwdev->hal.rf_path_num; i++) rtw8822c_pwr_track_stats(rtwdev, i); if (rtw_phy_pwrtrack_need_lck(rtwdev)) rtw8822c_do_lck(rtwdev); for (i = 0; i < rtwdev->hal.rf_path_num; i++) rtw8822c_pwr_track_path(rtwdev, &swing_table, i); } static void rtw8822c_pwr_track(struct rtw_dev *rtwdev) { struct rtw_efuse *efuse = &rtwdev->efuse; struct rtw_dm_info *dm_info = &rtwdev->dm_info; if (efuse->power_track_type != 0) return; if (!dm_info->pwr_trk_triggered) { rtw_write_rf(rtwdev, RF_PATH_A, RF_T_METER, BIT(19), 0x01); rtw_write_rf(rtwdev, RF_PATH_A, RF_T_METER, BIT(19), 0x00); rtw_write_rf(rtwdev, RF_PATH_A, RF_T_METER, BIT(19), 0x01); rtw_write_rf(rtwdev, RF_PATH_B, RF_T_METER, BIT(19), 0x01); rtw_write_rf(rtwdev, RF_PATH_B, RF_T_METER, BIT(19), 0x00); rtw_write_rf(rtwdev, RF_PATH_B, RF_T_METER, BIT(19), 0x01); dm_info->pwr_trk_triggered = true; return; } __rtw8822c_pwr_track(rtwdev); dm_info->pwr_trk_triggered = false; } static void rtw8822c_adaptivity_init(struct rtw_dev *rtwdev) { rtw_phy_set_edcca_th(rtwdev, RTW8822C_EDCCA_MAX, RTW8822C_EDCCA_MAX); /* mac edcca state setting */ rtw_write32_clr(rtwdev, REG_TX_PTCL_CTRL, BIT_DIS_EDCCA); rtw_write32_set(rtwdev, REG_RD_CTRL, BIT_EDCCA_MSK_CNTDOWN_EN); /* edcca decistion opt */ rtw_write32_clr(rtwdev, REG_EDCCA_DECISION, BIT_EDCCA_OPTION); } static void rtw8822c_adaptivity(struct rtw_dev *rtwdev) { struct rtw_dm_info *dm_info = &rtwdev->dm_info; s8 l2h, h2l; u8 igi; igi = dm_info->igi_history[0]; if (dm_info->edcca_mode == RTW_EDCCA_NORMAL) { l2h = max_t(s8, igi + EDCCA_IGI_L2H_DIFF, EDCCA_TH_L2H_LB); h2l = l2h - EDCCA_L2H_H2L_DIFF_NORMAL; } else { if (igi < dm_info->l2h_th_ini - EDCCA_ADC_BACKOFF) l2h = igi + EDCCA_ADC_BACKOFF; else l2h = dm_info->l2h_th_ini; h2l = l2h - EDCCA_L2H_H2L_DIFF; } rtw_phy_set_edcca_th(rtwdev, l2h, h2l); } +static void rtw8822c_fill_txdesc_checksum(struct rtw_dev *rtwdev, + struct rtw_tx_pkt_info *pkt_info, + u8 *txdesc) +{ + const struct rtw_chip_info *chip = rtwdev->chip; + size_t words; + + words = (pkt_info->pkt_offset * 8 + chip->tx_pkt_desc_sz) / 2; + + fill_txdesc_checksum_common(txdesc, words); +} + static const struct rtw_pwr_seq_cmd trans_carddis_to_cardemu_8822c[] = { {0x0086, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_SDIO, RTW_PWR_CMD_WRITE, BIT(0), 0}, {0x0086, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_SDIO, RTW_PWR_CMD_POLLING, BIT(1), BIT(1)}, {0x002E, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(2), BIT(2)}, {0x002D, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), 0}, {0x007F, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(7), 0}, {0x004A, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), 0}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(3) | BIT(4) | BIT(7), 0}, {0xFFFF, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, 0, RTW_PWR_CMD_END, 0, 0}, }; static const struct rtw_pwr_seq_cmd trans_cardemu_to_act_8822c[] = { {0x0000, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(5), 0}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, (BIT(4) | BIT(3) | BIT(2)), 0}, {0x0075, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, {0x0006, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_POLLING, BIT(1), BIT(1)}, {0x0075, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), 0}, {0xFF1A, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0}, {0x002E, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(3), 0}, {0x0006, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, (BIT(4) | BIT(3)), 0}, {0x1018, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(2), BIT(2)}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_POLLING, BIT(0), 0}, {0x0074, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(5), BIT(5)}, {0x0071, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(4), 0}, {0x0062, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, (BIT(7) | BIT(6) | BIT(5)), (BIT(7) | BIT(6) | BIT(5))}, {0x0061, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, (BIT(7) | BIT(6) | BIT(5)), 0}, {0x001F, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, (BIT(7) | BIT(6)), BIT(7)}, {0x00EF, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, (BIT(7) | BIT(6)), BIT(7)}, {0x1045, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(4), BIT(4)}, {0x0010, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(2), BIT(2)}, {0x1064, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(1), BIT(1)}, {0xFFFF, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, 0, RTW_PWR_CMD_END, 0, 0}, }; static const struct rtw_pwr_seq_cmd trans_act_to_cardemu_8822c[] = { {0x0093, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(3), 0}, {0x001F, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0}, {0x00EF, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0}, {0x1045, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(4), 0}, {0xFF1A, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0x30}, {0x0049, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(1), 0}, {0x0006, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, {0x0002, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(1), 0}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(1), BIT(1)}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_POLLING, BIT(1), 0}, {0x0000, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(5), BIT(5)}, {0xFFFF, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, 0, RTW_PWR_CMD_END, 0, 0}, }; static const struct rtw_pwr_seq_cmd trans_cardemu_to_carddis_8822c[] = { {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(7), BIT(7)}, {0x0007, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0x00}, {0x0067, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(5), 0}, {0x004A, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(0), 0}, {0x0081, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(7) | BIT(6), 0}, {0x0090, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(1), 0}, {0x0092, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0x20}, {0x0093, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, 0xFF, 0x04}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_USB_MSK | RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(3) | BIT(4), BIT(3)}, {0x0005, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_PCI_MSK, RTW_PWR_ADDR_MAC, RTW_PWR_CMD_WRITE, BIT(2), BIT(2)}, {0x0086, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_SDIO_MSK, RTW_PWR_ADDR_SDIO, RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, {0xFFFF, RTW_PWR_CUT_ALL_MSK, RTW_PWR_INTF_ALL_MSK, 0, RTW_PWR_CMD_END, 0, 0}, }; static const struct rtw_pwr_seq_cmd *card_enable_flow_8822c[] = { trans_carddis_to_cardemu_8822c, trans_cardemu_to_act_8822c, NULL }; static const struct rtw_pwr_seq_cmd *card_disable_flow_8822c[] = { trans_act_to_cardemu_8822c, trans_cardemu_to_carddis_8822c, NULL }; static const struct rtw_intf_phy_para usb2_param_8822c[] = { {0xFFFF, 0x00, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_ALL, RTW_INTF_PHY_PLATFORM_ALL}, }; static const struct rtw_intf_phy_para usb3_param_8822c[] = { {0xFFFF, 0x0000, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_ALL, RTW_INTF_PHY_PLATFORM_ALL}, }; static const struct rtw_intf_phy_para pcie_gen1_param_8822c[] = { {0xFFFF, 0x0000, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_ALL, RTW_INTF_PHY_PLATFORM_ALL}, }; static const struct rtw_intf_phy_para pcie_gen2_param_8822c[] = { {0xFFFF, 0x0000, RTW_IP_SEL_PHY, RTW_INTF_PHY_CUT_ALL, RTW_INTF_PHY_PLATFORM_ALL}, }; static const struct rtw_intf_phy_para_table phy_para_table_8822c = { .usb2_para = usb2_param_8822c, .usb3_para = usb3_param_8822c, .gen1_para = pcie_gen1_param_8822c, .gen2_para = pcie_gen2_param_8822c, .n_usb2_para = ARRAY_SIZE(usb2_param_8822c), .n_usb3_para = ARRAY_SIZE(usb2_param_8822c), .n_gen1_para = ARRAY_SIZE(pcie_gen1_param_8822c), .n_gen2_para = ARRAY_SIZE(pcie_gen2_param_8822c), }; static const struct rtw_rfe_def rtw8822c_rfe_defs[] = { [0] = RTW_DEF_RFE(8822c, 0, 0), [1] = RTW_DEF_RFE(8822c, 0, 0), [2] = RTW_DEF_RFE(8822c, 0, 0), + [3] = RTW_DEF_RFE(8822c, 0, 0), + [4] = RTW_DEF_RFE(8822c, 0, 0), [5] = RTW_DEF_RFE(8822c, 0, 5), [6] = RTW_DEF_RFE(8822c, 0, 0), }; static const struct rtw_hw_reg rtw8822c_dig[] = { [0] = { .addr = 0x1d70, .mask = 0x7f }, [1] = { .addr = 0x1d70, .mask = 0x7f00 }, }; static const struct rtw_ltecoex_addr rtw8822c_ltecoex_addr = { .ctrl = LTECOEX_ACCESS_CTRL, .wdata = LTECOEX_WRITE_DATA, .rdata = LTECOEX_READ_DATA, }; static const struct rtw_page_table page_table_8822c[] = { {64, 64, 64, 64, 1}, {64, 64, 64, 64, 1}, {64, 64, 0, 0, 1}, {64, 64, 64, 0, 1}, {64, 64, 64, 64, 1}, }; static const struct rtw_rqpn rqpn_table_8822c[] = { {RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_EXTRA, RTW_DMA_MAPPING_HIGH}, {RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_EXTRA, RTW_DMA_MAPPING_HIGH}, {RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH}, {RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH}, {RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_EXTRA, RTW_DMA_MAPPING_HIGH}, }; static struct rtw_prioq_addrs prioq_addrs_8822c = { .prio[RTW_DMA_MAPPING_EXTRA] = { .rsvd = REG_FIFOPAGE_INFO_4, .avail = REG_FIFOPAGE_INFO_4 + 2, }, .prio[RTW_DMA_MAPPING_LOW] = { .rsvd = REG_FIFOPAGE_INFO_2, .avail = REG_FIFOPAGE_INFO_2 + 2, }, .prio[RTW_DMA_MAPPING_NORMAL] = { .rsvd = REG_FIFOPAGE_INFO_3, .avail = REG_FIFOPAGE_INFO_3 + 2, }, .prio[RTW_DMA_MAPPING_HIGH] = { .rsvd = REG_FIFOPAGE_INFO_1, .avail = REG_FIFOPAGE_INFO_1 + 2, }, .wsize = true, }; static struct rtw_chip_ops rtw8822c_ops = { .phy_set_param = rtw8822c_phy_set_param, .read_efuse = rtw8822c_read_efuse, .query_rx_desc = rtw8822c_query_rx_desc, .set_channel = rtw8822c_set_channel, .mac_init = rtw8822c_mac_init, .dump_fw_crash = rtw8822c_dump_fw_crash, .read_rf = rtw_phy_read_rf, .write_rf = rtw_phy_write_rf_reg_mix, .set_tx_power_index = rtw8822c_set_tx_power_index, .set_antenna = rtw8822c_set_antenna, .cfg_ldo25 = rtw8822c_cfg_ldo25, .false_alarm_statistics = rtw8822c_false_alarm_statistics, .dpk_track = rtw8822c_dpk_track, .phy_calibration = rtw8822c_phy_calibration, .cck_pd_set = rtw8822c_phy_cck_pd_set, .pwr_track = rtw8822c_pwr_track, .config_bfee = rtw8822c_bf_config_bfee, .set_gid_table = rtw_bf_set_gid_table, .cfg_csi_rate = rtw_bf_cfg_csi_rate, .adaptivity_init = rtw8822c_adaptivity_init, .adaptivity = rtw8822c_adaptivity, .cfo_init = rtw8822c_cfo_init, .cfo_track = rtw8822c_cfo_track, .config_tx_path = rtw8822c_config_tx_path, .config_txrx_mode = rtw8822c_config_trx_mode, + .fill_txdesc_checksum = rtw8822c_fill_txdesc_checksum, .coex_set_init = rtw8822c_coex_cfg_init, .coex_set_ant_switch = NULL, .coex_set_gnt_fix = rtw8822c_coex_cfg_gnt_fix, .coex_set_gnt_debug = rtw8822c_coex_cfg_gnt_debug, .coex_set_rfe_type = rtw8822c_coex_cfg_rfe_type, .coex_set_wl_tx_power = rtw8822c_coex_cfg_wl_tx_power, .coex_set_wl_rx_gain = rtw8822c_coex_cfg_wl_rx_gain, }; /* Shared-Antenna Coex Table */ static const struct coex_table_para table_sant_8822c[] = { {0xffffffff, 0xffffffff}, /* case-0 */ {0x55555555, 0x55555555}, {0x66555555, 0x66555555}, {0xaaaaaaaa, 0xaaaaaaaa}, {0x5a5a5a5a, 0x5a5a5a5a}, {0xfafafafa, 0xfafafafa}, /* case-5 */ {0x6a5a5555, 0xaaaaaaaa}, {0x6a5a56aa, 0x6a5a56aa}, {0x6a5a5a5a, 0x6a5a5a5a}, {0x66555555, 0x5a5a5a5a}, {0x66555555, 0x6a5a5a5a}, /* case-10 */ {0x66555555, 0x6a5a5aaa}, {0x66555555, 0x5a5a5aaa}, {0x66555555, 0x6aaa5aaa}, {0x66555555, 0xaaaa5aaa}, {0x66555555, 0xaaaaaaaa}, /* case-15 */ {0xffff55ff, 0xfafafafa}, {0xffff55ff, 0x6afa5afa}, {0xaaffffaa, 0xfafafafa}, {0xaa5555aa, 0x5a5a5a5a}, {0xaa5555aa, 0x6a5a5a5a}, /* case-20 */ {0xaa5555aa, 0xaaaaaaaa}, {0xffffffff, 0x5a5a5a5a}, {0xffffffff, 0x5a5a5a5a}, {0xffffffff, 0x55555555}, {0xffffffff, 0x5a5a5aaa}, /* case-25 */ {0x55555555, 0x5a5a5a5a}, {0x55555555, 0xaaaaaaaa}, {0x55555555, 0x6a5a6a5a}, {0x66556655, 0x66556655}, {0x66556aaa, 0x6a5a6aaa}, /*case-30*/ {0xffffffff, 0x5aaa5aaa}, {0x56555555, 0x5a5a5aaa}, {0xdaffdaff, 0xdaffdaff}, {0xddffddff, 0xddffddff}, }; /* Non-Shared-Antenna Coex Table */ static const struct coex_table_para table_nsant_8822c[] = { {0xffffffff, 0xffffffff}, /* case-100 */ {0x55555555, 0x55555555}, {0x66555555, 0x66555555}, {0xaaaaaaaa, 0xaaaaaaaa}, {0x5a5a5a5a, 0x5a5a5a5a}, {0xfafafafa, 0xfafafafa}, /* case-105 */ {0x5afa5afa, 0x5afa5afa}, {0x55555555, 0xfafafafa}, {0x66555555, 0xfafafafa}, {0x66555555, 0x5a5a5a5a}, {0x66555555, 0x6a5a5a5a}, /* case-110 */ {0x66555555, 0xaaaaaaaa}, {0xffff55ff, 0xfafafafa}, {0xffff55ff, 0x5afa5afa}, {0xffff55ff, 0xaaaaaaaa}, {0xffff55ff, 0xffff55ff}, /* case-115 */ {0xaaffffaa, 0x5afa5afa}, {0xaaffffaa, 0xaaaaaaaa}, {0xffffffff, 0xfafafafa}, {0xffffffff, 0x5afa5afa}, {0xffffffff, 0xaaaaaaaa}, /* case-120 */ {0x55ff55ff, 0x5afa5afa}, {0x55ff55ff, 0xaaaaaaaa}, {0x55ff55ff, 0x55ff55ff} }; /* Shared-Antenna TDMA */ static const struct coex_tdma_para tdma_sant_8822c[] = { { {0x00, 0x00, 0x00, 0x00, 0x00} }, /* case-0 */ { {0x61, 0x45, 0x03, 0x11, 0x11} }, /* case-1 */ { {0x61, 0x3a, 0x03, 0x11, 0x11} }, { {0x61, 0x30, 0x03, 0x11, 0x11} }, { {0x61, 0x20, 0x03, 0x11, 0x11} }, { {0x61, 0x10, 0x03, 0x11, 0x11} }, /* case-5 */ { {0x61, 0x45, 0x03, 0x11, 0x10} }, { {0x61, 0x3a, 0x03, 0x11, 0x10} }, { {0x61, 0x30, 0x03, 0x11, 0x10} }, { {0x61, 0x20, 0x03, 0x11, 0x10} }, { {0x61, 0x10, 0x03, 0x11, 0x10} }, /* case-10 */ { {0x61, 0x08, 0x03, 0x11, 0x14} }, { {0x61, 0x08, 0x03, 0x10, 0x14} }, { {0x51, 0x08, 0x03, 0x10, 0x54} }, { {0x51, 0x08, 0x03, 0x10, 0x55} }, { {0x51, 0x08, 0x07, 0x10, 0x54} }, /* case-15 */ { {0x51, 0x45, 0x03, 0x10, 0x50} }, { {0x51, 0x3a, 0x03, 0x10, 0x50} }, { {0x51, 0x30, 0x03, 0x10, 0x50} }, { {0x51, 0x20, 0x03, 0x10, 0x50} }, { {0x51, 0x10, 0x03, 0x10, 0x50} }, /* case-20 */ { {0x51, 0x4a, 0x03, 0x10, 0x50} }, { {0x51, 0x0c, 0x03, 0x10, 0x54} }, { {0x55, 0x08, 0x03, 0x10, 0x54} }, { {0x65, 0x10, 0x03, 0x11, 0x10} }, { {0x51, 0x10, 0x03, 0x10, 0x51} }, /* case-25 */ { {0x51, 0x08, 0x03, 0x10, 0x50} }, { {0x61, 0x08, 0x03, 0x11, 0x11} } }; /* Non-Shared-Antenna TDMA */ static const struct coex_tdma_para tdma_nsant_8822c[] = { { {0x00, 0x00, 0x00, 0x00, 0x00} }, /* case-100 */ { {0x61, 0x45, 0x03, 0x11, 0x11} }, { {0x61, 0x3a, 0x03, 0x11, 0x11} }, { {0x61, 0x30, 0x03, 0x11, 0x11} }, { {0x61, 0x20, 0x03, 0x11, 0x11} }, { {0x61, 0x10, 0x03, 0x11, 0x11} }, /* case-105 */ { {0x61, 0x45, 0x03, 0x11, 0x10} }, { {0x61, 0x3a, 0x03, 0x11, 0x10} }, { {0x61, 0x30, 0x03, 0x11, 0x10} }, { {0x61, 0x20, 0x03, 0x11, 0x10} }, { {0x61, 0x10, 0x03, 0x11, 0x10} }, /* case-110 */ { {0x61, 0x08, 0x03, 0x11, 0x14} }, { {0x61, 0x08, 0x03, 0x10, 0x14} }, { {0x51, 0x08, 0x03, 0x10, 0x54} }, { {0x51, 0x08, 0x03, 0x10, 0x55} }, { {0x51, 0x08, 0x07, 0x10, 0x54} }, /* case-115 */ { {0x51, 0x45, 0x03, 0x10, 0x50} }, { {0x51, 0x3a, 0x03, 0x10, 0x50} }, { {0x51, 0x30, 0x03, 0x10, 0x50} }, { {0x51, 0x20, 0x03, 0x10, 0x50} }, { {0x51, 0x10, 0x03, 0x10, 0x50} }, /* case-120 */ { {0x51, 0x08, 0x03, 0x10, 0x50} } }; /* rssi in percentage % (dbm = % - 100) */ static const u8 wl_rssi_step_8822c[] = {60, 50, 44, 30}; static const u8 bt_rssi_step_8822c[] = {8, 15, 20, 25}; static const struct coex_5g_afh_map afh_5g_8822c[] = { {0, 0, 0} }; /* wl_tx_dec_power, bt_tx_dec_power, wl_rx_gain, bt_rx_lna_constrain */ static const struct coex_rf_para rf_para_tx_8822c[] = { {0, 0, false, 7}, /* for normal */ {0, 16, false, 7}, /* for WL-CPT */ {8, 17, true, 4}, {7, 18, true, 4}, {6, 19, true, 4}, {5, 20, true, 4}, {0, 21, true, 4} /* for gamg hid */ }; static const struct coex_rf_para rf_para_rx_8822c[] = { {0, 0, false, 7}, /* for normal */ {0, 16, false, 7}, /* for WL-CPT */ {3, 24, true, 5}, {2, 26, true, 5}, {1, 27, true, 5}, {0, 28, true, 5}, {0, 28, true, 5} /* for gamg hid */ }; #if defined(__linux__) static_assert(ARRAY_SIZE(rf_para_tx_8822c) == ARRAY_SIZE(rf_para_rx_8822c)); #elif defined(__FreeBSD__) rtw88_static_assert(ARRAY_SIZE(rf_para_tx_8822c) == ARRAY_SIZE(rf_para_rx_8822c)); #endif static const u8 rtw8822c_pwrtrk_5gb_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { { 0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 32 }, { 0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 32 }, { 0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 32 }, }; static const u8 rtw8822c_pwrtrk_5gb_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 22, 23, 24, 25, 26, 27 }, { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 22, 23, 24, 25, 26, 27 }, { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 22, 23, 24, 25, 26, 27 }, }; static const u8 rtw8822c_pwrtrk_5ga_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { { 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 26, 27, 28, 29, 30, 31, 33 }, { 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 26, 27, 28, 29, 30, 31, 33 }, { 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 26, 27, 28, 29, 30, 31, 33 }, }; static const u8 rtw8822c_pwrtrk_5ga_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 }, { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 }, { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 }, }; static const u8 rtw8822c_pwrtrk_2gb_n[RTW_PWR_TRK_TBL_SZ] = { 0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9, 9, 10, 11, 12, 13, 14, 15, 15, 16, 17, 18, 19, 20, 20, 21, 22, 23, 24, 25 }; static const u8 rtw8822c_pwrtrk_2gb_p[RTW_PWR_TRK_TBL_SZ] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28 }; static const u8 rtw8822c_pwrtrk_2ga_n[RTW_PWR_TRK_TBL_SZ] = { 0, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9, 10, 11, 11, 12, 13, 13, 14, 15, 15, 16, 17, 17, 18, 19, 19 }; static const u8 rtw8822c_pwrtrk_2ga_p[RTW_PWR_TRK_TBL_SZ] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 25, 26, 27 }; static const u8 rtw8822c_pwrtrk_2g_cck_b_n[RTW_PWR_TRK_TBL_SZ] = { 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10, 11, 11, 12, 13, 14, 15, 16, 17, 17, 18, 19, 20, 21, 22, 23, 23, 24, 25 }; static const u8 rtw8822c_pwrtrk_2g_cck_b_p[RTW_PWR_TRK_TBL_SZ] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 }; static const u8 rtw8822c_pwrtrk_2g_cck_a_n[RTW_PWR_TRK_TBL_SZ] = { 0, 1, 2, 3, 3, 4, 5, 6, 6, 7, 8, 9, 9, 10, 11, 12, 12, 13, 14, 15, 15, 16, 17, 18, 18, 19, 20, 21, 21, 22 }; static const u8 rtw8822c_pwrtrk_2g_cck_a_p[RTW_PWR_TRK_TBL_SZ] = { 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10, 11, 11, 12, 13, 14, 15, 16, 17, 18, 18, 19, 20, 21, 22, 23, 24, 24, 25 }; static const struct rtw_pwr_track_tbl rtw8822c_rtw_pwr_track_tbl = { .pwrtrk_5gb_n[RTW_PWR_TRK_5G_1] = rtw8822c_pwrtrk_5gb_n[RTW_PWR_TRK_5G_1], .pwrtrk_5gb_n[RTW_PWR_TRK_5G_2] = rtw8822c_pwrtrk_5gb_n[RTW_PWR_TRK_5G_2], .pwrtrk_5gb_n[RTW_PWR_TRK_5G_3] = rtw8822c_pwrtrk_5gb_n[RTW_PWR_TRK_5G_3], .pwrtrk_5gb_p[RTW_PWR_TRK_5G_1] = rtw8822c_pwrtrk_5gb_p[RTW_PWR_TRK_5G_1], .pwrtrk_5gb_p[RTW_PWR_TRK_5G_2] = rtw8822c_pwrtrk_5gb_p[RTW_PWR_TRK_5G_2], .pwrtrk_5gb_p[RTW_PWR_TRK_5G_3] = rtw8822c_pwrtrk_5gb_p[RTW_PWR_TRK_5G_3], .pwrtrk_5ga_n[RTW_PWR_TRK_5G_1] = rtw8822c_pwrtrk_5ga_n[RTW_PWR_TRK_5G_1], .pwrtrk_5ga_n[RTW_PWR_TRK_5G_2] = rtw8822c_pwrtrk_5ga_n[RTW_PWR_TRK_5G_2], .pwrtrk_5ga_n[RTW_PWR_TRK_5G_3] = rtw8822c_pwrtrk_5ga_n[RTW_PWR_TRK_5G_3], .pwrtrk_5ga_p[RTW_PWR_TRK_5G_1] = rtw8822c_pwrtrk_5ga_p[RTW_PWR_TRK_5G_1], .pwrtrk_5ga_p[RTW_PWR_TRK_5G_2] = rtw8822c_pwrtrk_5ga_p[RTW_PWR_TRK_5G_2], .pwrtrk_5ga_p[RTW_PWR_TRK_5G_3] = rtw8822c_pwrtrk_5ga_p[RTW_PWR_TRK_5G_3], .pwrtrk_2gb_n = rtw8822c_pwrtrk_2gb_n, .pwrtrk_2gb_p = rtw8822c_pwrtrk_2gb_p, .pwrtrk_2ga_n = rtw8822c_pwrtrk_2ga_n, .pwrtrk_2ga_p = rtw8822c_pwrtrk_2ga_p, .pwrtrk_2g_cckb_n = rtw8822c_pwrtrk_2g_cck_b_n, .pwrtrk_2g_cckb_p = rtw8822c_pwrtrk_2g_cck_b_p, .pwrtrk_2g_ccka_n = rtw8822c_pwrtrk_2g_cck_a_n, .pwrtrk_2g_ccka_p = rtw8822c_pwrtrk_2g_cck_a_p, }; static struct rtw_hw_reg_offset rtw8822c_edcca_th[] = { [EDCCA_TH_L2H_IDX] = { {.addr = 0x84c, .mask = MASKBYTE2}, .offset = 0x80 }, [EDCCA_TH_H2L_IDX] = { {.addr = 0x84c, .mask = MASKBYTE3}, .offset = 0x80 }, }; #ifdef CONFIG_PM static const struct wiphy_wowlan_support rtw_wowlan_stub_8822c = { .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_GTK_REKEY_FAILURE | WIPHY_WOWLAN_DISCONNECT | WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | WIPHY_WOWLAN_NET_DETECT, .n_patterns = RTW_MAX_PATTERN_NUM, .pattern_max_len = RTW_MAX_PATTERN_SIZE, .pattern_min_len = 1, .max_nd_match_sets = 4, }; #endif static const struct rtw_reg_domain coex_info_hw_regs_8822c[] = { {0x1860, BIT(3), RTW_REG_DOMAIN_MAC8}, {0x4160, BIT(3), RTW_REG_DOMAIN_MAC8}, {0x1c32, BIT(6), RTW_REG_DOMAIN_MAC8}, {0x1c38, BIT(28), RTW_REG_DOMAIN_MAC32}, {0, 0, RTW_REG_DOMAIN_NL}, {0x430, MASKDWORD, RTW_REG_DOMAIN_MAC32}, {0x434, MASKDWORD, RTW_REG_DOMAIN_MAC32}, {0x42a, MASKLWORD, RTW_REG_DOMAIN_MAC16}, {0x426, MASKBYTE0, RTW_REG_DOMAIN_MAC8}, {0x45e, BIT(3), RTW_REG_DOMAIN_MAC8}, {0x454, MASKLWORD, RTW_REG_DOMAIN_MAC16}, {0, 0, RTW_REG_DOMAIN_NL}, {0x4c, BIT(24) | BIT(23), RTW_REG_DOMAIN_MAC32}, {0x64, BIT(0), RTW_REG_DOMAIN_MAC8}, {0x4c6, BIT(4), RTW_REG_DOMAIN_MAC8}, {0x40, BIT(5), RTW_REG_DOMAIN_MAC8}, {0x1, RFREG_MASK, RTW_REG_DOMAIN_RF_B}, {0, 0, RTW_REG_DOMAIN_NL}, {0x550, MASKDWORD, RTW_REG_DOMAIN_MAC32}, {0x522, MASKBYTE0, RTW_REG_DOMAIN_MAC8}, {0x953, BIT(1), RTW_REG_DOMAIN_MAC8}, {0xc50, MASKBYTE0, RTW_REG_DOMAIN_MAC8}, }; const struct rtw_chip_info rtw8822c_hw_spec = { .ops = &rtw8822c_ops, .id = RTW_CHIP_TYPE_8822C, .fw_name = "rtw88/rtw8822c_fw.bin", .wlan_cpu = RTW_WCPU_11AC, .tx_pkt_desc_sz = 48, .tx_buf_desc_sz = 16, .rx_pkt_desc_sz = 24, .rx_buf_desc_sz = 8, .phy_efuse_size = 512, .log_efuse_size = 768, .ptct_efuse_size = 124, .txff_size = 262144, .rxff_size = 24576, .fw_rxff_size = 12288, + .rsvd_drv_pg_num = 16, .txgi_factor = 2, .is_pwr_by_rate_dec = false, .max_power_index = 0x7f, .csi_buf_pg_num = 50, .band = RTW_BAND_2G | RTW_BAND_5G, - .page_size = 128, + .page_size = TX_PAGE_SIZE, .dig_min = 0x20, .default_1ss_tx_path = BB_PATH_A, .path_div_supported = true, .ht_supported = true, .vht_supported = true, .lps_deep_mode_supported = BIT(LPS_DEEP_MODE_LCLK) | BIT(LPS_DEEP_MODE_PG), .sys_func_en = 0xD8, .pwr_on_seq = card_enable_flow_8822c, .pwr_off_seq = card_disable_flow_8822c, .page_table = page_table_8822c, .rqpn_table = rqpn_table_8822c, .prioq_addrs = &prioq_addrs_8822c, .intf_table = &phy_para_table_8822c, .dig = rtw8822c_dig, .dig_cck = NULL, .rf_base_addr = {0x3c00, 0x4c00}, .rf_sipi_addr = {0x1808, 0x4108}, .ltecoex_addr = &rtw8822c_ltecoex_addr, .mac_tbl = &rtw8822c_mac_tbl, .agc_tbl = &rtw8822c_agc_tbl, .bb_tbl = &rtw8822c_bb_tbl, .rfk_init_tbl = &rtw8822c_array_mp_cal_init_tbl, .rf_tbl = {&rtw8822c_rf_b_tbl, &rtw8822c_rf_a_tbl}, .rfe_defs = rtw8822c_rfe_defs, .rfe_defs_size = ARRAY_SIZE(rtw8822c_rfe_defs), .en_dis_dpd = true, .dpd_ratemask = DIS_DPD_RATEALL, .pwr_track_tbl = &rtw8822c_rtw_pwr_track_tbl, .iqk_threshold = 8, .lck_threshold = 8, .bfer_su_max_num = 2, .bfer_mu_max_num = 1, .rx_ldpc = true, .tx_stbc = true, .edcca_th = rtw8822c_edcca_th, .l2h_th_ini_cs = 60, .l2h_th_ini_ad = 45, .ampdu_density = IEEE80211_HT_MPDU_DENSITY_2, #ifdef CONFIG_PM .wow_fw_name = "rtw88/rtw8822c_wow_fw.bin", .wowlan_stub = &rtw_wowlan_stub_8822c, .max_sched_scan_ssids = 4, #endif + .max_scan_ie_len = (RTW_PROBE_PG_CNT - 1) * TX_PAGE_SIZE, .coex_para_ver = 0x22020720, .bt_desired_ver = 0x20, .scbd_support = true, .new_scbd10_def = true, .ble_hid_profile_support = true, .wl_mimo_ps_support = true, .pstdma_type = COEX_PSTDMA_FORCE_LPSOFF, .bt_rssi_type = COEX_BTRSSI_DBM, .ant_isolation = 15, .rssi_tolerance = 2, .wl_rssi_step = wl_rssi_step_8822c, .bt_rssi_step = bt_rssi_step_8822c, .table_sant_num = ARRAY_SIZE(table_sant_8822c), .table_sant = table_sant_8822c, .table_nsant_num = ARRAY_SIZE(table_nsant_8822c), .table_nsant = table_nsant_8822c, .tdma_sant_num = ARRAY_SIZE(tdma_sant_8822c), .tdma_sant = tdma_sant_8822c, .tdma_nsant_num = ARRAY_SIZE(tdma_nsant_8822c), .tdma_nsant = tdma_nsant_8822c, .wl_rf_para_num = ARRAY_SIZE(rf_para_tx_8822c), .wl_rf_para_tx = rf_para_tx_8822c, .wl_rf_para_rx = rf_para_rx_8822c, .bt_afh_span_bw20 = 0x24, .bt_afh_span_bw40 = 0x36, .afh_5g_num = ARRAY_SIZE(afh_5g_8822c), .afh_5g = afh_5g_8822c, .coex_info_hw_regs_num = ARRAY_SIZE(coex_info_hw_regs_8822c), .coex_info_hw_regs = coex_info_hw_regs_8822c, .fw_fifo_addr = {0x780, 0x700, 0x780, 0x660, 0x650, 0x680}, .fwcd_segs = &rtw8822c_fwcd_segs, }; EXPORT_SYMBOL(rtw8822c_hw_spec); MODULE_FIRMWARE("rtw88/rtw8822c_fw.bin"); MODULE_FIRMWARE("rtw88/rtw8822c_wow_fw.bin"); MODULE_AUTHOR("Realtek Corporation"); MODULE_DESCRIPTION("Realtek 802.11ac wireless 8822c driver"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sys/contrib/dev/rtw88/rtw8822c.h b/sys/contrib/dev/rtw88/rtw8822c.h index 479d5d769c52..1bc0e7f5d6bb 100644 --- a/sys/contrib/dev/rtw88/rtw8822c.h +++ b/sys/contrib/dev/rtw88/rtw8822c.h @@ -1,420 +1,426 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* Copyright(c) 2018-2019 Realtek Corporation */ #ifndef __RTW8822C_H__ #define __RTW8822C_H__ #include struct rtw8822cu_efuse { u8 res0[0x30]; /* 0x120 */ u8 vid[2]; /* 0x150 */ u8 pid[2]; u8 res1[3]; u8 mac_addr[ETH_ALEN]; /* 0x157 */ u8 res2[0x3d]; }; +struct rtw8822cs_efuse { + u8 res0[0x4a]; /* 0x120 */ + u8 mac_addr[ETH_ALEN]; /* 0x16a */ +} __packed; + struct rtw8822ce_efuse { u8 mac_addr[ETH_ALEN]; /* 0x120 */ u8 vender_id[2]; u8 device_id[2]; u8 sub_vender_id[2]; u8 sub_device_id[2]; u8 pmc[2]; u8 exp_device_cap[2]; u8 msi_cap; u8 ltr_cap; /* 0x133 */ u8 exp_link_control[2]; u8 link_cap[4]; u8 link_control[2]; u8 serial_number[8]; u8 res0:2; /* 0x144 */ u8 ltr_en:1; u8 res1:2; u8 obff:2; u8 res2:3; u8 obff_cap:2; u8 res3:4; u8 class_code[3]; u8 res4; u8 pci_pm_L1_2_supp:1; u8 pci_pm_L1_1_supp:1; u8 aspm_pm_L1_2_supp:1; u8 aspm_pm_L1_1_supp:1; u8 L1_pm_substates_supp:1; u8 res5:3; u8 port_common_mode_restore_time; u8 port_t_power_on_scale:2; u8 res6:1; u8 port_t_power_on_value:5; u8 res7; }; struct rtw8822c_efuse { __le16 rtl_id; u8 res0[0x0e]; /* power index for four RF paths */ struct rtw_txpwr_idx txpwr_idx_table[4]; u8 channel_plan; /* 0xb8 */ u8 xtal_k; u8 res1; u8 iqk_lck; u8 res2[5]; /* 0xbc */ u8 rf_board_option; u8 rf_feature_option; u8 rf_bt_setting; u8 eeprom_version; u8 eeprom_customer_id; u8 tx_bb_swing_setting_2g; u8 tx_bb_swing_setting_5g; u8 tx_pwr_calibrate_rate; u8 rf_antenna_option; /* 0xc9 */ u8 rfe_option; u8 country_code[2]; u8 res3[3]; u8 path_a_thermal; /* 0xd0 */ u8 path_b_thermal; u8 res4[2]; u8 rx_gain_gap_2g_ofdm; u8 res5; u8 rx_gain_gap_2g_cck; u8 res6; u8 rx_gain_gap_5gl; u8 res7; u8 rx_gain_gap_5gm; u8 res8; u8 rx_gain_gap_5gh; u8 res9; u8 res10[0x42]; union { - struct rtw8822cu_efuse u; struct rtw8822ce_efuse e; + struct rtw8822cu_efuse u; + struct rtw8822cs_efuse s; }; }; enum rtw8822c_dpk_agc_phase { RTW_DPK_GAIN_CHECK, RTW_DPK_GAIN_LARGE, RTW_DPK_GAIN_LESS, RTW_DPK_GL_LARGE, RTW_DPK_GL_LESS, RTW_DPK_LOSS_CHECK, RTW_DPK_AGC_OUT, }; enum rtw8822c_dpk_one_shot_action { RTW_DPK_CAL_PWR, RTW_DPK_GAIN_LOSS, RTW_DPK_DO_DPK, RTW_DPK_DPK_ON, RTW_DPK_DAGC, RTW_DPK_ACTION_MAX }; void rtw8822c_parse_tbl_dpk(struct rtw_dev *rtwdev, const struct rtw_table *tbl); extern const struct rtw_chip_info rtw8822c_hw_spec; #define RTW_DECL_TABLE_DPK(name) \ const struct rtw_table name ## _tbl = { \ .data = name, \ .size = ARRAY_SIZE(name), \ .parse = rtw8822c_parse_tbl_dpk, \ } #define DACK_PATH_8822C 2 #define DACK_REG_8822C 16 #define DACK_RF_8822C 1 #define DACK_SN_8822C 100 /* phy status page0 */ #define GET_PHY_STAT_P0_PWDB_A(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x00), GENMASK(15, 8)) #define GET_PHY_STAT_P0_PWDB_B(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x04), GENMASK(7, 0)) #define GET_PHY_STAT_P0_GAIN_A(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x00), GENMASK(21, 16)) #define GET_PHY_STAT_P0_CHANNEL(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x01), GENMASK(23, 16)) #define GET_PHY_STAT_P0_GAIN_B(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x04), GENMASK(29, 24)) /* phy status page1 */ #define GET_PHY_STAT_P1_PWDB_A(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x00), GENMASK(15, 8)) #define GET_PHY_STAT_P1_PWDB_B(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x00), GENMASK(23, 16)) #define GET_PHY_STAT_P1_L_RXSC(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x01), GENMASK(11, 8)) #define GET_PHY_STAT_P1_HT_RXSC(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x01), GENMASK(15, 12)) #define GET_PHY_STAT_P1_CHANNEL(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x01), GENMASK(23, 16)) #define GET_PHY_STAT_P1_RXEVM_A(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x04), GENMASK(7, 0)) #define GET_PHY_STAT_P1_RXEVM_B(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x04), GENMASK(15, 8)) #define GET_PHY_STAT_P1_CFO_TAIL_A(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x05), GENMASK(7, 0)) #define GET_PHY_STAT_P1_CFO_TAIL_B(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x05), GENMASK(15, 8)) #define GET_PHY_STAT_P1_RXSNR_A(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x06), GENMASK(7, 0)) #define GET_PHY_STAT_P1_RXSNR_B(phy_stat) \ le32_get_bits(*((__le32 *)(phy_stat) + 0x06), GENMASK(15, 8)) #define RTW8822C_EDCCA_MAX 0x7f #define REG_ANAPARLDO_POW_MAC 0x0029 #define BIT_LDOE25_PON BIT(0) #define XCAP_MASK GENMASK(6, 0) #define CFO_TRK_ENABLE_TH 20 #define CFO_TRK_STOP_TH 10 #define CFO_TRK_ADJ_TH 10 #define REG_TXDFIR0 0x808 #define REG_DFIRBW 0x810 #define REG_ANTMAP0 0x820 #define BIT_ANT_PATH GENMASK(1, 0) #define REG_ANTMAP 0x824 #define REG_EDCCA_DECISION 0x844 #define BIT_EDCCA_OPTION GENMASK(30, 29) #define REG_DYMPRITH 0x86c #define REG_DYMENTH0 0x870 #define REG_DYMENTH 0x874 #define REG_SBD 0x88c #define BITS_SUBTUNE GENMASK(15, 12) #define REG_DYMTHMIN 0x8a4 #define REG_TXBWCTL 0x9b0 #define REG_TXCLK 0x9b4 #define REG_SCOTRK 0xc30 #define REG_MRCM 0xc38 #define REG_AGCSWSH 0xc44 #define REG_ANTWTPD 0xc54 #define REG_PT_CHSMO 0xcbc #define BIT_PT_OPT BIT(21) #define REG_ORITXCODE 0x1800 #define BIT_PATH_EN BIT(31) #define REG_3WIRE 0x180c #define BIT_DIS_SHARERX_TXGAT BIT(27) #define BIT_3WIRE_TX_EN BIT(0) #define BIT_3WIRE_RX_EN BIT(1) #define BIT_3WIRE_EN GENMASK(1, 0) #define BIT_3WIRE_PI_ON BIT(28) #define REG_ANAPAR_A 0x1830 #define BIT_ANAPAR_UPDATE BIT(29) #define REG_RFTXEN_GCK_A 0x1864 #define BIT_RFTXEN_GCK_FORCE_ON BIT(31) #define REG_DIS_SHARE_RX_A 0x186c #define BIT_TX_SCALE_0DB BIT(7) #define REG_RXAGCCTL0 0x18ac #define BITS_RXAGC_CCK GENMASK(15, 12) #define BITS_RXAGC_OFDM GENMASK(8, 4) #define REG_DCKA_I_0 0x18bc #define REG_DCKA_I_1 0x18c0 #define REG_DCKA_Q_0 0x18d8 #define REG_DCKA_Q_1 0x18dc #define REG_CCKSB 0x1a00 #define BIT_BBMODE GENMASK(2, 1) #define REG_RXCCKSEL 0x1a04 #define REG_BGCTRL 0x1a14 #define BITS_RX_IQ_WEIGHT (BIT(8) | BIT(9)) #define REG_TXF0 0x1a20 #define REG_TXF1 0x1a24 #define REG_TXF2 0x1a28 #define REG_CCANRX 0x1a2c #define BIT_CCK_FA_RST (BIT(14) | BIT(15)) #define BIT_OFDM_FA_RST (BIT(12) | BIT(13)) #define REG_CCK_FACNT 0x1a5c #define REG_CCKTXONLY 0x1a80 #define BIT_BB_CCK_CHECK_EN BIT(18) #define REG_TXF3 0x1a98 #define REG_TXF4 0x1a9c #define REG_TXF5 0x1aa0 #define REG_TXF6 0x1aac #define REG_TXF7 0x1ab0 #define REG_CCK_SOURCE 0x1abc #define BIT_NBI_EN BIT(30) #define REG_NCTL0 0x1b00 #define BIT_SEL_PATH GENMASK(2, 1) #define BIT_SUBPAGE GENMASK(3, 0) #define REG_DPD_CTL0_S0 0x1b04 #define BIT_GS_PWSF GENMASK(27, 0) #define REG_DPD_CTL1_S0 0x1b08 #define BIT_DPD_EN BIT(31) #define BIT_PS_EN BIT(7) #define REG_IQKSTAT 0x1b10 #define REG_IQK_CTL1 0x1b20 #define BIT_TX_CFIR GENMASK(31, 30) #define BIT_CFIR_EN GENMASK(26, 24) #define BIT_BYPASS_DPD BIT(25) #define REG_TX_TONE_IDX 0x1b2c #define REG_DPD_LUT0 0x1b44 #define BIT_GLOSS_DB GENMASK(14, 12) #define REG_DPD_CTL0_S1 0x1b5c #define REG_DPD_CTL1_S1 0x1b60 #define REG_DPD_AGC 0x1b67 #define REG_TABLE_SEL 0x1b98 #define BIT_I_GAIN GENMASK(19, 16) #define BIT_GAIN_RST BIT(15) #define BIT_Q_GAIN_SEL GENMASK(14, 12) #define BIT_Q_GAIN GENMASK(11, 0) #define REG_TX_GAIN_SET 0x1b9c #define BIT_GAPK_RPT_IDX GENMASK(11, 8) #define REG_DPD_CTL0 0x1bb4 #define REG_SINGLE_TONE_SW 0x1bb8 #define BIT_IRQ_TEST_MODE BIT(20) #define REG_R_CONFIG 0x1bcc #define BIT_INNER_LB BIT(21) #define BIT_IQ_SWITCH GENMASK(5, 0) #define BIT_2G_SWING 0x2d #define BIT_5G_SWING 0x36 #define REG_RXSRAM_CTL 0x1bd4 #define BIT_RPT_EN BIT(21) #define BIT_RPT_SEL GENMASK(20, 16) #define BIT_DPD_CLK GENMASK(7, 4) #define REG_DPD_CTL11 0x1be4 #define REG_DPD_CTL12 0x1be8 #define REG_DPD_CTL15 0x1bf4 #define REG_DPD_CTL16 0x1bf8 #define REG_STAT_RPT 0x1bfc #define BIT_RPT_DGAIN GENMASK(27, 16) #define BIT_GAPK_RPT0 GENMASK(3, 0) #define BIT_GAPK_RPT1 GENMASK(7, 4) #define BIT_GAPK_RPT2 GENMASK(11, 8) #define BIT_GAPK_RPT3 GENMASK(15, 12) #define BIT_GAPK_RPT4 GENMASK(19, 16) #define BIT_GAPK_RPT5 GENMASK(23, 20) #define BIT_GAPK_RPT6 GENMASK(27, 24) #define BIT_GAPK_RPT7 GENMASK(31, 28) #define REG_TXANT 0x1c28 #define REG_IQK_CTRL 0x1c38 #define REG_ENCCK 0x1c3c #define BIT_CCK_BLK_EN BIT(1) #define BIT_CCK_OFDM_BLK_EN (BIT(0) | BIT(1)) #define REG_CCAMSK 0x1c80 #define REG_RSTB 0x1c90 #define BIT_RSTB_3WIRE BIT(8) #define REG_CH_DELAY_EXTR2 0x1cd0 #define BIT_TST_IQK2SET_SRC BIT(31) #define BIT_EN_IOQ_IQK_DPK BIT(30) #define BIT_IQK_DPK_RESET_SRC BIT(29) #define BIT_IQK_DPK_CLOCK_SRC BIT(28) #define REG_RX_BREAK 0x1d2c #define BIT_COM_RX_GCK_EN BIT(31) #define REG_RXFNCTL 0x1d30 #define REG_CCA_OFF 0x1d58 #define BIT_CCA_ON_BY_PW GENMASK(11, 3) #define REG_RXIGI 0x1d70 #define REG_ENFN 0x1e24 #define BIT_IQK_DPK_EN BIT(17) #define REG_TXANTSEG 0x1e28 #define BIT_ANTSEG GENMASK(3, 0) #define REG_TXLGMAP 0x1e2c #define REG_CCKPATH 0x1e5c #define REG_TX_FIFO 0x1e70 #define BIT_STOP_TX GENMASK(3, 0) #define REG_CNT_CTRL 0x1eb4 #define BIT_ALL_CNT_RST BIT(25) #define REG_OFDM_FACNT 0x2d00 #define REG_OFDM_FACNT1 0x2d04 #define REG_OFDM_FACNT2 0x2d08 #define REG_OFDM_FACNT3 0x2d0c #define REG_OFDM_FACNT4 0x2d10 #define REG_OFDM_FACNT5 0x2d20 #define REG_RPT_CIP 0x2d9c #define BIT_RPT_CIP_STATUS GENMASK(7, 0) #define REG_OFDM_TXCNT 0x2de0 #define REG_ORITXCODE2 0x4100 #define REG_3WIRE2 0x410c #define REG_ANAPAR_B 0x4130 #define REG_RFTXEN_GCK_B 0x4164 #define REG_DIS_SHARE_RX_B 0x416c #define BIT_EXT_TIA_BW BIT(1) #define REG_RXAGCCTL 0x41ac #define REG_DCKB_I_0 0x41bc #define REG_DCKB_I_1 0x41c0 #define REG_DCKB_Q_0 0x41d8 #define REG_DCKB_Q_1 0x41dc #define RF_MODE_TRXAGC 0x00 #define BIT_RF_MODE GENMASK(19, 16) #define BIT_RXAGC GENMASK(9, 5) #define BIT_TXAGC GENMASK(4, 0) #define RF_RXAGC_OFFSET 0x19 #define RF_BW_TRXBB 0x1a #define BIT_TX_CCK_IND BIT(16) #define BIT_BW_TXBB GENMASK(14, 12) #define BIT_BW_RXBB GENMASK(11, 10) #define BIT_DBG_CCK_CCA BIT(1) #define RF_TX_GAIN_OFFSET 0x55 #define BIT_BB_GAIN GENMASK(18, 14) #define BIT_RF_GAIN GENMASK(4, 2) #define RF_TX_GAIN 0x56 #define BIT_GAIN_TXBB GENMASK(4, 0) #define RF_IDAC 0x58 #define BIT_TX_MODE GENMASK(19, 8) #define RF_TX_RESULT 0x5f #define BIT_GAIN_TX_PAD_H GENMASK(11, 8) #define BIT_GAIN_TX_PAD_L GENMASK(7, 4) #define RF_PA 0x60 #define RF_PABIAS_2G_MASK GENMASK(15, 12) #define RF_PABIAS_5G_MASK GENMASK(19, 16) #define RF_TXA_LB_SW 0x63 #define BIT_TXA_LB_ATT GENMASK(15, 14) #define BIT_LB_SW GENMASK(13, 12) #define BIT_LB_ATT GENMASK(4, 2) #define RF_RXG_GAIN 0x87 #define BIT_RXG_GAIN BIT(18) #define RF_RXA_MIX_GAIN 0x8a #define BIT_RXA_MIX_GAIN GENMASK(4, 3) #define RF_EXT_TIA_BW 0x8f #define BIT_PW_EXT_TIA BIT(1) #define RF_DIS_BYPASS_TXBB 0x9e #define BIT_TXBB BIT(10) #define BIT_TIA_BYPASS BIT(5) #define RF_DEBUG 0xde #define BIT_DE_PWR_TRIM BIT(19) #define BIT_DE_TX_GAIN BIT(16) #define BIT_DE_TRXBW BIT(2) #define PPG_THERMAL_B 0x1b0 #define RF_THEMAL_MASK GENMASK(19, 16) #define PPG_2GH_TXAB 0x1d2 #define PPG_2G_A_MASK GENMASK(3, 0) #define PPG_2G_B_MASK GENMASK(7, 4) #define PPG_2GL_TXAB 0x1d4 #define PPG_PABIAS_2GB 0x1d5 #define PPG_PABIAS_2GA 0x1d6 #define PPG_PABIAS_MASK GENMASK(3, 0) #define PPG_PABIAS_5GB 0x1d7 #define PPG_PABIAS_5GA 0x1d8 #define PPG_5G_MASK GENMASK(4, 0) #define PPG_5GH1_TXB 0x1db #define PPG_5GH1_TXA 0x1dc #define PPG_5GM2_TXB 0x1df #define PPG_5GM2_TXA 0x1e0 #define PPG_5GM1_TXB 0x1e3 #define PPG_5GM1_TXA 0x1e4 #define PPG_5GL2_TXB 0x1e7 #define PPG_5GL2_TXA 0x1e8 #define PPG_5GL1_TXB 0x1eb #define PPG_5GL1_TXA 0x1ec #define PPG_2GM_TXAB 0x1ee #define PPG_THERMAL_A 0x1ef #endif diff --git a/sys/contrib/dev/rtw88/rtw8822cs.c b/sys/contrib/dev/rtw88/rtw8822cs.c new file mode 100644 index 000000000000..975e81c824f2 --- /dev/null +++ b/sys/contrib/dev/rtw88/rtw8822cs.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) Martin Blumenstingl + */ + +#include +#include +#include +#include "main.h" +#include "rtw8822c.h" +#include "sdio.h" + +static const struct sdio_device_id rtw_8822cs_id_table[] = { + { + SDIO_DEVICE(SDIO_VENDOR_ID_REALTEK, + SDIO_DEVICE_ID_REALTEK_RTW8822CS), + .driver_data = (kernel_ulong_t)&rtw8822c_hw_spec, + }, + {} +}; +MODULE_DEVICE_TABLE(sdio, rtw_8822cs_id_table); + +static struct sdio_driver rtw_8822cs_driver = { + .name = "rtw_8822cs", + .probe = rtw_sdio_probe, + .remove = rtw_sdio_remove, + .id_table = rtw_8822cs_id_table, + .drv = { + .pm = &rtw_sdio_pm_ops, + .shutdown = rtw_sdio_shutdown, + } +}; +module_sdio_driver(rtw_8822cs_driver); + +MODULE_AUTHOR("Martin Blumenstingl "); +MODULE_DESCRIPTION("Realtek 802.11ac wireless 8822cs driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sys/contrib/dev/rtw88/rtw8822cu.c b/sys/contrib/dev/rtw88/rtw8822cu.c new file mode 100644 index 000000000000..5b81eb788cce --- /dev/null +++ b/sys/contrib/dev/rtw88/rtw8822cu.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2018-2019 Realtek Corporation + */ + +#include +#include +#include "main.h" +#include "rtw8822c.h" +#include "usb.h" + +static const struct usb_device_id rtw_8822cu_id_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xc82c, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822c_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xc812, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822c_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xc82e, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822c_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xd820, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822c_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0xd82b, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822c_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x13b1, 0x0043, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822c_hw_spec) }, /* Alpha - Alpha */ + {}, +}; +MODULE_DEVICE_TABLE(usb, rtw_8822cu_id_table); + +static int rtw8822bu_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return rtw_usb_probe(intf, id); +} + +static struct usb_driver rtw_8822cu_driver = { + .name = "rtw_8822cu", + .id_table = rtw_8822cu_id_table, + .probe = rtw8822bu_probe, + .disconnect = rtw_usb_disconnect, +#if defined(__FreeBSD__) && defined(__notyet__) + .bsddriver.name = KBUILD_MODNAME, +#endif +}; +module_usb_driver(rtw_8822cu_driver); + +MODULE_AUTHOR("Realtek Corporation"); +MODULE_DESCRIPTION("Realtek 802.11ac wireless 8822cu driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sys/contrib/dev/rtw88/sdio.c b/sys/contrib/dev/rtw88/sdio.c new file mode 100644 index 000000000000..2c1fb2dabd40 --- /dev/null +++ b/sys/contrib/dev/rtw88/sdio.c @@ -0,0 +1,1404 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright (C) 2021 Martin Blumenstingl + * Copyright (C) 2021 Jernej Skrabec + * + * Based on rtw88/pci.c: + * Copyright(c) 2018-2019 Realtek Corporation + */ + +#include +#include +#include +#include "main.h" +#include "debug.h" +#include "fw.h" +#include "ps.h" +#include "reg.h" +#include "rx.h" +#include "sdio.h" +#include "tx.h" + +#define RTW_SDIO_INDIRECT_RW_RETRIES 50 + +static bool rtw_sdio_is_bus_addr(u32 addr) +{ + return !!(addr & RTW_SDIO_BUS_MSK); +} + +static bool rtw_sdio_bus_claim_needed(struct rtw_sdio *rtwsdio) +{ + return !rtwsdio->irq_thread || + rtwsdio->irq_thread != current; +} + +static u32 rtw_sdio_to_bus_offset(struct rtw_dev *rtwdev, u32 addr) +{ + switch (addr & RTW_SDIO_BUS_MSK) { + case WLAN_IOREG_OFFSET: + addr &= WLAN_IOREG_REG_MSK; + addr |= FIELD_PREP(REG_SDIO_CMD_ADDR_MSK, + REG_SDIO_CMD_ADDR_MAC_REG); + break; + case SDIO_LOCAL_OFFSET: + addr &= SDIO_LOCAL_REG_MSK; + addr |= FIELD_PREP(REG_SDIO_CMD_ADDR_MSK, + REG_SDIO_CMD_ADDR_SDIO_REG); + break; + default: + rtw_warn(rtwdev, "Cannot convert addr 0x%08x to bus offset", + addr); + } + + return addr; +} + +static bool rtw_sdio_use_memcpy_io(struct rtw_dev *rtwdev, u32 addr, + u8 alignment) +{ + return IS_ALIGNED(addr, alignment) && + test_bit(RTW_FLAG_POWERON, rtwdev->flags); +} + +static void rtw_sdio_writel(struct rtw_dev *rtwdev, u32 val, u32 addr, + int *err_ret) +{ + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; + u8 buf[4]; + int i; + + if (rtw_sdio_use_memcpy_io(rtwdev, addr, 4)) { + sdio_writel(rtwsdio->sdio_func, val, addr, err_ret); + return; + } + + *(__le32 *)buf = cpu_to_le32(val); + + for (i = 0; i < 4; i++) { + sdio_writeb(rtwsdio->sdio_func, buf[i], addr + i, err_ret); + if (*err_ret) + return; + } +} + +static void rtw_sdio_writew(struct rtw_dev *rtwdev, u16 val, u32 addr, + int *err_ret) +{ + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; + u8 buf[2]; + int i; + + *(__le16 *)buf = cpu_to_le16(val); + + for (i = 0; i < 2; i++) { + sdio_writeb(rtwsdio->sdio_func, buf[i], addr + i, err_ret); + if (*err_ret) + return; + } +} + +static u32 rtw_sdio_readl(struct rtw_dev *rtwdev, u32 addr, int *err_ret) +{ + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; + u8 buf[4]; + int i; + + if (rtw_sdio_use_memcpy_io(rtwdev, addr, 4)) + return sdio_readl(rtwsdio->sdio_func, addr, err_ret); + + for (i = 0; i < 4; i++) { + buf[i] = sdio_readb(rtwsdio->sdio_func, addr + i, err_ret); + if (*err_ret) + return 0; + } + + return le32_to_cpu(*(__le32 *)buf); +} + +static u16 rtw_sdio_readw(struct rtw_dev *rtwdev, u32 addr, int *err_ret) +{ + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; + u8 buf[2]; + int i; + + for (i = 0; i < 2; i++) { + buf[i] = sdio_readb(rtwsdio->sdio_func, addr + i, err_ret); + if (*err_ret) + return 0; + } + + return le16_to_cpu(*(__le16 *)buf); +} + +static u32 rtw_sdio_to_io_address(struct rtw_dev *rtwdev, u32 addr, + bool direct) +{ + if (!direct) + return addr; + + if (!rtw_sdio_is_bus_addr(addr)) + addr |= WLAN_IOREG_OFFSET; + + return rtw_sdio_to_bus_offset(rtwdev, addr); +} + +static bool rtw_sdio_use_direct_io(struct rtw_dev *rtwdev, u32 addr) +{ + return !rtw_sdio_is_sdio30_supported(rtwdev) || + rtw_sdio_is_bus_addr(addr); +} + +static int rtw_sdio_indirect_reg_cfg(struct rtw_dev *rtwdev, u32 addr, u32 cfg) +{ + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; + unsigned int retry; + u32 reg_cfg; + int ret; + u8 tmp; + + reg_cfg = rtw_sdio_to_bus_offset(rtwdev, REG_SDIO_INDIRECT_REG_CFG); + + rtw_sdio_writel(rtwdev, addr | cfg | BIT_SDIO_INDIRECT_REG_CFG_UNK20, + reg_cfg, &ret); + if (ret) + return ret; + + for (retry = 0; retry < RTW_SDIO_INDIRECT_RW_RETRIES; retry++) { + tmp = sdio_readb(rtwsdio->sdio_func, reg_cfg + 2, &ret); + if (!ret && (tmp & BIT(4))) + return 0; + } + + return -ETIMEDOUT; +} + +static u8 rtw_sdio_indirect_read8(struct rtw_dev *rtwdev, u32 addr, + int *err_ret) +{ + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; + u32 reg_data; + + *err_ret = rtw_sdio_indirect_reg_cfg(rtwdev, addr, + BIT_SDIO_INDIRECT_REG_CFG_READ); + if (*err_ret) + return 0; + + reg_data = rtw_sdio_to_bus_offset(rtwdev, REG_SDIO_INDIRECT_REG_DATA); + return sdio_readb(rtwsdio->sdio_func, reg_data, err_ret); +} + +static int rtw_sdio_indirect_read_bytes(struct rtw_dev *rtwdev, u32 addr, + u8 *buf, int count) +{ + int i, ret = 0; + + for (i = 0; i < count; i++) { + buf[i] = rtw_sdio_indirect_read8(rtwdev, addr + i, &ret); + if (ret) + break; + } + + return ret; +} + +static u16 rtw_sdio_indirect_read16(struct rtw_dev *rtwdev, u32 addr, + int *err_ret) +{ + u32 reg_data; + u8 buf[2]; + + if (!IS_ALIGNED(addr, 2)) { + *err_ret = rtw_sdio_indirect_read_bytes(rtwdev, addr, buf, 2); + if (*err_ret) + return 0; + + return le16_to_cpu(*(__le16 *)buf); + } + + *err_ret = rtw_sdio_indirect_reg_cfg(rtwdev, addr, + BIT_SDIO_INDIRECT_REG_CFG_READ); + if (*err_ret) + return 0; + + reg_data = rtw_sdio_to_bus_offset(rtwdev, REG_SDIO_INDIRECT_REG_DATA); + return rtw_sdio_readw(rtwdev, reg_data, err_ret); +} + +static u32 rtw_sdio_indirect_read32(struct rtw_dev *rtwdev, u32 addr, + int *err_ret) +{ + u32 reg_data; + u8 buf[4]; + + if (!IS_ALIGNED(addr, 4)) { + *err_ret = rtw_sdio_indirect_read_bytes(rtwdev, addr, buf, 4); + if (*err_ret) + return 0; + + return le32_to_cpu(*(__le32 *)buf); + } + + *err_ret = rtw_sdio_indirect_reg_cfg(rtwdev, addr, + BIT_SDIO_INDIRECT_REG_CFG_READ); + if (*err_ret) + return 0; + + reg_data = rtw_sdio_to_bus_offset(rtwdev, REG_SDIO_INDIRECT_REG_DATA); + return rtw_sdio_readl(rtwdev, reg_data, err_ret); +} + +static u8 rtw_sdio_read8(struct rtw_dev *rtwdev, u32 addr) +{ + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; + bool direct, bus_claim; + int ret; + u8 val; + + direct = rtw_sdio_use_direct_io(rtwdev, addr); + addr = rtw_sdio_to_io_address(rtwdev, addr, direct); + bus_claim = rtw_sdio_bus_claim_needed(rtwsdio); + + if (bus_claim) + sdio_claim_host(rtwsdio->sdio_func); + + if (direct) + val = sdio_readb(rtwsdio->sdio_func, addr, &ret); + else + val = rtw_sdio_indirect_read8(rtwdev, addr, &ret); + + if (bus_claim) + sdio_release_host(rtwsdio->sdio_func); + + if (ret) + rtw_warn(rtwdev, "sdio read8 failed (0x%x): %d", addr, ret); + + return val; +} + +static u16 rtw_sdio_read16(struct rtw_dev *rtwdev, u32 addr) +{ + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; + bool direct, bus_claim; + int ret; + u16 val; + + direct = rtw_sdio_use_direct_io(rtwdev, addr); + addr = rtw_sdio_to_io_address(rtwdev, addr, direct); + bus_claim = rtw_sdio_bus_claim_needed(rtwsdio); + + if (bus_claim) + sdio_claim_host(rtwsdio->sdio_func); + + if (direct) + val = rtw_sdio_readw(rtwdev, addr, &ret); + else + val = rtw_sdio_indirect_read16(rtwdev, addr, &ret); + + if (bus_claim) + sdio_release_host(rtwsdio->sdio_func); + + if (ret) + rtw_warn(rtwdev, "sdio read16 failed (0x%x): %d", addr, ret); + + return val; +} + +static u32 rtw_sdio_read32(struct rtw_dev *rtwdev, u32 addr) +{ + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; + bool direct, bus_claim; + u32 val; + int ret; + + direct = rtw_sdio_use_direct_io(rtwdev, addr); + addr = rtw_sdio_to_io_address(rtwdev, addr, direct); + bus_claim = rtw_sdio_bus_claim_needed(rtwsdio); + + if (bus_claim) + sdio_claim_host(rtwsdio->sdio_func); + + if (direct) + val = rtw_sdio_readl(rtwdev, addr, &ret); + else + val = rtw_sdio_indirect_read32(rtwdev, addr, &ret); + + if (bus_claim) + sdio_release_host(rtwsdio->sdio_func); + + if (ret) + rtw_warn(rtwdev, "sdio read32 failed (0x%x): %d", addr, ret); + + return val; +} + +static void rtw_sdio_indirect_write8(struct rtw_dev *rtwdev, u8 val, u32 addr, + int *err_ret) +{ + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; + u32 reg_data; + + reg_data = rtw_sdio_to_bus_offset(rtwdev, REG_SDIO_INDIRECT_REG_DATA); + sdio_writeb(rtwsdio->sdio_func, val, reg_data, err_ret); + if (*err_ret) + return; + + *err_ret = rtw_sdio_indirect_reg_cfg(rtwdev, addr, + BIT_SDIO_INDIRECT_REG_CFG_WRITE); +} + +static void rtw_sdio_indirect_write16(struct rtw_dev *rtwdev, u16 val, u32 addr, + int *err_ret) +{ + u32 reg_data; + + if (!IS_ALIGNED(addr, 2)) { + addr = rtw_sdio_to_io_address(rtwdev, addr, true); + rtw_sdio_writew(rtwdev, val, addr, err_ret); + return; + } + + reg_data = rtw_sdio_to_bus_offset(rtwdev, REG_SDIO_INDIRECT_REG_DATA); + rtw_sdio_writew(rtwdev, val, reg_data, err_ret); + if (*err_ret) + return; + + *err_ret = rtw_sdio_indirect_reg_cfg(rtwdev, addr, + BIT_SDIO_INDIRECT_REG_CFG_WRITE | + BIT_SDIO_INDIRECT_REG_CFG_WORD); +} + +static void rtw_sdio_indirect_write32(struct rtw_dev *rtwdev, u32 val, + u32 addr, int *err_ret) +{ + u32 reg_data; + + if (!IS_ALIGNED(addr, 4)) { + addr = rtw_sdio_to_io_address(rtwdev, addr, true); + rtw_sdio_writel(rtwdev, val, addr, err_ret); + return; + } + + reg_data = rtw_sdio_to_bus_offset(rtwdev, REG_SDIO_INDIRECT_REG_DATA); + rtw_sdio_writel(rtwdev, val, reg_data, err_ret); + + *err_ret = rtw_sdio_indirect_reg_cfg(rtwdev, addr, + BIT_SDIO_INDIRECT_REG_CFG_WRITE | + BIT_SDIO_INDIRECT_REG_CFG_DWORD); +} + +static void rtw_sdio_write8(struct rtw_dev *rtwdev, u32 addr, u8 val) +{ + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; + bool direct, bus_claim; + int ret; + + direct = rtw_sdio_use_direct_io(rtwdev, addr); + addr = rtw_sdio_to_io_address(rtwdev, addr, direct); + bus_claim = rtw_sdio_bus_claim_needed(rtwsdio); + + if (bus_claim) + sdio_claim_host(rtwsdio->sdio_func); + + if (direct) + sdio_writeb(rtwsdio->sdio_func, val, addr, &ret); + else + rtw_sdio_indirect_write8(rtwdev, val, addr, &ret); + + if (bus_claim) + sdio_release_host(rtwsdio->sdio_func); + + if (ret) + rtw_warn(rtwdev, "sdio write8 failed (0x%x): %d", addr, ret); +} + +static void rtw_sdio_write16(struct rtw_dev *rtwdev, u32 addr, u16 val) +{ + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; + bool direct, bus_claim; + int ret; + + direct = rtw_sdio_use_direct_io(rtwdev, addr); + addr = rtw_sdio_to_io_address(rtwdev, addr, direct); + bus_claim = rtw_sdio_bus_claim_needed(rtwsdio); + + if (bus_claim) + sdio_claim_host(rtwsdio->sdio_func); + + if (direct) + rtw_sdio_writew(rtwdev, val, addr, &ret); + else + rtw_sdio_indirect_write16(rtwdev, val, addr, &ret); + + if (bus_claim) + sdio_release_host(rtwsdio->sdio_func); + + if (ret) + rtw_warn(rtwdev, "sdio write16 failed (0x%x): %d", addr, ret); +} + +static void rtw_sdio_write32(struct rtw_dev *rtwdev, u32 addr, u32 val) +{ + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; + bool direct, bus_claim; + int ret; + + direct = rtw_sdio_use_direct_io(rtwdev, addr); + addr = rtw_sdio_to_io_address(rtwdev, addr, direct); + bus_claim = rtw_sdio_bus_claim_needed(rtwsdio); + + if (bus_claim) + sdio_claim_host(rtwsdio->sdio_func); + + if (direct) + rtw_sdio_writel(rtwdev, val, addr, &ret); + else + rtw_sdio_indirect_write32(rtwdev, val, addr, &ret); + + if (bus_claim) + sdio_release_host(rtwsdio->sdio_func); + + if (ret) + rtw_warn(rtwdev, "sdio write32 failed (0x%x): %d", addr, ret); +} + +static u32 rtw_sdio_get_tx_addr(struct rtw_dev *rtwdev, size_t size, + enum rtw_tx_queue_type queue) +{ + u32 txaddr; + + switch (queue) { + case RTW_TX_QUEUE_BCN: + case RTW_TX_QUEUE_H2C: + case RTW_TX_QUEUE_HI0: + txaddr = FIELD_PREP(REG_SDIO_CMD_ADDR_MSK, + REG_SDIO_CMD_ADDR_TXFF_HIGH); + break; + case RTW_TX_QUEUE_VI: + case RTW_TX_QUEUE_VO: + txaddr = FIELD_PREP(REG_SDIO_CMD_ADDR_MSK, + REG_SDIO_CMD_ADDR_TXFF_NORMAL); + break; + case RTW_TX_QUEUE_BE: + case RTW_TX_QUEUE_BK: + txaddr = FIELD_PREP(REG_SDIO_CMD_ADDR_MSK, + REG_SDIO_CMD_ADDR_TXFF_LOW); + break; + case RTW_TX_QUEUE_MGMT: + txaddr = FIELD_PREP(REG_SDIO_CMD_ADDR_MSK, + REG_SDIO_CMD_ADDR_TXFF_EXTRA); + break; + default: + rtw_warn(rtwdev, "Unsupported queue for TX addr: 0x%02x\n", + queue); + return 0; + } + + txaddr += DIV_ROUND_UP(size, 4); + + return txaddr; +}; + +static int rtw_sdio_read_port(struct rtw_dev *rtwdev, u8 *buf, size_t count) +{ + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; + bool bus_claim = rtw_sdio_bus_claim_needed(rtwsdio); + u32 rxaddr = rtwsdio->rx_addr++; + int ret; + + if (bus_claim) + sdio_claim_host(rtwsdio->sdio_func); + + ret = sdio_memcpy_fromio(rtwsdio->sdio_func, buf, + RTW_SDIO_ADDR_RX_RX0FF_GEN(rxaddr), count); + if (ret) + rtw_warn(rtwdev, + "Failed to read %zu byte(s) from SDIO port 0x%08x", + count, rxaddr); + + if (bus_claim) + sdio_release_host(rtwsdio->sdio_func); + + return ret; +} + +static int rtw_sdio_check_free_txpg(struct rtw_dev *rtwdev, u8 queue, + size_t count) +{ + unsigned int pages_free, pages_needed; + + if (rtw_chip_wcpu_11n(rtwdev)) { + u32 free_txpg; + + free_txpg = rtw_sdio_read32(rtwdev, REG_SDIO_FREE_TXPG); + + switch (queue) { + case RTW_TX_QUEUE_BCN: + case RTW_TX_QUEUE_H2C: + case RTW_TX_QUEUE_HI0: + case RTW_TX_QUEUE_MGMT: + /* high */ + pages_free = free_txpg & 0xff; + break; + case RTW_TX_QUEUE_VI: + case RTW_TX_QUEUE_VO: + /* normal */ + pages_free = (free_txpg >> 8) & 0xff; + break; + case RTW_TX_QUEUE_BE: + case RTW_TX_QUEUE_BK: + /* low */ + pages_free = (free_txpg >> 16) & 0xff; + break; + default: + rtw_warn(rtwdev, "Unknown mapping for queue %u\n", queue); + return -EINVAL; + } + + /* add the pages from the public queue */ + pages_free += (free_txpg >> 24) & 0xff; + } else { + u32 free_txpg[3]; + + free_txpg[0] = rtw_sdio_read32(rtwdev, REG_SDIO_FREE_TXPG); + free_txpg[1] = rtw_sdio_read32(rtwdev, REG_SDIO_FREE_TXPG + 4); + free_txpg[2] = rtw_sdio_read32(rtwdev, REG_SDIO_FREE_TXPG + 8); + + switch (queue) { + case RTW_TX_QUEUE_BCN: + case RTW_TX_QUEUE_H2C: + case RTW_TX_QUEUE_HI0: + /* high */ + pages_free = free_txpg[0] & 0xfff; + break; + case RTW_TX_QUEUE_VI: + case RTW_TX_QUEUE_VO: + /* normal */ + pages_free = (free_txpg[0] >> 16) & 0xfff; + break; + case RTW_TX_QUEUE_BE: + case RTW_TX_QUEUE_BK: + /* low */ + pages_free = free_txpg[1] & 0xfff; + break; + case RTW_TX_QUEUE_MGMT: + /* extra */ + pages_free = free_txpg[2] & 0xfff; + break; + default: + rtw_warn(rtwdev, "Unknown mapping for queue %u\n", queue); + return -EINVAL; + } + + /* add the pages from the public queue */ + pages_free += (free_txpg[1] >> 16) & 0xfff; + } + + pages_needed = DIV_ROUND_UP(count, rtwdev->chip->page_size); + + if (pages_needed > pages_free) { + rtw_dbg(rtwdev, RTW_DBG_SDIO, + "Not enough free pages (%u needed, %u free) in queue %u for %zu bytes\n", + pages_needed, pages_free, queue, count); + return -EBUSY; + } + + return 0; +} + +static int rtw_sdio_write_port(struct rtw_dev *rtwdev, struct sk_buff *skb, + enum rtw_tx_queue_type queue) +{ + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; + bool bus_claim; + size_t txsize; + u32 txaddr; + int ret; + + txaddr = rtw_sdio_get_tx_addr(rtwdev, skb->len, queue); + if (!txaddr) + return -EINVAL; + + txsize = sdio_align_size(rtwsdio->sdio_func, skb->len); + + ret = rtw_sdio_check_free_txpg(rtwdev, queue, txsize); + if (ret) + return ret; + + if (!IS_ALIGNED((unsigned long)skb->data, RTW_SDIO_DATA_PTR_ALIGN)) + rtw_warn(rtwdev, "Got unaligned SKB in %s() for queue %u\n", + __func__, queue); + + bus_claim = rtw_sdio_bus_claim_needed(rtwsdio); + + if (bus_claim) + sdio_claim_host(rtwsdio->sdio_func); + + ret = sdio_memcpy_toio(rtwsdio->sdio_func, txaddr, skb->data, txsize); + + if (bus_claim) + sdio_release_host(rtwsdio->sdio_func); + + if (ret) + rtw_warn(rtwdev, + "Failed to write %zu byte(s) to SDIO port 0x%08x", + txsize, txaddr); + + return ret; +} + +static void rtw_sdio_init(struct rtw_dev *rtwdev) +{ + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; + + rtwsdio->irq_mask = REG_SDIO_HIMR_RX_REQUEST | REG_SDIO_HIMR_CPWM1; +} + +static void rtw_sdio_enable_rx_aggregation(struct rtw_dev *rtwdev) +{ + u8 size, timeout; + + if (rtw_chip_wcpu_11n(rtwdev)) { + size = 0x6; + timeout = 0x6; + } else { + size = 0xff; + timeout = 0x1; + } + + /* Make the firmware honor the size limit configured below */ + rtw_write32_set(rtwdev, REG_RXDMA_AGG_PG_TH, BIT_EN_PRE_CALC); + + rtw_write8_set(rtwdev, REG_TXDMA_PQ_MAP, BIT_RXDMA_AGG_EN); + + rtw_write16(rtwdev, REG_RXDMA_AGG_PG_TH, + FIELD_PREP(BIT_RXDMA_AGG_PG_TH, size) | + FIELD_PREP(BIT_DMA_AGG_TO_V1, timeout)); + + rtw_write8_set(rtwdev, REG_RXDMA_MODE, BIT_DMA_MODE); +} + +static void rtw_sdio_enable_interrupt(struct rtw_dev *rtwdev) +{ + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; + + rtw_write32(rtwdev, REG_SDIO_HIMR, rtwsdio->irq_mask); +} + +static void rtw_sdio_disable_interrupt(struct rtw_dev *rtwdev) +{ + rtw_write32(rtwdev, REG_SDIO_HIMR, 0x0); +} + +static u8 rtw_sdio_get_tx_qsel(struct rtw_dev *rtwdev, struct sk_buff *skb, + u8 queue) +{ + switch (queue) { + case RTW_TX_QUEUE_BCN: + return TX_DESC_QSEL_BEACON; + case RTW_TX_QUEUE_H2C: + return TX_DESC_QSEL_H2C; + case RTW_TX_QUEUE_MGMT: + if (rtw_chip_wcpu_11n(rtwdev)) + return TX_DESC_QSEL_HIGH; + else + return TX_DESC_QSEL_MGMT; + case RTW_TX_QUEUE_HI0: + return TX_DESC_QSEL_HIGH; + default: + return skb->priority; + } +} + +static int rtw_sdio_setup(struct rtw_dev *rtwdev) +{ + /* nothing to do */ + return 0; +} + +static int rtw_sdio_start(struct rtw_dev *rtwdev) +{ + rtw_sdio_enable_rx_aggregation(rtwdev); + rtw_sdio_enable_interrupt(rtwdev); + + return 0; +} + +static void rtw_sdio_stop(struct rtw_dev *rtwdev) +{ + rtw_sdio_disable_interrupt(rtwdev); +} + +static void rtw_sdio_deep_ps_enter(struct rtw_dev *rtwdev) +{ + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; + bool tx_empty = true; + u8 queue; + + if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_TX_WAKE)) { + /* Deep PS state is not allowed to TX-DMA */ + for (queue = 0; queue < RTK_MAX_TX_QUEUE_NUM; queue++) { + /* BCN queue is rsvd page, does not have DMA interrupt + * H2C queue is managed by firmware + */ + if (queue == RTW_TX_QUEUE_BCN || + queue == RTW_TX_QUEUE_H2C) + continue; + + /* check if there is any skb DMAing */ + if (skb_queue_len(&rtwsdio->tx_queue[queue])) { + tx_empty = false; + break; + } + } + } + + if (!tx_empty) { + rtw_dbg(rtwdev, RTW_DBG_PS, + "TX path not empty, cannot enter deep power save state\n"); + return; + } + + set_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags); + rtw_power_mode_change(rtwdev, true); +} + +static void rtw_sdio_deep_ps_leave(struct rtw_dev *rtwdev) +{ + if (test_and_clear_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags)) + rtw_power_mode_change(rtwdev, false); +} + +static void rtw_sdio_deep_ps(struct rtw_dev *rtwdev, bool enter) +{ + if (enter && !test_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags)) + rtw_sdio_deep_ps_enter(rtwdev); + + if (!enter && test_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags)) + rtw_sdio_deep_ps_leave(rtwdev); +} + +static void rtw_sdio_tx_kick_off(struct rtw_dev *rtwdev) +{ + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; + + queue_work(rtwsdio->txwq, &rtwsdio->tx_handler_data->work); +} + +static void rtw_sdio_link_ps(struct rtw_dev *rtwdev, bool enter) +{ + /* nothing to do */ +} + +static void rtw_sdio_interface_cfg(struct rtw_dev *rtwdev) +{ + u32 val; + + rtw_read32(rtwdev, REG_SDIO_FREE_TXPG); + + val = rtw_read32(rtwdev, REG_SDIO_TX_CTRL); + val &= 0xfff8; + rtw_write32(rtwdev, REG_SDIO_TX_CTRL, val); +} + +static struct rtw_sdio_tx_data *rtw_sdio_get_tx_data(struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + BUILD_BUG_ON(sizeof(struct rtw_sdio_tx_data) > + sizeof(info->status.status_driver_data)); + + return (struct rtw_sdio_tx_data *)info->status.status_driver_data; +} + +static void rtw_sdio_tx_skb_prepare(struct rtw_dev *rtwdev, + struct rtw_tx_pkt_info *pkt_info, + struct sk_buff *skb, + enum rtw_tx_queue_type queue) +{ + const struct rtw_chip_info *chip = rtwdev->chip; + unsigned long data_addr, aligned_addr; + size_t offset; + u8 *pkt_desc; + + pkt_desc = skb_push(skb, chip->tx_pkt_desc_sz); + + data_addr = (unsigned long)pkt_desc; + aligned_addr = ALIGN(data_addr, RTW_SDIO_DATA_PTR_ALIGN); + + if (data_addr != aligned_addr) { + /* Ensure that the start of the pkt_desc is always aligned at + * RTW_SDIO_DATA_PTR_ALIGN. + */ + offset = RTW_SDIO_DATA_PTR_ALIGN - (aligned_addr - data_addr); + + pkt_desc = skb_push(skb, offset); + + /* By inserting padding to align the start of the pkt_desc we + * need to inform the firmware that the actual data starts at + * a different offset than normal. + */ + pkt_info->offset += offset; + } + + memset(pkt_desc, 0, chip->tx_pkt_desc_sz); + + pkt_info->qsel = rtw_sdio_get_tx_qsel(rtwdev, skb, queue); + + rtw_tx_fill_tx_desc(pkt_info, skb); + rtw_tx_fill_txdesc_checksum(rtwdev, pkt_info, pkt_desc); +} + +static int rtw_sdio_write_data(struct rtw_dev *rtwdev, + struct rtw_tx_pkt_info *pkt_info, + struct sk_buff *skb, + enum rtw_tx_queue_type queue) +{ + int ret; + + rtw_sdio_tx_skb_prepare(rtwdev, pkt_info, skb, queue); + + ret = rtw_sdio_write_port(rtwdev, skb, queue); + dev_kfree_skb_any(skb); + + return ret; +} + +static int rtw_sdio_write_data_rsvd_page(struct rtw_dev *rtwdev, u8 *buf, + u32 size) +{ + struct rtw_tx_pkt_info pkt_info = {}; + struct sk_buff *skb; + + skb = rtw_tx_write_data_rsvd_page_get(rtwdev, &pkt_info, buf, size); + if (!skb) + return -ENOMEM; + + return rtw_sdio_write_data(rtwdev, &pkt_info, skb, RTW_TX_QUEUE_BCN); +} + +static int rtw_sdio_write_data_h2c(struct rtw_dev *rtwdev, u8 *buf, u32 size) +{ + struct rtw_tx_pkt_info pkt_info = {}; + struct sk_buff *skb; + + skb = rtw_tx_write_data_h2c_get(rtwdev, &pkt_info, buf, size); + if (!skb) + return -ENOMEM; + + return rtw_sdio_write_data(rtwdev, &pkt_info, skb, RTW_TX_QUEUE_H2C); +} + +static int rtw_sdio_tx_write(struct rtw_dev *rtwdev, + struct rtw_tx_pkt_info *pkt_info, + struct sk_buff *skb) +{ + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; + enum rtw_tx_queue_type queue = rtw_tx_queue_mapping(skb); + struct rtw_sdio_tx_data *tx_data; + + rtw_sdio_tx_skb_prepare(rtwdev, pkt_info, skb, queue); + + tx_data = rtw_sdio_get_tx_data(skb); + tx_data->sn = pkt_info->sn; + + skb_queue_tail(&rtwsdio->tx_queue[queue], skb); + + return 0; +} + +static void rtw_sdio_tx_err_isr(struct rtw_dev *rtwdev) +{ + u32 val = rtw_read32(rtwdev, REG_TXDMA_STATUS); + + rtw_write32(rtwdev, REG_TXDMA_STATUS, val); +} + +static void rtw_sdio_rx_skb(struct rtw_dev *rtwdev, struct sk_buff *skb, + u32 pkt_offset, struct rtw_rx_pkt_stat *pkt_stat, + struct ieee80211_rx_status *rx_status) +{ + *IEEE80211_SKB_RXCB(skb) = *rx_status; + + if (pkt_stat->is_c2h) { + skb_put(skb, pkt_stat->pkt_len + pkt_offset); + rtw_fw_c2h_cmd_rx_irqsafe(rtwdev, pkt_offset, skb); + return; + } + + skb_put(skb, pkt_stat->pkt_len); + skb_reserve(skb, pkt_offset); + + rtw_rx_stats(rtwdev, pkt_stat->vif, skb); + + ieee80211_rx_irqsafe(rtwdev->hw, skb); +} + +static void rtw_sdio_rxfifo_recv(struct rtw_dev *rtwdev, u32 rx_len) +{ + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; + const struct rtw_chip_info *chip = rtwdev->chip; + u32 pkt_desc_sz = chip->rx_pkt_desc_sz; + struct ieee80211_rx_status rx_status; + struct rtw_rx_pkt_stat pkt_stat; + struct sk_buff *skb, *split_skb; + u32 pkt_offset, curr_pkt_len; + size_t bufsz; + u8 *rx_desc; + int ret; + + bufsz = sdio_align_size(rtwsdio->sdio_func, rx_len); + + skb = dev_alloc_skb(bufsz); + if (!skb) + return; + + ret = rtw_sdio_read_port(rtwdev, skb->data, bufsz); + if (ret) { + dev_kfree_skb_any(skb); + return; + } + + while (true) { + rx_desc = skb->data; + chip->ops->query_rx_desc(rtwdev, rx_desc, &pkt_stat, + &rx_status); + pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz + + pkt_stat.shift; + + curr_pkt_len = ALIGN(pkt_offset + pkt_stat.pkt_len, + RTW_SDIO_DATA_PTR_ALIGN); + + if ((curr_pkt_len + pkt_desc_sz) >= rx_len) { + /* Use the original skb (with it's adjusted offset) + * when processing the last (or even the only) entry to + * have it's memory freed automatically. + */ + rtw_sdio_rx_skb(rtwdev, skb, pkt_offset, &pkt_stat, + &rx_status); + break; + } + + split_skb = dev_alloc_skb(curr_pkt_len); + if (!split_skb) { + rtw_sdio_rx_skb(rtwdev, skb, pkt_offset, &pkt_stat, + &rx_status); + break; + } + + skb_copy_header(split_skb, skb); + memcpy(split_skb->data, skb->data, curr_pkt_len); + + rtw_sdio_rx_skb(rtwdev, split_skb, pkt_offset, &pkt_stat, + &rx_status); + + /* Move to the start of the next RX descriptor */ + skb_reserve(skb, curr_pkt_len); + rx_len -= curr_pkt_len; + } +} + +static void rtw_sdio_rx_isr(struct rtw_dev *rtwdev) +{ + u32 rx_len, hisr, total_rx_bytes = 0; + + do { + if (rtw_chip_wcpu_11n(rtwdev)) + rx_len = rtw_read16(rtwdev, REG_SDIO_RX0_REQ_LEN); + else + rx_len = rtw_read32(rtwdev, REG_SDIO_RX0_REQ_LEN); + + if (!rx_len) + break; + + rtw_sdio_rxfifo_recv(rtwdev, rx_len); + + total_rx_bytes += rx_len; + + if (rtw_chip_wcpu_11n(rtwdev)) { + /* Stop if no more RX requests are pending, even if + * rx_len could be greater than zero in the next + * iteration. This is needed because the RX buffer may + * already contain data while either HW or FW are not + * done filling that buffer yet. Still reading the + * buffer can result in packets where + * rtw_rx_pkt_stat.pkt_len is zero or points beyond the + * end of the buffer. + */ + hisr = rtw_read32(rtwdev, REG_SDIO_HISR); + } else { + /* RTW_WCPU_11AC chips have improved hardware or + * firmware and can use rx_len unconditionally. + */ + hisr = REG_SDIO_HISR_RX_REQUEST; + } + } while (total_rx_bytes < SZ_64K && hisr & REG_SDIO_HISR_RX_REQUEST); +} + +static void rtw_sdio_handle_interrupt(struct sdio_func *sdio_func) +{ + struct ieee80211_hw *hw = sdio_get_drvdata(sdio_func); + struct rtw_sdio *rtwsdio; + struct rtw_dev *rtwdev; + u32 hisr; + + rtwdev = hw->priv; + rtwsdio = (struct rtw_sdio *)rtwdev->priv; + + rtwsdio->irq_thread = current; + + hisr = rtw_read32(rtwdev, REG_SDIO_HISR); + + if (hisr & REG_SDIO_HISR_TXERR) + rtw_sdio_tx_err_isr(rtwdev); + if (hisr & REG_SDIO_HISR_RX_REQUEST) { + hisr &= ~REG_SDIO_HISR_RX_REQUEST; + rtw_sdio_rx_isr(rtwdev); + } + + rtw_write32(rtwdev, REG_SDIO_HISR, hisr); + + rtwsdio->irq_thread = NULL; +} + +static int __maybe_unused rtw_sdio_suspend(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + struct ieee80211_hw *hw = dev_get_drvdata(dev); + struct rtw_dev *rtwdev = hw->priv; + int ret; + + ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); + if (ret) + rtw_err(rtwdev, "Failed to host PM flag MMC_PM_KEEP_POWER"); + + return ret; +} + +static int __maybe_unused rtw_sdio_resume(struct device *dev) +{ + return 0; +} + +SIMPLE_DEV_PM_OPS(rtw_sdio_pm_ops, rtw_sdio_suspend, rtw_sdio_resume); +EXPORT_SYMBOL(rtw_sdio_pm_ops); + +static int rtw_sdio_claim(struct rtw_dev *rtwdev, struct sdio_func *sdio_func) +{ + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; + int ret; + + sdio_claim_host(sdio_func); + + ret = sdio_enable_func(sdio_func); + if (ret) { + rtw_err(rtwdev, "Failed to enable SDIO func"); + goto err_release_host; + } + + ret = sdio_set_block_size(sdio_func, RTW_SDIO_BLOCK_SIZE); + if (ret) { + rtw_err(rtwdev, "Failed to set SDIO block size to 512"); + goto err_disable_func; + } + + rtwsdio->sdio_func = sdio_func; + + rtwsdio->sdio3_bus_mode = mmc_card_uhs(sdio_func->card); + + sdio_set_drvdata(sdio_func, rtwdev->hw); + SET_IEEE80211_DEV(rtwdev->hw, &sdio_func->dev); + + sdio_release_host(sdio_func); + + return 0; + +err_disable_func: + sdio_disable_func(sdio_func); +err_release_host: + sdio_release_host(sdio_func); + return ret; +} + +static void rtw_sdio_declaim(struct rtw_dev *rtwdev, + struct sdio_func *sdio_func) +{ + sdio_claim_host(sdio_func); + sdio_disable_func(sdio_func); + sdio_release_host(sdio_func); +} + +static struct rtw_hci_ops rtw_sdio_ops = { + .tx_write = rtw_sdio_tx_write, + .tx_kick_off = rtw_sdio_tx_kick_off, + .setup = rtw_sdio_setup, + .start = rtw_sdio_start, + .stop = rtw_sdio_stop, + .deep_ps = rtw_sdio_deep_ps, + .link_ps = rtw_sdio_link_ps, + .interface_cfg = rtw_sdio_interface_cfg, + + .read8 = rtw_sdio_read8, + .read16 = rtw_sdio_read16, + .read32 = rtw_sdio_read32, + .write8 = rtw_sdio_write8, + .write16 = rtw_sdio_write16, + .write32 = rtw_sdio_write32, + .write_data_rsvd_page = rtw_sdio_write_data_rsvd_page, + .write_data_h2c = rtw_sdio_write_data_h2c, +}; + +static int rtw_sdio_request_irq(struct rtw_dev *rtwdev, + struct sdio_func *sdio_func) +{ + int ret; + + sdio_claim_host(sdio_func); + ret = sdio_claim_irq(sdio_func, &rtw_sdio_handle_interrupt); + sdio_release_host(sdio_func); + + if (ret) { + rtw_err(rtwdev, "failed to claim SDIO IRQ"); + return ret; + } + + return 0; +} + +static void rtw_sdio_indicate_tx_status(struct rtw_dev *rtwdev, + struct sk_buff *skb) +{ + struct rtw_sdio_tx_data *tx_data = rtw_sdio_get_tx_data(skb); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hw *hw = rtwdev->hw; + + /* enqueue to wait for tx report */ + if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) { + rtw_tx_report_enqueue(rtwdev, skb, tx_data->sn); + return; + } + + /* always ACK for others, then they won't be marked as drop */ + ieee80211_tx_info_clear_status(info); + if (info->flags & IEEE80211_TX_CTL_NO_ACK) + info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; + else + info->flags |= IEEE80211_TX_STAT_ACK; + + ieee80211_tx_status_irqsafe(hw, skb); +} + +static void rtw_sdio_process_tx_queue(struct rtw_dev *rtwdev, + enum rtw_tx_queue_type queue) +{ + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; + struct sk_buff *skb; + int ret; + + skb = skb_dequeue(&rtwsdio->tx_queue[queue]); + if (!skb) + return; + + ret = rtw_sdio_write_port(rtwdev, skb, queue); + if (ret) { + skb_queue_head(&rtwsdio->tx_queue[queue], skb); + return; + } + + if (queue <= RTW_TX_QUEUE_VO) + rtw_sdio_indicate_tx_status(rtwdev, skb); + else + dev_kfree_skb_any(skb); +} + +static void rtw_sdio_tx_handler(struct work_struct *work) +{ + struct rtw_sdio_work_data *work_data = + container_of(work, struct rtw_sdio_work_data, work); + struct rtw_sdio *rtwsdio; + struct rtw_dev *rtwdev; + int limit, queue; + + rtwdev = work_data->rtwdev; + rtwsdio = (struct rtw_sdio *)rtwdev->priv; + + if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_TX_WAKE)) + rtw_sdio_deep_ps_leave(rtwdev); + + for (queue = RTK_MAX_TX_QUEUE_NUM - 1; queue >= 0; queue--) { + for (limit = 0; limit < 1000; limit++) { + rtw_sdio_process_tx_queue(rtwdev, queue); + + if (skb_queue_empty(&rtwsdio->tx_queue[queue])) + break; + } + } +} + +static void rtw_sdio_free_irq(struct rtw_dev *rtwdev, + struct sdio_func *sdio_func) +{ + sdio_claim_host(sdio_func); + sdio_release_irq(sdio_func); + sdio_release_host(sdio_func); +} + +static int rtw_sdio_init_tx(struct rtw_dev *rtwdev) +{ + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; + int i; + + rtwsdio->txwq = create_singlethread_workqueue("rtw88_sdio: tx wq"); + if (!rtwsdio->txwq) { + rtw_err(rtwdev, "failed to create TX work queue\n"); + return -ENOMEM; + } + + for (i = 0; i < RTK_MAX_TX_QUEUE_NUM; i++) + skb_queue_head_init(&rtwsdio->tx_queue[i]); + rtwsdio->tx_handler_data = kmalloc(sizeof(*rtwsdio->tx_handler_data), + GFP_KERNEL); + if (!rtwsdio->tx_handler_data) + goto err_destroy_wq; + + rtwsdio->tx_handler_data->rtwdev = rtwdev; + INIT_WORK(&rtwsdio->tx_handler_data->work, rtw_sdio_tx_handler); + + return 0; + +err_destroy_wq: + destroy_workqueue(rtwsdio->txwq); + return -ENOMEM; +} + +static void rtw_sdio_deinit_tx(struct rtw_dev *rtwdev) +{ + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; + int i; + + for (i = 0; i < RTK_MAX_TX_QUEUE_NUM; i++) + skb_queue_purge(&rtwsdio->tx_queue[i]); + + flush_workqueue(rtwsdio->txwq); + destroy_workqueue(rtwsdio->txwq); + kfree(rtwsdio->tx_handler_data); +} + +int rtw_sdio_probe(struct sdio_func *sdio_func, + const struct sdio_device_id *id) +{ + struct ieee80211_hw *hw; + struct rtw_dev *rtwdev; + int drv_data_size; + int ret; + + drv_data_size = sizeof(struct rtw_dev) + sizeof(struct rtw_sdio); + hw = ieee80211_alloc_hw(drv_data_size, &rtw_ops); + if (!hw) { + dev_err(&sdio_func->dev, "failed to allocate hw"); + return -ENOMEM; + } + + rtwdev = hw->priv; + rtwdev->hw = hw; + rtwdev->dev = &sdio_func->dev; + rtwdev->chip = (struct rtw_chip_info *)id->driver_data; + rtwdev->hci.ops = &rtw_sdio_ops; + rtwdev->hci.type = RTW_HCI_TYPE_SDIO; + + ret = rtw_core_init(rtwdev); + if (ret) + goto err_release_hw; + + rtw_dbg(rtwdev, RTW_DBG_SDIO, + "rtw88 SDIO probe: vendor=0x%04x device=%04x class=%02x", + id->vendor, id->device, id->class); + + ret = rtw_sdio_claim(rtwdev, sdio_func); + if (ret) { + rtw_err(rtwdev, "failed to claim SDIO device"); + goto err_deinit_core; + } + + rtw_sdio_init(rtwdev); + + ret = rtw_sdio_init_tx(rtwdev); + if (ret) { + rtw_err(rtwdev, "failed to init SDIO TX queue\n"); + goto err_sdio_declaim; + } + + ret = rtw_chip_info_setup(rtwdev); + if (ret) { + rtw_err(rtwdev, "failed to setup chip information"); + goto err_destroy_txwq; + } + + ret = rtw_sdio_request_irq(rtwdev, sdio_func); + if (ret) + goto err_destroy_txwq; + + ret = rtw_register_hw(rtwdev, hw); + if (ret) { + rtw_err(rtwdev, "failed to register hw"); + goto err_free_irq; + } + + return 0; + +err_free_irq: + rtw_sdio_free_irq(rtwdev, sdio_func); +err_destroy_txwq: + rtw_sdio_deinit_tx(rtwdev); +err_sdio_declaim: + rtw_sdio_declaim(rtwdev, sdio_func); +err_deinit_core: + rtw_core_deinit(rtwdev); +err_release_hw: + ieee80211_free_hw(hw); + + return ret; +} +EXPORT_SYMBOL(rtw_sdio_probe); + +void rtw_sdio_remove(struct sdio_func *sdio_func) +{ + struct ieee80211_hw *hw = sdio_get_drvdata(sdio_func); + struct rtw_dev *rtwdev; + + if (!hw) + return; + + rtwdev = hw->priv; + + rtw_unregister_hw(rtwdev, hw); + rtw_sdio_disable_interrupt(rtwdev); + rtw_sdio_free_irq(rtwdev, sdio_func); + rtw_sdio_declaim(rtwdev, sdio_func); + rtw_sdio_deinit_tx(rtwdev); + rtw_core_deinit(rtwdev); + ieee80211_free_hw(hw); +} +EXPORT_SYMBOL(rtw_sdio_remove); + +void rtw_sdio_shutdown(struct device *dev) +{ + struct sdio_func *sdio_func = dev_to_sdio_func(dev); + const struct rtw_chip_info *chip; + struct ieee80211_hw *hw; + struct rtw_dev *rtwdev; + + hw = sdio_get_drvdata(sdio_func); + if (!hw) + return; + + rtwdev = hw->priv; + chip = rtwdev->chip; + + if (chip->ops->shutdown) + chip->ops->shutdown(rtwdev); +} +EXPORT_SYMBOL(rtw_sdio_shutdown); + +MODULE_AUTHOR("Martin Blumenstingl"); +MODULE_AUTHOR("Jernej Skrabec"); +MODULE_DESCRIPTION("Realtek 802.11ac wireless SDIO driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sys/contrib/dev/rtw88/sdio.h b/sys/contrib/dev/rtw88/sdio.h new file mode 100644 index 000000000000..3c659ed180f0 --- /dev/null +++ b/sys/contrib/dev/rtw88/sdio.h @@ -0,0 +1,178 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* Copyright (C) 2021 Martin Blumenstingl + * Copyright (C) 2021 Jernej Skrabec + */ + +#ifndef __REG_SDIO_H_ +#define __REG_SDIO_H_ + +/* I/O bus domain address mapping */ +#define SDIO_LOCAL_OFFSET 0x10250000 +#define WLAN_IOREG_OFFSET 0x10260000 +#define FIRMWARE_FIFO_OFFSET 0x10270000 +#define TX_HIQ_OFFSET 0x10310000 +#define TX_MIQ_OFFSET 0x10320000 +#define TX_LOQ_OFFSET 0x10330000 +#define TX_EPQ_OFFSET 0x10350000 +#define RX_RX0FF_OFFSET 0x10340000 + +#define RTW_SDIO_BUS_MSK 0xffff0000 +#define SDIO_LOCAL_REG_MSK 0x00000fff +#define WLAN_IOREG_REG_MSK 0x0000ffff + +/* SDIO Tx Control */ +#define REG_SDIO_TX_CTRL (SDIO_LOCAL_OFFSET + 0x0000) + +/*SDIO status timeout*/ +#define REG_SDIO_TIMEOUT (SDIO_LOCAL_OFFSET + 0x0002) + +/* SDIO Host Interrupt Mask */ +#define REG_SDIO_HIMR (SDIO_LOCAL_OFFSET + 0x0014) +#define REG_SDIO_HIMR_RX_REQUEST BIT(0) +#define REG_SDIO_HIMR_AVAL BIT(1) +#define REG_SDIO_HIMR_TXERR BIT(2) +#define REG_SDIO_HIMR_RXERR BIT(3) +#define REG_SDIO_HIMR_TXFOVW BIT(4) +#define REG_SDIO_HIMR_RXFOVW BIT(5) +#define REG_SDIO_HIMR_TXBCNOK BIT(6) +#define REG_SDIO_HIMR_TXBCNERR BIT(7) +#define REG_SDIO_HIMR_BCNERLY_INT BIT(16) +#define REG_SDIO_HIMR_C2HCMD BIT(17) +#define REG_SDIO_HIMR_CPWM1 BIT(18) +#define REG_SDIO_HIMR_CPWM2 BIT(19) +#define REG_SDIO_HIMR_HSISR_IND BIT(20) +#define REG_SDIO_HIMR_GTINT3_IND BIT(21) +#define REG_SDIO_HIMR_GTINT4_IND BIT(22) +#define REG_SDIO_HIMR_PSTIMEOUT BIT(23) +#define REG_SDIO_HIMR_OCPINT BIT(24) +#define REG_SDIO_HIMR_ATIMEND BIT(25) +#define REG_SDIO_HIMR_ATIMEND_E BIT(26) +#define REG_SDIO_HIMR_CTWEND BIT(27) +/* the following two are RTL8188 SDIO Specific */ +#define REG_SDIO_HIMR_MCU_ERR BIT(28) +#define REG_SDIO_HIMR_TSF_BIT32_TOGGLE BIT(29) + +/* SDIO Host Interrupt Service Routine */ +#define REG_SDIO_HISR (SDIO_LOCAL_OFFSET + 0x0018) +#define REG_SDIO_HISR_RX_REQUEST BIT(0) +#define REG_SDIO_HISR_AVAL BIT(1) +#define REG_SDIO_HISR_TXERR BIT(2) +#define REG_SDIO_HISR_RXERR BIT(3) +#define REG_SDIO_HISR_TXFOVW BIT(4) +#define REG_SDIO_HISR_RXFOVW BIT(5) +#define REG_SDIO_HISR_TXBCNOK BIT(6) +#define REG_SDIO_HISR_TXBCNERR BIT(7) +#define REG_SDIO_HISR_BCNERLY_INT BIT(16) +#define REG_SDIO_HISR_C2HCMD BIT(17) +#define REG_SDIO_HISR_CPWM1 BIT(18) +#define REG_SDIO_HISR_CPWM2 BIT(19) +#define REG_SDIO_HISR_HSISR_IND BIT(20) +#define REG_SDIO_HISR_GTINT3_IND BIT(21) +#define REG_SDIO_HISR_GTINT4_IND BIT(22) +#define REG_SDIO_HISR_PSTIMEOUT BIT(23) +#define REG_SDIO_HISR_OCPINT BIT(24) +#define REG_SDIO_HISR_ATIMEND BIT(25) +#define REG_SDIO_HISR_ATIMEND_E BIT(26) +#define REG_SDIO_HISR_CTWEND BIT(27) +/* the following two are RTL8188 SDIO Specific */ +#define REG_SDIO_HISR_MCU_ERR BIT(28) +#define REG_SDIO_HISR_TSF_BIT32_TOGGLE BIT(29) + +/* HCI Current Power Mode */ +#define REG_SDIO_HCPWM (SDIO_LOCAL_OFFSET + 0x0019) +/* RXDMA Request Length */ +#define REG_SDIO_RX0_REQ_LEN (SDIO_LOCAL_OFFSET + 0x001C) +/* OQT Free Page */ +#define REG_SDIO_OQT_FREE_PG (SDIO_LOCAL_OFFSET + 0x001E) +/* Free Tx Buffer Page */ +#define REG_SDIO_FREE_TXPG (SDIO_LOCAL_OFFSET + 0x0020) +/* HCI Current Power Mode 1 */ +#define REG_SDIO_HCPWM1 (SDIO_LOCAL_OFFSET + 0x0024) +/* HCI Current Power Mode 2 */ +#define REG_SDIO_HCPWM2 (SDIO_LOCAL_OFFSET + 0x0026) +/* Free Tx Page Sequence */ +#define REG_SDIO_FREE_TXPG_SEQ (SDIO_LOCAL_OFFSET + 0x0028) +/* HTSF Information */ +#define REG_SDIO_HTSFR_INFO (SDIO_LOCAL_OFFSET + 0x0030) +#define REG_SDIO_HCPWM1_V2 (SDIO_LOCAL_OFFSET + 0x0038) +/* H2C */ +#define REG_SDIO_H2C (SDIO_LOCAL_OFFSET + 0x0060) +/* HCI Request Power Mode 1 */ +#define REG_SDIO_HRPWM1 (SDIO_LOCAL_OFFSET + 0x0080) +/* HCI Request Power Mode 2 */ +#define REG_SDIO_HRPWM2 (SDIO_LOCAL_OFFSET + 0x0082) +/* HCI Power Save Clock */ +#define REG_SDIO_HPS_CLKR (SDIO_LOCAL_OFFSET + 0x0084) +/* SDIO HCI Suspend Control */ +#define REG_SDIO_HSUS_CTRL (SDIO_LOCAL_OFFSET + 0x0086) +#define BIT_HCI_SUS_REQ BIT(0) +#define BIT_HCI_RESUME_RDY BIT(1) +/* SDIO Host Extension Interrupt Mask Always */ +#define REG_SDIO_HIMR_ON (SDIO_LOCAL_OFFSET + 0x0090) +/* SDIO Host Extension Interrupt Status Always */ +#define REG_SDIO_HISR_ON (SDIO_LOCAL_OFFSET + 0x0091) + +#define REG_SDIO_INDIRECT_REG_CFG (SDIO_LOCAL_OFFSET + 0x0040) +#define BIT_SDIO_INDIRECT_REG_CFG_WORD BIT(16) +#define BIT_SDIO_INDIRECT_REG_CFG_DWORD BIT(17) +#define BIT_SDIO_INDIRECT_REG_CFG_WRITE BIT(18) +#define BIT_SDIO_INDIRECT_REG_CFG_READ BIT(19) +#define BIT_SDIO_INDIRECT_REG_CFG_UNK20 BIT(20) +#define REG_SDIO_INDIRECT_REG_DATA (SDIO_LOCAL_OFFSET + 0x0044) + +/* Sdio Address for SDIO Local Reg, TRX FIFO, MAC Reg */ +#define REG_SDIO_CMD_ADDR_MSK GENMASK(16, 13) +#define REG_SDIO_CMD_ADDR_SDIO_REG 0 +#define REG_SDIO_CMD_ADDR_MAC_REG 8 +#define REG_SDIO_CMD_ADDR_TXFF_HIGH 4 +#define REG_SDIO_CMD_ADDR_TXFF_LOW 6 +#define REG_SDIO_CMD_ADDR_TXFF_NORMAL 5 +#define REG_SDIO_CMD_ADDR_TXFF_EXTRA 7 +#define REG_SDIO_CMD_ADDR_RXFF 7 + +#define RTW_SDIO_BLOCK_SIZE 512 +#define RTW_SDIO_ADDR_RX_RX0FF_GEN(_id) (0x0e000 | ((_id) & 0x3)) + +#define RTW_SDIO_DATA_PTR_ALIGN 8 + +struct sdio_func; +struct sdio_device_id; + +struct rtw_sdio_tx_data { + u8 sn; +}; + +struct rtw_sdio_work_data { + struct work_struct work; + struct rtw_dev *rtwdev; +}; + +struct rtw_sdio { + struct sdio_func *sdio_func; + + u32 irq_mask; + u8 rx_addr; + bool sdio3_bus_mode; + + void *irq_thread; + + struct workqueue_struct *txwq; + struct rtw_sdio_work_data *tx_handler_data; + struct sk_buff_head tx_queue[RTK_MAX_TX_QUEUE_NUM]; +}; + +extern const struct dev_pm_ops rtw_sdio_pm_ops; + +int rtw_sdio_probe(struct sdio_func *sdio_func, + const struct sdio_device_id *id); +void rtw_sdio_remove(struct sdio_func *sdio_func); +void rtw_sdio_shutdown(struct device *dev); + +static inline bool rtw_sdio_is_sdio30_supported(struct rtw_dev *rtwdev) +{ + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; + + return rtwsdio->sdio3_bus_mode; +} + +#endif diff --git a/sys/contrib/dev/rtw88/tx.c b/sys/contrib/dev/rtw88/tx.c index 65a289937107..876481fabe02 100644 --- a/sys/contrib/dev/rtw88/tx.c +++ b/sys/contrib/dev/rtw88/tx.c @@ -1,717 +1,780 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* Copyright(c) 2018-2019 Realtek Corporation */ #include "main.h" #include "tx.h" #include "fw.h" #include "ps.h" #include "debug.h" static void rtw_tx_stats(struct rtw_dev *rtwdev, struct ieee80211_vif *vif, struct sk_buff *skb) { struct ieee80211_hdr *hdr; struct rtw_vif *rtwvif; hdr = (struct ieee80211_hdr *)skb->data; if (!ieee80211_is_data(hdr->frame_control)) return; if (!is_broadcast_ether_addr(hdr->addr1) && !is_multicast_ether_addr(hdr->addr1)) { rtwdev->stats.tx_unicast += skb->len; rtwdev->stats.tx_cnt++; if (vif) { rtwvif = (struct rtw_vif *)vif->drv_priv; rtwvif->stats.tx_unicast += skb->len; rtwvif->stats.tx_cnt++; } } } void rtw_tx_fill_tx_desc(struct rtw_tx_pkt_info *pkt_info, struct sk_buff *skb) { - __le32 *txdesc = (__le32 *)skb->data; - - SET_TX_DESC_TXPKTSIZE(txdesc, pkt_info->tx_pkt_size); - SET_TX_DESC_OFFSET(txdesc, pkt_info->offset); - SET_TX_DESC_PKT_OFFSET(txdesc, pkt_info->pkt_offset); - SET_TX_DESC_QSEL(txdesc, pkt_info->qsel); - SET_TX_DESC_BMC(txdesc, pkt_info->bmc); - SET_TX_DESC_RATE_ID(txdesc, pkt_info->rate_id); - SET_TX_DESC_DATARATE(txdesc, pkt_info->rate); - SET_TX_DESC_DISDATAFB(txdesc, pkt_info->dis_rate_fallback); - SET_TX_DESC_USE_RATE(txdesc, pkt_info->use_rate); - SET_TX_DESC_SEC_TYPE(txdesc, pkt_info->sec_type); - SET_TX_DESC_DATA_BW(txdesc, pkt_info->bw); - SET_TX_DESC_SW_SEQ(txdesc, pkt_info->seq); - SET_TX_DESC_MAX_AGG_NUM(txdesc, pkt_info->ampdu_factor); - SET_TX_DESC_AMPDU_DENSITY(txdesc, pkt_info->ampdu_density); - SET_TX_DESC_DATA_STBC(txdesc, pkt_info->stbc); - SET_TX_DESC_DATA_LDPC(txdesc, pkt_info->ldpc); - SET_TX_DESC_AGG_EN(txdesc, pkt_info->ampdu_en); - SET_TX_DESC_LS(txdesc, pkt_info->ls); - SET_TX_DESC_DATA_SHORT(txdesc, pkt_info->short_gi); - SET_TX_DESC_SPE_RPT(txdesc, pkt_info->report); - SET_TX_DESC_SW_DEFINE(txdesc, pkt_info->sn); - SET_TX_DESC_USE_RTS(txdesc, pkt_info->rts); + struct rtw_tx_desc *tx_desc = (struct rtw_tx_desc *)skb->data; + bool more_data = false; + + if (pkt_info->qsel == TX_DESC_QSEL_HIGH) + more_data = true; + + tx_desc->w0 = le32_encode_bits(pkt_info->tx_pkt_size, RTW_TX_DESC_W0_TXPKTSIZE) | + le32_encode_bits(pkt_info->offset, RTW_TX_DESC_W0_OFFSET) | + le32_encode_bits(pkt_info->bmc, RTW_TX_DESC_W0_BMC) | + le32_encode_bits(pkt_info->ls, RTW_TX_DESC_W0_LS) | + le32_encode_bits(pkt_info->dis_qselseq, RTW_TX_DESC_W0_DISQSELSEQ); + + tx_desc->w1 = le32_encode_bits(pkt_info->qsel, RTW_TX_DESC_W1_QSEL) | + le32_encode_bits(pkt_info->rate_id, RTW_TX_DESC_W1_RATE_ID) | + le32_encode_bits(pkt_info->sec_type, RTW_TX_DESC_W1_SEC_TYPE) | + le32_encode_bits(pkt_info->pkt_offset, RTW_TX_DESC_W1_PKT_OFFSET) | + le32_encode_bits(more_data, RTW_TX_DESC_W1_MORE_DATA); + + tx_desc->w2 = le32_encode_bits(pkt_info->ampdu_en, RTW_TX_DESC_W2_AGG_EN) | + le32_encode_bits(pkt_info->report, RTW_TX_DESC_W2_SPE_RPT) | + le32_encode_bits(pkt_info->ampdu_density, RTW_TX_DESC_W2_AMPDU_DEN) | + le32_encode_bits(pkt_info->bt_null, RTW_TX_DESC_W2_BT_NULL); + + tx_desc->w3 = le32_encode_bits(pkt_info->hw_ssn_sel, RTW_TX_DESC_W3_HW_SSN_SEL) | + le32_encode_bits(pkt_info->use_rate, RTW_TX_DESC_W3_USE_RATE) | + le32_encode_bits(pkt_info->dis_rate_fallback, RTW_TX_DESC_W3_DISDATAFB) | + le32_encode_bits(pkt_info->rts, RTW_TX_DESC_W3_USE_RTS) | + le32_encode_bits(pkt_info->nav_use_hdr, RTW_TX_DESC_W3_NAVUSEHDR) | + le32_encode_bits(pkt_info->ampdu_factor, RTW_TX_DESC_W3_MAX_AGG_NUM); + + tx_desc->w4 = le32_encode_bits(pkt_info->rate, RTW_TX_DESC_W4_DATARATE); + + tx_desc->w5 = le32_encode_bits(pkt_info->short_gi, RTW_TX_DESC_W5_DATA_SHORT) | + le32_encode_bits(pkt_info->bw, RTW_TX_DESC_W5_DATA_BW) | + le32_encode_bits(pkt_info->ldpc, RTW_TX_DESC_W5_DATA_LDPC) | + le32_encode_bits(pkt_info->stbc, RTW_TX_DESC_W5_DATA_STBC); + + tx_desc->w6 = le32_encode_bits(pkt_info->sn, RTW_TX_DESC_W6_SW_DEFINE); + + tx_desc->w8 = le32_encode_bits(pkt_info->en_hwseq, RTW_TX_DESC_W8_EN_HWSEQ); + + tx_desc->w9 = le32_encode_bits(pkt_info->seq, RTW_TX_DESC_W9_SW_SEQ); + if (pkt_info->rts) { - SET_TX_DESC_RTSRATE(txdesc, DESC_RATE24M); - SET_TX_DESC_DATA_RTS_SHORT(txdesc, 1); - } - SET_TX_DESC_DISQSELSEQ(txdesc, pkt_info->dis_qselseq); - SET_TX_DESC_EN_HWSEQ(txdesc, pkt_info->en_hwseq); - SET_TX_DESC_HW_SSN_SEL(txdesc, pkt_info->hw_ssn_sel); - SET_TX_DESC_NAVUSEHDR(txdesc, pkt_info->nav_use_hdr); - SET_TX_DESC_BT_NULL(txdesc, pkt_info->bt_null); - if (pkt_info->tim_offset) { - SET_TX_DESC_TIM_EN(txdesc, 1); - SET_TX_DESC_TIM_OFFSET(txdesc, pkt_info->tim_offset); + tx_desc->w4 |= le32_encode_bits(DESC_RATE24M, RTW_TX_DESC_W4_RTSRATE); + tx_desc->w5 |= le32_encode_bits(1, RTW_TX_DESC_W5_DATA_RTS_SHORT); } + + if (pkt_info->tim_offset) + tx_desc->w9 |= le32_encode_bits(1, RTW_TX_DESC_W9_TIM_EN) | + le32_encode_bits(pkt_info->tim_offset, RTW_TX_DESC_W9_TIM_OFFSET); } EXPORT_SYMBOL(rtw_tx_fill_tx_desc); static u8 get_tx_ampdu_factor(struct ieee80211_sta *sta) { u8 exp = sta->deflink.ht_cap.ampdu_factor; /* the least ampdu factor is 8K, and the value in the tx desc is the * max aggregation num, which represents val * 2 packets can be * aggregated in an AMPDU, so here we should use 8/2=4 as the base */ return (BIT(2) << exp) - 1; } static u8 get_tx_ampdu_density(struct ieee80211_sta *sta) { return sta->deflink.ht_cap.ampdu_density; } static u8 get_highest_ht_tx_rate(struct rtw_dev *rtwdev, struct ieee80211_sta *sta) { u8 rate; if (rtwdev->hal.rf_type == RF_2T2R && sta->deflink.ht_cap.mcs.rx_mask[1] != 0) rate = DESC_RATEMCS15; else rate = DESC_RATEMCS7; return rate; } static u8 get_highest_vht_tx_rate(struct rtw_dev *rtwdev, struct ieee80211_sta *sta) { struct rtw_efuse *efuse = &rtwdev->efuse; u8 rate; u16 tx_mcs_map; tx_mcs_map = le16_to_cpu(sta->deflink.vht_cap.vht_mcs.tx_mcs_map); if (efuse->hw_cap.nss == 1) { switch (tx_mcs_map & 0x3) { case IEEE80211_VHT_MCS_SUPPORT_0_7: rate = DESC_RATEVHT1SS_MCS7; break; case IEEE80211_VHT_MCS_SUPPORT_0_8: rate = DESC_RATEVHT1SS_MCS8; break; default: case IEEE80211_VHT_MCS_SUPPORT_0_9: rate = DESC_RATEVHT1SS_MCS9; break; } } else if (efuse->hw_cap.nss >= 2) { switch ((tx_mcs_map & 0xc) >> 2) { case IEEE80211_VHT_MCS_SUPPORT_0_7: rate = DESC_RATEVHT2SS_MCS7; break; case IEEE80211_VHT_MCS_SUPPORT_0_8: rate = DESC_RATEVHT2SS_MCS8; break; default: case IEEE80211_VHT_MCS_SUPPORT_0_9: rate = DESC_RATEVHT2SS_MCS9; break; } } else { rate = DESC_RATEVHT1SS_MCS9; } return rate; } static void rtw_tx_report_enable(struct rtw_dev *rtwdev, struct rtw_tx_pkt_info *pkt_info) { struct rtw_tx_report *tx_report = &rtwdev->tx_report; /* [11:8], reserved, fills with zero * [7:2], tx report sequence number * [1:0], firmware use, fills with zero */ pkt_info->sn = (atomic_inc_return(&tx_report->sn) << 2) & 0xfc; pkt_info->report = true; } void rtw_tx_report_purge_timer(struct timer_list *t) { struct rtw_dev *rtwdev = from_timer(rtwdev, t, tx_report.purge_timer); struct rtw_tx_report *tx_report = &rtwdev->tx_report; unsigned long flags; #if defined(__linux__) if (skb_queue_len(&tx_report->queue) == 0) return; rtw_warn(rtwdev, "failed to get tx report from firmware\n"); spin_lock_irqsave(&tx_report->q_lock, flags); skb_queue_purge(&tx_report->queue); spin_unlock_irqrestore(&tx_report->q_lock, flags); #elif defined(__FreeBSD__) uint32_t qlen; spin_lock_irqsave(&tx_report->q_lock, flags); qlen = skb_queue_len(&tx_report->queue); if (qlen > 0) skb_queue_purge(&tx_report->queue); spin_unlock_irqrestore(&tx_report->q_lock, flags); /* * XXX while there could be a new enqueue in the queue * simply not yet processed given the timer is updated without * locks after enqueue in rtw_tx_report_enqueue(), the numbers * seen can be in the 100s. We revert to rtw_dbg from * Linux git 584dce175f0461d5d9d63952a1e7955678c91086 . */ rtw_dbg(rtwdev, RTW_DBG_TX, "failed to get tx report from firmware: " "txreport qlen %u\n", qlen); #endif } void rtw_tx_report_enqueue(struct rtw_dev *rtwdev, struct sk_buff *skb, u8 sn) { struct rtw_tx_report *tx_report = &rtwdev->tx_report; unsigned long flags; u8 *drv_data; /* pass sn to tx report handler through driver data */ drv_data = (u8 *)IEEE80211_SKB_CB(skb)->status.status_driver_data; *drv_data = sn; spin_lock_irqsave(&tx_report->q_lock, flags); __skb_queue_tail(&tx_report->queue, skb); spin_unlock_irqrestore(&tx_report->q_lock, flags); mod_timer(&tx_report->purge_timer, jiffies + RTW_TX_PROBE_TIMEOUT); } EXPORT_SYMBOL(rtw_tx_report_enqueue); static void rtw_tx_report_tx_status(struct rtw_dev *rtwdev, struct sk_buff *skb, bool acked) { struct ieee80211_tx_info *info; info = IEEE80211_SKB_CB(skb); ieee80211_tx_info_clear_status(info); if (acked) info->flags |= IEEE80211_TX_STAT_ACK; else info->flags &= ~IEEE80211_TX_STAT_ACK; ieee80211_tx_status_irqsafe(rtwdev->hw, skb); } void rtw_tx_report_handle(struct rtw_dev *rtwdev, struct sk_buff *skb, int src) { struct rtw_tx_report *tx_report = &rtwdev->tx_report; struct rtw_c2h_cmd *c2h; struct sk_buff *cur, *tmp; unsigned long flags; u8 sn, st; u8 *n; c2h = get_c2h_from_skb(skb); if (src == C2H_CCX_TX_RPT) { sn = GET_CCX_REPORT_SEQNUM_V0(c2h->payload); st = GET_CCX_REPORT_STATUS_V0(c2h->payload); } else { sn = GET_CCX_REPORT_SEQNUM_V1(c2h->payload); st = GET_CCX_REPORT_STATUS_V1(c2h->payload); } spin_lock_irqsave(&tx_report->q_lock, flags); skb_queue_walk_safe(&tx_report->queue, cur, tmp) { n = (u8 *)IEEE80211_SKB_CB(cur)->status.status_driver_data; if (*n == sn) { __skb_unlink(cur, &tx_report->queue); rtw_tx_report_tx_status(rtwdev, cur, st == 0); break; } } spin_unlock_irqrestore(&tx_report->q_lock, flags); } static u8 rtw_get_mgmt_rate(struct rtw_dev *rtwdev, struct sk_buff *skb, u8 lowest_rate, bool ignore_rate) { struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_vif *vif = tx_info->control.vif; bool force_lowest = test_bit(RTW_FLAG_FORCE_LOWEST_RATE, rtwdev->flags); if (!vif || !vif->bss_conf.basic_rates || ignore_rate || force_lowest) return lowest_rate; return __ffs(vif->bss_conf.basic_rates) + lowest_rate; } static void rtw_tx_pkt_info_update_rate(struct rtw_dev *rtwdev, struct rtw_tx_pkt_info *pkt_info, struct sk_buff *skb, bool ignore_rate) { if (rtwdev->hal.current_band_type == RTW_BAND_2G) { pkt_info->rate_id = RTW_RATEID_B_20M; pkt_info->rate = rtw_get_mgmt_rate(rtwdev, skb, DESC_RATE1M, ignore_rate); } else { pkt_info->rate_id = RTW_RATEID_G; pkt_info->rate = rtw_get_mgmt_rate(rtwdev, skb, DESC_RATE6M, ignore_rate); } pkt_info->use_rate = true; pkt_info->dis_rate_fallback = true; } static void rtw_tx_pkt_info_update_sec(struct rtw_dev *rtwdev, struct rtw_tx_pkt_info *pkt_info, struct sk_buff *skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); u8 sec_type = 0; if (info && info->control.hw_key) { struct ieee80211_key_conf *key = info->control.hw_key; switch (key->cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: case WLAN_CIPHER_SUITE_TKIP: sec_type = 0x01; break; case WLAN_CIPHER_SUITE_CCMP: sec_type = 0x03; break; default: break; } } pkt_info->sec_type = sec_type; } static void rtw_tx_mgmt_pkt_info_update(struct rtw_dev *rtwdev, struct rtw_tx_pkt_info *pkt_info, struct ieee80211_sta *sta, struct sk_buff *skb) { rtw_tx_pkt_info_update_rate(rtwdev, pkt_info, skb, false); pkt_info->dis_qselseq = true; pkt_info->en_hwseq = true; pkt_info->hw_ssn_sel = 0; /* TODO: need to change hw port and hw ssn sel for multiple vifs */ } static void rtw_tx_data_pkt_info_update(struct rtw_dev *rtwdev, struct rtw_tx_pkt_info *pkt_info, struct ieee80211_sta *sta, struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hw *hw = rtwdev->hw; struct rtw_dm_info *dm_info = &rtwdev->dm_info; struct rtw_sta_info *si; u8 fix_rate; u16 seq; u8 ampdu_factor = 0; u8 ampdu_density = 0; bool ampdu_en = false; u8 rate = DESC_RATE6M; u8 rate_id = 6; u8 bw = RTW_CHANNEL_WIDTH_20; bool stbc = false; bool ldpc = false; seq = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; /* for broadcast/multicast, use default values */ if (!sta) goto out; if (info->flags & IEEE80211_TX_CTL_AMPDU) { ampdu_en = true; ampdu_factor = get_tx_ampdu_factor(sta); ampdu_density = get_tx_ampdu_density(sta); } if (info->control.use_rts || skb->len > hw->wiphy->rts_threshold) pkt_info->rts = true; if (sta->deflink.vht_cap.vht_supported) rate = get_highest_vht_tx_rate(rtwdev, sta); else if (sta->deflink.ht_cap.ht_supported) rate = get_highest_ht_tx_rate(rtwdev, sta); else if (sta->deflink.supp_rates[0] <= 0xf) rate = DESC_RATE11M; else rate = DESC_RATE54M; si = (struct rtw_sta_info *)sta->drv_priv; bw = si->bw_mode; rate_id = si->rate_id; stbc = rtwdev->hal.txrx_1ss ? false : si->stbc_en; ldpc = si->ldpc_en; out: pkt_info->seq = seq; pkt_info->ampdu_factor = ampdu_factor; pkt_info->ampdu_density = ampdu_density; pkt_info->ampdu_en = ampdu_en; pkt_info->rate = rate; pkt_info->rate_id = rate_id; pkt_info->bw = bw; pkt_info->stbc = stbc; pkt_info->ldpc = ldpc; fix_rate = dm_info->fix_rate; if (fix_rate < DESC_RATE_MAX) { pkt_info->rate = fix_rate; pkt_info->dis_rate_fallback = true; pkt_info->use_rate = true; } } void rtw_tx_pkt_info_update(struct rtw_dev *rtwdev, struct rtw_tx_pkt_info *pkt_info, struct ieee80211_sta *sta, struct sk_buff *skb) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct rtw_sta_info *si; struct ieee80211_vif *vif = NULL; __le16 fc = hdr->frame_control; bool bmc; if (sta) { si = (struct rtw_sta_info *)sta->drv_priv; vif = si->vif; } if (ieee80211_is_mgmt(fc) || ieee80211_is_nullfunc(fc)) rtw_tx_mgmt_pkt_info_update(rtwdev, pkt_info, sta, skb); else if (ieee80211_is_data(fc)) rtw_tx_data_pkt_info_update(rtwdev, pkt_info, sta, skb); bmc = is_broadcast_ether_addr(hdr->addr1) || is_multicast_ether_addr(hdr->addr1); if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) rtw_tx_report_enable(rtwdev, pkt_info); pkt_info->bmc = bmc; rtw_tx_pkt_info_update_sec(rtwdev, pkt_info, skb); pkt_info->tx_pkt_size = skb->len; pkt_info->offset = chip->tx_pkt_desc_sz; pkt_info->qsel = skb->priority; pkt_info->ls = true; /* maybe merge with tx status ? */ rtw_tx_stats(rtwdev, vif, skb); } void rtw_tx_rsvd_page_pkt_info_update(struct rtw_dev *rtwdev, struct rtw_tx_pkt_info *pkt_info, struct sk_buff *skb, enum rtw_rsvd_packet_type type) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; bool bmc; /* A beacon or dummy reserved page packet indicates that it is the first * reserved page, and the qsel of it will be set in each hci. */ if (type != RSVD_BEACON && type != RSVD_DUMMY) pkt_info->qsel = TX_DESC_QSEL_MGMT; rtw_tx_pkt_info_update_rate(rtwdev, pkt_info, skb, true); bmc = is_broadcast_ether_addr(hdr->addr1) || is_multicast_ether_addr(hdr->addr1); pkt_info->bmc = bmc; pkt_info->tx_pkt_size = skb->len; pkt_info->offset = chip->tx_pkt_desc_sz; pkt_info->ls = true; if (type == RSVD_PS_POLL) { pkt_info->nav_use_hdr = true; } else { pkt_info->dis_qselseq = true; pkt_info->en_hwseq = true; pkt_info->hw_ssn_sel = 0; } if (type == RSVD_QOS_NULL) pkt_info->bt_null = true; if (type == RSVD_BEACON) { struct rtw_rsvd_page *rsvd_pkt; int hdr_len; rsvd_pkt = list_first_entry_or_null(&rtwdev->rsvd_page_list, struct rtw_rsvd_page, build_list); if (rsvd_pkt && rsvd_pkt->tim_offset != 0) { hdr_len = sizeof(struct ieee80211_hdr_3addr); pkt_info->tim_offset = rsvd_pkt->tim_offset - hdr_len; } } rtw_tx_pkt_info_update_sec(rtwdev, pkt_info, skb); /* TODO: need to change hw port and hw ssn sel for multiple vifs */ } struct sk_buff * rtw_tx_write_data_rsvd_page_get(struct rtw_dev *rtwdev, struct rtw_tx_pkt_info *pkt_info, u8 *buf, u32 size) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct sk_buff *skb; u32 tx_pkt_desc_sz; u32 length; tx_pkt_desc_sz = chip->tx_pkt_desc_sz; length = size + tx_pkt_desc_sz; skb = dev_alloc_skb(length); if (!skb) { rtw_err(rtwdev, "failed to alloc write data rsvd page skb\n"); return NULL; } skb_reserve(skb, tx_pkt_desc_sz); skb_put_data(skb, buf, size); rtw_tx_rsvd_page_pkt_info_update(rtwdev, pkt_info, skb, RSVD_BEACON); return skb; } EXPORT_SYMBOL(rtw_tx_write_data_rsvd_page_get); struct sk_buff * rtw_tx_write_data_h2c_get(struct rtw_dev *rtwdev, struct rtw_tx_pkt_info *pkt_info, u8 *buf, u32 size) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; struct sk_buff *skb; u32 tx_pkt_desc_sz; u32 length; tx_pkt_desc_sz = chip->tx_pkt_desc_sz; length = size + tx_pkt_desc_sz; skb = dev_alloc_skb(length); if (!skb) { rtw_err(rtwdev, "failed to alloc write data h2c skb\n"); return NULL; } skb_reserve(skb, tx_pkt_desc_sz); skb_put_data(skb, buf, size); pkt_info->tx_pkt_size = size; return skb; } EXPORT_SYMBOL(rtw_tx_write_data_h2c_get); void rtw_tx(struct rtw_dev *rtwdev, struct ieee80211_tx_control *control, struct sk_buff *skb) { struct rtw_tx_pkt_info pkt_info = {0}; int ret; rtw_tx_pkt_info_update(rtwdev, &pkt_info, control->sta, skb); ret = rtw_hci_tx_write(rtwdev, &pkt_info, skb); if (ret) { #if defined(__linux__) rtw_err(rtwdev, "failed to write TX skb to HCI\n"); #elif defined(__FreeBSD__) rtw_err(rtwdev, "%s: failed to write TX skb to HCI: %d\n", __func__, ret); #endif goto out; } rtw_hci_tx_kick_off(rtwdev); return; out: ieee80211_free_txskb(rtwdev->hw, skb); } static void rtw_txq_check_agg(struct rtw_dev *rtwdev, struct rtw_txq *rtwtxq, struct sk_buff *skb) { struct ieee80211_txq *txq = rtwtxq_to_txq(rtwtxq); struct ieee80211_tx_info *info; struct rtw_sta_info *si; if (test_bit(RTW_TXQ_AMPDU, &rtwtxq->flags)) { info = IEEE80211_SKB_CB(skb); info->flags |= IEEE80211_TX_CTL_AMPDU; return; } if (skb_get_queue_mapping(skb) == IEEE80211_AC_VO) return; if (test_bit(RTW_TXQ_BLOCK_BA, &rtwtxq->flags)) return; if (unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE))) return; if (!txq->sta) return; si = (struct rtw_sta_info *)txq->sta->drv_priv; set_bit(txq->tid, si->tid_ba); ieee80211_queue_work(rtwdev->hw, &rtwdev->ba_work); } static int rtw_txq_push_skb(struct rtw_dev *rtwdev, struct rtw_txq *rtwtxq, struct sk_buff *skb) { struct ieee80211_txq *txq = rtwtxq_to_txq(rtwtxq); struct rtw_tx_pkt_info pkt_info = {0}; int ret; rtw_txq_check_agg(rtwdev, rtwtxq, skb); rtw_tx_pkt_info_update(rtwdev, &pkt_info, txq->sta, skb); ret = rtw_hci_tx_write(rtwdev, &pkt_info, skb); if (ret) { #if defined(__linux__) rtw_err(rtwdev, "failed to write TX skb to HCI\n"); #elif defined(__FreeBSD__) rtw_err(rtwdev, "%s: failed to write TX skb to HCI: %d\n", __func__, ret); #endif return ret; } - rtwtxq->last_push = jiffies; - return 0; } static struct sk_buff *rtw_txq_dequeue(struct rtw_dev *rtwdev, struct rtw_txq *rtwtxq) { struct ieee80211_txq *txq = rtwtxq_to_txq(rtwtxq); struct sk_buff *skb; skb = ieee80211_tx_dequeue(rtwdev->hw, txq); if (!skb) return NULL; return skb; } static void rtw_txq_push(struct rtw_dev *rtwdev, struct rtw_txq *rtwtxq, unsigned long frames) { struct sk_buff *skb; int ret; int i; rcu_read_lock(); for (i = 0; i < frames; i++) { skb = rtw_txq_dequeue(rtwdev, rtwtxq); if (!skb) break; ret = rtw_txq_push_skb(rtwdev, rtwtxq, skb); if (ret) { #if defined(__FreeBSD__) dev_kfree_skb_any(skb); rtw_err(rtwdev, "failed to push skb, ret %d\n", ret); #else rtw_err(rtwdev, "failed to pusk skb, ret %d\n", ret); #endif break; } } rcu_read_unlock(); } -void rtw_tx_work(struct work_struct *w) +void __rtw_tx_work(struct rtw_dev *rtwdev) { - struct rtw_dev *rtwdev = container_of(w, struct rtw_dev, tx_work); struct rtw_txq *rtwtxq, *tmp; spin_lock_bh(&rtwdev->txq_lock); list_for_each_entry_safe(rtwtxq, tmp, &rtwdev->txqs, list) { struct ieee80211_txq *txq = rtwtxq_to_txq(rtwtxq); unsigned long frame_cnt; unsigned long byte_cnt; ieee80211_txq_get_depth(txq, &frame_cnt, &byte_cnt); rtw_txq_push(rtwdev, rtwtxq, frame_cnt); list_del_init(&rtwtxq->list); } rtw_hci_tx_kick_off(rtwdev); spin_unlock_bh(&rtwdev->txq_lock); } +void rtw_tx_work(struct work_struct *w) +{ + struct rtw_dev *rtwdev = container_of(w, struct rtw_dev, tx_work); + + __rtw_tx_work(rtwdev); +} + void rtw_txq_init(struct rtw_dev *rtwdev, struct ieee80211_txq *txq) { struct rtw_txq *rtwtxq; if (!txq) return; rtwtxq = (struct rtw_txq *)txq->drv_priv; INIT_LIST_HEAD(&rtwtxq->list); } void rtw_txq_cleanup(struct rtw_dev *rtwdev, struct ieee80211_txq *txq) { struct rtw_txq *rtwtxq; if (!txq) return; rtwtxq = (struct rtw_txq *)txq->drv_priv; spin_lock_bh(&rtwdev->txq_lock); if (!list_empty(&rtwtxq->list)) list_del_init(&rtwtxq->list); spin_unlock_bh(&rtwdev->txq_lock); } + +static const enum rtw_tx_queue_type ac_to_hwq[] = { + [IEEE80211_AC_VO] = RTW_TX_QUEUE_VO, + [IEEE80211_AC_VI] = RTW_TX_QUEUE_VI, + [IEEE80211_AC_BE] = RTW_TX_QUEUE_BE, + [IEEE80211_AC_BK] = RTW_TX_QUEUE_BK, +}; + +#if defined(__linux__) +static_assert(ARRAY_SIZE(ac_to_hwq) == IEEE80211_NUM_ACS); +#elif defined(__FreeBSD__) +rtw88_static_assert(ARRAY_SIZE(ac_to_hwq) == IEEE80211_NUM_ACS); +#endif + +enum rtw_tx_queue_type rtw_tx_ac_to_hwq(enum ieee80211_ac_numbers ac) +{ + if (WARN_ON(unlikely(ac >= IEEE80211_NUM_ACS))) + return RTW_TX_QUEUE_BE; + + return ac_to_hwq[ac]; +} +EXPORT_SYMBOL(rtw_tx_ac_to_hwq); + +enum rtw_tx_queue_type rtw_tx_queue_mapping(struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + __le16 fc = hdr->frame_control; + u8 q_mapping = skb_get_queue_mapping(skb); + enum rtw_tx_queue_type queue; + + if (unlikely(ieee80211_is_beacon(fc))) + queue = RTW_TX_QUEUE_BCN; + else if (unlikely(ieee80211_is_mgmt(fc) || ieee80211_is_ctl(fc))) + queue = RTW_TX_QUEUE_MGMT; + else if (is_broadcast_ether_addr(hdr->addr1) || + is_multicast_ether_addr(hdr->addr1)) + queue = RTW_TX_QUEUE_HI0; + else if (WARN_ON_ONCE(q_mapping >= ARRAY_SIZE(ac_to_hwq))) + queue = ac_to_hwq[IEEE80211_AC_BE]; + else + queue = ac_to_hwq[q_mapping]; + + return queue; +} +EXPORT_SYMBOL(rtw_tx_queue_mapping); diff --git a/sys/contrib/dev/rtw88/tx.h b/sys/contrib/dev/rtw88/tx.h index 8419603adce4..324189606257 100644 --- a/sys/contrib/dev/rtw88/tx.h +++ b/sys/contrib/dev/rtw88/tx.h @@ -1,126 +1,140 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* Copyright(c) 2018-2019 Realtek Corporation */ #ifndef __RTW_TX_H_ #define __RTW_TX_H_ #define RTK_TX_MAX_AGG_NUM_MASK 0x1f #define RTW_TX_PROBE_TIMEOUT msecs_to_jiffies(500) -#define SET_TX_DESC_TXPKTSIZE(txdesc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x00, value, GENMASK(15, 0)) -#define SET_TX_DESC_OFFSET(txdesc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x00, value, GENMASK(23, 16)) -#define SET_TX_DESC_PKT_OFFSET(txdesc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x01, value, GENMASK(28, 24)) -#define SET_TX_DESC_QSEL(txdesc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x01, value, GENMASK(12, 8)) -#define SET_TX_DESC_BMC(txdesc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x00, value, BIT(24)) -#define SET_TX_DESC_RATE_ID(txdesc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x01, value, GENMASK(20, 16)) -#define SET_TX_DESC_DATARATE(txdesc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x04, value, GENMASK(6, 0)) -#define SET_TX_DESC_DISDATAFB(txdesc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x03, value, BIT(10)) -#define SET_TX_DESC_USE_RATE(txdesc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x03, value, BIT(8)) -#define SET_TX_DESC_SEC_TYPE(txdesc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x01, value, GENMASK(23, 22)) -#define SET_TX_DESC_DATA_BW(txdesc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x05, value, GENMASK(6, 5)) -#define SET_TX_DESC_SW_SEQ(txdesc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x09, value, GENMASK(23, 12)) -#define SET_TX_DESC_TIM_EN(txdesc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x09, value, BIT(7)) -#define SET_TX_DESC_TIM_OFFSET(txdesc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x09, value, GENMASK(6, 0)) -#define SET_TX_DESC_MAX_AGG_NUM(txdesc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x03, value, GENMASK(21, 17)) -#define SET_TX_DESC_USE_RTS(tx_desc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x03, value, BIT(12)) -#define SET_TX_DESC_RTSRATE(txdesc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x04, value, GENMASK(28, 24)) -#define SET_TX_DESC_DATA_RTS_SHORT(txdesc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x05, value, BIT(12)) -#define SET_TX_DESC_AMPDU_DENSITY(txdesc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x02, value, GENMASK(22, 20)) -#define SET_TX_DESC_DATA_STBC(txdesc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x05, value, GENMASK(9, 8)) -#define SET_TX_DESC_DATA_LDPC(txdesc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x05, value, BIT(7)) -#define SET_TX_DESC_AGG_EN(txdesc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x02, value, BIT(12)) -#define SET_TX_DESC_LS(txdesc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x00, value, BIT(26)) -#define SET_TX_DESC_DATA_SHORT(txdesc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x05, value, BIT(4)) -#define SET_TX_DESC_SPE_RPT(tx_desc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x02, value, BIT(19)) -#define SET_TX_DESC_SW_DEFINE(tx_desc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x06, value, GENMASK(11, 0)) -#define SET_TX_DESC_DISQSELSEQ(txdesc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x00, value, BIT(31)) -#define SET_TX_DESC_EN_HWSEQ(txdesc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x08, value, BIT(15)) -#define SET_TX_DESC_HW_SSN_SEL(txdesc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x03, value, GENMASK(7, 6)) -#define SET_TX_DESC_NAVUSEHDR(txdesc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x03, value, BIT(15)) -#define SET_TX_DESC_BT_NULL(txdesc, value) \ - le32p_replace_bits((__le32 *)(txdesc) + 0x02, value, BIT(23)) +struct rtw_tx_desc { + __le32 w0; + __le32 w1; + __le32 w2; + __le32 w3; + __le32 w4; + __le32 w5; + __le32 w6; + __le32 w7; + __le32 w8; + __le32 w9; +} __packed; + +#define RTW_TX_DESC_W0_TXPKTSIZE GENMASK(15, 0) +#define RTW_TX_DESC_W0_OFFSET GENMASK(23, 16) +#define RTW_TX_DESC_W0_BMC BIT(24) +#define RTW_TX_DESC_W0_LS BIT(26) +#define RTW_TX_DESC_W0_DISQSELSEQ BIT(31) +#define RTW_TX_DESC_W1_QSEL GENMASK(12, 8) +#define RTW_TX_DESC_W1_RATE_ID GENMASK(20, 16) +#define RTW_TX_DESC_W1_SEC_TYPE GENMASK(23, 22) +#define RTW_TX_DESC_W1_PKT_OFFSET GENMASK(28, 24) +#define RTW_TX_DESC_W1_MORE_DATA BIT(29) +#define RTW_TX_DESC_W2_AGG_EN BIT(12) +#define RTW_TX_DESC_W2_SPE_RPT BIT(19) +#define RTW_TX_DESC_W2_AMPDU_DEN GENMASK(22, 20) +#define RTW_TX_DESC_W2_BT_NULL BIT(23) +#define RTW_TX_DESC_W3_HW_SSN_SEL GENMASK(7, 6) +#define RTW_TX_DESC_W3_USE_RATE BIT(8) +#define RTW_TX_DESC_W3_DISDATAFB BIT(10) +#define RTW_TX_DESC_W3_USE_RTS BIT(12) +#define RTW_TX_DESC_W3_NAVUSEHDR BIT(15) +#define RTW_TX_DESC_W3_MAX_AGG_NUM GENMASK(21, 17) +#define RTW_TX_DESC_W4_DATARATE GENMASK(6, 0) +#define RTW_TX_DESC_W4_RTSRATE GENMASK(28, 24) +#define RTW_TX_DESC_W5_DATA_SHORT BIT(4) +#define RTW_TX_DESC_W5_DATA_BW GENMASK(6, 5) +#define RTW_TX_DESC_W5_DATA_LDPC BIT(7) +#define RTW_TX_DESC_W5_DATA_STBC GENMASK(9, 8) +#define RTW_TX_DESC_W5_DATA_RTS_SHORT BIT(12) +#define RTW_TX_DESC_W6_SW_DEFINE GENMASK(11, 0) +#define RTW_TX_DESC_W7_TXDESC_CHECKSUM GENMASK(15, 0) +#define RTW_TX_DESC_W7_DMA_TXAGG_NUM GENMASK(31, 24) +#define RTW_TX_DESC_W8_EN_HWSEQ BIT(15) +#define RTW_TX_DESC_W9_SW_SEQ GENMASK(23, 12) +#define RTW_TX_DESC_W9_TIM_EN BIT(7) +#define RTW_TX_DESC_W9_TIM_OFFSET GENMASK(6, 0) enum rtw_tx_desc_queue_select { TX_DESC_QSEL_TID0 = 0, TX_DESC_QSEL_TID1 = 1, TX_DESC_QSEL_TID2 = 2, TX_DESC_QSEL_TID3 = 3, TX_DESC_QSEL_TID4 = 4, TX_DESC_QSEL_TID5 = 5, TX_DESC_QSEL_TID6 = 6, TX_DESC_QSEL_TID7 = 7, TX_DESC_QSEL_TID8 = 8, TX_DESC_QSEL_TID9 = 9, TX_DESC_QSEL_TID10 = 10, TX_DESC_QSEL_TID11 = 11, TX_DESC_QSEL_TID12 = 12, TX_DESC_QSEL_TID13 = 13, TX_DESC_QSEL_TID14 = 14, TX_DESC_QSEL_TID15 = 15, TX_DESC_QSEL_BEACON = 16, TX_DESC_QSEL_HIGH = 17, TX_DESC_QSEL_MGMT = 18, TX_DESC_QSEL_H2C = 19, }; enum rtw_rsvd_packet_type; void rtw_tx(struct rtw_dev *rtwdev, struct ieee80211_tx_control *control, struct sk_buff *skb); void rtw_txq_init(struct rtw_dev *rtwdev, struct ieee80211_txq *txq); void rtw_txq_cleanup(struct rtw_dev *rtwdev, struct ieee80211_txq *txq); void rtw_tx_work(struct work_struct *w); +void __rtw_tx_work(struct rtw_dev *rtwdev); void rtw_tx_pkt_info_update(struct rtw_dev *rtwdev, struct rtw_tx_pkt_info *pkt_info, struct ieee80211_sta *sta, struct sk_buff *skb); void rtw_tx_fill_tx_desc(struct rtw_tx_pkt_info *pkt_info, struct sk_buff *skb); void rtw_tx_report_enqueue(struct rtw_dev *rtwdev, struct sk_buff *skb, u8 sn); void rtw_tx_report_handle(struct rtw_dev *rtwdev, struct sk_buff *skb, int src); void rtw_tx_rsvd_page_pkt_info_update(struct rtw_dev *rtwdev, struct rtw_tx_pkt_info *pkt_info, struct sk_buff *skb, enum rtw_rsvd_packet_type type); struct sk_buff * rtw_tx_write_data_rsvd_page_get(struct rtw_dev *rtwdev, struct rtw_tx_pkt_info *pkt_info, u8 *buf, u32 size); struct sk_buff * rtw_tx_write_data_h2c_get(struct rtw_dev *rtwdev, struct rtw_tx_pkt_info *pkt_info, u8 *buf, u32 size); +enum rtw_tx_queue_type rtw_tx_ac_to_hwq(enum ieee80211_ac_numbers ac); +enum rtw_tx_queue_type rtw_tx_queue_mapping(struct sk_buff *skb); + +static inline +void fill_txdesc_checksum_common(u8 *txdesc, size_t words) +{ + __le16 chksum = 0; + __le16 *data = (__le16 *)(txdesc); + struct rtw_tx_desc *tx_desc = (struct rtw_tx_desc *)txdesc; + + le32p_replace_bits(&tx_desc->w7, 0, RTW_TX_DESC_W7_TXDESC_CHECKSUM); + + while (words--) + chksum ^= *data++; + + le32p_replace_bits(&tx_desc->w7, __le16_to_cpu(chksum), + RTW_TX_DESC_W7_TXDESC_CHECKSUM); +} + +static inline void rtw_tx_fill_txdesc_checksum(struct rtw_dev *rtwdev, + struct rtw_tx_pkt_info *pkt_info, + u8 *txdesc) +{ + const struct rtw_chip_info *chip = rtwdev->chip; + + chip->ops->fill_txdesc_checksum(rtwdev, pkt_info, txdesc); +} + #endif diff --git a/sys/contrib/dev/rtw88/usb.c b/sys/contrib/dev/rtw88/usb.c new file mode 100644 index 000000000000..f0f8f9a02b28 --- /dev/null +++ b/sys/contrib/dev/rtw88/usb.c @@ -0,0 +1,919 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2018-2019 Realtek Corporation + */ + +#include +#include +#include +#include "main.h" +#include "debug.h" +#include "reg.h" +#include "tx.h" +#include "rx.h" +#include "fw.h" +#include "ps.h" +#include "usb.h" + +#define RTW_USB_MAX_RXQ_LEN 512 + +struct rtw_usb_txcb { + struct rtw_dev *rtwdev; + struct sk_buff_head tx_ack_queue; +}; + +static void rtw_usb_fill_tx_checksum(struct rtw_usb *rtwusb, + struct sk_buff *skb, int agg_num) +{ + struct rtw_tx_desc *tx_desc = (struct rtw_tx_desc *)skb->data; + struct rtw_dev *rtwdev = rtwusb->rtwdev; + struct rtw_tx_pkt_info pkt_info; + + le32p_replace_bits(&tx_desc->w7, agg_num, RTW_TX_DESC_W7_DMA_TXAGG_NUM); + pkt_info.pkt_offset = le32_get_bits(tx_desc->w1, RTW_TX_DESC_W1_PKT_OFFSET); + rtw_tx_fill_txdesc_checksum(rtwdev, &pkt_info, skb->data); +} + +static u32 rtw_usb_read(struct rtw_dev *rtwdev, u32 addr, u16 len) +{ + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + struct usb_device *udev = rtwusb->udev; + __le32 *data; + unsigned long flags; + int idx, ret; + static int count; + + spin_lock_irqsave(&rtwusb->usb_lock, flags); + + idx = rtwusb->usb_data_index; + rtwusb->usb_data_index = (idx + 1) & (RTW_USB_MAX_RXTX_COUNT - 1); + + spin_unlock_irqrestore(&rtwusb->usb_lock, flags); + + data = &rtwusb->usb_data[idx]; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + RTW_USB_CMD_REQ, RTW_USB_CMD_READ, addr, + RTW_USB_VENQT_CMD_IDX, data, len, 1000); + if (ret < 0 && ret != -ENODEV && count++ < 4) + rtw_err(rtwdev, "read register 0x%x failed with %d\n", + addr, ret); + + return le32_to_cpu(*data); +} + +static u8 rtw_usb_read8(struct rtw_dev *rtwdev, u32 addr) +{ + return (u8)rtw_usb_read(rtwdev, addr, 1); +} + +static u16 rtw_usb_read16(struct rtw_dev *rtwdev, u32 addr) +{ + return (u16)rtw_usb_read(rtwdev, addr, 2); +} + +static u32 rtw_usb_read32(struct rtw_dev *rtwdev, u32 addr) +{ + return (u32)rtw_usb_read(rtwdev, addr, 4); +} + +static void rtw_usb_write(struct rtw_dev *rtwdev, u32 addr, u32 val, int len) +{ + struct rtw_usb *rtwusb = (struct rtw_usb *)rtwdev->priv; + struct usb_device *udev = rtwusb->udev; + unsigned long flags; + __le32 *data; + int idx, ret; + static int count; + + spin_lock_irqsave(&rtwusb->usb_lock, flags); + + idx = rtwusb->usb_data_index; + rtwusb->usb_data_index = (idx + 1) & (RTW_USB_MAX_RXTX_COUNT - 1); + + spin_unlock_irqrestore(&rtwusb->usb_lock, flags); + + data = &rtwusb->usb_data[idx]; + + *data = cpu_to_le32(val); + + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + RTW_USB_CMD_REQ, RTW_USB_CMD_WRITE, + addr, 0, data, len, 30000); + if (ret < 0 && ret != -ENODEV && count++ < 4) + rtw_err(rtwdev, "write register 0x%x failed with %d\n", + addr, ret); +} + +static void rtw_usb_write8(struct rtw_dev *rtwdev, u32 addr, u8 val) +{ + rtw_usb_write(rtwdev, addr, val, 1); +} + +static void rtw_usb_write16(struct rtw_dev *rtwdev, u32 addr, u16 val) +{ + rtw_usb_write(rtwdev, addr, val, 2); +} + +static void rtw_usb_write32(struct rtw_dev *rtwdev, u32 addr, u32 val) +{ + rtw_usb_write(rtwdev, addr, val, 4); +} + +static int dma_mapping_to_ep(enum rtw_dma_mapping dma_mapping) +{ + switch (dma_mapping) { + case RTW_DMA_MAPPING_HIGH: + return 0; + case RTW_DMA_MAPPING_NORMAL: + return 1; + case RTW_DMA_MAPPING_LOW: + return 2; + case RTW_DMA_MAPPING_EXTRA: + return 3; + default: + return -EINVAL; + } +} + +static int rtw_usb_parse(struct rtw_dev *rtwdev, + struct usb_interface *interface) +{ + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + struct usb_host_interface *host_interface = &interface->altsetting[0]; + struct usb_interface_descriptor *interface_desc = &host_interface->desc; + struct usb_endpoint_descriptor *endpoint; + int num_out_pipes = 0; + int i; + u8 num; + const struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_rqpn *rqpn; + + for (i = 0; i < interface_desc->bNumEndpoints; i++) { + endpoint = &host_interface->endpoint[i].desc; + num = usb_endpoint_num(endpoint); + + if (usb_endpoint_dir_in(endpoint) && + usb_endpoint_xfer_bulk(endpoint)) { + if (rtwusb->pipe_in) { + rtw_err(rtwdev, "IN pipes overflow\n"); + return -EINVAL; + } + + rtwusb->pipe_in = num; + } + + if (usb_endpoint_dir_in(endpoint) && + usb_endpoint_xfer_int(endpoint)) { + if (rtwusb->pipe_interrupt) { + rtw_err(rtwdev, "INT pipes overflow\n"); + return -EINVAL; + } + + rtwusb->pipe_interrupt = num; + } + + if (usb_endpoint_dir_out(endpoint) && + usb_endpoint_xfer_bulk(endpoint)) { + if (num_out_pipes >= ARRAY_SIZE(rtwusb->out_ep)) { + rtw_err(rtwdev, "OUT pipes overflow\n"); + return -EINVAL; + } + + rtwusb->out_ep[num_out_pipes++] = num; + } + } + + rtwdev->hci.bulkout_num = num_out_pipes; + + if (num_out_pipes < 1 || num_out_pipes > 4) { + rtw_err(rtwdev, "invalid number of endpoints %d\n", num_out_pipes); + return -EINVAL; + } + + rqpn = &chip->rqpn_table[num_out_pipes]; + + rtwusb->qsel_to_ep[TX_DESC_QSEL_TID0] = dma_mapping_to_ep(rqpn->dma_map_be); + rtwusb->qsel_to_ep[TX_DESC_QSEL_TID1] = dma_mapping_to_ep(rqpn->dma_map_bk); + rtwusb->qsel_to_ep[TX_DESC_QSEL_TID2] = dma_mapping_to_ep(rqpn->dma_map_bk); + rtwusb->qsel_to_ep[TX_DESC_QSEL_TID3] = dma_mapping_to_ep(rqpn->dma_map_be); + rtwusb->qsel_to_ep[TX_DESC_QSEL_TID4] = dma_mapping_to_ep(rqpn->dma_map_vi); + rtwusb->qsel_to_ep[TX_DESC_QSEL_TID5] = dma_mapping_to_ep(rqpn->dma_map_vi); + rtwusb->qsel_to_ep[TX_DESC_QSEL_TID6] = dma_mapping_to_ep(rqpn->dma_map_vo); + rtwusb->qsel_to_ep[TX_DESC_QSEL_TID7] = dma_mapping_to_ep(rqpn->dma_map_vo); + rtwusb->qsel_to_ep[TX_DESC_QSEL_TID8] = -EINVAL; + rtwusb->qsel_to_ep[TX_DESC_QSEL_TID9] = -EINVAL; + rtwusb->qsel_to_ep[TX_DESC_QSEL_TID10] = -EINVAL; + rtwusb->qsel_to_ep[TX_DESC_QSEL_TID11] = -EINVAL; + rtwusb->qsel_to_ep[TX_DESC_QSEL_TID12] = -EINVAL; + rtwusb->qsel_to_ep[TX_DESC_QSEL_TID13] = -EINVAL; + rtwusb->qsel_to_ep[TX_DESC_QSEL_TID14] = -EINVAL; + rtwusb->qsel_to_ep[TX_DESC_QSEL_TID15] = -EINVAL; + rtwusb->qsel_to_ep[TX_DESC_QSEL_BEACON] = dma_mapping_to_ep(rqpn->dma_map_hi); + rtwusb->qsel_to_ep[TX_DESC_QSEL_HIGH] = dma_mapping_to_ep(rqpn->dma_map_hi); + rtwusb->qsel_to_ep[TX_DESC_QSEL_MGMT] = dma_mapping_to_ep(rqpn->dma_map_mg); + rtwusb->qsel_to_ep[TX_DESC_QSEL_H2C] = dma_mapping_to_ep(rqpn->dma_map_hi); + + return 0; +} + +static void rtw_usb_write_port_tx_complete(struct urb *urb) +{ + struct rtw_usb_txcb *txcb = urb->context; + struct rtw_dev *rtwdev = txcb->rtwdev; + struct ieee80211_hw *hw = rtwdev->hw; + + while (true) { + struct sk_buff *skb = skb_dequeue(&txcb->tx_ack_queue); + struct ieee80211_tx_info *info; + struct rtw_usb_tx_data *tx_data; + + if (!skb) + break; + + info = IEEE80211_SKB_CB(skb); + tx_data = rtw_usb_get_tx_data(skb); + + /* enqueue to wait for tx report */ + if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) { + rtw_tx_report_enqueue(rtwdev, skb, tx_data->sn); + continue; + } + + /* always ACK for others, then they won't be marked as drop */ + ieee80211_tx_info_clear_status(info); + if (info->flags & IEEE80211_TX_CTL_NO_ACK) + info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; + else + info->flags |= IEEE80211_TX_STAT_ACK; + + ieee80211_tx_status_irqsafe(hw, skb); + } + + kfree(txcb); +} + +static int qsel_to_ep(struct rtw_usb *rtwusb, unsigned int qsel) +{ + if (qsel >= ARRAY_SIZE(rtwusb->qsel_to_ep)) + return -EINVAL; + + return rtwusb->qsel_to_ep[qsel]; +} + +static int rtw_usb_write_port(struct rtw_dev *rtwdev, u8 qsel, struct sk_buff *skb, + usb_complete_t cb, void *context) +{ + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + struct usb_device *usbd = rtwusb->udev; + struct urb *urb; + unsigned int pipe; + int ret; + int ep = qsel_to_ep(rtwusb, qsel); + + if (ep < 0) + return ep; + + pipe = usb_sndbulkpipe(usbd, rtwusb->out_ep[ep]); + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + return -ENOMEM; + + usb_fill_bulk_urb(urb, usbd, pipe, skb->data, skb->len, cb, context); + urb->transfer_flags |= URB_ZERO_PACKET; + ret = usb_submit_urb(urb, GFP_ATOMIC); + + usb_free_urb(urb); + + return ret; +} + +static bool rtw_usb_tx_agg_skb(struct rtw_usb *rtwusb, struct sk_buff_head *list) +{ + struct rtw_dev *rtwdev = rtwusb->rtwdev; + struct rtw_tx_desc *tx_desc; + struct rtw_usb_txcb *txcb; + struct sk_buff *skb_head; + struct sk_buff *skb_iter; + int agg_num = 0; + unsigned int align_next = 0; + u8 qsel; + + if (skb_queue_empty(list)) + return false; + + txcb = kmalloc(sizeof(*txcb), GFP_ATOMIC); + if (!txcb) + return false; + + txcb->rtwdev = rtwdev; + skb_queue_head_init(&txcb->tx_ack_queue); + + skb_iter = skb_dequeue(list); + + if (skb_queue_empty(list)) { + skb_head = skb_iter; + goto queue; + } + + skb_head = dev_alloc_skb(RTW_USB_MAX_XMITBUF_SZ); + if (!skb_head) { + skb_head = skb_iter; + goto queue; + } + + while (skb_iter) { + unsigned long flags; + + skb_put(skb_head, align_next); + skb_put_data(skb_head, skb_iter->data, skb_iter->len); + + align_next = ALIGN(skb_iter->len, 8) - skb_iter->len; + + agg_num++; + + skb_queue_tail(&txcb->tx_ack_queue, skb_iter); + + spin_lock_irqsave(&list->lock, flags); + + skb_iter = skb_peek(list); + + if (skb_iter && skb_iter->len + skb_head->len <= RTW_USB_MAX_XMITBUF_SZ) + __skb_unlink(skb_iter, list); + else + skb_iter = NULL; + spin_unlock_irqrestore(&list->lock, flags); + } + + if (agg_num > 1) + rtw_usb_fill_tx_checksum(rtwusb, skb_head, agg_num); + +queue: + skb_queue_tail(&txcb->tx_ack_queue, skb_head); + tx_desc = (struct rtw_tx_desc *)skb_head->data; + qsel = le32_get_bits(tx_desc->w1, RTW_TX_DESC_W1_QSEL); + + rtw_usb_write_port(rtwdev, qsel, skb_head, rtw_usb_write_port_tx_complete, txcb); + + return true; +} + +static void rtw_usb_tx_handler(struct work_struct *work) +{ + struct rtw_usb *rtwusb = container_of(work, struct rtw_usb, tx_work); + int i, limit; + + for (i = ARRAY_SIZE(rtwusb->tx_queue) - 1; i >= 0; i--) { + for (limit = 0; limit < 200; limit++) { + struct sk_buff_head *list = &rtwusb->tx_queue[i]; + + if (!rtw_usb_tx_agg_skb(rtwusb, list)) + break; + } + } +} + +static void rtw_usb_tx_queue_purge(struct rtw_usb *rtwusb) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rtwusb->tx_queue); i++) + skb_queue_purge(&rtwusb->tx_queue[i]); +} + +static void rtw_usb_write_port_complete(struct urb *urb) +{ + struct sk_buff *skb = urb->context; + + dev_kfree_skb_any(skb); +} + +static int rtw_usb_write_data(struct rtw_dev *rtwdev, + struct rtw_tx_pkt_info *pkt_info, + u8 *buf) +{ + const struct rtw_chip_info *chip = rtwdev->chip; + struct sk_buff *skb; + unsigned int desclen, headsize, size; + u8 qsel; + int ret = 0; + + size = pkt_info->tx_pkt_size; + qsel = pkt_info->qsel; + desclen = chip->tx_pkt_desc_sz; + headsize = pkt_info->offset ? pkt_info->offset : desclen; + + skb = dev_alloc_skb(headsize + size); + if (unlikely(!skb)) + return -ENOMEM; + + skb_reserve(skb, headsize); + skb_put_data(skb, buf, size); + skb_push(skb, headsize); + memset(skb->data, 0, headsize); + rtw_tx_fill_tx_desc(pkt_info, skb); + rtw_tx_fill_txdesc_checksum(rtwdev, pkt_info, skb->data); + + ret = rtw_usb_write_port(rtwdev, qsel, skb, + rtw_usb_write_port_complete, skb); + if (unlikely(ret)) + rtw_err(rtwdev, "failed to do USB write, ret=%d\n", ret); + + return ret; +} + +static int rtw_usb_write_data_rsvd_page(struct rtw_dev *rtwdev, u8 *buf, + u32 size) +{ + const struct rtw_chip_info *chip = rtwdev->chip; + struct rtw_tx_pkt_info pkt_info = {0}; + + pkt_info.tx_pkt_size = size; + pkt_info.qsel = TX_DESC_QSEL_BEACON; + pkt_info.offset = chip->tx_pkt_desc_sz; + + return rtw_usb_write_data(rtwdev, &pkt_info, buf); +} + +static int rtw_usb_write_data_h2c(struct rtw_dev *rtwdev, u8 *buf, u32 size) +{ + struct rtw_tx_pkt_info pkt_info = {0}; + + pkt_info.tx_pkt_size = size; + pkt_info.qsel = TX_DESC_QSEL_H2C; + + return rtw_usb_write_data(rtwdev, &pkt_info, buf); +} + +static u8 rtw_usb_tx_queue_mapping_to_qsel(struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + __le16 fc = hdr->frame_control; + u8 qsel; + + if (unlikely(ieee80211_is_mgmt(fc) || ieee80211_is_ctl(fc))) + qsel = TX_DESC_QSEL_MGMT; + else if (is_broadcast_ether_addr(hdr->addr1) || + is_multicast_ether_addr(hdr->addr1)) + qsel = TX_DESC_QSEL_HIGH; + else if (skb_get_queue_mapping(skb) <= IEEE80211_AC_BK) + qsel = skb->priority; + else + qsel = TX_DESC_QSEL_BEACON; + + return qsel; +} + +static int rtw_usb_tx_write(struct rtw_dev *rtwdev, + struct rtw_tx_pkt_info *pkt_info, + struct sk_buff *skb) +{ + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + const struct rtw_chip_info *chip = rtwdev->chip; + struct rtw_usb_tx_data *tx_data; + u8 *pkt_desc; + int ep; + + pkt_info->qsel = rtw_usb_tx_queue_mapping_to_qsel(skb); + pkt_desc = skb_push(skb, chip->tx_pkt_desc_sz); + memset(pkt_desc, 0, chip->tx_pkt_desc_sz); + ep = qsel_to_ep(rtwusb, pkt_info->qsel); + rtw_tx_fill_tx_desc(pkt_info, skb); + rtw_tx_fill_txdesc_checksum(rtwdev, pkt_info, skb->data); + tx_data = rtw_usb_get_tx_data(skb); + tx_data->sn = pkt_info->sn; + + skb_queue_tail(&rtwusb->tx_queue[ep], skb); + + return 0; +} + +static void rtw_usb_tx_kick_off(struct rtw_dev *rtwdev) +{ + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + + queue_work(rtwusb->txwq, &rtwusb->tx_work); +} + +static void rtw_usb_rx_handler(struct work_struct *work) +{ + struct rtw_usb *rtwusb = container_of(work, struct rtw_usb, rx_work); + struct rtw_dev *rtwdev = rtwusb->rtwdev; + const struct rtw_chip_info *chip = rtwdev->chip; + struct rtw_rx_pkt_stat pkt_stat; + struct ieee80211_rx_status rx_status; + struct sk_buff *skb; + u32 pkt_desc_sz = chip->rx_pkt_desc_sz; + u32 pkt_offset; + u8 *rx_desc; + int limit; + + for (limit = 0; limit < 200; limit++) { + skb = skb_dequeue(&rtwusb->rx_queue); + if (!skb) + break; + + rx_desc = skb->data; + chip->ops->query_rx_desc(rtwdev, rx_desc, &pkt_stat, + &rx_status); + pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz + + pkt_stat.shift; + + if (pkt_stat.is_c2h) { + skb_put(skb, pkt_stat.pkt_len + pkt_offset); + rtw_fw_c2h_cmd_rx_irqsafe(rtwdev, pkt_offset, skb); + continue; + } + + if (skb_queue_len(&rtwusb->rx_queue) >= RTW_USB_MAX_RXQ_LEN) { + dev_dbg_ratelimited(rtwdev->dev, "failed to get rx_queue, overflow\n"); + dev_kfree_skb_any(skb); + continue; + } + + skb_put(skb, pkt_stat.pkt_len); + skb_reserve(skb, pkt_offset); + memcpy(skb->cb, &rx_status, sizeof(rx_status)); + ieee80211_rx_irqsafe(rtwdev->hw, skb); + } +} + +static void rtw_usb_read_port_complete(struct urb *urb); + +static void rtw_usb_rx_resubmit(struct rtw_usb *rtwusb, struct rx_usb_ctrl_block *rxcb) +{ + struct rtw_dev *rtwdev = rtwusb->rtwdev; + int error; + + rxcb->rx_skb = alloc_skb(RTW_USB_MAX_RECVBUF_SZ, GFP_ATOMIC); + if (!rxcb->rx_skb) + return; + + usb_fill_bulk_urb(rxcb->rx_urb, rtwusb->udev, + usb_rcvbulkpipe(rtwusb->udev, rtwusb->pipe_in), + rxcb->rx_skb->data, RTW_USB_MAX_RECVBUF_SZ, + rtw_usb_read_port_complete, rxcb); + + error = usb_submit_urb(rxcb->rx_urb, GFP_ATOMIC); + if (error) { + kfree_skb(rxcb->rx_skb); + if (error != -ENODEV) + rtw_err(rtwdev, "Err sending rx data urb %d\n", + error); + } +} + +static void rtw_usb_read_port_complete(struct urb *urb) +{ + struct rx_usb_ctrl_block *rxcb = urb->context; + struct rtw_dev *rtwdev = rxcb->rtwdev; + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + struct sk_buff *skb = rxcb->rx_skb; + + if (urb->status == 0) { + if (urb->actual_length >= RTW_USB_MAX_RECVBUF_SZ || + urb->actual_length < 24) { + rtw_err(rtwdev, "failed to get urb length:%d\n", + urb->actual_length); + if (skb) + dev_kfree_skb_any(skb); + } else { + skb_queue_tail(&rtwusb->rx_queue, skb); + queue_work(rtwusb->rxwq, &rtwusb->rx_work); + } + rtw_usb_rx_resubmit(rtwusb, rxcb); + } else { + switch (urb->status) { + case -EINVAL: + case -EPIPE: + case -ENODEV: + case -ESHUTDOWN: + case -ENOENT: + case -EPROTO: + case -EILSEQ: + case -ETIME: + case -ECOMM: + case -EOVERFLOW: + case -EINPROGRESS: + break; + default: + rtw_err(rtwdev, "status %d\n", urb->status); + break; + } + if (skb) + dev_kfree_skb_any(skb); + } +} + +static void rtw_usb_cancel_rx_bufs(struct rtw_usb *rtwusb) +{ + struct rx_usb_ctrl_block *rxcb; + int i; + + for (i = 0; i < RTW_USB_RXCB_NUM; i++) { + rxcb = &rtwusb->rx_cb[i]; + if (rxcb->rx_urb) + usb_kill_urb(rxcb->rx_urb); + } +} + +static void rtw_usb_free_rx_bufs(struct rtw_usb *rtwusb) +{ + struct rx_usb_ctrl_block *rxcb; + int i; + + for (i = 0; i < RTW_USB_RXCB_NUM; i++) { + rxcb = &rtwusb->rx_cb[i]; + if (rxcb->rx_urb) { + usb_kill_urb(rxcb->rx_urb); + usb_free_urb(rxcb->rx_urb); + } + } +} + +static int rtw_usb_alloc_rx_bufs(struct rtw_usb *rtwusb) +{ + int i; + + for (i = 0; i < RTW_USB_RXCB_NUM; i++) { + struct rx_usb_ctrl_block *rxcb = &rtwusb->rx_cb[i]; + + rxcb->rtwdev = rtwusb->rtwdev; + rxcb->rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!rxcb->rx_urb) + goto err; + } + + return 0; +err: + rtw_usb_free_rx_bufs(rtwusb); + return -ENOMEM; +} + +static int rtw_usb_setup(struct rtw_dev *rtwdev) +{ + /* empty function for rtw_hci_ops */ + return 0; +} + +static int rtw_usb_start(struct rtw_dev *rtwdev) +{ + return 0; +} + +static void rtw_usb_stop(struct rtw_dev *rtwdev) +{ +} + +static void rtw_usb_deep_ps(struct rtw_dev *rtwdev, bool enter) +{ + /* empty function for rtw_hci_ops */ +} + +static void rtw_usb_link_ps(struct rtw_dev *rtwdev, bool enter) +{ + /* empty function for rtw_hci_ops */ +} + +static void rtw_usb_interface_cfg(struct rtw_dev *rtwdev) +{ + /* empty function for rtw_hci_ops */ +} + +static struct rtw_hci_ops rtw_usb_ops = { + .tx_write = rtw_usb_tx_write, + .tx_kick_off = rtw_usb_tx_kick_off, + .setup = rtw_usb_setup, + .start = rtw_usb_start, + .stop = rtw_usb_stop, + .deep_ps = rtw_usb_deep_ps, + .link_ps = rtw_usb_link_ps, + .interface_cfg = rtw_usb_interface_cfg, + + .write8 = rtw_usb_write8, + .write16 = rtw_usb_write16, + .write32 = rtw_usb_write32, + .read8 = rtw_usb_read8, + .read16 = rtw_usb_read16, + .read32 = rtw_usb_read32, + + .write_data_rsvd_page = rtw_usb_write_data_rsvd_page, + .write_data_h2c = rtw_usb_write_data_h2c, +}; + +static int rtw_usb_init_rx(struct rtw_dev *rtwdev) +{ + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + int i; + + rtwusb->rxwq = create_singlethread_workqueue("rtw88_usb: rx wq"); + if (!rtwusb->rxwq) { + rtw_err(rtwdev, "failed to create RX work queue\n"); + return -ENOMEM; + } + + skb_queue_head_init(&rtwusb->rx_queue); + + INIT_WORK(&rtwusb->rx_work, rtw_usb_rx_handler); + + for (i = 0; i < RTW_USB_RXCB_NUM; i++) { + struct rx_usb_ctrl_block *rxcb = &rtwusb->rx_cb[i]; + + rtw_usb_rx_resubmit(rtwusb, rxcb); + } + + return 0; +} + +static void rtw_usb_deinit_rx(struct rtw_dev *rtwdev) +{ + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + + skb_queue_purge(&rtwusb->rx_queue); + + flush_workqueue(rtwusb->rxwq); + destroy_workqueue(rtwusb->rxwq); +} + +static int rtw_usb_init_tx(struct rtw_dev *rtwdev) +{ + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + int i; + + rtwusb->txwq = create_singlethread_workqueue("rtw88_usb: tx wq"); + if (!rtwusb->txwq) { + rtw_err(rtwdev, "failed to create TX work queue\n"); + return -ENOMEM; + } + + for (i = 0; i < ARRAY_SIZE(rtwusb->tx_queue); i++) + skb_queue_head_init(&rtwusb->tx_queue[i]); + + INIT_WORK(&rtwusb->tx_work, rtw_usb_tx_handler); + + return 0; +} + +static void rtw_usb_deinit_tx(struct rtw_dev *rtwdev) +{ + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + + rtw_usb_tx_queue_purge(rtwusb); + flush_workqueue(rtwusb->txwq); + destroy_workqueue(rtwusb->txwq); +} + +static int rtw_usb_intf_init(struct rtw_dev *rtwdev, + struct usb_interface *intf) +{ + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + struct usb_device *udev = usb_get_dev(interface_to_usbdev(intf)); + int ret; + + rtwusb->udev = udev; + ret = rtw_usb_parse(rtwdev, intf); + if (ret) + return ret; + + rtwusb->usb_data = kcalloc(RTW_USB_MAX_RXTX_COUNT, sizeof(u32), + GFP_KERNEL); + if (!rtwusb->usb_data) + return -ENOMEM; + + usb_set_intfdata(intf, rtwdev->hw); + + SET_IEEE80211_DEV(rtwdev->hw, &intf->dev); + spin_lock_init(&rtwusb->usb_lock); + + return 0; +} + +static void rtw_usb_intf_deinit(struct rtw_dev *rtwdev, + struct usb_interface *intf) +{ + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + + usb_put_dev(rtwusb->udev); + kfree(rtwusb->usb_data); + usb_set_intfdata(intf, NULL); +} + +int rtw_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct rtw_dev *rtwdev; + struct ieee80211_hw *hw; + struct rtw_usb *rtwusb; + int drv_data_size; + int ret; + + drv_data_size = sizeof(struct rtw_dev) + sizeof(struct rtw_usb); + hw = ieee80211_alloc_hw(drv_data_size, &rtw_ops); + if (!hw) + return -ENOMEM; + + rtwdev = hw->priv; + rtwdev->hw = hw; + rtwdev->dev = &intf->dev; + rtwdev->chip = (struct rtw_chip_info *)id->driver_info; + rtwdev->hci.ops = &rtw_usb_ops; + rtwdev->hci.type = RTW_HCI_TYPE_USB; + + rtwusb = rtw_get_usb_priv(rtwdev); + rtwusb->rtwdev = rtwdev; + + ret = rtw_usb_alloc_rx_bufs(rtwusb); + if (ret) + goto err_release_hw; + + ret = rtw_core_init(rtwdev); + if (ret) + goto err_release_hw; + + ret = rtw_usb_intf_init(rtwdev, intf); + if (ret) { + rtw_err(rtwdev, "failed to init USB interface\n"); + goto err_deinit_core; + } + + ret = rtw_usb_init_tx(rtwdev); + if (ret) { + rtw_err(rtwdev, "failed to init USB TX\n"); + goto err_destroy_usb; + } + + ret = rtw_usb_init_rx(rtwdev); + if (ret) { + rtw_err(rtwdev, "failed to init USB RX\n"); + goto err_destroy_txwq; + } + + ret = rtw_chip_info_setup(rtwdev); + if (ret) { + rtw_err(rtwdev, "failed to setup chip information\n"); + goto err_destroy_rxwq; + } + + ret = rtw_register_hw(rtwdev, rtwdev->hw); + if (ret) { + rtw_err(rtwdev, "failed to register hw\n"); + goto err_destroy_rxwq; + } + + return 0; + +err_destroy_rxwq: + rtw_usb_deinit_rx(rtwdev); + +err_destroy_txwq: + rtw_usb_deinit_tx(rtwdev); + +err_destroy_usb: + rtw_usb_intf_deinit(rtwdev, intf); + +err_deinit_core: + rtw_core_deinit(rtwdev); + +err_release_hw: + ieee80211_free_hw(hw); + + return ret; +} +EXPORT_SYMBOL(rtw_usb_probe); + +void rtw_usb_disconnect(struct usb_interface *intf) +{ + struct ieee80211_hw *hw = usb_get_intfdata(intf); + struct rtw_dev *rtwdev; + struct rtw_usb *rtwusb; + + if (!hw) + return; + + rtwdev = hw->priv; + rtwusb = rtw_get_usb_priv(rtwdev); + + rtw_usb_cancel_rx_bufs(rtwusb); + + rtw_unregister_hw(rtwdev, hw); + rtw_usb_deinit_tx(rtwdev); + rtw_usb_deinit_rx(rtwdev); + + if (rtwusb->udev->state != USB_STATE_NOTATTACHED) + usb_reset_device(rtwusb->udev); + + rtw_usb_free_rx_bufs(rtwusb); + + rtw_usb_intf_deinit(rtwdev, intf); + rtw_core_deinit(rtwdev); + ieee80211_free_hw(hw); +} +EXPORT_SYMBOL(rtw_usb_disconnect); + +MODULE_AUTHOR("Realtek Corporation"); +MODULE_DESCRIPTION("Realtek 802.11ac wireless USB driver"); +MODULE_LICENSE("Dual BSD/GPL"); +#if defined(__FreeBSD__) +MODULE_VERSION(rtw88_usb, 1); +MODULE_DEPEND(rtw88_usb, rtw88_core, 1, 1, 1); +MODULE_DEPEND(rtw88_usb, linuxkpi, 1, 1, 1); +MODULE_DEPEND(rtw88_usb, linuxkpi_wlan, 1, 1, 1); +#endif diff --git a/sys/contrib/dev/rtw88/usb.h b/sys/contrib/dev/rtw88/usb.h new file mode 100644 index 000000000000..86697a5c0103 --- /dev/null +++ b/sys/contrib/dev/rtw88/usb.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* Copyright(c) 2018-2019 Realtek Corporation + */ + +#ifndef __RTW_USB_H_ +#define __RTW_USB_H_ + +#define FW_8192C_START_ADDRESS 0x1000 +#define FW_8192C_END_ADDRESS 0x5fff + +#define RTW_USB_MAX_RXTX_COUNT 128 +#define RTW_USB_VENQT_MAX_BUF_SIZE 254 +#define MAX_USBCTRL_VENDORREQ_TIMES 10 + +#define RTW_USB_CMD_READ 0xc0 +#define RTW_USB_CMD_WRITE 0x40 +#define RTW_USB_CMD_REQ 0x05 + +#define RTW_USB_VENQT_CMD_IDX 0x00 + +#define RTW_USB_TX_SEL_HQ BIT(0) +#define RTW_USB_TX_SEL_LQ BIT(1) +#define RTW_USB_TX_SEL_NQ BIT(2) +#define RTW_USB_TX_SEL_EQ BIT(3) + +#define RTW_USB_BULK_IN_ADDR 0x80 +#define RTW_USB_INT_IN_ADDR 0x81 + +#define RTW_USB_HW_QUEUE_ENTRY 8 + +#define RTW_USB_PACKET_OFFSET_SZ 8 +#define RTW_USB_MAX_XMITBUF_SZ (1024 * 20) +#define RTW_USB_MAX_RECVBUF_SZ 32768 + +#define RTW_USB_RECVBUFF_ALIGN_SZ 8 + +#define RTW_USB_RXAGG_SIZE 6 +#define RTW_USB_RXAGG_TIMEOUT 10 + +#define RTW_USB_RXCB_NUM 4 + +#define RTW_USB_EP_MAX 4 + +#define TX_DESC_QSEL_MAX 20 + +#define RTW_USB_VENDOR_ID_REALTEK 0x0bda + +static inline struct rtw_usb *rtw_get_usb_priv(struct rtw_dev *rtwdev) +{ + return (struct rtw_usb *)rtwdev->priv; +} + +struct rx_usb_ctrl_block { + struct rtw_dev *rtwdev; + struct urb *rx_urb; + struct sk_buff *rx_skb; +}; + +struct rtw_usb_tx_data { + u8 sn; +}; + +struct rtw_usb { + struct rtw_dev *rtwdev; + struct usb_device *udev; + + /* protects usb_data_index */ + spinlock_t usb_lock; + __le32 *usb_data; + unsigned int usb_data_index; + + u8 pipe_interrupt; + u8 pipe_in; + u8 out_ep[RTW_USB_EP_MAX]; + int qsel_to_ep[TX_DESC_QSEL_MAX]; + + struct workqueue_struct *txwq, *rxwq; + + struct sk_buff_head tx_queue[RTW_USB_EP_MAX]; + struct work_struct tx_work; + + struct rx_usb_ctrl_block rx_cb[RTW_USB_RXCB_NUM]; + struct sk_buff_head rx_queue; + struct work_struct rx_work; +}; + +static inline struct rtw_usb_tx_data *rtw_usb_get_tx_data(struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + BUILD_BUG_ON(sizeof(struct rtw_usb_tx_data) > + sizeof(info->status.status_driver_data)); + + return (struct rtw_usb_tx_data *)info->status.status_driver_data; +} + +int rtw_usb_probe(struct usb_interface *intf, const struct usb_device_id *id); +void rtw_usb_disconnect(struct usb_interface *intf); + +#endif diff --git a/sys/contrib/dev/rtw88/util.c b/sys/contrib/dev/rtw88/util.c index 2c515af214e7..e222d3c01a77 100644 --- a/sys/contrib/dev/rtw88/util.c +++ b/sys/contrib/dev/rtw88/util.c @@ -1,107 +1,207 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* Copyright(c) 2018-2019 Realtek Corporation */ #include "main.h" #include "util.h" #include "reg.h" bool check_hw_ready(struct rtw_dev *rtwdev, u32 addr, u32 mask, u32 target) { u32 cnt; for (cnt = 0; cnt < 1000; cnt++) { if (rtw_read32_mask(rtwdev, addr, mask) == target) return true; udelay(10); } return false; } EXPORT_SYMBOL(check_hw_ready); bool ltecoex_read_reg(struct rtw_dev *rtwdev, u16 offset, u32 *val) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; const struct rtw_ltecoex_addr *ltecoex = chip->ltecoex_addr; if (!check_hw_ready(rtwdev, ltecoex->ctrl, LTECOEX_READY, 1)) return false; rtw_write32(rtwdev, ltecoex->ctrl, 0x800F0000 | offset); *val = rtw_read32(rtwdev, ltecoex->rdata); return true; } bool ltecoex_reg_write(struct rtw_dev *rtwdev, u16 offset, u32 value) { - struct rtw_chip_info *chip = rtwdev->chip; + const struct rtw_chip_info *chip = rtwdev->chip; const struct rtw_ltecoex_addr *ltecoex = chip->ltecoex_addr; if (!check_hw_ready(rtwdev, ltecoex->ctrl, LTECOEX_READY, 1)) return false; rtw_write32(rtwdev, ltecoex->wdata, value); rtw_write32(rtwdev, ltecoex->ctrl, 0xC00F0000 | offset); return true; } void rtw_restore_reg(struct rtw_dev *rtwdev, struct rtw_backup_info *bckp, u32 num) { u8 len; u32 reg; u32 val; int i; for (i = 0; i < num; i++, bckp++) { len = bckp->len; reg = bckp->reg; val = bckp->val; switch (len) { case 1: rtw_write8(rtwdev, reg, (u8)val); break; case 2: rtw_write16(rtwdev, reg, (u16)val); break; case 4: rtw_write32(rtwdev, reg, (u32)val); break; default: break; } } } EXPORT_SYMBOL(rtw_restore_reg); void rtw_desc_to_mcsrate(u16 rate, u8 *mcs, u8 *nss) { if (rate <= DESC_RATE54M) return; if (rate >= DESC_RATEVHT1SS_MCS0 && rate <= DESC_RATEVHT1SS_MCS9) { *nss = 1; *mcs = rate - DESC_RATEVHT1SS_MCS0; } else if (rate >= DESC_RATEVHT2SS_MCS0 && rate <= DESC_RATEVHT2SS_MCS9) { *nss = 2; *mcs = rate - DESC_RATEVHT2SS_MCS0; } else if (rate >= DESC_RATEVHT3SS_MCS0 && rate <= DESC_RATEVHT3SS_MCS9) { *nss = 3; *mcs = rate - DESC_RATEVHT3SS_MCS0; } else if (rate >= DESC_RATEVHT4SS_MCS0 && rate <= DESC_RATEVHT4SS_MCS9) { *nss = 4; *mcs = rate - DESC_RATEVHT4SS_MCS0; } else if (rate >= DESC_RATEMCS0 && rate <= DESC_RATEMCS15) { *mcs = rate - DESC_RATEMCS0; } } + +struct rtw_stas_entry { + struct list_head list; + struct ieee80211_sta *sta; +}; + +struct rtw_iter_stas_data { + struct rtw_dev *rtwdev; + struct list_head list; +}; + +static void rtw_collect_sta_iter(void *data, struct ieee80211_sta *sta) +{ + struct rtw_iter_stas_data *iter_stas = data; + struct rtw_stas_entry *stas_entry; + + stas_entry = kmalloc(sizeof(*stas_entry), GFP_ATOMIC); + if (!stas_entry) + return; + + stas_entry->sta = sta; + list_add_tail(&stas_entry->list, &iter_stas->list); +} + +void rtw_iterate_stas(struct rtw_dev *rtwdev, + void (*iterator)(void *data, + struct ieee80211_sta *sta), + void *data) +{ + struct rtw_iter_stas_data iter_data; + struct rtw_stas_entry *sta_entry, *tmp; + + /* &rtwdev->mutex makes sure no stations can be removed between + * collecting the stations and iterating over them. + */ + lockdep_assert_held(&rtwdev->mutex); + + iter_data.rtwdev = rtwdev; + INIT_LIST_HEAD(&iter_data.list); + + ieee80211_iterate_stations_atomic(rtwdev->hw, rtw_collect_sta_iter, + &iter_data); + + list_for_each_entry_safe(sta_entry, tmp, &iter_data.list, + list) { + list_del_init(&sta_entry->list); + iterator(data, sta_entry->sta); + kfree(sta_entry); + } +} + +struct rtw_vifs_entry { + struct list_head list; + struct ieee80211_vif *vif; +}; + +struct rtw_iter_vifs_data { + struct rtw_dev *rtwdev; + struct list_head list; +}; + +static void rtw_collect_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) +{ + struct rtw_iter_vifs_data *iter_stas = data; + struct rtw_vifs_entry *vifs_entry; + + vifs_entry = kmalloc(sizeof(*vifs_entry), GFP_ATOMIC); + if (!vifs_entry) + return; + + vifs_entry->vif = vif; + list_add_tail(&vifs_entry->list, &iter_stas->list); +} + +void rtw_iterate_vifs(struct rtw_dev *rtwdev, + void (*iterator)(void *data, struct ieee80211_vif *vif), + void *data) +{ + struct rtw_iter_vifs_data iter_data; + struct rtw_vifs_entry *vif_entry, *tmp; + + /* &rtwdev->mutex makes sure no interfaces can be removed between + * collecting the interfaces and iterating over them. + */ + lockdep_assert_held(&rtwdev->mutex); + + iter_data.rtwdev = rtwdev; + INIT_LIST_HEAD(&iter_data.list); + + ieee80211_iterate_active_interfaces_atomic(rtwdev->hw, + IEEE80211_IFACE_ITER_NORMAL, + rtw_collect_vif_iter, &iter_data); + + list_for_each_entry_safe(vif_entry, tmp, &iter_data.list, + list) { + list_del_init(&vif_entry->list); + iterator(data, vif_entry->vif); + kfree(vif_entry); + } +} diff --git a/sys/contrib/dev/rtw88/util.h b/sys/contrib/dev/rtw88/util.h index 0c23b5069be0..f8399128a9a3 100644 --- a/sys/contrib/dev/rtw88/util.h +++ b/sys/contrib/dev/rtw88/util.h @@ -1,38 +1,43 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* Copyright(c) 2018-2019 Realtek Corporation */ #ifndef __RTW_UTIL_H__ #define __RTW_UTIL_H__ struct rtw_dev; -#define rtw_iterate_vifs(rtwdev, iterator, data) \ - ieee80211_iterate_active_interfaces(rtwdev->hw, \ - IEEE80211_IFACE_ITER_NORMAL, iterator, data) #define rtw_iterate_vifs_atomic(rtwdev, iterator, data) \ ieee80211_iterate_active_interfaces_atomic(rtwdev->hw, \ IEEE80211_IFACE_ITER_NORMAL, iterator, data) #define rtw_iterate_stas_atomic(rtwdev, iterator, data) \ ieee80211_iterate_stations_atomic(rtwdev->hw, iterator, data) #define rtw_iterate_keys(rtwdev, vif, iterator, data) \ ieee80211_iter_keys(rtwdev->hw, vif, iterator, data) #define rtw_iterate_keys_rcu(rtwdev, vif, iterator, data) \ ieee80211_iter_keys_rcu((rtwdev)->hw, vif, iterator, data) +void rtw_iterate_vifs(struct rtw_dev *rtwdev, + void (*iterator)(void *data, struct ieee80211_vif *vif), + void *data); +void rtw_iterate_stas(struct rtw_dev *rtwdev, + void (*iterator)(void *data, + struct ieee80211_sta *sta), + void *data); + static inline u8 *get_hdr_bssid(struct ieee80211_hdr *hdr) { __le16 fc = hdr->frame_control; u8 *bssid; if (ieee80211_has_tods(fc)) bssid = hdr->addr1; else if (ieee80211_has_fromds(fc)) bssid = hdr->addr2; else bssid = hdr->addr3; return bssid; } #endif diff --git a/sys/contrib/dev/rtw88/wow.c b/sys/contrib/dev/rtw88/wow.c index 89dc595094d5..16ddee577efe 100644 --- a/sys/contrib/dev/rtw88/wow.c +++ b/sys/contrib/dev/rtw88/wow.c @@ -1,913 +1,913 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* Copyright(c) 2018-2019 Realtek Corporation */ #include "main.h" #include "fw.h" #include "wow.h" #include "reg.h" #include "debug.h" #include "mac.h" #include "ps.h" static void rtw_wow_show_wakeup_reason(struct rtw_dev *rtwdev) { struct cfg80211_wowlan_nd_info nd_info; struct cfg80211_wowlan_wakeup wakeup = { .pattern_idx = -1, }; u8 reason; reason = rtw_read8(rtwdev, REG_WOWLAN_WAKE_REASON); switch (reason) { case RTW_WOW_RSN_RX_DEAUTH: wakeup.disconnect = true; rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx deauth\n"); break; case RTW_WOW_RSN_DISCONNECT: wakeup.disconnect = true; rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: AP is off\n"); break; case RTW_WOW_RSN_RX_MAGIC_PKT: wakeup.magic_pkt = true; rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx magic packet\n"); break; case RTW_WOW_RSN_RX_GTK_REKEY: wakeup.gtk_rekey_failure = true; rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx gtk rekey\n"); break; case RTW_WOW_RSN_RX_PATTERN_MATCH: /* Current firmware and driver don't report pattern index * Use pattern_idx to 0 defaultly. */ wakeup.pattern_idx = 0; rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx pattern match packet\n"); break; case RTW_WOW_RSN_RX_NLO: /* Current firmware and driver don't report ssid index. * Use 0 for n_matches based on its comment. */ nd_info.n_matches = 0; wakeup.net_detect = &nd_info; rtw_dbg(rtwdev, RTW_DBG_WOW, "Rx NLO\n"); break; default: rtw_warn(rtwdev, "Unknown wakeup reason %x\n", reason); ieee80211_report_wowlan_wakeup(rtwdev->wow.wow_vif, NULL, GFP_KERNEL); return; } ieee80211_report_wowlan_wakeup(rtwdev->wow.wow_vif, &wakeup, GFP_KERNEL); } static void rtw_wow_pattern_write_cam(struct rtw_dev *rtwdev, u8 addr, u32 wdata) { rtw_write32(rtwdev, REG_WKFMCAM_RWD, wdata); rtw_write32(rtwdev, REG_WKFMCAM_CMD, BIT_WKFCAM_POLLING_V1 | BIT_WKFCAM_WE | BIT_WKFCAM_ADDR_V2(addr)); if (!check_hw_ready(rtwdev, REG_WKFMCAM_CMD, BIT_WKFCAM_POLLING_V1, 0)) rtw_err(rtwdev, "failed to write pattern cam\n"); } static void rtw_wow_pattern_write_cam_ent(struct rtw_dev *rtwdev, u8 id, struct rtw_wow_pattern *rtw_pattern) { int i; u8 addr; u32 wdata; for (i = 0; i < RTW_MAX_PATTERN_MASK_SIZE / 4; i++) { addr = (id << 3) + i; wdata = rtw_pattern->mask[i * 4]; wdata |= rtw_pattern->mask[i * 4 + 1] << 8; wdata |= rtw_pattern->mask[i * 4 + 2] << 16; wdata |= rtw_pattern->mask[i * 4 + 3] << 24; rtw_wow_pattern_write_cam(rtwdev, addr, wdata); } wdata = rtw_pattern->crc; addr = (id << 3) + RTW_MAX_PATTERN_MASK_SIZE / 4; switch (rtw_pattern->type) { case RTW_PATTERN_BROADCAST: wdata |= BIT_WKFMCAM_BC | BIT_WKFMCAM_VALID; break; case RTW_PATTERN_MULTICAST: wdata |= BIT_WKFMCAM_MC | BIT_WKFMCAM_VALID; break; case RTW_PATTERN_UNICAST: wdata |= BIT_WKFMCAM_UC | BIT_WKFMCAM_VALID; break; default: break; } rtw_wow_pattern_write_cam(rtwdev, addr, wdata); } /* RTK internal CRC16 for Pattern Cam */ static u16 __rtw_cal_crc16(u8 data, u16 crc) { u8 shift_in, data_bit; u8 crc_bit4, crc_bit11, crc_bit15; u16 crc_result; int index; for (index = 0; index < 8; index++) { crc_bit15 = ((crc & BIT(15)) ? 1 : 0); data_bit = (data & (BIT(0) << index) ? 1 : 0); shift_in = crc_bit15 ^ data_bit; crc_result = crc << 1; if (shift_in == 0) crc_result &= (~BIT(0)); else crc_result |= BIT(0); crc_bit11 = ((crc & BIT(11)) ? 1 : 0) ^ shift_in; if (crc_bit11 == 0) crc_result &= (~BIT(12)); else crc_result |= BIT(12); crc_bit4 = ((crc & BIT(4)) ? 1 : 0) ^ shift_in; if (crc_bit4 == 0) crc_result &= (~BIT(5)); else crc_result |= BIT(5); crc = crc_result; } return crc; } static u16 rtw_calc_crc(u8 *pdata, int length) { u16 crc = 0xffff; int i; for (i = 0; i < length; i++) crc = __rtw_cal_crc16(pdata[i], crc); /* get 1' complement */ return ~crc; } static void rtw_wow_pattern_generate(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif, const struct cfg80211_pkt_pattern *pkt_pattern, struct rtw_wow_pattern *rtw_pattern) { const u8 *mask; const u8 *pattern; u8 mask_hw[RTW_MAX_PATTERN_MASK_SIZE] = {0}; u8 content[RTW_MAX_PATTERN_SIZE] = {0}; u8 mac_addr[ETH_ALEN] = {0}; u8 mask_len; u16 count; int len; int i; pattern = pkt_pattern->pattern; len = pkt_pattern->pattern_len; mask = pkt_pattern->mask; ether_addr_copy(mac_addr, rtwvif->mac_addr); memset(rtw_pattern, 0, sizeof(*rtw_pattern)); mask_len = DIV_ROUND_UP(len, 8); if (is_broadcast_ether_addr(pattern)) rtw_pattern->type = RTW_PATTERN_BROADCAST; else if (is_multicast_ether_addr(pattern)) rtw_pattern->type = RTW_PATTERN_MULTICAST; else if (ether_addr_equal(pattern, mac_addr)) rtw_pattern->type = RTW_PATTERN_UNICAST; else rtw_pattern->type = RTW_PATTERN_INVALID; /* translate mask from os to mask for hw * pattern from OS uses 'ethenet frame', like this: * | 6 | 6 | 2 | 20 | Variable | 4 | * |--------+--------+------+-----------+------------+-----| * | 802.3 Mac Header | IP Header | TCP Packet | FCS | * | DA | SA | Type | * * BUT, packet catched by our HW is in '802.11 frame', begin from LLC * | 24 or 30 | 6 | 2 | 20 | Variable | 4 | * |-------------------+--------+------+-----------+------------+-----| * | 802.11 MAC Header | LLC | IP Header | TCP Packet | FCS | * | Others | Tpye | * * Therefore, we need translate mask_from_OS to mask_to_hw. * We should left-shift mask by 6 bits, then set the new bit[0~5] = 0, * because new mask[0~5] means 'SA', but our HW packet begins from LLC, * bit[0~5] corresponds to first 6 Bytes in LLC, they just don't match. */ /* Shift 6 bits */ for (i = 0; i < mask_len - 1; i++) { mask_hw[i] = u8_get_bits(mask[i], GENMASK(7, 6)); mask_hw[i] |= u8_get_bits(mask[i + 1], GENMASK(5, 0)) << 2; } mask_hw[i] = u8_get_bits(mask[i], GENMASK(7, 6)); /* Set bit 0-5 to zero */ mask_hw[0] &= (~GENMASK(5, 0)); memcpy(rtw_pattern->mask, mask_hw, RTW_MAX_PATTERN_MASK_SIZE); /* To get the wake up pattern from the mask. * We do not count first 12 bits which means * DA[6] and SA[6] in the pattern to match HW design. */ count = 0; for (i = 12; i < len; i++) { if ((mask[i / 8] >> (i % 8)) & 0x01) { content[count] = pattern[i]; count++; } } rtw_pattern->crc = rtw_calc_crc(content, count); } static void rtw_wow_pattern_clear_cam(struct rtw_dev *rtwdev) { bool ret; rtw_write32(rtwdev, REG_WKFMCAM_CMD, BIT_WKFCAM_POLLING_V1 | BIT_WKFCAM_CLR_V1); ret = check_hw_ready(rtwdev, REG_WKFMCAM_CMD, BIT_WKFCAM_POLLING_V1, 0); if (!ret) rtw_err(rtwdev, "failed to clean pattern cam\n"); } static void rtw_wow_pattern_write(struct rtw_dev *rtwdev) { struct rtw_wow_param *rtw_wow = &rtwdev->wow; struct rtw_wow_pattern *rtw_pattern = rtw_wow->patterns; int i = 0; for (i = 0; i < rtw_wow->pattern_cnt; i++) rtw_wow_pattern_write_cam_ent(rtwdev, i, rtw_pattern + i); } static void rtw_wow_pattern_clear(struct rtw_dev *rtwdev) { struct rtw_wow_param *rtw_wow = &rtwdev->wow; rtw_wow_pattern_clear_cam(rtwdev); rtw_wow->pattern_cnt = 0; memset(rtw_wow->patterns, 0, sizeof(rtw_wow->patterns)); } static void rtw_wow_bb_stop(struct rtw_dev *rtwdev) { struct rtw_wow_param *rtw_wow = &rtwdev->wow; /* wait 100ms for firmware to finish TX */ msleep(100); if (!rtw_read32_mask(rtwdev, REG_BCNQ_INFO, BIT_MGQ_CPU_EMPTY)) rtw_warn(rtwdev, "Wrong status of MGQ_CPU empty!\n"); rtw_wow->txpause = rtw_read8(rtwdev, REG_TXPAUSE); rtw_write8(rtwdev, REG_TXPAUSE, 0xff); rtw_write8_clr(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_BB_RSTB); } static void rtw_wow_bb_start(struct rtw_dev *rtwdev) { struct rtw_wow_param *rtw_wow = &rtwdev->wow; rtw_write8_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_BB_RSTB); rtw_write8(rtwdev, REG_TXPAUSE, rtw_wow->txpause); } static void rtw_wow_rx_dma_stop(struct rtw_dev *rtwdev) { /* wait 100ms for HW to finish rx dma */ msleep(100); rtw_write32_set(rtwdev, REG_RXPKT_NUM, BIT_RW_RELEASE); if (!check_hw_ready(rtwdev, REG_RXPKT_NUM, BIT_RXDMA_IDLE, 1)) rtw_err(rtwdev, "failed to stop rx dma\n"); } static void rtw_wow_rx_dma_start(struct rtw_dev *rtwdev) { rtw_write32_clr(rtwdev, REG_RXPKT_NUM, BIT_RW_RELEASE); } static int rtw_wow_check_fw_status(struct rtw_dev *rtwdev, bool wow_enable) { int ret; u8 check; u32 check_dis; if (wow_enable) { ret = read_poll_timeout(rtw_read8, check, !check, 1000, 100000, true, rtwdev, REG_WOWLAN_WAKE_REASON); if (ret) goto wow_fail; } else { ret = read_poll_timeout(rtw_read32_mask, check_dis, !check_dis, 1000, 100000, true, rtwdev, REG_FE1IMR, BIT_FS_RXDONE); if (ret) goto wow_fail; ret = read_poll_timeout(rtw_read32_mask, check_dis, !check_dis, 1000, 100000, false, rtwdev, REG_RXPKT_NUM, BIT_RW_RELEASE); if (ret) goto wow_fail; } return 0; wow_fail: rtw_err(rtwdev, "failed to check wow status %s\n", wow_enable ? "enabled" : "disabled"); return -EBUSY; } static void rtw_wow_fw_security_type_iter(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key, void *data) { struct rtw_fw_key_type_iter_data *iter_data = data; struct rtw_dev *rtwdev = hw->priv; u8 hw_key_type; if (vif != rtwdev->wow.wow_vif) return; switch (key->cipher) { case WLAN_CIPHER_SUITE_WEP40: hw_key_type = RTW_CAM_WEP40; break; case WLAN_CIPHER_SUITE_WEP104: hw_key_type = RTW_CAM_WEP104; break; case WLAN_CIPHER_SUITE_TKIP: hw_key_type = RTW_CAM_TKIP; key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; break; case WLAN_CIPHER_SUITE_CCMP: hw_key_type = RTW_CAM_AES; key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX; break; default: rtw_err(rtwdev, "Unsupported key type for wowlan mode: %#x\n", key->cipher); hw_key_type = 0; break; } if (sta) iter_data->pairwise_key_type = hw_key_type; else iter_data->group_key_type = hw_key_type; } static void rtw_wow_fw_security_type(struct rtw_dev *rtwdev) { struct rtw_fw_key_type_iter_data data = {}; struct ieee80211_vif *wow_vif = rtwdev->wow.wow_vif; data.rtwdev = rtwdev; rtw_iterate_keys(rtwdev, wow_vif, rtw_wow_fw_security_type_iter, &data); rtw_fw_set_aoac_global_info_cmd(rtwdev, data.pairwise_key_type, data.group_key_type); } static int rtw_wow_fw_start(struct rtw_dev *rtwdev) { if (rtw_wow_mgd_linked(rtwdev)) { rtw_send_rsvd_page_h2c(rtwdev); rtw_wow_pattern_write(rtwdev); rtw_wow_fw_security_type(rtwdev); rtw_fw_set_disconnect_decision_cmd(rtwdev, true); rtw_fw_set_keep_alive_cmd(rtwdev, true); } else if (rtw_wow_no_link(rtwdev)) { rtw_fw_set_nlo_info(rtwdev, true); rtw_fw_update_pkt_probe_req(rtwdev, NULL); rtw_fw_channel_switch(rtwdev, true); } rtw_fw_set_wowlan_ctrl_cmd(rtwdev, true); rtw_fw_set_remote_wake_ctrl_cmd(rtwdev, true); return rtw_wow_check_fw_status(rtwdev, true); } static int rtw_wow_fw_stop(struct rtw_dev *rtwdev) { if (rtw_wow_mgd_linked(rtwdev)) { rtw_fw_set_disconnect_decision_cmd(rtwdev, false); rtw_fw_set_keep_alive_cmd(rtwdev, false); rtw_wow_pattern_clear(rtwdev); } else if (rtw_wow_no_link(rtwdev)) { rtw_fw_channel_switch(rtwdev, false); rtw_fw_set_nlo_info(rtwdev, false); } rtw_fw_set_wowlan_ctrl_cmd(rtwdev, false); rtw_fw_set_remote_wake_ctrl_cmd(rtwdev, false); return rtw_wow_check_fw_status(rtwdev, false); } static void rtw_wow_avoid_reset_mac(struct rtw_dev *rtwdev) { /* When resuming from wowlan mode, some hosts issue signal * (PCIE: PREST, USB: SE0RST) to device, and lead to reset * mac core. If it happens, the connection to AP will be lost. * Setting REG_RSV_CTRL Register can avoid this process. */ switch (rtw_hci_type(rtwdev)) { case RTW_HCI_TYPE_PCIE: case RTW_HCI_TYPE_USB: rtw_write8(rtwdev, REG_RSV_CTRL, BIT_WLOCK_1C_B6); rtw_write8(rtwdev, REG_RSV_CTRL, BIT_WLOCK_1C_B6 | BIT_R_DIS_PRST); break; default: rtw_warn(rtwdev, "Unsupported hci type to disable reset MAC\n"); break; } } static void rtw_wow_fw_media_status_iter(void *data, struct ieee80211_sta *sta) { struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv; struct rtw_fw_media_status_iter_data *iter_data = data; struct rtw_dev *rtwdev = iter_data->rtwdev; rtw_fw_media_status_report(rtwdev, si->mac_id, iter_data->connect); } static void rtw_wow_fw_media_status(struct rtw_dev *rtwdev, bool connect) { struct rtw_fw_media_status_iter_data data; data.rtwdev = rtwdev; data.connect = connect; rtw_iterate_stas_atomic(rtwdev, rtw_wow_fw_media_status_iter, &data); } static int rtw_wow_config_wow_fw_rsvd_page(struct rtw_dev *rtwdev) { struct ieee80211_vif *wow_vif = rtwdev->wow.wow_vif; struct rtw_vif *rtwvif = (struct rtw_vif *)wow_vif->drv_priv; rtw_remove_rsvd_page(rtwdev, rtwvif); if (rtw_wow_no_link(rtwdev)) rtw_add_rsvd_page_pno(rtwdev, rtwvif); else rtw_add_rsvd_page_sta(rtwdev, rtwvif); return rtw_fw_download_rsvd_page(rtwdev); } static int rtw_wow_config_normal_fw_rsvd_page(struct rtw_dev *rtwdev) { struct ieee80211_vif *wow_vif = rtwdev->wow.wow_vif; struct rtw_vif *rtwvif = (struct rtw_vif *)wow_vif->drv_priv; rtw_remove_rsvd_page(rtwdev, rtwvif); rtw_add_rsvd_page_sta(rtwdev, rtwvif); if (rtw_wow_no_link(rtwdev)) return 0; return rtw_fw_download_rsvd_page(rtwdev); } static int rtw_wow_swap_fw(struct rtw_dev *rtwdev, enum rtw_fw_type type) { struct rtw_fw_state *fw; int ret; switch (type) { case RTW_WOWLAN_FW: fw = &rtwdev->wow_fw; break; case RTW_NORMAL_FW: fw = &rtwdev->fw; break; default: rtw_warn(rtwdev, "unsupported firmware type to swap\n"); return -ENOENT; } ret = rtw_download_firmware(rtwdev, fw); if (ret) goto out; rtw_fw_send_general_info(rtwdev); rtw_fw_send_phydm_info(rtwdev); rtw_wow_fw_media_status(rtwdev, true); out: return ret; } static void rtw_wow_check_pno(struct rtw_dev *rtwdev, struct cfg80211_sched_scan_request *nd_config) { struct rtw_wow_param *rtw_wow = &rtwdev->wow; struct rtw_pno_request *pno_req = &rtw_wow->pno_req; struct ieee80211_channel *channel; int i, size; if (!nd_config->n_match_sets || !nd_config->n_channels) goto err; pno_req->match_set_cnt = nd_config->n_match_sets; size = sizeof(*pno_req->match_sets) * pno_req->match_set_cnt; pno_req->match_sets = kmemdup(nd_config->match_sets, size, GFP_KERNEL); if (!pno_req->match_sets) goto err; pno_req->channel_cnt = nd_config->n_channels; size = sizeof(*nd_config->channels[0]) * nd_config->n_channels; pno_req->channels = kmalloc(size, GFP_KERNEL); if (!pno_req->channels) goto channel_err; for (i = 0 ; i < pno_req->channel_cnt; i++) { channel = pno_req->channels + i; memcpy(channel, nd_config->channels[i], sizeof(*channel)); } pno_req->scan_plan = *nd_config->scan_plans; pno_req->inited = true; rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: net-detect is enabled\n"); return; channel_err: kfree(pno_req->match_sets); err: rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: net-detect is disabled\n"); } static int rtw_wow_leave_linked_ps(struct rtw_dev *rtwdev) { if (!test_bit(RTW_FLAG_WOWLAN, rtwdev->flags)) cancel_delayed_work_sync(&rtwdev->watch_dog_work); rtw_leave_lps(rtwdev); return 0; } static int rtw_wow_leave_no_link_ps(struct rtw_dev *rtwdev) { struct rtw_wow_param *rtw_wow = &rtwdev->wow; int ret = 0; if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags)) { if (rtw_get_lps_deep_mode(rtwdev) != LPS_DEEP_MODE_NONE) rtw_leave_lps_deep(rtwdev); } else { - if (test_bit(RTW_FLAG_INACTIVE_PS, rtwdev->flags)) { + if (!test_bit(RTW_FLAG_POWERON, rtwdev->flags)) { rtw_wow->ips_enabled = true; ret = rtw_leave_ips(rtwdev); if (ret) return ret; } } return 0; } static int rtw_wow_leave_ps(struct rtw_dev *rtwdev) { int ret = 0; if (rtw_wow_mgd_linked(rtwdev)) ret = rtw_wow_leave_linked_ps(rtwdev); else if (rtw_wow_no_link(rtwdev)) ret = rtw_wow_leave_no_link_ps(rtwdev); return ret; } static int rtw_wow_restore_ps(struct rtw_dev *rtwdev) { int ret = 0; if (rtw_wow_no_link(rtwdev) && rtwdev->wow.ips_enabled) ret = rtw_enter_ips(rtwdev); return ret; } static int rtw_wow_enter_linked_ps(struct rtw_dev *rtwdev) { struct rtw_wow_param *rtw_wow = &rtwdev->wow; struct ieee80211_vif *wow_vif = rtw_wow->wow_vif; struct rtw_vif *rtwvif = (struct rtw_vif *)wow_vif->drv_priv; rtw_enter_lps(rtwdev, rtwvif->port); return 0; } static int rtw_wow_enter_no_link_ps(struct rtw_dev *rtwdev) { /* firmware enters deep ps by itself if supported */ set_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags); return 0; } static int rtw_wow_enter_ps(struct rtw_dev *rtwdev) { int ret = 0; if (rtw_wow_mgd_linked(rtwdev)) ret = rtw_wow_enter_linked_ps(rtwdev); else if (rtw_wow_no_link(rtwdev) && rtw_get_lps_deep_mode(rtwdev) != LPS_DEEP_MODE_NONE) ret = rtw_wow_enter_no_link_ps(rtwdev); return ret; } static void rtw_wow_stop_trx(struct rtw_dev *rtwdev) { rtw_wow_bb_stop(rtwdev); rtw_wow_rx_dma_stop(rtwdev); } static int rtw_wow_start(struct rtw_dev *rtwdev) { int ret; ret = rtw_wow_fw_start(rtwdev); if (ret) goto out; rtw_hci_stop(rtwdev); rtw_wow_bb_start(rtwdev); rtw_wow_avoid_reset_mac(rtwdev); out: return ret; } static int rtw_wow_enable(struct rtw_dev *rtwdev) { int ret = 0; rtw_wow_stop_trx(rtwdev); ret = rtw_wow_swap_fw(rtwdev, RTW_WOWLAN_FW); if (ret) { rtw_err(rtwdev, "failed to swap wow fw\n"); goto error; } set_bit(RTW_FLAG_WOWLAN, rtwdev->flags); ret = rtw_wow_config_wow_fw_rsvd_page(rtwdev); if (ret) { rtw_err(rtwdev, "failed to download wowlan rsvd page\n"); goto error; } ret = rtw_wow_start(rtwdev); if (ret) { rtw_err(rtwdev, "failed to start wow\n"); goto error; } return ret; error: clear_bit(RTW_FLAG_WOWLAN, rtwdev->flags); return ret; } static int rtw_wow_stop(struct rtw_dev *rtwdev) { int ret; /* some HCI related registers will be reset after resume, * need to set them again. */ ret = rtw_hci_setup(rtwdev); if (ret) { rtw_err(rtwdev, "failed to setup hci\n"); return ret; } ret = rtw_hci_start(rtwdev); if (ret) { rtw_err(rtwdev, "failed to start hci\n"); return ret; } ret = rtw_wow_fw_stop(rtwdev); if (ret) rtw_err(rtwdev, "failed to stop wowlan fw\n"); rtw_wow_bb_stop(rtwdev); return ret; } static void rtw_wow_resume_trx(struct rtw_dev *rtwdev) { rtw_wow_rx_dma_start(rtwdev); rtw_wow_bb_start(rtwdev); ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->watch_dog_work, RTW_WATCH_DOG_DELAY_TIME); } static int rtw_wow_disable(struct rtw_dev *rtwdev) { int ret; clear_bit(RTW_FLAG_WOWLAN, rtwdev->flags); ret = rtw_wow_stop(rtwdev); if (ret) { rtw_err(rtwdev, "failed to stop wow\n"); goto out; } ret = rtw_wow_swap_fw(rtwdev, RTW_NORMAL_FW); if (ret) { rtw_err(rtwdev, "failed to swap normal fw\n"); goto out; } ret = rtw_wow_config_normal_fw_rsvd_page(rtwdev); if (ret) rtw_err(rtwdev, "failed to download normal rsvd page\n"); out: rtw_wow_resume_trx(rtwdev); return ret; } static void rtw_wow_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { struct rtw_dev *rtwdev = data; struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; struct rtw_wow_param *rtw_wow = &rtwdev->wow; /* Current wowlan function support setting of only one STATION vif. * So when one suitable vif is found, stop the iteration. */ if (rtw_wow->wow_vif || vif->type != NL80211_IFTYPE_STATION) return; switch (rtwvif->net_type) { case RTW_NET_MGD_LINKED: rtw_wow->wow_vif = vif; break; case RTW_NET_NO_LINK: if (rtw_wow->pno_req.inited) rtwdev->wow.wow_vif = vif; break; default: break; } } static int rtw_wow_set_wakeups(struct rtw_dev *rtwdev, struct cfg80211_wowlan *wowlan) { struct rtw_wow_param *rtw_wow = &rtwdev->wow; struct rtw_wow_pattern *rtw_patterns = rtw_wow->patterns; struct rtw_vif *rtwvif; int i; if (wowlan->disconnect) set_bit(RTW_WOW_FLAG_EN_DISCONNECT, rtw_wow->flags); if (wowlan->magic_pkt) set_bit(RTW_WOW_FLAG_EN_MAGIC_PKT, rtw_wow->flags); if (wowlan->gtk_rekey_failure) set_bit(RTW_WOW_FLAG_EN_REKEY_PKT, rtw_wow->flags); if (wowlan->nd_config) rtw_wow_check_pno(rtwdev, wowlan->nd_config); rtw_iterate_vifs_atomic(rtwdev, rtw_wow_vif_iter, rtwdev); if (!rtw_wow->wow_vif) return -EPERM; rtwvif = (struct rtw_vif *)rtw_wow->wow_vif->drv_priv; if (wowlan->n_patterns && wowlan->patterns) { rtw_wow->pattern_cnt = wowlan->n_patterns; for (i = 0; i < wowlan->n_patterns; i++) rtw_wow_pattern_generate(rtwdev, rtwvif, wowlan->patterns + i, rtw_patterns + i); } return 0; } static void rtw_wow_clear_wakeups(struct rtw_dev *rtwdev) { struct rtw_wow_param *rtw_wow = &rtwdev->wow; struct rtw_pno_request *pno_req = &rtw_wow->pno_req; if (pno_req->inited) { kfree(pno_req->channels); kfree(pno_req->match_sets); } memset(rtw_wow, 0, sizeof(rtwdev->wow)); } int rtw_wow_suspend(struct rtw_dev *rtwdev, struct cfg80211_wowlan *wowlan) { int ret = 0; ret = rtw_wow_set_wakeups(rtwdev, wowlan); if (ret) { rtw_err(rtwdev, "failed to set wakeup event\n"); goto out; } ret = rtw_wow_leave_ps(rtwdev); if (ret) { rtw_err(rtwdev, "failed to leave ps from normal mode\n"); goto out; } ret = rtw_wow_enable(rtwdev); if (ret) { rtw_err(rtwdev, "failed to enable wow\n"); rtw_wow_restore_ps(rtwdev); goto out; } ret = rtw_wow_enter_ps(rtwdev); if (ret) rtw_err(rtwdev, "failed to enter ps for wow\n"); out: return ret; } int rtw_wow_resume(struct rtw_dev *rtwdev) { int ret; /* If wowlan mode is not enabled, do nothing */ if (!test_bit(RTW_FLAG_WOWLAN, rtwdev->flags)) { rtw_err(rtwdev, "wow is not enabled\n"); ret = -EPERM; goto out; } ret = rtw_wow_leave_ps(rtwdev); if (ret) { rtw_err(rtwdev, "failed to leave ps from wowlan mode\n"); goto out; } rtw_wow_show_wakeup_reason(rtwdev); ret = rtw_wow_disable(rtwdev); if (ret) { rtw_err(rtwdev, "failed to disable wow\n"); goto out; } ret = rtw_wow_restore_ps(rtwdev); if (ret) rtw_err(rtwdev, "failed to restore ps to normal mode\n"); out: rtw_wow_clear_wakeups(rtwdev); return ret; }