4965-calib.c 30.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
/******************************************************************************
 *
 * This file is provided under a dual BSD/GPLv2 license.  When using or
 * redistributing this file, you may do so under either license.
 *
 * GPL LICENSE SUMMARY
 *
 * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
 * USA
 *
 * The full GNU General Public License is included in this distribution
 * in the file called LICENSE.GPL.
 *
 * Contact Information:
 *  Intel Linux Wireless <ilw@linux.intel.com>
 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
 *
 * BSD LICENSE
 *
 * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *  * Neither the name Intel Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *****************************************************************************/

#include <linux/slab.h>
#include <net/mac80211.h>

66
#include "common.h"
67
#include "4965.h"
68 69 70 71 72

/*****************************************************************************
 * INIT calibrations framework
 *****************************************************************************/

S
Stanislaw Gruszka 已提交
73
struct stats_general_data {
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
	u32 beacon_silence_rssi_a;
	u32 beacon_silence_rssi_b;
	u32 beacon_silence_rssi_c;
	u32 beacon_energy_a;
	u32 beacon_energy_b;
	u32 beacon_energy_c;
};

/*****************************************************************************
 * RUNTIME calibrations framework
 *****************************************************************************/

/* "false alarms" are signals that our DSP tries to lock onto,
 *   but then determines that they are either noise, or transmissions
 *   from a distant wireless network (also "noise", really) that get
 *   "stepped on" by stronger transmissions within our own network.
 * This algorithm attempts to set a sensitivity level that is high
 *   enough to receive all of our own network traffic, but not so
 *   high that our DSP gets too busy trying to lock onto non-network
 *   activity/noise. */
94 95 96
static int
il4965_sens_energy_cck(struct il_priv *il, u32 norm_fa, u32 rx_enable_time,
		       struct stats_general_data *rx_info)
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
{
	u32 max_nrg_cck = 0;
	int i = 0;
	u8 max_silence_rssi = 0;
	u32 silence_ref = 0;
	u8 silence_rssi_a = 0;
	u8 silence_rssi_b = 0;
	u8 silence_rssi_c = 0;
	u32 val;

	/* "false_alarms" values below are cross-multiplications to assess the
	 *   numbers of false alarms within the measured period of actual Rx
	 *   (Rx is off when we're txing), vs the min/max expected false alarms
	 *   (some should be expected if rx is sensitive enough) in a
	 *   hypothetical listening period of 200 time units (TU), 204.8 msec:
	 *
	 * MIN_FA/fixed-time < false_alarms/actual-rx-time < MAX_FA/beacon-time
	 *
	 * */
	u32 false_alarms = norm_fa * 200 * 1024;
	u32 max_false_alarms = MAX_FA_CCK * rx_enable_time;
	u32 min_false_alarms = MIN_FA_CCK * rx_enable_time;
S
Stanislaw Gruszka 已提交
119
	struct il_sensitivity_data *data = NULL;
S
Stanislaw Gruszka 已提交
120
	const struct il_sensitivity_ranges *ranges = il->hw_params.sens;
121

S
Stanislaw Gruszka 已提交
122
	data = &(il->sensitivity_data);
123 124 125 126 127 128

	data->nrg_auto_corr_silence_diff = 0;

	/* Find max silence rssi among all 3 receivers.
	 * This is background noise, which may include transmissions from other
	 *    networks, measured during silence before our network's beacon */
129 130 131 132 133 134
	silence_rssi_a =
	    (u8) ((rx_info->beacon_silence_rssi_a & ALL_BAND_FILTER) >> 8);
	silence_rssi_b =
	    (u8) ((rx_info->beacon_silence_rssi_b & ALL_BAND_FILTER) >> 8);
	silence_rssi_c =
	    (u8) ((rx_info->beacon_silence_rssi_c & ALL_BAND_FILTER) >> 8);
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149

	val = max(silence_rssi_b, silence_rssi_c);
	max_silence_rssi = max(silence_rssi_a, (u8) val);

	/* Store silence rssi in 20-beacon history table */
	data->nrg_silence_rssi[data->nrg_silence_idx] = max_silence_rssi;
	data->nrg_silence_idx++;
	if (data->nrg_silence_idx >= NRG_NUM_PREV_STAT_L)
		data->nrg_silence_idx = 0;

	/* Find max silence rssi across 20 beacon history */
	for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) {
		val = data->nrg_silence_rssi[i];
		silence_ref = max(silence_ref, val);
	}
150 151
	D_CALIB("silence a %u, b %u, c %u, 20-bcn max %u\n", silence_rssi_a,
		silence_rssi_b, silence_rssi_c, silence_ref);
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172

	/* Find max rx energy (min value!) among all 3 receivers,
	 *   measured during beacon frame.
	 * Save it in 10-beacon history table. */
	i = data->nrg_energy_idx;
	val = min(rx_info->beacon_energy_b, rx_info->beacon_energy_c);
	data->nrg_value[i] = min(rx_info->beacon_energy_a, val);

	data->nrg_energy_idx++;
	if (data->nrg_energy_idx >= 10)
		data->nrg_energy_idx = 0;

	/* Find min rx energy (max value) across 10 beacon history.
	 * This is the minimum signal level that we want to receive well.
	 * Add backoff (margin so we don't miss slightly lower energy frames).
	 * This establishes an upper bound (min value) for energy threshold. */
	max_nrg_cck = data->nrg_value[0];
	for (i = 1; i < 10; i++)
		max_nrg_cck = (u32) max(max_nrg_cck, (data->nrg_value[i]));
	max_nrg_cck += 6;

173
	D_CALIB("rx energy a %u, b %u, c %u, 10-bcn max/min %u\n",
174 175
		rx_info->beacon_energy_a, rx_info->beacon_energy_b,
		rx_info->beacon_energy_c, max_nrg_cck - 6);
176 177 178 179 180 181 182

	/* Count number of consecutive beacons with fewer-than-desired
	 *   false alarms. */
	if (false_alarms < min_false_alarms)
		data->num_in_cck_no_fa++;
	else
		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 188
	if (false_alarms > max_false_alarms &&
	    data->auto_corr_cck > AUTO_CORR_MAX_TH_CCK) {
189 190
		D_CALIB("norm FA %u > max FA %u\n", false_alarms,
			max_false_alarms);
191
		D_CALIB("... reducing sensitivity\n");
S
Stanislaw Gruszka 已提交
192
		data->nrg_curr_state = IL_FA_TOO_MANY;
193 194 195 196 197 198
		/* Store for "fewer than desired" on later beacon */
		data->nrg_silence_ref = silence_ref;

		/* increase energy threshold (reduce nrg value)
		 *   to decrease sensitivity */
		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) {
S
Stanislaw Gruszka 已提交
201
		data->nrg_curr_state = IL_FA_TOO_FEW;
202 203 204

		/* Compare silence level with silence level for most recent
		 *   healthy number or too many false alarms */
205 206
		data->nrg_auto_corr_silence_diff =
		    (s32) data->nrg_silence_ref - (s32) silence_ref;
207

208 209 210
		D_CALIB("norm FA %u < min FA %u, silence diff %d\n",
			false_alarms, min_false_alarms,
			data->nrg_auto_corr_silence_diff);
211 212 213 214 215 216 217

		/* Increase value to increase sensitivity, but only if:
		 * 1a) previous beacon did *not* have *too many* false alarms
		 * 1b) AND there's a significant difference in Rx levels
		 *      from a previous beacon with too many, or healthy # FAs
		 * OR 2) We've seen a lot of beacons (100) with too few
		 *       false alarms */
218 219 220
		if (data->nrg_prev_state != IL_FA_TOO_MANY &&
		    (data->nrg_auto_corr_silence_diff > NRG_DIFF ||
		     data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA)) {
221

222
			D_CALIB("... increasing sensitivity\n");
223 224
			/* Increase nrg value to increase sensitivity */
			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");
S
Stanislaw Gruszka 已提交
233
		data->nrg_curr_state = IL_FA_GOOD_RANGE;
234 235 236 237 238 239 240

		/* Store for use in "fewer than desired" with later beacon */
		data->nrg_silence_ref = silence_ref;

		/* If previous beacon had too many false alarms,
		 *   give it some extra margin by reducing sensitivity again
		 *   (but don't go below measured energy of desired Rx) */
S
Stanislaw Gruszka 已提交
241
		if (IL_FA_TOO_MANY == data->nrg_prev_state) {
242
			D_CALIB("... increasing margin\n");
243 244 245 246 247 248 249 250 251 252 253 254 255
			if (data->nrg_th_cck > (max_nrg_cck + NRG_MARGIN))
				data->nrg_th_cck -= NRG_MARGIN;
			else
				data->nrg_th_cck = max_nrg_cck;
		}
	}

	/* Make sure the energy threshold does not go above the measured
	 * energy of the desired Rx signals (reduced by backoff margin),
	 * or else we might start missing Rx frames.
	 * Lower value is higher energy, so we use max()!
	 */
	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 259 260 261 262 263 264 265 266 267 268 269 270

	data->nrg_prev_state = data->nrg_curr_state;

	/* Auto-correlation CCK algorithm */
	if (false_alarms > min_false_alarms) {

		/* increase auto_corr values to decrease sensitivity
		 * so the DSP won't be disturbed by the noise
		 */
		if (data->auto_corr_cck < AUTO_CORR_MAX_TH_CCK)
			data->auto_corr_cck = AUTO_CORR_MAX_TH_CCK + 1;
		else {
			val = data->auto_corr_cck + AUTO_CORR_STEP_CCK;
			data->auto_corr_cck =
271
			    min((u32) ranges->auto_corr_max_cck, val);
272 273 274
		}
		val = data->auto_corr_cck_mrc + AUTO_CORR_STEP_CCK;
		data->auto_corr_cck_mrc =
275
		    min((u32) ranges->auto_corr_max_cck_mrc, val);
276 277 278
	} else if (false_alarms < min_false_alarms &&
		   (data->nrg_auto_corr_silence_diff > NRG_DIFF ||
		    data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA)) {
279 280 281

		/* Decrease auto_corr values to increase sensitivity */
		val = data->auto_corr_cck - AUTO_CORR_STEP_CCK;
282
		data->auto_corr_cck = max((u32) ranges->auto_corr_min_cck, val);
283 284
		val = data->auto_corr_cck_mrc - AUTO_CORR_STEP_CCK;
		data->auto_corr_cck_mrc =
285
		    max((u32) ranges->auto_corr_min_cck_mrc, val);
286 287 288 289 290
	}

	return 0;
}

291 292
static int
il4965_sens_auto_corr_ofdm(struct il_priv *il, u32 norm_fa, u32 rx_enable_time)
293 294 295 296 297
{
	u32 val;
	u32 false_alarms = norm_fa * 200 * 1024;
	u32 max_false_alarms = MAX_FA_OFDM * rx_enable_time;
	u32 min_false_alarms = MIN_FA_OFDM * rx_enable_time;
S
Stanislaw Gruszka 已提交
298
	struct il_sensitivity_data *data = NULL;
S
Stanislaw Gruszka 已提交
299
	const struct il_sensitivity_ranges *ranges = il->hw_params.sens;
300

S
Stanislaw Gruszka 已提交
301
	data = &(il->sensitivity_data);
302 303 304 305

	/* If we got too many false alarms this time, reduce sensitivity */
	if (false_alarms > max_false_alarms) {

306 307
		D_CALIB("norm FA %u > max FA %u)\n", false_alarms,
			max_false_alarms);
308 309 310

		val = data->auto_corr_ofdm + AUTO_CORR_STEP_OFDM;
		data->auto_corr_ofdm =
311
		    min((u32) ranges->auto_corr_max_ofdm, val);
312 313 314

		val = data->auto_corr_ofdm_mrc + AUTO_CORR_STEP_OFDM;
		data->auto_corr_ofdm_mrc =
315
		    min((u32) ranges->auto_corr_max_ofdm_mrc, val);
316 317 318

		val = data->auto_corr_ofdm_x1 + AUTO_CORR_STEP_OFDM;
		data->auto_corr_ofdm_x1 =
319
		    min((u32) ranges->auto_corr_max_ofdm_x1, val);
320 321 322

		val = data->auto_corr_ofdm_mrc_x1 + AUTO_CORR_STEP_OFDM;
		data->auto_corr_ofdm_mrc_x1 =
323
		    min((u32) ranges->auto_corr_max_ofdm_mrc_x1, val);
324 325 326 327 328
	}

	/* Else if we got fewer than desired, increase sensitivity */
	else if (false_alarms < min_false_alarms) {

329 330
		D_CALIB("norm FA %u < min FA %u\n", false_alarms,
			min_false_alarms);
331 332 333

		val = data->auto_corr_ofdm - AUTO_CORR_STEP_OFDM;
		data->auto_corr_ofdm =
334
		    max((u32) ranges->auto_corr_min_ofdm, val);
335 336 337

		val = data->auto_corr_ofdm_mrc - AUTO_CORR_STEP_OFDM;
		data->auto_corr_ofdm_mrc =
338
		    max((u32) ranges->auto_corr_min_ofdm_mrc, val);
339 340 341

		val = data->auto_corr_ofdm_x1 - AUTO_CORR_STEP_OFDM;
		data->auto_corr_ofdm_x1 =
342
		    max((u32) ranges->auto_corr_min_ofdm_x1, val);
343 344 345

		val = data->auto_corr_ofdm_mrc_x1 - AUTO_CORR_STEP_OFDM;
		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 352 353
	}
	return 0;
}

354 355 356
static void
il4965_prepare_legacy_sensitivity_tbl(struct il_priv *il,
				      struct il_sensitivity_data *data,
S
Stanislaw Gruszka 已提交
357
				      __le16 *tbl)
358
{
S
Stanislaw Gruszka 已提交
359
	tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_IDX] =
360
	    cpu_to_le16((u16) data->auto_corr_ofdm);
S
Stanislaw Gruszka 已提交
361
	tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_IDX] =
362
	    cpu_to_le16((u16) data->auto_corr_ofdm_mrc);
S
Stanislaw Gruszka 已提交
363
	tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_IDX] =
364
	    cpu_to_le16((u16) data->auto_corr_ofdm_x1);
S
Stanislaw Gruszka 已提交
365
	tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_IDX] =
366
	    cpu_to_le16((u16) data->auto_corr_ofdm_mrc_x1);
367

S
Stanislaw Gruszka 已提交
368
	tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_IDX] =
369
	    cpu_to_le16((u16) data->auto_corr_cck);
S
Stanislaw Gruszka 已提交
370
	tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_IDX] =
371
	    cpu_to_le16((u16) data->auto_corr_cck_mrc);
372

373 374
	tbl[HD_MIN_ENERGY_CCK_DET_IDX] = cpu_to_le16((u16) data->nrg_th_cck);
	tbl[HD_MIN_ENERGY_OFDM_DET_IDX] = cpu_to_le16((u16) data->nrg_th_ofdm);
375

S
Stanislaw Gruszka 已提交
376
	tbl[HD_BARKER_CORR_TH_ADD_MIN_IDX] =
377
	    cpu_to_le16(data->barker_corr_th_min);
S
Stanislaw Gruszka 已提交
378
	tbl[HD_BARKER_CORR_TH_ADD_MIN_MRC_IDX] =
379 380
	    cpu_to_le16(data->barker_corr_th_min_mrc);
	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 384 385
		data->auto_corr_ofdm, data->auto_corr_ofdm_mrc,
		data->auto_corr_ofdm_x1, data->auto_corr_ofdm_mrc_x1,
		data->nrg_th_ofdm);
386

387 388
	D_CALIB("cck: ac %u mrc %u thresh %u\n", data->auto_corr_cck,
		data->auto_corr_cck_mrc, data->nrg_th_cck);
389 390
}

391
/* Prepare a C_SENSITIVITY, send to uCode if values have changed */
392 393
static int
il4965_sensitivity_write(struct il_priv *il)
394
{
S
Stanislaw Gruszka 已提交
395 396 397
	struct il_sensitivity_cmd cmd;
	struct il_sensitivity_data *data = NULL;
	struct il_host_cmd cmd_out = {
398
		.id = C_SENSITIVITY,
S
Stanislaw Gruszka 已提交
399
		.len = sizeof(struct il_sensitivity_cmd),
400 401 402 403
		.flags = CMD_ASYNC,
		.data = &cmd,
	};

S
Stanislaw Gruszka 已提交
404
	data = &(il->sensitivity_data);
405 406 407

	memset(&cmd, 0, sizeof(cmd));

S
Stanislaw Gruszka 已提交
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 415 416
	if (!memcmp
	    (&cmd.table[0], &(il->sensitivity_tbl[0]),
	     sizeof(u16) * HD_TBL_SIZE)) {
417
		D_CALIB("No change in C_SENSITIVITY\n");
418 419 420 421
		return 0;
	}

	/* Copy table for comparison next time */
S
Stanislaw Gruszka 已提交
422
	memcpy(&(il->sensitivity_tbl[0]), &(cmd.table[0]),
423
	       sizeof(u16) * HD_TBL_SIZE);
424

S
Stanislaw Gruszka 已提交
425
	return il_send_cmd(il, &cmd_out);
426 427
}

428 429
void
il4965_init_sensitivity(struct il_priv *il)
430 431 432
{
	int ret = 0;
	int i;
S
Stanislaw Gruszka 已提交
433
	struct il_sensitivity_data *data = NULL;
S
Stanislaw Gruszka 已提交
434
	const struct il_sensitivity_ranges *ranges = il->hw_params.sens;
435

S
Stanislaw Gruszka 已提交
436
	if (il->disable_sens_cal)
437 438
		return;

439
	D_CALIB("Start il4965_init_sensitivity\n");
440 441

	/* Clear driver's sensitivity algo data */
S
Stanislaw Gruszka 已提交
442
	data = &(il->sensitivity_data);
443 444 445 446

	if (ranges == NULL)
		return;

S
Stanislaw Gruszka 已提交
447
	memset(data, 0, sizeof(struct il_sensitivity_data));
448 449

	data->num_in_cck_no_fa = 0;
S
Stanislaw Gruszka 已提交
450 451
	data->nrg_curr_state = IL_FA_TOO_MANY;
	data->nrg_prev_state = IL_FA_TOO_MANY;
452 453 454 455 456 457 458 459 460 461
	data->nrg_silence_ref = 0;
	data->nrg_silence_idx = 0;
	data->nrg_energy_idx = 0;

	for (i = 0; i < 10; i++)
		data->nrg_value[i] = 0;

	for (i = 0; i < NRG_NUM_PREV_STAT_L; i++)
		data->nrg_silence_rssi[i] = 0;

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 466 467 468 469 470 471 472 473 474 475 476 477 478
	data->auto_corr_ofdm_mrc_x1 = ranges->auto_corr_min_ofdm_mrc_x1;
	data->auto_corr_cck = AUTO_CORR_CCK_MIN_VAL_DEF;
	data->auto_corr_cck_mrc = ranges->auto_corr_min_cck_mrc;
	data->nrg_th_cck = ranges->nrg_th_cck;
	data->nrg_th_ofdm = ranges->nrg_th_ofdm;
	data->barker_corr_th_min = ranges->barker_corr_th_min;
	data->barker_corr_th_min_mrc = ranges->barker_corr_th_min_mrc;
	data->nrg_th_cca = ranges->nrg_th_cca;

	data->last_bad_plcp_cnt_ofdm = 0;
	data->last_fa_cnt_ofdm = 0;
	data->last_bad_plcp_cnt_cck = 0;
	data->last_fa_cnt_cck = 0;

S
Stanislaw Gruszka 已提交
479
	ret |= il4965_sensitivity_write(il);
480
	D_CALIB("<<return 0x%X\n", ret);
481 482
}

483 484
void
il4965_sensitivity_calibration(struct il_priv *il, void *resp)
485 486 487 488 489 490 491 492
{
	u32 rx_enable_time;
	u32 fa_cck;
	u32 fa_ofdm;
	u32 bad_plcp_cck;
	u32 bad_plcp_ofdm;
	u32 norm_fa_ofdm;
	u32 norm_fa_cck;
S
Stanislaw Gruszka 已提交
493
	struct il_sensitivity_data *data = NULL;
S
Stanislaw Gruszka 已提交
494 495
	struct stats_rx_non_phy *rx_info;
	struct stats_rx_phy *ofdm, *cck;
496
	unsigned long flags;
S
Stanislaw Gruszka 已提交
497
	struct stats_general_data statis;
498

S
Stanislaw Gruszka 已提交
499
	if (il->disable_sens_cal)
500 501
		return;

S
Stanislaw Gruszka 已提交
502
	data = &(il->sensitivity_data);
503

S
Stanislaw Gruszka 已提交
504
	if (!il_is_any_associated(il)) {
505
		D_CALIB("<< - not associated\n");
506 507 508
		return;
	}

S
Stanislaw Gruszka 已提交
509
	spin_lock_irqsave(&il->lock, flags);
510

S
Stanislaw Gruszka 已提交
511 512 513
	rx_info = &(((struct il_notif_stats *)resp)->rx.general);
	ofdm = &(((struct il_notif_stats *)resp)->rx.ofdm);
	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");
S
Stanislaw Gruszka 已提交
517
		spin_unlock_irqrestore(&il->lock, flags);
518 519 520 521 522 523 524 525 526 527 528
		return;
	}

	/* Extract Statistics: */
	rx_enable_time = le32_to_cpu(rx_info->channel_load);
	fa_cck = le32_to_cpu(cck->false_alarm_cnt);
	fa_ofdm = le32_to_cpu(ofdm->false_alarm_cnt);
	bad_plcp_cck = le32_to_cpu(cck->plcp_err);
	bad_plcp_ofdm = le32_to_cpu(ofdm->plcp_err);

	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 534 535 536
	    le32_to_cpu(rx_info->beacon_silence_rssi_c);
	statis.beacon_energy_a = le32_to_cpu(rx_info->beacon_energy_a);
	statis.beacon_energy_b = le32_to_cpu(rx_info->beacon_energy_b);
	statis.beacon_energy_c = le32_to_cpu(rx_info->beacon_energy_c);
537

S
Stanislaw Gruszka 已提交
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 545 546
		return;
	}

S
Stanislaw Gruszka 已提交
547
	/* These stats increase monotonically, and do not reset
548
	 *   at each beacon.  Calculate difference from last value, or just
S
Stanislaw Gruszka 已提交
549
	 *   use the new stats value if it has reset or wrapped around. */
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
	if (data->last_bad_plcp_cnt_cck > bad_plcp_cck)
		data->last_bad_plcp_cnt_cck = bad_plcp_cck;
	else {
		bad_plcp_cck -= data->last_bad_plcp_cnt_cck;
		data->last_bad_plcp_cnt_cck += bad_plcp_cck;
	}

	if (data->last_bad_plcp_cnt_ofdm > bad_plcp_ofdm)
		data->last_bad_plcp_cnt_ofdm = bad_plcp_ofdm;
	else {
		bad_plcp_ofdm -= data->last_bad_plcp_cnt_ofdm;
		data->last_bad_plcp_cnt_ofdm += bad_plcp_ofdm;
	}

	if (data->last_fa_cnt_ofdm > fa_ofdm)
		data->last_fa_cnt_ofdm = fa_ofdm;
	else {
		fa_ofdm -= data->last_fa_cnt_ofdm;
		data->last_fa_cnt_ofdm += fa_ofdm;
	}

	if (data->last_fa_cnt_cck > fa_cck)
		data->last_fa_cnt_cck = fa_cck;
	else {
		fa_cck -= data->last_fa_cnt_cck;
		data->last_fa_cnt_cck += fa_cck;
	}

	/* Total aborted signal locks */
	norm_fa_ofdm = fa_ofdm + bad_plcp_ofdm;
	norm_fa_cck = fa_cck + bad_plcp_cck;

582 583
	D_CALIB("cck: fa %u badp %u  ofdm: fa %u badp %u\n", fa_cck,
		bad_plcp_cck, fa_ofdm, bad_plcp_ofdm);
584

S
Stanislaw Gruszka 已提交
585 586
	il4965_sens_auto_corr_ofdm(il, norm_fa_ofdm, rx_enable_time);
	il4965_sens_energy_cck(il, norm_fa_cck, rx_enable_time, &statis);
587

S
Stanislaw Gruszka 已提交
588
	il4965_sensitivity_write(il);
589 590
}

591 592
static inline u8
il4965_find_first_chain(u8 mask)
593 594 595 596 597 598 599 600 601 602 603 604 605
{
	if (mask & ANT_A)
		return CHAIN_A;
	if (mask & ANT_B)
		return CHAIN_B;
	return CHAIN_C;
}

/**
 * Run disconnected antenna algorithm to find out which antennas are
 * disconnected.
 */
static void
606 607
il4965_find_disconn_antenna(struct il_priv *il, u32 * average_sig,
			    struct il_chain_noise_data *data)
608 609 610 611 612 613 614 615
{
	u32 active_chains = 0;
	u32 max_average_sig;
	u16 max_average_sig_antenna_i;
	u8 num_tx_chains;
	u8 first_chain;
	u16 i = 0;

616 617
	average_sig[0] =
	    data->chain_signal_a /
618
	    il->cfg->chain_noise_num_beacons;
619 620
	average_sig[1] =
	    data->chain_signal_b /
621
	    il->cfg->chain_noise_num_beacons;
622 623
	average_sig[2] =
	    data->chain_signal_c /
624
	    il->cfg->chain_noise_num_beacons;
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641

	if (average_sig[0] >= average_sig[1]) {
		max_average_sig = average_sig[0];
		max_average_sig_antenna_i = 0;
		active_chains = (1 << max_average_sig_antenna_i);
	} else {
		max_average_sig = average_sig[1];
		max_average_sig_antenna_i = 1;
		active_chains = (1 << max_average_sig_antenna_i);
	}

	if (average_sig[2] >= max_average_sig) {
		max_average_sig = average_sig[2];
		max_average_sig_antenna_i = 2;
		active_chains = (1 << max_average_sig_antenna_i);
	}

642 643 644 645
	D_CALIB("average_sig: a %d b %d c %d\n", average_sig[0], average_sig[1],
		average_sig[2]);
	D_CALIB("max_average_sig = %d, antenna %d\n", max_average_sig,
		max_average_sig_antenna_i);
646 647 648 649 650 651 652 653 654 655 656 657

	/* Compare signal strengths for all 3 receivers. */
	for (i = 0; i < NUM_RX_CHAINS; i++) {
		if (i != max_average_sig_antenna_i) {
			s32 rssi_delta = (max_average_sig - average_sig[i]);

			/* If signal is very weak, compared with
			 * strongest, mark it as disconnected. */
			if (rssi_delta > MAXIMUM_ALLOWED_PATHLOSS)
				data->disconn_array[i] = 1;
			else
				active_chains |= (1 << i);
658
			D_CALIB("i = %d  rssiDelta = %d  "
659 660
				"disconn_array[i] = %d\n", i, rssi_delta,
				data->disconn_array[i]);
661 662 663 664 665 666 667 668 669 670 671 672 673
		}
	}

	/*
	 * The above algorithm sometimes fails when the ucode
	 * reports 0 for all chains. It's not clear why that
	 * happens to start with, but it is then causing trouble
	 * because this can make us enable more chains than the
	 * hardware really has.
	 *
	 * To be safe, simply mask out any chains that we know
	 * are not on the device.
	 */
S
Stanislaw Gruszka 已提交
674
	active_chains &= il->hw_params.valid_rx_ant;
675 676 677 678

	num_tx_chains = 0;
	for (i = 0; i < NUM_RX_CHAINS; i++) {
		/* loops on all the bits of
S
Stanislaw Gruszka 已提交
679
		 * il->hw_setting.valid_tx_ant */
680
		u8 ant_msk = (1 << i);
S
Stanislaw Gruszka 已提交
681
		if (!(il->hw_params.valid_tx_ant & ant_msk))
682 683 684 685 686 687
			continue;

		num_tx_chains++;
		if (data->disconn_array[i] == 0)
			/* there is a Tx antenna connected */
			break;
S
Stanislaw Gruszka 已提交
688
		if (num_tx_chains == il->hw_params.tx_chains_num &&
689 690 691 692 693 694
		    data->disconn_array[i]) {
			/*
			 * If all chains are disconnected
			 * connect the first valid tx chain
			 */
			first_chain =
695
			    il4965_find_first_chain(il->cfg->valid_tx_ant);
696 697
			data->disconn_array[first_chain] = 0;
			active_chains |= BIT(first_chain);
S
Stanislaw Gruszka 已提交
698 699
			D_CALIB("All Tx chains are disconnected"
				"- declare %d as connected\n", first_chain);
700 701 702 703
			break;
		}
	}

S
Stanislaw Gruszka 已提交
704 705
	if (active_chains != il->hw_params.valid_rx_ant &&
	    active_chains != il->chain_noise_data.active_chains)
706 707 708
		D_CALIB("Detected that not all antennas are connected! "
			"Connected: %#x, valid: %#x.\n", active_chains,
			il->hw_params.valid_rx_ant);
709 710 711

	/* Save for use within RXON, TX, SCAN commands, etc. */
	data->active_chains = active_chains;
712
	D_CALIB("active_chains (bitwise) = 0x%x\n", active_chains);
713 714
}

715 716 717 718
static void
il4965_gain_computation(struct il_priv *il, u32 * average_noise,
			u16 min_average_noise_antenna_i, u32 min_average_noise,
			u8 default_chain)
719 720
{
	int i, ret;
S
Stanislaw Gruszka 已提交
721
	struct il_chain_noise_data *data = &il->chain_noise_data;
722 723 724 725 726 727

	data->delta_gain_code[min_average_noise_antenna_i] = 0;

	for (i = default_chain; i < NUM_RX_CHAINS; i++) {
		s32 delta_g = 0;

728
		if (!data->disconn_array[i] &&
729 730
		    data->delta_gain_code[i] ==
		    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 736 737
				(u8) CHAIN_NOISE_MAX_DELTA_GAIN_CODE);

			data->delta_gain_code[i] =
738
			    (data->delta_gain_code[i] | (1 << 2));
739 740 741 742
		} else {
			data->delta_gain_code[i] = 0;
		}
	}
743 744
	D_CALIB("delta_gain_codes: a %d b %d c %d\n", data->delta_gain_code[0],
		data->delta_gain_code[1], data->delta_gain_code[2]);
745 746 747

	/* Differential gain gets sent to uCode only once */
	if (!data->radio_write) {
S
Stanislaw Gruszka 已提交
748
		struct il_calib_diff_gain_cmd cmd;
749 750 751
		data->radio_write = 1;

		memset(&cmd, 0, sizeof(cmd));
S
Stanislaw Gruszka 已提交
752
		cmd.hdr.op_code = IL_PHY_CALIBRATE_DIFF_GAIN_CMD;
753 754 755
		cmd.diff_gain_a = data->delta_gain_code[0];
		cmd.diff_gain_b = data->delta_gain_code[1];
		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 761 762 763

		/* TODO we might want recalculate
		 * rx_chain in rxon cmd */

		/* Mark so we run this algo only once! */
S
Stanislaw Gruszka 已提交
764
		data->state = IL_CHAIN_NOISE_CALIBRATED;
765 766 767 768
	}
}

/*
S
Stanislaw Gruszka 已提交
769
 * Accumulate 16 beacons of signal and noise stats for each of
770 771 772 773
 *   3 receivers/antennas/rx-chains, then figure out:
 * 1)  Which antennas are connected.
 * 2)  Differential rx gain settings to balance the 3 receivers.
 */
774 775
void
il4965_chain_noise_calibration(struct il_priv *il, void *stat_resp)
776
{
S
Stanislaw Gruszka 已提交
777
	struct il_chain_noise_data *data = NULL;
778 779 780 781 782 783 784

	u32 chain_noise_a;
	u32 chain_noise_b;
	u32 chain_noise_c;
	u32 chain_sig_a;
	u32 chain_sig_b;
	u32 chain_sig_c;
785 786
	u32 average_sig[NUM_RX_CHAINS] = { INITIALIZATION_VALUE };
	u32 average_noise[NUM_RX_CHAINS] = { INITIALIZATION_VALUE };
787 788 789 790 791 792 793 794
	u32 min_average_noise = MIN_AVERAGE_NOISE_MAX_VALUE;
	u16 min_average_noise_antenna_i = INITIALIZATION_VALUE;
	u16 i = 0;
	u16 rxon_chnum = INITIALIZATION_VALUE;
	u16 stat_chnum = INITIALIZATION_VALUE;
	u8 rxon_band24;
	u8 stat_band24;
	unsigned long flags;
S
Stanislaw Gruszka 已提交
795
	struct stats_rx_non_phy *rx_info;
796

S
Stanislaw Gruszka 已提交
797
	if (il->disable_chain_noise_cal)
798 799
		return;

S
Stanislaw Gruszka 已提交
800
	data = &(il->chain_noise_data);
801 802 803 804 805

	/*
	 * Accumulate just the first "chain_noise_num_beacons" after
	 * the first association, then we're done forever.
	 */
S
Stanislaw Gruszka 已提交
806 807
	if (data->state != IL_CHAIN_NOISE_ACCUMULATE) {
		if (data->state == IL_CHAIN_NOISE_ALIVE)
808
			D_CALIB("Wait for noise calib reset\n");
809 810 811
		return;
	}

S
Stanislaw Gruszka 已提交
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");
S
Stanislaw Gruszka 已提交
818
		spin_unlock_irqrestore(&il->lock, flags);
819 820 821
		return;
	}

822 823
	rxon_band24 = !!(il->staging.flags & RXON_FLG_BAND_24G_MSK);
	rxon_chnum = le16_to_cpu(il->staging.channel);
824

825 826 827 828 829
	stat_band24 =
	    !!(((struct il_notif_stats *)stat_resp)->
	       flag & STATS_REPLY_FLG_BAND_24G_MSK);
	stat_chnum =
	    le32_to_cpu(((struct il_notif_stats *)stat_resp)->flag) >> 16;
830 831 832

	/* Make sure we accumulate data for just the associated channel
	 *   (even if scanning). */
833
	if (rxon_chnum != stat_chnum || rxon_band24 != stat_band24) {
834 835
		D_CALIB("Stats not from chan=%d, band24=%d\n", rxon_chnum,
			rxon_band24);
S
Stanislaw Gruszka 已提交
836
		spin_unlock_irqrestore(&il->lock, flags);
837 838 839 840
		return;
	}

	/*
S
Stanislaw Gruszka 已提交
841
	 *  Accumulate beacon stats values across
842 843
	 * "chain_noise_num_beacons"
	 */
844 845 846 847 848 849
	chain_noise_a =
	    le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER;
	chain_noise_b =
	    le32_to_cpu(rx_info->beacon_silence_rssi_b) & IN_BAND_FILTER;
	chain_noise_c =
	    le32_to_cpu(rx_info->beacon_silence_rssi_c) & IN_BAND_FILTER;
850 851 852 853 854

	chain_sig_a = le32_to_cpu(rx_info->beacon_rssi_a) & IN_BAND_FILTER;
	chain_sig_b = le32_to_cpu(rx_info->beacon_rssi_b) & IN_BAND_FILTER;
	chain_sig_c = le32_to_cpu(rx_info->beacon_rssi_c) & IN_BAND_FILTER;

S
Stanislaw Gruszka 已提交
855
	spin_unlock_irqrestore(&il->lock, flags);
856 857 858 859 860 861 862 863 864 865 866

	data->beacon_count++;

	data->chain_noise_a = (chain_noise_a + data->chain_noise_a);
	data->chain_noise_b = (chain_noise_b + data->chain_noise_b);
	data->chain_noise_c = (chain_noise_c + data->chain_noise_c);

	data->chain_signal_a = (chain_sig_a + data->chain_signal_a);
	data->chain_signal_b = (chain_sig_b + data->chain_signal_b);
	data->chain_signal_c = (chain_sig_c + data->chain_signal_c);

867 868 869 870 871 872
	D_CALIB("chan=%d, band24=%d, beacon=%d\n", rxon_chnum, rxon_band24,
		data->beacon_count);
	D_CALIB("chain_sig: a %d b %d c %d\n", chain_sig_a, chain_sig_b,
		chain_sig_c);
	D_CALIB("chain_noise: a %d b %d c %d\n", chain_noise_a, chain_noise_b,
		chain_noise_c);
873 874 875 876

	/* If this is the "chain_noise_num_beacons", determine:
	 * 1)  Disconnected antennas (using signal strengths)
	 * 2)  Differential gain (using silence noise) to balance receivers */
877
	if (data->beacon_count != il->cfg->chain_noise_num_beacons)
878 879 880
		return;

	/* Analyze signal for disconnected antenna */
S
Stanislaw Gruszka 已提交
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 893
		if (!data->disconn_array[i] &&
		    average_noise[i] <= min_average_noise) {
894 895 896 897 898 899 900
			/* This means that chain i is active and has
			 * lower noise values so far: */
			min_average_noise = average_noise[i];
			min_average_noise_antenna_i = i;
		}
	}

901 902
	D_CALIB("average_noise: a %d b %d c %d\n", average_noise[0],
		average_noise[1], average_noise[2]);
903

904 905
	D_CALIB("min_average_noise = %d, antenna %d\n", min_average_noise,
		min_average_noise_antenna_i);
906

907 908 909
	il4965_gain_computation(il, average_noise, min_average_noise_antenna_i,
				min_average_noise,
				il4965_find_first_chain(il->cfg->valid_rx_ant));
910 911 912 913

	/* Some power changes may have been made during the calibration.
	 * Update and commit the RXON
	 */
914 915
	if (il->ops->update_chain_flags)
		il->ops->update_chain_flags(il);
916

S
Stanislaw Gruszka 已提交
917
	data->state = IL_CHAIN_NOISE_DONE;
S
Stanislaw Gruszka 已提交
918
	il_power_update_mode(il, false);
919 920
}

921 922
void
il4965_reset_run_time_calib(struct il_priv *il)
923 924
{
	int i;
925 926
	memset(&(il->sensitivity_data), 0, sizeof(struct il_sensitivity_data));
	memset(&(il->chain_noise_data), 0, sizeof(struct il_chain_noise_data));
927
	for (i = 0; i < NUM_RX_CHAINS; i++)
S
Stanislaw Gruszka 已提交
928
		il->chain_noise_data.delta_gain_code[i] =
929
		    CHAIN_NOISE_DELTA_GAIN_INIT_VAL;
930

S
Stanislaw Gruszka 已提交
931
	/* Ask for stats now, the uCode will send notification
932
	 * periodically after association */
S
Stanislaw Gruszka 已提交
933
	il_send_stats_request(il, CMD_ASYNC, true);
934
}