1 | /* |
2 | * Copyright (c) 2002-2008 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 | * $Id: ar5112.c,v 1.1.1.1 2008/12/11 04:46:37 alc Exp $ |
18 | */ |
19 | #include "opt_ah.h" |
20 | |
21 | #include "ah.h" |
22 | #include "ah_internal.h" |
23 | |
24 | #include "ah_eeprom_v3.h" |
25 | |
26 | #include "ar5212/ar5212.h" |
27 | #include "ar5212/ar5212reg.h" |
28 | #include "ar5212/ar5212phy.h" |
29 | |
30 | #define AH_5212_5112 |
31 | #include "ar5212/ar5212.ini" |
32 | |
33 | #define N(a) (sizeof(a)/sizeof(a[0])) |
34 | |
35 | struct ar5112State { |
36 | RF_HAL_FUNCS base; /* public state, must be first */ |
37 | uint16_t pcdacTable[PWR_TABLE_SIZE]; |
38 | |
39 | uint32_t Bank1Data[N(ar5212Bank1_5112)]; |
40 | uint32_t Bank2Data[N(ar5212Bank2_5112)]; |
41 | uint32_t Bank3Data[N(ar5212Bank3_5112)]; |
42 | uint32_t Bank6Data[N(ar5212Bank6_5112)]; |
43 | uint32_t Bank7Data[N(ar5212Bank7_5112)]; |
44 | }; |
45 | #define AR5112(ah) ((struct ar5112State *) AH5212(ah)->ah_rfHal) |
46 | |
47 | static void ar5212GetLowerUpperIndex(uint16_t v, |
48 | uint16_t *lp, uint16_t listSize, |
49 | uint32_t *vlo, uint32_t *vhi); |
50 | static HAL_BOOL getFullPwrTable(uint16_t numPcdacs, uint16_t *pcdacs, |
51 | int16_t *power, int16_t maxPower, int16_t *retVals); |
52 | static int16_t getPminAndPcdacTableFromPowerTable(int16_t *pwrTableT4, |
53 | uint16_t retVals[]); |
54 | static int16_t getPminAndPcdacTableFromTwoPowerTables(int16_t *pwrTableLXpdT4, |
55 | int16_t *pwrTableHXpdT4, uint16_t retVals[], int16_t *pMid); |
56 | static int16_t interpolate_signed(uint16_t target, |
57 | uint16_t srcLeft, uint16_t srcRight, |
58 | int16_t targetLeft, int16_t targetRight); |
59 | |
60 | extern void ar5212ModifyRfBuffer(uint32_t *rfBuf, uint32_t reg32, |
61 | uint32_t numBits, uint32_t firstBit, uint32_t column); |
62 | |
63 | static void |
64 | ar5112WriteRegs(struct ath_hal *ah, u_int modesIndex, u_int freqIndex, |
65 | int writes) |
66 | { |
67 | HAL_INI_WRITE_ARRAY(ah, ar5212Modes_5112, modesIndex, writes); |
68 | HAL_INI_WRITE_ARRAY(ah, ar5212Common_5112, 1, writes); |
69 | HAL_INI_WRITE_ARRAY(ah, ar5212BB_RfGain_5112, freqIndex, writes); |
70 | } |
71 | |
72 | /* |
73 | * Take the MHz channel value and set the Channel value |
74 | * |
75 | * ASSUMES: Writes enabled to analog bus |
76 | */ |
77 | static HAL_BOOL |
78 | ar5112SetChannel(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *chan) |
79 | { |
80 | uint32_t channelSel = 0; |
81 | uint32_t bModeSynth = 0; |
82 | uint32_t aModeRefSel = 0; |
83 | uint32_t reg32 = 0; |
84 | uint16_t freq; |
85 | |
86 | OS_MARK(ah, AH_MARK_SETCHANNEL, chan->channel); |
87 | |
88 | if (chan->channel < 4800) { |
89 | uint32_t txctl; |
90 | |
91 | if (((chan->channel - 2192) % 5) == 0) { |
92 | channelSel = ((chan->channel - 672) * 2 - 3040)/10; |
93 | bModeSynth = 0; |
94 | } else if (((chan->channel - 2224) % 5) == 0) { |
95 | channelSel = ((chan->channel - 704) * 2 - 3040) / 10; |
96 | bModeSynth = 1; |
97 | } else { |
98 | HALDEBUG(ah, HAL_DEBUG_ANY, |
99 | "%s: invalid channel %u MHz\n" , |
100 | __func__, chan->channel); |
101 | return AH_FALSE; |
102 | } |
103 | |
104 | channelSel = (channelSel << 2) & 0xff; |
105 | channelSel = ath_hal_reverseBits(channelSel, 8); |
106 | |
107 | txctl = OS_REG_READ(ah, AR_PHY_CCK_TX_CTRL); |
108 | if (chan->channel == 2484) { |
109 | /* Enable channel spreading for channel 14 */ |
110 | OS_REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, |
111 | txctl | AR_PHY_CCK_TX_CTRL_JAPAN); |
112 | } else { |
113 | OS_REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, |
114 | txctl &~ AR_PHY_CCK_TX_CTRL_JAPAN); |
115 | } |
116 | } else if (((chan->channel % 5) == 2) && (chan->channel <= 5435)) { |
117 | freq = chan->channel - 2; /* Align to even 5MHz raster */ |
118 | channelSel = ath_hal_reverseBits( |
119 | (uint32_t)(((freq - 4800)*10)/25 + 1), 8); |
120 | aModeRefSel = ath_hal_reverseBits(0, 2); |
121 | } else if ((chan->channel % 20) == 0 && chan->channel >= 5120) { |
122 | channelSel = ath_hal_reverseBits( |
123 | ((chan->channel - 4800) / 20 << 2), 8); |
124 | aModeRefSel = ath_hal_reverseBits(3, 2); |
125 | } else if ((chan->channel % 10) == 0) { |
126 | channelSel = ath_hal_reverseBits( |
127 | ((chan->channel - 4800) / 10 << 1), 8); |
128 | aModeRefSel = ath_hal_reverseBits(2, 2); |
129 | } else if ((chan->channel % 5) == 0) { |
130 | channelSel = ath_hal_reverseBits( |
131 | (chan->channel - 4800) / 5, 8); |
132 | aModeRefSel = ath_hal_reverseBits(1, 2); |
133 | } else { |
134 | HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u MHz\n" , |
135 | __func__, chan->channel); |
136 | return AH_FALSE; |
137 | } |
138 | |
139 | reg32 = (channelSel << 4) | (aModeRefSel << 2) | (bModeSynth << 1) | |
140 | (1 << 12) | 0x1; |
141 | OS_REG_WRITE(ah, AR_PHY(0x27), reg32 & 0xff); |
142 | |
143 | reg32 >>= 8; |
144 | OS_REG_WRITE(ah, AR_PHY(0x36), reg32 & 0x7f); |
145 | |
146 | AH_PRIVATE(ah)->ah_curchan = chan; |
147 | return AH_TRUE; |
148 | } |
149 | |
150 | /* |
151 | * Return a reference to the requested RF Bank. |
152 | */ |
153 | static uint32_t * |
154 | ar5112GetRfBank(struct ath_hal *ah, int bank) |
155 | { |
156 | struct ar5112State *priv = AR5112(ah); |
157 | |
158 | HALASSERT(priv != AH_NULL); |
159 | switch (bank) { |
160 | case 1: return priv->Bank1Data; |
161 | case 2: return priv->Bank2Data; |
162 | case 3: return priv->Bank3Data; |
163 | case 6: return priv->Bank6Data; |
164 | case 7: return priv->Bank7Data; |
165 | } |
166 | HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown RF Bank %d requested\n" , |
167 | __func__, bank); |
168 | return AH_NULL; |
169 | } |
170 | |
171 | /* |
172 | * Reads EEPROM header info from device structure and programs |
173 | * all rf registers |
174 | * |
175 | * REQUIRES: Access to the analog rf device |
176 | */ |
177 | static HAL_BOOL |
178 | ar5112SetRfRegs(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *chan, |
179 | uint16_t modesIndex, uint16_t *rfXpdGain) |
180 | { |
181 | #define RF_BANK_SETUP(_priv, _ix, _col) do { \ |
182 | int i; \ |
183 | for (i = 0; i < N(ar5212Bank##_ix##_5112); i++) \ |
184 | (_priv)->Bank##_ix##Data[i] = ar5212Bank##_ix##_5112[i][_col];\ |
185 | } while (0) |
186 | struct ath_hal_5212 *ahp = AH5212(ah); |
187 | const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; |
188 | uint16_t rfXpdSel, gainI; |
189 | uint16_t ob5GHz = 0, db5GHz = 0; |
190 | uint16_t ob2GHz = 0, db2GHz = 0; |
191 | struct ar5112State *priv = AR5112(ah); |
192 | GAIN_VALUES *gv = &ahp->ah_gainValues; |
193 | int regWrites = 0; |
194 | |
195 | HALASSERT(priv); |
196 | |
197 | /* Setup rf parameters */ |
198 | switch (chan->channelFlags & CHANNEL_ALL) { |
199 | case CHANNEL_A: |
200 | case CHANNEL_T: |
201 | if (chan->channel > 4000 && chan->channel < 5260) { |
202 | ob5GHz = ee->ee_ob1; |
203 | db5GHz = ee->ee_db1; |
204 | } else if (chan->channel >= 5260 && chan->channel < 5500) { |
205 | ob5GHz = ee->ee_ob2; |
206 | db5GHz = ee->ee_db2; |
207 | } else if (chan->channel >= 5500 && chan->channel < 5725) { |
208 | ob5GHz = ee->ee_ob3; |
209 | db5GHz = ee->ee_db3; |
210 | } else if (chan->channel >= 5725) { |
211 | ob5GHz = ee->ee_ob4; |
212 | db5GHz = ee->ee_db4; |
213 | } else { |
214 | /* XXX else */ |
215 | } |
216 | rfXpdSel = ee->ee_xpd[headerInfo11A]; |
217 | gainI = ee->ee_gainI[headerInfo11A]; |
218 | break; |
219 | case CHANNEL_B: |
220 | ob2GHz = ee->ee_ob2GHz[0]; |
221 | db2GHz = ee->ee_db2GHz[0]; |
222 | rfXpdSel = ee->ee_xpd[headerInfo11B]; |
223 | gainI = ee->ee_gainI[headerInfo11B]; |
224 | break; |
225 | case CHANNEL_G: |
226 | case CHANNEL_108G: |
227 | ob2GHz = ee->ee_ob2GHz[1]; |
228 | db2GHz = ee->ee_ob2GHz[1]; |
229 | rfXpdSel = ee->ee_xpd[headerInfo11G]; |
230 | gainI = ee->ee_gainI[headerInfo11G]; |
231 | break; |
232 | default: |
233 | HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel flags 0x%x\n" , |
234 | __func__, chan->channelFlags); |
235 | return AH_FALSE; |
236 | } |
237 | |
238 | /* Setup Bank 1 Write */ |
239 | RF_BANK_SETUP(priv, 1, 1); |
240 | |
241 | /* Setup Bank 2 Write */ |
242 | RF_BANK_SETUP(priv, 2, modesIndex); |
243 | |
244 | /* Setup Bank 3 Write */ |
245 | RF_BANK_SETUP(priv, 3, modesIndex); |
246 | |
247 | /* Setup Bank 6 Write */ |
248 | RF_BANK_SETUP(priv, 6, modesIndex); |
249 | |
250 | ar5212ModifyRfBuffer(priv->Bank6Data, rfXpdSel, 1, 302, 0); |
251 | |
252 | ar5212ModifyRfBuffer(priv->Bank6Data, rfXpdGain[0], 2, 270, 0); |
253 | ar5212ModifyRfBuffer(priv->Bank6Data, rfXpdGain[1], 2, 257, 0); |
254 | |
255 | if (IS_CHAN_OFDM(chan)) { |
256 | ar5212ModifyRfBuffer(priv->Bank6Data, |
257 | gv->currStep->paramVal[GP_PWD_138], 1, 168, 3); |
258 | ar5212ModifyRfBuffer(priv->Bank6Data, |
259 | gv->currStep->paramVal[GP_PWD_137], 1, 169, 3); |
260 | ar5212ModifyRfBuffer(priv->Bank6Data, |
261 | gv->currStep->paramVal[GP_PWD_136], 1, 170, 3); |
262 | ar5212ModifyRfBuffer(priv->Bank6Data, |
263 | gv->currStep->paramVal[GP_PWD_132], 1, 174, 3); |
264 | ar5212ModifyRfBuffer(priv->Bank6Data, |
265 | gv->currStep->paramVal[GP_PWD_131], 1, 175, 3); |
266 | ar5212ModifyRfBuffer(priv->Bank6Data, |
267 | gv->currStep->paramVal[GP_PWD_130], 1, 176, 3); |
268 | } |
269 | |
270 | /* Only the 5 or 2 GHz OB/DB need to be set for a mode */ |
271 | if (IS_CHAN_2GHZ(chan)) { |
272 | ar5212ModifyRfBuffer(priv->Bank6Data, ob2GHz, 3, 287, 0); |
273 | ar5212ModifyRfBuffer(priv->Bank6Data, db2GHz, 3, 290, 0); |
274 | } else { |
275 | ar5212ModifyRfBuffer(priv->Bank6Data, ob5GHz, 3, 279, 0); |
276 | ar5212ModifyRfBuffer(priv->Bank6Data, db5GHz, 3, 282, 0); |
277 | } |
278 | |
279 | /* Lower synth voltage for X112 Rev 2.0 only */ |
280 | if (IS_RADX112_REV2(ah)) { |
281 | /* Non-Reversed analyg registers - so values are pre-reversed */ |
282 | ar5212ModifyRfBuffer(priv->Bank6Data, 2, 2, 90, 2); |
283 | ar5212ModifyRfBuffer(priv->Bank6Data, 2, 2, 92, 2); |
284 | ar5212ModifyRfBuffer(priv->Bank6Data, 2, 2, 94, 2); |
285 | ar5212ModifyRfBuffer(priv->Bank6Data, 2, 1, 254, 2); |
286 | } |
287 | |
288 | /* Decrease Power Consumption for 5312/5213 and up */ |
289 | if (AH_PRIVATE(ah)->ah_phyRev >= AR_PHY_CHIP_ID_REV_2) { |
290 | ar5212ModifyRfBuffer(priv->Bank6Data, 1, 1, 281, 1); |
291 | ar5212ModifyRfBuffer(priv->Bank6Data, 1, 2, 1, 3); |
292 | ar5212ModifyRfBuffer(priv->Bank6Data, 1, 2, 3, 3); |
293 | ar5212ModifyRfBuffer(priv->Bank6Data, 1, 1, 139, 3); |
294 | ar5212ModifyRfBuffer(priv->Bank6Data, 1, 1, 140, 3); |
295 | } |
296 | |
297 | /* Setup Bank 7 Setup */ |
298 | RF_BANK_SETUP(priv, 7, modesIndex); |
299 | if (IS_CHAN_OFDM(chan)) |
300 | ar5212ModifyRfBuffer(priv->Bank7Data, |
301 | gv->currStep->paramVal[GP_MIXGAIN_OVR], 2, 37, 0); |
302 | |
303 | ar5212ModifyRfBuffer(priv->Bank7Data, gainI, 6, 14, 0); |
304 | |
305 | /* Adjust params for Derby TX power control */ |
306 | if (IS_CHAN_HALF_RATE(chan) || IS_CHAN_QUARTER_RATE(chan)) { |
307 | uint32_t rfDelay, rfPeriod; |
308 | |
309 | rfDelay = 0xf; |
310 | rfPeriod = (IS_CHAN_HALF_RATE(chan)) ? 0x8 : 0xf; |
311 | ar5212ModifyRfBuffer(priv->Bank7Data, rfDelay, 4, 58, 0); |
312 | ar5212ModifyRfBuffer(priv->Bank7Data, rfPeriod, 4, 70, 0); |
313 | } |
314 | |
315 | #ifdef notyet |
316 | /* Analog registers are setup - EAR can modify */ |
317 | if (ar5212IsEarEngaged(pDev, chan)) |
318 | uint32_t modifier; |
319 | ar5212EarModify(pDev, EAR_LC_RF_WRITE, chan, &modifier); |
320 | #endif |
321 | /* Write Analog registers */ |
322 | HAL_INI_WRITE_BANK(ah, ar5212Bank1_5112, priv->Bank1Data, regWrites); |
323 | HAL_INI_WRITE_BANK(ah, ar5212Bank2_5112, priv->Bank2Data, regWrites); |
324 | HAL_INI_WRITE_BANK(ah, ar5212Bank3_5112, priv->Bank3Data, regWrites); |
325 | HAL_INI_WRITE_BANK(ah, ar5212Bank6_5112, priv->Bank6Data, regWrites); |
326 | HAL_INI_WRITE_BANK(ah, ar5212Bank7_5112, priv->Bank7Data, regWrites); |
327 | |
328 | /* Now that we have reprogrammed rfgain value, clear the flag. */ |
329 | ahp->ah_rfgainState = HAL_RFGAIN_INACTIVE; |
330 | return AH_TRUE; |
331 | #undef RF_BANK_SETUP |
332 | } |
333 | |
334 | /* |
335 | * Read the transmit power levels from the structures taken from EEPROM |
336 | * Interpolate read transmit power values for this channel |
337 | * Organize the transmit power values into a table for writing into the hardware |
338 | */ |
339 | static HAL_BOOL |
340 | ar5112SetPowerTable(struct ath_hal *ah, |
341 | int16_t *pPowerMin, int16_t *pPowerMax, HAL_CHANNEL_INTERNAL *chan, |
342 | uint16_t *rfXpdGain) |
343 | { |
344 | struct ath_hal_5212 *ahp = AH5212(ah); |
345 | const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; |
346 | uint32_t numXpdGain = IS_RADX112_REV2(ah) ? 2 : 1; |
347 | uint32_t xpdGainMask = 0; |
348 | int16_t powerMid, *pPowerMid = &powerMid; |
349 | |
350 | const EXPN_DATA_PER_CHANNEL_5112 *pRawCh; |
351 | const EEPROM_POWER_EXPN_5112 *pPowerExpn = AH_NULL; |
352 | |
353 | uint32_t ii, jj, kk; |
354 | int16_t minPwr_t4, maxPwr_t4, Pmin, Pmid; |
355 | |
356 | uint32_t chan_idx_L = 0, chan_idx_R = 0; |
357 | uint16_t chan_L, chan_R; |
358 | |
359 | int16_t pwr_table0[64]; |
360 | int16_t pwr_table1[64]; |
361 | uint16_t pcdacs[10]; |
362 | int16_t powers[10]; |
363 | uint16_t numPcd; |
364 | int16_t powTableLXPD[2][64]; |
365 | int16_t powTableHXPD[2][64]; |
366 | int16_t tmpPowerTable[64]; |
367 | uint16_t xgainList[2]; |
368 | uint16_t xpdMask; |
369 | |
370 | switch (chan->channelFlags & CHANNEL_ALL) { |
371 | case CHANNEL_A: |
372 | case CHANNEL_T: |
373 | pPowerExpn = &ee->ee_modePowerArray5112[headerInfo11A]; |
374 | xpdGainMask = ee->ee_xgain[headerInfo11A]; |
375 | break; |
376 | case CHANNEL_B: |
377 | pPowerExpn = &ee->ee_modePowerArray5112[headerInfo11B]; |
378 | xpdGainMask = ee->ee_xgain[headerInfo11B]; |
379 | break; |
380 | case CHANNEL_G: |
381 | case CHANNEL_108G: |
382 | pPowerExpn = &ee->ee_modePowerArray5112[headerInfo11G]; |
383 | xpdGainMask = ee->ee_xgain[headerInfo11G]; |
384 | break; |
385 | default: |
386 | HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown channel flags 0x%x\n" , |
387 | __func__, chan->channelFlags & CHANNEL_ALL); |
388 | return AH_FALSE; |
389 | } |
390 | |
391 | if ((xpdGainMask & pPowerExpn->xpdMask) < 1) { |
392 | HALDEBUG(ah, HAL_DEBUG_ANY, |
393 | "%s: desired xpdGainMask 0x%x not supported by " |
394 | "calibrated xpdMask 0x%x\n" , __func__, |
395 | xpdGainMask, pPowerExpn->xpdMask); |
396 | return AH_FALSE; |
397 | } |
398 | |
399 | maxPwr_t4 = (int16_t)(2*(*pPowerMax)); /* pwr_t2 -> pwr_t4 */ |
400 | minPwr_t4 = (int16_t)(2*(*pPowerMin)); /* pwr_t2 -> pwr_t4 */ |
401 | |
402 | xgainList[0] = 0xDEAD; |
403 | xgainList[1] = 0xDEAD; |
404 | |
405 | kk = 0; |
406 | xpdMask = pPowerExpn->xpdMask; |
407 | for (jj = 0; jj < NUM_XPD_PER_CHANNEL; jj++) { |
408 | if (((xpdMask >> jj) & 1) > 0) { |
409 | if (kk > 1) { |
410 | HALDEBUG(ah, HAL_DEBUG_ANY, |
411 | "A maximum of 2 xpdGains supported" |
412 | "in pExpnPower data\n" ); |
413 | return AH_FALSE; |
414 | } |
415 | xgainList[kk++] = (uint16_t)jj; |
416 | } |
417 | } |
418 | |
419 | ar5212GetLowerUpperIndex(chan->channel, &pPowerExpn->pChannels[0], |
420 | pPowerExpn->numChannels, &chan_idx_L, &chan_idx_R); |
421 | |
422 | kk = 0; |
423 | for (ii = chan_idx_L; ii <= chan_idx_R; ii++) { |
424 | pRawCh = &(pPowerExpn->pDataPerChannel[ii]); |
425 | if (xgainList[1] == 0xDEAD) { |
426 | jj = xgainList[0]; |
427 | numPcd = pRawCh->pDataPerXPD[jj].numPcdacs; |
428 | OS_MEMCPY(&pcdacs[0], &pRawCh->pDataPerXPD[jj].pcdac[0], |
429 | numPcd * sizeof(uint16_t)); |
430 | OS_MEMCPY(&powers[0], &pRawCh->pDataPerXPD[jj].pwr_t4[0], |
431 | numPcd * sizeof(int16_t)); |
432 | if (!getFullPwrTable(numPcd, &pcdacs[0], &powers[0], |
433 | pRawCh->maxPower_t4, &tmpPowerTable[0])) { |
434 | return AH_FALSE; |
435 | } |
436 | OS_MEMCPY(&powTableLXPD[kk][0], &tmpPowerTable[0], |
437 | 64*sizeof(int16_t)); |
438 | } else { |
439 | jj = xgainList[0]; |
440 | numPcd = pRawCh->pDataPerXPD[jj].numPcdacs; |
441 | OS_MEMCPY(&pcdacs[0], &pRawCh->pDataPerXPD[jj].pcdac[0], |
442 | numPcd*sizeof(uint16_t)); |
443 | OS_MEMCPY(&powers[0], |
444 | &pRawCh->pDataPerXPD[jj].pwr_t4[0], |
445 | numPcd*sizeof(int16_t)); |
446 | if (!getFullPwrTable(numPcd, &pcdacs[0], &powers[0], |
447 | pRawCh->maxPower_t4, &tmpPowerTable[0])) { |
448 | return AH_FALSE; |
449 | } |
450 | OS_MEMCPY(&powTableLXPD[kk][0], &tmpPowerTable[0], |
451 | 64 * sizeof(int16_t)); |
452 | |
453 | jj = xgainList[1]; |
454 | numPcd = pRawCh->pDataPerXPD[jj].numPcdacs; |
455 | OS_MEMCPY(&pcdacs[0], &pRawCh->pDataPerXPD[jj].pcdac[0], |
456 | numPcd * sizeof(uint16_t)); |
457 | OS_MEMCPY(&powers[0], |
458 | &pRawCh->pDataPerXPD[jj].pwr_t4[0], |
459 | numPcd * sizeof(int16_t)); |
460 | if (!getFullPwrTable(numPcd, &pcdacs[0], &powers[0], |
461 | pRawCh->maxPower_t4, &tmpPowerTable[0])) { |
462 | return AH_FALSE; |
463 | } |
464 | OS_MEMCPY(&powTableHXPD[kk][0], &tmpPowerTable[0], |
465 | 64 * sizeof(int16_t)); |
466 | } |
467 | kk++; |
468 | } |
469 | |
470 | chan_L = pPowerExpn->pChannels[chan_idx_L]; |
471 | chan_R = pPowerExpn->pChannels[chan_idx_R]; |
472 | kk = chan_idx_R - chan_idx_L; |
473 | |
474 | if (xgainList[1] == 0xDEAD) { |
475 | for (jj = 0; jj < 64; jj++) { |
476 | pwr_table0[jj] = interpolate_signed( |
477 | chan->channel, chan_L, chan_R, |
478 | powTableLXPD[0][jj], powTableLXPD[kk][jj]); |
479 | } |
480 | Pmin = getPminAndPcdacTableFromPowerTable(&pwr_table0[0], |
481 | ahp->ah_pcdacTable); |
482 | *pPowerMin = (int16_t) (Pmin / 2); |
483 | *pPowerMid = (int16_t) (pwr_table0[63] / 2); |
484 | *pPowerMax = (int16_t) (pwr_table0[63] / 2); |
485 | rfXpdGain[0] = xgainList[0]; |
486 | rfXpdGain[1] = rfXpdGain[0]; |
487 | } else { |
488 | for (jj = 0; jj < 64; jj++) { |
489 | pwr_table0[jj] = interpolate_signed( |
490 | chan->channel, chan_L, chan_R, |
491 | powTableLXPD[0][jj], powTableLXPD[kk][jj]); |
492 | pwr_table1[jj] = interpolate_signed( |
493 | chan->channel, chan_L, chan_R, |
494 | powTableHXPD[0][jj], powTableHXPD[kk][jj]); |
495 | } |
496 | if (numXpdGain == 2) { |
497 | Pmin = getPminAndPcdacTableFromTwoPowerTables( |
498 | &pwr_table0[0], &pwr_table1[0], |
499 | ahp->ah_pcdacTable, &Pmid); |
500 | *pPowerMin = (int16_t) (Pmin / 2); |
501 | *pPowerMid = (int16_t) (Pmid / 2); |
502 | *pPowerMax = (int16_t) (pwr_table0[63] / 2); |
503 | rfXpdGain[0] = xgainList[0]; |
504 | rfXpdGain[1] = xgainList[1]; |
505 | } else if (minPwr_t4 <= pwr_table1[63] && |
506 | maxPwr_t4 <= pwr_table1[63]) { |
507 | Pmin = getPminAndPcdacTableFromPowerTable( |
508 | &pwr_table1[0], ahp->ah_pcdacTable); |
509 | rfXpdGain[0] = xgainList[1]; |
510 | rfXpdGain[1] = rfXpdGain[0]; |
511 | *pPowerMin = (int16_t) (Pmin / 2); |
512 | *pPowerMid = (int16_t) (pwr_table1[63] / 2); |
513 | *pPowerMax = (int16_t) (pwr_table1[63] / 2); |
514 | } else { |
515 | Pmin = getPminAndPcdacTableFromPowerTable( |
516 | &pwr_table0[0], ahp->ah_pcdacTable); |
517 | rfXpdGain[0] = xgainList[0]; |
518 | rfXpdGain[1] = rfXpdGain[0]; |
519 | *pPowerMin = (int16_t) (Pmin/2); |
520 | *pPowerMid = (int16_t) (pwr_table0[63] / 2); |
521 | *pPowerMax = (int16_t) (pwr_table0[63] / 2); |
522 | } |
523 | } |
524 | |
525 | /* |
526 | * Move 5112 rates to match power tables where the max |
527 | * power table entry corresponds with maxPower. |
528 | */ |
529 | HALASSERT(*pPowerMax <= PCDAC_STOP); |
530 | ahp->ah_txPowerIndexOffset = PCDAC_STOP - *pPowerMax; |
531 | |
532 | return AH_TRUE; |
533 | } |
534 | |
535 | /* |
536 | * Returns interpolated or the scaled up interpolated value |
537 | */ |
538 | static int16_t |
539 | interpolate_signed(uint16_t target, uint16_t srcLeft, uint16_t srcRight, |
540 | int16_t targetLeft, int16_t targetRight) |
541 | { |
542 | int16_t rv; |
543 | |
544 | if (srcRight != srcLeft) { |
545 | rv = ((target - srcLeft)*targetRight + |
546 | (srcRight - target)*targetLeft) / (srcRight - srcLeft); |
547 | } else { |
548 | rv = targetLeft; |
549 | } |
550 | return rv; |
551 | } |
552 | |
553 | /* |
554 | * Return indices surrounding the value in sorted integer lists. |
555 | * |
556 | * NB: the input list is assumed to be sorted in ascending order |
557 | */ |
558 | static void |
559 | ar5212GetLowerUpperIndex(uint16_t v, uint16_t *lp, uint16_t listSize, |
560 | uint32_t *vlo, uint32_t *vhi) |
561 | { |
562 | uint32_t target = v; |
563 | uint16_t *ep = lp+listSize; |
564 | uint16_t *tp; |
565 | |
566 | /* |
567 | * Check first and last elements for out-of-bounds conditions. |
568 | */ |
569 | if (target < lp[0]) { |
570 | *vlo = *vhi = 0; |
571 | return; |
572 | } |
573 | if (target >= ep[-1]) { |
574 | *vlo = *vhi = listSize - 1; |
575 | return; |
576 | } |
577 | |
578 | /* look for value being near or between 2 values in list */ |
579 | for (tp = lp; tp < ep; tp++) { |
580 | /* |
581 | * If value is close to the current value of the list |
582 | * then target is not between values, it is one of the values |
583 | */ |
584 | if (*tp == target) { |
585 | *vlo = *vhi = tp - lp; |
586 | return; |
587 | } |
588 | /* |
589 | * Look for value being between current value and next value |
590 | * if so return these 2 values |
591 | */ |
592 | if (target < tp[1]) { |
593 | *vlo = tp - lp; |
594 | *vhi = *vlo + 1; |
595 | return; |
596 | } |
597 | } |
598 | } |
599 | |
600 | static HAL_BOOL |
601 | getFullPwrTable(uint16_t numPcdacs, uint16_t *pcdacs, int16_t *power, int16_t maxPower, int16_t *retVals) |
602 | { |
603 | uint16_t ii; |
604 | uint16_t idxL = 0; |
605 | uint16_t idxR = 1; |
606 | |
607 | if (numPcdacs < 2) { |
608 | HALDEBUG(AH_NULL, HAL_DEBUG_ANY, |
609 | "%s: at least 2 pcdac values needed [%d]\n" , |
610 | __func__, numPcdacs); |
611 | return AH_FALSE; |
612 | } |
613 | for (ii = 0; ii < 64; ii++) { |
614 | if (ii>pcdacs[idxR] && idxR < numPcdacs-1) { |
615 | idxL++; |
616 | idxR++; |
617 | } |
618 | retVals[ii] = interpolate_signed(ii, |
619 | pcdacs[idxL], pcdacs[idxR], power[idxL], power[idxR]); |
620 | if (retVals[ii] >= maxPower) { |
621 | while (ii < 64) |
622 | retVals[ii++] = maxPower; |
623 | } |
624 | } |
625 | return AH_TRUE; |
626 | } |
627 | |
628 | /* |
629 | * Takes a single calibration curve and creates a power table. |
630 | * Adjusts the new power table so the max power is relative |
631 | * to the maximum index in the power table. |
632 | * |
633 | * WARNING: rates must be adjusted for this relative power table |
634 | */ |
635 | static int16_t |
636 | getPminAndPcdacTableFromPowerTable(int16_t *pwrTableT4, uint16_t retVals[]) |
637 | { |
638 | int16_t ii, jj, jjMax; |
639 | int16_t pMin, currPower, pMax; |
640 | |
641 | /* If the spread is > 31.5dB, keep the upper 31.5dB range */ |
642 | if ((pwrTableT4[63] - pwrTableT4[0]) > 126) { |
643 | pMin = pwrTableT4[63] - 126; |
644 | } else { |
645 | pMin = pwrTableT4[0]; |
646 | } |
647 | |
648 | pMax = pwrTableT4[63]; |
649 | jjMax = 63; |
650 | |
651 | /* Search for highest pcdac 0.25dB below maxPower */ |
652 | while ((pwrTableT4[jjMax] > (pMax - 1) ) && (jjMax >= 0)) { |
653 | jjMax--; |
654 | } |
655 | |
656 | jj = jjMax; |
657 | currPower = pMax; |
658 | for (ii = 63; ii >= 0; ii--) { |
659 | while ((jj < 64) && (jj > 0) && (pwrTableT4[jj] >= currPower)) { |
660 | jj--; |
661 | } |
662 | if (jj == 0) { |
663 | while (ii >= 0) { |
664 | retVals[ii] = retVals[ii + 1]; |
665 | ii--; |
666 | } |
667 | break; |
668 | } |
669 | retVals[ii] = jj; |
670 | currPower -= 2; // corresponds to a 0.5dB step |
671 | } |
672 | return pMin; |
673 | } |
674 | |
675 | /* |
676 | * Combines the XPD curves from two calibration sets into a single |
677 | * power table and adjusts the power table so the max power is relative |
678 | * to the maximum index in the power table |
679 | * |
680 | * WARNING: rates must be adjusted for this relative power table |
681 | */ |
682 | static int16_t |
683 | getPminAndPcdacTableFromTwoPowerTables(int16_t *pwrTableLXpdT4, |
684 | int16_t *pwrTableHXpdT4, uint16_t retVals[], int16_t *pMid) |
685 | { |
686 | int16_t ii, jj, jjMax; |
687 | int16_t pMin, pMax, currPower; |
688 | int16_t *pwrTableT4; |
689 | uint16_t msbFlag = 0x40; // turns on the 7th bit of the pcdac |
690 | |
691 | /* If the spread is > 31.5dB, keep the upper 31.5dB range */ |
692 | if ((pwrTableLXpdT4[63] - pwrTableHXpdT4[0]) > 126) { |
693 | pMin = pwrTableLXpdT4[63] - 126; |
694 | } else { |
695 | pMin = pwrTableHXpdT4[0]; |
696 | } |
697 | |
698 | pMax = pwrTableLXpdT4[63]; |
699 | jjMax = 63; |
700 | /* Search for highest pcdac 0.25dB below maxPower */ |
701 | while ((pwrTableLXpdT4[jjMax] > (pMax - 1) ) && (jjMax >= 0)){ |
702 | jjMax--; |
703 | } |
704 | |
705 | *pMid = pwrTableHXpdT4[63]; |
706 | jj = jjMax; |
707 | ii = 63; |
708 | currPower = pMax; |
709 | pwrTableT4 = &(pwrTableLXpdT4[0]); |
710 | while (ii >= 0) { |
711 | if ((currPower <= *pMid) || ( (jj == 0) && (msbFlag == 0x40))){ |
712 | msbFlag = 0x00; |
713 | pwrTableT4 = &(pwrTableHXpdT4[0]); |
714 | jj = 63; |
715 | } |
716 | while ((jj > 0) && (pwrTableT4[jj] >= currPower)) { |
717 | jj--; |
718 | } |
719 | if ((jj == 0) && (msbFlag == 0x00)) { |
720 | while (ii >= 0) { |
721 | retVals[ii] = retVals[ii+1]; |
722 | ii--; |
723 | } |
724 | break; |
725 | } |
726 | retVals[ii] = jj | msbFlag; |
727 | currPower -= 2; // corresponds to a 0.5dB step |
728 | ii--; |
729 | } |
730 | return pMin; |
731 | } |
732 | |
733 | static int16_t |
734 | ar5112GetMinPower(struct ath_hal *ah, const EXPN_DATA_PER_CHANNEL_5112 *data) |
735 | { |
736 | int i, minIndex; |
737 | int16_t minGain,minPwr,minPcdac,retVal; |
738 | |
739 | /* Assume NUM_POINTS_XPD0 > 0 */ |
740 | minGain = data->pDataPerXPD[0].xpd_gain; |
741 | for (minIndex=0,i=1; i<NUM_XPD_PER_CHANNEL; i++) { |
742 | if (data->pDataPerXPD[i].xpd_gain < minGain) { |
743 | minIndex = i; |
744 | minGain = data->pDataPerXPD[i].xpd_gain; |
745 | } |
746 | } |
747 | minPwr = data->pDataPerXPD[minIndex].pwr_t4[0]; |
748 | minPcdac = data->pDataPerXPD[minIndex].pcdac[0]; |
749 | for (i=1; i<NUM_POINTS_XPD0; i++) { |
750 | if (data->pDataPerXPD[minIndex].pwr_t4[i] < minPwr) { |
751 | minPwr = data->pDataPerXPD[minIndex].pwr_t4[i]; |
752 | minPcdac = data->pDataPerXPD[minIndex].pcdac[i]; |
753 | } |
754 | } |
755 | retVal = minPwr - (minPcdac*2); |
756 | return(retVal); |
757 | } |
758 | |
759 | static HAL_BOOL |
760 | ar5112GetChannelMaxMinPower(struct ath_hal *ah, HAL_CHANNEL *chan, |
761 | int16_t *maxPow, int16_t *minPow) |
762 | { |
763 | const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; |
764 | int numChannels=0,i,last; |
765 | int totalD, totalF,totalMin; |
766 | const EXPN_DATA_PER_CHANNEL_5112 *data=AH_NULL; |
767 | const EEPROM_POWER_EXPN_5112 *powerArray=AH_NULL; |
768 | |
769 | *maxPow = 0; |
770 | if (IS_CHAN_A(chan)) { |
771 | powerArray = ee->ee_modePowerArray5112; |
772 | data = powerArray[headerInfo11A].pDataPerChannel; |
773 | numChannels = powerArray[headerInfo11A].numChannels; |
774 | } else if (IS_CHAN_G(chan) || IS_CHAN_108G(chan)) { |
775 | /* XXX - is this correct? Should we also use the same power for turbo G? */ |
776 | powerArray = ee->ee_modePowerArray5112; |
777 | data = powerArray[headerInfo11G].pDataPerChannel; |
778 | numChannels = powerArray[headerInfo11G].numChannels; |
779 | } else if (IS_CHAN_B(chan)) { |
780 | powerArray = ee->ee_modePowerArray5112; |
781 | data = powerArray[headerInfo11B].pDataPerChannel; |
782 | numChannels = powerArray[headerInfo11B].numChannels; |
783 | } else { |
784 | return (AH_TRUE); |
785 | } |
786 | /* Make sure the channel is in the range of the TP values |
787 | * (freq piers) |
788 | */ |
789 | if (numChannels < 1) |
790 | return(AH_FALSE); |
791 | |
792 | if ((chan->channel < data[0].channelValue) || |
793 | (chan->channel > data[numChannels-1].channelValue)) { |
794 | if (chan->channel < data[0].channelValue) { |
795 | *maxPow = data[0].maxPower_t4; |
796 | *minPow = ar5112GetMinPower(ah, &data[0]); |
797 | return(AH_TRUE); |
798 | } else { |
799 | *maxPow = data[numChannels - 1].maxPower_t4; |
800 | *minPow = ar5112GetMinPower(ah, &data[numChannels - 1]); |
801 | return(AH_TRUE); |
802 | } |
803 | } |
804 | |
805 | /* Linearly interpolate the power value now */ |
806 | for (last=0,i=0; |
807 | (i<numChannels) && (chan->channel > data[i].channelValue); |
808 | last=i++); |
809 | totalD = data[i].channelValue - data[last].channelValue; |
810 | if (totalD > 0) { |
811 | totalF = data[i].maxPower_t4 - data[last].maxPower_t4; |
812 | *maxPow = (int8_t) ((totalF*(chan->channel-data[last].channelValue) + data[last].maxPower_t4*totalD)/totalD); |
813 | |
814 | totalMin = ar5112GetMinPower(ah,&data[i]) - ar5112GetMinPower(ah, &data[last]); |
815 | *minPow = (int8_t) ((totalMin*(chan->channel-data[last].channelValue) + ar5112GetMinPower(ah, &data[last])*totalD)/totalD); |
816 | return (AH_TRUE); |
817 | } else { |
818 | if (chan->channel == data[i].channelValue) { |
819 | *maxPow = data[i].maxPower_t4; |
820 | *minPow = ar5112GetMinPower(ah, &data[i]); |
821 | return(AH_TRUE); |
822 | } else |
823 | return(AH_FALSE); |
824 | } |
825 | } |
826 | |
827 | /* |
828 | * Free memory for analog bank scratch buffers |
829 | */ |
830 | static void |
831 | ar5112RfDetach(struct ath_hal *ah) |
832 | { |
833 | struct ath_hal_5212 *ahp = AH5212(ah); |
834 | |
835 | HALASSERT(ahp->ah_rfHal != AH_NULL); |
836 | ath_hal_free(ahp->ah_rfHal); |
837 | ahp->ah_rfHal = AH_NULL; |
838 | } |
839 | |
840 | /* |
841 | * Allocate memory for analog bank scratch buffers |
842 | * Scratch Buffer will be reinitialized every reset so no need to zero now |
843 | */ |
844 | static HAL_BOOL |
845 | ar5112RfAttach(struct ath_hal *ah, HAL_STATUS *status) |
846 | { |
847 | struct ath_hal_5212 *ahp = AH5212(ah); |
848 | struct ar5112State *priv; |
849 | |
850 | HALASSERT(ah->ah_magic == AR5212_MAGIC); |
851 | |
852 | HALASSERT(ahp->ah_rfHal == AH_NULL); |
853 | priv = ath_hal_malloc(sizeof(struct ar5112State)); |
854 | if (priv == AH_NULL) { |
855 | HALDEBUG(ah, HAL_DEBUG_ANY, |
856 | "%s: cannot allocate private state\n" , __func__); |
857 | *status = HAL_ENOMEM; /* XXX */ |
858 | return AH_FALSE; |
859 | } |
860 | priv->base.rfDetach = ar5112RfDetach; |
861 | priv->base.writeRegs = ar5112WriteRegs; |
862 | priv->base.getRfBank = ar5112GetRfBank; |
863 | priv->base.setChannel = ar5112SetChannel; |
864 | priv->base.setRfRegs = ar5112SetRfRegs; |
865 | priv->base.setPowerTable = ar5112SetPowerTable; |
866 | priv->base.getChannelMaxMinPower = ar5112GetChannelMaxMinPower; |
867 | priv->base.getNfAdjust = ar5212GetNfAdjust; |
868 | |
869 | ahp->ah_pcdacTable = priv->pcdacTable; |
870 | ahp->ah_pcdacTableSize = sizeof(priv->pcdacTable); |
871 | ahp->ah_rfHal = &priv->base; |
872 | |
873 | return AH_TRUE; |
874 | } |
875 | |
876 | static HAL_BOOL |
877 | ar5112Probe(struct ath_hal *ah) |
878 | { |
879 | return IS_RAD5112(ah); |
880 | } |
881 | AH_RF(RF5112, ar5112Probe, ar5112RfAttach); |
882 | |