162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2005-2014 Intel Corporation 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <linux/slab.h> 662306a36Sopenharmony_ci#include <net/mac80211.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "iwl-trans.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "dev.h" 1162306a36Sopenharmony_ci#include "calib.h" 1262306a36Sopenharmony_ci#include "agn.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/***************************************************************************** 1562306a36Sopenharmony_ci * INIT calibrations framework 1662306a36Sopenharmony_ci *****************************************************************************/ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* Opaque calibration results */ 1962306a36Sopenharmony_cistruct iwl_calib_result { 2062306a36Sopenharmony_ci struct list_head list; 2162306a36Sopenharmony_ci size_t cmd_len; 2262306a36Sopenharmony_ci struct iwl_calib_cmd cmd; 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistruct statistics_general_data { 2662306a36Sopenharmony_ci u32 beacon_silence_rssi_a; 2762306a36Sopenharmony_ci u32 beacon_silence_rssi_b; 2862306a36Sopenharmony_ci u32 beacon_silence_rssi_c; 2962306a36Sopenharmony_ci u32 beacon_energy_a; 3062306a36Sopenharmony_ci u32 beacon_energy_b; 3162306a36Sopenharmony_ci u32 beacon_energy_c; 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ciint iwl_send_calib_results(struct iwl_priv *priv) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci struct iwl_host_cmd hcmd = { 3762306a36Sopenharmony_ci .id = REPLY_PHY_CALIBRATION_CMD, 3862306a36Sopenharmony_ci }; 3962306a36Sopenharmony_ci struct iwl_calib_result *res; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci list_for_each_entry(res, &priv->calib_results, list) { 4262306a36Sopenharmony_ci int ret; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci hcmd.len[0] = res->cmd_len; 4562306a36Sopenharmony_ci hcmd.data[0] = &res->cmd; 4662306a36Sopenharmony_ci hcmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; 4762306a36Sopenharmony_ci ret = iwl_dvm_send_cmd(priv, &hcmd); 4862306a36Sopenharmony_ci if (ret) { 4962306a36Sopenharmony_ci IWL_ERR(priv, "Error %d on calib cmd %d\n", 5062306a36Sopenharmony_ci ret, res->cmd.hdr.op_code); 5162306a36Sopenharmony_ci return ret; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci return 0; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ciint iwl_calib_set(struct iwl_priv *priv, 5962306a36Sopenharmony_ci const struct iwl_calib_cmd *cmd, size_t len) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci struct iwl_calib_result *res, *tmp; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (check_sub_overflow(len, sizeof(*cmd), &len)) 6462306a36Sopenharmony_ci return -ENOMEM; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci res = kmalloc(struct_size(res, cmd.data, len), GFP_ATOMIC); 6762306a36Sopenharmony_ci if (!res) 6862306a36Sopenharmony_ci return -ENOMEM; 6962306a36Sopenharmony_ci res->cmd = *cmd; 7062306a36Sopenharmony_ci memcpy(res->cmd.data, cmd->data, len); 7162306a36Sopenharmony_ci res->cmd_len = struct_size(cmd, data, len); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci list_for_each_entry(tmp, &priv->calib_results, list) { 7462306a36Sopenharmony_ci if (tmp->cmd.hdr.op_code == res->cmd.hdr.op_code) { 7562306a36Sopenharmony_ci list_replace(&tmp->list, &res->list); 7662306a36Sopenharmony_ci kfree(tmp); 7762306a36Sopenharmony_ci return 0; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci /* wasn't in list already */ 8262306a36Sopenharmony_ci list_add_tail(&res->list, &priv->calib_results); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci return 0; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_civoid iwl_calib_free_results(struct iwl_priv *priv) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci struct iwl_calib_result *res, *tmp; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci list_for_each_entry_safe(res, tmp, &priv->calib_results, list) { 9262306a36Sopenharmony_ci list_del(&res->list); 9362306a36Sopenharmony_ci kfree(res); 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/***************************************************************************** 9862306a36Sopenharmony_ci * RUNTIME calibrations framework 9962306a36Sopenharmony_ci *****************************************************************************/ 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/* "false alarms" are signals that our DSP tries to lock onto, 10262306a36Sopenharmony_ci * but then determines that they are either noise, or transmissions 10362306a36Sopenharmony_ci * from a distant wireless network (also "noise", really) that get 10462306a36Sopenharmony_ci * "stepped on" by stronger transmissions within our own network. 10562306a36Sopenharmony_ci * This algorithm attempts to set a sensitivity level that is high 10662306a36Sopenharmony_ci * enough to receive all of our own network traffic, but not so 10762306a36Sopenharmony_ci * high that our DSP gets too busy trying to lock onto non-network 10862306a36Sopenharmony_ci * activity/noise. */ 10962306a36Sopenharmony_cistatic int iwl_sens_energy_cck(struct iwl_priv *priv, 11062306a36Sopenharmony_ci u32 norm_fa, 11162306a36Sopenharmony_ci u32 rx_enable_time, 11262306a36Sopenharmony_ci struct statistics_general_data *rx_info) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci u32 max_nrg_cck = 0; 11562306a36Sopenharmony_ci int i = 0; 11662306a36Sopenharmony_ci u8 max_silence_rssi = 0; 11762306a36Sopenharmony_ci u32 silence_ref = 0; 11862306a36Sopenharmony_ci u8 silence_rssi_a = 0; 11962306a36Sopenharmony_ci u8 silence_rssi_b = 0; 12062306a36Sopenharmony_ci u8 silence_rssi_c = 0; 12162306a36Sopenharmony_ci u32 val; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* "false_alarms" values below are cross-multiplications to assess the 12462306a36Sopenharmony_ci * numbers of false alarms within the measured period of actual Rx 12562306a36Sopenharmony_ci * (Rx is off when we're txing), vs the min/max expected false alarms 12662306a36Sopenharmony_ci * (some should be expected if rx is sensitive enough) in a 12762306a36Sopenharmony_ci * hypothetical listening period of 200 time units (TU), 204.8 msec: 12862306a36Sopenharmony_ci * 12962306a36Sopenharmony_ci * MIN_FA/fixed-time < false_alarms/actual-rx-time < MAX_FA/beacon-time 13062306a36Sopenharmony_ci * 13162306a36Sopenharmony_ci * */ 13262306a36Sopenharmony_ci u32 false_alarms = norm_fa * 200 * 1024; 13362306a36Sopenharmony_ci u32 max_false_alarms = MAX_FA_CCK * rx_enable_time; 13462306a36Sopenharmony_ci u32 min_false_alarms = MIN_FA_CCK * rx_enable_time; 13562306a36Sopenharmony_ci struct iwl_sensitivity_data *data = NULL; 13662306a36Sopenharmony_ci const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci data = &(priv->sensitivity_data); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci data->nrg_auto_corr_silence_diff = 0; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci /* Find max silence rssi among all 3 receivers. 14362306a36Sopenharmony_ci * This is background noise, which may include transmissions from other 14462306a36Sopenharmony_ci * networks, measured during silence before our network's beacon */ 14562306a36Sopenharmony_ci silence_rssi_a = (u8)((rx_info->beacon_silence_rssi_a & 14662306a36Sopenharmony_ci ALL_BAND_FILTER) >> 8); 14762306a36Sopenharmony_ci silence_rssi_b = (u8)((rx_info->beacon_silence_rssi_b & 14862306a36Sopenharmony_ci ALL_BAND_FILTER) >> 8); 14962306a36Sopenharmony_ci silence_rssi_c = (u8)((rx_info->beacon_silence_rssi_c & 15062306a36Sopenharmony_ci ALL_BAND_FILTER) >> 8); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci val = max(silence_rssi_b, silence_rssi_c); 15362306a36Sopenharmony_ci max_silence_rssi = max(silence_rssi_a, (u8) val); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* Store silence rssi in 20-beacon history table */ 15662306a36Sopenharmony_ci data->nrg_silence_rssi[data->nrg_silence_idx] = max_silence_rssi; 15762306a36Sopenharmony_ci data->nrg_silence_idx++; 15862306a36Sopenharmony_ci if (data->nrg_silence_idx >= NRG_NUM_PREV_STAT_L) 15962306a36Sopenharmony_ci data->nrg_silence_idx = 0; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* Find max silence rssi across 20 beacon history */ 16262306a36Sopenharmony_ci for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) { 16362306a36Sopenharmony_ci val = data->nrg_silence_rssi[i]; 16462306a36Sopenharmony_ci silence_ref = max(silence_ref, val); 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "silence a %u, b %u, c %u, 20-bcn max %u\n", 16762306a36Sopenharmony_ci silence_rssi_a, silence_rssi_b, silence_rssi_c, 16862306a36Sopenharmony_ci silence_ref); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* Find max rx energy (min value!) among all 3 receivers, 17162306a36Sopenharmony_ci * measured during beacon frame. 17262306a36Sopenharmony_ci * Save it in 10-beacon history table. */ 17362306a36Sopenharmony_ci i = data->nrg_energy_idx; 17462306a36Sopenharmony_ci val = min(rx_info->beacon_energy_b, rx_info->beacon_energy_c); 17562306a36Sopenharmony_ci data->nrg_value[i] = min(rx_info->beacon_energy_a, val); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci data->nrg_energy_idx++; 17862306a36Sopenharmony_ci if (data->nrg_energy_idx >= 10) 17962306a36Sopenharmony_ci data->nrg_energy_idx = 0; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* Find min rx energy (max value) across 10 beacon history. 18262306a36Sopenharmony_ci * This is the minimum signal level that we want to receive well. 18362306a36Sopenharmony_ci * Add backoff (margin so we don't miss slightly lower energy frames). 18462306a36Sopenharmony_ci * This establishes an upper bound (min value) for energy threshold. */ 18562306a36Sopenharmony_ci max_nrg_cck = data->nrg_value[0]; 18662306a36Sopenharmony_ci for (i = 1; i < 10; i++) 18762306a36Sopenharmony_ci max_nrg_cck = (u32) max(max_nrg_cck, (data->nrg_value[i])); 18862306a36Sopenharmony_ci max_nrg_cck += 6; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "rx energy a %u, b %u, c %u, 10-bcn max/min %u\n", 19162306a36Sopenharmony_ci rx_info->beacon_energy_a, rx_info->beacon_energy_b, 19262306a36Sopenharmony_ci rx_info->beacon_energy_c, max_nrg_cck - 6); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* Count number of consecutive beacons with fewer-than-desired 19562306a36Sopenharmony_ci * false alarms. */ 19662306a36Sopenharmony_ci if (false_alarms < min_false_alarms) 19762306a36Sopenharmony_ci data->num_in_cck_no_fa++; 19862306a36Sopenharmony_ci else 19962306a36Sopenharmony_ci data->num_in_cck_no_fa = 0; 20062306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "consecutive bcns with few false alarms = %u\n", 20162306a36Sopenharmony_ci data->num_in_cck_no_fa); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* If we got too many false alarms this time, reduce sensitivity */ 20462306a36Sopenharmony_ci if ((false_alarms > max_false_alarms) && 20562306a36Sopenharmony_ci (data->auto_corr_cck > AUTO_CORR_MAX_TH_CCK)) { 20662306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "norm FA %u > max FA %u\n", 20762306a36Sopenharmony_ci false_alarms, max_false_alarms); 20862306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "... reducing sensitivity\n"); 20962306a36Sopenharmony_ci data->nrg_curr_state = IWL_FA_TOO_MANY; 21062306a36Sopenharmony_ci /* Store for "fewer than desired" on later beacon */ 21162306a36Sopenharmony_ci data->nrg_silence_ref = silence_ref; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* increase energy threshold (reduce nrg value) 21462306a36Sopenharmony_ci * to decrease sensitivity */ 21562306a36Sopenharmony_ci data->nrg_th_cck = data->nrg_th_cck - NRG_STEP_CCK; 21662306a36Sopenharmony_ci /* Else if we got fewer than desired, increase sensitivity */ 21762306a36Sopenharmony_ci } else if (false_alarms < min_false_alarms) { 21862306a36Sopenharmony_ci data->nrg_curr_state = IWL_FA_TOO_FEW; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* Compare silence level with silence level for most recent 22162306a36Sopenharmony_ci * healthy number or too many false alarms */ 22262306a36Sopenharmony_ci data->nrg_auto_corr_silence_diff = (s32)data->nrg_silence_ref - 22362306a36Sopenharmony_ci (s32)silence_ref; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "norm FA %u < min FA %u, silence diff %d\n", 22662306a36Sopenharmony_ci false_alarms, min_false_alarms, 22762306a36Sopenharmony_ci data->nrg_auto_corr_silence_diff); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* Increase value to increase sensitivity, but only if: 23062306a36Sopenharmony_ci * 1a) previous beacon did *not* have *too many* false alarms 23162306a36Sopenharmony_ci * 1b) AND there's a significant difference in Rx levels 23262306a36Sopenharmony_ci * from a previous beacon with too many, or healthy # FAs 23362306a36Sopenharmony_ci * OR 2) We've seen a lot of beacons (100) with too few 23462306a36Sopenharmony_ci * false alarms */ 23562306a36Sopenharmony_ci if ((data->nrg_prev_state != IWL_FA_TOO_MANY) && 23662306a36Sopenharmony_ci ((data->nrg_auto_corr_silence_diff > NRG_DIFF) || 23762306a36Sopenharmony_ci (data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) { 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "... increasing sensitivity\n"); 24062306a36Sopenharmony_ci /* Increase nrg value to increase sensitivity */ 24162306a36Sopenharmony_ci val = data->nrg_th_cck + NRG_STEP_CCK; 24262306a36Sopenharmony_ci data->nrg_th_cck = min((u32)ranges->min_nrg_cck, val); 24362306a36Sopenharmony_ci } else { 24462306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "... but not changing sensitivity\n"); 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* Else we got a healthy number of false alarms, keep status quo */ 24862306a36Sopenharmony_ci } else { 24962306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, " FA in safe zone\n"); 25062306a36Sopenharmony_ci data->nrg_curr_state = IWL_FA_GOOD_RANGE; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* Store for use in "fewer than desired" with later beacon */ 25362306a36Sopenharmony_ci data->nrg_silence_ref = silence_ref; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* If previous beacon had too many false alarms, 25662306a36Sopenharmony_ci * give it some extra margin by reducing sensitivity again 25762306a36Sopenharmony_ci * (but don't go below measured energy of desired Rx) */ 25862306a36Sopenharmony_ci if (data->nrg_prev_state == IWL_FA_TOO_MANY) { 25962306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "... increasing margin\n"); 26062306a36Sopenharmony_ci if (data->nrg_th_cck > (max_nrg_cck + NRG_MARGIN)) 26162306a36Sopenharmony_ci data->nrg_th_cck -= NRG_MARGIN; 26262306a36Sopenharmony_ci else 26362306a36Sopenharmony_ci data->nrg_th_cck = max_nrg_cck; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* Make sure the energy threshold does not go above the measured 26862306a36Sopenharmony_ci * energy of the desired Rx signals (reduced by backoff margin), 26962306a36Sopenharmony_ci * or else we might start missing Rx frames. 27062306a36Sopenharmony_ci * Lower value is higher energy, so we use max()! 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_ci data->nrg_th_cck = max(max_nrg_cck, data->nrg_th_cck); 27362306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "new nrg_th_cck %u\n", data->nrg_th_cck); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci data->nrg_prev_state = data->nrg_curr_state; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* Auto-correlation CCK algorithm */ 27862306a36Sopenharmony_ci if (false_alarms > min_false_alarms) { 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* increase auto_corr values to decrease sensitivity 28162306a36Sopenharmony_ci * so the DSP won't be disturbed by the noise 28262306a36Sopenharmony_ci */ 28362306a36Sopenharmony_ci if (data->auto_corr_cck < AUTO_CORR_MAX_TH_CCK) 28462306a36Sopenharmony_ci data->auto_corr_cck = AUTO_CORR_MAX_TH_CCK + 1; 28562306a36Sopenharmony_ci else { 28662306a36Sopenharmony_ci val = data->auto_corr_cck + AUTO_CORR_STEP_CCK; 28762306a36Sopenharmony_ci data->auto_corr_cck = 28862306a36Sopenharmony_ci min((u32)ranges->auto_corr_max_cck, val); 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci val = data->auto_corr_cck_mrc + AUTO_CORR_STEP_CCK; 29162306a36Sopenharmony_ci data->auto_corr_cck_mrc = 29262306a36Sopenharmony_ci min((u32)ranges->auto_corr_max_cck_mrc, val); 29362306a36Sopenharmony_ci } else if ((false_alarms < min_false_alarms) && 29462306a36Sopenharmony_ci ((data->nrg_auto_corr_silence_diff > NRG_DIFF) || 29562306a36Sopenharmony_ci (data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) { 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* Decrease auto_corr values to increase sensitivity */ 29862306a36Sopenharmony_ci val = data->auto_corr_cck - AUTO_CORR_STEP_CCK; 29962306a36Sopenharmony_ci data->auto_corr_cck = 30062306a36Sopenharmony_ci max((u32)ranges->auto_corr_min_cck, val); 30162306a36Sopenharmony_ci val = data->auto_corr_cck_mrc - AUTO_CORR_STEP_CCK; 30262306a36Sopenharmony_ci data->auto_corr_cck_mrc = 30362306a36Sopenharmony_ci max((u32)ranges->auto_corr_min_cck_mrc, val); 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci return 0; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic int iwl_sens_auto_corr_ofdm(struct iwl_priv *priv, 31162306a36Sopenharmony_ci u32 norm_fa, 31262306a36Sopenharmony_ci u32 rx_enable_time) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci u32 val; 31562306a36Sopenharmony_ci u32 false_alarms = norm_fa * 200 * 1024; 31662306a36Sopenharmony_ci u32 max_false_alarms = MAX_FA_OFDM * rx_enable_time; 31762306a36Sopenharmony_ci u32 min_false_alarms = MIN_FA_OFDM * rx_enable_time; 31862306a36Sopenharmony_ci struct iwl_sensitivity_data *data = NULL; 31962306a36Sopenharmony_ci const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci data = &(priv->sensitivity_data); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* If we got too many false alarms this time, reduce sensitivity */ 32462306a36Sopenharmony_ci if (false_alarms > max_false_alarms) { 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "norm FA %u > max FA %u)\n", 32762306a36Sopenharmony_ci false_alarms, max_false_alarms); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci val = data->auto_corr_ofdm + AUTO_CORR_STEP_OFDM; 33062306a36Sopenharmony_ci data->auto_corr_ofdm = 33162306a36Sopenharmony_ci min((u32)ranges->auto_corr_max_ofdm, val); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci val = data->auto_corr_ofdm_mrc + AUTO_CORR_STEP_OFDM; 33462306a36Sopenharmony_ci data->auto_corr_ofdm_mrc = 33562306a36Sopenharmony_ci min((u32)ranges->auto_corr_max_ofdm_mrc, val); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci val = data->auto_corr_ofdm_x1 + AUTO_CORR_STEP_OFDM; 33862306a36Sopenharmony_ci data->auto_corr_ofdm_x1 = 33962306a36Sopenharmony_ci min((u32)ranges->auto_corr_max_ofdm_x1, val); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci val = data->auto_corr_ofdm_mrc_x1 + AUTO_CORR_STEP_OFDM; 34262306a36Sopenharmony_ci data->auto_corr_ofdm_mrc_x1 = 34362306a36Sopenharmony_ci min((u32)ranges->auto_corr_max_ofdm_mrc_x1, val); 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci /* Else if we got fewer than desired, increase sensitivity */ 34762306a36Sopenharmony_ci else if (false_alarms < min_false_alarms) { 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "norm FA %u < min FA %u\n", 35062306a36Sopenharmony_ci false_alarms, min_false_alarms); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci val = data->auto_corr_ofdm - AUTO_CORR_STEP_OFDM; 35362306a36Sopenharmony_ci data->auto_corr_ofdm = 35462306a36Sopenharmony_ci max((u32)ranges->auto_corr_min_ofdm, val); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci val = data->auto_corr_ofdm_mrc - AUTO_CORR_STEP_OFDM; 35762306a36Sopenharmony_ci data->auto_corr_ofdm_mrc = 35862306a36Sopenharmony_ci max((u32)ranges->auto_corr_min_ofdm_mrc, val); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci val = data->auto_corr_ofdm_x1 - AUTO_CORR_STEP_OFDM; 36162306a36Sopenharmony_ci data->auto_corr_ofdm_x1 = 36262306a36Sopenharmony_ci max((u32)ranges->auto_corr_min_ofdm_x1, val); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci val = data->auto_corr_ofdm_mrc_x1 - AUTO_CORR_STEP_OFDM; 36562306a36Sopenharmony_ci data->auto_corr_ofdm_mrc_x1 = 36662306a36Sopenharmony_ci max((u32)ranges->auto_corr_min_ofdm_mrc_x1, val); 36762306a36Sopenharmony_ci } else { 36862306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "min FA %u < norm FA %u < max FA %u OK\n", 36962306a36Sopenharmony_ci min_false_alarms, false_alarms, max_false_alarms); 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci return 0; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic void iwl_prepare_legacy_sensitivity_tbl(struct iwl_priv *priv, 37562306a36Sopenharmony_ci struct iwl_sensitivity_data *data, 37662306a36Sopenharmony_ci __le16 *tbl) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX] = 37962306a36Sopenharmony_ci cpu_to_le16((u16)data->auto_corr_ofdm); 38062306a36Sopenharmony_ci tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX] = 38162306a36Sopenharmony_ci cpu_to_le16((u16)data->auto_corr_ofdm_mrc); 38262306a36Sopenharmony_ci tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX] = 38362306a36Sopenharmony_ci cpu_to_le16((u16)data->auto_corr_ofdm_x1); 38462306a36Sopenharmony_ci tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX] = 38562306a36Sopenharmony_ci cpu_to_le16((u16)data->auto_corr_ofdm_mrc_x1); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX] = 38862306a36Sopenharmony_ci cpu_to_le16((u16)data->auto_corr_cck); 38962306a36Sopenharmony_ci tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX] = 39062306a36Sopenharmony_ci cpu_to_le16((u16)data->auto_corr_cck_mrc); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci tbl[HD_MIN_ENERGY_CCK_DET_INDEX] = 39362306a36Sopenharmony_ci cpu_to_le16((u16)data->nrg_th_cck); 39462306a36Sopenharmony_ci tbl[HD_MIN_ENERGY_OFDM_DET_INDEX] = 39562306a36Sopenharmony_ci cpu_to_le16((u16)data->nrg_th_ofdm); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci tbl[HD_BARKER_CORR_TH_ADD_MIN_INDEX] = 39862306a36Sopenharmony_ci cpu_to_le16(data->barker_corr_th_min); 39962306a36Sopenharmony_ci tbl[HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX] = 40062306a36Sopenharmony_ci cpu_to_le16(data->barker_corr_th_min_mrc); 40162306a36Sopenharmony_ci tbl[HD_OFDM_ENERGY_TH_IN_INDEX] = 40262306a36Sopenharmony_ci cpu_to_le16(data->nrg_th_cca); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "ofdm: ac %u mrc %u x1 %u mrc_x1 %u thresh %u\n", 40562306a36Sopenharmony_ci data->auto_corr_ofdm, data->auto_corr_ofdm_mrc, 40662306a36Sopenharmony_ci data->auto_corr_ofdm_x1, data->auto_corr_ofdm_mrc_x1, 40762306a36Sopenharmony_ci data->nrg_th_ofdm); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "cck: ac %u mrc %u thresh %u\n", 41062306a36Sopenharmony_ci data->auto_corr_cck, data->auto_corr_cck_mrc, 41162306a36Sopenharmony_ci data->nrg_th_cck); 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci/* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */ 41562306a36Sopenharmony_cistatic int iwl_sensitivity_write(struct iwl_priv *priv) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct iwl_sensitivity_cmd cmd; 41862306a36Sopenharmony_ci struct iwl_sensitivity_data *data = NULL; 41962306a36Sopenharmony_ci struct iwl_host_cmd cmd_out = { 42062306a36Sopenharmony_ci .id = SENSITIVITY_CMD, 42162306a36Sopenharmony_ci .len = { sizeof(struct iwl_sensitivity_cmd), }, 42262306a36Sopenharmony_ci .flags = CMD_ASYNC, 42362306a36Sopenharmony_ci .data = { &cmd, }, 42462306a36Sopenharmony_ci }; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci data = &(priv->sensitivity_data); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.table[0]); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* Update uCode's "work" table, and copy it to DSP */ 43362306a36Sopenharmony_ci cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci /* Don't send command to uCode if nothing has changed */ 43662306a36Sopenharmony_ci if (!memcmp(&cmd.table[0], &(priv->sensitivity_tbl[0]), 43762306a36Sopenharmony_ci sizeof(u16)*HD_TABLE_SIZE)) { 43862306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "No change in SENSITIVITY_CMD\n"); 43962306a36Sopenharmony_ci return 0; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* Copy table for comparison next time */ 44362306a36Sopenharmony_ci memcpy(&(priv->sensitivity_tbl[0]), &(cmd.table[0]), 44462306a36Sopenharmony_ci sizeof(u16)*HD_TABLE_SIZE); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci return iwl_dvm_send_cmd(priv, &cmd_out); 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci/* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */ 45062306a36Sopenharmony_cistatic int iwl_enhance_sensitivity_write(struct iwl_priv *priv) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci struct iwl_enhance_sensitivity_cmd cmd; 45362306a36Sopenharmony_ci struct iwl_sensitivity_data *data = NULL; 45462306a36Sopenharmony_ci struct iwl_host_cmd cmd_out = { 45562306a36Sopenharmony_ci .id = SENSITIVITY_CMD, 45662306a36Sopenharmony_ci .len = { sizeof(struct iwl_enhance_sensitivity_cmd), }, 45762306a36Sopenharmony_ci .flags = CMD_ASYNC, 45862306a36Sopenharmony_ci .data = { &cmd, }, 45962306a36Sopenharmony_ci }; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci data = &(priv->sensitivity_data); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.enhance_table[0]); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (priv->lib->hd_v2) { 46862306a36Sopenharmony_ci cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] = 46962306a36Sopenharmony_ci HD_INA_NON_SQUARE_DET_OFDM_DATA_V2; 47062306a36Sopenharmony_ci cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] = 47162306a36Sopenharmony_ci HD_INA_NON_SQUARE_DET_CCK_DATA_V2; 47262306a36Sopenharmony_ci cmd.enhance_table[HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX] = 47362306a36Sopenharmony_ci HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V2; 47462306a36Sopenharmony_ci cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX] = 47562306a36Sopenharmony_ci HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V2; 47662306a36Sopenharmony_ci cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] = 47762306a36Sopenharmony_ci HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2; 47862306a36Sopenharmony_ci cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX] = 47962306a36Sopenharmony_ci HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V2; 48062306a36Sopenharmony_ci cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX] = 48162306a36Sopenharmony_ci HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V2; 48262306a36Sopenharmony_ci cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX] = 48362306a36Sopenharmony_ci HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V2; 48462306a36Sopenharmony_ci cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] = 48562306a36Sopenharmony_ci HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2; 48662306a36Sopenharmony_ci cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_INDEX] = 48762306a36Sopenharmony_ci HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V2; 48862306a36Sopenharmony_ci cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX] = 48962306a36Sopenharmony_ci HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V2; 49062306a36Sopenharmony_ci } else { 49162306a36Sopenharmony_ci cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] = 49262306a36Sopenharmony_ci HD_INA_NON_SQUARE_DET_OFDM_DATA_V1; 49362306a36Sopenharmony_ci cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] = 49462306a36Sopenharmony_ci HD_INA_NON_SQUARE_DET_CCK_DATA_V1; 49562306a36Sopenharmony_ci cmd.enhance_table[HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX] = 49662306a36Sopenharmony_ci HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V1; 49762306a36Sopenharmony_ci cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX] = 49862306a36Sopenharmony_ci HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V1; 49962306a36Sopenharmony_ci cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] = 50062306a36Sopenharmony_ci HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1; 50162306a36Sopenharmony_ci cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX] = 50262306a36Sopenharmony_ci HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V1; 50362306a36Sopenharmony_ci cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX] = 50462306a36Sopenharmony_ci HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V1; 50562306a36Sopenharmony_ci cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX] = 50662306a36Sopenharmony_ci HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V1; 50762306a36Sopenharmony_ci cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] = 50862306a36Sopenharmony_ci HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1; 50962306a36Sopenharmony_ci cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_INDEX] = 51062306a36Sopenharmony_ci HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V1; 51162306a36Sopenharmony_ci cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX] = 51262306a36Sopenharmony_ci HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V1; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* Update uCode's "work" table, and copy it to DSP */ 51662306a36Sopenharmony_ci cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci /* Don't send command to uCode if nothing has changed */ 51962306a36Sopenharmony_ci if (!memcmp(&cmd.enhance_table[0], &(priv->sensitivity_tbl[0]), 52062306a36Sopenharmony_ci sizeof(u16)*HD_TABLE_SIZE) && 52162306a36Sopenharmony_ci !memcmp(&cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX], 52262306a36Sopenharmony_ci &(priv->enhance_sensitivity_tbl[0]), 52362306a36Sopenharmony_ci sizeof(u16)*ENHANCE_HD_TABLE_ENTRIES)) { 52462306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "No change in SENSITIVITY_CMD\n"); 52562306a36Sopenharmony_ci return 0; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci /* Copy table for comparison next time */ 52962306a36Sopenharmony_ci memcpy(&(priv->sensitivity_tbl[0]), &(cmd.enhance_table[0]), 53062306a36Sopenharmony_ci sizeof(u16)*HD_TABLE_SIZE); 53162306a36Sopenharmony_ci memcpy(&(priv->enhance_sensitivity_tbl[0]), 53262306a36Sopenharmony_ci &(cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX]), 53362306a36Sopenharmony_ci sizeof(u16)*ENHANCE_HD_TABLE_ENTRIES); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci return iwl_dvm_send_cmd(priv, &cmd_out); 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_civoid iwl_init_sensitivity(struct iwl_priv *priv) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci int ret = 0; 54162306a36Sopenharmony_ci int i; 54262306a36Sopenharmony_ci struct iwl_sensitivity_data *data = NULL; 54362306a36Sopenharmony_ci const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci if (priv->calib_disabled & IWL_SENSITIVITY_CALIB_DISABLED) 54662306a36Sopenharmony_ci return; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "Start iwl_init_sensitivity\n"); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* Clear driver's sensitivity algo data */ 55162306a36Sopenharmony_ci data = &(priv->sensitivity_data); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (ranges == NULL) 55462306a36Sopenharmony_ci return; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci memset(data, 0, sizeof(struct iwl_sensitivity_data)); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci data->num_in_cck_no_fa = 0; 55962306a36Sopenharmony_ci data->nrg_curr_state = IWL_FA_TOO_MANY; 56062306a36Sopenharmony_ci data->nrg_prev_state = IWL_FA_TOO_MANY; 56162306a36Sopenharmony_ci data->nrg_silence_ref = 0; 56262306a36Sopenharmony_ci data->nrg_silence_idx = 0; 56362306a36Sopenharmony_ci data->nrg_energy_idx = 0; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci for (i = 0; i < 10; i++) 56662306a36Sopenharmony_ci data->nrg_value[i] = 0; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) 56962306a36Sopenharmony_ci data->nrg_silence_rssi[i] = 0; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci data->auto_corr_ofdm = ranges->auto_corr_min_ofdm; 57262306a36Sopenharmony_ci data->auto_corr_ofdm_mrc = ranges->auto_corr_min_ofdm_mrc; 57362306a36Sopenharmony_ci data->auto_corr_ofdm_x1 = ranges->auto_corr_min_ofdm_x1; 57462306a36Sopenharmony_ci data->auto_corr_ofdm_mrc_x1 = ranges->auto_corr_min_ofdm_mrc_x1; 57562306a36Sopenharmony_ci data->auto_corr_cck = AUTO_CORR_CCK_MIN_VAL_DEF; 57662306a36Sopenharmony_ci data->auto_corr_cck_mrc = ranges->auto_corr_min_cck_mrc; 57762306a36Sopenharmony_ci data->nrg_th_cck = ranges->nrg_th_cck; 57862306a36Sopenharmony_ci data->nrg_th_ofdm = ranges->nrg_th_ofdm; 57962306a36Sopenharmony_ci data->barker_corr_th_min = ranges->barker_corr_th_min; 58062306a36Sopenharmony_ci data->barker_corr_th_min_mrc = ranges->barker_corr_th_min_mrc; 58162306a36Sopenharmony_ci data->nrg_th_cca = ranges->nrg_th_cca; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci data->last_bad_plcp_cnt_ofdm = 0; 58462306a36Sopenharmony_ci data->last_fa_cnt_ofdm = 0; 58562306a36Sopenharmony_ci data->last_bad_plcp_cnt_cck = 0; 58662306a36Sopenharmony_ci data->last_fa_cnt_cck = 0; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (priv->fw->enhance_sensitivity_table) 58962306a36Sopenharmony_ci ret |= iwl_enhance_sensitivity_write(priv); 59062306a36Sopenharmony_ci else 59162306a36Sopenharmony_ci ret |= iwl_sensitivity_write(priv); 59262306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "<<return 0x%X\n", ret); 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_civoid iwl_sensitivity_calibration(struct iwl_priv *priv) 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci u32 rx_enable_time; 59862306a36Sopenharmony_ci u32 fa_cck; 59962306a36Sopenharmony_ci u32 fa_ofdm; 60062306a36Sopenharmony_ci u32 bad_plcp_cck; 60162306a36Sopenharmony_ci u32 bad_plcp_ofdm; 60262306a36Sopenharmony_ci u32 norm_fa_ofdm; 60362306a36Sopenharmony_ci u32 norm_fa_cck; 60462306a36Sopenharmony_ci struct iwl_sensitivity_data *data = NULL; 60562306a36Sopenharmony_ci struct statistics_rx_non_phy *rx_info; 60662306a36Sopenharmony_ci struct statistics_rx_phy *ofdm, *cck; 60762306a36Sopenharmony_ci struct statistics_general_data statis; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci if (priv->calib_disabled & IWL_SENSITIVITY_CALIB_DISABLED) 61062306a36Sopenharmony_ci return; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci data = &(priv->sensitivity_data); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (!iwl_is_any_associated(priv)) { 61562306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "<< - not associated\n"); 61662306a36Sopenharmony_ci return; 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci spin_lock_bh(&priv->statistics.lock); 62062306a36Sopenharmony_ci rx_info = &priv->statistics.rx_non_phy; 62162306a36Sopenharmony_ci ofdm = &priv->statistics.rx_ofdm; 62262306a36Sopenharmony_ci cck = &priv->statistics.rx_cck; 62362306a36Sopenharmony_ci if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) { 62462306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "<< invalid data.\n"); 62562306a36Sopenharmony_ci spin_unlock_bh(&priv->statistics.lock); 62662306a36Sopenharmony_ci return; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci /* Extract Statistics: */ 63062306a36Sopenharmony_ci rx_enable_time = le32_to_cpu(rx_info->channel_load); 63162306a36Sopenharmony_ci fa_cck = le32_to_cpu(cck->false_alarm_cnt); 63262306a36Sopenharmony_ci fa_ofdm = le32_to_cpu(ofdm->false_alarm_cnt); 63362306a36Sopenharmony_ci bad_plcp_cck = le32_to_cpu(cck->plcp_err); 63462306a36Sopenharmony_ci bad_plcp_ofdm = le32_to_cpu(ofdm->plcp_err); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci statis.beacon_silence_rssi_a = 63762306a36Sopenharmony_ci le32_to_cpu(rx_info->beacon_silence_rssi_a); 63862306a36Sopenharmony_ci statis.beacon_silence_rssi_b = 63962306a36Sopenharmony_ci le32_to_cpu(rx_info->beacon_silence_rssi_b); 64062306a36Sopenharmony_ci statis.beacon_silence_rssi_c = 64162306a36Sopenharmony_ci le32_to_cpu(rx_info->beacon_silence_rssi_c); 64262306a36Sopenharmony_ci statis.beacon_energy_a = 64362306a36Sopenharmony_ci le32_to_cpu(rx_info->beacon_energy_a); 64462306a36Sopenharmony_ci statis.beacon_energy_b = 64562306a36Sopenharmony_ci le32_to_cpu(rx_info->beacon_energy_b); 64662306a36Sopenharmony_ci statis.beacon_energy_c = 64762306a36Sopenharmony_ci le32_to_cpu(rx_info->beacon_energy_c); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci spin_unlock_bh(&priv->statistics.lock); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "rx_enable_time = %u usecs\n", rx_enable_time); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (!rx_enable_time) { 65462306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "<< RX Enable Time == 0!\n"); 65562306a36Sopenharmony_ci return; 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci /* These statistics increase monotonically, and do not reset 65962306a36Sopenharmony_ci * at each beacon. Calculate difference from last value, or just 66062306a36Sopenharmony_ci * use the new statistics value if it has reset or wrapped around. */ 66162306a36Sopenharmony_ci if (data->last_bad_plcp_cnt_cck > bad_plcp_cck) 66262306a36Sopenharmony_ci data->last_bad_plcp_cnt_cck = bad_plcp_cck; 66362306a36Sopenharmony_ci else { 66462306a36Sopenharmony_ci bad_plcp_cck -= data->last_bad_plcp_cnt_cck; 66562306a36Sopenharmony_ci data->last_bad_plcp_cnt_cck += bad_plcp_cck; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci if (data->last_bad_plcp_cnt_ofdm > bad_plcp_ofdm) 66962306a36Sopenharmony_ci data->last_bad_plcp_cnt_ofdm = bad_plcp_ofdm; 67062306a36Sopenharmony_ci else { 67162306a36Sopenharmony_ci bad_plcp_ofdm -= data->last_bad_plcp_cnt_ofdm; 67262306a36Sopenharmony_ci data->last_bad_plcp_cnt_ofdm += bad_plcp_ofdm; 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci if (data->last_fa_cnt_ofdm > fa_ofdm) 67662306a36Sopenharmony_ci data->last_fa_cnt_ofdm = fa_ofdm; 67762306a36Sopenharmony_ci else { 67862306a36Sopenharmony_ci fa_ofdm -= data->last_fa_cnt_ofdm; 67962306a36Sopenharmony_ci data->last_fa_cnt_ofdm += fa_ofdm; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (data->last_fa_cnt_cck > fa_cck) 68362306a36Sopenharmony_ci data->last_fa_cnt_cck = fa_cck; 68462306a36Sopenharmony_ci else { 68562306a36Sopenharmony_ci fa_cck -= data->last_fa_cnt_cck; 68662306a36Sopenharmony_ci data->last_fa_cnt_cck += fa_cck; 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci /* Total aborted signal locks */ 69062306a36Sopenharmony_ci norm_fa_ofdm = fa_ofdm + bad_plcp_ofdm; 69162306a36Sopenharmony_ci norm_fa_cck = fa_cck + bad_plcp_cck; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "cck: fa %u badp %u ofdm: fa %u badp %u\n", fa_cck, 69462306a36Sopenharmony_ci bad_plcp_cck, fa_ofdm, bad_plcp_ofdm); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci iwl_sens_auto_corr_ofdm(priv, norm_fa_ofdm, rx_enable_time); 69762306a36Sopenharmony_ci iwl_sens_energy_cck(priv, norm_fa_cck, rx_enable_time, &statis); 69862306a36Sopenharmony_ci if (priv->fw->enhance_sensitivity_table) 69962306a36Sopenharmony_ci iwl_enhance_sensitivity_write(priv); 70062306a36Sopenharmony_ci else 70162306a36Sopenharmony_ci iwl_sensitivity_write(priv); 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cistatic inline u8 find_first_chain(u8 mask) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci if (mask & ANT_A) 70762306a36Sopenharmony_ci return CHAIN_A; 70862306a36Sopenharmony_ci if (mask & ANT_B) 70962306a36Sopenharmony_ci return CHAIN_B; 71062306a36Sopenharmony_ci return CHAIN_C; 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci/* 71462306a36Sopenharmony_ci * Run disconnected antenna algorithm to find out which antennas are 71562306a36Sopenharmony_ci * disconnected. 71662306a36Sopenharmony_ci */ 71762306a36Sopenharmony_cistatic void iwl_find_disconn_antenna(struct iwl_priv *priv, u32* average_sig, 71862306a36Sopenharmony_ci struct iwl_chain_noise_data *data) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci u32 active_chains = 0; 72162306a36Sopenharmony_ci u32 max_average_sig; 72262306a36Sopenharmony_ci u16 max_average_sig_antenna_i; 72362306a36Sopenharmony_ci u8 num_tx_chains; 72462306a36Sopenharmony_ci u8 first_chain; 72562306a36Sopenharmony_ci u16 i = 0; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci average_sig[0] = data->chain_signal_a / IWL_CAL_NUM_BEACONS; 72862306a36Sopenharmony_ci average_sig[1] = data->chain_signal_b / IWL_CAL_NUM_BEACONS; 72962306a36Sopenharmony_ci average_sig[2] = data->chain_signal_c / IWL_CAL_NUM_BEACONS; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci if (average_sig[0] >= average_sig[1]) { 73262306a36Sopenharmony_ci max_average_sig = average_sig[0]; 73362306a36Sopenharmony_ci max_average_sig_antenna_i = 0; 73462306a36Sopenharmony_ci active_chains = (1 << max_average_sig_antenna_i); 73562306a36Sopenharmony_ci } else { 73662306a36Sopenharmony_ci max_average_sig = average_sig[1]; 73762306a36Sopenharmony_ci max_average_sig_antenna_i = 1; 73862306a36Sopenharmony_ci active_chains = (1 << max_average_sig_antenna_i); 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci if (average_sig[2] >= max_average_sig) { 74262306a36Sopenharmony_ci max_average_sig = average_sig[2]; 74362306a36Sopenharmony_ci max_average_sig_antenna_i = 2; 74462306a36Sopenharmony_ci active_chains = (1 << max_average_sig_antenna_i); 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "average_sig: a %d b %d c %d\n", 74862306a36Sopenharmony_ci average_sig[0], average_sig[1], average_sig[2]); 74962306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "max_average_sig = %d, antenna %d\n", 75062306a36Sopenharmony_ci max_average_sig, max_average_sig_antenna_i); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci /* Compare signal strengths for all 3 receivers. */ 75362306a36Sopenharmony_ci for (i = 0; i < NUM_RX_CHAINS; i++) { 75462306a36Sopenharmony_ci if (i != max_average_sig_antenna_i) { 75562306a36Sopenharmony_ci s32 rssi_delta = (max_average_sig - average_sig[i]); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci /* If signal is very weak, compared with 75862306a36Sopenharmony_ci * strongest, mark it as disconnected. */ 75962306a36Sopenharmony_ci if (rssi_delta > MAXIMUM_ALLOWED_PATHLOSS) 76062306a36Sopenharmony_ci data->disconn_array[i] = 1; 76162306a36Sopenharmony_ci else 76262306a36Sopenharmony_ci active_chains |= (1 << i); 76362306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "i = %d rssiDelta = %d " 76462306a36Sopenharmony_ci "disconn_array[i] = %d\n", 76562306a36Sopenharmony_ci i, rssi_delta, data->disconn_array[i]); 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* 77062306a36Sopenharmony_ci * The above algorithm sometimes fails when the ucode 77162306a36Sopenharmony_ci * reports 0 for all chains. It's not clear why that 77262306a36Sopenharmony_ci * happens to start with, but it is then causing trouble 77362306a36Sopenharmony_ci * because this can make us enable more chains than the 77462306a36Sopenharmony_ci * hardware really has. 77562306a36Sopenharmony_ci * 77662306a36Sopenharmony_ci * To be safe, simply mask out any chains that we know 77762306a36Sopenharmony_ci * are not on the device. 77862306a36Sopenharmony_ci */ 77962306a36Sopenharmony_ci active_chains &= priv->nvm_data->valid_rx_ant; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci num_tx_chains = 0; 78262306a36Sopenharmony_ci for (i = 0; i < NUM_RX_CHAINS; i++) { 78362306a36Sopenharmony_ci /* loops on all the bits of 78462306a36Sopenharmony_ci * priv->hw_setting.valid_tx_ant */ 78562306a36Sopenharmony_ci u8 ant_msk = (1 << i); 78662306a36Sopenharmony_ci if (!(priv->nvm_data->valid_tx_ant & ant_msk)) 78762306a36Sopenharmony_ci continue; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci num_tx_chains++; 79062306a36Sopenharmony_ci if (data->disconn_array[i] == 0) 79162306a36Sopenharmony_ci /* there is a Tx antenna connected */ 79262306a36Sopenharmony_ci break; 79362306a36Sopenharmony_ci if (num_tx_chains == priv->hw_params.tx_chains_num && 79462306a36Sopenharmony_ci data->disconn_array[i]) { 79562306a36Sopenharmony_ci /* 79662306a36Sopenharmony_ci * If all chains are disconnected 79762306a36Sopenharmony_ci * connect the first valid tx chain 79862306a36Sopenharmony_ci */ 79962306a36Sopenharmony_ci first_chain = 80062306a36Sopenharmony_ci find_first_chain(priv->nvm_data->valid_tx_ant); 80162306a36Sopenharmony_ci data->disconn_array[first_chain] = 0; 80262306a36Sopenharmony_ci active_chains |= BIT(first_chain); 80362306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, 80462306a36Sopenharmony_ci "All Tx chains are disconnected W/A - declare %d as connected\n", 80562306a36Sopenharmony_ci first_chain); 80662306a36Sopenharmony_ci break; 80762306a36Sopenharmony_ci } 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci if (active_chains != priv->nvm_data->valid_rx_ant && 81162306a36Sopenharmony_ci active_chains != priv->chain_noise_data.active_chains) 81262306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, 81362306a36Sopenharmony_ci "Detected that not all antennas are connected! " 81462306a36Sopenharmony_ci "Connected: %#x, valid: %#x.\n", 81562306a36Sopenharmony_ci active_chains, 81662306a36Sopenharmony_ci priv->nvm_data->valid_rx_ant); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci /* Save for use within RXON, TX, SCAN commands, etc. */ 81962306a36Sopenharmony_ci data->active_chains = active_chains; 82062306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "active_chains (bitwise) = 0x%x\n", 82162306a36Sopenharmony_ci active_chains); 82262306a36Sopenharmony_ci} 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_cistatic void iwlagn_gain_computation(struct iwl_priv *priv, 82562306a36Sopenharmony_ci u32 average_noise[NUM_RX_CHAINS], 82662306a36Sopenharmony_ci u8 default_chain) 82762306a36Sopenharmony_ci{ 82862306a36Sopenharmony_ci int i; 82962306a36Sopenharmony_ci s32 delta_g; 83062306a36Sopenharmony_ci struct iwl_chain_noise_data *data = &priv->chain_noise_data; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci /* 83362306a36Sopenharmony_ci * Find Gain Code for the chains based on "default chain" 83462306a36Sopenharmony_ci */ 83562306a36Sopenharmony_ci for (i = default_chain + 1; i < NUM_RX_CHAINS; i++) { 83662306a36Sopenharmony_ci if ((data->disconn_array[i])) { 83762306a36Sopenharmony_ci data->delta_gain_code[i] = 0; 83862306a36Sopenharmony_ci continue; 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci delta_g = (priv->lib->chain_noise_scale * 84262306a36Sopenharmony_ci ((s32)average_noise[default_chain] - 84362306a36Sopenharmony_ci (s32)average_noise[i])) / 1500; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci /* bound gain by 2 bits value max, 3rd bit is sign */ 84662306a36Sopenharmony_ci data->delta_gain_code[i] = 84762306a36Sopenharmony_ci min(abs(delta_g), CHAIN_NOISE_MAX_DELTA_GAIN_CODE); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci if (delta_g < 0) 85062306a36Sopenharmony_ci /* 85162306a36Sopenharmony_ci * set negative sign ... 85262306a36Sopenharmony_ci * note to Intel developers: This is uCode API format, 85362306a36Sopenharmony_ci * not the format of any internal device registers. 85462306a36Sopenharmony_ci * Do not change this format for e.g. 6050 or similar 85562306a36Sopenharmony_ci * devices. Change format only if more resolution 85662306a36Sopenharmony_ci * (i.e. more than 2 bits magnitude) is needed. 85762306a36Sopenharmony_ci */ 85862306a36Sopenharmony_ci data->delta_gain_code[i] |= (1 << 2); 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "Delta gains: ANT_B = %d ANT_C = %d\n", 86262306a36Sopenharmony_ci data->delta_gain_code[1], data->delta_gain_code[2]); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci if (!data->radio_write) { 86562306a36Sopenharmony_ci struct iwl_calib_chain_noise_gain_cmd cmd; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci iwl_set_calib_hdr(&cmd.hdr, 87062306a36Sopenharmony_ci priv->phy_calib_chain_noise_gain_cmd); 87162306a36Sopenharmony_ci cmd.delta_gain_1 = data->delta_gain_code[1]; 87262306a36Sopenharmony_ci cmd.delta_gain_2 = data->delta_gain_code[2]; 87362306a36Sopenharmony_ci iwl_dvm_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD, 87462306a36Sopenharmony_ci CMD_ASYNC, sizeof(cmd), &cmd); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci data->radio_write = 1; 87762306a36Sopenharmony_ci data->state = IWL_CHAIN_NOISE_CALIBRATED; 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci} 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci/* 88262306a36Sopenharmony_ci * Accumulate 16 beacons of signal and noise statistics for each of 88362306a36Sopenharmony_ci * 3 receivers/antennas/rx-chains, then figure out: 88462306a36Sopenharmony_ci * 1) Which antennas are connected. 88562306a36Sopenharmony_ci * 2) Differential rx gain settings to balance the 3 receivers. 88662306a36Sopenharmony_ci */ 88762306a36Sopenharmony_civoid iwl_chain_noise_calibration(struct iwl_priv *priv) 88862306a36Sopenharmony_ci{ 88962306a36Sopenharmony_ci struct iwl_chain_noise_data *data = NULL; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci u32 chain_noise_a; 89262306a36Sopenharmony_ci u32 chain_noise_b; 89362306a36Sopenharmony_ci u32 chain_noise_c; 89462306a36Sopenharmony_ci u32 chain_sig_a; 89562306a36Sopenharmony_ci u32 chain_sig_b; 89662306a36Sopenharmony_ci u32 chain_sig_c; 89762306a36Sopenharmony_ci u32 average_sig[NUM_RX_CHAINS] = {INITIALIZATION_VALUE}; 89862306a36Sopenharmony_ci u32 average_noise[NUM_RX_CHAINS] = {INITIALIZATION_VALUE}; 89962306a36Sopenharmony_ci u32 min_average_noise = MIN_AVERAGE_NOISE_MAX_VALUE; 90062306a36Sopenharmony_ci u16 min_average_noise_antenna_i = INITIALIZATION_VALUE; 90162306a36Sopenharmony_ci u16 i = 0; 90262306a36Sopenharmony_ci u16 rxon_chnum = INITIALIZATION_VALUE; 90362306a36Sopenharmony_ci u16 stat_chnum = INITIALIZATION_VALUE; 90462306a36Sopenharmony_ci u8 rxon_band24; 90562306a36Sopenharmony_ci u8 stat_band24; 90662306a36Sopenharmony_ci struct statistics_rx_non_phy *rx_info; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci /* 90962306a36Sopenharmony_ci * MULTI-FIXME: 91062306a36Sopenharmony_ci * When we support multiple interfaces on different channels, 91162306a36Sopenharmony_ci * this must be modified/fixed. 91262306a36Sopenharmony_ci */ 91362306a36Sopenharmony_ci struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci if (priv->calib_disabled & IWL_CHAIN_NOISE_CALIB_DISABLED) 91662306a36Sopenharmony_ci return; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci data = &(priv->chain_noise_data); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci /* 92162306a36Sopenharmony_ci * Accumulate just the first "chain_noise_num_beacons" after 92262306a36Sopenharmony_ci * the first association, then we're done forever. 92362306a36Sopenharmony_ci */ 92462306a36Sopenharmony_ci if (data->state != IWL_CHAIN_NOISE_ACCUMULATE) { 92562306a36Sopenharmony_ci if (data->state == IWL_CHAIN_NOISE_ALIVE) 92662306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "Wait for noise calib reset\n"); 92762306a36Sopenharmony_ci return; 92862306a36Sopenharmony_ci } 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci spin_lock_bh(&priv->statistics.lock); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci rx_info = &priv->statistics.rx_non_phy; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) { 93562306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, " << Interference data unavailable\n"); 93662306a36Sopenharmony_ci spin_unlock_bh(&priv->statistics.lock); 93762306a36Sopenharmony_ci return; 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci rxon_band24 = !!(ctx->staging.flags & RXON_FLG_BAND_24G_MSK); 94162306a36Sopenharmony_ci rxon_chnum = le16_to_cpu(ctx->staging.channel); 94262306a36Sopenharmony_ci stat_band24 = 94362306a36Sopenharmony_ci !!(priv->statistics.flag & STATISTICS_REPLY_FLG_BAND_24G_MSK); 94462306a36Sopenharmony_ci stat_chnum = le32_to_cpu(priv->statistics.flag) >> 16; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci /* Make sure we accumulate data for just the associated channel 94762306a36Sopenharmony_ci * (even if scanning). */ 94862306a36Sopenharmony_ci if ((rxon_chnum != stat_chnum) || (rxon_band24 != stat_band24)) { 94962306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "Stats not from chan=%d, band24=%d\n", 95062306a36Sopenharmony_ci rxon_chnum, rxon_band24); 95162306a36Sopenharmony_ci spin_unlock_bh(&priv->statistics.lock); 95262306a36Sopenharmony_ci return; 95362306a36Sopenharmony_ci } 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci /* 95662306a36Sopenharmony_ci * Accumulate beacon statistics values across 95762306a36Sopenharmony_ci * "chain_noise_num_beacons" 95862306a36Sopenharmony_ci */ 95962306a36Sopenharmony_ci chain_noise_a = le32_to_cpu(rx_info->beacon_silence_rssi_a) & 96062306a36Sopenharmony_ci IN_BAND_FILTER; 96162306a36Sopenharmony_ci chain_noise_b = le32_to_cpu(rx_info->beacon_silence_rssi_b) & 96262306a36Sopenharmony_ci IN_BAND_FILTER; 96362306a36Sopenharmony_ci chain_noise_c = le32_to_cpu(rx_info->beacon_silence_rssi_c) & 96462306a36Sopenharmony_ci IN_BAND_FILTER; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci chain_sig_a = le32_to_cpu(rx_info->beacon_rssi_a) & IN_BAND_FILTER; 96762306a36Sopenharmony_ci chain_sig_b = le32_to_cpu(rx_info->beacon_rssi_b) & IN_BAND_FILTER; 96862306a36Sopenharmony_ci chain_sig_c = le32_to_cpu(rx_info->beacon_rssi_c) & IN_BAND_FILTER; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci spin_unlock_bh(&priv->statistics.lock); 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci data->beacon_count++; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci data->chain_noise_a = (chain_noise_a + data->chain_noise_a); 97562306a36Sopenharmony_ci data->chain_noise_b = (chain_noise_b + data->chain_noise_b); 97662306a36Sopenharmony_ci data->chain_noise_c = (chain_noise_c + data->chain_noise_c); 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci data->chain_signal_a = (chain_sig_a + data->chain_signal_a); 97962306a36Sopenharmony_ci data->chain_signal_b = (chain_sig_b + data->chain_signal_b); 98062306a36Sopenharmony_ci data->chain_signal_c = (chain_sig_c + data->chain_signal_c); 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "chan=%d, band24=%d, beacon=%d\n", 98362306a36Sopenharmony_ci rxon_chnum, rxon_band24, data->beacon_count); 98462306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "chain_sig: a %d b %d c %d\n", 98562306a36Sopenharmony_ci chain_sig_a, chain_sig_b, chain_sig_c); 98662306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "chain_noise: a %d b %d c %d\n", 98762306a36Sopenharmony_ci chain_noise_a, chain_noise_b, chain_noise_c); 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci /* If this is the "chain_noise_num_beacons", determine: 99062306a36Sopenharmony_ci * 1) Disconnected antennas (using signal strengths) 99162306a36Sopenharmony_ci * 2) Differential gain (using silence noise) to balance receivers */ 99262306a36Sopenharmony_ci if (data->beacon_count != IWL_CAL_NUM_BEACONS) 99362306a36Sopenharmony_ci return; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci /* Analyze signal for disconnected antenna */ 99662306a36Sopenharmony_ci if (priv->lib->bt_params && 99762306a36Sopenharmony_ci priv->lib->bt_params->advanced_bt_coexist) { 99862306a36Sopenharmony_ci /* Disable disconnected antenna algorithm for advanced 99962306a36Sopenharmony_ci bt coex, assuming valid antennas are connected */ 100062306a36Sopenharmony_ci data->active_chains = priv->nvm_data->valid_rx_ant; 100162306a36Sopenharmony_ci for (i = 0; i < NUM_RX_CHAINS; i++) 100262306a36Sopenharmony_ci if (!(data->active_chains & (1<<i))) 100362306a36Sopenharmony_ci data->disconn_array[i] = 1; 100462306a36Sopenharmony_ci } else 100562306a36Sopenharmony_ci iwl_find_disconn_antenna(priv, average_sig, data); 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci /* Analyze noise for rx balance */ 100862306a36Sopenharmony_ci average_noise[0] = data->chain_noise_a / IWL_CAL_NUM_BEACONS; 100962306a36Sopenharmony_ci average_noise[1] = data->chain_noise_b / IWL_CAL_NUM_BEACONS; 101062306a36Sopenharmony_ci average_noise[2] = data->chain_noise_c / IWL_CAL_NUM_BEACONS; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci for (i = 0; i < NUM_RX_CHAINS; i++) { 101362306a36Sopenharmony_ci if (!(data->disconn_array[i]) && 101462306a36Sopenharmony_ci (average_noise[i] <= min_average_noise)) { 101562306a36Sopenharmony_ci /* This means that chain i is active and has 101662306a36Sopenharmony_ci * lower noise values so far: */ 101762306a36Sopenharmony_ci min_average_noise = average_noise[i]; 101862306a36Sopenharmony_ci min_average_noise_antenna_i = i; 101962306a36Sopenharmony_ci } 102062306a36Sopenharmony_ci } 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "average_noise: a %d b %d c %d\n", 102362306a36Sopenharmony_ci average_noise[0], average_noise[1], 102462306a36Sopenharmony_ci average_noise[2]); 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci IWL_DEBUG_CALIB(priv, "min_average_noise = %d, antenna %d\n", 102762306a36Sopenharmony_ci min_average_noise, min_average_noise_antenna_i); 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci iwlagn_gain_computation( 103062306a36Sopenharmony_ci priv, average_noise, 103162306a36Sopenharmony_ci find_first_chain(priv->nvm_data->valid_rx_ant)); 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci /* Some power changes may have been made during the calibration. 103462306a36Sopenharmony_ci * Update and commit the RXON 103562306a36Sopenharmony_ci */ 103662306a36Sopenharmony_ci iwl_update_chain_flags(priv); 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci data->state = IWL_CHAIN_NOISE_DONE; 103962306a36Sopenharmony_ci iwl_power_update_mode(priv, false); 104062306a36Sopenharmony_ci} 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_civoid iwl_reset_run_time_calib(struct iwl_priv *priv) 104362306a36Sopenharmony_ci{ 104462306a36Sopenharmony_ci int i; 104562306a36Sopenharmony_ci memset(&(priv->sensitivity_data), 0, 104662306a36Sopenharmony_ci sizeof(struct iwl_sensitivity_data)); 104762306a36Sopenharmony_ci memset(&(priv->chain_noise_data), 0, 104862306a36Sopenharmony_ci sizeof(struct iwl_chain_noise_data)); 104962306a36Sopenharmony_ci for (i = 0; i < NUM_RX_CHAINS; i++) 105062306a36Sopenharmony_ci priv->chain_noise_data.delta_gain_code[i] = 105162306a36Sopenharmony_ci CHAIN_NOISE_DELTA_GAIN_INIT_VAL; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci /* Ask for statistics now, the uCode will send notification 105462306a36Sopenharmony_ci * periodically after association */ 105562306a36Sopenharmony_ci iwl_send_statistics_request(priv, CMD_ASYNC, true); 105662306a36Sopenharmony_ci} 1057