linux/drivers/net/wireless/b43/ppr.c
<<
>>
Prefs
   1/*
   2 * Broadcom B43 wireless driver
   3 * PPR (Power Per Rate) management
   4 *
   5 * Copyright (c) 2014 Rafał Miłecki <zajec5@gmail.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License as published by
   9 * the Free Software Foundation; either version 2 of the License, or
  10 * (at your option) any later version.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 */
  17
  18#include "ppr.h"
  19#include "b43.h"
  20
  21#define ppr_for_each_entry(ppr, i, entry)                               \
  22        for (i = 0, entry = &(ppr)->__all_rates[i];                     \
  23             i < B43_PPR_RATES_NUM;                                     \
  24             i++, entry++)
  25
  26void b43_ppr_clear(struct b43_wldev *dev, struct b43_ppr *ppr)
  27{
  28        memset(ppr, 0, sizeof(*ppr));
  29
  30        /* Compile-time PPR check */
  31        BUILD_BUG_ON(sizeof(struct b43_ppr) != B43_PPR_RATES_NUM * sizeof(u8));
  32}
  33
  34void b43_ppr_add(struct b43_wldev *dev, struct b43_ppr *ppr, int diff)
  35{
  36        int i;
  37        u8 *rate;
  38
  39        ppr_for_each_entry(ppr, i, rate) {
  40                *rate = clamp_val(*rate + diff, 0, 127);
  41        }
  42}
  43
  44void b43_ppr_apply_max(struct b43_wldev *dev, struct b43_ppr *ppr, u8 max)
  45{
  46        int i;
  47        u8 *rate;
  48
  49        ppr_for_each_entry(ppr, i, rate) {
  50                *rate = min(*rate, max);
  51        }
  52}
  53
  54void b43_ppr_apply_min(struct b43_wldev *dev, struct b43_ppr *ppr, u8 min)
  55{
  56        int i;
  57        u8 *rate;
  58
  59        ppr_for_each_entry(ppr, i, rate) {
  60                *rate = max(*rate, min);
  61        }
  62}
  63
  64u8 b43_ppr_get_max(struct b43_wldev *dev, struct b43_ppr *ppr)
  65{
  66        u8 res = 0;
  67        int i;
  68        u8 *rate;
  69
  70        ppr_for_each_entry(ppr, i, rate) {
  71                res = max(*rate, res);
  72        }
  73
  74        return res;
  75}
  76
  77bool b43_ppr_load_max_from_sprom(struct b43_wldev *dev, struct b43_ppr *ppr,
  78                                 enum b43_band band)
  79{
  80        struct b43_ppr_rates *rates = &ppr->rates;
  81        struct ssb_sprom *sprom = dev->dev->bus_sprom;
  82        struct b43_phy *phy = &dev->phy;
  83        u8 maxpwr, off;
  84        u32 sprom_ofdm_po;
  85        u16 *sprom_mcs_po;
  86        u8 extra_cdd_po, extra_stbc_po;
  87        int i;
  88
  89        switch (band) {
  90        case B43_BAND_2G:
  91                maxpwr = min(sprom->core_pwr_info[0].maxpwr_2g,
  92                             sprom->core_pwr_info[1].maxpwr_2g);
  93                sprom_ofdm_po = sprom->ofdm2gpo;
  94                sprom_mcs_po = sprom->mcs2gpo;
  95                extra_cdd_po = (sprom->cddpo >> 0) & 0xf;
  96                extra_stbc_po = (sprom->stbcpo >> 0) & 0xf;
  97                break;
  98        case B43_BAND_5G_LO:
  99                maxpwr = min(sprom->core_pwr_info[0].maxpwr_5gl,
 100                             sprom->core_pwr_info[1].maxpwr_5gl);
 101                sprom_ofdm_po = sprom->ofdm5glpo;
 102                sprom_mcs_po = sprom->mcs5glpo;
 103                extra_cdd_po = (sprom->cddpo >> 8) & 0xf;
 104                extra_stbc_po = (sprom->stbcpo >> 8) & 0xf;
 105                break;
 106        case B43_BAND_5G_MI:
 107                maxpwr = min(sprom->core_pwr_info[0].maxpwr_5g,
 108                             sprom->core_pwr_info[1].maxpwr_5g);
 109                sprom_ofdm_po = sprom->ofdm5gpo;
 110                sprom_mcs_po = sprom->mcs5gpo;
 111                extra_cdd_po = (sprom->cddpo >> 4) & 0xf;
 112                extra_stbc_po = (sprom->stbcpo >> 4) & 0xf;
 113                break;
 114        case B43_BAND_5G_HI:
 115                maxpwr = min(sprom->core_pwr_info[0].maxpwr_5gh,
 116                             sprom->core_pwr_info[1].maxpwr_5gh);
 117                sprom_ofdm_po = sprom->ofdm5ghpo;
 118                sprom_mcs_po = sprom->mcs5ghpo;
 119                extra_cdd_po = (sprom->cddpo >> 12) & 0xf;
 120                extra_stbc_po = (sprom->stbcpo >> 12) & 0xf;
 121                break;
 122        default:
 123                WARN_ON_ONCE(1);
 124                return false;
 125        }
 126
 127        if (band == B43_BAND_2G) {
 128                for (i = 0; i < 4; i++) {
 129                        off = ((sprom->cck2gpo >> (i * 4)) & 0xf) * 2;
 130                        rates->cck[i] = maxpwr - off;
 131                }
 132        }
 133
 134        /* OFDM */
 135        for (i = 0; i < 8; i++) {
 136                off = ((sprom_ofdm_po >> (i * 4)) & 0xf) * 2;
 137                rates->ofdm[i] = maxpwr - off;
 138        }
 139
 140        /* MCS 20 SISO */
 141        rates->mcs_20[0] = rates->ofdm[0];
 142        rates->mcs_20[1] = rates->ofdm[2];
 143        rates->mcs_20[2] = rates->ofdm[3];
 144        rates->mcs_20[3] = rates->ofdm[4];
 145        rates->mcs_20[4] = rates->ofdm[5];
 146        rates->mcs_20[5] = rates->ofdm[6];
 147        rates->mcs_20[6] = rates->ofdm[7];
 148        rates->mcs_20[7] = rates->ofdm[7];
 149
 150        /* MCS 20 CDD */
 151        for (i = 0; i < 4; i++) {
 152                off = ((sprom_mcs_po[0] >> (i * 4)) & 0xf) * 2;
 153                rates->mcs_20_cdd[i] = maxpwr - off;
 154                if (phy->type == B43_PHYTYPE_N && phy->rev >= 3)
 155                        rates->mcs_20_cdd[i] -= extra_cdd_po;
 156        }
 157        for (i = 0; i < 4; i++) {
 158                off = ((sprom_mcs_po[1] >> (i * 4)) & 0xf) * 2;
 159                rates->mcs_20_cdd[4 + i] = maxpwr - off;
 160                if (phy->type == B43_PHYTYPE_N && phy->rev >= 3)
 161                        rates->mcs_20_cdd[4 + i] -= extra_cdd_po;
 162        }
 163
 164        /* OFDM 20 CDD */
 165        rates->ofdm_20_cdd[0] = rates->mcs_20_cdd[0];
 166        rates->ofdm_20_cdd[1] = rates->mcs_20_cdd[0];
 167        rates->ofdm_20_cdd[2] = rates->mcs_20_cdd[1];
 168        rates->ofdm_20_cdd[3] = rates->mcs_20_cdd[2];
 169        rates->ofdm_20_cdd[4] = rates->mcs_20_cdd[3];
 170        rates->ofdm_20_cdd[5] = rates->mcs_20_cdd[4];
 171        rates->ofdm_20_cdd[6] = rates->mcs_20_cdd[5];
 172        rates->ofdm_20_cdd[7] = rates->mcs_20_cdd[6];
 173
 174        /* MCS 20 STBC */
 175        for (i = 0; i < 4; i++) {
 176                off = ((sprom_mcs_po[0] >> (i * 4)) & 0xf) * 2;
 177                rates->mcs_20_stbc[i] = maxpwr - off;
 178                if (phy->type == B43_PHYTYPE_N && phy->rev >= 3)
 179                        rates->mcs_20_stbc[i] -= extra_stbc_po;
 180        }
 181        for (i = 0; i < 4; i++) {
 182                off = ((sprom_mcs_po[1] >> (i * 4)) & 0xf) * 2;
 183                rates->mcs_20_stbc[4 + i] = maxpwr - off;
 184                if (phy->type == B43_PHYTYPE_N && phy->rev >= 3)
 185                        rates->mcs_20_stbc[4 + i] -= extra_stbc_po;
 186        }
 187
 188        /* MCS 20 SDM */
 189        for (i = 0; i < 4; i++) {
 190                off = ((sprom_mcs_po[2] >> (i * 4)) & 0xf) * 2;
 191                rates->mcs_20_sdm[i] = maxpwr - off;
 192        }
 193        for (i = 0; i < 4; i++) {
 194                off = ((sprom_mcs_po[3] >> (i * 4)) & 0xf) * 2;
 195                rates->mcs_20_sdm[4 + i] = maxpwr - off;
 196        }
 197
 198        return true;
 199}
 200