@@ -530,8 +530,8 @@ static int rtw_debugfs_get_tx_pwr_tbl(struct seq_file *m, void *v)
u8 ch = hal->current_channel;
u8 regd = rtwdev->regd.txpwr_regd;
- seq_printf(m, "%-4s %-10s %-3s%6s %-4s %4s (%-4s %-4s)\n",
- "path", "rate", "pwr", "", "base", "", "byr", "lmt");
+ seq_printf(m, "%-4s %-10s %-3s%6s %-4s %4s (%-4s %-4s %-4s)\n",
+ "path", "rate", "pwr", "", "base", "", "byr", "lmt", "sar");
mutex_lock(&hal->tx_power_mutex);
for (path = RF_PATH_A; path <= RF_PATH_B; path++) {
@@ -553,13 +553,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)\n",
+ seq_printf(m, " %3u(0x%02x) %4u %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),
- 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);
}
}
@@ -386,6 +386,13 @@ enum rtw_wow_flags {
RTW_WOW_FLAG_MAX,
};
+enum rtw_sar_sources {
+ RTW_SAR_SOURCE_NONE,
+ RTW_SAR_SOURCE_VNDCMD,
+ RTW_SAR_SOURCE_ACPI_STATIC,
+ RTW_SAR_SOURCE_ACPI_DYNAMIC,
+};
+
/* the power index is represented by differences, which cck-1s & ht40-1s are
* the base values, so for 1s's differences, there are only ht20 & ofdm
*/
@@ -1513,6 +1520,10 @@ struct rtw_fw_state {
u16 h2c_version;
};
+struct rtw_sar {
+ enum rtw_sar_sources source;
+};
+
struct rtw_hal {
u32 rcr;
@@ -1558,6 +1569,10 @@ struct rtw_hal {
[RTW_CHANNEL_WIDTH_MAX]
[RTW_RATE_SECTION_MAX]
[RTW_MAX_CHANNEL_NUM_5G];
+ s8 tx_pwr_sar_2g[RTW_REGD_MAX][RTW_RF_PATH_MAX][RTW_RATE_SECTION_MAX]
+ [RTW_MAX_CHANNEL_NUM_2G];
+ s8 tx_pwr_sar_5g[RTW_REGD_MAX][RTW_RF_PATH_MAX][RTW_RATE_SECTION_MAX]
+ [RTW_MAX_CHANNEL_NUM_5G];
s8 tx_pwr_tbl[RTW_RF_PATH_MAX]
[DESC_RATE_MAX];
};
@@ -1630,6 +1645,8 @@ struct rtw_dev {
struct rtw_fw_state wow_fw;
struct rtw_wow_param wow;
+ struct rtw_sar sar;
+
/* hci related data, must be last */
u8 priv[0] __aligned(sizeof(void *));
};
@@ -1295,6 +1295,94 @@ static void rtw_phy_set_tx_power_limit(struct rtw_dev *rtwdev, u8 regd, u8 band,
}
}
+static void
+rtw_phy_set_tx_power_sar_by_chidx(struct rtw_dev *rtwdev, u8 regd, u8 rfpath,
+ u8 band, u8 rs, u8 ch_idx, s8 sar)
+{
+ struct rtw_hal *hal = &rtwdev->hal;
+ s8 base;
+ s8 ww_sar;
+ s8 s;
+
+ if (band == PHY_BAND_2G) {
+ base = hal->tx_pwr_by_rate_base_2g[rfpath][rs];
+ s = sar - base;
+ hal->tx_pwr_sar_2g[regd][rfpath][rs][ch_idx] = s;
+ if (regd == RTW_REGD_WW)
+ return;
+ ww_sar = hal->tx_pwr_sar_2g[RTW_REGD_WW][rfpath][rs][ch_idx];
+ ww_sar = min(ww_sar, s);
+ hal->tx_pwr_sar_2g[RTW_REGD_WW][rfpath][rs][ch_idx] = ww_sar;
+ } else {
+ base = hal->tx_pwr_by_rate_base_5g[rfpath][rs];
+ s = sar - base;
+ hal->tx_pwr_sar_5g[regd][rfpath][rs][ch_idx] = s;
+ if (regd == RTW_REGD_WW)
+ return;
+ ww_sar = hal->tx_pwr_sar_5g[RTW_REGD_WW][rfpath][rs][ch_idx];
+ ww_sar = min(ww_sar, s);
+ hal->tx_pwr_sar_5g[RTW_REGD_WW][rfpath][rs][ch_idx] = ww_sar;
+ }
+}
+
+static void
+rtw_phy_set_tx_power_sar_by_range(struct rtw_dev *rtwdev, u8 regd, u8 rfpath,
+ u8 band, u8 chidx_start, u8 chidx_end, u8 sar_q3)
+{
+ u8 rs;
+ u8 ch_idx;
+ s8 sar;
+
+ if (regd >= RTW_REGD_MAX || rfpath >= RTW_RF_PATH_MAX)
+ return;
+
+ sar = sar_q3 >> (3 - (int)rtwdev->chip->txgi_factor);
+
+ for (ch_idx = chidx_start; ch_idx <= chidx_end; ch_idx++)
+ for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++)
+ rtw_phy_set_tx_power_sar_by_chidx(rtwdev, regd, rfpath,
+ band, rs, ch_idx, sar);
+}
+
+void rtw_phy_set_tx_power_sar(struct rtw_dev *rtwdev, u8 regd, u8 rfpath,
+ u8 ch_start, u8 ch_end, u8 sar_q3)
+{
+ u8 band_start, band_end;
+ int chidx_start, chidx_end;
+
+ band_start = ch_start <= 14 ? PHY_BAND_2G : PHY_BAND_5G;
+ band_end = ch_end <= 14 ? PHY_BAND_2G : PHY_BAND_5G;
+
+ if (band_start == band_end) {
+ chidx_start = rtw_channel_to_idx(band_start, ch_start);
+ chidx_end = rtw_channel_to_idx(band_start, ch_end);
+ if (chidx_start < 0 || chidx_end < 0)
+ goto err;
+ rtw_phy_set_tx_power_sar_by_range(rtwdev, regd, rfpath, band_start,
+ chidx_start, chidx_end, sar_q3);
+ return;
+ }
+
+ chidx_start = rtw_channel_to_idx(PHY_BAND_2G, ch_start);
+ if (chidx_start < 0)
+ goto err;
+ rtw_phy_set_tx_power_sar_by_range(rtwdev, regd, rfpath, PHY_BAND_2G,
+ chidx_start, RTW_MAX_CHANNEL_NUM_2G - 1,
+ sar_q3);
+
+ chidx_end = rtw_channel_to_idx(PHY_BAND_5G, ch_end);
+ if (chidx_end < 0)
+ goto err;
+ rtw_phy_set_tx_power_sar_by_range(rtwdev, regd, rfpath, PHY_BAND_5G,
+ 0, chidx_end, sar_q3);
+
+ return;
+
+err:
+ rtw_warn(rtwdev, "SAR: invalid channel (start/end)=(%d/%d)\n",
+ ch_start, ch_end);
+}
+
/* cross-reference 5G power limits if values are not assigned */
static void
rtw_xref_5g_txpwr_lmt(struct rtw_dev *rtwdev, u8 regd,
@@ -1676,9 +1764,10 @@ static u8 rtw_phy_get_5g_tx_power_index(struct rtw_dev *rtwdev,
return tx_power;
}
-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)
+static void 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,
+ struct rtw_power_params *pwr_param)
{
struct rtw_hal *hal = &rtwdev->hal;
u8 *cch_by_bw = hal->cch_by_bw;
@@ -1687,9 +1776,10 @@ static s8 rtw_phy_get_tx_power_limit(struct rtw_dev *rtwdev, u8 band,
int ch_idx;
u8 cur_bw, cur_ch;
s8 cur_lmt;
+ s8 sar, sar_ww;
if (regd > RTW_REGD_WW)
- return power_limit;
+ goto err;
if (rate >= DESC_RATE1M && rate <= DESC_RATE11M)
rs = RTW_RATE_SECTION_CCK;
@@ -1729,44 +1819,68 @@ static s8 rtw_phy_get_tx_power_limit(struct rtw_dev *rtwdev, u8 band,
power_limit = min_t(s8, cur_lmt, power_limit);
}
- return power_limit;
+ ch_idx = rtw_channel_to_idx(band, channel);
+ if (ch_idx < 0)
+ goto err;
+
+ if (band == PHY_BAND_2G) {
+ sar = hal->tx_pwr_sar_2g[regd][rf_path][rs][ch_idx];
+ sar_ww = hal->tx_pwr_sar_2g[RTW_REGD_WW][rf_path][rs][ch_idx];
+ } else {
+ sar = hal->tx_pwr_sar_5g[regd][rf_path][rs][ch_idx];
+ sar_ww = hal->tx_pwr_sar_5g[RTW_REGD_WW][rf_path][rs][ch_idx];
+ }
+ if (sar >= rtwdev->chip->max_power_index)
+ sar = sar_ww;
+
+ pwr_param->pwr_sar = sar;
+ pwr_param->pwr_limit = power_limit;
+ return;
err:
WARN(1, "invalid arguments, band=%d, bw=%d, path=%d, rate=%d, ch=%d\n",
band, bw, rf_path, rate, channel);
- return (s8)rtwdev->chip->max_power_index;
+ pwr_param->pwr_sar = (s8)rtwdev->chip->max_power_index;
+ pwr_param->pwr_limit = (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)
+static void rtw_phy_get_tx_power_base(struct rtw_dev *rtwdev, u8 band,
+ enum rtw_bandwidth bw, u8 path, u8 rate,
+ u8 ch, struct rtw_power_params *pwr_param)
{
struct rtw_hal *hal = &rtwdev->hal;
struct rtw_txpwr_idx *pwr_idx;
- u8 group, band;
- u8 *base = &pwr_param->pwr_base;
- s8 *offset = &pwr_param->pwr_offset;
- s8 *limit = &pwr_param->pwr_limit;
+ u8 group;
+ u8 base;
+ s8 offset;
pwr_idx = &rtwdev->efuse.txpwr_idx_table[path];
group = rtw_get_channel_group(ch);
/* base power index for 2.4G/5G */
- if (IS_CH_2G_BAND(ch)) {
- band = PHY_BAND_2G;
- *base = rtw_phy_get_2g_tx_power_index(rtwdev,
- &pwr_idx->pwr_idx_2g,
- bw, rate, group);
- *offset = hal->tx_pwr_by_rate_offset_2g[path][rate];
+ if (band == PHY_BAND_2G) {
+ base = rtw_phy_get_2g_tx_power_index(rtwdev,
+ &pwr_idx->pwr_idx_2g,
+ bw, rate, group);
+ offset = hal->tx_pwr_by_rate_offset_2g[path][rate];
} else {
- band = PHY_BAND_5G;
- *base = rtw_phy_get_5g_tx_power_index(rtwdev,
- &pwr_idx->pwr_idx_5g,
- bw, rate, group);
- *offset = hal->tx_pwr_by_rate_offset_5g[path][rate];
+ base = rtw_phy_get_5g_tx_power_index(rtwdev,
+ &pwr_idx->pwr_idx_5g,
+ bw, rate, group);
+ offset = hal->tx_pwr_by_rate_offset_5g[path][rate];
}
- *limit = rtw_phy_get_tx_power_limit(rtwdev, band, bw, path,
- rate, ch, regd);
+ pwr_param->pwr_base = base;
+ pwr_param->pwr_offset = offset;
+}
+
+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)
+{
+ u8 band = IS_CH_2G_BAND(ch) ? PHY_BAND_2G : PHY_BAND_5G;
+
+ rtw_phy_get_tx_power_base(rtwdev, band, bw, path, rate, ch, pwr_param);
+ rtw_phy_get_tx_power_limit(rtwdev, band, bw, path, rate, ch, regd, pwr_param);
}
u8
@@ -1781,7 +1895,8 @@ 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);
@@ -1970,6 +2085,12 @@ void rtw_phy_init_tx_power(struct rtw_dev *rtwdev)
for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++)
rtw_phy_init_tx_power_limit(rtwdev, regd, bw,
rs);
+
+ /* init tx power sar */
+ memset(hal->tx_pwr_sar_2g, rtwdev->chip->max_power_index,
+ sizeof(hal->tx_pwr_sar_2g));
+ memset(hal->tx_pwr_sar_5g, rtwdev->chip->max_power_index,
+ sizeof(hal->tx_pwr_sar_5g));
}
void rtw_phy_config_swing_table(struct rtw_dev *rtwdev,
@@ -139,12 +139,15 @@ struct rtw_power_params {
u8 pwr_base;
s8 pwr_offset;
s8 pwr_limit;
+ s8 pwr_sar;
};
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);
+void rtw_phy_set_tx_power_sar(struct rtw_dev *rtwdev, u8 regd, u8 rfpath,
+ u8 ch_start, u8 ch_end, u8 sar_q3);
enum rtw_phy_cck_pd_lv {
CCK_PD_LV0,