diff options
Diffstat (limited to 'drivers/net/wireless/iwlegacy/iwl-4965-calib.c')
-rw-r--r-- | drivers/net/wireless/iwlegacy/iwl-4965-calib.c | 967 |
1 files changed, 967 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwlegacy/iwl-4965-calib.c b/drivers/net/wireless/iwlegacy/iwl-4965-calib.c new file mode 100644 index 000000000000..81d6a25eb04f --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-4965-calib.c | |||
@@ -0,0 +1,967 @@ | |||
1 | /****************************************************************************** | ||
2 | * | ||
3 | * This file is provided under a dual BSD/GPLv2 license. When using or | ||
4 | * redistributing this file, you may do so under either license. | ||
5 | * | ||
6 | * GPL LICENSE SUMMARY | ||
7 | * | ||
8 | * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of version 2 of the GNU General Public License as | ||
12 | * published by the Free Software Foundation. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, but | ||
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17 | * General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, | ||
22 | * USA | ||
23 | * | ||
24 | * The full GNU General Public License is included in this distribution | ||
25 | * in the file called LICENSE.GPL. | ||
26 | * | ||
27 | * Contact Information: | ||
28 | * Intel Linux Wireless <ilw@linux.intel.com> | ||
29 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 | ||
30 | * | ||
31 | * BSD LICENSE | ||
32 | * | ||
33 | * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. | ||
34 | * All rights reserved. | ||
35 | * | ||
36 | * Redistribution and use in source and binary forms, with or without | ||
37 | * modification, are permitted provided that the following conditions | ||
38 | * are met: | ||
39 | * | ||
40 | * * Redistributions of source code must retain the above copyright | ||
41 | * notice, this list of conditions and the following disclaimer. | ||
42 | * * Redistributions in binary form must reproduce the above copyright | ||
43 | * notice, this list of conditions and the following disclaimer in | ||
44 | * the documentation and/or other materials provided with the | ||
45 | * distribution. | ||
46 | * * Neither the name Intel Corporation nor the names of its | ||
47 | * contributors may be used to endorse or promote products derived | ||
48 | * from this software without specific prior written permission. | ||
49 | * | ||
50 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
51 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
52 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
53 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
54 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
55 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
56 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
57 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
58 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
59 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
60 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
61 | *****************************************************************************/ | ||
62 | |||
63 | #include <linux/slab.h> | ||
64 | #include <net/mac80211.h> | ||
65 | |||
66 | #include "iwl-dev.h" | ||
67 | #include "iwl-core.h" | ||
68 | #include "iwl-4965-calib.h" | ||
69 | |||
70 | /***************************************************************************** | ||
71 | * INIT calibrations framework | ||
72 | *****************************************************************************/ | ||
73 | |||
74 | struct statistics_general_data { | ||
75 | u32 beacon_silence_rssi_a; | ||
76 | u32 beacon_silence_rssi_b; | ||
77 | u32 beacon_silence_rssi_c; | ||
78 | u32 beacon_energy_a; | ||
79 | u32 beacon_energy_b; | ||
80 | u32 beacon_energy_c; | ||
81 | }; | ||
82 | |||
83 | void iwl4965_calib_free_results(struct iwl_priv *priv) | ||
84 | { | ||
85 | int i; | ||
86 | |||
87 | for (i = 0; i < IWL_CALIB_MAX; i++) { | ||
88 | kfree(priv->calib_results[i].buf); | ||
89 | priv->calib_results[i].buf = NULL; | ||
90 | priv->calib_results[i].buf_len = 0; | ||
91 | } | ||
92 | } | ||
93 | |||
94 | /***************************************************************************** | ||
95 | * RUNTIME calibrations framework | ||
96 | *****************************************************************************/ | ||
97 | |||
98 | /* "false alarms" are signals that our DSP tries to lock onto, | ||
99 | * but then determines that they are either noise, or transmissions | ||
100 | * from a distant wireless network (also "noise", really) that get | ||
101 | * "stepped on" by stronger transmissions within our own network. | ||
102 | * This algorithm attempts to set a sensitivity level that is high | ||
103 | * enough to receive all of our own network traffic, but not so | ||
104 | * high that our DSP gets too busy trying to lock onto non-network | ||
105 | * activity/noise. */ | ||
106 | static int iwl4965_sens_energy_cck(struct iwl_priv *priv, | ||
107 | u32 norm_fa, | ||
108 | u32 rx_enable_time, | ||
109 | struct statistics_general_data *rx_info) | ||
110 | { | ||
111 | u32 max_nrg_cck = 0; | ||
112 | int i = 0; | ||
113 | u8 max_silence_rssi = 0; | ||
114 | u32 silence_ref = 0; | ||
115 | u8 silence_rssi_a = 0; | ||
116 | u8 silence_rssi_b = 0; | ||
117 | u8 silence_rssi_c = 0; | ||
118 | u32 val; | ||
119 | |||
120 | /* "false_alarms" values below are cross-multiplications to assess the | ||
121 | * numbers of false alarms within the measured period of actual Rx | ||
122 | * (Rx is off when we're txing), vs the min/max expected false alarms | ||
123 | * (some should be expected if rx is sensitive enough) in a | ||
124 | * hypothetical listening period of 200 time units (TU), 204.8 msec: | ||
125 | * | ||
126 | * MIN_FA/fixed-time < false_alarms/actual-rx-time < MAX_FA/beacon-time | ||
127 | * | ||
128 | * */ | ||
129 | u32 false_alarms = norm_fa * 200 * 1024; | ||
130 | u32 max_false_alarms = MAX_FA_CCK * rx_enable_time; | ||
131 | u32 min_false_alarms = MIN_FA_CCK * rx_enable_time; | ||
132 | struct iwl_sensitivity_data *data = NULL; | ||
133 | const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens; | ||
134 | |||
135 | data = &(priv->sensitivity_data); | ||
136 | |||
137 | data->nrg_auto_corr_silence_diff = 0; | ||
138 | |||
139 | /* Find max silence rssi among all 3 receivers. | ||
140 | * This is background noise, which may include transmissions from other | ||
141 | * networks, measured during silence before our network's beacon */ | ||
142 | silence_rssi_a = (u8)((rx_info->beacon_silence_rssi_a & | ||
143 | ALL_BAND_FILTER) >> 8); | ||
144 | silence_rssi_b = (u8)((rx_info->beacon_silence_rssi_b & | ||
145 | ALL_BAND_FILTER) >> 8); | ||
146 | silence_rssi_c = (u8)((rx_info->beacon_silence_rssi_c & | ||
147 | ALL_BAND_FILTER) >> 8); | ||
148 | |||
149 | val = max(silence_rssi_b, silence_rssi_c); | ||
150 | max_silence_rssi = max(silence_rssi_a, (u8) val); | ||
151 | |||
152 | /* Store silence rssi in 20-beacon history table */ | ||
153 | data->nrg_silence_rssi[data->nrg_silence_idx] = max_silence_rssi; | ||
154 | data->nrg_silence_idx++; | ||
155 | if (data->nrg_silence_idx >= NRG_NUM_PREV_STAT_L) | ||
156 | data->nrg_silence_idx = 0; | ||
157 | |||
158 | /* Find max silence rssi across 20 beacon history */ | ||
159 | for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) { | ||
160 | val = data->nrg_silence_rssi[i]; | ||
161 | silence_ref = max(silence_ref, val); | ||
162 | } | ||
163 | IWL_DEBUG_CALIB(priv, "silence a %u, b %u, c %u, 20-bcn max %u\n", | ||
164 | silence_rssi_a, silence_rssi_b, silence_rssi_c, | ||
165 | silence_ref); | ||
166 | |||
167 | /* Find max rx energy (min value!) among all 3 receivers, | ||
168 | * measured during beacon frame. | ||
169 | * Save it in 10-beacon history table. */ | ||
170 | i = data->nrg_energy_idx; | ||
171 | val = min(rx_info->beacon_energy_b, rx_info->beacon_energy_c); | ||
172 | data->nrg_value[i] = min(rx_info->beacon_energy_a, val); | ||
173 | |||
174 | data->nrg_energy_idx++; | ||
175 | if (data->nrg_energy_idx >= 10) | ||
176 | data->nrg_energy_idx = 0; | ||
177 | |||
178 | /* Find min rx energy (max value) across 10 beacon history. | ||
179 | * This is the minimum signal level that we want to receive well. | ||
180 | * Add backoff (margin so we don't miss slightly lower energy frames). | ||
181 | * This establishes an upper bound (min value) for energy threshold. */ | ||
182 | max_nrg_cck = data->nrg_value[0]; | ||
183 | for (i = 1; i < 10; i++) | ||
184 | max_nrg_cck = (u32) max(max_nrg_cck, (data->nrg_value[i])); | ||
185 | max_nrg_cck += 6; | ||
186 | |||
187 | IWL_DEBUG_CALIB(priv, "rx energy a %u, b %u, c %u, 10-bcn max/min %u\n", | ||
188 | rx_info->beacon_energy_a, rx_info->beacon_energy_b, | ||
189 | rx_info->beacon_energy_c, max_nrg_cck - 6); | ||
190 | |||
191 | /* Count number of consecutive beacons with fewer-than-desired | ||
192 | * false alarms. */ | ||
193 | if (false_alarms < min_false_alarms) | ||
194 | data->num_in_cck_no_fa++; | ||
195 | else | ||
196 | data->num_in_cck_no_fa = 0; | ||
197 | IWL_DEBUG_CALIB(priv, "consecutive bcns with few false alarms = %u\n", | ||
198 | data->num_in_cck_no_fa); | ||
199 | |||
200 | /* If we got too many false alarms this time, reduce sensitivity */ | ||
201 | if ((false_alarms > max_false_alarms) && | ||
202 | (data->auto_corr_cck > AUTO_CORR_MAX_TH_CCK)) { | ||
203 | IWL_DEBUG_CALIB(priv, "norm FA %u > max FA %u\n", | ||
204 | false_alarms, max_false_alarms); | ||
205 | IWL_DEBUG_CALIB(priv, "... reducing sensitivity\n"); | ||
206 | data->nrg_curr_state = IWL_FA_TOO_MANY; | ||
207 | /* Store for "fewer than desired" on later beacon */ | ||
208 | data->nrg_silence_ref = silence_ref; | ||
209 | |||
210 | /* increase energy threshold (reduce nrg value) | ||
211 | * to decrease sensitivity */ | ||
212 | data->nrg_th_cck = data->nrg_th_cck - NRG_STEP_CCK; | ||
213 | /* Else if we got fewer than desired, increase sensitivity */ | ||
214 | } else if (false_alarms < min_false_alarms) { | ||
215 | data->nrg_curr_state = IWL_FA_TOO_FEW; | ||
216 | |||
217 | /* Compare silence level with silence level for most recent | ||
218 | * healthy number or too many false alarms */ | ||
219 | data->nrg_auto_corr_silence_diff = (s32)data->nrg_silence_ref - | ||
220 | (s32)silence_ref; | ||
221 | |||
222 | IWL_DEBUG_CALIB(priv, | ||
223 | "norm FA %u < min FA %u, silence diff %d\n", | ||
224 | false_alarms, min_false_alarms, | ||
225 | data->nrg_auto_corr_silence_diff); | ||
226 | |||
227 | /* Increase value to increase sensitivity, but only if: | ||
228 | * 1a) previous beacon did *not* have *too many* false alarms | ||
229 | * 1b) AND there's a significant difference in Rx levels | ||
230 | * from a previous beacon with too many, or healthy # FAs | ||
231 | * OR 2) We've seen a lot of beacons (100) with too few | ||
232 | * false alarms */ | ||
233 | if ((data->nrg_prev_state != IWL_FA_TOO_MANY) && | ||
234 | ((data->nrg_auto_corr_silence_diff > NRG_DIFF) || | ||
235 | (data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) { | ||
236 | |||
237 | IWL_DEBUG_CALIB(priv, "... increasing sensitivity\n"); | ||
238 | /* Increase nrg value to increase sensitivity */ | ||
239 | val = data->nrg_th_cck + NRG_STEP_CCK; | ||
240 | data->nrg_th_cck = min((u32)ranges->min_nrg_cck, val); | ||
241 | } else { | ||
242 | IWL_DEBUG_CALIB(priv, | ||
243 | "... but not changing sensitivity\n"); | ||
244 | } | ||
245 | |||
246 | /* Else we got a healthy number of false alarms, keep status quo */ | ||
247 | } else { | ||
248 | IWL_DEBUG_CALIB(priv, " FA in safe zone\n"); | ||
249 | data->nrg_curr_state = IWL_FA_GOOD_RANGE; | ||
250 | |||
251 | /* Store for use in "fewer than desired" with later beacon */ | ||
252 | data->nrg_silence_ref = silence_ref; | ||
253 | |||
254 | /* If previous beacon had too many false alarms, | ||
255 | * give it some extra margin by reducing sensitivity again | ||
256 | * (but don't go below measured energy of desired Rx) */ | ||
257 | if (IWL_FA_TOO_MANY == data->nrg_prev_state) { | ||
258 | IWL_DEBUG_CALIB(priv, "... increasing margin\n"); | ||
259 | if (data->nrg_th_cck > (max_nrg_cck + NRG_MARGIN)) | ||
260 | data->nrg_th_cck -= NRG_MARGIN; | ||
261 | else | ||
262 | data->nrg_th_cck = max_nrg_cck; | ||
263 | } | ||
264 | } | ||
265 | |||
266 | /* Make sure the energy threshold does not go above the measured | ||
267 | * energy of the desired Rx signals (reduced by backoff margin), | ||
268 | * or else we might start missing Rx frames. | ||
269 | * Lower value is higher energy, so we use max()! | ||
270 | */ | ||
271 | data->nrg_th_cck = max(max_nrg_cck, data->nrg_th_cck); | ||
272 | IWL_DEBUG_CALIB(priv, "new nrg_th_cck %u\n", data->nrg_th_cck); | ||
273 | |||
274 | data->nrg_prev_state = data->nrg_curr_state; | ||
275 | |||
276 | /* Auto-correlation CCK algorithm */ | ||
277 | if (false_alarms > min_false_alarms) { | ||
278 | |||
279 | /* increase auto_corr values to decrease sensitivity | ||
280 | * so the DSP won't be disturbed by the noise | ||
281 | */ | ||
282 | if (data->auto_corr_cck < AUTO_CORR_MAX_TH_CCK) | ||
283 | data->auto_corr_cck = AUTO_CORR_MAX_TH_CCK + 1; | ||
284 | else { | ||
285 | val = data->auto_corr_cck + AUTO_CORR_STEP_CCK; | ||
286 | data->auto_corr_cck = | ||
287 | min((u32)ranges->auto_corr_max_cck, val); | ||
288 | } | ||
289 | val = data->auto_corr_cck_mrc + AUTO_CORR_STEP_CCK; | ||
290 | data->auto_corr_cck_mrc = | ||
291 | min((u32)ranges->auto_corr_max_cck_mrc, val); | ||
292 | } else if ((false_alarms < min_false_alarms) && | ||
293 | ((data->nrg_auto_corr_silence_diff > NRG_DIFF) || | ||
294 | (data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) { | ||
295 | |||
296 | /* Decrease auto_corr values to increase sensitivity */ | ||
297 | val = data->auto_corr_cck - AUTO_CORR_STEP_CCK; | ||
298 | data->auto_corr_cck = | ||
299 | max((u32)ranges->auto_corr_min_cck, val); | ||
300 | val = data->auto_corr_cck_mrc - AUTO_CORR_STEP_CCK; | ||
301 | data->auto_corr_cck_mrc = | ||
302 | max((u32)ranges->auto_corr_min_cck_mrc, val); | ||
303 | } | ||
304 | |||
305 | return 0; | ||
306 | } | ||
307 | |||
308 | |||
309 | static int iwl4965_sens_auto_corr_ofdm(struct iwl_priv *priv, | ||
310 | u32 norm_fa, | ||
311 | u32 rx_enable_time) | ||
312 | { | ||
313 | u32 val; | ||
314 | u32 false_alarms = norm_fa * 200 * 1024; | ||
315 | u32 max_false_alarms = MAX_FA_OFDM * rx_enable_time; | ||
316 | u32 min_false_alarms = MIN_FA_OFDM * rx_enable_time; | ||
317 | struct iwl_sensitivity_data *data = NULL; | ||
318 | const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens; | ||
319 | |||
320 | data = &(priv->sensitivity_data); | ||
321 | |||
322 | /* If we got too many false alarms this time, reduce sensitivity */ | ||
323 | if (false_alarms > max_false_alarms) { | ||
324 | |||
325 | IWL_DEBUG_CALIB(priv, "norm FA %u > max FA %u)\n", | ||
326 | false_alarms, max_false_alarms); | ||
327 | |||
328 | val = data->auto_corr_ofdm + AUTO_CORR_STEP_OFDM; | ||
329 | data->auto_corr_ofdm = | ||
330 | min((u32)ranges->auto_corr_max_ofdm, val); | ||
331 | |||
332 | val = data->auto_corr_ofdm_mrc + AUTO_CORR_STEP_OFDM; | ||
333 | data->auto_corr_ofdm_mrc = | ||
334 | min((u32)ranges->auto_corr_max_ofdm_mrc, val); | ||
335 | |||
336 | val = data->auto_corr_ofdm_x1 + AUTO_CORR_STEP_OFDM; | ||
337 | data->auto_corr_ofdm_x1 = | ||
338 | min((u32)ranges->auto_corr_max_ofdm_x1, val); | ||
339 | |||
340 | val = data->auto_corr_ofdm_mrc_x1 + AUTO_CORR_STEP_OFDM; | ||
341 | data->auto_corr_ofdm_mrc_x1 = | ||
342 | min((u32)ranges->auto_corr_max_ofdm_mrc_x1, val); | ||
343 | } | ||
344 | |||
345 | /* Else if we got fewer than desired, increase sensitivity */ | ||
346 | else if (false_alarms < min_false_alarms) { | ||
347 | |||
348 | IWL_DEBUG_CALIB(priv, "norm FA %u < min FA %u\n", | ||
349 | false_alarms, min_false_alarms); | ||
350 | |||
351 | val = data->auto_corr_ofdm - AUTO_CORR_STEP_OFDM; | ||
352 | data->auto_corr_ofdm = | ||
353 | max((u32)ranges->auto_corr_min_ofdm, val); | ||
354 | |||
355 | val = data->auto_corr_ofdm_mrc - AUTO_CORR_STEP_OFDM; | ||
356 | data->auto_corr_ofdm_mrc = | ||
357 | max((u32)ranges->auto_corr_min_ofdm_mrc, val); | ||
358 | |||
359 | val = data->auto_corr_ofdm_x1 - AUTO_CORR_STEP_OFDM; | ||
360 | data->auto_corr_ofdm_x1 = | ||
361 | max((u32)ranges->auto_corr_min_ofdm_x1, val); | ||
362 | |||
363 | val = data->auto_corr_ofdm_mrc_x1 - AUTO_CORR_STEP_OFDM; | ||
364 | data->auto_corr_ofdm_mrc_x1 = | ||
365 | max((u32)ranges->auto_corr_min_ofdm_mrc_x1, val); | ||
366 | } else { | ||
367 | IWL_DEBUG_CALIB(priv, "min FA %u < norm FA %u < max FA %u OK\n", | ||
368 | min_false_alarms, false_alarms, max_false_alarms); | ||
369 | } | ||
370 | return 0; | ||
371 | } | ||
372 | |||
373 | static void iwl4965_prepare_legacy_sensitivity_tbl(struct iwl_priv *priv, | ||
374 | struct iwl_sensitivity_data *data, | ||
375 | __le16 *tbl) | ||
376 | { | ||
377 | tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX] = | ||
378 | cpu_to_le16((u16)data->auto_corr_ofdm); | ||
379 | tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX] = | ||
380 | cpu_to_le16((u16)data->auto_corr_ofdm_mrc); | ||
381 | tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX] = | ||
382 | cpu_to_le16((u16)data->auto_corr_ofdm_x1); | ||
383 | tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX] = | ||
384 | cpu_to_le16((u16)data->auto_corr_ofdm_mrc_x1); | ||
385 | |||
386 | tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX] = | ||
387 | cpu_to_le16((u16)data->auto_corr_cck); | ||
388 | tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX] = | ||
389 | cpu_to_le16((u16)data->auto_corr_cck_mrc); | ||
390 | |||
391 | tbl[HD_MIN_ENERGY_CCK_DET_INDEX] = | ||
392 | cpu_to_le16((u16)data->nrg_th_cck); | ||
393 | tbl[HD_MIN_ENERGY_OFDM_DET_INDEX] = | ||
394 | cpu_to_le16((u16)data->nrg_th_ofdm); | ||
395 | |||
396 | tbl[HD_BARKER_CORR_TH_ADD_MIN_INDEX] = | ||
397 | cpu_to_le16(data->barker_corr_th_min); | ||
398 | tbl[HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX] = | ||
399 | cpu_to_le16(data->barker_corr_th_min_mrc); | ||
400 | tbl[HD_OFDM_ENERGY_TH_IN_INDEX] = | ||
401 | cpu_to_le16(data->nrg_th_cca); | ||
402 | |||
403 | IWL_DEBUG_CALIB(priv, "ofdm: ac %u mrc %u x1 %u mrc_x1 %u thresh %u\n", | ||
404 | data->auto_corr_ofdm, data->auto_corr_ofdm_mrc, | ||
405 | data->auto_corr_ofdm_x1, data->auto_corr_ofdm_mrc_x1, | ||
406 | data->nrg_th_ofdm); | ||
407 | |||
408 | IWL_DEBUG_CALIB(priv, "cck: ac %u mrc %u thresh %u\n", | ||
409 | data->auto_corr_cck, data->auto_corr_cck_mrc, | ||
410 | data->nrg_th_cck); | ||
411 | } | ||
412 | |||
413 | /* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */ | ||
414 | static int iwl4965_sensitivity_write(struct iwl_priv *priv) | ||
415 | { | ||
416 | struct iwl_sensitivity_cmd cmd; | ||
417 | struct iwl_sensitivity_data *data = NULL; | ||
418 | struct iwl_host_cmd cmd_out = { | ||
419 | .id = SENSITIVITY_CMD, | ||
420 | .len = sizeof(struct iwl_sensitivity_cmd), | ||
421 | .flags = CMD_ASYNC, | ||
422 | .data = &cmd, | ||
423 | }; | ||
424 | |||
425 | data = &(priv->sensitivity_data); | ||
426 | |||
427 | memset(&cmd, 0, sizeof(cmd)); | ||
428 | |||
429 | iwl4965_prepare_legacy_sensitivity_tbl(priv, data, &cmd.table[0]); | ||
430 | |||
431 | /* Update uCode's "work" table, and copy it to DSP */ | ||
432 | cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE; | ||
433 | |||
434 | /* Don't send command to uCode if nothing has changed */ | ||
435 | if (!memcmp(&cmd.table[0], &(priv->sensitivity_tbl[0]), | ||
436 | sizeof(u16)*HD_TABLE_SIZE)) { | ||
437 | IWL_DEBUG_CALIB(priv, "No change in SENSITIVITY_CMD\n"); | ||
438 | return 0; | ||
439 | } | ||
440 | |||
441 | /* Copy table for comparison next time */ | ||
442 | memcpy(&(priv->sensitivity_tbl[0]), &(cmd.table[0]), | ||
443 | sizeof(u16)*HD_TABLE_SIZE); | ||
444 | |||
445 | return iwl_legacy_send_cmd(priv, &cmd_out); | ||
446 | } | ||
447 | |||
448 | void iwl4965_init_sensitivity(struct iwl_priv *priv) | ||
449 | { | ||
450 | int ret = 0; | ||
451 | int i; | ||
452 | struct iwl_sensitivity_data *data = NULL; | ||
453 | const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens; | ||
454 | |||
455 | if (priv->disable_sens_cal) | ||
456 | return; | ||
457 | |||
458 | IWL_DEBUG_CALIB(priv, "Start iwl4965_init_sensitivity\n"); | ||
459 | |||
460 | /* Clear driver's sensitivity algo data */ | ||
461 | data = &(priv->sensitivity_data); | ||
462 | |||
463 | if (ranges == NULL) | ||
464 | return; | ||
465 | |||
466 | memset(data, 0, sizeof(struct iwl_sensitivity_data)); | ||
467 | |||
468 | data->num_in_cck_no_fa = 0; | ||
469 | data->nrg_curr_state = IWL_FA_TOO_MANY; | ||
470 | data->nrg_prev_state = IWL_FA_TOO_MANY; | ||
471 | data->nrg_silence_ref = 0; | ||
472 | data->nrg_silence_idx = 0; | ||
473 | data->nrg_energy_idx = 0; | ||
474 | |||
475 | for (i = 0; i < 10; i++) | ||
476 | data->nrg_value[i] = 0; | ||
477 | |||
478 | for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) | ||
479 | data->nrg_silence_rssi[i] = 0; | ||
480 | |||
481 | data->auto_corr_ofdm = ranges->auto_corr_min_ofdm; | ||
482 | data->auto_corr_ofdm_mrc = ranges->auto_corr_min_ofdm_mrc; | ||
483 | data->auto_corr_ofdm_x1 = ranges->auto_corr_min_ofdm_x1; | ||
484 | data->auto_corr_ofdm_mrc_x1 = ranges->auto_corr_min_ofdm_mrc_x1; | ||
485 | data->auto_corr_cck = AUTO_CORR_CCK_MIN_VAL_DEF; | ||
486 | data->auto_corr_cck_mrc = ranges->auto_corr_min_cck_mrc; | ||
487 | data->nrg_th_cck = ranges->nrg_th_cck; | ||
488 | data->nrg_th_ofdm = ranges->nrg_th_ofdm; | ||
489 | data->barker_corr_th_min = ranges->barker_corr_th_min; | ||
490 | data->barker_corr_th_min_mrc = ranges->barker_corr_th_min_mrc; | ||
491 | data->nrg_th_cca = ranges->nrg_th_cca; | ||
492 | |||
493 | data->last_bad_plcp_cnt_ofdm = 0; | ||
494 | data->last_fa_cnt_ofdm = 0; | ||
495 | data->last_bad_plcp_cnt_cck = 0; | ||
496 | data->last_fa_cnt_cck = 0; | ||
497 | |||
498 | ret |= iwl4965_sensitivity_write(priv); | ||
499 | IWL_DEBUG_CALIB(priv, "<<return 0x%X\n", ret); | ||
500 | } | ||
501 | |||
502 | void iwl4965_sensitivity_calibration(struct iwl_priv *priv, void *resp) | ||
503 | { | ||
504 | u32 rx_enable_time; | ||
505 | u32 fa_cck; | ||
506 | u32 fa_ofdm; | ||
507 | u32 bad_plcp_cck; | ||
508 | u32 bad_plcp_ofdm; | ||
509 | u32 norm_fa_ofdm; | ||
510 | u32 norm_fa_cck; | ||
511 | struct iwl_sensitivity_data *data = NULL; | ||
512 | struct statistics_rx_non_phy *rx_info; | ||
513 | struct statistics_rx_phy *ofdm, *cck; | ||
514 | unsigned long flags; | ||
515 | struct statistics_general_data statis; | ||
516 | |||
517 | if (priv->disable_sens_cal) | ||
518 | return; | ||
519 | |||
520 | data = &(priv->sensitivity_data); | ||
521 | |||
522 | if (!iwl_legacy_is_any_associated(priv)) { | ||
523 | IWL_DEBUG_CALIB(priv, "<< - not associated\n"); | ||
524 | return; | ||
525 | } | ||
526 | |||
527 | spin_lock_irqsave(&priv->lock, flags); | ||
528 | |||
529 | rx_info = &(((struct iwl_notif_statistics *)resp)->rx.general); | ||
530 | ofdm = &(((struct iwl_notif_statistics *)resp)->rx.ofdm); | ||
531 | cck = &(((struct iwl_notif_statistics *)resp)->rx.cck); | ||
532 | |||
533 | if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) { | ||
534 | IWL_DEBUG_CALIB(priv, "<< invalid data.\n"); | ||
535 | spin_unlock_irqrestore(&priv->lock, flags); | ||
536 | return; | ||
537 | } | ||
538 | |||
539 | /* Extract Statistics: */ | ||
540 | rx_enable_time = le32_to_cpu(rx_info->channel_load); | ||
541 | fa_cck = le32_to_cpu(cck->false_alarm_cnt); | ||
542 | fa_ofdm = le32_to_cpu(ofdm->false_alarm_cnt); | ||
543 | bad_plcp_cck = le32_to_cpu(cck->plcp_err); | ||
544 | bad_plcp_ofdm = le32_to_cpu(ofdm->plcp_err); | ||
545 | |||
546 | statis.beacon_silence_rssi_a = | ||
547 | le32_to_cpu(rx_info->beacon_silence_rssi_a); | ||
548 | statis.beacon_silence_rssi_b = | ||
549 | le32_to_cpu(rx_info->beacon_silence_rssi_b); | ||
550 | statis.beacon_silence_rssi_c = | ||
551 | le32_to_cpu(rx_info->beacon_silence_rssi_c); | ||
552 | statis.beacon_energy_a = | ||
553 | le32_to_cpu(rx_info->beacon_energy_a); | ||
554 | statis.beacon_energy_b = | ||
555 | le32_to_cpu(rx_info->beacon_energy_b); | ||
556 | statis.beacon_energy_c = | ||
557 | le32_to_cpu(rx_info->beacon_energy_c); | ||
558 | |||
559 | spin_unlock_irqrestore(&priv->lock, flags); | ||
560 | |||
561 | IWL_DEBUG_CALIB(priv, "rx_enable_time = %u usecs\n", rx_enable_time); | ||
562 | |||
563 | if (!rx_enable_time) { | ||
564 | IWL_DEBUG_CALIB(priv, "<< RX Enable Time == 0!\n"); | ||
565 | return; | ||
566 | } | ||
567 | |||
568 | /* These statistics increase monotonically, and do not reset | ||
569 | * at each beacon. Calculate difference from last value, or just | ||
570 | * use the new statistics value if it has reset or wrapped around. */ | ||
571 | if (data->last_bad_plcp_cnt_cck > bad_plcp_cck) | ||
572 | data->last_bad_plcp_cnt_cck = bad_plcp_cck; | ||
573 | else { | ||
574 | bad_plcp_cck -= data->last_bad_plcp_cnt_cck; | ||
575 | data->last_bad_plcp_cnt_cck += bad_plcp_cck; | ||
576 | } | ||
577 | |||
578 | if (data->last_bad_plcp_cnt_ofdm > bad_plcp_ofdm) | ||
579 | data->last_bad_plcp_cnt_ofdm = bad_plcp_ofdm; | ||
580 | else { | ||
581 | bad_plcp_ofdm -= data->last_bad_plcp_cnt_ofdm; | ||
582 | data->last_bad_plcp_cnt_ofdm += bad_plcp_ofdm; | ||
583 | } | ||
584 | |||
585 | if (data->last_fa_cnt_ofdm > fa_ofdm) | ||
586 | data->last_fa_cnt_ofdm = fa_ofdm; | ||
587 | else { | ||
588 | fa_ofdm -= data->last_fa_cnt_ofdm; | ||
589 | data->last_fa_cnt_ofdm += fa_ofdm; | ||
590 | } | ||
591 | |||
592 | if (data->last_fa_cnt_cck > fa_cck) | ||
593 | data->last_fa_cnt_cck = fa_cck; | ||
594 | else { | ||
595 | fa_cck -= data->last_fa_cnt_cck; | ||
596 | data->last_fa_cnt_cck += fa_cck; | ||
597 | } | ||
598 | |||
599 | /* Total aborted signal locks */ | ||
600 | norm_fa_ofdm = fa_ofdm + bad_plcp_ofdm; | ||
601 | norm_fa_cck = fa_cck + bad_plcp_cck; | ||
602 | |||
603 | IWL_DEBUG_CALIB(priv, | ||
604 | "cck: fa %u badp %u ofdm: fa %u badp %u\n", fa_cck, | ||
605 | bad_plcp_cck, fa_ofdm, bad_plcp_ofdm); | ||
606 | |||
607 | iwl4965_sens_auto_corr_ofdm(priv, norm_fa_ofdm, rx_enable_time); | ||
608 | iwl4965_sens_energy_cck(priv, norm_fa_cck, rx_enable_time, &statis); | ||
609 | |||
610 | iwl4965_sensitivity_write(priv); | ||
611 | } | ||
612 | |||
613 | static inline u8 iwl4965_find_first_chain(u8 mask) | ||
614 | { | ||
615 | if (mask & ANT_A) | ||
616 | return CHAIN_A; | ||
617 | if (mask & ANT_B) | ||
618 | return CHAIN_B; | ||
619 | return CHAIN_C; | ||
620 | } | ||
621 | |||
622 | /** | ||
623 | * Run disconnected antenna algorithm to find out which antennas are | ||
624 | * disconnected. | ||
625 | */ | ||
626 | static void | ||
627 | iwl4965_find_disconn_antenna(struct iwl_priv *priv, u32* average_sig, | ||
628 | struct iwl_chain_noise_data *data) | ||
629 | { | ||
630 | u32 active_chains = 0; | ||
631 | u32 max_average_sig; | ||
632 | u16 max_average_sig_antenna_i; | ||
633 | u8 num_tx_chains; | ||
634 | u8 first_chain; | ||
635 | u16 i = 0; | ||
636 | |||
637 | average_sig[0] = data->chain_signal_a / | ||
638 | priv->cfg->base_params->chain_noise_num_beacons; | ||
639 | average_sig[1] = data->chain_signal_b / | ||
640 | priv->cfg->base_params->chain_noise_num_beacons; | ||
641 | average_sig[2] = data->chain_signal_c / | ||
642 | priv->cfg->base_params->chain_noise_num_beacons; | ||
643 | |||
644 | if (average_sig[0] >= average_sig[1]) { | ||
645 | max_average_sig = average_sig[0]; | ||
646 | max_average_sig_antenna_i = 0; | ||
647 | active_chains = (1 << max_average_sig_antenna_i); | ||
648 | } else { | ||
649 | max_average_sig = average_sig[1]; | ||
650 | max_average_sig_antenna_i = 1; | ||
651 | active_chains = (1 << max_average_sig_antenna_i); | ||
652 | } | ||
653 | |||
654 | if (average_sig[2] >= max_average_sig) { | ||
655 | max_average_sig = average_sig[2]; | ||
656 | max_average_sig_antenna_i = 2; | ||
657 | active_chains = (1 << max_average_sig_antenna_i); | ||
658 | } | ||
659 | |||
660 | IWL_DEBUG_CALIB(priv, "average_sig: a %d b %d c %d\n", | ||
661 | average_sig[0], average_sig[1], average_sig[2]); | ||
662 | IWL_DEBUG_CALIB(priv, "max_average_sig = %d, antenna %d\n", | ||
663 | max_average_sig, max_average_sig_antenna_i); | ||
664 | |||
665 | /* Compare signal strengths for all 3 receivers. */ | ||
666 | for (i = 0; i < NUM_RX_CHAINS; i++) { | ||
667 | if (i != max_average_sig_antenna_i) { | ||
668 | s32 rssi_delta = (max_average_sig - average_sig[i]); | ||
669 | |||
670 | /* If signal is very weak, compared with | ||
671 | * strongest, mark it as disconnected. */ | ||
672 | if (rssi_delta > MAXIMUM_ALLOWED_PATHLOSS) | ||
673 | data->disconn_array[i] = 1; | ||
674 | else | ||
675 | active_chains |= (1 << i); | ||
676 | IWL_DEBUG_CALIB(priv, "i = %d rssiDelta = %d " | ||
677 | "disconn_array[i] = %d\n", | ||
678 | i, rssi_delta, data->disconn_array[i]); | ||
679 | } | ||
680 | } | ||
681 | |||
682 | /* | ||
683 | * The above algorithm sometimes fails when the ucode | ||
684 | * reports 0 for all chains. It's not clear why that | ||
685 | * happens to start with, but it is then causing trouble | ||
686 | * because this can make us enable more chains than the | ||
687 | * hardware really has. | ||
688 | * | ||
689 | * To be safe, simply mask out any chains that we know | ||
690 | * are not on the device. | ||
691 | */ | ||
692 | active_chains &= priv->hw_params.valid_rx_ant; | ||
693 | |||
694 | num_tx_chains = 0; | ||
695 | for (i = 0; i < NUM_RX_CHAINS; i++) { | ||
696 | /* loops on all the bits of | ||
697 | * priv->hw_setting.valid_tx_ant */ | ||
698 | u8 ant_msk = (1 << i); | ||
699 | if (!(priv->hw_params.valid_tx_ant & ant_msk)) | ||
700 | continue; | ||
701 | |||
702 | num_tx_chains++; | ||
703 | if (data->disconn_array[i] == 0) | ||
704 | /* there is a Tx antenna connected */ | ||
705 | break; | ||
706 | if (num_tx_chains == priv->hw_params.tx_chains_num && | ||
707 | data->disconn_array[i]) { | ||
708 | /* | ||
709 | * If all chains are disconnected | ||
710 | * connect the first valid tx chain | ||
711 | */ | ||
712 | first_chain = | ||
713 | iwl4965_find_first_chain(priv->cfg->valid_tx_ant); | ||
714 | data->disconn_array[first_chain] = 0; | ||
715 | active_chains |= BIT(first_chain); | ||
716 | IWL_DEBUG_CALIB(priv, "All Tx chains are disconnected \ | ||
717 | W/A - declare %d as connected\n", | ||
718 | first_chain); | ||
719 | break; | ||
720 | } | ||
721 | } | ||
722 | |||
723 | if (active_chains != priv->hw_params.valid_rx_ant && | ||
724 | active_chains != priv->chain_noise_data.active_chains) | ||
725 | IWL_DEBUG_CALIB(priv, | ||
726 | "Detected that not all antennas are connected! " | ||
727 | "Connected: %#x, valid: %#x.\n", | ||
728 | active_chains, priv->hw_params.valid_rx_ant); | ||
729 | |||
730 | /* Save for use within RXON, TX, SCAN commands, etc. */ | ||
731 | data->active_chains = active_chains; | ||
732 | IWL_DEBUG_CALIB(priv, "active_chains (bitwise) = 0x%x\n", | ||
733 | active_chains); | ||
734 | } | ||
735 | |||
736 | static void iwl4965_gain_computation(struct iwl_priv *priv, | ||
737 | u32 *average_noise, | ||
738 | u16 min_average_noise_antenna_i, | ||
739 | u32 min_average_noise, | ||
740 | u8 default_chain) | ||
741 | { | ||
742 | int i, ret; | ||
743 | struct iwl_chain_noise_data *data = &priv->chain_noise_data; | ||
744 | |||
745 | data->delta_gain_code[min_average_noise_antenna_i] = 0; | ||
746 | |||
747 | for (i = default_chain; i < NUM_RX_CHAINS; i++) { | ||
748 | s32 delta_g = 0; | ||
749 | |||
750 | if (!(data->disconn_array[i]) && | ||
751 | (data->delta_gain_code[i] == | ||
752 | CHAIN_NOISE_DELTA_GAIN_INIT_VAL)) { | ||
753 | delta_g = average_noise[i] - min_average_noise; | ||
754 | data->delta_gain_code[i] = (u8)((delta_g * 10) / 15); | ||
755 | data->delta_gain_code[i] = | ||
756 | min(data->delta_gain_code[i], | ||
757 | (u8) CHAIN_NOISE_MAX_DELTA_GAIN_CODE); | ||
758 | |||
759 | data->delta_gain_code[i] = | ||
760 | (data->delta_gain_code[i] | (1 << 2)); | ||
761 | } else { | ||
762 | data->delta_gain_code[i] = 0; | ||
763 | } | ||
764 | } | ||
765 | IWL_DEBUG_CALIB(priv, "delta_gain_codes: a %d b %d c %d\n", | ||
766 | data->delta_gain_code[0], | ||
767 | data->delta_gain_code[1], | ||
768 | data->delta_gain_code[2]); | ||
769 | |||
770 | /* Differential gain gets sent to uCode only once */ | ||
771 | if (!data->radio_write) { | ||
772 | struct iwl_calib_diff_gain_cmd cmd; | ||
773 | data->radio_write = 1; | ||
774 | |||
775 | memset(&cmd, 0, sizeof(cmd)); | ||
776 | cmd.hdr.op_code = IWL_PHY_CALIBRATE_DIFF_GAIN_CMD; | ||
777 | cmd.diff_gain_a = data->delta_gain_code[0]; | ||
778 | cmd.diff_gain_b = data->delta_gain_code[1]; | ||
779 | cmd.diff_gain_c = data->delta_gain_code[2]; | ||
780 | ret = iwl_legacy_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD, | ||
781 | sizeof(cmd), &cmd); | ||
782 | if (ret) | ||
783 | IWL_DEBUG_CALIB(priv, "fail sending cmd " | ||
784 | "REPLY_PHY_CALIBRATION_CMD\n"); | ||
785 | |||
786 | /* TODO we might want recalculate | ||
787 | * rx_chain in rxon cmd */ | ||
788 | |||
789 | /* Mark so we run this algo only once! */ | ||
790 | data->state = IWL_CHAIN_NOISE_CALIBRATED; | ||
791 | } | ||
792 | } | ||
793 | |||
794 | |||
795 | |||
796 | /* | ||
797 | * Accumulate 16 beacons of signal and noise statistics for each of | ||
798 | * 3 receivers/antennas/rx-chains, then figure out: | ||
799 | * 1) Which antennas are connected. | ||
800 | * 2) Differential rx gain settings to balance the 3 receivers. | ||
801 | */ | ||
802 | void iwl4965_chain_noise_calibration(struct iwl_priv *priv, void *stat_resp) | ||
803 | { | ||
804 | struct iwl_chain_noise_data *data = NULL; | ||
805 | |||
806 | u32 chain_noise_a; | ||
807 | u32 chain_noise_b; | ||
808 | u32 chain_noise_c; | ||
809 | u32 chain_sig_a; | ||
810 | u32 chain_sig_b; | ||
811 | u32 chain_sig_c; | ||
812 | u32 average_sig[NUM_RX_CHAINS] = {INITIALIZATION_VALUE}; | ||
813 | u32 average_noise[NUM_RX_CHAINS] = {INITIALIZATION_VALUE}; | ||
814 | u32 min_average_noise = MIN_AVERAGE_NOISE_MAX_VALUE; | ||
815 | u16 min_average_noise_antenna_i = INITIALIZATION_VALUE; | ||
816 | u16 i = 0; | ||
817 | u16 rxon_chnum = INITIALIZATION_VALUE; | ||
818 | u16 stat_chnum = INITIALIZATION_VALUE; | ||
819 | u8 rxon_band24; | ||
820 | u8 stat_band24; | ||
821 | unsigned long flags; | ||
822 | struct statistics_rx_non_phy *rx_info; | ||
823 | |||
824 | struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; | ||
825 | |||
826 | if (priv->disable_chain_noise_cal) | ||
827 | return; | ||
828 | |||
829 | data = &(priv->chain_noise_data); | ||
830 | |||
831 | /* | ||
832 | * Accumulate just the first "chain_noise_num_beacons" after | ||
833 | * the first association, then we're done forever. | ||
834 | */ | ||
835 | if (data->state != IWL_CHAIN_NOISE_ACCUMULATE) { | ||
836 | if (data->state == IWL_CHAIN_NOISE_ALIVE) | ||
837 | IWL_DEBUG_CALIB(priv, "Wait for noise calib reset\n"); | ||
838 | return; | ||
839 | } | ||
840 | |||
841 | spin_lock_irqsave(&priv->lock, flags); | ||
842 | |||
843 | rx_info = &(((struct iwl_notif_statistics *)stat_resp)-> | ||
844 | rx.general); | ||
845 | |||
846 | if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) { | ||
847 | IWL_DEBUG_CALIB(priv, " << Interference data unavailable\n"); | ||
848 | spin_unlock_irqrestore(&priv->lock, flags); | ||
849 | return; | ||
850 | } | ||
851 | |||
852 | rxon_band24 = !!(ctx->staging.flags & RXON_FLG_BAND_24G_MSK); | ||
853 | rxon_chnum = le16_to_cpu(ctx->staging.channel); | ||
854 | |||
855 | stat_band24 = !!(((struct iwl_notif_statistics *) | ||
856 | stat_resp)->flag & | ||
857 | STATISTICS_REPLY_FLG_BAND_24G_MSK); | ||
858 | stat_chnum = le32_to_cpu(((struct iwl_notif_statistics *) | ||
859 | stat_resp)->flag) >> 16; | ||
860 | |||
861 | /* Make sure we accumulate data for just the associated channel | ||
862 | * (even if scanning). */ | ||
863 | if ((rxon_chnum != stat_chnum) || (rxon_band24 != stat_band24)) { | ||
864 | IWL_DEBUG_CALIB(priv, "Stats not from chan=%d, band24=%d\n", | ||
865 | rxon_chnum, rxon_band24); | ||
866 | spin_unlock_irqrestore(&priv->lock, flags); | ||
867 | return; | ||
868 | } | ||
869 | |||
870 | /* | ||
871 | * Accumulate beacon statistics values across | ||
872 | * "chain_noise_num_beacons" | ||
873 | */ | ||
874 | chain_noise_a = le32_to_cpu(rx_info->beacon_silence_rssi_a) & | ||
875 | IN_BAND_FILTER; | ||
876 | chain_noise_b = le32_to_cpu(rx_info->beacon_silence_rssi_b) & | ||
877 | IN_BAND_FILTER; | ||
878 | chain_noise_c = le32_to_cpu(rx_info->beacon_silence_rssi_c) & | ||
879 | IN_BAND_FILTER; | ||
880 | |||
881 | chain_sig_a = le32_to_cpu(rx_info->beacon_rssi_a) & IN_BAND_FILTER; | ||
882 | chain_sig_b = le32_to_cpu(rx_info->beacon_rssi_b) & IN_BAND_FILTER; | ||
883 | chain_sig_c = le32_to_cpu(rx_info->beacon_rssi_c) & IN_BAND_FILTER; | ||
884 | |||
885 | spin_unlock_irqrestore(&priv->lock, flags); | ||
886 | |||
887 | data->beacon_count++; | ||
888 | |||
889 | data->chain_noise_a = (chain_noise_a + data->chain_noise_a); | ||
890 | data->chain_noise_b = (chain_noise_b + data->chain_noise_b); | ||
891 | data->chain_noise_c = (chain_noise_c + data->chain_noise_c); | ||
892 | |||
893 | data->chain_signal_a = (chain_sig_a + data->chain_signal_a); | ||
894 | data->chain_signal_b = (chain_sig_b + data->chain_signal_b); | ||
895 | data->chain_signal_c = (chain_sig_c + data->chain_signal_c); | ||
896 | |||
897 | IWL_DEBUG_CALIB(priv, "chan=%d, band24=%d, beacon=%d\n", | ||
898 | rxon_chnum, rxon_band24, data->beacon_count); | ||
899 | IWL_DEBUG_CALIB(priv, "chain_sig: a %d b %d c %d\n", | ||
900 | chain_sig_a, chain_sig_b, chain_sig_c); | ||
901 | IWL_DEBUG_CALIB(priv, "chain_noise: a %d b %d c %d\n", | ||
902 | chain_noise_a, chain_noise_b, chain_noise_c); | ||
903 | |||
904 | /* If this is the "chain_noise_num_beacons", determine: | ||
905 | * 1) Disconnected antennas (using signal strengths) | ||
906 | * 2) Differential gain (using silence noise) to balance receivers */ | ||
907 | if (data->beacon_count != | ||
908 | priv->cfg->base_params->chain_noise_num_beacons) | ||
909 | return; | ||
910 | |||
911 | /* Analyze signal for disconnected antenna */ | ||
912 | iwl4965_find_disconn_antenna(priv, average_sig, data); | ||
913 | |||
914 | /* Analyze noise for rx balance */ | ||
915 | average_noise[0] = data->chain_noise_a / | ||
916 | priv->cfg->base_params->chain_noise_num_beacons; | ||
917 | average_noise[1] = data->chain_noise_b / | ||
918 | priv->cfg->base_params->chain_noise_num_beacons; | ||
919 | average_noise[2] = data->chain_noise_c / | ||
920 | priv->cfg->base_params->chain_noise_num_beacons; | ||
921 | |||
922 | for (i = 0; i < NUM_RX_CHAINS; i++) { | ||
923 | if (!(data->disconn_array[i]) && | ||
924 | (average_noise[i] <= min_average_noise)) { | ||
925 | /* This means that chain i is active and has | ||
926 | * lower noise values so far: */ | ||
927 | min_average_noise = average_noise[i]; | ||
928 | min_average_noise_antenna_i = i; | ||
929 | } | ||
930 | } | ||
931 | |||
932 | IWL_DEBUG_CALIB(priv, "average_noise: a %d b %d c %d\n", | ||
933 | average_noise[0], average_noise[1], | ||
934 | average_noise[2]); | ||
935 | |||
936 | IWL_DEBUG_CALIB(priv, "min_average_noise = %d, antenna %d\n", | ||
937 | min_average_noise, min_average_noise_antenna_i); | ||
938 | |||
939 | iwl4965_gain_computation(priv, average_noise, | ||
940 | min_average_noise_antenna_i, min_average_noise, | ||
941 | iwl4965_find_first_chain(priv->cfg->valid_rx_ant)); | ||
942 | |||
943 | /* Some power changes may have been made during the calibration. | ||
944 | * Update and commit the RXON | ||
945 | */ | ||
946 | if (priv->cfg->ops->lib->update_chain_flags) | ||
947 | priv->cfg->ops->lib->update_chain_flags(priv); | ||
948 | |||
949 | data->state = IWL_CHAIN_NOISE_DONE; | ||
950 | iwl_legacy_power_update_mode(priv, false); | ||
951 | } | ||
952 | |||
953 | void iwl4965_reset_run_time_calib(struct iwl_priv *priv) | ||
954 | { | ||
955 | int i; | ||
956 | memset(&(priv->sensitivity_data), 0, | ||
957 | sizeof(struct iwl_sensitivity_data)); | ||
958 | memset(&(priv->chain_noise_data), 0, | ||
959 | sizeof(struct iwl_chain_noise_data)); | ||
960 | for (i = 0; i < NUM_RX_CHAINS; i++) | ||
961 | priv->chain_noise_data.delta_gain_code[i] = | ||
962 | CHAIN_NOISE_DELTA_GAIN_INIT_VAL; | ||
963 | |||
964 | /* Ask for statistics now, the uCode will send notification | ||
965 | * periodically after association */ | ||
966 | iwl_legacy_send_statistics_request(priv, CMD_ASYNC, true); | ||
967 | } | ||