From: Ping-Ke Shih <pkshih@xxxxxxxxxxx> We apply four frequency ranges to calculate TX power, though RWRD defines five ranges. RWGS is used to adjust SAR power limit value and define the upper bound corresponding to geography. Some sar_read::ops are added to convert to proper unit, because the units and fields of SAR power limit for each customer ID are different. Signed-off-by: Ping-Ke Shih <pkshih@xxxxxxxxxxx> Signed-off-by: Yan-Hsuan Chuang <yhchuang@xxxxxxxxxxx> --- drivers/net/wireless/realtek/rtw88/sar.c | 129 +++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw88/sar.c b/drivers/net/wireless/realtek/rtw88/sar.c index d81a6511f138..80b8913d1a49 100644 --- a/drivers/net/wireless/realtek/rtw88/sar.c +++ b/drivers/net/wireless/realtek/rtw88/sar.c @@ -267,17 +267,101 @@ union rtw_sar_rwgs { struct rtw_sar_rwgs_rt rt; }; +struct rtw_sar_geo_map { + int idx; /* index of rwgs.geo[] */ + int rd; /* RTW_REGD_xxx */ +}; + +static const struct rtw_sar_geo_map geo_map_hp[] = { + {RTW_SAR_RWGS_HP_FCC_IC, RTW_REGD_FCC}, + {RTW_SAR_RWGS_HP_FCC_IC, RTW_REGD_IC}, + {RTW_SAR_RWGS_HP_ETSI_MKK, RTW_REGD_ETSI}, + {RTW_SAR_RWGS_HP_ETSI_MKK, RTW_REGD_MKK}, + {RTW_SAR_RWGS_HP_WW_KCC, RTW_REGD_KCC}, + {RTW_SAR_RWGS_HP_WW_KCC, RTW_REGD_WW}, +}; + +static const struct rtw_sar_geo_map geo_map_rt[] = { + {RTW_SAR_RWGS_RT_FCC, RTW_REGD_FCC}, + {RTW_SAR_RWGS_RT_CE, RTW_REGD_ETSI}, + {RTW_SAR_RWGS_RT_MKK, RTW_REGD_MKK}, + {RTW_SAR_RWGS_RT_IC, RTW_REGD_IC}, + {RTW_SAR_RWGS_RT_KCC, RTW_REGD_KCC}, + {RTW_SAR_RWGS_RT_WW, RTW_REGD_WW}, +}; + struct rtw_sar_read { + int (*rwsi_mode)(struct rtw_dev *rtwdev, int path); + int (*rwrd_base_q3)(struct rtw_dev *rtwdev, int mode, int path, int chidx); + int (*rwgs_delta_q3)(struct rtw_dev *rtwdev, int gi, int path, int band); + int (*rwgs_max_q3)(struct rtw_dev *rtwdev, int gi, int band); + const struct rtw_sar_geo_map *gm, *gm_end; int rwsi_sz; int rwgs_sz; }; +static int rwsi_mode_hp(struct rtw_dev *rtwdev, int path) +{ + return rtwdev->sar.rwsi->hp.index[path] - 1; +} + +static int rwrd_base_q3_hp(struct rtw_dev *rtwdev, int mode, int path, int chidx) +{ + int sar; + + sar = rtwdev->sar.rwrd->mode[mode].chain[path].limit[chidx]; + + return (10 << 3) + (sar << 2); +} + +static int rwgs_delta_q3_hp(struct rtw_dev *rtwdev, int gi, int path, int band) +{ + return rtwdev->sar.rwgs->hp.geo[gi].band[band].delta[path] << 2; +} + +static int rwgs_max_q3_hp(struct rtw_dev *rtwdev, int gi, int band) +{ + return (10 << 3) + (rtwdev->sar.rwgs->hp.geo[gi].band[band].max << 2); +} + static const struct rtw_sar_read sar_read_hp = { + .rwsi_mode = rwsi_mode_hp, + .rwrd_base_q3 = rwrd_base_q3_hp, + .rwgs_delta_q3 = rwgs_delta_q3_hp, + .rwgs_max_q3 = rwgs_max_q3_hp, + .gm = geo_map_hp, + .gm_end = geo_map_hp + ARRAY_SIZE(geo_map_hp), .rwsi_sz = sizeof(struct rtw_sar_rwsi_hp), .rwgs_sz = sizeof(struct rtw_sar_rwgs_hp), }; +static int rwsi_mode_rt(struct rtw_dev *rtwdev, int path) +{ + return rtwdev->sar.rwsi->rt.index - 1; +} + +static int rwrd_base_q3_rt(struct rtw_dev *rtwdev, int mode, int path, int chidx) +{ + return rtwdev->sar.rwrd->mode[mode].chain[path].limit[chidx] << 3; +} + +static int rwgs_delta_q3_rt(struct rtw_dev *rtwdev, int gi, int path, int band) +{ + return rtwdev->sar.rwgs->rt.geo[gi].band[band].delta << 2; +} + +static int rwgs_max_q3_rt(struct rtw_dev *rtwdev, int gi, int band) +{ + return rtwdev->sar.rwgs->rt.geo[gi].band[band].max; +} + static const struct rtw_sar_read sar_read_rt = { + .rwsi_mode = rwsi_mode_rt, + .rwrd_base_q3 = rwrd_base_q3_rt, + .rwgs_delta_q3 = rwgs_delta_q3_rt, + .rwgs_max_q3 = rwgs_max_q3_rt, + .gm = geo_map_rt, + .gm_end = geo_map_rt + ARRAY_SIZE(geo_map_rt), .rwsi_sz = sizeof(struct rtw_sar_rwsi_rt), .rwgs_sz = sizeof(struct rtw_sar_rwgs_rt), }; @@ -420,6 +504,49 @@ static bool is_valid_rwgs(struct rtw_dev *rtwdev, const struct rtw_sar_rwrd *rwr return false; } +static void rtw_sar_apply_dynamic_tables(struct rtw_dev *rtwdev) +{ + struct rtw_hal *hal = &rtwdev->hal; + const struct rtw_sar_read *r = rtwdev->sar.read; + const struct rtw_sar_geo_map *gm = r->gm; + const struct rtw_sar_geo_map *gm_end = r->gm_end; + int path_num = min_t(int, RTW_SAR_RWRD_CHAIN_NR, hal->rf_path_num); + int path, mode; + int sar, delta, max; + + for (; gm < gm_end; gm++) { + for (path = 0; path < path_num; path++) { + mode = r->rwsi_mode(rtwdev, path); + + /* 2.4G part */ + delta = r->rwgs_delta_q3(rtwdev, gm->idx, path, RTW_SAR_RWGS_2G); + max = r->rwgs_max_q3(rtwdev, gm->idx, RTW_SAR_RWGS_2G); + + sar = r->rwrd_base_q3(rtwdev, mode, path, RTW_SAR_LMT_CH1_14); + sar = min(sar + delta, max); + rtw_phy_set_tx_power_sar(rtwdev, gm->rd, path, 1, 14, sar); + + /* 5G part */ + delta = r->rwgs_delta_q3(rtwdev, gm->idx, path, RTW_SAR_RWGS_5G); + max = r->rwgs_max_q3(rtwdev, gm->idx, RTW_SAR_RWGS_5G); + + sar = r->rwrd_base_q3(rtwdev, mode, path, RTW_SAR_LMT_CH36_64); + sar = min(sar + delta, max); + rtw_phy_set_tx_power_sar(rtwdev, gm->rd, path, 36, 64, sar); + + sar = r->rwrd_base_q3(rtwdev, mode, path, RTW_SAR_LMT_CH100_144); + sar = min(sar + delta, max); + rtw_phy_set_tx_power_sar(rtwdev, gm->rd, path, 100, 144, sar); + + sar = r->rwrd_base_q3(rtwdev, mode, path, RTW_SAR_LMT_CH149_165); + sar = min(sar + delta, max); + rtw_phy_set_tx_power_sar(rtwdev, gm->rd, path, 149, 165, sar); + } + } + + rtwdev->sar.source = RTW_SAR_SOURCE_ACPI_DYNAMIC; +} + static int rtw_sar_load_dynamic_tables(struct rtw_dev *rtwdev) { struct rtw_sar_rwrd *rwrd; @@ -457,6 +584,8 @@ static int rtw_sar_load_dynamic_tables(struct rtw_dev *rtwdev) rtwdev->sar.rwsi = rwsi; rtwdev->sar.rwgs = rwgs; + rtw_sar_apply_dynamic_tables(rtwdev); + rtw_dbg(rtwdev, RTW_DBG_REGD, "SAR: RWRD/RWSI/RWGS is adopted\n"); return 0; -- 2.17.1