iwlwifi: multiple force reset mode
[safe/jmp/linux-2.6] / drivers / net / wireless / iwlwifi / iwl-core.c
index fd26c0d..500ced4 100644 (file)
@@ -2,7 +2,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2009 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -29,6 +29,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/etherdevice.h>
+#include <linux/sched.h>
 #include <net/mac80211.h>
 
 #include "iwl-eeprom.h"
@@ -46,6 +47,57 @@ MODULE_VERSION(IWLWIFI_VERSION);
 MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
 MODULE_LICENSE("GPL");
 
+/*
+ * set bt_coex_active to true, uCode will do kill/defer
+ * every time the priority line is asserted (BT is sending signals on the
+ * priority line in the PCIx).
+ * set bt_coex_active to false, uCode will ignore the BT activity and
+ * perform the normal operation
+ *
+ * User might experience transmit issue on some platform due to WiFi/BT
+ * co-exist problem. The possible behaviors are:
+ *   Able to scan and finding all the available AP
+ *   Not able to associate with any AP
+ * On those platforms, WiFi communication can be restored by set
+ * "bt_coex_active" module parameter to "false"
+ *
+ * default: bt_coex_active = true (BT_COEX_ENABLE)
+ */
+static bool bt_coex_active = true;
+module_param(bt_coex_active, bool, S_IRUGO);
+MODULE_PARM_DESC(bt_coex_active, "enable wifi/bluetooth co-exist\n");
+
+static struct iwl_wimax_coex_event_entry cu_priorities[COEX_NUM_OF_EVENTS] = {
+       {COEX_CU_UNASSOC_IDLE_RP, COEX_CU_UNASSOC_IDLE_WP,
+        0, COEX_UNASSOC_IDLE_FLAGS},
+       {COEX_CU_UNASSOC_MANUAL_SCAN_RP, COEX_CU_UNASSOC_MANUAL_SCAN_WP,
+        0, COEX_UNASSOC_MANUAL_SCAN_FLAGS},
+       {COEX_CU_UNASSOC_AUTO_SCAN_RP, COEX_CU_UNASSOC_AUTO_SCAN_WP,
+        0, COEX_UNASSOC_AUTO_SCAN_FLAGS},
+       {COEX_CU_CALIBRATION_RP, COEX_CU_CALIBRATION_WP,
+        0, COEX_CALIBRATION_FLAGS},
+       {COEX_CU_PERIODIC_CALIBRATION_RP, COEX_CU_PERIODIC_CALIBRATION_WP,
+        0, COEX_PERIODIC_CALIBRATION_FLAGS},
+       {COEX_CU_CONNECTION_ESTAB_RP, COEX_CU_CONNECTION_ESTAB_WP,
+        0, COEX_CONNECTION_ESTAB_FLAGS},
+       {COEX_CU_ASSOCIATED_IDLE_RP, COEX_CU_ASSOCIATED_IDLE_WP,
+        0, COEX_ASSOCIATED_IDLE_FLAGS},
+       {COEX_CU_ASSOC_MANUAL_SCAN_RP, COEX_CU_ASSOC_MANUAL_SCAN_WP,
+        0, COEX_ASSOC_MANUAL_SCAN_FLAGS},
+       {COEX_CU_ASSOC_AUTO_SCAN_RP, COEX_CU_ASSOC_AUTO_SCAN_WP,
+        0, COEX_ASSOC_AUTO_SCAN_FLAGS},
+       {COEX_CU_ASSOC_ACTIVE_LEVEL_RP, COEX_CU_ASSOC_ACTIVE_LEVEL_WP,
+        0, COEX_ASSOC_ACTIVE_LEVEL_FLAGS},
+       {COEX_CU_RF_ON_RP, COEX_CU_RF_ON_WP, 0, COEX_CU_RF_ON_FLAGS},
+       {COEX_CU_RF_OFF_RP, COEX_CU_RF_OFF_WP, 0, COEX_RF_OFF_FLAGS},
+       {COEX_CU_STAND_ALONE_DEBUG_RP, COEX_CU_STAND_ALONE_DEBUG_WP,
+        0, COEX_STAND_ALONE_DEBUG_FLAGS},
+       {COEX_CU_IPAN_ASSOC_LEVEL_RP, COEX_CU_IPAN_ASSOC_LEVEL_WP,
+        0, COEX_IPAN_ASSOC_LEVEL_FLAGS},
+       {COEX_CU_RSRVD1_RP, COEX_CU_RSRVD1_WP, 0, COEX_RSRVD1_FLAGS},
+       {COEX_CU_RSRVD2_RP, COEX_CU_RSRVD2_WP, 0, COEX_RSRVD2_FLAGS}
+};
+
 #define IWL_DECLARE_RATE_INFO(r, s, ip, in, rp, rn, pp, np)    \
        [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP,      \
                                    IWL_RATE_SISO_##s##M_PLCP, \
@@ -177,6 +229,7 @@ u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant)
        }
        return ant;
 }
+EXPORT_SYMBOL(iwl_toggle_tx_ant);
 
 const u8 iwl_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
 EXPORT_SYMBOL(iwl_bcast_addr);
@@ -223,7 +276,10 @@ int iwl_hw_nic_init(struct iwl_priv *priv)
        /* nic_init */
        spin_lock_irqsave(&priv->lock, flags);
        priv->cfg->ops->lib->apm_ops.init(priv);
-       iwl_write32(priv, CSR_INT_COALESCING, 512 / 32);
+
+       /* Set interrupt coalescing calibration timer to default (512 usecs) */
+       iwl_write8(priv, CSR_INT_COALESCING, IWL_HOST_INT_CALIB_TIMEOUT_DEF);
+
        spin_unlock_irqrestore(&priv->lock, flags);
 
        ret = priv->cfg->ops->lib->apm_ops.set_pwr_src(priv, IWL_PWR_SRC_VMAIN);
@@ -414,9 +470,6 @@ static void iwlcore_init_ht_hw_capab(const struct iwl_priv *priv,
        if (priv->cfg->ht_greenfield_support)
                ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD;
        ht_info->cap |= IEEE80211_HT_CAP_SGI_20;
-       ht_info->cap |= (IEEE80211_HT_CAP_SM_PS &
-                            (WLAN_HT_CAP_SM_PS_DISABLED << 2));
-
        max_bit_rate = MAX_BIT_RATE_20_MHZ;
        if (priv->hw_params.ht40_channel & BIT(band)) {
                ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
@@ -451,28 +504,6 @@ static void iwlcore_init_ht_hw_capab(const struct iwl_priv *priv,
        }
 }
 
-static void iwlcore_init_hw_rates(struct iwl_priv *priv,
-                             struct ieee80211_rate *rates)
-{
-       int i;
-
-       for (i = 0; i < IWL_RATE_COUNT_LEGACY; i++) {
-               rates[i].bitrate = iwl_rates[i].ieee * 5;
-               rates[i].hw_value = i; /* Rate scaling will work on indexes */
-               rates[i].hw_value_short = i;
-               rates[i].flags = 0;
-               if ((i >= IWL_FIRST_CCK_RATE) && (i <= IWL_LAST_CCK_RATE)) {
-                       /*
-                        * If CCK != 1M then set short preamble rate flag.
-                        */
-                       rates[i].flags |=
-                               (iwl_rates[i].plcp == IWL_RATE_1M_PLCP) ?
-                                       0 : IEEE80211_RATE_SHORT_PREAMBLE;
-               }
-       }
-}
-
-
 /**
  * iwlcore_init_geos - Initialize mac80211's geo/channel info based from eeprom
  */
@@ -604,11 +635,27 @@ void iwlcore_free_geos(struct iwl_priv *priv)
 }
 EXPORT_SYMBOL(iwlcore_free_geos);
 
+/*
+ *  iwlcore_rts_tx_cmd_flag: Set rts/cts. 3945 and 4965 only share this
+ *  function.
+ */
+void iwlcore_rts_tx_cmd_flag(struct ieee80211_tx_info *info,
+                               __le32 *tx_flags)
+{
+       if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) {
+               *tx_flags |= TX_CMD_FLG_RTS_MSK;
+               *tx_flags &= ~TX_CMD_FLG_CTS_MSK;
+       } else if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
+               *tx_flags &= ~TX_CMD_FLG_RTS_MSK;
+               *tx_flags |= TX_CMD_FLG_CTS_MSK;
+       }
+}
+EXPORT_SYMBOL(iwlcore_rts_tx_cmd_flag);
+
 static bool is_single_rx_stream(struct iwl_priv *priv)
 {
-       return !priv->current_ht_config.is_ht ||
-              ((priv->current_ht_config.mcs.rx_mask[1] == 0) &&
-               (priv->current_ht_config.mcs.rx_mask[2] == 0));
+       return priv->current_ht_config.smps == IEEE80211_SMPS_STATIC ||
+              priv->current_ht_config.single_chain_sufficient;
 }
 
 static u8 iwl_is_channel_extension(struct iwl_priv *priv,
@@ -634,10 +681,9 @@ static u8 iwl_is_channel_extension(struct iwl_priv *priv,
 u8 iwl_is_ht40_tx_allowed(struct iwl_priv *priv,
                         struct ieee80211_sta_ht_cap *sta_ht_inf)
 {
-       struct iwl_ht_info *iwl_ht_conf = &priv->current_ht_config;
+       struct iwl_ht_config *ht_conf = &priv->current_ht_config;
 
-       if ((!iwl_ht_conf->is_ht) ||
-           (iwl_ht_conf->supported_chan_width != IWL_CHANNEL_WIDTH_40MHZ))
+       if (!ht_conf->is_ht || !ht_conf->is_40mhz)
                return 0;
 
        /* We do not check for IEEE80211_HT_CAP_SUP_WIDTH_20_40
@@ -653,7 +699,7 @@ u8 iwl_is_ht40_tx_allowed(struct iwl_priv *priv,
 #endif
        return iwl_is_channel_extension(priv, priv->band,
                        le16_to_cpu(priv->staging_rxon.channel),
-                       iwl_ht_conf->extension_chan_offset);
+                       ht_conf->extension_chan_offset);
 }
 EXPORT_SYMBOL(iwl_is_ht40_tx_allowed);
 
@@ -877,11 +923,11 @@ u8 iwl_rate_get_lowest_plcp(struct iwl_priv *priv)
 }
 EXPORT_SYMBOL(iwl_rate_get_lowest_plcp);
 
-void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info)
+void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf)
 {
        struct iwl_rxon_cmd *rxon = &priv->staging_rxon;
 
-       if (!ht_info->is_ht) {
+       if (!ht_conf->is_ht) {
                rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK |
                        RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK |
                        RXON_FLG_HT40_PROT_MSK |
@@ -892,7 +938,7 @@ void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info)
        /* FIXME: if the definition of ht_protection changed, the "translation"
         * will be needed for rxon->flags
         */
-       rxon->flags |= cpu_to_le32(ht_info->ht_protection << RXON_FLG_HT_OPERATING_MODE_POS);
+       rxon->flags |= cpu_to_le32(ht_conf->ht_protection << RXON_FLG_HT_OPERATING_MODE_POS);
 
        /* Set up channel bandwidth:
         * 20 MHz only, 20/40 mixed or pure 40 if ht40 ok */
@@ -901,10 +947,10 @@ void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info)
                         RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK);
        if (iwl_is_ht40_tx_allowed(priv, NULL)) {
                /* pure ht40 */
-               if (ht_info->ht_protection == IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) {
+               if (ht_conf->ht_protection == IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) {
                        rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40;
                        /* Note: control channel is opposite of extension channel */
-                       switch (ht_info->extension_chan_offset) {
+                       switch (ht_conf->extension_chan_offset) {
                        case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
                                rxon->flags &= ~RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
                                break;
@@ -914,7 +960,7 @@ void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info)
                        }
                } else {
                        /* Note: control channel is opposite of extension channel */
-                       switch (ht_info->extension_chan_offset) {
+                       switch (ht_conf->extension_chan_offset) {
                        case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
                                rxon->flags &= ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK);
                                rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED;
@@ -937,14 +983,10 @@ void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info)
        if (priv->cfg->ops->hcmd->set_rxon_chain)
                priv->cfg->ops->hcmd->set_rxon_chain(priv);
 
-       IWL_DEBUG_ASSOC(priv, "supported HT rate 0x%X 0x%X 0x%X "
-                       "rxon flags 0x%X operation mode :0x%X "
+       IWL_DEBUG_ASSOC(priv, "rxon flags 0x%X operation mode :0x%X "
                        "extension channel offset 0x%x\n",
-                       ht_info->mcs.rx_mask[0],
-                       ht_info->mcs.rx_mask[1],
-                       ht_info->mcs.rx_mask[2],
-                       le32_to_cpu(rxon->flags), ht_info->ht_protection,
-                       ht_info->extension_chan_offset);
+                       le32_to_cpu(rxon->flags), ht_conf->ht_protection,
+                       ht_conf->extension_chan_offset);
        return;
 }
 EXPORT_SYMBOL(iwl_set_rxon_ht);
@@ -954,47 +996,43 @@ EXPORT_SYMBOL(iwl_set_rxon_ht);
 #define IWL_NUM_IDLE_CHAINS_DUAL       2
 #define IWL_NUM_IDLE_CHAINS_SINGLE     1
 
-/* Determine how many receiver/antenna chains to use.
- * More provides better reception via diversity.  Fewer saves power.
+/*
+ * Determine how many receiver/antenna chains to use.
+ *
+ * More provides better reception via diversity.  Fewer saves power
+ * at the expense of throughput, but only when not in powersave to
+ * start with.
+ *
  * MIMO (dual stream) requires at least 2, but works better with 3.
  * This does not determine *which* chains to use, just how many.
  */
 static int iwl_get_active_rx_chain_count(struct iwl_priv *priv)
 {
-       bool is_single = is_single_rx_stream(priv);
-       bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status);
-
        /* # of Rx chains to use when expecting MIMO. */
-       if (is_single || (!is_cam && (priv->current_ht_config.sm_ps ==
-                                                WLAN_HT_CAP_SM_PS_STATIC)))
+       if (is_single_rx_stream(priv))
                return IWL_NUM_RX_CHAINS_SINGLE;
        else
                return IWL_NUM_RX_CHAINS_MULTIPLE;
 }
 
+/*
+ * When we are in power saving mode, unless device support spatial
+ * multiplexing power save, use the active count for rx chain count.
+ */
 static int iwl_get_idle_rx_chain_count(struct iwl_priv *priv, int active_cnt)
 {
-       int idle_cnt;
-       bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status);
-       /* # Rx chains when idling and maybe trying to save power */
-       switch (priv->current_ht_config.sm_ps) {
-       case WLAN_HT_CAP_SM_PS_STATIC:
-       case WLAN_HT_CAP_SM_PS_DYNAMIC:
-               idle_cnt = (is_cam) ? IWL_NUM_IDLE_CHAINS_DUAL :
-                                       IWL_NUM_IDLE_CHAINS_SINGLE;
-               break;
-       case WLAN_HT_CAP_SM_PS_DISABLED:
-               idle_cnt = (is_cam) ? active_cnt : IWL_NUM_IDLE_CHAINS_SINGLE;
-               break;
-       case WLAN_HT_CAP_SM_PS_INVALID:
+       /* # Rx chains when idling, depending on SMPS mode */
+       switch (priv->current_ht_config.smps) {
+       case IEEE80211_SMPS_STATIC:
+       case IEEE80211_SMPS_DYNAMIC:
+               return IWL_NUM_IDLE_CHAINS_SINGLE;
+       case IEEE80211_SMPS_OFF:
+               return active_cnt;
        default:
-               IWL_ERR(priv, "invalid mimo ps mode %d\n",
-                          priv->current_ht_config.sm_ps);
-               WARN_ON(1);
-               idle_cnt = -1;
-               break;
+               WARN(1, "invalid SMPS mode %d",
+                    priv->current_ht_config.smps);
+               return active_cnt;
        }
-       return idle_cnt;
 }
 
 /* up to 4 chains */
@@ -1004,7 +1042,7 @@ static u8 iwl_count_chain_bitmap(u32 chain_bitmap)
        res = (chain_bitmap & BIT(0)) >> 0;
        res += (chain_bitmap & BIT(1)) >> 1;
        res += (chain_bitmap & BIT(2)) >> 2;
-       res += (chain_bitmap & BIT(4)) >> 4;
+       res += (chain_bitmap & BIT(3)) >> 3;
        return res;
 }
 
@@ -1280,18 +1318,28 @@ static void iwl_set_rate(struct iwl_priv *priv)
 
 void iwl_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb)
 {
-       struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
        struct iwl_rxon_cmd *rxon = (void *)&priv->active_rxon;
        struct iwl_csa_notification *csa = &(pkt->u.csa_notif);
-       IWL_DEBUG_11H(priv, "CSA notif: channel %d, status %d\n",
-                     le16_to_cpu(csa->channel), le32_to_cpu(csa->status));
-       rxon->channel = csa->channel;
-       priv->staging_rxon.channel = csa->channel;
+
+       if (priv->switch_rxon.switch_in_progress) {
+               if (!le32_to_cpu(csa->status) &&
+                   (csa->channel == priv->switch_rxon.channel)) {
+                       rxon->channel = csa->channel;
+                       priv->staging_rxon.channel = csa->channel;
+                       IWL_DEBUG_11H(priv, "CSA notif: channel %d\n",
+                             le16_to_cpu(csa->channel));
+               } else
+                       IWL_ERR(priv, "CSA notif (fail) : channel %d\n",
+                             le16_to_cpu(csa->channel));
+
+               priv->switch_rxon.switch_in_progress = false;
+       }
 }
 EXPORT_SYMBOL(iwl_rx_csa);
 
 #ifdef CONFIG_IWLWIFI_DEBUG
-static void iwl_print_rx_config_cmd(struct iwl_priv *priv)
+void iwl_print_rx_config_cmd(struct iwl_priv *priv)
 {
        struct iwl_rxon_cmd *rxon = &priv->staging_rxon;
 
@@ -1309,224 +1357,199 @@ static void iwl_print_rx_config_cmd(struct iwl_priv *priv)
        IWL_DEBUG_RADIO(priv, "u8[6] bssid_addr: %pM\n", rxon->bssid_addr);
        IWL_DEBUG_RADIO(priv, "u16 assoc_id: 0x%x\n", le16_to_cpu(rxon->assoc_id));
 }
-
-static const char *desc_lookup_text[] = {
-       "OK",
-       "FAIL",
-       "BAD_PARAM",
-       "BAD_CHECKSUM",
-       "NMI_INTERRUPT_WDG",
-       "SYSASSERT",
-       "FATAL_ERROR",
-       "BAD_COMMAND",
-       "HW_ERROR_TUNE_LOCK",
-       "HW_ERROR_TEMPERATURE",
-       "ILLEGAL_CHAN_FREQ",
-       "VCC_NOT_STABLE",
-       "FH_ERROR",
-       "NMI_INTERRUPT_HOST",
-       "NMI_INTERRUPT_ACTION_PT",
-       "NMI_INTERRUPT_UNKNOWN",
-       "UCODE_VERSION_MISMATCH",
-       "HW_ERROR_ABS_LOCK",
-       "HW_ERROR_CAL_LOCK_FAIL",
-       "NMI_INTERRUPT_INST_ACTION_PT",
-       "NMI_INTERRUPT_DATA_ACTION_PT",
-       "NMI_TRM_HW_ER",
-       "NMI_INTERRUPT_TRM",
-       "NMI_INTERRUPT_BREAK_POINT"
-       "DEBUG_0",
-       "DEBUG_1",
-       "DEBUG_2",
-       "DEBUG_3",
-       "UNKNOWN"
-};
-
-static const char *desc_lookup(int i)
+EXPORT_SYMBOL(iwl_print_rx_config_cmd);
+#endif
+/**
+ * iwl_irq_handle_error - called for HW or SW error interrupt from card
+ */
+void iwl_irq_handle_error(struct iwl_priv *priv)
 {
-       int max = ARRAY_SIZE(desc_lookup_text) - 1;
+       /* Set the FW error flag -- cleared on iwl_down */
+       set_bit(STATUS_FW_ERROR, &priv->status);
 
-       if (i < 0 || i > max)
-               i = max;
+       /* Cancel currently queued command. */
+       clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
 
-       return desc_lookup_text[i];
-}
+       priv->cfg->ops->lib->dump_nic_error_log(priv);
+       if (priv->cfg->ops->lib->dump_csr)
+               priv->cfg->ops->lib->dump_csr(priv);
+       if (priv->cfg->ops->lib->dump_fh)
+               priv->cfg->ops->lib->dump_fh(priv, NULL, false);
+       priv->cfg->ops->lib->dump_nic_event_log(priv, false, NULL, false);
+#ifdef CONFIG_IWLWIFI_DEBUG
+       if (iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS)
+               iwl_print_rx_config_cmd(priv);
+#endif
 
-#define ERROR_START_OFFSET  (1 * sizeof(u32))
-#define ERROR_ELEM_SIZE     (7 * sizeof(u32))
+       wake_up_interruptible(&priv->wait_command_queue);
 
-static void iwl_dump_nic_error_log(struct iwl_priv *priv)
-{
-       u32 data2, line;
-       u32 desc, time, count, base, data1;
-       u32 blink1, blink2, ilink1, ilink2;
+       /* Keep the restart process from trying to send host
+        * commands by clearing the INIT status bit */
+       clear_bit(STATUS_READY, &priv->status);
 
-       if (priv->ucode_type == UCODE_INIT)
-               base = le32_to_cpu(priv->card_alive_init.error_event_table_ptr);
-       else
-               base = le32_to_cpu(priv->card_alive.error_event_table_ptr);
+       if (!test_bit(STATUS_EXIT_PENDING, &priv->status)) {
+               IWL_DEBUG(priv, IWL_DL_FW_ERRORS,
+                         "Restarting adapter due to uCode error.\n");
 
-       if (!priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) {
-               IWL_ERR(priv, "Not valid error log pointer 0x%08X\n", base);
-               return;
+               if (priv->cfg->mod_params->restart_fw)
+                       queue_work(priv->workqueue, &priv->restart);
        }
+}
+EXPORT_SYMBOL(iwl_irq_handle_error);
 
-       count = iwl_read_targ_mem(priv, base);
+int iwl_apm_stop_master(struct iwl_priv *priv)
+{
+       int ret = 0;
 
-       if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) {
-               IWL_ERR(priv, "Start IWL Error Log Dump:\n");
-               IWL_ERR(priv, "Status: 0x%08lX, count: %d\n",
-                       priv->status, count);
-       }
+       /* stop device's busmaster DMA activity */
+       iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER);
 
-       desc = iwl_read_targ_mem(priv, base + 1 * sizeof(u32));
-       blink1 = iwl_read_targ_mem(priv, base + 3 * sizeof(u32));
-       blink2 = iwl_read_targ_mem(priv, base + 4 * sizeof(u32));
-       ilink1 = iwl_read_targ_mem(priv, base + 5 * sizeof(u32));
-       ilink2 = iwl_read_targ_mem(priv, base + 6 * sizeof(u32));
-       data1 = iwl_read_targ_mem(priv, base + 7 * sizeof(u32));
-       data2 = iwl_read_targ_mem(priv, base + 8 * sizeof(u32));
-       line = iwl_read_targ_mem(priv, base + 9 * sizeof(u32));
-       time = iwl_read_targ_mem(priv, base + 11 * sizeof(u32));
+       ret = iwl_poll_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_MASTER_DISABLED,
+                       CSR_RESET_REG_FLAG_MASTER_DISABLED, 100);
+       if (ret)
+               IWL_WARN(priv, "Master Disable Timed Out, 100 usec\n");
 
-       IWL_ERR(priv, "Desc                               Time       "
-               "data1      data2      line\n");
-       IWL_ERR(priv, "%-28s (#%02d) %010u 0x%08X 0x%08X %u\n",
-               desc_lookup(desc), desc, time, data1, data2, line);
-       IWL_ERR(priv, "blink1  blink2  ilink1  ilink2\n");
-       IWL_ERR(priv, "0x%05X 0x%05X 0x%05X 0x%05X\n", blink1, blink2,
-               ilink1, ilink2);
+       IWL_DEBUG_INFO(priv, "stop master\n");
 
+       return ret;
 }
+EXPORT_SYMBOL(iwl_apm_stop_master);
 
-#define EVENT_START_OFFSET  (4 * sizeof(u32))
-
-/**
- * iwl_print_event_log - Dump error event log to syslog
- *
- */
-static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
-                               u32 num_events, u32 mode)
+void iwl_apm_stop(struct iwl_priv *priv)
 {
-       u32 i;
-       u32 base;       /* SRAM byte address of event log header */
-       u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */
-       u32 ptr;        /* SRAM byte address of log data */
-       u32 ev, time, data; /* event log data */
+       IWL_DEBUG_INFO(priv, "Stop card, put in low power state\n");
 
-       if (num_events == 0)
-               return;
-       if (priv->ucode_type == UCODE_INIT)
-               base = le32_to_cpu(priv->card_alive_init.log_event_table_ptr);
-       else
-               base = le32_to_cpu(priv->card_alive.log_event_table_ptr);
+       /* Stop device's DMA activity */
+       iwl_apm_stop_master(priv);
 
-       if (mode == 0)
-               event_size = 2 * sizeof(u32);
-       else
-               event_size = 3 * sizeof(u32);
-
-       ptr = base + EVENT_START_OFFSET + (start_idx * event_size);
-
-       /* "time" is actually "data" for mode 0 (no timestamp).
-       * place event id # at far right for easier visual parsing. */
-       for (i = 0; i < num_events; i++) {
-               ev = iwl_read_targ_mem(priv, ptr);
-               ptr += sizeof(u32);
-               time = iwl_read_targ_mem(priv, ptr);
-               ptr += sizeof(u32);
-               if (mode == 0) {
-                       /* data, ev */
-                       IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n", time, ev);
-               } else {
-                       data = iwl_read_targ_mem(priv, ptr);
-                       ptr += sizeof(u32);
-                       IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n",
-                                       time, data, ev);
-               }
-       }
+       /* Reset the entire device */
+       iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
+
+       udelay(10);
+
+       /*
+        * Clear "initialization complete" bit to move adapter from
+        * D0A* (powered-up Active) --> D0U* (Uninitialized) state.
+        */
+       iwl_clear_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
 }
+EXPORT_SYMBOL(iwl_apm_stop);
+
 
-void iwl_dump_nic_event_log(struct iwl_priv *priv)
+/*
+ * Start up NIC's basic functionality after it has been reset
+ * (e.g. after platform boot, or shutdown via iwl_apm_stop())
+ * NOTE:  This does not load uCode nor start the embedded processor
+ */
+int iwl_apm_init(struct iwl_priv *priv)
 {
-       u32 base;       /* SRAM byte address of event log header */
-       u32 capacity;   /* event log capacity in # entries */
-       u32 mode;       /* 0 - no timestamp, 1 - timestamp recorded */
-       u32 num_wraps;  /* # times uCode wrapped to top of log */
-       u32 next_entry; /* index of next entry to be written by uCode */
-       u32 size;       /* # entries that we'll print */
+       int ret = 0;
+       u16 lctl;
 
-       if (priv->ucode_type == UCODE_INIT)
-               base = le32_to_cpu(priv->card_alive_init.log_event_table_ptr);
-       else
-               base = le32_to_cpu(priv->card_alive.log_event_table_ptr);
+       IWL_DEBUG_INFO(priv, "Init card's basic functions\n");
 
-       if (!priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) {
-               IWL_ERR(priv, "Invalid event log pointer 0x%08X\n", base);
-               return;
-       }
+       /*
+        * Use "set_bit" below rather than "write", to preserve any hardware
+        * bits already set by default after reset.
+        */
 
-       /* event log header */
-       capacity = iwl_read_targ_mem(priv, base);
-       mode = iwl_read_targ_mem(priv, base + (1 * sizeof(u32)));
-       num_wraps = iwl_read_targ_mem(priv, base + (2 * sizeof(u32)));
-       next_entry = iwl_read_targ_mem(priv, base + (3 * sizeof(u32)));
+       /* Disable L0S exit timer (platform NMI Work/Around) */
+       iwl_set_bit(priv, CSR_GIO_CHICKEN_BITS,
+                         CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER);
 
-       size = num_wraps ? capacity : next_entry;
+       /*
+        * Disable L0s without affecting L1;
+        *  don't wait for ICH L0s (ICH bug W/A)
+        */
+       iwl_set_bit(priv, CSR_GIO_CHICKEN_BITS,
+                         CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX);
 
-       /* bail out if nothing in log */
-       if (size == 0) {
-               IWL_ERR(priv, "Start IWL Event Log Dump: nothing in log\n");
-               return;
-       }
+       /* Set FH wait threshold to maximum (HW error during stress W/A) */
+       iwl_set_bit(priv, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL);
 
-       IWL_ERR(priv, "Start IWL Event Log Dump: display count %d, wraps %d\n",
-                       size, num_wraps);
+       /*
+        * Enable HAP INTA (interrupt from management bus) to
+        * wake device's PCI Express link L1a -> L0s
+        * NOTE:  This is no-op for 3945 (non-existant bit)
+        */
+       iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
+                                   CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A);
 
-       /* if uCode has wrapped back to top of log, start at the oldest entry,
-        * i.e the next one that uCode would fill. */
-       if (num_wraps)
-               iwl_print_event_log(priv, next_entry,
-                                       capacity - next_entry, mode);
-       /* (then/else) start at top of log */
-       iwl_print_event_log(priv, 0, next_entry, mode);
+       /*
+        * HW bug W/A for instability in PCIe bus L0->L0S->L1 transition.
+        * Check if BIOS (or OS) enabled L1-ASPM on this device.
+        * If so (likely), disable L0S, so device moves directly L0->L1;
+        *    costs negligible amount of power savings.
+        * If not (unlikely), enable L0S, so there is at least some
+        *    power savings, even without L1.
+        */
+       if (priv->cfg->set_l0s) {
+               lctl = iwl_pcie_link_ctl(priv);
+               if ((lctl & PCI_CFG_LINK_CTRL_VAL_L1_EN) ==
+                                       PCI_CFG_LINK_CTRL_VAL_L1_EN) {
+                       /* L1-ASPM enabled; disable(!) L0S  */
+                       iwl_set_bit(priv, CSR_GIO_REG,
+                                       CSR_GIO_REG_VAL_L0S_ENABLED);
+                       IWL_DEBUG_POWER(priv, "L1 Enabled; Disabling L0S\n");
+               } else {
+                       /* L1-ASPM disabled; enable(!) L0S */
+                       iwl_clear_bit(priv, CSR_GIO_REG,
+                                       CSR_GIO_REG_VAL_L0S_ENABLED);
+                       IWL_DEBUG_POWER(priv, "L1 Disabled; Enabling L0S\n");
+               }
+       }
 
-}
-#endif
-/**
- * iwl_irq_handle_error - called for HW or SW error interrupt from card
- */
-void iwl_irq_handle_error(struct iwl_priv *priv)
-{
-       /* Set the FW error flag -- cleared on iwl_down */
-       set_bit(STATUS_FW_ERROR, &priv->status);
+       /* Configure analog phase-lock-loop before activating to D0A */
+       if (priv->cfg->pll_cfg_val)
+               iwl_set_bit(priv, CSR_ANA_PLL_CFG, priv->cfg->pll_cfg_val);
 
-       /* Cancel currently queued command. */
-       clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
+       /*
+        * Set "initialization complete" bit to move adapter from
+        * D0U* --> D0A* (powered-up active) state.
+        */
+       iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
 
-#ifdef CONFIG_IWLWIFI_DEBUG
-       if (iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) {
-               iwl_dump_nic_error_log(priv);
-               iwl_dump_nic_event_log(priv);
-               iwl_print_rx_config_cmd(priv);
+       /*
+        * Wait for clock stabilization; once stabilized, access to
+        * device-internal resources is supported, e.g. iwl_write_prph()
+        * and accesses to uCode SRAM.
+        */
+       ret = iwl_poll_bit(priv, CSR_GP_CNTRL,
+                       CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+                       CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
+       if (ret < 0) {
+               IWL_DEBUG_INFO(priv, "Failed to init the card\n");
+               goto out;
        }
-#endif
-
-       wake_up_interruptible(&priv->wait_command_queue);
 
-       /* Keep the restart process from trying to send host
-        * commands by clearing the INIT status bit */
-       clear_bit(STATUS_READY, &priv->status);
+       /*
+        * Enable DMA and BSM (if used) clocks, wait for them to stabilize.
+        * BSM (Boostrap State Machine) is only in 3945 and 4965;
+        * later devices (i.e. 5000 and later) have non-volatile SRAM,
+        * and don't need BSM to restore data after power-saving sleep.
+        *
+        * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0" bits
+        * do not disable clocks.  This preserves any hardware bits already
+        * set by default in "CLK_CTRL_REG" after reset.
+        */
+       if (priv->cfg->use_bsm)
+               iwl_write_prph(priv, APMG_CLK_EN_REG,
+                       APMG_CLK_VAL_DMA_CLK_RQT | APMG_CLK_VAL_BSM_CLK_RQT);
+       else
+               iwl_write_prph(priv, APMG_CLK_EN_REG,
+                       APMG_CLK_VAL_DMA_CLK_RQT);
+       udelay(20);
 
-       if (!test_bit(STATUS_EXIT_PENDING, &priv->status)) {
-               IWL_DEBUG(priv, IWL_DL_FW_ERRORS,
-                         "Restarting adapter due to uCode error.\n");
+       /* Disable L1-Active */
+       iwl_set_bits_prph(priv, APMG_PCIDEV_STT_REG,
+                         APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
 
-               if (priv->cfg->mod_params->restart_fw)
-                       queue_work(priv->workqueue, &priv->restart);
-       }
+out:
+       return ret;
 }
-EXPORT_SYMBOL(iwl_irq_handle_error);
+EXPORT_SYMBOL(iwl_apm_init);
+
+
 
 void iwl_configure_filter(struct ieee80211_hw *hw,
                          unsigned int changed_flags,
@@ -1575,73 +1598,14 @@ void iwl_configure_filter(struct ieee80211_hw *hw,
 }
 EXPORT_SYMBOL(iwl_configure_filter);
 
-int iwl_setup_mac(struct iwl_priv *priv)
-{
-       int ret;
-       struct ieee80211_hw *hw = priv->hw;
-       hw->rate_control_algorithm = "iwl-agn-rs";
-
-       /* Tell mac80211 our characteristics */
-       hw->flags = IEEE80211_HW_SIGNAL_DBM |
-                   IEEE80211_HW_NOISE_DBM |
-                   IEEE80211_HW_AMPDU_AGGREGATION |
-                   IEEE80211_HW_SPECTRUM_MGMT;
-
-       if (!priv->cfg->broken_powersave)
-               hw->flags |= IEEE80211_HW_SUPPORTS_PS |
-                            IEEE80211_HW_SUPPORTS_DYNAMIC_PS;
-
-       hw->wiphy->interface_modes =
-               BIT(NL80211_IFTYPE_STATION) |
-               BIT(NL80211_IFTYPE_ADHOC);
-
-       hw->wiphy->custom_regulatory = true;
-
-       /* Firmware does not support this */
-       hw->wiphy->disable_beacon_hints = true;
-
-       /*
-        * For now, disable PS by default because it affects
-        * RX performance significantly.
-        */
-       hw->wiphy->ps_default = false;
-
-       hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX;
-       /* we create the 802.11 header and a zero-length SSID element */
-       hw->wiphy->max_scan_ie_len = IWL_MAX_PROBE_REQUEST - 24 - 2;
-
-       /* Default value; 4 EDCA QOS priorities */
-       hw->queues = 4;
-
-       hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL;
-
-       if (priv->bands[IEEE80211_BAND_2GHZ].n_channels)
-               priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
-                       &priv->bands[IEEE80211_BAND_2GHZ];
-       if (priv->bands[IEEE80211_BAND_5GHZ].n_channels)
-               priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
-                       &priv->bands[IEEE80211_BAND_5GHZ];
-
-       ret = ieee80211_register_hw(priv->hw);
-       if (ret) {
-               IWL_ERR(priv, "Failed to register hw (error %d)\n", ret);
-               return ret;
-       }
-       priv->mac80211_registered = 1;
-
-       return 0;
-}
-EXPORT_SYMBOL(iwl_setup_mac);
-
 int iwl_set_hw_params(struct iwl_priv *priv)
 {
        priv->hw_params.max_rxq_size = RX_QUEUE_SIZE;
        priv->hw_params.max_rxq_log = RX_QUEUE_SIZE_LOG;
        if (priv->cfg->mod_params->amsdu_size_8K)
-               priv->hw_params.rx_buf_size = IWL_RX_BUF_SIZE_8K;
+               priv->hw_params.rx_page_order = get_order(IWL_RX_BUF_SIZE_8K);
        else
-               priv->hw_params.rx_buf_size = IWL_RX_BUF_SIZE_4K;
-       priv->hw_params.max_pkt_size = priv->hw_params.rx_buf_size - 256;
+               priv->hw_params.rx_page_order = get_order(IWL_RX_BUF_SIZE_4K);
 
        priv->hw_params.max_beacon_itrvl = IWL_MAX_UCODE_BEACON_INTERVAL;
 
@@ -1653,71 +1617,6 @@ int iwl_set_hw_params(struct iwl_priv *priv)
 }
 EXPORT_SYMBOL(iwl_set_hw_params);
 
-int iwl_init_drv(struct iwl_priv *priv)
-{
-       int ret;
-
-       priv->ibss_beacon = NULL;
-
-       spin_lock_init(&priv->lock);
-       spin_lock_init(&priv->sta_lock);
-       spin_lock_init(&priv->hcmd_lock);
-
-       INIT_LIST_HEAD(&priv->free_frames);
-
-       mutex_init(&priv->mutex);
-
-       /* Clear the driver's (not device's) station table */
-       iwl_clear_stations_table(priv);
-
-       priv->data_retry_limit = -1;
-       priv->ieee_channels = NULL;
-       priv->ieee_rates = NULL;
-       priv->band = IEEE80211_BAND_2GHZ;
-
-       priv->iw_mode = NL80211_IFTYPE_STATION;
-
-       priv->current_ht_config.sm_ps = WLAN_HT_CAP_SM_PS_DISABLED;
-
-       /* Choose which receivers/antennas to use */
-       if (priv->cfg->ops->hcmd->set_rxon_chain)
-               priv->cfg->ops->hcmd->set_rxon_chain(priv);
-
-       iwl_init_scan_params(priv);
-
-       iwl_reset_qos(priv);
-
-       priv->qos_data.qos_active = 0;
-       priv->qos_data.qos_cap.val = 0;
-
-       priv->rates_mask = IWL_RATES_MASK;
-       /* Set the tx_power_user_lmt to the lowest power level
-        * this value will get overwritten by channel max power avg
-        * from eeprom */
-       priv->tx_power_user_lmt = IWL_TX_POWER_TARGET_POWER_MIN;
-
-       ret = iwl_init_channel_map(priv);
-       if (ret) {
-               IWL_ERR(priv, "initializing regulatory failed: %d\n", ret);
-               goto err;
-       }
-
-       ret = iwlcore_init_geos(priv);
-       if (ret) {
-               IWL_ERR(priv, "initializing geos failed: %d\n", ret);
-               goto err_free_channel_map;
-       }
-       iwlcore_init_hw_rates(priv, priv->ieee_rates);
-
-       return 0;
-
-err_free_channel_map:
-       iwl_free_channel_map(priv);
-err:
-       return ret;
-}
-EXPORT_SYMBOL(iwl_init_drv);
-
 int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force)
 {
        int ret = 0;
@@ -1765,15 +1664,6 @@ int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force)
 }
 EXPORT_SYMBOL(iwl_set_tx_power);
 
-void iwl_uninit_drv(struct iwl_priv *priv)
-{
-       iwl_calib_free_results(priv);
-       iwlcore_free_geos(priv);
-       iwl_free_channel_map(priv);
-       kfree(priv->scan);
-}
-EXPORT_SYMBOL(iwl_uninit_drv);
-
 #define ICT_COUNT (PAGE_SIZE/sizeof(u32))
 
 /* Free dram table */
@@ -1935,6 +1825,16 @@ irqreturn_t iwl_isr_ict(int irq, void *data)
        if (val == 0xffffffff)
                val = 0;
 
+       /*
+        * this is a w/a for a h/w bug. the h/w bug may cause the Rx bit
+        * (bit 15 before shifting it to 31) to clear when using interrupt
+        * coalescing. fortunately, bits 18 and 19 stay set when this happens
+        * so we use them to decide on the real state of the Rx bit.
+        * In order words, bit 15 is set if bit 18 or bit 19 are set.
+        */
+       if (val & 0xC0000)
+               val |= 0x8000;
+
        inta = (0xff & val) | ((0xff00 & val) << 16);
        IWL_DEBUG_ISR(priv, "ISR inta 0x%08x, enabled 0x%08x ict 0x%08x\n",
                        inta, inta_mask, val);
@@ -2097,28 +1997,40 @@ EXPORT_SYMBOL(iwl_isr_legacy);
 int iwl_send_bt_config(struct iwl_priv *priv)
 {
        struct iwl_bt_cmd bt_cmd = {
-               .flags = 3,
-               .lead_time = 0xAA,
-               .max_kill = 1,
+               .lead_time = BT_LEAD_TIME_DEF,
+               .max_kill = BT_MAX_KILL_DEF,
                .kill_ack_mask = 0,
                .kill_cts_mask = 0,
        };
 
+       if (!bt_coex_active)
+               bt_cmd.flags = BT_COEX_DISABLE;
+       else
+               bt_cmd.flags = BT_COEX_ENABLE;
+
+       IWL_DEBUG_INFO(priv, "BT coex %s\n",
+               (bt_cmd.flags == BT_COEX_DISABLE) ? "disable" : "active");
+
        return iwl_send_cmd_pdu(priv, REPLY_BT_CONFIG,
                                sizeof(struct iwl_bt_cmd), &bt_cmd);
 }
 EXPORT_SYMBOL(iwl_send_bt_config);
 
-int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags)
+int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear)
 {
-       u32 stat_flags = 0;
-       struct iwl_host_cmd cmd = {
-               .id = REPLY_STATISTICS_CMD,
-               .flags = flags,
-               .len = sizeof(stat_flags),
-               .data = (u8 *) &stat_flags,
+       struct iwl_statistics_cmd statistics_cmd = {
+               .configuration_flags =
+                       clear ? IWL_STATS_CONF_CLEAR_STATS : 0,
        };
-       return iwl_send_cmd(priv, &cmd);
+
+       if (flags & CMD_ASYNC)
+               return iwl_send_cmd_pdu_async(priv, REPLY_STATISTICS_CMD,
+                                              sizeof(struct iwl_statistics_cmd),
+                                              &statistics_cmd, NULL);
+       else
+               return iwl_send_cmd_pdu(priv, REPLY_STATISTICS_CMD,
+                                       sizeof(struct iwl_statistics_cmd),
+                                       &statistics_cmd);
 }
 EXPORT_SYMBOL(iwl_send_statistics_request);
 
@@ -2259,10 +2171,7 @@ void iwl_rf_kill_ct_config(struct iwl_priv *priv)
        spin_unlock_irqrestore(&priv->lock, flags);
        priv->thermal_throttle.ct_kill_toggle = false;
 
-       switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) {
-       case CSR_HW_REV_TYPE_1000:
-       case CSR_HW_REV_TYPE_6x00:
-       case CSR_HW_REV_TYPE_6x50:
+       if (priv->cfg->support_ct_kill_exit) {
                adv_cmd.critical_temperature_enter =
                        cpu_to_le32(priv->hw_params.ct_kill_threshold);
                adv_cmd.critical_temperature_exit =
@@ -2279,8 +2188,7 @@ void iwl_rf_kill_ct_config(struct iwl_priv *priv)
                                        "exit is %d\n",
                                       priv->hw_params.ct_kill_threshold,
                                       priv->hw_params.ct_kill_exit_threshold);
-               break;
-       default:
+       } else {
                cmd.critical_temperature_R =
                        cpu_to_le32(priv->hw_params.ct_kill_threshold);
 
@@ -2293,7 +2201,6 @@ void iwl_rf_kill_ct_config(struct iwl_priv *priv)
                                        "succeeded, "
                                        "critical temperature is %d\n",
                                        priv->hw_params.ct_kill_threshold);
-               break;
        }
 }
 EXPORT_SYMBOL(iwl_rf_kill_ct_config);
@@ -2325,7 +2232,7 @@ void iwl_rx_pm_sleep_notif(struct iwl_priv *priv,
                           struct iwl_rx_mem_buffer *rxb)
 {
 #ifdef CONFIG_IWLWIFI_DEBUG
-       struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
        struct iwl_sleep_notification *sleep = &(pkt->u.sleep_notif);
        IWL_DEBUG_RX(priv, "sleep mode: %d, src: %d\n",
                     sleep->pm_sleep_mode, sleep->pm_wakeup_src);
@@ -2336,7 +2243,7 @@ EXPORT_SYMBOL(iwl_rx_pm_sleep_notif);
 void iwl_rx_pm_debug_statistics_notif(struct iwl_priv *priv,
                                      struct iwl_rx_mem_buffer *rxb)
 {
-       struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
        u32 len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
        IWL_DEBUG_RADIO(priv, "Dumping %d bytes of unhandled "
                        "notification for %s:\n", len,
@@ -2348,7 +2255,7 @@ EXPORT_SYMBOL(iwl_rx_pm_debug_statistics_notif);
 void iwl_rx_reply_error(struct iwl_priv *priv,
                        struct iwl_rx_mem_buffer *rxb)
 {
-       struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
 
        IWL_ERR(priv, "Error Reply type 0x%08X cmd %s (0x%02X) "
                "seq 0x%04X ser 0x%08X\n",
@@ -2410,46 +2317,77 @@ int iwl_mac_conf_tx(struct ieee80211_hw *hw, u16 queue,
 EXPORT_SYMBOL(iwl_mac_conf_tx);
 
 static void iwl_ht_conf(struct iwl_priv *priv,
-                           struct ieee80211_bss_conf *bss_conf)
+                       struct ieee80211_bss_conf *bss_conf)
 {
-       struct ieee80211_sta_ht_cap *ht_conf;
-       struct iwl_ht_info *iwl_conf = &priv->current_ht_config;
+       struct iwl_ht_config *ht_conf = &priv->current_ht_config;
        struct ieee80211_sta *sta;
 
        IWL_DEBUG_MAC80211(priv, "enter: \n");
 
-       if (!iwl_conf->is_ht)
+       if (!ht_conf->is_ht)
                return;
 
+       ht_conf->ht_protection =
+               bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION;
+       ht_conf->non_GF_STA_present =
+               !!(bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
 
-       /*
-        * It is totally wrong to base global information on something
-        * that is valid only when associated, alas, this driver works
-        * that way and I don't know how to fix it.
-        */
+       ht_conf->single_chain_sufficient = false;
 
-       rcu_read_lock();
-       sta = ieee80211_find_sta(priv->hw, priv->bssid);
-       if (!sta) {
+       switch (priv->iw_mode) {
+       case NL80211_IFTYPE_STATION:
+               rcu_read_lock();
+               sta = ieee80211_find_sta(priv->vif, priv->bssid);
+               if (sta) {
+                       struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
+                       int maxstreams;
+
+                       maxstreams = (ht_cap->mcs.tx_params &
+                                     IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK)
+                                       >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT;
+                       maxstreams += 1;
+
+                       if ((ht_cap->mcs.rx_mask[1] == 0) &&
+                           (ht_cap->mcs.rx_mask[2] == 0))
+                               ht_conf->single_chain_sufficient = true;
+                       if (maxstreams <= 1)
+                               ht_conf->single_chain_sufficient = true;
+               } else {
+                       /*
+                        * If at all, this can only happen through a race
+                        * when the AP disconnects us while we're still
+                        * setting up the connection, in that case mac80211
+                        * will soon tell us about that.
+                        */
+                       ht_conf->single_chain_sufficient = true;
+               }
                rcu_read_unlock();
-               return;
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               ht_conf->single_chain_sufficient = true;
+               break;
+       default:
+               break;
        }
-       ht_conf = &sta->ht_cap;
-
-       iwl_conf->sm_ps = (u8)((ht_conf->cap & IEEE80211_HT_CAP_SM_PS) >> 2);
-
-       memcpy(&iwl_conf->mcs, &ht_conf->mcs, 16);
-
-       iwl_conf->ht_protection =
-               bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION;
-       iwl_conf->non_GF_STA_present =
-               !!(bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
-
-       rcu_read_unlock();
 
        IWL_DEBUG_MAC80211(priv, "leave\n");
 }
 
+static inline void iwl_set_no_assoc(struct iwl_priv *priv)
+{
+       priv->assoc_id = 0;
+       iwl_led_disassociate(priv);
+       /*
+        * inform the ucode that there is no longer an
+        * association and that no more packets should be
+        * sent
+        */
+       priv->staging_rxon.filter_flags &=
+               ~RXON_FILTER_ASSOC_MSK;
+       priv->staging_rxon.assoc_id = 0;
+       iwlcore_commit_rxon(priv);
+}
+
 #define IWL_DELAY_NEXT_SCAN_AFTER_ASSOC (HZ*6)
 void iwl_bss_info_changed(struct ieee80211_hw *hw,
                          struct ieee80211_vif *vif,
@@ -2569,6 +2507,8 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw,
                        priv->timestamp = bss_conf->timestamp;
                        priv->assoc_capability = bss_conf->assoc_capability;
 
+                       iwl_led_associate(priv);
+
                        /*
                         * We have just associated, don't start scan too early
                         * leave time for EAPOL exchange to complete.
@@ -2580,8 +2520,7 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw,
                        if (!iwl_is_rfkill(priv))
                                priv->cfg->ops->lib->post_associate(priv);
                } else
-                       priv->assoc_id = 0;
-
+                       iwl_set_no_assoc(priv);
        }
 
        if (changes && iwl_is_associated(priv) && priv->assoc_id) {
@@ -2596,6 +2535,16 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw,
                }
        }
 
+       if (changes & BSS_CHANGED_BEACON_ENABLED) {
+               if (vif->bss_conf.enable_beacon) {
+                       memcpy(priv->staging_rxon.bssid_addr,
+                              bss_conf->bssid, ETH_ALEN);
+                       memcpy(priv->bssid, bss_conf->bssid, ETH_ALEN);
+                       iwlcore_config_ap(priv);
+               } else
+                       iwl_set_no_assoc(priv);
+       }
+
        mutex_unlock(&priv->mutex);
 
        IWL_DEBUG_MAC80211(priv, "leave\n");
@@ -2679,44 +2628,43 @@ int iwl_set_mode(struct iwl_priv *priv, int mode)
 EXPORT_SYMBOL(iwl_set_mode);
 
 int iwl_mac_add_interface(struct ieee80211_hw *hw,
-                                struct ieee80211_if_init_conf *conf)
+                                struct ieee80211_vif *vif)
 {
        struct iwl_priv *priv = hw->priv;
-       unsigned long flags;
+       int err = 0;
+
+       IWL_DEBUG_MAC80211(priv, "enter: type %d\n", vif->type);
 
-       IWL_DEBUG_MAC80211(priv, "enter: type %d\n", conf->type);
+       mutex_lock(&priv->mutex);
 
        if (priv->vif) {
                IWL_DEBUG_MAC80211(priv, "leave - vif != NULL\n");
-               return -EOPNOTSUPP;
+               err = -EOPNOTSUPP;
+               goto out;
        }
 
-       spin_lock_irqsave(&priv->lock, flags);
-       priv->vif = conf->vif;
-       priv->iw_mode = conf->type;
+       priv->vif = vif;
+       priv->iw_mode = vif->type;
 
-       spin_unlock_irqrestore(&priv->lock, flags);
-
-       mutex_lock(&priv->mutex);
-
-       if (conf->mac_addr) {
-               IWL_DEBUG_MAC80211(priv, "Set %pM\n", conf->mac_addr);
-               memcpy(priv->mac_addr, conf->mac_addr, ETH_ALEN);
+       if (vif->addr) {
+               IWL_DEBUG_MAC80211(priv, "Set %pM\n", vif->addr);
+               memcpy(priv->mac_addr, vif->addr, ETH_ALEN);
        }
 
-       if (iwl_set_mode(priv, conf->type) == -EAGAIN)
+       if (iwl_set_mode(priv, vif->type) == -EAGAIN)
                /* we are not ready, will run again when ready */
                set_bit(STATUS_MODE_PENDING, &priv->status);
 
+ out:
        mutex_unlock(&priv->mutex);
 
        IWL_DEBUG_MAC80211(priv, "leave\n");
-       return 0;
+       return err;
 }
 EXPORT_SYMBOL(iwl_mac_add_interface);
 
 void iwl_mac_remove_interface(struct ieee80211_hw *hw,
-                                    struct ieee80211_if_init_conf *conf)
+                                    struct ieee80211_vif *vif)
 {
        struct iwl_priv *priv = hw->priv;
 
@@ -2729,7 +2677,7 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw,
                priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
                iwlcore_commit_rxon(priv);
        }
-       if (priv->vif == conf->vif) {
+       if (priv->vif == vif) {
                priv->vif = NULL;
                memset(priv->bssid, 0, ETH_ALEN);
        }
@@ -2752,7 +2700,7 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
        struct iwl_priv *priv = hw->priv;
        const struct iwl_channel_info *ch_info;
        struct ieee80211_conf *conf = &hw->conf;
-       struct iwl_ht_info *ht_conf = &priv->current_ht_config;
+       struct iwl_ht_config *ht_conf = &priv->current_ht_config;
        unsigned long flags = 0;
        int ret = 0;
        u16 ch;
@@ -2769,6 +2717,21 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
                IWL_DEBUG_MAC80211(priv, "leave - scanning\n");
        }
 
+       if (changed & (IEEE80211_CONF_CHANGE_SMPS |
+                      IEEE80211_CONF_CHANGE_CHANNEL)) {
+               /* mac80211 uses static for non-HT which is what we want */
+               priv->current_ht_config.smps = conf->smps_mode;
+
+               /*
+                * Recalculate chain counts.
+                *
+                * If monitor mode is enabled then mac80211 will
+                * set up the SM PS mode to OFF if an HT channel is
+                * configured.
+                */
+               if (priv->cfg->ops->hcmd->set_rxon_chain)
+                       priv->cfg->ops->hcmd->set_rxon_chain(priv);
+       }
 
        /* during scanning mac80211 will delay channel setting until
         * scan finish with changed = 0
@@ -2802,21 +2765,18 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
                        if (conf_is_ht40_minus(conf)) {
                                ht_conf->extension_chan_offset =
                                        IEEE80211_HT_PARAM_CHA_SEC_BELOW;
-                               ht_conf->supported_chan_width =
-                                       IWL_CHANNEL_WIDTH_40MHZ;
+                               ht_conf->is_40mhz = true;
                        } else if (conf_is_ht40_plus(conf)) {
                                ht_conf->extension_chan_offset =
                                        IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
-                               ht_conf->supported_chan_width =
-                                       IWL_CHANNEL_WIDTH_40MHZ;
+                               ht_conf->is_40mhz = true;
                        } else {
                                ht_conf->extension_chan_offset =
                                        IEEE80211_HT_PARAM_CHA_SEC_NONE;
-                               ht_conf->supported_chan_width =
-                                       IWL_CHANNEL_WIDTH_20MHZ;
+                               ht_conf->is_40mhz = false;
                        }
                } else
-                       ht_conf->supported_chan_width = IWL_CHANNEL_WIDTH_20MHZ;
+                       ht_conf->is_40mhz = false;
                /* Default to no protection. Protection mode will later be set
                 * from BSS config in iwl_ht_conf */
                ht_conf->ht_protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE;
@@ -2827,10 +2787,27 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
                if ((le16_to_cpu(priv->staging_rxon.channel) != ch))
                        priv->staging_rxon.flags = 0;
 
+               iwl_set_rxon_ht(priv, ht_conf);
                iwl_set_rxon_channel(priv, conf->channel);
 
                iwl_set_flags_for_band(priv, conf->channel->band);
                spin_unlock_irqrestore(&priv->lock, flags);
+               if (iwl_is_associated(priv) &&
+                   (le16_to_cpu(priv->active_rxon.channel) != ch) &&
+                   priv->cfg->ops->lib->set_channel_switch) {
+                       iwl_set_rate(priv);
+                       /*
+                        * at this point, staging_rxon has the
+                        * configuration for channel switch
+                        */
+                       ret = priv->cfg->ops->lib->set_channel_switch(priv,
+                               ch);
+                       if (!ret) {
+                               iwl_print_rx_config_cmd(priv);
+                               goto out;
+                       }
+                       priv->switch_rxon.switch_in_progress = false;
+               }
  set_ch_out:
                /* The list of supported rates and rate mask can be different
                 * for each band; since the band may have changed, reset
@@ -2838,7 +2815,8 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
                iwl_set_rate(priv);
        }
 
-       if (changed & IEEE80211_CONF_CHANGE_PS) {
+       if (changed & (IEEE80211_CONF_CHANGE_PS |
+                       IEEE80211_CONF_CHANGE_IDLE)) {
                ret = iwl_power_update_mode(priv, false);
                if (ret)
                        IWL_DEBUG_MAC80211(priv, "Error setting sleep level\n");
@@ -2851,10 +2829,6 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
                iwl_set_tx_power(priv, conf->power_level, false);
        }
 
-       /* call to ensure that 4965 rx_chain is set properly in monitor mode */
-       if (priv->cfg->ops->hcmd->set_rxon_chain)
-               priv->cfg->ops->hcmd->set_rxon_chain(priv);
-
        if (!iwl_is_ready(priv)) {
                IWL_DEBUG_MAC80211(priv, "leave - not ready\n");
                goto out;
@@ -2877,42 +2851,6 @@ out:
 }
 EXPORT_SYMBOL(iwl_mac_config);
 
-int iwl_mac_get_tx_stats(struct ieee80211_hw *hw,
-                        struct ieee80211_tx_queue_stats *stats)
-{
-       struct iwl_priv *priv = hw->priv;
-       int i, avail;
-       struct iwl_tx_queue *txq;
-       struct iwl_queue *q;
-       unsigned long flags;
-
-       IWL_DEBUG_MAC80211(priv, "enter\n");
-
-       if (!iwl_is_ready_rf(priv)) {
-               IWL_DEBUG_MAC80211(priv, "leave - RF not ready\n");
-               return -EIO;
-       }
-
-       spin_lock_irqsave(&priv->lock, flags);
-
-       for (i = 0; i < AC_NUM; i++) {
-               txq = &priv->txq[i];
-               q = &txq->q;
-               avail = iwl_queue_space(q);
-
-               stats[i].len = q->n_window - avail;
-               stats[i].limit = q->n_window - q->high_mark;
-               stats[i].count = q->n_window;
-
-       }
-       spin_unlock_irqrestore(&priv->lock, flags);
-
-       IWL_DEBUG_MAC80211(priv, "leave\n");
-
-       return 0;
-}
-EXPORT_SYMBOL(iwl_mac_get_tx_stats);
-
 void iwl_mac_reset_tsf(struct ieee80211_hw *hw)
 {
        struct iwl_priv *priv = hw->priv;
@@ -2922,7 +2860,7 @@ void iwl_mac_reset_tsf(struct ieee80211_hw *hw)
        IWL_DEBUG_MAC80211(priv, "enter\n");
 
        spin_lock_irqsave(&priv->lock, flags);
-       memset(&priv->current_ht_config, 0, sizeof(struct iwl_ht_info));
+       memset(&priv->current_ht_config, 0, sizeof(struct iwl_ht_config));
        spin_unlock_irqrestore(&priv->lock, flags);
 
        iwl_reset_qos(priv);
@@ -2974,6 +2912,55 @@ void iwl_mac_reset_tsf(struct ieee80211_hw *hw)
 }
 EXPORT_SYMBOL(iwl_mac_reset_tsf);
 
+int iwl_alloc_txq_mem(struct iwl_priv *priv)
+{
+       if (!priv->txq)
+               priv->txq = kzalloc(
+                       sizeof(struct iwl_tx_queue) * priv->cfg->num_of_queues,
+                       GFP_KERNEL);
+       if (!priv->txq) {
+               IWL_ERR(priv, "Not enough memory for txq \n");
+               return -ENOMEM;
+       }
+       return 0;
+}
+EXPORT_SYMBOL(iwl_alloc_txq_mem);
+
+void iwl_free_txq_mem(struct iwl_priv *priv)
+{
+       kfree(priv->txq);
+       priv->txq = NULL;
+}
+EXPORT_SYMBOL(iwl_free_txq_mem);
+
+int iwl_send_wimax_coex(struct iwl_priv *priv)
+{
+       struct iwl_wimax_coex_cmd uninitialized_var(coex_cmd);
+
+       if (priv->cfg->support_wimax_coexist) {
+               /* UnMask wake up src at associated sleep */
+               coex_cmd.flags |= COEX_FLAGS_ASSOC_WA_UNMASK_MSK;
+
+               /* UnMask wake up src at unassociated sleep */
+               coex_cmd.flags |= COEX_FLAGS_UNASSOC_WA_UNMASK_MSK;
+               memcpy(coex_cmd.sta_prio, cu_priorities,
+                       sizeof(struct iwl_wimax_coex_event_entry) *
+                        COEX_NUM_OF_EVENTS);
+
+               /* enabling the coexistence feature */
+               coex_cmd.flags |= COEX_FLAGS_COEX_ENABLE_MSK;
+
+               /* enabling the priorities tables */
+               coex_cmd.flags |= COEX_FLAGS_STA_TABLE_VALID_MSK;
+       } else {
+               /* coexistence is disabled */
+               memset(&coex_cmd, 0, sizeof(coex_cmd));
+       }
+       return iwl_send_cmd_pdu(priv, COEX_PRIORITY_TABLE_CMD,
+                               sizeof(coex_cmd), &coex_cmd);
+}
+EXPORT_SYMBOL(iwl_send_wimax_coex);
+
 #ifdef CONFIG_IWLWIFI_DEBUGFS
 
 #define IWL_TRAFFIC_DUMP_SIZE  (IWL_TRAFFIC_ENTRY_SIZE * IWL_TRAFFIC_ENTRIES)
@@ -3111,15 +3098,11 @@ const char *get_ctrl_string(int cmd)
        }
 }
 
-void iwl_clear_tx_stats(struct iwl_priv *priv)
+void iwl_clear_traffic_stats(struct iwl_priv *priv)
 {
        memset(&priv->tx_stats, 0, sizeof(struct traffic_stats));
-
-}
-
-void iwl_clear_rx_stats(struct iwl_priv *priv)
-{
        memset(&priv->rx_stats, 0, sizeof(struct traffic_stats));
+       priv->led_tpt = 0;
 }
 
 /*
@@ -3212,10 +3195,209 @@ void iwl_update_stats(struct iwl_priv *priv, bool is_tx, __le16 fc, u16 len)
                stats->data_cnt++;
                stats->data_bytes += len;
        }
+       iwl_leds_background(priv);
 }
 EXPORT_SYMBOL(iwl_update_stats);
 #endif
 
+const static char *get_csr_string(int cmd)
+{
+       switch (cmd) {
+               IWL_CMD(CSR_HW_IF_CONFIG_REG);
+               IWL_CMD(CSR_INT_COALESCING);
+               IWL_CMD(CSR_INT);
+               IWL_CMD(CSR_INT_MASK);
+               IWL_CMD(CSR_FH_INT_STATUS);
+               IWL_CMD(CSR_GPIO_IN);
+               IWL_CMD(CSR_RESET);
+               IWL_CMD(CSR_GP_CNTRL);
+               IWL_CMD(CSR_HW_REV);
+               IWL_CMD(CSR_EEPROM_REG);
+               IWL_CMD(CSR_EEPROM_GP);
+               IWL_CMD(CSR_OTP_GP_REG);
+               IWL_CMD(CSR_GIO_REG);
+               IWL_CMD(CSR_GP_UCODE_REG);
+               IWL_CMD(CSR_GP_DRIVER_REG);
+               IWL_CMD(CSR_UCODE_DRV_GP1);
+               IWL_CMD(CSR_UCODE_DRV_GP2);
+               IWL_CMD(CSR_LED_REG);
+               IWL_CMD(CSR_DRAM_INT_TBL_REG);
+               IWL_CMD(CSR_GIO_CHICKEN_BITS);
+               IWL_CMD(CSR_ANA_PLL_CFG);
+               IWL_CMD(CSR_HW_REV_WA_REG);
+               IWL_CMD(CSR_DBG_HPET_MEM_REG);
+       default:
+               return "UNKNOWN";
+
+       }
+}
+
+void iwl_dump_csr(struct iwl_priv *priv)
+{
+       int i;
+       u32 csr_tbl[] = {
+               CSR_HW_IF_CONFIG_REG,
+               CSR_INT_COALESCING,
+               CSR_INT,
+               CSR_INT_MASK,
+               CSR_FH_INT_STATUS,
+               CSR_GPIO_IN,
+               CSR_RESET,
+               CSR_GP_CNTRL,
+               CSR_HW_REV,
+               CSR_EEPROM_REG,
+               CSR_EEPROM_GP,
+               CSR_OTP_GP_REG,
+               CSR_GIO_REG,
+               CSR_GP_UCODE_REG,
+               CSR_GP_DRIVER_REG,
+               CSR_UCODE_DRV_GP1,
+               CSR_UCODE_DRV_GP2,
+               CSR_LED_REG,
+               CSR_DRAM_INT_TBL_REG,
+               CSR_GIO_CHICKEN_BITS,
+               CSR_ANA_PLL_CFG,
+               CSR_HW_REV_WA_REG,
+               CSR_DBG_HPET_MEM_REG
+       };
+       IWL_ERR(priv, "CSR values:\n");
+       IWL_ERR(priv, "(2nd byte of CSR_INT_COALESCING is "
+               "CSR_INT_PERIODIC_REG)\n");
+       for (i = 0; i <  ARRAY_SIZE(csr_tbl); i++) {
+               IWL_ERR(priv, "  %25s: 0X%08x\n",
+                       get_csr_string(csr_tbl[i]),
+                       iwl_read32(priv, csr_tbl[i]));
+       }
+}
+EXPORT_SYMBOL(iwl_dump_csr);
+
+const static char *get_fh_string(int cmd)
+{
+       switch (cmd) {
+               IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG);
+               IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG);
+               IWL_CMD(FH_RSCSR_CHNL0_WPTR);
+               IWL_CMD(FH_MEM_RCSR_CHNL0_CONFIG_REG);
+               IWL_CMD(FH_MEM_RSSR_SHARED_CTRL_REG);
+               IWL_CMD(FH_MEM_RSSR_RX_STATUS_REG);
+               IWL_CMD(FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV);
+               IWL_CMD(FH_TSSR_TX_STATUS_REG);
+               IWL_CMD(FH_TSSR_TX_ERROR_REG);
+       default:
+               return "UNKNOWN";
+
+       }
+}
+
+int iwl_dump_fh(struct iwl_priv *priv, char **buf, bool display)
+{
+       int i;
+#ifdef CONFIG_IWLWIFI_DEBUG
+       int pos = 0;
+       size_t bufsz = 0;
+#endif
+       u32 fh_tbl[] = {
+               FH_RSCSR_CHNL0_STTS_WPTR_REG,
+               FH_RSCSR_CHNL0_RBDCB_BASE_REG,
+               FH_RSCSR_CHNL0_WPTR,
+               FH_MEM_RCSR_CHNL0_CONFIG_REG,
+               FH_MEM_RSSR_SHARED_CTRL_REG,
+               FH_MEM_RSSR_RX_STATUS_REG,
+               FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV,
+               FH_TSSR_TX_STATUS_REG,
+               FH_TSSR_TX_ERROR_REG
+       };
+#ifdef CONFIG_IWLWIFI_DEBUG
+       if (display) {
+               bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40;
+               *buf = kmalloc(bufsz, GFP_KERNEL);
+               if (!*buf)
+                       return -ENOMEM;
+               pos += scnprintf(*buf + pos, bufsz - pos,
+                               "FH register values:\n");
+               for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) {
+                       pos += scnprintf(*buf + pos, bufsz - pos,
+                               "  %34s: 0X%08x\n",
+                               get_fh_string(fh_tbl[i]),
+                               iwl_read_direct32(priv, fh_tbl[i]));
+               }
+               return pos;
+       }
+#endif
+       IWL_ERR(priv, "FH register values:\n");
+       for (i = 0; i <  ARRAY_SIZE(fh_tbl); i++) {
+               IWL_ERR(priv, "  %34s: 0X%08x\n",
+                       get_fh_string(fh_tbl[i]),
+                       iwl_read_direct32(priv, fh_tbl[i]));
+       }
+       return 0;
+}
+EXPORT_SYMBOL(iwl_dump_fh);
+
+static void iwl_force_rf_reset(struct iwl_priv *priv)
+{
+       if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+               return;
+
+       if (!iwl_is_associated(priv)) {
+               IWL_DEBUG_SCAN(priv, "force reset rejected: not associated\n");
+               return;
+       }
+       /*
+        * There is no easy and better way to force reset the radio,
+        * the only known method is switching channel which will force to
+        * reset and tune the radio.
+        * Use internal short scan (single channel) operation to should
+        * achieve this objective.
+        * Driver should reset the radio when number of consecutive missed
+        * beacon, or any other uCode error condition detected.
+        */
+       IWL_DEBUG_INFO(priv, "perform radio reset.\n");
+       iwl_internal_short_hw_scan(priv);
+       return;
+}
+
+#define IWL_DELAY_NEXT_FORCE_RESET (HZ*3)
+
+int iwl_force_reset(struct iwl_priv *priv, int mode)
+{
+       if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+               return -EINVAL;
+
+       if (priv->last_force_reset_jiffies &&
+           time_after(priv->last_force_reset_jiffies +
+                      IWL_DELAY_NEXT_FORCE_RESET, jiffies)) {
+               IWL_DEBUG_INFO(priv, "force reset rejected\n");
+               return -EAGAIN;
+       }
+
+       IWL_DEBUG_INFO(priv, "perform force reset (%d)\n", mode);
+
+       switch (mode) {
+       case IWL_RF_RESET:
+               iwl_force_rf_reset(priv);
+               break;
+       case IWL_FW_RESET:
+               IWL_ERR(priv, "On demand firmware reload\n");
+               /* Set the FW error flag -- cleared on iwl_down */
+               set_bit(STATUS_FW_ERROR, &priv->status);
+               wake_up_interruptible(&priv->wait_command_queue);
+               /*
+                * Keep the restart process from trying to send host
+                * commands by clearing the INIT status bit
+                */
+               clear_bit(STATUS_READY, &priv->status);
+               queue_work(priv->workqueue, &priv->restart);
+               break;
+       default:
+               IWL_DEBUG_INFO(priv, "invalid reset request.\n");
+               return -EINVAL;
+       }
+       priv->last_force_reset_jiffies = jiffies;
+
+       return 0;
+}
+
 #ifdef CONFIG_PM
 
 int iwl_pci_suspend(struct pci_dev *pdev, pm_message_t state)