162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2003-2014, 2018-2022 Intel Corporation
462306a36Sopenharmony_ci * Copyright (C) 2015-2016 Intel Deutschland GmbH
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include <linux/delay.h>
762306a36Sopenharmony_ci#include <linux/device.h>
862306a36Sopenharmony_ci#include <linux/export.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "iwl-drv.h"
1162306a36Sopenharmony_ci#include "iwl-io.h"
1262306a36Sopenharmony_ci#include "iwl-csr.h"
1362306a36Sopenharmony_ci#include "iwl-debug.h"
1462306a36Sopenharmony_ci#include "iwl-prph.h"
1562306a36Sopenharmony_ci#include "iwl-fh.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_civoid iwl_write8(struct iwl_trans *trans, u32 ofs, u8 val)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	trace_iwlwifi_dev_iowrite8(trans->dev, ofs, val);
2062306a36Sopenharmony_ci	iwl_trans_write8(trans, ofs, val);
2162306a36Sopenharmony_ci}
2262306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_write8);
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_civoid iwl_write32(struct iwl_trans *trans, u32 ofs, u32 val)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	trace_iwlwifi_dev_iowrite32(trans->dev, ofs, val);
2762306a36Sopenharmony_ci	iwl_trans_write32(trans, ofs, val);
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_write32);
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_civoid iwl_write64(struct iwl_trans *trans, u64 ofs, u64 val)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	trace_iwlwifi_dev_iowrite64(trans->dev, ofs, val);
3462306a36Sopenharmony_ci	iwl_trans_write32(trans, ofs, lower_32_bits(val));
3562306a36Sopenharmony_ci	iwl_trans_write32(trans, ofs + 4, upper_32_bits(val));
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_write64);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ciu32 iwl_read32(struct iwl_trans *trans, u32 ofs)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	u32 val = iwl_trans_read32(trans, ofs);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	trace_iwlwifi_dev_ioread32(trans->dev, ofs, val);
4462306a36Sopenharmony_ci	return val;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_read32);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#define IWL_POLL_INTERVAL 10	/* microseconds */
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ciint iwl_poll_bit(struct iwl_trans *trans, u32 addr,
5162306a36Sopenharmony_ci		 u32 bits, u32 mask, int timeout)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	int t = 0;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	do {
5662306a36Sopenharmony_ci		if ((iwl_read32(trans, addr) & mask) == (bits & mask))
5762306a36Sopenharmony_ci			return t;
5862306a36Sopenharmony_ci		udelay(IWL_POLL_INTERVAL);
5962306a36Sopenharmony_ci		t += IWL_POLL_INTERVAL;
6062306a36Sopenharmony_ci	} while (t < timeout);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	return -ETIMEDOUT;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_poll_bit);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ciu32 iwl_read_direct32(struct iwl_trans *trans, u32 reg)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	if (iwl_trans_grab_nic_access(trans)) {
6962306a36Sopenharmony_ci		u32 value = iwl_read32(trans, reg);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci		iwl_trans_release_nic_access(trans);
7262306a36Sopenharmony_ci		return value;
7362306a36Sopenharmony_ci	}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	/* return as if we have a HW timeout/failure */
7662306a36Sopenharmony_ci	return 0x5a5a5a5a;
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_read_direct32);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_civoid iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	if (iwl_trans_grab_nic_access(trans)) {
8362306a36Sopenharmony_ci		iwl_write32(trans, reg, value);
8462306a36Sopenharmony_ci		iwl_trans_release_nic_access(trans);
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_write_direct32);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_civoid iwl_write_direct64(struct iwl_trans *trans, u64 reg, u64 value)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	if (iwl_trans_grab_nic_access(trans)) {
9262306a36Sopenharmony_ci		iwl_write64(trans, reg, value);
9362306a36Sopenharmony_ci		iwl_trans_release_nic_access(trans);
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_write_direct64);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ciint iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask,
9962306a36Sopenharmony_ci			int timeout)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	int t = 0;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	do {
10462306a36Sopenharmony_ci		if ((iwl_read_direct32(trans, addr) & mask) == mask)
10562306a36Sopenharmony_ci			return t;
10662306a36Sopenharmony_ci		udelay(IWL_POLL_INTERVAL);
10762306a36Sopenharmony_ci		t += IWL_POLL_INTERVAL;
10862306a36Sopenharmony_ci	} while (t < timeout);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	return -ETIMEDOUT;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_poll_direct_bit);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ciu32 iwl_read_prph_no_grab(struct iwl_trans *trans, u32 ofs)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	u32 val = iwl_trans_read_prph(trans, ofs);
11762306a36Sopenharmony_ci	trace_iwlwifi_dev_ioread_prph32(trans->dev, ofs, val);
11862306a36Sopenharmony_ci	return val;
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_read_prph_no_grab);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_civoid iwl_write_prph_no_grab(struct iwl_trans *trans, u32 ofs, u32 val)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	trace_iwlwifi_dev_iowrite_prph32(trans->dev, ofs, val);
12562306a36Sopenharmony_ci	iwl_trans_write_prph(trans, ofs, val);
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_write_prph_no_grab);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_civoid iwl_write_prph64_no_grab(struct iwl_trans *trans, u64 ofs, u64 val)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	trace_iwlwifi_dev_iowrite_prph64(trans->dev, ofs, val);
13262306a36Sopenharmony_ci	iwl_write_prph_no_grab(trans, ofs, val & 0xffffffff);
13362306a36Sopenharmony_ci	iwl_write_prph_no_grab(trans, ofs + 4, val >> 32);
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_write_prph64_no_grab);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ciu32 iwl_read_prph(struct iwl_trans *trans, u32 ofs)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	if (iwl_trans_grab_nic_access(trans)) {
14062306a36Sopenharmony_ci		u32 val = iwl_read_prph_no_grab(trans, ofs);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci		iwl_trans_release_nic_access(trans);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci		return val;
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	/* return as if we have a HW timeout/failure */
14862306a36Sopenharmony_ci	return 0x5a5a5a5a;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_read_prph);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_civoid iwl_write_prph_delay(struct iwl_trans *trans, u32 ofs, u32 val, u32 delay_ms)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	if (iwl_trans_grab_nic_access(trans)) {
15562306a36Sopenharmony_ci		mdelay(delay_ms);
15662306a36Sopenharmony_ci		iwl_write_prph_no_grab(trans, ofs, val);
15762306a36Sopenharmony_ci		iwl_trans_release_nic_access(trans);
15862306a36Sopenharmony_ci	}
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_write_prph_delay);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ciint iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr,
16362306a36Sopenharmony_ci		      u32 bits, u32 mask, int timeout)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	int t = 0;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	do {
16862306a36Sopenharmony_ci		if ((iwl_read_prph(trans, addr) & mask) == (bits & mask))
16962306a36Sopenharmony_ci			return t;
17062306a36Sopenharmony_ci		udelay(IWL_POLL_INTERVAL);
17162306a36Sopenharmony_ci		t += IWL_POLL_INTERVAL;
17262306a36Sopenharmony_ci	} while (t < timeout);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	return -ETIMEDOUT;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_civoid iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	if (iwl_trans_grab_nic_access(trans)) {
18062306a36Sopenharmony_ci		iwl_write_prph_no_grab(trans, ofs,
18162306a36Sopenharmony_ci				       iwl_read_prph_no_grab(trans, ofs) |
18262306a36Sopenharmony_ci				       mask);
18362306a36Sopenharmony_ci		iwl_trans_release_nic_access(trans);
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_set_bits_prph);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_civoid iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs,
18962306a36Sopenharmony_ci			    u32 bits, u32 mask)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	if (iwl_trans_grab_nic_access(trans)) {
19262306a36Sopenharmony_ci		iwl_write_prph_no_grab(trans, ofs,
19362306a36Sopenharmony_ci				       (iwl_read_prph_no_grab(trans, ofs) &
19462306a36Sopenharmony_ci					mask) | bits);
19562306a36Sopenharmony_ci		iwl_trans_release_nic_access(trans);
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_set_bits_mask_prph);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_civoid iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	u32 val;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	if (iwl_trans_grab_nic_access(trans)) {
20562306a36Sopenharmony_ci		val = iwl_read_prph_no_grab(trans, ofs);
20662306a36Sopenharmony_ci		iwl_write_prph_no_grab(trans, ofs, (val & ~mask));
20762306a36Sopenharmony_ci		iwl_trans_release_nic_access(trans);
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_clear_bits_prph);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_civoid iwl_force_nmi(struct iwl_trans *trans)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_9000)
21562306a36Sopenharmony_ci		iwl_write_prph_delay(trans, DEVICE_SET_NMI_REG,
21662306a36Sopenharmony_ci				     DEVICE_SET_NMI_VAL_DRV, 1);
21762306a36Sopenharmony_ci	else if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
21862306a36Sopenharmony_ci		iwl_write_umac_prph(trans, UREG_NIC_SET_NMI_DRIVER,
21962306a36Sopenharmony_ci				UREG_NIC_SET_NMI_DRIVER_NMI_FROM_DRIVER);
22062306a36Sopenharmony_ci	else if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ)
22162306a36Sopenharmony_ci		iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6,
22262306a36Sopenharmony_ci				    UREG_DOORBELL_TO_ISR6_NMI_BIT);
22362306a36Sopenharmony_ci	else
22462306a36Sopenharmony_ci		iwl_write32(trans, CSR_DOORBELL_VECTOR,
22562306a36Sopenharmony_ci			    UREG_DOORBELL_TO_ISR6_NMI_BIT);
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_force_nmi);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic const char *get_rfh_string(int cmd)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci#define IWL_CMD(x) case x: return #x
23262306a36Sopenharmony_ci#define IWL_CMD_MQ(arg, reg, q) { if (arg == reg(q)) return #reg; }
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	int i;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	for (i = 0; i < IWL_MAX_RX_HW_QUEUES; i++) {
23762306a36Sopenharmony_ci		IWL_CMD_MQ(cmd, RFH_Q_FRBDCB_BA_LSB, i);
23862306a36Sopenharmony_ci		IWL_CMD_MQ(cmd, RFH_Q_FRBDCB_WIDX, i);
23962306a36Sopenharmony_ci		IWL_CMD_MQ(cmd, RFH_Q_FRBDCB_RIDX, i);
24062306a36Sopenharmony_ci		IWL_CMD_MQ(cmd, RFH_Q_URBD_STTS_WPTR_LSB, i);
24162306a36Sopenharmony_ci	}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	switch (cmd) {
24462306a36Sopenharmony_ci	IWL_CMD(RFH_RXF_DMA_CFG);
24562306a36Sopenharmony_ci	IWL_CMD(RFH_GEN_CFG);
24662306a36Sopenharmony_ci	IWL_CMD(RFH_GEN_STATUS);
24762306a36Sopenharmony_ci	IWL_CMD(FH_TSSR_TX_STATUS_REG);
24862306a36Sopenharmony_ci	IWL_CMD(FH_TSSR_TX_ERROR_REG);
24962306a36Sopenharmony_ci	default:
25062306a36Sopenharmony_ci		return "UNKNOWN";
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci#undef IWL_CMD_MQ
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_cistruct reg {
25662306a36Sopenharmony_ci	u32 addr;
25762306a36Sopenharmony_ci	bool is64;
25862306a36Sopenharmony_ci};
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic int iwl_dump_rfh(struct iwl_trans *trans, char **buf)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	int i, q;
26362306a36Sopenharmony_ci	int num_q = trans->num_rx_queues;
26462306a36Sopenharmony_ci	static const u32 rfh_tbl[] = {
26562306a36Sopenharmony_ci		RFH_RXF_DMA_CFG,
26662306a36Sopenharmony_ci		RFH_GEN_CFG,
26762306a36Sopenharmony_ci		RFH_GEN_STATUS,
26862306a36Sopenharmony_ci		FH_TSSR_TX_STATUS_REG,
26962306a36Sopenharmony_ci		FH_TSSR_TX_ERROR_REG,
27062306a36Sopenharmony_ci	};
27162306a36Sopenharmony_ci	static const struct reg rfh_mq_tbl[] = {
27262306a36Sopenharmony_ci		{ RFH_Q0_FRBDCB_BA_LSB, true },
27362306a36Sopenharmony_ci		{ RFH_Q0_FRBDCB_WIDX, false },
27462306a36Sopenharmony_ci		{ RFH_Q0_FRBDCB_RIDX, false },
27562306a36Sopenharmony_ci		{ RFH_Q0_URBD_STTS_WPTR_LSB, true },
27662306a36Sopenharmony_ci	};
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS
27962306a36Sopenharmony_ci	if (buf) {
28062306a36Sopenharmony_ci		int pos = 0;
28162306a36Sopenharmony_ci		/*
28262306a36Sopenharmony_ci		 * Register (up to 34 for name + 8 blank/q for MQ): 40 chars
28362306a36Sopenharmony_ci		 * Colon + space: 2 characters
28462306a36Sopenharmony_ci		 * 0X%08x: 10 characters
28562306a36Sopenharmony_ci		 * New line: 1 character
28662306a36Sopenharmony_ci		 * Total of 53 characters
28762306a36Sopenharmony_ci		 */
28862306a36Sopenharmony_ci		size_t bufsz = ARRAY_SIZE(rfh_tbl) * 53 +
28962306a36Sopenharmony_ci			       ARRAY_SIZE(rfh_mq_tbl) * 53 * num_q + 40;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci		*buf = kmalloc(bufsz, GFP_KERNEL);
29262306a36Sopenharmony_ci		if (!*buf)
29362306a36Sopenharmony_ci			return -ENOMEM;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci		pos += scnprintf(*buf + pos, bufsz - pos,
29662306a36Sopenharmony_ci				"RFH register values:\n");
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(rfh_tbl); i++)
29962306a36Sopenharmony_ci			pos += scnprintf(*buf + pos, bufsz - pos,
30062306a36Sopenharmony_ci				"%40s: 0X%08x\n",
30162306a36Sopenharmony_ci				get_rfh_string(rfh_tbl[i]),
30262306a36Sopenharmony_ci				iwl_read_prph(trans, rfh_tbl[i]));
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(rfh_mq_tbl); i++)
30562306a36Sopenharmony_ci			for (q = 0; q < num_q; q++) {
30662306a36Sopenharmony_ci				u32 addr = rfh_mq_tbl[i].addr;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci				addr += q * (rfh_mq_tbl[i].is64 ? 8 : 4);
30962306a36Sopenharmony_ci				pos += scnprintf(*buf + pos, bufsz - pos,
31062306a36Sopenharmony_ci					"%34s(q %2d): 0X%08x\n",
31162306a36Sopenharmony_ci					get_rfh_string(addr), q,
31262306a36Sopenharmony_ci					iwl_read_prph(trans, addr));
31362306a36Sopenharmony_ci			}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci		return pos;
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci#endif
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	IWL_ERR(trans, "RFH register values:\n");
32062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(rfh_tbl); i++)
32162306a36Sopenharmony_ci		IWL_ERR(trans, "  %34s: 0X%08x\n",
32262306a36Sopenharmony_ci			get_rfh_string(rfh_tbl[i]),
32362306a36Sopenharmony_ci			iwl_read_prph(trans, rfh_tbl[i]));
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(rfh_mq_tbl); i++)
32662306a36Sopenharmony_ci		for (q = 0; q < num_q; q++) {
32762306a36Sopenharmony_ci			u32 addr = rfh_mq_tbl[i].addr;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci			addr += q * (rfh_mq_tbl[i].is64 ? 8 : 4);
33062306a36Sopenharmony_ci			IWL_ERR(trans, "  %34s(q %d): 0X%08x\n",
33162306a36Sopenharmony_ci				get_rfh_string(addr), q,
33262306a36Sopenharmony_ci				iwl_read_prph(trans, addr));
33362306a36Sopenharmony_ci		}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	return 0;
33662306a36Sopenharmony_ci}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cistatic const char *get_fh_string(int cmd)
33962306a36Sopenharmony_ci{
34062306a36Sopenharmony_ci	switch (cmd) {
34162306a36Sopenharmony_ci	IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG);
34262306a36Sopenharmony_ci	IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG);
34362306a36Sopenharmony_ci	IWL_CMD(FH_RSCSR_CHNL0_WPTR);
34462306a36Sopenharmony_ci	IWL_CMD(FH_MEM_RCSR_CHNL0_CONFIG_REG);
34562306a36Sopenharmony_ci	IWL_CMD(FH_MEM_RSSR_SHARED_CTRL_REG);
34662306a36Sopenharmony_ci	IWL_CMD(FH_MEM_RSSR_RX_STATUS_REG);
34762306a36Sopenharmony_ci	IWL_CMD(FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV);
34862306a36Sopenharmony_ci	IWL_CMD(FH_TSSR_TX_STATUS_REG);
34962306a36Sopenharmony_ci	IWL_CMD(FH_TSSR_TX_ERROR_REG);
35062306a36Sopenharmony_ci	default:
35162306a36Sopenharmony_ci		return "UNKNOWN";
35262306a36Sopenharmony_ci	}
35362306a36Sopenharmony_ci#undef IWL_CMD
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ciint iwl_dump_fh(struct iwl_trans *trans, char **buf)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	int i;
35962306a36Sopenharmony_ci	static const u32 fh_tbl[] = {
36062306a36Sopenharmony_ci		FH_RSCSR_CHNL0_STTS_WPTR_REG,
36162306a36Sopenharmony_ci		FH_RSCSR_CHNL0_RBDCB_BASE_REG,
36262306a36Sopenharmony_ci		FH_RSCSR_CHNL0_WPTR,
36362306a36Sopenharmony_ci		FH_MEM_RCSR_CHNL0_CONFIG_REG,
36462306a36Sopenharmony_ci		FH_MEM_RSSR_SHARED_CTRL_REG,
36562306a36Sopenharmony_ci		FH_MEM_RSSR_RX_STATUS_REG,
36662306a36Sopenharmony_ci		FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV,
36762306a36Sopenharmony_ci		FH_TSSR_TX_STATUS_REG,
36862306a36Sopenharmony_ci		FH_TSSR_TX_ERROR_REG
36962306a36Sopenharmony_ci	};
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	if (trans->trans_cfg->mq_rx_supported)
37262306a36Sopenharmony_ci		return iwl_dump_rfh(trans, buf);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS
37562306a36Sopenharmony_ci	if (buf) {
37662306a36Sopenharmony_ci		int pos = 0;
37762306a36Sopenharmony_ci		size_t bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci		*buf = kmalloc(bufsz, GFP_KERNEL);
38062306a36Sopenharmony_ci		if (!*buf)
38162306a36Sopenharmony_ci			return -ENOMEM;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci		pos += scnprintf(*buf + pos, bufsz - pos,
38462306a36Sopenharmony_ci				"FH register values:\n");
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(fh_tbl); i++)
38762306a36Sopenharmony_ci			pos += scnprintf(*buf + pos, bufsz - pos,
38862306a36Sopenharmony_ci				"  %34s: 0X%08x\n",
38962306a36Sopenharmony_ci				get_fh_string(fh_tbl[i]),
39062306a36Sopenharmony_ci				iwl_read_direct32(trans, fh_tbl[i]));
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci		return pos;
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci#endif
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	IWL_ERR(trans, "FH register values:\n");
39762306a36Sopenharmony_ci	for (i = 0; i <  ARRAY_SIZE(fh_tbl); i++)
39862306a36Sopenharmony_ci		IWL_ERR(trans, "  %34s: 0X%08x\n",
39962306a36Sopenharmony_ci			get_fh_string(fh_tbl[i]),
40062306a36Sopenharmony_ci			iwl_read_direct32(trans, fh_tbl[i]));
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	return 0;
40362306a36Sopenharmony_ci}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci#define IWL_HOST_MON_BLOCK_PEMON	0x00
40662306a36Sopenharmony_ci#define IWL_HOST_MON_BLOCK_HIPM		0x22
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci#define IWL_HOST_MON_BLOCK_PEMON_VEC0	0x00
40962306a36Sopenharmony_ci#define IWL_HOST_MON_BLOCK_PEMON_VEC1	0x01
41062306a36Sopenharmony_ci#define IWL_HOST_MON_BLOCK_PEMON_WFPM	0x06
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_cistatic void iwl_dump_host_monitor_block(struct iwl_trans *trans,
41362306a36Sopenharmony_ci					u32 block, u32 vec, u32 iter)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	int i;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	IWL_ERR(trans, "Host monitor block 0x%x vector 0x%x\n", block, vec);
41862306a36Sopenharmony_ci	iwl_write32(trans, CSR_MONITOR_CFG_REG, (block << 8) | vec);
41962306a36Sopenharmony_ci	for (i = 0; i < iter; i++)
42062306a36Sopenharmony_ci		IWL_ERR(trans, "    value [iter %d]: 0x%08x\n",
42162306a36Sopenharmony_ci			i, iwl_read32(trans, CSR_MONITOR_STATUS_REG));
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic void iwl_dump_host_monitor(struct iwl_trans *trans)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	switch (trans->trans_cfg->device_family) {
42762306a36Sopenharmony_ci	case IWL_DEVICE_FAMILY_22000:
42862306a36Sopenharmony_ci	case IWL_DEVICE_FAMILY_AX210:
42962306a36Sopenharmony_ci		IWL_ERR(trans, "CSR_RESET = 0x%x\n",
43062306a36Sopenharmony_ci			iwl_read32(trans, CSR_RESET));
43162306a36Sopenharmony_ci		iwl_dump_host_monitor_block(trans, IWL_HOST_MON_BLOCK_PEMON,
43262306a36Sopenharmony_ci					    IWL_HOST_MON_BLOCK_PEMON_VEC0, 15);
43362306a36Sopenharmony_ci		iwl_dump_host_monitor_block(trans, IWL_HOST_MON_BLOCK_PEMON,
43462306a36Sopenharmony_ci					    IWL_HOST_MON_BLOCK_PEMON_VEC1, 15);
43562306a36Sopenharmony_ci		iwl_dump_host_monitor_block(trans, IWL_HOST_MON_BLOCK_PEMON,
43662306a36Sopenharmony_ci					    IWL_HOST_MON_BLOCK_PEMON_WFPM, 15);
43762306a36Sopenharmony_ci		iwl_dump_host_monitor_block(trans, IWL_HOST_MON_BLOCK_HIPM,
43862306a36Sopenharmony_ci					    IWL_HOST_MON_BLOCK_PEMON_VEC0, 1);
43962306a36Sopenharmony_ci		break;
44062306a36Sopenharmony_ci	default:
44162306a36Sopenharmony_ci		/* not supported yet */
44262306a36Sopenharmony_ci		return;
44362306a36Sopenharmony_ci	}
44462306a36Sopenharmony_ci}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ciint iwl_finish_nic_init(struct iwl_trans *trans)
44762306a36Sopenharmony_ci{
44862306a36Sopenharmony_ci	const struct iwl_cfg_trans_params *cfg_trans = trans->trans_cfg;
44962306a36Sopenharmony_ci	u32 poll_ready;
45062306a36Sopenharmony_ci	int err;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	if (cfg_trans->bisr_workaround) {
45362306a36Sopenharmony_ci		/* ensure the TOP FSM isn't still in previous reset */
45462306a36Sopenharmony_ci		mdelay(2);
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	/*
45862306a36Sopenharmony_ci	 * Set "initialization complete" bit to move adapter from
45962306a36Sopenharmony_ci	 * D0U* --> D0A* (powered-up active) state.
46062306a36Sopenharmony_ci	 */
46162306a36Sopenharmony_ci	if (cfg_trans->device_family >= IWL_DEVICE_FAMILY_BZ) {
46262306a36Sopenharmony_ci		iwl_set_bit(trans, CSR_GP_CNTRL,
46362306a36Sopenharmony_ci			    CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
46462306a36Sopenharmony_ci			    CSR_GP_CNTRL_REG_FLAG_MAC_INIT);
46562306a36Sopenharmony_ci		poll_ready = CSR_GP_CNTRL_REG_FLAG_MAC_STATUS;
46662306a36Sopenharmony_ci	} else {
46762306a36Sopenharmony_ci		iwl_set_bit(trans, CSR_GP_CNTRL,
46862306a36Sopenharmony_ci			    CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
46962306a36Sopenharmony_ci		poll_ready = CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY;
47062306a36Sopenharmony_ci	}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	if (cfg_trans->device_family == IWL_DEVICE_FAMILY_8000)
47362306a36Sopenharmony_ci		udelay(2);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	/*
47662306a36Sopenharmony_ci	 * Wait for clock stabilization; once stabilized, access to
47762306a36Sopenharmony_ci	 * device-internal resources is supported, e.g. iwl_write_prph()
47862306a36Sopenharmony_ci	 * and accesses to uCode SRAM.
47962306a36Sopenharmony_ci	 */
48062306a36Sopenharmony_ci	err = iwl_poll_bit(trans, CSR_GP_CNTRL, poll_ready, poll_ready, 25000);
48162306a36Sopenharmony_ci	if (err < 0) {
48262306a36Sopenharmony_ci		IWL_DEBUG_INFO(trans, "Failed to wake NIC\n");
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci		iwl_dump_host_monitor(trans);
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	if (cfg_trans->bisr_workaround) {
48862306a36Sopenharmony_ci		/* ensure BISR shift has finished */
48962306a36Sopenharmony_ci		udelay(200);
49062306a36Sopenharmony_ci	}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	return err < 0 ? err : 0;
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_finish_nic_init);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_civoid iwl_trans_sync_nmi_with_addr(struct iwl_trans *trans, u32 inta_addr,
49762306a36Sopenharmony_ci				  u32 sw_err_bit)
49862306a36Sopenharmony_ci{
49962306a36Sopenharmony_ci	unsigned long timeout = jiffies + IWL_TRANS_NMI_TIMEOUT;
50062306a36Sopenharmony_ci	bool interrupts_enabled = test_bit(STATUS_INT_ENABLED, &trans->status);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	/* if the interrupts were already disabled, there is no point in
50362306a36Sopenharmony_ci	 * calling iwl_disable_interrupts
50462306a36Sopenharmony_ci	 */
50562306a36Sopenharmony_ci	if (interrupts_enabled)
50662306a36Sopenharmony_ci		iwl_trans_interrupts(trans, false);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	iwl_force_nmi(trans);
50962306a36Sopenharmony_ci	while (time_after(timeout, jiffies)) {
51062306a36Sopenharmony_ci		u32 inta_hw = iwl_read32(trans, inta_addr);
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci		/* Error detected by uCode */
51362306a36Sopenharmony_ci		if (inta_hw & sw_err_bit) {
51462306a36Sopenharmony_ci			/* Clear causes register */
51562306a36Sopenharmony_ci			iwl_write32(trans, inta_addr, inta_hw & sw_err_bit);
51662306a36Sopenharmony_ci			break;
51762306a36Sopenharmony_ci		}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci		mdelay(1);
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	/* enable interrupts only if there were already enabled before this
52362306a36Sopenharmony_ci	 * function to avoid a case were the driver enable interrupts before
52462306a36Sopenharmony_ci	 * proper configurations were made
52562306a36Sopenharmony_ci	 */
52662306a36Sopenharmony_ci	if (interrupts_enabled)
52762306a36Sopenharmony_ci		iwl_trans_interrupts(trans, true);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	iwl_trans_fw_error(trans, false);
53062306a36Sopenharmony_ci}
531