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