]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/net/wireless/ath/ath5k/reset.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / drivers / net / wireless / ath / ath5k / reset.c
index 5b179d01f97db16c64bddc97fc0ade0005bf57ee..84206898f77d89daa5821a9afeece34b275f3f67 100644 (file)
 
 #include <linux/pci.h>                 /* To determine if a card is pci-e */
 #include <linux/log2.h>
+#include <linux/platform_device.h>
 #include "ath5k.h"
 #include "reg.h"
 #include "base.h"
 #include "debug.h"
 
+
+/******************\
+* Helper functions *
+\******************/
+
 /*
  * Check if a register write has been completed
  */
@@ -53,146 +59,267 @@ int ath5k_hw_register_timeout(struct ath5k_hw *ah, u32 reg, u32 flag, u32 val,
        return (i <= 0) ? -EAGAIN : 0;
 }
 
+
+/*************************\
+* Clock related functions *
+\*************************/
+
 /**
- * ath5k_hw_write_ofdm_timings - set OFDM timings on AR5212
+ * ath5k_hw_htoclock - Translate usec to hw clock units
  *
- * @ah: the &struct ath5k_hw
- * @channel: the currently set channel upon reset
- *
- * Write the delta slope coefficient (used on pilot tracking ?) for OFDM
- * operation on the AR5212 upon reset. This is a helper for ath5k_hw_reset().
+ * @ah: The &struct ath5k_hw
+ * @usec: value in microseconds
+ */
+unsigned int ath5k_hw_htoclock(struct ath5k_hw *ah, unsigned int usec)
+{
+       struct ath_common *common = ath5k_hw_common(ah);
+       return usec * common->clockrate;
+}
+
+/**
+ * ath5k_hw_clocktoh - Translate hw clock units to usec
+ * @clock: value in hw clock units
+ */
+unsigned int ath5k_hw_clocktoh(struct ath5k_hw *ah, unsigned int clock)
+{
+       struct ath_common *common = ath5k_hw_common(ah);
+       return clock / common->clockrate;
+}
+
+/**
+ * ath5k_hw_init_core_clock - Initialize core clock
  *
- * Since delta slope is floating point we split it on its exponent and
- * mantissa and provide these values on hw.
+ * @ah The &struct ath5k_hw
  *
- * For more infos i think this patent is related
- * http://www.freepatentsonline.com/7184495.html
+ * Initialize core clock parameters (usec, usec32, latencies etc).
  */
-static inline int ath5k_hw_write_ofdm_timings(struct ath5k_hw *ah,
-       struct ieee80211_channel *channel)
+static void ath5k_hw_init_core_clock(struct ath5k_hw *ah)
 {
-       /* Get exponent and mantissa and set it */
-       u32 coef_scaled, coef_exp, coef_man,
-               ds_coef_exp, ds_coef_man, clock;
-
-       BUG_ON(!(ah->ah_version == AR5K_AR5212) ||
-               !(channel->hw_value & CHANNEL_OFDM));
-
-       /* Get coefficient
-        * ALGO: coef = (5 * clock / carrier_freq) / 2
-        * we scale coef by shifting clock value by 24 for
-        * better precision since we use integers */
-       /* TODO: Half/quarter rate */
-       clock =  (channel->hw_value & CHANNEL_TURBO) ? 80 : 40;
-       coef_scaled = ((5 * (clock << 24)) / 2) / channel->center_freq;
-
-       /* Get exponent
-        * ALGO: coef_exp = 14 - highest set bit position */
-       coef_exp = ilog2(coef_scaled);
-
-       /* Doesn't make sense if it's zero*/
-       if (!coef_scaled || !coef_exp)
-               return -EINVAL;
+       struct ieee80211_channel *channel = ah->ah_current_channel;
+       struct ath_common *common = ath5k_hw_common(ah);
+       u32 usec_reg, txlat, rxlat, usec, clock, sclock, txf2txs;
+
+       /*
+        * Set core clock frequency
+        */
+       if (channel->hw_value & CHANNEL_5GHZ)
+               clock = 40; /* 802.11a */
+       else if (channel->hw_value & CHANNEL_CCK)
+               clock = 22; /* 802.11b */
+       else
+               clock = 44; /* 802.11g */
+
+       /* Use clock multiplier for non-default
+        * bwmode */
+       switch (ah->ah_bwmode) {
+       case AR5K_BWMODE_40MHZ:
+               clock *= 2;
+               break;
+       case AR5K_BWMODE_10MHZ:
+               clock /= 2;
+               break;
+       case AR5K_BWMODE_5MHZ:
+               clock /= 4;
+               break;
+       default:
+               break;
+       }
 
-       /* Note: we've shifted coef_scaled by 24 */
-       coef_exp = 14 - (coef_exp - 24);
+       common->clockrate = clock;
 
+       /*
+        * Set USEC parameters
+        */
+       /* Set USEC counter on PCU*/
+       usec = clock - 1;
+       usec = AR5K_REG_SM(usec, AR5K_USEC_1);
 
-       /* Get mantissa (significant digits)
-        * ALGO: coef_mant = floor(coef_scaled* 2^coef_exp+0.5) */
-       coef_man = coef_scaled +
-               (1 << (24 - coef_exp - 1));
+       /* Set usec duration on DCU */
+       if (ah->ah_version != AR5K_AR5210)
+               AR5K_REG_WRITE_BITS(ah, AR5K_DCU_GBL_IFS_MISC,
+                                       AR5K_DCU_GBL_IFS_MISC_USEC_DUR,
+                                       clock);
 
-       /* Calculate delta slope coefficient exponent
-        * and mantissa (remove scaling) and set them on hw */
-       ds_coef_man = coef_man >> (24 - coef_exp);
-       ds_coef_exp = coef_exp - 16;
+       /* Set 32MHz USEC counter */
+       if ((ah->ah_radio == AR5K_RF5112) ||
+               (ah->ah_radio == AR5K_RF5413) ||
+               (ah->ah_radio == AR5K_RF2316) ||
+               (ah->ah_radio == AR5K_RF2317))
+       /* Remain on 40MHz clock ? */
+               sclock = 40 - 1;
+       else
+               sclock = 32 - 1;
+       sclock = AR5K_REG_SM(sclock, AR5K_USEC_32);
 
-       AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_3,
-               AR5K_PHY_TIMING_3_DSC_MAN, ds_coef_man);
-       AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_3,
-               AR5K_PHY_TIMING_3_DSC_EXP, ds_coef_exp);
+       /*
+        * Set tx/rx latencies
+        */
+       usec_reg = ath5k_hw_reg_read(ah, AR5K_USEC_5211);
+       txlat = AR5K_REG_MS(usec_reg, AR5K_USEC_TX_LATENCY_5211);
+       rxlat = AR5K_REG_MS(usec_reg, AR5K_USEC_RX_LATENCY_5211);
 
-       return 0;
-}
+       /*
+        * 5210 initvals don't include usec settings
+        * so we need to use magic values here for
+        * tx/rx latencies
+        */
+       if (ah->ah_version == AR5K_AR5210) {
+               /* same for turbo */
+               txlat = AR5K_INIT_TX_LATENCY_5210;
+               rxlat = AR5K_INIT_RX_LATENCY_5210;
+       }
 
+       if (ah->ah_mac_srev < AR5K_SREV_AR5211) {
+               /* 5311 has different tx/rx latency masks
+                * from 5211, since we deal 5311 the same
+                * as 5211 when setting initvals, shift
+                * values here to their proper locations
+                *
+                * Note: Initvals indicate tx/rx/ latencies
+                * are the same for turbo mode */
+               txlat = AR5K_REG_SM(txlat, AR5K_USEC_TX_LATENCY_5210);
+               rxlat = AR5K_REG_SM(rxlat, AR5K_USEC_RX_LATENCY_5210);
+       } else
+       switch (ah->ah_bwmode) {
+       case AR5K_BWMODE_10MHZ:
+               txlat = AR5K_REG_SM(txlat * 2,
+                               AR5K_USEC_TX_LATENCY_5211);
+               rxlat = AR5K_REG_SM(AR5K_INIT_RX_LAT_MAX,
+                               AR5K_USEC_RX_LATENCY_5211);
+               txf2txs = AR5K_INIT_TXF2TXD_START_DELAY_10MHZ;
+               break;
+       case AR5K_BWMODE_5MHZ:
+               txlat = AR5K_REG_SM(txlat * 4,
+                               AR5K_USEC_TX_LATENCY_5211);
+               rxlat = AR5K_REG_SM(AR5K_INIT_RX_LAT_MAX,
+                               AR5K_USEC_RX_LATENCY_5211);
+               txf2txs = AR5K_INIT_TXF2TXD_START_DELAY_5MHZ;
+               break;
+       case AR5K_BWMODE_40MHZ:
+               txlat = AR5K_INIT_TX_LAT_MIN;
+               rxlat = AR5K_REG_SM(rxlat / 2,
+                               AR5K_USEC_RX_LATENCY_5211);
+               txf2txs = AR5K_INIT_TXF2TXD_START_DEFAULT;
+               break;
+       default:
+               break;
+       }
 
-/*
- * index into rates for control rates, we can set it up like this because
- * this is only used for AR5212 and we know it supports G mode
- */
-static const unsigned int control_rates[] =
-       { 0, 1, 1, 1, 4, 4, 6, 6, 8, 8, 8, 8 };
+       usec_reg = (usec | sclock | txlat | rxlat);
+       ath5k_hw_reg_write(ah, usec_reg, AR5K_USEC);
 
-/**
- * ath5k_hw_write_rate_duration - fill rate code to duration table
- *
- * @ah: the &struct ath5k_hw
- * @mode: one of enum ath5k_driver_mode
- *
- * Write the rate code to duration table upon hw reset. This is a helper for
- * ath5k_hw_reset(). It seems all this is doing is setting an ACK timeout on
- * the hardware, based on current mode, for each rate. The rates which are
- * capable of short preamble (802.11b rates 2Mbps, 5.5Mbps, and 11Mbps) have
- * different rate code so we write their value twice (one for long preample
- * and one for short).
+       /* On 5112 set tx frane to tx data start delay */
+       if (ah->ah_radio == AR5K_RF5112) {
+               AR5K_REG_WRITE_BITS(ah, AR5K_PHY_RF_CTL2,
+                                       AR5K_PHY_RF_CTL2_TXF2TXD_START,
+                                       txf2txs);
+       }
+}
+
+/*
+ * If there is an external 32KHz crystal available, use it
+ * as ref. clock instead of 32/40MHz clock and baseband clocks
+ * to save power during sleep or restore normal 32/40MHz
+ * operation.
  *
- * Note: Band doesn't matter here, if we set the values for OFDM it works
- * on both a and g modes. So all we have to do is set values for all g rates
- * that include all OFDM and CCK rates. If we operate in turbo or xr/half/
- * quarter rate mode, we need to use another set of bitrates (that's why we
- * need the mode parameter) but we don't handle these proprietary modes yet.
+ * XXX: When operating on 32KHz certain PHY registers (27 - 31,
+ *     123 - 127) require delay on access.
  */
-static inline void ath5k_hw_write_rate_duration(struct ath5k_hw *ah,
-       unsigned int mode)
+static void ath5k_hw_set_sleep_clock(struct ath5k_hw *ah, bool enable)
 {
-       struct ath5k_softc *sc = ah->ah_sc;
-       struct ieee80211_rate *rate;
-       unsigned int i;
+       struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+       u32 scal, spending;
+
+       /* Only set 32KHz settings if we have an external
+        * 32KHz crystal present */
+       if ((AR5K_EEPROM_HAS32KHZCRYSTAL(ee->ee_misc1) ||
+       AR5K_EEPROM_HAS32KHZCRYSTAL_OLD(ee->ee_misc1)) &&
+       enable) {
 
-       /* Write rate duration table */
-       for (i = 0; i < sc->sbands[IEEE80211_BAND_2GHZ].n_bitrates; i++) {
-               u32 reg;
-               u16 tx_time;
+               /* 1 usec/cycle */
+               AR5K_REG_WRITE_BITS(ah, AR5K_USEC_5211, AR5K_USEC_32, 1);
+               /* Set up tsf increment on each cycle */
+               AR5K_REG_WRITE_BITS(ah, AR5K_TSF_PARM, AR5K_TSF_PARM_INC, 61);
 
-               rate = &sc->sbands[IEEE80211_BAND_2GHZ].bitrates[control_rates[i]];
+               /* Set baseband sleep control registers
+                * and sleep control rate */
+               ath5k_hw_reg_write(ah, 0x1f, AR5K_PHY_SCR);
+
+               if ((ah->ah_radio == AR5K_RF5112) ||
+               (ah->ah_radio == AR5K_RF5413) ||
+               (ah->ah_radio == AR5K_RF2316) ||
+               (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4)))
+                       spending = 0x14;
+               else
+                       spending = 0x18;
+               ath5k_hw_reg_write(ah, spending, AR5K_PHY_SPENDING);
 
-               /* Set ACK timeout */
-               reg = AR5K_RATE_DUR(rate->hw_value);
+               if ((ah->ah_radio == AR5K_RF5112) ||
+               (ah->ah_radio == AR5K_RF5413) ||
+               (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))) {
+                       ath5k_hw_reg_write(ah, 0x26, AR5K_PHY_SLMT);
+                       ath5k_hw_reg_write(ah, 0x0d, AR5K_PHY_SCAL);
+                       ath5k_hw_reg_write(ah, 0x07, AR5K_PHY_SCLOCK);
+                       ath5k_hw_reg_write(ah, 0x3f, AR5K_PHY_SDELAY);
+                       AR5K_REG_WRITE_BITS(ah, AR5K_PCICFG,
+                               AR5K_PCICFG_SLEEP_CLOCK_RATE, 0x02);
+               } else {
+                       ath5k_hw_reg_write(ah, 0x0a, AR5K_PHY_SLMT);
+                       ath5k_hw_reg_write(ah, 0x0c, AR5K_PHY_SCAL);
+                       ath5k_hw_reg_write(ah, 0x03, AR5K_PHY_SCLOCK);
+                       ath5k_hw_reg_write(ah, 0x20, AR5K_PHY_SDELAY);
+                       AR5K_REG_WRITE_BITS(ah, AR5K_PCICFG,
+                               AR5K_PCICFG_SLEEP_CLOCK_RATE, 0x03);
+               }
 
-               /* An ACK frame consists of 10 bytes. If you add the FCS,
-                * which ieee80211_generic_frame_duration() adds,
-                * its 14 bytes. Note we use the control rate and not the
-                * actual rate for this rate. See mac80211 tx.c
-                * ieee80211_duration() for a brief description of
-                * what rate we should choose to TX ACKs. */
-               tx_time = le16_to_cpu(ieee80211_generic_frame_duration(sc->hw,
-                                                       NULL, 10, rate));
+               /* Enable sleep clock operation */
+               AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG,
+                               AR5K_PCICFG_SLEEP_CLOCK_EN);
 
-               ath5k_hw_reg_write(ah, tx_time, reg);
+       } else {
 
-               if (!(rate->flags & IEEE80211_RATE_SHORT_PREAMBLE))
-                       continue;
+               /* Disable sleep clock operation and
+                * restore default parameters */
+               AR5K_REG_DISABLE_BITS(ah, AR5K_PCICFG,
+                               AR5K_PCICFG_SLEEP_CLOCK_EN);
 
-               /*
-                * We're not distinguishing short preamble here,
-                * This is true, all we'll get is a longer value here
-                * which is not necessarilly bad. We could use
-                * export ieee80211_frame_duration() but that needs to be
-                * fixed first to be properly used by mac802111 drivers:
-                *
-                *  - remove erp stuff and let the routine figure ofdm
-                *    erp rates
-                *  - remove passing argument ieee80211_local as
-                *    drivers don't have access to it
-                *  - move drivers using ieee80211_generic_frame_duration()
-                *    to this
-                */
-               ath5k_hw_reg_write(ah, tx_time,
-                       reg + (AR5K_SET_SHORT_PREAMBLE << 2));
+               AR5K_REG_WRITE_BITS(ah, AR5K_PCICFG,
+                               AR5K_PCICFG_SLEEP_CLOCK_RATE, 0);
+
+               /* Set DAC/ADC delays */
+               ath5k_hw_reg_write(ah, 0x1f, AR5K_PHY_SCR);
+               ath5k_hw_reg_write(ah, AR5K_PHY_SLMT_32MHZ, AR5K_PHY_SLMT);
+
+               if (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))
+                       scal = AR5K_PHY_SCAL_32MHZ_2417;
+               else if (ee->ee_is_hb63)
+                       scal = AR5K_PHY_SCAL_32MHZ_HB63;
+               else
+                       scal = AR5K_PHY_SCAL_32MHZ;
+               ath5k_hw_reg_write(ah, scal, AR5K_PHY_SCAL);
+
+               ath5k_hw_reg_write(ah, AR5K_PHY_SCLOCK_32MHZ, AR5K_PHY_SCLOCK);
+               ath5k_hw_reg_write(ah, AR5K_PHY_SDELAY_32MHZ, AR5K_PHY_SDELAY);
+
+               if ((ah->ah_radio == AR5K_RF5112) ||
+               (ah->ah_radio == AR5K_RF5413) ||
+               (ah->ah_radio == AR5K_RF2316) ||
+               (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4)))
+                       spending = 0x14;
+               else
+                       spending = 0x18;
+               ath5k_hw_reg_write(ah, spending, AR5K_PHY_SPENDING);
+
+               /* Set up tsf increment on each cycle */
+               AR5K_REG_WRITE_BITS(ah, AR5K_TSF_PARM, AR5K_TSF_PARM_INC, 1);
        }
 }
 
+
+/*********************\
+* Reset/Sleep control *
+\*********************/
+
 /*
  * Reset chipset
  */
@@ -235,6 +362,64 @@ static int ath5k_hw_nic_reset(struct ath5k_hw *ah, u32 val)
        return ret;
 }
 
+/*
+ * Reset AHB chipset
+ * AR5K_RESET_CTL_PCU flag resets WMAC
+ * AR5K_RESET_CTL_BASEBAND flag resets WBB
+ */
+static int ath5k_hw_wisoc_reset(struct ath5k_hw *ah, u32 flags)
+{
+       u32 mask = flags ? flags : ~0U;
+       volatile u32 *reg;
+       u32 regval;
+       u32 val = 0;
+
+       /* ah->ah_mac_srev is not available at this point yet */
+       if (ah->ah_sc->devid >= AR5K_SREV_AR2315_R6) {
+               reg = (u32 *) AR5K_AR2315_RESET;
+               if (mask & AR5K_RESET_CTL_PCU)
+                       val |= AR5K_AR2315_RESET_WMAC;
+               if (mask & AR5K_RESET_CTL_BASEBAND)
+                       val |= AR5K_AR2315_RESET_BB_WARM;
+       } else {
+               reg = (u32 *) AR5K_AR5312_RESET;
+               if (to_platform_device(ah->ah_sc->dev)->id == 0) {
+                       if (mask & AR5K_RESET_CTL_PCU)
+                               val |= AR5K_AR5312_RESET_WMAC0;
+                       if (mask & AR5K_RESET_CTL_BASEBAND)
+                               val |= AR5K_AR5312_RESET_BB0_COLD |
+                                      AR5K_AR5312_RESET_BB0_WARM;
+               } else {
+                       if (mask & AR5K_RESET_CTL_PCU)
+                               val |= AR5K_AR5312_RESET_WMAC1;
+                       if (mask & AR5K_RESET_CTL_BASEBAND)
+                               val |= AR5K_AR5312_RESET_BB1_COLD |
+                                      AR5K_AR5312_RESET_BB1_WARM;
+               }
+       }
+
+       /* Put BB/MAC into reset */
+       regval = __raw_readl(reg);
+       __raw_writel(regval | val, reg);
+       regval = __raw_readl(reg);
+       udelay(100);
+
+       /* Bring BB/MAC out of reset */
+       __raw_writel(regval & ~val, reg);
+       regval = __raw_readl(reg);
+
+       /*
+        * Reset configuration register (for hw byte-swap). Note that this
+        * is only set for big endian. We do the necessary magic in
+        * AR5K_INIT_CFG.
+        */
+       if ((flags & AR5K_RESET_CTL_PCU) == 0)
+               ath5k_hw_reg_write(ah, AR5K_INIT_CFG, AR5K_CFG);
+
+       return 0;
+}
+
+
 /*
  * Sleep control
  */
@@ -334,6 +519,9 @@ int ath5k_hw_on_hold(struct ath5k_hw *ah)
        u32 bus_flags;
        int ret;
 
+       if (ath5k_get_bus_type(ah) == ATH_AHB)
+               return 0;
+
        /* Make sure device is awake */
        ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0);
        if (ret) {
@@ -349,7 +537,7 @@ int ath5k_hw_on_hold(struct ath5k_hw *ah)
         * we ingore that flag for PCI-E cards. On PCI cards
         * this flag gets cleared after 64 PCI clocks.
         */
-       bus_flags = (pdev->is_pcie) ? 0 : AR5K_RESET_CTL_PCI;
+       bus_flags = (pdev && pci_is_pcie(pdev)) ? 0 : AR5K_RESET_CTL_PCI;
 
        if (ah->ah_version == AR5K_AR5210) {
                ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU |
@@ -378,7 +566,6 @@ int ath5k_hw_on_hold(struct ath5k_hw *ah)
 
 /*
  * Bring up MAC + PHY Chips and program PLL
- * TODO: Half/Quarter rate support
  */
 int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
 {
@@ -390,11 +577,13 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
        mode = 0;
        clock = 0;
 
-       /* Wakeup the device */
-       ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0);
-       if (ret) {
-               ATH5K_ERR(ah->ah_sc, "failed to wakeup the MAC Chip\n");
-               return ret;
+       if ((ath5k_get_bus_type(ah) != ATH_AHB) || !initial) {
+               /* Wakeup the device */
+               ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0);
+               if (ret) {
+                       ATH5K_ERR(ah->ah_sc, "failed to wakeup the MAC Chip\n");
+                       return ret;
+               }
        }
 
        /*
@@ -405,7 +594,7 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
         * we ingore that flag for PCI-E cards. On PCI cards
         * this flag gets cleared after 64 PCI clocks.
         */
-       bus_flags = (pdev->is_pcie) ? 0 : AR5K_RESET_CTL_PCI;
+       bus_flags = (pdev && pci_is_pcie(pdev)) ? 0 : AR5K_RESET_CTL_PCI;
 
        if (ah->ah_version == AR5K_AR5210) {
                ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU |
@@ -413,8 +602,12 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
                        AR5K_RESET_CTL_PHY | AR5K_RESET_CTL_PCI);
                        mdelay(2);
        } else {
-               ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU |
-                       AR5K_RESET_CTL_BASEBAND | bus_flags);
+               if (ath5k_get_bus_type(ah) == ATH_AHB)
+                       ret = ath5k_hw_wisoc_reset(ah, AR5K_RESET_CTL_PCU |
+                               AR5K_RESET_CTL_BASEBAND);
+               else
+                       ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU |
+                               AR5K_RESET_CTL_BASEBAND | bus_flags);
        }
 
        if (ret) {
@@ -429,9 +622,15 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
                return ret;
        }
 
-       /* ...clear reset control register and pull device out of
-        * warm reset */
-       if (ath5k_hw_nic_reset(ah, 0)) {
+       /* ...reset configuration regiter on Wisoc ...
+        * ...clear reset control register and pull device out of
+        * warm reset on others */
+       if (ath5k_get_bus_type(ah) == ATH_AHB)
+               ret = ath5k_hw_wisoc_reset(ah, 0);
+       else
+               ret = ath5k_hw_nic_reset(ah, 0);
+
+       if (ret) {
                ATH5K_ERR(ah->ah_sc, "failed to warm reset the MAC Chip\n");
                return -EIO;
        }
@@ -466,7 +665,8 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
                                 * CCK headers) operation. We need to test
                                 * this, 5211 might support ofdm-only g after
                                 * all, there are also initial register values
-                                * in the code for g mode (see initvals.c). */
+                                * in the code for g mode (see initvals.c).
+                                */
                                if (ah->ah_version == AR5K_AR5211)
                                        mode |= AR5K_PHY_MODE_MOD_OFDM;
                                else
@@ -479,6 +679,7 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
                } else if (flags & CHANNEL_5GHZ) {
                        mode |= AR5K_PHY_MODE_FREQ_5GHZ;
 
+                       /* Different PLL setting for 5413 */
                        if (ah->ah_radio == AR5K_RF5413)
                                clock = AR5K_PHY_PLL_40MHZ_5413;
                        else
@@ -496,12 +697,29 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
                        return -EINVAL;
                }
 
-               if (flags & CHANNEL_TURBO)
-                       turbo = AR5K_PHY_TURBO_MODE | AR5K_PHY_TURBO_SHORT;
+               /*XXX: Can bwmode be used with dynamic mode ?
+                * (I don't think it supports 44MHz) */
+               /* On 2425 initvals TURBO_SHORT is not pressent */
+               if (ah->ah_bwmode == AR5K_BWMODE_40MHZ) {
+                       turbo = AR5K_PHY_TURBO_MODE |
+                               (ah->ah_radio == AR5K_RF2425) ? 0 :
+                               AR5K_PHY_TURBO_SHORT;
+               } else if (ah->ah_bwmode != AR5K_BWMODE_DEFAULT) {
+                       if (ah->ah_radio == AR5K_RF5413) {
+                               mode |= (ah->ah_bwmode == AR5K_BWMODE_10MHZ) ?
+                                       AR5K_PHY_MODE_HALF_RATE :
+                                       AR5K_PHY_MODE_QUARTER_RATE;
+                       } else if (ah->ah_version == AR5K_AR5212) {
+                               clock |= (ah->ah_bwmode == AR5K_BWMODE_10MHZ) ?
+                                       AR5K_PHY_PLL_HALF_RATE :
+                                       AR5K_PHY_PLL_QUARTER_RATE;
+                       }
+               }
+
        } else { /* Reset the device */
 
                /* ...enable Atheros turbo mode if requested */
-               if (flags & CHANNEL_TURBO)
+               if (ah->ah_bwmode == AR5K_BWMODE_40MHZ)
                        ath5k_hw_reg_write(ah, AR5K_PHY_TURBO_MODE,
                                        AR5K_PHY_TURBO);
        }
@@ -522,107 +740,10 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
        return 0;
 }
 
-/*
- * If there is an external 32KHz crystal available, use it
- * as ref. clock instead of 32/40MHz clock and baseband clocks
- * to save power during sleep or restore normal 32/40MHz
- * operation.
- *
- * XXX: When operating on 32KHz certain PHY registers (27 - 31,
- *     123 - 127) require delay on access.
- */
-static void ath5k_hw_set_sleep_clock(struct ath5k_hw *ah, bool enable)
-{
-       struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
-       u32 scal, spending, usec32;
 
-       /* Only set 32KHz settings if we have an external
-        * 32KHz crystal present */
-       if ((AR5K_EEPROM_HAS32KHZCRYSTAL(ee->ee_misc1) ||
-       AR5K_EEPROM_HAS32KHZCRYSTAL_OLD(ee->ee_misc1)) &&
-       enable) {
-
-               /* 1 usec/cycle */
-               AR5K_REG_WRITE_BITS(ah, AR5K_USEC_5211, AR5K_USEC_32, 1);
-               /* Set up tsf increment on each cycle */
-               AR5K_REG_WRITE_BITS(ah, AR5K_TSF_PARM, AR5K_TSF_PARM_INC, 61);
-
-               /* Set baseband sleep control registers
-                * and sleep control rate */
-               ath5k_hw_reg_write(ah, 0x1f, AR5K_PHY_SCR);
-
-               if ((ah->ah_radio == AR5K_RF5112) ||
-               (ah->ah_radio == AR5K_RF5413) ||
-               (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4)))
-                       spending = 0x14;
-               else
-                       spending = 0x18;
-               ath5k_hw_reg_write(ah, spending, AR5K_PHY_SPENDING);
-
-               if ((ah->ah_radio == AR5K_RF5112) ||
-               (ah->ah_radio == AR5K_RF5413) ||
-               (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))) {
-                       ath5k_hw_reg_write(ah, 0x26, AR5K_PHY_SLMT);
-                       ath5k_hw_reg_write(ah, 0x0d, AR5K_PHY_SCAL);
-                       ath5k_hw_reg_write(ah, 0x07, AR5K_PHY_SCLOCK);
-                       ath5k_hw_reg_write(ah, 0x3f, AR5K_PHY_SDELAY);
-                       AR5K_REG_WRITE_BITS(ah, AR5K_PCICFG,
-                               AR5K_PCICFG_SLEEP_CLOCK_RATE, 0x02);
-               } else {
-                       ath5k_hw_reg_write(ah, 0x0a, AR5K_PHY_SLMT);
-                       ath5k_hw_reg_write(ah, 0x0c, AR5K_PHY_SCAL);
-                       ath5k_hw_reg_write(ah, 0x03, AR5K_PHY_SCLOCK);
-                       ath5k_hw_reg_write(ah, 0x20, AR5K_PHY_SDELAY);
-                       AR5K_REG_WRITE_BITS(ah, AR5K_PCICFG,
-                               AR5K_PCICFG_SLEEP_CLOCK_RATE, 0x03);
-               }
-
-               /* Enable sleep clock operation */
-               AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG,
-                               AR5K_PCICFG_SLEEP_CLOCK_EN);
-
-       } else {
-
-               /* Disable sleep clock operation and
-                * restore default parameters */
-               AR5K_REG_DISABLE_BITS(ah, AR5K_PCICFG,
-                               AR5K_PCICFG_SLEEP_CLOCK_EN);
-
-               AR5K_REG_WRITE_BITS(ah, AR5K_PCICFG,
-                               AR5K_PCICFG_SLEEP_CLOCK_RATE, 0);
-
-               ath5k_hw_reg_write(ah, 0x1f, AR5K_PHY_SCR);
-               ath5k_hw_reg_write(ah, AR5K_PHY_SLMT_32MHZ, AR5K_PHY_SLMT);
-
-               if (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))
-                       scal = AR5K_PHY_SCAL_32MHZ_2417;
-               else if (ee->ee_is_hb63)
-                       scal = AR5K_PHY_SCAL_32MHZ_HB63;
-               else
-                       scal = AR5K_PHY_SCAL_32MHZ;
-               ath5k_hw_reg_write(ah, scal, AR5K_PHY_SCAL);
-
-               ath5k_hw_reg_write(ah, AR5K_PHY_SCLOCK_32MHZ, AR5K_PHY_SCLOCK);
-               ath5k_hw_reg_write(ah, AR5K_PHY_SDELAY_32MHZ, AR5K_PHY_SDELAY);
-
-               if ((ah->ah_radio == AR5K_RF5112) ||
-               (ah->ah_radio == AR5K_RF5413) ||
-               (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4)))
-                       spending = 0x14;
-               else
-                       spending = 0x18;
-               ath5k_hw_reg_write(ah, spending, AR5K_PHY_SPENDING);
-
-               if ((ah->ah_radio == AR5K_RF5112) ||
-               (ah->ah_radio == AR5K_RF5413))
-                       usec32 = 39;
-               else
-                       usec32 = 31;
-               AR5K_REG_WRITE_BITS(ah, AR5K_USEC_5211, AR5K_USEC_32, usec32);
-
-               AR5K_REG_WRITE_BITS(ah, AR5K_TSF_PARM, AR5K_TSF_PARM_INC, 1);
-       }
-}
+/**************************************\
+* Post-initvals register modifications *
+\**************************************/
 
 /* TODO: Half/Quarter rate */
 static void ath5k_hw_tweak_initval_settings(struct ath5k_hw *ah,
@@ -663,22 +784,10 @@ static void ath5k_hw_tweak_initval_settings(struct ath5k_hw *ah,
                AR5K_REG_DISABLE_BITS(ah, AR5K_TXCFG,
                                AR5K_TXCFG_DCU_DBL_BUF_DIS);
 
-       /* Set DAC/ADC delays */
-       if (ah->ah_version == AR5K_AR5212) {
-               u32 scal;
-               struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
-               if (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))
-                       scal = AR5K_PHY_SCAL_32MHZ_2417;
-               else if (ee->ee_is_hb63)
-                       scal = AR5K_PHY_SCAL_32MHZ_HB63;
-               else
-                       scal = AR5K_PHY_SCAL_32MHZ;
-               ath5k_hw_reg_write(ah, scal, AR5K_PHY_SCAL);
-       }
-
        /* Set fast ADC */
        if ((ah->ah_radio == AR5K_RF5413) ||
-       (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))) {
+               (ah->ah_radio == AR5K_RF2317) ||
+               (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))) {
                u32 fast_adc = true;
 
                if (channel->center_freq == 2462 ||
@@ -706,33 +815,68 @@ static void ath5k_hw_tweak_initval_settings(struct ath5k_hw *ah,
        }
 
        if (ah->ah_mac_srev < AR5K_SREV_AR5211) {
-               u32 usec_reg;
-               /* 5311 has different tx/rx latency masks
-                * from 5211, since we deal 5311 the same
-                * as 5211 when setting initvals, shift
-                * values here to their proper locations */
-               usec_reg = ath5k_hw_reg_read(ah, AR5K_USEC_5211);
-               ath5k_hw_reg_write(ah, usec_reg & (AR5K_USEC_1 |
-                               AR5K_USEC_32 |
-                               AR5K_USEC_TX_LATENCY_5211 |
-                               AR5K_REG_SM(29,
-                               AR5K_USEC_RX_LATENCY_5210)),
-                               AR5K_USEC_5211);
                /* Clear QCU/DCU clock gating register */
                ath5k_hw_reg_write(ah, 0, AR5K_QCUDCU_CLKGT);
                /* Set DAC/ADC delays */
-               ath5k_hw_reg_write(ah, 0x08, AR5K_PHY_SCAL);
+               ath5k_hw_reg_write(ah, AR5K_PHY_SCAL_32MHZ_5311,
+                                               AR5K_PHY_SCAL);
                /* Enable PCU FIFO corruption ECO */
                AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5211,
                                        AR5K_DIAG_SW_ECO_ENABLE);
        }
+
+       if (ah->ah_bwmode) {
+               /* Increase PHY switch and AGC settling time
+                * on turbo mode (ath5k_hw_commit_eeprom_settings
+                * will override settling time if available) */
+               if (ah->ah_bwmode == AR5K_BWMODE_40MHZ) {
+
+                       AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SETTLING,
+                                               AR5K_PHY_SETTLING_AGC,
+                                               AR5K_AGC_SETTLING_TURBO);
+
+                       /* XXX: Initvals indicate we only increase
+                        * switch time on AR5212, 5211 and 5210
+                        * only change agc time (bug?) */
+                       if (ah->ah_version == AR5K_AR5212)
+                               AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SETTLING,
+                                               AR5K_PHY_SETTLING_SWITCH,
+                                               AR5K_SWITCH_SETTLING_TURBO);
+
+                       if (ah->ah_version == AR5K_AR5210) {
+                               /* Set Frame Control Register */
+                               ath5k_hw_reg_write(ah,
+                                       (AR5K_PHY_FRAME_CTL_INI |
+                                       AR5K_PHY_TURBO_MODE |
+                                       AR5K_PHY_TURBO_SHORT | 0x2020),
+                                       AR5K_PHY_FRAME_CTL_5210);
+                       }
+               /* On 5413 PHY force window length for half/quarter rate*/
+               } else if ((ah->ah_mac_srev >= AR5K_SREV_AR5424) &&
+               (ah->ah_mac_srev <= AR5K_SREV_AR5414)) {
+                       AR5K_REG_WRITE_BITS(ah, AR5K_PHY_FRAME_CTL_5211,
+                                               AR5K_PHY_FRAME_CTL_WIN_LEN,
+                                               3);
+               }
+       } else if (ah->ah_version == AR5K_AR5210) {
+               /* Set Frame Control Register for normal operation */
+               ath5k_hw_reg_write(ah, (AR5K_PHY_FRAME_CTL_INI | 0x1020),
+                                               AR5K_PHY_FRAME_CTL_5210);
+       }
 }
 
 static void ath5k_hw_commit_eeprom_settings(struct ath5k_hw *ah,
-               struct ieee80211_channel *channel, u8 ee_mode)
+               struct ieee80211_channel *channel)
 {
        struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
        s16 cck_ofdm_pwr_delta;
+       u8 ee_mode;
+
+       /* TODO: Add support for AR5210 EEPROM */
+       if (ah->ah_version == AR5K_AR5210)
+               return;
+
+       ee_mode = ath5k_eeprom_mode_from_channel(channel);
 
        /* Adjust power delta for channel 14 */
        if (channel->center_freq == 2484)
@@ -772,7 +916,7 @@ static void ath5k_hw_commit_eeprom_settings(struct ath5k_hw *ah,
                AR5K_PHY_NF_SVAL(ee->ee_noise_floor_thr[ee_mode]),
                AR5K_PHY_NFTHRES);
 
-       if ((channel->hw_value & CHANNEL_TURBO) &&
+       if ((ah->ah_bwmode == AR5K_BWMODE_40MHZ) &&
        (ah->ah_ee_version >= AR5K_EEPROM_VERSION_5_0)) {
                /* Switch settling time (Turbo) */
                AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SETTLING,
@@ -870,143 +1014,172 @@ static void ath5k_hw_commit_eeprom_settings(struct ath5k_hw *ah,
                ath5k_hw_reg_write(ah, 0, AR5K_PHY_HEAVY_CLIP_ENABLE);
 }
 
-/*
- * Main reset function
- */
+
+/*********************\
+* Main reset function *
+\*********************/
+
 int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
-       struct ieee80211_channel *channel, bool change_channel)
+               struct ieee80211_channel *channel, bool fast, bool skip_pcu)
 {
-       struct ath_common *common = ath5k_hw_common(ah);
-       u32 s_seq[10], s_led[3], staid1_flags, tsf_up, tsf_lo;
-       u32 phy_tst1;
-       u8 mode, freq, ee_mode;
+       u32 s_seq[10], s_led[3], tsf_up, tsf_lo;
+       u8 mode;
        int i, ret;
 
-       ee_mode = 0;
-       staid1_flags = 0;
        tsf_up = 0;
        tsf_lo = 0;
-       freq = 0;
        mode = 0;
 
        /*
-        * Save some registers before a reset
+        * Sanity check for fast flag
+        * Fast channel change only available
+        * on AR2413/AR5413.
         */
-       /*DCU/Antenna selection not available on 5210*/
-       if (ah->ah_version != AR5K_AR5210) {
+       if (fast && (ah->ah_radio != AR5K_RF2413) &&
+       (ah->ah_radio != AR5K_RF5413))
+               fast = 0;
 
-               switch (channel->hw_value & CHANNEL_MODES) {
-               case CHANNEL_A:
-                       mode = AR5K_MODE_11A;
-                       freq = AR5K_INI_RFGAIN_5GHZ;
-                       ee_mode = AR5K_EEPROM_MODE_11A;
-                       break;
-               case CHANNEL_G:
-                       mode = AR5K_MODE_11G;
-                       freq = AR5K_INI_RFGAIN_2GHZ;
-                       ee_mode = AR5K_EEPROM_MODE_11G;
-                       break;
-               case CHANNEL_B:
-                       mode = AR5K_MODE_11B;
-                       freq = AR5K_INI_RFGAIN_2GHZ;
-                       ee_mode = AR5K_EEPROM_MODE_11B;
-                       break;
-               case CHANNEL_T:
-                       mode = AR5K_MODE_11A_TURBO;
-                       freq = AR5K_INI_RFGAIN_5GHZ;
-                       ee_mode = AR5K_EEPROM_MODE_11A;
-                       break;
-               case CHANNEL_TG:
-                       if (ah->ah_version == AR5K_AR5211) {
-                               ATH5K_ERR(ah->ah_sc,
-                                       "TurboG mode not available on 5211");
-                               return -EINVAL;
-                       }
-                       mode = AR5K_MODE_11G_TURBO;
-                       freq = AR5K_INI_RFGAIN_2GHZ;
-                       ee_mode = AR5K_EEPROM_MODE_11G;
-                       break;
-               case CHANNEL_XR:
-                       if (ah->ah_version == AR5K_AR5211) {
-                               ATH5K_ERR(ah->ah_sc,
-                                       "XR mode not available on 5211");
-                               return -EINVAL;
-                       }
-                       mode = AR5K_MODE_XR;
-                       freq = AR5K_INI_RFGAIN_5GHZ;
-                       ee_mode = AR5K_EEPROM_MODE_11A;
-                       break;
-               default:
+       /* Disable sleep clock operation
+        * to avoid register access delay on certain
+        * PHY registers */
+       if (ah->ah_version == AR5K_AR5212)
+               ath5k_hw_set_sleep_clock(ah, false);
+
+       /*
+        * Stop PCU
+        */
+       ath5k_hw_stop_rx_pcu(ah);
+
+       /*
+        * Stop DMA
+        *
+        * Note: If DMA didn't stop continue
+        * since only a reset will fix it.
+        */
+       ret = ath5k_hw_dma_stop(ah);
+
+       /* RF Bus grant won't work if we have pending
+        * frames */
+       if (ret && fast) {
+               ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_RESET,
+                       "DMA didn't stop, falling back to normal reset\n");
+               fast = 0;
+               /* Non fatal, just continue with
+                * normal reset */
+               ret = 0;
+       }
+
+       switch (channel->hw_value & CHANNEL_MODES) {
+       case CHANNEL_A:
+               mode = AR5K_MODE_11A;
+               break;
+       case CHANNEL_G:
+
+               if (ah->ah_version <= AR5K_AR5211) {
                        ATH5K_ERR(ah->ah_sc,
-                               "invalid channel: %d\n", channel->center_freq);
+                               "G mode not available on 5210/5211");
                        return -EINVAL;
                }
 
-               if (change_channel) {
-                       /*
-                        * Save frame sequence count
-                        * For revs. after Oahu, only save
-                        * seq num for DCU 0 (Global seq num)
-                        */
-                       if (ah->ah_mac_srev < AR5K_SREV_AR5211) {
-
-                               for (i = 0; i < 10; i++)
-                                       s_seq[i] = ath5k_hw_reg_read(ah,
-                                               AR5K_QUEUE_DCU_SEQNUM(i));
+               mode = AR5K_MODE_11G;
+               break;
+       case CHANNEL_B:
 
-                       } else {
-                               s_seq[0] = ath5k_hw_reg_read(ah,
-                                               AR5K_QUEUE_DCU_SEQNUM(0));
-                       }
+               if (ah->ah_version < AR5K_AR5211) {
+                       ATH5K_ERR(ah->ah_sc,
+                               "B mode not available on 5210");
+                       return -EINVAL;
+               }
 
-                       /* TSF accelerates on AR5211 during reset
-                        * As a workaround save it here and restore
-                        * it later so that it's back in time after
-                        * reset. This way it'll get re-synced on the
-                        * next beacon without breaking ad-hoc.
-                        *
-                        * On AR5212 TSF is almost preserved across a
-                        * reset so it stays back in time anyway and
-                        * we don't have to save/restore it.
-                        *
-                        * XXX: Since this breaks power saving we have
-                        * to disable power saving until we receive the
-                        * next beacon, so we can resync beacon timers */
-                       if (ah->ah_version == AR5K_AR5211) {
-                               tsf_up = ath5k_hw_reg_read(ah, AR5K_TSF_U32);
-                               tsf_lo = ath5k_hw_reg_read(ah, AR5K_TSF_L32);
-                       }
+               mode = AR5K_MODE_11B;
+               break;
+       case CHANNEL_XR:
+               if (ah->ah_version == AR5K_AR5211) {
+                       ATH5K_ERR(ah->ah_sc,
+                               "XR mode not available on 5211");
+                       return -EINVAL;
                }
+               mode = AR5K_MODE_XR;
+               break;
+       default:
+               ATH5K_ERR(ah->ah_sc,
+                       "invalid channel: %d\n", channel->center_freq);
+               return -EINVAL;
+       }
 
-               if (ah->ah_version == AR5K_AR5212) {
-                       /* Restore normal 32/40MHz clock operation
-                        * to avoid register access delay on certain
-                        * PHY registers */
-                       ath5k_hw_set_sleep_clock(ah, false);
+       /*
+        * If driver requested fast channel change and DMA has stopped
+        * go on. If it fails continue with a normal reset.
+        */
+       if (fast) {
+               ret = ath5k_hw_phy_init(ah, channel, mode, true);
+               if (ret) {
+                       ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_RESET,
+                               "fast chan change failed, falling back to normal reset\n");
+                       /* Non fatal, can happen eg.
+                        * on mode change */
+                       ret = 0;
+               } else
+                       return 0;
+       }
 
-                       /* Since we are going to write rf buffer
-                        * check if we have any pending gain_F
-                        * optimization settings */
-                       if (change_channel && ah->ah_rf_banks != NULL)
-                               ath5k_hw_gainf_calibrate(ah);
+       /*
+        * Save some registers before a reset
+        */
+       if (ah->ah_version != AR5K_AR5210) {
+               /*
+                * Save frame sequence count
+                * For revs. after Oahu, only save
+                * seq num for DCU 0 (Global seq num)
+                */
+               if (ah->ah_mac_srev < AR5K_SREV_AR5211) {
+
+                       for (i = 0; i < 10; i++)
+                               s_seq[i] = ath5k_hw_reg_read(ah,
+                                       AR5K_QUEUE_DCU_SEQNUM(i));
+
+               } else {
+                       s_seq[0] = ath5k_hw_reg_read(ah,
+                                       AR5K_QUEUE_DCU_SEQNUM(0));
+               }
+
+               /* TSF accelerates on AR5211 during reset
+                * As a workaround save it here and restore
+                * it later so that it's back in time after
+                * reset. This way it'll get re-synced on the
+                * next beacon without breaking ad-hoc.
+                *
+                * On AR5212 TSF is almost preserved across a
+                * reset so it stays back in time anyway and
+                * we don't have to save/restore it.
+                *
+                * XXX: Since this breaks power saving we have
+                * to disable power saving until we receive the
+                * next beacon, so we can resync beacon timers */
+               if (ah->ah_version == AR5K_AR5211) {
+                       tsf_up = ath5k_hw_reg_read(ah, AR5K_TSF_U32);
+                       tsf_lo = ath5k_hw_reg_read(ah, AR5K_TSF_L32);
                }
        }
 
+
        /*GPIOs*/
        s_led[0] = ath5k_hw_reg_read(ah, AR5K_PCICFG) &
                                        AR5K_PCICFG_LEDSTATE;
        s_led[1] = ath5k_hw_reg_read(ah, AR5K_GPIOCR);
        s_led[2] = ath5k_hw_reg_read(ah, AR5K_GPIODO);
 
-       /* AR5K_STA_ID1 flags, only preserve antenna
-        * settings and ack/cts rate mode */
-       staid1_flags = ath5k_hw_reg_read(ah, AR5K_STA_ID1) &
-                       (AR5K_STA_ID1_DEFAULT_ANTENNA |
-                       AR5K_STA_ID1_DESC_ANTENNA |
-                       AR5K_STA_ID1_RTS_DEF_ANTENNA |
-                       AR5K_STA_ID1_ACKCTS_6MB |
-                       AR5K_STA_ID1_BASE_RATE_11B |
-                       AR5K_STA_ID1_SELFGEN_DEF_ANT);
+
+       /*
+        * Since we are going to write rf buffer
+        * check if we have any pending gain_F
+        * optimization settings
+        */
+       if (ah->ah_version == AR5K_AR5212 &&
+       (ah->ah_radio <= AR5K_RF5112)) {
+               if (!fast && ah->ah_rf_banks != NULL)
+                               ath5k_hw_gainf_calibrate(ah);
+       }
 
        /* Wakeup the device */
        ret = ath5k_hw_nic_wakeup(ah, channel->hw_value, false);
@@ -1021,121 +1194,42 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
                                                        AR5K_PHY(0));
 
        /* Write initial settings */
-       ret = ath5k_hw_write_initvals(ah, mode, change_channel);
+       ret = ath5k_hw_write_initvals(ah, mode, skip_pcu);
        if (ret)
                return ret;
 
+       /* Initialize core clock settings */
+       ath5k_hw_init_core_clock(ah);
+
        /*
-        * 5211/5212 Specific
+        * Tweak initval settings for revised
+        * chipsets and add some more config
+        * bits
         */
-       if (ah->ah_version != AR5K_AR5210) {
-
-               /*
-                * Write initial RF gain settings
-                * This should work for both 5111/5112
-                */
-               ret = ath5k_hw_rfgain_init(ah, freq);
-               if (ret)
-                       return ret;
-
-               mdelay(1);
-
-               /*
-                * Tweak initval settings for revised
-                * chipsets and add some more config
-                * bits
-                */
-               ath5k_hw_tweak_initval_settings(ah, channel);
-
-               /*
-                * Set TX power
-                */
-               ret = ath5k_hw_txpower(ah, channel, ee_mode,
-                                       ah->ah_txpower.txp_max_pwr / 2);
-               if (ret)
-                       return ret;
+       ath5k_hw_tweak_initval_settings(ah, channel);
 
-               /* Write rate duration table only on AR5212 and if
-                * virtual interface has already been brought up
-                * XXX: rethink this after new mode changes to
-                * mac80211 are integrated */
-               if (ah->ah_version == AR5K_AR5212 &&
-                       ah->ah_sc->nvifs)
-                       ath5k_hw_write_rate_duration(ah, mode);
+       /* Commit values from EEPROM */
+       ath5k_hw_commit_eeprom_settings(ah, channel);
 
-               /*
-                * Write RF buffer
-                */
-               ret = ath5k_hw_rfregs_init(ah, channel, mode);
-               if (ret)
-                       return ret;
-
-
-               /* Write OFDM timings on 5212*/
-               if (ah->ah_version == AR5K_AR5212 &&
-                       channel->hw_value & CHANNEL_OFDM) {
-
-                       ret = ath5k_hw_write_ofdm_timings(ah, channel);
-                       if (ret)
-                               return ret;
-
-                       /* Spur info is available only from EEPROM versions
-                        * greater than 5.3, but the EEPROM routines will use
-                        * static values for older versions */
-                       if (ah->ah_mac_srev >= AR5K_SREV_AR5424)
-                               ath5k_hw_set_spur_mitigation_filter(ah,
-                                                                   channel);
-               }
-
-               /*Enable/disable 802.11b mode on 5111
-               (enable 2111 frequency converter + CCK)*/
-               if (ah->ah_radio == AR5K_RF5111) {
-                       if (mode == AR5K_MODE_11B)
-                               AR5K_REG_ENABLE_BITS(ah, AR5K_TXCFG,
-                                   AR5K_TXCFG_B_MODE);
-                       else
-                               AR5K_REG_DISABLE_BITS(ah, AR5K_TXCFG,
-                                   AR5K_TXCFG_B_MODE);
-               }
-
-               /* Commit values from EEPROM */
-               ath5k_hw_commit_eeprom_settings(ah, channel, ee_mode);
-
-       } else {
-               /*
-                * For 5210 we do all initialization using
-                * initvals, so we don't have to modify
-                * any settings (5210 also only supports
-                * a/aturbo modes)
-                */
-               mdelay(1);
-               /* Disable phy and wait */
-               ath5k_hw_reg_write(ah, AR5K_PHY_ACT_DISABLE, AR5K_PHY_ACT);
-               mdelay(1);
-       }
 
        /*
         * Restore saved values
         */
 
-       /*DCU/Antenna selection not available on 5210*/
+       /* Seqnum, TSF */
        if (ah->ah_version != AR5K_AR5210) {
+               if (ah->ah_mac_srev < AR5K_SREV_AR5211) {
+                       for (i = 0; i < 10; i++)
+                               ath5k_hw_reg_write(ah, s_seq[i],
+                                       AR5K_QUEUE_DCU_SEQNUM(i));
+               } else {
+                       ath5k_hw_reg_write(ah, s_seq[0],
+                               AR5K_QUEUE_DCU_SEQNUM(0));
+               }
 
-               if (change_channel) {
-                       if (ah->ah_mac_srev < AR5K_SREV_AR5211) {
-                               for (i = 0; i < 10; i++)
-                                       ath5k_hw_reg_write(ah, s_seq[i],
-                                               AR5K_QUEUE_DCU_SEQNUM(i));
-                       } else {
-                               ath5k_hw_reg_write(ah, s_seq[0],
-                                       AR5K_QUEUE_DCU_SEQNUM(0));
-                       }
-
-
-                       if (ah->ah_version == AR5K_AR5211) {
-                               ath5k_hw_reg_write(ah, tsf_up, AR5K_TSF_U32);
-                               ath5k_hw_reg_write(ah, tsf_lo, AR5K_TSF_L32);
-                       }
+               if (ah->ah_version == AR5K_AR5211) {
+                       ath5k_hw_reg_write(ah, tsf_up, AR5K_TSF_U32);
+                       ath5k_hw_reg_write(ah, tsf_lo, AR5K_TSF_L32);
                }
        }
 
@@ -1146,203 +1240,34 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
        ath5k_hw_reg_write(ah, s_led[1], AR5K_GPIOCR);
        ath5k_hw_reg_write(ah, s_led[2], AR5K_GPIODO);
 
-       /* Restore sta_id flags and preserve our mac address*/
-       ath5k_hw_reg_write(ah,
-                          get_unaligned_le32(common->macaddr),
-                          AR5K_STA_ID0);
-       ath5k_hw_reg_write(ah,
-                          staid1_flags | get_unaligned_le16(common->macaddr + 4),
-                          AR5K_STA_ID1);
-
-
        /*
-        * Configure PCU
+        * Initialize PCU
         */
-
-       /* Restore bssid and bssid mask */
-       ath5k_hw_set_bssid(ah);
-
-       /* Set PCU config */
-       ath5k_hw_set_opmode(ah, op_mode);
-
-       /* Clear any pending interrupts
-        * PISR/SISR Not available on 5210 */
-       if (ah->ah_version != AR5K_AR5210)
-               ath5k_hw_reg_write(ah, 0xffffffff, AR5K_PISR);
-
-       /* Set RSSI/BRSSI thresholds
-        *
-        * Note: If we decide to set this value
-        * dynamically, keep in mind that when AR5K_RSSI_THR
-        * register is read, it might return 0x40 if we haven't
-        * written anything to it.  Also, BMISS RSSI threshold is zeroed.
-        * So doing a save/restore procedure here isn't the right
-        * choice. Instead, store it in ath5k_hw */
-       ath5k_hw_reg_write(ah, (AR5K_TUNE_RSSI_THRES |
-                               AR5K_TUNE_BMISS_THRES <<
-                               AR5K_RSSI_THR_BMISS_S),
-                               AR5K_RSSI_THR);
-
-       /* MIC QoS support */
-       if (ah->ah_mac_srev >= AR5K_SREV_AR2413) {
-               ath5k_hw_reg_write(ah, 0x000100aa, AR5K_MIC_QOS_CTL);
-               ath5k_hw_reg_write(ah, 0x00003210, AR5K_MIC_QOS_SEL);
-       }
-
-       /* QoS NOACK Policy */
-       if (ah->ah_version == AR5K_AR5212) {
-               ath5k_hw_reg_write(ah,
-                       AR5K_REG_SM(2, AR5K_QOS_NOACK_2BIT_VALUES) |
-                       AR5K_REG_SM(5, AR5K_QOS_NOACK_BIT_OFFSET)  |
-                       AR5K_REG_SM(0, AR5K_QOS_NOACK_BYTE_OFFSET),
-                       AR5K_QOS_NOACK);
-       }
-
+       ath5k_hw_pcu_init(ah, op_mode, mode);
 
        /*
-        * Configure PHY
+        * Initialize PHY
         */
-
-       /* Set channel on PHY */
-       ret = ath5k_hw_channel(ah, channel);
-       if (ret)
+       ret = ath5k_hw_phy_init(ah, channel, mode, false);
+       if (ret) {
+               ATH5K_ERR(ah->ah_sc,
+                       "failed to initialize PHY (%i) !\n", ret);
                return ret;
-
-       /*
-        * Enable the PHY and wait until completion
-        * This includes BaseBand and Synthesizer
-        * activation.
-        */
-       ath5k_hw_reg_write(ah, AR5K_PHY_ACT_ENABLE, AR5K_PHY_ACT);
-
-       /*
-        * On 5211+ read activation -> rx delay
-        * and use it.
-        *
-        * TODO: Half/quarter rate support
-        */
-       if (ah->ah_version != AR5K_AR5210) {
-               u32 delay;
-               delay = ath5k_hw_reg_read(ah, AR5K_PHY_RX_DELAY) &
-                       AR5K_PHY_RX_DELAY_M;
-               delay = (channel->hw_value & CHANNEL_CCK) ?
-                       ((delay << 2) / 22) : (delay / 10);
-
-               udelay(100 + (2 * delay));
-       } else {
-               mdelay(1);
        }
 
-       /*
-        * Perform ADC test to see if baseband is ready
-        * Set TX hold and check ADC test register
-        */
-       phy_tst1 = ath5k_hw_reg_read(ah, AR5K_PHY_TST1);
-       ath5k_hw_reg_write(ah, AR5K_PHY_TST1_TXHOLD, AR5K_PHY_TST1);
-       for (i = 0; i <= 20; i++) {
-               if (!(ath5k_hw_reg_read(ah, AR5K_PHY_ADC_TEST) & 0x10))
-                       break;
-               udelay(200);
-       }
-       ath5k_hw_reg_write(ah, phy_tst1, AR5K_PHY_TST1);
-
-       /*
-        * Start automatic gain control calibration
-        *
-        * During AGC calibration RX path is re-routed to
-        * a power detector so we don't receive anything.
-        *
-        * This method is used to calibrate some static offsets
-        * used together with on-the fly I/Q calibration (the
-        * one performed via ath5k_hw_phy_calibrate), which doesn't
-        * interrupt rx path.
-        *
-        * While rx path is re-routed to the power detector we also
-        * start a noise floor calibration to measure the
-        * card's noise floor (the noise we measure when we are not
-        * transmitting or receiving anything).
-        *
-        * If we are in a noisy environment, AGC calibration may time
-        * out and/or noise floor calibration might timeout.
-        */
-       AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL,
-                               AR5K_PHY_AGCCTL_CAL | AR5K_PHY_AGCCTL_NF);
-
-       /* At the same time start I/Q calibration for QAM constellation
-        * -no need for CCK- */
-       ah->ah_calibration = false;
-       if (!(mode == AR5K_MODE_11B)) {
-               ah->ah_calibration = true;
-               AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ,
-                               AR5K_PHY_IQ_CAL_NUM_LOG_MAX, 15);
-               AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ,
-                               AR5K_PHY_IQ_RUN);
-       }
-
-       /* Wait for gain calibration to finish (we check for I/Q calibration
-        * during ath5k_phy_calibrate) */
-       if (ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL,
-                       AR5K_PHY_AGCCTL_CAL, 0, false)) {
-               ATH5K_ERR(ah->ah_sc, "gain calibration timeout (%uMHz)\n",
-                       channel->center_freq);
-       }
-
-       /* Restore antenna mode */
-       ath5k_hw_set_antenna_mode(ah, ah->ah_ant_mode);
-
-       /* Restore slot time and ACK timeouts */
-       if (ah->ah_coverage_class > 0)
-               ath5k_hw_set_coverage_class(ah, ah->ah_coverage_class);
-
        /*
         * Configure QCUs/DCUs
         */
+       ret = ath5k_hw_init_queues(ah);
+       if (ret)
+               return ret;
 
-       /* TODO: HW Compression support for data queues */
-       /* TODO: Burst prefetch for data queues */
-
-       /*
-        * Reset queues and start beacon timers at the end of the reset routine
-        * This also sets QCU mask on each DCU for 1:1 qcu to dcu mapping
-        * Note: If we want we can assign multiple qcus on one dcu.
-        */
-       for (i = 0; i < ah->ah_capabilities.cap_queues.q_tx_num; i++) {
-               ret = ath5k_hw_reset_tx_queue(ah, i);
-               if (ret) {
-                       ATH5K_ERR(ah->ah_sc,
-                               "failed to reset TX queue #%d\n", i);
-                       return ret;
-               }
-       }
-
-
-       /*
-        * Configure DMA/Interrupts
-        */
 
        /*
-        * Set Rx/Tx DMA Configuration
-        *
-        * Set standard DMA size (128). Note that
-        * a DMA size of 512 causes rx overruns and tx errors
-        * on pci-e cards (tested on 5424 but since rx overruns
-        * also occur on 5416/5418 with madwifi we set 128
-        * for all PCI-E cards to be safe).
-        *
-        * XXX: need to check 5210 for this
-        * TODO: Check out tx triger level, it's always 64 on dumps but I
-        * guess we can tweak it and see how it goes ;-)
+        * Initialize DMA/Interrupts
         */
-       if (ah->ah_version != AR5K_AR5210) {
-               AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG,
-                       AR5K_TXCFG_SDMAMR, AR5K_DMASIZE_128B);
-               AR5K_REG_WRITE_BITS(ah, AR5K_RXCFG,
-                       AR5K_RXCFG_SDMAMW, AR5K_DMASIZE_128B);
-       }
+       ath5k_hw_dma_init(ah);
 
-       /* Pre-enable interrupts on 5211/5212*/
-       if (ah->ah_version != AR5K_AR5210)
-               ath5k_hw_set_imr(ah, ah->ah_imr);
 
        /* Enable 32KHz clock function for AR5212+ chips
         * Set clocks to 32KHz operation and use an