1 /* 2 * Copyright (C) 2010 Bruno Randolf <br1@einfach.org> 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 #include "ath5k.h" 18 #include "reg.h" 19 #include "debug.h" 20 #include "ani.h" 21 22 /** 23 * DOC: Basic ANI Operation 24 * 25 * Adaptive Noise Immunity (ANI) controls five noise immunity parameters 26 * depending on the amount of interference in the environment, increasing 27 * or reducing sensitivity as necessary. 28 * 29 * The parameters are: 30 * 31 * - "noise immunity" 32 * 33 * - "spur immunity" 34 * 35 * - "firstep level" 36 * 37 * - "OFDM weak signal detection" 38 * 39 * - "CCK weak signal detection" 40 * 41 * Basically we look at the amount of ODFM and CCK timing errors we get and then 42 * raise or lower immunity accordingly by setting one or more of these 43 * parameters. 44 * 45 * Newer chipsets have PHY error counters in hardware which will generate a MIB 46 * interrupt when they overflow. Older hardware has too enable PHY error frames 47 * by setting a RX flag and then count every single PHY error. When a specified 48 * threshold of errors has been reached we will raise immunity. 49 * Also we regularly check the amount of errors and lower or raise immunity as 50 * necessary. 51 */ 52 53 54 /***********************\ 55 * ANI parameter control * 56 \***********************/ 57 58 /** 59 * ath5k_ani_set_noise_immunity_level() - Set noise immunity level 60 * @ah: The &struct ath5k_hw 61 * @level: level between 0 and @ATH5K_ANI_MAX_NOISE_IMM_LVL 62 */ 63 void 64 ath5k_ani_set_noise_immunity_level(struct ath5k_hw *ah, int level) 65 { 66 /* TODO: 67 * ANI documents suggest the following five levels to use, but the HAL 68 * and ath9k use only the last two levels, making this 69 * essentially an on/off option. There *may* be a reason for this (???), 70 * so i stick with the HAL version for now... 71 */ 72 #if 0 73 static const s8 lo[] = { -52, -56, -60, -64, -70 }; 74 static const s8 hi[] = { -18, -18, -16, -14, -12 }; 75 static const s8 sz[] = { -34, -41, -48, -55, -62 }; 76 static const s8 fr[] = { -70, -72, -75, -78, -80 }; 77 #else 78 static const s8 lo[] = { -64, -70 }; 79 static const s8 hi[] = { -14, -12 }; 80 static const s8 sz[] = { -55, -62 }; 81 static const s8 fr[] = { -78, -80 }; 82 #endif 83 if (level < 0 || level >= ARRAY_SIZE(sz)) { 84 ATH5K_ERR(ah, "noise immunity level %d out of range", 85 level); 86 return; 87 } 88 89 AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DESIRED_SIZE, 90 AR5K_PHY_DESIRED_SIZE_TOT, sz[level]); 91 AR5K_REG_WRITE_BITS(ah, AR5K_PHY_AGCCOARSE, 92 AR5K_PHY_AGCCOARSE_LO, lo[level]); 93 AR5K_REG_WRITE_BITS(ah, AR5K_PHY_AGCCOARSE, 94 AR5K_PHY_AGCCOARSE_HI, hi[level]); 95 AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SIG, 96 AR5K_PHY_SIG_FIRPWR, fr[level]); 97 98 ah->ani_state.noise_imm_level = level; 99 ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level); 100 } 101 102 /** 103 * ath5k_ani_set_spur_immunity_level() - Set spur immunity level 104 * @ah: The &struct ath5k_hw 105 * @level: level between 0 and @max_spur_level (the maximum level is dependent 106 * on the chip revision). 107 */ 108 void 109 ath5k_ani_set_spur_immunity_level(struct ath5k_hw *ah, int level) 110 { 111 static const int val[] = { 2, 4, 6, 8, 10, 12, 14, 16 }; 112 113 if (level < 0 || level >= ARRAY_SIZE(val) || 114 level > ah->ani_state.max_spur_level) { 115 ATH5K_ERR(ah, "spur immunity level %d out of range", 116 level); 117 return; 118 } 119 120 AR5K_REG_WRITE_BITS(ah, AR5K_PHY_OFDM_SELFCORR, 121 AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1, val[level]); 122 123 ah->ani_state.spur_level = level; 124 ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level); 125 } 126 127 /** 128 * ath5k_ani_set_firstep_level() - Set "firstep" level 129 * @ah: The &struct ath5k_hw 130 * @level: level between 0 and @ATH5K_ANI_MAX_FIRSTEP_LVL 131 */ 132 void 133 ath5k_ani_set_firstep_level(struct ath5k_hw *ah, int level) 134 { 135 static const int val[] = { 0, 4, 8 }; 136 137 if (level < 0 || level >= ARRAY_SIZE(val)) { 138 ATH5K_ERR(ah, "firstep level %d out of range", level); 139 return; 140 } 141 142 AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SIG, 143 AR5K_PHY_SIG_FIRSTEP, val[level]); 144 145 ah->ani_state.firstep_level = level; 146 ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level); 147 } 148 149 /** 150 * ath5k_ani_set_ofdm_weak_signal_detection() - Set OFDM weak signal detection 151 * @ah: The &struct ath5k_hw 152 * @on: turn on or off 153 */ 154 void 155 ath5k_ani_set_ofdm_weak_signal_detection(struct ath5k_hw *ah, bool on) 156 { 157 static const int m1l[] = { 127, 50 }; 158 static const int m2l[] = { 127, 40 }; 159 static const int m1[] = { 127, 0x4d }; 160 static const int m2[] = { 127, 0x40 }; 161 static const int m2cnt[] = { 31, 16 }; 162 static const int m2lcnt[] = { 63, 48 }; 163 164 AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR, 165 AR5K_PHY_WEAK_OFDM_LOW_THR_M1, m1l[on]); 166 AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR, 167 AR5K_PHY_WEAK_OFDM_LOW_THR_M2, m2l[on]); 168 AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_HIGH_THR, 169 AR5K_PHY_WEAK_OFDM_HIGH_THR_M1, m1[on]); 170 AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_HIGH_THR, 171 AR5K_PHY_WEAK_OFDM_HIGH_THR_M2, m2[on]); 172 AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_HIGH_THR, 173 AR5K_PHY_WEAK_OFDM_HIGH_THR_M2_COUNT, m2cnt[on]); 174 AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR, 175 AR5K_PHY_WEAK_OFDM_LOW_THR_M2_COUNT, m2lcnt[on]); 176 177 if (on) 178 AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR, 179 AR5K_PHY_WEAK_OFDM_LOW_THR_SELFCOR_EN); 180 else 181 AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR, 182 AR5K_PHY_WEAK_OFDM_LOW_THR_SELFCOR_EN); 183 184 ah->ani_state.ofdm_weak_sig = on; 185 ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "turned %s", 186 on ? "on" : "off"); 187 } 188 189 /** 190 * ath5k_ani_set_cck_weak_signal_detection() - Set CCK weak signal detection 191 * @ah: The &struct ath5k_hw 192 * @on: turn on or off 193 */ 194 void 195 ath5k_ani_set_cck_weak_signal_detection(struct ath5k_hw *ah, bool on) 196 { 197 static const int val[] = { 8, 6 }; 198 AR5K_REG_WRITE_BITS(ah, AR5K_PHY_CCK_CROSSCORR, 199 AR5K_PHY_CCK_CROSSCORR_WEAK_SIG_THR, val[on]); 200 ah->ani_state.cck_weak_sig = on; 201 ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "turned %s", 202 on ? "on" : "off"); 203 } 204 205 206 /***************\ 207 * ANI algorithm * 208 \***************/ 209 210 /** 211 * ath5k_ani_raise_immunity() - Increase noise immunity 212 * @ah: The &struct ath5k_hw 213 * @as: The &struct ath5k_ani_state 214 * @ofdm_trigger: If this is true we are called because of too many OFDM errors, 215 * the algorithm will tune more parameters then. 216 * 217 * Try to raise noise immunity (=decrease sensitivity) in several steps 218 * depending on the average RSSI of the beacons we received. 219 */ 220 static void 221 ath5k_ani_raise_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as, 222 bool ofdm_trigger) 223 { 224 int rssi = ewma_read(&ah->ah_beacon_rssi_avg); 225 226 ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "raise immunity (%s)", 227 ofdm_trigger ? "ODFM" : "CCK"); 228 229 /* first: raise noise immunity */ 230 if (as->noise_imm_level < ATH5K_ANI_MAX_NOISE_IMM_LVL) { 231 ath5k_ani_set_noise_immunity_level(ah, as->noise_imm_level + 1); 232 return; 233 } 234 235 /* only OFDM: raise spur immunity level */ 236 if (ofdm_trigger && 237 as->spur_level < ah->ani_state.max_spur_level) { 238 ath5k_ani_set_spur_immunity_level(ah, as->spur_level + 1); 239 return; 240 } 241 242 /* AP mode */ 243 if (ah->opmode == NL80211_IFTYPE_AP) { 244 if (as->firstep_level < ATH5K_ANI_MAX_FIRSTEP_LVL) 245 ath5k_ani_set_firstep_level(ah, as->firstep_level + 1); 246 return; 247 } 248 249 /* STA and IBSS mode */ 250 251 /* TODO: for IBSS mode it would be better to keep a beacon RSSI average 252 * per each neighbour node and use the minimum of these, to make sure we 253 * don't shut out a remote node by raising immunity too high. */ 254 255 if (rssi > ATH5K_ANI_RSSI_THR_HIGH) { 256 ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, 257 "beacon RSSI high"); 258 /* only OFDM: beacon RSSI is high, we can disable ODFM weak 259 * signal detection */ 260 if (ofdm_trigger && as->ofdm_weak_sig == true) { 261 ath5k_ani_set_ofdm_weak_signal_detection(ah, false); 262 ath5k_ani_set_spur_immunity_level(ah, 0); 263 return; 264 } 265 /* as a last resort or CCK: raise firstep level */ 266 if (as->firstep_level < ATH5K_ANI_MAX_FIRSTEP_LVL) { 267 ath5k_ani_set_firstep_level(ah, as->firstep_level + 1); 268 return; 269 } 270 } else if (rssi > ATH5K_ANI_RSSI_THR_LOW) { 271 /* beacon RSSI in mid range, we need OFDM weak signal detect, 272 * but can raise firstep level */ 273 ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, 274 "beacon RSSI mid"); 275 if (ofdm_trigger && as->ofdm_weak_sig == false) 276 ath5k_ani_set_ofdm_weak_signal_detection(ah, true); 277 if (as->firstep_level < ATH5K_ANI_MAX_FIRSTEP_LVL) 278 ath5k_ani_set_firstep_level(ah, as->firstep_level + 1); 279 return; 280 } else if (ah->ah_current_channel->band == IEEE80211_BAND_2GHZ) { 281 /* beacon RSSI is low. in B/G mode turn of OFDM weak signal 282 * detect and zero firstep level to maximize CCK sensitivity */ 283 ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, 284 "beacon RSSI low, 2GHz"); 285 if (ofdm_trigger && as->ofdm_weak_sig == true) 286 ath5k_ani_set_ofdm_weak_signal_detection(ah, false); 287 if (as->firstep_level > 0) 288 ath5k_ani_set_firstep_level(ah, 0); 289 return; 290 } 291 292 /* TODO: why not?: 293 if (as->cck_weak_sig == true) { 294 ath5k_ani_set_cck_weak_signal_detection(ah, false); 295 } 296 */ 297 } 298 299 /** 300 * ath5k_ani_lower_immunity() - Decrease noise immunity 301 * @ah: The &struct ath5k_hw 302 * @as: The &struct ath5k_ani_state 303 * 304 * Try to lower noise immunity (=increase sensitivity) in several steps 305 * depending on the average RSSI of the beacons we received. 306 */ 307 static void 308 ath5k_ani_lower_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as) 309 { 310 int rssi = ewma_read(&ah->ah_beacon_rssi_avg); 311 312 ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "lower immunity"); 313 314 if (ah->opmode == NL80211_IFTYPE_AP) { 315 /* AP mode */ 316 if (as->firstep_level > 0) { 317 ath5k_ani_set_firstep_level(ah, as->firstep_level - 1); 318 return; 319 } 320 } else { 321 /* STA and IBSS mode (see TODO above) */ 322 if (rssi > ATH5K_ANI_RSSI_THR_HIGH) { 323 /* beacon signal is high, leave OFDM weak signal 324 * detection off or it may oscillate 325 * TODO: who said it's off??? */ 326 } else if (rssi > ATH5K_ANI_RSSI_THR_LOW) { 327 /* beacon RSSI is mid-range: turn on ODFM weak signal 328 * detection and next, lower firstep level */ 329 if (as->ofdm_weak_sig == false) { 330 ath5k_ani_set_ofdm_weak_signal_detection(ah, 331 true); 332 return; 333 } 334 if (as->firstep_level > 0) { 335 ath5k_ani_set_firstep_level(ah, 336 as->firstep_level - 1); 337 return; 338 } 339 } else { 340 /* beacon signal is low: only reduce firstep level */ 341 if (as->firstep_level > 0) { 342 ath5k_ani_set_firstep_level(ah, 343 as->firstep_level - 1); 344 return; 345 } 346 } 347 } 348 349 /* all modes */ 350 if (as->spur_level > 0) { 351 ath5k_ani_set_spur_immunity_level(ah, as->spur_level - 1); 352 return; 353 } 354 355 /* finally, reduce noise immunity */ 356 if (as->noise_imm_level > 0) { 357 ath5k_ani_set_noise_immunity_level(ah, as->noise_imm_level - 1); 358 return; 359 } 360 } 361 362 /** 363 * ath5k_hw_ani_get_listen_time() - Update counters and return listening time 364 * @ah: The &struct ath5k_hw 365 * @as: The &struct ath5k_ani_state 366 * 367 * Return an approximation of the time spent "listening" in milliseconds (ms) 368 * since the last call of this function. 369 * Save a snapshot of the counter values for debugging/statistics. 370 */ 371 static int 372 ath5k_hw_ani_get_listen_time(struct ath5k_hw *ah, struct ath5k_ani_state *as) 373 { 374 struct ath_common *common = ath5k_hw_common(ah); 375 int listen; 376 377 spin_lock_bh(&common->cc_lock); 378 379 ath_hw_cycle_counters_update(common); 380 memcpy(&as->last_cc, &common->cc_ani, sizeof(as->last_cc)); 381 382 /* clears common->cc_ani */ 383 listen = ath_hw_get_listen_time(common); 384 385 spin_unlock_bh(&common->cc_lock); 386 387 return listen; 388 } 389 390 /** 391 * ath5k_ani_save_and_clear_phy_errors() - Clear and save PHY error counters 392 * @ah: The &struct ath5k_hw 393 * @as: The &struct ath5k_ani_state 394 * 395 * Clear the PHY error counters as soon as possible, since this might be called 396 * from a MIB interrupt and we want to make sure we don't get interrupted again. 397 * Add the count of CCK and OFDM errors to our internal state, so it can be used 398 * by the algorithm later. 399 * 400 * Will be called from interrupt and tasklet context. 401 * Returns 0 if both counters are zero. 402 */ 403 static int 404 ath5k_ani_save_and_clear_phy_errors(struct ath5k_hw *ah, 405 struct ath5k_ani_state *as) 406 { 407 unsigned int ofdm_err, cck_err; 408 409 if (!ah->ah_capabilities.cap_has_phyerr_counters) 410 return 0; 411 412 ofdm_err = ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1); 413 cck_err = ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2); 414 415 /* reset counters first, we might be in a hurry (interrupt) */ 416 ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_OFDM_TRIG_HIGH, 417 AR5K_PHYERR_CNT1); 418 ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_CCK_TRIG_HIGH, 419 AR5K_PHYERR_CNT2); 420 421 ofdm_err = ATH5K_ANI_OFDM_TRIG_HIGH - (ATH5K_PHYERR_CNT_MAX - ofdm_err); 422 cck_err = ATH5K_ANI_CCK_TRIG_HIGH - (ATH5K_PHYERR_CNT_MAX - cck_err); 423 424 /* sometimes both can be zero, especially when there is a superfluous 425 * second interrupt. detect that here and return an error. */ 426 if (ofdm_err <= 0 && cck_err <= 0) 427 return 0; 428 429 /* avoid negative values should one of the registers overflow */ 430 if (ofdm_err > 0) { 431 as->ofdm_errors += ofdm_err; 432 as->sum_ofdm_errors += ofdm_err; 433 } 434 if (cck_err > 0) { 435 as->cck_errors += cck_err; 436 as->sum_cck_errors += cck_err; 437 } 438 return 1; 439 } 440 441 /** 442 * ath5k_ani_period_restart() - Restart ANI period 443 * @as: The &struct ath5k_ani_state 444 * 445 * Just reset counters, so they are clear for the next "ani period". 446 */ 447 static void 448 ath5k_ani_period_restart(struct ath5k_ani_state *as) 449 { 450 /* keep last values for debugging */ 451 as->last_ofdm_errors = as->ofdm_errors; 452 as->last_cck_errors = as->cck_errors; 453 as->last_listen = as->listen_time; 454 455 as->ofdm_errors = 0; 456 as->cck_errors = 0; 457 as->listen_time = 0; 458 } 459 460 /** 461 * ath5k_ani_calibration() - The main ANI calibration function 462 * @ah: The &struct ath5k_hw 463 * 464 * We count OFDM and CCK errors relative to the time where we did not send or 465 * receive ("listen" time) and raise or lower immunity accordingly. 466 * This is called regularly (every second) from the calibration timer, but also 467 * when an error threshold has been reached. 468 * 469 * In order to synchronize access from different contexts, this should be 470 * called only indirectly by scheduling the ANI tasklet! 471 */ 472 void 473 ath5k_ani_calibration(struct ath5k_hw *ah) 474 { 475 struct ath5k_ani_state *as = &ah->ani_state; 476 int listen, ofdm_high, ofdm_low, cck_high, cck_low; 477 478 /* get listen time since last call and add it to the counter because we 479 * might not have restarted the "ani period" last time. 480 * always do this to calculate the busy time also in manual mode */ 481 listen = ath5k_hw_ani_get_listen_time(ah, as); 482 as->listen_time += listen; 483 484 if (as->ani_mode != ATH5K_ANI_MODE_AUTO) 485 return; 486 487 ath5k_ani_save_and_clear_phy_errors(ah, as); 488 489 ofdm_high = as->listen_time * ATH5K_ANI_OFDM_TRIG_HIGH / 1000; 490 cck_high = as->listen_time * ATH5K_ANI_CCK_TRIG_HIGH / 1000; 491 ofdm_low = as->listen_time * ATH5K_ANI_OFDM_TRIG_LOW / 1000; 492 cck_low = as->listen_time * ATH5K_ANI_CCK_TRIG_LOW / 1000; 493 494 ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, 495 "listen %d (now %d)", as->listen_time, listen); 496 ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, 497 "check high ofdm %d/%d cck %d/%d", 498 as->ofdm_errors, ofdm_high, as->cck_errors, cck_high); 499 500 if (as->ofdm_errors > ofdm_high || as->cck_errors > cck_high) { 501 /* too many PHY errors - we have to raise immunity */ 502 bool ofdm_flag = as->ofdm_errors > ofdm_high ? true : false; 503 ath5k_ani_raise_immunity(ah, as, ofdm_flag); 504 ath5k_ani_period_restart(as); 505 506 } else if (as->listen_time > 5 * ATH5K_ANI_LISTEN_PERIOD) { 507 /* If more than 5 (TODO: why 5?) periods have passed and we got 508 * relatively little errors we can try to lower immunity */ 509 ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, 510 "check low ofdm %d/%d cck %d/%d", 511 as->ofdm_errors, ofdm_low, as->cck_errors, cck_low); 512 513 if (as->ofdm_errors <= ofdm_low && as->cck_errors <= cck_low) 514 ath5k_ani_lower_immunity(ah, as); 515 516 ath5k_ani_period_restart(as); 517 } 518 } 519 520 521 /*******************\ 522 * Interrupt handler * 523 \*******************/ 524 525 /** 526 * ath5k_ani_mib_intr() - Interrupt handler for ANI MIB counters 527 * @ah: The &struct ath5k_hw 528 * 529 * Just read & reset the registers quickly, so they don't generate more 530 * interrupts, save the counters and schedule the tasklet to decide whether 531 * to raise immunity or not. 532 * 533 * We just need to handle PHY error counters, ath5k_hw_update_mib_counters() 534 * should take care of all "normal" MIB interrupts. 535 */ 536 void 537 ath5k_ani_mib_intr(struct ath5k_hw *ah) 538 { 539 struct ath5k_ani_state *as = &ah->ani_state; 540 541 /* nothing to do here if HW does not have PHY error counters - they 542 * can't be the reason for the MIB interrupt then */ 543 if (!ah->ah_capabilities.cap_has_phyerr_counters) 544 return; 545 546 /* not in use but clear anyways */ 547 ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT); 548 ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT); 549 550 if (ah->ani_state.ani_mode != ATH5K_ANI_MODE_AUTO) 551 return; 552 553 /* If one of the errors triggered, we can get a superfluous second 554 * interrupt, even though we have already reset the register. The 555 * function detects that so we can return early. */ 556 if (ath5k_ani_save_and_clear_phy_errors(ah, as) == 0) 557 return; 558 559 if (as->ofdm_errors > ATH5K_ANI_OFDM_TRIG_HIGH || 560 as->cck_errors > ATH5K_ANI_CCK_TRIG_HIGH) 561 tasklet_schedule(&ah->ani_tasklet); 562 } 563 564 /** 565 * ath5k_ani_phy_error_report - Used by older HW to report PHY errors 566 * 567 * @ah: The &struct ath5k_hw 568 * @phyerr: One of enum ath5k_phy_error_code 569 * 570 * This is used by hardware without PHY error counters to report PHY errors 571 * on a frame-by-frame basis, instead of the interrupt. 572 */ 573 void 574 ath5k_ani_phy_error_report(struct ath5k_hw *ah, 575 enum ath5k_phy_error_code phyerr) 576 { 577 struct ath5k_ani_state *as = &ah->ani_state; 578 579 if (phyerr == AR5K_RX_PHY_ERROR_OFDM_TIMING) { 580 as->ofdm_errors++; 581 if (as->ofdm_errors > ATH5K_ANI_OFDM_TRIG_HIGH) 582 tasklet_schedule(&ah->ani_tasklet); 583 } else if (phyerr == AR5K_RX_PHY_ERROR_CCK_TIMING) { 584 as->cck_errors++; 585 if (as->cck_errors > ATH5K_ANI_CCK_TRIG_HIGH) 586 tasklet_schedule(&ah->ani_tasklet); 587 } 588 } 589 590 591 /****************\ 592 * Initialization * 593 \****************/ 594 595 /** 596 * ath5k_enable_phy_err_counters() - Enable PHY error counters 597 * @ah: The &struct ath5k_hw 598 * 599 * Enable PHY error counters for OFDM and CCK timing errors. 600 */ 601 static void 602 ath5k_enable_phy_err_counters(struct ath5k_hw *ah) 603 { 604 ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_OFDM_TRIG_HIGH, 605 AR5K_PHYERR_CNT1); 606 ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_CCK_TRIG_HIGH, 607 AR5K_PHYERR_CNT2); 608 ath5k_hw_reg_write(ah, AR5K_PHY_ERR_FIL_OFDM, AR5K_PHYERR_CNT1_MASK); 609 ath5k_hw_reg_write(ah, AR5K_PHY_ERR_FIL_CCK, AR5K_PHYERR_CNT2_MASK); 610 611 /* not in use */ 612 ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT); 613 ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT); 614 } 615 616 /** 617 * ath5k_disable_phy_err_counters() - Disable PHY error counters 618 * @ah: The &struct ath5k_hw 619 * 620 * Disable PHY error counters for OFDM and CCK timing errors. 621 */ 622 static void 623 ath5k_disable_phy_err_counters(struct ath5k_hw *ah) 624 { 625 ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT1); 626 ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT2); 627 ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT1_MASK); 628 ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT2_MASK); 629 630 /* not in use */ 631 ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT); 632 ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT); 633 } 634 635 /** 636 * ath5k_ani_init() - Initialize ANI 637 * @ah: The &struct ath5k_hw 638 * @mode: One of enum ath5k_ani_mode 639 * 640 * Initialize ANI according to mode. 641 */ 642 void 643 ath5k_ani_init(struct ath5k_hw *ah, enum ath5k_ani_mode mode) 644 { 645 /* ANI is only possible on 5212 and newer */ 646 if (ah->ah_version < AR5K_AR5212) 647 return; 648 649 if (mode < ATH5K_ANI_MODE_OFF || mode > ATH5K_ANI_MODE_AUTO) { 650 ATH5K_ERR(ah, "ANI mode %d out of range", mode); 651 return; 652 } 653 654 /* clear old state information */ 655 memset(&ah->ani_state, 0, sizeof(ah->ani_state)); 656 657 /* older hardware has more spur levels than newer */ 658 if (ah->ah_mac_srev < AR5K_SREV_AR2414) 659 ah->ani_state.max_spur_level = 7; 660 else 661 ah->ani_state.max_spur_level = 2; 662 663 /* initial values for our ani parameters */ 664 if (mode == ATH5K_ANI_MODE_OFF) { 665 ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "ANI off\n"); 666 } else if (mode == ATH5K_ANI_MODE_MANUAL_LOW) { 667 ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, 668 "ANI manual low -> high sensitivity\n"); 669 ath5k_ani_set_noise_immunity_level(ah, 0); 670 ath5k_ani_set_spur_immunity_level(ah, 0); 671 ath5k_ani_set_firstep_level(ah, 0); 672 ath5k_ani_set_ofdm_weak_signal_detection(ah, true); 673 ath5k_ani_set_cck_weak_signal_detection(ah, true); 674 } else if (mode == ATH5K_ANI_MODE_MANUAL_HIGH) { 675 ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, 676 "ANI manual high -> low sensitivity\n"); 677 ath5k_ani_set_noise_immunity_level(ah, 678 ATH5K_ANI_MAX_NOISE_IMM_LVL); 679 ath5k_ani_set_spur_immunity_level(ah, 680 ah->ani_state.max_spur_level); 681 ath5k_ani_set_firstep_level(ah, ATH5K_ANI_MAX_FIRSTEP_LVL); 682 ath5k_ani_set_ofdm_weak_signal_detection(ah, false); 683 ath5k_ani_set_cck_weak_signal_detection(ah, false); 684 } else if (mode == ATH5K_ANI_MODE_AUTO) { 685 ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "ANI auto\n"); 686 ath5k_ani_set_noise_immunity_level(ah, 0); 687 ath5k_ani_set_spur_immunity_level(ah, 0); 688 ath5k_ani_set_firstep_level(ah, 0); 689 ath5k_ani_set_ofdm_weak_signal_detection(ah, true); 690 ath5k_ani_set_cck_weak_signal_detection(ah, false); 691 } 692 693 /* newer hardware has PHY error counter registers which we can use to 694 * get OFDM and CCK error counts. older hardware has to set rxfilter and 695 * report every single PHY error by calling ath5k_ani_phy_error_report() 696 */ 697 if (mode == ATH5K_ANI_MODE_AUTO) { 698 if (ah->ah_capabilities.cap_has_phyerr_counters) 699 ath5k_enable_phy_err_counters(ah); 700 else 701 ath5k_hw_set_rx_filter(ah, ath5k_hw_get_rx_filter(ah) | 702 AR5K_RX_FILTER_PHYERR); 703 } else { 704 if (ah->ah_capabilities.cap_has_phyerr_counters) 705 ath5k_disable_phy_err_counters(ah); 706 else 707 ath5k_hw_set_rx_filter(ah, ath5k_hw_get_rx_filter(ah) & 708 ~AR5K_RX_FILTER_PHYERR); 709 } 710 711 ah->ani_state.ani_mode = mode; 712 } 713 714 715 /**************\ 716 * Debug output * 717 \**************/ 718 719 #ifdef CONFIG_ATH5K_DEBUG 720 721 /** 722 * ath5k_ani_print_counters() - Print ANI counters 723 * @ah: The &struct ath5k_hw 724 * 725 * Used for debugging ANI 726 */ 727 void 728 ath5k_ani_print_counters(struct ath5k_hw *ah) 729 { 730 /* clears too */ 731 printk(KERN_NOTICE "ACK fail\t%d\n", 732 ath5k_hw_reg_read(ah, AR5K_ACK_FAIL)); 733 printk(KERN_NOTICE "RTS fail\t%d\n", 734 ath5k_hw_reg_read(ah, AR5K_RTS_FAIL)); 735 printk(KERN_NOTICE "RTS success\t%d\n", 736 ath5k_hw_reg_read(ah, AR5K_RTS_OK)); 737 printk(KERN_NOTICE "FCS error\t%d\n", 738 ath5k_hw_reg_read(ah, AR5K_FCS_FAIL)); 739 740 /* no clear */ 741 printk(KERN_NOTICE "tx\t%d\n", 742 ath5k_hw_reg_read(ah, AR5K_PROFCNT_TX)); 743 printk(KERN_NOTICE "rx\t%d\n", 744 ath5k_hw_reg_read(ah, AR5K_PROFCNT_RX)); 745 printk(KERN_NOTICE "busy\t%d\n", 746 ath5k_hw_reg_read(ah, AR5K_PROFCNT_RXCLR)); 747 printk(KERN_NOTICE "cycles\t%d\n", 748 ath5k_hw_reg_read(ah, AR5K_PROFCNT_CYCLE)); 749 750 printk(KERN_NOTICE "AR5K_PHYERR_CNT1\t%d\n", 751 ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1)); 752 printk(KERN_NOTICE "AR5K_PHYERR_CNT2\t%d\n", 753 ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2)); 754 printk(KERN_NOTICE "AR5K_OFDM_FIL_CNT\t%d\n", 755 ath5k_hw_reg_read(ah, AR5K_OFDM_FIL_CNT)); 756 printk(KERN_NOTICE "AR5K_CCK_FIL_CNT\t%d\n", 757 ath5k_hw_reg_read(ah, AR5K_CCK_FIL_CNT)); 758 } 759 760 #endif 761