2 * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
3 * Copyright (c) 2002-2008 Atheros Communications, Inc.
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.
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.
17 * $FreeBSD: head/sys/dev/ath/ath_hal/ar5416/ar9285_reset.c 203930 2010-02-15 17:49:49Z rpaulo $
21 * This is almost the same as ar5416_reset.c but uses the v4k EEPROM and
22 * supports only 2Ghz operation.
28 #include "ah_internal.h"
31 #include "ah_eeprom_v14.h"
32 #include "ah_eeprom_v4k.h"
34 #include "ar5416/ar9285.h"
35 #include "ar5416/ar5416.h"
36 #include "ar5416/ar5416reg.h"
37 #include "ar5416/ar5416phy.h"
39 /* Eeprom versioning macros. Returns true if the version is equal or newer than the ver specified */
40 #define EEP_MINOR(_ah) \
41 (AH_PRIVATE(_ah)->ah_eeversion & AR5416_EEP_VER_MINOR_MASK)
42 #define IS_EEP_MINOR_V2(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_2)
43 #define IS_EEP_MINOR_V3(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_3)
45 /* Additional Time delay to wait after activiting the Base band */
46 #define BASE_ACTIVATE_DELAY 100 /* 100 usec */
47 #define PLL_SETTLE_DELAY 300 /* 300 usec */
48 #define RTC_PLL_SETTLE_DELAY 1000 /* 1 ms */
50 static HAL_BOOL
ar9285SetPowerPerRateTable(struct ath_hal
*ah
,
51 struct ar5416eeprom_4k
*pEepData
,
52 const struct ieee80211_channel
*chan
, int16_t *ratesArray
,
53 uint16_t cfgCtl
, uint16_t AntennaReduction
,
54 uint16_t twiceMaxRegulatoryPower
,
56 static HAL_BOOL
ar9285SetPowerCalTable(struct ath_hal
*ah
,
57 struct ar5416eeprom_4k
*pEepData
,
58 const struct ieee80211_channel
*chan
,
59 int16_t *pTxPowerIndexOffset
);
60 static int16_t interpolate(uint16_t target
, uint16_t srcLeft
,
61 uint16_t srcRight
, int16_t targetLeft
, int16_t targetRight
);
62 static HAL_BOOL
ar9285FillVpdTable(uint8_t, uint8_t, uint8_t *, uint8_t *,
64 static void ar9285GetGainBoundariesAndPdadcs(struct ath_hal
*ah
,
65 const struct ieee80211_channel
*chan
, CAL_DATA_PER_FREQ_4K
*pRawDataSet
,
66 uint8_t * bChans
, uint16_t availPiers
,
67 uint16_t tPdGainOverlap
, int16_t *pMinCalPower
,
68 uint16_t * pPdGainBoundaries
, uint8_t * pPDADCValues
,
69 uint16_t numXpdGains
);
70 static HAL_BOOL
getLowerUpperIndex(uint8_t target
, uint8_t *pList
,
71 uint16_t listSize
, uint16_t *indexL
, uint16_t *indexR
);
72 static uint16_t ar9285GetMaxEdgePower(uint16_t, CAL_CTL_EDGES
*);
74 /* XXX gag, this is sick */
75 typedef enum Ar5416_Rates
{
76 rate6mb
, rate9mb
, rate12mb
, rate18mb
,
77 rate24mb
, rate36mb
, rate48mb
, rate54mb
,
78 rate1l
, rate2l
, rate2s
, rate5_5l
,
79 rate5_5s
, rate11l
, rate11s
, rateXr
,
80 rateHt20_0
, rateHt20_1
, rateHt20_2
, rateHt20_3
,
81 rateHt20_4
, rateHt20_5
, rateHt20_6
, rateHt20_7
,
82 rateHt40_0
, rateHt40_1
, rateHt40_2
, rateHt40_3
,
83 rateHt40_4
, rateHt40_5
, rateHt40_6
, rateHt40_7
,
84 rateDupCck
, rateDupOfdm
, rateExtCck
, rateExtOfdm
,
89 ar9285SetTransmitPower(struct ath_hal
*ah
,
90 const struct ieee80211_channel
*chan
, uint16_t *rfXpdGain
)
92 #define POW_SM(_r, _s) (((_r) & 0x3f) << (_s))
94 MODAL_EEP4K_HEADER
*pModal
;
95 struct ath_hal_5212
*ahp
= AH5212(ah
);
96 int16_t ratesArray
[Ar5416RateSize
];
97 int16_t txPowerIndexOffset
= 0;
98 uint8_t ht40PowerIncForPdadc
= 2;
103 uint16_t twiceAntennaReduction
;
104 uint16_t twiceMaxRegulatoryPower
;
106 HAL_EEPROM_v4k
*ee
= AH_PRIVATE(ah
)->ah_eeprom
;
107 struct ar5416eeprom_4k
*pEepData
= &ee
->ee_base
;
109 HALASSERT(AH_PRIVATE(ah
)->ah_eeversion
>= AR_EEPROM_VER14_1
);
111 /* Setup info for the actual eeprom */
112 OS_MEMZERO(ratesArray
, sizeof(ratesArray
));
113 cfgCtl
= ath_hal_getctl(ah
, chan
);
114 powerLimit
= chan
->ic_maxregpower
* 2;
115 twiceAntennaReduction
= chan
->ic_maxantgain
;
116 twiceMaxRegulatoryPower
= AH_MIN(MAX_RATE_POWER
, AH_PRIVATE(ah
)->ah_powerLimit
);
117 pModal
= &pEepData
->modalHeader
;
118 HALDEBUG(ah
, HAL_DEBUG_RESET
, "%s Channel=%u CfgCtl=%u\n",
119 __func__
,chan
->ic_freq
, cfgCtl
);
121 if (IS_EEP_MINOR_V2(ah
)) {
122 ht40PowerIncForPdadc
= pModal
->ht40PowerIncForPdadc
;
125 if (!ar9285SetPowerPerRateTable(ah
, pEepData
, chan
,
126 &ratesArray
[0],cfgCtl
,
127 twiceAntennaReduction
,
128 twiceMaxRegulatoryPower
, powerLimit
)) {
129 HALDEBUG(ah
, HAL_DEBUG_ANY
,
130 "%s: unable to set tx power per rate table\n", __func__
);
134 if (!ar9285SetPowerCalTable(ah
, pEepData
, chan
, &txPowerIndexOffset
)) {
135 HALDEBUG(ah
, HAL_DEBUG_ANY
, "%s: unable to set power table\n",
140 maxPower
= AH_MAX(ratesArray
[rate6mb
], ratesArray
[rateHt20_0
]);
141 maxPower
= AH_MAX(maxPower
, ratesArray
[rate1l
]);
143 if (IEEE80211_IS_CHAN_HT40(chan
)) {
144 maxPower
= AH_MAX(maxPower
, ratesArray
[rateHt40_0
]);
147 ahp
->ah_tx6PowerInHalfDbm
= maxPower
;
148 AH_PRIVATE(ah
)->ah_maxPowerLevel
= maxPower
;
149 ahp
->ah_txPowerIndexOffset
= txPowerIndexOffset
;
152 * txPowerIndexOffset is set by the SetPowerTable() call -
153 * adjust the rate table (0 offset if rates EEPROM not loaded)
155 for (i
= 0; i
< NELEM(ratesArray
); i
++) {
156 ratesArray
[i
] = (int16_t)(txPowerIndexOffset
+ ratesArray
[i
]);
157 if (ratesArray
[i
] > AR5416_MAX_RATE_POWER
)
158 ratesArray
[i
] = AR5416_MAX_RATE_POWER
;
159 ratesArray
[i
] -= AR5416_PWR_TABLE_OFFSET_DB
* 2;
162 #ifdef AH_EEPROM_DUMP
163 ar5416PrintPowerPerRate(ah
, ratesArray
);
166 /* Write the OFDM power per rate set */
167 OS_REG_WRITE(ah
, AR_PHY_POWER_TX_RATE1
,
168 POW_SM(ratesArray
[rate18mb
], 24)
169 | POW_SM(ratesArray
[rate12mb
], 16)
170 | POW_SM(ratesArray
[rate9mb
], 8)
171 | POW_SM(ratesArray
[rate6mb
], 0)
173 OS_REG_WRITE(ah
, AR_PHY_POWER_TX_RATE2
,
174 POW_SM(ratesArray
[rate54mb
], 24)
175 | POW_SM(ratesArray
[rate48mb
], 16)
176 | POW_SM(ratesArray
[rate36mb
], 8)
177 | POW_SM(ratesArray
[rate24mb
], 0)
180 /* Write the CCK power per rate set */
181 OS_REG_WRITE(ah
, AR_PHY_POWER_TX_RATE3
,
182 POW_SM(ratesArray
[rate2s
], 24)
183 | POW_SM(ratesArray
[rate2l
], 16)
184 | POW_SM(ratesArray
[rateXr
], 8) /* XR target power */
185 | POW_SM(ratesArray
[rate1l
], 0)
187 OS_REG_WRITE(ah
, AR_PHY_POWER_TX_RATE4
,
188 POW_SM(ratesArray
[rate11s
], 24)
189 | POW_SM(ratesArray
[rate11l
], 16)
190 | POW_SM(ratesArray
[rate5_5s
], 8)
191 | POW_SM(ratesArray
[rate5_5l
], 0)
193 HALDEBUG(ah
, HAL_DEBUG_RESET
,
194 "%s AR_PHY_POWER_TX_RATE3=0x%x AR_PHY_POWER_TX_RATE4=0x%x\n",
195 __func__
, OS_REG_READ(ah
,AR_PHY_POWER_TX_RATE3
),
196 OS_REG_READ(ah
,AR_PHY_POWER_TX_RATE4
));
198 /* Write the HT20 power per rate set */
199 OS_REG_WRITE(ah
, AR_PHY_POWER_TX_RATE5
,
200 POW_SM(ratesArray
[rateHt20_3
], 24)
201 | POW_SM(ratesArray
[rateHt20_2
], 16)
202 | POW_SM(ratesArray
[rateHt20_1
], 8)
203 | POW_SM(ratesArray
[rateHt20_0
], 0)
205 OS_REG_WRITE(ah
, AR_PHY_POWER_TX_RATE6
,
206 POW_SM(ratesArray
[rateHt20_7
], 24)
207 | POW_SM(ratesArray
[rateHt20_6
], 16)
208 | POW_SM(ratesArray
[rateHt20_5
], 8)
209 | POW_SM(ratesArray
[rateHt20_4
], 0)
212 if (IEEE80211_IS_CHAN_HT40(chan
)) {
213 /* Write the HT40 power per rate set */
214 /* Correct PAR difference between HT40 and HT20/LEGACY */
215 OS_REG_WRITE(ah
, AR_PHY_POWER_TX_RATE7
,
216 POW_SM(ratesArray
[rateHt40_3
] + ht40PowerIncForPdadc
, 24)
217 | POW_SM(ratesArray
[rateHt40_2
] + ht40PowerIncForPdadc
, 16)
218 | POW_SM(ratesArray
[rateHt40_1
] + ht40PowerIncForPdadc
, 8)
219 | POW_SM(ratesArray
[rateHt40_0
] + ht40PowerIncForPdadc
, 0)
221 OS_REG_WRITE(ah
, AR_PHY_POWER_TX_RATE8
,
222 POW_SM(ratesArray
[rateHt40_7
] + ht40PowerIncForPdadc
, 24)
223 | POW_SM(ratesArray
[rateHt40_6
] + ht40PowerIncForPdadc
, 16)
224 | POW_SM(ratesArray
[rateHt40_5
] + ht40PowerIncForPdadc
, 8)
225 | POW_SM(ratesArray
[rateHt40_4
] + ht40PowerIncForPdadc
, 0)
227 /* Write the Dup/Ext 40 power per rate set */
228 OS_REG_WRITE(ah
, AR_PHY_POWER_TX_RATE9
,
229 POW_SM(ratesArray
[rateExtOfdm
], 24)
230 | POW_SM(ratesArray
[rateExtCck
], 16)
231 | POW_SM(ratesArray
[rateDupOfdm
], 8)
232 | POW_SM(ratesArray
[rateDupCck
], 0)
241 ar9285SetBoardValues(struct ath_hal
*ah
, const struct ieee80211_channel
*chan
)
243 const HAL_EEPROM_v4k
*ee
= AH_PRIVATE(ah
)->ah_eeprom
;
244 const struct ar5416eeprom_4k
*eep
= &ee
->ee_base
;
245 const MODAL_EEP4K_HEADER
*pModal
;
246 uint8_t txRxAttenLocal
= 23;
248 HALASSERT(AH_PRIVATE(ah
)->ah_eeversion
>= AR_EEPROM_VER14_1
);
249 pModal
= &eep
->modalHeader
;
251 OS_REG_WRITE(ah
, AR_PHY_SWITCH_COM
, pModal
->antCtrlCommon
);
252 OS_REG_WRITE(ah
, AR_PHY_SWITCH_CHAIN_0
, pModal
->antCtrlChain
[0]);
253 OS_REG_WRITE(ah
, AR_PHY_TIMING_CTRL4
,
254 (OS_REG_READ(ah
, AR_PHY_TIMING_CTRL4
) &
255 ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF
| AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF
)) |
256 SM(pModal
->iqCalICh
[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF
) |
257 SM(pModal
->iqCalQCh
[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF
));
259 if (IS_EEP_MINOR_V3(ah
)) {
260 if (IEEE80211_IS_CHAN_HT40(chan
)) {
261 /* Overwrite switch settling with HT40 value */
262 OS_REG_RMW_FIELD(ah
, AR_PHY_SETTLING
, AR_PHY_SETTLING_SWITCH
,
263 pModal
->swSettleHt40
);
265 txRxAttenLocal
= pModal
->txRxAttenCh
[0];
267 OS_REG_RMW_FIELD(ah
, AR_PHY_GAIN_2GHZ
, AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN
,
268 pModal
->bswMargin
[0]);
269 OS_REG_RMW_FIELD(ah
, AR_PHY_GAIN_2GHZ
, AR_PHY_GAIN_2GHZ_XATTEN1_DB
,
270 pModal
->bswAtten
[0]);
271 OS_REG_RMW_FIELD(ah
, AR_PHY_GAIN_2GHZ
, AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN
,
272 pModal
->xatten2Margin
[0]);
273 OS_REG_RMW_FIELD(ah
, AR_PHY_GAIN_2GHZ
, AR_PHY_GAIN_2GHZ_XATTEN2_DB
,
274 pModal
->xatten2Db
[0]);
276 /* block 1 has the same values as block 0 */
277 OS_REG_RMW_FIELD(ah
, AR_PHY_GAIN_2GHZ
+ 0x1000,
278 AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN
, pModal
->bswMargin
[0]);
279 OS_REG_RMW_FIELD(ah
, AR_PHY_GAIN_2GHZ
+ 0x1000,
280 AR_PHY_GAIN_2GHZ_XATTEN1_DB
, pModal
->bswAtten
[0]);
281 OS_REG_RMW_FIELD(ah
, AR_PHY_GAIN_2GHZ
+ 0x1000,
282 AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN
, pModal
->xatten2Margin
[0]);
283 OS_REG_RMW_FIELD(ah
, AR_PHY_GAIN_2GHZ
+ 0x1000,
284 AR_PHY_GAIN_2GHZ_XATTEN2_DB
, pModal
->xatten2Db
[0]);
287 OS_REG_RMW_FIELD(ah
, AR_PHY_RXGAIN
,
288 AR9280_PHY_RXGAIN_TXRX_ATTEN
, txRxAttenLocal
);
289 OS_REG_RMW_FIELD(ah
, AR_PHY_RXGAIN
,
290 AR9280_PHY_RXGAIN_TXRX_MARGIN
, pModal
->rxTxMarginCh
[0]);
292 OS_REG_RMW_FIELD(ah
, AR_PHY_RXGAIN
+ 0x1000,
293 AR9280_PHY_RXGAIN_TXRX_ATTEN
, txRxAttenLocal
);
294 OS_REG_RMW_FIELD(ah
, AR_PHY_RXGAIN
+ 0x1000,
295 AR9280_PHY_RXGAIN_TXRX_MARGIN
, pModal
->rxTxMarginCh
[0]);
297 if (AR_SREV_KITE_11(ah
))
298 OS_REG_WRITE(ah
, AR9285_AN_TOP4
, (AR9285_AN_TOP4_DEFAULT
| 0x14));
304 * Helper functions common for AP/CB/XB
308 ar9285SetPowerPerRateTable(struct ath_hal
*ah
, struct ar5416eeprom_4k
*pEepData
,
309 const struct ieee80211_channel
*chan
,
310 int16_t *ratesArray
, uint16_t cfgCtl
,
311 uint16_t AntennaReduction
,
312 uint16_t twiceMaxRegulatoryPower
,
315 /* Local defines to distinguish between extension and control CTL's */
316 #define EXT_ADDITIVE (0x8000)
317 #define CTL_11G_EXT (CTL_11G | EXT_ADDITIVE)
318 #define CTL_11B_EXT (CTL_11B | EXT_ADDITIVE)
320 uint16_t twiceMaxEdgePower
= AR5416_MAX_RATE_POWER
;
322 int16_t twiceLargestAntenna
;
323 CAL_CTL_DATA_4K
*rep
;
324 CAL_TARGET_POWER_LEG targetPowerOfdm
, targetPowerCck
= {0, {0, 0, 0, 0}};
325 CAL_TARGET_POWER_LEG targetPowerOfdmExt
= {0, {0, 0, 0, 0}}, targetPowerCckExt
= {0, {0, 0, 0, 0}};
326 CAL_TARGET_POWER_HT targetPowerHt20
, targetPowerHt40
= {0, {0, 0, 0, 0}};
327 int16_t scaledPower
, minCtlPower
;
329 #define SUB_NUM_CTL_MODES_AT_2G_40 3 /* excluding HT40, EXT-OFDM, EXT-CCK */
330 static const uint16_t ctlModesFor11g
[] = {
331 CTL_11B
, CTL_11G
, CTL_2GHT20
, CTL_11B_EXT
, CTL_11G_EXT
, CTL_2GHT40
333 const uint16_t *pCtlMode
;
334 uint16_t numCtlModes
, ctlMode
, freq
;
335 CHAN_CENTERS centers
;
337 ar5416GetChannelCenters(ah
, chan
, ¢ers
);
339 /* Compute TxPower reduction due to Antenna Gain */
341 twiceLargestAntenna
= pEepData
->modalHeader
.antennaGainCh
[0];
342 twiceLargestAntenna
= (int16_t)AH_MIN((AntennaReduction
) - twiceLargestAntenna
, 0);
344 /* XXX setup for 5212 use (really used?) */
345 ath_hal_eepromSet(ah
, AR_EEP_ANTGAINMAX_2
, twiceLargestAntenna
);
348 * scaledPower is the minimum of the user input power level and
349 * the regulatory allowed power level
351 scaledPower
= AH_MIN(powerLimit
, twiceMaxRegulatoryPower
+ twiceLargestAntenna
);
353 /* Get target powers from EEPROM - our baseline for TX Power */
354 /* Setup for CTL modes */
355 numCtlModes
= NELEM(ctlModesFor11g
) - SUB_NUM_CTL_MODES_AT_2G_40
; /* CTL_11B, CTL_11G, CTL_2GHT20 */
356 pCtlMode
= ctlModesFor11g
;
358 ar5416GetTargetPowersLeg(ah
, chan
, pEepData
->calTargetPowerCck
,
359 AR5416_4K_NUM_2G_CCK_TARGET_POWERS
, &targetPowerCck
, 4, AH_FALSE
);
360 ar5416GetTargetPowersLeg(ah
, chan
, pEepData
->calTargetPower2G
,
361 AR5416_4K_NUM_2G_20_TARGET_POWERS
, &targetPowerOfdm
, 4, AH_FALSE
);
362 ar5416GetTargetPowers(ah
, chan
, pEepData
->calTargetPower2GHT20
,
363 AR5416_4K_NUM_2G_20_TARGET_POWERS
, &targetPowerHt20
, 8, AH_FALSE
);
365 if (IEEE80211_IS_CHAN_HT40(chan
)) {
366 numCtlModes
= NELEM(ctlModesFor11g
); /* All 2G CTL's */
368 ar5416GetTargetPowers(ah
, chan
, pEepData
->calTargetPower2GHT40
,
369 AR5416_4K_NUM_2G_40_TARGET_POWERS
, &targetPowerHt40
, 8, AH_TRUE
);
370 /* Get target powers for extension channels */
371 ar5416GetTargetPowersLeg(ah
, chan
, pEepData
->calTargetPowerCck
,
372 AR5416_4K_NUM_2G_CCK_TARGET_POWERS
, &targetPowerCckExt
, 4, AH_TRUE
);
373 ar5416GetTargetPowersLeg(ah
, chan
, pEepData
->calTargetPower2G
,
374 AR5416_4K_NUM_2G_20_TARGET_POWERS
, &targetPowerOfdmExt
, 4, AH_TRUE
);
378 * For MIMO, need to apply regulatory caps individually across dynamically
379 * running modes: CCK, OFDM, HT20, HT40
381 * The outer loop walks through each possible applicable runtime mode.
382 * The inner loop walks through each ctlIndex entry in EEPROM.
383 * The ctl value is encoded as [7:4] == test group, [3:0] == test mode.
386 for (ctlMode
= 0; ctlMode
< numCtlModes
; ctlMode
++) {
387 HAL_BOOL isHt40CtlMode
= (pCtlMode
[ctlMode
] == CTL_5GHT40
) ||
388 (pCtlMode
[ctlMode
] == CTL_2GHT40
);
390 freq
= centers
.ctl_center
;
391 } else if (pCtlMode
[ctlMode
] & EXT_ADDITIVE
) {
392 freq
= centers
.ext_center
;
394 freq
= centers
.ctl_center
;
397 /* walk through each CTL index stored in EEPROM */
398 for (i
= 0; (i
< AR5416_4K_NUM_CTLS
) && pEepData
->ctlIndex
[i
]; i
++) {
399 uint16_t twiceMinEdgePower
;
401 /* compare test group from regulatory channel list with test mode from pCtlMode list */
402 if ((((cfgCtl
& ~CTL_MODE_M
) | (pCtlMode
[ctlMode
] & CTL_MODE_M
)) == pEepData
->ctlIndex
[i
]) ||
403 (((cfgCtl
& ~CTL_MODE_M
) | (pCtlMode
[ctlMode
] & CTL_MODE_M
)) ==
404 ((pEepData
->ctlIndex
[i
] & CTL_MODE_M
) | SD_NO_CTL
))) {
405 rep
= &(pEepData
->ctlData
[i
]);
406 twiceMinEdgePower
= ar9285GetMaxEdgePower(freq
,
408 owl_get_ntxchains(AH5416(ah
)->ah_tx_chainmask
) - 1]);
409 if ((cfgCtl
& ~CTL_MODE_M
) == SD_NO_CTL
) {
410 /* Find the minimum of all CTL edge powers that apply to this channel */
411 twiceMaxEdgePower
= AH_MIN(twiceMaxEdgePower
, twiceMinEdgePower
);
414 twiceMaxEdgePower
= twiceMinEdgePower
;
419 minCtlPower
= (uint8_t)AH_MIN(twiceMaxEdgePower
, scaledPower
);
420 /* Apply ctl mode to correct target power set */
421 switch(pCtlMode
[ctlMode
]) {
423 for (i
= 0; i
< NELEM(targetPowerCck
.tPow2x
); i
++) {
424 targetPowerCck
.tPow2x
[i
] = (uint8_t)AH_MIN(targetPowerCck
.tPow2x
[i
], minCtlPower
);
429 for (i
= 0; i
< NELEM(targetPowerOfdm
.tPow2x
); i
++) {
430 targetPowerOfdm
.tPow2x
[i
] = (uint8_t)AH_MIN(targetPowerOfdm
.tPow2x
[i
], minCtlPower
);
435 for (i
= 0; i
< NELEM(targetPowerHt20
.tPow2x
); i
++) {
436 targetPowerHt20
.tPow2x
[i
] = (uint8_t)AH_MIN(targetPowerHt20
.tPow2x
[i
], minCtlPower
);
440 targetPowerCckExt
.tPow2x
[0] = (uint8_t)AH_MIN(targetPowerCckExt
.tPow2x
[0], minCtlPower
);
443 targetPowerOfdmExt
.tPow2x
[0] = (uint8_t)AH_MIN(targetPowerOfdmExt
.tPow2x
[0], minCtlPower
);
447 for (i
= 0; i
< NELEM(targetPowerHt40
.tPow2x
); i
++) {
448 targetPowerHt40
.tPow2x
[i
] = (uint8_t)AH_MIN(targetPowerHt40
.tPow2x
[i
], minCtlPower
);
455 } /* end ctl mode checking */
457 /* Set rates Array from collected data */
458 ratesArray
[rate6mb
] = ratesArray
[rate9mb
] = ratesArray
[rate12mb
] = ratesArray
[rate18mb
] = ratesArray
[rate24mb
] = targetPowerOfdm
.tPow2x
[0];
459 ratesArray
[rate36mb
] = targetPowerOfdm
.tPow2x
[1];
460 ratesArray
[rate48mb
] = targetPowerOfdm
.tPow2x
[2];
461 ratesArray
[rate54mb
] = targetPowerOfdm
.tPow2x
[3];
462 ratesArray
[rateXr
] = targetPowerOfdm
.tPow2x
[0];
464 for (i
= 0; i
< NELEM(targetPowerHt20
.tPow2x
); i
++) {
465 ratesArray
[rateHt20_0
+ i
] = targetPowerHt20
.tPow2x
[i
];
468 ratesArray
[rate1l
] = targetPowerCck
.tPow2x
[0];
469 ratesArray
[rate2s
] = ratesArray
[rate2l
] = targetPowerCck
.tPow2x
[1];
470 ratesArray
[rate5_5s
] = ratesArray
[rate5_5l
] = targetPowerCck
.tPow2x
[2];
471 ratesArray
[rate11s
] = ratesArray
[rate11l
] = targetPowerCck
.tPow2x
[3];
472 if (IEEE80211_IS_CHAN_HT40(chan
)) {
473 for (i
= 0; i
< NELEM(targetPowerHt40
.tPow2x
); i
++) {
474 ratesArray
[rateHt40_0
+ i
] = targetPowerHt40
.tPow2x
[i
];
476 ratesArray
[rateDupOfdm
] = targetPowerHt40
.tPow2x
[0];
477 ratesArray
[rateDupCck
] = targetPowerHt40
.tPow2x
[0];
478 ratesArray
[rateExtOfdm
] = targetPowerOfdmExt
.tPow2x
[0];
479 if (IEEE80211_IS_CHAN_2GHZ(chan
)) {
480 ratesArray
[rateExtCck
] = targetPowerCckExt
.tPow2x
[0];
487 #undef SUB_NUM_CTL_MODES_AT_2G_40
490 /**************************************************************************
493 * Get channel value from binary representation held in eeprom
494 * RETURNS: the frequency in MHz
497 fbin2freq(uint8_t fbin
)
500 * Reserved value 0xFF provides an empty definition both as
501 * an fbin and as a frequency - do not convert
503 if (fbin
== AR5416_BCHAN_UNUSED
) {
507 return (uint16_t)(2300 + fbin
);
511 * XXX almost the same as ar5416GetMaxEdgePower.
514 ar9285GetMaxEdgePower(uint16_t freq
, CAL_CTL_EDGES
*pRdEdgesPower
)
516 uint16_t twiceMaxEdgePower
= AR5416_MAX_RATE_POWER
;
519 /* Get the edge power */
520 for (i
= 0; (i
< AR5416_NUM_BAND_EDGES
) && (pRdEdgesPower
[i
].bChannel
!= AR5416_BCHAN_UNUSED
) ; i
++) {
522 * If there's an exact channel match or an inband flag set
523 * on the lower channel use the given rdEdgePower
525 if (freq
== fbin2freq(pRdEdgesPower
[i
].bChannel
)) {
526 twiceMaxEdgePower
= MS(pRdEdgesPower
[i
].tPowerFlag
, CAL_CTL_EDGES_POWER
);
528 } else if ((i
> 0) && (freq
< fbin2freq(pRdEdgesPower
[i
].bChannel
))) {
529 if (fbin2freq(pRdEdgesPower
[i
- 1].bChannel
) < freq
&& (pRdEdgesPower
[i
- 1].tPowerFlag
& CAL_CTL_EDGES_FLAG
) != 0) {
530 twiceMaxEdgePower
= MS(pRdEdgesPower
[i
- 1].tPowerFlag
, CAL_CTL_EDGES_POWER
);
532 /* Leave loop - no more affecting edges possible in this monotonic increasing list */
536 HALASSERT(twiceMaxEdgePower
> 0);
537 return twiceMaxEdgePower
;
543 ar9285SetPowerCalTable(struct ath_hal
*ah
, struct ar5416eeprom_4k
*pEepData
,
544 const struct ieee80211_channel
*chan
, int16_t *pTxPowerIndexOffset
)
546 CAL_DATA_PER_FREQ_4K
*pRawDataset
;
547 uint8_t *pCalBChans
= AH_NULL
;
548 uint16_t pdGainOverlap_t2
;
549 static uint8_t pdadcValues
[AR5416_NUM_PDADC_VALUES
];
550 uint16_t gainBoundaries
[AR5416_PD_GAINS_IN_MASK
];
551 uint16_t numPiers
, i
, j
;
552 int16_t tMinCalPower
;
553 uint16_t numXpdGain
, xpdMask
;
554 uint16_t xpdGainValues
[AR5416_4K_NUM_PD_GAINS
];
555 uint32_t reg32
, regOffset
, regChainOffset
;
557 OS_MEMZERO(xpdGainValues
, sizeof(xpdGainValues
));
559 xpdMask
= pEepData
->modalHeader
.xpdGain
;
561 if (IS_EEP_MINOR_V2(ah
)) {
562 pdGainOverlap_t2
= pEepData
->modalHeader
.pdGainOverlap
;
564 pdGainOverlap_t2
= (uint16_t)(MS(OS_REG_READ(ah
, AR_PHY_TPCRG5
), AR_PHY_TPCRG5_PD_GAIN_OVERLAP
));
567 pCalBChans
= pEepData
->calFreqPier2G
;
568 numPiers
= AR5416_4K_NUM_2G_CAL_PIERS
;
570 /* Calculate the value of xpdgains from the xpdGain Mask */
571 for (i
= 1; i
<= AR5416_PD_GAINS_IN_MASK
; i
++) {
572 if ((xpdMask
>> (AR5416_PD_GAINS_IN_MASK
- i
)) & 1) {
573 if (numXpdGain
>= AR5416_4K_NUM_PD_GAINS
) {
577 xpdGainValues
[numXpdGain
] = (uint16_t)(AR5416_PD_GAINS_IN_MASK
- i
);
582 /* Write the detector gain biases and their number */
583 OS_REG_WRITE(ah
, AR_PHY_TPCRG1
, (OS_REG_READ(ah
, AR_PHY_TPCRG1
) &
584 ~(AR_PHY_TPCRG1_NUM_PD_GAIN
| AR_PHY_TPCRG1_PD_GAIN_1
| AR_PHY_TPCRG1_PD_GAIN_2
| AR_PHY_TPCRG1_PD_GAIN_3
)) |
585 SM(numXpdGain
- 1, AR_PHY_TPCRG1_NUM_PD_GAIN
) | SM(xpdGainValues
[0], AR_PHY_TPCRG1_PD_GAIN_1
) |
586 SM(xpdGainValues
[1], AR_PHY_TPCRG1_PD_GAIN_2
) | SM(0, AR_PHY_TPCRG1_PD_GAIN_3
));
588 for (i
= 0; i
< AR5416_MAX_CHAINS
; i
++) {
590 if (AR_SREV_OWL_20_OR_LATER(ah
) &&
591 ( AH5416(ah
)->ah_rx_chainmask
== 0x5 || AH5416(ah
)->ah_tx_chainmask
== 0x5) && (i
!= 0)) {
592 /* Regs are swapped from chain 2 to 1 for 5416 2_0 with
593 * only chains 0 and 2 populated
595 regChainOffset
= (i
== 1) ? 0x2000 : 0x1000;
597 regChainOffset
= i
* 0x1000;
600 if (pEepData
->baseEepHeader
.txMask
& (1 << i
)) {
601 pRawDataset
= pEepData
->calPierData2G
[i
];
603 ar9285GetGainBoundariesAndPdadcs(ah
, chan
, pRawDataset
,
604 pCalBChans
, numPiers
,
606 &tMinCalPower
, gainBoundaries
,
607 pdadcValues
, numXpdGain
);
609 if ((i
== 0) || AR_SREV_OWL_20_OR_LATER(ah
)) {
611 * Note the pdadc table may not start at 0 dBm power, could be
612 * negative or greater than 0. Need to offset the power
613 * values by the amount of minPower for griffin
616 OS_REG_WRITE(ah
, AR_PHY_TPCRG5
+ regChainOffset
,
617 SM(pdGainOverlap_t2
, AR_PHY_TPCRG5_PD_GAIN_OVERLAP
) |
618 SM(gainBoundaries
[0], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1
) |
619 SM(gainBoundaries
[1], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2
) |
620 SM(gainBoundaries
[2], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3
) |
621 SM(gainBoundaries
[3], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4
));
624 /* Write the power values into the baseband power table */
625 regOffset
= AR_PHY_BASE
+ (672 << 2) + regChainOffset
;
627 for (j
= 0; j
< 32; j
++) {
628 reg32
= ((pdadcValues
[4*j
+ 0] & 0xFF) << 0) |
629 ((pdadcValues
[4*j
+ 1] & 0xFF) << 8) |
630 ((pdadcValues
[4*j
+ 2] & 0xFF) << 16) |
631 ((pdadcValues
[4*j
+ 3] & 0xFF) << 24) ;
632 OS_REG_WRITE(ah
, regOffset
, reg32
);
635 ath_hal_printf(ah
, "PDADC: Chain %d | PDADC %3d Value %3d | PDADC %3d Value %3d | PDADC %3d Value %3d | PDADC %3d Value %3d |\n",
637 4*j
, pdadcValues
[4*j
],
638 4*j
+1, pdadcValues
[4*j
+ 1],
639 4*j
+2, pdadcValues
[4*j
+ 2],
640 4*j
+3, pdadcValues
[4*j
+ 3]);
646 *pTxPowerIndexOffset
= 0;
652 ar9285GetGainBoundariesAndPdadcs(struct ath_hal
*ah
,
653 const struct ieee80211_channel
*chan
,
654 CAL_DATA_PER_FREQ_4K
*pRawDataSet
,
655 uint8_t * bChans
, uint16_t availPiers
,
656 uint16_t tPdGainOverlap
, int16_t *pMinCalPower
, uint16_t * pPdGainBoundaries
,
657 uint8_t * pPDADCValues
, uint16_t numXpdGains
)
661 int16_t ss
; /* potentially -ve index for taking care of pdGainOverlap */
662 uint16_t idxL
, idxR
, numPiers
; /* Pier indexes */
664 /* filled out Vpd table for all pdGains (chanL) */
665 static uint8_t vpdTableL
[AR5416_4K_NUM_PD_GAINS
][AR5416_MAX_PWR_RANGE_IN_HALF_DB
];
667 /* filled out Vpd table for all pdGains (chanR) */
668 static uint8_t vpdTableR
[AR5416_4K_NUM_PD_GAINS
][AR5416_MAX_PWR_RANGE_IN_HALF_DB
];
670 /* filled out Vpd table for all pdGains (interpolated) */
671 static uint8_t vpdTableI
[AR5416_4K_NUM_PD_GAINS
][AR5416_MAX_PWR_RANGE_IN_HALF_DB
];
673 uint8_t *pVpdL
, *pVpdR
, *pPwrL
, *pPwrR
;
674 uint8_t minPwrT4
[AR5416_4K_NUM_PD_GAINS
];
675 uint8_t maxPwrT4
[AR5416_4K_NUM_PD_GAINS
];
678 uint16_t sizeCurrVpdTable
, maxIndex
, tgtIndex
;
680 int16_t minDelta
= 0;
681 CHAN_CENTERS centers
;
683 ar5416GetChannelCenters(ah
, chan
, ¢ers
);
685 /* Trim numPiers for the number of populated channel Piers */
686 for (numPiers
= 0; numPiers
< availPiers
; numPiers
++) {
687 if (bChans
[numPiers
] == AR5416_BCHAN_UNUSED
) {
692 /* Find pier indexes around the current channel */
693 match
= getLowerUpperIndex((uint8_t)FREQ2FBIN(centers
.synth_center
, IEEE80211_IS_CHAN_2GHZ(chan
)),
694 bChans
, numPiers
, &idxL
, &idxR
);
697 /* Directly fill both vpd tables from the matching index */
698 for (i
= 0; i
< numXpdGains
; i
++) {
699 minPwrT4
[i
] = pRawDataSet
[idxL
].pwrPdg
[i
][0];
700 maxPwrT4
[i
] = pRawDataSet
[idxL
].pwrPdg
[i
][4];
701 ar9285FillVpdTable(minPwrT4
[i
], maxPwrT4
[i
],
702 pRawDataSet
[idxL
].pwrPdg
[i
],
703 pRawDataSet
[idxL
].vpdPdg
[i
],
704 AR5416_PD_GAIN_ICEPTS
, vpdTableI
[i
]);
707 for (i
= 0; i
< numXpdGains
; i
++) {
708 pVpdL
= pRawDataSet
[idxL
].vpdPdg
[i
];
709 pPwrL
= pRawDataSet
[idxL
].pwrPdg
[i
];
710 pVpdR
= pRawDataSet
[idxR
].vpdPdg
[i
];
711 pPwrR
= pRawDataSet
[idxR
].pwrPdg
[i
];
713 /* Start Vpd interpolation from the max of the minimum powers */
714 minPwrT4
[i
] = AH_MAX(pPwrL
[0], pPwrR
[0]);
716 /* End Vpd interpolation from the min of the max powers */
717 maxPwrT4
[i
] = AH_MIN(pPwrL
[AR5416_PD_GAIN_ICEPTS
- 1], pPwrR
[AR5416_PD_GAIN_ICEPTS
- 1]);
718 HALASSERT(maxPwrT4
[i
] > minPwrT4
[i
]);
721 ar9285FillVpdTable(minPwrT4
[i
], maxPwrT4
[i
], pPwrL
, pVpdL
,
722 AR5416_PD_GAIN_ICEPTS
, vpdTableL
[i
]);
723 ar9285FillVpdTable(minPwrT4
[i
], maxPwrT4
[i
], pPwrR
, pVpdR
,
724 AR5416_PD_GAIN_ICEPTS
, vpdTableR
[i
]);
726 /* Interpolate the final vpd */
727 for (j
= 0; j
<= (maxPwrT4
[i
] - minPwrT4
[i
]) / 2; j
++) {
728 vpdTableI
[i
][j
] = (uint8_t)(interpolate((uint16_t)FREQ2FBIN(centers
.synth_center
, IEEE80211_IS_CHAN_2GHZ(chan
)),
729 bChans
[idxL
], bChans
[idxR
], vpdTableL
[i
][j
], vpdTableR
[i
][j
]));
733 *pMinCalPower
= (int16_t)(minPwrT4
[0] / 2);
735 k
= 0; /* index for the final table */
736 for (i
= 0; i
< numXpdGains
; i
++) {
737 if (i
== (numXpdGains
- 1)) {
738 pPdGainBoundaries
[i
] = (uint16_t)(maxPwrT4
[i
] / 2);
740 pPdGainBoundaries
[i
] = (uint16_t)((maxPwrT4
[i
] + minPwrT4
[i
+1]) / 4);
743 pPdGainBoundaries
[i
] = (uint16_t)AH_MIN(AR5416_MAX_RATE_POWER
, pPdGainBoundaries
[i
]);
745 /* NB: only applies to owl 1.0 */
746 if ((i
== 0) && !AR_SREV_OWL_20_OR_LATER(ah
) ) {
748 * fix the gain delta, but get a delta that can be applied to min to
749 * keep the upper power values accurate, don't think max needs to
750 * be adjusted because should not be at that area of the table?
752 minDelta
= pPdGainBoundaries
[0] - 23;
753 pPdGainBoundaries
[0] = 23;
759 /* Find starting index for this pdGain */
761 ss
= 0; /* for the first pdGain, start from index 0 */
763 /* need overlap entries extrapolated below. */
764 ss
= (int16_t)((pPdGainBoundaries
[i
-1] - (minPwrT4
[i
] / 2)) - tPdGainOverlap
+ 1 + minDelta
);
766 vpdStep
= (int16_t)(vpdTableI
[i
][1] - vpdTableI
[i
][0]);
767 vpdStep
= (int16_t)((vpdStep
< 1) ? 1 : vpdStep
);
769 *-ve ss indicates need to extrapolate data below for this pdGain
771 while ((ss
< 0) && (k
< (AR5416_NUM_PDADC_VALUES
- 1))) {
772 tmpVal
= (int16_t)(vpdTableI
[i
][0] + ss
* vpdStep
);
773 pPDADCValues
[k
++] = (uint8_t)((tmpVal
< 0) ? 0 : tmpVal
);
777 sizeCurrVpdTable
= (uint8_t)((maxPwrT4
[i
] - minPwrT4
[i
]) / 2 +1);
778 tgtIndex
= (uint8_t)(pPdGainBoundaries
[i
] + tPdGainOverlap
- (minPwrT4
[i
] / 2));
779 maxIndex
= (tgtIndex
< sizeCurrVpdTable
) ? tgtIndex
: sizeCurrVpdTable
;
781 while ((ss
< maxIndex
) && (k
< (AR5416_NUM_PDADC_VALUES
- 1))) {
782 pPDADCValues
[k
++] = vpdTableI
[i
][ss
++];
785 vpdStep
= (int16_t)(vpdTableI
[i
][sizeCurrVpdTable
- 1] - vpdTableI
[i
][sizeCurrVpdTable
- 2]);
786 vpdStep
= (int16_t)((vpdStep
< 1) ? 1 : vpdStep
);
788 * for last gain, pdGainBoundary == Pmax_t2, so will
789 * have to extrapolate
791 if (tgtIndex
>= maxIndex
) { /* need to extrapolate above */
792 while ((ss
<= tgtIndex
) && (k
< (AR5416_NUM_PDADC_VALUES
- 1))) {
793 tmpVal
= (int16_t)((vpdTableI
[i
][sizeCurrVpdTable
- 1] +
794 (ss
- maxIndex
+1) * vpdStep
));
795 pPDADCValues
[k
++] = (uint8_t)((tmpVal
> 255) ? 255 : tmpVal
);
798 } /* extrapolated above */
799 } /* for all pdGainUsed */
801 /* Fill out pdGainBoundaries - only up to 2 allowed here, but hardware allows up to 4 */
802 while (i
< AR5416_PD_GAINS_IN_MASK
) {
803 pPdGainBoundaries
[i
] = pPdGainBoundaries
[i
-1];
807 while (k
< AR5416_NUM_PDADC_VALUES
) {
808 pPDADCValues
[k
] = pPDADCValues
[k
-1];
814 * XXX same as ar5416FillVpdTable
817 ar9285FillVpdTable(uint8_t pwrMin
, uint8_t pwrMax
, uint8_t *pPwrList
,
818 uint8_t *pVpdList
, uint16_t numIntercepts
, uint8_t *pRetVpdList
)
821 uint8_t currPwr
= pwrMin
;
824 HALASSERT(pwrMax
> pwrMin
);
825 for (i
= 0; i
<= (pwrMax
- pwrMin
) / 2; i
++) {
826 getLowerUpperIndex(currPwr
, pPwrList
, numIntercepts
,
829 idxR
= 1; /* extrapolate below */
830 if (idxL
== numIntercepts
- 1)
831 idxL
= (uint16_t)(numIntercepts
- 2); /* extrapolate above */
832 if (pPwrList
[idxL
] == pPwrList
[idxR
])
835 k
= (uint16_t)( ((currPwr
- pPwrList
[idxL
]) * pVpdList
[idxR
] + (pPwrList
[idxR
] - currPwr
) * pVpdList
[idxL
]) /
836 (pPwrList
[idxR
] - pPwrList
[idxL
]) );
838 pRetVpdList
[i
] = (uint8_t)k
;
839 currPwr
+= 2; /* half dB steps */
845 interpolate(uint16_t target
, uint16_t srcLeft
, uint16_t srcRight
,
846 int16_t targetLeft
, int16_t targetRight
)
850 if (srcRight
== srcLeft
) {
853 rv
= (int16_t)( ((target
- srcLeft
) * targetRight
+
854 (srcRight
- target
) * targetLeft
) / (srcRight
- srcLeft
) );
860 getLowerUpperIndex(uint8_t target
, uint8_t *pList
, uint16_t listSize
,
861 uint16_t *indexL
, uint16_t *indexR
)
866 * Check first and last elements for beyond ordered array cases.
868 if (target
<= pList
[0]) {
869 *indexL
= *indexR
= 0;
872 if (target
>= pList
[listSize
-1]) {
873 *indexL
= *indexR
= (uint16_t)(listSize
- 1);
877 /* look for value being near or between 2 values in list */
878 for (i
= 0; i
< listSize
- 1; i
++) {
880 * If value is close to the current value of the list
881 * then target is not between values, it is one of the values
883 if (pList
[i
] == target
) {
884 *indexL
= *indexR
= i
;
888 * Look for value being between current value and next value
889 * if so return these 2 values
891 if (target
< pList
[i
+ 1]) {
893 *indexR
= (uint16_t)(i
+ 1);
898 *indexL
= *indexR
= 0;