18704d0beSZong-Zhe Yang // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
28704d0beSZong-Zhe Yang /* Copyright(c) 2018-2021  Realtek Corporation
38704d0beSZong-Zhe Yang  */
48704d0beSZong-Zhe Yang 
58704d0beSZong-Zhe Yang #include "sar.h"
68704d0beSZong-Zhe Yang #include "phy.h"
78704d0beSZong-Zhe Yang #include "debug.h"
88704d0beSZong-Zhe Yang 
rtw_query_sar(struct rtw_dev * rtwdev,const struct rtw_sar_arg * arg)98704d0beSZong-Zhe Yang s8 rtw_query_sar(struct rtw_dev *rtwdev, const struct rtw_sar_arg *arg)
108704d0beSZong-Zhe Yang {
118704d0beSZong-Zhe Yang 	const struct rtw_hal *hal = &rtwdev->hal;
128704d0beSZong-Zhe Yang 	const struct rtw_sar *sar = &hal->sar;
138704d0beSZong-Zhe Yang 
148704d0beSZong-Zhe Yang 	switch (sar->src) {
158704d0beSZong-Zhe Yang 	default:
168704d0beSZong-Zhe Yang 		rtw_warn(rtwdev, "unknown SAR source: %d\n", sar->src);
178704d0beSZong-Zhe Yang 		fallthrough;
188704d0beSZong-Zhe Yang 	case RTW_SAR_SOURCE_NONE:
198704d0beSZong-Zhe Yang 		return (s8)rtwdev->chip->max_power_index;
208704d0beSZong-Zhe Yang 	case RTW_SAR_SOURCE_COMMON:
218704d0beSZong-Zhe Yang 		return sar->cfg[arg->path][arg->rs].common[arg->sar_band];
228704d0beSZong-Zhe Yang 	}
238704d0beSZong-Zhe Yang }
248704d0beSZong-Zhe Yang 
rtw_apply_sar(struct rtw_dev * rtwdev,const struct rtw_sar * new)258704d0beSZong-Zhe Yang static int rtw_apply_sar(struct rtw_dev *rtwdev, const struct rtw_sar *new)
268704d0beSZong-Zhe Yang {
278704d0beSZong-Zhe Yang 	struct rtw_hal *hal = &rtwdev->hal;
288704d0beSZong-Zhe Yang 	struct rtw_sar *sar = &hal->sar;
298704d0beSZong-Zhe Yang 
308704d0beSZong-Zhe Yang 	if (sar->src != RTW_SAR_SOURCE_NONE && new->src != sar->src) {
318704d0beSZong-Zhe Yang 		rtw_warn(rtwdev, "SAR source: %d is in use\n", sar->src);
328704d0beSZong-Zhe Yang 		return -EBUSY;
338704d0beSZong-Zhe Yang 	}
348704d0beSZong-Zhe Yang 
358704d0beSZong-Zhe Yang 	*sar = *new;
368704d0beSZong-Zhe Yang 	rtw_phy_set_tx_power_level(rtwdev, hal->current_channel);
378704d0beSZong-Zhe Yang 
388704d0beSZong-Zhe Yang 	return 0;
398704d0beSZong-Zhe Yang }
408704d0beSZong-Zhe Yang 
rtw_sar_to_phy(struct rtw_dev * rtwdev,u8 fct,s32 sar,const struct rtw_sar_arg * arg)418704d0beSZong-Zhe Yang static s8 rtw_sar_to_phy(struct rtw_dev *rtwdev, u8 fct, s32 sar,
428704d0beSZong-Zhe Yang 			 const struct rtw_sar_arg *arg)
438704d0beSZong-Zhe Yang {
448704d0beSZong-Zhe Yang 	struct rtw_hal *hal = &rtwdev->hal;
458704d0beSZong-Zhe Yang 	u8 txgi = rtwdev->chip->txgi_factor;
468704d0beSZong-Zhe Yang 	u8 max = rtwdev->chip->max_power_index;
478704d0beSZong-Zhe Yang 	s32 tmp;
488704d0beSZong-Zhe Yang 	s8 base;
498704d0beSZong-Zhe Yang 
508704d0beSZong-Zhe Yang 	tmp = fct > txgi ? sar >> (fct - txgi) : sar << (txgi - fct);
518704d0beSZong-Zhe Yang 	base = arg->sar_band == RTW_SAR_BAND_0 ?
528704d0beSZong-Zhe Yang 	       hal->tx_pwr_by_rate_base_2g[arg->path][arg->rs] :
538704d0beSZong-Zhe Yang 	       hal->tx_pwr_by_rate_base_5g[arg->path][arg->rs];
548704d0beSZong-Zhe Yang 
558704d0beSZong-Zhe Yang 	return (s8)clamp_t(s32, tmp, -max - 1, max) - base;
568704d0beSZong-Zhe Yang }
578704d0beSZong-Zhe Yang 
588704d0beSZong-Zhe Yang static const struct cfg80211_sar_freq_ranges rtw_common_sar_freq_ranges[] = {
598704d0beSZong-Zhe Yang 	[RTW_SAR_BAND_0] = { .start_freq = 2412, .end_freq = 2484, },
608704d0beSZong-Zhe Yang 	[RTW_SAR_BAND_1] = { .start_freq = 5180, .end_freq = 5320, },
618704d0beSZong-Zhe Yang 	[RTW_SAR_BAND_3] = { .start_freq = 5500, .end_freq = 5720, },
628704d0beSZong-Zhe Yang 	[RTW_SAR_BAND_4] = { .start_freq = 5745, .end_freq = 5825, },
638704d0beSZong-Zhe Yang };
648704d0beSZong-Zhe Yang 
658704d0beSZong-Zhe Yang static_assert(ARRAY_SIZE(rtw_common_sar_freq_ranges) == RTW_SAR_BAND_NR);
668704d0beSZong-Zhe Yang 
678704d0beSZong-Zhe Yang const struct cfg80211_sar_capa rtw_sar_capa = {
688704d0beSZong-Zhe Yang 	.type = NL80211_SAR_TYPE_POWER,
698704d0beSZong-Zhe Yang 	.num_freq_ranges = RTW_SAR_BAND_NR,
708704d0beSZong-Zhe Yang 	.freq_ranges = rtw_common_sar_freq_ranges,
718704d0beSZong-Zhe Yang };
728704d0beSZong-Zhe Yang 
rtw_set_sar_specs(struct rtw_dev * rtwdev,const struct cfg80211_sar_specs * sar)738704d0beSZong-Zhe Yang int rtw_set_sar_specs(struct rtw_dev *rtwdev,
748704d0beSZong-Zhe Yang 		      const struct cfg80211_sar_specs *sar)
758704d0beSZong-Zhe Yang {
768704d0beSZong-Zhe Yang 	struct rtw_sar_arg arg = {0};
778704d0beSZong-Zhe Yang 	struct rtw_sar new = {0};
788704d0beSZong-Zhe Yang 	u32 idx, i, j, k;
798704d0beSZong-Zhe Yang 	s32 power;
808704d0beSZong-Zhe Yang 	s8 val;
818704d0beSZong-Zhe Yang 
828704d0beSZong-Zhe Yang 	if (sar->type != NL80211_SAR_TYPE_POWER)
838704d0beSZong-Zhe Yang 		return -EINVAL;
848704d0beSZong-Zhe Yang 
858704d0beSZong-Zhe Yang 	memset(&new, rtwdev->chip->max_power_index, sizeof(new));
868704d0beSZong-Zhe Yang 	new.src = RTW_SAR_SOURCE_COMMON;
878704d0beSZong-Zhe Yang 
888704d0beSZong-Zhe Yang 	for (i = 0; i < sar->num_sub_specs; i++) {
898704d0beSZong-Zhe Yang 		idx = sar->sub_specs[i].freq_range_index;
908704d0beSZong-Zhe Yang 		if (idx >= RTW_SAR_BAND_NR)
918704d0beSZong-Zhe Yang 			return -EINVAL;
928704d0beSZong-Zhe Yang 
938704d0beSZong-Zhe Yang 		power = sar->sub_specs[i].power;
94*a0061be4SPing-Ke Shih 		rtw_dbg(rtwdev, RTW_DBG_REGD, "On freq %u to %u, set SAR %d in 1/%lu dBm\n",
958704d0beSZong-Zhe Yang 			rtw_common_sar_freq_ranges[idx].start_freq,
968704d0beSZong-Zhe Yang 			rtw_common_sar_freq_ranges[idx].end_freq,
978704d0beSZong-Zhe Yang 			power, BIT(RTW_COMMON_SAR_FCT));
988704d0beSZong-Zhe Yang 
998704d0beSZong-Zhe Yang 		for (j = 0; j < RTW_RF_PATH_MAX; j++) {
1008704d0beSZong-Zhe Yang 			for (k = 0; k < RTW_RATE_SECTION_MAX; k++) {
1018704d0beSZong-Zhe Yang 				arg = (struct rtw_sar_arg){
1028704d0beSZong-Zhe Yang 					.sar_band = idx,
1038704d0beSZong-Zhe Yang 					.path = j,
1048704d0beSZong-Zhe Yang 					.rs = k,
1058704d0beSZong-Zhe Yang 				};
1068704d0beSZong-Zhe Yang 				val = rtw_sar_to_phy(rtwdev, RTW_COMMON_SAR_FCT,
1078704d0beSZong-Zhe Yang 						     power, &arg);
1088704d0beSZong-Zhe Yang 				new.cfg[j][k].common[idx] = val;
1098704d0beSZong-Zhe Yang 			}
1108704d0beSZong-Zhe Yang 		}
1118704d0beSZong-Zhe Yang 	}
1128704d0beSZong-Zhe Yang 
1138704d0beSZong-Zhe Yang 	return rtw_apply_sar(rtwdev, &new);
1148704d0beSZong-Zhe Yang }
115