Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  phy.c   Sprache: C

 
/*
 * Copyright (c) 2004-2007 Reyk Floeter <reyk@openbsd.org>
 * Copyright (c) 2006-2009 Nick Kossifidis <mickflemm@gmail.com>
 * Copyright (c) 2007-2008 Jiri Slaby <jirislaby@gmail.com>
 * Copyright (c) 2008-2009 Felix Fietkau <nbd@openwrt.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */


/***********************\
* PHY related functions *
\***********************/


#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/sort.h>
#include <linux/unaligned.h>

#include "ath5k.h"
#include "reg.h"
#include "rfbuffer.h"
#include "rfgain.h"
#include "../regd.h"


/**
 * DOC: PHY related functions
 *
 * Here we handle the low-level functions related to baseband
 * and analog frontend (RF) parts. This is by far the most complex
 * part of the hw code so make sure you know what you are doing.
 *
 * Here is a list of what this is all about:
 *
 * - Channel setting/switching
 *
 * - Automatic Gain Control (AGC) calibration
 *
 * - Noise Floor calibration
 *
 * - I/Q imbalance calibration (QAM correction)
 *
 * - Calibration due to thermal changes (gain_F)
 *
 * - Spur noise mitigation
 *
 * - RF/PHY initialization for the various operating modes and bwmodes
 *
 * - Antenna control
 *
 * - TX power control per channel/rate/packet type
 *
 * Also have in mind we never got documentation for most of these
 * functions, what we have comes mostly from Atheros's code, reverse
 * engineering and patent docs/presentations etc.
 */



/******************\
* Helper functions *
\******************/


/**
 * ath5k_hw_radio_revision() - Get the PHY Chip revision
 * @ah: The &struct ath5k_hw
 * @band: One of enum nl80211_band
 *
 * Returns the revision number of a 2GHz, 5GHz or single chip
 * radio.
 */

u16
ath5k_hw_radio_revision(struct ath5k_hw *ah, enum nl80211_band band)
{
 unsigned int i;
 u32 srev;
 u16 ret;

 /*
 * Set the radio chip access register
 */

 switch (band) {
 case NL80211_BAND_2GHZ:
  ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_2GHZ, AR5K_PHY(0));
  break;
 case NL80211_BAND_5GHZ:
  ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0));
  break;
 default:
  return 0;
 }

 usleep_range(2000, 2500);

 /* ...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;
}

/**
 * ath5k_channel_ok() - Check if a channel is supported by the hw
 * @ah: The &struct ath5k_hw
 * @channel: The &struct ieee80211_channel
 *
 * Note: We don't do any regulatory domain checks here, it's just
 * a sanity check.
 */

bool
ath5k_channel_ok(struct ath5k_hw *ah, struct ieee80211_channel *channel)
{
 u16 freq = channel->center_freq;

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

 return false;
}

/**
 * ath5k_hw_chan_has_spur_noise() - Check if channel is sensitive to spur noise
 * @ah: The &struct ath5k_hw
 * @channel: The &struct ieee80211_channel
 */

bool
ath5k_hw_chan_has_spur_noise(struct ath5k_hw *ah,
    struct ieee80211_channel *channel)
{
 u8 refclk_freq;

 if ((ah->ah_radio == AR5K_RF5112) ||
 (ah->ah_radio == AR5K_RF5413) ||
 (ah->ah_radio == AR5K_RF2413) ||
 (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4)))
  refclk_freq = 40;
 else
  refclk_freq = 32;

 if ((channel->center_freq % refclk_freq != 0) &&
 ((channel->center_freq % refclk_freq < 10) ||
 (channel->center_freq % refclk_freq > 22)))
  return true;
 else
  return false;
}

/**
 * ath5k_hw_rfb_op() - Perform an operation on the given RF Buffer
 * @ah: The &struct ath5k_hw
 * @rf_regs: The struct ath5k_rf_reg
 * @val: New value
 * @reg_id: RF register ID
 * @set: Indicate we need to swap data
 *
 * This is an internal function used to modify RF Banks before
 * writing them to AR5K_RF_BUFFER. Check out rfbuffer.h for more
 * infos.
 */

static unsigned int
ath5k_hw_rfb_op(struct ath5k_hw *ah, const struct ath5k_rf_reg *rf_regs,
     u32 val, u8 reg_id, bool set)
{
 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;
 int 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) {
  ATH5K_PRINTF("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)) {
  ATH5K_PRINTF("invalid values at offset %u\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;
}

/**
 * ath5k_hw_write_ofdm_timings() - set OFDM timings on AR5212
 * @ah: the &struct ath5k_hw
 * @channel: the currently set channel upon reset
 *
 * Write the delta slope coefficient (used on pilot tracking ?) for OFDM
 * operation on the AR5212 upon reset. This is a helper for ath5k_hw_phy_init.
 *
 * Since delta slope is floating point we split it on its exponent and
 * mantissa and provide these values on hw.
 *
 * For more infos i think this patent is related
 * "http://www.freepatentsonline.com/7184495.html"
 */

static inline int
ath5k_hw_write_ofdm_timings(struct ath5k_hw *ah,
    struct ieee80211_channel *channel)
{
 /* Get exponent and mantissa and set it */
 u32 coef_scaled, coef_exp, coef_man,
  ds_coef_exp, ds_coef_man, clock;

 BUG_ON(!(ah->ah_version == AR5K_AR5212) ||
  (channel->hw_value == AR5K_MODE_11B));

 /* Get coefficient
 * ALGO: coef = (5 * clock / carrier_freq) / 2
 * we scale coef by shifting clock value by 24 for
 * better precision since we use integers */

 switch (ah->ah_bwmode) {
 case AR5K_BWMODE_40MHZ:
  clock = 40 * 2;
  break;
 case AR5K_BWMODE_10MHZ:
  clock = 40 / 2;
  break;
 case AR5K_BWMODE_5MHZ:
  clock = 40 / 4;
  break;
 default:
  clock = 40;
  break;
 }
 coef_scaled = ((5 * (clock << 24)) / 2) / channel->center_freq;

 /* Get exponent
 * ALGO: coef_exp = 14 - highest set bit position */

 coef_exp = ilog2(coef_scaled);

 /* Doesn't make sense if it's zero*/
 if (!coef_scaled || !coef_exp)
  return -EINVAL;

 /* Note: we've shifted coef_scaled by 24 */
 coef_exp = 14 - (coef_exp - 24);


 /* Get mantissa (significant digits)
 * ALGO: coef_mant = floor(coef_scaled* 2^coef_exp+0.5) */

 coef_man = coef_scaled +
  (1 << (24 - coef_exp - 1));

 /* Calculate delta slope coefficient exponent
 * and mantissa (remove scaling) and set them on hw */

 ds_coef_man = coef_man >> (24 - coef_exp);
 ds_coef_exp = coef_exp - 16;

 AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_3,
  AR5K_PHY_TIMING_3_DSC_MAN, ds_coef_man);
 AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_3,
  AR5K_PHY_TIMING_3_DSC_EXP, ds_coef_exp);

 return 0;
}

/**
 * ath5k_hw_phy_disable() - Disable PHY
 * @ah: The &struct ath5k_hw
 */

int ath5k_hw_phy_disable(struct ath5k_hw *ah)
{
 /*Just a try M.F.*/
 ath5k_hw_reg_write(ah, AR5K_PHY_ACT_DISABLE, AR5K_PHY_ACT);

 return 0;
}

/**
 * ath5k_hw_wait_for_synth() - Wait for synth to settle
 * @ah: The &struct ath5k_hw
 * @channel: The &struct ieee80211_channel
 */

static void
ath5k_hw_wait_for_synth(struct ath5k_hw *ah,
   struct ieee80211_channel *channel)
{
 /*
 * On 5211+ read activation -> rx delay
 * and use it (100ns steps).
 */

 if (ah->ah_version != AR5K_AR5210) {
  u32 delay;
  delay = ath5k_hw_reg_read(ah, AR5K_PHY_RX_DELAY) &
   AR5K_PHY_RX_DELAY_M;
  delay = (channel->hw_value == AR5K_MODE_11B) ?
   ((delay << 2) / 22) : (delay / 10);
  if (ah->ah_bwmode == AR5K_BWMODE_10MHZ)
   delay = delay << 1;
  if (ah->ah_bwmode == AR5K_BWMODE_5MHZ)
   delay = delay << 2;
  /* XXX: /2 on turbo ? Let's be safe
 * for now */

  usleep_range(100 + delay, 100 + (2 * delay));
 } else {
  usleep_range(1000, 1500);
 }
}


/**********************\
* RF Gain optimization *
\**********************/


/**
 * DOC: RF Gain optimization
 *
 * This code is used to optimize RF gain on different environments
 * (temperature mostly) based on feedback from a power detector.
 *
 * It's only used on RF5111 and RF5112, later RF chips seem to have
 * auto adjustment on hw -notice they have a much smaller BANK 7 and
 * no gain optimization ladder-.
 *
 * For more infos check out this patent doc
 * "http://www.freepatentsonline.com/7400691.html"
 *
 * This paper describes power drops as seen on the receiver due to
 * probe packets
 * "http://www.cnri.dit.ie/publications/ICT08%20-%20Practical%20Issues
 * %20of%20Power%20Control.pdf"
 *
 * And this is the MadWiFi bug entry related to the above
 * "http://madwifi-project.org/ticket/1659"
 * with various measurements and diagrams
 */


/**
 * ath5k_hw_rfgain_opt_init() - Initialize ah_gain during attach
 * @ah: The &struct ath5k_hw
 */

int ath5k_hw_rfgain_opt_init(struct ath5k_hw *ah)
{
 /* Initialize the gain optimization values */
 switch (ah->ah_radio) {
 case AR5K_RF5111:
  ah->ah_gain.g_step_idx = rfgain_opt_5111.go_default;
  ah->ah_gain.g_low = 20;
  ah->ah_gain.g_high = 35;
  ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE;
  break;
 case AR5K_RF5112:
  ah->ah_gain.g_step_idx = rfgain_opt_5112.go_default;
  ah->ah_gain.g_low = 20;
  ah->ah_gain.g_high = 85;
  ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE;
  break;
 default:
  return -EINVAL;
 }

 return 0;
}

/**
 * ath5k_hw_request_rfgain_probe() - Request a PAPD probe packet
 * @ah: The &struct ath5k_hw
 *
 * Schedules a gain probe check on the next transmitted packet.
 * That means our next packet is going to be sent with lower
 * tx power and a Peak to Average Power Detector (PAPD) will try
 * to measure the gain.
 *
 * TODO: Force a tx packet (bypassing PCU arbitrator etc)
 * just after we enable the probe so that we don't mess with
 * standard traffic.
 */

static void
ath5k_hw_request_rfgain_probe(struct ath5k_hw *ah)
{

 /* Skip if gain calibration is inactive or
 * we already handle a probe request */

 if (ah->ah_gain.g_state != AR5K_RFGAIN_ACTIVE)
  return;

 /* Send the packet with 2dB below max power as
 * patent doc suggest */

 ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txpower.txp_ofdm - 4,
   AR5K_PHY_PAPD_PROBE_TXPOWER) |
   AR5K_PHY_PAPD_PROBE_TX_NEXT, AR5K_PHY_PAPD_PROBE);

 ah->ah_gain.g_state = AR5K_RFGAIN_READ_REQUESTED;

}

/**
 * ath5k_hw_rf_gainf_corr() - Calculate Gain_F measurement correction
 * @ah: The &struct ath5k_hw
 *
 * Calculate Gain_F measurement correction
 * based on the current step for RF5112 rev. 2
 */

static u32
ath5k_hw_rf_gainf_corr(struct ath5k_hw *ah)
{
 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, false) != 1)
  return 0;

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

 /* 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;
}

/**
 * ath5k_hw_rf_check_gainf_readback() - Validate Gain_F feedback from detector
 * @ah: The &struct ath5k_hw
 *
 * Check if current gain_F measurement is in the range of our
 * power detector windows. If we get a measurement outside range
 * we know it's not accurate (detectors can't measure anything outside
 * their detection window) so we must ignore it.
 *
 * Returns true if readback was O.K. or false on failure
 */

static bool
ath5k_hw_rf_check_gainf_readback(struct ath5k_hw *ah)
{
 const struct ath5k_rf_reg *rf_regs;
 u32 step, mix_ovr, level[4];

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

 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,
   false);

  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,
   false);

  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]);
}

/**
 * ath5k_hw_rf_gainf_adjust() - Perform Gain_F adjustment
 * @ah: The &struct ath5k_hw
 *
 * Choose the right target gain based on current gain
 * and RF gain optimization ladder
 */

static s8
ath5k_hw_rf_gainf_adjust(struct ath5k_hw *ah)
{
 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:
 ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE,
  "ret %d, gain step %u, current gain %u, target gain %u\n",
  ret, ah->ah_gain.g_step_idx, ah->ah_gain.g_current,
  ah->ah_gain.g_target);

 return ret;
}

/**
 * ath5k_hw_gainf_calibrate() - Do a gain_F calibration
 * @ah: The &struct ath5k_hw
 *
 * Main callback for thermal RF gain calibration engine
 * Check for a new gain reading and schedule an adjustment
 * if needed.
 *
 * Returns one of enum ath5k_rfgain codes
 */

enum ath5k_rfgain
ath5k_hw_gainf_calibrate(struct ath5k_hw *ah)
{
 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 active 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;
}

/**
 * ath5k_hw_rfgain_init() - Write initial RF gain settings to hw
 * @ah: The &struct ath5k_hw
 * @band: One of enum nl80211_band
 *
 * Write initial RF gain table to set the RF sensitivity.
 *
 * NOTE: This one works on all RF chips and has nothing to do
 * with Gain_F calibration
 */

static int
ath5k_hw_rfgain_init(struct ath5k_hw *ah, enum nl80211_band band)
{
 const struct ath5k_ini_rfgain *ath5k_rfg;
 unsigned int i, size, index;

 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;
 }

 index = (band == NL80211_BAND_2GHZ) ? 1 : 0;

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

 return 0;
}


/********************\
* RF Registers setup *
\********************/


/**
 * ath5k_hw_rfregs_init() - Initialize RF register settings
 * @ah: The &struct ath5k_hw
 * @channel: The &struct ieee80211_channel
 * @mode: One of enum ath5k_driver_mode
 *
 * Setup RF registers by writing RF buffer on hw. For
 * more infos on this, check out rfbuffer.h
 */

static int
ath5k_hw_rfregs_init(struct ath5k_hw *ah,
   struct ieee80211_channel *channel,
   unsigned int mode)
{
 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 i, obdb = -1, bank = -1;

 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 = kmalloc_array(ah->ah_rf_banks_size,
        sizeof(u32),
        GFP_KERNEL);
  if (ah->ah_rf_banks == NULL) {
   ATH5K_ERR(ah, "out of memory\n");
   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) {
   ATH5K_ERR(ah, "invalid 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->band == NL80211_BAND_2GHZ) {

  if (channel->hw_value == AR5K_MODE_11B)
   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, true);

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

 /* RF5111 always needs OB/DB for 5GHz, even if we use 2GHz */
 } else if ((channel->band == NL80211_BAND_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, true);

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

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

 /* Set turbo mode (N/A on RF5413) */
 if ((ah->ah_bwmode == AR5K_BWMODE_40MHZ) &&
 (ah->ah_radio != AR5K_RF5413))
  ath5k_hw_rfb_op(ah, rf_regs, 1, AR5K_RF_TURBO, false);

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

  /* Set gain_F settings according to current step */
  if (channel->hw_value != AR5K_MODE_11B) {

   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, true);

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

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

   /* 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, true);

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

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

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

  /* Tweak power detectors for half/quarter rate support */
  if (ah->ah_bwmode == AR5K_BWMODE_5MHZ ||
  ah->ah_bwmode == AR5K_BWMODE_10MHZ) {
   u8 wait_i;

   ath5k_hw_rfb_op(ah, rf_regs, 0x1f,
      AR5K_RF_WAIT_S, true);

   wait_i = (ah->ah_bwmode == AR5K_BWMODE_5MHZ) ?
       0x1f : 0x10;

   ath5k_hw_rfb_op(ah, rf_regs, wait_i,
      AR5K_RF_WAIT_I, true);
   ath5k_hw_rfb_op(ah, rf_regs, 3,
      AR5K_RF_MAX_TIME, true);

  }
 }

 if (ah->ah_radio == AR5K_RF5112) {

  /* Set gain_F settings according to current step */
  if (channel->hw_value != AR5K_MODE_11B) {

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

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

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

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

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

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

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

   /* 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, true);

  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, true);

  } else {
   u8 *pdg_curve_to_idx = ee->ee_pdc_to_idx[ee_mode];
   if (ee->ee_pd_gains[ee_mode] > 1) {
    ath5k_hw_rfb_op(ah, rf_regs,
      pdg_curve_to_idx[0],
      AR5K_RF_PD_GAIN_LO, true);
    ath5k_hw_rfb_op(ah, rf_regs,
      pdg_curve_to_idx[1],
      AR5K_RF_PD_GAIN_HI, true);
   } else {
    ath5k_hw_rfb_op(ah, rf_regs,
      pdg_curve_to_idx[0],
      AR5K_RF_PD_GAIN_LO, true);
    ath5k_hw_rfb_op(ah, rf_regs,
      pdg_curve_to_idx[0],
      AR5K_RF_PD_GAIN_HI, true);
   }

   /* Lower synth voltage on Rev 2 */
   if (ah->ah_radio == AR5K_RF5112 &&
       (ah->ah_radio_5ghz_revision & AR5K_SREV_REV) > 0) {
    ath5k_hw_rfb_op(ah, rf_regs, 2,
      AR5K_RF_HIGH_VC_CP, true);

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

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

    ath5k_hw_rfb_op(ah, rf_regs, 2,
      AR5K_RF_PUSH_UP, true);
   }

   /* 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, true);

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

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

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

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

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

  /* Tweak power detector for half/quarter rates */
  if (ah->ah_bwmode == AR5K_BWMODE_5MHZ ||
  ah->ah_bwmode == AR5K_BWMODE_10MHZ) {
   u8 pd_delay;

   pd_delay = (ah->ah_bwmode == AR5K_BWMODE_5MHZ) ?
       0xf : 0x8;

   ath5k_hw_rfb_op(ah, rf_regs, pd_delay,
      AR5K_RF_PD_PERIOD_A, true);
   ath5k_hw_rfb_op(ah, rf_regs, 0xf,
      AR5K_RF_PD_DELAY_A, true);

  }
 }

 if (ah->ah_radio == AR5K_RF5413 &&
 channel->band == NL80211_BAND_2GHZ) {

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

  /* 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, true);

 }

 /* 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;
}


/**************************\
  PHY/RF channel functions
\**************************/


/**
 * ath5k_hw_rf5110_chan2athchan() - Convert channel freq on RF5110
 * @channel: The &struct ieee80211_channel
 *
 * Map channel frequency to IEEE channel number and convert it
 * to an internal channel value used by the RF5110 chipset.
 */

static u32
ath5k_hw_rf5110_chan2athchan(struct ieee80211_channel *channel)
{
 u32 athchan;

 athchan = (ath5k_hw_bitswap(
   (ieee80211_frequency_to_channel(
    channel->center_freq) - 24) / 2, 5)
    << 1) | (1 << 6) | 0x1;
 return athchan;
}

/**
 * ath5k_hw_rf5110_channel() - Set channel frequency on RF5110
 * @ah: The &struct ath5k_hw
 * @channel: The &struct ieee80211_channel
 */

static int
ath5k_hw_rf5110_channel(struct ath5k_hw *ah,
  struct ieee80211_channel *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);
 usleep_range(1000, 1500);

 return 0;
}

/**
 * ath5k_hw_rf5111_chan2athchan() - Handle 2GHz channels on RF5111/2111
 * @ieee: IEEE channel number
 * @athchan: The &struct ath5k_athchan_2ghz
 *
 * In order to enable the RF2111 frequency converter on RF5111/2111 setups
 * we need to add some offsets and extra flags to the data values we pass
 * on to the PHY. So for every 2GHz channel this function gets called
 * to do the conversion.
 */

static int
ath5k_hw_rf5111_chan2athchan(unsigned int ieee,
  struct ath5k_athchan_2ghz *athchan)
{
 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;
}

/**
 * ath5k_hw_rf5111_channel() - Set channel frequency on RF5111/2111
 * @ah: The &struct ath5k_hw
 * @channel: The &struct ieee80211_channel
 */

static int
ath5k_hw_rf5111_channel(struct ath5k_hw *ah,
  struct ieee80211_channel *channel)
{
 struct ath5k_athchan_2ghz ath5k_channel_2ghz;
 unsigned int ath5k_channel =
  ieee80211_frequency_to_channel(channel->center_freq);
 u32 data0, data1, clock;
 int ret;

 /*
 * Set the channel on the RF5111 radio
 */

 data0 = data1 = 0;

 if (channel->band == NL80211_BAND_2GHZ) {
  /* Map 2GHz channel to 5GHz Atheros channel ID */
  ret = ath5k_hw_rf5111_chan2athchan(
   ieee80211_frequency_to_channel(channel->center_freq),
   &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;
}

/**
 * ath5k_hw_rf5112_channel() - Set channel frequency on 5112 and newer
 * @ah: The &struct ath5k_hw
 * @channel: The &struct ieee80211_channel
 *
 * On RF5112/2112 and newer we don't need to do any conversion.
 * We pass the frequency value after a few modifications to the
 * chip directly.
 *
 * NOTE: Make sure channel frequency given is within our range or else
 * we might damage the chip ! Use ath5k_channel_ok before calling this one.
 */

static int
ath5k_hw_rf5112_channel(struct ath5k_hw *ah,
  struct ieee80211_channel *channel)
{
 u32 data, data0, data1, data2;
 u16 c;

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

 /* My guess based on code:
 * 2GHz RF has 2 synth modes, one with a Local Oscillator
 * at 2224Hz and one with a LO at 2192Hz. IF is 1520Hz
 * (3040/2). data0 is used to set the PLL divider and data1
 * selects synth mode. */

 if (c < 4800) {
  /* Channel 14 and all frequencies with 2Hz spacing
 * below/above (non-standard channels) */

  if (!((c - 2224) % 5)) {
   /* Same as (c - 2224) / 5 */
   data0 = ((2 * (c - 704)) - 3040) / 10;
   data1 = 1;
  /* Channel 1 and all frequencies with 5Hz spacing
 * below/above (standard channels without channel 14) */

  } else if (!((c - 2192) % 5)) {
   /* Same as (c - 2192) / 5 */
   data0 = ((2 * (c - 672)) - 3040) / 10;
   data1 = 0;
  } else
   return -EINVAL;

  data0 = ath5k_hw_bitswap((data0 << 2) & 0xff, 8);
 /* This is more complex, we have a single synthesizer with
 * 4 reference clock settings (?) based on frequency spacing
 * and set using data2. LO is at 4800Hz and data0 is again used
 * to set some divider.
 *
 * NOTE: There is an old atheros presentation at Stanford
 * that mentions a method called dual direct conversion
 * with 1GHz sliding IF for RF5110. Maybe that's what we
 * have here, or an updated version. */

 } else if ((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;
}

/**
 * ath5k_hw_rf2425_channel() - Set channel frequency on RF2425
 * @ah: The &struct ath5k_hw
 * @channel: The &struct ieee80211_channel
 *
 * AR2425/2417 have a different 2GHz RF so code changes
 * a little bit from RF5112.
 */

static int
ath5k_hw_rf2425_channel(struct ath5k_hw *ah,
  struct ieee80211_channel *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 % 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;
}

/**
 * ath5k_hw_channel() - Set a channel on the radio chip
 * @ah: The &struct ath5k_hw
 * @channel: The &struct ieee80211_channel
 *
 * This is the main function called to set a channel on the
 * radio chip based on the radio chip version.
 */

static int
ath5k_hw_channel(struct ath5k_hw *ah,
  struct ieee80211_channel *channel)
{
 int ret;
 /*
 * Check bounds supported by the PHY (we don't care about regulatory
 * restrictions at this point).
 */

 if (!ath5k_channel_ok(ah, channel)) {
  ATH5K_ERR(ah,
   "channel frequency (%u MHz) out of supported "
   "band 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_RF2317:
 case AR5K_RF2425:
  ret = ath5k_hw_rf2425_channel(ah, channel);
  break;
 default:
  ret = ath5k_hw_rf5112_channel(ah, channel);
  break;
 }

 if (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;

 return 0;
}


/*****************\
  PHY calibration
\*****************/


/**
 * DOC: PHY Calibration routines
 *
 * Noise floor calibration: When we tell the hardware to
 * perform a noise floor calibration by setting the
 * AR5K_PHY_AGCCTL_NF bit on AR5K_PHY_AGCCTL, it will periodically
 * sample-and-hold the minimum noise level seen at the antennas.
 * This value is then stored in a ring buffer of recently measured
 * noise floor values so we have a moving window of the last few
 * samples. The median of the values in the history is then loaded
 * into the hardware for its own use for RSSI and CCA measurements.
 * This type of calibration doesn't interfere with traffic.
 *
 * AGC calibration: When we tell the hardware to perform
 * an AGC (Automatic Gain Control) calibration by setting the
 * AR5K_PHY_AGCCTL_CAL, hw disconnects the antennas and does
 * a calibration on the DC offsets of ADCs. During this period
 * rx/tx gets disabled so we have to deal with it on the driver
 * part.
 *
 * I/Q calibration: When we tell the hardware to perform
 * an I/Q calibration, it tries to correct I/Q imbalance and
 * fix QAM constellation by sampling data from rxed frames.
 * It doesn't interfere with traffic.
 *
 * For more infos on AGC and I/Q calibration check out patent doc
 * #03/094463.
 */


/**
 * ath5k_hw_read_measured_noise_floor() - Read measured NF from hw
 * @ah: The &struct ath5k_hw
 */

static s32
ath5k_hw_read_measured_noise_floor(struct ath5k_hw *ah)
{
 s32 val;

 val = ath5k_hw_reg_read(ah, AR5K_PHY_NF);
 return sign_extend32(AR5K_REG_MS(val, AR5K_PHY_NF_MINCCA_PWR), 8);
}

/**
 * ath5k_hw_init_nfcal_hist() - Initialize NF calibration history buffer
 * @ah: The &struct ath5k_hw
 */

void
ath5k_hw_init_nfcal_hist(struct ath5k_hw *ah)
{
 int i;

 ah->ah_nfcal_hist.index = 0;
 for (i = 0; i < ATH5K_NF_CAL_HIST_MAX; i++)
  ah->ah_nfcal_hist.nfval[i] = AR5K_TUNE_CCA_MAX_GOOD_VALUE;
}

/**
 * ath5k_hw_update_nfcal_hist() - Update NF calibration history buffer
 * @ah: The &struct ath5k_hw
 * @noise_floor: The NF we got from hw
 */

static void ath5k_hw_update_nfcal_hist(struct ath5k_hw *ah, s16 noise_floor)
{
 struct ath5k_nfcal_hist *hist = &ah->ah_nfcal_hist;
 hist->index = (hist->index + 1) & (ATH5K_NF_CAL_HIST_MAX - 1);
 hist->nfval[hist->index] = noise_floor;
}

static int cmps16(const void *a, const void *b)
{
 return *(s16 *)a - *(s16 *)b;
}

/**
 * ath5k_hw_get_median_noise_floor() - Get median NF from history buffer
 * @ah: The &struct ath5k_hw
 */

static s16
ath5k_hw_get_median_noise_floor(struct ath5k_hw *ah)
{
 s16 sorted_nfval[ATH5K_NF_CAL_HIST_MAX];
 int i;

 memcpy(sorted_nfval, ah->ah_nfcal_hist.nfval, sizeof(sorted_nfval));
 sort(sorted_nfval, ATH5K_NF_CAL_HIST_MAX, sizeof(s16), cmps16, NULL);
 for (i = 0; i < ATH5K_NF_CAL_HIST_MAX; i++) {
  ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE,
   "cal %d:%d\n", i, sorted_nfval[i]);
 }
 return sorted_nfval[(ATH5K_NF_CAL_HIST_MAX - 1) / 2];
}

/**
 * ath5k_hw_update_noise_floor() - Update NF on hardware
 * @ah: The &struct ath5k_hw
 *
 * This is the main function we call to perform a NF calibration,
 * it reads NF from hardware, calculates the median and updates
 * NF on hw.
 */

void
ath5k_hw_update_noise_floor(struct ath5k_hw *ah)
{
 struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
 u32 val;
 s16 nf, threshold;
 u8 ee_mode;

 /* keep last value if calibration hasn't completed */
 if (ath5k_hw_reg_read(ah, AR5K_PHY_AGCCTL) & AR5K_PHY_AGCCTL_NF) {
  ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE,
   "NF did not complete in calibration window\n");

  return;
 }

 ah->ah_cal_mask |= AR5K_CALIBRATION_NF;

 ee_mode = ath5k_eeprom_mode_from_channel(ah, ah->ah_current_channel);

 /* completed NF calibration, test threshold */
 nf = ath5k_hw_read_measured_noise_floor(ah);
 threshold = ee->ee_noise_floor_thr[ee_mode];

 if (nf > threshold) {
  ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE,
   "noise floor failure detected; "
   "read %d, threshold %d\n",
   nf, threshold);

  nf = AR5K_TUNE_CCA_MAX_GOOD_VALUE;
 }

 ath5k_hw_update_nfcal_hist(ah, nf);
 nf = ath5k_hw_get_median_noise_floor(ah);

 /* load noise floor (in .5 dBm) so the hardware will use it */
 val = ath5k_hw_reg_read(ah, AR5K_PHY_NF) & ~AR5K_PHY_NF_M;
 val |= (nf * 2) & AR5K_PHY_NF_M;
 ath5k_hw_reg_write(ah, val, AR5K_PHY_NF);

 AR5K_REG_MASKED_BITS(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_NF,
  ~(AR5K_PHY_AGCCTL_NF_EN | AR5K_PHY_AGCCTL_NF_NOUPDATE));

 ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_NF,
  0, false);

 /*
 * Load a high max CCA Power value (-50 dBm in .5 dBm units)
 * so that we're not capped by the median we just loaded.
 * This will be used as the initial value for the next noise
 * floor calibration.
 */

 val = (val & ~AR5K_PHY_NF_M) | ((-50 * 2) & AR5K_PHY_NF_M);
 ath5k_hw_reg_write(ah, val, AR5K_PHY_NF);
 AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL,
  AR5K_PHY_AGCCTL_NF_EN |
  AR5K_PHY_AGCCTL_NF_NOUPDATE |
  AR5K_PHY_AGCCTL_NF);

 ah->ah_noise_floor = nf;

 ah->ah_cal_mask &= ~AR5K_CALIBRATION_NF;

 ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE,
  "noise floor calibrated: %d\n", nf);
}

/**
 * ath5k_hw_rf5110_calibrate() - Perform a PHY calibration on RF5110
 * @ah: The &struct ath5k_hw
 * @channel: The &struct ieee80211_channel
 *
 * Do a complete PHY calibration (AGC + NF + I/Q) on RF5110
 */

static int
ath5k_hw_rf5110_calibrate(struct ath5k_hw *ah,
  struct ieee80211_channel *channel)
{
 u32 phy_sig, phy_agc, phy_sat, beacon;
 int ret;

 if (!(ah->ah_cal_mask & AR5K_CALIBRATION_FULL))
  return 0;

 /*
 * Disable beacons and RX/TX queues, wait
 */

 AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5210,
  AR5K_DIAG_SW_DIS_TX_5210 | 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);

 usleep_range(2000, 2500);

 /*
 * 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);
 usleep_range(1000, 1500);

 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(-1, 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(-1, AR5K_PHY_AGCCOARSE_HI) |
  AR5K_REG_SM(-127, 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);

 usleep_range(1000, 1500);

 /*
 * 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, false);

 /* 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) {
  ATH5K_ERR(ah, "calibration timeout (%uMHz)\n",
    channel->center_freq);
  return ret;
 }

 /*
 * Re-enable RX/TX and beacons
 */

 AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW_5210,
  AR5K_DIAG_SW_DIS_TX_5210 | AR5K_DIAG_SW_DIS_RX_5210);
 ath5k_hw_reg_write(ah, beacon, AR5K_BEACON_5210);

 return 0;
}

/**
 * ath5k_hw_rf511x_iq_calibrate() - Perform I/Q calibration on RF5111 and newer
 * @ah: The &struct ath5k_hw
 */

static int
ath5k_hw_rf511x_iq_calibrate(struct ath5k_hw *ah)
{
 u32 i_pwr, q_pwr;
 s32 iq_corr, i_coff, i_coffd, q_coff, q_coffd;
 int i;

 /* Skip if I/Q calibration is not needed or if it's still running */
 if (!ah->ah_iq_cal_needed)
  return -EINVAL;
 else if (ath5k_hw_reg_read(ah, AR5K_PHY_IQ) & AR5K_PHY_IQ_RUN) {
  ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_CALIBRATE,
    "I/Q calibration still running");
  return -EBUSY;
 }

 /* Calibration has finished, get the results and re-run */

 /* Work around for empty results which can apparently happen on 5212:
 * Read registers up to 10 times until we get both i_pr and q_pwr */

 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);
  ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_CALIBRATE,
   "iq_corr:%x i_pwr:%x q_pwr:%x", iq_corr, i_pwr, q_pwr);
  if (i_pwr && q_pwr)
   break;
 }

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

 if (ah->ah_version == AR5K_AR5211)
  q_coffd = q_pwr >> 6;
 else
  q_coffd = q_pwr >> 7;

 /* In case i_coffd became zero, cancel calibration
 * not only it's too small, it'll also result a divide
 * by zero later on. */

 if (i_coffd == 0 || q_coffd < 2)
  return -ECANCELED;

 /* Protect against loss of sign bits */

 i_coff = (-iq_corr) / i_coffd;
 i_coff = clamp(i_coff, -32, 31); /* signed 6 bit */

 if (ah->ah_version == AR5K_AR5211)
  q_coff = (i_pwr / q_coffd) - 64;
 else
  q_coff = (i_pwr / q_coffd) - 128;
 q_coff = clamp(q_coff, -16, 15); /* signed 5 bit */

 ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_CALIBRATE,
   "new I:%d Q:%d (i_coffd:%x q_coffd:%x)",
   i_coff, q_coff, i_coffd, q_coffd);

 /* Commit new I/Q values (set enable bit last to match HAL sources) */
 AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_Q_I_COFF, i_coff);
 AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_Q_Q_COFF, q_coff);
 AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_ENABLE);

 /* 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);

 return 0;
}

/**
 * ath5k_hw_phy_calibrate() - Perform a PHY calibration
 * @ah: The &struct ath5k_hw
 * @channel: The &struct ieee80211_channel
 *
 * The main function we call from above to perform
 * a short or full PHY calibration based on RF chip
 * and current channel
 */

int
ath5k_hw_phy_calibrate(struct ath5k_hw *ah,
  struct ieee80211_channel *channel)
{
 int ret;

 if (ah->ah_radio == AR5K_RF5110)
  return ath5k_hw_rf5110_calibrate(ah, channel);

 ret = ath5k_hw_rf511x_iq_calibrate(ah);
 if (ret) {
  ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_CALIBRATE,
   "No I/Q correction performed (%uMHz)\n",
   channel->center_freq);

  /* Happens all the time if there is not much
 * traffic, consider it normal behaviour. */

  ret = 0;
 }

 /* On full calibration request a PAPD probe for
 * gainf calibration if needed */

 if ((ah->ah_cal_mask & AR5K_CALIBRATION_FULL) &&
     (ah->ah_radio == AR5K_RF5111 ||
      ah->ah_radio == AR5K_RF5112) &&
     channel->hw_value != AR5K_MODE_11B)
  ath5k_hw_request_rfgain_probe(ah);

 /* Update noise floor */
 if (!(ah->ah_cal_mask & AR5K_CALIBRATION_NF))
  ath5k_hw_update_noise_floor(ah);

 return ret;
}


/***************************\
* Spur mitigation functions *
\***************************/


/**
 * ath5k_hw_set_spur_mitigation_filter() - Configure SPUR filter
 * @ah: The &struct ath5k_hw
 * @channel: The &struct ieee80211_channel
 *
 * This function gets called during PHY initialization to
 * configure the spur filter for the given channel. Spur is noise
 * generated due to "reflection" effects, for more information on this
 * method check out patent US7643810
 */

static void
ath5k_hw_set_spur_mitigation_filter(struct ath5k_hw *ah,
    struct ieee80211_channel *channel)
{
 struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
 u32 mag_mask[4] = {0, 0, 0, 0};
 u32 pilot_mask[2] = {0, 0};
 /* Note: fbin values are scaled up by 2 */
 u16 spur_chan_fbin, chan_fbin, symbol_width, spur_detection_window;
 s32 spur_delta_phase, spur_freq_sigma_delta;
 s32 spur_offset, num_symbols_x16;
 u8 num_symbol_offsets, i, freq_band;

 /* Convert current frequency to fbin value (the same way channels
 * are stored on EEPROM, check out ath5k_eeprom_bin2freq) and scale
 * up by 2 so we can compare it later */

 if (channel->band == NL80211_BAND_2GHZ) {
  chan_fbin = (channel->center_freq - 2300) * 10;
  freq_band = AR5K_EEPROM_BAND_2GHZ;
 } else {
  chan_fbin = (channel->center_freq - 4900) * 10;
  freq_band = AR5K_EEPROM_BAND_5GHZ;
 }

 /* Check if any spur_chan_fbin from EEPROM is
 * within our current channel's spur detection range */

 spur_chan_fbin = AR5K_EEPROM_NO_SPUR;
 spur_detection_window = AR5K_SPUR_CHAN_WIDTH;
 /* XXX: Half/Quarter channels ?*/
 if (ah->ah_bwmode == AR5K_BWMODE_40MHZ)
  spur_detection_window *= 2;

 for (i = 0; i < AR5K_EEPROM_N_SPUR_CHANS; i++) {
  spur_chan_fbin = ee->ee_spur_chans[i][freq_band];

  /* Note: mask cleans AR5K_EEPROM_NO_SPUR flag
 * so it's zero if we got nothing from EEPROM */

  if (spur_chan_fbin == AR5K_EEPROM_NO_SPUR) {
   spur_chan_fbin &= AR5K_EEPROM_SPUR_CHAN_MASK;
   break;
  }

  if ((chan_fbin - spur_detection_window <=
  (spur_chan_fbin & AR5K_EEPROM_SPUR_CHAN_MASK)) &&
  (chan_fbin + spur_detection_window >=
  (spur_chan_fbin & AR5K_EEPROM_SPUR_CHAN_MASK))) {
   spur_chan_fbin &= AR5K_EEPROM_SPUR_CHAN_MASK;
   break;
  }
 }

 /* We need to enable spur filter for this channel */
 if (spur_chan_fbin) {
  spur_offset = spur_chan_fbin - chan_fbin;
  /*
 * Calculate deltas:
 * spur_freq_sigma_delta -> spur_offset / sample_freq << 21
 * spur_delta_phase -> spur_offset / chip_freq << 11
 * Note: Both values have 100Hz resolution
 */

  switch (ah->ah_bwmode) {
  case AR5K_BWMODE_40MHZ:
   /* Both sample_freq and chip_freq are 80MHz */
   spur_delta_phase = (spur_offset << 16) / 25;
   spur_freq_sigma_delta = (spur_delta_phase >> 10);
   symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz * 2;
   break;
  case AR5K_BWMODE_10MHZ:
   /* Both sample_freq and chip_freq are 20MHz (?) */
   spur_delta_phase = (spur_offset << 18) / 25;
   spur_freq_sigma_delta = (spur_delta_phase >> 10);
   symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz / 2;
   break;
  case AR5K_BWMODE_5MHZ:
   /* Both sample_freq and chip_freq are 10MHz (?) */
   spur_delta_phase = (spur_offset << 19) / 25;
   spur_freq_sigma_delta = (spur_delta_phase >> 10);
   symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz / 4;
   break;
  default:
   if (channel->band == NL80211_BAND_5GHZ) {
    /* Both sample_freq and chip_freq are 40MHz */
    spur_delta_phase = (spur_offset << 17) / 25;
    spur_freq_sigma_delta =
      (spur_delta_phase >> 10);
    symbol_width =
     AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz;
   } else {
    /* sample_freq -> 40MHz chip_freq -> 44MHz
 * (for b compatibility) */

    spur_delta_phase = (spur_offset << 17) / 25;
    spur_freq_sigma_delta =
      (spur_offset << 8) / 55;
    symbol_width =
     AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz;
   }
   break;
  }

  /* Calculate pilot and magnitude masks */

  /* Scale up spur_offset by 1000 to switch to 100HZ resolution
 * and divide by symbol_width to find how many symbols we have
 * Note: number of symbols is scaled up by 16 */

  num_symbols_x16 = ((spur_offset * 1000) << 4) / symbol_width;

  /* Spur is on a symbol if num_symbols_x16 % 16 is zero */
  if (!(num_symbols_x16 & 0xF))
   /* _X_ */
   num_symbol_offsets = 3;
  else
   /* _xx_ */
   num_symbol_offsets = 4;

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

   /* Calculate pilot mask */
   s32 curr_sym_off =
    (num_symbols_x16 / 16) + i + 25;

   /* Pilot magnitude mask seems to be a way to
 * declare the boundaries for our detection
 * window or something, it's 2 for the middle
 * value(s) where the symbol is expected to be
 * and 1 on the boundary values */

   u8 plt_mag_map =
    (i == 0 || i == (num_symbol_offsets - 1))
        ? 1 : 2;

   if (curr_sym_off >= 0 && curr_sym_off <= 32) {
    if (curr_sym_off <= 25)
     pilot_mask[0] |= 1 << curr_sym_off;
    else if (curr_sym_off >= 27)
     pilot_mask[0] |= 1 << (curr_sym_off - 1);
   } else if (curr_sym_off >= 33 && curr_sym_off <= 52)
    pilot_mask[1] |= 1 << (curr_sym_off - 33);

   /* Calculate magnitude mask (for viterbi decoder) */
   if (curr_sym_off >= -1 && curr_sym_off <= 14)
    mag_mask[0] |=
     plt_mag_map << (curr_sym_off + 1) * 2;
   else if (curr_sym_off >= 15 && curr_sym_off <= 30)
    mag_mask[1] |=
     plt_mag_map << (curr_sym_off - 15) * 2;
   else if (curr_sym_off >= 31 && curr_sym_off <= 46)
    mag_mask[2] |=
     plt_mag_map << (curr_sym_off - 31) * 2;
   else if (curr_sym_off >= 47 && curr_sym_off <= 53)
    mag_mask[3] |=
     plt_mag_map << (curr_sym_off - 47) * 2;

  }

  /* Write settings on hw to enable spur filter */
  AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL,
     AR5K_PHY_BIN_MASK_CTL_RATE, 0xff);
  /* XXX: Self correlator also ? */
  AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ,
     AR5K_PHY_IQ_PILOT_MASK_EN |
     AR5K_PHY_IQ_CHAN_MASK_EN |
     AR5K_PHY_IQ_SPUR_FILT_EN);

  /* Set delta phase and freq sigma delta */
  ath5k_hw_reg_write(ah,
    AR5K_REG_SM(spur_delta_phase,
     AR5K_PHY_TIMING_11_SPUR_DELTA_PHASE) |
    AR5K_REG_SM(spur_freq_sigma_delta,
    AR5K_PHY_TIMING_11_SPUR_FREQ_SD) |
    AR5K_PHY_TIMING_11_USE_SPUR_IN_AGC,
    AR5K_PHY_TIMING_11);

  /* Write pilot masks */
  ath5k_hw_reg_write(ah, pilot_mask[0], AR5K_PHY_TIMING_7);
  AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_8,
     AR5K_PHY_TIMING_8_PILOT_MASK_2,
     pilot_mask[1]);

  ath5k_hw_reg_write(ah, pilot_mask[0], AR5K_PHY_TIMING_9);
  AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_10,
     AR5K_PHY_TIMING_10_PILOT_MASK_2,
     pilot_mask[1]);

  /* Write magnitude masks */
  ath5k_hw_reg_write(ah, mag_mask[0], AR5K_PHY_BIN_MASK_1);
  ath5k_hw_reg_write(ah, mag_mask[1], AR5K_PHY_BIN_MASK_2);
  ath5k_hw_reg_write(ah, mag_mask[2], AR5K_PHY_BIN_MASK_3);
  AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL,
     AR5K_PHY_BIN_MASK_CTL_MASK_4,
     mag_mask[3]);

  ath5k_hw_reg_write(ah, mag_mask[0], AR5K_PHY_BIN_MASK2_1);
  ath5k_hw_reg_write(ah, mag_mask[1], AR5K_PHY_BIN_MASK2_2);
  ath5k_hw_reg_write(ah, mag_mask[2], AR5K_PHY_BIN_MASK2_3);
  AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK2_4,
     AR5K_PHY_BIN_MASK2_4_MASK_4,
     mag_mask[3]);

 } else if (ath5k_hw_reg_read(ah, AR5K_PHY_IQ) &
 AR5K_PHY_IQ_SPUR_FILT_EN) {
  /* Clean up spur mitigation settings and disable filter */
  AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL,
     AR5K_PHY_BIN_MASK_CTL_RATE, 0);
  AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_IQ,
     AR5K_PHY_IQ_PILOT_MASK_EN |
     AR5K_PHY_IQ_CHAN_MASK_EN |
     AR5K_PHY_IQ_SPUR_FILT_EN);
  ath5k_hw_reg_write(ah, 0, AR5K_PHY_TIMING_11);

  /* Clear pilot masks */
  ath5k_hw_reg_write(ah, 0, AR5K_PHY_TIMING_7);
  AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_8,
     AR5K_PHY_TIMING_8_PILOT_MASK_2,
     0);

  ath5k_hw_reg_write(ah, 0, AR5K_PHY_TIMING_9);
  AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_10,
     AR5K_PHY_TIMING_10_PILOT_MASK_2,
     0);

  /* Clear magnitude masks */
  ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK_1);
  ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK_2);
  ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK_3);
  AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL,
     AR5K_PHY_BIN_MASK_CTL_MASK_4,
     0);

  ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK2_1);
  ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK2_2);
  ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK2_3);
  AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK2_4,
     AR5K_PHY_BIN_MASK2_4_MASK_4,
     0);
 }
}


/*****************\
* Antenna control *
\*****************/


/**
 * DOC: Antenna control
 *
 * Hw supports up to 14 antennas ! I haven't found any card that implements
 * that. The maximum number of antennas I've seen is up to 4 (2 for 2GHz and 2
 * for 5GHz). Antenna 1 (MAIN) should be omnidirectional, 2 (AUX)
 * omnidirectional or sectorial and antennas 3-14 sectorial (or directional).
 *
 * We can have a single antenna for RX and multiple antennas for TX.
 * RX antenna is our "default" antenna (usually antenna 1) set on
 * DEFAULT_ANTENNA register and TX antenna is set on each TX control descriptor
 * (0 for automatic selection, 1 - 14 antenna number).
 *
 * We can let hw do all the work doing fast antenna diversity for both
 * tx and rx or we can do things manually. Here are the options we have
 * (all are bits of STA_ID1 register):
 *
 * AR5K_STA_ID1_DEFAULT_ANTENNA -> When 0 is set as the TX antenna on TX
 * control descriptor, use the default antenna to transmit or else use the last
 * antenna on which we received an ACK.
 *
 * AR5K_STA_ID1_DESC_ANTENNA -> Update default antenna after each TX frame to
 * the antenna on which we got the ACK for that frame.
 *
 * AR5K_STA_ID1_RTS_DEF_ANTENNA -> Use default antenna for RTS or else use the
 * one on the TX descriptor.
 *
 * AR5K_STA_ID1_SELFGEN_DEF_ANT -> Use default antenna for self generated frames
 * (ACKs etc), or else use current antenna (the one we just used for TX).
 *
 * Using the above we support the following scenarios:
 *
 * AR5K_ANTMODE_DEFAULT -> Hw handles antenna diversity etc automatically
 *
 * AR5K_ANTMODE_FIXED_A -> Only antenna A (MAIN) is present
 *
 * AR5K_ANTMODE_FIXED_B -> Only antenna B (AUX) is present
 *
 * AR5K_ANTMODE_SINGLE_AP -> Sta locked on a single ap
 *
 * AR5K_ANTMODE_SECTOR_AP -> AP with tx antenna set on tx desc
 *
 * AR5K_ANTMODE_SECTOR_STA -> STA with tx antenna set on tx desc
 *
 * AR5K_ANTMODE_DEBUG Debug mode -A -> Rx, B-> Tx-
 *
 * Also note that when setting antenna to F on tx descriptor card inverts
 * current tx antenna.
 */


/**
 * ath5k_hw_set_def_antenna() - Set default rx antenna on AR5211/5212 and newer
 * @ah: The &struct ath5k_hw
 * @ant: Antenna number
 */

static void
ath5k_hw_set_def_antenna(struct ath5k_hw *ah, u8 ant)
{
 if (ah->ah_version != AR5K_AR5210)
  ath5k_hw_reg_write(ah, ant & 0x7, AR5K_DEFAULT_ANTENNA);
}

/**
 * ath5k_hw_set_fast_div() -  Enable/disable fast rx antenna diversity
 * @ah: The &struct ath5k_hw
 * @ee_mode: One of enum ath5k_driver_mode
 * @enable: True to enable, false to disable
 */

static void
ath5k_hw_set_fast_div(struct ath5k_hw *ah, u8 ee_mode, bool enable)
{
 switch (ee_mode) {
 case AR5K_EEPROM_MODE_11G:
  /* XXX: This is set to
 * disabled on initvals !!! */

 case AR5K_EEPROM_MODE_11A:
  if (enable)
   AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_AGCCTL,
     AR5K_PHY_AGCCTL_OFDM_DIV_DIS);
  else
   AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL,
     AR5K_PHY_AGCCTL_OFDM_DIV_DIS);
  break;
 case AR5K_EEPROM_MODE_11B:
  AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL,
     AR5K_PHY_AGCCTL_OFDM_DIV_DIS);
  break;
 default:
  return;
 }

 if (enable) {
  AR5K_REG_WRITE_BITS(ah, AR5K_PHY_RESTART,
    AR5K_PHY_RESTART_DIV_GC, 4);

  AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_FAST_ANT_DIV,
     AR5K_PHY_FAST_ANT_DIV_EN);
 } else {
  AR5K_REG_WRITE_BITS(ah, AR5K_PHY_RESTART,
    AR5K_PHY_RESTART_DIV_GC, 0);

  AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_FAST_ANT_DIV,
     AR5K_PHY_FAST_ANT_DIV_EN);
 }
}

/**
 * ath5k_hw_set_antenna_switch() - Set up antenna switch table
 * @ah: The &struct ath5k_hw
 * @ee_mode: One of enum ath5k_driver_mode
 *
 * Switch table comes from EEPROM and includes information on controlling
 * the 2 antenna RX attenuators
 */

void
ath5k_hw_set_antenna_switch(struct ath5k_hw *ah, u8 ee_mode)
{
 u8 ant0, ant1;

 /*
 * In case a fixed antenna was set as default
 * use the same switch table twice.
 */

 if (ah->ah_ant_mode == AR5K_ANTMODE_FIXED_A)
  ant0 = ant1 = AR5K_ANT_SWTABLE_A;
 else if (ah->ah_ant_mode == AR5K_ANTMODE_FIXED_B)
  ant0 = ant1 = AR5K_ANT_SWTABLE_B;
 else {
  ant0 = AR5K_ANT_SWTABLE_A;
  ant1 = AR5K_ANT_SWTABLE_B;
 }

 /* Set antenna idle switch table */
 AR5K_REG_WRITE_BITS(ah, AR5K_PHY_ANT_CTL,
   AR5K_PHY_ANT_CTL_SWTABLE_IDLE,
   (ah->ah_ant_ctl[ee_mode][AR5K_ANT_CTL] |
   AR5K_PHY_ANT_CTL_TXRX_EN));

 /* Set antenna switch tables */
 ath5k_hw_reg_write(ah, ah->ah_ant_ctl[ee_mode][ant0],
  AR5K_PHY_ANT_SWITCH_TABLE_0);
 ath5k_hw_reg_write(ah, ah->ah_ant_ctl[ee_mode][ant1],
  AR5K_PHY_ANT_SWITCH_TABLE_1);
}

/**
 * ath5k_hw_set_antenna_mode() -  Set antenna operating mode
 * @ah: The &struct ath5k_hw
 * @ant_mode: One of enum ath5k_ant_mode
 */

void
ath5k_hw_set_antenna_mode(struct ath5k_hw *ah, u8 ant_mode)
{
 struct ieee80211_channel *channel = ah->ah_current_channel;
 bool use_def_for_tx, update_def_on_tx, use_def_for_rts, fast_div;
 bool use_def_for_sg;
 int ee_mode;
 u8 def_ant, tx_ant;
 u32 sta_id1 = 0;

 /* if channel is not initialized yet we can't set the antennas
 * so just store the mode. it will be set on the next reset */

 if (channel == NULL) {
  ah->ah_ant_mode = ant_mode;
  return;
 }

 def_ant = ah->ah_def_ant;

 ee_mode = ath5k_eeprom_mode_from_channel(ah, channel);

 switch (ant_mode) {
 case AR5K_ANTMODE_DEFAULT:
  tx_ant = 0;
  use_def_for_tx = false;
  update_def_on_tx = false;
  use_def_for_rts = false;
  use_def_for_sg = false;
  fast_div = true;
  break;
 case AR5K_ANTMODE_FIXED_A:
  def_ant = 1;
  tx_ant = 1;
  use_def_for_tx = true;
  update_def_on_tx = false;
  use_def_for_rts = true;
  use_def_for_sg = true;
  fast_div = false;
  break;
 case AR5K_ANTMODE_FIXED_B:
  def_ant = 2;
  tx_ant = 2;
  use_def_for_tx = true;
  update_def_on_tx = false;
  use_def_for_rts = true;
  use_def_for_sg = true;
  fast_div = false;
  break;
 case AR5K_ANTMODE_SINGLE_AP:
  def_ant = 1; /* updated on tx */
  tx_ant = 0;
  use_def_for_tx = true;
  update_def_on_tx = true;
  use_def_for_rts = true;
  use_def_for_sg = true;
  fast_div = true;
  break;
 case AR5K_ANTMODE_SECTOR_AP:
  tx_ant = 1; /* variable */
  use_def_for_tx = false;
  update_def_on_tx = false;
  use_def_for_rts = true;
  use_def_for_sg = false;
  fast_div = false;
  break;
 case AR5K_ANTMODE_SECTOR_STA:
  tx_ant = 1; /* variable */
  use_def_for_tx = true;
  update_def_on_tx = false;
  use_def_for_rts = true;
  use_def_for_sg = false;
  fast_div = true;
  break;
 case AR5K_ANTMODE_DEBUG:
  def_ant = 1;
  tx_ant = 2;
  use_def_for_tx = false;
  update_def_on_tx = false;
  use_def_for_rts = false;
  use_def_for_sg = false;
  fast_div = false;
  break;
 default:
  return;
 }

 ah->ah_tx_ant = tx_ant;
 ah->ah_ant_mode = ant_mode;
 ah->ah_def_ant = def_ant;

 sta_id1 |= use_def_for_tx ? AR5K_STA_ID1_DEFAULT_ANTENNA : 0;
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=97 G=95

¤ Dauer der Verarbeitung: 0.25 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge