goto err_irq;
        }
 
+       /* set up multi-rate retry capabilities */
+       if (sc->ah->ah_version == AR5K_AR5212) {
+               hw->max_altrates = 3;
+               hw->max_altrate_tries = 11;
+       }
+
        /* Finish private driver data initialization */
        ret = ath5k_attach(pdev, hw);
        if (ret)
        struct sk_buff *skb = bf->skb;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        unsigned int pktlen, flags, keyidx = AR5K_TXKEYIX_INVALID;
-       int ret;
+       struct ieee80211_rate *rate;
+       unsigned int mrr_rate[3], mrr_tries[3];
+       int i, ret;
 
        flags = AR5K_TXDESC_INTREQ | AR5K_TXDESC_CLRDMASK;
 
        if (ret)
                goto err_unmap;
 
+       memset(mrr_rate, 0, sizeof(mrr_rate));
+       memset(mrr_tries, 0, sizeof(mrr_tries));
+       for (i = 0; i < 3; i++) {
+               rate = ieee80211_get_alt_retry_rate(sc->hw, info, i);
+               if (!rate)
+                       break;
+
+               mrr_rate[i] = rate->hw_value;
+               mrr_tries[i] = info->control.retries[i].limit;
+       }
+
+       ah->ah_setup_mrr_tx_desc(ah, ds,
+               mrr_rate[0], mrr_tries[0],
+               mrr_rate[1], mrr_tries[1],
+               mrr_rate[2], mrr_tries[2]);
+
        ds->ds_link = 0;
        ds->ds_data = bf->skbaddr;
 
        struct ath5k_desc *ds;
        struct sk_buff *skb;
        struct ieee80211_tx_info *info;
-       int ret;
+       int i, ret;
 
        spin_lock(&txq->lock);
        list_for_each_entry_safe(bf, bf0, &txq->q, list) {
                pci_unmap_single(sc->pdev, bf->skbaddr, skb->len,
                                PCI_DMA_TODEVICE);
 
-               info->status.retry_count = ts.ts_shortretry + ts.ts_longretry / 6;
+               memset(&info->status, 0, sizeof(info->status));
+               info->tx_rate_idx = ath5k_hw_to_driver_rix(sc,
+                               ts.ts_rate[ts.ts_final_idx]);
+               info->status.retry_count = ts.ts_longretry;
+
+               for (i = 0; i < 4; i++) {
+                       struct ieee80211_tx_altrate *r =
+                               &info->status.retries[i];
+
+                       if (ts.ts_rate[i]) {
+                               r->rate_idx = ath5k_hw_to_driver_rix(sc, ts.ts_rate[i]);
+                               r->limit = ts.ts_retry[i];
+                       } else {
+                               r->rate_idx = -1;
+                               r->limit = 0;
+                       }
+               }
+
+               info->status.excessive_retries = 0;
                if (unlikely(ts.ts_status)) {
                        sc->ll_stats.dot11ACKFailureCount++;
                        if (ts.ts_status & AR5K_TXERR_XRETRY)
 
        return 0;
 }
 
+/* no mrr support for cards older than 5212 */
+static int
+ath5k_hw_setup_no_mrr(struct ath5k_hw *ah, struct ath5k_desc *desc,
+       unsigned int tx_rate1, u_int tx_tries1, u_int tx_rate2,
+       u_int tx_tries2, unsigned int tx_rate3, u_int tx_tries3)
+{
+       return 0;
+}
+
 /*
  * Proccess the tx status descriptor on 5210/5211
  */
                AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH);
        ts->ts_antenna = 1;
        ts->ts_status = 0;
-       ts->ts_rate = AR5K_REG_MS(tx_ctl->tx_control_0,
+       ts->ts_rate[0] = AR5K_REG_MS(tx_ctl->tx_control_0,
                AR5K_2W_TX_DESC_CTL0_XMIT_RATE);
+       ts->ts_retry[0] = ts->ts_longretry;
+       ts->ts_final_idx = 0;
 
        if (!(tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK)) {
                if (tx_status->tx_status_0 &
                AR5K_DESC_TX_STATUS1_XMIT_ANTENNA) ? 2 : 1;
        ts->ts_status = 0;
 
-       switch (AR5K_REG_MS(tx_status->tx_status_1,
-                       AR5K_DESC_TX_STATUS1_FINAL_TS_INDEX)) {
-       case 0:
-               ts->ts_rate = tx_ctl->tx_control_3 &
-                       AR5K_4W_TX_DESC_CTL3_XMIT_RATE0;
-               break;
+       ts->ts_final_idx = AR5K_REG_MS(tx_status->tx_status_1,
+                       AR5K_DESC_TX_STATUS1_FINAL_TS_INDEX);
+
+       /* The longretry counter has the number of un-acked retries
+        * for the final rate. To get the total number of retries
+        * we have to add the retry counters for the other rates
+        * as well
+        */
+       ts->ts_retry[ts->ts_final_idx] = ts->ts_longretry;
+       switch (ts->ts_final_idx) {
+       case 3:
+               ts->ts_rate[3] = AR5K_REG_MS(tx_ctl->tx_control_3,
+                       AR5K_4W_TX_DESC_CTL3_XMIT_RATE3);
+
+               ts->ts_retry[2] = AR5K_REG_MS(tx_ctl->tx_control_2,
+                       AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2);
+               ts->ts_longretry += ts->ts_retry[2];
+               /* fall through */
+       case 2:
+               ts->ts_rate[2] = AR5K_REG_MS(tx_ctl->tx_control_3,
+                       AR5K_4W_TX_DESC_CTL3_XMIT_RATE2);
+
+               ts->ts_retry[1] = AR5K_REG_MS(tx_ctl->tx_control_2,
+                       AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1);
+               ts->ts_longretry += ts->ts_retry[1];
+               /* fall through */
        case 1:
-               ts->ts_rate = AR5K_REG_MS(tx_ctl->tx_control_3,
+               ts->ts_rate[1] = AR5K_REG_MS(tx_ctl->tx_control_3,
                        AR5K_4W_TX_DESC_CTL3_XMIT_RATE1);
-               ts->ts_longretry += AR5K_REG_MS(tx_ctl->tx_control_2,
+
+               ts->ts_retry[0] = AR5K_REG_MS(tx_ctl->tx_control_2,
                        AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1);
-               break;
-       case 2:
-               ts->ts_rate = AR5K_REG_MS(tx_ctl->tx_control_3,
-                       AR5K_4W_TX_DESC_CTL3_XMIT_RATE2);
-               ts->ts_longretry += AR5K_REG_MS(tx_ctl->tx_control_2,
-                       AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2);
-               break;
-       case 3:
-               ts->ts_rate = AR5K_REG_MS(tx_ctl->tx_control_3,
-                       AR5K_4W_TX_DESC_CTL3_XMIT_RATE3);
-               ts->ts_longretry += AR5K_REG_MS(tx_ctl->tx_control_2,
-                       AR5K_4W_TX_DESC_CTL2_XMIT_TRIES3);
+               ts->ts_longretry += ts->ts_retry[0];
+               /* fall through */
+       case 0:
+               ts->ts_rate[0] = tx_ctl->tx_control_3 &
+                       AR5K_4W_TX_DESC_CTL3_XMIT_RATE0;
                break;
        }
 
        } else {
                ah->ah_setup_rx_desc = ath5k_hw_setup_rx_desc;
                ah->ah_setup_tx_desc = ath5k_hw_setup_2word_tx_desc;
-               ah->ah_setup_mrr_tx_desc = ath5k_hw_setup_mrr_tx_desc;
+               ah->ah_setup_mrr_tx_desc = ath5k_hw_setup_no_mrr;
                ah->ah_proc_tx_desc = ath5k_hw_proc_2word_tx_status;
        }