linux/drivers/net/wireless/intel/iwlwifi/iwl-io.c

527 lines
13 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2003-2014, 2018-2021 Intel Corporation
* Copyright (C) 2015-2016 Intel Deutschland GmbH
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/export.h>
#include "iwl-drv.h"
#include "iwl-io.h"
#include "iwl-csr.h"
#include "iwl-debug.h"
#include "iwl-prph.h"
#include "iwl-fh.h"
void iwl_write8(struct iwl_trans *trans, u32 ofs, u8 val)
{
trace_iwlwifi_dev_iowrite8(trans->dev, ofs, val);
iwl_trans_write8(trans, ofs, val);
}
IWL_EXPORT_SYMBOL(iwl_write8);
void iwl_write32(struct iwl_trans *trans, u32 ofs, u32 val)
{
trace_iwlwifi_dev_iowrite32(trans->dev, ofs, val);
iwl_trans_write32(trans, ofs, val);
}
IWL_EXPORT_SYMBOL(iwl_write32);
void iwl_write64(struct iwl_trans *trans, u64 ofs, u64 val)
{
trace_iwlwifi_dev_iowrite64(trans->dev, ofs, val);
iwl_trans_write32(trans, ofs, lower_32_bits(val));
iwl_trans_write32(trans, ofs + 4, upper_32_bits(val));
}
IWL_EXPORT_SYMBOL(iwl_write64);
u32 iwl_read32(struct iwl_trans *trans, u32 ofs)
{
u32 val = iwl_trans_read32(trans, ofs);
trace_iwlwifi_dev_ioread32(trans->dev, ofs, val);
return val;
}
IWL_EXPORT_SYMBOL(iwl_read32);
#define IWL_POLL_INTERVAL 10 /* microseconds */
int iwl_poll_bit(struct iwl_trans *trans, u32 addr,
u32 bits, u32 mask, int timeout)
{
int t = 0;
do {
if ((iwl_read32(trans, addr) & mask) == (bits & mask))
return t;
udelay(IWL_POLL_INTERVAL);
t += IWL_POLL_INTERVAL;
} while (t < timeout);
return -ETIMEDOUT;
}
IWL_EXPORT_SYMBOL(iwl_poll_bit);
u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg)
{
u32 value = 0x5a5a5a5a;
if (iwl_trans_grab_nic_access(trans)) {
value = iwl_read32(trans, reg);
iwl_trans_release_nic_access(trans);
}
return value;
}
IWL_EXPORT_SYMBOL(iwl_read_direct32);
void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value)
{
if (iwl_trans_grab_nic_access(trans)) {
iwl_write32(trans, reg, value);
iwl_trans_release_nic_access(trans);
}
}
IWL_EXPORT_SYMBOL(iwl_write_direct32);
void iwl_write_direct64(struct iwl_trans *trans, u64 reg, u64 value)
{
if (iwl_trans_grab_nic_access(trans)) {
iwl_write64(trans, reg, value);
iwl_trans_release_nic_access(trans);
}
}
IWL_EXPORT_SYMBOL(iwl_write_direct64);
int iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask,
int timeout)
{
int t = 0;
do {
if ((iwl_read_direct32(trans, addr) & mask) == mask)
return t;
udelay(IWL_POLL_INTERVAL);
t += IWL_POLL_INTERVAL;
} while (t < timeout);
return -ETIMEDOUT;
}
IWL_EXPORT_SYMBOL(iwl_poll_direct_bit);
u32 iwl_read_prph_no_grab(struct iwl_trans *trans, u32 ofs)
{
u32 val = iwl_trans_read_prph(trans, ofs);
trace_iwlwifi_dev_ioread_prph32(trans->dev, ofs, val);
return val;
}
IWL_EXPORT_SYMBOL(iwl_read_prph_no_grab);
void iwl_write_prph_no_grab(struct iwl_trans *trans, u32 ofs, u32 val)
{
trace_iwlwifi_dev_iowrite_prph32(trans->dev, ofs, val);
iwl_trans_write_prph(trans, ofs, val);
}
IWL_EXPORT_SYMBOL(iwl_write_prph_no_grab);
void iwl_write_prph64_no_grab(struct iwl_trans *trans, u64 ofs, u64 val)
{
trace_iwlwifi_dev_iowrite_prph64(trans->dev, ofs, val);
iwl_write_prph_no_grab(trans, ofs, val & 0xffffffff);
iwl_write_prph_no_grab(trans, ofs + 4, val >> 32);
}
IWL_EXPORT_SYMBOL(iwl_write_prph64_no_grab);
u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs)
{
u32 val = 0x5a5a5a5a;
if (iwl_trans_grab_nic_access(trans)) {
val = iwl_read_prph_no_grab(trans, ofs);
iwl_trans_release_nic_access(trans);
}
return val;
}
IWL_EXPORT_SYMBOL(iwl_read_prph);
iwlwifi: fix the NMI flow for old devices I noticed that the flow that triggers an NMI on the firmware for old devices (tested on 7265) doesn't work. Apparently, the firmware / device is still in low power when we write the register that triggers the NMI. We call the "grab_nic_access" function to make sure the device is awake but that wasn't enough. I played with this and noticed that if we wait 1 ms after the device reports it is awake before we write to the NMI register, the device always sees our write and the firmware gets properly asserted. Triggering an NMI to the firmware can be done with the debugfs hook: echo 1 > /sys/kernel/debug/iwlwifi/0000\:00\:03.0/iwlmvm/fw_nmi What happened before is that the firmware would just stall without running its NMI routine. Because of that the driver wouldn't get the "firmware crashed" interrupt. After a while the driver would notice that the firmware is not responding to some command and it would read the error data from the firmware, but this data is populated in the NMI service routine in the firmware which was not called. So in the logs it looked like: iwlwifi 0000:00:03.0: Error sending REPLY_ERROR: time out after 2000ms. iwlwifi 0000:00:03.0: Current CMD queue read_ptr 33 write_ptr 34 iwlwifi 0000:00:03.0: Loaded firmware version: 29.09bd31e1.0 7265D-29.ucode iwlwifi 0000:00:03.0: 0x00000000 | ADVANCED_SYSASSERT iwlwifi 0000:00:03.0: 0x00000000 | trm_hw_status0 iwlwifi 0000:00:03.0: 0x00000000 | trm_hw_status1 iwlwifi 0000:00:03.0: 0x00000000 | branchlink2 iwlwifi 0000:00:03.0: 0x00000000 | interruptlink1 iwlwifi 0000:00:03.0: 0x00000000 | interruptlink2 iwlwifi 0000:00:03.0: 0x00000000 | data1 iwlwifi 0000:00:03.0: 0x00000000 | data2 iwlwifi 0000:00:03.0: 0x00000000 | data3 iwlwifi 0000:00:03.0: 0x00000000 | beacon time iwlwifi 0000:00:03.0: 0x00000000 | tsf low ... With this fix, immediately after we trigger the NMI to the firmware, we get the expected: iwlwifi 0000:00:03.0: Microcode SW error detected. Restarting 0x2000000. iwlwifi 0000:00:03.0: Start IWL Error Log Dump: iwlwifi 0000:00:03.0: Status: 0x00000040, count: 6 iwlwifi 0000:00:03.0: Loaded firmware version: 29.09bd31e1.0 7265D-29.ucode iwlwifi 0000:00:03.0: 0x00000084 | NMI_INTERRUPT_UNKNOWN iwlwifi 0000:00:03.0: 0x000002F1 | trm_hw_status0 iwlwifi 0000:00:03.0: 0x00000000 | trm_hw_status1 iwlwifi 0000:00:03.0: 0x00043D6C | branchlink2 iwlwifi 0000:00:03.0: 0x0004AFD6 | interruptlink1 iwlwifi 0000:00:03.0: 0x000008C4 | interruptlink2 iwlwifi 0000:00:03.0: 0x00000000 | data1 iwlwifi 0000:00:03.0: 0x00000080 | data2 iwlwifi 0000:00:03.0: 0x07030000 | data3 iwlwifi 0000:00:03.0: 0x003FD4C3 | beacon time iwlwifi 0000:00:03.0: 0x00C22AC3 | tsf low iwlwifi 0000:00:03.0: 0x00000000 | tsf hi iwlwifi 0000:00:03.0: 0x00000000 | time gp1 iwlwifi 0000:00:03.0: 0x00C22AC3 | time gp2 iwlwifi 0000:00:03.0: 0x00000001 | uCode revision type iwlwifi 0000:00:03.0: 0x0000001D | uCode version major Notice the first line: "Microcode SW error detected:" which is printed in the driver's ISR, which means that the driver actually got an interrupt from the firmware saying that it crashed. And then we have the properly populated error data. Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com> Signed-off-by: Luca Coelho <luciano.coelho@intel.com> Signed-off-by: Kalle Valo <kvalo@codeaurora.org> Link: https://lore.kernel.org/r/iwlwifi.20210115130252.70e67cc75d88.I6615cad4361862e7f3c9f2d3cafb6a8c61e16781@changeid
2021-01-15 13:05:52 +02:00
void iwl_write_prph_delay(struct iwl_trans *trans, u32 ofs, u32 val, u32 delay_ms)
{
if (iwl_trans_grab_nic_access(trans)) {
iwlwifi: fix the NMI flow for old devices I noticed that the flow that triggers an NMI on the firmware for old devices (tested on 7265) doesn't work. Apparently, the firmware / device is still in low power when we write the register that triggers the NMI. We call the "grab_nic_access" function to make sure the device is awake but that wasn't enough. I played with this and noticed that if we wait 1 ms after the device reports it is awake before we write to the NMI register, the device always sees our write and the firmware gets properly asserted. Triggering an NMI to the firmware can be done with the debugfs hook: echo 1 > /sys/kernel/debug/iwlwifi/0000\:00\:03.0/iwlmvm/fw_nmi What happened before is that the firmware would just stall without running its NMI routine. Because of that the driver wouldn't get the "firmware crashed" interrupt. After a while the driver would notice that the firmware is not responding to some command and it would read the error data from the firmware, but this data is populated in the NMI service routine in the firmware which was not called. So in the logs it looked like: iwlwifi 0000:00:03.0: Error sending REPLY_ERROR: time out after 2000ms. iwlwifi 0000:00:03.0: Current CMD queue read_ptr 33 write_ptr 34 iwlwifi 0000:00:03.0: Loaded firmware version: 29.09bd31e1.0 7265D-29.ucode iwlwifi 0000:00:03.0: 0x00000000 | ADVANCED_SYSASSERT iwlwifi 0000:00:03.0: 0x00000000 | trm_hw_status0 iwlwifi 0000:00:03.0: 0x00000000 | trm_hw_status1 iwlwifi 0000:00:03.0: 0x00000000 | branchlink2 iwlwifi 0000:00:03.0: 0x00000000 | interruptlink1 iwlwifi 0000:00:03.0: 0x00000000 | interruptlink2 iwlwifi 0000:00:03.0: 0x00000000 | data1 iwlwifi 0000:00:03.0: 0x00000000 | data2 iwlwifi 0000:00:03.0: 0x00000000 | data3 iwlwifi 0000:00:03.0: 0x00000000 | beacon time iwlwifi 0000:00:03.0: 0x00000000 | tsf low ... With this fix, immediately after we trigger the NMI to the firmware, we get the expected: iwlwifi 0000:00:03.0: Microcode SW error detected. Restarting 0x2000000. iwlwifi 0000:00:03.0: Start IWL Error Log Dump: iwlwifi 0000:00:03.0: Status: 0x00000040, count: 6 iwlwifi 0000:00:03.0: Loaded firmware version: 29.09bd31e1.0 7265D-29.ucode iwlwifi 0000:00:03.0: 0x00000084 | NMI_INTERRUPT_UNKNOWN iwlwifi 0000:00:03.0: 0x000002F1 | trm_hw_status0 iwlwifi 0000:00:03.0: 0x00000000 | trm_hw_status1 iwlwifi 0000:00:03.0: 0x00043D6C | branchlink2 iwlwifi 0000:00:03.0: 0x0004AFD6 | interruptlink1 iwlwifi 0000:00:03.0: 0x000008C4 | interruptlink2 iwlwifi 0000:00:03.0: 0x00000000 | data1 iwlwifi 0000:00:03.0: 0x00000080 | data2 iwlwifi 0000:00:03.0: 0x07030000 | data3 iwlwifi 0000:00:03.0: 0x003FD4C3 | beacon time iwlwifi 0000:00:03.0: 0x00C22AC3 | tsf low iwlwifi 0000:00:03.0: 0x00000000 | tsf hi iwlwifi 0000:00:03.0: 0x00000000 | time gp1 iwlwifi 0000:00:03.0: 0x00C22AC3 | time gp2 iwlwifi 0000:00:03.0: 0x00000001 | uCode revision type iwlwifi 0000:00:03.0: 0x0000001D | uCode version major Notice the first line: "Microcode SW error detected:" which is printed in the driver's ISR, which means that the driver actually got an interrupt from the firmware saying that it crashed. And then we have the properly populated error data. Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com> Signed-off-by: Luca Coelho <luciano.coelho@intel.com> Signed-off-by: Kalle Valo <kvalo@codeaurora.org> Link: https://lore.kernel.org/r/iwlwifi.20210115130252.70e67cc75d88.I6615cad4361862e7f3c9f2d3cafb6a8c61e16781@changeid
2021-01-15 13:05:52 +02:00
mdelay(delay_ms);
iwl_write_prph_no_grab(trans, ofs, val);
iwl_trans_release_nic_access(trans);
}
}
iwlwifi: fix the NMI flow for old devices I noticed that the flow that triggers an NMI on the firmware for old devices (tested on 7265) doesn't work. Apparently, the firmware / device is still in low power when we write the register that triggers the NMI. We call the "grab_nic_access" function to make sure the device is awake but that wasn't enough. I played with this and noticed that if we wait 1 ms after the device reports it is awake before we write to the NMI register, the device always sees our write and the firmware gets properly asserted. Triggering an NMI to the firmware can be done with the debugfs hook: echo 1 > /sys/kernel/debug/iwlwifi/0000\:00\:03.0/iwlmvm/fw_nmi What happened before is that the firmware would just stall without running its NMI routine. Because of that the driver wouldn't get the "firmware crashed" interrupt. After a while the driver would notice that the firmware is not responding to some command and it would read the error data from the firmware, but this data is populated in the NMI service routine in the firmware which was not called. So in the logs it looked like: iwlwifi 0000:00:03.0: Error sending REPLY_ERROR: time out after 2000ms. iwlwifi 0000:00:03.0: Current CMD queue read_ptr 33 write_ptr 34 iwlwifi 0000:00:03.0: Loaded firmware version: 29.09bd31e1.0 7265D-29.ucode iwlwifi 0000:00:03.0: 0x00000000 | ADVANCED_SYSASSERT iwlwifi 0000:00:03.0: 0x00000000 | trm_hw_status0 iwlwifi 0000:00:03.0: 0x00000000 | trm_hw_status1 iwlwifi 0000:00:03.0: 0x00000000 | branchlink2 iwlwifi 0000:00:03.0: 0x00000000 | interruptlink1 iwlwifi 0000:00:03.0: 0x00000000 | interruptlink2 iwlwifi 0000:00:03.0: 0x00000000 | data1 iwlwifi 0000:00:03.0: 0x00000000 | data2 iwlwifi 0000:00:03.0: 0x00000000 | data3 iwlwifi 0000:00:03.0: 0x00000000 | beacon time iwlwifi 0000:00:03.0: 0x00000000 | tsf low ... With this fix, immediately after we trigger the NMI to the firmware, we get the expected: iwlwifi 0000:00:03.0: Microcode SW error detected. Restarting 0x2000000. iwlwifi 0000:00:03.0: Start IWL Error Log Dump: iwlwifi 0000:00:03.0: Status: 0x00000040, count: 6 iwlwifi 0000:00:03.0: Loaded firmware version: 29.09bd31e1.0 7265D-29.ucode iwlwifi 0000:00:03.0: 0x00000084 | NMI_INTERRUPT_UNKNOWN iwlwifi 0000:00:03.0: 0x000002F1 | trm_hw_status0 iwlwifi 0000:00:03.0: 0x00000000 | trm_hw_status1 iwlwifi 0000:00:03.0: 0x00043D6C | branchlink2 iwlwifi 0000:00:03.0: 0x0004AFD6 | interruptlink1 iwlwifi 0000:00:03.0: 0x000008C4 | interruptlink2 iwlwifi 0000:00:03.0: 0x00000000 | data1 iwlwifi 0000:00:03.0: 0x00000080 | data2 iwlwifi 0000:00:03.0: 0x07030000 | data3 iwlwifi 0000:00:03.0: 0x003FD4C3 | beacon time iwlwifi 0000:00:03.0: 0x00C22AC3 | tsf low iwlwifi 0000:00:03.0: 0x00000000 | tsf hi iwlwifi 0000:00:03.0: 0x00000000 | time gp1 iwlwifi 0000:00:03.0: 0x00C22AC3 | time gp2 iwlwifi 0000:00:03.0: 0x00000001 | uCode revision type iwlwifi 0000:00:03.0: 0x0000001D | uCode version major Notice the first line: "Microcode SW error detected:" which is printed in the driver's ISR, which means that the driver actually got an interrupt from the firmware saying that it crashed. And then we have the properly populated error data. Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com> Signed-off-by: Luca Coelho <luciano.coelho@intel.com> Signed-off-by: Kalle Valo <kvalo@codeaurora.org> Link: https://lore.kernel.org/r/iwlwifi.20210115130252.70e67cc75d88.I6615cad4361862e7f3c9f2d3cafb6a8c61e16781@changeid
2021-01-15 13:05:52 +02:00
IWL_EXPORT_SYMBOL(iwl_write_prph_delay);
int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr,
u32 bits, u32 mask, int timeout)
{
int t = 0;
do {
if ((iwl_read_prph(trans, addr) & mask) == (bits & mask))
return t;
udelay(IWL_POLL_INTERVAL);
t += IWL_POLL_INTERVAL;
} while (t < timeout);
return -ETIMEDOUT;
}
void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
{
if (iwl_trans_grab_nic_access(trans)) {
iwl_write_prph_no_grab(trans, ofs,
iwl_read_prph_no_grab(trans, ofs) |
mask);
iwl_trans_release_nic_access(trans);
}
}
IWL_EXPORT_SYMBOL(iwl_set_bits_prph);
void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs,
u32 bits, u32 mask)
{
if (iwl_trans_grab_nic_access(trans)) {
iwl_write_prph_no_grab(trans, ofs,
(iwl_read_prph_no_grab(trans, ofs) &
mask) | bits);
iwl_trans_release_nic_access(trans);
}
}
IWL_EXPORT_SYMBOL(iwl_set_bits_mask_prph);
void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
{
u32 val;
if (iwl_trans_grab_nic_access(trans)) {
val = iwl_read_prph_no_grab(trans, ofs);
iwl_write_prph_no_grab(trans, ofs, (val & ~mask));
iwl_trans_release_nic_access(trans);
}
}
IWL_EXPORT_SYMBOL(iwl_clear_bits_prph);
void iwl_force_nmi(struct iwl_trans *trans)
{
if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_9000)
iwlwifi: fix the NMI flow for old devices I noticed that the flow that triggers an NMI on the firmware for old devices (tested on 7265) doesn't work. Apparently, the firmware / device is still in low power when we write the register that triggers the NMI. We call the "grab_nic_access" function to make sure the device is awake but that wasn't enough. I played with this and noticed that if we wait 1 ms after the device reports it is awake before we write to the NMI register, the device always sees our write and the firmware gets properly asserted. Triggering an NMI to the firmware can be done with the debugfs hook: echo 1 > /sys/kernel/debug/iwlwifi/0000\:00\:03.0/iwlmvm/fw_nmi What happened before is that the firmware would just stall without running its NMI routine. Because of that the driver wouldn't get the "firmware crashed" interrupt. After a while the driver would notice that the firmware is not responding to some command and it would read the error data from the firmware, but this data is populated in the NMI service routine in the firmware which was not called. So in the logs it looked like: iwlwifi 0000:00:03.0: Error sending REPLY_ERROR: time out after 2000ms. iwlwifi 0000:00:03.0: Current CMD queue read_ptr 33 write_ptr 34 iwlwifi 0000:00:03.0: Loaded firmware version: 29.09bd31e1.0 7265D-29.ucode iwlwifi 0000:00:03.0: 0x00000000 | ADVANCED_SYSASSERT iwlwifi 0000:00:03.0: 0x00000000 | trm_hw_status0 iwlwifi 0000:00:03.0: 0x00000000 | trm_hw_status1 iwlwifi 0000:00:03.0: 0x00000000 | branchlink2 iwlwifi 0000:00:03.0: 0x00000000 | interruptlink1 iwlwifi 0000:00:03.0: 0x00000000 | interruptlink2 iwlwifi 0000:00:03.0: 0x00000000 | data1 iwlwifi 0000:00:03.0: 0x00000000 | data2 iwlwifi 0000:00:03.0: 0x00000000 | data3 iwlwifi 0000:00:03.0: 0x00000000 | beacon time iwlwifi 0000:00:03.0: 0x00000000 | tsf low ... With this fix, immediately after we trigger the NMI to the firmware, we get the expected: iwlwifi 0000:00:03.0: Microcode SW error detected. Restarting 0x2000000. iwlwifi 0000:00:03.0: Start IWL Error Log Dump: iwlwifi 0000:00:03.0: Status: 0x00000040, count: 6 iwlwifi 0000:00:03.0: Loaded firmware version: 29.09bd31e1.0 7265D-29.ucode iwlwifi 0000:00:03.0: 0x00000084 | NMI_INTERRUPT_UNKNOWN iwlwifi 0000:00:03.0: 0x000002F1 | trm_hw_status0 iwlwifi 0000:00:03.0: 0x00000000 | trm_hw_status1 iwlwifi 0000:00:03.0: 0x00043D6C | branchlink2 iwlwifi 0000:00:03.0: 0x0004AFD6 | interruptlink1 iwlwifi 0000:00:03.0: 0x000008C4 | interruptlink2 iwlwifi 0000:00:03.0: 0x00000000 | data1 iwlwifi 0000:00:03.0: 0x00000080 | data2 iwlwifi 0000:00:03.0: 0x07030000 | data3 iwlwifi 0000:00:03.0: 0x003FD4C3 | beacon time iwlwifi 0000:00:03.0: 0x00C22AC3 | tsf low iwlwifi 0000:00:03.0: 0x00000000 | tsf hi iwlwifi 0000:00:03.0: 0x00000000 | time gp1 iwlwifi 0000:00:03.0: 0x00C22AC3 | time gp2 iwlwifi 0000:00:03.0: 0x00000001 | uCode revision type iwlwifi 0000:00:03.0: 0x0000001D | uCode version major Notice the first line: "Microcode SW error detected:" which is printed in the driver's ISR, which means that the driver actually got an interrupt from the firmware saying that it crashed. And then we have the properly populated error data. Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com> Signed-off-by: Luca Coelho <luciano.coelho@intel.com> Signed-off-by: Kalle Valo <kvalo@codeaurora.org> Link: https://lore.kernel.org/r/iwlwifi.20210115130252.70e67cc75d88.I6615cad4361862e7f3c9f2d3cafb6a8c61e16781@changeid
2021-01-15 13:05:52 +02:00
iwl_write_prph_delay(trans, DEVICE_SET_NMI_REG,
DEVICE_SET_NMI_VAL_DRV, 1);
else if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
iwl_write_umac_prph(trans, UREG_NIC_SET_NMI_DRIVER,
UREG_NIC_SET_NMI_DRIVER_NMI_FROM_DRIVER);
else if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ)
iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6,
UREG_DOORBELL_TO_ISR6_NMI_BIT);
else
iwl_write32(trans, CSR_DOORBELL_VECTOR,
CSR_DOORBELL_VECTOR_NMI);
}
IWL_EXPORT_SYMBOL(iwl_force_nmi);
static const char *get_rfh_string(int cmd)
{
#define IWL_CMD(x) case x: return #x
#define IWL_CMD_MQ(arg, reg, q) { if (arg == reg(q)) return #reg; }
int i;
for (i = 0; i < IWL_MAX_RX_HW_QUEUES; i++) {
IWL_CMD_MQ(cmd, RFH_Q_FRBDCB_BA_LSB, i);
IWL_CMD_MQ(cmd, RFH_Q_FRBDCB_WIDX, i);
IWL_CMD_MQ(cmd, RFH_Q_FRBDCB_RIDX, i);
IWL_CMD_MQ(cmd, RFH_Q_URBD_STTS_WPTR_LSB, i);
}
switch (cmd) {
IWL_CMD(RFH_RXF_DMA_CFG);
IWL_CMD(RFH_GEN_CFG);
IWL_CMD(RFH_GEN_STATUS);
IWL_CMD(FH_TSSR_TX_STATUS_REG);
IWL_CMD(FH_TSSR_TX_ERROR_REG);
default:
return "UNKNOWN";
}
#undef IWL_CMD_MQ
}
struct reg {
u32 addr;
bool is64;
};
static int iwl_dump_rfh(struct iwl_trans *trans, char **buf)
{
int i, q;
int num_q = trans->num_rx_queues;
static const u32 rfh_tbl[] = {
RFH_RXF_DMA_CFG,
RFH_GEN_CFG,
RFH_GEN_STATUS,
FH_TSSR_TX_STATUS_REG,
FH_TSSR_TX_ERROR_REG,
};
static const struct reg rfh_mq_tbl[] = {
{ RFH_Q0_FRBDCB_BA_LSB, true },
{ RFH_Q0_FRBDCB_WIDX, false },
{ RFH_Q0_FRBDCB_RIDX, false },
{ RFH_Q0_URBD_STTS_WPTR_LSB, true },
};
#ifdef CONFIG_IWLWIFI_DEBUGFS
if (buf) {
int pos = 0;
/*
* Register (up to 34 for name + 8 blank/q for MQ): 40 chars
* Colon + space: 2 characters
* 0X%08x: 10 characters
* New line: 1 character
* Total of 53 characters
*/
size_t bufsz = ARRAY_SIZE(rfh_tbl) * 53 +
ARRAY_SIZE(rfh_mq_tbl) * 53 * num_q + 40;
*buf = kmalloc(bufsz, GFP_KERNEL);
if (!*buf)
return -ENOMEM;
pos += scnprintf(*buf + pos, bufsz - pos,
"RFH register values:\n");
for (i = 0; i < ARRAY_SIZE(rfh_tbl); i++)
pos += scnprintf(*buf + pos, bufsz - pos,
"%40s: 0X%08x\n",
get_rfh_string(rfh_tbl[i]),
iwl_read_prph(trans, rfh_tbl[i]));
for (i = 0; i < ARRAY_SIZE(rfh_mq_tbl); i++)
for (q = 0; q < num_q; q++) {
u32 addr = rfh_mq_tbl[i].addr;
addr += q * (rfh_mq_tbl[i].is64 ? 8 : 4);
pos += scnprintf(*buf + pos, bufsz - pos,
"%34s(q %2d): 0X%08x\n",
get_rfh_string(addr), q,
iwl_read_prph(trans, addr));
}
return pos;
}
#endif
IWL_ERR(trans, "RFH register values:\n");
for (i = 0; i < ARRAY_SIZE(rfh_tbl); i++)
IWL_ERR(trans, " %34s: 0X%08x\n",
get_rfh_string(rfh_tbl[i]),
iwl_read_prph(trans, rfh_tbl[i]));
for (i = 0; i < ARRAY_SIZE(rfh_mq_tbl); i++)
for (q = 0; q < num_q; q++) {
u32 addr = rfh_mq_tbl[i].addr;
addr += q * (rfh_mq_tbl[i].is64 ? 8 : 4);
IWL_ERR(trans, " %34s(q %d): 0X%08x\n",
get_rfh_string(addr), q,
iwl_read_prph(trans, addr));
}
return 0;
}
static const char *get_fh_string(int cmd)
{
switch (cmd) {
IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG);
IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG);
IWL_CMD(FH_RSCSR_CHNL0_WPTR);
IWL_CMD(FH_MEM_RCSR_CHNL0_CONFIG_REG);
IWL_CMD(FH_MEM_RSSR_SHARED_CTRL_REG);
IWL_CMD(FH_MEM_RSSR_RX_STATUS_REG);
IWL_CMD(FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV);
IWL_CMD(FH_TSSR_TX_STATUS_REG);
IWL_CMD(FH_TSSR_TX_ERROR_REG);
default:
return "UNKNOWN";
}
#undef IWL_CMD
}
int iwl_dump_fh(struct iwl_trans *trans, char **buf)
{
int i;
static const u32 fh_tbl[] = {
FH_RSCSR_CHNL0_STTS_WPTR_REG,
FH_RSCSR_CHNL0_RBDCB_BASE_REG,
FH_RSCSR_CHNL0_WPTR,
FH_MEM_RCSR_CHNL0_CONFIG_REG,
FH_MEM_RSSR_SHARED_CTRL_REG,
FH_MEM_RSSR_RX_STATUS_REG,
FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV,
FH_TSSR_TX_STATUS_REG,
FH_TSSR_TX_ERROR_REG
};
if (trans->trans_cfg->mq_rx_supported)
return iwl_dump_rfh(trans, buf);
#ifdef CONFIG_IWLWIFI_DEBUGFS
if (buf) {
int pos = 0;
size_t bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40;
*buf = kmalloc(bufsz, GFP_KERNEL);
if (!*buf)
return -ENOMEM;
pos += scnprintf(*buf + pos, bufsz - pos,
"FH register values:\n");
for (i = 0; i < ARRAY_SIZE(fh_tbl); i++)
pos += scnprintf(*buf + pos, bufsz - pos,
" %34s: 0X%08x\n",
get_fh_string(fh_tbl[i]),
iwl_read_direct32(trans, fh_tbl[i]));
return pos;
}
#endif
IWL_ERR(trans, "FH register values:\n");
for (i = 0; i < ARRAY_SIZE(fh_tbl); i++)
IWL_ERR(trans, " %34s: 0X%08x\n",
get_fh_string(fh_tbl[i]),
iwl_read_direct32(trans, fh_tbl[i]));
return 0;
}
#define IWL_HOST_MON_BLOCK_PEMON 0x00
#define IWL_HOST_MON_BLOCK_HIPM 0x22
#define IWL_HOST_MON_BLOCK_PEMON_VEC0 0x00
#define IWL_HOST_MON_BLOCK_PEMON_VEC1 0x01
#define IWL_HOST_MON_BLOCK_PEMON_WFPM 0x06
static void iwl_dump_host_monitor_block(struct iwl_trans *trans,
u32 block, u32 vec, u32 iter)
{
int i;
IWL_ERR(trans, "Host monitor block 0x%x vector 0x%x\n", block, vec);
iwl_write32(trans, CSR_MONITOR_CFG_REG, (block << 8) | vec);
for (i = 0; i < iter; i++)
IWL_ERR(trans, " value [iter %d]: 0x%08x\n",
i, iwl_read32(trans, CSR_MONITOR_STATUS_REG));
}
static void iwl_dump_host_monitor(struct iwl_trans *trans)
{
switch (trans->trans_cfg->device_family) {
case IWL_DEVICE_FAMILY_22000:
case IWL_DEVICE_FAMILY_AX210:
IWL_ERR(trans, "CSR_RESET = 0x%x\n",
iwl_read32(trans, CSR_RESET));
iwl_dump_host_monitor_block(trans, IWL_HOST_MON_BLOCK_PEMON,
IWL_HOST_MON_BLOCK_PEMON_VEC0, 15);
iwl_dump_host_monitor_block(trans, IWL_HOST_MON_BLOCK_PEMON,
IWL_HOST_MON_BLOCK_PEMON_VEC1, 15);
iwl_dump_host_monitor_block(trans, IWL_HOST_MON_BLOCK_PEMON,
IWL_HOST_MON_BLOCK_PEMON_WFPM, 15);
iwl_dump_host_monitor_block(trans, IWL_HOST_MON_BLOCK_HIPM,
IWL_HOST_MON_BLOCK_PEMON_VEC0, 1);
break;
default:
/* not supported yet */
return;
}
}
int iwl_finish_nic_init(struct iwl_trans *trans)
{
const struct iwl_cfg_trans_params *cfg_trans = trans->trans_cfg;
u32 poll_ready;
int err;
if (cfg_trans->bisr_workaround) {
/* ensure the TOP FSM isn't still in previous reset */
mdelay(2);
}
/*
* Set "initialization complete" bit to move adapter from
* D0U* --> D0A* (powered-up active) state.
*/
if (cfg_trans->device_family >= IWL_DEVICE_FAMILY_BZ) {
iwl_set_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
CSR_GP_CNTRL_REG_FLAG_MAC_INIT);
poll_ready = CSR_GP_CNTRL_REG_FLAG_MAC_STATUS;
} else {
iwl_set_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
poll_ready = CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY;
}
if (cfg_trans->device_family == IWL_DEVICE_FAMILY_8000)
udelay(2);
/*
* Wait for clock stabilization; once stabilized, access to
* device-internal resources is supported, e.g. iwl_write_prph()
* and accesses to uCode SRAM.
*/
err = iwl_poll_bit(trans, CSR_GP_CNTRL, poll_ready, poll_ready, 25000);
if (err < 0) {
IWL_DEBUG_INFO(trans, "Failed to wake NIC\n");
iwl_dump_host_monitor(trans);
}
if (cfg_trans->bisr_workaround) {
/* ensure BISR shift has finished */
udelay(200);
}
return err < 0 ? err : 0;
}
IWL_EXPORT_SYMBOL(iwl_finish_nic_init);
void iwl_trans_sync_nmi_with_addr(struct iwl_trans *trans, u32 inta_addr,
u32 sw_err_bit)
{
unsigned long timeout = jiffies + IWL_TRANS_NMI_TIMEOUT;
bool interrupts_enabled = test_bit(STATUS_INT_ENABLED, &trans->status);
/* if the interrupts were already disabled, there is no point in
* calling iwl_disable_interrupts
*/
if (interrupts_enabled)
iwl_trans_interrupts(trans, false);
iwl_force_nmi(trans);
while (time_after(timeout, jiffies)) {
u32 inta_hw = iwl_read32(trans, inta_addr);
/* Error detected by uCode */
if (inta_hw & sw_err_bit) {
/* Clear causes register */
iwl_write32(trans, inta_addr, inta_hw & sw_err_bit);
break;
}
mdelay(1);
}
/* enable interrupts only if there were already enabled before this
* function to avoid a case were the driver enable interrupts before
* proper configurations were made
*/
if (interrupts_enabled)
iwl_trans_interrupts(trans, true);
iwl_trans_fw_error(trans, false);
}