1/*
2 * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
3 * Copyright (c) 2002-2008 Atheros Communications, Inc.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 *
17 * $FreeBSD$
18 */
19#include "opt_ah.h"
20
21#include "ah.h"
22#include "ah_internal.h"
23
24#include "ar5212/ar5212.h"
25#include "ar5212/ar5212reg.h"
26#include "ar5212/ar5212phy.h"
27
28#include "ah_eeprom_v3.h"
29
30#define AH_5212_2317
31#include "ar5212/ar5212.ini"
32
33#define	N(a)	(sizeof(a)/sizeof(a[0]))
34
35typedef	RAW_DATA_STRUCT_2413 RAW_DATA_STRUCT_2317;
36typedef RAW_DATA_PER_CHANNEL_2413 RAW_DATA_PER_CHANNEL_2317;
37#define PWR_TABLE_SIZE_2317 PWR_TABLE_SIZE_2413
38
39struct ar2317State {
40	RF_HAL_FUNCS	base;		/* public state, must be first */
41	uint16_t	pcdacTable[PWR_TABLE_SIZE_2317];
42
43	uint32_t	Bank1Data[N(ar5212Bank1_2317)];
44	uint32_t	Bank2Data[N(ar5212Bank2_2317)];
45	uint32_t	Bank3Data[N(ar5212Bank3_2317)];
46	uint32_t	Bank6Data[N(ar5212Bank6_2317)];
47	uint32_t	Bank7Data[N(ar5212Bank7_2317)];
48
49	/*
50	 * Private state for reduced stack usage.
51	 */
52	/* filled out Vpd table for all pdGains (chanL) */
53	uint16_t vpdTable_L[MAX_NUM_PDGAINS_PER_CHANNEL]
54			    [MAX_PWR_RANGE_IN_HALF_DB];
55	/* filled out Vpd table for all pdGains (chanR) */
56	uint16_t vpdTable_R[MAX_NUM_PDGAINS_PER_CHANNEL]
57			    [MAX_PWR_RANGE_IN_HALF_DB];
58	/* filled out Vpd table for all pdGains (interpolated) */
59	uint16_t vpdTable_I[MAX_NUM_PDGAINS_PER_CHANNEL]
60			    [MAX_PWR_RANGE_IN_HALF_DB];
61};
62#define	AR2317(ah)	((struct ar2317State *) AH5212(ah)->ah_rfHal)
63
64extern	void ar5212ModifyRfBuffer(uint32_t *rfBuf, uint32_t reg32,
65		uint32_t numBits, uint32_t firstBit, uint32_t column);
66
67static void
68ar2317WriteRegs(struct ath_hal *ah, u_int modesIndex, u_int freqIndex,
69	int writes)
70{
71	HAL_INI_WRITE_ARRAY(ah, ar5212Modes_2317, modesIndex, writes);
72	HAL_INI_WRITE_ARRAY(ah, ar5212Common_2317, 1, writes);
73	HAL_INI_WRITE_ARRAY(ah, ar5212BB_RfGain_2317, freqIndex, writes);
74}
75
76/*
77 * Take the MHz channel value and set the Channel value
78 *
79 * ASSUMES: Writes enabled to analog bus
80 */
81static HAL_BOOL
82ar2317SetChannel(struct ath_hal *ah,  const struct ieee80211_channel *chan)
83{
84	uint16_t freq = ath_hal_gethwchannel(ah, chan);
85	uint32_t channelSel  = 0;
86	uint32_t bModeSynth  = 0;
87	uint32_t aModeRefSel = 0;
88	uint32_t reg32       = 0;
89
90	OS_MARK(ah, AH_MARK_SETCHANNEL, freq);
91
92	if (freq < 4800) {
93		uint32_t txctl;
94		channelSel = freq - 2272 ;
95		channelSel = ath_hal_reverseBits(channelSel, 8);
96
97		txctl = OS_REG_READ(ah, AR_PHY_CCK_TX_CTRL);
98		if (freq == 2484) {
99			/* Enable channel spreading for channel 14 */
100			OS_REG_WRITE(ah, AR_PHY_CCK_TX_CTRL,
101				txctl | AR_PHY_CCK_TX_CTRL_JAPAN);
102		} else {
103			OS_REG_WRITE(ah, AR_PHY_CCK_TX_CTRL,
104				txctl &~ AR_PHY_CCK_TX_CTRL_JAPAN);
105		}
106	} else if ((freq % 20) == 0 && freq >= 5120) {
107		channelSel = ath_hal_reverseBits(
108			((freq - 4800) / 20 << 2), 8);
109		aModeRefSel = ath_hal_reverseBits(3, 2);
110	} else if ((freq % 10) == 0) {
111		channelSel = ath_hal_reverseBits(
112			((freq - 4800) / 10 << 1), 8);
113		aModeRefSel = ath_hal_reverseBits(2, 2);
114	} else if ((freq % 5) == 0) {
115		channelSel = ath_hal_reverseBits(
116			(freq - 4800) / 5, 8);
117		aModeRefSel = ath_hal_reverseBits(1, 2);
118	} else {
119		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u MHz\n",
120		    __func__, freq);
121		return AH_FALSE;
122	}
123
124	reg32 = (channelSel << 4) | (aModeRefSel << 2) | (bModeSynth << 1) |
125			(1 << 12) | 0x1;
126	OS_REG_WRITE(ah, AR_PHY(0x27), reg32 & 0xff);
127
128	reg32 >>= 8;
129	OS_REG_WRITE(ah, AR_PHY(0x36), reg32 & 0x7f);
130
131	AH_PRIVATE(ah)->ah_curchan = chan;
132	return AH_TRUE;
133}
134
135/*
136 * Reads EEPROM header info from device structure and programs
137 * all rf registers
138 *
139 * REQUIRES: Access to the analog rf device
140 */
141static HAL_BOOL
142ar2317SetRfRegs(struct ath_hal *ah,
143	const struct ieee80211_channel *chan,
144	uint16_t modesIndex, uint16_t *rfXpdGain)
145{
146#define	RF_BANK_SETUP(_priv, _ix, _col) do {				    \
147	int i;								    \
148	for (i = 0; i < N(ar5212Bank##_ix##_2317); i++)			    \
149		(_priv)->Bank##_ix##Data[i] = ar5212Bank##_ix##_2317[i][_col];\
150} while (0)
151	struct ath_hal_5212 *ahp = AH5212(ah);
152	const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom;
153	uint16_t ob2GHz = 0, db2GHz = 0;
154	struct ar2317State *priv = AR2317(ah);
155	int regWrites = 0;
156
157	HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: chan %u/0x%x modesIndex %u\n",
158	    __func__, chan->ic_freq, chan->ic_flags, modesIndex);
159
160	HALASSERT(priv);
161
162	/* Setup rf parameters */
163	if (IEEE80211_IS_CHAN_B(chan)) {
164		ob2GHz = ee->ee_obFor24;
165		db2GHz = ee->ee_dbFor24;
166	} else {
167		ob2GHz = ee->ee_obFor24g;
168		db2GHz = ee->ee_dbFor24g;
169	}
170
171	/* Bank 1 Write */
172	RF_BANK_SETUP(priv, 1, 1);
173
174	/* Bank 2 Write */
175	RF_BANK_SETUP(priv, 2, modesIndex);
176
177	/* Bank 3 Write */
178	RF_BANK_SETUP(priv, 3, modesIndex);
179
180	/* Bank 6 Write */
181	RF_BANK_SETUP(priv, 6, modesIndex);
182
183	ar5212ModifyRfBuffer(priv->Bank6Data, ob2GHz,   3, 193, 0);
184	ar5212ModifyRfBuffer(priv->Bank6Data, db2GHz,   3, 190, 0);
185
186	/* Bank 7 Setup */
187	RF_BANK_SETUP(priv, 7, modesIndex);
188
189	/* Write Analog registers */
190	HAL_INI_WRITE_BANK(ah, ar5212Bank1_2317, priv->Bank1Data, regWrites);
191	HAL_INI_WRITE_BANK(ah, ar5212Bank2_2317, priv->Bank2Data, regWrites);
192	HAL_INI_WRITE_BANK(ah, ar5212Bank3_2317, priv->Bank3Data, regWrites);
193	HAL_INI_WRITE_BANK(ah, ar5212Bank6_2317, priv->Bank6Data, regWrites);
194	HAL_INI_WRITE_BANK(ah, ar5212Bank7_2317, priv->Bank7Data, regWrites);
195	/* Now that we have reprogrammed rfgain value, clear the flag. */
196	ahp->ah_rfgainState = HAL_RFGAIN_INACTIVE;
197
198	return AH_TRUE;
199#undef	RF_BANK_SETUP
200}
201
202/*
203 * Return a reference to the requested RF Bank.
204 */
205static uint32_t *
206ar2317GetRfBank(struct ath_hal *ah, int bank)
207{
208	struct ar2317State *priv = AR2317(ah);
209
210	HALASSERT(priv != AH_NULL);
211	switch (bank) {
212	case 1: return priv->Bank1Data;
213	case 2: return priv->Bank2Data;
214	case 3: return priv->Bank3Data;
215	case 6: return priv->Bank6Data;
216	case 7: return priv->Bank7Data;
217	}
218	HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown RF Bank %d requested\n",
219	    __func__, bank);
220	return AH_NULL;
221}
222
223/*
224 * Return indices surrounding the value in sorted integer lists.
225 *
226 * NB: the input list is assumed to be sorted in ascending order
227 */
228static void
229GetLowerUpperIndex(int16_t v, const uint16_t *lp, uint16_t listSize,
230                          uint32_t *vlo, uint32_t *vhi)
231{
232	int16_t target = v;
233	const int16_t *ep = lp+listSize;
234	const int16_t *tp;
235
236	/*
237	 * Check first and last elements for out-of-bounds conditions.
238	 */
239	if (target < lp[0]) {
240		*vlo = *vhi = 0;
241		return;
242	}
243	if (target >= ep[-1]) {
244		*vlo = *vhi = listSize - 1;
245		return;
246	}
247
248	/* look for value being near or between 2 values in list */
249	for (tp = lp; tp < ep; tp++) {
250		/*
251		 * If value is close to the current value of the list
252		 * then target is not between values, it is one of the values
253		 */
254		if (*tp == target) {
255			*vlo = *vhi = tp - (const int16_t *) lp;
256			return;
257		}
258		/*
259		 * Look for value being between current value and next value
260		 * if so return these 2 values
261		 */
262		if (target < tp[1]) {
263			*vlo = tp - (const int16_t *) lp;
264			*vhi = *vlo + 1;
265			return;
266		}
267	}
268}
269
270/*
271 * Fill the Vpdlist for indices Pmax-Pmin
272 */
273static HAL_BOOL
274ar2317FillVpdTable(uint32_t pdGainIdx, int16_t Pmin, int16_t  Pmax,
275		   const int16_t *pwrList, const int16_t *VpdList,
276		   uint16_t numIntercepts, uint16_t retVpdList[][64])
277{
278	uint16_t ii, jj, kk;
279	int16_t currPwr = (int16_t)(2*Pmin);
280	/* since Pmin is pwr*2 and pwrList is 4*pwr */
281	uint32_t  idxL, idxR;
282
283	ii = 0;
284	jj = 0;
285
286	if (numIntercepts < 2)
287		return AH_FALSE;
288
289	while (ii <= (uint16_t)(Pmax - Pmin)) {
290		GetLowerUpperIndex(currPwr, pwrList, numIntercepts,
291					 &(idxL), &(idxR));
292		if (idxR < 1)
293			idxR = 1;			/* extrapolate below */
294		if (idxL == (uint32_t)(numIntercepts - 1))
295			idxL = numIntercepts - 2;	/* extrapolate above */
296		if (pwrList[idxL] == pwrList[idxR])
297			kk = VpdList[idxL];
298		else
299			kk = (uint16_t)
300				(((currPwr - pwrList[idxL])*VpdList[idxR]+
301				  (pwrList[idxR] - currPwr)*VpdList[idxL])/
302				 (pwrList[idxR] - pwrList[idxL]));
303		retVpdList[pdGainIdx][ii] = kk;
304		ii++;
305		currPwr += 2;				/* half dB steps */
306	}
307
308	return AH_TRUE;
309}
310
311/*
312 * Returns interpolated or the scaled up interpolated value
313 */
314static int16_t
315interpolate_signed(uint16_t target, uint16_t srcLeft, uint16_t srcRight,
316	int16_t targetLeft, int16_t targetRight)
317{
318	int16_t rv;
319
320	if (srcRight != srcLeft) {
321		rv = ((target - srcLeft)*targetRight +
322		      (srcRight - target)*targetLeft) / (srcRight - srcLeft);
323	} else {
324		rv = targetLeft;
325	}
326	return rv;
327}
328
329/*
330 * Uses the data points read from EEPROM to reconstruct the pdadc power table
331 * Called by ar2317SetPowerTable()
332 */
333static int
334ar2317getGainBoundariesAndPdadcsForPowers(struct ath_hal *ah, uint16_t channel,
335		const RAW_DATA_STRUCT_2317 *pRawDataset,
336		uint16_t pdGainOverlap_t2,
337		int16_t  *pMinCalPower, uint16_t pPdGainBoundaries[],
338		uint16_t pPdGainValues[], uint16_t pPDADCValues[])
339{
340	struct ar2317State *priv = AR2317(ah);
341#define	VpdTable_L	priv->vpdTable_L
342#define	VpdTable_R	priv->vpdTable_R
343#define	VpdTable_I	priv->vpdTable_I
344	/* XXX excessive stack usage? */
345	uint32_t ii, jj, kk;
346	int32_t ss;/* potentially -ve index for taking care of pdGainOverlap */
347	uint32_t idxL, idxR;
348	uint32_t numPdGainsUsed = 0;
349	/*
350	 * If desired to support -ve power levels in future, just
351	 * change pwr_I_0 to signed 5-bits.
352	 */
353	int16_t Pmin_t2[MAX_NUM_PDGAINS_PER_CHANNEL];
354	/* to accomodate -ve power levels later on. */
355	int16_t Pmax_t2[MAX_NUM_PDGAINS_PER_CHANNEL];
356	/* to accomodate -ve power levels later on */
357	uint16_t numVpd = 0;
358	uint16_t Vpd_step;
359	int16_t tmpVal ;
360	uint32_t sizeCurrVpdTable, maxIndex, tgtIndex;
361
362	/* Get upper lower index */
363	GetLowerUpperIndex(channel, pRawDataset->pChannels,
364				 pRawDataset->numChannels, &(idxL), &(idxR));
365
366	for (ii = 0; ii < MAX_NUM_PDGAINS_PER_CHANNEL; ii++) {
367		jj = MAX_NUM_PDGAINS_PER_CHANNEL - ii - 1;
368		/* work backwards 'cause highest pdGain for lowest power */
369		numVpd = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].numVpd;
370		if (numVpd > 0) {
371			pPdGainValues[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pd_gain;
372			Pmin_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pwr_t4[0];
373			if (Pmin_t2[numPdGainsUsed] >pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[0]) {
374				Pmin_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[0];
375			}
376			Pmin_t2[numPdGainsUsed] = (int16_t)
377				(Pmin_t2[numPdGainsUsed] / 2);
378			Pmax_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pwr_t4[numVpd-1];
379			if (Pmax_t2[numPdGainsUsed] > pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[numVpd-1])
380				Pmax_t2[numPdGainsUsed] =
381					pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[numVpd-1];
382			Pmax_t2[numPdGainsUsed] = (int16_t)(Pmax_t2[numPdGainsUsed] / 2);
383			ar2317FillVpdTable(
384					   numPdGainsUsed, Pmin_t2[numPdGainsUsed], Pmax_t2[numPdGainsUsed],
385					   &(pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pwr_t4[0]),
386					   &(pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].Vpd[0]), numVpd, VpdTable_L
387					   );
388			ar2317FillVpdTable(
389					   numPdGainsUsed, Pmin_t2[numPdGainsUsed], Pmax_t2[numPdGainsUsed],
390					   &(pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[0]),
391					   &(pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].Vpd[0]), numVpd, VpdTable_R
392					   );
393			for (kk = 0; kk < (uint16_t)(Pmax_t2[numPdGainsUsed] - Pmin_t2[numPdGainsUsed]); kk++) {
394				VpdTable_I[numPdGainsUsed][kk] =
395					interpolate_signed(
396							   channel, pRawDataset->pChannels[idxL], pRawDataset->pChannels[idxR],
397							   (int16_t)VpdTable_L[numPdGainsUsed][kk], (int16_t)VpdTable_R[numPdGainsUsed][kk]);
398			}
399			/* fill VpdTable_I for this pdGain */
400			numPdGainsUsed++;
401		}
402		/* if this pdGain is used */
403	}
404
405	*pMinCalPower = Pmin_t2[0];
406	kk = 0; /* index for the final table */
407	for (ii = 0; ii < numPdGainsUsed; ii++) {
408		if (ii == (numPdGainsUsed - 1))
409			pPdGainBoundaries[ii] = Pmax_t2[ii] +
410				PD_GAIN_BOUNDARY_STRETCH_IN_HALF_DB;
411		else
412			pPdGainBoundaries[ii] = (uint16_t)
413				((Pmax_t2[ii] + Pmin_t2[ii+1]) / 2 );
414		if (pPdGainBoundaries[ii] > 63) {
415			HALDEBUG(ah, HAL_DEBUG_ANY,
416			    "%s: clamp pPdGainBoundaries[%d] %d\n",
417			   __func__, ii, pPdGainBoundaries[ii]);/*XXX*/
418			pPdGainBoundaries[ii] = 63;
419		}
420
421		/* Find starting index for this pdGain */
422		if (ii == 0)
423			ss = 0; /* for the first pdGain, start from index 0 */
424		else
425			ss = (pPdGainBoundaries[ii-1] - Pmin_t2[ii]) -
426				pdGainOverlap_t2;
427		Vpd_step = (uint16_t)(VpdTable_I[ii][1] - VpdTable_I[ii][0]);
428		Vpd_step = (uint16_t)((Vpd_step < 1) ? 1 : Vpd_step);
429		/*
430		 *-ve ss indicates need to extrapolate data below for this pdGain
431		 */
432		while (ss < 0) {
433			tmpVal = (int16_t)(VpdTable_I[ii][0] + ss*Vpd_step);
434			pPDADCValues[kk++] = (uint16_t)((tmpVal < 0) ? 0 : tmpVal);
435			ss++;
436		}
437
438		sizeCurrVpdTable = Pmax_t2[ii] - Pmin_t2[ii];
439		tgtIndex = pPdGainBoundaries[ii] + pdGainOverlap_t2 - Pmin_t2[ii];
440		maxIndex = (tgtIndex < sizeCurrVpdTable) ? tgtIndex : sizeCurrVpdTable;
441
442		while (ss < (int16_t)maxIndex)
443			pPDADCValues[kk++] = VpdTable_I[ii][ss++];
444
445		Vpd_step = (uint16_t)(VpdTable_I[ii][sizeCurrVpdTable-1] -
446				       VpdTable_I[ii][sizeCurrVpdTable-2]);
447		Vpd_step = (uint16_t)((Vpd_step < 1) ? 1 : Vpd_step);
448		/*
449		 * for last gain, pdGainBoundary == Pmax_t2, so will
450		 * have to extrapolate
451		 */
452		if (tgtIndex > maxIndex) {	/* need to extrapolate above */
453			while(ss < (int16_t)tgtIndex) {
454				tmpVal = (uint16_t)
455					(VpdTable_I[ii][sizeCurrVpdTable-1] +
456					 (ss-maxIndex)*Vpd_step);
457				pPDADCValues[kk++] = (tmpVal > 127) ?
458					127 : tmpVal;
459				ss++;
460			}
461		}				/* extrapolated above */
462	}					/* for all pdGainUsed */
463
464	while (ii < MAX_NUM_PDGAINS_PER_CHANNEL) {
465		pPdGainBoundaries[ii] = pPdGainBoundaries[ii-1];
466		ii++;
467	}
468	while (kk < 128) {
469		pPDADCValues[kk] = pPDADCValues[kk-1];
470		kk++;
471	}
472
473	return numPdGainsUsed;
474#undef VpdTable_L
475#undef VpdTable_R
476#undef VpdTable_I
477}
478
479static HAL_BOOL
480ar2317SetPowerTable(struct ath_hal *ah,
481	int16_t *minPower, int16_t *maxPower,
482	const struct ieee80211_channel *chan,
483	uint16_t *rfXpdGain)
484{
485	struct ath_hal_5212 *ahp = AH5212(ah);
486	const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom;
487	const RAW_DATA_STRUCT_2317 *pRawDataset = AH_NULL;
488	uint16_t pdGainOverlap_t2;
489	int16_t minCalPower2317_t2;
490	uint16_t *pdadcValues = ahp->ah_pcdacTable;
491	uint16_t gainBoundaries[4];
492	uint32_t reg32, regoffset;
493	int i, numPdGainsUsed;
494#ifndef AH_USE_INIPDGAIN
495	uint32_t tpcrg1;
496#endif
497
498	HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: chan 0x%x flag 0x%x\n",
499	    __func__, chan->ic_freq, chan->ic_flags);
500
501	if (IEEE80211_IS_CHAN_G(chan) || IEEE80211_IS_CHAN_108G(chan))
502		pRawDataset = &ee->ee_rawDataset2413[headerInfo11G];
503	else if (IEEE80211_IS_CHAN_B(chan))
504		pRawDataset = &ee->ee_rawDataset2413[headerInfo11B];
505	else {
506		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: illegal mode\n", __func__);
507		return AH_FALSE;
508	}
509
510	pdGainOverlap_t2 = (uint16_t) SM(OS_REG_READ(ah, AR_PHY_TPCRG5),
511					  AR_PHY_TPCRG5_PD_GAIN_OVERLAP);
512
513	numPdGainsUsed = ar2317getGainBoundariesAndPdadcsForPowers(ah,
514		chan->channel, pRawDataset, pdGainOverlap_t2,
515		&minCalPower2317_t2,gainBoundaries, rfXpdGain, pdadcValues);
516	HALASSERT(1 <= numPdGainsUsed && numPdGainsUsed <= 3);
517
518#ifdef AH_USE_INIPDGAIN
519	/*
520	 * Use pd_gains curve from eeprom; Atheros always uses
521	 * the default curve from the ini file but some vendors
522	 * (e.g. Zcomax) want to override this curve and not
523	 * honoring their settings results in tx power 5dBm low.
524	 */
525	OS_REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_NUM_PD_GAIN,
526			 (pRawDataset->pDataPerChannel[0].numPdGains - 1));
527#else
528	tpcrg1 = OS_REG_READ(ah, AR_PHY_TPCRG1);
529	tpcrg1 = (tpcrg1 &~ AR_PHY_TPCRG1_NUM_PD_GAIN)
530		  | SM(numPdGainsUsed-1, AR_PHY_TPCRG1_NUM_PD_GAIN);
531	switch (numPdGainsUsed) {
532	case 3:
533		tpcrg1 &= ~AR_PHY_TPCRG1_PDGAIN_SETTING3;
534		tpcrg1 |= SM(rfXpdGain[2], AR_PHY_TPCRG1_PDGAIN_SETTING3);
535		/* fall thru... */
536	case 2:
537		tpcrg1 &= ~AR_PHY_TPCRG1_PDGAIN_SETTING2;
538		tpcrg1 |= SM(rfXpdGain[1], AR_PHY_TPCRG1_PDGAIN_SETTING2);
539		/* fall thru... */
540	case 1:
541		tpcrg1 &= ~AR_PHY_TPCRG1_PDGAIN_SETTING1;
542		tpcrg1 |= SM(rfXpdGain[0], AR_PHY_TPCRG1_PDGAIN_SETTING1);
543		break;
544	}
545#ifdef AH_DEBUG
546	if (tpcrg1 != OS_REG_READ(ah, AR_PHY_TPCRG1))
547		HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: using non-default "
548		    "pd_gains (default 0x%x, calculated 0x%x)\n",
549		    __func__, OS_REG_READ(ah, AR_PHY_TPCRG1), tpcrg1);
550#endif
551	OS_REG_WRITE(ah, AR_PHY_TPCRG1, tpcrg1);
552#endif
553
554	/*
555	 * Note the pdadc table may not start at 0 dBm power, could be
556	 * negative or greater than 0.  Need to offset the power
557	 * values by the amount of minPower for griffin
558	 */
559	if (minCalPower2317_t2 != 0)
560		ahp->ah_txPowerIndexOffset = (int16_t)(0 - minCalPower2317_t2);
561	else
562		ahp->ah_txPowerIndexOffset = 0;
563
564	/* Finally, write the power values into the baseband power table */
565	regoffset = 0x9800 + (672 <<2); /* beginning of pdadc table in griffin */
566	for (i = 0; i < 32; i++) {
567		reg32 = ((pdadcValues[4*i + 0] & 0xFF) << 0)  |
568			((pdadcValues[4*i + 1] & 0xFF) << 8)  |
569			((pdadcValues[4*i + 2] & 0xFF) << 16) |
570			((pdadcValues[4*i + 3] & 0xFF) << 24) ;
571		OS_REG_WRITE(ah, regoffset, reg32);
572		regoffset += 4;
573	}
574
575	OS_REG_WRITE(ah, AR_PHY_TPCRG5,
576		     SM(pdGainOverlap_t2, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) |
577		     SM(gainBoundaries[0], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1) |
578		     SM(gainBoundaries[1], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2) |
579		     SM(gainBoundaries[2], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3) |
580		     SM(gainBoundaries[3], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4));
581
582	return AH_TRUE;
583}
584
585static int16_t
586ar2317GetMinPower(struct ath_hal *ah, const RAW_DATA_PER_CHANNEL_2317 *data)
587{
588	uint32_t ii,jj;
589	uint16_t Pmin=0,numVpd;
590
591	for (ii = 0; ii < MAX_NUM_PDGAINS_PER_CHANNEL; ii++) {
592		jj = MAX_NUM_PDGAINS_PER_CHANNEL - ii - 1;
593		/* work backwards 'cause highest pdGain for lowest power */
594		numVpd = data->pDataPerPDGain[jj].numVpd;
595		if (numVpd > 0) {
596			Pmin = data->pDataPerPDGain[jj].pwr_t4[0];
597			return(Pmin);
598		}
599	}
600	return(Pmin);
601}
602
603static int16_t
604ar2317GetMaxPower(struct ath_hal *ah, const RAW_DATA_PER_CHANNEL_2317 *data)
605{
606	uint32_t ii;
607	uint16_t Pmax=0,numVpd;
608	uint16_t vpdmax;
609
610	for (ii=0; ii< MAX_NUM_PDGAINS_PER_CHANNEL; ii++) {
611		/* work forwards cuase lowest pdGain for highest power */
612		numVpd = data->pDataPerPDGain[ii].numVpd;
613		if (numVpd > 0) {
614			Pmax = data->pDataPerPDGain[ii].pwr_t4[numVpd-1];
615			vpdmax = data->pDataPerPDGain[ii].Vpd[numVpd-1];
616			return(Pmax);
617		}
618	}
619	return(Pmax);
620}
621
622static HAL_BOOL
623ar2317GetChannelMaxMinPower(struct ath_hal *ah,
624	const struct ieee80211_channel *chan,
625	int16_t *maxPow, int16_t *minPow)
626{
627	uint16_t freq = chan->ic_freq;		/* NB: never mapped */
628	const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom;
629	const RAW_DATA_STRUCT_2317 *pRawDataset = AH_NULL;
630	const RAW_DATA_PER_CHANNEL_2317 *data=AH_NULL;
631	uint16_t numChannels;
632	int totalD,totalF, totalMin,last, i;
633
634	*maxPow = 0;
635
636	if (IEEE80211_IS_CHAN_G(chan) || IEEE80211_IS_CHAN_108G(chan))
637		pRawDataset = &ee->ee_rawDataset2413[headerInfo11G];
638	else if (IEEE80211_IS_CHAN_B(chan))
639		pRawDataset = &ee->ee_rawDataset2413[headerInfo11B];
640	else
641		return(AH_FALSE);
642
643	numChannels = pRawDataset->numChannels;
644	data = pRawDataset->pDataPerChannel;
645
646	/* Make sure the channel is in the range of the TP values
647	 *  (freq piers)
648	 */
649	if (numChannels < 1)
650		return(AH_FALSE);
651
652	if ((freq < data[0].channelValue) ||
653	    (freq > data[numChannels-1].channelValue)) {
654		if (freq < data[0].channelValue) {
655			*maxPow = ar2317GetMaxPower(ah, &data[0]);
656			*minPow = ar2317GetMinPower(ah, &data[0]);
657			return(AH_TRUE);
658		} else {
659			*maxPow = ar2317GetMaxPower(ah, &data[numChannels - 1]);
660			*minPow = ar2317GetMinPower(ah, &data[numChannels - 1]);
661			return(AH_TRUE);
662		}
663	}
664
665	/* Linearly interpolate the power value now */
666	for (last=0,i=0; (i<numChannels) && (freq > data[i].channelValue);
667	     last = i++);
668	totalD = data[i].channelValue - data[last].channelValue;
669	if (totalD > 0) {
670		totalF = ar2317GetMaxPower(ah, &data[i]) - ar2317GetMaxPower(ah, &data[last]);
671		*maxPow = (int8_t) ((totalF*(freq-data[last].channelValue) +
672				     ar2317GetMaxPower(ah, &data[last])*totalD)/totalD);
673		totalMin = ar2317GetMinPower(ah, &data[i]) - ar2317GetMinPower(ah, &data[last]);
674		*minPow = (int8_t) ((totalMin*(freq-data[last].channelValue) +
675				     ar2317GetMinPower(ah, &data[last])*totalD)/totalD);
676		return(AH_TRUE);
677	} else {
678		if (freq == data[i].channelValue) {
679			*maxPow = ar2317GetMaxPower(ah, &data[i]);
680			*minPow = ar2317GetMinPower(ah, &data[i]);
681			return(AH_TRUE);
682		} else
683			return(AH_FALSE);
684	}
685}
686
687/*
688 * Free memory for analog bank scratch buffers
689 */
690static void
691ar2317RfDetach(struct ath_hal *ah)
692{
693	struct ath_hal_5212 *ahp = AH5212(ah);
694
695	HALASSERT(ahp->ah_rfHal != AH_NULL);
696	ath_hal_free(ahp->ah_rfHal);
697	ahp->ah_rfHal = AH_NULL;
698}
699
700/*
701 * Allocate memory for analog bank scratch buffers
702 * Scratch Buffer will be reinitialized every reset so no need to zero now
703 */
704static HAL_BOOL
705ar2317RfAttach(struct ath_hal *ah, HAL_STATUS *status)
706{
707	struct ath_hal_5212 *ahp = AH5212(ah);
708	struct ar2317State *priv;
709
710	HALASSERT(ah->ah_magic == AR5212_MAGIC);
711
712	HALASSERT(ahp->ah_rfHal == AH_NULL);
713	priv = ath_hal_malloc(sizeof(struct ar2317State));
714	if (priv == AH_NULL) {
715		HALDEBUG(ah, HAL_DEBUG_ANY,
716		    "%s: cannot allocate private state\n", __func__);
717		*status = HAL_ENOMEM;		/* XXX */
718		return AH_FALSE;
719	}
720	priv->base.rfDetach		= ar2317RfDetach;
721	priv->base.writeRegs		= ar2317WriteRegs;
722	priv->base.getRfBank		= ar2317GetRfBank;
723	priv->base.setChannel		= ar2317SetChannel;
724	priv->base.setRfRegs		= ar2317SetRfRegs;
725	priv->base.setPowerTable	= ar2317SetPowerTable;
726	priv->base.getChannelMaxMinPower = ar2317GetChannelMaxMinPower;
727	priv->base.getNfAdjust		= ar5212GetNfAdjust;
728
729	ahp->ah_pcdacTable = priv->pcdacTable;
730	ahp->ah_pcdacTableSize = sizeof(priv->pcdacTable);
731	ahp->ah_rfHal = &priv->base;
732
733	return AH_TRUE;
734}
735
736static HAL_BOOL
737ar2317Probe(struct ath_hal *ah)
738{
739	return IS_2317(ah);
740}
741AH_RF(RF2317, ar2317Probe, ar2317RfAttach);
742