Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[pandora-kernel.git] / drivers / net / wireless / rt2x00 / rt2800lib.c
index 8c00fbd..3bb6749 100644 (file)
@@ -1,4 +1,5 @@
 /*
+       Copyright (C) 2010 Willow Garage <http://www.willowgarage.com>
        Copyright (C) 2010 Ivo van Doorn <IvDoorn@gmail.com>
        Copyright (C) 2009 Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
        Copyright (C) 2009 Gertjan van Wingerde <gwingerde@gmail.com>
@@ -254,6 +255,23 @@ void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev,
 }
 EXPORT_SYMBOL_GPL(rt2800_mcu_request);
 
+int rt2800_wait_csr_ready(struct rt2x00_dev *rt2x00dev)
+{
+       unsigned int i = 0;
+       u32 reg;
+
+       for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+               rt2800_register_read(rt2x00dev, MAC_CSR0, &reg);
+               if (reg && reg != ~0)
+                       return 0;
+               msleep(1);
+       }
+
+       ERROR(rt2x00dev, "Unstable hardware.\n");
+       return -EBUSY;
+}
+EXPORT_SYMBOL_GPL(rt2800_wait_csr_ready);
+
 int rt2800_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev)
 {
        unsigned int i;
@@ -367,19 +385,16 @@ int rt2800_load_firmware(struct rt2x00_dev *rt2x00dev,
        u32 reg;
 
        /*
-        * Wait for stable hardware.
+        * If driver doesn't wake up firmware here,
+        * rt2800_load_firmware will hang forever when interface is up again.
         */
-       for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
-               rt2800_register_read(rt2x00dev, MAC_CSR0, &reg);
-               if (reg && reg != ~0)
-                       break;
-               msleep(1);
-       }
+       rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, 0x00000000);
 
-       if (i == REGISTER_BUSY_COUNT) {
-               ERROR(rt2x00dev, "Unstable hardware.\n");
+       /*
+        * Wait for stable hardware.
+        */
+       if (rt2800_wait_csr_ready(rt2x00dev))
                return -EBUSY;
-       }
 
        if (rt2x00_is_pci(rt2x00dev))
                rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000002);
@@ -427,8 +442,10 @@ int rt2800_load_firmware(struct rt2x00_dev *rt2x00dev,
 }
 EXPORT_SYMBOL_GPL(rt2800_load_firmware);
 
-void rt2800_write_txwi(__le32 *txwi, struct txentry_desc *txdesc)
+void rt2800_write_tx_data(struct queue_entry *entry,
+                         struct txentry_desc *txdesc)
 {
+       __le32 *txwi = rt2800_drv_get_txwi(entry);
        u32 word;
 
        /*
@@ -437,7 +454,8 @@ void rt2800_write_txwi(__le32 *txwi, struct txentry_desc *txdesc)
        rt2x00_desc_read(txwi, 0, &word);
        rt2x00_set_field32(&word, TXWI_W0_FRAG,
                           test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags));
-       rt2x00_set_field32(&word, TXWI_W0_MIMO_PS, 0);
+       rt2x00_set_field32(&word, TXWI_W0_MIMO_PS,
+                          test_bit(ENTRY_TXD_HT_MIMO_PS, &txdesc->flags));
        rt2x00_set_field32(&word, TXWI_W0_CF_ACK, 0);
        rt2x00_set_field32(&word, TXWI_W0_TS,
                           test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags));
@@ -465,7 +483,7 @@ void rt2800_write_txwi(__le32 *txwi, struct txentry_desc *txdesc)
                           txdesc->key_idx : 0xff);
        rt2x00_set_field32(&word, TXWI_W1_MPDU_TOTAL_BYTE_COUNT,
                           txdesc->length);
-       rt2x00_set_field32(&word, TXWI_W1_PACKETID, txdesc->queue + 1);
+       rt2x00_set_field32(&word, TXWI_W1_PACKETID, txdesc->qid + 1);
        rt2x00_desc_write(txwi, 1, word);
 
        /*
@@ -478,7 +496,7 @@ void rt2800_write_txwi(__le32 *txwi, struct txentry_desc *txdesc)
        _rt2x00_desc_write(txwi, 2, 0 /* skbdesc->iv[0] */);
        _rt2x00_desc_write(txwi, 3, 0 /* skbdesc->iv[1] */);
 }
-EXPORT_SYMBOL_GPL(rt2800_write_txwi);
+EXPORT_SYMBOL_GPL(rt2800_write_tx_data);
 
 static int rt2800_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxwi_w2)
 {
@@ -490,7 +508,7 @@ static int rt2800_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxwi_w2)
        u8 offset1;
        u8 offset2;
 
-       if (rt2x00dev->rx_status.band == IEEE80211_BAND_2GHZ) {
+       if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) {
                rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &eeprom);
                offset0 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG_OFFSET0);
                offset1 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG_OFFSET1);
@@ -569,6 +587,148 @@ void rt2800_process_rxwi(struct queue_entry *entry,
 }
 EXPORT_SYMBOL_GPL(rt2800_process_rxwi);
 
+static bool rt2800_txdone_entry_check(struct queue_entry *entry, u32 reg)
+{
+       __le32 *txwi;
+       u32 word;
+       int wcid, ack, pid;
+       int tx_wcid, tx_ack, tx_pid;
+
+       wcid    = rt2x00_get_field32(reg, TX_STA_FIFO_WCID);
+       ack     = rt2x00_get_field32(reg, TX_STA_FIFO_TX_ACK_REQUIRED);
+       pid     = rt2x00_get_field32(reg, TX_STA_FIFO_PID_TYPE);
+
+       /*
+        * This frames has returned with an IO error,
+        * so the status report is not intended for this
+        * frame.
+        */
+       if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags)) {
+               rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE);
+               return false;
+       }
+
+       /*
+        * Validate if this TX status report is intended for
+        * this entry by comparing the WCID/ACK/PID fields.
+        */
+       txwi = rt2800_drv_get_txwi(entry);
+
+       rt2x00_desc_read(txwi, 1, &word);
+       tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID);
+       tx_ack  = rt2x00_get_field32(word, TXWI_W1_ACK);
+       tx_pid  = rt2x00_get_field32(word, TXWI_W1_PACKETID);
+
+       if ((wcid != tx_wcid) || (ack != tx_ack) || (pid != tx_pid)) {
+               WARNING(entry->queue->rt2x00dev,
+                       "TX status report missed for queue %d entry %d\n",
+               entry->queue->qid, entry->entry_idx);
+               rt2x00lib_txdone_noinfo(entry, TXDONE_UNKNOWN);
+               return false;
+       }
+
+       return true;
+}
+
+void rt2800_txdone(struct rt2x00_dev *rt2x00dev)
+{
+       struct data_queue *queue;
+       struct queue_entry *entry;
+       __le32 *txwi;
+       struct txdone_entry_desc txdesc;
+       u32 word;
+       u32 reg;
+       u16 mcs, real_mcs;
+       u8 pid;
+       int i;
+
+       /*
+        * TX_STA_FIFO is a stack of X entries, hence read TX_STA_FIFO
+        * at most X times and also stop processing once the TX_STA_FIFO_VALID
+        * flag is not set anymore.
+        *
+        * The legacy drivers use X=TX_RING_SIZE but state in a comment
+        * that the TX_STA_FIFO stack has a size of 16. We stick to our
+        * tx ring size for now.
+        */
+       for (i = 0; i < TX_ENTRIES; i++) {
+               rt2800_register_read(rt2x00dev, TX_STA_FIFO, &reg);
+               if (!rt2x00_get_field32(reg, TX_STA_FIFO_VALID))
+                       break;
+
+               /*
+                * Skip this entry when it contains an invalid
+                * queue identication number.
+                */
+               pid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_TYPE) - 1;
+               if (pid >= QID_RX)
+                       continue;
+
+               queue = rt2x00queue_get_queue(rt2x00dev, pid);
+               if (unlikely(!queue))
+                       continue;
+
+               /*
+                * Inside each queue, we process each entry in a chronological
+                * order. We first check that the queue is not empty.
+                */
+               entry = NULL;
+               txwi = NULL;
+               while (!rt2x00queue_empty(queue)) {
+                       entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
+                       if (rt2800_txdone_entry_check(entry, reg))
+                               break;
+               }
+
+               if (!entry || rt2x00queue_empty(queue))
+                       break;
+
+
+               /*
+                * Obtain the status about this packet.
+                */
+               txdesc.flags = 0;
+               txwi = rt2800_drv_get_txwi(entry);
+               rt2x00_desc_read(txwi, 0, &word);
+               mcs = rt2x00_get_field32(word, TXWI_W0_MCS);
+               real_mcs = rt2x00_get_field32(reg, TX_STA_FIFO_MCS);
+
+               /*
+                * Ralink has a retry mechanism using a global fallback
+                * table. We setup this fallback table to try the immediate
+                * lower rate for all rates. In the TX_STA_FIFO, the MCS field
+                * always contains the MCS used for the last transmission, be
+                * it successful or not.
+                */
+               if (rt2x00_get_field32(reg, TX_STA_FIFO_TX_SUCCESS)) {
+                       /*
+                        * Transmission succeeded. The number of retries is
+                        * mcs - real_mcs
+                        */
+                       __set_bit(TXDONE_SUCCESS, &txdesc.flags);
+                       txdesc.retry = ((mcs > real_mcs) ? mcs - real_mcs : 0);
+               } else {
+                       /*
+                        * Transmission failed. The number of retries is
+                        * always 7 in this case (for a total number of 8
+                        * frames sent).
+                        */
+                       __set_bit(TXDONE_FAILURE, &txdesc.flags);
+                       txdesc.retry = rt2x00dev->long_retry;
+               }
+
+               /*
+                * the frame was retried at least once
+                * -> hw used fallback rates
+                */
+               if (txdesc.retry)
+                       __set_bit(TXDONE_FALLBACK, &txdesc.flags);
+
+               rt2x00lib_txdone(entry, &txdesc);
+       }
+}
+EXPORT_SYMBOL_GPL(rt2800_txdone);
+
 void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
 {
        struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
@@ -600,7 +760,7 @@ void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
        /*
         * Add the TXWI for the beacon to the skb.
         */
-       rt2800_write_txwi((__le32 *)entry->skb->data, txdesc);
+       rt2800_write_tx_data(entry, txdesc);
 
        /*
         * Dump beacon to userspace through debugfs.
@@ -975,19 +1135,23 @@ void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf,
        }
 
        if (flags & CONFIG_UPDATE_MAC) {
-               reg = le32_to_cpu(conf->mac[1]);
-               rt2x00_set_field32(&reg, MAC_ADDR_DW1_UNICAST_TO_ME_MASK, 0xff);
-               conf->mac[1] = cpu_to_le32(reg);
+               if (!is_zero_ether_addr((const u8 *)conf->mac)) {
+                       reg = le32_to_cpu(conf->mac[1]);
+                       rt2x00_set_field32(&reg, MAC_ADDR_DW1_UNICAST_TO_ME_MASK, 0xff);
+                       conf->mac[1] = cpu_to_le32(reg);
+               }
 
                rt2800_register_multiwrite(rt2x00dev, MAC_ADDR_DW0,
                                              conf->mac, sizeof(conf->mac));
        }
 
        if (flags & CONFIG_UPDATE_BSSID) {
-               reg = le32_to_cpu(conf->bssid[1]);
-               rt2x00_set_field32(&reg, MAC_BSSID_DW1_BSS_ID_MASK, 3);
-               rt2x00_set_field32(&reg, MAC_BSSID_DW1_BSS_BCN_NUM, 7);
-               conf->bssid[1] = cpu_to_le32(reg);
+               if (!is_zero_ether_addr((const u8 *)conf->bssid)) {
+                       reg = le32_to_cpu(conf->bssid[1]);
+                       rt2x00_set_field32(&reg, MAC_BSSID_DW1_BSS_ID_MASK, 3);
+                       rt2x00_set_field32(&reg, MAC_BSSID_DW1_BSS_BCN_NUM, 7);
+                       conf->bssid[1] = cpu_to_le32(reg);
+               }
 
                rt2800_register_multiwrite(rt2x00dev, MAC_BSSID_DW0,
                                              conf->bssid, sizeof(conf->bssid));
@@ -995,38 +1159,50 @@ void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf,
 }
 EXPORT_SYMBOL_GPL(rt2800_config_intf);
 
-void rt2800_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp)
+void rt2800_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp,
+                      u32 changed)
 {
        u32 reg;
 
-       rt2800_register_read(rt2x00dev, AUTO_RSP_CFG, &reg);
-       rt2x00_set_field32(&reg, AUTO_RSP_CFG_BAC_ACK_POLICY,
-                          !!erp->short_preamble);
-       rt2x00_set_field32(&reg, AUTO_RSP_CFG_AR_PREAMBLE,
-                          !!erp->short_preamble);
-       rt2800_register_write(rt2x00dev, AUTO_RSP_CFG, reg);
+       if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+               rt2800_register_read(rt2x00dev, AUTO_RSP_CFG, &reg);
+               rt2x00_set_field32(&reg, AUTO_RSP_CFG_BAC_ACK_POLICY,
+                                  !!erp->short_preamble);
+               rt2x00_set_field32(&reg, AUTO_RSP_CFG_AR_PREAMBLE,
+                                  !!erp->short_preamble);
+               rt2800_register_write(rt2x00dev, AUTO_RSP_CFG, reg);
+       }
 
-       rt2800_register_read(rt2x00dev, OFDM_PROT_CFG, &reg);
-       rt2x00_set_field32(&reg, OFDM_PROT_CFG_PROTECT_CTRL,
-                          erp->cts_protection ? 2 : 0);
-       rt2800_register_write(rt2x00dev, OFDM_PROT_CFG, reg);
+       if (changed & BSS_CHANGED_ERP_CTS_PROT) {
+               rt2800_register_read(rt2x00dev, OFDM_PROT_CFG, &reg);
+               rt2x00_set_field32(&reg, OFDM_PROT_CFG_PROTECT_CTRL,
+                                  erp->cts_protection ? 2 : 0);
+               rt2800_register_write(rt2x00dev, OFDM_PROT_CFG, reg);
+       }
 
-       rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE,
-                                erp->basic_rates);
-       rt2800_register_write(rt2x00dev, HT_BASIC_RATE, 0x00008003);
+       if (changed & BSS_CHANGED_BASIC_RATES) {
+               rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE,
+                                        erp->basic_rates);
+               rt2800_register_write(rt2x00dev, HT_BASIC_RATE, 0x00008003);
+       }
 
-       rt2800_register_read(rt2x00dev, BKOFF_SLOT_CFG, &reg);
-       rt2x00_set_field32(&reg, BKOFF_SLOT_CFG_SLOT_TIME, erp->slot_time);
-       rt2800_register_write(rt2x00dev, BKOFF_SLOT_CFG, reg);
+       if (changed & BSS_CHANGED_ERP_SLOT) {
+               rt2800_register_read(rt2x00dev, BKOFF_SLOT_CFG, &reg);
+               rt2x00_set_field32(&reg, BKOFF_SLOT_CFG_SLOT_TIME,
+                                  erp->slot_time);
+               rt2800_register_write(rt2x00dev, BKOFF_SLOT_CFG, reg);
 
-       rt2800_register_read(rt2x00dev, XIFS_TIME_CFG, &reg);
-       rt2x00_set_field32(&reg, XIFS_TIME_CFG_EIFS, erp->eifs);
-       rt2800_register_write(rt2x00dev, XIFS_TIME_CFG, reg);
+               rt2800_register_read(rt2x00dev, XIFS_TIME_CFG, &reg);
+               rt2x00_set_field32(&reg, XIFS_TIME_CFG_EIFS, erp->eifs);
+               rt2800_register_write(rt2x00dev, XIFS_TIME_CFG, reg);
+       }
 
-       rt2800_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
-       rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_INTERVAL,
-                          erp->beacon_int * 16);
-       rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
+       if (changed & BSS_CHANGED_BEACON_INT) {
+               rt2800_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
+               rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_INTERVAL,
+                                  erp->beacon_int * 16);
+               rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
+       }
 }
 EXPORT_SYMBOL_GPL(rt2800_config_erp);
 
@@ -1120,27 +1296,23 @@ static void rt2800_config_channel_rf2xxx(struct rt2x00_dev *rt2x00dev,
                 * double meaning, and we should set a 7DBm boost flag.
                 */
                rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_A_7DBM_BOOST,
-                                  (info->tx_power1 >= 0));
+                                  (info->default_power1 >= 0));
 
-               if (info->tx_power1 < 0)
-                       info->tx_power1 += 7;
+               if (info->default_power1 < 0)
+                       info->default_power1 += 7;
 
-               rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_A,
-                                  TXPOWER_A_TO_DEV(info->tx_power1));
+               rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_A, info->default_power1);
 
                rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_A_7DBM_BOOST,
-                                  (info->tx_power2 >= 0));
+                                  (info->default_power2 >= 0));
 
-               if (info->tx_power2 < 0)
-                       info->tx_power2 += 7;
+               if (info->default_power2 < 0)
+                       info->default_power2 += 7;
 
-               rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_A,
-                                  TXPOWER_A_TO_DEV(info->tx_power2));
+               rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_A, info->default_power2);
        } else {
-               rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_G,
-                                  TXPOWER_G_TO_DEV(info->tx_power1));
-               rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_G,
-                                  TXPOWER_G_TO_DEV(info->tx_power2));
+               rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_G, info->default_power1);
+               rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_G, info->default_power2);
        }
 
        rt2x00_set_field32(&rf->rf4, RF4_HT40, conf_is_ht40(conf));
@@ -1180,13 +1352,11 @@ static void rt2800_config_channel_rf3xxx(struct rt2x00_dev *rt2x00dev,
        rt2800_rfcsr_write(rt2x00dev, 6, rfcsr);
 
        rt2800_rfcsr_read(rt2x00dev, 12, &rfcsr);
-       rt2x00_set_field8(&rfcsr, RFCSR12_TX_POWER,
-                         TXPOWER_G_TO_DEV(info->tx_power1));
+       rt2x00_set_field8(&rfcsr, RFCSR12_TX_POWER, info->default_power1);
        rt2800_rfcsr_write(rt2x00dev, 12, rfcsr);
 
        rt2800_rfcsr_read(rt2x00dev, 13, &rfcsr);
-       rt2x00_set_field8(&rfcsr, RFCSR13_TX_POWER,
-                         TXPOWER_G_TO_DEV(info->tx_power2));
+       rt2x00_set_field8(&rfcsr, RFCSR13_TX_POWER, info->default_power2);
        rt2800_rfcsr_write(rt2x00dev, 13, rfcsr);
 
        rt2800_rfcsr_read(rt2x00dev, 23, &rfcsr);
@@ -1210,10 +1380,19 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
        unsigned int tx_pin;
        u8 bbp;
 
+       if (rf->channel <= 14) {
+               info->default_power1 = TXPOWER_G_TO_DEV(info->default_power1);
+               info->default_power2 = TXPOWER_G_TO_DEV(info->default_power2);
+       } else {
+               info->default_power1 = TXPOWER_A_TO_DEV(info->default_power1);
+               info->default_power2 = TXPOWER_A_TO_DEV(info->default_power2);
+       }
+
        if (rt2x00_rf(rt2x00dev, RF2020) ||
            rt2x00_rf(rt2x00dev, RF3020) ||
            rt2x00_rf(rt2x00dev, RF3021) ||
-           rt2x00_rf(rt2x00dev, RF3022))
+           rt2x00_rf(rt2x00dev, RF3022) ||
+           rt2x00_rf(rt2x00dev, RF3052))
                rt2800_config_channel_rf3xxx(rt2x00dev, conf, rf, info);
        else
                rt2800_config_channel_rf2xxx(rt2x00dev, conf, rf, info);
@@ -1536,7 +1715,7 @@ EXPORT_SYMBOL_GPL(rt2800_link_tuner);
 /*
  * Initialization functions.
  */
-int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
+static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
 {
        u32 reg;
        u16 eeprom;
@@ -1885,6 +2064,14 @@ int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
        rt2x00_set_field32(&reg, LG_FBK_CFG0_CCKMCS3FBK, 2);
        rt2800_register_write(rt2x00dev, LG_FBK_CFG1, reg);
 
+       /*
+        * Do not force the BA window size, we use the TXWI to set it
+        */
+       rt2800_register_read(rt2x00dev, AMPDU_BA_WINSIZE, &reg);
+       rt2x00_set_field32(&reg, AMPDU_BA_WINSIZE_FORCE_WINSIZE_ENABLE, 0);
+       rt2x00_set_field32(&reg, AMPDU_BA_WINSIZE_FORCE_WINSIZE, 0);
+       rt2800_register_write(rt2x00dev, AMPDU_BA_WINSIZE, reg);
+
        /*
         * We must clear the error counters.
         * These registers are cleared on read,
@@ -1906,7 +2093,6 @@ int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(rt2800_init_registers);
 
 static int rt2800_wait_bbp_rf_ready(struct rt2x00_dev *rt2x00dev)
 {
@@ -1949,7 +2135,7 @@ static int rt2800_wait_bbp_ready(struct rt2x00_dev *rt2x00dev)
        return -EACCES;
 }
 
-int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
+static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
 {
        unsigned int i;
        u16 eeprom;
@@ -2044,7 +2230,6 @@ int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(rt2800_init_bbp);
 
 static u8 rt2800_init_rx_filter(struct rt2x00_dev *rt2x00dev,
                                bool bw40, u8 rfcsr24, u8 filter_target)
@@ -2106,7 +2291,7 @@ static u8 rt2800_init_rx_filter(struct rt2x00_dev *rt2x00dev,
        return rfcsr24;
 }
 
-int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev)
+static int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev)
 {
        u8 rfcsr;
        u8 bbp;
@@ -2360,7 +2545,100 @@ int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev)
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(rt2800_init_rfcsr);
+
+int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev)
+{
+       u32 reg;
+       u16 word;
+
+       /*
+        * Initialize all registers.
+        */
+       if (unlikely(rt2800_wait_wpdma_ready(rt2x00dev) ||
+                    rt2800_init_registers(rt2x00dev) ||
+                    rt2800_init_bbp(rt2x00dev) ||
+                    rt2800_init_rfcsr(rt2x00dev)))
+               return -EIO;
+
+       /*
+        * Send signal to firmware during boot time.
+        */
+       rt2800_mcu_request(rt2x00dev, MCU_BOOT_SIGNAL, 0, 0, 0);
+
+       if (rt2x00_is_usb(rt2x00dev) &&
+           (rt2x00_rt(rt2x00dev, RT3070) ||
+            rt2x00_rt(rt2x00dev, RT3071) ||
+            rt2x00_rt(rt2x00dev, RT3572))) {
+               udelay(200);
+               rt2800_mcu_request(rt2x00dev, MCU_CURRENT, 0, 0, 0);
+               udelay(10);
+       }
+
+       /*
+        * Enable RX.
+        */
+       rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
+       rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_TX, 1);
+       rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_RX, 0);
+       rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
+
+       udelay(50);
+
+       rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, &reg);
+       rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_TX_DMA, 1);
+       rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_RX_DMA, 1);
+       rt2x00_set_field32(&reg, WPDMA_GLO_CFG_WP_DMA_BURST_SIZE, 2);
+       rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1);
+       rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg);
+
+       rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
+       rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_TX, 1);
+       rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_RX, 1);
+       rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
+
+       /*
+        * Initialize LED control
+        */
+       rt2x00_eeprom_read(rt2x00dev, EEPROM_LED1, &word);
+       rt2800_mcu_request(rt2x00dev, MCU_LED_1, 0xff,
+                          word & 0xff, (word >> 8) & 0xff);
+
+       rt2x00_eeprom_read(rt2x00dev, EEPROM_LED2, &word);
+       rt2800_mcu_request(rt2x00dev, MCU_LED_2, 0xff,
+                          word & 0xff, (word >> 8) & 0xff);
+
+       rt2x00_eeprom_read(rt2x00dev, EEPROM_LED3, &word);
+       rt2800_mcu_request(rt2x00dev, MCU_LED_3, 0xff,
+                          word & 0xff, (word >> 8) & 0xff);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rt2800_enable_radio);
+
+void rt2800_disable_radio(struct rt2x00_dev *rt2x00dev)
+{
+       u32 reg;
+
+       rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, &reg);
+       rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0);
+       rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_DMA_BUSY, 0);
+       rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0);
+       rt2x00_set_field32(&reg, WPDMA_GLO_CFG_RX_DMA_BUSY, 0);
+       rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1);
+       rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg);
+
+       /* Wait for DMA, ignore error */
+       rt2800_wait_wpdma_ready(rt2x00dev);
+
+       rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
+       rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_TX, 0);
+       rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_RX, 0);
+       rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
+
+       rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0);
+       rt2800_register_write(rt2x00dev, TX_PIN_CFG, 0);
+}
+EXPORT_SYMBOL_GPL(rt2800_disable_radio);
 
 int rt2800_efuse_detect(struct rt2x00_dev *rt2x00dev)
 {
@@ -2516,6 +2794,13 @@ int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev)
                                   default_lna_gain);
        rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_A2, word);
 
+       rt2x00_eeprom_read(rt2x00dev, EEPROM_MAX_TX_POWER, &word);
+       if (rt2x00_get_field16(word, EEPROM_MAX_TX_POWER_24GHZ) == 0xff)
+               rt2x00_set_field16(&word, EEPROM_MAX_TX_POWER_24GHZ, MAX_G_TXPOWER);
+       if (rt2x00_get_field16(word, EEPROM_MAX_TX_POWER_5GHZ) == 0xff)
+               rt2x00_set_field16(&word, EEPROM_MAX_TX_POWER_5GHZ, MAX_A_TXPOWER);
+       rt2x00_eeprom_write(rt2x00dev, EEPROM_MAX_TX_POWER, word);
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(rt2800_validate_eeprom);
@@ -2755,9 +3040,10 @@ int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
 {
        struct hw_mode_spec *spec = &rt2x00dev->spec;
        struct channel_info *info;
-       char *tx_power1;
-       char *tx_power2;
+       char *default_power1;
+       char *default_power2;
        unsigned int i;
+       unsigned short max_power;
        u16 eeprom;
 
        /*
@@ -2871,21 +3157,26 @@ int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
 
        spec->channels_info = info;
 
-       tx_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG1);
-       tx_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG2);
+       rt2x00_eeprom_read(rt2x00dev, EEPROM_MAX_TX_POWER, &eeprom);
+       max_power = rt2x00_get_field16(eeprom, EEPROM_MAX_TX_POWER_24GHZ);
+       default_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG1);
+       default_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG2);
 
        for (i = 0; i < 14; i++) {
-               info[i].tx_power1 = TXPOWER_G_FROM_DEV(tx_power1[i]);
-               info[i].tx_power2 = TXPOWER_G_FROM_DEV(tx_power2[i]);
+               info[i].max_power = max_power;
+               info[i].default_power1 = TXPOWER_G_FROM_DEV(default_power1[i]);
+               info[i].default_power2 = TXPOWER_G_FROM_DEV(default_power2[i]);
        }
 
        if (spec->num_channels > 14) {
-               tx_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A1);
-               tx_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A2);
+               max_power = rt2x00_get_field16(eeprom, EEPROM_MAX_TX_POWER_5GHZ);
+               default_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A1);
+               default_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A2);
 
                for (i = 14; i < spec->num_channels; i++) {
-                       info[i].tx_power1 = TXPOWER_A_FROM_DEV(tx_power1[i]);
-                       info[i].tx_power2 = TXPOWER_A_FROM_DEV(tx_power2[i]);
+                       info[i].max_power = max_power;
+                       info[i].default_power1 = TXPOWER_A_FROM_DEV(default_power1[i]);
+                       info[i].default_power2 = TXPOWER_A_FROM_DEV(default_power2[i]);
                }
        }