162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2004-2007 Reyk Floeter <reyk@openbsd.org>
362306a36Sopenharmony_ci * Copyright (c) 2006-2009 Nick Kossifidis <mickflemm@gmail.com>
462306a36Sopenharmony_ci * Copyright (c) 2007-2008 Jiri Slaby <jirislaby@gmail.com>
562306a36Sopenharmony_ci * Copyright (c) 2008-2009 Felix Fietkau <nbd@openwrt.org>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Permission to use, copy, modify, and distribute this software for any
862306a36Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above
962306a36Sopenharmony_ci * copyright notice and this permission notice appear in all copies.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1262306a36Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1362306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1462306a36Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1562306a36Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1662306a36Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1762306a36Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/***********************\
2262306a36Sopenharmony_ci* PHY related functions *
2362306a36Sopenharmony_ci\***********************/
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include <linux/delay.h>
2862306a36Sopenharmony_ci#include <linux/slab.h>
2962306a36Sopenharmony_ci#include <linux/sort.h>
3062306a36Sopenharmony_ci#include <asm/unaligned.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#include "ath5k.h"
3362306a36Sopenharmony_ci#include "reg.h"
3462306a36Sopenharmony_ci#include "rfbuffer.h"
3562306a36Sopenharmony_ci#include "rfgain.h"
3662306a36Sopenharmony_ci#include "../regd.h"
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/**
4062306a36Sopenharmony_ci * DOC: PHY related functions
4162306a36Sopenharmony_ci *
4262306a36Sopenharmony_ci * Here we handle the low-level functions related to baseband
4362306a36Sopenharmony_ci * and analog frontend (RF) parts. This is by far the most complex
4462306a36Sopenharmony_ci * part of the hw code so make sure you know what you are doing.
4562306a36Sopenharmony_ci *
4662306a36Sopenharmony_ci * Here is a list of what this is all about:
4762306a36Sopenharmony_ci *
4862306a36Sopenharmony_ci * - Channel setting/switching
4962306a36Sopenharmony_ci *
5062306a36Sopenharmony_ci * - Automatic Gain Control (AGC) calibration
5162306a36Sopenharmony_ci *
5262306a36Sopenharmony_ci * - Noise Floor calibration
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci * - I/Q imbalance calibration (QAM correction)
5562306a36Sopenharmony_ci *
5662306a36Sopenharmony_ci * - Calibration due to thermal changes (gain_F)
5762306a36Sopenharmony_ci *
5862306a36Sopenharmony_ci * - Spur noise mitigation
5962306a36Sopenharmony_ci *
6062306a36Sopenharmony_ci * - RF/PHY initialization for the various operating modes and bwmodes
6162306a36Sopenharmony_ci *
6262306a36Sopenharmony_ci * - Antenna control
6362306a36Sopenharmony_ci *
6462306a36Sopenharmony_ci * - TX power control per channel/rate/packet type
6562306a36Sopenharmony_ci *
6662306a36Sopenharmony_ci * Also have in mind we never got documentation for most of these
6762306a36Sopenharmony_ci * functions, what we have comes mostly from Atheros's code, reverse
6862306a36Sopenharmony_ci * engineering and patent docs/presentations etc.
6962306a36Sopenharmony_ci */
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/******************\
7362306a36Sopenharmony_ci* Helper functions *
7462306a36Sopenharmony_ci\******************/
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/**
7762306a36Sopenharmony_ci * ath5k_hw_radio_revision() - Get the PHY Chip revision
7862306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
7962306a36Sopenharmony_ci * @band: One of enum nl80211_band
8062306a36Sopenharmony_ci *
8162306a36Sopenharmony_ci * Returns the revision number of a 2GHz, 5GHz or single chip
8262306a36Sopenharmony_ci * radio.
8362306a36Sopenharmony_ci */
8462306a36Sopenharmony_ciu16
8562306a36Sopenharmony_ciath5k_hw_radio_revision(struct ath5k_hw *ah, enum nl80211_band band)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	unsigned int i;
8862306a36Sopenharmony_ci	u32 srev;
8962306a36Sopenharmony_ci	u16 ret;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	/*
9262306a36Sopenharmony_ci	 * Set the radio chip access register
9362306a36Sopenharmony_ci	 */
9462306a36Sopenharmony_ci	switch (band) {
9562306a36Sopenharmony_ci	case NL80211_BAND_2GHZ:
9662306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_2GHZ, AR5K_PHY(0));
9762306a36Sopenharmony_ci		break;
9862306a36Sopenharmony_ci	case NL80211_BAND_5GHZ:
9962306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0));
10062306a36Sopenharmony_ci		break;
10162306a36Sopenharmony_ci	default:
10262306a36Sopenharmony_ci		return 0;
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	usleep_range(2000, 2500);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	/* ...wait until PHY is ready and read the selected radio revision */
10862306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, 0x00001c16, AR5K_PHY(0x34));
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	for (i = 0; i < 8; i++)
11162306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, 0x00010000, AR5K_PHY(0x20));
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	if (ah->ah_version == AR5K_AR5210) {
11462306a36Sopenharmony_ci		srev = (ath5k_hw_reg_read(ah, AR5K_PHY(256)) >> 28) & 0xf;
11562306a36Sopenharmony_ci		ret = (u16)ath5k_hw_bitswap(srev, 4) + 1;
11662306a36Sopenharmony_ci	} else {
11762306a36Sopenharmony_ci		srev = (ath5k_hw_reg_read(ah, AR5K_PHY(0x100)) >> 24) & 0xff;
11862306a36Sopenharmony_ci		ret = (u16)ath5k_hw_bitswap(((srev & 0xf0) >> 4) |
11962306a36Sopenharmony_ci				((srev & 0x0f) << 4), 8);
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	/* Reset to the 5GHz mode */
12362306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0));
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	return ret;
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci/**
12962306a36Sopenharmony_ci * ath5k_channel_ok() - Check if a channel is supported by the hw
13062306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
13162306a36Sopenharmony_ci * @channel: The &struct ieee80211_channel
13262306a36Sopenharmony_ci *
13362306a36Sopenharmony_ci * Note: We don't do any regulatory domain checks here, it's just
13462306a36Sopenharmony_ci * a sanity check.
13562306a36Sopenharmony_ci */
13662306a36Sopenharmony_cibool
13762306a36Sopenharmony_ciath5k_channel_ok(struct ath5k_hw *ah, struct ieee80211_channel *channel)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	u16 freq = channel->center_freq;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	/* Check if the channel is in our supported range */
14262306a36Sopenharmony_ci	if (channel->band == NL80211_BAND_2GHZ) {
14362306a36Sopenharmony_ci		if ((freq >= ah->ah_capabilities.cap_range.range_2ghz_min) &&
14462306a36Sopenharmony_ci		    (freq <= ah->ah_capabilities.cap_range.range_2ghz_max))
14562306a36Sopenharmony_ci			return true;
14662306a36Sopenharmony_ci	} else if (channel->band == NL80211_BAND_5GHZ)
14762306a36Sopenharmony_ci		if ((freq >= ah->ah_capabilities.cap_range.range_5ghz_min) &&
14862306a36Sopenharmony_ci		    (freq <= ah->ah_capabilities.cap_range.range_5ghz_max))
14962306a36Sopenharmony_ci			return true;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	return false;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci/**
15562306a36Sopenharmony_ci * ath5k_hw_chan_has_spur_noise() - Check if channel is sensitive to spur noise
15662306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
15762306a36Sopenharmony_ci * @channel: The &struct ieee80211_channel
15862306a36Sopenharmony_ci */
15962306a36Sopenharmony_cibool
16062306a36Sopenharmony_ciath5k_hw_chan_has_spur_noise(struct ath5k_hw *ah,
16162306a36Sopenharmony_ci				struct ieee80211_channel *channel)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	u8 refclk_freq;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	if ((ah->ah_radio == AR5K_RF5112) ||
16662306a36Sopenharmony_ci	(ah->ah_radio == AR5K_RF5413) ||
16762306a36Sopenharmony_ci	(ah->ah_radio == AR5K_RF2413) ||
16862306a36Sopenharmony_ci	(ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4)))
16962306a36Sopenharmony_ci		refclk_freq = 40;
17062306a36Sopenharmony_ci	else
17162306a36Sopenharmony_ci		refclk_freq = 32;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if ((channel->center_freq % refclk_freq != 0) &&
17462306a36Sopenharmony_ci	((channel->center_freq % refclk_freq < 10) ||
17562306a36Sopenharmony_ci	(channel->center_freq % refclk_freq > 22)))
17662306a36Sopenharmony_ci		return true;
17762306a36Sopenharmony_ci	else
17862306a36Sopenharmony_ci		return false;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci/**
18262306a36Sopenharmony_ci * ath5k_hw_rfb_op() - Perform an operation on the given RF Buffer
18362306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
18462306a36Sopenharmony_ci * @rf_regs: The struct ath5k_rf_reg
18562306a36Sopenharmony_ci * @val: New value
18662306a36Sopenharmony_ci * @reg_id: RF register ID
18762306a36Sopenharmony_ci * @set: Indicate we need to swap data
18862306a36Sopenharmony_ci *
18962306a36Sopenharmony_ci * This is an internal function used to modify RF Banks before
19062306a36Sopenharmony_ci * writing them to AR5K_RF_BUFFER. Check out rfbuffer.h for more
19162306a36Sopenharmony_ci * infos.
19262306a36Sopenharmony_ci */
19362306a36Sopenharmony_cistatic unsigned int
19462306a36Sopenharmony_ciath5k_hw_rfb_op(struct ath5k_hw *ah, const struct ath5k_rf_reg *rf_regs,
19562306a36Sopenharmony_ci					u32 val, u8 reg_id, bool set)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	const struct ath5k_rf_reg *rfreg = NULL;
19862306a36Sopenharmony_ci	u8 offset, bank, num_bits, col, position;
19962306a36Sopenharmony_ci	u16 entry;
20062306a36Sopenharmony_ci	u32 mask, data, last_bit, bits_shifted, first_bit;
20162306a36Sopenharmony_ci	u32 *rfb;
20262306a36Sopenharmony_ci	s32 bits_left;
20362306a36Sopenharmony_ci	int i;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	data = 0;
20662306a36Sopenharmony_ci	rfb = ah->ah_rf_banks;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	for (i = 0; i < ah->ah_rf_regs_count; i++) {
20962306a36Sopenharmony_ci		if (rf_regs[i].index == reg_id) {
21062306a36Sopenharmony_ci			rfreg = &rf_regs[i];
21162306a36Sopenharmony_ci			break;
21262306a36Sopenharmony_ci		}
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (rfb == NULL || rfreg == NULL) {
21662306a36Sopenharmony_ci		ATH5K_PRINTF("Rf register not found!\n");
21762306a36Sopenharmony_ci		/* should not happen */
21862306a36Sopenharmony_ci		return 0;
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	bank = rfreg->bank;
22262306a36Sopenharmony_ci	num_bits = rfreg->field.len;
22362306a36Sopenharmony_ci	first_bit = rfreg->field.pos;
22462306a36Sopenharmony_ci	col = rfreg->field.col;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	/* first_bit is an offset from bank's
22762306a36Sopenharmony_ci	 * start. Since we have all banks on
22862306a36Sopenharmony_ci	 * the same array, we use this offset
22962306a36Sopenharmony_ci	 * to mark each bank's start */
23062306a36Sopenharmony_ci	offset = ah->ah_offset[bank];
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	/* Boundary check */
23362306a36Sopenharmony_ci	if (!(col <= 3 && num_bits <= 32 && first_bit + num_bits <= 319)) {
23462306a36Sopenharmony_ci		ATH5K_PRINTF("invalid values at offset %u\n", offset);
23562306a36Sopenharmony_ci		return 0;
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	entry = ((first_bit - 1) / 8) + offset;
23962306a36Sopenharmony_ci	position = (first_bit - 1) % 8;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	if (set)
24262306a36Sopenharmony_ci		data = ath5k_hw_bitswap(val, num_bits);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	for (bits_shifted = 0, bits_left = num_bits; bits_left > 0;
24562306a36Sopenharmony_ci	     position = 0, entry++) {
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci		last_bit = (position + bits_left > 8) ? 8 :
24862306a36Sopenharmony_ci					position + bits_left;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci		mask = (((1 << last_bit) - 1) ^ ((1 << position) - 1)) <<
25162306a36Sopenharmony_ci								(col * 8);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci		if (set) {
25462306a36Sopenharmony_ci			rfb[entry] &= ~mask;
25562306a36Sopenharmony_ci			rfb[entry] |= ((data << position) << (col * 8)) & mask;
25662306a36Sopenharmony_ci			data >>= (8 - position);
25762306a36Sopenharmony_ci		} else {
25862306a36Sopenharmony_ci			data |= (((rfb[entry] & mask) >> (col * 8)) >> position)
25962306a36Sopenharmony_ci				<< bits_shifted;
26062306a36Sopenharmony_ci			bits_shifted += last_bit - position;
26162306a36Sopenharmony_ci		}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci		bits_left -= 8 - position;
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	data = set ? 1 : ath5k_hw_bitswap(data, num_bits);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	return data;
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci/**
27262306a36Sopenharmony_ci * ath5k_hw_write_ofdm_timings() - set OFDM timings on AR5212
27362306a36Sopenharmony_ci * @ah: the &struct ath5k_hw
27462306a36Sopenharmony_ci * @channel: the currently set channel upon reset
27562306a36Sopenharmony_ci *
27662306a36Sopenharmony_ci * Write the delta slope coefficient (used on pilot tracking ?) for OFDM
27762306a36Sopenharmony_ci * operation on the AR5212 upon reset. This is a helper for ath5k_hw_phy_init.
27862306a36Sopenharmony_ci *
27962306a36Sopenharmony_ci * Since delta slope is floating point we split it on its exponent and
28062306a36Sopenharmony_ci * mantissa and provide these values on hw.
28162306a36Sopenharmony_ci *
28262306a36Sopenharmony_ci * For more infos i think this patent is related
28362306a36Sopenharmony_ci * "http://www.freepatentsonline.com/7184495.html"
28462306a36Sopenharmony_ci */
28562306a36Sopenharmony_cistatic inline int
28662306a36Sopenharmony_ciath5k_hw_write_ofdm_timings(struct ath5k_hw *ah,
28762306a36Sopenharmony_ci				struct ieee80211_channel *channel)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	/* Get exponent and mantissa and set it */
29062306a36Sopenharmony_ci	u32 coef_scaled, coef_exp, coef_man,
29162306a36Sopenharmony_ci		ds_coef_exp, ds_coef_man, clock;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	BUG_ON(!(ah->ah_version == AR5K_AR5212) ||
29462306a36Sopenharmony_ci		(channel->hw_value == AR5K_MODE_11B));
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	/* Get coefficient
29762306a36Sopenharmony_ci	 * ALGO: coef = (5 * clock / carrier_freq) / 2
29862306a36Sopenharmony_ci	 * we scale coef by shifting clock value by 24 for
29962306a36Sopenharmony_ci	 * better precision since we use integers */
30062306a36Sopenharmony_ci	switch (ah->ah_bwmode) {
30162306a36Sopenharmony_ci	case AR5K_BWMODE_40MHZ:
30262306a36Sopenharmony_ci		clock = 40 * 2;
30362306a36Sopenharmony_ci		break;
30462306a36Sopenharmony_ci	case AR5K_BWMODE_10MHZ:
30562306a36Sopenharmony_ci		clock = 40 / 2;
30662306a36Sopenharmony_ci		break;
30762306a36Sopenharmony_ci	case AR5K_BWMODE_5MHZ:
30862306a36Sopenharmony_ci		clock = 40 / 4;
30962306a36Sopenharmony_ci		break;
31062306a36Sopenharmony_ci	default:
31162306a36Sopenharmony_ci		clock = 40;
31262306a36Sopenharmony_ci		break;
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci	coef_scaled = ((5 * (clock << 24)) / 2) / channel->center_freq;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	/* Get exponent
31762306a36Sopenharmony_ci	 * ALGO: coef_exp = 14 - highest set bit position */
31862306a36Sopenharmony_ci	coef_exp = ilog2(coef_scaled);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	/* Doesn't make sense if it's zero*/
32162306a36Sopenharmony_ci	if (!coef_scaled || !coef_exp)
32262306a36Sopenharmony_ci		return -EINVAL;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	/* Note: we've shifted coef_scaled by 24 */
32562306a36Sopenharmony_ci	coef_exp = 14 - (coef_exp - 24);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	/* Get mantissa (significant digits)
32962306a36Sopenharmony_ci	 * ALGO: coef_mant = floor(coef_scaled* 2^coef_exp+0.5) */
33062306a36Sopenharmony_ci	coef_man = coef_scaled +
33162306a36Sopenharmony_ci		(1 << (24 - coef_exp - 1));
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	/* Calculate delta slope coefficient exponent
33462306a36Sopenharmony_ci	 * and mantissa (remove scaling) and set them on hw */
33562306a36Sopenharmony_ci	ds_coef_man = coef_man >> (24 - coef_exp);
33662306a36Sopenharmony_ci	ds_coef_exp = coef_exp - 16;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_3,
33962306a36Sopenharmony_ci		AR5K_PHY_TIMING_3_DSC_MAN, ds_coef_man);
34062306a36Sopenharmony_ci	AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_3,
34162306a36Sopenharmony_ci		AR5K_PHY_TIMING_3_DSC_EXP, ds_coef_exp);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	return 0;
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci/**
34762306a36Sopenharmony_ci * ath5k_hw_phy_disable() - Disable PHY
34862306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
34962306a36Sopenharmony_ci */
35062306a36Sopenharmony_ciint ath5k_hw_phy_disable(struct ath5k_hw *ah)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	/*Just a try M.F.*/
35362306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, AR5K_PHY_ACT_DISABLE, AR5K_PHY_ACT);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	return 0;
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci/**
35962306a36Sopenharmony_ci * ath5k_hw_wait_for_synth() - Wait for synth to settle
36062306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
36162306a36Sopenharmony_ci * @channel: The &struct ieee80211_channel
36262306a36Sopenharmony_ci */
36362306a36Sopenharmony_cistatic void
36462306a36Sopenharmony_ciath5k_hw_wait_for_synth(struct ath5k_hw *ah,
36562306a36Sopenharmony_ci			struct ieee80211_channel *channel)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	/*
36862306a36Sopenharmony_ci	 * On 5211+ read activation -> rx delay
36962306a36Sopenharmony_ci	 * and use it (100ns steps).
37062306a36Sopenharmony_ci	 */
37162306a36Sopenharmony_ci	if (ah->ah_version != AR5K_AR5210) {
37262306a36Sopenharmony_ci		u32 delay;
37362306a36Sopenharmony_ci		delay = ath5k_hw_reg_read(ah, AR5K_PHY_RX_DELAY) &
37462306a36Sopenharmony_ci			AR5K_PHY_RX_DELAY_M;
37562306a36Sopenharmony_ci		delay = (channel->hw_value == AR5K_MODE_11B) ?
37662306a36Sopenharmony_ci			((delay << 2) / 22) : (delay / 10);
37762306a36Sopenharmony_ci		if (ah->ah_bwmode == AR5K_BWMODE_10MHZ)
37862306a36Sopenharmony_ci			delay = delay << 1;
37962306a36Sopenharmony_ci		if (ah->ah_bwmode == AR5K_BWMODE_5MHZ)
38062306a36Sopenharmony_ci			delay = delay << 2;
38162306a36Sopenharmony_ci		/* XXX: /2 on turbo ? Let's be safe
38262306a36Sopenharmony_ci		 * for now */
38362306a36Sopenharmony_ci		usleep_range(100 + delay, 100 + (2 * delay));
38462306a36Sopenharmony_ci	} else {
38562306a36Sopenharmony_ci		usleep_range(1000, 1500);
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci/**********************\
39162306a36Sopenharmony_ci* RF Gain optimization *
39262306a36Sopenharmony_ci\**********************/
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci/**
39562306a36Sopenharmony_ci * DOC: RF Gain optimization
39662306a36Sopenharmony_ci *
39762306a36Sopenharmony_ci * This code is used to optimize RF gain on different environments
39862306a36Sopenharmony_ci * (temperature mostly) based on feedback from a power detector.
39962306a36Sopenharmony_ci *
40062306a36Sopenharmony_ci * It's only used on RF5111 and RF5112, later RF chips seem to have
40162306a36Sopenharmony_ci * auto adjustment on hw -notice they have a much smaller BANK 7 and
40262306a36Sopenharmony_ci * no gain optimization ladder-.
40362306a36Sopenharmony_ci *
40462306a36Sopenharmony_ci * For more infos check out this patent doc
40562306a36Sopenharmony_ci * "http://www.freepatentsonline.com/7400691.html"
40662306a36Sopenharmony_ci *
40762306a36Sopenharmony_ci * This paper describes power drops as seen on the receiver due to
40862306a36Sopenharmony_ci * probe packets
40962306a36Sopenharmony_ci * "http://www.cnri.dit.ie/publications/ICT08%20-%20Practical%20Issues
41062306a36Sopenharmony_ci * %20of%20Power%20Control.pdf"
41162306a36Sopenharmony_ci *
41262306a36Sopenharmony_ci * And this is the MadWiFi bug entry related to the above
41362306a36Sopenharmony_ci * "http://madwifi-project.org/ticket/1659"
41462306a36Sopenharmony_ci * with various measurements and diagrams
41562306a36Sopenharmony_ci */
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci/**
41862306a36Sopenharmony_ci * ath5k_hw_rfgain_opt_init() - Initialize ah_gain during attach
41962306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
42062306a36Sopenharmony_ci */
42162306a36Sopenharmony_ciint ath5k_hw_rfgain_opt_init(struct ath5k_hw *ah)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	/* Initialize the gain optimization values */
42462306a36Sopenharmony_ci	switch (ah->ah_radio) {
42562306a36Sopenharmony_ci	case AR5K_RF5111:
42662306a36Sopenharmony_ci		ah->ah_gain.g_step_idx = rfgain_opt_5111.go_default;
42762306a36Sopenharmony_ci		ah->ah_gain.g_low = 20;
42862306a36Sopenharmony_ci		ah->ah_gain.g_high = 35;
42962306a36Sopenharmony_ci		ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE;
43062306a36Sopenharmony_ci		break;
43162306a36Sopenharmony_ci	case AR5K_RF5112:
43262306a36Sopenharmony_ci		ah->ah_gain.g_step_idx = rfgain_opt_5112.go_default;
43362306a36Sopenharmony_ci		ah->ah_gain.g_low = 20;
43462306a36Sopenharmony_ci		ah->ah_gain.g_high = 85;
43562306a36Sopenharmony_ci		ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE;
43662306a36Sopenharmony_ci		break;
43762306a36Sopenharmony_ci	default:
43862306a36Sopenharmony_ci		return -EINVAL;
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	return 0;
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci/**
44562306a36Sopenharmony_ci * ath5k_hw_request_rfgain_probe() - Request a PAPD probe packet
44662306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
44762306a36Sopenharmony_ci *
44862306a36Sopenharmony_ci * Schedules a gain probe check on the next transmitted packet.
44962306a36Sopenharmony_ci * That means our next packet is going to be sent with lower
45062306a36Sopenharmony_ci * tx power and a Peak to Average Power Detector (PAPD) will try
45162306a36Sopenharmony_ci * to measure the gain.
45262306a36Sopenharmony_ci *
45362306a36Sopenharmony_ci * TODO: Force a tx packet (bypassing PCU arbitrator etc)
45462306a36Sopenharmony_ci * just after we enable the probe so that we don't mess with
45562306a36Sopenharmony_ci * standard traffic.
45662306a36Sopenharmony_ci */
45762306a36Sopenharmony_cistatic void
45862306a36Sopenharmony_ciath5k_hw_request_rfgain_probe(struct ath5k_hw *ah)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	/* Skip if gain calibration is inactive or
46262306a36Sopenharmony_ci	 * we already handle a probe request */
46362306a36Sopenharmony_ci	if (ah->ah_gain.g_state != AR5K_RFGAIN_ACTIVE)
46462306a36Sopenharmony_ci		return;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	/* Send the packet with 2dB below max power as
46762306a36Sopenharmony_ci	 * patent doc suggest */
46862306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txpower.txp_ofdm - 4,
46962306a36Sopenharmony_ci			AR5K_PHY_PAPD_PROBE_TXPOWER) |
47062306a36Sopenharmony_ci			AR5K_PHY_PAPD_PROBE_TX_NEXT, AR5K_PHY_PAPD_PROBE);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	ah->ah_gain.g_state = AR5K_RFGAIN_READ_REQUESTED;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci/**
47762306a36Sopenharmony_ci * ath5k_hw_rf_gainf_corr() - Calculate Gain_F measurement correction
47862306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
47962306a36Sopenharmony_ci *
48062306a36Sopenharmony_ci * Calculate Gain_F measurement correction
48162306a36Sopenharmony_ci * based on the current step for RF5112 rev. 2
48262306a36Sopenharmony_ci */
48362306a36Sopenharmony_cistatic u32
48462306a36Sopenharmony_ciath5k_hw_rf_gainf_corr(struct ath5k_hw *ah)
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	u32 mix, step;
48762306a36Sopenharmony_ci	const struct ath5k_gain_opt *go;
48862306a36Sopenharmony_ci	const struct ath5k_gain_opt_step *g_step;
48962306a36Sopenharmony_ci	const struct ath5k_rf_reg *rf_regs;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	/* Only RF5112 Rev. 2 supports it */
49262306a36Sopenharmony_ci	if ((ah->ah_radio != AR5K_RF5112) ||
49362306a36Sopenharmony_ci	(ah->ah_radio_5ghz_revision <= AR5K_SREV_RAD_5112A))
49462306a36Sopenharmony_ci		return 0;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	go = &rfgain_opt_5112;
49762306a36Sopenharmony_ci	rf_regs = rf_regs_5112a;
49862306a36Sopenharmony_ci	ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5112a);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	g_step = &go->go_step[ah->ah_gain.g_step_idx];
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	if (ah->ah_rf_banks == NULL)
50362306a36Sopenharmony_ci		return 0;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	ah->ah_gain.g_f_corr = 0;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	/* No VGA (Variable Gain Amplifier) override, skip */
50862306a36Sopenharmony_ci	if (ath5k_hw_rfb_op(ah, rf_regs, 0, AR5K_RF_MIXVGA_OVR, false) != 1)
50962306a36Sopenharmony_ci		return 0;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	/* Mix gain stepping */
51262306a36Sopenharmony_ci	step = ath5k_hw_rfb_op(ah, rf_regs, 0, AR5K_RF_MIXGAIN_STEP, false);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	/* Mix gain override */
51562306a36Sopenharmony_ci	mix = g_step->gos_param[0];
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	switch (mix) {
51862306a36Sopenharmony_ci	case 3:
51962306a36Sopenharmony_ci		ah->ah_gain.g_f_corr = step * 2;
52062306a36Sopenharmony_ci		break;
52162306a36Sopenharmony_ci	case 2:
52262306a36Sopenharmony_ci		ah->ah_gain.g_f_corr = (step - 5) * 2;
52362306a36Sopenharmony_ci		break;
52462306a36Sopenharmony_ci	case 1:
52562306a36Sopenharmony_ci		ah->ah_gain.g_f_corr = step;
52662306a36Sopenharmony_ci		break;
52762306a36Sopenharmony_ci	default:
52862306a36Sopenharmony_ci		ah->ah_gain.g_f_corr = 0;
52962306a36Sopenharmony_ci		break;
53062306a36Sopenharmony_ci	}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	return ah->ah_gain.g_f_corr;
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci/**
53662306a36Sopenharmony_ci * ath5k_hw_rf_check_gainf_readback() - Validate Gain_F feedback from detector
53762306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
53862306a36Sopenharmony_ci *
53962306a36Sopenharmony_ci * Check if current gain_F measurement is in the range of our
54062306a36Sopenharmony_ci * power detector windows. If we get a measurement outside range
54162306a36Sopenharmony_ci * we know it's not accurate (detectors can't measure anything outside
54262306a36Sopenharmony_ci * their detection window) so we must ignore it.
54362306a36Sopenharmony_ci *
54462306a36Sopenharmony_ci * Returns true if readback was O.K. or false on failure
54562306a36Sopenharmony_ci */
54662306a36Sopenharmony_cistatic bool
54762306a36Sopenharmony_ciath5k_hw_rf_check_gainf_readback(struct ath5k_hw *ah)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	const struct ath5k_rf_reg *rf_regs;
55062306a36Sopenharmony_ci	u32 step, mix_ovr, level[4];
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	if (ah->ah_rf_banks == NULL)
55362306a36Sopenharmony_ci		return false;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	if (ah->ah_radio == AR5K_RF5111) {
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci		rf_regs = rf_regs_5111;
55862306a36Sopenharmony_ci		ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5111);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci		step = ath5k_hw_rfb_op(ah, rf_regs, 0, AR5K_RF_RFGAIN_STEP,
56162306a36Sopenharmony_ci			false);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci		level[0] = 0;
56462306a36Sopenharmony_ci		level[1] = (step == 63) ? 50 : step + 4;
56562306a36Sopenharmony_ci		level[2] = (step != 63) ? 64 : level[0];
56662306a36Sopenharmony_ci		level[3] = level[2] + 50;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci		ah->ah_gain.g_high = level[3] -
56962306a36Sopenharmony_ci			(step == 63 ? AR5K_GAIN_DYN_ADJUST_HI_MARGIN : -5);
57062306a36Sopenharmony_ci		ah->ah_gain.g_low = level[0] +
57162306a36Sopenharmony_ci			(step == 63 ? AR5K_GAIN_DYN_ADJUST_LO_MARGIN : 0);
57262306a36Sopenharmony_ci	} else {
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci		rf_regs = rf_regs_5112;
57562306a36Sopenharmony_ci		ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5112);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci		mix_ovr = ath5k_hw_rfb_op(ah, rf_regs, 0, AR5K_RF_MIXVGA_OVR,
57862306a36Sopenharmony_ci			false);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci		level[0] = level[2] = 0;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci		if (mix_ovr == 1) {
58362306a36Sopenharmony_ci			level[1] = level[3] = 83;
58462306a36Sopenharmony_ci		} else {
58562306a36Sopenharmony_ci			level[1] = level[3] = 107;
58662306a36Sopenharmony_ci			ah->ah_gain.g_high = 55;
58762306a36Sopenharmony_ci		}
58862306a36Sopenharmony_ci	}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	return (ah->ah_gain.g_current >= level[0] &&
59162306a36Sopenharmony_ci			ah->ah_gain.g_current <= level[1]) ||
59262306a36Sopenharmony_ci		(ah->ah_gain.g_current >= level[2] &&
59362306a36Sopenharmony_ci			ah->ah_gain.g_current <= level[3]);
59462306a36Sopenharmony_ci}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci/**
59762306a36Sopenharmony_ci * ath5k_hw_rf_gainf_adjust() - Perform Gain_F adjustment
59862306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
59962306a36Sopenharmony_ci *
60062306a36Sopenharmony_ci * Choose the right target gain based on current gain
60162306a36Sopenharmony_ci * and RF gain optimization ladder
60262306a36Sopenharmony_ci */
60362306a36Sopenharmony_cistatic s8
60462306a36Sopenharmony_ciath5k_hw_rf_gainf_adjust(struct ath5k_hw *ah)
60562306a36Sopenharmony_ci{
60662306a36Sopenharmony_ci	const struct ath5k_gain_opt *go;
60762306a36Sopenharmony_ci	const struct ath5k_gain_opt_step *g_step;
60862306a36Sopenharmony_ci	int ret = 0;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	switch (ah->ah_radio) {
61162306a36Sopenharmony_ci	case AR5K_RF5111:
61262306a36Sopenharmony_ci		go = &rfgain_opt_5111;
61362306a36Sopenharmony_ci		break;
61462306a36Sopenharmony_ci	case AR5K_RF5112:
61562306a36Sopenharmony_ci		go = &rfgain_opt_5112;
61662306a36Sopenharmony_ci		break;
61762306a36Sopenharmony_ci	default:
61862306a36Sopenharmony_ci		return 0;
61962306a36Sopenharmony_ci	}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	g_step = &go->go_step[ah->ah_gain.g_step_idx];
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	if (ah->ah_gain.g_current >= ah->ah_gain.g_high) {
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci		/* Reached maximum */
62662306a36Sopenharmony_ci		if (ah->ah_gain.g_step_idx == 0)
62762306a36Sopenharmony_ci			return -1;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci		for (ah->ah_gain.g_target = ah->ah_gain.g_current;
63062306a36Sopenharmony_ci				ah->ah_gain.g_target >=  ah->ah_gain.g_high &&
63162306a36Sopenharmony_ci				ah->ah_gain.g_step_idx > 0;
63262306a36Sopenharmony_ci				g_step = &go->go_step[ah->ah_gain.g_step_idx])
63362306a36Sopenharmony_ci			ah->ah_gain.g_target -= 2 *
63462306a36Sopenharmony_ci			    (go->go_step[--(ah->ah_gain.g_step_idx)].gos_gain -
63562306a36Sopenharmony_ci			    g_step->gos_gain);
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci		ret = 1;
63862306a36Sopenharmony_ci		goto done;
63962306a36Sopenharmony_ci	}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	if (ah->ah_gain.g_current <= ah->ah_gain.g_low) {
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci		/* Reached minimum */
64462306a36Sopenharmony_ci		if (ah->ah_gain.g_step_idx == (go->go_steps_count - 1))
64562306a36Sopenharmony_ci			return -2;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci		for (ah->ah_gain.g_target = ah->ah_gain.g_current;
64862306a36Sopenharmony_ci				ah->ah_gain.g_target <= ah->ah_gain.g_low &&
64962306a36Sopenharmony_ci				ah->ah_gain.g_step_idx < go->go_steps_count - 1;
65062306a36Sopenharmony_ci				g_step = &go->go_step[ah->ah_gain.g_step_idx])
65162306a36Sopenharmony_ci			ah->ah_gain.g_target -= 2 *
65262306a36Sopenharmony_ci			    (go->go_step[++ah->ah_gain.g_step_idx].gos_gain -
65362306a36Sopenharmony_ci			    g_step->gos_gain);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci		ret = 2;
65662306a36Sopenharmony_ci		goto done;
65762306a36Sopenharmony_ci	}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_cidone:
66062306a36Sopenharmony_ci	ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE,
66162306a36Sopenharmony_ci		"ret %d, gain step %u, current gain %u, target gain %u\n",
66262306a36Sopenharmony_ci		ret, ah->ah_gain.g_step_idx, ah->ah_gain.g_current,
66362306a36Sopenharmony_ci		ah->ah_gain.g_target);
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	return ret;
66662306a36Sopenharmony_ci}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci/**
66962306a36Sopenharmony_ci * ath5k_hw_gainf_calibrate() - Do a gain_F calibration
67062306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
67162306a36Sopenharmony_ci *
67262306a36Sopenharmony_ci * Main callback for thermal RF gain calibration engine
67362306a36Sopenharmony_ci * Check for a new gain reading and schedule an adjustment
67462306a36Sopenharmony_ci * if needed.
67562306a36Sopenharmony_ci *
67662306a36Sopenharmony_ci * Returns one of enum ath5k_rfgain codes
67762306a36Sopenharmony_ci */
67862306a36Sopenharmony_cienum ath5k_rfgain
67962306a36Sopenharmony_ciath5k_hw_gainf_calibrate(struct ath5k_hw *ah)
68062306a36Sopenharmony_ci{
68162306a36Sopenharmony_ci	u32 data, type;
68262306a36Sopenharmony_ci	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	if (ah->ah_rf_banks == NULL ||
68562306a36Sopenharmony_ci	ah->ah_gain.g_state == AR5K_RFGAIN_INACTIVE)
68662306a36Sopenharmony_ci		return AR5K_RFGAIN_INACTIVE;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	/* No check requested, either engine is inactive
68962306a36Sopenharmony_ci	 * or an adjustment is already requested */
69062306a36Sopenharmony_ci	if (ah->ah_gain.g_state != AR5K_RFGAIN_READ_REQUESTED)
69162306a36Sopenharmony_ci		goto done;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	/* Read the PAPD (Peak to Average Power Detector)
69462306a36Sopenharmony_ci	 * register */
69562306a36Sopenharmony_ci	data = ath5k_hw_reg_read(ah, AR5K_PHY_PAPD_PROBE);
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	/* No probe is scheduled, read gain_F measurement */
69862306a36Sopenharmony_ci	if (!(data & AR5K_PHY_PAPD_PROBE_TX_NEXT)) {
69962306a36Sopenharmony_ci		ah->ah_gain.g_current = data >> AR5K_PHY_PAPD_PROBE_GAINF_S;
70062306a36Sopenharmony_ci		type = AR5K_REG_MS(data, AR5K_PHY_PAPD_PROBE_TYPE);
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci		/* If tx packet is CCK correct the gain_F measurement
70362306a36Sopenharmony_ci		 * by cck ofdm gain delta */
70462306a36Sopenharmony_ci		if (type == AR5K_PHY_PAPD_PROBE_TYPE_CCK) {
70562306a36Sopenharmony_ci			if (ah->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A)
70662306a36Sopenharmony_ci				ah->ah_gain.g_current +=
70762306a36Sopenharmony_ci					ee->ee_cck_ofdm_gain_delta;
70862306a36Sopenharmony_ci			else
70962306a36Sopenharmony_ci				ah->ah_gain.g_current +=
71062306a36Sopenharmony_ci					AR5K_GAIN_CCK_PROBE_CORR;
71162306a36Sopenharmony_ci		}
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci		/* Further correct gain_F measurement for
71462306a36Sopenharmony_ci		 * RF5112A radios */
71562306a36Sopenharmony_ci		if (ah->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A) {
71662306a36Sopenharmony_ci			ath5k_hw_rf_gainf_corr(ah);
71762306a36Sopenharmony_ci			ah->ah_gain.g_current =
71862306a36Sopenharmony_ci				ah->ah_gain.g_current >= ah->ah_gain.g_f_corr ?
71962306a36Sopenharmony_ci				(ah->ah_gain.g_current - ah->ah_gain.g_f_corr) :
72062306a36Sopenharmony_ci				0;
72162306a36Sopenharmony_ci		}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci		/* Check if measurement is ok and if we need
72462306a36Sopenharmony_ci		 * to adjust gain, schedule a gain adjustment,
72562306a36Sopenharmony_ci		 * else switch back to the active state */
72662306a36Sopenharmony_ci		if (ath5k_hw_rf_check_gainf_readback(ah) &&
72762306a36Sopenharmony_ci		AR5K_GAIN_CHECK_ADJUST(&ah->ah_gain) &&
72862306a36Sopenharmony_ci		ath5k_hw_rf_gainf_adjust(ah)) {
72962306a36Sopenharmony_ci			ah->ah_gain.g_state = AR5K_RFGAIN_NEED_CHANGE;
73062306a36Sopenharmony_ci		} else {
73162306a36Sopenharmony_ci			ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE;
73262306a36Sopenharmony_ci		}
73362306a36Sopenharmony_ci	}
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_cidone:
73662306a36Sopenharmony_ci	return ah->ah_gain.g_state;
73762306a36Sopenharmony_ci}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci/**
74062306a36Sopenharmony_ci * ath5k_hw_rfgain_init() - Write initial RF gain settings to hw
74162306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
74262306a36Sopenharmony_ci * @band: One of enum nl80211_band
74362306a36Sopenharmony_ci *
74462306a36Sopenharmony_ci * Write initial RF gain table to set the RF sensitivity.
74562306a36Sopenharmony_ci *
74662306a36Sopenharmony_ci * NOTE: This one works on all RF chips and has nothing to do
74762306a36Sopenharmony_ci * with Gain_F calibration
74862306a36Sopenharmony_ci */
74962306a36Sopenharmony_cistatic int
75062306a36Sopenharmony_ciath5k_hw_rfgain_init(struct ath5k_hw *ah, enum nl80211_band band)
75162306a36Sopenharmony_ci{
75262306a36Sopenharmony_ci	const struct ath5k_ini_rfgain *ath5k_rfg;
75362306a36Sopenharmony_ci	unsigned int i, size, index;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	switch (ah->ah_radio) {
75662306a36Sopenharmony_ci	case AR5K_RF5111:
75762306a36Sopenharmony_ci		ath5k_rfg = rfgain_5111;
75862306a36Sopenharmony_ci		size = ARRAY_SIZE(rfgain_5111);
75962306a36Sopenharmony_ci		break;
76062306a36Sopenharmony_ci	case AR5K_RF5112:
76162306a36Sopenharmony_ci		ath5k_rfg = rfgain_5112;
76262306a36Sopenharmony_ci		size = ARRAY_SIZE(rfgain_5112);
76362306a36Sopenharmony_ci		break;
76462306a36Sopenharmony_ci	case AR5K_RF2413:
76562306a36Sopenharmony_ci		ath5k_rfg = rfgain_2413;
76662306a36Sopenharmony_ci		size = ARRAY_SIZE(rfgain_2413);
76762306a36Sopenharmony_ci		break;
76862306a36Sopenharmony_ci	case AR5K_RF2316:
76962306a36Sopenharmony_ci		ath5k_rfg = rfgain_2316;
77062306a36Sopenharmony_ci		size = ARRAY_SIZE(rfgain_2316);
77162306a36Sopenharmony_ci		break;
77262306a36Sopenharmony_ci	case AR5K_RF5413:
77362306a36Sopenharmony_ci		ath5k_rfg = rfgain_5413;
77462306a36Sopenharmony_ci		size = ARRAY_SIZE(rfgain_5413);
77562306a36Sopenharmony_ci		break;
77662306a36Sopenharmony_ci	case AR5K_RF2317:
77762306a36Sopenharmony_ci	case AR5K_RF2425:
77862306a36Sopenharmony_ci		ath5k_rfg = rfgain_2425;
77962306a36Sopenharmony_ci		size = ARRAY_SIZE(rfgain_2425);
78062306a36Sopenharmony_ci		break;
78162306a36Sopenharmony_ci	default:
78262306a36Sopenharmony_ci		return -EINVAL;
78362306a36Sopenharmony_ci	}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	index = (band == NL80211_BAND_2GHZ) ? 1 : 0;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	for (i = 0; i < size; i++) {
78862306a36Sopenharmony_ci		AR5K_REG_WAIT(i);
78962306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, ath5k_rfg[i].rfg_value[index],
79062306a36Sopenharmony_ci			(u32)ath5k_rfg[i].rfg_register);
79162306a36Sopenharmony_ci	}
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	return 0;
79462306a36Sopenharmony_ci}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci/********************\
79862306a36Sopenharmony_ci* RF Registers setup *
79962306a36Sopenharmony_ci\********************/
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci/**
80262306a36Sopenharmony_ci * ath5k_hw_rfregs_init() - Initialize RF register settings
80362306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
80462306a36Sopenharmony_ci * @channel: The &struct ieee80211_channel
80562306a36Sopenharmony_ci * @mode: One of enum ath5k_driver_mode
80662306a36Sopenharmony_ci *
80762306a36Sopenharmony_ci * Setup RF registers by writing RF buffer on hw. For
80862306a36Sopenharmony_ci * more infos on this, check out rfbuffer.h
80962306a36Sopenharmony_ci */
81062306a36Sopenharmony_cistatic int
81162306a36Sopenharmony_ciath5k_hw_rfregs_init(struct ath5k_hw *ah,
81262306a36Sopenharmony_ci			struct ieee80211_channel *channel,
81362306a36Sopenharmony_ci			unsigned int mode)
81462306a36Sopenharmony_ci{
81562306a36Sopenharmony_ci	const struct ath5k_rf_reg *rf_regs;
81662306a36Sopenharmony_ci	const struct ath5k_ini_rfbuffer *ini_rfb;
81762306a36Sopenharmony_ci	const struct ath5k_gain_opt *go = NULL;
81862306a36Sopenharmony_ci	const struct ath5k_gain_opt_step *g_step;
81962306a36Sopenharmony_ci	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
82062306a36Sopenharmony_ci	u8 ee_mode = 0;
82162306a36Sopenharmony_ci	u32 *rfb;
82262306a36Sopenharmony_ci	int i, obdb = -1, bank = -1;
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	switch (ah->ah_radio) {
82562306a36Sopenharmony_ci	case AR5K_RF5111:
82662306a36Sopenharmony_ci		rf_regs = rf_regs_5111;
82762306a36Sopenharmony_ci		ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5111);
82862306a36Sopenharmony_ci		ini_rfb = rfb_5111;
82962306a36Sopenharmony_ci		ah->ah_rf_banks_size = ARRAY_SIZE(rfb_5111);
83062306a36Sopenharmony_ci		go = &rfgain_opt_5111;
83162306a36Sopenharmony_ci		break;
83262306a36Sopenharmony_ci	case AR5K_RF5112:
83362306a36Sopenharmony_ci		if (ah->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A) {
83462306a36Sopenharmony_ci			rf_regs = rf_regs_5112a;
83562306a36Sopenharmony_ci			ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5112a);
83662306a36Sopenharmony_ci			ini_rfb = rfb_5112a;
83762306a36Sopenharmony_ci			ah->ah_rf_banks_size = ARRAY_SIZE(rfb_5112a);
83862306a36Sopenharmony_ci		} else {
83962306a36Sopenharmony_ci			rf_regs = rf_regs_5112;
84062306a36Sopenharmony_ci			ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5112);
84162306a36Sopenharmony_ci			ini_rfb = rfb_5112;
84262306a36Sopenharmony_ci			ah->ah_rf_banks_size = ARRAY_SIZE(rfb_5112);
84362306a36Sopenharmony_ci		}
84462306a36Sopenharmony_ci		go = &rfgain_opt_5112;
84562306a36Sopenharmony_ci		break;
84662306a36Sopenharmony_ci	case AR5K_RF2413:
84762306a36Sopenharmony_ci		rf_regs = rf_regs_2413;
84862306a36Sopenharmony_ci		ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_2413);
84962306a36Sopenharmony_ci		ini_rfb = rfb_2413;
85062306a36Sopenharmony_ci		ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2413);
85162306a36Sopenharmony_ci		break;
85262306a36Sopenharmony_ci	case AR5K_RF2316:
85362306a36Sopenharmony_ci		rf_regs = rf_regs_2316;
85462306a36Sopenharmony_ci		ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_2316);
85562306a36Sopenharmony_ci		ini_rfb = rfb_2316;
85662306a36Sopenharmony_ci		ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2316);
85762306a36Sopenharmony_ci		break;
85862306a36Sopenharmony_ci	case AR5K_RF5413:
85962306a36Sopenharmony_ci		rf_regs = rf_regs_5413;
86062306a36Sopenharmony_ci		ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_5413);
86162306a36Sopenharmony_ci		ini_rfb = rfb_5413;
86262306a36Sopenharmony_ci		ah->ah_rf_banks_size = ARRAY_SIZE(rfb_5413);
86362306a36Sopenharmony_ci		break;
86462306a36Sopenharmony_ci	case AR5K_RF2317:
86562306a36Sopenharmony_ci		rf_regs = rf_regs_2425;
86662306a36Sopenharmony_ci		ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_2425);
86762306a36Sopenharmony_ci		ini_rfb = rfb_2317;
86862306a36Sopenharmony_ci		ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2317);
86962306a36Sopenharmony_ci		break;
87062306a36Sopenharmony_ci	case AR5K_RF2425:
87162306a36Sopenharmony_ci		rf_regs = rf_regs_2425;
87262306a36Sopenharmony_ci		ah->ah_rf_regs_count = ARRAY_SIZE(rf_regs_2425);
87362306a36Sopenharmony_ci		if (ah->ah_mac_srev < AR5K_SREV_AR2417) {
87462306a36Sopenharmony_ci			ini_rfb = rfb_2425;
87562306a36Sopenharmony_ci			ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2425);
87662306a36Sopenharmony_ci		} else {
87762306a36Sopenharmony_ci			ini_rfb = rfb_2417;
87862306a36Sopenharmony_ci			ah->ah_rf_banks_size = ARRAY_SIZE(rfb_2417);
87962306a36Sopenharmony_ci		}
88062306a36Sopenharmony_ci		break;
88162306a36Sopenharmony_ci	default:
88262306a36Sopenharmony_ci		return -EINVAL;
88362306a36Sopenharmony_ci	}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	/* If it's the first time we set RF buffer, allocate
88662306a36Sopenharmony_ci	 * ah->ah_rf_banks based on ah->ah_rf_banks_size
88762306a36Sopenharmony_ci	 * we set above */
88862306a36Sopenharmony_ci	if (ah->ah_rf_banks == NULL) {
88962306a36Sopenharmony_ci		ah->ah_rf_banks = kmalloc_array(ah->ah_rf_banks_size,
89062306a36Sopenharmony_ci								sizeof(u32),
89162306a36Sopenharmony_ci								GFP_KERNEL);
89262306a36Sopenharmony_ci		if (ah->ah_rf_banks == NULL) {
89362306a36Sopenharmony_ci			ATH5K_ERR(ah, "out of memory\n");
89462306a36Sopenharmony_ci			return -ENOMEM;
89562306a36Sopenharmony_ci		}
89662306a36Sopenharmony_ci	}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	/* Copy values to modify them */
89962306a36Sopenharmony_ci	rfb = ah->ah_rf_banks;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	for (i = 0; i < ah->ah_rf_banks_size; i++) {
90262306a36Sopenharmony_ci		if (ini_rfb[i].rfb_bank >= AR5K_MAX_RF_BANKS) {
90362306a36Sopenharmony_ci			ATH5K_ERR(ah, "invalid bank\n");
90462306a36Sopenharmony_ci			return -EINVAL;
90562306a36Sopenharmony_ci		}
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci		/* Bank changed, write down the offset */
90862306a36Sopenharmony_ci		if (bank != ini_rfb[i].rfb_bank) {
90962306a36Sopenharmony_ci			bank = ini_rfb[i].rfb_bank;
91062306a36Sopenharmony_ci			ah->ah_offset[bank] = i;
91162306a36Sopenharmony_ci		}
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci		rfb[i] = ini_rfb[i].rfb_mode_data[mode];
91462306a36Sopenharmony_ci	}
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	/* Set Output and Driver bias current (OB/DB) */
91762306a36Sopenharmony_ci	if (channel->band == NL80211_BAND_2GHZ) {
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci		if (channel->hw_value == AR5K_MODE_11B)
92062306a36Sopenharmony_ci			ee_mode = AR5K_EEPROM_MODE_11B;
92162306a36Sopenharmony_ci		else
92262306a36Sopenharmony_ci			ee_mode = AR5K_EEPROM_MODE_11G;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci		/* For RF511X/RF211X combination we
92562306a36Sopenharmony_ci		 * use b_OB and b_DB parameters stored
92662306a36Sopenharmony_ci		 * in eeprom on ee->ee_ob[ee_mode][0]
92762306a36Sopenharmony_ci		 *
92862306a36Sopenharmony_ci		 * For all other chips we use OB/DB for 2GHz
92962306a36Sopenharmony_ci		 * stored in the b/g modal section just like
93062306a36Sopenharmony_ci		 * 802.11a on ee->ee_ob[ee_mode][1] */
93162306a36Sopenharmony_ci		if ((ah->ah_radio == AR5K_RF5111) ||
93262306a36Sopenharmony_ci		(ah->ah_radio == AR5K_RF5112))
93362306a36Sopenharmony_ci			obdb = 0;
93462306a36Sopenharmony_ci		else
93562306a36Sopenharmony_ci			obdb = 1;
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci		ath5k_hw_rfb_op(ah, rf_regs, ee->ee_ob[ee_mode][obdb],
93862306a36Sopenharmony_ci						AR5K_RF_OB_2GHZ, true);
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci		ath5k_hw_rfb_op(ah, rf_regs, ee->ee_db[ee_mode][obdb],
94162306a36Sopenharmony_ci						AR5K_RF_DB_2GHZ, true);
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	/* RF5111 always needs OB/DB for 5GHz, even if we use 2GHz */
94462306a36Sopenharmony_ci	} else if ((channel->band == NL80211_BAND_5GHZ) ||
94562306a36Sopenharmony_ci			(ah->ah_radio == AR5K_RF5111)) {
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci		/* For 11a, Turbo and XR we need to choose
94862306a36Sopenharmony_ci		 * OB/DB based on frequency range */
94962306a36Sopenharmony_ci		ee_mode = AR5K_EEPROM_MODE_11A;
95062306a36Sopenharmony_ci		obdb =	 channel->center_freq >= 5725 ? 3 :
95162306a36Sopenharmony_ci			(channel->center_freq >= 5500 ? 2 :
95262306a36Sopenharmony_ci			(channel->center_freq >= 5260 ? 1 :
95362306a36Sopenharmony_ci			 (channel->center_freq > 4000 ? 0 : -1)));
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci		if (obdb < 0)
95662306a36Sopenharmony_ci			return -EINVAL;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci		ath5k_hw_rfb_op(ah, rf_regs, ee->ee_ob[ee_mode][obdb],
95962306a36Sopenharmony_ci						AR5K_RF_OB_5GHZ, true);
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci		ath5k_hw_rfb_op(ah, rf_regs, ee->ee_db[ee_mode][obdb],
96262306a36Sopenharmony_ci						AR5K_RF_DB_5GHZ, true);
96362306a36Sopenharmony_ci	}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	g_step = &go->go_step[ah->ah_gain.g_step_idx];
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	/* Set turbo mode (N/A on RF5413) */
96862306a36Sopenharmony_ci	if ((ah->ah_bwmode == AR5K_BWMODE_40MHZ) &&
96962306a36Sopenharmony_ci	(ah->ah_radio != AR5K_RF5413))
97062306a36Sopenharmony_ci		ath5k_hw_rfb_op(ah, rf_regs, 1, AR5K_RF_TURBO, false);
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	/* Bank Modifications (chip-specific) */
97362306a36Sopenharmony_ci	if (ah->ah_radio == AR5K_RF5111) {
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci		/* Set gain_F settings according to current step */
97662306a36Sopenharmony_ci		if (channel->hw_value != AR5K_MODE_11B) {
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci			AR5K_REG_WRITE_BITS(ah, AR5K_PHY_FRAME_CTL,
97962306a36Sopenharmony_ci					AR5K_PHY_FRAME_CTL_TX_CLIP,
98062306a36Sopenharmony_ci					g_step->gos_param[0]);
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci			ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[1],
98362306a36Sopenharmony_ci							AR5K_RF_PWD_90, true);
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci			ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[2],
98662306a36Sopenharmony_ci							AR5K_RF_PWD_84, true);
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci			ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[3],
98962306a36Sopenharmony_ci						AR5K_RF_RFGAIN_SEL, true);
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci			/* We programmed gain_F parameters, switch back
99262306a36Sopenharmony_ci			 * to active state */
99362306a36Sopenharmony_ci			ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE;
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci		}
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci		/* Bank 6/7 setup */
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci		ath5k_hw_rfb_op(ah, rf_regs, !ee->ee_xpd[ee_mode],
100062306a36Sopenharmony_ci						AR5K_RF_PWD_XPD, true);
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci		ath5k_hw_rfb_op(ah, rf_regs, ee->ee_x_gain[ee_mode],
100362306a36Sopenharmony_ci						AR5K_RF_XPD_GAIN, true);
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci		ath5k_hw_rfb_op(ah, rf_regs, ee->ee_i_gain[ee_mode],
100662306a36Sopenharmony_ci						AR5K_RF_GAIN_I, true);
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci		ath5k_hw_rfb_op(ah, rf_regs, ee->ee_xpd[ee_mode],
100962306a36Sopenharmony_ci						AR5K_RF_PLO_SEL, true);
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci		/* Tweak power detectors for half/quarter rate support */
101262306a36Sopenharmony_ci		if (ah->ah_bwmode == AR5K_BWMODE_5MHZ ||
101362306a36Sopenharmony_ci		ah->ah_bwmode == AR5K_BWMODE_10MHZ) {
101462306a36Sopenharmony_ci			u8 wait_i;
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci			ath5k_hw_rfb_op(ah, rf_regs, 0x1f,
101762306a36Sopenharmony_ci						AR5K_RF_WAIT_S, true);
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci			wait_i = (ah->ah_bwmode == AR5K_BWMODE_5MHZ) ?
102062306a36Sopenharmony_ci							0x1f : 0x10;
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci			ath5k_hw_rfb_op(ah, rf_regs, wait_i,
102362306a36Sopenharmony_ci						AR5K_RF_WAIT_I, true);
102462306a36Sopenharmony_ci			ath5k_hw_rfb_op(ah, rf_regs, 3,
102562306a36Sopenharmony_ci						AR5K_RF_MAX_TIME, true);
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci		}
102862306a36Sopenharmony_ci	}
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	if (ah->ah_radio == AR5K_RF5112) {
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci		/* Set gain_F settings according to current step */
103362306a36Sopenharmony_ci		if (channel->hw_value != AR5K_MODE_11B) {
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci			ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[0],
103662306a36Sopenharmony_ci						AR5K_RF_MIXGAIN_OVR, true);
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci			ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[1],
103962306a36Sopenharmony_ci						AR5K_RF_PWD_138, true);
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci			ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[2],
104262306a36Sopenharmony_ci						AR5K_RF_PWD_137, true);
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci			ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[3],
104562306a36Sopenharmony_ci						AR5K_RF_PWD_136, true);
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci			ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[4],
104862306a36Sopenharmony_ci						AR5K_RF_PWD_132, true);
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci			ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[5],
105162306a36Sopenharmony_ci						AR5K_RF_PWD_131, true);
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci			ath5k_hw_rfb_op(ah, rf_regs, g_step->gos_param[6],
105462306a36Sopenharmony_ci						AR5K_RF_PWD_130, true);
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci			/* We programmed gain_F parameters, switch back
105762306a36Sopenharmony_ci			 * to active state */
105862306a36Sopenharmony_ci			ah->ah_gain.g_state = AR5K_RFGAIN_ACTIVE;
105962306a36Sopenharmony_ci		}
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci		/* Bank 6/7 setup */
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci		ath5k_hw_rfb_op(ah, rf_regs, ee->ee_xpd[ee_mode],
106462306a36Sopenharmony_ci						AR5K_RF_XPD_SEL, true);
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci		if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_5112A) {
106762306a36Sopenharmony_ci			/* Rev. 1 supports only one xpd */
106862306a36Sopenharmony_ci			ath5k_hw_rfb_op(ah, rf_regs,
106962306a36Sopenharmony_ci						ee->ee_x_gain[ee_mode],
107062306a36Sopenharmony_ci						AR5K_RF_XPD_GAIN, true);
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci		} else {
107362306a36Sopenharmony_ci			u8 *pdg_curve_to_idx = ee->ee_pdc_to_idx[ee_mode];
107462306a36Sopenharmony_ci			if (ee->ee_pd_gains[ee_mode] > 1) {
107562306a36Sopenharmony_ci				ath5k_hw_rfb_op(ah, rf_regs,
107662306a36Sopenharmony_ci						pdg_curve_to_idx[0],
107762306a36Sopenharmony_ci						AR5K_RF_PD_GAIN_LO, true);
107862306a36Sopenharmony_ci				ath5k_hw_rfb_op(ah, rf_regs,
107962306a36Sopenharmony_ci						pdg_curve_to_idx[1],
108062306a36Sopenharmony_ci						AR5K_RF_PD_GAIN_HI, true);
108162306a36Sopenharmony_ci			} else {
108262306a36Sopenharmony_ci				ath5k_hw_rfb_op(ah, rf_regs,
108362306a36Sopenharmony_ci						pdg_curve_to_idx[0],
108462306a36Sopenharmony_ci						AR5K_RF_PD_GAIN_LO, true);
108562306a36Sopenharmony_ci				ath5k_hw_rfb_op(ah, rf_regs,
108662306a36Sopenharmony_ci						pdg_curve_to_idx[0],
108762306a36Sopenharmony_ci						AR5K_RF_PD_GAIN_HI, true);
108862306a36Sopenharmony_ci			}
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci			/* Lower synth voltage on Rev 2 */
109162306a36Sopenharmony_ci			if (ah->ah_radio == AR5K_RF5112 &&
109262306a36Sopenharmony_ci			    (ah->ah_radio_5ghz_revision & AR5K_SREV_REV) > 0) {
109362306a36Sopenharmony_ci				ath5k_hw_rfb_op(ah, rf_regs, 2,
109462306a36Sopenharmony_ci						AR5K_RF_HIGH_VC_CP, true);
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci				ath5k_hw_rfb_op(ah, rf_regs, 2,
109762306a36Sopenharmony_ci						AR5K_RF_MID_VC_CP, true);
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci				ath5k_hw_rfb_op(ah, rf_regs, 2,
110062306a36Sopenharmony_ci						AR5K_RF_LOW_VC_CP, true);
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci				ath5k_hw_rfb_op(ah, rf_regs, 2,
110362306a36Sopenharmony_ci						AR5K_RF_PUSH_UP, true);
110462306a36Sopenharmony_ci			}
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci			/* Decrease power consumption on 5213+ BaseBand */
110762306a36Sopenharmony_ci			if (ah->ah_phy_revision >= AR5K_SREV_PHY_5212A) {
110862306a36Sopenharmony_ci				ath5k_hw_rfb_op(ah, rf_regs, 1,
110962306a36Sopenharmony_ci						AR5K_RF_PAD2GND, true);
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci				ath5k_hw_rfb_op(ah, rf_regs, 1,
111262306a36Sopenharmony_ci						AR5K_RF_XB2_LVL, true);
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci				ath5k_hw_rfb_op(ah, rf_regs, 1,
111562306a36Sopenharmony_ci						AR5K_RF_XB5_LVL, true);
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci				ath5k_hw_rfb_op(ah, rf_regs, 1,
111862306a36Sopenharmony_ci						AR5K_RF_PWD_167, true);
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci				ath5k_hw_rfb_op(ah, rf_regs, 1,
112162306a36Sopenharmony_ci						AR5K_RF_PWD_166, true);
112262306a36Sopenharmony_ci			}
112362306a36Sopenharmony_ci		}
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci		ath5k_hw_rfb_op(ah, rf_regs, ee->ee_i_gain[ee_mode],
112662306a36Sopenharmony_ci						AR5K_RF_GAIN_I, true);
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci		/* Tweak power detector for half/quarter rates */
112962306a36Sopenharmony_ci		if (ah->ah_bwmode == AR5K_BWMODE_5MHZ ||
113062306a36Sopenharmony_ci		ah->ah_bwmode == AR5K_BWMODE_10MHZ) {
113162306a36Sopenharmony_ci			u8 pd_delay;
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci			pd_delay = (ah->ah_bwmode == AR5K_BWMODE_5MHZ) ?
113462306a36Sopenharmony_ci							0xf : 0x8;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci			ath5k_hw_rfb_op(ah, rf_regs, pd_delay,
113762306a36Sopenharmony_ci						AR5K_RF_PD_PERIOD_A, true);
113862306a36Sopenharmony_ci			ath5k_hw_rfb_op(ah, rf_regs, 0xf,
113962306a36Sopenharmony_ci						AR5K_RF_PD_DELAY_A, true);
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci		}
114262306a36Sopenharmony_ci	}
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	if (ah->ah_radio == AR5K_RF5413 &&
114562306a36Sopenharmony_ci	channel->band == NL80211_BAND_2GHZ) {
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci		ath5k_hw_rfb_op(ah, rf_regs, 1, AR5K_RF_DERBY_CHAN_SEL_MODE,
114862306a36Sopenharmony_ci									true);
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci		/* Set optimum value for early revisions (on pci-e chips) */
115162306a36Sopenharmony_ci		if (ah->ah_mac_srev >= AR5K_SREV_AR5424 &&
115262306a36Sopenharmony_ci		ah->ah_mac_srev < AR5K_SREV_AR5413)
115362306a36Sopenharmony_ci			ath5k_hw_rfb_op(ah, rf_regs, ath5k_hw_bitswap(6, 3),
115462306a36Sopenharmony_ci						AR5K_RF_PWD_ICLOBUF_2G, true);
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	}
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	/* Write RF banks on hw */
115962306a36Sopenharmony_ci	for (i = 0; i < ah->ah_rf_banks_size; i++) {
116062306a36Sopenharmony_ci		AR5K_REG_WAIT(i);
116162306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, rfb[i], ini_rfb[i].rfb_ctrl_register);
116262306a36Sopenharmony_ci	}
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	return 0;
116562306a36Sopenharmony_ci}
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci/**************************\
116962306a36Sopenharmony_ci  PHY/RF channel functions
117062306a36Sopenharmony_ci\**************************/
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci/**
117362306a36Sopenharmony_ci * ath5k_hw_rf5110_chan2athchan() - Convert channel freq on RF5110
117462306a36Sopenharmony_ci * @channel: The &struct ieee80211_channel
117562306a36Sopenharmony_ci *
117662306a36Sopenharmony_ci * Map channel frequency to IEEE channel number and convert it
117762306a36Sopenharmony_ci * to an internal channel value used by the RF5110 chipset.
117862306a36Sopenharmony_ci */
117962306a36Sopenharmony_cistatic u32
118062306a36Sopenharmony_ciath5k_hw_rf5110_chan2athchan(struct ieee80211_channel *channel)
118162306a36Sopenharmony_ci{
118262306a36Sopenharmony_ci	u32 athchan;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	athchan = (ath5k_hw_bitswap(
118562306a36Sopenharmony_ci			(ieee80211_frequency_to_channel(
118662306a36Sopenharmony_ci				channel->center_freq) - 24) / 2, 5)
118762306a36Sopenharmony_ci				<< 1) | (1 << 6) | 0x1;
118862306a36Sopenharmony_ci	return athchan;
118962306a36Sopenharmony_ci}
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci/**
119262306a36Sopenharmony_ci * ath5k_hw_rf5110_channel() - Set channel frequency on RF5110
119362306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
119462306a36Sopenharmony_ci * @channel: The &struct ieee80211_channel
119562306a36Sopenharmony_ci */
119662306a36Sopenharmony_cistatic int
119762306a36Sopenharmony_ciath5k_hw_rf5110_channel(struct ath5k_hw *ah,
119862306a36Sopenharmony_ci		struct ieee80211_channel *channel)
119962306a36Sopenharmony_ci{
120062306a36Sopenharmony_ci	u32 data;
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	/*
120362306a36Sopenharmony_ci	 * Set the channel and wait
120462306a36Sopenharmony_ci	 */
120562306a36Sopenharmony_ci	data = ath5k_hw_rf5110_chan2athchan(channel);
120662306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, data, AR5K_RF_BUFFER);
120762306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, 0, AR5K_RF_BUFFER_CONTROL_0);
120862306a36Sopenharmony_ci	usleep_range(1000, 1500);
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	return 0;
121162306a36Sopenharmony_ci}
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci/**
121462306a36Sopenharmony_ci * ath5k_hw_rf5111_chan2athchan() - Handle 2GHz channels on RF5111/2111
121562306a36Sopenharmony_ci * @ieee: IEEE channel number
121662306a36Sopenharmony_ci * @athchan: The &struct ath5k_athchan_2ghz
121762306a36Sopenharmony_ci *
121862306a36Sopenharmony_ci * In order to enable the RF2111 frequency converter on RF5111/2111 setups
121962306a36Sopenharmony_ci * we need to add some offsets and extra flags to the data values we pass
122062306a36Sopenharmony_ci * on to the PHY. So for every 2GHz channel this function gets called
122162306a36Sopenharmony_ci * to do the conversion.
122262306a36Sopenharmony_ci */
122362306a36Sopenharmony_cistatic int
122462306a36Sopenharmony_ciath5k_hw_rf5111_chan2athchan(unsigned int ieee,
122562306a36Sopenharmony_ci		struct ath5k_athchan_2ghz *athchan)
122662306a36Sopenharmony_ci{
122762306a36Sopenharmony_ci	int channel;
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	/* Cast this value to catch negative channel numbers (>= -19) */
123062306a36Sopenharmony_ci	channel = (int)ieee;
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	/*
123362306a36Sopenharmony_ci	 * Map 2GHz IEEE channel to 5GHz Atheros channel
123462306a36Sopenharmony_ci	 */
123562306a36Sopenharmony_ci	if (channel <= 13) {
123662306a36Sopenharmony_ci		athchan->a2_athchan = 115 + channel;
123762306a36Sopenharmony_ci		athchan->a2_flags = 0x46;
123862306a36Sopenharmony_ci	} else if (channel == 14) {
123962306a36Sopenharmony_ci		athchan->a2_athchan = 124;
124062306a36Sopenharmony_ci		athchan->a2_flags = 0x44;
124162306a36Sopenharmony_ci	} else if (channel >= 15 && channel <= 26) {
124262306a36Sopenharmony_ci		athchan->a2_athchan = ((channel - 14) * 4) + 132;
124362306a36Sopenharmony_ci		athchan->a2_flags = 0x46;
124462306a36Sopenharmony_ci	} else
124562306a36Sopenharmony_ci		return -EINVAL;
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	return 0;
124862306a36Sopenharmony_ci}
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci/**
125162306a36Sopenharmony_ci * ath5k_hw_rf5111_channel() - Set channel frequency on RF5111/2111
125262306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
125362306a36Sopenharmony_ci * @channel: The &struct ieee80211_channel
125462306a36Sopenharmony_ci */
125562306a36Sopenharmony_cistatic int
125662306a36Sopenharmony_ciath5k_hw_rf5111_channel(struct ath5k_hw *ah,
125762306a36Sopenharmony_ci		struct ieee80211_channel *channel)
125862306a36Sopenharmony_ci{
125962306a36Sopenharmony_ci	struct ath5k_athchan_2ghz ath5k_channel_2ghz;
126062306a36Sopenharmony_ci	unsigned int ath5k_channel =
126162306a36Sopenharmony_ci		ieee80211_frequency_to_channel(channel->center_freq);
126262306a36Sopenharmony_ci	u32 data0, data1, clock;
126362306a36Sopenharmony_ci	int ret;
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	/*
126662306a36Sopenharmony_ci	 * Set the channel on the RF5111 radio
126762306a36Sopenharmony_ci	 */
126862306a36Sopenharmony_ci	data0 = data1 = 0;
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	if (channel->band == NL80211_BAND_2GHZ) {
127162306a36Sopenharmony_ci		/* Map 2GHz channel to 5GHz Atheros channel ID */
127262306a36Sopenharmony_ci		ret = ath5k_hw_rf5111_chan2athchan(
127362306a36Sopenharmony_ci			ieee80211_frequency_to_channel(channel->center_freq),
127462306a36Sopenharmony_ci			&ath5k_channel_2ghz);
127562306a36Sopenharmony_ci		if (ret)
127662306a36Sopenharmony_ci			return ret;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci		ath5k_channel = ath5k_channel_2ghz.a2_athchan;
127962306a36Sopenharmony_ci		data0 = ((ath5k_hw_bitswap(ath5k_channel_2ghz.a2_flags, 8) & 0xff)
128062306a36Sopenharmony_ci		    << 5) | (1 << 4);
128162306a36Sopenharmony_ci	}
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	if (ath5k_channel < 145 || !(ath5k_channel & 1)) {
128462306a36Sopenharmony_ci		clock = 1;
128562306a36Sopenharmony_ci		data1 = ((ath5k_hw_bitswap(ath5k_channel - 24, 8) & 0xff) << 2) |
128662306a36Sopenharmony_ci			(clock << 1) | (1 << 10) | 1;
128762306a36Sopenharmony_ci	} else {
128862306a36Sopenharmony_ci		clock = 0;
128962306a36Sopenharmony_ci		data1 = ((ath5k_hw_bitswap((ath5k_channel - 24) / 2, 8) & 0xff)
129062306a36Sopenharmony_ci			<< 2) | (clock << 1) | (1 << 10) | 1;
129162306a36Sopenharmony_ci	}
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, (data1 & 0xff) | ((data0 & 0xff) << 8),
129462306a36Sopenharmony_ci			AR5K_RF_BUFFER);
129562306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, ((data1 >> 8) & 0xff) | (data0 & 0xff00),
129662306a36Sopenharmony_ci			AR5K_RF_BUFFER_CONTROL_3);
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	return 0;
129962306a36Sopenharmony_ci}
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci/**
130262306a36Sopenharmony_ci * ath5k_hw_rf5112_channel() - Set channel frequency on 5112 and newer
130362306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
130462306a36Sopenharmony_ci * @channel: The &struct ieee80211_channel
130562306a36Sopenharmony_ci *
130662306a36Sopenharmony_ci * On RF5112/2112 and newer we don't need to do any conversion.
130762306a36Sopenharmony_ci * We pass the frequency value after a few modifications to the
130862306a36Sopenharmony_ci * chip directly.
130962306a36Sopenharmony_ci *
131062306a36Sopenharmony_ci * NOTE: Make sure channel frequency given is within our range or else
131162306a36Sopenharmony_ci * we might damage the chip ! Use ath5k_channel_ok before calling this one.
131262306a36Sopenharmony_ci */
131362306a36Sopenharmony_cistatic int
131462306a36Sopenharmony_ciath5k_hw_rf5112_channel(struct ath5k_hw *ah,
131562306a36Sopenharmony_ci		struct ieee80211_channel *channel)
131662306a36Sopenharmony_ci{
131762306a36Sopenharmony_ci	u32 data, data0, data1, data2;
131862306a36Sopenharmony_ci	u16 c;
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	data = data0 = data1 = data2 = 0;
132162306a36Sopenharmony_ci	c = channel->center_freq;
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	/* My guess based on code:
132462306a36Sopenharmony_ci	 * 2GHz RF has 2 synth modes, one with a Local Oscillator
132562306a36Sopenharmony_ci	 * at 2224Hz and one with a LO at 2192Hz. IF is 1520Hz
132662306a36Sopenharmony_ci	 * (3040/2). data0 is used to set the PLL divider and data1
132762306a36Sopenharmony_ci	 * selects synth mode. */
132862306a36Sopenharmony_ci	if (c < 4800) {
132962306a36Sopenharmony_ci		/* Channel 14 and all frequencies with 2Hz spacing
133062306a36Sopenharmony_ci		 * below/above (non-standard channels) */
133162306a36Sopenharmony_ci		if (!((c - 2224) % 5)) {
133262306a36Sopenharmony_ci			/* Same as (c - 2224) / 5 */
133362306a36Sopenharmony_ci			data0 = ((2 * (c - 704)) - 3040) / 10;
133462306a36Sopenharmony_ci			data1 = 1;
133562306a36Sopenharmony_ci		/* Channel 1 and all frequencies with 5Hz spacing
133662306a36Sopenharmony_ci		 * below/above (standard channels without channel 14) */
133762306a36Sopenharmony_ci		} else if (!((c - 2192) % 5)) {
133862306a36Sopenharmony_ci			/* Same as (c - 2192) / 5 */
133962306a36Sopenharmony_ci			data0 = ((2 * (c - 672)) - 3040) / 10;
134062306a36Sopenharmony_ci			data1 = 0;
134162306a36Sopenharmony_ci		} else
134262306a36Sopenharmony_ci			return -EINVAL;
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci		data0 = ath5k_hw_bitswap((data0 << 2) & 0xff, 8);
134562306a36Sopenharmony_ci	/* This is more complex, we have a single synthesizer with
134662306a36Sopenharmony_ci	 * 4 reference clock settings (?) based on frequency spacing
134762306a36Sopenharmony_ci	 * and set using data2. LO is at 4800Hz and data0 is again used
134862306a36Sopenharmony_ci	 * to set some divider.
134962306a36Sopenharmony_ci	 *
135062306a36Sopenharmony_ci	 * NOTE: There is an old atheros presentation at Stanford
135162306a36Sopenharmony_ci	 * that mentions a method called dual direct conversion
135262306a36Sopenharmony_ci	 * with 1GHz sliding IF for RF5110. Maybe that's what we
135362306a36Sopenharmony_ci	 * have here, or an updated version. */
135462306a36Sopenharmony_ci	} else if ((c % 5) != 2 || c > 5435) {
135562306a36Sopenharmony_ci		if (!(c % 20) && c >= 5120) {
135662306a36Sopenharmony_ci			data0 = ath5k_hw_bitswap(((c - 4800) / 20 << 2), 8);
135762306a36Sopenharmony_ci			data2 = ath5k_hw_bitswap(3, 2);
135862306a36Sopenharmony_ci		} else if (!(c % 10)) {
135962306a36Sopenharmony_ci			data0 = ath5k_hw_bitswap(((c - 4800) / 10 << 1), 8);
136062306a36Sopenharmony_ci			data2 = ath5k_hw_bitswap(2, 2);
136162306a36Sopenharmony_ci		} else if (!(c % 5)) {
136262306a36Sopenharmony_ci			data0 = ath5k_hw_bitswap((c - 4800) / 5, 8);
136362306a36Sopenharmony_ci			data2 = ath5k_hw_bitswap(1, 2);
136462306a36Sopenharmony_ci		} else
136562306a36Sopenharmony_ci			return -EINVAL;
136662306a36Sopenharmony_ci	} else {
136762306a36Sopenharmony_ci		data0 = ath5k_hw_bitswap((10 * (c - 2 - 4800)) / 25 + 1, 8);
136862306a36Sopenharmony_ci		data2 = ath5k_hw_bitswap(0, 2);
136962306a36Sopenharmony_ci	}
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci	data = (data0 << 4) | (data1 << 1) | (data2 << 2) | 0x1001;
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, data & 0xff, AR5K_RF_BUFFER);
137462306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, (data >> 8) & 0x7f, AR5K_RF_BUFFER_CONTROL_5);
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	return 0;
137762306a36Sopenharmony_ci}
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci/**
138062306a36Sopenharmony_ci * ath5k_hw_rf2425_channel() - Set channel frequency on RF2425
138162306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
138262306a36Sopenharmony_ci * @channel: The &struct ieee80211_channel
138362306a36Sopenharmony_ci *
138462306a36Sopenharmony_ci * AR2425/2417 have a different 2GHz RF so code changes
138562306a36Sopenharmony_ci * a little bit from RF5112.
138662306a36Sopenharmony_ci */
138762306a36Sopenharmony_cistatic int
138862306a36Sopenharmony_ciath5k_hw_rf2425_channel(struct ath5k_hw *ah,
138962306a36Sopenharmony_ci		struct ieee80211_channel *channel)
139062306a36Sopenharmony_ci{
139162306a36Sopenharmony_ci	u32 data, data0, data2;
139262306a36Sopenharmony_ci	u16 c;
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	data = data0 = data2 = 0;
139562306a36Sopenharmony_ci	c = channel->center_freq;
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	if (c < 4800) {
139862306a36Sopenharmony_ci		data0 = ath5k_hw_bitswap((c - 2272), 8);
139962306a36Sopenharmony_ci		data2 = 0;
140062306a36Sopenharmony_ci	/* ? 5GHz ? */
140162306a36Sopenharmony_ci	} else if ((c % 5) != 2 || c > 5435) {
140262306a36Sopenharmony_ci		if (!(c % 20) && c < 5120)
140362306a36Sopenharmony_ci			data0 = ath5k_hw_bitswap(((c - 4800) / 20 << 2), 8);
140462306a36Sopenharmony_ci		else if (!(c % 10))
140562306a36Sopenharmony_ci			data0 = ath5k_hw_bitswap(((c - 4800) / 10 << 1), 8);
140662306a36Sopenharmony_ci		else if (!(c % 5))
140762306a36Sopenharmony_ci			data0 = ath5k_hw_bitswap((c - 4800) / 5, 8);
140862306a36Sopenharmony_ci		else
140962306a36Sopenharmony_ci			return -EINVAL;
141062306a36Sopenharmony_ci		data2 = ath5k_hw_bitswap(1, 2);
141162306a36Sopenharmony_ci	} else {
141262306a36Sopenharmony_ci		data0 = ath5k_hw_bitswap((10 * (c - 2 - 4800)) / 25 + 1, 8);
141362306a36Sopenharmony_ci		data2 = ath5k_hw_bitswap(0, 2);
141462306a36Sopenharmony_ci	}
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	data = (data0 << 4) | data2 << 2 | 0x1001;
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, data & 0xff, AR5K_RF_BUFFER);
141962306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, (data >> 8) & 0x7f, AR5K_RF_BUFFER_CONTROL_5);
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	return 0;
142262306a36Sopenharmony_ci}
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci/**
142562306a36Sopenharmony_ci * ath5k_hw_channel() - Set a channel on the radio chip
142662306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
142762306a36Sopenharmony_ci * @channel: The &struct ieee80211_channel
142862306a36Sopenharmony_ci *
142962306a36Sopenharmony_ci * This is the main function called to set a channel on the
143062306a36Sopenharmony_ci * radio chip based on the radio chip version.
143162306a36Sopenharmony_ci */
143262306a36Sopenharmony_cistatic int
143362306a36Sopenharmony_ciath5k_hw_channel(struct ath5k_hw *ah,
143462306a36Sopenharmony_ci		struct ieee80211_channel *channel)
143562306a36Sopenharmony_ci{
143662306a36Sopenharmony_ci	int ret;
143762306a36Sopenharmony_ci	/*
143862306a36Sopenharmony_ci	 * Check bounds supported by the PHY (we don't care about regulatory
143962306a36Sopenharmony_ci	 * restrictions at this point).
144062306a36Sopenharmony_ci	 */
144162306a36Sopenharmony_ci	if (!ath5k_channel_ok(ah, channel)) {
144262306a36Sopenharmony_ci		ATH5K_ERR(ah,
144362306a36Sopenharmony_ci			"channel frequency (%u MHz) out of supported "
144462306a36Sopenharmony_ci			"band range\n",
144562306a36Sopenharmony_ci			channel->center_freq);
144662306a36Sopenharmony_ci		return -EINVAL;
144762306a36Sopenharmony_ci	}
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	/*
145062306a36Sopenharmony_ci	 * Set the channel and wait
145162306a36Sopenharmony_ci	 */
145262306a36Sopenharmony_ci	switch (ah->ah_radio) {
145362306a36Sopenharmony_ci	case AR5K_RF5110:
145462306a36Sopenharmony_ci		ret = ath5k_hw_rf5110_channel(ah, channel);
145562306a36Sopenharmony_ci		break;
145662306a36Sopenharmony_ci	case AR5K_RF5111:
145762306a36Sopenharmony_ci		ret = ath5k_hw_rf5111_channel(ah, channel);
145862306a36Sopenharmony_ci		break;
145962306a36Sopenharmony_ci	case AR5K_RF2317:
146062306a36Sopenharmony_ci	case AR5K_RF2425:
146162306a36Sopenharmony_ci		ret = ath5k_hw_rf2425_channel(ah, channel);
146262306a36Sopenharmony_ci		break;
146362306a36Sopenharmony_ci	default:
146462306a36Sopenharmony_ci		ret = ath5k_hw_rf5112_channel(ah, channel);
146562306a36Sopenharmony_ci		break;
146662306a36Sopenharmony_ci	}
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	if (ret)
146962306a36Sopenharmony_ci		return ret;
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	/* Set JAPAN setting for channel 14 */
147262306a36Sopenharmony_ci	if (channel->center_freq == 2484) {
147362306a36Sopenharmony_ci		AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_CCKTXCTL,
147462306a36Sopenharmony_ci				AR5K_PHY_CCKTXCTL_JAPAN);
147562306a36Sopenharmony_ci	} else {
147662306a36Sopenharmony_ci		AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_CCKTXCTL,
147762306a36Sopenharmony_ci				AR5K_PHY_CCKTXCTL_WORLD);
147862306a36Sopenharmony_ci	}
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	ah->ah_current_channel = channel;
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci	return 0;
148362306a36Sopenharmony_ci}
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci/*****************\
148762306a36Sopenharmony_ci  PHY calibration
148862306a36Sopenharmony_ci\*****************/
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci/**
149162306a36Sopenharmony_ci * DOC: PHY Calibration routines
149262306a36Sopenharmony_ci *
149362306a36Sopenharmony_ci * Noise floor calibration: When we tell the hardware to
149462306a36Sopenharmony_ci * perform a noise floor calibration by setting the
149562306a36Sopenharmony_ci * AR5K_PHY_AGCCTL_NF bit on AR5K_PHY_AGCCTL, it will periodically
149662306a36Sopenharmony_ci * sample-and-hold the minimum noise level seen at the antennas.
149762306a36Sopenharmony_ci * This value is then stored in a ring buffer of recently measured
149862306a36Sopenharmony_ci * noise floor values so we have a moving window of the last few
149962306a36Sopenharmony_ci * samples. The median of the values in the history is then loaded
150062306a36Sopenharmony_ci * into the hardware for its own use for RSSI and CCA measurements.
150162306a36Sopenharmony_ci * This type of calibration doesn't interfere with traffic.
150262306a36Sopenharmony_ci *
150362306a36Sopenharmony_ci * AGC calibration: When we tell the hardware to perform
150462306a36Sopenharmony_ci * an AGC (Automatic Gain Control) calibration by setting the
150562306a36Sopenharmony_ci * AR5K_PHY_AGCCTL_CAL, hw disconnects the antennas and does
150662306a36Sopenharmony_ci * a calibration on the DC offsets of ADCs. During this period
150762306a36Sopenharmony_ci * rx/tx gets disabled so we have to deal with it on the driver
150862306a36Sopenharmony_ci * part.
150962306a36Sopenharmony_ci *
151062306a36Sopenharmony_ci * I/Q calibration: When we tell the hardware to perform
151162306a36Sopenharmony_ci * an I/Q calibration, it tries to correct I/Q imbalance and
151262306a36Sopenharmony_ci * fix QAM constellation by sampling data from rxed frames.
151362306a36Sopenharmony_ci * It doesn't interfere with traffic.
151462306a36Sopenharmony_ci *
151562306a36Sopenharmony_ci * For more infos on AGC and I/Q calibration check out patent doc
151662306a36Sopenharmony_ci * #03/094463.
151762306a36Sopenharmony_ci */
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci/**
152062306a36Sopenharmony_ci * ath5k_hw_read_measured_noise_floor() - Read measured NF from hw
152162306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
152262306a36Sopenharmony_ci */
152362306a36Sopenharmony_cistatic s32
152462306a36Sopenharmony_ciath5k_hw_read_measured_noise_floor(struct ath5k_hw *ah)
152562306a36Sopenharmony_ci{
152662306a36Sopenharmony_ci	s32 val;
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci	val = ath5k_hw_reg_read(ah, AR5K_PHY_NF);
152962306a36Sopenharmony_ci	return sign_extend32(AR5K_REG_MS(val, AR5K_PHY_NF_MINCCA_PWR), 8);
153062306a36Sopenharmony_ci}
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci/**
153362306a36Sopenharmony_ci * ath5k_hw_init_nfcal_hist() - Initialize NF calibration history buffer
153462306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
153562306a36Sopenharmony_ci */
153662306a36Sopenharmony_civoid
153762306a36Sopenharmony_ciath5k_hw_init_nfcal_hist(struct ath5k_hw *ah)
153862306a36Sopenharmony_ci{
153962306a36Sopenharmony_ci	int i;
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	ah->ah_nfcal_hist.index = 0;
154262306a36Sopenharmony_ci	for (i = 0; i < ATH5K_NF_CAL_HIST_MAX; i++)
154362306a36Sopenharmony_ci		ah->ah_nfcal_hist.nfval[i] = AR5K_TUNE_CCA_MAX_GOOD_VALUE;
154462306a36Sopenharmony_ci}
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci/**
154762306a36Sopenharmony_ci * ath5k_hw_update_nfcal_hist() - Update NF calibration history buffer
154862306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
154962306a36Sopenharmony_ci * @noise_floor: The NF we got from hw
155062306a36Sopenharmony_ci */
155162306a36Sopenharmony_cistatic void ath5k_hw_update_nfcal_hist(struct ath5k_hw *ah, s16 noise_floor)
155262306a36Sopenharmony_ci{
155362306a36Sopenharmony_ci	struct ath5k_nfcal_hist *hist = &ah->ah_nfcal_hist;
155462306a36Sopenharmony_ci	hist->index = (hist->index + 1) & (ATH5K_NF_CAL_HIST_MAX - 1);
155562306a36Sopenharmony_ci	hist->nfval[hist->index] = noise_floor;
155662306a36Sopenharmony_ci}
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_cistatic int cmps16(const void *a, const void *b)
155962306a36Sopenharmony_ci{
156062306a36Sopenharmony_ci	return *(s16 *)a - *(s16 *)b;
156162306a36Sopenharmony_ci}
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci/**
156462306a36Sopenharmony_ci * ath5k_hw_get_median_noise_floor() - Get median NF from history buffer
156562306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
156662306a36Sopenharmony_ci */
156762306a36Sopenharmony_cistatic s16
156862306a36Sopenharmony_ciath5k_hw_get_median_noise_floor(struct ath5k_hw *ah)
156962306a36Sopenharmony_ci{
157062306a36Sopenharmony_ci	s16 sorted_nfval[ATH5K_NF_CAL_HIST_MAX];
157162306a36Sopenharmony_ci	int i;
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci	memcpy(sorted_nfval, ah->ah_nfcal_hist.nfval, sizeof(sorted_nfval));
157462306a36Sopenharmony_ci	sort(sorted_nfval, ATH5K_NF_CAL_HIST_MAX, sizeof(s16), cmps16, NULL);
157562306a36Sopenharmony_ci	for (i = 0; i < ATH5K_NF_CAL_HIST_MAX; i++) {
157662306a36Sopenharmony_ci		ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE,
157762306a36Sopenharmony_ci			"cal %d:%d\n", i, sorted_nfval[i]);
157862306a36Sopenharmony_ci	}
157962306a36Sopenharmony_ci	return sorted_nfval[(ATH5K_NF_CAL_HIST_MAX - 1) / 2];
158062306a36Sopenharmony_ci}
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci/**
158362306a36Sopenharmony_ci * ath5k_hw_update_noise_floor() - Update NF on hardware
158462306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
158562306a36Sopenharmony_ci *
158662306a36Sopenharmony_ci * This is the main function we call to perform a NF calibration,
158762306a36Sopenharmony_ci * it reads NF from hardware, calculates the median and updates
158862306a36Sopenharmony_ci * NF on hw.
158962306a36Sopenharmony_ci */
159062306a36Sopenharmony_civoid
159162306a36Sopenharmony_ciath5k_hw_update_noise_floor(struct ath5k_hw *ah)
159262306a36Sopenharmony_ci{
159362306a36Sopenharmony_ci	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
159462306a36Sopenharmony_ci	u32 val;
159562306a36Sopenharmony_ci	s16 nf, threshold;
159662306a36Sopenharmony_ci	u8 ee_mode;
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci	/* keep last value if calibration hasn't completed */
159962306a36Sopenharmony_ci	if (ath5k_hw_reg_read(ah, AR5K_PHY_AGCCTL) & AR5K_PHY_AGCCTL_NF) {
160062306a36Sopenharmony_ci		ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE,
160162306a36Sopenharmony_ci			"NF did not complete in calibration window\n");
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ci		return;
160462306a36Sopenharmony_ci	}
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci	ah->ah_cal_mask |= AR5K_CALIBRATION_NF;
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci	ee_mode = ath5k_eeprom_mode_from_channel(ah, ah->ah_current_channel);
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci	/* completed NF calibration, test threshold */
161162306a36Sopenharmony_ci	nf = ath5k_hw_read_measured_noise_floor(ah);
161262306a36Sopenharmony_ci	threshold = ee->ee_noise_floor_thr[ee_mode];
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	if (nf > threshold) {
161562306a36Sopenharmony_ci		ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE,
161662306a36Sopenharmony_ci			"noise floor failure detected; "
161762306a36Sopenharmony_ci			"read %d, threshold %d\n",
161862306a36Sopenharmony_ci			nf, threshold);
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci		nf = AR5K_TUNE_CCA_MAX_GOOD_VALUE;
162162306a36Sopenharmony_ci	}
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci	ath5k_hw_update_nfcal_hist(ah, nf);
162462306a36Sopenharmony_ci	nf = ath5k_hw_get_median_noise_floor(ah);
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci	/* load noise floor (in .5 dBm) so the hardware will use it */
162762306a36Sopenharmony_ci	val = ath5k_hw_reg_read(ah, AR5K_PHY_NF) & ~AR5K_PHY_NF_M;
162862306a36Sopenharmony_ci	val |= (nf * 2) & AR5K_PHY_NF_M;
162962306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, val, AR5K_PHY_NF);
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci	AR5K_REG_MASKED_BITS(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_NF,
163262306a36Sopenharmony_ci		~(AR5K_PHY_AGCCTL_NF_EN | AR5K_PHY_AGCCTL_NF_NOUPDATE));
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_NF,
163562306a36Sopenharmony_ci		0, false);
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci	/*
163862306a36Sopenharmony_ci	 * Load a high max CCA Power value (-50 dBm in .5 dBm units)
163962306a36Sopenharmony_ci	 * so that we're not capped by the median we just loaded.
164062306a36Sopenharmony_ci	 * This will be used as the initial value for the next noise
164162306a36Sopenharmony_ci	 * floor calibration.
164262306a36Sopenharmony_ci	 */
164362306a36Sopenharmony_ci	val = (val & ~AR5K_PHY_NF_M) | ((-50 * 2) & AR5K_PHY_NF_M);
164462306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, val, AR5K_PHY_NF);
164562306a36Sopenharmony_ci	AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL,
164662306a36Sopenharmony_ci		AR5K_PHY_AGCCTL_NF_EN |
164762306a36Sopenharmony_ci		AR5K_PHY_AGCCTL_NF_NOUPDATE |
164862306a36Sopenharmony_ci		AR5K_PHY_AGCCTL_NF);
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	ah->ah_noise_floor = nf;
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci	ah->ah_cal_mask &= ~AR5K_CALIBRATION_NF;
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci	ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE,
165562306a36Sopenharmony_ci		"noise floor calibrated: %d\n", nf);
165662306a36Sopenharmony_ci}
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci/**
165962306a36Sopenharmony_ci * ath5k_hw_rf5110_calibrate() - Perform a PHY calibration on RF5110
166062306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
166162306a36Sopenharmony_ci * @channel: The &struct ieee80211_channel
166262306a36Sopenharmony_ci *
166362306a36Sopenharmony_ci * Do a complete PHY calibration (AGC + NF + I/Q) on RF5110
166462306a36Sopenharmony_ci */
166562306a36Sopenharmony_cistatic int
166662306a36Sopenharmony_ciath5k_hw_rf5110_calibrate(struct ath5k_hw *ah,
166762306a36Sopenharmony_ci		struct ieee80211_channel *channel)
166862306a36Sopenharmony_ci{
166962306a36Sopenharmony_ci	u32 phy_sig, phy_agc, phy_sat, beacon;
167062306a36Sopenharmony_ci	int ret;
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	if (!(ah->ah_cal_mask & AR5K_CALIBRATION_FULL))
167362306a36Sopenharmony_ci		return 0;
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	/*
167662306a36Sopenharmony_ci	 * Disable beacons and RX/TX queues, wait
167762306a36Sopenharmony_ci	 */
167862306a36Sopenharmony_ci	AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5210,
167962306a36Sopenharmony_ci		AR5K_DIAG_SW_DIS_TX_5210 | AR5K_DIAG_SW_DIS_RX_5210);
168062306a36Sopenharmony_ci	beacon = ath5k_hw_reg_read(ah, AR5K_BEACON_5210);
168162306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, beacon & ~AR5K_BEACON_ENABLE, AR5K_BEACON_5210);
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci	usleep_range(2000, 2500);
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	/*
168662306a36Sopenharmony_ci	 * Set the channel (with AGC turned off)
168762306a36Sopenharmony_ci	 */
168862306a36Sopenharmony_ci	AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE);
168962306a36Sopenharmony_ci	udelay(10);
169062306a36Sopenharmony_ci	ret = ath5k_hw_channel(ah, channel);
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci	/*
169362306a36Sopenharmony_ci	 * Activate PHY and wait
169462306a36Sopenharmony_ci	 */
169562306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, AR5K_PHY_ACT_ENABLE, AR5K_PHY_ACT);
169662306a36Sopenharmony_ci	usleep_range(1000, 1500);
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_ci	AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE);
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci	if (ret)
170162306a36Sopenharmony_ci		return ret;
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_ci	/*
170462306a36Sopenharmony_ci	 * Calibrate the radio chip
170562306a36Sopenharmony_ci	 */
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci	/* Remember normal state */
170862306a36Sopenharmony_ci	phy_sig = ath5k_hw_reg_read(ah, AR5K_PHY_SIG);
170962306a36Sopenharmony_ci	phy_agc = ath5k_hw_reg_read(ah, AR5K_PHY_AGCCOARSE);
171062306a36Sopenharmony_ci	phy_sat = ath5k_hw_reg_read(ah, AR5K_PHY_ADCSAT);
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	/* Update radio registers */
171362306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, (phy_sig & ~(AR5K_PHY_SIG_FIRPWR)) |
171462306a36Sopenharmony_ci		AR5K_REG_SM(-1, AR5K_PHY_SIG_FIRPWR), AR5K_PHY_SIG);
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, (phy_agc & ~(AR5K_PHY_AGCCOARSE_HI |
171762306a36Sopenharmony_ci			AR5K_PHY_AGCCOARSE_LO)) |
171862306a36Sopenharmony_ci		AR5K_REG_SM(-1, AR5K_PHY_AGCCOARSE_HI) |
171962306a36Sopenharmony_ci		AR5K_REG_SM(-127, AR5K_PHY_AGCCOARSE_LO), AR5K_PHY_AGCCOARSE);
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, (phy_sat & ~(AR5K_PHY_ADCSAT_ICNT |
172262306a36Sopenharmony_ci			AR5K_PHY_ADCSAT_THR)) |
172362306a36Sopenharmony_ci		AR5K_REG_SM(2, AR5K_PHY_ADCSAT_ICNT) |
172462306a36Sopenharmony_ci		AR5K_REG_SM(12, AR5K_PHY_ADCSAT_THR), AR5K_PHY_ADCSAT);
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci	udelay(20);
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci	AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE);
172962306a36Sopenharmony_ci	udelay(10);
173062306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, AR5K_PHY_RFSTG_DISABLE, AR5K_PHY_RFSTG);
173162306a36Sopenharmony_ci	AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE);
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci	usleep_range(1000, 1500);
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci	/*
173662306a36Sopenharmony_ci	 * Enable calibration and wait until completion
173762306a36Sopenharmony_ci	 */
173862306a36Sopenharmony_ci	AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_CAL);
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci	ret = ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL,
174162306a36Sopenharmony_ci			AR5K_PHY_AGCCTL_CAL, 0, false);
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	/* Reset to normal state */
174462306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, phy_sig, AR5K_PHY_SIG);
174562306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, phy_agc, AR5K_PHY_AGCCOARSE);
174662306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, phy_sat, AR5K_PHY_ADCSAT);
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_ci	if (ret) {
174962306a36Sopenharmony_ci		ATH5K_ERR(ah, "calibration timeout (%uMHz)\n",
175062306a36Sopenharmony_ci				channel->center_freq);
175162306a36Sopenharmony_ci		return ret;
175262306a36Sopenharmony_ci	}
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci	/*
175562306a36Sopenharmony_ci	 * Re-enable RX/TX and beacons
175662306a36Sopenharmony_ci	 */
175762306a36Sopenharmony_ci	AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW_5210,
175862306a36Sopenharmony_ci		AR5K_DIAG_SW_DIS_TX_5210 | AR5K_DIAG_SW_DIS_RX_5210);
175962306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, beacon, AR5K_BEACON_5210);
176062306a36Sopenharmony_ci
176162306a36Sopenharmony_ci	return 0;
176262306a36Sopenharmony_ci}
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci/**
176562306a36Sopenharmony_ci * ath5k_hw_rf511x_iq_calibrate() - Perform I/Q calibration on RF5111 and newer
176662306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
176762306a36Sopenharmony_ci */
176862306a36Sopenharmony_cistatic int
176962306a36Sopenharmony_ciath5k_hw_rf511x_iq_calibrate(struct ath5k_hw *ah)
177062306a36Sopenharmony_ci{
177162306a36Sopenharmony_ci	u32 i_pwr, q_pwr;
177262306a36Sopenharmony_ci	s32 iq_corr, i_coff, i_coffd, q_coff, q_coffd;
177362306a36Sopenharmony_ci	int i;
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_ci	/* Skip if I/Q calibration is not needed or if it's still running */
177662306a36Sopenharmony_ci	if (!ah->ah_iq_cal_needed)
177762306a36Sopenharmony_ci		return -EINVAL;
177862306a36Sopenharmony_ci	else if (ath5k_hw_reg_read(ah, AR5K_PHY_IQ) & AR5K_PHY_IQ_RUN) {
177962306a36Sopenharmony_ci		ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_CALIBRATE,
178062306a36Sopenharmony_ci				"I/Q calibration still running");
178162306a36Sopenharmony_ci		return -EBUSY;
178262306a36Sopenharmony_ci	}
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	/* Calibration has finished, get the results and re-run */
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	/* Work around for empty results which can apparently happen on 5212:
178762306a36Sopenharmony_ci	 * Read registers up to 10 times until we get both i_pr and q_pwr */
178862306a36Sopenharmony_ci	for (i = 0; i <= 10; i++) {
178962306a36Sopenharmony_ci		iq_corr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_CORR);
179062306a36Sopenharmony_ci		i_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_I);
179162306a36Sopenharmony_ci		q_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_Q);
179262306a36Sopenharmony_ci		ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_CALIBRATE,
179362306a36Sopenharmony_ci			"iq_corr:%x i_pwr:%x q_pwr:%x", iq_corr, i_pwr, q_pwr);
179462306a36Sopenharmony_ci		if (i_pwr && q_pwr)
179562306a36Sopenharmony_ci			break;
179662306a36Sopenharmony_ci	}
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci	i_coffd = ((i_pwr >> 1) + (q_pwr >> 1)) >> 7;
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_ci	if (ah->ah_version == AR5K_AR5211)
180162306a36Sopenharmony_ci		q_coffd = q_pwr >> 6;
180262306a36Sopenharmony_ci	else
180362306a36Sopenharmony_ci		q_coffd = q_pwr >> 7;
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci	/* In case i_coffd became zero, cancel calibration
180662306a36Sopenharmony_ci	 * not only it's too small, it'll also result a divide
180762306a36Sopenharmony_ci	 * by zero later on. */
180862306a36Sopenharmony_ci	if (i_coffd == 0 || q_coffd < 2)
180962306a36Sopenharmony_ci		return -ECANCELED;
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci	/* Protect against loss of sign bits */
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci	i_coff = (-iq_corr) / i_coffd;
181462306a36Sopenharmony_ci	i_coff = clamp(i_coff, -32, 31); /* signed 6 bit */
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci	if (ah->ah_version == AR5K_AR5211)
181762306a36Sopenharmony_ci		q_coff = (i_pwr / q_coffd) - 64;
181862306a36Sopenharmony_ci	else
181962306a36Sopenharmony_ci		q_coff = (i_pwr / q_coffd) - 128;
182062306a36Sopenharmony_ci	q_coff = clamp(q_coff, -16, 15); /* signed 5 bit */
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci	ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_CALIBRATE,
182362306a36Sopenharmony_ci			"new I:%d Q:%d (i_coffd:%x q_coffd:%x)",
182462306a36Sopenharmony_ci			i_coff, q_coff, i_coffd, q_coffd);
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci	/* Commit new I/Q values (set enable bit last to match HAL sources) */
182762306a36Sopenharmony_ci	AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_Q_I_COFF, i_coff);
182862306a36Sopenharmony_ci	AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_Q_Q_COFF, q_coff);
182962306a36Sopenharmony_ci	AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_ENABLE);
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci	/* Re-enable calibration -if we don't we'll commit
183262306a36Sopenharmony_ci	 * the same values again and again */
183362306a36Sopenharmony_ci	AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ,
183462306a36Sopenharmony_ci			AR5K_PHY_IQ_CAL_NUM_LOG_MAX, 15);
183562306a36Sopenharmony_ci	AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_RUN);
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	return 0;
183862306a36Sopenharmony_ci}
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci/**
184162306a36Sopenharmony_ci * ath5k_hw_phy_calibrate() - Perform a PHY calibration
184262306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
184362306a36Sopenharmony_ci * @channel: The &struct ieee80211_channel
184462306a36Sopenharmony_ci *
184562306a36Sopenharmony_ci * The main function we call from above to perform
184662306a36Sopenharmony_ci * a short or full PHY calibration based on RF chip
184762306a36Sopenharmony_ci * and current channel
184862306a36Sopenharmony_ci */
184962306a36Sopenharmony_ciint
185062306a36Sopenharmony_ciath5k_hw_phy_calibrate(struct ath5k_hw *ah,
185162306a36Sopenharmony_ci		struct ieee80211_channel *channel)
185262306a36Sopenharmony_ci{
185362306a36Sopenharmony_ci	int ret;
185462306a36Sopenharmony_ci
185562306a36Sopenharmony_ci	if (ah->ah_radio == AR5K_RF5110)
185662306a36Sopenharmony_ci		return ath5k_hw_rf5110_calibrate(ah, channel);
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci	ret = ath5k_hw_rf511x_iq_calibrate(ah);
185962306a36Sopenharmony_ci	if (ret) {
186062306a36Sopenharmony_ci		ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_CALIBRATE,
186162306a36Sopenharmony_ci			"No I/Q correction performed (%uMHz)\n",
186262306a36Sopenharmony_ci			channel->center_freq);
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci		/* Happens all the time if there is not much
186562306a36Sopenharmony_ci		 * traffic, consider it normal behaviour. */
186662306a36Sopenharmony_ci		ret = 0;
186762306a36Sopenharmony_ci	}
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ci	/* On full calibration request a PAPD probe for
187062306a36Sopenharmony_ci	 * gainf calibration if needed */
187162306a36Sopenharmony_ci	if ((ah->ah_cal_mask & AR5K_CALIBRATION_FULL) &&
187262306a36Sopenharmony_ci	    (ah->ah_radio == AR5K_RF5111 ||
187362306a36Sopenharmony_ci	     ah->ah_radio == AR5K_RF5112) &&
187462306a36Sopenharmony_ci	    channel->hw_value != AR5K_MODE_11B)
187562306a36Sopenharmony_ci		ath5k_hw_request_rfgain_probe(ah);
187662306a36Sopenharmony_ci
187762306a36Sopenharmony_ci	/* Update noise floor */
187862306a36Sopenharmony_ci	if (!(ah->ah_cal_mask & AR5K_CALIBRATION_NF))
187962306a36Sopenharmony_ci		ath5k_hw_update_noise_floor(ah);
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci	return ret;
188262306a36Sopenharmony_ci}
188362306a36Sopenharmony_ci
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_ci/***************************\
188662306a36Sopenharmony_ci* Spur mitigation functions *
188762306a36Sopenharmony_ci\***************************/
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_ci/**
189062306a36Sopenharmony_ci * ath5k_hw_set_spur_mitigation_filter() - Configure SPUR filter
189162306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
189262306a36Sopenharmony_ci * @channel: The &struct ieee80211_channel
189362306a36Sopenharmony_ci *
189462306a36Sopenharmony_ci * This function gets called during PHY initialization to
189562306a36Sopenharmony_ci * configure the spur filter for the given channel. Spur is noise
189662306a36Sopenharmony_ci * generated due to "reflection" effects, for more information on this
189762306a36Sopenharmony_ci * method check out patent US7643810
189862306a36Sopenharmony_ci */
189962306a36Sopenharmony_cistatic void
190062306a36Sopenharmony_ciath5k_hw_set_spur_mitigation_filter(struct ath5k_hw *ah,
190162306a36Sopenharmony_ci				struct ieee80211_channel *channel)
190262306a36Sopenharmony_ci{
190362306a36Sopenharmony_ci	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
190462306a36Sopenharmony_ci	u32 mag_mask[4] = {0, 0, 0, 0};
190562306a36Sopenharmony_ci	u32 pilot_mask[2] = {0, 0};
190662306a36Sopenharmony_ci	/* Note: fbin values are scaled up by 2 */
190762306a36Sopenharmony_ci	u16 spur_chan_fbin, chan_fbin, symbol_width, spur_detection_window;
190862306a36Sopenharmony_ci	s32 spur_delta_phase, spur_freq_sigma_delta;
190962306a36Sopenharmony_ci	s32 spur_offset, num_symbols_x16;
191062306a36Sopenharmony_ci	u8 num_symbol_offsets, i, freq_band;
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci	/* Convert current frequency to fbin value (the same way channels
191362306a36Sopenharmony_ci	 * are stored on EEPROM, check out ath5k_eeprom_bin2freq) and scale
191462306a36Sopenharmony_ci	 * up by 2 so we can compare it later */
191562306a36Sopenharmony_ci	if (channel->band == NL80211_BAND_2GHZ) {
191662306a36Sopenharmony_ci		chan_fbin = (channel->center_freq - 2300) * 10;
191762306a36Sopenharmony_ci		freq_band = AR5K_EEPROM_BAND_2GHZ;
191862306a36Sopenharmony_ci	} else {
191962306a36Sopenharmony_ci		chan_fbin = (channel->center_freq - 4900) * 10;
192062306a36Sopenharmony_ci		freq_band = AR5K_EEPROM_BAND_5GHZ;
192162306a36Sopenharmony_ci	}
192262306a36Sopenharmony_ci
192362306a36Sopenharmony_ci	/* Check if any spur_chan_fbin from EEPROM is
192462306a36Sopenharmony_ci	 * within our current channel's spur detection range */
192562306a36Sopenharmony_ci	spur_chan_fbin = AR5K_EEPROM_NO_SPUR;
192662306a36Sopenharmony_ci	spur_detection_window = AR5K_SPUR_CHAN_WIDTH;
192762306a36Sopenharmony_ci	/* XXX: Half/Quarter channels ?*/
192862306a36Sopenharmony_ci	if (ah->ah_bwmode == AR5K_BWMODE_40MHZ)
192962306a36Sopenharmony_ci		spur_detection_window *= 2;
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_ci	for (i = 0; i < AR5K_EEPROM_N_SPUR_CHANS; i++) {
193262306a36Sopenharmony_ci		spur_chan_fbin = ee->ee_spur_chans[i][freq_band];
193362306a36Sopenharmony_ci
193462306a36Sopenharmony_ci		/* Note: mask cleans AR5K_EEPROM_NO_SPUR flag
193562306a36Sopenharmony_ci		 * so it's zero if we got nothing from EEPROM */
193662306a36Sopenharmony_ci		if (spur_chan_fbin == AR5K_EEPROM_NO_SPUR) {
193762306a36Sopenharmony_ci			spur_chan_fbin &= AR5K_EEPROM_SPUR_CHAN_MASK;
193862306a36Sopenharmony_ci			break;
193962306a36Sopenharmony_ci		}
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci		if ((chan_fbin - spur_detection_window <=
194262306a36Sopenharmony_ci		(spur_chan_fbin & AR5K_EEPROM_SPUR_CHAN_MASK)) &&
194362306a36Sopenharmony_ci		(chan_fbin + spur_detection_window >=
194462306a36Sopenharmony_ci		(spur_chan_fbin & AR5K_EEPROM_SPUR_CHAN_MASK))) {
194562306a36Sopenharmony_ci			spur_chan_fbin &= AR5K_EEPROM_SPUR_CHAN_MASK;
194662306a36Sopenharmony_ci			break;
194762306a36Sopenharmony_ci		}
194862306a36Sopenharmony_ci	}
194962306a36Sopenharmony_ci
195062306a36Sopenharmony_ci	/* We need to enable spur filter for this channel */
195162306a36Sopenharmony_ci	if (spur_chan_fbin) {
195262306a36Sopenharmony_ci		spur_offset = spur_chan_fbin - chan_fbin;
195362306a36Sopenharmony_ci		/*
195462306a36Sopenharmony_ci		 * Calculate deltas:
195562306a36Sopenharmony_ci		 * spur_freq_sigma_delta -> spur_offset / sample_freq << 21
195662306a36Sopenharmony_ci		 * spur_delta_phase -> spur_offset / chip_freq << 11
195762306a36Sopenharmony_ci		 * Note: Both values have 100Hz resolution
195862306a36Sopenharmony_ci		 */
195962306a36Sopenharmony_ci		switch (ah->ah_bwmode) {
196062306a36Sopenharmony_ci		case AR5K_BWMODE_40MHZ:
196162306a36Sopenharmony_ci			/* Both sample_freq and chip_freq are 80MHz */
196262306a36Sopenharmony_ci			spur_delta_phase = (spur_offset << 16) / 25;
196362306a36Sopenharmony_ci			spur_freq_sigma_delta = (spur_delta_phase >> 10);
196462306a36Sopenharmony_ci			symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz * 2;
196562306a36Sopenharmony_ci			break;
196662306a36Sopenharmony_ci		case AR5K_BWMODE_10MHZ:
196762306a36Sopenharmony_ci			/* Both sample_freq and chip_freq are 20MHz (?) */
196862306a36Sopenharmony_ci			spur_delta_phase = (spur_offset << 18) / 25;
196962306a36Sopenharmony_ci			spur_freq_sigma_delta = (spur_delta_phase >> 10);
197062306a36Sopenharmony_ci			symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz / 2;
197162306a36Sopenharmony_ci			break;
197262306a36Sopenharmony_ci		case AR5K_BWMODE_5MHZ:
197362306a36Sopenharmony_ci			/* Both sample_freq and chip_freq are 10MHz (?) */
197462306a36Sopenharmony_ci			spur_delta_phase = (spur_offset << 19) / 25;
197562306a36Sopenharmony_ci			spur_freq_sigma_delta = (spur_delta_phase >> 10);
197662306a36Sopenharmony_ci			symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz / 4;
197762306a36Sopenharmony_ci			break;
197862306a36Sopenharmony_ci		default:
197962306a36Sopenharmony_ci			if (channel->band == NL80211_BAND_5GHZ) {
198062306a36Sopenharmony_ci				/* Both sample_freq and chip_freq are 40MHz */
198162306a36Sopenharmony_ci				spur_delta_phase = (spur_offset << 17) / 25;
198262306a36Sopenharmony_ci				spur_freq_sigma_delta =
198362306a36Sopenharmony_ci						(spur_delta_phase >> 10);
198462306a36Sopenharmony_ci				symbol_width =
198562306a36Sopenharmony_ci					AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz;
198662306a36Sopenharmony_ci			} else {
198762306a36Sopenharmony_ci				/* sample_freq -> 40MHz chip_freq -> 44MHz
198862306a36Sopenharmony_ci				 * (for b compatibility) */
198962306a36Sopenharmony_ci				spur_delta_phase = (spur_offset << 17) / 25;
199062306a36Sopenharmony_ci				spur_freq_sigma_delta =
199162306a36Sopenharmony_ci						(spur_offset << 8) / 55;
199262306a36Sopenharmony_ci				symbol_width =
199362306a36Sopenharmony_ci					AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz;
199462306a36Sopenharmony_ci			}
199562306a36Sopenharmony_ci			break;
199662306a36Sopenharmony_ci		}
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_ci		/* Calculate pilot and magnitude masks */
199962306a36Sopenharmony_ci
200062306a36Sopenharmony_ci		/* Scale up spur_offset by 1000 to switch to 100HZ resolution
200162306a36Sopenharmony_ci		 * and divide by symbol_width to find how many symbols we have
200262306a36Sopenharmony_ci		 * Note: number of symbols is scaled up by 16 */
200362306a36Sopenharmony_ci		num_symbols_x16 = ((spur_offset * 1000) << 4) / symbol_width;
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_ci		/* Spur is on a symbol if num_symbols_x16 % 16 is zero */
200662306a36Sopenharmony_ci		if (!(num_symbols_x16 & 0xF))
200762306a36Sopenharmony_ci			/* _X_ */
200862306a36Sopenharmony_ci			num_symbol_offsets = 3;
200962306a36Sopenharmony_ci		else
201062306a36Sopenharmony_ci			/* _xx_ */
201162306a36Sopenharmony_ci			num_symbol_offsets = 4;
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_ci		for (i = 0; i < num_symbol_offsets; i++) {
201462306a36Sopenharmony_ci
201562306a36Sopenharmony_ci			/* Calculate pilot mask */
201662306a36Sopenharmony_ci			s32 curr_sym_off =
201762306a36Sopenharmony_ci				(num_symbols_x16 / 16) + i + 25;
201862306a36Sopenharmony_ci
201962306a36Sopenharmony_ci			/* Pilot magnitude mask seems to be a way to
202062306a36Sopenharmony_ci			 * declare the boundaries for our detection
202162306a36Sopenharmony_ci			 * window or something, it's 2 for the middle
202262306a36Sopenharmony_ci			 * value(s) where the symbol is expected to be
202362306a36Sopenharmony_ci			 * and 1 on the boundary values */
202462306a36Sopenharmony_ci			u8 plt_mag_map =
202562306a36Sopenharmony_ci				(i == 0 || i == (num_symbol_offsets - 1))
202662306a36Sopenharmony_ci								? 1 : 2;
202762306a36Sopenharmony_ci
202862306a36Sopenharmony_ci			if (curr_sym_off >= 0 && curr_sym_off <= 32) {
202962306a36Sopenharmony_ci				if (curr_sym_off <= 25)
203062306a36Sopenharmony_ci					pilot_mask[0] |= 1 << curr_sym_off;
203162306a36Sopenharmony_ci				else if (curr_sym_off >= 27)
203262306a36Sopenharmony_ci					pilot_mask[0] |= 1 << (curr_sym_off - 1);
203362306a36Sopenharmony_ci			} else if (curr_sym_off >= 33 && curr_sym_off <= 52)
203462306a36Sopenharmony_ci				pilot_mask[1] |= 1 << (curr_sym_off - 33);
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ci			/* Calculate magnitude mask (for viterbi decoder) */
203762306a36Sopenharmony_ci			if (curr_sym_off >= -1 && curr_sym_off <= 14)
203862306a36Sopenharmony_ci				mag_mask[0] |=
203962306a36Sopenharmony_ci					plt_mag_map << (curr_sym_off + 1) * 2;
204062306a36Sopenharmony_ci			else if (curr_sym_off >= 15 && curr_sym_off <= 30)
204162306a36Sopenharmony_ci				mag_mask[1] |=
204262306a36Sopenharmony_ci					plt_mag_map << (curr_sym_off - 15) * 2;
204362306a36Sopenharmony_ci			else if (curr_sym_off >= 31 && curr_sym_off <= 46)
204462306a36Sopenharmony_ci				mag_mask[2] |=
204562306a36Sopenharmony_ci					plt_mag_map << (curr_sym_off - 31) * 2;
204662306a36Sopenharmony_ci			else if (curr_sym_off >= 47 && curr_sym_off <= 53)
204762306a36Sopenharmony_ci				mag_mask[3] |=
204862306a36Sopenharmony_ci					plt_mag_map << (curr_sym_off - 47) * 2;
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_ci		}
205162306a36Sopenharmony_ci
205262306a36Sopenharmony_ci		/* Write settings on hw to enable spur filter */
205362306a36Sopenharmony_ci		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL,
205462306a36Sopenharmony_ci					AR5K_PHY_BIN_MASK_CTL_RATE, 0xff);
205562306a36Sopenharmony_ci		/* XXX: Self correlator also ? */
205662306a36Sopenharmony_ci		AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ,
205762306a36Sopenharmony_ci					AR5K_PHY_IQ_PILOT_MASK_EN |
205862306a36Sopenharmony_ci					AR5K_PHY_IQ_CHAN_MASK_EN |
205962306a36Sopenharmony_ci					AR5K_PHY_IQ_SPUR_FILT_EN);
206062306a36Sopenharmony_ci
206162306a36Sopenharmony_ci		/* Set delta phase and freq sigma delta */
206262306a36Sopenharmony_ci		ath5k_hw_reg_write(ah,
206362306a36Sopenharmony_ci				AR5K_REG_SM(spur_delta_phase,
206462306a36Sopenharmony_ci					AR5K_PHY_TIMING_11_SPUR_DELTA_PHASE) |
206562306a36Sopenharmony_ci				AR5K_REG_SM(spur_freq_sigma_delta,
206662306a36Sopenharmony_ci				AR5K_PHY_TIMING_11_SPUR_FREQ_SD) |
206762306a36Sopenharmony_ci				AR5K_PHY_TIMING_11_USE_SPUR_IN_AGC,
206862306a36Sopenharmony_ci				AR5K_PHY_TIMING_11);
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_ci		/* Write pilot masks */
207162306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, pilot_mask[0], AR5K_PHY_TIMING_7);
207262306a36Sopenharmony_ci		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_8,
207362306a36Sopenharmony_ci					AR5K_PHY_TIMING_8_PILOT_MASK_2,
207462306a36Sopenharmony_ci					pilot_mask[1]);
207562306a36Sopenharmony_ci
207662306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, pilot_mask[0], AR5K_PHY_TIMING_9);
207762306a36Sopenharmony_ci		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_10,
207862306a36Sopenharmony_ci					AR5K_PHY_TIMING_10_PILOT_MASK_2,
207962306a36Sopenharmony_ci					pilot_mask[1]);
208062306a36Sopenharmony_ci
208162306a36Sopenharmony_ci		/* Write magnitude masks */
208262306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, mag_mask[0], AR5K_PHY_BIN_MASK_1);
208362306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, mag_mask[1], AR5K_PHY_BIN_MASK_2);
208462306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, mag_mask[2], AR5K_PHY_BIN_MASK_3);
208562306a36Sopenharmony_ci		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL,
208662306a36Sopenharmony_ci					AR5K_PHY_BIN_MASK_CTL_MASK_4,
208762306a36Sopenharmony_ci					mag_mask[3]);
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, mag_mask[0], AR5K_PHY_BIN_MASK2_1);
209062306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, mag_mask[1], AR5K_PHY_BIN_MASK2_2);
209162306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, mag_mask[2], AR5K_PHY_BIN_MASK2_3);
209262306a36Sopenharmony_ci		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK2_4,
209362306a36Sopenharmony_ci					AR5K_PHY_BIN_MASK2_4_MASK_4,
209462306a36Sopenharmony_ci					mag_mask[3]);
209562306a36Sopenharmony_ci
209662306a36Sopenharmony_ci	} else if (ath5k_hw_reg_read(ah, AR5K_PHY_IQ) &
209762306a36Sopenharmony_ci	AR5K_PHY_IQ_SPUR_FILT_EN) {
209862306a36Sopenharmony_ci		/* Clean up spur mitigation settings and disable filter */
209962306a36Sopenharmony_ci		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL,
210062306a36Sopenharmony_ci					AR5K_PHY_BIN_MASK_CTL_RATE, 0);
210162306a36Sopenharmony_ci		AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_IQ,
210262306a36Sopenharmony_ci					AR5K_PHY_IQ_PILOT_MASK_EN |
210362306a36Sopenharmony_ci					AR5K_PHY_IQ_CHAN_MASK_EN |
210462306a36Sopenharmony_ci					AR5K_PHY_IQ_SPUR_FILT_EN);
210562306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, 0, AR5K_PHY_TIMING_11);
210662306a36Sopenharmony_ci
210762306a36Sopenharmony_ci		/* Clear pilot masks */
210862306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, 0, AR5K_PHY_TIMING_7);
210962306a36Sopenharmony_ci		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_8,
211062306a36Sopenharmony_ci					AR5K_PHY_TIMING_8_PILOT_MASK_2,
211162306a36Sopenharmony_ci					0);
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, 0, AR5K_PHY_TIMING_9);
211462306a36Sopenharmony_ci		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_10,
211562306a36Sopenharmony_ci					AR5K_PHY_TIMING_10_PILOT_MASK_2,
211662306a36Sopenharmony_ci					0);
211762306a36Sopenharmony_ci
211862306a36Sopenharmony_ci		/* Clear magnitude masks */
211962306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK_1);
212062306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK_2);
212162306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK_3);
212262306a36Sopenharmony_ci		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL,
212362306a36Sopenharmony_ci					AR5K_PHY_BIN_MASK_CTL_MASK_4,
212462306a36Sopenharmony_ci					0);
212562306a36Sopenharmony_ci
212662306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK2_1);
212762306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK2_2);
212862306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK2_3);
212962306a36Sopenharmony_ci		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK2_4,
213062306a36Sopenharmony_ci					AR5K_PHY_BIN_MASK2_4_MASK_4,
213162306a36Sopenharmony_ci					0);
213262306a36Sopenharmony_ci	}
213362306a36Sopenharmony_ci}
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_ci
213662306a36Sopenharmony_ci/*****************\
213762306a36Sopenharmony_ci* Antenna control *
213862306a36Sopenharmony_ci\*****************/
213962306a36Sopenharmony_ci
214062306a36Sopenharmony_ci/**
214162306a36Sopenharmony_ci * DOC: Antenna control
214262306a36Sopenharmony_ci *
214362306a36Sopenharmony_ci * Hw supports up to 14 antennas ! I haven't found any card that implements
214462306a36Sopenharmony_ci * that. The maximum number of antennas I've seen is up to 4 (2 for 2GHz and 2
214562306a36Sopenharmony_ci * for 5GHz). Antenna 1 (MAIN) should be omnidirectional, 2 (AUX)
214662306a36Sopenharmony_ci * omnidirectional or sectorial and antennas 3-14 sectorial (or directional).
214762306a36Sopenharmony_ci *
214862306a36Sopenharmony_ci * We can have a single antenna for RX and multiple antennas for TX.
214962306a36Sopenharmony_ci * RX antenna is our "default" antenna (usually antenna 1) set on
215062306a36Sopenharmony_ci * DEFAULT_ANTENNA register and TX antenna is set on each TX control descriptor
215162306a36Sopenharmony_ci * (0 for automatic selection, 1 - 14 antenna number).
215262306a36Sopenharmony_ci *
215362306a36Sopenharmony_ci * We can let hw do all the work doing fast antenna diversity for both
215462306a36Sopenharmony_ci * tx and rx or we can do things manually. Here are the options we have
215562306a36Sopenharmony_ci * (all are bits of STA_ID1 register):
215662306a36Sopenharmony_ci *
215762306a36Sopenharmony_ci * AR5K_STA_ID1_DEFAULT_ANTENNA -> When 0 is set as the TX antenna on TX
215862306a36Sopenharmony_ci * control descriptor, use the default antenna to transmit or else use the last
215962306a36Sopenharmony_ci * antenna on which we received an ACK.
216062306a36Sopenharmony_ci *
216162306a36Sopenharmony_ci * AR5K_STA_ID1_DESC_ANTENNA -> Update default antenna after each TX frame to
216262306a36Sopenharmony_ci * the antenna on which we got the ACK for that frame.
216362306a36Sopenharmony_ci *
216462306a36Sopenharmony_ci * AR5K_STA_ID1_RTS_DEF_ANTENNA -> Use default antenna for RTS or else use the
216562306a36Sopenharmony_ci * one on the TX descriptor.
216662306a36Sopenharmony_ci *
216762306a36Sopenharmony_ci * AR5K_STA_ID1_SELFGEN_DEF_ANT -> Use default antenna for self generated frames
216862306a36Sopenharmony_ci * (ACKs etc), or else use current antenna (the one we just used for TX).
216962306a36Sopenharmony_ci *
217062306a36Sopenharmony_ci * Using the above we support the following scenarios:
217162306a36Sopenharmony_ci *
217262306a36Sopenharmony_ci * AR5K_ANTMODE_DEFAULT -> Hw handles antenna diversity etc automatically
217362306a36Sopenharmony_ci *
217462306a36Sopenharmony_ci * AR5K_ANTMODE_FIXED_A	-> Only antenna A (MAIN) is present
217562306a36Sopenharmony_ci *
217662306a36Sopenharmony_ci * AR5K_ANTMODE_FIXED_B	-> Only antenna B (AUX) is present
217762306a36Sopenharmony_ci *
217862306a36Sopenharmony_ci * AR5K_ANTMODE_SINGLE_AP -> Sta locked on a single ap
217962306a36Sopenharmony_ci *
218062306a36Sopenharmony_ci * AR5K_ANTMODE_SECTOR_AP -> AP with tx antenna set on tx desc
218162306a36Sopenharmony_ci *
218262306a36Sopenharmony_ci * AR5K_ANTMODE_SECTOR_STA -> STA with tx antenna set on tx desc
218362306a36Sopenharmony_ci *
218462306a36Sopenharmony_ci * AR5K_ANTMODE_DEBUG Debug mode -A -> Rx, B-> Tx-
218562306a36Sopenharmony_ci *
218662306a36Sopenharmony_ci * Also note that when setting antenna to F on tx descriptor card inverts
218762306a36Sopenharmony_ci * current tx antenna.
218862306a36Sopenharmony_ci */
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci/**
219162306a36Sopenharmony_ci * ath5k_hw_set_def_antenna() - Set default rx antenna on AR5211/5212 and newer
219262306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
219362306a36Sopenharmony_ci * @ant: Antenna number
219462306a36Sopenharmony_ci */
219562306a36Sopenharmony_cistatic void
219662306a36Sopenharmony_ciath5k_hw_set_def_antenna(struct ath5k_hw *ah, u8 ant)
219762306a36Sopenharmony_ci{
219862306a36Sopenharmony_ci	if (ah->ah_version != AR5K_AR5210)
219962306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, ant & 0x7, AR5K_DEFAULT_ANTENNA);
220062306a36Sopenharmony_ci}
220162306a36Sopenharmony_ci
220262306a36Sopenharmony_ci/**
220362306a36Sopenharmony_ci * ath5k_hw_set_fast_div() -  Enable/disable fast rx antenna diversity
220462306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
220562306a36Sopenharmony_ci * @ee_mode: One of enum ath5k_driver_mode
220662306a36Sopenharmony_ci * @enable: True to enable, false to disable
220762306a36Sopenharmony_ci */
220862306a36Sopenharmony_cistatic void
220962306a36Sopenharmony_ciath5k_hw_set_fast_div(struct ath5k_hw *ah, u8 ee_mode, bool enable)
221062306a36Sopenharmony_ci{
221162306a36Sopenharmony_ci	switch (ee_mode) {
221262306a36Sopenharmony_ci	case AR5K_EEPROM_MODE_11G:
221362306a36Sopenharmony_ci		/* XXX: This is set to
221462306a36Sopenharmony_ci		 * disabled on initvals !!! */
221562306a36Sopenharmony_ci	case AR5K_EEPROM_MODE_11A:
221662306a36Sopenharmony_ci		if (enable)
221762306a36Sopenharmony_ci			AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_AGCCTL,
221862306a36Sopenharmony_ci					AR5K_PHY_AGCCTL_OFDM_DIV_DIS);
221962306a36Sopenharmony_ci		else
222062306a36Sopenharmony_ci			AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL,
222162306a36Sopenharmony_ci					AR5K_PHY_AGCCTL_OFDM_DIV_DIS);
222262306a36Sopenharmony_ci		break;
222362306a36Sopenharmony_ci	case AR5K_EEPROM_MODE_11B:
222462306a36Sopenharmony_ci		AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL,
222562306a36Sopenharmony_ci					AR5K_PHY_AGCCTL_OFDM_DIV_DIS);
222662306a36Sopenharmony_ci		break;
222762306a36Sopenharmony_ci	default:
222862306a36Sopenharmony_ci		return;
222962306a36Sopenharmony_ci	}
223062306a36Sopenharmony_ci
223162306a36Sopenharmony_ci	if (enable) {
223262306a36Sopenharmony_ci		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_RESTART,
223362306a36Sopenharmony_ci				AR5K_PHY_RESTART_DIV_GC, 4);
223462306a36Sopenharmony_ci
223562306a36Sopenharmony_ci		AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_FAST_ANT_DIV,
223662306a36Sopenharmony_ci					AR5K_PHY_FAST_ANT_DIV_EN);
223762306a36Sopenharmony_ci	} else {
223862306a36Sopenharmony_ci		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_RESTART,
223962306a36Sopenharmony_ci				AR5K_PHY_RESTART_DIV_GC, 0);
224062306a36Sopenharmony_ci
224162306a36Sopenharmony_ci		AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_FAST_ANT_DIV,
224262306a36Sopenharmony_ci					AR5K_PHY_FAST_ANT_DIV_EN);
224362306a36Sopenharmony_ci	}
224462306a36Sopenharmony_ci}
224562306a36Sopenharmony_ci
224662306a36Sopenharmony_ci/**
224762306a36Sopenharmony_ci * ath5k_hw_set_antenna_switch() - Set up antenna switch table
224862306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
224962306a36Sopenharmony_ci * @ee_mode: One of enum ath5k_driver_mode
225062306a36Sopenharmony_ci *
225162306a36Sopenharmony_ci * Switch table comes from EEPROM and includes information on controlling
225262306a36Sopenharmony_ci * the 2 antenna RX attenuators
225362306a36Sopenharmony_ci */
225462306a36Sopenharmony_civoid
225562306a36Sopenharmony_ciath5k_hw_set_antenna_switch(struct ath5k_hw *ah, u8 ee_mode)
225662306a36Sopenharmony_ci{
225762306a36Sopenharmony_ci	u8 ant0, ant1;
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_ci	/*
226062306a36Sopenharmony_ci	 * In case a fixed antenna was set as default
226162306a36Sopenharmony_ci	 * use the same switch table twice.
226262306a36Sopenharmony_ci	 */
226362306a36Sopenharmony_ci	if (ah->ah_ant_mode == AR5K_ANTMODE_FIXED_A)
226462306a36Sopenharmony_ci		ant0 = ant1 = AR5K_ANT_SWTABLE_A;
226562306a36Sopenharmony_ci	else if (ah->ah_ant_mode == AR5K_ANTMODE_FIXED_B)
226662306a36Sopenharmony_ci		ant0 = ant1 = AR5K_ANT_SWTABLE_B;
226762306a36Sopenharmony_ci	else {
226862306a36Sopenharmony_ci		ant0 = AR5K_ANT_SWTABLE_A;
226962306a36Sopenharmony_ci		ant1 = AR5K_ANT_SWTABLE_B;
227062306a36Sopenharmony_ci	}
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_ci	/* Set antenna idle switch table */
227362306a36Sopenharmony_ci	AR5K_REG_WRITE_BITS(ah, AR5K_PHY_ANT_CTL,
227462306a36Sopenharmony_ci			AR5K_PHY_ANT_CTL_SWTABLE_IDLE,
227562306a36Sopenharmony_ci			(ah->ah_ant_ctl[ee_mode][AR5K_ANT_CTL] |
227662306a36Sopenharmony_ci			AR5K_PHY_ANT_CTL_TXRX_EN));
227762306a36Sopenharmony_ci
227862306a36Sopenharmony_ci	/* Set antenna switch tables */
227962306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, ah->ah_ant_ctl[ee_mode][ant0],
228062306a36Sopenharmony_ci		AR5K_PHY_ANT_SWITCH_TABLE_0);
228162306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, ah->ah_ant_ctl[ee_mode][ant1],
228262306a36Sopenharmony_ci		AR5K_PHY_ANT_SWITCH_TABLE_1);
228362306a36Sopenharmony_ci}
228462306a36Sopenharmony_ci
228562306a36Sopenharmony_ci/**
228662306a36Sopenharmony_ci * ath5k_hw_set_antenna_mode() -  Set antenna operating mode
228762306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
228862306a36Sopenharmony_ci * @ant_mode: One of enum ath5k_ant_mode
228962306a36Sopenharmony_ci */
229062306a36Sopenharmony_civoid
229162306a36Sopenharmony_ciath5k_hw_set_antenna_mode(struct ath5k_hw *ah, u8 ant_mode)
229262306a36Sopenharmony_ci{
229362306a36Sopenharmony_ci	struct ieee80211_channel *channel = ah->ah_current_channel;
229462306a36Sopenharmony_ci	bool use_def_for_tx, update_def_on_tx, use_def_for_rts, fast_div;
229562306a36Sopenharmony_ci	bool use_def_for_sg;
229662306a36Sopenharmony_ci	int ee_mode;
229762306a36Sopenharmony_ci	u8 def_ant, tx_ant;
229862306a36Sopenharmony_ci	u32 sta_id1 = 0;
229962306a36Sopenharmony_ci
230062306a36Sopenharmony_ci	/* if channel is not initialized yet we can't set the antennas
230162306a36Sopenharmony_ci	 * so just store the mode. it will be set on the next reset */
230262306a36Sopenharmony_ci	if (channel == NULL) {
230362306a36Sopenharmony_ci		ah->ah_ant_mode = ant_mode;
230462306a36Sopenharmony_ci		return;
230562306a36Sopenharmony_ci	}
230662306a36Sopenharmony_ci
230762306a36Sopenharmony_ci	def_ant = ah->ah_def_ant;
230862306a36Sopenharmony_ci
230962306a36Sopenharmony_ci	ee_mode = ath5k_eeprom_mode_from_channel(ah, channel);
231062306a36Sopenharmony_ci
231162306a36Sopenharmony_ci	switch (ant_mode) {
231262306a36Sopenharmony_ci	case AR5K_ANTMODE_DEFAULT:
231362306a36Sopenharmony_ci		tx_ant = 0;
231462306a36Sopenharmony_ci		use_def_for_tx = false;
231562306a36Sopenharmony_ci		update_def_on_tx = false;
231662306a36Sopenharmony_ci		use_def_for_rts = false;
231762306a36Sopenharmony_ci		use_def_for_sg = false;
231862306a36Sopenharmony_ci		fast_div = true;
231962306a36Sopenharmony_ci		break;
232062306a36Sopenharmony_ci	case AR5K_ANTMODE_FIXED_A:
232162306a36Sopenharmony_ci		def_ant = 1;
232262306a36Sopenharmony_ci		tx_ant = 1;
232362306a36Sopenharmony_ci		use_def_for_tx = true;
232462306a36Sopenharmony_ci		update_def_on_tx = false;
232562306a36Sopenharmony_ci		use_def_for_rts = true;
232662306a36Sopenharmony_ci		use_def_for_sg = true;
232762306a36Sopenharmony_ci		fast_div = false;
232862306a36Sopenharmony_ci		break;
232962306a36Sopenharmony_ci	case AR5K_ANTMODE_FIXED_B:
233062306a36Sopenharmony_ci		def_ant = 2;
233162306a36Sopenharmony_ci		tx_ant = 2;
233262306a36Sopenharmony_ci		use_def_for_tx = true;
233362306a36Sopenharmony_ci		update_def_on_tx = false;
233462306a36Sopenharmony_ci		use_def_for_rts = true;
233562306a36Sopenharmony_ci		use_def_for_sg = true;
233662306a36Sopenharmony_ci		fast_div = false;
233762306a36Sopenharmony_ci		break;
233862306a36Sopenharmony_ci	case AR5K_ANTMODE_SINGLE_AP:
233962306a36Sopenharmony_ci		def_ant = 1;	/* updated on tx */
234062306a36Sopenharmony_ci		tx_ant = 0;
234162306a36Sopenharmony_ci		use_def_for_tx = true;
234262306a36Sopenharmony_ci		update_def_on_tx = true;
234362306a36Sopenharmony_ci		use_def_for_rts = true;
234462306a36Sopenharmony_ci		use_def_for_sg = true;
234562306a36Sopenharmony_ci		fast_div = true;
234662306a36Sopenharmony_ci		break;
234762306a36Sopenharmony_ci	case AR5K_ANTMODE_SECTOR_AP:
234862306a36Sopenharmony_ci		tx_ant = 1;	/* variable */
234962306a36Sopenharmony_ci		use_def_for_tx = false;
235062306a36Sopenharmony_ci		update_def_on_tx = false;
235162306a36Sopenharmony_ci		use_def_for_rts = true;
235262306a36Sopenharmony_ci		use_def_for_sg = false;
235362306a36Sopenharmony_ci		fast_div = false;
235462306a36Sopenharmony_ci		break;
235562306a36Sopenharmony_ci	case AR5K_ANTMODE_SECTOR_STA:
235662306a36Sopenharmony_ci		tx_ant = 1;	/* variable */
235762306a36Sopenharmony_ci		use_def_for_tx = true;
235862306a36Sopenharmony_ci		update_def_on_tx = false;
235962306a36Sopenharmony_ci		use_def_for_rts = true;
236062306a36Sopenharmony_ci		use_def_for_sg = false;
236162306a36Sopenharmony_ci		fast_div = true;
236262306a36Sopenharmony_ci		break;
236362306a36Sopenharmony_ci	case AR5K_ANTMODE_DEBUG:
236462306a36Sopenharmony_ci		def_ant = 1;
236562306a36Sopenharmony_ci		tx_ant = 2;
236662306a36Sopenharmony_ci		use_def_for_tx = false;
236762306a36Sopenharmony_ci		update_def_on_tx = false;
236862306a36Sopenharmony_ci		use_def_for_rts = false;
236962306a36Sopenharmony_ci		use_def_for_sg = false;
237062306a36Sopenharmony_ci		fast_div = false;
237162306a36Sopenharmony_ci		break;
237262306a36Sopenharmony_ci	default:
237362306a36Sopenharmony_ci		return;
237462306a36Sopenharmony_ci	}
237562306a36Sopenharmony_ci
237662306a36Sopenharmony_ci	ah->ah_tx_ant = tx_ant;
237762306a36Sopenharmony_ci	ah->ah_ant_mode = ant_mode;
237862306a36Sopenharmony_ci	ah->ah_def_ant = def_ant;
237962306a36Sopenharmony_ci
238062306a36Sopenharmony_ci	sta_id1 |= use_def_for_tx ? AR5K_STA_ID1_DEFAULT_ANTENNA : 0;
238162306a36Sopenharmony_ci	sta_id1 |= update_def_on_tx ? AR5K_STA_ID1_DESC_ANTENNA : 0;
238262306a36Sopenharmony_ci	sta_id1 |= use_def_for_rts ? AR5K_STA_ID1_RTS_DEF_ANTENNA : 0;
238362306a36Sopenharmony_ci	sta_id1 |= use_def_for_sg ? AR5K_STA_ID1_SELFGEN_DEF_ANT : 0;
238462306a36Sopenharmony_ci
238562306a36Sopenharmony_ci	AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, AR5K_STA_ID1_ANTENNA_SETTINGS);
238662306a36Sopenharmony_ci
238762306a36Sopenharmony_ci	if (sta_id1)
238862306a36Sopenharmony_ci		AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1, sta_id1);
238962306a36Sopenharmony_ci
239062306a36Sopenharmony_ci	ath5k_hw_set_antenna_switch(ah, ee_mode);
239162306a36Sopenharmony_ci	/* Note: set diversity before default antenna
239262306a36Sopenharmony_ci	 * because it won't work correctly */
239362306a36Sopenharmony_ci	ath5k_hw_set_fast_div(ah, ee_mode, fast_div);
239462306a36Sopenharmony_ci	ath5k_hw_set_def_antenna(ah, def_ant);
239562306a36Sopenharmony_ci}
239662306a36Sopenharmony_ci
239762306a36Sopenharmony_ci
239862306a36Sopenharmony_ci/****************\
239962306a36Sopenharmony_ci* TX power setup *
240062306a36Sopenharmony_ci\****************/
240162306a36Sopenharmony_ci
240262306a36Sopenharmony_ci/*
240362306a36Sopenharmony_ci * Helper functions
240462306a36Sopenharmony_ci */
240562306a36Sopenharmony_ci
240662306a36Sopenharmony_ci/**
240762306a36Sopenharmony_ci * ath5k_get_interpolated_value() - Get interpolated Y val between two points
240862306a36Sopenharmony_ci * @target: X value of the middle point
240962306a36Sopenharmony_ci * @x_left: X value of the left point
241062306a36Sopenharmony_ci * @x_right: X value of the right point
241162306a36Sopenharmony_ci * @y_left: Y value of the left point
241262306a36Sopenharmony_ci * @y_right: Y value of the right point
241362306a36Sopenharmony_ci */
241462306a36Sopenharmony_cistatic s16
241562306a36Sopenharmony_ciath5k_get_interpolated_value(s16 target, s16 x_left, s16 x_right,
241662306a36Sopenharmony_ci					s16 y_left, s16 y_right)
241762306a36Sopenharmony_ci{
241862306a36Sopenharmony_ci	s16 ratio, result;
241962306a36Sopenharmony_ci
242062306a36Sopenharmony_ci	/* Avoid divide by zero and skip interpolation
242162306a36Sopenharmony_ci	 * if we have the same point */
242262306a36Sopenharmony_ci	if ((x_left == x_right) || (y_left == y_right))
242362306a36Sopenharmony_ci		return y_left;
242462306a36Sopenharmony_ci
242562306a36Sopenharmony_ci	/*
242662306a36Sopenharmony_ci	 * Since we use ints and not fps, we need to scale up in
242762306a36Sopenharmony_ci	 * order to get a sane ratio value (or else we 'll eg. get
242862306a36Sopenharmony_ci	 * always 1 instead of 1.25, 1.75 etc). We scale up by 100
242962306a36Sopenharmony_ci	 * to have some accuracy both for 0.5 and 0.25 steps.
243062306a36Sopenharmony_ci	 */
243162306a36Sopenharmony_ci	ratio = ((100 * y_right - 100 * y_left) / (x_right - x_left));
243262306a36Sopenharmony_ci
243362306a36Sopenharmony_ci	/* Now scale down to be in range */
243462306a36Sopenharmony_ci	result = y_left + (ratio * (target - x_left) / 100);
243562306a36Sopenharmony_ci
243662306a36Sopenharmony_ci	return result;
243762306a36Sopenharmony_ci}
243862306a36Sopenharmony_ci
243962306a36Sopenharmony_ci/**
244062306a36Sopenharmony_ci * ath5k_get_linear_pcdac_min() - Find vertical boundary (min pwr) for the
244162306a36Sopenharmony_ci * linear PCDAC curve
244262306a36Sopenharmony_ci * @stepL: Left array with y values (pcdac steps)
244362306a36Sopenharmony_ci * @stepR: Right array with y values (pcdac steps)
244462306a36Sopenharmony_ci * @pwrL: Left array with x values (power steps)
244562306a36Sopenharmony_ci * @pwrR: Right array with x values (power steps)
244662306a36Sopenharmony_ci *
244762306a36Sopenharmony_ci * Since we have the top of the curve and we draw the line below
244862306a36Sopenharmony_ci * until we reach 1 (1 pcdac step) we need to know which point
244962306a36Sopenharmony_ci * (x value) that is so that we don't go below x axis and have negative
245062306a36Sopenharmony_ci * pcdac values when creating the curve, or fill the table with zeros.
245162306a36Sopenharmony_ci */
245262306a36Sopenharmony_cistatic s16
245362306a36Sopenharmony_ciath5k_get_linear_pcdac_min(const u8 *stepL, const u8 *stepR,
245462306a36Sopenharmony_ci				const s16 *pwrL, const s16 *pwrR)
245562306a36Sopenharmony_ci{
245662306a36Sopenharmony_ci	s8 tmp;
245762306a36Sopenharmony_ci	s16 min_pwrL, min_pwrR;
245862306a36Sopenharmony_ci	s16 pwr_i;
245962306a36Sopenharmony_ci
246062306a36Sopenharmony_ci	/* Some vendors write the same pcdac value twice !!! */
246162306a36Sopenharmony_ci	if (stepL[0] == stepL[1] || stepR[0] == stepR[1])
246262306a36Sopenharmony_ci		return max(pwrL[0], pwrR[0]);
246362306a36Sopenharmony_ci
246462306a36Sopenharmony_ci	if (pwrL[0] == pwrL[1])
246562306a36Sopenharmony_ci		min_pwrL = pwrL[0];
246662306a36Sopenharmony_ci	else {
246762306a36Sopenharmony_ci		pwr_i = pwrL[0];
246862306a36Sopenharmony_ci		do {
246962306a36Sopenharmony_ci			pwr_i--;
247062306a36Sopenharmony_ci			tmp = (s8) ath5k_get_interpolated_value(pwr_i,
247162306a36Sopenharmony_ci							pwrL[0], pwrL[1],
247262306a36Sopenharmony_ci							stepL[0], stepL[1]);
247362306a36Sopenharmony_ci		} while (tmp > 1);
247462306a36Sopenharmony_ci
247562306a36Sopenharmony_ci		min_pwrL = pwr_i;
247662306a36Sopenharmony_ci	}
247762306a36Sopenharmony_ci
247862306a36Sopenharmony_ci	if (pwrR[0] == pwrR[1])
247962306a36Sopenharmony_ci		min_pwrR = pwrR[0];
248062306a36Sopenharmony_ci	else {
248162306a36Sopenharmony_ci		pwr_i = pwrR[0];
248262306a36Sopenharmony_ci		do {
248362306a36Sopenharmony_ci			pwr_i--;
248462306a36Sopenharmony_ci			tmp = (s8) ath5k_get_interpolated_value(pwr_i,
248562306a36Sopenharmony_ci							pwrR[0], pwrR[1],
248662306a36Sopenharmony_ci							stepR[0], stepR[1]);
248762306a36Sopenharmony_ci		} while (tmp > 1);
248862306a36Sopenharmony_ci
248962306a36Sopenharmony_ci		min_pwrR = pwr_i;
249062306a36Sopenharmony_ci	}
249162306a36Sopenharmony_ci
249262306a36Sopenharmony_ci	/* Keep the right boundary so that it works for both curves */
249362306a36Sopenharmony_ci	return max(min_pwrL, min_pwrR);
249462306a36Sopenharmony_ci}
249562306a36Sopenharmony_ci
249662306a36Sopenharmony_ci/**
249762306a36Sopenharmony_ci * ath5k_create_power_curve() - Create a Power to PDADC or PCDAC curve
249862306a36Sopenharmony_ci * @pmin: Minimum power value (xmin)
249962306a36Sopenharmony_ci * @pmax: Maximum power value (xmax)
250062306a36Sopenharmony_ci * @pwr: Array of power steps (x values)
250162306a36Sopenharmony_ci * @vpd: Array of matching PCDAC/PDADC steps (y values)
250262306a36Sopenharmony_ci * @num_points: Number of provided points
250362306a36Sopenharmony_ci * @vpd_table: Array to fill with the full PCDAC/PDADC values (y values)
250462306a36Sopenharmony_ci * @type: One of enum ath5k_powertable_type (eeprom.h)
250562306a36Sopenharmony_ci *
250662306a36Sopenharmony_ci * Interpolate (pwr,vpd) points to create a Power to PDADC or a
250762306a36Sopenharmony_ci * Power to PCDAC curve.
250862306a36Sopenharmony_ci *
250962306a36Sopenharmony_ci * Each curve has power on x axis (in 0.5dB units) and PCDAC/PDADC
251062306a36Sopenharmony_ci * steps (offsets) on y axis. Power can go up to 31.5dB and max
251162306a36Sopenharmony_ci * PCDAC/PDADC step for each curve is 64 but we can write more than
251262306a36Sopenharmony_ci * one curves on hw so we can go up to 128 (which is the max step we
251362306a36Sopenharmony_ci * can write on the final table).
251462306a36Sopenharmony_ci *
251562306a36Sopenharmony_ci * We write y values (PCDAC/PDADC steps) on hw.
251662306a36Sopenharmony_ci */
251762306a36Sopenharmony_cistatic void
251862306a36Sopenharmony_ciath5k_create_power_curve(s16 pmin, s16 pmax,
251962306a36Sopenharmony_ci			const s16 *pwr, const u8 *vpd,
252062306a36Sopenharmony_ci			u8 num_points,
252162306a36Sopenharmony_ci			u8 *vpd_table, u8 type)
252262306a36Sopenharmony_ci{
252362306a36Sopenharmony_ci	u8 idx[2] = { 0, 1 };
252462306a36Sopenharmony_ci	s16 pwr_i = 2 * pmin;
252562306a36Sopenharmony_ci	int i;
252662306a36Sopenharmony_ci
252762306a36Sopenharmony_ci	if (num_points < 2)
252862306a36Sopenharmony_ci		return;
252962306a36Sopenharmony_ci
253062306a36Sopenharmony_ci	/* We want the whole line, so adjust boundaries
253162306a36Sopenharmony_ci	 * to cover the entire power range. Note that
253262306a36Sopenharmony_ci	 * power values are already 0.25dB so no need
253362306a36Sopenharmony_ci	 * to multiply pwr_i by 2 */
253462306a36Sopenharmony_ci	if (type == AR5K_PWRTABLE_LINEAR_PCDAC) {
253562306a36Sopenharmony_ci		pwr_i = pmin;
253662306a36Sopenharmony_ci		pmin = 0;
253762306a36Sopenharmony_ci		pmax = 63;
253862306a36Sopenharmony_ci	}
253962306a36Sopenharmony_ci
254062306a36Sopenharmony_ci	/* Find surrounding turning points (TPs)
254162306a36Sopenharmony_ci	 * and interpolate between them */
254262306a36Sopenharmony_ci	for (i = 0; (i <= (u16) (pmax - pmin)) &&
254362306a36Sopenharmony_ci	(i < AR5K_EEPROM_POWER_TABLE_SIZE); i++) {
254462306a36Sopenharmony_ci
254562306a36Sopenharmony_ci		/* We passed the right TP, move to the next set of TPs
254662306a36Sopenharmony_ci		 * if we pass the last TP, extrapolate above using the last
254762306a36Sopenharmony_ci		 * two TPs for ratio */
254862306a36Sopenharmony_ci		if ((pwr_i > pwr[idx[1]]) && (idx[1] < num_points - 1)) {
254962306a36Sopenharmony_ci			idx[0]++;
255062306a36Sopenharmony_ci			idx[1]++;
255162306a36Sopenharmony_ci		}
255262306a36Sopenharmony_ci
255362306a36Sopenharmony_ci		vpd_table[i] = (u8) ath5k_get_interpolated_value(pwr_i,
255462306a36Sopenharmony_ci						pwr[idx[0]], pwr[idx[1]],
255562306a36Sopenharmony_ci						vpd[idx[0]], vpd[idx[1]]);
255662306a36Sopenharmony_ci
255762306a36Sopenharmony_ci		/* Increase by 0.5dB
255862306a36Sopenharmony_ci		 * (0.25 dB units) */
255962306a36Sopenharmony_ci		pwr_i += 2;
256062306a36Sopenharmony_ci	}
256162306a36Sopenharmony_ci}
256262306a36Sopenharmony_ci
256362306a36Sopenharmony_ci/**
256462306a36Sopenharmony_ci * ath5k_get_chan_pcal_surrounding_piers() - Get surrounding calibration piers
256562306a36Sopenharmony_ci * for a given channel.
256662306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
256762306a36Sopenharmony_ci * @channel: The &struct ieee80211_channel
256862306a36Sopenharmony_ci * @pcinfo_l: The &struct ath5k_chan_pcal_info to put the left cal. pier
256962306a36Sopenharmony_ci * @pcinfo_r: The &struct ath5k_chan_pcal_info to put the right cal. pier
257062306a36Sopenharmony_ci *
257162306a36Sopenharmony_ci * Get the surrounding per-channel power calibration piers
257262306a36Sopenharmony_ci * for a given frequency so that we can interpolate between
257362306a36Sopenharmony_ci * them and come up with an appropriate dataset for our current
257462306a36Sopenharmony_ci * channel.
257562306a36Sopenharmony_ci */
257662306a36Sopenharmony_cistatic void
257762306a36Sopenharmony_ciath5k_get_chan_pcal_surrounding_piers(struct ath5k_hw *ah,
257862306a36Sopenharmony_ci			struct ieee80211_channel *channel,
257962306a36Sopenharmony_ci			struct ath5k_chan_pcal_info **pcinfo_l,
258062306a36Sopenharmony_ci			struct ath5k_chan_pcal_info **pcinfo_r)
258162306a36Sopenharmony_ci{
258262306a36Sopenharmony_ci	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
258362306a36Sopenharmony_ci	struct ath5k_chan_pcal_info *pcinfo;
258462306a36Sopenharmony_ci	u8 idx_l, idx_r;
258562306a36Sopenharmony_ci	u8 mode, max, i;
258662306a36Sopenharmony_ci	u32 target = channel->center_freq;
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_ci	idx_l = 0;
258962306a36Sopenharmony_ci	idx_r = 0;
259062306a36Sopenharmony_ci
259162306a36Sopenharmony_ci	switch (channel->hw_value) {
259262306a36Sopenharmony_ci	case AR5K_EEPROM_MODE_11A:
259362306a36Sopenharmony_ci		pcinfo = ee->ee_pwr_cal_a;
259462306a36Sopenharmony_ci		mode = AR5K_EEPROM_MODE_11A;
259562306a36Sopenharmony_ci		break;
259662306a36Sopenharmony_ci	case AR5K_EEPROM_MODE_11B:
259762306a36Sopenharmony_ci		pcinfo = ee->ee_pwr_cal_b;
259862306a36Sopenharmony_ci		mode = AR5K_EEPROM_MODE_11B;
259962306a36Sopenharmony_ci		break;
260062306a36Sopenharmony_ci	case AR5K_EEPROM_MODE_11G:
260162306a36Sopenharmony_ci	default:
260262306a36Sopenharmony_ci		pcinfo = ee->ee_pwr_cal_g;
260362306a36Sopenharmony_ci		mode = AR5K_EEPROM_MODE_11G;
260462306a36Sopenharmony_ci		break;
260562306a36Sopenharmony_ci	}
260662306a36Sopenharmony_ci	max = ee->ee_n_piers[mode] - 1;
260762306a36Sopenharmony_ci
260862306a36Sopenharmony_ci	/* Frequency is below our calibrated
260962306a36Sopenharmony_ci	 * range. Use the lowest power curve
261062306a36Sopenharmony_ci	 * we have */
261162306a36Sopenharmony_ci	if (target < pcinfo[0].freq) {
261262306a36Sopenharmony_ci		idx_l = idx_r = 0;
261362306a36Sopenharmony_ci		goto done;
261462306a36Sopenharmony_ci	}
261562306a36Sopenharmony_ci
261662306a36Sopenharmony_ci	/* Frequency is above our calibrated
261762306a36Sopenharmony_ci	 * range. Use the highest power curve
261862306a36Sopenharmony_ci	 * we have */
261962306a36Sopenharmony_ci	if (target > pcinfo[max].freq) {
262062306a36Sopenharmony_ci		idx_l = idx_r = max;
262162306a36Sopenharmony_ci		goto done;
262262306a36Sopenharmony_ci	}
262362306a36Sopenharmony_ci
262462306a36Sopenharmony_ci	/* Frequency is inside our calibrated
262562306a36Sopenharmony_ci	 * channel range. Pick the surrounding
262662306a36Sopenharmony_ci	 * calibration piers so that we can
262762306a36Sopenharmony_ci	 * interpolate */
262862306a36Sopenharmony_ci	for (i = 0; i <= max; i++) {
262962306a36Sopenharmony_ci
263062306a36Sopenharmony_ci		/* Frequency matches one of our calibration
263162306a36Sopenharmony_ci		 * piers, no need to interpolate, just use
263262306a36Sopenharmony_ci		 * that calibration pier */
263362306a36Sopenharmony_ci		if (pcinfo[i].freq == target) {
263462306a36Sopenharmony_ci			idx_l = idx_r = i;
263562306a36Sopenharmony_ci			goto done;
263662306a36Sopenharmony_ci		}
263762306a36Sopenharmony_ci
263862306a36Sopenharmony_ci		/* We found a calibration pier that's above
263962306a36Sopenharmony_ci		 * frequency, use this pier and the previous
264062306a36Sopenharmony_ci		 * one to interpolate */
264162306a36Sopenharmony_ci		if (target < pcinfo[i].freq) {
264262306a36Sopenharmony_ci			idx_r = i;
264362306a36Sopenharmony_ci			idx_l = idx_r - 1;
264462306a36Sopenharmony_ci			goto done;
264562306a36Sopenharmony_ci		}
264662306a36Sopenharmony_ci	}
264762306a36Sopenharmony_ci
264862306a36Sopenharmony_cidone:
264962306a36Sopenharmony_ci	*pcinfo_l = &pcinfo[idx_l];
265062306a36Sopenharmony_ci	*pcinfo_r = &pcinfo[idx_r];
265162306a36Sopenharmony_ci}
265262306a36Sopenharmony_ci
265362306a36Sopenharmony_ci/**
265462306a36Sopenharmony_ci * ath5k_get_rate_pcal_data() - Get the interpolated per-rate power
265562306a36Sopenharmony_ci * calibration data
265662306a36Sopenharmony_ci * @ah: The &struct ath5k_hw *ah,
265762306a36Sopenharmony_ci * @channel: The &struct ieee80211_channel
265862306a36Sopenharmony_ci * @rates: The &struct ath5k_rate_pcal_info to fill
265962306a36Sopenharmony_ci *
266062306a36Sopenharmony_ci * Get the surrounding per-rate power calibration data
266162306a36Sopenharmony_ci * for a given frequency and interpolate between power
266262306a36Sopenharmony_ci * values to set max target power supported by hw for
266362306a36Sopenharmony_ci * each rate on this frequency.
266462306a36Sopenharmony_ci */
266562306a36Sopenharmony_cistatic void
266662306a36Sopenharmony_ciath5k_get_rate_pcal_data(struct ath5k_hw *ah,
266762306a36Sopenharmony_ci			struct ieee80211_channel *channel,
266862306a36Sopenharmony_ci			struct ath5k_rate_pcal_info *rates)
266962306a36Sopenharmony_ci{
267062306a36Sopenharmony_ci	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
267162306a36Sopenharmony_ci	struct ath5k_rate_pcal_info *rpinfo;
267262306a36Sopenharmony_ci	u8 idx_l, idx_r;
267362306a36Sopenharmony_ci	u8 mode, max, i;
267462306a36Sopenharmony_ci	u32 target = channel->center_freq;
267562306a36Sopenharmony_ci
267662306a36Sopenharmony_ci	idx_l = 0;
267762306a36Sopenharmony_ci	idx_r = 0;
267862306a36Sopenharmony_ci
267962306a36Sopenharmony_ci	switch (channel->hw_value) {
268062306a36Sopenharmony_ci	case AR5K_MODE_11A:
268162306a36Sopenharmony_ci		rpinfo = ee->ee_rate_tpwr_a;
268262306a36Sopenharmony_ci		mode = AR5K_EEPROM_MODE_11A;
268362306a36Sopenharmony_ci		break;
268462306a36Sopenharmony_ci	case AR5K_MODE_11B:
268562306a36Sopenharmony_ci		rpinfo = ee->ee_rate_tpwr_b;
268662306a36Sopenharmony_ci		mode = AR5K_EEPROM_MODE_11B;
268762306a36Sopenharmony_ci		break;
268862306a36Sopenharmony_ci	case AR5K_MODE_11G:
268962306a36Sopenharmony_ci	default:
269062306a36Sopenharmony_ci		rpinfo = ee->ee_rate_tpwr_g;
269162306a36Sopenharmony_ci		mode = AR5K_EEPROM_MODE_11G;
269262306a36Sopenharmony_ci		break;
269362306a36Sopenharmony_ci	}
269462306a36Sopenharmony_ci	max = ee->ee_rate_target_pwr_num[mode] - 1;
269562306a36Sopenharmony_ci
269662306a36Sopenharmony_ci	/* Get the surrounding calibration
269762306a36Sopenharmony_ci	 * piers - same as above */
269862306a36Sopenharmony_ci	if (target < rpinfo[0].freq) {
269962306a36Sopenharmony_ci		idx_l = idx_r = 0;
270062306a36Sopenharmony_ci		goto done;
270162306a36Sopenharmony_ci	}
270262306a36Sopenharmony_ci
270362306a36Sopenharmony_ci	if (target > rpinfo[max].freq) {
270462306a36Sopenharmony_ci		idx_l = idx_r = max;
270562306a36Sopenharmony_ci		goto done;
270662306a36Sopenharmony_ci	}
270762306a36Sopenharmony_ci
270862306a36Sopenharmony_ci	for (i = 0; i <= max; i++) {
270962306a36Sopenharmony_ci
271062306a36Sopenharmony_ci		if (rpinfo[i].freq == target) {
271162306a36Sopenharmony_ci			idx_l = idx_r = i;
271262306a36Sopenharmony_ci			goto done;
271362306a36Sopenharmony_ci		}
271462306a36Sopenharmony_ci
271562306a36Sopenharmony_ci		if (target < rpinfo[i].freq) {
271662306a36Sopenharmony_ci			idx_r = i;
271762306a36Sopenharmony_ci			idx_l = idx_r - 1;
271862306a36Sopenharmony_ci			goto done;
271962306a36Sopenharmony_ci		}
272062306a36Sopenharmony_ci	}
272162306a36Sopenharmony_ci
272262306a36Sopenharmony_cidone:
272362306a36Sopenharmony_ci	/* Now interpolate power value, based on the frequency */
272462306a36Sopenharmony_ci	rates->freq = target;
272562306a36Sopenharmony_ci
272662306a36Sopenharmony_ci	rates->target_power_6to24 =
272762306a36Sopenharmony_ci		ath5k_get_interpolated_value(target, rpinfo[idx_l].freq,
272862306a36Sopenharmony_ci					rpinfo[idx_r].freq,
272962306a36Sopenharmony_ci					rpinfo[idx_l].target_power_6to24,
273062306a36Sopenharmony_ci					rpinfo[idx_r].target_power_6to24);
273162306a36Sopenharmony_ci
273262306a36Sopenharmony_ci	rates->target_power_36 =
273362306a36Sopenharmony_ci		ath5k_get_interpolated_value(target, rpinfo[idx_l].freq,
273462306a36Sopenharmony_ci					rpinfo[idx_r].freq,
273562306a36Sopenharmony_ci					rpinfo[idx_l].target_power_36,
273662306a36Sopenharmony_ci					rpinfo[idx_r].target_power_36);
273762306a36Sopenharmony_ci
273862306a36Sopenharmony_ci	rates->target_power_48 =
273962306a36Sopenharmony_ci		ath5k_get_interpolated_value(target, rpinfo[idx_l].freq,
274062306a36Sopenharmony_ci					rpinfo[idx_r].freq,
274162306a36Sopenharmony_ci					rpinfo[idx_l].target_power_48,
274262306a36Sopenharmony_ci					rpinfo[idx_r].target_power_48);
274362306a36Sopenharmony_ci
274462306a36Sopenharmony_ci	rates->target_power_54 =
274562306a36Sopenharmony_ci		ath5k_get_interpolated_value(target, rpinfo[idx_l].freq,
274662306a36Sopenharmony_ci					rpinfo[idx_r].freq,
274762306a36Sopenharmony_ci					rpinfo[idx_l].target_power_54,
274862306a36Sopenharmony_ci					rpinfo[idx_r].target_power_54);
274962306a36Sopenharmony_ci}
275062306a36Sopenharmony_ci
275162306a36Sopenharmony_ci/**
275262306a36Sopenharmony_ci * ath5k_get_max_ctl_power() - Get max edge power for a given frequency
275362306a36Sopenharmony_ci * @ah: the &struct ath5k_hw
275462306a36Sopenharmony_ci * @channel: The &struct ieee80211_channel
275562306a36Sopenharmony_ci *
275662306a36Sopenharmony_ci * Get the max edge power for this channel if
275762306a36Sopenharmony_ci * we have such data from EEPROM's Conformance Test
275862306a36Sopenharmony_ci * Limits (CTL), and limit max power if needed.
275962306a36Sopenharmony_ci */
276062306a36Sopenharmony_cistatic void
276162306a36Sopenharmony_ciath5k_get_max_ctl_power(struct ath5k_hw *ah,
276262306a36Sopenharmony_ci			struct ieee80211_channel *channel)
276362306a36Sopenharmony_ci{
276462306a36Sopenharmony_ci	struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah);
276562306a36Sopenharmony_ci	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
276662306a36Sopenharmony_ci	struct ath5k_edge_power *rep = ee->ee_ctl_pwr;
276762306a36Sopenharmony_ci	u8 *ctl_val = ee->ee_ctl;
276862306a36Sopenharmony_ci	s16 max_chan_pwr = ah->ah_txpower.txp_max_pwr / 4;
276962306a36Sopenharmony_ci	s16 edge_pwr = 0;
277062306a36Sopenharmony_ci	u8 rep_idx;
277162306a36Sopenharmony_ci	u8 i, ctl_mode;
277262306a36Sopenharmony_ci	u8 ctl_idx = 0xFF;
277362306a36Sopenharmony_ci	u32 target = channel->center_freq;
277462306a36Sopenharmony_ci
277562306a36Sopenharmony_ci	ctl_mode = ath_regd_get_band_ctl(regulatory, channel->band);
277662306a36Sopenharmony_ci
277762306a36Sopenharmony_ci	switch (channel->hw_value) {
277862306a36Sopenharmony_ci	case AR5K_MODE_11A:
277962306a36Sopenharmony_ci		if (ah->ah_bwmode == AR5K_BWMODE_40MHZ)
278062306a36Sopenharmony_ci			ctl_mode |= AR5K_CTL_TURBO;
278162306a36Sopenharmony_ci		else
278262306a36Sopenharmony_ci			ctl_mode |= AR5K_CTL_11A;
278362306a36Sopenharmony_ci		break;
278462306a36Sopenharmony_ci	case AR5K_MODE_11G:
278562306a36Sopenharmony_ci		if (ah->ah_bwmode == AR5K_BWMODE_40MHZ)
278662306a36Sopenharmony_ci			ctl_mode |= AR5K_CTL_TURBOG;
278762306a36Sopenharmony_ci		else
278862306a36Sopenharmony_ci			ctl_mode |= AR5K_CTL_11G;
278962306a36Sopenharmony_ci		break;
279062306a36Sopenharmony_ci	case AR5K_MODE_11B:
279162306a36Sopenharmony_ci		ctl_mode |= AR5K_CTL_11B;
279262306a36Sopenharmony_ci		break;
279362306a36Sopenharmony_ci	default:
279462306a36Sopenharmony_ci		return;
279562306a36Sopenharmony_ci	}
279662306a36Sopenharmony_ci
279762306a36Sopenharmony_ci	for (i = 0; i < ee->ee_ctls; i++) {
279862306a36Sopenharmony_ci		if (ctl_val[i] == ctl_mode) {
279962306a36Sopenharmony_ci			ctl_idx = i;
280062306a36Sopenharmony_ci			break;
280162306a36Sopenharmony_ci		}
280262306a36Sopenharmony_ci	}
280362306a36Sopenharmony_ci
280462306a36Sopenharmony_ci	/* If we have a CTL dataset available grab it and find the
280562306a36Sopenharmony_ci	 * edge power for our frequency */
280662306a36Sopenharmony_ci	if (ctl_idx == 0xFF)
280762306a36Sopenharmony_ci		return;
280862306a36Sopenharmony_ci
280962306a36Sopenharmony_ci	/* Edge powers are sorted by frequency from lower
281062306a36Sopenharmony_ci	 * to higher. Each CTL corresponds to 8 edge power
281162306a36Sopenharmony_ci	 * measurements. */
281262306a36Sopenharmony_ci	rep_idx = ctl_idx * AR5K_EEPROM_N_EDGES;
281362306a36Sopenharmony_ci
281462306a36Sopenharmony_ci	/* Don't do boundaries check because we
281562306a36Sopenharmony_ci	 * might have more that one bands defined
281662306a36Sopenharmony_ci	 * for this mode */
281762306a36Sopenharmony_ci
281862306a36Sopenharmony_ci	/* Get the edge power that's closer to our
281962306a36Sopenharmony_ci	 * frequency */
282062306a36Sopenharmony_ci	for (i = 0; i < AR5K_EEPROM_N_EDGES; i++) {
282162306a36Sopenharmony_ci		rep_idx += i;
282262306a36Sopenharmony_ci		if (target <= rep[rep_idx].freq)
282362306a36Sopenharmony_ci			edge_pwr = (s16) rep[rep_idx].edge;
282462306a36Sopenharmony_ci	}
282562306a36Sopenharmony_ci
282662306a36Sopenharmony_ci	if (edge_pwr)
282762306a36Sopenharmony_ci		ah->ah_txpower.txp_max_pwr = 4 * min(edge_pwr, max_chan_pwr);
282862306a36Sopenharmony_ci}
282962306a36Sopenharmony_ci
283062306a36Sopenharmony_ci
283162306a36Sopenharmony_ci/*
283262306a36Sopenharmony_ci * Power to PCDAC table functions
283362306a36Sopenharmony_ci */
283462306a36Sopenharmony_ci
283562306a36Sopenharmony_ci/**
283662306a36Sopenharmony_ci * DOC: Power to PCDAC table functions
283762306a36Sopenharmony_ci *
283862306a36Sopenharmony_ci * For RF5111 we have an XPD -eXternal Power Detector- curve
283962306a36Sopenharmony_ci * for each calibrated channel. Each curve has 0,5dB Power steps
284062306a36Sopenharmony_ci * on x axis and PCDAC steps (offsets) on y axis and looks like an
284162306a36Sopenharmony_ci * exponential function. To recreate the curve we read 11 points
284262306a36Sopenharmony_ci * from eeprom (eeprom.c) and interpolate here.
284362306a36Sopenharmony_ci *
284462306a36Sopenharmony_ci * For RF5112 we have 4 XPD -eXternal Power Detector- curves
284562306a36Sopenharmony_ci * for each calibrated channel on 0, -6, -12 and -18dBm but we only
284662306a36Sopenharmony_ci * use the higher (3) and the lower (0) curves. Each curve again has 0.5dB
284762306a36Sopenharmony_ci * power steps on x axis and PCDAC steps on y axis and looks like a
284862306a36Sopenharmony_ci * linear function. To recreate the curve and pass the power values
284962306a36Sopenharmony_ci * on hw, we get 4 points for xpd 0 (lower gain -> max power)
285062306a36Sopenharmony_ci * and 3 points for xpd 3 (higher gain -> lower power) from eeprom (eeprom.c)
285162306a36Sopenharmony_ci * and interpolate here.
285262306a36Sopenharmony_ci *
285362306a36Sopenharmony_ci * For a given channel we get the calibrated points (piers) for it or
285462306a36Sopenharmony_ci * -if we don't have calibration data for this specific channel- from the
285562306a36Sopenharmony_ci * available surrounding channels we have calibration data for, after we do a
285662306a36Sopenharmony_ci * linear interpolation between them. Then since we have our calibrated points
285762306a36Sopenharmony_ci * for this channel, we do again a linear interpolation between them to get the
285862306a36Sopenharmony_ci * whole curve.
285962306a36Sopenharmony_ci *
286062306a36Sopenharmony_ci * We finally write the Y values of the curve(s) (the PCDAC values) on hw
286162306a36Sopenharmony_ci */
286262306a36Sopenharmony_ci
286362306a36Sopenharmony_ci/**
286462306a36Sopenharmony_ci * ath5k_fill_pwr_to_pcdac_table() - Fill Power to PCDAC table on RF5111
286562306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
286662306a36Sopenharmony_ci * @table_min: Minimum power (x min)
286762306a36Sopenharmony_ci * @table_max: Maximum power (x max)
286862306a36Sopenharmony_ci *
286962306a36Sopenharmony_ci * No further processing is needed for RF5111, the only thing we have to
287062306a36Sopenharmony_ci * do is fill the values below and above calibration range since eeprom data
287162306a36Sopenharmony_ci * may not cover the entire PCDAC table.
287262306a36Sopenharmony_ci */
287362306a36Sopenharmony_cistatic void
287462306a36Sopenharmony_ciath5k_fill_pwr_to_pcdac_table(struct ath5k_hw *ah, s16* table_min,
287562306a36Sopenharmony_ci							s16 *table_max)
287662306a36Sopenharmony_ci{
287762306a36Sopenharmony_ci	u8	*pcdac_out = ah->ah_txpower.txp_pd_table;
287862306a36Sopenharmony_ci	u8	*pcdac_tmp = ah->ah_txpower.tmpL[0];
287962306a36Sopenharmony_ci	u8	pcdac_0, pcdac_n, pcdac_i, pwr_idx, i;
288062306a36Sopenharmony_ci	s16	min_pwr, max_pwr;
288162306a36Sopenharmony_ci
288262306a36Sopenharmony_ci	/* Get table boundaries */
288362306a36Sopenharmony_ci	min_pwr = table_min[0];
288462306a36Sopenharmony_ci	pcdac_0 = pcdac_tmp[0];
288562306a36Sopenharmony_ci
288662306a36Sopenharmony_ci	max_pwr = table_max[0];
288762306a36Sopenharmony_ci	pcdac_n = pcdac_tmp[table_max[0] - table_min[0]];
288862306a36Sopenharmony_ci
288962306a36Sopenharmony_ci	/* Extrapolate below minimum using pcdac_0 */
289062306a36Sopenharmony_ci	pcdac_i = 0;
289162306a36Sopenharmony_ci	for (i = 0; i < min_pwr; i++)
289262306a36Sopenharmony_ci		pcdac_out[pcdac_i++] = pcdac_0;
289362306a36Sopenharmony_ci
289462306a36Sopenharmony_ci	/* Copy values from pcdac_tmp */
289562306a36Sopenharmony_ci	pwr_idx = min_pwr;
289662306a36Sopenharmony_ci	for (i = 0; pwr_idx <= max_pwr &&
289762306a36Sopenharmony_ci		    pcdac_i < AR5K_EEPROM_POWER_TABLE_SIZE; i++) {
289862306a36Sopenharmony_ci		pcdac_out[pcdac_i++] = pcdac_tmp[i];
289962306a36Sopenharmony_ci		pwr_idx++;
290062306a36Sopenharmony_ci	}
290162306a36Sopenharmony_ci
290262306a36Sopenharmony_ci	/* Extrapolate above maximum */
290362306a36Sopenharmony_ci	while (pcdac_i < AR5K_EEPROM_POWER_TABLE_SIZE)
290462306a36Sopenharmony_ci		pcdac_out[pcdac_i++] = pcdac_n;
290562306a36Sopenharmony_ci
290662306a36Sopenharmony_ci}
290762306a36Sopenharmony_ci
290862306a36Sopenharmony_ci/**
290962306a36Sopenharmony_ci * ath5k_combine_linear_pcdac_curves() - Combine available PCDAC Curves
291062306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
291162306a36Sopenharmony_ci * @table_min: Minimum power (x min)
291262306a36Sopenharmony_ci * @table_max: Maximum power (x max)
291362306a36Sopenharmony_ci * @pdcurves: Number of pd curves
291462306a36Sopenharmony_ci *
291562306a36Sopenharmony_ci * Combine available XPD Curves and fill Linear Power to PCDAC table on RF5112
291662306a36Sopenharmony_ci * RFX112 can have up to 2 curves (one for low txpower range and one for
291762306a36Sopenharmony_ci * higher txpower range). We need to put them both on pcdac_out and place
291862306a36Sopenharmony_ci * them in the correct location. In case we only have one curve available
291962306a36Sopenharmony_ci * just fit it on pcdac_out (it's supposed to cover the entire range of
292062306a36Sopenharmony_ci * available pwr levels since it's always the higher power curve). Extrapolate
292162306a36Sopenharmony_ci * below and above final table if needed.
292262306a36Sopenharmony_ci */
292362306a36Sopenharmony_cistatic void
292462306a36Sopenharmony_ciath5k_combine_linear_pcdac_curves(struct ath5k_hw *ah, s16* table_min,
292562306a36Sopenharmony_ci						s16 *table_max, u8 pdcurves)
292662306a36Sopenharmony_ci{
292762306a36Sopenharmony_ci	u8	*pcdac_out = ah->ah_txpower.txp_pd_table;
292862306a36Sopenharmony_ci	u8	*pcdac_low_pwr;
292962306a36Sopenharmony_ci	u8	*pcdac_high_pwr;
293062306a36Sopenharmony_ci	u8	*pcdac_tmp;
293162306a36Sopenharmony_ci	u8	pwr;
293262306a36Sopenharmony_ci	s16	max_pwr_idx;
293362306a36Sopenharmony_ci	s16	min_pwr_idx;
293462306a36Sopenharmony_ci	s16	mid_pwr_idx = 0;
293562306a36Sopenharmony_ci	/* Edge flag turns on the 7nth bit on the PCDAC
293662306a36Sopenharmony_ci	 * to declare the higher power curve (force values
293762306a36Sopenharmony_ci	 * to be greater than 64). If we only have one curve
293862306a36Sopenharmony_ci	 * we don't need to set this, if we have 2 curves and
293962306a36Sopenharmony_ci	 * fill the table backwards this can also be used to
294062306a36Sopenharmony_ci	 * switch from higher power curve to lower power curve */
294162306a36Sopenharmony_ci	u8	edge_flag;
294262306a36Sopenharmony_ci	int	i;
294362306a36Sopenharmony_ci
294462306a36Sopenharmony_ci	/* When we have only one curve available
294562306a36Sopenharmony_ci	 * that's the higher power curve. If we have
294662306a36Sopenharmony_ci	 * two curves the first is the high power curve
294762306a36Sopenharmony_ci	 * and the next is the low power curve. */
294862306a36Sopenharmony_ci	if (pdcurves > 1) {
294962306a36Sopenharmony_ci		pcdac_low_pwr = ah->ah_txpower.tmpL[1];
295062306a36Sopenharmony_ci		pcdac_high_pwr = ah->ah_txpower.tmpL[0];
295162306a36Sopenharmony_ci		mid_pwr_idx = table_max[1] - table_min[1] - 1;
295262306a36Sopenharmony_ci		max_pwr_idx = (table_max[0] - table_min[0]) / 2;
295362306a36Sopenharmony_ci
295462306a36Sopenharmony_ci		/* If table size goes beyond 31.5dB, keep the
295562306a36Sopenharmony_ci		 * upper 31.5dB range when setting tx power.
295662306a36Sopenharmony_ci		 * Note: 126 = 31.5 dB in quarter dB steps */
295762306a36Sopenharmony_ci		if (table_max[0] - table_min[1] > 126)
295862306a36Sopenharmony_ci			min_pwr_idx = table_max[0] - 126;
295962306a36Sopenharmony_ci		else
296062306a36Sopenharmony_ci			min_pwr_idx = table_min[1];
296162306a36Sopenharmony_ci
296262306a36Sopenharmony_ci		/* Since we fill table backwards
296362306a36Sopenharmony_ci		 * start from high power curve */
296462306a36Sopenharmony_ci		pcdac_tmp = pcdac_high_pwr;
296562306a36Sopenharmony_ci
296662306a36Sopenharmony_ci		edge_flag = 0x40;
296762306a36Sopenharmony_ci	} else {
296862306a36Sopenharmony_ci		pcdac_low_pwr = ah->ah_txpower.tmpL[1]; /* Zeroed */
296962306a36Sopenharmony_ci		pcdac_high_pwr = ah->ah_txpower.tmpL[0];
297062306a36Sopenharmony_ci		min_pwr_idx = table_min[0];
297162306a36Sopenharmony_ci		max_pwr_idx = (table_max[0] - table_min[0]) / 2;
297262306a36Sopenharmony_ci		pcdac_tmp = pcdac_high_pwr;
297362306a36Sopenharmony_ci		edge_flag = 0;
297462306a36Sopenharmony_ci	}
297562306a36Sopenharmony_ci
297662306a36Sopenharmony_ci	/* This is used when setting tx power*/
297762306a36Sopenharmony_ci	ah->ah_txpower.txp_min_idx = min_pwr_idx / 2;
297862306a36Sopenharmony_ci
297962306a36Sopenharmony_ci	/* Fill Power to PCDAC table backwards */
298062306a36Sopenharmony_ci	pwr = max_pwr_idx;
298162306a36Sopenharmony_ci	for (i = 63; i >= 0; i--) {
298262306a36Sopenharmony_ci		/* Entering lower power range, reset
298362306a36Sopenharmony_ci		 * edge flag and set pcdac_tmp to lower
298462306a36Sopenharmony_ci		 * power curve.*/
298562306a36Sopenharmony_ci		if (edge_flag == 0x40 &&
298662306a36Sopenharmony_ci		(2 * pwr <= (table_max[1] - table_min[0]) || pwr == 0)) {
298762306a36Sopenharmony_ci			edge_flag = 0x00;
298862306a36Sopenharmony_ci			pcdac_tmp = pcdac_low_pwr;
298962306a36Sopenharmony_ci			pwr = mid_pwr_idx / 2;
299062306a36Sopenharmony_ci		}
299162306a36Sopenharmony_ci
299262306a36Sopenharmony_ci		/* Don't go below 1, extrapolate below if we have
299362306a36Sopenharmony_ci		 * already switched to the lower power curve -or
299462306a36Sopenharmony_ci		 * we only have one curve and edge_flag is zero
299562306a36Sopenharmony_ci		 * anyway */
299662306a36Sopenharmony_ci		if (pcdac_tmp[pwr] < 1 && (edge_flag == 0x00)) {
299762306a36Sopenharmony_ci			while (i >= 0) {
299862306a36Sopenharmony_ci				pcdac_out[i] = pcdac_out[i + 1];
299962306a36Sopenharmony_ci				i--;
300062306a36Sopenharmony_ci			}
300162306a36Sopenharmony_ci			break;
300262306a36Sopenharmony_ci		}
300362306a36Sopenharmony_ci
300462306a36Sopenharmony_ci		pcdac_out[i] = pcdac_tmp[pwr] | edge_flag;
300562306a36Sopenharmony_ci
300662306a36Sopenharmony_ci		/* Extrapolate above if pcdac is greater than
300762306a36Sopenharmony_ci		 * 126 -this can happen because we OR pcdac_out
300862306a36Sopenharmony_ci		 * value with edge_flag on high power curve */
300962306a36Sopenharmony_ci		if (pcdac_out[i] > 126)
301062306a36Sopenharmony_ci			pcdac_out[i] = 126;
301162306a36Sopenharmony_ci
301262306a36Sopenharmony_ci		/* Decrease by a 0.5dB step */
301362306a36Sopenharmony_ci		pwr--;
301462306a36Sopenharmony_ci	}
301562306a36Sopenharmony_ci}
301662306a36Sopenharmony_ci
301762306a36Sopenharmony_ci/**
301862306a36Sopenharmony_ci * ath5k_write_pcdac_table() - Write the PCDAC values on hw
301962306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
302062306a36Sopenharmony_ci */
302162306a36Sopenharmony_cistatic void
302262306a36Sopenharmony_ciath5k_write_pcdac_table(struct ath5k_hw *ah)
302362306a36Sopenharmony_ci{
302462306a36Sopenharmony_ci	u8	*pcdac_out = ah->ah_txpower.txp_pd_table;
302562306a36Sopenharmony_ci	int	i;
302662306a36Sopenharmony_ci
302762306a36Sopenharmony_ci	/*
302862306a36Sopenharmony_ci	 * Write TX power values
302962306a36Sopenharmony_ci	 */
303062306a36Sopenharmony_ci	for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) {
303162306a36Sopenharmony_ci		ath5k_hw_reg_write(ah,
303262306a36Sopenharmony_ci			(((pcdac_out[2 * i + 0] << 8 | 0xff) & 0xffff) << 0) |
303362306a36Sopenharmony_ci			(((pcdac_out[2 * i + 1] << 8 | 0xff) & 0xffff) << 16),
303462306a36Sopenharmony_ci			AR5K_PHY_PCDAC_TXPOWER(i));
303562306a36Sopenharmony_ci	}
303662306a36Sopenharmony_ci}
303762306a36Sopenharmony_ci
303862306a36Sopenharmony_ci
303962306a36Sopenharmony_ci/*
304062306a36Sopenharmony_ci * Power to PDADC table functions
304162306a36Sopenharmony_ci */
304262306a36Sopenharmony_ci
304362306a36Sopenharmony_ci/**
304462306a36Sopenharmony_ci * DOC: Power to PDADC table functions
304562306a36Sopenharmony_ci *
304662306a36Sopenharmony_ci * For RF2413 and later we have a Power to PDADC table (Power Detector)
304762306a36Sopenharmony_ci * instead of a PCDAC (Power Control) and 4 pd gain curves for each
304862306a36Sopenharmony_ci * calibrated channel. Each curve has power on x axis in 0.5 db steps and
304962306a36Sopenharmony_ci * PDADC steps on y axis and looks like an exponential function like the
305062306a36Sopenharmony_ci * RF5111 curve.
305162306a36Sopenharmony_ci *
305262306a36Sopenharmony_ci * To recreate the curves we read the points from eeprom (eeprom.c)
305362306a36Sopenharmony_ci * and interpolate here. Note that in most cases only 2 (higher and lower)
305462306a36Sopenharmony_ci * curves are used (like RF5112) but vendors have the opportunity to include
305562306a36Sopenharmony_ci * all 4 curves on eeprom. The final curve (higher power) has an extra
305662306a36Sopenharmony_ci * point for better accuracy like RF5112.
305762306a36Sopenharmony_ci *
305862306a36Sopenharmony_ci * The process is similar to what we do above for RF5111/5112
305962306a36Sopenharmony_ci */
306062306a36Sopenharmony_ci
306162306a36Sopenharmony_ci/**
306262306a36Sopenharmony_ci * ath5k_combine_pwr_to_pdadc_curves() - Combine the various PDADC curves
306362306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
306462306a36Sopenharmony_ci * @pwr_min: Minimum power (x min)
306562306a36Sopenharmony_ci * @pwr_max: Maximum power (x max)
306662306a36Sopenharmony_ci * @pdcurves: Number of available curves
306762306a36Sopenharmony_ci *
306862306a36Sopenharmony_ci * Combine the various pd curves and create the final Power to PDADC table
306962306a36Sopenharmony_ci * We can have up to 4 pd curves, we need to do a similar process
307062306a36Sopenharmony_ci * as we do for RF5112. This time we don't have an edge_flag but we
307162306a36Sopenharmony_ci * set the gain boundaries on a separate register.
307262306a36Sopenharmony_ci */
307362306a36Sopenharmony_cistatic void
307462306a36Sopenharmony_ciath5k_combine_pwr_to_pdadc_curves(struct ath5k_hw *ah,
307562306a36Sopenharmony_ci			s16 *pwr_min, s16 *pwr_max, u8 pdcurves)
307662306a36Sopenharmony_ci{
307762306a36Sopenharmony_ci	u8 gain_boundaries[AR5K_EEPROM_N_PD_GAINS];
307862306a36Sopenharmony_ci	u8 *pdadc_out = ah->ah_txpower.txp_pd_table;
307962306a36Sopenharmony_ci	u8 *pdadc_tmp;
308062306a36Sopenharmony_ci	s16 pdadc_0;
308162306a36Sopenharmony_ci	u8 pdadc_i, pdadc_n, pwr_step, pdg, max_idx, table_size;
308262306a36Sopenharmony_ci	u8 pd_gain_overlap;
308362306a36Sopenharmony_ci
308462306a36Sopenharmony_ci	/* Note: Register value is initialized on initvals
308562306a36Sopenharmony_ci	 * there is no feedback from hw.
308662306a36Sopenharmony_ci	 * XXX: What about pd_gain_overlap from EEPROM ? */
308762306a36Sopenharmony_ci	pd_gain_overlap = (u8) ath5k_hw_reg_read(ah, AR5K_PHY_TPC_RG5) &
308862306a36Sopenharmony_ci		AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP;
308962306a36Sopenharmony_ci
309062306a36Sopenharmony_ci	/* Create final PDADC table */
309162306a36Sopenharmony_ci	for (pdg = 0, pdadc_i = 0; pdg < pdcurves; pdg++) {
309262306a36Sopenharmony_ci		pdadc_tmp = ah->ah_txpower.tmpL[pdg];
309362306a36Sopenharmony_ci
309462306a36Sopenharmony_ci		if (pdg == pdcurves - 1)
309562306a36Sopenharmony_ci			/* 2 dB boundary stretch for last
309662306a36Sopenharmony_ci			 * (higher power) curve */
309762306a36Sopenharmony_ci			gain_boundaries[pdg] = pwr_max[pdg] + 4;
309862306a36Sopenharmony_ci		else
309962306a36Sopenharmony_ci			/* Set gain boundary in the middle
310062306a36Sopenharmony_ci			 * between this curve and the next one */
310162306a36Sopenharmony_ci			gain_boundaries[pdg] =
310262306a36Sopenharmony_ci				(pwr_max[pdg] + pwr_min[pdg + 1]) / 2;
310362306a36Sopenharmony_ci
310462306a36Sopenharmony_ci		/* Sanity check in case our 2 db stretch got out of
310562306a36Sopenharmony_ci		 * range. */
310662306a36Sopenharmony_ci		if (gain_boundaries[pdg] > AR5K_TUNE_MAX_TXPOWER)
310762306a36Sopenharmony_ci			gain_boundaries[pdg] = AR5K_TUNE_MAX_TXPOWER;
310862306a36Sopenharmony_ci
310962306a36Sopenharmony_ci		/* For the first curve (lower power)
311062306a36Sopenharmony_ci		 * start from 0 dB */
311162306a36Sopenharmony_ci		if (pdg == 0)
311262306a36Sopenharmony_ci			pdadc_0 = 0;
311362306a36Sopenharmony_ci		else
311462306a36Sopenharmony_ci			/* For the other curves use the gain overlap */
311562306a36Sopenharmony_ci			pdadc_0 = (gain_boundaries[pdg - 1] - pwr_min[pdg]) -
311662306a36Sopenharmony_ci							pd_gain_overlap;
311762306a36Sopenharmony_ci
311862306a36Sopenharmony_ci		/* Force each power step to be at least 0.5 dB */
311962306a36Sopenharmony_ci		if ((pdadc_tmp[1] - pdadc_tmp[0]) > 1)
312062306a36Sopenharmony_ci			pwr_step = pdadc_tmp[1] - pdadc_tmp[0];
312162306a36Sopenharmony_ci		else
312262306a36Sopenharmony_ci			pwr_step = 1;
312362306a36Sopenharmony_ci
312462306a36Sopenharmony_ci		/* If pdadc_0 is negative, we need to extrapolate
312562306a36Sopenharmony_ci		 * below this pdgain by a number of pwr_steps */
312662306a36Sopenharmony_ci		while ((pdadc_0 < 0) && (pdadc_i < 128)) {
312762306a36Sopenharmony_ci			s16 tmp = pdadc_tmp[0] + pdadc_0 * pwr_step;
312862306a36Sopenharmony_ci			pdadc_out[pdadc_i++] = (tmp < 0) ? 0 : (u8) tmp;
312962306a36Sopenharmony_ci			pdadc_0++;
313062306a36Sopenharmony_ci		}
313162306a36Sopenharmony_ci
313262306a36Sopenharmony_ci		/* Set last pwr level, using gain boundaries */
313362306a36Sopenharmony_ci		pdadc_n = gain_boundaries[pdg] + pd_gain_overlap - pwr_min[pdg];
313462306a36Sopenharmony_ci		/* Limit it to be inside pwr range */
313562306a36Sopenharmony_ci		table_size = pwr_max[pdg] - pwr_min[pdg];
313662306a36Sopenharmony_ci		max_idx = min(pdadc_n, table_size);
313762306a36Sopenharmony_ci
313862306a36Sopenharmony_ci		/* Fill pdadc_out table */
313962306a36Sopenharmony_ci		while (pdadc_0 < max_idx && pdadc_i < 128)
314062306a36Sopenharmony_ci			pdadc_out[pdadc_i++] = pdadc_tmp[pdadc_0++];
314162306a36Sopenharmony_ci
314262306a36Sopenharmony_ci		/* Need to extrapolate above this pdgain? */
314362306a36Sopenharmony_ci		if (pdadc_n <= max_idx)
314462306a36Sopenharmony_ci			continue;
314562306a36Sopenharmony_ci
314662306a36Sopenharmony_ci		/* Force each power step to be at least 0.5 dB */
314762306a36Sopenharmony_ci		if ((pdadc_tmp[table_size - 1] - pdadc_tmp[table_size - 2]) > 1)
314862306a36Sopenharmony_ci			pwr_step = pdadc_tmp[table_size - 1] -
314962306a36Sopenharmony_ci						pdadc_tmp[table_size - 2];
315062306a36Sopenharmony_ci		else
315162306a36Sopenharmony_ci			pwr_step = 1;
315262306a36Sopenharmony_ci
315362306a36Sopenharmony_ci		/* Extrapolate above */
315462306a36Sopenharmony_ci		while ((pdadc_0 < (s16) pdadc_n) &&
315562306a36Sopenharmony_ci		(pdadc_i < AR5K_EEPROM_POWER_TABLE_SIZE * 2)) {
315662306a36Sopenharmony_ci			s16 tmp = pdadc_tmp[table_size - 1] +
315762306a36Sopenharmony_ci					(pdadc_0 - max_idx) * pwr_step;
315862306a36Sopenharmony_ci			pdadc_out[pdadc_i++] = (tmp > 127) ? 127 : (u8) tmp;
315962306a36Sopenharmony_ci			pdadc_0++;
316062306a36Sopenharmony_ci		}
316162306a36Sopenharmony_ci	}
316262306a36Sopenharmony_ci
316362306a36Sopenharmony_ci	while (pdg < AR5K_EEPROM_N_PD_GAINS) {
316462306a36Sopenharmony_ci		gain_boundaries[pdg] = gain_boundaries[pdg - 1];
316562306a36Sopenharmony_ci		pdg++;
316662306a36Sopenharmony_ci	}
316762306a36Sopenharmony_ci
316862306a36Sopenharmony_ci	while (pdadc_i < AR5K_EEPROM_POWER_TABLE_SIZE * 2) {
316962306a36Sopenharmony_ci		pdadc_out[pdadc_i] = pdadc_out[pdadc_i - 1];
317062306a36Sopenharmony_ci		pdadc_i++;
317162306a36Sopenharmony_ci	}
317262306a36Sopenharmony_ci
317362306a36Sopenharmony_ci	/* Set gain boundaries */
317462306a36Sopenharmony_ci	ath5k_hw_reg_write(ah,
317562306a36Sopenharmony_ci		AR5K_REG_SM(pd_gain_overlap,
317662306a36Sopenharmony_ci			AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP) |
317762306a36Sopenharmony_ci		AR5K_REG_SM(gain_boundaries[0],
317862306a36Sopenharmony_ci			AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_1) |
317962306a36Sopenharmony_ci		AR5K_REG_SM(gain_boundaries[1],
318062306a36Sopenharmony_ci			AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_2) |
318162306a36Sopenharmony_ci		AR5K_REG_SM(gain_boundaries[2],
318262306a36Sopenharmony_ci			AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_3) |
318362306a36Sopenharmony_ci		AR5K_REG_SM(gain_boundaries[3],
318462306a36Sopenharmony_ci			AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_4),
318562306a36Sopenharmony_ci		AR5K_PHY_TPC_RG5);
318662306a36Sopenharmony_ci
318762306a36Sopenharmony_ci	/* Used for setting rate power table */
318862306a36Sopenharmony_ci	ah->ah_txpower.txp_min_idx = pwr_min[0];
318962306a36Sopenharmony_ci
319062306a36Sopenharmony_ci}
319162306a36Sopenharmony_ci
319262306a36Sopenharmony_ci/**
319362306a36Sopenharmony_ci * ath5k_write_pwr_to_pdadc_table() - Write the PDADC values on hw
319462306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
319562306a36Sopenharmony_ci * @ee_mode: One of enum ath5k_driver_mode
319662306a36Sopenharmony_ci */
319762306a36Sopenharmony_cistatic void
319862306a36Sopenharmony_ciath5k_write_pwr_to_pdadc_table(struct ath5k_hw *ah, u8 ee_mode)
319962306a36Sopenharmony_ci{
320062306a36Sopenharmony_ci	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
320162306a36Sopenharmony_ci	u8 *pdadc_out = ah->ah_txpower.txp_pd_table;
320262306a36Sopenharmony_ci	u8 *pdg_to_idx = ee->ee_pdc_to_idx[ee_mode];
320362306a36Sopenharmony_ci	u8 pdcurves = ee->ee_pd_gains[ee_mode];
320462306a36Sopenharmony_ci	u32 reg;
320562306a36Sopenharmony_ci	u8 i;
320662306a36Sopenharmony_ci
320762306a36Sopenharmony_ci	/* Select the right pdgain curves */
320862306a36Sopenharmony_ci
320962306a36Sopenharmony_ci	/* Clear current settings */
321062306a36Sopenharmony_ci	reg = ath5k_hw_reg_read(ah, AR5K_PHY_TPC_RG1);
321162306a36Sopenharmony_ci	reg &= ~(AR5K_PHY_TPC_RG1_PDGAIN_1 |
321262306a36Sopenharmony_ci		AR5K_PHY_TPC_RG1_PDGAIN_2 |
321362306a36Sopenharmony_ci		AR5K_PHY_TPC_RG1_PDGAIN_3 |
321462306a36Sopenharmony_ci		AR5K_PHY_TPC_RG1_NUM_PD_GAIN);
321562306a36Sopenharmony_ci
321662306a36Sopenharmony_ci	/*
321762306a36Sopenharmony_ci	 * Use pd_gains curve from eeprom
321862306a36Sopenharmony_ci	 *
321962306a36Sopenharmony_ci	 * This overrides the default setting from initvals
322062306a36Sopenharmony_ci	 * in case some vendors (e.g. Zcomax) don't use the default
322162306a36Sopenharmony_ci	 * curves. If we don't honor their settings we 'll get a
322262306a36Sopenharmony_ci	 * 5dB (1 * gain overlap ?) drop.
322362306a36Sopenharmony_ci	 */
322462306a36Sopenharmony_ci	reg |= AR5K_REG_SM(pdcurves, AR5K_PHY_TPC_RG1_NUM_PD_GAIN);
322562306a36Sopenharmony_ci
322662306a36Sopenharmony_ci	switch (pdcurves) {
322762306a36Sopenharmony_ci	case 3:
322862306a36Sopenharmony_ci		reg |= AR5K_REG_SM(pdg_to_idx[2], AR5K_PHY_TPC_RG1_PDGAIN_3);
322962306a36Sopenharmony_ci		fallthrough;
323062306a36Sopenharmony_ci	case 2:
323162306a36Sopenharmony_ci		reg |= AR5K_REG_SM(pdg_to_idx[1], AR5K_PHY_TPC_RG1_PDGAIN_2);
323262306a36Sopenharmony_ci		fallthrough;
323362306a36Sopenharmony_ci	case 1:
323462306a36Sopenharmony_ci		reg |= AR5K_REG_SM(pdg_to_idx[0], AR5K_PHY_TPC_RG1_PDGAIN_1);
323562306a36Sopenharmony_ci		break;
323662306a36Sopenharmony_ci	}
323762306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, reg, AR5K_PHY_TPC_RG1);
323862306a36Sopenharmony_ci
323962306a36Sopenharmony_ci	/*
324062306a36Sopenharmony_ci	 * Write TX power values
324162306a36Sopenharmony_ci	 */
324262306a36Sopenharmony_ci	for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) {
324362306a36Sopenharmony_ci		u32 val = get_unaligned_le32(&pdadc_out[4 * i]);
324462306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, val, AR5K_PHY_PDADC_TXPOWER(i));
324562306a36Sopenharmony_ci	}
324662306a36Sopenharmony_ci}
324762306a36Sopenharmony_ci
324862306a36Sopenharmony_ci
324962306a36Sopenharmony_ci/*
325062306a36Sopenharmony_ci * Common code for PCDAC/PDADC tables
325162306a36Sopenharmony_ci */
325262306a36Sopenharmony_ci
325362306a36Sopenharmony_ci/**
325462306a36Sopenharmony_ci * ath5k_setup_channel_powertable() - Set up power table for this channel
325562306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
325662306a36Sopenharmony_ci * @channel: The &struct ieee80211_channel
325762306a36Sopenharmony_ci * @ee_mode: One of enum ath5k_driver_mode
325862306a36Sopenharmony_ci * @type: One of enum ath5k_powertable_type (eeprom.h)
325962306a36Sopenharmony_ci *
326062306a36Sopenharmony_ci * This is the main function that uses all of the above
326162306a36Sopenharmony_ci * to set PCDAC/PDADC table on hw for the current channel.
326262306a36Sopenharmony_ci * This table is used for tx power calibration on the baseband,
326362306a36Sopenharmony_ci * without it we get weird tx power levels and in some cases
326462306a36Sopenharmony_ci * distorted spectral mask
326562306a36Sopenharmony_ci */
326662306a36Sopenharmony_cistatic int
326762306a36Sopenharmony_ciath5k_setup_channel_powertable(struct ath5k_hw *ah,
326862306a36Sopenharmony_ci			struct ieee80211_channel *channel,
326962306a36Sopenharmony_ci			u8 ee_mode, u8 type)
327062306a36Sopenharmony_ci{
327162306a36Sopenharmony_ci	struct ath5k_pdgain_info *pdg_L, *pdg_R;
327262306a36Sopenharmony_ci	struct ath5k_chan_pcal_info *pcinfo_L;
327362306a36Sopenharmony_ci	struct ath5k_chan_pcal_info *pcinfo_R;
327462306a36Sopenharmony_ci	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
327562306a36Sopenharmony_ci	u8 *pdg_curve_to_idx = ee->ee_pdc_to_idx[ee_mode];
327662306a36Sopenharmony_ci	s16 table_min[AR5K_EEPROM_N_PD_GAINS];
327762306a36Sopenharmony_ci	s16 table_max[AR5K_EEPROM_N_PD_GAINS];
327862306a36Sopenharmony_ci	u8 *tmpL;
327962306a36Sopenharmony_ci	u8 *tmpR;
328062306a36Sopenharmony_ci	u32 target = channel->center_freq;
328162306a36Sopenharmony_ci	int pdg, i;
328262306a36Sopenharmony_ci
328362306a36Sopenharmony_ci	/* Get surrounding freq piers for this channel */
328462306a36Sopenharmony_ci	ath5k_get_chan_pcal_surrounding_piers(ah, channel,
328562306a36Sopenharmony_ci						&pcinfo_L,
328662306a36Sopenharmony_ci						&pcinfo_R);
328762306a36Sopenharmony_ci
328862306a36Sopenharmony_ci	/* Loop over pd gain curves on
328962306a36Sopenharmony_ci	 * surrounding freq piers by index */
329062306a36Sopenharmony_ci	for (pdg = 0; pdg < ee->ee_pd_gains[ee_mode]; pdg++) {
329162306a36Sopenharmony_ci
329262306a36Sopenharmony_ci		/* Fill curves in reverse order
329362306a36Sopenharmony_ci		 * from lower power (max gain)
329462306a36Sopenharmony_ci		 * to higher power. Use curve -> idx
329562306a36Sopenharmony_ci		 * backmapping we did on eeprom init */
329662306a36Sopenharmony_ci		u8 idx = pdg_curve_to_idx[pdg];
329762306a36Sopenharmony_ci
329862306a36Sopenharmony_ci		/* Grab the needed curves by index */
329962306a36Sopenharmony_ci		pdg_L = &pcinfo_L->pd_curves[idx];
330062306a36Sopenharmony_ci		pdg_R = &pcinfo_R->pd_curves[idx];
330162306a36Sopenharmony_ci
330262306a36Sopenharmony_ci		/* Initialize the temp tables */
330362306a36Sopenharmony_ci		tmpL = ah->ah_txpower.tmpL[pdg];
330462306a36Sopenharmony_ci		tmpR = ah->ah_txpower.tmpR[pdg];
330562306a36Sopenharmony_ci
330662306a36Sopenharmony_ci		/* Set curve's x boundaries and create
330762306a36Sopenharmony_ci		 * curves so that they cover the same
330862306a36Sopenharmony_ci		 * range (if we don't do that one table
330962306a36Sopenharmony_ci		 * will have values on some range and the
331062306a36Sopenharmony_ci		 * other one won't have any so interpolation
331162306a36Sopenharmony_ci		 * will fail) */
331262306a36Sopenharmony_ci		table_min[pdg] = min(pdg_L->pd_pwr[0],
331362306a36Sopenharmony_ci					pdg_R->pd_pwr[0]) / 2;
331462306a36Sopenharmony_ci
331562306a36Sopenharmony_ci		table_max[pdg] = max(pdg_L->pd_pwr[pdg_L->pd_points - 1],
331662306a36Sopenharmony_ci				pdg_R->pd_pwr[pdg_R->pd_points - 1]) / 2;
331762306a36Sopenharmony_ci
331862306a36Sopenharmony_ci		/* Now create the curves on surrounding channels
331962306a36Sopenharmony_ci		 * and interpolate if needed to get the final
332062306a36Sopenharmony_ci		 * curve for this gain on this channel */
332162306a36Sopenharmony_ci		switch (type) {
332262306a36Sopenharmony_ci		case AR5K_PWRTABLE_LINEAR_PCDAC:
332362306a36Sopenharmony_ci			/* Override min/max so that we don't loose
332462306a36Sopenharmony_ci			 * accuracy (don't divide by 2) */
332562306a36Sopenharmony_ci			table_min[pdg] = min(pdg_L->pd_pwr[0],
332662306a36Sopenharmony_ci						pdg_R->pd_pwr[0]);
332762306a36Sopenharmony_ci
332862306a36Sopenharmony_ci			table_max[pdg] =
332962306a36Sopenharmony_ci				max(pdg_L->pd_pwr[pdg_L->pd_points - 1],
333062306a36Sopenharmony_ci					pdg_R->pd_pwr[pdg_R->pd_points - 1]);
333162306a36Sopenharmony_ci
333262306a36Sopenharmony_ci			/* Override minimum so that we don't get
333362306a36Sopenharmony_ci			 * out of bounds while extrapolating
333462306a36Sopenharmony_ci			 * below. Don't do this when we have 2
333562306a36Sopenharmony_ci			 * curves and we are on the high power curve
333662306a36Sopenharmony_ci			 * because table_min is ok in this case */
333762306a36Sopenharmony_ci			if (!(ee->ee_pd_gains[ee_mode] > 1 && pdg == 0)) {
333862306a36Sopenharmony_ci
333962306a36Sopenharmony_ci				table_min[pdg] =
334062306a36Sopenharmony_ci					ath5k_get_linear_pcdac_min(pdg_L->pd_step,
334162306a36Sopenharmony_ci								pdg_R->pd_step,
334262306a36Sopenharmony_ci								pdg_L->pd_pwr,
334362306a36Sopenharmony_ci								pdg_R->pd_pwr);
334462306a36Sopenharmony_ci
334562306a36Sopenharmony_ci				/* Don't go too low because we will
334662306a36Sopenharmony_ci				 * miss the upper part of the curve.
334762306a36Sopenharmony_ci				 * Note: 126 = 31.5dB (max power supported)
334862306a36Sopenharmony_ci				 * in 0.25dB units */
334962306a36Sopenharmony_ci				if (table_max[pdg] - table_min[pdg] > 126)
335062306a36Sopenharmony_ci					table_min[pdg] = table_max[pdg] - 126;
335162306a36Sopenharmony_ci			}
335262306a36Sopenharmony_ci
335362306a36Sopenharmony_ci			fallthrough;
335462306a36Sopenharmony_ci		case AR5K_PWRTABLE_PWR_TO_PCDAC:
335562306a36Sopenharmony_ci		case AR5K_PWRTABLE_PWR_TO_PDADC:
335662306a36Sopenharmony_ci
335762306a36Sopenharmony_ci			ath5k_create_power_curve(table_min[pdg],
335862306a36Sopenharmony_ci						table_max[pdg],
335962306a36Sopenharmony_ci						pdg_L->pd_pwr,
336062306a36Sopenharmony_ci						pdg_L->pd_step,
336162306a36Sopenharmony_ci						pdg_L->pd_points, tmpL, type);
336262306a36Sopenharmony_ci
336362306a36Sopenharmony_ci			/* We are in a calibration
336462306a36Sopenharmony_ci			 * pier, no need to interpolate
336562306a36Sopenharmony_ci			 * between freq piers */
336662306a36Sopenharmony_ci			if (pcinfo_L == pcinfo_R)
336762306a36Sopenharmony_ci				continue;
336862306a36Sopenharmony_ci
336962306a36Sopenharmony_ci			ath5k_create_power_curve(table_min[pdg],
337062306a36Sopenharmony_ci						table_max[pdg],
337162306a36Sopenharmony_ci						pdg_R->pd_pwr,
337262306a36Sopenharmony_ci						pdg_R->pd_step,
337362306a36Sopenharmony_ci						pdg_R->pd_points, tmpR, type);
337462306a36Sopenharmony_ci			break;
337562306a36Sopenharmony_ci		default:
337662306a36Sopenharmony_ci			return -EINVAL;
337762306a36Sopenharmony_ci		}
337862306a36Sopenharmony_ci
337962306a36Sopenharmony_ci		/* Interpolate between curves
338062306a36Sopenharmony_ci		 * of surrounding freq piers to
338162306a36Sopenharmony_ci		 * get the final curve for this
338262306a36Sopenharmony_ci		 * pd gain. Re-use tmpL for interpolation
338362306a36Sopenharmony_ci		 * output */
338462306a36Sopenharmony_ci		for (i = 0; (i < (u16) (table_max[pdg] - table_min[pdg])) &&
338562306a36Sopenharmony_ci		(i < AR5K_EEPROM_POWER_TABLE_SIZE); i++) {
338662306a36Sopenharmony_ci			tmpL[i] = (u8) ath5k_get_interpolated_value(target,
338762306a36Sopenharmony_ci							(s16) pcinfo_L->freq,
338862306a36Sopenharmony_ci							(s16) pcinfo_R->freq,
338962306a36Sopenharmony_ci							(s16) tmpL[i],
339062306a36Sopenharmony_ci							(s16) tmpR[i]);
339162306a36Sopenharmony_ci		}
339262306a36Sopenharmony_ci	}
339362306a36Sopenharmony_ci
339462306a36Sopenharmony_ci	/* Now we have a set of curves for this
339562306a36Sopenharmony_ci	 * channel on tmpL (x range is table_max - table_min
339662306a36Sopenharmony_ci	 * and y values are tmpL[pdg][]) sorted in the same
339762306a36Sopenharmony_ci	 * order as EEPROM (because we've used the backmapping).
339862306a36Sopenharmony_ci	 * So for RF5112 it's from higher power to lower power
339962306a36Sopenharmony_ci	 * and for RF2413 it's from lower power to higher power.
340062306a36Sopenharmony_ci	 * For RF5111 we only have one curve. */
340162306a36Sopenharmony_ci
340262306a36Sopenharmony_ci	/* Fill min and max power levels for this
340362306a36Sopenharmony_ci	 * channel by interpolating the values on
340462306a36Sopenharmony_ci	 * surrounding channels to complete the dataset */
340562306a36Sopenharmony_ci	ah->ah_txpower.txp_min_pwr = ath5k_get_interpolated_value(target,
340662306a36Sopenharmony_ci					(s16) pcinfo_L->freq,
340762306a36Sopenharmony_ci					(s16) pcinfo_R->freq,
340862306a36Sopenharmony_ci					pcinfo_L->min_pwr, pcinfo_R->min_pwr);
340962306a36Sopenharmony_ci
341062306a36Sopenharmony_ci	ah->ah_txpower.txp_max_pwr = ath5k_get_interpolated_value(target,
341162306a36Sopenharmony_ci					(s16) pcinfo_L->freq,
341262306a36Sopenharmony_ci					(s16) pcinfo_R->freq,
341362306a36Sopenharmony_ci					pcinfo_L->max_pwr, pcinfo_R->max_pwr);
341462306a36Sopenharmony_ci
341562306a36Sopenharmony_ci	/* Fill PCDAC/PDADC table */
341662306a36Sopenharmony_ci	switch (type) {
341762306a36Sopenharmony_ci	case AR5K_PWRTABLE_LINEAR_PCDAC:
341862306a36Sopenharmony_ci		/* For RF5112 we can have one or two curves
341962306a36Sopenharmony_ci		 * and each curve covers a certain power lvl
342062306a36Sopenharmony_ci		 * range so we need to do some more processing */
342162306a36Sopenharmony_ci		ath5k_combine_linear_pcdac_curves(ah, table_min, table_max,
342262306a36Sopenharmony_ci						ee->ee_pd_gains[ee_mode]);
342362306a36Sopenharmony_ci
342462306a36Sopenharmony_ci		/* Set txp.offset so that we can
342562306a36Sopenharmony_ci		 * match max power value with max
342662306a36Sopenharmony_ci		 * table index */
342762306a36Sopenharmony_ci		ah->ah_txpower.txp_offset = 64 - (table_max[0] / 2);
342862306a36Sopenharmony_ci		break;
342962306a36Sopenharmony_ci	case AR5K_PWRTABLE_PWR_TO_PCDAC:
343062306a36Sopenharmony_ci		/* We are done for RF5111 since it has only
343162306a36Sopenharmony_ci		 * one curve, just fit the curve on the table */
343262306a36Sopenharmony_ci		ath5k_fill_pwr_to_pcdac_table(ah, table_min, table_max);
343362306a36Sopenharmony_ci
343462306a36Sopenharmony_ci		/* No rate powertable adjustment for RF5111 */
343562306a36Sopenharmony_ci		ah->ah_txpower.txp_min_idx = 0;
343662306a36Sopenharmony_ci		ah->ah_txpower.txp_offset = 0;
343762306a36Sopenharmony_ci		break;
343862306a36Sopenharmony_ci	case AR5K_PWRTABLE_PWR_TO_PDADC:
343962306a36Sopenharmony_ci		/* Set PDADC boundaries and fill
344062306a36Sopenharmony_ci		 * final PDADC table */
344162306a36Sopenharmony_ci		ath5k_combine_pwr_to_pdadc_curves(ah, table_min, table_max,
344262306a36Sopenharmony_ci						ee->ee_pd_gains[ee_mode]);
344362306a36Sopenharmony_ci
344462306a36Sopenharmony_ci		/* Set txp.offset, note that table_min
344562306a36Sopenharmony_ci		 * can be negative */
344662306a36Sopenharmony_ci		ah->ah_txpower.txp_offset = table_min[0];
344762306a36Sopenharmony_ci		break;
344862306a36Sopenharmony_ci	default:
344962306a36Sopenharmony_ci		return -EINVAL;
345062306a36Sopenharmony_ci	}
345162306a36Sopenharmony_ci
345262306a36Sopenharmony_ci	ah->ah_txpower.txp_setup = true;
345362306a36Sopenharmony_ci
345462306a36Sopenharmony_ci	return 0;
345562306a36Sopenharmony_ci}
345662306a36Sopenharmony_ci
345762306a36Sopenharmony_ci/**
345862306a36Sopenharmony_ci * ath5k_write_channel_powertable() - Set power table for current channel on hw
345962306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
346062306a36Sopenharmony_ci * @ee_mode: One of enum ath5k_driver_mode
346162306a36Sopenharmony_ci * @type: One of enum ath5k_powertable_type (eeprom.h)
346262306a36Sopenharmony_ci */
346362306a36Sopenharmony_cistatic void
346462306a36Sopenharmony_ciath5k_write_channel_powertable(struct ath5k_hw *ah, u8 ee_mode, u8 type)
346562306a36Sopenharmony_ci{
346662306a36Sopenharmony_ci	if (type == AR5K_PWRTABLE_PWR_TO_PDADC)
346762306a36Sopenharmony_ci		ath5k_write_pwr_to_pdadc_table(ah, ee_mode);
346862306a36Sopenharmony_ci	else
346962306a36Sopenharmony_ci		ath5k_write_pcdac_table(ah);
347062306a36Sopenharmony_ci}
347162306a36Sopenharmony_ci
347262306a36Sopenharmony_ci
347362306a36Sopenharmony_ci/**
347462306a36Sopenharmony_ci * DOC: Per-rate tx power setting
347562306a36Sopenharmony_ci *
347662306a36Sopenharmony_ci * This is the code that sets the desired tx power limit (below
347762306a36Sopenharmony_ci * maximum) on hw for each rate (we also have TPC that sets
347862306a36Sopenharmony_ci * power per packet type). We do that by providing an index on the
347962306a36Sopenharmony_ci * PCDAC/PDADC table we set up above, for each rate.
348062306a36Sopenharmony_ci *
348162306a36Sopenharmony_ci * For now we only limit txpower based on maximum tx power
348262306a36Sopenharmony_ci * supported by hw (what's inside rate_info) + conformance test
348362306a36Sopenharmony_ci * limits. We need to limit this even more, based on regulatory domain
348462306a36Sopenharmony_ci * etc to be safe. Normally this is done from above so we don't care
348562306a36Sopenharmony_ci * here, all we care is that the tx power we set will be O.K.
348662306a36Sopenharmony_ci * for the hw (e.g. won't create noise on PA etc).
348762306a36Sopenharmony_ci *
348862306a36Sopenharmony_ci * Rate power table contains indices to PCDAC/PDADC table (0.5dB steps -
348962306a36Sopenharmony_ci * x values) and is indexed as follows:
349062306a36Sopenharmony_ci * rates[0] - rates[7] -> OFDM rates
349162306a36Sopenharmony_ci * rates[8] - rates[14] -> CCK rates
349262306a36Sopenharmony_ci * rates[15] -> XR rates (they all have the same power)
349362306a36Sopenharmony_ci */
349462306a36Sopenharmony_ci
349562306a36Sopenharmony_ci/**
349662306a36Sopenharmony_ci * ath5k_setup_rate_powertable() - Set up rate power table for a given tx power
349762306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
349862306a36Sopenharmony_ci * @max_pwr: The maximum tx power requested in 0.5dB steps
349962306a36Sopenharmony_ci * @rate_info: The &struct ath5k_rate_pcal_info to fill
350062306a36Sopenharmony_ci * @ee_mode: One of enum ath5k_driver_mode
350162306a36Sopenharmony_ci */
350262306a36Sopenharmony_cistatic void
350362306a36Sopenharmony_ciath5k_setup_rate_powertable(struct ath5k_hw *ah, u16 max_pwr,
350462306a36Sopenharmony_ci			struct ath5k_rate_pcal_info *rate_info,
350562306a36Sopenharmony_ci			u8 ee_mode)
350662306a36Sopenharmony_ci{
350762306a36Sopenharmony_ci	unsigned int i;
350862306a36Sopenharmony_ci	u16 *rates;
350962306a36Sopenharmony_ci	s16 rate_idx_scaled = 0;
351062306a36Sopenharmony_ci
351162306a36Sopenharmony_ci	/* max_pwr is power level we got from driver/user in 0.5dB
351262306a36Sopenharmony_ci	 * units, switch to 0.25dB units so we can compare */
351362306a36Sopenharmony_ci	max_pwr *= 2;
351462306a36Sopenharmony_ci	max_pwr = min(max_pwr, (u16) ah->ah_txpower.txp_max_pwr) / 2;
351562306a36Sopenharmony_ci
351662306a36Sopenharmony_ci	/* apply rate limits */
351762306a36Sopenharmony_ci	rates = ah->ah_txpower.txp_rates_power_table;
351862306a36Sopenharmony_ci
351962306a36Sopenharmony_ci	/* OFDM rates 6 to 24Mb/s */
352062306a36Sopenharmony_ci	for (i = 0; i < 5; i++)
352162306a36Sopenharmony_ci		rates[i] = min(max_pwr, rate_info->target_power_6to24);
352262306a36Sopenharmony_ci
352362306a36Sopenharmony_ci	/* Rest OFDM rates */
352462306a36Sopenharmony_ci	rates[5] = min(rates[0], rate_info->target_power_36);
352562306a36Sopenharmony_ci	rates[6] = min(rates[0], rate_info->target_power_48);
352662306a36Sopenharmony_ci	rates[7] = min(rates[0], rate_info->target_power_54);
352762306a36Sopenharmony_ci
352862306a36Sopenharmony_ci	/* CCK rates */
352962306a36Sopenharmony_ci	/* 1L */
353062306a36Sopenharmony_ci	rates[8] = min(rates[0], rate_info->target_power_6to24);
353162306a36Sopenharmony_ci	/* 2L */
353262306a36Sopenharmony_ci	rates[9] = min(rates[0], rate_info->target_power_36);
353362306a36Sopenharmony_ci	/* 2S */
353462306a36Sopenharmony_ci	rates[10] = min(rates[0], rate_info->target_power_36);
353562306a36Sopenharmony_ci	/* 5L */
353662306a36Sopenharmony_ci	rates[11] = min(rates[0], rate_info->target_power_48);
353762306a36Sopenharmony_ci	/* 5S */
353862306a36Sopenharmony_ci	rates[12] = min(rates[0], rate_info->target_power_48);
353962306a36Sopenharmony_ci	/* 11L */
354062306a36Sopenharmony_ci	rates[13] = min(rates[0], rate_info->target_power_54);
354162306a36Sopenharmony_ci	/* 11S */
354262306a36Sopenharmony_ci	rates[14] = min(rates[0], rate_info->target_power_54);
354362306a36Sopenharmony_ci
354462306a36Sopenharmony_ci	/* XR rates */
354562306a36Sopenharmony_ci	rates[15] = min(rates[0], rate_info->target_power_6to24);
354662306a36Sopenharmony_ci
354762306a36Sopenharmony_ci	/* CCK rates have different peak to average ratio
354862306a36Sopenharmony_ci	 * so we have to tweak their power so that gainf
354962306a36Sopenharmony_ci	 * correction works ok. For this we use OFDM to
355062306a36Sopenharmony_ci	 * CCK delta from eeprom */
355162306a36Sopenharmony_ci	if ((ee_mode == AR5K_EEPROM_MODE_11G) &&
355262306a36Sopenharmony_ci	(ah->ah_phy_revision < AR5K_SREV_PHY_5212A))
355362306a36Sopenharmony_ci		for (i = 8; i <= 15; i++)
355462306a36Sopenharmony_ci			rates[i] -= ah->ah_txpower.txp_cck_ofdm_gainf_delta;
355562306a36Sopenharmony_ci
355662306a36Sopenharmony_ci	/* Save min/max and current tx power for this channel
355762306a36Sopenharmony_ci	 * in 0.25dB units.
355862306a36Sopenharmony_ci	 *
355962306a36Sopenharmony_ci	 * Note: We use rates[0] for current tx power because
356062306a36Sopenharmony_ci	 * it covers most of the rates, in most cases. It's our
356162306a36Sopenharmony_ci	 * tx power limit and what the user expects to see. */
356262306a36Sopenharmony_ci	ah->ah_txpower.txp_min_pwr = 2 * rates[7];
356362306a36Sopenharmony_ci	ah->ah_txpower.txp_cur_pwr = 2 * rates[0];
356462306a36Sopenharmony_ci
356562306a36Sopenharmony_ci	/* Set max txpower for correct OFDM operation on all rates
356662306a36Sopenharmony_ci	 * -that is the txpower for 54Mbit-, it's used for the PAPD
356762306a36Sopenharmony_ci	 * gain probe and it's in 0.5dB units */
356862306a36Sopenharmony_ci	ah->ah_txpower.txp_ofdm = rates[7];
356962306a36Sopenharmony_ci
357062306a36Sopenharmony_ci	/* Now that we have all rates setup use table offset to
357162306a36Sopenharmony_ci	 * match the power range set by user with the power indices
357262306a36Sopenharmony_ci	 * on PCDAC/PDADC table */
357362306a36Sopenharmony_ci	for (i = 0; i < 16; i++) {
357462306a36Sopenharmony_ci		rate_idx_scaled = rates[i] + ah->ah_txpower.txp_offset;
357562306a36Sopenharmony_ci		/* Don't get out of bounds */
357662306a36Sopenharmony_ci		if (rate_idx_scaled > 63)
357762306a36Sopenharmony_ci			rate_idx_scaled = 63;
357862306a36Sopenharmony_ci		if (rate_idx_scaled < 0)
357962306a36Sopenharmony_ci			rate_idx_scaled = 0;
358062306a36Sopenharmony_ci		rates[i] = rate_idx_scaled;
358162306a36Sopenharmony_ci	}
358262306a36Sopenharmony_ci}
358362306a36Sopenharmony_ci
358462306a36Sopenharmony_ci
358562306a36Sopenharmony_ci/**
358662306a36Sopenharmony_ci * ath5k_hw_txpower() - Set transmission power limit for a given channel
358762306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
358862306a36Sopenharmony_ci * @channel: The &struct ieee80211_channel
358962306a36Sopenharmony_ci * @txpower: Requested tx power in 0.5dB steps
359062306a36Sopenharmony_ci *
359162306a36Sopenharmony_ci * Combines all of the above to set the requested tx power limit
359262306a36Sopenharmony_ci * on hw.
359362306a36Sopenharmony_ci */
359462306a36Sopenharmony_cistatic int
359562306a36Sopenharmony_ciath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel,
359662306a36Sopenharmony_ci		 u8 txpower)
359762306a36Sopenharmony_ci{
359862306a36Sopenharmony_ci	struct ath5k_rate_pcal_info rate_info;
359962306a36Sopenharmony_ci	struct ieee80211_channel *curr_channel = ah->ah_current_channel;
360062306a36Sopenharmony_ci	int ee_mode;
360162306a36Sopenharmony_ci	u8 type;
360262306a36Sopenharmony_ci	int ret;
360362306a36Sopenharmony_ci
360462306a36Sopenharmony_ci	if (txpower > AR5K_TUNE_MAX_TXPOWER) {
360562306a36Sopenharmony_ci		ATH5K_ERR(ah, "invalid tx power: %u\n", txpower);
360662306a36Sopenharmony_ci		return -EINVAL;
360762306a36Sopenharmony_ci	}
360862306a36Sopenharmony_ci
360962306a36Sopenharmony_ci	ee_mode = ath5k_eeprom_mode_from_channel(ah, channel);
361062306a36Sopenharmony_ci
361162306a36Sopenharmony_ci	/* Initialize TX power table */
361262306a36Sopenharmony_ci	switch (ah->ah_radio) {
361362306a36Sopenharmony_ci	case AR5K_RF5110:
361462306a36Sopenharmony_ci		/* TODO */
361562306a36Sopenharmony_ci		return 0;
361662306a36Sopenharmony_ci	case AR5K_RF5111:
361762306a36Sopenharmony_ci		type = AR5K_PWRTABLE_PWR_TO_PCDAC;
361862306a36Sopenharmony_ci		break;
361962306a36Sopenharmony_ci	case AR5K_RF5112:
362062306a36Sopenharmony_ci		type = AR5K_PWRTABLE_LINEAR_PCDAC;
362162306a36Sopenharmony_ci		break;
362262306a36Sopenharmony_ci	case AR5K_RF2413:
362362306a36Sopenharmony_ci	case AR5K_RF5413:
362462306a36Sopenharmony_ci	case AR5K_RF2316:
362562306a36Sopenharmony_ci	case AR5K_RF2317:
362662306a36Sopenharmony_ci	case AR5K_RF2425:
362762306a36Sopenharmony_ci		type = AR5K_PWRTABLE_PWR_TO_PDADC;
362862306a36Sopenharmony_ci		break;
362962306a36Sopenharmony_ci	default:
363062306a36Sopenharmony_ci		return -EINVAL;
363162306a36Sopenharmony_ci	}
363262306a36Sopenharmony_ci
363362306a36Sopenharmony_ci	/*
363462306a36Sopenharmony_ci	 * If we don't change channel/mode skip tx powertable calculation
363562306a36Sopenharmony_ci	 * and use the cached one.
363662306a36Sopenharmony_ci	 */
363762306a36Sopenharmony_ci	if (!ah->ah_txpower.txp_setup ||
363862306a36Sopenharmony_ci	    (channel->hw_value != curr_channel->hw_value) ||
363962306a36Sopenharmony_ci	    (channel->center_freq != curr_channel->center_freq)) {
364062306a36Sopenharmony_ci		/* Reset TX power values but preserve requested
364162306a36Sopenharmony_ci		 * tx power from above */
364262306a36Sopenharmony_ci		int requested_txpower = ah->ah_txpower.txp_requested;
364362306a36Sopenharmony_ci
364462306a36Sopenharmony_ci		memset(&ah->ah_txpower, 0, sizeof(ah->ah_txpower));
364562306a36Sopenharmony_ci
364662306a36Sopenharmony_ci		/* Restore TPC setting and requested tx power */
364762306a36Sopenharmony_ci		ah->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER;
364862306a36Sopenharmony_ci
364962306a36Sopenharmony_ci		ah->ah_txpower.txp_requested = requested_txpower;
365062306a36Sopenharmony_ci
365162306a36Sopenharmony_ci		/* Calculate the powertable */
365262306a36Sopenharmony_ci		ret = ath5k_setup_channel_powertable(ah, channel,
365362306a36Sopenharmony_ci							ee_mode, type);
365462306a36Sopenharmony_ci		if (ret)
365562306a36Sopenharmony_ci			return ret;
365662306a36Sopenharmony_ci	}
365762306a36Sopenharmony_ci
365862306a36Sopenharmony_ci	/* Write table on hw */
365962306a36Sopenharmony_ci	ath5k_write_channel_powertable(ah, ee_mode, type);
366062306a36Sopenharmony_ci
366162306a36Sopenharmony_ci	/* Limit max power if we have a CTL available */
366262306a36Sopenharmony_ci	ath5k_get_max_ctl_power(ah, channel);
366362306a36Sopenharmony_ci
366462306a36Sopenharmony_ci	/* FIXME: Antenna reduction stuff */
366562306a36Sopenharmony_ci
366662306a36Sopenharmony_ci	/* FIXME: Limit power on turbo modes */
366762306a36Sopenharmony_ci
366862306a36Sopenharmony_ci	/* FIXME: TPC scale reduction */
366962306a36Sopenharmony_ci
367062306a36Sopenharmony_ci	/* Get surrounding channels for per-rate power table
367162306a36Sopenharmony_ci	 * calibration */
367262306a36Sopenharmony_ci	ath5k_get_rate_pcal_data(ah, channel, &rate_info);
367362306a36Sopenharmony_ci
367462306a36Sopenharmony_ci	/* Setup rate power table */
367562306a36Sopenharmony_ci	ath5k_setup_rate_powertable(ah, txpower, &rate_info, ee_mode);
367662306a36Sopenharmony_ci
367762306a36Sopenharmony_ci	/* Write rate power table on hw */
367862306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, AR5K_TXPOWER_OFDM(3, 24) |
367962306a36Sopenharmony_ci		AR5K_TXPOWER_OFDM(2, 16) | AR5K_TXPOWER_OFDM(1, 8) |
368062306a36Sopenharmony_ci		AR5K_TXPOWER_OFDM(0, 0), AR5K_PHY_TXPOWER_RATE1);
368162306a36Sopenharmony_ci
368262306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, AR5K_TXPOWER_OFDM(7, 24) |
368362306a36Sopenharmony_ci		AR5K_TXPOWER_OFDM(6, 16) | AR5K_TXPOWER_OFDM(5, 8) |
368462306a36Sopenharmony_ci		AR5K_TXPOWER_OFDM(4, 0), AR5K_PHY_TXPOWER_RATE2);
368562306a36Sopenharmony_ci
368662306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, AR5K_TXPOWER_CCK(10, 24) |
368762306a36Sopenharmony_ci		AR5K_TXPOWER_CCK(9, 16) | AR5K_TXPOWER_CCK(15, 8) |
368862306a36Sopenharmony_ci		AR5K_TXPOWER_CCK(8, 0), AR5K_PHY_TXPOWER_RATE3);
368962306a36Sopenharmony_ci
369062306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, AR5K_TXPOWER_CCK(14, 24) |
369162306a36Sopenharmony_ci		AR5K_TXPOWER_CCK(13, 16) | AR5K_TXPOWER_CCK(12, 8) |
369262306a36Sopenharmony_ci		AR5K_TXPOWER_CCK(11, 0), AR5K_PHY_TXPOWER_RATE4);
369362306a36Sopenharmony_ci
369462306a36Sopenharmony_ci	/* FIXME: TPC support */
369562306a36Sopenharmony_ci	if (ah->ah_txpower.txp_tpc) {
369662306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX_TPC_ENABLE |
369762306a36Sopenharmony_ci			AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX);
369862306a36Sopenharmony_ci
369962306a36Sopenharmony_ci		ath5k_hw_reg_write(ah,
370062306a36Sopenharmony_ci			AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_ACK) |
370162306a36Sopenharmony_ci			AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CTS) |
370262306a36Sopenharmony_ci			AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CHIRP),
370362306a36Sopenharmony_ci			AR5K_TPC);
370462306a36Sopenharmony_ci	} else {
370562306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, AR5K_TUNE_MAX_TXPOWER,
370662306a36Sopenharmony_ci			AR5K_PHY_TXPOWER_RATE_MAX);
370762306a36Sopenharmony_ci	}
370862306a36Sopenharmony_ci
370962306a36Sopenharmony_ci	return 0;
371062306a36Sopenharmony_ci}
371162306a36Sopenharmony_ci
371262306a36Sopenharmony_ci/**
371362306a36Sopenharmony_ci * ath5k_hw_set_txpower_limit() - Set txpower limit for the current channel
371462306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
371562306a36Sopenharmony_ci * @txpower: The requested tx power limit in 0.5dB steps
371662306a36Sopenharmony_ci *
371762306a36Sopenharmony_ci * This function provides access to ath5k_hw_txpower to the driver in
371862306a36Sopenharmony_ci * case user or an application changes it while PHY is running.
371962306a36Sopenharmony_ci */
372062306a36Sopenharmony_ciint
372162306a36Sopenharmony_ciath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 txpower)
372262306a36Sopenharmony_ci{
372362306a36Sopenharmony_ci	ATH5K_DBG(ah, ATH5K_DEBUG_TXPOWER,
372462306a36Sopenharmony_ci		"changing txpower to %d\n", txpower);
372562306a36Sopenharmony_ci
372662306a36Sopenharmony_ci	return ath5k_hw_txpower(ah, ah->ah_current_channel, txpower);
372762306a36Sopenharmony_ci}
372862306a36Sopenharmony_ci
372962306a36Sopenharmony_ci
373062306a36Sopenharmony_ci/*************\
373162306a36Sopenharmony_ci Init function
373262306a36Sopenharmony_ci\*************/
373362306a36Sopenharmony_ci
373462306a36Sopenharmony_ci/**
373562306a36Sopenharmony_ci * ath5k_hw_phy_init() - Initialize PHY
373662306a36Sopenharmony_ci * @ah: The &struct ath5k_hw
373762306a36Sopenharmony_ci * @channel: The @struct ieee80211_channel
373862306a36Sopenharmony_ci * @mode: One of enum ath5k_driver_mode
373962306a36Sopenharmony_ci * @fast: Try a fast channel switch instead
374062306a36Sopenharmony_ci *
374162306a36Sopenharmony_ci * This is the main function used during reset to initialize PHY
374262306a36Sopenharmony_ci * or do a fast channel change if possible.
374362306a36Sopenharmony_ci *
374462306a36Sopenharmony_ci * NOTE: Do not call this one from the driver, it assumes PHY is in a
374562306a36Sopenharmony_ci * warm reset state !
374662306a36Sopenharmony_ci */
374762306a36Sopenharmony_ciint
374862306a36Sopenharmony_ciath5k_hw_phy_init(struct ath5k_hw *ah, struct ieee80211_channel *channel,
374962306a36Sopenharmony_ci		      u8 mode, bool fast)
375062306a36Sopenharmony_ci{
375162306a36Sopenharmony_ci	struct ieee80211_channel *curr_channel;
375262306a36Sopenharmony_ci	int ret, i;
375362306a36Sopenharmony_ci	u32 phy_tst1;
375462306a36Sopenharmony_ci	ret = 0;
375562306a36Sopenharmony_ci
375662306a36Sopenharmony_ci	/*
375762306a36Sopenharmony_ci	 * Sanity check for fast flag
375862306a36Sopenharmony_ci	 * Don't try fast channel change when changing modulation
375962306a36Sopenharmony_ci	 * mode/band. We check for chip compatibility on
376062306a36Sopenharmony_ci	 * ath5k_hw_reset.
376162306a36Sopenharmony_ci	 */
376262306a36Sopenharmony_ci	curr_channel = ah->ah_current_channel;
376362306a36Sopenharmony_ci	if (fast && (channel->hw_value != curr_channel->hw_value))
376462306a36Sopenharmony_ci		return -EINVAL;
376562306a36Sopenharmony_ci
376662306a36Sopenharmony_ci	/*
376762306a36Sopenharmony_ci	 * On fast channel change we only set the synth parameters
376862306a36Sopenharmony_ci	 * while PHY is running, enable calibration and skip the rest.
376962306a36Sopenharmony_ci	 */
377062306a36Sopenharmony_ci	if (fast) {
377162306a36Sopenharmony_ci		AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_RFBUS_REQ,
377262306a36Sopenharmony_ci				    AR5K_PHY_RFBUS_REQ_REQUEST);
377362306a36Sopenharmony_ci		for (i = 0; i < 100; i++) {
377462306a36Sopenharmony_ci			if (ath5k_hw_reg_read(ah, AR5K_PHY_RFBUS_GRANT))
377562306a36Sopenharmony_ci				break;
377662306a36Sopenharmony_ci			udelay(5);
377762306a36Sopenharmony_ci		}
377862306a36Sopenharmony_ci		/* Failed */
377962306a36Sopenharmony_ci		if (i >= 100)
378062306a36Sopenharmony_ci			return -EIO;
378162306a36Sopenharmony_ci
378262306a36Sopenharmony_ci		/* Set channel and wait for synth */
378362306a36Sopenharmony_ci		ret = ath5k_hw_channel(ah, channel);
378462306a36Sopenharmony_ci		if (ret)
378562306a36Sopenharmony_ci			return ret;
378662306a36Sopenharmony_ci
378762306a36Sopenharmony_ci		ath5k_hw_wait_for_synth(ah, channel);
378862306a36Sopenharmony_ci	}
378962306a36Sopenharmony_ci
379062306a36Sopenharmony_ci	/*
379162306a36Sopenharmony_ci	 * Set TX power
379262306a36Sopenharmony_ci	 *
379362306a36Sopenharmony_ci	 * Note: We need to do that before we set
379462306a36Sopenharmony_ci	 * RF buffer settings on 5211/5212+ so that we
379562306a36Sopenharmony_ci	 * properly set curve indices.
379662306a36Sopenharmony_ci	 */
379762306a36Sopenharmony_ci	ret = ath5k_hw_txpower(ah, channel, ah->ah_txpower.txp_requested ?
379862306a36Sopenharmony_ci					ah->ah_txpower.txp_requested * 2 :
379962306a36Sopenharmony_ci					AR5K_TUNE_MAX_TXPOWER);
380062306a36Sopenharmony_ci	if (ret)
380162306a36Sopenharmony_ci		return ret;
380262306a36Sopenharmony_ci
380362306a36Sopenharmony_ci	/* Write OFDM timings on 5212*/
380462306a36Sopenharmony_ci	if (ah->ah_version == AR5K_AR5212 &&
380562306a36Sopenharmony_ci		channel->hw_value != AR5K_MODE_11B) {
380662306a36Sopenharmony_ci
380762306a36Sopenharmony_ci		ret = ath5k_hw_write_ofdm_timings(ah, channel);
380862306a36Sopenharmony_ci		if (ret)
380962306a36Sopenharmony_ci			return ret;
381062306a36Sopenharmony_ci
381162306a36Sopenharmony_ci		/* Spur info is available only from EEPROM versions
381262306a36Sopenharmony_ci		 * greater than 5.3, but the EEPROM routines will use
381362306a36Sopenharmony_ci		 * static values for older versions */
381462306a36Sopenharmony_ci		if (ah->ah_mac_srev >= AR5K_SREV_AR5424)
381562306a36Sopenharmony_ci			ath5k_hw_set_spur_mitigation_filter(ah,
381662306a36Sopenharmony_ci							    channel);
381762306a36Sopenharmony_ci	}
381862306a36Sopenharmony_ci
381962306a36Sopenharmony_ci	/* If we used fast channel switching
382062306a36Sopenharmony_ci	 * we are done, release RF bus and
382162306a36Sopenharmony_ci	 * fire up NF calibration.
382262306a36Sopenharmony_ci	 *
382362306a36Sopenharmony_ci	 * Note: Only NF calibration due to
382462306a36Sopenharmony_ci	 * channel change, not AGC calibration
382562306a36Sopenharmony_ci	 * since AGC is still running !
382662306a36Sopenharmony_ci	 */
382762306a36Sopenharmony_ci	if (fast) {
382862306a36Sopenharmony_ci		/*
382962306a36Sopenharmony_ci		 * Release RF Bus grant
383062306a36Sopenharmony_ci		 */
383162306a36Sopenharmony_ci		AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_RFBUS_REQ,
383262306a36Sopenharmony_ci				    AR5K_PHY_RFBUS_REQ_REQUEST);
383362306a36Sopenharmony_ci
383462306a36Sopenharmony_ci		/*
383562306a36Sopenharmony_ci		 * Start NF calibration
383662306a36Sopenharmony_ci		 */
383762306a36Sopenharmony_ci		AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL,
383862306a36Sopenharmony_ci					AR5K_PHY_AGCCTL_NF);
383962306a36Sopenharmony_ci
384062306a36Sopenharmony_ci		return ret;
384162306a36Sopenharmony_ci	}
384262306a36Sopenharmony_ci
384362306a36Sopenharmony_ci	/*
384462306a36Sopenharmony_ci	 * For 5210 we do all initialization using
384562306a36Sopenharmony_ci	 * initvals, so we don't have to modify
384662306a36Sopenharmony_ci	 * any settings (5210 also only supports
384762306a36Sopenharmony_ci	 * a/aturbo modes)
384862306a36Sopenharmony_ci	 */
384962306a36Sopenharmony_ci	if (ah->ah_version != AR5K_AR5210) {
385062306a36Sopenharmony_ci
385162306a36Sopenharmony_ci		/*
385262306a36Sopenharmony_ci		 * Write initial RF gain settings
385362306a36Sopenharmony_ci		 * This should work for both 5111/5112
385462306a36Sopenharmony_ci		 */
385562306a36Sopenharmony_ci		ret = ath5k_hw_rfgain_init(ah, channel->band);
385662306a36Sopenharmony_ci		if (ret)
385762306a36Sopenharmony_ci			return ret;
385862306a36Sopenharmony_ci
385962306a36Sopenharmony_ci		usleep_range(1000, 1500);
386062306a36Sopenharmony_ci
386162306a36Sopenharmony_ci		/*
386262306a36Sopenharmony_ci		 * Write RF buffer
386362306a36Sopenharmony_ci		 */
386462306a36Sopenharmony_ci		ret = ath5k_hw_rfregs_init(ah, channel, mode);
386562306a36Sopenharmony_ci		if (ret)
386662306a36Sopenharmony_ci			return ret;
386762306a36Sopenharmony_ci
386862306a36Sopenharmony_ci		/*Enable/disable 802.11b mode on 5111
386962306a36Sopenharmony_ci		(enable 2111 frequency converter + CCK)*/
387062306a36Sopenharmony_ci		if (ah->ah_radio == AR5K_RF5111) {
387162306a36Sopenharmony_ci			if (mode == AR5K_MODE_11B)
387262306a36Sopenharmony_ci				AR5K_REG_ENABLE_BITS(ah, AR5K_TXCFG,
387362306a36Sopenharmony_ci				    AR5K_TXCFG_B_MODE);
387462306a36Sopenharmony_ci			else
387562306a36Sopenharmony_ci				AR5K_REG_DISABLE_BITS(ah, AR5K_TXCFG,
387662306a36Sopenharmony_ci				    AR5K_TXCFG_B_MODE);
387762306a36Sopenharmony_ci		}
387862306a36Sopenharmony_ci
387962306a36Sopenharmony_ci	} else if (ah->ah_version == AR5K_AR5210) {
388062306a36Sopenharmony_ci		usleep_range(1000, 1500);
388162306a36Sopenharmony_ci		/* Disable phy and wait */
388262306a36Sopenharmony_ci		ath5k_hw_reg_write(ah, AR5K_PHY_ACT_DISABLE, AR5K_PHY_ACT);
388362306a36Sopenharmony_ci		usleep_range(1000, 1500);
388462306a36Sopenharmony_ci	}
388562306a36Sopenharmony_ci
388662306a36Sopenharmony_ci	/* Set channel on PHY */
388762306a36Sopenharmony_ci	ret = ath5k_hw_channel(ah, channel);
388862306a36Sopenharmony_ci	if (ret)
388962306a36Sopenharmony_ci		return ret;
389062306a36Sopenharmony_ci
389162306a36Sopenharmony_ci	/*
389262306a36Sopenharmony_ci	 * Enable the PHY and wait until completion
389362306a36Sopenharmony_ci	 * This includes BaseBand and Synthesizer
389462306a36Sopenharmony_ci	 * activation.
389562306a36Sopenharmony_ci	 */
389662306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, AR5K_PHY_ACT_ENABLE, AR5K_PHY_ACT);
389762306a36Sopenharmony_ci
389862306a36Sopenharmony_ci	ath5k_hw_wait_for_synth(ah, channel);
389962306a36Sopenharmony_ci
390062306a36Sopenharmony_ci	/*
390162306a36Sopenharmony_ci	 * Perform ADC test to see if baseband is ready
390262306a36Sopenharmony_ci	 * Set tx hold and check adc test register
390362306a36Sopenharmony_ci	 */
390462306a36Sopenharmony_ci	phy_tst1 = ath5k_hw_reg_read(ah, AR5K_PHY_TST1);
390562306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, AR5K_PHY_TST1_TXHOLD, AR5K_PHY_TST1);
390662306a36Sopenharmony_ci	for (i = 0; i <= 20; i++) {
390762306a36Sopenharmony_ci		if (!(ath5k_hw_reg_read(ah, AR5K_PHY_ADC_TEST) & 0x10))
390862306a36Sopenharmony_ci			break;
390962306a36Sopenharmony_ci		usleep_range(200, 250);
391062306a36Sopenharmony_ci	}
391162306a36Sopenharmony_ci	ath5k_hw_reg_write(ah, phy_tst1, AR5K_PHY_TST1);
391262306a36Sopenharmony_ci
391362306a36Sopenharmony_ci	/*
391462306a36Sopenharmony_ci	 * Start automatic gain control calibration
391562306a36Sopenharmony_ci	 *
391662306a36Sopenharmony_ci	 * During AGC calibration RX path is re-routed to
391762306a36Sopenharmony_ci	 * a power detector so we don't receive anything.
391862306a36Sopenharmony_ci	 *
391962306a36Sopenharmony_ci	 * This method is used to calibrate some static offsets
392062306a36Sopenharmony_ci	 * used together with on-the fly I/Q calibration (the
392162306a36Sopenharmony_ci	 * one performed via ath5k_hw_phy_calibrate), which doesn't
392262306a36Sopenharmony_ci	 * interrupt rx path.
392362306a36Sopenharmony_ci	 *
392462306a36Sopenharmony_ci	 * While rx path is re-routed to the power detector we also
392562306a36Sopenharmony_ci	 * start a noise floor calibration to measure the
392662306a36Sopenharmony_ci	 * card's noise floor (the noise we measure when we are not
392762306a36Sopenharmony_ci	 * transmitting or receiving anything).
392862306a36Sopenharmony_ci	 *
392962306a36Sopenharmony_ci	 * If we are in a noisy environment, AGC calibration may time
393062306a36Sopenharmony_ci	 * out and/or noise floor calibration might timeout.
393162306a36Sopenharmony_ci	 */
393262306a36Sopenharmony_ci	AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL,
393362306a36Sopenharmony_ci				AR5K_PHY_AGCCTL_CAL | AR5K_PHY_AGCCTL_NF);
393462306a36Sopenharmony_ci
393562306a36Sopenharmony_ci	/* At the same time start I/Q calibration for QAM constellation
393662306a36Sopenharmony_ci	 * -no need for CCK- */
393762306a36Sopenharmony_ci	ah->ah_iq_cal_needed = false;
393862306a36Sopenharmony_ci	if (!(mode == AR5K_MODE_11B)) {
393962306a36Sopenharmony_ci		ah->ah_iq_cal_needed = true;
394062306a36Sopenharmony_ci		AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ,
394162306a36Sopenharmony_ci				AR5K_PHY_IQ_CAL_NUM_LOG_MAX, 15);
394262306a36Sopenharmony_ci		AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ,
394362306a36Sopenharmony_ci				AR5K_PHY_IQ_RUN);
394462306a36Sopenharmony_ci	}
394562306a36Sopenharmony_ci
394662306a36Sopenharmony_ci	/* Wait for gain calibration to finish (we check for I/Q calibration
394762306a36Sopenharmony_ci	 * during ath5k_phy_calibrate) */
394862306a36Sopenharmony_ci	if (ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL,
394962306a36Sopenharmony_ci			AR5K_PHY_AGCCTL_CAL, 0, false)) {
395062306a36Sopenharmony_ci		ATH5K_ERR(ah, "gain calibration timeout (%uMHz)\n",
395162306a36Sopenharmony_ci			channel->center_freq);
395262306a36Sopenharmony_ci	}
395362306a36Sopenharmony_ci
395462306a36Sopenharmony_ci	/* Restore antenna mode */
395562306a36Sopenharmony_ci	ath5k_hw_set_antenna_mode(ah, ah->ah_ant_mode);
395662306a36Sopenharmony_ci
395762306a36Sopenharmony_ci	return ret;
395862306a36Sopenharmony_ci}
3959