mirror of
https://github.com/torvalds/linux.git
synced 2026-06-04 20:46:48 +02:00
wifi: rtw89: regd: support loading regd table from fw element
Regd table is a table that we use to describe how to map Realtek RF TX power settings on different countries. Originally, a common regd table for all chips is implemented in driver. However, in order to work on all chips, the common regd table might have some trade-off. So now, there are also an individual regd table for some chips. And, we support loading it from FW element. Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com> Signed-off-by: Ping-Ke Shih <pkshih@realtek.com> Link: https://patch.msgid.link/20250120032723.19042-2-pkshih@realtek.com
This commit is contained in:
parent
076652f56e
commit
e7196b32a4
|
|
@ -22,6 +22,7 @@ struct rtw89_h2c_rf_tssi;
|
|||
struct rtw89_fw_txpwr_track_cfg;
|
||||
struct rtw89_phy_rfk_log_fmt;
|
||||
struct rtw89_debugfs;
|
||||
struct rtw89_regd_data;
|
||||
|
||||
extern const struct ieee80211_ops rtw89_ops;
|
||||
|
||||
|
|
@ -718,6 +719,7 @@ enum rtw89_ofdma_type {
|
|||
RTW89_OFDMA_NUM,
|
||||
};
|
||||
|
||||
/* neither insert new in the middle, nor change any given definition */
|
||||
enum rtw89_regulation_type {
|
||||
RTW89_WW = 0,
|
||||
RTW89_ETSI = 1,
|
||||
|
|
@ -4539,6 +4541,7 @@ struct rtw89_fw_elm_info {
|
|||
struct rtw89_phy_table *rf_nctl;
|
||||
struct rtw89_fw_txpwr_track_cfg *txpwr_trk;
|
||||
struct rtw89_phy_rfk_log_fmt *rfk_log_fmt;
|
||||
const struct rtw89_regd_data *regd;
|
||||
};
|
||||
|
||||
enum rtw89_fw_mss_dev_type {
|
||||
|
|
@ -5153,11 +5156,22 @@ struct rtw89_regd {
|
|||
u8 txpwr_regd[RTW89_BAND_NUM];
|
||||
};
|
||||
|
||||
struct rtw89_regd_data {
|
||||
unsigned int nr;
|
||||
struct rtw89_regd map[] __counted_by(nr);
|
||||
};
|
||||
|
||||
struct rtw89_regd_ctrl {
|
||||
unsigned int nr;
|
||||
const struct rtw89_regd *map;
|
||||
};
|
||||
|
||||
#define RTW89_REGD_MAX_COUNTRY_NUM U8_MAX
|
||||
#define RTW89_5GHZ_UNII4_CHANNEL_NUM 3
|
||||
#define RTW89_5GHZ_UNII4_START_INDEX 25
|
||||
|
||||
struct rtw89_regulatory_info {
|
||||
struct rtw89_regd_ctrl ctrl;
|
||||
const struct rtw89_regd *regd;
|
||||
enum rtw89_reg_6ghz_power reg_6ghz_power;
|
||||
struct rtw89_reg_6ghz_tpe reg_6ghz_tpe;
|
||||
|
|
|
|||
|
|
@ -1056,6 +1056,89 @@ int rtw89_build_rfk_log_fmt_from_elm(struct rtw89_dev *rtwdev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool rtw89_regd_entcpy(struct rtw89_regd *regd, const void *cursor,
|
||||
u8 cursor_size)
|
||||
{
|
||||
/* fill default values if needed for backward compatibility */
|
||||
struct rtw89_fw_regd_entry entry = {
|
||||
.rule_2ghz = RTW89_NA,
|
||||
.rule_5ghz = RTW89_NA,
|
||||
.rule_6ghz = RTW89_NA,
|
||||
};
|
||||
u8 valid_size = min_t(u8, sizeof(entry), cursor_size);
|
||||
|
||||
memcpy(&entry, cursor, valid_size);
|
||||
memset(regd, 0, sizeof(*regd));
|
||||
|
||||
regd->alpha2[0] = entry.alpha2_0;
|
||||
regd->alpha2[1] = entry.alpha2_1;
|
||||
regd->alpha2[2] = '\0';
|
||||
|
||||
/* also need to consider forward compatibility */
|
||||
regd->txpwr_regd[RTW89_BAND_2G] = entry.rule_2ghz < RTW89_REGD_NUM ?
|
||||
entry.rule_2ghz : RTW89_NA;
|
||||
regd->txpwr_regd[RTW89_BAND_5G] = entry.rule_5ghz < RTW89_REGD_NUM ?
|
||||
entry.rule_5ghz : RTW89_NA;
|
||||
regd->txpwr_regd[RTW89_BAND_6G] = entry.rule_6ghz < RTW89_REGD_NUM ?
|
||||
entry.rule_6ghz : RTW89_NA;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define rtw89_for_each_in_regd_element(regd, element) \
|
||||
for (const void *cursor = (element)->content, \
|
||||
*end = (element)->content + \
|
||||
le32_to_cpu((element)->num_ents) * (element)->ent_sz; \
|
||||
cursor < end; cursor += (element)->ent_sz) \
|
||||
if (rtw89_regd_entcpy(regd, cursor, (element)->ent_sz))
|
||||
|
||||
static
|
||||
int rtw89_recognize_regd_from_elm(struct rtw89_dev *rtwdev,
|
||||
const struct rtw89_fw_element_hdr *elm,
|
||||
const union rtw89_fw_element_arg arg)
|
||||
{
|
||||
const struct __rtw89_fw_regd_element *regd_elm = &elm->u.regd;
|
||||
struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info;
|
||||
u32 num_ents = le32_to_cpu(regd_elm->num_ents);
|
||||
struct rtw89_regd_data *p;
|
||||
struct rtw89_regd regd;
|
||||
u32 i = 0;
|
||||
|
||||
if (num_ents > RTW89_REGD_MAX_COUNTRY_NUM) {
|
||||
rtw89_warn(rtwdev,
|
||||
"regd element ents (%d) are over max num (%d)\n",
|
||||
num_ents, RTW89_REGD_MAX_COUNTRY_NUM);
|
||||
rtw89_warn(rtwdev,
|
||||
"regd element ignore and take another/common\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (elm_info->regd) {
|
||||
rtw89_debug(rtwdev, RTW89_DBG_REGD,
|
||||
"regd element take the latter\n");
|
||||
devm_kfree(rtwdev->dev, elm_info->regd);
|
||||
elm_info->regd = NULL;
|
||||
}
|
||||
|
||||
p = devm_kzalloc(rtwdev->dev, struct_size(p, map, num_ents), GFP_KERNEL);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
p->nr = num_ents;
|
||||
rtw89_for_each_in_regd_element(®d, regd_elm)
|
||||
p->map[i++] = regd;
|
||||
|
||||
if (i != num_ents) {
|
||||
rtw89_err(rtwdev, "regd element has %d invalid ents\n",
|
||||
num_ents - i);
|
||||
devm_kfree(rtwdev->dev, p);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
elm_info->regd = p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rtw89_fw_element_handler __fw_element_handlers[] = {
|
||||
[RTW89_FW_ELEMENT_ID_BBMCU0] = {__rtw89_fw_recognize_from_elm,
|
||||
{ .fw_type = RTW89_FW_BBMCU0 }, NULL},
|
||||
|
|
@ -1114,6 +1197,9 @@ static const struct rtw89_fw_element_handler __fw_element_handlers[] = {
|
|||
[RTW89_FW_ELEMENT_ID_RFKLOG_FMT] = {
|
||||
rtw89_build_rfk_log_fmt_from_elm, {}, NULL,
|
||||
},
|
||||
[RTW89_FW_ELEMENT_ID_REGD] = {
|
||||
rtw89_recognize_regd_from_elm, {}, "REGD",
|
||||
},
|
||||
};
|
||||
|
||||
int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev)
|
||||
|
|
|
|||
|
|
@ -3882,6 +3882,7 @@ enum rtw89_fw_element_id {
|
|||
RTW89_FW_ELEMENT_ID_TX_SHAPE_LMT_RU = 17,
|
||||
RTW89_FW_ELEMENT_ID_TXPWR_TRK = 18,
|
||||
RTW89_FW_ELEMENT_ID_RFKLOG_FMT = 19,
|
||||
RTW89_FW_ELEMENT_ID_REGD = 20,
|
||||
|
||||
RTW89_FW_ELEMENT_ID_NUM,
|
||||
};
|
||||
|
|
@ -3925,6 +3926,15 @@ struct __rtw89_fw_txpwr_element {
|
|||
u8 content[];
|
||||
} __packed;
|
||||
|
||||
struct __rtw89_fw_regd_element {
|
||||
u8 rsvd0;
|
||||
u8 rsvd1;
|
||||
u8 rsvd2;
|
||||
u8 ent_sz;
|
||||
__le32 num_ents;
|
||||
u8 content[];
|
||||
} __packed;
|
||||
|
||||
enum rtw89_fw_txpwr_trk_type {
|
||||
__RTW89_FW_TXPWR_TRK_TYPE_6GHZ_START = 0,
|
||||
RTW89_FW_TXPWR_TRK_TYPE_6GB_N = 0,
|
||||
|
|
@ -4016,6 +4026,7 @@ struct rtw89_fw_element_hdr {
|
|||
__le16 offset[];
|
||||
} __packed rfk_log_fmt;
|
||||
struct __rtw89_fw_txpwr_element txpwr;
|
||||
struct __rtw89_fw_regd_element regd;
|
||||
} __packed u;
|
||||
} __packed;
|
||||
|
||||
|
|
@ -4874,6 +4885,17 @@ int rtw89_chip_h2c_ba_cam(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Must consider compatibility; don't insert new in the mid.
|
||||
* Fill each field's default value in rtw89_regd_entcpy().
|
||||
*/
|
||||
struct rtw89_fw_regd_entry {
|
||||
u8 alpha2_0;
|
||||
u8 alpha2_1;
|
||||
u8 rule_2ghz;
|
||||
u8 rule_5ghz;
|
||||
u8 rule_6ghz;
|
||||
} __packed;
|
||||
|
||||
/* must consider compatibility; don't insert new in the mid */
|
||||
struct rtw89_fw_txpwr_byrate_entry {
|
||||
u8 band;
|
||||
|
|
|
|||
|
|
@ -7,9 +7,12 @@
|
|||
#include "ps.h"
|
||||
#include "util.h"
|
||||
|
||||
#define COUNTRY_REGD(_alpha2, _txpwr_regd...) \
|
||||
{.alpha2 = (_alpha2), \
|
||||
.txpwr_regd = {_txpwr_regd}, \
|
||||
#define COUNTRY_REGD(_alpha2, _rule_2ghz, _rule_5ghz, _rule_6ghz) \
|
||||
{ \
|
||||
.alpha2 = _alpha2, \
|
||||
.txpwr_regd[RTW89_BAND_2G] = _rule_2ghz, \
|
||||
.txpwr_regd[RTW89_BAND_5G] = _rule_5ghz, \
|
||||
.txpwr_regd[RTW89_BAND_6G] = _rule_6ghz, \
|
||||
}
|
||||
|
||||
static const struct rtw89_regd rtw89_ww_regd =
|
||||
|
|
@ -295,13 +298,16 @@ static const char rtw89_alpha2_list_eu[][3] = {
|
|||
"RO",
|
||||
};
|
||||
|
||||
static const struct rtw89_regd *rtw89_regd_find_reg_by_name(const char *alpha2)
|
||||
static const struct rtw89_regd *rtw89_regd_find_reg_by_name(struct rtw89_dev *rtwdev,
|
||||
const char *alpha2)
|
||||
{
|
||||
struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
|
||||
const struct rtw89_regd_ctrl *regd_ctrl = ®ulatory->ctrl;
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(rtw89_regd_map); i++) {
|
||||
if (!memcmp(rtw89_regd_map[i].alpha2, alpha2, 2))
|
||||
return &rtw89_regd_map[i];
|
||||
for (i = 0; i < regd_ctrl->nr; i++) {
|
||||
if (!memcmp(regd_ctrl->map[i].alpha2, alpha2, 2))
|
||||
return ®d_ctrl->map[i];
|
||||
}
|
||||
|
||||
return &rtw89_ww_regd;
|
||||
|
|
@ -312,22 +318,25 @@ static bool rtw89_regd_is_ww(const struct rtw89_regd *regd)
|
|||
return regd == &rtw89_ww_regd;
|
||||
}
|
||||
|
||||
static u8 rtw89_regd_get_index(const struct rtw89_regd *regd)
|
||||
static u8 rtw89_regd_get_index(struct rtw89_dev *rtwdev, const struct rtw89_regd *regd)
|
||||
{
|
||||
struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
|
||||
const struct rtw89_regd_ctrl *regd_ctrl = ®ulatory->ctrl;
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(rtw89_regd_map) > RTW89_REGD_MAX_COUNTRY_NUM);
|
||||
|
||||
if (rtw89_regd_is_ww(regd))
|
||||
return RTW89_REGD_MAX_COUNTRY_NUM;
|
||||
|
||||
return regd - rtw89_regd_map;
|
||||
return regd - regd_ctrl->map;
|
||||
}
|
||||
|
||||
static u8 rtw89_regd_get_index_by_name(const char *alpha2)
|
||||
static u8 rtw89_regd_get_index_by_name(struct rtw89_dev *rtwdev, const char *alpha2)
|
||||
{
|
||||
const struct rtw89_regd *regd;
|
||||
|
||||
regd = rtw89_regd_find_reg_by_name(alpha2);
|
||||
return rtw89_regd_get_index(regd);
|
||||
regd = rtw89_regd_find_reg_by_name(rtwdev, alpha2);
|
||||
return rtw89_regd_get_index(rtwdev, regd);
|
||||
}
|
||||
|
||||
#define rtw89_debug_regd(_dev, _regd, _desc, _argv...) \
|
||||
|
|
@ -345,6 +354,7 @@ static void rtw89_regd_setup_unii4(struct rtw89_dev *rtwdev,
|
|||
struct wiphy *wiphy)
|
||||
{
|
||||
struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
|
||||
const struct rtw89_regd_ctrl *regd_ctrl = ®ulatory->ctrl;
|
||||
const struct rtw89_chip_info *chip = rtwdev->chip;
|
||||
struct ieee80211_supported_band *sband;
|
||||
struct rtw89_acpi_dsm_result res = {};
|
||||
|
|
@ -382,8 +392,8 @@ static void rtw89_regd_setup_unii4(struct rtw89_dev *rtwdev,
|
|||
"acpi: eval if allow unii-4: 0x%x\n", val);
|
||||
|
||||
bottom:
|
||||
for (i = 0; i < ARRAY_SIZE(rtw89_regd_map); i++) {
|
||||
const struct rtw89_regd *regd = &rtw89_regd_map[i];
|
||||
for (i = 0; i < regd_ctrl->nr; i++) {
|
||||
const struct rtw89_regd *regd = ®d_ctrl->map[i];
|
||||
|
||||
switch (regd->txpwr_regd[RTW89_BAND_5G]) {
|
||||
case RTW89_FCC:
|
||||
|
|
@ -406,7 +416,7 @@ static void __rtw89_regd_setup_policy_6ghz(struct rtw89_dev *rtwdev, bool block,
|
|||
struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
|
||||
u8 index;
|
||||
|
||||
index = rtw89_regd_get_index_by_name(alpha2);
|
||||
index = rtw89_regd_get_index_by_name(rtwdev, alpha2);
|
||||
if (index == RTW89_REGD_MAX_COUNTRY_NUM) {
|
||||
rtw89_debug(rtwdev, RTW89_DBG_REGD, "%s: unknown alpha2 %c%c\n",
|
||||
__func__, alpha2[0], alpha2[1]);
|
||||
|
|
@ -474,6 +484,7 @@ static void rtw89_regd_setup_policy_6ghz(struct rtw89_dev *rtwdev)
|
|||
static void rtw89_regd_setup_policy_6ghz_sp(struct rtw89_dev *rtwdev)
|
||||
{
|
||||
struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
|
||||
const struct rtw89_regd_ctrl *regd_ctrl = ®ulatory->ctrl;
|
||||
const struct rtw89_acpi_policy_6ghz_sp *ptr;
|
||||
struct rtw89_acpi_dsm_result res = {};
|
||||
bool enable_by_us;
|
||||
|
|
@ -505,8 +516,8 @@ static void rtw89_regd_setup_policy_6ghz_sp(struct rtw89_dev *rtwdev)
|
|||
|
||||
enable_by_us = u8_get_bits(ptr->conf, RTW89_ACPI_CONF_6GHZ_SP_US);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(rtw89_regd_map); i++) {
|
||||
const struct rtw89_regd *tmp = &rtw89_regd_map[i];
|
||||
for (i = 0; i < regd_ctrl->nr; i++) {
|
||||
const struct rtw89_regd *tmp = ®d_ctrl->map[i];
|
||||
|
||||
if (enable_by_us && memcmp(tmp->alpha2, "US", 2) == 0)
|
||||
clear_bit(i, regulatory->block_6ghz_sp);
|
||||
|
|
@ -573,8 +584,19 @@ static void rtw89_regd_setup_6ghz(struct rtw89_dev *rtwdev, struct wiphy *wiphy)
|
|||
|
||||
int rtw89_regd_setup(struct rtw89_dev *rtwdev)
|
||||
{
|
||||
struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
|
||||
struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info;
|
||||
const struct rtw89_regd_data *regd_data = elm_info->regd;
|
||||
struct wiphy *wiphy = rtwdev->hw->wiphy;
|
||||
|
||||
if (regd_data) {
|
||||
regulatory->ctrl.nr = regd_data->nr;
|
||||
regulatory->ctrl.map = regd_data->map;
|
||||
} else {
|
||||
regulatory->ctrl.nr = ARRAY_SIZE(rtw89_regd_map);
|
||||
regulatory->ctrl.map = rtw89_regd_map;
|
||||
}
|
||||
|
||||
if (!wiphy)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
@ -599,7 +621,7 @@ int rtw89_regd_init(struct rtw89_dev *rtwdev,
|
|||
if (!wiphy)
|
||||
return -EINVAL;
|
||||
|
||||
chip_regd = rtw89_regd_find_reg_by_name(rtwdev->efuse.country_code);
|
||||
chip_regd = rtw89_regd_find_reg_by_name(rtwdev, rtwdev->efuse.country_code);
|
||||
if (!rtw89_regd_is_ww(chip_regd)) {
|
||||
rtwdev->regulatory.regd = chip_regd;
|
||||
/* Ignore country ie if there is a country domain programmed in chip */
|
||||
|
|
@ -637,7 +659,7 @@ static void rtw89_regd_apply_policy_unii4(struct rtw89_dev *rtwdev,
|
|||
if (!chip->support_unii4)
|
||||
return;
|
||||
|
||||
index = rtw89_regd_get_index(regd);
|
||||
index = rtw89_regd_get_index(rtwdev, regd);
|
||||
if (index != RTW89_REGD_MAX_COUNTRY_NUM &&
|
||||
!test_bit(index, regulatory->block_unii4))
|
||||
return;
|
||||
|
|
@ -655,7 +677,7 @@ static bool regd_is_6ghz_blocked(struct rtw89_dev *rtwdev)
|
|||
const struct rtw89_regd *regd = regulatory->regd;
|
||||
u8 index;
|
||||
|
||||
index = rtw89_regd_get_index(regd);
|
||||
index = rtw89_regd_get_index(rtwdev, regd);
|
||||
if (index != RTW89_REGD_MAX_COUNTRY_NUM &&
|
||||
!test_bit(index, regulatory->block_6ghz))
|
||||
return false;
|
||||
|
|
@ -700,7 +722,7 @@ static void rtw89_regd_notifier_apply(struct rtw89_dev *rtwdev,
|
|||
struct wiphy *wiphy,
|
||||
struct regulatory_request *request)
|
||||
{
|
||||
rtwdev->regulatory.regd = rtw89_regd_find_reg_by_name(request->alpha2);
|
||||
rtwdev->regulatory.regd = rtw89_regd_find_reg_by_name(rtwdev, request->alpha2);
|
||||
/* This notification might be set from the system of distros,
|
||||
* and it does not expect the regulatory will be modified by
|
||||
* connecting to an AP (i.e. country ie).
|
||||
|
|
@ -925,7 +947,7 @@ static bool __rtw89_reg_6ghz_power_recalc(struct rtw89_dev *rtwdev)
|
|||
sel = RTW89_REG_6GHZ_POWER_DFLT;
|
||||
|
||||
if (sel == RTW89_REG_6GHZ_POWER_STD) {
|
||||
index = rtw89_regd_get_index(regd);
|
||||
index = rtw89_regd_get_index(rtwdev, regd);
|
||||
if (index == RTW89_REGD_MAX_COUNTRY_NUM ||
|
||||
test_bit(index, regulatory->block_6ghz_sp)) {
|
||||
rtw89_debug(rtwdev, RTW89_DBG_REGD,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user