iwlwifi: pcie: allow the op_mode to block the tx queues
authorEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Tue, 24 Nov 2015 11:24:24 +0000 (13:24 +0200)
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Sun, 13 Dec 2015 06:52:52 +0000 (08:52 +0200)
In certain flows (see next patches), the op_mode may need to
block the Tx queues for a short period. Provide an API for
that. The transport is in charge of counting the number of
times the queues are blocked since the op_mode may block the
queues several times in a row before unblocking them.

Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
drivers/net/wireless/intel/iwlwifi/iwl-trans.h
drivers/net/wireless/intel/iwlwifi/pcie/internal.h
drivers/net/wireless/intel/iwlwifi/pcie/trans.c
drivers/net/wireless/intel/iwlwifi/pcie/tx.c

index 3d089ae171b70d6690c1e242bdca3e355692485e..0cd69cd98cd6e2d64a7d134789f309b66ec04b96 100644 (file)
@@ -542,6 +542,11 @@ struct iwl_trans_txq_scd_cfg {
  * @wait_tx_queue_empty: wait until tx queues are empty. May sleep.
  * @freeze_txq_timer: prevents the timer of the queue from firing until the
  *     queue is set to awake. Must be atomic.
+ * @block_txq_ptrs: stop updating the write pointers of the Tx queues. Note
+ *     that the transport needs to refcount the calls since this function
+ *     will be called several times with block = true, and then the queues
+ *     need to be unblocked only after the same number of calls with
+ *     block = false.
  * @write8: write a u8 to a register at offset ofs from the BAR
  * @write32: write a u32 to a register at offset ofs from the BAR
  * @read32: read a u32 register at offset ofs from the BAR
@@ -600,6 +605,7 @@ struct iwl_trans_ops {
        int (*wait_tx_queue_empty)(struct iwl_trans *trans, u32 txq_bm);
        void (*freeze_txq_timer)(struct iwl_trans *trans, unsigned long txqs,
                                 bool freeze);
+       void (*block_txq_ptrs)(struct iwl_trans *trans, bool block);
 
        void (*write8)(struct iwl_trans *trans, u32 ofs, u8 val);
        void (*write32)(struct iwl_trans *trans, u32 ofs, u32 val);
@@ -1010,6 +1016,16 @@ static inline void iwl_trans_freeze_txq_timer(struct iwl_trans *trans,
                trans->ops->freeze_txq_timer(trans, txqs, freeze);
 }
 
+static inline void iwl_trans_block_txq_ptrs(struct iwl_trans *trans,
+                                           bool block)
+{
+       if (unlikely(trans->state != IWL_TRANS_FW_ALIVE))
+               IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
+
+       if (trans->ops->block_txq_ptrs)
+               trans->ops->block_txq_ptrs(trans, block);
+}
+
 static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans,
                                                u32 txqs)
 {
index 44dc09d84d426a6307f96e6ca2c8ad801651cc03..31f30a2dd654d7195fd407634f95b59cb60b5149 100644 (file)
@@ -278,6 +278,7 @@ struct iwl_txq {
        bool frozen;
        u8 active;
        bool ampdu;
+       bool block;
        unsigned long wd_timeout;
 };
 
index efef4871478d82db1abd9d014bd80cbb391175e0..316162a4598991e2137e0cc65fb72bb03253f2fa 100644 (file)
@@ -1673,6 +1673,33 @@ next_queue:
        }
 }
 
+static void iwl_trans_pcie_block_txq_ptrs(struct iwl_trans *trans, bool block)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       int i;
+
+       for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) {
+               struct iwl_txq *txq = &trans_pcie->txq[i];
+
+               if (i == trans_pcie->cmd_queue)
+                       continue;
+
+               spin_lock_bh(&txq->lock);
+
+               if (!block && !(WARN_ON_ONCE(!txq->block))) {
+                       txq->block--;
+                       if (!txq->block) {
+                               iwl_write32(trans, HBUS_TARG_WRPTR,
+                                           txq->q.write_ptr | (i << 8));
+                       }
+               } else if (block) {
+                       txq->block++;
+               }
+
+               spin_unlock_bh(&txq->lock);
+       }
+}
+
 #define IWL_FLUSH_WAIT_MS      2000
 
 static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, u32 txq_bm)
@@ -2467,6 +2494,7 @@ static const struct iwl_trans_ops trans_ops_pcie = {
 
        .wait_tx_queue_empty = iwl_trans_pcie_wait_txq_empty,
        .freeze_txq_timer = iwl_trans_pcie_freeze_txq_timer,
+       .block_txq_ptrs = iwl_trans_pcie_block_txq_ptrs,
 
        .write8 = iwl_trans_pcie_write8,
        .write32 = iwl_trans_pcie_write32,
index a8c8a4a7420b53d02a798bcc261f408f77440ace..95ba920edb1d5d49d1ce99aff1af006443551675 100644 (file)
@@ -318,7 +318,9 @@ static void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans,
         * trying to tx (during RFKILL, we're not trying to tx).
         */
        IWL_DEBUG_TX(trans, "Q:%d WR: 0x%x\n", txq_id, txq->q.write_ptr);
-       iwl_write32(trans, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8));
+       if (!txq->block)
+               iwl_write32(trans, HBUS_TARG_WRPTR,
+                           txq->q.write_ptr | (txq_id << 8));
 }
 
 void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans)