2 * Copyright (c) 2008-2009 Sam Leffler, Errno Consulting
3 * Copyright (c) 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/ar9280.c 203882 2010-02-14 16:26:32Z rpaulo $
22 * NB: Merlin and later have a simpler RF backend.
25 #include "ah_internal.h"
27 #include "ah_eeprom_v14.h"
29 #include "ar5416/ar9280.h"
30 #include "ar5416/ar5416reg.h"
31 #include "ar5416/ar5416phy.h"
34 RF_HAL_FUNCS base
; /* public state, must be first */
35 uint16_t pcdacTable
[1]; /* XXX */
37 #define AR9280(ah) ((struct ar9280State *) AH5212(ah)->ah_rfHal)
39 static HAL_BOOL
ar9280GetChannelMaxMinPower(struct ath_hal
*,
40 const struct ieee80211_channel
*, int16_t *maxPow
,int16_t *minPow
);
41 int16_t ar9280GetNfAdjust(struct ath_hal
*ah
, const HAL_CHANNEL_INTERNAL
*c
);
44 ar9280WriteRegs(struct ath_hal
*ah
, u_int modesIndex
, u_int freqIndex
,
47 (void) ath_hal_ini_write(ah
, &AH5416(ah
)->ah_ini_bb_rfgain
,
52 * Take the MHz channel value and set the Channel value
54 * ASSUMES: Writes enabled to analog bus
59 * Channel Frequency = (3/4) * freq_ref * (chansel[8:0] + chanfrac[16:0]/2^17)
63 * Channel Frequency = (3/2) * freq_ref * (chansel[8:0] + chanfrac[16:0]/2^10)
64 * (freq_ref = 40MHz/(24>>amodeRefSel))
66 * For 5GHz channels which are 5MHz spaced,
67 * Channel Frequency = (3/2) * freq_ref * (chansel[8:0] + chanfrac[16:0]/2^17)
71 ar9280SetChannel(struct ath_hal
*ah
, const struct ieee80211_channel
*chan
)
73 uint16_t bMode
, fracMode
, aModeRefSel
= 0;
74 uint32_t freq
, ndiv
, channelSel
= 0, channelFrac
= 0, reg32
= 0;
76 uint32_t refDivA
= 24;
78 OS_MARK(ah
, AH_MARK_SETCHANNEL
, chan
->ic_freq
);
80 ar5416GetChannelCenters(ah
, chan
, ¢ers
);
81 freq
= centers
.synth_center
;
83 reg32
= OS_REG_READ(ah
, AR_PHY_SYNTH_CONTROL
);
86 if (freq
< 4800) { /* 2 GHz, fractional mode */
92 channelSel
= (freq
* 0x10000)/15;
94 txctl
= OS_REG_READ(ah
, AR_PHY_CCK_TX_CTRL
);
96 /* Enable channel spreading for channel 14 */
97 OS_REG_WRITE(ah
, AR_PHY_CCK_TX_CTRL
,
98 txctl
| AR_PHY_CCK_TX_CTRL_JAPAN
);
100 OS_REG_WRITE(ah
, AR_PHY_CCK_TX_CTRL
,
101 txctl
&~ AR_PHY_CCK_TX_CTRL_JAPAN
);
107 if ((freq
% 20) == 0) {
109 } else if ((freq
% 10) == 0) {
113 /* Enable 2G (fractional) mode for channels which are 5MHz spaced */
116 channelSel
= (freq
* 0x8000)/15;
118 /* RefDivA setting */
119 OS_REG_RMW_FIELD(ah
, AR_AN_SYNTH9
,
120 AR_AN_SYNTH9_REFDIVA
, refDivA
);
123 ndiv
= (freq
* (refDivA
>> aModeRefSel
))/60;
124 channelSel
= ndiv
& 0x1ff;
125 channelFrac
= (ndiv
& 0xfffffe00) * 2;
126 channelSel
= (channelSel
<< 17) | channelFrac
;
130 reg32
= reg32
| (bMode
<< 29) | (fracMode
<< 28) |
131 (aModeRefSel
<< 26) | (channelSel
);
133 OS_REG_WRITE(ah
, AR_PHY_SYNTH_CONTROL
, reg32
);
135 AH_PRIVATE(ah
)->ah_curchan
= chan
;
141 * Return a reference to the requested RF Bank.
144 ar9280GetRfBank(struct ath_hal
*ah
, int bank
)
146 HALDEBUG(ah
, HAL_DEBUG_ANY
, "%s: unknown RF Bank %d requested\n",
152 * Reads EEPROM header info from device structure and programs
156 ar9280SetRfRegs(struct ath_hal
*ah
, const struct ieee80211_channel
*chan
,
157 uint16_t modesIndex
, uint16_t *rfXpdGain
)
159 return AH_TRUE
; /* nothing to do */
163 * Read the transmit power levels from the structures taken from EEPROM
164 * Interpolate read transmit power values for this channel
165 * Organize the transmit power values into a table for writing into the hardware
169 ar9280SetPowerTable(struct ath_hal
*ah
, int16_t *pPowerMin
, int16_t *pPowerMax
,
170 const struct ieee80211_channel
*chan
, uint16_t *rfXpdGain
)
177 ar9280GetMinPower(struct ath_hal
*ah
, EXPN_DATA_PER_CHANNEL_5112
*data
)
180 int16_t minGain
,minPwr
,minPcdac
,retVal
;
182 /* Assume NUM_POINTS_XPD0 > 0 */
183 minGain
= data
->pDataPerXPD
[0].xpd_gain
;
184 for (minIndex
=0,i
=1; i
<NUM_XPD_PER_CHANNEL
; i
++) {
185 if (data
->pDataPerXPD
[i
].xpd_gain
< minGain
) {
187 minGain
= data
->pDataPerXPD
[i
].xpd_gain
;
190 minPwr
= data
->pDataPerXPD
[minIndex
].pwr_t4
[0];
191 minPcdac
= data
->pDataPerXPD
[minIndex
].pcdac
[0];
192 for (i
=1; i
<NUM_POINTS_XPD0
; i
++) {
193 if (data
->pDataPerXPD
[minIndex
].pwr_t4
[i
] < minPwr
) {
194 minPwr
= data
->pDataPerXPD
[minIndex
].pwr_t4
[i
];
195 minPcdac
= data
->pDataPerXPD
[minIndex
].pcdac
[i
];
198 retVal
= minPwr
- (minPcdac
*2);
204 ar9280GetChannelMaxMinPower(struct ath_hal
*ah
,
205 const struct ieee80211_channel
*chan
,
206 int16_t *maxPow
, int16_t *minPow
)
209 struct ath_hal_5212
*ahp
= AH5212(ah
);
210 int numChannels
=0,i
,last
;
211 int totalD
, totalF
,totalMin
;
212 EXPN_DATA_PER_CHANNEL_5112
*data
=AH_NULL
;
213 EEPROM_POWER_EXPN_5112
*powerArray
=AH_NULL
;
216 if (IS_CHAN_A(chan
)) {
217 powerArray
= ahp
->ah_modePowerArray5112
;
218 data
= powerArray
[headerInfo11A
].pDataPerChannel
;
219 numChannels
= powerArray
[headerInfo11A
].numChannels
;
220 } else if (IS_CHAN_G(chan
) || IS_CHAN_108G(chan
)) {
221 /* XXX - is this correct? Should we also use the same power for turbo G? */
222 powerArray
= ahp
->ah_modePowerArray5112
;
223 data
= powerArray
[headerInfo11G
].pDataPerChannel
;
224 numChannels
= powerArray
[headerInfo11G
].numChannels
;
225 } else if (IS_CHAN_B(chan
)) {
226 powerArray
= ahp
->ah_modePowerArray5112
;
227 data
= powerArray
[headerInfo11B
].pDataPerChannel
;
228 numChannels
= powerArray
[headerInfo11B
].numChannels
;
232 /* Make sure the channel is in the range of the TP values
235 if ((numChannels
< 1) ||
236 (chan
->channel
< data
[0].channelValue
) ||
237 (chan
->channel
> data
[numChannels
-1].channelValue
))
240 /* Linearly interpolate the power value now */
242 (i
<numChannels
) && (chan
->channel
> data
[i
].channelValue
);
244 totalD
= data
[i
].channelValue
- data
[last
].channelValue
;
246 totalF
= data
[i
].maxPower_t4
- data
[last
].maxPower_t4
;
247 *maxPow
= (int8_t) ((totalF
*(chan
->channel
-data
[last
].channelValue
) + data
[last
].maxPower_t4
*totalD
)/totalD
);
249 totalMin
= ar9280GetMinPower(ah
,&data
[i
]) - ar9280GetMinPower(ah
, &data
[last
]);
250 *minPow
= (int8_t) ((totalMin
*(chan
->channel
-data
[last
].channelValue
) + ar9280GetMinPower(ah
, &data
[last
])*totalD
)/totalD
);
253 if (chan
->channel
== data
[i
].channelValue
) {
254 *maxPow
= data
[i
].maxPower_t4
;
255 *minPow
= ar9280GetMinPower(ah
, &data
[i
]);
261 *maxPow
= *minPow
= 0;
267 ar9280GetNoiseFloor(struct ath_hal
*ah
, int16_t nfarray
[])
271 nf
= MS(OS_REG_READ(ah
, AR_PHY_CCA
), AR9280_PHY_MINCCA_PWR
);
273 nf
= 0 - ((nf
^ 0x1ff) + 1);
274 HALDEBUG(ah
, HAL_DEBUG_NFCAL
,
275 "NF calibrated [ctl] [chain 0] is %d\n", nf
);
278 nf
= MS(OS_REG_READ(ah
, AR_PHY_CH1_CCA
), AR9280_PHY_CH1_MINCCA_PWR
);
280 nf
= 0 - ((nf
^ 0x1ff) + 1);
281 HALDEBUG(ah
, HAL_DEBUG_NFCAL
,
282 "NF calibrated [ctl] [chain 1] is %d\n", nf
);
285 nf
= MS(OS_REG_READ(ah
, AR_PHY_EXT_CCA
), AR9280_PHY_EXT_MINCCA_PWR
);
287 nf
= 0 - ((nf
^ 0x1ff) + 1);
288 HALDEBUG(ah
, HAL_DEBUG_NFCAL
,
289 "NF calibrated [ext] [chain 0] is %d\n", nf
);
292 nf
= MS(OS_REG_READ(ah
, AR_PHY_CH1_EXT_CCA
), AR9280_PHY_CH1_EXT_MINCCA_PWR
);
294 nf
= 0 - ((nf
^ 0x1ff) + 1);
295 HALDEBUG(ah
, HAL_DEBUG_NFCAL
,
296 "NF calibrated [ext] [chain 1] is %d\n", nf
);
301 * Adjust NF based on statistical values for 5GHz frequencies.
302 * Stubbed:Not used by Fowl
305 ar9280GetNfAdjust(struct ath_hal
*ah
, const HAL_CHANNEL_INTERNAL
*c
)
311 * Free memory for analog bank scratch buffers
314 ar9280RfDetach(struct ath_hal
*ah
)
316 struct ath_hal_5212
*ahp
= AH5212(ah
);
318 HALASSERT(ahp
->ah_rfHal
!= AH_NULL
);
319 ath_hal_free(ahp
->ah_rfHal
);
320 ahp
->ah_rfHal
= AH_NULL
;
324 ar9280RfAttach(struct ath_hal
*ah
, HAL_STATUS
*status
)
326 struct ath_hal_5212
*ahp
= AH5212(ah
);
327 struct ar9280State
*priv
;
329 HALDEBUG(ah
, HAL_DEBUG_ATTACH
, "%s: attach AR9280 radio\n", __func__
);
331 HALASSERT(ahp
->ah_rfHal
== AH_NULL
);
332 priv
= ath_hal_malloc(sizeof(struct ar9280State
));
333 if (priv
== AH_NULL
) {
334 HALDEBUG(ah
, HAL_DEBUG_ANY
,
335 "%s: cannot allocate private state\n", __func__
);
336 *status
= HAL_ENOMEM
; /* XXX */
339 priv
->base
.rfDetach
= ar9280RfDetach
;
340 priv
->base
.writeRegs
= ar9280WriteRegs
;
341 priv
->base
.getRfBank
= ar9280GetRfBank
;
342 priv
->base
.setChannel
= ar9280SetChannel
;
343 priv
->base
.setRfRegs
= ar9280SetRfRegs
;
344 priv
->base
.setPowerTable
= ar9280SetPowerTable
;
345 priv
->base
.getChannelMaxMinPower
= ar9280GetChannelMaxMinPower
;
346 priv
->base
.getNfAdjust
= ar9280GetNfAdjust
;
348 ahp
->ah_pcdacTable
= priv
->pcdacTable
;
349 ahp
->ah_pcdacTableSize
= sizeof(priv
->pcdacTable
);
350 ahp
->ah_rfHal
= &priv
->base
;
352 * Set noise floor adjust method; we arrange a
353 * direct call instead of thunking.
355 AH_PRIVATE(ah
)->ah_getNfAdjust
= priv
->base
.getNfAdjust
;
356 AH_PRIVATE(ah
)->ah_getNoiseFloor
= ar9280GetNoiseFloor
;