ath5k: Cleanup turbo channel flags
[pandora-kernel.git] / drivers / net / wireless / ath / ath5k / base.c
index 8251946..8083117 100644 (file)
@@ -80,7 +80,8 @@ MODULE_SUPPORTED_DEVICE("Atheros 5xxx WLAN cards");
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_VERSION("0.6.0 (EXPERIMENTAL)");
 
-static int ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan);
+static int ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan,
+                                                               bool skip_pcu);
 static int ath5k_beacon_update(struct ieee80211_hw *hw,
                struct ieee80211_vif *vif);
 static void ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf);
@@ -363,11 +364,6 @@ ath5k_copy_channels(struct ath5k_hw *ah,
                case AR5K_MODE_11G:
                        channels[count].hw_value = chfreq | CHANNEL_OFDM;
                        break;
-               case AR5K_MODE_11A_TURBO:
-               case AR5K_MODE_11G_TURBO:
-                       channels[count].hw_value = chfreq |
-                               CHANNEL_OFDM | CHANNEL_TURBO;
-                       break;
                case AR5K_MODE_11B:
                        channels[count].hw_value = CHANNEL_B;
                }
@@ -496,7 +492,7 @@ ath5k_chan_set(struct ath5k_softc *sc, struct ieee80211_channel *chan)
         * hardware at the new frequency, and then re-enable
         * the relevant bits of the h/w.
         */
-       return ath5k_reset(sc, chan);
+       return ath5k_reset(sc, chan, true);
 }
 
 static void
@@ -549,7 +545,7 @@ static void ath_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
        /* Calculate combined mode - when APs are active, operate in AP mode.
         * Otherwise use the mode of the new interface. This can currently
         * only deal with combinations of APs and STAs. Only one ad-hoc
-        * interfaces is allowed above.
+        * interfaces is allowed.
         */
        if (avf->opmode == NL80211_IFTYPE_AP)
                iter_data->opmode = NL80211_IFTYPE_AP;
@@ -558,16 +554,8 @@ static void ath_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
                        iter_data->opmode = avf->opmode;
 }
 
-static void ath_do_set_opmode(struct ath5k_softc *sc)
-{
-       struct ath5k_hw *ah = sc->ah;
-       ath5k_hw_set_opmode(ah, sc->opmode);
-       ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "mode setup opmode %d (%s)\n",
-                 sc->opmode, ath_opmode_to_string(sc->opmode));
-}
-
-void ath5k_update_bssid_mask_and_opmode(struct ath5k_softc *sc,
-                                       struct ieee80211_vif *vif)
+static void ath5k_update_bssid_mask_and_opmode(struct ath5k_softc *sc,
+                                              struct ieee80211_vif *vif)
 {
        struct ath_common *common = ath5k_hw_common(sc->ah);
        struct ath_vif_iter_data iter_data;
@@ -595,7 +583,9 @@ void ath5k_update_bssid_mask_and_opmode(struct ath5k_softc *sc,
                /* Nothing active, default to station mode */
                sc->opmode = NL80211_IFTYPE_STATION;
 
-       ath_do_set_opmode(sc);
+       ath5k_hw_set_opmode(sc->ah, sc->opmode);
+       ATH5K_DBG(sc, ATH5K_DEBUG_MODE, "mode setup opmode %d (%s)\n",
+                 sc->opmode, ath_opmode_to_string(sc->opmode));
 
        if (iter_data.need_set_hw_addr && iter_data.found_active)
                ath5k_hw_set_lladdr(sc->ah, iter_data.active_mac);
@@ -1069,62 +1059,44 @@ err:
        return ret;
 }
 
+/**
+ * ath5k_drain_tx_buffs - Empty tx buffers
+ *
+ * @sc The &struct ath5k_softc
+ *
+ * Empty tx buffers from all queues in preparation
+ * of a reset or during shutdown.
+ *
+ * NB: this assumes output has been stopped and
+ *     we do not need to block ath5k_tx_tasklet
+ */
 static void
-ath5k_txq_drainq(struct ath5k_softc *sc, struct ath5k_txq *txq)
+ath5k_drain_tx_buffs(struct ath5k_softc *sc)
 {
+       struct ath5k_txq *txq;
        struct ath5k_buf *bf, *bf0;
+       int i;
 
-       /*
-        * NB: this assumes output has been stopped and
-        *     we do not need to block ath5k_tx_tasklet
-        */
-       spin_lock_bh(&txq->lock);
-       list_for_each_entry_safe(bf, bf0, &txq->q, list) {
-               ath5k_debug_printtxbuf(sc, bf);
-
-               ath5k_txbuf_free_skb(sc, bf);
+       for (i = 0; i < ARRAY_SIZE(sc->txqs); i++) {
+               if (sc->txqs[i].setup) {
+                       txq = &sc->txqs[i];
+                       spin_lock_bh(&txq->lock);
+                       list_for_each_entry_safe(bf, bf0, &txq->q, list) {
+                               ath5k_debug_printtxbuf(sc, bf);
 
-               spin_lock_bh(&sc->txbuflock);
-               list_move_tail(&bf->list, &sc->txbuf);
-               sc->txbuf_len++;
-               txq->txq_len--;
-               spin_unlock_bh(&sc->txbuflock);
-       }
-       txq->link = NULL;
-       txq->txq_poll_mark = false;
-       spin_unlock_bh(&txq->lock);
-}
+                               ath5k_txbuf_free_skb(sc, bf);
 
-/*
- * Drain the transmit queues and reclaim resources.
- */
-static void
-ath5k_txq_cleanup(struct ath5k_softc *sc)
-{
-       struct ath5k_hw *ah = sc->ah;
-       unsigned int i;
-
-       /* XXX return value */
-       if (likely(!test_bit(ATH_STAT_INVALID, sc->status))) {
-               /* don't touch the hardware if marked invalid */
-               ath5k_hw_stop_tx_dma(ah, sc->bhalq);
-               ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "beacon queue %x\n",
-                       ath5k_hw_get_txdp(ah, sc->bhalq));
-               for (i = 0; i < ARRAY_SIZE(sc->txqs); i++)
-                       if (sc->txqs[i].setup) {
-                               ath5k_hw_stop_tx_dma(ah, sc->txqs[i].qnum);
-                               ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "txq [%u] %x, "
-                                       "link %p\n",
-                                       sc->txqs[i].qnum,
-                                       ath5k_hw_get_txdp(ah,
-                                                       sc->txqs[i].qnum),
-                                       sc->txqs[i].link);
+                               spin_lock_bh(&sc->txbuflock);
+                               list_move_tail(&bf->list, &sc->txbuf);
+                               sc->txbuf_len++;
+                               txq->txq_len--;
+                               spin_unlock_bh(&sc->txbuflock);
                        }
+                       txq->link = NULL;
+                       txq->txq_poll_mark = false;
+                       spin_unlock_bh(&txq->lock);
+               }
        }
-
-       for (i = 0; i < ARRAY_SIZE(sc->txqs); i++)
-               if (sc->txqs[i].setup)
-                       ath5k_txq_drainq(sc, &sc->txqs[i]);
 }
 
 static void
@@ -1184,16 +1156,19 @@ err:
 }
 
 /*
- * Disable the receive h/w in preparation for a reset.
+ * Disable the receive logic on PCU (DRU)
+ * In preparation for a shutdown.
+ *
+ * Note: Doesn't stop rx DMA, ath5k_hw_dma_stop
+ * does.
  */
 static void
 ath5k_rx_stop(struct ath5k_softc *sc)
 {
        struct ath5k_hw *ah = sc->ah;
 
-       ath5k_hw_stop_rx_pcu(ah);       /* disable PCU */
        ath5k_hw_set_rx_filter(ah, 0);  /* clear recv filter */
-       ath5k_hw_stop_rx_dma(ah);       /* disable DMA engine */
+       ath5k_hw_stop_rx_pcu(ah);       /* disable PCU */
 
        ath5k_debug_printrxbuffs(sc, ah);
 }
@@ -1307,8 +1282,7 @@ ath5k_update_beacon_rssi(struct ath5k_softc *sc, struct sk_buff *skb, int rssi)
            memcmp(mgmt->bssid, common->curbssid, ETH_ALEN) != 0)
                return;
 
-       ah->ah_beacon_rssi_avg = ath5k_moving_average(ah->ah_beacon_rssi_avg,
-                                                     rssi);
+       ewma_add(&ah->ah_beacon_rssi_avg, rssi);
 
        /* in IBSS mode we should keep RSSI statistics per neighbour */
        /* le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS */
@@ -1944,7 +1918,7 @@ ath5k_beacon_send(struct ath5k_softc *sc)
         * This should never fail since we check above that no frames
         * are still pending on the queue.
         */
-       if (unlikely(ath5k_hw_stop_tx_dma(ah, sc->bhalq))) {
+       if (unlikely(ath5k_hw_stop_beacon_queue(ah, sc->bhalq))) {
                ATH5K_WARN(sc, "beacon queue %u didn't start/stop ?\n", sc->bhalq);
                /* NB: hw still stops DMA, so proceed */
        }
@@ -2113,7 +2087,7 @@ ath5k_beacon_config(struct ath5k_softc *sc)
                } else
                        ath5k_beacon_update_timers(sc, -1);
        } else {
-               ath5k_hw_stop_tx_dma(sc->ah, sc->bhalq);
+               ath5k_hw_stop_beacon_queue(sc->ah, sc->bhalq);
        }
 
        ath5k_hw_set_imr(ah, sc->imask);
@@ -2349,7 +2323,7 @@ ath5k_tx_complete_poll_work(struct work_struct *work)
        if (needreset) {
                ATH5K_DBG(sc, ATH5K_DEBUG_RESET,
                          "TX queues stuck, resetting\n");
-               ath5k_reset(sc, sc->curchan);
+               ath5k_reset(sc, NULL, true);
        }
 
        ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work,
@@ -2390,10 +2364,9 @@ ath5k_stop_locked(struct ath5k_softc *sc)
                ath5k_led_off(sc);
                ath5k_hw_set_imr(ah, 0);
                synchronize_irq(sc->pdev->irq);
-       }
-       ath5k_txq_cleanup(sc);
-       if (!test_bit(ATH_STAT_INVALID, sc->status)) {
                ath5k_rx_stop(sc);
+               ath5k_hw_dma_stop(ah);
+               ath5k_drain_tx_buffs(sc);
                ath5k_hw_phy_disable(ah);
        }
 
@@ -2430,7 +2403,7 @@ ath5k_init(struct ath5k_softc *sc)
                AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL |
                AR5K_INT_FATAL | AR5K_INT_GLOBAL | AR5K_INT_MIB;
 
-       ret = ath5k_reset(sc, NULL);
+       ret = ath5k_reset(sc, NULL, false);
        if (ret)
                goto done;
 
@@ -2443,7 +2416,9 @@ ath5k_init(struct ath5k_softc *sc)
        for (i = 0; i < common->keymax; i++)
                ath_hw_keyreset(common, (u16) i);
 
-       ath5k_hw_set_ack_bitrate_high(ah, true);
+       /* Use higher rates for acks instead of base
+        * rate */
+       ah->ah_ack_bitrate_high = true;
 
        for (i = 0; i < ARRAY_SIZE(sc->bslot); i++)
                sc->bslot[i] = NULL;
@@ -2527,7 +2502,8 @@ ath5k_stop_hw(struct ath5k_softc *sc)
  * This should be called with sc->lock.
  */
 static int
-ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan)
+ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan,
+                                                       bool skip_pcu)
 {
        struct ath5k_hw *ah = sc->ah;
        int ret;
@@ -2539,13 +2515,13 @@ ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan)
        stop_tasklets(sc);
 
        if (chan) {
-               ath5k_txq_cleanup(sc);
-               ath5k_rx_stop(sc);
+               ath5k_drain_tx_buffs(sc);
 
                sc->curchan = chan;
                sc->curband = &sc->sbands[chan->band];
        }
-       ret = ath5k_hw_reset(ah, sc->opmode, sc->curchan, chan != NULL);
+       ret = ath5k_hw_reset(ah, sc->opmode, sc->curchan, chan != NULL,
+                                                               skip_pcu);
        if (ret) {
                ATH5K_ERR(sc, "can't reset hardware (%d)\n", ret);
                goto err;
@@ -2562,6 +2538,7 @@ ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan)
        ah->ah_cal_next_full = jiffies;
        ah->ah_cal_next_ani = jiffies;
        ah->ah_cal_next_nf = jiffies;
+       ewma_init(&ah->ah_beacon_rssi_avg, 1000, 8);
 
        /*
         * Change channels and update the h/w rate map if we're switching;
@@ -2590,7 +2567,7 @@ static void ath5k_reset_work(struct work_struct *work)
                reset_work);
 
        mutex_lock(&sc->lock);
-       ath5k_reset(sc, sc->curchan);
+       ath5k_reset(sc, NULL, true);
        mutex_unlock(&sc->lock);
 }
 
@@ -3206,14 +3183,32 @@ static int ath5k_get_survey(struct ieee80211_hw *hw, int idx,
 {
        struct ath5k_softc *sc = hw->priv;
        struct ieee80211_conf *conf = &hw->conf;
+       struct ath_common *common = ath5k_hw_common(sc->ah);
+       struct ath_cycle_counters *cc = &common->cc_survey;
+       unsigned int div = common->clockrate * 1000;
 
-        if (idx != 0)
+       if (idx != 0)
                return -ENOENT;
 
        survey->channel = conf->channel;
        survey->filled = SURVEY_INFO_NOISE_DBM;
        survey->noise = sc->ah->ah_noise_floor;
 
+       spin_lock_bh(&common->cc_lock);
+       ath_hw_cycle_counters_update(common);
+       if (cc->cycles > 0) {
+               survey->filled |= SURVEY_INFO_CHANNEL_TIME |
+                       SURVEY_INFO_CHANNEL_TIME_BUSY |
+                       SURVEY_INFO_CHANNEL_TIME_RX |
+                       SURVEY_INFO_CHANNEL_TIME_TX;
+               survey->channel_time += cc->cycles / div;
+               survey->channel_time_busy += cc->rx_busy / div;
+               survey->channel_time_rx += cc->rx_frame / div;
+               survey->channel_time_tx += cc->tx_frame / div;
+       }
+       memset(cc, 0, sizeof(*cc));
+       spin_unlock_bh(&common->cc_lock);
+
        return 0;
 }
 
@@ -3395,6 +3390,36 @@ static int ath5k_conf_tx(struct ieee80211_hw *hw, u16 queue,
        return ret;
 }
 
+static int ath5k_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
+{
+       struct ath5k_softc *sc = hw->priv;
+
+       if (tx_ant == 1 && rx_ant == 1)
+               ath5k_hw_set_antenna_mode(sc->ah, AR5K_ANTMODE_FIXED_A);
+       else if (tx_ant == 2 && rx_ant == 2)
+               ath5k_hw_set_antenna_mode(sc->ah, AR5K_ANTMODE_FIXED_B);
+       else if ((tx_ant & 3) == 3 && (rx_ant & 3) == 3)
+               ath5k_hw_set_antenna_mode(sc->ah, AR5K_ANTMODE_DEFAULT);
+       else
+               return -EINVAL;
+       return 0;
+}
+
+static int ath5k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
+{
+       struct ath5k_softc *sc = hw->priv;
+
+       switch (sc->ah->ah_ant_mode) {
+       case AR5K_ANTMODE_FIXED_A:
+               *tx_ant = 1; *rx_ant = 1; break;
+       case AR5K_ANTMODE_FIXED_B:
+               *tx_ant = 2; *rx_ant = 2; break;
+       case AR5K_ANTMODE_DEFAULT:
+               *tx_ant = 3; *rx_ant = 3; break;
+       }
+       return 0;
+}
+
 static const struct ieee80211_ops ath5k_hw_ops = {
        .tx             = ath5k_tx,
        .start          = ath5k_start,
@@ -3415,6 +3440,8 @@ static const struct ieee80211_ops ath5k_hw_ops = {
        .sw_scan_start  = ath5k_sw_scan_start,
        .sw_scan_complete = ath5k_sw_scan_complete,
        .set_coverage_class = ath5k_set_coverage_class,
+       .set_antenna    = ath5k_set_antenna,
+       .get_antenna    = ath5k_get_antenna,
 };
 
 /********************\