blob: 18b5c0dcc1fc571b2c38ef09a4fc78ba71d07ada [file] [log] [blame]
Sujithf1dc5602008-10-29 10:16:30 +05301/*
Sujithcee075a2009-03-13 09:07:23 +05302 * Copyright (c) 2008-2009 Atheros Communications Inc.
Sujithf1dc5602008-10-29 10:16:30 +05303 *
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
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -070017#include "hw.h"
Felix Fietkau641d9922010-04-15 17:38:49 -040018#include "hw-ops.h"
Sujithf1dc5602008-10-29 10:16:30 +053019
Luis R. Rodriguez795f5e22010-04-15 17:39:00 -040020/* Common calibration code */
21
Sujithf1dc5602008-10-29 10:16:30 +053022/* We can tune this as we go by monitoring really low values */
23#define ATH9K_NF_TOO_LOW -60
24
Sujithf1dc5602008-10-29 10:16:30 +053025static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer)
26{
27 int16_t nfval;
28 int16_t sort[ATH9K_NF_CAL_HIST_MAX];
29 int i, j;
30
31 for (i = 0; i < ATH9K_NF_CAL_HIST_MAX; i++)
32 sort[i] = nfCalBuffer[i];
33
34 for (i = 0; i < ATH9K_NF_CAL_HIST_MAX - 1; i++) {
35 for (j = 1; j < ATH9K_NF_CAL_HIST_MAX - i; j++) {
36 if (sort[j] > sort[j - 1]) {
37 nfval = sort[j];
38 sort[j] = sort[j - 1];
39 sort[j - 1] = nfval;
40 }
41 }
42 }
43 nfval = sort[(ATH9K_NF_CAL_HIST_MAX - 1) >> 1];
44
45 return nfval;
46}
47
48static void ath9k_hw_update_nfcal_hist_buffer(struct ath9k_nfcal_hist *h,
49 int16_t *nfarray)
50{
51 int i;
52
53 for (i = 0; i < NUM_NF_READINGS; i++) {
54 h[i].nfCalBuffer[h[i].currIndex] = nfarray[i];
55
56 if (++h[i].currIndex >= ATH9K_NF_CAL_HIST_MAX)
57 h[i].currIndex = 0;
58
59 if (h[i].invalidNFcount > 0) {
Felix Fietkauf2552e22010-07-02 00:09:50 +020060 h[i].invalidNFcount--;
61 h[i].privNF = nfarray[i];
Sujithf1dc5602008-10-29 10:16:30 +053062 } else {
63 h[i].privNF =
64 ath9k_hw_get_nf_hist_mid(h[i].nfCalBuffer);
65 }
66 }
Sujithf1dc5602008-10-29 10:16:30 +053067}
68
Luis R. Rodriguezb43d59f2010-04-15 17:38:58 -040069static bool ath9k_hw_get_nf_thresh(struct ath_hw *ah,
70 enum ieee80211_band band,
71 int16_t *nft)
Sujithf1dc5602008-10-29 10:16:30 +053072{
Luis R. Rodriguez76061ab2008-12-23 15:58:41 -080073 switch (band) {
74 case IEEE80211_BAND_5GHZ:
Sujithf74df6f2009-02-09 13:27:24 +053075 *nft = (int8_t)ah->eep_ops->get_eeprom(ah, EEP_NFTHRESH_5);
Sujithf1dc5602008-10-29 10:16:30 +053076 break;
Luis R. Rodriguez76061ab2008-12-23 15:58:41 -080077 case IEEE80211_BAND_2GHZ:
Sujithf74df6f2009-02-09 13:27:24 +053078 *nft = (int8_t)ah->eep_ops->get_eeprom(ah, EEP_NFTHRESH_2);
Sujithf1dc5602008-10-29 10:16:30 +053079 break;
80 default:
Luis R. Rodriguez76061ab2008-12-23 15:58:41 -080081 BUG_ON(1);
Sujithf1dc5602008-10-29 10:16:30 +053082 return false;
83 }
84
85 return true;
86}
87
Luis R. Rodriguez795f5e22010-04-15 17:39:00 -040088void ath9k_hw_reset_calibration(struct ath_hw *ah,
89 struct ath9k_cal_list *currCal)
Sujithf1dc5602008-10-29 10:16:30 +053090{
Sujithf1dc5602008-10-29 10:16:30 +053091 int i;
92
93 ath9k_hw_setup_calibration(ah, currCal);
94
95 currCal->calState = CAL_RUNNING;
96
97 for (i = 0; i < AR5416_MAX_CHAINS; i++) {
Sujith2660b812009-02-09 13:27:26 +053098 ah->meas0.sign[i] = 0;
99 ah->meas1.sign[i] = 0;
100 ah->meas2.sign[i] = 0;
101 ah->meas3.sign[i] = 0;
Sujithf1dc5602008-10-29 10:16:30 +0530102 }
103
Sujith2660b812009-02-09 13:27:26 +0530104 ah->cal_samples = 0;
Sujithf1dc5602008-10-29 10:16:30 +0530105}
106
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200107static 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
Luis R. Rodriguezc9e27d92008-12-23 15:58:42 -0800120/* This is done for the currently configured channel */
Sujithcbe61d82009-02-09 13:27:12 +0530121bool ath9k_hw_reset_calvalid(struct ath_hw *ah)
Sujithf1dc5602008-10-29 10:16:30 +0530122{
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700123 struct ath_common *common = ath9k_hw_common(ah);
124 struct ieee80211_conf *conf = &common->hw->conf;
Sujithcbfe9462009-04-13 21:56:56 +0530125 struct ath9k_cal_list *currCal = ah->cal_list_curr;
Sujithf1dc5602008-10-29 10:16:30 +0530126
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200127 if (!ah->caldata)
Luis R. Rodriguezc9e27d92008-12-23 15:58:42 -0800128 return true;
Sujithf1dc5602008-10-29 10:16:30 +0530129
130 if (!AR_SREV_9100(ah) && !AR_SREV_9160_10_OR_LATER(ah))
Luis R. Rodriguezc9e27d92008-12-23 15:58:42 -0800131 return true;
Sujithf1dc5602008-10-29 10:16:30 +0530132
133 if (currCal == NULL)
Luis R. Rodriguezc9e27d92008-12-23 15:58:42 -0800134 return true;
Sujithf1dc5602008-10-29 10:16:30 +0530135
136 if (currCal->calState != CAL_DONE) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700137 ath_print(common, ATH_DBG_CALIBRATE,
138 "Calibration state incorrect, %d\n",
139 currCal->calState);
Luis R. Rodriguezc9e27d92008-12-23 15:58:42 -0800140 return true;
Sujithf1dc5602008-10-29 10:16:30 +0530141 }
142
Luis R. Rodriguezc9e27d92008-12-23 15:58:42 -0800143 if (!ath9k_hw_iscal_supported(ah, currCal->calData->calType))
144 return true;
Sujithf1dc5602008-10-29 10:16:30 +0530145
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700146 ath_print(common, ATH_DBG_CALIBRATE,
147 "Resetting Cal %d state for channel %u\n",
148 currCal->calData->calType, conf->channel->center_freq);
Sujithf1dc5602008-10-29 10:16:30 +0530149
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200150 ah->caldata->CalValid &= ~currCal->calData->calType;
Sujithf1dc5602008-10-29 10:16:30 +0530151 currCal->calState = CAL_WAITING;
152
Luis R. Rodriguezc9e27d92008-12-23 15:58:42 -0800153 return false;
Sujithf1dc5602008-10-29 10:16:30 +0530154}
Luis R. Rodriguez7322fd12009-09-23 23:07:00 -0400155EXPORT_SYMBOL(ath9k_hw_reset_calvalid);
Sujithf1dc5602008-10-29 10:16:30 +0530156
Felix Fietkau00c86592010-07-30 21:02:09 +0200157void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update)
Sujithf1dc5602008-10-29 10:16:30 +0530158{
159 REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
160 AR_PHY_AGC_CONTROL_ENABLE_NF);
Felix Fietkau00c86592010-07-30 21:02:09 +0200161
162 if (update)
163 REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
Sujithf1dc5602008-10-29 10:16:30 +0530164 AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
Felix Fietkau00c86592010-07-30 21:02:09 +0200165 else
166 REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
167 AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
168
Sujithf1dc5602008-10-29 10:16:30 +0530169 REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
170}
171
Felix Fietkaubbacee12010-07-11 15:44:42 +0200172void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
173{
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200174 struct ath9k_nfcal_hist *h = NULL;
Felix Fietkaubbacee12010-07-11 15:44:42 +0200175 unsigned i, j;
176 int32_t val;
Felix Fietkau487f0e02010-07-23 04:31:56 +0200177 u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
Felix Fietkaubbacee12010-07-11 15:44:42 +0200178 struct ath_common *common = ath9k_hw_common(ah);
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200179 s16 default_nf = ath9k_hw_get_default_nf(ah, chan);
Felix Fietkaubbacee12010-07-11 15:44:42 +0200180
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200181 if (ah->caldata)
182 h = ah->caldata->nfCalHist;
Felix Fietkaubbacee12010-07-11 15:44:42 +0200183
184 for (i = 0; i < NUM_NF_READINGS; i++) {
185 if (chainmask & (1 << i)) {
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200186 s16 nfval;
187
188 if (h)
189 nfval = h[i].privNF;
190 else
191 nfval = default_nf;
192
Felix Fietkaubbacee12010-07-11 15:44:42 +0200193 val = REG_READ(ah, ah->nf_regs[i]);
194 val &= 0xFFFFFE00;
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200195 val |= (((u32) nfval << 1) & 0x1ff);
Felix Fietkaubbacee12010-07-11 15:44:42 +0200196 REG_WRITE(ah, ah->nf_regs[i], val);
197 }
198 }
199
200 /*
201 * Load software filtered NF value into baseband internal minCCApwr
202 * variable.
203 */
204 REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
205 AR_PHY_AGC_CONTROL_ENABLE_NF);
206 REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
207 AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
208 REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
209
210 /*
211 * Wait for load to complete, should be fast, a few 10s of us.
212 * The max delay was changed from an original 250us to 10000us
213 * since 250us often results in NF load timeout and causes deaf
214 * condition during stress testing 12/12/2009
215 */
216 for (j = 0; j < 1000; j++) {
217 if ((REG_READ(ah, AR_PHY_AGC_CONTROL) &
218 AR_PHY_AGC_CONTROL_NF) == 0)
219 break;
220 udelay(10);
221 }
222
223 /*
224 * We timed out waiting for the noisefloor to load, probably due to an
225 * in-progress rx. Simply return here and allow the load plenty of time
226 * to complete before the next calibration interval. We need to avoid
227 * trying to load -50 (which happens below) while the previous load is
228 * still in progress as this can cause rx deafness. Instead by returning
229 * here, the baseband nf cal will just be capped by our present
230 * noisefloor until the next calibration timer.
231 */
232 if (j == 1000) {
233 ath_print(common, ATH_DBG_ANY, "Timeout while waiting for nf "
234 "to load: AR_PHY_AGC_CONTROL=0x%x\n",
235 REG_READ(ah, AR_PHY_AGC_CONTROL));
236 return;
237 }
238
239 /*
240 * Restore maxCCAPower register parameter again so that we're not capped
241 * by the median we just loaded. This will be initial (and max) value
242 * of next noise floor calibration the baseband does.
243 */
244 ENABLE_REGWRITE_BUFFER(ah);
245 for (i = 0; i < NUM_NF_READINGS; i++) {
246 if (chainmask & (1 << i)) {
247 val = REG_READ(ah, ah->nf_regs[i]);
248 val &= 0xFFFFFE00;
249 val |= (((u32) (-50) << 1) & 0x1ff);
250 REG_WRITE(ah, ah->nf_regs[i], val);
251 }
252 }
253 REGWRITE_BUFFER_FLUSH(ah);
254 DISABLE_REGWRITE_BUFFER(ah);
255}
256
257
Felix Fietkauf2552e22010-07-02 00:09:50 +0200258static void ath9k_hw_nf_sanitize(struct ath_hw *ah, s16 *nf)
259{
260 struct ath_common *common = ath9k_hw_common(ah);
261 struct ath_nf_limits *limit;
262 int i;
263
264 if (IS_CHAN_2GHZ(ah->curchan))
265 limit = &ah->nf_2g;
266 else
267 limit = &ah->nf_5g;
268
269 for (i = 0; i < NUM_NF_READINGS; i++) {
270 if (!nf[i])
271 continue;
272
Felix Fietkau54bd5002010-07-02 00:09:51 +0200273 ath_print(common, ATH_DBG_CALIBRATE,
274 "NF calibrated [%s] [chain %d] is %d\n",
Felix Fietkaud9292c02010-07-23 04:12:19 +0200275 (i >= 3 ? "ext" : "ctl"), i % 3, nf[i]);
Felix Fietkau54bd5002010-07-02 00:09:51 +0200276
Felix Fietkauf2552e22010-07-02 00:09:50 +0200277 if (nf[i] > limit->max) {
278 ath_print(common, ATH_DBG_CALIBRATE,
279 "NF[%d] (%d) > MAX (%d), correcting to MAX",
280 i, nf[i], limit->max);
281 nf[i] = limit->max;
282 } else if (nf[i] < limit->min) {
283 ath_print(common, ATH_DBG_CALIBRATE,
284 "NF[%d] (%d) < MIN (%d), correcting to NOM",
285 i, nf[i], limit->min);
286 nf[i] = limit->nominal;
287 }
288 }
289}
290
Sujithcbe61d82009-02-09 13:27:12 +0530291int16_t ath9k_hw_getnf(struct ath_hw *ah,
Sujithf1dc5602008-10-29 10:16:30 +0530292 struct ath9k_channel *chan)
293{
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700294 struct ath_common *common = ath9k_hw_common(ah);
Sujithf1dc5602008-10-29 10:16:30 +0530295 int16_t nf, nfThresh;
296 int16_t nfarray[NUM_NF_READINGS] = { 0 };
297 struct ath9k_nfcal_hist *h;
Luis R. Rodriguez76061ab2008-12-23 15:58:41 -0800298 struct ieee80211_channel *c = chan->chan;
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200299 struct ath9k_hw_cal_data *caldata = ah->caldata;
300
301 if (!caldata)
302 return ath9k_hw_get_default_nf(ah, chan);
Sujithf1dc5602008-10-29 10:16:30 +0530303
304 chan->channelFlags &= (~CHANNEL_CW_INT);
305 if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700306 ath_print(common, ATH_DBG_CALIBRATE,
307 "NF did not complete in calibration window\n");
Sujithf1dc5602008-10-29 10:16:30 +0530308 nf = 0;
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200309 caldata->rawNoiseFloor = nf;
310 return caldata->rawNoiseFloor;
Sujithf1dc5602008-10-29 10:16:30 +0530311 } else {
312 ath9k_hw_do_getnf(ah, nfarray);
Felix Fietkauf2552e22010-07-02 00:09:50 +0200313 ath9k_hw_nf_sanitize(ah, nfarray);
Sujithf1dc5602008-10-29 10:16:30 +0530314 nf = nfarray[0];
Luis R. Rodriguezb43d59f2010-04-15 17:38:58 -0400315 if (ath9k_hw_get_nf_thresh(ah, c->band, &nfThresh)
Sujithf1dc5602008-10-29 10:16:30 +0530316 && nf > nfThresh) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700317 ath_print(common, ATH_DBG_CALIBRATE,
318 "noise floor failed detected; "
319 "detected %d, threshold %d\n",
320 nf, nfThresh);
Sujithf1dc5602008-10-29 10:16:30 +0530321 chan->channelFlags |= CHANNEL_CW_INT;
322 }
323 }
324
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200325 h = caldata->nfCalHist;
Sujithf1dc5602008-10-29 10:16:30 +0530326
327 ath9k_hw_update_nfcal_hist_buffer(h, nfarray);
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200328 caldata->rawNoiseFloor = h[0].privNF;
Sujithf1dc5602008-10-29 10:16:30 +0530329
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200330 return ah->caldata->rawNoiseFloor;
Sujithf1dc5602008-10-29 10:16:30 +0530331}
332
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200333void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah,
334 struct ath9k_channel *chan)
Sujithf1dc5602008-10-29 10:16:30 +0530335{
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200336 struct ath9k_nfcal_hist *h;
337 s16 default_nf;
Sujithf1dc5602008-10-29 10:16:30 +0530338 int i, j;
Senthil Balasubramaniana59b5a52009-07-14 20:17:07 -0400339
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200340 if (!ah->caldata)
341 return;
Sujithf1dc5602008-10-29 10:16:30 +0530342
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200343 h = ah->caldata->nfCalHist;
344 default_nf = ath9k_hw_get_default_nf(ah, chan);
Sujithf1dc5602008-10-29 10:16:30 +0530345 for (i = 0; i < NUM_NF_READINGS; i++) {
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200346 h[i].currIndex = 0;
347 h[i].privNF = default_nf;
348 h[i].invalidNFcount = AR_PHY_CCA_FILTERWINDOW_LENGTH;
Sujithf1dc5602008-10-29 10:16:30 +0530349 for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) {
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200350 h[i].nfCalBuffer[j] = default_nf;
Sujithf1dc5602008-10-29 10:16:30 +0530351 }
352 }
Sujithf1dc5602008-10-29 10:16:30 +0530353}
354
Sujithcbe61d82009-02-09 13:27:12 +0530355s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan)
Sujithf1dc5602008-10-29 10:16:30 +0530356{
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200357 if (!ah->caldata || !ah->caldata->rawNoiseFloor)
358 return ath9k_hw_get_default_nf(ah, chan);
Sujithf1dc5602008-10-29 10:16:30 +0530359
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200360 return ah->caldata->rawNoiseFloor;
Sujithf1dc5602008-10-29 10:16:30 +0530361}
Luis R. Rodriguez7322fd12009-09-23 23:07:00 -0400362EXPORT_SYMBOL(ath9k_hw_getchan_noise);