ath9k: Enable dynamic power save in ath9k.
[pandora-kernel.git] / drivers / net / wireless / ath9k / main.c
index 727f067..b494a0d 100644 (file)
@@ -28,72 +28,42 @@ MODULE_DESCRIPTION("Support for Atheros 802.11n wireless LAN cards.");
 MODULE_SUPPORTED_DEVICE("Atheros 802.11n WLAN cards");
 MODULE_LICENSE("Dual BSD/GPL");
 
-static struct pci_device_id ath_pci_id_table[] __devinitdata = {
-       { PCI_VDEVICE(ATHEROS, 0x0023) }, /* PCI   */
-       { PCI_VDEVICE(ATHEROS, 0x0024) }, /* PCI-E */
-       { PCI_VDEVICE(ATHEROS, 0x0027) }, /* PCI   */
-       { PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI   */
-       { PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */
-       { PCI_VDEVICE(ATHEROS, 0x002B) }, /* PCI-E */
-       { 0 }
-};
-
-static void ath_detach(struct ath_softc *sc);
-
-/* return bus cachesize in 4B word units */
-
-static void bus_read_cachesize(struct ath_softc *sc, int *csz)
+static void ath_cache_conf_rate(struct ath_softc *sc,
+                               struct ieee80211_conf *conf)
 {
-       u8 u8tmp;
-
-       pci_read_config_byte(sc->pdev, PCI_CACHE_LINE_SIZE, (u8 *)&u8tmp);
-       *csz = (int)u8tmp;
-
-       /*
-        * This check was put in to avoid "unplesant" consequences if
-        * the bootrom has not fully initialized all PCI devices.
-        * Sometimes the cache line size register is not set
-        */
-
-       if (*csz == 0)
-               *csz = DEFAULT_CACHELINE >> 2;   /* Use the default size */
-}
-
-static void ath_setcurmode(struct ath_softc *sc, enum wireless_mode mode)
-{
-       sc->cur_rate_table = sc->hw_rate_table[mode];
-       /*
-        * All protection frames are transmited at 2Mb/s for
-        * 11g, otherwise at 1Mb/s.
-        * XXX select protection rate index from rate table.
-        */
-       sc->sc_protrix = (mode == ATH9K_MODE_11G ? 1 : 0);
-}
-
-static enum wireless_mode ath_chan2mode(struct ath9k_channel *chan)
-{
-       if (chan->chanmode == CHANNEL_A)
-               return ATH9K_MODE_11A;
-       else if (chan->chanmode == CHANNEL_G)
-               return ATH9K_MODE_11G;
-       else if (chan->chanmode == CHANNEL_B)
-               return ATH9K_MODE_11B;
-       else if (chan->chanmode == CHANNEL_A_HT20)
-               return ATH9K_MODE_11NA_HT20;
-       else if (chan->chanmode == CHANNEL_G_HT20)
-               return ATH9K_MODE_11NG_HT20;
-       else if (chan->chanmode == CHANNEL_A_HT40PLUS)
-               return ATH9K_MODE_11NA_HT40PLUS;
-       else if (chan->chanmode == CHANNEL_A_HT40MINUS)
-               return ATH9K_MODE_11NA_HT40MINUS;
-       else if (chan->chanmode == CHANNEL_G_HT40PLUS)
-               return ATH9K_MODE_11NG_HT40PLUS;
-       else if (chan->chanmode == CHANNEL_G_HT40MINUS)
-               return ATH9K_MODE_11NG_HT40MINUS;
-
-       WARN_ON(1); /* should not get here */
-
-       return ATH9K_MODE_11B;
+       switch (conf->channel->band) {
+       case IEEE80211_BAND_2GHZ:
+               if (conf_is_ht20(conf))
+                       sc->cur_rate_table =
+                         sc->hw_rate_table[ATH9K_MODE_11NG_HT20];
+               else if (conf_is_ht40_minus(conf))
+                       sc->cur_rate_table =
+                         sc->hw_rate_table[ATH9K_MODE_11NG_HT40MINUS];
+               else if (conf_is_ht40_plus(conf))
+                       sc->cur_rate_table =
+                         sc->hw_rate_table[ATH9K_MODE_11NG_HT40PLUS];
+               else
+                       sc->cur_rate_table =
+                         sc->hw_rate_table[ATH9K_MODE_11G];
+               break;
+       case IEEE80211_BAND_5GHZ:
+               if (conf_is_ht20(conf))
+                       sc->cur_rate_table =
+                         sc->hw_rate_table[ATH9K_MODE_11NA_HT20];
+               else if (conf_is_ht40_minus(conf))
+                       sc->cur_rate_table =
+                         sc->hw_rate_table[ATH9K_MODE_11NA_HT40MINUS];
+               else if (conf_is_ht40_plus(conf))
+                       sc->cur_rate_table =
+                         sc->hw_rate_table[ATH9K_MODE_11NA_HT40PLUS];
+               else
+                       sc->cur_rate_table =
+                         sc->hw_rate_table[ATH9K_MODE_11A];
+               break;
+       default:
+               BUG_ON(1);
+               break;
+       }
 }
 
 static void ath_update_txpow(struct ath_softc *sc)
@@ -217,6 +187,7 @@ static int ath_setup_channels(struct ath_softc *sc)
                        chan_2ghz[a].band = IEEE80211_BAND_2GHZ;
                        chan_2ghz[a].center_freq = c->channel;
                        chan_2ghz[a].max_power = c->maxTxPower;
+                       c->chan = &chan_2ghz[a];
 
                        if (c->privFlags & CHANNEL_DISALLOW_ADHOC)
                                chan_2ghz[a].flags |= IEEE80211_CHAN_NO_IBSS;
@@ -232,6 +203,7 @@ static int ath_setup_channels(struct ath_softc *sc)
                        chan_5ghz[b].band = IEEE80211_BAND_5GHZ;
                        chan_5ghz[b].center_freq = c->channel;
                        chan_5ghz[b].max_power = c->maxTxPower;
+                       c->chan = &chan_5ghz[a];
 
                        if (c->privFlags & CHANNEL_DISALLOW_ADHOC)
                                chan_5ghz[b].flags |= IEEE80211_CHAN_NO_IBSS;
@@ -258,68 +230,66 @@ static int ath_set_channel(struct ath_softc *sc, struct ath9k_channel *hchan)
 {
        struct ath_hal *ah = sc->sc_ah;
        bool fastcc = true, stopped;
+       struct ieee80211_hw *hw = sc->hw;
+       struct ieee80211_channel *channel = hw->conf.channel;
+       int r;
 
        if (sc->sc_flags & SC_OP_INVALID)
                return -EIO;
 
-       if (hchan->channel != sc->sc_ah->ah_curchan->channel ||
-           hchan->channelFlags != sc->sc_ah->ah_curchan->channelFlags ||
-           (sc->sc_flags & SC_OP_CHAINMASK_UPDATE) ||
-           (sc->sc_flags & SC_OP_FULL_RESET)) {
-               int status;
-               /*
-                * This is only performed if the channel settings have
-                * actually changed.
-                *
-                * To switch channels clear any pending DMA operations;
-                * wait long enough for the RX fifo to drain, reset the
-                * hardware at the new frequency, and then re-enable
-                * the relevant bits of the h/w.
-                */
-               ath9k_hw_set_interrupts(ah, 0);
-               ath_draintxq(sc, false);
-               stopped = ath_stoprecv(sc);
-
-               /* XXX: do not flush receive queue here. We don't want
-                * to flush data frames already in queue because of
-                * changing channel. */
-
-               if (!stopped || (sc->sc_flags & SC_OP_FULL_RESET))
-                       fastcc = false;
-
-               DPRINTF(sc, ATH_DBG_CONFIG,
-                       "(%u MHz) -> (%u MHz), cflags:%x, chanwidth: %d\n",
-                       sc->sc_ah->ah_curchan->channel,
-                       hchan->channel, hchan->channelFlags, sc->tx_chan_width);
-
-               spin_lock_bh(&sc->sc_resetlock);
-               if (!ath9k_hw_reset(ah, hchan, sc->tx_chan_width,
-                                   sc->sc_tx_chainmask, sc->sc_rx_chainmask,
-                                   sc->sc_ht_extprotspacing, fastcc, &status)) {
-                       DPRINTF(sc, ATH_DBG_FATAL,
-                               "Unable to reset channel %u (%uMhz) "
-                               "flags 0x%x hal status %u\n",
-                               ath9k_hw_mhz2ieee(ah, hchan->channel,
-                                                 hchan->channelFlags),
-                               hchan->channel, hchan->channelFlags, status);
-                       spin_unlock_bh(&sc->sc_resetlock);
-                       return -EIO;
-               }
-               spin_unlock_bh(&sc->sc_resetlock);
+       ath9k_ps_wakeup(sc);
 
-               sc->sc_flags &= ~SC_OP_CHAINMASK_UPDATE;
-               sc->sc_flags &= ~SC_OP_FULL_RESET;
+       /*
+        * This is only performed if the channel settings have
+        * actually changed.
+        *
+        * To switch channels clear any pending DMA operations;
+        * wait long enough for the RX fifo to drain, reset the
+        * hardware at the new frequency, and then re-enable
+        * the relevant bits of the h/w.
+        */
+       ath9k_hw_set_interrupts(ah, 0);
+       ath_drain_all_txq(sc, false);
+       stopped = ath_stoprecv(sc);
 
-               if (ath_startrecv(sc) != 0) {
-                       DPRINTF(sc, ATH_DBG_FATAL,
-                               "Unable to restart recv logic\n");
-                       return -EIO;
-               }
+       /* XXX: do not flush receive queue here. We don't want
+        * to flush data frames already in queue because of
+        * changing channel. */
+
+       if (!stopped || (sc->sc_flags & SC_OP_FULL_RESET))
+               fastcc = false;
+
+       DPRINTF(sc, ATH_DBG_CONFIG,
+               "(%u MHz) -> (%u MHz), chanwidth: %d\n",
+               sc->sc_ah->ah_curchan->channel,
+               channel->center_freq, sc->tx_chan_width);
+
+       spin_lock_bh(&sc->sc_resetlock);
+
+       r = ath9k_hw_reset(ah, hchan, fastcc);
+       if (r) {
+               DPRINTF(sc, ATH_DBG_FATAL,
+                       "Unable to reset channel (%u Mhz) "
+                       "reset status %u\n",
+                       channel->center_freq, r);
+               spin_unlock_bh(&sc->sc_resetlock);
+               return r;
+       }
+       spin_unlock_bh(&sc->sc_resetlock);
 
-               ath_setcurmode(sc, ath_chan2mode(hchan));
-               ath_update_txpow(sc);
-               ath9k_hw_set_interrupts(ah, sc->sc_imask);
+       sc->sc_flags &= ~SC_OP_CHAINMASK_UPDATE;
+       sc->sc_flags &= ~SC_OP_FULL_RESET;
+
+       if (ath_startrecv(sc) != 0) {
+               DPRINTF(sc, ATH_DBG_FATAL,
+                       "Unable to restart recv logic\n");
+               return -EIO;
        }
+
+       ath_cache_conf_rate(sc, &hw->conf);
+       ath_update_txpow(sc);
+       ath9k_hw_set_interrupts(ah, sc->sc_imask);
+       ath9k_ps_restore(sc);
        return 0;
 }
 
@@ -369,8 +339,7 @@ static void ath_ani_calibrate(unsigned long data)
        } else {
                if ((timestamp - sc->sc_ani.sc_resetcal_timer) >=
                    ATH_RESTART_CALINTERVAL) {
-                       ath9k_hw_reset_calvalid(ah, ah->ah_curchan,
-                                               &sc->sc_ani.sc_caldone);
+                       sc->sc_ani.sc_caldone = ath9k_hw_reset_calvalid(ah);
                        if (sc->sc_ani.sc_caldone)
                                sc->sc_ani.sc_resetcal_timer = timestamp;
                }
@@ -434,12 +403,14 @@ static void ath_ani_calibrate(unsigned long data)
 /*
  * Update tx/rx chainmask. For legacy association,
  * hard code chainmask to 1x1, for 11n association, use
- * the chainmask configuration.
+ * the chainmask configuration, for bt coexistence, use
+ * the chainmask configuration even in legacy mode.
  */
 static void ath_update_chainmask(struct ath_softc *sc, int is_ht)
 {
        sc->sc_flags |= SC_OP_CHAINMASK_UPDATE;
-       if (is_ht) {
+       if (is_ht ||
+           (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_BT_COEX)) {
                sc->sc_tx_chainmask = sc->sc_ah->ah_caps.tx_chainmask;
                sc->sc_rx_chainmask = sc->sc_ah->ah_caps.rx_chainmask;
        } else {
@@ -499,7 +470,7 @@ static void ath9k_tasklet(unsigned long data)
        ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_imask);
 }
 
-static irqreturn_t ath_isr(int irq, void *dev)
+irqreturn_t ath_isr(int irq, void *dev)
 {
        struct ath_softc *sc = dev;
        struct ath_hal *ah = sc->sc_ah;
@@ -591,8 +562,10 @@ static irqreturn_t ath_isr(int irq, void *dev)
                                      ATH9K_HW_CAP_AUTOSLEEP)) {
                                        /* Clear RxAbort bit so that we can
                                         * receive frames */
+                                       ath9k_hw_setpower(ah, ATH9K_PM_AWAKE);
                                        ath9k_hw_setrxabort(ah, 0);
                                        sched = true;
+                                       sc->sc_flags |= SC_OP_WAIT_FOR_BEACON;
                                }
                        }
                }
@@ -797,7 +770,7 @@ static int ath_reserve_key_cache_slot(struct ath_softc *sc)
 }
 
 static int ath_key_config(struct ath_softc *sc,
-                         const u8 *addr,
+                         struct ieee80211_sta *sta,
                          struct ieee80211_key_conf *key)
 {
        struct ath9k_keyval hk;
@@ -818,7 +791,7 @@ static int ath_key_config(struct ath_softc *sc,
                hk.kv_type = ATH9K_CIPHER_AES_CCM;
                break;
        default:
-               return -EINVAL;
+               return -EOPNOTSUPP;
        }
 
        hk.kv_len = key->keylen;
@@ -831,7 +804,10 @@ static int ath_key_config(struct ath_softc *sc,
        } else if (key->keyidx) {
                struct ieee80211_vif *vif;
 
-               mac = addr;
+               if (WARN_ON(!sta))
+                       return -EOPNOTSUPP;
+               mac = sta->addr;
+
                vif = sc->sc_vaps[0];
                if (vif->type != NL80211_IFTYPE_AP) {
                        /* Only keyidx 0 should be used with unicast key, but
@@ -840,13 +816,16 @@ static int ath_key_config(struct ath_softc *sc,
                } else
                        return -EIO;
        } else {
-               mac = addr;
+               if (WARN_ON(!sta))
+                       return -EOPNOTSUPP;
+               mac = sta->addr;
+
                if (key->alg == ALG_TKIP)
                        idx = ath_reserve_key_cache_slot_tkip(sc);
                else
                        idx = ath_reserve_key_cache_slot(sc);
                if (idx < 0)
-                       return -EIO; /* no free key cache entries */
+                       return -ENOSPC; /* no free key cache entries */
        }
 
        if (key->alg == ALG_TKIP)
@@ -1067,23 +1046,19 @@ fail:
 static void ath_radio_enable(struct ath_softc *sc)
 {
        struct ath_hal *ah = sc->sc_ah;
-       int status;
+       struct ieee80211_channel *channel = sc->hw->conf.channel;
+       int r;
 
+       ath9k_ps_wakeup(sc);
        spin_lock_bh(&sc->sc_resetlock);
-       if (!ath9k_hw_reset(ah, ah->ah_curchan,
-                           sc->tx_chan_width,
-                           sc->sc_tx_chainmask,
-                           sc->sc_rx_chainmask,
-                           sc->sc_ht_extprotspacing,
-                           false, &status)) {
+
+       r = ath9k_hw_reset(ah, ah->ah_curchan, false);
+
+       if (r) {
                DPRINTF(sc, ATH_DBG_FATAL,
-                       "Unable to reset channel %u (%uMhz) "
-                       "flags 0x%x hal status %u\n",
-                       ath9k_hw_mhz2ieee(ah,
-                                         ah->ah_curchan->channel,
-                                         ah->ah_curchan->channelFlags),
-                       ah->ah_curchan->channel,
-                       ah->ah_curchan->channelFlags, status);
+                       "Unable to reset channel %u (%uMhz) ",
+                       "reset status %u\n",
+                       channel->center_freq, r);
        }
        spin_unlock_bh(&sc->sc_resetlock);
 
@@ -1106,14 +1081,16 @@ static void ath_radio_enable(struct ath_softc *sc)
        ath9k_hw_set_gpio(ah, ATH_LED_PIN, 0);
 
        ieee80211_wake_queues(sc->hw);
+       ath9k_ps_restore(sc);
 }
 
 static void ath_radio_disable(struct ath_softc *sc)
 {
        struct ath_hal *ah = sc->sc_ah;
-       int status;
-
+       struct ieee80211_channel *channel = sc->hw->conf.channel;
+       int r;
 
+       ath9k_ps_wakeup(sc);
        ieee80211_stop_queues(sc->hw);
 
        /* Disable LED */
@@ -1123,30 +1100,23 @@ static void ath_radio_disable(struct ath_softc *sc)
        /* Disable interrupts */
        ath9k_hw_set_interrupts(ah, 0);
 
-       ath_draintxq(sc, false);        /* clear pending tx frames */
+       ath_drain_all_txq(sc, false);   /* clear pending tx frames */
        ath_stoprecv(sc);               /* turn off frame recv */
        ath_flushrecv(sc);              /* flush recv queue */
 
        spin_lock_bh(&sc->sc_resetlock);
-       if (!ath9k_hw_reset(ah, ah->ah_curchan,
-                           sc->tx_chan_width,
-                           sc->sc_tx_chainmask,
-                           sc->sc_rx_chainmask,
-                           sc->sc_ht_extprotspacing,
-                           false, &status)) {
+       r = ath9k_hw_reset(ah, ah->ah_curchan, false);
+       if (r) {
                DPRINTF(sc, ATH_DBG_FATAL,
                        "Unable to reset channel %u (%uMhz) "
-                       "flags 0x%x hal status %u\n",
-                       ath9k_hw_mhz2ieee(ah,
-                               ah->ah_curchan->channel,
-                               ah->ah_curchan->channelFlags),
-                       ah->ah_curchan->channel,
-                       ah->ah_curchan->channelFlags, status);
+                       "reset status %u\n",
+                       channel->center_freq, r);
        }
        spin_unlock_bh(&sc->sc_resetlock);
 
        ath9k_hw_phy_disable(ah);
        ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP);
+       ath9k_ps_restore(sc);
 }
 
 static bool ath_is_rfkill_set(struct ath_softc *sc)
@@ -1274,13 +1244,7 @@ static int ath_start_rfkill_poll(struct ath_softc *sc)
                        rfkill_free(sc->rf_kill.rfkill);
 
                        /* Deinitialize the device */
-                       ath_detach(sc);
-                       if (sc->pdev->irq)
-                               free_irq(sc->pdev->irq, sc);
-                       pci_iounmap(sc->pdev, sc->mem);
-                       pci_release_region(sc->pdev, 0);
-                       pci_disable_device(sc->pdev);
-                       ieee80211_free_hw(sc->hw);
+                       ath_cleanup(sc);
                        return -EIO;
                } else {
                        sc->sc_flags |= SC_OP_RFKILL_REGISTERED;
@@ -1291,11 +1255,21 @@ static int ath_start_rfkill_poll(struct ath_softc *sc)
 }
 #endif /* CONFIG_RFKILL */
 
-static void ath_detach(struct ath_softc *sc)
+void ath_cleanup(struct ath_softc *sc)
+{
+       ath_detach(sc);
+       free_irq(sc->irq, sc);
+       ath_bus_cleanup(sc);
+       ieee80211_free_hw(sc->hw);
+}
+
+void ath_detach(struct ath_softc *sc)
 {
        struct ieee80211_hw *hw = sc->hw;
        int i = 0;
 
+       ath9k_ps_wakeup(sc);
+
        DPRINTF(sc, ATH_DBG_CONFIG, "Detach ATH hw\n");
 
 #if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
@@ -1320,6 +1294,7 @@ static void ath_detach(struct ath_softc *sc)
 
        ath9k_hw_detach(sc->sc_ah);
        ath9k_exit_debug(sc);
+       ath9k_ps_restore(sc);
 }
 
 static int ath_init(u16 devid, struct ath_softc *sc)
@@ -1345,14 +1320,14 @@ static int ath_init(u16 devid, struct ath_softc *sc)
         * Cache line size is used to size and align various
         * structures used to communicate with the hardware.
         */
-       bus_read_cachesize(sc, &csz);
+       ath_read_cachesize(sc, &csz);
        /* XXX assert csz is non-zero */
        sc->sc_cachelsz = csz << 2;     /* convert to bytes */
 
        ah = ath9k_hw_attach(devid, sc, sc->mem, &status);
        if (ah == NULL) {
                DPRINTF(sc, ATH_DBG_FATAL,
-                       "Unable to attach hardware; HAL status %u\n", status);
+                       "Unable to attach hardware; HAL status %d\n", status);
                error = -ENXIO;
                goto bad;
        }
@@ -1529,6 +1504,9 @@ static int ath_init(u16 devid, struct ath_softc *sc)
                sc->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ;
        }
 
+       if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_BT_COEX)
+               ath9k_hw_btcoex_enable(sc->sc_ah);
+
        return 0;
 bad2:
        /* cleanup tx queues */
@@ -1542,7 +1520,7 @@ bad:
        return error;
 }
 
-static int ath_attach(u16 devid, struct ath_softc *sc)
+int ath_attach(u16 devid, struct ath_softc *sc)
 {
        struct ieee80211_hw *hw = sc->hw;
        int error = 0;
@@ -1560,7 +1538,12 @@ static int ath_attach(u16 devid, struct ath_softc *sc)
        hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
                IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
                IEEE80211_HW_SIGNAL_DBM |
-               IEEE80211_HW_AMPDU_AGGREGATION;
+               IEEE80211_HW_AMPDU_AGGREGATION |
+               IEEE80211_HW_SUPPORTS_PS |
+               IEEE80211_HW_PS_NULLFUNC_STACK;
+
+       if (AR_SREV_9160_10_OR_LATER(sc->sc_ah))
+               hw->flags |= IEEE80211_HW_MFP_CAPABLE;
 
        hw->wiphy->interface_modes =
                BIT(NL80211_IFTYPE_AP) |
@@ -1619,23 +1602,19 @@ detach:
 int ath_reset(struct ath_softc *sc, bool retry_tx)
 {
        struct ath_hal *ah = sc->sc_ah;
-       int status;
-       int error = 0;
+       struct ieee80211_hw *hw = sc->hw;
+       int r;
 
        ath9k_hw_set_interrupts(ah, 0);
-       ath_draintxq(sc, retry_tx);
+       ath_drain_all_txq(sc, retry_tx);
        ath_stoprecv(sc);
        ath_flushrecv(sc);
 
        spin_lock_bh(&sc->sc_resetlock);
-       if (!ath9k_hw_reset(ah, sc->sc_ah->ah_curchan,
-                           sc->tx_chan_width,
-                           sc->sc_tx_chainmask, sc->sc_rx_chainmask,
-                           sc->sc_ht_extprotspacing, false, &status)) {
+       r = ath9k_hw_reset(ah, sc->sc_ah->ah_curchan, false);
+       if (r)
                DPRINTF(sc, ATH_DBG_FATAL,
-                       "Unable to reset hardware; hal status %u\n", status);
-               error = -EIO;
-       }
+                       "Unable to reset hardware; reset status %u\n", r);
        spin_unlock_bh(&sc->sc_resetlock);
 
        if (ath_startrecv(sc) != 0)
@@ -1646,7 +1625,7 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)
         * that changes the channel so update any state that
         * might change as a result.
         */
-       ath_setcurmode(sc, ath_chan2mode(sc->sc_ah->ah_curchan));
+       ath_cache_conf_rate(sc, &hw->conf);
 
        ath_update_txpow(sc);
 
@@ -1666,7 +1645,7 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)
                }
        }
 
-       return error;
+       return r;
 }
 
 /*
@@ -1720,9 +1699,8 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
        }
 
        /* allocate descriptors */
-       dd->dd_desc = pci_alloc_consistent(sc->pdev,
-                             dd->dd_desc_len,
-                             &dd->dd_desc_paddr);
+       dd->dd_desc = dma_alloc_coherent(sc->dev, dd->dd_desc_len,
+                                        &dd->dd_desc_paddr, GFP_ATOMIC);
        if (dd->dd_desc == NULL) {
                error = -ENOMEM;
                goto fail;
@@ -1768,8 +1746,8 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
        }
        return 0;
 fail2:
-       pci_free_consistent(sc->pdev,
-               dd->dd_desc_len, dd->dd_desc, dd->dd_desc_paddr);
+       dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc,
+                         dd->dd_desc_paddr);
 fail:
        memset(dd, 0, sizeof(*dd));
        return error;
@@ -1782,8 +1760,8 @@ void ath_descdma_cleanup(struct ath_softc *sc,
                         struct ath_descdma *dd,
                         struct list_head *head)
 {
-       pci_free_consistent(sc->pdev,
-               dd->dd_desc_len, dd->dd_desc, dd->dd_desc_paddr);
+       dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc,
+                         dd->dd_desc_paddr);
 
        INIT_LIST_HEAD(head);
        kfree(dd->dd_bufptr);
@@ -1849,7 +1827,7 @@ static int ath9k_start(struct ieee80211_hw *hw)
        struct ath_softc *sc = hw->priv;
        struct ieee80211_channel *curchan = hw->conf.channel;
        struct ath9k_channel *init_channel;
-       int error = 0, pos, status;
+       int r, pos;
 
        DPRINTF(sc, ATH_DBG_CONFIG, "Starting driver with "
                "initial channel: %d MHz\n", curchan->center_freq);
@@ -1859,8 +1837,7 @@ static int ath9k_start(struct ieee80211_hw *hw)
        pos = ath_get_channel(sc, curchan);
        if (pos == -1) {
                DPRINTF(sc, ATH_DBG_FATAL, "Invalid channel: %d\n", curchan->center_freq);
-               error = -EINVAL;
-               goto error;
+               return -EINVAL;
        }
 
        sc->tx_chan_width = ATH9K_HT_MACMODE_20;
@@ -1879,17 +1856,14 @@ static int ath9k_start(struct ieee80211_hw *hw)
         * and then setup of the interrupt mask.
         */
        spin_lock_bh(&sc->sc_resetlock);
-       if (!ath9k_hw_reset(sc->sc_ah, init_channel,
-                           sc->tx_chan_width,
-                           sc->sc_tx_chainmask, sc->sc_rx_chainmask,
-                           sc->sc_ht_extprotspacing, false, &status)) {
+       r = ath9k_hw_reset(sc->sc_ah, init_channel, false);
+       if (r) {
                DPRINTF(sc, ATH_DBG_FATAL,
-                       "Unable to reset hardware; hal status %u "
-                       "(freq %u flags 0x%x)\n", status,
-                       init_channel->channel, init_channel->channelFlags);
-               error = -EIO;
+                       "Unable to reset hardware; reset status %u "
+                       "(freq %u MHz)\n", r,
+                       curchan->center_freq);
                spin_unlock_bh(&sc->sc_resetlock);
-               goto error;
+               return r;
        }
        spin_unlock_bh(&sc->sc_resetlock);
 
@@ -1909,8 +1883,7 @@ static int ath9k_start(struct ieee80211_hw *hw)
        if (ath_startrecv(sc) != 0) {
                DPRINTF(sc, ATH_DBG_FATAL,
                        "Unable to start recv logic\n");
-               error = -EIO;
-               goto error;
+               return -EIO;
        }
 
        /* Setup our intr mask. */
@@ -1943,7 +1916,7 @@ static int ath9k_start(struct ieee80211_hw *hw)
            !sc->sc_config.swBeaconProcess)
                sc->sc_imask |= ATH9K_INT_TIM;
 
-       ath_setcurmode(sc, ath_chan2mode(init_channel));
+       ath_cache_conf_rate(sc, &hw->conf);
 
        sc->sc_flags &= ~SC_OP_INVALID;
 
@@ -1954,11 +1927,9 @@ static int ath9k_start(struct ieee80211_hw *hw)
        ieee80211_wake_queues(sc->hw);
 
 #if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
-       error = ath_start_rfkill_poll(sc);
+       r = ath_start_rfkill_poll(sc);
 #endif
-
-error:
-       return error;
+       return r;
 }
 
 static int ath9k_tx(struct ieee80211_hw *hw,
@@ -2031,7 +2002,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
        ath9k_hw_set_interrupts(sc->sc_ah, 0);
 
        if (!(sc->sc_flags & SC_OP_INVALID)) {
-               ath_draintxq(sc, false);
+               ath_drain_all_txq(sc, false);
                ath_stoprecv(sc);
                ath9k_hw_phy_disable(sc->sc_ah);
        } else
@@ -2133,8 +2104,28 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
        struct ieee80211_conf *conf = &hw->conf;
 
        mutex_lock(&sc->mutex);
-       if (changed & (IEEE80211_CONF_CHANGE_CHANNEL |
-                      IEEE80211_CONF_CHANGE_HT)) {
+       if (changed & IEEE80211_CONF_CHANGE_PS) {
+               if (conf->flags & IEEE80211_CONF_PS) {
+                       if ((sc->sc_imask & ATH9K_INT_TIM_TIMER) == 0) {
+                               sc->sc_imask |= ATH9K_INT_TIM_TIMER;
+                               ath9k_hw_set_interrupts(sc->sc_ah,
+                                               sc->sc_imask);
+                       }
+                       ath9k_hw_setrxabort(sc->sc_ah, 1);
+                       ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP);
+               } else {
+                       ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE);
+                       ath9k_hw_setrxabort(sc->sc_ah, 0);
+                       sc->sc_flags &= ~SC_OP_WAIT_FOR_BEACON;
+                       if (sc->sc_imask & ATH9K_INT_TIM_TIMER) {
+                               sc->sc_imask &= ~ATH9K_INT_TIM_TIMER;
+                               ath9k_hw_set_interrupts(sc->sc_ah,
+                                               sc->sc_imask);
+                       }
+               }
+       }
+
+       if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
                struct ieee80211_channel *curchan = hw->conf.channel;
                int pos;
 
@@ -2154,17 +2145,16 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
                        (curchan->band == IEEE80211_BAND_2GHZ) ?
                        CHANNEL_G : CHANNEL_A;
 
-               if (conf->ht.enabled) {
-                       if (conf->ht.channel_type == NL80211_CHAN_HT40PLUS ||
-                           conf->ht.channel_type == NL80211_CHAN_HT40MINUS)
+               if (conf_is_ht(conf)) {
+                       if (conf_is_ht40(conf))
                                sc->tx_chan_width = ATH9K_HT_MACMODE_2040;
 
                        sc->sc_ah->ah_channels[pos].chanmode =
                                ath_get_extchanmode(sc, curchan,
-                                                   conf->ht.channel_type);
+                                                   conf->channel_type);
                }
 
-               ath_update_chainmask(sc, conf->ht.enabled);
+               ath_update_chainmask(sc, conf_is_ht(conf));
 
                if (ath_set_channel(sc, &sc->sc_ah->ah_channels[pos]) < 0) {
                        DPRINTF(sc, ATH_DBG_FATAL, "Unable to set channel\n");
@@ -2348,24 +2338,27 @@ static int ath9k_conf_tx(struct ieee80211_hw *hw,
 
 static int ath9k_set_key(struct ieee80211_hw *hw,
                         enum set_key_cmd cmd,
-                        const u8 *local_addr,
-                        const u8 *addr,
+                        struct ieee80211_vif *vif,
+                        struct ieee80211_sta *sta,
                         struct ieee80211_key_conf *key)
 {
        struct ath_softc *sc = hw->priv;
        int ret = 0;
 
+       ath9k_ps_wakeup(sc);
        DPRINTF(sc, ATH_DBG_KEYCACHE, "Set HW Key\n");
 
        switch (cmd) {
        case SET_KEY:
-               ret = ath_key_config(sc, addr, key);
+               ret = ath_key_config(sc, sta, key);
                if (ret >= 0) {
                        key->hw_key_idx = ret;
                        /* push IV and Michael MIC generation to stack */
                        key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
                        if (key->alg == ALG_TKIP)
                                key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+                       if (sc->sc_ah->sw_mgmt_crypto && key->alg == ALG_CCMP)
+                               key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
                        ret = 0;
                }
                break;
@@ -2376,6 +2369,7 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
                ret = -EINVAL;
        }
 
+       ath9k_ps_restore(sc);
        return ret;
 }
 
@@ -2472,7 +2466,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw,
        return ret;
 }
 
-static struct ieee80211_ops ath9k_ops = {
+struct ieee80211_ops ath9k_ops = {
        .tx                 = ath9k_tx,
        .start              = ath9k_start,
        .stop               = ath9k_stop,
@@ -2516,7 +2510,7 @@ static struct {
 /*
  * Return the MAC/BB name. "????" is returned if the MAC/BB is unknown.
  */
-static const char *
+const char *
 ath_mac_bb_name(u32 mac_bb_version)
 {
        int i;
@@ -2533,7 +2527,7 @@ ath_mac_bb_name(u32 mac_bb_version)
 /*
  * Return the RF name. "????" is returned if the RF is unknown.
  */
-static const char *
+const char *
 ath_rf_name(u16 rf_version)
 {
        int i;
@@ -2547,223 +2541,7 @@ ath_rf_name(u16 rf_version)
        return "????";
 }
 
-static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
-{
-       void __iomem *mem;
-       struct ath_softc *sc;
-       struct ieee80211_hw *hw;
-       u8 csz;
-       u32 val;
-       int ret = 0;
-       struct ath_hal *ah;
-
-       if (pci_enable_device(pdev))
-               return -EIO;
-
-       ret =  pci_set_dma_mask(pdev, DMA_32BIT_MASK);
-
-       if (ret) {
-               printk(KERN_ERR "ath9k: 32-bit DMA not available\n");
-               goto bad;
-       }
-
-       ret = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
-
-       if (ret) {
-               printk(KERN_ERR "ath9k: 32-bit DMA consistent "
-                       "DMA enable failed\n");
-               goto bad;
-       }
-
-       /*
-        * Cache line size is used to size and align various
-        * structures used to communicate with the hardware.
-        */
-       pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &csz);
-       if (csz == 0) {
-               /*
-                * Linux 2.4.18 (at least) writes the cache line size
-                * register as a 16-bit wide register which is wrong.
-                * We must have this setup properly for rx buffer
-                * DMA to work so force a reasonable value here if it
-                * comes up zero.
-                */
-               csz = L1_CACHE_BYTES / sizeof(u32);
-               pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, csz);
-       }
-       /*
-        * The default setting of latency timer yields poor results,
-        * set it to the value used by other systems. It may be worth
-        * tweaking this setting more.
-        */
-       pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8);
-
-       pci_set_master(pdev);
-
-       /*
-        * Disable the RETRY_TIMEOUT register (0x41) to keep
-        * PCI Tx retries from interfering with C3 CPU state.
-        */
-       pci_read_config_dword(pdev, 0x40, &val);
-       if ((val & 0x0000ff00) != 0)
-               pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
-
-       ret = pci_request_region(pdev, 0, "ath9k");
-       if (ret) {
-               dev_err(&pdev->dev, "PCI memory region reserve error\n");
-               ret = -ENODEV;
-               goto bad;
-       }
-
-       mem = pci_iomap(pdev, 0, 0);
-       if (!mem) {
-               printk(KERN_ERR "PCI memory map error\n") ;
-               ret = -EIO;
-               goto bad1;
-       }
-
-       hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops);
-       if (hw == NULL) {
-               printk(KERN_ERR "ath_pci: no memory for ieee80211_hw\n");
-               goto bad2;
-       }
-
-       SET_IEEE80211_DEV(hw, &pdev->dev);
-       pci_set_drvdata(pdev, hw);
-
-       sc = hw->priv;
-       sc->hw = hw;
-       sc->pdev = pdev;
-       sc->mem = mem;
-
-       if (ath_attach(id->device, sc) != 0) {
-               ret = -ENODEV;
-               goto bad3;
-       }
-
-       /* setup interrupt service routine */
-
-       if (request_irq(pdev->irq, ath_isr, IRQF_SHARED, "ath", sc)) {
-               printk(KERN_ERR "%s: request_irq failed\n",
-                       wiphy_name(hw->wiphy));
-               ret = -EIO;
-               goto bad4;
-       }
-
-       ah = sc->sc_ah;
-       printk(KERN_INFO
-              "%s: Atheros AR%s MAC/BB Rev:%x "
-              "AR%s RF Rev:%x: mem=0x%lx, irq=%d\n",
-              wiphy_name(hw->wiphy),
-              ath_mac_bb_name(ah->ah_macVersion),
-              ah->ah_macRev,
-              ath_rf_name((ah->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR)),
-              ah->ah_phyRev,
-              (unsigned long)mem, pdev->irq);
-
-       return 0;
-bad4:
-       ath_detach(sc);
-bad3:
-       ieee80211_free_hw(hw);
-bad2:
-       pci_iounmap(pdev, mem);
-bad1:
-       pci_release_region(pdev, 0);
-bad:
-       pci_disable_device(pdev);
-       return ret;
-}
-
-static void ath_pci_remove(struct pci_dev *pdev)
-{
-       struct ieee80211_hw *hw = pci_get_drvdata(pdev);
-       struct ath_softc *sc = hw->priv;
-
-       ath_detach(sc);
-       if (pdev->irq)
-               free_irq(pdev->irq, sc);
-       pci_iounmap(pdev, sc->mem);
-       pci_release_region(pdev, 0);
-       pci_disable_device(pdev);
-       ieee80211_free_hw(hw);
-}
-
-#ifdef CONFIG_PM
-
-static int ath_pci_suspend(struct pci_dev *pdev, pm_message_t state)
-{
-       struct ieee80211_hw *hw = pci_get_drvdata(pdev);
-       struct ath_softc *sc = hw->priv;
-
-       ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
-
-#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
-       if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
-               cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll);
-#endif
-
-       pci_save_state(pdev);
-       pci_disable_device(pdev);
-       pci_set_power_state(pdev, 3);
-
-       return 0;
-}
-
-static int ath_pci_resume(struct pci_dev *pdev)
-{
-       struct ieee80211_hw *hw = pci_get_drvdata(pdev);
-       struct ath_softc *sc = hw->priv;
-       u32 val;
-       int err;
-
-       err = pci_enable_device(pdev);
-       if (err)
-               return err;
-       pci_restore_state(pdev);
-       /*
-        * Suspend/Resume resets the PCI configuration space, so we have to
-        * re-disable the RETRY_TIMEOUT register (0x41) to keep
-        * PCI Tx retries from interfering with C3 CPU state
-        */
-       pci_read_config_dword(pdev, 0x40, &val);
-       if ((val & 0x0000ff00) != 0)
-               pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
-
-       /* Enable LED */
-       ath9k_hw_cfg_output(sc->sc_ah, ATH_LED_PIN,
-                           AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
-       ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
-
-#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
-       /*
-        * check the h/w rfkill state on resume
-        * and start the rfkill poll timer
-        */
-       if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
-               queue_delayed_work(sc->hw->workqueue,
-                                  &sc->rf_kill.rfkill_poll, 0);
-#endif
-
-       return 0;
-}
-
-#endif /* CONFIG_PM */
-
-MODULE_DEVICE_TABLE(pci, ath_pci_id_table);
-
-static struct pci_driver ath_pci_driver = {
-       .name       = "ath9k",
-       .id_table   = ath_pci_id_table,
-       .probe      = ath_pci_probe,
-       .remove     = ath_pci_remove,
-#ifdef CONFIG_PM
-       .suspend    = ath_pci_suspend,
-       .resume     = ath_pci_resume,
-#endif /* CONFIG_PM */
-};
-
-static int __init init_ath_pci(void)
+static int __init ath9k_init(void)
 {
        int error;
 
@@ -2775,26 +2553,40 @@ static int __init init_ath_pci(void)
                printk(KERN_ERR
                        "Unable to register rate control algorithm: %d\n",
                        error);
-               ath_rate_control_unregister();
-               return error;
+               goto err_out;
        }
 
-       if (pci_register_driver(&ath_pci_driver) < 0) {
+       error = ath_pci_init();
+       if (error < 0) {
                printk(KERN_ERR
                        "ath_pci: No devices found, driver not installed.\n");
-               ath_rate_control_unregister();
-               pci_unregister_driver(&ath_pci_driver);
-               return -ENODEV;
+               error = -ENODEV;
+               goto err_rate_unregister;
+       }
+
+       error = ath_ahb_init();
+       if (error < 0) {
+               error = -ENODEV;
+               goto err_pci_exit;
        }
 
        return 0;
+
+ err_pci_exit:
+       ath_pci_exit();
+
+ err_rate_unregister:
+       ath_rate_control_unregister();
+ err_out:
+       return error;
 }
-module_init(init_ath_pci);
+module_init(ath9k_init);
 
-static void __exit exit_ath_pci(void)
+static void __exit ath9k_exit(void)
 {
+       ath_ahb_exit();
+       ath_pci_exit();
        ath_rate_control_unregister();
-       pci_unregister_driver(&ath_pci_driver);
        printk(KERN_INFO "%s: Driver unloaded\n", dev_info);
 }
-module_exit(exit_ath_pci);
+module_exit(ath9k_exit);