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