linux/drivers/net/wireless/realtek/rtw88/sar.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
   2/* Copyright(c) 2018-2021  Realtek Corporation
   3 */
   4
   5#include "sar.h"
   6#include "phy.h"
   7#include "debug.h"
   8
   9s8 rtw_query_sar(struct rtw_dev *rtwdev, const struct rtw_sar_arg *arg)
  10{
  11        const struct rtw_hal *hal = &rtwdev->hal;
  12        const struct rtw_sar *sar = &hal->sar;
  13
  14        switch (sar->src) {
  15        default:
  16                rtw_warn(rtwdev, "unknown SAR source: %d\n", sar->src);
  17                fallthrough;
  18        case RTW_SAR_SOURCE_NONE:
  19                return (s8)rtwdev->chip->max_power_index;
  20        case RTW_SAR_SOURCE_COMMON:
  21                return sar->cfg[arg->path][arg->rs].common[arg->sar_band];
  22        }
  23}
  24
  25static int rtw_apply_sar(struct rtw_dev *rtwdev, const struct rtw_sar *new)
  26{
  27        struct rtw_hal *hal = &rtwdev->hal;
  28        struct rtw_sar *sar = &hal->sar;
  29
  30        if (sar->src != RTW_SAR_SOURCE_NONE && new->src != sar->src) {
  31                rtw_warn(rtwdev, "SAR source: %d is in use\n", sar->src);
  32                return -EBUSY;
  33        }
  34
  35        *sar = *new;
  36        rtw_phy_set_tx_power_level(rtwdev, hal->current_channel);
  37
  38        return 0;
  39}
  40
  41static s8 rtw_sar_to_phy(struct rtw_dev *rtwdev, u8 fct, s32 sar,
  42                         const struct rtw_sar_arg *arg)
  43{
  44        struct rtw_hal *hal = &rtwdev->hal;
  45        u8 txgi = rtwdev->chip->txgi_factor;
  46        u8 max = rtwdev->chip->max_power_index;
  47        s32 tmp;
  48        s8 base;
  49
  50        tmp = fct > txgi ? sar >> (fct - txgi) : sar << (txgi - fct);
  51        base = arg->sar_band == RTW_SAR_BAND_0 ?
  52               hal->tx_pwr_by_rate_base_2g[arg->path][arg->rs] :
  53               hal->tx_pwr_by_rate_base_5g[arg->path][arg->rs];
  54
  55        return (s8)clamp_t(s32, tmp, -max - 1, max) - base;
  56}
  57
  58static const struct cfg80211_sar_freq_ranges rtw_common_sar_freq_ranges[] = {
  59        [RTW_SAR_BAND_0] = { .start_freq = 2412, .end_freq = 2484, },
  60        [RTW_SAR_BAND_1] = { .start_freq = 5180, .end_freq = 5320, },
  61        [RTW_SAR_BAND_3] = { .start_freq = 5500, .end_freq = 5720, },
  62        [RTW_SAR_BAND_4] = { .start_freq = 5745, .end_freq = 5825, },
  63};
  64
  65static_assert(ARRAY_SIZE(rtw_common_sar_freq_ranges) == RTW_SAR_BAND_NR);
  66
  67const struct cfg80211_sar_capa rtw_sar_capa = {
  68        .type = NL80211_SAR_TYPE_POWER,
  69        .num_freq_ranges = RTW_SAR_BAND_NR,
  70        .freq_ranges = rtw_common_sar_freq_ranges,
  71};
  72
  73int rtw_set_sar_specs(struct rtw_dev *rtwdev,
  74                      const struct cfg80211_sar_specs *sar)
  75{
  76        struct rtw_sar_arg arg = {0};
  77        struct rtw_sar new = {0};
  78        u32 idx, i, j, k;
  79        s32 power;
  80        s8 val;
  81
  82        if (sar->type != NL80211_SAR_TYPE_POWER)
  83                return -EINVAL;
  84
  85        memset(&new, rtwdev->chip->max_power_index, sizeof(new));
  86        new.src = RTW_SAR_SOURCE_COMMON;
  87
  88        for (i = 0; i < sar->num_sub_specs; i++) {
  89                idx = sar->sub_specs[i].freq_range_index;
  90                if (idx >= RTW_SAR_BAND_NR)
  91                        return -EINVAL;
  92
  93                power = sar->sub_specs[i].power;
  94                rtw_dbg(rtwdev, RTW_DBG_REGD, "On freq %u to %u, set SAR %d in 1/%lu dBm\n",
  95                        rtw_common_sar_freq_ranges[idx].start_freq,
  96                        rtw_common_sar_freq_ranges[idx].end_freq,
  97                        power, BIT(RTW_COMMON_SAR_FCT));
  98
  99                for (j = 0; j < RTW_RF_PATH_MAX; j++) {
 100                        for (k = 0; k < RTW_RATE_SECTION_MAX; k++) {
 101                                arg = (struct rtw_sar_arg){
 102                                        .sar_band = idx,
 103                                        .path = j,
 104                                        .rs = k,
 105                                };
 106                                val = rtw_sar_to_phy(rtwdev, RTW_COMMON_SAR_FCT,
 107                                                     power, &arg);
 108                                new.cfg[j][k].common[idx] = val;
 109                        }
 110                }
 111        }
 112
 113        return rtw_apply_sar(rtwdev, &new);
 114}
 115