]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/net/wireless/intel/iwlwifi/pcie/trans.c
Merge tag 'fbdev-v4.13-rc5' of git://github.com/bzolnier/linux
[karo-tx-linux.git] / drivers / net / wireless / intel / iwlwifi / pcie / trans.c
index 93cbc7a69bcd55d3560529c88b6a127451606cd7..f95eec52508e9bc4784f5cda71548bbd7d761b0b 100644 (file)
@@ -80,7 +80,7 @@
 #include "iwl-prph.h"
 #include "iwl-scd.h"
 #include "iwl-agn-hw.h"
-#include "iwl-fw-error-dump.h"
+#include "fw/error-dump.h"
 #include "internal.h"
 #include "iwl-fh.h"
 
@@ -224,9 +224,9 @@ void iwl_pcie_apm_config(struct iwl_trans *trans)
 
        pcie_capability_read_word(trans_pcie->pci_dev, PCI_EXP_DEVCTL2, &cap);
        trans->ltr_enabled = cap & PCI_EXP_DEVCTL2_LTR_EN;
-       dev_info(trans->dev, "L1 %sabled - LTR %sabled\n",
-                (lctl & PCI_EXP_LNKCTL_ASPM_L1) ? "En" : "Dis",
-                trans->ltr_enabled ? "En" : "Dis");
+       IWL_DEBUG_POWER(trans, "L1 %sabled - LTR %sabled\n",
+                       (lctl & PCI_EXP_LNKCTL_ASPM_L1) ? "En" : "Dis",
+                       trans->ltr_enabled ? "En" : "Dis");
 }
 
 /*
@@ -236,7 +236,8 @@ void iwl_pcie_apm_config(struct iwl_trans *trans)
  */
 static int iwl_pcie_apm_init(struct iwl_trans *trans)
 {
-       int ret = 0;
+       int ret;
+
        IWL_DEBUG_INFO(trans, "Init card's basic functions\n");
 
        /*
@@ -245,7 +246,7 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans)
         */
 
        /* Disable L0S exit timer (platform NMI Work/Around) */
-       if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
+       if (trans->cfg->device_family < IWL_DEVICE_FAMILY_8000)
                iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS,
                            CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER);
 
@@ -287,8 +288,8 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans)
                           CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
                           CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
        if (ret < 0) {
-               IWL_DEBUG_INFO(trans, "Failed to init the card\n");
-               goto out;
+               IWL_ERR(trans, "Failed to init the card\n");
+               return ret;
        }
 
        if (trans->cfg->host_interrupt_operation_mode) {
@@ -336,8 +337,7 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans)
 
        set_bit(STATUS_DEVICE_ENABLED, &trans->status);
 
-out:
-       return ret;
+       return 0;
 }
 
 /*
@@ -358,9 +358,7 @@ static void iwl_pcie_apm_lp_xtal_enable(struct iwl_trans *trans)
        __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
                                 CSR_GP_CNTRL_REG_FLAG_XTAL_ON);
 
-       /* Reset entire device - do controller reset (results in SHRD_HW_RST) */
-       iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
-       usleep_range(1000, 2000);
+       iwl_pcie_sw_reset(trans);
 
        /*
         * Set "initialization complete" bit to move adapter from
@@ -401,12 +399,7 @@ static void iwl_pcie_apm_lp_xtal_enable(struct iwl_trans *trans)
                                 apmg_xtal_cfg_reg |
                                 SHR_APMG_XTAL_CFG_XTAL_ON_REQ);
 
-       /*
-        * Reset entire device again - do controller reset (results in
-        * SHRD_HW_RST). Turn MAC off before proceeding.
-        */
-       iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
-       usleep_range(1000, 2000);
+       iwl_pcie_sw_reset(trans);
 
        /* Enable LP XTAL by indirect access through CSR */
        apmg_gp1_reg = iwl_trans_pcie_read_shr(trans, SHR_APMG_GP1_REG);
@@ -448,9 +441,9 @@ static void iwl_pcie_apm_lp_xtal_enable(struct iwl_trans *trans)
                                 ~SHR_APMG_XTAL_CFG_XTAL_ON_REQ);
 }
 
-int iwl_pcie_apm_stop_master(struct iwl_trans *trans)
+void iwl_pcie_apm_stop_master(struct iwl_trans *trans)
 {
-       int ret = 0;
+       int ret;
 
        /* stop device's busmaster DMA activity */
        iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER);
@@ -462,8 +455,6 @@ int iwl_pcie_apm_stop_master(struct iwl_trans *trans)
                IWL_WARN(trans, "Master Disable Timed Out, 100 usec\n");
 
        IWL_DEBUG_INFO(trans, "stop master\n");
-
-       return ret;
 }
 
 static void iwl_pcie_apm_stop(struct iwl_trans *trans, bool op_mode_leave)
@@ -478,7 +469,7 @@ static void iwl_pcie_apm_stop(struct iwl_trans *trans, bool op_mode_leave)
                if (trans->cfg->device_family == IWL_DEVICE_FAMILY_7000)
                        iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG,
                                          APMG_PCIDEV_STT_VAL_WAKE_ME);
-               else if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) {
+               else if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_8000) {
                        iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG,
                                    CSR_RESET_LINK_PWR_MGMT_DISABLED);
                        iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
@@ -501,9 +492,7 @@ static void iwl_pcie_apm_stop(struct iwl_trans *trans, bool op_mode_leave)
                return;
        }
 
-       /* Reset the entire device */
-       iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
-       usleep_range(1000, 2000);
+       iwl_pcie_sw_reset(trans);
 
        /*
         * Clear "initialization complete" bit to move adapter from
@@ -516,13 +505,16 @@ static void iwl_pcie_apm_stop(struct iwl_trans *trans, bool op_mode_leave)
 static int iwl_pcie_nic_init(struct iwl_trans *trans)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       int ret;
 
        /* nic_init */
        spin_lock(&trans_pcie->irq_lock);
-       iwl_pcie_apm_init(trans);
-
+       ret = iwl_pcie_apm_init(trans);
        spin_unlock(&trans_pcie->irq_lock);
 
+       if (ret)
+               return ret;
+
        iwl_pcie_set_pwr(trans, false);
 
        iwl_op_mode_nic_config(trans->op_mode);
@@ -892,7 +884,7 @@ monitor:
        if (dest->monitor_mode == EXTERNAL_MODE && trans_pcie->fw_mon_size) {
                iwl_write_prph(trans, le32_to_cpu(dest->base_reg),
                               trans_pcie->fw_mon_phys >> dest->base_shift);
-               if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+               if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_8000)
                        iwl_write_prph(trans, le32_to_cpu(dest->end_reg),
                                       (trans_pcie->fw_mon_phys +
                                        trans_pcie->fw_mon_size - 256) >>
@@ -996,14 +988,24 @@ static int iwl_pcie_load_given_ucode_8000(struct iwl_trans *trans,
 
 bool iwl_trans_check_hw_rf_kill(struct iwl_trans *trans)
 {
+       struct iwl_trans_pcie *trans_pcie =  IWL_TRANS_GET_PCIE_TRANS(trans);
        bool hw_rfkill = iwl_is_rfkill_set(trans);
+       bool prev = test_bit(STATUS_RFKILL_OPMODE, &trans->status);
+       bool report;
 
-       if (hw_rfkill)
-               set_bit(STATUS_RFKILL, &trans->status);
-       else
-               clear_bit(STATUS_RFKILL, &trans->status);
+       if (hw_rfkill) {
+               set_bit(STATUS_RFKILL_HW, &trans->status);
+               set_bit(STATUS_RFKILL_OPMODE, &trans->status);
+       } else {
+               clear_bit(STATUS_RFKILL_HW, &trans->status);
+               if (trans_pcie->opmode_down)
+                       clear_bit(STATUS_RFKILL_OPMODE, &trans->status);
+       }
+
+       report = test_bit(STATUS_RFKILL_OPMODE, &trans->status);
 
-       iwl_trans_pcie_rf_kill(trans, hw_rfkill);
+       if (prev != report)
+               iwl_trans_pcie_rf_kill(trans, report);
 
        return hw_rfkill;
 }
@@ -1128,7 +1130,6 @@ static void iwl_pcie_init_msix(struct iwl_trans_pcie *trans_pcie)
 static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-       bool hw_rfkill, was_hw_rfkill;
 
        lockdep_assert_held(&trans_pcie->mutex);
 
@@ -1137,8 +1138,6 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power)
 
        trans_pcie->is_down = true;
 
-       was_hw_rfkill = iwl_is_rfkill_set(trans);
-
        /* tell the device to stop sending interrupts */
        iwl_disable_interrupts(trans);
 
@@ -1173,9 +1172,7 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power)
        /* Stop the device, and put it in low power state */
        iwl_pcie_apm_stop(trans, false);
 
-       /* stop and reset the on-board processor */
-       iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
-       usleep_range(1000, 2000);
+       iwl_pcie_sw_reset(trans);
 
        /*
         * Upon stop, the IVAR table gets erased, so msi-x won't
@@ -1199,7 +1196,6 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power)
        clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
        clear_bit(STATUS_INT_ENABLED, &trans->status);
        clear_bit(STATUS_TPOWER_PMI, &trans->status);
-       clear_bit(STATUS_RFKILL, &trans->status);
 
        /*
         * Even if we stop the HW, we still want the RF kill
@@ -1207,26 +1203,6 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power)
         */
        iwl_enable_rfkill_int(trans);
 
-       /*
-        * Check again since the RF kill state may have changed while
-        * all the interrupts were disabled, in this case we couldn't
-        * receive the RF kill interrupt and update the state in the
-        * op_mode.
-        * Don't call the op_mode if the rkfill state hasn't changed.
-        * This allows the op_mode to call stop_device from the rfkill
-        * notification without endless recursion. Under very rare
-        * circumstances, we might have a small recursion if the rfkill
-        * state changed exactly now while we were called from stop_device.
-        * This is very unlikely but can happen and is supported.
-        */
-       hw_rfkill = iwl_is_rfkill_set(trans);
-       if (hw_rfkill)
-               set_bit(STATUS_RFKILL, &trans->status);
-       else
-               clear_bit(STATUS_RFKILL, &trans->status);
-       if (hw_rfkill != was_hw_rfkill)
-               iwl_trans_pcie_rf_kill(trans, hw_rfkill);
-
        /* re-take ownership to prevent other users from stealing the device */
        iwl_pcie_prepare_card_hw(trans);
 }
@@ -1318,7 +1294,7 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
        iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
 
        /* Load the given image to the HW */
-       if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+       if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_8000)
                ret = iwl_pcie_load_given_ucode_8000(trans, fw);
        else
                ret = iwl_pcie_load_given_ucode(trans, fw);
@@ -1339,12 +1315,45 @@ static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr)
        iwl_pcie_tx_start(trans, scd_addr);
 }
 
+void iwl_trans_pcie_handle_stop_rfkill(struct iwl_trans *trans,
+                                      bool was_in_rfkill)
+{
+       bool hw_rfkill;
+
+       /*
+        * Check again since the RF kill state may have changed while
+        * all the interrupts were disabled, in this case we couldn't
+        * receive the RF kill interrupt and update the state in the
+        * op_mode.
+        * Don't call the op_mode if the rkfill state hasn't changed.
+        * This allows the op_mode to call stop_device from the rfkill
+        * notification without endless recursion. Under very rare
+        * circumstances, we might have a small recursion if the rfkill
+        * state changed exactly now while we were called from stop_device.
+        * This is very unlikely but can happen and is supported.
+        */
+       hw_rfkill = iwl_is_rfkill_set(trans);
+       if (hw_rfkill) {
+               set_bit(STATUS_RFKILL_HW, &trans->status);
+               set_bit(STATUS_RFKILL_OPMODE, &trans->status);
+       } else {
+               clear_bit(STATUS_RFKILL_HW, &trans->status);
+               clear_bit(STATUS_RFKILL_OPMODE, &trans->status);
+       }
+       if (hw_rfkill != was_in_rfkill)
+               iwl_trans_pcie_rf_kill(trans, hw_rfkill);
+}
+
 static void iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       bool was_in_rfkill;
 
        mutex_lock(&trans_pcie->mutex);
+       trans_pcie->opmode_down = true;
+       was_in_rfkill = test_bit(STATUS_RFKILL_OPMODE, &trans->status);
        _iwl_trans_pcie_stop_device(trans, low_power);
+       iwl_trans_pcie_handle_stop_rfkill(trans, was_in_rfkill);
        mutex_unlock(&trans_pcie->mutex);
 }
 
@@ -1355,6 +1364,8 @@ void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state)
 
        lockdep_assert_held(&trans_pcie->mutex);
 
+       IWL_WARN(trans, "reporting RF_KILL (radio %s)\n",
+                state ? "disabled" : "enabled");
        if (iwl_op_mode_hw_rf_kill(trans->op_mode, state)) {
                if (trans->cfg->gen2)
                        _iwl_trans_pcie_gen2_stop_device(trans, true);
@@ -1435,7 +1446,7 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
        iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
        iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
 
-       if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+       if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_8000)
                udelay(2);
 
        ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
@@ -1635,17 +1646,19 @@ static int _iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power)
                return err;
        }
 
-       /* Reset the entire device */
-       iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
-       usleep_range(1000, 2000);
+       iwl_pcie_sw_reset(trans);
 
-       iwl_pcie_apm_init(trans);
+       err = iwl_pcie_apm_init(trans);
+       if (err)
+               return err;
 
        iwl_pcie_init_msix(trans_pcie);
 
        /* From now on, the op_mode will be kept updated about RF kill state */
        iwl_enable_rfkill_int(trans);
 
+       trans_pcie->opmode_down = false;
+
        /* Set is_down to false here so that...*/
        trans_pcie->is_down = false;
 
@@ -1822,7 +1835,7 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans,
        /* this bit wakes up the NIC */
        __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
                                 CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
-       if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+       if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_8000)
                udelay(2);
 
        /*
@@ -2045,17 +2058,52 @@ void iwl_trans_pcie_log_scd_error(struct iwl_trans *trans, struct iwl_txq *txq)
                iwl_read_direct32(trans, FH_TX_TRB_REG(fifo)));
 }
 
-static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, u32 txq_bm)
+static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, int txq_idx)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        struct iwl_txq *txq;
-       int cnt;
        unsigned long now = jiffies;
+       u8 wr_ptr;
+
+       if (!test_bit(txq_idx, trans_pcie->queue_used))
+               return -EINVAL;
+
+       IWL_DEBUG_TX_QUEUES(trans, "Emptying queue %d...\n", txq_idx);
+       txq = trans_pcie->txq[txq_idx];
+       wr_ptr = ACCESS_ONCE(txq->write_ptr);
+
+       while (txq->read_ptr != ACCESS_ONCE(txq->write_ptr) &&
+              !time_after(jiffies,
+                          now + msecs_to_jiffies(IWL_FLUSH_WAIT_MS))) {
+               u8 write_ptr = ACCESS_ONCE(txq->write_ptr);
+
+               if (WARN_ONCE(wr_ptr != write_ptr,
+                             "WR pointer moved while flushing %d -> %d\n",
+                             wr_ptr, write_ptr))
+                       return -ETIMEDOUT;
+               usleep_range(1000, 2000);
+       }
+
+       if (txq->read_ptr != txq->write_ptr) {
+               IWL_ERR(trans,
+                       "fail to flush all tx fifo queues Q %d\n", txq_idx);
+               iwl_trans_pcie_log_scd_error(trans, txq);
+               return -ETIMEDOUT;
+       }
+
+       IWL_DEBUG_TX_QUEUES(trans, "Queue %d is now empty.\n", txq_idx);
+
+       return 0;
+}
+
+static int iwl_trans_pcie_wait_txqs_empty(struct iwl_trans *trans, u32 txq_bm)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       int cnt;
        int ret = 0;
 
        /* waiting for all the tx frames complete might take a while */
        for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) {
-               u8 wr_ptr;
 
                if (cnt == trans_pcie->cmd_queue)
                        continue;
@@ -2064,34 +2112,11 @@ static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, u32 txq_bm)
                if (!(BIT(cnt) & txq_bm))
                        continue;
 
-               IWL_DEBUG_TX_QUEUES(trans, "Emptying queue %d...\n", cnt);
-               txq = trans_pcie->txq[cnt];
-               wr_ptr = ACCESS_ONCE(txq->write_ptr);
-
-               while (txq->read_ptr != ACCESS_ONCE(txq->write_ptr) &&
-                      !time_after(jiffies,
-                                  now + msecs_to_jiffies(IWL_FLUSH_WAIT_MS))) {
-                       u8 write_ptr = ACCESS_ONCE(txq->write_ptr);
-
-                       if (WARN_ONCE(wr_ptr != write_ptr,
-                                     "WR pointer moved while flushing %d -> %d\n",
-                                     wr_ptr, write_ptr))
-                               return -ETIMEDOUT;
-                       usleep_range(1000, 2000);
-               }
-
-               if (txq->read_ptr != txq->write_ptr) {
-                       IWL_ERR(trans,
-                               "fail to flush all tx fifo queues Q %d\n", cnt);
-                       ret = -ETIMEDOUT;
+               ret = iwl_trans_pcie_wait_txq_empty(trans, cnt);
+               if (ret)
                        break;
-               }
-               IWL_DEBUG_TX_QUEUES(trans, "Queue %d is now empty.\n", cnt);
        }
 
-       if (ret)
-               iwl_trans_pcie_log_scd_error(trans, txq);
-
        return ret;
 }
 
@@ -2393,17 +2418,12 @@ static ssize_t iwl_dbgfs_interrupt_write(struct file *file,
        struct iwl_trans *trans = file->private_data;
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        struct isr_statistics *isr_stats = &trans_pcie->isr_stats;
-
-       char buf[8];
-       int buf_size;
        u32 reset_flag;
+       int ret;
 
-       memset(buf, 0, sizeof(buf));
-       buf_size = min(count, sizeof(buf) -  1);
-       if (copy_from_user(buf, user_buf, buf_size))
-               return -EFAULT;
-       if (sscanf(buf, "%x", &reset_flag) != 1)
-               return -EFAULT;
+       ret = kstrtou32_from_user(user_buf, count, 16, &reset_flag);
+       if (ret)
+               return ret;
        if (reset_flag == 0)
                memset(isr_stats, 0, sizeof(*isr_stats));
 
@@ -2415,16 +2435,6 @@ static ssize_t iwl_dbgfs_csr_write(struct file *file,
                                   size_t count, loff_t *ppos)
 {
        struct iwl_trans *trans = file->private_data;
-       char buf[8];
-       int buf_size;
-       int csr;
-
-       memset(buf, 0, sizeof(buf));
-       buf_size = min(count, sizeof(buf) -  1);
-       if (copy_from_user(buf, user_buf, buf_size))
-               return -EFAULT;
-       if (sscanf(buf, "%d", &csr) != 1)
-               return -EFAULT;
 
        iwl_pcie_dump_csr(trans);
 
@@ -2449,11 +2459,50 @@ static ssize_t iwl_dbgfs_fh_reg_read(struct file *file,
        return ret;
 }
 
+static ssize_t iwl_dbgfs_rfkill_read(struct file *file,
+                                    char __user *user_buf,
+                                    size_t count, loff_t *ppos)
+{
+       struct iwl_trans *trans = file->private_data;
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       char buf[100];
+       int pos;
+
+       pos = scnprintf(buf, sizeof(buf), "debug: %d\nhw: %d\n",
+                       trans_pcie->debug_rfkill,
+                       !(iwl_read32(trans, CSR_GP_CNTRL) &
+                               CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW));
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_rfkill_write(struct file *file,
+                                     const char __user *user_buf,
+                                     size_t count, loff_t *ppos)
+{
+       struct iwl_trans *trans = file->private_data;
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       bool old = trans_pcie->debug_rfkill;
+       int ret;
+
+       ret = kstrtobool_from_user(user_buf, count, &trans_pcie->debug_rfkill);
+       if (ret)
+               return ret;
+       if (old == trans_pcie->debug_rfkill)
+               return count;
+       IWL_WARN(trans, "changing debug rfkill %d->%d\n",
+                old, trans_pcie->debug_rfkill);
+       iwl_pcie_handle_rfkill_irq(trans);
+
+       return count;
+}
+
 DEBUGFS_READ_WRITE_FILE_OPS(interrupt);
 DEBUGFS_READ_FILE_OPS(fh_reg);
 DEBUGFS_READ_FILE_OPS(rx_queue);
 DEBUGFS_READ_FILE_OPS(tx_queue);
 DEBUGFS_WRITE_FILE_OPS(csr);
+DEBUGFS_READ_WRITE_FILE_OPS(rfkill);
 
 /* Create the debugfs files and directories */
 int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans)
@@ -2465,6 +2514,7 @@ int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans)
        DEBUGFS_ADD_FILE(interrupt, dir, S_IWUSR | S_IRUSR);
        DEBUGFS_ADD_FILE(csr, dir, S_IWUSR);
        DEBUGFS_ADD_FILE(fh_reg, dir, S_IRUSR);
+       DEBUGFS_ADD_FILE(rfkill, dir, S_IWUSR | S_IRUSR);
        return 0;
 
 err:
@@ -2563,8 +2613,15 @@ static u32 iwl_trans_pcie_fh_regs_dump(struct iwl_trans *trans,
        (*data)->len = cpu_to_le32(fh_regs_len);
        val = (void *)(*data)->data;
 
-       for (i = FH_MEM_LOWER_BOUND; i < FH_MEM_UPPER_BOUND; i += sizeof(u32))
-               *val++ = cpu_to_le32(iwl_trans_pcie_read32(trans, i));
+       if (!trans->cfg->gen2)
+               for (i = FH_MEM_LOWER_BOUND; i < FH_MEM_UPPER_BOUND;
+                    i += sizeof(u32))
+                       *val++ = cpu_to_le32(iwl_trans_pcie_read32(trans, i));
+       else
+               for (i = FH_MEM_LOWER_BOUND_GEN2; i < FH_MEM_UPPER_BOUND_GEN2;
+                    i += sizeof(u32))
+                       *val++ = cpu_to_le32(iwl_trans_pcie_read_prph(trans,
+                                                                     i));
 
        iwl_trans_release_nic_access(trans, &flags);
 
@@ -2714,7 +2771,7 @@ static struct iwl_trans_dump_data
                      trans->dbg_dest_tlv->end_shift;
 
                /* Make "end" point to the actual end */
-               if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000 ||
+               if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_8000 ||
                    trans->dbg_dest_tlv->monitor_mode == MARBH_MODE)
                        end += (1 << trans->dbg_dest_tlv->end_shift);
                monitor_len = end - base;
@@ -2740,7 +2797,12 @@ static struct iwl_trans_dump_data
        len += sizeof(*data) + IWL_CSR_TO_DUMP;
 
        /* FH registers */
-       len += sizeof(*data) + (FH_MEM_UPPER_BOUND - FH_MEM_LOWER_BOUND);
+       if (trans->cfg->gen2)
+               len += sizeof(*data) +
+                      (FH_MEM_UPPER_BOUND_GEN2 - FH_MEM_LOWER_BOUND_GEN2);
+       else
+               len += sizeof(*data) +
+                      (FH_MEM_UPPER_BOUND - FH_MEM_LOWER_BOUND);
 
        if (dump_rbs) {
                /* Dump RBs is supported only for pre-9000 devices (1 queue) */
@@ -2754,6 +2816,13 @@ static struct iwl_trans_dump_data
                                  (PAGE_SIZE << trans_pcie->rx_page_order));
        }
 
+       /* Paged memory for gen2 HW */
+       if (trans->cfg->gen2)
+               for (i = 0; i < trans_pcie->init_dram.paging_cnt; i++)
+                       len += sizeof(*data) +
+                              sizeof(struct iwl_fw_error_dump_paging) +
+                              trans_pcie->init_dram.paging[i].size;
+
        dump_data = vzalloc(len);
        if (!dump_data)
                return NULL;
@@ -2793,6 +2862,28 @@ static struct iwl_trans_dump_data
        if (dump_rbs)
                len += iwl_trans_pcie_dump_rbs(trans, &data, num_rbs);
 
+       /* Paged memory for gen2 HW */
+       if (trans->cfg->gen2) {
+               for (i = 0; i < trans_pcie->init_dram.paging_cnt; i++) {
+                       struct iwl_fw_error_dump_paging *paging;
+                       dma_addr_t addr =
+                               trans_pcie->init_dram.paging[i].physical;
+                       u32 page_len = trans_pcie->init_dram.paging[i].size;
+
+                       data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING);
+                       data->len = cpu_to_le32(sizeof(*paging) + page_len);
+                       paging = (void *)data->data;
+                       paging->index = cpu_to_le32(i);
+                       dma_sync_single_for_cpu(trans->dev, addr, page_len,
+                                               DMA_BIDIRECTIONAL);
+                       memcpy(paging->data,
+                              trans_pcie->init_dram.paging[i].block, page_len);
+                       data = iwl_fw_error_next_data(data);
+
+                       len += sizeof(*data) + sizeof(*paging) + page_len;
+               }
+       }
+
        len += iwl_trans_pcie_dump_monitor(trans, &data, monitor_len);
 
        dump_data->len = len;
@@ -2835,7 +2926,6 @@ static void iwl_trans_pcie_resume(struct iwl_trans *trans)
        .ref = iwl_trans_pcie_ref,                                      \
        .unref = iwl_trans_pcie_unref,                                  \
        .dump_data = iwl_trans_pcie_dump_data,                          \
-       .wait_tx_queues_empty = iwl_trans_pcie_wait_txq_empty,          \
        .d3_suspend = iwl_trans_pcie_d3_suspend,                        \
        .d3_resume = iwl_trans_pcie_d3_resume
 
@@ -2865,6 +2955,8 @@ static const struct iwl_trans_ops trans_ops_pcie = {
 
        .txq_set_shared_mode = iwl_trans_pcie_txq_set_shared_mode,
 
+       .wait_tx_queues_empty = iwl_trans_pcie_wait_txqs_empty,
+
        .freeze_txq_timer = iwl_trans_pcie_freeze_txq_timer,
        .block_txq_ptrs = iwl_trans_pcie_block_txq_ptrs,
 };
@@ -2884,6 +2976,7 @@ static const struct iwl_trans_ops trans_ops_pcie_gen2 = {
 
        .txq_alloc = iwl_trans_pcie_dyn_txq_alloc,
        .txq_free = iwl_trans_pcie_dyn_txq_free,
+       .wait_txq_empty = iwl_trans_pcie_wait_txq_empty,
 };
 
 struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
@@ -2910,6 +3003,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
        trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
 
        trans_pcie->trans = trans;
+       trans_pcie->opmode_down = true;
        spin_lock_init(&trans_pcie->irq_lock);
        spin_lock_init(&trans_pcie->reg_lock);
        mutex_init(&trans_pcie->mutex);
@@ -2988,7 +3082,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
         * "dash" value). To keep hw_rev backwards compatible - we'll store it
         * in the old format.
         */
-       if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) {
+       if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_8000) {
                unsigned long flags;
 
                trans->hw_rev = (trans->hw_rev & 0xfff0) |
@@ -3032,6 +3126,17 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
                }
        }
 
+       /*
+        * 9000-series integrated A-step has a problem with suspend/resume
+        * and sometimes even causes the whole platform to get stuck. This
+        * workaround makes the hardware not go into the problematic state.
+        */
+       if (trans->cfg->integrated &&
+           trans->cfg->device_family == IWL_DEVICE_FAMILY_9000 &&
+           CSR_HW_REV_STEP(trans->hw_rev) == SILICON_A_STEP)
+               iwl_set_bit(trans, CSR_HOST_CHICKEN,
+                           CSR_HOST_CHICKEN_PM_IDLE_SRC_DIS_SB_PME);
+
        trans->hw_rf_id = iwl_read32(trans, CSR_HW_RF_ID);
 
        iwl_pcie_set_interrupt_capa(pdev, trans);
@@ -3045,7 +3150,8 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
        init_waitqueue_head(&trans_pcie->d0i3_waitq);
 
        if (trans_pcie->msix_enabled) {
-               if (iwl_pcie_init_msix_handler(pdev, trans_pcie))
+               ret = iwl_pcie_init_msix_handler(pdev, trans_pcie);
+               if (ret)
                        goto out_no_pci;
         } else {
                ret = iwl_pcie_alloc_ict(trans);