]>
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) { | |
77 | if (nfarray[i] < AR_PHY_CCA_MIN_BAD_VALUE || | |
78 | nfarray[i] > AR_PHY_CCA_MAX_HIGH_VALUE) { | |
79 | h[i].invalidNFcount = ATH9K_NF_CAL_HIST_MAX; | |
80 | } else { | |
81 | h[i].invalidNFcount--; | |
82 | h[i].privNF = nfarray[i]; | |
83 | } | |
84 | } else { | |
85 | h[i].privNF = | |
86 | ath9k_hw_get_nf_hist_mid(h[i].nfCalBuffer); | |
87 | } | |
88 | } | |
89 | return; | |
90 | } | |
91 | ||
b43d59fb LR |
92 | static bool ath9k_hw_get_nf_thresh(struct ath_hw *ah, |
93 | enum ieee80211_band band, | |
94 | int16_t *nft) | |
f1dc5600 | 95 | { |
76061abb LR |
96 | switch (band) { |
97 | case IEEE80211_BAND_5GHZ: | |
f74df6fb | 98 | *nft = (int8_t)ah->eep_ops->get_eeprom(ah, EEP_NFTHRESH_5); |
f1dc5600 | 99 | break; |
76061abb | 100 | case IEEE80211_BAND_2GHZ: |
f74df6fb | 101 | *nft = (int8_t)ah->eep_ops->get_eeprom(ah, EEP_NFTHRESH_2); |
f1dc5600 S |
102 | break; |
103 | default: | |
76061abb | 104 | BUG_ON(1); |
f1dc5600 S |
105 | return false; |
106 | } | |
107 | ||
108 | return true; | |
109 | } | |
110 | ||
795f5e2c LR |
111 | void ath9k_hw_reset_calibration(struct ath_hw *ah, |
112 | struct ath9k_cal_list *currCal) | |
f1dc5600 | 113 | { |
f1dc5600 S |
114 | int i; |
115 | ||
116 | ath9k_hw_setup_calibration(ah, currCal); | |
117 | ||
118 | currCal->calState = CAL_RUNNING; | |
119 | ||
120 | for (i = 0; i < AR5416_MAX_CHAINS; i++) { | |
2660b81a S |
121 | ah->meas0.sign[i] = 0; |
122 | ah->meas1.sign[i] = 0; | |
123 | ah->meas2.sign[i] = 0; | |
124 | ah->meas3.sign[i] = 0; | |
f1dc5600 S |
125 | } |
126 | ||
2660b81a | 127 | ah->cal_samples = 0; |
f1dc5600 S |
128 | } |
129 | ||
c9e27d94 | 130 | /* This is done for the currently configured channel */ |
cbe61d8a | 131 | bool ath9k_hw_reset_calvalid(struct ath_hw *ah) |
f1dc5600 | 132 | { |
c46917bb LR |
133 | struct ath_common *common = ath9k_hw_common(ah); |
134 | struct ieee80211_conf *conf = &common->hw->conf; | |
cbfe9468 | 135 | struct ath9k_cal_list *currCal = ah->cal_list_curr; |
f1dc5600 | 136 | |
2660b81a | 137 | if (!ah->curchan) |
c9e27d94 | 138 | return true; |
f1dc5600 S |
139 | |
140 | if (!AR_SREV_9100(ah) && !AR_SREV_9160_10_OR_LATER(ah)) | |
c9e27d94 | 141 | return true; |
f1dc5600 S |
142 | |
143 | if (currCal == NULL) | |
c9e27d94 | 144 | return true; |
f1dc5600 S |
145 | |
146 | if (currCal->calState != CAL_DONE) { | |
c46917bb LR |
147 | ath_print(common, ATH_DBG_CALIBRATE, |
148 | "Calibration state incorrect, %d\n", | |
149 | currCal->calState); | |
c9e27d94 | 150 | return true; |
f1dc5600 S |
151 | } |
152 | ||
c9e27d94 LR |
153 | if (!ath9k_hw_iscal_supported(ah, currCal->calData->calType)) |
154 | return true; | |
f1dc5600 | 155 | |
c46917bb LR |
156 | ath_print(common, ATH_DBG_CALIBRATE, |
157 | "Resetting Cal %d state for channel %u\n", | |
158 | currCal->calData->calType, conf->channel->center_freq); | |
f1dc5600 | 159 | |
2660b81a | 160 | ah->curchan->CalValid &= ~currCal->calData->calType; |
f1dc5600 S |
161 | currCal->calState = CAL_WAITING; |
162 | ||
c9e27d94 | 163 | return false; |
f1dc5600 | 164 | } |
7322fd19 | 165 | EXPORT_SYMBOL(ath9k_hw_reset_calvalid); |
f1dc5600 | 166 | |
cbe61d8a | 167 | void ath9k_hw_start_nfcal(struct ath_hw *ah) |
f1dc5600 S |
168 | { |
169 | REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, | |
170 | AR_PHY_AGC_CONTROL_ENABLE_NF); | |
171 | REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, | |
172 | AR_PHY_AGC_CONTROL_NO_UPDATE_NF); | |
173 | REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); | |
174 | } | |
175 | ||
cbe61d8a | 176 | int16_t ath9k_hw_getnf(struct ath_hw *ah, |
f1dc5600 S |
177 | struct ath9k_channel *chan) |
178 | { | |
c46917bb | 179 | struct ath_common *common = ath9k_hw_common(ah); |
f1dc5600 S |
180 | int16_t nf, nfThresh; |
181 | int16_t nfarray[NUM_NF_READINGS] = { 0 }; | |
182 | struct ath9k_nfcal_hist *h; | |
76061abb | 183 | struct ieee80211_channel *c = chan->chan; |
f1dc5600 S |
184 | |
185 | chan->channelFlags &= (~CHANNEL_CW_INT); | |
186 | if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) { | |
c46917bb LR |
187 | ath_print(common, ATH_DBG_CALIBRATE, |
188 | "NF did not complete in calibration window\n"); | |
f1dc5600 S |
189 | nf = 0; |
190 | chan->rawNoiseFloor = nf; | |
191 | return chan->rawNoiseFloor; | |
192 | } else { | |
193 | ath9k_hw_do_getnf(ah, nfarray); | |
194 | nf = nfarray[0]; | |
b43d59fb | 195 | if (ath9k_hw_get_nf_thresh(ah, c->band, &nfThresh) |
f1dc5600 | 196 | && nf > nfThresh) { |
c46917bb LR |
197 | ath_print(common, ATH_DBG_CALIBRATE, |
198 | "noise floor failed detected; " | |
199 | "detected %d, threshold %d\n", | |
200 | nf, nfThresh); | |
f1dc5600 S |
201 | chan->channelFlags |= CHANNEL_CW_INT; |
202 | } | |
203 | } | |
204 | ||
f1dc5600 | 205 | h = ah->nfCalHist; |
f1dc5600 S |
206 | |
207 | ath9k_hw_update_nfcal_hist_buffer(h, nfarray); | |
208 | chan->rawNoiseFloor = h[0].privNF; | |
209 | ||
210 | return chan->rawNoiseFloor; | |
211 | } | |
212 | ||
cbe61d8a | 213 | void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah) |
f1dc5600 S |
214 | { |
215 | int i, j; | |
a59b5a5e SB |
216 | s16 noise_floor; |
217 | ||
218 | if (AR_SREV_9280(ah)) | |
219 | noise_floor = AR_PHY_CCA_MAX_AR9280_GOOD_VALUE; | |
6398dc03 | 220 | else if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) |
a59b5a5e | 221 | noise_floor = AR_PHY_CCA_MAX_AR9285_GOOD_VALUE; |
6170cd5c VN |
222 | else if (AR_SREV_9287(ah)) |
223 | noise_floor = AR_PHY_CCA_MAX_AR9287_GOOD_VALUE; | |
a59b5a5e SB |
224 | else |
225 | noise_floor = AR_PHY_CCA_MAX_AR5416_GOOD_VALUE; | |
f1dc5600 S |
226 | |
227 | for (i = 0; i < NUM_NF_READINGS; i++) { | |
228 | ah->nfCalHist[i].currIndex = 0; | |
a59b5a5e | 229 | ah->nfCalHist[i].privNF = noise_floor; |
f1dc5600 S |
230 | ah->nfCalHist[i].invalidNFcount = |
231 | AR_PHY_CCA_FILTERWINDOW_LENGTH; | |
232 | for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) { | |
a59b5a5e | 233 | ah->nfCalHist[i].nfCalBuffer[j] = noise_floor; |
f1dc5600 S |
234 | } |
235 | } | |
f1dc5600 S |
236 | } |
237 | ||
cbe61d8a | 238 | s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan) |
f1dc5600 | 239 | { |
f1dc5600 S |
240 | s16 nf; |
241 | ||
5f8e077c | 242 | if (chan->rawNoiseFloor == 0) |
e56db718 LR |
243 | nf = -96; |
244 | else | |
5f8e077c | 245 | nf = chan->rawNoiseFloor; |
f1dc5600 S |
246 | |
247 | if (!ath9k_hw_nf_in_range(ah, nf)) | |
248 | nf = ATH_DEFAULT_NOISE_FLOOR; | |
249 | ||
250 | return nf; | |
251 | } | |
7322fd19 | 252 | EXPORT_SYMBOL(ath9k_hw_getchan_noise); |