ath5k: Calibration re-work
[pandora-kernel.git] / drivers / net / wireless / ath / ath5k / base.c
index 47194a4..a8cb1c7 100644 (file)
@@ -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);