diff options
author | Felix Fietkau <nbd@openwrt.org> | 2010-06-12 00:34:01 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-06-14 15:39:32 -0400 |
commit | 9f42c2b667691f6ad29842302c66c864e7eb326c (patch) | |
tree | 10a411c5704e79dae091431b2176764d09951807 | |
parent | 717f6bedcd2d3d39624437e1de7067c90ec931f0 (diff) |
ath9k: implement PA predistortion support
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | drivers/net/wireless/ath/ath9k/ath9k.h | 7 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/init.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/main.c | 116 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/xmit.c | 16 |
4 files changed, 137 insertions, 3 deletions
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 1a19aea8c88c..8d163ae4255e 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -20,6 +20,7 @@ #include <linux/etherdevice.h> #include <linux/device.h> #include <linux/leds.h> +#include <linux/completion.h> #include "debug.h" #include "common.h" @@ -224,6 +225,7 @@ struct ath_buf_state { int bfs_tidno; int bfs_retries; u8 bf_type; + u8 bfs_paprd; u32 bfs_keyix; enum ath9k_key_type bfs_keytype; }; @@ -280,6 +282,7 @@ struct ath_tx_control { struct ath_txq *txq; int if_id; enum ath9k_internal_frame_type frame_type; + u8 paprd; }; #define ATH_TX_ERROR 0x01 @@ -422,6 +425,7 @@ int ath_beaconq_config(struct ath_softc *sc); #define ATH_LONG_CALINTERVAL 30000 /* 30 seconds */ #define ATH_RESTART_CALINTERVAL 1200000 /* 20 minutes */ +void ath_paprd_calibrate(struct work_struct *work); void ath_ani_calibrate(unsigned long data); /**********/ @@ -553,6 +557,9 @@ struct ath_softc { spinlock_t sc_serial_rw; spinlock_t sc_pm_lock; struct mutex mutex; + struct work_struct paprd_work; + struct completion paprd_complete; + int paprd_txok; u32 intrstatus; u32 sc_flags; /* SC_OP_* */ diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 4e078301c887..e1fa26840a53 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -736,6 +736,7 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc, u16 subsysid, goto error_world; } + INIT_WORK(&sc->paprd_work, ath_paprd_calibrate); INIT_WORK(&sc->chan_work, ath9k_wiphy_chan_work); INIT_DELAYED_WORK(&sc->wiphy_work, ath9k_wiphy_work); sc->wiphy_scheduler_int = msecs_to_jiffies(500); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index b4136100abfb..f38da2920f85 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -232,6 +232,113 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, return r; } +static void ath_paprd_activate(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + int chain; + + if (!ah->curchan->paprd_done) + return; + + ath9k_ps_wakeup(sc); + for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { + if (!(ah->caps.tx_chainmask & BIT(chain))) + continue; + + ar9003_paprd_populate_single_table(ah, ah->curchan, chain); + } + + ar9003_paprd_enable(ah, true); + ath9k_ps_restore(sc); +} + +void ath_paprd_calibrate(struct work_struct *work) +{ + struct ath_softc *sc = container_of(work, struct ath_softc, paprd_work); + struct ieee80211_hw *hw = sc->hw; + struct ath_hw *ah = sc->sc_ah; + struct ieee80211_hdr *hdr; + struct sk_buff *skb = NULL; + struct ieee80211_tx_info *tx_info; + int band = hw->conf.channel->band; + struct ieee80211_supported_band *sband = &sc->sbands[band]; + struct ath_tx_control txctl; + int qnum, ftype; + int chain_ok = 0; + int chain; + int len = 1800; + int time_left; + int i; + + ath9k_ps_wakeup(sc); + skb = alloc_skb(len, GFP_KERNEL); + if (!skb) + return; + + tx_info = IEEE80211_SKB_CB(skb); + + skb_put(skb, len); + memset(skb->data, 0, len); + hdr = (struct ieee80211_hdr *)skb->data; + ftype = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC; + hdr->frame_control = cpu_to_le16(ftype); + hdr->duration_id = 10; + memcpy(hdr->addr1, hw->wiphy->perm_addr, ETH_ALEN); + memcpy(hdr->addr2, hw->wiphy->perm_addr, ETH_ALEN); + memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN); + + memset(&txctl, 0, sizeof(txctl)); + qnum = sc->tx.hwq_map[WME_AC_BE]; + txctl.txq = &sc->tx.txq[qnum]; + + ar9003_paprd_init_table(ah); + for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { + if (!(ah->caps.tx_chainmask & BIT(chain))) + continue; + + chain_ok = 0; + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->band = band; + + for (i = 0; i < 4; i++) { + tx_info->control.rates[i].idx = sband->n_bitrates - 1; + tx_info->control.rates[i].count = 6; + } + + init_completion(&sc->paprd_complete); + ar9003_paprd_setup_gain_table(ah, chain); + txctl.paprd = BIT(chain); + if (ath_tx_start(hw, skb, &txctl) != 0) + break; + + time_left = wait_for_completion_timeout(&sc->paprd_complete, + 100); + if (!time_left) { + ath_print(ath9k_hw_common(ah), ATH_DBG_CALIBRATE, + "Timeout waiting for paprd training on " + "TX chain %d\n", + chain); + break; + } + + if (!ar9003_paprd_is_done(ah)) + break; + + if (ar9003_paprd_create_curve(ah, ah->curchan, chain) != 0) + break; + + chain_ok = 1; + } + kfree_skb(skb); + + if (chain_ok) { + ah->curchan->paprd_done = true; + ath_paprd_activate(sc); + } + + ath9k_ps_restore(sc); +} + /* * This routine performs the periodic noise floor calibration function * that is used to adjust and optimize the chip performance. This @@ -333,6 +440,13 @@ set_timer: cal_interval = min(cal_interval, (u32)short_cal_interval); mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval)); + if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_PAPRD) && + !(sc->sc_flags & SC_OP_SCANNING)) { + if (!sc->sc_ah->curchan->paprd_done) + ieee80211_queue_work(sc->hw, &sc->paprd_work); + else + ath_paprd_activate(sc); + } } static void ath_start_ani(struct ath_common *common) @@ -1131,6 +1245,7 @@ static void ath9k_stop(struct ieee80211_hw *hw) cancel_delayed_work_sync(&sc->ath_led_blink_work); cancel_delayed_work_sync(&sc->tx_complete_work); + cancel_work_sync(&sc->paprd_work); if (!sc->num_sec_wiphy) { cancel_delayed_work_sync(&sc->wiphy_work); @@ -1850,6 +1965,7 @@ static void ath9k_sw_scan_start(struct ieee80211_hw *hw) ath9k_wiphy_pause_all_forced(sc, aphy); sc->sc_flags |= SC_OP_SCANNING; del_timer_sync(&common->ani.timer); + cancel_work_sync(&sc->paprd_work); cancel_delayed_work_sync(&sc->tx_complete_work); mutex_unlock(&sc->mutex); } diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 875b8b47fefe..20221b8c04fd 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1637,12 +1637,13 @@ static int ath_tx_setup_buffer(struct ieee80211_hw *hw, struct ath_buf *bf, bf->bf_frmlen -= padsize; } - if (conf_is_ht(&hw->conf)) { + if (!txctl->paprd && conf_is_ht(&hw->conf)) { bf->bf_state.bf_type |= BUF_HT; if (tx_info->flags & IEEE80211_TX_CTL_LDPC) use_ldpc = true; } + bf->bf_state.bfs_paprd = txctl->paprd; bf->bf_flags = setup_tx_flags(skb, use_ldpc); bf->bf_keytype = get_hw_crypto_keytype(skb); @@ -1717,6 +1718,9 @@ static void ath_tx_start_dma(struct ath_softc *sc, struct ath_buf *bf, bf->bf_buf_addr, txctl->txq->axq_qnum); + if (bf->bf_state.bfs_paprd) + ar9003_hw_set_paprd_txdesc(ah, ds, bf->bf_state.bfs_paprd); + spin_lock_bh(&txctl->txq->axq_lock); if (bf_isht(bf) && (sc->sc_flags & SC_OP_TXAGGR) && @@ -1938,8 +1942,14 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, } dma_unmap_single(sc->dev, bf->bf_dmacontext, skb->len, DMA_TO_DEVICE); - ath_tx_complete(sc, skb, bf->aphy, tx_flags); - ath_debug_stat_tx(sc, txq, bf, ts); + + if (bf->bf_state.bfs_paprd) { + sc->paprd_txok = txok; + complete(&sc->paprd_complete); + } else { + ath_tx_complete(sc, skb, bf->aphy, tx_flags); + ath_debug_stat_tx(sc, txq, bf, ts); + } /* * Return the list of ath_buf of this mpdu to free queue |