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: ar5111.c,v 1.3 2009/01/06 06:03:57 mrg 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_5111 |
31 | #include "ar5212/ar5212.ini" |
32 | |
33 | #define N(a) (sizeof(a)/sizeof(a[0])) |
34 | |
35 | struct ar5111State { |
36 | RF_HAL_FUNCS base; /* public state, must be first */ |
37 | uint16_t pcdacTable[PWR_TABLE_SIZE]; |
38 | |
39 | uint32_t Bank0Data[N(ar5212Bank0_5111)]; |
40 | uint32_t Bank1Data[N(ar5212Bank1_5111)]; |
41 | uint32_t Bank2Data[N(ar5212Bank2_5111)]; |
42 | uint32_t Bank3Data[N(ar5212Bank3_5111)]; |
43 | uint32_t Bank6Data[N(ar5212Bank6_5111)]; |
44 | uint32_t Bank7Data[N(ar5212Bank7_5111)]; |
45 | }; |
46 | #define AR5111(ah) ((struct ar5111State *) AH5212(ah)->ah_rfHal) |
47 | |
48 | static uint16_t ar5212GetScaledPower(uint16_t channel, uint16_t pcdacValue, |
49 | const PCDACS_EEPROM *pSrcStruct); |
50 | static HAL_BOOL ar5212FindValueInList(uint16_t channel, uint16_t pcdacValue, |
51 | const PCDACS_EEPROM *pSrcStruct, uint16_t *powerValue); |
52 | static void ar5212GetLowerUpperPcdacs(uint16_t pcdac, uint16_t channel, |
53 | const PCDACS_EEPROM *pSrcStruct, |
54 | uint16_t *pLowerPcdac, uint16_t *pUpperPcdac); |
55 | |
56 | extern void ar5212GetLowerUpperValues(uint16_t value, |
57 | const uint16_t *pList, uint16_t listSize, |
58 | uint16_t *pLowerValue, uint16_t *pUpperValue); |
59 | extern void ar5212ModifyRfBuffer(uint32_t *rfBuf, uint32_t reg32, |
60 | uint32_t numBits, uint32_t firstBit, uint32_t column); |
61 | |
62 | static void |
63 | ar5111WriteRegs(struct ath_hal *ah, u_int modesIndex, u_int freqIndex, |
64 | int writes) |
65 | { |
66 | HAL_INI_WRITE_ARRAY(ah, ar5212Modes_5111, modesIndex, writes); |
67 | HAL_INI_WRITE_ARRAY(ah, ar5212Common_5111, 1, writes); |
68 | HAL_INI_WRITE_ARRAY(ah, ar5212BB_RfGain_5111, freqIndex, writes); |
69 | } |
70 | |
71 | /* |
72 | * Take the MHz channel value and set the Channel value |
73 | * |
74 | * ASSUMES: Writes enabled to analog bus |
75 | */ |
76 | static HAL_BOOL |
77 | ar5111SetChannel(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *chan) |
78 | { |
79 | #define CI_2GHZ_INDEX_CORRECTION 19 |
80 | uint32_t refClk, reg32, data2111; |
81 | int16_t chan5111, chanIEEE; |
82 | |
83 | /* |
84 | * Structure to hold 11b tuning information for 5111/2111 |
85 | * 16 MHz mode, divider ratio = 198 = NP+S. N=16, S=4 or 6, P=12 |
86 | */ |
87 | typedef struct { |
88 | uint32_t refClkSel; /* reference clock, 1 for 16 MHz */ |
89 | uint32_t channelSelect; /* P[7:4]S[3:0] bits */ |
90 | uint16_t channel5111; /* 11a channel for 5111 */ |
91 | } CHAN_INFO_2GHZ; |
92 | |
93 | static const CHAN_INFO_2GHZ chan2GHzData[] = { |
94 | { 1, 0x46, 96 }, /* 2312 -19 */ |
95 | { 1, 0x46, 97 }, /* 2317 -18 */ |
96 | { 1, 0x46, 98 }, /* 2322 -17 */ |
97 | { 1, 0x46, 99 }, /* 2327 -16 */ |
98 | { 1, 0x46, 100 }, /* 2332 -15 */ |
99 | { 1, 0x46, 101 }, /* 2337 -14 */ |
100 | { 1, 0x46, 102 }, /* 2342 -13 */ |
101 | { 1, 0x46, 103 }, /* 2347 -12 */ |
102 | { 1, 0x46, 104 }, /* 2352 -11 */ |
103 | { 1, 0x46, 105 }, /* 2357 -10 */ |
104 | { 1, 0x46, 106 }, /* 2362 -9 */ |
105 | { 1, 0x46, 107 }, /* 2367 -8 */ |
106 | { 1, 0x46, 108 }, /* 2372 -7 */ |
107 | /* index -6 to 0 are pad to make this a nolookup table */ |
108 | { 1, 0x46, 116 }, /* -6 */ |
109 | { 1, 0x46, 116 }, /* -5 */ |
110 | { 1, 0x46, 116 }, /* -4 */ |
111 | { 1, 0x46, 116 }, /* -3 */ |
112 | { 1, 0x46, 116 }, /* -2 */ |
113 | { 1, 0x46, 116 }, /* -1 */ |
114 | { 1, 0x46, 116 }, /* 0 */ |
115 | { 1, 0x46, 116 }, /* 2412 1 */ |
116 | { 1, 0x46, 117 }, /* 2417 2 */ |
117 | { 1, 0x46, 118 }, /* 2422 3 */ |
118 | { 1, 0x46, 119 }, /* 2427 4 */ |
119 | { 1, 0x46, 120 }, /* 2432 5 */ |
120 | { 1, 0x46, 121 }, /* 2437 6 */ |
121 | { 1, 0x46, 122 }, /* 2442 7 */ |
122 | { 1, 0x46, 123 }, /* 2447 8 */ |
123 | { 1, 0x46, 124 }, /* 2452 9 */ |
124 | { 1, 0x46, 125 }, /* 2457 10 */ |
125 | { 1, 0x46, 126 }, /* 2462 11 */ |
126 | { 1, 0x46, 127 }, /* 2467 12 */ |
127 | { 1, 0x46, 128 }, /* 2472 13 */ |
128 | { 1, 0x44, 124 }, /* 2484 14 */ |
129 | { 1, 0x46, 136 }, /* 2512 15 */ |
130 | { 1, 0x46, 140 }, /* 2532 16 */ |
131 | { 1, 0x46, 144 }, /* 2552 17 */ |
132 | { 1, 0x46, 148 }, /* 2572 18 */ |
133 | { 1, 0x46, 152 }, /* 2592 19 */ |
134 | { 1, 0x46, 156 }, /* 2612 20 */ |
135 | { 1, 0x46, 160 }, /* 2632 21 */ |
136 | { 1, 0x46, 164 }, /* 2652 22 */ |
137 | { 1, 0x46, 168 }, /* 2672 23 */ |
138 | { 1, 0x46, 172 }, /* 2692 24 */ |
139 | { 1, 0x46, 176 }, /* 2712 25 */ |
140 | { 1, 0x46, 180 } /* 2732 26 */ |
141 | }; |
142 | |
143 | OS_MARK(ah, AH_MARK_SETCHANNEL, chan->channel); |
144 | |
145 | chanIEEE = ath_hal_mhz2ieee(ah, chan->channel, chan->channelFlags); |
146 | if (IS_CHAN_2GHZ(chan)) { |
147 | const CHAN_INFO_2GHZ* ci = |
148 | &chan2GHzData[chanIEEE + CI_2GHZ_INDEX_CORRECTION]; |
149 | uint32_t txctl; |
150 | |
151 | data2111 = ((ath_hal_reverseBits(ci->channelSelect, 8) & 0xff) |
152 | << 5) |
153 | | (ci->refClkSel << 4); |
154 | chan5111 = ci->channel5111; |
155 | txctl = OS_REG_READ(ah, AR_PHY_CCK_TX_CTRL); |
156 | if (chan->channel == 2484) { |
157 | /* Enable channel spreading for channel 14 */ |
158 | OS_REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, |
159 | txctl | AR_PHY_CCK_TX_CTRL_JAPAN); |
160 | } else { |
161 | OS_REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, |
162 | txctl &~ AR_PHY_CCK_TX_CTRL_JAPAN); |
163 | } |
164 | } else { |
165 | chan5111 = chanIEEE; /* no conversion needed */ |
166 | data2111 = 0; |
167 | } |
168 | |
169 | /* Rest of the code is common for 5 GHz and 2.4 GHz. */ |
170 | if (chan5111 >= 145 || (chan5111 & 0x1)) { |
171 | reg32 = ath_hal_reverseBits(chan5111 - 24, 8) & 0xff; |
172 | refClk = 1; |
173 | } else { |
174 | reg32 = ath_hal_reverseBits(((chan5111 - 24)/2), 8) & 0xff; |
175 | refClk = 0; |
176 | } |
177 | |
178 | reg32 = (reg32 << 2) | (refClk << 1) | (1 << 10) | 0x1; |
179 | OS_REG_WRITE(ah, AR_PHY(0x27), ((data2111 & 0xff) << 8) | (reg32 & 0xff)); |
180 | reg32 >>= 8; |
181 | OS_REG_WRITE(ah, AR_PHY(0x34), (data2111 & 0xff00) | (reg32 & 0xff)); |
182 | |
183 | AH_PRIVATE(ah)->ah_curchan = chan; |
184 | return AH_TRUE; |
185 | #undef CI_2GHZ_INDEX_CORRECTION |
186 | } |
187 | |
188 | /* |
189 | * Return a reference to the requested RF Bank. |
190 | */ |
191 | static uint32_t * |
192 | ar5111GetRfBank(struct ath_hal *ah, int bank) |
193 | { |
194 | struct ar5111State *priv = AR5111(ah); |
195 | |
196 | HALASSERT(priv != AH_NULL); |
197 | switch (bank) { |
198 | case 0: return priv->Bank0Data; |
199 | case 1: return priv->Bank1Data; |
200 | case 2: return priv->Bank2Data; |
201 | case 3: return priv->Bank3Data; |
202 | case 6: return priv->Bank6Data; |
203 | case 7: return priv->Bank7Data; |
204 | } |
205 | HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown RF Bank %d requested\n" , |
206 | __func__, bank); |
207 | return AH_NULL; |
208 | } |
209 | |
210 | /* |
211 | * Reads EEPROM header info from device structure and programs |
212 | * all rf registers |
213 | * |
214 | * REQUIRES: Access to the analog rf device |
215 | */ |
216 | static HAL_BOOL |
217 | ar5111SetRfRegs(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *chan, |
218 | uint16_t modesIndex, uint16_t *rfXpdGain) |
219 | { |
220 | struct ath_hal_5212 *ahp = AH5212(ah); |
221 | const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; |
222 | uint16_t rfXpdGainFixed, rfPloSel, rfPwdXpd, gainI; |
223 | uint16_t tempOB, tempDB; |
224 | uint32_t ob2GHz, db2GHz, rfReg[N(ar5212Bank6_5111)]; |
225 | int i, regWrites = 0; |
226 | |
227 | /* Setup rf parameters */ |
228 | switch (chan->channelFlags & CHANNEL_ALL) { |
229 | case CHANNEL_A: |
230 | case CHANNEL_T: |
231 | if (4000 < chan->channel && chan->channel < 5260) { |
232 | tempOB = ee->ee_ob1; |
233 | tempDB = ee->ee_db1; |
234 | } else if (5260 <= chan->channel && chan->channel < 5500) { |
235 | tempOB = ee->ee_ob2; |
236 | tempDB = ee->ee_db2; |
237 | } else if (5500 <= chan->channel && chan->channel < 5725) { |
238 | tempOB = ee->ee_ob3; |
239 | tempDB = ee->ee_db3; |
240 | } else if (chan->channel >= 5725) { |
241 | tempOB = ee->ee_ob4; |
242 | tempDB = ee->ee_db4; |
243 | } else { |
244 | /* XXX when does this happen??? */ |
245 | tempOB = tempDB = 0; |
246 | } |
247 | ob2GHz = db2GHz = 0; |
248 | |
249 | rfXpdGainFixed = ee->ee_xgain[headerInfo11A]; |
250 | rfPloSel = ee->ee_xpd[headerInfo11A]; |
251 | rfPwdXpd = !ee->ee_xpd[headerInfo11A]; |
252 | gainI = ee->ee_gainI[headerInfo11A]; |
253 | break; |
254 | case CHANNEL_B: |
255 | tempOB = ee->ee_obFor24; |
256 | tempDB = ee->ee_dbFor24; |
257 | ob2GHz = ee->ee_ob2GHz[0]; |
258 | db2GHz = ee->ee_db2GHz[0]; |
259 | |
260 | rfXpdGainFixed = ee->ee_xgain[headerInfo11B]; |
261 | rfPloSel = ee->ee_xpd[headerInfo11B]; |
262 | rfPwdXpd = !ee->ee_xpd[headerInfo11B]; |
263 | gainI = ee->ee_gainI[headerInfo11B]; |
264 | break; |
265 | case CHANNEL_G: |
266 | tempOB = ee->ee_obFor24g; |
267 | tempDB = ee->ee_dbFor24g; |
268 | ob2GHz = ee->ee_ob2GHz[1]; |
269 | db2GHz = ee->ee_db2GHz[1]; |
270 | |
271 | rfXpdGainFixed = ee->ee_xgain[headerInfo11G]; |
272 | rfPloSel = ee->ee_xpd[headerInfo11G]; |
273 | rfPwdXpd = !ee->ee_xpd[headerInfo11G]; |
274 | gainI = ee->ee_gainI[headerInfo11G]; |
275 | break; |
276 | default: |
277 | HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel flags 0x%x\n" , |
278 | __func__, chan->channelFlags); |
279 | return AH_FALSE; |
280 | } |
281 | |
282 | HALASSERT(1 <= tempOB && tempOB <= 5); |
283 | HALASSERT(1 <= tempDB && tempDB <= 5); |
284 | |
285 | /* Bank 0 Write */ |
286 | for (i = 0; i < N(ar5212Bank0_5111); i++) |
287 | rfReg[i] = ar5212Bank0_5111[i][modesIndex]; |
288 | if (IS_CHAN_2GHZ(chan)) { |
289 | ar5212ModifyRfBuffer(rfReg, ob2GHz, 3, 119, 0); |
290 | ar5212ModifyRfBuffer(rfReg, db2GHz, 3, 122, 0); |
291 | } |
292 | HAL_INI_WRITE_BANK(ah, ar5212Bank0_5111, rfReg, regWrites); |
293 | |
294 | /* Bank 1 Write */ |
295 | HAL_INI_WRITE_ARRAY(ah, ar5212Bank1_5111, 1, regWrites); |
296 | |
297 | /* Bank 2 Write */ |
298 | HAL_INI_WRITE_ARRAY(ah, ar5212Bank2_5111, modesIndex, regWrites); |
299 | |
300 | /* Bank 3 Write */ |
301 | HAL_INI_WRITE_ARRAY(ah, ar5212Bank3_5111, modesIndex, regWrites); |
302 | |
303 | /* Bank 6 Write */ |
304 | for (i = 0; i < N(ar5212Bank6_5111); i++) |
305 | rfReg[i] = ar5212Bank6_5111[i][modesIndex]; |
306 | if (IS_CHAN_A(chan)) { /* NB: CHANNEL_A | CHANNEL_T */ |
307 | ar5212ModifyRfBuffer(rfReg, ee->ee_cornerCal.pd84, 1, 51, 3); |
308 | ar5212ModifyRfBuffer(rfReg, ee->ee_cornerCal.pd90, 1, 45, 3); |
309 | } |
310 | ar5212ModifyRfBuffer(rfReg, rfPwdXpd, 1, 95, 0); |
311 | ar5212ModifyRfBuffer(rfReg, rfXpdGainFixed, 4, 96, 0); |
312 | /* Set 5212 OB & DB */ |
313 | ar5212ModifyRfBuffer(rfReg, tempOB, 3, 104, 0); |
314 | ar5212ModifyRfBuffer(rfReg, tempDB, 3, 107, 0); |
315 | HAL_INI_WRITE_BANK(ah, ar5212Bank6_5111, rfReg, regWrites); |
316 | |
317 | /* Bank 7 Write */ |
318 | for (i = 0; i < N(ar5212Bank7_5111); i++) |
319 | rfReg[i] = ar5212Bank7_5111[i][modesIndex]; |
320 | ar5212ModifyRfBuffer(rfReg, gainI, 6, 29, 0); |
321 | ar5212ModifyRfBuffer(rfReg, rfPloSel, 1, 4, 0); |
322 | |
323 | if (IS_CHAN_QUARTER_RATE(chan) || IS_CHAN_HALF_RATE(chan)) { |
324 | uint32_t rfWaitI, rfWaitS, rfMaxTime; |
325 | |
326 | rfWaitS = 0x1f; |
327 | rfWaitI = (IS_CHAN_HALF_RATE(chan)) ? 0x10 : 0x1f; |
328 | rfMaxTime = 3; |
329 | ar5212ModifyRfBuffer(rfReg, rfWaitS, 5, 19, 0); |
330 | ar5212ModifyRfBuffer(rfReg, rfWaitI, 5, 24, 0); |
331 | ar5212ModifyRfBuffer(rfReg, rfMaxTime, 2, 49, 0); |
332 | |
333 | } |
334 | |
335 | HAL_INI_WRITE_BANK(ah, ar5212Bank7_5111, rfReg, regWrites); |
336 | |
337 | /* Now that we have reprogrammed rfgain value, clear the flag. */ |
338 | ahp->ah_rfgainState = HAL_RFGAIN_INACTIVE; |
339 | |
340 | return AH_TRUE; |
341 | } |
342 | |
343 | /* |
344 | * Returns interpolated or the scaled up interpolated value |
345 | */ |
346 | static uint16_t |
347 | interpolate(uint16_t target, uint16_t srcLeft, uint16_t srcRight, |
348 | uint16_t targetLeft, uint16_t targetRight) |
349 | { |
350 | uint16_t rv; |
351 | int16_t lRatio; |
352 | |
353 | /* to get an accurate ratio, always scale, if want to scale, then don't scale back down */ |
354 | if ((targetLeft * targetRight) == 0) |
355 | return 0; |
356 | |
357 | if (srcRight != srcLeft) { |
358 | /* |
359 | * Note the ratio always need to be scaled, |
360 | * since it will be a fraction. |
361 | */ |
362 | lRatio = (target - srcLeft) * EEP_SCALE / (srcRight - srcLeft); |
363 | if (lRatio < 0) { |
364 | /* Return as Left target if value would be negative */ |
365 | rv = targetLeft; |
366 | } else if (lRatio > EEP_SCALE) { |
367 | /* Return as Right target if Ratio is greater than 100% (SCALE) */ |
368 | rv = targetRight; |
369 | } else { |
370 | rv = (lRatio * targetRight + (EEP_SCALE - lRatio) * |
371 | targetLeft) / EEP_SCALE; |
372 | } |
373 | } else { |
374 | rv = targetLeft; |
375 | } |
376 | return rv; |
377 | } |
378 | |
379 | /* |
380 | * Read the transmit power levels from the structures taken from EEPROM |
381 | * Interpolate read transmit power values for this channel |
382 | * Organize the transmit power values into a table for writing into the hardware |
383 | */ |
384 | static HAL_BOOL |
385 | ar5111SetPowerTable(struct ath_hal *ah, |
386 | int16_t *pMinPower, int16_t *pMaxPower, HAL_CHANNEL_INTERNAL *chan, |
387 | uint16_t *rfXpdGain) |
388 | { |
389 | struct ath_hal_5212 *ahp = AH5212(ah); |
390 | const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; |
391 | FULL_PCDAC_STRUCT pcdacStruct; |
392 | int i, j; |
393 | |
394 | uint16_t *pPcdacValues; |
395 | int16_t *pScaledUpDbm; |
396 | int16_t minScaledPwr; |
397 | int16_t maxScaledPwr; |
398 | int16_t pwr; |
399 | uint16_t pcdacMin = 0; |
400 | uint16_t pcdacMax = PCDAC_STOP; |
401 | uint16_t pcdacTableIndex; |
402 | uint16_t scaledPcdac; |
403 | PCDACS_EEPROM *pSrcStruct; |
404 | PCDACS_EEPROM eepromPcdacs; |
405 | |
406 | /* setup the pcdac struct to point to the correct info, based on mode */ |
407 | switch (chan->channelFlags & CHANNEL_ALL) { |
408 | case CHANNEL_A: |
409 | case CHANNEL_T: |
410 | eepromPcdacs.numChannels = ee->ee_numChannels11a; |
411 | eepromPcdacs.pChannelList = ee->ee_channels11a; |
412 | eepromPcdacs.pDataPerChannel = ee->ee_dataPerChannel11a; |
413 | break; |
414 | case CHANNEL_B: |
415 | eepromPcdacs.numChannels = ee->ee_numChannels2_4; |
416 | eepromPcdacs.pChannelList = ee->ee_channels11b; |
417 | eepromPcdacs.pDataPerChannel = ee->ee_dataPerChannel11b; |
418 | break; |
419 | case CHANNEL_G: |
420 | case CHANNEL_108G: |
421 | eepromPcdacs.numChannels = ee->ee_numChannels2_4; |
422 | eepromPcdacs.pChannelList = ee->ee_channels11g; |
423 | eepromPcdacs.pDataPerChannel = ee->ee_dataPerChannel11g; |
424 | break; |
425 | default: |
426 | HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel flags 0x%x\n" , |
427 | __func__, chan->channelFlags); |
428 | return AH_FALSE; |
429 | } |
430 | |
431 | pSrcStruct = &eepromPcdacs; |
432 | |
433 | OS_MEMZERO(&pcdacStruct, sizeof(pcdacStruct)); |
434 | pPcdacValues = pcdacStruct.PcdacValues; |
435 | pScaledUpDbm = pcdacStruct.PwrValues; |
436 | |
437 | /* Initialize the pcdacs to dBM structs pcdacs to be 1 to 63 */ |
438 | for (i = PCDAC_START, j = 0; i <= PCDAC_STOP; i+= PCDAC_STEP, j++) |
439 | pPcdacValues[j] = i; |
440 | |
441 | pcdacStruct.numPcdacValues = j; |
442 | pcdacStruct.pcdacMin = PCDAC_START; |
443 | pcdacStruct.pcdacMax = PCDAC_STOP; |
444 | |
445 | /* Fill out the power values for this channel */ |
446 | for (j = 0; j < pcdacStruct.numPcdacValues; j++ ) |
447 | pScaledUpDbm[j] = ar5212GetScaledPower(chan->channel, |
448 | pPcdacValues[j], pSrcStruct); |
449 | |
450 | /* Now scale the pcdac values to fit in the 64 entry power table */ |
451 | minScaledPwr = pScaledUpDbm[0]; |
452 | maxScaledPwr = pScaledUpDbm[pcdacStruct.numPcdacValues - 1]; |
453 | |
454 | /* find minimum and make monotonic */ |
455 | for (j = 0; j < pcdacStruct.numPcdacValues; j++) { |
456 | if (minScaledPwr >= pScaledUpDbm[j]) { |
457 | minScaledPwr = pScaledUpDbm[j]; |
458 | pcdacMin = j; |
459 | } |
460 | /* |
461 | * Make the full_hsh monotonically increasing otherwise |
462 | * interpolation algorithm will get fooled gotta start |
463 | * working from the top, hence i = 63 - j. |
464 | */ |
465 | i = (uint16_t)(pcdacStruct.numPcdacValues - 1 - j); |
466 | if (i == 0) |
467 | break; |
468 | if (pScaledUpDbm[i-1] > pScaledUpDbm[i]) { |
469 | /* |
470 | * It could be a glitch, so make the power for |
471 | * this pcdac the same as the power from the |
472 | * next highest pcdac. |
473 | */ |
474 | pScaledUpDbm[i - 1] = pScaledUpDbm[i]; |
475 | } |
476 | } |
477 | |
478 | for (j = 0; j < pcdacStruct.numPcdacValues; j++) |
479 | if (maxScaledPwr < pScaledUpDbm[j]) { |
480 | maxScaledPwr = pScaledUpDbm[j]; |
481 | pcdacMax = j; |
482 | } |
483 | |
484 | /* Find the first power level with a pcdac */ |
485 | pwr = (uint16_t)(PWR_STEP * |
486 | ((minScaledPwr - PWR_MIN + PWR_STEP / 2) / PWR_STEP) + PWR_MIN); |
487 | |
488 | /* Write all the first pcdac entries based off the pcdacMin */ |
489 | pcdacTableIndex = 0; |
490 | for (i = 0; i < (2 * (pwr - PWR_MIN) / EEP_SCALE + 1); i++) { |
491 | HALASSERT(pcdacTableIndex < PWR_TABLE_SIZE); |
492 | ahp->ah_pcdacTable[pcdacTableIndex++] = pcdacMin; |
493 | } |
494 | |
495 | i = 0; |
496 | while (pwr < pScaledUpDbm[pcdacStruct.numPcdacValues - 1] && |
497 | pcdacTableIndex < PWR_TABLE_SIZE) { |
498 | pwr += PWR_STEP; |
499 | /* stop if dbM > max_power_possible */ |
500 | while (pwr < pScaledUpDbm[pcdacStruct.numPcdacValues - 1] && |
501 | (pwr - pScaledUpDbm[i])*(pwr - pScaledUpDbm[i+1]) > 0) |
502 | i++; |
503 | /* scale by 2 and add 1 to enable round up or down as needed */ |
504 | scaledPcdac = (uint16_t)(interpolate(pwr, |
505 | pScaledUpDbm[i], pScaledUpDbm[i + 1], |
506 | (uint16_t)(pPcdacValues[i] * 2), |
507 | (uint16_t)(pPcdacValues[i + 1] * 2)) + 1); |
508 | |
509 | HALASSERT(pcdacTableIndex < PWR_TABLE_SIZE); |
510 | ahp->ah_pcdacTable[pcdacTableIndex] = scaledPcdac / 2; |
511 | if (ahp->ah_pcdacTable[pcdacTableIndex] > pcdacMax) |
512 | ahp->ah_pcdacTable[pcdacTableIndex] = pcdacMax; |
513 | pcdacTableIndex++; |
514 | } |
515 | |
516 | /* Write all the last pcdac entries based off the last valid pcdac */ |
517 | while (pcdacTableIndex < PWR_TABLE_SIZE) { |
518 | ahp->ah_pcdacTable[pcdacTableIndex] = |
519 | ahp->ah_pcdacTable[pcdacTableIndex - 1]; |
520 | pcdacTableIndex++; |
521 | } |
522 | |
523 | /* No power table adjustment for 5111 */ |
524 | ahp->ah_txPowerIndexOffset = 0; |
525 | |
526 | return AH_TRUE; |
527 | } |
528 | |
529 | /* |
530 | * Get or interpolate the pcdac value from the calibrated data. |
531 | */ |
532 | static uint16_t |
533 | ar5212GetScaledPower(uint16_t channel, uint16_t pcdacValue, |
534 | const PCDACS_EEPROM *pSrcStruct) |
535 | { |
536 | uint16_t powerValue; |
537 | uint16_t lFreq, rFreq; /* left and right frequency values */ |
538 | uint16_t llPcdac, ulPcdac; /* lower and upper left pcdac values */ |
539 | uint16_t lrPcdac, urPcdac; /* lower and upper right pcdac values */ |
540 | uint16_t lPwr = 0, uPwr = 0; /* lower and upper temp pwr values */ |
541 | uint16_t lScaledPwr, rScaledPwr; /* left and right scaled power */ |
542 | |
543 | if (ar5212FindValueInList(channel, pcdacValue, pSrcStruct, &powerValue)) { |
544 | /* value was copied from srcStruct */ |
545 | return powerValue; |
546 | } |
547 | |
548 | ar5212GetLowerUpperValues(channel, |
549 | pSrcStruct->pChannelList, pSrcStruct->numChannels, |
550 | &lFreq, &rFreq); |
551 | ar5212GetLowerUpperPcdacs(pcdacValue, |
552 | lFreq, pSrcStruct, &llPcdac, &ulPcdac); |
553 | ar5212GetLowerUpperPcdacs(pcdacValue, |
554 | rFreq, pSrcStruct, &lrPcdac, &urPcdac); |
555 | |
556 | /* get the power index for the pcdac value */ |
557 | ar5212FindValueInList(lFreq, llPcdac, pSrcStruct, &lPwr); |
558 | ar5212FindValueInList(lFreq, ulPcdac, pSrcStruct, &uPwr); |
559 | lScaledPwr = interpolate(pcdacValue, llPcdac, ulPcdac, lPwr, uPwr); |
560 | |
561 | ar5212FindValueInList(rFreq, lrPcdac, pSrcStruct, &lPwr); |
562 | ar5212FindValueInList(rFreq, urPcdac, pSrcStruct, &uPwr); |
563 | rScaledPwr = interpolate(pcdacValue, lrPcdac, urPcdac, lPwr, uPwr); |
564 | |
565 | return interpolate(channel, lFreq, rFreq, lScaledPwr, rScaledPwr); |
566 | } |
567 | |
568 | /* |
569 | * Find the value from the calibrated source data struct |
570 | */ |
571 | static HAL_BOOL |
572 | ar5212FindValueInList(uint16_t channel, uint16_t pcdacValue, |
573 | const PCDACS_EEPROM *pSrcStruct, uint16_t *powerValue) |
574 | { |
575 | const DATA_PER_CHANNEL *pChannelData = pSrcStruct->pDataPerChannel; |
576 | int i; |
577 | |
578 | for (i = 0; i < pSrcStruct->numChannels; i++ ) { |
579 | if (pChannelData->channelValue == channel) { |
580 | const uint16_t* pPcdac = pChannelData->PcdacValues; |
581 | int j; |
582 | |
583 | for (j = 0; j < pChannelData->numPcdacValues; j++ ) { |
584 | if (*pPcdac == pcdacValue) { |
585 | *powerValue = pChannelData->PwrValues[j]; |
586 | return AH_TRUE; |
587 | } |
588 | pPcdac++; |
589 | } |
590 | } |
591 | pChannelData++; |
592 | } |
593 | return AH_FALSE; |
594 | } |
595 | |
596 | /* |
597 | * Get the upper and lower pcdac given the channel and the pcdac |
598 | * used in the search |
599 | */ |
600 | static void |
601 | ar5212GetLowerUpperPcdacs(uint16_t pcdac, uint16_t channel, |
602 | const PCDACS_EEPROM *pSrcStruct, |
603 | uint16_t *pLowerPcdac, uint16_t *pUpperPcdac) |
604 | { |
605 | const DATA_PER_CHANNEL *pChannelData = pSrcStruct->pDataPerChannel; |
606 | int i; |
607 | |
608 | /* Find the channel information */ |
609 | for (i = 0; i < pSrcStruct->numChannels; i++) { |
610 | if (pChannelData->channelValue == channel) |
611 | break; |
612 | pChannelData++; |
613 | } |
614 | ar5212GetLowerUpperValues(pcdac, pChannelData->PcdacValues, |
615 | pChannelData->numPcdacValues, |
616 | pLowerPcdac, pUpperPcdac); |
617 | } |
618 | |
619 | static HAL_BOOL |
620 | ar5111GetChannelMaxMinPower(struct ath_hal *ah, HAL_CHANNEL *chan, |
621 | int16_t *maxPow, int16_t *minPow) |
622 | { |
623 | /* XXX - Get 5111 power limits! */ |
624 | /* NB: caller will cope */ |
625 | return AH_FALSE; |
626 | } |
627 | |
628 | /* |
629 | * Adjust NF based on statistical values for 5GHz frequencies. |
630 | */ |
631 | static int16_t |
632 | ar5111GetNfAdjust(struct ath_hal *ah, const HAL_CHANNEL_INTERNAL *c) |
633 | { |
634 | static const struct { |
635 | uint16_t freqLow; |
636 | int16_t adjust; |
637 | } adjust5111[] = { |
638 | { 5790, 6 }, /* NB: ordered high -> low */ |
639 | { 5730, 4 }, |
640 | { 5690, 3 }, |
641 | { 5660, 2 }, |
642 | { 5610, 1 }, |
643 | { 5530, 0 }, |
644 | { 5450, 0 }, |
645 | { 5379, 1 }, |
646 | { 5209, 3 }, |
647 | { 3000, 5 }, |
648 | { 0, 0 }, |
649 | }; |
650 | int i; |
651 | |
652 | for (i = 0; c->channel <= adjust5111[i].freqLow; i++) |
653 | ; |
654 | return adjust5111[i].adjust; |
655 | } |
656 | |
657 | /* |
658 | * Free memory for analog bank scratch buffers |
659 | */ |
660 | static void |
661 | ar5111RfDetach(struct ath_hal *ah) |
662 | { |
663 | struct ath_hal_5212 *ahp = AH5212(ah); |
664 | |
665 | HALASSERT(ahp->ah_rfHal != AH_NULL); |
666 | ath_hal_free(ahp->ah_rfHal); |
667 | ahp->ah_rfHal = AH_NULL; |
668 | } |
669 | |
670 | /* |
671 | * Allocate memory for analog bank scratch buffers |
672 | * Scratch Buffer will be reinitialized every reset so no need to zero now |
673 | */ |
674 | static HAL_BOOL |
675 | ar5111RfAttach(struct ath_hal *ah, HAL_STATUS *status) |
676 | { |
677 | struct ath_hal_5212 *ahp = AH5212(ah); |
678 | struct ar5111State *priv; |
679 | |
680 | HALASSERT(ah->ah_magic == AR5212_MAGIC); |
681 | |
682 | HALASSERT(ahp->ah_rfHal == AH_NULL); |
683 | priv = ath_hal_malloc(sizeof(struct ar5111State)); |
684 | if (priv == AH_NULL) { |
685 | HALDEBUG(ah, HAL_DEBUG_ANY, |
686 | "%s: cannot allocate private state\n" , __func__); |
687 | *status = HAL_ENOMEM; /* XXX */ |
688 | return AH_FALSE; |
689 | } |
690 | priv->base.rfDetach = ar5111RfDetach; |
691 | priv->base.writeRegs = ar5111WriteRegs; |
692 | priv->base.getRfBank = ar5111GetRfBank; |
693 | priv->base.setChannel = ar5111SetChannel; |
694 | priv->base.setRfRegs = ar5111SetRfRegs; |
695 | priv->base.setPowerTable = ar5111SetPowerTable; |
696 | priv->base.getChannelMaxMinPower = ar5111GetChannelMaxMinPower; |
697 | priv->base.getNfAdjust = ar5111GetNfAdjust; |
698 | |
699 | ahp->ah_pcdacTable = priv->pcdacTable; |
700 | ahp->ah_pcdacTableSize = sizeof(priv->pcdacTable); |
701 | ahp->ah_rfHal = &priv->base; |
702 | |
703 | return AH_TRUE; |
704 | } |
705 | |
706 | static HAL_BOOL |
707 | ar5111Probe(struct ath_hal *ah) |
708 | { |
709 | return IS_RAD5111(ah); |
710 | } |
711 | AH_RF(RF5111, ar5111Probe, ar5111RfAttach); |
712 | |