Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/ide-2.6
[pandora-kernel.git] / drivers / net / wireless / ath / ath5k / base.c
index 349a596..b6c5d37 100644 (file)
@@ -72,6 +72,11 @@ static int modparam_all_channels;
 module_param_named(all_channels, modparam_all_channels, bool, S_IRUGO);
 MODULE_PARM_DESC(all_channels, "Expose all channels the device can use.");
 
+static int modparam_fastchanswitch;
+module_param_named(fastchanswitch, modparam_fastchanswitch, bool, S_IRUGO);
+MODULE_PARM_DESC(fastchanswitch, "Enable fast channel switching for AR2413/AR5413 radios.");
+
+
 /* Module info */
 MODULE_AUTHOR("Jiri Slaby");
 MODULE_AUTHOR("Nick Kossifidis");
@@ -1443,6 +1448,21 @@ ath5k_receive_frame_ok(struct ath5k_softc *sc, struct ath5k_rx_status *rs)
        return true;
 }
 
+static void
+ath5k_set_current_imask(struct ath5k_softc *sc)
+{
+       enum ath5k_int imask = sc->imask;
+       unsigned long flags;
+
+       spin_lock_irqsave(&sc->irqlock, flags);
+       if (sc->rx_pending)
+               imask &= ~AR5K_INT_RX_ALL;
+       if (sc->tx_pending)
+               imask &= ~AR5K_INT_TX_ALL;
+       ath5k_hw_set_imr(sc->ah, imask);
+       spin_unlock_irqrestore(&sc->irqlock, flags);
+}
+
 static void
 ath5k_tasklet_rx(unsigned long data)
 {
@@ -1506,6 +1526,8 @@ next:
        } while (ath5k_rxbuf_setup(sc, bf) == 0);
 unlock:
        spin_unlock(&sc->rxbuflock);
+       sc->rx_pending = false;
+       ath5k_set_current_imask(sc);
 }
 
 
@@ -1573,28 +1595,28 @@ ath5k_tx_frame_completed(struct ath5k_softc *sc, struct sk_buff *skb,
                         struct ath5k_txq *txq, struct ath5k_tx_status *ts)
 {
        struct ieee80211_tx_info *info;
+       u8 tries[3];
        int i;
 
        sc->stats.tx_all_count++;
        sc->stats.tx_bytes_count += skb->len;
        info = IEEE80211_SKB_CB(skb);
 
+       tries[0] = info->status.rates[0].count;
+       tries[1] = info->status.rates[1].count;
+       tries[2] = info->status.rates[2].count;
+
        ieee80211_tx_info_clear_status(info);
-       for (i = 0; i < 4; i++) {
+
+       for (i = 0; i < ts->ts_final_idx; i++) {
                struct ieee80211_tx_rate *r =
                        &info->status.rates[i];
 
-               if (ts->ts_rate[i]) {
-                       r->idx = ath5k_hw_to_driver_rix(sc, ts->ts_rate[i]);
-                       r->count = ts->ts_retry[i];
-               } else {
-                       r->idx = -1;
-                       r->count = 0;
-               }
+               r->count = tries[i];
        }
 
-       /* count the successful attempt as well */
-       info->status.rates[ts->ts_final_idx].count++;
+       info->status.rates[ts->ts_final_idx].count = ts->ts_final_retry;
+       info->status.rates[ts->ts_final_idx + 1].idx = -1;
 
        if (unlikely(ts->ts_status)) {
                sc->stats.ack_fail++;
@@ -1609,6 +1631,9 @@ ath5k_tx_frame_completed(struct ath5k_softc *sc, struct sk_buff *skb,
        } else {
                info->flags |= IEEE80211_TX_STAT_ACK;
                info->status.ack_signal = ts->ts_rssi;
+
+               /* count the successful attempt as well */
+               info->status.rates[ts->ts_final_idx].count++;
        }
 
        /*
@@ -1690,6 +1715,9 @@ ath5k_tasklet_tx(unsigned long data)
        for (i=0; i < AR5K_NUM_TX_QUEUES; i++)
                if (sc->txqs[i].setup && (sc->ah->ah_txq_isr & BIT(i)))
                        ath5k_tx_processq(sc, &sc->txqs[i]);
+
+       sc->tx_pending = false;
+       ath5k_set_current_imask(sc);
 }
 
 
@@ -2119,6 +2147,20 @@ ath5k_intr_calibration_poll(struct ath5k_hw *ah)
         * AR5K_REG_ENABLE_BITS(ah, AR5K_CR, AR5K_CR_SWI); */
 }
 
+static void
+ath5k_schedule_rx(struct ath5k_softc *sc)
+{
+       sc->rx_pending = true;
+       tasklet_schedule(&sc->rxtq);
+}
+
+static void
+ath5k_schedule_tx(struct ath5k_softc *sc)
+{
+       sc->tx_pending = true;
+       tasklet_schedule(&sc->txtq);
+}
+
 irqreturn_t
 ath5k_intr(int irq, void *dev_id)
 {
@@ -2161,7 +2203,7 @@ ath5k_intr(int irq, void *dev_id)
                                ieee80211_queue_work(sc->hw, &sc->reset_work);
                        }
                        else
-                               tasklet_schedule(&sc->rxtq);
+                               ath5k_schedule_rx(sc);
                } else {
                        if (status & AR5K_INT_SWBA) {
                                tasklet_hi_schedule(&sc->beacontq);
@@ -2179,10 +2221,10 @@ ath5k_intr(int irq, void *dev_id)
                                ath5k_hw_update_tx_triglevel(ah, true);
                        }
                        if (status & (AR5K_INT_RXOK | AR5K_INT_RXERR))
-                               tasklet_schedule(&sc->rxtq);
+                               ath5k_schedule_rx(sc);
                        if (status & (AR5K_INT_TXOK | AR5K_INT_TXDESC
                                        | AR5K_INT_TXERR | AR5K_INT_TXEOL))
-                               tasklet_schedule(&sc->txtq);
+                               ath5k_schedule_tx(sc);
                        if (status & AR5K_INT_BMISS) {
                                /* TODO */
                        }
@@ -2201,6 +2243,9 @@ ath5k_intr(int irq, void *dev_id)
 
        } while (ath5k_hw_is_intr_pending(ah) && --counter > 0);
 
+       if (sc->rx_pending || sc->tx_pending)
+               ath5k_set_current_imask(sc);
+
        if (unlikely(!counter))
                ATH5K_WARN(sc, "too many interrupts, giving up for now\n");
 
@@ -2354,7 +2399,7 @@ ath5k_init_softc(struct ath5k_softc *sc, const struct ath_bus_ops *bus_ops)
        spin_lock_init(&sc->rxbuflock);
        spin_lock_init(&sc->txbuflock);
        spin_lock_init(&sc->block);
-
+       spin_lock_init(&sc->irqlock);
 
        /* Setup interrupt handler */
        ret = request_irq(sc->irq, ath5k_intr, IRQF_SHARED, "ath", sc);
@@ -2572,6 +2617,8 @@ done:
 
 static void stop_tasklets(struct ath5k_softc *sc)
 {
+       sc->rx_pending = false;
+       sc->tx_pending = false;
        tasklet_kill(&sc->rxtq);
        tasklet_kill(&sc->txtq);
        tasklet_kill(&sc->calib);
@@ -2644,6 +2691,7 @@ ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan,
        struct ath5k_hw *ah = sc->ah;
        struct ath_common *common = ath5k_hw_common(ah);
        int ret, ani_mode;
+       bool fast;
 
        ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "resetting\n");
 
@@ -2663,7 +2711,10 @@ ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan,
        ath5k_drain_tx_buffs(sc);
        if (chan)
                sc->curchan = chan;
-       ret = ath5k_hw_reset(ah, sc->opmode, sc->curchan, chan != NULL,
+
+       fast = ((chan != NULL) && modparam_fastchanswitch) ? 1 : 0;
+
+       ret = ath5k_hw_reset(ah, sc->opmode, sc->curchan, fast,
                                                                skip_pcu);
        if (ret) {
                ATH5K_ERR(sc, "can't reset hardware (%d)\n", ret);
@@ -2838,7 +2889,7 @@ ath5k_init(struct ieee80211_hw *hw)
        INIT_WORK(&sc->reset_work, ath5k_reset_work);
        INIT_DELAYED_WORK(&sc->tx_complete_work, ath5k_tx_complete_poll_work);
 
-       ret = ath5k_eeprom_read_mac(ah, mac);
+       ret = ath5k_hw_common(ah)->bus_ops->eeprom_read_mac(ah, mac);
        if (ret) {
                ATH5K_ERR(sc, "unable to read address from EEPROM\n");
                goto err_queues;
@@ -2898,7 +2949,6 @@ ath5k_deinit_softc(struct ath5k_softc *sc)
         * XXX: ??? detach ath5k_hw ???
         * Other than that, it's straightforward...
         */
-       ath5k_debug_finish_device(sc);
        ieee80211_unregister_hw(hw);
        ath5k_desc_free(sc);
        ath5k_txq_release(sc);