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) 2013 - 2015 Intel Mobile Communications GmbH 98c2ecf20Sopenharmony_ci * Copyright(c) 2016 - 2017 Intel Deutschland GmbH 108c2ecf20Sopenharmony_ci * Copyright(c) 2007 - 2015, 2018 - 2020 Intel Corporation 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 138c2ecf20Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as 148c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but 178c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 188c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 198c2ecf20Sopenharmony_ci * General Public License for more details. 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * The full GNU General Public License is included in this distribution 228c2ecf20Sopenharmony_ci * in the file called COPYING. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * Contact Information: 258c2ecf20Sopenharmony_ci * Intel Linux Wireless <linuxwifi@intel.com> 268c2ecf20Sopenharmony_ci * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * BSD LICENSE 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH 318c2ecf20Sopenharmony_ci * Copyright(c) 2016 - 2017 Intel Deutschland GmbH 328c2ecf20Sopenharmony_ci * Copyright(c) 2007 - 2015, 2018 - 2020 Intel Corporation 338c2ecf20Sopenharmony_ci * All rights reserved. 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 368c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 378c2ecf20Sopenharmony_ci * are met: 388c2ecf20Sopenharmony_ci * 398c2ecf20Sopenharmony_ci * * Redistributions of source code must retain the above copyright 408c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 418c2ecf20Sopenharmony_ci * * Redistributions in binary form must reproduce the above copyright 428c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer in 438c2ecf20Sopenharmony_ci * the documentation and/or other materials provided with the 448c2ecf20Sopenharmony_ci * distribution. 458c2ecf20Sopenharmony_ci * * Neither the name Intel Corporation nor the names of its 468c2ecf20Sopenharmony_ci * contributors may be used to endorse or promote products derived 478c2ecf20Sopenharmony_ci * from this software without specific prior written permission. 488c2ecf20Sopenharmony_ci * 498c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 508c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 518c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 528c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 538c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 548c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 558c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 568c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 578c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 588c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 598c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 608c2ecf20Sopenharmony_ci * 618c2ecf20Sopenharmony_ci *****************************************************************************/ 628c2ecf20Sopenharmony_ci#include <linux/pci.h> 638c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 648c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 658c2ecf20Sopenharmony_ci#include <linux/sched.h> 668c2ecf20Sopenharmony_ci#include <linux/bitops.h> 678c2ecf20Sopenharmony_ci#include <linux/gfp.h> 688c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 698c2ecf20Sopenharmony_ci#include <linux/module.h> 708c2ecf20Sopenharmony_ci#include <linux/wait.h> 718c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#include "iwl-drv.h" 748c2ecf20Sopenharmony_ci#include "iwl-trans.h" 758c2ecf20Sopenharmony_ci#include "iwl-csr.h" 768c2ecf20Sopenharmony_ci#include "iwl-prph.h" 778c2ecf20Sopenharmony_ci#include "iwl-scd.h" 788c2ecf20Sopenharmony_ci#include "iwl-agn-hw.h" 798c2ecf20Sopenharmony_ci#include "fw/error-dump.h" 808c2ecf20Sopenharmony_ci#include "fw/dbg.h" 818c2ecf20Sopenharmony_ci#include "fw/api/tx.h" 828c2ecf20Sopenharmony_ci#include "internal.h" 838c2ecf20Sopenharmony_ci#include "iwl-fh.h" 848c2ecf20Sopenharmony_ci#include "iwl-context-info-gen3.h" 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* extended range in FW SRAM */ 878c2ecf20Sopenharmony_ci#define IWL_FW_MEM_EXTENDED_START 0x40000 888c2ecf20Sopenharmony_ci#define IWL_FW_MEM_EXTENDED_END 0x57FFF 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_civoid iwl_trans_pcie_dump_regs(struct iwl_trans *trans) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci#define PCI_DUMP_SIZE 352 938c2ecf20Sopenharmony_ci#define PCI_MEM_DUMP_SIZE 64 948c2ecf20Sopenharmony_ci#define PCI_PARENT_DUMP_SIZE 524 958c2ecf20Sopenharmony_ci#define PREFIX_LEN 32 968c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 978c2ecf20Sopenharmony_ci struct pci_dev *pdev = trans_pcie->pci_dev; 988c2ecf20Sopenharmony_ci u32 i, pos, alloc_size, *ptr, *buf; 998c2ecf20Sopenharmony_ci char *prefix; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (trans_pcie->pcie_dbg_dumped_once) 1028c2ecf20Sopenharmony_ci return; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci /* Should be a multiple of 4 */ 1058c2ecf20Sopenharmony_ci BUILD_BUG_ON(PCI_DUMP_SIZE > 4096 || PCI_DUMP_SIZE & 0x3); 1068c2ecf20Sopenharmony_ci BUILD_BUG_ON(PCI_MEM_DUMP_SIZE > 4096 || PCI_MEM_DUMP_SIZE & 0x3); 1078c2ecf20Sopenharmony_ci BUILD_BUG_ON(PCI_PARENT_DUMP_SIZE > 4096 || PCI_PARENT_DUMP_SIZE & 0x3); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* Alloc a max size buffer */ 1108c2ecf20Sopenharmony_ci alloc_size = PCI_ERR_ROOT_ERR_SRC + 4 + PREFIX_LEN; 1118c2ecf20Sopenharmony_ci alloc_size = max_t(u32, alloc_size, PCI_DUMP_SIZE + PREFIX_LEN); 1128c2ecf20Sopenharmony_ci alloc_size = max_t(u32, alloc_size, PCI_MEM_DUMP_SIZE + PREFIX_LEN); 1138c2ecf20Sopenharmony_ci alloc_size = max_t(u32, alloc_size, PCI_PARENT_DUMP_SIZE + PREFIX_LEN); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci buf = kmalloc(alloc_size, GFP_ATOMIC); 1168c2ecf20Sopenharmony_ci if (!buf) 1178c2ecf20Sopenharmony_ci return; 1188c2ecf20Sopenharmony_ci prefix = (char *)buf + alloc_size - PREFIX_LEN; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci IWL_ERR(trans, "iwlwifi transaction failed, dumping registers\n"); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* Print wifi device registers */ 1238c2ecf20Sopenharmony_ci sprintf(prefix, "iwlwifi %s: ", pci_name(pdev)); 1248c2ecf20Sopenharmony_ci IWL_ERR(trans, "iwlwifi device config registers:\n"); 1258c2ecf20Sopenharmony_ci for (i = 0, ptr = buf; i < PCI_DUMP_SIZE; i += 4, ptr++) 1268c2ecf20Sopenharmony_ci if (pci_read_config_dword(pdev, i, ptr)) 1278c2ecf20Sopenharmony_ci goto err_read; 1288c2ecf20Sopenharmony_ci print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 4, buf, i, 0); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci IWL_ERR(trans, "iwlwifi device memory mapped registers:\n"); 1318c2ecf20Sopenharmony_ci for (i = 0, ptr = buf; i < PCI_MEM_DUMP_SIZE; i += 4, ptr++) 1328c2ecf20Sopenharmony_ci *ptr = iwl_read32(trans, i); 1338c2ecf20Sopenharmony_ci print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 4, buf, i, 0); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR); 1368c2ecf20Sopenharmony_ci if (pos) { 1378c2ecf20Sopenharmony_ci IWL_ERR(trans, "iwlwifi device AER capability structure:\n"); 1388c2ecf20Sopenharmony_ci for (i = 0, ptr = buf; i < PCI_ERR_ROOT_COMMAND; i += 4, ptr++) 1398c2ecf20Sopenharmony_ci if (pci_read_config_dword(pdev, pos + i, ptr)) 1408c2ecf20Sopenharmony_ci goto err_read; 1418c2ecf20Sopenharmony_ci print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 1428c2ecf20Sopenharmony_ci 32, 4, buf, i, 0); 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* Print parent device registers next */ 1468c2ecf20Sopenharmony_ci if (!pdev->bus->self) 1478c2ecf20Sopenharmony_ci goto out; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci pdev = pdev->bus->self; 1508c2ecf20Sopenharmony_ci sprintf(prefix, "iwlwifi %s: ", pci_name(pdev)); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci IWL_ERR(trans, "iwlwifi parent port (%s) config registers:\n", 1538c2ecf20Sopenharmony_ci pci_name(pdev)); 1548c2ecf20Sopenharmony_ci for (i = 0, ptr = buf; i < PCI_PARENT_DUMP_SIZE; i += 4, ptr++) 1558c2ecf20Sopenharmony_ci if (pci_read_config_dword(pdev, i, ptr)) 1568c2ecf20Sopenharmony_ci goto err_read; 1578c2ecf20Sopenharmony_ci print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 4, buf, i, 0); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* Print root port AER registers */ 1608c2ecf20Sopenharmony_ci pos = 0; 1618c2ecf20Sopenharmony_ci pdev = pcie_find_root_port(pdev); 1628c2ecf20Sopenharmony_ci if (pdev) 1638c2ecf20Sopenharmony_ci pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR); 1648c2ecf20Sopenharmony_ci if (pos) { 1658c2ecf20Sopenharmony_ci IWL_ERR(trans, "iwlwifi root port (%s) AER cap structure:\n", 1668c2ecf20Sopenharmony_ci pci_name(pdev)); 1678c2ecf20Sopenharmony_ci sprintf(prefix, "iwlwifi %s: ", pci_name(pdev)); 1688c2ecf20Sopenharmony_ci for (i = 0, ptr = buf; i <= PCI_ERR_ROOT_ERR_SRC; i += 4, ptr++) 1698c2ecf20Sopenharmony_ci if (pci_read_config_dword(pdev, pos + i, ptr)) 1708c2ecf20Sopenharmony_ci goto err_read; 1718c2ecf20Sopenharmony_ci print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 1728c2ecf20Sopenharmony_ci 4, buf, i, 0); 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci goto out; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cierr_read: 1778c2ecf20Sopenharmony_ci print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 4, buf, i, 0); 1788c2ecf20Sopenharmony_ci IWL_ERR(trans, "Read failed at 0x%X\n", i); 1798c2ecf20Sopenharmony_ciout: 1808c2ecf20Sopenharmony_ci trans_pcie->pcie_dbg_dumped_once = 1; 1818c2ecf20Sopenharmony_ci kfree(buf); 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic void iwl_trans_pcie_sw_reset(struct iwl_trans *trans) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci /* Reset entire device - do controller reset (results in SHRD_HW_RST) */ 1878c2ecf20Sopenharmony_ci iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); 1888c2ecf20Sopenharmony_ci usleep_range(5000, 6000); 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic void iwl_pcie_free_fw_monitor(struct iwl_trans *trans) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct iwl_dram_data *fw_mon = &trans->dbg.fw_mon; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (!fw_mon->size) 1968c2ecf20Sopenharmony_ci return; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci dma_free_coherent(trans->dev, fw_mon->size, fw_mon->block, 1998c2ecf20Sopenharmony_ci fw_mon->physical); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci fw_mon->block = NULL; 2028c2ecf20Sopenharmony_ci fw_mon->physical = 0; 2038c2ecf20Sopenharmony_ci fw_mon->size = 0; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic void iwl_pcie_alloc_fw_monitor_block(struct iwl_trans *trans, 2078c2ecf20Sopenharmony_ci u8 max_power, u8 min_power) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct iwl_dram_data *fw_mon = &trans->dbg.fw_mon; 2108c2ecf20Sopenharmony_ci void *block = NULL; 2118c2ecf20Sopenharmony_ci dma_addr_t physical = 0; 2128c2ecf20Sopenharmony_ci u32 size = 0; 2138c2ecf20Sopenharmony_ci u8 power; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (fw_mon->size) 2168c2ecf20Sopenharmony_ci return; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci for (power = max_power; power >= min_power; power--) { 2198c2ecf20Sopenharmony_ci size = BIT(power); 2208c2ecf20Sopenharmony_ci block = dma_alloc_coherent(trans->dev, size, &physical, 2218c2ecf20Sopenharmony_ci GFP_KERNEL | __GFP_NOWARN); 2228c2ecf20Sopenharmony_ci if (!block) 2238c2ecf20Sopenharmony_ci continue; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci IWL_INFO(trans, 2268c2ecf20Sopenharmony_ci "Allocated 0x%08x bytes for firmware monitor.\n", 2278c2ecf20Sopenharmony_ci size); 2288c2ecf20Sopenharmony_ci break; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!block)) 2328c2ecf20Sopenharmony_ci return; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (power != max_power) 2358c2ecf20Sopenharmony_ci IWL_ERR(trans, 2368c2ecf20Sopenharmony_ci "Sorry - debug buffer is only %luK while you requested %luK\n", 2378c2ecf20Sopenharmony_ci (unsigned long)BIT(power - 10), 2388c2ecf20Sopenharmony_ci (unsigned long)BIT(max_power - 10)); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci fw_mon->block = block; 2418c2ecf20Sopenharmony_ci fw_mon->physical = physical; 2428c2ecf20Sopenharmony_ci fw_mon->size = size; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_civoid iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans, u8 max_power) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci if (!max_power) { 2488c2ecf20Sopenharmony_ci /* default max_power is maximum */ 2498c2ecf20Sopenharmony_ci max_power = 26; 2508c2ecf20Sopenharmony_ci } else { 2518c2ecf20Sopenharmony_ci max_power += 11; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (WARN(max_power > 26, 2558c2ecf20Sopenharmony_ci "External buffer size for monitor is too big %d, check the FW TLV\n", 2568c2ecf20Sopenharmony_ci max_power)) 2578c2ecf20Sopenharmony_ci return; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (trans->dbg.fw_mon.size) 2608c2ecf20Sopenharmony_ci return; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci iwl_pcie_alloc_fw_monitor_block(trans, max_power, 11); 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic u32 iwl_trans_pcie_read_shr(struct iwl_trans *trans, u32 reg) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_CTRL_REG, 2688c2ecf20Sopenharmony_ci ((reg & 0x0000ffff) | (2 << 28))); 2698c2ecf20Sopenharmony_ci return iwl_read32(trans, HEEP_CTRL_WRD_PCIEX_DATA_REG); 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic void iwl_trans_pcie_write_shr(struct iwl_trans *trans, u32 reg, u32 val) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_DATA_REG, val); 2758c2ecf20Sopenharmony_ci iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_CTRL_REG, 2768c2ecf20Sopenharmony_ci ((reg & 0x0000ffff) | (3 << 28))); 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic void iwl_pcie_set_pwr(struct iwl_trans *trans, bool vaux) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci if (trans->cfg->apmg_not_supported) 2828c2ecf20Sopenharmony_ci return; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (vaux && pci_pme_capable(to_pci_dev(trans->dev), PCI_D3cold)) 2858c2ecf20Sopenharmony_ci iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG, 2868c2ecf20Sopenharmony_ci APMG_PS_CTRL_VAL_PWR_SRC_VAUX, 2878c2ecf20Sopenharmony_ci ~APMG_PS_CTRL_MSK_PWR_SRC); 2888c2ecf20Sopenharmony_ci else 2898c2ecf20Sopenharmony_ci iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG, 2908c2ecf20Sopenharmony_ci APMG_PS_CTRL_VAL_PWR_SRC_VMAIN, 2918c2ecf20Sopenharmony_ci ~APMG_PS_CTRL_MSK_PWR_SRC); 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci/* PCI registers */ 2958c2ecf20Sopenharmony_ci#define PCI_CFG_RETRY_TIMEOUT 0x041 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_civoid iwl_pcie_apm_config(struct iwl_trans *trans) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 3008c2ecf20Sopenharmony_ci u16 lctl; 3018c2ecf20Sopenharmony_ci u16 cap; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* 3048c2ecf20Sopenharmony_ci * L0S states have been found to be unstable with our devices 3058c2ecf20Sopenharmony_ci * and in newer hardware they are not officially supported at 3068c2ecf20Sopenharmony_ci * all, so we must always set the L0S_DISABLED bit. 3078c2ecf20Sopenharmony_ci */ 3088c2ecf20Sopenharmony_ci iwl_set_bit(trans, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_DISABLED); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci pcie_capability_read_word(trans_pcie->pci_dev, PCI_EXP_LNKCTL, &lctl); 3118c2ecf20Sopenharmony_ci trans->pm_support = !(lctl & PCI_EXP_LNKCTL_ASPM_L0S); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci pcie_capability_read_word(trans_pcie->pci_dev, PCI_EXP_DEVCTL2, &cap); 3148c2ecf20Sopenharmony_ci trans->ltr_enabled = cap & PCI_EXP_DEVCTL2_LTR_EN; 3158c2ecf20Sopenharmony_ci IWL_DEBUG_POWER(trans, "L1 %sabled - LTR %sabled\n", 3168c2ecf20Sopenharmony_ci (lctl & PCI_EXP_LNKCTL_ASPM_L1) ? "En" : "Dis", 3178c2ecf20Sopenharmony_ci trans->ltr_enabled ? "En" : "Dis"); 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci/* 3218c2ecf20Sopenharmony_ci * Start up NIC's basic functionality after it has been reset 3228c2ecf20Sopenharmony_ci * (e.g. after platform boot, or shutdown via iwl_pcie_apm_stop()) 3238c2ecf20Sopenharmony_ci * NOTE: This does not load uCode nor start the embedded processor 3248c2ecf20Sopenharmony_ci */ 3258c2ecf20Sopenharmony_cistatic int iwl_pcie_apm_init(struct iwl_trans *trans) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci int ret; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(trans, "Init card's basic functions\n"); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci /* 3328c2ecf20Sopenharmony_ci * Use "set_bit" below rather than "write", to preserve any hardware 3338c2ecf20Sopenharmony_ci * bits already set by default after reset. 3348c2ecf20Sopenharmony_ci */ 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* Disable L0S exit timer (platform NMI Work/Around) */ 3378c2ecf20Sopenharmony_ci if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_8000) 3388c2ecf20Sopenharmony_ci iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS, 3398c2ecf20Sopenharmony_ci CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci /* 3428c2ecf20Sopenharmony_ci * Disable L0s without affecting L1; 3438c2ecf20Sopenharmony_ci * don't wait for ICH L0s (ICH bug W/A) 3448c2ecf20Sopenharmony_ci */ 3458c2ecf20Sopenharmony_ci iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS, 3468c2ecf20Sopenharmony_ci CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci /* Set FH wait threshold to maximum (HW error during stress W/A) */ 3498c2ecf20Sopenharmony_ci iwl_set_bit(trans, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci /* 3528c2ecf20Sopenharmony_ci * Enable HAP INTA (interrupt from management bus) to 3538c2ecf20Sopenharmony_ci * wake device's PCI Express link L1a -> L0s 3548c2ecf20Sopenharmony_ci */ 3558c2ecf20Sopenharmony_ci iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, 3568c2ecf20Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci iwl_pcie_apm_config(trans); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci /* Configure analog phase-lock-loop before activating to D0A */ 3618c2ecf20Sopenharmony_ci if (trans->trans_cfg->base_params->pll_cfg) 3628c2ecf20Sopenharmony_ci iwl_set_bit(trans, CSR_ANA_PLL_CFG, CSR50_ANA_PLL_CFG_VAL); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci ret = iwl_finish_nic_init(trans, trans->trans_cfg); 3658c2ecf20Sopenharmony_ci if (ret) 3668c2ecf20Sopenharmony_ci return ret; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (trans->cfg->host_interrupt_operation_mode) { 3698c2ecf20Sopenharmony_ci /* 3708c2ecf20Sopenharmony_ci * This is a bit of an abuse - This is needed for 7260 / 3160 3718c2ecf20Sopenharmony_ci * only check host_interrupt_operation_mode even if this is 3728c2ecf20Sopenharmony_ci * not related to host_interrupt_operation_mode. 3738c2ecf20Sopenharmony_ci * 3748c2ecf20Sopenharmony_ci * Enable the oscillator to count wake up time for L1 exit. This 3758c2ecf20Sopenharmony_ci * consumes slightly more power (100uA) - but allows to be sure 3768c2ecf20Sopenharmony_ci * that we wake up from L1 on time. 3778c2ecf20Sopenharmony_ci * 3788c2ecf20Sopenharmony_ci * This looks weird: read twice the same register, discard the 3798c2ecf20Sopenharmony_ci * value, set a bit, and yet again, read that same register 3808c2ecf20Sopenharmony_ci * just to discard the value. But that's the way the hardware 3818c2ecf20Sopenharmony_ci * seems to like it. 3828c2ecf20Sopenharmony_ci */ 3838c2ecf20Sopenharmony_ci iwl_read_prph(trans, OSC_CLK); 3848c2ecf20Sopenharmony_ci iwl_read_prph(trans, OSC_CLK); 3858c2ecf20Sopenharmony_ci iwl_set_bits_prph(trans, OSC_CLK, OSC_CLK_FORCE_CONTROL); 3868c2ecf20Sopenharmony_ci iwl_read_prph(trans, OSC_CLK); 3878c2ecf20Sopenharmony_ci iwl_read_prph(trans, OSC_CLK); 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci /* 3918c2ecf20Sopenharmony_ci * Enable DMA clock and wait for it to stabilize. 3928c2ecf20Sopenharmony_ci * 3938c2ecf20Sopenharmony_ci * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0" 3948c2ecf20Sopenharmony_ci * bits do not disable clocks. This preserves any hardware 3958c2ecf20Sopenharmony_ci * bits already set by default in "CLK_CTRL_REG" after reset. 3968c2ecf20Sopenharmony_ci */ 3978c2ecf20Sopenharmony_ci if (!trans->cfg->apmg_not_supported) { 3988c2ecf20Sopenharmony_ci iwl_write_prph(trans, APMG_CLK_EN_REG, 3998c2ecf20Sopenharmony_ci APMG_CLK_VAL_DMA_CLK_RQT); 4008c2ecf20Sopenharmony_ci udelay(20); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci /* Disable L1-Active */ 4038c2ecf20Sopenharmony_ci iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG, 4048c2ecf20Sopenharmony_ci APMG_PCIDEV_STT_VAL_L1_ACT_DIS); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci /* Clear the interrupt in APMG if the NIC is in RFKILL */ 4078c2ecf20Sopenharmony_ci iwl_write_prph(trans, APMG_RTC_INT_STT_REG, 4088c2ecf20Sopenharmony_ci APMG_RTC_INT_STT_RFKILL); 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci set_bit(STATUS_DEVICE_ENABLED, &trans->status); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci return 0; 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci/* 4178c2ecf20Sopenharmony_ci * Enable LP XTAL to avoid HW bug where device may consume much power if 4188c2ecf20Sopenharmony_ci * FW is not loaded after device reset. LP XTAL is disabled by default 4198c2ecf20Sopenharmony_ci * after device HW reset. Do it only if XTAL is fed by internal source. 4208c2ecf20Sopenharmony_ci * Configure device's "persistence" mode to avoid resetting XTAL again when 4218c2ecf20Sopenharmony_ci * SHRD_HW_RST occurs in S3. 4228c2ecf20Sopenharmony_ci */ 4238c2ecf20Sopenharmony_cistatic void iwl_pcie_apm_lp_xtal_enable(struct iwl_trans *trans) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci int ret; 4268c2ecf20Sopenharmony_ci u32 apmg_gp1_reg; 4278c2ecf20Sopenharmony_ci u32 apmg_xtal_cfg_reg; 4288c2ecf20Sopenharmony_ci u32 dl_cfg_reg; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci /* Force XTAL ON */ 4318c2ecf20Sopenharmony_ci __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL, 4328c2ecf20Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_XTAL_ON); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci iwl_trans_pcie_sw_reset(trans); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci ret = iwl_finish_nic_init(trans, trans->trans_cfg); 4378c2ecf20Sopenharmony_ci if (WARN_ON(ret)) { 4388c2ecf20Sopenharmony_ci /* Release XTAL ON request */ 4398c2ecf20Sopenharmony_ci __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, 4408c2ecf20Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_XTAL_ON); 4418c2ecf20Sopenharmony_ci return; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci /* 4458c2ecf20Sopenharmony_ci * Clear "disable persistence" to avoid LP XTAL resetting when 4468c2ecf20Sopenharmony_ci * SHRD_HW_RST is applied in S3. 4478c2ecf20Sopenharmony_ci */ 4488c2ecf20Sopenharmony_ci iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG, 4498c2ecf20Sopenharmony_ci APMG_PCIDEV_STT_VAL_PERSIST_DIS); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci /* 4528c2ecf20Sopenharmony_ci * Force APMG XTAL to be active to prevent its disabling by HW 4538c2ecf20Sopenharmony_ci * caused by APMG idle state. 4548c2ecf20Sopenharmony_ci */ 4558c2ecf20Sopenharmony_ci apmg_xtal_cfg_reg = iwl_trans_pcie_read_shr(trans, 4568c2ecf20Sopenharmony_ci SHR_APMG_XTAL_CFG_REG); 4578c2ecf20Sopenharmony_ci iwl_trans_pcie_write_shr(trans, SHR_APMG_XTAL_CFG_REG, 4588c2ecf20Sopenharmony_ci apmg_xtal_cfg_reg | 4598c2ecf20Sopenharmony_ci SHR_APMG_XTAL_CFG_XTAL_ON_REQ); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci iwl_trans_pcie_sw_reset(trans); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* Enable LP XTAL by indirect access through CSR */ 4648c2ecf20Sopenharmony_ci apmg_gp1_reg = iwl_trans_pcie_read_shr(trans, SHR_APMG_GP1_REG); 4658c2ecf20Sopenharmony_ci iwl_trans_pcie_write_shr(trans, SHR_APMG_GP1_REG, apmg_gp1_reg | 4668c2ecf20Sopenharmony_ci SHR_APMG_GP1_WF_XTAL_LP_EN | 4678c2ecf20Sopenharmony_ci SHR_APMG_GP1_CHICKEN_BIT_SELECT); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci /* Clear delay line clock power up */ 4708c2ecf20Sopenharmony_ci dl_cfg_reg = iwl_trans_pcie_read_shr(trans, SHR_APMG_DL_CFG_REG); 4718c2ecf20Sopenharmony_ci iwl_trans_pcie_write_shr(trans, SHR_APMG_DL_CFG_REG, dl_cfg_reg & 4728c2ecf20Sopenharmony_ci ~SHR_APMG_DL_CFG_DL_CLOCK_POWER_UP); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci /* 4758c2ecf20Sopenharmony_ci * Enable persistence mode to avoid LP XTAL resetting when 4768c2ecf20Sopenharmony_ci * SHRD_HW_RST is applied in S3. 4778c2ecf20Sopenharmony_ci */ 4788c2ecf20Sopenharmony_ci iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, 4798c2ecf20Sopenharmony_ci CSR_HW_IF_CONFIG_REG_PERSIST_MODE); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci /* 4828c2ecf20Sopenharmony_ci * Clear "initialization complete" bit to move adapter from 4838c2ecf20Sopenharmony_ci * D0A* (powered-up Active) --> D0U* (Uninitialized) state. 4848c2ecf20Sopenharmony_ci */ 4858c2ecf20Sopenharmony_ci iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* Activates XTAL resources monitor */ 4888c2ecf20Sopenharmony_ci __iwl_trans_pcie_set_bit(trans, CSR_MONITOR_CFG_REG, 4898c2ecf20Sopenharmony_ci CSR_MONITOR_XTAL_RESOURCES); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci /* Release XTAL ON request */ 4928c2ecf20Sopenharmony_ci __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, 4938c2ecf20Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_XTAL_ON); 4948c2ecf20Sopenharmony_ci udelay(10); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci /* Release APMG XTAL */ 4978c2ecf20Sopenharmony_ci iwl_trans_pcie_write_shr(trans, SHR_APMG_XTAL_CFG_REG, 4988c2ecf20Sopenharmony_ci apmg_xtal_cfg_reg & 4998c2ecf20Sopenharmony_ci ~SHR_APMG_XTAL_CFG_XTAL_ON_REQ); 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_civoid iwl_pcie_apm_stop_master(struct iwl_trans *trans) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci int ret; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci /* stop device's busmaster DMA activity */ 5078c2ecf20Sopenharmony_ci iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci ret = iwl_poll_bit(trans, CSR_RESET, 5108c2ecf20Sopenharmony_ci CSR_RESET_REG_FLAG_MASTER_DISABLED, 5118c2ecf20Sopenharmony_ci CSR_RESET_REG_FLAG_MASTER_DISABLED, 100); 5128c2ecf20Sopenharmony_ci if (ret < 0) 5138c2ecf20Sopenharmony_ci IWL_WARN(trans, "Master Disable Timed Out, 100 usec\n"); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(trans, "stop master\n"); 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_cistatic void iwl_pcie_apm_stop(struct iwl_trans *trans, bool op_mode_leave) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(trans, "Stop card, put in low power state\n"); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci if (op_mode_leave) { 5238c2ecf20Sopenharmony_ci if (!test_bit(STATUS_DEVICE_ENABLED, &trans->status)) 5248c2ecf20Sopenharmony_ci iwl_pcie_apm_init(trans); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci /* inform ME that we are leaving */ 5278c2ecf20Sopenharmony_ci if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_7000) 5288c2ecf20Sopenharmony_ci iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG, 5298c2ecf20Sopenharmony_ci APMG_PCIDEV_STT_VAL_WAKE_ME); 5308c2ecf20Sopenharmony_ci else if (trans->trans_cfg->device_family >= 5318c2ecf20Sopenharmony_ci IWL_DEVICE_FAMILY_8000) { 5328c2ecf20Sopenharmony_ci iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, 5338c2ecf20Sopenharmony_ci CSR_RESET_LINK_PWR_MGMT_DISABLED); 5348c2ecf20Sopenharmony_ci iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, 5358c2ecf20Sopenharmony_ci CSR_HW_IF_CONFIG_REG_PREPARE | 5368c2ecf20Sopenharmony_ci CSR_HW_IF_CONFIG_REG_ENABLE_PME); 5378c2ecf20Sopenharmony_ci mdelay(1); 5388c2ecf20Sopenharmony_ci iwl_clear_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, 5398c2ecf20Sopenharmony_ci CSR_RESET_LINK_PWR_MGMT_DISABLED); 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci mdelay(5); 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci clear_bit(STATUS_DEVICE_ENABLED, &trans->status); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci /* Stop device's DMA activity */ 5478c2ecf20Sopenharmony_ci iwl_pcie_apm_stop_master(trans); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (trans->cfg->lp_xtal_workaround) { 5508c2ecf20Sopenharmony_ci iwl_pcie_apm_lp_xtal_enable(trans); 5518c2ecf20Sopenharmony_ci return; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci iwl_trans_pcie_sw_reset(trans); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci /* 5578c2ecf20Sopenharmony_ci * Clear "initialization complete" bit to move adapter from 5588c2ecf20Sopenharmony_ci * D0A* (powered-up Active) --> D0U* (Uninitialized) state. 5598c2ecf20Sopenharmony_ci */ 5608c2ecf20Sopenharmony_ci iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic int iwl_pcie_nic_init(struct iwl_trans *trans) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 5668c2ecf20Sopenharmony_ci int ret; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci /* nic_init */ 5698c2ecf20Sopenharmony_ci spin_lock(&trans_pcie->irq_lock); 5708c2ecf20Sopenharmony_ci ret = iwl_pcie_apm_init(trans); 5718c2ecf20Sopenharmony_ci spin_unlock(&trans_pcie->irq_lock); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci if (ret) 5748c2ecf20Sopenharmony_ci return ret; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci iwl_pcie_set_pwr(trans, false); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci iwl_op_mode_nic_config(trans->op_mode); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci /* Allocate the RX queue, or reset if it is already allocated */ 5818c2ecf20Sopenharmony_ci iwl_pcie_rx_init(trans); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci /* Allocate or reset and init all Tx and Command queues */ 5848c2ecf20Sopenharmony_ci if (iwl_pcie_tx_init(trans)) 5858c2ecf20Sopenharmony_ci return -ENOMEM; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci if (trans->trans_cfg->base_params->shadow_reg_enable) { 5888c2ecf20Sopenharmony_ci /* enable shadow regs in HW */ 5898c2ecf20Sopenharmony_ci iwl_set_bit(trans, CSR_MAC_SHADOW_REG_CTRL, 0x800FFFFF); 5908c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(trans, "Enabling shadow registers in device\n"); 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci return 0; 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci#define HW_READY_TIMEOUT (50) 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci/* Note: returns poll_bit return value, which is >= 0 if success */ 5998c2ecf20Sopenharmony_cistatic int iwl_pcie_set_hw_ready(struct iwl_trans *trans) 6008c2ecf20Sopenharmony_ci{ 6018c2ecf20Sopenharmony_ci int ret; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, 6048c2ecf20Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_NIC_READY); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci /* See if we got it */ 6078c2ecf20Sopenharmony_ci ret = iwl_poll_bit(trans, CSR_HW_IF_CONFIG_REG, 6088c2ecf20Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, 6098c2ecf20Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, 6108c2ecf20Sopenharmony_ci HW_READY_TIMEOUT); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci if (ret >= 0) 6138c2ecf20Sopenharmony_ci iwl_set_bit(trans, CSR_MBOX_SET_REG, CSR_MBOX_SET_REG_OS_ALIVE); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(trans, "hardware%s ready\n", ret < 0 ? " not" : ""); 6168c2ecf20Sopenharmony_ci return ret; 6178c2ecf20Sopenharmony_ci} 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci/* Note: returns standard 0/-ERROR code */ 6208c2ecf20Sopenharmony_ciint iwl_pcie_prepare_card_hw(struct iwl_trans *trans) 6218c2ecf20Sopenharmony_ci{ 6228c2ecf20Sopenharmony_ci int ret; 6238c2ecf20Sopenharmony_ci int iter; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(trans, "iwl_trans_prepare_card_hw enter\n"); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci ret = iwl_pcie_set_hw_ready(trans); 6288c2ecf20Sopenharmony_ci /* If the card is ready, exit 0 */ 6298c2ecf20Sopenharmony_ci if (ret >= 0) 6308c2ecf20Sopenharmony_ci return 0; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, 6338c2ecf20Sopenharmony_ci CSR_RESET_LINK_PWR_MGMT_DISABLED); 6348c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci for (iter = 0; iter < 10; iter++) { 6378c2ecf20Sopenharmony_ci int t = 0; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci /* If HW is not ready, prepare the conditions to check again */ 6408c2ecf20Sopenharmony_ci iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, 6418c2ecf20Sopenharmony_ci CSR_HW_IF_CONFIG_REG_PREPARE); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci do { 6448c2ecf20Sopenharmony_ci ret = iwl_pcie_set_hw_ready(trans); 6458c2ecf20Sopenharmony_ci if (ret >= 0) 6468c2ecf20Sopenharmony_ci return 0; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci usleep_range(200, 1000); 6498c2ecf20Sopenharmony_ci t += 200; 6508c2ecf20Sopenharmony_ci } while (t < 150000); 6518c2ecf20Sopenharmony_ci msleep(25); 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci IWL_ERR(trans, "Couldn't prepare the card\n"); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci return ret; 6578c2ecf20Sopenharmony_ci} 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci/* 6608c2ecf20Sopenharmony_ci * ucode 6618c2ecf20Sopenharmony_ci */ 6628c2ecf20Sopenharmony_cistatic void iwl_pcie_load_firmware_chunk_fh(struct iwl_trans *trans, 6638c2ecf20Sopenharmony_ci u32 dst_addr, dma_addr_t phy_addr, 6648c2ecf20Sopenharmony_ci u32 byte_cnt) 6658c2ecf20Sopenharmony_ci{ 6668c2ecf20Sopenharmony_ci iwl_write32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL), 6678c2ecf20Sopenharmony_ci FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci iwl_write32(trans, FH_SRVC_CHNL_SRAM_ADDR_REG(FH_SRVC_CHNL), 6708c2ecf20Sopenharmony_ci dst_addr); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci iwl_write32(trans, FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL), 6738c2ecf20Sopenharmony_ci phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci iwl_write32(trans, FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL), 6768c2ecf20Sopenharmony_ci (iwl_get_dma_hi_addr(phy_addr) 6778c2ecf20Sopenharmony_ci << FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt); 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci iwl_write32(trans, FH_TCSR_CHNL_TX_BUF_STS_REG(FH_SRVC_CHNL), 6808c2ecf20Sopenharmony_ci BIT(FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM) | 6818c2ecf20Sopenharmony_ci BIT(FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX) | 6828c2ecf20Sopenharmony_ci FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci iwl_write32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL), 6858c2ecf20Sopenharmony_ci FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | 6868c2ecf20Sopenharmony_ci FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE | 6878c2ecf20Sopenharmony_ci FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD); 6888c2ecf20Sopenharmony_ci} 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_cistatic int iwl_pcie_load_firmware_chunk(struct iwl_trans *trans, 6918c2ecf20Sopenharmony_ci u32 dst_addr, dma_addr_t phy_addr, 6928c2ecf20Sopenharmony_ci u32 byte_cnt) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 6958c2ecf20Sopenharmony_ci unsigned long flags; 6968c2ecf20Sopenharmony_ci int ret; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci trans_pcie->ucode_write_complete = false; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci if (!iwl_trans_grab_nic_access(trans, &flags)) 7018c2ecf20Sopenharmony_ci return -EIO; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci iwl_pcie_load_firmware_chunk_fh(trans, dst_addr, phy_addr, 7048c2ecf20Sopenharmony_ci byte_cnt); 7058c2ecf20Sopenharmony_ci iwl_trans_release_nic_access(trans, &flags); 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci ret = wait_event_timeout(trans_pcie->ucode_write_waitq, 7088c2ecf20Sopenharmony_ci trans_pcie->ucode_write_complete, 5 * HZ); 7098c2ecf20Sopenharmony_ci if (!ret) { 7108c2ecf20Sopenharmony_ci IWL_ERR(trans, "Failed to load firmware chunk!\n"); 7118c2ecf20Sopenharmony_ci iwl_trans_pcie_dump_regs(trans); 7128c2ecf20Sopenharmony_ci return -ETIMEDOUT; 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci return 0; 7168c2ecf20Sopenharmony_ci} 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_cistatic int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num, 7198c2ecf20Sopenharmony_ci const struct fw_desc *section) 7208c2ecf20Sopenharmony_ci{ 7218c2ecf20Sopenharmony_ci u8 *v_addr; 7228c2ecf20Sopenharmony_ci dma_addr_t p_addr; 7238c2ecf20Sopenharmony_ci u32 offset, chunk_sz = min_t(u32, FH_MEM_TB_MAX_LENGTH, section->len); 7248c2ecf20Sopenharmony_ci int ret = 0; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n", 7278c2ecf20Sopenharmony_ci section_num); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci v_addr = dma_alloc_coherent(trans->dev, chunk_sz, &p_addr, 7308c2ecf20Sopenharmony_ci GFP_KERNEL | __GFP_NOWARN); 7318c2ecf20Sopenharmony_ci if (!v_addr) { 7328c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(trans, "Falling back to small chunks of DMA\n"); 7338c2ecf20Sopenharmony_ci chunk_sz = PAGE_SIZE; 7348c2ecf20Sopenharmony_ci v_addr = dma_alloc_coherent(trans->dev, chunk_sz, 7358c2ecf20Sopenharmony_ci &p_addr, GFP_KERNEL); 7368c2ecf20Sopenharmony_ci if (!v_addr) 7378c2ecf20Sopenharmony_ci return -ENOMEM; 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci for (offset = 0; offset < section->len; offset += chunk_sz) { 7418c2ecf20Sopenharmony_ci u32 copy_size, dst_addr; 7428c2ecf20Sopenharmony_ci bool extended_addr = false; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci copy_size = min_t(u32, chunk_sz, section->len - offset); 7458c2ecf20Sopenharmony_ci dst_addr = section->offset + offset; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (dst_addr >= IWL_FW_MEM_EXTENDED_START && 7488c2ecf20Sopenharmony_ci dst_addr <= IWL_FW_MEM_EXTENDED_END) 7498c2ecf20Sopenharmony_ci extended_addr = true; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci if (extended_addr) 7528c2ecf20Sopenharmony_ci iwl_set_bits_prph(trans, LMPM_CHICK, 7538c2ecf20Sopenharmony_ci LMPM_CHICK_EXTENDED_ADDR_SPACE); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci memcpy(v_addr, (u8 *)section->data + offset, copy_size); 7568c2ecf20Sopenharmony_ci ret = iwl_pcie_load_firmware_chunk(trans, dst_addr, p_addr, 7578c2ecf20Sopenharmony_ci copy_size); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci if (extended_addr) 7608c2ecf20Sopenharmony_ci iwl_clear_bits_prph(trans, LMPM_CHICK, 7618c2ecf20Sopenharmony_ci LMPM_CHICK_EXTENDED_ADDR_SPACE); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci if (ret) { 7648c2ecf20Sopenharmony_ci IWL_ERR(trans, 7658c2ecf20Sopenharmony_ci "Could not load the [%d] uCode section\n", 7668c2ecf20Sopenharmony_ci section_num); 7678c2ecf20Sopenharmony_ci break; 7688c2ecf20Sopenharmony_ci } 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci dma_free_coherent(trans->dev, chunk_sz, v_addr, p_addr); 7728c2ecf20Sopenharmony_ci return ret; 7738c2ecf20Sopenharmony_ci} 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_cistatic int iwl_pcie_load_cpu_sections_8000(struct iwl_trans *trans, 7768c2ecf20Sopenharmony_ci const struct fw_img *image, 7778c2ecf20Sopenharmony_ci int cpu, 7788c2ecf20Sopenharmony_ci int *first_ucode_section) 7798c2ecf20Sopenharmony_ci{ 7808c2ecf20Sopenharmony_ci int shift_param; 7818c2ecf20Sopenharmony_ci int i, ret = 0, sec_num = 0x1; 7828c2ecf20Sopenharmony_ci u32 val, last_read_idx = 0; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci if (cpu == 1) { 7858c2ecf20Sopenharmony_ci shift_param = 0; 7868c2ecf20Sopenharmony_ci *first_ucode_section = 0; 7878c2ecf20Sopenharmony_ci } else { 7888c2ecf20Sopenharmony_ci shift_param = 16; 7898c2ecf20Sopenharmony_ci (*first_ucode_section)++; 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci for (i = *first_ucode_section; i < image->num_sec; i++) { 7938c2ecf20Sopenharmony_ci last_read_idx = i; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci /* 7968c2ecf20Sopenharmony_ci * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between 7978c2ecf20Sopenharmony_ci * CPU1 to CPU2. 7988c2ecf20Sopenharmony_ci * PAGING_SEPARATOR_SECTION delimiter - separate between 7998c2ecf20Sopenharmony_ci * CPU2 non paged to CPU2 paging sec. 8008c2ecf20Sopenharmony_ci */ 8018c2ecf20Sopenharmony_ci if (!image->sec[i].data || 8028c2ecf20Sopenharmony_ci image->sec[i].offset == CPU1_CPU2_SEPARATOR_SECTION || 8038c2ecf20Sopenharmony_ci image->sec[i].offset == PAGING_SEPARATOR_SECTION) { 8048c2ecf20Sopenharmony_ci IWL_DEBUG_FW(trans, 8058c2ecf20Sopenharmony_ci "Break since Data not valid or Empty section, sec = %d\n", 8068c2ecf20Sopenharmony_ci i); 8078c2ecf20Sopenharmony_ci break; 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci ret = iwl_pcie_load_section(trans, i, &image->sec[i]); 8118c2ecf20Sopenharmony_ci if (ret) 8128c2ecf20Sopenharmony_ci return ret; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci /* Notify ucode of loaded section number and status */ 8158c2ecf20Sopenharmony_ci val = iwl_read_direct32(trans, FH_UCODE_LOAD_STATUS); 8168c2ecf20Sopenharmony_ci val = val | (sec_num << shift_param); 8178c2ecf20Sopenharmony_ci iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, val); 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci sec_num = (sec_num << 1) | 0x1; 8208c2ecf20Sopenharmony_ci } 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci *first_ucode_section = last_read_idx; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci iwl_enable_interrupts(trans); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci if (trans->trans_cfg->use_tfh) { 8278c2ecf20Sopenharmony_ci if (cpu == 1) 8288c2ecf20Sopenharmony_ci iwl_write_prph(trans, UREG_UCODE_LOAD_STATUS, 8298c2ecf20Sopenharmony_ci 0xFFFF); 8308c2ecf20Sopenharmony_ci else 8318c2ecf20Sopenharmony_ci iwl_write_prph(trans, UREG_UCODE_LOAD_STATUS, 8328c2ecf20Sopenharmony_ci 0xFFFFFFFF); 8338c2ecf20Sopenharmony_ci } else { 8348c2ecf20Sopenharmony_ci if (cpu == 1) 8358c2ecf20Sopenharmony_ci iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, 8368c2ecf20Sopenharmony_ci 0xFFFF); 8378c2ecf20Sopenharmony_ci else 8388c2ecf20Sopenharmony_ci iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, 8398c2ecf20Sopenharmony_ci 0xFFFFFFFF); 8408c2ecf20Sopenharmony_ci } 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci return 0; 8438c2ecf20Sopenharmony_ci} 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_cistatic int iwl_pcie_load_cpu_sections(struct iwl_trans *trans, 8468c2ecf20Sopenharmony_ci const struct fw_img *image, 8478c2ecf20Sopenharmony_ci int cpu, 8488c2ecf20Sopenharmony_ci int *first_ucode_section) 8498c2ecf20Sopenharmony_ci{ 8508c2ecf20Sopenharmony_ci int i, ret = 0; 8518c2ecf20Sopenharmony_ci u32 last_read_idx = 0; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci if (cpu == 1) 8548c2ecf20Sopenharmony_ci *first_ucode_section = 0; 8558c2ecf20Sopenharmony_ci else 8568c2ecf20Sopenharmony_ci (*first_ucode_section)++; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci for (i = *first_ucode_section; i < image->num_sec; i++) { 8598c2ecf20Sopenharmony_ci last_read_idx = i; 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci /* 8628c2ecf20Sopenharmony_ci * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between 8638c2ecf20Sopenharmony_ci * CPU1 to CPU2. 8648c2ecf20Sopenharmony_ci * PAGING_SEPARATOR_SECTION delimiter - separate between 8658c2ecf20Sopenharmony_ci * CPU2 non paged to CPU2 paging sec. 8668c2ecf20Sopenharmony_ci */ 8678c2ecf20Sopenharmony_ci if (!image->sec[i].data || 8688c2ecf20Sopenharmony_ci image->sec[i].offset == CPU1_CPU2_SEPARATOR_SECTION || 8698c2ecf20Sopenharmony_ci image->sec[i].offset == PAGING_SEPARATOR_SECTION) { 8708c2ecf20Sopenharmony_ci IWL_DEBUG_FW(trans, 8718c2ecf20Sopenharmony_ci "Break since Data not valid or Empty section, sec = %d\n", 8728c2ecf20Sopenharmony_ci i); 8738c2ecf20Sopenharmony_ci break; 8748c2ecf20Sopenharmony_ci } 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci ret = iwl_pcie_load_section(trans, i, &image->sec[i]); 8778c2ecf20Sopenharmony_ci if (ret) 8788c2ecf20Sopenharmony_ci return ret; 8798c2ecf20Sopenharmony_ci } 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci *first_ucode_section = last_read_idx; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci return 0; 8848c2ecf20Sopenharmony_ci} 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_cistatic void iwl_pcie_apply_destination_ini(struct iwl_trans *trans) 8878c2ecf20Sopenharmony_ci{ 8888c2ecf20Sopenharmony_ci enum iwl_fw_ini_allocation_id alloc_id = IWL_FW_INI_ALLOCATION_ID_DBGC1; 8898c2ecf20Sopenharmony_ci struct iwl_fw_ini_allocation_tlv *fw_mon_cfg = 8908c2ecf20Sopenharmony_ci &trans->dbg.fw_mon_cfg[alloc_id]; 8918c2ecf20Sopenharmony_ci struct iwl_dram_data *frag; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci if (!iwl_trans_dbg_ini_valid(trans)) 8948c2ecf20Sopenharmony_ci return; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci if (le32_to_cpu(fw_mon_cfg->buf_location) == 8978c2ecf20Sopenharmony_ci IWL_FW_INI_LOCATION_SRAM_PATH) { 8988c2ecf20Sopenharmony_ci IWL_DEBUG_FW(trans, "WRT: Applying SMEM buffer destination\n"); 8998c2ecf20Sopenharmony_ci /* set sram monitor by enabling bit 7 */ 9008c2ecf20Sopenharmony_ci iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, 9018c2ecf20Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_MONITOR_SRAM); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci return; 9048c2ecf20Sopenharmony_ci } 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci if (le32_to_cpu(fw_mon_cfg->buf_location) != 9078c2ecf20Sopenharmony_ci IWL_FW_INI_LOCATION_DRAM_PATH || 9088c2ecf20Sopenharmony_ci !trans->dbg.fw_mon_ini[alloc_id].num_frags) 9098c2ecf20Sopenharmony_ci return; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci frag = &trans->dbg.fw_mon_ini[alloc_id].frags[0]; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci IWL_DEBUG_FW(trans, "WRT: Applying DRAM destination (alloc_id=%u)\n", 9148c2ecf20Sopenharmony_ci alloc_id); 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci iwl_write_umac_prph(trans, MON_BUFF_BASE_ADDR_VER2, 9178c2ecf20Sopenharmony_ci frag->physical >> MON_BUFF_SHIFT_VER2); 9188c2ecf20Sopenharmony_ci iwl_write_umac_prph(trans, MON_BUFF_END_ADDR_VER2, 9198c2ecf20Sopenharmony_ci (frag->physical + frag->size - 256) >> 9208c2ecf20Sopenharmony_ci MON_BUFF_SHIFT_VER2); 9218c2ecf20Sopenharmony_ci} 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_civoid iwl_pcie_apply_destination(struct iwl_trans *trans) 9248c2ecf20Sopenharmony_ci{ 9258c2ecf20Sopenharmony_ci const struct iwl_fw_dbg_dest_tlv_v1 *dest = trans->dbg.dest_tlv; 9268c2ecf20Sopenharmony_ci const struct iwl_dram_data *fw_mon = &trans->dbg.fw_mon; 9278c2ecf20Sopenharmony_ci int i; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci if (iwl_trans_dbg_ini_valid(trans)) { 9308c2ecf20Sopenharmony_ci iwl_pcie_apply_destination_ini(trans); 9318c2ecf20Sopenharmony_ci return; 9328c2ecf20Sopenharmony_ci } 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci IWL_INFO(trans, "Applying debug destination %s\n", 9358c2ecf20Sopenharmony_ci get_fw_dbg_mode_string(dest->monitor_mode)); 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci if (dest->monitor_mode == EXTERNAL_MODE) 9388c2ecf20Sopenharmony_ci iwl_pcie_alloc_fw_monitor(trans, dest->size_power); 9398c2ecf20Sopenharmony_ci else 9408c2ecf20Sopenharmony_ci IWL_WARN(trans, "PCI should have external buffer debug\n"); 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci for (i = 0; i < trans->dbg.n_dest_reg; i++) { 9438c2ecf20Sopenharmony_ci u32 addr = le32_to_cpu(dest->reg_ops[i].addr); 9448c2ecf20Sopenharmony_ci u32 val = le32_to_cpu(dest->reg_ops[i].val); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci switch (dest->reg_ops[i].op) { 9478c2ecf20Sopenharmony_ci case CSR_ASSIGN: 9488c2ecf20Sopenharmony_ci iwl_write32(trans, addr, val); 9498c2ecf20Sopenharmony_ci break; 9508c2ecf20Sopenharmony_ci case CSR_SETBIT: 9518c2ecf20Sopenharmony_ci iwl_set_bit(trans, addr, BIT(val)); 9528c2ecf20Sopenharmony_ci break; 9538c2ecf20Sopenharmony_ci case CSR_CLEARBIT: 9548c2ecf20Sopenharmony_ci iwl_clear_bit(trans, addr, BIT(val)); 9558c2ecf20Sopenharmony_ci break; 9568c2ecf20Sopenharmony_ci case PRPH_ASSIGN: 9578c2ecf20Sopenharmony_ci iwl_write_prph(trans, addr, val); 9588c2ecf20Sopenharmony_ci break; 9598c2ecf20Sopenharmony_ci case PRPH_SETBIT: 9608c2ecf20Sopenharmony_ci iwl_set_bits_prph(trans, addr, BIT(val)); 9618c2ecf20Sopenharmony_ci break; 9628c2ecf20Sopenharmony_ci case PRPH_CLEARBIT: 9638c2ecf20Sopenharmony_ci iwl_clear_bits_prph(trans, addr, BIT(val)); 9648c2ecf20Sopenharmony_ci break; 9658c2ecf20Sopenharmony_ci case PRPH_BLOCKBIT: 9668c2ecf20Sopenharmony_ci if (iwl_read_prph(trans, addr) & BIT(val)) { 9678c2ecf20Sopenharmony_ci IWL_ERR(trans, 9688c2ecf20Sopenharmony_ci "BIT(%u) in address 0x%x is 1, stopping FW configuration\n", 9698c2ecf20Sopenharmony_ci val, addr); 9708c2ecf20Sopenharmony_ci goto monitor; 9718c2ecf20Sopenharmony_ci } 9728c2ecf20Sopenharmony_ci break; 9738c2ecf20Sopenharmony_ci default: 9748c2ecf20Sopenharmony_ci IWL_ERR(trans, "FW debug - unknown OP %d\n", 9758c2ecf20Sopenharmony_ci dest->reg_ops[i].op); 9768c2ecf20Sopenharmony_ci break; 9778c2ecf20Sopenharmony_ci } 9788c2ecf20Sopenharmony_ci } 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_cimonitor: 9818c2ecf20Sopenharmony_ci if (dest->monitor_mode == EXTERNAL_MODE && fw_mon->size) { 9828c2ecf20Sopenharmony_ci iwl_write_prph(trans, le32_to_cpu(dest->base_reg), 9838c2ecf20Sopenharmony_ci fw_mon->physical >> dest->base_shift); 9848c2ecf20Sopenharmony_ci if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_8000) 9858c2ecf20Sopenharmony_ci iwl_write_prph(trans, le32_to_cpu(dest->end_reg), 9868c2ecf20Sopenharmony_ci (fw_mon->physical + fw_mon->size - 9878c2ecf20Sopenharmony_ci 256) >> dest->end_shift); 9888c2ecf20Sopenharmony_ci else 9898c2ecf20Sopenharmony_ci iwl_write_prph(trans, le32_to_cpu(dest->end_reg), 9908c2ecf20Sopenharmony_ci (fw_mon->physical + fw_mon->size) >> 9918c2ecf20Sopenharmony_ci dest->end_shift); 9928c2ecf20Sopenharmony_ci } 9938c2ecf20Sopenharmony_ci} 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_cistatic int iwl_pcie_load_given_ucode(struct iwl_trans *trans, 9968c2ecf20Sopenharmony_ci const struct fw_img *image) 9978c2ecf20Sopenharmony_ci{ 9988c2ecf20Sopenharmony_ci int ret = 0; 9998c2ecf20Sopenharmony_ci int first_ucode_section; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci IWL_DEBUG_FW(trans, "working with %s CPU\n", 10028c2ecf20Sopenharmony_ci image->is_dual_cpus ? "Dual" : "Single"); 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci /* load to FW the binary non secured sections of CPU1 */ 10058c2ecf20Sopenharmony_ci ret = iwl_pcie_load_cpu_sections(trans, image, 1, &first_ucode_section); 10068c2ecf20Sopenharmony_ci if (ret) 10078c2ecf20Sopenharmony_ci return ret; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci if (image->is_dual_cpus) { 10108c2ecf20Sopenharmony_ci /* set CPU2 header address */ 10118c2ecf20Sopenharmony_ci iwl_write_prph(trans, 10128c2ecf20Sopenharmony_ci LMPM_SECURE_UCODE_LOAD_CPU2_HDR_ADDR, 10138c2ecf20Sopenharmony_ci LMPM_SECURE_CPU2_HDR_MEM_SPACE); 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci /* load to FW the binary sections of CPU2 */ 10168c2ecf20Sopenharmony_ci ret = iwl_pcie_load_cpu_sections(trans, image, 2, 10178c2ecf20Sopenharmony_ci &first_ucode_section); 10188c2ecf20Sopenharmony_ci if (ret) 10198c2ecf20Sopenharmony_ci return ret; 10208c2ecf20Sopenharmony_ci } 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci if (iwl_pcie_dbg_on(trans)) 10238c2ecf20Sopenharmony_ci iwl_pcie_apply_destination(trans); 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci iwl_enable_interrupts(trans); 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci /* release CPU reset */ 10288c2ecf20Sopenharmony_ci iwl_write32(trans, CSR_RESET, 0); 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci return 0; 10318c2ecf20Sopenharmony_ci} 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_cistatic int iwl_pcie_load_given_ucode_8000(struct iwl_trans *trans, 10348c2ecf20Sopenharmony_ci const struct fw_img *image) 10358c2ecf20Sopenharmony_ci{ 10368c2ecf20Sopenharmony_ci int ret = 0; 10378c2ecf20Sopenharmony_ci int first_ucode_section; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci IWL_DEBUG_FW(trans, "working with %s CPU\n", 10408c2ecf20Sopenharmony_ci image->is_dual_cpus ? "Dual" : "Single"); 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci if (iwl_pcie_dbg_on(trans)) 10438c2ecf20Sopenharmony_ci iwl_pcie_apply_destination(trans); 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci IWL_DEBUG_POWER(trans, "Original WFPM value = 0x%08X\n", 10468c2ecf20Sopenharmony_ci iwl_read_prph(trans, WFPM_GP2)); 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci /* 10498c2ecf20Sopenharmony_ci * Set default value. On resume reading the values that were 10508c2ecf20Sopenharmony_ci * zeored can provide debug data on the resume flow. 10518c2ecf20Sopenharmony_ci * This is for debugging only and has no functional impact. 10528c2ecf20Sopenharmony_ci */ 10538c2ecf20Sopenharmony_ci iwl_write_prph(trans, WFPM_GP2, 0x01010101); 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci /* configure the ucode to be ready to get the secured image */ 10568c2ecf20Sopenharmony_ci /* release CPU reset */ 10578c2ecf20Sopenharmony_ci iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT); 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci /* load to FW the binary Secured sections of CPU1 */ 10608c2ecf20Sopenharmony_ci ret = iwl_pcie_load_cpu_sections_8000(trans, image, 1, 10618c2ecf20Sopenharmony_ci &first_ucode_section); 10628c2ecf20Sopenharmony_ci if (ret) 10638c2ecf20Sopenharmony_ci return ret; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci /* load to FW the binary sections of CPU2 */ 10668c2ecf20Sopenharmony_ci return iwl_pcie_load_cpu_sections_8000(trans, image, 2, 10678c2ecf20Sopenharmony_ci &first_ucode_section); 10688c2ecf20Sopenharmony_ci} 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_cibool iwl_pcie_check_hw_rf_kill(struct iwl_trans *trans) 10718c2ecf20Sopenharmony_ci{ 10728c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 10738c2ecf20Sopenharmony_ci bool hw_rfkill = iwl_is_rfkill_set(trans); 10748c2ecf20Sopenharmony_ci bool prev = test_bit(STATUS_RFKILL_OPMODE, &trans->status); 10758c2ecf20Sopenharmony_ci bool report; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci if (hw_rfkill) { 10788c2ecf20Sopenharmony_ci set_bit(STATUS_RFKILL_HW, &trans->status); 10798c2ecf20Sopenharmony_ci set_bit(STATUS_RFKILL_OPMODE, &trans->status); 10808c2ecf20Sopenharmony_ci } else { 10818c2ecf20Sopenharmony_ci clear_bit(STATUS_RFKILL_HW, &trans->status); 10828c2ecf20Sopenharmony_ci if (trans_pcie->opmode_down) 10838c2ecf20Sopenharmony_ci clear_bit(STATUS_RFKILL_OPMODE, &trans->status); 10848c2ecf20Sopenharmony_ci } 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci report = test_bit(STATUS_RFKILL_OPMODE, &trans->status); 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci if (prev != report) 10898c2ecf20Sopenharmony_ci iwl_trans_pcie_rf_kill(trans, report); 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci return hw_rfkill; 10928c2ecf20Sopenharmony_ci} 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_cistruct iwl_causes_list { 10958c2ecf20Sopenharmony_ci u32 cause_num; 10968c2ecf20Sopenharmony_ci u32 mask_reg; 10978c2ecf20Sopenharmony_ci u8 addr; 10988c2ecf20Sopenharmony_ci}; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_cistatic struct iwl_causes_list causes_list[] = { 11018c2ecf20Sopenharmony_ci {MSIX_FH_INT_CAUSES_D2S_CH0_NUM, CSR_MSIX_FH_INT_MASK_AD, 0}, 11028c2ecf20Sopenharmony_ci {MSIX_FH_INT_CAUSES_D2S_CH1_NUM, CSR_MSIX_FH_INT_MASK_AD, 0x1}, 11038c2ecf20Sopenharmony_ci {MSIX_FH_INT_CAUSES_S2D, CSR_MSIX_FH_INT_MASK_AD, 0x3}, 11048c2ecf20Sopenharmony_ci {MSIX_FH_INT_CAUSES_FH_ERR, CSR_MSIX_FH_INT_MASK_AD, 0x5}, 11058c2ecf20Sopenharmony_ci {MSIX_HW_INT_CAUSES_REG_ALIVE, CSR_MSIX_HW_INT_MASK_AD, 0x10}, 11068c2ecf20Sopenharmony_ci {MSIX_HW_INT_CAUSES_REG_WAKEUP, CSR_MSIX_HW_INT_MASK_AD, 0x11}, 11078c2ecf20Sopenharmony_ci {MSIX_HW_INT_CAUSES_REG_IML, CSR_MSIX_HW_INT_MASK_AD, 0x12}, 11088c2ecf20Sopenharmony_ci {MSIX_HW_INT_CAUSES_REG_CT_KILL, CSR_MSIX_HW_INT_MASK_AD, 0x16}, 11098c2ecf20Sopenharmony_ci {MSIX_HW_INT_CAUSES_REG_RF_KILL, CSR_MSIX_HW_INT_MASK_AD, 0x17}, 11108c2ecf20Sopenharmony_ci {MSIX_HW_INT_CAUSES_REG_PERIODIC, CSR_MSIX_HW_INT_MASK_AD, 0x18}, 11118c2ecf20Sopenharmony_ci {MSIX_HW_INT_CAUSES_REG_SW_ERR, CSR_MSIX_HW_INT_MASK_AD, 0x29}, 11128c2ecf20Sopenharmony_ci {MSIX_HW_INT_CAUSES_REG_SCD, CSR_MSIX_HW_INT_MASK_AD, 0x2A}, 11138c2ecf20Sopenharmony_ci {MSIX_HW_INT_CAUSES_REG_FH_TX, CSR_MSIX_HW_INT_MASK_AD, 0x2B}, 11148c2ecf20Sopenharmony_ci {MSIX_HW_INT_CAUSES_REG_HW_ERR, CSR_MSIX_HW_INT_MASK_AD, 0x2D}, 11158c2ecf20Sopenharmony_ci {MSIX_HW_INT_CAUSES_REG_HAP, CSR_MSIX_HW_INT_MASK_AD, 0x2E}, 11168c2ecf20Sopenharmony_ci}; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_cistatic void iwl_pcie_map_non_rx_causes(struct iwl_trans *trans) 11198c2ecf20Sopenharmony_ci{ 11208c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 11218c2ecf20Sopenharmony_ci int val = trans_pcie->def_irq | MSIX_NON_AUTO_CLEAR_CAUSE; 11228c2ecf20Sopenharmony_ci int i, arr_size = ARRAY_SIZE(causes_list); 11238c2ecf20Sopenharmony_ci struct iwl_causes_list *causes = causes_list; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci /* 11268c2ecf20Sopenharmony_ci * Access all non RX causes and map them to the default irq. 11278c2ecf20Sopenharmony_ci * In case we are missing at least one interrupt vector, 11288c2ecf20Sopenharmony_ci * the first interrupt vector will serve non-RX and FBQ causes. 11298c2ecf20Sopenharmony_ci */ 11308c2ecf20Sopenharmony_ci for (i = 0; i < arr_size; i++) { 11318c2ecf20Sopenharmony_ci iwl_write8(trans, CSR_MSIX_IVAR(causes[i].addr), val); 11328c2ecf20Sopenharmony_ci iwl_clear_bit(trans, causes[i].mask_reg, 11338c2ecf20Sopenharmony_ci causes[i].cause_num); 11348c2ecf20Sopenharmony_ci } 11358c2ecf20Sopenharmony_ci} 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_cistatic void iwl_pcie_map_rx_causes(struct iwl_trans *trans) 11388c2ecf20Sopenharmony_ci{ 11398c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 11408c2ecf20Sopenharmony_ci u32 offset = 11418c2ecf20Sopenharmony_ci trans_pcie->shared_vec_mask & IWL_SHARED_IRQ_FIRST_RSS ? 1 : 0; 11428c2ecf20Sopenharmony_ci u32 val, idx; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci /* 11458c2ecf20Sopenharmony_ci * The first RX queue - fallback queue, which is designated for 11468c2ecf20Sopenharmony_ci * management frame, command responses etc, is always mapped to the 11478c2ecf20Sopenharmony_ci * first interrupt vector. The other RX queues are mapped to 11488c2ecf20Sopenharmony_ci * the other (N - 2) interrupt vectors. 11498c2ecf20Sopenharmony_ci */ 11508c2ecf20Sopenharmony_ci val = BIT(MSIX_FH_INT_CAUSES_Q(0)); 11518c2ecf20Sopenharmony_ci for (idx = 1; idx < trans->num_rx_queues; idx++) { 11528c2ecf20Sopenharmony_ci iwl_write8(trans, CSR_MSIX_RX_IVAR(idx), 11538c2ecf20Sopenharmony_ci MSIX_FH_INT_CAUSES_Q(idx - offset)); 11548c2ecf20Sopenharmony_ci val |= BIT(MSIX_FH_INT_CAUSES_Q(idx)); 11558c2ecf20Sopenharmony_ci } 11568c2ecf20Sopenharmony_ci iwl_write32(trans, CSR_MSIX_FH_INT_MASK_AD, ~val); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci val = MSIX_FH_INT_CAUSES_Q(0); 11598c2ecf20Sopenharmony_ci if (trans_pcie->shared_vec_mask & IWL_SHARED_IRQ_NON_RX) 11608c2ecf20Sopenharmony_ci val |= MSIX_NON_AUTO_CLEAR_CAUSE; 11618c2ecf20Sopenharmony_ci iwl_write8(trans, CSR_MSIX_RX_IVAR(0), val); 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci if (trans_pcie->shared_vec_mask & IWL_SHARED_IRQ_FIRST_RSS) 11648c2ecf20Sopenharmony_ci iwl_write8(trans, CSR_MSIX_RX_IVAR(1), val); 11658c2ecf20Sopenharmony_ci} 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_civoid iwl_pcie_conf_msix_hw(struct iwl_trans_pcie *trans_pcie) 11688c2ecf20Sopenharmony_ci{ 11698c2ecf20Sopenharmony_ci struct iwl_trans *trans = trans_pcie->trans; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci if (!trans_pcie->msix_enabled) { 11728c2ecf20Sopenharmony_ci if (trans->trans_cfg->mq_rx_supported && 11738c2ecf20Sopenharmony_ci test_bit(STATUS_DEVICE_ENABLED, &trans->status)) 11748c2ecf20Sopenharmony_ci iwl_write_umac_prph(trans, UREG_CHICK, 11758c2ecf20Sopenharmony_ci UREG_CHICK_MSI_ENABLE); 11768c2ecf20Sopenharmony_ci return; 11778c2ecf20Sopenharmony_ci } 11788c2ecf20Sopenharmony_ci /* 11798c2ecf20Sopenharmony_ci * The IVAR table needs to be configured again after reset, 11808c2ecf20Sopenharmony_ci * but if the device is disabled, we can't write to 11818c2ecf20Sopenharmony_ci * prph. 11828c2ecf20Sopenharmony_ci */ 11838c2ecf20Sopenharmony_ci if (test_bit(STATUS_DEVICE_ENABLED, &trans->status)) 11848c2ecf20Sopenharmony_ci iwl_write_umac_prph(trans, UREG_CHICK, UREG_CHICK_MSIX_ENABLE); 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci /* 11878c2ecf20Sopenharmony_ci * Each cause from the causes list above and the RX causes is 11888c2ecf20Sopenharmony_ci * represented as a byte in the IVAR table. The first nibble 11898c2ecf20Sopenharmony_ci * represents the bound interrupt vector of the cause, the second 11908c2ecf20Sopenharmony_ci * represents no auto clear for this cause. This will be set if its 11918c2ecf20Sopenharmony_ci * interrupt vector is bound to serve other causes. 11928c2ecf20Sopenharmony_ci */ 11938c2ecf20Sopenharmony_ci iwl_pcie_map_rx_causes(trans); 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci iwl_pcie_map_non_rx_causes(trans); 11968c2ecf20Sopenharmony_ci} 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_cistatic void iwl_pcie_init_msix(struct iwl_trans_pcie *trans_pcie) 11998c2ecf20Sopenharmony_ci{ 12008c2ecf20Sopenharmony_ci struct iwl_trans *trans = trans_pcie->trans; 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci iwl_pcie_conf_msix_hw(trans_pcie); 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci if (!trans_pcie->msix_enabled) 12058c2ecf20Sopenharmony_ci return; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci trans_pcie->fh_init_mask = ~iwl_read32(trans, CSR_MSIX_FH_INT_MASK_AD); 12088c2ecf20Sopenharmony_ci trans_pcie->fh_mask = trans_pcie->fh_init_mask; 12098c2ecf20Sopenharmony_ci trans_pcie->hw_init_mask = ~iwl_read32(trans, CSR_MSIX_HW_INT_MASK_AD); 12108c2ecf20Sopenharmony_ci trans_pcie->hw_mask = trans_pcie->hw_init_mask; 12118c2ecf20Sopenharmony_ci} 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_cistatic void _iwl_trans_pcie_stop_device(struct iwl_trans *trans) 12148c2ecf20Sopenharmony_ci{ 12158c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci lockdep_assert_held(&trans_pcie->mutex); 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci if (trans_pcie->is_down) 12208c2ecf20Sopenharmony_ci return; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci trans_pcie->is_down = true; 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci /* tell the device to stop sending interrupts */ 12258c2ecf20Sopenharmony_ci iwl_disable_interrupts(trans); 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci /* device going down, Stop using ICT table */ 12288c2ecf20Sopenharmony_ci iwl_pcie_disable_ict(trans); 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci /* 12318c2ecf20Sopenharmony_ci * If a HW restart happens during firmware loading, 12328c2ecf20Sopenharmony_ci * then the firmware loading might call this function 12338c2ecf20Sopenharmony_ci * and later it might be called again due to the 12348c2ecf20Sopenharmony_ci * restart. So don't process again if the device is 12358c2ecf20Sopenharmony_ci * already dead. 12368c2ecf20Sopenharmony_ci */ 12378c2ecf20Sopenharmony_ci if (test_and_clear_bit(STATUS_DEVICE_ENABLED, &trans->status)) { 12388c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(trans, 12398c2ecf20Sopenharmony_ci "DEVICE_ENABLED bit was set and is now cleared\n"); 12408c2ecf20Sopenharmony_ci iwl_pcie_tx_stop(trans); 12418c2ecf20Sopenharmony_ci iwl_pcie_rx_stop(trans); 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci /* Power-down device's busmaster DMA clocks */ 12448c2ecf20Sopenharmony_ci if (!trans->cfg->apmg_not_supported) { 12458c2ecf20Sopenharmony_ci iwl_write_prph(trans, APMG_CLK_DIS_REG, 12468c2ecf20Sopenharmony_ci APMG_CLK_VAL_DMA_CLK_RQT); 12478c2ecf20Sopenharmony_ci udelay(5); 12488c2ecf20Sopenharmony_ci } 12498c2ecf20Sopenharmony_ci } 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci /* Make sure (redundant) we've released our request to stay awake */ 12528c2ecf20Sopenharmony_ci iwl_clear_bit(trans, CSR_GP_CNTRL, 12538c2ecf20Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci /* Stop the device, and put it in low power state */ 12568c2ecf20Sopenharmony_ci iwl_pcie_apm_stop(trans, false); 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci iwl_trans_pcie_sw_reset(trans); 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci /* 12618c2ecf20Sopenharmony_ci * Upon stop, the IVAR table gets erased, so msi-x won't 12628c2ecf20Sopenharmony_ci * work. This causes a bug in RF-KILL flows, since the interrupt 12638c2ecf20Sopenharmony_ci * that enables radio won't fire on the correct irq, and the 12648c2ecf20Sopenharmony_ci * driver won't be able to handle the interrupt. 12658c2ecf20Sopenharmony_ci * Configure the IVAR table again after reset. 12668c2ecf20Sopenharmony_ci */ 12678c2ecf20Sopenharmony_ci iwl_pcie_conf_msix_hw(trans_pcie); 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci /* 12708c2ecf20Sopenharmony_ci * Upon stop, the APM issues an interrupt if HW RF kill is set. 12718c2ecf20Sopenharmony_ci * This is a bug in certain verions of the hardware. 12728c2ecf20Sopenharmony_ci * Certain devices also keep sending HW RF kill interrupt all 12738c2ecf20Sopenharmony_ci * the time, unless the interrupt is ACKed even if the interrupt 12748c2ecf20Sopenharmony_ci * should be masked. Re-ACK all the interrupts here. 12758c2ecf20Sopenharmony_ci */ 12768c2ecf20Sopenharmony_ci iwl_disable_interrupts(trans); 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci /* clear all status bits */ 12798c2ecf20Sopenharmony_ci clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); 12808c2ecf20Sopenharmony_ci clear_bit(STATUS_INT_ENABLED, &trans->status); 12818c2ecf20Sopenharmony_ci clear_bit(STATUS_TPOWER_PMI, &trans->status); 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci /* 12848c2ecf20Sopenharmony_ci * Even if we stop the HW, we still want the RF kill 12858c2ecf20Sopenharmony_ci * interrupt 12868c2ecf20Sopenharmony_ci */ 12878c2ecf20Sopenharmony_ci iwl_enable_rfkill_int(trans); 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci /* re-take ownership to prevent other users from stealing the device */ 12908c2ecf20Sopenharmony_ci iwl_pcie_prepare_card_hw(trans); 12918c2ecf20Sopenharmony_ci} 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_civoid iwl_pcie_synchronize_irqs(struct iwl_trans *trans) 12948c2ecf20Sopenharmony_ci{ 12958c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci if (trans_pcie->msix_enabled) { 12988c2ecf20Sopenharmony_ci int i; 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci for (i = 0; i < trans_pcie->alloc_vecs; i++) 13018c2ecf20Sopenharmony_ci synchronize_irq(trans_pcie->msix_entries[i].vector); 13028c2ecf20Sopenharmony_ci } else { 13038c2ecf20Sopenharmony_ci synchronize_irq(trans_pcie->pci_dev->irq); 13048c2ecf20Sopenharmony_ci } 13058c2ecf20Sopenharmony_ci} 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_cistatic int iwl_trans_pcie_start_fw(struct iwl_trans *trans, 13088c2ecf20Sopenharmony_ci const struct fw_img *fw, bool run_in_rfkill) 13098c2ecf20Sopenharmony_ci{ 13108c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 13118c2ecf20Sopenharmony_ci bool hw_rfkill; 13128c2ecf20Sopenharmony_ci int ret; 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci /* This may fail if AMT took ownership of the device */ 13158c2ecf20Sopenharmony_ci if (iwl_pcie_prepare_card_hw(trans)) { 13168c2ecf20Sopenharmony_ci IWL_WARN(trans, "Exit HW not ready\n"); 13178c2ecf20Sopenharmony_ci return -EIO; 13188c2ecf20Sopenharmony_ci } 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci iwl_enable_rfkill_int(trans); 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci iwl_write32(trans, CSR_INT, 0xFFFFFFFF); 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci /* 13258c2ecf20Sopenharmony_ci * We enabled the RF-Kill interrupt and the handler may very 13268c2ecf20Sopenharmony_ci * well be running. Disable the interrupts to make sure no other 13278c2ecf20Sopenharmony_ci * interrupt can be fired. 13288c2ecf20Sopenharmony_ci */ 13298c2ecf20Sopenharmony_ci iwl_disable_interrupts(trans); 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci /* Make sure it finished running */ 13328c2ecf20Sopenharmony_ci iwl_pcie_synchronize_irqs(trans); 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci mutex_lock(&trans_pcie->mutex); 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci /* If platform's RF_KILL switch is NOT set to KILL */ 13378c2ecf20Sopenharmony_ci hw_rfkill = iwl_pcie_check_hw_rf_kill(trans); 13388c2ecf20Sopenharmony_ci if (hw_rfkill && !run_in_rfkill) { 13398c2ecf20Sopenharmony_ci ret = -ERFKILL; 13408c2ecf20Sopenharmony_ci goto out; 13418c2ecf20Sopenharmony_ci } 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci /* Someone called stop_device, don't try to start_fw */ 13448c2ecf20Sopenharmony_ci if (trans_pcie->is_down) { 13458c2ecf20Sopenharmony_ci IWL_WARN(trans, 13468c2ecf20Sopenharmony_ci "Can't start_fw since the HW hasn't been started\n"); 13478c2ecf20Sopenharmony_ci ret = -EIO; 13488c2ecf20Sopenharmony_ci goto out; 13498c2ecf20Sopenharmony_ci } 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci /* make sure rfkill handshake bits are cleared */ 13528c2ecf20Sopenharmony_ci iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); 13538c2ecf20Sopenharmony_ci iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, 13548c2ecf20Sopenharmony_ci CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci /* clear (again), then enable host interrupts */ 13578c2ecf20Sopenharmony_ci iwl_write32(trans, CSR_INT, 0xFFFFFFFF); 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci ret = iwl_pcie_nic_init(trans); 13608c2ecf20Sopenharmony_ci if (ret) { 13618c2ecf20Sopenharmony_ci IWL_ERR(trans, "Unable to init nic\n"); 13628c2ecf20Sopenharmony_ci goto out; 13638c2ecf20Sopenharmony_ci } 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci /* 13668c2ecf20Sopenharmony_ci * Now, we load the firmware and don't want to be interrupted, even 13678c2ecf20Sopenharmony_ci * by the RF-Kill interrupt (hence mask all the interrupt besides the 13688c2ecf20Sopenharmony_ci * FH_TX interrupt which is needed to load the firmware). If the 13698c2ecf20Sopenharmony_ci * RF-Kill switch is toggled, we will find out after having loaded 13708c2ecf20Sopenharmony_ci * the firmware and return the proper value to the caller. 13718c2ecf20Sopenharmony_ci */ 13728c2ecf20Sopenharmony_ci iwl_enable_fw_load_int(trans); 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci /* really make sure rfkill handshake bits are cleared */ 13758c2ecf20Sopenharmony_ci iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); 13768c2ecf20Sopenharmony_ci iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci /* Load the given image to the HW */ 13798c2ecf20Sopenharmony_ci if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_8000) 13808c2ecf20Sopenharmony_ci ret = iwl_pcie_load_given_ucode_8000(trans, fw); 13818c2ecf20Sopenharmony_ci else 13828c2ecf20Sopenharmony_ci ret = iwl_pcie_load_given_ucode(trans, fw); 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci /* re-check RF-Kill state since we may have missed the interrupt */ 13858c2ecf20Sopenharmony_ci hw_rfkill = iwl_pcie_check_hw_rf_kill(trans); 13868c2ecf20Sopenharmony_ci if (hw_rfkill && !run_in_rfkill) 13878c2ecf20Sopenharmony_ci ret = -ERFKILL; 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ciout: 13908c2ecf20Sopenharmony_ci mutex_unlock(&trans_pcie->mutex); 13918c2ecf20Sopenharmony_ci return ret; 13928c2ecf20Sopenharmony_ci} 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_cistatic void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr) 13958c2ecf20Sopenharmony_ci{ 13968c2ecf20Sopenharmony_ci iwl_pcie_reset_ict(trans); 13978c2ecf20Sopenharmony_ci iwl_pcie_tx_start(trans, scd_addr); 13988c2ecf20Sopenharmony_ci} 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_civoid iwl_trans_pcie_handle_stop_rfkill(struct iwl_trans *trans, 14018c2ecf20Sopenharmony_ci bool was_in_rfkill) 14028c2ecf20Sopenharmony_ci{ 14038c2ecf20Sopenharmony_ci bool hw_rfkill; 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci /* 14068c2ecf20Sopenharmony_ci * Check again since the RF kill state may have changed while 14078c2ecf20Sopenharmony_ci * all the interrupts were disabled, in this case we couldn't 14088c2ecf20Sopenharmony_ci * receive the RF kill interrupt and update the state in the 14098c2ecf20Sopenharmony_ci * op_mode. 14108c2ecf20Sopenharmony_ci * Don't call the op_mode if the rkfill state hasn't changed. 14118c2ecf20Sopenharmony_ci * This allows the op_mode to call stop_device from the rfkill 14128c2ecf20Sopenharmony_ci * notification without endless recursion. Under very rare 14138c2ecf20Sopenharmony_ci * circumstances, we might have a small recursion if the rfkill 14148c2ecf20Sopenharmony_ci * state changed exactly now while we were called from stop_device. 14158c2ecf20Sopenharmony_ci * This is very unlikely but can happen and is supported. 14168c2ecf20Sopenharmony_ci */ 14178c2ecf20Sopenharmony_ci hw_rfkill = iwl_is_rfkill_set(trans); 14188c2ecf20Sopenharmony_ci if (hw_rfkill) { 14198c2ecf20Sopenharmony_ci set_bit(STATUS_RFKILL_HW, &trans->status); 14208c2ecf20Sopenharmony_ci set_bit(STATUS_RFKILL_OPMODE, &trans->status); 14218c2ecf20Sopenharmony_ci } else { 14228c2ecf20Sopenharmony_ci clear_bit(STATUS_RFKILL_HW, &trans->status); 14238c2ecf20Sopenharmony_ci clear_bit(STATUS_RFKILL_OPMODE, &trans->status); 14248c2ecf20Sopenharmony_ci } 14258c2ecf20Sopenharmony_ci if (hw_rfkill != was_in_rfkill) 14268c2ecf20Sopenharmony_ci iwl_trans_pcie_rf_kill(trans, hw_rfkill); 14278c2ecf20Sopenharmony_ci} 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_cistatic void iwl_trans_pcie_stop_device(struct iwl_trans *trans) 14308c2ecf20Sopenharmony_ci{ 14318c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 14328c2ecf20Sopenharmony_ci bool was_in_rfkill; 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci mutex_lock(&trans_pcie->mutex); 14358c2ecf20Sopenharmony_ci trans_pcie->opmode_down = true; 14368c2ecf20Sopenharmony_ci was_in_rfkill = test_bit(STATUS_RFKILL_OPMODE, &trans->status); 14378c2ecf20Sopenharmony_ci _iwl_trans_pcie_stop_device(trans); 14388c2ecf20Sopenharmony_ci iwl_trans_pcie_handle_stop_rfkill(trans, was_in_rfkill); 14398c2ecf20Sopenharmony_ci mutex_unlock(&trans_pcie->mutex); 14408c2ecf20Sopenharmony_ci} 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_civoid iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state) 14438c2ecf20Sopenharmony_ci{ 14448c2ecf20Sopenharmony_ci struct iwl_trans_pcie __maybe_unused *trans_pcie = 14458c2ecf20Sopenharmony_ci IWL_TRANS_GET_PCIE_TRANS(trans); 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci lockdep_assert_held(&trans_pcie->mutex); 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci IWL_WARN(trans, "reporting RF_KILL (radio %s)\n", 14508c2ecf20Sopenharmony_ci state ? "disabled" : "enabled"); 14518c2ecf20Sopenharmony_ci if (iwl_op_mode_hw_rf_kill(trans->op_mode, state)) { 14528c2ecf20Sopenharmony_ci if (trans->trans_cfg->gen2) 14538c2ecf20Sopenharmony_ci _iwl_trans_pcie_gen2_stop_device(trans); 14548c2ecf20Sopenharmony_ci else 14558c2ecf20Sopenharmony_ci _iwl_trans_pcie_stop_device(trans); 14568c2ecf20Sopenharmony_ci } 14578c2ecf20Sopenharmony_ci} 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_civoid iwl_pcie_d3_complete_suspend(struct iwl_trans *trans, 14608c2ecf20Sopenharmony_ci bool test, bool reset) 14618c2ecf20Sopenharmony_ci{ 14628c2ecf20Sopenharmony_ci iwl_disable_interrupts(trans); 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci /* 14658c2ecf20Sopenharmony_ci * in testing mode, the host stays awake and the 14668c2ecf20Sopenharmony_ci * hardware won't be reset (not even partially) 14678c2ecf20Sopenharmony_ci */ 14688c2ecf20Sopenharmony_ci if (test) 14698c2ecf20Sopenharmony_ci return; 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci iwl_pcie_disable_ict(trans); 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci iwl_pcie_synchronize_irqs(trans); 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci iwl_clear_bit(trans, CSR_GP_CNTRL, 14768c2ecf20Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 14778c2ecf20Sopenharmony_ci iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci if (reset) { 14808c2ecf20Sopenharmony_ci /* 14818c2ecf20Sopenharmony_ci * reset TX queues -- some of their registers reset during S3 14828c2ecf20Sopenharmony_ci * so if we don't reset everything here the D3 image would try 14838c2ecf20Sopenharmony_ci * to execute some invalid memory upon resume 14848c2ecf20Sopenharmony_ci */ 14858c2ecf20Sopenharmony_ci iwl_trans_pcie_tx_reset(trans); 14868c2ecf20Sopenharmony_ci } 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci iwl_pcie_set_pwr(trans, true); 14898c2ecf20Sopenharmony_ci} 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_cistatic int iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test, 14928c2ecf20Sopenharmony_ci bool reset) 14938c2ecf20Sopenharmony_ci{ 14948c2ecf20Sopenharmony_ci int ret; 14958c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci if (!reset) 14988c2ecf20Sopenharmony_ci /* Enable persistence mode to avoid reset */ 14998c2ecf20Sopenharmony_ci iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, 15008c2ecf20Sopenharmony_ci CSR_HW_IF_CONFIG_REG_PERSIST_MODE); 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { 15038c2ecf20Sopenharmony_ci iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6, 15048c2ecf20Sopenharmony_ci UREG_DOORBELL_TO_ISR6_SUSPEND); 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_ci ret = wait_event_timeout(trans_pcie->sx_waitq, 15078c2ecf20Sopenharmony_ci trans_pcie->sx_complete, 2 * HZ); 15088c2ecf20Sopenharmony_ci /* 15098c2ecf20Sopenharmony_ci * Invalidate it toward resume. 15108c2ecf20Sopenharmony_ci */ 15118c2ecf20Sopenharmony_ci trans_pcie->sx_complete = false; 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci if (!ret) { 15148c2ecf20Sopenharmony_ci IWL_ERR(trans, "Timeout entering D3\n"); 15158c2ecf20Sopenharmony_ci return -ETIMEDOUT; 15168c2ecf20Sopenharmony_ci } 15178c2ecf20Sopenharmony_ci } 15188c2ecf20Sopenharmony_ci iwl_pcie_d3_complete_suspend(trans, test, reset); 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci return 0; 15218c2ecf20Sopenharmony_ci} 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_cistatic int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, 15248c2ecf20Sopenharmony_ci enum iwl_d3_status *status, 15258c2ecf20Sopenharmony_ci bool test, bool reset) 15268c2ecf20Sopenharmony_ci{ 15278c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 15288c2ecf20Sopenharmony_ci u32 val; 15298c2ecf20Sopenharmony_ci int ret; 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci if (test) { 15328c2ecf20Sopenharmony_ci iwl_enable_interrupts(trans); 15338c2ecf20Sopenharmony_ci *status = IWL_D3_STATUS_ALIVE; 15348c2ecf20Sopenharmony_ci goto out; 15358c2ecf20Sopenharmony_ci } 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci iwl_set_bit(trans, CSR_GP_CNTRL, 15388c2ecf20Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci ret = iwl_finish_nic_init(trans, trans->trans_cfg); 15418c2ecf20Sopenharmony_ci if (ret) 15428c2ecf20Sopenharmony_ci return ret; 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci /* 15458c2ecf20Sopenharmony_ci * Reconfigure IVAR table in case of MSIX or reset ict table in 15468c2ecf20Sopenharmony_ci * MSI mode since HW reset erased it. 15478c2ecf20Sopenharmony_ci * Also enables interrupts - none will happen as 15488c2ecf20Sopenharmony_ci * the device doesn't know we're waking it up, only when 15498c2ecf20Sopenharmony_ci * the opmode actually tells it after this call. 15508c2ecf20Sopenharmony_ci */ 15518c2ecf20Sopenharmony_ci iwl_pcie_conf_msix_hw(trans_pcie); 15528c2ecf20Sopenharmony_ci if (!trans_pcie->msix_enabled) 15538c2ecf20Sopenharmony_ci iwl_pcie_reset_ict(trans); 15548c2ecf20Sopenharmony_ci iwl_enable_interrupts(trans); 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci iwl_pcie_set_pwr(trans, false); 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci if (!reset) { 15598c2ecf20Sopenharmony_ci iwl_clear_bit(trans, CSR_GP_CNTRL, 15608c2ecf20Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 15618c2ecf20Sopenharmony_ci } else { 15628c2ecf20Sopenharmony_ci iwl_trans_pcie_tx_reset(trans); 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci ret = iwl_pcie_rx_init(trans); 15658c2ecf20Sopenharmony_ci if (ret) { 15668c2ecf20Sopenharmony_ci IWL_ERR(trans, 15678c2ecf20Sopenharmony_ci "Failed to resume the device (RX reset)\n"); 15688c2ecf20Sopenharmony_ci return ret; 15698c2ecf20Sopenharmony_ci } 15708c2ecf20Sopenharmony_ci } 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci IWL_DEBUG_POWER(trans, "WFPM value upon resume = 0x%08X\n", 15738c2ecf20Sopenharmony_ci iwl_read_umac_prph(trans, WFPM_GP2)); 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci val = iwl_read32(trans, CSR_RESET); 15768c2ecf20Sopenharmony_ci if (val & CSR_RESET_REG_FLAG_NEVO_RESET) 15778c2ecf20Sopenharmony_ci *status = IWL_D3_STATUS_RESET; 15788c2ecf20Sopenharmony_ci else 15798c2ecf20Sopenharmony_ci *status = IWL_D3_STATUS_ALIVE; 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ciout: 15828c2ecf20Sopenharmony_ci if (*status == IWL_D3_STATUS_ALIVE && 15838c2ecf20Sopenharmony_ci trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { 15848c2ecf20Sopenharmony_ci trans_pcie->sx_complete = false; 15858c2ecf20Sopenharmony_ci iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6, 15868c2ecf20Sopenharmony_ci UREG_DOORBELL_TO_ISR6_RESUME); 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci ret = wait_event_timeout(trans_pcie->sx_waitq, 15898c2ecf20Sopenharmony_ci trans_pcie->sx_complete, 2 * HZ); 15908c2ecf20Sopenharmony_ci /* 15918c2ecf20Sopenharmony_ci * Invalidate it toward next suspend. 15928c2ecf20Sopenharmony_ci */ 15938c2ecf20Sopenharmony_ci trans_pcie->sx_complete = false; 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci if (!ret) { 15968c2ecf20Sopenharmony_ci IWL_ERR(trans, "Timeout exiting D3\n"); 15978c2ecf20Sopenharmony_ci return -ETIMEDOUT; 15988c2ecf20Sopenharmony_ci } 15998c2ecf20Sopenharmony_ci } 16008c2ecf20Sopenharmony_ci return 0; 16018c2ecf20Sopenharmony_ci} 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_cistatic void 16048c2ecf20Sopenharmony_ciiwl_pcie_set_interrupt_capa(struct pci_dev *pdev, 16058c2ecf20Sopenharmony_ci struct iwl_trans *trans, 16068c2ecf20Sopenharmony_ci const struct iwl_cfg_trans_params *cfg_trans) 16078c2ecf20Sopenharmony_ci{ 16088c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 16098c2ecf20Sopenharmony_ci int max_irqs, num_irqs, i, ret; 16108c2ecf20Sopenharmony_ci u16 pci_cmd; 16118c2ecf20Sopenharmony_ci u32 max_rx_queues = IWL_MAX_RX_HW_QUEUES; 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci if (!cfg_trans->mq_rx_supported) 16148c2ecf20Sopenharmony_ci goto enable_msi; 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci if (cfg_trans->device_family <= IWL_DEVICE_FAMILY_9000) 16178c2ecf20Sopenharmony_ci max_rx_queues = IWL_9000_MAX_RX_HW_QUEUES; 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci max_irqs = min_t(u32, num_online_cpus() + 2, max_rx_queues); 16208c2ecf20Sopenharmony_ci for (i = 0; i < max_irqs; i++) 16218c2ecf20Sopenharmony_ci trans_pcie->msix_entries[i].entry = i; 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci num_irqs = pci_enable_msix_range(pdev, trans_pcie->msix_entries, 16248c2ecf20Sopenharmony_ci MSIX_MIN_INTERRUPT_VECTORS, 16258c2ecf20Sopenharmony_ci max_irqs); 16268c2ecf20Sopenharmony_ci if (num_irqs < 0) { 16278c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(trans, 16288c2ecf20Sopenharmony_ci "Failed to enable msi-x mode (ret %d). Moving to msi mode.\n", 16298c2ecf20Sopenharmony_ci num_irqs); 16308c2ecf20Sopenharmony_ci goto enable_msi; 16318c2ecf20Sopenharmony_ci } 16328c2ecf20Sopenharmony_ci trans_pcie->def_irq = (num_irqs == max_irqs) ? num_irqs - 1 : 0; 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(trans, 16358c2ecf20Sopenharmony_ci "MSI-X enabled. %d interrupt vectors were allocated\n", 16368c2ecf20Sopenharmony_ci num_irqs); 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci /* 16398c2ecf20Sopenharmony_ci * In case the OS provides fewer interrupts than requested, different 16408c2ecf20Sopenharmony_ci * causes will share the same interrupt vector as follows: 16418c2ecf20Sopenharmony_ci * One interrupt less: non rx causes shared with FBQ. 16428c2ecf20Sopenharmony_ci * Two interrupts less: non rx causes shared with FBQ and RSS. 16438c2ecf20Sopenharmony_ci * More than two interrupts: we will use fewer RSS queues. 16448c2ecf20Sopenharmony_ci */ 16458c2ecf20Sopenharmony_ci if (num_irqs <= max_irqs - 2) { 16468c2ecf20Sopenharmony_ci trans_pcie->trans->num_rx_queues = num_irqs + 1; 16478c2ecf20Sopenharmony_ci trans_pcie->shared_vec_mask = IWL_SHARED_IRQ_NON_RX | 16488c2ecf20Sopenharmony_ci IWL_SHARED_IRQ_FIRST_RSS; 16498c2ecf20Sopenharmony_ci } else if (num_irqs == max_irqs - 1) { 16508c2ecf20Sopenharmony_ci trans_pcie->trans->num_rx_queues = num_irqs; 16518c2ecf20Sopenharmony_ci trans_pcie->shared_vec_mask = IWL_SHARED_IRQ_NON_RX; 16528c2ecf20Sopenharmony_ci } else { 16538c2ecf20Sopenharmony_ci trans_pcie->trans->num_rx_queues = num_irqs - 1; 16548c2ecf20Sopenharmony_ci } 16558c2ecf20Sopenharmony_ci WARN_ON(trans_pcie->trans->num_rx_queues > IWL_MAX_RX_HW_QUEUES); 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci trans_pcie->alloc_vecs = num_irqs; 16588c2ecf20Sopenharmony_ci trans_pcie->msix_enabled = true; 16598c2ecf20Sopenharmony_ci return; 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_cienable_msi: 16628c2ecf20Sopenharmony_ci ret = pci_enable_msi(pdev); 16638c2ecf20Sopenharmony_ci if (ret) { 16648c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "pci_enable_msi failed - %d\n", ret); 16658c2ecf20Sopenharmony_ci /* enable rfkill interrupt: hw bug w/a */ 16668c2ecf20Sopenharmony_ci pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd); 16678c2ecf20Sopenharmony_ci if (pci_cmd & PCI_COMMAND_INTX_DISABLE) { 16688c2ecf20Sopenharmony_ci pci_cmd &= ~PCI_COMMAND_INTX_DISABLE; 16698c2ecf20Sopenharmony_ci pci_write_config_word(pdev, PCI_COMMAND, pci_cmd); 16708c2ecf20Sopenharmony_ci } 16718c2ecf20Sopenharmony_ci } 16728c2ecf20Sopenharmony_ci} 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_cistatic void iwl_pcie_irq_set_affinity(struct iwl_trans *trans) 16758c2ecf20Sopenharmony_ci{ 16768c2ecf20Sopenharmony_ci int iter_rx_q, i, ret, cpu, offset; 16778c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 16788c2ecf20Sopenharmony_ci 16798c2ecf20Sopenharmony_ci i = trans_pcie->shared_vec_mask & IWL_SHARED_IRQ_FIRST_RSS ? 0 : 1; 16808c2ecf20Sopenharmony_ci iter_rx_q = trans_pcie->trans->num_rx_queues - 1 + i; 16818c2ecf20Sopenharmony_ci offset = 1 + i; 16828c2ecf20Sopenharmony_ci for (; i < iter_rx_q ; i++) { 16838c2ecf20Sopenharmony_ci /* 16848c2ecf20Sopenharmony_ci * Get the cpu prior to the place to search 16858c2ecf20Sopenharmony_ci * (i.e. return will be > i - 1). 16868c2ecf20Sopenharmony_ci */ 16878c2ecf20Sopenharmony_ci cpu = cpumask_next(i - offset, cpu_online_mask); 16888c2ecf20Sopenharmony_ci cpumask_set_cpu(cpu, &trans_pcie->affinity_mask[i]); 16898c2ecf20Sopenharmony_ci ret = irq_set_affinity_hint(trans_pcie->msix_entries[i].vector, 16908c2ecf20Sopenharmony_ci &trans_pcie->affinity_mask[i]); 16918c2ecf20Sopenharmony_ci if (ret) 16928c2ecf20Sopenharmony_ci IWL_ERR(trans_pcie->trans, 16938c2ecf20Sopenharmony_ci "Failed to set affinity mask for IRQ %d\n", 16948c2ecf20Sopenharmony_ci i); 16958c2ecf20Sopenharmony_ci } 16968c2ecf20Sopenharmony_ci} 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_cistatic int iwl_pcie_init_msix_handler(struct pci_dev *pdev, 16998c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie) 17008c2ecf20Sopenharmony_ci{ 17018c2ecf20Sopenharmony_ci int i; 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci for (i = 0; i < trans_pcie->alloc_vecs; i++) { 17048c2ecf20Sopenharmony_ci int ret; 17058c2ecf20Sopenharmony_ci struct msix_entry *msix_entry; 17068c2ecf20Sopenharmony_ci const char *qname = queue_name(&pdev->dev, trans_pcie, i); 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci if (!qname) 17098c2ecf20Sopenharmony_ci return -ENOMEM; 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci msix_entry = &trans_pcie->msix_entries[i]; 17128c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, 17138c2ecf20Sopenharmony_ci msix_entry->vector, 17148c2ecf20Sopenharmony_ci iwl_pcie_msix_isr, 17158c2ecf20Sopenharmony_ci (i == trans_pcie->def_irq) ? 17168c2ecf20Sopenharmony_ci iwl_pcie_irq_msix_handler : 17178c2ecf20Sopenharmony_ci iwl_pcie_irq_rx_msix_handler, 17188c2ecf20Sopenharmony_ci IRQF_SHARED, 17198c2ecf20Sopenharmony_ci qname, 17208c2ecf20Sopenharmony_ci msix_entry); 17218c2ecf20Sopenharmony_ci if (ret) { 17228c2ecf20Sopenharmony_ci IWL_ERR(trans_pcie->trans, 17238c2ecf20Sopenharmony_ci "Error allocating IRQ %d\n", i); 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci return ret; 17268c2ecf20Sopenharmony_ci } 17278c2ecf20Sopenharmony_ci } 17288c2ecf20Sopenharmony_ci iwl_pcie_irq_set_affinity(trans_pcie->trans); 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci return 0; 17318c2ecf20Sopenharmony_ci} 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_cistatic int iwl_trans_pcie_clear_persistence_bit(struct iwl_trans *trans) 17348c2ecf20Sopenharmony_ci{ 17358c2ecf20Sopenharmony_ci u32 hpm, wprot; 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci switch (trans->trans_cfg->device_family) { 17388c2ecf20Sopenharmony_ci case IWL_DEVICE_FAMILY_9000: 17398c2ecf20Sopenharmony_ci wprot = PREG_PRPH_WPROT_9000; 17408c2ecf20Sopenharmony_ci break; 17418c2ecf20Sopenharmony_ci case IWL_DEVICE_FAMILY_22000: 17428c2ecf20Sopenharmony_ci wprot = PREG_PRPH_WPROT_22000; 17438c2ecf20Sopenharmony_ci break; 17448c2ecf20Sopenharmony_ci default: 17458c2ecf20Sopenharmony_ci return 0; 17468c2ecf20Sopenharmony_ci } 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_ci hpm = iwl_read_umac_prph_no_grab(trans, HPM_DEBUG); 17498c2ecf20Sopenharmony_ci if (hpm != 0xa5a5a5a0 && (hpm & PERSISTENCE_BIT)) { 17508c2ecf20Sopenharmony_ci u32 wprot_val = iwl_read_umac_prph_no_grab(trans, wprot); 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci if (wprot_val & PREG_WFPM_ACCESS) { 17538c2ecf20Sopenharmony_ci IWL_ERR(trans, 17548c2ecf20Sopenharmony_ci "Error, can not clear persistence bit\n"); 17558c2ecf20Sopenharmony_ci return -EPERM; 17568c2ecf20Sopenharmony_ci } 17578c2ecf20Sopenharmony_ci iwl_write_umac_prph_no_grab(trans, HPM_DEBUG, 17588c2ecf20Sopenharmony_ci hpm & ~PERSISTENCE_BIT); 17598c2ecf20Sopenharmony_ci } 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci return 0; 17628c2ecf20Sopenharmony_ci} 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_cistatic int iwl_pcie_gen2_force_power_gating(struct iwl_trans *trans) 17658c2ecf20Sopenharmony_ci{ 17668c2ecf20Sopenharmony_ci int ret; 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_ci ret = iwl_finish_nic_init(trans, trans->trans_cfg); 17698c2ecf20Sopenharmony_ci if (ret < 0) 17708c2ecf20Sopenharmony_ci return ret; 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci iwl_set_bits_prph(trans, HPM_HIPM_GEN_CFG, 17738c2ecf20Sopenharmony_ci HPM_HIPM_GEN_CFG_CR_FORCE_ACTIVE); 17748c2ecf20Sopenharmony_ci udelay(20); 17758c2ecf20Sopenharmony_ci iwl_set_bits_prph(trans, HPM_HIPM_GEN_CFG, 17768c2ecf20Sopenharmony_ci HPM_HIPM_GEN_CFG_CR_PG_EN | 17778c2ecf20Sopenharmony_ci HPM_HIPM_GEN_CFG_CR_SLP_EN); 17788c2ecf20Sopenharmony_ci udelay(20); 17798c2ecf20Sopenharmony_ci iwl_clear_bits_prph(trans, HPM_HIPM_GEN_CFG, 17808c2ecf20Sopenharmony_ci HPM_HIPM_GEN_CFG_CR_FORCE_ACTIVE); 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_ci iwl_trans_pcie_sw_reset(trans); 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci return 0; 17858c2ecf20Sopenharmony_ci} 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_cistatic int _iwl_trans_pcie_start_hw(struct iwl_trans *trans) 17888c2ecf20Sopenharmony_ci{ 17898c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 17908c2ecf20Sopenharmony_ci int err; 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci lockdep_assert_held(&trans_pcie->mutex); 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_ci err = iwl_pcie_prepare_card_hw(trans); 17958c2ecf20Sopenharmony_ci if (err) { 17968c2ecf20Sopenharmony_ci IWL_ERR(trans, "Error while preparing HW: %d\n", err); 17978c2ecf20Sopenharmony_ci return err; 17988c2ecf20Sopenharmony_ci } 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci err = iwl_trans_pcie_clear_persistence_bit(trans); 18018c2ecf20Sopenharmony_ci if (err) 18028c2ecf20Sopenharmony_ci return err; 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci iwl_trans_pcie_sw_reset(trans); 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_22000 && 18078c2ecf20Sopenharmony_ci trans->trans_cfg->integrated) { 18088c2ecf20Sopenharmony_ci err = iwl_pcie_gen2_force_power_gating(trans); 18098c2ecf20Sopenharmony_ci if (err) 18108c2ecf20Sopenharmony_ci return err; 18118c2ecf20Sopenharmony_ci } 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci err = iwl_pcie_apm_init(trans); 18148c2ecf20Sopenharmony_ci if (err) 18158c2ecf20Sopenharmony_ci return err; 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_ci iwl_pcie_init_msix(trans_pcie); 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci /* From now on, the op_mode will be kept updated about RF kill state */ 18208c2ecf20Sopenharmony_ci iwl_enable_rfkill_int(trans); 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci trans_pcie->opmode_down = false; 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci /* Set is_down to false here so that...*/ 18258c2ecf20Sopenharmony_ci trans_pcie->is_down = false; 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci /* ...rfkill can call stop_device and set it false if needed */ 18288c2ecf20Sopenharmony_ci iwl_pcie_check_hw_rf_kill(trans); 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci return 0; 18318c2ecf20Sopenharmony_ci} 18328c2ecf20Sopenharmony_ci 18338c2ecf20Sopenharmony_cistatic int iwl_trans_pcie_start_hw(struct iwl_trans *trans) 18348c2ecf20Sopenharmony_ci{ 18358c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 18368c2ecf20Sopenharmony_ci int ret; 18378c2ecf20Sopenharmony_ci 18388c2ecf20Sopenharmony_ci mutex_lock(&trans_pcie->mutex); 18398c2ecf20Sopenharmony_ci ret = _iwl_trans_pcie_start_hw(trans); 18408c2ecf20Sopenharmony_ci mutex_unlock(&trans_pcie->mutex); 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci return ret; 18438c2ecf20Sopenharmony_ci} 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_cistatic void iwl_trans_pcie_op_mode_leave(struct iwl_trans *trans) 18468c2ecf20Sopenharmony_ci{ 18478c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci mutex_lock(&trans_pcie->mutex); 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci /* disable interrupts - don't enable HW RF kill interrupt */ 18528c2ecf20Sopenharmony_ci iwl_disable_interrupts(trans); 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_ci iwl_pcie_apm_stop(trans, true); 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci iwl_disable_interrupts(trans); 18578c2ecf20Sopenharmony_ci 18588c2ecf20Sopenharmony_ci iwl_pcie_disable_ict(trans); 18598c2ecf20Sopenharmony_ci 18608c2ecf20Sopenharmony_ci mutex_unlock(&trans_pcie->mutex); 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci iwl_pcie_synchronize_irqs(trans); 18638c2ecf20Sopenharmony_ci} 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_cistatic void iwl_trans_pcie_write8(struct iwl_trans *trans, u32 ofs, u8 val) 18668c2ecf20Sopenharmony_ci{ 18678c2ecf20Sopenharmony_ci writeb(val, IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs); 18688c2ecf20Sopenharmony_ci} 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_cistatic void iwl_trans_pcie_write32(struct iwl_trans *trans, u32 ofs, u32 val) 18718c2ecf20Sopenharmony_ci{ 18728c2ecf20Sopenharmony_ci writel(val, IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs); 18738c2ecf20Sopenharmony_ci} 18748c2ecf20Sopenharmony_ci 18758c2ecf20Sopenharmony_cistatic u32 iwl_trans_pcie_read32(struct iwl_trans *trans, u32 ofs) 18768c2ecf20Sopenharmony_ci{ 18778c2ecf20Sopenharmony_ci return readl(IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs); 18788c2ecf20Sopenharmony_ci} 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_cistatic u32 iwl_trans_pcie_prph_msk(struct iwl_trans *trans) 18818c2ecf20Sopenharmony_ci{ 18828c2ecf20Sopenharmony_ci if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) 18838c2ecf20Sopenharmony_ci return 0x00FFFFFF; 18848c2ecf20Sopenharmony_ci else 18858c2ecf20Sopenharmony_ci return 0x000FFFFF; 18868c2ecf20Sopenharmony_ci} 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_cistatic u32 iwl_trans_pcie_read_prph(struct iwl_trans *trans, u32 reg) 18898c2ecf20Sopenharmony_ci{ 18908c2ecf20Sopenharmony_ci u32 mask = iwl_trans_pcie_prph_msk(trans); 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_ci iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_RADDR, 18938c2ecf20Sopenharmony_ci ((reg & mask) | (3 << 24))); 18948c2ecf20Sopenharmony_ci return iwl_trans_pcie_read32(trans, HBUS_TARG_PRPH_RDAT); 18958c2ecf20Sopenharmony_ci} 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_cistatic void iwl_trans_pcie_write_prph(struct iwl_trans *trans, u32 addr, 18988c2ecf20Sopenharmony_ci u32 val) 18998c2ecf20Sopenharmony_ci{ 19008c2ecf20Sopenharmony_ci u32 mask = iwl_trans_pcie_prph_msk(trans); 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_ci iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_WADDR, 19038c2ecf20Sopenharmony_ci ((addr & mask) | (3 << 24))); 19048c2ecf20Sopenharmony_ci iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_WDAT, val); 19058c2ecf20Sopenharmony_ci} 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_cistatic void iwl_trans_pcie_configure(struct iwl_trans *trans, 19088c2ecf20Sopenharmony_ci const struct iwl_trans_config *trans_cfg) 19098c2ecf20Sopenharmony_ci{ 19108c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci /* free all first - we might be reconfigured for a different size */ 19138c2ecf20Sopenharmony_ci iwl_pcie_free_rbs_pool(trans); 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci trans->txqs.cmd.q_id = trans_cfg->cmd_queue; 19168c2ecf20Sopenharmony_ci trans->txqs.cmd.fifo = trans_cfg->cmd_fifo; 19178c2ecf20Sopenharmony_ci trans->txqs.cmd.wdg_timeout = trans_cfg->cmd_q_wdg_timeout; 19188c2ecf20Sopenharmony_ci trans->txqs.page_offs = trans_cfg->cb_data_offs; 19198c2ecf20Sopenharmony_ci trans->txqs.dev_cmd_offs = trans_cfg->cb_data_offs + sizeof(void *); 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci if (WARN_ON(trans_cfg->n_no_reclaim_cmds > MAX_NO_RECLAIM_CMDS)) 19228c2ecf20Sopenharmony_ci trans_pcie->n_no_reclaim_cmds = 0; 19238c2ecf20Sopenharmony_ci else 19248c2ecf20Sopenharmony_ci trans_pcie->n_no_reclaim_cmds = trans_cfg->n_no_reclaim_cmds; 19258c2ecf20Sopenharmony_ci if (trans_pcie->n_no_reclaim_cmds) 19268c2ecf20Sopenharmony_ci memcpy(trans_pcie->no_reclaim_cmds, trans_cfg->no_reclaim_cmds, 19278c2ecf20Sopenharmony_ci trans_pcie->n_no_reclaim_cmds * sizeof(u8)); 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_ci trans_pcie->rx_buf_size = trans_cfg->rx_buf_size; 19308c2ecf20Sopenharmony_ci trans_pcie->rx_page_order = 19318c2ecf20Sopenharmony_ci iwl_trans_get_rb_size_order(trans_pcie->rx_buf_size); 19328c2ecf20Sopenharmony_ci trans_pcie->rx_buf_bytes = 19338c2ecf20Sopenharmony_ci iwl_trans_get_rb_size(trans_pcie->rx_buf_size); 19348c2ecf20Sopenharmony_ci trans_pcie->supported_dma_mask = DMA_BIT_MASK(12); 19358c2ecf20Sopenharmony_ci if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) 19368c2ecf20Sopenharmony_ci trans_pcie->supported_dma_mask = DMA_BIT_MASK(11); 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_ci trans->txqs.bc_table_dword = trans_cfg->bc_table_dword; 19398c2ecf20Sopenharmony_ci trans_pcie->scd_set_active = trans_cfg->scd_set_active; 19408c2ecf20Sopenharmony_ci trans_pcie->sw_csum_tx = trans_cfg->sw_csum_tx; 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci trans->command_groups = trans_cfg->command_groups; 19438c2ecf20Sopenharmony_ci trans->command_groups_size = trans_cfg->command_groups_size; 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_ci /* Initialize NAPI here - it should be before registering to mac80211 19468c2ecf20Sopenharmony_ci * in the opmode but after the HW struct is allocated. 19478c2ecf20Sopenharmony_ci * As this function may be called again in some corner cases don't 19488c2ecf20Sopenharmony_ci * do anything if NAPI was already initialized. 19498c2ecf20Sopenharmony_ci */ 19508c2ecf20Sopenharmony_ci if (trans_pcie->napi_dev.reg_state != NETREG_DUMMY) 19518c2ecf20Sopenharmony_ci init_dummy_netdev(&trans_pcie->napi_dev); 19528c2ecf20Sopenharmony_ci} 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_civoid iwl_trans_pcie_free(struct iwl_trans *trans) 19558c2ecf20Sopenharmony_ci{ 19568c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 19578c2ecf20Sopenharmony_ci int i; 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_ci iwl_pcie_synchronize_irqs(trans); 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_ci if (trans->trans_cfg->gen2) 19628c2ecf20Sopenharmony_ci iwl_txq_gen2_tx_free(trans); 19638c2ecf20Sopenharmony_ci else 19648c2ecf20Sopenharmony_ci iwl_pcie_tx_free(trans); 19658c2ecf20Sopenharmony_ci iwl_pcie_rx_free(trans); 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_ci if (trans_pcie->rba.alloc_wq) { 19688c2ecf20Sopenharmony_ci destroy_workqueue(trans_pcie->rba.alloc_wq); 19698c2ecf20Sopenharmony_ci trans_pcie->rba.alloc_wq = NULL; 19708c2ecf20Sopenharmony_ci } 19718c2ecf20Sopenharmony_ci 19728c2ecf20Sopenharmony_ci if (trans_pcie->msix_enabled) { 19738c2ecf20Sopenharmony_ci for (i = 0; i < trans_pcie->alloc_vecs; i++) { 19748c2ecf20Sopenharmony_ci irq_set_affinity_hint( 19758c2ecf20Sopenharmony_ci trans_pcie->msix_entries[i].vector, 19768c2ecf20Sopenharmony_ci NULL); 19778c2ecf20Sopenharmony_ci } 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci trans_pcie->msix_enabled = false; 19808c2ecf20Sopenharmony_ci } else { 19818c2ecf20Sopenharmony_ci iwl_pcie_free_ict(trans); 19828c2ecf20Sopenharmony_ci } 19838c2ecf20Sopenharmony_ci 19848c2ecf20Sopenharmony_ci iwl_pcie_free_fw_monitor(trans); 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci if (trans_pcie->pnvm_dram.size) 19878c2ecf20Sopenharmony_ci dma_free_coherent(trans->dev, trans_pcie->pnvm_dram.size, 19888c2ecf20Sopenharmony_ci trans_pcie->pnvm_dram.block, 19898c2ecf20Sopenharmony_ci trans_pcie->pnvm_dram.physical); 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci mutex_destroy(&trans_pcie->mutex); 19928c2ecf20Sopenharmony_ci iwl_trans_free(trans); 19938c2ecf20Sopenharmony_ci} 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_cistatic void iwl_trans_pcie_set_pmi(struct iwl_trans *trans, bool state) 19968c2ecf20Sopenharmony_ci{ 19978c2ecf20Sopenharmony_ci if (state) 19988c2ecf20Sopenharmony_ci set_bit(STATUS_TPOWER_PMI, &trans->status); 19998c2ecf20Sopenharmony_ci else 20008c2ecf20Sopenharmony_ci clear_bit(STATUS_TPOWER_PMI, &trans->status); 20018c2ecf20Sopenharmony_ci} 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_cistruct iwl_trans_pcie_removal { 20048c2ecf20Sopenharmony_ci struct pci_dev *pdev; 20058c2ecf20Sopenharmony_ci struct work_struct work; 20068c2ecf20Sopenharmony_ci}; 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_cistatic void iwl_trans_pcie_removal_wk(struct work_struct *wk) 20098c2ecf20Sopenharmony_ci{ 20108c2ecf20Sopenharmony_ci struct iwl_trans_pcie_removal *removal = 20118c2ecf20Sopenharmony_ci container_of(wk, struct iwl_trans_pcie_removal, work); 20128c2ecf20Sopenharmony_ci struct pci_dev *pdev = removal->pdev; 20138c2ecf20Sopenharmony_ci static char *prop[] = {"EVENT=INACCESSIBLE", NULL}; 20148c2ecf20Sopenharmony_ci 20158c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Device gone - attempting removal\n"); 20168c2ecf20Sopenharmony_ci kobject_uevent_env(&pdev->dev.kobj, KOBJ_CHANGE, prop); 20178c2ecf20Sopenharmony_ci pci_lock_rescan_remove(); 20188c2ecf20Sopenharmony_ci pci_dev_put(pdev); 20198c2ecf20Sopenharmony_ci pci_stop_and_remove_bus_device(pdev); 20208c2ecf20Sopenharmony_ci pci_unlock_rescan_remove(); 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci kfree(removal); 20238c2ecf20Sopenharmony_ci module_put(THIS_MODULE); 20248c2ecf20Sopenharmony_ci} 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_cistatic bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, 20278c2ecf20Sopenharmony_ci unsigned long *flags) 20288c2ecf20Sopenharmony_ci{ 20298c2ecf20Sopenharmony_ci int ret; 20308c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_ci spin_lock_bh(&trans_pcie->reg_lock); 20338c2ecf20Sopenharmony_ci 20348c2ecf20Sopenharmony_ci if (trans_pcie->cmd_hold_nic_awake) 20358c2ecf20Sopenharmony_ci goto out; 20368c2ecf20Sopenharmony_ci 20378c2ecf20Sopenharmony_ci /* this bit wakes up the NIC */ 20388c2ecf20Sopenharmony_ci __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL, 20398c2ecf20Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 20408c2ecf20Sopenharmony_ci if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_8000) 20418c2ecf20Sopenharmony_ci udelay(2); 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci /* 20448c2ecf20Sopenharmony_ci * These bits say the device is running, and should keep running for 20458c2ecf20Sopenharmony_ci * at least a short while (at least as long as MAC_ACCESS_REQ stays 1), 20468c2ecf20Sopenharmony_ci * but they do not indicate that embedded SRAM is restored yet; 20478c2ecf20Sopenharmony_ci * HW with volatile SRAM must save/restore contents to/from 20488c2ecf20Sopenharmony_ci * host DRAM when sleeping/waking for power-saving. 20498c2ecf20Sopenharmony_ci * Each direction takes approximately 1/4 millisecond; with this 20508c2ecf20Sopenharmony_ci * overhead, it's a good idea to grab and hold MAC_ACCESS_REQUEST if a 20518c2ecf20Sopenharmony_ci * series of register accesses are expected (e.g. reading Event Log), 20528c2ecf20Sopenharmony_ci * to keep device from sleeping. 20538c2ecf20Sopenharmony_ci * 20548c2ecf20Sopenharmony_ci * CSR_UCODE_DRV_GP1 register bit MAC_SLEEP == 0 indicates that 20558c2ecf20Sopenharmony_ci * SRAM is okay/restored. We don't check that here because this call 20568c2ecf20Sopenharmony_ci * is just for hardware register access; but GP1 MAC_SLEEP 20578c2ecf20Sopenharmony_ci * check is a good idea before accessing the SRAM of HW with 20588c2ecf20Sopenharmony_ci * volatile SRAM (e.g. reading Event Log). 20598c2ecf20Sopenharmony_ci * 20608c2ecf20Sopenharmony_ci * 5000 series and later (including 1000 series) have non-volatile SRAM, 20618c2ecf20Sopenharmony_ci * and do not save/restore SRAM when power cycling. 20628c2ecf20Sopenharmony_ci */ 20638c2ecf20Sopenharmony_ci ret = iwl_poll_bit(trans, CSR_GP_CNTRL, 20648c2ecf20Sopenharmony_ci CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN, 20658c2ecf20Sopenharmony_ci (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY | 20668c2ecf20Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000); 20678c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) { 20688c2ecf20Sopenharmony_ci u32 cntrl = iwl_read32(trans, CSR_GP_CNTRL); 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_ci WARN_ONCE(1, 20718c2ecf20Sopenharmony_ci "Timeout waiting for hardware access (CSR_GP_CNTRL 0x%08x)\n", 20728c2ecf20Sopenharmony_ci cntrl); 20738c2ecf20Sopenharmony_ci 20748c2ecf20Sopenharmony_ci iwl_trans_pcie_dump_regs(trans); 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_ci if (iwlwifi_mod_params.remove_when_gone && cntrl == ~0U) { 20778c2ecf20Sopenharmony_ci struct iwl_trans_pcie_removal *removal; 20788c2ecf20Sopenharmony_ci 20798c2ecf20Sopenharmony_ci if (test_bit(STATUS_TRANS_DEAD, &trans->status)) 20808c2ecf20Sopenharmony_ci goto err; 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ci IWL_ERR(trans, "Device gone - scheduling removal!\n"); 20838c2ecf20Sopenharmony_ci 20848c2ecf20Sopenharmony_ci /* 20858c2ecf20Sopenharmony_ci * get a module reference to avoid doing this 20868c2ecf20Sopenharmony_ci * while unloading anyway and to avoid 20878c2ecf20Sopenharmony_ci * scheduling a work with code that's being 20888c2ecf20Sopenharmony_ci * removed. 20898c2ecf20Sopenharmony_ci */ 20908c2ecf20Sopenharmony_ci if (!try_module_get(THIS_MODULE)) { 20918c2ecf20Sopenharmony_ci IWL_ERR(trans, 20928c2ecf20Sopenharmony_ci "Module is being unloaded - abort\n"); 20938c2ecf20Sopenharmony_ci goto err; 20948c2ecf20Sopenharmony_ci } 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_ci removal = kzalloc(sizeof(*removal), GFP_ATOMIC); 20978c2ecf20Sopenharmony_ci if (!removal) { 20988c2ecf20Sopenharmony_ci module_put(THIS_MODULE); 20998c2ecf20Sopenharmony_ci goto err; 21008c2ecf20Sopenharmony_ci } 21018c2ecf20Sopenharmony_ci /* 21028c2ecf20Sopenharmony_ci * we don't need to clear this flag, because 21038c2ecf20Sopenharmony_ci * the trans will be freed and reallocated. 21048c2ecf20Sopenharmony_ci */ 21058c2ecf20Sopenharmony_ci set_bit(STATUS_TRANS_DEAD, &trans->status); 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_ci removal->pdev = to_pci_dev(trans->dev); 21088c2ecf20Sopenharmony_ci INIT_WORK(&removal->work, iwl_trans_pcie_removal_wk); 21098c2ecf20Sopenharmony_ci pci_dev_get(removal->pdev); 21108c2ecf20Sopenharmony_ci schedule_work(&removal->work); 21118c2ecf20Sopenharmony_ci } else { 21128c2ecf20Sopenharmony_ci iwl_write32(trans, CSR_RESET, 21138c2ecf20Sopenharmony_ci CSR_RESET_REG_FLAG_FORCE_NMI); 21148c2ecf20Sopenharmony_ci } 21158c2ecf20Sopenharmony_ci 21168c2ecf20Sopenharmony_cierr: 21178c2ecf20Sopenharmony_ci spin_unlock_bh(&trans_pcie->reg_lock); 21188c2ecf20Sopenharmony_ci return false; 21198c2ecf20Sopenharmony_ci } 21208c2ecf20Sopenharmony_ci 21218c2ecf20Sopenharmony_ciout: 21228c2ecf20Sopenharmony_ci /* 21238c2ecf20Sopenharmony_ci * Fool sparse by faking we release the lock - sparse will 21248c2ecf20Sopenharmony_ci * track nic_access anyway. 21258c2ecf20Sopenharmony_ci */ 21268c2ecf20Sopenharmony_ci __release(&trans_pcie->reg_lock); 21278c2ecf20Sopenharmony_ci return true; 21288c2ecf20Sopenharmony_ci} 21298c2ecf20Sopenharmony_ci 21308c2ecf20Sopenharmony_cistatic void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans, 21318c2ecf20Sopenharmony_ci unsigned long *flags) 21328c2ecf20Sopenharmony_ci{ 21338c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci lockdep_assert_held(&trans_pcie->reg_lock); 21368c2ecf20Sopenharmony_ci 21378c2ecf20Sopenharmony_ci /* 21388c2ecf20Sopenharmony_ci * Fool sparse by faking we acquiring the lock - sparse will 21398c2ecf20Sopenharmony_ci * track nic_access anyway. 21408c2ecf20Sopenharmony_ci */ 21418c2ecf20Sopenharmony_ci __acquire(&trans_pcie->reg_lock); 21428c2ecf20Sopenharmony_ci 21438c2ecf20Sopenharmony_ci if (trans_pcie->cmd_hold_nic_awake) 21448c2ecf20Sopenharmony_ci goto out; 21458c2ecf20Sopenharmony_ci 21468c2ecf20Sopenharmony_ci __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, 21478c2ecf20Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 21488c2ecf20Sopenharmony_ci /* 21498c2ecf20Sopenharmony_ci * Above we read the CSR_GP_CNTRL register, which will flush 21508c2ecf20Sopenharmony_ci * any previous writes, but we need the write that clears the 21518c2ecf20Sopenharmony_ci * MAC_ACCESS_REQ bit to be performed before any other writes 21528c2ecf20Sopenharmony_ci * scheduled on different CPUs (after we drop reg_lock). 21538c2ecf20Sopenharmony_ci */ 21548c2ecf20Sopenharmony_ciout: 21558c2ecf20Sopenharmony_ci spin_unlock_bh(&trans_pcie->reg_lock); 21568c2ecf20Sopenharmony_ci} 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_cistatic int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr, 21598c2ecf20Sopenharmony_ci void *buf, int dwords) 21608c2ecf20Sopenharmony_ci{ 21618c2ecf20Sopenharmony_ci unsigned long flags; 21628c2ecf20Sopenharmony_ci int offs = 0; 21638c2ecf20Sopenharmony_ci u32 *vals = buf; 21648c2ecf20Sopenharmony_ci 21658c2ecf20Sopenharmony_ci while (offs < dwords) { 21668c2ecf20Sopenharmony_ci /* limit the time we spin here under lock to 1/2s */ 21678c2ecf20Sopenharmony_ci unsigned long end = jiffies + HZ / 2; 21688c2ecf20Sopenharmony_ci bool resched = false; 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_ci if (iwl_trans_grab_nic_access(trans, &flags)) { 21718c2ecf20Sopenharmony_ci iwl_write32(trans, HBUS_TARG_MEM_RADDR, 21728c2ecf20Sopenharmony_ci addr + 4 * offs); 21738c2ecf20Sopenharmony_ci 21748c2ecf20Sopenharmony_ci while (offs < dwords) { 21758c2ecf20Sopenharmony_ci vals[offs] = iwl_read32(trans, 21768c2ecf20Sopenharmony_ci HBUS_TARG_MEM_RDAT); 21778c2ecf20Sopenharmony_ci offs++; 21788c2ecf20Sopenharmony_ci 21798c2ecf20Sopenharmony_ci if (time_after(jiffies, end)) { 21808c2ecf20Sopenharmony_ci resched = true; 21818c2ecf20Sopenharmony_ci break; 21828c2ecf20Sopenharmony_ci } 21838c2ecf20Sopenharmony_ci } 21848c2ecf20Sopenharmony_ci iwl_trans_release_nic_access(trans, &flags); 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_ci if (resched) 21878c2ecf20Sopenharmony_ci cond_resched(); 21888c2ecf20Sopenharmony_ci } else { 21898c2ecf20Sopenharmony_ci return -EBUSY; 21908c2ecf20Sopenharmony_ci } 21918c2ecf20Sopenharmony_ci } 21928c2ecf20Sopenharmony_ci 21938c2ecf20Sopenharmony_ci return 0; 21948c2ecf20Sopenharmony_ci} 21958c2ecf20Sopenharmony_ci 21968c2ecf20Sopenharmony_cistatic int iwl_trans_pcie_write_mem(struct iwl_trans *trans, u32 addr, 21978c2ecf20Sopenharmony_ci const void *buf, int dwords) 21988c2ecf20Sopenharmony_ci{ 21998c2ecf20Sopenharmony_ci unsigned long flags; 22008c2ecf20Sopenharmony_ci int offs, ret = 0; 22018c2ecf20Sopenharmony_ci const u32 *vals = buf; 22028c2ecf20Sopenharmony_ci 22038c2ecf20Sopenharmony_ci if (iwl_trans_grab_nic_access(trans, &flags)) { 22048c2ecf20Sopenharmony_ci iwl_write32(trans, HBUS_TARG_MEM_WADDR, addr); 22058c2ecf20Sopenharmony_ci for (offs = 0; offs < dwords; offs++) 22068c2ecf20Sopenharmony_ci iwl_write32(trans, HBUS_TARG_MEM_WDAT, 22078c2ecf20Sopenharmony_ci vals ? vals[offs] : 0); 22088c2ecf20Sopenharmony_ci iwl_trans_release_nic_access(trans, &flags); 22098c2ecf20Sopenharmony_ci } else { 22108c2ecf20Sopenharmony_ci ret = -EBUSY; 22118c2ecf20Sopenharmony_ci } 22128c2ecf20Sopenharmony_ci return ret; 22138c2ecf20Sopenharmony_ci} 22148c2ecf20Sopenharmony_ci 22158c2ecf20Sopenharmony_cistatic int iwl_trans_pcie_read_config32(struct iwl_trans *trans, u32 ofs, 22168c2ecf20Sopenharmony_ci u32 *val) 22178c2ecf20Sopenharmony_ci{ 22188c2ecf20Sopenharmony_ci return pci_read_config_dword(IWL_TRANS_GET_PCIE_TRANS(trans)->pci_dev, 22198c2ecf20Sopenharmony_ci ofs, val); 22208c2ecf20Sopenharmony_ci} 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_cistatic void iwl_trans_pcie_freeze_txq_timer(struct iwl_trans *trans, 22238c2ecf20Sopenharmony_ci unsigned long txqs, 22248c2ecf20Sopenharmony_ci bool freeze) 22258c2ecf20Sopenharmony_ci{ 22268c2ecf20Sopenharmony_ci int queue; 22278c2ecf20Sopenharmony_ci 22288c2ecf20Sopenharmony_ci for_each_set_bit(queue, &txqs, BITS_PER_LONG) { 22298c2ecf20Sopenharmony_ci struct iwl_txq *txq = trans->txqs.txq[queue]; 22308c2ecf20Sopenharmony_ci unsigned long now; 22318c2ecf20Sopenharmony_ci 22328c2ecf20Sopenharmony_ci spin_lock_bh(&txq->lock); 22338c2ecf20Sopenharmony_ci 22348c2ecf20Sopenharmony_ci now = jiffies; 22358c2ecf20Sopenharmony_ci 22368c2ecf20Sopenharmony_ci if (txq->frozen == freeze) 22378c2ecf20Sopenharmony_ci goto next_queue; 22388c2ecf20Sopenharmony_ci 22398c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(trans, "%s TXQ %d\n", 22408c2ecf20Sopenharmony_ci freeze ? "Freezing" : "Waking", queue); 22418c2ecf20Sopenharmony_ci 22428c2ecf20Sopenharmony_ci txq->frozen = freeze; 22438c2ecf20Sopenharmony_ci 22448c2ecf20Sopenharmony_ci if (txq->read_ptr == txq->write_ptr) 22458c2ecf20Sopenharmony_ci goto next_queue; 22468c2ecf20Sopenharmony_ci 22478c2ecf20Sopenharmony_ci if (freeze) { 22488c2ecf20Sopenharmony_ci if (unlikely(time_after(now, 22498c2ecf20Sopenharmony_ci txq->stuck_timer.expires))) { 22508c2ecf20Sopenharmony_ci /* 22518c2ecf20Sopenharmony_ci * The timer should have fired, maybe it is 22528c2ecf20Sopenharmony_ci * spinning right now on the lock. 22538c2ecf20Sopenharmony_ci */ 22548c2ecf20Sopenharmony_ci goto next_queue; 22558c2ecf20Sopenharmony_ci } 22568c2ecf20Sopenharmony_ci /* remember how long until the timer fires */ 22578c2ecf20Sopenharmony_ci txq->frozen_expiry_remainder = 22588c2ecf20Sopenharmony_ci txq->stuck_timer.expires - now; 22598c2ecf20Sopenharmony_ci del_timer(&txq->stuck_timer); 22608c2ecf20Sopenharmony_ci goto next_queue; 22618c2ecf20Sopenharmony_ci } 22628c2ecf20Sopenharmony_ci 22638c2ecf20Sopenharmony_ci /* 22648c2ecf20Sopenharmony_ci * Wake a non-empty queue -> arm timer with the 22658c2ecf20Sopenharmony_ci * remainder before it froze 22668c2ecf20Sopenharmony_ci */ 22678c2ecf20Sopenharmony_ci mod_timer(&txq->stuck_timer, 22688c2ecf20Sopenharmony_ci now + txq->frozen_expiry_remainder); 22698c2ecf20Sopenharmony_ci 22708c2ecf20Sopenharmony_cinext_queue: 22718c2ecf20Sopenharmony_ci spin_unlock_bh(&txq->lock); 22728c2ecf20Sopenharmony_ci } 22738c2ecf20Sopenharmony_ci} 22748c2ecf20Sopenharmony_ci 22758c2ecf20Sopenharmony_cistatic void iwl_trans_pcie_block_txq_ptrs(struct iwl_trans *trans, bool block) 22768c2ecf20Sopenharmony_ci{ 22778c2ecf20Sopenharmony_ci int i; 22788c2ecf20Sopenharmony_ci 22798c2ecf20Sopenharmony_ci for (i = 0; i < trans->trans_cfg->base_params->num_of_queues; i++) { 22808c2ecf20Sopenharmony_ci struct iwl_txq *txq = trans->txqs.txq[i]; 22818c2ecf20Sopenharmony_ci 22828c2ecf20Sopenharmony_ci if (i == trans->txqs.cmd.q_id) 22838c2ecf20Sopenharmony_ci continue; 22848c2ecf20Sopenharmony_ci 22858c2ecf20Sopenharmony_ci spin_lock_bh(&txq->lock); 22868c2ecf20Sopenharmony_ci 22878c2ecf20Sopenharmony_ci if (!block && !(WARN_ON_ONCE(!txq->block))) { 22888c2ecf20Sopenharmony_ci txq->block--; 22898c2ecf20Sopenharmony_ci if (!txq->block) { 22908c2ecf20Sopenharmony_ci iwl_write32(trans, HBUS_TARG_WRPTR, 22918c2ecf20Sopenharmony_ci txq->write_ptr | (i << 8)); 22928c2ecf20Sopenharmony_ci } 22938c2ecf20Sopenharmony_ci } else if (block) { 22948c2ecf20Sopenharmony_ci txq->block++; 22958c2ecf20Sopenharmony_ci } 22968c2ecf20Sopenharmony_ci 22978c2ecf20Sopenharmony_ci spin_unlock_bh(&txq->lock); 22988c2ecf20Sopenharmony_ci } 22998c2ecf20Sopenharmony_ci} 23008c2ecf20Sopenharmony_ci 23018c2ecf20Sopenharmony_ci#define IWL_FLUSH_WAIT_MS 2000 23028c2ecf20Sopenharmony_ci 23038c2ecf20Sopenharmony_cistatic int iwl_trans_pcie_rxq_dma_data(struct iwl_trans *trans, int queue, 23048c2ecf20Sopenharmony_ci struct iwl_trans_rxq_dma_data *data) 23058c2ecf20Sopenharmony_ci{ 23068c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 23078c2ecf20Sopenharmony_ci 23088c2ecf20Sopenharmony_ci if (queue >= trans->num_rx_queues || !trans_pcie->rxq) 23098c2ecf20Sopenharmony_ci return -EINVAL; 23108c2ecf20Sopenharmony_ci 23118c2ecf20Sopenharmony_ci data->fr_bd_cb = trans_pcie->rxq[queue].bd_dma; 23128c2ecf20Sopenharmony_ci data->urbd_stts_wrptr = trans_pcie->rxq[queue].rb_stts_dma; 23138c2ecf20Sopenharmony_ci data->ur_bd_cb = trans_pcie->rxq[queue].used_bd_dma; 23148c2ecf20Sopenharmony_ci data->fr_bd_wid = 0; 23158c2ecf20Sopenharmony_ci 23168c2ecf20Sopenharmony_ci return 0; 23178c2ecf20Sopenharmony_ci} 23188c2ecf20Sopenharmony_ci 23198c2ecf20Sopenharmony_cistatic int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, int txq_idx) 23208c2ecf20Sopenharmony_ci{ 23218c2ecf20Sopenharmony_ci struct iwl_txq *txq; 23228c2ecf20Sopenharmony_ci unsigned long now = jiffies; 23238c2ecf20Sopenharmony_ci bool overflow_tx; 23248c2ecf20Sopenharmony_ci u8 wr_ptr; 23258c2ecf20Sopenharmony_ci 23268c2ecf20Sopenharmony_ci /* Make sure the NIC is still alive in the bus */ 23278c2ecf20Sopenharmony_ci if (test_bit(STATUS_TRANS_DEAD, &trans->status)) 23288c2ecf20Sopenharmony_ci return -ENODEV; 23298c2ecf20Sopenharmony_ci 23308c2ecf20Sopenharmony_ci if (!test_bit(txq_idx, trans->txqs.queue_used)) 23318c2ecf20Sopenharmony_ci return -EINVAL; 23328c2ecf20Sopenharmony_ci 23338c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(trans, "Emptying queue %d...\n", txq_idx); 23348c2ecf20Sopenharmony_ci txq = trans->txqs.txq[txq_idx]; 23358c2ecf20Sopenharmony_ci 23368c2ecf20Sopenharmony_ci spin_lock_bh(&txq->lock); 23378c2ecf20Sopenharmony_ci overflow_tx = txq->overflow_tx || 23388c2ecf20Sopenharmony_ci !skb_queue_empty(&txq->overflow_q); 23398c2ecf20Sopenharmony_ci spin_unlock_bh(&txq->lock); 23408c2ecf20Sopenharmony_ci 23418c2ecf20Sopenharmony_ci wr_ptr = READ_ONCE(txq->write_ptr); 23428c2ecf20Sopenharmony_ci 23438c2ecf20Sopenharmony_ci while ((txq->read_ptr != READ_ONCE(txq->write_ptr) || 23448c2ecf20Sopenharmony_ci overflow_tx) && 23458c2ecf20Sopenharmony_ci !time_after(jiffies, 23468c2ecf20Sopenharmony_ci now + msecs_to_jiffies(IWL_FLUSH_WAIT_MS))) { 23478c2ecf20Sopenharmony_ci u8 write_ptr = READ_ONCE(txq->write_ptr); 23488c2ecf20Sopenharmony_ci 23498c2ecf20Sopenharmony_ci /* 23508c2ecf20Sopenharmony_ci * If write pointer moved during the wait, warn only 23518c2ecf20Sopenharmony_ci * if the TX came from op mode. In case TX came from 23528c2ecf20Sopenharmony_ci * trans layer (overflow TX) don't warn. 23538c2ecf20Sopenharmony_ci */ 23548c2ecf20Sopenharmony_ci if (WARN_ONCE(wr_ptr != write_ptr && !overflow_tx, 23558c2ecf20Sopenharmony_ci "WR pointer moved while flushing %d -> %d\n", 23568c2ecf20Sopenharmony_ci wr_ptr, write_ptr)) 23578c2ecf20Sopenharmony_ci return -ETIMEDOUT; 23588c2ecf20Sopenharmony_ci wr_ptr = write_ptr; 23598c2ecf20Sopenharmony_ci 23608c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 23618c2ecf20Sopenharmony_ci 23628c2ecf20Sopenharmony_ci spin_lock_bh(&txq->lock); 23638c2ecf20Sopenharmony_ci overflow_tx = txq->overflow_tx || 23648c2ecf20Sopenharmony_ci !skb_queue_empty(&txq->overflow_q); 23658c2ecf20Sopenharmony_ci spin_unlock_bh(&txq->lock); 23668c2ecf20Sopenharmony_ci } 23678c2ecf20Sopenharmony_ci 23688c2ecf20Sopenharmony_ci if (txq->read_ptr != txq->write_ptr) { 23698c2ecf20Sopenharmony_ci IWL_ERR(trans, 23708c2ecf20Sopenharmony_ci "fail to flush all tx fifo queues Q %d\n", txq_idx); 23718c2ecf20Sopenharmony_ci iwl_txq_log_scd_error(trans, txq); 23728c2ecf20Sopenharmony_ci return -ETIMEDOUT; 23738c2ecf20Sopenharmony_ci } 23748c2ecf20Sopenharmony_ci 23758c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(trans, "Queue %d is now empty.\n", txq_idx); 23768c2ecf20Sopenharmony_ci 23778c2ecf20Sopenharmony_ci return 0; 23788c2ecf20Sopenharmony_ci} 23798c2ecf20Sopenharmony_ci 23808c2ecf20Sopenharmony_cistatic int iwl_trans_pcie_wait_txqs_empty(struct iwl_trans *trans, u32 txq_bm) 23818c2ecf20Sopenharmony_ci{ 23828c2ecf20Sopenharmony_ci int cnt; 23838c2ecf20Sopenharmony_ci int ret = 0; 23848c2ecf20Sopenharmony_ci 23858c2ecf20Sopenharmony_ci /* waiting for all the tx frames complete might take a while */ 23868c2ecf20Sopenharmony_ci for (cnt = 0; 23878c2ecf20Sopenharmony_ci cnt < trans->trans_cfg->base_params->num_of_queues; 23888c2ecf20Sopenharmony_ci cnt++) { 23898c2ecf20Sopenharmony_ci 23908c2ecf20Sopenharmony_ci if (cnt == trans->txqs.cmd.q_id) 23918c2ecf20Sopenharmony_ci continue; 23928c2ecf20Sopenharmony_ci if (!test_bit(cnt, trans->txqs.queue_used)) 23938c2ecf20Sopenharmony_ci continue; 23948c2ecf20Sopenharmony_ci if (!(BIT(cnt) & txq_bm)) 23958c2ecf20Sopenharmony_ci continue; 23968c2ecf20Sopenharmony_ci 23978c2ecf20Sopenharmony_ci ret = iwl_trans_pcie_wait_txq_empty(trans, cnt); 23988c2ecf20Sopenharmony_ci if (ret) 23998c2ecf20Sopenharmony_ci break; 24008c2ecf20Sopenharmony_ci } 24018c2ecf20Sopenharmony_ci 24028c2ecf20Sopenharmony_ci return ret; 24038c2ecf20Sopenharmony_ci} 24048c2ecf20Sopenharmony_ci 24058c2ecf20Sopenharmony_cistatic void iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, u32 reg, 24068c2ecf20Sopenharmony_ci u32 mask, u32 value) 24078c2ecf20Sopenharmony_ci{ 24088c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 24098c2ecf20Sopenharmony_ci 24108c2ecf20Sopenharmony_ci spin_lock_bh(&trans_pcie->reg_lock); 24118c2ecf20Sopenharmony_ci __iwl_trans_pcie_set_bits_mask(trans, reg, mask, value); 24128c2ecf20Sopenharmony_ci spin_unlock_bh(&trans_pcie->reg_lock); 24138c2ecf20Sopenharmony_ci} 24148c2ecf20Sopenharmony_ci 24158c2ecf20Sopenharmony_cistatic const char *get_csr_string(int cmd) 24168c2ecf20Sopenharmony_ci{ 24178c2ecf20Sopenharmony_ci#define IWL_CMD(x) case x: return #x 24188c2ecf20Sopenharmony_ci switch (cmd) { 24198c2ecf20Sopenharmony_ci IWL_CMD(CSR_HW_IF_CONFIG_REG); 24208c2ecf20Sopenharmony_ci IWL_CMD(CSR_INT_COALESCING); 24218c2ecf20Sopenharmony_ci IWL_CMD(CSR_INT); 24228c2ecf20Sopenharmony_ci IWL_CMD(CSR_INT_MASK); 24238c2ecf20Sopenharmony_ci IWL_CMD(CSR_FH_INT_STATUS); 24248c2ecf20Sopenharmony_ci IWL_CMD(CSR_GPIO_IN); 24258c2ecf20Sopenharmony_ci IWL_CMD(CSR_RESET); 24268c2ecf20Sopenharmony_ci IWL_CMD(CSR_GP_CNTRL); 24278c2ecf20Sopenharmony_ci IWL_CMD(CSR_HW_REV); 24288c2ecf20Sopenharmony_ci IWL_CMD(CSR_EEPROM_REG); 24298c2ecf20Sopenharmony_ci IWL_CMD(CSR_EEPROM_GP); 24308c2ecf20Sopenharmony_ci IWL_CMD(CSR_OTP_GP_REG); 24318c2ecf20Sopenharmony_ci IWL_CMD(CSR_GIO_REG); 24328c2ecf20Sopenharmony_ci IWL_CMD(CSR_GP_UCODE_REG); 24338c2ecf20Sopenharmony_ci IWL_CMD(CSR_GP_DRIVER_REG); 24348c2ecf20Sopenharmony_ci IWL_CMD(CSR_UCODE_DRV_GP1); 24358c2ecf20Sopenharmony_ci IWL_CMD(CSR_UCODE_DRV_GP2); 24368c2ecf20Sopenharmony_ci IWL_CMD(CSR_LED_REG); 24378c2ecf20Sopenharmony_ci IWL_CMD(CSR_DRAM_INT_TBL_REG); 24388c2ecf20Sopenharmony_ci IWL_CMD(CSR_GIO_CHICKEN_BITS); 24398c2ecf20Sopenharmony_ci IWL_CMD(CSR_ANA_PLL_CFG); 24408c2ecf20Sopenharmony_ci IWL_CMD(CSR_HW_REV_WA_REG); 24418c2ecf20Sopenharmony_ci IWL_CMD(CSR_MONITOR_STATUS_REG); 24428c2ecf20Sopenharmony_ci IWL_CMD(CSR_DBG_HPET_MEM_REG); 24438c2ecf20Sopenharmony_ci default: 24448c2ecf20Sopenharmony_ci return "UNKNOWN"; 24458c2ecf20Sopenharmony_ci } 24468c2ecf20Sopenharmony_ci#undef IWL_CMD 24478c2ecf20Sopenharmony_ci} 24488c2ecf20Sopenharmony_ci 24498c2ecf20Sopenharmony_civoid iwl_pcie_dump_csr(struct iwl_trans *trans) 24508c2ecf20Sopenharmony_ci{ 24518c2ecf20Sopenharmony_ci int i; 24528c2ecf20Sopenharmony_ci static const u32 csr_tbl[] = { 24538c2ecf20Sopenharmony_ci CSR_HW_IF_CONFIG_REG, 24548c2ecf20Sopenharmony_ci CSR_INT_COALESCING, 24558c2ecf20Sopenharmony_ci CSR_INT, 24568c2ecf20Sopenharmony_ci CSR_INT_MASK, 24578c2ecf20Sopenharmony_ci CSR_FH_INT_STATUS, 24588c2ecf20Sopenharmony_ci CSR_GPIO_IN, 24598c2ecf20Sopenharmony_ci CSR_RESET, 24608c2ecf20Sopenharmony_ci CSR_GP_CNTRL, 24618c2ecf20Sopenharmony_ci CSR_HW_REV, 24628c2ecf20Sopenharmony_ci CSR_EEPROM_REG, 24638c2ecf20Sopenharmony_ci CSR_EEPROM_GP, 24648c2ecf20Sopenharmony_ci CSR_OTP_GP_REG, 24658c2ecf20Sopenharmony_ci CSR_GIO_REG, 24668c2ecf20Sopenharmony_ci CSR_GP_UCODE_REG, 24678c2ecf20Sopenharmony_ci CSR_GP_DRIVER_REG, 24688c2ecf20Sopenharmony_ci CSR_UCODE_DRV_GP1, 24698c2ecf20Sopenharmony_ci CSR_UCODE_DRV_GP2, 24708c2ecf20Sopenharmony_ci CSR_LED_REG, 24718c2ecf20Sopenharmony_ci CSR_DRAM_INT_TBL_REG, 24728c2ecf20Sopenharmony_ci CSR_GIO_CHICKEN_BITS, 24738c2ecf20Sopenharmony_ci CSR_ANA_PLL_CFG, 24748c2ecf20Sopenharmony_ci CSR_MONITOR_STATUS_REG, 24758c2ecf20Sopenharmony_ci CSR_HW_REV_WA_REG, 24768c2ecf20Sopenharmony_ci CSR_DBG_HPET_MEM_REG 24778c2ecf20Sopenharmony_ci }; 24788c2ecf20Sopenharmony_ci IWL_ERR(trans, "CSR values:\n"); 24798c2ecf20Sopenharmony_ci IWL_ERR(trans, "(2nd byte of CSR_INT_COALESCING is " 24808c2ecf20Sopenharmony_ci "CSR_INT_PERIODIC_REG)\n"); 24818c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(csr_tbl); i++) { 24828c2ecf20Sopenharmony_ci IWL_ERR(trans, " %25s: 0X%08x\n", 24838c2ecf20Sopenharmony_ci get_csr_string(csr_tbl[i]), 24848c2ecf20Sopenharmony_ci iwl_read32(trans, csr_tbl[i])); 24858c2ecf20Sopenharmony_ci } 24868c2ecf20Sopenharmony_ci} 24878c2ecf20Sopenharmony_ci 24888c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS 24898c2ecf20Sopenharmony_ci/* create and remove of files */ 24908c2ecf20Sopenharmony_ci#define DEBUGFS_ADD_FILE(name, parent, mode) do { \ 24918c2ecf20Sopenharmony_ci debugfs_create_file(#name, mode, parent, trans, \ 24928c2ecf20Sopenharmony_ci &iwl_dbgfs_##name##_ops); \ 24938c2ecf20Sopenharmony_ci} while (0) 24948c2ecf20Sopenharmony_ci 24958c2ecf20Sopenharmony_ci/* file operation */ 24968c2ecf20Sopenharmony_ci#define DEBUGFS_READ_FILE_OPS(name) \ 24978c2ecf20Sopenharmony_cistatic const struct file_operations iwl_dbgfs_##name##_ops = { \ 24988c2ecf20Sopenharmony_ci .read = iwl_dbgfs_##name##_read, \ 24998c2ecf20Sopenharmony_ci .open = simple_open, \ 25008c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, \ 25018c2ecf20Sopenharmony_ci}; 25028c2ecf20Sopenharmony_ci 25038c2ecf20Sopenharmony_ci#define DEBUGFS_WRITE_FILE_OPS(name) \ 25048c2ecf20Sopenharmony_cistatic const struct file_operations iwl_dbgfs_##name##_ops = { \ 25058c2ecf20Sopenharmony_ci .write = iwl_dbgfs_##name##_write, \ 25068c2ecf20Sopenharmony_ci .open = simple_open, \ 25078c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, \ 25088c2ecf20Sopenharmony_ci}; 25098c2ecf20Sopenharmony_ci 25108c2ecf20Sopenharmony_ci#define DEBUGFS_READ_WRITE_FILE_OPS(name) \ 25118c2ecf20Sopenharmony_cistatic const struct file_operations iwl_dbgfs_##name##_ops = { \ 25128c2ecf20Sopenharmony_ci .write = iwl_dbgfs_##name##_write, \ 25138c2ecf20Sopenharmony_ci .read = iwl_dbgfs_##name##_read, \ 25148c2ecf20Sopenharmony_ci .open = simple_open, \ 25158c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, \ 25168c2ecf20Sopenharmony_ci}; 25178c2ecf20Sopenharmony_ci 25188c2ecf20Sopenharmony_cistruct iwl_dbgfs_tx_queue_priv { 25198c2ecf20Sopenharmony_ci struct iwl_trans *trans; 25208c2ecf20Sopenharmony_ci}; 25218c2ecf20Sopenharmony_ci 25228c2ecf20Sopenharmony_cistruct iwl_dbgfs_tx_queue_state { 25238c2ecf20Sopenharmony_ci loff_t pos; 25248c2ecf20Sopenharmony_ci}; 25258c2ecf20Sopenharmony_ci 25268c2ecf20Sopenharmony_cistatic void *iwl_dbgfs_tx_queue_seq_start(struct seq_file *seq, loff_t *pos) 25278c2ecf20Sopenharmony_ci{ 25288c2ecf20Sopenharmony_ci struct iwl_dbgfs_tx_queue_priv *priv = seq->private; 25298c2ecf20Sopenharmony_ci struct iwl_dbgfs_tx_queue_state *state; 25308c2ecf20Sopenharmony_ci 25318c2ecf20Sopenharmony_ci if (*pos >= priv->trans->trans_cfg->base_params->num_of_queues) 25328c2ecf20Sopenharmony_ci return NULL; 25338c2ecf20Sopenharmony_ci 25348c2ecf20Sopenharmony_ci state = kmalloc(sizeof(*state), GFP_KERNEL); 25358c2ecf20Sopenharmony_ci if (!state) 25368c2ecf20Sopenharmony_ci return NULL; 25378c2ecf20Sopenharmony_ci state->pos = *pos; 25388c2ecf20Sopenharmony_ci return state; 25398c2ecf20Sopenharmony_ci} 25408c2ecf20Sopenharmony_ci 25418c2ecf20Sopenharmony_cistatic void *iwl_dbgfs_tx_queue_seq_next(struct seq_file *seq, 25428c2ecf20Sopenharmony_ci void *v, loff_t *pos) 25438c2ecf20Sopenharmony_ci{ 25448c2ecf20Sopenharmony_ci struct iwl_dbgfs_tx_queue_priv *priv = seq->private; 25458c2ecf20Sopenharmony_ci struct iwl_dbgfs_tx_queue_state *state = v; 25468c2ecf20Sopenharmony_ci 25478c2ecf20Sopenharmony_ci *pos = ++state->pos; 25488c2ecf20Sopenharmony_ci 25498c2ecf20Sopenharmony_ci if (*pos >= priv->trans->trans_cfg->base_params->num_of_queues) 25508c2ecf20Sopenharmony_ci return NULL; 25518c2ecf20Sopenharmony_ci 25528c2ecf20Sopenharmony_ci return state; 25538c2ecf20Sopenharmony_ci} 25548c2ecf20Sopenharmony_ci 25558c2ecf20Sopenharmony_cistatic void iwl_dbgfs_tx_queue_seq_stop(struct seq_file *seq, void *v) 25568c2ecf20Sopenharmony_ci{ 25578c2ecf20Sopenharmony_ci kfree(v); 25588c2ecf20Sopenharmony_ci} 25598c2ecf20Sopenharmony_ci 25608c2ecf20Sopenharmony_cistatic int iwl_dbgfs_tx_queue_seq_show(struct seq_file *seq, void *v) 25618c2ecf20Sopenharmony_ci{ 25628c2ecf20Sopenharmony_ci struct iwl_dbgfs_tx_queue_priv *priv = seq->private; 25638c2ecf20Sopenharmony_ci struct iwl_dbgfs_tx_queue_state *state = v; 25648c2ecf20Sopenharmony_ci struct iwl_trans *trans = priv->trans; 25658c2ecf20Sopenharmony_ci struct iwl_txq *txq = trans->txqs.txq[state->pos]; 25668c2ecf20Sopenharmony_ci 25678c2ecf20Sopenharmony_ci seq_printf(seq, "hwq %.3u: used=%d stopped=%d ", 25688c2ecf20Sopenharmony_ci (unsigned int)state->pos, 25698c2ecf20Sopenharmony_ci !!test_bit(state->pos, trans->txqs.queue_used), 25708c2ecf20Sopenharmony_ci !!test_bit(state->pos, trans->txqs.queue_stopped)); 25718c2ecf20Sopenharmony_ci if (txq) 25728c2ecf20Sopenharmony_ci seq_printf(seq, 25738c2ecf20Sopenharmony_ci "read=%u write=%u need_update=%d frozen=%d n_window=%d ampdu=%d", 25748c2ecf20Sopenharmony_ci txq->read_ptr, txq->write_ptr, 25758c2ecf20Sopenharmony_ci txq->need_update, txq->frozen, 25768c2ecf20Sopenharmony_ci txq->n_window, txq->ampdu); 25778c2ecf20Sopenharmony_ci else 25788c2ecf20Sopenharmony_ci seq_puts(seq, "(unallocated)"); 25798c2ecf20Sopenharmony_ci 25808c2ecf20Sopenharmony_ci if (state->pos == trans->txqs.cmd.q_id) 25818c2ecf20Sopenharmony_ci seq_puts(seq, " (HCMD)"); 25828c2ecf20Sopenharmony_ci seq_puts(seq, "\n"); 25838c2ecf20Sopenharmony_ci 25848c2ecf20Sopenharmony_ci return 0; 25858c2ecf20Sopenharmony_ci} 25868c2ecf20Sopenharmony_ci 25878c2ecf20Sopenharmony_cistatic const struct seq_operations iwl_dbgfs_tx_queue_seq_ops = { 25888c2ecf20Sopenharmony_ci .start = iwl_dbgfs_tx_queue_seq_start, 25898c2ecf20Sopenharmony_ci .next = iwl_dbgfs_tx_queue_seq_next, 25908c2ecf20Sopenharmony_ci .stop = iwl_dbgfs_tx_queue_seq_stop, 25918c2ecf20Sopenharmony_ci .show = iwl_dbgfs_tx_queue_seq_show, 25928c2ecf20Sopenharmony_ci}; 25938c2ecf20Sopenharmony_ci 25948c2ecf20Sopenharmony_cistatic int iwl_dbgfs_tx_queue_open(struct inode *inode, struct file *filp) 25958c2ecf20Sopenharmony_ci{ 25968c2ecf20Sopenharmony_ci struct iwl_dbgfs_tx_queue_priv *priv; 25978c2ecf20Sopenharmony_ci 25988c2ecf20Sopenharmony_ci priv = __seq_open_private(filp, &iwl_dbgfs_tx_queue_seq_ops, 25998c2ecf20Sopenharmony_ci sizeof(*priv)); 26008c2ecf20Sopenharmony_ci 26018c2ecf20Sopenharmony_ci if (!priv) 26028c2ecf20Sopenharmony_ci return -ENOMEM; 26038c2ecf20Sopenharmony_ci 26048c2ecf20Sopenharmony_ci priv->trans = inode->i_private; 26058c2ecf20Sopenharmony_ci return 0; 26068c2ecf20Sopenharmony_ci} 26078c2ecf20Sopenharmony_ci 26088c2ecf20Sopenharmony_cistatic ssize_t iwl_dbgfs_rx_queue_read(struct file *file, 26098c2ecf20Sopenharmony_ci char __user *user_buf, 26108c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 26118c2ecf20Sopenharmony_ci{ 26128c2ecf20Sopenharmony_ci struct iwl_trans *trans = file->private_data; 26138c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 26148c2ecf20Sopenharmony_ci char *buf; 26158c2ecf20Sopenharmony_ci int pos = 0, i, ret; 26168c2ecf20Sopenharmony_ci size_t bufsz; 26178c2ecf20Sopenharmony_ci 26188c2ecf20Sopenharmony_ci bufsz = sizeof(char) * 121 * trans->num_rx_queues; 26198c2ecf20Sopenharmony_ci 26208c2ecf20Sopenharmony_ci if (!trans_pcie->rxq) 26218c2ecf20Sopenharmony_ci return -EAGAIN; 26228c2ecf20Sopenharmony_ci 26238c2ecf20Sopenharmony_ci buf = kzalloc(bufsz, GFP_KERNEL); 26248c2ecf20Sopenharmony_ci if (!buf) 26258c2ecf20Sopenharmony_ci return -ENOMEM; 26268c2ecf20Sopenharmony_ci 26278c2ecf20Sopenharmony_ci for (i = 0; i < trans->num_rx_queues && pos < bufsz; i++) { 26288c2ecf20Sopenharmony_ci struct iwl_rxq *rxq = &trans_pcie->rxq[i]; 26298c2ecf20Sopenharmony_ci 26308c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, "queue#: %2d\n", 26318c2ecf20Sopenharmony_ci i); 26328c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, "\tread: %u\n", 26338c2ecf20Sopenharmony_ci rxq->read); 26348c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, "\twrite: %u\n", 26358c2ecf20Sopenharmony_ci rxq->write); 26368c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, "\twrite_actual: %u\n", 26378c2ecf20Sopenharmony_ci rxq->write_actual); 26388c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, "\tneed_update: %2d\n", 26398c2ecf20Sopenharmony_ci rxq->need_update); 26408c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, "\tfree_count: %u\n", 26418c2ecf20Sopenharmony_ci rxq->free_count); 26428c2ecf20Sopenharmony_ci if (rxq->rb_stts) { 26438c2ecf20Sopenharmony_ci u32 r = __le16_to_cpu(iwl_get_closed_rb_stts(trans, 26448c2ecf20Sopenharmony_ci rxq)); 26458c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, 26468c2ecf20Sopenharmony_ci "\tclosed_rb_num: %u\n", 26478c2ecf20Sopenharmony_ci r & 0x0FFF); 26488c2ecf20Sopenharmony_ci } else { 26498c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, 26508c2ecf20Sopenharmony_ci "\tclosed_rb_num: Not Allocated\n"); 26518c2ecf20Sopenharmony_ci } 26528c2ecf20Sopenharmony_ci } 26538c2ecf20Sopenharmony_ci ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); 26548c2ecf20Sopenharmony_ci kfree(buf); 26558c2ecf20Sopenharmony_ci 26568c2ecf20Sopenharmony_ci return ret; 26578c2ecf20Sopenharmony_ci} 26588c2ecf20Sopenharmony_ci 26598c2ecf20Sopenharmony_cistatic ssize_t iwl_dbgfs_interrupt_read(struct file *file, 26608c2ecf20Sopenharmony_ci char __user *user_buf, 26618c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 26628c2ecf20Sopenharmony_ci{ 26638c2ecf20Sopenharmony_ci struct iwl_trans *trans = file->private_data; 26648c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 26658c2ecf20Sopenharmony_ci struct isr_statistics *isr_stats = &trans_pcie->isr_stats; 26668c2ecf20Sopenharmony_ci 26678c2ecf20Sopenharmony_ci int pos = 0; 26688c2ecf20Sopenharmony_ci char *buf; 26698c2ecf20Sopenharmony_ci int bufsz = 24 * 64; /* 24 items * 64 char per item */ 26708c2ecf20Sopenharmony_ci ssize_t ret; 26718c2ecf20Sopenharmony_ci 26728c2ecf20Sopenharmony_ci buf = kzalloc(bufsz, GFP_KERNEL); 26738c2ecf20Sopenharmony_ci if (!buf) 26748c2ecf20Sopenharmony_ci return -ENOMEM; 26758c2ecf20Sopenharmony_ci 26768c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, 26778c2ecf20Sopenharmony_ci "Interrupt Statistics Report:\n"); 26788c2ecf20Sopenharmony_ci 26798c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, "HW Error:\t\t\t %u\n", 26808c2ecf20Sopenharmony_ci isr_stats->hw); 26818c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, "SW Error:\t\t\t %u\n", 26828c2ecf20Sopenharmony_ci isr_stats->sw); 26838c2ecf20Sopenharmony_ci if (isr_stats->sw || isr_stats->hw) { 26848c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, 26858c2ecf20Sopenharmony_ci "\tLast Restarting Code: 0x%X\n", 26868c2ecf20Sopenharmony_ci isr_stats->err_code); 26878c2ecf20Sopenharmony_ci } 26888c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUG 26898c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, "Frame transmitted:\t\t %u\n", 26908c2ecf20Sopenharmony_ci isr_stats->sch); 26918c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, "Alive interrupt:\t\t %u\n", 26928c2ecf20Sopenharmony_ci isr_stats->alive); 26938c2ecf20Sopenharmony_ci#endif 26948c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, 26958c2ecf20Sopenharmony_ci "HW RF KILL switch toggled:\t %u\n", isr_stats->rfkill); 26968c2ecf20Sopenharmony_ci 26978c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, "CT KILL:\t\t\t %u\n", 26988c2ecf20Sopenharmony_ci isr_stats->ctkill); 26998c2ecf20Sopenharmony_ci 27008c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, "Wakeup Interrupt:\t\t %u\n", 27018c2ecf20Sopenharmony_ci isr_stats->wakeup); 27028c2ecf20Sopenharmony_ci 27038c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, 27048c2ecf20Sopenharmony_ci "Rx command responses:\t\t %u\n", isr_stats->rx); 27058c2ecf20Sopenharmony_ci 27068c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, "Tx/FH interrupt:\t\t %u\n", 27078c2ecf20Sopenharmony_ci isr_stats->tx); 27088c2ecf20Sopenharmony_ci 27098c2ecf20Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, "Unexpected INTA:\t\t %u\n", 27108c2ecf20Sopenharmony_ci isr_stats->unhandled); 27118c2ecf20Sopenharmony_ci 27128c2ecf20Sopenharmony_ci ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); 27138c2ecf20Sopenharmony_ci kfree(buf); 27148c2ecf20Sopenharmony_ci return ret; 27158c2ecf20Sopenharmony_ci} 27168c2ecf20Sopenharmony_ci 27178c2ecf20Sopenharmony_cistatic ssize_t iwl_dbgfs_interrupt_write(struct file *file, 27188c2ecf20Sopenharmony_ci const char __user *user_buf, 27198c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 27208c2ecf20Sopenharmony_ci{ 27218c2ecf20Sopenharmony_ci struct iwl_trans *trans = file->private_data; 27228c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 27238c2ecf20Sopenharmony_ci struct isr_statistics *isr_stats = &trans_pcie->isr_stats; 27248c2ecf20Sopenharmony_ci u32 reset_flag; 27258c2ecf20Sopenharmony_ci int ret; 27268c2ecf20Sopenharmony_ci 27278c2ecf20Sopenharmony_ci ret = kstrtou32_from_user(user_buf, count, 16, &reset_flag); 27288c2ecf20Sopenharmony_ci if (ret) 27298c2ecf20Sopenharmony_ci return ret; 27308c2ecf20Sopenharmony_ci if (reset_flag == 0) 27318c2ecf20Sopenharmony_ci memset(isr_stats, 0, sizeof(*isr_stats)); 27328c2ecf20Sopenharmony_ci 27338c2ecf20Sopenharmony_ci return count; 27348c2ecf20Sopenharmony_ci} 27358c2ecf20Sopenharmony_ci 27368c2ecf20Sopenharmony_cistatic ssize_t iwl_dbgfs_csr_write(struct file *file, 27378c2ecf20Sopenharmony_ci const char __user *user_buf, 27388c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 27398c2ecf20Sopenharmony_ci{ 27408c2ecf20Sopenharmony_ci struct iwl_trans *trans = file->private_data; 27418c2ecf20Sopenharmony_ci 27428c2ecf20Sopenharmony_ci iwl_pcie_dump_csr(trans); 27438c2ecf20Sopenharmony_ci 27448c2ecf20Sopenharmony_ci return count; 27458c2ecf20Sopenharmony_ci} 27468c2ecf20Sopenharmony_ci 27478c2ecf20Sopenharmony_cistatic ssize_t iwl_dbgfs_fh_reg_read(struct file *file, 27488c2ecf20Sopenharmony_ci char __user *user_buf, 27498c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 27508c2ecf20Sopenharmony_ci{ 27518c2ecf20Sopenharmony_ci struct iwl_trans *trans = file->private_data; 27528c2ecf20Sopenharmony_ci char *buf = NULL; 27538c2ecf20Sopenharmony_ci ssize_t ret; 27548c2ecf20Sopenharmony_ci 27558c2ecf20Sopenharmony_ci ret = iwl_dump_fh(trans, &buf); 27568c2ecf20Sopenharmony_ci if (ret < 0) 27578c2ecf20Sopenharmony_ci return ret; 27588c2ecf20Sopenharmony_ci if (!buf) 27598c2ecf20Sopenharmony_ci return -EINVAL; 27608c2ecf20Sopenharmony_ci ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); 27618c2ecf20Sopenharmony_ci kfree(buf); 27628c2ecf20Sopenharmony_ci return ret; 27638c2ecf20Sopenharmony_ci} 27648c2ecf20Sopenharmony_ci 27658c2ecf20Sopenharmony_cistatic ssize_t iwl_dbgfs_rfkill_read(struct file *file, 27668c2ecf20Sopenharmony_ci char __user *user_buf, 27678c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 27688c2ecf20Sopenharmony_ci{ 27698c2ecf20Sopenharmony_ci struct iwl_trans *trans = file->private_data; 27708c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 27718c2ecf20Sopenharmony_ci char buf[100]; 27728c2ecf20Sopenharmony_ci int pos; 27738c2ecf20Sopenharmony_ci 27748c2ecf20Sopenharmony_ci pos = scnprintf(buf, sizeof(buf), "debug: %d\nhw: %d\n", 27758c2ecf20Sopenharmony_ci trans_pcie->debug_rfkill, 27768c2ecf20Sopenharmony_ci !(iwl_read32(trans, CSR_GP_CNTRL) & 27778c2ecf20Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)); 27788c2ecf20Sopenharmony_ci 27798c2ecf20Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, buf, pos); 27808c2ecf20Sopenharmony_ci} 27818c2ecf20Sopenharmony_ci 27828c2ecf20Sopenharmony_cistatic ssize_t iwl_dbgfs_rfkill_write(struct file *file, 27838c2ecf20Sopenharmony_ci const char __user *user_buf, 27848c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 27858c2ecf20Sopenharmony_ci{ 27868c2ecf20Sopenharmony_ci struct iwl_trans *trans = file->private_data; 27878c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 27888c2ecf20Sopenharmony_ci bool new_value; 27898c2ecf20Sopenharmony_ci int ret; 27908c2ecf20Sopenharmony_ci 27918c2ecf20Sopenharmony_ci ret = kstrtobool_from_user(user_buf, count, &new_value); 27928c2ecf20Sopenharmony_ci if (ret) 27938c2ecf20Sopenharmony_ci return ret; 27948c2ecf20Sopenharmony_ci if (new_value == trans_pcie->debug_rfkill) 27958c2ecf20Sopenharmony_ci return count; 27968c2ecf20Sopenharmony_ci IWL_WARN(trans, "changing debug rfkill %d->%d\n", 27978c2ecf20Sopenharmony_ci trans_pcie->debug_rfkill, new_value); 27988c2ecf20Sopenharmony_ci trans_pcie->debug_rfkill = new_value; 27998c2ecf20Sopenharmony_ci iwl_pcie_handle_rfkill_irq(trans); 28008c2ecf20Sopenharmony_ci 28018c2ecf20Sopenharmony_ci return count; 28028c2ecf20Sopenharmony_ci} 28038c2ecf20Sopenharmony_ci 28048c2ecf20Sopenharmony_cistatic int iwl_dbgfs_monitor_data_open(struct inode *inode, 28058c2ecf20Sopenharmony_ci struct file *file) 28068c2ecf20Sopenharmony_ci{ 28078c2ecf20Sopenharmony_ci struct iwl_trans *trans = inode->i_private; 28088c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 28098c2ecf20Sopenharmony_ci 28108c2ecf20Sopenharmony_ci if (!trans->dbg.dest_tlv || 28118c2ecf20Sopenharmony_ci trans->dbg.dest_tlv->monitor_mode != EXTERNAL_MODE) { 28128c2ecf20Sopenharmony_ci IWL_ERR(trans, "Debug destination is not set to DRAM\n"); 28138c2ecf20Sopenharmony_ci return -ENOENT; 28148c2ecf20Sopenharmony_ci } 28158c2ecf20Sopenharmony_ci 28168c2ecf20Sopenharmony_ci if (trans_pcie->fw_mon_data.state != IWL_FW_MON_DBGFS_STATE_CLOSED) 28178c2ecf20Sopenharmony_ci return -EBUSY; 28188c2ecf20Sopenharmony_ci 28198c2ecf20Sopenharmony_ci trans_pcie->fw_mon_data.state = IWL_FW_MON_DBGFS_STATE_OPEN; 28208c2ecf20Sopenharmony_ci return simple_open(inode, file); 28218c2ecf20Sopenharmony_ci} 28228c2ecf20Sopenharmony_ci 28238c2ecf20Sopenharmony_cistatic int iwl_dbgfs_monitor_data_release(struct inode *inode, 28248c2ecf20Sopenharmony_ci struct file *file) 28258c2ecf20Sopenharmony_ci{ 28268c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = 28278c2ecf20Sopenharmony_ci IWL_TRANS_GET_PCIE_TRANS(inode->i_private); 28288c2ecf20Sopenharmony_ci 28298c2ecf20Sopenharmony_ci if (trans_pcie->fw_mon_data.state == IWL_FW_MON_DBGFS_STATE_OPEN) 28308c2ecf20Sopenharmony_ci trans_pcie->fw_mon_data.state = IWL_FW_MON_DBGFS_STATE_CLOSED; 28318c2ecf20Sopenharmony_ci return 0; 28328c2ecf20Sopenharmony_ci} 28338c2ecf20Sopenharmony_ci 28348c2ecf20Sopenharmony_cistatic bool iwl_write_to_user_buf(char __user *user_buf, ssize_t count, 28358c2ecf20Sopenharmony_ci void *buf, ssize_t *size, 28368c2ecf20Sopenharmony_ci ssize_t *bytes_copied) 28378c2ecf20Sopenharmony_ci{ 28388c2ecf20Sopenharmony_ci ssize_t buf_size_left = count - *bytes_copied; 28398c2ecf20Sopenharmony_ci 28408c2ecf20Sopenharmony_ci buf_size_left = buf_size_left - (buf_size_left % sizeof(u32)); 28418c2ecf20Sopenharmony_ci if (*size > buf_size_left) 28428c2ecf20Sopenharmony_ci *size = buf_size_left; 28438c2ecf20Sopenharmony_ci 28448c2ecf20Sopenharmony_ci *size -= copy_to_user(user_buf, buf, *size); 28458c2ecf20Sopenharmony_ci *bytes_copied += *size; 28468c2ecf20Sopenharmony_ci 28478c2ecf20Sopenharmony_ci if (buf_size_left == *size) 28488c2ecf20Sopenharmony_ci return true; 28498c2ecf20Sopenharmony_ci return false; 28508c2ecf20Sopenharmony_ci} 28518c2ecf20Sopenharmony_ci 28528c2ecf20Sopenharmony_cistatic ssize_t iwl_dbgfs_monitor_data_read(struct file *file, 28538c2ecf20Sopenharmony_ci char __user *user_buf, 28548c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 28558c2ecf20Sopenharmony_ci{ 28568c2ecf20Sopenharmony_ci struct iwl_trans *trans = file->private_data; 28578c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 28588c2ecf20Sopenharmony_ci void *cpu_addr = (void *)trans->dbg.fw_mon.block, *curr_buf; 28598c2ecf20Sopenharmony_ci struct cont_rec *data = &trans_pcie->fw_mon_data; 28608c2ecf20Sopenharmony_ci u32 write_ptr_addr, wrap_cnt_addr, write_ptr, wrap_cnt; 28618c2ecf20Sopenharmony_ci ssize_t size, bytes_copied = 0; 28628c2ecf20Sopenharmony_ci bool b_full; 28638c2ecf20Sopenharmony_ci 28648c2ecf20Sopenharmony_ci if (trans->dbg.dest_tlv) { 28658c2ecf20Sopenharmony_ci write_ptr_addr = 28668c2ecf20Sopenharmony_ci le32_to_cpu(trans->dbg.dest_tlv->write_ptr_reg); 28678c2ecf20Sopenharmony_ci wrap_cnt_addr = le32_to_cpu(trans->dbg.dest_tlv->wrap_count); 28688c2ecf20Sopenharmony_ci } else { 28698c2ecf20Sopenharmony_ci write_ptr_addr = MON_BUFF_WRPTR; 28708c2ecf20Sopenharmony_ci wrap_cnt_addr = MON_BUFF_CYCLE_CNT; 28718c2ecf20Sopenharmony_ci } 28728c2ecf20Sopenharmony_ci 28738c2ecf20Sopenharmony_ci if (unlikely(!trans->dbg.rec_on)) 28748c2ecf20Sopenharmony_ci return 0; 28758c2ecf20Sopenharmony_ci 28768c2ecf20Sopenharmony_ci mutex_lock(&data->mutex); 28778c2ecf20Sopenharmony_ci if (data->state == 28788c2ecf20Sopenharmony_ci IWL_FW_MON_DBGFS_STATE_DISABLED) { 28798c2ecf20Sopenharmony_ci mutex_unlock(&data->mutex); 28808c2ecf20Sopenharmony_ci return 0; 28818c2ecf20Sopenharmony_ci } 28828c2ecf20Sopenharmony_ci 28838c2ecf20Sopenharmony_ci /* write_ptr position in bytes rather then DW */ 28848c2ecf20Sopenharmony_ci write_ptr = iwl_read_prph(trans, write_ptr_addr) * sizeof(u32); 28858c2ecf20Sopenharmony_ci wrap_cnt = iwl_read_prph(trans, wrap_cnt_addr); 28868c2ecf20Sopenharmony_ci 28878c2ecf20Sopenharmony_ci if (data->prev_wrap_cnt == wrap_cnt) { 28888c2ecf20Sopenharmony_ci size = write_ptr - data->prev_wr_ptr; 28898c2ecf20Sopenharmony_ci curr_buf = cpu_addr + data->prev_wr_ptr; 28908c2ecf20Sopenharmony_ci b_full = iwl_write_to_user_buf(user_buf, count, 28918c2ecf20Sopenharmony_ci curr_buf, &size, 28928c2ecf20Sopenharmony_ci &bytes_copied); 28938c2ecf20Sopenharmony_ci data->prev_wr_ptr += size; 28948c2ecf20Sopenharmony_ci 28958c2ecf20Sopenharmony_ci } else if (data->prev_wrap_cnt == wrap_cnt - 1 && 28968c2ecf20Sopenharmony_ci write_ptr < data->prev_wr_ptr) { 28978c2ecf20Sopenharmony_ci size = trans->dbg.fw_mon.size - data->prev_wr_ptr; 28988c2ecf20Sopenharmony_ci curr_buf = cpu_addr + data->prev_wr_ptr; 28998c2ecf20Sopenharmony_ci b_full = iwl_write_to_user_buf(user_buf, count, 29008c2ecf20Sopenharmony_ci curr_buf, &size, 29018c2ecf20Sopenharmony_ci &bytes_copied); 29028c2ecf20Sopenharmony_ci data->prev_wr_ptr += size; 29038c2ecf20Sopenharmony_ci 29048c2ecf20Sopenharmony_ci if (!b_full) { 29058c2ecf20Sopenharmony_ci size = write_ptr; 29068c2ecf20Sopenharmony_ci b_full = iwl_write_to_user_buf(user_buf, count, 29078c2ecf20Sopenharmony_ci cpu_addr, &size, 29088c2ecf20Sopenharmony_ci &bytes_copied); 29098c2ecf20Sopenharmony_ci data->prev_wr_ptr = size; 29108c2ecf20Sopenharmony_ci data->prev_wrap_cnt++; 29118c2ecf20Sopenharmony_ci } 29128c2ecf20Sopenharmony_ci } else { 29138c2ecf20Sopenharmony_ci if (data->prev_wrap_cnt == wrap_cnt - 1 && 29148c2ecf20Sopenharmony_ci write_ptr > data->prev_wr_ptr) 29158c2ecf20Sopenharmony_ci IWL_WARN(trans, 29168c2ecf20Sopenharmony_ci "write pointer passed previous write pointer, start copying from the beginning\n"); 29178c2ecf20Sopenharmony_ci else if (!unlikely(data->prev_wrap_cnt == 0 && 29188c2ecf20Sopenharmony_ci data->prev_wr_ptr == 0)) 29198c2ecf20Sopenharmony_ci IWL_WARN(trans, 29208c2ecf20Sopenharmony_ci "monitor data is out of sync, start copying from the beginning\n"); 29218c2ecf20Sopenharmony_ci 29228c2ecf20Sopenharmony_ci size = write_ptr; 29238c2ecf20Sopenharmony_ci b_full = iwl_write_to_user_buf(user_buf, count, 29248c2ecf20Sopenharmony_ci cpu_addr, &size, 29258c2ecf20Sopenharmony_ci &bytes_copied); 29268c2ecf20Sopenharmony_ci data->prev_wr_ptr = size; 29278c2ecf20Sopenharmony_ci data->prev_wrap_cnt = wrap_cnt; 29288c2ecf20Sopenharmony_ci } 29298c2ecf20Sopenharmony_ci 29308c2ecf20Sopenharmony_ci mutex_unlock(&data->mutex); 29318c2ecf20Sopenharmony_ci 29328c2ecf20Sopenharmony_ci return bytes_copied; 29338c2ecf20Sopenharmony_ci} 29348c2ecf20Sopenharmony_ci 29358c2ecf20Sopenharmony_ciDEBUGFS_READ_WRITE_FILE_OPS(interrupt); 29368c2ecf20Sopenharmony_ciDEBUGFS_READ_FILE_OPS(fh_reg); 29378c2ecf20Sopenharmony_ciDEBUGFS_READ_FILE_OPS(rx_queue); 29388c2ecf20Sopenharmony_ciDEBUGFS_WRITE_FILE_OPS(csr); 29398c2ecf20Sopenharmony_ciDEBUGFS_READ_WRITE_FILE_OPS(rfkill); 29408c2ecf20Sopenharmony_cistatic const struct file_operations iwl_dbgfs_tx_queue_ops = { 29418c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 29428c2ecf20Sopenharmony_ci .open = iwl_dbgfs_tx_queue_open, 29438c2ecf20Sopenharmony_ci .read = seq_read, 29448c2ecf20Sopenharmony_ci .llseek = seq_lseek, 29458c2ecf20Sopenharmony_ci .release = seq_release_private, 29468c2ecf20Sopenharmony_ci}; 29478c2ecf20Sopenharmony_ci 29488c2ecf20Sopenharmony_cistatic const struct file_operations iwl_dbgfs_monitor_data_ops = { 29498c2ecf20Sopenharmony_ci .read = iwl_dbgfs_monitor_data_read, 29508c2ecf20Sopenharmony_ci .open = iwl_dbgfs_monitor_data_open, 29518c2ecf20Sopenharmony_ci .release = iwl_dbgfs_monitor_data_release, 29528c2ecf20Sopenharmony_ci}; 29538c2ecf20Sopenharmony_ci 29548c2ecf20Sopenharmony_ci/* Create the debugfs files and directories */ 29558c2ecf20Sopenharmony_civoid iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans) 29568c2ecf20Sopenharmony_ci{ 29578c2ecf20Sopenharmony_ci struct dentry *dir = trans->dbgfs_dir; 29588c2ecf20Sopenharmony_ci 29598c2ecf20Sopenharmony_ci DEBUGFS_ADD_FILE(rx_queue, dir, 0400); 29608c2ecf20Sopenharmony_ci DEBUGFS_ADD_FILE(tx_queue, dir, 0400); 29618c2ecf20Sopenharmony_ci DEBUGFS_ADD_FILE(interrupt, dir, 0600); 29628c2ecf20Sopenharmony_ci DEBUGFS_ADD_FILE(csr, dir, 0200); 29638c2ecf20Sopenharmony_ci DEBUGFS_ADD_FILE(fh_reg, dir, 0400); 29648c2ecf20Sopenharmony_ci DEBUGFS_ADD_FILE(rfkill, dir, 0600); 29658c2ecf20Sopenharmony_ci DEBUGFS_ADD_FILE(monitor_data, dir, 0400); 29668c2ecf20Sopenharmony_ci} 29678c2ecf20Sopenharmony_ci 29688c2ecf20Sopenharmony_cistatic void iwl_trans_pcie_debugfs_cleanup(struct iwl_trans *trans) 29698c2ecf20Sopenharmony_ci{ 29708c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 29718c2ecf20Sopenharmony_ci struct cont_rec *data = &trans_pcie->fw_mon_data; 29728c2ecf20Sopenharmony_ci 29738c2ecf20Sopenharmony_ci mutex_lock(&data->mutex); 29748c2ecf20Sopenharmony_ci data->state = IWL_FW_MON_DBGFS_STATE_DISABLED; 29758c2ecf20Sopenharmony_ci mutex_unlock(&data->mutex); 29768c2ecf20Sopenharmony_ci} 29778c2ecf20Sopenharmony_ci#endif /*CONFIG_IWLWIFI_DEBUGFS */ 29788c2ecf20Sopenharmony_ci 29798c2ecf20Sopenharmony_cistatic u32 iwl_trans_pcie_get_cmdlen(struct iwl_trans *trans, void *tfd) 29808c2ecf20Sopenharmony_ci{ 29818c2ecf20Sopenharmony_ci u32 cmdlen = 0; 29828c2ecf20Sopenharmony_ci int i; 29838c2ecf20Sopenharmony_ci 29848c2ecf20Sopenharmony_ci for (i = 0; i < trans->txqs.tfd.max_tbs; i++) 29858c2ecf20Sopenharmony_ci cmdlen += iwl_txq_gen1_tfd_tb_get_len(trans, tfd, i); 29868c2ecf20Sopenharmony_ci 29878c2ecf20Sopenharmony_ci return cmdlen; 29888c2ecf20Sopenharmony_ci} 29898c2ecf20Sopenharmony_ci 29908c2ecf20Sopenharmony_cistatic u32 iwl_trans_pcie_dump_rbs(struct iwl_trans *trans, 29918c2ecf20Sopenharmony_ci struct iwl_fw_error_dump_data **data, 29928c2ecf20Sopenharmony_ci int allocated_rb_nums) 29938c2ecf20Sopenharmony_ci{ 29948c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 29958c2ecf20Sopenharmony_ci int max_len = trans_pcie->rx_buf_bytes; 29968c2ecf20Sopenharmony_ci /* Dump RBs is supported only for pre-9000 devices (1 queue) */ 29978c2ecf20Sopenharmony_ci struct iwl_rxq *rxq = &trans_pcie->rxq[0]; 29988c2ecf20Sopenharmony_ci u32 i, r, j, rb_len = 0; 29998c2ecf20Sopenharmony_ci 30008c2ecf20Sopenharmony_ci spin_lock(&rxq->lock); 30018c2ecf20Sopenharmony_ci 30028c2ecf20Sopenharmony_ci r = le16_to_cpu(iwl_get_closed_rb_stts(trans, rxq)) & 0x0FFF; 30038c2ecf20Sopenharmony_ci 30048c2ecf20Sopenharmony_ci for (i = rxq->read, j = 0; 30058c2ecf20Sopenharmony_ci i != r && j < allocated_rb_nums; 30068c2ecf20Sopenharmony_ci i = (i + 1) & RX_QUEUE_MASK, j++) { 30078c2ecf20Sopenharmony_ci struct iwl_rx_mem_buffer *rxb = rxq->queue[i]; 30088c2ecf20Sopenharmony_ci struct iwl_fw_error_dump_rb *rb; 30098c2ecf20Sopenharmony_ci 30108c2ecf20Sopenharmony_ci dma_unmap_page(trans->dev, rxb->page_dma, max_len, 30118c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 30128c2ecf20Sopenharmony_ci 30138c2ecf20Sopenharmony_ci rb_len += sizeof(**data) + sizeof(*rb) + max_len; 30148c2ecf20Sopenharmony_ci 30158c2ecf20Sopenharmony_ci (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RB); 30168c2ecf20Sopenharmony_ci (*data)->len = cpu_to_le32(sizeof(*rb) + max_len); 30178c2ecf20Sopenharmony_ci rb = (void *)(*data)->data; 30188c2ecf20Sopenharmony_ci rb->index = cpu_to_le32(i); 30198c2ecf20Sopenharmony_ci memcpy(rb->data, page_address(rxb->page), max_len); 30208c2ecf20Sopenharmony_ci /* remap the page for the free benefit */ 30218c2ecf20Sopenharmony_ci rxb->page_dma = dma_map_page(trans->dev, rxb->page, 30228c2ecf20Sopenharmony_ci rxb->offset, max_len, 30238c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 30248c2ecf20Sopenharmony_ci 30258c2ecf20Sopenharmony_ci *data = iwl_fw_error_next_data(*data); 30268c2ecf20Sopenharmony_ci } 30278c2ecf20Sopenharmony_ci 30288c2ecf20Sopenharmony_ci spin_unlock(&rxq->lock); 30298c2ecf20Sopenharmony_ci 30308c2ecf20Sopenharmony_ci return rb_len; 30318c2ecf20Sopenharmony_ci} 30328c2ecf20Sopenharmony_ci#define IWL_CSR_TO_DUMP (0x250) 30338c2ecf20Sopenharmony_ci 30348c2ecf20Sopenharmony_cistatic u32 iwl_trans_pcie_dump_csr(struct iwl_trans *trans, 30358c2ecf20Sopenharmony_ci struct iwl_fw_error_dump_data **data) 30368c2ecf20Sopenharmony_ci{ 30378c2ecf20Sopenharmony_ci u32 csr_len = sizeof(**data) + IWL_CSR_TO_DUMP; 30388c2ecf20Sopenharmony_ci __le32 *val; 30398c2ecf20Sopenharmony_ci int i; 30408c2ecf20Sopenharmony_ci 30418c2ecf20Sopenharmony_ci (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_CSR); 30428c2ecf20Sopenharmony_ci (*data)->len = cpu_to_le32(IWL_CSR_TO_DUMP); 30438c2ecf20Sopenharmony_ci val = (void *)(*data)->data; 30448c2ecf20Sopenharmony_ci 30458c2ecf20Sopenharmony_ci for (i = 0; i < IWL_CSR_TO_DUMP; i += 4) 30468c2ecf20Sopenharmony_ci *val++ = cpu_to_le32(iwl_trans_pcie_read32(trans, i)); 30478c2ecf20Sopenharmony_ci 30488c2ecf20Sopenharmony_ci *data = iwl_fw_error_next_data(*data); 30498c2ecf20Sopenharmony_ci 30508c2ecf20Sopenharmony_ci return csr_len; 30518c2ecf20Sopenharmony_ci} 30528c2ecf20Sopenharmony_ci 30538c2ecf20Sopenharmony_cistatic u32 iwl_trans_pcie_fh_regs_dump(struct iwl_trans *trans, 30548c2ecf20Sopenharmony_ci struct iwl_fw_error_dump_data **data) 30558c2ecf20Sopenharmony_ci{ 30568c2ecf20Sopenharmony_ci u32 fh_regs_len = FH_MEM_UPPER_BOUND - FH_MEM_LOWER_BOUND; 30578c2ecf20Sopenharmony_ci unsigned long flags; 30588c2ecf20Sopenharmony_ci __le32 *val; 30598c2ecf20Sopenharmony_ci int i; 30608c2ecf20Sopenharmony_ci 30618c2ecf20Sopenharmony_ci if (!iwl_trans_grab_nic_access(trans, &flags)) 30628c2ecf20Sopenharmony_ci return 0; 30638c2ecf20Sopenharmony_ci 30648c2ecf20Sopenharmony_ci (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FH_REGS); 30658c2ecf20Sopenharmony_ci (*data)->len = cpu_to_le32(fh_regs_len); 30668c2ecf20Sopenharmony_ci val = (void *)(*data)->data; 30678c2ecf20Sopenharmony_ci 30688c2ecf20Sopenharmony_ci if (!trans->trans_cfg->gen2) 30698c2ecf20Sopenharmony_ci for (i = FH_MEM_LOWER_BOUND; i < FH_MEM_UPPER_BOUND; 30708c2ecf20Sopenharmony_ci i += sizeof(u32)) 30718c2ecf20Sopenharmony_ci *val++ = cpu_to_le32(iwl_trans_pcie_read32(trans, i)); 30728c2ecf20Sopenharmony_ci else 30738c2ecf20Sopenharmony_ci for (i = iwl_umac_prph(trans, FH_MEM_LOWER_BOUND_GEN2); 30748c2ecf20Sopenharmony_ci i < iwl_umac_prph(trans, FH_MEM_UPPER_BOUND_GEN2); 30758c2ecf20Sopenharmony_ci i += sizeof(u32)) 30768c2ecf20Sopenharmony_ci *val++ = cpu_to_le32(iwl_trans_pcie_read_prph(trans, 30778c2ecf20Sopenharmony_ci i)); 30788c2ecf20Sopenharmony_ci 30798c2ecf20Sopenharmony_ci iwl_trans_release_nic_access(trans, &flags); 30808c2ecf20Sopenharmony_ci 30818c2ecf20Sopenharmony_ci *data = iwl_fw_error_next_data(*data); 30828c2ecf20Sopenharmony_ci 30838c2ecf20Sopenharmony_ci return sizeof(**data) + fh_regs_len; 30848c2ecf20Sopenharmony_ci} 30858c2ecf20Sopenharmony_ci 30868c2ecf20Sopenharmony_cistatic u32 30878c2ecf20Sopenharmony_ciiwl_trans_pci_dump_marbh_monitor(struct iwl_trans *trans, 30888c2ecf20Sopenharmony_ci struct iwl_fw_error_dump_fw_mon *fw_mon_data, 30898c2ecf20Sopenharmony_ci u32 monitor_len) 30908c2ecf20Sopenharmony_ci{ 30918c2ecf20Sopenharmony_ci u32 buf_size_in_dwords = (monitor_len >> 2); 30928c2ecf20Sopenharmony_ci u32 *buffer = (u32 *)fw_mon_data->data; 30938c2ecf20Sopenharmony_ci unsigned long flags; 30948c2ecf20Sopenharmony_ci u32 i; 30958c2ecf20Sopenharmony_ci 30968c2ecf20Sopenharmony_ci if (!iwl_trans_grab_nic_access(trans, &flags)) 30978c2ecf20Sopenharmony_ci return 0; 30988c2ecf20Sopenharmony_ci 30998c2ecf20Sopenharmony_ci iwl_write_umac_prph_no_grab(trans, MON_DMARB_RD_CTL_ADDR, 0x1); 31008c2ecf20Sopenharmony_ci for (i = 0; i < buf_size_in_dwords; i++) 31018c2ecf20Sopenharmony_ci buffer[i] = iwl_read_umac_prph_no_grab(trans, 31028c2ecf20Sopenharmony_ci MON_DMARB_RD_DATA_ADDR); 31038c2ecf20Sopenharmony_ci iwl_write_umac_prph_no_grab(trans, MON_DMARB_RD_CTL_ADDR, 0x0); 31048c2ecf20Sopenharmony_ci 31058c2ecf20Sopenharmony_ci iwl_trans_release_nic_access(trans, &flags); 31068c2ecf20Sopenharmony_ci 31078c2ecf20Sopenharmony_ci return monitor_len; 31088c2ecf20Sopenharmony_ci} 31098c2ecf20Sopenharmony_ci 31108c2ecf20Sopenharmony_cistatic void 31118c2ecf20Sopenharmony_ciiwl_trans_pcie_dump_pointers(struct iwl_trans *trans, 31128c2ecf20Sopenharmony_ci struct iwl_fw_error_dump_fw_mon *fw_mon_data) 31138c2ecf20Sopenharmony_ci{ 31148c2ecf20Sopenharmony_ci u32 base, base_high, write_ptr, write_ptr_val, wrap_cnt; 31158c2ecf20Sopenharmony_ci 31168c2ecf20Sopenharmony_ci if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { 31178c2ecf20Sopenharmony_ci base = DBGC_CUR_DBGBUF_BASE_ADDR_LSB; 31188c2ecf20Sopenharmony_ci base_high = DBGC_CUR_DBGBUF_BASE_ADDR_MSB; 31198c2ecf20Sopenharmony_ci write_ptr = DBGC_CUR_DBGBUF_STATUS; 31208c2ecf20Sopenharmony_ci wrap_cnt = DBGC_DBGBUF_WRAP_AROUND; 31218c2ecf20Sopenharmony_ci } else if (trans->dbg.dest_tlv) { 31228c2ecf20Sopenharmony_ci write_ptr = le32_to_cpu(trans->dbg.dest_tlv->write_ptr_reg); 31238c2ecf20Sopenharmony_ci wrap_cnt = le32_to_cpu(trans->dbg.dest_tlv->wrap_count); 31248c2ecf20Sopenharmony_ci base = le32_to_cpu(trans->dbg.dest_tlv->base_reg); 31258c2ecf20Sopenharmony_ci } else { 31268c2ecf20Sopenharmony_ci base = MON_BUFF_BASE_ADDR; 31278c2ecf20Sopenharmony_ci write_ptr = MON_BUFF_WRPTR; 31288c2ecf20Sopenharmony_ci wrap_cnt = MON_BUFF_CYCLE_CNT; 31298c2ecf20Sopenharmony_ci } 31308c2ecf20Sopenharmony_ci 31318c2ecf20Sopenharmony_ci write_ptr_val = iwl_read_prph(trans, write_ptr); 31328c2ecf20Sopenharmony_ci fw_mon_data->fw_mon_cycle_cnt = 31338c2ecf20Sopenharmony_ci cpu_to_le32(iwl_read_prph(trans, wrap_cnt)); 31348c2ecf20Sopenharmony_ci fw_mon_data->fw_mon_base_ptr = 31358c2ecf20Sopenharmony_ci cpu_to_le32(iwl_read_prph(trans, base)); 31368c2ecf20Sopenharmony_ci if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { 31378c2ecf20Sopenharmony_ci fw_mon_data->fw_mon_base_high_ptr = 31388c2ecf20Sopenharmony_ci cpu_to_le32(iwl_read_prph(trans, base_high)); 31398c2ecf20Sopenharmony_ci write_ptr_val &= DBGC_CUR_DBGBUF_STATUS_OFFSET_MSK; 31408c2ecf20Sopenharmony_ci } 31418c2ecf20Sopenharmony_ci fw_mon_data->fw_mon_wr_ptr = cpu_to_le32(write_ptr_val); 31428c2ecf20Sopenharmony_ci} 31438c2ecf20Sopenharmony_ci 31448c2ecf20Sopenharmony_cistatic u32 31458c2ecf20Sopenharmony_ciiwl_trans_pcie_dump_monitor(struct iwl_trans *trans, 31468c2ecf20Sopenharmony_ci struct iwl_fw_error_dump_data **data, 31478c2ecf20Sopenharmony_ci u32 monitor_len) 31488c2ecf20Sopenharmony_ci{ 31498c2ecf20Sopenharmony_ci struct iwl_dram_data *fw_mon = &trans->dbg.fw_mon; 31508c2ecf20Sopenharmony_ci u32 len = 0; 31518c2ecf20Sopenharmony_ci 31528c2ecf20Sopenharmony_ci if (trans->dbg.dest_tlv || 31538c2ecf20Sopenharmony_ci (fw_mon->size && 31548c2ecf20Sopenharmony_ci (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_7000 || 31558c2ecf20Sopenharmony_ci trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210))) { 31568c2ecf20Sopenharmony_ci struct iwl_fw_error_dump_fw_mon *fw_mon_data; 31578c2ecf20Sopenharmony_ci 31588c2ecf20Sopenharmony_ci (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FW_MONITOR); 31598c2ecf20Sopenharmony_ci fw_mon_data = (void *)(*data)->data; 31608c2ecf20Sopenharmony_ci 31618c2ecf20Sopenharmony_ci iwl_trans_pcie_dump_pointers(trans, fw_mon_data); 31628c2ecf20Sopenharmony_ci 31638c2ecf20Sopenharmony_ci len += sizeof(**data) + sizeof(*fw_mon_data); 31648c2ecf20Sopenharmony_ci if (fw_mon->size) { 31658c2ecf20Sopenharmony_ci memcpy(fw_mon_data->data, fw_mon->block, fw_mon->size); 31668c2ecf20Sopenharmony_ci monitor_len = fw_mon->size; 31678c2ecf20Sopenharmony_ci } else if (trans->dbg.dest_tlv->monitor_mode == SMEM_MODE) { 31688c2ecf20Sopenharmony_ci u32 base = le32_to_cpu(fw_mon_data->fw_mon_base_ptr); 31698c2ecf20Sopenharmony_ci /* 31708c2ecf20Sopenharmony_ci * Update pointers to reflect actual values after 31718c2ecf20Sopenharmony_ci * shifting 31728c2ecf20Sopenharmony_ci */ 31738c2ecf20Sopenharmony_ci if (trans->dbg.dest_tlv->version) { 31748c2ecf20Sopenharmony_ci base = (iwl_read_prph(trans, base) & 31758c2ecf20Sopenharmony_ci IWL_LDBG_M2S_BUF_BA_MSK) << 31768c2ecf20Sopenharmony_ci trans->dbg.dest_tlv->base_shift; 31778c2ecf20Sopenharmony_ci base *= IWL_M2S_UNIT_SIZE; 31788c2ecf20Sopenharmony_ci base += trans->cfg->smem_offset; 31798c2ecf20Sopenharmony_ci } else { 31808c2ecf20Sopenharmony_ci base = iwl_read_prph(trans, base) << 31818c2ecf20Sopenharmony_ci trans->dbg.dest_tlv->base_shift; 31828c2ecf20Sopenharmony_ci } 31838c2ecf20Sopenharmony_ci 31848c2ecf20Sopenharmony_ci iwl_trans_read_mem(trans, base, fw_mon_data->data, 31858c2ecf20Sopenharmony_ci monitor_len / sizeof(u32)); 31868c2ecf20Sopenharmony_ci } else if (trans->dbg.dest_tlv->monitor_mode == MARBH_MODE) { 31878c2ecf20Sopenharmony_ci monitor_len = 31888c2ecf20Sopenharmony_ci iwl_trans_pci_dump_marbh_monitor(trans, 31898c2ecf20Sopenharmony_ci fw_mon_data, 31908c2ecf20Sopenharmony_ci monitor_len); 31918c2ecf20Sopenharmony_ci } else { 31928c2ecf20Sopenharmony_ci /* Didn't match anything - output no monitor data */ 31938c2ecf20Sopenharmony_ci monitor_len = 0; 31948c2ecf20Sopenharmony_ci } 31958c2ecf20Sopenharmony_ci 31968c2ecf20Sopenharmony_ci len += monitor_len; 31978c2ecf20Sopenharmony_ci (*data)->len = cpu_to_le32(monitor_len + sizeof(*fw_mon_data)); 31988c2ecf20Sopenharmony_ci } 31998c2ecf20Sopenharmony_ci 32008c2ecf20Sopenharmony_ci return len; 32018c2ecf20Sopenharmony_ci} 32028c2ecf20Sopenharmony_ci 32038c2ecf20Sopenharmony_cistatic int iwl_trans_get_fw_monitor_len(struct iwl_trans *trans, u32 *len) 32048c2ecf20Sopenharmony_ci{ 32058c2ecf20Sopenharmony_ci if (trans->dbg.fw_mon.size) { 32068c2ecf20Sopenharmony_ci *len += sizeof(struct iwl_fw_error_dump_data) + 32078c2ecf20Sopenharmony_ci sizeof(struct iwl_fw_error_dump_fw_mon) + 32088c2ecf20Sopenharmony_ci trans->dbg.fw_mon.size; 32098c2ecf20Sopenharmony_ci return trans->dbg.fw_mon.size; 32108c2ecf20Sopenharmony_ci } else if (trans->dbg.dest_tlv) { 32118c2ecf20Sopenharmony_ci u32 base, end, cfg_reg, monitor_len; 32128c2ecf20Sopenharmony_ci 32138c2ecf20Sopenharmony_ci if (trans->dbg.dest_tlv->version == 1) { 32148c2ecf20Sopenharmony_ci cfg_reg = le32_to_cpu(trans->dbg.dest_tlv->base_reg); 32158c2ecf20Sopenharmony_ci cfg_reg = iwl_read_prph(trans, cfg_reg); 32168c2ecf20Sopenharmony_ci base = (cfg_reg & IWL_LDBG_M2S_BUF_BA_MSK) << 32178c2ecf20Sopenharmony_ci trans->dbg.dest_tlv->base_shift; 32188c2ecf20Sopenharmony_ci base *= IWL_M2S_UNIT_SIZE; 32198c2ecf20Sopenharmony_ci base += trans->cfg->smem_offset; 32208c2ecf20Sopenharmony_ci 32218c2ecf20Sopenharmony_ci monitor_len = 32228c2ecf20Sopenharmony_ci (cfg_reg & IWL_LDBG_M2S_BUF_SIZE_MSK) >> 32238c2ecf20Sopenharmony_ci trans->dbg.dest_tlv->end_shift; 32248c2ecf20Sopenharmony_ci monitor_len *= IWL_M2S_UNIT_SIZE; 32258c2ecf20Sopenharmony_ci } else { 32268c2ecf20Sopenharmony_ci base = le32_to_cpu(trans->dbg.dest_tlv->base_reg); 32278c2ecf20Sopenharmony_ci end = le32_to_cpu(trans->dbg.dest_tlv->end_reg); 32288c2ecf20Sopenharmony_ci 32298c2ecf20Sopenharmony_ci base = iwl_read_prph(trans, base) << 32308c2ecf20Sopenharmony_ci trans->dbg.dest_tlv->base_shift; 32318c2ecf20Sopenharmony_ci end = iwl_read_prph(trans, end) << 32328c2ecf20Sopenharmony_ci trans->dbg.dest_tlv->end_shift; 32338c2ecf20Sopenharmony_ci 32348c2ecf20Sopenharmony_ci /* Make "end" point to the actual end */ 32358c2ecf20Sopenharmony_ci if (trans->trans_cfg->device_family >= 32368c2ecf20Sopenharmony_ci IWL_DEVICE_FAMILY_8000 || 32378c2ecf20Sopenharmony_ci trans->dbg.dest_tlv->monitor_mode == MARBH_MODE) 32388c2ecf20Sopenharmony_ci end += (1 << trans->dbg.dest_tlv->end_shift); 32398c2ecf20Sopenharmony_ci monitor_len = end - base; 32408c2ecf20Sopenharmony_ci } 32418c2ecf20Sopenharmony_ci *len += sizeof(struct iwl_fw_error_dump_data) + 32428c2ecf20Sopenharmony_ci sizeof(struct iwl_fw_error_dump_fw_mon) + 32438c2ecf20Sopenharmony_ci monitor_len; 32448c2ecf20Sopenharmony_ci return monitor_len; 32458c2ecf20Sopenharmony_ci } 32468c2ecf20Sopenharmony_ci return 0; 32478c2ecf20Sopenharmony_ci} 32488c2ecf20Sopenharmony_ci 32498c2ecf20Sopenharmony_cistatic struct iwl_trans_dump_data 32508c2ecf20Sopenharmony_ci*iwl_trans_pcie_dump_data(struct iwl_trans *trans, 32518c2ecf20Sopenharmony_ci u32 dump_mask) 32528c2ecf20Sopenharmony_ci{ 32538c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 32548c2ecf20Sopenharmony_ci struct iwl_fw_error_dump_data *data; 32558c2ecf20Sopenharmony_ci struct iwl_txq *cmdq = trans->txqs.txq[trans->txqs.cmd.q_id]; 32568c2ecf20Sopenharmony_ci struct iwl_fw_error_dump_txcmd *txcmd; 32578c2ecf20Sopenharmony_ci struct iwl_trans_dump_data *dump_data; 32588c2ecf20Sopenharmony_ci u32 len, num_rbs = 0, monitor_len = 0; 32598c2ecf20Sopenharmony_ci int i, ptr; 32608c2ecf20Sopenharmony_ci bool dump_rbs = test_bit(STATUS_FW_ERROR, &trans->status) && 32618c2ecf20Sopenharmony_ci !trans->trans_cfg->mq_rx_supported && 32628c2ecf20Sopenharmony_ci dump_mask & BIT(IWL_FW_ERROR_DUMP_RB); 32638c2ecf20Sopenharmony_ci 32648c2ecf20Sopenharmony_ci if (!dump_mask) 32658c2ecf20Sopenharmony_ci return NULL; 32668c2ecf20Sopenharmony_ci 32678c2ecf20Sopenharmony_ci /* transport dump header */ 32688c2ecf20Sopenharmony_ci len = sizeof(*dump_data); 32698c2ecf20Sopenharmony_ci 32708c2ecf20Sopenharmony_ci /* host commands */ 32718c2ecf20Sopenharmony_ci if (dump_mask & BIT(IWL_FW_ERROR_DUMP_TXCMD) && cmdq) 32728c2ecf20Sopenharmony_ci len += sizeof(*data) + 32738c2ecf20Sopenharmony_ci cmdq->n_window * (sizeof(*txcmd) + 32748c2ecf20Sopenharmony_ci TFD_MAX_PAYLOAD_SIZE); 32758c2ecf20Sopenharmony_ci 32768c2ecf20Sopenharmony_ci /* FW monitor */ 32778c2ecf20Sopenharmony_ci if (dump_mask & BIT(IWL_FW_ERROR_DUMP_FW_MONITOR)) 32788c2ecf20Sopenharmony_ci monitor_len = iwl_trans_get_fw_monitor_len(trans, &len); 32798c2ecf20Sopenharmony_ci 32808c2ecf20Sopenharmony_ci /* CSR registers */ 32818c2ecf20Sopenharmony_ci if (dump_mask & BIT(IWL_FW_ERROR_DUMP_CSR)) 32828c2ecf20Sopenharmony_ci len += sizeof(*data) + IWL_CSR_TO_DUMP; 32838c2ecf20Sopenharmony_ci 32848c2ecf20Sopenharmony_ci /* FH registers */ 32858c2ecf20Sopenharmony_ci if (dump_mask & BIT(IWL_FW_ERROR_DUMP_FH_REGS)) { 32868c2ecf20Sopenharmony_ci if (trans->trans_cfg->gen2) 32878c2ecf20Sopenharmony_ci len += sizeof(*data) + 32888c2ecf20Sopenharmony_ci (iwl_umac_prph(trans, FH_MEM_UPPER_BOUND_GEN2) - 32898c2ecf20Sopenharmony_ci iwl_umac_prph(trans, FH_MEM_LOWER_BOUND_GEN2)); 32908c2ecf20Sopenharmony_ci else 32918c2ecf20Sopenharmony_ci len += sizeof(*data) + 32928c2ecf20Sopenharmony_ci (FH_MEM_UPPER_BOUND - 32938c2ecf20Sopenharmony_ci FH_MEM_LOWER_BOUND); 32948c2ecf20Sopenharmony_ci } 32958c2ecf20Sopenharmony_ci 32968c2ecf20Sopenharmony_ci if (dump_rbs) { 32978c2ecf20Sopenharmony_ci /* Dump RBs is supported only for pre-9000 devices (1 queue) */ 32988c2ecf20Sopenharmony_ci struct iwl_rxq *rxq = &trans_pcie->rxq[0]; 32998c2ecf20Sopenharmony_ci /* RBs */ 33008c2ecf20Sopenharmony_ci num_rbs = 33018c2ecf20Sopenharmony_ci le16_to_cpu(iwl_get_closed_rb_stts(trans, rxq)) 33028c2ecf20Sopenharmony_ci & 0x0FFF; 33038c2ecf20Sopenharmony_ci num_rbs = (num_rbs - rxq->read) & RX_QUEUE_MASK; 33048c2ecf20Sopenharmony_ci len += num_rbs * (sizeof(*data) + 33058c2ecf20Sopenharmony_ci sizeof(struct iwl_fw_error_dump_rb) + 33068c2ecf20Sopenharmony_ci (PAGE_SIZE << trans_pcie->rx_page_order)); 33078c2ecf20Sopenharmony_ci } 33088c2ecf20Sopenharmony_ci 33098c2ecf20Sopenharmony_ci /* Paged memory for gen2 HW */ 33108c2ecf20Sopenharmony_ci if (trans->trans_cfg->gen2 && dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING)) 33118c2ecf20Sopenharmony_ci for (i = 0; i < trans->init_dram.paging_cnt; i++) 33128c2ecf20Sopenharmony_ci len += sizeof(*data) + 33138c2ecf20Sopenharmony_ci sizeof(struct iwl_fw_error_dump_paging) + 33148c2ecf20Sopenharmony_ci trans->init_dram.paging[i].size; 33158c2ecf20Sopenharmony_ci 33168c2ecf20Sopenharmony_ci dump_data = vzalloc(len); 33178c2ecf20Sopenharmony_ci if (!dump_data) 33188c2ecf20Sopenharmony_ci return NULL; 33198c2ecf20Sopenharmony_ci 33208c2ecf20Sopenharmony_ci len = 0; 33218c2ecf20Sopenharmony_ci data = (void *)dump_data->data; 33228c2ecf20Sopenharmony_ci 33238c2ecf20Sopenharmony_ci if (dump_mask & BIT(IWL_FW_ERROR_DUMP_TXCMD) && cmdq) { 33248c2ecf20Sopenharmony_ci u16 tfd_size = trans->txqs.tfd.size; 33258c2ecf20Sopenharmony_ci 33268c2ecf20Sopenharmony_ci data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXCMD); 33278c2ecf20Sopenharmony_ci txcmd = (void *)data->data; 33288c2ecf20Sopenharmony_ci spin_lock_bh(&cmdq->lock); 33298c2ecf20Sopenharmony_ci ptr = cmdq->write_ptr; 33308c2ecf20Sopenharmony_ci for (i = 0; i < cmdq->n_window; i++) { 33318c2ecf20Sopenharmony_ci u8 idx = iwl_txq_get_cmd_index(cmdq, ptr); 33328c2ecf20Sopenharmony_ci u8 tfdidx; 33338c2ecf20Sopenharmony_ci u32 caplen, cmdlen; 33348c2ecf20Sopenharmony_ci 33358c2ecf20Sopenharmony_ci if (trans->trans_cfg->use_tfh) 33368c2ecf20Sopenharmony_ci tfdidx = idx; 33378c2ecf20Sopenharmony_ci else 33388c2ecf20Sopenharmony_ci tfdidx = ptr; 33398c2ecf20Sopenharmony_ci 33408c2ecf20Sopenharmony_ci cmdlen = iwl_trans_pcie_get_cmdlen(trans, 33418c2ecf20Sopenharmony_ci (u8 *)cmdq->tfds + 33428c2ecf20Sopenharmony_ci tfd_size * tfdidx); 33438c2ecf20Sopenharmony_ci caplen = min_t(u32, TFD_MAX_PAYLOAD_SIZE, cmdlen); 33448c2ecf20Sopenharmony_ci 33458c2ecf20Sopenharmony_ci if (cmdlen) { 33468c2ecf20Sopenharmony_ci len += sizeof(*txcmd) + caplen; 33478c2ecf20Sopenharmony_ci txcmd->cmdlen = cpu_to_le32(cmdlen); 33488c2ecf20Sopenharmony_ci txcmd->caplen = cpu_to_le32(caplen); 33498c2ecf20Sopenharmony_ci memcpy(txcmd->data, cmdq->entries[idx].cmd, 33508c2ecf20Sopenharmony_ci caplen); 33518c2ecf20Sopenharmony_ci txcmd = (void *)((u8 *)txcmd->data + caplen); 33528c2ecf20Sopenharmony_ci } 33538c2ecf20Sopenharmony_ci 33548c2ecf20Sopenharmony_ci ptr = iwl_txq_dec_wrap(trans, ptr); 33558c2ecf20Sopenharmony_ci } 33568c2ecf20Sopenharmony_ci spin_unlock_bh(&cmdq->lock); 33578c2ecf20Sopenharmony_ci 33588c2ecf20Sopenharmony_ci data->len = cpu_to_le32(len); 33598c2ecf20Sopenharmony_ci len += sizeof(*data); 33608c2ecf20Sopenharmony_ci data = iwl_fw_error_next_data(data); 33618c2ecf20Sopenharmony_ci } 33628c2ecf20Sopenharmony_ci 33638c2ecf20Sopenharmony_ci if (dump_mask & BIT(IWL_FW_ERROR_DUMP_CSR)) 33648c2ecf20Sopenharmony_ci len += iwl_trans_pcie_dump_csr(trans, &data); 33658c2ecf20Sopenharmony_ci if (dump_mask & BIT(IWL_FW_ERROR_DUMP_FH_REGS)) 33668c2ecf20Sopenharmony_ci len += iwl_trans_pcie_fh_regs_dump(trans, &data); 33678c2ecf20Sopenharmony_ci if (dump_rbs) 33688c2ecf20Sopenharmony_ci len += iwl_trans_pcie_dump_rbs(trans, &data, num_rbs); 33698c2ecf20Sopenharmony_ci 33708c2ecf20Sopenharmony_ci /* Paged memory for gen2 HW */ 33718c2ecf20Sopenharmony_ci if (trans->trans_cfg->gen2 && 33728c2ecf20Sopenharmony_ci dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING)) { 33738c2ecf20Sopenharmony_ci for (i = 0; i < trans->init_dram.paging_cnt; i++) { 33748c2ecf20Sopenharmony_ci struct iwl_fw_error_dump_paging *paging; 33758c2ecf20Sopenharmony_ci u32 page_len = trans->init_dram.paging[i].size; 33768c2ecf20Sopenharmony_ci 33778c2ecf20Sopenharmony_ci data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING); 33788c2ecf20Sopenharmony_ci data->len = cpu_to_le32(sizeof(*paging) + page_len); 33798c2ecf20Sopenharmony_ci paging = (void *)data->data; 33808c2ecf20Sopenharmony_ci paging->index = cpu_to_le32(i); 33818c2ecf20Sopenharmony_ci memcpy(paging->data, 33828c2ecf20Sopenharmony_ci trans->init_dram.paging[i].block, page_len); 33838c2ecf20Sopenharmony_ci data = iwl_fw_error_next_data(data); 33848c2ecf20Sopenharmony_ci 33858c2ecf20Sopenharmony_ci len += sizeof(*data) + sizeof(*paging) + page_len; 33868c2ecf20Sopenharmony_ci } 33878c2ecf20Sopenharmony_ci } 33888c2ecf20Sopenharmony_ci if (dump_mask & BIT(IWL_FW_ERROR_DUMP_FW_MONITOR)) 33898c2ecf20Sopenharmony_ci len += iwl_trans_pcie_dump_monitor(trans, &data, monitor_len); 33908c2ecf20Sopenharmony_ci 33918c2ecf20Sopenharmony_ci dump_data->len = len; 33928c2ecf20Sopenharmony_ci 33938c2ecf20Sopenharmony_ci return dump_data; 33948c2ecf20Sopenharmony_ci} 33958c2ecf20Sopenharmony_ci 33968c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 33978c2ecf20Sopenharmony_cistatic int iwl_trans_pcie_suspend(struct iwl_trans *trans) 33988c2ecf20Sopenharmony_ci{ 33998c2ecf20Sopenharmony_ci return 0; 34008c2ecf20Sopenharmony_ci} 34018c2ecf20Sopenharmony_ci 34028c2ecf20Sopenharmony_cistatic void iwl_trans_pcie_resume(struct iwl_trans *trans) 34038c2ecf20Sopenharmony_ci{ 34048c2ecf20Sopenharmony_ci} 34058c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 34068c2ecf20Sopenharmony_ci 34078c2ecf20Sopenharmony_ci#define IWL_TRANS_COMMON_OPS \ 34088c2ecf20Sopenharmony_ci .op_mode_leave = iwl_trans_pcie_op_mode_leave, \ 34098c2ecf20Sopenharmony_ci .write8 = iwl_trans_pcie_write8, \ 34108c2ecf20Sopenharmony_ci .write32 = iwl_trans_pcie_write32, \ 34118c2ecf20Sopenharmony_ci .read32 = iwl_trans_pcie_read32, \ 34128c2ecf20Sopenharmony_ci .read_prph = iwl_trans_pcie_read_prph, \ 34138c2ecf20Sopenharmony_ci .write_prph = iwl_trans_pcie_write_prph, \ 34148c2ecf20Sopenharmony_ci .read_mem = iwl_trans_pcie_read_mem, \ 34158c2ecf20Sopenharmony_ci .write_mem = iwl_trans_pcie_write_mem, \ 34168c2ecf20Sopenharmony_ci .read_config32 = iwl_trans_pcie_read_config32, \ 34178c2ecf20Sopenharmony_ci .configure = iwl_trans_pcie_configure, \ 34188c2ecf20Sopenharmony_ci .set_pmi = iwl_trans_pcie_set_pmi, \ 34198c2ecf20Sopenharmony_ci .sw_reset = iwl_trans_pcie_sw_reset, \ 34208c2ecf20Sopenharmony_ci .grab_nic_access = iwl_trans_pcie_grab_nic_access, \ 34218c2ecf20Sopenharmony_ci .release_nic_access = iwl_trans_pcie_release_nic_access, \ 34228c2ecf20Sopenharmony_ci .set_bits_mask = iwl_trans_pcie_set_bits_mask, \ 34238c2ecf20Sopenharmony_ci .dump_data = iwl_trans_pcie_dump_data, \ 34248c2ecf20Sopenharmony_ci .d3_suspend = iwl_trans_pcie_d3_suspend, \ 34258c2ecf20Sopenharmony_ci .d3_resume = iwl_trans_pcie_d3_resume, \ 34268c2ecf20Sopenharmony_ci .sync_nmi = iwl_trans_pcie_sync_nmi 34278c2ecf20Sopenharmony_ci 34288c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 34298c2ecf20Sopenharmony_ci#define IWL_TRANS_PM_OPS \ 34308c2ecf20Sopenharmony_ci .suspend = iwl_trans_pcie_suspend, \ 34318c2ecf20Sopenharmony_ci .resume = iwl_trans_pcie_resume, 34328c2ecf20Sopenharmony_ci#else 34338c2ecf20Sopenharmony_ci#define IWL_TRANS_PM_OPS 34348c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 34358c2ecf20Sopenharmony_ci 34368c2ecf20Sopenharmony_cistatic const struct iwl_trans_ops trans_ops_pcie = { 34378c2ecf20Sopenharmony_ci IWL_TRANS_COMMON_OPS, 34388c2ecf20Sopenharmony_ci IWL_TRANS_PM_OPS 34398c2ecf20Sopenharmony_ci .start_hw = iwl_trans_pcie_start_hw, 34408c2ecf20Sopenharmony_ci .fw_alive = iwl_trans_pcie_fw_alive, 34418c2ecf20Sopenharmony_ci .start_fw = iwl_trans_pcie_start_fw, 34428c2ecf20Sopenharmony_ci .stop_device = iwl_trans_pcie_stop_device, 34438c2ecf20Sopenharmony_ci 34448c2ecf20Sopenharmony_ci .send_cmd = iwl_trans_pcie_send_hcmd, 34458c2ecf20Sopenharmony_ci 34468c2ecf20Sopenharmony_ci .tx = iwl_trans_pcie_tx, 34478c2ecf20Sopenharmony_ci .reclaim = iwl_trans_pcie_reclaim, 34488c2ecf20Sopenharmony_ci 34498c2ecf20Sopenharmony_ci .txq_disable = iwl_trans_pcie_txq_disable, 34508c2ecf20Sopenharmony_ci .txq_enable = iwl_trans_pcie_txq_enable, 34518c2ecf20Sopenharmony_ci 34528c2ecf20Sopenharmony_ci .txq_set_shared_mode = iwl_trans_pcie_txq_set_shared_mode, 34538c2ecf20Sopenharmony_ci 34548c2ecf20Sopenharmony_ci .wait_tx_queues_empty = iwl_trans_pcie_wait_txqs_empty, 34558c2ecf20Sopenharmony_ci 34568c2ecf20Sopenharmony_ci .freeze_txq_timer = iwl_trans_pcie_freeze_txq_timer, 34578c2ecf20Sopenharmony_ci .block_txq_ptrs = iwl_trans_pcie_block_txq_ptrs, 34588c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS 34598c2ecf20Sopenharmony_ci .debugfs_cleanup = iwl_trans_pcie_debugfs_cleanup, 34608c2ecf20Sopenharmony_ci#endif 34618c2ecf20Sopenharmony_ci}; 34628c2ecf20Sopenharmony_ci 34638c2ecf20Sopenharmony_cistatic const struct iwl_trans_ops trans_ops_pcie_gen2 = { 34648c2ecf20Sopenharmony_ci IWL_TRANS_COMMON_OPS, 34658c2ecf20Sopenharmony_ci IWL_TRANS_PM_OPS 34668c2ecf20Sopenharmony_ci .start_hw = iwl_trans_pcie_start_hw, 34678c2ecf20Sopenharmony_ci .fw_alive = iwl_trans_pcie_gen2_fw_alive, 34688c2ecf20Sopenharmony_ci .start_fw = iwl_trans_pcie_gen2_start_fw, 34698c2ecf20Sopenharmony_ci .stop_device = iwl_trans_pcie_gen2_stop_device, 34708c2ecf20Sopenharmony_ci 34718c2ecf20Sopenharmony_ci .send_cmd = iwl_trans_pcie_gen2_send_hcmd, 34728c2ecf20Sopenharmony_ci 34738c2ecf20Sopenharmony_ci .tx = iwl_txq_gen2_tx, 34748c2ecf20Sopenharmony_ci .reclaim = iwl_trans_pcie_reclaim, 34758c2ecf20Sopenharmony_ci 34768c2ecf20Sopenharmony_ci .set_q_ptrs = iwl_trans_pcie_set_q_ptrs, 34778c2ecf20Sopenharmony_ci 34788c2ecf20Sopenharmony_ci .txq_alloc = iwl_txq_dyn_alloc, 34798c2ecf20Sopenharmony_ci .txq_free = iwl_txq_dyn_free, 34808c2ecf20Sopenharmony_ci .wait_txq_empty = iwl_trans_pcie_wait_txq_empty, 34818c2ecf20Sopenharmony_ci .rxq_dma_data = iwl_trans_pcie_rxq_dma_data, 34828c2ecf20Sopenharmony_ci .set_pnvm = iwl_trans_pcie_ctx_info_gen3_set_pnvm, 34838c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS 34848c2ecf20Sopenharmony_ci .debugfs_cleanup = iwl_trans_pcie_debugfs_cleanup, 34858c2ecf20Sopenharmony_ci#endif 34868c2ecf20Sopenharmony_ci}; 34878c2ecf20Sopenharmony_ci 34888c2ecf20Sopenharmony_cistruct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, 34898c2ecf20Sopenharmony_ci const struct pci_device_id *ent, 34908c2ecf20Sopenharmony_ci const struct iwl_cfg_trans_params *cfg_trans) 34918c2ecf20Sopenharmony_ci{ 34928c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie; 34938c2ecf20Sopenharmony_ci struct iwl_trans *trans; 34948c2ecf20Sopenharmony_ci int ret, addr_size; 34958c2ecf20Sopenharmony_ci const struct iwl_trans_ops *ops = &trans_ops_pcie_gen2; 34968c2ecf20Sopenharmony_ci 34978c2ecf20Sopenharmony_ci if (!cfg_trans->gen2) 34988c2ecf20Sopenharmony_ci ops = &trans_ops_pcie; 34998c2ecf20Sopenharmony_ci 35008c2ecf20Sopenharmony_ci ret = pcim_enable_device(pdev); 35018c2ecf20Sopenharmony_ci if (ret) 35028c2ecf20Sopenharmony_ci return ERR_PTR(ret); 35038c2ecf20Sopenharmony_ci 35048c2ecf20Sopenharmony_ci trans = iwl_trans_alloc(sizeof(struct iwl_trans_pcie), &pdev->dev, ops, 35058c2ecf20Sopenharmony_ci cfg_trans); 35068c2ecf20Sopenharmony_ci if (!trans) 35078c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 35088c2ecf20Sopenharmony_ci 35098c2ecf20Sopenharmony_ci trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 35108c2ecf20Sopenharmony_ci 35118c2ecf20Sopenharmony_ci trans_pcie->trans = trans; 35128c2ecf20Sopenharmony_ci trans_pcie->opmode_down = true; 35138c2ecf20Sopenharmony_ci spin_lock_init(&trans_pcie->irq_lock); 35148c2ecf20Sopenharmony_ci spin_lock_init(&trans_pcie->reg_lock); 35158c2ecf20Sopenharmony_ci spin_lock_init(&trans_pcie->alloc_page_lock); 35168c2ecf20Sopenharmony_ci mutex_init(&trans_pcie->mutex); 35178c2ecf20Sopenharmony_ci init_waitqueue_head(&trans_pcie->ucode_write_waitq); 35188c2ecf20Sopenharmony_ci 35198c2ecf20Sopenharmony_ci trans_pcie->rba.alloc_wq = alloc_workqueue("rb_allocator", 35208c2ecf20Sopenharmony_ci WQ_HIGHPRI | WQ_UNBOUND, 1); 35218c2ecf20Sopenharmony_ci if (!trans_pcie->rba.alloc_wq) { 35228c2ecf20Sopenharmony_ci ret = -ENOMEM; 35238c2ecf20Sopenharmony_ci goto out_free_trans; 35248c2ecf20Sopenharmony_ci } 35258c2ecf20Sopenharmony_ci INIT_WORK(&trans_pcie->rba.rx_alloc, iwl_pcie_rx_allocator_work); 35268c2ecf20Sopenharmony_ci 35278c2ecf20Sopenharmony_ci trans_pcie->debug_rfkill = -1; 35288c2ecf20Sopenharmony_ci 35298c2ecf20Sopenharmony_ci if (!cfg_trans->base_params->pcie_l1_allowed) { 35308c2ecf20Sopenharmony_ci /* 35318c2ecf20Sopenharmony_ci * W/A - seems to solve weird behavior. We need to remove this 35328c2ecf20Sopenharmony_ci * if we don't want to stay in L1 all the time. This wastes a 35338c2ecf20Sopenharmony_ci * lot of power. 35348c2ecf20Sopenharmony_ci */ 35358c2ecf20Sopenharmony_ci pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | 35368c2ecf20Sopenharmony_ci PCIE_LINK_STATE_L1 | 35378c2ecf20Sopenharmony_ci PCIE_LINK_STATE_CLKPM); 35388c2ecf20Sopenharmony_ci } 35398c2ecf20Sopenharmony_ci 35408c2ecf20Sopenharmony_ci trans_pcie->def_rx_queue = 0; 35418c2ecf20Sopenharmony_ci 35428c2ecf20Sopenharmony_ci pci_set_master(pdev); 35438c2ecf20Sopenharmony_ci 35448c2ecf20Sopenharmony_ci addr_size = trans->txqs.tfd.addr_size; 35458c2ecf20Sopenharmony_ci ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(addr_size)); 35468c2ecf20Sopenharmony_ci if (!ret) 35478c2ecf20Sopenharmony_ci ret = pci_set_consistent_dma_mask(pdev, 35488c2ecf20Sopenharmony_ci DMA_BIT_MASK(addr_size)); 35498c2ecf20Sopenharmony_ci if (ret) { 35508c2ecf20Sopenharmony_ci ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); 35518c2ecf20Sopenharmony_ci if (!ret) 35528c2ecf20Sopenharmony_ci ret = pci_set_consistent_dma_mask(pdev, 35538c2ecf20Sopenharmony_ci DMA_BIT_MASK(32)); 35548c2ecf20Sopenharmony_ci /* both attempts failed: */ 35558c2ecf20Sopenharmony_ci if (ret) { 35568c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No suitable DMA available\n"); 35578c2ecf20Sopenharmony_ci goto out_no_pci; 35588c2ecf20Sopenharmony_ci } 35598c2ecf20Sopenharmony_ci } 35608c2ecf20Sopenharmony_ci 35618c2ecf20Sopenharmony_ci ret = pcim_iomap_regions_request_all(pdev, BIT(0), DRV_NAME); 35628c2ecf20Sopenharmony_ci if (ret) { 35638c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "pcim_iomap_regions_request_all failed\n"); 35648c2ecf20Sopenharmony_ci goto out_no_pci; 35658c2ecf20Sopenharmony_ci } 35668c2ecf20Sopenharmony_ci 35678c2ecf20Sopenharmony_ci trans_pcie->hw_base = pcim_iomap_table(pdev)[0]; 35688c2ecf20Sopenharmony_ci if (!trans_pcie->hw_base) { 35698c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "pcim_iomap_table failed\n"); 35708c2ecf20Sopenharmony_ci ret = -ENODEV; 35718c2ecf20Sopenharmony_ci goto out_no_pci; 35728c2ecf20Sopenharmony_ci } 35738c2ecf20Sopenharmony_ci 35748c2ecf20Sopenharmony_ci /* We disable the RETRY_TIMEOUT register (0x41) to keep 35758c2ecf20Sopenharmony_ci * PCI Tx retries from interfering with C3 CPU state */ 35768c2ecf20Sopenharmony_ci pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); 35778c2ecf20Sopenharmony_ci 35788c2ecf20Sopenharmony_ci trans_pcie->pci_dev = pdev; 35798c2ecf20Sopenharmony_ci iwl_disable_interrupts(trans); 35808c2ecf20Sopenharmony_ci 35818c2ecf20Sopenharmony_ci trans->hw_rev = iwl_read32(trans, CSR_HW_REV); 35828c2ecf20Sopenharmony_ci if (trans->hw_rev == 0xffffffff) { 35838c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "HW_REV=0xFFFFFFFF, PCI issues?\n"); 35848c2ecf20Sopenharmony_ci ret = -EIO; 35858c2ecf20Sopenharmony_ci goto out_no_pci; 35868c2ecf20Sopenharmony_ci } 35878c2ecf20Sopenharmony_ci 35888c2ecf20Sopenharmony_ci /* 35898c2ecf20Sopenharmony_ci * In the 8000 HW family the format of the 4 bytes of CSR_HW_REV have 35908c2ecf20Sopenharmony_ci * changed, and now the revision step also includes bit 0-1 (no more 35918c2ecf20Sopenharmony_ci * "dash" value). To keep hw_rev backwards compatible - we'll store it 35928c2ecf20Sopenharmony_ci * in the old format. 35938c2ecf20Sopenharmony_ci */ 35948c2ecf20Sopenharmony_ci if (cfg_trans->device_family >= IWL_DEVICE_FAMILY_8000) { 35958c2ecf20Sopenharmony_ci trans->hw_rev = (trans->hw_rev & 0xfff0) | 35968c2ecf20Sopenharmony_ci (CSR_HW_REV_STEP(trans->hw_rev << 2) << 2); 35978c2ecf20Sopenharmony_ci 35988c2ecf20Sopenharmony_ci ret = iwl_pcie_prepare_card_hw(trans); 35998c2ecf20Sopenharmony_ci if (ret) { 36008c2ecf20Sopenharmony_ci IWL_WARN(trans, "Exit HW not ready\n"); 36018c2ecf20Sopenharmony_ci goto out_no_pci; 36028c2ecf20Sopenharmony_ci } 36038c2ecf20Sopenharmony_ci 36048c2ecf20Sopenharmony_ci /* 36058c2ecf20Sopenharmony_ci * in-order to recognize C step driver should read chip version 36068c2ecf20Sopenharmony_ci * id located at the AUX bus MISC address space. 36078c2ecf20Sopenharmony_ci */ 36088c2ecf20Sopenharmony_ci ret = iwl_finish_nic_init(trans, cfg_trans); 36098c2ecf20Sopenharmony_ci if (ret) 36108c2ecf20Sopenharmony_ci goto out_no_pci; 36118c2ecf20Sopenharmony_ci 36128c2ecf20Sopenharmony_ci } 36138c2ecf20Sopenharmony_ci 36148c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(trans, "HW REV: 0x%0x\n", trans->hw_rev); 36158c2ecf20Sopenharmony_ci 36168c2ecf20Sopenharmony_ci iwl_pcie_set_interrupt_capa(pdev, trans, cfg_trans); 36178c2ecf20Sopenharmony_ci trans->hw_id = (pdev->device << 16) + pdev->subsystem_device; 36188c2ecf20Sopenharmony_ci snprintf(trans->hw_id_str, sizeof(trans->hw_id_str), 36198c2ecf20Sopenharmony_ci "PCI ID: 0x%04X:0x%04X", pdev->device, pdev->subsystem_device); 36208c2ecf20Sopenharmony_ci 36218c2ecf20Sopenharmony_ci /* Initialize the wait queue for commands */ 36228c2ecf20Sopenharmony_ci init_waitqueue_head(&trans_pcie->wait_command_queue); 36238c2ecf20Sopenharmony_ci 36248c2ecf20Sopenharmony_ci init_waitqueue_head(&trans_pcie->sx_waitq); 36258c2ecf20Sopenharmony_ci 36268c2ecf20Sopenharmony_ci 36278c2ecf20Sopenharmony_ci if (trans_pcie->msix_enabled) { 36288c2ecf20Sopenharmony_ci ret = iwl_pcie_init_msix_handler(pdev, trans_pcie); 36298c2ecf20Sopenharmony_ci if (ret) 36308c2ecf20Sopenharmony_ci goto out_no_pci; 36318c2ecf20Sopenharmony_ci } else { 36328c2ecf20Sopenharmony_ci ret = iwl_pcie_alloc_ict(trans); 36338c2ecf20Sopenharmony_ci if (ret) 36348c2ecf20Sopenharmony_ci goto out_no_pci; 36358c2ecf20Sopenharmony_ci 36368c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, pdev->irq, 36378c2ecf20Sopenharmony_ci iwl_pcie_isr, 36388c2ecf20Sopenharmony_ci iwl_pcie_irq_handler, 36398c2ecf20Sopenharmony_ci IRQF_SHARED, DRV_NAME, trans); 36408c2ecf20Sopenharmony_ci if (ret) { 36418c2ecf20Sopenharmony_ci IWL_ERR(trans, "Error allocating IRQ %d\n", pdev->irq); 36428c2ecf20Sopenharmony_ci goto out_free_ict; 36438c2ecf20Sopenharmony_ci } 36448c2ecf20Sopenharmony_ci trans_pcie->inta_mask = CSR_INI_SET_MASK; 36458c2ecf20Sopenharmony_ci } 36468c2ecf20Sopenharmony_ci 36478c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS 36488c2ecf20Sopenharmony_ci trans_pcie->fw_mon_data.state = IWL_FW_MON_DBGFS_STATE_CLOSED; 36498c2ecf20Sopenharmony_ci mutex_init(&trans_pcie->fw_mon_data.mutex); 36508c2ecf20Sopenharmony_ci#endif 36518c2ecf20Sopenharmony_ci 36528c2ecf20Sopenharmony_ci iwl_dbg_tlv_init(trans); 36538c2ecf20Sopenharmony_ci 36548c2ecf20Sopenharmony_ci return trans; 36558c2ecf20Sopenharmony_ci 36568c2ecf20Sopenharmony_ciout_free_ict: 36578c2ecf20Sopenharmony_ci iwl_pcie_free_ict(trans); 36588c2ecf20Sopenharmony_ciout_no_pci: 36598c2ecf20Sopenharmony_ci destroy_workqueue(trans_pcie->rba.alloc_wq); 36608c2ecf20Sopenharmony_ciout_free_trans: 36618c2ecf20Sopenharmony_ci iwl_trans_free(trans); 36628c2ecf20Sopenharmony_ci return ERR_PTR(ret); 36638c2ecf20Sopenharmony_ci} 36648c2ecf20Sopenharmony_ci 36658c2ecf20Sopenharmony_civoid iwl_trans_pcie_sync_nmi(struct iwl_trans *trans) 36668c2ecf20Sopenharmony_ci{ 36678c2ecf20Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 36688c2ecf20Sopenharmony_ci unsigned long timeout = jiffies + IWL_TRANS_NMI_TIMEOUT; 36698c2ecf20Sopenharmony_ci bool interrupts_enabled = test_bit(STATUS_INT_ENABLED, &trans->status); 36708c2ecf20Sopenharmony_ci u32 inta_addr, sw_err_bit; 36718c2ecf20Sopenharmony_ci 36728c2ecf20Sopenharmony_ci if (trans_pcie->msix_enabled) { 36738c2ecf20Sopenharmony_ci inta_addr = CSR_MSIX_HW_INT_CAUSES_AD; 36748c2ecf20Sopenharmony_ci sw_err_bit = MSIX_HW_INT_CAUSES_REG_SW_ERR; 36758c2ecf20Sopenharmony_ci } else { 36768c2ecf20Sopenharmony_ci inta_addr = CSR_INT; 36778c2ecf20Sopenharmony_ci sw_err_bit = CSR_INT_BIT_SW_ERR; 36788c2ecf20Sopenharmony_ci } 36798c2ecf20Sopenharmony_ci 36808c2ecf20Sopenharmony_ci /* if the interrupts were already disabled, there is no point in 36818c2ecf20Sopenharmony_ci * calling iwl_disable_interrupts 36828c2ecf20Sopenharmony_ci */ 36838c2ecf20Sopenharmony_ci if (interrupts_enabled) 36848c2ecf20Sopenharmony_ci iwl_disable_interrupts(trans); 36858c2ecf20Sopenharmony_ci 36868c2ecf20Sopenharmony_ci iwl_force_nmi(trans); 36878c2ecf20Sopenharmony_ci while (time_after(timeout, jiffies)) { 36888c2ecf20Sopenharmony_ci u32 inta_hw = iwl_read32(trans, inta_addr); 36898c2ecf20Sopenharmony_ci 36908c2ecf20Sopenharmony_ci /* Error detected by uCode */ 36918c2ecf20Sopenharmony_ci if (inta_hw & sw_err_bit) { 36928c2ecf20Sopenharmony_ci /* Clear causes register */ 36938c2ecf20Sopenharmony_ci iwl_write32(trans, inta_addr, inta_hw & sw_err_bit); 36948c2ecf20Sopenharmony_ci break; 36958c2ecf20Sopenharmony_ci } 36968c2ecf20Sopenharmony_ci 36978c2ecf20Sopenharmony_ci mdelay(1); 36988c2ecf20Sopenharmony_ci } 36998c2ecf20Sopenharmony_ci 37008c2ecf20Sopenharmony_ci /* enable interrupts only if there were already enabled before this 37018c2ecf20Sopenharmony_ci * function to avoid a case were the driver enable interrupts before 37028c2ecf20Sopenharmony_ci * proper configurations were made 37038c2ecf20Sopenharmony_ci */ 37048c2ecf20Sopenharmony_ci if (interrupts_enabled) 37058c2ecf20Sopenharmony_ci iwl_enable_interrupts(trans); 37068c2ecf20Sopenharmony_ci 37078c2ecf20Sopenharmony_ci iwl_trans_fw_error(trans); 37088c2ecf20Sopenharmony_ci} 3709