aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless
diff options
context:
space:
mode:
authorEmmanuel Grumbach <emmanuel.grumbach@intel.com>2008-04-16 19:34:47 -0400
committerJohn W. Linville <linville@tuxdriver.com>2008-05-07 15:02:10 -0400
commitf0832f137c21d130998a0f97f97ac01a2d97210b (patch)
tree6b9af836baa6133caaf4aebe3717719c9cbb7725 /drivers/net/wireless
parenta7ca0268b5dfffcaa8a1fe40c6eccdeac50fa3ea (diff)
iwlwifi: HW dependent run time calibration
This patch does several things: 1) rename CONFIG_IWL4965_SENSITIVITY to IWL4965_RUN_TIME_CALIB which is better semantic 2) move all the run time calibration to a new file: iwl-calib.c 3) simplify the sensitivity calibration flow and make it HW dependent 4) make the chain noise calibration flow HW dependent Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com> Signed-off-by: Tomas Winkler <tomas.winkler@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r--drivers/net/wireless/iwlwifi/Kconfig19
-rw-r--r--drivers/net/wireless/iwlwifi/Makefile1
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-4965-commands.h8
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-4965.c870
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-4965.h68
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-calib.c778
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-calib.h104
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.h7
-rw-r--r--drivers/net/wireless/iwlwifi/iwl4965-base.c18
9 files changed, 1054 insertions, 819 deletions
diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig
index d5b7a76fcaad..a5fdd0f21a7a 100644
--- a/drivers/net/wireless/iwlwifi/Kconfig
+++ b/drivers/net/wireless/iwlwifi/Kconfig
@@ -15,6 +15,15 @@ config IWLWIFI_LEDS
15 bool 15 bool
16 default n 16 default n
17 17
18config IWLWIFI_RUN_TIME_CALIB
19 bool
20 depends on IWLCORE
21 default n
22 ---help---
23 This option will enable run time calibration for the iwlwifi driver.
24 These calibrations are Sensitivity and Chain Noise.
25
26
18config IWLWIFI_RFKILL 27config IWLWIFI_RFKILL
19 boolean "IWLWIFI RF kill support" 28 boolean "IWLWIFI RF kill support"
20 depends on IWLCORE 29 depends on IWLCORE
@@ -68,12 +77,14 @@ config IWL4965_SPECTRUM_MEASUREMENT
68 ---help--- 77 ---help---
69 This option will enable spectrum measurement for the iwl4965 driver. 78 This option will enable spectrum measurement for the iwl4965 driver.
70 79
71config IWL4965_SENSITIVITY 80config IWL4965_RUN_TIME_CALIB
72 bool "Enable Sensitivity Calibration in iwl4965 driver" 81 bool "Enable run time Calibration for 4965 NIC"
82 select IWLWIFI_RUN_TIME_CALIB
73 depends on IWL4965 83 depends on IWL4965
84 default y
74 ---help--- 85 ---help---
75 This option will enable sensitivity calibration for the iwl4965 86 This option will enable run time calibration for the iwl4965 driver.
76 driver. 87 These calibrations are Sensitivity and Chain Noise. If unsure, say yes
77 88
78config IWLWIFI_DEBUG 89config IWLWIFI_DEBUG
79 bool "Enable full debugging output in iwl4965 driver" 90 bool "Enable full debugging output in iwl4965 driver"
diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile
index ec6187b75c3b..7bc569be4ba7 100644
--- a/drivers/net/wireless/iwlwifi/Makefile
+++ b/drivers/net/wireless/iwlwifi/Makefile
@@ -3,6 +3,7 @@ iwlcore-objs := iwl-core.o iwl-eeprom.o iwl-hcmd.o
3iwlcore-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-debugfs.o 3iwlcore-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-debugfs.o
4iwlcore-$(CONFIG_IWLWIFI_LEDS) += iwl-led.o 4iwlcore-$(CONFIG_IWLWIFI_LEDS) += iwl-led.o
5iwlcore-$(CONFIG_IWLWIFI_RFKILL) += iwl-rfkill.o 5iwlcore-$(CONFIG_IWLWIFI_RFKILL) += iwl-rfkill.o
6iwlcore-$(CONFIG_IWLWIFI_RUN_TIME_CALIB) += iwl-calib.o
6 7
7obj-$(CONFIG_IWL3945) += iwl3945.o 8obj-$(CONFIG_IWL3945) += iwl3945.o
8iwl3945-objs := iwl3945-base.o iwl-3945.o iwl-3945-rs.o 9iwl3945-objs := iwl3945-base.o iwl-3945.o iwl-3945-rs.o
diff --git a/drivers/net/wireless/iwlwifi/iwl-4965-commands.h b/drivers/net/wireless/iwlwifi/iwl-4965-commands.h
index 3bcd107e2d71..c03c04fe14c7 100644
--- a/drivers/net/wireless/iwlwifi/iwl-4965-commands.h
+++ b/drivers/net/wireless/iwlwifi/iwl-4965-commands.h
@@ -2559,7 +2559,7 @@ struct iwl4965_missed_beacon_notif {
2559 */ 2559 */
2560 2560
2561/* 2561/*
2562 * Table entries in SENSITIVITY_CMD (struct iwl4965_sensitivity_cmd) 2562 * Table entries in SENSITIVITY_CMD (struct iwl_sensitivity_cmd)
2563 */ 2563 */
2564#define HD_TABLE_SIZE (11) /* number of entries */ 2564#define HD_TABLE_SIZE (11) /* number of entries */
2565#define HD_MIN_ENERGY_CCK_DET_INDEX (0) /* table indexes */ 2565#define HD_MIN_ENERGY_CCK_DET_INDEX (0) /* table indexes */
@@ -2574,18 +2574,18 @@ struct iwl4965_missed_beacon_notif {
2574#define HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX (9) 2574#define HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX (9)
2575#define HD_OFDM_ENERGY_TH_IN_INDEX (10) 2575#define HD_OFDM_ENERGY_TH_IN_INDEX (10)
2576 2576
2577/* Control field in struct iwl4965_sensitivity_cmd */ 2577/* Control field in struct iwl_sensitivity_cmd */
2578#define SENSITIVITY_CMD_CONTROL_DEFAULT_TABLE __constant_cpu_to_le16(0) 2578#define SENSITIVITY_CMD_CONTROL_DEFAULT_TABLE __constant_cpu_to_le16(0)
2579#define SENSITIVITY_CMD_CONTROL_WORK_TABLE __constant_cpu_to_le16(1) 2579#define SENSITIVITY_CMD_CONTROL_WORK_TABLE __constant_cpu_to_le16(1)
2580 2580
2581/** 2581/**
2582 * struct iwl4965_sensitivity_cmd 2582 * struct iwl_sensitivity_cmd
2583 * @control: (1) updates working table, (0) updates default table 2583 * @control: (1) updates working table, (0) updates default table
2584 * @table: energy threshold values, use HD_* as index into table 2584 * @table: energy threshold values, use HD_* as index into table
2585 * 2585 *
2586 * Always use "1" in "control" to update uCode's working table and DSP. 2586 * Always use "1" in "control" to update uCode's working table and DSP.
2587 */ 2587 */
2588struct iwl4965_sensitivity_cmd { 2588struct iwl_sensitivity_cmd {
2589 __le16 control; /* always use "1" */ 2589 __le16 control; /* always use "1" */
2590 __le16 table[HD_TABLE_SIZE]; /* use HD_* as index */ 2590 __le16 table[HD_TABLE_SIZE]; /* use HD_* as index */
2591} __attribute__ ((packed)); 2591} __attribute__ ((packed));
diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c
index 17f629fb96ff..be4cc5ffa742 100644
--- a/drivers/net/wireless/iwlwifi/iwl-4965.c
+++ b/drivers/net/wireless/iwlwifi/iwl-4965.c
@@ -43,6 +43,7 @@
43#include "iwl-core.h" 43#include "iwl-core.h"
44#include "iwl-io.h" 44#include "iwl-io.h"
45#include "iwl-helpers.h" 45#include "iwl-helpers.h"
46#include "iwl-calib.h"
46 47
47/* module parameters */ 48/* module parameters */
48static struct iwl_mod_params iwl4965_mod_params = { 49static struct iwl_mod_params iwl4965_mod_params = {
@@ -1032,418 +1033,15 @@ void iwl4965_rf_kill_ct_config(struct iwl_priv *priv)
1032 IWL_DEBUG_INFO("REPLY_CT_KILL_CONFIG_CMD succeeded\n"); 1033 IWL_DEBUG_INFO("REPLY_CT_KILL_CONFIG_CMD succeeded\n");
1033} 1034}
1034 1035
1035#ifdef CONFIG_IWL4965_SENSITIVITY 1036#ifdef CONFIG_IWL4965_RUN_TIME_CALIB
1036
1037/* "false alarms" are signals that our DSP tries to lock onto,
1038 * but then determines that they are either noise, or transmissions
1039 * from a distant wireless network (also "noise", really) that get
1040 * "stepped on" by stronger transmissions within our own network.
1041 * This algorithm attempts to set a sensitivity level that is high
1042 * enough to receive all of our own network traffic, but not so
1043 * high that our DSP gets too busy trying to lock onto non-network
1044 * activity/noise. */
1045static int iwl4965_sens_energy_cck(struct iwl_priv *priv,
1046 u32 norm_fa,
1047 u32 rx_enable_time,
1048 struct statistics_general_data *rx_info)
1049{
1050 u32 max_nrg_cck = 0;
1051 int i = 0;
1052 u8 max_silence_rssi = 0;
1053 u32 silence_ref = 0;
1054 u8 silence_rssi_a = 0;
1055 u8 silence_rssi_b = 0;
1056 u8 silence_rssi_c = 0;
1057 u32 val;
1058
1059 /* "false_alarms" values below are cross-multiplications to assess the
1060 * numbers of false alarms within the measured period of actual Rx
1061 * (Rx is off when we're txing), vs the min/max expected false alarms
1062 * (some should be expected if rx is sensitive enough) in a
1063 * hypothetical listening period of 200 time units (TU), 204.8 msec:
1064 *
1065 * MIN_FA/fixed-time < false_alarms/actual-rx-time < MAX_FA/beacon-time
1066 *
1067 * */
1068 u32 false_alarms = norm_fa * 200 * 1024;
1069 u32 max_false_alarms = MAX_FA_CCK * rx_enable_time;
1070 u32 min_false_alarms = MIN_FA_CCK * rx_enable_time;
1071 struct iwl4965_sensitivity_data *data = NULL;
1072
1073 data = &(priv->sensitivity_data);
1074
1075 data->nrg_auto_corr_silence_diff = 0;
1076
1077 /* Find max silence rssi among all 3 receivers.
1078 * This is background noise, which may include transmissions from other
1079 * networks, measured during silence before our network's beacon */
1080 silence_rssi_a = (u8)((rx_info->beacon_silence_rssi_a &
1081 ALL_BAND_FILTER) >> 8);
1082 silence_rssi_b = (u8)((rx_info->beacon_silence_rssi_b &
1083 ALL_BAND_FILTER) >> 8);
1084 silence_rssi_c = (u8)((rx_info->beacon_silence_rssi_c &
1085 ALL_BAND_FILTER) >> 8);
1086
1087 val = max(silence_rssi_b, silence_rssi_c);
1088 max_silence_rssi = max(silence_rssi_a, (u8) val);
1089
1090 /* Store silence rssi in 20-beacon history table */
1091 data->nrg_silence_rssi[data->nrg_silence_idx] = max_silence_rssi;
1092 data->nrg_silence_idx++;
1093 if (data->nrg_silence_idx >= NRG_NUM_PREV_STAT_L)
1094 data->nrg_silence_idx = 0;
1095
1096 /* Find max silence rssi across 20 beacon history */
1097 for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) {
1098 val = data->nrg_silence_rssi[i];
1099 silence_ref = max(silence_ref, val);
1100 }
1101 IWL_DEBUG_CALIB("silence a %u, b %u, c %u, 20-bcn max %u\n",
1102 silence_rssi_a, silence_rssi_b, silence_rssi_c,
1103 silence_ref);
1104
1105 /* Find max rx energy (min value!) among all 3 receivers,
1106 * measured during beacon frame.
1107 * Save it in 10-beacon history table. */
1108 i = data->nrg_energy_idx;
1109 val = min(rx_info->beacon_energy_b, rx_info->beacon_energy_c);
1110 data->nrg_value[i] = min(rx_info->beacon_energy_a, val);
1111
1112 data->nrg_energy_idx++;
1113 if (data->nrg_energy_idx >= 10)
1114 data->nrg_energy_idx = 0;
1115
1116 /* Find min rx energy (max value) across 10 beacon history.
1117 * This is the minimum signal level that we want to receive well.
1118 * Add backoff (margin so we don't miss slightly lower energy frames).
1119 * This establishes an upper bound (min value) for energy threshold. */
1120 max_nrg_cck = data->nrg_value[0];
1121 for (i = 1; i < 10; i++)
1122 max_nrg_cck = (u32) max(max_nrg_cck, (data->nrg_value[i]));
1123 max_nrg_cck += 6;
1124
1125 IWL_DEBUG_CALIB("rx energy a %u, b %u, c %u, 10-bcn max/min %u\n",
1126 rx_info->beacon_energy_a, rx_info->beacon_energy_b,
1127 rx_info->beacon_energy_c, max_nrg_cck - 6);
1128
1129 /* Count number of consecutive beacons with fewer-than-desired
1130 * false alarms. */
1131 if (false_alarms < min_false_alarms)
1132 data->num_in_cck_no_fa++;
1133 else
1134 data->num_in_cck_no_fa = 0;
1135 IWL_DEBUG_CALIB("consecutive bcns with few false alarms = %u\n",
1136 data->num_in_cck_no_fa);
1137
1138 /* If we got too many false alarms this time, reduce sensitivity */
1139 if (false_alarms > max_false_alarms) {
1140 IWL_DEBUG_CALIB("norm FA %u > max FA %u\n",
1141 false_alarms, max_false_alarms);
1142 IWL_DEBUG_CALIB("... reducing sensitivity\n");
1143 data->nrg_curr_state = IWL_FA_TOO_MANY;
1144
1145 if (data->auto_corr_cck > AUTO_CORR_MAX_TH_CCK) {
1146 /* Store for "fewer than desired" on later beacon */
1147 data->nrg_silence_ref = silence_ref;
1148
1149 /* increase energy threshold (reduce nrg value)
1150 * to decrease sensitivity */
1151 if (data->nrg_th_cck > (NRG_MAX_CCK + NRG_STEP_CCK))
1152 data->nrg_th_cck = data->nrg_th_cck
1153 - NRG_STEP_CCK;
1154 }
1155
1156 /* increase auto_corr values to decrease sensitivity */
1157 if (data->auto_corr_cck < AUTO_CORR_MAX_TH_CCK)
1158 data->auto_corr_cck = AUTO_CORR_MAX_TH_CCK + 1;
1159 else {
1160 val = data->auto_corr_cck + AUTO_CORR_STEP_CCK;
1161 data->auto_corr_cck = min((u32)AUTO_CORR_MAX_CCK, val);
1162 }
1163 val = data->auto_corr_cck_mrc + AUTO_CORR_STEP_CCK;
1164 data->auto_corr_cck_mrc = min((u32)AUTO_CORR_MAX_CCK_MRC, val);
1165
1166 /* Else if we got fewer than desired, increase sensitivity */
1167 } else if (false_alarms < min_false_alarms) {
1168 data->nrg_curr_state = IWL_FA_TOO_FEW;
1169
1170 /* Compare silence level with silence level for most recent
1171 * healthy number or too many false alarms */
1172 data->nrg_auto_corr_silence_diff = (s32)data->nrg_silence_ref -
1173 (s32)silence_ref;
1174
1175 IWL_DEBUG_CALIB("norm FA %u < min FA %u, silence diff %d\n",
1176 false_alarms, min_false_alarms,
1177 data->nrg_auto_corr_silence_diff);
1178
1179 /* Increase value to increase sensitivity, but only if:
1180 * 1a) previous beacon did *not* have *too many* false alarms
1181 * 1b) AND there's a significant difference in Rx levels
1182 * from a previous beacon with too many, or healthy # FAs
1183 * OR 2) We've seen a lot of beacons (100) with too few
1184 * false alarms */
1185 if ((data->nrg_prev_state != IWL_FA_TOO_MANY) &&
1186 ((data->nrg_auto_corr_silence_diff > NRG_DIFF) ||
1187 (data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) {
1188
1189 IWL_DEBUG_CALIB("... increasing sensitivity\n");
1190 /* Increase nrg value to increase sensitivity */
1191 val = data->nrg_th_cck + NRG_STEP_CCK;
1192 data->nrg_th_cck = min((u32)NRG_MIN_CCK, val);
1193
1194 /* Decrease auto_corr values to increase sensitivity */
1195 val = data->auto_corr_cck - AUTO_CORR_STEP_CCK;
1196 data->auto_corr_cck = max((u32)AUTO_CORR_MIN_CCK, val);
1197
1198 val = data->auto_corr_cck_mrc - AUTO_CORR_STEP_CCK;
1199 data->auto_corr_cck_mrc =
1200 max((u32)AUTO_CORR_MIN_CCK_MRC, val);
1201
1202 } else
1203 IWL_DEBUG_CALIB("... but not changing sensitivity\n");
1204
1205 /* Else we got a healthy number of false alarms, keep status quo */
1206 } else {
1207 IWL_DEBUG_CALIB(" FA in safe zone\n");
1208 data->nrg_curr_state = IWL_FA_GOOD_RANGE;
1209
1210 /* Store for use in "fewer than desired" with later beacon */
1211 data->nrg_silence_ref = silence_ref;
1212
1213 /* If previous beacon had too many false alarms,
1214 * give it some extra margin by reducing sensitivity again
1215 * (but don't go below measured energy of desired Rx) */
1216 if (IWL_FA_TOO_MANY == data->nrg_prev_state) {
1217 IWL_DEBUG_CALIB("... increasing margin\n");
1218 data->nrg_th_cck -= NRG_MARGIN;
1219 }
1220 }
1221
1222 /* Make sure the energy threshold does not go above the measured
1223 * energy of the desired Rx signals (reduced by backoff margin),
1224 * or else we might start missing Rx frames.
1225 * Lower value is higher energy, so we use max()!
1226 */
1227 data->nrg_th_cck = max(max_nrg_cck, data->nrg_th_cck);
1228 IWL_DEBUG_CALIB("new nrg_th_cck %u\n", data->nrg_th_cck);
1229
1230 data->nrg_prev_state = data->nrg_curr_state;
1231
1232 return 0;
1233}
1234
1235
1236static int iwl4965_sens_auto_corr_ofdm(struct iwl_priv *priv,
1237 u32 norm_fa,
1238 u32 rx_enable_time)
1239{
1240 u32 val;
1241 u32 false_alarms = norm_fa * 200 * 1024;
1242 u32 max_false_alarms = MAX_FA_OFDM * rx_enable_time;
1243 u32 min_false_alarms = MIN_FA_OFDM * rx_enable_time;
1244 struct iwl4965_sensitivity_data *data = NULL;
1245
1246 data = &(priv->sensitivity_data);
1247
1248 /* If we got too many false alarms this time, reduce sensitivity */
1249 if (false_alarms > max_false_alarms) {
1250
1251 IWL_DEBUG_CALIB("norm FA %u > max FA %u)\n",
1252 false_alarms, max_false_alarms);
1253
1254 val = data->auto_corr_ofdm + AUTO_CORR_STEP_OFDM;
1255 data->auto_corr_ofdm =
1256 min((u32)AUTO_CORR_MAX_OFDM, val);
1257
1258 val = data->auto_corr_ofdm_mrc + AUTO_CORR_STEP_OFDM;
1259 data->auto_corr_ofdm_mrc =
1260 min((u32)AUTO_CORR_MAX_OFDM_MRC, val);
1261
1262 val = data->auto_corr_ofdm_x1 + AUTO_CORR_STEP_OFDM;
1263 data->auto_corr_ofdm_x1 =
1264 min((u32)AUTO_CORR_MAX_OFDM_X1, val);
1265
1266 val = data->auto_corr_ofdm_mrc_x1 + AUTO_CORR_STEP_OFDM;
1267 data->auto_corr_ofdm_mrc_x1 =
1268 min((u32)AUTO_CORR_MAX_OFDM_MRC_X1, val);
1269 }
1270
1271 /* Else if we got fewer than desired, increase sensitivity */
1272 else if (false_alarms < min_false_alarms) {
1273
1274 IWL_DEBUG_CALIB("norm FA %u < min FA %u\n",
1275 false_alarms, min_false_alarms);
1276
1277 val = data->auto_corr_ofdm - AUTO_CORR_STEP_OFDM;
1278 data->auto_corr_ofdm =
1279 max((u32)AUTO_CORR_MIN_OFDM, val);
1280
1281 val = data->auto_corr_ofdm_mrc - AUTO_CORR_STEP_OFDM;
1282 data->auto_corr_ofdm_mrc =
1283 max((u32)AUTO_CORR_MIN_OFDM_MRC, val);
1284
1285 val = data->auto_corr_ofdm_x1 - AUTO_CORR_STEP_OFDM;
1286 data->auto_corr_ofdm_x1 =
1287 max((u32)AUTO_CORR_MIN_OFDM_X1, val);
1288
1289 val = data->auto_corr_ofdm_mrc_x1 - AUTO_CORR_STEP_OFDM;
1290 data->auto_corr_ofdm_mrc_x1 =
1291 max((u32)AUTO_CORR_MIN_OFDM_MRC_X1, val);
1292 }
1293
1294 else
1295 IWL_DEBUG_CALIB("min FA %u < norm FA %u < max FA %u OK\n",
1296 min_false_alarms, false_alarms, max_false_alarms);
1297
1298 return 0;
1299}
1300
1301static int iwl4965_sensitivity_callback(struct iwl_priv *priv,
1302 struct iwl_cmd *cmd, struct sk_buff *skb)
1303{
1304 /* We didn't cache the SKB; let the caller free it */
1305 return 1;
1306}
1307
1308/* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */
1309static int iwl4965_sensitivity_write(struct iwl_priv *priv, u8 flags)
1310{
1311 struct iwl4965_sensitivity_cmd cmd ;
1312 struct iwl4965_sensitivity_data *data = NULL;
1313 struct iwl_host_cmd cmd_out = {
1314 .id = SENSITIVITY_CMD,
1315 .len = sizeof(struct iwl4965_sensitivity_cmd),
1316 .meta.flags = flags,
1317 .data = &cmd,
1318 };
1319 int ret;
1320
1321 data = &(priv->sensitivity_data);
1322
1323 memset(&cmd, 0, sizeof(cmd));
1324
1325 cmd.table[HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX] =
1326 cpu_to_le16((u16)data->auto_corr_ofdm);
1327 cmd.table[HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX] =
1328 cpu_to_le16((u16)data->auto_corr_ofdm_mrc);
1329 cmd.table[HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX] =
1330 cpu_to_le16((u16)data->auto_corr_ofdm_x1);
1331 cmd.table[HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX] =
1332 cpu_to_le16((u16)data->auto_corr_ofdm_mrc_x1);
1333
1334 cmd.table[HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX] =
1335 cpu_to_le16((u16)data->auto_corr_cck);
1336 cmd.table[HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX] =
1337 cpu_to_le16((u16)data->auto_corr_cck_mrc);
1338
1339 cmd.table[HD_MIN_ENERGY_CCK_DET_INDEX] =
1340 cpu_to_le16((u16)data->nrg_th_cck);
1341 cmd.table[HD_MIN_ENERGY_OFDM_DET_INDEX] =
1342 cpu_to_le16((u16)data->nrg_th_ofdm);
1343
1344 cmd.table[HD_BARKER_CORR_TH_ADD_MIN_INDEX] =
1345 __constant_cpu_to_le16(190);
1346 cmd.table[HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX] =
1347 __constant_cpu_to_le16(390);
1348 cmd.table[HD_OFDM_ENERGY_TH_IN_INDEX] =
1349 __constant_cpu_to_le16(62);
1350
1351 IWL_DEBUG_CALIB("ofdm: ac %u mrc %u x1 %u mrc_x1 %u thresh %u\n",
1352 data->auto_corr_ofdm, data->auto_corr_ofdm_mrc,
1353 data->auto_corr_ofdm_x1, data->auto_corr_ofdm_mrc_x1,
1354 data->nrg_th_ofdm);
1355
1356 IWL_DEBUG_CALIB("cck: ac %u mrc %u thresh %u\n",
1357 data->auto_corr_cck, data->auto_corr_cck_mrc,
1358 data->nrg_th_cck);
1359
1360 /* Update uCode's "work" table, and copy it to DSP */
1361 cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE;
1362
1363 if (flags & CMD_ASYNC)
1364 cmd_out.meta.u.callback = iwl4965_sensitivity_callback;
1365
1366 /* Don't send command to uCode if nothing has changed */
1367 if (!memcmp(&cmd.table[0], &(priv->sensitivity_tbl[0]),
1368 sizeof(u16)*HD_TABLE_SIZE)) {
1369 IWL_DEBUG_CALIB("No change in SENSITIVITY_CMD\n");
1370 return 0;
1371 }
1372
1373 /* Copy table for comparison next time */
1374 memcpy(&(priv->sensitivity_tbl[0]), &(cmd.table[0]),
1375 sizeof(u16)*HD_TABLE_SIZE);
1376
1377 ret = iwl_send_cmd(priv, &cmd_out);
1378 if (ret)
1379 IWL_ERROR("SENSITIVITY_CMD failed\n");
1380
1381 return ret;
1382}
1383
1384void iwl4965_init_sensitivity(struct iwl_priv *priv, u8 flags, u8 force)
1385{
1386 struct iwl4965_sensitivity_data *data = NULL;
1387 int i;
1388 int ret = 0;
1389
1390 IWL_DEBUG_CALIB("Start iwl4965_init_sensitivity\n");
1391
1392 if (force)
1393 memset(&(priv->sensitivity_tbl[0]), 0,
1394 sizeof(u16)*HD_TABLE_SIZE);
1395
1396 /* Clear driver's sensitivity algo data */
1397 data = &(priv->sensitivity_data);
1398 memset(data, 0, sizeof(struct iwl4965_sensitivity_data));
1399
1400 data->num_in_cck_no_fa = 0;
1401 data->nrg_curr_state = IWL_FA_TOO_MANY;
1402 data->nrg_prev_state = IWL_FA_TOO_MANY;
1403 data->nrg_silence_ref = 0;
1404 data->nrg_silence_idx = 0;
1405 data->nrg_energy_idx = 0;
1406
1407 for (i = 0; i < 10; i++)
1408 data->nrg_value[i] = 0;
1409
1410 for (i = 0; i < NRG_NUM_PREV_STAT_L; i++)
1411 data->nrg_silence_rssi[i] = 0;
1412
1413 data->auto_corr_ofdm = 90;
1414 data->auto_corr_ofdm_mrc = 170;
1415 data->auto_corr_ofdm_x1 = 105;
1416 data->auto_corr_ofdm_mrc_x1 = 220;
1417 data->auto_corr_cck = AUTO_CORR_CCK_MIN_VAL_DEF;
1418 data->auto_corr_cck_mrc = 200;
1419 data->nrg_th_cck = 100;
1420 data->nrg_th_ofdm = 100;
1421
1422 data->last_bad_plcp_cnt_ofdm = 0;
1423 data->last_fa_cnt_ofdm = 0;
1424 data->last_bad_plcp_cnt_cck = 0;
1425 data->last_fa_cnt_cck = 0;
1426
1427 /* Clear prior Sensitivity command data to force send to uCode */
1428 if (force)
1429 memset(&(priv->sensitivity_tbl[0]), 0,
1430 sizeof(u16)*HD_TABLE_SIZE);
1431
1432 ret |= iwl4965_sensitivity_write(priv, flags);
1433 IWL_DEBUG_CALIB("<<return 0x%X\n", ret);
1434
1435 return;
1436}
1437
1438 1037
1439/* Reset differential Rx gains in NIC to prepare for chain noise calibration. 1038/* Reset differential Rx gains in NIC to prepare for chain noise calibration.
1440 * Called after every association, but this runs only once! 1039 * Called after every association, but this runs only once!
1441 * ... once chain noise is calibrated the first time, it's good forever. */ 1040 * ... once chain noise is calibrated the first time, it's good forever. */
1442void iwl4965_chain_noise_reset(struct iwl_priv *priv) 1041static void iwl4965_chain_noise_reset(struct iwl_priv *priv)
1443{ 1042{
1444 struct iwl4965_chain_noise_data *data = NULL; 1043 struct iwl_chain_noise_data *data = &(priv->chain_noise_data);
1445 1044
1446 data = &(priv->chain_noise_data);
1447 if ((data->state == IWL_CHAIN_NOISE_ALIVE) && iwl_is_associated(priv)) { 1045 if ((data->state == IWL_CHAIN_NOISE_ALIVE) && iwl_is_associated(priv)) {
1448 struct iwl4965_calibration_cmd cmd; 1046 struct iwl4965_calibration_cmd cmd;
1449 1047
@@ -1452,357 +1050,76 @@ void iwl4965_chain_noise_reset(struct iwl_priv *priv)
1452 cmd.diff_gain_a = 0; 1050 cmd.diff_gain_a = 0;
1453 cmd.diff_gain_b = 0; 1051 cmd.diff_gain_b = 0;
1454 cmd.diff_gain_c = 0; 1052 cmd.diff_gain_c = 0;
1455 iwl_send_cmd_pdu_async(priv, REPLY_PHY_CALIBRATION_CMD, 1053 if (iwl_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD,
1456 sizeof(cmd), &cmd, NULL); 1054 sizeof(cmd), &cmd))
1457 msleep(4); 1055 IWL_ERROR("Could not send REPLY_PHY_CALIBRATION_CMD\n");
1458 data->state = IWL_CHAIN_NOISE_ACCUMULATE; 1056 data->state = IWL_CHAIN_NOISE_ACCUMULATE;
1459 IWL_DEBUG_CALIB("Run chain_noise_calibrate\n"); 1057 IWL_DEBUG_CALIB("Run chain_noise_calibrate\n");
1460 } 1058 }
1461 return;
1462} 1059}
1463 1060
1464/* 1061static void iwl4965_gain_computation(struct iwl_priv *priv,
1465 * Accumulate 20 beacons of signal and noise statistics for each of 1062 u32 *average_noise,
1466 * 3 receivers/antennas/rx-chains, then figure out: 1063 u16 min_average_noise_antenna_i,
1467 * 1) Which antennas are connected. 1064 u32 min_average_noise)
1468 * 2) Differential rx gain settings to balance the 3 receivers.
1469 */
1470static void iwl4965_noise_calibration(struct iwl_priv *priv,
1471 struct iwl4965_notif_statistics *stat_resp)
1472{ 1065{
1473 struct iwl4965_chain_noise_data *data = NULL; 1066 int i, ret;
1474 int ret = 0; 1067 struct iwl_chain_noise_data *data = &priv->chain_noise_data;
1475
1476 u32 chain_noise_a;
1477 u32 chain_noise_b;
1478 u32 chain_noise_c;
1479 u32 chain_sig_a;
1480 u32 chain_sig_b;
1481 u32 chain_sig_c;
1482 u32 average_sig[NUM_RX_CHAINS] = {INITIALIZATION_VALUE};
1483 u32 average_noise[NUM_RX_CHAINS] = {INITIALIZATION_VALUE};
1484 u32 max_average_sig;
1485 u16 max_average_sig_antenna_i;
1486 u32 min_average_noise = MIN_AVERAGE_NOISE_MAX_VALUE;
1487 u16 min_average_noise_antenna_i = INITIALIZATION_VALUE;
1488 u16 i = 0;
1489 u16 chan_num = INITIALIZATION_VALUE;
1490 u32 band = INITIALIZATION_VALUE;
1491 u32 active_chains = 0;
1492 unsigned long flags;
1493 struct statistics_rx_non_phy *rx_info = &(stat_resp->rx.general);
1494
1495 data = &(priv->chain_noise_data);
1496 1068
1497 /* Accumulate just the first 20 beacons after the first association, 1069 data->delta_gain_code[min_average_noise_antenna_i] = 0;
1498 * then we're done forever. */
1499 if (data->state != IWL_CHAIN_NOISE_ACCUMULATE) {
1500 if (data->state == IWL_CHAIN_NOISE_ALIVE)
1501 IWL_DEBUG_CALIB("Wait for noise calib reset\n");
1502 return;
1503 }
1504
1505 spin_lock_irqsave(&priv->lock, flags);
1506 if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) {
1507 IWL_DEBUG_CALIB(" << Interference data unavailable\n");
1508 spin_unlock_irqrestore(&priv->lock, flags);
1509 return;
1510 }
1511
1512 band = (priv->staging_rxon.flags & RXON_FLG_BAND_24G_MSK) ? 0 : 1;
1513 chan_num = le16_to_cpu(priv->staging_rxon.channel);
1514
1515 /* Make sure we accumulate data for just the associated channel
1516 * (even if scanning). */
1517 if ((chan_num != (le32_to_cpu(stat_resp->flag) >> 16)) ||
1518 ((STATISTICS_REPLY_FLG_BAND_24G_MSK ==
1519 (stat_resp->flag & STATISTICS_REPLY_FLG_BAND_24G_MSK)) && band)) {
1520 IWL_DEBUG_CALIB("Stats not from chan=%d, band=%d\n",
1521 chan_num, band);
1522 spin_unlock_irqrestore(&priv->lock, flags);
1523 return;
1524 }
1525
1526 /* Accumulate beacon statistics values across 20 beacons */
1527 chain_noise_a = le32_to_cpu(rx_info->beacon_silence_rssi_a) &
1528 IN_BAND_FILTER;
1529 chain_noise_b = le32_to_cpu(rx_info->beacon_silence_rssi_b) &
1530 IN_BAND_FILTER;
1531 chain_noise_c = le32_to_cpu(rx_info->beacon_silence_rssi_c) &
1532 IN_BAND_FILTER;
1533
1534 chain_sig_a = le32_to_cpu(rx_info->beacon_rssi_a) & IN_BAND_FILTER;
1535 chain_sig_b = le32_to_cpu(rx_info->beacon_rssi_b) & IN_BAND_FILTER;
1536 chain_sig_c = le32_to_cpu(rx_info->beacon_rssi_c) & IN_BAND_FILTER;
1537 1070
1538 spin_unlock_irqrestore(&priv->lock, flags); 1071 for (i = 0; i < NUM_RX_CHAINS; i++) {
1072 s32 delta_g = 0;
1539 1073
1540 data->beacon_count++; 1074 if (!(data->disconn_array[i]) &&
1541 1075 (data->delta_gain_code[i] ==
1542 data->chain_noise_a = (chain_noise_a + data->chain_noise_a);
1543 data->chain_noise_b = (chain_noise_b + data->chain_noise_b);
1544 data->chain_noise_c = (chain_noise_c + data->chain_noise_c);
1545
1546 data->chain_signal_a = (chain_sig_a + data->chain_signal_a);
1547 data->chain_signal_b = (chain_sig_b + data->chain_signal_b);
1548 data->chain_signal_c = (chain_sig_c + data->chain_signal_c);
1549
1550 IWL_DEBUG_CALIB("chan=%d, band=%d, beacon=%d\n", chan_num, band,
1551 data->beacon_count);
1552 IWL_DEBUG_CALIB("chain_sig: a %d b %d c %d\n",
1553 chain_sig_a, chain_sig_b, chain_sig_c);
1554 IWL_DEBUG_CALIB("chain_noise: a %d b %d c %d\n",
1555 chain_noise_a, chain_noise_b, chain_noise_c);
1556
1557 /* If this is the 20th beacon, determine:
1558 * 1) Disconnected antennas (using signal strengths)
1559 * 2) Differential gain (using silence noise) to balance receivers */
1560 if (data->beacon_count == CAL_NUM_OF_BEACONS) {
1561
1562 /* Analyze signal for disconnected antenna */
1563 average_sig[0] = (data->chain_signal_a) / CAL_NUM_OF_BEACONS;
1564 average_sig[1] = (data->chain_signal_b) / CAL_NUM_OF_BEACONS;
1565 average_sig[2] = (data->chain_signal_c) / CAL_NUM_OF_BEACONS;
1566
1567 if (average_sig[0] >= average_sig[1]) {
1568 max_average_sig = average_sig[0];
1569 max_average_sig_antenna_i = 0;
1570 active_chains = (1 << max_average_sig_antenna_i);
1571 } else {
1572 max_average_sig = average_sig[1];
1573 max_average_sig_antenna_i = 1;
1574 active_chains = (1 << max_average_sig_antenna_i);
1575 }
1576
1577 if (average_sig[2] >= max_average_sig) {
1578 max_average_sig = average_sig[2];
1579 max_average_sig_antenna_i = 2;
1580 active_chains = (1 << max_average_sig_antenna_i);
1581 }
1582
1583 IWL_DEBUG_CALIB("average_sig: a %d b %d c %d\n",
1584 average_sig[0], average_sig[1], average_sig[2]);
1585 IWL_DEBUG_CALIB("max_average_sig = %d, antenna %d\n",
1586 max_average_sig, max_average_sig_antenna_i);
1587
1588 /* Compare signal strengths for all 3 receivers. */
1589 for (i = 0; i < NUM_RX_CHAINS; i++) {
1590 if (i != max_average_sig_antenna_i) {
1591 s32 rssi_delta = (max_average_sig -
1592 average_sig[i]);
1593
1594 /* If signal is very weak, compared with
1595 * strongest, mark it as disconnected. */
1596 if (rssi_delta > MAXIMUM_ALLOWED_PATHLOSS)
1597 data->disconn_array[i] = 1;
1598 else
1599 active_chains |= (1 << i);
1600 IWL_DEBUG_CALIB("i = %d rssiDelta = %d "
1601 "disconn_array[i] = %d\n",
1602 i, rssi_delta, data->disconn_array[i]);
1603 }
1604 }
1605
1606 /*If both chains A & B are disconnected -
1607 * connect B and leave A as is */
1608 if (data->disconn_array[CHAIN_A] &&
1609 data->disconn_array[CHAIN_B]) {
1610 data->disconn_array[CHAIN_B] = 0;
1611 active_chains |= (1 << CHAIN_B);
1612 IWL_DEBUG_CALIB("both A & B chains are disconnected! "
1613 "W/A - declare B as connected\n");
1614 }
1615
1616 IWL_DEBUG_CALIB("active_chains (bitwise) = 0x%x\n",
1617 active_chains);
1618
1619 /* Save for use within RXON, TX, SCAN commands, etc. */
1620 priv->valid_antenna = active_chains;
1621
1622 /* Analyze noise for rx balance */
1623 average_noise[0] = ((data->chain_noise_a)/CAL_NUM_OF_BEACONS);
1624 average_noise[1] = ((data->chain_noise_b)/CAL_NUM_OF_BEACONS);
1625 average_noise[2] = ((data->chain_noise_c)/CAL_NUM_OF_BEACONS);
1626
1627 for (i = 0; i < NUM_RX_CHAINS; i++) {
1628 if (!(data->disconn_array[i]) &&
1629 (average_noise[i] <= min_average_noise)) {
1630 /* This means that chain i is active and has
1631 * lower noise values so far: */
1632 min_average_noise = average_noise[i];
1633 min_average_noise_antenna_i = i;
1634 }
1635 }
1636
1637 data->delta_gain_code[min_average_noise_antenna_i] = 0;
1638
1639 IWL_DEBUG_CALIB("average_noise: a %d b %d c %d\n",
1640 average_noise[0], average_noise[1],
1641 average_noise[2]);
1642
1643 IWL_DEBUG_CALIB("min_average_noise = %d, antenna %d\n",
1644 min_average_noise, min_average_noise_antenna_i);
1645
1646 for (i = 0; i < NUM_RX_CHAINS; i++) {
1647 s32 delta_g = 0;
1648
1649 if (!(data->disconn_array[i]) &&
1650 (data->delta_gain_code[i] ==
1651 CHAIN_NOISE_DELTA_GAIN_INIT_VAL)) { 1076 CHAIN_NOISE_DELTA_GAIN_INIT_VAL)) {
1652 delta_g = average_noise[i] - min_average_noise; 1077 delta_g = average_noise[i] - min_average_noise;
1653 data->delta_gain_code[i] = (u8)((delta_g * 1078 data->delta_gain_code[i] = (u8)((delta_g * 10) / 15);
1654 10) / 15); 1079 data->delta_gain_code[i] =
1655 if (CHAIN_NOISE_MAX_DELTA_GAIN_CODE < 1080 min(data->delta_gain_code[i],
1656 data->delta_gain_code[i]) 1081 (u8) CHAIN_NOISE_MAX_DELTA_GAIN_CODE);
1657 data->delta_gain_code[i] = 1082
1658 CHAIN_NOISE_MAX_DELTA_GAIN_CODE; 1083 data->delta_gain_code[i] =
1659 1084 (data->delta_gain_code[i] | (1 << 2));
1660 data->delta_gain_code[i] = 1085 } else {
1661 (data->delta_gain_code[i] | (1 << 2)); 1086 data->delta_gain_code[i] = 0;
1662 } else
1663 data->delta_gain_code[i] = 0;
1664 }
1665 IWL_DEBUG_CALIB("delta_gain_codes: a %d b %d c %d\n",
1666 data->delta_gain_code[0],
1667 data->delta_gain_code[1],
1668 data->delta_gain_code[2]);
1669
1670 /* Differential gain gets sent to uCode only once */
1671 if (!data->radio_write) {
1672 struct iwl4965_calibration_cmd cmd;
1673 data->radio_write = 1;
1674
1675 memset(&cmd, 0, sizeof(cmd));
1676 cmd.opCode = PHY_CALIBRATE_DIFF_GAIN_CMD;
1677 cmd.diff_gain_a = data->delta_gain_code[0];
1678 cmd.diff_gain_b = data->delta_gain_code[1];
1679 cmd.diff_gain_c = data->delta_gain_code[2];
1680 ret = iwl_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD,
1681 sizeof(cmd), &cmd);
1682 if (ret)
1683 IWL_DEBUG_CALIB("fail sending cmd "
1684 "REPLY_PHY_CALIBRATION_CMD \n");
1685
1686 /* TODO we might want recalculate
1687 * rx_chain in rxon cmd */
1688
1689 /* Mark so we run this algo only once! */
1690 data->state = IWL_CHAIN_NOISE_CALIBRATED;
1691 } 1087 }
1692 data->chain_noise_a = 0;
1693 data->chain_noise_b = 0;
1694 data->chain_noise_c = 0;
1695 data->chain_signal_a = 0;
1696 data->chain_signal_b = 0;
1697 data->chain_signal_c = 0;
1698 data->beacon_count = 0;
1699 }
1700 return;
1701}
1702
1703static void iwl4965_sensitivity_calibration(struct iwl_priv *priv,
1704 struct iwl4965_notif_statistics *resp)
1705{
1706 u32 rx_enable_time;
1707 u32 fa_cck;
1708 u32 fa_ofdm;
1709 u32 bad_plcp_cck;
1710 u32 bad_plcp_ofdm;
1711 u32 norm_fa_ofdm;
1712 u32 norm_fa_cck;
1713 struct iwl4965_sensitivity_data *data = NULL;
1714 struct statistics_rx_non_phy *rx_info = &(resp->rx.general);
1715 struct statistics_rx *statistics = &(resp->rx);
1716 unsigned long flags;
1717 struct statistics_general_data statis;
1718 int ret;
1719
1720 data = &(priv->sensitivity_data);
1721
1722 if (!iwl_is_associated(priv)) {
1723 IWL_DEBUG_CALIB("<< - not associated\n");
1724 return;
1725 }
1726
1727 spin_lock_irqsave(&priv->lock, flags);
1728 if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) {
1729 IWL_DEBUG_CALIB("<< invalid data.\n");
1730 spin_unlock_irqrestore(&priv->lock, flags);
1731 return;
1732 }
1733
1734 /* Extract Statistics: */
1735 rx_enable_time = le32_to_cpu(rx_info->channel_load);
1736 fa_cck = le32_to_cpu(statistics->cck.false_alarm_cnt);
1737 fa_ofdm = le32_to_cpu(statistics->ofdm.false_alarm_cnt);
1738 bad_plcp_cck = le32_to_cpu(statistics->cck.plcp_err);
1739 bad_plcp_ofdm = le32_to_cpu(statistics->ofdm.plcp_err);
1740
1741 statis.beacon_silence_rssi_a =
1742 le32_to_cpu(statistics->general.beacon_silence_rssi_a);
1743 statis.beacon_silence_rssi_b =
1744 le32_to_cpu(statistics->general.beacon_silence_rssi_b);
1745 statis.beacon_silence_rssi_c =
1746 le32_to_cpu(statistics->general.beacon_silence_rssi_c);
1747 statis.beacon_energy_a =
1748 le32_to_cpu(statistics->general.beacon_energy_a);
1749 statis.beacon_energy_b =
1750 le32_to_cpu(statistics->general.beacon_energy_b);
1751 statis.beacon_energy_c =
1752 le32_to_cpu(statistics->general.beacon_energy_c);
1753
1754 spin_unlock_irqrestore(&priv->lock, flags);
1755
1756 IWL_DEBUG_CALIB("rx_enable_time = %u usecs\n", rx_enable_time);
1757
1758 if (!rx_enable_time) {
1759 IWL_DEBUG_CALIB("<< RX Enable Time == 0! \n");
1760 return;
1761 }
1762
1763 /* These statistics increase monotonically, and do not reset
1764 * at each beacon. Calculate difference from last value, or just
1765 * use the new statistics value if it has reset or wrapped around. */
1766 if (data->last_bad_plcp_cnt_cck > bad_plcp_cck)
1767 data->last_bad_plcp_cnt_cck = bad_plcp_cck;
1768 else {
1769 bad_plcp_cck -= data->last_bad_plcp_cnt_cck;
1770 data->last_bad_plcp_cnt_cck += bad_plcp_cck;
1771 }
1772
1773 if (data->last_bad_plcp_cnt_ofdm > bad_plcp_ofdm)
1774 data->last_bad_plcp_cnt_ofdm = bad_plcp_ofdm;
1775 else {
1776 bad_plcp_ofdm -= data->last_bad_plcp_cnt_ofdm;
1777 data->last_bad_plcp_cnt_ofdm += bad_plcp_ofdm;
1778 } 1088 }
1089 IWL_DEBUG_CALIB("delta_gain_codes: a %d b %d c %d\n",
1090 data->delta_gain_code[0],
1091 data->delta_gain_code[1],
1092 data->delta_gain_code[2]);
1779 1093
1780 if (data->last_fa_cnt_ofdm > fa_ofdm) 1094 /* Differential gain gets sent to uCode only once */
1781 data->last_fa_cnt_ofdm = fa_ofdm; 1095 if (!data->radio_write) {
1782 else { 1096 struct iwl4965_calibration_cmd cmd;
1783 fa_ofdm -= data->last_fa_cnt_ofdm; 1097 data->radio_write = 1;
1784 data->last_fa_cnt_ofdm += fa_ofdm;
1785 }
1786 1098
1787 if (data->last_fa_cnt_cck > fa_cck) 1099 memset(&cmd, 0, sizeof(cmd));
1788 data->last_fa_cnt_cck = fa_cck; 1100 cmd.opCode = PHY_CALIBRATE_DIFF_GAIN_CMD;
1789 else { 1101 cmd.diff_gain_a = data->delta_gain_code[0];
1790 fa_cck -= data->last_fa_cnt_cck; 1102 cmd.diff_gain_b = data->delta_gain_code[1];
1791 data->last_fa_cnt_cck += fa_cck; 1103 cmd.diff_gain_c = data->delta_gain_code[2];
1104 ret = iwl_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD,
1105 sizeof(cmd), &cmd);
1106 if (ret)
1107 IWL_DEBUG_CALIB("fail sending cmd "
1108 "REPLY_PHY_CALIBRATION_CMD \n");
1109
1110 /* TODO we might want recalculate
1111 * rx_chain in rxon cmd */
1112
1113 /* Mark so we run this algo only once! */
1114 data->state = IWL_CHAIN_NOISE_CALIBRATED;
1792 } 1115 }
1793 1116 data->chain_noise_a = 0;
1794 /* Total aborted signal locks */ 1117 data->chain_noise_b = 0;
1795 norm_fa_ofdm = fa_ofdm + bad_plcp_ofdm; 1118 data->chain_noise_c = 0;
1796 norm_fa_cck = fa_cck + bad_plcp_cck; 1119 data->chain_signal_a = 0;
1797 1120 data->chain_signal_b = 0;
1798 IWL_DEBUG_CALIB("cck: fa %u badp %u ofdm: fa %u badp %u\n", fa_cck, 1121 data->chain_signal_c = 0;
1799 bad_plcp_cck, fa_ofdm, bad_plcp_ofdm); 1122 data->beacon_count = 0;
1800
1801 iwl4965_sens_auto_corr_ofdm(priv, norm_fa_ofdm, rx_enable_time);
1802 iwl4965_sens_energy_cck(priv, norm_fa_cck, rx_enable_time, &statis);
1803 ret = iwl4965_sensitivity_write(priv, CMD_ASYNC);
1804
1805 return;
1806} 1123}
1807 1124
1808static void iwl4965_bg_sensitivity_work(struct work_struct *work) 1125static void iwl4965_bg_sensitivity_work(struct work_struct *work)
@@ -1819,21 +1136,15 @@ static void iwl4965_bg_sensitivity_work(struct work_struct *work)
1819 } 1136 }
1820 1137
1821 if (priv->start_calib) { 1138 if (priv->start_calib) {
1822 iwl4965_noise_calibration(priv, &priv->statistics); 1139 iwl_chain_noise_calibration(priv, &priv->statistics);
1823 1140
1824 if (priv->sensitivity_data.state == 1141 iwl_sensitivity_calibration(priv, &priv->statistics);
1825 IWL_SENS_CALIB_NEED_REINIT) {
1826 iwl4965_init_sensitivity(priv, CMD_ASYNC, 0);
1827 priv->sensitivity_data.state = IWL_SENS_CALIB_ALLOWED;
1828 } else
1829 iwl4965_sensitivity_calibration(priv,
1830 &priv->statistics);
1831 } 1142 }
1832 1143
1833 mutex_unlock(&priv->mutex); 1144 mutex_unlock(&priv->mutex);
1834 return; 1145 return;
1835} 1146}
1836#endif /*CONFIG_IWL4965_SENSITIVITY*/ 1147#endif /*CONFIG_IWL4965_RUN_TIME_CALIB*/
1837 1148
1838static void iwl4965_bg_txpower_work(struct work_struct *work) 1149static void iwl4965_bg_txpower_work(struct work_struct *work)
1839{ 1150{
@@ -1932,15 +1243,15 @@ int iwl4965_alive_notify(struct iwl_priv *priv)
1932 1243
1933 spin_lock_irqsave(&priv->lock, flags); 1244 spin_lock_irqsave(&priv->lock, flags);
1934 1245
1935#ifdef CONFIG_IWL4965_SENSITIVITY 1246#ifdef CONFIG_IWL4965_RUN_TIME_CALIB
1936 memset(&(priv->sensitivity_data), 0, 1247 memset(&(priv->sensitivity_data), 0,
1937 sizeof(struct iwl4965_sensitivity_data)); 1248 sizeof(struct iwl_sensitivity_data));
1938 memset(&(priv->chain_noise_data), 0, 1249 memset(&(priv->chain_noise_data), 0,
1939 sizeof(struct iwl4965_chain_noise_data)); 1250 sizeof(struct iwl_chain_noise_data));
1940 for (i = 0; i < NUM_RX_CHAINS; i++) 1251 for (i = 0; i < NUM_RX_CHAINS; i++)
1941 priv->chain_noise_data.delta_gain_code[i] = 1252 priv->chain_noise_data.delta_gain_code[i] =
1942 CHAIN_NOISE_DELTA_GAIN_INIT_VAL; 1253 CHAIN_NOISE_DELTA_GAIN_INIT_VAL;
1943#endif /* CONFIG_IWL4965_SENSITIVITY*/ 1254#endif /* CONFIG_IWL4965_RUN_TIME_CALIB*/
1944 ret = iwl_grab_nic_access(priv); 1255 ret = iwl_grab_nic_access(priv);
1945 if (ret) { 1256 if (ret) {
1946 spin_unlock_irqrestore(&priv->lock, flags); 1257 spin_unlock_irqrestore(&priv->lock, flags);
@@ -2013,6 +1324,31 @@ int iwl4965_alive_notify(struct iwl_priv *priv)
2013 return ret; 1324 return ret;
2014} 1325}
2015 1326
1327#ifdef CONFIG_IWL4965_RUN_TIME_CALIB
1328static struct iwl_sensitivity_ranges iwl4965_sensitivity = {
1329 .min_nrg_cck = 97,
1330 .max_nrg_cck = 0,
1331
1332 .auto_corr_min_ofdm = 85,
1333 .auto_corr_min_ofdm_mrc = 170,
1334 .auto_corr_min_ofdm_x1 = 105,
1335 .auto_corr_min_ofdm_mrc_x1 = 220,
1336
1337 .auto_corr_max_ofdm = 120,
1338 .auto_corr_max_ofdm_mrc = 210,
1339 .auto_corr_max_ofdm_x1 = 140,
1340 .auto_corr_max_ofdm_mrc_x1 = 270,
1341
1342 .auto_corr_min_cck = 125,
1343 .auto_corr_max_cck = 200,
1344 .auto_corr_min_cck_mrc = 200,
1345 .auto_corr_max_cck_mrc = 400,
1346
1347 .nrg_th_cck = 100,
1348 .nrg_th_ofdm = 100,
1349};
1350#endif
1351
2016/** 1352/**
2017 * iwl4965_hw_set_hw_params 1353 * iwl4965_hw_set_hw_params
2018 * 1354 *
@@ -2044,6 +1380,9 @@ int iwl4965_hw_set_hw_params(struct iwl_priv *priv)
2044 priv->hw_params.rx_chains_num = 2; 1380 priv->hw_params.rx_chains_num = 2;
2045 priv->hw_params.valid_tx_ant = (IWL_ANTENNA_MAIN | IWL_ANTENNA_AUX); 1381 priv->hw_params.valid_tx_ant = (IWL_ANTENNA_MAIN | IWL_ANTENNA_AUX);
2046 priv->hw_params.valid_rx_ant = (IWL_ANTENNA_MAIN | IWL_ANTENNA_AUX); 1382 priv->hw_params.valid_rx_ant = (IWL_ANTENNA_MAIN | IWL_ANTENNA_AUX);
1383#ifdef CONFIG_IWL4965_RUN_TIME_CALIB
1384 priv->hw_params.sens = &iwl4965_sensitivity;
1385#endif
2047 1386
2048 return 0; 1387 return 0;
2049} 1388}
@@ -3202,7 +2541,7 @@ void iwl4965_set_rxon_chain(struct iwl_priv *priv)
3202 2541
3203 /* Tell uCode which antennas are actually connected. 2542 /* Tell uCode which antennas are actually connected.
3204 * Before first association, we assume all antennas are connected. 2543 * Before first association, we assume all antennas are connected.
3205 * Just after first association, iwl4965_noise_calibration() 2544 * Just after first association, iwl_chain_noise_calibration()
3206 * checks which antennas actually *are* connected. */ 2545 * checks which antennas actually *are* connected. */
3207 priv->staging_rxon.rx_chain |= 2546 priv->staging_rxon.rx_chain |=
3208 cpu_to_le16(priv->valid_antenna << RXON_RX_CHAIN_VALID_POS); 2547 cpu_to_le16(priv->valid_antenna << RXON_RX_CHAIN_VALID_POS);
@@ -3412,7 +2751,7 @@ void iwl4965_hw_rx_statistics(struct iwl_priv *priv, struct iwl4965_rx_mem_buffe
3412 if (unlikely(!test_bit(STATUS_SCANNING, &priv->status)) && 2751 if (unlikely(!test_bit(STATUS_SCANNING, &priv->status)) &&
3413 (pkt->hdr.cmd == STATISTICS_NOTIFICATION)) { 2752 (pkt->hdr.cmd == STATISTICS_NOTIFICATION)) {
3414 iwl4965_rx_calc_noise(priv); 2753 iwl4965_rx_calc_noise(priv);
3415#ifdef CONFIG_IWL4965_SENSITIVITY 2754#ifdef CONFIG_IWL4965_RUN_TIME_CALIB
3416 queue_work(priv->workqueue, &priv->sensitivity_work); 2755 queue_work(priv->workqueue, &priv->sensitivity_work);
3417#endif 2756#endif
3418 } 2757 }
@@ -4139,7 +3478,7 @@ static void iwl4965_rx_missed_beacon_notif(struct iwl_priv *priv,
4139 struct iwl4965_rx_mem_buffer *rxb) 3478 struct iwl4965_rx_mem_buffer *rxb)
4140 3479
4141{ 3480{
4142#ifdef CONFIG_IWL4965_SENSITIVITY 3481#ifdef CONFIG_IWL4965_RUN_TIME_CALIB
4143 struct iwl4965_rx_packet *pkt = (void *)rxb->skb->data; 3482 struct iwl4965_rx_packet *pkt = (void *)rxb->skb->data;
4144 struct iwl4965_missed_beacon_notif *missed_beacon; 3483 struct iwl4965_missed_beacon_notif *missed_beacon;
4145 3484
@@ -4150,11 +3489,10 @@ static void iwl4965_rx_missed_beacon_notif(struct iwl_priv *priv,
4150 le32_to_cpu(missed_beacon->total_missed_becons), 3489 le32_to_cpu(missed_beacon->total_missed_becons),
4151 le32_to_cpu(missed_beacon->num_recvd_beacons), 3490 le32_to_cpu(missed_beacon->num_recvd_beacons),
4152 le32_to_cpu(missed_beacon->num_expected_beacons)); 3491 le32_to_cpu(missed_beacon->num_expected_beacons));
4153 priv->sensitivity_data.state = IWL_SENS_CALIB_NEED_REINIT; 3492 if (!test_bit(STATUS_SCANNING, &priv->status))
4154 if (unlikely(!test_bit(STATUS_SCANNING, &priv->status))) 3493 iwl_init_sensitivity(priv);
4155 queue_work(priv->workqueue, &priv->sensitivity_work);
4156 } 3494 }
4157#endif /*CONFIG_IWL4965_SENSITIVITY*/ 3495#endif /*CONFIG_IWL4965_RUN_TIME_CALIB*/
4158} 3496}
4159#ifdef CONFIG_IWL4965_HT 3497#ifdef CONFIG_IWL4965_HT
4160 3498
@@ -4930,7 +4268,7 @@ void iwl4965_hw_rx_handler_setup(struct iwl_priv *priv)
4930void iwl4965_hw_setup_deferred_work(struct iwl_priv *priv) 4268void iwl4965_hw_setup_deferred_work(struct iwl_priv *priv)
4931{ 4269{
4932 INIT_WORK(&priv->txpower_work, iwl4965_bg_txpower_work); 4270 INIT_WORK(&priv->txpower_work, iwl4965_bg_txpower_work);
4933#ifdef CONFIG_IWL4965_SENSITIVITY 4271#ifdef CONFIG_IWL4965_RUN_TIME_CALIB
4934 INIT_WORK(&priv->sensitivity_work, iwl4965_bg_sensitivity_work); 4272 INIT_WORK(&priv->sensitivity_work, iwl4965_bg_sensitivity_work);
4935#endif 4273#endif
4936 init_timer(&priv->statistics_periodic); 4274 init_timer(&priv->statistics_periodic);
@@ -4952,6 +4290,10 @@ static struct iwl_hcmd_ops iwl4965_hcmd = {
4952 4290
4953static struct iwl_hcmd_utils_ops iwl4965_hcmd_utils = { 4291static struct iwl_hcmd_utils_ops iwl4965_hcmd_utils = {
4954 .enqueue_hcmd = iwl4965_enqueue_hcmd, 4292 .enqueue_hcmd = iwl4965_enqueue_hcmd,
4293#ifdef CONFIG_IWL4965_RUN_TIME_CALIB
4294 .chain_noise_reset = iwl4965_chain_noise_reset,
4295 .gain_computation = iwl4965_gain_computation,
4296#endif
4955}; 4297};
4956 4298
4957static struct iwl_lib_ops iwl4965_lib = { 4299static struct iwl_lib_ops iwl4965_lib = {
diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.h b/drivers/net/wireless/iwlwifi/iwl-4965.h
index 581b98556c86..d728dd837e97 100644
--- a/drivers/net/wireless/iwlwifi/iwl-4965.h
+++ b/drivers/net/wireless/iwlwifi/iwl-4965.h
@@ -566,6 +566,29 @@ struct iwl4965_ibss_seq {
566 struct list_head list; 566 struct list_head list;
567}; 567};
568 568
569struct iwl_sensitivity_ranges {
570 u16 min_nrg_cck;
571 u16 max_nrg_cck;
572
573 u16 nrg_th_cck;
574 u16 nrg_th_ofdm;
575
576 u16 auto_corr_min_ofdm;
577 u16 auto_corr_min_ofdm_mrc;
578 u16 auto_corr_min_ofdm_x1;
579 u16 auto_corr_min_ofdm_mrc_x1;
580
581 u16 auto_corr_max_ofdm;
582 u16 auto_corr_max_ofdm_mrc;
583 u16 auto_corr_max_ofdm_x1;
584 u16 auto_corr_max_ofdm_mrc_x1;
585
586 u16 auto_corr_max_cck;
587 u16 auto_corr_max_cck_mrc;
588 u16 auto_corr_min_cck;
589 u16 auto_corr_min_cck_mrc;
590};
591
569/** 592/**
570 * struct iwl_hw_params 593 * struct iwl_hw_params
571 * @max_txq_num: Max # Tx queues supported 594 * @max_txq_num: Max # Tx queues supported
@@ -576,6 +599,7 @@ struct iwl4965_ibss_seq {
576 * @max_rxq_log: Log-base-2 of max_rxq_size 599 * @max_rxq_log: Log-base-2 of max_rxq_size
577 * @max_stations: 600 * @max_stations:
578 * @bcast_sta_id: 601 * @bcast_sta_id:
602 * @struct iwl_sensitivity_ranges: range of sensitivity values
579 */ 603 */
580struct iwl_hw_params { 604struct iwl_hw_params {
581 u16 max_txq_num; 605 u16 max_txq_num;
@@ -590,6 +614,9 @@ struct iwl_hw_params {
590 u32 max_pkt_size; 614 u32 max_pkt_size;
591 u8 max_stations; 615 u8 max_stations;
592 u8 bcast_sta_id; 616 u8 bcast_sta_id;
617#ifdef CONFIG_IWLWIFI_RUN_TIME_CALIB
618 const struct iwl_sensitivity_ranges *sens;
619#endif
593}; 620};
594 621
595#define HT_SHORT_GI_20MHZ_ONLY (1 << 0) 622#define HT_SHORT_GI_20MHZ_ONLY (1 << 0)
@@ -732,9 +759,6 @@ extern void iwl4965_add_station(struct iwl_priv *priv, const u8 *addr,
732extern void iwl4965_set_rxon_chain(struct iwl_priv *priv); 759extern void iwl4965_set_rxon_chain(struct iwl_priv *priv);
733extern int iwl4965_alive_notify(struct iwl_priv *priv); 760extern int iwl4965_alive_notify(struct iwl_priv *priv);
734extern void iwl4965_update_rate_scaling(struct iwl_priv *priv, u8 mode); 761extern void iwl4965_update_rate_scaling(struct iwl_priv *priv, u8 mode);
735extern void iwl4965_chain_noise_reset(struct iwl_priv *priv);
736extern void iwl4965_init_sensitivity(struct iwl_priv *priv, u8 flags,
737 u8 force);
738extern void iwl4965_rf_kill_ct_config(struct iwl_priv *priv); 762extern void iwl4965_rf_kill_ct_config(struct iwl_priv *priv);
739extern void iwl4965_hwrate_to_tx_control(struct iwl_priv *priv, 763extern void iwl4965_hwrate_to_tx_control(struct iwl_priv *priv,
740 u32 rate_n_flags, 764 u32 rate_n_flags,
@@ -818,23 +842,8 @@ struct iwl4965_lq_mngr {
818#define MAX_FA_CCK 50 842#define MAX_FA_CCK 50
819#define MIN_FA_CCK 5 843#define MIN_FA_CCK 5
820 844
821#define NRG_MIN_CCK 97
822#define NRG_MAX_CCK 0
823
824#define AUTO_CORR_MIN_OFDM 85
825#define AUTO_CORR_MIN_OFDM_MRC 170
826#define AUTO_CORR_MIN_OFDM_X1 105
827#define AUTO_CORR_MIN_OFDM_MRC_X1 220
828#define AUTO_CORR_MAX_OFDM 120
829#define AUTO_CORR_MAX_OFDM_MRC 210
830#define AUTO_CORR_MAX_OFDM_X1 140
831#define AUTO_CORR_MAX_OFDM_MRC_X1 270
832#define AUTO_CORR_STEP_OFDM 1 845#define AUTO_CORR_STEP_OFDM 1
833 846
834#define AUTO_CORR_MIN_CCK (125)
835#define AUTO_CORR_MAX_CCK (200)
836#define AUTO_CORR_MIN_CCK_MRC 200
837#define AUTO_CORR_MAX_CCK_MRC 400
838#define AUTO_CORR_STEP_CCK 3 847#define AUTO_CORR_STEP_CCK 3
839#define AUTO_CORR_MAX_TH_CCK 160 848#define AUTO_CORR_MAX_TH_CCK 160
840 849
@@ -865,11 +874,6 @@ enum iwl4965_chain_noise_state {
865 IWL_CHAIN_NOISE_CALIBRATED = 2, 874 IWL_CHAIN_NOISE_CALIBRATED = 2,
866}; 875};
867 876
868enum iwl4965_sensitivity_state {
869 IWL_SENS_CALIB_ALLOWED = 0,
870 IWL_SENS_CALIB_NEED_REINIT = 1,
871};
872
873enum iwl4965_calib_enabled_state { 877enum iwl4965_calib_enabled_state {
874 IWL_CALIB_DISABLED = 0, /* must be 0 */ 878 IWL_CALIB_DISABLED = 0, /* must be 0 */
875 IWL_CALIB_ENABLED = 1, 879 IWL_CALIB_ENABLED = 1,
@@ -884,8 +888,9 @@ struct statistics_general_data {
884 u32 beacon_energy_c; 888 u32 beacon_energy_c;
885}; 889};
886 890
891#ifdef CONFIG_IWLWIFI_RUN_TIME_CALIB
887/* Sensitivity calib data */ 892/* Sensitivity calib data */
888struct iwl4965_sensitivity_data { 893struct iwl_sensitivity_data {
889 u32 auto_corr_ofdm; 894 u32 auto_corr_ofdm;
890 u32 auto_corr_ofdm_mrc; 895 u32 auto_corr_ofdm_mrc;
891 u32 auto_corr_ofdm_x1; 896 u32 auto_corr_ofdm_x1;
@@ -909,12 +914,10 @@ struct iwl4965_sensitivity_data {
909 s32 nrg_auto_corr_silence_diff; 914 s32 nrg_auto_corr_silence_diff;
910 u32 num_in_cck_no_fa; 915 u32 num_in_cck_no_fa;
911 u32 nrg_th_ofdm; 916 u32 nrg_th_ofdm;
912
913 u8 state;
914}; 917};
915 918
916/* Chain noise (differential Rx gain) calib data */ 919/* Chain noise (differential Rx gain) calib data */
917struct iwl4965_chain_noise_data { 920struct iwl_chain_noise_data {
918 u8 state; 921 u8 state;
919 u16 beacon_count; 922 u16 beacon_count;
920 u32 chain_noise_a; 923 u32 chain_noise_a;
@@ -927,6 +930,7 @@ struct iwl4965_chain_noise_data {
927 u8 delta_gain_code[NUM_RX_CHAINS]; 930 u8 delta_gain_code[NUM_RX_CHAINS];
928 u8 radio_write; 931 u8 radio_write;
929}; 932};
933#endif /* CONFIG_IWLWIFI_RUN_TIME_CALIB */
930 934
931#define EEPROM_SEM_TIMEOUT 10 /* milliseconds */ 935#define EEPROM_SEM_TIMEOUT 10 /* milliseconds */
932#define EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ 936#define EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */
@@ -1051,12 +1055,12 @@ struct iwl_priv {
1051 u8 assoc_station_added; 1055 u8 assoc_station_added;
1052 u8 use_ant_b_for_management_frame; /* Tx antenna selection */ 1056 u8 use_ant_b_for_management_frame; /* Tx antenna selection */
1053 u8 valid_antenna; /* Bit mask of antennas actually connected */ 1057 u8 valid_antenna; /* Bit mask of antennas actually connected */
1054#ifdef CONFIG_IWL4965_SENSITIVITY
1055 struct iwl4965_sensitivity_data sensitivity_data;
1056 struct iwl4965_chain_noise_data chain_noise_data;
1057 u8 start_calib; 1058 u8 start_calib;
1059#ifdef CONFIG_IWLWIFI_RUN_TIME_CALIB
1060 struct iwl_sensitivity_data sensitivity_data;
1061 struct iwl_chain_noise_data chain_noise_data;
1058 __le16 sensitivity_tbl[HD_TABLE_SIZE]; 1062 __le16 sensitivity_tbl[HD_TABLE_SIZE];
1059#endif /*CONFIG_IWL4965_SENSITIVITY*/ 1063#endif /*CONFIG_IWLWIFI_RUN_TIME_CALIB*/
1060 1064
1061#ifdef CONFIG_IWL4965_HT 1065#ifdef CONFIG_IWL4965_HT
1062 struct iwl_ht_info current_ht_config; 1066 struct iwl_ht_info current_ht_config;
@@ -1206,7 +1210,7 @@ struct iwl_priv {
1206#endif /* CONFIG_IWLWIFI_DEBUG */ 1210#endif /* CONFIG_IWLWIFI_DEBUG */
1207 1211
1208 struct work_struct txpower_work; 1212 struct work_struct txpower_work;
1209#ifdef CONFIG_IWL4965_SENSITIVITY 1213#ifdef CONFIG_IWL4965_RUN_TIME_CALIB
1210 struct work_struct sensitivity_work; 1214 struct work_struct sensitivity_work;
1211#endif 1215#endif
1212 struct timer_list statistics_periodic; 1216 struct timer_list statistics_periodic;
diff --git a/drivers/net/wireless/iwlwifi/iwl-calib.c b/drivers/net/wireless/iwlwifi/iwl-calib.c
new file mode 100644
index 000000000000..16213b05ed93
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-calib.c
@@ -0,0 +1,778 @@
1/******************************************************************************
2 *
3 * This file is provided under a dual BSD/GPLv2 license. When using or
4 * redistributing this file, you may do so under either license.
5 *
6 * GPL LICENSE SUMMARY
7 *
8 * Copyright(c) 2008 Intel Corporation. All rights reserved.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of version 2 of the GNU General Public License as
12 * published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
22 * USA
23 *
24 * The full GNU General Public License is included in this distribution
25 * in the file called LICENSE.GPL.
26 *
27 * Contact Information:
28 * Tomas Winkler <tomas.winkler@intel.com>
29 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
30 *
31 * BSD LICENSE
32 *
33 * Copyright(c) 2005 - 2008 Intel Corporation. All rights reserved.
34 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 *
40 * * Redistributions of source code must retain the above copyright
41 * notice, this list of conditions and the following disclaimer.
42 * * Redistributions in binary form must reproduce the above copyright
43 * notice, this list of conditions and the following disclaimer in
44 * the documentation and/or other materials provided with the
45 * distribution.
46 * * Neither the name Intel Corporation nor the names of its
47 * contributors may be used to endorse or promote products derived
48 * from this software without specific prior written permission.
49 *
50 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
51 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
52 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
53 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
54 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
55 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
56 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
57 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
58 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
59 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
60 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
61 *****************************************************************************/
62
63#include <linux/kernel.h>
64#include <net/mac80211.h>
65
66#include "iwl-4965.h"
67#include "iwl-core.h"
68#include "iwl-calib.h"
69#include "iwl-eeprom.h"
70
71/* "false alarms" are signals that our DSP tries to lock onto,
72 * but then determines that they are either noise, or transmissions
73 * from a distant wireless network (also "noise", really) that get
74 * "stepped on" by stronger transmissions within our own network.
75 * This algorithm attempts to set a sensitivity level that is high
76 * enough to receive all of our own network traffic, but not so
77 * high that our DSP gets too busy trying to lock onto non-network
78 * activity/noise. */
79static int iwl_sens_energy_cck(struct iwl_priv *priv,
80 u32 norm_fa,
81 u32 rx_enable_time,
82 struct statistics_general_data *rx_info)
83{
84 u32 max_nrg_cck = 0;
85 int i = 0;
86 u8 max_silence_rssi = 0;
87 u32 silence_ref = 0;
88 u8 silence_rssi_a = 0;
89 u8 silence_rssi_b = 0;
90 u8 silence_rssi_c = 0;
91 u32 val;
92
93 /* "false_alarms" values below are cross-multiplications to assess the
94 * numbers of false alarms within the measured period of actual Rx
95 * (Rx is off when we're txing), vs the min/max expected false alarms
96 * (some should be expected if rx is sensitive enough) in a
97 * hypothetical listening period of 200 time units (TU), 204.8 msec:
98 *
99 * MIN_FA/fixed-time < false_alarms/actual-rx-time < MAX_FA/beacon-time
100 *
101 * */
102 u32 false_alarms = norm_fa * 200 * 1024;
103 u32 max_false_alarms = MAX_FA_CCK * rx_enable_time;
104 u32 min_false_alarms = MIN_FA_CCK * rx_enable_time;
105 struct iwl_sensitivity_data *data = NULL;
106 const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens;
107
108 data = &(priv->sensitivity_data);
109
110 data->nrg_auto_corr_silence_diff = 0;
111
112 /* Find max silence rssi among all 3 receivers.
113 * This is background noise, which may include transmissions from other
114 * networks, measured during silence before our network's beacon */
115 silence_rssi_a = (u8)((rx_info->beacon_silence_rssi_a &
116 ALL_BAND_FILTER) >> 8);
117 silence_rssi_b = (u8)((rx_info->beacon_silence_rssi_b &
118 ALL_BAND_FILTER) >> 8);
119 silence_rssi_c = (u8)((rx_info->beacon_silence_rssi_c &
120 ALL_BAND_FILTER) >> 8);
121
122 val = max(silence_rssi_b, silence_rssi_c);
123 max_silence_rssi = max(silence_rssi_a, (u8) val);
124
125 /* Store silence rssi in 20-beacon history table */
126 data->nrg_silence_rssi[data->nrg_silence_idx] = max_silence_rssi;
127 data->nrg_silence_idx++;
128 if (data->nrg_silence_idx >= NRG_NUM_PREV_STAT_L)
129 data->nrg_silence_idx = 0;
130
131 /* Find max silence rssi across 20 beacon history */
132 for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) {
133 val = data->nrg_silence_rssi[i];
134 silence_ref = max(silence_ref, val);
135 }
136 IWL_DEBUG_CALIB("silence a %u, b %u, c %u, 20-bcn max %u\n",
137 silence_rssi_a, silence_rssi_b, silence_rssi_c,
138 silence_ref);
139
140 /* Find max rx energy (min value!) among all 3 receivers,
141 * measured during beacon frame.
142 * Save it in 10-beacon history table. */
143 i = data->nrg_energy_idx;
144 val = min(rx_info->beacon_energy_b, rx_info->beacon_energy_c);
145 data->nrg_value[i] = min(rx_info->beacon_energy_a, val);
146
147 data->nrg_energy_idx++;
148 if (data->nrg_energy_idx >= 10)
149 data->nrg_energy_idx = 0;
150
151 /* Find min rx energy (max value) across 10 beacon history.
152 * This is the minimum signal level that we want to receive well.
153 * Add backoff (margin so we don't miss slightly lower energy frames).
154 * This establishes an upper bound (min value) for energy threshold. */
155 max_nrg_cck = data->nrg_value[0];
156 for (i = 1; i < 10; i++)
157 max_nrg_cck = (u32) max(max_nrg_cck, (data->nrg_value[i]));
158 max_nrg_cck += 6;
159
160 IWL_DEBUG_CALIB("rx energy a %u, b %u, c %u, 10-bcn max/min %u\n",
161 rx_info->beacon_energy_a, rx_info->beacon_energy_b,
162 rx_info->beacon_energy_c, max_nrg_cck - 6);
163
164 /* Count number of consecutive beacons with fewer-than-desired
165 * false alarms. */
166 if (false_alarms < min_false_alarms)
167 data->num_in_cck_no_fa++;
168 else
169 data->num_in_cck_no_fa = 0;
170 IWL_DEBUG_CALIB("consecutive bcns with few false alarms = %u\n",
171 data->num_in_cck_no_fa);
172
173 /* If we got too many false alarms this time, reduce sensitivity */
174 if ((false_alarms > max_false_alarms) &&
175 (data->auto_corr_cck > AUTO_CORR_MAX_TH_CCK)) {
176 IWL_DEBUG_CALIB("norm FA %u > max FA %u\n",
177 false_alarms, max_false_alarms);
178 IWL_DEBUG_CALIB("... reducing sensitivity\n");
179 data->nrg_curr_state = IWL_FA_TOO_MANY;
180 /* Store for "fewer than desired" on later beacon */
181 data->nrg_silence_ref = silence_ref;
182
183 /* increase energy threshold (reduce nrg value)
184 * to decrease sensitivity */
185 if (data->nrg_th_cck >
186 (ranges->max_nrg_cck + NRG_STEP_CCK))
187 data->nrg_th_cck = data->nrg_th_cck
188 - NRG_STEP_CCK;
189 else
190 data->nrg_th_cck = ranges->max_nrg_cck;
191 /* Else if we got fewer than desired, increase sensitivity */
192 } else if (false_alarms < min_false_alarms) {
193 data->nrg_curr_state = IWL_FA_TOO_FEW;
194
195 /* Compare silence level with silence level for most recent
196 * healthy number or too many false alarms */
197 data->nrg_auto_corr_silence_diff = (s32)data->nrg_silence_ref -
198 (s32)silence_ref;
199
200 IWL_DEBUG_CALIB("norm FA %u < min FA %u, silence diff %d\n",
201 false_alarms, min_false_alarms,
202 data->nrg_auto_corr_silence_diff);
203
204 /* Increase value to increase sensitivity, but only if:
205 * 1a) previous beacon did *not* have *too many* false alarms
206 * 1b) AND there's a significant difference in Rx levels
207 * from a previous beacon with too many, or healthy # FAs
208 * OR 2) We've seen a lot of beacons (100) with too few
209 * false alarms */
210 if ((data->nrg_prev_state != IWL_FA_TOO_MANY) &&
211 ((data->nrg_auto_corr_silence_diff > NRG_DIFF) ||
212 (data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) {
213
214 IWL_DEBUG_CALIB("... increasing sensitivity\n");
215 /* Increase nrg value to increase sensitivity */
216 val = data->nrg_th_cck + NRG_STEP_CCK;
217 data->nrg_th_cck = min((u32)ranges->min_nrg_cck, val);
218 } else {
219 IWL_DEBUG_CALIB("... but not changing sensitivity\n");
220 }
221
222 /* Else we got a healthy number of false alarms, keep status quo */
223 } else {
224 IWL_DEBUG_CALIB(" FA in safe zone\n");
225 data->nrg_curr_state = IWL_FA_GOOD_RANGE;
226
227 /* Store for use in "fewer than desired" with later beacon */
228 data->nrg_silence_ref = silence_ref;
229
230 /* If previous beacon had too many false alarms,
231 * give it some extra margin by reducing sensitivity again
232 * (but don't go below measured energy of desired Rx) */
233 if (IWL_FA_TOO_MANY == data->nrg_prev_state) {
234 IWL_DEBUG_CALIB("... increasing margin\n");
235 if (data->nrg_th_cck > (max_nrg_cck + NRG_MARGIN))
236 data->nrg_th_cck -= NRG_MARGIN;
237 else
238 data->nrg_th_cck = max_nrg_cck;
239 }
240 }
241
242 /* Make sure the energy threshold does not go above the measured
243 * energy of the desired Rx signals (reduced by backoff margin),
244 * or else we might start missing Rx frames.
245 * Lower value is higher energy, so we use max()!
246 */
247 data->nrg_th_cck = max(max_nrg_cck, data->nrg_th_cck);
248 IWL_DEBUG_CALIB("new nrg_th_cck %u\n", data->nrg_th_cck);
249
250 data->nrg_prev_state = data->nrg_curr_state;
251
252 /* Auto-correlation CCK algorithm */
253 if (false_alarms > min_false_alarms) {
254
255 /* increase auto_corr values to decrease sensitivity
256 * so the DSP won't be disturbed by the noise
257 */
258 if (data->auto_corr_cck < AUTO_CORR_MAX_TH_CCK)
259 data->auto_corr_cck = AUTO_CORR_MAX_TH_CCK + 1;
260 else {
261 val = data->auto_corr_cck + AUTO_CORR_STEP_CCK;
262 data->auto_corr_cck =
263 min((u32)ranges->auto_corr_max_cck, val);
264 }
265 val = data->auto_corr_cck_mrc + AUTO_CORR_STEP_CCK;
266 data->auto_corr_cck_mrc =
267 min((u32)ranges->auto_corr_max_cck_mrc, val);
268 } else if ((false_alarms < min_false_alarms) &&
269 ((data->nrg_auto_corr_silence_diff > NRG_DIFF) ||
270 (data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) {
271
272 /* Decrease auto_corr values to increase sensitivity */
273 val = data->auto_corr_cck - AUTO_CORR_STEP_CCK;
274 data->auto_corr_cck =
275 max((u32)ranges->auto_corr_min_cck, val);
276 val = data->auto_corr_cck_mrc - AUTO_CORR_STEP_CCK;
277 data->auto_corr_cck_mrc =
278 max((u32)ranges->auto_corr_min_cck_mrc, val);
279 }
280
281 return 0;
282}
283
284
285static int iwl_sens_auto_corr_ofdm(struct iwl_priv *priv,
286 u32 norm_fa,
287 u32 rx_enable_time)
288{
289 u32 val;
290 u32 false_alarms = norm_fa * 200 * 1024;
291 u32 max_false_alarms = MAX_FA_OFDM * rx_enable_time;
292 u32 min_false_alarms = MIN_FA_OFDM * rx_enable_time;
293 struct iwl_sensitivity_data *data = NULL;
294 const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens;
295
296 data = &(priv->sensitivity_data);
297
298 /* If we got too many false alarms this time, reduce sensitivity */
299 if (false_alarms > max_false_alarms) {
300
301 IWL_DEBUG_CALIB("norm FA %u > max FA %u)\n",
302 false_alarms, max_false_alarms);
303
304 val = data->auto_corr_ofdm + AUTO_CORR_STEP_OFDM;
305 data->auto_corr_ofdm =
306 min((u32)ranges->auto_corr_max_ofdm, val);
307
308 val = data->auto_corr_ofdm_mrc + AUTO_CORR_STEP_OFDM;
309 data->auto_corr_ofdm_mrc =
310 min((u32)ranges->auto_corr_max_ofdm_mrc, val);
311
312 val = data->auto_corr_ofdm_x1 + AUTO_CORR_STEP_OFDM;
313 data->auto_corr_ofdm_x1 =
314 min((u32)ranges->auto_corr_max_ofdm_x1, val);
315
316 val = data->auto_corr_ofdm_mrc_x1 + AUTO_CORR_STEP_OFDM;
317 data->auto_corr_ofdm_mrc_x1 =
318 min((u32)ranges->auto_corr_max_ofdm_mrc_x1, val);
319 }
320
321 /* Else if we got fewer than desired, increase sensitivity */
322 else if (false_alarms < min_false_alarms) {
323
324 IWL_DEBUG_CALIB("norm FA %u < min FA %u\n",
325 false_alarms, min_false_alarms);
326
327 val = data->auto_corr_ofdm - AUTO_CORR_STEP_OFDM;
328 data->auto_corr_ofdm =
329 max((u32)ranges->auto_corr_min_ofdm, val);
330
331 val = data->auto_corr_ofdm_mrc - AUTO_CORR_STEP_OFDM;
332 data->auto_corr_ofdm_mrc =
333 max((u32)ranges->auto_corr_min_ofdm_mrc, val);
334
335 val = data->auto_corr_ofdm_x1 - AUTO_CORR_STEP_OFDM;
336 data->auto_corr_ofdm_x1 =
337 max((u32)ranges->auto_corr_min_ofdm_x1, val);
338
339 val = data->auto_corr_ofdm_mrc_x1 - AUTO_CORR_STEP_OFDM;
340 data->auto_corr_ofdm_mrc_x1 =
341 max((u32)ranges->auto_corr_min_ofdm_mrc_x1, val);
342 } else {
343 IWL_DEBUG_CALIB("min FA %u < norm FA %u < max FA %u OK\n",
344 min_false_alarms, false_alarms, max_false_alarms);
345 }
346 return 0;
347}
348
349/* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */
350static int iwl_sensitivity_write(struct iwl_priv *priv)
351{
352 int ret = 0;
353 struct iwl_sensitivity_cmd cmd ;
354 struct iwl_sensitivity_data *data = NULL;
355 struct iwl_host_cmd cmd_out = {
356 .id = SENSITIVITY_CMD,
357 .len = sizeof(struct iwl_sensitivity_cmd),
358 .meta.flags = CMD_ASYNC,
359 .data = &cmd,
360 };
361
362 data = &(priv->sensitivity_data);
363
364 memset(&cmd, 0, sizeof(cmd));
365
366 cmd.table[HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX] =
367 cpu_to_le16((u16)data->auto_corr_ofdm);
368 cmd.table[HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX] =
369 cpu_to_le16((u16)data->auto_corr_ofdm_mrc);
370 cmd.table[HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX] =
371 cpu_to_le16((u16)data->auto_corr_ofdm_x1);
372 cmd.table[HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX] =
373 cpu_to_le16((u16)data->auto_corr_ofdm_mrc_x1);
374
375 cmd.table[HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX] =
376 cpu_to_le16((u16)data->auto_corr_cck);
377 cmd.table[HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX] =
378 cpu_to_le16((u16)data->auto_corr_cck_mrc);
379
380 cmd.table[HD_MIN_ENERGY_CCK_DET_INDEX] =
381 cpu_to_le16((u16)data->nrg_th_cck);
382 cmd.table[HD_MIN_ENERGY_OFDM_DET_INDEX] =
383 cpu_to_le16((u16)data->nrg_th_ofdm);
384
385 cmd.table[HD_BARKER_CORR_TH_ADD_MIN_INDEX] =
386 __constant_cpu_to_le16(190);
387 cmd.table[HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX] =
388 __constant_cpu_to_le16(390);
389 cmd.table[HD_OFDM_ENERGY_TH_IN_INDEX] =
390 __constant_cpu_to_le16(62);
391
392 IWL_DEBUG_CALIB("ofdm: ac %u mrc %u x1 %u mrc_x1 %u thresh %u\n",
393 data->auto_corr_ofdm, data->auto_corr_ofdm_mrc,
394 data->auto_corr_ofdm_x1, data->auto_corr_ofdm_mrc_x1,
395 data->nrg_th_ofdm);
396
397 IWL_DEBUG_CALIB("cck: ac %u mrc %u thresh %u\n",
398 data->auto_corr_cck, data->auto_corr_cck_mrc,
399 data->nrg_th_cck);
400
401 /* Update uCode's "work" table, and copy it to DSP */
402 cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE;
403
404 /* Don't send command to uCode if nothing has changed */
405 if (!memcmp(&cmd.table[0], &(priv->sensitivity_tbl[0]),
406 sizeof(u16)*HD_TABLE_SIZE)) {
407 IWL_DEBUG_CALIB("No change in SENSITIVITY_CMD\n");
408 return 0;
409 }
410
411 /* Copy table for comparison next time */
412 memcpy(&(priv->sensitivity_tbl[0]), &(cmd.table[0]),
413 sizeof(u16)*HD_TABLE_SIZE);
414
415 ret = iwl_send_cmd(priv, &cmd_out);
416 if (ret)
417 IWL_ERROR("SENSITIVITY_CMD failed\n");
418
419 return ret;
420}
421
422void iwl_init_sensitivity(struct iwl_priv *priv)
423{
424 int ret = 0;
425 int i;
426 struct iwl_sensitivity_data *data = NULL;
427 const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens;
428
429 IWL_DEBUG_CALIB("Start iwl_init_sensitivity\n");
430
431 /* Clear driver's sensitivity algo data */
432 data = &(priv->sensitivity_data);
433
434 if (ranges == NULL)
435 /* can happen if IWLWIFI_RUN_TIME_CALIB is selected
436 * but no IWLXXXX_RUN_TIME_CALIB for specific is selected */
437 return;
438
439 memset(data, 0, sizeof(struct iwl_sensitivity_data));
440
441 data->num_in_cck_no_fa = 0;
442 data->nrg_curr_state = IWL_FA_TOO_MANY;
443 data->nrg_prev_state = IWL_FA_TOO_MANY;
444 data->nrg_silence_ref = 0;
445 data->nrg_silence_idx = 0;
446 data->nrg_energy_idx = 0;
447
448 for (i = 0; i < 10; i++)
449 data->nrg_value[i] = 0;
450
451 for (i = 0; i < NRG_NUM_PREV_STAT_L; i++)
452 data->nrg_silence_rssi[i] = 0;
453
454 data->auto_corr_ofdm = 90;
455 data->auto_corr_ofdm_mrc = ranges->auto_corr_min_ofdm_mrc;
456 data->auto_corr_ofdm_x1 = ranges->auto_corr_min_ofdm_x1;
457 data->auto_corr_ofdm_mrc_x1 = ranges->auto_corr_min_ofdm_mrc_x1;
458 data->auto_corr_cck = AUTO_CORR_CCK_MIN_VAL_DEF;
459 data->auto_corr_cck_mrc = ranges->auto_corr_min_cck_mrc;
460 data->nrg_th_cck = ranges->nrg_th_cck;
461 data->nrg_th_ofdm = ranges->nrg_th_ofdm;
462
463 data->last_bad_plcp_cnt_ofdm = 0;
464 data->last_fa_cnt_ofdm = 0;
465 data->last_bad_plcp_cnt_cck = 0;
466 data->last_fa_cnt_cck = 0;
467
468 ret |= iwl_sensitivity_write(priv);
469 IWL_DEBUG_CALIB("<<return 0x%X\n", ret);
470}
471EXPORT_SYMBOL(iwl_init_sensitivity);
472
473void iwl_sensitivity_calibration(struct iwl_priv *priv,
474 struct iwl4965_notif_statistics *resp)
475{
476 u32 rx_enable_time;
477 u32 fa_cck;
478 u32 fa_ofdm;
479 u32 bad_plcp_cck;
480 u32 bad_plcp_ofdm;
481 u32 norm_fa_ofdm;
482 u32 norm_fa_cck;
483 struct iwl_sensitivity_data *data = NULL;
484 struct statistics_rx_non_phy *rx_info = &(resp->rx.general);
485 struct statistics_rx *statistics = &(resp->rx);
486 unsigned long flags;
487 struct statistics_general_data statis;
488
489 data = &(priv->sensitivity_data);
490
491 if (!iwl_is_associated(priv)) {
492 IWL_DEBUG_CALIB("<< - not associated\n");
493 return;
494 }
495
496 spin_lock_irqsave(&priv->lock, flags);
497 if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) {
498 IWL_DEBUG_CALIB("<< invalid data.\n");
499 spin_unlock_irqrestore(&priv->lock, flags);
500 return;
501 }
502
503 /* Extract Statistics: */
504 rx_enable_time = le32_to_cpu(rx_info->channel_load);
505 fa_cck = le32_to_cpu(statistics->cck.false_alarm_cnt);
506 fa_ofdm = le32_to_cpu(statistics->ofdm.false_alarm_cnt);
507 bad_plcp_cck = le32_to_cpu(statistics->cck.plcp_err);
508 bad_plcp_ofdm = le32_to_cpu(statistics->ofdm.plcp_err);
509
510 statis.beacon_silence_rssi_a =
511 le32_to_cpu(statistics->general.beacon_silence_rssi_a);
512 statis.beacon_silence_rssi_b =
513 le32_to_cpu(statistics->general.beacon_silence_rssi_b);
514 statis.beacon_silence_rssi_c =
515 le32_to_cpu(statistics->general.beacon_silence_rssi_c);
516 statis.beacon_energy_a =
517 le32_to_cpu(statistics->general.beacon_energy_a);
518 statis.beacon_energy_b =
519 le32_to_cpu(statistics->general.beacon_energy_b);
520 statis.beacon_energy_c =
521 le32_to_cpu(statistics->general.beacon_energy_c);
522
523 spin_unlock_irqrestore(&priv->lock, flags);
524
525 IWL_DEBUG_CALIB("rx_enable_time = %u usecs\n", rx_enable_time);
526
527 if (!rx_enable_time) {
528 IWL_DEBUG_CALIB("<< RX Enable Time == 0! \n");
529 return;
530 }
531
532 /* These statistics increase monotonically, and do not reset
533 * at each beacon. Calculate difference from last value, or just
534 * use the new statistics value if it has reset or wrapped around. */
535 if (data->last_bad_plcp_cnt_cck > bad_plcp_cck)
536 data->last_bad_plcp_cnt_cck = bad_plcp_cck;
537 else {
538 bad_plcp_cck -= data->last_bad_plcp_cnt_cck;
539 data->last_bad_plcp_cnt_cck += bad_plcp_cck;
540 }
541
542 if (data->last_bad_plcp_cnt_ofdm > bad_plcp_ofdm)
543 data->last_bad_plcp_cnt_ofdm = bad_plcp_ofdm;
544 else {
545 bad_plcp_ofdm -= data->last_bad_plcp_cnt_ofdm;
546 data->last_bad_plcp_cnt_ofdm += bad_plcp_ofdm;
547 }
548
549 if (data->last_fa_cnt_ofdm > fa_ofdm)
550 data->last_fa_cnt_ofdm = fa_ofdm;
551 else {
552 fa_ofdm -= data->last_fa_cnt_ofdm;
553 data->last_fa_cnt_ofdm += fa_ofdm;
554 }
555
556 if (data->last_fa_cnt_cck > fa_cck)
557 data->last_fa_cnt_cck = fa_cck;
558 else {
559 fa_cck -= data->last_fa_cnt_cck;
560 data->last_fa_cnt_cck += fa_cck;
561 }
562
563 /* Total aborted signal locks */
564 norm_fa_ofdm = fa_ofdm + bad_plcp_ofdm;
565 norm_fa_cck = fa_cck + bad_plcp_cck;
566
567 IWL_DEBUG_CALIB("cck: fa %u badp %u ofdm: fa %u badp %u\n", fa_cck,
568 bad_plcp_cck, fa_ofdm, bad_plcp_ofdm);
569
570 iwl_sens_auto_corr_ofdm(priv, norm_fa_ofdm, rx_enable_time);
571 iwl_sens_energy_cck(priv, norm_fa_cck, rx_enable_time, &statis);
572 iwl_sensitivity_write(priv);
573
574 return;
575}
576EXPORT_SYMBOL(iwl_sensitivity_calibration);
577
578/*
579 * Accumulate 20 beacons of signal and noise statistics for each of
580 * 3 receivers/antennas/rx-chains, then figure out:
581 * 1) Which antennas are connected.
582 * 2) Differential rx gain settings to balance the 3 receivers.
583 */
584void iwl_chain_noise_calibration(struct iwl_priv *priv,
585 struct iwl4965_notif_statistics *stat_resp)
586{
587 struct iwl_chain_noise_data *data = NULL;
588
589 u32 chain_noise_a;
590 u32 chain_noise_b;
591 u32 chain_noise_c;
592 u32 chain_sig_a;
593 u32 chain_sig_b;
594 u32 chain_sig_c;
595 u32 average_sig[NUM_RX_CHAINS] = {INITIALIZATION_VALUE};
596 u32 average_noise[NUM_RX_CHAINS] = {INITIALIZATION_VALUE};
597 u32 max_average_sig;
598 u16 max_average_sig_antenna_i;
599 u32 min_average_noise = MIN_AVERAGE_NOISE_MAX_VALUE;
600 u16 min_average_noise_antenna_i = INITIALIZATION_VALUE;
601 u16 i = 0;
602 u16 rxon_chnum = INITIALIZATION_VALUE;
603 u16 stat_chnum = INITIALIZATION_VALUE;
604 u8 rxon_band24;
605 u8 stat_band24;
606 u32 active_chains = 0;
607 u8 num_tx_chains;
608 unsigned long flags;
609 struct statistics_rx_non_phy *rx_info = &(stat_resp->rx.general);
610
611 data = &(priv->chain_noise_data);
612
613 /* Accumulate just the first 20 beacons after the first association,
614 * then we're done forever. */
615 if (data->state != IWL_CHAIN_NOISE_ACCUMULATE) {
616 if (data->state == IWL_CHAIN_NOISE_ALIVE)
617 IWL_DEBUG_CALIB("Wait for noise calib reset\n");
618 return;
619 }
620
621 spin_lock_irqsave(&priv->lock, flags);
622 if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) {
623 IWL_DEBUG_CALIB(" << Interference data unavailable\n");
624 spin_unlock_irqrestore(&priv->lock, flags);
625 return;
626 }
627
628 rxon_band24 = !!(priv->staging_rxon.flags & RXON_FLG_BAND_24G_MSK);
629 rxon_chnum = le16_to_cpu(priv->staging_rxon.channel);
630 stat_band24 = !!(stat_resp->flag & STATISTICS_REPLY_FLG_BAND_24G_MSK);
631 stat_chnum = le32_to_cpu(stat_resp->flag) >> 16;
632
633 /* Make sure we accumulate data for just the associated channel
634 * (even if scanning). */
635 if ((rxon_chnum != stat_chnum) || (rxon_band24 != stat_band24)) {
636 IWL_DEBUG_CALIB("Stats not from chan=%d, band24=%d\n",
637 rxon_chnum, rxon_band24);
638 spin_unlock_irqrestore(&priv->lock, flags);
639 return;
640 }
641
642 /* Accumulate beacon statistics values across 20 beacons */
643 chain_noise_a = le32_to_cpu(rx_info->beacon_silence_rssi_a) &
644 IN_BAND_FILTER;
645 chain_noise_b = le32_to_cpu(rx_info->beacon_silence_rssi_b) &
646 IN_BAND_FILTER;
647 chain_noise_c = le32_to_cpu(rx_info->beacon_silence_rssi_c) &
648 IN_BAND_FILTER;
649
650 chain_sig_a = le32_to_cpu(rx_info->beacon_rssi_a) & IN_BAND_FILTER;
651 chain_sig_b = le32_to_cpu(rx_info->beacon_rssi_b) & IN_BAND_FILTER;
652 chain_sig_c = le32_to_cpu(rx_info->beacon_rssi_c) & IN_BAND_FILTER;
653
654 spin_unlock_irqrestore(&priv->lock, flags);
655
656 data->beacon_count++;
657
658 data->chain_noise_a = (chain_noise_a + data->chain_noise_a);
659 data->chain_noise_b = (chain_noise_b + data->chain_noise_b);
660 data->chain_noise_c = (chain_noise_c + data->chain_noise_c);
661
662 data->chain_signal_a = (chain_sig_a + data->chain_signal_a);
663 data->chain_signal_b = (chain_sig_b + data->chain_signal_b);
664 data->chain_signal_c = (chain_sig_c + data->chain_signal_c);
665
666 IWL_DEBUG_CALIB("chan=%d, band24=%d, beacon=%d\n",
667 rxon_chnum, rxon_band24, data->beacon_count);
668 IWL_DEBUG_CALIB("chain_sig: a %d b %d c %d\n",
669 chain_sig_a, chain_sig_b, chain_sig_c);
670 IWL_DEBUG_CALIB("chain_noise: a %d b %d c %d\n",
671 chain_noise_a, chain_noise_b, chain_noise_c);
672
673 /* If this is the 20th beacon, determine:
674 * 1) Disconnected antennas (using signal strengths)
675 * 2) Differential gain (using silence noise) to balance receivers */
676 if (data->beacon_count != CAL_NUM_OF_BEACONS)
677 return;
678
679 /* Analyze signal for disconnected antenna */
680 average_sig[0] = (data->chain_signal_a) / CAL_NUM_OF_BEACONS;
681 average_sig[1] = (data->chain_signal_b) / CAL_NUM_OF_BEACONS;
682 average_sig[2] = (data->chain_signal_c) / CAL_NUM_OF_BEACONS;
683
684 if (average_sig[0] >= average_sig[1]) {
685 max_average_sig = average_sig[0];
686 max_average_sig_antenna_i = 0;
687 active_chains = (1 << max_average_sig_antenna_i);
688 } else {
689 max_average_sig = average_sig[1];
690 max_average_sig_antenna_i = 1;
691 active_chains = (1 << max_average_sig_antenna_i);
692 }
693
694 if (average_sig[2] >= max_average_sig) {
695 max_average_sig = average_sig[2];
696 max_average_sig_antenna_i = 2;
697 active_chains = (1 << max_average_sig_antenna_i);
698 }
699
700 IWL_DEBUG_CALIB("average_sig: a %d b %d c %d\n",
701 average_sig[0], average_sig[1], average_sig[2]);
702 IWL_DEBUG_CALIB("max_average_sig = %d, antenna %d\n",
703 max_average_sig, max_average_sig_antenna_i);
704
705 /* Compare signal strengths for all 3 receivers. */
706 for (i = 0; i < NUM_RX_CHAINS; i++) {
707 if (i != max_average_sig_antenna_i) {
708 s32 rssi_delta = (max_average_sig - average_sig[i]);
709
710 /* If signal is very weak, compared with
711 * strongest, mark it as disconnected. */
712 if (rssi_delta > MAXIMUM_ALLOWED_PATHLOSS)
713 data->disconn_array[i] = 1;
714 else
715 active_chains |= (1 << i);
716 IWL_DEBUG_CALIB("i = %d rssiDelta = %d "
717 "disconn_array[i] = %d\n",
718 i, rssi_delta, data->disconn_array[i]);
719 }
720 }
721
722 num_tx_chains = 0;
723 for (i = 0; i < NUM_RX_CHAINS; i++) {
724 /* loops on all the bits of
725 * priv->hw_setting.valid_tx_ant */
726 u8 ant_msk = (1 << i);
727 if (!(priv->hw_params.valid_tx_ant & ant_msk))
728 continue;
729
730 num_tx_chains++;
731 if (data->disconn_array[i] == 0)
732 /* there is a Tx antenna connected */
733 break;
734 if (num_tx_chains == priv->hw_params.tx_chains_num &&
735 data->disconn_array[i]) {
736 /* This is the last TX antenna and is also
737 * disconnected connect it anyway */
738 data->disconn_array[i] = 0;
739 active_chains |= ant_msk;
740 IWL_DEBUG_CALIB("All Tx chains are disconnected W/A - "
741 "declare %d as connected\n", i);
742 break;
743 }
744 }
745
746 IWL_DEBUG_CALIB("active_chains (bitwise) = 0x%x\n",
747 active_chains);
748
749 /* Save for use within RXON, TX, SCAN commands, etc. */
750 priv->valid_antenna = active_chains;
751
752 /* Analyze noise for rx balance */
753 average_noise[0] = ((data->chain_noise_a)/CAL_NUM_OF_BEACONS);
754 average_noise[1] = ((data->chain_noise_b)/CAL_NUM_OF_BEACONS);
755 average_noise[2] = ((data->chain_noise_c)/CAL_NUM_OF_BEACONS);
756
757 for (i = 0; i < NUM_RX_CHAINS; i++) {
758 if (!(data->disconn_array[i]) &&
759 (average_noise[i] <= min_average_noise)) {
760 /* This means that chain i is active and has
761 * lower noise values so far: */
762 min_average_noise = average_noise[i];
763 min_average_noise_antenna_i = i;
764 }
765 }
766
767 IWL_DEBUG_CALIB("average_noise: a %d b %d c %d\n",
768 average_noise[0], average_noise[1],
769 average_noise[2]);
770
771 IWL_DEBUG_CALIB("min_average_noise = %d, antenna %d\n",
772 min_average_noise, min_average_noise_antenna_i);
773
774 priv->cfg->ops->utils->gain_computation(priv, average_noise,
775 min_average_noise_antenna_i, min_average_noise);
776}
777EXPORT_SYMBOL(iwl_chain_noise_calibration);
778
diff --git a/drivers/net/wireless/iwlwifi/iwl-calib.h b/drivers/net/wireless/iwlwifi/iwl-calib.h
new file mode 100644
index 000000000000..c3a3db311999
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-calib.h
@@ -0,0 +1,104 @@
1/******************************************************************************
2 *
3 * This file is provided under a dual BSD/GPLv2 license. When using or
4 * redistributing this file, you may do so under either license.
5 *
6 * GPL LICENSE SUMMARY
7 *
8 * Copyright(c) 2008 Intel Corporation. All rights reserved.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of version 2 of the GNU General Public License as
12 * published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
22 * USA
23 *
24 * The full GNU General Public License is included in this distribution
25 * in the file called LICENSE.GPL.
26 *
27 * Contact Information:
28 * Tomas Winkler <tomas.winkler@intel.com>
29 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
30 *
31 * BSD LICENSE
32 *
33 * Copyright(c) 2005 - 2008 Intel Corporation. All rights reserved.
34 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 *
40 * * Redistributions of source code must retain the above copyright
41 * notice, this list of conditions and the following disclaimer.
42 * * Redistributions in binary form must reproduce the above copyright
43 * notice, this list of conditions and the following disclaimer in
44 * the documentation and/or other materials provided with the
45 * distribution.
46 * * Neither the name Intel Corporation nor the names of its
47 * contributors may be used to endorse or promote products derived
48 * from this software without specific prior written permission.
49 *
50 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
51 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
52 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
53 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
54 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
55 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
56 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
57 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
58 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
59 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
60 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
61 *****************************************************************************/
62#ifndef __iwl_calib_h__
63#define __iwl_calib_h__
64
65#include <linux/kernel.h>
66#include <linux/module.h>
67#include <linux/version.h>
68
69#include <net/mac80211.h>
70#include "iwl-eeprom.h"
71#include "iwl-core.h"
72#include "iwl-4965.h"
73
74#ifdef CONFIG_IWLWIFI_RUN_TIME_CALIB
75void iwl_chain_noise_calibration(struct iwl_priv *priv,
76 struct iwl4965_notif_statistics *stat_resp);
77void iwl_sensitivity_calibration(struct iwl_priv *priv,
78 struct iwl4965_notif_statistics *resp);
79
80void iwl_init_sensitivity(struct iwl_priv *priv);
81
82static inline void iwl_chain_noise_reset(struct iwl_priv *priv)
83{
84 if (priv->cfg->ops->utils->chain_noise_reset)
85 priv->cfg->ops->utils->chain_noise_reset(priv);
86}
87#else
88static inline void iwl_chain_noise_calibration(struct iwl_priv *priv,
89 struct iwl4965_notif_statistics *stat_resp)
90{
91}
92static inline void iwl_sensitivity_calibration(struct iwl_priv *priv,
93 struct iwl4965_notif_statistics *resp)
94{
95}
96static inline void iwl_init_sensitivity(struct iwl_priv *priv)
97{
98}
99static inline void iwl_chain_noise_reset(struct iwl_priv *priv)
100{
101}
102#endif
103
104#endif /* __iwl_calib_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h
index 7193d97630dc..dcbd411a4767 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.h
+++ b/drivers/net/wireless/iwlwifi/iwl-core.h
@@ -87,6 +87,13 @@ struct iwl_hcmd_ops {
87}; 87};
88struct iwl_hcmd_utils_ops { 88struct iwl_hcmd_utils_ops {
89 int (*enqueue_hcmd)(struct iwl_priv *priv, struct iwl_host_cmd *cmd); 89 int (*enqueue_hcmd)(struct iwl_priv *priv, struct iwl_host_cmd *cmd);
90#ifdef CONFIG_IWLWIFI_RUN_TIME_CALIB
91 void (*gain_computation)(struct iwl_priv *priv,
92 u32 *average_noise,
93 u16 min_average_noise_antennat_i,
94 u32 min_average_noise);
95 void (*chain_noise_reset)(struct iwl_priv *priv);
96#endif
90}; 97};
91 98
92struct iwl_lib_ops { 99struct iwl_lib_ops {
diff --git a/drivers/net/wireless/iwlwifi/iwl4965-base.c b/drivers/net/wireless/iwlwifi/iwl4965-base.c
index 883b42f7e998..8a68ff4323de 100644
--- a/drivers/net/wireless/iwlwifi/iwl4965-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl4965-base.c
@@ -51,6 +51,7 @@
51#include "iwl-io.h" 51#include "iwl-io.h"
52#include "iwl-helpers.h" 52#include "iwl-helpers.h"
53#include "iwl-sta.h" 53#include "iwl-sta.h"
54#include "iwl-calib.h"
54 55
55static int iwl4965_tx_queue_update_write_ptr(struct iwl_priv *priv, 56static int iwl4965_tx_queue_update_write_ptr(struct iwl_priv *priv,
56 struct iwl4965_tx_queue *txq); 57 struct iwl4965_tx_queue *txq);
@@ -795,14 +796,6 @@ static int iwl4965_commit_rxon(struct iwl_priv *priv)
795 /* station table will be cleared */ 796 /* station table will be cleared */
796 priv->assoc_station_added = 0; 797 priv->assoc_station_added = 0;
797 798
798#ifdef CONFIG_IWL4965_SENSITIVITY
799 priv->sensitivity_data.state = IWL_SENS_CALIB_NEED_REINIT;
800 if (!priv->error_recovering)
801 priv->start_calib = 0;
802
803 iwl4965_init_sensitivity(priv, CMD_ASYNC, 1);
804#endif /* CONFIG_IWL4965_SENSITIVITY */
805
806 /* If we are currently associated and the new config requires 799 /* If we are currently associated and the new config requires
807 * an RXON_ASSOC and the new config wants the associated mask enabled, 800 * an RXON_ASSOC and the new config wants the associated mask enabled,
808 * we must clear the associated from the active configuration 801 * we must clear the associated from the active configuration
@@ -846,13 +839,10 @@ static int iwl4965_commit_rxon(struct iwl_priv *priv)
846 839
847 iwlcore_clear_stations_table(priv); 840 iwlcore_clear_stations_table(priv);
848 841
849#ifdef CONFIG_IWL4965_SENSITIVITY
850 if (!priv->error_recovering) 842 if (!priv->error_recovering)
851 priv->start_calib = 0; 843 priv->start_calib = 0;
852 844
853 priv->sensitivity_data.state = IWL_SENS_CALIB_NEED_REINIT; 845 iwl_init_sensitivity(priv);
854 iwl4965_init_sensitivity(priv, CMD_ASYNC, 1);
855#endif /* CONFIG_IWL4965_SENSITIVITY */
856 846
857 memcpy(active_rxon, &priv->staging_rxon, sizeof(*active_rxon)); 847 memcpy(active_rxon, &priv->staging_rxon, sizeof(*active_rxon));
858 848
@@ -6040,11 +6030,9 @@ static void iwl4965_post_associate(struct iwl_priv *priv)
6040 6030
6041 iwl4965_sequence_reset(priv); 6031 iwl4965_sequence_reset(priv);
6042 6032
6043#ifdef CONFIG_IWL4965_SENSITIVITY
6044 /* Enable Rx differential gain and sensitivity calibrations */ 6033 /* Enable Rx differential gain and sensitivity calibrations */
6045 iwl4965_chain_noise_reset(priv); 6034 iwl_chain_noise_reset(priv);
6046 priv->start_calib = 1; 6035 priv->start_calib = 1;
6047#endif /* CONFIG_IWL4965_SENSITIVITY */
6048 6036
6049 if (priv->iw_mode == IEEE80211_IF_TYPE_IBSS) 6037 if (priv->iw_mode == IEEE80211_IF_TYPE_IBSS)
6050 priv->assoc_station_added = 1; 6038 priv->assoc_station_added = 1;