diff options
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/pcie')
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 6 | ||||
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 6 | ||||
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c | 162 | ||||
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 8 |
4 files changed, 171 insertions, 11 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index 98c1308eab0d..5e191579b9a9 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -758,10 +758,16 @@ void iwl_pcie_apm_config(struct iwl_trans *trans); int iwl_pcie_prepare_card_hw(struct iwl_trans *trans); void iwl_pcie_synchronize_irqs(struct iwl_trans *trans); bool iwl_trans_check_hw_rf_kill(struct iwl_trans *trans); +void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq); /* transport gen 2 exported functions */ int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans, const struct fw_img *fw, bool run_in_rfkill); void iwl_trans_pcie_gen2_fw_alive(struct iwl_trans *trans, u32 scd_addr); +int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans, + struct iwl_tx_queue_cfg_cmd *cmd, + int cmd_id, + unsigned int timeout); +void iwl_trans_pcie_dyn_txq_free(struct iwl_trans *trans, int queue); #endif /* __iwl_trans_int_pcie_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 361f0b0a6f42..9cd9c1f5a3dc 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -2923,10 +2923,8 @@ static const struct iwl_trans_ops trans_ops_pcie_gen2 = { .tx = iwl_trans_pcie_tx, .reclaim = iwl_trans_pcie_reclaim, - .txq_disable = iwl_trans_pcie_txq_disable, - .txq_enable = iwl_trans_pcie_txq_enable, - - .txq_set_shared_mode = iwl_trans_pcie_txq_set_shared_mode, + .txq_alloc = iwl_trans_pcie_dyn_txq_alloc, + .txq_free = iwl_trans_pcie_dyn_txq_free, .freeze_txq_timer = iwl_trans_pcie_freeze_txq_timer, .block_txq_ptrs = iwl_trans_pcie_block_txq_ptrs, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c new file mode 100644 index 000000000000..7c93f266871e --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c @@ -0,0 +1,162 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2017 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * BSD LICENSE + * + * Copyright(c) 2017 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#include "iwl-debug.h" +#include "iwl-csr.h" +#include "iwl-io.h" +#include "internal.h" + +/* + * iwl_pcie_gen2_txq_unmap - Unmap any remaining DMA mappings and free skb's + */ +void iwl_pcie_gen2_txq_unmap(struct iwl_trans *trans, int txq_id) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_txq *txq = &trans_pcie->txq[txq_id]; + + spin_lock_bh(&txq->lock); + while (txq->write_ptr != txq->read_ptr) { + IWL_DEBUG_TX_REPLY(trans, "Q %d Free %d\n", + txq_id, txq->read_ptr); + + iwl_pcie_txq_free_tfd(trans, txq); + txq->read_ptr = iwl_queue_inc_wrap(txq->read_ptr); + + if (txq->read_ptr == txq->write_ptr) { + unsigned long flags; + + spin_lock_irqsave(&trans_pcie->reg_lock, flags); + if (txq_id != trans_pcie->cmd_queue) { + IWL_DEBUG_RPM(trans, "Q %d - last tx freed\n", + txq->id); + iwl_trans_unref(trans); + } else if (trans_pcie->ref_cmd_in_flight) { + trans_pcie->ref_cmd_in_flight = false; + IWL_DEBUG_RPM(trans, + "clear ref_cmd_in_flight\n"); + iwl_trans_unref(trans); + } + spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); + } + } + spin_unlock_bh(&txq->lock); + + /* just in case - this queue may have been stopped */ + iwl_wake_queue(trans, txq); +} + +int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans, + struct iwl_tx_queue_cfg_cmd *cmd, + int cmd_id, + unsigned int timeout) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_txq *txq = &trans_pcie->txq[cmd->scd_queue]; + struct iwl_host_cmd hcmd = { + .id = cmd_id, + .len = { sizeof(*cmd) }, + .data = { cmd, }, + .flags = 0, + }; + u16 ssn = le16_to_cpu(cmd->ssn); + + if (test_and_set_bit(cmd->scd_queue, trans_pcie->queue_used)) { + WARN_ONCE(1, "queue %d already used", cmd->scd_queue); + return -EINVAL; + } + + txq->wd_timeout = msecs_to_jiffies(timeout); + + /* + * Place first TFD at index corresponding to start sequence number. + * Assumes that ssn_idx is valid (!= 0xFFF) + */ + txq->read_ptr = (ssn & 0xff); + txq->write_ptr = (ssn & 0xff); + iwl_write_direct32(trans, HBUS_TARG_WRPTR, + (ssn & 0xff) | (cmd->scd_queue << 8)); + + IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d WrPtr: %d\n", + cmd->scd_queue, ssn & 0xff); + + cmd->tfdq_addr = cpu_to_le64(txq->dma_addr); + cmd->byte_cnt_addr = cpu_to_le64(trans_pcie->scd_bc_tbls.dma + + cmd->scd_queue * + sizeof(struct iwlagn_scd_bc_tbl)); + cmd->cb_size = cpu_to_le64(TFD_QUEUE_CB_SIZE(TFD_QUEUE_SIZE_MAX)); + + return iwl_trans_send_cmd(trans, &hcmd); +} + +void iwl_trans_pcie_dyn_txq_free(struct iwl_trans *trans, int queue) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + trans_pcie->txq[queue].frozen_expiry_remainder = 0; + trans_pcie->txq[queue].frozen = false; + + /* + * Upon HW Rfkill - we stop the device, and then stop the queues + * in the op_mode. Just for the sake of the simplicity of the op_mode, + * allow the op_mode to call txq_disable after it already called + * stop_device. + */ + if (!test_and_clear_bit(queue, trans_pcie->queue_used)) { + WARN_ONCE(test_bit(STATUS_DEVICE_ENABLED, &trans->status), + "queue %d not used", queue); + return; + } + + iwl_pcie_gen2_txq_unmap(trans, queue); + + IWL_DEBUG_TX_QUEUES(trans, "Deactivate queue %d\n", queue); +} + diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index b3ac1fb51009..9c903270645c 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -456,7 +456,7 @@ static void iwl_pcie_tfd_unmap(struct iwl_trans *trans, * Does NOT advance any TFD circular buffer read/write indexes * Does NOT free the TFD itself (which is within circular buffer) */ -static void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq) +void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq) { /* rd_ptr is bounded by TFD_QUEUE_SIZE_MAX and * idx is bounded by n_window @@ -1359,9 +1359,6 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, u16 ssn, if (test_and_set_bit(txq_id, trans_pcie->queue_used)) WARN_ONCE(1, "queue %d already used - expect issues", txq_id); - if (cfg && trans->cfg->use_tfh) - WARN_ONCE(1, "Expected no calls to SCD configuration"); - txq->wd_timeout = msecs_to_jiffies(wdg_timeout); if (cfg) { @@ -1477,9 +1474,6 @@ void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id, return; } - if (configure_scd && trans->cfg->use_tfh) - WARN_ONCE(1, "Expected no calls to SCD configuration"); - if (configure_scd) { iwl_scd_txq_set_inactive(trans, txq_id); |