rtw88: set power trim according to efuse PG values
authorTzu-En Huang <tehuang@realtek.com>
Mon, 20 Apr 2020 10:52:07 +0000 (18:52 +0800)
committerKalle Valo <kvalo@codeaurora.org>
Tue, 21 Apr 2020 13:00:21 +0000 (16:00 +0300)
8822C devices have power trim, thermal and PA bias values
programmed in efuse. Driver should configure the RF components
according to the values.

If the power trim is not configured, then the devices might have
distortion on the output tx power.

Signed-off-by: Tzu-En Huang <tehuang@realtek.com>
Signed-off-by: Yan-Hsuan Chuang <yhchuang@realtek.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/20200420105207.31899-1-yhchuang@realtek.com
drivers/net/wireless/realtek/rtw88/efuse.c
drivers/net/wireless/realtek/rtw88/efuse.h
drivers/net/wireless/realtek/rtw88/rtw8822c.c
drivers/net/wireless/realtek/rtw88/rtw8822c.h

index df969d346b4102e2939bbb0c6acea6748a2f86c4..13d1c58d6de555375876377f482401761cc42a17 100644 (file)
@@ -2,6 +2,8 @@
 /* Copyright(c) 2018-2019  Realtek Corporation
  */
 
+#include <linux/iopoll.h>
+
 #include "main.h"
 #include "efuse.h"
 #include "reg.h"
@@ -120,6 +122,26 @@ static int rtw_dump_physical_efuse_map(struct rtw_dev *rtwdev, u8 *map)
        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;
+}
+
 int rtw_parse_efuse_map(struct rtw_dev *rtwdev)
 {
        struct rtw_chip_info *chip = rtwdev->chip;
index 115bbe85946ac6c643926c725d9bda49da03ff9a..97a51f0b0e466a57e29bb28e3000133d77a7ab3b 100644 (file)
@@ -10,6 +10,8 @@
 #define EFUSE_HW_CAP_SUPP_BW80         7
 #define EFUSE_HW_CAP_SUPP_BW40         6
 
+#define EFUSE_READ_FAIL                        0xff
+
 #define GET_EFUSE_HW_CAP_HCI(hw_cap)                                          \
        le32_get_bits(*((__le32 *)(hw_cap) + 0x01), GENMASK(3, 0))
 #define GET_EFUSE_HW_CAP_BW(hw_cap)                                           \
@@ -22,5 +24,6 @@
        le32_get_bits(*((__le32 *)(hw_cap) + 0x01), GENMASK(27, 26))
 
 int rtw_parse_efuse_map(struct rtw_dev *rtwdev);
+int rtw_read8_physical_efuse(struct rtw_dev *rtwdev, u16 addr, u8 *data);
 
 #endif
index c99b1de54bfcbf00e958e89c09819e4a60e7ee52..ee0d39135617b387539561e3b2babb3ddb8a9492 100644 (file)
@@ -15,6 +15,7 @@
 #include "debug.h"
 #include "util.h"
 #include "bf.h"
+#include "efuse.h"
 
 static void rtw8822c_config_trx_mode(struct rtw_dev *rtwdev, u8 tx_path,
                                     u8 rx_path, bool is_tx2_path);
@@ -1000,10 +1001,122 @@ static void rtw8822c_rf_x2_check(struct rtw_dev *rtwdev)
        }
 }
 
+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] = {0};
+       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, 0x60, 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, 0x60, RF_PABIAS_5G_MASK, pg_pa_bias);
+       }
+}
+
 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)
index dfd8662a0c0eade75fef04a536a91e4a9eaed7a7..32b4771e04d00910de922346833698ccb167391e 100644 (file)
@@ -309,4 +309,32 @@ const struct rtw_table name ## _tbl = {                    \
 #define BIT_GS_PWSF            GENMASK(27, 0)
 #define BIT_RPT_DGAIN          GENMASK(27, 16)
 #define BIT_TX_CFIR            GENMASK(31, 30)
+
+#define PPG_THERMAL_A 0x1ef
+#define PPG_THERMAL_B 0x1b0
+#define RF_THEMAL_MASK GENMASK(19, 16)
+#define PPG_2GL_TXAB 0x1d4
+#define PPG_2GM_TXAB 0x1ee
+#define PPG_2GH_TXAB 0x1d2
+#define PPG_2G_A_MASK GENMASK(3, 0)
+#define PPG_2G_B_MASK GENMASK(7, 4)
+#define PPG_5GL1_TXA 0x1ec
+#define PPG_5GL2_TXA 0x1e8
+#define PPG_5GM1_TXA 0x1e4
+#define PPG_5GM2_TXA 0x1e0
+#define PPG_5GH1_TXA 0x1dc
+#define PPG_5GL1_TXB 0x1eb
+#define PPG_5GL2_TXB 0x1e7
+#define PPG_5GM1_TXB 0x1e3
+#define PPG_5GM2_TXB 0x1df
+#define PPG_5GH1_TXB 0x1db
+#define PPG_5G_MASK GENMASK(4, 0)
+#define PPG_PABIAS_2GA 0x1d6
+#define PPG_PABIAS_2GB 0x1d5
+#define PPG_PABIAS_5GA 0x1d8
+#define PPG_PABIAS_5GB 0x1d7
+#define PPG_PABIAS_MASK GENMASK(3, 0)
+#define RF_PABIAS_2G_MASK GENMASK(15, 12)
+#define RF_PABIAS_5G_MASK GENMASK(19, 16)
+
 #endif