forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
SAR stands for specific absorption rate. Driver controls TX power to follow the regulation of SAR. Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
- Loading branch information
1 parent
c7eb316
commit b1045ad
Showing
2 changed files
with
216 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause | ||
/* Copyright(c) 2019-2020 Realtek Corporation | ||
*/ | ||
|
||
#include "debug.h" | ||
#include "sar.h" | ||
|
||
static int rtw89_query_sar_config_common(struct rtw89_dev *rtwdev, s32 *cfg) | ||
{ | ||
struct rtw89_sar_cfg_common *rtwsar = &rtwdev->sar.cfg_common; | ||
enum rtw89_subband subband = rtwdev->hal.current_subband; | ||
|
||
if (!rtwsar->set[subband]) | ||
return -ENODATA; | ||
|
||
*cfg = rtwsar->cfg[subband]; | ||
return 0; | ||
} | ||
|
||
static const | ||
struct rtw89_sar_handler rtw89_sar_handlers[RTW89_SAR_SOURCE_NR] = { | ||
[RTW89_SAR_SOURCE_COMMON] = { | ||
.descr_sar_source = "RTW89_SAR_SOURCE_COMMON", | ||
.txpwr_factor_sar = 2, | ||
.query_sar_config = rtw89_query_sar_config_common, | ||
}, | ||
}; | ||
|
||
#define rtw89_sar_set_src(_dev, _src, _cfg_name, _cfg_data) \ | ||
do { \ | ||
typeof(_src) _s = (_src); \ | ||
typeof(_dev) _d = (_dev); \ | ||
BUILD_BUG_ON(!rtw89_sar_handlers[_s].descr_sar_source); \ | ||
BUILD_BUG_ON(!rtw89_sar_handlers[_s].query_sar_config); \ | ||
lockdep_assert_held(&_d->mutex); \ | ||
_d->sar._cfg_name = *(_cfg_data); \ | ||
_d->sar.src = _s; \ | ||
} while (0) | ||
|
||
static s8 rtw89_txpwr_sar_to_mac(struct rtw89_dev *rtwdev, u8 fct, s32 cfg) | ||
{ | ||
const u8 fct_mac = rtwdev->chip->txpwr_factor_mac; | ||
s32 cfg_mac; | ||
|
||
cfg_mac = fct > fct_mac ? | ||
cfg >> (fct - fct_mac) : cfg << (fct_mac - fct); | ||
|
||
return (s8)clamp_t(s32, cfg_mac, | ||
RTW89_SAR_TXPWR_MAC_MIN, | ||
RTW89_SAR_TXPWR_MAC_MAX); | ||
} | ||
|
||
s8 rtw89_query_sar(struct rtw89_dev *rtwdev) | ||
{ | ||
const enum rtw89_sar_sources src = rtwdev->sar.src; | ||
/* its members are protected by rtw89_sar_set_src() */ | ||
const struct rtw89_sar_handler *sar_hdl = &rtw89_sar_handlers[src]; | ||
int ret; | ||
s32 cfg; | ||
u8 fct; | ||
|
||
lockdep_assert_held(&rtwdev->mutex); | ||
|
||
if (src == RTW89_SAR_SOURCE_NONE) | ||
return RTW89_SAR_TXPWR_MAC_MAX; | ||
|
||
ret = sar_hdl->query_sar_config(rtwdev, &cfg); | ||
if (ret) | ||
return RTW89_SAR_TXPWR_MAC_MAX; | ||
|
||
fct = sar_hdl->txpwr_factor_sar; | ||
|
||
return rtw89_txpwr_sar_to_mac(rtwdev, fct, cfg); | ||
} | ||
|
||
void rtw89_print_sar(struct seq_file *m, struct rtw89_dev *rtwdev) | ||
{ | ||
const enum rtw89_sar_sources src = rtwdev->sar.src; | ||
/* its members are protected by rtw89_sar_set_src() */ | ||
const struct rtw89_sar_handler *sar_hdl = &rtw89_sar_handlers[src]; | ||
const u8 fct_mac = rtwdev->chip->txpwr_factor_mac; | ||
int ret; | ||
s32 cfg; | ||
u8 fct; | ||
|
||
lockdep_assert_held(&rtwdev->mutex); | ||
|
||
if (src == RTW89_SAR_SOURCE_NONE) { | ||
seq_puts(m, "no SAR is applied\n"); | ||
return; | ||
} | ||
|
||
seq_printf(m, "source: %d (%s)\n", src, sar_hdl->descr_sar_source); | ||
|
||
ret = sar_hdl->query_sar_config(rtwdev, &cfg); | ||
if (ret) { | ||
seq_printf(m, "config: return code: %d\n", ret); | ||
seq_printf(m, "assign: max setting: %d (unit: 1/%lu dBm)\n", | ||
RTW89_SAR_TXPWR_MAC_MAX, BIT(fct_mac)); | ||
return; | ||
} | ||
|
||
fct = sar_hdl->txpwr_factor_sar; | ||
|
||
seq_printf(m, "config: %d (unit: 1/%lu dBm)\n", cfg, BIT(fct)); | ||
} | ||
|
||
static int rtw89_apply_sar_common(struct rtw89_dev *rtwdev, | ||
const struct rtw89_sar_cfg_common *sar) | ||
{ | ||
enum rtw89_sar_sources src; | ||
int ret = 0; | ||
|
||
mutex_lock(&rtwdev->mutex); | ||
|
||
src = rtwdev->sar.src; | ||
if (src != RTW89_SAR_SOURCE_NONE && src != RTW89_SAR_SOURCE_COMMON) { | ||
rtw89_warn(rtwdev, "SAR source: %d is in use", src); | ||
ret = -EBUSY; | ||
goto exit; | ||
} | ||
|
||
rtw89_sar_set_src(rtwdev, RTW89_SAR_SOURCE_COMMON, cfg_common, sar); | ||
rtw89_chip_set_txpwr(rtwdev); | ||
|
||
exit: | ||
mutex_unlock(&rtwdev->mutex); | ||
return ret; | ||
} | ||
|
||
static const u8 rtw89_common_sar_subband_map[] = { | ||
RTW89_CH_2G, | ||
RTW89_CH_5G_BAND_1, | ||
RTW89_CH_5G_BAND_3, | ||
RTW89_CH_5G_BAND_4, | ||
}; | ||
|
||
static const struct cfg80211_sar_freq_ranges rtw89_common_sar_freq_ranges[] = { | ||
{ .start_freq = 2412, .end_freq = 2484, }, | ||
{ .start_freq = 5180, .end_freq = 5320, }, | ||
{ .start_freq = 5500, .end_freq = 5720, }, | ||
{ .start_freq = 5745, .end_freq = 5825, }, | ||
}; | ||
|
||
static_assert(ARRAY_SIZE(rtw89_common_sar_subband_map) == | ||
ARRAY_SIZE(rtw89_common_sar_freq_ranges)); | ||
|
||
const struct cfg80211_sar_capa rtw89_sar_capa = { | ||
.type = NL80211_SAR_TYPE_POWER, | ||
.num_freq_ranges = ARRAY_SIZE(rtw89_common_sar_freq_ranges), | ||
.freq_ranges = rtw89_common_sar_freq_ranges, | ||
}; | ||
|
||
int rtw89_ops_set_sar_specs(struct ieee80211_hw *hw, | ||
const struct cfg80211_sar_specs *sar) | ||
{ | ||
struct rtw89_dev *rtwdev = hw->priv; | ||
struct rtw89_sar_cfg_common sar_common = {0}; | ||
u8 fct; | ||
u32 freq_start; | ||
u32 freq_end; | ||
u32 band; | ||
s32 power; | ||
u32 i, idx; | ||
|
||
if (sar->type != NL80211_SAR_TYPE_POWER) | ||
return -EINVAL; | ||
|
||
fct = rtw89_sar_handlers[RTW89_SAR_SOURCE_COMMON].txpwr_factor_sar; | ||
|
||
for (i = 0; i < sar->num_sub_specs; i++) { | ||
idx = sar->sub_specs[i].freq_range_index; | ||
if (idx >= ARRAY_SIZE(rtw89_common_sar_freq_ranges)) | ||
return -EINVAL; | ||
|
||
freq_start = rtw89_common_sar_freq_ranges[idx].start_freq; | ||
freq_end = rtw89_common_sar_freq_ranges[idx].end_freq; | ||
band = rtw89_common_sar_subband_map[idx]; | ||
power = sar->sub_specs[i].power; | ||
|
||
rtw89_info(rtwdev, "On freq %u to %u, ", freq_start, freq_end); | ||
rtw89_info(rtwdev, "set SAR power limit %d (unit: 1/%lu dBm)\n", | ||
power, BIT(fct)); | ||
|
||
sar_common.set[band] = true; | ||
sar_common.cfg[band] = power; | ||
} | ||
|
||
return rtw89_apply_sar_common(rtwdev, &sar_common); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ | ||
/* Copyright(c) 2019-2020 Realtek Corporation | ||
*/ | ||
|
||
#ifndef __RTW89_SAR_H__ | ||
#define __RTW89_SAR_H__ | ||
|
||
#include "core.h" | ||
|
||
#define RTW89_SAR_TXPWR_MAC_MAX S8_MAX | ||
#define RTW89_SAR_TXPWR_MAC_MIN S8_MIN | ||
|
||
struct rtw89_sar_handler { | ||
const char *descr_sar_source; | ||
u8 txpwr_factor_sar; | ||
int (*query_sar_config)(struct rtw89_dev *rtwdev, s32 *cfg); | ||
}; | ||
|
||
extern const struct cfg80211_sar_capa rtw89_sar_capa; | ||
|
||
s8 rtw89_query_sar(struct rtw89_dev *rtwdev); | ||
void rtw89_print_sar(struct seq_file *m, struct rtw89_dev *rtwdev); | ||
int rtw89_ops_set_sar_specs(struct ieee80211_hw *hw, | ||
const struct cfg80211_sar_specs *sar); | ||
|
||
#endif |