Commit 0cd58eaa authored by Emmanuel Grumbach's avatar Emmanuel Grumbach

iwlwifi: pcie: allow the op_mode to block the tx queues

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: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
parent 3f50a690
...@@ -542,6 +542,11 @@ struct iwl_trans_txq_scd_cfg { ...@@ -542,6 +542,11 @@ struct iwl_trans_txq_scd_cfg {
* @wait_tx_queue_empty: wait until tx queues are empty. May sleep. * @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 * @freeze_txq_timer: prevents the timer of the queue from firing until the
* queue is set to awake. Must be atomic. * 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 * @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 * @write32: write a u32 to a register at offset ofs from the BAR
* @read32: read a u32 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 { ...@@ -600,6 +605,7 @@ struct iwl_trans_ops {
int (*wait_tx_queue_empty)(struct iwl_trans *trans, u32 txq_bm); int (*wait_tx_queue_empty)(struct iwl_trans *trans, u32 txq_bm);
void (*freeze_txq_timer)(struct iwl_trans *trans, unsigned long txqs, void (*freeze_txq_timer)(struct iwl_trans *trans, unsigned long txqs,
bool freeze); bool freeze);
void (*block_txq_ptrs)(struct iwl_trans *trans, bool block);
void (*write8)(struct iwl_trans *trans, u32 ofs, u8 val); void (*write8)(struct iwl_trans *trans, u32 ofs, u8 val);
void (*write32)(struct iwl_trans *trans, u32 ofs, u32 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, ...@@ -1010,6 +1016,16 @@ static inline void iwl_trans_freeze_txq_timer(struct iwl_trans *trans,
trans->ops->freeze_txq_timer(trans, txqs, freeze); 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, static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans,
u32 txqs) u32 txqs)
{ {
......
...@@ -278,6 +278,7 @@ struct iwl_txq { ...@@ -278,6 +278,7 @@ struct iwl_txq {
bool frozen; bool frozen;
u8 active; u8 active;
bool ampdu; bool ampdu;
bool block;
unsigned long wd_timeout; unsigned long wd_timeout;
}; };
......
...@@ -1673,6 +1673,33 @@ static void iwl_trans_pcie_freeze_txq_timer(struct iwl_trans *trans, ...@@ -1673,6 +1673,33 @@ static void iwl_trans_pcie_freeze_txq_timer(struct iwl_trans *trans,
} }
} }
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 #define IWL_FLUSH_WAIT_MS 2000
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, u32 txq_bm)
...@@ -2467,6 +2494,7 @@ static const struct iwl_trans_ops trans_ops_pcie = { ...@@ -2467,6 +2494,7 @@ static const struct iwl_trans_ops trans_ops_pcie = {
.wait_tx_queue_empty = iwl_trans_pcie_wait_txq_empty, .wait_tx_queue_empty = iwl_trans_pcie_wait_txq_empty,
.freeze_txq_timer = iwl_trans_pcie_freeze_txq_timer, .freeze_txq_timer = iwl_trans_pcie_freeze_txq_timer,
.block_txq_ptrs = iwl_trans_pcie_block_txq_ptrs,
.write8 = iwl_trans_pcie_write8, .write8 = iwl_trans_pcie_write8,
.write32 = iwl_trans_pcie_write32, .write32 = iwl_trans_pcie_write32,
......
...@@ -318,7 +318,9 @@ static void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans, ...@@ -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). * 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_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) void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment