Commit 6e220662 authored by Nick Kossifidis's avatar Nick Kossifidis Committed by John W. Linville

ath5k: Use SWI to trigger calibration

* Get rid of calibration timer, instead use a software interrupt
  to schedule the calibration tasklet.

 a) We don't need a timer for this, there is no need for accuracy
   even with round_jiffies i think this is a waste of resources.
   Also we don't need to run calibration if we are idle (no
   interrupts).

 b) When we add ANI support we 'll just extend the poll function
   and calibration tasklet and handle all periodic phy calibration
   on one place (much cleaner).

 c) Having calibration on a tasklet is better since during calibration
   we can't transmit or receive (antennas are detached to measure
   noise floor), previously calibration could run in parallel with
   tx/rx and interfere (packet loss).

 v2: kill tasklet on stop_hw, stop/wake queues
 v3: use time_is_before_eq_jiffies to compare timestamp with current
     time
Signed-off-by: default avatarNick Kossifidis <mickflemm@gmail.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent b55a5de1
...@@ -919,6 +919,12 @@ enum ath5k_int { ...@@ -919,6 +919,12 @@ enum ath5k_int {
AR5K_INT_NOCARD = 0xffffffff AR5K_INT_NOCARD = 0xffffffff
}; };
/* Software interrupts used for calibration */
enum ath5k_software_interrupt {
AR5K_SWI_FULL_CALIBRATION = 0x01,
AR5K_SWI_SHORT_CALIBRATION = 0x02,
};
/* /*
* Power management * Power management
*/ */
...@@ -1123,6 +1129,15 @@ struct ath5k_hw { ...@@ -1123,6 +1129,15 @@ struct ath5k_hw {
/* noise floor from last periodic calibration */ /* noise floor from last periodic calibration */
s32 ah_noise_floor; s32 ah_noise_floor;
/* Calibration timestamp */
unsigned long ah_cal_tstamp;
/* Calibration interval (secs) */
u8 ah_cal_intval;
/* Software interrupt mask */
u8 ah_swi_mask;
/* /*
* Function pointers * Function pointers
*/ */
...@@ -1276,6 +1291,7 @@ extern int ath5k_hw_channel(struct ath5k_hw *ah, struct ieee80211_channel *chann ...@@ -1276,6 +1291,7 @@ extern int ath5k_hw_channel(struct ath5k_hw *ah, struct ieee80211_channel *chann
/* PHY calibration */ /* PHY calibration */
extern int ath5k_hw_phy_calibrate(struct ath5k_hw *ah, struct ieee80211_channel *channel); extern int ath5k_hw_phy_calibrate(struct ath5k_hw *ah, struct ieee80211_channel *channel);
extern int ath5k_hw_noise_floor_calibration(struct ath5k_hw *ah, short freq); extern int ath5k_hw_noise_floor_calibration(struct ath5k_hw *ah, short freq);
extern void ath5k_hw_calibration_poll(struct ath5k_hw *ah);
/* Spur mitigation */ /* Spur mitigation */
bool ath5k_hw_chan_has_spur_noise(struct ath5k_hw *ah, bool ath5k_hw_chan_has_spur_noise(struct ath5k_hw *ah,
struct ieee80211_channel *channel); struct ieee80211_channel *channel);
......
...@@ -59,7 +59,7 @@ ...@@ -59,7 +59,7 @@
#include "reg.h" #include "reg.h"
#include "debug.h" #include "debug.h"
static int ath5k_calinterval = 10; /* Calibrate PHY every 10 secs (TODO: Fixme) */ static u8 ath5k_calinterval = 10; /* Calibrate PHY every 10 secs (TODO: Fixme) */
static int modparam_nohwcrypt; static int modparam_nohwcrypt;
module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
...@@ -376,7 +376,7 @@ static int ath5k_stop_hw(struct ath5k_softc *sc); ...@@ -376,7 +376,7 @@ static int ath5k_stop_hw(struct ath5k_softc *sc);
static irqreturn_t ath5k_intr(int irq, void *dev_id); static irqreturn_t ath5k_intr(int irq, void *dev_id);
static void ath5k_tasklet_reset(unsigned long data); static void ath5k_tasklet_reset(unsigned long data);
static void ath5k_calibrate(unsigned long data); static void ath5k_tasklet_calibrate(unsigned long data);
/* /*
* Module init/exit functions * Module init/exit functions
...@@ -799,8 +799,8 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw) ...@@ -799,8 +799,8 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
tasklet_init(&sc->rxtq, ath5k_tasklet_rx, (unsigned long)sc); tasklet_init(&sc->rxtq, ath5k_tasklet_rx, (unsigned long)sc);
tasklet_init(&sc->txtq, ath5k_tasklet_tx, (unsigned long)sc); tasklet_init(&sc->txtq, ath5k_tasklet_tx, (unsigned long)sc);
tasklet_init(&sc->restq, ath5k_tasklet_reset, (unsigned long)sc); tasklet_init(&sc->restq, ath5k_tasklet_reset, (unsigned long)sc);
tasklet_init(&sc->calib, ath5k_tasklet_calibrate, (unsigned long)sc);
tasklet_init(&sc->beacontq, ath5k_tasklet_beacon, (unsigned long)sc); tasklet_init(&sc->beacontq, ath5k_tasklet_beacon, (unsigned long)sc);
setup_timer(&sc->calib_tim, ath5k_calibrate, (unsigned long)sc);
ret = ath5k_eeprom_read_mac(ah, mac); ret = ath5k_eeprom_read_mac(ah, mac);
if (ret) { if (ret) {
...@@ -2364,7 +2364,7 @@ ath5k_init(struct ath5k_softc *sc) ...@@ -2364,7 +2364,7 @@ ath5k_init(struct ath5k_softc *sc)
sc->curband = &sc->sbands[sc->curchan->band]; sc->curband = &sc->sbands[sc->curchan->band];
sc->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL | sc->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL |
AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL | AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL |
AR5K_INT_FATAL | AR5K_INT_GLOBAL; AR5K_INT_FATAL | AR5K_INT_GLOBAL | AR5K_INT_SWI;
ret = ath5k_reset(sc, NULL); ret = ath5k_reset(sc, NULL);
if (ret) if (ret)
goto done; goto done;
...@@ -2381,8 +2381,8 @@ ath5k_init(struct ath5k_softc *sc) ...@@ -2381,8 +2381,8 @@ ath5k_init(struct ath5k_softc *sc)
/* Set ack to be sent at low bit-rates */ /* Set ack to be sent at low bit-rates */
ath5k_hw_set_ack_bitrate_high(ah, false); ath5k_hw_set_ack_bitrate_high(ah, false);
mod_timer(&sc->calib_tim, round_jiffies(jiffies + /* Set PHY calibration inteval */
msecs_to_jiffies(ath5k_calinterval * 1000))); ah->ah_cal_intval = ath5k_calinterval;
ret = 0; ret = 0;
done: done:
...@@ -2475,10 +2475,10 @@ ath5k_stop_hw(struct ath5k_softc *sc) ...@@ -2475,10 +2475,10 @@ ath5k_stop_hw(struct ath5k_softc *sc)
mmiowb(); mmiowb();
mutex_unlock(&sc->lock); mutex_unlock(&sc->lock);
del_timer_sync(&sc->calib_tim);
tasklet_kill(&sc->rxtq); tasklet_kill(&sc->rxtq);
tasklet_kill(&sc->txtq); tasklet_kill(&sc->txtq);
tasklet_kill(&sc->restq); tasklet_kill(&sc->restq);
tasklet_kill(&sc->calib);
tasklet_kill(&sc->beacontq); tasklet_kill(&sc->beacontq);
ath5k_rfkill_hw_stop(sc->ah); ath5k_rfkill_hw_stop(sc->ah);
...@@ -2534,6 +2534,9 @@ ath5k_intr(int irq, void *dev_id) ...@@ -2534,6 +2534,9 @@ ath5k_intr(int irq, void *dev_id)
if (status & AR5K_INT_BMISS) { if (status & AR5K_INT_BMISS) {
/* TODO */ /* TODO */
} }
if (status & AR5K_INT_SWI) {
tasklet_schedule(&sc->calib);
}
if (status & AR5K_INT_MIB) { if (status & AR5K_INT_MIB) {
/* /*
* These stats are also used for ANI i think * These stats are also used for ANI i think
...@@ -2550,6 +2553,8 @@ ath5k_intr(int irq, void *dev_id) ...@@ -2550,6 +2553,8 @@ ath5k_intr(int irq, void *dev_id)
if (unlikely(!counter)) if (unlikely(!counter))
ATH5K_WARN(sc, "too many interrupts, giving up for now\n"); ATH5K_WARN(sc, "too many interrupts, giving up for now\n");
ath5k_hw_calibration_poll(ah);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -2566,11 +2571,19 @@ ath5k_tasklet_reset(unsigned long data) ...@@ -2566,11 +2571,19 @@ ath5k_tasklet_reset(unsigned long data)
* for temperature/environment changes. * for temperature/environment changes.
*/ */
static void static void
ath5k_calibrate(unsigned long data) ath5k_tasklet_calibrate(unsigned long data)
{ {
struct ath5k_softc *sc = (void *)data; struct ath5k_softc *sc = (void *)data;
struct ath5k_hw *ah = sc->ah; struct ath5k_hw *ah = sc->ah;
/* Only full calibration for now */
if (ah->ah_swi_mask != AR5K_SWI_FULL_CALIBRATION)
return;
/* Stop queues so that calibration
* doesn't interfere with tx */
ieee80211_stop_queues(sc->hw);
ATH5K_DBG(sc, ATH5K_DEBUG_CALIBRATE, "channel %u/%x\n", ATH5K_DBG(sc, ATH5K_DEBUG_CALIBRATE, "channel %u/%x\n",
ieee80211_frequency_to_channel(sc->curchan->center_freq), ieee80211_frequency_to_channel(sc->curchan->center_freq),
sc->curchan->hw_value); sc->curchan->hw_value);
...@@ -2588,8 +2601,11 @@ ath5k_calibrate(unsigned long data) ...@@ -2588,8 +2601,11 @@ ath5k_calibrate(unsigned long data)
ieee80211_frequency_to_channel( ieee80211_frequency_to_channel(
sc->curchan->center_freq)); sc->curchan->center_freq));
mod_timer(&sc->calib_tim, round_jiffies(jiffies + ah->ah_swi_mask = 0;
msecs_to_jiffies(ath5k_calinterval * 1000)));
/* Wake queues */
ieee80211_wake_queues(sc->hw);
} }
......
...@@ -177,6 +177,8 @@ struct ath5k_softc { ...@@ -177,6 +177,8 @@ struct ath5k_softc {
struct ath5k_rfkill rf_kill; struct ath5k_rfkill rf_kill;
struct tasklet_struct calib; /* calibration tasklet */
spinlock_t block; /* protects beacon */ spinlock_t block; /* protects beacon */
struct tasklet_struct beacontq; /* beacon intr tasklet */ struct tasklet_struct beacontq; /* beacon intr tasklet */
struct ath5k_buf *bbuf; /* beacon buffer */ struct ath5k_buf *bbuf; /* beacon buffer */
...@@ -187,7 +189,6 @@ struct ath5k_softc { ...@@ -187,7 +189,6 @@ struct ath5k_softc {
unsigned int nexttbtt; /* next beacon time in TU */ unsigned int nexttbtt; /* next beacon time in TU */
struct ath5k_txq *cabq; /* content after beacon */ struct ath5k_txq *cabq; /* content after beacon */
struct timer_list calib_tim; /* calibration timer */
int power_level; /* Requested tx power in dbm */ int power_level; /* Requested tx power in dbm */
bool assoc; /* assocate state */ bool assoc; /* assocate state */
bool enable_beacon; /* true if beacons are on */ bool enable_beacon; /* true if beacons are on */
......
...@@ -1104,6 +1104,29 @@ int ath5k_hw_channel(struct ath5k_hw *ah, struct ieee80211_channel *channel) ...@@ -1104,6 +1104,29 @@ int ath5k_hw_channel(struct ath5k_hw *ah, struct ieee80211_channel *channel)
PHY calibration PHY calibration
\*****************/ \*****************/
void
ath5k_hw_calibration_poll(struct ath5k_hw *ah)
{
/* Calibration interval in jiffies */
unsigned long cal_intval;
cal_intval = msecs_to_jiffies(ah->ah_cal_intval * 1000);
/* Initialize timestamp if needed */
if (!ah->ah_cal_tstamp)
ah->ah_cal_tstamp = jiffies;
/* For now we always do full calibration
* Mark software interrupt mask and fire software
* interrupt (bit gets auto-cleared) */
if (time_is_before_eq_jiffies(ah->ah_cal_tstamp + cal_intval)) {
ah->ah_cal_tstamp = jiffies;
ah->ah_swi_mask = AR5K_SWI_FULL_CALIBRATION;
AR5K_REG_ENABLE_BITS(ah, AR5K_CR, AR5K_CR_SWI);
}
}
/** /**
* ath5k_hw_noise_floor_calibration - perform PHY noise floor calibration * ath5k_hw_noise_floor_calibration - perform PHY noise floor calibration
* *
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment