]>
Commit | Line | Data |
---|---|---|
f1dc5600 | 1 | /* |
cee075a2 | 2 | * Copyright (c) 2008-2009 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" |
f1dc5600 | 19 | |
795f5e2c LR |
20 | /* Common calibration code */ |
21 | ||
f1dc5600 S |
22 | /* We can tune this as we go by monitoring really low values */ |
23 | #define ATH9K_NF_TOO_LOW -60 | |
24 | ||
25 | /* AR5416 may return very high value (like -31 dBm), in those cases the nf | |
26 | * is incorrect and we should use the static NF value. Later we can try to | |
27 | * find out why they are reporting these values */ | |
28 | ||
cbe61d8a | 29 | static bool ath9k_hw_nf_in_range(struct ath_hw *ah, s16 nf) |
f1dc5600 S |
30 | { |
31 | if (nf > ATH9K_NF_TOO_LOW) { | |
c46917bb LR |
32 | ath_print(ath9k_hw_common(ah), ATH_DBG_CALIBRATE, |
33 | "noise floor value detected (%d) is " | |
34 | "lower than what we think is a " | |
35 | "reasonable value (%d)\n", | |
36 | nf, ATH9K_NF_TOO_LOW); | |
f1dc5600 S |
37 | return false; |
38 | } | |
39 | return true; | |
40 | } | |
41 | ||
42 | static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer) | |
43 | { | |
44 | int16_t nfval; | |
45 | int16_t sort[ATH9K_NF_CAL_HIST_MAX]; | |
46 | int i, j; | |
47 | ||
48 | for (i = 0; i < ATH9K_NF_CAL_HIST_MAX; i++) | |
49 | sort[i] = nfCalBuffer[i]; | |
50 | ||
51 | for (i = 0; i < ATH9K_NF_CAL_HIST_MAX - 1; i++) { | |
52 | for (j = 1; j < ATH9K_NF_CAL_HIST_MAX - i; j++) { | |
53 | if (sort[j] > sort[j - 1]) { | |
54 | nfval = sort[j]; | |
55 | sort[j] = sort[j - 1]; | |
56 | sort[j - 1] = nfval; | |
57 | } | |
58 | } | |
59 | } | |
60 | nfval = sort[(ATH9K_NF_CAL_HIST_MAX - 1) >> 1]; | |
61 | ||
62 | return nfval; | |
63 | } | |
64 | ||
65 | static void ath9k_hw_update_nfcal_hist_buffer(struct ath9k_nfcal_hist *h, | |
66 | int16_t *nfarray) | |
67 | { | |
68 | int i; | |
69 | ||
70 | for (i = 0; i < NUM_NF_READINGS; i++) { | |
71 | h[i].nfCalBuffer[h[i].currIndex] = nfarray[i]; | |
72 | ||
73 | if (++h[i].currIndex >= ATH9K_NF_CAL_HIST_MAX) | |
74 | h[i].currIndex = 0; | |
75 | ||
76 | if (h[i].invalidNFcount > 0) { | |
f2552e28 FF |
77 | h[i].invalidNFcount--; |
78 | h[i].privNF = nfarray[i]; | |
f1dc5600 S |
79 | } else { |
80 | h[i].privNF = | |
81 | ath9k_hw_get_nf_hist_mid(h[i].nfCalBuffer); | |
82 | } | |
83 | } | |
f1dc5600 S |
84 | } |
85 | ||
b43d59fb LR |
86 | static bool ath9k_hw_get_nf_thresh(struct ath_hw *ah, |
87 | enum ieee80211_band band, | |
88 | int16_t *nft) | |
f1dc5600 | 89 | { |
76061abb LR |
90 | switch (band) { |
91 | case IEEE80211_BAND_5GHZ: | |
f74df6fb | 92 | *nft = (int8_t)ah->eep_ops->get_eeprom(ah, EEP_NFTHRESH_5); |
f1dc5600 | 93 | break; |
76061abb | 94 | case IEEE80211_BAND_2GHZ: |
f74df6fb | 95 | *nft = (int8_t)ah->eep_ops->get_eeprom(ah, EEP_NFTHRESH_2); |
f1dc5600 S |
96 | break; |
97 | default: | |
76061abb | 98 | BUG_ON(1); |
f1dc5600 S |
99 | return false; |
100 | } | |
101 | ||
102 | return true; | |
103 | } | |
104 | ||
795f5e2c LR |
105 | void ath9k_hw_reset_calibration(struct ath_hw *ah, |
106 | struct ath9k_cal_list *currCal) | |
f1dc5600 | 107 | { |
f1dc5600 S |
108 | int i; |
109 | ||
110 | ath9k_hw_setup_calibration(ah, currCal); | |
111 | ||
112 | currCal->calState = CAL_RUNNING; | |
113 | ||
114 | for (i = 0; i < AR5416_MAX_CHAINS; i++) { | |
2660b81a S |
115 | ah->meas0.sign[i] = 0; |
116 | ah->meas1.sign[i] = 0; | |
117 | ah->meas2.sign[i] = 0; | |
118 | ah->meas3.sign[i] = 0; | |
f1dc5600 S |
119 | } |
120 | ||
2660b81a | 121 | ah->cal_samples = 0; |
f1dc5600 S |
122 | } |
123 | ||
c9e27d94 | 124 | /* This is done for the currently configured channel */ |
cbe61d8a | 125 | bool ath9k_hw_reset_calvalid(struct ath_hw *ah) |
f1dc5600 | 126 | { |
c46917bb LR |
127 | struct ath_common *common = ath9k_hw_common(ah); |
128 | struct ieee80211_conf *conf = &common->hw->conf; | |
cbfe9468 | 129 | struct ath9k_cal_list *currCal = ah->cal_list_curr; |
f1dc5600 | 130 | |
2660b81a | 131 | if (!ah->curchan) |
c9e27d94 | 132 | return true; |
f1dc5600 S |
133 | |
134 | if (!AR_SREV_9100(ah) && !AR_SREV_9160_10_OR_LATER(ah)) | |
c9e27d94 | 135 | return true; |
f1dc5600 S |
136 | |
137 | if (currCal == NULL) | |
c9e27d94 | 138 | return true; |
f1dc5600 S |
139 | |
140 | if (currCal->calState != CAL_DONE) { | |
c46917bb LR |
141 | ath_print(common, ATH_DBG_CALIBRATE, |
142 | "Calibration state incorrect, %d\n", | |
143 | currCal->calState); | |
c9e27d94 | 144 | return true; |
f1dc5600 S |
145 | } |
146 | ||
c9e27d94 LR |
147 | if (!ath9k_hw_iscal_supported(ah, currCal->calData->calType)) |
148 | return true; | |
f1dc5600 | 149 | |
c46917bb LR |
150 | ath_print(common, ATH_DBG_CALIBRATE, |
151 | "Resetting Cal %d state for channel %u\n", | |
152 | currCal->calData->calType, conf->channel->center_freq); | |
f1dc5600 | 153 | |
2660b81a | 154 | ah->curchan->CalValid &= ~currCal->calData->calType; |
f1dc5600 S |
155 | currCal->calState = CAL_WAITING; |
156 | ||
c9e27d94 | 157 | return false; |
f1dc5600 | 158 | } |
7322fd19 | 159 | EXPORT_SYMBOL(ath9k_hw_reset_calvalid); |
f1dc5600 | 160 | |
cbe61d8a | 161 | void ath9k_hw_start_nfcal(struct ath_hw *ah) |
f1dc5600 S |
162 | { |
163 | REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, | |
164 | AR_PHY_AGC_CONTROL_ENABLE_NF); | |
165 | REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, | |
166 | AR_PHY_AGC_CONTROL_NO_UPDATE_NF); | |
167 | REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); | |
168 | } | |
169 | ||
f2552e28 FF |
170 | static void ath9k_hw_nf_sanitize(struct ath_hw *ah, s16 *nf) |
171 | { | |
172 | struct ath_common *common = ath9k_hw_common(ah); | |
173 | struct ath_nf_limits *limit; | |
174 | int i; | |
175 | ||
176 | if (IS_CHAN_2GHZ(ah->curchan)) | |
177 | limit = &ah->nf_2g; | |
178 | else | |
179 | limit = &ah->nf_5g; | |
180 | ||
181 | for (i = 0; i < NUM_NF_READINGS; i++) { | |
182 | if (!nf[i]) | |
183 | continue; | |
184 | ||
54bd5006 FF |
185 | ath_print(common, ATH_DBG_CALIBRATE, |
186 | "NF calibrated [%s] [chain %d] is %d\n", | |
187 | (i > 3 ? "ext" : "ctl"), i % 3, nf[i]); | |
188 | ||
f2552e28 FF |
189 | if (nf[i] > limit->max) { |
190 | ath_print(common, ATH_DBG_CALIBRATE, | |
191 | "NF[%d] (%d) > MAX (%d), correcting to MAX", | |
192 | i, nf[i], limit->max); | |
193 | nf[i] = limit->max; | |
194 | } else if (nf[i] < limit->min) { | |
195 | ath_print(common, ATH_DBG_CALIBRATE, | |
196 | "NF[%d] (%d) < MIN (%d), correcting to NOM", | |
197 | i, nf[i], limit->min); | |
198 | nf[i] = limit->nominal; | |
199 | } | |
200 | } | |
201 | } | |
202 | ||
cbe61d8a | 203 | int16_t ath9k_hw_getnf(struct ath_hw *ah, |
f1dc5600 S |
204 | struct ath9k_channel *chan) |
205 | { | |
c46917bb | 206 | struct ath_common *common = ath9k_hw_common(ah); |
f1dc5600 S |
207 | int16_t nf, nfThresh; |
208 | int16_t nfarray[NUM_NF_READINGS] = { 0 }; | |
209 | struct ath9k_nfcal_hist *h; | |
76061abb | 210 | struct ieee80211_channel *c = chan->chan; |
f1dc5600 S |
211 | |
212 | chan->channelFlags &= (~CHANNEL_CW_INT); | |
213 | if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) { | |
c46917bb LR |
214 | ath_print(common, ATH_DBG_CALIBRATE, |
215 | "NF did not complete in calibration window\n"); | |
f1dc5600 S |
216 | nf = 0; |
217 | chan->rawNoiseFloor = nf; | |
218 | return chan->rawNoiseFloor; | |
219 | } else { | |
220 | ath9k_hw_do_getnf(ah, nfarray); | |
f2552e28 | 221 | ath9k_hw_nf_sanitize(ah, nfarray); |
f1dc5600 | 222 | nf = nfarray[0]; |
b43d59fb | 223 | if (ath9k_hw_get_nf_thresh(ah, c->band, &nfThresh) |
f1dc5600 | 224 | && nf > nfThresh) { |
c46917bb LR |
225 | ath_print(common, ATH_DBG_CALIBRATE, |
226 | "noise floor failed detected; " | |
227 | "detected %d, threshold %d\n", | |
228 | nf, nfThresh); | |
f1dc5600 S |
229 | chan->channelFlags |= CHANNEL_CW_INT; |
230 | } | |
231 | } | |
232 | ||
f1dc5600 | 233 | h = ah->nfCalHist; |
f1dc5600 S |
234 | |
235 | ath9k_hw_update_nfcal_hist_buffer(h, nfarray); | |
236 | chan->rawNoiseFloor = h[0].privNF; | |
237 | ||
238 | return chan->rawNoiseFloor; | |
239 | } | |
240 | ||
cbe61d8a | 241 | void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah) |
f1dc5600 | 242 | { |
f2552e28 | 243 | struct ath_nf_limits *limit; |
f1dc5600 | 244 | int i, j; |
f2552e28 FF |
245 | |
246 | if (!ah->curchan || IS_CHAN_2GHZ(ah->curchan)) | |
247 | limit = &ah->nf_2g; | |
a59b5a5e | 248 | else |
f2552e28 | 249 | limit = &ah->nf_5g; |
f1dc5600 S |
250 | |
251 | for (i = 0; i < NUM_NF_READINGS; i++) { | |
252 | ah->nfCalHist[i].currIndex = 0; | |
f2552e28 | 253 | ah->nfCalHist[i].privNF = limit->nominal; |
f1dc5600 S |
254 | ah->nfCalHist[i].invalidNFcount = |
255 | AR_PHY_CCA_FILTERWINDOW_LENGTH; | |
256 | for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) { | |
f2552e28 | 257 | ah->nfCalHist[i].nfCalBuffer[j] = limit->nominal; |
f1dc5600 S |
258 | } |
259 | } | |
f1dc5600 S |
260 | } |
261 | ||
cbe61d8a | 262 | s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan) |
f1dc5600 | 263 | { |
f1dc5600 S |
264 | s16 nf; |
265 | ||
5f8e077c | 266 | if (chan->rawNoiseFloor == 0) |
e56db718 LR |
267 | nf = -96; |
268 | else | |
5f8e077c | 269 | nf = chan->rawNoiseFloor; |
f1dc5600 S |
270 | |
271 | if (!ath9k_hw_nf_in_range(ah, nf)) | |
272 | nf = ATH_DEFAULT_NOISE_FLOOR; | |
273 | ||
274 | return nf; | |
275 | } | |
7322fd19 | 276 | EXPORT_SYMBOL(ath9k_hw_getchan_noise); |