rtw88: support SAR via kernel common API

Register cfg80211_sar_capa with type NL80211_SAR_TYPE_POWER and four
frequency ranges for configurations in unit of 0.25 dBm. And handle
callback set_sar_specs.

Originally, TX power has three main parameters, i.e. power base,
power by rate, and power limit. The formula can be simply considered
as TX power = power base + min(power by rate, power limit). With the
support of SAR which can be treated as another power limit, there is
one more parameter for TX power. And the formula will evolve into
TX power = power base + min(power by rate, power limit, power sar).

Besides, debugfs tx_pwr_tbl is also refined to show SAR information.
The following is an example for the difference.
Before supporting SAR,
-----------------------------------
...
path rate       pwr       base      (byr  lmt ) rem
   A  CCK_1M     66(0x42)   78  -12 (  12  -12)    0
   A  CCK_2M     66(0x42)   78  -12 (   8  -12)    0
...
-----------------------------------
After supporting SAR and making some configurations,
-----------------------------------
...
path rate       pwr       base      (byr  lmt  sar ) rem
   A  CCK_1M     62(0x3e)   78  -16 (  12  -12  -16)    0
   A  CCK_2M     62(0x3e)   78  -16 (   8  -12  -16)    0
...
-----------------------------------

Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://lore.kernel.org/r/20211220093656.65312-1-pkshih@realtek.com
This commit is contained in:
Zong-Zhe Yang 2021-12-20 17:36:56 +08:00 committed by Kalle Valo
parent 10d162b2ed
commit 8704d0befb
9 changed files with 257 additions and 20 deletions

View File

@ -15,6 +15,7 @@ rtw88_core-y += main.o \
ps.o \
sec.o \
bf.o \
sar.o \
regd.o
rtw88_core-$(CONFIG_PM) += wow.o

View File

@ -634,8 +634,8 @@ static int rtw_debugfs_get_tx_pwr_tbl(struct seq_file *m, void *v)
seq_printf(m, "channel: %u\n", ch);
seq_printf(m, "bandwidth: %u\n", bw);
seq_printf(m, "regulatory: %s\n", rtw_get_regd_string(regd));
seq_printf(m, "%-4s %-10s %-3s%6s %-4s %4s (%-4s %-4s) %-4s\n",
"path", "rate", "pwr", "", "base", "", "byr", "lmt", "rem");
seq_printf(m, "%-4s %-10s %-9s %-9s (%-4s %-4s %-4s) %-4s\n",
"path", "rate", "pwr", "base", "byr", "lmt", "sar", "rem");
mutex_lock(&hal->tx_power_mutex);
for (path = RF_PATH_A; path <= RF_PATH_B; path++) {
@ -657,13 +657,15 @@ static int rtw_debugfs_get_tx_pwr_tbl(struct seq_file *m, void *v)
seq_printf(m, "%4c ", path + 'A');
rtw_print_rate(m, rate);
seq_printf(m, " %3u(0x%02x) %4u %4d (%4d %4d) %4d\n",
seq_printf(m, " %3u(0x%02x) %4u %4d (%4d %4d %4d) %4d\n",
hal->tx_pwr_tbl[path][rate],
hal->tx_pwr_tbl[path][rate],
pwr_param.pwr_base,
min_t(s8, pwr_param.pwr_offset,
pwr_param.pwr_limit),
min3(pwr_param.pwr_offset,
pwr_param.pwr_limit,
pwr_param.pwr_sar),
pwr_param.pwr_offset, pwr_param.pwr_limit,
pwr_param.pwr_sar,
pwr_param.pwr_remnant);
}
}

View File

@ -13,6 +13,7 @@
#include "bf.h"
#include "debug.h"
#include "wow.h"
#include "sar.h"
static void rtw_ops_tx(struct ieee80211_hw *hw,
struct ieee80211_tx_control *control,
@ -836,6 +837,16 @@ static void rtw_ops_cancel_hw_scan(struct ieee80211_hw *hw,
mutex_unlock(&rtwdev->mutex);
}
static int rtw_ops_set_sar_specs(struct ieee80211_hw *hw,
const struct cfg80211_sar_specs *sar)
{
struct rtw_dev *rtwdev = hw->priv;
rtw_set_sar_specs(rtwdev, sar);
return 0;
}
const struct ieee80211_ops rtw_ops = {
.tx = rtw_ops_tx,
.wake_tx_queue = rtw_ops_wake_tx_queue,
@ -865,6 +876,7 @@ const struct ieee80211_ops rtw_ops = {
.reconfig_complete = rtw_reconfig_complete,
.hw_scan = rtw_ops_hw_scan,
.cancel_hw_scan = rtw_ops_cancel_hw_scan,
.set_sar_specs = rtw_ops_set_sar_specs,
#ifdef CONFIG_PM
.suspend = rtw_ops_suspend,
.resume = rtw_ops_resume,

View File

@ -17,6 +17,7 @@
#include "tx.h"
#include "debug.h"
#include "bf.h"
#include "sar.h"
bool rtw_disable_lps_deep_mode;
EXPORT_SYMBOL(rtw_disable_lps_deep_mode);
@ -751,6 +752,25 @@ void rtw_set_channel(struct rtw_dev *rtwdev)
hal->current_primary_channel_index = primary_chan_idx;
hal->current_band_type = center_chan > 14 ? RTW_BAND_5G : RTW_BAND_2G;
switch (center_chan) {
case 1 ... 14:
hal->sar_band = RTW_SAR_BAND_0;
break;
case 36 ... 64:
hal->sar_band = RTW_SAR_BAND_1;
break;
case 100 ... 144:
hal->sar_band = RTW_SAR_BAND_3;
break;
case 149 ... 177:
hal->sar_band = RTW_SAR_BAND_4;
break;
default:
WARN(1, "unknown ch(%u) to SAR band\n", center_chan);
hal->sar_band = RTW_SAR_BAND_0;
break;
}
for (i = RTW_CHANNEL_WIDTH_20; i <= RTW_MAX_CHANNEL_WIDTH; i++)
hal->cch_by_bw[i] = ch_param.cch_by_bw[i];
@ -2037,6 +2057,8 @@ int rtw_register_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw)
rtw_set_supported_band(hw, rtwdev->chip);
SET_IEEE80211_PERM_ADDR(hw, rtwdev->efuse.addr);
hw->wiphy->sar_capa = &rtw_sar_capa;
ret = rtw_regd_init(rtwdev);
if (ret) {
rtw_err(rtwdev, "failed to init regd\n");

View File

@ -1813,6 +1813,33 @@ struct rtw_fw_state {
u32 feature;
};
enum rtw_sar_sources {
RTW_SAR_SOURCE_NONE,
RTW_SAR_SOURCE_COMMON,
};
enum rtw_sar_bands {
RTW_SAR_BAND_0,
RTW_SAR_BAND_1,
/* RTW_SAR_BAND_2, not used now */
RTW_SAR_BAND_3,
RTW_SAR_BAND_4,
RTW_SAR_BAND_NR,
};
/* the union is reserved for other knids of SAR sources
* which might not re-use same format with array common.
*/
union rtw_sar_cfg {
s8 common[RTW_SAR_BAND_NR];
};
struct rtw_sar {
enum rtw_sar_sources src;
union rtw_sar_cfg cfg[RTW_RF_PATH_MAX][RTW_RATE_SECTION_MAX];
};
struct rtw_hal {
u32 rcr;
@ -1861,6 +1888,9 @@ struct rtw_hal {
[RTW_MAX_CHANNEL_NUM_5G];
s8 tx_pwr_tbl[RTW_RF_PATH_MAX]
[DESC_RATE_MAX];
enum rtw_sar_bands sar_band;
struct rtw_sar sar;
};
struct rtw_path_div {

View File

@ -10,6 +10,7 @@
#include "phy.h"
#include "debug.h"
#include "regd.h"
#include "sar.h"
struct phy_cfg_pair {
u32 addr;
@ -2004,6 +2005,25 @@ static u8 rtw_phy_get_5g_tx_power_index(struct rtw_dev *rtwdev,
return tx_power;
}
/* return RTW_RATE_SECTION_MAX to indicate rate is invalid */
static u8 rtw_phy_rate_to_rate_section(u8 rate)
{
if (rate >= DESC_RATE1M && rate <= DESC_RATE11M)
return RTW_RATE_SECTION_CCK;
else if (rate >= DESC_RATE6M && rate <= DESC_RATE54M)
return RTW_RATE_SECTION_OFDM;
else if (rate >= DESC_RATEMCS0 && rate <= DESC_RATEMCS7)
return RTW_RATE_SECTION_HT_1S;
else if (rate >= DESC_RATEMCS8 && rate <= DESC_RATEMCS15)
return RTW_RATE_SECTION_HT_2S;
else if (rate >= DESC_RATEVHT1SS_MCS0 && rate <= DESC_RATEVHT1SS_MCS9)
return RTW_RATE_SECTION_VHT_1S;
else if (rate >= DESC_RATEVHT2SS_MCS0 && rate <= DESC_RATEVHT2SS_MCS9)
return RTW_RATE_SECTION_VHT_2S;
else
return RTW_RATE_SECTION_MAX;
}
static s8 rtw_phy_get_tx_power_limit(struct rtw_dev *rtwdev, u8 band,
enum rtw_bandwidth bw, u8 rf_path,
u8 rate, u8 channel, u8 regd)
@ -2011,7 +2031,7 @@ static s8 rtw_phy_get_tx_power_limit(struct rtw_dev *rtwdev, u8 band,
struct rtw_hal *hal = &rtwdev->hal;
u8 *cch_by_bw = hal->cch_by_bw;
s8 power_limit = (s8)rtwdev->chip->max_power_index;
u8 rs;
u8 rs = rtw_phy_rate_to_rate_section(rate);
int ch_idx;
u8 cur_bw, cur_ch;
s8 cur_lmt;
@ -2019,19 +2039,7 @@ static s8 rtw_phy_get_tx_power_limit(struct rtw_dev *rtwdev, u8 band,
if (regd > RTW_REGD_WW)
return power_limit;
if (rate >= DESC_RATE1M && rate <= DESC_RATE11M)
rs = RTW_RATE_SECTION_CCK;
else if (rate >= DESC_RATE6M && rate <= DESC_RATE54M)
rs = RTW_RATE_SECTION_OFDM;
else if (rate >= DESC_RATEMCS0 && rate <= DESC_RATEMCS7)
rs = RTW_RATE_SECTION_HT_1S;
else if (rate >= DESC_RATEMCS8 && rate <= DESC_RATEMCS15)
rs = RTW_RATE_SECTION_HT_2S;
else if (rate >= DESC_RATEVHT1SS_MCS0 && rate <= DESC_RATEVHT1SS_MCS9)
rs = RTW_RATE_SECTION_VHT_1S;
else if (rate >= DESC_RATEVHT2SS_MCS0 && rate <= DESC_RATEVHT2SS_MCS9)
rs = RTW_RATE_SECTION_VHT_2S;
else
if (rs == RTW_RATE_SECTION_MAX)
goto err;
/* only 20M BW with cck and ofdm */
@ -2065,6 +2073,27 @@ err:
return (s8)rtwdev->chip->max_power_index;
}
static s8 rtw_phy_get_tx_power_sar(struct rtw_dev *rtwdev, u8 sar_band,
u8 rf_path, u8 rate)
{
u8 rs = rtw_phy_rate_to_rate_section(rate);
struct rtw_sar_arg arg = {
.sar_band = sar_band,
.path = rf_path,
.rs = rs,
};
if (rs == RTW_RATE_SECTION_MAX)
goto err;
return rtw_query_sar(rtwdev, &arg);
err:
WARN(1, "invalid arguments, sar_band=%d, path=%d, rate=%d\n",
sar_band, rf_path, rate);
return (s8)rtwdev->chip->max_power_index;
}
void rtw_get_tx_power_params(struct rtw_dev *rtwdev, u8 path, u8 rate, u8 bw,
u8 ch, u8 regd, struct rtw_power_params *pwr_param)
{
@ -2076,6 +2105,7 @@ void rtw_get_tx_power_params(struct rtw_dev *rtwdev, u8 path, u8 rate, u8 bw,
s8 *offset = &pwr_param->pwr_offset;
s8 *limit = &pwr_param->pwr_limit;
s8 *remnant = &pwr_param->pwr_remnant;
s8 *sar = &pwr_param->pwr_sar;
pwr_idx = &rtwdev->efuse.txpwr_idx_table[path];
group = rtw_get_channel_group(ch, rate);
@ -2099,6 +2129,7 @@ void rtw_get_tx_power_params(struct rtw_dev *rtwdev, u8 path, u8 rate, u8 bw,
rate, ch, regd);
*remnant = (rate <= DESC_RATE11M ? dm_info->txagc_remnant_cck :
dm_info->txagc_remnant_ofdm);
*sar = rtw_phy_get_tx_power_sar(rtwdev, hal->sar_band, path, rate);
}
u8
@ -2113,7 +2144,9 @@ rtw_phy_get_tx_power_index(struct rtw_dev *rtwdev, u8 rf_path, u8 rate,
channel, regd, &pwr_param);
tx_power = pwr_param.pwr_base;
offset = min_t(s8, pwr_param.pwr_offset, pwr_param.pwr_limit);
offset = min3(pwr_param.pwr_offset,
pwr_param.pwr_limit,
pwr_param.pwr_sar);
if (rtwdev->chip->en_dis_dpd)
offset += rtw_phy_get_dis_dpd_by_rate_diff(rtwdev, rate);

View File

@ -148,6 +148,7 @@ struct rtw_power_params {
s8 pwr_offset;
s8 pwr_limit;
s8 pwr_remnant;
s8 pwr_sar;
};
void

View File

@ -0,0 +1,114 @@
// 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_info(rtwdev, "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);
}

View File

@ -0,0 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/* Copyright(c) 2018-2021 Realtek Corporation
*/
#include "main.h"
/* NL80211_SAR_TYPE_POWER means unit is in 0.25 dBm,
* where 0.25 = 1/4 = 2^(-2), so make factor 2.
*/
#define RTW_COMMON_SAR_FCT 2
struct rtw_sar_arg {
u8 sar_band;
u8 path;
u8 rs;
};
extern const struct cfg80211_sar_capa rtw_sar_capa;
s8 rtw_query_sar(struct rtw_dev *rtwdev, const struct rtw_sar_arg *arg);
int rtw_set_sar_specs(struct rtw_dev *rtwdev,
const struct cfg80211_sar_specs *sar);