Merge tag 'iwlwifi-next-for-kalle-2014-12-30' of https://git.kernel.org/pub/scm/linux...
authorKalle Valo <kvalo@codeaurora.org>
Tue, 6 Jan 2015 17:36:11 +0000 (19:36 +0200)
committerKalle Valo <kvalo@codeaurora.org>
Tue, 6 Jan 2015 17:36:11 +0000 (19:36 +0200)
* more work on d0i3 power state
* enhancements to the firmware debugging infrastructure
* support for 2 concurrent channel contexts
* fixes / cleanups in the rate control
* general cleanups

1  2 
drivers/net/wireless/iwlwifi/iwl-drv.c
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/pcie/trans.c

   *
   ******************************************************************************/
  
- /*
-  * module name, copyright, version, etc.
-  */
  #define DRV_DESCRIPTION       "Intel(R) Wireless WiFi driver for Linux"
- #ifdef CONFIG_IWLWIFI_DEBUG
- #define VD "d"
- #else
- #define VD
- #endif
- #define DRV_VERSION     IWLWIFI_VERSION VD
  MODULE_DESCRIPTION(DRV_DESCRIPTION);
- MODULE_VERSION(DRV_VERSION);
  MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
  MODULE_LICENSE("GPL");
  
@@@ -250,9 -237,6 +237,6 @@@ static int iwl_request_firmware(struct 
        /*
         * Starting 8000B - FW name format has changed. This overwrites the
         * previous name and uses the new format.
-        *
-        * TODO:
-        * Once there is only one supported step for 8000 family - delete this!
         */
        if (drv->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) {
                char rev_step[2] = {
                if (CSR_HW_REV_STEP(drv->trans->hw_rev) == SILICON_A_STEP)
                        rev_step[0] = 0;
  
-               /*
-                * If hw_rev wasn't set yet - default as B-step. If it IS A-step
-                * we'll reload that FW later instead.
-                */
-               if (drv->trans->hw_rev == 0)
-                       rev_step[0] = 'B';
                snprintf(drv->firmware_name, sizeof(drv->firmware_name),
                         "%s%s-%s.ucode", name_pre, rev_step, tag);
        }
@@@ -926,6 -903,12 +903,12 @@@ static int iwl_parse_tlv_firmware(struc
                                            IWL_UCODE_REGULAR_USNIFFER,
                                            tlv_len);
                        break;
+               case IWL_UCODE_TLV_SDIO_ADMA_ADDR:
+                       if (tlv_len != sizeof(u32))
+                               goto invalid_tlv_len;
+                       drv->fw.sdio_adma_addr =
+                               le32_to_cpup((__le32 *)tlv_data);
+                       break;
                default:
                        IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
                        break;
@@@ -1082,7 -1065,6 +1065,6 @@@ static void iwl_req_fw_callback(const s
        u32 api_ver;
        int i;
        bool load_module = false;
-       u32 hw_rev = drv->trans->hw_rev;
  
        fw->ucode_capa.max_probe_length = IWL_DEFAULT_MAX_PROBE_LENGTH;
        fw->ucode_capa.standard_phy_calibration_size =
                                op->name, err);
  #endif
        }
-       /*
-        * We may have loaded the wrong FW file in 8000 HW family if it is an
-        * A-step card, and if drv->trans->hw_rev wasn't properly read when
-        * the FW file had been loaded. (This might happen in SDIO.) In such a
-        * case - unload and reload the correct file.
-        *
-        * TODO:
-        * Once there is only one supported step for 8000 family - delete this!
-        */
-       if (drv->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
-           CSR_HW_REV_STEP(drv->trans->hw_rev) == SILICON_A_STEP &&
-           drv->trans->hw_rev != hw_rev) {
-               char firmware_name[32];
-               /* Free previous FW resources */
-               if (drv->op_mode)
-                       _iwl_op_mode_stop(drv);
-               iwl_dealloc_ucode(drv);
-               /* Build name of correct-step FW */
-               snprintf(firmware_name, sizeof(firmware_name),
-                        strrchr(drv->firmware_name, '-'));
-               snprintf(drv->firmware_name, sizeof(drv->firmware_name),
-                        "%s%s", drv->cfg->fw_name_pre, firmware_name);
-               /* Clear data before loading correct FW */
-               list_del(&drv->list);
-               /* Request correct FW file this time */
-               IWL_DEBUG_INFO(drv, "attempting to load A-step FW %s\n",
-                              drv->firmware_name);
-               err = request_firmware(&ucode_raw, drv->firmware_name,
-                                      drv->trans->dev);
-               if (err) {
-                       IWL_ERR(drv, "Failed swapping FW!\n");
-                       goto out_unbind;
-               }
-               /* Redo callback function - this time with right FW */
-               iwl_req_fw_callback(ucode_raw, context);
-       }
-       kfree(pieces);
        return;
  
   try_again:
        /* try next, if any */
 -      kfree(pieces);
        release_firmware(ucode_raw);
        if (iwl_request_firmware(drv, false))
                goto out_unbind;
 +      kfree(pieces);
        return;
  
   out_free_fw:
@@@ -1430,6 -1368,7 +1368,7 @@@ struct iwl_mod_params iwlwifi_mod_param
        .bt_coex_active = true,
        .power_level = IWL_POWER_INDEX_1,
        .wd_disable = true,
+       .d0i3_disable = true,
  #ifndef CONFIG_IWLWIFI_UAPSD
        .uapsd_disable = true,
  #endif /* CONFIG_IWLWIFI_UAPSD */
@@@ -1492,7 -1431,7 +1431,7 @@@ static int __init iwl_drv_init(void
        for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++)
                INIT_LIST_HEAD(&iwlwifi_opmode_table[i].drv);
  
-       pr_info(DRV_DESCRIPTION ", " DRV_VERSION "\n");
+       pr_info(DRV_DESCRIPTION "\n");
        pr_info(DRV_COPYRIGHT "\n");
  
  #ifdef CONFIG_IWLWIFI_DEBUGFS
@@@ -1546,6 -1485,10 +1485,10 @@@ MODULE_PARM_DESC(wd_disable
  module_param_named(nvm_file, iwlwifi_mod_params.nvm_file, charp, S_IRUGO);
  MODULE_PARM_DESC(nvm_file, "NVM file name");
  
+ module_param_named(d0i3_disable, iwlwifi_mod_params.d0i3_disable,
+                  bool, S_IRUGO);
+ MODULE_PARM_DESC(d0i3_disable, "disable d0i3 functionality (default: Y)");
  module_param_named(uapsd_disable, iwlwifi_mod_params.uapsd_disable,
                   bool, S_IRUGO);
  #ifdef CONFIG_IWLWIFI_UAPSD
@@@ -85,6 -85,7 +85,7 @@@
  #include "testmode.h"
  #include "iwl-fw-error-dump.h"
  #include "iwl-prph.h"
+ #include "iwl-csr.h"
  
  static const struct ieee80211_iface_limit iwl_mvm_limits[] = {
        {
  
  static const struct ieee80211_iface_combination iwl_mvm_iface_combinations[] = {
        {
-               .num_different_channels = 1,
+               .num_different_channels = 2,
                .max_interfaces = 3,
                .limits = iwl_mvm_limits,
                .n_limits = ARRAY_SIZE(iwl_mvm_limits),
@@@ -377,6 -378,8 +378,8 @@@ int iwl_mvm_mac_setup_register(struct i
  
        hw->wiphy->max_remain_on_channel_duration = 10000;
        hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL;
+       /* we can compensate an offset of up to 3 channels = 15 MHz */
+       hw->wiphy->max_adj_channel_rssi_comp = 3 * 5;
  
        /* Extract MAC address */
        memcpy(mvm->addresses[0].addr, mvm->nvm_data->hw_addr, ETH_ALEN);
            device_can_wakeup(mvm->trans->dev)) {
                mvm->wowlan.flags = WIPHY_WOWLAN_ANY;
                hw->wiphy->wowlan = &mvm->wowlan;
-       } else if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len &&
+       }
+       if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len &&
            mvm->trans->ops->d3_suspend &&
            mvm->trans->ops->d3_resume &&
            device_can_wakeup(mvm->trans->dev)) {
-               mvm->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
-                                   WIPHY_WOWLAN_DISCONNECT |
-                                   WIPHY_WOWLAN_EAP_IDENTITY_REQ |
-                                   WIPHY_WOWLAN_RFKILL_RELEASE |
-                                   WIPHY_WOWLAN_NET_DETECT;
+               mvm->wowlan.flags |= WIPHY_WOWLAN_MAGIC_PKT |
+                                    WIPHY_WOWLAN_DISCONNECT |
+                                    WIPHY_WOWLAN_EAP_IDENTITY_REQ |
+                                    WIPHY_WOWLAN_RFKILL_RELEASE |
+                                    WIPHY_WOWLAN_NET_DETECT;
                if (!iwlwifi_mod_params.sw_crypto)
                        mvm->wowlan.flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
                                             WIPHY_WOWLAN_GTK_REKEY_FAILURE |
@@@ -766,22 -771,37 +771,37 @@@ void iwl_mvm_fw_error_dump(struct iwl_m
        struct iwl_fw_error_dump_file *dump_file;
        struct iwl_fw_error_dump_data *dump_data;
        struct iwl_fw_error_dump_info *dump_info;
+       struct iwl_fw_error_dump_mem *dump_mem;
        struct iwl_mvm_dump_ptrs *fw_error_dump;
-       const struct fw_img *img;
        u32 sram_len, sram_ofs;
        u32 file_len, rxf_len;
        unsigned long flags;
        int reg_val;
+       u32 smem_len = mvm->cfg->smem_len;
  
        lockdep_assert_held(&mvm->mutex);
  
+       /* W/A for 8000 HW family A-step */
+       if (mvm->cfg->smem_len &&
+           mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
+           CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_A_STEP)
+               smem_len = 0x38000;
        fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL);
        if (!fw_error_dump)
                return;
  
-       img = &mvm->fw->img[mvm->cur_ucode];
-       sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
-       sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
+       /* SRAM - include stack CCM if driver knows the values for it */
+       if (!mvm->cfg->dccm_offset || !mvm->cfg->dccm_len) {
+               const struct fw_img *img;
+               img = &mvm->fw->img[mvm->cur_ucode];
+               sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
+               sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
+       } else {
+               sram_ofs = mvm->cfg->dccm_offset;
+               sram_len = mvm->cfg->dccm_len;
+       }
  
        /* reading buffer size */
        reg_val = iwl_trans_read_prph(mvm->trans, RXF_SIZE_ADDR);
  
        file_len = sizeof(*dump_file) +
                   sizeof(*dump_data) * 3 +
-                  sram_len +
+                  sram_len + sizeof(*dump_mem) +
                   rxf_len +
                   sizeof(*dump_info);
  
+       /* Make room for the SMEM, if it exists */
+       if (smem_len)
+               file_len += sizeof(*dump_data) + sizeof(*dump_mem) + smem_len;
        dump_file = vzalloc(file_len);
        if (!dump_file) {
                kfree(fw_error_dump);
                mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ?
                        cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) :
                        cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8);
+       dump_info->hw_step = cpu_to_le32(CSR_HW_REV_STEP(mvm->trans->hw_rev));
        memcpy(dump_info->fw_human_readable, mvm->fw->human_readable,
               sizeof(dump_info->fw_human_readable));
        strncpy(dump_info->dev_human_readable, mvm->cfg->name,
        }
  
        dump_data = iwl_fw_error_next_data(dump_data);
-       dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_SRAM);
-       dump_data->len = cpu_to_le32(sram_len);
-       iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_data->data,
+       dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
+       dump_data->len = cpu_to_le32(sram_len + sizeof(*dump_mem));
+       dump_mem = (void *)dump_data->data;
+       dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM);
+       dump_mem->offset = cpu_to_le32(sram_ofs);
+       iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_mem->data,
                                 sram_len);
  
+       if (smem_len) {
+               dump_data = iwl_fw_error_next_data(dump_data);
+               dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
+               dump_data->len = cpu_to_le32(smem_len + sizeof(*dump_mem));
+               dump_mem = (void *)dump_data->data;
+               dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SMEM);
+               dump_mem->offset = cpu_to_le32(mvm->cfg->smem_offset);
+               iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->smem_offset,
+                                        dump_mem->data, smem_len);
+       }
        fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans);
        fw_error_dump->op_mode_len = file_len;
        if (fw_error_dump->trans_ptr)
@@@ -864,6 -903,11 +903,11 @@@ static void iwl_mvm_restart_cleanup(str
        if (!test_and_clear_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status))
                iwl_mvm_fw_error_dump(mvm);
  
+       /* cleanup all stale references (scan, roc), but keep the
+        * ucode_down ref until reconfig is complete
+        */
+       iwl_mvm_unref_all_except(mvm, IWL_MVM_REF_UCODE_DOWN);
        iwl_trans_stop_device(mvm->trans);
  
        mvm->scan_status = IWL_MVM_SCAN_NONE;
  
        ieee80211_wake_queues(mvm->hw);
  
-       /* cleanup all stale references (scan, roc), but keep the
-        * ucode_down ref until reconfig is complete */
-       iwl_mvm_unref_all_except(mvm, IWL_MVM_REF_UCODE_DOWN);
        /* clear any stale d0i3 state */
        clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status);
  
@@@ -933,6 -973,19 +973,19 @@@ static int iwl_mvm_mac_start(struct iee
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        int ret;
  
+       /* Some hw restart cleanups must not hold the mutex */
+       if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
+               /*
+                * Make sure we are out of d0i3. This is needed
+                * to make sure the reference accounting is correct
+                * (and there is no stale d0i3_exit_work).
+                */
+               wait_event_timeout(mvm->d0i3_exit_waitq,
+                                  !test_bit(IWL_MVM_STATUS_IN_D0I3,
+                                            &mvm->status),
+                                  HZ);
+       }
        mutex_lock(&mvm->mutex);
        ret = __iwl_mvm_mac_start(mvm);
        mutex_unlock(&mvm->mutex);
@@@ -982,6 -1035,13 +1035,13 @@@ static void iwl_mvm_resume_complete(str
                IWL_DEBUG_RPM(mvm, "Run deferred d0i3 exit\n");
                _iwl_mvm_exit_d0i3(mvm);
        }
+       if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND)
+               if (!wait_event_timeout(mvm->d0i3_exit_waitq,
+                                       !test_bit(IWL_MVM_STATUS_IN_D0I3,
+                                                 &mvm->status),
+                                       HZ))
+                       WARN_ONCE(1, "D0i3 exit on resume timed out\n");
  }
  
  static void
@@@ -1004,13 -1064,8 +1064,13 @@@ void __iwl_mvm_mac_stop(struct iwl_mvm 
  {
        lockdep_assert_held(&mvm->mutex);
  
 -      /* disallow low power states when the FW is down */
 -      iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
 +      /*
 +       * Disallow low power states when the FW is down by taking
 +       * the UCODE_DOWN ref. in case of ongoing hw restart the
 +       * ref is already taken, so don't take it again.
 +       */
 +      if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
 +              iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
  
        /* async_handlers_wk is now blocked */
  
        /* the fw is stopped, the aux sta is dead: clean up driver state */
        iwl_mvm_del_aux_sta(mvm);
  
 +      /*
 +       * Clear IN_HW_RESTART flag when stopping the hw (as restart_complete()
 +       * won't be called in this case).
 +       */
 +      clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
 +
        mvm->ucode_loaded = false;
  }
  
@@@ -2088,7 -2137,7 +2148,7 @@@ static void iwl_mvm_sta_pre_rcu_remove(
                                       struct ieee80211_sta *sta)
  {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-       struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+       struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
  
        /*
         * This is called before mac80211 does RCU synchronisation,
@@@ -3103,7 -3152,7 +3163,7 @@@ static int iwl_mvm_set_tim(struct ieee8
                           bool set)
  {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-       struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+       struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
  
        if (!mvm_sta || !mvm_sta->vif) {
                IWL_ERR(mvm, "Station is not associated to a vif\n");
@@@ -443,10 -443,25 +443,25 @@@ static int iwl_pcie_apm_stop_master(str
        return ret;
  }
  
- static void iwl_pcie_apm_stop(struct iwl_trans *trans)
+ static void iwl_pcie_apm_stop(struct iwl_trans *trans, bool op_mode_leave)
  {
        IWL_DEBUG_INFO(trans, "Stop card, put in low power state\n");
  
+       if (op_mode_leave) {
+               if (!test_bit(STATUS_DEVICE_ENABLED, &trans->status))
+                       iwl_pcie_apm_init(trans);
+               /* inform ME that we are leaving */
+               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)
+                       iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
+                                   CSR_HW_IF_CONFIG_REG_PREPARE |
+                                   CSR_HW_IF_CONFIG_REG_ENABLE_PME);
+               mdelay(5);
+       }
        clear_bit(STATUS_DEVICE_ENABLED, &trans->status);
  
        /* Stop device's DMA activity */
@@@ -614,7 -629,7 +629,7 @@@ static int iwl_pcie_load_section(struc
  {
        u8 *v_addr;
        dma_addr_t p_addr;
 -      u32 offset, chunk_sz = section->len;
 +      u32 offset, chunk_sz = min_t(u32, FH_MEM_TB_MAX_LENGTH, section->len);
        int ret = 0;
  
        IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n",
@@@ -893,6 -908,9 +908,9 @@@ static int iwl_pcie_load_given_ucode_80
        if (ret)
                return ret;
  
+       if (trans->dbg_dest_tlv)
+               iwl_pcie_apply_destination(trans);
        /* Notify FW loading is done */
        iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, 0xFFFFFFFF);
  
  static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
                                   const struct fw_img *fw, bool run_in_rfkill)
  {
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        int ret;
        bool hw_rfkill;
  
                return ret;
        }
  
+       /* init ref_count to 1 (should be cleared when ucode is loaded) */
+       trans_pcie->ref_count = 1;
        /* make sure rfkill handshake bits are cleared */
        iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
        iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR,
@@@ -1010,23 -1032,18 +1032,23 @@@ static void iwl_trans_pcie_stop_device(
                      CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
  
        /* Stop the device, and put it in low power state */
-       iwl_pcie_apm_stop(trans);
+       iwl_pcie_apm_stop(trans, false);
  
 -      /* Upon stop, the APM issues an interrupt if HW RF kill is set.
 -       * Clean again the interrupt here
 +      /* stop and reset the on-board processor */
 +      iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
 +      udelay(20);
 +
 +      /*
 +       * Upon stop, the APM issues an interrupt if HW RF kill is set.
 +       * This is a bug in certain verions of the hardware.
 +       * Certain devices also keep sending HW RF kill interrupt all
 +       * the time, unless the interrupt is ACKed even if the interrupt
 +       * should be masked. Re-ACK all the interrupts here.
         */
        spin_lock(&trans_pcie->irq_lock);
        iwl_disable_interrupts(trans);
        spin_unlock(&trans_pcie->irq_lock);
  
 -      /* stop and reset the on-board processor */
 -      iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
 -      udelay(20);
  
        /* clear all status bits */
        clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
@@@ -1192,7 -1209,7 +1214,7 @@@ static void iwl_trans_pcie_op_mode_leav
        iwl_disable_interrupts(trans);
        spin_unlock(&trans_pcie->irq_lock);
  
-       iwl_pcie_apm_stop(trans);
+       iwl_pcie_apm_stop(trans, true);
  
        spin_lock(&trans_pcie->irq_lock);
        iwl_disable_interrupts(trans);
@@@ -1540,6 -1557,38 +1562,38 @@@ static void iwl_trans_pcie_set_bits_mas
        spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
  }
  
+ void iwl_trans_pcie_ref(struct iwl_trans *trans)
+ {
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       unsigned long flags;
+       if (iwlwifi_mod_params.d0i3_disable)
+               return;
+       spin_lock_irqsave(&trans_pcie->ref_lock, flags);
+       IWL_DEBUG_RPM(trans, "ref_counter: %d\n", trans_pcie->ref_count);
+       trans_pcie->ref_count++;
+       spin_unlock_irqrestore(&trans_pcie->ref_lock, flags);
+ }
+ void iwl_trans_pcie_unref(struct iwl_trans *trans)
+ {
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       unsigned long flags;
+       if (iwlwifi_mod_params.d0i3_disable)
+               return;
+       spin_lock_irqsave(&trans_pcie->ref_lock, flags);
+       IWL_DEBUG_RPM(trans, "ref_counter: %d\n", trans_pcie->ref_count);
+       if (WARN_ON_ONCE(trans_pcie->ref_count == 0)) {
+               spin_unlock_irqrestore(&trans_pcie->ref_lock, flags);
+               return;
+       }
+       trans_pcie->ref_count--;
+       spin_unlock_irqrestore(&trans_pcie->ref_lock, flags);
+ }
  static const char *get_csr_string(int cmd)
  {
  #define IWL_CMD(x) case x: return #x
@@@ -2264,6 -2313,9 +2318,9 @@@ static const struct iwl_trans_ops trans
        .release_nic_access = iwl_trans_pcie_release_nic_access,
        .set_bits_mask = iwl_trans_pcie_set_bits_mask,
  
+       .ref = iwl_trans_pcie_ref,
+       .unref = iwl_trans_pcie_unref,
        .dump_data = iwl_trans_pcie_dump_data,
  };
  
@@@ -2404,6 -2456,7 +2461,7 @@@ struct iwl_trans *iwl_trans_pcie_alloc(
        }
  
        trans_pcie->inta_mask = CSR_INI_SET_MASK;
+       trans->d0i3_mode = IWL_D0I3_MODE_ON_SUSPEND;
  
        return trans;