iwlwifi: pcie: add basic reference accounting
authorEliad Peller <eliad@wizery.com>
Thu, 20 Nov 2014 15:33:43 +0000 (17:33 +0200)
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Sun, 28 Dec 2014 18:00:18 +0000 (20:00 +0200)
Implement the ref/unref trans ops and track both tx and
host command queues (and hold references while they
are not empty).

Signed-off-by: Eliad Peller <eliadx.peller@intel.com>
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
drivers/net/wireless/iwlwifi/iwl-drv.c
drivers/net/wireless/iwlwifi/iwl-modparams.h
drivers/net/wireless/iwlwifi/pcie/internal.h
drivers/net/wireless/iwlwifi/pcie/trans.c
drivers/net/wireless/iwlwifi/pcie/tx.c

index afa63f7..0381dc4 100644 (file)
@@ -1362,6 +1362,7 @@ struct iwl_mod_params iwlwifi_mod_params = {
        .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 */
@@ -1478,6 +1479,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
index 71507cf..2a8cf4b 100644 (file)
@@ -103,6 +103,7 @@ enum iwl_disable_11n {
  * @power_level: power level, default = 1
  * @debug_level: levels are IWL_DL_*
  * @ant_coupling: antenna coupling in dB, default = 0
+ * @d0i3_disable: disable d0i3, default = 1,
  * @fw_monitor: allow to use firmware monitor
  */
 struct iwl_mod_params {
@@ -121,6 +122,7 @@ struct iwl_mod_params {
        int ant_coupling;
        char *nvm_file;
        bool uapsd_disable;
+       bool d0i3_disable;
        bool fw_monitor;
 };
 
index 1aea6b6..e5652d8 100644 (file)
@@ -318,6 +318,11 @@ struct iwl_trans_pcie {
        /*protect hw register */
        spinlock_t reg_lock;
        bool cmd_in_flight;
+       bool ref_cmd_in_flight;
+
+       /* protect ref counter */
+       spinlock_t ref_lock;
+       u32 ref_count;
 
        dma_addr_t fw_mon_phys;
        struct page *fw_mon_page;
@@ -381,6 +386,9 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
                            struct sk_buff_head *skbs);
 void iwl_trans_pcie_tx_reset(struct iwl_trans *trans);
 
+void iwl_trans_pcie_ref(struct iwl_trans *trans);
+void iwl_trans_pcie_unref(struct iwl_trans *trans);
+
 static inline u16 iwl_pcie_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx)
 {
        struct iwl_tfd_tb *tb = &tfd->tbs[idx];
index 4b42de3..fbdbec8 100644 (file)
@@ -931,6 +931,7 @@ static int iwl_pcie_load_given_ucode_8000b(struct iwl_trans *trans,
 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;
 
@@ -960,6 +961,9 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
                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,
@@ -1550,6 +1554,38 @@ static void iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, u32 reg,
        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
@@ -2274,6 +2310,9 @@ static const struct iwl_trans_ops trans_ops_pcie = {
        .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,
 };
 
index 8a6c7a0..c1c4c75 100644 (file)
@@ -985,17 +985,31 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
 
        if (iwl_queue_space(&txq->q) > txq->q.low_mark)
                iwl_wake_queue(trans, txq);
+
+       if (q->read_ptr == q->write_ptr) {
+               IWL_DEBUG_RPM(trans, "Q %d - last tx reclaimed\n", q->id);
+               iwl_trans_pcie_unref(trans);
+       }
+
 out:
        spin_unlock_bh(&txq->lock);
 }
 
-static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans)
+static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans,
+                                     const struct iwl_host_cmd *cmd)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        int ret;
 
        lockdep_assert_held(&trans_pcie->reg_lock);
 
+       if (!(cmd->flags & CMD_SEND_IN_IDLE) &&
+           !trans_pcie->ref_cmd_in_flight) {
+               trans_pcie->ref_cmd_in_flight = true;
+               IWL_DEBUG_RPM(trans, "set ref_cmd_in_flight - ref\n");
+               iwl_trans_pcie_ref(trans);
+       }
+
        if (trans_pcie->cmd_in_flight)
                return 0;
 
@@ -1036,6 +1050,12 @@ static int iwl_pcie_clear_cmd_in_flight(struct iwl_trans *trans)
 
        lockdep_assert_held(&trans_pcie->reg_lock);
 
+       if (trans_pcie->ref_cmd_in_flight) {
+               trans_pcie->ref_cmd_in_flight = false;
+               IWL_DEBUG_RPM(trans, "clear ref_cmd_in_flight - unref\n");
+               iwl_trans_pcie_unref(trans);
+       }
+
        if (WARN_ON(!trans_pcie->cmd_in_flight))
                return 0;
 
@@ -1473,7 +1493,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
                mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout);
 
        spin_lock_irqsave(&trans_pcie->reg_lock, flags);
-       ret = iwl_pcie_set_cmd_in_flight(trans);
+       ret = iwl_pcie_set_cmd_in_flight(trans, cmd);
        if (ret < 0) {
                idx = ret;
                spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
@@ -1819,9 +1839,13 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
        wait_write_ptr = ieee80211_has_morefrags(fc);
 
        /* start timer if queue currently empty */
-       if (txq->need_update && q->read_ptr == q->write_ptr &&
-           trans_pcie->wd_timeout)
-               mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout);
+       if (q->read_ptr == q->write_ptr) {
+               if (txq->need_update && trans_pcie->wd_timeout)
+                       mod_timer(&txq->stuck_timer,
+                                 jiffies + trans_pcie->wd_timeout);
+               IWL_DEBUG_RPM(trans, "Q: %d first tx - take ref\n", q->id);
+               iwl_trans_pcie_ref(trans);
+       }
 
        /* Tell device the write index *just past* this latest filled TFD */
        q->write_ptr = iwl_queue_inc_wrap(q->write_ptr);