ath9k: implement PA predistortion support
authorFelix Fietkau <nbd@openwrt.org>
Sat, 12 Jun 2010 04:34:01 +0000 (00:34 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 14 Jun 2010 19:39:32 +0000 (15:39 -0400)
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>
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/xmit.c

index 1a19aea8c88c4870a1bf904133732706e10081a1..8d163ae4255ee58814de2484878e9f8a6c46795f 100644 (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_* */
index 4e078301c887cb09f4dcaadf86b1ca58cdb98d1c..e1fa26840a53a17ba51761a3d8c77c6cd95563cc 100644 (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);
index b4136100abfb50d6ebce03bb62d99ddfbe9a9e77..f38da2920f859239a9acf8823e3bf6e7604805ff 100644 (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);
 }
index 875b8b47fefeeb085db918b86619193ec61bddaf..20221b8c04fd886ea3556a609bda5494800ba2df 100644 (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