diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath5k/base.c')
-rw-r--r-- | drivers/net/wireless/ath/ath5k/base.c | 112 |
1 files changed, 78 insertions, 34 deletions
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 47194a4e365..a8cb1c7f4bf 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -2112,16 +2112,29 @@ static void ath5k_intr_calibration_poll(struct ath5k_hw *ah) { if (time_is_before_eq_jiffies(ah->ah_cal_next_ani) && - !(ah->ah_cal_mask & AR5K_CALIBRATION_FULL)) { - /* run ANI only when full calibration is not active */ + !(ah->ah_cal_mask & AR5K_CALIBRATION_FULL) && + !(ah->ah_cal_mask & AR5K_CALIBRATION_SHORT)) { + + /* Run ANI only when calibration is not active */ + ah->ah_cal_next_ani = jiffies + msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_ANI); tasklet_schedule(&ah->ani_tasklet); - } else if (time_is_before_eq_jiffies(ah->ah_cal_next_full)) { - ah->ah_cal_next_full = jiffies + - msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_FULL); - tasklet_schedule(&ah->calib); + } else if (time_is_before_eq_jiffies(ah->ah_cal_next_short) && + !(ah->ah_cal_mask & AR5K_CALIBRATION_FULL) && + !(ah->ah_cal_mask & AR5K_CALIBRATION_SHORT)) { + + /* Run calibration only when another calibration + * is not running. + * + * Note: This is for both full/short calibration, + * if it's time for a full one, ath5k_calibrate_work will deal + * with it. */ + + ah->ah_cal_next_short = jiffies + + msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_SHORT); + ieee80211_queue_work(ah->hw, &ah->calib_work); } /* we could use SWI to generate enough interrupts to meet our * calibration interval requirements, if necessary: @@ -2286,41 +2299,58 @@ ath5k_intr(int irq, void *dev_id) * for temperature/environment changes. */ static void -ath5k_tasklet_calibrate(unsigned long data) +ath5k_calibrate_work(struct work_struct *work) { - struct ath5k_hw *ah = (void *)data; + struct ath5k_hw *ah = container_of(work, struct ath5k_hw, + calib_work); + + /* Should we run a full calibration ? */ + if (time_is_before_eq_jiffies(ah->ah_cal_next_full)) { + + ah->ah_cal_next_full = jiffies + + msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_FULL); + ah->ah_cal_mask |= AR5K_CALIBRATION_FULL; + + ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE, + "running full calibration\n"); + + if (ath5k_hw_gainf_calibrate(ah) == AR5K_RFGAIN_NEED_CHANGE) { + /* + * Rfgain is out of bounds, reset the chip + * to load new gain values. + */ + ATH5K_DBG(ah, ATH5K_DEBUG_RESET, + "got new rfgain, resetting\n"); + ieee80211_queue_work(ah->hw, &ah->reset_work); + } + + /* TODO: On full calibration we should stop TX here, + * so that it doesn't interfere (mostly due to gain_f + * calibration that messes with tx packets -see phy.c). + * + * NOTE: Stopping the queues from above is not enough + * to stop TX but saves us from disconecting (at least + * we don't lose packets). */ + ieee80211_stop_queues(ah->hw); + } else + ah->ah_cal_mask |= AR5K_CALIBRATION_SHORT; - /* Only full calibration for now */ - ah->ah_cal_mask |= AR5K_CALIBRATION_FULL; ATH5K_DBG(ah, ATH5K_DEBUG_CALIBRATE, "channel %u/%x\n", ieee80211_frequency_to_channel(ah->curchan->center_freq), ah->curchan->hw_value); - if (ath5k_hw_gainf_calibrate(ah) == AR5K_RFGAIN_NEED_CHANGE) { - /* - * Rfgain is out of bounds, reset the chip - * to load new gain values. - */ - ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "calibration, resetting\n"); - ieee80211_queue_work(ah->hw, &ah->reset_work); - } if (ath5k_hw_phy_calibrate(ah, ah->curchan)) ATH5K_ERR(ah, "calibration of channel %u failed\n", ieee80211_frequency_to_channel( ah->curchan->center_freq)); - /* Noise floor calibration interrupts rx/tx path while I/Q calibration - * doesn't. - * TODO: We should stop TX here, so that it doesn't interfere. - * Note that stopping the queues is not enough to stop TX! */ - if (time_is_before_eq_jiffies(ah->ah_cal_next_nf)) { - ah->ah_cal_next_nf = jiffies + - msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_NF); - ath5k_hw_update_noise_floor(ah); - } - - ah->ah_cal_mask &= ~AR5K_CALIBRATION_FULL; + /* Clear calibration flags */ + if (ah->ah_cal_mask & AR5K_CALIBRATION_FULL) { + ieee80211_wake_queues(ah->hw); + ah->ah_cal_mask &= ~AR5K_CALIBRATION_FULL; + } else if (ah->ah_cal_mask & AR5K_CALIBRATION_SHORT) + ah->ah_cal_mask &= ~AR5K_CALIBRATION_SHORT; } @@ -2639,7 +2669,6 @@ static void ath5k_stop_tasklets(struct ath5k_hw *ah) ah->tx_pending = false; tasklet_kill(&ah->rxtq); tasklet_kill(&ah->txtq); - tasklet_kill(&ah->calib); tasklet_kill(&ah->beacontq); tasklet_kill(&ah->ani_tasklet); } @@ -2743,9 +2772,24 @@ ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan, ath5k_ani_init(ah, ani_mode); - ah->ah_cal_next_full = jiffies + msecs_to_jiffies(100); - ah->ah_cal_next_ani = jiffies; - ah->ah_cal_next_nf = jiffies; + /* + * Set calibration intervals + * + * Note: We don't need to run calibration imediately + * since some initial calibration is done on reset + * even for fast channel switching. Also on scanning + * this will get set again and again and it won't get + * executed unless we connect somewhere and spend some + * time on the channel (that's what calibration needs + * anyway to be accurate). + */ + ah->ah_cal_next_full = jiffies + + msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_FULL); + ah->ah_cal_next_ani = jiffies + + msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_ANI); + ah->ah_cal_next_short = jiffies + + msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_SHORT); + ewma_init(&ah->ah_beacon_rssi_avg, 1024, 8); /* clear survey data and cycle counters */ @@ -2895,11 +2939,11 @@ ath5k_init(struct ieee80211_hw *hw) tasklet_init(&ah->rxtq, ath5k_tasklet_rx, (unsigned long)ah); tasklet_init(&ah->txtq, ath5k_tasklet_tx, (unsigned long)ah); - tasklet_init(&ah->calib, ath5k_tasklet_calibrate, (unsigned long)ah); tasklet_init(&ah->beacontq, ath5k_tasklet_beacon, (unsigned long)ah); tasklet_init(&ah->ani_tasklet, ath5k_tasklet_ani, (unsigned long)ah); INIT_WORK(&ah->reset_work, ath5k_reset_work); + INIT_WORK(&ah->calib_work, ath5k_calibrate_work); INIT_DELAYED_WORK(&ah->tx_complete_work, ath5k_tx_complete_poll_work); ret = ath5k_hw_common(ah)->bus_ops->eeprom_read_mac(ah, mac); |