4965-calib.c 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948
  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 - 2011 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. * Intel Linux Wireless <ilw@linux.intel.com>
  29. * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
  30. *
  31. * BSD LICENSE
  32. *
  33. * Copyright(c) 2005 - 2011 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. #include <linux/slab.h>
  63. #include <net/mac80211.h>
  64. #include "common.h"
  65. #include "4965.h"
  66. /*****************************************************************************
  67. * INIT calibrations framework
  68. *****************************************************************************/
  69. struct stats_general_data {
  70. u32 beacon_silence_rssi_a;
  71. u32 beacon_silence_rssi_b;
  72. u32 beacon_silence_rssi_c;
  73. u32 beacon_energy_a;
  74. u32 beacon_energy_b;
  75. u32 beacon_energy_c;
  76. };
  77. void
  78. il4965_calib_free_results(struct il_priv *il)
  79. {
  80. int i;
  81. for (i = 0; i < IL_CALIB_MAX; i++) {
  82. kfree(il->calib_results[i].buf);
  83. il->calib_results[i].buf = NULL;
  84. il->calib_results[i].buf_len = 0;
  85. }
  86. }
  87. /*****************************************************************************
  88. * RUNTIME calibrations framework
  89. *****************************************************************************/
  90. /* "false alarms" are signals that our DSP tries to lock onto,
  91. * but then determines that they are either noise, or transmissions
  92. * from a distant wireless network (also "noise", really) that get
  93. * "stepped on" by stronger transmissions within our own network.
  94. * This algorithm attempts to set a sensitivity level that is high
  95. * enough to receive all of our own network traffic, but not so
  96. * high that our DSP gets too busy trying to lock onto non-network
  97. * activity/noise. */
  98. static int
  99. il4965_sens_energy_cck(struct il_priv *il, u32 norm_fa, u32 rx_enable_time,
  100. struct stats_general_data *rx_info)
  101. {
  102. u32 max_nrg_cck = 0;
  103. int i = 0;
  104. u8 max_silence_rssi = 0;
  105. u32 silence_ref = 0;
  106. u8 silence_rssi_a = 0;
  107. u8 silence_rssi_b = 0;
  108. u8 silence_rssi_c = 0;
  109. u32 val;
  110. /* "false_alarms" values below are cross-multiplications to assess the
  111. * numbers of false alarms within the measured period of actual Rx
  112. * (Rx is off when we're txing), vs the min/max expected false alarms
  113. * (some should be expected if rx is sensitive enough) in a
  114. * hypothetical listening period of 200 time units (TU), 204.8 msec:
  115. *
  116. * MIN_FA/fixed-time < false_alarms/actual-rx-time < MAX_FA/beacon-time
  117. *
  118. * */
  119. u32 false_alarms = norm_fa * 200 * 1024;
  120. u32 max_false_alarms = MAX_FA_CCK * rx_enable_time;
  121. u32 min_false_alarms = MIN_FA_CCK * rx_enable_time;
  122. struct il_sensitivity_data *data = NULL;
  123. const struct il_sensitivity_ranges *ranges = il->hw_params.sens;
  124. data = &(il->sensitivity_data);
  125. data->nrg_auto_corr_silence_diff = 0;
  126. /* Find max silence rssi among all 3 receivers.
  127. * This is background noise, which may include transmissions from other
  128. * networks, measured during silence before our network's beacon */
  129. silence_rssi_a =
  130. (u8) ((rx_info->beacon_silence_rssi_a & ALL_BAND_FILTER) >> 8);
  131. silence_rssi_b =
  132. (u8) ((rx_info->beacon_silence_rssi_b & ALL_BAND_FILTER) >> 8);
  133. silence_rssi_c =
  134. (u8) ((rx_info->beacon_silence_rssi_c & ALL_BAND_FILTER) >> 8);
  135. val = max(silence_rssi_b, silence_rssi_c);
  136. max_silence_rssi = max(silence_rssi_a, (u8) val);
  137. /* Store silence rssi in 20-beacon history table */
  138. data->nrg_silence_rssi[data->nrg_silence_idx] = max_silence_rssi;
  139. data->nrg_silence_idx++;
  140. if (data->nrg_silence_idx >= NRG_NUM_PREV_STAT_L)
  141. data->nrg_silence_idx = 0;
  142. /* Find max silence rssi across 20 beacon history */
  143. for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) {
  144. val = data->nrg_silence_rssi[i];
  145. silence_ref = max(silence_ref, val);
  146. }
  147. D_CALIB("silence a %u, b %u, c %u, 20-bcn max %u\n", silence_rssi_a,
  148. silence_rssi_b, silence_rssi_c, silence_ref);
  149. /* Find max rx energy (min value!) among all 3 receivers,
  150. * measured during beacon frame.
  151. * Save it in 10-beacon history table. */
  152. i = data->nrg_energy_idx;
  153. val = min(rx_info->beacon_energy_b, rx_info->beacon_energy_c);
  154. data->nrg_value[i] = min(rx_info->beacon_energy_a, val);
  155. data->nrg_energy_idx++;
  156. if (data->nrg_energy_idx >= 10)
  157. data->nrg_energy_idx = 0;
  158. /* Find min rx energy (max value) across 10 beacon history.
  159. * This is the minimum signal level that we want to receive well.
  160. * Add backoff (margin so we don't miss slightly lower energy frames).
  161. * This establishes an upper bound (min value) for energy threshold. */
  162. max_nrg_cck = data->nrg_value[0];
  163. for (i = 1; i < 10; i++)
  164. max_nrg_cck = (u32) max(max_nrg_cck, (data->nrg_value[i]));
  165. max_nrg_cck += 6;
  166. D_CALIB("rx energy a %u, b %u, c %u, 10-bcn max/min %u\n",
  167. rx_info->beacon_energy_a, rx_info->beacon_energy_b,
  168. rx_info->beacon_energy_c, max_nrg_cck - 6);
  169. /* Count number of consecutive beacons with fewer-than-desired
  170. * false alarms. */
  171. if (false_alarms < min_false_alarms)
  172. data->num_in_cck_no_fa++;
  173. else
  174. data->num_in_cck_no_fa = 0;
  175. D_CALIB("consecutive bcns with few false alarms = %u\n",
  176. data->num_in_cck_no_fa);
  177. /* If we got too many false alarms this time, reduce sensitivity */
  178. if (false_alarms > max_false_alarms &&
  179. data->auto_corr_cck > AUTO_CORR_MAX_TH_CCK) {
  180. D_CALIB("norm FA %u > max FA %u\n", false_alarms,
  181. max_false_alarms);
  182. D_CALIB("... reducing sensitivity\n");
  183. data->nrg_curr_state = IL_FA_TOO_MANY;
  184. /* Store for "fewer than desired" on later beacon */
  185. data->nrg_silence_ref = silence_ref;
  186. /* increase energy threshold (reduce nrg value)
  187. * to decrease sensitivity */
  188. data->nrg_th_cck = data->nrg_th_cck - NRG_STEP_CCK;
  189. /* Else if we got fewer than desired, increase sensitivity */
  190. } else if (false_alarms < min_false_alarms) {
  191. data->nrg_curr_state = IL_FA_TOO_FEW;
  192. /* Compare silence level with silence level for most recent
  193. * healthy number or too many false alarms */
  194. data->nrg_auto_corr_silence_diff =
  195. (s32) data->nrg_silence_ref - (s32) silence_ref;
  196. D_CALIB("norm FA %u < min FA %u, silence diff %d\n",
  197. false_alarms, min_false_alarms,
  198. data->nrg_auto_corr_silence_diff);
  199. /* Increase value to increase sensitivity, but only if:
  200. * 1a) previous beacon did *not* have *too many* false alarms
  201. * 1b) AND there's a significant difference in Rx levels
  202. * from a previous beacon with too many, or healthy # FAs
  203. * OR 2) We've seen a lot of beacons (100) with too few
  204. * false alarms */
  205. if (data->nrg_prev_state != IL_FA_TOO_MANY &&
  206. (data->nrg_auto_corr_silence_diff > NRG_DIFF ||
  207. data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA)) {
  208. D_CALIB("... increasing sensitivity\n");
  209. /* Increase nrg value to increase sensitivity */
  210. val = data->nrg_th_cck + NRG_STEP_CCK;
  211. data->nrg_th_cck = min((u32) ranges->min_nrg_cck, val);
  212. } else {
  213. D_CALIB("... but not changing sensitivity\n");
  214. }
  215. /* Else we got a healthy number of false alarms, keep status quo */
  216. } else {
  217. D_CALIB(" FA in safe zone\n");
  218. data->nrg_curr_state = IL_FA_GOOD_RANGE;
  219. /* Store for use in "fewer than desired" with later beacon */
  220. data->nrg_silence_ref = silence_ref;
  221. /* If previous beacon had too many false alarms,
  222. * give it some extra margin by reducing sensitivity again
  223. * (but don't go below measured energy of desired Rx) */
  224. if (IL_FA_TOO_MANY == data->nrg_prev_state) {
  225. D_CALIB("... increasing margin\n");
  226. if (data->nrg_th_cck > (max_nrg_cck + NRG_MARGIN))
  227. data->nrg_th_cck -= NRG_MARGIN;
  228. else
  229. data->nrg_th_cck = max_nrg_cck;
  230. }
  231. }
  232. /* Make sure the energy threshold does not go above the measured
  233. * energy of the desired Rx signals (reduced by backoff margin),
  234. * or else we might start missing Rx frames.
  235. * Lower value is higher energy, so we use max()!
  236. */
  237. data->nrg_th_cck = max(max_nrg_cck, data->nrg_th_cck);
  238. D_CALIB("new nrg_th_cck %u\n", data->nrg_th_cck);
  239. data->nrg_prev_state = data->nrg_curr_state;
  240. /* Auto-correlation CCK algorithm */
  241. if (false_alarms > min_false_alarms) {
  242. /* increase auto_corr values to decrease sensitivity
  243. * so the DSP won't be disturbed by the noise
  244. */
  245. if (data->auto_corr_cck < AUTO_CORR_MAX_TH_CCK)
  246. data->auto_corr_cck = AUTO_CORR_MAX_TH_CCK + 1;
  247. else {
  248. val = data->auto_corr_cck + AUTO_CORR_STEP_CCK;
  249. data->auto_corr_cck =
  250. min((u32) ranges->auto_corr_max_cck, val);
  251. }
  252. val = data->auto_corr_cck_mrc + AUTO_CORR_STEP_CCK;
  253. data->auto_corr_cck_mrc =
  254. min((u32) ranges->auto_corr_max_cck_mrc, val);
  255. } else if (false_alarms < min_false_alarms &&
  256. (data->nrg_auto_corr_silence_diff > NRG_DIFF ||
  257. data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA)) {
  258. /* Decrease auto_corr values to increase sensitivity */
  259. val = data->auto_corr_cck - AUTO_CORR_STEP_CCK;
  260. data->auto_corr_cck = max((u32) ranges->auto_corr_min_cck, val);
  261. val = data->auto_corr_cck_mrc - AUTO_CORR_STEP_CCK;
  262. data->auto_corr_cck_mrc =
  263. max((u32) ranges->auto_corr_min_cck_mrc, val);
  264. }
  265. return 0;
  266. }
  267. static int
  268. il4965_sens_auto_corr_ofdm(struct il_priv *il, u32 norm_fa, u32 rx_enable_time)
  269. {
  270. u32 val;
  271. u32 false_alarms = norm_fa * 200 * 1024;
  272. u32 max_false_alarms = MAX_FA_OFDM * rx_enable_time;
  273. u32 min_false_alarms = MIN_FA_OFDM * rx_enable_time;
  274. struct il_sensitivity_data *data = NULL;
  275. const struct il_sensitivity_ranges *ranges = il->hw_params.sens;
  276. data = &(il->sensitivity_data);
  277. /* If we got too many false alarms this time, reduce sensitivity */
  278. if (false_alarms > max_false_alarms) {
  279. D_CALIB("norm FA %u > max FA %u)\n", false_alarms,
  280. max_false_alarms);
  281. val = data->auto_corr_ofdm + AUTO_CORR_STEP_OFDM;
  282. data->auto_corr_ofdm =
  283. min((u32) ranges->auto_corr_max_ofdm, val);
  284. val = data->auto_corr_ofdm_mrc + AUTO_CORR_STEP_OFDM;
  285. data->auto_corr_ofdm_mrc =
  286. min((u32) ranges->auto_corr_max_ofdm_mrc, val);
  287. val = data->auto_corr_ofdm_x1 + AUTO_CORR_STEP_OFDM;
  288. data->auto_corr_ofdm_x1 =
  289. min((u32) ranges->auto_corr_max_ofdm_x1, val);
  290. val = data->auto_corr_ofdm_mrc_x1 + AUTO_CORR_STEP_OFDM;
  291. data->auto_corr_ofdm_mrc_x1 =
  292. min((u32) ranges->auto_corr_max_ofdm_mrc_x1, val);
  293. }
  294. /* Else if we got fewer than desired, increase sensitivity */
  295. else if (false_alarms < min_false_alarms) {
  296. D_CALIB("norm FA %u < min FA %u\n", false_alarms,
  297. min_false_alarms);
  298. val = data->auto_corr_ofdm - AUTO_CORR_STEP_OFDM;
  299. data->auto_corr_ofdm =
  300. max((u32) ranges->auto_corr_min_ofdm, val);
  301. val = data->auto_corr_ofdm_mrc - AUTO_CORR_STEP_OFDM;
  302. data->auto_corr_ofdm_mrc =
  303. max((u32) ranges->auto_corr_min_ofdm_mrc, val);
  304. val = data->auto_corr_ofdm_x1 - AUTO_CORR_STEP_OFDM;
  305. data->auto_corr_ofdm_x1 =
  306. max((u32) ranges->auto_corr_min_ofdm_x1, val);
  307. val = data->auto_corr_ofdm_mrc_x1 - AUTO_CORR_STEP_OFDM;
  308. data->auto_corr_ofdm_mrc_x1 =
  309. max((u32) ranges->auto_corr_min_ofdm_mrc_x1, val);
  310. } else {
  311. D_CALIB("min FA %u < norm FA %u < max FA %u OK\n",
  312. min_false_alarms, false_alarms, max_false_alarms);
  313. }
  314. return 0;
  315. }
  316. static void
  317. il4965_prepare_legacy_sensitivity_tbl(struct il_priv *il,
  318. struct il_sensitivity_data *data,
  319. __le16 *tbl)
  320. {
  321. tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_IDX] =
  322. cpu_to_le16((u16) data->auto_corr_ofdm);
  323. tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_IDX] =
  324. cpu_to_le16((u16) data->auto_corr_ofdm_mrc);
  325. tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_IDX] =
  326. cpu_to_le16((u16) data->auto_corr_ofdm_x1);
  327. tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_IDX] =
  328. cpu_to_le16((u16) data->auto_corr_ofdm_mrc_x1);
  329. tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_IDX] =
  330. cpu_to_le16((u16) data->auto_corr_cck);
  331. tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_IDX] =
  332. cpu_to_le16((u16) data->auto_corr_cck_mrc);
  333. tbl[HD_MIN_ENERGY_CCK_DET_IDX] = cpu_to_le16((u16) data->nrg_th_cck);
  334. tbl[HD_MIN_ENERGY_OFDM_DET_IDX] = cpu_to_le16((u16) data->nrg_th_ofdm);
  335. tbl[HD_BARKER_CORR_TH_ADD_MIN_IDX] =
  336. cpu_to_le16(data->barker_corr_th_min);
  337. tbl[HD_BARKER_CORR_TH_ADD_MIN_MRC_IDX] =
  338. cpu_to_le16(data->barker_corr_th_min_mrc);
  339. tbl[HD_OFDM_ENERGY_TH_IN_IDX] = cpu_to_le16(data->nrg_th_cca);
  340. D_CALIB("ofdm: ac %u mrc %u x1 %u mrc_x1 %u thresh %u\n",
  341. data->auto_corr_ofdm, data->auto_corr_ofdm_mrc,
  342. data->auto_corr_ofdm_x1, data->auto_corr_ofdm_mrc_x1,
  343. data->nrg_th_ofdm);
  344. D_CALIB("cck: ac %u mrc %u thresh %u\n", data->auto_corr_cck,
  345. data->auto_corr_cck_mrc, data->nrg_th_cck);
  346. }
  347. /* Prepare a C_SENSITIVITY, send to uCode if values have changed */
  348. static int
  349. il4965_sensitivity_write(struct il_priv *il)
  350. {
  351. struct il_sensitivity_cmd cmd;
  352. struct il_sensitivity_data *data = NULL;
  353. struct il_host_cmd cmd_out = {
  354. .id = C_SENSITIVITY,
  355. .len = sizeof(struct il_sensitivity_cmd),
  356. .flags = CMD_ASYNC,
  357. .data = &cmd,
  358. };
  359. data = &(il->sensitivity_data);
  360. memset(&cmd, 0, sizeof(cmd));
  361. il4965_prepare_legacy_sensitivity_tbl(il, data, &cmd.table[0]);
  362. /* Update uCode's "work" table, and copy it to DSP */
  363. cmd.control = C_SENSITIVITY_CONTROL_WORK_TBL;
  364. /* Don't send command to uCode if nothing has changed */
  365. if (!memcmp
  366. (&cmd.table[0], &(il->sensitivity_tbl[0]),
  367. sizeof(u16) * HD_TBL_SIZE)) {
  368. D_CALIB("No change in C_SENSITIVITY\n");
  369. return 0;
  370. }
  371. /* Copy table for comparison next time */
  372. memcpy(&(il->sensitivity_tbl[0]), &(cmd.table[0]),
  373. sizeof(u16) * HD_TBL_SIZE);
  374. return il_send_cmd(il, &cmd_out);
  375. }
  376. void
  377. il4965_init_sensitivity(struct il_priv *il)
  378. {
  379. int ret = 0;
  380. int i;
  381. struct il_sensitivity_data *data = NULL;
  382. const struct il_sensitivity_ranges *ranges = il->hw_params.sens;
  383. if (il->disable_sens_cal)
  384. return;
  385. D_CALIB("Start il4965_init_sensitivity\n");
  386. /* Clear driver's sensitivity algo data */
  387. data = &(il->sensitivity_data);
  388. if (ranges == NULL)
  389. return;
  390. memset(data, 0, sizeof(struct il_sensitivity_data));
  391. data->num_in_cck_no_fa = 0;
  392. data->nrg_curr_state = IL_FA_TOO_MANY;
  393. data->nrg_prev_state = IL_FA_TOO_MANY;
  394. data->nrg_silence_ref = 0;
  395. data->nrg_silence_idx = 0;
  396. data->nrg_energy_idx = 0;
  397. for (i = 0; i < 10; i++)
  398. data->nrg_value[i] = 0;
  399. for (i = 0; i < NRG_NUM_PREV_STAT_L; i++)
  400. data->nrg_silence_rssi[i] = 0;
  401. data->auto_corr_ofdm = ranges->auto_corr_min_ofdm;
  402. data->auto_corr_ofdm_mrc = ranges->auto_corr_min_ofdm_mrc;
  403. data->auto_corr_ofdm_x1 = ranges->auto_corr_min_ofdm_x1;
  404. data->auto_corr_ofdm_mrc_x1 = ranges->auto_corr_min_ofdm_mrc_x1;
  405. data->auto_corr_cck = AUTO_CORR_CCK_MIN_VAL_DEF;
  406. data->auto_corr_cck_mrc = ranges->auto_corr_min_cck_mrc;
  407. data->nrg_th_cck = ranges->nrg_th_cck;
  408. data->nrg_th_ofdm = ranges->nrg_th_ofdm;
  409. data->barker_corr_th_min = ranges->barker_corr_th_min;
  410. data->barker_corr_th_min_mrc = ranges->barker_corr_th_min_mrc;
  411. data->nrg_th_cca = ranges->nrg_th_cca;
  412. data->last_bad_plcp_cnt_ofdm = 0;
  413. data->last_fa_cnt_ofdm = 0;
  414. data->last_bad_plcp_cnt_cck = 0;
  415. data->last_fa_cnt_cck = 0;
  416. ret |= il4965_sensitivity_write(il);
  417. D_CALIB("<<return 0x%X\n", ret);
  418. }
  419. void
  420. il4965_sensitivity_calibration(struct il_priv *il, void *resp)
  421. {
  422. u32 rx_enable_time;
  423. u32 fa_cck;
  424. u32 fa_ofdm;
  425. u32 bad_plcp_cck;
  426. u32 bad_plcp_ofdm;
  427. u32 norm_fa_ofdm;
  428. u32 norm_fa_cck;
  429. struct il_sensitivity_data *data = NULL;
  430. struct stats_rx_non_phy *rx_info;
  431. struct stats_rx_phy *ofdm, *cck;
  432. unsigned long flags;
  433. struct stats_general_data statis;
  434. if (il->disable_sens_cal)
  435. return;
  436. data = &(il->sensitivity_data);
  437. if (!il_is_any_associated(il)) {
  438. D_CALIB("<< - not associated\n");
  439. return;
  440. }
  441. spin_lock_irqsave(&il->lock, flags);
  442. rx_info = &(((struct il_notif_stats *)resp)->rx.general);
  443. ofdm = &(((struct il_notif_stats *)resp)->rx.ofdm);
  444. cck = &(((struct il_notif_stats *)resp)->rx.cck);
  445. if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) {
  446. D_CALIB("<< invalid data.\n");
  447. spin_unlock_irqrestore(&il->lock, flags);
  448. return;
  449. }
  450. /* Extract Statistics: */
  451. rx_enable_time = le32_to_cpu(rx_info->channel_load);
  452. fa_cck = le32_to_cpu(cck->false_alarm_cnt);
  453. fa_ofdm = le32_to_cpu(ofdm->false_alarm_cnt);
  454. bad_plcp_cck = le32_to_cpu(cck->plcp_err);
  455. bad_plcp_ofdm = le32_to_cpu(ofdm->plcp_err);
  456. statis.beacon_silence_rssi_a =
  457. le32_to_cpu(rx_info->beacon_silence_rssi_a);
  458. statis.beacon_silence_rssi_b =
  459. le32_to_cpu(rx_info->beacon_silence_rssi_b);
  460. statis.beacon_silence_rssi_c =
  461. le32_to_cpu(rx_info->beacon_silence_rssi_c);
  462. statis.beacon_energy_a = le32_to_cpu(rx_info->beacon_energy_a);
  463. statis.beacon_energy_b = le32_to_cpu(rx_info->beacon_energy_b);
  464. statis.beacon_energy_c = le32_to_cpu(rx_info->beacon_energy_c);
  465. spin_unlock_irqrestore(&il->lock, flags);
  466. D_CALIB("rx_enable_time = %u usecs\n", rx_enable_time);
  467. if (!rx_enable_time) {
  468. D_CALIB("<< RX Enable Time == 0!\n");
  469. return;
  470. }
  471. /* These stats increase monotonically, and do not reset
  472. * at each beacon. Calculate difference from last value, or just
  473. * use the new stats value if it has reset or wrapped around. */
  474. if (data->last_bad_plcp_cnt_cck > bad_plcp_cck)
  475. data->last_bad_plcp_cnt_cck = bad_plcp_cck;
  476. else {
  477. bad_plcp_cck -= data->last_bad_plcp_cnt_cck;
  478. data->last_bad_plcp_cnt_cck += bad_plcp_cck;
  479. }
  480. if (data->last_bad_plcp_cnt_ofdm > bad_plcp_ofdm)
  481. data->last_bad_plcp_cnt_ofdm = bad_plcp_ofdm;
  482. else {
  483. bad_plcp_ofdm -= data->last_bad_plcp_cnt_ofdm;
  484. data->last_bad_plcp_cnt_ofdm += bad_plcp_ofdm;
  485. }
  486. if (data->last_fa_cnt_ofdm > fa_ofdm)
  487. data->last_fa_cnt_ofdm = fa_ofdm;
  488. else {
  489. fa_ofdm -= data->last_fa_cnt_ofdm;
  490. data->last_fa_cnt_ofdm += fa_ofdm;
  491. }
  492. if (data->last_fa_cnt_cck > fa_cck)
  493. data->last_fa_cnt_cck = fa_cck;
  494. else {
  495. fa_cck -= data->last_fa_cnt_cck;
  496. data->last_fa_cnt_cck += fa_cck;
  497. }
  498. /* Total aborted signal locks */
  499. norm_fa_ofdm = fa_ofdm + bad_plcp_ofdm;
  500. norm_fa_cck = fa_cck + bad_plcp_cck;
  501. D_CALIB("cck: fa %u badp %u ofdm: fa %u badp %u\n", fa_cck,
  502. bad_plcp_cck, fa_ofdm, bad_plcp_ofdm);
  503. il4965_sens_auto_corr_ofdm(il, norm_fa_ofdm, rx_enable_time);
  504. il4965_sens_energy_cck(il, norm_fa_cck, rx_enable_time, &statis);
  505. il4965_sensitivity_write(il);
  506. }
  507. static inline u8
  508. il4965_find_first_chain(u8 mask)
  509. {
  510. if (mask & ANT_A)
  511. return CHAIN_A;
  512. if (mask & ANT_B)
  513. return CHAIN_B;
  514. return CHAIN_C;
  515. }
  516. /**
  517. * Run disconnected antenna algorithm to find out which antennas are
  518. * disconnected.
  519. */
  520. static void
  521. il4965_find_disconn_antenna(struct il_priv *il, u32 * average_sig,
  522. struct il_chain_noise_data *data)
  523. {
  524. u32 active_chains = 0;
  525. u32 max_average_sig;
  526. u16 max_average_sig_antenna_i;
  527. u8 num_tx_chains;
  528. u8 first_chain;
  529. u16 i = 0;
  530. average_sig[0] =
  531. data->chain_signal_a /
  532. il->cfg->base_params->chain_noise_num_beacons;
  533. average_sig[1] =
  534. data->chain_signal_b /
  535. il->cfg->base_params->chain_noise_num_beacons;
  536. average_sig[2] =
  537. data->chain_signal_c /
  538. il->cfg->base_params->chain_noise_num_beacons;
  539. if (average_sig[0] >= average_sig[1]) {
  540. max_average_sig = average_sig[0];
  541. max_average_sig_antenna_i = 0;
  542. active_chains = (1 << max_average_sig_antenna_i);
  543. } else {
  544. max_average_sig = average_sig[1];
  545. max_average_sig_antenna_i = 1;
  546. active_chains = (1 << max_average_sig_antenna_i);
  547. }
  548. if (average_sig[2] >= max_average_sig) {
  549. max_average_sig = average_sig[2];
  550. max_average_sig_antenna_i = 2;
  551. active_chains = (1 << max_average_sig_antenna_i);
  552. }
  553. D_CALIB("average_sig: a %d b %d c %d\n", average_sig[0], average_sig[1],
  554. average_sig[2]);
  555. D_CALIB("max_average_sig = %d, antenna %d\n", max_average_sig,
  556. max_average_sig_antenna_i);
  557. /* Compare signal strengths for all 3 receivers. */
  558. for (i = 0; i < NUM_RX_CHAINS; i++) {
  559. if (i != max_average_sig_antenna_i) {
  560. s32 rssi_delta = (max_average_sig - average_sig[i]);
  561. /* If signal is very weak, compared with
  562. * strongest, mark it as disconnected. */
  563. if (rssi_delta > MAXIMUM_ALLOWED_PATHLOSS)
  564. data->disconn_array[i] = 1;
  565. else
  566. active_chains |= (1 << i);
  567. D_CALIB("i = %d rssiDelta = %d "
  568. "disconn_array[i] = %d\n", i, rssi_delta,
  569. data->disconn_array[i]);
  570. }
  571. }
  572. /*
  573. * The above algorithm sometimes fails when the ucode
  574. * reports 0 for all chains. It's not clear why that
  575. * happens to start with, but it is then causing trouble
  576. * because this can make us enable more chains than the
  577. * hardware really has.
  578. *
  579. * To be safe, simply mask out any chains that we know
  580. * are not on the device.
  581. */
  582. active_chains &= il->hw_params.valid_rx_ant;
  583. num_tx_chains = 0;
  584. for (i = 0; i < NUM_RX_CHAINS; i++) {
  585. /* loops on all the bits of
  586. * il->hw_setting.valid_tx_ant */
  587. u8 ant_msk = (1 << i);
  588. if (!(il->hw_params.valid_tx_ant & ant_msk))
  589. continue;
  590. num_tx_chains++;
  591. if (data->disconn_array[i] == 0)
  592. /* there is a Tx antenna connected */
  593. break;
  594. if (num_tx_chains == il->hw_params.tx_chains_num &&
  595. data->disconn_array[i]) {
  596. /*
  597. * If all chains are disconnected
  598. * connect the first valid tx chain
  599. */
  600. first_chain =
  601. il4965_find_first_chain(il->cfg->valid_tx_ant);
  602. data->disconn_array[first_chain] = 0;
  603. active_chains |= BIT(first_chain);
  604. D_CALIB("All Tx chains are disconnected"
  605. "- declare %d as connected\n", first_chain);
  606. break;
  607. }
  608. }
  609. if (active_chains != il->hw_params.valid_rx_ant &&
  610. active_chains != il->chain_noise_data.active_chains)
  611. D_CALIB("Detected that not all antennas are connected! "
  612. "Connected: %#x, valid: %#x.\n", active_chains,
  613. il->hw_params.valid_rx_ant);
  614. /* Save for use within RXON, TX, SCAN commands, etc. */
  615. data->active_chains = active_chains;
  616. D_CALIB("active_chains (bitwise) = 0x%x\n", active_chains);
  617. }
  618. static void
  619. il4965_gain_computation(struct il_priv *il, u32 * average_noise,
  620. u16 min_average_noise_antenna_i, u32 min_average_noise,
  621. u8 default_chain)
  622. {
  623. int i, ret;
  624. struct il_chain_noise_data *data = &il->chain_noise_data;
  625. data->delta_gain_code[min_average_noise_antenna_i] = 0;
  626. for (i = default_chain; i < NUM_RX_CHAINS; i++) {
  627. s32 delta_g = 0;
  628. if (!data->disconn_array[i] &&
  629. data->delta_gain_code[i] ==
  630. CHAIN_NOISE_DELTA_GAIN_INIT_VAL) {
  631. delta_g = average_noise[i] - min_average_noise;
  632. data->delta_gain_code[i] = (u8) ((delta_g * 10) / 15);
  633. data->delta_gain_code[i] =
  634. min(data->delta_gain_code[i],
  635. (u8) CHAIN_NOISE_MAX_DELTA_GAIN_CODE);
  636. data->delta_gain_code[i] =
  637. (data->delta_gain_code[i] | (1 << 2));
  638. } else {
  639. data->delta_gain_code[i] = 0;
  640. }
  641. }
  642. D_CALIB("delta_gain_codes: a %d b %d c %d\n", data->delta_gain_code[0],
  643. data->delta_gain_code[1], data->delta_gain_code[2]);
  644. /* Differential gain gets sent to uCode only once */
  645. if (!data->radio_write) {
  646. struct il_calib_diff_gain_cmd cmd;
  647. data->radio_write = 1;
  648. memset(&cmd, 0, sizeof(cmd));
  649. cmd.hdr.op_code = IL_PHY_CALIBRATE_DIFF_GAIN_CMD;
  650. cmd.diff_gain_a = data->delta_gain_code[0];
  651. cmd.diff_gain_b = data->delta_gain_code[1];
  652. cmd.diff_gain_c = data->delta_gain_code[2];
  653. ret = il_send_cmd_pdu(il, C_PHY_CALIBRATION, sizeof(cmd), &cmd);
  654. if (ret)
  655. D_CALIB("fail sending cmd " "C_PHY_CALIBRATION\n");
  656. /* TODO we might want recalculate
  657. * rx_chain in rxon cmd */
  658. /* Mark so we run this algo only once! */
  659. data->state = IL_CHAIN_NOISE_CALIBRATED;
  660. }
  661. }
  662. /*
  663. * Accumulate 16 beacons of signal and noise stats for each of
  664. * 3 receivers/antennas/rx-chains, then figure out:
  665. * 1) Which antennas are connected.
  666. * 2) Differential rx gain settings to balance the 3 receivers.
  667. */
  668. void
  669. il4965_chain_noise_calibration(struct il_priv *il, void *stat_resp)
  670. {
  671. struct il_chain_noise_data *data = NULL;
  672. u32 chain_noise_a;
  673. u32 chain_noise_b;
  674. u32 chain_noise_c;
  675. u32 chain_sig_a;
  676. u32 chain_sig_b;
  677. u32 chain_sig_c;
  678. u32 average_sig[NUM_RX_CHAINS] = { INITIALIZATION_VALUE };
  679. u32 average_noise[NUM_RX_CHAINS] = { INITIALIZATION_VALUE };
  680. u32 min_average_noise = MIN_AVERAGE_NOISE_MAX_VALUE;
  681. u16 min_average_noise_antenna_i = INITIALIZATION_VALUE;
  682. u16 i = 0;
  683. u16 rxon_chnum = INITIALIZATION_VALUE;
  684. u16 stat_chnum = INITIALIZATION_VALUE;
  685. u8 rxon_band24;
  686. u8 stat_band24;
  687. unsigned long flags;
  688. struct stats_rx_non_phy *rx_info;
  689. struct il_rxon_context *ctx = &il->ctx;
  690. if (il->disable_chain_noise_cal)
  691. return;
  692. data = &(il->chain_noise_data);
  693. /*
  694. * Accumulate just the first "chain_noise_num_beacons" after
  695. * the first association, then we're done forever.
  696. */
  697. if (data->state != IL_CHAIN_NOISE_ACCUMULATE) {
  698. if (data->state == IL_CHAIN_NOISE_ALIVE)
  699. D_CALIB("Wait for noise calib reset\n");
  700. return;
  701. }
  702. spin_lock_irqsave(&il->lock, flags);
  703. rx_info = &(((struct il_notif_stats *)stat_resp)->rx.general);
  704. if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) {
  705. D_CALIB(" << Interference data unavailable\n");
  706. spin_unlock_irqrestore(&il->lock, flags);
  707. return;
  708. }
  709. rxon_band24 = !!(ctx->staging.flags & RXON_FLG_BAND_24G_MSK);
  710. rxon_chnum = le16_to_cpu(ctx->staging.channel);
  711. stat_band24 =
  712. !!(((struct il_notif_stats *)stat_resp)->
  713. flag & STATS_REPLY_FLG_BAND_24G_MSK);
  714. stat_chnum =
  715. le32_to_cpu(((struct il_notif_stats *)stat_resp)->flag) >> 16;
  716. /* Make sure we accumulate data for just the associated channel
  717. * (even if scanning). */
  718. if (rxon_chnum != stat_chnum || rxon_band24 != stat_band24) {
  719. D_CALIB("Stats not from chan=%d, band24=%d\n", rxon_chnum,
  720. rxon_band24);
  721. spin_unlock_irqrestore(&il->lock, flags);
  722. return;
  723. }
  724. /*
  725. * Accumulate beacon stats values across
  726. * "chain_noise_num_beacons"
  727. */
  728. chain_noise_a =
  729. le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER;
  730. chain_noise_b =
  731. le32_to_cpu(rx_info->beacon_silence_rssi_b) & IN_BAND_FILTER;
  732. chain_noise_c =
  733. le32_to_cpu(rx_info->beacon_silence_rssi_c) & IN_BAND_FILTER;
  734. chain_sig_a = le32_to_cpu(rx_info->beacon_rssi_a) & IN_BAND_FILTER;
  735. chain_sig_b = le32_to_cpu(rx_info->beacon_rssi_b) & IN_BAND_FILTER;
  736. chain_sig_c = le32_to_cpu(rx_info->beacon_rssi_c) & IN_BAND_FILTER;
  737. spin_unlock_irqrestore(&il->lock, flags);
  738. data->beacon_count++;
  739. data->chain_noise_a = (chain_noise_a + data->chain_noise_a);
  740. data->chain_noise_b = (chain_noise_b + data->chain_noise_b);
  741. data->chain_noise_c = (chain_noise_c + data->chain_noise_c);
  742. data->chain_signal_a = (chain_sig_a + data->chain_signal_a);
  743. data->chain_signal_b = (chain_sig_b + data->chain_signal_b);
  744. data->chain_signal_c = (chain_sig_c + data->chain_signal_c);
  745. D_CALIB("chan=%d, band24=%d, beacon=%d\n", rxon_chnum, rxon_band24,
  746. data->beacon_count);
  747. D_CALIB("chain_sig: a %d b %d c %d\n", chain_sig_a, chain_sig_b,
  748. chain_sig_c);
  749. D_CALIB("chain_noise: a %d b %d c %d\n", chain_noise_a, chain_noise_b,
  750. chain_noise_c);
  751. /* If this is the "chain_noise_num_beacons", determine:
  752. * 1) Disconnected antennas (using signal strengths)
  753. * 2) Differential gain (using silence noise) to balance receivers */
  754. if (data->beacon_count != il->cfg->base_params->chain_noise_num_beacons)
  755. return;
  756. /* Analyze signal for disconnected antenna */
  757. il4965_find_disconn_antenna(il, average_sig, data);
  758. /* Analyze noise for rx balance */
  759. average_noise[0] =
  760. data->chain_noise_a / il->cfg->base_params->chain_noise_num_beacons;
  761. average_noise[1] =
  762. data->chain_noise_b / il->cfg->base_params->chain_noise_num_beacons;
  763. average_noise[2] =
  764. data->chain_noise_c / il->cfg->base_params->chain_noise_num_beacons;
  765. for (i = 0; i < NUM_RX_CHAINS; i++) {
  766. if (!data->disconn_array[i] &&
  767. average_noise[i] <= min_average_noise) {
  768. /* This means that chain i is active and has
  769. * lower noise values so far: */
  770. min_average_noise = average_noise[i];
  771. min_average_noise_antenna_i = i;
  772. }
  773. }
  774. D_CALIB("average_noise: a %d b %d c %d\n", average_noise[0],
  775. average_noise[1], average_noise[2]);
  776. D_CALIB("min_average_noise = %d, antenna %d\n", min_average_noise,
  777. min_average_noise_antenna_i);
  778. il4965_gain_computation(il, average_noise, min_average_noise_antenna_i,
  779. min_average_noise,
  780. il4965_find_first_chain(il->cfg->valid_rx_ant));
  781. /* Some power changes may have been made during the calibration.
  782. * Update and commit the RXON
  783. */
  784. if (il->cfg->ops->lib->update_chain_flags)
  785. il->cfg->ops->lib->update_chain_flags(il);
  786. data->state = IL_CHAIN_NOISE_DONE;
  787. il_power_update_mode(il, false);
  788. }
  789. void
  790. il4965_reset_run_time_calib(struct il_priv *il)
  791. {
  792. int i;
  793. memset(&(il->sensitivity_data), 0, sizeof(struct il_sensitivity_data));
  794. memset(&(il->chain_noise_data), 0, sizeof(struct il_chain_noise_data));
  795. for (i = 0; i < NUM_RX_CHAINS; i++)
  796. il->chain_noise_data.delta_gain_code[i] =
  797. CHAIN_NOISE_DELTA_GAIN_INIT_VAL;
  798. /* Ask for stats now, the uCode will send notification
  799. * periodically after association */
  800. il_send_stats_request(il, CMD_ASYNC, true);
  801. }