115 lines
3.1 KiB
C
115 lines
3.1 KiB
C
|
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||
|
/* Copyright(c) 2018-2021 Realtek Corporation
|
||
|
*/
|
||
|
|
||
|
#include "sar.h"
|
||
|
#include "phy.h"
|
||
|
#include "debug.h"
|
||
|
|
||
|
s8 rtw_query_sar(struct rtw_dev *rtwdev, const struct rtw_sar_arg *arg)
|
||
|
{
|
||
|
const struct rtw_hal *hal = &rtwdev->hal;
|
||
|
const struct rtw_sar *sar = &hal->sar;
|
||
|
|
||
|
switch (sar->src) {
|
||
|
default:
|
||
|
rtw_warn(rtwdev, "unknown SAR source: %d\n", sar->src);
|
||
|
fallthrough;
|
||
|
case RTW_SAR_SOURCE_NONE:
|
||
|
return (s8)rtwdev->chip->max_power_index;
|
||
|
case RTW_SAR_SOURCE_COMMON:
|
||
|
return sar->cfg[arg->path][arg->rs].common[arg->sar_band];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int rtw_apply_sar(struct rtw_dev *rtwdev, const struct rtw_sar *new)
|
||
|
{
|
||
|
struct rtw_hal *hal = &rtwdev->hal;
|
||
|
struct rtw_sar *sar = &hal->sar;
|
||
|
|
||
|
if (sar->src != RTW_SAR_SOURCE_NONE && new->src != sar->src) {
|
||
|
rtw_warn(rtwdev, "SAR source: %d is in use\n", sar->src);
|
||
|
return -EBUSY;
|
||
|
}
|
||
|
|
||
|
*sar = *new;
|
||
|
rtw_phy_set_tx_power_level(rtwdev, hal->current_channel);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static s8 rtw_sar_to_phy(struct rtw_dev *rtwdev, u8 fct, s32 sar,
|
||
|
const struct rtw_sar_arg *arg)
|
||
|
{
|
||
|
struct rtw_hal *hal = &rtwdev->hal;
|
||
|
u8 txgi = rtwdev->chip->txgi_factor;
|
||
|
u8 max = rtwdev->chip->max_power_index;
|
||
|
s32 tmp;
|
||
|
s8 base;
|
||
|
|
||
|
tmp = fct > txgi ? sar >> (fct - txgi) : sar << (txgi - fct);
|
||
|
base = arg->sar_band == RTW_SAR_BAND_0 ?
|
||
|
hal->tx_pwr_by_rate_base_2g[arg->path][arg->rs] :
|
||
|
hal->tx_pwr_by_rate_base_5g[arg->path][arg->rs];
|
||
|
|
||
|
return (s8)clamp_t(s32, tmp, -max - 1, max) - base;
|
||
|
}
|
||
|
|
||
|
static const struct cfg80211_sar_freq_ranges rtw_common_sar_freq_ranges[] = {
|
||
|
[RTW_SAR_BAND_0] = { .start_freq = 2412, .end_freq = 2484, },
|
||
|
[RTW_SAR_BAND_1] = { .start_freq = 5180, .end_freq = 5320, },
|
||
|
[RTW_SAR_BAND_3] = { .start_freq = 5500, .end_freq = 5720, },
|
||
|
[RTW_SAR_BAND_4] = { .start_freq = 5745, .end_freq = 5825, },
|
||
|
};
|
||
|
|
||
|
static_assert(ARRAY_SIZE(rtw_common_sar_freq_ranges) == RTW_SAR_BAND_NR);
|
||
|
|
||
|
const struct cfg80211_sar_capa rtw_sar_capa = {
|
||
|
.type = NL80211_SAR_TYPE_POWER,
|
||
|
.num_freq_ranges = RTW_SAR_BAND_NR,
|
||
|
.freq_ranges = rtw_common_sar_freq_ranges,
|
||
|
};
|
||
|
|
||
|
int rtw_set_sar_specs(struct rtw_dev *rtwdev,
|
||
|
const struct cfg80211_sar_specs *sar)
|
||
|
{
|
||
|
struct rtw_sar_arg arg = {0};
|
||
|
struct rtw_sar new = {0};
|
||
|
u32 idx, i, j, k;
|
||
|
s32 power;
|
||
|
s8 val;
|
||
|
|
||
|
if (sar->type != NL80211_SAR_TYPE_POWER)
|
||
|
return -EINVAL;
|
||
|
|
||
|
memset(&new, rtwdev->chip->max_power_index, sizeof(new));
|
||
|
new.src = RTW_SAR_SOURCE_COMMON;
|
||
|
|
||
|
for (i = 0; i < sar->num_sub_specs; i++) {
|
||
|
idx = sar->sub_specs[i].freq_range_index;
|
||
|
if (idx >= RTW_SAR_BAND_NR)
|
||
|
return -EINVAL;
|
||
|
|
||
|
power = sar->sub_specs[i].power;
|
||
|
rtw_dbg(rtwdev, RTW_DBG_REGD, "On freq %u to %u, set SAR %d in 1/%lu dBm\n",
|
||
|
rtw_common_sar_freq_ranges[idx].start_freq,
|
||
|
rtw_common_sar_freq_ranges[idx].end_freq,
|
||
|
power, BIT(RTW_COMMON_SAR_FCT));
|
||
|
|
||
|
for (j = 0; j < RTW_RF_PATH_MAX; j++) {
|
||
|
for (k = 0; k < RTW_RATE_SECTION_MAX; k++) {
|
||
|
arg = (struct rtw_sar_arg){
|
||
|
.sar_band = idx,
|
||
|
.path = j,
|
||
|
.rs = k,
|
||
|
};
|
||
|
val = rtw_sar_to_phy(rtwdev, RTW_COMMON_SAR_FCT,
|
||
|
power, &arg);
|
||
|
new.cfg[j][k].common[idx] = val;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return rtw_apply_sar(rtwdev, &new);
|
||
|
}
|