diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/calib.c')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/calib.c | 211 |
1 files changed, 142 insertions, 69 deletions
diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index 45208690c0ec..a1250c586e40 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (c) 2008-2009 Atheros Communications Inc. | 2 | * Copyright (c) 2008-2011 Atheros Communications Inc. |
3 | * | 3 | * |
4 | * Permission to use, copy, modify, and/or distribute this software for any | 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 | 5 | * purpose with or without fee is hereby granted, provided that the above |
@@ -19,8 +19,7 @@ | |||
19 | 19 | ||
20 | /* Common calibration code */ | 20 | /* Common calibration code */ |
21 | 21 | ||
22 | /* We can tune this as we go by monitoring really low values */ | 22 | #define ATH9K_NF_TOO_HIGH -60 |
23 | #define ATH9K_NF_TOO_LOW -60 | ||
24 | 23 | ||
25 | static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer) | 24 | static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer) |
26 | { | 25 | { |
@@ -45,12 +44,46 @@ static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer) | |||
45 | return nfval; | 44 | return nfval; |
46 | } | 45 | } |
47 | 46 | ||
48 | static void ath9k_hw_update_nfcal_hist_buffer(struct ath9k_nfcal_hist *h, | 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 | |||
66 | |||
67 | static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah, | ||
68 | struct ath9k_hw_cal_data *cal, | ||
49 | int16_t *nfarray) | 69 | int16_t *nfarray) |
50 | { | 70 | { |
71 | struct ath_common *common = ath9k_hw_common(ah); | ||
72 | struct ieee80211_conf *conf = &common->hw->conf; | ||
73 | struct ath_nf_limits *limit; | ||
74 | struct ath9k_nfcal_hist *h; | ||
75 | bool high_nf_mid = false; | ||
76 | u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask; | ||
51 | int i; | 77 | int i; |
52 | 78 | ||
79 | h = cal->nfCalHist; | ||
80 | limit = ath9k_hw_get_nf_limits(ah, ah->curchan); | ||
81 | |||
53 | for (i = 0; i < NUM_NF_READINGS; i++) { | 82 | for (i = 0; i < NUM_NF_READINGS; i++) { |
83 | if (!(chainmask & (1 << i)) || | ||
84 | ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf))) | ||
85 | continue; | ||
86 | |||
54 | h[i].nfCalBuffer[h[i].currIndex] = nfarray[i]; | 87 | h[i].nfCalBuffer[h[i].currIndex] = nfarray[i]; |
55 | 88 | ||
56 | if (++h[i].currIndex >= ATH9K_NF_CAL_HIST_MAX) | 89 | if (++h[i].currIndex >= ATH9K_NF_CAL_HIST_MAX) |
@@ -63,7 +96,39 @@ static void ath9k_hw_update_nfcal_hist_buffer(struct ath9k_nfcal_hist *h, | |||
63 | h[i].privNF = | 96 | h[i].privNF = |
64 | ath9k_hw_get_nf_hist_mid(h[i].nfCalBuffer); | 97 | ath9k_hw_get_nf_hist_mid(h[i].nfCalBuffer); |
65 | } | 98 | } |
99 | |||
100 | if (!h[i].privNF) | ||
101 | continue; | ||
102 | |||
103 | if (h[i].privNF > limit->max) { | ||
104 | high_nf_mid = true; | ||
105 | |||
106 | ath_dbg(common, ATH_DBG_CALIBRATE, | ||
107 | "NFmid[%d] (%d) > MAX (%d), %s\n", | ||
108 | i, h[i].privNF, limit->max, | ||
109 | (cal->nfcal_interference ? | ||
110 | "not corrected (due to interference)" : | ||
111 | "correcting to MAX")); | ||
112 | |||
113 | /* | ||
114 | * Normally we limit the average noise floor by the | ||
115 | * hardware specific maximum here. However if we have | ||
116 | * encountered stuck beacons because of interference, | ||
117 | * we bypass this limit here in order to better deal | ||
118 | * with our environment. | ||
119 | */ | ||
120 | if (!cal->nfcal_interference) | ||
121 | h[i].privNF = limit->max; | ||
122 | } | ||
66 | } | 123 | } |
124 | |||
125 | /* | ||
126 | * If the noise floor seems normal for all chains, assume that | ||
127 | * there is no significant interference in the environment anymore. | ||
128 | * Re-enable the enforcement of the NF maximum again. | ||
129 | */ | ||
130 | if (!high_nf_mid) | ||
131 | cal->nfcal_interference = false; | ||
67 | } | 132 | } |
68 | 133 | ||
69 | static bool ath9k_hw_get_nf_thresh(struct ath_hw *ah, | 134 | static bool ath9k_hw_get_nf_thresh(struct ath_hw *ah, |
@@ -104,19 +169,6 @@ void ath9k_hw_reset_calibration(struct ath_hw *ah, | |||
104 | ah->cal_samples = 0; | 169 | ah->cal_samples = 0; |
105 | } | 170 | } |
106 | 171 | ||
107 | static s16 ath9k_hw_get_default_nf(struct ath_hw *ah, | ||
108 | struct ath9k_channel *chan) | ||
109 | { | ||
110 | struct ath_nf_limits *limit; | ||
111 | |||
112 | if (!chan || IS_CHAN_2GHZ(chan)) | ||
113 | limit = &ah->nf_2g; | ||
114 | else | ||
115 | limit = &ah->nf_5g; | ||
116 | |||
117 | return limit->nominal; | ||
118 | } | ||
119 | |||
120 | /* This is done for the currently configured channel */ | 172 | /* This is done for the currently configured channel */ |
121 | bool ath9k_hw_reset_calvalid(struct ath_hw *ah) | 173 | bool ath9k_hw_reset_calvalid(struct ath_hw *ah) |
122 | { | 174 | { |
@@ -134,18 +186,18 @@ bool ath9k_hw_reset_calvalid(struct ath_hw *ah) | |||
134 | return true; | 186 | return true; |
135 | 187 | ||
136 | if (currCal->calState != CAL_DONE) { | 188 | if (currCal->calState != CAL_DONE) { |
137 | ath_print(common, ATH_DBG_CALIBRATE, | 189 | ath_dbg(common, ATH_DBG_CALIBRATE, |
138 | "Calibration state incorrect, %d\n", | 190 | "Calibration state incorrect, %d\n", |
139 | currCal->calState); | 191 | currCal->calState); |
140 | return true; | 192 | return true; |
141 | } | 193 | } |
142 | 194 | ||
143 | if (!ath9k_hw_iscal_supported(ah, currCal->calData->calType)) | 195 | if (!(ah->supp_cals & currCal->calData->calType)) |
144 | return true; | 196 | return true; |
145 | 197 | ||
146 | ath_print(common, ATH_DBG_CALIBRATE, | 198 | ath_dbg(common, ATH_DBG_CALIBRATE, |
147 | "Resetting Cal %d state for channel %u\n", | 199 | "Resetting Cal %d state for channel %u\n", |
148 | currCal->calData->calType, conf->channel->center_freq); | 200 | currCal->calData->calType, conf->channel->center_freq); |
149 | 201 | ||
150 | ah->caldata->CalValid &= ~currCal->calData->calType; | 202 | ah->caldata->CalValid &= ~currCal->calData->calType; |
151 | currCal->calState = CAL_WAITING; | 203 | currCal->calState = CAL_WAITING; |
@@ -179,6 +231,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) | |||
179 | int32_t val; | 231 | int32_t val; |
180 | u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask; | 232 | u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask; |
181 | struct ath_common *common = ath9k_hw_common(ah); | 233 | struct ath_common *common = ath9k_hw_common(ah); |
234 | struct ieee80211_conf *conf = &common->hw->conf; | ||
182 | s16 default_nf = ath9k_hw_get_default_nf(ah, chan); | 235 | s16 default_nf = ath9k_hw_get_default_nf(ah, chan); |
183 | 236 | ||
184 | if (ah->caldata) | 237 | if (ah->caldata) |
@@ -188,6 +241,9 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) | |||
188 | if (chainmask & (1 << i)) { | 241 | if (chainmask & (1 << i)) { |
189 | s16 nfval; | 242 | s16 nfval; |
190 | 243 | ||
244 | if ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf)) | ||
245 | continue; | ||
246 | |||
191 | if (h) | 247 | if (h) |
192 | nfval = h[i].privNF; | 248 | nfval = h[i].privNF; |
193 | else | 249 | else |
@@ -216,7 +272,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) | |||
216 | * since 250us often results in NF load timeout and causes deaf | 272 | * since 250us often results in NF load timeout and causes deaf |
217 | * condition during stress testing 12/12/2009 | 273 | * condition during stress testing 12/12/2009 |
218 | */ | 274 | */ |
219 | for (j = 0; j < 1000; j++) { | 275 | for (j = 0; j < 10000; j++) { |
220 | if ((REG_READ(ah, AR_PHY_AGC_CONTROL) & | 276 | if ((REG_READ(ah, AR_PHY_AGC_CONTROL) & |
221 | AR_PHY_AGC_CONTROL_NF) == 0) | 277 | AR_PHY_AGC_CONTROL_NF) == 0) |
222 | break; | 278 | break; |
@@ -232,10 +288,10 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) | |||
232 | * here, the baseband nf cal will just be capped by our present | 288 | * here, the baseband nf cal will just be capped by our present |
233 | * noisefloor until the next calibration timer. | 289 | * noisefloor until the next calibration timer. |
234 | */ | 290 | */ |
235 | if (j == 1000) { | 291 | if (j == 10000) { |
236 | ath_print(common, ATH_DBG_ANY, "Timeout while waiting for nf " | 292 | ath_dbg(common, ATH_DBG_ANY, |
237 | "to load: AR_PHY_AGC_CONTROL=0x%x\n", | 293 | "Timeout while waiting for nf to load: AR_PHY_AGC_CONTROL=0x%x\n", |
238 | REG_READ(ah, AR_PHY_AGC_CONTROL)); | 294 | REG_READ(ah, AR_PHY_AGC_CONTROL)); |
239 | return; | 295 | return; |
240 | } | 296 | } |
241 | 297 | ||
@@ -247,6 +303,9 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) | |||
247 | ENABLE_REGWRITE_BUFFER(ah); | 303 | ENABLE_REGWRITE_BUFFER(ah); |
248 | for (i = 0; i < NUM_NF_READINGS; i++) { | 304 | for (i = 0; i < NUM_NF_READINGS; i++) { |
249 | if (chainmask & (1 << i)) { | 305 | if (chainmask & (1 << i)) { |
306 | if ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf)) | ||
307 | continue; | ||
308 | |||
250 | val = REG_READ(ah, ah->nf_regs[i]); | 309 | val = REG_READ(ah, ah->nf_regs[i]); |
251 | val &= 0xFFFFFE00; | 310 | val &= 0xFFFFFE00; |
252 | val |= (((u32) (-50) << 1) & 0x1ff); | 311 | val |= (((u32) (-50) << 1) & 0x1ff); |
@@ -254,7 +313,6 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) | |||
254 | } | 313 | } |
255 | } | 314 | } |
256 | REGWRITE_BUFFER_FLUSH(ah); | 315 | REGWRITE_BUFFER_FLUSH(ah); |
257 | DISABLE_REGWRITE_BUFFER(ah); | ||
258 | } | 316 | } |
259 | 317 | ||
260 | 318 | ||
@@ -273,19 +331,19 @@ static void ath9k_hw_nf_sanitize(struct ath_hw *ah, s16 *nf) | |||
273 | if (!nf[i]) | 331 | if (!nf[i]) |
274 | continue; | 332 | continue; |
275 | 333 | ||
276 | ath_print(common, ATH_DBG_CALIBRATE, | 334 | ath_dbg(common, ATH_DBG_CALIBRATE, |
277 | "NF calibrated [%s] [chain %d] is %d\n", | 335 | "NF calibrated [%s] [chain %d] is %d\n", |
278 | (i >= 3 ? "ext" : "ctl"), i % 3, nf[i]); | 336 | (i >= 3 ? "ext" : "ctl"), i % 3, nf[i]); |
279 | 337 | ||
280 | if (nf[i] > limit->max) { | 338 | if (nf[i] > ATH9K_NF_TOO_HIGH) { |
281 | ath_print(common, ATH_DBG_CALIBRATE, | 339 | ath_dbg(common, ATH_DBG_CALIBRATE, |
282 | "NF[%d] (%d) > MAX (%d), correcting to MAX", | 340 | "NF[%d] (%d) > MAX (%d), correcting to MAX\n", |
283 | i, nf[i], limit->max); | 341 | i, nf[i], ATH9K_NF_TOO_HIGH); |
284 | nf[i] = limit->max; | 342 | nf[i] = limit->max; |
285 | } else if (nf[i] < limit->min) { | 343 | } else if (nf[i] < limit->min) { |
286 | ath_print(common, ATH_DBG_CALIBRATE, | 344 | ath_dbg(common, ATH_DBG_CALIBRATE, |
287 | "NF[%d] (%d) < MIN (%d), correcting to NOM", | 345 | "NF[%d] (%d) < MIN (%d), correcting to NOM\n", |
288 | i, nf[i], limit->min); | 346 | i, nf[i], limit->min); |
289 | nf[i] = limit->nominal; | 347 | nf[i] = limit->nominal; |
290 | } | 348 | } |
291 | } | 349 | } |
@@ -300,34 +358,33 @@ bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan) | |||
300 | struct ieee80211_channel *c = chan->chan; | 358 | struct ieee80211_channel *c = chan->chan; |
301 | struct ath9k_hw_cal_data *caldata = ah->caldata; | 359 | struct ath9k_hw_cal_data *caldata = ah->caldata; |
302 | 360 | ||
303 | if (!caldata) | ||
304 | return false; | ||
305 | |||
306 | chan->channelFlags &= (~CHANNEL_CW_INT); | 361 | chan->channelFlags &= (~CHANNEL_CW_INT); |
307 | if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) { | 362 | if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) { |
308 | ath_print(common, ATH_DBG_CALIBRATE, | 363 | ath_dbg(common, ATH_DBG_CALIBRATE, |
309 | "NF did not complete in calibration window\n"); | 364 | "NF did not complete in calibration window\n"); |
310 | nf = 0; | 365 | return false; |
311 | caldata->rawNoiseFloor = nf; | 366 | } |
367 | |||
368 | ath9k_hw_do_getnf(ah, nfarray); | ||
369 | ath9k_hw_nf_sanitize(ah, nfarray); | ||
370 | nf = nfarray[0]; | ||
371 | if (ath9k_hw_get_nf_thresh(ah, c->band, &nfThresh) | ||
372 | && nf > nfThresh) { | ||
373 | ath_dbg(common, ATH_DBG_CALIBRATE, | ||
374 | "noise floor failed detected; detected %d, threshold %d\n", | ||
375 | nf, nfThresh); | ||
376 | chan->channelFlags |= CHANNEL_CW_INT; | ||
377 | } | ||
378 | |||
379 | if (!caldata) { | ||
380 | chan->noisefloor = nf; | ||
312 | return false; | 381 | return false; |
313 | } else { | ||
314 | ath9k_hw_do_getnf(ah, nfarray); | ||
315 | ath9k_hw_nf_sanitize(ah, nfarray); | ||
316 | nf = nfarray[0]; | ||
317 | if (ath9k_hw_get_nf_thresh(ah, c->band, &nfThresh) | ||
318 | && nf > nfThresh) { | ||
319 | ath_print(common, ATH_DBG_CALIBRATE, | ||
320 | "noise floor failed detected; " | ||
321 | "detected %d, threshold %d\n", | ||
322 | nf, nfThresh); | ||
323 | chan->channelFlags |= CHANNEL_CW_INT; | ||
324 | } | ||
325 | } | 382 | } |
326 | 383 | ||
327 | h = caldata->nfCalHist; | 384 | h = caldata->nfCalHist; |
328 | caldata->nfcal_pending = false; | 385 | caldata->nfcal_pending = false; |
329 | ath9k_hw_update_nfcal_hist_buffer(h, nfarray); | 386 | ath9k_hw_update_nfcal_hist_buffer(ah, caldata, nfarray); |
330 | caldata->rawNoiseFloor = h[0].privNF; | 387 | chan->noisefloor = h[0].privNF; |
331 | return true; | 388 | return true; |
332 | } | 389 | } |
333 | 390 | ||
@@ -338,9 +395,8 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah, | |||
338 | s16 default_nf; | 395 | s16 default_nf; |
339 | int i, j; | 396 | int i, j; |
340 | 397 | ||
341 | if (!ah->caldata) | 398 | ah->caldata->channel = chan->channel; |
342 | return; | 399 | ah->caldata->channelFlags = chan->channelFlags & ~CHANNEL_CW_INT; |
343 | |||
344 | h = ah->caldata->nfCalHist; | 400 | h = ah->caldata->nfCalHist; |
345 | default_nf = ath9k_hw_get_default_nf(ah, chan); | 401 | default_nf = ath9k_hw_get_default_nf(ah, chan); |
346 | for (i = 0; i < NUM_NF_READINGS; i++) { | 402 | for (i = 0; i < NUM_NF_READINGS; i++) { |
@@ -353,11 +409,28 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah, | |||
353 | } | 409 | } |
354 | } | 410 | } |
355 | 411 | ||
356 | s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan) | 412 | |
413 | void ath9k_hw_bstuck_nfcal(struct ath_hw *ah) | ||
357 | { | 414 | { |
358 | if (!ah->caldata || !ah->caldata->rawNoiseFloor) | 415 | struct ath9k_hw_cal_data *caldata = ah->caldata; |
359 | return ath9k_hw_get_default_nf(ah, chan); | 416 | |
417 | if (unlikely(!caldata)) | ||
418 | return; | ||
360 | 419 | ||
361 | return ah->caldata->rawNoiseFloor; | 420 | /* |
421 | * If beacons are stuck, the most likely cause is interference. | ||
422 | * Triggering a noise floor calibration at this point helps the | ||
423 | * hardware adapt to a noisy environment much faster. | ||
424 | * To ensure that we recover from stuck beacons quickly, let | ||
425 | * the baseband update the internal NF value itself, similar to | ||
426 | * what is being done after a full reset. | ||
427 | */ | ||
428 | if (!caldata->nfcal_pending) | ||
429 | ath9k_hw_start_nfcal(ah, true); | ||
430 | else if (!(REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF)) | ||
431 | ath9k_hw_getnf(ah, ah->curchan); | ||
432 | |||
433 | caldata->nfcal_interference = true; | ||
362 | } | 434 | } |
363 | EXPORT_SYMBOL(ath9k_hw_getchan_noise); | 435 | EXPORT_SYMBOL(ath9k_hw_bstuck_nfcal); |
436 | |||