18c2ecf20Sopenharmony_ci/****************************************************************************** 28c2ecf20Sopenharmony_ci * 38c2ecf20Sopenharmony_ci * This file is provided under a dual BSD/GPLv2 license. When using or 48c2ecf20Sopenharmony_ci * redistributing this file, you may do so under either license. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * GPL LICENSE SUMMARY 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright(c) 2017 Intel Deutschland GmbH 98c2ecf20Sopenharmony_ci * Copyright(c) 2018 - 2020 Intel Corporation 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 128c2ecf20Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as 138c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but 168c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 178c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 188c2ecf20Sopenharmony_ci * General Public License for more details. 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * BSD LICENSE 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * Copyright(c) 2017 Intel Deutschland GmbH 238c2ecf20Sopenharmony_ci * Copyright(c) 2018 - 2020 Intel Corporation 248c2ecf20Sopenharmony_ci * All rights reserved. 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 278c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 288c2ecf20Sopenharmony_ci * are met: 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * * Redistributions of source code must retain the above copyright 318c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 328c2ecf20Sopenharmony_ci * * Redistributions in binary form must reproduce the above copyright 338c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer in 348c2ecf20Sopenharmony_ci * the documentation and/or other materials provided with the 358c2ecf20Sopenharmony_ci * distribution. 368c2ecf20Sopenharmony_ci * * Neither the name Intel Corporation nor the names of its 378c2ecf20Sopenharmony_ci * contributors may be used to endorse or promote products derived 388c2ecf20Sopenharmony_ci * from this software without specific prior written permission. 398c2ecf20Sopenharmony_ci * 408c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 418c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 428c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 438c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 448c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 458c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 468c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 478c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 488c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 498c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 508c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 518c2ecf20Sopenharmony_ci * 528c2ecf20Sopenharmony_ci *****************************************************************************/ 538c2ecf20Sopenharmony_ci#include <net/tso.h> 548c2ecf20Sopenharmony_ci#include <linux/tcp.h> 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#include "iwl-debug.h" 578c2ecf20Sopenharmony_ci#include "iwl-csr.h" 588c2ecf20Sopenharmony_ci#include "iwl-io.h" 598c2ecf20Sopenharmony_ci#include "internal.h" 608c2ecf20Sopenharmony_ci#include "fw/api/tx.h" 618c2ecf20Sopenharmony_ci#include "queue/tx.h" 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/*************** HOST COMMAND QUEUE FUNCTIONS *****/ 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* 668c2ecf20Sopenharmony_ci * iwl_pcie_gen2_enqueue_hcmd - enqueue a uCode command 678c2ecf20Sopenharmony_ci * @priv: device private data point 688c2ecf20Sopenharmony_ci * @cmd: a pointer to the ucode command structure 698c2ecf20Sopenharmony_ci * 708c2ecf20Sopenharmony_ci * The function returns < 0 values to indicate the operation 718c2ecf20Sopenharmony_ci * failed. On success, it returns the index (>= 0) of command in the 728c2ecf20Sopenharmony_ci * command queue. 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_cistatic int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans, 758c2ecf20Sopenharmony_ci struct iwl_host_cmd *cmd) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 788c2ecf20Sopenharmony_ci struct iwl_txq *txq = trans->txqs.txq[trans->txqs.cmd.q_id]; 798c2ecf20Sopenharmony_ci struct iwl_device_cmd *out_cmd; 808c2ecf20Sopenharmony_ci struct iwl_cmd_meta *out_meta; 818c2ecf20Sopenharmony_ci void *dup_buf = NULL; 828c2ecf20Sopenharmony_ci dma_addr_t phys_addr; 838c2ecf20Sopenharmony_ci int i, cmd_pos, idx; 848c2ecf20Sopenharmony_ci u16 copy_size, cmd_size, tb0_size; 858c2ecf20Sopenharmony_ci bool had_nocopy = false; 868c2ecf20Sopenharmony_ci u8 group_id = iwl_cmd_groupid(cmd->id); 878c2ecf20Sopenharmony_ci const u8 *cmddata[IWL_MAX_CMD_TBS_PER_TFD]; 888c2ecf20Sopenharmony_ci u16 cmdlen[IWL_MAX_CMD_TBS_PER_TFD]; 898c2ecf20Sopenharmony_ci struct iwl_tfh_tfd *tfd; 908c2ecf20Sopenharmony_ci unsigned long flags; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci copy_size = sizeof(struct iwl_cmd_header_wide); 938c2ecf20Sopenharmony_ci cmd_size = sizeof(struct iwl_cmd_header_wide); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { 968c2ecf20Sopenharmony_ci cmddata[i] = cmd->data[i]; 978c2ecf20Sopenharmony_ci cmdlen[i] = cmd->len[i]; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (!cmd->len[i]) 1008c2ecf20Sopenharmony_ci continue; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* need at least IWL_FIRST_TB_SIZE copied */ 1038c2ecf20Sopenharmony_ci if (copy_size < IWL_FIRST_TB_SIZE) { 1048c2ecf20Sopenharmony_ci int copy = IWL_FIRST_TB_SIZE - copy_size; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (copy > cmdlen[i]) 1078c2ecf20Sopenharmony_ci copy = cmdlen[i]; 1088c2ecf20Sopenharmony_ci cmdlen[i] -= copy; 1098c2ecf20Sopenharmony_ci cmddata[i] += copy; 1108c2ecf20Sopenharmony_ci copy_size += copy; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) { 1148c2ecf20Sopenharmony_ci had_nocopy = true; 1158c2ecf20Sopenharmony_ci if (WARN_ON(cmd->dataflags[i] & IWL_HCMD_DFL_DUP)) { 1168c2ecf20Sopenharmony_ci idx = -EINVAL; 1178c2ecf20Sopenharmony_ci goto free_dup_buf; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci } else if (cmd->dataflags[i] & IWL_HCMD_DFL_DUP) { 1208c2ecf20Sopenharmony_ci /* 1218c2ecf20Sopenharmony_ci * This is also a chunk that isn't copied 1228c2ecf20Sopenharmony_ci * to the static buffer so set had_nocopy. 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_ci had_nocopy = true; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* only allowed once */ 1278c2ecf20Sopenharmony_ci if (WARN_ON(dup_buf)) { 1288c2ecf20Sopenharmony_ci idx = -EINVAL; 1298c2ecf20Sopenharmony_ci goto free_dup_buf; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci dup_buf = kmemdup(cmddata[i], cmdlen[i], 1338c2ecf20Sopenharmony_ci GFP_ATOMIC); 1348c2ecf20Sopenharmony_ci if (!dup_buf) 1358c2ecf20Sopenharmony_ci return -ENOMEM; 1368c2ecf20Sopenharmony_ci } else { 1378c2ecf20Sopenharmony_ci /* NOCOPY must not be followed by normal! */ 1388c2ecf20Sopenharmony_ci if (WARN_ON(had_nocopy)) { 1398c2ecf20Sopenharmony_ci idx = -EINVAL; 1408c2ecf20Sopenharmony_ci goto free_dup_buf; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci copy_size += cmdlen[i]; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci cmd_size += cmd->len[i]; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* 1488c2ecf20Sopenharmony_ci * If any of the command structures end up being larger than the 1498c2ecf20Sopenharmony_ci * TFD_MAX_PAYLOAD_SIZE and they aren't dynamically allocated into 1508c2ecf20Sopenharmony_ci * separate TFDs, then we will need to increase the size of the buffers 1518c2ecf20Sopenharmony_ci */ 1528c2ecf20Sopenharmony_ci if (WARN(copy_size > TFD_MAX_PAYLOAD_SIZE, 1538c2ecf20Sopenharmony_ci "Command %s (%#x) is too large (%d bytes)\n", 1548c2ecf20Sopenharmony_ci iwl_get_cmd_string(trans, cmd->id), cmd->id, copy_size)) { 1558c2ecf20Sopenharmony_ci idx = -EINVAL; 1568c2ecf20Sopenharmony_ci goto free_dup_buf; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci spin_lock_irqsave(&txq->lock, flags); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci idx = iwl_txq_get_cmd_index(txq, txq->write_ptr); 1628c2ecf20Sopenharmony_ci tfd = iwl_txq_get_tfd(trans, txq, txq->write_ptr); 1638c2ecf20Sopenharmony_ci memset(tfd, 0, sizeof(*tfd)); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (iwl_txq_space(trans, txq) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { 1668c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&txq->lock, flags); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci IWL_ERR(trans, "No space in command queue\n"); 1698c2ecf20Sopenharmony_ci iwl_op_mode_cmd_queue_full(trans->op_mode); 1708c2ecf20Sopenharmony_ci idx = -ENOSPC; 1718c2ecf20Sopenharmony_ci goto free_dup_buf; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci out_cmd = txq->entries[idx].cmd; 1758c2ecf20Sopenharmony_ci out_meta = &txq->entries[idx].meta; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci /* re-initialize to NULL */ 1788c2ecf20Sopenharmony_ci memset(out_meta, 0, sizeof(*out_meta)); 1798c2ecf20Sopenharmony_ci if (cmd->flags & CMD_WANT_SKB) 1808c2ecf20Sopenharmony_ci out_meta->source = cmd; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* set up the header */ 1838c2ecf20Sopenharmony_ci out_cmd->hdr_wide.cmd = iwl_cmd_opcode(cmd->id); 1848c2ecf20Sopenharmony_ci out_cmd->hdr_wide.group_id = group_id; 1858c2ecf20Sopenharmony_ci out_cmd->hdr_wide.version = iwl_cmd_version(cmd->id); 1868c2ecf20Sopenharmony_ci out_cmd->hdr_wide.length = 1878c2ecf20Sopenharmony_ci cpu_to_le16(cmd_size - sizeof(struct iwl_cmd_header_wide)); 1888c2ecf20Sopenharmony_ci out_cmd->hdr_wide.reserved = 0; 1898c2ecf20Sopenharmony_ci out_cmd->hdr_wide.sequence = 1908c2ecf20Sopenharmony_ci cpu_to_le16(QUEUE_TO_SEQ(trans->txqs.cmd.q_id) | 1918c2ecf20Sopenharmony_ci INDEX_TO_SEQ(txq->write_ptr)); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci cmd_pos = sizeof(struct iwl_cmd_header_wide); 1948c2ecf20Sopenharmony_ci copy_size = sizeof(struct iwl_cmd_header_wide); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* and copy the data that needs to be copied */ 1978c2ecf20Sopenharmony_ci for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { 1988c2ecf20Sopenharmony_ci int copy; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (!cmd->len[i]) 2018c2ecf20Sopenharmony_ci continue; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* copy everything if not nocopy/dup */ 2048c2ecf20Sopenharmony_ci if (!(cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY | 2058c2ecf20Sopenharmony_ci IWL_HCMD_DFL_DUP))) { 2068c2ecf20Sopenharmony_ci copy = cmd->len[i]; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy); 2098c2ecf20Sopenharmony_ci cmd_pos += copy; 2108c2ecf20Sopenharmony_ci copy_size += copy; 2118c2ecf20Sopenharmony_ci continue; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* 2158c2ecf20Sopenharmony_ci * Otherwise we need at least IWL_FIRST_TB_SIZE copied 2168c2ecf20Sopenharmony_ci * in total (for bi-directional DMA), but copy up to what 2178c2ecf20Sopenharmony_ci * we can fit into the payload for debug dump purposes. 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_ci copy = min_t(int, TFD_MAX_PAYLOAD_SIZE - cmd_pos, cmd->len[i]); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy); 2228c2ecf20Sopenharmony_ci cmd_pos += copy; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* However, treat copy_size the proper way, we need it below */ 2258c2ecf20Sopenharmony_ci if (copy_size < IWL_FIRST_TB_SIZE) { 2268c2ecf20Sopenharmony_ci copy = IWL_FIRST_TB_SIZE - copy_size; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (copy > cmd->len[i]) 2298c2ecf20Sopenharmony_ci copy = cmd->len[i]; 2308c2ecf20Sopenharmony_ci copy_size += copy; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci IWL_DEBUG_HC(trans, 2358c2ecf20Sopenharmony_ci "Sending command %s (%.2x.%.2x), seq: 0x%04X, %d bytes at %d[%d]:%d\n", 2368c2ecf20Sopenharmony_ci iwl_get_cmd_string(trans, cmd->id), group_id, 2378c2ecf20Sopenharmony_ci out_cmd->hdr.cmd, le16_to_cpu(out_cmd->hdr.sequence), 2388c2ecf20Sopenharmony_ci cmd_size, txq->write_ptr, idx, trans->txqs.cmd.q_id); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* start the TFD with the minimum copy bytes */ 2418c2ecf20Sopenharmony_ci tb0_size = min_t(int, copy_size, IWL_FIRST_TB_SIZE); 2428c2ecf20Sopenharmony_ci memcpy(&txq->first_tb_bufs[idx], out_cmd, tb0_size); 2438c2ecf20Sopenharmony_ci iwl_txq_gen2_set_tb(trans, tfd, iwl_txq_get_first_tb_dma(txq, idx), 2448c2ecf20Sopenharmony_ci tb0_size); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* map first command fragment, if any remains */ 2478c2ecf20Sopenharmony_ci if (copy_size > tb0_size) { 2488c2ecf20Sopenharmony_ci phys_addr = dma_map_single(trans->dev, 2498c2ecf20Sopenharmony_ci (u8 *)out_cmd + tb0_size, 2508c2ecf20Sopenharmony_ci copy_size - tb0_size, 2518c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 2528c2ecf20Sopenharmony_ci if (dma_mapping_error(trans->dev, phys_addr)) { 2538c2ecf20Sopenharmony_ci idx = -ENOMEM; 2548c2ecf20Sopenharmony_ci iwl_txq_gen2_tfd_unmap(trans, out_meta, tfd); 2558c2ecf20Sopenharmony_ci goto out; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci iwl_txq_gen2_set_tb(trans, tfd, phys_addr, 2588c2ecf20Sopenharmony_ci copy_size - tb0_size); 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* map the remaining (adjusted) nocopy/dup fragments */ 2628c2ecf20Sopenharmony_ci for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { 2638c2ecf20Sopenharmony_ci const void *data = cmddata[i]; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (!cmdlen[i]) 2668c2ecf20Sopenharmony_ci continue; 2678c2ecf20Sopenharmony_ci if (!(cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY | 2688c2ecf20Sopenharmony_ci IWL_HCMD_DFL_DUP))) 2698c2ecf20Sopenharmony_ci continue; 2708c2ecf20Sopenharmony_ci if (cmd->dataflags[i] & IWL_HCMD_DFL_DUP) 2718c2ecf20Sopenharmony_ci data = dup_buf; 2728c2ecf20Sopenharmony_ci phys_addr = dma_map_single(trans->dev, (void *)data, 2738c2ecf20Sopenharmony_ci cmdlen[i], DMA_TO_DEVICE); 2748c2ecf20Sopenharmony_ci if (dma_mapping_error(trans->dev, phys_addr)) { 2758c2ecf20Sopenharmony_ci idx = -ENOMEM; 2768c2ecf20Sopenharmony_ci iwl_txq_gen2_tfd_unmap(trans, out_meta, tfd); 2778c2ecf20Sopenharmony_ci goto out; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci iwl_txq_gen2_set_tb(trans, tfd, phys_addr, cmdlen[i]); 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci BUILD_BUG_ON(IWL_TFH_NUM_TBS > sizeof(out_meta->tbs) * BITS_PER_BYTE); 2838c2ecf20Sopenharmony_ci out_meta->flags = cmd->flags; 2848c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(txq->entries[idx].free_buf)) 2858c2ecf20Sopenharmony_ci kfree_sensitive(txq->entries[idx].free_buf); 2868c2ecf20Sopenharmony_ci txq->entries[idx].free_buf = dup_buf; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci trace_iwlwifi_dev_hcmd(trans->dev, cmd, cmd_size, &out_cmd->hdr_wide); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci /* start timer if queue currently empty */ 2918c2ecf20Sopenharmony_ci if (txq->read_ptr == txq->write_ptr && txq->wd_timeout) 2928c2ecf20Sopenharmony_ci mod_timer(&txq->stuck_timer, jiffies + txq->wd_timeout); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci spin_lock(&trans_pcie->reg_lock); 2958c2ecf20Sopenharmony_ci /* Increment and update queue's write index */ 2968c2ecf20Sopenharmony_ci txq->write_ptr = iwl_txq_inc_wrap(trans, txq->write_ptr); 2978c2ecf20Sopenharmony_ci iwl_txq_inc_wr_ptr(trans, txq); 2988c2ecf20Sopenharmony_ci spin_unlock(&trans_pcie->reg_lock); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ciout: 3018c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&txq->lock, flags); 3028c2ecf20Sopenharmony_cifree_dup_buf: 3038c2ecf20Sopenharmony_ci if (idx < 0) 3048c2ecf20Sopenharmony_ci kfree(dup_buf); 3058c2ecf20Sopenharmony_ci return idx; 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci#define HOST_COMPLETE_TIMEOUT (2 * HZ) 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic int iwl_pcie_gen2_send_hcmd_sync(struct iwl_trans *trans, 3118c2ecf20Sopenharmony_ci struct iwl_host_cmd *cmd) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 3148c2ecf20Sopenharmony_ci const char *cmd_str = iwl_get_cmd_string(trans, cmd->id); 3158c2ecf20Sopenharmony_ci struct iwl_txq *txq = trans->txqs.txq[trans->txqs.cmd.q_id]; 3168c2ecf20Sopenharmony_ci int cmd_idx; 3178c2ecf20Sopenharmony_ci int ret; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n", cmd_str); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci if (WARN(test_and_set_bit(STATUS_SYNC_HCMD_ACTIVE, 3228c2ecf20Sopenharmony_ci &trans->status), 3238c2ecf20Sopenharmony_ci "Command %s: a command is already active!\n", cmd_str)) 3248c2ecf20Sopenharmony_ci return -EIO; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(trans, "Setting HCMD_ACTIVE for command %s\n", cmd_str); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci cmd_idx = iwl_pcie_gen2_enqueue_hcmd(trans, cmd); 3298c2ecf20Sopenharmony_ci if (cmd_idx < 0) { 3308c2ecf20Sopenharmony_ci ret = cmd_idx; 3318c2ecf20Sopenharmony_ci clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); 3328c2ecf20Sopenharmony_ci IWL_ERR(trans, "Error sending %s: enqueue_hcmd failed: %d\n", 3338c2ecf20Sopenharmony_ci cmd_str, ret); 3348c2ecf20Sopenharmony_ci return ret; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci ret = wait_event_timeout(trans_pcie->wait_command_queue, 3388c2ecf20Sopenharmony_ci !test_bit(STATUS_SYNC_HCMD_ACTIVE, 3398c2ecf20Sopenharmony_ci &trans->status), 3408c2ecf20Sopenharmony_ci HOST_COMPLETE_TIMEOUT); 3418c2ecf20Sopenharmony_ci if (!ret) { 3428c2ecf20Sopenharmony_ci IWL_ERR(trans, "Error sending %s: time out after %dms.\n", 3438c2ecf20Sopenharmony_ci cmd_str, jiffies_to_msecs(HOST_COMPLETE_TIMEOUT)); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci IWL_ERR(trans, "Current CMD queue read_ptr %d write_ptr %d\n", 3468c2ecf20Sopenharmony_ci txq->read_ptr, txq->write_ptr); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); 3498c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n", 3508c2ecf20Sopenharmony_ci cmd_str); 3518c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci iwl_trans_pcie_sync_nmi(trans); 3548c2ecf20Sopenharmony_ci goto cancel; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (test_bit(STATUS_FW_ERROR, &trans->status)) { 3588c2ecf20Sopenharmony_ci IWL_ERR(trans, "FW error in SYNC CMD %s\n", cmd_str); 3598c2ecf20Sopenharmony_ci dump_stack(); 3608c2ecf20Sopenharmony_ci ret = -EIO; 3618c2ecf20Sopenharmony_ci goto cancel; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci if (!(cmd->flags & CMD_SEND_IN_RFKILL) && 3658c2ecf20Sopenharmony_ci test_bit(STATUS_RFKILL_OPMODE, &trans->status)) { 3668c2ecf20Sopenharmony_ci IWL_DEBUG_RF_KILL(trans, "RFKILL in SYNC CMD... no rsp\n"); 3678c2ecf20Sopenharmony_ci ret = -ERFKILL; 3688c2ecf20Sopenharmony_ci goto cancel; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if ((cmd->flags & CMD_WANT_SKB) && !cmd->resp_pkt) { 3728c2ecf20Sopenharmony_ci IWL_ERR(trans, "Error: Response NULL in '%s'\n", cmd_str); 3738c2ecf20Sopenharmony_ci ret = -EIO; 3748c2ecf20Sopenharmony_ci goto cancel; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci return 0; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cicancel: 3808c2ecf20Sopenharmony_ci if (cmd->flags & CMD_WANT_SKB) { 3818c2ecf20Sopenharmony_ci /* 3828c2ecf20Sopenharmony_ci * Cancel the CMD_WANT_SKB flag for the cmd in the 3838c2ecf20Sopenharmony_ci * TX cmd queue. Otherwise in case the cmd comes 3848c2ecf20Sopenharmony_ci * in later, it will possibly set an invalid 3858c2ecf20Sopenharmony_ci * address (cmd->meta.source). 3868c2ecf20Sopenharmony_ci */ 3878c2ecf20Sopenharmony_ci txq->entries[cmd_idx].meta.flags &= ~CMD_WANT_SKB; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (cmd->resp_pkt) { 3918c2ecf20Sopenharmony_ci iwl_free_resp(cmd); 3928c2ecf20Sopenharmony_ci cmd->resp_pkt = NULL; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci return ret; 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ciint iwl_trans_pcie_gen2_send_hcmd(struct iwl_trans *trans, 3998c2ecf20Sopenharmony_ci struct iwl_host_cmd *cmd) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci if (!(cmd->flags & CMD_SEND_IN_RFKILL) && 4028c2ecf20Sopenharmony_ci test_bit(STATUS_RFKILL_OPMODE, &trans->status)) { 4038c2ecf20Sopenharmony_ci IWL_DEBUG_RF_KILL(trans, "Dropping CMD 0x%x: RF KILL\n", 4048c2ecf20Sopenharmony_ci cmd->id); 4058c2ecf20Sopenharmony_ci return -ERFKILL; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (cmd->flags & CMD_ASYNC) { 4098c2ecf20Sopenharmony_ci int ret; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci /* An asynchronous command can not expect an SKB to be set. */ 4128c2ecf20Sopenharmony_ci if (WARN_ON(cmd->flags & CMD_WANT_SKB)) 4138c2ecf20Sopenharmony_ci return -EINVAL; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci ret = iwl_pcie_gen2_enqueue_hcmd(trans, cmd); 4168c2ecf20Sopenharmony_ci if (ret < 0) { 4178c2ecf20Sopenharmony_ci IWL_ERR(trans, 4188c2ecf20Sopenharmony_ci "Error sending %s: enqueue_hcmd failed: %d\n", 4198c2ecf20Sopenharmony_ci iwl_get_cmd_string(trans, cmd->id), ret); 4208c2ecf20Sopenharmony_ci return ret; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci return 0; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci return iwl_pcie_gen2_send_hcmd_sync(trans, cmd); 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 428