1185377Ssam/*
2187831Ssam * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
3185377Ssam * Copyright (c) 2002-2008 Atheros Communications, Inc.
4185377Ssam *
5185377Ssam * Permission to use, copy, modify, and/or distribute this software for any
6185377Ssam * purpose with or without fee is hereby granted, provided that the above
7185377Ssam * copyright notice and this permission notice appear in all copies.
8185377Ssam *
9185377Ssam * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10185377Ssam * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11185377Ssam * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12185377Ssam * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13185377Ssam * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14185377Ssam * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15185377Ssam * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16185377Ssam *
17187831Ssam * $FreeBSD$
18185377Ssam */
19185377Ssam#include "opt_ah.h"
20185377Ssam
21185377Ssam#include "ah.h"
22185377Ssam#include "ah_internal.h"
23185377Ssam
24185377Ssam#include "ar5212/ar5212.h"
25185377Ssam#include "ar5212/ar5212reg.h"
26185377Ssam#include "ar5212/ar5212phy.h"
27185377Ssam
28185377Ssam#include "ah_eeprom_v3.h"
29185377Ssam
30185377Ssam#define AH_5212_2316
31185377Ssam#include "ar5212/ar5212.ini"
32185377Ssam
33185377Ssam#define	N(a)	(sizeof(a)/sizeof(a[0]))
34185377Ssam
35185377Ssamtypedef	RAW_DATA_STRUCT_2413 RAW_DATA_STRUCT_2316;
36185377Ssamtypedef RAW_DATA_PER_CHANNEL_2413 RAW_DATA_PER_CHANNEL_2316;
37185377Ssam#define PWR_TABLE_SIZE_2316 PWR_TABLE_SIZE_2413
38185377Ssam
39185377Ssamstruct ar2316State {
40185377Ssam	RF_HAL_FUNCS	base;		/* public state, must be first */
41185377Ssam	uint16_t	pcdacTable[PWR_TABLE_SIZE_2316];
42185377Ssam
43185377Ssam	uint32_t	Bank1Data[N(ar5212Bank1_2316)];
44185377Ssam	uint32_t	Bank2Data[N(ar5212Bank2_2316)];
45185377Ssam	uint32_t	Bank3Data[N(ar5212Bank3_2316)];
46185377Ssam	uint32_t	Bank6Data[N(ar5212Bank6_2316)];
47185377Ssam	uint32_t	Bank7Data[N(ar5212Bank7_2316)];
48185377Ssam
49185377Ssam	/*
50185377Ssam	 * Private state for reduced stack usage.
51185377Ssam	 */
52185377Ssam	/* filled out Vpd table for all pdGains (chanL) */
53185377Ssam	uint16_t vpdTable_L[MAX_NUM_PDGAINS_PER_CHANNEL]
54185377Ssam			    [MAX_PWR_RANGE_IN_HALF_DB];
55185377Ssam	/* filled out Vpd table for all pdGains (chanR) */
56185377Ssam	uint16_t vpdTable_R[MAX_NUM_PDGAINS_PER_CHANNEL]
57185377Ssam			    [MAX_PWR_RANGE_IN_HALF_DB];
58185377Ssam	/* filled out Vpd table for all pdGains (interpolated) */
59185377Ssam	uint16_t vpdTable_I[MAX_NUM_PDGAINS_PER_CHANNEL]
60185377Ssam			    [MAX_PWR_RANGE_IN_HALF_DB];
61185377Ssam};
62185377Ssam#define	AR2316(ah)	((struct ar2316State *) AH5212(ah)->ah_rfHal)
63185377Ssam
64185377Ssamextern	void ar5212ModifyRfBuffer(uint32_t *rfBuf, uint32_t reg32,
65185377Ssam		uint32_t numBits, uint32_t firstBit, uint32_t column);
66185377Ssam
67185377Ssamstatic void
68185377Ssamar2316WriteRegs(struct ath_hal *ah, u_int modesIndex, u_int freqIndex,
69185377Ssam	int regWrites)
70185377Ssam{
71185377Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
72185377Ssam
73185377Ssam	HAL_INI_WRITE_ARRAY(ah, ar5212Modes_2316, modesIndex, regWrites);
74185377Ssam	HAL_INI_WRITE_ARRAY(ah, ar5212Common_2316, 1, regWrites);
75185377Ssam	HAL_INI_WRITE_ARRAY(ah, ar5212BB_RfGain_2316, freqIndex, regWrites);
76185377Ssam
77185377Ssam	/* For AP51 */
78185377Ssam        if (!ahp->ah_cwCalRequire) {
79185377Ssam		OS_REG_WRITE(ah, 0xa358, (OS_REG_READ(ah, 0xa358) & ~0x2));
80185377Ssam        } else {
81185377Ssam		ahp->ah_cwCalRequire = AH_FALSE;
82185377Ssam        }
83185377Ssam}
84185377Ssam
85185377Ssam/*
86185377Ssam * Take the MHz channel value and set the Channel value
87185377Ssam *
88185377Ssam * ASSUMES: Writes enabled to analog bus
89185377Ssam */
90185377Ssamstatic HAL_BOOL
91187831Ssamar2316SetChannel(struct ath_hal *ah,  struct ieee80211_channel *chan)
92185377Ssam{
93187831Ssam	uint16_t freq = ath_hal_gethwchannel(ah, chan);
94185377Ssam	uint32_t channelSel  = 0;
95185377Ssam	uint32_t bModeSynth  = 0;
96185377Ssam	uint32_t aModeRefSel = 0;
97185377Ssam	uint32_t reg32       = 0;
98185377Ssam
99187831Ssam	OS_MARK(ah, AH_MARK_SETCHANNEL, freq);
100185377Ssam
101187831Ssam	if (freq < 4800) {
102185377Ssam		uint32_t txctl;
103185377Ssam
104187831Ssam		if (((freq - 2192) % 5) == 0) {
105187831Ssam			channelSel = ((freq - 672) * 2 - 3040)/10;
106185377Ssam			bModeSynth = 0;
107187831Ssam		} else if (((freq - 2224) % 5) == 0) {
108187831Ssam			channelSel = ((freq - 704) * 2 - 3040) / 10;
109185377Ssam			bModeSynth = 1;
110185377Ssam		} else {
111185377Ssam			HALDEBUG(ah, HAL_DEBUG_ANY,
112185377Ssam			    "%s: invalid channel %u MHz\n",
113187831Ssam			    __func__, freq);
114185377Ssam			return AH_FALSE;
115185377Ssam		}
116185377Ssam
117185377Ssam		channelSel = (channelSel << 2) & 0xff;
118185377Ssam		channelSel = ath_hal_reverseBits(channelSel, 8);
119185377Ssam
120185377Ssam		txctl = OS_REG_READ(ah, AR_PHY_CCK_TX_CTRL);
121187831Ssam		if (freq == 2484) {
122185377Ssam			/* Enable channel spreading for channel 14 */
123185377Ssam			OS_REG_WRITE(ah, AR_PHY_CCK_TX_CTRL,
124185377Ssam				txctl | AR_PHY_CCK_TX_CTRL_JAPAN);
125185377Ssam		} else {
126185377Ssam			OS_REG_WRITE(ah, AR_PHY_CCK_TX_CTRL,
127185377Ssam				txctl &~ AR_PHY_CCK_TX_CTRL_JAPAN);
128185377Ssam		}
129187831Ssam	} else if ((freq % 20) == 0 && freq >= 5120) {
130185377Ssam		channelSel = ath_hal_reverseBits(
131187831Ssam			((freq - 4800) / 20 << 2), 8);
132185377Ssam		aModeRefSel = ath_hal_reverseBits(3, 2);
133187831Ssam	} else if ((freq % 10) == 0) {
134185377Ssam		channelSel = ath_hal_reverseBits(
135187831Ssam			((freq - 4800) / 10 << 1), 8);
136185377Ssam		aModeRefSel = ath_hal_reverseBits(2, 2);
137187831Ssam	} else if ((freq % 5) == 0) {
138185377Ssam		channelSel = ath_hal_reverseBits(
139187831Ssam			(freq - 4800) / 5, 8);
140185377Ssam		aModeRefSel = ath_hal_reverseBits(1, 2);
141185377Ssam	} else {
142185377Ssam		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u MHz\n",
143187831Ssam		    __func__, freq);
144185377Ssam		return AH_FALSE;
145185377Ssam	}
146185377Ssam
147185377Ssam	reg32 = (channelSel << 4) | (aModeRefSel << 2) | (bModeSynth << 1) |
148185377Ssam			(1 << 12) | 0x1;
149185377Ssam	OS_REG_WRITE(ah, AR_PHY(0x27), reg32 & 0xff);
150185377Ssam
151185377Ssam	reg32 >>= 8;
152185377Ssam	OS_REG_WRITE(ah, AR_PHY(0x36), reg32 & 0x7f);
153185377Ssam
154185377Ssam	AH_PRIVATE(ah)->ah_curchan = chan;
155185377Ssam	return AH_TRUE;
156185377Ssam}
157185377Ssam
158185377Ssam/*
159185377Ssam * Reads EEPROM header info from device structure and programs
160185377Ssam * all rf registers
161185377Ssam *
162185377Ssam * REQUIRES: Access to the analog rf device
163185377Ssam */
164185377Ssamstatic HAL_BOOL
165187831Ssamar2316SetRfRegs(struct ath_hal *ah, const struct ieee80211_channel *chan,
166187831Ssam	uint16_t modesIndex, uint16_t *rfXpdGain)
167185377Ssam{
168185377Ssam#define	RF_BANK_SETUP(_priv, _ix, _col) do {				    \
169185377Ssam	int i;								    \
170185377Ssam	for (i = 0; i < N(ar5212Bank##_ix##_2316); i++)			    \
171185377Ssam		(_priv)->Bank##_ix##Data[i] = ar5212Bank##_ix##_2316[i][_col];\
172185377Ssam} while (0)
173185377Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
174185377Ssam	const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom;
175185377Ssam	uint16_t ob2GHz = 0, db2GHz = 0;
176185377Ssam	struct ar2316State *priv = AR2316(ah);
177185377Ssam	int regWrites = 0;
178185377Ssam
179187831Ssam	HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: chan %u/0x%x modesIndex %u\n",
180187831Ssam	    __func__, chan->ic_freq, chan->ic_flags, modesIndex);
181185377Ssam
182185377Ssam	HALASSERT(priv != AH_NULL);
183185377Ssam
184185377Ssam	/* Setup rf parameters */
185187831Ssam	if (IEEE80211_IS_CHAN_B(chan)) {
186185377Ssam		ob2GHz = ee->ee_obFor24;
187185377Ssam		db2GHz = ee->ee_dbFor24;
188187831Ssam	} else {
189185377Ssam		ob2GHz = ee->ee_obFor24g;
190185377Ssam		db2GHz = ee->ee_dbFor24g;
191185377Ssam	}
192185377Ssam
193185377Ssam	/* Bank 1 Write */
194185377Ssam	RF_BANK_SETUP(priv, 1, 1);
195185377Ssam
196185377Ssam	/* Bank 2 Write */
197185377Ssam	RF_BANK_SETUP(priv, 2, modesIndex);
198185377Ssam
199185377Ssam	/* Bank 3 Write */
200185377Ssam	RF_BANK_SETUP(priv, 3, modesIndex);
201185377Ssam
202185377Ssam	/* Bank 6 Write */
203185377Ssam	RF_BANK_SETUP(priv, 6, modesIndex);
204185377Ssam
205185377Ssam	ar5212ModifyRfBuffer(priv->Bank6Data, ob2GHz,   3, 178, 0);
206185377Ssam	ar5212ModifyRfBuffer(priv->Bank6Data, db2GHz,   3, 175, 0);
207185377Ssam
208185377Ssam	/* Bank 7 Setup */
209185377Ssam	RF_BANK_SETUP(priv, 7, modesIndex);
210185377Ssam
211185377Ssam	/* Write Analog registers */
212185377Ssam	HAL_INI_WRITE_BANK(ah, ar5212Bank1_2316, priv->Bank1Data, regWrites);
213185377Ssam	HAL_INI_WRITE_BANK(ah, ar5212Bank2_2316, priv->Bank2Data, regWrites);
214185377Ssam	HAL_INI_WRITE_BANK(ah, ar5212Bank3_2316, priv->Bank3Data, regWrites);
215185377Ssam	HAL_INI_WRITE_BANK(ah, ar5212Bank6_2316, priv->Bank6Data, regWrites);
216185377Ssam	HAL_INI_WRITE_BANK(ah, ar5212Bank7_2316, priv->Bank7Data, regWrites);
217185377Ssam
218185377Ssam	/* Now that we have reprogrammed rfgain value, clear the flag. */
219185377Ssam	ahp->ah_rfgainState = HAL_RFGAIN_INACTIVE;
220185377Ssam
221185377Ssam	return AH_TRUE;
222185377Ssam#undef	RF_BANK_SETUP
223185377Ssam}
224185377Ssam
225185377Ssam/*
226185377Ssam * Return a reference to the requested RF Bank.
227185377Ssam */
228185377Ssamstatic uint32_t *
229185377Ssamar2316GetRfBank(struct ath_hal *ah, int bank)
230185377Ssam{
231185377Ssam	struct ar2316State *priv = AR2316(ah);
232185377Ssam
233185377Ssam	HALASSERT(priv != AH_NULL);
234185377Ssam	switch (bank) {
235185377Ssam	case 1: return priv->Bank1Data;
236185377Ssam	case 2: return priv->Bank2Data;
237185377Ssam	case 3: return priv->Bank3Data;
238185377Ssam	case 6: return priv->Bank6Data;
239185377Ssam	case 7: return priv->Bank7Data;
240185377Ssam	}
241185377Ssam	HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown RF Bank %d requested\n",
242185377Ssam	    __func__, bank);
243185377Ssam	return AH_NULL;
244185377Ssam}
245185377Ssam
246185377Ssam/*
247185377Ssam * Return indices surrounding the value in sorted integer lists.
248185377Ssam *
249185377Ssam * NB: the input list is assumed to be sorted in ascending order
250185377Ssam */
251185377Ssamstatic void
252185377SsamGetLowerUpperIndex(int16_t v, const uint16_t *lp, uint16_t listSize,
253185377Ssam                          uint32_t *vlo, uint32_t *vhi)
254185377Ssam{
255185377Ssam	int16_t target = v;
256185377Ssam	const int16_t *ep = lp+listSize;
257185377Ssam	const int16_t *tp;
258185377Ssam
259185377Ssam	/*
260185377Ssam	 * Check first and last elements for out-of-bounds conditions.
261185377Ssam	 */
262185377Ssam	if (target < lp[0]) {
263185377Ssam		*vlo = *vhi = 0;
264185377Ssam		return;
265185377Ssam	}
266185377Ssam	if (target >= ep[-1]) {
267185377Ssam		*vlo = *vhi = listSize - 1;
268185377Ssam		return;
269185377Ssam	}
270185377Ssam
271185377Ssam	/* look for value being near or between 2 values in list */
272185377Ssam	for (tp = lp; tp < ep; tp++) {
273185377Ssam		/*
274185377Ssam		 * If value is close to the current value of the list
275185377Ssam		 * then target is not between values, it is one of the values
276185377Ssam		 */
277185377Ssam		if (*tp == target) {
278185377Ssam			*vlo = *vhi = tp - (const int16_t *) lp;
279185377Ssam			return;
280185377Ssam		}
281185377Ssam		/*
282185377Ssam		 * Look for value being between current value and next value
283185377Ssam		 * if so return these 2 values
284185377Ssam		 */
285185377Ssam		if (target < tp[1]) {
286185377Ssam			*vlo = tp - (const int16_t *) lp;
287185377Ssam			*vhi = *vlo + 1;
288185377Ssam			return;
289185377Ssam		}
290185377Ssam	}
291185377Ssam}
292185377Ssam
293185377Ssam/*
294185377Ssam * Fill the Vpdlist for indices Pmax-Pmin
295185377Ssam */
296185377Ssamstatic HAL_BOOL
297185377Ssamar2316FillVpdTable(uint32_t pdGainIdx, int16_t Pmin, int16_t  Pmax,
298185377Ssam		   const int16_t *pwrList, const int16_t *VpdList,
299185377Ssam		   uint16_t numIntercepts, uint16_t retVpdList[][64])
300185377Ssam{
301185377Ssam	uint16_t ii, jj, kk;
302185377Ssam	int16_t currPwr = (int16_t)(2*Pmin);
303185377Ssam	/* since Pmin is pwr*2 and pwrList is 4*pwr */
304185377Ssam	uint32_t  idxL, idxR;
305185377Ssam
306185377Ssam	ii = 0;
307185377Ssam	jj = 0;
308185377Ssam
309185377Ssam	if (numIntercepts < 2)
310185377Ssam		return AH_FALSE;
311185377Ssam
312185377Ssam	while (ii <= (uint16_t)(Pmax - Pmin)) {
313185377Ssam		GetLowerUpperIndex(currPwr, pwrList, numIntercepts,
314185377Ssam					 &(idxL), &(idxR));
315185377Ssam		if (idxR < 1)
316185377Ssam			idxR = 1;			/* extrapolate below */
317185377Ssam		if (idxL == (uint32_t)(numIntercepts - 1))
318185377Ssam			idxL = numIntercepts - 2;	/* extrapolate above */
319185377Ssam		if (pwrList[idxL] == pwrList[idxR])
320185377Ssam			kk = VpdList[idxL];
321185377Ssam		else
322185377Ssam			kk = (uint16_t)
323185377Ssam				(((currPwr - pwrList[idxL])*VpdList[idxR]+
324185377Ssam				  (pwrList[idxR] - currPwr)*VpdList[idxL])/
325185377Ssam				 (pwrList[idxR] - pwrList[idxL]));
326185377Ssam		retVpdList[pdGainIdx][ii] = kk;
327185377Ssam		ii++;
328185377Ssam		currPwr += 2;				/* half dB steps */
329185377Ssam	}
330185377Ssam
331185377Ssam	return AH_TRUE;
332185377Ssam}
333185377Ssam
334185377Ssam/*
335185377Ssam * Returns interpolated or the scaled up interpolated value
336185377Ssam */
337185377Ssamstatic int16_t
338185377Ssaminterpolate_signed(uint16_t target, uint16_t srcLeft, uint16_t srcRight,
339185377Ssam	int16_t targetLeft, int16_t targetRight)
340185377Ssam{
341185377Ssam	int16_t rv;
342185377Ssam
343185377Ssam	if (srcRight != srcLeft) {
344185377Ssam		rv = ((target - srcLeft)*targetRight +
345185377Ssam		      (srcRight - target)*targetLeft) / (srcRight - srcLeft);
346185377Ssam	} else {
347185377Ssam		rv = targetLeft;
348185377Ssam	}
349185377Ssam	return rv;
350185377Ssam}
351185377Ssam
352185377Ssam/*
353185377Ssam * Uses the data points read from EEPROM to reconstruct the pdadc power table
354185377Ssam * Called by ar2316SetPowerTable()
355185377Ssam */
356185377Ssamstatic int
357185377Ssamar2316getGainBoundariesAndPdadcsForPowers(struct ath_hal *ah, uint16_t channel,
358185377Ssam		const RAW_DATA_STRUCT_2316 *pRawDataset,
359185377Ssam		uint16_t pdGainOverlap_t2,
360185377Ssam		int16_t  *pMinCalPower, uint16_t pPdGainBoundaries[],
361185377Ssam		uint16_t pPdGainValues[], uint16_t pPDADCValues[])
362185377Ssam{
363185377Ssam	struct ar2316State *priv = AR2316(ah);
364185377Ssam#define	VpdTable_L	priv->vpdTable_L
365185377Ssam#define	VpdTable_R	priv->vpdTable_R
366185377Ssam#define	VpdTable_I	priv->vpdTable_I
367185377Ssam	uint32_t ii, jj, kk;
368185377Ssam	int32_t ss;/* potentially -ve index for taking care of pdGainOverlap */
369185377Ssam	uint32_t idxL, idxR;
370185377Ssam	uint32_t numPdGainsUsed = 0;
371185377Ssam	/*
372185377Ssam	 * If desired to support -ve power levels in future, just
373185377Ssam	 * change pwr_I_0 to signed 5-bits.
374185377Ssam	 */
375185377Ssam	int16_t Pmin_t2[MAX_NUM_PDGAINS_PER_CHANNEL];
376185377Ssam	/* to accomodate -ve power levels later on. */
377185377Ssam	int16_t Pmax_t2[MAX_NUM_PDGAINS_PER_CHANNEL];
378185377Ssam	/* to accomodate -ve power levels later on */
379185377Ssam	uint16_t numVpd = 0;
380185377Ssam	uint16_t Vpd_step;
381185377Ssam	int16_t tmpVal ;
382185377Ssam	uint32_t sizeCurrVpdTable, maxIndex, tgtIndex;
383185377Ssam
384185377Ssam	/* Get upper lower index */
385185377Ssam	GetLowerUpperIndex(channel, pRawDataset->pChannels,
386185377Ssam				 pRawDataset->numChannels, &(idxL), &(idxR));
387185377Ssam
388185377Ssam	for (ii = 0; ii < MAX_NUM_PDGAINS_PER_CHANNEL; ii++) {
389185377Ssam		jj = MAX_NUM_PDGAINS_PER_CHANNEL - ii - 1;
390185377Ssam		/* work backwards 'cause highest pdGain for lowest power */
391185377Ssam		numVpd = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].numVpd;
392185377Ssam		if (numVpd > 0) {
393185377Ssam			pPdGainValues[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pd_gain;
394185377Ssam			Pmin_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pwr_t4[0];
395185377Ssam			if (Pmin_t2[numPdGainsUsed] >pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[0]) {
396185377Ssam				Pmin_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[0];
397185377Ssam			}
398185377Ssam			Pmin_t2[numPdGainsUsed] = (int16_t)
399185377Ssam				(Pmin_t2[numPdGainsUsed] / 2);
400185377Ssam			Pmax_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pwr_t4[numVpd-1];
401185377Ssam			if (Pmax_t2[numPdGainsUsed] > pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[numVpd-1])
402185377Ssam				Pmax_t2[numPdGainsUsed] =
403185377Ssam					pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[numVpd-1];
404185377Ssam			Pmax_t2[numPdGainsUsed] = (int16_t)(Pmax_t2[numPdGainsUsed] / 2);
405185377Ssam			ar2316FillVpdTable(
406185377Ssam					   numPdGainsUsed, Pmin_t2[numPdGainsUsed], Pmax_t2[numPdGainsUsed],
407185377Ssam					   &(pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pwr_t4[0]),
408185377Ssam					   &(pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].Vpd[0]), numVpd, VpdTable_L
409185377Ssam					   );
410185377Ssam			ar2316FillVpdTable(
411185377Ssam					   numPdGainsUsed, Pmin_t2[numPdGainsUsed], Pmax_t2[numPdGainsUsed],
412185377Ssam					   &(pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[0]),
413185377Ssam					   &(pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].Vpd[0]), numVpd, VpdTable_R
414185377Ssam					   );
415185377Ssam			for (kk = 0; kk < (uint16_t)(Pmax_t2[numPdGainsUsed] - Pmin_t2[numPdGainsUsed]); kk++) {
416185377Ssam				VpdTable_I[numPdGainsUsed][kk] =
417185377Ssam					interpolate_signed(
418185377Ssam							   channel, pRawDataset->pChannels[idxL], pRawDataset->pChannels[idxR],
419185377Ssam							   (int16_t)VpdTable_L[numPdGainsUsed][kk], (int16_t)VpdTable_R[numPdGainsUsed][kk]);
420185377Ssam			}
421185377Ssam			/* fill VpdTable_I for this pdGain */
422185377Ssam			numPdGainsUsed++;
423185377Ssam		}
424185377Ssam		/* if this pdGain is used */
425185377Ssam	}
426185377Ssam
427185377Ssam	*pMinCalPower = Pmin_t2[0];
428185377Ssam	kk = 0; /* index for the final table */
429185377Ssam	for (ii = 0; ii < numPdGainsUsed; ii++) {
430185377Ssam		if (ii == (numPdGainsUsed - 1))
431185377Ssam			pPdGainBoundaries[ii] = Pmax_t2[ii] +
432185377Ssam				PD_GAIN_BOUNDARY_STRETCH_IN_HALF_DB;
433185377Ssam		else
434185377Ssam			pPdGainBoundaries[ii] = (uint16_t)
435185377Ssam				((Pmax_t2[ii] + Pmin_t2[ii+1]) / 2 );
436185377Ssam		if (pPdGainBoundaries[ii] > 63) {
437185377Ssam			HALDEBUG(ah, HAL_DEBUG_ANY,
438185377Ssam			    "%s: clamp pPdGainBoundaries[%d] %d\n",
439185377Ssam			    __func__, ii, pPdGainBoundaries[ii]);/*XXX*/
440185377Ssam			pPdGainBoundaries[ii] = 63;
441185377Ssam		}
442185377Ssam
443185377Ssam		/* Find starting index for this pdGain */
444185377Ssam		if (ii == 0)
445185377Ssam			ss = 0; /* for the first pdGain, start from index 0 */
446185377Ssam		else
447185377Ssam			ss = (pPdGainBoundaries[ii-1] - Pmin_t2[ii]) -
448185377Ssam				pdGainOverlap_t2;
449185377Ssam		Vpd_step = (uint16_t)(VpdTable_I[ii][1] - VpdTable_I[ii][0]);
450185377Ssam		Vpd_step = (uint16_t)((Vpd_step < 1) ? 1 : Vpd_step);
451185377Ssam		/*
452185377Ssam		 *-ve ss indicates need to extrapolate data below for this pdGain
453185377Ssam		 */
454185377Ssam		while (ss < 0) {
455185377Ssam			tmpVal = (int16_t)(VpdTable_I[ii][0] + ss*Vpd_step);
456185377Ssam			pPDADCValues[kk++] = (uint16_t)((tmpVal < 0) ? 0 : tmpVal);
457185377Ssam			ss++;
458185377Ssam		}
459185377Ssam
460185377Ssam		sizeCurrVpdTable = Pmax_t2[ii] - Pmin_t2[ii];
461185377Ssam		tgtIndex = pPdGainBoundaries[ii] + pdGainOverlap_t2 - Pmin_t2[ii];
462185377Ssam		maxIndex = (tgtIndex < sizeCurrVpdTable) ? tgtIndex : sizeCurrVpdTable;
463185377Ssam
464185377Ssam		while (ss < (int16_t)maxIndex)
465185377Ssam			pPDADCValues[kk++] = VpdTable_I[ii][ss++];
466185377Ssam
467185377Ssam		Vpd_step = (uint16_t)(VpdTable_I[ii][sizeCurrVpdTable-1] -
468185377Ssam				       VpdTable_I[ii][sizeCurrVpdTable-2]);
469185377Ssam		Vpd_step = (uint16_t)((Vpd_step < 1) ? 1 : Vpd_step);
470185377Ssam		/*
471185377Ssam		 * for last gain, pdGainBoundary == Pmax_t2, so will
472185377Ssam		 * have to extrapolate
473185377Ssam		 */
474185377Ssam		if (tgtIndex > maxIndex) {	/* need to extrapolate above */
475185377Ssam			while(ss < (int16_t)tgtIndex) {
476185377Ssam				tmpVal = (uint16_t)
477185377Ssam					(VpdTable_I[ii][sizeCurrVpdTable-1] +
478185377Ssam					 (ss-maxIndex)*Vpd_step);
479185377Ssam				pPDADCValues[kk++] = (tmpVal > 127) ?
480185377Ssam					127 : tmpVal;
481185377Ssam				ss++;
482185377Ssam			}
483185377Ssam		}				/* extrapolated above */
484185377Ssam	}					/* for all pdGainUsed */
485185377Ssam
486185377Ssam	while (ii < MAX_NUM_PDGAINS_PER_CHANNEL) {
487185377Ssam		pPdGainBoundaries[ii] = pPdGainBoundaries[ii-1];
488185377Ssam		ii++;
489185377Ssam	}
490185377Ssam	while (kk < 128) {
491185377Ssam		pPDADCValues[kk] = pPDADCValues[kk-1];
492185377Ssam		kk++;
493185377Ssam	}
494185377Ssam
495185377Ssam	return numPdGainsUsed;
496185377Ssam#undef VpdTable_L
497185377Ssam#undef VpdTable_R
498185377Ssam#undef VpdTable_I
499185377Ssam}
500185377Ssam
501185377Ssamstatic HAL_BOOL
502185377Ssamar2316SetPowerTable(struct ath_hal *ah,
503187831Ssam	int16_t *minPower, int16_t *maxPower,
504187831Ssam	const struct ieee80211_channel *chan,
505185377Ssam	uint16_t *rfXpdGain)
506185377Ssam{
507185377Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
508185377Ssam	const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom;
509185377Ssam	const RAW_DATA_STRUCT_2316 *pRawDataset = AH_NULL;
510185377Ssam	uint16_t pdGainOverlap_t2;
511185377Ssam	int16_t minCalPower2316_t2;
512185377Ssam	uint16_t *pdadcValues = ahp->ah_pcdacTable;
513185377Ssam	uint16_t gainBoundaries[4];
514185380Ssam	uint32_t reg32, regoffset;
515185380Ssam	int i, numPdGainsUsed;
516185380Ssam#ifndef AH_USE_INIPDGAIN
517185380Ssam	uint32_t tpcrg1;
518185380Ssam#endif
519185377Ssam
520185377Ssam	HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: chan 0x%x flag 0x%x\n",
521187831Ssam	    __func__, chan->ic_freq, chan->ic_flags);
522185377Ssam
523187831Ssam	if (IEEE80211_IS_CHAN_G(chan) || IEEE80211_IS_CHAN_108G(chan))
524185377Ssam		pRawDataset = &ee->ee_rawDataset2413[headerInfo11G];
525187831Ssam	else if (IEEE80211_IS_CHAN_B(chan))
526185377Ssam		pRawDataset = &ee->ee_rawDataset2413[headerInfo11B];
527185377Ssam	else {
528185377Ssam		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: illegal mode\n", __func__);
529185377Ssam		return AH_FALSE;
530185377Ssam	}
531185377Ssam
532185377Ssam	pdGainOverlap_t2 = (uint16_t) SM(OS_REG_READ(ah, AR_PHY_TPCRG5),
533185377Ssam					  AR_PHY_TPCRG5_PD_GAIN_OVERLAP);
534185377Ssam
535185377Ssam	numPdGainsUsed = ar2316getGainBoundariesAndPdadcsForPowers(ah,
536185377Ssam		chan->channel, pRawDataset, pdGainOverlap_t2,
537185377Ssam		&minCalPower2316_t2,gainBoundaries, rfXpdGain, pdadcValues);
538185377Ssam	HALASSERT(1 <= numPdGainsUsed && numPdGainsUsed <= 3);
539185377Ssam
540185380Ssam#ifdef AH_USE_INIPDGAIN
541185380Ssam	/*
542185380Ssam	 * Use pd_gains curve from eeprom; Atheros always uses
543185380Ssam	 * the default curve from the ini file but some vendors
544185380Ssam	 * (e.g. Zcomax) want to override this curve and not
545185380Ssam	 * honoring their settings results in tx power 5dBm low.
546185380Ssam	 */
547185377Ssam	OS_REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_NUM_PD_GAIN,
548185377Ssam			 (pRawDataset->pDataPerChannel[0].numPdGains - 1));
549185380Ssam#else
550185377Ssam	tpcrg1 = OS_REG_READ(ah, AR_PHY_TPCRG1);
551185377Ssam	tpcrg1 = (tpcrg1 &~ AR_PHY_TPCRG1_NUM_PD_GAIN)
552185377Ssam		  | SM(numPdGainsUsed-1, AR_PHY_TPCRG1_NUM_PD_GAIN);
553185377Ssam	switch (numPdGainsUsed) {
554185377Ssam	case 3:
555185377Ssam		tpcrg1 &= ~AR_PHY_TPCRG1_PDGAIN_SETTING3;
556185377Ssam		tpcrg1 |= SM(rfXpdGain[2], AR_PHY_TPCRG1_PDGAIN_SETTING3);
557185377Ssam		/* fall thru... */
558185377Ssam	case 2:
559185377Ssam		tpcrg1 &= ~AR_PHY_TPCRG1_PDGAIN_SETTING2;
560185377Ssam		tpcrg1 |= SM(rfXpdGain[1], AR_PHY_TPCRG1_PDGAIN_SETTING2);
561185377Ssam		/* fall thru... */
562185377Ssam	case 1:
563185377Ssam		tpcrg1 &= ~AR_PHY_TPCRG1_PDGAIN_SETTING1;
564185377Ssam		tpcrg1 |= SM(rfXpdGain[0], AR_PHY_TPCRG1_PDGAIN_SETTING1);
565185377Ssam		break;
566185377Ssam	}
567185377Ssam#ifdef AH_DEBUG
568185377Ssam	if (tpcrg1 != OS_REG_READ(ah, AR_PHY_TPCRG1))
569185377Ssam		HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: using non-default "
570185377Ssam		    "pd_gains (default 0x%x, calculated 0x%x)\n",
571185377Ssam		    __func__, OS_REG_READ(ah, AR_PHY_TPCRG1), tpcrg1);
572185377Ssam#endif
573185377Ssam	OS_REG_WRITE(ah, AR_PHY_TPCRG1, tpcrg1);
574185380Ssam#endif
575185377Ssam
576185377Ssam	/*
577185377Ssam	 * Note the pdadc table may not start at 0 dBm power, could be
578185377Ssam	 * negative or greater than 0.  Need to offset the power
579185377Ssam	 * values by the amount of minPower for griffin
580185377Ssam	 */
581185377Ssam	if (minCalPower2316_t2 != 0)
582185377Ssam		ahp->ah_txPowerIndexOffset = (int16_t)(0 - minCalPower2316_t2);
583185377Ssam	else
584185377Ssam		ahp->ah_txPowerIndexOffset = 0;
585185377Ssam
586185377Ssam	/* Finally, write the power values into the baseband power table */
587185377Ssam	regoffset = 0x9800 + (672 <<2); /* beginning of pdadc table in griffin */
588185377Ssam	for (i = 0; i < 32; i++) {
589185377Ssam		reg32 = ((pdadcValues[4*i + 0] & 0xFF) << 0)  |
590185377Ssam			((pdadcValues[4*i + 1] & 0xFF) << 8)  |
591185377Ssam			((pdadcValues[4*i + 2] & 0xFF) << 16) |
592185377Ssam			((pdadcValues[4*i + 3] & 0xFF) << 24) ;
593185377Ssam		OS_REG_WRITE(ah, regoffset, reg32);
594185377Ssam		regoffset += 4;
595185377Ssam	}
596185377Ssam
597185377Ssam	OS_REG_WRITE(ah, AR_PHY_TPCRG5,
598185377Ssam		     SM(pdGainOverlap_t2, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) |
599185377Ssam		     SM(gainBoundaries[0], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1) |
600185377Ssam		     SM(gainBoundaries[1], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2) |
601185377Ssam		     SM(gainBoundaries[2], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3) |
602185377Ssam		     SM(gainBoundaries[3], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4));
603185377Ssam
604185377Ssam	return AH_TRUE;
605185377Ssam}
606185377Ssam
607185377Ssamstatic int16_t
608185377Ssamar2316GetMinPower(struct ath_hal *ah, const RAW_DATA_PER_CHANNEL_2316 *data)
609185377Ssam{
610185377Ssam	uint32_t ii,jj;
611185377Ssam	uint16_t Pmin=0,numVpd;
612185377Ssam
613185377Ssam	for (ii = 0; ii < MAX_NUM_PDGAINS_PER_CHANNEL; ii++) {
614185377Ssam		jj = MAX_NUM_PDGAINS_PER_CHANNEL - ii - 1;
615185377Ssam		/* work backwards 'cause highest pdGain for lowest power */
616185377Ssam		numVpd = data->pDataPerPDGain[jj].numVpd;
617185377Ssam		if (numVpd > 0) {
618185377Ssam			Pmin = data->pDataPerPDGain[jj].pwr_t4[0];
619185377Ssam			return(Pmin);
620185377Ssam		}
621185377Ssam	}
622185377Ssam	return(Pmin);
623185377Ssam}
624185377Ssam
625185377Ssamstatic int16_t
626185377Ssamar2316GetMaxPower(struct ath_hal *ah, const RAW_DATA_PER_CHANNEL_2316 *data)
627185377Ssam{
628185377Ssam	uint32_t ii;
629185377Ssam	uint16_t Pmax=0,numVpd;
630185377Ssam
631185377Ssam	for (ii=0; ii< MAX_NUM_PDGAINS_PER_CHANNEL; ii++) {
632185377Ssam		/* work forwards cuase lowest pdGain for highest power */
633185377Ssam		numVpd = data->pDataPerPDGain[ii].numVpd;
634185377Ssam		if (numVpd > 0) {
635185377Ssam			Pmax = data->pDataPerPDGain[ii].pwr_t4[numVpd-1];
636185377Ssam			return(Pmax);
637185377Ssam		}
638185377Ssam	}
639185377Ssam	return(Pmax);
640185377Ssam}
641185377Ssam
642185377Ssamstatic HAL_BOOL
643187831Ssamar2316GetChannelMaxMinPower(struct ath_hal *ah,
644187831Ssam	const struct ieee80211_channel *chan,
645185377Ssam	int16_t *maxPow, int16_t *minPow)
646185377Ssam{
647187831Ssam	uint16_t freq = chan->ic_freq;		/* NB: never mapped */
648185377Ssam	const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom;
649185377Ssam	const RAW_DATA_STRUCT_2316 *pRawDataset = AH_NULL;
650185377Ssam	const RAW_DATA_PER_CHANNEL_2316 *data=AH_NULL;
651185377Ssam	uint16_t numChannels;
652185377Ssam	int totalD,totalF, totalMin,last, i;
653185377Ssam
654185377Ssam	*maxPow = 0;
655185377Ssam
656187831Ssam	if (IEEE80211_IS_CHAN_G(chan) || IEEE80211_IS_CHAN_108G(chan))
657185377Ssam		pRawDataset = &ee->ee_rawDataset2413[headerInfo11G];
658187831Ssam	else if (IEEE80211_IS_CHAN_B(chan))
659185377Ssam		pRawDataset = &ee->ee_rawDataset2413[headerInfo11B];
660185377Ssam	else
661185377Ssam		return(AH_FALSE);
662185377Ssam
663185377Ssam	numChannels = pRawDataset->numChannels;
664185377Ssam	data = pRawDataset->pDataPerChannel;
665185377Ssam
666185377Ssam	/* Make sure the channel is in the range of the TP values
667185377Ssam	 *  (freq piers)
668185377Ssam	 */
669185377Ssam	if (numChannels < 1)
670185377Ssam		return(AH_FALSE);
671185377Ssam
672187831Ssam	if ((freq < data[0].channelValue) ||
673187831Ssam	    (freq > data[numChannels-1].channelValue)) {
674187831Ssam		if (freq < data[0].channelValue) {
675185377Ssam			*maxPow = ar2316GetMaxPower(ah, &data[0]);
676185377Ssam			*minPow = ar2316GetMinPower(ah, &data[0]);
677185377Ssam			return(AH_TRUE);
678185377Ssam		} else {
679185377Ssam			*maxPow = ar2316GetMaxPower(ah, &data[numChannels - 1]);
680185377Ssam			*minPow = ar2316GetMinPower(ah, &data[numChannels - 1]);
681185377Ssam			return(AH_TRUE);
682185377Ssam		}
683185377Ssam	}
684185377Ssam
685185377Ssam	/* Linearly interpolate the power value now */
686187831Ssam	for (last=0,i=0; (i<numChannels) && (freq > data[i].channelValue);
687185377Ssam	     last = i++);
688185377Ssam	totalD = data[i].channelValue - data[last].channelValue;
689185377Ssam	if (totalD > 0) {
690185377Ssam		totalF = ar2316GetMaxPower(ah, &data[i]) - ar2316GetMaxPower(ah, &data[last]);
691187831Ssam		*maxPow = (int8_t) ((totalF*(freq-data[last].channelValue) +
692185377Ssam				     ar2316GetMaxPower(ah, &data[last])*totalD)/totalD);
693185377Ssam		totalMin = ar2316GetMinPower(ah, &data[i]) - ar2316GetMinPower(ah, &data[last]);
694187831Ssam		*minPow = (int8_t) ((totalMin*(freq-data[last].channelValue) +
695185377Ssam				     ar2316GetMinPower(ah, &data[last])*totalD)/totalD);
696185377Ssam		return(AH_TRUE);
697185377Ssam	} else {
698187831Ssam		if (freq == data[i].channelValue) {
699185377Ssam			*maxPow = ar2316GetMaxPower(ah, &data[i]);
700185377Ssam			*minPow = ar2316GetMinPower(ah, &data[i]);
701185377Ssam			return(AH_TRUE);
702185377Ssam		} else
703185377Ssam			return(AH_FALSE);
704185377Ssam	}
705185377Ssam}
706185377Ssam
707185377Ssam/*
708185377Ssam * Free memory for analog bank scratch buffers
709185377Ssam */
710185377Ssamstatic void
711185377Ssamar2316RfDetach(struct ath_hal *ah)
712185377Ssam{
713185377Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
714185377Ssam
715185377Ssam	HALASSERT(ahp->ah_rfHal != AH_NULL);
716185377Ssam	ath_hal_free(ahp->ah_rfHal);
717185377Ssam	ahp->ah_rfHal = AH_NULL;
718185377Ssam}
719185377Ssam
720185377Ssam/*
721185377Ssam * Allocate memory for private state.
722185377Ssam * Scratch Buffer will be reinitialized every reset so no need to zero now
723185377Ssam */
724185406Ssamstatic HAL_BOOL
725185377Ssamar2316RfAttach(struct ath_hal *ah, HAL_STATUS *status)
726185377Ssam{
727185377Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
728185377Ssam	struct ar2316State *priv;
729185377Ssam
730185377Ssam	HALASSERT(ah->ah_magic == AR5212_MAGIC);
731185377Ssam
732185377Ssam	HALASSERT(ahp->ah_rfHal == AH_NULL);
733185377Ssam	priv = ath_hal_malloc(sizeof(struct ar2316State));
734185377Ssam	if (priv == AH_NULL) {
735185377Ssam		HALDEBUG(ah, HAL_DEBUG_ANY,
736185377Ssam		    "%s: cannot allocate private state\n", __func__);
737185377Ssam		*status = HAL_ENOMEM;		/* XXX */
738185377Ssam		return AH_FALSE;
739185377Ssam	}
740185377Ssam	priv->base.rfDetach		= ar2316RfDetach;
741185377Ssam	priv->base.writeRegs		= ar2316WriteRegs;
742185377Ssam	priv->base.getRfBank		= ar2316GetRfBank;
743185377Ssam	priv->base.setChannel		= ar2316SetChannel;
744185377Ssam	priv->base.setRfRegs		= ar2316SetRfRegs;
745185377Ssam	priv->base.setPowerTable	= ar2316SetPowerTable;
746185377Ssam	priv->base.getChannelMaxMinPower = ar2316GetChannelMaxMinPower;
747185377Ssam	priv->base.getNfAdjust		= ar5212GetNfAdjust;
748185377Ssam
749185377Ssam	ahp->ah_pcdacTable = priv->pcdacTable;
750185377Ssam	ahp->ah_pcdacTableSize = sizeof(priv->pcdacTable);
751185377Ssam	ahp->ah_rfHal = &priv->base;
752185377Ssam
753185377Ssam	ahp->ah_cwCalRequire = AH_TRUE;		/* force initial cal */
754185377Ssam
755185377Ssam	return AH_TRUE;
756185377Ssam}
757185406Ssam
758185406Ssamstatic HAL_BOOL
759185406Ssamar2316Probe(struct ath_hal *ah)
760185406Ssam{
761185406Ssam	return IS_2316(ah);
762185406Ssam}
763185418SsamAH_RF(RF2316, ar2316Probe, ar2316RfAttach);
764