0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063 #include <linux/slab.h>
0064 #include <net/mac80211.h>
0065
0066 #include "common.h"
0067 #include "4965.h"
0068
0069
0070
0071
0072
0073 struct stats_general_data {
0074 u32 beacon_silence_rssi_a;
0075 u32 beacon_silence_rssi_b;
0076 u32 beacon_silence_rssi_c;
0077 u32 beacon_energy_a;
0078 u32 beacon_energy_b;
0079 u32 beacon_energy_c;
0080 };
0081
0082
0083
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094 static int
0095 il4965_sens_energy_cck(struct il_priv *il, u32 norm_fa, u32 rx_enable_time,
0096 struct stats_general_data *rx_info)
0097 {
0098 u32 max_nrg_cck = 0;
0099 int i = 0;
0100 u8 max_silence_rssi = 0;
0101 u32 silence_ref = 0;
0102 u8 silence_rssi_a = 0;
0103 u8 silence_rssi_b = 0;
0104 u8 silence_rssi_c = 0;
0105 u32 val;
0106
0107
0108
0109
0110
0111
0112
0113
0114
0115
0116 u32 false_alarms = norm_fa * 200 * 1024;
0117 u32 max_false_alarms = MAX_FA_CCK * rx_enable_time;
0118 u32 min_false_alarms = MIN_FA_CCK * rx_enable_time;
0119 struct il_sensitivity_data *data = NULL;
0120 const struct il_sensitivity_ranges *ranges = il->hw_params.sens;
0121
0122 data = &(il->sensitivity_data);
0123
0124 data->nrg_auto_corr_silence_diff = 0;
0125
0126
0127
0128
0129 silence_rssi_a =
0130 (u8) ((rx_info->beacon_silence_rssi_a & ALL_BAND_FILTER) >> 8);
0131 silence_rssi_b =
0132 (u8) ((rx_info->beacon_silence_rssi_b & ALL_BAND_FILTER) >> 8);
0133 silence_rssi_c =
0134 (u8) ((rx_info->beacon_silence_rssi_c & ALL_BAND_FILTER) >> 8);
0135
0136 val = max(silence_rssi_b, silence_rssi_c);
0137 max_silence_rssi = max(silence_rssi_a, (u8) val);
0138
0139
0140 data->nrg_silence_rssi[data->nrg_silence_idx] = max_silence_rssi;
0141 data->nrg_silence_idx++;
0142 if (data->nrg_silence_idx >= NRG_NUM_PREV_STAT_L)
0143 data->nrg_silence_idx = 0;
0144
0145
0146 for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) {
0147 val = data->nrg_silence_rssi[i];
0148 silence_ref = max(silence_ref, val);
0149 }
0150 D_CALIB("silence a %u, b %u, c %u, 20-bcn max %u\n", silence_rssi_a,
0151 silence_rssi_b, silence_rssi_c, silence_ref);
0152
0153
0154
0155
0156 i = data->nrg_energy_idx;
0157 val = min(rx_info->beacon_energy_b, rx_info->beacon_energy_c);
0158 data->nrg_value[i] = min(rx_info->beacon_energy_a, val);
0159
0160 data->nrg_energy_idx++;
0161 if (data->nrg_energy_idx >= 10)
0162 data->nrg_energy_idx = 0;
0163
0164
0165
0166
0167
0168 max_nrg_cck = data->nrg_value[0];
0169 for (i = 1; i < 10; i++)
0170 max_nrg_cck = (u32) max(max_nrg_cck, (data->nrg_value[i]));
0171 max_nrg_cck += 6;
0172
0173 D_CALIB("rx energy a %u, b %u, c %u, 10-bcn max/min %u\n",
0174 rx_info->beacon_energy_a, rx_info->beacon_energy_b,
0175 rx_info->beacon_energy_c, max_nrg_cck - 6);
0176
0177
0178
0179 if (false_alarms < min_false_alarms)
0180 data->num_in_cck_no_fa++;
0181 else
0182 data->num_in_cck_no_fa = 0;
0183 D_CALIB("consecutive bcns with few false alarms = %u\n",
0184 data->num_in_cck_no_fa);
0185
0186
0187 if (false_alarms > max_false_alarms &&
0188 data->auto_corr_cck > AUTO_CORR_MAX_TH_CCK) {
0189 D_CALIB("norm FA %u > max FA %u\n", false_alarms,
0190 max_false_alarms);
0191 D_CALIB("... reducing sensitivity\n");
0192 data->nrg_curr_state = IL_FA_TOO_MANY;
0193
0194 data->nrg_silence_ref = silence_ref;
0195
0196
0197
0198 data->nrg_th_cck = data->nrg_th_cck - NRG_STEP_CCK;
0199
0200 } else if (false_alarms < min_false_alarms) {
0201 data->nrg_curr_state = IL_FA_TOO_FEW;
0202
0203
0204
0205 data->nrg_auto_corr_silence_diff =
0206 (s32) data->nrg_silence_ref - (s32) silence_ref;
0207
0208 D_CALIB("norm FA %u < min FA %u, silence diff %d\n",
0209 false_alarms, min_false_alarms,
0210 data->nrg_auto_corr_silence_diff);
0211
0212
0213
0214
0215
0216
0217
0218 if (data->nrg_prev_state != IL_FA_TOO_MANY &&
0219 (data->nrg_auto_corr_silence_diff > NRG_DIFF ||
0220 data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA)) {
0221
0222 D_CALIB("... increasing sensitivity\n");
0223
0224 val = data->nrg_th_cck + NRG_STEP_CCK;
0225 data->nrg_th_cck = min((u32) ranges->min_nrg_cck, val);
0226 } else {
0227 D_CALIB("... but not changing sensitivity\n");
0228 }
0229
0230
0231 } else {
0232 D_CALIB(" FA in safe zone\n");
0233 data->nrg_curr_state = IL_FA_GOOD_RANGE;
0234
0235
0236 data->nrg_silence_ref = silence_ref;
0237
0238
0239
0240
0241 if (IL_FA_TOO_MANY == data->nrg_prev_state) {
0242 D_CALIB("... increasing margin\n");
0243 if (data->nrg_th_cck > (max_nrg_cck + NRG_MARGIN))
0244 data->nrg_th_cck -= NRG_MARGIN;
0245 else
0246 data->nrg_th_cck = max_nrg_cck;
0247 }
0248 }
0249
0250
0251
0252
0253
0254
0255 data->nrg_th_cck = max(max_nrg_cck, data->nrg_th_cck);
0256 D_CALIB("new nrg_th_cck %u\n", data->nrg_th_cck);
0257
0258 data->nrg_prev_state = data->nrg_curr_state;
0259
0260
0261 if (false_alarms > min_false_alarms) {
0262
0263
0264
0265
0266 if (data->auto_corr_cck < AUTO_CORR_MAX_TH_CCK)
0267 data->auto_corr_cck = AUTO_CORR_MAX_TH_CCK + 1;
0268 else {
0269 val = data->auto_corr_cck + AUTO_CORR_STEP_CCK;
0270 data->auto_corr_cck =
0271 min((u32) ranges->auto_corr_max_cck, val);
0272 }
0273 val = data->auto_corr_cck_mrc + AUTO_CORR_STEP_CCK;
0274 data->auto_corr_cck_mrc =
0275 min((u32) ranges->auto_corr_max_cck_mrc, val);
0276 } else if (false_alarms < min_false_alarms &&
0277 (data->nrg_auto_corr_silence_diff > NRG_DIFF ||
0278 data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA)) {
0279
0280
0281 val = data->auto_corr_cck - AUTO_CORR_STEP_CCK;
0282 data->auto_corr_cck = max((u32) ranges->auto_corr_min_cck, val);
0283 val = data->auto_corr_cck_mrc - AUTO_CORR_STEP_CCK;
0284 data->auto_corr_cck_mrc =
0285 max((u32) ranges->auto_corr_min_cck_mrc, val);
0286 }
0287
0288 return 0;
0289 }
0290
0291 static int
0292 il4965_sens_auto_corr_ofdm(struct il_priv *il, u32 norm_fa, u32 rx_enable_time)
0293 {
0294 u32 val;
0295 u32 false_alarms = norm_fa * 200 * 1024;
0296 u32 max_false_alarms = MAX_FA_OFDM * rx_enable_time;
0297 u32 min_false_alarms = MIN_FA_OFDM * rx_enable_time;
0298 struct il_sensitivity_data *data = NULL;
0299 const struct il_sensitivity_ranges *ranges = il->hw_params.sens;
0300
0301 data = &(il->sensitivity_data);
0302
0303
0304 if (false_alarms > max_false_alarms) {
0305
0306 D_CALIB("norm FA %u > max FA %u)\n", false_alarms,
0307 max_false_alarms);
0308
0309 val = data->auto_corr_ofdm + AUTO_CORR_STEP_OFDM;
0310 data->auto_corr_ofdm =
0311 min((u32) ranges->auto_corr_max_ofdm, val);
0312
0313 val = data->auto_corr_ofdm_mrc + AUTO_CORR_STEP_OFDM;
0314 data->auto_corr_ofdm_mrc =
0315 min((u32) ranges->auto_corr_max_ofdm_mrc, val);
0316
0317 val = data->auto_corr_ofdm_x1 + AUTO_CORR_STEP_OFDM;
0318 data->auto_corr_ofdm_x1 =
0319 min((u32) ranges->auto_corr_max_ofdm_x1, val);
0320
0321 val = data->auto_corr_ofdm_mrc_x1 + AUTO_CORR_STEP_OFDM;
0322 data->auto_corr_ofdm_mrc_x1 =
0323 min((u32) ranges->auto_corr_max_ofdm_mrc_x1, val);
0324 }
0325
0326
0327 else if (false_alarms < min_false_alarms) {
0328
0329 D_CALIB("norm FA %u < min FA %u\n", false_alarms,
0330 min_false_alarms);
0331
0332 val = data->auto_corr_ofdm - AUTO_CORR_STEP_OFDM;
0333 data->auto_corr_ofdm =
0334 max((u32) ranges->auto_corr_min_ofdm, val);
0335
0336 val = data->auto_corr_ofdm_mrc - AUTO_CORR_STEP_OFDM;
0337 data->auto_corr_ofdm_mrc =
0338 max((u32) ranges->auto_corr_min_ofdm_mrc, val);
0339
0340 val = data->auto_corr_ofdm_x1 - AUTO_CORR_STEP_OFDM;
0341 data->auto_corr_ofdm_x1 =
0342 max((u32) ranges->auto_corr_min_ofdm_x1, val);
0343
0344 val = data->auto_corr_ofdm_mrc_x1 - AUTO_CORR_STEP_OFDM;
0345 data->auto_corr_ofdm_mrc_x1 =
0346 max((u32) ranges->auto_corr_min_ofdm_mrc_x1, val);
0347 } else {
0348 D_CALIB("min FA %u < norm FA %u < max FA %u OK\n",
0349 min_false_alarms, false_alarms, max_false_alarms);
0350 }
0351 return 0;
0352 }
0353
0354 static void
0355 il4965_prepare_legacy_sensitivity_tbl(struct il_priv *il,
0356 struct il_sensitivity_data *data,
0357 __le16 *tbl)
0358 {
0359 tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_IDX] =
0360 cpu_to_le16((u16) data->auto_corr_ofdm);
0361 tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_IDX] =
0362 cpu_to_le16((u16) data->auto_corr_ofdm_mrc);
0363 tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_IDX] =
0364 cpu_to_le16((u16) data->auto_corr_ofdm_x1);
0365 tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_IDX] =
0366 cpu_to_le16((u16) data->auto_corr_ofdm_mrc_x1);
0367
0368 tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_IDX] =
0369 cpu_to_le16((u16) data->auto_corr_cck);
0370 tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_IDX] =
0371 cpu_to_le16((u16) data->auto_corr_cck_mrc);
0372
0373 tbl[HD_MIN_ENERGY_CCK_DET_IDX] = cpu_to_le16((u16) data->nrg_th_cck);
0374 tbl[HD_MIN_ENERGY_OFDM_DET_IDX] = cpu_to_le16((u16) data->nrg_th_ofdm);
0375
0376 tbl[HD_BARKER_CORR_TH_ADD_MIN_IDX] =
0377 cpu_to_le16(data->barker_corr_th_min);
0378 tbl[HD_BARKER_CORR_TH_ADD_MIN_MRC_IDX] =
0379 cpu_to_le16(data->barker_corr_th_min_mrc);
0380 tbl[HD_OFDM_ENERGY_TH_IN_IDX] = cpu_to_le16(data->nrg_th_cca);
0381
0382 D_CALIB("ofdm: ac %u mrc %u x1 %u mrc_x1 %u thresh %u\n",
0383 data->auto_corr_ofdm, data->auto_corr_ofdm_mrc,
0384 data->auto_corr_ofdm_x1, data->auto_corr_ofdm_mrc_x1,
0385 data->nrg_th_ofdm);
0386
0387 D_CALIB("cck: ac %u mrc %u thresh %u\n", data->auto_corr_cck,
0388 data->auto_corr_cck_mrc, data->nrg_th_cck);
0389 }
0390
0391
0392 static int
0393 il4965_sensitivity_write(struct il_priv *il)
0394 {
0395 struct il_sensitivity_cmd cmd;
0396 struct il_sensitivity_data *data = NULL;
0397 struct il_host_cmd cmd_out = {
0398 .id = C_SENSITIVITY,
0399 .len = sizeof(struct il_sensitivity_cmd),
0400 .flags = CMD_ASYNC,
0401 .data = &cmd,
0402 };
0403
0404 data = &(il->sensitivity_data);
0405
0406 memset(&cmd, 0, sizeof(cmd));
0407
0408 il4965_prepare_legacy_sensitivity_tbl(il, data, &cmd.table[0]);
0409
0410
0411 cmd.control = C_SENSITIVITY_CONTROL_WORK_TBL;
0412
0413
0414 if (!memcmp
0415 (&cmd.table[0], &(il->sensitivity_tbl[0]),
0416 sizeof(u16) * HD_TBL_SIZE)) {
0417 D_CALIB("No change in C_SENSITIVITY\n");
0418 return 0;
0419 }
0420
0421
0422 memcpy(&(il->sensitivity_tbl[0]), &(cmd.table[0]),
0423 sizeof(u16) * HD_TBL_SIZE);
0424
0425 return il_send_cmd(il, &cmd_out);
0426 }
0427
0428 void
0429 il4965_init_sensitivity(struct il_priv *il)
0430 {
0431 int ret = 0;
0432 int i;
0433 struct il_sensitivity_data *data = NULL;
0434 const struct il_sensitivity_ranges *ranges = il->hw_params.sens;
0435
0436 if (il->disable_sens_cal)
0437 return;
0438
0439 D_CALIB("Start il4965_init_sensitivity\n");
0440
0441
0442 data = &(il->sensitivity_data);
0443
0444 if (ranges == NULL)
0445 return;
0446
0447 memset(data, 0, sizeof(struct il_sensitivity_data));
0448
0449 data->num_in_cck_no_fa = 0;
0450 data->nrg_curr_state = IL_FA_TOO_MANY;
0451 data->nrg_prev_state = IL_FA_TOO_MANY;
0452 data->nrg_silence_ref = 0;
0453 data->nrg_silence_idx = 0;
0454 data->nrg_energy_idx = 0;
0455
0456 for (i = 0; i < 10; i++)
0457 data->nrg_value[i] = 0;
0458
0459 for (i = 0; i < NRG_NUM_PREV_STAT_L; i++)
0460 data->nrg_silence_rssi[i] = 0;
0461
0462 data->auto_corr_ofdm = ranges->auto_corr_min_ofdm;
0463 data->auto_corr_ofdm_mrc = ranges->auto_corr_min_ofdm_mrc;
0464 data->auto_corr_ofdm_x1 = ranges->auto_corr_min_ofdm_x1;
0465 data->auto_corr_ofdm_mrc_x1 = ranges->auto_corr_min_ofdm_mrc_x1;
0466 data->auto_corr_cck = AUTO_CORR_CCK_MIN_VAL_DEF;
0467 data->auto_corr_cck_mrc = ranges->auto_corr_min_cck_mrc;
0468 data->nrg_th_cck = ranges->nrg_th_cck;
0469 data->nrg_th_ofdm = ranges->nrg_th_ofdm;
0470 data->barker_corr_th_min = ranges->barker_corr_th_min;
0471 data->barker_corr_th_min_mrc = ranges->barker_corr_th_min_mrc;
0472 data->nrg_th_cca = ranges->nrg_th_cca;
0473
0474 data->last_bad_plcp_cnt_ofdm = 0;
0475 data->last_fa_cnt_ofdm = 0;
0476 data->last_bad_plcp_cnt_cck = 0;
0477 data->last_fa_cnt_cck = 0;
0478
0479 ret |= il4965_sensitivity_write(il);
0480 D_CALIB("<<return 0x%X\n", ret);
0481 }
0482
0483 void
0484 il4965_sensitivity_calibration(struct il_priv *il, void *resp)
0485 {
0486 u32 rx_enable_time;
0487 u32 fa_cck;
0488 u32 fa_ofdm;
0489 u32 bad_plcp_cck;
0490 u32 bad_plcp_ofdm;
0491 u32 norm_fa_ofdm;
0492 u32 norm_fa_cck;
0493 struct il_sensitivity_data *data = NULL;
0494 struct stats_rx_non_phy *rx_info;
0495 struct stats_rx_phy *ofdm, *cck;
0496 unsigned long flags;
0497 struct stats_general_data statis;
0498
0499 if (il->disable_sens_cal)
0500 return;
0501
0502 data = &(il->sensitivity_data);
0503
0504 if (!il_is_any_associated(il)) {
0505 D_CALIB("<< - not associated\n");
0506 return;
0507 }
0508
0509 spin_lock_irqsave(&il->lock, flags);
0510
0511 rx_info = &(((struct il_notif_stats *)resp)->rx.general);
0512 ofdm = &(((struct il_notif_stats *)resp)->rx.ofdm);
0513 cck = &(((struct il_notif_stats *)resp)->rx.cck);
0514
0515 if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) {
0516 D_CALIB("<< invalid data.\n");
0517 spin_unlock_irqrestore(&il->lock, flags);
0518 return;
0519 }
0520
0521
0522 rx_enable_time = le32_to_cpu(rx_info->channel_load);
0523 fa_cck = le32_to_cpu(cck->false_alarm_cnt);
0524 fa_ofdm = le32_to_cpu(ofdm->false_alarm_cnt);
0525 bad_plcp_cck = le32_to_cpu(cck->plcp_err);
0526 bad_plcp_ofdm = le32_to_cpu(ofdm->plcp_err);
0527
0528 statis.beacon_silence_rssi_a =
0529 le32_to_cpu(rx_info->beacon_silence_rssi_a);
0530 statis.beacon_silence_rssi_b =
0531 le32_to_cpu(rx_info->beacon_silence_rssi_b);
0532 statis.beacon_silence_rssi_c =
0533 le32_to_cpu(rx_info->beacon_silence_rssi_c);
0534 statis.beacon_energy_a = le32_to_cpu(rx_info->beacon_energy_a);
0535 statis.beacon_energy_b = le32_to_cpu(rx_info->beacon_energy_b);
0536 statis.beacon_energy_c = le32_to_cpu(rx_info->beacon_energy_c);
0537
0538 spin_unlock_irqrestore(&il->lock, flags);
0539
0540 D_CALIB("rx_enable_time = %u usecs\n", rx_enable_time);
0541
0542 if (!rx_enable_time) {
0543 D_CALIB("<< RX Enable Time == 0!\n");
0544 return;
0545 }
0546
0547
0548
0549
0550 if (data->last_bad_plcp_cnt_cck > bad_plcp_cck)
0551 data->last_bad_plcp_cnt_cck = bad_plcp_cck;
0552 else {
0553 bad_plcp_cck -= data->last_bad_plcp_cnt_cck;
0554 data->last_bad_plcp_cnt_cck += bad_plcp_cck;
0555 }
0556
0557 if (data->last_bad_plcp_cnt_ofdm > bad_plcp_ofdm)
0558 data->last_bad_plcp_cnt_ofdm = bad_plcp_ofdm;
0559 else {
0560 bad_plcp_ofdm -= data->last_bad_plcp_cnt_ofdm;
0561 data->last_bad_plcp_cnt_ofdm += bad_plcp_ofdm;
0562 }
0563
0564 if (data->last_fa_cnt_ofdm > fa_ofdm)
0565 data->last_fa_cnt_ofdm = fa_ofdm;
0566 else {
0567 fa_ofdm -= data->last_fa_cnt_ofdm;
0568 data->last_fa_cnt_ofdm += fa_ofdm;
0569 }
0570
0571 if (data->last_fa_cnt_cck > fa_cck)
0572 data->last_fa_cnt_cck = fa_cck;
0573 else {
0574 fa_cck -= data->last_fa_cnt_cck;
0575 data->last_fa_cnt_cck += fa_cck;
0576 }
0577
0578
0579 norm_fa_ofdm = fa_ofdm + bad_plcp_ofdm;
0580 norm_fa_cck = fa_cck + bad_plcp_cck;
0581
0582 D_CALIB("cck: fa %u badp %u ofdm: fa %u badp %u\n", fa_cck,
0583 bad_plcp_cck, fa_ofdm, bad_plcp_ofdm);
0584
0585 il4965_sens_auto_corr_ofdm(il, norm_fa_ofdm, rx_enable_time);
0586 il4965_sens_energy_cck(il, norm_fa_cck, rx_enable_time, &statis);
0587
0588 il4965_sensitivity_write(il);
0589 }
0590
0591 static inline u8
0592 il4965_find_first_chain(u8 mask)
0593 {
0594 if (mask & ANT_A)
0595 return CHAIN_A;
0596 if (mask & ANT_B)
0597 return CHAIN_B;
0598 return CHAIN_C;
0599 }
0600
0601
0602
0603
0604
0605 static void
0606 il4965_find_disconn_antenna(struct il_priv *il, u32 * average_sig,
0607 struct il_chain_noise_data *data)
0608 {
0609 u32 active_chains = 0;
0610 u32 max_average_sig;
0611 u16 max_average_sig_antenna_i;
0612 u8 num_tx_chains;
0613 u8 first_chain;
0614 u16 i = 0;
0615
0616 average_sig[0] =
0617 data->chain_signal_a /
0618 il->cfg->chain_noise_num_beacons;
0619 average_sig[1] =
0620 data->chain_signal_b /
0621 il->cfg->chain_noise_num_beacons;
0622 average_sig[2] =
0623 data->chain_signal_c /
0624 il->cfg->chain_noise_num_beacons;
0625
0626 if (average_sig[0] >= average_sig[1]) {
0627 max_average_sig = average_sig[0];
0628 max_average_sig_antenna_i = 0;
0629 active_chains = (1 << max_average_sig_antenna_i);
0630 } else {
0631 max_average_sig = average_sig[1];
0632 max_average_sig_antenna_i = 1;
0633 active_chains = (1 << max_average_sig_antenna_i);
0634 }
0635
0636 if (average_sig[2] >= max_average_sig) {
0637 max_average_sig = average_sig[2];
0638 max_average_sig_antenna_i = 2;
0639 active_chains = (1 << max_average_sig_antenna_i);
0640 }
0641
0642 D_CALIB("average_sig: a %d b %d c %d\n", average_sig[0], average_sig[1],
0643 average_sig[2]);
0644 D_CALIB("max_average_sig = %d, antenna %d\n", max_average_sig,
0645 max_average_sig_antenna_i);
0646
0647
0648 for (i = 0; i < NUM_RX_CHAINS; i++) {
0649 if (i != max_average_sig_antenna_i) {
0650 s32 rssi_delta = (max_average_sig - average_sig[i]);
0651
0652
0653
0654 if (rssi_delta > MAXIMUM_ALLOWED_PATHLOSS)
0655 data->disconn_array[i] = 1;
0656 else
0657 active_chains |= (1 << i);
0658 D_CALIB("i = %d rssiDelta = %d "
0659 "disconn_array[i] = %d\n", i, rssi_delta,
0660 data->disconn_array[i]);
0661 }
0662 }
0663
0664
0665
0666
0667
0668
0669
0670
0671
0672
0673
0674 active_chains &= il->hw_params.valid_rx_ant;
0675
0676 num_tx_chains = 0;
0677 for (i = 0; i < NUM_RX_CHAINS; i++) {
0678
0679
0680 u8 ant_msk = (1 << i);
0681 if (!(il->hw_params.valid_tx_ant & ant_msk))
0682 continue;
0683
0684 num_tx_chains++;
0685 if (data->disconn_array[i] == 0)
0686
0687 break;
0688 if (num_tx_chains == il->hw_params.tx_chains_num &&
0689 data->disconn_array[i]) {
0690
0691
0692
0693
0694 first_chain =
0695 il4965_find_first_chain(il->cfg->valid_tx_ant);
0696 data->disconn_array[first_chain] = 0;
0697 active_chains |= BIT(first_chain);
0698 D_CALIB("All Tx chains are disconnected"
0699 "- declare %d as connected\n", first_chain);
0700 break;
0701 }
0702 }
0703
0704 if (active_chains != il->hw_params.valid_rx_ant &&
0705 active_chains != il->chain_noise_data.active_chains)
0706 D_CALIB("Detected that not all antennas are connected! "
0707 "Connected: %#x, valid: %#x.\n", active_chains,
0708 il->hw_params.valid_rx_ant);
0709
0710
0711 data->active_chains = active_chains;
0712 D_CALIB("active_chains (bitwise) = 0x%x\n", active_chains);
0713 }
0714
0715 static void
0716 il4965_gain_computation(struct il_priv *il, u32 * average_noise,
0717 u16 min_average_noise_antenna_i, u32 min_average_noise,
0718 u8 default_chain)
0719 {
0720 int i, ret;
0721 struct il_chain_noise_data *data = &il->chain_noise_data;
0722
0723 data->delta_gain_code[min_average_noise_antenna_i] = 0;
0724
0725 for (i = default_chain; i < NUM_RX_CHAINS; i++) {
0726 s32 delta_g = 0;
0727
0728 if (!data->disconn_array[i] &&
0729 data->delta_gain_code[i] ==
0730 CHAIN_NOISE_DELTA_GAIN_INIT_VAL) {
0731 delta_g = average_noise[i] - min_average_noise;
0732 data->delta_gain_code[i] = (u8) ((delta_g * 10) / 15);
0733 data->delta_gain_code[i] =
0734 min(data->delta_gain_code[i],
0735 (u8) CHAIN_NOISE_MAX_DELTA_GAIN_CODE);
0736
0737 data->delta_gain_code[i] =
0738 (data->delta_gain_code[i] | (1 << 2));
0739 } else {
0740 data->delta_gain_code[i] = 0;
0741 }
0742 }
0743 D_CALIB("delta_gain_codes: a %d b %d c %d\n", data->delta_gain_code[0],
0744 data->delta_gain_code[1], data->delta_gain_code[2]);
0745
0746
0747 if (!data->radio_write) {
0748 struct il_calib_diff_gain_cmd cmd;
0749 data->radio_write = 1;
0750
0751 memset(&cmd, 0, sizeof(cmd));
0752 cmd.hdr.op_code = IL_PHY_CALIBRATE_DIFF_GAIN_CMD;
0753 cmd.diff_gain_a = data->delta_gain_code[0];
0754 cmd.diff_gain_b = data->delta_gain_code[1];
0755 cmd.diff_gain_c = data->delta_gain_code[2];
0756 ret = il_send_cmd_pdu(il, C_PHY_CALIBRATION, sizeof(cmd), &cmd);
0757 if (ret)
0758 D_CALIB("fail sending cmd " "C_PHY_CALIBRATION\n");
0759
0760
0761
0762
0763
0764 data->state = IL_CHAIN_NOISE_CALIBRATED;
0765 }
0766 }
0767
0768
0769
0770
0771
0772
0773
0774 void
0775 il4965_chain_noise_calibration(struct il_priv *il, void *stat_resp)
0776 {
0777 struct il_chain_noise_data *data = NULL;
0778
0779 u32 chain_noise_a;
0780 u32 chain_noise_b;
0781 u32 chain_noise_c;
0782 u32 chain_sig_a;
0783 u32 chain_sig_b;
0784 u32 chain_sig_c;
0785 u32 average_sig[NUM_RX_CHAINS] = { INITIALIZATION_VALUE };
0786 u32 average_noise[NUM_RX_CHAINS] = { INITIALIZATION_VALUE };
0787 u32 min_average_noise = MIN_AVERAGE_NOISE_MAX_VALUE;
0788 u16 min_average_noise_antenna_i = INITIALIZATION_VALUE;
0789 u16 i = 0;
0790 u16 rxon_chnum = INITIALIZATION_VALUE;
0791 u16 stat_chnum = INITIALIZATION_VALUE;
0792 u8 rxon_band24;
0793 u8 stat_band24;
0794 unsigned long flags;
0795 struct stats_rx_non_phy *rx_info;
0796
0797 if (il->disable_chain_noise_cal)
0798 return;
0799
0800 data = &(il->chain_noise_data);
0801
0802
0803
0804
0805
0806 if (data->state != IL_CHAIN_NOISE_ACCUMULATE) {
0807 if (data->state == IL_CHAIN_NOISE_ALIVE)
0808 D_CALIB("Wait for noise calib reset\n");
0809 return;
0810 }
0811
0812 spin_lock_irqsave(&il->lock, flags);
0813
0814 rx_info = &(((struct il_notif_stats *)stat_resp)->rx.general);
0815
0816 if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) {
0817 D_CALIB(" << Interference data unavailable\n");
0818 spin_unlock_irqrestore(&il->lock, flags);
0819 return;
0820 }
0821
0822 rxon_band24 = !!(il->staging.flags & RXON_FLG_BAND_24G_MSK);
0823 rxon_chnum = le16_to_cpu(il->staging.channel);
0824
0825 stat_band24 =
0826 !!(((struct il_notif_stats *)stat_resp)->
0827 flag & STATS_REPLY_FLG_BAND_24G_MSK);
0828 stat_chnum =
0829 le32_to_cpu(((struct il_notif_stats *)stat_resp)->flag) >> 16;
0830
0831
0832
0833 if (rxon_chnum != stat_chnum || rxon_band24 != stat_band24) {
0834 D_CALIB("Stats not from chan=%d, band24=%d\n", rxon_chnum,
0835 rxon_band24);
0836 spin_unlock_irqrestore(&il->lock, flags);
0837 return;
0838 }
0839
0840
0841
0842
0843
0844 chain_noise_a =
0845 le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER;
0846 chain_noise_b =
0847 le32_to_cpu(rx_info->beacon_silence_rssi_b) & IN_BAND_FILTER;
0848 chain_noise_c =
0849 le32_to_cpu(rx_info->beacon_silence_rssi_c) & IN_BAND_FILTER;
0850
0851 chain_sig_a = le32_to_cpu(rx_info->beacon_rssi_a) & IN_BAND_FILTER;
0852 chain_sig_b = le32_to_cpu(rx_info->beacon_rssi_b) & IN_BAND_FILTER;
0853 chain_sig_c = le32_to_cpu(rx_info->beacon_rssi_c) & IN_BAND_FILTER;
0854
0855 spin_unlock_irqrestore(&il->lock, flags);
0856
0857 data->beacon_count++;
0858
0859 data->chain_noise_a = (chain_noise_a + data->chain_noise_a);
0860 data->chain_noise_b = (chain_noise_b + data->chain_noise_b);
0861 data->chain_noise_c = (chain_noise_c + data->chain_noise_c);
0862
0863 data->chain_signal_a = (chain_sig_a + data->chain_signal_a);
0864 data->chain_signal_b = (chain_sig_b + data->chain_signal_b);
0865 data->chain_signal_c = (chain_sig_c + data->chain_signal_c);
0866
0867 D_CALIB("chan=%d, band24=%d, beacon=%d\n", rxon_chnum, rxon_band24,
0868 data->beacon_count);
0869 D_CALIB("chain_sig: a %d b %d c %d\n", chain_sig_a, chain_sig_b,
0870 chain_sig_c);
0871 D_CALIB("chain_noise: a %d b %d c %d\n", chain_noise_a, chain_noise_b,
0872 chain_noise_c);
0873
0874
0875
0876
0877 if (data->beacon_count != il->cfg->chain_noise_num_beacons)
0878 return;
0879
0880
0881 il4965_find_disconn_antenna(il, average_sig, data);
0882
0883
0884 average_noise[0] =
0885 data->chain_noise_a / il->cfg->chain_noise_num_beacons;
0886 average_noise[1] =
0887 data->chain_noise_b / il->cfg->chain_noise_num_beacons;
0888 average_noise[2] =
0889 data->chain_noise_c / il->cfg->chain_noise_num_beacons;
0890
0891 for (i = 0; i < NUM_RX_CHAINS; i++) {
0892 if (!data->disconn_array[i] &&
0893 average_noise[i] <= min_average_noise) {
0894
0895
0896 min_average_noise = average_noise[i];
0897 min_average_noise_antenna_i = i;
0898 }
0899 }
0900
0901 D_CALIB("average_noise: a %d b %d c %d\n", average_noise[0],
0902 average_noise[1], average_noise[2]);
0903
0904 D_CALIB("min_average_noise = %d, antenna %d\n", min_average_noise,
0905 min_average_noise_antenna_i);
0906
0907 il4965_gain_computation(il, average_noise, min_average_noise_antenna_i,
0908 min_average_noise,
0909 il4965_find_first_chain(il->cfg->valid_rx_ant));
0910
0911
0912
0913
0914 if (il->ops->update_chain_flags)
0915 il->ops->update_chain_flags(il);
0916
0917 data->state = IL_CHAIN_NOISE_DONE;
0918 il_power_update_mode(il, false);
0919 }
0920
0921 void
0922 il4965_reset_run_time_calib(struct il_priv *il)
0923 {
0924 int i;
0925 memset(&(il->sensitivity_data), 0, sizeof(struct il_sensitivity_data));
0926 memset(&(il->chain_noise_data), 0, sizeof(struct il_chain_noise_data));
0927 for (i = 0; i < NUM_RX_CHAINS; i++)
0928 il->chain_noise_data.delta_gain_code[i] =
0929 CHAIN_NOISE_DELTA_GAIN_INIT_VAL;
0930
0931
0932
0933 il_send_stats_request(il, CMD_ASYNC, true);
0934 }