Merge branch 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab...
[cascardo/linux.git] / drivers / net / wireless / ath / ath5k / phy.c
index 02869c7..78c26fd 100644 (file)
@@ -228,8 +228,20 @@ static inline int ath5k_hw_write_ofdm_timings(struct ath5k_hw *ah,
         * 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;
+       switch (ah->ah_bwmode) {
+       case AR5K_BWMODE_40MHZ:
+               clock = 40 * 2;
+               break;
+       case AR5K_BWMODE_10MHZ:
+               clock = 40 / 2;
+               break;
+       case AR5K_BWMODE_5MHZ:
+               clock = 40 / 4;
+               break;
+       default:
+               clock = 40;
+               break;
+       }
        coef_scaled = ((5 * (clock << 24)) / 2) / channel->center_freq;
 
        /* Get exponent
@@ -597,10 +609,10 @@ done:
 /* Write initial RF gain table to set the RF sensitivity
  * this one works on all RF chips and has nothing to do
  * with gain_F calibration */
-static int ath5k_hw_rfgain_init(struct ath5k_hw *ah, unsigned int freq)
+static int ath5k_hw_rfgain_init(struct ath5k_hw *ah, enum ieee80211_band band)
 {
        const struct ath5k_ini_rfgain *ath5k_rfg;
-       unsigned int i, size;
+       unsigned int i, size, index;
 
        switch (ah->ah_radio) {
        case AR5K_RF5111:
@@ -632,17 +644,11 @@ static int ath5k_hw_rfgain_init(struct ath5k_hw *ah, unsigned int freq)
                return -EINVAL;
        }
 
-       switch (freq) {
-       case AR5K_INI_RFGAIN_2GHZ:
-       case AR5K_INI_RFGAIN_5GHZ:
-               break;
-       default:
-               return -EINVAL;
-       }
+       index = (band == IEEE80211_BAND_2GHZ) ? 1 : 0;
 
        for (i = 0; i < size; i++) {
                AR5K_REG_WAIT(i);
-               ath5k_hw_reg_write(ah, ath5k_rfg[i].rfg_value[freq],
+               ath5k_hw_reg_write(ah, ath5k_rfg[i].rfg_value[index],
                        (u32)ath5k_rfg[i].rfg_register);
        }
 
@@ -812,6 +818,11 @@ static int ath5k_hw_rfregs_init(struct ath5k_hw *ah,
 
        g_step = &go->go_step[ah->ah_gain.g_step_idx];
 
+       /* Set turbo mode (N/A on RF5413) */
+       if ((ah->ah_bwmode == AR5K_BWMODE_40MHZ) &&
+       (ah->ah_radio != AR5K_RF5413))
+               ath5k_hw_rfb_op(ah, rf_regs, 1, AR5K_RF_TURBO, false);
+
        /* Bank Modifications (chip-specific) */
        if (ah->ah_radio == AR5K_RF5111) {
 
@@ -851,7 +862,23 @@ static int ath5k_hw_rfregs_init(struct ath5k_hw *ah,
                ath5k_hw_rfb_op(ah, rf_regs, ee->ee_xpd[ee_mode],
                                                AR5K_RF_PLO_SEL, true);
 
-               /* TODO: Half/quarter channel support */
+               /* Tweak power detectors for half/quarter rate support */
+               if (ah->ah_bwmode == AR5K_BWMODE_5MHZ ||
+               ah->ah_bwmode == AR5K_BWMODE_10MHZ) {
+                       u8 wait_i;
+
+                       ath5k_hw_rfb_op(ah, rf_regs, 0x1f,
+                                               AR5K_RF_WAIT_S, true);
+
+                       wait_i = (ah->ah_bwmode == AR5K_BWMODE_5MHZ) ?
+                                                       0x1f : 0x10;
+
+                       ath5k_hw_rfb_op(ah, rf_regs, wait_i,
+                                               AR5K_RF_WAIT_I, true);
+                       ath5k_hw_rfb_op(ah, rf_regs, 3,
+                                               AR5K_RF_MAX_TIME, true);
+
+               }
        }
 
        if (ah->ah_radio == AR5K_RF5112) {
@@ -949,8 +976,20 @@ static int ath5k_hw_rfregs_init(struct ath5k_hw *ah,
                ath5k_hw_rfb_op(ah, rf_regs, ee->ee_i_gain[ee_mode],
                                                AR5K_RF_GAIN_I, true);
 
-               /* TODO: Half/quarter channel support */
+               /* Tweak power detector for half/quarter rates */
+               if (ah->ah_bwmode == AR5K_BWMODE_5MHZ ||
+               ah->ah_bwmode == AR5K_BWMODE_10MHZ) {
+                       u8 pd_delay;
+
+                       pd_delay = (ah->ah_bwmode == AR5K_BWMODE_5MHZ) ?
+                                                       0xf : 0x8;
 
+                       ath5k_hw_rfb_op(ah, rf_regs, pd_delay,
+                                               AR5K_RF_PD_PERIOD_A, true);
+                       ath5k_hw_rfb_op(ah, rf_regs, 0xf,
+                                               AR5K_RF_PD_DELAY_A, true);
+
+               }
        }
 
        if (ah->ah_radio == AR5K_RF5413 &&
@@ -1235,7 +1274,6 @@ static int ath5k_hw_channel(struct ath5k_hw *ah,
        }
 
        ah->ah_current_channel = channel;
-       ath5k_hw_set_clockrate(ah);
 
        return 0;
 }
@@ -1317,22 +1355,7 @@ void ath5k_hw_update_noise_floor(struct ath5k_hw *ah)
                return;
        }
 
-       switch (ah->ah_current_channel->hw_value & CHANNEL_MODES) {
-       case CHANNEL_A:
-       case CHANNEL_T:
-       case CHANNEL_XR:
-               ee_mode = AR5K_EEPROM_MODE_11A;
-               break;
-       case CHANNEL_G:
-       case CHANNEL_TG:
-               ee_mode = AR5K_EEPROM_MODE_11G;
-               break;
-       default:
-       case CHANNEL_B:
-               ee_mode = AR5K_EEPROM_MODE_11B;
-               break;
-       }
-
+       ee_mode = ath5k_eeprom_mode_from_channel(ah->ah_current_channel);
 
        /* completed NF calibration, test threshold */
        nf = ath5k_hw_read_measured_noise_floor(ah);
@@ -1595,7 +1618,7 @@ ath5k_hw_set_spur_mitigation_filter(struct ath5k_hw *ah,
        spur_chan_fbin = AR5K_EEPROM_NO_SPUR;
        spur_detection_window = AR5K_SPUR_CHAN_WIDTH;
        /* XXX: Half/Quarter channels ?*/
-       if (channel->hw_value & CHANNEL_TURBO)
+       if (ah->ah_bwmode == AR5K_BWMODE_40MHZ)
                spur_detection_window *= 2;
 
        for (i = 0; i < AR5K_EEPROM_N_SPUR_CHANS; i++) {
@@ -1624,32 +1647,43 @@ ath5k_hw_set_spur_mitigation_filter(struct ath5k_hw *ah,
                 * Calculate deltas:
                 * spur_freq_sigma_delta -> spur_offset / sample_freq << 21
                 * spur_delta_phase -> spur_offset / chip_freq << 11
-                * Note: Both values have 100KHz resolution
+                * Note: Both values have 100Hz resolution
                 */
-               /* XXX: Half/Quarter rate channels ? */
-               switch (channel->hw_value) {
-               case CHANNEL_A:
-                       /* Both sample_freq and chip_freq are 40MHz */
-                       spur_delta_phase = (spur_offset << 17) / 25;
-                       spur_freq_sigma_delta = (spur_delta_phase >> 10);
-                       symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz;
-                       break;
-               case CHANNEL_G:
-                       /* sample_freq -> 40MHz chip_freq -> 44MHz
-                        * (for b compatibility) */
-                       spur_freq_sigma_delta = (spur_offset << 8) / 55;
-                       spur_delta_phase = (spur_offset << 17) / 25;
-                       symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz;
-                       break;
-               case CHANNEL_T:
-               case CHANNEL_TG:
+               switch (ah->ah_bwmode) {
+               case AR5K_BWMODE_40MHZ:
                        /* Both sample_freq and chip_freq are 80MHz */
                        spur_delta_phase = (spur_offset << 16) / 25;
                        spur_freq_sigma_delta = (spur_delta_phase >> 10);
-                       symbol_width = AR5K_SPUR_SYMBOL_WIDTH_TURBO_100Hz;
+                       symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz * 2;
                        break;
+               case AR5K_BWMODE_10MHZ:
+                       /* Both sample_freq and chip_freq are 20MHz (?) */
+                       spur_delta_phase = (spur_offset << 18) / 25;
+                       spur_freq_sigma_delta = (spur_delta_phase >> 10);
+                       symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz / 2;
+               case AR5K_BWMODE_5MHZ:
+                       /* Both sample_freq and chip_freq are 10MHz (?) */
+                       spur_delta_phase = (spur_offset << 19) / 25;
+                       spur_freq_sigma_delta = (spur_delta_phase >> 10);
+                       symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz / 4;
                default:
-                       return;
+                       if (channel->hw_value == CHANNEL_A) {
+                               /* Both sample_freq and chip_freq are 40MHz */
+                               spur_delta_phase = (spur_offset << 17) / 25;
+                               spur_freq_sigma_delta =
+                                               (spur_delta_phase >> 10);
+                               symbol_width =
+                                       AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz;
+                       } else {
+                               /* sample_freq -> 40MHz chip_freq -> 44MHz
+                                * (for b compatibility) */
+                               spur_delta_phase = (spur_offset << 17) / 25;
+                               spur_freq_sigma_delta =
+                                               (spur_offset << 8) / 55;
+                               symbol_width =
+                                       AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz;
+                       }
+                       break;
                }
 
                /* Calculate pilot and magnitude masks */
@@ -1882,7 +1916,8 @@ ath5k_hw_set_antenna_mode(struct ath5k_hw *ah, u8 ant_mode)
        struct ieee80211_channel *channel = ah->ah_current_channel;
        bool use_def_for_tx, update_def_on_tx, use_def_for_rts, fast_div;
        bool use_def_for_sg;
-       u8 def_ant, tx_ant, ee_mode;
+       int ee_mode;
+       u8 def_ant, tx_ant;
        u32 sta_id1 = 0;
 
        /* if channel is not initialized yet we can't set the antennas
@@ -1894,20 +1929,8 @@ ath5k_hw_set_antenna_mode(struct ath5k_hw *ah, u8 ant_mode)
 
        def_ant = ah->ah_def_ant;
 
-       switch (channel->hw_value & CHANNEL_MODES) {
-       case CHANNEL_A:
-       case CHANNEL_T:
-       case CHANNEL_XR:
-               ee_mode = AR5K_EEPROM_MODE_11A;
-               break;
-       case CHANNEL_G:
-       case CHANNEL_TG:
-               ee_mode = AR5K_EEPROM_MODE_11G;
-               break;
-       case CHANNEL_B:
-               ee_mode = AR5K_EEPROM_MODE_11B;
-               break;
-       default:
+       ee_mode = ath5k_eeprom_mode_from_channel(channel);
+       if (ee_mode < 0) {
                ATH5K_ERR(ah->ah_sc,
                        "invalid channel: %d\n", channel->center_freq);
                return;
@@ -2335,20 +2358,20 @@ ath5k_get_max_ctl_power(struct ath5k_hw *ah,
 
        switch (channel->hw_value & CHANNEL_MODES) {
        case CHANNEL_A:
-               ctl_mode |= AR5K_CTL_11A;
+               if (ah->ah_bwmode == AR5K_BWMODE_40MHZ)
+                       ctl_mode |= AR5K_CTL_TURBO;
+               else
+                       ctl_mode |= AR5K_CTL_11A;
                break;
        case CHANNEL_G:
-               ctl_mode |= AR5K_CTL_11G;
+               if (ah->ah_bwmode == AR5K_BWMODE_40MHZ)
+                       ctl_mode |= AR5K_CTL_TURBOG;
+               else
+                       ctl_mode |= AR5K_CTL_11G;
                break;
        case CHANNEL_B:
                ctl_mode |= AR5K_CTL_11B;
                break;
-       case CHANNEL_T:
-               ctl_mode |= AR5K_CTL_TURBO;
-               break;
-       case CHANNEL_TG:
-               ctl_mode |= AR5K_CTL_TURBOG;
-               break;
        case CHANNEL_XR:
                /* Fall through */
        default:
@@ -2542,7 +2565,7 @@ ath5k_combine_linear_pcdac_curves(struct ath5k_hw *ah, s16* table_min,
 
 /* Write PCDAC values on hw */
 static void
-ath5k_setup_pcdac_table(struct ath5k_hw *ah)
+ath5k_write_pcdac_table(struct ath5k_hw *ah)
 {
        u8      *pcdac_out = ah->ah_txpower.txp_pd_table;
        int     i;
@@ -2691,10 +2714,12 @@ ath5k_combine_pwr_to_pdadc_curves(struct ath5k_hw *ah,
 
 /* Write PDADC values on hw */
 static void
-ath5k_setup_pwr_to_pdadc_table(struct ath5k_hw *ah,
-                       u8 pdcurves, u8 *pdg_to_idx)
+ath5k_write_pwr_to_pdadc_table(struct ath5k_hw *ah, u8 ee_mode)
 {
+       struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
        u8 *pdadc_out = ah->ah_txpower.txp_pd_table;
+       u8 *pdg_to_idx = ee->ee_pdc_to_idx[ee_mode];
+       u8 pdcurves = ee->ee_pd_gains[ee_mode];
        u32 reg;
        u8 i;
 
@@ -2904,8 +2929,7 @@ ath5k_setup_channel_powertable(struct ath5k_hw *ah,
                                        (s16) pcinfo_R->freq,
                                        pcinfo_L->max_pwr, pcinfo_R->max_pwr);
 
-       /* We are ready to go, fill PCDAC/PDADC
-        * table and write settings on hardware */
+       /* Fill PCDAC/PDADC table */
        switch (type) {
        case AR5K_PWRTABLE_LINEAR_PCDAC:
                /* For RF5112 we can have one or two curves
@@ -2918,9 +2942,6 @@ ath5k_setup_channel_powertable(struct ath5k_hw *ah,
                 * match max power value with max
                 * table index */
                ah->ah_txpower.txp_offset = 64 - (table_max[0] / 2);
-
-               /* Write settings on hw */
-               ath5k_setup_pcdac_table(ah);
                break;
        case AR5K_PWRTABLE_PWR_TO_PCDAC:
                /* We are done for RF5111 since it has only
@@ -2930,9 +2951,6 @@ ath5k_setup_channel_powertable(struct ath5k_hw *ah,
                /* No rate powertable adjustment for RF5111 */
                ah->ah_txpower.txp_min_idx = 0;
                ah->ah_txpower.txp_offset = 0;
-
-               /* Write settings on hw */
-               ath5k_setup_pcdac_table(ah);
                break;
        case AR5K_PWRTABLE_PWR_TO_PDADC:
                /* Set PDADC boundaries and fill
@@ -2940,9 +2958,6 @@ ath5k_setup_channel_powertable(struct ath5k_hw *ah,
                ath5k_combine_pwr_to_pdadc_curves(ah, table_min, table_max,
                                                ee->ee_pd_gains[ee_mode]);
 
-               /* Write settings on hw */
-               ath5k_setup_pwr_to_pdadc_table(ah, pdg, pdg_curve_to_idx);
-
                /* Set txp.offset, note that table_min
                 * can be negative */
                ah->ah_txpower.txp_offset = table_min[0];
@@ -2951,9 +2966,20 @@ ath5k_setup_channel_powertable(struct ath5k_hw *ah,
                return -EINVAL;
        }
 
+       ah->ah_txpower.txp_setup = true;
+
        return 0;
 }
 
+/* Write power table for current channel to hw */
+static void
+ath5k_write_channel_powertable(struct ath5k_hw *ah, u8 ee_mode, u8 type)
+{
+       if (type == AR5K_PWRTABLE_PWR_TO_PDADC)
+               ath5k_write_pwr_to_pdadc_table(ah, ee_mode);
+       else
+               ath5k_write_pcdac_table(ah);
+}
 
 /*
  * Per-rate tx power setting
@@ -3042,7 +3068,7 @@ ath5k_setup_rate_powertable(struct ath5k_hw *ah, u16 max_pwr,
 
        /* Min/max in 0.25dB units */
        ah->ah_txpower.txp_min_pwr = 2 * rates[7];
-       ah->ah_txpower.txp_max_pwr = 2 * rates[0];
+       ah->ah_txpower.txp_cur_pwr = 2 * rates[0];
        ah->ah_txpower.txp_ofdm = rates[7];
 }
 
@@ -3052,9 +3078,11 @@ ath5k_setup_rate_powertable(struct ath5k_hw *ah, u16 max_pwr,
  */
 static int
 ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel,
-               u8 ee_mode, u8 txpower)
+                u8 txpower)
 {
        struct ath5k_rate_pcal_info rate_info;
+       struct ieee80211_channel *curr_channel = ah->ah_current_channel;
+       int ee_mode;
        u8 type;
        int ret;
 
@@ -3063,14 +3091,18 @@ ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel,
                return -EINVAL;
        }
 
-       /* Reset TX power values */
-       memset(&ah->ah_txpower, 0, sizeof(ah->ah_txpower));
-       ah->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER;
-       ah->ah_txpower.txp_min_pwr = 0;
-       ah->ah_txpower.txp_max_pwr = AR5K_TUNE_MAX_TXPOWER;
+       ee_mode = ath5k_eeprom_mode_from_channel(channel);
+       if (ee_mode < 0) {
+               ATH5K_ERR(ah->ah_sc,
+                       "invalid channel: %d\n", channel->center_freq);
+               return -EINVAL;
+       }
 
        /* Initialize TX power table */
        switch (ah->ah_radio) {
+       case AR5K_RF5110:
+               /* TODO */
+               return 0;
        case AR5K_RF5111:
                type = AR5K_PWRTABLE_PWR_TO_PCDAC;
                break;
@@ -3088,10 +3120,26 @@ ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel,
                return -EINVAL;
        }
 
-       /* FIXME: Only on channel/mode change */
-       ret = ath5k_setup_channel_powertable(ah, channel, ee_mode, type);
-       if (ret)
-               return ret;
+       /*
+        * If we don't change channel/mode skip tx powertable calculation
+        * and use the cached one.
+        */
+       if (!ah->ah_txpower.txp_setup ||
+           (channel->hw_value != curr_channel->hw_value) ||
+           (channel->center_freq != curr_channel->center_freq)) {
+               /* Reset TX power values */
+               memset(&ah->ah_txpower, 0, sizeof(ah->ah_txpower));
+               ah->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER;
+
+               /* Calculate the powertable */
+               ret = ath5k_setup_channel_powertable(ah, channel,
+                                                       ee_mode, type);
+               if (ret)
+                       return ret;
+       }
+
+       /* Write table on hw */
+       ath5k_write_channel_powertable(ah, ee_mode, type);
 
        /* Limit max power if we have a CTL available */
        ath5k_get_max_ctl_power(ah, channel);
@@ -3146,33 +3194,10 @@ ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel,
 
 int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 txpower)
 {
-       /*Just a try M.F.*/
-       struct ieee80211_channel *channel = ah->ah_current_channel;
-       u8 ee_mode;
-
-       switch (channel->hw_value & CHANNEL_MODES) {
-       case CHANNEL_A:
-       case CHANNEL_T:
-       case CHANNEL_XR:
-               ee_mode = AR5K_EEPROM_MODE_11A;
-               break;
-       case CHANNEL_G:
-       case CHANNEL_TG:
-               ee_mode = AR5K_EEPROM_MODE_11G;
-               break;
-       case CHANNEL_B:
-               ee_mode = AR5K_EEPROM_MODE_11B;
-               break;
-       default:
-               ATH5K_ERR(ah->ah_sc,
-                       "invalid channel: %d\n", channel->center_freq);
-               return -EINVAL;
-       }
-
        ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_TXPOWER,
                "changing txpower to %d\n", txpower);
 
-       return ath5k_hw_txpower(ah, channel, ee_mode, txpower);
+       return ath5k_hw_txpower(ah, ah->ah_current_channel, txpower);
 }
 
 /*************\
@@ -3180,36 +3205,70 @@ int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 txpower)
 \*************/
 
 int ath5k_hw_phy_init(struct ath5k_hw *ah, struct ieee80211_channel *channel,
-                                               u8 mode, u8 ee_mode, u8 freq)
+                     u8 mode, bool fast)
 {
+       struct ieee80211_channel *curr_channel;
        int ret, i;
        u32 phy_tst1;
-
        ret = 0;
 
        /*
-        * 5211/5212 Specific
+        * Sanity check for fast flag
+        * Don't try fast channel change when changing modulation
+        * mode/band. We check for chip compatibility on
+        * ath5k_hw_reset.
         */
-       if (ah->ah_version != AR5K_AR5210) {
+       curr_channel = ah->ah_current_channel;
+       if (fast && (channel->hw_value != curr_channel->hw_value))
+               return -EINVAL;
+
+       /*
+        * On fast channel change we only set the synth parameters
+        * while PHY is running, enable calibration and skip the rest.
+        */
+       if (fast) {
+               AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_RFBUS_REQ,
+                                   AR5K_PHY_RFBUS_REQ_REQUEST);
+               for (i = 0; i < 100; i++) {
+                       if (ath5k_hw_reg_read(ah, AR5K_PHY_RFBUS_GRANT))
+                               break;
+                       udelay(5);
+               }
+               /* Failed */
+               if (i >= 100)
+                       return -EIO;
+       }
+
+       /*
+        * Set TX power
+        *
+        * Note: We need to do that before we set
+        * RF buffer settings on 5211/5212+ so that we
+        * properly set curve indices.
+        */
+       ret = ath5k_hw_txpower(ah, channel, ah->ah_txpower.txp_cur_pwr ?
+                       ah->ah_txpower.txp_cur_pwr / 2 : AR5K_TUNE_MAX_TXPOWER);
+       if (ret)
+               return ret;
+
+       /*
+        * For 5210 we do all initialization using
+        * initvals, so we don't have to modify
+        * any settings (5210 also only supports
+        * a/aturbo modes)
+        */
+       if ((ah->ah_version != AR5K_AR5210) && !fast) {
 
                /*
                 * Write initial RF gain settings
                 * This should work for both 5111/5112
                 */
-               ret = ath5k_hw_rfgain_init(ah, freq);
+               ret = ath5k_hw_rfgain_init(ah, channel->band);
                if (ret)
                        return ret;
 
                mdelay(1);
 
-               /*
-                * Set TX power
-                */
-               ret = ath5k_hw_txpower(ah, channel, ee_mode,
-                                       ah->ah_txpower.txp_max_pwr / 2);
-               if (ret)
-                       return ret;
-
                /*
                 * Write RF buffer
                 */
@@ -3217,7 +3276,6 @@ int ath5k_hw_phy_init(struct ath5k_hw *ah, struct ieee80211_channel *channel,
                if (ret)
                        return ret;
 
-
                /* Write OFDM timings on 5212*/
                if (ah->ah_version == AR5K_AR5212 &&
                        channel->hw_value & CHANNEL_OFDM) {
@@ -3245,13 +3303,7 @@ int ath5k_hw_phy_init(struct ath5k_hw *ah, struct ieee80211_channel *channel,
                                    AR5K_TXCFG_B_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)
-                */
+       } else if (ah->ah_version == AR5K_AR5210) {
                mdelay(1);
                /* Disable phy and wait */
                ath5k_hw_reg_write(ah, AR5K_PHY_ACT_DISABLE, AR5K_PHY_ACT);
@@ -3273,8 +3325,6 @@ int ath5k_hw_phy_init(struct ath5k_hw *ah, struct ieee80211_channel *channel,
        /*
         * On 5211+ read activation -> rx delay
         * and use it.
-        *
-        * TODO: Half/quarter rate support
         */
        if (ah->ah_version != AR5K_AR5210) {
                u32 delay;
@@ -3282,24 +3332,37 @@ int ath5k_hw_phy_init(struct ath5k_hw *ah, struct ieee80211_channel *channel,
                        AR5K_PHY_RX_DELAY_M;
                delay = (channel->hw_value & CHANNEL_CCK) ?
                        ((delay << 2) / 22) : (delay / 10);
-
-               udelay(100 + (2 * delay));
+               if (ah->ah_bwmode == AR5K_BWMODE_10MHZ)
+                       delay = delay << 1;
+               if (ah->ah_bwmode == AR5K_BWMODE_5MHZ)
+                       delay = delay << 2;
+               /* XXX: /2 on turbo ? Let's be safe
+                * for now */
+               udelay(100 + 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);
+       if (fast)
+               /*
+                * Release RF Bus grant
+                */
+               AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_RFBUS_REQ,
+                                   AR5K_PHY_RFBUS_REQ_REQUEST);
+       else {
+               /*
+                * 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);
        }
-       ath5k_hw_reg_write(ah, phy_tst1, AR5K_PHY_TST1);
 
        /*
         * Start automatic gain control calibration