iPXE
Defines | Functions
ath5k_phy.c File Reference
#include <unistd.h>
#include <stdlib.h>
#include "ath5k.h"
#include "reg.h"
#include "base.h"
#include "rfbuffer.h"
#include "rfgain.h"

Go to the source code of this file.

Defines

#define _ATH5K_PHY

Functions

 FILE_LICENCE (MIT)
static int min (int x, int y)
static int max (int x, int y)
static unsigned int ath5k_hw_rfb_op (struct ath5k_hw *ah, const struct ath5k_rf_reg *rf_regs, u32 val, u8 reg_id, int set)
int ath5k_hw_rfgain_opt_init (struct ath5k_hw *ah)
static void ath5k_hw_request_rfgain_probe (struct ath5k_hw *ah)
static u32 ath5k_hw_rf_gainf_corr (struct ath5k_hw *ah)
static int ath5k_hw_rf_check_gainf_readback (struct ath5k_hw *ah)
static s8 ath5k_hw_rf_gainf_adjust (struct ath5k_hw *ah)
enum ath5k_rfgain ath5k_hw_gainf_calibrate (struct ath5k_hw *ah)
int ath5k_hw_rfgain_init (struct ath5k_hw *ah, unsigned int freq)
int ath5k_hw_rfregs_init (struct ath5k_hw *ah, struct net80211_channel *channel, unsigned int mode)
int ath5k_channel_ok (struct ath5k_hw *ah, u16 freq, unsigned int flags)
static u32 ath5k_hw_rf5110_chan2athchan (struct net80211_channel *channel)
static int ath5k_hw_rf5110_channel (struct ath5k_hw *ah, struct net80211_channel *channel)
static int ath5k_hw_rf5111_chan2athchan (unsigned int ieee, struct ath5k_athchan_2ghz *athchan)
static int ath5k_hw_rf5111_channel (struct ath5k_hw *ah, struct net80211_channel *channel)
static int ath5k_hw_rf5112_channel (struct ath5k_hw *ah, struct net80211_channel *channel)
static int ath5k_hw_rf2425_channel (struct ath5k_hw *ah, struct net80211_channel *channel)
int ath5k_hw_channel (struct ath5k_hw *ah, struct net80211_channel *channel)
int ath5k_hw_noise_floor_calibration (struct ath5k_hw *ah, short freq)
 ath5k_hw_noise_floor_calibration - perform PHY noise floor calibration
static int ath5k_hw_rf5110_calibrate (struct ath5k_hw *ah, struct net80211_channel *channel)
static int ath5k_hw_rf511x_calibrate (struct ath5k_hw *ah, struct net80211_channel *channel)
int ath5k_hw_phy_calibrate (struct ath5k_hw *ah, struct net80211_channel *channel)
int ath5k_hw_phy_disable (struct ath5k_hw *ah)
u16 ath5k_hw_radio_revision (struct ath5k_hw *ah, unsigned int chan)
void ath5k_hw_set_def_antenna (struct ath5k_hw *ah, unsigned int ant)
unsigned int ath5k_hw_get_def_antenna (struct ath5k_hw *ah)
static s16 ath5k_get_interpolated_value (s16 target, s16 x_left, s16 x_right, s16 y_left, s16 y_right)
static s16 ath5k_get_linear_pcdac_min (const u8 *stepL, const u8 *stepR, const s16 *pwrL, const s16 *pwrR)
static void ath5k_create_power_curve (s16 pmin, s16 pmax, const s16 *pwr, const u8 *vpd, u8 num_points, u8 *vpd_table, u8 type)
static void ath5k_get_chan_pcal_surrounding_piers (struct ath5k_hw *ah, struct net80211_channel *channel, struct ath5k_chan_pcal_info **pcinfo_l, struct ath5k_chan_pcal_info **pcinfo_r)
static void ath5k_get_rate_pcal_data (struct ath5k_hw *ah, struct net80211_channel *channel, struct ath5k_rate_pcal_info *rates)
static void ath5k_get_max_ctl_power (struct ath5k_hw *ah, struct net80211_channel *channel)
static void ath5k_fill_pwr_to_pcdac_table (struct ath5k_hw *ah, s16 *table_min, s16 *table_max)
static void ath5k_combine_linear_pcdac_curves (struct ath5k_hw *ah, s16 *table_min, s16 *table_max, u8 pdcurves)
static void ath5k_setup_pcdac_table (struct ath5k_hw *ah)
static void ath5k_combine_pwr_to_pdadc_curves (struct ath5k_hw *ah, s16 *pwr_min, s16 *pwr_max, u8 pdcurves)
static void ath5k_setup_pwr_to_pdadc_table (struct ath5k_hw *ah, u8 pdcurves, u8 *pdg_to_idx)
static int ath5k_setup_channel_powertable (struct ath5k_hw *ah, struct net80211_channel *channel, u8 ee_mode, u8 type)
static void ath5k_setup_rate_powertable (struct ath5k_hw *ah, u16 max_pwr, struct ath5k_rate_pcal_info *rate_info, u8 ee_mode)
int ath5k_hw_txpower (struct ath5k_hw *ah, struct net80211_channel *channel, u8 ee_mode, u8 txpower)
int ath5k_hw_set_txpower_limit (struct ath5k_hw *ah, u8 mode, u8 txpower)

Define Documentation

#define _ATH5K_PHY

Definition at line 27 of file ath5k_phy.c.


Function Documentation

FILE_LICENCE ( MIT  )
static int min ( int  x,
int  y 
) [inline, static]

Definition at line 38 of file ath5k_phy.c.

{
        return (x < y) ? x : y;
}
static int max ( int  x,
int  y 
) [inline, static]

Definition at line 43 of file ath5k_phy.c.

{
        return (x > y) ? x : y;
}
static unsigned int ath5k_hw_rfb_op ( struct ath5k_hw ah,
const struct ath5k_rf_reg rf_regs,
u32  val,
u8  reg_id,
int  set 
) [static]

Definition at line 51 of file ath5k_phy.c.

References ath5k_hw::ah_offset, ath5k_hw::ah_rf_banks, ath5k_hw::ah_rf_regs_count, ath5k_hw_bitswap(), ath5k_rf_reg::bank, ath5k_rfb_field::col, data, DBG, entry, ath5k_rf_reg::field, index, ath5k_rfb_field::len, NULL, offset, and ath5k_rfb_field::pos.

Referenced by ath5k_hw_rf_check_gainf_readback(), ath5k_hw_rf_gainf_corr(), and ath5k_hw_rfregs_init().

{
        const struct ath5k_rf_reg *rfreg = NULL;
        u8 offset, bank, num_bits, col, position;
        u16 entry;
        u32 mask, data, last_bit, bits_shifted, first_bit;
        u32 *rfb;
        s32 bits_left;
        unsigned i;

        data = 0;
        rfb = ah->ah_rf_banks;

        for (i = 0; i < ah->ah_rf_regs_count; i++) {
                if (rf_regs[i].index == reg_id) {
                        rfreg = &rf_regs[i];
                        break;
                }
        }

        if (rfb == NULL || rfreg == NULL) {
                DBG("ath5k: RF register not found!\n");
                /* should not happen */
                return 0;
        }

        bank = rfreg->bank;
        num_bits = rfreg->field.len;
        first_bit = rfreg->field.pos;
        col = rfreg->field.col;

        /* first_bit is an offset from bank's
         * start. Since we have all banks on
         * the same array, we use this offset
         * to mark each bank's start */
        offset = ah->ah_offset[bank];

        /* Boundary check */
        if (!(col <= 3 && num_bits <= 32 && first_bit + num_bits <= 319)) {
                DBG("ath5k: RF invalid values at offset %d\n", offset);
                return 0;
        }

        entry = ((first_bit - 1) / 8) + offset;
        position = (first_bit - 1) % 8;

        if (set)
                data = ath5k_hw_bitswap(val, num_bits);

        for (bits_shifted = 0, bits_left = num_bits; bits_left > 0;
        position = 0, entry++) {

                last_bit = (position + bits_left > 8) ? 8 :
                                        position + bits_left;

                mask = (((1 << last_bit) - 1) ^ ((1 << position) - 1)) <<
                                                                (col * 8);

                if (set) {
                        rfb[entry] &= ~mask;
                        rfb[entry] |= ((data << position) << (col * 8)) & mask;
                        data >>= (8 - position);
                } else {
                        data |= (((rfb[entry] & mask) >> (col * 8)) >> position)
                                << bits_shifted;
                        bits_shifted += last_bit - position;
                }

                bits_left -= 8 - position;
        }

        data = set ? 1 : ath5k_hw_bitswap(data, num_bits);

        return data;
}
int ath5k_hw_rfgain_opt_init ( struct ath5k_hw ah)
static void ath5k_hw_request_rfgain_probe ( struct ath5k_hw ah) [static]
static u32 ath5k_hw_rf_gainf_corr ( struct ath5k_hw ah) [static]

Definition at line 214 of file ath5k_phy.c.

References ath5k_hw::ah_gain, ath5k_hw::ah_radio, ath5k_hw::ah_radio_5ghz_revision, ath5k_hw::ah_rf_banks, ath5k_hw::ah_rf_regs_count, AR5K_RF5112, AR5K_RF_MIXGAIN_STEP, AR5K_RF_MIXVGA_OVR, AR5K_SREV_RAD_5112A, ARRAY_SIZE, ath5k_hw_rfb_op(), ath5k_gain::g_f_corr, ath5k_gain::g_step_idx, ath5k_gain_opt::go_step, ath5k_gain_opt_step::gos_param, NULL, rf_regs_5112a, rfgain_opt_5112, and step().

Referenced by ath5k_hw_gainf_calibrate().

{
        u32 mix, step;
        const struct ath5k_gain_opt *go;
        const struct ath5k_gain_opt_step *g_step;
        const struct ath5k_rf_reg *rf_regs;

        /* Only RF5112 Rev. 2 supports it */
        if ((ah->ah_radio != AR5K_RF5112) ||
        (ah->ah_radio_5ghz_revision <= AR5K_SREV_RAD_5112A))
                return 0;

        go = &rfgain_opt_5112;
        rf_regs = rf_regs_5112a;
        ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5112a);

        g_step = &go->go_step[ah->ah_gain.g_step_idx];

        if (ah->ah_rf_banks == NULL)
                return 0;

        ah->ah_gain.g_f_corr = 0;

        /* No VGA (Variable Gain Amplifier) override, skip */
        if (ath5k_hw_rfb_op(ah, rf_regs, 0, AR5K_RF_MIXVGA_OVR, 0) != 1)
                return 0;

        /* Mix gain stepping */
        step = ath5k_hw_rfb_op(ah, rf_regs, 0, AR5K_RF_MIXGAIN_STEP, 0);

        /* Mix gain override */
        mix = g_step->gos_param[0];

        switch (mix) {
        case 3:
                ah->ah_gain.g_f_corr = step * 2;
                break;
        case 2:
                ah->ah_gain.g_f_corr = (step - 5) * 2;
                break;
        case 1:
                ah->ah_gain.g_f_corr = step;
                break;
        default:
                ah->ah_gain.g_f_corr = 0;
                break;
        }

        return ah->ah_gain.g_f_corr;
}
static int ath5k_hw_rf_check_gainf_readback ( struct ath5k_hw ah) [static]

Definition at line 269 of file ath5k_phy.c.

References ath5k_hw::ah_gain, ath5k_hw::ah_radio, ath5k_hw::ah_rf_banks, ath5k_hw::ah_rf_regs_count, AR5K_GAIN_DYN_ADJUST_HI_MARGIN, AR5K_GAIN_DYN_ADJUST_LO_MARGIN, AR5K_RF5111, AR5K_RF_MIXVGA_OVR, AR5K_RF_RFGAIN_STEP, ARRAY_SIZE, ath5k_hw_rfb_op(), ath5k_gain::g_current, ath5k_gain::g_high, ath5k_gain::g_low, NULL, rf_regs_5111, rf_regs_5112, and step().

Referenced by ath5k_hw_gainf_calibrate().

{
        const struct ath5k_rf_reg *rf_regs;
        u32 step, mix_ovr, level[4];

        if (ah->ah_rf_banks == NULL)
                return 0;

        if (ah->ah_radio == AR5K_RF5111) {

                rf_regs = rf_regs_5111;
                ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5111);

                step = ath5k_hw_rfb_op(ah, rf_regs, 0, AR5K_RF_RFGAIN_STEP,
                        0);

                level[0] = 0;
                level[1] = (step == 63) ? 50 : step + 4;
                level[2] = (step != 63) ? 64 : level[0];
                level[3] = level[2] + 50 ;

                ah->ah_gain.g_high = level[3] -
                        (step == 63 ? AR5K_GAIN_DYN_ADJUST_HI_MARGIN : -5);
                ah->ah_gain.g_low = level[0] +
                        (step == 63 ? AR5K_GAIN_DYN_ADJUST_LO_MARGIN : 0);
        } else {

                rf_regs = rf_regs_5112;
                ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5112);

                mix_ovr = ath5k_hw_rfb_op(ah, rf_regs, 0, AR5K_RF_MIXVGA_OVR,
                        0);

                level[0] = level[2] = 0;

                if (mix_ovr == 1) {
                        level[1] = level[3] = 83;
                } else {
                        level[1] = level[3] = 107;
                        ah->ah_gain.g_high = 55;
                }
        }

        return (ah->ah_gain.g_current >= level[0] &&
                        ah->ah_gain.g_current <= level[1]) ||
                (ah->ah_gain.g_current >= level[2] &&
                        ah->ah_gain.g_current <= level[3]);
}
static s8 ath5k_hw_rf_gainf_adjust ( struct ath5k_hw ah) [static]

Definition at line 320 of file ath5k_phy.c.

References ath5k_hw::ah_gain, ath5k_hw::ah_radio, AR5K_RF5111, AR5K_RF5112, DBG2, done, ath5k_gain::g_current, ath5k_gain::g_high, ath5k_gain::g_low, ath5k_gain::g_step_idx, ath5k_gain::g_target, ath5k_gain_opt::go_step, ath5k_gain_opt::go_steps_count, ath5k_gain_opt_step::gos_gain, ret, rfgain_opt_5111, and rfgain_opt_5112.

Referenced by ath5k_hw_gainf_calibrate().

{
        const struct ath5k_gain_opt *go;
        const struct ath5k_gain_opt_step *g_step;
        int ret = 0;

        switch (ah->ah_radio) {
        case AR5K_RF5111:
                go = &rfgain_opt_5111;
                break;
        case AR5K_RF5112:
                go = &rfgain_opt_5112;
                break;
        default:
                return 0;
        }

        g_step = &go->go_step[ah->ah_gain.g_step_idx];

        if (ah->ah_gain.g_current >= ah->ah_gain.g_high) {

                /* Reached maximum */
                if (ah->ah_gain.g_step_idx == 0)
                        return -1;

                for (ah->ah_gain.g_target = ah->ah_gain.g_current;
                                ah->ah_gain.g_target >=  ah->ah_gain.g_high &&
                                ah->ah_gain.g_step_idx > 0;
                                g_step = &go->go_step[ah->ah_gain.g_step_idx])
                        ah->ah_gain.g_target -= 2 *
                            (go->go_step[--(ah->ah_gain.g_step_idx)].gos_gain -
                            g_step->gos_gain);

                ret = 1;
                goto done;
        }

        if (ah->ah_gain.g_current <= ah->ah_gain.g_low) {

                /* Reached minimum */
                if (ah->ah_gain.g_step_idx == (go->go_steps_count - 1))
                        return -2;

                for (ah->ah_gain.g_target = ah->ah_gain.g_current;
                                ah->ah_gain.g_target <= ah->ah_gain.g_low &&
                                ah->ah_gain.g_step_idx < go->go_steps_count-1;
                                g_step = &go->go_step[ah->ah_gain.g_step_idx])
                        ah->ah_gain.g_target -= 2 *
                            (go->go_step[++ah->ah_gain.g_step_idx].gos_gain -
                            g_step->gos_gain);

                ret = 2;
                goto done;
        }

done:
        DBG2("ath5k RF adjust: ret %d, gain step %d, current gain %d, "
             "target gain %d\n", ret, ah->ah_gain.g_step_idx,
             ah->ah_gain.g_current, ah->ah_gain.g_target);

        return ret;
}

Definition at line 389 of file ath5k_phy.c.

References ath5k_hw::ah_capabilities, ath5k_hw::ah_gain, ath5k_hw::ah_radio_5ghz_revision, ath5k_hw::ah_rf_banks, AR5K_GAIN_CCK_PROBE_CORR, AR5K_GAIN_CHECK_ADJUST, AR5K_PHY_PAPD_PROBE, AR5K_PHY_PAPD_PROBE_GAINF_S, AR5K_PHY_PAPD_PROBE_TX_NEXT, AR5K_PHY_PAPD_PROBE_TYPE, AR5K_PHY_PAPD_PROBE_TYPE_CCK, AR5K_REG_MS, AR5K_RFGAIN_ACTIVE, AR5K_RFGAIN_INACTIVE, AR5K_RFGAIN_NEED_CHANGE, AR5K_RFGAIN_READ_REQUESTED, AR5K_SREV_RAD_5112A, ath5k_hw_reg_read(), ath5k_hw_rf_check_gainf_readback(), ath5k_hw_rf_gainf_adjust(), ath5k_hw_rf_gainf_corr(), ath5k_capabilities::cap_eeprom, data, done, ath5k_eeprom_info::ee_cck_ofdm_gain_delta, ath5k_gain::g_current, ath5k_gain::g_f_corr, ath5k_gain::g_state, NULL, and type.

Referenced by ath5k_calibrate(), and ath5k_hw_reset().

{
        u32 data, type;
        struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;

        if (ah->ah_rf_banks == NULL ||
        ah->ah_gain.g_state == AR5K_RFGAIN_INACTIVE)
                return AR5K_RFGAIN_INACTIVE;

        /* No check requested, either engine is inactive
         * or an adjustment is already requested */
        if (ah->ah_gain.g_state != AR5K_RFGAIN_READ_REQUESTED)
                goto done;

        /* Read the PAPD (Peak to Average Power Detector)
         * register */
        data = ath5k_hw_reg_read(ah, AR5K_PHY_PAPD_PROBE);

        /* No probe is scheduled, read gain_F measurement */
        if (!(data & AR5K_PHY_PAPD_PROBE_TX_NEXT)) {
                ah->ah_gain.g_current = data >> AR5K_PHY_PAPD_PROBE_GAINF_S;
                type = AR5K_REG_MS(data, AR5K_PHY_PAPD_PROBE_TYPE);

                /* If tx packet is CCK correct the gain_F measurement
                 * by cck ofdm gain delta */
                if (type == AR5K_PHY_PAPD_PROBE_TYPE_CCK) {
                        if (ah->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A)
                                ah->ah_gain.g_current +=
                                        ee->ee_cck_ofdm_gain_delta;
                        else
                                ah->ah_gain.g_current +=
                                        AR5K_GAIN_CCK_PROBE_CORR;
                }

                /* Further correct gain_F measurement for
                 * RF5112A radios */
                if (ah->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A) {
                        ath5k_hw_rf_gainf_corr(ah);
                        ah->ah_gain.g_current =
                                ah->ah_gain.g_current >= ah->ah_gain.g_f_corr ?
                                (ah->ah_gain.g_current-ah->ah_gain.g_f_corr) :
                                0;
                }

                /* Check if measurement is ok and if we need
                 * to adjust gain, schedule a gain adjustment,
                 * else switch back to the acive state */
                if (ath5k_hw_rf_check_gainf_readback(ah) &&
                AR5K_GAIN_CHECK_ADJUST(&ah->ah_gain) &&
                ath5k_hw_rf_gainf_adjust(ah)) {
                        ah->ah_gain.g_state = AR5K_RFGAIN_NEED_CHANGE;
                } else {
                        ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE;
                }
        }

done:
        return ah->ah_gain.g_state;
}
int ath5k_hw_rfgain_init ( struct ath5k_hw ah,
unsigned int  freq 
)

Definition at line 452 of file ath5k_phy.c.

References ath5k_hw::ah_radio, AR5K_INI_RFGAIN_2GHZ, AR5K_INI_RFGAIN_5GHZ, AR5K_REG_WAIT, AR5K_RF2316, AR5K_RF2317, AR5K_RF2413, AR5K_RF2425, AR5K_RF5111, AR5K_RF5112, AR5K_RF5413, ARRAY_SIZE, ath5k_hw_reg_write(), EINVAL, ath5k_ini_rfgain::rfg_register, ath5k_ini_rfgain::rfg_value, rfgain_2316, rfgain_2413, rfgain_2425, rfgain_5111, rfgain_5112, rfgain_5413, and size.

Referenced by ath5k_hw_reset().

{
        const struct ath5k_ini_rfgain *ath5k_rfg;
        unsigned int i, size;

        switch (ah->ah_radio) {
        case AR5K_RF5111:
                ath5k_rfg = rfgain_5111;
                size = ARRAY_SIZE(rfgain_5111);
                break;
        case AR5K_RF5112:
                ath5k_rfg = rfgain_5112;
                size = ARRAY_SIZE(rfgain_5112);
                break;
        case AR5K_RF2413:
                ath5k_rfg = rfgain_2413;
                size = ARRAY_SIZE(rfgain_2413);
                break;
        case AR5K_RF2316:
                ath5k_rfg = rfgain_2316;
                size = ARRAY_SIZE(rfgain_2316);
                break;
        case AR5K_RF5413:
                ath5k_rfg = rfgain_5413;
                size = ARRAY_SIZE(rfgain_5413);
                break;
        case AR5K_RF2317:
        case AR5K_RF2425:
                ath5k_rfg = rfgain_2425;
                size = ARRAY_SIZE(rfgain_2425);
                break;
        default:
                return -EINVAL;
        }

        switch (freq) {
        case AR5K_INI_RFGAIN_2GHZ:
        case AR5K_INI_RFGAIN_5GHZ:
                break;
        default:
                return -EINVAL;
        }

        for (i = 0; i < size; i++) {
                AR5K_REG_WAIT(i);
                ath5k_hw_reg_write(ah, ath5k_rfg[i].rfg_value[freq],
                        (u32)ath5k_rfg[i].rfg_register);
        }

        return 0;
}
int ath5k_hw_rfregs_init ( struct ath5k_hw ah,
struct net80211_channel channel,
unsigned int  mode 
)

Definition at line 514 of file ath5k_phy.c.

References ath5k_hw::ah_capabilities, ath5k_hw::ah_gain, ath5k_hw::ah_mac_srev, ath5k_hw::ah_offset, ath5k_hw::ah_phy_revision, ath5k_hw::ah_radio, ath5k_hw::ah_radio_5ghz_revision, ath5k_hw::ah_rf_banks, ath5k_hw::ah_rf_banks_size, ath5k_hw::ah_rf_regs_count, AR5K_EEPROM_MODE_11A, AR5K_EEPROM_MODE_11B, AR5K_EEPROM_MODE_11G, AR5K_MAX_RF_BANKS, AR5K_PHY_FRAME_CTL, AR5K_PHY_FRAME_CTL_TX_CLIP, AR5K_REG_WAIT, AR5K_REG_WRITE_BITS, AR5K_RF2316, AR5K_RF2317, AR5K_RF2413, AR5K_RF2425, AR5K_RF5111, AR5K_RF5112, AR5K_RF5413, AR5K_RF_DB_2GHZ, AR5K_RF_DB_5GHZ, AR5K_RF_DERBY_CHAN_SEL_MODE, AR5K_RF_GAIN_I, AR5K_RF_HIGH_VC_CP, AR5K_RF_LOW_VC_CP, AR5K_RF_MID_VC_CP, AR5K_RF_MIXGAIN_OVR, AR5K_RF_OB_2GHZ, AR5K_RF_OB_5GHZ, AR5K_RF_PAD2GND, AR5K_RF_PD_GAIN_HI, AR5K_RF_PD_GAIN_LO, AR5K_RF_PLO_SEL, AR5K_RF_PUSH_UP, AR5K_RF_PWD_130, AR5K_RF_PWD_131, AR5K_RF_PWD_132, AR5K_RF_PWD_136, AR5K_RF_PWD_137, AR5K_RF_PWD_138, AR5K_RF_PWD_166, AR5K_RF_PWD_167, AR5K_RF_PWD_84, AR5K_RF_PWD_90, AR5K_RF_PWD_ICLOBUF_2G, AR5K_RF_PWD_XPD, AR5K_RF_RFGAIN_SEL, AR5K_RF_XB2_LVL, AR5K_RF_XB5_LVL, AR5K_RF_XPD_GAIN, AR5K_RF_XPD_SEL, AR5K_RFGAIN_ACTIVE, AR5K_SREV_AR2417, AR5K_SREV_AR5413, AR5K_SREV_AR5424, AR5K_SREV_PHY_5212A, AR5K_SREV_RAD_5112A, ARRAY_SIZE, ath5k_hw_bitswap(), ath5k_hw_reg_write(), ath5k_hw_rfb_op(), ath5k_capabilities::cap_eeprom, net80211_channel::center_freq, CHANNEL_2GHZ, CHANNEL_5GHZ, CHANNEL_CCK, CHANNEL_OFDM, DBG, ath5k_eeprom_info::ee_db, ath5k_eeprom_info::ee_i_gain, ath5k_eeprom_info::ee_ob, ath5k_eeprom_info::ee_x_gain, ath5k_eeprom_info::ee_xpd, EINVAL, ENOMEM, ath5k_gain::g_state, ath5k_gain::g_step_idx, ath5k_gain_opt::go_step, ath5k_gain_opt_step::gos_param, net80211_channel::hw_value, malloc(), NULL, rf_regs_2316, rf_regs_2413, rf_regs_2425, rf_regs_5111, rf_regs_5112, rf_regs_5112a, rf_regs_5413, rfb_2316, rfb_2317, rfb_2413, rfb_2417, rfb_2425, rfb_5111, rfb_5112, rfb_5112a, rfb_5413, ath5k_ini_rfbuffer::rfb_bank, ath5k_ini_rfbuffer::rfb_mode_data, rfgain_opt_5111, and rfgain_opt_5112.

Referenced by ath5k_hw_reset().

{
        const struct ath5k_rf_reg *rf_regs;
        const struct ath5k_ini_rfbuffer *ini_rfb;
        const struct ath5k_gain_opt *go = NULL;
        const struct ath5k_gain_opt_step *g_step;
        struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
        u8 ee_mode = 0;
        u32 *rfb;
        int obdb = -1, bank = -1;
        unsigned i;

        switch (ah->ah_radio) {
        case AR5K_RF5111:
                rf_regs = rf_regs_5111;
                ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5111);
                ini_rfb = rfb_5111;
                ah->ah_rf_banks_size = ARRAY_SIZE(rfb_5111);
                go = &rfgain_opt_5111;
                break;
        case AR5K_RF5112:
                if (ah->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A) {
                        rf_regs = rf_regs_5112a;
                        ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5112a);
                        ini_rfb = rfb_5112a;
                        ah->ah_rf_banks_size = ARRAY_SIZE(rfb_5112a);
                } else {
                        rf_regs = rf_regs_5112;
                        ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5112);
                        ini_rfb = rfb_5112;
                        ah->ah_rf_banks_size = ARRAY_SIZE(rfb_5112);
                }
                go = &rfgain_opt_5112;
                break;
        case AR5K_RF2413:
                rf_regs = rf_regs_2413;
                ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_2413);
                ini_rfb = rfb_2413;
                ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2413);
                break;
        case AR5K_RF2316:
                rf_regs = rf_regs_2316;
                ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_2316);
                ini_rfb = rfb_2316;
                ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2316);
                break;
        case AR5K_RF5413:
                rf_regs = rf_regs_5413;
                ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5413);
                ini_rfb = rfb_5413;
                ah->ah_rf_banks_size = ARRAY_SIZE(rfb_5413);
                break;
        case AR5K_RF2317:
                rf_regs = rf_regs_2425;
                ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_2425);
                ini_rfb = rfb_2317;
                ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2317);
                break;
        case AR5K_RF2425:
                rf_regs = rf_regs_2425;
                ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_2425);
                if (ah->ah_mac_srev < AR5K_SREV_AR2417) {
                        ini_rfb = rfb_2425;
                        ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2425);
                } else {
                        ini_rfb = rfb_2417;
                        ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2417);
                }
                break;
        default:
                return -EINVAL;
        }

        /* If it's the first time we set rf buffer, allocate
         * ah->ah_rf_banks based on ah->ah_rf_banks_size
         * we set above */
        if (ah->ah_rf_banks == NULL) {
                ah->ah_rf_banks = malloc(sizeof(u32) * ah->ah_rf_banks_size);
                if (ah->ah_rf_banks == NULL) {
                        return -ENOMEM;
                }
        }

        /* Copy values to modify them */
        rfb = ah->ah_rf_banks;

        for (i = 0; i < ah->ah_rf_banks_size; i++) {
                if (ini_rfb[i].rfb_bank >= AR5K_MAX_RF_BANKS) {
                        DBG("ath5k: invalid RF register bank\n");
                        return -EINVAL;
                }

                /* Bank changed, write down the offset */
                if (bank != ini_rfb[i].rfb_bank) {
                        bank = ini_rfb[i].rfb_bank;
                        ah->ah_offset[bank] = i;
                }

                rfb[i] = ini_rfb[i].rfb_mode_data[mode];
        }

        /* Set Output and Driver bias current (OB/DB) */
        if (channel->hw_value & CHANNEL_2GHZ) {

                if (channel->hw_value & CHANNEL_CCK)
                        ee_mode = AR5K_EEPROM_MODE_11B;
                else
                        ee_mode = AR5K_EEPROM_MODE_11G;

                /* For RF511X/RF211X combination we
                 * use b_OB and b_DB parameters stored
                 * in eeprom on ee->ee_ob[ee_mode][0]
                 *
                 * For all other chips we use OB/DB for 2Ghz
                 * stored in the b/g modal section just like
                 * 802.11a on ee->ee_ob[ee_mode][1] */
                if ((ah->ah_radio == AR5K_RF5111) ||
                (ah->ah_radio == AR5K_RF5112))
                        obdb = 0;
                else
                        obdb = 1;

                ath5k_hw_rfb_op(ah, rf_regs, ee->ee_ob[ee_mode][obdb],
                                                AR5K_RF_OB_2GHZ, 1);

                ath5k_hw_rfb_op(ah, rf_regs, ee->ee_db[ee_mode][obdb],
                                                AR5K_RF_DB_2GHZ, 1);

        /* RF5111 always needs OB/DB for 5GHz, even if we use 2GHz */
        } else if ((channel->hw_value & CHANNEL_5GHZ) ||
                        (ah->ah_radio == AR5K_RF5111)) {

                /* For 11a, Turbo and XR we need to choose
                 * OB/DB based on frequency range */
                ee_mode = AR5K_EEPROM_MODE_11A;
                obdb =   channel->center_freq >= 5725 ? 3 :
                        (channel->center_freq >= 5500 ? 2 :
                        (channel->center_freq >= 5260 ? 1 :
                         (channel->center_freq > 4000 ? 0 : -1)));

                if (obdb < 0)
                        return -EINVAL;

                ath5k_hw_rfb_op(ah, rf_regs, ee->ee_ob[ee_mode][obdb],
                                                AR5K_RF_OB_5GHZ, 1);

                ath5k_hw_rfb_op(ah, rf_regs, ee->ee_db[ee_mode][obdb],
                                                AR5K_RF_DB_5GHZ, 1);
        }

        g_step = &go->go_step[ah->ah_gain.g_step_idx];

        /* Bank Modifications (chip-specific) */
        if (ah->ah_radio == AR5K_RF5111) {

                /* Set gain_F settings according to current step */
                if (channel->hw_value & CHANNEL_OFDM) {

                        AR5K_REG_WRITE_BITS(ah, AR5K_PHY_FRAME_CTL,
                                        AR5K_PHY_FRAME_CTL_TX_CLIP,
                                        g_step->gos_param[0]);

                        ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[1],
                                                        AR5K_RF_PWD_90, 1);

                        ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[2],
                                                        AR5K_RF_PWD_84, 1);

                        ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[3],
                                                AR5K_RF_RFGAIN_SEL, 1);

                        /* We programmed gain_F parameters, switch back
                         * to active state */
                        ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE;

                }

                /* Bank 6/7 setup */

                ath5k_hw_rfb_op(ah, rf_regs, !ee->ee_xpd[ee_mode],
                                                AR5K_RF_PWD_XPD, 1);

                ath5k_hw_rfb_op(ah, rf_regs, ee->ee_x_gain[ee_mode],
                                                AR5K_RF_XPD_GAIN, 1);

                ath5k_hw_rfb_op(ah, rf_regs, ee->ee_i_gain[ee_mode],
                                                AR5K_RF_GAIN_I, 1);

                ath5k_hw_rfb_op(ah, rf_regs, ee->ee_xpd[ee_mode],
                                                AR5K_RF_PLO_SEL, 1);

                /* TODO: Half/quarter channel support */
        }

        if (ah->ah_radio == AR5K_RF5112) {

                /* Set gain_F settings according to current step */
                if (channel->hw_value & CHANNEL_OFDM) {

                        ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[0],
                                                AR5K_RF_MIXGAIN_OVR, 1);

                        ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[1],
                                                AR5K_RF_PWD_138, 1);

                        ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[2],
                                                AR5K_RF_PWD_137, 1);

                        ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[3],
                                                AR5K_RF_PWD_136, 1);

                        ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[4],
                                                AR5K_RF_PWD_132, 1);

                        ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[5],
                                                AR5K_RF_PWD_131, 1);

                        ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[6],
                                                AR5K_RF_PWD_130, 1);

                        /* We programmed gain_F parameters, switch back
                         * to active state */
                        ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE;
                }

                /* Bank 6/7 setup */

                ath5k_hw_rfb_op(ah, rf_regs, ee->ee_xpd[ee_mode],
                                                AR5K_RF_XPD_SEL, 1);

                if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_5112A) {
                        /* Rev. 1 supports only one xpd */
                        ath5k_hw_rfb_op(ah, rf_regs,
                                                ee->ee_x_gain[ee_mode],
                                                AR5K_RF_XPD_GAIN, 1);

                } else {
                        /* TODO: Set high and low gain bits */
                        ath5k_hw_rfb_op(ah, rf_regs,
                                                ee->ee_x_gain[ee_mode],
                                                AR5K_RF_PD_GAIN_LO, 1);
                        ath5k_hw_rfb_op(ah, rf_regs,
                                                ee->ee_x_gain[ee_mode],
                                                AR5K_RF_PD_GAIN_HI, 1);

                        /* Lower synth voltage on Rev 2 */
                        ath5k_hw_rfb_op(ah, rf_regs, 2,
                                        AR5K_RF_HIGH_VC_CP, 1);

                        ath5k_hw_rfb_op(ah, rf_regs, 2,
                                        AR5K_RF_MID_VC_CP, 1);

                        ath5k_hw_rfb_op(ah, rf_regs, 2,
                                        AR5K_RF_LOW_VC_CP, 1);

                        ath5k_hw_rfb_op(ah, rf_regs, 2,
                                        AR5K_RF_PUSH_UP, 1);

                        /* Decrease power consumption on 5213+ BaseBand */
                        if (ah->ah_phy_revision >= AR5K_SREV_PHY_5212A) {
                                ath5k_hw_rfb_op(ah, rf_regs, 1,
                                                AR5K_RF_PAD2GND, 1);

                                ath5k_hw_rfb_op(ah, rf_regs, 1,
                                                AR5K_RF_XB2_LVL, 1);

                                ath5k_hw_rfb_op(ah, rf_regs, 1,
                                                AR5K_RF_XB5_LVL, 1);

                                ath5k_hw_rfb_op(ah, rf_regs, 1,
                                                AR5K_RF_PWD_167, 1);

                                ath5k_hw_rfb_op(ah, rf_regs, 1,
                                                AR5K_RF_PWD_166, 1);
                        }
                }

                ath5k_hw_rfb_op(ah, rf_regs, ee->ee_i_gain[ee_mode],
                                                AR5K_RF_GAIN_I, 1);

                /* TODO: Half/quarter channel support */

        }

        if (ah->ah_radio == AR5K_RF5413 &&
        channel->hw_value & CHANNEL_2GHZ) {

                ath5k_hw_rfb_op(ah, rf_regs, 1, AR5K_RF_DERBY_CHAN_SEL_MODE,
                                                                        1);

                /* Set optimum value for early revisions (on pci-e chips) */
                if (ah->ah_mac_srev >= AR5K_SREV_AR5424 &&
                ah->ah_mac_srev < AR5K_SREV_AR5413)
                        ath5k_hw_rfb_op(ah, rf_regs, ath5k_hw_bitswap(6, 3),
                                                AR5K_RF_PWD_ICLOBUF_2G, 1);

        }

        /* Write RF banks on hw */
        for (i = 0; i < ah->ah_rf_banks_size; i++) {
                AR5K_REG_WAIT(i);
                ath5k_hw_reg_write(ah, rfb[i], ini_rfb[i].rfb_ctrl_register);
        }

        return 0;
}
int ath5k_channel_ok ( struct ath5k_hw ah,
u16  freq,
unsigned int  flags 
)

Definition at line 830 of file ath5k_phy.c.

References ath5k_hw::ah_capabilities, ath5k_capabilities::cap_range, CHANNEL_2GHZ, CHANNEL_5GHZ, ath5k_capabilities::range_2ghz_min, and ath5k_capabilities::range_5ghz_min.

Referenced by ath5k_copy_channels(), and ath5k_hw_channel().

{
        /* Check if the channel is in our supported range */
        if (flags & CHANNEL_2GHZ) {
                if ((freq >= ah->ah_capabilities.cap_range.range_2ghz_min) &&
                    (freq <= ah->ah_capabilities.cap_range.range_2ghz_max))
                        return 1;
        } else if (flags & CHANNEL_5GHZ)
                if ((freq >= ah->ah_capabilities.cap_range.range_5ghz_min) &&
                    (freq <= ah->ah_capabilities.cap_range.range_5ghz_max))
                        return 1;

        return 0;
}
static u32 ath5k_hw_rf5110_chan2athchan ( struct net80211_channel channel) [static]

Definition at line 848 of file ath5k_phy.c.

References ath5k_hw_bitswap(), and net80211_channel::center_freq.

Referenced by ath5k_hw_rf5110_channel().

{
        u32 athchan;

        /*
         * Convert IEEE channel/MHz to an internal channel value used
         * by the AR5210 chipset. This has not been verified with
         * newer chipsets like the AR5212A who have a completely
         * different RF/PHY part.
         */
        athchan = (ath5k_hw_bitswap((ath5k_freq_to_channel(channel->center_freq)
                                     - 24) / 2, 5) << 1)
                | (1 << 6) | 0x1;
        return athchan;
}
static int ath5k_hw_rf5110_channel ( struct ath5k_hw ah,
struct net80211_channel channel 
) [static]

Definition at line 867 of file ath5k_phy.c.

References AR5K_RF_BUFFER, AR5K_RF_BUFFER_CONTROL_0, ath5k_hw_reg_write(), ath5k_hw_rf5110_chan2athchan(), data, and mdelay().

Referenced by ath5k_hw_channel().

{
        u32 data;

        /*
         * Set the channel and wait
         */
        data = ath5k_hw_rf5110_chan2athchan(channel);
        ath5k_hw_reg_write(ah, data, AR5K_RF_BUFFER);
        ath5k_hw_reg_write(ah, 0, AR5K_RF_BUFFER_CONTROL_0);
        mdelay(1);

        return 0;
}
static int ath5k_hw_rf5111_chan2athchan ( unsigned int  ieee,
struct ath5k_athchan_2ghz athchan 
) [static]

Definition at line 886 of file ath5k_phy.c.

References ath5k_athchan_2ghz::a2_athchan, ath5k_athchan_2ghz::a2_flags, channel, and EINVAL.

Referenced by ath5k_hw_rf5111_channel().

{
        int channel;

        /* Cast this value to catch negative channel numbers (>= -19) */
        channel = (int)ieee;

        /*
         * Map 2GHz IEEE channel to 5GHz Atheros channel
         */
        if (channel <= 13) {
                athchan->a2_athchan = 115 + channel;
                athchan->a2_flags = 0x46;
        } else if (channel == 14) {
                athchan->a2_athchan = 124;
                athchan->a2_flags = 0x44;
        } else if (channel >= 15 && channel <= 26) {
                athchan->a2_athchan = ((channel - 14) * 4) + 132;
                athchan->a2_flags = 0x46;
        } else
                return -EINVAL;

        return 0;
}
static int ath5k_hw_rf5111_channel ( struct ath5k_hw ah,
struct net80211_channel channel 
) [static]

Definition at line 915 of file ath5k_phy.c.

References ath5k_athchan_2ghz::a2_athchan, ath5k_athchan_2ghz::a2_flags, AR5K_RF_BUFFER, AR5K_RF_BUFFER_CONTROL_3, ath5k_hw_bitswap(), ath5k_hw_reg_write(), ath5k_hw_rf5111_chan2athchan(), net80211_channel::center_freq, CHANNEL_2GHZ, data0, data1, net80211_channel::hw_value, and ret.

Referenced by ath5k_hw_channel().

{
        struct ath5k_athchan_2ghz ath5k_channel_2ghz;
        unsigned int ath5k_channel = ath5k_freq_to_channel(channel->center_freq);
        u32 data0, data1, clock;
        int ret;

        /*
         * Set the channel on the RF5111 radio
         */
        data0 = data1 = 0;

        if (channel->hw_value & CHANNEL_2GHZ) {
                /* Map 2GHz channel to 5GHz Atheros channel ID */
                ret = ath5k_hw_rf5111_chan2athchan(ath5k_channel,
                                                   &ath5k_channel_2ghz);
                if (ret)
                        return ret;

                ath5k_channel = ath5k_channel_2ghz.a2_athchan;
                data0 = ((ath5k_hw_bitswap(ath5k_channel_2ghz.a2_flags, 8) & 0xff)
                    << 5) | (1 << 4);
        }

        if (ath5k_channel < 145 || !(ath5k_channel & 1)) {
                clock = 1;
                data1 = ((ath5k_hw_bitswap(ath5k_channel - 24, 8) & 0xff) << 2) |
                        (clock << 1) | (1 << 10) | 1;
        } else {
                clock = 0;
                data1 = ((ath5k_hw_bitswap((ath5k_channel - 24) / 2, 8) & 0xff)
                        << 2) | (clock << 1) | (1 << 10) | 1;
        }

        ath5k_hw_reg_write(ah, (data1 & 0xff) | ((data0 & 0xff) << 8),
                        AR5K_RF_BUFFER);
        ath5k_hw_reg_write(ah, ((data1 >> 8) & 0xff) | (data0 & 0xff00),
                        AR5K_RF_BUFFER_CONTROL_3);

        return 0;
}
static int ath5k_hw_rf5112_channel ( struct ath5k_hw ah,
struct net80211_channel channel 
) [static]

Definition at line 961 of file ath5k_phy.c.

References AR5K_RF_BUFFER, AR5K_RF_BUFFER_CONTROL_5, ath5k_hw_bitswap(), ath5k_hw_reg_write(), net80211_channel::center_freq, data, data0, data1, data2, and EINVAL.

Referenced by ath5k_hw_channel().

{
        u32 data, data0, data1, data2;
        u16 c;

        data = data0 = data1 = data2 = 0;
        c = channel->center_freq;

        if (c < 4800) {
                if (!((c - 2224) % 5)) {
                        data0 = ((2 * (c - 704)) - 3040) / 10;
                        data1 = 1;
                } else if (!((c - 2192) % 5)) {
                        data0 = ((2 * (c - 672)) - 3040) / 10;
                        data1 = 0;
                } else
                        return -EINVAL;

                data0 = ath5k_hw_bitswap((data0 << 2) & 0xff, 8);
        } else if ((c - (c % 5)) != 2 || c > 5435) {
                if (!(c % 20) && c >= 5120) {
                        data0 = ath5k_hw_bitswap(((c - 4800) / 20 << 2), 8);
                        data2 = ath5k_hw_bitswap(3, 2);
                } else if (!(c % 10)) {
                        data0 = ath5k_hw_bitswap(((c - 4800) / 10 << 1), 8);
                        data2 = ath5k_hw_bitswap(2, 2);
                } else if (!(c % 5)) {
                        data0 = ath5k_hw_bitswap((c - 4800) / 5, 8);
                        data2 = ath5k_hw_bitswap(1, 2);
                } else
                        return -EINVAL;
        } else {
                data0 = ath5k_hw_bitswap((10 * (c - 2) - 4800) / 25 + 1, 8);
                data2 = ath5k_hw_bitswap(0, 2);
        }

        data = (data0 << 4) | (data1 << 1) | (data2 << 2) | 0x1001;

        ath5k_hw_reg_write(ah, data & 0xff, AR5K_RF_BUFFER);
        ath5k_hw_reg_write(ah, (data >> 8) & 0x7f, AR5K_RF_BUFFER_CONTROL_5);

        return 0;
}
static int ath5k_hw_rf2425_channel ( struct ath5k_hw ah,
struct net80211_channel channel 
) [static]

Definition at line 1009 of file ath5k_phy.c.

References AR5K_RF_BUFFER, AR5K_RF_BUFFER_CONTROL_5, ath5k_hw_bitswap(), ath5k_hw_reg_write(), net80211_channel::center_freq, data, data0, data2, and EINVAL.

Referenced by ath5k_hw_channel().

{
        u32 data, data0, data2;
        u16 c;

        data = data0 = data2 = 0;
        c = channel->center_freq;

        if (c < 4800) {
                data0 = ath5k_hw_bitswap((c - 2272), 8);
                data2 = 0;
        /* ? 5GHz ? */
        } else if ((c - (c % 5)) != 2 || c > 5435) {
                if (!(c % 20) && c < 5120)
                        data0 = ath5k_hw_bitswap(((c - 4800) / 20 << 2), 8);
                else if (!(c % 10))
                        data0 = ath5k_hw_bitswap(((c - 4800) / 10 << 1), 8);
                else if (!(c % 5))
                        data0 = ath5k_hw_bitswap((c - 4800) / 5, 8);
                else
                        return -EINVAL;
                data2 = ath5k_hw_bitswap(1, 2);
        } else {
                data0 = ath5k_hw_bitswap((10 * (c - 2) - 4800) / 25 + 1, 8);
                data2 = ath5k_hw_bitswap(0, 2);
        }

        data = (data0 << 4) | data2 << 2 | 0x1001;

        ath5k_hw_reg_write(ah, data & 0xff, AR5K_RF_BUFFER);
        ath5k_hw_reg_write(ah, (data >> 8) & 0x7f, AR5K_RF_BUFFER_CONTROL_5);

        return 0;
}
int ath5k_hw_channel ( struct ath5k_hw ah,
struct net80211_channel channel 
)

Definition at line 1048 of file ath5k_phy.c.

References ath5k_hw::ah_current_channel, ath5k_hw::ah_radio, ath5k_hw::ah_turbo, AR5K_PHY_CCKTXCTL, AR5K_PHY_CCKTXCTL_JAPAN, AR5K_PHY_CCKTXCTL_WORLD, AR5K_REG_ENABLE_BITS, AR5K_RF2425, AR5K_RF5110, AR5K_RF5111, ath5k_channel_ok(), ath5k_hw_rf2425_channel(), ath5k_hw_rf5110_channel(), ath5k_hw_rf5111_channel(), ath5k_hw_rf5112_channel(), net80211_channel::center_freq, channel, CHANNEL_T, DBG, EINVAL, net80211_channel::hw_value, ret, and strerror().

Referenced by ath5k_hw_reset(), and ath5k_hw_rf5110_calibrate().

{
        int ret;
        /*
         * Check bounds supported by the PHY (we don't care about regultory
         * restrictions at this point). Note: hw_value already has the band
         * (CHANNEL_2GHZ, or CHANNEL_5GHZ) so we inform ath5k_channel_ok()
         * of the band by that */
        if (!ath5k_channel_ok(ah, channel->center_freq, channel->hw_value)) {
                DBG("ath5k: channel frequency (%d MHz) out of supported "
                    "range\n", channel->center_freq);
                return -EINVAL;
        }

        /*
         * Set the channel and wait
         */
        switch (ah->ah_radio) {
        case AR5K_RF5110:
                ret = ath5k_hw_rf5110_channel(ah, channel);
                break;
        case AR5K_RF5111:
                ret = ath5k_hw_rf5111_channel(ah, channel);
                break;
        case AR5K_RF2425:
                ret = ath5k_hw_rf2425_channel(ah, channel);
                break;
        default:
                ret = ath5k_hw_rf5112_channel(ah, channel);
                break;
        }

        if (ret) {
                DBG("ath5k: setting channel failed: %s\n", strerror(ret));
                return ret;
        }

        /* Set JAPAN setting for channel 14 */
        if (channel->center_freq == 2484) {
                AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_CCKTXCTL,
                                AR5K_PHY_CCKTXCTL_JAPAN);
        } else {
                AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_CCKTXCTL,
                                AR5K_PHY_CCKTXCTL_WORLD);
        }

        ah->ah_current_channel = channel;
        ah->ah_turbo = (channel->hw_value == CHANNEL_T ? 1 : 0);

        return 0;
}
int ath5k_hw_noise_floor_calibration ( struct ath5k_hw ah,
short  freq 
)

ath5k_hw_noise_floor_calibration - perform PHY noise floor calibration

: struct ath5k_hw pointer we are operating on : the channel frequency, just used for error logging

This function performs a noise floor calibration of the PHY and waits for it to complete. Then the noise floor value is compared to some maximum noise floor we consider valid.

Note that this is different from what the madwifi HAL does: it reads the noise floor and afterwards initiates the calibration. Since the noise floor calibration can take some time to finish, depending on the current channel use, that avoids the occasional timeout warnings we are seeing now.

See the following link for an Atheros patent on noise floor calibration: http://patft.uspto.gov/netacgi/nph-Parser?Sect1=PTO1&Sect2=HITOFF&d=PALL \ &p=1&u=%2Fnetahtml%2FPTO%2Fsrchnum.htm&r=1&f=G&l=50&s1=7245893.PN.&OS=PN/7

XXX: Since during noise floor calibration antennas are detached according to the patent, we should stop tx queues here.

Definition at line 1127 of file ath5k_phy.c.

References ath5k_hw::ah_noise_floor, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_NF, AR5K_PHY_NF, AR5K_PHY_NF_ACTIVE, AR5K_PHY_NF_AVAL, AR5K_PHY_NF_RVAL, AR5K_REG_ENABLE_BITS, AR5K_TUNE_NOISE_FLOOR, ath5k_hw_reg_read(), DBG, DBG2, EAGAIN, mdelay(), and ret.

Referenced by ath5k_hw_reset(), ath5k_hw_rf5110_calibrate(), and ath5k_hw_rf511x_calibrate().

{
        int ret;
        unsigned int i;
        s32 noise_floor;

        /*
         * Enable noise floor calibration
         */
        AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL,
                                AR5K_PHY_AGCCTL_NF);

        ret = ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL,
                        AR5K_PHY_AGCCTL_NF, 0, 0);

        if (ret) {
                DBG("ath5k: noise floor calibration timeout (%d MHz)\n", freq);
                return -EAGAIN;
        }

        /* Wait until the noise floor is calibrated and read the value */
        for (i = 20; i > 0; i--) {
                mdelay(1);
                noise_floor = ath5k_hw_reg_read(ah, AR5K_PHY_NF);
                noise_floor = AR5K_PHY_NF_RVAL(noise_floor);
                if (noise_floor & AR5K_PHY_NF_ACTIVE) {
                        noise_floor = AR5K_PHY_NF_AVAL(noise_floor);

                        if (noise_floor <= AR5K_TUNE_NOISE_FLOOR)
                                break;
                }
        }

        DBG2("ath5k: noise floor %d\n", noise_floor);

        if (noise_floor > AR5K_TUNE_NOISE_FLOOR) {
                DBG("ath5k: noise floor calibration failed (%d MHz)\n", freq);
                return -EAGAIN;
        }

        ah->ah_noise_floor = noise_floor;

        return 0;
}
static int ath5k_hw_rf5110_calibrate ( struct ath5k_hw ah,
struct net80211_channel channel 
) [static]

Definition at line 1177 of file ath5k_phy.c.

References AR5K_BEACON_5210, AR5K_BEACON_ENABLE, AR5K_DIAG_SW_5210, AR5K_DIAG_SW_DIS_RX_5210, AR5K_DIAG_SW_DIS_TX, AR5K_PHY_ACT, AR5K_PHY_ACT_ENABLE, AR5K_PHY_ADCSAT, AR5K_PHY_ADCSAT_ICNT, AR5K_PHY_ADCSAT_THR, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE, AR5K_PHY_AGCCOARSE, AR5K_PHY_AGCCOARSE_HI, AR5K_PHY_AGCCOARSE_LO, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_CAL, AR5K_PHY_RFSTG, AR5K_PHY_RFSTG_DISABLE, AR5K_PHY_SIG, AR5K_PHY_SIG_FIRPWR, AR5K_REG_DISABLE_BITS, AR5K_REG_ENABLE_BITS, AR5K_REG_SM, ath5k_hw_channel(), ath5k_hw_noise_floor_calibration(), ath5k_hw_reg_read(), ath5k_hw_reg_write(), net80211_channel::center_freq, DBG, mdelay(), ret, and udelay().

Referenced by ath5k_hw_phy_calibrate().

{
        u32 phy_sig, phy_agc, phy_sat, beacon;
        int ret;

        /*
         * Disable beacons and RX/TX queues, wait
         */
        AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5210,
                AR5K_DIAG_SW_DIS_TX | AR5K_DIAG_SW_DIS_RX_5210);
        beacon = ath5k_hw_reg_read(ah, AR5K_BEACON_5210);
        ath5k_hw_reg_write(ah, beacon & ~AR5K_BEACON_ENABLE, AR5K_BEACON_5210);

        mdelay(2);

        /*
         * Set the channel (with AGC turned off)
         */
        AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE);
        udelay(10);
        ret = ath5k_hw_channel(ah, channel);

        /*
         * Activate PHY and wait
         */
        ath5k_hw_reg_write(ah, AR5K_PHY_ACT_ENABLE, AR5K_PHY_ACT);
        mdelay(1);

        AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE);

        if (ret)
                return ret;

        /*
         * Calibrate the radio chip
         */

        /* Remember normal state */
        phy_sig = ath5k_hw_reg_read(ah, AR5K_PHY_SIG);
        phy_agc = ath5k_hw_reg_read(ah, AR5K_PHY_AGCCOARSE);
        phy_sat = ath5k_hw_reg_read(ah, AR5K_PHY_ADCSAT);

        /* Update radio registers */
        ath5k_hw_reg_write(ah, (phy_sig & ~(AR5K_PHY_SIG_FIRPWR)) |
                AR5K_REG_SM(-1U, AR5K_PHY_SIG_FIRPWR), AR5K_PHY_SIG);

        ath5k_hw_reg_write(ah, (phy_agc & ~(AR5K_PHY_AGCCOARSE_HI |
                        AR5K_PHY_AGCCOARSE_LO)) |
                AR5K_REG_SM(-1U, AR5K_PHY_AGCCOARSE_HI) |
                AR5K_REG_SM(-127U, AR5K_PHY_AGCCOARSE_LO), AR5K_PHY_AGCCOARSE);

        ath5k_hw_reg_write(ah, (phy_sat & ~(AR5K_PHY_ADCSAT_ICNT |
                        AR5K_PHY_ADCSAT_THR)) |
                AR5K_REG_SM(2, AR5K_PHY_ADCSAT_ICNT) |
                AR5K_REG_SM(12, AR5K_PHY_ADCSAT_THR), AR5K_PHY_ADCSAT);

        udelay(20);

        AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE);
        udelay(10);
        ath5k_hw_reg_write(ah, AR5K_PHY_RFSTG_DISABLE, AR5K_PHY_RFSTG);
        AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE);

        mdelay(1);

        /*
         * Enable calibration and wait until completion
         */
        AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_CAL);

        ret = ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL,
                        AR5K_PHY_AGCCTL_CAL, 0, 0);

        /* Reset to normal state */
        ath5k_hw_reg_write(ah, phy_sig, AR5K_PHY_SIG);
        ath5k_hw_reg_write(ah, phy_agc, AR5K_PHY_AGCCOARSE);
        ath5k_hw_reg_write(ah, phy_sat, AR5K_PHY_ADCSAT);

        if (ret) {
                DBG("ath5k: calibration timeout (%d MHz)\n",
                    channel->center_freq);
                return ret;
        }

        ath5k_hw_noise_floor_calibration(ah, channel->center_freq);

        /*
         * Re-enable RX/TX and beacons
         */
        AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW_5210,
                AR5K_DIAG_SW_DIS_TX | AR5K_DIAG_SW_DIS_RX_5210);
        ath5k_hw_reg_write(ah, beacon, AR5K_BEACON_5210);

        return 0;
}
static int ath5k_hw_rf511x_calibrate ( struct ath5k_hw ah,
struct net80211_channel channel 
) [static]

Definition at line 1277 of file ath5k_phy.c.

References ath5k_hw::ah_calibration, AR5K_PHY_IQ, AR5K_PHY_IQ_CAL_NUM_LOG_MAX, AR5K_PHY_IQ_CORR_ENABLE, AR5K_PHY_IQ_CORR_Q_I_COFF_S, AR5K_PHY_IQ_RUN, AR5K_PHY_IQRES_CAL_CORR, AR5K_PHY_IQRES_CAL_PWR_I, AR5K_PHY_IQRES_CAL_PWR_Q, AR5K_REG_ENABLE_BITS, AR5K_REG_WRITE_BITS, ath5k_hw_noise_floor_calibration(), ath5k_hw_reg_read(), ath5k_hw_request_rfgain_probe(), net80211_channel::center_freq, and done.

Referenced by ath5k_hw_phy_calibrate().

{
        u32 i_pwr, q_pwr;
        s32 iq_corr, i_coff, i_coffd, q_coff, q_coffd;
        int i;

        if (!ah->ah_calibration ||
                ath5k_hw_reg_read(ah, AR5K_PHY_IQ) & AR5K_PHY_IQ_RUN)
                goto done;

        /* Calibration has finished, get the results and re-run */
        for (i = 0; i <= 10; i++) {
                iq_corr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_CORR);
                i_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_I);
                q_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_Q);
        }

        i_coffd = ((i_pwr >> 1) + (q_pwr >> 1)) >> 7;
        q_coffd = q_pwr >> 7;

        /* No correction */
        if (i_coffd == 0 || q_coffd == 0)
                goto done;

        i_coff = ((-iq_corr) / i_coffd) & 0x3f;

        /* Boundary check */
        if (i_coff > 31)
                i_coff = 31;
        if (i_coff < -32)
                i_coff = -32;

        q_coff = (((s32)i_pwr / q_coffd) - 128) & 0x1f;

        /* Boundary check */
        if (q_coff > 15)
                q_coff = 15;
        if (q_coff < -16)
                q_coff = -16;

        /* Commit new I/Q value */
        AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_ENABLE |
                ((u32)q_coff) | ((u32)i_coff << AR5K_PHY_IQ_CORR_Q_I_COFF_S));

        /* Re-enable calibration -if we don't we'll commit
         * the same values again and again */
        AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ,
                        AR5K_PHY_IQ_CAL_NUM_LOG_MAX, 15);
        AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_RUN);

done:

        /* TODO: Separate noise floor calibration from I/Q calibration
         * since noise floor calibration interrupts rx path while I/Q
         * calibration doesn't. We don't need to run noise floor calibration
         * as often as I/Q calibration.*/
        ath5k_hw_noise_floor_calibration(ah, channel->center_freq);

        /* Initiate a gain_F calibration */
        ath5k_hw_request_rfgain_probe(ah);

        return 0;
}
int ath5k_hw_phy_calibrate ( struct ath5k_hw ah,
struct net80211_channel channel 
)

Definition at line 1345 of file ath5k_phy.c.

References ath5k_hw::ah_radio, AR5K_RF5110, ath5k_hw_rf5110_calibrate(), ath5k_hw_rf511x_calibrate(), and ret.

Referenced by ath5k_calibrate().

{
        int ret;

        if (ah->ah_radio == AR5K_RF5110)
                ret = ath5k_hw_rf5110_calibrate(ah, channel);
        else
                ret = ath5k_hw_rf511x_calibrate(ah, channel);

        return ret;
}
int ath5k_hw_phy_disable ( struct ath5k_hw ah)

Definition at line 1358 of file ath5k_phy.c.

References AR5K_PHY_ACT, AR5K_PHY_ACT_DISABLE, and ath5k_hw_reg_write().

Referenced by ath5k_stop_hw().

u16 ath5k_hw_radio_revision ( struct ath5k_hw ah,
unsigned int  chan 
)

Definition at line 1372 of file ath5k_phy.c.

References ath5k_hw::ah_version, AR5K_AR5210, AR5K_PHY, AR5K_PHY_SHIFT_2GHZ, AR5K_PHY_SHIFT_5GHZ, ath5k_hw_bitswap(), ath5k_hw_reg_read(), ath5k_hw_reg_write(), CHANNEL_2GHZ, CHANNEL_5GHZ, mdelay(), ret, and u16.

Referenced by ath5k_hw_attach().

{
        unsigned int i;
        u32 srev;
        u16 ret;

        /*
         * Set the radio chip access register
         */
        switch (chan) {
        case CHANNEL_2GHZ:
                ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_2GHZ, AR5K_PHY(0));
                break;
        case CHANNEL_5GHZ:
                ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0));
                break;
        default:
                return 0;
        }

        mdelay(2);

        /* ...wait until PHY is ready and read the selected radio revision */
        ath5k_hw_reg_write(ah, 0x00001c16, AR5K_PHY(0x34));

        for (i = 0; i < 8; i++)
                ath5k_hw_reg_write(ah, 0x00010000, AR5K_PHY(0x20));

        if (ah->ah_version == AR5K_AR5210) {
                srev = ath5k_hw_reg_read(ah, AR5K_PHY(256) >> 28) & 0xf;
                ret = (u16)ath5k_hw_bitswap(srev, 4) + 1;
        } else {
                srev = (ath5k_hw_reg_read(ah, AR5K_PHY(0x100)) >> 24) & 0xff;
                ret = (u16)ath5k_hw_bitswap(((srev & 0xf0) >> 4) |
                                ((srev & 0x0f) << 4), 8);
        }

        /* Reset to the 5GHz mode */
        ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0));

        return ret;
}
void ath5k_hw_set_def_antenna ( struct ath5k_hw ah,
unsigned int  ant 
)
unsigned int ath5k_hw_get_def_antenna ( struct ath5k_hw ah)

Definition at line 1422 of file ath5k_phy.c.

References ath5k_hw::ah_version, AR5K_AR5210, AR5K_DEFAULT_ANTENNA, and ath5k_hw_reg_read().

{
        if (ah->ah_version != AR5K_AR5210)
                return ath5k_hw_reg_read(ah, AR5K_DEFAULT_ANTENNA);

        return 0; /*XXX: What do we return for 5210 ?*/
}
static s16 ath5k_get_interpolated_value ( s16  target,
s16  x_left,
s16  x_right,
s16  y_left,
s16  y_right 
) [static]

Definition at line 1443 of file ath5k_phy.c.

Referenced by ath5k_create_power_curve(), ath5k_get_linear_pcdac_min(), ath5k_get_rate_pcal_data(), and ath5k_setup_channel_powertable().

{
        s16 ratio, result;

        /* Avoid divide by zero and skip interpolation
         * if we have the same point */
        if ((x_left == x_right) || (y_left == y_right))
                return y_left;

        /*
         * Since we use ints and not fps, we need to scale up in
         * order to get a sane ratio value (or else we 'll eg. get
         * always 1 instead of 1.25, 1.75 etc). We scale up by 100
         * to have some accuracy both for 0.5 and 0.25 steps.
         */
        ratio = ((100 * y_right - 100 * y_left)/(x_right - x_left));

        /* Now scale down to be in range */
        result = y_left + (ratio * (target - x_left) / 100);

        return result;
}
static s16 ath5k_get_linear_pcdac_min ( const u8 stepL,
const u8 stepR,
const s16 pwrL,
const s16 pwrR 
) [static]

Definition at line 1476 of file ath5k_phy.c.

References ath5k_get_interpolated_value(), and max.

Referenced by ath5k_setup_channel_powertable().

{
        s8 tmp;
        s16 min_pwrL, min_pwrR;
        s16 pwr_i;

        if (pwrL[0] == pwrL[1])
                min_pwrL = pwrL[0];
        else {
                pwr_i = pwrL[0];
                do {
                        pwr_i--;
                        tmp = (s8) ath5k_get_interpolated_value(pwr_i,
                                                        pwrL[0], pwrL[1],
                                                        stepL[0], stepL[1]);
                } while (tmp > 1);

                min_pwrL = pwr_i;
        }

        if (pwrR[0] == pwrR[1])
                min_pwrR = pwrR[0];
        else {
                pwr_i = pwrR[0];
                do {
                        pwr_i--;
                        tmp = (s8) ath5k_get_interpolated_value(pwr_i,
                                                        pwrR[0], pwrR[1],
                                                        stepR[0], stepR[1]);
                } while (tmp > 1);

                min_pwrR = pwr_i;
        }

        /* Keep the right boundary so that it works for both curves */
        return max(min_pwrL, min_pwrR);
}
static void ath5k_create_power_curve ( s16  pmin,
s16  pmax,
const s16 pwr,
const u8 vpd,
u8  num_points,
u8 vpd_table,
u8  type 
) [static]

Definition at line 1528 of file ath5k_phy.c.

References AR5K_EEPROM_POWER_TABLE_SIZE, AR5K_PWRTABLE_LINEAR_PCDAC, ath5k_get_interpolated_value(), and u16.

Referenced by ath5k_setup_channel_powertable().

{
        u8 idx[2] = { 0, 1 };
        s16 pwr_i = 2*pmin;
        int i;

        if (num_points < 2)
                return;

        /* We want the whole line, so adjust boundaries
         * to cover the entire power range. Note that
         * power values are already 0.25dB so no need
         * to multiply pwr_i by 2 */
        if (type == AR5K_PWRTABLE_LINEAR_PCDAC) {
                pwr_i = pmin;
                pmin = 0;
                pmax = 63;
        }

        /* Find surrounding turning points (TPs)
         * and interpolate between them */
        for (i = 0; (i <= (u16) (pmax - pmin)) &&
        (i < AR5K_EEPROM_POWER_TABLE_SIZE); i++) {

                /* We passed the right TP, move to the next set of TPs
                 * if we pass the last TP, extrapolate above using the last
                 * two TPs for ratio */
                if ((pwr_i > pwr[idx[1]]) && (idx[1] < num_points - 1)) {
                        idx[0]++;
                        idx[1]++;
                }

                vpd_table[i] = (u8) ath5k_get_interpolated_value(pwr_i,
                                                pwr[idx[0]], pwr[idx[1]],
                                                vpd[idx[0]], vpd[idx[1]]);

                /* Increase by 0.5dB
                 * (0.25 dB units) */
                pwr_i += 2;
        }
}
static void ath5k_get_chan_pcal_surrounding_piers ( struct ath5k_hw ah,
struct net80211_channel channel,
struct ath5k_chan_pcal_info **  pcinfo_l,
struct ath5k_chan_pcal_info **  pcinfo_r 
) [static]

Definition at line 1580 of file ath5k_phy.c.

References ath5k_hw::ah_capabilities, AR5K_EEPROM_MODE_11A, AR5K_EEPROM_MODE_11B, AR5K_EEPROM_MODE_11G, ath5k_capabilities::cap_eeprom, net80211_channel::center_freq, CHANNEL_2GHZ, CHANNEL_OFDM, done, ath5k_eeprom_info::ee_n_piers, ath5k_eeprom_info::ee_pwr_cal_a, ath5k_eeprom_info::ee_pwr_cal_b, ath5k_eeprom_info::ee_pwr_cal_g, ath5k_chan_pcal_info::freq, net80211_channel::hw_value, and max.

Referenced by ath5k_setup_channel_powertable().

{
        struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
        struct ath5k_chan_pcal_info *pcinfo;
        u8 idx_l, idx_r;
        u8 mode, max, i;
        u32 target = channel->center_freq;

        idx_l = 0;
        idx_r = 0;

        if (!(channel->hw_value & CHANNEL_OFDM)) {
                pcinfo = ee->ee_pwr_cal_b;
                mode = AR5K_EEPROM_MODE_11B;
        } else if (channel->hw_value & CHANNEL_2GHZ) {
                pcinfo = ee->ee_pwr_cal_g;
                mode = AR5K_EEPROM_MODE_11G;
        } else {
                pcinfo = ee->ee_pwr_cal_a;
                mode = AR5K_EEPROM_MODE_11A;
        }
        max = ee->ee_n_piers[mode] - 1;

        /* Frequency is below our calibrated
         * range. Use the lowest power curve
         * we have */
        if (target < pcinfo[0].freq) {
                idx_l = idx_r = 0;
                goto done;
        }

        /* Frequency is above our calibrated
         * range. Use the highest power curve
         * we have */
        if (target > pcinfo[max].freq) {
                idx_l = idx_r = max;
                goto done;
        }

        /* Frequency is inside our calibrated
         * channel range. Pick the surrounding
         * calibration piers so that we can
         * interpolate */
        for (i = 0; i <= max; i++) {

                /* Frequency matches one of our calibration
                 * piers, no need to interpolate, just use
                 * that calibration pier */
                if (pcinfo[i].freq == target) {
                        idx_l = idx_r = i;
                        goto done;
                }

                /* We found a calibration pier that's above
                 * frequency, use this pier and the previous
                 * one to interpolate */
                if (target < pcinfo[i].freq) {
                        idx_r = i;
                        idx_l = idx_r - 1;
                        goto done;
                }
        }

done:
        *pcinfo_l = &pcinfo[idx_l];
        *pcinfo_r = &pcinfo[idx_r];

        return;
}
static void ath5k_get_rate_pcal_data ( struct ath5k_hw ah,
struct net80211_channel channel,
struct ath5k_rate_pcal_info rates 
) [static]

Definition at line 1660 of file ath5k_phy.c.

References ath5k_hw::ah_capabilities, AR5K_EEPROM_MODE_11A, AR5K_EEPROM_MODE_11B, AR5K_EEPROM_MODE_11G, ath5k_get_interpolated_value(), ath5k_capabilities::cap_eeprom, net80211_channel::center_freq, CHANNEL_2GHZ, CHANNEL_OFDM, done, ath5k_eeprom_info::ee_rate_target_pwr_num, ath5k_eeprom_info::ee_rate_tpwr_a, ath5k_eeprom_info::ee_rate_tpwr_b, ath5k_eeprom_info::ee_rate_tpwr_g, ath5k_rate_pcal_info::freq, net80211_channel::hw_value, max, ath5k_rate_pcal_info::target_power_36, ath5k_rate_pcal_info::target_power_48, ath5k_rate_pcal_info::target_power_54, and ath5k_rate_pcal_info::target_power_6to24.

Referenced by ath5k_hw_txpower().

{
        struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
        struct ath5k_rate_pcal_info *rpinfo;
        u8 idx_l, idx_r;
        u8 mode, max, i;
        u32 target = channel->center_freq;

        idx_l = 0;
        idx_r = 0;

        if (!(channel->hw_value & CHANNEL_OFDM)) {
                rpinfo = ee->ee_rate_tpwr_b;
                mode = AR5K_EEPROM_MODE_11B;
        } else if (channel->hw_value & CHANNEL_2GHZ) {
                rpinfo = ee->ee_rate_tpwr_g;
                mode = AR5K_EEPROM_MODE_11G;
        } else {
                rpinfo = ee->ee_rate_tpwr_a;
                mode = AR5K_EEPROM_MODE_11A;
        }
        max = ee->ee_rate_target_pwr_num[mode] - 1;

        /* Get the surrounding calibration
         * piers - same as above */
        if (target < rpinfo[0].freq) {
                idx_l = idx_r = 0;
                goto done;
        }

        if (target > rpinfo[max].freq) {
                idx_l = idx_r = max;
                goto done;
        }

        for (i = 0; i <= max; i++) {

                if (rpinfo[i].freq == target) {
                        idx_l = idx_r = i;
                        goto done;
                }

                if (target < rpinfo[i].freq) {
                        idx_r = i;
                        idx_l = idx_r - 1;
                        goto done;
                }
        }

done:
        /* Now interpolate power value, based on the frequency */
        rates->freq = target;

        rates->target_power_6to24 =
                ath5k_get_interpolated_value(target, rpinfo[idx_l].freq,
                                        rpinfo[idx_r].freq,
                                        rpinfo[idx_l].target_power_6to24,
                                        rpinfo[idx_r].target_power_6to24);

        rates->target_power_36 =
                ath5k_get_interpolated_value(target, rpinfo[idx_l].freq,
                                        rpinfo[idx_r].freq,
                                        rpinfo[idx_l].target_power_36,
                                        rpinfo[idx_r].target_power_36);

        rates->target_power_48 =
                ath5k_get_interpolated_value(target, rpinfo[idx_l].freq,
                                        rpinfo[idx_r].freq,
                                        rpinfo[idx_l].target_power_48,
                                        rpinfo[idx_r].target_power_48);

        rates->target_power_54 =
                ath5k_get_interpolated_value(target, rpinfo[idx_l].freq,
                                        rpinfo[idx_r].freq,
                                        rpinfo[idx_l].target_power_54,
                                        rpinfo[idx_r].target_power_54);
}
static void ath5k_get_max_ctl_power ( struct ath5k_hw ah,
struct net80211_channel channel 
) [static]

Definition at line 1748 of file ath5k_phy.c.

References ath5k_hw::ah_capabilities, ath5k_hw::ah_txpower, AR5K_CTL_11A, AR5K_CTL_11B, AR5K_CTL_11G, AR5K_CTL_NO_REGDOMAIN, AR5K_CTL_TURBO, AR5K_CTL_TURBOG, AR5K_EEPROM_N_EDGES, ath5k_capabilities::cap_eeprom, net80211_channel::center_freq, CHANNEL_A, CHANNEL_B, CHANNEL_G, CHANNEL_MODES, CHANNEL_T, CHANNEL_TG, CHANNEL_XR, ath5k_edge_power::edge, ath5k_eeprom_info::ee_ctl, ath5k_eeprom_info::ee_ctl_pwr, ath5k_eeprom_info::ee_ctls, ath5k_edge_power::freq, net80211_channel::hw_value, min, and ath5k_hw::txp_max_pwr.

Referenced by ath5k_hw_txpower().

{
        struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
        struct ath5k_edge_power *rep = ee->ee_ctl_pwr;
        u8 *ctl_val = ee->ee_ctl;
        s16 max_chan_pwr = ah->ah_txpower.txp_max_pwr / 4;
        s16 edge_pwr = 0;
        u8 rep_idx;
        u8 i, ctl_mode;
        u8 ctl_idx = 0xFF;
        u32 target = channel->center_freq;

        /* Find out a CTL for our mode that's not mapped
         * on a specific reg domain.
         *
         * TODO: Map our current reg domain to one of the 3 available
         * reg domain ids so that we can support more CTLs. */
        switch (channel->hw_value & CHANNEL_MODES) {
        case CHANNEL_A:
                ctl_mode = AR5K_CTL_11A | AR5K_CTL_NO_REGDOMAIN;
                break;
        case CHANNEL_G:
                ctl_mode = AR5K_CTL_11G | AR5K_CTL_NO_REGDOMAIN;
                break;
        case CHANNEL_B:
                ctl_mode = AR5K_CTL_11B | AR5K_CTL_NO_REGDOMAIN;
                break;
        case CHANNEL_T:
                ctl_mode = AR5K_CTL_TURBO | AR5K_CTL_NO_REGDOMAIN;
                break;
        case CHANNEL_TG:
                ctl_mode = AR5K_CTL_TURBOG | AR5K_CTL_NO_REGDOMAIN;
                break;
        case CHANNEL_XR:
                /* Fall through */
        default:
                return;
        }

        for (i = 0; i < ee->ee_ctls; i++) {
                if (ctl_val[i] == ctl_mode) {
                        ctl_idx = i;
                        break;
                }
        }

        /* If we have a CTL dataset available grab it and find the
         * edge power for our frequency */
        if (ctl_idx == 0xFF)
                return;

        /* Edge powers are sorted by frequency from lower
         * to higher. Each CTL corresponds to 8 edge power
         * measurements. */
        rep_idx = ctl_idx * AR5K_EEPROM_N_EDGES;

        /* Don't do boundaries check because we
         * might have more that one bands defined
         * for this mode */

        /* Get the edge power that's closer to our
         * frequency */
        for (i = 0; i < AR5K_EEPROM_N_EDGES; i++) {
                rep_idx += i;
                if (target <= rep[rep_idx].freq)
                        edge_pwr = (s16) rep[rep_idx].edge;
        }

        if (edge_pwr) {
                ah->ah_txpower.txp_max_pwr = 4*min(edge_pwr, max_chan_pwr);
        }
}
static void ath5k_fill_pwr_to_pcdac_table ( struct ath5k_hw ah,
s16 table_min,
s16 table_max 
) [static]

Definition at line 1835 of file ath5k_phy.c.

References ath5k_hw::ah_txpower, AR5K_EEPROM_POWER_TABLE_SIZE, ath5k_hw::tmpL, and ath5k_hw::txp_pd_table.

Referenced by ath5k_setup_channel_powertable().

{
        u8      *pcdac_out = ah->ah_txpower.txp_pd_table;
        u8      *pcdac_tmp = ah->ah_txpower.tmpL[0];
        u8      pcdac_0, pcdac_n, pcdac_i, pwr_idx, i;
        s16     min_pwr, max_pwr;

        /* Get table boundaries */
        min_pwr = table_min[0];
        pcdac_0 = pcdac_tmp[0];

        max_pwr = table_max[0];
        pcdac_n = pcdac_tmp[table_max[0] - table_min[0]];

        /* Extrapolate below minimum using pcdac_0 */
        pcdac_i = 0;
        for (i = 0; i < min_pwr; i++)
                pcdac_out[pcdac_i++] = pcdac_0;

        /* Copy values from pcdac_tmp */
        pwr_idx = min_pwr;
        for (i = 0 ; pwr_idx <= max_pwr &&
        pcdac_i < AR5K_EEPROM_POWER_TABLE_SIZE; i++) {
                pcdac_out[pcdac_i++] = pcdac_tmp[i];
                pwr_idx++;
        }

        /* Extrapolate above maximum */
        while (pcdac_i < AR5K_EEPROM_POWER_TABLE_SIZE)
                pcdac_out[pcdac_i++] = pcdac_n;

}
static void ath5k_combine_linear_pcdac_curves ( struct ath5k_hw ah,
s16 table_min,
s16 table_max,
u8  pdcurves 
) [static]

Definition at line 1881 of file ath5k_phy.c.

References ath5k_hw::ah_txpower, ath5k_hw::tmpL, ath5k_hw::txp_min_idx, and ath5k_hw::txp_pd_table.

Referenced by ath5k_setup_channel_powertable().

{
        u8      *pcdac_out = ah->ah_txpower.txp_pd_table;
        u8      *pcdac_low_pwr;
        u8      *pcdac_high_pwr;
        u8      *pcdac_tmp;
        u8      pwr;
        s16     max_pwr_idx;
        s16     min_pwr_idx;
        s16     mid_pwr_idx = 0;
        /* Edge flag turs on the 7nth bit on the PCDAC
         * to delcare the higher power curve (force values
         * to be greater than 64). If we only have one curve
         * we don't need to set this, if we have 2 curves and
         * fill the table backwards this can also be used to
         * switch from higher power curve to lower power curve */
        u8      edge_flag;
        int     i;

        /* When we have only one curve available
         * that's the higher power curve. If we have
         * two curves the first is the high power curve
         * and the next is the low power curve. */
        if (pdcurves > 1) {
                pcdac_low_pwr = ah->ah_txpower.tmpL[1];
                pcdac_high_pwr = ah->ah_txpower.tmpL[0];
                mid_pwr_idx = table_max[1] - table_min[1] - 1;
                max_pwr_idx = (table_max[0] - table_min[0]) / 2;

                /* If table size goes beyond 31.5dB, keep the
                 * upper 31.5dB range when setting tx power.
                 * Note: 126 = 31.5 dB in quarter dB steps */
                if (table_max[0] - table_min[1] > 126)
                        min_pwr_idx = table_max[0] - 126;
                else
                        min_pwr_idx = table_min[1];

                /* Since we fill table backwards
                 * start from high power curve */
                pcdac_tmp = pcdac_high_pwr;

                edge_flag = 0x40;
        } else {
                pcdac_low_pwr = ah->ah_txpower.tmpL[1]; /* Zeroed */
                pcdac_high_pwr = ah->ah_txpower.tmpL[0];
                min_pwr_idx = table_min[0];
                max_pwr_idx = (table_max[0] - table_min[0]) / 2;
                pcdac_tmp = pcdac_high_pwr;
                edge_flag = 0;
        }

        /* This is used when setting tx power*/
        ah->ah_txpower.txp_min_idx = min_pwr_idx/2;

        /* Fill Power to PCDAC table backwards */
        pwr = max_pwr_idx;
        for (i = 63; i >= 0; i--) {
                /* Entering lower power range, reset
                 * edge flag and set pcdac_tmp to lower
                 * power curve.*/
                if (edge_flag == 0x40 &&
                (2*pwr <= (table_max[1] - table_min[0]) || pwr == 0)) {
                        edge_flag = 0x00;
                        pcdac_tmp = pcdac_low_pwr;
                        pwr = mid_pwr_idx/2;
                }

                /* Don't go below 1, extrapolate below if we have
                 * already swithced to the lower power curve -or
                 * we only have one curve and edge_flag is zero
                 * anyway */
                if (pcdac_tmp[pwr] < 1 && (edge_flag == 0x00)) {
                        while (i >= 0) {
                                pcdac_out[i] = pcdac_out[i + 1];
                                i--;
                        }
                        break;
                }

                pcdac_out[i] = pcdac_tmp[pwr] | edge_flag;

                /* Extrapolate above if pcdac is greater than
                 * 126 -this can happen because we OR pcdac_out
                 * value with edge_flag on high power curve */
                if (pcdac_out[i] > 126)
                        pcdac_out[i] = 126;

                /* Decrease by a 0.5dB step */
                pwr--;
        }
}
static void ath5k_setup_pcdac_table ( struct ath5k_hw ah) [static]

Definition at line 1976 of file ath5k_phy.c.

References ath5k_hw::ah_txpower, AR5K_EEPROM_POWER_TABLE_SIZE, AR5K_PHY_PCDAC_TXPOWER, ath5k_hw_reg_write(), and ath5k_hw::txp_pd_table.

Referenced by ath5k_setup_channel_powertable().

{
        u8      *pcdac_out = ah->ah_txpower.txp_pd_table;
        int     i;

        /*
         * Write TX power values
         */
        for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) {
                ath5k_hw_reg_write(ah,
                        (((pcdac_out[2*i + 0] << 8 | 0xff) & 0xffff) << 0) |
                        (((pcdac_out[2*i + 1] << 8 | 0xff) & 0xffff) << 16),
                        AR5K_PHY_PCDAC_TXPOWER(i));
        }
}
static void ath5k_combine_pwr_to_pdadc_curves ( struct ath5k_hw ah,
s16 pwr_min,
s16 pwr_max,
u8  pdcurves 
) [static]

Definition at line 2005 of file ath5k_phy.c.

References ath5k_hw::ah_txpower, AR5K_EEPROM_N_PD_GAINS, AR5K_EEPROM_POWER_TABLE_SIZE, AR5K_PHY_TPC_RG5, AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_1, AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_2, AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_3, AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_4, AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP, AR5K_REG_SM, AR5K_TUNE_MAX_TXPOWER, ath5k_hw_reg_read(), ath5k_hw_reg_write(), ath5k_hw::tmpL, ath5k_hw::txp_min_idx, and ath5k_hw::txp_pd_table.

Referenced by ath5k_setup_channel_powertable().

{
        u8 gain_boundaries[AR5K_EEPROM_N_PD_GAINS];
        u8 *pdadc_out = ah->ah_txpower.txp_pd_table;
        u8 *pdadc_tmp;
        s16 pdadc_0;
        u8 pdadc_i, pdadc_n, pwr_step, pdg, max_idx, table_size;
        u8 pd_gain_overlap;

        /* Note: Register value is initialized on initvals
         * there is no feedback from hw.
         * XXX: What about pd_gain_overlap from EEPROM ? */
        pd_gain_overlap = (u8) ath5k_hw_reg_read(ah, AR5K_PHY_TPC_RG5) &
                AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP;

        /* Create final PDADC table */
        for (pdg = 0, pdadc_i = 0; pdg < pdcurves; pdg++) {
                pdadc_tmp = ah->ah_txpower.tmpL[pdg];

                if (pdg == pdcurves - 1)
                        /* 2 dB boundary stretch for last
                         * (higher power) curve */
                        gain_boundaries[pdg] = pwr_max[pdg] + 4;
                else
                        /* Set gain boundary in the middle
                         * between this curve and the next one */
                        gain_boundaries[pdg] =
                                (pwr_max[pdg] + pwr_min[pdg + 1]) / 2;

                /* Sanity check in case our 2 db stretch got out of
                 * range. */
                if (gain_boundaries[pdg] > AR5K_TUNE_MAX_TXPOWER)
                        gain_boundaries[pdg] = AR5K_TUNE_MAX_TXPOWER;

                /* For the first curve (lower power)
                 * start from 0 dB */
                if (pdg == 0)
                        pdadc_0 = 0;
                else
                        /* For the other curves use the gain overlap */
                        pdadc_0 = (gain_boundaries[pdg - 1] - pwr_min[pdg]) -
                                                        pd_gain_overlap;

                /* Force each power step to be at least 0.5 dB */
                if ((pdadc_tmp[1] - pdadc_tmp[0]) > 1)
                        pwr_step = pdadc_tmp[1] - pdadc_tmp[0];
                else
                        pwr_step = 1;

                /* If pdadc_0 is negative, we need to extrapolate
                 * below this pdgain by a number of pwr_steps */
                while ((pdadc_0 < 0) && (pdadc_i < 128)) {
                        s16 tmp = pdadc_tmp[0] + pdadc_0 * pwr_step;
                        pdadc_out[pdadc_i++] = (tmp < 0) ? 0 : (u8) tmp;
                        pdadc_0++;
                }

                /* Set last pwr level, using gain boundaries */
                pdadc_n = gain_boundaries[pdg] + pd_gain_overlap - pwr_min[pdg];
                /* Limit it to be inside pwr range */
                table_size = pwr_max[pdg] - pwr_min[pdg];
                max_idx = (pdadc_n < table_size) ? pdadc_n : table_size;

                /* Fill pdadc_out table */
                while (pdadc_0 < max_idx)
                        pdadc_out[pdadc_i++] = pdadc_tmp[pdadc_0++];

                /* Need to extrapolate above this pdgain? */
                if (pdadc_n <= max_idx)
                        continue;

                /* Force each power step to be at least 0.5 dB */
                if ((pdadc_tmp[table_size - 1] - pdadc_tmp[table_size - 2]) > 1)
                        pwr_step = pdadc_tmp[table_size - 1] -
                                                pdadc_tmp[table_size - 2];
                else
                        pwr_step = 1;

                /* Extrapolate above */
                while ((pdadc_0 < (s16) pdadc_n) &&
                (pdadc_i < AR5K_EEPROM_POWER_TABLE_SIZE * 2)) {
                        s16 tmp = pdadc_tmp[table_size - 1] +
                                        (pdadc_0 - max_idx) * pwr_step;
                        pdadc_out[pdadc_i++] = (tmp > 127) ? 127 : (u8) tmp;
                        pdadc_0++;
                }
        }

        while (pdg < AR5K_EEPROM_N_PD_GAINS) {
                gain_boundaries[pdg] = gain_boundaries[pdg - 1];
                pdg++;
        }

        while (pdadc_i < AR5K_EEPROM_POWER_TABLE_SIZE * 2) {
                pdadc_out[pdadc_i] = pdadc_out[pdadc_i - 1];
                pdadc_i++;
        }

        /* Set gain boundaries */
        ath5k_hw_reg_write(ah,
                AR5K_REG_SM(pd_gain_overlap,
                        AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP) |
                AR5K_REG_SM(gain_boundaries[0],
                        AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_1) |
                AR5K_REG_SM(gain_boundaries[1],
                        AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_2) |
                AR5K_REG_SM(gain_boundaries[2],
                        AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_3) |
                AR5K_REG_SM(gain_boundaries[3],
                        AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_4),
                AR5K_PHY_TPC_RG5);

        /* Used for setting rate power table */
        ah->ah_txpower.txp_min_idx = pwr_min[0];

}
static void ath5k_setup_pwr_to_pdadc_table ( struct ath5k_hw ah,
u8  pdcurves,
u8 pdg_to_idx 
) [static]

Definition at line 2125 of file ath5k_phy.c.

References ath5k_hw::ah_txpower, AR5K_EEPROM_POWER_TABLE_SIZE, AR5K_PHY_PDADC_TXPOWER, AR5K_PHY_TPC_RG1, AR5K_PHY_TPC_RG1_NUM_PD_GAIN, AR5K_PHY_TPC_RG1_PDGAIN_1, AR5K_PHY_TPC_RG1_PDGAIN_2, AR5K_PHY_TPC_RG1_PDGAIN_3, AR5K_REG_SM, ath5k_hw_reg_read(), ath5k_hw_reg_write(), reg, and ath5k_hw::txp_pd_table.

Referenced by ath5k_setup_channel_powertable().

{
        u8 *pdadc_out = ah->ah_txpower.txp_pd_table;
        u32 reg;
        u8 i;

        /* Select the right pdgain curves */

        /* Clear current settings */
        reg = ath5k_hw_reg_read(ah, AR5K_PHY_TPC_RG1);
        reg &= ~(AR5K_PHY_TPC_RG1_PDGAIN_1 |
                AR5K_PHY_TPC_RG1_PDGAIN_2 |
                AR5K_PHY_TPC_RG1_PDGAIN_3 |
                AR5K_PHY_TPC_RG1_NUM_PD_GAIN);

        /*
         * Use pd_gains curve from eeprom
         *
         * This overrides the default setting from initvals
         * in case some vendors (e.g. Zcomax) don't use the default
         * curves. If we don't honor their settings we 'll get a
         * 5dB (1 * gain overlap ?) drop.
         */
        reg |= AR5K_REG_SM(pdcurves, AR5K_PHY_TPC_RG1_NUM_PD_GAIN);

        switch (pdcurves) {
        case 3:
                reg |= AR5K_REG_SM(pdg_to_idx[2], AR5K_PHY_TPC_RG1_PDGAIN_3);
                /* Fall through */
        case 2:
                reg |= AR5K_REG_SM(pdg_to_idx[1], AR5K_PHY_TPC_RG1_PDGAIN_2);
                /* Fall through */
        case 1:
                reg |= AR5K_REG_SM(pdg_to_idx[0], AR5K_PHY_TPC_RG1_PDGAIN_1);
                break;
        }
        ath5k_hw_reg_write(ah, reg, AR5K_PHY_TPC_RG1);

        /*
         * Write TX power values
         */
        for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) {
                ath5k_hw_reg_write(ah,
                        ((pdadc_out[4*i + 0] & 0xff) << 0) |
                        ((pdadc_out[4*i + 1] & 0xff) << 8) |
                        ((pdadc_out[4*i + 2] & 0xff) << 16) |
                        ((pdadc_out[4*i + 3] & 0xff) << 24),
                        AR5K_PHY_PDADC_TXPOWER(i));
        }
}
static int ath5k_setup_channel_powertable ( struct ath5k_hw ah,
struct net80211_channel channel,
u8  ee_mode,
u8  type 
) [static]

Definition at line 2190 of file ath5k_phy.c.

References ath5k_hw::ah_capabilities, ath5k_hw::ah_txpower, AR5K_EEPROM_N_PD_GAINS, AR5K_EEPROM_POWER_TABLE_SIZE, AR5K_PWRTABLE_LINEAR_PCDAC, AR5K_PWRTABLE_PWR_TO_PCDAC, AR5K_PWRTABLE_PWR_TO_PDADC, ath5k_combine_linear_pcdac_curves(), ath5k_combine_pwr_to_pdadc_curves(), ath5k_create_power_curve(), ath5k_fill_pwr_to_pcdac_table(), ath5k_get_chan_pcal_surrounding_piers(), ath5k_get_interpolated_value(), ath5k_get_linear_pcdac_min(), ath5k_setup_pcdac_table(), ath5k_setup_pwr_to_pdadc_table(), ath5k_capabilities::cap_eeprom, net80211_channel::center_freq, ath5k_eeprom_info::ee_pd_gains, ath5k_eeprom_info::ee_pdc_to_idx, EINVAL, ath5k_chan_pcal_info::freq, max, ath5k_chan_pcal_info::max_pwr, min, ath5k_chan_pcal_info::min_pwr, ath5k_chan_pcal_info::pd_curves, ath5k_pdgain_info::pd_points, ath5k_pdgain_info::pd_pwr, ath5k_pdgain_info::pd_step, ath5k_hw::tmpL, ath5k_hw::tmpR, ath5k_hw::txp_max_pwr, ath5k_hw::txp_min_idx, ath5k_hw::txp_min_pwr, ath5k_hw::txp_offset, and u16.

Referenced by ath5k_hw_txpower().

{
        struct ath5k_pdgain_info *pdg_L, *pdg_R;
        struct ath5k_chan_pcal_info *pcinfo_L;
        struct ath5k_chan_pcal_info *pcinfo_R;
        struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
        u8 *pdg_curve_to_idx = ee->ee_pdc_to_idx[ee_mode];
        s16 table_min[AR5K_EEPROM_N_PD_GAINS];
        s16 table_max[AR5K_EEPROM_N_PD_GAINS];
        u8 *tmpL;
        u8 *tmpR;
        u32 target = channel->center_freq;
        int pdg, i;

        /* Get surounding freq piers for this channel */
        ath5k_get_chan_pcal_surrounding_piers(ah, channel,
                                                &pcinfo_L,
                                                &pcinfo_R);

        /* Loop over pd gain curves on
         * surounding freq piers by index */
        for (pdg = 0; pdg < ee->ee_pd_gains[ee_mode]; pdg++) {

                /* Fill curves in reverse order
                 * from lower power (max gain)
                 * to higher power. Use curve -> idx
                 * backmaping we did on eeprom init */
                u8 idx = pdg_curve_to_idx[pdg];

                /* Grab the needed curves by index */
                pdg_L = &pcinfo_L->pd_curves[idx];
                pdg_R = &pcinfo_R->pd_curves[idx];

                /* Initialize the temp tables */
                tmpL = ah->ah_txpower.tmpL[pdg];
                tmpR = ah->ah_txpower.tmpR[pdg];

                /* Set curve's x boundaries and create
                 * curves so that they cover the same
                 * range (if we don't do that one table
                 * will have values on some range and the
                 * other one won't have any so interpolation
                 * will fail) */
                table_min[pdg] = min(pdg_L->pd_pwr[0],
                                        pdg_R->pd_pwr[0]) / 2;

                table_max[pdg] = max(pdg_L->pd_pwr[pdg_L->pd_points - 1],
                                pdg_R->pd_pwr[pdg_R->pd_points - 1]) / 2;

                /* Now create the curves on surrounding channels
                 * and interpolate if needed to get the final
                 * curve for this gain on this channel */
                switch (type) {
                case AR5K_PWRTABLE_LINEAR_PCDAC:
                        /* Override min/max so that we don't loose
                         * accuracy (don't divide by 2) */
                        table_min[pdg] = min(pdg_L->pd_pwr[0],
                                                pdg_R->pd_pwr[0]);

                        table_max[pdg] =
                                max(pdg_L->pd_pwr[pdg_L->pd_points - 1],
                                        pdg_R->pd_pwr[pdg_R->pd_points - 1]);

                        /* Override minimum so that we don't get
                         * out of bounds while extrapolating
                         * below. Don't do this when we have 2
                         * curves and we are on the high power curve
                         * because table_min is ok in this case */
                        if (!(ee->ee_pd_gains[ee_mode] > 1 && pdg == 0)) {

                                table_min[pdg] =
                                        ath5k_get_linear_pcdac_min(pdg_L->pd_step,
                                                                pdg_R->pd_step,
                                                                pdg_L->pd_pwr,
                                                                pdg_R->pd_pwr);

                                /* Don't go too low because we will
                                 * miss the upper part of the curve.
                                 * Note: 126 = 31.5dB (max power supported)
                                 * in 0.25dB units */
                                if (table_max[pdg] - table_min[pdg] > 126)
                                        table_min[pdg] = table_max[pdg] - 126;
                        }

                        /* Fall through */
                case AR5K_PWRTABLE_PWR_TO_PCDAC:
                case AR5K_PWRTABLE_PWR_TO_PDADC:

                        ath5k_create_power_curve(table_min[pdg],
                                                table_max[pdg],
                                                pdg_L->pd_pwr,
                                                pdg_L->pd_step,
                                                pdg_L->pd_points, tmpL, type);

                        /* We are in a calibration
                         * pier, no need to interpolate
                         * between freq piers */
                        if (pcinfo_L == pcinfo_R)
                                continue;

                        ath5k_create_power_curve(table_min[pdg],
                                                table_max[pdg],
                                                pdg_R->pd_pwr,
                                                pdg_R->pd_step,
                                                pdg_R->pd_points, tmpR, type);
                        break;
                default:
                        return -EINVAL;
                }

                /* Interpolate between curves
                 * of surounding freq piers to
                 * get the final curve for this
                 * pd gain. Re-use tmpL for interpolation
                 * output */
                for (i = 0; (i < (u16) (table_max[pdg] - table_min[pdg])) &&
                (i < AR5K_EEPROM_POWER_TABLE_SIZE); i++) {
                        tmpL[i] = (u8) ath5k_get_interpolated_value(target,
                                                        (s16) pcinfo_L->freq,
                                                        (s16) pcinfo_R->freq,
                                                        (s16) tmpL[i],
                                                        (s16) tmpR[i]);
                }
        }

        /* Now we have a set of curves for this
         * channel on tmpL (x range is table_max - table_min
         * and y values are tmpL[pdg][]) sorted in the same
         * order as EEPROM (because we've used the backmaping).
         * So for RF5112 it's from higher power to lower power
         * and for RF2413 it's from lower power to higher power.
         * For RF5111 we only have one curve. */

        /* Fill min and max power levels for this
         * channel by interpolating the values on
         * surounding channels to complete the dataset */
        ah->ah_txpower.txp_min_pwr = ath5k_get_interpolated_value(target,
                                        (s16) pcinfo_L->freq,
                                        (s16) pcinfo_R->freq,
                                        pcinfo_L->min_pwr, pcinfo_R->min_pwr);

        ah->ah_txpower.txp_max_pwr = ath5k_get_interpolated_value(target,
                                        (s16) pcinfo_L->freq,
                                        (s16) pcinfo_R->freq,
                                        pcinfo_L->max_pwr, pcinfo_R->max_pwr);

        /* We are ready to go, fill PCDAC/PDADC
         * table and write settings on hardware */
        switch (type) {
        case AR5K_PWRTABLE_LINEAR_PCDAC:
                /* For RF5112 we can have one or two curves
                 * and each curve covers a certain power lvl
                 * range so we need to do some more processing */
                ath5k_combine_linear_pcdac_curves(ah, table_min, table_max,
                                                ee->ee_pd_gains[ee_mode]);

                /* Set txp.offset so that we can
                 * match max power value with max
                 * table index */
                ah->ah_txpower.txp_offset = 64 - (table_max[0] / 2);

                /* Write settings on hw */
                ath5k_setup_pcdac_table(ah);
                break;
        case AR5K_PWRTABLE_PWR_TO_PCDAC:
                /* We are done for RF5111 since it has only
                 * one curve, just fit the curve on the table */
                ath5k_fill_pwr_to_pcdac_table(ah, table_min, table_max);

                /* No rate powertable adjustment for RF5111 */
                ah->ah_txpower.txp_min_idx = 0;
                ah->ah_txpower.txp_offset = 0;

                /* Write settings on hw */
                ath5k_setup_pcdac_table(ah);
                break;
        case AR5K_PWRTABLE_PWR_TO_PDADC:
                /* Set PDADC boundaries and fill
                 * final PDADC table */
                ath5k_combine_pwr_to_pdadc_curves(ah, table_min, table_max,
                                                ee->ee_pd_gains[ee_mode]);

                /* Write settings on hw */
                ath5k_setup_pwr_to_pdadc_table(ah, pdg, pdg_curve_to_idx);

                /* Set txp.offset, note that table_min
                 * can be negative */
                ah->ah_txpower.txp_offset = table_min[0];
                break;
        default:
                return -EINVAL;
        }

        return 0;
}
static void ath5k_setup_rate_powertable ( struct ath5k_hw ah,
u16  max_pwr,
struct ath5k_rate_pcal_info rate_info,
u8  ee_mode 
) [static]

Definition at line 2412 of file ath5k_phy.c.

References ath5k_hw::ah_phy_revision, ath5k_hw::ah_txpower, AR5K_EEPROM_MODE_11G, AR5K_SREV_PHY_5212A, min, rates, ath5k_rate_pcal_info::target_power_36, ath5k_rate_pcal_info::target_power_48, ath5k_rate_pcal_info::target_power_54, ath5k_rate_pcal_info::target_power_6to24, ath5k_hw::txp_cck_ofdm_gainf_delta, ath5k_hw::txp_max_pwr, ath5k_hw::txp_min_pwr, ath5k_hw::txp_ofdm, and ath5k_hw::txp_rates_power_table.

Referenced by ath5k_hw_txpower().

{
        unsigned int i;
        u16 *rates;

        /* max_pwr is power level we got from driver/user in 0.5dB
         * units, switch to 0.25dB units so we can compare */
        max_pwr *= 2;
        max_pwr = min(max_pwr, (u16) ah->ah_txpower.txp_max_pwr) / 2;

        /* apply rate limits */
        rates = ah->ah_txpower.txp_rates_power_table;

        /* OFDM rates 6 to 24Mb/s */
        for (i = 0; i < 5; i++)
                rates[i] = min(max_pwr, rate_info->target_power_6to24);

        /* Rest OFDM rates */
        rates[5] = min(rates[0], rate_info->target_power_36);
        rates[6] = min(rates[0], rate_info->target_power_48);
        rates[7] = min(rates[0], rate_info->target_power_54);

        /* CCK rates */
        /* 1L */
        rates[8] = min(rates[0], rate_info->target_power_6to24);
        /* 2L */
        rates[9] = min(rates[0], rate_info->target_power_36);
        /* 2S */
        rates[10] = min(rates[0], rate_info->target_power_36);
        /* 5L */
        rates[11] = min(rates[0], rate_info->target_power_48);
        /* 5S */
        rates[12] = min(rates[0], rate_info->target_power_48);
        /* 11L */
        rates[13] = min(rates[0], rate_info->target_power_54);
        /* 11S */
        rates[14] = min(rates[0], rate_info->target_power_54);

        /* XR rates */
        rates[15] = min(rates[0], rate_info->target_power_6to24);

        /* CCK rates have different peak to average ratio
         * so we have to tweak their power so that gainf
         * correction works ok. For this we use OFDM to
         * CCK delta from eeprom */
        if ((ee_mode == AR5K_EEPROM_MODE_11G) &&
        (ah->ah_phy_revision < AR5K_SREV_PHY_5212A))
                for (i = 8; i <= 15; i++)
                        rates[i] -= ah->ah_txpower.txp_cck_ofdm_gainf_delta;

        ah->ah_txpower.txp_min_pwr = rates[7];
        ah->ah_txpower.txp_max_pwr = rates[0];
        ah->ah_txpower.txp_ofdm = rates[7];
}
int ath5k_hw_txpower ( struct ath5k_hw ah,
struct net80211_channel channel,
u8  ee_mode,
u8  txpower 
)

Definition at line 2474 of file ath5k_phy.c.

References ath5k_hw::ah_radio, ath5k_hw::ah_txpower, AR5K_PHY_TXPOWER_RATE1, AR5K_PHY_TXPOWER_RATE2, AR5K_PHY_TXPOWER_RATE3, AR5K_PHY_TXPOWER_RATE4, AR5K_PHY_TXPOWER_RATE_MAX, AR5K_PHY_TXPOWER_RATE_MAX_TPC_ENABLE, AR5K_PWRTABLE_LINEAR_PCDAC, AR5K_PWRTABLE_PWR_TO_PCDAC, AR5K_PWRTABLE_PWR_TO_PDADC, AR5K_REG_MS, AR5K_RF2316, AR5K_RF2317, AR5K_RF2413, AR5K_RF2425, AR5K_RF5111, AR5K_RF5112, AR5K_RF5413, AR5K_TPC, AR5K_TPC_ACK, AR5K_TPC_CHIRP, AR5K_TPC_CTS, AR5K_TUNE_DEFAULT_TXPOWER, AR5K_TUNE_MAX_TXPOWER, AR5K_TUNE_TPC_TXPOWER, AR5K_TXPOWER_CCK, AR5K_TXPOWER_OFDM, ath5k_get_max_ctl_power(), ath5k_get_rate_pcal_data(), ath5k_hw_reg_write(), ath5k_setup_channel_powertable(), ath5k_setup_rate_powertable(), DBG, EINVAL, memset(), ret, ath5k_hw::txp_max_pwr, ath5k_hw::txp_min_pwr, ath5k_hw::txp_tpc, and type.

Referenced by ath5k_hw_reset(), and ath5k_hw_set_txpower_limit().

{
        struct ath5k_rate_pcal_info rate_info;
        u8 type;
        int ret;

        if (txpower > AR5K_TUNE_MAX_TXPOWER) {
                DBG("ath5k: invalid tx power %d\n", txpower);
                return -EINVAL;
        }
        if (txpower == 0)
                txpower = AR5K_TUNE_DEFAULT_TXPOWER;

        /* Reset TX power values */
        memset(&ah->ah_txpower, 0, sizeof(ah->ah_txpower));
        ah->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER;
        ah->ah_txpower.txp_min_pwr = 0;
        ah->ah_txpower.txp_max_pwr = AR5K_TUNE_MAX_TXPOWER;

        /* Initialize TX power table */
        switch (ah->ah_radio) {
        case AR5K_RF5111:
                type = AR5K_PWRTABLE_PWR_TO_PCDAC;
                break;
        case AR5K_RF5112:
                type = AR5K_PWRTABLE_LINEAR_PCDAC;
                break;
        case AR5K_RF2413:
        case AR5K_RF5413:
        case AR5K_RF2316:
        case AR5K_RF2317:
        case AR5K_RF2425:
                type = AR5K_PWRTABLE_PWR_TO_PDADC;
                break;
        default:
                return -EINVAL;
        }

        /* FIXME: Only on channel/mode change */
        ret = ath5k_setup_channel_powertable(ah, channel, ee_mode, type);
        if (ret)
                return ret;

        /* Limit max power if we have a CTL available */
        ath5k_get_max_ctl_power(ah, channel);

        /* FIXME: Tx power limit for this regdomain
         * XXX: Mac80211/CRDA will do that anyway ? */

        /* FIXME: Antenna reduction stuff */

        /* FIXME: Limit power on turbo modes */

        /* FIXME: TPC scale reduction */

        /* Get surounding channels for per-rate power table
         * calibration */
        ath5k_get_rate_pcal_data(ah, channel, &rate_info);

        /* Setup rate power table */
        ath5k_setup_rate_powertable(ah, txpower, &rate_info, ee_mode);

        /* Write rate power table on hw */
        ath5k_hw_reg_write(ah, AR5K_TXPOWER_OFDM(3, 24) |
                AR5K_TXPOWER_OFDM(2, 16) | AR5K_TXPOWER_OFDM(1, 8) |
                AR5K_TXPOWER_OFDM(0, 0), AR5K_PHY_TXPOWER_RATE1);

        ath5k_hw_reg_write(ah, AR5K_TXPOWER_OFDM(7, 24) |
                AR5K_TXPOWER_OFDM(6, 16) | AR5K_TXPOWER_OFDM(5, 8) |
                AR5K_TXPOWER_OFDM(4, 0), AR5K_PHY_TXPOWER_RATE2);

        ath5k_hw_reg_write(ah, AR5K_TXPOWER_CCK(10, 24) |
                AR5K_TXPOWER_CCK(9, 16) | AR5K_TXPOWER_CCK(15, 8) |
                AR5K_TXPOWER_CCK(8, 0), AR5K_PHY_TXPOWER_RATE3);

        ath5k_hw_reg_write(ah, AR5K_TXPOWER_CCK(14, 24) |
                AR5K_TXPOWER_CCK(13, 16) | AR5K_TXPOWER_CCK(12, 8) |
                AR5K_TXPOWER_CCK(11, 0), AR5K_PHY_TXPOWER_RATE4);

        /* FIXME: TPC support */
        if (ah->ah_txpower.txp_tpc) {
                ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX_TPC_ENABLE |
                        AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX);

                ath5k_hw_reg_write(ah,
                        AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_ACK) |
                        AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CTS) |
                        AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CHIRP),
                        AR5K_TPC);
        } else {
                ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX |
                        AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX);
        }

        return 0;
}
int ath5k_hw_set_txpower_limit ( struct ath5k_hw ah,
u8  mode,
u8  txpower 
)

Definition at line 2572 of file ath5k_phy.c.

References ath5k_hw::ah_current_channel, ath5k_hw_txpower(), channel, and DBG2.

{
        struct net80211_channel *channel = ah->ah_current_channel;

        DBG2("ath5k: changing txpower to %d\n", txpower);

        return ath5k_hw_txpower(ah, channel, mode, txpower);
}