drivers/net/wireless/ath/ath5k/ath5k.h | 9 ++-- drivers/net/wireless/ath/ath5k/base.c | 66 ++++++++++++++++++++----------- drivers/net/wireless/ath/ath5k/phy.c | 22 +++++++++-- 3 files changed, 65 insertions(+), 32 deletions(-) diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index e43175a..4659f55 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -190,9 +190,9 @@ #define AR5K_TUNE_MAX_TXPOWER 63 #define AR5K_TUNE_DEFAULT_TXPOWER 25 #define AR5K_TUNE_TPC_TXPOWER false -#define ATH5K_TUNE_CALIBRATION_INTERVAL_FULL 10000 /* 10 sec */ +#define ATH5K_TUNE_CALIBRATION_INTERVAL_FULL 60000 /* 60 sec */ +#define ATH5K_TUNE_CALIBRATION_INTERVAL_SHORT 10000 /* 10 sec */ #define ATH5K_TUNE_CALIBRATION_INTERVAL_ANI 1000 /* 1 sec */ -#define ATH5K_TUNE_CALIBRATION_INTERVAL_NF 60000 /* 60 sec */ #define ATH5K_TX_COMPLETE_POLL_INT 3000 /* 3 sec */ @@ -904,7 +904,8 @@ enum ath5k_int { enum ath5k_calibration_mask { AR5K_CALIBRATION_FULL = 0x01, AR5K_CALIBRATION_SHORT = 0x02, - AR5K_CALIBRATION_ANI = 0x04, + AR5K_CALIBRATION_NF = 0x04, + AR5K_CALIBRATION_ANI = 0x08, }; /* @@ -1125,8 +1126,8 @@ struct ath5k_hw { /* Calibration timestamp */ unsigned long ah_cal_next_full; + unsigned long ah_cal_next_short; unsigned long ah_cal_next_ani; - unsigned long ah_cal_next_nf; /* Calibration mask */ u8 ah_cal_mask; diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index dbc45e0..ed6abe2 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -2109,15 +2109,19 @@ 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->ah_sc->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); + } else if (time_is_before_eq_jiffies(ah->ah_cal_next_short) && + !(ah->ah_cal_mask & AR5K_CALIBRATION_FULL)) { + /* run short calibration only when full calibration + * is not running */ + ah->ah_cal_next_short = jiffies + + msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_SHORT); tasklet_schedule(&ah->ah_sc->calib); } /* we could use SWI to generate enough interrupts to meet our @@ -2225,37 +2229,51 @@ ath5k_tasklet_calibrate(unsigned long data) struct ath5k_softc *sc = (void *)data; struct ath5k_hw *ah = sc->ah; - /* Only full calibration for now */ - ah->ah_cal_mask |= AR5K_CALIBRATION_FULL; + 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(sc, ATH5K_DEBUG_CALIBRATE, + "running full calibration\n"); + + /* TODO: On full calibration we should stop TX here, + * so that it doesn't interfere. Note that stopping + * the queues is not enough to stop TX but saves us + * from disconecting. */ + ieee80211_stop_queues(sc->hw); + } else + ah->ah_cal_mask |= AR5K_CALIBRATION_SHORT; + ATH5K_DBG(sc, ATH5K_DEBUG_CALIBRATE, "channel %u/%x\n", ieee80211_frequency_to_channel(sc->curchan->center_freq), sc->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(sc, ATH5K_DEBUG_RESET, "calibration, resetting\n"); - ieee80211_queue_work(sc->hw, &sc->reset_work); + if (ah->ah_cal_mask & AR5K_CALIBRATION_FULL) { + 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(sc, ATH5K_DEBUG_RESET, + "got new rfgain, resetting\n"); + ieee80211_queue_work(sc->hw, &sc->reset_work); + } } if (ath5k_hw_phy_calibrate(ah, sc->curchan)) ATH5K_ERR(sc, "calibration of channel %u failed\n", ieee80211_frequency_to_channel( sc->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); + + ath5k_hw_update_noise_floor(ah); + + if (ah->ah_cal_mask & AR5K_CALIBRATION_FULL) { + ieee80211_wake_queues(sc->hw); + ah->ah_cal_mask &= ~AR5K_CALIBRATION_FULL; } - ah->ah_cal_mask &= ~AR5K_CALIBRATION_FULL; + ah->ah_cal_mask &= ~AR5K_CALIBRATION_SHORT; } @@ -2685,8 +2703,8 @@ ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan, ath5k_ani_init(ah, ani_mode); ah->ah_cal_next_full = jiffies; + ah->ah_cal_next_short = jiffies; ah->ah_cal_next_ani = jiffies; - ah->ah_cal_next_nf = jiffies; ewma_init(&ah->ah_beacon_rssi_avg, 1024, 8); /* clear survey data and cycle counters */ diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c index d673ab2..48da97b 100644 --- a/drivers/net/wireless/ath/ath5k/phy.c +++ b/drivers/net/wireless/ath/ath5k/phy.c @@ -571,9 +571,7 @@ done: /* Main callback for thermal RF gain calibration engine * Check for a new gain reading and schedule an adjustment * if needed. - * - * TODO: Use sw interrupt to schedule reset if gain_F needs - * adjustment */ + */ enum ath5k_rfgain ath5k_hw_gainf_calibrate(struct ath5k_hw *ah) { u32 data, type; @@ -1383,6 +1381,8 @@ void ath5k_hw_update_noise_floor(struct ath5k_hw *ah) return; } + ah->ah_cal_mask |= AR5K_CALIBRATION_NF; + ee_mode = ath5k_eeprom_mode_from_channel(ah->ah_current_channel); /* completed NF calibration, test threshold */ @@ -1427,6 +1427,8 @@ void ath5k_hw_update_noise_floor(struct ath5k_hw *ah) ah->ah_noise_floor = nf; + ah->ah_cal_mask &= ~AR5K_CALIBRATION_NF; + ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE, "noise floor calibrated: %d\n", nf); } @@ -1606,7 +1608,19 @@ int ath5k_hw_phy_calibrate(struct ath5k_hw *ah, ret = ath5k_hw_rf5110_calibrate(ah, channel); else { ret = ath5k_hw_rf511x_iq_calibrate(ah); - ath5k_hw_request_rfgain_probe(ah); + if (ah->ah_cal_mask & AR5K_CALIBRATION_FULL) { + AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, + AR5K_PHY_AGCCTL_CAL); + ret = ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, + AR5K_PHY_AGCCTL_CAL | AR5K_PHY_AGCCTL_NF, + 0, false); + if (ret) { + ATH5K_ERR(ah->ah_sc, + "gain calibration timeout (%uMHz)\n", + channel->center_freq); + } + ath5k_hw_request_rfgain_probe(ah); + } } return ret;