162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2007-2015, 2018-2023 Intel Corporation 462306a36Sopenharmony_ci * Copyright (C) 2013-2015 Intel Mobile Communications GmbH 562306a36Sopenharmony_ci * Copyright (C) 2016-2017 Intel Deutschland GmbH 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/pci.h> 862306a36Sopenharmony_ci#include <linux/interrupt.h> 962306a36Sopenharmony_ci#include <linux/debugfs.h> 1062306a36Sopenharmony_ci#include <linux/sched.h> 1162306a36Sopenharmony_ci#include <linux/bitops.h> 1262306a36Sopenharmony_ci#include <linux/gfp.h> 1362306a36Sopenharmony_ci#include <linux/vmalloc.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/wait.h> 1662306a36Sopenharmony_ci#include <linux/seq_file.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "iwl-drv.h" 1962306a36Sopenharmony_ci#include "iwl-trans.h" 2062306a36Sopenharmony_ci#include "iwl-csr.h" 2162306a36Sopenharmony_ci#include "iwl-prph.h" 2262306a36Sopenharmony_ci#include "iwl-scd.h" 2362306a36Sopenharmony_ci#include "iwl-agn-hw.h" 2462306a36Sopenharmony_ci#include "fw/error-dump.h" 2562306a36Sopenharmony_ci#include "fw/dbg.h" 2662306a36Sopenharmony_ci#include "fw/api/tx.h" 2762306a36Sopenharmony_ci#include "mei/iwl-mei.h" 2862306a36Sopenharmony_ci#include "internal.h" 2962306a36Sopenharmony_ci#include "iwl-fh.h" 3062306a36Sopenharmony_ci#include "iwl-context-info-gen3.h" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* extended range in FW SRAM */ 3362306a36Sopenharmony_ci#define IWL_FW_MEM_EXTENDED_START 0x40000 3462306a36Sopenharmony_ci#define IWL_FW_MEM_EXTENDED_END 0x57FFF 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_civoid iwl_trans_pcie_dump_regs(struct iwl_trans *trans) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci#define PCI_DUMP_SIZE 352 3962306a36Sopenharmony_ci#define PCI_MEM_DUMP_SIZE 64 4062306a36Sopenharmony_ci#define PCI_PARENT_DUMP_SIZE 524 4162306a36Sopenharmony_ci#define PREFIX_LEN 32 4262306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 4362306a36Sopenharmony_ci struct pci_dev *pdev = trans_pcie->pci_dev; 4462306a36Sopenharmony_ci u32 i, pos, alloc_size, *ptr, *buf; 4562306a36Sopenharmony_ci char *prefix; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci if (trans_pcie->pcie_dbg_dumped_once) 4862306a36Sopenharmony_ci return; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci /* Should be a multiple of 4 */ 5162306a36Sopenharmony_ci BUILD_BUG_ON(PCI_DUMP_SIZE > 4096 || PCI_DUMP_SIZE & 0x3); 5262306a36Sopenharmony_ci BUILD_BUG_ON(PCI_MEM_DUMP_SIZE > 4096 || PCI_MEM_DUMP_SIZE & 0x3); 5362306a36Sopenharmony_ci BUILD_BUG_ON(PCI_PARENT_DUMP_SIZE > 4096 || PCI_PARENT_DUMP_SIZE & 0x3); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci /* Alloc a max size buffer */ 5662306a36Sopenharmony_ci alloc_size = PCI_ERR_ROOT_ERR_SRC + 4 + PREFIX_LEN; 5762306a36Sopenharmony_ci alloc_size = max_t(u32, alloc_size, PCI_DUMP_SIZE + PREFIX_LEN); 5862306a36Sopenharmony_ci alloc_size = max_t(u32, alloc_size, PCI_MEM_DUMP_SIZE + PREFIX_LEN); 5962306a36Sopenharmony_ci alloc_size = max_t(u32, alloc_size, PCI_PARENT_DUMP_SIZE + PREFIX_LEN); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci buf = kmalloc(alloc_size, GFP_ATOMIC); 6262306a36Sopenharmony_ci if (!buf) 6362306a36Sopenharmony_ci return; 6462306a36Sopenharmony_ci prefix = (char *)buf + alloc_size - PREFIX_LEN; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci IWL_ERR(trans, "iwlwifi transaction failed, dumping registers\n"); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* Print wifi device registers */ 6962306a36Sopenharmony_ci sprintf(prefix, "iwlwifi %s: ", pci_name(pdev)); 7062306a36Sopenharmony_ci IWL_ERR(trans, "iwlwifi device config registers:\n"); 7162306a36Sopenharmony_ci for (i = 0, ptr = buf; i < PCI_DUMP_SIZE; i += 4, ptr++) 7262306a36Sopenharmony_ci if (pci_read_config_dword(pdev, i, ptr)) 7362306a36Sopenharmony_ci goto err_read; 7462306a36Sopenharmony_ci print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 4, buf, i, 0); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci IWL_ERR(trans, "iwlwifi device memory mapped registers:\n"); 7762306a36Sopenharmony_ci for (i = 0, ptr = buf; i < PCI_MEM_DUMP_SIZE; i += 4, ptr++) 7862306a36Sopenharmony_ci *ptr = iwl_read32(trans, i); 7962306a36Sopenharmony_ci print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 4, buf, i, 0); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR); 8262306a36Sopenharmony_ci if (pos) { 8362306a36Sopenharmony_ci IWL_ERR(trans, "iwlwifi device AER capability structure:\n"); 8462306a36Sopenharmony_ci for (i = 0, ptr = buf; i < PCI_ERR_ROOT_COMMAND; i += 4, ptr++) 8562306a36Sopenharmony_ci if (pci_read_config_dword(pdev, pos + i, ptr)) 8662306a36Sopenharmony_ci goto err_read; 8762306a36Sopenharmony_ci print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 8862306a36Sopenharmony_ci 32, 4, buf, i, 0); 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* Print parent device registers next */ 9262306a36Sopenharmony_ci if (!pdev->bus->self) 9362306a36Sopenharmony_ci goto out; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci pdev = pdev->bus->self; 9662306a36Sopenharmony_ci sprintf(prefix, "iwlwifi %s: ", pci_name(pdev)); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci IWL_ERR(trans, "iwlwifi parent port (%s) config registers:\n", 9962306a36Sopenharmony_ci pci_name(pdev)); 10062306a36Sopenharmony_ci for (i = 0, ptr = buf; i < PCI_PARENT_DUMP_SIZE; i += 4, ptr++) 10162306a36Sopenharmony_ci if (pci_read_config_dword(pdev, i, ptr)) 10262306a36Sopenharmony_ci goto err_read; 10362306a36Sopenharmony_ci print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 4, buf, i, 0); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* Print root port AER registers */ 10662306a36Sopenharmony_ci pos = 0; 10762306a36Sopenharmony_ci pdev = pcie_find_root_port(pdev); 10862306a36Sopenharmony_ci if (pdev) 10962306a36Sopenharmony_ci pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR); 11062306a36Sopenharmony_ci if (pos) { 11162306a36Sopenharmony_ci IWL_ERR(trans, "iwlwifi root port (%s) AER cap structure:\n", 11262306a36Sopenharmony_ci pci_name(pdev)); 11362306a36Sopenharmony_ci sprintf(prefix, "iwlwifi %s: ", pci_name(pdev)); 11462306a36Sopenharmony_ci for (i = 0, ptr = buf; i <= PCI_ERR_ROOT_ERR_SRC; i += 4, ptr++) 11562306a36Sopenharmony_ci if (pci_read_config_dword(pdev, pos + i, ptr)) 11662306a36Sopenharmony_ci goto err_read; 11762306a36Sopenharmony_ci print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 11862306a36Sopenharmony_ci 4, buf, i, 0); 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci goto out; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cierr_read: 12362306a36Sopenharmony_ci print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 32, 4, buf, i, 0); 12462306a36Sopenharmony_ci IWL_ERR(trans, "Read failed at 0x%X\n", i); 12562306a36Sopenharmony_ciout: 12662306a36Sopenharmony_ci trans_pcie->pcie_dbg_dumped_once = 1; 12762306a36Sopenharmony_ci kfree(buf); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic int iwl_trans_pcie_sw_reset(struct iwl_trans *trans, 13162306a36Sopenharmony_ci bool retake_ownership) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci /* Reset entire device - do controller reset (results in SHRD_HW_RST) */ 13462306a36Sopenharmony_ci if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { 13562306a36Sopenharmony_ci iwl_set_bit(trans, CSR_GP_CNTRL, 13662306a36Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_SW_RESET); 13762306a36Sopenharmony_ci usleep_range(10000, 20000); 13862306a36Sopenharmony_ci } else { 13962306a36Sopenharmony_ci iwl_set_bit(trans, CSR_RESET, 14062306a36Sopenharmony_ci CSR_RESET_REG_FLAG_SW_RESET); 14162306a36Sopenharmony_ci usleep_range(5000, 6000); 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (retake_ownership) 14562306a36Sopenharmony_ci return iwl_pcie_prepare_card_hw(trans); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci return 0; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic void iwl_pcie_free_fw_monitor(struct iwl_trans *trans) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci struct iwl_dram_data *fw_mon = &trans->dbg.fw_mon; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (!fw_mon->size) 15562306a36Sopenharmony_ci return; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci dma_free_coherent(trans->dev, fw_mon->size, fw_mon->block, 15862306a36Sopenharmony_ci fw_mon->physical); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci fw_mon->block = NULL; 16162306a36Sopenharmony_ci fw_mon->physical = 0; 16262306a36Sopenharmony_ci fw_mon->size = 0; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic void iwl_pcie_alloc_fw_monitor_block(struct iwl_trans *trans, 16662306a36Sopenharmony_ci u8 max_power) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct iwl_dram_data *fw_mon = &trans->dbg.fw_mon; 16962306a36Sopenharmony_ci void *block = NULL; 17062306a36Sopenharmony_ci dma_addr_t physical = 0; 17162306a36Sopenharmony_ci u32 size = 0; 17262306a36Sopenharmony_ci u8 power; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (fw_mon->size) { 17562306a36Sopenharmony_ci memset(fw_mon->block, 0, fw_mon->size); 17662306a36Sopenharmony_ci return; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* need at least 2 KiB, so stop at 11 */ 18062306a36Sopenharmony_ci for (power = max_power; power >= 11; power--) { 18162306a36Sopenharmony_ci size = BIT(power); 18262306a36Sopenharmony_ci block = dma_alloc_coherent(trans->dev, size, &physical, 18362306a36Sopenharmony_ci GFP_KERNEL | __GFP_NOWARN); 18462306a36Sopenharmony_ci if (!block) 18562306a36Sopenharmony_ci continue; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci IWL_INFO(trans, 18862306a36Sopenharmony_ci "Allocated 0x%08x bytes for firmware monitor.\n", 18962306a36Sopenharmony_ci size); 19062306a36Sopenharmony_ci break; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (WARN_ON_ONCE(!block)) 19462306a36Sopenharmony_ci return; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (power != max_power) 19762306a36Sopenharmony_ci IWL_ERR(trans, 19862306a36Sopenharmony_ci "Sorry - debug buffer is only %luK while you requested %luK\n", 19962306a36Sopenharmony_ci (unsigned long)BIT(power - 10), 20062306a36Sopenharmony_ci (unsigned long)BIT(max_power - 10)); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci fw_mon->block = block; 20362306a36Sopenharmony_ci fw_mon->physical = physical; 20462306a36Sopenharmony_ci fw_mon->size = size; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_civoid iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans, u8 max_power) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci if (!max_power) { 21062306a36Sopenharmony_ci /* default max_power is maximum */ 21162306a36Sopenharmony_ci max_power = 26; 21262306a36Sopenharmony_ci } else { 21362306a36Sopenharmony_ci max_power += 11; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (WARN(max_power > 26, 21762306a36Sopenharmony_ci "External buffer size for monitor is too big %d, check the FW TLV\n", 21862306a36Sopenharmony_ci max_power)) 21962306a36Sopenharmony_ci return; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci iwl_pcie_alloc_fw_monitor_block(trans, max_power); 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic u32 iwl_trans_pcie_read_shr(struct iwl_trans *trans, u32 reg) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_CTRL_REG, 22762306a36Sopenharmony_ci ((reg & 0x0000ffff) | (2 << 28))); 22862306a36Sopenharmony_ci return iwl_read32(trans, HEEP_CTRL_WRD_PCIEX_DATA_REG); 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic void iwl_trans_pcie_write_shr(struct iwl_trans *trans, u32 reg, u32 val) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_DATA_REG, val); 23462306a36Sopenharmony_ci iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_CTRL_REG, 23562306a36Sopenharmony_ci ((reg & 0x0000ffff) | (3 << 28))); 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic void iwl_pcie_set_pwr(struct iwl_trans *trans, bool vaux) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci if (trans->cfg->apmg_not_supported) 24162306a36Sopenharmony_ci return; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (vaux && pci_pme_capable(to_pci_dev(trans->dev), PCI_D3cold)) 24462306a36Sopenharmony_ci iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG, 24562306a36Sopenharmony_ci APMG_PS_CTRL_VAL_PWR_SRC_VAUX, 24662306a36Sopenharmony_ci ~APMG_PS_CTRL_MSK_PWR_SRC); 24762306a36Sopenharmony_ci else 24862306a36Sopenharmony_ci iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG, 24962306a36Sopenharmony_ci APMG_PS_CTRL_VAL_PWR_SRC_VMAIN, 25062306a36Sopenharmony_ci ~APMG_PS_CTRL_MSK_PWR_SRC); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci/* PCI registers */ 25462306a36Sopenharmony_ci#define PCI_CFG_RETRY_TIMEOUT 0x041 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_civoid iwl_pcie_apm_config(struct iwl_trans *trans) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 25962306a36Sopenharmony_ci u16 lctl; 26062306a36Sopenharmony_ci u16 cap; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* 26362306a36Sopenharmony_ci * L0S states have been found to be unstable with our devices 26462306a36Sopenharmony_ci * and in newer hardware they are not officially supported at 26562306a36Sopenharmony_ci * all, so we must always set the L0S_DISABLED bit. 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_ci iwl_set_bit(trans, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_DISABLED); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci pcie_capability_read_word(trans_pcie->pci_dev, PCI_EXP_LNKCTL, &lctl); 27062306a36Sopenharmony_ci trans->pm_support = !(lctl & PCI_EXP_LNKCTL_ASPM_L0S); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci pcie_capability_read_word(trans_pcie->pci_dev, PCI_EXP_DEVCTL2, &cap); 27362306a36Sopenharmony_ci trans->ltr_enabled = cap & PCI_EXP_DEVCTL2_LTR_EN; 27462306a36Sopenharmony_ci IWL_DEBUG_POWER(trans, "L1 %sabled - LTR %sabled\n", 27562306a36Sopenharmony_ci (lctl & PCI_EXP_LNKCTL_ASPM_L1) ? "En" : "Dis", 27662306a36Sopenharmony_ci trans->ltr_enabled ? "En" : "Dis"); 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci/* 28062306a36Sopenharmony_ci * Start up NIC's basic functionality after it has been reset 28162306a36Sopenharmony_ci * (e.g. after platform boot, or shutdown via iwl_pcie_apm_stop()) 28262306a36Sopenharmony_ci * NOTE: This does not load uCode nor start the embedded processor 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_cistatic int iwl_pcie_apm_init(struct iwl_trans *trans) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci int ret; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci IWL_DEBUG_INFO(trans, "Init card's basic functions\n"); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* 29162306a36Sopenharmony_ci * Use "set_bit" below rather than "write", to preserve any hardware 29262306a36Sopenharmony_ci * bits already set by default after reset. 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* Disable L0S exit timer (platform NMI Work/Around) */ 29662306a36Sopenharmony_ci if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_8000) 29762306a36Sopenharmony_ci iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS, 29862306a36Sopenharmony_ci CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* 30162306a36Sopenharmony_ci * Disable L0s without affecting L1; 30262306a36Sopenharmony_ci * don't wait for ICH L0s (ICH bug W/A) 30362306a36Sopenharmony_ci */ 30462306a36Sopenharmony_ci iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS, 30562306a36Sopenharmony_ci CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* Set FH wait threshold to maximum (HW error during stress W/A) */ 30862306a36Sopenharmony_ci iwl_set_bit(trans, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* 31162306a36Sopenharmony_ci * Enable HAP INTA (interrupt from management bus) to 31262306a36Sopenharmony_ci * wake device's PCI Express link L1a -> L0s 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_ci iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, 31562306a36Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci iwl_pcie_apm_config(trans); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* Configure analog phase-lock-loop before activating to D0A */ 32062306a36Sopenharmony_ci if (trans->trans_cfg->base_params->pll_cfg) 32162306a36Sopenharmony_ci iwl_set_bit(trans, CSR_ANA_PLL_CFG, CSR50_ANA_PLL_CFG_VAL); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci ret = iwl_finish_nic_init(trans); 32462306a36Sopenharmony_ci if (ret) 32562306a36Sopenharmony_ci return ret; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (trans->cfg->host_interrupt_operation_mode) { 32862306a36Sopenharmony_ci /* 32962306a36Sopenharmony_ci * This is a bit of an abuse - This is needed for 7260 / 3160 33062306a36Sopenharmony_ci * only check host_interrupt_operation_mode even if this is 33162306a36Sopenharmony_ci * not related to host_interrupt_operation_mode. 33262306a36Sopenharmony_ci * 33362306a36Sopenharmony_ci * Enable the oscillator to count wake up time for L1 exit. This 33462306a36Sopenharmony_ci * consumes slightly more power (100uA) - but allows to be sure 33562306a36Sopenharmony_ci * that we wake up from L1 on time. 33662306a36Sopenharmony_ci * 33762306a36Sopenharmony_ci * This looks weird: read twice the same register, discard the 33862306a36Sopenharmony_ci * value, set a bit, and yet again, read that same register 33962306a36Sopenharmony_ci * just to discard the value. But that's the way the hardware 34062306a36Sopenharmony_ci * seems to like it. 34162306a36Sopenharmony_ci */ 34262306a36Sopenharmony_ci iwl_read_prph(trans, OSC_CLK); 34362306a36Sopenharmony_ci iwl_read_prph(trans, OSC_CLK); 34462306a36Sopenharmony_ci iwl_set_bits_prph(trans, OSC_CLK, OSC_CLK_FORCE_CONTROL); 34562306a36Sopenharmony_ci iwl_read_prph(trans, OSC_CLK); 34662306a36Sopenharmony_ci iwl_read_prph(trans, OSC_CLK); 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* 35062306a36Sopenharmony_ci * Enable DMA clock and wait for it to stabilize. 35162306a36Sopenharmony_ci * 35262306a36Sopenharmony_ci * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0" 35362306a36Sopenharmony_ci * bits do not disable clocks. This preserves any hardware 35462306a36Sopenharmony_ci * bits already set by default in "CLK_CTRL_REG" after reset. 35562306a36Sopenharmony_ci */ 35662306a36Sopenharmony_ci if (!trans->cfg->apmg_not_supported) { 35762306a36Sopenharmony_ci iwl_write_prph(trans, APMG_CLK_EN_REG, 35862306a36Sopenharmony_ci APMG_CLK_VAL_DMA_CLK_RQT); 35962306a36Sopenharmony_ci udelay(20); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* Disable L1-Active */ 36262306a36Sopenharmony_ci iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG, 36362306a36Sopenharmony_ci APMG_PCIDEV_STT_VAL_L1_ACT_DIS); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci /* Clear the interrupt in APMG if the NIC is in RFKILL */ 36662306a36Sopenharmony_ci iwl_write_prph(trans, APMG_RTC_INT_STT_REG, 36762306a36Sopenharmony_ci APMG_RTC_INT_STT_RFKILL); 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci set_bit(STATUS_DEVICE_ENABLED, &trans->status); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci return 0; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci/* 37662306a36Sopenharmony_ci * Enable LP XTAL to avoid HW bug where device may consume much power if 37762306a36Sopenharmony_ci * FW is not loaded after device reset. LP XTAL is disabled by default 37862306a36Sopenharmony_ci * after device HW reset. Do it only if XTAL is fed by internal source. 37962306a36Sopenharmony_ci * Configure device's "persistence" mode to avoid resetting XTAL again when 38062306a36Sopenharmony_ci * SHRD_HW_RST occurs in S3. 38162306a36Sopenharmony_ci */ 38262306a36Sopenharmony_cistatic void iwl_pcie_apm_lp_xtal_enable(struct iwl_trans *trans) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci int ret; 38562306a36Sopenharmony_ci u32 apmg_gp1_reg; 38662306a36Sopenharmony_ci u32 apmg_xtal_cfg_reg; 38762306a36Sopenharmony_ci u32 dl_cfg_reg; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* Force XTAL ON */ 39062306a36Sopenharmony_ci __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL, 39162306a36Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_XTAL_ON); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci ret = iwl_trans_pcie_sw_reset(trans, true); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (!ret) 39662306a36Sopenharmony_ci ret = iwl_finish_nic_init(trans); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci if (WARN_ON(ret)) { 39962306a36Sopenharmony_ci /* Release XTAL ON request */ 40062306a36Sopenharmony_ci __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, 40162306a36Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_XTAL_ON); 40262306a36Sopenharmony_ci return; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci /* 40662306a36Sopenharmony_ci * Clear "disable persistence" to avoid LP XTAL resetting when 40762306a36Sopenharmony_ci * SHRD_HW_RST is applied in S3. 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_ci iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG, 41062306a36Sopenharmony_ci APMG_PCIDEV_STT_VAL_PERSIST_DIS); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* 41362306a36Sopenharmony_ci * Force APMG XTAL to be active to prevent its disabling by HW 41462306a36Sopenharmony_ci * caused by APMG idle state. 41562306a36Sopenharmony_ci */ 41662306a36Sopenharmony_ci apmg_xtal_cfg_reg = iwl_trans_pcie_read_shr(trans, 41762306a36Sopenharmony_ci SHR_APMG_XTAL_CFG_REG); 41862306a36Sopenharmony_ci iwl_trans_pcie_write_shr(trans, SHR_APMG_XTAL_CFG_REG, 41962306a36Sopenharmony_ci apmg_xtal_cfg_reg | 42062306a36Sopenharmony_ci SHR_APMG_XTAL_CFG_XTAL_ON_REQ); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci ret = iwl_trans_pcie_sw_reset(trans, true); 42362306a36Sopenharmony_ci if (ret) 42462306a36Sopenharmony_ci IWL_ERR(trans, 42562306a36Sopenharmony_ci "iwl_pcie_apm_lp_xtal_enable: failed to retake NIC ownership\n"); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* Enable LP XTAL by indirect access through CSR */ 42862306a36Sopenharmony_ci apmg_gp1_reg = iwl_trans_pcie_read_shr(trans, SHR_APMG_GP1_REG); 42962306a36Sopenharmony_ci iwl_trans_pcie_write_shr(trans, SHR_APMG_GP1_REG, apmg_gp1_reg | 43062306a36Sopenharmony_ci SHR_APMG_GP1_WF_XTAL_LP_EN | 43162306a36Sopenharmony_ci SHR_APMG_GP1_CHICKEN_BIT_SELECT); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci /* Clear delay line clock power up */ 43462306a36Sopenharmony_ci dl_cfg_reg = iwl_trans_pcie_read_shr(trans, SHR_APMG_DL_CFG_REG); 43562306a36Sopenharmony_ci iwl_trans_pcie_write_shr(trans, SHR_APMG_DL_CFG_REG, dl_cfg_reg & 43662306a36Sopenharmony_ci ~SHR_APMG_DL_CFG_DL_CLOCK_POWER_UP); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci /* 43962306a36Sopenharmony_ci * Enable persistence mode to avoid LP XTAL resetting when 44062306a36Sopenharmony_ci * SHRD_HW_RST is applied in S3. 44162306a36Sopenharmony_ci */ 44262306a36Sopenharmony_ci iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, 44362306a36Sopenharmony_ci CSR_HW_IF_CONFIG_REG_PERSIST_MODE); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* 44662306a36Sopenharmony_ci * Clear "initialization complete" bit to move adapter from 44762306a36Sopenharmony_ci * D0A* (powered-up Active) --> D0U* (Uninitialized) state. 44862306a36Sopenharmony_ci */ 44962306a36Sopenharmony_ci iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci /* Activates XTAL resources monitor */ 45262306a36Sopenharmony_ci __iwl_trans_pcie_set_bit(trans, CSR_MONITOR_CFG_REG, 45362306a36Sopenharmony_ci CSR_MONITOR_XTAL_RESOURCES); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* Release XTAL ON request */ 45662306a36Sopenharmony_ci __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, 45762306a36Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_XTAL_ON); 45862306a36Sopenharmony_ci udelay(10); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* Release APMG XTAL */ 46162306a36Sopenharmony_ci iwl_trans_pcie_write_shr(trans, SHR_APMG_XTAL_CFG_REG, 46262306a36Sopenharmony_ci apmg_xtal_cfg_reg & 46362306a36Sopenharmony_ci ~SHR_APMG_XTAL_CFG_XTAL_ON_REQ); 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_civoid iwl_pcie_apm_stop_master(struct iwl_trans *trans) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci int ret; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci /* stop device's busmaster DMA activity */ 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { 47362306a36Sopenharmony_ci iwl_set_bit(trans, CSR_GP_CNTRL, 47462306a36Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_BUS_MASTER_DISABLE_REQ); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci ret = iwl_poll_bit(trans, CSR_GP_CNTRL, 47762306a36Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_BUS_MASTER_DISABLE_STATUS, 47862306a36Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_BUS_MASTER_DISABLE_STATUS, 47962306a36Sopenharmony_ci 100); 48062306a36Sopenharmony_ci usleep_range(10000, 20000); 48162306a36Sopenharmony_ci } else { 48262306a36Sopenharmony_ci iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci ret = iwl_poll_bit(trans, CSR_RESET, 48562306a36Sopenharmony_ci CSR_RESET_REG_FLAG_MASTER_DISABLED, 48662306a36Sopenharmony_ci CSR_RESET_REG_FLAG_MASTER_DISABLED, 100); 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (ret < 0) 49062306a36Sopenharmony_ci IWL_WARN(trans, "Master Disable Timed Out, 100 usec\n"); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci IWL_DEBUG_INFO(trans, "stop master\n"); 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic void iwl_pcie_apm_stop(struct iwl_trans *trans, bool op_mode_leave) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci IWL_DEBUG_INFO(trans, "Stop card, put in low power state\n"); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (op_mode_leave) { 50062306a36Sopenharmony_ci if (!test_bit(STATUS_DEVICE_ENABLED, &trans->status)) 50162306a36Sopenharmony_ci iwl_pcie_apm_init(trans); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci /* inform ME that we are leaving */ 50462306a36Sopenharmony_ci if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_7000) 50562306a36Sopenharmony_ci iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG, 50662306a36Sopenharmony_ci APMG_PCIDEV_STT_VAL_WAKE_ME); 50762306a36Sopenharmony_ci else if (trans->trans_cfg->device_family >= 50862306a36Sopenharmony_ci IWL_DEVICE_FAMILY_8000) { 50962306a36Sopenharmony_ci iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, 51062306a36Sopenharmony_ci CSR_RESET_LINK_PWR_MGMT_DISABLED); 51162306a36Sopenharmony_ci iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, 51262306a36Sopenharmony_ci CSR_HW_IF_CONFIG_REG_PREPARE | 51362306a36Sopenharmony_ci CSR_HW_IF_CONFIG_REG_ENABLE_PME); 51462306a36Sopenharmony_ci mdelay(1); 51562306a36Sopenharmony_ci iwl_clear_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, 51662306a36Sopenharmony_ci CSR_RESET_LINK_PWR_MGMT_DISABLED); 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci mdelay(5); 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci clear_bit(STATUS_DEVICE_ENABLED, &trans->status); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* Stop device's DMA activity */ 52462306a36Sopenharmony_ci iwl_pcie_apm_stop_master(trans); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci if (trans->cfg->lp_xtal_workaround) { 52762306a36Sopenharmony_ci iwl_pcie_apm_lp_xtal_enable(trans); 52862306a36Sopenharmony_ci return; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci iwl_trans_pcie_sw_reset(trans, false); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* 53462306a36Sopenharmony_ci * Clear "initialization complete" bit to move adapter from 53562306a36Sopenharmony_ci * D0A* (powered-up Active) --> D0U* (Uninitialized) state. 53662306a36Sopenharmony_ci */ 53762306a36Sopenharmony_ci iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cistatic int iwl_pcie_nic_init(struct iwl_trans *trans) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 54362306a36Sopenharmony_ci int ret; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci /* nic_init */ 54662306a36Sopenharmony_ci spin_lock_bh(&trans_pcie->irq_lock); 54762306a36Sopenharmony_ci ret = iwl_pcie_apm_init(trans); 54862306a36Sopenharmony_ci spin_unlock_bh(&trans_pcie->irq_lock); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci if (ret) 55162306a36Sopenharmony_ci return ret; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci iwl_pcie_set_pwr(trans, false); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci iwl_op_mode_nic_config(trans->op_mode); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci /* Allocate the RX queue, or reset if it is already allocated */ 55862306a36Sopenharmony_ci ret = iwl_pcie_rx_init(trans); 55962306a36Sopenharmony_ci if (ret) 56062306a36Sopenharmony_ci return ret; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci /* Allocate or reset and init all Tx and Command queues */ 56362306a36Sopenharmony_ci if (iwl_pcie_tx_init(trans)) { 56462306a36Sopenharmony_ci iwl_pcie_rx_free(trans); 56562306a36Sopenharmony_ci return -ENOMEM; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci if (trans->trans_cfg->base_params->shadow_reg_enable) { 56962306a36Sopenharmony_ci /* enable shadow regs in HW */ 57062306a36Sopenharmony_ci iwl_set_bit(trans, CSR_MAC_SHADOW_REG_CTRL, 0x800FFFFF); 57162306a36Sopenharmony_ci IWL_DEBUG_INFO(trans, "Enabling shadow registers in device\n"); 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci return 0; 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci#define HW_READY_TIMEOUT (50) 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci/* Note: returns poll_bit return value, which is >= 0 if success */ 58062306a36Sopenharmony_cistatic int iwl_pcie_set_hw_ready(struct iwl_trans *trans) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci int ret; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, 58562306a36Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_NIC_READY); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci /* See if we got it */ 58862306a36Sopenharmony_ci ret = iwl_poll_bit(trans, CSR_HW_IF_CONFIG_REG, 58962306a36Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, 59062306a36Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, 59162306a36Sopenharmony_ci HW_READY_TIMEOUT); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (ret >= 0) 59462306a36Sopenharmony_ci iwl_set_bit(trans, CSR_MBOX_SET_REG, CSR_MBOX_SET_REG_OS_ALIVE); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci IWL_DEBUG_INFO(trans, "hardware%s ready\n", ret < 0 ? " not" : ""); 59762306a36Sopenharmony_ci return ret; 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci/* Note: returns standard 0/-ERROR code */ 60162306a36Sopenharmony_ciint iwl_pcie_prepare_card_hw(struct iwl_trans *trans) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci int ret; 60462306a36Sopenharmony_ci int iter; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci IWL_DEBUG_INFO(trans, "iwl_trans_prepare_card_hw enter\n"); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci ret = iwl_pcie_set_hw_ready(trans); 60962306a36Sopenharmony_ci /* If the card is ready, exit 0 */ 61062306a36Sopenharmony_ci if (ret >= 0) { 61162306a36Sopenharmony_ci trans->csme_own = false; 61262306a36Sopenharmony_ci return 0; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, 61662306a36Sopenharmony_ci CSR_RESET_LINK_PWR_MGMT_DISABLED); 61762306a36Sopenharmony_ci usleep_range(1000, 2000); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci for (iter = 0; iter < 10; iter++) { 62062306a36Sopenharmony_ci int t = 0; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci /* If HW is not ready, prepare the conditions to check again */ 62362306a36Sopenharmony_ci iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, 62462306a36Sopenharmony_ci CSR_HW_IF_CONFIG_REG_PREPARE); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci do { 62762306a36Sopenharmony_ci ret = iwl_pcie_set_hw_ready(trans); 62862306a36Sopenharmony_ci if (ret >= 0) { 62962306a36Sopenharmony_ci trans->csme_own = false; 63062306a36Sopenharmony_ci return 0; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci if (iwl_mei_is_connected()) { 63462306a36Sopenharmony_ci IWL_DEBUG_INFO(trans, 63562306a36Sopenharmony_ci "Couldn't prepare the card but SAP is connected\n"); 63662306a36Sopenharmony_ci trans->csme_own = true; 63762306a36Sopenharmony_ci if (trans->trans_cfg->device_family != 63862306a36Sopenharmony_ci IWL_DEVICE_FAMILY_9000) 63962306a36Sopenharmony_ci IWL_ERR(trans, 64062306a36Sopenharmony_ci "SAP not supported for this NIC family\n"); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci return -EBUSY; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci usleep_range(200, 1000); 64662306a36Sopenharmony_ci t += 200; 64762306a36Sopenharmony_ci } while (t < 150000); 64862306a36Sopenharmony_ci msleep(25); 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci IWL_ERR(trans, "Couldn't prepare the card\n"); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci return ret; 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci/* 65762306a36Sopenharmony_ci * ucode 65862306a36Sopenharmony_ci */ 65962306a36Sopenharmony_cistatic void iwl_pcie_load_firmware_chunk_fh(struct iwl_trans *trans, 66062306a36Sopenharmony_ci u32 dst_addr, dma_addr_t phy_addr, 66162306a36Sopenharmony_ci u32 byte_cnt) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci iwl_write32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL), 66462306a36Sopenharmony_ci FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci iwl_write32(trans, FH_SRVC_CHNL_SRAM_ADDR_REG(FH_SRVC_CHNL), 66762306a36Sopenharmony_ci dst_addr); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci iwl_write32(trans, FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL), 67062306a36Sopenharmony_ci phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci iwl_write32(trans, FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL), 67362306a36Sopenharmony_ci (iwl_get_dma_hi_addr(phy_addr) 67462306a36Sopenharmony_ci << FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci iwl_write32(trans, FH_TCSR_CHNL_TX_BUF_STS_REG(FH_SRVC_CHNL), 67762306a36Sopenharmony_ci BIT(FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM) | 67862306a36Sopenharmony_ci BIT(FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX) | 67962306a36Sopenharmony_ci FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci iwl_write32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL), 68262306a36Sopenharmony_ci FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | 68362306a36Sopenharmony_ci FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE | 68462306a36Sopenharmony_ci FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD); 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cistatic int iwl_pcie_load_firmware_chunk(struct iwl_trans *trans, 68862306a36Sopenharmony_ci u32 dst_addr, dma_addr_t phy_addr, 68962306a36Sopenharmony_ci u32 byte_cnt) 69062306a36Sopenharmony_ci{ 69162306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 69262306a36Sopenharmony_ci int ret; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci trans_pcie->ucode_write_complete = false; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci if (!iwl_trans_grab_nic_access(trans)) 69762306a36Sopenharmony_ci return -EIO; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci iwl_pcie_load_firmware_chunk_fh(trans, dst_addr, phy_addr, 70062306a36Sopenharmony_ci byte_cnt); 70162306a36Sopenharmony_ci iwl_trans_release_nic_access(trans); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci ret = wait_event_timeout(trans_pcie->ucode_write_waitq, 70462306a36Sopenharmony_ci trans_pcie->ucode_write_complete, 5 * HZ); 70562306a36Sopenharmony_ci if (!ret) { 70662306a36Sopenharmony_ci IWL_ERR(trans, "Failed to load firmware chunk!\n"); 70762306a36Sopenharmony_ci iwl_trans_pcie_dump_regs(trans); 70862306a36Sopenharmony_ci return -ETIMEDOUT; 70962306a36Sopenharmony_ci } 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci return 0; 71262306a36Sopenharmony_ci} 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_cistatic int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num, 71562306a36Sopenharmony_ci const struct fw_desc *section) 71662306a36Sopenharmony_ci{ 71762306a36Sopenharmony_ci u8 *v_addr; 71862306a36Sopenharmony_ci dma_addr_t p_addr; 71962306a36Sopenharmony_ci u32 offset, chunk_sz = min_t(u32, FH_MEM_TB_MAX_LENGTH, section->len); 72062306a36Sopenharmony_ci int ret = 0; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n", 72362306a36Sopenharmony_ci section_num); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci v_addr = dma_alloc_coherent(trans->dev, chunk_sz, &p_addr, 72662306a36Sopenharmony_ci GFP_KERNEL | __GFP_NOWARN); 72762306a36Sopenharmony_ci if (!v_addr) { 72862306a36Sopenharmony_ci IWL_DEBUG_INFO(trans, "Falling back to small chunks of DMA\n"); 72962306a36Sopenharmony_ci chunk_sz = PAGE_SIZE; 73062306a36Sopenharmony_ci v_addr = dma_alloc_coherent(trans->dev, chunk_sz, 73162306a36Sopenharmony_ci &p_addr, GFP_KERNEL); 73262306a36Sopenharmony_ci if (!v_addr) 73362306a36Sopenharmony_ci return -ENOMEM; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci for (offset = 0; offset < section->len; offset += chunk_sz) { 73762306a36Sopenharmony_ci u32 copy_size, dst_addr; 73862306a36Sopenharmony_ci bool extended_addr = false; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci copy_size = min_t(u32, chunk_sz, section->len - offset); 74162306a36Sopenharmony_ci dst_addr = section->offset + offset; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (dst_addr >= IWL_FW_MEM_EXTENDED_START && 74462306a36Sopenharmony_ci dst_addr <= IWL_FW_MEM_EXTENDED_END) 74562306a36Sopenharmony_ci extended_addr = true; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci if (extended_addr) 74862306a36Sopenharmony_ci iwl_set_bits_prph(trans, LMPM_CHICK, 74962306a36Sopenharmony_ci LMPM_CHICK_EXTENDED_ADDR_SPACE); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci memcpy(v_addr, (const u8 *)section->data + offset, copy_size); 75262306a36Sopenharmony_ci ret = iwl_pcie_load_firmware_chunk(trans, dst_addr, p_addr, 75362306a36Sopenharmony_ci copy_size); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci if (extended_addr) 75662306a36Sopenharmony_ci iwl_clear_bits_prph(trans, LMPM_CHICK, 75762306a36Sopenharmony_ci LMPM_CHICK_EXTENDED_ADDR_SPACE); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci if (ret) { 76062306a36Sopenharmony_ci IWL_ERR(trans, 76162306a36Sopenharmony_ci "Could not load the [%d] uCode section\n", 76262306a36Sopenharmony_ci section_num); 76362306a36Sopenharmony_ci break; 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci dma_free_coherent(trans->dev, chunk_sz, v_addr, p_addr); 76862306a36Sopenharmony_ci return ret; 76962306a36Sopenharmony_ci} 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_cistatic int iwl_pcie_load_cpu_sections_8000(struct iwl_trans *trans, 77262306a36Sopenharmony_ci const struct fw_img *image, 77362306a36Sopenharmony_ci int cpu, 77462306a36Sopenharmony_ci int *first_ucode_section) 77562306a36Sopenharmony_ci{ 77662306a36Sopenharmony_ci int shift_param; 77762306a36Sopenharmony_ci int i, ret = 0, sec_num = 0x1; 77862306a36Sopenharmony_ci u32 val, last_read_idx = 0; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (cpu == 1) { 78162306a36Sopenharmony_ci shift_param = 0; 78262306a36Sopenharmony_ci *first_ucode_section = 0; 78362306a36Sopenharmony_ci } else { 78462306a36Sopenharmony_ci shift_param = 16; 78562306a36Sopenharmony_ci (*first_ucode_section)++; 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci for (i = *first_ucode_section; i < image->num_sec; i++) { 78962306a36Sopenharmony_ci last_read_idx = i; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci /* 79262306a36Sopenharmony_ci * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between 79362306a36Sopenharmony_ci * CPU1 to CPU2. 79462306a36Sopenharmony_ci * PAGING_SEPARATOR_SECTION delimiter - separate between 79562306a36Sopenharmony_ci * CPU2 non paged to CPU2 paging sec. 79662306a36Sopenharmony_ci */ 79762306a36Sopenharmony_ci if (!image->sec[i].data || 79862306a36Sopenharmony_ci image->sec[i].offset == CPU1_CPU2_SEPARATOR_SECTION || 79962306a36Sopenharmony_ci image->sec[i].offset == PAGING_SEPARATOR_SECTION) { 80062306a36Sopenharmony_ci IWL_DEBUG_FW(trans, 80162306a36Sopenharmony_ci "Break since Data not valid or Empty section, sec = %d\n", 80262306a36Sopenharmony_ci i); 80362306a36Sopenharmony_ci break; 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci ret = iwl_pcie_load_section(trans, i, &image->sec[i]); 80762306a36Sopenharmony_ci if (ret) 80862306a36Sopenharmony_ci return ret; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci /* Notify ucode of loaded section number and status */ 81162306a36Sopenharmony_ci val = iwl_read_direct32(trans, FH_UCODE_LOAD_STATUS); 81262306a36Sopenharmony_ci val = val | (sec_num << shift_param); 81362306a36Sopenharmony_ci iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, val); 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci sec_num = (sec_num << 1) | 0x1; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci *first_ucode_section = last_read_idx; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci iwl_enable_interrupts(trans); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci if (trans->trans_cfg->gen2) { 82362306a36Sopenharmony_ci if (cpu == 1) 82462306a36Sopenharmony_ci iwl_write_prph(trans, UREG_UCODE_LOAD_STATUS, 82562306a36Sopenharmony_ci 0xFFFF); 82662306a36Sopenharmony_ci else 82762306a36Sopenharmony_ci iwl_write_prph(trans, UREG_UCODE_LOAD_STATUS, 82862306a36Sopenharmony_ci 0xFFFFFFFF); 82962306a36Sopenharmony_ci } else { 83062306a36Sopenharmony_ci if (cpu == 1) 83162306a36Sopenharmony_ci iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, 83262306a36Sopenharmony_ci 0xFFFF); 83362306a36Sopenharmony_ci else 83462306a36Sopenharmony_ci iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, 83562306a36Sopenharmony_ci 0xFFFFFFFF); 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci return 0; 83962306a36Sopenharmony_ci} 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_cistatic int iwl_pcie_load_cpu_sections(struct iwl_trans *trans, 84262306a36Sopenharmony_ci const struct fw_img *image, 84362306a36Sopenharmony_ci int cpu, 84462306a36Sopenharmony_ci int *first_ucode_section) 84562306a36Sopenharmony_ci{ 84662306a36Sopenharmony_ci int i, ret = 0; 84762306a36Sopenharmony_ci u32 last_read_idx = 0; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci if (cpu == 1) 85062306a36Sopenharmony_ci *first_ucode_section = 0; 85162306a36Sopenharmony_ci else 85262306a36Sopenharmony_ci (*first_ucode_section)++; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci for (i = *first_ucode_section; i < image->num_sec; i++) { 85562306a36Sopenharmony_ci last_read_idx = i; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci /* 85862306a36Sopenharmony_ci * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between 85962306a36Sopenharmony_ci * CPU1 to CPU2. 86062306a36Sopenharmony_ci * PAGING_SEPARATOR_SECTION delimiter - separate between 86162306a36Sopenharmony_ci * CPU2 non paged to CPU2 paging sec. 86262306a36Sopenharmony_ci */ 86362306a36Sopenharmony_ci if (!image->sec[i].data || 86462306a36Sopenharmony_ci image->sec[i].offset == CPU1_CPU2_SEPARATOR_SECTION || 86562306a36Sopenharmony_ci image->sec[i].offset == PAGING_SEPARATOR_SECTION) { 86662306a36Sopenharmony_ci IWL_DEBUG_FW(trans, 86762306a36Sopenharmony_ci "Break since Data not valid or Empty section, sec = %d\n", 86862306a36Sopenharmony_ci i); 86962306a36Sopenharmony_ci break; 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci ret = iwl_pcie_load_section(trans, i, &image->sec[i]); 87362306a36Sopenharmony_ci if (ret) 87462306a36Sopenharmony_ci return ret; 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci *first_ucode_section = last_read_idx; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci return 0; 88062306a36Sopenharmony_ci} 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_cistatic void iwl_pcie_apply_destination_ini(struct iwl_trans *trans) 88362306a36Sopenharmony_ci{ 88462306a36Sopenharmony_ci enum iwl_fw_ini_allocation_id alloc_id = IWL_FW_INI_ALLOCATION_ID_DBGC1; 88562306a36Sopenharmony_ci struct iwl_fw_ini_allocation_tlv *fw_mon_cfg = 88662306a36Sopenharmony_ci &trans->dbg.fw_mon_cfg[alloc_id]; 88762306a36Sopenharmony_ci struct iwl_dram_data *frag; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci if (!iwl_trans_dbg_ini_valid(trans)) 89062306a36Sopenharmony_ci return; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci if (le32_to_cpu(fw_mon_cfg->buf_location) == 89362306a36Sopenharmony_ci IWL_FW_INI_LOCATION_SRAM_PATH) { 89462306a36Sopenharmony_ci IWL_DEBUG_FW(trans, "WRT: Applying SMEM buffer destination\n"); 89562306a36Sopenharmony_ci /* set sram monitor by enabling bit 7 */ 89662306a36Sopenharmony_ci iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, 89762306a36Sopenharmony_ci CSR_HW_IF_CONFIG_REG_BIT_MONITOR_SRAM); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci return; 90062306a36Sopenharmony_ci } 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci if (le32_to_cpu(fw_mon_cfg->buf_location) != 90362306a36Sopenharmony_ci IWL_FW_INI_LOCATION_DRAM_PATH || 90462306a36Sopenharmony_ci !trans->dbg.fw_mon_ini[alloc_id].num_frags) 90562306a36Sopenharmony_ci return; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci frag = &trans->dbg.fw_mon_ini[alloc_id].frags[0]; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci IWL_DEBUG_FW(trans, "WRT: Applying DRAM destination (alloc_id=%u)\n", 91062306a36Sopenharmony_ci alloc_id); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci iwl_write_umac_prph(trans, MON_BUFF_BASE_ADDR_VER2, 91362306a36Sopenharmony_ci frag->physical >> MON_BUFF_SHIFT_VER2); 91462306a36Sopenharmony_ci iwl_write_umac_prph(trans, MON_BUFF_END_ADDR_VER2, 91562306a36Sopenharmony_ci (frag->physical + frag->size - 256) >> 91662306a36Sopenharmony_ci MON_BUFF_SHIFT_VER2); 91762306a36Sopenharmony_ci} 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_civoid iwl_pcie_apply_destination(struct iwl_trans *trans) 92062306a36Sopenharmony_ci{ 92162306a36Sopenharmony_ci const struct iwl_fw_dbg_dest_tlv_v1 *dest = trans->dbg.dest_tlv; 92262306a36Sopenharmony_ci const struct iwl_dram_data *fw_mon = &trans->dbg.fw_mon; 92362306a36Sopenharmony_ci int i; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci if (iwl_trans_dbg_ini_valid(trans)) { 92662306a36Sopenharmony_ci iwl_pcie_apply_destination_ini(trans); 92762306a36Sopenharmony_ci return; 92862306a36Sopenharmony_ci } 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci IWL_INFO(trans, "Applying debug destination %s\n", 93162306a36Sopenharmony_ci get_fw_dbg_mode_string(dest->monitor_mode)); 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci if (dest->monitor_mode == EXTERNAL_MODE) 93462306a36Sopenharmony_ci iwl_pcie_alloc_fw_monitor(trans, dest->size_power); 93562306a36Sopenharmony_ci else 93662306a36Sopenharmony_ci IWL_WARN(trans, "PCI should have external buffer debug\n"); 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci for (i = 0; i < trans->dbg.n_dest_reg; i++) { 93962306a36Sopenharmony_ci u32 addr = le32_to_cpu(dest->reg_ops[i].addr); 94062306a36Sopenharmony_ci u32 val = le32_to_cpu(dest->reg_ops[i].val); 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci switch (dest->reg_ops[i].op) { 94362306a36Sopenharmony_ci case CSR_ASSIGN: 94462306a36Sopenharmony_ci iwl_write32(trans, addr, val); 94562306a36Sopenharmony_ci break; 94662306a36Sopenharmony_ci case CSR_SETBIT: 94762306a36Sopenharmony_ci iwl_set_bit(trans, addr, BIT(val)); 94862306a36Sopenharmony_ci break; 94962306a36Sopenharmony_ci case CSR_CLEARBIT: 95062306a36Sopenharmony_ci iwl_clear_bit(trans, addr, BIT(val)); 95162306a36Sopenharmony_ci break; 95262306a36Sopenharmony_ci case PRPH_ASSIGN: 95362306a36Sopenharmony_ci iwl_write_prph(trans, addr, val); 95462306a36Sopenharmony_ci break; 95562306a36Sopenharmony_ci case PRPH_SETBIT: 95662306a36Sopenharmony_ci iwl_set_bits_prph(trans, addr, BIT(val)); 95762306a36Sopenharmony_ci break; 95862306a36Sopenharmony_ci case PRPH_CLEARBIT: 95962306a36Sopenharmony_ci iwl_clear_bits_prph(trans, addr, BIT(val)); 96062306a36Sopenharmony_ci break; 96162306a36Sopenharmony_ci case PRPH_BLOCKBIT: 96262306a36Sopenharmony_ci if (iwl_read_prph(trans, addr) & BIT(val)) { 96362306a36Sopenharmony_ci IWL_ERR(trans, 96462306a36Sopenharmony_ci "BIT(%u) in address 0x%x is 1, stopping FW configuration\n", 96562306a36Sopenharmony_ci val, addr); 96662306a36Sopenharmony_ci goto monitor; 96762306a36Sopenharmony_ci } 96862306a36Sopenharmony_ci break; 96962306a36Sopenharmony_ci default: 97062306a36Sopenharmony_ci IWL_ERR(trans, "FW debug - unknown OP %d\n", 97162306a36Sopenharmony_ci dest->reg_ops[i].op); 97262306a36Sopenharmony_ci break; 97362306a36Sopenharmony_ci } 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_cimonitor: 97762306a36Sopenharmony_ci if (dest->monitor_mode == EXTERNAL_MODE && fw_mon->size) { 97862306a36Sopenharmony_ci iwl_write_prph(trans, le32_to_cpu(dest->base_reg), 97962306a36Sopenharmony_ci fw_mon->physical >> dest->base_shift); 98062306a36Sopenharmony_ci if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_8000) 98162306a36Sopenharmony_ci iwl_write_prph(trans, le32_to_cpu(dest->end_reg), 98262306a36Sopenharmony_ci (fw_mon->physical + fw_mon->size - 98362306a36Sopenharmony_ci 256) >> dest->end_shift); 98462306a36Sopenharmony_ci else 98562306a36Sopenharmony_ci iwl_write_prph(trans, le32_to_cpu(dest->end_reg), 98662306a36Sopenharmony_ci (fw_mon->physical + fw_mon->size) >> 98762306a36Sopenharmony_ci dest->end_shift); 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci} 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_cistatic int iwl_pcie_load_given_ucode(struct iwl_trans *trans, 99262306a36Sopenharmony_ci const struct fw_img *image) 99362306a36Sopenharmony_ci{ 99462306a36Sopenharmony_ci int ret = 0; 99562306a36Sopenharmony_ci int first_ucode_section; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci IWL_DEBUG_FW(trans, "working with %s CPU\n", 99862306a36Sopenharmony_ci image->is_dual_cpus ? "Dual" : "Single"); 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci /* load to FW the binary non secured sections of CPU1 */ 100162306a36Sopenharmony_ci ret = iwl_pcie_load_cpu_sections(trans, image, 1, &first_ucode_section); 100262306a36Sopenharmony_ci if (ret) 100362306a36Sopenharmony_ci return ret; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci if (image->is_dual_cpus) { 100662306a36Sopenharmony_ci /* set CPU2 header address */ 100762306a36Sopenharmony_ci iwl_write_prph(trans, 100862306a36Sopenharmony_ci LMPM_SECURE_UCODE_LOAD_CPU2_HDR_ADDR, 100962306a36Sopenharmony_ci LMPM_SECURE_CPU2_HDR_MEM_SPACE); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci /* load to FW the binary sections of CPU2 */ 101262306a36Sopenharmony_ci ret = iwl_pcie_load_cpu_sections(trans, image, 2, 101362306a36Sopenharmony_ci &first_ucode_section); 101462306a36Sopenharmony_ci if (ret) 101562306a36Sopenharmony_ci return ret; 101662306a36Sopenharmony_ci } 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci if (iwl_pcie_dbg_on(trans)) 101962306a36Sopenharmony_ci iwl_pcie_apply_destination(trans); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci iwl_enable_interrupts(trans); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci /* release CPU reset */ 102462306a36Sopenharmony_ci iwl_write32(trans, CSR_RESET, 0); 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci return 0; 102762306a36Sopenharmony_ci} 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_cistatic int iwl_pcie_load_given_ucode_8000(struct iwl_trans *trans, 103062306a36Sopenharmony_ci const struct fw_img *image) 103162306a36Sopenharmony_ci{ 103262306a36Sopenharmony_ci int ret = 0; 103362306a36Sopenharmony_ci int first_ucode_section; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci IWL_DEBUG_FW(trans, "working with %s CPU\n", 103662306a36Sopenharmony_ci image->is_dual_cpus ? "Dual" : "Single"); 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci if (iwl_pcie_dbg_on(trans)) 103962306a36Sopenharmony_ci iwl_pcie_apply_destination(trans); 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci IWL_DEBUG_POWER(trans, "Original WFPM value = 0x%08X\n", 104262306a36Sopenharmony_ci iwl_read_prph(trans, WFPM_GP2)); 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci /* 104562306a36Sopenharmony_ci * Set default value. On resume reading the values that were 104662306a36Sopenharmony_ci * zeored can provide debug data on the resume flow. 104762306a36Sopenharmony_ci * This is for debugging only and has no functional impact. 104862306a36Sopenharmony_ci */ 104962306a36Sopenharmony_ci iwl_write_prph(trans, WFPM_GP2, 0x01010101); 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci /* configure the ucode to be ready to get the secured image */ 105262306a36Sopenharmony_ci /* release CPU reset */ 105362306a36Sopenharmony_ci iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT); 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci /* load to FW the binary Secured sections of CPU1 */ 105662306a36Sopenharmony_ci ret = iwl_pcie_load_cpu_sections_8000(trans, image, 1, 105762306a36Sopenharmony_ci &first_ucode_section); 105862306a36Sopenharmony_ci if (ret) 105962306a36Sopenharmony_ci return ret; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci /* load to FW the binary sections of CPU2 */ 106262306a36Sopenharmony_ci return iwl_pcie_load_cpu_sections_8000(trans, image, 2, 106362306a36Sopenharmony_ci &first_ucode_section); 106462306a36Sopenharmony_ci} 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_cibool iwl_pcie_check_hw_rf_kill(struct iwl_trans *trans) 106762306a36Sopenharmony_ci{ 106862306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 106962306a36Sopenharmony_ci bool hw_rfkill = iwl_is_rfkill_set(trans); 107062306a36Sopenharmony_ci bool prev = test_bit(STATUS_RFKILL_OPMODE, &trans->status); 107162306a36Sopenharmony_ci bool report; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci if (hw_rfkill) { 107462306a36Sopenharmony_ci set_bit(STATUS_RFKILL_HW, &trans->status); 107562306a36Sopenharmony_ci set_bit(STATUS_RFKILL_OPMODE, &trans->status); 107662306a36Sopenharmony_ci } else { 107762306a36Sopenharmony_ci clear_bit(STATUS_RFKILL_HW, &trans->status); 107862306a36Sopenharmony_ci if (trans_pcie->opmode_down) 107962306a36Sopenharmony_ci clear_bit(STATUS_RFKILL_OPMODE, &trans->status); 108062306a36Sopenharmony_ci } 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci report = test_bit(STATUS_RFKILL_OPMODE, &trans->status); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci if (prev != report) 108562306a36Sopenharmony_ci iwl_trans_pcie_rf_kill(trans, report, false); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci return hw_rfkill; 108862306a36Sopenharmony_ci} 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_cistruct iwl_causes_list { 109162306a36Sopenharmony_ci u16 mask_reg; 109262306a36Sopenharmony_ci u8 bit; 109362306a36Sopenharmony_ci u8 addr; 109462306a36Sopenharmony_ci}; 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci#define IWL_CAUSE(reg, mask) \ 109762306a36Sopenharmony_ci { \ 109862306a36Sopenharmony_ci .mask_reg = reg, \ 109962306a36Sopenharmony_ci .bit = ilog2(mask), \ 110062306a36Sopenharmony_ci .addr = ilog2(mask) + \ 110162306a36Sopenharmony_ci ((reg) == CSR_MSIX_FH_INT_MASK_AD ? -16 : \ 110262306a36Sopenharmony_ci (reg) == CSR_MSIX_HW_INT_MASK_AD ? 16 : \ 110362306a36Sopenharmony_ci 0xffff), /* causes overflow warning */ \ 110462306a36Sopenharmony_ci } 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_cistatic const struct iwl_causes_list causes_list_common[] = { 110762306a36Sopenharmony_ci IWL_CAUSE(CSR_MSIX_FH_INT_MASK_AD, MSIX_FH_INT_CAUSES_D2S_CH0_NUM), 110862306a36Sopenharmony_ci IWL_CAUSE(CSR_MSIX_FH_INT_MASK_AD, MSIX_FH_INT_CAUSES_D2S_CH1_NUM), 110962306a36Sopenharmony_ci IWL_CAUSE(CSR_MSIX_FH_INT_MASK_AD, MSIX_FH_INT_CAUSES_S2D), 111062306a36Sopenharmony_ci IWL_CAUSE(CSR_MSIX_FH_INT_MASK_AD, MSIX_FH_INT_CAUSES_FH_ERR), 111162306a36Sopenharmony_ci IWL_CAUSE(CSR_MSIX_HW_INT_MASK_AD, MSIX_HW_INT_CAUSES_REG_ALIVE), 111262306a36Sopenharmony_ci IWL_CAUSE(CSR_MSIX_HW_INT_MASK_AD, MSIX_HW_INT_CAUSES_REG_WAKEUP), 111362306a36Sopenharmony_ci IWL_CAUSE(CSR_MSIX_HW_INT_MASK_AD, MSIX_HW_INT_CAUSES_REG_RESET_DONE), 111462306a36Sopenharmony_ci IWL_CAUSE(CSR_MSIX_HW_INT_MASK_AD, MSIX_HW_INT_CAUSES_REG_CT_KILL), 111562306a36Sopenharmony_ci IWL_CAUSE(CSR_MSIX_HW_INT_MASK_AD, MSIX_HW_INT_CAUSES_REG_RF_KILL), 111662306a36Sopenharmony_ci IWL_CAUSE(CSR_MSIX_HW_INT_MASK_AD, MSIX_HW_INT_CAUSES_REG_PERIODIC), 111762306a36Sopenharmony_ci IWL_CAUSE(CSR_MSIX_HW_INT_MASK_AD, MSIX_HW_INT_CAUSES_REG_SCD), 111862306a36Sopenharmony_ci IWL_CAUSE(CSR_MSIX_HW_INT_MASK_AD, MSIX_HW_INT_CAUSES_REG_FH_TX), 111962306a36Sopenharmony_ci IWL_CAUSE(CSR_MSIX_HW_INT_MASK_AD, MSIX_HW_INT_CAUSES_REG_HW_ERR), 112062306a36Sopenharmony_ci IWL_CAUSE(CSR_MSIX_HW_INT_MASK_AD, MSIX_HW_INT_CAUSES_REG_HAP), 112162306a36Sopenharmony_ci}; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_cistatic const struct iwl_causes_list causes_list_pre_bz[] = { 112462306a36Sopenharmony_ci IWL_CAUSE(CSR_MSIX_HW_INT_MASK_AD, MSIX_HW_INT_CAUSES_REG_SW_ERR), 112562306a36Sopenharmony_ci}; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_cistatic const struct iwl_causes_list causes_list_bz[] = { 112862306a36Sopenharmony_ci IWL_CAUSE(CSR_MSIX_HW_INT_MASK_AD, MSIX_HW_INT_CAUSES_REG_SW_ERR_BZ), 112962306a36Sopenharmony_ci}; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_cistatic void iwl_pcie_map_list(struct iwl_trans *trans, 113262306a36Sopenharmony_ci const struct iwl_causes_list *causes, 113362306a36Sopenharmony_ci int arr_size, int val) 113462306a36Sopenharmony_ci{ 113562306a36Sopenharmony_ci int i; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci for (i = 0; i < arr_size; i++) { 113862306a36Sopenharmony_ci iwl_write8(trans, CSR_MSIX_IVAR(causes[i].addr), val); 113962306a36Sopenharmony_ci iwl_clear_bit(trans, causes[i].mask_reg, 114062306a36Sopenharmony_ci BIT(causes[i].bit)); 114162306a36Sopenharmony_ci } 114262306a36Sopenharmony_ci} 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_cistatic void iwl_pcie_map_non_rx_causes(struct iwl_trans *trans) 114562306a36Sopenharmony_ci{ 114662306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 114762306a36Sopenharmony_ci int val = trans_pcie->def_irq | MSIX_NON_AUTO_CLEAR_CAUSE; 114862306a36Sopenharmony_ci /* 114962306a36Sopenharmony_ci * Access all non RX causes and map them to the default irq. 115062306a36Sopenharmony_ci * In case we are missing at least one interrupt vector, 115162306a36Sopenharmony_ci * the first interrupt vector will serve non-RX and FBQ causes. 115262306a36Sopenharmony_ci */ 115362306a36Sopenharmony_ci iwl_pcie_map_list(trans, causes_list_common, 115462306a36Sopenharmony_ci ARRAY_SIZE(causes_list_common), val); 115562306a36Sopenharmony_ci if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) 115662306a36Sopenharmony_ci iwl_pcie_map_list(trans, causes_list_bz, 115762306a36Sopenharmony_ci ARRAY_SIZE(causes_list_bz), val); 115862306a36Sopenharmony_ci else 115962306a36Sopenharmony_ci iwl_pcie_map_list(trans, causes_list_pre_bz, 116062306a36Sopenharmony_ci ARRAY_SIZE(causes_list_pre_bz), val); 116162306a36Sopenharmony_ci} 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_cistatic void iwl_pcie_map_rx_causes(struct iwl_trans *trans) 116462306a36Sopenharmony_ci{ 116562306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 116662306a36Sopenharmony_ci u32 offset = 116762306a36Sopenharmony_ci trans_pcie->shared_vec_mask & IWL_SHARED_IRQ_FIRST_RSS ? 1 : 0; 116862306a36Sopenharmony_ci u32 val, idx; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci /* 117162306a36Sopenharmony_ci * The first RX queue - fallback queue, which is designated for 117262306a36Sopenharmony_ci * management frame, command responses etc, is always mapped to the 117362306a36Sopenharmony_ci * first interrupt vector. The other RX queues are mapped to 117462306a36Sopenharmony_ci * the other (N - 2) interrupt vectors. 117562306a36Sopenharmony_ci */ 117662306a36Sopenharmony_ci val = BIT(MSIX_FH_INT_CAUSES_Q(0)); 117762306a36Sopenharmony_ci for (idx = 1; idx < trans->num_rx_queues; idx++) { 117862306a36Sopenharmony_ci iwl_write8(trans, CSR_MSIX_RX_IVAR(idx), 117962306a36Sopenharmony_ci MSIX_FH_INT_CAUSES_Q(idx - offset)); 118062306a36Sopenharmony_ci val |= BIT(MSIX_FH_INT_CAUSES_Q(idx)); 118162306a36Sopenharmony_ci } 118262306a36Sopenharmony_ci iwl_write32(trans, CSR_MSIX_FH_INT_MASK_AD, ~val); 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci val = MSIX_FH_INT_CAUSES_Q(0); 118562306a36Sopenharmony_ci if (trans_pcie->shared_vec_mask & IWL_SHARED_IRQ_NON_RX) 118662306a36Sopenharmony_ci val |= MSIX_NON_AUTO_CLEAR_CAUSE; 118762306a36Sopenharmony_ci iwl_write8(trans, CSR_MSIX_RX_IVAR(0), val); 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci if (trans_pcie->shared_vec_mask & IWL_SHARED_IRQ_FIRST_RSS) 119062306a36Sopenharmony_ci iwl_write8(trans, CSR_MSIX_RX_IVAR(1), val); 119162306a36Sopenharmony_ci} 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_civoid iwl_pcie_conf_msix_hw(struct iwl_trans_pcie *trans_pcie) 119462306a36Sopenharmony_ci{ 119562306a36Sopenharmony_ci struct iwl_trans *trans = trans_pcie->trans; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci if (!trans_pcie->msix_enabled) { 119862306a36Sopenharmony_ci if (trans->trans_cfg->mq_rx_supported && 119962306a36Sopenharmony_ci test_bit(STATUS_DEVICE_ENABLED, &trans->status)) 120062306a36Sopenharmony_ci iwl_write_umac_prph(trans, UREG_CHICK, 120162306a36Sopenharmony_ci UREG_CHICK_MSI_ENABLE); 120262306a36Sopenharmony_ci return; 120362306a36Sopenharmony_ci } 120462306a36Sopenharmony_ci /* 120562306a36Sopenharmony_ci * The IVAR table needs to be configured again after reset, 120662306a36Sopenharmony_ci * but if the device is disabled, we can't write to 120762306a36Sopenharmony_ci * prph. 120862306a36Sopenharmony_ci */ 120962306a36Sopenharmony_ci if (test_bit(STATUS_DEVICE_ENABLED, &trans->status)) 121062306a36Sopenharmony_ci iwl_write_umac_prph(trans, UREG_CHICK, UREG_CHICK_MSIX_ENABLE); 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci /* 121362306a36Sopenharmony_ci * Each cause from the causes list above and the RX causes is 121462306a36Sopenharmony_ci * represented as a byte in the IVAR table. The first nibble 121562306a36Sopenharmony_ci * represents the bound interrupt vector of the cause, the second 121662306a36Sopenharmony_ci * represents no auto clear for this cause. This will be set if its 121762306a36Sopenharmony_ci * interrupt vector is bound to serve other causes. 121862306a36Sopenharmony_ci */ 121962306a36Sopenharmony_ci iwl_pcie_map_rx_causes(trans); 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci iwl_pcie_map_non_rx_causes(trans); 122262306a36Sopenharmony_ci} 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_cistatic void iwl_pcie_init_msix(struct iwl_trans_pcie *trans_pcie) 122562306a36Sopenharmony_ci{ 122662306a36Sopenharmony_ci struct iwl_trans *trans = trans_pcie->trans; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci iwl_pcie_conf_msix_hw(trans_pcie); 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci if (!trans_pcie->msix_enabled) 123162306a36Sopenharmony_ci return; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci trans_pcie->fh_init_mask = ~iwl_read32(trans, CSR_MSIX_FH_INT_MASK_AD); 123462306a36Sopenharmony_ci trans_pcie->fh_mask = trans_pcie->fh_init_mask; 123562306a36Sopenharmony_ci trans_pcie->hw_init_mask = ~iwl_read32(trans, CSR_MSIX_HW_INT_MASK_AD); 123662306a36Sopenharmony_ci trans_pcie->hw_mask = trans_pcie->hw_init_mask; 123762306a36Sopenharmony_ci} 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_cistatic void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool from_irq) 124062306a36Sopenharmony_ci{ 124162306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci lockdep_assert_held(&trans_pcie->mutex); 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci if (trans_pcie->is_down) 124662306a36Sopenharmony_ci return; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci trans_pcie->is_down = true; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci /* tell the device to stop sending interrupts */ 125162306a36Sopenharmony_ci iwl_disable_interrupts(trans); 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci /* device going down, Stop using ICT table */ 125462306a36Sopenharmony_ci iwl_pcie_disable_ict(trans); 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci /* 125762306a36Sopenharmony_ci * If a HW restart happens during firmware loading, 125862306a36Sopenharmony_ci * then the firmware loading might call this function 125962306a36Sopenharmony_ci * and later it might be called again due to the 126062306a36Sopenharmony_ci * restart. So don't process again if the device is 126162306a36Sopenharmony_ci * already dead. 126262306a36Sopenharmony_ci */ 126362306a36Sopenharmony_ci if (test_and_clear_bit(STATUS_DEVICE_ENABLED, &trans->status)) { 126462306a36Sopenharmony_ci IWL_DEBUG_INFO(trans, 126562306a36Sopenharmony_ci "DEVICE_ENABLED bit was set and is now cleared\n"); 126662306a36Sopenharmony_ci if (!from_irq) 126762306a36Sopenharmony_ci iwl_pcie_synchronize_irqs(trans); 126862306a36Sopenharmony_ci iwl_pcie_rx_napi_sync(trans); 126962306a36Sopenharmony_ci iwl_pcie_tx_stop(trans); 127062306a36Sopenharmony_ci iwl_pcie_rx_stop(trans); 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci /* Power-down device's busmaster DMA clocks */ 127362306a36Sopenharmony_ci if (!trans->cfg->apmg_not_supported) { 127462306a36Sopenharmony_ci iwl_write_prph(trans, APMG_CLK_DIS_REG, 127562306a36Sopenharmony_ci APMG_CLK_VAL_DMA_CLK_RQT); 127662306a36Sopenharmony_ci udelay(5); 127762306a36Sopenharmony_ci } 127862306a36Sopenharmony_ci } 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci /* Make sure (redundant) we've released our request to stay awake */ 128162306a36Sopenharmony_ci if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) 128262306a36Sopenharmony_ci iwl_clear_bit(trans, CSR_GP_CNTRL, 128362306a36Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_BZ_MAC_ACCESS_REQ); 128462306a36Sopenharmony_ci else 128562306a36Sopenharmony_ci iwl_clear_bit(trans, CSR_GP_CNTRL, 128662306a36Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci /* Stop the device, and put it in low power state */ 128962306a36Sopenharmony_ci iwl_pcie_apm_stop(trans, false); 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci /* re-take ownership to prevent other users from stealing the device */ 129262306a36Sopenharmony_ci iwl_trans_pcie_sw_reset(trans, true); 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci /* 129562306a36Sopenharmony_ci * Upon stop, the IVAR table gets erased, so msi-x won't 129662306a36Sopenharmony_ci * work. This causes a bug in RF-KILL flows, since the interrupt 129762306a36Sopenharmony_ci * that enables radio won't fire on the correct irq, and the 129862306a36Sopenharmony_ci * driver won't be able to handle the interrupt. 129962306a36Sopenharmony_ci * Configure the IVAR table again after reset. 130062306a36Sopenharmony_ci */ 130162306a36Sopenharmony_ci iwl_pcie_conf_msix_hw(trans_pcie); 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci /* 130462306a36Sopenharmony_ci * Upon stop, the APM issues an interrupt if HW RF kill is set. 130562306a36Sopenharmony_ci * This is a bug in certain verions of the hardware. 130662306a36Sopenharmony_ci * Certain devices also keep sending HW RF kill interrupt all 130762306a36Sopenharmony_ci * the time, unless the interrupt is ACKed even if the interrupt 130862306a36Sopenharmony_ci * should be masked. Re-ACK all the interrupts here. 130962306a36Sopenharmony_ci */ 131062306a36Sopenharmony_ci iwl_disable_interrupts(trans); 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci /* clear all status bits */ 131362306a36Sopenharmony_ci clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); 131462306a36Sopenharmony_ci clear_bit(STATUS_INT_ENABLED, &trans->status); 131562306a36Sopenharmony_ci clear_bit(STATUS_TPOWER_PMI, &trans->status); 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci /* 131862306a36Sopenharmony_ci * Even if we stop the HW, we still want the RF kill 131962306a36Sopenharmony_ci * interrupt 132062306a36Sopenharmony_ci */ 132162306a36Sopenharmony_ci iwl_enable_rfkill_int(trans); 132262306a36Sopenharmony_ci} 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_civoid iwl_pcie_synchronize_irqs(struct iwl_trans *trans) 132562306a36Sopenharmony_ci{ 132662306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci if (trans_pcie->msix_enabled) { 132962306a36Sopenharmony_ci int i; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci for (i = 0; i < trans_pcie->alloc_vecs; i++) 133262306a36Sopenharmony_ci synchronize_irq(trans_pcie->msix_entries[i].vector); 133362306a36Sopenharmony_ci } else { 133462306a36Sopenharmony_ci synchronize_irq(trans_pcie->pci_dev->irq); 133562306a36Sopenharmony_ci } 133662306a36Sopenharmony_ci} 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_cistatic int iwl_trans_pcie_start_fw(struct iwl_trans *trans, 133962306a36Sopenharmony_ci const struct fw_img *fw, bool run_in_rfkill) 134062306a36Sopenharmony_ci{ 134162306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 134262306a36Sopenharmony_ci bool hw_rfkill; 134362306a36Sopenharmony_ci int ret; 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci /* This may fail if AMT took ownership of the device */ 134662306a36Sopenharmony_ci if (iwl_pcie_prepare_card_hw(trans)) { 134762306a36Sopenharmony_ci IWL_WARN(trans, "Exit HW not ready\n"); 134862306a36Sopenharmony_ci return -EIO; 134962306a36Sopenharmony_ci } 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci iwl_enable_rfkill_int(trans); 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci iwl_write32(trans, CSR_INT, 0xFFFFFFFF); 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci /* 135662306a36Sopenharmony_ci * We enabled the RF-Kill interrupt and the handler may very 135762306a36Sopenharmony_ci * well be running. Disable the interrupts to make sure no other 135862306a36Sopenharmony_ci * interrupt can be fired. 135962306a36Sopenharmony_ci */ 136062306a36Sopenharmony_ci iwl_disable_interrupts(trans); 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci /* Make sure it finished running */ 136362306a36Sopenharmony_ci iwl_pcie_synchronize_irqs(trans); 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci mutex_lock(&trans_pcie->mutex); 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci /* If platform's RF_KILL switch is NOT set to KILL */ 136862306a36Sopenharmony_ci hw_rfkill = iwl_pcie_check_hw_rf_kill(trans); 136962306a36Sopenharmony_ci if (hw_rfkill && !run_in_rfkill) { 137062306a36Sopenharmony_ci ret = -ERFKILL; 137162306a36Sopenharmony_ci goto out; 137262306a36Sopenharmony_ci } 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci /* Someone called stop_device, don't try to start_fw */ 137562306a36Sopenharmony_ci if (trans_pcie->is_down) { 137662306a36Sopenharmony_ci IWL_WARN(trans, 137762306a36Sopenharmony_ci "Can't start_fw since the HW hasn't been started\n"); 137862306a36Sopenharmony_ci ret = -EIO; 137962306a36Sopenharmony_ci goto out; 138062306a36Sopenharmony_ci } 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci /* make sure rfkill handshake bits are cleared */ 138362306a36Sopenharmony_ci iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); 138462306a36Sopenharmony_ci iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, 138562306a36Sopenharmony_ci CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci /* clear (again), then enable host interrupts */ 138862306a36Sopenharmony_ci iwl_write32(trans, CSR_INT, 0xFFFFFFFF); 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci ret = iwl_pcie_nic_init(trans); 139162306a36Sopenharmony_ci if (ret) { 139262306a36Sopenharmony_ci IWL_ERR(trans, "Unable to init nic\n"); 139362306a36Sopenharmony_ci goto out; 139462306a36Sopenharmony_ci } 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci /* 139762306a36Sopenharmony_ci * Now, we load the firmware and don't want to be interrupted, even 139862306a36Sopenharmony_ci * by the RF-Kill interrupt (hence mask all the interrupt besides the 139962306a36Sopenharmony_ci * FH_TX interrupt which is needed to load the firmware). If the 140062306a36Sopenharmony_ci * RF-Kill switch is toggled, we will find out after having loaded 140162306a36Sopenharmony_ci * the firmware and return the proper value to the caller. 140262306a36Sopenharmony_ci */ 140362306a36Sopenharmony_ci iwl_enable_fw_load_int(trans); 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci /* really make sure rfkill handshake bits are cleared */ 140662306a36Sopenharmony_ci iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); 140762306a36Sopenharmony_ci iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci /* Load the given image to the HW */ 141062306a36Sopenharmony_ci if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_8000) 141162306a36Sopenharmony_ci ret = iwl_pcie_load_given_ucode_8000(trans, fw); 141262306a36Sopenharmony_ci else 141362306a36Sopenharmony_ci ret = iwl_pcie_load_given_ucode(trans, fw); 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci /* re-check RF-Kill state since we may have missed the interrupt */ 141662306a36Sopenharmony_ci hw_rfkill = iwl_pcie_check_hw_rf_kill(trans); 141762306a36Sopenharmony_ci if (hw_rfkill && !run_in_rfkill) 141862306a36Sopenharmony_ci ret = -ERFKILL; 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ciout: 142162306a36Sopenharmony_ci mutex_unlock(&trans_pcie->mutex); 142262306a36Sopenharmony_ci return ret; 142362306a36Sopenharmony_ci} 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_cistatic void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr) 142662306a36Sopenharmony_ci{ 142762306a36Sopenharmony_ci iwl_pcie_reset_ict(trans); 142862306a36Sopenharmony_ci iwl_pcie_tx_start(trans, scd_addr); 142962306a36Sopenharmony_ci} 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_civoid iwl_trans_pcie_handle_stop_rfkill(struct iwl_trans *trans, 143262306a36Sopenharmony_ci bool was_in_rfkill) 143362306a36Sopenharmony_ci{ 143462306a36Sopenharmony_ci bool hw_rfkill; 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci /* 143762306a36Sopenharmony_ci * Check again since the RF kill state may have changed while 143862306a36Sopenharmony_ci * all the interrupts were disabled, in this case we couldn't 143962306a36Sopenharmony_ci * receive the RF kill interrupt and update the state in the 144062306a36Sopenharmony_ci * op_mode. 144162306a36Sopenharmony_ci * Don't call the op_mode if the rkfill state hasn't changed. 144262306a36Sopenharmony_ci * This allows the op_mode to call stop_device from the rfkill 144362306a36Sopenharmony_ci * notification without endless recursion. Under very rare 144462306a36Sopenharmony_ci * circumstances, we might have a small recursion if the rfkill 144562306a36Sopenharmony_ci * state changed exactly now while we were called from stop_device. 144662306a36Sopenharmony_ci * This is very unlikely but can happen and is supported. 144762306a36Sopenharmony_ci */ 144862306a36Sopenharmony_ci hw_rfkill = iwl_is_rfkill_set(trans); 144962306a36Sopenharmony_ci if (hw_rfkill) { 145062306a36Sopenharmony_ci set_bit(STATUS_RFKILL_HW, &trans->status); 145162306a36Sopenharmony_ci set_bit(STATUS_RFKILL_OPMODE, &trans->status); 145262306a36Sopenharmony_ci } else { 145362306a36Sopenharmony_ci clear_bit(STATUS_RFKILL_HW, &trans->status); 145462306a36Sopenharmony_ci clear_bit(STATUS_RFKILL_OPMODE, &trans->status); 145562306a36Sopenharmony_ci } 145662306a36Sopenharmony_ci if (hw_rfkill != was_in_rfkill) 145762306a36Sopenharmony_ci iwl_trans_pcie_rf_kill(trans, hw_rfkill, false); 145862306a36Sopenharmony_ci} 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_cistatic void iwl_trans_pcie_stop_device(struct iwl_trans *trans) 146162306a36Sopenharmony_ci{ 146262306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 146362306a36Sopenharmony_ci bool was_in_rfkill; 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci iwl_op_mode_time_point(trans->op_mode, 146662306a36Sopenharmony_ci IWL_FW_INI_TIME_POINT_HOST_DEVICE_DISABLE, 146762306a36Sopenharmony_ci NULL); 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci mutex_lock(&trans_pcie->mutex); 147062306a36Sopenharmony_ci trans_pcie->opmode_down = true; 147162306a36Sopenharmony_ci was_in_rfkill = test_bit(STATUS_RFKILL_OPMODE, &trans->status); 147262306a36Sopenharmony_ci _iwl_trans_pcie_stop_device(trans, false); 147362306a36Sopenharmony_ci iwl_trans_pcie_handle_stop_rfkill(trans, was_in_rfkill); 147462306a36Sopenharmony_ci mutex_unlock(&trans_pcie->mutex); 147562306a36Sopenharmony_ci} 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_civoid iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state, bool from_irq) 147862306a36Sopenharmony_ci{ 147962306a36Sopenharmony_ci struct iwl_trans_pcie __maybe_unused *trans_pcie = 148062306a36Sopenharmony_ci IWL_TRANS_GET_PCIE_TRANS(trans); 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci lockdep_assert_held(&trans_pcie->mutex); 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci IWL_WARN(trans, "reporting RF_KILL (radio %s)\n", 148562306a36Sopenharmony_ci state ? "disabled" : "enabled"); 148662306a36Sopenharmony_ci if (iwl_op_mode_hw_rf_kill(trans->op_mode, state)) { 148762306a36Sopenharmony_ci if (trans->trans_cfg->gen2) 148862306a36Sopenharmony_ci _iwl_trans_pcie_gen2_stop_device(trans); 148962306a36Sopenharmony_ci else 149062306a36Sopenharmony_ci _iwl_trans_pcie_stop_device(trans, from_irq); 149162306a36Sopenharmony_ci } 149262306a36Sopenharmony_ci} 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_civoid iwl_pcie_d3_complete_suspend(struct iwl_trans *trans, 149562306a36Sopenharmony_ci bool test, bool reset) 149662306a36Sopenharmony_ci{ 149762306a36Sopenharmony_ci iwl_disable_interrupts(trans); 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci /* 150062306a36Sopenharmony_ci * in testing mode, the host stays awake and the 150162306a36Sopenharmony_ci * hardware won't be reset (not even partially) 150262306a36Sopenharmony_ci */ 150362306a36Sopenharmony_ci if (test) 150462306a36Sopenharmony_ci return; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci iwl_pcie_disable_ict(trans); 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci iwl_pcie_synchronize_irqs(trans); 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci iwl_clear_bit(trans, CSR_GP_CNTRL, 151162306a36Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 151262306a36Sopenharmony_ci iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci if (reset) { 151562306a36Sopenharmony_ci /* 151662306a36Sopenharmony_ci * reset TX queues -- some of their registers reset during S3 151762306a36Sopenharmony_ci * so if we don't reset everything here the D3 image would try 151862306a36Sopenharmony_ci * to execute some invalid memory upon resume 151962306a36Sopenharmony_ci */ 152062306a36Sopenharmony_ci iwl_trans_pcie_tx_reset(trans); 152162306a36Sopenharmony_ci } 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci iwl_pcie_set_pwr(trans, true); 152462306a36Sopenharmony_ci} 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_cistatic int iwl_pcie_d3_handshake(struct iwl_trans *trans, bool suspend) 152762306a36Sopenharmony_ci{ 152862306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 152962306a36Sopenharmony_ci int ret; 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_AX210) 153262306a36Sopenharmony_ci iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6, 153362306a36Sopenharmony_ci suspend ? UREG_DOORBELL_TO_ISR6_SUSPEND : 153462306a36Sopenharmony_ci UREG_DOORBELL_TO_ISR6_RESUME); 153562306a36Sopenharmony_ci else if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) 153662306a36Sopenharmony_ci iwl_write32(trans, CSR_IPC_SLEEP_CONTROL, 153762306a36Sopenharmony_ci suspend ? CSR_IPC_SLEEP_CONTROL_SUSPEND : 153862306a36Sopenharmony_ci CSR_IPC_SLEEP_CONTROL_RESUME); 153962306a36Sopenharmony_ci else 154062306a36Sopenharmony_ci return 0; 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci ret = wait_event_timeout(trans_pcie->sx_waitq, 154362306a36Sopenharmony_ci trans_pcie->sx_complete, 2 * HZ); 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci /* Invalidate it toward next suspend or resume */ 154662306a36Sopenharmony_ci trans_pcie->sx_complete = false; 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci if (!ret) { 154962306a36Sopenharmony_ci IWL_ERR(trans, "Timeout %s D3\n", 155062306a36Sopenharmony_ci suspend ? "entering" : "exiting"); 155162306a36Sopenharmony_ci return -ETIMEDOUT; 155262306a36Sopenharmony_ci } 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci return 0; 155562306a36Sopenharmony_ci} 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_cistatic int iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test, 155862306a36Sopenharmony_ci bool reset) 155962306a36Sopenharmony_ci{ 156062306a36Sopenharmony_ci int ret; 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci if (!reset) 156362306a36Sopenharmony_ci /* Enable persistence mode to avoid reset */ 156462306a36Sopenharmony_ci iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, 156562306a36Sopenharmony_ci CSR_HW_IF_CONFIG_REG_PERSIST_MODE); 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci ret = iwl_pcie_d3_handshake(trans, true); 156862306a36Sopenharmony_ci if (ret) 156962306a36Sopenharmony_ci return ret; 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci iwl_pcie_d3_complete_suspend(trans, test, reset); 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci return 0; 157462306a36Sopenharmony_ci} 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_cistatic int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, 157762306a36Sopenharmony_ci enum iwl_d3_status *status, 157862306a36Sopenharmony_ci bool test, bool reset) 157962306a36Sopenharmony_ci{ 158062306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 158162306a36Sopenharmony_ci u32 val; 158262306a36Sopenharmony_ci int ret; 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci if (test) { 158562306a36Sopenharmony_ci iwl_enable_interrupts(trans); 158662306a36Sopenharmony_ci *status = IWL_D3_STATUS_ALIVE; 158762306a36Sopenharmony_ci ret = 0; 158862306a36Sopenharmony_ci goto out; 158962306a36Sopenharmony_ci } 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci iwl_set_bit(trans, CSR_GP_CNTRL, 159262306a36Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci ret = iwl_finish_nic_init(trans); 159562306a36Sopenharmony_ci if (ret) 159662306a36Sopenharmony_ci return ret; 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci /* 159962306a36Sopenharmony_ci * Reconfigure IVAR table in case of MSIX or reset ict table in 160062306a36Sopenharmony_ci * MSI mode since HW reset erased it. 160162306a36Sopenharmony_ci * Also enables interrupts - none will happen as 160262306a36Sopenharmony_ci * the device doesn't know we're waking it up, only when 160362306a36Sopenharmony_ci * the opmode actually tells it after this call. 160462306a36Sopenharmony_ci */ 160562306a36Sopenharmony_ci iwl_pcie_conf_msix_hw(trans_pcie); 160662306a36Sopenharmony_ci if (!trans_pcie->msix_enabled) 160762306a36Sopenharmony_ci iwl_pcie_reset_ict(trans); 160862306a36Sopenharmony_ci iwl_enable_interrupts(trans); 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci iwl_pcie_set_pwr(trans, false); 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci if (!reset) { 161362306a36Sopenharmony_ci iwl_clear_bit(trans, CSR_GP_CNTRL, 161462306a36Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 161562306a36Sopenharmony_ci } else { 161662306a36Sopenharmony_ci iwl_trans_pcie_tx_reset(trans); 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci ret = iwl_pcie_rx_init(trans); 161962306a36Sopenharmony_ci if (ret) { 162062306a36Sopenharmony_ci IWL_ERR(trans, 162162306a36Sopenharmony_ci "Failed to resume the device (RX reset)\n"); 162262306a36Sopenharmony_ci return ret; 162362306a36Sopenharmony_ci } 162462306a36Sopenharmony_ci } 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci IWL_DEBUG_POWER(trans, "WFPM value upon resume = 0x%08X\n", 162762306a36Sopenharmony_ci iwl_read_umac_prph(trans, WFPM_GP2)); 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci val = iwl_read32(trans, CSR_RESET); 163062306a36Sopenharmony_ci if (val & CSR_RESET_REG_FLAG_NEVO_RESET) 163162306a36Sopenharmony_ci *status = IWL_D3_STATUS_RESET; 163262306a36Sopenharmony_ci else 163362306a36Sopenharmony_ci *status = IWL_D3_STATUS_ALIVE; 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ciout: 163662306a36Sopenharmony_ci if (*status == IWL_D3_STATUS_ALIVE) 163762306a36Sopenharmony_ci ret = iwl_pcie_d3_handshake(trans, false); 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci return ret; 164062306a36Sopenharmony_ci} 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_cistatic void 164362306a36Sopenharmony_ciiwl_pcie_set_interrupt_capa(struct pci_dev *pdev, 164462306a36Sopenharmony_ci struct iwl_trans *trans, 164562306a36Sopenharmony_ci const struct iwl_cfg_trans_params *cfg_trans) 164662306a36Sopenharmony_ci{ 164762306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 164862306a36Sopenharmony_ci int max_irqs, num_irqs, i, ret; 164962306a36Sopenharmony_ci u16 pci_cmd; 165062306a36Sopenharmony_ci u32 max_rx_queues = IWL_MAX_RX_HW_QUEUES; 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci if (!cfg_trans->mq_rx_supported) 165362306a36Sopenharmony_ci goto enable_msi; 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci if (cfg_trans->device_family <= IWL_DEVICE_FAMILY_9000) 165662306a36Sopenharmony_ci max_rx_queues = IWL_9000_MAX_RX_HW_QUEUES; 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci max_irqs = min_t(u32, num_online_cpus() + 2, max_rx_queues); 165962306a36Sopenharmony_ci for (i = 0; i < max_irqs; i++) 166062306a36Sopenharmony_ci trans_pcie->msix_entries[i].entry = i; 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_ci num_irqs = pci_enable_msix_range(pdev, trans_pcie->msix_entries, 166362306a36Sopenharmony_ci MSIX_MIN_INTERRUPT_VECTORS, 166462306a36Sopenharmony_ci max_irqs); 166562306a36Sopenharmony_ci if (num_irqs < 0) { 166662306a36Sopenharmony_ci IWL_DEBUG_INFO(trans, 166762306a36Sopenharmony_ci "Failed to enable msi-x mode (ret %d). Moving to msi mode.\n", 166862306a36Sopenharmony_ci num_irqs); 166962306a36Sopenharmony_ci goto enable_msi; 167062306a36Sopenharmony_ci } 167162306a36Sopenharmony_ci trans_pcie->def_irq = (num_irqs == max_irqs) ? num_irqs - 1 : 0; 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci IWL_DEBUG_INFO(trans, 167462306a36Sopenharmony_ci "MSI-X enabled. %d interrupt vectors were allocated\n", 167562306a36Sopenharmony_ci num_irqs); 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci /* 167862306a36Sopenharmony_ci * In case the OS provides fewer interrupts than requested, different 167962306a36Sopenharmony_ci * causes will share the same interrupt vector as follows: 168062306a36Sopenharmony_ci * One interrupt less: non rx causes shared with FBQ. 168162306a36Sopenharmony_ci * Two interrupts less: non rx causes shared with FBQ and RSS. 168262306a36Sopenharmony_ci * More than two interrupts: we will use fewer RSS queues. 168362306a36Sopenharmony_ci */ 168462306a36Sopenharmony_ci if (num_irqs <= max_irqs - 2) { 168562306a36Sopenharmony_ci trans_pcie->trans->num_rx_queues = num_irqs + 1; 168662306a36Sopenharmony_ci trans_pcie->shared_vec_mask = IWL_SHARED_IRQ_NON_RX | 168762306a36Sopenharmony_ci IWL_SHARED_IRQ_FIRST_RSS; 168862306a36Sopenharmony_ci } else if (num_irqs == max_irqs - 1) { 168962306a36Sopenharmony_ci trans_pcie->trans->num_rx_queues = num_irqs; 169062306a36Sopenharmony_ci trans_pcie->shared_vec_mask = IWL_SHARED_IRQ_NON_RX; 169162306a36Sopenharmony_ci } else { 169262306a36Sopenharmony_ci trans_pcie->trans->num_rx_queues = num_irqs - 1; 169362306a36Sopenharmony_ci } 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci IWL_DEBUG_INFO(trans, 169662306a36Sopenharmony_ci "MSI-X enabled with rx queues %d, vec mask 0x%x\n", 169762306a36Sopenharmony_ci trans_pcie->trans->num_rx_queues, trans_pcie->shared_vec_mask); 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci WARN_ON(trans_pcie->trans->num_rx_queues > IWL_MAX_RX_HW_QUEUES); 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci trans_pcie->alloc_vecs = num_irqs; 170262306a36Sopenharmony_ci trans_pcie->msix_enabled = true; 170362306a36Sopenharmony_ci return; 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_cienable_msi: 170662306a36Sopenharmony_ci ret = pci_enable_msi(pdev); 170762306a36Sopenharmony_ci if (ret) { 170862306a36Sopenharmony_ci dev_err(&pdev->dev, "pci_enable_msi failed - %d\n", ret); 170962306a36Sopenharmony_ci /* enable rfkill interrupt: hw bug w/a */ 171062306a36Sopenharmony_ci pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd); 171162306a36Sopenharmony_ci if (pci_cmd & PCI_COMMAND_INTX_DISABLE) { 171262306a36Sopenharmony_ci pci_cmd &= ~PCI_COMMAND_INTX_DISABLE; 171362306a36Sopenharmony_ci pci_write_config_word(pdev, PCI_COMMAND, pci_cmd); 171462306a36Sopenharmony_ci } 171562306a36Sopenharmony_ci } 171662306a36Sopenharmony_ci} 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_cistatic void iwl_pcie_irq_set_affinity(struct iwl_trans *trans) 171962306a36Sopenharmony_ci{ 172062306a36Sopenharmony_ci int iter_rx_q, i, ret, cpu, offset; 172162306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci i = trans_pcie->shared_vec_mask & IWL_SHARED_IRQ_FIRST_RSS ? 0 : 1; 172462306a36Sopenharmony_ci iter_rx_q = trans_pcie->trans->num_rx_queues - 1 + i; 172562306a36Sopenharmony_ci offset = 1 + i; 172662306a36Sopenharmony_ci for (; i < iter_rx_q ; i++) { 172762306a36Sopenharmony_ci /* 172862306a36Sopenharmony_ci * Get the cpu prior to the place to search 172962306a36Sopenharmony_ci * (i.e. return will be > i - 1). 173062306a36Sopenharmony_ci */ 173162306a36Sopenharmony_ci cpu = cpumask_next(i - offset, cpu_online_mask); 173262306a36Sopenharmony_ci cpumask_set_cpu(cpu, &trans_pcie->affinity_mask[i]); 173362306a36Sopenharmony_ci ret = irq_set_affinity_hint(trans_pcie->msix_entries[i].vector, 173462306a36Sopenharmony_ci &trans_pcie->affinity_mask[i]); 173562306a36Sopenharmony_ci if (ret) 173662306a36Sopenharmony_ci IWL_ERR(trans_pcie->trans, 173762306a36Sopenharmony_ci "Failed to set affinity mask for IRQ %d\n", 173862306a36Sopenharmony_ci trans_pcie->msix_entries[i].vector); 173962306a36Sopenharmony_ci } 174062306a36Sopenharmony_ci} 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_cistatic int iwl_pcie_init_msix_handler(struct pci_dev *pdev, 174362306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie) 174462306a36Sopenharmony_ci{ 174562306a36Sopenharmony_ci int i; 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci for (i = 0; i < trans_pcie->alloc_vecs; i++) { 174862306a36Sopenharmony_ci int ret; 174962306a36Sopenharmony_ci struct msix_entry *msix_entry; 175062306a36Sopenharmony_ci const char *qname = queue_name(&pdev->dev, trans_pcie, i); 175162306a36Sopenharmony_ci 175262306a36Sopenharmony_ci if (!qname) 175362306a36Sopenharmony_ci return -ENOMEM; 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci msix_entry = &trans_pcie->msix_entries[i]; 175662306a36Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, 175762306a36Sopenharmony_ci msix_entry->vector, 175862306a36Sopenharmony_ci iwl_pcie_msix_isr, 175962306a36Sopenharmony_ci (i == trans_pcie->def_irq) ? 176062306a36Sopenharmony_ci iwl_pcie_irq_msix_handler : 176162306a36Sopenharmony_ci iwl_pcie_irq_rx_msix_handler, 176262306a36Sopenharmony_ci IRQF_SHARED, 176362306a36Sopenharmony_ci qname, 176462306a36Sopenharmony_ci msix_entry); 176562306a36Sopenharmony_ci if (ret) { 176662306a36Sopenharmony_ci IWL_ERR(trans_pcie->trans, 176762306a36Sopenharmony_ci "Error allocating IRQ %d\n", i); 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci return ret; 177062306a36Sopenharmony_ci } 177162306a36Sopenharmony_ci } 177262306a36Sopenharmony_ci iwl_pcie_irq_set_affinity(trans_pcie->trans); 177362306a36Sopenharmony_ci 177462306a36Sopenharmony_ci return 0; 177562306a36Sopenharmony_ci} 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_cistatic int iwl_trans_pcie_clear_persistence_bit(struct iwl_trans *trans) 177862306a36Sopenharmony_ci{ 177962306a36Sopenharmony_ci u32 hpm, wprot; 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci switch (trans->trans_cfg->device_family) { 178262306a36Sopenharmony_ci case IWL_DEVICE_FAMILY_9000: 178362306a36Sopenharmony_ci wprot = PREG_PRPH_WPROT_9000; 178462306a36Sopenharmony_ci break; 178562306a36Sopenharmony_ci case IWL_DEVICE_FAMILY_22000: 178662306a36Sopenharmony_ci wprot = PREG_PRPH_WPROT_22000; 178762306a36Sopenharmony_ci break; 178862306a36Sopenharmony_ci default: 178962306a36Sopenharmony_ci return 0; 179062306a36Sopenharmony_ci } 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ci hpm = iwl_read_umac_prph_no_grab(trans, HPM_DEBUG); 179362306a36Sopenharmony_ci if (!iwl_trans_is_hw_error_value(hpm) && (hpm & PERSISTENCE_BIT)) { 179462306a36Sopenharmony_ci u32 wprot_val = iwl_read_umac_prph_no_grab(trans, wprot); 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci if (wprot_val & PREG_WFPM_ACCESS) { 179762306a36Sopenharmony_ci IWL_ERR(trans, 179862306a36Sopenharmony_ci "Error, can not clear persistence bit\n"); 179962306a36Sopenharmony_ci return -EPERM; 180062306a36Sopenharmony_ci } 180162306a36Sopenharmony_ci iwl_write_umac_prph_no_grab(trans, HPM_DEBUG, 180262306a36Sopenharmony_ci hpm & ~PERSISTENCE_BIT); 180362306a36Sopenharmony_ci } 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_ci return 0; 180662306a36Sopenharmony_ci} 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_cistatic int iwl_pcie_gen2_force_power_gating(struct iwl_trans *trans) 180962306a36Sopenharmony_ci{ 181062306a36Sopenharmony_ci int ret; 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_ci ret = iwl_finish_nic_init(trans); 181362306a36Sopenharmony_ci if (ret < 0) 181462306a36Sopenharmony_ci return ret; 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci iwl_set_bits_prph(trans, HPM_HIPM_GEN_CFG, 181762306a36Sopenharmony_ci HPM_HIPM_GEN_CFG_CR_FORCE_ACTIVE); 181862306a36Sopenharmony_ci udelay(20); 181962306a36Sopenharmony_ci iwl_set_bits_prph(trans, HPM_HIPM_GEN_CFG, 182062306a36Sopenharmony_ci HPM_HIPM_GEN_CFG_CR_PG_EN | 182162306a36Sopenharmony_ci HPM_HIPM_GEN_CFG_CR_SLP_EN); 182262306a36Sopenharmony_ci udelay(20); 182362306a36Sopenharmony_ci iwl_clear_bits_prph(trans, HPM_HIPM_GEN_CFG, 182462306a36Sopenharmony_ci HPM_HIPM_GEN_CFG_CR_FORCE_ACTIVE); 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci return iwl_trans_pcie_sw_reset(trans, true); 182762306a36Sopenharmony_ci} 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_cistatic int _iwl_trans_pcie_start_hw(struct iwl_trans *trans) 183062306a36Sopenharmony_ci{ 183162306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 183262306a36Sopenharmony_ci int err; 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci lockdep_assert_held(&trans_pcie->mutex); 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci err = iwl_pcie_prepare_card_hw(trans); 183762306a36Sopenharmony_ci if (err) { 183862306a36Sopenharmony_ci IWL_ERR(trans, "Error while preparing HW: %d\n", err); 183962306a36Sopenharmony_ci return err; 184062306a36Sopenharmony_ci } 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_ci err = iwl_trans_pcie_clear_persistence_bit(trans); 184362306a36Sopenharmony_ci if (err) 184462306a36Sopenharmony_ci return err; 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci err = iwl_trans_pcie_sw_reset(trans, true); 184762306a36Sopenharmony_ci if (err) 184862306a36Sopenharmony_ci return err; 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_22000 && 185162306a36Sopenharmony_ci trans->trans_cfg->integrated) { 185262306a36Sopenharmony_ci err = iwl_pcie_gen2_force_power_gating(trans); 185362306a36Sopenharmony_ci if (err) 185462306a36Sopenharmony_ci return err; 185562306a36Sopenharmony_ci } 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci err = iwl_pcie_apm_init(trans); 185862306a36Sopenharmony_ci if (err) 185962306a36Sopenharmony_ci return err; 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci iwl_pcie_init_msix(trans_pcie); 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ci /* From now on, the op_mode will be kept updated about RF kill state */ 186462306a36Sopenharmony_ci iwl_enable_rfkill_int(trans); 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci trans_pcie->opmode_down = false; 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci /* Set is_down to false here so that...*/ 186962306a36Sopenharmony_ci trans_pcie->is_down = false; 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci /* ...rfkill can call stop_device and set it false if needed */ 187262306a36Sopenharmony_ci iwl_pcie_check_hw_rf_kill(trans); 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_ci return 0; 187562306a36Sopenharmony_ci} 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_cistatic int iwl_trans_pcie_start_hw(struct iwl_trans *trans) 187862306a36Sopenharmony_ci{ 187962306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 188062306a36Sopenharmony_ci int ret; 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_ci mutex_lock(&trans_pcie->mutex); 188362306a36Sopenharmony_ci ret = _iwl_trans_pcie_start_hw(trans); 188462306a36Sopenharmony_ci mutex_unlock(&trans_pcie->mutex); 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci return ret; 188762306a36Sopenharmony_ci} 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_cistatic void iwl_trans_pcie_op_mode_leave(struct iwl_trans *trans) 189062306a36Sopenharmony_ci{ 189162306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci mutex_lock(&trans_pcie->mutex); 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_ci /* disable interrupts - don't enable HW RF kill interrupt */ 189662306a36Sopenharmony_ci iwl_disable_interrupts(trans); 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_ci iwl_pcie_apm_stop(trans, true); 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_ci iwl_disable_interrupts(trans); 190162306a36Sopenharmony_ci 190262306a36Sopenharmony_ci iwl_pcie_disable_ict(trans); 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_ci mutex_unlock(&trans_pcie->mutex); 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_ci iwl_pcie_synchronize_irqs(trans); 190762306a36Sopenharmony_ci} 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_cistatic void iwl_trans_pcie_write8(struct iwl_trans *trans, u32 ofs, u8 val) 191062306a36Sopenharmony_ci{ 191162306a36Sopenharmony_ci writeb(val, IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs); 191262306a36Sopenharmony_ci} 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_cistatic void iwl_trans_pcie_write32(struct iwl_trans *trans, u32 ofs, u32 val) 191562306a36Sopenharmony_ci{ 191662306a36Sopenharmony_ci writel(val, IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs); 191762306a36Sopenharmony_ci} 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_cistatic u32 iwl_trans_pcie_read32(struct iwl_trans *trans, u32 ofs) 192062306a36Sopenharmony_ci{ 192162306a36Sopenharmony_ci return readl(IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs); 192262306a36Sopenharmony_ci} 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_cistatic u32 iwl_trans_pcie_prph_msk(struct iwl_trans *trans) 192562306a36Sopenharmony_ci{ 192662306a36Sopenharmony_ci if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) 192762306a36Sopenharmony_ci return 0x00FFFFFF; 192862306a36Sopenharmony_ci else 192962306a36Sopenharmony_ci return 0x000FFFFF; 193062306a36Sopenharmony_ci} 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_cistatic u32 iwl_trans_pcie_read_prph(struct iwl_trans *trans, u32 reg) 193362306a36Sopenharmony_ci{ 193462306a36Sopenharmony_ci u32 mask = iwl_trans_pcie_prph_msk(trans); 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_ci iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_RADDR, 193762306a36Sopenharmony_ci ((reg & mask) | (3 << 24))); 193862306a36Sopenharmony_ci return iwl_trans_pcie_read32(trans, HBUS_TARG_PRPH_RDAT); 193962306a36Sopenharmony_ci} 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_cistatic void iwl_trans_pcie_write_prph(struct iwl_trans *trans, u32 addr, 194262306a36Sopenharmony_ci u32 val) 194362306a36Sopenharmony_ci{ 194462306a36Sopenharmony_ci u32 mask = iwl_trans_pcie_prph_msk(trans); 194562306a36Sopenharmony_ci 194662306a36Sopenharmony_ci iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_WADDR, 194762306a36Sopenharmony_ci ((addr & mask) | (3 << 24))); 194862306a36Sopenharmony_ci iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_WDAT, val); 194962306a36Sopenharmony_ci} 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_cistatic void iwl_trans_pcie_configure(struct iwl_trans *trans, 195262306a36Sopenharmony_ci const struct iwl_trans_config *trans_cfg) 195362306a36Sopenharmony_ci{ 195462306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 195562306a36Sopenharmony_ci 195662306a36Sopenharmony_ci /* free all first - we might be reconfigured for a different size */ 195762306a36Sopenharmony_ci iwl_pcie_free_rbs_pool(trans); 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci trans->txqs.cmd.q_id = trans_cfg->cmd_queue; 196062306a36Sopenharmony_ci trans->txqs.cmd.fifo = trans_cfg->cmd_fifo; 196162306a36Sopenharmony_ci trans->txqs.cmd.wdg_timeout = trans_cfg->cmd_q_wdg_timeout; 196262306a36Sopenharmony_ci trans->txqs.page_offs = trans_cfg->cb_data_offs; 196362306a36Sopenharmony_ci trans->txqs.dev_cmd_offs = trans_cfg->cb_data_offs + sizeof(void *); 196462306a36Sopenharmony_ci trans->txqs.queue_alloc_cmd_ver = trans_cfg->queue_alloc_cmd_ver; 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci if (WARN_ON(trans_cfg->n_no_reclaim_cmds > MAX_NO_RECLAIM_CMDS)) 196762306a36Sopenharmony_ci trans_pcie->n_no_reclaim_cmds = 0; 196862306a36Sopenharmony_ci else 196962306a36Sopenharmony_ci trans_pcie->n_no_reclaim_cmds = trans_cfg->n_no_reclaim_cmds; 197062306a36Sopenharmony_ci if (trans_pcie->n_no_reclaim_cmds) 197162306a36Sopenharmony_ci memcpy(trans_pcie->no_reclaim_cmds, trans_cfg->no_reclaim_cmds, 197262306a36Sopenharmony_ci trans_pcie->n_no_reclaim_cmds * sizeof(u8)); 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci trans_pcie->rx_buf_size = trans_cfg->rx_buf_size; 197562306a36Sopenharmony_ci trans_pcie->rx_page_order = 197662306a36Sopenharmony_ci iwl_trans_get_rb_size_order(trans_pcie->rx_buf_size); 197762306a36Sopenharmony_ci trans_pcie->rx_buf_bytes = 197862306a36Sopenharmony_ci iwl_trans_get_rb_size(trans_pcie->rx_buf_size); 197962306a36Sopenharmony_ci trans_pcie->supported_dma_mask = DMA_BIT_MASK(12); 198062306a36Sopenharmony_ci if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) 198162306a36Sopenharmony_ci trans_pcie->supported_dma_mask = DMA_BIT_MASK(11); 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_ci trans->txqs.bc_table_dword = trans_cfg->bc_table_dword; 198462306a36Sopenharmony_ci trans_pcie->scd_set_active = trans_cfg->scd_set_active; 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci trans->command_groups = trans_cfg->command_groups; 198762306a36Sopenharmony_ci trans->command_groups_size = trans_cfg->command_groups_size; 198862306a36Sopenharmony_ci 198962306a36Sopenharmony_ci /* Initialize NAPI here - it should be before registering to mac80211 199062306a36Sopenharmony_ci * in the opmode but after the HW struct is allocated. 199162306a36Sopenharmony_ci * As this function may be called again in some corner cases don't 199262306a36Sopenharmony_ci * do anything if NAPI was already initialized. 199362306a36Sopenharmony_ci */ 199462306a36Sopenharmony_ci if (trans_pcie->napi_dev.reg_state != NETREG_DUMMY) 199562306a36Sopenharmony_ci init_dummy_netdev(&trans_pcie->napi_dev); 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_ci trans_pcie->fw_reset_handshake = trans_cfg->fw_reset_handshake; 199862306a36Sopenharmony_ci} 199962306a36Sopenharmony_ci 200062306a36Sopenharmony_civoid iwl_trans_pcie_free_pnvm_dram_regions(struct iwl_dram_regions *dram_regions, 200162306a36Sopenharmony_ci struct device *dev) 200262306a36Sopenharmony_ci{ 200362306a36Sopenharmony_ci u8 i; 200462306a36Sopenharmony_ci struct iwl_dram_data *desc_dram = &dram_regions->prph_scratch_mem_desc; 200562306a36Sopenharmony_ci 200662306a36Sopenharmony_ci /* free DRAM payloads */ 200762306a36Sopenharmony_ci for (i = 0; i < dram_regions->n_regions; i++) { 200862306a36Sopenharmony_ci dma_free_coherent(dev, dram_regions->drams[i].size, 200962306a36Sopenharmony_ci dram_regions->drams[i].block, 201062306a36Sopenharmony_ci dram_regions->drams[i].physical); 201162306a36Sopenharmony_ci } 201262306a36Sopenharmony_ci dram_regions->n_regions = 0; 201362306a36Sopenharmony_ci 201462306a36Sopenharmony_ci /* free DRAM addresses array */ 201562306a36Sopenharmony_ci if (desc_dram->block) { 201662306a36Sopenharmony_ci dma_free_coherent(dev, desc_dram->size, 201762306a36Sopenharmony_ci desc_dram->block, 201862306a36Sopenharmony_ci desc_dram->physical); 201962306a36Sopenharmony_ci } 202062306a36Sopenharmony_ci memset(desc_dram, 0, sizeof(*desc_dram)); 202162306a36Sopenharmony_ci} 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_cistatic void iwl_pcie_free_invalid_tx_cmd(struct iwl_trans *trans) 202462306a36Sopenharmony_ci{ 202562306a36Sopenharmony_ci iwl_pcie_free_dma_ptr(trans, &trans->invalid_tx_cmd); 202662306a36Sopenharmony_ci} 202762306a36Sopenharmony_ci 202862306a36Sopenharmony_cistatic int iwl_pcie_alloc_invalid_tx_cmd(struct iwl_trans *trans) 202962306a36Sopenharmony_ci{ 203062306a36Sopenharmony_ci struct iwl_cmd_header_wide bad_cmd = { 203162306a36Sopenharmony_ci .cmd = INVALID_WR_PTR_CMD, 203262306a36Sopenharmony_ci .group_id = DEBUG_GROUP, 203362306a36Sopenharmony_ci .sequence = cpu_to_le16(0xffff), 203462306a36Sopenharmony_ci .length = cpu_to_le16(0), 203562306a36Sopenharmony_ci .version = 0, 203662306a36Sopenharmony_ci }; 203762306a36Sopenharmony_ci int ret; 203862306a36Sopenharmony_ci 203962306a36Sopenharmony_ci ret = iwl_pcie_alloc_dma_ptr(trans, &trans->invalid_tx_cmd, 204062306a36Sopenharmony_ci sizeof(bad_cmd)); 204162306a36Sopenharmony_ci if (ret) 204262306a36Sopenharmony_ci return ret; 204362306a36Sopenharmony_ci memcpy(trans->invalid_tx_cmd.addr, &bad_cmd, sizeof(bad_cmd)); 204462306a36Sopenharmony_ci return 0; 204562306a36Sopenharmony_ci} 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_civoid iwl_trans_pcie_free(struct iwl_trans *trans) 204862306a36Sopenharmony_ci{ 204962306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 205062306a36Sopenharmony_ci int i; 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_ci iwl_pcie_synchronize_irqs(trans); 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_ci if (trans->trans_cfg->gen2) 205562306a36Sopenharmony_ci iwl_txq_gen2_tx_free(trans); 205662306a36Sopenharmony_ci else 205762306a36Sopenharmony_ci iwl_pcie_tx_free(trans); 205862306a36Sopenharmony_ci iwl_pcie_rx_free(trans); 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci if (trans_pcie->rba.alloc_wq) { 206162306a36Sopenharmony_ci destroy_workqueue(trans_pcie->rba.alloc_wq); 206262306a36Sopenharmony_ci trans_pcie->rba.alloc_wq = NULL; 206362306a36Sopenharmony_ci } 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci if (trans_pcie->msix_enabled) { 206662306a36Sopenharmony_ci for (i = 0; i < trans_pcie->alloc_vecs; i++) { 206762306a36Sopenharmony_ci irq_set_affinity_hint( 206862306a36Sopenharmony_ci trans_pcie->msix_entries[i].vector, 206962306a36Sopenharmony_ci NULL); 207062306a36Sopenharmony_ci } 207162306a36Sopenharmony_ci 207262306a36Sopenharmony_ci trans_pcie->msix_enabled = false; 207362306a36Sopenharmony_ci } else { 207462306a36Sopenharmony_ci iwl_pcie_free_ict(trans); 207562306a36Sopenharmony_ci } 207662306a36Sopenharmony_ci 207762306a36Sopenharmony_ci iwl_pcie_free_invalid_tx_cmd(trans); 207862306a36Sopenharmony_ci 207962306a36Sopenharmony_ci iwl_pcie_free_fw_monitor(trans); 208062306a36Sopenharmony_ci 208162306a36Sopenharmony_ci iwl_trans_pcie_free_pnvm_dram_regions(&trans_pcie->pnvm_data, 208262306a36Sopenharmony_ci trans->dev); 208362306a36Sopenharmony_ci iwl_trans_pcie_free_pnvm_dram_regions(&trans_pcie->reduced_tables_data, 208462306a36Sopenharmony_ci trans->dev); 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci mutex_destroy(&trans_pcie->mutex); 208762306a36Sopenharmony_ci iwl_trans_free(trans); 208862306a36Sopenharmony_ci} 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_cistatic void iwl_trans_pcie_set_pmi(struct iwl_trans *trans, bool state) 209162306a36Sopenharmony_ci{ 209262306a36Sopenharmony_ci if (state) 209362306a36Sopenharmony_ci set_bit(STATUS_TPOWER_PMI, &trans->status); 209462306a36Sopenharmony_ci else 209562306a36Sopenharmony_ci clear_bit(STATUS_TPOWER_PMI, &trans->status); 209662306a36Sopenharmony_ci} 209762306a36Sopenharmony_ci 209862306a36Sopenharmony_cistruct iwl_trans_pcie_removal { 209962306a36Sopenharmony_ci struct pci_dev *pdev; 210062306a36Sopenharmony_ci struct work_struct work; 210162306a36Sopenharmony_ci bool rescan; 210262306a36Sopenharmony_ci}; 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_cistatic void iwl_trans_pcie_removal_wk(struct work_struct *wk) 210562306a36Sopenharmony_ci{ 210662306a36Sopenharmony_ci struct iwl_trans_pcie_removal *removal = 210762306a36Sopenharmony_ci container_of(wk, struct iwl_trans_pcie_removal, work); 210862306a36Sopenharmony_ci struct pci_dev *pdev = removal->pdev; 210962306a36Sopenharmony_ci static char *prop[] = {"EVENT=INACCESSIBLE", NULL}; 211062306a36Sopenharmony_ci struct pci_bus *bus = pdev->bus; 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_ci dev_err(&pdev->dev, "Device gone - attempting removal\n"); 211362306a36Sopenharmony_ci kobject_uevent_env(&pdev->dev.kobj, KOBJ_CHANGE, prop); 211462306a36Sopenharmony_ci pci_lock_rescan_remove(); 211562306a36Sopenharmony_ci pci_dev_put(pdev); 211662306a36Sopenharmony_ci pci_stop_and_remove_bus_device(pdev); 211762306a36Sopenharmony_ci if (removal->rescan) 211862306a36Sopenharmony_ci pci_rescan_bus(bus->parent); 211962306a36Sopenharmony_ci pci_unlock_rescan_remove(); 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_ci kfree(removal); 212262306a36Sopenharmony_ci module_put(THIS_MODULE); 212362306a36Sopenharmony_ci} 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_civoid iwl_trans_pcie_remove(struct iwl_trans *trans, bool rescan) 212662306a36Sopenharmony_ci{ 212762306a36Sopenharmony_ci struct iwl_trans_pcie_removal *removal; 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_ci if (test_bit(STATUS_TRANS_DEAD, &trans->status)) 213062306a36Sopenharmony_ci return; 213162306a36Sopenharmony_ci 213262306a36Sopenharmony_ci IWL_ERR(trans, "Device gone - scheduling removal!\n"); 213362306a36Sopenharmony_ci 213462306a36Sopenharmony_ci /* 213562306a36Sopenharmony_ci * get a module reference to avoid doing this 213662306a36Sopenharmony_ci * while unloading anyway and to avoid 213762306a36Sopenharmony_ci * scheduling a work with code that's being 213862306a36Sopenharmony_ci * removed. 213962306a36Sopenharmony_ci */ 214062306a36Sopenharmony_ci if (!try_module_get(THIS_MODULE)) { 214162306a36Sopenharmony_ci IWL_ERR(trans, 214262306a36Sopenharmony_ci "Module is being unloaded - abort\n"); 214362306a36Sopenharmony_ci return; 214462306a36Sopenharmony_ci } 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci removal = kzalloc(sizeof(*removal), GFP_ATOMIC); 214762306a36Sopenharmony_ci if (!removal) { 214862306a36Sopenharmony_ci module_put(THIS_MODULE); 214962306a36Sopenharmony_ci return; 215062306a36Sopenharmony_ci } 215162306a36Sopenharmony_ci /* 215262306a36Sopenharmony_ci * we don't need to clear this flag, because 215362306a36Sopenharmony_ci * the trans will be freed and reallocated. 215462306a36Sopenharmony_ci */ 215562306a36Sopenharmony_ci set_bit(STATUS_TRANS_DEAD, &trans->status); 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci removal->pdev = to_pci_dev(trans->dev); 215862306a36Sopenharmony_ci removal->rescan = rescan; 215962306a36Sopenharmony_ci INIT_WORK(&removal->work, iwl_trans_pcie_removal_wk); 216062306a36Sopenharmony_ci pci_dev_get(removal->pdev); 216162306a36Sopenharmony_ci schedule_work(&removal->work); 216262306a36Sopenharmony_ci} 216362306a36Sopenharmony_ciEXPORT_SYMBOL(iwl_trans_pcie_remove); 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_ci/* 216662306a36Sopenharmony_ci * This version doesn't disable BHs but rather assumes they're 216762306a36Sopenharmony_ci * already disabled. 216862306a36Sopenharmony_ci */ 216962306a36Sopenharmony_cibool __iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans) 217062306a36Sopenharmony_ci{ 217162306a36Sopenharmony_ci int ret; 217262306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 217362306a36Sopenharmony_ci u32 write = CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ; 217462306a36Sopenharmony_ci u32 mask = CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY | 217562306a36Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP; 217662306a36Sopenharmony_ci u32 poll = CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN; 217762306a36Sopenharmony_ci 217862306a36Sopenharmony_ci spin_lock(&trans_pcie->reg_lock); 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_ci if (trans_pcie->cmd_hold_nic_awake) 218162306a36Sopenharmony_ci goto out; 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_ci if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { 218462306a36Sopenharmony_ci write = CSR_GP_CNTRL_REG_FLAG_BZ_MAC_ACCESS_REQ; 218562306a36Sopenharmony_ci mask = CSR_GP_CNTRL_REG_FLAG_MAC_STATUS; 218662306a36Sopenharmony_ci poll = CSR_GP_CNTRL_REG_FLAG_MAC_STATUS; 218762306a36Sopenharmony_ci } 218862306a36Sopenharmony_ci 218962306a36Sopenharmony_ci /* this bit wakes up the NIC */ 219062306a36Sopenharmony_ci __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL, write); 219162306a36Sopenharmony_ci if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_8000) 219262306a36Sopenharmony_ci udelay(2); 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ci /* 219562306a36Sopenharmony_ci * These bits say the device is running, and should keep running for 219662306a36Sopenharmony_ci * at least a short while (at least as long as MAC_ACCESS_REQ stays 1), 219762306a36Sopenharmony_ci * but they do not indicate that embedded SRAM is restored yet; 219862306a36Sopenharmony_ci * HW with volatile SRAM must save/restore contents to/from 219962306a36Sopenharmony_ci * host DRAM when sleeping/waking for power-saving. 220062306a36Sopenharmony_ci * Each direction takes approximately 1/4 millisecond; with this 220162306a36Sopenharmony_ci * overhead, it's a good idea to grab and hold MAC_ACCESS_REQUEST if a 220262306a36Sopenharmony_ci * series of register accesses are expected (e.g. reading Event Log), 220362306a36Sopenharmony_ci * to keep device from sleeping. 220462306a36Sopenharmony_ci * 220562306a36Sopenharmony_ci * CSR_UCODE_DRV_GP1 register bit MAC_SLEEP == 0 indicates that 220662306a36Sopenharmony_ci * SRAM is okay/restored. We don't check that here because this call 220762306a36Sopenharmony_ci * is just for hardware register access; but GP1 MAC_SLEEP 220862306a36Sopenharmony_ci * check is a good idea before accessing the SRAM of HW with 220962306a36Sopenharmony_ci * volatile SRAM (e.g. reading Event Log). 221062306a36Sopenharmony_ci * 221162306a36Sopenharmony_ci * 5000 series and later (including 1000 series) have non-volatile SRAM, 221262306a36Sopenharmony_ci * and do not save/restore SRAM when power cycling. 221362306a36Sopenharmony_ci */ 221462306a36Sopenharmony_ci ret = iwl_poll_bit(trans, CSR_GP_CNTRL, poll, mask, 15000); 221562306a36Sopenharmony_ci if (unlikely(ret < 0)) { 221662306a36Sopenharmony_ci u32 cntrl = iwl_read32(trans, CSR_GP_CNTRL); 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_ci WARN_ONCE(1, 221962306a36Sopenharmony_ci "Timeout waiting for hardware access (CSR_GP_CNTRL 0x%08x)\n", 222062306a36Sopenharmony_ci cntrl); 222162306a36Sopenharmony_ci 222262306a36Sopenharmony_ci iwl_trans_pcie_dump_regs(trans); 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_ci if (iwlwifi_mod_params.remove_when_gone && cntrl == ~0U) 222562306a36Sopenharmony_ci iwl_trans_pcie_remove(trans, false); 222662306a36Sopenharmony_ci else 222762306a36Sopenharmony_ci iwl_write32(trans, CSR_RESET, 222862306a36Sopenharmony_ci CSR_RESET_REG_FLAG_FORCE_NMI); 222962306a36Sopenharmony_ci 223062306a36Sopenharmony_ci spin_unlock(&trans_pcie->reg_lock); 223162306a36Sopenharmony_ci return false; 223262306a36Sopenharmony_ci } 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_ciout: 223562306a36Sopenharmony_ci /* 223662306a36Sopenharmony_ci * Fool sparse by faking we release the lock - sparse will 223762306a36Sopenharmony_ci * track nic_access anyway. 223862306a36Sopenharmony_ci */ 223962306a36Sopenharmony_ci __release(&trans_pcie->reg_lock); 224062306a36Sopenharmony_ci return true; 224162306a36Sopenharmony_ci} 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_cistatic bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans) 224462306a36Sopenharmony_ci{ 224562306a36Sopenharmony_ci bool ret; 224662306a36Sopenharmony_ci 224762306a36Sopenharmony_ci local_bh_disable(); 224862306a36Sopenharmony_ci ret = __iwl_trans_pcie_grab_nic_access(trans); 224962306a36Sopenharmony_ci if (ret) { 225062306a36Sopenharmony_ci /* keep BHs disabled until iwl_trans_pcie_release_nic_access */ 225162306a36Sopenharmony_ci return ret; 225262306a36Sopenharmony_ci } 225362306a36Sopenharmony_ci local_bh_enable(); 225462306a36Sopenharmony_ci return false; 225562306a36Sopenharmony_ci} 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_cistatic void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans) 225862306a36Sopenharmony_ci{ 225962306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 226062306a36Sopenharmony_ci 226162306a36Sopenharmony_ci lockdep_assert_held(&trans_pcie->reg_lock); 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_ci /* 226462306a36Sopenharmony_ci * Fool sparse by faking we acquiring the lock - sparse will 226562306a36Sopenharmony_ci * track nic_access anyway. 226662306a36Sopenharmony_ci */ 226762306a36Sopenharmony_ci __acquire(&trans_pcie->reg_lock); 226862306a36Sopenharmony_ci 226962306a36Sopenharmony_ci if (trans_pcie->cmd_hold_nic_awake) 227062306a36Sopenharmony_ci goto out; 227162306a36Sopenharmony_ci if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) 227262306a36Sopenharmony_ci __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, 227362306a36Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_BZ_MAC_ACCESS_REQ); 227462306a36Sopenharmony_ci else 227562306a36Sopenharmony_ci __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, 227662306a36Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 227762306a36Sopenharmony_ci /* 227862306a36Sopenharmony_ci * Above we read the CSR_GP_CNTRL register, which will flush 227962306a36Sopenharmony_ci * any previous writes, but we need the write that clears the 228062306a36Sopenharmony_ci * MAC_ACCESS_REQ bit to be performed before any other writes 228162306a36Sopenharmony_ci * scheduled on different CPUs (after we drop reg_lock). 228262306a36Sopenharmony_ci */ 228362306a36Sopenharmony_ciout: 228462306a36Sopenharmony_ci spin_unlock_bh(&trans_pcie->reg_lock); 228562306a36Sopenharmony_ci} 228662306a36Sopenharmony_ci 228762306a36Sopenharmony_cistatic int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr, 228862306a36Sopenharmony_ci void *buf, int dwords) 228962306a36Sopenharmony_ci{ 229062306a36Sopenharmony_ci int offs = 0; 229162306a36Sopenharmony_ci u32 *vals = buf; 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_ci while (offs < dwords) { 229462306a36Sopenharmony_ci /* limit the time we spin here under lock to 1/2s */ 229562306a36Sopenharmony_ci unsigned long end = jiffies + HZ / 2; 229662306a36Sopenharmony_ci bool resched = false; 229762306a36Sopenharmony_ci 229862306a36Sopenharmony_ci if (iwl_trans_grab_nic_access(trans)) { 229962306a36Sopenharmony_ci iwl_write32(trans, HBUS_TARG_MEM_RADDR, 230062306a36Sopenharmony_ci addr + 4 * offs); 230162306a36Sopenharmony_ci 230262306a36Sopenharmony_ci while (offs < dwords) { 230362306a36Sopenharmony_ci vals[offs] = iwl_read32(trans, 230462306a36Sopenharmony_ci HBUS_TARG_MEM_RDAT); 230562306a36Sopenharmony_ci offs++; 230662306a36Sopenharmony_ci 230762306a36Sopenharmony_ci if (time_after(jiffies, end)) { 230862306a36Sopenharmony_ci resched = true; 230962306a36Sopenharmony_ci break; 231062306a36Sopenharmony_ci } 231162306a36Sopenharmony_ci } 231262306a36Sopenharmony_ci iwl_trans_release_nic_access(trans); 231362306a36Sopenharmony_ci 231462306a36Sopenharmony_ci if (resched) 231562306a36Sopenharmony_ci cond_resched(); 231662306a36Sopenharmony_ci } else { 231762306a36Sopenharmony_ci return -EBUSY; 231862306a36Sopenharmony_ci } 231962306a36Sopenharmony_ci } 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_ci return 0; 232262306a36Sopenharmony_ci} 232362306a36Sopenharmony_ci 232462306a36Sopenharmony_cistatic int iwl_trans_pcie_write_mem(struct iwl_trans *trans, u32 addr, 232562306a36Sopenharmony_ci const void *buf, int dwords) 232662306a36Sopenharmony_ci{ 232762306a36Sopenharmony_ci int offs, ret = 0; 232862306a36Sopenharmony_ci const u32 *vals = buf; 232962306a36Sopenharmony_ci 233062306a36Sopenharmony_ci if (iwl_trans_grab_nic_access(trans)) { 233162306a36Sopenharmony_ci iwl_write32(trans, HBUS_TARG_MEM_WADDR, addr); 233262306a36Sopenharmony_ci for (offs = 0; offs < dwords; offs++) 233362306a36Sopenharmony_ci iwl_write32(trans, HBUS_TARG_MEM_WDAT, 233462306a36Sopenharmony_ci vals ? vals[offs] : 0); 233562306a36Sopenharmony_ci iwl_trans_release_nic_access(trans); 233662306a36Sopenharmony_ci } else { 233762306a36Sopenharmony_ci ret = -EBUSY; 233862306a36Sopenharmony_ci } 233962306a36Sopenharmony_ci return ret; 234062306a36Sopenharmony_ci} 234162306a36Sopenharmony_ci 234262306a36Sopenharmony_cistatic int iwl_trans_pcie_read_config32(struct iwl_trans *trans, u32 ofs, 234362306a36Sopenharmony_ci u32 *val) 234462306a36Sopenharmony_ci{ 234562306a36Sopenharmony_ci return pci_read_config_dword(IWL_TRANS_GET_PCIE_TRANS(trans)->pci_dev, 234662306a36Sopenharmony_ci ofs, val); 234762306a36Sopenharmony_ci} 234862306a36Sopenharmony_ci 234962306a36Sopenharmony_cistatic void iwl_trans_pcie_block_txq_ptrs(struct iwl_trans *trans, bool block) 235062306a36Sopenharmony_ci{ 235162306a36Sopenharmony_ci int i; 235262306a36Sopenharmony_ci 235362306a36Sopenharmony_ci for (i = 0; i < trans->trans_cfg->base_params->num_of_queues; i++) { 235462306a36Sopenharmony_ci struct iwl_txq *txq = trans->txqs.txq[i]; 235562306a36Sopenharmony_ci 235662306a36Sopenharmony_ci if (i == trans->txqs.cmd.q_id) 235762306a36Sopenharmony_ci continue; 235862306a36Sopenharmony_ci 235962306a36Sopenharmony_ci spin_lock_bh(&txq->lock); 236062306a36Sopenharmony_ci 236162306a36Sopenharmony_ci if (!block && !(WARN_ON_ONCE(!txq->block))) { 236262306a36Sopenharmony_ci txq->block--; 236362306a36Sopenharmony_ci if (!txq->block) { 236462306a36Sopenharmony_ci iwl_write32(trans, HBUS_TARG_WRPTR, 236562306a36Sopenharmony_ci txq->write_ptr | (i << 8)); 236662306a36Sopenharmony_ci } 236762306a36Sopenharmony_ci } else if (block) { 236862306a36Sopenharmony_ci txq->block++; 236962306a36Sopenharmony_ci } 237062306a36Sopenharmony_ci 237162306a36Sopenharmony_ci spin_unlock_bh(&txq->lock); 237262306a36Sopenharmony_ci } 237362306a36Sopenharmony_ci} 237462306a36Sopenharmony_ci 237562306a36Sopenharmony_ci#define IWL_FLUSH_WAIT_MS 2000 237662306a36Sopenharmony_ci 237762306a36Sopenharmony_cistatic int iwl_trans_pcie_rxq_dma_data(struct iwl_trans *trans, int queue, 237862306a36Sopenharmony_ci struct iwl_trans_rxq_dma_data *data) 237962306a36Sopenharmony_ci{ 238062306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 238162306a36Sopenharmony_ci 238262306a36Sopenharmony_ci if (queue >= trans->num_rx_queues || !trans_pcie->rxq) 238362306a36Sopenharmony_ci return -EINVAL; 238462306a36Sopenharmony_ci 238562306a36Sopenharmony_ci data->fr_bd_cb = trans_pcie->rxq[queue].bd_dma; 238662306a36Sopenharmony_ci data->urbd_stts_wrptr = trans_pcie->rxq[queue].rb_stts_dma; 238762306a36Sopenharmony_ci data->ur_bd_cb = trans_pcie->rxq[queue].used_bd_dma; 238862306a36Sopenharmony_ci data->fr_bd_wid = 0; 238962306a36Sopenharmony_ci 239062306a36Sopenharmony_ci return 0; 239162306a36Sopenharmony_ci} 239262306a36Sopenharmony_ci 239362306a36Sopenharmony_cistatic int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, int txq_idx) 239462306a36Sopenharmony_ci{ 239562306a36Sopenharmony_ci struct iwl_txq *txq; 239662306a36Sopenharmony_ci unsigned long now = jiffies; 239762306a36Sopenharmony_ci bool overflow_tx; 239862306a36Sopenharmony_ci u8 wr_ptr; 239962306a36Sopenharmony_ci 240062306a36Sopenharmony_ci /* Make sure the NIC is still alive in the bus */ 240162306a36Sopenharmony_ci if (test_bit(STATUS_TRANS_DEAD, &trans->status)) 240262306a36Sopenharmony_ci return -ENODEV; 240362306a36Sopenharmony_ci 240462306a36Sopenharmony_ci if (!test_bit(txq_idx, trans->txqs.queue_used)) 240562306a36Sopenharmony_ci return -EINVAL; 240662306a36Sopenharmony_ci 240762306a36Sopenharmony_ci IWL_DEBUG_TX_QUEUES(trans, "Emptying queue %d...\n", txq_idx); 240862306a36Sopenharmony_ci txq = trans->txqs.txq[txq_idx]; 240962306a36Sopenharmony_ci 241062306a36Sopenharmony_ci spin_lock_bh(&txq->lock); 241162306a36Sopenharmony_ci overflow_tx = txq->overflow_tx || 241262306a36Sopenharmony_ci !skb_queue_empty(&txq->overflow_q); 241362306a36Sopenharmony_ci spin_unlock_bh(&txq->lock); 241462306a36Sopenharmony_ci 241562306a36Sopenharmony_ci wr_ptr = READ_ONCE(txq->write_ptr); 241662306a36Sopenharmony_ci 241762306a36Sopenharmony_ci while ((txq->read_ptr != READ_ONCE(txq->write_ptr) || 241862306a36Sopenharmony_ci overflow_tx) && 241962306a36Sopenharmony_ci !time_after(jiffies, 242062306a36Sopenharmony_ci now + msecs_to_jiffies(IWL_FLUSH_WAIT_MS))) { 242162306a36Sopenharmony_ci u8 write_ptr = READ_ONCE(txq->write_ptr); 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_ci /* 242462306a36Sopenharmony_ci * If write pointer moved during the wait, warn only 242562306a36Sopenharmony_ci * if the TX came from op mode. In case TX came from 242662306a36Sopenharmony_ci * trans layer (overflow TX) don't warn. 242762306a36Sopenharmony_ci */ 242862306a36Sopenharmony_ci if (WARN_ONCE(wr_ptr != write_ptr && !overflow_tx, 242962306a36Sopenharmony_ci "WR pointer moved while flushing %d -> %d\n", 243062306a36Sopenharmony_ci wr_ptr, write_ptr)) 243162306a36Sopenharmony_ci return -ETIMEDOUT; 243262306a36Sopenharmony_ci wr_ptr = write_ptr; 243362306a36Sopenharmony_ci 243462306a36Sopenharmony_ci usleep_range(1000, 2000); 243562306a36Sopenharmony_ci 243662306a36Sopenharmony_ci spin_lock_bh(&txq->lock); 243762306a36Sopenharmony_ci overflow_tx = txq->overflow_tx || 243862306a36Sopenharmony_ci !skb_queue_empty(&txq->overflow_q); 243962306a36Sopenharmony_ci spin_unlock_bh(&txq->lock); 244062306a36Sopenharmony_ci } 244162306a36Sopenharmony_ci 244262306a36Sopenharmony_ci if (txq->read_ptr != txq->write_ptr) { 244362306a36Sopenharmony_ci IWL_ERR(trans, 244462306a36Sopenharmony_ci "fail to flush all tx fifo queues Q %d\n", txq_idx); 244562306a36Sopenharmony_ci iwl_txq_log_scd_error(trans, txq); 244662306a36Sopenharmony_ci return -ETIMEDOUT; 244762306a36Sopenharmony_ci } 244862306a36Sopenharmony_ci 244962306a36Sopenharmony_ci IWL_DEBUG_TX_QUEUES(trans, "Queue %d is now empty.\n", txq_idx); 245062306a36Sopenharmony_ci 245162306a36Sopenharmony_ci return 0; 245262306a36Sopenharmony_ci} 245362306a36Sopenharmony_ci 245462306a36Sopenharmony_cistatic int iwl_trans_pcie_wait_txqs_empty(struct iwl_trans *trans, u32 txq_bm) 245562306a36Sopenharmony_ci{ 245662306a36Sopenharmony_ci int cnt; 245762306a36Sopenharmony_ci int ret = 0; 245862306a36Sopenharmony_ci 245962306a36Sopenharmony_ci /* waiting for all the tx frames complete might take a while */ 246062306a36Sopenharmony_ci for (cnt = 0; 246162306a36Sopenharmony_ci cnt < trans->trans_cfg->base_params->num_of_queues; 246262306a36Sopenharmony_ci cnt++) { 246362306a36Sopenharmony_ci 246462306a36Sopenharmony_ci if (cnt == trans->txqs.cmd.q_id) 246562306a36Sopenharmony_ci continue; 246662306a36Sopenharmony_ci if (!test_bit(cnt, trans->txqs.queue_used)) 246762306a36Sopenharmony_ci continue; 246862306a36Sopenharmony_ci if (!(BIT(cnt) & txq_bm)) 246962306a36Sopenharmony_ci continue; 247062306a36Sopenharmony_ci 247162306a36Sopenharmony_ci ret = iwl_trans_pcie_wait_txq_empty(trans, cnt); 247262306a36Sopenharmony_ci if (ret) 247362306a36Sopenharmony_ci break; 247462306a36Sopenharmony_ci } 247562306a36Sopenharmony_ci 247662306a36Sopenharmony_ci return ret; 247762306a36Sopenharmony_ci} 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_cistatic void iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, u32 reg, 248062306a36Sopenharmony_ci u32 mask, u32 value) 248162306a36Sopenharmony_ci{ 248262306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 248362306a36Sopenharmony_ci 248462306a36Sopenharmony_ci spin_lock_bh(&trans_pcie->reg_lock); 248562306a36Sopenharmony_ci __iwl_trans_pcie_set_bits_mask(trans, reg, mask, value); 248662306a36Sopenharmony_ci spin_unlock_bh(&trans_pcie->reg_lock); 248762306a36Sopenharmony_ci} 248862306a36Sopenharmony_ci 248962306a36Sopenharmony_cistatic const char *get_csr_string(int cmd) 249062306a36Sopenharmony_ci{ 249162306a36Sopenharmony_ci#define IWL_CMD(x) case x: return #x 249262306a36Sopenharmony_ci switch (cmd) { 249362306a36Sopenharmony_ci IWL_CMD(CSR_HW_IF_CONFIG_REG); 249462306a36Sopenharmony_ci IWL_CMD(CSR_INT_COALESCING); 249562306a36Sopenharmony_ci IWL_CMD(CSR_INT); 249662306a36Sopenharmony_ci IWL_CMD(CSR_INT_MASK); 249762306a36Sopenharmony_ci IWL_CMD(CSR_FH_INT_STATUS); 249862306a36Sopenharmony_ci IWL_CMD(CSR_GPIO_IN); 249962306a36Sopenharmony_ci IWL_CMD(CSR_RESET); 250062306a36Sopenharmony_ci IWL_CMD(CSR_GP_CNTRL); 250162306a36Sopenharmony_ci IWL_CMD(CSR_HW_REV); 250262306a36Sopenharmony_ci IWL_CMD(CSR_EEPROM_REG); 250362306a36Sopenharmony_ci IWL_CMD(CSR_EEPROM_GP); 250462306a36Sopenharmony_ci IWL_CMD(CSR_OTP_GP_REG); 250562306a36Sopenharmony_ci IWL_CMD(CSR_GIO_REG); 250662306a36Sopenharmony_ci IWL_CMD(CSR_GP_UCODE_REG); 250762306a36Sopenharmony_ci IWL_CMD(CSR_GP_DRIVER_REG); 250862306a36Sopenharmony_ci IWL_CMD(CSR_UCODE_DRV_GP1); 250962306a36Sopenharmony_ci IWL_CMD(CSR_UCODE_DRV_GP2); 251062306a36Sopenharmony_ci IWL_CMD(CSR_LED_REG); 251162306a36Sopenharmony_ci IWL_CMD(CSR_DRAM_INT_TBL_REG); 251262306a36Sopenharmony_ci IWL_CMD(CSR_GIO_CHICKEN_BITS); 251362306a36Sopenharmony_ci IWL_CMD(CSR_ANA_PLL_CFG); 251462306a36Sopenharmony_ci IWL_CMD(CSR_HW_REV_WA_REG); 251562306a36Sopenharmony_ci IWL_CMD(CSR_MONITOR_STATUS_REG); 251662306a36Sopenharmony_ci IWL_CMD(CSR_DBG_HPET_MEM_REG); 251762306a36Sopenharmony_ci default: 251862306a36Sopenharmony_ci return "UNKNOWN"; 251962306a36Sopenharmony_ci } 252062306a36Sopenharmony_ci#undef IWL_CMD 252162306a36Sopenharmony_ci} 252262306a36Sopenharmony_ci 252362306a36Sopenharmony_civoid iwl_pcie_dump_csr(struct iwl_trans *trans) 252462306a36Sopenharmony_ci{ 252562306a36Sopenharmony_ci int i; 252662306a36Sopenharmony_ci static const u32 csr_tbl[] = { 252762306a36Sopenharmony_ci CSR_HW_IF_CONFIG_REG, 252862306a36Sopenharmony_ci CSR_INT_COALESCING, 252962306a36Sopenharmony_ci CSR_INT, 253062306a36Sopenharmony_ci CSR_INT_MASK, 253162306a36Sopenharmony_ci CSR_FH_INT_STATUS, 253262306a36Sopenharmony_ci CSR_GPIO_IN, 253362306a36Sopenharmony_ci CSR_RESET, 253462306a36Sopenharmony_ci CSR_GP_CNTRL, 253562306a36Sopenharmony_ci CSR_HW_REV, 253662306a36Sopenharmony_ci CSR_EEPROM_REG, 253762306a36Sopenharmony_ci CSR_EEPROM_GP, 253862306a36Sopenharmony_ci CSR_OTP_GP_REG, 253962306a36Sopenharmony_ci CSR_GIO_REG, 254062306a36Sopenharmony_ci CSR_GP_UCODE_REG, 254162306a36Sopenharmony_ci CSR_GP_DRIVER_REG, 254262306a36Sopenharmony_ci CSR_UCODE_DRV_GP1, 254362306a36Sopenharmony_ci CSR_UCODE_DRV_GP2, 254462306a36Sopenharmony_ci CSR_LED_REG, 254562306a36Sopenharmony_ci CSR_DRAM_INT_TBL_REG, 254662306a36Sopenharmony_ci CSR_GIO_CHICKEN_BITS, 254762306a36Sopenharmony_ci CSR_ANA_PLL_CFG, 254862306a36Sopenharmony_ci CSR_MONITOR_STATUS_REG, 254962306a36Sopenharmony_ci CSR_HW_REV_WA_REG, 255062306a36Sopenharmony_ci CSR_DBG_HPET_MEM_REG 255162306a36Sopenharmony_ci }; 255262306a36Sopenharmony_ci IWL_ERR(trans, "CSR values:\n"); 255362306a36Sopenharmony_ci IWL_ERR(trans, "(2nd byte of CSR_INT_COALESCING is " 255462306a36Sopenharmony_ci "CSR_INT_PERIODIC_REG)\n"); 255562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(csr_tbl); i++) { 255662306a36Sopenharmony_ci IWL_ERR(trans, " %25s: 0X%08x\n", 255762306a36Sopenharmony_ci get_csr_string(csr_tbl[i]), 255862306a36Sopenharmony_ci iwl_read32(trans, csr_tbl[i])); 255962306a36Sopenharmony_ci } 256062306a36Sopenharmony_ci} 256162306a36Sopenharmony_ci 256262306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS 256362306a36Sopenharmony_ci/* create and remove of files */ 256462306a36Sopenharmony_ci#define DEBUGFS_ADD_FILE(name, parent, mode) do { \ 256562306a36Sopenharmony_ci debugfs_create_file(#name, mode, parent, trans, \ 256662306a36Sopenharmony_ci &iwl_dbgfs_##name##_ops); \ 256762306a36Sopenharmony_ci} while (0) 256862306a36Sopenharmony_ci 256962306a36Sopenharmony_ci/* file operation */ 257062306a36Sopenharmony_ci#define DEBUGFS_READ_FILE_OPS(name) \ 257162306a36Sopenharmony_cistatic const struct file_operations iwl_dbgfs_##name##_ops = { \ 257262306a36Sopenharmony_ci .read = iwl_dbgfs_##name##_read, \ 257362306a36Sopenharmony_ci .open = simple_open, \ 257462306a36Sopenharmony_ci .llseek = generic_file_llseek, \ 257562306a36Sopenharmony_ci}; 257662306a36Sopenharmony_ci 257762306a36Sopenharmony_ci#define DEBUGFS_WRITE_FILE_OPS(name) \ 257862306a36Sopenharmony_cistatic const struct file_operations iwl_dbgfs_##name##_ops = { \ 257962306a36Sopenharmony_ci .write = iwl_dbgfs_##name##_write, \ 258062306a36Sopenharmony_ci .open = simple_open, \ 258162306a36Sopenharmony_ci .llseek = generic_file_llseek, \ 258262306a36Sopenharmony_ci}; 258362306a36Sopenharmony_ci 258462306a36Sopenharmony_ci#define DEBUGFS_READ_WRITE_FILE_OPS(name) \ 258562306a36Sopenharmony_cistatic const struct file_operations iwl_dbgfs_##name##_ops = { \ 258662306a36Sopenharmony_ci .write = iwl_dbgfs_##name##_write, \ 258762306a36Sopenharmony_ci .read = iwl_dbgfs_##name##_read, \ 258862306a36Sopenharmony_ci .open = simple_open, \ 258962306a36Sopenharmony_ci .llseek = generic_file_llseek, \ 259062306a36Sopenharmony_ci}; 259162306a36Sopenharmony_ci 259262306a36Sopenharmony_cistruct iwl_dbgfs_tx_queue_priv { 259362306a36Sopenharmony_ci struct iwl_trans *trans; 259462306a36Sopenharmony_ci}; 259562306a36Sopenharmony_ci 259662306a36Sopenharmony_cistruct iwl_dbgfs_tx_queue_state { 259762306a36Sopenharmony_ci loff_t pos; 259862306a36Sopenharmony_ci}; 259962306a36Sopenharmony_ci 260062306a36Sopenharmony_cistatic void *iwl_dbgfs_tx_queue_seq_start(struct seq_file *seq, loff_t *pos) 260162306a36Sopenharmony_ci{ 260262306a36Sopenharmony_ci struct iwl_dbgfs_tx_queue_priv *priv = seq->private; 260362306a36Sopenharmony_ci struct iwl_dbgfs_tx_queue_state *state; 260462306a36Sopenharmony_ci 260562306a36Sopenharmony_ci if (*pos >= priv->trans->trans_cfg->base_params->num_of_queues) 260662306a36Sopenharmony_ci return NULL; 260762306a36Sopenharmony_ci 260862306a36Sopenharmony_ci state = kmalloc(sizeof(*state), GFP_KERNEL); 260962306a36Sopenharmony_ci if (!state) 261062306a36Sopenharmony_ci return NULL; 261162306a36Sopenharmony_ci state->pos = *pos; 261262306a36Sopenharmony_ci return state; 261362306a36Sopenharmony_ci} 261462306a36Sopenharmony_ci 261562306a36Sopenharmony_cistatic void *iwl_dbgfs_tx_queue_seq_next(struct seq_file *seq, 261662306a36Sopenharmony_ci void *v, loff_t *pos) 261762306a36Sopenharmony_ci{ 261862306a36Sopenharmony_ci struct iwl_dbgfs_tx_queue_priv *priv = seq->private; 261962306a36Sopenharmony_ci struct iwl_dbgfs_tx_queue_state *state = v; 262062306a36Sopenharmony_ci 262162306a36Sopenharmony_ci *pos = ++state->pos; 262262306a36Sopenharmony_ci 262362306a36Sopenharmony_ci if (*pos >= priv->trans->trans_cfg->base_params->num_of_queues) 262462306a36Sopenharmony_ci return NULL; 262562306a36Sopenharmony_ci 262662306a36Sopenharmony_ci return state; 262762306a36Sopenharmony_ci} 262862306a36Sopenharmony_ci 262962306a36Sopenharmony_cistatic void iwl_dbgfs_tx_queue_seq_stop(struct seq_file *seq, void *v) 263062306a36Sopenharmony_ci{ 263162306a36Sopenharmony_ci kfree(v); 263262306a36Sopenharmony_ci} 263362306a36Sopenharmony_ci 263462306a36Sopenharmony_cistatic int iwl_dbgfs_tx_queue_seq_show(struct seq_file *seq, void *v) 263562306a36Sopenharmony_ci{ 263662306a36Sopenharmony_ci struct iwl_dbgfs_tx_queue_priv *priv = seq->private; 263762306a36Sopenharmony_ci struct iwl_dbgfs_tx_queue_state *state = v; 263862306a36Sopenharmony_ci struct iwl_trans *trans = priv->trans; 263962306a36Sopenharmony_ci struct iwl_txq *txq = trans->txqs.txq[state->pos]; 264062306a36Sopenharmony_ci 264162306a36Sopenharmony_ci seq_printf(seq, "hwq %.3u: used=%d stopped=%d ", 264262306a36Sopenharmony_ci (unsigned int)state->pos, 264362306a36Sopenharmony_ci !!test_bit(state->pos, trans->txqs.queue_used), 264462306a36Sopenharmony_ci !!test_bit(state->pos, trans->txqs.queue_stopped)); 264562306a36Sopenharmony_ci if (txq) 264662306a36Sopenharmony_ci seq_printf(seq, 264762306a36Sopenharmony_ci "read=%u write=%u need_update=%d frozen=%d n_window=%d ampdu=%d", 264862306a36Sopenharmony_ci txq->read_ptr, txq->write_ptr, 264962306a36Sopenharmony_ci txq->need_update, txq->frozen, 265062306a36Sopenharmony_ci txq->n_window, txq->ampdu); 265162306a36Sopenharmony_ci else 265262306a36Sopenharmony_ci seq_puts(seq, "(unallocated)"); 265362306a36Sopenharmony_ci 265462306a36Sopenharmony_ci if (state->pos == trans->txqs.cmd.q_id) 265562306a36Sopenharmony_ci seq_puts(seq, " (HCMD)"); 265662306a36Sopenharmony_ci seq_puts(seq, "\n"); 265762306a36Sopenharmony_ci 265862306a36Sopenharmony_ci return 0; 265962306a36Sopenharmony_ci} 266062306a36Sopenharmony_ci 266162306a36Sopenharmony_cistatic const struct seq_operations iwl_dbgfs_tx_queue_seq_ops = { 266262306a36Sopenharmony_ci .start = iwl_dbgfs_tx_queue_seq_start, 266362306a36Sopenharmony_ci .next = iwl_dbgfs_tx_queue_seq_next, 266462306a36Sopenharmony_ci .stop = iwl_dbgfs_tx_queue_seq_stop, 266562306a36Sopenharmony_ci .show = iwl_dbgfs_tx_queue_seq_show, 266662306a36Sopenharmony_ci}; 266762306a36Sopenharmony_ci 266862306a36Sopenharmony_cistatic int iwl_dbgfs_tx_queue_open(struct inode *inode, struct file *filp) 266962306a36Sopenharmony_ci{ 267062306a36Sopenharmony_ci struct iwl_dbgfs_tx_queue_priv *priv; 267162306a36Sopenharmony_ci 267262306a36Sopenharmony_ci priv = __seq_open_private(filp, &iwl_dbgfs_tx_queue_seq_ops, 267362306a36Sopenharmony_ci sizeof(*priv)); 267462306a36Sopenharmony_ci 267562306a36Sopenharmony_ci if (!priv) 267662306a36Sopenharmony_ci return -ENOMEM; 267762306a36Sopenharmony_ci 267862306a36Sopenharmony_ci priv->trans = inode->i_private; 267962306a36Sopenharmony_ci return 0; 268062306a36Sopenharmony_ci} 268162306a36Sopenharmony_ci 268262306a36Sopenharmony_cistatic ssize_t iwl_dbgfs_rx_queue_read(struct file *file, 268362306a36Sopenharmony_ci char __user *user_buf, 268462306a36Sopenharmony_ci size_t count, loff_t *ppos) 268562306a36Sopenharmony_ci{ 268662306a36Sopenharmony_ci struct iwl_trans *trans = file->private_data; 268762306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 268862306a36Sopenharmony_ci char *buf; 268962306a36Sopenharmony_ci int pos = 0, i, ret; 269062306a36Sopenharmony_ci size_t bufsz; 269162306a36Sopenharmony_ci 269262306a36Sopenharmony_ci bufsz = sizeof(char) * 121 * trans->num_rx_queues; 269362306a36Sopenharmony_ci 269462306a36Sopenharmony_ci if (!trans_pcie->rxq) 269562306a36Sopenharmony_ci return -EAGAIN; 269662306a36Sopenharmony_ci 269762306a36Sopenharmony_ci buf = kzalloc(bufsz, GFP_KERNEL); 269862306a36Sopenharmony_ci if (!buf) 269962306a36Sopenharmony_ci return -ENOMEM; 270062306a36Sopenharmony_ci 270162306a36Sopenharmony_ci for (i = 0; i < trans->num_rx_queues && pos < bufsz; i++) { 270262306a36Sopenharmony_ci struct iwl_rxq *rxq = &trans_pcie->rxq[i]; 270362306a36Sopenharmony_ci 270462306a36Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, "queue#: %2d\n", 270562306a36Sopenharmony_ci i); 270662306a36Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, "\tread: %u\n", 270762306a36Sopenharmony_ci rxq->read); 270862306a36Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, "\twrite: %u\n", 270962306a36Sopenharmony_ci rxq->write); 271062306a36Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, "\twrite_actual: %u\n", 271162306a36Sopenharmony_ci rxq->write_actual); 271262306a36Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, "\tneed_update: %2d\n", 271362306a36Sopenharmony_ci rxq->need_update); 271462306a36Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, "\tfree_count: %u\n", 271562306a36Sopenharmony_ci rxq->free_count); 271662306a36Sopenharmony_ci if (rxq->rb_stts) { 271762306a36Sopenharmony_ci u32 r = __le16_to_cpu(iwl_get_closed_rb_stts(trans, 271862306a36Sopenharmony_ci rxq)); 271962306a36Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, 272062306a36Sopenharmony_ci "\tclosed_rb_num: %u\n", 272162306a36Sopenharmony_ci r & 0x0FFF); 272262306a36Sopenharmony_ci } else { 272362306a36Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, 272462306a36Sopenharmony_ci "\tclosed_rb_num: Not Allocated\n"); 272562306a36Sopenharmony_ci } 272662306a36Sopenharmony_ci } 272762306a36Sopenharmony_ci ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); 272862306a36Sopenharmony_ci kfree(buf); 272962306a36Sopenharmony_ci 273062306a36Sopenharmony_ci return ret; 273162306a36Sopenharmony_ci} 273262306a36Sopenharmony_ci 273362306a36Sopenharmony_cistatic ssize_t iwl_dbgfs_interrupt_read(struct file *file, 273462306a36Sopenharmony_ci char __user *user_buf, 273562306a36Sopenharmony_ci size_t count, loff_t *ppos) 273662306a36Sopenharmony_ci{ 273762306a36Sopenharmony_ci struct iwl_trans *trans = file->private_data; 273862306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 273962306a36Sopenharmony_ci struct isr_statistics *isr_stats = &trans_pcie->isr_stats; 274062306a36Sopenharmony_ci 274162306a36Sopenharmony_ci int pos = 0; 274262306a36Sopenharmony_ci char *buf; 274362306a36Sopenharmony_ci int bufsz = 24 * 64; /* 24 items * 64 char per item */ 274462306a36Sopenharmony_ci ssize_t ret; 274562306a36Sopenharmony_ci 274662306a36Sopenharmony_ci buf = kzalloc(bufsz, GFP_KERNEL); 274762306a36Sopenharmony_ci if (!buf) 274862306a36Sopenharmony_ci return -ENOMEM; 274962306a36Sopenharmony_ci 275062306a36Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, 275162306a36Sopenharmony_ci "Interrupt Statistics Report:\n"); 275262306a36Sopenharmony_ci 275362306a36Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, "HW Error:\t\t\t %u\n", 275462306a36Sopenharmony_ci isr_stats->hw); 275562306a36Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, "SW Error:\t\t\t %u\n", 275662306a36Sopenharmony_ci isr_stats->sw); 275762306a36Sopenharmony_ci if (isr_stats->sw || isr_stats->hw) { 275862306a36Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, 275962306a36Sopenharmony_ci "\tLast Restarting Code: 0x%X\n", 276062306a36Sopenharmony_ci isr_stats->err_code); 276162306a36Sopenharmony_ci } 276262306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUG 276362306a36Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, "Frame transmitted:\t\t %u\n", 276462306a36Sopenharmony_ci isr_stats->sch); 276562306a36Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, "Alive interrupt:\t\t %u\n", 276662306a36Sopenharmony_ci isr_stats->alive); 276762306a36Sopenharmony_ci#endif 276862306a36Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, 276962306a36Sopenharmony_ci "HW RF KILL switch toggled:\t %u\n", isr_stats->rfkill); 277062306a36Sopenharmony_ci 277162306a36Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, "CT KILL:\t\t\t %u\n", 277262306a36Sopenharmony_ci isr_stats->ctkill); 277362306a36Sopenharmony_ci 277462306a36Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, "Wakeup Interrupt:\t\t %u\n", 277562306a36Sopenharmony_ci isr_stats->wakeup); 277662306a36Sopenharmony_ci 277762306a36Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, 277862306a36Sopenharmony_ci "Rx command responses:\t\t %u\n", isr_stats->rx); 277962306a36Sopenharmony_ci 278062306a36Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, "Tx/FH interrupt:\t\t %u\n", 278162306a36Sopenharmony_ci isr_stats->tx); 278262306a36Sopenharmony_ci 278362306a36Sopenharmony_ci pos += scnprintf(buf + pos, bufsz - pos, "Unexpected INTA:\t\t %u\n", 278462306a36Sopenharmony_ci isr_stats->unhandled); 278562306a36Sopenharmony_ci 278662306a36Sopenharmony_ci ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); 278762306a36Sopenharmony_ci kfree(buf); 278862306a36Sopenharmony_ci return ret; 278962306a36Sopenharmony_ci} 279062306a36Sopenharmony_ci 279162306a36Sopenharmony_cistatic ssize_t iwl_dbgfs_interrupt_write(struct file *file, 279262306a36Sopenharmony_ci const char __user *user_buf, 279362306a36Sopenharmony_ci size_t count, loff_t *ppos) 279462306a36Sopenharmony_ci{ 279562306a36Sopenharmony_ci struct iwl_trans *trans = file->private_data; 279662306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 279762306a36Sopenharmony_ci struct isr_statistics *isr_stats = &trans_pcie->isr_stats; 279862306a36Sopenharmony_ci u32 reset_flag; 279962306a36Sopenharmony_ci int ret; 280062306a36Sopenharmony_ci 280162306a36Sopenharmony_ci ret = kstrtou32_from_user(user_buf, count, 16, &reset_flag); 280262306a36Sopenharmony_ci if (ret) 280362306a36Sopenharmony_ci return ret; 280462306a36Sopenharmony_ci if (reset_flag == 0) 280562306a36Sopenharmony_ci memset(isr_stats, 0, sizeof(*isr_stats)); 280662306a36Sopenharmony_ci 280762306a36Sopenharmony_ci return count; 280862306a36Sopenharmony_ci} 280962306a36Sopenharmony_ci 281062306a36Sopenharmony_cistatic ssize_t iwl_dbgfs_csr_write(struct file *file, 281162306a36Sopenharmony_ci const char __user *user_buf, 281262306a36Sopenharmony_ci size_t count, loff_t *ppos) 281362306a36Sopenharmony_ci{ 281462306a36Sopenharmony_ci struct iwl_trans *trans = file->private_data; 281562306a36Sopenharmony_ci 281662306a36Sopenharmony_ci iwl_pcie_dump_csr(trans); 281762306a36Sopenharmony_ci 281862306a36Sopenharmony_ci return count; 281962306a36Sopenharmony_ci} 282062306a36Sopenharmony_ci 282162306a36Sopenharmony_cistatic ssize_t iwl_dbgfs_fh_reg_read(struct file *file, 282262306a36Sopenharmony_ci char __user *user_buf, 282362306a36Sopenharmony_ci size_t count, loff_t *ppos) 282462306a36Sopenharmony_ci{ 282562306a36Sopenharmony_ci struct iwl_trans *trans = file->private_data; 282662306a36Sopenharmony_ci char *buf = NULL; 282762306a36Sopenharmony_ci ssize_t ret; 282862306a36Sopenharmony_ci 282962306a36Sopenharmony_ci ret = iwl_dump_fh(trans, &buf); 283062306a36Sopenharmony_ci if (ret < 0) 283162306a36Sopenharmony_ci return ret; 283262306a36Sopenharmony_ci if (!buf) 283362306a36Sopenharmony_ci return -EINVAL; 283462306a36Sopenharmony_ci ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); 283562306a36Sopenharmony_ci kfree(buf); 283662306a36Sopenharmony_ci return ret; 283762306a36Sopenharmony_ci} 283862306a36Sopenharmony_ci 283962306a36Sopenharmony_cistatic ssize_t iwl_dbgfs_rfkill_read(struct file *file, 284062306a36Sopenharmony_ci char __user *user_buf, 284162306a36Sopenharmony_ci size_t count, loff_t *ppos) 284262306a36Sopenharmony_ci{ 284362306a36Sopenharmony_ci struct iwl_trans *trans = file->private_data; 284462306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 284562306a36Sopenharmony_ci char buf[100]; 284662306a36Sopenharmony_ci int pos; 284762306a36Sopenharmony_ci 284862306a36Sopenharmony_ci pos = scnprintf(buf, sizeof(buf), "debug: %d\nhw: %d\n", 284962306a36Sopenharmony_ci trans_pcie->debug_rfkill, 285062306a36Sopenharmony_ci !(iwl_read32(trans, CSR_GP_CNTRL) & 285162306a36Sopenharmony_ci CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)); 285262306a36Sopenharmony_ci 285362306a36Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, buf, pos); 285462306a36Sopenharmony_ci} 285562306a36Sopenharmony_ci 285662306a36Sopenharmony_cistatic ssize_t iwl_dbgfs_rfkill_write(struct file *file, 285762306a36Sopenharmony_ci const char __user *user_buf, 285862306a36Sopenharmony_ci size_t count, loff_t *ppos) 285962306a36Sopenharmony_ci{ 286062306a36Sopenharmony_ci struct iwl_trans *trans = file->private_data; 286162306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 286262306a36Sopenharmony_ci bool new_value; 286362306a36Sopenharmony_ci int ret; 286462306a36Sopenharmony_ci 286562306a36Sopenharmony_ci ret = kstrtobool_from_user(user_buf, count, &new_value); 286662306a36Sopenharmony_ci if (ret) 286762306a36Sopenharmony_ci return ret; 286862306a36Sopenharmony_ci if (new_value == trans_pcie->debug_rfkill) 286962306a36Sopenharmony_ci return count; 287062306a36Sopenharmony_ci IWL_WARN(trans, "changing debug rfkill %d->%d\n", 287162306a36Sopenharmony_ci trans_pcie->debug_rfkill, new_value); 287262306a36Sopenharmony_ci trans_pcie->debug_rfkill = new_value; 287362306a36Sopenharmony_ci iwl_pcie_handle_rfkill_irq(trans, false); 287462306a36Sopenharmony_ci 287562306a36Sopenharmony_ci return count; 287662306a36Sopenharmony_ci} 287762306a36Sopenharmony_ci 287862306a36Sopenharmony_cistatic int iwl_dbgfs_monitor_data_open(struct inode *inode, 287962306a36Sopenharmony_ci struct file *file) 288062306a36Sopenharmony_ci{ 288162306a36Sopenharmony_ci struct iwl_trans *trans = inode->i_private; 288262306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 288362306a36Sopenharmony_ci 288462306a36Sopenharmony_ci if (!trans->dbg.dest_tlv || 288562306a36Sopenharmony_ci trans->dbg.dest_tlv->monitor_mode != EXTERNAL_MODE) { 288662306a36Sopenharmony_ci IWL_ERR(trans, "Debug destination is not set to DRAM\n"); 288762306a36Sopenharmony_ci return -ENOENT; 288862306a36Sopenharmony_ci } 288962306a36Sopenharmony_ci 289062306a36Sopenharmony_ci if (trans_pcie->fw_mon_data.state != IWL_FW_MON_DBGFS_STATE_CLOSED) 289162306a36Sopenharmony_ci return -EBUSY; 289262306a36Sopenharmony_ci 289362306a36Sopenharmony_ci trans_pcie->fw_mon_data.state = IWL_FW_MON_DBGFS_STATE_OPEN; 289462306a36Sopenharmony_ci return simple_open(inode, file); 289562306a36Sopenharmony_ci} 289662306a36Sopenharmony_ci 289762306a36Sopenharmony_cistatic int iwl_dbgfs_monitor_data_release(struct inode *inode, 289862306a36Sopenharmony_ci struct file *file) 289962306a36Sopenharmony_ci{ 290062306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = 290162306a36Sopenharmony_ci IWL_TRANS_GET_PCIE_TRANS(inode->i_private); 290262306a36Sopenharmony_ci 290362306a36Sopenharmony_ci if (trans_pcie->fw_mon_data.state == IWL_FW_MON_DBGFS_STATE_OPEN) 290462306a36Sopenharmony_ci trans_pcie->fw_mon_data.state = IWL_FW_MON_DBGFS_STATE_CLOSED; 290562306a36Sopenharmony_ci return 0; 290662306a36Sopenharmony_ci} 290762306a36Sopenharmony_ci 290862306a36Sopenharmony_cistatic bool iwl_write_to_user_buf(char __user *user_buf, ssize_t count, 290962306a36Sopenharmony_ci void *buf, ssize_t *size, 291062306a36Sopenharmony_ci ssize_t *bytes_copied) 291162306a36Sopenharmony_ci{ 291262306a36Sopenharmony_ci ssize_t buf_size_left = count - *bytes_copied; 291362306a36Sopenharmony_ci 291462306a36Sopenharmony_ci buf_size_left = buf_size_left - (buf_size_left % sizeof(u32)); 291562306a36Sopenharmony_ci if (*size > buf_size_left) 291662306a36Sopenharmony_ci *size = buf_size_left; 291762306a36Sopenharmony_ci 291862306a36Sopenharmony_ci *size -= copy_to_user(user_buf, buf, *size); 291962306a36Sopenharmony_ci *bytes_copied += *size; 292062306a36Sopenharmony_ci 292162306a36Sopenharmony_ci if (buf_size_left == *size) 292262306a36Sopenharmony_ci return true; 292362306a36Sopenharmony_ci return false; 292462306a36Sopenharmony_ci} 292562306a36Sopenharmony_ci 292662306a36Sopenharmony_cistatic ssize_t iwl_dbgfs_monitor_data_read(struct file *file, 292762306a36Sopenharmony_ci char __user *user_buf, 292862306a36Sopenharmony_ci size_t count, loff_t *ppos) 292962306a36Sopenharmony_ci{ 293062306a36Sopenharmony_ci struct iwl_trans *trans = file->private_data; 293162306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 293262306a36Sopenharmony_ci u8 *cpu_addr = (void *)trans->dbg.fw_mon.block, *curr_buf; 293362306a36Sopenharmony_ci struct cont_rec *data = &trans_pcie->fw_mon_data; 293462306a36Sopenharmony_ci u32 write_ptr_addr, wrap_cnt_addr, write_ptr, wrap_cnt; 293562306a36Sopenharmony_ci ssize_t size, bytes_copied = 0; 293662306a36Sopenharmony_ci bool b_full; 293762306a36Sopenharmony_ci 293862306a36Sopenharmony_ci if (trans->dbg.dest_tlv) { 293962306a36Sopenharmony_ci write_ptr_addr = 294062306a36Sopenharmony_ci le32_to_cpu(trans->dbg.dest_tlv->write_ptr_reg); 294162306a36Sopenharmony_ci wrap_cnt_addr = le32_to_cpu(trans->dbg.dest_tlv->wrap_count); 294262306a36Sopenharmony_ci } else { 294362306a36Sopenharmony_ci write_ptr_addr = MON_BUFF_WRPTR; 294462306a36Sopenharmony_ci wrap_cnt_addr = MON_BUFF_CYCLE_CNT; 294562306a36Sopenharmony_ci } 294662306a36Sopenharmony_ci 294762306a36Sopenharmony_ci if (unlikely(!trans->dbg.rec_on)) 294862306a36Sopenharmony_ci return 0; 294962306a36Sopenharmony_ci 295062306a36Sopenharmony_ci mutex_lock(&data->mutex); 295162306a36Sopenharmony_ci if (data->state == 295262306a36Sopenharmony_ci IWL_FW_MON_DBGFS_STATE_DISABLED) { 295362306a36Sopenharmony_ci mutex_unlock(&data->mutex); 295462306a36Sopenharmony_ci return 0; 295562306a36Sopenharmony_ci } 295662306a36Sopenharmony_ci 295762306a36Sopenharmony_ci /* write_ptr position in bytes rather then DW */ 295862306a36Sopenharmony_ci write_ptr = iwl_read_prph(trans, write_ptr_addr) * sizeof(u32); 295962306a36Sopenharmony_ci wrap_cnt = iwl_read_prph(trans, wrap_cnt_addr); 296062306a36Sopenharmony_ci 296162306a36Sopenharmony_ci if (data->prev_wrap_cnt == wrap_cnt) { 296262306a36Sopenharmony_ci size = write_ptr - data->prev_wr_ptr; 296362306a36Sopenharmony_ci curr_buf = cpu_addr + data->prev_wr_ptr; 296462306a36Sopenharmony_ci b_full = iwl_write_to_user_buf(user_buf, count, 296562306a36Sopenharmony_ci curr_buf, &size, 296662306a36Sopenharmony_ci &bytes_copied); 296762306a36Sopenharmony_ci data->prev_wr_ptr += size; 296862306a36Sopenharmony_ci 296962306a36Sopenharmony_ci } else if (data->prev_wrap_cnt == wrap_cnt - 1 && 297062306a36Sopenharmony_ci write_ptr < data->prev_wr_ptr) { 297162306a36Sopenharmony_ci size = trans->dbg.fw_mon.size - data->prev_wr_ptr; 297262306a36Sopenharmony_ci curr_buf = cpu_addr + data->prev_wr_ptr; 297362306a36Sopenharmony_ci b_full = iwl_write_to_user_buf(user_buf, count, 297462306a36Sopenharmony_ci curr_buf, &size, 297562306a36Sopenharmony_ci &bytes_copied); 297662306a36Sopenharmony_ci data->prev_wr_ptr += size; 297762306a36Sopenharmony_ci 297862306a36Sopenharmony_ci if (!b_full) { 297962306a36Sopenharmony_ci size = write_ptr; 298062306a36Sopenharmony_ci b_full = iwl_write_to_user_buf(user_buf, count, 298162306a36Sopenharmony_ci cpu_addr, &size, 298262306a36Sopenharmony_ci &bytes_copied); 298362306a36Sopenharmony_ci data->prev_wr_ptr = size; 298462306a36Sopenharmony_ci data->prev_wrap_cnt++; 298562306a36Sopenharmony_ci } 298662306a36Sopenharmony_ci } else { 298762306a36Sopenharmony_ci if (data->prev_wrap_cnt == wrap_cnt - 1 && 298862306a36Sopenharmony_ci write_ptr > data->prev_wr_ptr) 298962306a36Sopenharmony_ci IWL_WARN(trans, 299062306a36Sopenharmony_ci "write pointer passed previous write pointer, start copying from the beginning\n"); 299162306a36Sopenharmony_ci else if (!unlikely(data->prev_wrap_cnt == 0 && 299262306a36Sopenharmony_ci data->prev_wr_ptr == 0)) 299362306a36Sopenharmony_ci IWL_WARN(trans, 299462306a36Sopenharmony_ci "monitor data is out of sync, start copying from the beginning\n"); 299562306a36Sopenharmony_ci 299662306a36Sopenharmony_ci size = write_ptr; 299762306a36Sopenharmony_ci b_full = iwl_write_to_user_buf(user_buf, count, 299862306a36Sopenharmony_ci cpu_addr, &size, 299962306a36Sopenharmony_ci &bytes_copied); 300062306a36Sopenharmony_ci data->prev_wr_ptr = size; 300162306a36Sopenharmony_ci data->prev_wrap_cnt = wrap_cnt; 300262306a36Sopenharmony_ci } 300362306a36Sopenharmony_ci 300462306a36Sopenharmony_ci mutex_unlock(&data->mutex); 300562306a36Sopenharmony_ci 300662306a36Sopenharmony_ci return bytes_copied; 300762306a36Sopenharmony_ci} 300862306a36Sopenharmony_ci 300962306a36Sopenharmony_cistatic ssize_t iwl_dbgfs_rf_read(struct file *file, 301062306a36Sopenharmony_ci char __user *user_buf, 301162306a36Sopenharmony_ci size_t count, loff_t *ppos) 301262306a36Sopenharmony_ci{ 301362306a36Sopenharmony_ci struct iwl_trans *trans = file->private_data; 301462306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 301562306a36Sopenharmony_ci 301662306a36Sopenharmony_ci if (!trans_pcie->rf_name[0]) 301762306a36Sopenharmony_ci return -ENODEV; 301862306a36Sopenharmony_ci 301962306a36Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, 302062306a36Sopenharmony_ci trans_pcie->rf_name, 302162306a36Sopenharmony_ci strlen(trans_pcie->rf_name)); 302262306a36Sopenharmony_ci} 302362306a36Sopenharmony_ci 302462306a36Sopenharmony_ciDEBUGFS_READ_WRITE_FILE_OPS(interrupt); 302562306a36Sopenharmony_ciDEBUGFS_READ_FILE_OPS(fh_reg); 302662306a36Sopenharmony_ciDEBUGFS_READ_FILE_OPS(rx_queue); 302762306a36Sopenharmony_ciDEBUGFS_WRITE_FILE_OPS(csr); 302862306a36Sopenharmony_ciDEBUGFS_READ_WRITE_FILE_OPS(rfkill); 302962306a36Sopenharmony_ciDEBUGFS_READ_FILE_OPS(rf); 303062306a36Sopenharmony_ci 303162306a36Sopenharmony_cistatic const struct file_operations iwl_dbgfs_tx_queue_ops = { 303262306a36Sopenharmony_ci .owner = THIS_MODULE, 303362306a36Sopenharmony_ci .open = iwl_dbgfs_tx_queue_open, 303462306a36Sopenharmony_ci .read = seq_read, 303562306a36Sopenharmony_ci .llseek = seq_lseek, 303662306a36Sopenharmony_ci .release = seq_release_private, 303762306a36Sopenharmony_ci}; 303862306a36Sopenharmony_ci 303962306a36Sopenharmony_cistatic const struct file_operations iwl_dbgfs_monitor_data_ops = { 304062306a36Sopenharmony_ci .read = iwl_dbgfs_monitor_data_read, 304162306a36Sopenharmony_ci .open = iwl_dbgfs_monitor_data_open, 304262306a36Sopenharmony_ci .release = iwl_dbgfs_monitor_data_release, 304362306a36Sopenharmony_ci}; 304462306a36Sopenharmony_ci 304562306a36Sopenharmony_ci/* Create the debugfs files and directories */ 304662306a36Sopenharmony_civoid iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans) 304762306a36Sopenharmony_ci{ 304862306a36Sopenharmony_ci struct dentry *dir = trans->dbgfs_dir; 304962306a36Sopenharmony_ci 305062306a36Sopenharmony_ci DEBUGFS_ADD_FILE(rx_queue, dir, 0400); 305162306a36Sopenharmony_ci DEBUGFS_ADD_FILE(tx_queue, dir, 0400); 305262306a36Sopenharmony_ci DEBUGFS_ADD_FILE(interrupt, dir, 0600); 305362306a36Sopenharmony_ci DEBUGFS_ADD_FILE(csr, dir, 0200); 305462306a36Sopenharmony_ci DEBUGFS_ADD_FILE(fh_reg, dir, 0400); 305562306a36Sopenharmony_ci DEBUGFS_ADD_FILE(rfkill, dir, 0600); 305662306a36Sopenharmony_ci DEBUGFS_ADD_FILE(monitor_data, dir, 0400); 305762306a36Sopenharmony_ci DEBUGFS_ADD_FILE(rf, dir, 0400); 305862306a36Sopenharmony_ci} 305962306a36Sopenharmony_ci 306062306a36Sopenharmony_cistatic void iwl_trans_pcie_debugfs_cleanup(struct iwl_trans *trans) 306162306a36Sopenharmony_ci{ 306262306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 306362306a36Sopenharmony_ci struct cont_rec *data = &trans_pcie->fw_mon_data; 306462306a36Sopenharmony_ci 306562306a36Sopenharmony_ci mutex_lock(&data->mutex); 306662306a36Sopenharmony_ci data->state = IWL_FW_MON_DBGFS_STATE_DISABLED; 306762306a36Sopenharmony_ci mutex_unlock(&data->mutex); 306862306a36Sopenharmony_ci} 306962306a36Sopenharmony_ci#endif /*CONFIG_IWLWIFI_DEBUGFS */ 307062306a36Sopenharmony_ci 307162306a36Sopenharmony_cistatic u32 iwl_trans_pcie_get_cmdlen(struct iwl_trans *trans, void *tfd) 307262306a36Sopenharmony_ci{ 307362306a36Sopenharmony_ci u32 cmdlen = 0; 307462306a36Sopenharmony_ci int i; 307562306a36Sopenharmony_ci 307662306a36Sopenharmony_ci for (i = 0; i < trans->txqs.tfd.max_tbs; i++) 307762306a36Sopenharmony_ci cmdlen += iwl_txq_gen1_tfd_tb_get_len(trans, tfd, i); 307862306a36Sopenharmony_ci 307962306a36Sopenharmony_ci return cmdlen; 308062306a36Sopenharmony_ci} 308162306a36Sopenharmony_ci 308262306a36Sopenharmony_cistatic u32 iwl_trans_pcie_dump_rbs(struct iwl_trans *trans, 308362306a36Sopenharmony_ci struct iwl_fw_error_dump_data **data, 308462306a36Sopenharmony_ci int allocated_rb_nums) 308562306a36Sopenharmony_ci{ 308662306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 308762306a36Sopenharmony_ci int max_len = trans_pcie->rx_buf_bytes; 308862306a36Sopenharmony_ci /* Dump RBs is supported only for pre-9000 devices (1 queue) */ 308962306a36Sopenharmony_ci struct iwl_rxq *rxq = &trans_pcie->rxq[0]; 309062306a36Sopenharmony_ci u32 i, r, j, rb_len = 0; 309162306a36Sopenharmony_ci 309262306a36Sopenharmony_ci spin_lock_bh(&rxq->lock); 309362306a36Sopenharmony_ci 309462306a36Sopenharmony_ci r = le16_to_cpu(iwl_get_closed_rb_stts(trans, rxq)) & 0x0FFF; 309562306a36Sopenharmony_ci 309662306a36Sopenharmony_ci for (i = rxq->read, j = 0; 309762306a36Sopenharmony_ci i != r && j < allocated_rb_nums; 309862306a36Sopenharmony_ci i = (i + 1) & RX_QUEUE_MASK, j++) { 309962306a36Sopenharmony_ci struct iwl_rx_mem_buffer *rxb = rxq->queue[i]; 310062306a36Sopenharmony_ci struct iwl_fw_error_dump_rb *rb; 310162306a36Sopenharmony_ci 310262306a36Sopenharmony_ci dma_sync_single_for_cpu(trans->dev, rxb->page_dma, 310362306a36Sopenharmony_ci max_len, DMA_FROM_DEVICE); 310462306a36Sopenharmony_ci 310562306a36Sopenharmony_ci rb_len += sizeof(**data) + sizeof(*rb) + max_len; 310662306a36Sopenharmony_ci 310762306a36Sopenharmony_ci (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RB); 310862306a36Sopenharmony_ci (*data)->len = cpu_to_le32(sizeof(*rb) + max_len); 310962306a36Sopenharmony_ci rb = (void *)(*data)->data; 311062306a36Sopenharmony_ci rb->index = cpu_to_le32(i); 311162306a36Sopenharmony_ci memcpy(rb->data, page_address(rxb->page), max_len); 311262306a36Sopenharmony_ci 311362306a36Sopenharmony_ci *data = iwl_fw_error_next_data(*data); 311462306a36Sopenharmony_ci } 311562306a36Sopenharmony_ci 311662306a36Sopenharmony_ci spin_unlock_bh(&rxq->lock); 311762306a36Sopenharmony_ci 311862306a36Sopenharmony_ci return rb_len; 311962306a36Sopenharmony_ci} 312062306a36Sopenharmony_ci#define IWL_CSR_TO_DUMP (0x250) 312162306a36Sopenharmony_ci 312262306a36Sopenharmony_cistatic u32 iwl_trans_pcie_dump_csr(struct iwl_trans *trans, 312362306a36Sopenharmony_ci struct iwl_fw_error_dump_data **data) 312462306a36Sopenharmony_ci{ 312562306a36Sopenharmony_ci u32 csr_len = sizeof(**data) + IWL_CSR_TO_DUMP; 312662306a36Sopenharmony_ci __le32 *val; 312762306a36Sopenharmony_ci int i; 312862306a36Sopenharmony_ci 312962306a36Sopenharmony_ci (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_CSR); 313062306a36Sopenharmony_ci (*data)->len = cpu_to_le32(IWL_CSR_TO_DUMP); 313162306a36Sopenharmony_ci val = (void *)(*data)->data; 313262306a36Sopenharmony_ci 313362306a36Sopenharmony_ci for (i = 0; i < IWL_CSR_TO_DUMP; i += 4) 313462306a36Sopenharmony_ci *val++ = cpu_to_le32(iwl_trans_pcie_read32(trans, i)); 313562306a36Sopenharmony_ci 313662306a36Sopenharmony_ci *data = iwl_fw_error_next_data(*data); 313762306a36Sopenharmony_ci 313862306a36Sopenharmony_ci return csr_len; 313962306a36Sopenharmony_ci} 314062306a36Sopenharmony_ci 314162306a36Sopenharmony_cistatic u32 iwl_trans_pcie_fh_regs_dump(struct iwl_trans *trans, 314262306a36Sopenharmony_ci struct iwl_fw_error_dump_data **data) 314362306a36Sopenharmony_ci{ 314462306a36Sopenharmony_ci u32 fh_regs_len = FH_MEM_UPPER_BOUND - FH_MEM_LOWER_BOUND; 314562306a36Sopenharmony_ci __le32 *val; 314662306a36Sopenharmony_ci int i; 314762306a36Sopenharmony_ci 314862306a36Sopenharmony_ci if (!iwl_trans_grab_nic_access(trans)) 314962306a36Sopenharmony_ci return 0; 315062306a36Sopenharmony_ci 315162306a36Sopenharmony_ci (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FH_REGS); 315262306a36Sopenharmony_ci (*data)->len = cpu_to_le32(fh_regs_len); 315362306a36Sopenharmony_ci val = (void *)(*data)->data; 315462306a36Sopenharmony_ci 315562306a36Sopenharmony_ci if (!trans->trans_cfg->gen2) 315662306a36Sopenharmony_ci for (i = FH_MEM_LOWER_BOUND; i < FH_MEM_UPPER_BOUND; 315762306a36Sopenharmony_ci i += sizeof(u32)) 315862306a36Sopenharmony_ci *val++ = cpu_to_le32(iwl_trans_pcie_read32(trans, i)); 315962306a36Sopenharmony_ci else 316062306a36Sopenharmony_ci for (i = iwl_umac_prph(trans, FH_MEM_LOWER_BOUND_GEN2); 316162306a36Sopenharmony_ci i < iwl_umac_prph(trans, FH_MEM_UPPER_BOUND_GEN2); 316262306a36Sopenharmony_ci i += sizeof(u32)) 316362306a36Sopenharmony_ci *val++ = cpu_to_le32(iwl_trans_pcie_read_prph(trans, 316462306a36Sopenharmony_ci i)); 316562306a36Sopenharmony_ci 316662306a36Sopenharmony_ci iwl_trans_release_nic_access(trans); 316762306a36Sopenharmony_ci 316862306a36Sopenharmony_ci *data = iwl_fw_error_next_data(*data); 316962306a36Sopenharmony_ci 317062306a36Sopenharmony_ci return sizeof(**data) + fh_regs_len; 317162306a36Sopenharmony_ci} 317262306a36Sopenharmony_ci 317362306a36Sopenharmony_cistatic u32 317462306a36Sopenharmony_ciiwl_trans_pci_dump_marbh_monitor(struct iwl_trans *trans, 317562306a36Sopenharmony_ci struct iwl_fw_error_dump_fw_mon *fw_mon_data, 317662306a36Sopenharmony_ci u32 monitor_len) 317762306a36Sopenharmony_ci{ 317862306a36Sopenharmony_ci u32 buf_size_in_dwords = (monitor_len >> 2); 317962306a36Sopenharmony_ci u32 *buffer = (u32 *)fw_mon_data->data; 318062306a36Sopenharmony_ci u32 i; 318162306a36Sopenharmony_ci 318262306a36Sopenharmony_ci if (!iwl_trans_grab_nic_access(trans)) 318362306a36Sopenharmony_ci return 0; 318462306a36Sopenharmony_ci 318562306a36Sopenharmony_ci iwl_write_umac_prph_no_grab(trans, MON_DMARB_RD_CTL_ADDR, 0x1); 318662306a36Sopenharmony_ci for (i = 0; i < buf_size_in_dwords; i++) 318762306a36Sopenharmony_ci buffer[i] = iwl_read_umac_prph_no_grab(trans, 318862306a36Sopenharmony_ci MON_DMARB_RD_DATA_ADDR); 318962306a36Sopenharmony_ci iwl_write_umac_prph_no_grab(trans, MON_DMARB_RD_CTL_ADDR, 0x0); 319062306a36Sopenharmony_ci 319162306a36Sopenharmony_ci iwl_trans_release_nic_access(trans); 319262306a36Sopenharmony_ci 319362306a36Sopenharmony_ci return monitor_len; 319462306a36Sopenharmony_ci} 319562306a36Sopenharmony_ci 319662306a36Sopenharmony_cistatic void 319762306a36Sopenharmony_ciiwl_trans_pcie_dump_pointers(struct iwl_trans *trans, 319862306a36Sopenharmony_ci struct iwl_fw_error_dump_fw_mon *fw_mon_data) 319962306a36Sopenharmony_ci{ 320062306a36Sopenharmony_ci u32 base, base_high, write_ptr, write_ptr_val, wrap_cnt; 320162306a36Sopenharmony_ci 320262306a36Sopenharmony_ci if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { 320362306a36Sopenharmony_ci base = DBGC_CUR_DBGBUF_BASE_ADDR_LSB; 320462306a36Sopenharmony_ci base_high = DBGC_CUR_DBGBUF_BASE_ADDR_MSB; 320562306a36Sopenharmony_ci write_ptr = DBGC_CUR_DBGBUF_STATUS; 320662306a36Sopenharmony_ci wrap_cnt = DBGC_DBGBUF_WRAP_AROUND; 320762306a36Sopenharmony_ci } else if (trans->dbg.dest_tlv) { 320862306a36Sopenharmony_ci write_ptr = le32_to_cpu(trans->dbg.dest_tlv->write_ptr_reg); 320962306a36Sopenharmony_ci wrap_cnt = le32_to_cpu(trans->dbg.dest_tlv->wrap_count); 321062306a36Sopenharmony_ci base = le32_to_cpu(trans->dbg.dest_tlv->base_reg); 321162306a36Sopenharmony_ci } else { 321262306a36Sopenharmony_ci base = MON_BUFF_BASE_ADDR; 321362306a36Sopenharmony_ci write_ptr = MON_BUFF_WRPTR; 321462306a36Sopenharmony_ci wrap_cnt = MON_BUFF_CYCLE_CNT; 321562306a36Sopenharmony_ci } 321662306a36Sopenharmony_ci 321762306a36Sopenharmony_ci write_ptr_val = iwl_read_prph(trans, write_ptr); 321862306a36Sopenharmony_ci fw_mon_data->fw_mon_cycle_cnt = 321962306a36Sopenharmony_ci cpu_to_le32(iwl_read_prph(trans, wrap_cnt)); 322062306a36Sopenharmony_ci fw_mon_data->fw_mon_base_ptr = 322162306a36Sopenharmony_ci cpu_to_le32(iwl_read_prph(trans, base)); 322262306a36Sopenharmony_ci if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { 322362306a36Sopenharmony_ci fw_mon_data->fw_mon_base_high_ptr = 322462306a36Sopenharmony_ci cpu_to_le32(iwl_read_prph(trans, base_high)); 322562306a36Sopenharmony_ci write_ptr_val &= DBGC_CUR_DBGBUF_STATUS_OFFSET_MSK; 322662306a36Sopenharmony_ci /* convert wrtPtr to DWs, to align with all HWs */ 322762306a36Sopenharmony_ci write_ptr_val >>= 2; 322862306a36Sopenharmony_ci } 322962306a36Sopenharmony_ci fw_mon_data->fw_mon_wr_ptr = cpu_to_le32(write_ptr_val); 323062306a36Sopenharmony_ci} 323162306a36Sopenharmony_ci 323262306a36Sopenharmony_cistatic u32 323362306a36Sopenharmony_ciiwl_trans_pcie_dump_monitor(struct iwl_trans *trans, 323462306a36Sopenharmony_ci struct iwl_fw_error_dump_data **data, 323562306a36Sopenharmony_ci u32 monitor_len) 323662306a36Sopenharmony_ci{ 323762306a36Sopenharmony_ci struct iwl_dram_data *fw_mon = &trans->dbg.fw_mon; 323862306a36Sopenharmony_ci u32 len = 0; 323962306a36Sopenharmony_ci 324062306a36Sopenharmony_ci if (trans->dbg.dest_tlv || 324162306a36Sopenharmony_ci (fw_mon->size && 324262306a36Sopenharmony_ci (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_7000 || 324362306a36Sopenharmony_ci trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210))) { 324462306a36Sopenharmony_ci struct iwl_fw_error_dump_fw_mon *fw_mon_data; 324562306a36Sopenharmony_ci 324662306a36Sopenharmony_ci (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FW_MONITOR); 324762306a36Sopenharmony_ci fw_mon_data = (void *)(*data)->data; 324862306a36Sopenharmony_ci 324962306a36Sopenharmony_ci iwl_trans_pcie_dump_pointers(trans, fw_mon_data); 325062306a36Sopenharmony_ci 325162306a36Sopenharmony_ci len += sizeof(**data) + sizeof(*fw_mon_data); 325262306a36Sopenharmony_ci if (fw_mon->size) { 325362306a36Sopenharmony_ci memcpy(fw_mon_data->data, fw_mon->block, fw_mon->size); 325462306a36Sopenharmony_ci monitor_len = fw_mon->size; 325562306a36Sopenharmony_ci } else if (trans->dbg.dest_tlv->monitor_mode == SMEM_MODE) { 325662306a36Sopenharmony_ci u32 base = le32_to_cpu(fw_mon_data->fw_mon_base_ptr); 325762306a36Sopenharmony_ci /* 325862306a36Sopenharmony_ci * Update pointers to reflect actual values after 325962306a36Sopenharmony_ci * shifting 326062306a36Sopenharmony_ci */ 326162306a36Sopenharmony_ci if (trans->dbg.dest_tlv->version) { 326262306a36Sopenharmony_ci base = (iwl_read_prph(trans, base) & 326362306a36Sopenharmony_ci IWL_LDBG_M2S_BUF_BA_MSK) << 326462306a36Sopenharmony_ci trans->dbg.dest_tlv->base_shift; 326562306a36Sopenharmony_ci base *= IWL_M2S_UNIT_SIZE; 326662306a36Sopenharmony_ci base += trans->cfg->smem_offset; 326762306a36Sopenharmony_ci } else { 326862306a36Sopenharmony_ci base = iwl_read_prph(trans, base) << 326962306a36Sopenharmony_ci trans->dbg.dest_tlv->base_shift; 327062306a36Sopenharmony_ci } 327162306a36Sopenharmony_ci 327262306a36Sopenharmony_ci iwl_trans_read_mem(trans, base, fw_mon_data->data, 327362306a36Sopenharmony_ci monitor_len / sizeof(u32)); 327462306a36Sopenharmony_ci } else if (trans->dbg.dest_tlv->monitor_mode == MARBH_MODE) { 327562306a36Sopenharmony_ci monitor_len = 327662306a36Sopenharmony_ci iwl_trans_pci_dump_marbh_monitor(trans, 327762306a36Sopenharmony_ci fw_mon_data, 327862306a36Sopenharmony_ci monitor_len); 327962306a36Sopenharmony_ci } else { 328062306a36Sopenharmony_ci /* Didn't match anything - output no monitor data */ 328162306a36Sopenharmony_ci monitor_len = 0; 328262306a36Sopenharmony_ci } 328362306a36Sopenharmony_ci 328462306a36Sopenharmony_ci len += monitor_len; 328562306a36Sopenharmony_ci (*data)->len = cpu_to_le32(monitor_len + sizeof(*fw_mon_data)); 328662306a36Sopenharmony_ci } 328762306a36Sopenharmony_ci 328862306a36Sopenharmony_ci return len; 328962306a36Sopenharmony_ci} 329062306a36Sopenharmony_ci 329162306a36Sopenharmony_cistatic int iwl_trans_get_fw_monitor_len(struct iwl_trans *trans, u32 *len) 329262306a36Sopenharmony_ci{ 329362306a36Sopenharmony_ci if (trans->dbg.fw_mon.size) { 329462306a36Sopenharmony_ci *len += sizeof(struct iwl_fw_error_dump_data) + 329562306a36Sopenharmony_ci sizeof(struct iwl_fw_error_dump_fw_mon) + 329662306a36Sopenharmony_ci trans->dbg.fw_mon.size; 329762306a36Sopenharmony_ci return trans->dbg.fw_mon.size; 329862306a36Sopenharmony_ci } else if (trans->dbg.dest_tlv) { 329962306a36Sopenharmony_ci u32 base, end, cfg_reg, monitor_len; 330062306a36Sopenharmony_ci 330162306a36Sopenharmony_ci if (trans->dbg.dest_tlv->version == 1) { 330262306a36Sopenharmony_ci cfg_reg = le32_to_cpu(trans->dbg.dest_tlv->base_reg); 330362306a36Sopenharmony_ci cfg_reg = iwl_read_prph(trans, cfg_reg); 330462306a36Sopenharmony_ci base = (cfg_reg & IWL_LDBG_M2S_BUF_BA_MSK) << 330562306a36Sopenharmony_ci trans->dbg.dest_tlv->base_shift; 330662306a36Sopenharmony_ci base *= IWL_M2S_UNIT_SIZE; 330762306a36Sopenharmony_ci base += trans->cfg->smem_offset; 330862306a36Sopenharmony_ci 330962306a36Sopenharmony_ci monitor_len = 331062306a36Sopenharmony_ci (cfg_reg & IWL_LDBG_M2S_BUF_SIZE_MSK) >> 331162306a36Sopenharmony_ci trans->dbg.dest_tlv->end_shift; 331262306a36Sopenharmony_ci monitor_len *= IWL_M2S_UNIT_SIZE; 331362306a36Sopenharmony_ci } else { 331462306a36Sopenharmony_ci base = le32_to_cpu(trans->dbg.dest_tlv->base_reg); 331562306a36Sopenharmony_ci end = le32_to_cpu(trans->dbg.dest_tlv->end_reg); 331662306a36Sopenharmony_ci 331762306a36Sopenharmony_ci base = iwl_read_prph(trans, base) << 331862306a36Sopenharmony_ci trans->dbg.dest_tlv->base_shift; 331962306a36Sopenharmony_ci end = iwl_read_prph(trans, end) << 332062306a36Sopenharmony_ci trans->dbg.dest_tlv->end_shift; 332162306a36Sopenharmony_ci 332262306a36Sopenharmony_ci /* Make "end" point to the actual end */ 332362306a36Sopenharmony_ci if (trans->trans_cfg->device_family >= 332462306a36Sopenharmony_ci IWL_DEVICE_FAMILY_8000 || 332562306a36Sopenharmony_ci trans->dbg.dest_tlv->monitor_mode == MARBH_MODE) 332662306a36Sopenharmony_ci end += (1 << trans->dbg.dest_tlv->end_shift); 332762306a36Sopenharmony_ci monitor_len = end - base; 332862306a36Sopenharmony_ci } 332962306a36Sopenharmony_ci *len += sizeof(struct iwl_fw_error_dump_data) + 333062306a36Sopenharmony_ci sizeof(struct iwl_fw_error_dump_fw_mon) + 333162306a36Sopenharmony_ci monitor_len; 333262306a36Sopenharmony_ci return monitor_len; 333362306a36Sopenharmony_ci } 333462306a36Sopenharmony_ci return 0; 333562306a36Sopenharmony_ci} 333662306a36Sopenharmony_ci 333762306a36Sopenharmony_cistatic struct iwl_trans_dump_data * 333862306a36Sopenharmony_ciiwl_trans_pcie_dump_data(struct iwl_trans *trans, 333962306a36Sopenharmony_ci u32 dump_mask, 334062306a36Sopenharmony_ci const struct iwl_dump_sanitize_ops *sanitize_ops, 334162306a36Sopenharmony_ci void *sanitize_ctx) 334262306a36Sopenharmony_ci{ 334362306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 334462306a36Sopenharmony_ci struct iwl_fw_error_dump_data *data; 334562306a36Sopenharmony_ci struct iwl_txq *cmdq = trans->txqs.txq[trans->txqs.cmd.q_id]; 334662306a36Sopenharmony_ci struct iwl_fw_error_dump_txcmd *txcmd; 334762306a36Sopenharmony_ci struct iwl_trans_dump_data *dump_data; 334862306a36Sopenharmony_ci u32 len, num_rbs = 0, monitor_len = 0; 334962306a36Sopenharmony_ci int i, ptr; 335062306a36Sopenharmony_ci bool dump_rbs = test_bit(STATUS_FW_ERROR, &trans->status) && 335162306a36Sopenharmony_ci !trans->trans_cfg->mq_rx_supported && 335262306a36Sopenharmony_ci dump_mask & BIT(IWL_FW_ERROR_DUMP_RB); 335362306a36Sopenharmony_ci 335462306a36Sopenharmony_ci if (!dump_mask) 335562306a36Sopenharmony_ci return NULL; 335662306a36Sopenharmony_ci 335762306a36Sopenharmony_ci /* transport dump header */ 335862306a36Sopenharmony_ci len = sizeof(*dump_data); 335962306a36Sopenharmony_ci 336062306a36Sopenharmony_ci /* host commands */ 336162306a36Sopenharmony_ci if (dump_mask & BIT(IWL_FW_ERROR_DUMP_TXCMD) && cmdq) 336262306a36Sopenharmony_ci len += sizeof(*data) + 336362306a36Sopenharmony_ci cmdq->n_window * (sizeof(*txcmd) + 336462306a36Sopenharmony_ci TFD_MAX_PAYLOAD_SIZE); 336562306a36Sopenharmony_ci 336662306a36Sopenharmony_ci /* FW monitor */ 336762306a36Sopenharmony_ci if (dump_mask & BIT(IWL_FW_ERROR_DUMP_FW_MONITOR)) 336862306a36Sopenharmony_ci monitor_len = iwl_trans_get_fw_monitor_len(trans, &len); 336962306a36Sopenharmony_ci 337062306a36Sopenharmony_ci /* CSR registers */ 337162306a36Sopenharmony_ci if (dump_mask & BIT(IWL_FW_ERROR_DUMP_CSR)) 337262306a36Sopenharmony_ci len += sizeof(*data) + IWL_CSR_TO_DUMP; 337362306a36Sopenharmony_ci 337462306a36Sopenharmony_ci /* FH registers */ 337562306a36Sopenharmony_ci if (dump_mask & BIT(IWL_FW_ERROR_DUMP_FH_REGS)) { 337662306a36Sopenharmony_ci if (trans->trans_cfg->gen2) 337762306a36Sopenharmony_ci len += sizeof(*data) + 337862306a36Sopenharmony_ci (iwl_umac_prph(trans, FH_MEM_UPPER_BOUND_GEN2) - 337962306a36Sopenharmony_ci iwl_umac_prph(trans, FH_MEM_LOWER_BOUND_GEN2)); 338062306a36Sopenharmony_ci else 338162306a36Sopenharmony_ci len += sizeof(*data) + 338262306a36Sopenharmony_ci (FH_MEM_UPPER_BOUND - 338362306a36Sopenharmony_ci FH_MEM_LOWER_BOUND); 338462306a36Sopenharmony_ci } 338562306a36Sopenharmony_ci 338662306a36Sopenharmony_ci if (dump_rbs) { 338762306a36Sopenharmony_ci /* Dump RBs is supported only for pre-9000 devices (1 queue) */ 338862306a36Sopenharmony_ci struct iwl_rxq *rxq = &trans_pcie->rxq[0]; 338962306a36Sopenharmony_ci /* RBs */ 339062306a36Sopenharmony_ci num_rbs = 339162306a36Sopenharmony_ci le16_to_cpu(iwl_get_closed_rb_stts(trans, rxq)) 339262306a36Sopenharmony_ci & 0x0FFF; 339362306a36Sopenharmony_ci num_rbs = (num_rbs - rxq->read) & RX_QUEUE_MASK; 339462306a36Sopenharmony_ci len += num_rbs * (sizeof(*data) + 339562306a36Sopenharmony_ci sizeof(struct iwl_fw_error_dump_rb) + 339662306a36Sopenharmony_ci (PAGE_SIZE << trans_pcie->rx_page_order)); 339762306a36Sopenharmony_ci } 339862306a36Sopenharmony_ci 339962306a36Sopenharmony_ci /* Paged memory for gen2 HW */ 340062306a36Sopenharmony_ci if (trans->trans_cfg->gen2 && dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING)) 340162306a36Sopenharmony_ci for (i = 0; i < trans->init_dram.paging_cnt; i++) 340262306a36Sopenharmony_ci len += sizeof(*data) + 340362306a36Sopenharmony_ci sizeof(struct iwl_fw_error_dump_paging) + 340462306a36Sopenharmony_ci trans->init_dram.paging[i].size; 340562306a36Sopenharmony_ci 340662306a36Sopenharmony_ci dump_data = vzalloc(len); 340762306a36Sopenharmony_ci if (!dump_data) 340862306a36Sopenharmony_ci return NULL; 340962306a36Sopenharmony_ci 341062306a36Sopenharmony_ci len = 0; 341162306a36Sopenharmony_ci data = (void *)dump_data->data; 341262306a36Sopenharmony_ci 341362306a36Sopenharmony_ci if (dump_mask & BIT(IWL_FW_ERROR_DUMP_TXCMD) && cmdq) { 341462306a36Sopenharmony_ci u16 tfd_size = trans->txqs.tfd.size; 341562306a36Sopenharmony_ci 341662306a36Sopenharmony_ci data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXCMD); 341762306a36Sopenharmony_ci txcmd = (void *)data->data; 341862306a36Sopenharmony_ci spin_lock_bh(&cmdq->lock); 341962306a36Sopenharmony_ci ptr = cmdq->write_ptr; 342062306a36Sopenharmony_ci for (i = 0; i < cmdq->n_window; i++) { 342162306a36Sopenharmony_ci u8 idx = iwl_txq_get_cmd_index(cmdq, ptr); 342262306a36Sopenharmony_ci u8 tfdidx; 342362306a36Sopenharmony_ci u32 caplen, cmdlen; 342462306a36Sopenharmony_ci 342562306a36Sopenharmony_ci if (trans->trans_cfg->gen2) 342662306a36Sopenharmony_ci tfdidx = idx; 342762306a36Sopenharmony_ci else 342862306a36Sopenharmony_ci tfdidx = ptr; 342962306a36Sopenharmony_ci 343062306a36Sopenharmony_ci cmdlen = iwl_trans_pcie_get_cmdlen(trans, 343162306a36Sopenharmony_ci (u8 *)cmdq->tfds + 343262306a36Sopenharmony_ci tfd_size * tfdidx); 343362306a36Sopenharmony_ci caplen = min_t(u32, TFD_MAX_PAYLOAD_SIZE, cmdlen); 343462306a36Sopenharmony_ci 343562306a36Sopenharmony_ci if (cmdlen) { 343662306a36Sopenharmony_ci len += sizeof(*txcmd) + caplen; 343762306a36Sopenharmony_ci txcmd->cmdlen = cpu_to_le32(cmdlen); 343862306a36Sopenharmony_ci txcmd->caplen = cpu_to_le32(caplen); 343962306a36Sopenharmony_ci memcpy(txcmd->data, cmdq->entries[idx].cmd, 344062306a36Sopenharmony_ci caplen); 344162306a36Sopenharmony_ci if (sanitize_ops && sanitize_ops->frob_hcmd) 344262306a36Sopenharmony_ci sanitize_ops->frob_hcmd(sanitize_ctx, 344362306a36Sopenharmony_ci txcmd->data, 344462306a36Sopenharmony_ci caplen); 344562306a36Sopenharmony_ci txcmd = (void *)((u8 *)txcmd->data + caplen); 344662306a36Sopenharmony_ci } 344762306a36Sopenharmony_ci 344862306a36Sopenharmony_ci ptr = iwl_txq_dec_wrap(trans, ptr); 344962306a36Sopenharmony_ci } 345062306a36Sopenharmony_ci spin_unlock_bh(&cmdq->lock); 345162306a36Sopenharmony_ci 345262306a36Sopenharmony_ci data->len = cpu_to_le32(len); 345362306a36Sopenharmony_ci len += sizeof(*data); 345462306a36Sopenharmony_ci data = iwl_fw_error_next_data(data); 345562306a36Sopenharmony_ci } 345662306a36Sopenharmony_ci 345762306a36Sopenharmony_ci if (dump_mask & BIT(IWL_FW_ERROR_DUMP_CSR)) 345862306a36Sopenharmony_ci len += iwl_trans_pcie_dump_csr(trans, &data); 345962306a36Sopenharmony_ci if (dump_mask & BIT(IWL_FW_ERROR_DUMP_FH_REGS)) 346062306a36Sopenharmony_ci len += iwl_trans_pcie_fh_regs_dump(trans, &data); 346162306a36Sopenharmony_ci if (dump_rbs) 346262306a36Sopenharmony_ci len += iwl_trans_pcie_dump_rbs(trans, &data, num_rbs); 346362306a36Sopenharmony_ci 346462306a36Sopenharmony_ci /* Paged memory for gen2 HW */ 346562306a36Sopenharmony_ci if (trans->trans_cfg->gen2 && 346662306a36Sopenharmony_ci dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING)) { 346762306a36Sopenharmony_ci for (i = 0; i < trans->init_dram.paging_cnt; i++) { 346862306a36Sopenharmony_ci struct iwl_fw_error_dump_paging *paging; 346962306a36Sopenharmony_ci u32 page_len = trans->init_dram.paging[i].size; 347062306a36Sopenharmony_ci 347162306a36Sopenharmony_ci data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING); 347262306a36Sopenharmony_ci data->len = cpu_to_le32(sizeof(*paging) + page_len); 347362306a36Sopenharmony_ci paging = (void *)data->data; 347462306a36Sopenharmony_ci paging->index = cpu_to_le32(i); 347562306a36Sopenharmony_ci memcpy(paging->data, 347662306a36Sopenharmony_ci trans->init_dram.paging[i].block, page_len); 347762306a36Sopenharmony_ci data = iwl_fw_error_next_data(data); 347862306a36Sopenharmony_ci 347962306a36Sopenharmony_ci len += sizeof(*data) + sizeof(*paging) + page_len; 348062306a36Sopenharmony_ci } 348162306a36Sopenharmony_ci } 348262306a36Sopenharmony_ci if (dump_mask & BIT(IWL_FW_ERROR_DUMP_FW_MONITOR)) 348362306a36Sopenharmony_ci len += iwl_trans_pcie_dump_monitor(trans, &data, monitor_len); 348462306a36Sopenharmony_ci 348562306a36Sopenharmony_ci dump_data->len = len; 348662306a36Sopenharmony_ci 348762306a36Sopenharmony_ci return dump_data; 348862306a36Sopenharmony_ci} 348962306a36Sopenharmony_ci 349062306a36Sopenharmony_cistatic void iwl_trans_pci_interrupts(struct iwl_trans *trans, bool enable) 349162306a36Sopenharmony_ci{ 349262306a36Sopenharmony_ci if (enable) 349362306a36Sopenharmony_ci iwl_enable_interrupts(trans); 349462306a36Sopenharmony_ci else 349562306a36Sopenharmony_ci iwl_disable_interrupts(trans); 349662306a36Sopenharmony_ci} 349762306a36Sopenharmony_ci 349862306a36Sopenharmony_cistatic void iwl_trans_pcie_sync_nmi(struct iwl_trans *trans) 349962306a36Sopenharmony_ci{ 350062306a36Sopenharmony_ci u32 inta_addr, sw_err_bit; 350162306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 350262306a36Sopenharmony_ci 350362306a36Sopenharmony_ci if (trans_pcie->msix_enabled) { 350462306a36Sopenharmony_ci inta_addr = CSR_MSIX_HW_INT_CAUSES_AD; 350562306a36Sopenharmony_ci if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) 350662306a36Sopenharmony_ci sw_err_bit = MSIX_HW_INT_CAUSES_REG_SW_ERR_BZ; 350762306a36Sopenharmony_ci else 350862306a36Sopenharmony_ci sw_err_bit = MSIX_HW_INT_CAUSES_REG_SW_ERR; 350962306a36Sopenharmony_ci } else { 351062306a36Sopenharmony_ci inta_addr = CSR_INT; 351162306a36Sopenharmony_ci sw_err_bit = CSR_INT_BIT_SW_ERR; 351262306a36Sopenharmony_ci } 351362306a36Sopenharmony_ci 351462306a36Sopenharmony_ci iwl_trans_sync_nmi_with_addr(trans, inta_addr, sw_err_bit); 351562306a36Sopenharmony_ci} 351662306a36Sopenharmony_ci 351762306a36Sopenharmony_ci#define IWL_TRANS_COMMON_OPS \ 351862306a36Sopenharmony_ci .op_mode_leave = iwl_trans_pcie_op_mode_leave, \ 351962306a36Sopenharmony_ci .write8 = iwl_trans_pcie_write8, \ 352062306a36Sopenharmony_ci .write32 = iwl_trans_pcie_write32, \ 352162306a36Sopenharmony_ci .read32 = iwl_trans_pcie_read32, \ 352262306a36Sopenharmony_ci .read_prph = iwl_trans_pcie_read_prph, \ 352362306a36Sopenharmony_ci .write_prph = iwl_trans_pcie_write_prph, \ 352462306a36Sopenharmony_ci .read_mem = iwl_trans_pcie_read_mem, \ 352562306a36Sopenharmony_ci .write_mem = iwl_trans_pcie_write_mem, \ 352662306a36Sopenharmony_ci .read_config32 = iwl_trans_pcie_read_config32, \ 352762306a36Sopenharmony_ci .configure = iwl_trans_pcie_configure, \ 352862306a36Sopenharmony_ci .set_pmi = iwl_trans_pcie_set_pmi, \ 352962306a36Sopenharmony_ci .sw_reset = iwl_trans_pcie_sw_reset, \ 353062306a36Sopenharmony_ci .grab_nic_access = iwl_trans_pcie_grab_nic_access, \ 353162306a36Sopenharmony_ci .release_nic_access = iwl_trans_pcie_release_nic_access, \ 353262306a36Sopenharmony_ci .set_bits_mask = iwl_trans_pcie_set_bits_mask, \ 353362306a36Sopenharmony_ci .dump_data = iwl_trans_pcie_dump_data, \ 353462306a36Sopenharmony_ci .d3_suspend = iwl_trans_pcie_d3_suspend, \ 353562306a36Sopenharmony_ci .d3_resume = iwl_trans_pcie_d3_resume, \ 353662306a36Sopenharmony_ci .interrupts = iwl_trans_pci_interrupts, \ 353762306a36Sopenharmony_ci .sync_nmi = iwl_trans_pcie_sync_nmi, \ 353862306a36Sopenharmony_ci .imr_dma_data = iwl_trans_pcie_copy_imr \ 353962306a36Sopenharmony_ci 354062306a36Sopenharmony_cistatic const struct iwl_trans_ops trans_ops_pcie = { 354162306a36Sopenharmony_ci IWL_TRANS_COMMON_OPS, 354262306a36Sopenharmony_ci .start_hw = iwl_trans_pcie_start_hw, 354362306a36Sopenharmony_ci .fw_alive = iwl_trans_pcie_fw_alive, 354462306a36Sopenharmony_ci .start_fw = iwl_trans_pcie_start_fw, 354562306a36Sopenharmony_ci .stop_device = iwl_trans_pcie_stop_device, 354662306a36Sopenharmony_ci 354762306a36Sopenharmony_ci .send_cmd = iwl_pcie_enqueue_hcmd, 354862306a36Sopenharmony_ci 354962306a36Sopenharmony_ci .tx = iwl_trans_pcie_tx, 355062306a36Sopenharmony_ci .reclaim = iwl_txq_reclaim, 355162306a36Sopenharmony_ci 355262306a36Sopenharmony_ci .txq_disable = iwl_trans_pcie_txq_disable, 355362306a36Sopenharmony_ci .txq_enable = iwl_trans_pcie_txq_enable, 355462306a36Sopenharmony_ci 355562306a36Sopenharmony_ci .txq_set_shared_mode = iwl_trans_pcie_txq_set_shared_mode, 355662306a36Sopenharmony_ci 355762306a36Sopenharmony_ci .wait_tx_queues_empty = iwl_trans_pcie_wait_txqs_empty, 355862306a36Sopenharmony_ci 355962306a36Sopenharmony_ci .freeze_txq_timer = iwl_trans_txq_freeze_timer, 356062306a36Sopenharmony_ci .block_txq_ptrs = iwl_trans_pcie_block_txq_ptrs, 356162306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS 356262306a36Sopenharmony_ci .debugfs_cleanup = iwl_trans_pcie_debugfs_cleanup, 356362306a36Sopenharmony_ci#endif 356462306a36Sopenharmony_ci}; 356562306a36Sopenharmony_ci 356662306a36Sopenharmony_cistatic const struct iwl_trans_ops trans_ops_pcie_gen2 = { 356762306a36Sopenharmony_ci IWL_TRANS_COMMON_OPS, 356862306a36Sopenharmony_ci .start_hw = iwl_trans_pcie_start_hw, 356962306a36Sopenharmony_ci .fw_alive = iwl_trans_pcie_gen2_fw_alive, 357062306a36Sopenharmony_ci .start_fw = iwl_trans_pcie_gen2_start_fw, 357162306a36Sopenharmony_ci .stop_device = iwl_trans_pcie_gen2_stop_device, 357262306a36Sopenharmony_ci 357362306a36Sopenharmony_ci .send_cmd = iwl_pcie_gen2_enqueue_hcmd, 357462306a36Sopenharmony_ci 357562306a36Sopenharmony_ci .tx = iwl_txq_gen2_tx, 357662306a36Sopenharmony_ci .reclaim = iwl_txq_reclaim, 357762306a36Sopenharmony_ci 357862306a36Sopenharmony_ci .set_q_ptrs = iwl_txq_set_q_ptrs, 357962306a36Sopenharmony_ci 358062306a36Sopenharmony_ci .txq_alloc = iwl_txq_dyn_alloc, 358162306a36Sopenharmony_ci .txq_free = iwl_txq_dyn_free, 358262306a36Sopenharmony_ci .wait_txq_empty = iwl_trans_pcie_wait_txq_empty, 358362306a36Sopenharmony_ci .rxq_dma_data = iwl_trans_pcie_rxq_dma_data, 358462306a36Sopenharmony_ci .load_pnvm = iwl_trans_pcie_ctx_info_gen3_load_pnvm, 358562306a36Sopenharmony_ci .set_pnvm = iwl_trans_pcie_ctx_info_gen3_set_pnvm, 358662306a36Sopenharmony_ci .load_reduce_power = iwl_trans_pcie_ctx_info_gen3_load_reduce_power, 358762306a36Sopenharmony_ci .set_reduce_power = iwl_trans_pcie_ctx_info_gen3_set_reduce_power, 358862306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS 358962306a36Sopenharmony_ci .debugfs_cleanup = iwl_trans_pcie_debugfs_cleanup, 359062306a36Sopenharmony_ci#endif 359162306a36Sopenharmony_ci}; 359262306a36Sopenharmony_ci 359362306a36Sopenharmony_cistruct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, 359462306a36Sopenharmony_ci const struct pci_device_id *ent, 359562306a36Sopenharmony_ci const struct iwl_cfg_trans_params *cfg_trans) 359662306a36Sopenharmony_ci{ 359762306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie; 359862306a36Sopenharmony_ci struct iwl_trans *trans; 359962306a36Sopenharmony_ci int ret, addr_size; 360062306a36Sopenharmony_ci const struct iwl_trans_ops *ops = &trans_ops_pcie_gen2; 360162306a36Sopenharmony_ci void __iomem * const *table; 360262306a36Sopenharmony_ci 360362306a36Sopenharmony_ci if (!cfg_trans->gen2) 360462306a36Sopenharmony_ci ops = &trans_ops_pcie; 360562306a36Sopenharmony_ci 360662306a36Sopenharmony_ci ret = pcim_enable_device(pdev); 360762306a36Sopenharmony_ci if (ret) 360862306a36Sopenharmony_ci return ERR_PTR(ret); 360962306a36Sopenharmony_ci 361062306a36Sopenharmony_ci trans = iwl_trans_alloc(sizeof(struct iwl_trans_pcie), &pdev->dev, ops, 361162306a36Sopenharmony_ci cfg_trans); 361262306a36Sopenharmony_ci if (!trans) 361362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 361462306a36Sopenharmony_ci 361562306a36Sopenharmony_ci trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 361662306a36Sopenharmony_ci 361762306a36Sopenharmony_ci trans_pcie->trans = trans; 361862306a36Sopenharmony_ci trans_pcie->opmode_down = true; 361962306a36Sopenharmony_ci spin_lock_init(&trans_pcie->irq_lock); 362062306a36Sopenharmony_ci spin_lock_init(&trans_pcie->reg_lock); 362162306a36Sopenharmony_ci spin_lock_init(&trans_pcie->alloc_page_lock); 362262306a36Sopenharmony_ci mutex_init(&trans_pcie->mutex); 362362306a36Sopenharmony_ci init_waitqueue_head(&trans_pcie->ucode_write_waitq); 362462306a36Sopenharmony_ci init_waitqueue_head(&trans_pcie->fw_reset_waitq); 362562306a36Sopenharmony_ci init_waitqueue_head(&trans_pcie->imr_waitq); 362662306a36Sopenharmony_ci 362762306a36Sopenharmony_ci trans_pcie->rba.alloc_wq = alloc_workqueue("rb_allocator", 362862306a36Sopenharmony_ci WQ_HIGHPRI | WQ_UNBOUND, 0); 362962306a36Sopenharmony_ci if (!trans_pcie->rba.alloc_wq) { 363062306a36Sopenharmony_ci ret = -ENOMEM; 363162306a36Sopenharmony_ci goto out_free_trans; 363262306a36Sopenharmony_ci } 363362306a36Sopenharmony_ci INIT_WORK(&trans_pcie->rba.rx_alloc, iwl_pcie_rx_allocator_work); 363462306a36Sopenharmony_ci 363562306a36Sopenharmony_ci trans_pcie->debug_rfkill = -1; 363662306a36Sopenharmony_ci 363762306a36Sopenharmony_ci if (!cfg_trans->base_params->pcie_l1_allowed) { 363862306a36Sopenharmony_ci /* 363962306a36Sopenharmony_ci * W/A - seems to solve weird behavior. We need to remove this 364062306a36Sopenharmony_ci * if we don't want to stay in L1 all the time. This wastes a 364162306a36Sopenharmony_ci * lot of power. 364262306a36Sopenharmony_ci */ 364362306a36Sopenharmony_ci pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | 364462306a36Sopenharmony_ci PCIE_LINK_STATE_L1 | 364562306a36Sopenharmony_ci PCIE_LINK_STATE_CLKPM); 364662306a36Sopenharmony_ci } 364762306a36Sopenharmony_ci 364862306a36Sopenharmony_ci pci_set_master(pdev); 364962306a36Sopenharmony_ci 365062306a36Sopenharmony_ci addr_size = trans->txqs.tfd.addr_size; 365162306a36Sopenharmony_ci ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(addr_size)); 365262306a36Sopenharmony_ci if (ret) { 365362306a36Sopenharmony_ci ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 365462306a36Sopenharmony_ci /* both attempts failed: */ 365562306a36Sopenharmony_ci if (ret) { 365662306a36Sopenharmony_ci dev_err(&pdev->dev, "No suitable DMA available\n"); 365762306a36Sopenharmony_ci goto out_no_pci; 365862306a36Sopenharmony_ci } 365962306a36Sopenharmony_ci } 366062306a36Sopenharmony_ci 366162306a36Sopenharmony_ci ret = pcim_iomap_regions_request_all(pdev, BIT(0), DRV_NAME); 366262306a36Sopenharmony_ci if (ret) { 366362306a36Sopenharmony_ci dev_err(&pdev->dev, "pcim_iomap_regions_request_all failed\n"); 366462306a36Sopenharmony_ci goto out_no_pci; 366562306a36Sopenharmony_ci } 366662306a36Sopenharmony_ci 366762306a36Sopenharmony_ci table = pcim_iomap_table(pdev); 366862306a36Sopenharmony_ci if (!table) { 366962306a36Sopenharmony_ci dev_err(&pdev->dev, "pcim_iomap_table failed\n"); 367062306a36Sopenharmony_ci ret = -ENOMEM; 367162306a36Sopenharmony_ci goto out_no_pci; 367262306a36Sopenharmony_ci } 367362306a36Sopenharmony_ci 367462306a36Sopenharmony_ci trans_pcie->hw_base = table[0]; 367562306a36Sopenharmony_ci if (!trans_pcie->hw_base) { 367662306a36Sopenharmony_ci dev_err(&pdev->dev, "couldn't find IO mem in first BAR\n"); 367762306a36Sopenharmony_ci ret = -ENODEV; 367862306a36Sopenharmony_ci goto out_no_pci; 367962306a36Sopenharmony_ci } 368062306a36Sopenharmony_ci 368162306a36Sopenharmony_ci /* We disable the RETRY_TIMEOUT register (0x41) to keep 368262306a36Sopenharmony_ci * PCI Tx retries from interfering with C3 CPU state */ 368362306a36Sopenharmony_ci pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); 368462306a36Sopenharmony_ci 368562306a36Sopenharmony_ci trans_pcie->pci_dev = pdev; 368662306a36Sopenharmony_ci iwl_disable_interrupts(trans); 368762306a36Sopenharmony_ci 368862306a36Sopenharmony_ci trans->hw_rev = iwl_read32(trans, CSR_HW_REV); 368962306a36Sopenharmony_ci if (trans->hw_rev == 0xffffffff) { 369062306a36Sopenharmony_ci dev_err(&pdev->dev, "HW_REV=0xFFFFFFFF, PCI issues?\n"); 369162306a36Sopenharmony_ci ret = -EIO; 369262306a36Sopenharmony_ci goto out_no_pci; 369362306a36Sopenharmony_ci } 369462306a36Sopenharmony_ci 369562306a36Sopenharmony_ci /* 369662306a36Sopenharmony_ci * In the 8000 HW family the format of the 4 bytes of CSR_HW_REV have 369762306a36Sopenharmony_ci * changed, and now the revision step also includes bit 0-1 (no more 369862306a36Sopenharmony_ci * "dash" value). To keep hw_rev backwards compatible - we'll store it 369962306a36Sopenharmony_ci * in the old format. 370062306a36Sopenharmony_ci */ 370162306a36Sopenharmony_ci if (cfg_trans->device_family >= IWL_DEVICE_FAMILY_8000) 370262306a36Sopenharmony_ci trans->hw_rev_step = trans->hw_rev & 0xF; 370362306a36Sopenharmony_ci else 370462306a36Sopenharmony_ci trans->hw_rev_step = (trans->hw_rev & 0xC) >> 2; 370562306a36Sopenharmony_ci 370662306a36Sopenharmony_ci IWL_DEBUG_INFO(trans, "HW REV: 0x%0x\n", trans->hw_rev); 370762306a36Sopenharmony_ci 370862306a36Sopenharmony_ci iwl_pcie_set_interrupt_capa(pdev, trans, cfg_trans); 370962306a36Sopenharmony_ci trans->hw_id = (pdev->device << 16) + pdev->subsystem_device; 371062306a36Sopenharmony_ci snprintf(trans->hw_id_str, sizeof(trans->hw_id_str), 371162306a36Sopenharmony_ci "PCI ID: 0x%04X:0x%04X", pdev->device, pdev->subsystem_device); 371262306a36Sopenharmony_ci 371362306a36Sopenharmony_ci init_waitqueue_head(&trans_pcie->sx_waitq); 371462306a36Sopenharmony_ci 371562306a36Sopenharmony_ci ret = iwl_pcie_alloc_invalid_tx_cmd(trans); 371662306a36Sopenharmony_ci if (ret) 371762306a36Sopenharmony_ci goto out_no_pci; 371862306a36Sopenharmony_ci 371962306a36Sopenharmony_ci if (trans_pcie->msix_enabled) { 372062306a36Sopenharmony_ci ret = iwl_pcie_init_msix_handler(pdev, trans_pcie); 372162306a36Sopenharmony_ci if (ret) 372262306a36Sopenharmony_ci goto out_no_pci; 372362306a36Sopenharmony_ci } else { 372462306a36Sopenharmony_ci ret = iwl_pcie_alloc_ict(trans); 372562306a36Sopenharmony_ci if (ret) 372662306a36Sopenharmony_ci goto out_no_pci; 372762306a36Sopenharmony_ci 372862306a36Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, pdev->irq, 372962306a36Sopenharmony_ci iwl_pcie_isr, 373062306a36Sopenharmony_ci iwl_pcie_irq_handler, 373162306a36Sopenharmony_ci IRQF_SHARED, DRV_NAME, trans); 373262306a36Sopenharmony_ci if (ret) { 373362306a36Sopenharmony_ci IWL_ERR(trans, "Error allocating IRQ %d\n", pdev->irq); 373462306a36Sopenharmony_ci goto out_free_ict; 373562306a36Sopenharmony_ci } 373662306a36Sopenharmony_ci } 373762306a36Sopenharmony_ci 373862306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS 373962306a36Sopenharmony_ci trans_pcie->fw_mon_data.state = IWL_FW_MON_DBGFS_STATE_CLOSED; 374062306a36Sopenharmony_ci mutex_init(&trans_pcie->fw_mon_data.mutex); 374162306a36Sopenharmony_ci#endif 374262306a36Sopenharmony_ci 374362306a36Sopenharmony_ci iwl_dbg_tlv_init(trans); 374462306a36Sopenharmony_ci 374562306a36Sopenharmony_ci return trans; 374662306a36Sopenharmony_ci 374762306a36Sopenharmony_ciout_free_ict: 374862306a36Sopenharmony_ci iwl_pcie_free_ict(trans); 374962306a36Sopenharmony_ciout_no_pci: 375062306a36Sopenharmony_ci destroy_workqueue(trans_pcie->rba.alloc_wq); 375162306a36Sopenharmony_ciout_free_trans: 375262306a36Sopenharmony_ci iwl_trans_free(trans); 375362306a36Sopenharmony_ci return ERR_PTR(ret); 375462306a36Sopenharmony_ci} 375562306a36Sopenharmony_ci 375662306a36Sopenharmony_civoid iwl_trans_pcie_copy_imr_fh(struct iwl_trans *trans, 375762306a36Sopenharmony_ci u32 dst_addr, u64 src_addr, u32 byte_cnt) 375862306a36Sopenharmony_ci{ 375962306a36Sopenharmony_ci iwl_write_prph(trans, IMR_UREG_CHICK, 376062306a36Sopenharmony_ci iwl_read_prph(trans, IMR_UREG_CHICK) | 376162306a36Sopenharmony_ci IMR_UREG_CHICK_HALT_UMAC_PERMANENTLY_MSK); 376262306a36Sopenharmony_ci iwl_write_prph(trans, IMR_TFH_SRV_DMA_CHNL0_SRAM_ADDR, dst_addr); 376362306a36Sopenharmony_ci iwl_write_prph(trans, IMR_TFH_SRV_DMA_CHNL0_DRAM_ADDR_LSB, 376462306a36Sopenharmony_ci (u32)(src_addr & 0xFFFFFFFF)); 376562306a36Sopenharmony_ci iwl_write_prph(trans, IMR_TFH_SRV_DMA_CHNL0_DRAM_ADDR_MSB, 376662306a36Sopenharmony_ci iwl_get_dma_hi_addr(src_addr)); 376762306a36Sopenharmony_ci iwl_write_prph(trans, IMR_TFH_SRV_DMA_CHNL0_BC, byte_cnt); 376862306a36Sopenharmony_ci iwl_write_prph(trans, IMR_TFH_SRV_DMA_CHNL0_CTRL, 376962306a36Sopenharmony_ci IMR_TFH_SRV_DMA_CHNL0_CTRL_D2S_IRQ_TARGET_POS | 377062306a36Sopenharmony_ci IMR_TFH_SRV_DMA_CHNL0_CTRL_D2S_DMA_EN_POS | 377162306a36Sopenharmony_ci IMR_TFH_SRV_DMA_CHNL0_CTRL_D2S_RS_MSK); 377262306a36Sopenharmony_ci} 377362306a36Sopenharmony_ci 377462306a36Sopenharmony_ciint iwl_trans_pcie_copy_imr(struct iwl_trans *trans, 377562306a36Sopenharmony_ci u32 dst_addr, u64 src_addr, u32 byte_cnt) 377662306a36Sopenharmony_ci{ 377762306a36Sopenharmony_ci struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 377862306a36Sopenharmony_ci int ret = -1; 377962306a36Sopenharmony_ci 378062306a36Sopenharmony_ci trans_pcie->imr_status = IMR_D2S_REQUESTED; 378162306a36Sopenharmony_ci iwl_trans_pcie_copy_imr_fh(trans, dst_addr, src_addr, byte_cnt); 378262306a36Sopenharmony_ci ret = wait_event_timeout(trans_pcie->imr_waitq, 378362306a36Sopenharmony_ci trans_pcie->imr_status != 378462306a36Sopenharmony_ci IMR_D2S_REQUESTED, 5 * HZ); 378562306a36Sopenharmony_ci if (!ret || trans_pcie->imr_status == IMR_D2S_ERROR) { 378662306a36Sopenharmony_ci IWL_ERR(trans, "Failed to copy IMR Memory chunk!\n"); 378762306a36Sopenharmony_ci iwl_trans_pcie_dump_regs(trans); 378862306a36Sopenharmony_ci return -ETIMEDOUT; 378962306a36Sopenharmony_ci } 379062306a36Sopenharmony_ci trans_pcie->imr_status = IMR_D2S_IDLE; 379162306a36Sopenharmony_ci return 0; 379262306a36Sopenharmony_ci} 3793