From e0c27cdbbd414877864773152ad0291913e18eae Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 20 Apr 2020 13:50:50 +0800 Subject: rtw88: 8723d: Add RF read/write ops 8723D use SIPI to indirectly read RF register instead of directly read, so introduce a new struct rtw_rf_sipi_addr and new function rtw_phy_read_rf_sipi(). Since other chips don't use the new function, only 8723D needs to fill struct rtw_rf_sipi_addr in rtw_chip_info. Because there are two kinds of functions for reading RF registers now, change rtw_phy_read_rf() to chip->ops->read_rf() in rtw_phy_write_rf_reg_sipi() so that we can switch tp proper RF read functions depends on the type of the chip. Though 8723D is an 1x1 chip, it has two RF PHY and we can switch to one of them, and that should be configured properly. Hence, add a fix_rf_phy_num to struct rtw_chip_info to allow driver to set one of the PHY's registers for 8723D, even it is only 1x1. Another variable rf_phy_num is introduced to keep the constraint number of RF path we can access, and its value is: rf_phy_num = (fix_rf_phy_num ? fix_rf_phy_num : rf_path_num) Signed-off-by: Ping-Ke Shih Signed-off-by: Zong-Zhe Yang Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20200420055054.14592-5-yhchuang@realtek.com --- drivers/net/wireless/realtek/rtw88/phy.c | 56 +++++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/realtek/rtw88/phy.c') diff --git a/drivers/net/wireless/realtek/rtw88/phy.c b/drivers/net/wireless/realtek/rtw88/phy.c index 8793dd22188f..8489abfdc12e 100644 --- a/drivers/net/wireless/realtek/rtw88/phy.c +++ b/drivers/net/wireless/realtek/rtw88/phy.c @@ -679,7 +679,7 @@ u32 rtw_phy_read_rf(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, const u32 *base_addr = chip->rf_base_addr; u32 val, direct_addr; - if (rf_path >= hal->rf_path_num) { + if (rf_path >= hal->rf_phy_num) { rtw_err(rtwdev, "unsupported rf path (%d)\n", rf_path); return INV_RF_DATA; } @@ -693,6 +693,54 @@ u32 rtw_phy_read_rf(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, return val; } +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_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; +} + bool rtw_phy_write_rf_reg_sipi(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, u32 addr, u32 mask, u32 data) { @@ -703,7 +751,7 @@ bool rtw_phy_write_rf_reg_sipi(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, u32 old_data = 0; u32 shift; - if (rf_path >= hal->rf_path_num) { + if (rf_path >= hal->rf_phy_num) { rtw_err(rtwdev, "unsupported rf path (%d)\n", rf_path); return false; } @@ -712,7 +760,7 @@ bool rtw_phy_write_rf_reg_sipi(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, mask &= RFREG_MASK; if (mask != RFREG_MASK) { - old_data = rtw_phy_read_rf(rtwdev, rf_path, addr, 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"); @@ -740,7 +788,7 @@ bool rtw_phy_write_rf_reg(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, const u32 *base_addr = chip->rf_base_addr; u32 direct_addr; - if (rf_path >= hal->rf_path_num) { + if (rf_path >= hal->rf_phy_num) { rtw_err(rtwdev, "unsupported rf path (%d)\n", rf_path); return false; } -- cgit From fc637a860a825e934886498874f9f8372798a462 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Mon, 4 May 2020 18:50:09 +0800 Subject: rtw88: 8723d: Set IG register for CCK rate DIG sets only one IG register for most chips, but 8723D need to set additional register for CCK rate. Signed-off-by: Ping-Ke Shih Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20200504105010.10780-8-yhchuang@realtek.com --- drivers/net/wireless/realtek/rtw88/phy.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/net/wireless/realtek/rtw88/phy.c') diff --git a/drivers/net/wireless/realtek/rtw88/phy.c b/drivers/net/wireless/realtek/rtw88/phy.c index 8489abfdc12e..72a16eff9db3 100644 --- a/drivers/net/wireless/realtek/rtw88/phy.c +++ b/drivers/net/wireless/realtek/rtw88/phy.c @@ -140,9 +140,13 @@ void rtw_phy_dig_write(struct rtw_dev *rtwdev, u8 igi) { struct rtw_chip_info *chip = rtwdev->chip; struct rtw_hal *hal = &rtwdev->hal; + const struct rtw_hw_reg *dig_cck = &chip->dig_cck[0]; u32 addr, mask; u8 path; + if (dig_cck) + 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; -- cgit From 1d229e88e53c20717a51ef8d8f9a69573167543a Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 12 May 2020 18:26:14 +0800 Subject: rtw88: 8723d: add IQ calibration IQ calibration is used to calibrate RF characteristic to yield expected performance. Basically, we do calibration twice and compare the similarity to determine calibration is good or not, if not we do the third calibration, and then compare with the results of first and second calibration. If it still not similar, IQK is failed. Before doing calibration, we need to backup registers that will be modified in calibration procedure, and restore these registers after calibration is done. A calibration procedure can divided into four sub-procedures that are S1-TX, S1-RX, S0-TX and S0-RX. Where, S1 and S0 represent to path A and B respectively. Each sub-procedure configure proper registers, and then rigger one-shot calibration and poll until completion. For RX calibration, it needs to do twice one-shot calibration, first one is to yield parameter used by second one. The result of TX part is stored for TX power tracking that adjusts TX AGC to output expected power. Signed-off-by: Ping-Ke Shih Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20200512102621.5148-3-yhchuang@realtek.com --- drivers/net/wireless/realtek/rtw88/phy.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/net/wireless/realtek/rtw88/phy.c') diff --git a/drivers/net/wireless/realtek/rtw88/phy.c b/drivers/net/wireless/realtek/rtw88/phy.c index 72a16eff9db3..837b7bd04126 100644 --- a/drivers/net/wireless/realtek/rtw88/phy.c +++ b/drivers/net/wireless/realtek/rtw88/phy.c @@ -134,6 +134,8 @@ void rtw_phy_init(struct rtw_dev *rtwdev) 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; } void rtw_phy_dig_write(struct rtw_dev *rtwdev, u8 igi) -- cgit From 608d2a08f842d8f1ca877ced7bf092f084717553 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 12 May 2020 18:26:15 +0800 Subject: rtw88: 8723d: Add power tracking When chip's temperature is changed, RF characters are changed. To keep the characters to be consistent, 8723d uses thermal meter to assist in calibrating LCK, IQK, crystal and TX power. A base thermal value is programmed in efuse, all calibration data in MP process is based on this thermal value. So we calucate the delta of thermal value between the base value, and use this delta to reference XTAL and TX power offset tables to know how much we need to adjust. For IQK and LCK, driver checks if delta of thermal value is over 8, then they are triggered. For crystal adjustment, when delta of thermal value is changed, we check XTAL tables to get offset of XTAL value. If thermal value is larger than base value, positive table (_p as suffix) is used. Otherwise, we use negative table (_n as suffix). Then, we add offset to XTAL default value programmed in efuse, and write sum value to register. To compensate TX power, there are two hierarchical tables. First level use delta of thermal value to access eight tables to yield delta of TX power index. Then, plus base TX power index to get index of BB swing table (second level tables) where register value is induced. BB swing table can't deal with all cases, if index of BB swing table is over the size of the table. In this case, TX AGC is used to compensate the remnant part. Assume 'upper' is the upper bound of BB swing table, and 'target' is the desired index. Then, we can illustrate them as compensation method BB swing TX AGC ------------------- -------- -------------- target > upper upper target - upper target < 0 0 target otherwise target 0 For debug purpose, add a column 'rem' to tx_pwr_tbl entry, and it looks like path rate pwr base (byr lmt ) rem A CCK_1M 32(0x20) 34 -2 ( 0 -2) 0 Signed-off-by: Ping-Ke Shih Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20200512102621.5148-4-yhchuang@realtek.com --- drivers/net/wireless/realtek/rtw88/phy.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/realtek/rtw88/phy.c') diff --git a/drivers/net/wireless/realtek/rtw88/phy.c b/drivers/net/wireless/realtek/rtw88/phy.c index 837b7bd04126..45181f602e3f 100644 --- a/drivers/net/wireless/realtek/rtw88/phy.c +++ b/drivers/net/wireless/realtek/rtw88/phy.c @@ -1785,11 +1785,13 @@ 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; pwr_idx = &rtwdev->efuse.txpwr_idx_table[path]; group = rtw_get_channel_group(ch); @@ -1811,6 +1813,8 @@ void rtw_get_tx_power_params(struct rtw_dev *rtwdev, u8 path, u8 rate, u8 bw, *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); } u8 @@ -1830,7 +1834,7 @@ rtw_phy_get_tx_power_index(struct rtw_dev *rtwdev, u8 rf_path, u8 rate, if (rtwdev->chip->en_dis_dpd) offset += rtw_phy_get_dis_dpd_by_rate_diff(rtwdev, rate); - tx_power += offset; + tx_power += offset + pwr_param.pwr_remnant; if (tx_power > rtwdev->chip->max_power_index) tx_power = rtwdev->chip->max_power_index; -- cgit From 449be86670f50284069cb0ddc4fe98f064ac6d87 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Fri, 15 May 2020 13:23:21 +0800 Subject: rtw88: extract: export symbols used in chip functionalities In the current design, various chip functions and tables are built into rtw88 core. That causes kernel to load its functionalities even if a chip isn't currently used. We plan to make each chip's functionalities a separate kernel module to reduce rtw88 core. And kernel will be able to load the necessary. Before extracting chip functionalities, we export symbols inside rtw88 core which will be used in chip modules. Signed-off-by: Zong-Zhe Yang Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Kalle Valo Link: https://lore.kernel.org/r/20200515052327.31874-2-yhchuang@realtek.com --- drivers/net/wireless/realtek/rtw88/phy.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'drivers/net/wireless/realtek/rtw88/phy.c') diff --git a/drivers/net/wireless/realtek/rtw88/phy.c b/drivers/net/wireless/realtek/rtw88/phy.c index 45181f602e3f..8d93f3159746 100644 --- a/drivers/net/wireless/realtek/rtw88/phy.c +++ b/drivers/net/wireless/realtek/rtw88/phy.c @@ -82,6 +82,8 @@ u8 *rtw_rate_section[RTW_RATE_SECTION_MAX] = { 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), @@ -90,6 +92,8 @@ u8 rtw_rate_size[RTW_RATE_SECTION_MAX] = { 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); @@ -137,6 +141,7 @@ void rtw_phy_init(struct rtw_dev *rtwdev) dm_info->iqk.done = false; } +EXPORT_SYMBOL(rtw_phy_init); void rtw_phy_dig_write(struct rtw_dev *rtwdev, u8 igi) { @@ -676,6 +681,7 @@ u8 rtw_phy_rf_power_2_rssi(s8 *rf_power, u8 path_num) 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) @@ -698,6 +704,7 @@ u32 rtw_phy_read_rf(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, 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) @@ -746,6 +753,7 @@ u32 rtw_phy_read_rf_sipi(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, 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) @@ -785,6 +793,7 @@ bool rtw_phy_write_rf_reg_sipi(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, 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) @@ -818,6 +827,7 @@ bool rtw_phy_write_rf_reg_mix(struct rtw_dev *rtwdev, enum rtw_rf_path rf_path, 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) { @@ -910,6 +920,7 @@ void rtw_parse_tbl_phy_cond(struct rtw_dev *rtwdev, const struct rtw_table *tbl) } } } +EXPORT_SYMBOL(rtw_parse_tbl_phy_cond); #define bcd_to_dec_pwr_by_rate(val, i) bcd2bin(val >> (i * 8)) @@ -1273,6 +1284,7 @@ void rtw_parse_tbl_bb_pg(struct rtw_dev *rtwdev, const struct rtw_table *tbl) 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 */ @@ -1417,18 +1429,21 @@ void rtw_parse_tbl_txpwr_lmt(struct rtw_dev *rtwdev, 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) @@ -1448,6 +1463,7 @@ void rtw_phy_cfg_bb(struct rtw_dev *rtwdev, const struct rtw_table *tbl, 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) @@ -1461,6 +1477,7 @@ void rtw_phy_cfg_rf(struct rtw_dev *rtwdev, const struct rtw_table *tbl, udelay(1); } } +EXPORT_SYMBOL(rtw_phy_cfg_rf); static void rtw_load_rfk_table(struct rtw_dev *rtwdev) { @@ -1498,6 +1515,7 @@ void rtw_phy_load_tables(struct rtw_dev *rtwdev) rtw_load_table(rtwdev, tbl); } } +EXPORT_SYMBOL(rtw_phy_load_tables); static u8 rtw_get_channel_group(u8 channel) { @@ -1841,6 +1859,7 @@ rtw_phy_get_tx_power_index(struct rtw_dev *rtwdev, u8 rf_path, u8 rate, 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) @@ -1903,6 +1922,7 @@ void rtw_phy_set_tx_power_level(struct rtw_dev *rtwdev, u8 channel) 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, @@ -2060,6 +2080,7 @@ void rtw_phy_config_swing_table(struct rtw_dev *rtwdev, 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) { @@ -2069,6 +2090,7 @@ void rtw_phy_pwrtrack_avg(struct rtw_dev *rtwdev, u8 thermal, u8 path) 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) @@ -2081,6 +2103,7 @@ bool rtw_phy_pwrtrack_thermal_changed(struct rtw_dev *rtwdev, u8 thermal, return true; } +EXPORT_SYMBOL(rtw_phy_pwrtrack_thermal_changed); u8 rtw_phy_pwrtrack_get_delta(struct rtw_dev *rtwdev, u8 path) { @@ -2093,6 +2116,7 @@ u8 rtw_phy_pwrtrack_get_delta(struct rtw_dev *rtwdev, u8 path) 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, @@ -2126,6 +2150,7 @@ s8 rtw_phy_pwrtrack_get_pwridx(struct rtw_dev *rtwdev, else return -delta_swing_table_idx_neg[delta]; } +EXPORT_SYMBOL(rtw_phy_pwrtrack_get_pwridx); bool rtw_phy_pwrtrack_need_iqk(struct rtw_dev *rtwdev) { @@ -2139,3 +2164,4 @@ bool rtw_phy_pwrtrack_need_iqk(struct rtw_dev *rtwdev) } return false; } +EXPORT_SYMBOL(rtw_phy_pwrtrack_need_iqk); -- cgit