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>
This commit is contained in:
Felix Fietkau 2010-06-12 00:34:01 -04:00 committed by John W. Linville
parent 717f6bedcd
commit 9f42c2b667
4 changed files with 137 additions and 3 deletions

View File

@ -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_* */

View File

@ -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);

View File

@ -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);
}

View File

@ -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