162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
262306a36Sopenharmony_ci/* Copyright(c) 2018-2021  Realtek Corporation
362306a36Sopenharmony_ci */
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include "sar.h"
662306a36Sopenharmony_ci#include "phy.h"
762306a36Sopenharmony_ci#include "debug.h"
862306a36Sopenharmony_ci
962306a36Sopenharmony_cis8 rtw_query_sar(struct rtw_dev *rtwdev, const struct rtw_sar_arg *arg)
1062306a36Sopenharmony_ci{
1162306a36Sopenharmony_ci	const struct rtw_hal *hal = &rtwdev->hal;
1262306a36Sopenharmony_ci	const struct rtw_sar *sar = &hal->sar;
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci	switch (sar->src) {
1562306a36Sopenharmony_ci	default:
1662306a36Sopenharmony_ci		rtw_warn(rtwdev, "unknown SAR source: %d\n", sar->src);
1762306a36Sopenharmony_ci		fallthrough;
1862306a36Sopenharmony_ci	case RTW_SAR_SOURCE_NONE:
1962306a36Sopenharmony_ci		return (s8)rtwdev->chip->max_power_index;
2062306a36Sopenharmony_ci	case RTW_SAR_SOURCE_COMMON:
2162306a36Sopenharmony_ci		return sar->cfg[arg->path][arg->rs].common[arg->sar_band];
2262306a36Sopenharmony_ci	}
2362306a36Sopenharmony_ci}
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic int rtw_apply_sar(struct rtw_dev *rtwdev, const struct rtw_sar *new)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	struct rtw_hal *hal = &rtwdev->hal;
2862306a36Sopenharmony_ci	struct rtw_sar *sar = &hal->sar;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	if (sar->src != RTW_SAR_SOURCE_NONE && new->src != sar->src) {
3162306a36Sopenharmony_ci		rtw_warn(rtwdev, "SAR source: %d is in use\n", sar->src);
3262306a36Sopenharmony_ci		return -EBUSY;
3362306a36Sopenharmony_ci	}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	*sar = *new;
3662306a36Sopenharmony_ci	rtw_phy_set_tx_power_level(rtwdev, hal->current_channel);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	return 0;
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic s8 rtw_sar_to_phy(struct rtw_dev *rtwdev, u8 fct, s32 sar,
4262306a36Sopenharmony_ci			 const struct rtw_sar_arg *arg)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	struct rtw_hal *hal = &rtwdev->hal;
4562306a36Sopenharmony_ci	u8 txgi = rtwdev->chip->txgi_factor;
4662306a36Sopenharmony_ci	u8 max = rtwdev->chip->max_power_index;
4762306a36Sopenharmony_ci	s32 tmp;
4862306a36Sopenharmony_ci	s8 base;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	tmp = fct > txgi ? sar >> (fct - txgi) : sar << (txgi - fct);
5162306a36Sopenharmony_ci	base = arg->sar_band == RTW_SAR_BAND_0 ?
5262306a36Sopenharmony_ci	       hal->tx_pwr_by_rate_base_2g[arg->path][arg->rs] :
5362306a36Sopenharmony_ci	       hal->tx_pwr_by_rate_base_5g[arg->path][arg->rs];
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	return (s8)clamp_t(s32, tmp, -max - 1, max) - base;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic const struct cfg80211_sar_freq_ranges rtw_common_sar_freq_ranges[] = {
5962306a36Sopenharmony_ci	[RTW_SAR_BAND_0] = { .start_freq = 2412, .end_freq = 2484, },
6062306a36Sopenharmony_ci	[RTW_SAR_BAND_1] = { .start_freq = 5180, .end_freq = 5320, },
6162306a36Sopenharmony_ci	[RTW_SAR_BAND_3] = { .start_freq = 5500, .end_freq = 5720, },
6262306a36Sopenharmony_ci	[RTW_SAR_BAND_4] = { .start_freq = 5745, .end_freq = 5825, },
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic_assert(ARRAY_SIZE(rtw_common_sar_freq_ranges) == RTW_SAR_BAND_NR);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ciconst struct cfg80211_sar_capa rtw_sar_capa = {
6862306a36Sopenharmony_ci	.type = NL80211_SAR_TYPE_POWER,
6962306a36Sopenharmony_ci	.num_freq_ranges = RTW_SAR_BAND_NR,
7062306a36Sopenharmony_ci	.freq_ranges = rtw_common_sar_freq_ranges,
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ciint rtw_set_sar_specs(struct rtw_dev *rtwdev,
7462306a36Sopenharmony_ci		      const struct cfg80211_sar_specs *sar)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	struct rtw_sar_arg arg = {0};
7762306a36Sopenharmony_ci	struct rtw_sar new = {0};
7862306a36Sopenharmony_ci	u32 idx, i, j, k;
7962306a36Sopenharmony_ci	s32 power;
8062306a36Sopenharmony_ci	s8 val;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	if (sar->type != NL80211_SAR_TYPE_POWER)
8362306a36Sopenharmony_ci		return -EINVAL;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	memset(&new, rtwdev->chip->max_power_index, sizeof(new));
8662306a36Sopenharmony_ci	new.src = RTW_SAR_SOURCE_COMMON;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	for (i = 0; i < sar->num_sub_specs; i++) {
8962306a36Sopenharmony_ci		idx = sar->sub_specs[i].freq_range_index;
9062306a36Sopenharmony_ci		if (idx >= RTW_SAR_BAND_NR)
9162306a36Sopenharmony_ci			return -EINVAL;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci		power = sar->sub_specs[i].power;
9462306a36Sopenharmony_ci		rtw_dbg(rtwdev, RTW_DBG_REGD, "On freq %u to %u, set SAR %d in 1/%lu dBm\n",
9562306a36Sopenharmony_ci			rtw_common_sar_freq_ranges[idx].start_freq,
9662306a36Sopenharmony_ci			rtw_common_sar_freq_ranges[idx].end_freq,
9762306a36Sopenharmony_ci			power, BIT(RTW_COMMON_SAR_FCT));
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci		for (j = 0; j < RTW_RF_PATH_MAX; j++) {
10062306a36Sopenharmony_ci			for (k = 0; k < RTW_RATE_SECTION_MAX; k++) {
10162306a36Sopenharmony_ci				arg = (struct rtw_sar_arg){
10262306a36Sopenharmony_ci					.sar_band = idx,
10362306a36Sopenharmony_ci					.path = j,
10462306a36Sopenharmony_ci					.rs = k,
10562306a36Sopenharmony_ci				};
10662306a36Sopenharmony_ci				val = rtw_sar_to_phy(rtwdev, RTW_COMMON_SAR_FCT,
10762306a36Sopenharmony_ci						     power, &arg);
10862306a36Sopenharmony_ci				new.cfg[j][k].common[idx] = val;
10962306a36Sopenharmony_ci			}
11062306a36Sopenharmony_ci		}
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	return rtw_apply_sar(rtwdev, &new);
11462306a36Sopenharmony_ci}
115