root/drivers/net/wireless/intel/iwlegacy/4965-calib.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. il4965_sens_energy_cck
  2. il4965_sens_auto_corr_ofdm
  3. il4965_prepare_legacy_sensitivity_tbl
  4. il4965_sensitivity_write
  5. il4965_init_sensitivity
  6. il4965_sensitivity_calibration
  7. il4965_find_first_chain
  8. il4965_find_disconn_antenna
  9. il4965_gain_computation
  10. il4965_chain_noise_calibration
  11. il4965_reset_run_time_calib

   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 
  73 struct 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. */
  94 static int
  95 il4965_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 
 291 static int
 292 il4965_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 
 354 static void
 355 il4965_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 */
 392 static int
 393 il4965_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 
 428 void
 429 il4965_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 
 483 void
 484 il4965_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 
 591 static inline u8
 592 il4965_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  */
 605 static void
 606 il4965_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 
 715 static void
 716 il4965_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  */
 774 void
 775 il4965_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 
 921 void
 922 il4965_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 }

/* [<][>][^][v][top][bottom][index][help] */