18c2ecf20Sopenharmony_ci/******************************************************************************
28c2ecf20Sopenharmony_ci *
38c2ecf20Sopenharmony_ci * This file is provided under a dual BSD/GPLv2 license.  When using or
48c2ecf20Sopenharmony_ci * redistributing this file, you may do so under either license.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * GPL LICENSE SUMMARY
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
118c2ecf20Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as
128c2ecf20Sopenharmony_ci * published by the Free Software Foundation.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but
158c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of
168c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
178c2ecf20Sopenharmony_ci * General Public License for more details.
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci * The full GNU General Public License is included in this distribution
208c2ecf20Sopenharmony_ci * in the file called COPYING.
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci * Contact Information:
238c2ecf20Sopenharmony_ci *  Intel Linux Wireless <linuxwifi@intel.com>
248c2ecf20Sopenharmony_ci * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
258c2ecf20Sopenharmony_ci *
268c2ecf20Sopenharmony_ci * BSD LICENSE
278c2ecf20Sopenharmony_ci *
288c2ecf20Sopenharmony_ci * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
298c2ecf20Sopenharmony_ci * All rights reserved.
308c2ecf20Sopenharmony_ci *
318c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
328c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions
338c2ecf20Sopenharmony_ci * are met:
348c2ecf20Sopenharmony_ci *
358c2ecf20Sopenharmony_ci *  * Redistributions of source code must retain the above copyright
368c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer.
378c2ecf20Sopenharmony_ci *  * Redistributions in binary form must reproduce the above copyright
388c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer in
398c2ecf20Sopenharmony_ci *    the documentation and/or other materials provided with the
408c2ecf20Sopenharmony_ci *    distribution.
418c2ecf20Sopenharmony_ci *  * Neither the name Intel Corporation nor the names of its
428c2ecf20Sopenharmony_ci *    contributors may be used to endorse or promote products derived
438c2ecf20Sopenharmony_ci *    from this software without specific prior written permission.
448c2ecf20Sopenharmony_ci *
458c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
468c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
478c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
488c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
498c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
508c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
518c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
528c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
538c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
548c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
558c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
568c2ecf20Sopenharmony_ci *****************************************************************************/
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci#include <linux/slab.h>
598c2ecf20Sopenharmony_ci#include <net/mac80211.h>
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci#include "iwl-trans.h"
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci#include "dev.h"
648c2ecf20Sopenharmony_ci#include "calib.h"
658c2ecf20Sopenharmony_ci#include "agn.h"
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci/*****************************************************************************
688c2ecf20Sopenharmony_ci * INIT calibrations framework
698c2ecf20Sopenharmony_ci *****************************************************************************/
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci/* Opaque calibration results */
728c2ecf20Sopenharmony_cistruct iwl_calib_result {
738c2ecf20Sopenharmony_ci	struct list_head list;
748c2ecf20Sopenharmony_ci	size_t cmd_len;
758c2ecf20Sopenharmony_ci	struct iwl_calib_hdr hdr;
768c2ecf20Sopenharmony_ci	/* data follows */
778c2ecf20Sopenharmony_ci};
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistruct statistics_general_data {
808c2ecf20Sopenharmony_ci	u32 beacon_silence_rssi_a;
818c2ecf20Sopenharmony_ci	u32 beacon_silence_rssi_b;
828c2ecf20Sopenharmony_ci	u32 beacon_silence_rssi_c;
838c2ecf20Sopenharmony_ci	u32 beacon_energy_a;
848c2ecf20Sopenharmony_ci	u32 beacon_energy_b;
858c2ecf20Sopenharmony_ci	u32 beacon_energy_c;
868c2ecf20Sopenharmony_ci};
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ciint iwl_send_calib_results(struct iwl_priv *priv)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	struct iwl_host_cmd hcmd = {
918c2ecf20Sopenharmony_ci		.id = REPLY_PHY_CALIBRATION_CMD,
928c2ecf20Sopenharmony_ci	};
938c2ecf20Sopenharmony_ci	struct iwl_calib_result *res;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	list_for_each_entry(res, &priv->calib_results, list) {
968c2ecf20Sopenharmony_ci		int ret;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci		hcmd.len[0] = res->cmd_len;
998c2ecf20Sopenharmony_ci		hcmd.data[0] = &res->hdr;
1008c2ecf20Sopenharmony_ci		hcmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY;
1018c2ecf20Sopenharmony_ci		ret = iwl_dvm_send_cmd(priv, &hcmd);
1028c2ecf20Sopenharmony_ci		if (ret) {
1038c2ecf20Sopenharmony_ci			IWL_ERR(priv, "Error %d on calib cmd %d\n",
1048c2ecf20Sopenharmony_ci				ret, res->hdr.op_code);
1058c2ecf20Sopenharmony_ci			return ret;
1068c2ecf20Sopenharmony_ci		}
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	return 0;
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ciint iwl_calib_set(struct iwl_priv *priv,
1138c2ecf20Sopenharmony_ci		  const struct iwl_calib_hdr *cmd, int len)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	struct iwl_calib_result *res, *tmp;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	res = kmalloc(sizeof(*res) + len - sizeof(struct iwl_calib_hdr),
1188c2ecf20Sopenharmony_ci		      GFP_ATOMIC);
1198c2ecf20Sopenharmony_ci	if (!res)
1208c2ecf20Sopenharmony_ci		return -ENOMEM;
1218c2ecf20Sopenharmony_ci	memcpy(&res->hdr, cmd, len);
1228c2ecf20Sopenharmony_ci	res->cmd_len = len;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	list_for_each_entry(tmp, &priv->calib_results, list) {
1258c2ecf20Sopenharmony_ci		if (tmp->hdr.op_code == res->hdr.op_code) {
1268c2ecf20Sopenharmony_ci			list_replace(&tmp->list, &res->list);
1278c2ecf20Sopenharmony_ci			kfree(tmp);
1288c2ecf20Sopenharmony_ci			return 0;
1298c2ecf20Sopenharmony_ci		}
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	/* wasn't in list already */
1338c2ecf20Sopenharmony_ci	list_add_tail(&res->list, &priv->calib_results);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	return 0;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_civoid iwl_calib_free_results(struct iwl_priv *priv)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	struct iwl_calib_result *res, *tmp;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	list_for_each_entry_safe(res, tmp, &priv->calib_results, list) {
1438c2ecf20Sopenharmony_ci		list_del(&res->list);
1448c2ecf20Sopenharmony_ci		kfree(res);
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci/*****************************************************************************
1498c2ecf20Sopenharmony_ci * RUNTIME calibrations framework
1508c2ecf20Sopenharmony_ci *****************************************************************************/
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci/* "false alarms" are signals that our DSP tries to lock onto,
1538c2ecf20Sopenharmony_ci *   but then determines that they are either noise, or transmissions
1548c2ecf20Sopenharmony_ci *   from a distant wireless network (also "noise", really) that get
1558c2ecf20Sopenharmony_ci *   "stepped on" by stronger transmissions within our own network.
1568c2ecf20Sopenharmony_ci * This algorithm attempts to set a sensitivity level that is high
1578c2ecf20Sopenharmony_ci *   enough to receive all of our own network traffic, but not so
1588c2ecf20Sopenharmony_ci *   high that our DSP gets too busy trying to lock onto non-network
1598c2ecf20Sopenharmony_ci *   activity/noise. */
1608c2ecf20Sopenharmony_cistatic int iwl_sens_energy_cck(struct iwl_priv *priv,
1618c2ecf20Sopenharmony_ci				   u32 norm_fa,
1628c2ecf20Sopenharmony_ci				   u32 rx_enable_time,
1638c2ecf20Sopenharmony_ci				   struct statistics_general_data *rx_info)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	u32 max_nrg_cck = 0;
1668c2ecf20Sopenharmony_ci	int i = 0;
1678c2ecf20Sopenharmony_ci	u8 max_silence_rssi = 0;
1688c2ecf20Sopenharmony_ci	u32 silence_ref = 0;
1698c2ecf20Sopenharmony_ci	u8 silence_rssi_a = 0;
1708c2ecf20Sopenharmony_ci	u8 silence_rssi_b = 0;
1718c2ecf20Sopenharmony_ci	u8 silence_rssi_c = 0;
1728c2ecf20Sopenharmony_ci	u32 val;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	/* "false_alarms" values below are cross-multiplications to assess the
1758c2ecf20Sopenharmony_ci	 *   numbers of false alarms within the measured period of actual Rx
1768c2ecf20Sopenharmony_ci	 *   (Rx is off when we're txing), vs the min/max expected false alarms
1778c2ecf20Sopenharmony_ci	 *   (some should be expected if rx is sensitive enough) in a
1788c2ecf20Sopenharmony_ci	 *   hypothetical listening period of 200 time units (TU), 204.8 msec:
1798c2ecf20Sopenharmony_ci	 *
1808c2ecf20Sopenharmony_ci	 * MIN_FA/fixed-time < false_alarms/actual-rx-time < MAX_FA/beacon-time
1818c2ecf20Sopenharmony_ci	 *
1828c2ecf20Sopenharmony_ci	 * */
1838c2ecf20Sopenharmony_ci	u32 false_alarms = norm_fa * 200 * 1024;
1848c2ecf20Sopenharmony_ci	u32 max_false_alarms = MAX_FA_CCK * rx_enable_time;
1858c2ecf20Sopenharmony_ci	u32 min_false_alarms = MIN_FA_CCK * rx_enable_time;
1868c2ecf20Sopenharmony_ci	struct iwl_sensitivity_data *data = NULL;
1878c2ecf20Sopenharmony_ci	const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	data = &(priv->sensitivity_data);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	data->nrg_auto_corr_silence_diff = 0;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	/* Find max silence rssi among all 3 receivers.
1948c2ecf20Sopenharmony_ci	 * This is background noise, which may include transmissions from other
1958c2ecf20Sopenharmony_ci	 *    networks, measured during silence before our network's beacon */
1968c2ecf20Sopenharmony_ci	silence_rssi_a = (u8)((rx_info->beacon_silence_rssi_a &
1978c2ecf20Sopenharmony_ci			    ALL_BAND_FILTER) >> 8);
1988c2ecf20Sopenharmony_ci	silence_rssi_b = (u8)((rx_info->beacon_silence_rssi_b &
1998c2ecf20Sopenharmony_ci			    ALL_BAND_FILTER) >> 8);
2008c2ecf20Sopenharmony_ci	silence_rssi_c = (u8)((rx_info->beacon_silence_rssi_c &
2018c2ecf20Sopenharmony_ci			    ALL_BAND_FILTER) >> 8);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	val = max(silence_rssi_b, silence_rssi_c);
2048c2ecf20Sopenharmony_ci	max_silence_rssi = max(silence_rssi_a, (u8) val);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	/* Store silence rssi in 20-beacon history table */
2078c2ecf20Sopenharmony_ci	data->nrg_silence_rssi[data->nrg_silence_idx] = max_silence_rssi;
2088c2ecf20Sopenharmony_ci	data->nrg_silence_idx++;
2098c2ecf20Sopenharmony_ci	if (data->nrg_silence_idx >= NRG_NUM_PREV_STAT_L)
2108c2ecf20Sopenharmony_ci		data->nrg_silence_idx = 0;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	/* Find max silence rssi across 20 beacon history */
2138c2ecf20Sopenharmony_ci	for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) {
2148c2ecf20Sopenharmony_ci		val = data->nrg_silence_rssi[i];
2158c2ecf20Sopenharmony_ci		silence_ref = max(silence_ref, val);
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci	IWL_DEBUG_CALIB(priv, "silence a %u, b %u, c %u, 20-bcn max %u\n",
2188c2ecf20Sopenharmony_ci			silence_rssi_a, silence_rssi_b, silence_rssi_c,
2198c2ecf20Sopenharmony_ci			silence_ref);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	/* Find max rx energy (min value!) among all 3 receivers,
2228c2ecf20Sopenharmony_ci	 *   measured during beacon frame.
2238c2ecf20Sopenharmony_ci	 * Save it in 10-beacon history table. */
2248c2ecf20Sopenharmony_ci	i = data->nrg_energy_idx;
2258c2ecf20Sopenharmony_ci	val = min(rx_info->beacon_energy_b, rx_info->beacon_energy_c);
2268c2ecf20Sopenharmony_ci	data->nrg_value[i] = min(rx_info->beacon_energy_a, val);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	data->nrg_energy_idx++;
2298c2ecf20Sopenharmony_ci	if (data->nrg_energy_idx >= 10)
2308c2ecf20Sopenharmony_ci		data->nrg_energy_idx = 0;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	/* Find min rx energy (max value) across 10 beacon history.
2338c2ecf20Sopenharmony_ci	 * This is the minimum signal level that we want to receive well.
2348c2ecf20Sopenharmony_ci	 * Add backoff (margin so we don't miss slightly lower energy frames).
2358c2ecf20Sopenharmony_ci	 * This establishes an upper bound (min value) for energy threshold. */
2368c2ecf20Sopenharmony_ci	max_nrg_cck = data->nrg_value[0];
2378c2ecf20Sopenharmony_ci	for (i = 1; i < 10; i++)
2388c2ecf20Sopenharmony_ci		max_nrg_cck = (u32) max(max_nrg_cck, (data->nrg_value[i]));
2398c2ecf20Sopenharmony_ci	max_nrg_cck += 6;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	IWL_DEBUG_CALIB(priv, "rx energy a %u, b %u, c %u, 10-bcn max/min %u\n",
2428c2ecf20Sopenharmony_ci			rx_info->beacon_energy_a, rx_info->beacon_energy_b,
2438c2ecf20Sopenharmony_ci			rx_info->beacon_energy_c, max_nrg_cck - 6);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	/* Count number of consecutive beacons with fewer-than-desired
2468c2ecf20Sopenharmony_ci	 *   false alarms. */
2478c2ecf20Sopenharmony_ci	if (false_alarms < min_false_alarms)
2488c2ecf20Sopenharmony_ci		data->num_in_cck_no_fa++;
2498c2ecf20Sopenharmony_ci	else
2508c2ecf20Sopenharmony_ci		data->num_in_cck_no_fa = 0;
2518c2ecf20Sopenharmony_ci	IWL_DEBUG_CALIB(priv, "consecutive bcns with few false alarms = %u\n",
2528c2ecf20Sopenharmony_ci			data->num_in_cck_no_fa);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	/* If we got too many false alarms this time, reduce sensitivity */
2558c2ecf20Sopenharmony_ci	if ((false_alarms > max_false_alarms) &&
2568c2ecf20Sopenharmony_ci		(data->auto_corr_cck > AUTO_CORR_MAX_TH_CCK)) {
2578c2ecf20Sopenharmony_ci		IWL_DEBUG_CALIB(priv, "norm FA %u > max FA %u\n",
2588c2ecf20Sopenharmony_ci		     false_alarms, max_false_alarms);
2598c2ecf20Sopenharmony_ci		IWL_DEBUG_CALIB(priv, "... reducing sensitivity\n");
2608c2ecf20Sopenharmony_ci		data->nrg_curr_state = IWL_FA_TOO_MANY;
2618c2ecf20Sopenharmony_ci		/* Store for "fewer than desired" on later beacon */
2628c2ecf20Sopenharmony_ci		data->nrg_silence_ref = silence_ref;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci		/* increase energy threshold (reduce nrg value)
2658c2ecf20Sopenharmony_ci		 *   to decrease sensitivity */
2668c2ecf20Sopenharmony_ci		data->nrg_th_cck = data->nrg_th_cck - NRG_STEP_CCK;
2678c2ecf20Sopenharmony_ci	/* Else if we got fewer than desired, increase sensitivity */
2688c2ecf20Sopenharmony_ci	} else if (false_alarms < min_false_alarms) {
2698c2ecf20Sopenharmony_ci		data->nrg_curr_state = IWL_FA_TOO_FEW;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci		/* Compare silence level with silence level for most recent
2728c2ecf20Sopenharmony_ci		 *   healthy number or too many false alarms */
2738c2ecf20Sopenharmony_ci		data->nrg_auto_corr_silence_diff = (s32)data->nrg_silence_ref -
2748c2ecf20Sopenharmony_ci						   (s32)silence_ref;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci		IWL_DEBUG_CALIB(priv, "norm FA %u < min FA %u, silence diff %d\n",
2778c2ecf20Sopenharmony_ci			 false_alarms, min_false_alarms,
2788c2ecf20Sopenharmony_ci			 data->nrg_auto_corr_silence_diff);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci		/* Increase value to increase sensitivity, but only if:
2818c2ecf20Sopenharmony_ci		 * 1a) previous beacon did *not* have *too many* false alarms
2828c2ecf20Sopenharmony_ci		 * 1b) AND there's a significant difference in Rx levels
2838c2ecf20Sopenharmony_ci		 *      from a previous beacon with too many, or healthy # FAs
2848c2ecf20Sopenharmony_ci		 * OR 2) We've seen a lot of beacons (100) with too few
2858c2ecf20Sopenharmony_ci		 *       false alarms */
2868c2ecf20Sopenharmony_ci		if ((data->nrg_prev_state != IWL_FA_TOO_MANY) &&
2878c2ecf20Sopenharmony_ci			((data->nrg_auto_corr_silence_diff > NRG_DIFF) ||
2888c2ecf20Sopenharmony_ci			(data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) {
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci			IWL_DEBUG_CALIB(priv, "... increasing sensitivity\n");
2918c2ecf20Sopenharmony_ci			/* Increase nrg value to increase sensitivity */
2928c2ecf20Sopenharmony_ci			val = data->nrg_th_cck + NRG_STEP_CCK;
2938c2ecf20Sopenharmony_ci			data->nrg_th_cck = min((u32)ranges->min_nrg_cck, val);
2948c2ecf20Sopenharmony_ci		} else {
2958c2ecf20Sopenharmony_ci			IWL_DEBUG_CALIB(priv, "... but not changing sensitivity\n");
2968c2ecf20Sopenharmony_ci		}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	/* Else we got a healthy number of false alarms, keep status quo */
2998c2ecf20Sopenharmony_ci	} else {
3008c2ecf20Sopenharmony_ci		IWL_DEBUG_CALIB(priv, " FA in safe zone\n");
3018c2ecf20Sopenharmony_ci		data->nrg_curr_state = IWL_FA_GOOD_RANGE;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci		/* Store for use in "fewer than desired" with later beacon */
3048c2ecf20Sopenharmony_ci		data->nrg_silence_ref = silence_ref;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci		/* If previous beacon had too many false alarms,
3078c2ecf20Sopenharmony_ci		 *   give it some extra margin by reducing sensitivity again
3088c2ecf20Sopenharmony_ci		 *   (but don't go below measured energy of desired Rx) */
3098c2ecf20Sopenharmony_ci		if (data->nrg_prev_state == IWL_FA_TOO_MANY) {
3108c2ecf20Sopenharmony_ci			IWL_DEBUG_CALIB(priv, "... increasing margin\n");
3118c2ecf20Sopenharmony_ci			if (data->nrg_th_cck > (max_nrg_cck + NRG_MARGIN))
3128c2ecf20Sopenharmony_ci				data->nrg_th_cck -= NRG_MARGIN;
3138c2ecf20Sopenharmony_ci			else
3148c2ecf20Sopenharmony_ci				data->nrg_th_cck = max_nrg_cck;
3158c2ecf20Sopenharmony_ci		}
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	/* Make sure the energy threshold does not go above the measured
3198c2ecf20Sopenharmony_ci	 * energy of the desired Rx signals (reduced by backoff margin),
3208c2ecf20Sopenharmony_ci	 * or else we might start missing Rx frames.
3218c2ecf20Sopenharmony_ci	 * Lower value is higher energy, so we use max()!
3228c2ecf20Sopenharmony_ci	 */
3238c2ecf20Sopenharmony_ci	data->nrg_th_cck = max(max_nrg_cck, data->nrg_th_cck);
3248c2ecf20Sopenharmony_ci	IWL_DEBUG_CALIB(priv, "new nrg_th_cck %u\n", data->nrg_th_cck);
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	data->nrg_prev_state = data->nrg_curr_state;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	/* Auto-correlation CCK algorithm */
3298c2ecf20Sopenharmony_ci	if (false_alarms > min_false_alarms) {
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci		/* increase auto_corr values to decrease sensitivity
3328c2ecf20Sopenharmony_ci		 * so the DSP won't be disturbed by the noise
3338c2ecf20Sopenharmony_ci		 */
3348c2ecf20Sopenharmony_ci		if (data->auto_corr_cck < AUTO_CORR_MAX_TH_CCK)
3358c2ecf20Sopenharmony_ci			data->auto_corr_cck = AUTO_CORR_MAX_TH_CCK + 1;
3368c2ecf20Sopenharmony_ci		else {
3378c2ecf20Sopenharmony_ci			val = data->auto_corr_cck + AUTO_CORR_STEP_CCK;
3388c2ecf20Sopenharmony_ci			data->auto_corr_cck =
3398c2ecf20Sopenharmony_ci				min((u32)ranges->auto_corr_max_cck, val);
3408c2ecf20Sopenharmony_ci		}
3418c2ecf20Sopenharmony_ci		val = data->auto_corr_cck_mrc + AUTO_CORR_STEP_CCK;
3428c2ecf20Sopenharmony_ci		data->auto_corr_cck_mrc =
3438c2ecf20Sopenharmony_ci			min((u32)ranges->auto_corr_max_cck_mrc, val);
3448c2ecf20Sopenharmony_ci	} else if ((false_alarms < min_false_alarms) &&
3458c2ecf20Sopenharmony_ci	   ((data->nrg_auto_corr_silence_diff > NRG_DIFF) ||
3468c2ecf20Sopenharmony_ci	   (data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) {
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci		/* Decrease auto_corr values to increase sensitivity */
3498c2ecf20Sopenharmony_ci		val = data->auto_corr_cck - AUTO_CORR_STEP_CCK;
3508c2ecf20Sopenharmony_ci		data->auto_corr_cck =
3518c2ecf20Sopenharmony_ci			max((u32)ranges->auto_corr_min_cck, val);
3528c2ecf20Sopenharmony_ci		val = data->auto_corr_cck_mrc - AUTO_CORR_STEP_CCK;
3538c2ecf20Sopenharmony_ci		data->auto_corr_cck_mrc =
3548c2ecf20Sopenharmony_ci			max((u32)ranges->auto_corr_min_cck_mrc, val);
3558c2ecf20Sopenharmony_ci	}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	return 0;
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic int iwl_sens_auto_corr_ofdm(struct iwl_priv *priv,
3628c2ecf20Sopenharmony_ci				       u32 norm_fa,
3638c2ecf20Sopenharmony_ci				       u32 rx_enable_time)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	u32 val;
3668c2ecf20Sopenharmony_ci	u32 false_alarms = norm_fa * 200 * 1024;
3678c2ecf20Sopenharmony_ci	u32 max_false_alarms = MAX_FA_OFDM * rx_enable_time;
3688c2ecf20Sopenharmony_ci	u32 min_false_alarms = MIN_FA_OFDM * rx_enable_time;
3698c2ecf20Sopenharmony_ci	struct iwl_sensitivity_data *data = NULL;
3708c2ecf20Sopenharmony_ci	const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	data = &(priv->sensitivity_data);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	/* If we got too many false alarms this time, reduce sensitivity */
3758c2ecf20Sopenharmony_ci	if (false_alarms > max_false_alarms) {
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci		IWL_DEBUG_CALIB(priv, "norm FA %u > max FA %u)\n",
3788c2ecf20Sopenharmony_ci			     false_alarms, max_false_alarms);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci		val = data->auto_corr_ofdm + AUTO_CORR_STEP_OFDM;
3818c2ecf20Sopenharmony_ci		data->auto_corr_ofdm =
3828c2ecf20Sopenharmony_ci			min((u32)ranges->auto_corr_max_ofdm, val);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci		val = data->auto_corr_ofdm_mrc + AUTO_CORR_STEP_OFDM;
3858c2ecf20Sopenharmony_ci		data->auto_corr_ofdm_mrc =
3868c2ecf20Sopenharmony_ci			min((u32)ranges->auto_corr_max_ofdm_mrc, val);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci		val = data->auto_corr_ofdm_x1 + AUTO_CORR_STEP_OFDM;
3898c2ecf20Sopenharmony_ci		data->auto_corr_ofdm_x1 =
3908c2ecf20Sopenharmony_ci			min((u32)ranges->auto_corr_max_ofdm_x1, val);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci		val = data->auto_corr_ofdm_mrc_x1 + AUTO_CORR_STEP_OFDM;
3938c2ecf20Sopenharmony_ci		data->auto_corr_ofdm_mrc_x1 =
3948c2ecf20Sopenharmony_ci			min((u32)ranges->auto_corr_max_ofdm_mrc_x1, val);
3958c2ecf20Sopenharmony_ci	}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	/* Else if we got fewer than desired, increase sensitivity */
3988c2ecf20Sopenharmony_ci	else if (false_alarms < min_false_alarms) {
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci		IWL_DEBUG_CALIB(priv, "norm FA %u < min FA %u\n",
4018c2ecf20Sopenharmony_ci			     false_alarms, min_false_alarms);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci		val = data->auto_corr_ofdm - AUTO_CORR_STEP_OFDM;
4048c2ecf20Sopenharmony_ci		data->auto_corr_ofdm =
4058c2ecf20Sopenharmony_ci			max((u32)ranges->auto_corr_min_ofdm, val);
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci		val = data->auto_corr_ofdm_mrc - AUTO_CORR_STEP_OFDM;
4088c2ecf20Sopenharmony_ci		data->auto_corr_ofdm_mrc =
4098c2ecf20Sopenharmony_ci			max((u32)ranges->auto_corr_min_ofdm_mrc, val);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci		val = data->auto_corr_ofdm_x1 - AUTO_CORR_STEP_OFDM;
4128c2ecf20Sopenharmony_ci		data->auto_corr_ofdm_x1 =
4138c2ecf20Sopenharmony_ci			max((u32)ranges->auto_corr_min_ofdm_x1, val);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci		val = data->auto_corr_ofdm_mrc_x1 - AUTO_CORR_STEP_OFDM;
4168c2ecf20Sopenharmony_ci		data->auto_corr_ofdm_mrc_x1 =
4178c2ecf20Sopenharmony_ci			max((u32)ranges->auto_corr_min_ofdm_mrc_x1, val);
4188c2ecf20Sopenharmony_ci	} else {
4198c2ecf20Sopenharmony_ci		IWL_DEBUG_CALIB(priv, "min FA %u < norm FA %u < max FA %u OK\n",
4208c2ecf20Sopenharmony_ci			 min_false_alarms, false_alarms, max_false_alarms);
4218c2ecf20Sopenharmony_ci	}
4228c2ecf20Sopenharmony_ci	return 0;
4238c2ecf20Sopenharmony_ci}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_cistatic void iwl_prepare_legacy_sensitivity_tbl(struct iwl_priv *priv,
4268c2ecf20Sopenharmony_ci				struct iwl_sensitivity_data *data,
4278c2ecf20Sopenharmony_ci				__le16 *tbl)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX] =
4308c2ecf20Sopenharmony_ci				cpu_to_le16((u16)data->auto_corr_ofdm);
4318c2ecf20Sopenharmony_ci	tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX] =
4328c2ecf20Sopenharmony_ci				cpu_to_le16((u16)data->auto_corr_ofdm_mrc);
4338c2ecf20Sopenharmony_ci	tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX] =
4348c2ecf20Sopenharmony_ci				cpu_to_le16((u16)data->auto_corr_ofdm_x1);
4358c2ecf20Sopenharmony_ci	tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX] =
4368c2ecf20Sopenharmony_ci				cpu_to_le16((u16)data->auto_corr_ofdm_mrc_x1);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX] =
4398c2ecf20Sopenharmony_ci				cpu_to_le16((u16)data->auto_corr_cck);
4408c2ecf20Sopenharmony_ci	tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX] =
4418c2ecf20Sopenharmony_ci				cpu_to_le16((u16)data->auto_corr_cck_mrc);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	tbl[HD_MIN_ENERGY_CCK_DET_INDEX] =
4448c2ecf20Sopenharmony_ci				cpu_to_le16((u16)data->nrg_th_cck);
4458c2ecf20Sopenharmony_ci	tbl[HD_MIN_ENERGY_OFDM_DET_INDEX] =
4468c2ecf20Sopenharmony_ci				cpu_to_le16((u16)data->nrg_th_ofdm);
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	tbl[HD_BARKER_CORR_TH_ADD_MIN_INDEX] =
4498c2ecf20Sopenharmony_ci				cpu_to_le16(data->barker_corr_th_min);
4508c2ecf20Sopenharmony_ci	tbl[HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX] =
4518c2ecf20Sopenharmony_ci				cpu_to_le16(data->barker_corr_th_min_mrc);
4528c2ecf20Sopenharmony_ci	tbl[HD_OFDM_ENERGY_TH_IN_INDEX] =
4538c2ecf20Sopenharmony_ci				cpu_to_le16(data->nrg_th_cca);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	IWL_DEBUG_CALIB(priv, "ofdm: ac %u mrc %u x1 %u mrc_x1 %u thresh %u\n",
4568c2ecf20Sopenharmony_ci			data->auto_corr_ofdm, data->auto_corr_ofdm_mrc,
4578c2ecf20Sopenharmony_ci			data->auto_corr_ofdm_x1, data->auto_corr_ofdm_mrc_x1,
4588c2ecf20Sopenharmony_ci			data->nrg_th_ofdm);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	IWL_DEBUG_CALIB(priv, "cck: ac %u mrc %u thresh %u\n",
4618c2ecf20Sopenharmony_ci			data->auto_corr_cck, data->auto_corr_cck_mrc,
4628c2ecf20Sopenharmony_ci			data->nrg_th_cck);
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci/* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */
4668c2ecf20Sopenharmony_cistatic int iwl_sensitivity_write(struct iwl_priv *priv)
4678c2ecf20Sopenharmony_ci{
4688c2ecf20Sopenharmony_ci	struct iwl_sensitivity_cmd cmd;
4698c2ecf20Sopenharmony_ci	struct iwl_sensitivity_data *data = NULL;
4708c2ecf20Sopenharmony_ci	struct iwl_host_cmd cmd_out = {
4718c2ecf20Sopenharmony_ci		.id = SENSITIVITY_CMD,
4728c2ecf20Sopenharmony_ci		.len = { sizeof(struct iwl_sensitivity_cmd), },
4738c2ecf20Sopenharmony_ci		.flags = CMD_ASYNC,
4748c2ecf20Sopenharmony_ci		.data = { &cmd, },
4758c2ecf20Sopenharmony_ci	};
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	data = &(priv->sensitivity_data);
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.table[0]);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	/* Update uCode's "work" table, and copy it to DSP */
4848c2ecf20Sopenharmony_ci	cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	/* Don't send command to uCode if nothing has changed */
4878c2ecf20Sopenharmony_ci	if (!memcmp(&cmd.table[0], &(priv->sensitivity_tbl[0]),
4888c2ecf20Sopenharmony_ci		    sizeof(u16)*HD_TABLE_SIZE)) {
4898c2ecf20Sopenharmony_ci		IWL_DEBUG_CALIB(priv, "No change in SENSITIVITY_CMD\n");
4908c2ecf20Sopenharmony_ci		return 0;
4918c2ecf20Sopenharmony_ci	}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	/* Copy table for comparison next time */
4948c2ecf20Sopenharmony_ci	memcpy(&(priv->sensitivity_tbl[0]), &(cmd.table[0]),
4958c2ecf20Sopenharmony_ci	       sizeof(u16)*HD_TABLE_SIZE);
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	return iwl_dvm_send_cmd(priv, &cmd_out);
4988c2ecf20Sopenharmony_ci}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci/* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */
5018c2ecf20Sopenharmony_cistatic int iwl_enhance_sensitivity_write(struct iwl_priv *priv)
5028c2ecf20Sopenharmony_ci{
5038c2ecf20Sopenharmony_ci	struct iwl_enhance_sensitivity_cmd cmd;
5048c2ecf20Sopenharmony_ci	struct iwl_sensitivity_data *data = NULL;
5058c2ecf20Sopenharmony_ci	struct iwl_host_cmd cmd_out = {
5068c2ecf20Sopenharmony_ci		.id = SENSITIVITY_CMD,
5078c2ecf20Sopenharmony_ci		.len = { sizeof(struct iwl_enhance_sensitivity_cmd), },
5088c2ecf20Sopenharmony_ci		.flags = CMD_ASYNC,
5098c2ecf20Sopenharmony_ci		.data = { &cmd, },
5108c2ecf20Sopenharmony_ci	};
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	data = &(priv->sensitivity_data);
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.enhance_table[0]);
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	if (priv->lib->hd_v2) {
5198c2ecf20Sopenharmony_ci		cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] =
5208c2ecf20Sopenharmony_ci			HD_INA_NON_SQUARE_DET_OFDM_DATA_V2;
5218c2ecf20Sopenharmony_ci		cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] =
5228c2ecf20Sopenharmony_ci			HD_INA_NON_SQUARE_DET_CCK_DATA_V2;
5238c2ecf20Sopenharmony_ci		cmd.enhance_table[HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX] =
5248c2ecf20Sopenharmony_ci			HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V2;
5258c2ecf20Sopenharmony_ci		cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX] =
5268c2ecf20Sopenharmony_ci			HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V2;
5278c2ecf20Sopenharmony_ci		cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] =
5288c2ecf20Sopenharmony_ci			HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2;
5298c2ecf20Sopenharmony_ci		cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX] =
5308c2ecf20Sopenharmony_ci			HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V2;
5318c2ecf20Sopenharmony_ci		cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX] =
5328c2ecf20Sopenharmony_ci			HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V2;
5338c2ecf20Sopenharmony_ci		cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX] =
5348c2ecf20Sopenharmony_ci			HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V2;
5358c2ecf20Sopenharmony_ci		cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] =
5368c2ecf20Sopenharmony_ci			HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2;
5378c2ecf20Sopenharmony_ci		cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_INDEX] =
5388c2ecf20Sopenharmony_ci			HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V2;
5398c2ecf20Sopenharmony_ci		cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX] =
5408c2ecf20Sopenharmony_ci			HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V2;
5418c2ecf20Sopenharmony_ci	} else {
5428c2ecf20Sopenharmony_ci		cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] =
5438c2ecf20Sopenharmony_ci			HD_INA_NON_SQUARE_DET_OFDM_DATA_V1;
5448c2ecf20Sopenharmony_ci		cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] =
5458c2ecf20Sopenharmony_ci			HD_INA_NON_SQUARE_DET_CCK_DATA_V1;
5468c2ecf20Sopenharmony_ci		cmd.enhance_table[HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX] =
5478c2ecf20Sopenharmony_ci			HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V1;
5488c2ecf20Sopenharmony_ci		cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX] =
5498c2ecf20Sopenharmony_ci			HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V1;
5508c2ecf20Sopenharmony_ci		cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] =
5518c2ecf20Sopenharmony_ci			HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1;
5528c2ecf20Sopenharmony_ci		cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX] =
5538c2ecf20Sopenharmony_ci			HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V1;
5548c2ecf20Sopenharmony_ci		cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX] =
5558c2ecf20Sopenharmony_ci			HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V1;
5568c2ecf20Sopenharmony_ci		cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX] =
5578c2ecf20Sopenharmony_ci			HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V1;
5588c2ecf20Sopenharmony_ci		cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] =
5598c2ecf20Sopenharmony_ci			HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1;
5608c2ecf20Sopenharmony_ci		cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_INDEX] =
5618c2ecf20Sopenharmony_ci			HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V1;
5628c2ecf20Sopenharmony_ci		cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX] =
5638c2ecf20Sopenharmony_ci			HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V1;
5648c2ecf20Sopenharmony_ci	}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	/* Update uCode's "work" table, and copy it to DSP */
5678c2ecf20Sopenharmony_ci	cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	/* Don't send command to uCode if nothing has changed */
5708c2ecf20Sopenharmony_ci	if (!memcmp(&cmd.enhance_table[0], &(priv->sensitivity_tbl[0]),
5718c2ecf20Sopenharmony_ci		    sizeof(u16)*HD_TABLE_SIZE) &&
5728c2ecf20Sopenharmony_ci	    !memcmp(&cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX],
5738c2ecf20Sopenharmony_ci		    &(priv->enhance_sensitivity_tbl[0]),
5748c2ecf20Sopenharmony_ci		    sizeof(u16)*ENHANCE_HD_TABLE_ENTRIES)) {
5758c2ecf20Sopenharmony_ci		IWL_DEBUG_CALIB(priv, "No change in SENSITIVITY_CMD\n");
5768c2ecf20Sopenharmony_ci		return 0;
5778c2ecf20Sopenharmony_ci	}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	/* Copy table for comparison next time */
5808c2ecf20Sopenharmony_ci	memcpy(&(priv->sensitivity_tbl[0]), &(cmd.enhance_table[0]),
5818c2ecf20Sopenharmony_ci	       sizeof(u16)*HD_TABLE_SIZE);
5828c2ecf20Sopenharmony_ci	memcpy(&(priv->enhance_sensitivity_tbl[0]),
5838c2ecf20Sopenharmony_ci	       &(cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX]),
5848c2ecf20Sopenharmony_ci	       sizeof(u16)*ENHANCE_HD_TABLE_ENTRIES);
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	return iwl_dvm_send_cmd(priv, &cmd_out);
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_civoid iwl_init_sensitivity(struct iwl_priv *priv)
5908c2ecf20Sopenharmony_ci{
5918c2ecf20Sopenharmony_ci	int ret = 0;
5928c2ecf20Sopenharmony_ci	int i;
5938c2ecf20Sopenharmony_ci	struct iwl_sensitivity_data *data = NULL;
5948c2ecf20Sopenharmony_ci	const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	if (priv->calib_disabled & IWL_SENSITIVITY_CALIB_DISABLED)
5978c2ecf20Sopenharmony_ci		return;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	IWL_DEBUG_CALIB(priv, "Start iwl_init_sensitivity\n");
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	/* Clear driver's sensitivity algo data */
6028c2ecf20Sopenharmony_ci	data = &(priv->sensitivity_data);
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	if (ranges == NULL)
6058c2ecf20Sopenharmony_ci		return;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	memset(data, 0, sizeof(struct iwl_sensitivity_data));
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	data->num_in_cck_no_fa = 0;
6108c2ecf20Sopenharmony_ci	data->nrg_curr_state = IWL_FA_TOO_MANY;
6118c2ecf20Sopenharmony_ci	data->nrg_prev_state = IWL_FA_TOO_MANY;
6128c2ecf20Sopenharmony_ci	data->nrg_silence_ref = 0;
6138c2ecf20Sopenharmony_ci	data->nrg_silence_idx = 0;
6148c2ecf20Sopenharmony_ci	data->nrg_energy_idx = 0;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	for (i = 0; i < 10; i++)
6178c2ecf20Sopenharmony_ci		data->nrg_value[i] = 0;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	for (i = 0; i < NRG_NUM_PREV_STAT_L; i++)
6208c2ecf20Sopenharmony_ci		data->nrg_silence_rssi[i] = 0;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	data->auto_corr_ofdm =  ranges->auto_corr_min_ofdm;
6238c2ecf20Sopenharmony_ci	data->auto_corr_ofdm_mrc = ranges->auto_corr_min_ofdm_mrc;
6248c2ecf20Sopenharmony_ci	data->auto_corr_ofdm_x1  = ranges->auto_corr_min_ofdm_x1;
6258c2ecf20Sopenharmony_ci	data->auto_corr_ofdm_mrc_x1 = ranges->auto_corr_min_ofdm_mrc_x1;
6268c2ecf20Sopenharmony_ci	data->auto_corr_cck = AUTO_CORR_CCK_MIN_VAL_DEF;
6278c2ecf20Sopenharmony_ci	data->auto_corr_cck_mrc = ranges->auto_corr_min_cck_mrc;
6288c2ecf20Sopenharmony_ci	data->nrg_th_cck = ranges->nrg_th_cck;
6298c2ecf20Sopenharmony_ci	data->nrg_th_ofdm = ranges->nrg_th_ofdm;
6308c2ecf20Sopenharmony_ci	data->barker_corr_th_min = ranges->barker_corr_th_min;
6318c2ecf20Sopenharmony_ci	data->barker_corr_th_min_mrc = ranges->barker_corr_th_min_mrc;
6328c2ecf20Sopenharmony_ci	data->nrg_th_cca = ranges->nrg_th_cca;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	data->last_bad_plcp_cnt_ofdm = 0;
6358c2ecf20Sopenharmony_ci	data->last_fa_cnt_ofdm = 0;
6368c2ecf20Sopenharmony_ci	data->last_bad_plcp_cnt_cck = 0;
6378c2ecf20Sopenharmony_ci	data->last_fa_cnt_cck = 0;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	if (priv->fw->enhance_sensitivity_table)
6408c2ecf20Sopenharmony_ci		ret |= iwl_enhance_sensitivity_write(priv);
6418c2ecf20Sopenharmony_ci	else
6428c2ecf20Sopenharmony_ci		ret |= iwl_sensitivity_write(priv);
6438c2ecf20Sopenharmony_ci	IWL_DEBUG_CALIB(priv, "<<return 0x%X\n", ret);
6448c2ecf20Sopenharmony_ci}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_civoid iwl_sensitivity_calibration(struct iwl_priv *priv)
6478c2ecf20Sopenharmony_ci{
6488c2ecf20Sopenharmony_ci	u32 rx_enable_time;
6498c2ecf20Sopenharmony_ci	u32 fa_cck;
6508c2ecf20Sopenharmony_ci	u32 fa_ofdm;
6518c2ecf20Sopenharmony_ci	u32 bad_plcp_cck;
6528c2ecf20Sopenharmony_ci	u32 bad_plcp_ofdm;
6538c2ecf20Sopenharmony_ci	u32 norm_fa_ofdm;
6548c2ecf20Sopenharmony_ci	u32 norm_fa_cck;
6558c2ecf20Sopenharmony_ci	struct iwl_sensitivity_data *data = NULL;
6568c2ecf20Sopenharmony_ci	struct statistics_rx_non_phy *rx_info;
6578c2ecf20Sopenharmony_ci	struct statistics_rx_phy *ofdm, *cck;
6588c2ecf20Sopenharmony_ci	struct statistics_general_data statis;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	if (priv->calib_disabled & IWL_SENSITIVITY_CALIB_DISABLED)
6618c2ecf20Sopenharmony_ci		return;
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	data = &(priv->sensitivity_data);
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	if (!iwl_is_any_associated(priv)) {
6668c2ecf20Sopenharmony_ci		IWL_DEBUG_CALIB(priv, "<< - not associated\n");
6678c2ecf20Sopenharmony_ci		return;
6688c2ecf20Sopenharmony_ci	}
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	spin_lock_bh(&priv->statistics.lock);
6718c2ecf20Sopenharmony_ci	rx_info = &priv->statistics.rx_non_phy;
6728c2ecf20Sopenharmony_ci	ofdm = &priv->statistics.rx_ofdm;
6738c2ecf20Sopenharmony_ci	cck = &priv->statistics.rx_cck;
6748c2ecf20Sopenharmony_ci	if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) {
6758c2ecf20Sopenharmony_ci		IWL_DEBUG_CALIB(priv, "<< invalid data.\n");
6768c2ecf20Sopenharmony_ci		spin_unlock_bh(&priv->statistics.lock);
6778c2ecf20Sopenharmony_ci		return;
6788c2ecf20Sopenharmony_ci	}
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	/* Extract Statistics: */
6818c2ecf20Sopenharmony_ci	rx_enable_time = le32_to_cpu(rx_info->channel_load);
6828c2ecf20Sopenharmony_ci	fa_cck = le32_to_cpu(cck->false_alarm_cnt);
6838c2ecf20Sopenharmony_ci	fa_ofdm = le32_to_cpu(ofdm->false_alarm_cnt);
6848c2ecf20Sopenharmony_ci	bad_plcp_cck = le32_to_cpu(cck->plcp_err);
6858c2ecf20Sopenharmony_ci	bad_plcp_ofdm = le32_to_cpu(ofdm->plcp_err);
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	statis.beacon_silence_rssi_a =
6888c2ecf20Sopenharmony_ci			le32_to_cpu(rx_info->beacon_silence_rssi_a);
6898c2ecf20Sopenharmony_ci	statis.beacon_silence_rssi_b =
6908c2ecf20Sopenharmony_ci			le32_to_cpu(rx_info->beacon_silence_rssi_b);
6918c2ecf20Sopenharmony_ci	statis.beacon_silence_rssi_c =
6928c2ecf20Sopenharmony_ci			le32_to_cpu(rx_info->beacon_silence_rssi_c);
6938c2ecf20Sopenharmony_ci	statis.beacon_energy_a =
6948c2ecf20Sopenharmony_ci			le32_to_cpu(rx_info->beacon_energy_a);
6958c2ecf20Sopenharmony_ci	statis.beacon_energy_b =
6968c2ecf20Sopenharmony_ci			le32_to_cpu(rx_info->beacon_energy_b);
6978c2ecf20Sopenharmony_ci	statis.beacon_energy_c =
6988c2ecf20Sopenharmony_ci			le32_to_cpu(rx_info->beacon_energy_c);
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	spin_unlock_bh(&priv->statistics.lock);
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	IWL_DEBUG_CALIB(priv, "rx_enable_time = %u usecs\n", rx_enable_time);
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	if (!rx_enable_time) {
7058c2ecf20Sopenharmony_ci		IWL_DEBUG_CALIB(priv, "<< RX Enable Time == 0!\n");
7068c2ecf20Sopenharmony_ci		return;
7078c2ecf20Sopenharmony_ci	}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	/* These statistics increase monotonically, and do not reset
7108c2ecf20Sopenharmony_ci	 *   at each beacon.  Calculate difference from last value, or just
7118c2ecf20Sopenharmony_ci	 *   use the new statistics value if it has reset or wrapped around. */
7128c2ecf20Sopenharmony_ci	if (data->last_bad_plcp_cnt_cck > bad_plcp_cck)
7138c2ecf20Sopenharmony_ci		data->last_bad_plcp_cnt_cck = bad_plcp_cck;
7148c2ecf20Sopenharmony_ci	else {
7158c2ecf20Sopenharmony_ci		bad_plcp_cck -= data->last_bad_plcp_cnt_cck;
7168c2ecf20Sopenharmony_ci		data->last_bad_plcp_cnt_cck += bad_plcp_cck;
7178c2ecf20Sopenharmony_ci	}
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	if (data->last_bad_plcp_cnt_ofdm > bad_plcp_ofdm)
7208c2ecf20Sopenharmony_ci		data->last_bad_plcp_cnt_ofdm = bad_plcp_ofdm;
7218c2ecf20Sopenharmony_ci	else {
7228c2ecf20Sopenharmony_ci		bad_plcp_ofdm -= data->last_bad_plcp_cnt_ofdm;
7238c2ecf20Sopenharmony_ci		data->last_bad_plcp_cnt_ofdm += bad_plcp_ofdm;
7248c2ecf20Sopenharmony_ci	}
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	if (data->last_fa_cnt_ofdm > fa_ofdm)
7278c2ecf20Sopenharmony_ci		data->last_fa_cnt_ofdm = fa_ofdm;
7288c2ecf20Sopenharmony_ci	else {
7298c2ecf20Sopenharmony_ci		fa_ofdm -= data->last_fa_cnt_ofdm;
7308c2ecf20Sopenharmony_ci		data->last_fa_cnt_ofdm += fa_ofdm;
7318c2ecf20Sopenharmony_ci	}
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	if (data->last_fa_cnt_cck > fa_cck)
7348c2ecf20Sopenharmony_ci		data->last_fa_cnt_cck = fa_cck;
7358c2ecf20Sopenharmony_ci	else {
7368c2ecf20Sopenharmony_ci		fa_cck -= data->last_fa_cnt_cck;
7378c2ecf20Sopenharmony_ci		data->last_fa_cnt_cck += fa_cck;
7388c2ecf20Sopenharmony_ci	}
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	/* Total aborted signal locks */
7418c2ecf20Sopenharmony_ci	norm_fa_ofdm = fa_ofdm + bad_plcp_ofdm;
7428c2ecf20Sopenharmony_ci	norm_fa_cck = fa_cck + bad_plcp_cck;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	IWL_DEBUG_CALIB(priv, "cck: fa %u badp %u  ofdm: fa %u badp %u\n", fa_cck,
7458c2ecf20Sopenharmony_ci			bad_plcp_cck, fa_ofdm, bad_plcp_ofdm);
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	iwl_sens_auto_corr_ofdm(priv, norm_fa_ofdm, rx_enable_time);
7488c2ecf20Sopenharmony_ci	iwl_sens_energy_cck(priv, norm_fa_cck, rx_enable_time, &statis);
7498c2ecf20Sopenharmony_ci	if (priv->fw->enhance_sensitivity_table)
7508c2ecf20Sopenharmony_ci		iwl_enhance_sensitivity_write(priv);
7518c2ecf20Sopenharmony_ci	else
7528c2ecf20Sopenharmony_ci		iwl_sensitivity_write(priv);
7538c2ecf20Sopenharmony_ci}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_cistatic inline u8 find_first_chain(u8 mask)
7568c2ecf20Sopenharmony_ci{
7578c2ecf20Sopenharmony_ci	if (mask & ANT_A)
7588c2ecf20Sopenharmony_ci		return CHAIN_A;
7598c2ecf20Sopenharmony_ci	if (mask & ANT_B)
7608c2ecf20Sopenharmony_ci		return CHAIN_B;
7618c2ecf20Sopenharmony_ci	return CHAIN_C;
7628c2ecf20Sopenharmony_ci}
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci/*
7658c2ecf20Sopenharmony_ci * Run disconnected antenna algorithm to find out which antennas are
7668c2ecf20Sopenharmony_ci * disconnected.
7678c2ecf20Sopenharmony_ci */
7688c2ecf20Sopenharmony_cistatic void iwl_find_disconn_antenna(struct iwl_priv *priv, u32* average_sig,
7698c2ecf20Sopenharmony_ci				     struct iwl_chain_noise_data *data)
7708c2ecf20Sopenharmony_ci{
7718c2ecf20Sopenharmony_ci	u32 active_chains = 0;
7728c2ecf20Sopenharmony_ci	u32 max_average_sig;
7738c2ecf20Sopenharmony_ci	u16 max_average_sig_antenna_i;
7748c2ecf20Sopenharmony_ci	u8 num_tx_chains;
7758c2ecf20Sopenharmony_ci	u8 first_chain;
7768c2ecf20Sopenharmony_ci	u16 i = 0;
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	average_sig[0] = data->chain_signal_a / IWL_CAL_NUM_BEACONS;
7798c2ecf20Sopenharmony_ci	average_sig[1] = data->chain_signal_b / IWL_CAL_NUM_BEACONS;
7808c2ecf20Sopenharmony_ci	average_sig[2] = data->chain_signal_c / IWL_CAL_NUM_BEACONS;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	if (average_sig[0] >= average_sig[1]) {
7838c2ecf20Sopenharmony_ci		max_average_sig = average_sig[0];
7848c2ecf20Sopenharmony_ci		max_average_sig_antenna_i = 0;
7858c2ecf20Sopenharmony_ci		active_chains = (1 << max_average_sig_antenna_i);
7868c2ecf20Sopenharmony_ci	} else {
7878c2ecf20Sopenharmony_ci		max_average_sig = average_sig[1];
7888c2ecf20Sopenharmony_ci		max_average_sig_antenna_i = 1;
7898c2ecf20Sopenharmony_ci		active_chains = (1 << max_average_sig_antenna_i);
7908c2ecf20Sopenharmony_ci	}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	if (average_sig[2] >= max_average_sig) {
7938c2ecf20Sopenharmony_ci		max_average_sig = average_sig[2];
7948c2ecf20Sopenharmony_ci		max_average_sig_antenna_i = 2;
7958c2ecf20Sopenharmony_ci		active_chains = (1 << max_average_sig_antenna_i);
7968c2ecf20Sopenharmony_ci	}
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	IWL_DEBUG_CALIB(priv, "average_sig: a %d b %d c %d\n",
7998c2ecf20Sopenharmony_ci		     average_sig[0], average_sig[1], average_sig[2]);
8008c2ecf20Sopenharmony_ci	IWL_DEBUG_CALIB(priv, "max_average_sig = %d, antenna %d\n",
8018c2ecf20Sopenharmony_ci		     max_average_sig, max_average_sig_antenna_i);
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	/* Compare signal strengths for all 3 receivers. */
8048c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_RX_CHAINS; i++) {
8058c2ecf20Sopenharmony_ci		if (i != max_average_sig_antenna_i) {
8068c2ecf20Sopenharmony_ci			s32 rssi_delta = (max_average_sig - average_sig[i]);
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci			/* If signal is very weak, compared with
8098c2ecf20Sopenharmony_ci			 * strongest, mark it as disconnected. */
8108c2ecf20Sopenharmony_ci			if (rssi_delta > MAXIMUM_ALLOWED_PATHLOSS)
8118c2ecf20Sopenharmony_ci				data->disconn_array[i] = 1;
8128c2ecf20Sopenharmony_ci			else
8138c2ecf20Sopenharmony_ci				active_chains |= (1 << i);
8148c2ecf20Sopenharmony_ci			IWL_DEBUG_CALIB(priv, "i = %d  rssiDelta = %d  "
8158c2ecf20Sopenharmony_ci			     "disconn_array[i] = %d\n",
8168c2ecf20Sopenharmony_ci			     i, rssi_delta, data->disconn_array[i]);
8178c2ecf20Sopenharmony_ci		}
8188c2ecf20Sopenharmony_ci	}
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	/*
8218c2ecf20Sopenharmony_ci	 * The above algorithm sometimes fails when the ucode
8228c2ecf20Sopenharmony_ci	 * reports 0 for all chains. It's not clear why that
8238c2ecf20Sopenharmony_ci	 * happens to start with, but it is then causing trouble
8248c2ecf20Sopenharmony_ci	 * because this can make us enable more chains than the
8258c2ecf20Sopenharmony_ci	 * hardware really has.
8268c2ecf20Sopenharmony_ci	 *
8278c2ecf20Sopenharmony_ci	 * To be safe, simply mask out any chains that we know
8288c2ecf20Sopenharmony_ci	 * are not on the device.
8298c2ecf20Sopenharmony_ci	 */
8308c2ecf20Sopenharmony_ci	active_chains &= priv->nvm_data->valid_rx_ant;
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	num_tx_chains = 0;
8338c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_RX_CHAINS; i++) {
8348c2ecf20Sopenharmony_ci		/* loops on all the bits of
8358c2ecf20Sopenharmony_ci		 * priv->hw_setting.valid_tx_ant */
8368c2ecf20Sopenharmony_ci		u8 ant_msk = (1 << i);
8378c2ecf20Sopenharmony_ci		if (!(priv->nvm_data->valid_tx_ant & ant_msk))
8388c2ecf20Sopenharmony_ci			continue;
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci		num_tx_chains++;
8418c2ecf20Sopenharmony_ci		if (data->disconn_array[i] == 0)
8428c2ecf20Sopenharmony_ci			/* there is a Tx antenna connected */
8438c2ecf20Sopenharmony_ci			break;
8448c2ecf20Sopenharmony_ci		if (num_tx_chains == priv->hw_params.tx_chains_num &&
8458c2ecf20Sopenharmony_ci		    data->disconn_array[i]) {
8468c2ecf20Sopenharmony_ci			/*
8478c2ecf20Sopenharmony_ci			 * If all chains are disconnected
8488c2ecf20Sopenharmony_ci			 * connect the first valid tx chain
8498c2ecf20Sopenharmony_ci			 */
8508c2ecf20Sopenharmony_ci			first_chain =
8518c2ecf20Sopenharmony_ci				find_first_chain(priv->nvm_data->valid_tx_ant);
8528c2ecf20Sopenharmony_ci			data->disconn_array[first_chain] = 0;
8538c2ecf20Sopenharmony_ci			active_chains |= BIT(first_chain);
8548c2ecf20Sopenharmony_ci			IWL_DEBUG_CALIB(priv,
8558c2ecf20Sopenharmony_ci					"All Tx chains are disconnected W/A - declare %d as connected\n",
8568c2ecf20Sopenharmony_ci					first_chain);
8578c2ecf20Sopenharmony_ci			break;
8588c2ecf20Sopenharmony_ci		}
8598c2ecf20Sopenharmony_ci	}
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	if (active_chains != priv->nvm_data->valid_rx_ant &&
8628c2ecf20Sopenharmony_ci	    active_chains != priv->chain_noise_data.active_chains)
8638c2ecf20Sopenharmony_ci		IWL_DEBUG_CALIB(priv,
8648c2ecf20Sopenharmony_ci				"Detected that not all antennas are connected! "
8658c2ecf20Sopenharmony_ci				"Connected: %#x, valid: %#x.\n",
8668c2ecf20Sopenharmony_ci				active_chains,
8678c2ecf20Sopenharmony_ci				priv->nvm_data->valid_rx_ant);
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	/* Save for use within RXON, TX, SCAN commands, etc. */
8708c2ecf20Sopenharmony_ci	data->active_chains = active_chains;
8718c2ecf20Sopenharmony_ci	IWL_DEBUG_CALIB(priv, "active_chains (bitwise) = 0x%x\n",
8728c2ecf20Sopenharmony_ci			active_chains);
8738c2ecf20Sopenharmony_ci}
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_cistatic void iwlagn_gain_computation(struct iwl_priv *priv,
8768c2ecf20Sopenharmony_ci				    u32 average_noise[NUM_RX_CHAINS],
8778c2ecf20Sopenharmony_ci				    u8 default_chain)
8788c2ecf20Sopenharmony_ci{
8798c2ecf20Sopenharmony_ci	int i;
8808c2ecf20Sopenharmony_ci	s32 delta_g;
8818c2ecf20Sopenharmony_ci	struct iwl_chain_noise_data *data = &priv->chain_noise_data;
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	/*
8848c2ecf20Sopenharmony_ci	 * Find Gain Code for the chains based on "default chain"
8858c2ecf20Sopenharmony_ci	 */
8868c2ecf20Sopenharmony_ci	for (i = default_chain + 1; i < NUM_RX_CHAINS; i++) {
8878c2ecf20Sopenharmony_ci		if ((data->disconn_array[i])) {
8888c2ecf20Sopenharmony_ci			data->delta_gain_code[i] = 0;
8898c2ecf20Sopenharmony_ci			continue;
8908c2ecf20Sopenharmony_ci		}
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci		delta_g = (priv->lib->chain_noise_scale *
8938c2ecf20Sopenharmony_ci			((s32)average_noise[default_chain] -
8948c2ecf20Sopenharmony_ci			(s32)average_noise[i])) / 1500;
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci		/* bound gain by 2 bits value max, 3rd bit is sign */
8978c2ecf20Sopenharmony_ci		data->delta_gain_code[i] =
8988c2ecf20Sopenharmony_ci			min(abs(delta_g), CHAIN_NOISE_MAX_DELTA_GAIN_CODE);
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci		if (delta_g < 0)
9018c2ecf20Sopenharmony_ci			/*
9028c2ecf20Sopenharmony_ci			 * set negative sign ...
9038c2ecf20Sopenharmony_ci			 * note to Intel developers:  This is uCode API format,
9048c2ecf20Sopenharmony_ci			 *   not the format of any internal device registers.
9058c2ecf20Sopenharmony_ci			 *   Do not change this format for e.g. 6050 or similar
9068c2ecf20Sopenharmony_ci			 *   devices.  Change format only if more resolution
9078c2ecf20Sopenharmony_ci			 *   (i.e. more than 2 bits magnitude) is needed.
9088c2ecf20Sopenharmony_ci			 */
9098c2ecf20Sopenharmony_ci			data->delta_gain_code[i] |= (1 << 2);
9108c2ecf20Sopenharmony_ci	}
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	IWL_DEBUG_CALIB(priv, "Delta gains: ANT_B = %d  ANT_C = %d\n",
9138c2ecf20Sopenharmony_ci			data->delta_gain_code[1], data->delta_gain_code[2]);
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	if (!data->radio_write) {
9168c2ecf20Sopenharmony_ci		struct iwl_calib_chain_noise_gain_cmd cmd;
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci		memset(&cmd, 0, sizeof(cmd));
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci		iwl_set_calib_hdr(&cmd.hdr,
9218c2ecf20Sopenharmony_ci			priv->phy_calib_chain_noise_gain_cmd);
9228c2ecf20Sopenharmony_ci		cmd.delta_gain_1 = data->delta_gain_code[1];
9238c2ecf20Sopenharmony_ci		cmd.delta_gain_2 = data->delta_gain_code[2];
9248c2ecf20Sopenharmony_ci		iwl_dvm_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD,
9258c2ecf20Sopenharmony_ci			CMD_ASYNC, sizeof(cmd), &cmd);
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci		data->radio_write = 1;
9288c2ecf20Sopenharmony_ci		data->state = IWL_CHAIN_NOISE_CALIBRATED;
9298c2ecf20Sopenharmony_ci	}
9308c2ecf20Sopenharmony_ci}
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci/*
9338c2ecf20Sopenharmony_ci * Accumulate 16 beacons of signal and noise statistics for each of
9348c2ecf20Sopenharmony_ci *   3 receivers/antennas/rx-chains, then figure out:
9358c2ecf20Sopenharmony_ci * 1)  Which antennas are connected.
9368c2ecf20Sopenharmony_ci * 2)  Differential rx gain settings to balance the 3 receivers.
9378c2ecf20Sopenharmony_ci */
9388c2ecf20Sopenharmony_civoid iwl_chain_noise_calibration(struct iwl_priv *priv)
9398c2ecf20Sopenharmony_ci{
9408c2ecf20Sopenharmony_ci	struct iwl_chain_noise_data *data = NULL;
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	u32 chain_noise_a;
9438c2ecf20Sopenharmony_ci	u32 chain_noise_b;
9448c2ecf20Sopenharmony_ci	u32 chain_noise_c;
9458c2ecf20Sopenharmony_ci	u32 chain_sig_a;
9468c2ecf20Sopenharmony_ci	u32 chain_sig_b;
9478c2ecf20Sopenharmony_ci	u32 chain_sig_c;
9488c2ecf20Sopenharmony_ci	u32 average_sig[NUM_RX_CHAINS] = {INITIALIZATION_VALUE};
9498c2ecf20Sopenharmony_ci	u32 average_noise[NUM_RX_CHAINS] = {INITIALIZATION_VALUE};
9508c2ecf20Sopenharmony_ci	u32 min_average_noise = MIN_AVERAGE_NOISE_MAX_VALUE;
9518c2ecf20Sopenharmony_ci	u16 min_average_noise_antenna_i = INITIALIZATION_VALUE;
9528c2ecf20Sopenharmony_ci	u16 i = 0;
9538c2ecf20Sopenharmony_ci	u16 rxon_chnum = INITIALIZATION_VALUE;
9548c2ecf20Sopenharmony_ci	u16 stat_chnum = INITIALIZATION_VALUE;
9558c2ecf20Sopenharmony_ci	u8 rxon_band24;
9568c2ecf20Sopenharmony_ci	u8 stat_band24;
9578c2ecf20Sopenharmony_ci	struct statistics_rx_non_phy *rx_info;
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	/*
9608c2ecf20Sopenharmony_ci	 * MULTI-FIXME:
9618c2ecf20Sopenharmony_ci	 * When we support multiple interfaces on different channels,
9628c2ecf20Sopenharmony_ci	 * this must be modified/fixed.
9638c2ecf20Sopenharmony_ci	 */
9648c2ecf20Sopenharmony_ci	struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci	if (priv->calib_disabled & IWL_CHAIN_NOISE_CALIB_DISABLED)
9678c2ecf20Sopenharmony_ci		return;
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	data = &(priv->chain_noise_data);
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	/*
9728c2ecf20Sopenharmony_ci	 * Accumulate just the first "chain_noise_num_beacons" after
9738c2ecf20Sopenharmony_ci	 * the first association, then we're done forever.
9748c2ecf20Sopenharmony_ci	 */
9758c2ecf20Sopenharmony_ci	if (data->state != IWL_CHAIN_NOISE_ACCUMULATE) {
9768c2ecf20Sopenharmony_ci		if (data->state == IWL_CHAIN_NOISE_ALIVE)
9778c2ecf20Sopenharmony_ci			IWL_DEBUG_CALIB(priv, "Wait for noise calib reset\n");
9788c2ecf20Sopenharmony_ci		return;
9798c2ecf20Sopenharmony_ci	}
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	spin_lock_bh(&priv->statistics.lock);
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	rx_info = &priv->statistics.rx_non_phy;
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) {
9868c2ecf20Sopenharmony_ci		IWL_DEBUG_CALIB(priv, " << Interference data unavailable\n");
9878c2ecf20Sopenharmony_ci		spin_unlock_bh(&priv->statistics.lock);
9888c2ecf20Sopenharmony_ci		return;
9898c2ecf20Sopenharmony_ci	}
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	rxon_band24 = !!(ctx->staging.flags & RXON_FLG_BAND_24G_MSK);
9928c2ecf20Sopenharmony_ci	rxon_chnum = le16_to_cpu(ctx->staging.channel);
9938c2ecf20Sopenharmony_ci	stat_band24 =
9948c2ecf20Sopenharmony_ci		!!(priv->statistics.flag & STATISTICS_REPLY_FLG_BAND_24G_MSK);
9958c2ecf20Sopenharmony_ci	stat_chnum = le32_to_cpu(priv->statistics.flag) >> 16;
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	/* Make sure we accumulate data for just the associated channel
9988c2ecf20Sopenharmony_ci	 *   (even if scanning). */
9998c2ecf20Sopenharmony_ci	if ((rxon_chnum != stat_chnum) || (rxon_band24 != stat_band24)) {
10008c2ecf20Sopenharmony_ci		IWL_DEBUG_CALIB(priv, "Stats not from chan=%d, band24=%d\n",
10018c2ecf20Sopenharmony_ci				rxon_chnum, rxon_band24);
10028c2ecf20Sopenharmony_ci		spin_unlock_bh(&priv->statistics.lock);
10038c2ecf20Sopenharmony_ci		return;
10048c2ecf20Sopenharmony_ci	}
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci	/*
10078c2ecf20Sopenharmony_ci	 *  Accumulate beacon statistics values across
10088c2ecf20Sopenharmony_ci	 * "chain_noise_num_beacons"
10098c2ecf20Sopenharmony_ci	 */
10108c2ecf20Sopenharmony_ci	chain_noise_a = le32_to_cpu(rx_info->beacon_silence_rssi_a) &
10118c2ecf20Sopenharmony_ci				IN_BAND_FILTER;
10128c2ecf20Sopenharmony_ci	chain_noise_b = le32_to_cpu(rx_info->beacon_silence_rssi_b) &
10138c2ecf20Sopenharmony_ci				IN_BAND_FILTER;
10148c2ecf20Sopenharmony_ci	chain_noise_c = le32_to_cpu(rx_info->beacon_silence_rssi_c) &
10158c2ecf20Sopenharmony_ci				IN_BAND_FILTER;
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	chain_sig_a = le32_to_cpu(rx_info->beacon_rssi_a) & IN_BAND_FILTER;
10188c2ecf20Sopenharmony_ci	chain_sig_b = le32_to_cpu(rx_info->beacon_rssi_b) & IN_BAND_FILTER;
10198c2ecf20Sopenharmony_ci	chain_sig_c = le32_to_cpu(rx_info->beacon_rssi_c) & IN_BAND_FILTER;
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	spin_unlock_bh(&priv->statistics.lock);
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci	data->beacon_count++;
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	data->chain_noise_a = (chain_noise_a + data->chain_noise_a);
10268c2ecf20Sopenharmony_ci	data->chain_noise_b = (chain_noise_b + data->chain_noise_b);
10278c2ecf20Sopenharmony_ci	data->chain_noise_c = (chain_noise_c + data->chain_noise_c);
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci	data->chain_signal_a = (chain_sig_a + data->chain_signal_a);
10308c2ecf20Sopenharmony_ci	data->chain_signal_b = (chain_sig_b + data->chain_signal_b);
10318c2ecf20Sopenharmony_ci	data->chain_signal_c = (chain_sig_c + data->chain_signal_c);
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	IWL_DEBUG_CALIB(priv, "chan=%d, band24=%d, beacon=%d\n",
10348c2ecf20Sopenharmony_ci			rxon_chnum, rxon_band24, data->beacon_count);
10358c2ecf20Sopenharmony_ci	IWL_DEBUG_CALIB(priv, "chain_sig: a %d b %d c %d\n",
10368c2ecf20Sopenharmony_ci			chain_sig_a, chain_sig_b, chain_sig_c);
10378c2ecf20Sopenharmony_ci	IWL_DEBUG_CALIB(priv, "chain_noise: a %d b %d c %d\n",
10388c2ecf20Sopenharmony_ci			chain_noise_a, chain_noise_b, chain_noise_c);
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci	/* If this is the "chain_noise_num_beacons", determine:
10418c2ecf20Sopenharmony_ci	 * 1)  Disconnected antennas (using signal strengths)
10428c2ecf20Sopenharmony_ci	 * 2)  Differential gain (using silence noise) to balance receivers */
10438c2ecf20Sopenharmony_ci	if (data->beacon_count != IWL_CAL_NUM_BEACONS)
10448c2ecf20Sopenharmony_ci		return;
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	/* Analyze signal for disconnected antenna */
10478c2ecf20Sopenharmony_ci	if (priv->lib->bt_params &&
10488c2ecf20Sopenharmony_ci	    priv->lib->bt_params->advanced_bt_coexist) {
10498c2ecf20Sopenharmony_ci		/* Disable disconnected antenna algorithm for advanced
10508c2ecf20Sopenharmony_ci		   bt coex, assuming valid antennas are connected */
10518c2ecf20Sopenharmony_ci		data->active_chains = priv->nvm_data->valid_rx_ant;
10528c2ecf20Sopenharmony_ci		for (i = 0; i < NUM_RX_CHAINS; i++)
10538c2ecf20Sopenharmony_ci			if (!(data->active_chains & (1<<i)))
10548c2ecf20Sopenharmony_ci				data->disconn_array[i] = 1;
10558c2ecf20Sopenharmony_ci	} else
10568c2ecf20Sopenharmony_ci		iwl_find_disconn_antenna(priv, average_sig, data);
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	/* Analyze noise for rx balance */
10598c2ecf20Sopenharmony_ci	average_noise[0] = data->chain_noise_a / IWL_CAL_NUM_BEACONS;
10608c2ecf20Sopenharmony_ci	average_noise[1] = data->chain_noise_b / IWL_CAL_NUM_BEACONS;
10618c2ecf20Sopenharmony_ci	average_noise[2] = data->chain_noise_c / IWL_CAL_NUM_BEACONS;
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_RX_CHAINS; i++) {
10648c2ecf20Sopenharmony_ci		if (!(data->disconn_array[i]) &&
10658c2ecf20Sopenharmony_ci		   (average_noise[i] <= min_average_noise)) {
10668c2ecf20Sopenharmony_ci			/* This means that chain i is active and has
10678c2ecf20Sopenharmony_ci			 * lower noise values so far: */
10688c2ecf20Sopenharmony_ci			min_average_noise = average_noise[i];
10698c2ecf20Sopenharmony_ci			min_average_noise_antenna_i = i;
10708c2ecf20Sopenharmony_ci		}
10718c2ecf20Sopenharmony_ci	}
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	IWL_DEBUG_CALIB(priv, "average_noise: a %d b %d c %d\n",
10748c2ecf20Sopenharmony_ci			average_noise[0], average_noise[1],
10758c2ecf20Sopenharmony_ci			average_noise[2]);
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	IWL_DEBUG_CALIB(priv, "min_average_noise = %d, antenna %d\n",
10788c2ecf20Sopenharmony_ci			min_average_noise, min_average_noise_antenna_i);
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci	iwlagn_gain_computation(
10818c2ecf20Sopenharmony_ci		priv, average_noise,
10828c2ecf20Sopenharmony_ci		find_first_chain(priv->nvm_data->valid_rx_ant));
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	/* Some power changes may have been made during the calibration.
10858c2ecf20Sopenharmony_ci	 * Update and commit the RXON
10868c2ecf20Sopenharmony_ci	 */
10878c2ecf20Sopenharmony_ci	iwl_update_chain_flags(priv);
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	data->state = IWL_CHAIN_NOISE_DONE;
10908c2ecf20Sopenharmony_ci	iwl_power_update_mode(priv, false);
10918c2ecf20Sopenharmony_ci}
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_civoid iwl_reset_run_time_calib(struct iwl_priv *priv)
10948c2ecf20Sopenharmony_ci{
10958c2ecf20Sopenharmony_ci	int i;
10968c2ecf20Sopenharmony_ci	memset(&(priv->sensitivity_data), 0,
10978c2ecf20Sopenharmony_ci	       sizeof(struct iwl_sensitivity_data));
10988c2ecf20Sopenharmony_ci	memset(&(priv->chain_noise_data), 0,
10998c2ecf20Sopenharmony_ci	       sizeof(struct iwl_chain_noise_data));
11008c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_RX_CHAINS; i++)
11018c2ecf20Sopenharmony_ci		priv->chain_noise_data.delta_gain_code[i] =
11028c2ecf20Sopenharmony_ci				CHAIN_NOISE_DELTA_GAIN_INIT_VAL;
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	/* Ask for statistics now, the uCode will send notification
11058c2ecf20Sopenharmony_ci	 * periodically after association */
11068c2ecf20Sopenharmony_ci	iwl_send_statistics_request(priv, CMD_ASYNC, true);
11078c2ecf20Sopenharmony_ci}
1108