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_2317
31185377Ssam#include "ar5212/ar5212.ini"
32185377Ssam
33185377Ssam#define	N(a)	(sizeof(a)/sizeof(a[0]))
34185377Ssam
35185377Ssamtypedef	RAW_DATA_STRUCT_2413 RAW_DATA_STRUCT_2317;
36185377Ssamtypedef RAW_DATA_PER_CHANNEL_2413 RAW_DATA_PER_CHANNEL_2317;
37185377Ssam#define PWR_TABLE_SIZE_2317 PWR_TABLE_SIZE_2413
38185377Ssam
39185377Ssamstruct ar2317State {
40185377Ssam	RF_HAL_FUNCS	base;		/* public state, must be first */
41185377Ssam	uint16_t	pcdacTable[PWR_TABLE_SIZE_2317];
42185377Ssam
43185377Ssam	uint32_t	Bank1Data[N(ar5212Bank1_2317)];
44185377Ssam	uint32_t	Bank2Data[N(ar5212Bank2_2317)];
45185377Ssam	uint32_t	Bank3Data[N(ar5212Bank3_2317)];
46185377Ssam	uint32_t	Bank6Data[N(ar5212Bank6_2317)];
47185377Ssam	uint32_t	Bank7Data[N(ar5212Bank7_2317)];
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	AR2317(ah)	((struct ar2317State *) 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
68185377Ssamar2317WriteRegs(struct ath_hal *ah, u_int modesIndex, u_int freqIndex,
69185377Ssam	int writes)
70185377Ssam{
71185377Ssam	HAL_INI_WRITE_ARRAY(ah, ar5212Modes_2317, modesIndex, writes);
72185377Ssam	HAL_INI_WRITE_ARRAY(ah, ar5212Common_2317, 1, writes);
73185377Ssam	HAL_INI_WRITE_ARRAY(ah, ar5212BB_RfGain_2317, freqIndex, writes);
74185377Ssam}
75185377Ssam
76185377Ssam/*
77185377Ssam * Take the MHz channel value and set the Channel value
78185377Ssam *
79185377Ssam * ASSUMES: Writes enabled to analog bus
80185377Ssam */
81185377Ssamstatic HAL_BOOL
82187831Ssamar2317SetChannel(struct ath_hal *ah,  const struct ieee80211_channel *chan)
83185377Ssam{
84187831Ssam	uint16_t freq = ath_hal_gethwchannel(ah, chan);
85185377Ssam	uint32_t channelSel  = 0;
86185377Ssam	uint32_t bModeSynth  = 0;
87185377Ssam	uint32_t aModeRefSel = 0;
88185377Ssam	uint32_t reg32       = 0;
89185377Ssam
90187831Ssam	OS_MARK(ah, AH_MARK_SETCHANNEL, freq);
91185377Ssam
92187831Ssam	if (freq < 4800) {
93185377Ssam		uint32_t txctl;
94187831Ssam		channelSel = freq - 2272 ;
95185377Ssam		channelSel = ath_hal_reverseBits(channelSel, 8);
96185377Ssam
97185377Ssam		txctl = OS_REG_READ(ah, AR_PHY_CCK_TX_CTRL);
98187831Ssam		if (freq == 2484) {
99185377Ssam			/* Enable channel spreading for channel 14 */
100185377Ssam			OS_REG_WRITE(ah, AR_PHY_CCK_TX_CTRL,
101185377Ssam				txctl | AR_PHY_CCK_TX_CTRL_JAPAN);
102185377Ssam		} else {
103185377Ssam			OS_REG_WRITE(ah, AR_PHY_CCK_TX_CTRL,
104185377Ssam				txctl &~ AR_PHY_CCK_TX_CTRL_JAPAN);
105185377Ssam		}
106187831Ssam	} else if ((freq % 20) == 0 && freq >= 5120) {
107185377Ssam		channelSel = ath_hal_reverseBits(
108187831Ssam			((freq - 4800) / 20 << 2), 8);
109185377Ssam		aModeRefSel = ath_hal_reverseBits(3, 2);
110187831Ssam	} else if ((freq % 10) == 0) {
111185377Ssam		channelSel = ath_hal_reverseBits(
112187831Ssam			((freq - 4800) / 10 << 1), 8);
113185377Ssam		aModeRefSel = ath_hal_reverseBits(2, 2);
114187831Ssam	} else if ((freq % 5) == 0) {
115185377Ssam		channelSel = ath_hal_reverseBits(
116187831Ssam			(freq - 4800) / 5, 8);
117185377Ssam		aModeRefSel = ath_hal_reverseBits(1, 2);
118185377Ssam	} else {
119185377Ssam		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u MHz\n",
120187831Ssam		    __func__, freq);
121185377Ssam		return AH_FALSE;
122185377Ssam	}
123185377Ssam
124185377Ssam	reg32 = (channelSel << 4) | (aModeRefSel << 2) | (bModeSynth << 1) |
125185377Ssam			(1 << 12) | 0x1;
126185377Ssam	OS_REG_WRITE(ah, AR_PHY(0x27), reg32 & 0xff);
127185377Ssam
128185377Ssam	reg32 >>= 8;
129185377Ssam	OS_REG_WRITE(ah, AR_PHY(0x36), reg32 & 0x7f);
130185377Ssam
131185377Ssam	AH_PRIVATE(ah)->ah_curchan = chan;
132185377Ssam	return AH_TRUE;
133185377Ssam}
134185377Ssam
135185377Ssam/*
136185377Ssam * Reads EEPROM header info from device structure and programs
137185377Ssam * all rf registers
138185377Ssam *
139185377Ssam * REQUIRES: Access to the analog rf device
140185377Ssam */
141185377Ssamstatic HAL_BOOL
142187831Ssamar2317SetRfRegs(struct ath_hal *ah,
143187831Ssam	const struct ieee80211_channel *chan,
144187831Ssam	uint16_t modesIndex, uint16_t *rfXpdGain)
145185377Ssam{
146185377Ssam#define	RF_BANK_SETUP(_priv, _ix, _col) do {				    \
147185377Ssam	int i;								    \
148185377Ssam	for (i = 0; i < N(ar5212Bank##_ix##_2317); i++)			    \
149185377Ssam		(_priv)->Bank##_ix##Data[i] = ar5212Bank##_ix##_2317[i][_col];\
150185377Ssam} while (0)
151185377Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
152185377Ssam	const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom;
153185377Ssam	uint16_t ob2GHz = 0, db2GHz = 0;
154185377Ssam	struct ar2317State *priv = AR2317(ah);
155185377Ssam	int regWrites = 0;
156185377Ssam
157187831Ssam	HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: chan %u/0x%x modesIndex %u\n",
158187831Ssam	    __func__, chan->ic_freq, chan->ic_flags, modesIndex);
159185377Ssam
160185377Ssam	HALASSERT(priv);
161185377Ssam
162185377Ssam	/* Setup rf parameters */
163187831Ssam	if (IEEE80211_IS_CHAN_B(chan)) {
164185377Ssam		ob2GHz = ee->ee_obFor24;
165185377Ssam		db2GHz = ee->ee_dbFor24;
166187831Ssam	} else {
167185377Ssam		ob2GHz = ee->ee_obFor24g;
168185377Ssam		db2GHz = ee->ee_dbFor24g;
169185377Ssam	}
170185377Ssam
171185377Ssam	/* Bank 1 Write */
172185377Ssam	RF_BANK_SETUP(priv, 1, 1);
173185377Ssam
174185377Ssam	/* Bank 2 Write */
175185377Ssam	RF_BANK_SETUP(priv, 2, modesIndex);
176185377Ssam
177185377Ssam	/* Bank 3 Write */
178185377Ssam	RF_BANK_SETUP(priv, 3, modesIndex);
179185377Ssam
180185377Ssam	/* Bank 6 Write */
181185377Ssam	RF_BANK_SETUP(priv, 6, modesIndex);
182185377Ssam
183185377Ssam	ar5212ModifyRfBuffer(priv->Bank6Data, ob2GHz,   3, 193, 0);
184185377Ssam	ar5212ModifyRfBuffer(priv->Bank6Data, db2GHz,   3, 190, 0);
185185377Ssam
186185377Ssam	/* Bank 7 Setup */
187185377Ssam	RF_BANK_SETUP(priv, 7, modesIndex);
188185377Ssam
189185377Ssam	/* Write Analog registers */
190185377Ssam	HAL_INI_WRITE_BANK(ah, ar5212Bank1_2317, priv->Bank1Data, regWrites);
191185377Ssam	HAL_INI_WRITE_BANK(ah, ar5212Bank2_2317, priv->Bank2Data, regWrites);
192185377Ssam	HAL_INI_WRITE_BANK(ah, ar5212Bank3_2317, priv->Bank3Data, regWrites);
193185377Ssam	HAL_INI_WRITE_BANK(ah, ar5212Bank6_2317, priv->Bank6Data, regWrites);
194185377Ssam	HAL_INI_WRITE_BANK(ah, ar5212Bank7_2317, priv->Bank7Data, regWrites);
195185377Ssam	/* Now that we have reprogrammed rfgain value, clear the flag. */
196185377Ssam	ahp->ah_rfgainState = HAL_RFGAIN_INACTIVE;
197185377Ssam
198185377Ssam	return AH_TRUE;
199185377Ssam#undef	RF_BANK_SETUP
200185377Ssam}
201185377Ssam
202185377Ssam/*
203185377Ssam * Return a reference to the requested RF Bank.
204185377Ssam */
205185377Ssamstatic uint32_t *
206185377Ssamar2317GetRfBank(struct ath_hal *ah, int bank)
207185377Ssam{
208185377Ssam	struct ar2317State *priv = AR2317(ah);
209185377Ssam
210185377Ssam	HALASSERT(priv != AH_NULL);
211185377Ssam	switch (bank) {
212185377Ssam	case 1: return priv->Bank1Data;
213185377Ssam	case 2: return priv->Bank2Data;
214185377Ssam	case 3: return priv->Bank3Data;
215185377Ssam	case 6: return priv->Bank6Data;
216185377Ssam	case 7: return priv->Bank7Data;
217185377Ssam	}
218185377Ssam	HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown RF Bank %d requested\n",
219185377Ssam	    __func__, bank);
220185377Ssam	return AH_NULL;
221185377Ssam}
222185377Ssam
223185377Ssam/*
224185377Ssam * Return indices surrounding the value in sorted integer lists.
225185377Ssam *
226185377Ssam * NB: the input list is assumed to be sorted in ascending order
227185377Ssam */
228185377Ssamstatic void
229185377SsamGetLowerUpperIndex(int16_t v, const uint16_t *lp, uint16_t listSize,
230185377Ssam                          uint32_t *vlo, uint32_t *vhi)
231185377Ssam{
232185377Ssam	int16_t target = v;
233185377Ssam	const int16_t *ep = lp+listSize;
234185377Ssam	const int16_t *tp;
235185377Ssam
236185377Ssam	/*
237185377Ssam	 * Check first and last elements for out-of-bounds conditions.
238185377Ssam	 */
239185377Ssam	if (target < lp[0]) {
240185377Ssam		*vlo = *vhi = 0;
241185377Ssam		return;
242185377Ssam	}
243185377Ssam	if (target >= ep[-1]) {
244185377Ssam		*vlo = *vhi = listSize - 1;
245185377Ssam		return;
246185377Ssam	}
247185377Ssam
248185377Ssam	/* look for value being near or between 2 values in list */
249185377Ssam	for (tp = lp; tp < ep; tp++) {
250185377Ssam		/*
251185377Ssam		 * If value is close to the current value of the list
252185377Ssam		 * then target is not between values, it is one of the values
253185377Ssam		 */
254185377Ssam		if (*tp == target) {
255185377Ssam			*vlo = *vhi = tp - (const int16_t *) lp;
256185377Ssam			return;
257185377Ssam		}
258185377Ssam		/*
259185377Ssam		 * Look for value being between current value and next value
260185377Ssam		 * if so return these 2 values
261185377Ssam		 */
262185377Ssam		if (target < tp[1]) {
263185377Ssam			*vlo = tp - (const int16_t *) lp;
264185377Ssam			*vhi = *vlo + 1;
265185377Ssam			return;
266185377Ssam		}
267185377Ssam	}
268185377Ssam}
269185377Ssam
270185377Ssam/*
271185377Ssam * Fill the Vpdlist for indices Pmax-Pmin
272185377Ssam */
273185377Ssamstatic HAL_BOOL
274185377Ssamar2317FillVpdTable(uint32_t pdGainIdx, int16_t Pmin, int16_t  Pmax,
275185377Ssam		   const int16_t *pwrList, const int16_t *VpdList,
276185377Ssam		   uint16_t numIntercepts, uint16_t retVpdList[][64])
277185377Ssam{
278185377Ssam	uint16_t ii, jj, kk;
279185377Ssam	int16_t currPwr = (int16_t)(2*Pmin);
280185377Ssam	/* since Pmin is pwr*2 and pwrList is 4*pwr */
281185377Ssam	uint32_t  idxL, idxR;
282185377Ssam
283185377Ssam	ii = 0;
284185377Ssam	jj = 0;
285185377Ssam
286185377Ssam	if (numIntercepts < 2)
287185377Ssam		return AH_FALSE;
288185377Ssam
289185377Ssam	while (ii <= (uint16_t)(Pmax - Pmin)) {
290185377Ssam		GetLowerUpperIndex(currPwr, pwrList, numIntercepts,
291185377Ssam					 &(idxL), &(idxR));
292185377Ssam		if (idxR < 1)
293185377Ssam			idxR = 1;			/* extrapolate below */
294185377Ssam		if (idxL == (uint32_t)(numIntercepts - 1))
295185377Ssam			idxL = numIntercepts - 2;	/* extrapolate above */
296185377Ssam		if (pwrList[idxL] == pwrList[idxR])
297185377Ssam			kk = VpdList[idxL];
298185377Ssam		else
299185377Ssam			kk = (uint16_t)
300185377Ssam				(((currPwr - pwrList[idxL])*VpdList[idxR]+
301185377Ssam				  (pwrList[idxR] - currPwr)*VpdList[idxL])/
302185377Ssam				 (pwrList[idxR] - pwrList[idxL]));
303185377Ssam		retVpdList[pdGainIdx][ii] = kk;
304185377Ssam		ii++;
305185377Ssam		currPwr += 2;				/* half dB steps */
306185377Ssam	}
307185377Ssam
308185377Ssam	return AH_TRUE;
309185377Ssam}
310185377Ssam
311185377Ssam/*
312185377Ssam * Returns interpolated or the scaled up interpolated value
313185377Ssam */
314185377Ssamstatic int16_t
315185377Ssaminterpolate_signed(uint16_t target, uint16_t srcLeft, uint16_t srcRight,
316185377Ssam	int16_t targetLeft, int16_t targetRight)
317185377Ssam{
318185377Ssam	int16_t rv;
319185377Ssam
320185377Ssam	if (srcRight != srcLeft) {
321185377Ssam		rv = ((target - srcLeft)*targetRight +
322185377Ssam		      (srcRight - target)*targetLeft) / (srcRight - srcLeft);
323185377Ssam	} else {
324185377Ssam		rv = targetLeft;
325185377Ssam	}
326185377Ssam	return rv;
327185377Ssam}
328185377Ssam
329185377Ssam/*
330185377Ssam * Uses the data points read from EEPROM to reconstruct the pdadc power table
331185377Ssam * Called by ar2317SetPowerTable()
332185377Ssam */
333185377Ssamstatic int
334185377Ssamar2317getGainBoundariesAndPdadcsForPowers(struct ath_hal *ah, uint16_t channel,
335185377Ssam		const RAW_DATA_STRUCT_2317 *pRawDataset,
336185377Ssam		uint16_t pdGainOverlap_t2,
337185377Ssam		int16_t  *pMinCalPower, uint16_t pPdGainBoundaries[],
338185377Ssam		uint16_t pPdGainValues[], uint16_t pPDADCValues[])
339185377Ssam{
340185377Ssam	struct ar2317State *priv = AR2317(ah);
341185377Ssam#define	VpdTable_L	priv->vpdTable_L
342185377Ssam#define	VpdTable_R	priv->vpdTable_R
343185377Ssam#define	VpdTable_I	priv->vpdTable_I
344185377Ssam	/* XXX excessive stack usage? */
345185377Ssam	uint32_t ii, jj, kk;
346185377Ssam	int32_t ss;/* potentially -ve index for taking care of pdGainOverlap */
347185377Ssam	uint32_t idxL, idxR;
348185377Ssam	uint32_t numPdGainsUsed = 0;
349185377Ssam	/*
350185377Ssam	 * If desired to support -ve power levels in future, just
351185377Ssam	 * change pwr_I_0 to signed 5-bits.
352185377Ssam	 */
353185377Ssam	int16_t Pmin_t2[MAX_NUM_PDGAINS_PER_CHANNEL];
354185377Ssam	/* to accomodate -ve power levels later on. */
355185377Ssam	int16_t Pmax_t2[MAX_NUM_PDGAINS_PER_CHANNEL];
356185377Ssam	/* to accomodate -ve power levels later on */
357185377Ssam	uint16_t numVpd = 0;
358185377Ssam	uint16_t Vpd_step;
359185377Ssam	int16_t tmpVal ;
360185377Ssam	uint32_t sizeCurrVpdTable, maxIndex, tgtIndex;
361185377Ssam
362185377Ssam	/* Get upper lower index */
363185377Ssam	GetLowerUpperIndex(channel, pRawDataset->pChannels,
364185377Ssam				 pRawDataset->numChannels, &(idxL), &(idxR));
365185377Ssam
366185377Ssam	for (ii = 0; ii < MAX_NUM_PDGAINS_PER_CHANNEL; ii++) {
367185377Ssam		jj = MAX_NUM_PDGAINS_PER_CHANNEL - ii - 1;
368185377Ssam		/* work backwards 'cause highest pdGain for lowest power */
369185377Ssam		numVpd = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].numVpd;
370185377Ssam		if (numVpd > 0) {
371185377Ssam			pPdGainValues[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pd_gain;
372185377Ssam			Pmin_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pwr_t4[0];
373185377Ssam			if (Pmin_t2[numPdGainsUsed] >pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[0]) {
374185377Ssam				Pmin_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[0];
375185377Ssam			}
376185377Ssam			Pmin_t2[numPdGainsUsed] = (int16_t)
377185377Ssam				(Pmin_t2[numPdGainsUsed] / 2);
378185377Ssam			Pmax_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pwr_t4[numVpd-1];
379185377Ssam			if (Pmax_t2[numPdGainsUsed] > pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[numVpd-1])
380185377Ssam				Pmax_t2[numPdGainsUsed] =
381185377Ssam					pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[numVpd-1];
382185377Ssam			Pmax_t2[numPdGainsUsed] = (int16_t)(Pmax_t2[numPdGainsUsed] / 2);
383185377Ssam			ar2317FillVpdTable(
384185377Ssam					   numPdGainsUsed, Pmin_t2[numPdGainsUsed], Pmax_t2[numPdGainsUsed],
385185377Ssam					   &(pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pwr_t4[0]),
386185377Ssam					   &(pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].Vpd[0]), numVpd, VpdTable_L
387185377Ssam					   );
388185377Ssam			ar2317FillVpdTable(
389185377Ssam					   numPdGainsUsed, Pmin_t2[numPdGainsUsed], Pmax_t2[numPdGainsUsed],
390185377Ssam					   &(pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[0]),
391185377Ssam					   &(pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].Vpd[0]), numVpd, VpdTable_R
392185377Ssam					   );
393185377Ssam			for (kk = 0; kk < (uint16_t)(Pmax_t2[numPdGainsUsed] - Pmin_t2[numPdGainsUsed]); kk++) {
394185377Ssam				VpdTable_I[numPdGainsUsed][kk] =
395185377Ssam					interpolate_signed(
396185377Ssam							   channel, pRawDataset->pChannels[idxL], pRawDataset->pChannels[idxR],
397185377Ssam							   (int16_t)VpdTable_L[numPdGainsUsed][kk], (int16_t)VpdTable_R[numPdGainsUsed][kk]);
398185377Ssam			}
399185377Ssam			/* fill VpdTable_I for this pdGain */
400185377Ssam			numPdGainsUsed++;
401185377Ssam		}
402185377Ssam		/* if this pdGain is used */
403185377Ssam	}
404185377Ssam
405185377Ssam	*pMinCalPower = Pmin_t2[0];
406185377Ssam	kk = 0; /* index for the final table */
407185377Ssam	for (ii = 0; ii < numPdGainsUsed; ii++) {
408185377Ssam		if (ii == (numPdGainsUsed - 1))
409185377Ssam			pPdGainBoundaries[ii] = Pmax_t2[ii] +
410185377Ssam				PD_GAIN_BOUNDARY_STRETCH_IN_HALF_DB;
411185377Ssam		else
412185377Ssam			pPdGainBoundaries[ii] = (uint16_t)
413185377Ssam				((Pmax_t2[ii] + Pmin_t2[ii+1]) / 2 );
414185377Ssam		if (pPdGainBoundaries[ii] > 63) {
415185377Ssam			HALDEBUG(ah, HAL_DEBUG_ANY,
416185377Ssam			    "%s: clamp pPdGainBoundaries[%d] %d\n",
417185377Ssam			   __func__, ii, pPdGainBoundaries[ii]);/*XXX*/
418185377Ssam			pPdGainBoundaries[ii] = 63;
419185377Ssam		}
420185377Ssam
421185377Ssam		/* Find starting index for this pdGain */
422185377Ssam		if (ii == 0)
423185377Ssam			ss = 0; /* for the first pdGain, start from index 0 */
424185377Ssam		else
425185377Ssam			ss = (pPdGainBoundaries[ii-1] - Pmin_t2[ii]) -
426185377Ssam				pdGainOverlap_t2;
427185377Ssam		Vpd_step = (uint16_t)(VpdTable_I[ii][1] - VpdTable_I[ii][0]);
428185377Ssam		Vpd_step = (uint16_t)((Vpd_step < 1) ? 1 : Vpd_step);
429185377Ssam		/*
430185377Ssam		 *-ve ss indicates need to extrapolate data below for this pdGain
431185377Ssam		 */
432185377Ssam		while (ss < 0) {
433185377Ssam			tmpVal = (int16_t)(VpdTable_I[ii][0] + ss*Vpd_step);
434185377Ssam			pPDADCValues[kk++] = (uint16_t)((tmpVal < 0) ? 0 : tmpVal);
435185377Ssam			ss++;
436185377Ssam		}
437185377Ssam
438185377Ssam		sizeCurrVpdTable = Pmax_t2[ii] - Pmin_t2[ii];
439185377Ssam		tgtIndex = pPdGainBoundaries[ii] + pdGainOverlap_t2 - Pmin_t2[ii];
440185377Ssam		maxIndex = (tgtIndex < sizeCurrVpdTable) ? tgtIndex : sizeCurrVpdTable;
441185377Ssam
442185377Ssam		while (ss < (int16_t)maxIndex)
443185377Ssam			pPDADCValues[kk++] = VpdTable_I[ii][ss++];
444185377Ssam
445185377Ssam		Vpd_step = (uint16_t)(VpdTable_I[ii][sizeCurrVpdTable-1] -
446185377Ssam				       VpdTable_I[ii][sizeCurrVpdTable-2]);
447185377Ssam		Vpd_step = (uint16_t)((Vpd_step < 1) ? 1 : Vpd_step);
448185377Ssam		/*
449185377Ssam		 * for last gain, pdGainBoundary == Pmax_t2, so will
450185377Ssam		 * have to extrapolate
451185377Ssam		 */
452185377Ssam		if (tgtIndex > maxIndex) {	/* need to extrapolate above */
453185377Ssam			while(ss < (int16_t)tgtIndex) {
454185377Ssam				tmpVal = (uint16_t)
455185377Ssam					(VpdTable_I[ii][sizeCurrVpdTable-1] +
456185377Ssam					 (ss-maxIndex)*Vpd_step);
457185377Ssam				pPDADCValues[kk++] = (tmpVal > 127) ?
458185377Ssam					127 : tmpVal;
459185377Ssam				ss++;
460185377Ssam			}
461185377Ssam		}				/* extrapolated above */
462185377Ssam	}					/* for all pdGainUsed */
463185377Ssam
464185377Ssam	while (ii < MAX_NUM_PDGAINS_PER_CHANNEL) {
465185377Ssam		pPdGainBoundaries[ii] = pPdGainBoundaries[ii-1];
466185377Ssam		ii++;
467185377Ssam	}
468185377Ssam	while (kk < 128) {
469185377Ssam		pPDADCValues[kk] = pPDADCValues[kk-1];
470185377Ssam		kk++;
471185377Ssam	}
472185377Ssam
473185377Ssam	return numPdGainsUsed;
474185377Ssam#undef VpdTable_L
475185377Ssam#undef VpdTable_R
476185377Ssam#undef VpdTable_I
477185377Ssam}
478185377Ssam
479185377Ssamstatic HAL_BOOL
480185377Ssamar2317SetPowerTable(struct ath_hal *ah,
481187831Ssam	int16_t *minPower, int16_t *maxPower,
482187831Ssam	const struct ieee80211_channel *chan,
483185377Ssam	uint16_t *rfXpdGain)
484185377Ssam{
485185377Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
486185377Ssam	const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom;
487185377Ssam	const RAW_DATA_STRUCT_2317 *pRawDataset = AH_NULL;
488185377Ssam	uint16_t pdGainOverlap_t2;
489185377Ssam	int16_t minCalPower2317_t2;
490185377Ssam	uint16_t *pdadcValues = ahp->ah_pcdacTable;
491185377Ssam	uint16_t gainBoundaries[4];
492185380Ssam	uint32_t reg32, regoffset;
493185380Ssam	int i, numPdGainsUsed;
494185380Ssam#ifndef AH_USE_INIPDGAIN
495185380Ssam	uint32_t tpcrg1;
496185380Ssam#endif
497185377Ssam
498185377Ssam	HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: chan 0x%x flag 0x%x\n",
499187831Ssam	    __func__, chan->ic_freq, chan->ic_flags);
500185377Ssam
501187831Ssam	if (IEEE80211_IS_CHAN_G(chan) || IEEE80211_IS_CHAN_108G(chan))
502185377Ssam		pRawDataset = &ee->ee_rawDataset2413[headerInfo11G];
503187831Ssam	else if (IEEE80211_IS_CHAN_B(chan))
504185377Ssam		pRawDataset = &ee->ee_rawDataset2413[headerInfo11B];
505185377Ssam	else {
506185377Ssam		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: illegal mode\n", __func__);
507185377Ssam		return AH_FALSE;
508185377Ssam	}
509185377Ssam
510185377Ssam	pdGainOverlap_t2 = (uint16_t) SM(OS_REG_READ(ah, AR_PHY_TPCRG5),
511185377Ssam					  AR_PHY_TPCRG5_PD_GAIN_OVERLAP);
512185377Ssam
513185377Ssam	numPdGainsUsed = ar2317getGainBoundariesAndPdadcsForPowers(ah,
514185377Ssam		chan->channel, pRawDataset, pdGainOverlap_t2,
515185377Ssam		&minCalPower2317_t2,gainBoundaries, rfXpdGain, pdadcValues);
516185377Ssam	HALASSERT(1 <= numPdGainsUsed && numPdGainsUsed <= 3);
517185377Ssam
518185380Ssam#ifdef AH_USE_INIPDGAIN
519185377Ssam	/*
520185377Ssam	 * Use pd_gains curve from eeprom; Atheros always uses
521185377Ssam	 * the default curve from the ini file but some vendors
522185377Ssam	 * (e.g. Zcomax) want to override this curve and not
523185377Ssam	 * honoring their settings results in tx power 5dBm low.
524185377Ssam	 */
525185377Ssam	OS_REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_NUM_PD_GAIN,
526185377Ssam			 (pRawDataset->pDataPerChannel[0].numPdGains - 1));
527185380Ssam#else
528185377Ssam	tpcrg1 = OS_REG_READ(ah, AR_PHY_TPCRG1);
529185377Ssam	tpcrg1 = (tpcrg1 &~ AR_PHY_TPCRG1_NUM_PD_GAIN)
530185377Ssam		  | SM(numPdGainsUsed-1, AR_PHY_TPCRG1_NUM_PD_GAIN);
531185377Ssam	switch (numPdGainsUsed) {
532185377Ssam	case 3:
533185377Ssam		tpcrg1 &= ~AR_PHY_TPCRG1_PDGAIN_SETTING3;
534185377Ssam		tpcrg1 |= SM(rfXpdGain[2], AR_PHY_TPCRG1_PDGAIN_SETTING3);
535185377Ssam		/* fall thru... */
536185377Ssam	case 2:
537185377Ssam		tpcrg1 &= ~AR_PHY_TPCRG1_PDGAIN_SETTING2;
538185377Ssam		tpcrg1 |= SM(rfXpdGain[1], AR_PHY_TPCRG1_PDGAIN_SETTING2);
539185377Ssam		/* fall thru... */
540185377Ssam	case 1:
541185377Ssam		tpcrg1 &= ~AR_PHY_TPCRG1_PDGAIN_SETTING1;
542185377Ssam		tpcrg1 |= SM(rfXpdGain[0], AR_PHY_TPCRG1_PDGAIN_SETTING1);
543185377Ssam		break;
544185377Ssam	}
545185377Ssam#ifdef AH_DEBUG
546185377Ssam	if (tpcrg1 != OS_REG_READ(ah, AR_PHY_TPCRG1))
547185377Ssam		HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: using non-default "
548185377Ssam		    "pd_gains (default 0x%x, calculated 0x%x)\n",
549185377Ssam		    __func__, OS_REG_READ(ah, AR_PHY_TPCRG1), tpcrg1);
550185377Ssam#endif
551185377Ssam	OS_REG_WRITE(ah, AR_PHY_TPCRG1, tpcrg1);
552185380Ssam#endif
553185377Ssam
554185377Ssam	/*
555185377Ssam	 * Note the pdadc table may not start at 0 dBm power, could be
556185377Ssam	 * negative or greater than 0.  Need to offset the power
557185377Ssam	 * values by the amount of minPower for griffin
558185377Ssam	 */
559185377Ssam	if (minCalPower2317_t2 != 0)
560185377Ssam		ahp->ah_txPowerIndexOffset = (int16_t)(0 - minCalPower2317_t2);
561185377Ssam	else
562185377Ssam		ahp->ah_txPowerIndexOffset = 0;
563185377Ssam
564185377Ssam	/* Finally, write the power values into the baseband power table */
565185377Ssam	regoffset = 0x9800 + (672 <<2); /* beginning of pdadc table in griffin */
566185377Ssam	for (i = 0; i < 32; i++) {
567185377Ssam		reg32 = ((pdadcValues[4*i + 0] & 0xFF) << 0)  |
568185377Ssam			((pdadcValues[4*i + 1] & 0xFF) << 8)  |
569185377Ssam			((pdadcValues[4*i + 2] & 0xFF) << 16) |
570185377Ssam			((pdadcValues[4*i + 3] & 0xFF) << 24) ;
571185377Ssam		OS_REG_WRITE(ah, regoffset, reg32);
572185377Ssam		regoffset += 4;
573185377Ssam	}
574185377Ssam
575185377Ssam	OS_REG_WRITE(ah, AR_PHY_TPCRG5,
576185377Ssam		     SM(pdGainOverlap_t2, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) |
577185377Ssam		     SM(gainBoundaries[0], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1) |
578185377Ssam		     SM(gainBoundaries[1], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2) |
579185377Ssam		     SM(gainBoundaries[2], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3) |
580185377Ssam		     SM(gainBoundaries[3], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4));
581185377Ssam
582185377Ssam	return AH_TRUE;
583185377Ssam}
584185377Ssam
585185377Ssamstatic int16_t
586185377Ssamar2317GetMinPower(struct ath_hal *ah, const RAW_DATA_PER_CHANNEL_2317 *data)
587185377Ssam{
588185377Ssam	uint32_t ii,jj;
589185377Ssam	uint16_t Pmin=0,numVpd;
590185377Ssam
591185377Ssam	for (ii = 0; ii < MAX_NUM_PDGAINS_PER_CHANNEL; ii++) {
592185377Ssam		jj = MAX_NUM_PDGAINS_PER_CHANNEL - ii - 1;
593185377Ssam		/* work backwards 'cause highest pdGain for lowest power */
594185377Ssam		numVpd = data->pDataPerPDGain[jj].numVpd;
595185377Ssam		if (numVpd > 0) {
596185377Ssam			Pmin = data->pDataPerPDGain[jj].pwr_t4[0];
597185377Ssam			return(Pmin);
598185377Ssam		}
599185377Ssam	}
600185377Ssam	return(Pmin);
601185377Ssam}
602185377Ssam
603185377Ssamstatic int16_t
604185377Ssamar2317GetMaxPower(struct ath_hal *ah, const RAW_DATA_PER_CHANNEL_2317 *data)
605185377Ssam{
606185377Ssam	uint32_t ii;
607185377Ssam	uint16_t Pmax=0,numVpd;
608185377Ssam	uint16_t vpdmax;
609185377Ssam
610185377Ssam	for (ii=0; ii< MAX_NUM_PDGAINS_PER_CHANNEL; ii++) {
611185377Ssam		/* work forwards cuase lowest pdGain for highest power */
612185377Ssam		numVpd = data->pDataPerPDGain[ii].numVpd;
613185377Ssam		if (numVpd > 0) {
614185377Ssam			Pmax = data->pDataPerPDGain[ii].pwr_t4[numVpd-1];
615185377Ssam			vpdmax = data->pDataPerPDGain[ii].Vpd[numVpd-1];
616185377Ssam			return(Pmax);
617185377Ssam		}
618185377Ssam	}
619185377Ssam	return(Pmax);
620185377Ssam}
621185377Ssam
622185377Ssamstatic HAL_BOOL
623187831Ssamar2317GetChannelMaxMinPower(struct ath_hal *ah,
624187831Ssam	const struct ieee80211_channel *chan,
625185377Ssam	int16_t *maxPow, int16_t *minPow)
626185377Ssam{
627187831Ssam	uint16_t freq = chan->ic_freq;		/* NB: never mapped */
628185377Ssam	const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom;
629185377Ssam	const RAW_DATA_STRUCT_2317 *pRawDataset = AH_NULL;
630185377Ssam	const RAW_DATA_PER_CHANNEL_2317 *data=AH_NULL;
631185377Ssam	uint16_t numChannels;
632185377Ssam	int totalD,totalF, totalMin,last, i;
633185377Ssam
634185377Ssam	*maxPow = 0;
635185377Ssam
636187831Ssam	if (IEEE80211_IS_CHAN_G(chan) || IEEE80211_IS_CHAN_108G(chan))
637185377Ssam		pRawDataset = &ee->ee_rawDataset2413[headerInfo11G];
638187831Ssam	else if (IEEE80211_IS_CHAN_B(chan))
639185377Ssam		pRawDataset = &ee->ee_rawDataset2413[headerInfo11B];
640185377Ssam	else
641185377Ssam		return(AH_FALSE);
642185377Ssam
643185377Ssam	numChannels = pRawDataset->numChannels;
644185377Ssam	data = pRawDataset->pDataPerChannel;
645185377Ssam
646185377Ssam	/* Make sure the channel is in the range of the TP values
647185377Ssam	 *  (freq piers)
648185377Ssam	 */
649185377Ssam	if (numChannels < 1)
650185377Ssam		return(AH_FALSE);
651185377Ssam
652187831Ssam	if ((freq < data[0].channelValue) ||
653187831Ssam	    (freq > data[numChannels-1].channelValue)) {
654187831Ssam		if (freq < data[0].channelValue) {
655185377Ssam			*maxPow = ar2317GetMaxPower(ah, &data[0]);
656185377Ssam			*minPow = ar2317GetMinPower(ah, &data[0]);
657185377Ssam			return(AH_TRUE);
658185377Ssam		} else {
659185377Ssam			*maxPow = ar2317GetMaxPower(ah, &data[numChannels - 1]);
660185377Ssam			*minPow = ar2317GetMinPower(ah, &data[numChannels - 1]);
661185377Ssam			return(AH_TRUE);
662185377Ssam		}
663185377Ssam	}
664185377Ssam
665185377Ssam	/* Linearly interpolate the power value now */
666187831Ssam	for (last=0,i=0; (i<numChannels) && (freq > data[i].channelValue);
667185377Ssam	     last = i++);
668185377Ssam	totalD = data[i].channelValue - data[last].channelValue;
669185377Ssam	if (totalD > 0) {
670185377Ssam		totalF = ar2317GetMaxPower(ah, &data[i]) - ar2317GetMaxPower(ah, &data[last]);
671187831Ssam		*maxPow = (int8_t) ((totalF*(freq-data[last].channelValue) +
672185377Ssam				     ar2317GetMaxPower(ah, &data[last])*totalD)/totalD);
673185377Ssam		totalMin = ar2317GetMinPower(ah, &data[i]) - ar2317GetMinPower(ah, &data[last]);
674187831Ssam		*minPow = (int8_t) ((totalMin*(freq-data[last].channelValue) +
675185377Ssam				     ar2317GetMinPower(ah, &data[last])*totalD)/totalD);
676185377Ssam		return(AH_TRUE);
677185377Ssam	} else {
678187831Ssam		if (freq == data[i].channelValue) {
679185377Ssam			*maxPow = ar2317GetMaxPower(ah, &data[i]);
680185377Ssam			*minPow = ar2317GetMinPower(ah, &data[i]);
681185377Ssam			return(AH_TRUE);
682185377Ssam		} else
683185377Ssam			return(AH_FALSE);
684185377Ssam	}
685185377Ssam}
686185377Ssam
687185377Ssam/*
688185377Ssam * Free memory for analog bank scratch buffers
689185377Ssam */
690185377Ssamstatic void
691185377Ssamar2317RfDetach(struct ath_hal *ah)
692185377Ssam{
693185377Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
694185377Ssam
695185377Ssam	HALASSERT(ahp->ah_rfHal != AH_NULL);
696185377Ssam	ath_hal_free(ahp->ah_rfHal);
697185377Ssam	ahp->ah_rfHal = AH_NULL;
698185377Ssam}
699185377Ssam
700185377Ssam/*
701185377Ssam * Allocate memory for analog bank scratch buffers
702185377Ssam * Scratch Buffer will be reinitialized every reset so no need to zero now
703185377Ssam */
704185406Ssamstatic HAL_BOOL
705185377Ssamar2317RfAttach(struct ath_hal *ah, HAL_STATUS *status)
706185377Ssam{
707185377Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
708185377Ssam	struct ar2317State *priv;
709185377Ssam
710185377Ssam	HALASSERT(ah->ah_magic == AR5212_MAGIC);
711185377Ssam
712185377Ssam	HALASSERT(ahp->ah_rfHal == AH_NULL);
713185377Ssam	priv = ath_hal_malloc(sizeof(struct ar2317State));
714185377Ssam	if (priv == AH_NULL) {
715185377Ssam		HALDEBUG(ah, HAL_DEBUG_ANY,
716185377Ssam		    "%s: cannot allocate private state\n", __func__);
717185377Ssam		*status = HAL_ENOMEM;		/* XXX */
718185377Ssam		return AH_FALSE;
719185377Ssam	}
720185377Ssam	priv->base.rfDetach		= ar2317RfDetach;
721185377Ssam	priv->base.writeRegs		= ar2317WriteRegs;
722185377Ssam	priv->base.getRfBank		= ar2317GetRfBank;
723185377Ssam	priv->base.setChannel		= ar2317SetChannel;
724185377Ssam	priv->base.setRfRegs		= ar2317SetRfRegs;
725185377Ssam	priv->base.setPowerTable	= ar2317SetPowerTable;
726185377Ssam	priv->base.getChannelMaxMinPower = ar2317GetChannelMaxMinPower;
727185377Ssam	priv->base.getNfAdjust		= ar5212GetNfAdjust;
728185377Ssam
729185377Ssam	ahp->ah_pcdacTable = priv->pcdacTable;
730185377Ssam	ahp->ah_pcdacTableSize = sizeof(priv->pcdacTable);
731185377Ssam	ahp->ah_rfHal = &priv->base;
732185377Ssam
733185377Ssam	return AH_TRUE;
734185377Ssam}
735185406Ssam
736185406Ssamstatic HAL_BOOL
737185406Ssamar2317Probe(struct ath_hal *ah)
738185406Ssam{
739185406Ssam	return IS_2317(ah);
740185406Ssam}
741185418SsamAH_RF(RF2317, ar2317Probe, ar2317RfAttach);
742