4965-calib.c (30734B)
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 "common.h" 67#include "4965.h" 68 69/***************************************************************************** 70 * INIT calibrations framework 71 *****************************************************************************/ 72 73struct stats_general_data { 74 u32 beacon_silence_rssi_a; 75 u32 beacon_silence_rssi_b; 76 u32 beacon_silence_rssi_c; 77 u32 beacon_energy_a; 78 u32 beacon_energy_b; 79 u32 beacon_energy_c; 80}; 81 82/***************************************************************************** 83 * RUNTIME calibrations framework 84 *****************************************************************************/ 85 86/* "false alarms" are signals that our DSP tries to lock onto, 87 * but then determines that they are either noise, or transmissions 88 * from a distant wireless network (also "noise", really) that get 89 * "stepped on" by stronger transmissions within our own network. 90 * This algorithm attempts to set a sensitivity level that is high 91 * enough to receive all of our own network traffic, but not so 92 * high that our DSP gets too busy trying to lock onto non-network 93 * activity/noise. */ 94static int 95il4965_sens_energy_cck(struct il_priv *il, u32 norm_fa, u32 rx_enable_time, 96 struct stats_general_data *rx_info) 97{ 98 u32 max_nrg_cck = 0; 99 int i = 0; 100 u8 max_silence_rssi = 0; 101 u32 silence_ref = 0; 102 u8 silence_rssi_a = 0; 103 u8 silence_rssi_b = 0; 104 u8 silence_rssi_c = 0; 105 u32 val; 106 107 /* "false_alarms" values below are cross-multiplications to assess the 108 * numbers of false alarms within the measured period of actual Rx 109 * (Rx is off when we're txing), vs the min/max expected false alarms 110 * (some should be expected if rx is sensitive enough) in a 111 * hypothetical listening period of 200 time units (TU), 204.8 msec: 112 * 113 * MIN_FA/fixed-time < false_alarms/actual-rx-time < MAX_FA/beacon-time 114 * 115 * */ 116 u32 false_alarms = norm_fa * 200 * 1024; 117 u32 max_false_alarms = MAX_FA_CCK * rx_enable_time; 118 u32 min_false_alarms = MIN_FA_CCK * rx_enable_time; 119 struct il_sensitivity_data *data = NULL; 120 const struct il_sensitivity_ranges *ranges = il->hw_params.sens; 121 122 data = &(il->sensitivity_data); 123 124 data->nrg_auto_corr_silence_diff = 0; 125 126 /* Find max silence rssi among all 3 receivers. 127 * This is background noise, which may include transmissions from other 128 * networks, measured during silence before our network's beacon */ 129 silence_rssi_a = 130 (u8) ((rx_info->beacon_silence_rssi_a & ALL_BAND_FILTER) >> 8); 131 silence_rssi_b = 132 (u8) ((rx_info->beacon_silence_rssi_b & ALL_BAND_FILTER) >> 8); 133 silence_rssi_c = 134 (u8) ((rx_info->beacon_silence_rssi_c & ALL_BAND_FILTER) >> 8); 135 136 val = max(silence_rssi_b, silence_rssi_c); 137 max_silence_rssi = max(silence_rssi_a, (u8) val); 138 139 /* Store silence rssi in 20-beacon history table */ 140 data->nrg_silence_rssi[data->nrg_silence_idx] = max_silence_rssi; 141 data->nrg_silence_idx++; 142 if (data->nrg_silence_idx >= NRG_NUM_PREV_STAT_L) 143 data->nrg_silence_idx = 0; 144 145 /* Find max silence rssi across 20 beacon history */ 146 for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) { 147 val = data->nrg_silence_rssi[i]; 148 silence_ref = max(silence_ref, val); 149 } 150 D_CALIB("silence a %u, b %u, c %u, 20-bcn max %u\n", silence_rssi_a, 151 silence_rssi_b, silence_rssi_c, silence_ref); 152 153 /* Find max rx energy (min value!) among all 3 receivers, 154 * measured during beacon frame. 155 * Save it in 10-beacon history table. */ 156 i = data->nrg_energy_idx; 157 val = min(rx_info->beacon_energy_b, rx_info->beacon_energy_c); 158 data->nrg_value[i] = min(rx_info->beacon_energy_a, val); 159 160 data->nrg_energy_idx++; 161 if (data->nrg_energy_idx >= 10) 162 data->nrg_energy_idx = 0; 163 164 /* Find min rx energy (max value) across 10 beacon history. 165 * This is the minimum signal level that we want to receive well. 166 * Add backoff (margin so we don't miss slightly lower energy frames). 167 * This establishes an upper bound (min value) for energy threshold. */ 168 max_nrg_cck = data->nrg_value[0]; 169 for (i = 1; i < 10; i++) 170 max_nrg_cck = (u32) max(max_nrg_cck, (data->nrg_value[i])); 171 max_nrg_cck += 6; 172 173 D_CALIB("rx energy a %u, b %u, c %u, 10-bcn max/min %u\n", 174 rx_info->beacon_energy_a, rx_info->beacon_energy_b, 175 rx_info->beacon_energy_c, max_nrg_cck - 6); 176 177 /* Count number of consecutive beacons with fewer-than-desired 178 * false alarms. */ 179 if (false_alarms < min_false_alarms) 180 data->num_in_cck_no_fa++; 181 else 182 data->num_in_cck_no_fa = 0; 183 D_CALIB("consecutive bcns with few false alarms = %u\n", 184 data->num_in_cck_no_fa); 185 186 /* If we got too many false alarms this time, reduce sensitivity */ 187 if (false_alarms > max_false_alarms && 188 data->auto_corr_cck > AUTO_CORR_MAX_TH_CCK) { 189 D_CALIB("norm FA %u > max FA %u\n", false_alarms, 190 max_false_alarms); 191 D_CALIB("... reducing sensitivity\n"); 192 data->nrg_curr_state = IL_FA_TOO_MANY; 193 /* Store for "fewer than desired" on later beacon */ 194 data->nrg_silence_ref = silence_ref; 195 196 /* increase energy threshold (reduce nrg value) 197 * to decrease sensitivity */ 198 data->nrg_th_cck = data->nrg_th_cck - NRG_STEP_CCK; 199 /* Else if we got fewer than desired, increase sensitivity */ 200 } else if (false_alarms < min_false_alarms) { 201 data->nrg_curr_state = IL_FA_TOO_FEW; 202 203 /* Compare silence level with silence level for most recent 204 * healthy number or too many false alarms */ 205 data->nrg_auto_corr_silence_diff = 206 (s32) data->nrg_silence_ref - (s32) silence_ref; 207 208 D_CALIB("norm FA %u < min FA %u, silence diff %d\n", 209 false_alarms, min_false_alarms, 210 data->nrg_auto_corr_silence_diff); 211 212 /* Increase value to increase sensitivity, but only if: 213 * 1a) previous beacon did *not* have *too many* false alarms 214 * 1b) AND there's a significant difference in Rx levels 215 * from a previous beacon with too many, or healthy # FAs 216 * OR 2) We've seen a lot of beacons (100) with too few 217 * false alarms */ 218 if (data->nrg_prev_state != IL_FA_TOO_MANY && 219 (data->nrg_auto_corr_silence_diff > NRG_DIFF || 220 data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA)) { 221 222 D_CALIB("... increasing sensitivity\n"); 223 /* Increase nrg value to increase sensitivity */ 224 val = data->nrg_th_cck + NRG_STEP_CCK; 225 data->nrg_th_cck = min((u32) ranges->min_nrg_cck, val); 226 } else { 227 D_CALIB("... but not changing sensitivity\n"); 228 } 229 230 /* Else we got a healthy number of false alarms, keep status quo */ 231 } else { 232 D_CALIB(" FA in safe zone\n"); 233 data->nrg_curr_state = IL_FA_GOOD_RANGE; 234 235 /* Store for use in "fewer than desired" with later beacon */ 236 data->nrg_silence_ref = silence_ref; 237 238 /* If previous beacon had too many false alarms, 239 * give it some extra margin by reducing sensitivity again 240 * (but don't go below measured energy of desired Rx) */ 241 if (IL_FA_TOO_MANY == data->nrg_prev_state) { 242 D_CALIB("... increasing margin\n"); 243 if (data->nrg_th_cck > (max_nrg_cck + NRG_MARGIN)) 244 data->nrg_th_cck -= NRG_MARGIN; 245 else 246 data->nrg_th_cck = max_nrg_cck; 247 } 248 } 249 250 /* Make sure the energy threshold does not go above the measured 251 * energy of the desired Rx signals (reduced by backoff margin), 252 * or else we might start missing Rx frames. 253 * Lower value is higher energy, so we use max()! 254 */ 255 data->nrg_th_cck = max(max_nrg_cck, data->nrg_th_cck); 256 D_CALIB("new nrg_th_cck %u\n", data->nrg_th_cck); 257 258 data->nrg_prev_state = data->nrg_curr_state; 259 260 /* Auto-correlation CCK algorithm */ 261 if (false_alarms > min_false_alarms) { 262 263 /* increase auto_corr values to decrease sensitivity 264 * so the DSP won't be disturbed by the noise 265 */ 266 if (data->auto_corr_cck < AUTO_CORR_MAX_TH_CCK) 267 data->auto_corr_cck = AUTO_CORR_MAX_TH_CCK + 1; 268 else { 269 val = data->auto_corr_cck + AUTO_CORR_STEP_CCK; 270 data->auto_corr_cck = 271 min((u32) ranges->auto_corr_max_cck, val); 272 } 273 val = data->auto_corr_cck_mrc + AUTO_CORR_STEP_CCK; 274 data->auto_corr_cck_mrc = 275 min((u32) ranges->auto_corr_max_cck_mrc, val); 276 } else if (false_alarms < min_false_alarms && 277 (data->nrg_auto_corr_silence_diff > NRG_DIFF || 278 data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA)) { 279 280 /* Decrease auto_corr values to increase sensitivity */ 281 val = data->auto_corr_cck - AUTO_CORR_STEP_CCK; 282 data->auto_corr_cck = max((u32) ranges->auto_corr_min_cck, val); 283 val = data->auto_corr_cck_mrc - AUTO_CORR_STEP_CCK; 284 data->auto_corr_cck_mrc = 285 max((u32) ranges->auto_corr_min_cck_mrc, val); 286 } 287 288 return 0; 289} 290 291static int 292il4965_sens_auto_corr_ofdm(struct il_priv *il, u32 norm_fa, u32 rx_enable_time) 293{ 294 u32 val; 295 u32 false_alarms = norm_fa * 200 * 1024; 296 u32 max_false_alarms = MAX_FA_OFDM * rx_enable_time; 297 u32 min_false_alarms = MIN_FA_OFDM * rx_enable_time; 298 struct il_sensitivity_data *data = NULL; 299 const struct il_sensitivity_ranges *ranges = il->hw_params.sens; 300 301 data = &(il->sensitivity_data); 302 303 /* If we got too many false alarms this time, reduce sensitivity */ 304 if (false_alarms > max_false_alarms) { 305 306 D_CALIB("norm FA %u > max FA %u)\n", false_alarms, 307 max_false_alarms); 308 309 val = data->auto_corr_ofdm + AUTO_CORR_STEP_OFDM; 310 data->auto_corr_ofdm = 311 min((u32) ranges->auto_corr_max_ofdm, val); 312 313 val = data->auto_corr_ofdm_mrc + AUTO_CORR_STEP_OFDM; 314 data->auto_corr_ofdm_mrc = 315 min((u32) ranges->auto_corr_max_ofdm_mrc, val); 316 317 val = data->auto_corr_ofdm_x1 + AUTO_CORR_STEP_OFDM; 318 data->auto_corr_ofdm_x1 = 319 min((u32) ranges->auto_corr_max_ofdm_x1, val); 320 321 val = data->auto_corr_ofdm_mrc_x1 + AUTO_CORR_STEP_OFDM; 322 data->auto_corr_ofdm_mrc_x1 = 323 min((u32) ranges->auto_corr_max_ofdm_mrc_x1, val); 324 } 325 326 /* Else if we got fewer than desired, increase sensitivity */ 327 else if (false_alarms < min_false_alarms) { 328 329 D_CALIB("norm FA %u < min FA %u\n", false_alarms, 330 min_false_alarms); 331 332 val = data->auto_corr_ofdm - AUTO_CORR_STEP_OFDM; 333 data->auto_corr_ofdm = 334 max((u32) ranges->auto_corr_min_ofdm, val); 335 336 val = data->auto_corr_ofdm_mrc - AUTO_CORR_STEP_OFDM; 337 data->auto_corr_ofdm_mrc = 338 max((u32) ranges->auto_corr_min_ofdm_mrc, val); 339 340 val = data->auto_corr_ofdm_x1 - AUTO_CORR_STEP_OFDM; 341 data->auto_corr_ofdm_x1 = 342 max((u32) ranges->auto_corr_min_ofdm_x1, val); 343 344 val = data->auto_corr_ofdm_mrc_x1 - AUTO_CORR_STEP_OFDM; 345 data->auto_corr_ofdm_mrc_x1 = 346 max((u32) ranges->auto_corr_min_ofdm_mrc_x1, val); 347 } else { 348 D_CALIB("min FA %u < norm FA %u < max FA %u OK\n", 349 min_false_alarms, false_alarms, max_false_alarms); 350 } 351 return 0; 352} 353 354static void 355il4965_prepare_legacy_sensitivity_tbl(struct il_priv *il, 356 struct il_sensitivity_data *data, 357 __le16 *tbl) 358{ 359 tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_IDX] = 360 cpu_to_le16((u16) data->auto_corr_ofdm); 361 tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_IDX] = 362 cpu_to_le16((u16) data->auto_corr_ofdm_mrc); 363 tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_IDX] = 364 cpu_to_le16((u16) data->auto_corr_ofdm_x1); 365 tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_IDX] = 366 cpu_to_le16((u16) data->auto_corr_ofdm_mrc_x1); 367 368 tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_IDX] = 369 cpu_to_le16((u16) data->auto_corr_cck); 370 tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_IDX] = 371 cpu_to_le16((u16) data->auto_corr_cck_mrc); 372 373 tbl[HD_MIN_ENERGY_CCK_DET_IDX] = cpu_to_le16((u16) data->nrg_th_cck); 374 tbl[HD_MIN_ENERGY_OFDM_DET_IDX] = cpu_to_le16((u16) data->nrg_th_ofdm); 375 376 tbl[HD_BARKER_CORR_TH_ADD_MIN_IDX] = 377 cpu_to_le16(data->barker_corr_th_min); 378 tbl[HD_BARKER_CORR_TH_ADD_MIN_MRC_IDX] = 379 cpu_to_le16(data->barker_corr_th_min_mrc); 380 tbl[HD_OFDM_ENERGY_TH_IN_IDX] = cpu_to_le16(data->nrg_th_cca); 381 382 D_CALIB("ofdm: ac %u mrc %u x1 %u mrc_x1 %u thresh %u\n", 383 data->auto_corr_ofdm, data->auto_corr_ofdm_mrc, 384 data->auto_corr_ofdm_x1, data->auto_corr_ofdm_mrc_x1, 385 data->nrg_th_ofdm); 386 387 D_CALIB("cck: ac %u mrc %u thresh %u\n", data->auto_corr_cck, 388 data->auto_corr_cck_mrc, data->nrg_th_cck); 389} 390 391/* Prepare a C_SENSITIVITY, send to uCode if values have changed */ 392static int 393il4965_sensitivity_write(struct il_priv *il) 394{ 395 struct il_sensitivity_cmd cmd; 396 struct il_sensitivity_data *data = NULL; 397 struct il_host_cmd cmd_out = { 398 .id = C_SENSITIVITY, 399 .len = sizeof(struct il_sensitivity_cmd), 400 .flags = CMD_ASYNC, 401 .data = &cmd, 402 }; 403 404 data = &(il->sensitivity_data); 405 406 memset(&cmd, 0, sizeof(cmd)); 407 408 il4965_prepare_legacy_sensitivity_tbl(il, data, &cmd.table[0]); 409 410 /* Update uCode's "work" table, and copy it to DSP */ 411 cmd.control = C_SENSITIVITY_CONTROL_WORK_TBL; 412 413 /* Don't send command to uCode if nothing has changed */ 414 if (!memcmp 415 (&cmd.table[0], &(il->sensitivity_tbl[0]), 416 sizeof(u16) * HD_TBL_SIZE)) { 417 D_CALIB("No change in C_SENSITIVITY\n"); 418 return 0; 419 } 420 421 /* Copy table for comparison next time */ 422 memcpy(&(il->sensitivity_tbl[0]), &(cmd.table[0]), 423 sizeof(u16) * HD_TBL_SIZE); 424 425 return il_send_cmd(il, &cmd_out); 426} 427 428void 429il4965_init_sensitivity(struct il_priv *il) 430{ 431 int ret = 0; 432 int i; 433 struct il_sensitivity_data *data = NULL; 434 const struct il_sensitivity_ranges *ranges = il->hw_params.sens; 435 436 if (il->disable_sens_cal) 437 return; 438 439 D_CALIB("Start il4965_init_sensitivity\n"); 440 441 /* Clear driver's sensitivity algo data */ 442 data = &(il->sensitivity_data); 443 444 if (ranges == NULL) 445 return; 446 447 memset(data, 0, sizeof(struct il_sensitivity_data)); 448 449 data->num_in_cck_no_fa = 0; 450 data->nrg_curr_state = IL_FA_TOO_MANY; 451 data->nrg_prev_state = IL_FA_TOO_MANY; 452 data->nrg_silence_ref = 0; 453 data->nrg_silence_idx = 0; 454 data->nrg_energy_idx = 0; 455 456 for (i = 0; i < 10; i++) 457 data->nrg_value[i] = 0; 458 459 for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) 460 data->nrg_silence_rssi[i] = 0; 461 462 data->auto_corr_ofdm = ranges->auto_corr_min_ofdm; 463 data->auto_corr_ofdm_mrc = ranges->auto_corr_min_ofdm_mrc; 464 data->auto_corr_ofdm_x1 = ranges->auto_corr_min_ofdm_x1; 465 data->auto_corr_ofdm_mrc_x1 = ranges->auto_corr_min_ofdm_mrc_x1; 466 data->auto_corr_cck = AUTO_CORR_CCK_MIN_VAL_DEF; 467 data->auto_corr_cck_mrc = ranges->auto_corr_min_cck_mrc; 468 data->nrg_th_cck = ranges->nrg_th_cck; 469 data->nrg_th_ofdm = ranges->nrg_th_ofdm; 470 data->barker_corr_th_min = ranges->barker_corr_th_min; 471 data->barker_corr_th_min_mrc = ranges->barker_corr_th_min_mrc; 472 data->nrg_th_cca = ranges->nrg_th_cca; 473 474 data->last_bad_plcp_cnt_ofdm = 0; 475 data->last_fa_cnt_ofdm = 0; 476 data->last_bad_plcp_cnt_cck = 0; 477 data->last_fa_cnt_cck = 0; 478 479 ret |= il4965_sensitivity_write(il); 480 D_CALIB("<<return 0x%X\n", ret); 481} 482 483void 484il4965_sensitivity_calibration(struct il_priv *il, void *resp) 485{ 486 u32 rx_enable_time; 487 u32 fa_cck; 488 u32 fa_ofdm; 489 u32 bad_plcp_cck; 490 u32 bad_plcp_ofdm; 491 u32 norm_fa_ofdm; 492 u32 norm_fa_cck; 493 struct il_sensitivity_data *data = NULL; 494 struct stats_rx_non_phy *rx_info; 495 struct stats_rx_phy *ofdm, *cck; 496 unsigned long flags; 497 struct stats_general_data statis; 498 499 if (il->disable_sens_cal) 500 return; 501 502 data = &(il->sensitivity_data); 503 504 if (!il_is_any_associated(il)) { 505 D_CALIB("<< - not associated\n"); 506 return; 507 } 508 509 spin_lock_irqsave(&il->lock, flags); 510 511 rx_info = &(((struct il_notif_stats *)resp)->rx.general); 512 ofdm = &(((struct il_notif_stats *)resp)->rx.ofdm); 513 cck = &(((struct il_notif_stats *)resp)->rx.cck); 514 515 if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) { 516 D_CALIB("<< invalid data.\n"); 517 spin_unlock_irqrestore(&il->lock, flags); 518 return; 519 } 520 521 /* Extract Statistics: */ 522 rx_enable_time = le32_to_cpu(rx_info->channel_load); 523 fa_cck = le32_to_cpu(cck->false_alarm_cnt); 524 fa_ofdm = le32_to_cpu(ofdm->false_alarm_cnt); 525 bad_plcp_cck = le32_to_cpu(cck->plcp_err); 526 bad_plcp_ofdm = le32_to_cpu(ofdm->plcp_err); 527 528 statis.beacon_silence_rssi_a = 529 le32_to_cpu(rx_info->beacon_silence_rssi_a); 530 statis.beacon_silence_rssi_b = 531 le32_to_cpu(rx_info->beacon_silence_rssi_b); 532 statis.beacon_silence_rssi_c = 533 le32_to_cpu(rx_info->beacon_silence_rssi_c); 534 statis.beacon_energy_a = le32_to_cpu(rx_info->beacon_energy_a); 535 statis.beacon_energy_b = le32_to_cpu(rx_info->beacon_energy_b); 536 statis.beacon_energy_c = le32_to_cpu(rx_info->beacon_energy_c); 537 538 spin_unlock_irqrestore(&il->lock, flags); 539 540 D_CALIB("rx_enable_time = %u usecs\n", rx_enable_time); 541 542 if (!rx_enable_time) { 543 D_CALIB("<< RX Enable Time == 0!\n"); 544 return; 545 } 546 547 /* These stats increase monotonically, and do not reset 548 * at each beacon. Calculate difference from last value, or just 549 * use the new stats value if it has reset or wrapped around. */ 550 if (data->last_bad_plcp_cnt_cck > bad_plcp_cck) 551 data->last_bad_plcp_cnt_cck = bad_plcp_cck; 552 else { 553 bad_plcp_cck -= data->last_bad_plcp_cnt_cck; 554 data->last_bad_plcp_cnt_cck += bad_plcp_cck; 555 } 556 557 if (data->last_bad_plcp_cnt_ofdm > bad_plcp_ofdm) 558 data->last_bad_plcp_cnt_ofdm = bad_plcp_ofdm; 559 else { 560 bad_plcp_ofdm -= data->last_bad_plcp_cnt_ofdm; 561 data->last_bad_plcp_cnt_ofdm += bad_plcp_ofdm; 562 } 563 564 if (data->last_fa_cnt_ofdm > fa_ofdm) 565 data->last_fa_cnt_ofdm = fa_ofdm; 566 else { 567 fa_ofdm -= data->last_fa_cnt_ofdm; 568 data->last_fa_cnt_ofdm += fa_ofdm; 569 } 570 571 if (data->last_fa_cnt_cck > fa_cck) 572 data->last_fa_cnt_cck = fa_cck; 573 else { 574 fa_cck -= data->last_fa_cnt_cck; 575 data->last_fa_cnt_cck += fa_cck; 576 } 577 578 /* Total aborted signal locks */ 579 norm_fa_ofdm = fa_ofdm + bad_plcp_ofdm; 580 norm_fa_cck = fa_cck + bad_plcp_cck; 581 582 D_CALIB("cck: fa %u badp %u ofdm: fa %u badp %u\n", fa_cck, 583 bad_plcp_cck, fa_ofdm, bad_plcp_ofdm); 584 585 il4965_sens_auto_corr_ofdm(il, norm_fa_ofdm, rx_enable_time); 586 il4965_sens_energy_cck(il, norm_fa_cck, rx_enable_time, &statis); 587 588 il4965_sensitivity_write(il); 589} 590 591static inline u8 592il4965_find_first_chain(u8 mask) 593{ 594 if (mask & ANT_A) 595 return CHAIN_A; 596 if (mask & ANT_B) 597 return CHAIN_B; 598 return CHAIN_C; 599} 600 601/* 602 * Run disconnected antenna algorithm to find out which antennas are 603 * disconnected. 604 */ 605static void 606il4965_find_disconn_antenna(struct il_priv *il, u32 * average_sig, 607 struct il_chain_noise_data *data) 608{ 609 u32 active_chains = 0; 610 u32 max_average_sig; 611 u16 max_average_sig_antenna_i; 612 u8 num_tx_chains; 613 u8 first_chain; 614 u16 i = 0; 615 616 average_sig[0] = 617 data->chain_signal_a / 618 il->cfg->chain_noise_num_beacons; 619 average_sig[1] = 620 data->chain_signal_b / 621 il->cfg->chain_noise_num_beacons; 622 average_sig[2] = 623 data->chain_signal_c / 624 il->cfg->chain_noise_num_beacons; 625 626 if (average_sig[0] >= average_sig[1]) { 627 max_average_sig = average_sig[0]; 628 max_average_sig_antenna_i = 0; 629 active_chains = (1 << max_average_sig_antenna_i); 630 } else { 631 max_average_sig = average_sig[1]; 632 max_average_sig_antenna_i = 1; 633 active_chains = (1 << max_average_sig_antenna_i); 634 } 635 636 if (average_sig[2] >= max_average_sig) { 637 max_average_sig = average_sig[2]; 638 max_average_sig_antenna_i = 2; 639 active_chains = (1 << max_average_sig_antenna_i); 640 } 641 642 D_CALIB("average_sig: a %d b %d c %d\n", average_sig[0], average_sig[1], 643 average_sig[2]); 644 D_CALIB("max_average_sig = %d, antenna %d\n", max_average_sig, 645 max_average_sig_antenna_i); 646 647 /* Compare signal strengths for all 3 receivers. */ 648 for (i = 0; i < NUM_RX_CHAINS; i++) { 649 if (i != max_average_sig_antenna_i) { 650 s32 rssi_delta = (max_average_sig - average_sig[i]); 651 652 /* If signal is very weak, compared with 653 * strongest, mark it as disconnected. */ 654 if (rssi_delta > MAXIMUM_ALLOWED_PATHLOSS) 655 data->disconn_array[i] = 1; 656 else 657 active_chains |= (1 << i); 658 D_CALIB("i = %d rssiDelta = %d " 659 "disconn_array[i] = %d\n", i, rssi_delta, 660 data->disconn_array[i]); 661 } 662 } 663 664 /* 665 * The above algorithm sometimes fails when the ucode 666 * reports 0 for all chains. It's not clear why that 667 * happens to start with, but it is then causing trouble 668 * because this can make us enable more chains than the 669 * hardware really has. 670 * 671 * To be safe, simply mask out any chains that we know 672 * are not on the device. 673 */ 674 active_chains &= il->hw_params.valid_rx_ant; 675 676 num_tx_chains = 0; 677 for (i = 0; i < NUM_RX_CHAINS; i++) { 678 /* loops on all the bits of 679 * il->hw_setting.valid_tx_ant */ 680 u8 ant_msk = (1 << i); 681 if (!(il->hw_params.valid_tx_ant & ant_msk)) 682 continue; 683 684 num_tx_chains++; 685 if (data->disconn_array[i] == 0) 686 /* there is a Tx antenna connected */ 687 break; 688 if (num_tx_chains == il->hw_params.tx_chains_num && 689 data->disconn_array[i]) { 690 /* 691 * If all chains are disconnected 692 * connect the first valid tx chain 693 */ 694 first_chain = 695 il4965_find_first_chain(il->cfg->valid_tx_ant); 696 data->disconn_array[first_chain] = 0; 697 active_chains |= BIT(first_chain); 698 D_CALIB("All Tx chains are disconnected" 699 "- declare %d as connected\n", first_chain); 700 break; 701 } 702 } 703 704 if (active_chains != il->hw_params.valid_rx_ant && 705 active_chains != il->chain_noise_data.active_chains) 706 D_CALIB("Detected that not all antennas are connected! " 707 "Connected: %#x, valid: %#x.\n", active_chains, 708 il->hw_params.valid_rx_ant); 709 710 /* Save for use within RXON, TX, SCAN commands, etc. */ 711 data->active_chains = active_chains; 712 D_CALIB("active_chains (bitwise) = 0x%x\n", active_chains); 713} 714 715static void 716il4965_gain_computation(struct il_priv *il, u32 * average_noise, 717 u16 min_average_noise_antenna_i, u32 min_average_noise, 718 u8 default_chain) 719{ 720 int i, ret; 721 struct il_chain_noise_data *data = &il->chain_noise_data; 722 723 data->delta_gain_code[min_average_noise_antenna_i] = 0; 724 725 for (i = default_chain; i < NUM_RX_CHAINS; i++) { 726 s32 delta_g = 0; 727 728 if (!data->disconn_array[i] && 729 data->delta_gain_code[i] == 730 CHAIN_NOISE_DELTA_GAIN_INIT_VAL) { 731 delta_g = average_noise[i] - min_average_noise; 732 data->delta_gain_code[i] = (u8) ((delta_g * 10) / 15); 733 data->delta_gain_code[i] = 734 min(data->delta_gain_code[i], 735 (u8) CHAIN_NOISE_MAX_DELTA_GAIN_CODE); 736 737 data->delta_gain_code[i] = 738 (data->delta_gain_code[i] | (1 << 2)); 739 } else { 740 data->delta_gain_code[i] = 0; 741 } 742 } 743 D_CALIB("delta_gain_codes: a %d b %d c %d\n", data->delta_gain_code[0], 744 data->delta_gain_code[1], data->delta_gain_code[2]); 745 746 /* Differential gain gets sent to uCode only once */ 747 if (!data->radio_write) { 748 struct il_calib_diff_gain_cmd cmd; 749 data->radio_write = 1; 750 751 memset(&cmd, 0, sizeof(cmd)); 752 cmd.hdr.op_code = IL_PHY_CALIBRATE_DIFF_GAIN_CMD; 753 cmd.diff_gain_a = data->delta_gain_code[0]; 754 cmd.diff_gain_b = data->delta_gain_code[1]; 755 cmd.diff_gain_c = data->delta_gain_code[2]; 756 ret = il_send_cmd_pdu(il, C_PHY_CALIBRATION, sizeof(cmd), &cmd); 757 if (ret) 758 D_CALIB("fail sending cmd " "C_PHY_CALIBRATION\n"); 759 760 /* TODO we might want recalculate 761 * rx_chain in rxon cmd */ 762 763 /* Mark so we run this algo only once! */ 764 data->state = IL_CHAIN_NOISE_CALIBRATED; 765 } 766} 767 768/* 769 * Accumulate 16 beacons of signal and noise stats for each of 770 * 3 receivers/antennas/rx-chains, then figure out: 771 * 1) Which antennas are connected. 772 * 2) Differential rx gain settings to balance the 3 receivers. 773 */ 774void 775il4965_chain_noise_calibration(struct il_priv *il, void *stat_resp) 776{ 777 struct il_chain_noise_data *data = NULL; 778 779 u32 chain_noise_a; 780 u32 chain_noise_b; 781 u32 chain_noise_c; 782 u32 chain_sig_a; 783 u32 chain_sig_b; 784 u32 chain_sig_c; 785 u32 average_sig[NUM_RX_CHAINS] = { INITIALIZATION_VALUE }; 786 u32 average_noise[NUM_RX_CHAINS] = { INITIALIZATION_VALUE }; 787 u32 min_average_noise = MIN_AVERAGE_NOISE_MAX_VALUE; 788 u16 min_average_noise_antenna_i = INITIALIZATION_VALUE; 789 u16 i = 0; 790 u16 rxon_chnum = INITIALIZATION_VALUE; 791 u16 stat_chnum = INITIALIZATION_VALUE; 792 u8 rxon_band24; 793 u8 stat_band24; 794 unsigned long flags; 795 struct stats_rx_non_phy *rx_info; 796 797 if (il->disable_chain_noise_cal) 798 return; 799 800 data = &(il->chain_noise_data); 801 802 /* 803 * Accumulate just the first "chain_noise_num_beacons" after 804 * the first association, then we're done forever. 805 */ 806 if (data->state != IL_CHAIN_NOISE_ACCUMULATE) { 807 if (data->state == IL_CHAIN_NOISE_ALIVE) 808 D_CALIB("Wait for noise calib reset\n"); 809 return; 810 } 811 812 spin_lock_irqsave(&il->lock, flags); 813 814 rx_info = &(((struct il_notif_stats *)stat_resp)->rx.general); 815 816 if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) { 817 D_CALIB(" << Interference data unavailable\n"); 818 spin_unlock_irqrestore(&il->lock, flags); 819 return; 820 } 821 822 rxon_band24 = !!(il->staging.flags & RXON_FLG_BAND_24G_MSK); 823 rxon_chnum = le16_to_cpu(il->staging.channel); 824 825 stat_band24 = 826 !!(((struct il_notif_stats *)stat_resp)-> 827 flag & STATS_REPLY_FLG_BAND_24G_MSK); 828 stat_chnum = 829 le32_to_cpu(((struct il_notif_stats *)stat_resp)->flag) >> 16; 830 831 /* Make sure we accumulate data for just the associated channel 832 * (even if scanning). */ 833 if (rxon_chnum != stat_chnum || rxon_band24 != stat_band24) { 834 D_CALIB("Stats not from chan=%d, band24=%d\n", rxon_chnum, 835 rxon_band24); 836 spin_unlock_irqrestore(&il->lock, flags); 837 return; 838 } 839 840 /* 841 * Accumulate beacon stats values across 842 * "chain_noise_num_beacons" 843 */ 844 chain_noise_a = 845 le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER; 846 chain_noise_b = 847 le32_to_cpu(rx_info->beacon_silence_rssi_b) & IN_BAND_FILTER; 848 chain_noise_c = 849 le32_to_cpu(rx_info->beacon_silence_rssi_c) & IN_BAND_FILTER; 850 851 chain_sig_a = le32_to_cpu(rx_info->beacon_rssi_a) & IN_BAND_FILTER; 852 chain_sig_b = le32_to_cpu(rx_info->beacon_rssi_b) & IN_BAND_FILTER; 853 chain_sig_c = le32_to_cpu(rx_info->beacon_rssi_c) & IN_BAND_FILTER; 854 855 spin_unlock_irqrestore(&il->lock, flags); 856 857 data->beacon_count++; 858 859 data->chain_noise_a = (chain_noise_a + data->chain_noise_a); 860 data->chain_noise_b = (chain_noise_b + data->chain_noise_b); 861 data->chain_noise_c = (chain_noise_c + data->chain_noise_c); 862 863 data->chain_signal_a = (chain_sig_a + data->chain_signal_a); 864 data->chain_signal_b = (chain_sig_b + data->chain_signal_b); 865 data->chain_signal_c = (chain_sig_c + data->chain_signal_c); 866 867 D_CALIB("chan=%d, band24=%d, beacon=%d\n", rxon_chnum, rxon_band24, 868 data->beacon_count); 869 D_CALIB("chain_sig: a %d b %d c %d\n", chain_sig_a, chain_sig_b, 870 chain_sig_c); 871 D_CALIB("chain_noise: a %d b %d c %d\n", chain_noise_a, chain_noise_b, 872 chain_noise_c); 873 874 /* If this is the "chain_noise_num_beacons", determine: 875 * 1) Disconnected antennas (using signal strengths) 876 * 2) Differential gain (using silence noise) to balance receivers */ 877 if (data->beacon_count != il->cfg->chain_noise_num_beacons) 878 return; 879 880 /* Analyze signal for disconnected antenna */ 881 il4965_find_disconn_antenna(il, average_sig, data); 882 883 /* Analyze noise for rx balance */ 884 average_noise[0] = 885 data->chain_noise_a / il->cfg->chain_noise_num_beacons; 886 average_noise[1] = 887 data->chain_noise_b / il->cfg->chain_noise_num_beacons; 888 average_noise[2] = 889 data->chain_noise_c / il->cfg->chain_noise_num_beacons; 890 891 for (i = 0; i < NUM_RX_CHAINS; i++) { 892 if (!data->disconn_array[i] && 893 average_noise[i] <= min_average_noise) { 894 /* This means that chain i is active and has 895 * lower noise values so far: */ 896 min_average_noise = average_noise[i]; 897 min_average_noise_antenna_i = i; 898 } 899 } 900 901 D_CALIB("average_noise: a %d b %d c %d\n", average_noise[0], 902 average_noise[1], average_noise[2]); 903 904 D_CALIB("min_average_noise = %d, antenna %d\n", min_average_noise, 905 min_average_noise_antenna_i); 906 907 il4965_gain_computation(il, average_noise, min_average_noise_antenna_i, 908 min_average_noise, 909 il4965_find_first_chain(il->cfg->valid_rx_ant)); 910 911 /* Some power changes may have been made during the calibration. 912 * Update and commit the RXON 913 */ 914 if (il->ops->update_chain_flags) 915 il->ops->update_chain_flags(il); 916 917 data->state = IL_CHAIN_NOISE_DONE; 918 il_power_update_mode(il, false); 919} 920 921void 922il4965_reset_run_time_calib(struct il_priv *il) 923{ 924 int i; 925 memset(&(il->sensitivity_data), 0, sizeof(struct il_sensitivity_data)); 926 memset(&(il->chain_noise_data), 0, sizeof(struct il_chain_noise_data)); 927 for (i = 0; i < NUM_RX_CHAINS; i++) 928 il->chain_noise_data.delta_gain_code[i] = 929 CHAIN_NOISE_DELTA_GAIN_INIT_VAL; 930 931 /* Ask for stats now, the uCode will send notification 932 * periodically after association */ 933 il_send_stats_request(il, CMD_ASYNC, true); 934}