OSDN Git Service

rtw88: support SAR via kernel common API
authorZong-Zhe Yang <kevin_yang@realtek.com>
Mon, 20 Dec 2021 09:36:56 +0000 (17:36 +0800)
committerKalle Valo <kvalo@kernel.org>
Tue, 21 Dec 2021 18:22:38 +0000 (20:22 +0200)
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
drivers/net/wireless/realtek/rtw88/Makefile
drivers/net/wireless/realtek/rtw88/debug.c
drivers/net/wireless/realtek/rtw88/mac80211.c
drivers/net/wireless/realtek/rtw88/main.c
drivers/net/wireless/realtek/rtw88/main.h
drivers/net/wireless/realtek/rtw88/phy.c
drivers/net/wireless/realtek/rtw88/phy.h
drivers/net/wireless/realtek/rtw88/sar.c [new file with mode: 0644]
drivers/net/wireless/realtek/rtw88/sar.h [new file with mode: 0644]

index 73d6807..834c66e 100644 (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
index b863c74..e429428 100644 (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);
                }
        }
index 5c01c40..ae7d97d 100644 (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,
index b569a67..3825211 100644 (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");
index ff7570c..dc1cd9b 100644 (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 {
index bfddfcb..e505d17 100644 (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);
index 02d1ec4..b6c5ae6 100644 (file)
@@ -148,6 +148,7 @@ struct rtw_power_params {
        s8 pwr_offset;
        s8 pwr_limit;
        s8 pwr_remnant;
+       s8 pwr_sar;
 };
 
 void
diff --git a/drivers/net/wireless/realtek/rtw88/sar.c b/drivers/net/wireless/realtek/rtw88/sar.c
new file mode 100644 (file)
index 0000000..3383726
--- /dev/null
@@ -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);
+}
diff --git a/drivers/net/wireless/realtek/rtw88/sar.h b/drivers/net/wireless/realtek/rtw88/sar.h
new file mode 100644 (file)
index 0000000..e01e7bb
--- /dev/null
@@ -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);