u8 cck_rpt_b_ofdm_cfosho_b;
u8 reserved_1;
u8 noise_power_db_msb;
- u8 path_cfotail[RTL8723A_MAX_RF_PATHS];
+ s8 path_cfotail[RTL8723A_MAX_RF_PATHS];
u8 pcts_mask[RTL8723A_MAX_RF_PATHS];
s8 stream_rxevm[RTL8723A_MAX_RF_PATHS];
u8 path_rxsnr[RTL8723A_MAX_RF_PATHS];
u8 desc_rate;
};
+#define CFO_TH_XTAL_HIGH 20 /* kHz */
+#define CFO_TH_XTAL_LOW 10 /* kHz */
+#define CFO_TH_ATC 80 /* kHz */
+
+struct rtl8xxxu_cfo_tracking {
+ bool adjust;
+ bool atc_status;
+ int cfo_tail[2];
+ u8 crystal_cap;
+ u32 packet_count;
+ u32 packet_count_pre;
+};
+
struct rtl8xxxu_priv {
struct ieee80211_hw *hw;
struct usb_device *udev;
u32 ep_tx_high_queue:1;
u32 ep_tx_normal_queue:1;
u32 ep_tx_low_queue:1;
- u32 has_xtalk:1;
u32 rx_buf_aggregation:1;
- u8 xtalk;
+ u8 default_crystal_cap;
unsigned int pipe_interrupt;
unsigned int pipe_in;
unsigned int pipe_out[TXDESC_QUEUE_MAX];
struct sk_buff_head c2hcmd_queue;
struct rtl8xxxu_btcoex bt_coex;
struct rtl8xxxu_ra_report ra_report;
+ struct rtl8xxxu_cfo_tracking cfo_tracking;
};
struct rtl8xxxu_rx_urb {
struct rtl8xxxu_txdesc32 *tx_desc, bool sgi,
bool short_preamble, bool ampdu_enable,
u32 rts_rate);
+ void (*set_crystal_cap) (struct rtl8xxxu_priv *priv, u8 crystal_cap);
int writeN_block_size;
int rx_agg_buf_size;
char tx_desc_size;
void rtl8723bu_set_ps_tdma(struct rtl8xxxu_priv *priv,
u8 arg1, u8 arg2, u8 arg3, u8 arg4, u8 arg5);
void rtl8723bu_phy_init_antenna_selection(struct rtl8xxxu_priv *priv);
+void rtl8723a_set_crystal_cap(struct rtl8xxxu_priv *priv, u8 crystal_cap);
extern struct rtl8xxxu_fileops rtl8188fu_fops;
extern struct rtl8xxxu_fileops rtl8192cu_fops;
priv->ofdm_tx_power_diff[0].a = efuse->tx_power_index_A.ht20_ofdm_1s_diff.a;
priv->ht20_tx_power_diff[0].a = efuse->tx_power_index_A.ht20_ofdm_1s_diff.b;
- priv->xtalk = efuse->xtal_k & 0x3f;
+ priv->default_crystal_cap = efuse->xtal_k & 0x3f;
dev_info(&priv->udev->dev, "Vendor: %.7s\n", efuse->vendor_name);
dev_info(&priv->udev->dev, "Product: %.7s\n", efuse->device_name);
{
u8 val8;
u16 val16;
- u32 val32;
/* Enable BB and RF */
val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC);
rtl8xxxu_init_phy_regs(priv, rtl8188fu_phy_init_table);
rtl8xxxu_init_phy_regs(priv, rtl8188f_agc_table);
-
- val32 = rtl8xxxu_read32(priv, REG_AFE_XTAL_CTRL);
- val8 = priv->xtalk;
- val32 &= ~0x007FF800;
- val32 |= ((val8 | (val8 << 6)) << 11);
- rtl8xxxu_write32(priv, REG_AFE_XTAL_CTRL, val32);
}
static int rtl8188fu_init_phy_rf(struct rtl8xxxu_priv *priv)
rtl8xxxu_write32(priv, REG_TXDMA_OFFSET_CHK, val32);
}
+#define XTAL1 GENMASK(22, 17)
+#define XTAL0 GENMASK(16, 11)
+
+static void rtl8188f_set_crystal_cap(struct rtl8xxxu_priv *priv, u8 crystal_cap)
+{
+ struct rtl8xxxu_cfo_tracking *cfo = &priv->cfo_tracking;
+ u32 val32;
+
+ if (crystal_cap == cfo->crystal_cap)
+ return;
+
+ val32 = rtl8xxxu_read32(priv, REG_AFE_XTAL_CTRL);
+
+ dev_dbg(&priv->udev->dev,
+ "%s: Adjusting crystal cap from 0x%x (actually 0x%lx 0x%lx) to 0x%x\n",
+ __func__,
+ cfo->crystal_cap,
+ FIELD_GET(XTAL1, val32),
+ FIELD_GET(XTAL0, val32),
+ crystal_cap);
+
+ val32 &= ~(XTAL1 | XTAL0);
+ val32 |= FIELD_PREP(XTAL1, crystal_cap) |
+ FIELD_PREP(XTAL0, crystal_cap);
+ rtl8xxxu_write32(priv, REG_AFE_XTAL_CTRL, val32);
+
+ cfo->crystal_cap = crystal_cap;
+}
+
struct rtl8xxxu_fileops rtl8188fu_fops = {
.parse_efuse = rtl8188fu_parse_efuse,
.load_firmware = rtl8188fu_load_firmware,
.update_rate_mask = rtl8xxxu_gen2_update_rate_mask,
.report_connect = rtl8xxxu_gen2_report_connect,
.fill_txdesc = rtl8xxxu_fill_txdesc_v2,
+ .set_crystal_cap = rtl8188f_set_crystal_cap,
.writeN_block_size = 128,
.rx_desc_size = sizeof(struct rtl8xxxu_rxdesc24),
.tx_desc_size = sizeof(struct rtl8xxxu_txdesc40),
efuse->tx_power_index_B.pwr_diff[i - 1].ht40;
}
- priv->has_xtalk = 1;
- priv->xtalk = priv->efuse_wifi.efuse8192eu.xtal_k & 0x3f;
+ priv->default_crystal_cap = priv->efuse_wifi.efuse8192eu.xtal_k & 0x3f;
/*
* device_info section seems to be laid out as records
.update_rate_mask = rtl8xxxu_gen2_update_rate_mask,
.report_connect = rtl8xxxu_gen2_report_connect,
.fill_txdesc = rtl8xxxu_fill_txdesc_v2,
+ .set_crystal_cap = rtl8723a_set_crystal_cap,
.writeN_block_size = 128,
.tx_desc_size = sizeof(struct rtl8xxxu_txdesc40),
.rx_desc_size = sizeof(struct rtl8xxxu_rxdesc24),
efuse->ht20_max_power_offset,
sizeof(efuse->ht20_max_power_offset));
- if (priv->efuse_wifi.efuse8723.version >= 0x01) {
- priv->has_xtalk = 1;
- priv->xtalk = priv->efuse_wifi.efuse8723.xtal_k & 0x3f;
- }
+ if (priv->efuse_wifi.efuse8723.version >= 0x01)
+ priv->default_crystal_cap = priv->efuse_wifi.efuse8723.xtal_k & 0x3f;
+ else
+ priv->fops->set_crystal_cap = NULL;
priv->power_base = &rtl8723a_power_base;
return ret;
}
+#define XTAL1 GENMASK(23, 18)
+#define XTAL0 GENMASK(17, 12)
+
+void rtl8723a_set_crystal_cap(struct rtl8xxxu_priv *priv, u8 crystal_cap)
+{
+ struct rtl8xxxu_cfo_tracking *cfo = &priv->cfo_tracking;
+ u32 val32;
+
+ if (crystal_cap == cfo->crystal_cap)
+ return;
+
+ val32 = rtl8xxxu_read32(priv, REG_MAC_PHY_CTRL);
+
+ dev_dbg(&priv->udev->dev,
+ "%s: Adjusting crystal cap from 0x%x (actually 0x%lx 0x%lx) to 0x%x\n",
+ __func__,
+ cfo->crystal_cap,
+ FIELD_GET(XTAL1, val32),
+ FIELD_GET(XTAL0, val32),
+ crystal_cap);
+
+ val32 &= ~(XTAL1 | XTAL0);
+ val32 |= FIELD_PREP(XTAL1, crystal_cap) |
+ FIELD_PREP(XTAL0, crystal_cap);
+ rtl8xxxu_write32(priv, REG_MAC_PHY_CTRL, val32);
+
+ cfo->crystal_cap = crystal_cap;
+}
+
struct rtl8xxxu_fileops rtl8723au_fops = {
.parse_efuse = rtl8723au_parse_efuse,
.load_firmware = rtl8723au_load_firmware,
.update_rate_mask = rtl8xxxu_update_rate_mask,
.report_connect = rtl8xxxu_gen1_report_connect,
.fill_txdesc = rtl8xxxu_fill_txdesc_v1,
+ .set_crystal_cap = rtl8723a_set_crystal_cap,
.writeN_block_size = 1024,
.rx_agg_buf_size = 16000,
.tx_desc_size = sizeof(struct rtl8xxxu_txdesc32),
efuse->tx_power_index_B.pwr_diff[i - 1].ht40;
}
- priv->has_xtalk = 1;
- priv->xtalk = priv->efuse_wifi.efuse8723bu.xtal_k & 0x3f;
+ priv->default_crystal_cap = priv->efuse_wifi.efuse8723bu.xtal_k & 0x3f;
dev_info(&priv->udev->dev, "Vendor: %.7s\n", efuse->vendor_name);
dev_info(&priv->udev->dev, "Product: %.41s\n", efuse->device_name);
.update_rate_mask = rtl8xxxu_gen2_update_rate_mask,
.report_connect = rtl8xxxu_gen2_report_connect,
.fill_txdesc = rtl8xxxu_fill_txdesc_v2,
+ .set_crystal_cap = rtl8723a_set_crystal_cap,
.writeN_block_size = 1024,
.tx_desc_size = sizeof(struct rtl8xxxu_txdesc40),
.rx_desc_size = sizeof(struct rtl8xxxu_rxdesc24),
*/
static int rtl8xxxu_init_phy_bb(struct rtl8xxxu_priv *priv)
{
- u8 val8;
u32 val32;
priv->fops->init_phy_bb(priv);
rtl8xxxu_write32(priv, REG_TX_TO_TX, val32);
}
- if (priv->has_xtalk) {
- val32 = rtl8xxxu_read32(priv, REG_MAC_PHY_CTRL);
-
- val8 = priv->xtalk;
- val32 &= 0xff000fff;
- val32 |= ((val8 | (val8 << 6)) << 12);
-
- rtl8xxxu_write32(priv, REG_MAC_PHY_CTRL, val32);
- }
+ if (priv->fops->set_crystal_cap)
+ priv->fops->set_crystal_cap(priv, priv->default_crystal_cap);
if (priv->rtl_chip == RTL8192E)
rtl8xxxu_write32(priv, REG_AFE_XTAL_CTRL, 0x000f81fb);
val32 |= 0x0007e000;
rtl8xxxu_write32(priv, REG_AFE_MISC, val32);
}
+
+ /* Initialise the center frequency offset tracking */
+ if (priv->fops->set_crystal_cap) {
+ val32 = rtl8xxxu_read32(priv, REG_OFDM1_CFO_TRACKING);
+ priv->cfo_tracking.atc_status = val32 & CFO_TRACKING_ATC_STATUS;
+ priv->cfo_tracking.adjust = true;
+ priv->cfo_tracking.crystal_cap = priv->default_crystal_cap;
+ }
+
exit:
return ret;
}
static void rtl8xxxu_rx_parse_phystats(struct rtl8xxxu_priv *priv,
struct ieee80211_rx_status *rx_status,
struct rtl8723au_phy_stats *phy_stats,
- u32 rxmcs)
+ u32 rxmcs, struct ieee80211_hdr *hdr,
+ bool crc_icv_err)
{
if (phy_stats->sgi_en)
rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
break;
}
} else {
+ bool parse_cfo = priv->fops->set_crystal_cap &&
+ priv->vif &&
+ priv->vif->type == NL80211_IFTYPE_STATION &&
+ priv->vif->cfg.assoc &&
+ !crc_icv_err &&
+ !ieee80211_is_ctl(hdr->frame_control) &&
+ ether_addr_equal(priv->vif->bss_conf.bssid, hdr->addr2);
+
+ if (parse_cfo) {
+ priv->cfo_tracking.cfo_tail[0] = phy_stats->path_cfotail[0];
+ priv->cfo_tracking.cfo_tail[1] = phy_stats->path_cfotail[1];
+
+ priv->cfo_tracking.packet_count++;
+ }
+
rx_status->signal =
(phy_stats->cck_sig_qual_ofdm_pwdb_all >> 1) - 110;
}
if (rx_desc->phy_stats)
rtl8xxxu_rx_parse_phystats(priv, rx_status, phy_stats,
- rx_desc->rxmcs);
+ rx_desc->rxmcs, (struct ieee80211_hdr *)skb->data,
+ rx_desc->crc32 || rx_desc->icverr);
rx_status->mactime = rx_desc->tsfl;
rx_status->flag |= RX_FLAG_MACTIME_START;
if (rx_desc->phy_stats)
rtl8xxxu_rx_parse_phystats(priv, rx_status, phy_stats,
- rx_desc->rxmcs);
+ rx_desc->rxmcs, (struct ieee80211_hdr *)skb->data,
+ rx_desc->crc32 || rx_desc->icverr);
rx_status->mactime = rx_desc->tsfl;
rx_status->flag |= RX_FLAG_MACTIME_START;
}
}
+static void rtl8xxxu_set_atc_status(struct rtl8xxxu_priv *priv, bool atc_status)
+{
+ struct rtl8xxxu_cfo_tracking *cfo = &priv->cfo_tracking;
+ u32 val32;
+
+ if (atc_status == cfo->atc_status)
+ return;
+
+ cfo->atc_status = atc_status;
+
+ val32 = rtl8xxxu_read32(priv, REG_OFDM1_CFO_TRACKING);
+ if (atc_status)
+ val32 |= CFO_TRACKING_ATC_STATUS;
+ else
+ val32 &= ~CFO_TRACKING_ATC_STATUS;
+ rtl8xxxu_write32(priv, REG_OFDM1_CFO_TRACKING, val32);
+}
+
+/* Central frequency offset correction */
+static void rtl8xxxu_track_cfo(struct rtl8xxxu_priv *priv)
+{
+ struct rtl8xxxu_cfo_tracking *cfo = &priv->cfo_tracking;
+ int cfo_khz_a, cfo_khz_b, cfo_average;
+ int crystal_cap;
+
+ if (!priv->vif || !priv->vif->cfg.assoc) {
+ /* Reset */
+ cfo->adjust = true;
+
+ if (cfo->crystal_cap > priv->default_crystal_cap)
+ priv->fops->set_crystal_cap(priv, cfo->crystal_cap - 1);
+ else if (cfo->crystal_cap < priv->default_crystal_cap)
+ priv->fops->set_crystal_cap(priv, cfo->crystal_cap + 1);
+
+ rtl8xxxu_set_atc_status(priv, true);
+
+ return;
+ }
+
+ if (cfo->packet_count == cfo->packet_count_pre)
+ /* No new information. */
+ return;
+
+ cfo->packet_count_pre = cfo->packet_count;
+
+ /* CFO_tail[1:0] is S(8,7), (num_subcarrier>>7) x 312.5K = CFO value(K Hz) */
+ cfo_khz_a = (int)((cfo->cfo_tail[0] * 3125) / 10) >> 7;
+ cfo_khz_b = (int)((cfo->cfo_tail[1] * 3125) / 10) >> 7;
+
+ if (priv->tx_paths == 1)
+ cfo_average = cfo_khz_a;
+ else
+ cfo_average = (cfo_khz_a + cfo_khz_b) / 2;
+
+ dev_dbg(&priv->udev->dev, "cfo_average: %d\n", cfo_average);
+
+ if (cfo->adjust) {
+ if (abs(cfo_average) < CFO_TH_XTAL_LOW)
+ cfo->adjust = false;
+ } else {
+ if (abs(cfo_average) > CFO_TH_XTAL_HIGH)
+ cfo->adjust = true;
+ }
+
+ /*
+ * TODO: We should return here only if bluetooth is enabled.
+ * See the vendor drivers for how to determine that.
+ */
+ if (priv->has_bluetooth)
+ return;
+
+ if (!cfo->adjust)
+ return;
+
+ crystal_cap = cfo->crystal_cap;
+
+ if (cfo_average > CFO_TH_XTAL_LOW)
+ crystal_cap++;
+ else if (cfo_average < -CFO_TH_XTAL_LOW)
+ crystal_cap--;
+
+ crystal_cap = clamp(crystal_cap, 0, 0x3f);
+
+ priv->fops->set_crystal_cap(priv, crystal_cap);
+
+ rtl8xxxu_set_atc_status(priv, abs(cfo_average) >= CFO_TH_ATC);
+}
+
static void rtl8xxxu_watchdog_callback(struct work_struct *work)
{
struct ieee80211_vif *vif;
rcu_read_unlock();
signal = ieee80211_ave_rssi(vif);
+
+ if (priv->fops->set_crystal_cap)
+ rtl8xxxu_track_cfo(priv);
+
rtl8xxxu_refresh_rate_mask(priv, signal, sta);
}
#define REG_OFDM1_TRX_PATH_ENABLE 0x0d04
#define REG_OFDM1_CFO_TRACKING 0x0d2c
+#define CFO_TRACKING_ATC_STATUS BIT(11)
#define REG_OFDM1_CSI_FIX_MASK1 0x0d40
#define REG_OFDM1_CSI_FIX_MASK2 0x0d44