]>
Commit | Line | Data |
---|---|---|
f1dc5600 | 1 | /* |
5b68138e | 2 | * Copyright (c) 2008-2011 Atheros Communications Inc. |
f1dc5600 S |
3 | * |
4 | * Permission to use, copy, modify, and/or distribute this software for any | |
5 | * purpose with or without fee is hereby granted, provided that the above | |
6 | * copyright notice and this permission notice appear in all copies. | |
7 | * | |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 | */ | |
16 | ||
c46917bb | 17 | #include "hw.h" |
641d9921 | 18 | #include "hw-ops.h" |
ee40fa06 | 19 | #include <linux/export.h> |
f1dc5600 | 20 | |
795f5e2c LR |
21 | /* Common calibration code */ |
22 | ||
f1dc5600 | 23 | |
f1dc5600 S |
24 | static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer) |
25 | { | |
26 | int16_t nfval; | |
27 | int16_t sort[ATH9K_NF_CAL_HIST_MAX]; | |
28 | int i, j; | |
29 | ||
30 | for (i = 0; i < ATH9K_NF_CAL_HIST_MAX; i++) | |
31 | sort[i] = nfCalBuffer[i]; | |
32 | ||
33 | for (i = 0; i < ATH9K_NF_CAL_HIST_MAX - 1; i++) { | |
34 | for (j = 1; j < ATH9K_NF_CAL_HIST_MAX - i; j++) { | |
35 | if (sort[j] > sort[j - 1]) { | |
36 | nfval = sort[j]; | |
37 | sort[j] = sort[j - 1]; | |
38 | sort[j - 1] = nfval; | |
39 | } | |
40 | } | |
41 | } | |
42 | nfval = sort[(ATH9K_NF_CAL_HIST_MAX - 1) >> 1]; | |
43 | ||
44 | return nfval; | |
45 | } | |
46 | ||
2292ca6d FF |
47 | static struct ath_nf_limits *ath9k_hw_get_nf_limits(struct ath_hw *ah, |
48 | struct ath9k_channel *chan) | |
49 | { | |
50 | struct ath_nf_limits *limit; | |
51 | ||
52 | if (!chan || IS_CHAN_2GHZ(chan)) | |
53 | limit = &ah->nf_2g; | |
54 | else | |
55 | limit = &ah->nf_5g; | |
56 | ||
57 | return limit; | |
58 | } | |
59 | ||
60 | static s16 ath9k_hw_get_default_nf(struct ath_hw *ah, | |
61 | struct ath9k_channel *chan) | |
62 | { | |
63 | return ath9k_hw_get_nf_limits(ah, chan)->nominal; | |
64 | } | |
65 | ||
5bc225ac LB |
66 | s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan, |
67 | s16 nf) | |
f23fba49 FF |
68 | { |
69 | s8 noise = ATH_DEFAULT_NOISE_FLOOR; | |
70 | ||
5bc225ac LB |
71 | if (nf) { |
72 | s8 delta = nf - ATH9K_NF_CAL_NOISE_THRESH - | |
f23fba49 FF |
73 | ath9k_hw_get_default_nf(ah, chan); |
74 | if (delta > 0) | |
75 | noise += delta; | |
76 | } | |
77 | return noise; | |
78 | } | |
79 | EXPORT_SYMBOL(ath9k_hw_getchan_noise); | |
2292ca6d FF |
80 | |
81 | static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah, | |
70cf1533 | 82 | struct ath9k_hw_cal_data *cal, |
f1dc5600 S |
83 | int16_t *nfarray) |
84 | { | |
70cf1533 | 85 | struct ath_common *common = ath9k_hw_common(ah); |
2292ca6d | 86 | struct ath_nf_limits *limit; |
70cf1533 FF |
87 | struct ath9k_nfcal_hist *h; |
88 | bool high_nf_mid = false; | |
28ef6450 | 89 | u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask; |
f1dc5600 S |
90 | int i; |
91 | ||
70cf1533 | 92 | h = cal->nfCalHist; |
2292ca6d FF |
93 | limit = ath9k_hw_get_nf_limits(ah, ah->curchan); |
94 | ||
f1dc5600 | 95 | for (i = 0; i < NUM_NF_READINGS; i++) { |
28ef6450 | 96 | if (!(chainmask & (1 << i)) || |
6b3d3486 | 97 | ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(ah->curchan))) |
28ef6450 RM |
98 | continue; |
99 | ||
f1dc5600 S |
100 | h[i].nfCalBuffer[h[i].currIndex] = nfarray[i]; |
101 | ||
102 | if (++h[i].currIndex >= ATH9K_NF_CAL_HIST_MAX) | |
103 | h[i].currIndex = 0; | |
104 | ||
105 | if (h[i].invalidNFcount > 0) { | |
f2552e28 FF |
106 | h[i].invalidNFcount--; |
107 | h[i].privNF = nfarray[i]; | |
f1dc5600 S |
108 | } else { |
109 | h[i].privNF = | |
110 | ath9k_hw_get_nf_hist_mid(h[i].nfCalBuffer); | |
111 | } | |
2292ca6d | 112 | |
70cf1533 FF |
113 | if (!h[i].privNF) |
114 | continue; | |
115 | ||
116 | if (h[i].privNF > limit->max) { | |
117 | high_nf_mid = true; | |
118 | ||
d2182b69 | 119 | ath_dbg(common, CALIBRATE, |
226afe68 JP |
120 | "NFmid[%d] (%d) > MAX (%d), %s\n", |
121 | i, h[i].privNF, limit->max, | |
4b9b42bf | 122 | (test_bit(NFCAL_INTF, &cal->cal_flags) ? |
226afe68 JP |
123 | "not corrected (due to interference)" : |
124 | "correcting to MAX")); | |
70cf1533 FF |
125 | |
126 | /* | |
127 | * Normally we limit the average noise floor by the | |
128 | * hardware specific maximum here. However if we have | |
129 | * encountered stuck beacons because of interference, | |
130 | * we bypass this limit here in order to better deal | |
131 | * with our environment. | |
132 | */ | |
4b9b42bf | 133 | if (!test_bit(NFCAL_INTF, &cal->cal_flags)) |
70cf1533 FF |
134 | h[i].privNF = limit->max; |
135 | } | |
f1dc5600 | 136 | } |
70cf1533 FF |
137 | |
138 | /* | |
139 | * If the noise floor seems normal for all chains, assume that | |
140 | * there is no significant interference in the environment anymore. | |
141 | * Re-enable the enforcement of the NF maximum again. | |
142 | */ | |
143 | if (!high_nf_mid) | |
4b9b42bf | 144 | clear_bit(NFCAL_INTF, &cal->cal_flags); |
f1dc5600 S |
145 | } |
146 | ||
b43d59fb | 147 | static bool ath9k_hw_get_nf_thresh(struct ath_hw *ah, |
57fbcce3 | 148 | enum nl80211_band band, |
b43d59fb | 149 | int16_t *nft) |
f1dc5600 | 150 | { |
76061abb | 151 | switch (band) { |
57fbcce3 | 152 | case NL80211_BAND_5GHZ: |
f74df6fb | 153 | *nft = (int8_t)ah->eep_ops->get_eeprom(ah, EEP_NFTHRESH_5); |
f1dc5600 | 154 | break; |
57fbcce3 | 155 | case NL80211_BAND_2GHZ: |
f74df6fb | 156 | *nft = (int8_t)ah->eep_ops->get_eeprom(ah, EEP_NFTHRESH_2); |
f1dc5600 S |
157 | break; |
158 | default: | |
76061abb | 159 | BUG_ON(1); |
f1dc5600 S |
160 | return false; |
161 | } | |
162 | ||
163 | return true; | |
164 | } | |
165 | ||
795f5e2c LR |
166 | void ath9k_hw_reset_calibration(struct ath_hw *ah, |
167 | struct ath9k_cal_list *currCal) | |
f1dc5600 | 168 | { |
f1dc5600 S |
169 | int i; |
170 | ||
171 | ath9k_hw_setup_calibration(ah, currCal); | |
172 | ||
173 | currCal->calState = CAL_RUNNING; | |
174 | ||
175 | for (i = 0; i < AR5416_MAX_CHAINS; i++) { | |
2660b81a S |
176 | ah->meas0.sign[i] = 0; |
177 | ah->meas1.sign[i] = 0; | |
178 | ah->meas2.sign[i] = 0; | |
179 | ah->meas3.sign[i] = 0; | |
f1dc5600 S |
180 | } |
181 | ||
2660b81a | 182 | ah->cal_samples = 0; |
f1dc5600 S |
183 | } |
184 | ||
c9e27d94 | 185 | /* This is done for the currently configured channel */ |
cbe61d8a | 186 | bool ath9k_hw_reset_calvalid(struct ath_hw *ah) |
f1dc5600 | 187 | { |
c46917bb | 188 | struct ath_common *common = ath9k_hw_common(ah); |
cbfe9468 | 189 | struct ath9k_cal_list *currCal = ah->cal_list_curr; |
f1dc5600 | 190 | |
20bd2a09 | 191 | if (!ah->caldata) |
c9e27d94 | 192 | return true; |
f1dc5600 S |
193 | |
194 | if (!AR_SREV_9100(ah) && !AR_SREV_9160_10_OR_LATER(ah)) | |
c9e27d94 | 195 | return true; |
f1dc5600 S |
196 | |
197 | if (currCal == NULL) | |
c9e27d94 | 198 | return true; |
f1dc5600 S |
199 | |
200 | if (currCal->calState != CAL_DONE) { | |
d2182b69 | 201 | ath_dbg(common, CALIBRATE, "Calibration state incorrect, %d\n", |
226afe68 | 202 | currCal->calState); |
c9e27d94 | 203 | return true; |
f1dc5600 S |
204 | } |
205 | ||
6497827f | 206 | if (!(ah->supp_cals & currCal->calData->calType)) |
c9e27d94 | 207 | return true; |
f1dc5600 | 208 | |
d2182b69 | 209 | ath_dbg(common, CALIBRATE, "Resetting Cal %d state for channel %u\n", |
e4744ec7 | 210 | currCal->calData->calType, ah->curchan->chan->center_freq); |
f1dc5600 | 211 | |
20bd2a09 | 212 | ah->caldata->CalValid &= ~currCal->calData->calType; |
f1dc5600 S |
213 | currCal->calState = CAL_WAITING; |
214 | ||
c9e27d94 | 215 | return false; |
f1dc5600 | 216 | } |
7322fd19 | 217 | EXPORT_SYMBOL(ath9k_hw_reset_calvalid); |
f1dc5600 | 218 | |
00c86590 | 219 | void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update) |
f1dc5600 | 220 | { |
4254bc1c | 221 | if (ah->caldata) |
4b9b42bf | 222 | set_bit(NFCAL_PENDING, &ah->caldata->cal_flags); |
4254bc1c | 223 | |
f1dc5600 S |
224 | REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, |
225 | AR_PHY_AGC_CONTROL_ENABLE_NF); | |
00c86590 FF |
226 | |
227 | if (update) | |
228 | REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, | |
f1dc5600 | 229 | AR_PHY_AGC_CONTROL_NO_UPDATE_NF); |
00c86590 FF |
230 | else |
231 | REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, | |
232 | AR_PHY_AGC_CONTROL_NO_UPDATE_NF); | |
233 | ||
f1dc5600 S |
234 | REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); |
235 | } | |
236 | ||
7b8aaead | 237 | int ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) |
bbacee13 | 238 | { |
20bd2a09 | 239 | struct ath9k_nfcal_hist *h = NULL; |
bbacee13 | 240 | unsigned i, j; |
487f0e01 | 241 | u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask; |
bbacee13 | 242 | struct ath_common *common = ath9k_hw_common(ah); |
20bd2a09 | 243 | s16 default_nf = ath9k_hw_get_default_nf(ah, chan); |
82def495 | 244 | u32 bb_agc_ctl = REG_READ(ah, AR_PHY_AGC_CONTROL); |
bbacee13 | 245 | |
20bd2a09 FF |
246 | if (ah->caldata) |
247 | h = ah->caldata->nfCalHist; | |
bbacee13 | 248 | |
9830ba6c | 249 | ENABLE_REG_RMW_BUFFER(ah); |
bbacee13 FF |
250 | for (i = 0; i < NUM_NF_READINGS; i++) { |
251 | if (chainmask & (1 << i)) { | |
20bd2a09 FF |
252 | s16 nfval; |
253 | ||
e4744ec7 | 254 | if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan)) |
28ef6450 RM |
255 | continue; |
256 | ||
b9018975 SW |
257 | if (ah->nf_override) |
258 | nfval = ah->nf_override; | |
259 | else if (h) | |
20bd2a09 FF |
260 | nfval = h[i].privNF; |
261 | else | |
262 | nfval = default_nf; | |
263 | ||
9830ba6c OR |
264 | REG_RMW(ah, ah->nf_regs[i], |
265 | (((u32) nfval << 1) & 0x1ff), 0x1ff); | |
bbacee13 FF |
266 | } |
267 | } | |
268 | ||
82def495 MP |
269 | /* |
270 | * stop NF cal if ongoing to ensure NF load completes immediately | |
271 | * (or after end rx/tx frame if ongoing) | |
272 | */ | |
273 | if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NF) { | |
274 | REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); | |
275 | REG_RMW_BUFFER_FLUSH(ah); | |
276 | ENABLE_REG_RMW_BUFFER(ah); | |
277 | } | |
278 | ||
bbacee13 FF |
279 | /* |
280 | * Load software filtered NF value into baseband internal minCCApwr | |
281 | * variable. | |
282 | */ | |
283 | REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, | |
284 | AR_PHY_AGC_CONTROL_ENABLE_NF); | |
285 | REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, | |
286 | AR_PHY_AGC_CONTROL_NO_UPDATE_NF); | |
287 | REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); | |
9830ba6c | 288 | REG_RMW_BUFFER_FLUSH(ah); |
bbacee13 FF |
289 | |
290 | /* | |
291 | * Wait for load to complete, should be fast, a few 10s of us. | |
82def495 MP |
292 | * The max delay was changed from an original 250us to 22.2 msec. |
293 | * This would increase timeout to the longest possible frame | |
294 | * (11n max length 22.1 msec) | |
bbacee13 | 295 | */ |
82def495 | 296 | for (j = 0; j < 22200; j++) { |
bbacee13 | 297 | if ((REG_READ(ah, AR_PHY_AGC_CONTROL) & |
82def495 | 298 | AR_PHY_AGC_CONTROL_NF) == 0) |
bbacee13 FF |
299 | break; |
300 | udelay(10); | |
301 | } | |
302 | ||
82def495 MP |
303 | /* |
304 | * Restart NF so it can continue. | |
305 | */ | |
306 | if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NF) { | |
307 | ENABLE_REG_RMW_BUFFER(ah); | |
308 | if (bb_agc_ctl & AR_PHY_AGC_CONTROL_ENABLE_NF) | |
309 | REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, | |
310 | AR_PHY_AGC_CONTROL_ENABLE_NF); | |
311 | if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NO_UPDATE_NF) | |
312 | REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, | |
313 | AR_PHY_AGC_CONTROL_NO_UPDATE_NF); | |
314 | REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); | |
315 | REG_RMW_BUFFER_FLUSH(ah); | |
316 | } | |
317 | ||
bbacee13 FF |
318 | /* |
319 | * We timed out waiting for the noisefloor to load, probably due to an | |
320 | * in-progress rx. Simply return here and allow the load plenty of time | |
321 | * to complete before the next calibration interval. We need to avoid | |
322 | * trying to load -50 (which happens below) while the previous load is | |
323 | * still in progress as this can cause rx deafness. Instead by returning | |
324 | * here, the baseband nf cal will just be capped by our present | |
325 | * noisefloor until the next calibration timer. | |
326 | */ | |
82def495 | 327 | if (j == 22200) { |
d2182b69 | 328 | ath_dbg(common, ANY, |
226afe68 JP |
329 | "Timeout while waiting for nf to load: AR_PHY_AGC_CONTROL=0x%x\n", |
330 | REG_READ(ah, AR_PHY_AGC_CONTROL)); | |
7b8aaead | 331 | return -ETIMEDOUT; |
bbacee13 FF |
332 | } |
333 | ||
334 | /* | |
335 | * Restore maxCCAPower register parameter again so that we're not capped | |
336 | * by the median we just loaded. This will be initial (and max) value | |
337 | * of next noise floor calibration the baseband does. | |
338 | */ | |
9830ba6c | 339 | ENABLE_REG_RMW_BUFFER(ah); |
bbacee13 FF |
340 | for (i = 0; i < NUM_NF_READINGS; i++) { |
341 | if (chainmask & (1 << i)) { | |
e4744ec7 | 342 | if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan)) |
28ef6450 RM |
343 | continue; |
344 | ||
9830ba6c OR |
345 | REG_RMW(ah, ah->nf_regs[i], |
346 | (((u32) (-50) << 1) & 0x1ff), 0x1ff); | |
bbacee13 FF |
347 | } |
348 | } | |
9830ba6c | 349 | REG_RMW_BUFFER_FLUSH(ah); |
7b8aaead FF |
350 | |
351 | return 0; | |
bbacee13 | 352 | } |
b9018975 | 353 | EXPORT_SYMBOL(ath9k_hw_loadnf); |
bbacee13 FF |
354 | |
355 | ||
f2552e28 FF |
356 | static void ath9k_hw_nf_sanitize(struct ath_hw *ah, s16 *nf) |
357 | { | |
358 | struct ath_common *common = ath9k_hw_common(ah); | |
359 | struct ath_nf_limits *limit; | |
360 | int i; | |
361 | ||
362 | if (IS_CHAN_2GHZ(ah->curchan)) | |
363 | limit = &ah->nf_2g; | |
364 | else | |
365 | limit = &ah->nf_5g; | |
366 | ||
367 | for (i = 0; i < NUM_NF_READINGS; i++) { | |
368 | if (!nf[i]) | |
369 | continue; | |
370 | ||
d2182b69 | 371 | ath_dbg(common, CALIBRATE, |
226afe68 JP |
372 | "NF calibrated [%s] [chain %d] is %d\n", |
373 | (i >= 3 ? "ext" : "ctl"), i % 3, nf[i]); | |
54bd5006 | 374 | |
2ee0a070 | 375 | if (nf[i] > limit->max) { |
d2182b69 | 376 | ath_dbg(common, CALIBRATE, |
226afe68 | 377 | "NF[%d] (%d) > MAX (%d), correcting to MAX\n", |
2ee0a070 | 378 | i, nf[i], limit->max); |
f2552e28 FF |
379 | nf[i] = limit->max; |
380 | } else if (nf[i] < limit->min) { | |
d2182b69 | 381 | ath_dbg(common, CALIBRATE, |
226afe68 JP |
382 | "NF[%d] (%d) < MIN (%d), correcting to NOM\n", |
383 | i, nf[i], limit->min); | |
f2552e28 FF |
384 | nf[i] = limit->nominal; |
385 | } | |
386 | } | |
387 | } | |
388 | ||
4254bc1c | 389 | bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan) |
f1dc5600 | 390 | { |
c46917bb | 391 | struct ath_common *common = ath9k_hw_common(ah); |
f1dc5600 S |
392 | int16_t nf, nfThresh; |
393 | int16_t nfarray[NUM_NF_READINGS] = { 0 }; | |
394 | struct ath9k_nfcal_hist *h; | |
76061abb | 395 | struct ieee80211_channel *c = chan->chan; |
20bd2a09 FF |
396 | struct ath9k_hw_cal_data *caldata = ah->caldata; |
397 | ||
f1dc5600 | 398 | if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) { |
d2182b69 | 399 | ath_dbg(common, CALIBRATE, |
226afe68 | 400 | "NF did not complete in calibration window\n"); |
4254bc1c | 401 | return false; |
d9891c78 FF |
402 | } |
403 | ||
404 | ath9k_hw_do_getnf(ah, nfarray); | |
405 | ath9k_hw_nf_sanitize(ah, nfarray); | |
406 | nf = nfarray[0]; | |
407 | if (ath9k_hw_get_nf_thresh(ah, c->band, &nfThresh) | |
408 | && nf > nfThresh) { | |
d2182b69 | 409 | ath_dbg(common, CALIBRATE, |
226afe68 JP |
410 | "noise floor failed detected; detected %d, threshold %d\n", |
411 | nf, nfThresh); | |
d9891c78 FF |
412 | } |
413 | ||
414 | if (!caldata) { | |
415 | chan->noisefloor = nf; | |
416 | return false; | |
f1dc5600 S |
417 | } |
418 | ||
20bd2a09 | 419 | h = caldata->nfCalHist; |
4b9b42bf | 420 | clear_bit(NFCAL_PENDING, &caldata->cal_flags); |
70cf1533 | 421 | ath9k_hw_update_nfcal_hist_buffer(ah, caldata, nfarray); |
d9891c78 | 422 | chan->noisefloor = h[0].privNF; |
5bc225ac | 423 | ah->noise = ath9k_hw_getchan_noise(ah, chan, chan->noisefloor); |
4254bc1c | 424 | return true; |
f1dc5600 | 425 | } |
1a19f77f | 426 | EXPORT_SYMBOL(ath9k_hw_getnf); |
f1dc5600 | 427 | |
20bd2a09 FF |
428 | void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah, |
429 | struct ath9k_channel *chan) | |
f1dc5600 | 430 | { |
20bd2a09 FF |
431 | struct ath9k_nfcal_hist *h; |
432 | s16 default_nf; | |
f1dc5600 | 433 | int i, j; |
f2552e28 | 434 | |
bdd196a3 | 435 | ah->caldata->channel = chan->channel; |
fcb9a3de | 436 | ah->caldata->channelFlags = chan->channelFlags; |
20bd2a09 FF |
437 | h = ah->caldata->nfCalHist; |
438 | default_nf = ath9k_hw_get_default_nf(ah, chan); | |
f1dc5600 | 439 | for (i = 0; i < NUM_NF_READINGS; i++) { |
20bd2a09 FF |
440 | h[i].currIndex = 0; |
441 | h[i].privNF = default_nf; | |
442 | h[i].invalidNFcount = AR_PHY_CCA_FILTERWINDOW_LENGTH; | |
f1dc5600 | 443 | for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) { |
20bd2a09 | 444 | h[i].nfCalBuffer[j] = default_nf; |
f1dc5600 S |
445 | } |
446 | } | |
f1dc5600 S |
447 | } |
448 | ||
70cf1533 FF |
449 | |
450 | void ath9k_hw_bstuck_nfcal(struct ath_hw *ah) | |
451 | { | |
452 | struct ath9k_hw_cal_data *caldata = ah->caldata; | |
453 | ||
454 | if (unlikely(!caldata)) | |
455 | return; | |
456 | ||
457 | /* | |
458 | * If beacons are stuck, the most likely cause is interference. | |
459 | * Triggering a noise floor calibration at this point helps the | |
460 | * hardware adapt to a noisy environment much faster. | |
461 | * To ensure that we recover from stuck beacons quickly, let | |
462 | * the baseband update the internal NF value itself, similar to | |
463 | * what is being done after a full reset. | |
464 | */ | |
4b9b42bf | 465 | if (!test_bit(NFCAL_PENDING, &caldata->cal_flags)) |
70cf1533 FF |
466 | ath9k_hw_start_nfcal(ah, true); |
467 | else if (!(REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF)) | |
468 | ath9k_hw_getnf(ah, ah->curchan); | |
469 | ||
4b9b42bf | 470 | set_bit(NFCAL_INTF, &caldata->cal_flags); |
70cf1533 FF |
471 | } |
472 | EXPORT_SYMBOL(ath9k_hw_bstuck_nfcal); | |
473 |